From a52ada36ac678e8f97998bfc7775183c6e642716 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Tue, 27 Dec 2022 00:30:08 +0100 Subject: [PATCH 001/182] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..eb9389959 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# ethereum-beacon-light-client +Implementation of the Ethereum beacon chain light client in Rust From 09d11f6eff8a0158032cbb262ca688b9ed6126e1 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 27 Dec 2022 00:34:16 +0100 Subject: [PATCH 002/182] Initialized repo Spec out primitives Implement BeaconBlockBody --- src/primitives/beacon.rs | 119 +++++++++++++++++++++++++++++++++ src/primitives/consts.rs | 56 ++++++++++++++++ src/primitives/light_client.rs | 1 + src/primitives/mod.rs | 4 ++ src/primitives/sync.rs | 1 + src/util.rs | 1 + 6 files changed, 182 insertions(+) create mode 100644 src/primitives/beacon.rs create mode 100644 src/primitives/consts.rs create mode 100644 src/primitives/light_client.rs create mode 100644 src/primitives/mod.rs create mode 100644 src/primitives/sync.rs create mode 100644 src/util.rs diff --git a/src/primitives/beacon.rs b/src/primitives/beacon.rs new file mode 100644 index 000000000..6af3b624a --- /dev/null +++ b/src/primitives/beacon.rs @@ -0,0 +1,119 @@ +use crate::primitives::consts::{ + DEPOSIT_CONTRACT_TREE_DEPTH, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, + MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SYNC_COMMITTEE_SIZE, +}; +use sp_core::H256; + +struct ValidatorIndex(u64); +struct Slot(u64); +struct BLSSignature([u8; 24]); +struct BLSPubkey([u8; 12]); +struct Root([u8; 32]); +struct CommitteeIndex(u64); +struct Epoch(u64); +struct Gwei(u64); + +struct Eth1Data { + deposit_root: H256, + deposit_count: u64, + block_hash: H256, +} + +struct ProposerSlashing { + signed_header_1: SignedBeaconBlockHeader, + signed_header_2: SignedBeaconBlockHeader, +} + +struct SignedBeaconBlockHeader { + message: BeaconBlockHeader, + signature: BLSSignature, +} + +struct AttesterSlashing { + attestation_1: IndexedAttestation, + attestation_2: IndexedAttestation, +} + +struct IndexedAttestation { + attesting_indices: Vec<[ValidatorIndex; MAX_VALIDATORS_PER_COMMITTEE as usize]>, + data: AttestationData, + signature: BLSSignature, +} + +struct AttestationData { + slot: Slot, + index: CommitteeIndex, + //LMD GHOST vote, + beacon_block_root: H256, + //FFG vote, + source: Checkpoint, + target: Checkpoint, +} + +struct Checkpoint { + epoch: Epoch, + root: H256, +} + +struct Attestation { + aggregation_bits: Vec<[u64; MAX_VALIDATORS_PER_COMMITTEE as usize]>, + data: AttestationData, + signature: BLSSignature, +} + +pub struct SignedVoluntaryExit { + message: VoluntaryExit, + signature: BLSSignature, +} + +struct VoluntaryExit { + epoch: Epoch, + validator_index: ValidatorIndex, +} + +struct SyncAggregate { + sync_committee_bits: Vec<[u64; SYNC_COMMITTEE_SIZE as usize]>, + sync_committee_signature: BLSSignature, +} + +struct Deposit { + proof: Vec<([u8; 4], [u64; (DEPOSIT_CONTRACT_TREE_DEPTH + 1) as usize])>, + data: DepositData, +} + +struct DepositData { + pubkey: BLSPubkey, + withdrawal_credentials: [u8; 32], + amount: Gwei, + signature: BLSSignature, +} + +/// The beacon block header +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader) +pub struct BeaconBlockHeader { + /// current slot for this block + slot: Slot, + /// validator index + proposer_index: ValidatorIndex, + /// ssz root of parent block + parent_root: H256, + /// ssz root of associated [`BeaconState`] + state_root: H256, + /// ssz root of associated [`BeaconBlockBody`] + body_root: H256, +} + +/// The beacon block body +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconblockbody) +struct BeaconBlockBody { + randao_reveal: BLSSignature, + eth1_data: Eth1Data, // Eth1 data vote + graffiti: H256, // Arbitrary data + // Operations + proposer_slashings: [ProposerSlashing; MAX_PROPOSER_SLASHINGS as usize], + attester_slashings: [AttesterSlashing; MAX_ATTESTER_SLASHINGS as usize], + attestations: [Attestation; MAX_ATTESTATIONS as usize], + deposits: [Deposit; MAX_DEPOSITS as usize], + voluntary_exits: [SignedVoluntaryExit; MAX_VOLUNTARY_EXITS as usize], + sync_aggregate: SyncAggregate, +} diff --git a/src/primitives/consts.rs b/src/primitives/consts.rs new file mode 100644 index 000000000..28e743592 --- /dev/null +++ b/src/primitives/consts.rs @@ -0,0 +1,56 @@ +use sp_core::H256; + +/// The block root and state root for every slot are stored in the state for `SLOTS_PER_HISTORICAL_ROOT` slots. +/// When that list is full, both lists are Merkleized into a single Merkle root, +/// which is added to the ever-growing state.historical_roots list. +/// [source](https://eth2book.info/bellatrix/part3/config/preset/#slots_per_historical_root) +const SLOTS_PER_HISTORICAL_ROOT: u64 = 2 ^ 13; // 8,192 + +/// Every `SLOTS_PER_HISTORICAL_ROOT` slots, the list of block roots and the list of state roots in the beacon state +/// are Merkleized and added to state.historical_roots list. Although state.historical_roots is in principle unbounded, +/// all SSZ lists must have maximum sizes specified. +/// +/// The size `HISTORICAL_ROOTS_LIMIT` will be fine for the next few millennia, after which it will be somebody else's problem. +/// The list grows at less than 10 KB per year. Storing past roots like this allows Merkle proofs to be constructed +/// about anything in the beacon chain's history if required. +/// [source](https://eth2book.info/bellatrix/part3/config/preset/#historical_roots_limit) +const HISTORICAL_ROOTS_LIMIT: u64 = 2 ^ 24; // 16,777,216 + +/// Generalized merkle tree index for the latest finalized header +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#constants) +const FINALIZED_ROOT_INDEX: u64 = 105; + +/// Generalized merkle tree index for the next sync committee +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#constants) +const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; + +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#domain-types) +pub const DOMAIN_SYNC_COMMITTEE: [u8; 4] = [7, 0, 0, 0]; + +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee) +const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: u64 = 2 ^ 8; + +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#time-parameters) +const SLOTS_PER_EPOCH: u64 = 2 ^ 5; + +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/fork.md#configuration) +const ALTAIR_FORK_EPOCH: u64 = 74240; + +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/fork.md#configuration) +const ALTAIR_FORK_VERSION: [u8; 4] = [1, 0, 0, 0]; + +/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#genesis-settings) +const GENESIS_FORK_VERSION: [u8; 4] = [0, 0, 0, 0]; + +const GENESIS_VALIDATORS_ROOT: H256 = H256([0u8; 32]); + +pub const MAX_PROPOSER_SLASHINGS: u64 = 2 ^ 4; +pub const MAX_ATTESTER_SLASHINGS: u64 = 2 ^ 4; +pub const MAX_ATTESTATIONS: u64 = 2 ^ 4; +pub const MAX_DEPOSITS: u64 = 2 ^ 4; +pub const MAX_VOLUNTARY_EXITS: u64 = 2 ^ 4; +pub const MAX_VALIDATORS_PER_COMMITTEE: u64 = 2 ^ 11; + +pub const SYNC_COMMITTEE_SIZE: u64 = 2 ^ 9; + +pub const DEPOSIT_CONTRACT_TREE_DEPTH: u64 = 2 ^ 5; diff --git a/src/primitives/light_client.rs b/src/primitives/light_client.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/primitives/light_client.rs @@ -0,0 +1 @@ + diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs new file mode 100644 index 000000000..0cbab59aa --- /dev/null +++ b/src/primitives/mod.rs @@ -0,0 +1,4 @@ +mod beacon; +pub mod consts; +mod light_client; +mod sync; diff --git a/src/primitives/sync.rs b/src/primitives/sync.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/primitives/sync.rs @@ -0,0 +1 @@ + diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 000000000..d905d9da8 --- /dev/null +++ b/src/util.rs @@ -0,0 +1 @@ +e From 8200e016efa89865b529256c0e950bc1f37f6991 Mon Sep 17 00:00:00 2001 From: David Salami Date: Sun, 8 Jan 2023 05:45:12 +0100 Subject: [PATCH 003/182] init folder structure --- .gitignore | 35 ++++++++++++++++++++++++++++++ Cargo.toml | 7 ++++++ ics15-ethereum/Cargo.toml | 9 ++++++++ ics15-ethereum/src/lib.rs | 14 ++++++++++++ light-client-primitives/Cargo.toml | 9 ++++++++ light-client-primitives/src/lib.rs | 14 ++++++++++++ light-client-verifier/Cargo.toml | 9 ++++++++ light-client-verifier/src/lib.rs | 14 ++++++++++++ 8 files changed, 111 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 ics15-ethereum/Cargo.toml create mode 100644 ics15-ethereum/src/lib.rs create mode 100644 light-client-primitives/Cargo.toml create mode 100644 light-client-primitives/src/lib.rs create mode 100644 light-client-verifier/Cargo.toml create mode 100644 light-client-verifier/src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..34a7e4e83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ + +# Generated by Cargo +# will have compiled files and executables +**/target/ +# These are backup files generated by rustfmt +**/*.rs.bk + +.DS_Store + +# The cache for docker container dependency +.cargo + +# The cache for chain data in container +.local + +# direnv cache +.direnv + +node_modules + +# don't add vim swap files +.*.swp + +.idea +.fleet + +# ignore user vscode sttings +.vscode/* +# but include tasks +!.vscode/tasks.json + +.rust-analyzer +target +.env + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..ff86a5654 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +resolver = "2" +members = [ + "light-client-verifier", + "light-client-primitives", + "ics15-ethereum" +] \ No newline at end of file diff --git a/ics15-ethereum/Cargo.toml b/ics15-ethereum/Cargo.toml new file mode 100644 index 000000000..bc7bf5b27 --- /dev/null +++ b/ics15-ethereum/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ics15-ethereum" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/ics15-ethereum/src/lib.rs b/ics15-ethereum/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/ics15-ethereum/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml new file mode 100644 index 000000000..50c7f3522 --- /dev/null +++ b/light-client-primitives/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "eth-beacon-light-client-primitives" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/light-client-primitives/src/lib.rs b/light-client-primitives/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/light-client-primitives/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml new file mode 100644 index 000000000..ddb6e0fd0 --- /dev/null +++ b/light-client-verifier/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "eth-beacon-light-client-verifier" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/light-client-verifier/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From ae1f1bc05f2fb18795e749d409aae69fe5b0160a Mon Sep 17 00:00:00 2001 From: Damilare Date: Sun, 8 Jan 2023 22:18:42 +0100 Subject: [PATCH 004/182] uncommitted files --- .gitignore | 5 +++++ Cargo.toml | 9 +++++++++ src/lib.rs | 16 ++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..925d4330b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +/Cargo.lock + +*.iml + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..52f880a0c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ethereum-beacon-light-client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..e0e52886a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,16 @@ +mod primitives; + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 04d828e136a0e81a6368a43329482c2ee256ec9c Mon Sep 17 00:00:00 2001 From: Damilare Date: Sun, 8 Jan 2023 22:45:55 +0100 Subject: [PATCH 005/182] started implementing spec types in primitives --- Cargo.toml | 11 --- light-client-primitives/Cargo.toml | 1 + light-client-primitives/src/lib.rs | 18 ++-- light-client-primitives/src/types.rs | 65 +++++++++++++++ src/lib.rs | 2 - src/primitives/beacon.rs | 119 --------------------------- src/primitives/consts.rs | 56 ------------- src/primitives/light_client.rs | 1 - src/primitives/mod.rs | 4 - src/primitives/sync.rs | 1 - src/util.rs | 1 - 11 files changed, 72 insertions(+), 207 deletions(-) create mode 100644 light-client-primitives/src/types.rs delete mode 100644 src/primitives/beacon.rs delete mode 100644 src/primitives/consts.rs delete mode 100644 src/primitives/light_client.rs delete mode 100644 src/primitives/mod.rs delete mode 100644 src/primitives/sync.rs delete mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index cb50c3900..1544c2ba5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,3 @@ -[package] -name = "ethereum-beacon-light-client" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" } - - [workspace] resolver = "2" members = [ diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index 50c7f3522..af8570aa7 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -7,3 +7,4 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } diff --git a/light-client-primitives/src/lib.rs b/light-client-primitives/src/lib.rs index 7d12d9af8..3ca2408f0 100644 --- a/light-client-primitives/src/lib.rs +++ b/light-client-primitives/src/lib.rs @@ -1,14 +1,8 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +#![cfg_attr(not(feature = "std"), no_std)] -#[cfg(test)] -mod tests { - use super::*; +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(not(feature = "std"))] +extern crate core; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod types; diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs new file mode 100644 index 000000000..f70762279 --- /dev/null +++ b/light-client-primitives/src/types.rs @@ -0,0 +1,65 @@ +use ethereum_consensus::primitives::Hash32; +use alloc::vec::Vec; +use ethereum_consensus::altair::{BeaconBlockHeader, SyncCommittee}; + +/// This holds the relevant data required to prove the state root in the execution payload. +struct ExecutionPayloadProof { + /// The state root in the `ExecutionPayload` which represents the commitment to + /// the ethereum world state in the yellow paper. + state_root: Hash32, + /// the block number of the execution header. + block_number: u64, + /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. + multi_proof: Vec, + /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. + execution_payload_branch: Vec, +} + + +/// Holds the neccessary proofs required to verify a header in the `block_roots` field +/// either in [`BeaconState`] or [`HistoricalBatch`]. +struct BlockRootsProof { + /// Generalized index of the header in the `block_roots` list. + block_header_index: u64, + /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` + block_header_branch: Vec, +} + +/// The block header ancestry proof, this is an enum because the header may either exist in +/// `state.block_roots` or `state.historical_roots`. +enum AncestryProof { + /// This variant defines the proof data for a beacon chain header in the `state.block_roots` + BlockRoots { + /// Proof for the header in `state.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] + block_roots_branch: Vec, + }, + /// This variant defines the neccessary proofs for a beacon chain header in the + /// `state.historical_roots`. + HistoricalRoots { + /// Proof for the header in `historical_batch.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the `historical_batch.block_roots`, needed to reconstruct + /// `hash_tree_root(historical_batch)` + historical_batch_proof: Vec, + /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` + historical_roots_proof: Vec, + /// The generalized index for the historical_batch in `state.historical_roots`. + historical_roots_index: u64, + /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in + /// [`BeaconState`] + historical_roots_branch: Vec, + }, +} + +/// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized +/// header. +struct AncestorBlock { + /// The actual beacon chain header + header: BeaconBlockHeader, + /// Associated execution header proofs + execution_payload: ExecutionPayloadProof, + /// Ancestry proofs of the beacon chain header. + ancestry_proof: AncenstryProof, +} diff --git a/src/lib.rs b/src/lib.rs index e0e52886a..7d12d9af8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -mod primitives; - pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/src/primitives/beacon.rs b/src/primitives/beacon.rs deleted file mode 100644 index 6af3b624a..000000000 --- a/src/primitives/beacon.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::primitives::consts::{ - DEPOSIT_CONTRACT_TREE_DEPTH, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, - MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SYNC_COMMITTEE_SIZE, -}; -use sp_core::H256; - -struct ValidatorIndex(u64); -struct Slot(u64); -struct BLSSignature([u8; 24]); -struct BLSPubkey([u8; 12]); -struct Root([u8; 32]); -struct CommitteeIndex(u64); -struct Epoch(u64); -struct Gwei(u64); - -struct Eth1Data { - deposit_root: H256, - deposit_count: u64, - block_hash: H256, -} - -struct ProposerSlashing { - signed_header_1: SignedBeaconBlockHeader, - signed_header_2: SignedBeaconBlockHeader, -} - -struct SignedBeaconBlockHeader { - message: BeaconBlockHeader, - signature: BLSSignature, -} - -struct AttesterSlashing { - attestation_1: IndexedAttestation, - attestation_2: IndexedAttestation, -} - -struct IndexedAttestation { - attesting_indices: Vec<[ValidatorIndex; MAX_VALIDATORS_PER_COMMITTEE as usize]>, - data: AttestationData, - signature: BLSSignature, -} - -struct AttestationData { - slot: Slot, - index: CommitteeIndex, - //LMD GHOST vote, - beacon_block_root: H256, - //FFG vote, - source: Checkpoint, - target: Checkpoint, -} - -struct Checkpoint { - epoch: Epoch, - root: H256, -} - -struct Attestation { - aggregation_bits: Vec<[u64; MAX_VALIDATORS_PER_COMMITTEE as usize]>, - data: AttestationData, - signature: BLSSignature, -} - -pub struct SignedVoluntaryExit { - message: VoluntaryExit, - signature: BLSSignature, -} - -struct VoluntaryExit { - epoch: Epoch, - validator_index: ValidatorIndex, -} - -struct SyncAggregate { - sync_committee_bits: Vec<[u64; SYNC_COMMITTEE_SIZE as usize]>, - sync_committee_signature: BLSSignature, -} - -struct Deposit { - proof: Vec<([u8; 4], [u64; (DEPOSIT_CONTRACT_TREE_DEPTH + 1) as usize])>, - data: DepositData, -} - -struct DepositData { - pubkey: BLSPubkey, - withdrawal_credentials: [u8; 32], - amount: Gwei, - signature: BLSSignature, -} - -/// The beacon block header -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader) -pub struct BeaconBlockHeader { - /// current slot for this block - slot: Slot, - /// validator index - proposer_index: ValidatorIndex, - /// ssz root of parent block - parent_root: H256, - /// ssz root of associated [`BeaconState`] - state_root: H256, - /// ssz root of associated [`BeaconBlockBody`] - body_root: H256, -} - -/// The beacon block body -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconblockbody) -struct BeaconBlockBody { - randao_reveal: BLSSignature, - eth1_data: Eth1Data, // Eth1 data vote - graffiti: H256, // Arbitrary data - // Operations - proposer_slashings: [ProposerSlashing; MAX_PROPOSER_SLASHINGS as usize], - attester_slashings: [AttesterSlashing; MAX_ATTESTER_SLASHINGS as usize], - attestations: [Attestation; MAX_ATTESTATIONS as usize], - deposits: [Deposit; MAX_DEPOSITS as usize], - voluntary_exits: [SignedVoluntaryExit; MAX_VOLUNTARY_EXITS as usize], - sync_aggregate: SyncAggregate, -} diff --git a/src/primitives/consts.rs b/src/primitives/consts.rs deleted file mode 100644 index 28e743592..000000000 --- a/src/primitives/consts.rs +++ /dev/null @@ -1,56 +0,0 @@ -use sp_core::H256; - -/// The block root and state root for every slot are stored in the state for `SLOTS_PER_HISTORICAL_ROOT` slots. -/// When that list is full, both lists are Merkleized into a single Merkle root, -/// which is added to the ever-growing state.historical_roots list. -/// [source](https://eth2book.info/bellatrix/part3/config/preset/#slots_per_historical_root) -const SLOTS_PER_HISTORICAL_ROOT: u64 = 2 ^ 13; // 8,192 - -/// Every `SLOTS_PER_HISTORICAL_ROOT` slots, the list of block roots and the list of state roots in the beacon state -/// are Merkleized and added to state.historical_roots list. Although state.historical_roots is in principle unbounded, -/// all SSZ lists must have maximum sizes specified. -/// -/// The size `HISTORICAL_ROOTS_LIMIT` will be fine for the next few millennia, after which it will be somebody else's problem. -/// The list grows at less than 10 KB per year. Storing past roots like this allows Merkle proofs to be constructed -/// about anything in the beacon chain's history if required. -/// [source](https://eth2book.info/bellatrix/part3/config/preset/#historical_roots_limit) -const HISTORICAL_ROOTS_LIMIT: u64 = 2 ^ 24; // 16,777,216 - -/// Generalized merkle tree index for the latest finalized header -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#constants) -const FINALIZED_ROOT_INDEX: u64 = 105; - -/// Generalized merkle tree index for the next sync committee -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#constants) -const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; - -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#domain-types) -pub const DOMAIN_SYNC_COMMITTEE: [u8; 4] = [7, 0, 0, 0]; - -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#sync-committee) -const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: u64 = 2 ^ 8; - -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#time-parameters) -const SLOTS_PER_EPOCH: u64 = 2 ^ 5; - -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/fork.md#configuration) -const ALTAIR_FORK_EPOCH: u64 = 74240; - -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/fork.md#configuration) -const ALTAIR_FORK_VERSION: [u8; 4] = [1, 0, 0, 0]; - -/// [source](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#genesis-settings) -const GENESIS_FORK_VERSION: [u8; 4] = [0, 0, 0, 0]; - -const GENESIS_VALIDATORS_ROOT: H256 = H256([0u8; 32]); - -pub const MAX_PROPOSER_SLASHINGS: u64 = 2 ^ 4; -pub const MAX_ATTESTER_SLASHINGS: u64 = 2 ^ 4; -pub const MAX_ATTESTATIONS: u64 = 2 ^ 4; -pub const MAX_DEPOSITS: u64 = 2 ^ 4; -pub const MAX_VOLUNTARY_EXITS: u64 = 2 ^ 4; -pub const MAX_VALIDATORS_PER_COMMITTEE: u64 = 2 ^ 11; - -pub const SYNC_COMMITTEE_SIZE: u64 = 2 ^ 9; - -pub const DEPOSIT_CONTRACT_TREE_DEPTH: u64 = 2 ^ 5; diff --git a/src/primitives/light_client.rs b/src/primitives/light_client.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/primitives/light_client.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs deleted file mode 100644 index 0cbab59aa..000000000 --- a/src/primitives/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod beacon; -pub mod consts; -mod light_client; -mod sync; diff --git a/src/primitives/sync.rs b/src/primitives/sync.rs deleted file mode 100644 index 8b1378917..000000000 --- a/src/primitives/sync.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/util.rs b/src/util.rs deleted file mode 100644 index d905d9da8..000000000 --- a/src/util.rs +++ /dev/null @@ -1 +0,0 @@ -e From 56d7bd4a9730b748694de42fe15486bc078af8b0 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sun, 8 Jan 2023 22:48:13 +0100 Subject: [PATCH 006/182] typo fix --- light-client-primitives/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index f70762279..bd82c734f 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -61,5 +61,5 @@ struct AncestorBlock { /// Associated execution header proofs execution_payload: ExecutionPayloadProof, /// Ancestry proofs of the beacon chain header. - ancestry_proof: AncenstryProof, + ancestry_proof: AncestryProof, } From a9a460749f52f3c3180d2b0852ace198156a3de1 Mon Sep 17 00:00:00 2001 From: Damilare Date: Mon, 9 Jan 2023 21:41:27 +0100 Subject: [PATCH 007/182] other types spec --- light-client-primitives/src/lib.rs | 2 -- light-client-primitives/src/types.rs | 45 ++++++++++++++++++++++++++-- src/lib.rs | 14 --------- 3 files changed, 43 insertions(+), 18 deletions(-) delete mode 100644 src/lib.rs diff --git a/light-client-primitives/src/lib.rs b/light-client-primitives/src/lib.rs index 3ca2408f0..5c785ebad 100644 --- a/light-client-primitives/src/lib.rs +++ b/light-client-primitives/src/lib.rs @@ -2,7 +2,5 @@ #[cfg(not(feature = "std"))] extern crate alloc; -#[cfg(not(feature = "std"))] -extern crate core; pub mod types; diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index bd82c734f..a6f4f518b 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -1,6 +1,9 @@ -use ethereum_consensus::primitives::Hash32; +use ethereum_consensus::primitives::{Hash32, Slot}; use alloc::vec::Vec; -use ethereum_consensus::altair::{BeaconBlockHeader, SyncCommittee}; +use ethereum_consensus::altair::{BeaconBlockHeader, SyncAggregate, SyncCommittee}; + +const NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = 10; +const FINALIZED_ROOT_INDEX_FLOOR_LOG_2: usize = 10; /// This holds the relevant data required to prove the state root in the execution payload. struct ExecutionPayloadProof { @@ -63,3 +66,41 @@ struct AncestorBlock { /// Ancestry proofs of the beacon chain header. ancestry_proof: AncestryProof, } + +/// Holds the latest sync committee as well as an ssz proof for it's existence +/// in a finalized header. +struct SyncCommitteeUpdate { + // actual sync committee + next_sync_committee: SyncCommittee, + // sync committee, ssz merkle proof. + next_sync_committee_branch: [Hash32; NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2], +} + +/// Minimum state required by the light client to validate new sync committee attestations +struct LightClientState { + /// The latest recorded finalized header + finalized_header: BeaconBlockHeader, + // Sync committees corresponding to the finalized header + current_sync_committee: SyncCommittee, + next_sync_committee: SyncCommittee, +} + +/// Data required to advance the state of the light client. +struct LightClientUpdate { + /// the header that the sync committee signed + attested_header: BeaconBlockHeader, + /// the sync committee has potentially changed, here's an ssz proof for that. + sync_committee_update: Option>, + /// the actual header which was finalized by the ethereum attestation protocol. + finalized_header: BeaconBlockHeader, + /// execution payload of the finalized header + execution_payload: ExecutionPayloadProof, + /// the ssz merkle proof for this header in the attested header, finalized headers lag by 2 epochs. + finality_branch: [Hash32; FINALIZED_ROOT_INDEX_FLOOR_LOG_2], + /// signature & participation bits + sync_aggregate: SyncAggregate, + /// slot at which signature was produced + signature_slot: Slot, + /// ancestors of the finalized block to be verified, may be empty. + ancestor_blocks: Vec, +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 7d12d9af8..000000000 --- a/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} From f0f80bffb402f5a6d66fe80f04c4969d4bf0e385 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 10 Jan 2023 11:39:05 +0100 Subject: [PATCH 008/182] make types public and derive macros --- light-client-primitives/src/types.rs | 70 +++++++++++++++------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index a6f4f518b..ab8f0ee12 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -1,36 +1,36 @@ use ethereum_consensus::primitives::{Hash32, Slot}; use alloc::vec::Vec; -use ethereum_consensus::altair::{BeaconBlockHeader, SyncAggregate, SyncCommittee}; - -const NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = 10; -const FINALIZED_ROOT_INDEX_FLOOR_LOG_2: usize = 10; +use ethereum_consensus::altair::{BeaconBlockHeader, SyncAggregate, SyncCommittee, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, FINALIZED_ROOT_INDEX_FLOOR_LOG_2}; /// This holds the relevant data required to prove the state root in the execution payload. -struct ExecutionPayloadProof { +#[derive(Debug, Clone)] +pub struct ExecutionPayloadProof { /// The state root in the `ExecutionPayload` which represents the commitment to /// the ethereum world state in the yellow paper. - state_root: Hash32, + pub state_root: Hash32, /// the block number of the execution header. - block_number: u64, + pub block_number: u64, /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. - multi_proof: Vec, + pub multi_proof: Vec, /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. - execution_payload_branch: Vec, + pub execution_payload_branch: Vec, } /// Holds the neccessary proofs required to verify a header in the `block_roots` field /// either in [`BeaconState`] or [`HistoricalBatch`]. -struct BlockRootsProof { +#[derive(Debug, Clone)] +pub struct BlockRootsProof { /// Generalized index of the header in the `block_roots` list. - block_header_index: u64, + pub block_header_index: u64, /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` - block_header_branch: Vec, + pub block_header_branch: Vec, } /// The block header ancestry proof, this is an enum because the header may either exist in /// `state.block_roots` or `state.historical_roots`. -enum AncestryProof { +#[derive(Debug, Clone)] +pub enum AncestryProof { /// This variant defines the proof data for a beacon chain header in the `state.block_roots` BlockRoots { /// Proof for the header in `state.block_roots` @@ -58,49 +58,53 @@ enum AncestryProof { /// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized /// header. -struct AncestorBlock { +#[derive(Debug, Clone)] +pub struct AncestorBlock { /// The actual beacon chain header - header: BeaconBlockHeader, + pub header: BeaconBlockHeader, /// Associated execution header proofs - execution_payload: ExecutionPayloadProof, + pub execution_payload: ExecutionPayloadProof, /// Ancestry proofs of the beacon chain header. - ancestry_proof: AncestryProof, + pub ancestry_proof: AncestryProof, } /// Holds the latest sync committee as well as an ssz proof for it's existence /// in a finalized header. -struct SyncCommitteeUpdate { +#[derive(Debug, Clone)] +pub struct SyncCommitteeUpdate { // actual sync committee - next_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, // sync committee, ssz merkle proof. - next_sync_committee_branch: [Hash32; NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2], + pub next_sync_committee_branch: [Hash32; NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2], } /// Minimum state required by the light client to validate new sync committee attestations -struct LightClientState { +#[derive(Debug, Clone)] +pub struct LightClientState { /// The latest recorded finalized header - finalized_header: BeaconBlockHeader, + pub finalized_header: BeaconBlockHeader, // Sync committees corresponding to the finalized header - current_sync_committee: SyncCommittee, - next_sync_committee: SyncCommittee, + pub current_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, } /// Data required to advance the state of the light client. -struct LightClientUpdate { +#[derive(Debug, Clone)] +pub struct LightClientUpdate { /// the header that the sync committee signed - attested_header: BeaconBlockHeader, + pub attested_header: BeaconBlockHeader, /// the sync committee has potentially changed, here's an ssz proof for that. - sync_committee_update: Option>, + pub sync_committee_update: Option>, /// the actual header which was finalized by the ethereum attestation protocol. - finalized_header: BeaconBlockHeader, + pub finalized_header: BeaconBlockHeader, /// execution payload of the finalized header - execution_payload: ExecutionPayloadProof, + pub execution_payload: ExecutionPayloadProof, /// the ssz merkle proof for this header in the attested header, finalized headers lag by 2 epochs. - finality_branch: [Hash32; FINALIZED_ROOT_INDEX_FLOOR_LOG_2], + pub finality_branch: [Hash32; FINALIZED_ROOT_INDEX_FLOOR_LOG_2], /// signature & participation bits - sync_aggregate: SyncAggregate, + pub sync_aggregate: SyncAggregate, /// slot at which signature was produced - signature_slot: Slot, + pub signature_slot: Slot, /// ancestors of the finalized block to be verified, may be empty. - ancestor_blocks: Vec, + pub ancestor_blocks: Vec, } From cd71cd19460dd68fbaab5f0c2abb4f6a07ea609c Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 10 Jan 2023 11:39:33 +0100 Subject: [PATCH 009/182] cargo fmt --- light-client-primitives/src/types.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index ab8f0ee12..94ef450d7 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -1,6 +1,9 @@ -use ethereum_consensus::primitives::{Hash32, Slot}; use alloc::vec::Vec; -use ethereum_consensus::altair::{BeaconBlockHeader, SyncAggregate, SyncCommittee, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, FINALIZED_ROOT_INDEX_FLOOR_LOG_2}; +use ethereum_consensus::altair::{ + BeaconBlockHeader, SyncAggregate, SyncCommittee, FINALIZED_ROOT_INDEX_FLOOR_LOG_2, + NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, +}; +use ethereum_consensus::primitives::{Hash32, Slot}; /// This holds the relevant data required to prove the state root in the execution payload. #[derive(Debug, Clone)] @@ -16,7 +19,6 @@ pub struct ExecutionPayloadProof { pub execution_payload_branch: Vec, } - /// Holds the neccessary proofs required to verify a header in the `block_roots` field /// either in [`BeaconState`] or [`HistoricalBatch`]. #[derive(Debug, Clone)] From ed209e45816b1297a1ab0d87a91a20a8038bafc6 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 10 Jan 2023 23:35:02 +0100 Subject: [PATCH 010/182] the remaining utility functions --- light-client-primitives/Cargo.toml | 1 + light-client-primitives/src/lib.rs | 1 + light-client-primitives/src/util.rs | 34 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 light-client-primitives/src/util.rs diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index af8570aa7..f8e4197a3 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -8,3 +8,4 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } +base2 = {version="0.2.2", default-features=false} diff --git a/light-client-primitives/src/lib.rs b/light-client-primitives/src/lib.rs index 5c785ebad..d28e53bb2 100644 --- a/light-client-primitives/src/lib.rs +++ b/light-client-primitives/src/lib.rs @@ -4,3 +4,4 @@ extern crate alloc; pub mod types; +pub mod util; diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs new file mode 100644 index 000000000..063643889 --- /dev/null +++ b/light-client-primitives/src/util.rs @@ -0,0 +1,34 @@ +use base2::Base2; +use ethereum_consensus::altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD; +use ethereum_consensus::configs::mainnet::{ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION}; +use ethereum_consensus::phase0::mainnet::SLOTS_PER_EPOCH; + +/// Calculate the subtree index from the ``generalized_index`` +pub fn get_subtree_index(generalized_index: u64) -> u64 { + generalized_index % 2 ^ (generalized_index.floor_log2() as u64) +} + +/// Return the sync committe period at the given ``epoch`` +pub fn compute_sync_committee_period(epoch: u64) -> u64 { + epoch / EPOCHS_PER_SYNC_COMMITTEE_PERIOD +} + +/// Return the epoch number at ``slot``. +pub fn compute_epoch_at_slot(slot: u64) -> u64 { + slot / SLOTS_PER_EPOCH +} + +/// Return the fork version at the given ``epoch``. +pub fn compute_fork_version(epoch: u64) -> [u8; 4] { + if epoch >= ALTAIR_FORK_EPOCH { + ALTAIR_FORK_VERSION + } else { + GENESIS_FORK_VERSION + } +} + +/// Return the sync committee period at ``slot`` +pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { + compute_sync_committee_period(compute_epoch_at_slot(slot)) +} + From 8a7335c6565879885f9bd48a070cca8a7e111b75 Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 11 Jan 2023 16:11:29 +0100 Subject: [PATCH 011/182] change fork import --- light-client-primitives/src/types.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index 94ef450d7..083ada75a 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; -use ethereum_consensus::altair::{ - BeaconBlockHeader, SyncAggregate, SyncCommittee, FINALIZED_ROOT_INDEX_FLOOR_LOG_2, - NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, +use ethereum_consensus::altair::{FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2}; +use ethereum_consensus::bellatrix::{ + BeaconBlockHeader, SyncAggregate, SyncCommittee }; use ethereum_consensus::primitives::{Hash32, Slot}; From 2c41d4121ac12f2b80e5a20df4491eb56af40663 Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 11 Jan 2023 21:26:45 +0100 Subject: [PATCH 012/182] init verifier with struct and errors --- light-client-primitives/Cargo.toml | 2 +- light-client-primitives/src/types.rs | 6 ++--- light-client-primitives/src/util.rs | 5 ++-- light-client-verifier/Cargo.toml | 5 +++- light-client-verifier/src/lib.rs | 16 +++--------- light-client-verifier/src/light_client.rs | 30 +++++++++++++++++++++++ 6 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 light-client-verifier/src/light_client.rs diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index f8e4197a3..a94a97281 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "eth-beacon-light-client-primitives" +name = "light-client-primitives" version = "0.1.0" edition = "2021" authors = ["Polytope Labs"] diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index 083ada75a..d665a7120 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -1,8 +1,8 @@ use alloc::vec::Vec; -use ethereum_consensus::altair::{FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2}; -use ethereum_consensus::bellatrix::{ - BeaconBlockHeader, SyncAggregate, SyncCommittee +use ethereum_consensus::altair::{ + FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, }; +use ethereum_consensus::bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}; use ethereum_consensus::primitives::{Hash32, Slot}; /// This holds the relevant data required to prove the state root in the execution payload. diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index 063643889..a6fe7a10b 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -1,6 +1,8 @@ use base2::Base2; use ethereum_consensus::altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD; -use ethereum_consensus::configs::mainnet::{ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION}; +use ethereum_consensus::configs::mainnet::{ + ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION, +}; use ethereum_consensus::phase0::mainnet::SLOTS_PER_EPOCH; /// Calculate the subtree index from the ``generalized_index`` @@ -31,4 +33,3 @@ pub fn compute_fork_version(epoch: u64) -> [u8; 4] { pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { compute_sync_committee_period(compute_epoch_at_slot(slot)) } - diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index ddb6e0fd0..aedd24a6b 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "eth-beacon-light-client-verifier" +name = "light-client-verifier" version = "0.1.0" edition = "2021" authors = ["Polytope Labs"] @@ -7,3 +7,6 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } +base2 = {version="0.2.2", default-features=false} +light-client-primitives = {path="../light-client-primitives"} diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index 7d12d9af8..3dfb4bbd6 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -1,14 +1,6 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +#![cfg_attr(not(feature = "std"), no_std)] -#[cfg(test)] -mod tests { - use super::*; +#[cfg(not(feature = "std"))] +extern crate alloc; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod light_client; diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs new file mode 100644 index 000000000..6c963b6f9 --- /dev/null +++ b/light-client-verifier/src/light_client.rs @@ -0,0 +1,30 @@ +use core::fmt::{Display, Formatter}; +use light_client_primitives::types::{LightClientState, LightClientUpdate}; + +#[derive(Debug)] +pub enum Error { + SyncCommitteeParticiapntsTooLow, + InvalidUpdate, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Error::SyncCommitteeParticiapntsTooLow => { + write!(f, "Sync committee participants are too low") + } + Error::InvalidUpdate => write!(f, "Invalid update"), + } + } +} + +struct EthLightClient {} + +impl EthLightClient { + pub fn verify_sync_committee_attestation( + trusted_state: LightClientState, + update: LightClientUpdate, + ) -> Result<(), Error> { + Ok(()) + } +} From e0e26bc85e5411d950d604de3e5ae044e918500d Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 12 Jan 2023 00:01:28 +0100 Subject: [PATCH 013/182] further implementation --- light-client-primitives/Cargo.toml | 1 + light-client-primitives/src/util.rs | 7 ++ light-client-verifier/Cargo.toml | 2 + light-client-verifier/src/error.rs | 28 ++++++ light-client-verifier/src/lib.rs | 1 + light-client-verifier/src/light_client.rs | 110 ++++++++++++++++++---- 6 files changed, 129 insertions(+), 20 deletions(-) create mode 100644 light-client-verifier/src/error.rs diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index a94a97281..fddf74dde 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -9,3 +9,4 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } base2 = {version="0.2.2", default-features=false} +ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index a6fe7a10b..b0af478f6 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -4,6 +4,8 @@ use ethereum_consensus::configs::mainnet::{ ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION, }; use ethereum_consensus::phase0::mainnet::SLOTS_PER_EPOCH; +use ethereum_consensus::primitives::Root; +use ssz_rs::Node; /// Calculate the subtree index from the ``generalized_index`` pub fn get_subtree_index(generalized_index: u64) -> u64 { @@ -33,3 +35,8 @@ pub fn compute_fork_version(epoch: u64) -> [u8; 4] { pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { compute_sync_committee_period(compute_epoch_at_slot(slot)) } + +// TODO: We probably need to change this +pub fn genesis_validator_root() -> Root { + Node::from_bytes([0u8; 32]).into() +} diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index aedd24a6b..6652f1f76 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -10,3 +10,5 @@ authors = ["Polytope Labs"] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives"} +ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } +milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } diff --git a/light-client-verifier/src/error.rs b/light-client-verifier/src/error.rs new file mode 100644 index 000000000..7295018af --- /dev/null +++ b/light-client-verifier/src/error.rs @@ -0,0 +1,28 @@ +use core::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum Error { + SyncCommitteeParticiapntsTooLow, + InvalidUpdate, + DomainError, + FastAggregateError(ethereum_consensus::crypto::Error), +} + +impl From for Error { + fn from(error: ethereum_consensus::crypto::Error) -> Self { + Error::FastAggregateError(error) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Error::SyncCommitteeParticiapntsTooLow => { + write!(f, "Sync committee participants are too low") + } + Error::InvalidUpdate => write!(f, "Invalid update"), + Error::DomainError => write!(f, "Couldn't get domain"), + Error::FastAggregateError(err) => write!(f, "Fast aggregate error"), + } + } +} diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index 3dfb4bbd6..3fc6cd500 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -3,4 +3,5 @@ #[cfg(not(feature = "std"))] extern crate alloc; +pub mod error; pub mod light_client; diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 6c963b6f9..e5acaf097 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -1,30 +1,100 @@ +use crate::error::Error; +use alloc::vec::Vec; use core::fmt::{Display, Formatter}; -use light_client_primitives::types::{LightClientState, LightClientUpdate}; +use ethereum_consensus::altair::mainnet::SYNC_COMMITTEE_SIZE; +use ethereum_consensus::bellatrix::compute_domain; +use ethereum_consensus::domains::DomainType; +use ethereum_consensus::primitives::Root; +use ethereum_consensus::signing::compute_signing_root; +use ethereum_consensus::state_transition::Context; +use light_client_primitives::util::{ + compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, + genesis_validator_root, +}; +use ssz_rs::Node; -#[derive(Debug)] -pub enum Error { - SyncCommitteeParticiapntsTooLow, - InvalidUpdate, -} +pub type LightClientState = light_client_primitives::types::LightClientState; +pub type LightClientUpdate = light_client_primitives::types::LightClientUpdate; -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - Error::SyncCommitteeParticiapntsTooLow => { - write!(f, "Sync committee participants are too low") - } - Error::InvalidUpdate => write!(f, "Invalid update"), - } - } -} +//TODO: we might change this +const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; -struct EthLightClient {} +pub struct EthLightClient {} impl EthLightClient { - pub fn verify_sync_committee_attestation( - trusted_state: LightClientState, - update: LightClientUpdate, + /// This function simply verifies a sync committee's attestation & it's finalized counterpart. + pub fn verify_sync_committee_attestation( + state: LightClientState, + mut update: LightClientUpdate, ) -> Result<(), Error> { + // Verify sync committee has super majority participants + let sync_committee_bits = update.sync_aggregate.sync_committee_bits; + let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; + if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { + Err(Error::SyncCommitteeParticiapntsTooLow)? + } + + // Verify update does not skip a sync committee period + let is_valid_update = update.signature_slot > update.attested_header.slot + && update.attested_header.slot >= update.finalized_header.slot; + if !is_valid_update { + Err(Error::InvalidUpdate)? + } + + let state_period = compute_sync_committee_period_at_slot(state.finalized_header.slot); + let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); + if !(state_period..=state_period + 1).contains(&update_signature_period) { + Err(Error::InvalidUpdate)? + } + + // Verify update is relevant + let update_attested_period = + compute_sync_committee_period_at_slot(update.attested_header.slot); + let update_has_next_sync_committee = + update.sync_committee_update.is_some() && update_attested_period == state_period; + + if !(update.attested_header.slot > state.finalized_header.slot + || update_has_next_sync_committee) + { + Err(Error::InvalidUpdate)? + } + + // Verify sync committee aggregate signature + let sync_committee = if update_signature_period == state_period { + state.current_sync_committee + } else { + state.next_sync_committee + }; + + let sync_committee_pubkeys = sync_committee.public_keys; + + let participant_pubkeys = sync_committee_bits + .iter() + .zip(sync_committee_pubkeys.iter()) + .filter_map(|(bit, key)| if *bit { Some(key) } else { None }) + .collect::>(); + + let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); + //TODO: we probably need to construct context + let domain = compute_domain( + DOMAIN_SYNC_COMMITTEE, + Some(fork_version), + Some(genesis_validator_root()), + &Context::default(), + ); + + if domain.is_err() { + Err(Error::InvalidUpdate)? + } + let signing_root = compute_signing_root(&mut update.attested_header, domain.unwrap()); + + //TODO: not sure if we are to use update to get the signature + ethereum_consensus::crypto::fast_aggregate_verify( + &*participant_pubkeys, + signing_root.unwrap().as_bytes(), + &update.sync_aggregate.sync_committee_signature, + )?; + Ok(()) } } From dc470743e0e0c583a0c1bc95f4647aea6a011c07 Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 13 Jan 2023 01:35:48 +0100 Subject: [PATCH 014/182] switch to seun's branch work on std for now finish verify_sync_committee_attestation() function --- light-client-primitives/src/util.rs | 11 +- light-client-verifier/Cargo.toml | 4 +- light-client-verifier/src/error.rs | 2 + light-client-verifier/src/lib.rs | 1 + light-client-verifier/src/light_client.rs | 308 +++++++++++++++++++++- 5 files changed, 316 insertions(+), 10 deletions(-) diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index b0af478f6..41ec198d2 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -4,8 +4,8 @@ use ethereum_consensus::configs::mainnet::{ ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION, }; use ethereum_consensus::phase0::mainnet::SLOTS_PER_EPOCH; -use ethereum_consensus::primitives::Root; -use ssz_rs::Node; +use ethereum_consensus::primitives::{Hash32, Root}; +use ssz_rs::{MerkleizationError, Node}; /// Calculate the subtree index from the ``generalized_index`` pub fn get_subtree_index(generalized_index: u64) -> u64 { @@ -40,3 +40,10 @@ pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { pub fn genesis_validator_root() -> Root { Node::from_bytes([0u8; 32]).into() } + +pub fn hash_tree_root( + mut object: T, +) -> Result { + let root = object.hash_tree_root()?.try_into().unwrap(); + Ok(root) +} diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 6652f1f76..72b303c1b 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives"} -ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } diff --git a/light-client-verifier/src/error.rs b/light-client-verifier/src/error.rs index 7295018af..5cb2d2a24 100644 --- a/light-client-verifier/src/error.rs +++ b/light-client-verifier/src/error.rs @@ -6,6 +6,7 @@ pub enum Error { InvalidUpdate, DomainError, FastAggregateError(ethereum_consensus::crypto::Error), + InvalidMerkleBranch, } impl From for Error { @@ -23,6 +24,7 @@ impl Display for Error { Error::InvalidUpdate => write!(f, "Invalid update"), Error::DomainError => write!(f, "Couldn't get domain"), Error::FastAggregateError(err) => write!(f, "Fast aggregate error"), + Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), } } } diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index 3fc6cd500..5d23718fb 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(adt_const_params)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index e5acaf097..7de1921ae 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -1,5 +1,7 @@ use crate::error::Error; use alloc::vec::Vec; +use base2::Base2; +use core::borrow::Borrow; use core::fmt::{Display, Formatter}; use ethereum_consensus::altair::mainnet::SYNC_COMMITTEE_SIZE; use ethereum_consensus::bellatrix::compute_domain; @@ -7,23 +9,37 @@ use ethereum_consensus::domains::DomainType; use ethereum_consensus::primitives::Root; use ethereum_consensus::signing::compute_signing_root; use ethereum_consensus::state_transition::Context; +use light_client_primitives::types::AncestryProof; use light_client_primitives::util::{ compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, - genesis_validator_root, + genesis_validator_root, get_subtree_index, hash_tree_root, }; -use ssz_rs::Node; +use ssz_rs::prelude::is_valid_merkle_branch; +use ssz_rs::Merkleized; +use ssz_rs::{calculate_merkle_root, calculate_multi_merkle_root, GeneralizedIndex, Node}; pub type LightClientState = light_client_primitives::types::LightClientState; pub type LightClientUpdate = light_client_primitives::types::LightClientUpdate; -//TODO: we might change this +//TODO: we need to change this const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; +//TODO: we need to change all these consts to the right value +const FINALIZED_ROOT_INDEX: u64 = 0; +const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: GeneralizedIndex = GeneralizedIndex(0 as usize); +const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: GeneralizedIndex = GeneralizedIndex(0 as usize); +const EXECUTION_PAYLOAD_INDEX: u64 = 0; +const NEXT_SYNC_COMMITTEE_INDEX: u64 = 0; + pub struct EthLightClient {} impl EthLightClient { /// This function simply verifies a sync committee's attestation & it's finalized counterpart. - pub fn verify_sync_committee_attestation( + pub fn verify_sync_committee_attestation< + const BLOCK_ROOTS_INDEX: u64, + const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: GeneralizedIndex, + const HISTORICAL_ROOTS_INDEX: u64, + >( state: LightClientState, mut update: LightClientUpdate, ) -> Result<(), Error> { @@ -61,9 +77,9 @@ impl EthLightClient { // Verify sync committee aggregate signature let sync_committee = if update_signature_period == state_period { - state.current_sync_committee + state.clone().current_sync_committee } else { - state.next_sync_committee + state.clone().next_sync_committee }; let sync_committee_pubkeys = sync_committee.public_keys; @@ -95,6 +111,286 @@ impl EthLightClient { &update.sync_aggregate.sync_committee_signature, )?; + // Verify that the `finality_branch` confirms `finalized_header` + // to match the finalized checkpoint root saved in the state of `attested_header`. + // Note that the genesis finalized checkpoint root is represented as a zero hash. + let finalized_root = &Node::from_bytes( + light_client_primitives::util::hash_tree_root(update.finalized_header.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ); + + let branch = update + .finality_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + finalized_root, + branch.iter(), + FINALIZED_ROOT_INDEX.floor_log2() as usize, + get_subtree_index(FINALIZED_ROOT_INDEX) as usize, + &Node::from_bytes( + update + .attested_header + .state_root + .as_ref() + .try_into() + .unwrap(), + ), + ); + + if is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + + // verify the associated execution header of the finalized beacon header. + let mut execution_payload = update.execution_payload; + let multi_proof_vec = execution_payload.multi_proof; + let multi_proof_nodes = multi_proof_vec + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), + execution_payload.block_number.hash_tree_root().unwrap(), + ], + &multi_proof_nodes, + &[ + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + ], + ); + + let execution_payload_branch = execution_payload + .execution_payload_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + get_subtree_index(EXECUTION_PAYLOAD_INDEX) as usize, + &Node::from_bytes( + update + .finalized_header + .clone() + .body_root + .as_ref() + .try_into() + .unwrap(), + ), + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + + if let Some(sync_committee_update) = update.sync_committee_update { + if update_attested_period == state_period { + if sync_committee_update.next_sync_committee != state.clone().next_sync_committee { + Err(Error::InvalidUpdate)? + } + } + + let next_sync_committee_branch = sync_committee_update + .next_sync_committee_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &Node::from_bytes( + light_client_primitives::util::hash_tree_root( + sync_committee_update.next_sync_committee, + ) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize, + &Node::from_bytes( + update + .attested_header + .state_root + .as_ref() + .try_into() + .unwrap(), + ), + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + } + + // verify the ancestry proofs + for ancestor in update.ancestor_blocks { + match ancestor.ancestry_proof { + AncestryProof::BlockRoots { + block_roots_proof, + block_roots_branch, + } => { + let block_header_branch = block_roots_proof + .block_header_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let block_roots_root = calculate_merkle_root( + &Node::from_bytes( + hash_tree_root(ancestor.header.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + &*block_header_branch, + &GeneralizedIndex(block_roots_proof.block_header_index as usize), + ); + + let block_roots_branch_node = block_roots_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &block_roots_root, + block_roots_branch_node.iter(), + BLOCK_ROOTS_INDEX.floor_log2() as usize, + get_subtree_index(BLOCK_ROOTS_INDEX) as usize, + &Node::from_bytes( + update + .finalized_header + .state_root + .as_ref() + .try_into() + .unwrap(), + ), + ); + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + } + AncestryProof::HistoricalRoots { + block_roots_proof, + historical_batch_proof, + historical_roots_proof, + historical_roots_index, + historical_roots_branch, + } => { + let block_header_branch = block_roots_proof + .block_header_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let block_roots_root = calculate_merkle_root( + &Node::from_bytes( + hash_tree_root(ancestor.header.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + &block_header_branch, + &GeneralizedIndex(block_roots_proof.block_header_index as usize), + ); + + let historical_batch_proof_nodes = historical_batch_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let historical_batch_root = calculate_merkle_root( + &block_roots_root, + &historical_batch_proof_nodes, + &HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, + ); + + let historical_roots_proof_nodes = historical_roots_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let historical_roots_root = calculate_merkle_root( + &historical_batch_root, + &historical_roots_proof_nodes, + &GeneralizedIndex(historical_roots_index as usize), + ); + + let historical_roots_branch_nodes = historical_roots_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &historical_roots_root, + historical_roots_branch_nodes.iter(), + HISTORICAL_ROOTS_INDEX.floor_log2() as usize, + get_subtree_index(HISTORICAL_ROOTS_INDEX) as usize, + &Node::from_bytes( + update + .finalized_header + .state_root + .as_ref() + .try_into() + .unwrap(), + ), + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + } + }; + + // verify the associated execution paylaod header. + let execution_payload = ancestor.execution_payload; + let multi_proof = execution_payload + .multi_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), + Node::from_bytes( + hash_tree_root(execution_payload.block_number) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + ], + &multi_proof, + &[ + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + ], + ); + + let execution_payload_branch = execution_payload + .execution_payload_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + get_subtree_index(EXECUTION_PAYLOAD_INDEX) as usize, + &Node::from_bytes(ancestor.header.body_root.as_ref().try_into().unwrap()), + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + } Ok(()) } } From 0090fd40d57474a7413a25bb50b76ebd494af221 Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 13 Jan 2023 01:47:59 +0100 Subject: [PATCH 015/182] use generic const types instead push cargo lock --- Cargo.lock | 1405 +++++++++++++++++++++ light-client-verifier/src/light_client.rs | 16 +- 2 files changed, 1411 insertions(+), 10 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..a083d13b4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1405 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "amcl" +version = "0.3.0" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "as-any" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +dependencies = [ + "int", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +dependencies = [ + "base64", + "bs58", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "ethereum-consensus" +version = "0.1.1" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#df115f52c3ab4c32f89c188607b6130687a561e7" +dependencies = [ + "async-stream", + "bs58", + "enr", + "error-chain", + "getrandom", + "hashbrown", + "hex", + "integer-sqrt", + "milagro_bls", + "multiaddr", + "multihash", + "rand", + "serde", + "serde_json", + "sha2 0.9.9", + "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fields-iter" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edfbdc0f742f479dece939ba9c3999962c95d324849ae764dfcebd419fb884b" +dependencies = [ + "fields-iter-macros", +] + +[[package]] +name = "fields-iter-macros" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d7d1a714a91ae8b40f541b455eb63c84157511ec30bee8649ca8db27453412" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "ics15-ethereum" +version = "0.1.0" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "int" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +dependencies = [ + "num-traits", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "light-client-primitives" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", +] + +[[package]] +name = "light-client-verifier" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "light-client-primitives", + "milagro_bls", + "ssz-rs 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "milagro_bls" +version = "1.5.1" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +dependencies = [ + "amcl", + "rand", + "zeroize", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution#ea113b48f3ad134603f11c9be9f5fc696d30c259" +dependencies = [ + "bitvec", + "hex", + "lazy_static", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", + "thiserror", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#6969b00ea8244c9282b8a580037d700f787c614c" +dependencies = [ + "as-any", + "bitvec", + "fields-iter", + "hex", + "itertools", + "lazy_static", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", + "thiserror", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution#ea113b48f3ad134603f11c9be9f5fc696d30c259" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#6969b00ea8244c9282b8a580037d700f787c614c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 7de1921ae..f01c62759 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -21,21 +21,17 @@ use ssz_rs::{calculate_merkle_root, calculate_multi_merkle_root, GeneralizedInde pub type LightClientState = light_client_primitives::types::LightClientState; pub type LightClientUpdate = light_client_primitives::types::LightClientUpdate; -//TODO: we need to change this -const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; - -//TODO: we need to change all these consts to the right value -const FINALIZED_ROOT_INDEX: u64 = 0; -const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: GeneralizedIndex = GeneralizedIndex(0 as usize); -const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: GeneralizedIndex = GeneralizedIndex(0 as usize); -const EXECUTION_PAYLOAD_INDEX: u64 = 0; -const NEXT_SYNC_COMMITTEE_INDEX: u64 = 0; - pub struct EthLightClient {} impl EthLightClient { /// This function simply verifies a sync committee's attestation & it's finalized counterpart. pub fn verify_sync_committee_attestation< + const DOMAIN_SYNC_COMMITTEE: DomainType, + const FINALIZED_ROOT_INDEX: u64, + const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: GeneralizedIndex, + const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: GeneralizedIndex, + const EXECUTION_PAYLOAD_INDEX: u64, + const NEXT_SYNC_COMMITTEE_INDEX: u64, const BLOCK_ROOTS_INDEX: u64, const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: GeneralizedIndex, const HISTORICAL_ROOTS_INDEX: u64, From b9f5c9502aa60bc46fb59130e0df15c3cf437c59 Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 13 Jan 2023 01:52:35 +0100 Subject: [PATCH 016/182] GENESIS_VALIDATORS_ROOT generic const --- light-client-primitives/src/util.rs | 7 ++----- light-client-verifier/src/light_client.rs | 5 +++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index 41ec198d2..63851cb8f 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -36,11 +36,8 @@ pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { compute_sync_committee_period(compute_epoch_at_slot(slot)) } -// TODO: We probably need to change this -pub fn genesis_validator_root() -> Root { - Node::from_bytes([0u8; 32]).into() -} - +/// method for hashing objects into a single root by utilizing a hash tree structure, as defined in +/// the SSZ spec. pub fn hash_tree_root( mut object: T, ) -> Result { diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index f01c62759..b43f0cff3 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -12,7 +12,7 @@ use ethereum_consensus::state_transition::Context; use light_client_primitives::types::AncestryProof; use light_client_primitives::util::{ compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, - genesis_validator_root, get_subtree_index, hash_tree_root, + get_subtree_index, hash_tree_root, }; use ssz_rs::prelude::is_valid_merkle_branch; use ssz_rs::Merkleized; @@ -35,6 +35,7 @@ impl EthLightClient { const BLOCK_ROOTS_INDEX: u64, const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: GeneralizedIndex, const HISTORICAL_ROOTS_INDEX: u64, + const GENESIS_VALIDATORS_ROOT: Root, >( state: LightClientState, mut update: LightClientUpdate, @@ -91,7 +92,7 @@ impl EthLightClient { let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), - Some(genesis_validator_root()), + Some(GENESIS_VALIDATORS_ROOT), &Context::default(), ); From 59627b3e32738e90b2888f11e09bde1ef5c5412d Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 13 Jan 2023 19:53:27 +0100 Subject: [PATCH 017/182] return error result/error instead --- light-client-primitives/src/util.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index 63851cb8f..a0f34f5d6 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -38,9 +38,9 @@ pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { /// method for hashing objects into a single root by utilizing a hash tree structure, as defined in /// the SSZ spec. -pub fn hash_tree_root( - mut object: T, -) -> Result { - let root = object.hash_tree_root()?.try_into().unwrap(); - Ok(root) -} + pub fn hash_tree_root( + mut object: T, + ) -> Result { + let root = object.hash_tree_root()?; + Ok(root) + } From 4641f1c1c8aff1bd6b0532420bdfd4fc46621e8e Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 13 Jan 2023 21:57:10 +0100 Subject: [PATCH 018/182] remove generic consts --- light-client-primitives/src/types.rs | 14 +++++- light-client-primitives/src/util.rs | 12 ++--- light-client-verifier/src/light_client.rs | 56 +++++++++++------------ 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index d665a7120..d4e9b1694 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -3,7 +3,19 @@ use ethereum_consensus::altair::{ FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, }; use ethereum_consensus::bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}; -use ethereum_consensus::primitives::{Hash32, Slot}; +use ethereum_consensus::domains::DomainType; +use ethereum_consensus::primitives::{Hash32, Root, Slot}; + +pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; +pub const FINALIZED_ROOT_INDEX: u64 = 0; +pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 0; +pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 0; +pub const EXECUTION_PAYLOAD_INDEX: u64 = 0; +pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 0; +pub const BLOCK_ROOTS_INDEX: u64 = 0; +pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; +pub const HISTORICAL_ROOTS_INDEX: u64 = 0; +pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = [0u8; 32]; /// This holds the relevant data required to prove the state root in the execution payload. #[derive(Debug, Clone)] diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index a0f34f5d6..b272ce849 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -38,9 +38,9 @@ pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { /// method for hashing objects into a single root by utilizing a hash tree structure, as defined in /// the SSZ spec. - pub fn hash_tree_root( - mut object: T, - ) -> Result { - let root = object.hash_tree_root()?; - Ok(root) - } +pub fn hash_tree_root( + mut object: T, +) -> Result { + let root = object.hash_tree_root()?; + Ok(root) +} diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index b43f0cff3..0e807adf6 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -9,7 +9,12 @@ use ethereum_consensus::domains::DomainType; use ethereum_consensus::primitives::Root; use ethereum_consensus::signing::compute_signing_root; use ethereum_consensus::state_transition::Context; -use light_client_primitives::types::AncestryProof; +use light_client_primitives::types::{ + AncestryProof, BLOCK_ROOTS_INDEX, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, + GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, + NEXT_SYNC_COMMITTEE_INDEX, +}; use light_client_primitives::util::{ compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, get_subtree_index, hash_tree_root, @@ -21,24 +26,13 @@ use ssz_rs::{calculate_merkle_root, calculate_multi_merkle_root, GeneralizedInde pub type LightClientState = light_client_primitives::types::LightClientState; pub type LightClientUpdate = light_client_primitives::types::LightClientUpdate; -pub struct EthLightClient {} +pub struct EthLightClient; impl EthLightClient { /// This function simply verifies a sync committee's attestation & it's finalized counterpart. - pub fn verify_sync_committee_attestation< - const DOMAIN_SYNC_COMMITTEE: DomainType, - const FINALIZED_ROOT_INDEX: u64, - const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: GeneralizedIndex, - const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: GeneralizedIndex, - const EXECUTION_PAYLOAD_INDEX: u64, - const NEXT_SYNC_COMMITTEE_INDEX: u64, - const BLOCK_ROOTS_INDEX: u64, - const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: GeneralizedIndex, - const HISTORICAL_ROOTS_INDEX: u64, - const GENESIS_VALIDATORS_ROOT: Root, - >( - state: LightClientState, - mut update: LightClientUpdate, + pub fn verify_sync_committee_attestation( + trusted_state: LightClientState, + update: LightClientUpdate, ) -> Result<(), Error> { // Verify sync committee has super majority participants let sync_committee_bits = update.sync_aggregate.sync_committee_bits; @@ -54,7 +48,8 @@ impl EthLightClient { Err(Error::InvalidUpdate)? } - let state_period = compute_sync_committee_period_at_slot(state.finalized_header.slot); + let state_period = + compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); if !(state_period..=state_period + 1).contains(&update_signature_period) { Err(Error::InvalidUpdate)? @@ -66,7 +61,7 @@ impl EthLightClient { let update_has_next_sync_committee = update.sync_committee_update.is_some() && update_attested_period == state_period; - if !(update.attested_header.slot > state.finalized_header.slot + if !(update.attested_header.slot > trusted_state.finalized_header.slot || update_has_next_sync_committee) { Err(Error::InvalidUpdate)? @@ -74,9 +69,9 @@ impl EthLightClient { // Verify sync committee aggregate signature let sync_committee = if update_signature_period == state_period { - state.clone().current_sync_committee + trusted_state.clone().current_sync_committee } else { - state.clone().next_sync_committee + trusted_state.clone().next_sync_committee }; let sync_committee_pubkeys = sync_committee.public_keys; @@ -92,14 +87,17 @@ impl EthLightClient { let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), - Some(GENESIS_VALIDATORS_ROOT), + Some(Root::from_bytes( + GENESIS_VALIDATORS_ROOT.as_ref().try_into().unwrap(), + )), &Context::default(), ); if domain.is_err() { Err(Error::InvalidUpdate)? } - let signing_root = compute_signing_root(&mut update.attested_header, domain.unwrap()); + let signing_root = + compute_signing_root(&mut update.attested_header.clone(), domain.unwrap()); //TODO: not sure if we are to use update to get the signature ethereum_consensus::crypto::fast_aggregate_verify( @@ -158,8 +156,8 @@ impl EthLightClient { ], &multi_proof_nodes, &[ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), ], ); @@ -191,7 +189,9 @@ impl EthLightClient { if let Some(sync_committee_update) = update.sync_committee_update { if update_attested_period == state_period { - if sync_committee_update.next_sync_committee != state.clone().next_sync_committee { + if sync_committee_update.next_sync_committee + != trusted_state.clone().next_sync_committee + { Err(Error::InvalidUpdate)? } } @@ -308,7 +308,7 @@ impl EthLightClient { let historical_batch_root = calculate_merkle_root( &block_roots_root, &historical_batch_proof_nodes, - &HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, + &GeneralizedIndex(HISTORICAL_BATCH_BLOCK_ROOTS_INDEX as usize), ); let historical_roots_proof_nodes = historical_roots_proof @@ -366,8 +366,8 @@ impl EthLightClient { ], &multi_proof, &[ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), ], ); From 0300ba745b93e8f227abc46fade415fe92c16ca7 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 14 Jan 2023 12:16:18 +0100 Subject: [PATCH 019/182] return new LightClientState --- light-client-verifier/src/light_client.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 0e807adf6..21add1f37 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -33,7 +33,7 @@ impl EthLightClient { pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, - ) -> Result<(), Error> { + ) -> Result { // Verify sync committee has super majority participants let sync_committee_bits = update.sync_aggregate.sync_committee_bits; let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; @@ -187,7 +187,7 @@ impl EthLightClient { Err(Error::InvalidMerkleBranch)?; } - if let Some(sync_committee_update) = update.sync_committee_update { + if let Some(sync_committee_update) = update.sync_committee_update.clone() { if update_attested_period == state_period { if sync_committee_update.next_sync_committee != trusted_state.clone().next_sync_committee @@ -388,6 +388,13 @@ impl EthLightClient { Err(Error::InvalidMerkleBranch)?; } } - Ok(()) + + let new_light_client_state = LightClientState { + finalized_header: update.finalized_header.clone(), + current_sync_committee: trusted_state.current_sync_committee, + next_sync_committee: update.sync_committee_update.unwrap().next_sync_committee, + }; + + Ok(new_light_client_state) } } From 28606a44466268219e0506a70cfaa69f3968fedc Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 14 Jan 2023 12:56:19 +0100 Subject: [PATCH 020/182] put in the right consts values --- Cargo.lock | 1 + light-client-primitives/src/types.rs | 17 +++++++++-------- light-client-verifier/Cargo.toml | 1 + light-client-verifier/src/light_client.rs | 3 ++- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a083d13b4..2a1014b72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,6 +548,7 @@ version = "0.1.0" dependencies = [ "base2", "ethereum-consensus", + "hex", "light-client-primitives", "milagro_bls", "ssz-rs 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index d4e9b1694..08f1adf3a 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -7,15 +7,16 @@ use ethereum_consensus::domains::DomainType; use ethereum_consensus::primitives::{Hash32, Root, Slot}; pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; -pub const FINALIZED_ROOT_INDEX: u64 = 0; -pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 0; -pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 0; -pub const EXECUTION_PAYLOAD_INDEX: u64 = 0; -pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 0; -pub const BLOCK_ROOTS_INDEX: u64 = 0; +pub const FINALIZED_ROOT_INDEX: u64 = 105; +pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; +pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; +pub const EXECUTION_PAYLOAD_INDEX: u64 = 25; +pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; +pub const BLOCK_ROOTS_INDEX: u64 = 37; pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; -pub const HISTORICAL_ROOTS_INDEX: u64 = 0; -pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = [0u8; 32]; +pub const HISTORICAL_ROOTS_INDEX: u64 = 39; +pub const GENESIS_VALIDATORS_ROOT: &str = + "4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"; /// This holds the relevant data required to prove the state root in the execution payload. #[derive(Debug, Clone)] diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 72b303c1b..71ba43d96 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -12,3 +12,4 @@ base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives"} ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } +hex = {version = "0.4.3" } diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 21add1f37..2216aa762 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -83,12 +83,13 @@ impl EthLightClient { .collect::>(); let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); + let genesis_validators_root_bytes = hex::decode(GENESIS_VALIDATORS_ROOT).unwrap(); //TODO: we probably need to construct context let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), Some(Root::from_bytes( - GENESIS_VALIDATORS_ROOT.as_ref().try_into().unwrap(), + genesis_validators_root_bytes.try_into().unwrap(), )), &Context::default(), ); From affcee85a87c8ed95357c6968aed38e48df44ec1 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 17 Jan 2023 00:02:21 +0100 Subject: [PATCH 021/182] use hex_literal refractor cloning --- Cargo.lock | 8 ++++- light-client-primitives/Cargo.toml | 1 + light-client-primitives/src/types.rs | 4 +-- light-client-verifier/Cargo.toml | 3 +- light-client-verifier/src/error.rs | 4 +-- light-client-verifier/src/lib.rs | 1 - light-client-verifier/src/light_client.rs | 39 ++++++++++++----------- 7 files changed, 34 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a1014b72..b3b81c58b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,6 +432,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + [[package]] name = "hmac" version = "0.12.1" @@ -539,6 +545,7 @@ version = "0.1.0" dependencies = [ "base2", "ethereum-consensus", + "hex-literal", "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", ] @@ -548,7 +555,6 @@ version = "0.1.0" dependencies = [ "base2", "ethereum-consensus", - "hex", "light-client-primitives", "milagro_bls", "ssz-rs 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index fddf74dde..3852ccb25 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -10,3 +10,4 @@ authors = ["Polytope Labs"] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } base2 = {version="0.2.2", default-features=false} ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } +hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index 08f1adf3a..17b63b292 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -15,8 +15,8 @@ pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; pub const BLOCK_ROOTS_INDEX: u64 = 37; pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; pub const HISTORICAL_ROOTS_INDEX: u64 = 39; -pub const GENESIS_VALIDATORS_ROOT: &str = - "4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"; +pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); /// This holds the relevant data required to prove the state root in the execution payload. #[derive(Debug, Clone)] diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 71ba43d96..99fd897df 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -9,7 +9,6 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } base2 = {version="0.2.2", default-features=false} -light-client-primitives = {path="../light-client-primitives"} +light-client-primitives = {path="../light-client-primitives", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } -hex = {version = "0.4.3" } diff --git a/light-client-verifier/src/error.rs b/light-client-verifier/src/error.rs index 5cb2d2a24..cde16f2ba 100644 --- a/light-client-verifier/src/error.rs +++ b/light-client-verifier/src/error.rs @@ -2,7 +2,7 @@ use core::fmt::{Display, Formatter}; #[derive(Debug)] pub enum Error { - SyncCommitteeParticiapntsTooLow, + SyncCommitteeParticipantsTooLow, InvalidUpdate, DomainError, FastAggregateError(ethereum_consensus::crypto::Error), @@ -18,7 +18,7 @@ impl From for Error { impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { - Error::SyncCommitteeParticiapntsTooLow => { + Error::SyncCommitteeParticipantsTooLow => { write!(f, "Sync committee participants are too low") } Error::InvalidUpdate => write!(f, "Invalid update"), diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index 5d23718fb..3fc6cd500 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(adt_const_params)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 2216aa762..eb6aeeac4 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -38,7 +38,7 @@ impl EthLightClient { let sync_committee_bits = update.sync_aggregate.sync_committee_bits; let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { - Err(Error::SyncCommitteeParticiapntsTooLow)? + Err(Error::SyncCommitteeParticipantsTooLow)? } // Verify update does not skip a sync committee period @@ -69,9 +69,9 @@ impl EthLightClient { // Verify sync committee aggregate signature let sync_committee = if update_signature_period == state_period { - trusted_state.clone().current_sync_committee + trusted_state.current_sync_committee.clone() } else { - trusted_state.clone().next_sync_committee + trusted_state.next_sync_committee.clone() }; let sync_committee_pubkeys = sync_committee.public_keys; @@ -83,24 +83,19 @@ impl EthLightClient { .collect::>(); let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); - let genesis_validators_root_bytes = hex::decode(GENESIS_VALIDATORS_ROOT).unwrap(); //TODO: we probably need to construct context let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), Some(Root::from_bytes( - genesis_validators_root_bytes.try_into().unwrap(), + GENESIS_VALIDATORS_ROOT.try_into().unwrap(), )), &Context::default(), - ); + ) + .map_err(|_| Error::InvalidUpdate)?; - if domain.is_err() { - Err(Error::InvalidUpdate)? - } - let signing_root = - compute_signing_root(&mut update.attested_header.clone(), domain.unwrap()); + let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain); - //TODO: not sure if we are to use update to get the signature ethereum_consensus::crypto::fast_aggregate_verify( &*participant_pubkeys, signing_root.unwrap().as_bytes(), @@ -191,7 +186,7 @@ impl EthLightClient { if let Some(sync_committee_update) = update.sync_committee_update.clone() { if update_attested_period == state_period { if sync_committee_update.next_sync_committee - != trusted_state.clone().next_sync_committee + != trusted_state.next_sync_committee.clone() { Err(Error::InvalidUpdate)? } @@ -390,11 +385,19 @@ impl EthLightClient { } } - let new_light_client_state = LightClientState { - finalized_header: update.finalized_header.clone(), - current_sync_committee: trusted_state.current_sync_committee, - next_sync_committee: update.sync_committee_update.unwrap().next_sync_committee, - }; + let new_light_client_state = + if let Some(sync_committee_update) = update.sync_committee_update { + LightClientState { + finalized_header: update.finalized_header, + current_sync_committee: trusted_state.next_sync_committee, + next_sync_committee: sync_committee_update.next_sync_committee, + } + } else { + LightClientState { + finalized_header: update.finalized_header, + ..trusted_state + } + }; Ok(new_light_client_state) } From f07292f512118024549b953d6460aa1956a4ff42 Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 19 Jan 2023 10:49:51 +0100 Subject: [PATCH 022/182] use hex_literal refractor cloning --- light-client-primitives/src/util.rs | 2 +- light-client-verifier/src/error.rs | 4 ++ light-client-verifier/src/light_client.rs | 75 +++++++++++++++-------- 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/light-client-primitives/src/util.rs b/light-client-primitives/src/util.rs index b272ce849..6685bf4f5 100644 --- a/light-client-primitives/src/util.rs +++ b/light-client-primitives/src/util.rs @@ -40,7 +40,7 @@ pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { /// the SSZ spec. pub fn hash_tree_root( mut object: T, -) -> Result { +) -> Result { let root = object.hash_tree_root()?; Ok(root) } diff --git a/light-client-verifier/src/error.rs b/light-client-verifier/src/error.rs index cde16f2ba..6d703b41d 100644 --- a/light-client-verifier/src/error.rs +++ b/light-client-verifier/src/error.rs @@ -7,6 +7,8 @@ pub enum Error { DomainError, FastAggregateError(ethereum_consensus::crypto::Error), InvalidMerkleBranch, + InvalidRoot, + MerkleizationError, } impl From for Error { @@ -25,6 +27,8 @@ impl Display for Error { Error::DomainError => write!(f, "Couldn't get domain"), Error::FastAggregateError(err) => write!(f, "Fast aggregate error"), Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), + Error::InvalidRoot => write!(f, "Invalid root"), + Error::MerkleizationError => write!(f, "Merkleization error"), } } } diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index eb6aeeac4..496505585 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -88,7 +88,9 @@ impl EthLightClient { DOMAIN_SYNC_COMMITTEE, Some(fork_version), Some(Root::from_bytes( - GENESIS_VALIDATORS_ROOT.try_into().unwrap(), + GENESIS_VALIDATORS_ROOT + .try_into() + .map_err(|_| Error::InvalidRoot)?, )), &Context::default(), ) @@ -98,7 +100,7 @@ impl EthLightClient { ethereum_consensus::crypto::fast_aggregate_verify( &*participant_pubkeys, - signing_root.unwrap().as_bytes(), + signing_root.map_err(|_| Error::InvalidRoot)?.as_bytes(), &update.sync_aggregate.sync_committee_signature, )?; @@ -107,10 +109,10 @@ impl EthLightClient { // Note that the genesis finalized checkpoint root is represented as a zero hash. let finalized_root = &Node::from_bytes( light_client_primitives::util::hash_tree_root(update.finalized_header.clone()) - .unwrap() + .map_err(|_| Error::MerkleizationError)? .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ); let branch = update @@ -130,7 +132,7 @@ impl EthLightClient { .state_root .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), ); @@ -147,8 +149,17 @@ impl EthLightClient { .collect::>(); let execution_payload_root = calculate_multi_merkle_root( &[ - Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), - execution_payload.block_number.hash_tree_root().unwrap(), + Node::from_bytes( + execution_payload + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + execution_payload + .block_number + .hash_tree_root() + .map_err(|_| Error::InvalidRoot)?, ], &multi_proof_nodes, &[ @@ -175,7 +186,7 @@ impl EthLightClient { .body_root .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), ); @@ -184,12 +195,11 @@ impl EthLightClient { } if let Some(sync_committee_update) = update.sync_committee_update.clone() { - if update_attested_period == state_period { - if sync_committee_update.next_sync_committee + if update_attested_period == state_period + && sync_committee_update.next_sync_committee != trusted_state.next_sync_committee.clone() - { - Err(Error::InvalidUpdate)? - } + { + Err(Error::InvalidUpdate)? } let next_sync_committee_branch = sync_committee_update @@ -202,10 +212,10 @@ impl EthLightClient { light_client_primitives::util::hash_tree_root( sync_committee_update.next_sync_committee, ) - .unwrap() + .map_err(|_| Error::MerkleizationError)? .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), next_sync_committee_branch.iter(), NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, @@ -216,7 +226,7 @@ impl EthLightClient { .state_root .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), ); @@ -241,10 +251,10 @@ impl EthLightClient { let block_roots_root = calculate_merkle_root( &Node::from_bytes( hash_tree_root(ancestor.header.clone()) - .unwrap() + .map_err(|_| Error::MerkleizationError)? .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), &*block_header_branch, &GeneralizedIndex(block_roots_proof.block_header_index as usize), @@ -266,7 +276,7 @@ impl EthLightClient { .state_root .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), ); if !is_merkle_branch_valid { @@ -288,10 +298,10 @@ impl EthLightClient { let block_roots_root = calculate_merkle_root( &Node::from_bytes( hash_tree_root(ancestor.header.clone()) - .unwrap() + .map_err(|_| Error::MerkleizationError)? .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), &block_header_branch, &GeneralizedIndex(block_roots_proof.block_header_index as usize), @@ -332,7 +342,7 @@ impl EthLightClient { .state_root .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), ); @@ -351,13 +361,19 @@ impl EthLightClient { .collect::>(); let execution_payload_root = calculate_multi_merkle_root( &[ - Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), + Node::from_bytes( + execution_payload + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), Node::from_bytes( hash_tree_root(execution_payload.block_number) - .unwrap() + .map_err(|_| Error::MerkleizationError)? .as_ref() .try_into() - .unwrap(), + .map_err(|_| Error::InvalidRoot)?, ), ], &multi_proof, @@ -377,7 +393,14 @@ impl EthLightClient { execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, get_subtree_index(EXECUTION_PAYLOAD_INDEX) as usize, - &Node::from_bytes(ancestor.header.body_root.as_ref().try_into().unwrap()), + &Node::from_bytes( + ancestor + .header + .body_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), ); if !is_merkle_branch_valid { From f063d09b5426b20f63f832979743302673c8473c Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 25 Jan 2023 01:52:31 +0100 Subject: [PATCH 023/182] init prover --- Cargo.toml | 3 ++- sync-committee-prover/Cargo.toml | 11 ++++++++++ sync-committee-prover/src/lib.rs | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 sync-committee-prover/Cargo.toml create mode 100644 sync-committee-prover/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1544c2ba5..b87e07965 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,6 @@ resolver = "2" members = [ "light-client-verifier", "light-client-primitives", - "ics15-ethereum" + "ics15-ethereum", + "sync-committee-prover" ] diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml new file mode 100644 index 000000000..372b9bb37 --- /dev/null +++ b/sync-committee-prover/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "sync-committee-prover" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +reqwest = {version="0.11.14", features=["json"]} + diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs new file mode 100644 index 000000000..554ace02c --- /dev/null +++ b/sync-committee-prover/src/lib.rs @@ -0,0 +1,36 @@ +use ethereum_consensus::bellatrix::{BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee}; + +pub fn header_route(block_id: String) -> String { + format!("/eth/v1/beacon/headers/{}", block_id) +} + +pub struct SyncCommitteeProver { + pub node_url: String, +} + +impl SyncCommitteeProver { + + pub fn new(node_url: String) -> Self { + SyncCommitteeProver { node_url } + } + + pub async fn fetch_header(&self, block_id: String) -> Result { + let mut node_url = self.node_url.clone(); + let path = header_route(block_id); + node_url.push_str(&path); + + let client = reqwest::Client::new(); + let response = client + .get(node_url).send() + .await?; + + + let beacon_block_header = response.json::().await; + + beacon_block_header + } + /*pub async fn fetch_block(block_id: String) -> BeaconBlock { } + pub async fn fetch_sync_committee(state_id: String) -> SyncCommittee { } + pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } + pub fn signed_beacon_block_header(beacon_block: SignedBeaconBlock) -> SignedBeaconBlockHeader { }*/ +} From e911d28f6bd5c1b16140320376dc5a01b9d400ba Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 25 Jan 2023 02:17:09 +0100 Subject: [PATCH 024/182] fetch block header and sync committee --- sync-committee-prover/src/lib.rs | 107 ++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 15 deletions(-) diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 554ace02c..391af98a6 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -1,36 +1,113 @@ -use ethereum_consensus::bellatrix::{BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee}; +use ethereum_consensus::bellatrix::{ + BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, +}; +use reqwest::Client; + +//TODO: Remove all these and use the light client primitive consts in the other PR +const MAX_PROPOSER_SLASHINGS: usize = 0; +const MAX_VALIDATORS_PER_COMMITTEE: usize = 0; +const MAX_ATTESTER_SLASHINGS: usize = 0; +const MAX_ATTESTATIONS: usize = 0; +const MAX_DEPOSITS: usize = 0; +const MAX_VOLUNTARY_EXITS: usize = 0; +const SYNC_COMMITTEE_SIZE: usize = 0; +const BYTES_PER_LOGS_BLOOM: usize = 0; +const MAX_EXTRA_DATA_BYTES: usize = 0; +const MAX_BYTES_PER_TRANSACTION: usize = 0; +const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 0; pub fn header_route(block_id: String) -> String { format!("/eth/v1/beacon/headers/{}", block_id) } +pub fn block_route(block_id: String) -> String { + format!("/eth/v2/beacon/blocks/{}", block_id) +} + +pub fn sync_committee_route(state_id: String) -> String { + format!("/eth/v1/beacon/states/{}/sync_committees", state_id) +} + pub struct SyncCommitteeProver { pub node_url: String, + pub client: Client, } impl SyncCommitteeProver { - pub fn new(node_url: String) -> Self { - SyncCommitteeProver { node_url } - } + let client = reqwest::Client::new(); - pub async fn fetch_header(&self, block_id: String) -> Result { - let mut node_url = self.node_url.clone(); - let path = header_route(block_id); - node_url.push_str(&path); + SyncCommitteeProver { node_url, client } + } - let client = reqwest::Client::new(); - let response = client - .get(node_url).send() - .await?; + pub async fn fetch_header( + &self, + block_id: String, + ) -> Result { + let path = header_route(block_id); + let full_url = format!("{}/{}", self.node_url.clone(), path); + let response = self.client.get(full_url).send().await?; let beacon_block_header = response.json::().await; beacon_block_header } - /*pub async fn fetch_block(block_id: String) -> BeaconBlock { } - pub async fn fetch_sync_committee(state_id: String) -> SyncCommittee { } - pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } + pub async fn fetch_block( + &self, + block_id: String, + ) -> Result< + BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + reqwest::Error, + > { + let path = block_route(block_id); + let full_url = format!("{}/{}", self.node_url.clone(), path); + + let response = self.client.get(full_url).send().await?; + + let beacon_block = response + .json::>() + .await; + + beacon_block + } + pub async fn fetch_sync_committee( + &self, + state_id: String, + ) -> Result, reqwest::Error> { + let path = sync_committee_route(state_id); + let full_url = format!("{}/{}", self.node_url.clone(), path); + + let response = self.client.get(full_url).send().await?; + + let sync_committee = response.json::>().await; + + sync_committee + } + /*pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } pub fn signed_beacon_block_header(beacon_block: SignedBeaconBlock) -> SignedBeaconBlockHeader { }*/ } From 9253c3046e1ffed9f6819d2c7abe3f41d53180ee Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 25 Jan 2023 11:30:45 +0100 Subject: [PATCH 025/182] wrote tests --- sync-committee-prover/Cargo.toml | 3 +++ sync-committee-prover/src/lib.rs | 15 ++++++++++----- .../src/responses/beacon_block_response.rs | 19 +++++++++++++++++++ sync-committee-prover/src/responses/mod.rs | 1 + sync-committee-prover/src/test.rs | 12 ++++++++++++ 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 sync-committee-prover/src/responses/beacon_block_response.rs create mode 100644 sync-committee-prover/src/responses/mod.rs create mode 100644 sync-committee-prover/src/test.rs diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index 372b9bb37..00b852e6c 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -8,4 +8,7 @@ edition = "2021" [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } reqwest = {version="0.11.14", features=["json"]} +serde = { version = "1.0", features = ["derive"]} +serde_json = { version = "1.0.81"} +actix-rt = "*" diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 391af98a6..748266a9e 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -1,3 +1,8 @@ +#[cfg(test)] +mod test; +mod responses; + + use ethereum_consensus::bellatrix::{ BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, }; @@ -43,15 +48,15 @@ impl SyncCommitteeProver { pub async fn fetch_header( &self, block_id: String, - ) -> Result { + ) ->Result { let path = header_route(block_id); - let full_url = format!("{}/{}", self.node_url.clone(), path); - + let full_url = format!("{}{}", self.node_url.clone(), path); let response = self.client.get(full_url).send().await?; + let response_data = response.json::().await?; - let beacon_block_header = response.json::().await; + let beacon_block_header = response_data.data.header.message; - beacon_block_header + Ok(beacon_block_header) } pub async fn fetch_block( &self, diff --git a/sync-committee-prover/src/responses/beacon_block_response.rs b/sync-committee-prover/src/responses/beacon_block_response.rs new file mode 100644 index 000000000..ffc50176a --- /dev/null +++ b/sync-committee-prover/src/responses/beacon_block_response.rs @@ -0,0 +1,19 @@ +use ethereum_consensus::bellatrix::BeaconBlockHeader; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, )] +pub struct Response { + pub(crate) data: ResponseData, + execution_optimistic: bool +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, )] +pub struct ResponseData { + root: String, + canonical: bool, + pub(crate) header: ResponseDataBeaconBlockHeaderMessage, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseDataBeaconBlockHeaderMessage { + pub message: BeaconBlockHeader +} diff --git a/sync-committee-prover/src/responses/mod.rs b/sync-committee-prover/src/responses/mod.rs new file mode 100644 index 000000000..d345f28a8 --- /dev/null +++ b/sync-committee-prover/src/responses/mod.rs @@ -0,0 +1 @@ +pub mod beacon_block_response; diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs new file mode 100644 index 000000000..6b0b94bab --- /dev/null +++ b/sync-committee-prover/src/test.rs @@ -0,0 +1,12 @@ +use super::*; + +#[cfg(test)] +#[allow(non_snake_case)] + +#[actix_rt::test] +async fn fetches_block_header_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block_header = sync_committee_prover.fetch_header("100".to_string()).await; + assert!(block_header.is_ok()); +} From 868181e72e2f19fdf671ebb73111fe2a54869012 Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 26 Jan 2023 00:26:51 +0100 Subject: [PATCH 026/182] beacon block impl --- sync-committee-prover/src/lib.rs | 9 ++-- .../responses/beacon_block_header_response.rs | 19 +++++++++ .../src/responses/beacon_block_response.rs | 42 ++++++++++++++----- sync-committee-prover/src/responses/mod.rs | 1 + sync-committee-prover/src/test.rs | 1 - 5 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 sync-committee-prover/src/responses/beacon_block_header_response.rs diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 748266a9e..929f49586 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -1,7 +1,6 @@ +mod responses; #[cfg(test)] mod test; -mod responses; - use ethereum_consensus::bellatrix::{ BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, @@ -48,11 +47,13 @@ impl SyncCommitteeProver { pub async fn fetch_header( &self, block_id: String, - ) ->Result { + ) -> Result { let path = header_route(block_id); let full_url = format!("{}{}", self.node_url.clone(), path); let response = self.client.get(full_url).send().await?; - let response_data = response.json::().await?; + let response_data = response + .json::() + .await?; let beacon_block_header = response_data.data.header.message; diff --git a/sync-committee-prover/src/responses/beacon_block_header_response.rs b/sync-committee-prover/src/responses/beacon_block_header_response.rs new file mode 100644 index 000000000..4ac4748ed --- /dev/null +++ b/sync-committee-prover/src/responses/beacon_block_header_response.rs @@ -0,0 +1,19 @@ +use ethereum_consensus::bellatrix::BeaconBlockHeader; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: ResponseData, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseData { + root: String, + canonical: bool, + pub(crate) header: ResponseDataBeaconBlockHeaderMessage, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseDataBeaconBlockHeaderMessage { + pub message: BeaconBlockHeader, +} diff --git a/sync-committee-prover/src/responses/beacon_block_response.rs b/sync-committee-prover/src/responses/beacon_block_response.rs index ffc50176a..b791c356c 100644 --- a/sync-committee-prover/src/responses/beacon_block_response.rs +++ b/sync-committee-prover/src/responses/beacon_block_response.rs @@ -1,19 +1,41 @@ -use ethereum_consensus::bellatrix::BeaconBlockHeader; +use ethereum_consensus::bellatrix::{BeaconBlock, BeaconBlockHeader}; -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, )] +//TODO: Remove all these and use the light client primitive consts in the other PR +const MAX_PROPOSER_SLASHINGS: usize = 0; +const MAX_VALIDATORS_PER_COMMITTEE: usize = 0; +const MAX_ATTESTER_SLASHINGS: usize = 0; +const MAX_ATTESTATIONS: usize = 0; +const MAX_DEPOSITS: usize = 0; +const MAX_VOLUNTARY_EXITS: usize = 0; +const SYNC_COMMITTEE_SIZE: usize = 0; +const BYTES_PER_LOGS_BLOOM: usize = 0; +const MAX_EXTRA_DATA_BYTES: usize = 0; +const MAX_BYTES_PER_TRANSACTION: usize = 0; +const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 0; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { pub(crate) data: ResponseData, - execution_optimistic: bool + version: String, + execution_optimistic: bool, } -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, )] +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ResponseData { root: String, canonical: bool, - pub(crate) header: ResponseDataBeaconBlockHeaderMessage, -} - -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct ResponseDataBeaconBlockHeaderMessage { - pub message: BeaconBlockHeader + pub(crate) message: BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + pub signature: String, } diff --git a/sync-committee-prover/src/responses/mod.rs b/sync-committee-prover/src/responses/mod.rs index d345f28a8..15a351a34 100644 --- a/sync-committee-prover/src/responses/mod.rs +++ b/sync-committee-prover/src/responses/mod.rs @@ -1 +1,2 @@ +pub mod beacon_block_header_response; pub mod beacon_block_response; diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 6b0b94bab..014581440 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -2,7 +2,6 @@ use super::*; #[cfg(test)] #[allow(non_snake_case)] - #[actix_rt::test] async fn fetches_block_header_works() { let node_url: String = "http://localhost:3500".to_string(); From e47e5135ddb772ac803f9dd389f5e9c494410762 Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 26 Jan 2023 00:27:32 +0100 Subject: [PATCH 027/182] lock --- Cargo.lock | 1879 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1879 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..c472ff031 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1879 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "amcl" +version = "0.3.0" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +dependencies = [ + "int", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +dependencies = [ + "base64 0.13.1", + "bs58", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "eth-beacon-light-client-primitives" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", +] + +[[package]] +name = "eth-beacon-light-client-verifier" +version = "0.1.0" + +[[package]] +name = "ethereum-consensus" +version = "0.1.1" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#248b5d1e53d87fde1d9368dc25ad483633ef0e4a" +dependencies = [ + "async-stream", + "bs58", + "enr", + "error-chain", + "getrandom", + "hashbrown 0.13.2", + "hex", + "integer-sqrt", + "milagro_bls", + "multiaddr", + "multihash", + "rand", + "serde", + "serde_json", + "sha2 0.9.9", + "ssz-rs", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ics15-ethereum" +version = "0.1.0" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "int" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +dependencies = [ + "num-traits", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "milagro_bls" +version = "1.5.1" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +dependencies = [ + "amcl", + "rand", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution#ea113b48f3ad134603f11c9be9f5fc696d30c259" +dependencies = [ + "bitvec", + "hex", + "lazy_static", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive", + "thiserror", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution#ea113b48f3ad134603f11c9be9f5fc696d30c259" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync-committee-prover" +version = "0.1.0" +dependencies = [ + "actix-rt", + "ethereum-consensus", + "reqwest", + "serde", + "serde_json", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" From ae748ef61f9eec3eef9fa88d2d2cc4aa77597b68 Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 26 Jan 2023 00:37:51 +0100 Subject: [PATCH 028/182] use the right const generic types --- Cargo.lock | 91 ++++++++++++++++--- sync-committee-prover/Cargo.toml | 1 + sync-committee-prover/src/lib.rs | 22 ++--- .../src/responses/beacon_block_response.rs | 21 ++--- 4 files changed, 98 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c472ff031..076dcb94e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +[[package]] +name = "as-any" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" + [[package]] name = "async-stream" version = "0.3.3" @@ -287,6 +293,12 @@ dependencies = [ "signature", ] +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + [[package]] name = "elliptic-curve" version = "0.12.3" @@ -344,18 +356,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "eth-beacon-light-client-primitives" -version = "0.1.0" -dependencies = [ - "base2", - "ethereum-consensus", -] - -[[package]] -name = "eth-beacon-light-client-verifier" -version = "0.1.0" - [[package]] name = "ethereum-consensus" version = "0.1.1" @@ -376,7 +376,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.9.9", - "ssz-rs", + "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", "thiserror", "tokio", "tokio-stream", @@ -559,6 +559,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + [[package]] name = "hmac" version = "0.12.1" @@ -696,6 +702,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.5" @@ -747,6 +762,27 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "light-client-primitives" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "hex-literal", + "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", +] + +[[package]] +name = "light-client-verifier" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "light-client-primitives", + "milagro_bls", + "ssz-rs 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -1399,7 +1435,23 @@ dependencies = [ "num-bigint", "serde", "sha2 0.9.9", - "ssz-rs-derive", + "ssz-rs-derive 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", + "thiserror", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#ab1503da4dd1d93ad1095fbce5e468b79b0eda55" +dependencies = [ + "as-any", + "bitvec", + "hex", + "itertools", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", "thiserror", ] @@ -1413,6 +1465,16 @@ dependencies = [ "syn", ] +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#ab1503da4dd1d93ad1095fbce5e468b79b0eda55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1442,6 +1504,7 @@ version = "0.1.0" dependencies = [ "actix-rt", "ethereum-consensus", + "light-client-primitives", "reqwest", "serde", "serde_json", diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index 00b852e6c..ed60d1747 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +light-client-primitives = {path="../light-client-primitives", default-features = false } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 929f49586..2f1e50721 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -7,18 +7,16 @@ use ethereum_consensus::bellatrix::{ }; use reqwest::Client; -//TODO: Remove all these and use the light client primitive consts in the other PR -const MAX_PROPOSER_SLASHINGS: usize = 0; -const MAX_VALIDATORS_PER_COMMITTEE: usize = 0; -const MAX_ATTESTER_SLASHINGS: usize = 0; -const MAX_ATTESTATIONS: usize = 0; -const MAX_DEPOSITS: usize = 0; -const MAX_VOLUNTARY_EXITS: usize = 0; -const SYNC_COMMITTEE_SIZE: usize = 0; -const BYTES_PER_LOGS_BLOOM: usize = 0; -const MAX_EXTRA_DATA_BYTES: usize = 0; -const MAX_BYTES_PER_TRANSACTION: usize = 0; -const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 0; +use ethereum_consensus::bellatrix::mainnet::{ + BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, + MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, +}; +use ethereum_consensus::phase0::mainnet::{ + EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, + HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, + MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, + SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, +}; pub fn header_route(block_id: String) -> String { format!("/eth/v1/beacon/headers/{}", block_id) diff --git a/sync-committee-prover/src/responses/beacon_block_response.rs b/sync-committee-prover/src/responses/beacon_block_response.rs index b791c356c..5a6a6a1d6 100644 --- a/sync-committee-prover/src/responses/beacon_block_response.rs +++ b/sync-committee-prover/src/responses/beacon_block_response.rs @@ -1,17 +1,16 @@ +use ethereum_consensus::bellatrix::mainnet::{ + BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, + MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, +}; use ethereum_consensus::bellatrix::{BeaconBlock, BeaconBlockHeader}; +use ethereum_consensus::phase0::mainnet::{ + EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, + HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, + MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, + SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, +}; //TODO: Remove all these and use the light client primitive consts in the other PR -const MAX_PROPOSER_SLASHINGS: usize = 0; -const MAX_VALIDATORS_PER_COMMITTEE: usize = 0; -const MAX_ATTESTER_SLASHINGS: usize = 0; -const MAX_ATTESTATIONS: usize = 0; -const MAX_DEPOSITS: usize = 0; -const MAX_VOLUNTARY_EXITS: usize = 0; -const SYNC_COMMITTEE_SIZE: usize = 0; -const BYTES_PER_LOGS_BLOOM: usize = 0; -const MAX_EXTRA_DATA_BYTES: usize = 0; -const MAX_BYTES_PER_TRANSACTION: usize = 0; -const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 0; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { From 61ab3fcd3b3a38ed985c4498df5f7aebeb66854b Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 26 Jan 2023 01:41:29 +0100 Subject: [PATCH 029/182] implement and test beacon block implement sync committee --- Cargo.lock | 1942 ----------------- light-client-primitives/Cargo.toml | 3 +- light-client-verifier/Cargo.toml | 3 +- sync-committee-prover/Cargo.toml | 3 +- sync-committee-prover/src/lib.rs | 43 +- .../src/responses/beacon_block_response.rs | 4 - sync-committee-prover/src/responses/mod.rs | 1 + .../src/responses/sync_committee_response.rs | 11 + sync-committee-prover/src/test.rs | 13 +- 9 files changed, 54 insertions(+), 1969 deletions(-) delete mode 100644 Cargo.lock create mode 100644 sync-committee-prover/src/responses/sync_committee_response.rs diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 076dcb94e..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,1942 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-rt" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "amcl" -version = "0.3.0" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "as-any" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" - -[[package]] -name = "async-stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base2" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" -dependencies = [ - "int", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "base64ct" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" - -[[package]] -name = "cc" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.6", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enr" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" -dependencies = [ - "base64 0.13.1", - "bs58", - "bytes", - "hex", - "k256", - "log", - "rand", - "rlp", - "serde", - "sha3", - "zeroize", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - -[[package]] -name = "ethereum-consensus" -version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#248b5d1e53d87fde1d9368dc25ad483633ef0e4a" -dependencies = [ - "async-stream", - "bs58", - "enr", - "error-chain", - "getrandom", - "hashbrown 0.13.2", - "hex", - "integer-sqrt", - "milagro_bls", - "multiaddr", - "multihash", - "rand", - "serde", - "serde_json", - "sha2 0.9.9", - "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", - "thiserror", - "tokio", - "tokio-stream", -] - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures-channel" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" - -[[package]] -name = "futures-sink" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" - -[[package]] -name = "futures-task" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" - -[[package]] -name = "futures-util" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ics15-ethereum" -version = "0.1.0" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "int" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" -dependencies = [ - "num-traits", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ipnet" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "light-client-primitives" -version = "0.1.0" -dependencies = [ - "base2", - "ethereum-consensus", - "hex-literal", - "ssz-rs 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", -] - -[[package]] -name = "light-client-verifier" -version = "0.1.0" -dependencies = [ - "base2", - "ethereum-consensus", - "light-client-primitives", - "milagro_bls", - "ssz-rs 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "milagro_bls" -version = "1.5.1" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" -dependencies = [ - "amcl", - "rand", - "zeroize", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mio" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys", -] - -[[package]] -name = "multiaddr" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" -dependencies = [ - "arrayref", - "bs58", - "byteorder", - "data-encoding", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - -[[package]] -name = "multihash" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" -dependencies = [ - "core2", - "digest 0.10.6", - "multihash-derive", - "sha2 0.10.6", - "unsigned-varint", -] - -[[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" -dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" -dependencies = [ - "digest 0.10.6", - "keccak", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "ssz-rs" -version = "0.8.0" -source = "git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution#ea113b48f3ad134603f11c9be9f5fc696d30c259" -dependencies = [ - "bitvec", - "hex", - "lazy_static", - "num-bigint", - "serde", - "sha2 0.9.9", - "ssz-rs-derive 0.8.0 (git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution)", - "thiserror", -] - -[[package]] -name = "ssz-rs" -version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#ab1503da4dd1d93ad1095fbce5e468b79b0eda55" -dependencies = [ - "as-any", - "bitvec", - "hex", - "itertools", - "num-bigint", - "serde", - "sha2 0.9.9", - "ssz-rs-derive 0.8.0 (git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1)", - "thiserror", -] - -[[package]] -name = "ssz-rs-derive" -version = "0.8.0" -source = "git+https://github.com/Snowfork/ssz_rs?branch=feat/contribution#ea113b48f3ad134603f11c9be9f5fc696d30c259" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ssz-rs-derive" -version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#ab1503da4dd1d93ad1095fbce5e468b79b0eda55" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync-committee-prover" -version = "0.1.0" -dependencies = [ - "actix-rt", - "ethereum-consensus", - "light-client-primitives", - "reqwest", - "serde", - "serde_json", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "unsigned-varint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index 3852ccb25..4cec7a852 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -7,7 +7,8 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } +#ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } +ethereum-consensus = { path = "../../ethereum-consensus", default-features = false} base2 = {version="0.2.2", default-features=false} ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 99fd897df..29228c598 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -7,7 +7,8 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +#ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +ethereum-consensus = { path = "../../ethereum-consensus" } base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index ed60d1747..6a24dfadd 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +#ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +ethereum-consensus = { path = "../../ethereum-consensus" } light-client-primitives = {path="../light-client-primitives", default-features = false } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 2f1e50721..69296ce66 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -81,23 +81,24 @@ impl SyncCommitteeProver { let response = self.client.get(full_url).send().await?; - let beacon_block = response - .json::>() - .await; - - beacon_block + println!("gotten response, deserializzing..."); + println!("Response status {}", response.status()); + + let response_data = response + .json::() + .await?; + + println!("response data is {:?}", response_data); + + //println!("Response data {:?}", response.text().await); + + //TODO: proceess error + //let beacon_block_header = response_data.header.unwrap().message; + + + let beacon_block = response_data.data.message; + + Ok(beacon_block) } pub async fn fetch_sync_committee( &self, @@ -108,9 +109,13 @@ impl SyncCommitteeProver { let response = self.client.get(full_url).send().await?; - let sync_committee = response.json::>().await; + let response_data = response + .json::() + .await.unwrap(); + + let sync_committee = response_data.data; - sync_committee + Ok(sync_committee) } /*pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } pub fn signed_beacon_block_header(beacon_block: SignedBeaconBlock) -> SignedBeaconBlockHeader { }*/ diff --git a/sync-committee-prover/src/responses/beacon_block_response.rs b/sync-committee-prover/src/responses/beacon_block_response.rs index 5a6a6a1d6..e4f1a19c6 100644 --- a/sync-committee-prover/src/responses/beacon_block_response.rs +++ b/sync-committee-prover/src/responses/beacon_block_response.rs @@ -10,8 +10,6 @@ use ethereum_consensus::phase0::mainnet::{ SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }; -//TODO: Remove all these and use the light client primitive consts in the other PR - #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { pub(crate) data: ResponseData, @@ -21,8 +19,6 @@ pub struct Response { #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ResponseData { - root: String, - canonical: bool, pub(crate) message: BeaconBlock< MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, diff --git a/sync-committee-prover/src/responses/mod.rs b/sync-committee-prover/src/responses/mod.rs index 15a351a34..ab769a6ec 100644 --- a/sync-committee-prover/src/responses/mod.rs +++ b/sync-committee-prover/src/responses/mod.rs @@ -1,2 +1,3 @@ pub mod beacon_block_header_response; pub mod beacon_block_response; +pub mod sync_committee_response; diff --git a/sync-committee-prover/src/responses/sync_committee_response.rs b/sync-committee-prover/src/responses/sync_committee_response.rs new file mode 100644 index 000000000..17f7ababe --- /dev/null +++ b/sync-committee-prover/src/responses/sync_committee_response.rs @@ -0,0 +1,11 @@ +use ethereum_consensus::bellatrix::mainnet::{ + SYNC_COMMITTEE_SIZE, +}; +use ethereum_consensus::bellatrix::{SyncCommittee}; + + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: SyncCommittee, + execution_optimistic: bool, +} diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 014581440..bfc06e87b 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -3,9 +3,20 @@ use super::*; #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -async fn fetches_block_header_works() { +async fn fetch_block_header_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let block_header = sync_committee_prover.fetch_header("100".to_string()).await; assert!(block_header.is_ok()); } + + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_sync_committee_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block = sync_committee_prover.fetch_sync_committee("117".to_string()).await; + assert!(block.is_ok()); +} From 107903a4df963037d9eeee5528c802136a092a03 Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 27 Jan 2023 02:21:15 +0100 Subject: [PATCH 030/182] node sync committee fetch validator --- light-client-primitives/Cargo.toml | 4 +-- light-client-verifier/Cargo.toml | 4 +-- sync-committee-prover/Cargo.toml | 5 +-- sync-committee-prover/src/lib.rs | 31 +++++++++++++++++-- sync-committee-prover/src/responses/mod.rs | 1 + .../src/responses/sync_committee_response.rs | 14 ++++----- .../src/responses/validator_response.rs | 15 +++++++++ sync-committee-prover/src/test.rs | 26 +++++++++++++++- 8 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 sync-committee-prover/src/responses/validator_response.rs diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index 4cec7a852..e127bd017 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -#ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } -ethereum-consensus = { path = "../../ethereum-consensus", default-features = false} +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } +#ethereum-consensus = { path = "../../ethereum-consensus", default-features = false} base2 = {version="0.2.2", default-features=false} ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 29228c598..51736979f 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -#ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -ethereum-consensus = { path = "../../ethereum-consensus" } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +#ethereum-consensus = { path = "../../ethereum-consensus" } base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index 6a24dfadd..c8fbafa87 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -6,9 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -#ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -ethereum-consensus = { path = "../../ethereum-consensus" } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +#ethereum-consensus = { path = "../../ethereum-consensus" } light-client-primitives = {path="../light-client-primitives", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 69296ce66..8b2f397fb 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -2,11 +2,13 @@ mod responses; #[cfg(test)] mod test; +use ethereum_consensus::altair::Validator; use ethereum_consensus::bellatrix::{ BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, }; use reqwest::Client; +use crate::responses::sync_committee_response::NodeSyncCommittee; use ethereum_consensus::bellatrix::mainnet::{ BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, @@ -30,6 +32,10 @@ pub fn sync_committee_route(state_id: String) -> String { format!("/eth/v1/beacon/states/{}/sync_committees", state_id) } +pub fn validator_route(state_id: String, validator_index: String) -> String { + format!("/eth/v1/beacon/states/{}/validators/{}", state_id, validator_index) +} + pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, @@ -95,7 +101,6 @@ impl SyncCommitteeProver { //TODO: proceess error //let beacon_block_header = response_data.header.unwrap().message; - let beacon_block = response_data.data.message; Ok(beacon_block) @@ -103,7 +108,7 @@ impl SyncCommitteeProver { pub async fn fetch_sync_committee( &self, state_id: String, - ) -> Result, reqwest::Error> { + ) -> Result { let path = sync_committee_route(state_id); let full_url = format!("{}/{}", self.node_url.clone(), path); @@ -111,12 +116,32 @@ impl SyncCommitteeProver { let response_data = response .json::() - .await.unwrap(); + .await + .unwrap(); let sync_committee = response_data.data; Ok(sync_committee) } + pub async fn fetch_validator( + &self, + state_id: String, + validator_index: String, + ) -> Result { + let path = validator_route(state_id, validator_index); + let full_url = format!("{}/{}", self.node_url.clone(), path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response + .json::() + .await + .unwrap(); + + let validator= response_data.data.validator; + + Ok(validator) + } /*pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } pub fn signed_beacon_block_header(beacon_block: SignedBeaconBlock) -> SignedBeaconBlockHeader { }*/ } diff --git a/sync-committee-prover/src/responses/mod.rs b/sync-committee-prover/src/responses/mod.rs index ab769a6ec..41b26cd36 100644 --- a/sync-committee-prover/src/responses/mod.rs +++ b/sync-committee-prover/src/responses/mod.rs @@ -1,3 +1,4 @@ pub mod beacon_block_header_response; pub mod beacon_block_response; pub mod sync_committee_response; +pub mod validator_response; diff --git a/sync-committee-prover/src/responses/sync_committee_response.rs b/sync-committee-prover/src/responses/sync_committee_response.rs index 17f7ababe..80bbcd7d8 100644 --- a/sync-committee-prover/src/responses/sync_committee_response.rs +++ b/sync-committee-prover/src/responses/sync_committee_response.rs @@ -1,11 +1,11 @@ -use ethereum_consensus::bellatrix::mainnet::{ - SYNC_COMMITTEE_SIZE, -}; -use ethereum_consensus::bellatrix::{SyncCommittee}; - - #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { - pub(crate) data: SyncCommittee, + pub(crate) data: NodeSyncCommittee, execution_optimistic: bool, } + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct NodeSyncCommittee { + pub validators: Vec, + pub validator_aggregates: Vec>, +} diff --git a/sync-committee-prover/src/responses/validator_response.rs b/sync-committee-prover/src/responses/validator_response.rs new file mode 100644 index 000000000..4422a4b8f --- /dev/null +++ b/sync-committee-prover/src/responses/validator_response.rs @@ -0,0 +1,15 @@ +use ethereum_consensus::bellatrix::Validator; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: ValidatorData, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ValidatorData { + pub index: String, + pub balance: String, + pub status: String, + pub(crate) validator: Validator, +} diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index bfc06e87b..068dc8467 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -10,6 +10,15 @@ async fn fetch_block_header_works() { assert!(block_header.is_ok()); } +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_block_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block = sync_committee_prover.fetch_block("100".to_string()).await; + assert!(block.is_ok()); +} #[cfg(test)] #[allow(non_snake_case)] @@ -17,6 +26,21 @@ async fn fetch_block_header_works() { async fn fetch_sync_committee_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_sync_committee("117".to_string()).await; + let block = sync_committee_prover + .fetch_sync_committee("117".to_string()) + .await; assert!(block.is_ok()); } + + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_validator_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let validator = sync_committee_prover + .fetch_validator("2561".to_string(), "48".to_string()) + .await; + assert!(validator.is_ok()); +} From 4b0471a13e9bd298c448112febf110681cc8be50 Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 27 Jan 2023 03:16:43 +0100 Subject: [PATCH 031/182] processed sync committees --- sync-committee-prover/Cargo.toml | 4 ++- sync-committee-prover/src/lib.rs | 44 +++++++++++++++++++++++++++++-- sync-committee-prover/src/test.rs | 13 ++++++++- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index c8fbafa87..be7b0e655 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -9,7 +9,9 @@ edition = "2021" ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } #ethereum-consensus = { path = "../../ethereum-consensus" } light-client-primitives = {path="../light-client-primitives", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } +#ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } +ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } + reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 8b2f397fb..4e96d4644 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -13,12 +13,15 @@ use ethereum_consensus::bellatrix::mainnet::{ BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, }; +use ethereum_consensus::crypto::{eth_aggregate_public_keys, PublicKey}; use ethereum_consensus::phase0::mainnet::{ EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }; +use ethereum_consensus::primitives::{BlsPublicKey, ValidatorIndex}; +use ssz_rs::{List, Vector}; pub fn header_route(block_id: String) -> String { format!("/eth/v1/beacon/headers/{}", block_id) @@ -33,7 +36,10 @@ pub fn sync_committee_route(state_id: String) -> String { } pub fn validator_route(state_id: String, validator_index: String) -> String { - format!("/eth/v1/beacon/states/{}/validators/{}", state_id, validator_index) + format!( + "/eth/v1/beacon/states/{}/validators/{}", + state_id, validator_index + ) } pub struct SyncCommitteeProver { @@ -138,10 +144,44 @@ impl SyncCommitteeProver { .await .unwrap(); - let validator= response_data.data.validator; + let validator = response_data.data.validator; Ok(validator) } + + pub async fn fetch_processed_sync_committee( + &self, + state_id: String, + ) -> Result, reqwest::Error> { + // fetches sync committee from Noe + let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; + + let mut validators: List = Default::default(); + let mut validator_indexes: Vec = Vec::new(); + + for mut validator_index in node_sync_committee.validators { + // fetches validator based on validator index + let validator = self + .fetch_validator(state_id.clone(), validator_index.clone()) + .await?; + validators.push(validator); + validator_indexes.push(validator_index.parse().unwrap()); + } + + let public_keys_vector = validator_indexes + .into_iter() + .map(|i| validators[i].public_key.clone()) + .collect::>(); + + let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector).unwrap(); + + let sync_committee = SyncCommittee:: { + public_keys: public_keys_vector, + aggregate_public_key, + }; + + Ok(sync_committee) + } /*pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } pub fn signed_beacon_block_header(beacon_block: SignedBeaconBlock) -> SignedBeaconBlockHeader { }*/ } diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 068dc8467..9c863e320 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -32,7 +32,6 @@ async fn fetch_sync_committee_works() { assert!(block.is_ok()); } - #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] @@ -44,3 +43,15 @@ async fn fetch_validator_works() { .await; assert!(validator.is_ok()); } + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_processed_sync_committee_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let validator = sync_committee_prover + .fetch_processed_sync_committee("2561".to_string()) + .await; + assert!(validator.is_ok()); +} From af5a17921240e13d0916b6193e826c15778e1906 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 28 Jan 2023 20:48:48 +0100 Subject: [PATCH 032/182] signed beacon block signed beacon block header --- sync-committee-prover/src/lib.rs | 112 ++++++++++++++++++++++++++---- sync-committee-prover/src/test.rs | 35 ++++++++++ 2 files changed, 133 insertions(+), 14 deletions(-) diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 4e96d4644..99d78579a 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -13,7 +13,7 @@ use ethereum_consensus::bellatrix::mainnet::{ BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, }; -use ethereum_consensus::crypto::{eth_aggregate_public_keys, PublicKey}; +use ethereum_consensus::crypto::{aggregate, eth_aggregate_public_keys, PublicKey}; use ethereum_consensus::phase0::mainnet::{ EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, @@ -42,6 +42,12 @@ pub fn validator_route(state_id: String, validator_index: String) -> String { ) } +#[derive(Debug)] +pub enum Error { + AggregateSignatureError, + EmptySignedBeaconBlock, +} + pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, @@ -93,20 +99,10 @@ impl SyncCommitteeProver { let response = self.client.get(full_url).send().await?; - println!("gotten response, deserializzing..."); - println!("Response status {}", response.status()); - let response_data = response .json::() .await?; - println!("response data is {:?}", response_data); - - //println!("Response data {:?}", response.text().await); - - //TODO: proceess error - //let beacon_block_header = response_data.header.unwrap().message; - let beacon_block = response_data.data.message; Ok(beacon_block) @@ -153,7 +149,7 @@ impl SyncCommitteeProver { &self, state_id: String, ) -> Result, reqwest::Error> { - // fetches sync committee from Noe + // fetches sync committee from Node let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; let mut validators: List = Default::default(); @@ -182,6 +178,94 @@ impl SyncCommitteeProver { Ok(sync_committee) } - /*pub fn signed_beacon_block(beacon_block: BeaconBlock) -> SignedBeaconBlock { } - pub fn signed_beacon_block_header(beacon_block: SignedBeaconBlock) -> SignedBeaconBlockHeader { }*/ + + pub fn signed_beacon_block( + &self, + beacon_block: BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + ) -> Option< + SignedBeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + > { + let attestations = beacon_block.body.attestations.clone(); + let signatures: Vec<_> = attestations + .iter() + .map(|sig| sig.signature.clone()) + .collect(); + + let aggregate_signature = + aggregate(signatures.as_ref()).map_err(|_| Error::AggregateSignatureError); + + let signed_beacon_block = SignedBeaconBlock::< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + > { + message: beacon_block, + signature: aggregate_signature.unwrap(), + }; + + Some(signed_beacon_block) + } + + pub fn signed_beacon_block_header( + &self, + signed_beacon_block: Option< + SignedBeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + >, + beacon_block_header: BeaconBlockHeader, + ) -> Result { + if signed_beacon_block.is_none() { + return Err(Error::EmptySignedBeaconBlock); + } + + let signed_beacon_block_header = SignedBeaconBlockHeader { + message: beacon_block_header, + signature: signed_beacon_block.unwrap().signature, + }; + + Ok(signed_beacon_block_header) + } } diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 9c863e320..3d1dce550 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -55,3 +55,38 @@ async fn fetch_processed_sync_committee_works() { .await; assert!(validator.is_ok()); } + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_signed_beacon_block_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block = sync_committee_prover.fetch_block("100".to_string()).await; + assert!(block.is_ok()); + let signed_beacon_block = sync_committee_prover.signed_beacon_block(block.unwrap()); + assert!(signed_beacon_block.is_some()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_signed_beacon_block_header_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + // fetch beacon block header + let header = sync_committee_prover.fetch_header("100".to_string()).await; + assert!(header.is_ok()); + + // fetch block + let block = sync_committee_prover.fetch_block("100".to_string()).await; + assert!(block.is_ok()); + // fetch signed beacon block + let signed_beacon_block = sync_committee_prover.signed_beacon_block(block.unwrap()); + assert!(signed_beacon_block.is_some()); + + // fetch sigend beacon block header + let signed_beacon_block_header = + sync_committee_prover.signed_beacon_block_header(signed_beacon_block, header.unwrap()); + assert!(signed_beacon_block_header.is_ok()); +} From 122935347a4152f43f357a5ec84269c5c74adbad Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 28 Jan 2023 20:55:50 +0100 Subject: [PATCH 033/182] error in it's crate --- light-client-primitives/Cargo.toml | 1 - light-client-verifier/Cargo.toml | 1 - sync-committee-prover/Cargo.toml | 2 -- sync-committee-prover/src/error.rs | 5 +++++ sync-committee-prover/src/lib.rs | 8 ++------ 5 files changed, 7 insertions(+), 10 deletions(-) create mode 100644 sync-committee-prover/src/error.rs diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index e127bd017..3852ccb25 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -8,7 +8,6 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } -#ethereum-consensus = { path = "../../ethereum-consensus", default-features = false} base2 = {version="0.2.2", default-features=false} ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 51736979f..99fd897df 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -8,7 +8,6 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -#ethereum-consensus = { path = "../../ethereum-consensus" } base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index be7b0e655..f44854438 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -7,9 +7,7 @@ edition = "2021" [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -#ethereum-consensus = { path = "../../ethereum-consensus" } light-client-primitives = {path="../light-client-primitives", default-features = false } -#ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} diff --git a/sync-committee-prover/src/error.rs b/sync-committee-prover/src/error.rs new file mode 100644 index 000000000..9ec588852 --- /dev/null +++ b/sync-committee-prover/src/error.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub enum Error { + AggregateSignatureError, + EmptySignedBeaconBlock, +} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 99d78579a..862f63a27 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -1,3 +1,4 @@ +mod error; mod responses; #[cfg(test)] mod test; @@ -8,6 +9,7 @@ use ethereum_consensus::bellatrix::{ }; use reqwest::Client; +use crate::error::Error; use crate::responses::sync_committee_response::NodeSyncCommittee; use ethereum_consensus::bellatrix::mainnet::{ BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, @@ -42,12 +44,6 @@ pub fn validator_route(state_id: String, validator_index: String) -> String { ) } -#[derive(Debug)] -pub enum Error { - AggregateSignatureError, - EmptySignedBeaconBlock, -} - pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, From 2ef67bbe60f6afbfe7f6fc6e8a8fdedabb794179 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 28 Jan 2023 20:59:01 +0100 Subject: [PATCH 034/182] routes in it's crate --- sync-committee-prover/src/lib.rs | 21 ++------------------- sync-committee-prover/src/routes.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 19 deletions(-) create mode 100644 sync-committee-prover/src/routes.rs diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 862f63a27..5cc5b1b40 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -1,5 +1,6 @@ mod error; mod responses; +mod routes; #[cfg(test)] mod test; @@ -11,6 +12,7 @@ use reqwest::Client; use crate::error::Error; use crate::responses::sync_committee_response::NodeSyncCommittee; +use crate::routes::*; use ethereum_consensus::bellatrix::mainnet::{ BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, @@ -25,25 +27,6 @@ use ethereum_consensus::phase0::mainnet::{ use ethereum_consensus::primitives::{BlsPublicKey, ValidatorIndex}; use ssz_rs::{List, Vector}; -pub fn header_route(block_id: String) -> String { - format!("/eth/v1/beacon/headers/{}", block_id) -} - -pub fn block_route(block_id: String) -> String { - format!("/eth/v2/beacon/blocks/{}", block_id) -} - -pub fn sync_committee_route(state_id: String) -> String { - format!("/eth/v1/beacon/states/{}/sync_committees", state_id) -} - -pub fn validator_route(state_id: String, validator_index: String) -> String { - format!( - "/eth/v1/beacon/states/{}/validators/{}", - state_id, validator_index - ) -} - pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, diff --git a/sync-committee-prover/src/routes.rs b/sync-committee-prover/src/routes.rs new file mode 100644 index 000000000..e47b31607 --- /dev/null +++ b/sync-committee-prover/src/routes.rs @@ -0,0 +1,18 @@ +pub fn header_route(block_id: String) -> String { + format!("/eth/v1/beacon/headers/{}", block_id) +} + +pub fn block_route(block_id: String) -> String { + format!("/eth/v2/beacon/blocks/{}", block_id) +} + +pub fn sync_committee_route(state_id: String) -> String { + format!("/eth/v1/beacon/states/{}/sync_committees", state_id) +} + +pub fn validator_route(state_id: String, validator_index: String) -> String { + format!( + "/eth/v1/beacon/states/{}/validators/{}", + state_id, validator_index + ) +} From 5bb902e2fbd268f8b700dfb3146cec7598c7a1d8 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 28 Jan 2023 21:03:28 +0100 Subject: [PATCH 035/182] use types for SignedBeaconBlock and BeaconBlock --- sync-committee-prover/src/lib.rs | 84 ++++++++++++-------------------- 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 5cc5b1b40..30c11d162 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -27,6 +27,34 @@ use ethereum_consensus::phase0::mainnet::{ use ethereum_consensus::primitives::{BlsPublicKey, ValidatorIndex}; use ssz_rs::{List, Vector}; +type BeaconBlockType = BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + +type SignedBeaconBlockType = SignedBeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, @@ -160,33 +188,9 @@ impl SyncCommitteeProver { pub fn signed_beacon_block( &self, - beacon_block: BeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, + beacon_block: BeaconBlockType ) -> Option< - SignedBeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, + SignedBeaconBlockType, > { let attestations = beacon_block.body.attestations.clone(); let signatures: Vec<_> = attestations @@ -197,19 +201,7 @@ impl SyncCommitteeProver { let aggregate_signature = aggregate(signatures.as_ref()).map_err(|_| Error::AggregateSignatureError); - let signed_beacon_block = SignedBeaconBlock::< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - > { + let signed_beacon_block = SignedBeaconBlockType { message: beacon_block, signature: aggregate_signature.unwrap(), }; @@ -220,19 +212,7 @@ impl SyncCommitteeProver { pub fn signed_beacon_block_header( &self, signed_beacon_block: Option< - SignedBeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, + SignedBeaconBlockType >, beacon_block_header: BeaconBlockHeader, ) -> Result { From 2439716cd771bd482160899aa8c64118868c18f4 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 31 Jan 2023 00:05:11 +0100 Subject: [PATCH 036/182] fetch beacon state --- sync-committee-prover/Cargo.toml | 1 - sync-committee-prover/src/lib.rs | 55 ++++++++++++++----- .../src/responses/beacon_state_response.rs | 26 +++++++++ sync-committee-prover/src/responses/mod.rs | 1 + sync-committee-prover/src/routes.rs | 3 + sync-committee-prover/src/test.rs | 12 ++++ 6 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 sync-committee-prover/src/responses/beacon_state_response.rs diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index f44854438..69380d04e 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } light-client-primitives = {path="../light-client-primitives", default-features = false } ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } - reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 30c11d162..ee545ea1b 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -6,7 +6,8 @@ mod test; use ethereum_consensus::altair::Validator; use ethereum_consensus::bellatrix::{ - BeaconBlock, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, + BeaconBlock, BeaconBlockHeader, BeaconState, SignedBeaconBlock, SignedBeaconBlockHeader, + SyncCommittee, }; use reqwest::Client; @@ -41,7 +42,7 @@ type BeaconBlockType = BeaconBlock< MAX_TRANSACTIONS_PER_PAYLOAD, >; -type SignedBeaconBlockType = SignedBeaconBlock< +type SignedBeaconBlockType = SignedBeaconBlock< MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_ATTESTER_SLASHINGS, @@ -125,8 +126,7 @@ impl SyncCommitteeProver { let response_data = response .json::() - .await - .unwrap(); + .await?; let sync_committee = response_data.data; @@ -144,14 +144,47 @@ impl SyncCommitteeProver { let response_data = response .json::() - .await - .unwrap(); + .await?; let validator = response_data.data.validator; Ok(validator) } + pub async fn fetch_beacon_state( + &self, + state_id: String, + ) -> Result< + BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + reqwest::Error, + > { + let path = beacon_state_route(state_id); + let full_url = format!("{}/{}", self.node_url.clone(), path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response + .json::() + .await?; + + let beacon_state = response_data.data; + + Ok(beacon_state) + } + pub async fn fetch_processed_sync_committee( &self, state_id: String, @@ -188,10 +221,8 @@ impl SyncCommitteeProver { pub fn signed_beacon_block( &self, - beacon_block: BeaconBlockType - ) -> Option< - SignedBeaconBlockType, - > { + beacon_block: BeaconBlockType, + ) -> Option { let attestations = beacon_block.body.attestations.clone(); let signatures: Vec<_> = attestations .iter() @@ -211,9 +242,7 @@ impl SyncCommitteeProver { pub fn signed_beacon_block_header( &self, - signed_beacon_block: Option< - SignedBeaconBlockType - >, + signed_beacon_block: Option, beacon_block_header: BeaconBlockHeader, ) -> Result { if signed_beacon_block.is_none() { diff --git a/sync-committee-prover/src/responses/beacon_state_response.rs b/sync-committee-prover/src/responses/beacon_state_response.rs new file mode 100644 index 000000000..d9155750b --- /dev/null +++ b/sync-committee-prover/src/responses/beacon_state_response.rs @@ -0,0 +1,26 @@ +use ethereum_consensus::bellatrix::BeaconState; + +use ethereum_consensus::bellatrix::mainnet::{ + BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, + ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, + MAX_TRANSACTIONS_PER_PAYLOAD, MAX_VALIDATORS_PER_COMMITTEE, SLOTS_PER_HISTORICAL_ROOT, + SYNC_COMMITTEE_SIZE, VALIDATOR_REGISTRY_LIMIT, +}; +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + version: String, + pub(crate) data: BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, +} diff --git a/sync-committee-prover/src/responses/mod.rs b/sync-committee-prover/src/responses/mod.rs index 41b26cd36..2b5846289 100644 --- a/sync-committee-prover/src/responses/mod.rs +++ b/sync-committee-prover/src/responses/mod.rs @@ -1,4 +1,5 @@ pub mod beacon_block_header_response; pub mod beacon_block_response; +pub mod beacon_state_response; pub mod sync_committee_response; pub mod validator_response; diff --git a/sync-committee-prover/src/routes.rs b/sync-committee-prover/src/routes.rs index e47b31607..d536a9e93 100644 --- a/sync-committee-prover/src/routes.rs +++ b/sync-committee-prover/src/routes.rs @@ -16,3 +16,6 @@ pub fn validator_route(state_id: String, validator_index: String) -> String { state_id, validator_index ) } +pub fn beacon_state_route(state_id: String) -> String { + format!("/eth/v2/debug/beacon/states/{}", state_id) +} diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 3d1dce550..cc221f7f4 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -90,3 +90,15 @@ async fn fetch_signed_beacon_block_header_works() { sync_committee_prover.signed_beacon_block_header(signed_beacon_block, header.unwrap()); assert!(signed_beacon_block_header.is_ok()); } + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_beacon_state_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let beacon_state = sync_committee_prover + .fetch_beacon_state("genesis".to_string()) + .await; + assert!(beacon_state.is_ok()); +} From d68ff1d18e801b6eef7079f288a3d740b711360a Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 31 Jan 2023 15:59:50 +0100 Subject: [PATCH 037/182] state root and block header root matches --- sync-committee-prover/src/test.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index cc221f7f4..623c9200b 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -1,4 +1,5 @@ use super::*; +use ssz_rs::Merkleized; #[cfg(test)] #[allow(non_snake_case)] @@ -102,3 +103,25 @@ async fn fetch_beacon_state_works() { .await; assert!(beacon_state.is_ok()); } + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn state_root_and_block_header_root_matches() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let beacon_state = sync_committee_prover + .fetch_beacon_state("100".to_string()) + .await; + assert!(beacon_state.is_ok()); + + let block_header = sync_committee_prover.fetch_header("100".to_string()).await; + assert!(block_header.is_ok()); + + let state = beacon_state.unwrap(); + let block_header = block_header.unwrap(); + let hash_tree_root = state.clone().hash_tree_root(); + + assert!(block_header.state_root == hash_tree_root.unwrap()); + +} From 9c4ee1646a60494bd14e761da5a83e20c592203d Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 31 Jan 2023 22:55:58 +0100 Subject: [PATCH 038/182] remove redundant index storage introduce helper function --- sync-committee-prover/src/lib.rs | 27 ++++++++++++++++----------- sync-committee-prover/src/test.rs | 3 +-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index ee545ea1b..cd679dc5e 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -73,7 +73,7 @@ impl SyncCommitteeProver { block_id: String, ) -> Result { let path = header_route(block_id); - let full_url = format!("{}{}", self.node_url.clone(), path); + let full_url = self.generate_route(path); let response = self.client.get(full_url).send().await?; let response_data = response .json::() @@ -103,7 +103,7 @@ impl SyncCommitteeProver { reqwest::Error, > { let path = block_route(block_id); - let full_url = format!("{}/{}", self.node_url.clone(), path); + let full_url = self.generate_route(path); let response = self.client.get(full_url).send().await?; @@ -120,7 +120,7 @@ impl SyncCommitteeProver { state_id: String, ) -> Result { let path = sync_committee_route(state_id); - let full_url = format!("{}/{}", self.node_url.clone(), path); + let full_url = self.generate_route(path); let response = self.client.get(full_url).send().await?; @@ -138,7 +138,7 @@ impl SyncCommitteeProver { validator_index: String, ) -> Result { let path = validator_route(state_id, validator_index); - let full_url = format!("{}/{}", self.node_url.clone(), path); + let full_url = self.generate_route(path); let response = self.client.get(full_url).send().await?; @@ -172,7 +172,7 @@ impl SyncCommitteeProver { reqwest::Error, > { let path = beacon_state_route(state_id); - let full_url = format!("{}/{}", self.node_url.clone(), path); + let full_url = self.generate_route(path); let response = self.client.get(full_url).send().await?; @@ -193,20 +193,21 @@ impl SyncCommitteeProver { let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; let mut validators: List = Default::default(); - let mut validator_indexes: Vec = Vec::new(); - - for mut validator_index in node_sync_committee.validators { + for mut validator_index in node_sync_committee.validators.clone() { // fetches validator based on validator index let validator = self .fetch_validator(state_id.clone(), validator_index.clone()) .await?; validators.push(validator); - validator_indexes.push(validator_index.parse().unwrap()); } - let public_keys_vector = validator_indexes + let public_keys_vector = node_sync_committee + .validators .into_iter() - .map(|i| validators[i].public_key.clone()) + .map(|i| { + let validator_index: ValidatorIndex = i.parse().unwrap(); + validators[validator_index].public_key.clone() + }) .collect::>(); let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector).unwrap(); @@ -256,4 +257,8 @@ impl SyncCommitteeProver { Ok(signed_beacon_block_header) } + + fn generate_route(&self, path: String) -> String { + format!("{}{}", self.node_url.clone(), path) + } } diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 623c9200b..44f7205e3 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -120,8 +120,7 @@ async fn state_root_and_block_header_root_matches() { let state = beacon_state.unwrap(); let block_header = block_header.unwrap(); - let hash_tree_root = state.clone().hash_tree_root(); + let hash_tree_root = state.clone().hash_tree_root(); assert!(block_header.state_root == hash_tree_root.unwrap()); - } From 4c982dc83aeba896816a44e250cab2671059639b Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 1 Feb 2023 17:42:06 +0100 Subject: [PATCH 039/182] provers --- Cargo.toml | 3 + light-client-primitives/Cargo.toml | 2 +- sync-committee-prover/Cargo.toml | 4 +- sync-committee-prover/src/lib.rs | 127 ++++++++++++++++++++++++----- 4 files changed, 113 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b87e07965..17f68bdd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ members = [ "ics15-ethereum", "sync-committee-prover" ] + +[patch."https://github.com/ralexstokes/ssz-rs"] +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "seun/ssz-merkle-multi-proof-phase-1"} diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index 3852ccb25..b58cc2e4a 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -9,5 +9,5 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } base2 = {version="0.2.2", default-features=false} -ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev="ea113b48f3ad134603f11c9be9f5fc696d30c259", default-features=false, features=["serde", "std"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index 69380d04e..c088253fb 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -8,9 +8,11 @@ edition = "2021" [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } light-client-primitives = {path="../light-client-primitives", default-features = false } -ssz-rs = { git = "https://github.com/Snowfork/ssz_rs", branch="feat/contribution", default-features=false, features=["serde", "std"] } +#ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev="ea113b48f3ad134603f11c9be9f5fc696d30c259", default-features=false, features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} +anyhow = "1.0.68" actix-rt = "*" diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index cd679dc5e..4b22efced 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -6,8 +6,8 @@ mod test; use ethereum_consensus::altair::Validator; use ethereum_consensus::bellatrix::{ - BeaconBlock, BeaconBlockHeader, BeaconState, SignedBeaconBlock, SignedBeaconBlockHeader, - SyncCommittee, + BeaconBlock, BeaconBlockBody, BeaconBlockHeader, BeaconState, SignedBeaconBlock, + SignedBeaconBlockHeader, SyncCommittee, }; use reqwest::Client; @@ -25,8 +25,13 @@ use ethereum_consensus::phase0::mainnet::{ MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }; -use ethereum_consensus::primitives::{BlsPublicKey, ValidatorIndex}; -use ssz_rs::{List, Vector}; +use ethereum_consensus::primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}; +use light_client_primitives::types::{ + ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, +}; +use light_client_primitives::util::get_subtree_index; +use ssz_rs::{List, Node, Vector}; type BeaconBlockType = BeaconBlock< MAX_PROPOSER_SLASHINGS, @@ -56,6 +61,21 @@ type SignedBeaconBlockType = SignedBeaconBlock< MAX_TRANSACTIONS_PER_PAYLOAD, >; +pub type BeaconStateType = BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, @@ -154,23 +174,7 @@ impl SyncCommitteeProver { pub async fn fetch_beacon_state( &self, state_id: String, - ) -> Result< - BeaconState< - SLOTS_PER_HISTORICAL_ROOT, - HISTORICAL_ROOTS_LIMIT, - ETH1_DATA_VOTES_BOUND, - VALIDATOR_REGISTRY_LIMIT, - EPOCHS_PER_HISTORICAL_VECTOR, - EPOCHS_PER_SLASHINGS_VECTOR, - MAX_VALIDATORS_PER_COMMITTEE, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, - reqwest::Error, - > { + ) -> Result { let path = beacon_state_route(state_id); let full_url = self.generate_route(path); @@ -258,7 +262,88 @@ impl SyncCommitteeProver { Ok(signed_beacon_block_header) } + async fn fetch_latest_finalized_block( + &self, + ) -> Result<(BeaconBlockHeader, BeaconBlockType), reqwest::Error> { + let block_header = self.fetch_header("finalized".to_string()).await?; + let block = self.fetch_block("finalized".to_string()).await?; + + Ok((block_header, block)) + } + fn generate_route(&self, path: String) -> String { format!("{}{}", self.node_url.clone(), path) } } + +fn get_attestation_slots_for_finalized_header(finalized_header: &BeaconBlockHeader) -> Slot { + let finalized_header_slot = finalized_header.slot; + + // given that an epoch is 32 slots and blocks are finalized every 2 epochs + // so the attested slot for a finalized block is 64 slots away + let attested_slot = finalized_header_slot + 64; + + attested_slot +} + +fn prove_beacon_state_values( + data: BeaconStateType, + indices: &[usize], +) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(data, indices)?; + Ok(proof) +} + +fn prove_beacon_block_values( + data: BeaconBlockType, + indices: &[usize], +) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(data, indices)?; + Ok(proof) +} + +fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result { + let indices = [ + EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, + ]; + // generate multi proofs + let multi_proof = ssz_rs::generate_proof( + block.body.execution_payload.clone(), + indices.as_slice() + )?; + + let execution_payload_index = [get_subtree_index(EXECUTION_PAYLOAD_INDEX) as usize]; + let execution_payload_branch = ssz_rs::generate_proof( + block.body.clone(), + execution_payload_index.as_slice(), + )?; + + Ok(ExecutionPayloadProof { + state_root: block.body.execution_payload.state_root, + block_number: block.body.execution_payload.block_number, + multi_proof: multi_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect(), + execution_payload_branch: execution_payload_branch + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect(), + }) +} + +fn prove_sync_committee_update_and_finalized_header( + state: BeaconStateType, +) -> anyhow::Result> { + let indices = [ + NEXT_SYNC_COMMITTEE_INDEX as usize, + get_subtree_index(FINALIZED_ROOT_INDEX) as usize, + ]; + let multi_proof = ssz_rs::generate_proof( + state.clone(), + indices.as_slice(), + )?; + + Ok(multi_proof) +} From 5dd679811c2546d8614112f702b733560279bc26 Mon Sep 17 00:00:00 2001 From: Damilare Date: Thu, 2 Feb 2023 00:38:29 +0100 Subject: [PATCH 040/182] fix errors relating ssz-rs --- Cargo.toml | 3 --- light-client-primitives/Cargo.toml | 4 ++-- light-client-verifier/Cargo.toml | 4 ++-- sync-committee-prover/Cargo.toml | 5 ++--- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 17f68bdd1..b87e07965 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,3 @@ members = [ "ics15-ethereum", "sync-committee-prover" ] - -[patch."https://github.com/ralexstokes/ssz-rs"] -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "seun/ssz-merkle-multi-proof-phase-1"} diff --git a/light-client-primitives/Cargo.toml b/light-client-primitives/Cargo.toml index b58cc2e4a..4e83181c9 100644 --- a/light-client-primitives/Cargo.toml +++ b/light-client-primitives/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support", default-features = false } base2 = {version="0.2.2", default-features=false} -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev="ea113b48f3ad134603f11c9be9f5fc696d30c259", default-features=false, features=["serde", "std"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 99fd897df..124982664 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } base2 = {version="0.2.2", default-features=false} light-client-primitives = {path="../light-client-primitives", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index c088253fb..e205045ea 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -6,10 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } light-client-primitives = {path="../light-client-primitives", default-features = false } -#ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="seun/ssz-merkle-multi-proof-phase-1", features=["serde"] } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev="ea113b48f3ad134603f11c9be9f5fc696d30c259", default-features=false, features=["serde", "std"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} From 6b9892103a9a534929a8a77a4107f3043dcefad8 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Thu, 2 Feb 2023 10:42:50 +0100 Subject: [PATCH 041/182] remove ics15-ethereum --- Cargo.lock | 1936 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 - ics15-ethereum/Cargo.toml | 9 - ics15-ethereum/src/lib.rs | 14 - 4 files changed, 1936 insertions(+), 24 deletions(-) create mode 100644 Cargo.lock delete mode 100644 ics15-ethereum/Cargo.toml delete mode 100644 ics15-ethereum/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..6097dadc3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1936 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "amcl" +version = "0.3.0" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" + +[[package]] +name = "anyhow" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "as-any" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +dependencies = [ + "int", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +dependencies = [ + "base64 0.13.1", + "bs58", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "ethereum-consensus" +version = "0.1.1" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#9862cab0797fe80c27f435b5b0313f32c24708da" +dependencies = [ + "async-stream", + "bs58", + "enr", + "error-chain", + "getrandom", + "hashbrown 0.13.2", + "hex", + "integer-sqrt", + "milagro_bls", + "multiaddr", + "multihash", + "rand", + "serde", + "serde_json", + "sha2 0.9.9", + "ssz-rs", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "int" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +dependencies = [ + "num-traits", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "light-client-primitives" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "hex-literal", + "ssz-rs", +] + +[[package]] +name = "light-client-verifier" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "light-client-primitives", + "milagro_bls", + "ssz-rs", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "milagro_bls" +version = "1.5.1" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +dependencies = [ + "amcl", + "rand", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.42.0", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=dami/seun-multi-proofs#24f04b8daefc110aff6dd8f20778f3da95a9cdf7" +dependencies = [ + "as-any", + "bitvec", + "hex", + "itertools", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive", + "thiserror", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=dami/seun-multi-proofs#24f04b8daefc110aff6dd8f20778f3da95a9cdf7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync-committee-prover" +version = "0.1.0" +dependencies = [ + "actix-rt", + "anyhow", + "ethereum-consensus", + "light-client-primitives", + "reqwest", + "serde", + "serde_json", + "ssz-rs", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.42.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/Cargo.toml b/Cargo.toml index b87e07965..82f538aa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,5 @@ resolver = "2" members = [ "light-client-verifier", "light-client-primitives", - "ics15-ethereum", "sync-committee-prover" ] diff --git a/ics15-ethereum/Cargo.toml b/ics15-ethereum/Cargo.toml deleted file mode 100644 index bc7bf5b27..000000000 --- a/ics15-ethereum/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "ics15-ethereum" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/ics15-ethereum/src/lib.rs b/ics15-ethereum/src/lib.rs deleted file mode 100644 index 7d12d9af8..000000000 --- a/ics15-ethereum/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} From 2dd8e30d77f54aa08ff67fbbd327f7b2e9885537 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 4 Feb 2023 09:26:53 +0100 Subject: [PATCH 042/182] test for prover --- light-client-primitives/src/types.rs | 4 +- light-client-verifier/src/light_client.rs | 7 ++ sync-committee-prover/Cargo.toml | 5 + sync-committee-prover/src/lib.rs | 102 ++++++--------- sync-committee-prover/src/test.rs | 145 ++++++++++++++++------ 5 files changed, 162 insertions(+), 101 deletions(-) diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs index 17b63b292..69305e0ee 100644 --- a/light-client-primitives/src/types.rs +++ b/light-client-primitives/src/types.rs @@ -90,7 +90,7 @@ pub struct SyncCommitteeUpdate { // actual sync committee pub next_sync_committee: SyncCommittee, // sync committee, ssz merkle proof. - pub next_sync_committee_branch: [Hash32; NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2], + pub next_sync_committee_branch: Vec, } /// Minimum state required by the light client to validate new sync committee attestations @@ -115,7 +115,7 @@ pub struct LightClientUpdate { /// execution payload of the finalized header pub execution_payload: ExecutionPayloadProof, /// the ssz merkle proof for this header in the attested header, finalized headers lag by 2 epochs. - pub finality_branch: [Hash32; FINALIZED_ROOT_INDEX_FLOOR_LOG_2], + pub finality_branch: Vec, /// signature & participation bits pub sync_aggregate: SyncAggregate, /// slot at which signature was produced diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 496505585..f5b69805b 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use base2::Base2; use core::borrow::Borrow; use core::fmt::{Display, Formatter}; +use ethereum_consensus::altair::{FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2}; use ethereum_consensus::altair::mainnet::SYNC_COMMITTEE_SIZE; use ethereum_consensus::bellatrix::compute_domain; use ethereum_consensus::domains::DomainType; @@ -34,6 +35,12 @@ impl EthLightClient { trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { + if update.finality_branch.len() != FINALIZED_ROOT_INDEX_FLOOR_LOG_2 as usize && + update.sync_committee_update.is_some() && + update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize { + Err(Error::InvalidUpdate)? + } + // Verify sync committee has super majority participants let sync_committee_bits = update.sync_aggregate.sync_committee_bits; let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index e205045ea..728120acd 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] light-client-primitives = {path="../light-client-primitives", default-features = false } +light-client-verifier = {path="../light-client-verifier", default-features = false } ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} @@ -14,4 +15,8 @@ serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} anyhow = "1.0.68" actix-rt = "*" +tokio = { version = "1.18.2", features = ["full"]} +tokio-stream = { version = "0.1.8" } +async-stream = { version = "0.3.3"} + diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 4b22efced..2fbcf76cc 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -26,12 +26,9 @@ use ethereum_consensus::phase0::mainnet::{ SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }; use ethereum_consensus::primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}; -use light_client_primitives::types::{ - ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, -}; +use light_client_primitives::types::{ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, BlockRootsProof}; use light_client_primitives::util::get_subtree_index; -use ssz_rs::{List, Node, Vector}; +use ssz_rs::{Bitlist, List, Node, Vector}; type BeaconBlockType = BeaconBlock< MAX_PROPOSER_SLASHINGS, @@ -224,44 +221,6 @@ impl SyncCommitteeProver { Ok(sync_committee) } - pub fn signed_beacon_block( - &self, - beacon_block: BeaconBlockType, - ) -> Option { - let attestations = beacon_block.body.attestations.clone(); - let signatures: Vec<_> = attestations - .iter() - .map(|sig| sig.signature.clone()) - .collect(); - - let aggregate_signature = - aggregate(signatures.as_ref()).map_err(|_| Error::AggregateSignatureError); - - let signed_beacon_block = SignedBeaconBlockType { - message: beacon_block, - signature: aggregate_signature.unwrap(), - }; - - Some(signed_beacon_block) - } - - pub fn signed_beacon_block_header( - &self, - signed_beacon_block: Option, - beacon_block_header: BeaconBlockHeader, - ) -> Result { - if signed_beacon_block.is_none() { - return Err(Error::EmptySignedBeaconBlock); - } - - let signed_beacon_block_header = SignedBeaconBlockHeader { - message: beacon_block_header, - signature: signed_beacon_block.unwrap().signature, - }; - - Ok(signed_beacon_block_header) - } - async fn fetch_latest_finalized_block( &self, ) -> Result<(BeaconBlockHeader, BeaconBlockType), reqwest::Error> { @@ -308,16 +267,12 @@ fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result anyhow::Result anyhow::Result> { - let indices = [ - NEXT_SYNC_COMMITTEE_INDEX as usize, - get_subtree_index(FINALIZED_ROOT_INDEX) as usize, - ]; - let multi_proof = ssz_rs::generate_proof( - state.clone(), - indices.as_slice(), - )?; +fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result> { + let indices = vec![get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize]; + let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; + + Ok(proof) +} + +fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { + let indices = vec![get_subtree_index(FINALIZED_ROOT_INDEX) as usize]; + let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; + + Ok(proof) +} + +// function that generates block roots proof +// block number and convert to get_subtree_index +// beacon state has a block roots vec, pass the block root to generate proof +fn prove_block_roots_proof(state: BeaconStateType, block_number: u64) -> anyhow::Result { + let indices = vec![get_subtree_index(block_number) as usize]; + let proof = ssz_rs::generate_proof(state.block_roots, indices.as_slice())?; - Ok(multi_proof) + Ok(BlockRootsProof { + block_header_index: block_number, + block_header_branch: proof.into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect() + }) } + +// prove the block roots vec inside the beacon state which will be the block roots branch + + +// when aggr sigs, create a new bit list diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 44f7205e3..f5df415ba 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -1,5 +1,13 @@ use super::*; use ssz_rs::Merkleized; +use tokio_stream::wrappers::IntervalStream; +use std::time::Duration; +use ethereum_consensus::altair::NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2; +use tokio::time; +use tokio_stream::StreamExt; +use light_client_primitives::types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}; +use light_client_primitives::util::compute_sync_committee_period_at_slot; +use light_client_verifier::light_client::EthLightClient; #[cfg(test)] #[allow(non_snake_case)] @@ -7,7 +15,7 @@ use ssz_rs::Merkleized; async fn fetch_block_header_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block_header = sync_committee_prover.fetch_header("100".to_string()).await; + let block_header = sync_committee_prover.fetch_header("finalized".to_string()).await; assert!(block_header.is_ok()); } @@ -57,40 +65,6 @@ async fn fetch_processed_sync_committee_works() { assert!(validator.is_ok()); } -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_signed_beacon_block_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_block("100".to_string()).await; - assert!(block.is_ok()); - let signed_beacon_block = sync_committee_prover.signed_beacon_block(block.unwrap()); - assert!(signed_beacon_block.is_some()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_signed_beacon_block_header_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - // fetch beacon block header - let header = sync_committee_prover.fetch_header("100".to_string()).await; - assert!(header.is_ok()); - - // fetch block - let block = sync_committee_prover.fetch_block("100".to_string()).await; - assert!(block.is_ok()); - // fetch signed beacon block - let signed_beacon_block = sync_committee_prover.signed_beacon_block(block.unwrap()); - assert!(signed_beacon_block.is_some()); - - // fetch sigend beacon block header - let signed_beacon_block_header = - sync_committee_prover.signed_beacon_block_header(signed_beacon_block, header.unwrap()); - assert!(signed_beacon_block_header.is_ok()); -} #[cfg(test)] #[allow(non_snake_case)] @@ -124,3 +98,104 @@ async fn state_root_and_block_header_root_matches() { assert!(block_header.state_root == hash_tree_root.unwrap()); } + +// use tokio interval(should run every 13 minutes) +// every 13 minutes, fetch latest finalized block +// then prove the execution payload +// prove the finality branch + +// prove sync committee if there is a sync committee update +// to prove sync comnmittee update, calculate state_period and the update_attested_period +// ensure they are the same, and then prove sync committee + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_prover() { + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(14 * 60))); + + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block_header = sync_committee_prover.fetch_header("finalized".to_string()).await.unwrap(); + + let state = sync_committee_prover.fetch_beacon_state("finalized".to_string()).await.unwrap(); + + let mut client_state = LightClientState { + finalized_header: block_header.clone(), + current_sync_committee: state.current_sync_committee, + next_sync_committee: state.next_sync_committee + }; + + + while let Some(_ts) = stream.next().await { + let block = sync_committee_prover.fetch_block("finalized".to_string()).await; + assert!(block.is_ok()); + + let block = block.unwrap(); + + let execution_payload_proof = prove_execution_payload(block.clone()); + assert!(execution_payload_proof.is_ok()); + + let state = sync_committee_prover.fetch_beacon_state(block.slot.to_string()).await; + assert!(state.is_ok()); + + let state = state.unwrap(); + + let finality_branch_proof = prove_finalized_header(state.clone()).unwrap(); + let finality_branch_proof = finality_branch_proof.into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); + //let block_header = sync_committee_prover.fetch_header(block.slot.to_string()).await; + //let block_header = sync_committee_prover.fetch_header("finalized".to_string()).await; + //dbg!(block_header.unwrap()); + //assert!(block_header); + + let block_header = block_header.clone(); + + let state_period = + compute_sync_committee_period_at_slot(block_header.slot); + + let attested_header_slot = get_attestation_slots_for_finalized_header(&block_header); + + let attested_header = sync_committee_prover.fetch_header(attested_header_slot.to_string()).await.unwrap(); + + let update_attested_period = + compute_sync_committee_period_at_slot(attested_header_slot); + + let sync_committee_update = if state_period == attested_header_slot{ + let sync_committee_proof = prove_sync_committee_update(state).unwrap(); + + let sync_committee_proof = sync_committee_proof.into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); + + + let sync_committee = sync_committee_prover.fetch_processed_sync_committee(block.slot.to_string()).await; + assert!(sync_committee.is_ok()); + + let sync_committee = sync_committee.unwrap(); + + Some (SyncCommitteeUpdate { + next_sync_committee: sync_committee, + next_sync_committee_branch: sync_committee_proof + }) + } else { + None + }; + + + // construct light client + let light_client_update = LightClientUpdate { + attested_header, + sync_committee_update, + finalized_header: block_header, + execution_payload: execution_payload_proof.unwrap(), + finality_branch: finality_branch_proof, + sync_aggregate: block.body.sync_aggregate, + signature_slot: attested_header_slot, + ancestor_blocks: vec![] + }; + + let new_light_client_state = EthLightClient::verify_sync_committee_attestation(client_state.clone(), light_client_update); + } +} From 4efb44d1dbe5f164b48c26a6fa6a70762fe049d2 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 4 Feb 2023 09:34:15 +0100 Subject: [PATCH 043/182] lock --- Cargo.lock | 1944 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1944 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..75bfcc17e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1944 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "amcl" +version = "0.3.0" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" + +[[package]] +name = "anyhow" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "as-any" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +dependencies = [ + "int", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +dependencies = [ + "base64 0.13.1", + "bs58", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "ethereum-consensus" +version = "0.1.1" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#9862cab0797fe80c27f435b5b0313f32c24708da" +dependencies = [ + "async-stream", + "bs58", + "enr", + "error-chain", + "getrandom", + "hashbrown 0.13.2", + "hex", + "integer-sqrt", + "milagro_bls", + "multiaddr", + "multihash", + "rand", + "serde", + "serde_json", + "sha2 0.9.9", + "ssz-rs", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "ics15-ethereum" +version = "0.1.0" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "int" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +dependencies = [ + "num-traits", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "light-client-primitives" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "hex-literal", + "ssz-rs", +] + +[[package]] +name = "light-client-verifier" +version = "0.1.0" +dependencies = [ + "base2", + "ethereum-consensus", + "light-client-primitives", + "milagro_bls", + "ssz-rs", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "milagro_bls" +version = "1.5.1" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +dependencies = [ + "amcl", + "rand", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.42.0", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=dami/seun-multi-proofs#24f04b8daefc110aff6dd8f20778f3da95a9cdf7" +dependencies = [ + "as-any", + "bitvec", + "hex", + "itertools", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive", + "thiserror", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=dami/seun-multi-proofs#24f04b8daefc110aff6dd8f20778f3da95a9cdf7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync-committee-prover" +version = "0.1.0" +dependencies = [ + "actix-rt", + "anyhow", + "async-stream", + "ethereum-consensus", + "light-client-primitives", + "light-client-verifier", + "reqwest", + "serde", + "serde_json", + "ssz-rs", + "tokio", + "tokio-stream", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.42.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" From 241aa2b515c0afdbd543d541ddb9111dfe34d05e Mon Sep 17 00:00:00 2001 From: David Salami Date: Sat, 4 Feb 2023 11:45:05 +0100 Subject: [PATCH 044/182] try and rewrite integration test --- Cargo.lock | 5 +- light-client-verifier/src/light_client.rs | 17 ++- sync-committee-prover/Cargo.toml | 3 + sync-committee-prover/src/lib.rs | 80 +++++++---- sync-committee-prover/src/routes.rs | 26 ++-- sync-committee-prover/src/test.rs | 164 ++++++++++++---------- 6 files changed, 174 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75bfcc17e..0c5096417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -651,10 +651,6 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "ics15-ethereum" -version = "0.1.0" - [[package]] name = "idna" version = "0.3.0" @@ -1478,6 +1474,7 @@ dependencies = [ "anyhow", "async-stream", "ethereum-consensus", + "hex", "light-client-primitives", "light-client-verifier", "reqwest", diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index f5b69805b..93f3496ac 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -3,8 +3,10 @@ use alloc::vec::Vec; use base2::Base2; use core::borrow::Borrow; use core::fmt::{Display, Formatter}; -use ethereum_consensus::altair::{FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2}; use ethereum_consensus::altair::mainnet::SYNC_COMMITTEE_SIZE; +use ethereum_consensus::altair::{ + FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, +}; use ethereum_consensus::bellatrix::compute_domain; use ethereum_consensus::domains::DomainType; use ethereum_consensus::primitives::Root; @@ -35,9 +37,16 @@ impl EthLightClient { trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { - if update.finality_branch.len() != FINALIZED_ROOT_INDEX_FLOOR_LOG_2 as usize && - update.sync_committee_update.is_some() && - update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize { + if update.finality_branch.len() != FINALIZED_ROOT_INDEX_FLOOR_LOG_2 as usize + && update.sync_committee_update.is_some() + && update + .clone() + .sync_committee_update + .unwrap() + .next_sync_committee_branch + .len() + != NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize + { Err(Error::InvalidUpdate)? } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index 728120acd..fabe30615 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -19,4 +19,7 @@ tokio = { version = "1.18.2", features = ["full"]} tokio-stream = { version = "0.1.8" } async-stream = { version = "0.3.3"} +[dev-dependencies] +hex = "0.4.3" + diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 2fbcf76cc..96a96bde2 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -6,7 +6,7 @@ mod test; use ethereum_consensus::altair::Validator; use ethereum_consensus::bellatrix::{ - BeaconBlock, BeaconBlockBody, BeaconBlockHeader, BeaconState, SignedBeaconBlock, + BeaconBlock, BeaconBlockBody, BeaconBlockHeader, BeaconState, Checkpoint, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, }; use reqwest::Client; @@ -26,7 +26,11 @@ use ethereum_consensus::phase0::mainnet::{ SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }; use ethereum_consensus::primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}; -use light_client_primitives::types::{ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, BlockRootsProof}; +use light_client_primitives::types::{ + BlockRootsProof, ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, + NEXT_SYNC_COMMITTEE_INDEX, +}; use light_client_primitives::util::get_subtree_index; use ssz_rs::{Bitlist, List, Node, Vector}; @@ -85,12 +89,28 @@ impl SyncCommitteeProver { SyncCommitteeProver { node_url, client } } - pub async fn fetch_header( - &self, - block_id: String, - ) -> Result { + pub async fn fetch_finalized_checkpoint(&self) -> Result { + let full_url = self.generate_route(&finality_checkpoints("head")); + let response = self.client.get(full_url).send().await?; + #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] + struct Response { + execution_optimistic: bool, + data: ResponseData, + } + + #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] + struct ResponseData { + previous_justified: Checkpoint, + current_justified: Checkpoint, + final_justified: Checkpoint, + } + let response_data = response.json::().await?; + Ok(response_data.data.final_justified) + } + + pub async fn fetch_header(&self, block_id: &str) -> Result { let path = header_route(block_id); - let full_url = self.generate_route(path); + let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; let response_data = response .json::() @@ -102,7 +122,7 @@ impl SyncCommitteeProver { } pub async fn fetch_block( &self, - block_id: String, + block_id: &str, ) -> Result< BeaconBlock< MAX_PROPOSER_SLASHINGS, @@ -120,7 +140,7 @@ impl SyncCommitteeProver { reqwest::Error, > { let path = block_route(block_id); - let full_url = self.generate_route(path); + let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; @@ -134,10 +154,10 @@ impl SyncCommitteeProver { } pub async fn fetch_sync_committee( &self, - state_id: String, + state_id: &str, ) -> Result { let path = sync_committee_route(state_id); - let full_url = self.generate_route(path); + let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; @@ -151,11 +171,11 @@ impl SyncCommitteeProver { } pub async fn fetch_validator( &self, - state_id: String, - validator_index: String, + state_id: &str, + validator_index: &str, ) -> Result { let path = validator_route(state_id, validator_index); - let full_url = self.generate_route(path); + let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; @@ -170,10 +190,10 @@ impl SyncCommitteeProver { pub async fn fetch_beacon_state( &self, - state_id: String, + state_id: &str, ) -> Result { let path = beacon_state_route(state_id); - let full_url = self.generate_route(path); + let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; @@ -188,7 +208,7 @@ impl SyncCommitteeProver { pub async fn fetch_processed_sync_committee( &self, - state_id: String, + state_id: &str, ) -> Result, reqwest::Error> { // fetches sync committee from Node let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; @@ -197,7 +217,7 @@ impl SyncCommitteeProver { for mut validator_index in node_sync_committee.validators.clone() { // fetches validator based on validator index let validator = self - .fetch_validator(state_id.clone(), validator_index.clone()) + .fetch_validator(state_id.clone(), &validator_index) .await?; validators.push(validator); } @@ -224,23 +244,26 @@ impl SyncCommitteeProver { async fn fetch_latest_finalized_block( &self, ) -> Result<(BeaconBlockHeader, BeaconBlockType), reqwest::Error> { - let block_header = self.fetch_header("finalized".to_string()).await?; - let block = self.fetch_block("finalized".to_string()).await?; + let block_header = self.fetch_header("finalized").await?; + let block = self.fetch_block("finalized").await?; Ok((block_header, block)) } - fn generate_route(&self, path: String) -> String { + fn generate_route(&self, path: &str) -> String { format!("{}{}", self.node_url.clone(), path) } } -fn get_attestation_slots_for_finalized_header(finalized_header: &BeaconBlockHeader) -> Slot { +fn get_attestation_slots_for_finalized_header( + finalized_header: &BeaconBlockHeader, + slots_per_epoch: u64, +) -> Slot { let finalized_header_slot = finalized_header.slot; // given that an epoch is 32 slots and blocks are finalized every 2 epochs // so the attested slot for a finalized block is 64 slots away - let attested_slot = finalized_header_slot + 64; + let attested_slot = finalized_header_slot + (slots_per_epoch * 2); attested_slot } @@ -305,19 +328,22 @@ fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { // function that generates block roots proof // block number and convert to get_subtree_index // beacon state has a block roots vec, pass the block root to generate proof -fn prove_block_roots_proof(state: BeaconStateType, block_number: u64) -> anyhow::Result { +fn prove_block_roots_proof( + state: BeaconStateType, + block_number: u64, +) -> anyhow::Result { let indices = vec![get_subtree_index(block_number) as usize]; let proof = ssz_rs::generate_proof(state.block_roots, indices.as_slice())?; Ok(BlockRootsProof { block_header_index: block_number, - block_header_branch: proof.into_iter() + block_header_branch: proof + .into_iter() .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect() + .collect(), }) } // prove the block roots vec inside the beacon state which will be the block roots branch - // when aggr sigs, create a new bit list diff --git a/sync-committee-prover/src/routes.rs b/sync-committee-prover/src/routes.rs index d536a9e93..a7b398b28 100644 --- a/sync-committee-prover/src/routes.rs +++ b/sync-committee-prover/src/routes.rs @@ -1,21 +1,21 @@ -pub fn header_route(block_id: String) -> String { - format!("/eth/v1/beacon/headers/{}", block_id) +pub fn header_route(block_id: &str) -> String { + format!("/eth/v1/beacon/headers/{block_id}") } -pub fn block_route(block_id: String) -> String { - format!("/eth/v2/beacon/blocks/{}", block_id) +pub fn block_route(block_id: &str) -> String { + format!("/eth/v2/beacon/blocks/{block_id}") } -pub fn sync_committee_route(state_id: String) -> String { - format!("/eth/v1/beacon/states/{}/sync_committees", state_id) +pub fn sync_committee_route(state_id: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/sync_committees") } -pub fn validator_route(state_id: String, validator_index: String) -> String { - format!( - "/eth/v1/beacon/states/{}/validators/{}", - state_id, validator_index - ) +pub fn validator_route(state_id: &str, validator_index: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/validators/{validator_index}") } -pub fn beacon_state_route(state_id: String) -> String { - format!("/eth/v2/debug/beacon/states/{}", state_id) +pub fn beacon_state_route(state_id: &str) -> String { + format!("/eth/v2/debug/beacon/states/{state_id}") +} +pub fn finality_checkpoints(state_id: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/finality_checkpoints") } diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index f5df415ba..572fef566 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -1,13 +1,13 @@ use super::*; -use ssz_rs::Merkleized; -use tokio_stream::wrappers::IntervalStream; -use std::time::Duration; use ethereum_consensus::altair::NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2; -use tokio::time; -use tokio_stream::StreamExt; use light_client_primitives::types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}; use light_client_primitives::util::compute_sync_committee_period_at_slot; use light_client_verifier::light_client::EthLightClient; +use ssz_rs::Merkleized; +use std::time::Duration; +use tokio::time; +use tokio_stream::wrappers::IntervalStream; +use tokio_stream::StreamExt; #[cfg(test)] #[allow(non_snake_case)] @@ -15,7 +15,7 @@ use light_client_verifier::light_client::EthLightClient; async fn fetch_block_header_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block_header = sync_committee_prover.fetch_header("finalized".to_string()).await; + let block_header = sync_committee_prover.fetch_header("finalized").await; assert!(block_header.is_ok()); } @@ -25,7 +25,7 @@ async fn fetch_block_header_works() { async fn fetch_block_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_block("100".to_string()).await; + let block = sync_committee_prover.fetch_block("100").await; assert!(block.is_ok()); } @@ -35,9 +35,7 @@ async fn fetch_block_works() { async fn fetch_sync_committee_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover - .fetch_sync_committee("117".to_string()) - .await; + let block = sync_committee_prover.fetch_sync_committee("117").await; assert!(block.is_ok()); } @@ -47,9 +45,7 @@ async fn fetch_sync_committee_works() { async fn fetch_validator_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let validator = sync_committee_prover - .fetch_validator("2561".to_string(), "48".to_string()) - .await; + let validator = sync_committee_prover.fetch_validator("2561", "48").await; assert!(validator.is_ok()); } @@ -60,21 +56,18 @@ async fn fetch_processed_sync_committee_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let validator = sync_committee_prover - .fetch_processed_sync_committee("2561".to_string()) + .fetch_processed_sync_committee("2561") .await; assert!(validator.is_ok()); } - #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] async fn fetch_beacon_state_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let beacon_state = sync_committee_prover - .fetch_beacon_state("genesis".to_string()) - .await; + let beacon_state = sync_committee_prover.fetch_beacon_state("genesis").await; assert!(beacon_state.is_ok()); } @@ -84,12 +77,10 @@ async fn fetch_beacon_state_works() { async fn state_root_and_block_header_root_matches() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let beacon_state = sync_committee_prover - .fetch_beacon_state("100".to_string()) - .await; + let beacon_state = sync_committee_prover.fetch_beacon_state("100").await; assert!(beacon_state.is_ok()); - let block_header = sync_committee_prover.fetch_header("100".to_string()).await; + let block_header = sync_committee_prover.fetch_header("100").await; assert!(block_header.is_ok()); let state = beacon_state.unwrap(); @@ -112,90 +103,117 @@ async fn state_root_and_block_header_root_matches() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_prover() { - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(14 * 60))); + // In test config an epoch is 6 slots and we expect finalization every two epochs, + // a slot is 12 seconds so that brings us to 144 seconds + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block_header = sync_committee_prover.fetch_header("finalized".to_string()).await.unwrap(); + let block_header = sync_committee_prover + .fetch_header("finalized") + .await + .unwrap(); - let state = sync_committee_prover.fetch_beacon_state("finalized".to_string()).await.unwrap(); + let state = sync_committee_prover + .fetch_beacon_state("finalized") + .await + .unwrap(); let mut client_state = LightClientState { finalized_header: block_header.clone(), current_sync_committee: state.current_sync_committee, - next_sync_committee: state.next_sync_committee + next_sync_committee: state.next_sync_committee, }; + while let Some(_ts) = stream.next().await { + let finality_checkpoint = sync_committee_prover + .fetch_finalized_checkpoint() + .await + .unwrap(); + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); - while let Some(_ts) = stream.next().await { - let block = sync_committee_prover.fetch_block("finalized".to_string()).await; - assert!(block.is_ok()); + if finalized_block.slot <= client_state.finalized_header.slot { + continue; + } - let block = block.unwrap(); + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - let execution_payload_proof = prove_execution_payload(block.clone()); - assert!(execution_payload_proof.is_ok()); + let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); - let state = sync_committee_prover.fetch_beacon_state(block.slot.to_string()).await; - assert!(state.is_ok()); + let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); - let state = state.unwrap(); + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_header_slot.to_string().as_str()) + .await + .unwrap(); - let finality_branch_proof = prove_finalized_header(state.clone()).unwrap(); - let finality_branch_proof = finality_branch_proof.into_iter() + let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); + let finality_branch_proof = finality_branch_proof + .into_iter() .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect::>(); - //let block_header = sync_committee_prover.fetch_header(block.slot.to_string()).await; - //let block_header = sync_committee_prover.fetch_header("finalized".to_string()).await; - //dbg!(block_header.unwrap()); - //assert!(block_header); - - let block_header = block_header.clone(); - let state_period = - compute_sync_committee_period_at_slot(block_header.slot); + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - let attested_header_slot = get_attestation_slots_for_finalized_header(&block_header); + let attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await + .unwrap(); - let attested_header = sync_committee_prover.fetch_header(attested_header_slot.to_string()).await.unwrap(); + let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); - let update_attested_period = - compute_sync_committee_period_at_slot(attested_header_slot); + let sync_committee_update = if state_period == attested_header_slot { + let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); - let sync_committee_update = if state_period == attested_header_slot{ - let sync_committee_proof = prove_sync_committee_update(state).unwrap(); + let sync_committee_proof = sync_committee_proof + .into_iter() + .map(|node| { + Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") + }) + .collect::>(); - let sync_committee_proof = sync_committee_proof.into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); + let sync_committee = sync_committee_prover + .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .await + .unwrap(); - - let sync_committee = sync_committee_prover.fetch_processed_sync_committee(block.slot.to_string()).await; - assert!(sync_committee.is_ok()); - - let sync_committee = sync_committee.unwrap(); - - Some (SyncCommitteeUpdate { - next_sync_committee: sync_committee, - next_sync_committee_branch: sync_committee_proof - }) - } else { + Some(SyncCommitteeUpdate { + next_sync_committee: sync_committee, + next_sync_committee_branch: sync_committee_proof, + }) + } else { None - }; - + }; // construct light client - let light_client_update = LightClientUpdate { + let light_client_update = LightClientUpdate { attested_header, sync_committee_update, - finalized_header: block_header, - execution_payload: execution_payload_proof.unwrap(), + finalized_header, + execution_payload: execution_payload_proof, finality_branch: finality_branch_proof, - sync_aggregate: block.body.sync_aggregate, + sync_aggregate: finalized_block.body.sync_aggregate, signature_slot: attested_header_slot, - ancestor_blocks: vec![] + ancestor_blocks: vec![], }; - let new_light_client_state = EthLightClient::verify_sync_committee_attestation(client_state.clone(), light_client_update); + client_state = EthLightClient::verify_sync_committee_attestation( + client_state.clone(), + light_client_update, + ) + .unwrap(); + println!( + "Sucessfully verified Ethereum block at slot {:?}", + client_state.finalized_header.slot + ); } } From 31bf2b687594ce43747b691305d14edc9f73d09e Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 4 Feb 2023 18:43:28 +0100 Subject: [PATCH 045/182] lock --- Cargo.lock | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75bfcc17e..8e57056ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -651,10 +651,6 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "ics15-ethereum" -version = "0.1.0" - [[package]] name = "idna" version = "0.3.0" From 2c542d0899784037d9df7e09e94bd878af9c055e Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 4 Feb 2023 22:42:15 +0100 Subject: [PATCH 046/182] further work and testing --- sync-committee-prover/src/lib.rs | 21 +++---- .../responses/finality_checkpoint_response.rs | 14 +++++ sync-committee-prover/src/responses/mod.rs | 1 + sync-committee-prover/src/test.rs | 63 ++++++++++++++++--- 4 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 sync-committee-prover/src/responses/finality_checkpoint_response.rs diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 96a96bde2..259150f4e 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -9,7 +9,7 @@ use ethereum_consensus::bellatrix::{ BeaconBlock, BeaconBlockBody, BeaconBlockHeader, BeaconState, Checkpoint, SignedBeaconBlock, SignedBeaconBlockHeader, SyncCommittee, }; -use reqwest::Client; +use reqwest::{Client, StatusCode}; use crate::error::Error; use crate::responses::sync_committee_response::NodeSyncCommittee; @@ -92,26 +92,19 @@ impl SyncCommitteeProver { pub async fn fetch_finalized_checkpoint(&self) -> Result { let full_url = self.generate_route(&finality_checkpoints("head")); let response = self.client.get(full_url).send().await?; - #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] - struct Response { - execution_optimistic: bool, - data: ResponseData, - } - #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] - struct ResponseData { - previous_justified: Checkpoint, - current_justified: Checkpoint, - final_justified: Checkpoint, - } - let response_data = response.json::().await?; - Ok(response_data.data.final_justified) + let response_data = response + .json::() + .await?; + Ok(response_data.data.finalized) } pub async fn fetch_header(&self, block_id: &str) -> Result { let path = header_route(block_id); let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; + let status = response.status().as_u16(); + let response_data = response .json::() .await?; diff --git a/sync-committee-prover/src/responses/finality_checkpoint_response.rs b/sync-committee-prover/src/responses/finality_checkpoint_response.rs new file mode 100644 index 000000000..f9e9a20eb --- /dev/null +++ b/sync-committee-prover/src/responses/finality_checkpoint_response.rs @@ -0,0 +1,14 @@ +use ethereum_consensus::bellatrix::Checkpoint; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub(crate) struct Response { + execution_optimistic: bool, + pub data: ResponseData, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseData { + previous_justified: Checkpoint, + current_justified: Checkpoint, + pub finalized: Checkpoint, +} diff --git a/sync-committee-prover/src/responses/mod.rs b/sync-committee-prover/src/responses/mod.rs index 2b5846289..1d63bda98 100644 --- a/sync-committee-prover/src/responses/mod.rs +++ b/sync-committee-prover/src/responses/mod.rs @@ -1,5 +1,6 @@ pub mod beacon_block_header_response; pub mod beacon_block_response; pub mod beacon_state_response; +pub mod finality_checkpoint_response; pub mod sync_committee_response; pub mod validator_response; diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 572fef566..c77dec2f5 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -4,6 +4,7 @@ use light_client_primitives::types::{LightClientState, LightClientUpdate, SyncCo use light_client_primitives::util::compute_sync_committee_period_at_slot; use light_client_verifier::light_client::EthLightClient; use ssz_rs::Merkleized; +use std::thread; use std::time::Duration; use tokio::time; use tokio_stream::wrappers::IntervalStream; @@ -15,7 +16,11 @@ use tokio_stream::StreamExt; async fn fetch_block_header_works() { let node_url: String = "http://localhost:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block_header = sync_committee_prover.fetch_header("finalized").await; + let mut block_header = sync_committee_prover.fetch_header("1000").await; + while block_header.is_err() { + println!("I am running till i am ok. lol"); + block_header = sync_committee_prover.fetch_header("1000").await; + } assert!(block_header.is_ok()); } @@ -90,6 +95,16 @@ async fn state_root_and_block_header_root_matches() { assert!(block_header.state_root == hash_tree_root.unwrap()); } +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_finality_checkpoints_work() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; + assert!(finality_checkpoint.is_ok()); +} + // use tokio interval(should run every 13 minutes) // every 13 minutes, fetch latest finalized block // then prove the execution payload @@ -107,15 +122,27 @@ async fn test_prover() { // a slot is 12 seconds so that brings us to 144 seconds let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://127.0.0.1:3500".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block_header = sync_committee_prover - .fetch_header("finalized") + + let finality_checkpoint = sync_committee_prover + .fetch_finalized_checkpoint() .await .unwrap(); + dbg!(&finality_checkpoint.root); + + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + dbg!(&block_id); + + let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); let state = sync_committee_prover - .fetch_beacon_state("finalized") + .fetch_beacon_state(&block_header.slot.to_string()) .await .unwrap(); @@ -130,14 +157,22 @@ async fn test_prover() { .fetch_finalized_checkpoint() .await .unwrap(); + dbg!(&finality_checkpoint.root); let block_id = { let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); block_id.insert_str(0, "0x"); block_id }; + + dbg!(&block_id); let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); if finalized_block.slot <= client_state.finalized_header.slot { + println!("finalized_block slot is {}", &finalized_block.slot); + println!( + "finalized_header slot is {}", + &client_state.finalized_header.slot + ); continue; } @@ -164,10 +199,22 @@ async fn test_prover() { let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - let attested_header = sync_committee_prover + // purposely for waiting + //println!("sleeping"); + thread::sleep(time::Duration::from_secs(5)); + + let mut attested_header = sync_committee_prover .fetch_header(attested_header_slot.to_string().as_str()) - .await - .unwrap(); + .await; + + while attested_header.is_err() { + println!("I am running till i am ok. lol {}", &block_id); + attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + } + + let attested_header = attested_header.unwrap(); let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); From cca3b4f0ddae278992f4fae00d95ad4c7005dbc9 Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 8 Feb 2023 23:13:59 +0100 Subject: [PATCH 047/182] test execution_payload_proof passes --- Cargo.lock | 11 ++ light-client-verifier/Cargo.toml | 1 + light-client-verifier/src/light_client.rs | 75 ++++++++-- sync-committee-prover/Cargo.toml | 1 + sync-committee-prover/src/lib.rs | 2 +- sync-committee-prover/src/test.rs | 166 +++++++++++++++++++++- 6 files changed, 245 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c5096417..b51b461e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -761,6 +761,15 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "libc-print" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a574491aebd99996f31b32469bbd3b50e580c35f6305663d68f6d6b28eaced09" +dependencies = [ + "libc", +] + [[package]] name = "light-client-primitives" version = "0.1.0" @@ -777,6 +786,7 @@ version = "0.1.0" dependencies = [ "base2", "ethereum-consensus", + "libc-print", "light-client-primitives", "milagro_bls", "ssz-rs", @@ -1473,6 +1483,7 @@ dependencies = [ "actix-rt", "anyhow", "async-stream", + "base2", "ethereum-consensus", "hex", "light-client-primitives", diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 124982664..5e18be9d1 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -12,3 +12,4 @@ light-client-primitives = {path="../light-client-primitives", default-features = ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } +libc-print = {version=" 0.1.20"} diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index 93f3496ac..da0ec01d7 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -12,6 +12,7 @@ use ethereum_consensus::domains::DomainType; use ethereum_consensus::primitives::Root; use ethereum_consensus::signing::compute_signing_root; use ethereum_consensus::state_transition::Context; +use libc_print::libc_println; use light_client_primitives::types::{ AncestryProof, BLOCK_ROOTS_INDEX, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, @@ -47,28 +48,71 @@ impl EthLightClient { .len() != NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize { - Err(Error::InvalidUpdate)? + libc_println!("Invalid update "); + libc_println!( + "update finality branch length {} ", + update.finality_branch.len() + ); + libc_println!( + "FINALIZED_ROOT_INDEX_FLOOR_LOG_2 {}", + FINALIZED_ROOT_INDEX_FLOOR_LOG_2 + ); + libc_println!( + "update next sync committee branch length {} ", + update + .clone() + .sync_committee_update + .unwrap() + .next_sync_committee_branch + .len() + ); + libc_println!( + "NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 {}", + NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 + ); + //Err(Error::InvalidUpdate)? } // Verify sync committee has super majority participants let sync_committee_bits = update.sync_aggregate.sync_committee_bits; let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { - Err(Error::SyncCommitteeParticipantsTooLow)? + libc_println!("SyncCommitteeParticipantsTooLow "); + libc_println!("sync_aggregate_participants {} ", { + sync_aggregate_participants * 3 + }); + libc_println!("sync_committee_bits {}", { + sync_committee_bits.clone().len() * 2 + }); + //Err(Error::SyncCommitteeParticipantsTooLow)? } // Verify update does not skip a sync committee period let is_valid_update = update.signature_slot > update.attested_header.slot && update.attested_header.slot >= update.finalized_header.slot; if !is_valid_update { - Err(Error::InvalidUpdate)? + libc_println!("is_valid_update {} ", is_valid_update); + libc_println!( + "update.signature_slot {} update.attested_header.slot {}", + update.signature_slot, + update.attested_header.slot + ); + libc_println!( + "update.attested_header.slot {} update.finalized_header.slot {}", + update.attested_header.slot, + update.finalized_header.slot + ); + //Err(Error::InvalidUpdate)? } let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); if !(state_period..=state_period + 1).contains(&update_signature_period) { - Err(Error::InvalidUpdate)? + libc_println!("invalid update"); + libc_println!("state_period is {}", state_period); + libc_println!("update_signature_period is {}", update_signature_period); + //Err(Error::InvalidUpdate)? } // Verify update is relevant @@ -152,8 +196,13 @@ impl EthLightClient { ), ); - if is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; + libc_println!( + "valid merkle branch for finalized_root {}", + is_merkle_branch_valid + ); + if !is_merkle_branch_valid { + libc_println!("invalid merkle branch for finalized root"); + //Err(Error::InvalidMerkleBranch)?; } // verify the associated execution header of the finalized beacon header. @@ -206,8 +255,10 @@ impl EthLightClient { ), ); + libc_println!("valid merkle branch for execution_payload_branch"); if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; + libc_println!("invalid merkle branch for execution_payload_branch"); + //Err(Error::InvalidMerkleBranch)?; } if let Some(sync_committee_update) = update.sync_committee_update.clone() { @@ -215,7 +266,8 @@ impl EthLightClient { && sync_committee_update.next_sync_committee != trusted_state.next_sync_committee.clone() { - Err(Error::InvalidUpdate)? + libc_println!("invalid update for sync committee update"); + //rr(Error::InvalidUpdate)? } let next_sync_committee_branch = sync_committee_update @@ -246,8 +298,13 @@ impl EthLightClient { ), ); + libc_println!( + "valid merkle branch for sync committee {}", + is_merkle_branch_valid + ); if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; + libc_println!("invalid merkle branch for sync committee"); + // Err(Error::InvalidMerkleBranch)?; } } diff --git a/sync-committee-prover/Cargo.toml b/sync-committee-prover/Cargo.toml index fabe30615..83216377d 100644 --- a/sync-committee-prover/Cargo.toml +++ b/sync-committee-prover/Cargo.toml @@ -18,6 +18,7 @@ actix-rt = "*" tokio = { version = "1.18.2", features = ["full"]} tokio-stream = { version = "0.1.8" } async-stream = { version = "0.3.3"} +base2 = {version="0.2.2", default-features=false} [dev-dependencies] hex = "0.4.3" diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 259150f4e..89e1e6b40 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -286,7 +286,7 @@ fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result>(); + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), + execution_payload.block_number.hash_tree_root().unwrap(), + ], + &multi_proof_nodes, + &[ + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + ], + ); + + println!("execution_payload_root {:?}", execution_payload_root); + + let execution_payload_hash_tree_root = finalized_block + .body + .execution_payload + .clone() + .hash_tree_root() + .unwrap(); + + println!( + "execution_payload_hash_tree_root {:?}", + execution_payload_hash_tree_root + ); + + assert_eq!(execution_payload_root, execution_payload_hash_tree_root); + + let execution_payload_branch = execution_payload + .execution_payload_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + EXECUTION_PAYLOAD_INDEX as usize, + &Node::from_bytes( + finalized_header + .clone() + .body_root + .as_ref() + .try_into() + .unwrap(), + ), + ); + + assert_eq!(is_merkle_branch_valid, true); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_finality_proof() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let block_id = "finalized"; + let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); + + let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + + let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); + + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_header_slot.to_string().as_str()) + .await + .unwrap(); + + let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); + + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + // purposely for waiting + //println!("sleeping"); + thread::sleep(time::Duration::from_secs(5)); + + let mut attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + + while attested_header.is_err() { + println!("I am running till i am ok. lol {}", &block_id); + attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + } + + let attested_header = attested_header.unwrap(); + + let finality_branch_proof = finality_branch_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); + + // verify the associated execution header of the finalized beacon header. + + // Verify that the `finality_branch` confirms `finalized_header` + // to match the finalized checkpoint root saved in the state of `attested_header`. + // Note that the genesis finalized checkpoint root is represented as a zero hash. + let finalized_root = &Node::from_bytes( + light_client_primitives::util::hash_tree_root(finalized_header.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ); + + let branch = finality_branch_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + println!("finalized_root {:?}", finalized_root.clone()); + + let finality_hash_tree_root = finalized_block.clone().hash_tree_root().unwrap(); + + assert_eq!(finalized_root, &finality_hash_tree_root); + + println!("finalized_root {:?}", finality_hash_tree_root); + let is_merkle_branch_valid = is_valid_merkle_branch( + finalized_root, + branch.iter(), + FINALIZED_ROOT_INDEX.floor_log2() as usize, + get_subtree_index(FINALIZED_ROOT_INDEX) as usize, + &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), + ); + + println!( + "is_merkle_branch_valid {:?}", + is_merkle_branch_valid.clone() + ); + + assert!(is_merkle_branch_valid, "{}", true); +} + // use tokio interval(should run every 13 minutes) // every 13 minutes, fetch latest finalized block // then prove the execution payload From 2c3b21de9d6b5b7d64edaf32cdfafc98eb3398c9 Mon Sep 17 00:00:00 2001 From: Damilare Date: Fri, 10 Feb 2023 18:34:03 +0100 Subject: [PATCH 048/182] sync committee proof test --- light-client-verifier/src/light_client.rs | 8 +- sync-committee-prover/src/lib.rs | 10 +- sync-committee-prover/src/test.rs | 143 +++++++++++++++++++++- 3 files changed, 152 insertions(+), 9 deletions(-) diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs index da0ec01d7..35a022299 100644 --- a/light-client-verifier/src/light_client.rs +++ b/light-client-verifier/src/light_client.rs @@ -185,7 +185,7 @@ impl EthLightClient { finalized_root, branch.iter(), FINALIZED_ROOT_INDEX.floor_log2() as usize, - get_subtree_index(FINALIZED_ROOT_INDEX) as usize, + FINALIZED_ROOT_INDEX as usize, &Node::from_bytes( update .attested_header @@ -243,7 +243,7 @@ impl EthLightClient { &execution_payload_root, execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - get_subtree_index(EXECUTION_PAYLOAD_INDEX) as usize, + EXECUTION_PAYLOAD_INDEX as usize, &Node::from_bytes( update .finalized_header @@ -342,7 +342,7 @@ impl EthLightClient { &block_roots_root, block_roots_branch_node.iter(), BLOCK_ROOTS_INDEX.floor_log2() as usize, - get_subtree_index(BLOCK_ROOTS_INDEX) as usize, + BLOCK_ROOTS_INDEX as usize, &Node::from_bytes( update .finalized_header @@ -465,7 +465,7 @@ impl EthLightClient { &execution_payload_root, execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - get_subtree_index(EXECUTION_PAYLOAD_INDEX) as usize, + EXECUTION_PAYLOAD_INDEX as usize, &Node::from_bytes( ancestor .header diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs index 89e1e6b40..add7b1cda 100644 --- a/sync-committee-prover/src/lib.rs +++ b/sync-committee-prover/src/lib.rs @@ -32,7 +32,9 @@ use light_client_primitives::types::{ NEXT_SYNC_COMMITTEE_INDEX, }; use light_client_primitives::util::get_subtree_index; -use ssz_rs::{Bitlist, List, Node, Vector}; +use ssz_rs::{ + get_generalized_index, Bitlist, GeneralizedIndex, List, Node, SszVariableOrIndex, Vector, +}; type BeaconBlockType = BeaconBlock< MAX_PROPOSER_SLASHINGS, @@ -286,7 +288,7 @@ fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result anyhow::Result anyhow::Result> { - let indices = vec![get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize]; + let indices = vec![NEXT_SYNC_COMMITTEE_INDEX as usize]; let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; Ok(proof) } fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { - let indices = vec![get_subtree_index(FINALIZED_ROOT_INDEX) as usize]; + let indices = [FINALIZED_ROOT_INDEX as usize]; //vec![get_subtree_index(FINALIZED_ROOT_INDEX) as usize]; let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; Ok(proof) diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 7e57be24d..28150e157 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -165,7 +165,7 @@ async fn test_execution_payload_proof() { &execution_payload_root, execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - EXECUTION_PAYLOAD_INDEX as usize, + GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0, &Node::from_bytes( finalized_header .clone() @@ -269,6 +269,147 @@ async fn test_finality_proof() { assert!(is_merkle_branch_valid, "{}", true); } +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_sync_committee_proof() { + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); + + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let finality_checkpoint = sync_committee_prover + .fetch_finalized_checkpoint() + .await + .unwrap(); + dbg!(&finality_checkpoint.root); + + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + dbg!(&block_id); + + let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + let state = sync_committee_prover + .fetch_beacon_state(&block_header.slot.to_string()) + .await + .unwrap(); + + let mut client_state = LightClientState { + finalized_header: block_header.clone(), + current_sync_committee: state.current_sync_committee, + next_sync_committee: state.next_sync_committee, + }; + + while let Some(_ts) = stream.next().await { + let block_id = "finalized"; + let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); + + let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + + let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); + + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_header_slot.to_string().as_str()) + .await + .unwrap(); + + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + // purposely for waiting + //println!("sleeping"); + thread::sleep(time::Duration::from_secs(5)); + + let mut attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + + while attested_header.is_err() { + println!("I am running till i am ok. lol {}", &block_id); + attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + } + + let attested_header = attested_header.unwrap(); + + let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); + + let sync_committee_update = if state_period == attested_header_slot { + println!("sync committee present"); + let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); + + let sync_committee_proof = sync_committee_proof + .into_iter() + .map(|node| { + Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") + }) + .collect::>(); + + let sync_committee = sync_committee_prover + .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .await + .unwrap(); + + Some(SyncCommitteeUpdate { + next_sync_committee: sync_committee, + next_sync_committee_branch: sync_committee_proof, + }) + } else { + println!("No sync committee present"); + None + }; + + if let Some(sync_committee_update) = sync_committee_update.clone() { + if update_attested_period == state_period + && sync_committee_update.next_sync_committee + != client_state.next_sync_committee.clone() + { + println!("invalid update for sync committee update"); + //rr(Error::InvalidUpdate)? + } + + let next_sync_committee_branch = sync_committee_update + .next_sync_committee_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &Node::from_bytes( + light_client_primitives::util::hash_tree_root( + sync_committee_update.next_sync_committee, + ) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, + &Node::from_bytes(attested_header.state_root.as_ref().try_into().unwrap()), + ); + + println!( + "valid merkle branch for sync committee {}", + is_merkle_branch_valid + ); + if !is_merkle_branch_valid { + println!("invalid merkle branch for sync committee"); + // Err(Error::InvalidMerkleBranch)?; + } + } + } +} + // use tokio interval(should run every 13 minutes) // every 13 minutes, fetch latest finalized block // then prove the execution payload From bcce5f76717e60d9caec7e1b89f87e7ce9a5749c Mon Sep 17 00:00:00 2001 From: Damilare Date: Mon, 13 Feb 2023 22:29:16 +0100 Subject: [PATCH 049/182] sync committee proof test --- sync-committee-prover/src/test.rs | 165 +++++++++++++++--------------- 1 file changed, 81 insertions(+), 84 deletions(-) diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index 28150e157..cee965395 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -305,107 +305,104 @@ async fn test_sync_committee_proof() { next_sync_committee: state.next_sync_committee, }; - while let Some(_ts) = stream.next().await { - let block_id = "finalized"; - let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); + let block_id = "finalized"; + let mut finalized_block = sync_committee_prover.fetch_block(block_id).await; - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + while finalized_block.is_err() { + println!("I am running finalized block till i am ok. lol {}", &block_id); + finalized_block = sync_committee_prover.fetch_block(block_id).await; + } - let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) - .await - .unwrap(); + let finalized_block = finalized_block.unwrap(); - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_header_slot.to_string().as_str()) - .await - .unwrap(); + let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); - // purposely for waiting - //println!("sleeping"); - thread::sleep(time::Duration::from_secs(5)); + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_header_slot.to_string().as_str()) + .await + .unwrap(); - let mut attested_header = sync_committee_prover + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + // purposely for waiting + //println!("sleeping"); + thread::sleep(time::Duration::from_secs(5)); + + let mut attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + + while attested_header.is_err() { + println!("I am running till i am ok. lol {}", &block_id); + attested_header = sync_committee_prover .fetch_header(attested_header_slot.to_string().as_str()) .await; + } - while attested_header.is_err() { - println!("I am running till i am ok. lol {}", &block_id); - attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - } + let attested_header = attested_header.unwrap(); - let attested_header = attested_header.unwrap(); + let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); - let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); + let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); - let sync_committee_update = if state_period == attested_header_slot { - println!("sync committee present"); - let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); + let sync_committee_proof = sync_committee_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); - let sync_committee_proof = sync_committee_proof - .into_iter() - .map(|node| { - Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") - }) - .collect::>(); + let mut sync_committee = sync_committee_prover + .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .await; - let sync_committee = sync_committee_prover - .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) - .await - .unwrap(); + while sync_committee.is_err() { + println!("I am fetching sync committee till i am ok. lol {}", &block_id); + sync_committee = sync_committee_prover + .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .await; + } - Some(SyncCommitteeUpdate { - next_sync_committee: sync_committee, - next_sync_committee_branch: sync_committee_proof, - }) - } else { - println!("No sync committee present"); - None - }; + let sync_committee = sync_committee.unwrap(); - if let Some(sync_committee_update) = sync_committee_update.clone() { - if update_attested_period == state_period - && sync_committee_update.next_sync_committee - != client_state.next_sync_committee.clone() - { - println!("invalid update for sync committee update"); - //rr(Error::InvalidUpdate)? - } - - let next_sync_committee_branch = sync_committee_update - .next_sync_committee_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes( - light_client_primitives::util::hash_tree_root( - sync_committee_update.next_sync_committee, - ) - .unwrap() - .as_ref() - .try_into() - .unwrap(), - ), - next_sync_committee_branch.iter(), - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, - NEXT_SYNC_COMMITTEE_INDEX as usize, - &Node::from_bytes(attested_header.state_root.as_ref().try_into().unwrap()), - ); + let sync_committee_update = Some(SyncCommitteeUpdate { + next_sync_committee: sync_committee, + next_sync_committee_branch: sync_committee_proof, + }); - println!( - "valid merkle branch for sync committee {}", - is_merkle_branch_valid - ); - if !is_merkle_branch_valid { - println!("invalid merkle branch for sync committee"); - // Err(Error::InvalidMerkleBranch)?; - } + if let Some(sync_committee_update) = sync_committee_update.clone() { + let next_sync_committee_branch = sync_committee_update + .next_sync_committee_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &Node::from_bytes( + light_client_primitives::util::hash_tree_root( + sync_committee_update.next_sync_committee, + ) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, + &Node::from_bytes(attested_header.state_root.as_ref().try_into().unwrap()), + ); + + println!( + "valid merkle branch for sync committee {}", + is_merkle_branch_valid + ); + if !is_merkle_branch_valid { + //println!("invalid merkle branch for sync committee"); + // Err(Error::InvalidMerkleBranch)?; } } } From 2678ce7e65d9ebb27e6a309d73b921e46c068ffe Mon Sep 17 00:00:00 2001 From: Damilare Date: Wed, 15 Feb 2023 23:03:00 +0100 Subject: [PATCH 050/182] test sync committee changes --- sync-committee-prover/src/test.rs | 136 ++++++++++++++---------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs index cee965395..7e408823f 100644 --- a/sync-committee-prover/src/test.rs +++ b/sync-committee-prover/src/test.rs @@ -299,17 +299,14 @@ async fn test_sync_committee_proof() { .await .unwrap(); - let mut client_state = LightClientState { - finalized_header: block_header.clone(), - current_sync_committee: state.current_sync_committee, - next_sync_committee: state.next_sync_committee, - }; - - let block_id = "finalized"; + let block_id = "head"; let mut finalized_block = sync_committee_prover.fetch_block(block_id).await; while finalized_block.is_err() { - println!("I am running finalized block till i am ok. lol {}", &block_id); + println!( + "I am running finalized block till i am ok. lol {}", + &block_id + ); finalized_block = sync_committee_prover.fetch_block(block_id).await; } @@ -317,94 +314,89 @@ async fn test_sync_committee_proof() { let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); - let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); let finalized_state = sync_committee_prover .fetch_beacon_state(finalized_block.slot.to_string().as_str()) .await .unwrap(); - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_header_slot.to_string().as_str()) - .await - .unwrap(); - - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - // purposely for waiting - //println!("sleeping"); - thread::sleep(time::Duration::from_secs(5)); - - let mut attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - - while attested_header.is_err() { - println!("I am running till i am ok. lol {}", &block_id); - attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - } - - let attested_header = attested_header.unwrap(); - - let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); - - let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); + let sync_committee_proof = prove_sync_committee_update(finalized_state.clone()).unwrap(); let sync_committee_proof = sync_committee_proof .into_iter() .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect::>(); - let mut sync_committee = sync_committee_prover - .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) .await; while sync_committee.is_err() { - println!("I am fetching sync committee till i am ok. lol {}", &block_id); + println!( + "I am fetching sync committee till i am ok. lol {}", + &block_id + ); sync_committee = sync_committee_prover - .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) .await; } let sync_committee = sync_committee.unwrap(); - let sync_committee_update = Some(SyncCommitteeUpdate { - next_sync_committee: sync_committee, - next_sync_committee_branch: sync_committee_proof, - }); - - if let Some(sync_committee_update) = sync_committee_update.clone() { - let next_sync_committee_branch = sync_committee_update - .next_sync_committee_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes( + let calculated_finalized_root = calculate_multi_merkle_root( + &[ + Node::from_bytes( light_client_primitives::util::hash_tree_root( - sync_committee_update.next_sync_committee, + sync_committee.clone(), ) - .unwrap() - .as_ref() - .try_into() - .unwrap(), + .unwrap() + .as_ref() + .try_into() + .unwrap(), ), - next_sync_committee_branch.iter(), - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, - NEXT_SYNC_COMMITTEE_INDEX as usize, - &Node::from_bytes(attested_header.state_root.as_ref().try_into().unwrap()), - ); + ], + &sync_committee_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(), + &[ + GeneralizedIndex(NEXT_SYNC_COMMITTEE_INDEX as usize), + ], + ); - println!( - "valid merkle branch for sync committee {}", - is_merkle_branch_valid - ); - if !is_merkle_branch_valid { - //println!("invalid merkle branch for sync committee"); - // Err(Error::InvalidMerkleBranch)?; - } - } + let hash_tree_root = finalized_state + .clone() + .hash_tree_root() + .unwrap(); + + println!("calculated_finalized_root {:?}", calculated_finalized_root); + println!("finalized_state_root {:?}", finalized_header.state_root); + println!("hash_tree_root {:?}", hash_tree_root); + + + let next_sync_committee_branch = sync_committee_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &Node::from_bytes( + light_client_primitives::util::hash_tree_root( + sync_committee.clone(), + ) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, + &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), + ); + + println!( + "valid merkle branch for sync committee {}", + is_merkle_branch_valid + ); + assert!(is_merkle_branch_valid, "{}", true); } // use tokio interval(should run every 13 minutes) From 63a9c7a09ad06e525677424571b6bf2ead0c7161 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Wed, 15 Feb 2023 19:21:46 -0400 Subject: [PATCH 051/182] clean up code --- Cargo.lock | 79 +-- Cargo.toml | 6 +- light-client-primitives/src/types.rs | 125 ---- light-client-verifier/src/error.rs | 34 -- light-client-verifier/src/lib.rs | 7 - light-client-verifier/src/light_client.rs | 500 ---------------- .../Cargo.toml | 5 +- .../src/lib.rs | 0 primitives/src/types.rs | 125 ++++ .../src/util.rs | 35 +- {sync-committee-prover => prover}/Cargo.toml | 4 +- prover/src/error.rs | 5 + prover/src/lib.rs | 346 +++++++++++ .../responses/beacon_block_header_response.rs | 12 +- prover/src/responses/beacon_block_response.rs | 40 ++ prover/src/responses/beacon_state_response.rs | 26 + .../responses/finality_checkpoint_response.rs | 10 +- .../src/responses/mod.rs | 2 + .../src/responses/sync_committee_response.rs | 8 +- .../src/responses/validator_response.rs | 12 +- prover/src/routes.rs | 21 + prover/src/test.rs | 508 ++++++++++++++++ rustfmt.toml | 23 + sync-committee-prover/src/error.rs | 5 - sync-committee-prover/src/lib.rs | 344 ----------- .../src/responses/beacon_block_response.rs | 36 -- .../src/responses/beacon_state_response.rs | 26 - sync-committee-prover/src/routes.rs | 21 - sync-committee-prover/src/test.rs | 560 ------------------ .../Cargo.toml | 10 +- verifier/src/error.rs | 34 ++ verifier/src/lib.rs | 461 ++++++++++++++ 32 files changed, 1684 insertions(+), 1746 deletions(-) delete mode 100644 light-client-primitives/src/types.rs delete mode 100644 light-client-verifier/src/error.rs delete mode 100644 light-client-verifier/src/lib.rs delete mode 100644 light-client-verifier/src/light_client.rs rename {light-client-primitives => primitives}/Cargo.toml (69%) rename {light-client-primitives => primitives}/src/lib.rs (100%) create mode 100644 primitives/src/types.rs rename {light-client-primitives => primitives}/src/util.rs (52%) rename {sync-committee-prover => prover}/Cargo.toml (82%) create mode 100644 prover/src/error.rs create mode 100644 prover/src/lib.rs rename {sync-committee-prover => prover}/src/responses/beacon_block_header_response.rs (67%) create mode 100644 prover/src/responses/beacon_block_response.rs create mode 100644 prover/src/responses/beacon_state_response.rs rename {sync-committee-prover => prover}/src/responses/finality_checkpoint_response.rs (63%) rename {sync-committee-prover => prover}/src/responses/mod.rs (85%) rename {sync-committee-prover => prover}/src/responses/sync_committee_response.rs (60%) rename {sync-committee-prover => prover}/src/responses/validator_response.rs (60%) create mode 100644 prover/src/routes.rs create mode 100644 prover/src/test.rs create mode 100644 rustfmt.toml delete mode 100644 sync-committee-prover/src/error.rs delete mode 100644 sync-committee-prover/src/lib.rs delete mode 100644 sync-committee-prover/src/responses/beacon_block_response.rs delete mode 100644 sync-committee-prover/src/responses/beacon_state_response.rs delete mode 100644 sync-committee-prover/src/routes.rs delete mode 100644 sync-committee-prover/src/test.rs rename {light-client-verifier => verifier}/Cargo.toml (59%) create mode 100644 verifier/src/error.rs create mode 100644 verifier/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b51b461e9..4980c81fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" dependencies = [ - "int", + "int 0.2.11", +] + +[[package]] +name = "base2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" +dependencies = [ + "int 0.3.0", ] [[package]] @@ -689,6 +698,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "int" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" +dependencies = [ + "num-traits", +] + [[package]] name = "integer-sqrt" version = "0.1.5" @@ -761,37 +779,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libc-print" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a574491aebd99996f31b32469bbd3b50e580c35f6305663d68f6d6b28eaced09" -dependencies = [ - "libc", -] - -[[package]] -name = "light-client-primitives" -version = "0.1.0" -dependencies = [ - "base2", - "ethereum-consensus", - "hex-literal", - "ssz-rs", -] - -[[package]] -name = "light-client-verifier" -version = "0.1.0" -dependencies = [ - "base2", - "ethereum-consensus", - "libc-print", - "light-client-primitives", - "milagro_bls", - "ssz-rs", -] - [[package]] name = "lock_api" version = "0.4.9" @@ -1476,6 +1463,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync-committee-primitives" +version = "0.1.0" +dependencies = [ + "base2 0.3.1", + "ethereum-consensus", + "hex-literal", + "ssz-rs", +] + [[package]] name = "sync-committee-prover" version = "0.1.0" @@ -1483,19 +1480,31 @@ dependencies = [ "actix-rt", "anyhow", "async-stream", - "base2", + "base2 0.2.2", "ethereum-consensus", "hex", - "light-client-primitives", - "light-client-verifier", "reqwest", "serde", "serde_json", "ssz-rs", + "sync-committee-primitives", + "sync-committee-verifier", "tokio", "tokio-stream", ] +[[package]] +name = "sync-committee-verifier" +version = "0.1.0" +dependencies = [ + "base2 0.2.2", + "ethereum-consensus", + "log", + "milagro_bls", + "ssz-rs", + "sync-committee-primitives", +] + [[package]] name = "synstructure" version = "0.12.6" diff --git a/Cargo.toml b/Cargo.toml index 82f538aa2..37dfd2d6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" members = [ - "light-client-verifier", - "light-client-primitives", - "sync-committee-prover" + "verifier", + "primitives", + "prover" ] diff --git a/light-client-primitives/src/types.rs b/light-client-primitives/src/types.rs deleted file mode 100644 index 69305e0ee..000000000 --- a/light-client-primitives/src/types.rs +++ /dev/null @@ -1,125 +0,0 @@ -use alloc::vec::Vec; -use ethereum_consensus::altair::{ - FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, -}; -use ethereum_consensus::bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}; -use ethereum_consensus::domains::DomainType; -use ethereum_consensus::primitives::{Hash32, Root, Slot}; - -pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; -pub const FINALIZED_ROOT_INDEX: u64 = 105; -pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; -pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; -pub const EXECUTION_PAYLOAD_INDEX: u64 = 25; -pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; -pub const BLOCK_ROOTS_INDEX: u64 = 37; -pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; -pub const HISTORICAL_ROOTS_INDEX: u64 = 39; -pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = - hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); - -/// This holds the relevant data required to prove the state root in the execution payload. -#[derive(Debug, Clone)] -pub struct ExecutionPayloadProof { - /// The state root in the `ExecutionPayload` which represents the commitment to - /// the ethereum world state in the yellow paper. - pub state_root: Hash32, - /// the block number of the execution header. - pub block_number: u64, - /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. - pub multi_proof: Vec, - /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. - pub execution_payload_branch: Vec, -} - -/// Holds the neccessary proofs required to verify a header in the `block_roots` field -/// either in [`BeaconState`] or [`HistoricalBatch`]. -#[derive(Debug, Clone)] -pub struct BlockRootsProof { - /// Generalized index of the header in the `block_roots` list. - pub block_header_index: u64, - /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` - pub block_header_branch: Vec, -} - -/// The block header ancestry proof, this is an enum because the header may either exist in -/// `state.block_roots` or `state.historical_roots`. -#[derive(Debug, Clone)] -pub enum AncestryProof { - /// This variant defines the proof data for a beacon chain header in the `state.block_roots` - BlockRoots { - /// Proof for the header in `state.block_roots` - block_roots_proof: BlockRootsProof, - /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] - block_roots_branch: Vec, - }, - /// This variant defines the neccessary proofs for a beacon chain header in the - /// `state.historical_roots`. - HistoricalRoots { - /// Proof for the header in `historical_batch.block_roots` - block_roots_proof: BlockRootsProof, - /// The proof for the `historical_batch.block_roots`, needed to reconstruct - /// `hash_tree_root(historical_batch)` - historical_batch_proof: Vec, - /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` - historical_roots_proof: Vec, - /// The generalized index for the historical_batch in `state.historical_roots`. - historical_roots_index: u64, - /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in - /// [`BeaconState`] - historical_roots_branch: Vec, - }, -} - -/// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized -/// header. -#[derive(Debug, Clone)] -pub struct AncestorBlock { - /// The actual beacon chain header - pub header: BeaconBlockHeader, - /// Associated execution header proofs - pub execution_payload: ExecutionPayloadProof, - /// Ancestry proofs of the beacon chain header. - pub ancestry_proof: AncestryProof, -} - -/// Holds the latest sync committee as well as an ssz proof for it's existence -/// in a finalized header. -#[derive(Debug, Clone)] -pub struct SyncCommitteeUpdate { - // actual sync committee - pub next_sync_committee: SyncCommittee, - // sync committee, ssz merkle proof. - pub next_sync_committee_branch: Vec, -} - -/// Minimum state required by the light client to validate new sync committee attestations -#[derive(Debug, Clone)] -pub struct LightClientState { - /// The latest recorded finalized header - pub finalized_header: BeaconBlockHeader, - // Sync committees corresponding to the finalized header - pub current_sync_committee: SyncCommittee, - pub next_sync_committee: SyncCommittee, -} - -/// Data required to advance the state of the light client. -#[derive(Debug, Clone)] -pub struct LightClientUpdate { - /// the header that the sync committee signed - pub attested_header: BeaconBlockHeader, - /// the sync committee has potentially changed, here's an ssz proof for that. - pub sync_committee_update: Option>, - /// the actual header which was finalized by the ethereum attestation protocol. - pub finalized_header: BeaconBlockHeader, - /// execution payload of the finalized header - pub execution_payload: ExecutionPayloadProof, - /// the ssz merkle proof for this header in the attested header, finalized headers lag by 2 epochs. - pub finality_branch: Vec, - /// signature & participation bits - pub sync_aggregate: SyncAggregate, - /// slot at which signature was produced - pub signature_slot: Slot, - /// ancestors of the finalized block to be verified, may be empty. - pub ancestor_blocks: Vec, -} diff --git a/light-client-verifier/src/error.rs b/light-client-verifier/src/error.rs deleted file mode 100644 index 6d703b41d..000000000 --- a/light-client-verifier/src/error.rs +++ /dev/null @@ -1,34 +0,0 @@ -use core::fmt::{Display, Formatter}; - -#[derive(Debug)] -pub enum Error { - SyncCommitteeParticipantsTooLow, - InvalidUpdate, - DomainError, - FastAggregateError(ethereum_consensus::crypto::Error), - InvalidMerkleBranch, - InvalidRoot, - MerkleizationError, -} - -impl From for Error { - fn from(error: ethereum_consensus::crypto::Error) -> Self { - Error::FastAggregateError(error) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - Error::SyncCommitteeParticipantsTooLow => { - write!(f, "Sync committee participants are too low") - } - Error::InvalidUpdate => write!(f, "Invalid update"), - Error::DomainError => write!(f, "Couldn't get domain"), - Error::FastAggregateError(err) => write!(f, "Fast aggregate error"), - Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), - Error::InvalidRoot => write!(f, "Invalid root"), - Error::MerkleizationError => write!(f, "Merkleization error"), - } - } -} diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs deleted file mode 100644 index 3fc6cd500..000000000 --- a/light-client-verifier/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(not(feature = "std"))] -extern crate alloc; - -pub mod error; -pub mod light_client; diff --git a/light-client-verifier/src/light_client.rs b/light-client-verifier/src/light_client.rs deleted file mode 100644 index 35a022299..000000000 --- a/light-client-verifier/src/light_client.rs +++ /dev/null @@ -1,500 +0,0 @@ -use crate::error::Error; -use alloc::vec::Vec; -use base2::Base2; -use core::borrow::Borrow; -use core::fmt::{Display, Formatter}; -use ethereum_consensus::altair::mainnet::SYNC_COMMITTEE_SIZE; -use ethereum_consensus::altair::{ - FINALIZED_ROOT_INDEX_FLOOR_LOG_2, NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, -}; -use ethereum_consensus::bellatrix::compute_domain; -use ethereum_consensus::domains::DomainType; -use ethereum_consensus::primitives::Root; -use ethereum_consensus::signing::compute_signing_root; -use ethereum_consensus::state_transition::Context; -use libc_print::libc_println; -use light_client_primitives::types::{ - AncestryProof, BLOCK_ROOTS_INDEX, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, - EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, - GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, - NEXT_SYNC_COMMITTEE_INDEX, -}; -use light_client_primitives::util::{ - compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, - get_subtree_index, hash_tree_root, -}; -use ssz_rs::prelude::is_valid_merkle_branch; -use ssz_rs::Merkleized; -use ssz_rs::{calculate_merkle_root, calculate_multi_merkle_root, GeneralizedIndex, Node}; - -pub type LightClientState = light_client_primitives::types::LightClientState; -pub type LightClientUpdate = light_client_primitives::types::LightClientUpdate; - -pub struct EthLightClient; - -impl EthLightClient { - /// This function simply verifies a sync committee's attestation & it's finalized counterpart. - pub fn verify_sync_committee_attestation( - trusted_state: LightClientState, - update: LightClientUpdate, - ) -> Result { - if update.finality_branch.len() != FINALIZED_ROOT_INDEX_FLOOR_LOG_2 as usize - && update.sync_committee_update.is_some() - && update - .clone() - .sync_committee_update - .unwrap() - .next_sync_committee_branch - .len() - != NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize - { - libc_println!("Invalid update "); - libc_println!( - "update finality branch length {} ", - update.finality_branch.len() - ); - libc_println!( - "FINALIZED_ROOT_INDEX_FLOOR_LOG_2 {}", - FINALIZED_ROOT_INDEX_FLOOR_LOG_2 - ); - libc_println!( - "update next sync committee branch length {} ", - update - .clone() - .sync_committee_update - .unwrap() - .next_sync_committee_branch - .len() - ); - libc_println!( - "NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 {}", - NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 - ); - //Err(Error::InvalidUpdate)? - } - - // Verify sync committee has super majority participants - let sync_committee_bits = update.sync_aggregate.sync_committee_bits; - let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; - if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { - libc_println!("SyncCommitteeParticipantsTooLow "); - libc_println!("sync_aggregate_participants {} ", { - sync_aggregate_participants * 3 - }); - libc_println!("sync_committee_bits {}", { - sync_committee_bits.clone().len() * 2 - }); - //Err(Error::SyncCommitteeParticipantsTooLow)? - } - - // Verify update does not skip a sync committee period - let is_valid_update = update.signature_slot > update.attested_header.slot - && update.attested_header.slot >= update.finalized_header.slot; - if !is_valid_update { - libc_println!("is_valid_update {} ", is_valid_update); - libc_println!( - "update.signature_slot {} update.attested_header.slot {}", - update.signature_slot, - update.attested_header.slot - ); - libc_println!( - "update.attested_header.slot {} update.finalized_header.slot {}", - update.attested_header.slot, - update.finalized_header.slot - ); - //Err(Error::InvalidUpdate)? - } - - let state_period = - compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); - let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); - if !(state_period..=state_period + 1).contains(&update_signature_period) { - libc_println!("invalid update"); - libc_println!("state_period is {}", state_period); - libc_println!("update_signature_period is {}", update_signature_period); - //Err(Error::InvalidUpdate)? - } - - // Verify update is relevant - let update_attested_period = - compute_sync_committee_period_at_slot(update.attested_header.slot); - let update_has_next_sync_committee = - update.sync_committee_update.is_some() && update_attested_period == state_period; - - if !(update.attested_header.slot > trusted_state.finalized_header.slot - || update_has_next_sync_committee) - { - Err(Error::InvalidUpdate)? - } - - // Verify sync committee aggregate signature - let sync_committee = if update_signature_period == state_period { - trusted_state.current_sync_committee.clone() - } else { - trusted_state.next_sync_committee.clone() - }; - - let sync_committee_pubkeys = sync_committee.public_keys; - - let participant_pubkeys = sync_committee_bits - .iter() - .zip(sync_committee_pubkeys.iter()) - .filter_map(|(bit, key)| if *bit { Some(key) } else { None }) - .collect::>(); - - let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); - //TODO: we probably need to construct context - let domain = compute_domain( - DOMAIN_SYNC_COMMITTEE, - Some(fork_version), - Some(Root::from_bytes( - GENESIS_VALIDATORS_ROOT - .try_into() - .map_err(|_| Error::InvalidRoot)?, - )), - &Context::default(), - ) - .map_err(|_| Error::InvalidUpdate)?; - - let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain); - - ethereum_consensus::crypto::fast_aggregate_verify( - &*participant_pubkeys, - signing_root.map_err(|_| Error::InvalidRoot)?.as_bytes(), - &update.sync_aggregate.sync_committee_signature, - )?; - - // Verify that the `finality_branch` confirms `finalized_header` - // to match the finalized checkpoint root saved in the state of `attested_header`. - // Note that the genesis finalized checkpoint root is represented as a zero hash. - let finalized_root = &Node::from_bytes( - light_client_primitives::util::hash_tree_root(update.finalized_header.clone()) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ); - - let branch = update - .finality_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let is_merkle_branch_valid = is_valid_merkle_branch( - finalized_root, - branch.iter(), - FINALIZED_ROOT_INDEX.floor_log2() as usize, - FINALIZED_ROOT_INDEX as usize, - &Node::from_bytes( - update - .attested_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - - libc_println!( - "valid merkle branch for finalized_root {}", - is_merkle_branch_valid - ); - if !is_merkle_branch_valid { - libc_println!("invalid merkle branch for finalized root"); - //Err(Error::InvalidMerkleBranch)?; - } - - // verify the associated execution header of the finalized beacon header. - let mut execution_payload = update.execution_payload; - let multi_proof_vec = execution_payload.multi_proof; - let multi_proof_nodes = multi_proof_vec - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let execution_payload_root = calculate_multi_merkle_root( - &[ - Node::from_bytes( - execution_payload - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - execution_payload - .block_number - .hash_tree_root() - .map_err(|_| Error::InvalidRoot)?, - ], - &multi_proof_nodes, - &[ - GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), - GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), - ], - ); - - let execution_payload_branch = execution_payload - .execution_payload_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let is_merkle_branch_valid = is_valid_merkle_branch( - &execution_payload_root, - execution_payload_branch.iter(), - EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - EXECUTION_PAYLOAD_INDEX as usize, - &Node::from_bytes( - update - .finalized_header - .clone() - .body_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - - libc_println!("valid merkle branch for execution_payload_branch"); - if !is_merkle_branch_valid { - libc_println!("invalid merkle branch for execution_payload_branch"); - //Err(Error::InvalidMerkleBranch)?; - } - - if let Some(sync_committee_update) = update.sync_committee_update.clone() { - if update_attested_period == state_period - && sync_committee_update.next_sync_committee - != trusted_state.next_sync_committee.clone() - { - libc_println!("invalid update for sync committee update"); - //rr(Error::InvalidUpdate)? - } - - let next_sync_committee_branch = sync_committee_update - .next_sync_committee_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes( - light_client_primitives::util::hash_tree_root( - sync_committee_update.next_sync_committee, - ) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - next_sync_committee_branch.iter(), - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, - get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize, - &Node::from_bytes( - update - .attested_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - - libc_println!( - "valid merkle branch for sync committee {}", - is_merkle_branch_valid - ); - if !is_merkle_branch_valid { - libc_println!("invalid merkle branch for sync committee"); - // Err(Error::InvalidMerkleBranch)?; - } - } - - // verify the ancestry proofs - for ancestor in update.ancestor_blocks { - match ancestor.ancestry_proof { - AncestryProof::BlockRoots { - block_roots_proof, - block_roots_branch, - } => { - let block_header_branch = block_roots_proof - .block_header_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let block_roots_root = calculate_merkle_root( - &Node::from_bytes( - hash_tree_root(ancestor.header.clone()) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - &*block_header_branch, - &GeneralizedIndex(block_roots_proof.block_header_index as usize), - ); - - let block_roots_branch_node = block_roots_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let is_merkle_branch_valid = is_valid_merkle_branch( - &block_roots_root, - block_roots_branch_node.iter(), - BLOCK_ROOTS_INDEX.floor_log2() as usize, - BLOCK_ROOTS_INDEX as usize, - &Node::from_bytes( - update - .finalized_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; - } - } - AncestryProof::HistoricalRoots { - block_roots_proof, - historical_batch_proof, - historical_roots_proof, - historical_roots_index, - historical_roots_branch, - } => { - let block_header_branch = block_roots_proof - .block_header_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let block_roots_root = calculate_merkle_root( - &Node::from_bytes( - hash_tree_root(ancestor.header.clone()) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - &block_header_branch, - &GeneralizedIndex(block_roots_proof.block_header_index as usize), - ); - - let historical_batch_proof_nodes = historical_batch_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let historical_batch_root = calculate_merkle_root( - &block_roots_root, - &historical_batch_proof_nodes, - &GeneralizedIndex(HISTORICAL_BATCH_BLOCK_ROOTS_INDEX as usize), - ); - - let historical_roots_proof_nodes = historical_roots_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let historical_roots_root = calculate_merkle_root( - &historical_batch_root, - &historical_roots_proof_nodes, - &GeneralizedIndex(historical_roots_index as usize), - ); - - let historical_roots_branch_nodes = historical_roots_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &historical_roots_root, - historical_roots_branch_nodes.iter(), - HISTORICAL_ROOTS_INDEX.floor_log2() as usize, - get_subtree_index(HISTORICAL_ROOTS_INDEX) as usize, - &Node::from_bytes( - update - .finalized_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - - if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; - } - } - }; - - // verify the associated execution paylaod header. - let execution_payload = ancestor.execution_payload; - let multi_proof = execution_payload - .multi_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let execution_payload_root = calculate_multi_merkle_root( - &[ - Node::from_bytes( - execution_payload - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - Node::from_bytes( - hash_tree_root(execution_payload.block_number) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ], - &multi_proof, - &[ - GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), - GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), - ], - ); - - let execution_payload_branch = execution_payload - .execution_payload_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &execution_payload_root, - execution_payload_branch.iter(), - EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - EXECUTION_PAYLOAD_INDEX as usize, - &Node::from_bytes( - ancestor - .header - .body_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - - if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; - } - } - - let new_light_client_state = - if let Some(sync_committee_update) = update.sync_committee_update { - LightClientState { - finalized_header: update.finalized_header, - current_sync_committee: trusted_state.next_sync_committee, - next_sync_committee: sync_committee_update.next_sync_committee, - } - } else { - LightClientState { - finalized_header: update.finalized_header, - ..trusted_state - } - }; - - Ok(new_light_client_state) - } -} diff --git a/light-client-primitives/Cargo.toml b/primitives/Cargo.toml similarity index 69% rename from light-client-primitives/Cargo.toml rename to primitives/Cargo.toml index 4e83181c9..a9f5cdf96 100644 --- a/light-client-primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -1,13 +1,12 @@ [package] -name = "light-client-primitives" +name = "sync-committee-primitives" version = "0.1.0" edition = "2021" authors = ["Polytope Labs"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -base2 = {version="0.2.2", default-features=false} +base2 = { version = "0.3.1", default-features=false} ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/light-client-primitives/src/lib.rs b/primitives/src/lib.rs similarity index 100% rename from light-client-primitives/src/lib.rs rename to primitives/src/lib.rs diff --git a/primitives/src/types.rs b/primitives/src/types.rs new file mode 100644 index 000000000..dc115b6f8 --- /dev/null +++ b/primitives/src/types.rs @@ -0,0 +1,125 @@ +use alloc::vec::Vec; +use ethereum_consensus::{ + bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, + domains::DomainType, + primitives::{Hash32, Slot}, +}; + +pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; +pub const FINALIZED_ROOT_INDEX: u64 = 105; +pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; +pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; +pub const EXECUTION_PAYLOAD_INDEX: u64 = 25; +pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; +pub const BLOCK_ROOTS_INDEX: u64 = 37; +pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; +pub const HISTORICAL_ROOTS_INDEX: u64 = 39; +pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); + +/// This holds the relevant data required to prove the state root in the execution payload. +#[derive(Debug, Clone)] +pub struct ExecutionPayloadProof { + /// The state root in the `ExecutionPayload` which represents the commitment to + /// the ethereum world state in the yellow paper. + pub state_root: Hash32, + /// the block number of the execution header. + pub block_number: u64, + /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. + pub multi_proof: Vec, + /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. + pub execution_payload_branch: Vec, +} + +/// Holds the neccessary proofs required to verify a header in the `block_roots` field +/// either in [`BeaconState`] or [`HistoricalBatch`]. +#[derive(Debug, Clone)] +pub struct BlockRootsProof { + /// Generalized index of the header in the `block_roots` list. + pub block_header_index: u64, + /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` + pub block_header_branch: Vec, +} + +/// The block header ancestry proof, this is an enum because the header may either exist in +/// `state.block_roots` or `state.historical_roots`. +#[derive(Debug, Clone)] +pub enum AncestryProof { + /// This variant defines the proof data for a beacon chain header in the `state.block_roots` + BlockRoots { + /// Proof for the header in `state.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] + block_roots_branch: Vec, + }, + /// This variant defines the neccessary proofs for a beacon chain header in the + /// `state.historical_roots`. + HistoricalRoots { + /// Proof for the header in `historical_batch.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the `historical_batch.block_roots`, needed to reconstruct + /// `hash_tree_root(historical_batch)` + historical_batch_proof: Vec, + /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` + historical_roots_proof: Vec, + /// The generalized index for the historical_batch in `state.historical_roots`. + historical_roots_index: u64, + /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in + /// [`BeaconState`] + historical_roots_branch: Vec, + }, +} + +/// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized +/// header. +#[derive(Debug, Clone)] +pub struct AncestorBlock { + /// The actual beacon chain header + pub header: BeaconBlockHeader, + /// Associated execution header proofs + pub execution_payload: ExecutionPayloadProof, + /// Ancestry proofs of the beacon chain header. + pub ancestry_proof: AncestryProof, +} + +/// Holds the latest sync committee as well as an ssz proof for it's existence +/// in a finalized header. +#[derive(Debug, Clone)] +pub struct SyncCommitteeUpdate { + // actual sync committee + pub next_sync_committee: SyncCommittee, + // sync committee, ssz merkle proof. + pub next_sync_committee_branch: Vec, +} + +/// Minimum state required by the light client to validate new sync committee attestations +#[derive(Debug, Clone)] +pub struct LightClientState { + /// The latest recorded finalized header + pub finalized_header: BeaconBlockHeader, + // Sync committees corresponding to the finalized header + pub current_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, +} + +/// Data required to advance the state of the light client. +#[derive(Debug, Clone)] +pub struct LightClientUpdate { + /// the header that the sync committee signed + pub attested_header: BeaconBlockHeader, + /// the sync committee has potentially changed, here's an ssz proof for that. + pub sync_committee_update: Option>, + /// the actual header which was finalized by the ethereum attestation protocol. + pub finalized_header: BeaconBlockHeader, + /// execution payload of the finalized header + pub execution_payload: ExecutionPayloadProof, + /// the ssz merkle proof for this header in the attested header, finalized headers lag by 2 + /// epochs. + pub finality_branch: Vec, + /// signature & participation bits + pub sync_aggregate: SyncAggregate, + /// slot at which signature was produced + pub signature_slot: Slot, + /// ancestors of the finalized block to be verified, may be empty. + pub ancestor_blocks: Vec, +} diff --git a/light-client-primitives/src/util.rs b/primitives/src/util.rs similarity index 52% rename from light-client-primitives/src/util.rs rename to primitives/src/util.rs index 6685bf4f5..ab58d83be 100644 --- a/light-client-primitives/src/util.rs +++ b/primitives/src/util.rs @@ -1,46 +1,45 @@ use base2::Base2; -use ethereum_consensus::altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD; -use ethereum_consensus::configs::mainnet::{ - ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION, +use ethereum_consensus::{ + altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD, + configs::mainnet::{ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION}, + phase0::mainnet::SLOTS_PER_EPOCH, }; -use ethereum_consensus::phase0::mainnet::SLOTS_PER_EPOCH; -use ethereum_consensus::primitives::{Hash32, Root}; -use ssz_rs::{MerkleizationError, Node}; +use ssz_rs::Node; /// Calculate the subtree index from the ``generalized_index`` pub fn get_subtree_index(generalized_index: u64) -> u64 { - generalized_index % 2 ^ (generalized_index.floor_log2() as u64) + generalized_index % 2 ^ (generalized_index.floor_log2() as u64) } /// Return the sync committe period at the given ``epoch`` pub fn compute_sync_committee_period(epoch: u64) -> u64 { - epoch / EPOCHS_PER_SYNC_COMMITTEE_PERIOD + epoch / EPOCHS_PER_SYNC_COMMITTEE_PERIOD } /// Return the epoch number at ``slot``. pub fn compute_epoch_at_slot(slot: u64) -> u64 { - slot / SLOTS_PER_EPOCH + slot / SLOTS_PER_EPOCH } /// Return the fork version at the given ``epoch``. pub fn compute_fork_version(epoch: u64) -> [u8; 4] { - if epoch >= ALTAIR_FORK_EPOCH { - ALTAIR_FORK_VERSION - } else { - GENESIS_FORK_VERSION - } + if epoch >= ALTAIR_FORK_EPOCH { + ALTAIR_FORK_VERSION + } else { + GENESIS_FORK_VERSION + } } /// Return the sync committee period at ``slot`` pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { - compute_sync_committee_period(compute_epoch_at_slot(slot)) + compute_sync_committee_period(compute_epoch_at_slot(slot)) } /// method for hashing objects into a single root by utilizing a hash tree structure, as defined in /// the SSZ spec. pub fn hash_tree_root( - mut object: T, + mut object: T, ) -> Result { - let root = object.hash_tree_root()?; - Ok(root) + let root = object.hash_tree_root()?; + Ok(root) } diff --git a/sync-committee-prover/Cargo.toml b/prover/Cargo.toml similarity index 82% rename from sync-committee-prover/Cargo.toml rename to prover/Cargo.toml index 83216377d..513a81cca 100644 --- a/sync-committee-prover/Cargo.toml +++ b/prover/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -light-client-primitives = {path="../light-client-primitives", default-features = false } -light-client-verifier = {path="../light-client-verifier", default-features = false } +sync-committee-primitives = { path= "../primitives", default-features = false } +sync-committee-verifier = { path= "../verifier", default-features = false } ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} diff --git a/prover/src/error.rs b/prover/src/error.rs new file mode 100644 index 000000000..e6a6a5c49 --- /dev/null +++ b/prover/src/error.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub enum Error { + AggregateSignatureError, + EmptySignedBeaconBlock, +} diff --git a/prover/src/lib.rs b/prover/src/lib.rs new file mode 100644 index 000000000..219439c79 --- /dev/null +++ b/prover/src/lib.rs @@ -0,0 +1,346 @@ +mod error; +mod responses; +mod routes; +#[cfg(test)] +mod test; + +// todo: split up this file + +use ethereum_consensus::{ + altair::Validator, + bellatrix::{ + BeaconBlock, BeaconBlockHeader, BeaconState, Checkpoint, SignedBeaconBlock, SyncCommittee, + }, +}; +use reqwest::Client; + +use crate::{responses::sync_committee_response::NodeSyncCommittee, routes::*}; +use ethereum_consensus::{ + bellatrix::mainnet::{ + BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, + MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, + }, + crypto::eth_aggregate_public_keys, + phase0::mainnet::{ + EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, + HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, + MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, + SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, + }, + primitives::{Bytes32, Slot, ValidatorIndex}, +}; +use light_client_primitives::{ + types::{ + BlockRootsProof, ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, + NEXT_SYNC_COMMITTEE_INDEX, + }, + util::get_subtree_index, +}; +use ssz_rs::{GeneralizedIndex, List, Node, Vector}; +use sync_committee_primitives::{ + types::{ + BlockRootsProof, ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, + EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, + NEXT_SYNC_COMMITTEE_INDEX, + }, + util::get_subtree_index, +}; + +type BeaconBlockType = BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + +type SignedBeaconBlockType = SignedBeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + +pub type BeaconStateType = BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + +pub struct SyncCommitteeProver { + pub node_url: String, + pub client: Client, +} + +impl SyncCommitteeProver { + pub fn new(node_url: String) -> Self { + let client = Client::new(); + + SyncCommitteeProver { node_url, client } + } + + pub async fn fetch_finalized_checkpoint(&self) -> Result { + let full_url = self.generate_route(&finality_checkpoints("head")); + let response = self.client.get(full_url).send().await?; + + let response_data = + response.json::().await?; + Ok(response_data.data.finalized) + } + + pub async fn fetch_header(&self, block_id: &str) -> Result { + let path = header_route(block_id); + let full_url = self.generate_route(&path); + let response = self.client.get(full_url).send().await?; + let status = response.status().as_u16(); + + let response_data = + response.json::().await?; + + let beacon_block_header = response_data.data.header.message; + + Ok(beacon_block_header) + } + + pub async fn fetch_block( + &self, + block_id: &str, + ) -> Result< + BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + reqwest::Error, + > { + let path = block_route(block_id); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let beacon_block = response_data.data.message; + + Ok(beacon_block) + } + + pub async fn fetch_sync_committee( + &self, + state_id: &str, + ) -> Result { + let path = sync_committee_route(state_id); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let sync_committee = response_data.data; + + Ok(sync_committee) + } + + pub async fn fetch_validator( + &self, + state_id: &str, + validator_index: &str, + ) -> Result { + let path = validator_route(state_id, validator_index); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let validator = response_data.data.validator; + + Ok(validator) + } + + pub async fn fetch_beacon_state( + &self, + state_id: &str, + ) -> Result { + let path = beacon_state_route(state_id); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let beacon_state = response_data.data; + + Ok(beacon_state) + } + + pub async fn fetch_processed_sync_committee( + &self, + state_id: &str, + ) -> Result, reqwest::Error> { + // fetches sync committee from Node + let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; + + let mut validators: List = Default::default(); + for mut validator_index in node_sync_committee.validators.clone() { + // fetches validator based on validator index + let validator = self.fetch_validator(state_id.clone(), &validator_index).await?; + validators.push(validator); + } + + let public_keys_vector = node_sync_committee + .validators + .into_iter() + .map(|i| { + let validator_index: ValidatorIndex = i.parse().unwrap(); + validators[validator_index].public_key.clone() + }) + .collect::>(); + + let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector).unwrap(); + + let sync_committee = SyncCommittee:: { + public_keys: public_keys_vector, + aggregate_public_key, + }; + + Ok(sync_committee) + } + + async fn fetch_latest_finalized_block( + &self, + ) -> Result<(BeaconBlockHeader, BeaconBlockType), reqwest::Error> { + let block_header = self.fetch_header("finalized").await?; + let block = self.fetch_block("finalized").await?; + + Ok((block_header, block)) + } + + fn generate_route(&self, path: &str) -> String { + format!("{}{}", self.node_url.clone(), path) + } +} + +fn get_attestation_slots_for_finalized_header( + finalized_header: &BeaconBlockHeader, + slots_per_epoch: u64, +) -> Slot { + let finalized_header_slot = finalized_header.slot; + + // given that an epoch is 32 slots and blocks are finalized every 2 epochs + // so the attested slot for a finalized block is 64 slots away + let attested_slot = finalized_header_slot + (slots_per_epoch * 2); + + attested_slot +} + +fn prove_beacon_state_values( + data: BeaconStateType, + indices: &[usize], +) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(data, indices)?; + Ok(proof) +} + +fn prove_beacon_block_values( + data: BeaconBlockType, + indices: &[usize], +) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(data, indices)?; + Ok(proof) +} + +fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result { + let indices = [ + EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, + ]; + // generate multi proofs + let multi_proof = + ssz_rs::generate_proof(block.body.execution_payload.clone(), indices.as_slice())?; + + let execution_payload_index = [GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0]; + let execution_payload_branch = + ssz_rs::generate_proof(block.body.clone(), execution_payload_index.as_slice())?; + + Ok(ExecutionPayloadProof { + state_root: block.body.execution_payload.state_root, + block_number: block.body.execution_payload.block_number, + multi_proof: multi_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect(), + execution_payload_branch: execution_payload_branch + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect(), + }) +} + +fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result> { + let indices = vec![NEXT_SYNC_COMMITTEE_INDEX as usize]; + let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; + + Ok(proof) +} + +fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { + let indices = [FINALIZED_ROOT_INDEX as usize]; //vec![get_subtree_index(FINALIZED_ROOT_INDEX) as usize]; + let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; + + Ok(proof) +} + +// function that generates block roots proof +// block number and convert to get_subtree_index +// beacon state has a block roots vec, pass the block root to generate proof +fn prove_block_roots_proof( + state: BeaconStateType, + block_number: u64, +) -> anyhow::Result { + let indices = vec![get_subtree_index(block_number) as usize]; + let proof = ssz_rs::generate_proof(state.block_roots, indices.as_slice())?; + + Ok(BlockRootsProof { + block_header_index: block_number, + block_header_branch: proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect(), + }) +} + +// prove the block roots vec inside the beacon state which will be the block roots branch + +// when aggr sigs, create a new bit list diff --git a/sync-committee-prover/src/responses/beacon_block_header_response.rs b/prover/src/responses/beacon_block_header_response.rs similarity index 67% rename from sync-committee-prover/src/responses/beacon_block_header_response.rs rename to prover/src/responses/beacon_block_header_response.rs index 4ac4748ed..3d6d3a533 100644 --- a/sync-committee-prover/src/responses/beacon_block_header_response.rs +++ b/prover/src/responses/beacon_block_header_response.rs @@ -2,18 +2,18 @@ use ethereum_consensus::bellatrix::BeaconBlockHeader; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { - pub(crate) data: ResponseData, - execution_optimistic: bool, + pub(crate) data: ResponseData, + execution_optimistic: bool, } #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ResponseData { - root: String, - canonical: bool, - pub(crate) header: ResponseDataBeaconBlockHeaderMessage, + root: String, + canonical: bool, + pub(crate) header: ResponseDataBeaconBlockHeaderMessage, } #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ResponseDataBeaconBlockHeaderMessage { - pub message: BeaconBlockHeader, + pub message: BeaconBlockHeader, } diff --git a/prover/src/responses/beacon_block_response.rs b/prover/src/responses/beacon_block_response.rs new file mode 100644 index 000000000..d90317c7a --- /dev/null +++ b/prover/src/responses/beacon_block_response.rs @@ -0,0 +1,40 @@ +use ethereum_consensus::{ + bellatrix::{ + mainnet::{ + BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, + MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, + }, + BeaconBlock, BeaconBlockHeader, + }, + phase0::mainnet::{ + EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, + HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, + MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, + SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, + }, +}; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: ResponseData, + version: String, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseData { + pub(crate) message: BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, + pub signature: String, +} diff --git a/prover/src/responses/beacon_state_response.rs b/prover/src/responses/beacon_state_response.rs new file mode 100644 index 000000000..14b0b91ee --- /dev/null +++ b/prover/src/responses/beacon_state_response.rs @@ -0,0 +1,26 @@ +use ethereum_consensus::bellatrix::BeaconState; + +use ethereum_consensus::bellatrix::mainnet::{ + BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, + ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, + MAX_TRANSACTIONS_PER_PAYLOAD, MAX_VALIDATORS_PER_COMMITTEE, SLOTS_PER_HISTORICAL_ROOT, + SYNC_COMMITTEE_SIZE, VALIDATOR_REGISTRY_LIMIT, +}; +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + version: String, + pub(crate) data: BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, +} diff --git a/sync-committee-prover/src/responses/finality_checkpoint_response.rs b/prover/src/responses/finality_checkpoint_response.rs similarity index 63% rename from sync-committee-prover/src/responses/finality_checkpoint_response.rs rename to prover/src/responses/finality_checkpoint_response.rs index f9e9a20eb..17c276be7 100644 --- a/sync-committee-prover/src/responses/finality_checkpoint_response.rs +++ b/prover/src/responses/finality_checkpoint_response.rs @@ -2,13 +2,13 @@ use ethereum_consensus::bellatrix::Checkpoint; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub(crate) struct Response { - execution_optimistic: bool, - pub data: ResponseData, + execution_optimistic: bool, + pub data: ResponseData, } #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ResponseData { - previous_justified: Checkpoint, - current_justified: Checkpoint, - pub finalized: Checkpoint, + previous_justified: Checkpoint, + current_justified: Checkpoint, + pub finalized: Checkpoint, } diff --git a/sync-committee-prover/src/responses/mod.rs b/prover/src/responses/mod.rs similarity index 85% rename from sync-committee-prover/src/responses/mod.rs rename to prover/src/responses/mod.rs index 1d63bda98..bb7714c6f 100644 --- a/sync-committee-prover/src/responses/mod.rs +++ b/prover/src/responses/mod.rs @@ -4,3 +4,5 @@ pub mod beacon_state_response; pub mod finality_checkpoint_response; pub mod sync_committee_response; pub mod validator_response; + +// todo: collapse into one file. diff --git a/sync-committee-prover/src/responses/sync_committee_response.rs b/prover/src/responses/sync_committee_response.rs similarity index 60% rename from sync-committee-prover/src/responses/sync_committee_response.rs rename to prover/src/responses/sync_committee_response.rs index 80bbcd7d8..7e0637eb5 100644 --- a/sync-committee-prover/src/responses/sync_committee_response.rs +++ b/prover/src/responses/sync_committee_response.rs @@ -1,11 +1,11 @@ #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { - pub(crate) data: NodeSyncCommittee, - execution_optimistic: bool, + pub(crate) data: NodeSyncCommittee, + execution_optimistic: bool, } #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct NodeSyncCommittee { - pub validators: Vec, - pub validator_aggregates: Vec>, + pub validators: Vec, + pub validator_aggregates: Vec>, } diff --git a/sync-committee-prover/src/responses/validator_response.rs b/prover/src/responses/validator_response.rs similarity index 60% rename from sync-committee-prover/src/responses/validator_response.rs rename to prover/src/responses/validator_response.rs index 4422a4b8f..6a37e0afd 100644 --- a/sync-committee-prover/src/responses/validator_response.rs +++ b/prover/src/responses/validator_response.rs @@ -2,14 +2,14 @@ use ethereum_consensus::bellatrix::Validator; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { - pub(crate) data: ValidatorData, - execution_optimistic: bool, + pub(crate) data: ValidatorData, + execution_optimistic: bool, } #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct ValidatorData { - pub index: String, - pub balance: String, - pub status: String, - pub(crate) validator: Validator, + pub index: String, + pub balance: String, + pub status: String, + pub(crate) validator: Validator, } diff --git a/prover/src/routes.rs b/prover/src/routes.rs new file mode 100644 index 000000000..a6b7d37ed --- /dev/null +++ b/prover/src/routes.rs @@ -0,0 +1,21 @@ +pub fn header_route(block_id: &str) -> String { + format!("/eth/v1/beacon/headers/{block_id}") +} + +pub fn block_route(block_id: &str) -> String { + format!("/eth/v2/beacon/blocks/{block_id}") +} + +pub fn sync_committee_route(state_id: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/sync_committees") +} + +pub fn validator_route(state_id: &str, validator_index: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/validators/{validator_index}") +} +pub fn beacon_state_route(state_id: &str) -> String { + format!("/eth/v2/debug/beacon/states/{state_id}") +} +pub fn finality_checkpoints(state_id: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/finality_checkpoints") +} diff --git a/prover/src/test.rs b/prover/src/test.rs new file mode 100644 index 000000000..3854bd5f6 --- /dev/null +++ b/prover/src/test.rs @@ -0,0 +1,508 @@ +use super::*; +use base2::Base2; +use ethereum_consensus::altair::NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2; +use light_client_primitives::{ + types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, + util::compute_sync_committee_period_at_slot, +}; +use light_client_verifier::light_client::EthLightClient; +use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; +use std::{thread, time::Duration}; +use tokio::time; +use tokio_stream::{wrappers::IntervalStream, StreamExt}; + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_block_header_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let mut block_header = sync_committee_prover.fetch_header("1000").await; + while block_header.is_err() { + println!("I am running till i am ok. lol"); + block_header = sync_committee_prover.fetch_header("1000").await; + } + assert!(block_header.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_block_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block = sync_committee_prover.fetch_block("100").await; + assert!(block.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_sync_committee_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let block = sync_committee_prover.fetch_sync_committee("117").await; + assert!(block.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_validator_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let validator = sync_committee_prover.fetch_validator("2561", "48").await; + assert!(validator.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_processed_sync_committee_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let validator = sync_committee_prover.fetch_processed_sync_committee("2561").await; + assert!(validator.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_beacon_state_works() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let beacon_state = sync_committee_prover.fetch_beacon_state("genesis").await; + assert!(beacon_state.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn state_root_and_block_header_root_matches() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let beacon_state = sync_committee_prover.fetch_beacon_state("100").await; + assert!(beacon_state.is_ok()); + + let block_header = sync_committee_prover.fetch_header("100").await; + assert!(block_header.is_ok()); + + let state = beacon_state.unwrap(); + let block_header = block_header.unwrap(); + let hash_tree_root = state.clone().hash_tree_root(); + + assert!(block_header.state_root == hash_tree_root.unwrap()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn fetch_finality_checkpoints_work() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; + assert!(finality_checkpoint.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_execution_payload_proof() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let block_id = "finalized"; + let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); + + let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); + + let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + + // verify the associated execution header of the finalized beacon header. + let mut execution_payload = execution_payload_proof.clone(); + let multi_proof_vec = execution_payload.multi_proof; + let multi_proof_nodes = multi_proof_vec + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), + execution_payload.block_number.hash_tree_root().unwrap(), + ], + &multi_proof_nodes, + &[ + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + ], + ); + + println!("execution_payload_root {:?}", execution_payload_root); + + let execution_payload_hash_tree_root = + finalized_block.body.execution_payload.clone().hash_tree_root().unwrap(); + + println!("execution_payload_hash_tree_root {:?}", execution_payload_hash_tree_root); + + assert_eq!(execution_payload_root, execution_payload_hash_tree_root); + + let execution_payload_branch = execution_payload + .execution_payload_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0, + &Node::from_bytes(finalized_header.clone().body_root.as_ref().try_into().unwrap()), + ); + + assert_eq!(is_merkle_branch_valid, true); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_finality_proof() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let block_id = "finalized"; + let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); + + let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + + let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); + + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_header_slot.to_string().as_str()) + .await + .unwrap(); + + let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); + + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + // purposely for waiting + //println!("sleeping"); + thread::sleep(time::Duration::from_secs(5)); + + let mut attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + + while attested_header.is_err() { + println!("I am running till i am ok. lol {}", &block_id); + attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + } + + let attested_header = attested_header.unwrap(); + + let finality_branch_proof = finality_branch_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); + + // verify the associated execution header of the finalized beacon header. + + // Verify that the `finality_branch` confirms `finalized_header` + // to match the finalized checkpoint root saved in the state of `attested_header`. + // Note that the genesis finalized checkpoint root is represented as a zero hash. + let finalized_root = &Node::from_bytes( + light_client_primitives::util::hash_tree_root(finalized_header.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ); + + let branch = finality_branch_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + println!("finalized_root {:?}", finalized_root.clone()); + + let finality_hash_tree_root = finalized_block.clone().hash_tree_root().unwrap(); + + assert_eq!(finalized_root, &finality_hash_tree_root); + + println!("finalized_root {:?}", finality_hash_tree_root); + let is_merkle_branch_valid = is_valid_merkle_branch( + finalized_root, + branch.iter(), + FINALIZED_ROOT_INDEX.floor_log2() as usize, + get_subtree_index(FINALIZED_ROOT_INDEX) as usize, + &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), + ); + + println!("is_merkle_branch_valid {:?}", is_merkle_branch_valid.clone()); + + assert!(is_merkle_branch_valid, "{}", true); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_sync_committee_proof() { + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); + + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); + dbg!(&finality_checkpoint.root); + + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + dbg!(&block_id); + + let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + let state = sync_committee_prover + .fetch_beacon_state(&block_header.slot.to_string()) + .await + .unwrap(); + + let block_id = "head"; + let mut finalized_block = sync_committee_prover.fetch_block(block_id).await; + + while finalized_block.is_err() { + println!("I am running finalized block till i am ok. lol {}", &block_id); + finalized_block = sync_committee_prover.fetch_block(block_id).await; + } + + let finalized_block = finalized_block.unwrap(); + + let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); + + let sync_committee_proof = prove_sync_committee_update(finalized_state.clone()).unwrap(); + + let sync_committee_proof = sync_committee_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); + let mut sync_committee = sync_committee_prover + .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) + .await; + + while sync_committee.is_err() { + println!("I am fetching sync committee till i am ok. lol {}", &block_id); + sync_committee = sync_committee_prover + .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) + .await; + } + + let sync_committee = sync_committee.unwrap(); + + let calculated_finalized_root = calculate_multi_merkle_root( + &[Node::from_bytes( + light_client_primitives::util::hash_tree_root(sync_committee.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + )], + &sync_committee_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(), + &[GeneralizedIndex(NEXT_SYNC_COMMITTEE_INDEX as usize)], + ); + + let hash_tree_root = finalized_state.clone().hash_tree_root().unwrap(); + + println!("calculated_finalized_root {:?}", calculated_finalized_root); + println!("finalized_state_root {:?}", finalized_header.state_root); + println!("hash_tree_root {:?}", hash_tree_root); + + let next_sync_committee_branch = sync_committee_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &Node::from_bytes( + light_client_primitives::util::hash_tree_root(sync_committee.clone()) + .unwrap() + .as_ref() + .try_into() + .unwrap(), + ), + next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, + &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), + ); + + println!("valid merkle branch for sync committee {}", is_merkle_branch_valid); + assert!(is_merkle_branch_valid, "{}", true); +} + +// use tokio interval(should run every 13 minutes) +// every 13 minutes, fetch latest finalized block +// then prove the execution payload +// prove the finality branch + +// prove sync committee if there is a sync committee update +// to prove sync comnmittee update, calculate state_period and the update_attested_period +// ensure they are the same, and then prove sync committee + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_prover() { + // In test config an epoch is 6 slots and we expect finalization every two epochs, + // a slot is 12 seconds so that brings us to 144 seconds + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); + + let node_url: String = "http://127.0.0.1:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); + dbg!(&finality_checkpoint.root); + + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + dbg!(&block_id); + + let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + let state = sync_committee_prover + .fetch_beacon_state(&block_header.slot.to_string()) + .await + .unwrap(); + + let mut client_state = LightClientState { + finalized_header: block_header.clone(), + current_sync_committee: state.current_sync_committee, + next_sync_committee: state.next_sync_committee, + }; + + while let Some(_ts) = stream.next().await { + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); + dbg!(&finality_checkpoint.root); + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + dbg!(&block_id); + let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); + + if finalized_block.slot <= client_state.finalized_header.slot { + println!("finalized_block slot is {}", &finalized_block.slot); + println!("finalized_header slot is {}", &client_state.finalized_header.slot); + continue + } + + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); + + let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .await + .unwrap(); + + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_header_slot.to_string().as_str()) + .await + .unwrap(); + + let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); + let finality_branch_proof = finality_branch_proof + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect::>(); + + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + // purposely for waiting + //println!("sleeping"); + thread::sleep(time::Duration::from_secs(5)); + + let mut attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + + while attested_header.is_err() { + println!("I am running till i am ok. lol {}", &block_id); + attested_header = sync_committee_prover + .fetch_header(attested_header_slot.to_string().as_str()) + .await; + } + + let attested_header = attested_header.unwrap(); + + let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); + + let sync_committee_update = if state_period == attested_header_slot { + let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); + + let sync_committee_proof = sync_committee_proof + .into_iter() + .map(|node| { + Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") + }) + .collect::>(); + + let sync_committee = sync_committee_prover + .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) + .await + .unwrap(); + + Some(SyncCommitteeUpdate { + next_sync_committee: sync_committee, + next_sync_committee_branch: sync_committee_proof, + }) + } else { + None + }; + + // construct light client + let light_client_update = LightClientUpdate { + attested_header, + sync_committee_update, + finalized_header, + execution_payload: execution_payload_proof, + finality_branch: finality_branch_proof, + sync_aggregate: finalized_block.body.sync_aggregate, + signature_slot: attested_header_slot, + ancestor_blocks: vec![], + }; + + client_state = EthLightClient::verify_sync_committee_attestation( + client_state.clone(), + light_client_update, + ) + .unwrap(); + println!( + "Sucessfully verified Ethereum block at slot {:?}", + client_state.finalized_header.slot + ); + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..441913f61 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,23 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Format comments +comment_width = 100 +wrap_comments = true +# Misc +chain_width = 80 +spaces_around_ranges = false +binop_separator = "Back" +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true diff --git a/sync-committee-prover/src/error.rs b/sync-committee-prover/src/error.rs deleted file mode 100644 index 9ec588852..000000000 --- a/sync-committee-prover/src/error.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug)] -pub enum Error { - AggregateSignatureError, - EmptySignedBeaconBlock, -} diff --git a/sync-committee-prover/src/lib.rs b/sync-committee-prover/src/lib.rs deleted file mode 100644 index add7b1cda..000000000 --- a/sync-committee-prover/src/lib.rs +++ /dev/null @@ -1,344 +0,0 @@ -mod error; -mod responses; -mod routes; -#[cfg(test)] -mod test; - -use ethereum_consensus::altair::Validator; -use ethereum_consensus::bellatrix::{ - BeaconBlock, BeaconBlockBody, BeaconBlockHeader, BeaconState, Checkpoint, SignedBeaconBlock, - SignedBeaconBlockHeader, SyncCommittee, -}; -use reqwest::{Client, StatusCode}; - -use crate::error::Error; -use crate::responses::sync_committee_response::NodeSyncCommittee; -use crate::routes::*; -use ethereum_consensus::bellatrix::mainnet::{ - BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, - MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, -}; -use ethereum_consensus::crypto::{aggregate, eth_aggregate_public_keys, PublicKey}; -use ethereum_consensus::phase0::mainnet::{ - EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, - HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, - MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, - SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, -}; -use ethereum_consensus::primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}; -use light_client_primitives::types::{ - BlockRootsProof, ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, - EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, - NEXT_SYNC_COMMITTEE_INDEX, -}; -use light_client_primitives::util::get_subtree_index; -use ssz_rs::{ - get_generalized_index, Bitlist, GeneralizedIndex, List, Node, SszVariableOrIndex, Vector, -}; - -type BeaconBlockType = BeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, ->; - -type SignedBeaconBlockType = SignedBeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, ->; - -pub type BeaconStateType = BeaconState< - SLOTS_PER_HISTORICAL_ROOT, - HISTORICAL_ROOTS_LIMIT, - ETH1_DATA_VOTES_BOUND, - VALIDATOR_REGISTRY_LIMIT, - EPOCHS_PER_HISTORICAL_VECTOR, - EPOCHS_PER_SLASHINGS_VECTOR, - MAX_VALIDATORS_PER_COMMITTEE, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, ->; - -pub struct SyncCommitteeProver { - pub node_url: String, - pub client: Client, -} - -impl SyncCommitteeProver { - pub fn new(node_url: String) -> Self { - let client = reqwest::Client::new(); - - SyncCommitteeProver { node_url, client } - } - - pub async fn fetch_finalized_checkpoint(&self) -> Result { - let full_url = self.generate_route(&finality_checkpoints("head")); - let response = self.client.get(full_url).send().await?; - - let response_data = response - .json::() - .await?; - Ok(response_data.data.finalized) - } - - pub async fn fetch_header(&self, block_id: &str) -> Result { - let path = header_route(block_id); - let full_url = self.generate_route(&path); - let response = self.client.get(full_url).send().await?; - let status = response.status().as_u16(); - - let response_data = response - .json::() - .await?; - - let beacon_block_header = response_data.data.header.message; - - Ok(beacon_block_header) - } - pub async fn fetch_block( - &self, - block_id: &str, - ) -> Result< - BeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, - reqwest::Error, - > { - let path = block_route(block_id); - let full_url = self.generate_route(&path); - - let response = self.client.get(full_url).send().await?; - - let response_data = response - .json::() - .await?; - - let beacon_block = response_data.data.message; - - Ok(beacon_block) - } - pub async fn fetch_sync_committee( - &self, - state_id: &str, - ) -> Result { - let path = sync_committee_route(state_id); - let full_url = self.generate_route(&path); - - let response = self.client.get(full_url).send().await?; - - let response_data = response - .json::() - .await?; - - let sync_committee = response_data.data; - - Ok(sync_committee) - } - pub async fn fetch_validator( - &self, - state_id: &str, - validator_index: &str, - ) -> Result { - let path = validator_route(state_id, validator_index); - let full_url = self.generate_route(&path); - - let response = self.client.get(full_url).send().await?; - - let response_data = response - .json::() - .await?; - - let validator = response_data.data.validator; - - Ok(validator) - } - - pub async fn fetch_beacon_state( - &self, - state_id: &str, - ) -> Result { - let path = beacon_state_route(state_id); - let full_url = self.generate_route(&path); - - let response = self.client.get(full_url).send().await?; - - let response_data = response - .json::() - .await?; - - let beacon_state = response_data.data; - - Ok(beacon_state) - } - - pub async fn fetch_processed_sync_committee( - &self, - state_id: &str, - ) -> Result, reqwest::Error> { - // fetches sync committee from Node - let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; - - let mut validators: List = Default::default(); - for mut validator_index in node_sync_committee.validators.clone() { - // fetches validator based on validator index - let validator = self - .fetch_validator(state_id.clone(), &validator_index) - .await?; - validators.push(validator); - } - - let public_keys_vector = node_sync_committee - .validators - .into_iter() - .map(|i| { - let validator_index: ValidatorIndex = i.parse().unwrap(); - validators[validator_index].public_key.clone() - }) - .collect::>(); - - let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector).unwrap(); - - let sync_committee = SyncCommittee:: { - public_keys: public_keys_vector, - aggregate_public_key, - }; - - Ok(sync_committee) - } - - async fn fetch_latest_finalized_block( - &self, - ) -> Result<(BeaconBlockHeader, BeaconBlockType), reqwest::Error> { - let block_header = self.fetch_header("finalized").await?; - let block = self.fetch_block("finalized").await?; - - Ok((block_header, block)) - } - - fn generate_route(&self, path: &str) -> String { - format!("{}{}", self.node_url.clone(), path) - } -} - -fn get_attestation_slots_for_finalized_header( - finalized_header: &BeaconBlockHeader, - slots_per_epoch: u64, -) -> Slot { - let finalized_header_slot = finalized_header.slot; - - // given that an epoch is 32 slots and blocks are finalized every 2 epochs - // so the attested slot for a finalized block is 64 slots away - let attested_slot = finalized_header_slot + (slots_per_epoch * 2); - - attested_slot -} - -fn prove_beacon_state_values( - data: BeaconStateType, - indices: &[usize], -) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(data, indices)?; - Ok(proof) -} - -fn prove_beacon_block_values( - data: BeaconBlockType, - indices: &[usize], -) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(data, indices)?; - Ok(proof) -} - -fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result { - let indices = [ - EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, - ]; - // generate multi proofs - let multi_proof = - ssz_rs::generate_proof(block.body.execution_payload.clone(), indices.as_slice())?; - - let execution_payload_index = [GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0]; - let execution_payload_branch = - ssz_rs::generate_proof(block.body.clone(), execution_payload_index.as_slice())?; - - Ok(ExecutionPayloadProof { - state_root: block.body.execution_payload.state_root, - block_number: block.body.execution_payload.block_number, - multi_proof: multi_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), - execution_payload_branch: execution_payload_branch - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), - }) -} - -fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result> { - let indices = vec![NEXT_SYNC_COMMITTEE_INDEX as usize]; - let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; - - Ok(proof) -} - -fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { - let indices = [FINALIZED_ROOT_INDEX as usize]; //vec![get_subtree_index(FINALIZED_ROOT_INDEX) as usize]; - let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; - - Ok(proof) -} - -// function that generates block roots proof -// block number and convert to get_subtree_index -// beacon state has a block roots vec, pass the block root to generate proof -fn prove_block_roots_proof( - state: BeaconStateType, - block_number: u64, -) -> anyhow::Result { - let indices = vec![get_subtree_index(block_number) as usize]; - let proof = ssz_rs::generate_proof(state.block_roots, indices.as_slice())?; - - Ok(BlockRootsProof { - block_header_index: block_number, - block_header_branch: proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), - }) -} - -// prove the block roots vec inside the beacon state which will be the block roots branch - -// when aggr sigs, create a new bit list diff --git a/sync-committee-prover/src/responses/beacon_block_response.rs b/sync-committee-prover/src/responses/beacon_block_response.rs deleted file mode 100644 index e4f1a19c6..000000000 --- a/sync-committee-prover/src/responses/beacon_block_response.rs +++ /dev/null @@ -1,36 +0,0 @@ -use ethereum_consensus::bellatrix::mainnet::{ - BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, - MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, -}; -use ethereum_consensus::bellatrix::{BeaconBlock, BeaconBlockHeader}; -use ethereum_consensus::phase0::mainnet::{ - EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, - HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, - MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, - SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, -}; - -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct Response { - pub(crate) data: ResponseData, - version: String, - execution_optimistic: bool, -} - -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct ResponseData { - pub(crate) message: BeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, - pub signature: String, -} diff --git a/sync-committee-prover/src/responses/beacon_state_response.rs b/sync-committee-prover/src/responses/beacon_state_response.rs deleted file mode 100644 index d9155750b..000000000 --- a/sync-committee-prover/src/responses/beacon_state_response.rs +++ /dev/null @@ -1,26 +0,0 @@ -use ethereum_consensus::bellatrix::BeaconState; - -use ethereum_consensus::bellatrix::mainnet::{ - BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, - ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, - MAX_TRANSACTIONS_PER_PAYLOAD, MAX_VALIDATORS_PER_COMMITTEE, SLOTS_PER_HISTORICAL_ROOT, - SYNC_COMMITTEE_SIZE, VALIDATOR_REGISTRY_LIMIT, -}; -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct Response { - version: String, - pub(crate) data: BeaconState< - SLOTS_PER_HISTORICAL_ROOT, - HISTORICAL_ROOTS_LIMIT, - ETH1_DATA_VOTES_BOUND, - VALIDATOR_REGISTRY_LIMIT, - EPOCHS_PER_HISTORICAL_VECTOR, - EPOCHS_PER_SLASHINGS_VECTOR, - MAX_VALIDATORS_PER_COMMITTEE, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, - >, -} diff --git a/sync-committee-prover/src/routes.rs b/sync-committee-prover/src/routes.rs deleted file mode 100644 index a7b398b28..000000000 --- a/sync-committee-prover/src/routes.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub fn header_route(block_id: &str) -> String { - format!("/eth/v1/beacon/headers/{block_id}") -} - -pub fn block_route(block_id: &str) -> String { - format!("/eth/v2/beacon/blocks/{block_id}") -} - -pub fn sync_committee_route(state_id: &str) -> String { - format!("/eth/v1/beacon/states/{state_id}/sync_committees") -} - -pub fn validator_route(state_id: &str, validator_index: &str) -> String { - format!("/eth/v1/beacon/states/{state_id}/validators/{validator_index}") -} -pub fn beacon_state_route(state_id: &str) -> String { - format!("/eth/v2/debug/beacon/states/{state_id}") -} -pub fn finality_checkpoints(state_id: &str) -> String { - format!("/eth/v1/beacon/states/{state_id}/finality_checkpoints") -} diff --git a/sync-committee-prover/src/test.rs b/sync-committee-prover/src/test.rs deleted file mode 100644 index 7e408823f..000000000 --- a/sync-committee-prover/src/test.rs +++ /dev/null @@ -1,560 +0,0 @@ -use super::*; -use base2::Base2; -use ethereum_consensus::altair::NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2; -use light_client_primitives::types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}; -use light_client_primitives::util::compute_sync_committee_period_at_slot; -use light_client_verifier::light_client::EthLightClient; -use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; -use std::thread; -use std::time::Duration; -use tokio::time; -use tokio_stream::wrappers::IntervalStream; -use tokio_stream::StreamExt; - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_block_header_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let mut block_header = sync_committee_prover.fetch_header("1000").await; - while block_header.is_err() { - println!("I am running till i am ok. lol"); - block_header = sync_committee_prover.fetch_header("1000").await; - } - assert!(block_header.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_block_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_block("100").await; - assert!(block.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_sync_committee_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_sync_committee("117").await; - assert!(block.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_validator_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let validator = sync_committee_prover.fetch_validator("2561", "48").await; - assert!(validator.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_processed_sync_committee_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let validator = sync_committee_prover - .fetch_processed_sync_committee("2561") - .await; - assert!(validator.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_beacon_state_works() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let beacon_state = sync_committee_prover.fetch_beacon_state("genesis").await; - assert!(beacon_state.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn state_root_and_block_header_root_matches() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let beacon_state = sync_committee_prover.fetch_beacon_state("100").await; - assert!(beacon_state.is_ok()); - - let block_header = sync_committee_prover.fetch_header("100").await; - assert!(block_header.is_ok()); - - let state = beacon_state.unwrap(); - let block_header = block_header.unwrap(); - let hash_tree_root = state.clone().hash_tree_root(); - - assert!(block_header.state_root == hash_tree_root.unwrap()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_finality_checkpoints_work() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; - assert!(finality_checkpoint.is_ok()); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_execution_payload_proof() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - - let block_id = "finalized"; - let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); - - let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); - - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); - - // verify the associated execution header of the finalized beacon header. - let mut execution_payload = execution_payload_proof.clone(); - let multi_proof_vec = execution_payload.multi_proof; - let multi_proof_nodes = multi_proof_vec - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let execution_payload_root = calculate_multi_merkle_root( - &[ - Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), - execution_payload.block_number.hash_tree_root().unwrap(), - ], - &multi_proof_nodes, - &[ - GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), - GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), - ], - ); - - println!("execution_payload_root {:?}", execution_payload_root); - - let execution_payload_hash_tree_root = finalized_block - .body - .execution_payload - .clone() - .hash_tree_root() - .unwrap(); - - println!( - "execution_payload_hash_tree_root {:?}", - execution_payload_hash_tree_root - ); - - assert_eq!(execution_payload_root, execution_payload_hash_tree_root); - - let execution_payload_branch = execution_payload - .execution_payload_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let is_merkle_branch_valid = is_valid_merkle_branch( - &execution_payload_root, - execution_payload_branch.iter(), - EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0, - &Node::from_bytes( - finalized_header - .clone() - .body_root - .as_ref() - .try_into() - .unwrap(), - ), - ); - - assert_eq!(is_merkle_branch_valid, true); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_finality_proof() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - - let block_id = "finalized"; - let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); - - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); - - let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) - .await - .unwrap(); - - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_header_slot.to_string().as_str()) - .await - .unwrap(); - - let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); - - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - // purposely for waiting - //println!("sleeping"); - thread::sleep(time::Duration::from_secs(5)); - - let mut attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - - while attested_header.is_err() { - println!("I am running till i am ok. lol {}", &block_id); - attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - } - - let attested_header = attested_header.unwrap(); - - let finality_branch_proof = finality_branch_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); - - // verify the associated execution header of the finalized beacon header. - - // Verify that the `finality_branch` confirms `finalized_header` - // to match the finalized checkpoint root saved in the state of `attested_header`. - // Note that the genesis finalized checkpoint root is represented as a zero hash. - let finalized_root = &Node::from_bytes( - light_client_primitives::util::hash_tree_root(finalized_header.clone()) - .unwrap() - .as_ref() - .try_into() - .unwrap(), - ); - - let branch = finality_branch_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - println!("finalized_root {:?}", finalized_root.clone()); - - let finality_hash_tree_root = finalized_block.clone().hash_tree_root().unwrap(); - - assert_eq!(finalized_root, &finality_hash_tree_root); - - println!("finalized_root {:?}", finality_hash_tree_root); - let is_merkle_branch_valid = is_valid_merkle_branch( - finalized_root, - branch.iter(), - FINALIZED_ROOT_INDEX.floor_log2() as usize, - get_subtree_index(FINALIZED_ROOT_INDEX) as usize, - &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), - ); - - println!( - "is_merkle_branch_valid {:?}", - is_merkle_branch_valid.clone() - ); - - assert!(is_merkle_branch_valid, "{}", true); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_sync_committee_proof() { - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); - - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - - let finality_checkpoint = sync_committee_prover - .fetch_finalized_checkpoint() - .await - .unwrap(); - dbg!(&finality_checkpoint.root); - - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - dbg!(&block_id); - - let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - - let state = sync_committee_prover - .fetch_beacon_state(&block_header.slot.to_string()) - .await - .unwrap(); - - let block_id = "head"; - let mut finalized_block = sync_committee_prover.fetch_block(block_id).await; - - while finalized_block.is_err() { - println!( - "I am running finalized block till i am ok. lol {}", - &block_id - ); - finalized_block = sync_committee_prover.fetch_block(block_id).await; - } - - let finalized_block = finalized_block.unwrap(); - - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); - - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) - .await - .unwrap(); - - let sync_committee_proof = prove_sync_committee_update(finalized_state.clone()).unwrap(); - - let sync_committee_proof = sync_committee_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); - let mut sync_committee = sync_committee_prover - .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) - .await; - - while sync_committee.is_err() { - println!( - "I am fetching sync committee till i am ok. lol {}", - &block_id - ); - sync_committee = sync_committee_prover - .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) - .await; - } - - let sync_committee = sync_committee.unwrap(); - - let calculated_finalized_root = calculate_multi_merkle_root( - &[ - Node::from_bytes( - light_client_primitives::util::hash_tree_root( - sync_committee.clone(), - ) - .unwrap() - .as_ref() - .try_into() - .unwrap(), - ), - ], - &sync_committee_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(), - &[ - GeneralizedIndex(NEXT_SYNC_COMMITTEE_INDEX as usize), - ], - ); - - let hash_tree_root = finalized_state - .clone() - .hash_tree_root() - .unwrap(); - - println!("calculated_finalized_root {:?}", calculated_finalized_root); - println!("finalized_state_root {:?}", finalized_header.state_root); - println!("hash_tree_root {:?}", hash_tree_root); - - - let next_sync_committee_branch = sync_committee_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes( - light_client_primitives::util::hash_tree_root( - sync_committee.clone(), - ) - .unwrap() - .as_ref() - .try_into() - .unwrap(), - ), - next_sync_committee_branch.iter(), - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, - NEXT_SYNC_COMMITTEE_INDEX as usize, - &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), - ); - - println!( - "valid merkle branch for sync committee {}", - is_merkle_branch_valid - ); - assert!(is_merkle_branch_valid, "{}", true); -} - -// use tokio interval(should run every 13 minutes) -// every 13 minutes, fetch latest finalized block -// then prove the execution payload -// prove the finality branch - -// prove sync committee if there is a sync committee update -// to prove sync comnmittee update, calculate state_period and the update_attested_period -// ensure they are the same, and then prove sync committee - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_prover() { - // In test config an epoch is 6 slots and we expect finalization every two epochs, - // a slot is 12 seconds so that brings us to 144 seconds - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); - - let node_url: String = "http://127.0.0.1:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - - let finality_checkpoint = sync_committee_prover - .fetch_finalized_checkpoint() - .await - .unwrap(); - dbg!(&finality_checkpoint.root); - - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - dbg!(&block_id); - - let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - - let state = sync_committee_prover - .fetch_beacon_state(&block_header.slot.to_string()) - .await - .unwrap(); - - let mut client_state = LightClientState { - finalized_header: block_header.clone(), - current_sync_committee: state.current_sync_committee, - next_sync_committee: state.next_sync_committee, - }; - - while let Some(_ts) = stream.next().await { - let finality_checkpoint = sync_committee_prover - .fetch_finalized_checkpoint() - .await - .unwrap(); - dbg!(&finality_checkpoint.root); - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - dbg!(&block_id); - let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); - - if finalized_block.slot <= client_state.finalized_header.slot { - println!("finalized_block slot is {}", &finalized_block.slot); - println!( - "finalized_header slot is {}", - &client_state.finalized_header.slot - ); - continue; - } - - let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - - let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); - - let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) - .await - .unwrap(); - - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_header_slot.to_string().as_str()) - .await - .unwrap(); - - let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); - let finality_branch_proof = finality_branch_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); - - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - // purposely for waiting - //println!("sleeping"); - thread::sleep(time::Duration::from_secs(5)); - - let mut attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - - while attested_header.is_err() { - println!("I am running till i am ok. lol {}", &block_id); - attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - } - - let attested_header = attested_header.unwrap(); - - let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); - - let sync_committee_update = if state_period == attested_header_slot { - let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); - - let sync_committee_proof = sync_committee_proof - .into_iter() - .map(|node| { - Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") - }) - .collect::>(); - - let sync_committee = sync_committee_prover - .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) - .await - .unwrap(); - - Some(SyncCommitteeUpdate { - next_sync_committee: sync_committee, - next_sync_committee_branch: sync_committee_proof, - }) - } else { - None - }; - - // construct light client - let light_client_update = LightClientUpdate { - attested_header, - sync_committee_update, - finalized_header, - execution_payload: execution_payload_proof, - finality_branch: finality_branch_proof, - sync_aggregate: finalized_block.body.sync_aggregate, - signature_slot: attested_header_slot, - ancestor_blocks: vec![], - }; - - client_state = EthLightClient::verify_sync_committee_attestation( - client_state.clone(), - light_client_update, - ) - .unwrap(); - println!( - "Sucessfully verified Ethereum block at slot {:?}", - client_state.finalized_header.slot - ); - } -} diff --git a/light-client-verifier/Cargo.toml b/verifier/Cargo.toml similarity index 59% rename from light-client-verifier/Cargo.toml rename to verifier/Cargo.toml index 5e18be9d1..4fa8e4f74 100644 --- a/light-client-verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -1,15 +1,13 @@ [package] -name = "light-client-verifier" +name = "sync-committee-verifier" version = "0.1.0" edition = "2021" authors = ["Polytope Labs"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -base2 = {version="0.2.2", default-features=false} -light-client-primitives = {path="../light-client-primitives", default-features = false } +base2 = { version="0.2.2", default-features = false } +sync-committee-primitives = { path= "../primitives", default-features = false } ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } -libc-print = {version=" 0.1.20"} +log = "0.4.17" \ No newline at end of file diff --git a/verifier/src/error.rs b/verifier/src/error.rs new file mode 100644 index 000000000..dcd6e74b5 --- /dev/null +++ b/verifier/src/error.rs @@ -0,0 +1,34 @@ +use core::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum Error { + SyncCommitteeParticipantsTooLow, + InvalidUpdate, + DomainError, + FastAggregateError(ethereum_consensus::crypto::Error), + InvalidMerkleBranch, + InvalidRoot, + MerkleizationError, +} + +impl From for Error { + fn from(error: ethereum_consensus::crypto::Error) -> Self { + Error::FastAggregateError(error) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Error::SyncCommitteeParticipantsTooLow => { + write!(f, "Sync committee participants are too low") + }, + Error::InvalidUpdate => write!(f, "Invalid update"), + Error::DomainError => write!(f, "Couldn't get domain"), + Error::FastAggregateError(err) => write!(f, "Fast aggregate error"), + Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), + Error::InvalidRoot => write!(f, "Invalid root"), + Error::MerkleizationError => write!(f, "Merkleization error"), + } + } +} diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs new file mode 100644 index 000000000..9c5f0e35f --- /dev/null +++ b/verifier/src/lib.rs @@ -0,0 +1,461 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + +pub mod error; + +use crate::error::Error; +use alloc::vec::Vec; +use base2::Base2; +use core::{borrow::Borrow, fmt::Display}; +use ethereum_consensus::{ + altair::{ + mainnet::SYNC_COMMITTEE_SIZE, FINALIZED_ROOT_INDEX_FLOOR_LOG_2, + NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, + }, + bellatrix::compute_domain, + primitives::Root, + signing::compute_signing_root, + state_transition::Context, +}; +use ssz_rs::{ + calculate_merkle_root, calculate_multi_merkle_root, prelude::is_valid_merkle_branch, + GeneralizedIndex, Merkleized, Node, +}; +use sync_committee_primitives::{ + types::{ + AncestryProof, BLOCK_ROOTS_INDEX, DOMAIN_SYNC_COMMITTEE, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, GENESIS_VALIDATORS_ROOT, + HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, NEXT_SYNC_COMMITTEE_INDEX, + }, + util::{ + compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, + get_subtree_index, hash_tree_root, + }, +}; + +pub type LightClientState = sync_committee_primitives::types::LightClientState; +pub type LightClientUpdate = + sync_committee_primitives::types::LightClientUpdate; + +/// This function simply verifies a sync committee's attestation & it's finalized counterpart. +pub fn verify_sync_committee_attestation( + trusted_state: LightClientState, + update: LightClientUpdate, +) -> Result { + if update.finality_branch.len() != FINALIZED_ROOT_INDEX_FLOOR_LOG_2 as usize && + update.sync_committee_update.is_some() && + update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() != + NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize + { + log::debug!("Invalid update "); + log::debug!("update finality branch length {} ", update.finality_branch.len()); + log::debug!("FINALIZED_ROOT_INDEX_FLOOR_LOG_2 {}", FINALIZED_ROOT_INDEX_FLOOR_LOG_2); + log::debug!( + "update next sync committee branch length {} ", + update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() + ); + log::debug!( + "NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 {}", + NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 + ); + //Err(Error::InvalidUpdate)? + } + + // Verify sync committee has super majority participants + let sync_committee_bits = update.sync_aggregate.sync_committee_bits; + let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; + if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { + log::debug!("SyncCommitteeParticipantsTooLow "); + log::debug!("sync_aggregate_participants {} ", { sync_aggregate_participants * 3 }); + log::debug!("sync_committee_bits {}", { sync_committee_bits.clone().len() * 2 }); + //Err(Error::SyncCommitteeParticipantsTooLow)? + } + + // Verify update does not skip a sync committee period + let is_valid_update = update.signature_slot > update.attested_header.slot && + update.attested_header.slot >= update.finalized_header.slot; + if !is_valid_update { + log::debug!("is_valid_update {} ", is_valid_update); + log::debug!( + "update.signature_slot {} update.attested_header.slot {}", + update.signature_slot, + update.attested_header.slot + ); + log::debug!( + "update.attested_header.slot {} update.finalized_header.slot {}", + update.attested_header.slot, + update.finalized_header.slot + ); + //Err(Error::InvalidUpdate)? + } + + let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); + let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); + if !(state_period..=state_period + 1).contains(&update_signature_period) { + log::debug!("invalid update"); + log::debug!("state_period is {}", state_period); + log::debug!("update_signature_period is {}", update_signature_period); + //Err(Error::InvalidUpdate)? + } + + // Verify update is relevant + let update_attested_period = compute_sync_committee_period_at_slot(update.attested_header.slot); + let update_has_next_sync_committee = + update.sync_committee_update.is_some() && update_attested_period == state_period; + + if !(update.attested_header.slot > trusted_state.finalized_header.slot || + update_has_next_sync_committee) + { + Err(Error::InvalidUpdate)? + } + + // Verify sync committee aggregate signature + let sync_committee = if update_signature_period == state_period { + trusted_state.current_sync_committee.clone() + } else { + trusted_state.next_sync_committee.clone() + }; + + let sync_committee_pubkeys = sync_committee.public_keys; + + let participant_pubkeys = sync_committee_bits + .iter() + .zip(sync_committee_pubkeys.iter()) + .filter_map(|(bit, key)| if *bit { Some(key) } else { None }) + .collect::>(); + + let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); + //TODO: we probably need to construct context + let domain = compute_domain( + DOMAIN_SYNC_COMMITTEE, + Some(fork_version), + Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().map_err(|_| Error::InvalidRoot)?)), + &Context::default(), + ) + .map_err(|_| Error::InvalidUpdate)?; + + let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain); + + // todo: should be generic + ethereum_consensus::crypto::fast_aggregate_verify( + &*participant_pubkeys, + signing_root.map_err(|_| Error::InvalidRoot)?.as_bytes(), + &update.sync_aggregate.sync_committee_signature, + )?; + + // Verify that the `finality_branch` confirms `finalized_header` + // to match the finalized checkpoint root saved in the state of `attested_header`. + // Note that the genesis finalized checkpoint root is represented as a zero hash. + let finalized_root = &Node::from_bytes( + hash_tree_root(update.finalized_header.clone()) + .map_err(|_| Error::MerkleizationError)? + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ); + + let branch = update + .finality_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + finalized_root, + branch.iter(), + FINALIZED_ROOT_INDEX.floor_log2() as usize, + FINALIZED_ROOT_INDEX as usize, + &Node::from_bytes( + update + .attested_header + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + ); + + log::debug!("valid merkle branch for finalized_root {}", is_merkle_branch_valid); + if !is_merkle_branch_valid { + log::debug!("invalid merkle branch for finalized root"); + //Err(Error::InvalidMerkleBranch)?; + } + + // verify the associated execution header of the finalized beacon header. + let mut execution_payload = update.execution_payload; + let multi_proof_vec = execution_payload.multi_proof; + let multi_proof_nodes = multi_proof_vec + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes( + execution_payload + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + execution_payload + .block_number + .hash_tree_root() + .map_err(|_| Error::InvalidRoot)?, + ], + &multi_proof_nodes, + &[ + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + ], + ); + + let execution_payload_branch = execution_payload + .execution_payload_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + EXECUTION_PAYLOAD_INDEX as usize, + &Node::from_bytes( + update + .finalized_header + .clone() + .body_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + ); + + log::debug!("valid merkle branch for execution_payload_branch"); + if !is_merkle_branch_valid { + log::debug!("invalid merkle branch for execution_payload_branch"); + //Err(Error::InvalidMerkleBranch)?; + } + + if let Some(sync_committee_update) = update.sync_committee_update.clone() { + if update_attested_period == state_period && + sync_committee_update.next_sync_committee != + trusted_state.next_sync_committee.clone() + { + log::debug!("invalid update for sync committee update"); + //rr(Error::InvalidUpdate)? + } + + let next_sync_committee_branch = sync_committee_update + .next_sync_committee_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &Node::from_bytes( + hash_tree_root(sync_committee_update.next_sync_committee) + .map_err(|_| Error::MerkleizationError)? + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize, + &Node::from_bytes( + update + .attested_header + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + ); + + log::debug!("valid merkle branch for sync committee {}", is_merkle_branch_valid); + if !is_merkle_branch_valid { + log::debug!("invalid merkle branch for sync committee"); + // Err(Error::InvalidMerkleBranch)?; + } + } + + // verify the ancestry proofs + for ancestor in update.ancestor_blocks { + match ancestor.ancestry_proof { + AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => { + let block_header_branch = block_roots_proof + .block_header_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let block_roots_root = calculate_merkle_root( + &Node::from_bytes( + hash_tree_root(ancestor.header.clone()) + .map_err(|_| Error::MerkleizationError)? + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + &*block_header_branch, + &GeneralizedIndex(block_roots_proof.block_header_index as usize), + ); + + let block_roots_branch_node = block_roots_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &block_roots_root, + block_roots_branch_node.iter(), + BLOCK_ROOTS_INDEX.floor_log2() as usize, + BLOCK_ROOTS_INDEX as usize, + &Node::from_bytes( + update + .finalized_header + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + ); + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + }, + AncestryProof::HistoricalRoots { + block_roots_proof, + historical_batch_proof, + historical_roots_proof, + historical_roots_index, + historical_roots_branch, + } => { + let block_header_branch = block_roots_proof + .block_header_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let block_roots_root = calculate_merkle_root( + &Node::from_bytes( + hash_tree_root(ancestor.header.clone()) + .map_err(|_| Error::MerkleizationError)? + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + &block_header_branch, + &GeneralizedIndex(block_roots_proof.block_header_index as usize), + ); + + let historical_batch_proof_nodes = historical_batch_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let historical_batch_root = calculate_merkle_root( + &block_roots_root, + &historical_batch_proof_nodes, + &GeneralizedIndex(HISTORICAL_BATCH_BLOCK_ROOTS_INDEX as usize), + ); + + let historical_roots_proof_nodes = historical_roots_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let historical_roots_root = calculate_merkle_root( + &historical_batch_root, + &historical_roots_proof_nodes, + &GeneralizedIndex(historical_roots_index as usize), + ); + + let historical_roots_branch_nodes = historical_roots_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &historical_roots_root, + historical_roots_branch_nodes.iter(), + HISTORICAL_ROOTS_INDEX.floor_log2() as usize, + get_subtree_index(HISTORICAL_ROOTS_INDEX) as usize, + &Node::from_bytes( + update + .finalized_header + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + }, + }; + + // verify the associated execution paylaod header. + let execution_payload = ancestor.execution_payload; + let multi_proof = execution_payload + .multi_proof + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes( + execution_payload + .state_root + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + Node::from_bytes( + hash_tree_root(execution_payload.block_number) + .map_err(|_| Error::MerkleizationError)? + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), + ], + &multi_proof, + &[ + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + ], + ); + + let execution_payload_branch = execution_payload + .execution_payload_branch + .iter() + .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) + .collect::>(); + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + EXECUTION_PAYLOAD_INDEX as usize, + &Node::from_bytes( + ancestor.header.body_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, + ), + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch)?; + } + } + + let new_light_client_state = if let Some(sync_committee_update) = update.sync_committee_update { + LightClientState { + finalized_header: update.finalized_header, + current_sync_committee: trusted_state.next_sync_committee, + next_sync_committee: sync_committee_update.next_sync_committee, + } + } else { + LightClientState { finalized_header: update.finalized_header, ..trusted_state } + }; + + Ok(new_light_client_state) +} From 76b92a8a5578fe3ae39d085fa1078e0cf79efaf1 Mon Sep 17 00:00:00 2001 From: David Salami Date: Tue, 21 Feb 2023 21:35:56 +0100 Subject: [PATCH 052/182] finalized header test --- prover/src/lib.rs | 2 +- prover/src/test.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 219439c79..6782cdcaf 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -290,7 +290,7 @@ fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result Date: Tue, 21 Feb 2023 22:23:11 +0100 Subject: [PATCH 053/182] test finality proof --- prover/src/lib.rs | 8 -------- prover/src/test.rs | 44 +++++++++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 6782cdcaf..47ac6e725 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -29,14 +29,6 @@ use ethereum_consensus::{ }, primitives::{Bytes32, Slot, ValidatorIndex}, }; -use light_client_primitives::{ - types::{ - BlockRootsProof, ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, - EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, - NEXT_SYNC_COMMITTEE_INDEX, - }, - util::get_subtree_index, -}; use ssz_rs::{GeneralizedIndex, List, Node, Vector}; use sync_committee_primitives::{ types::{ diff --git a/prover/src/test.rs b/prover/src/test.rs index 3fc50f721..eaf8a5721 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -1,12 +1,12 @@ use super::*; use base2::Base2; use ethereum_consensus::altair::NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2; -use light_client_primitives::{ +use sync_committee_primitives::{ types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, util::compute_sync_committee_period_at_slot, }; -use light_client_verifier::light_client::EthLightClient; -use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; +//use sync_committee_verifier::light_client::EthLightClient; +use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized, get_generalized_index, SszVariableOrIndex}; use std::{thread, time::Duration}; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; @@ -104,6 +104,25 @@ async fn fetch_finality_checkpoints_work() { assert!(finality_checkpoint.is_ok()); } +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_finalized_header() { + let node_url: String = "http://localhost:3500".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + let mut state = sync_committee_prover.fetch_beacon_state("finalized").await.unwrap(); + + let generalized_index = get_generalized_index(&state, &[SszVariableOrIndex::Name("finalized_checkpoint")]); + dbg!(generalized_index); + let proof = ssz_rs::generate_proof(state.clone(), &vec![generalized_index]); + + + let leaves = vec![Node::from_bytes(state.finalized_checkpoint.hash_tree_root().unwrap().as_ref().try_into().unwrap())]; + let root = calculate_multi_merkle_root(&leaves, &proof.unwrap(), &[GeneralizedIndex(generalized_index)]); + assert_eq!(root, state.hash_tree_root().unwrap()); +} + +/* #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] @@ -252,6 +271,7 @@ async fn test_finality_proof() { assert!(is_merkle_branch_valid, "{}", true); } + #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] @@ -342,7 +362,7 @@ async fn test_sync_committee_proof() { .collect::>(); let is_merkle_branch_valid = is_valid_merkle_branch( &Node::from_bytes( - light_client_primitives::util::hash_tree_root(sync_committee.clone()) + sync_committee_primitives::util::hash_tree_root(sync_committee.clone()) .unwrap() .as_ref() .try_into() @@ -358,20 +378,6 @@ async fn test_sync_committee_proof() { assert!(is_merkle_branch_valid, "{}", true); } -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_finalized_header() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let state = sync_committee_prover.fetch_beacon_state("finalized").await.unwrap(); - let proof = ssz_rs::generate_proof(state, vec![FINALIZED_ROOT_INDEX]); - - let leaves = vec![Node::from_bytes(state.finalized_checkpoint.hash_tree_root().unwrap().as_ref().try_into().unwrap())]; - let root = calculate_multi_merkle_root(&leaves, &proof, vec![FINALIZED_ROOT_INDEX]); - assert_eq!(root, state.hash_tree_root()); -} - // use tokio interval(should run every 13 minutes) // every 13 minutes, fetch latest finalized block // then prove the execution payload @@ -521,4 +527,4 @@ async fn test_prover() { client_state.finalized_header.slot ); } -} +}*/ From b04323e93c6f88faacbbf09d27c3df4e245c6e8e Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 22 Feb 2023 14:10:42 +0100 Subject: [PATCH 054/182] update sync committee test --- Cargo.lock | 73 +++--- Cargo.toml | 6 + primitives/Cargo.toml | 4 +- primitives/src/types.rs | 25 +- prover/Cargo.toml | 4 +- prover/src/lib.rs | 58 +++-- prover/src/test.rs | 531 +++++++++++++++------------------------- verifier/Cargo.toml | 4 +- verifier/src/lib.rs | 129 ++++------ 9 files changed, 344 insertions(+), 490 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4980c81fe..cc1219cb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,9 +41,9 @@ source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee67 [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "arrayref" @@ -59,19 +59,20 @@ checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ "proc-macro2", "quote", @@ -374,7 +375,7 @@ dependencies = [ [[package]] name = "ethereum-consensus" version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#9862cab0797fe80c27f435b5b0313f32c24708da" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#8be43ea5768297f83acfc22651121154fb1a5874" dependencies = [ "async-stream", "bs58", @@ -399,9 +400,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -591,9 +592,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -625,9 +626,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -822,14 +823,14 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -937,9 +938,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -1091,9 +1092,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -1305,9 +1306,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -1362,9 +1363,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -1381,9 +1382,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -1417,7 +1418,7 @@ dependencies = [ [[package]] name = "ssz-rs" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=dami/seun-multi-proofs#24f04b8daefc110aff6dd8f20778f3da95a9cdf7" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f85f088552ee45c4c6080eede3fb57d47bff92e3" dependencies = [ "as-any", "bitvec", @@ -1433,7 +1434,7 @@ dependencies = [ [[package]] name = "ssz-rs-derive" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=dami/seun-multi-proofs#24f04b8daefc110aff6dd8f20778f3da95a9cdf7" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f85f088552ee45c4c6080eede3fb57d47bff92e3" dependencies = [ "proc-macro2", "quote", @@ -1568,9 +1569,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" @@ -1605,9 +1606,9 @@ dependencies = [ [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -1615,9 +1616,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite", @@ -1626,9 +1627,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", diff --git a/Cargo.toml b/Cargo.toml index 37dfd2d6a..29417d48b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,9 @@ members = [ "primitives", "prover" ] + +[patch."https://github.com/ralexstokes/ethereum-consensus"] +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "dami/no-std-support" } + +[patch."https://github.com/ralexstokes/ssz-rs"] +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "seun/ssz-merkle-multi-proof-phase-1" } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index a9f5cdf96..3638636cd 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -7,6 +7,6 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version = "0.3.1", default-features=false} -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features=false, features=["serde"] } hex-literal = { package = "hex-literal", version = "0.3.3" } diff --git a/primitives/src/types.rs b/primitives/src/types.rs index dc115b6f8..87d4ada6a 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -1,21 +1,25 @@ use alloc::vec::Vec; +use base2::Base2; use ethereum_consensus::{ bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, domains::DomainType, - primitives::{Hash32, Slot}, + primitives::{Epoch, Hash32, Slot}, }; pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; -pub const FINALIZED_ROOT_INDEX: u64 = 105; +pub const FINALIZED_ROOT_INDEX: u64 = 52; pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; -pub const EXECUTION_PAYLOAD_INDEX: u64 = 25; +pub const EXECUTION_PAYLOAD_INDEX: u64 = 56; pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; pub const BLOCK_ROOTS_INDEX: u64 = 37; pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; pub const HISTORICAL_ROOTS_INDEX: u64 = 39; pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); +// pub const NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = NEXT_SYNC_COMMITTEE_INDEX.floor_log2() +// as usize; pub const FINALIZED_ROOT_INDEX_FLOOR_LOG_2: usize = FINALIZED_ROOT_INDEX.floor_log2() +// as usize; /// This holds the relevant data required to prove the state root in the execution payload. #[derive(Debug, Clone)] @@ -102,6 +106,16 @@ pub struct LightClientState { pub next_sync_committee: SyncCommittee, } +/// Minimum state required by the light client to validate new sync committee attestations +#[derive(Debug, Clone)] +pub struct FinalityProof { + /// Epoch that was finalized + pub finalized_epoch: Epoch, + /// the ssz merkle proof for the finalized checkpoint in the attested header, finalized headers + /// lag by 2 epochs. + pub finality_branch: Vec, +} + /// Data required to advance the state of the light client. #[derive(Debug, Clone)] pub struct LightClientUpdate { @@ -113,9 +127,8 @@ pub struct LightClientUpdate { pub finalized_header: BeaconBlockHeader, /// execution payload of the finalized header pub execution_payload: ExecutionPayloadProof, - /// the ssz merkle proof for this header in the attested header, finalized headers lag by 2 - /// epochs. - pub finality_branch: Vec, + /// Finalized header proof + pub finality_proof: FinalityProof, /// signature & participation bits pub sync_aggregate: SyncAggregate, /// slot at which signature was produced diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 513a81cca..f07ffedd3 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] sync-committee-primitives = { path= "../primitives", default-features = false } sync-committee-verifier = { path= "../verifier", default-features = false } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 47ac6e725..802a3f87a 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -27,14 +27,14 @@ use ethereum_consensus::{ MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }, - primitives::{Bytes32, Slot, ValidatorIndex}, + primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}, }; -use ssz_rs::{GeneralizedIndex, List, Node, Vector}; +use ssz_rs::{get_generalized_index, GeneralizedIndex, List, Node, SszVariableOrIndex, Vector}; use sync_committee_primitives::{ types::{ - BlockRootsProof, ExecutionPayloadProof, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, - EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, - NEXT_SYNC_COMMITTEE_INDEX, + BlockRootsProof, ExecutionPayloadProof, FinalityProof, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, }, util::get_subtree_index, }; @@ -218,12 +218,13 @@ impl SyncCommitteeProver { let validator_index: ValidatorIndex = i.parse().unwrap(); validators[validator_index].public_key.clone() }) - .collect::>(); + .collect::>(); let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector).unwrap(); let sync_committee = SyncCommittee:: { - public_keys: public_keys_vector, + public_keys: Vector::::try_from(public_keys_vector) + .unwrap(), aggregate_public_key, }; @@ -273,45 +274,50 @@ fn prove_beacon_block_values( Ok(proof) } -fn prove_execution_payload(block: BeaconBlockType) -> anyhow::Result { +fn prove_execution_payload(beacon_state: BeaconStateType) -> anyhow::Result { let indices = [ EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, ]; // generate multi proofs - let multi_proof = - ssz_rs::generate_proof(block.body.execution_payload.clone(), indices.as_slice())?; - - let execution_payload_index = [EXECUTION_PAYLOAD_INDEX as usize]; - let execution_payload_branch = - ssz_rs::generate_proof(block.body.clone(), execution_payload_index.as_slice())?; + let multi_proof = ssz_rs::generate_proof( + beacon_state.latest_execution_payload_header.clone(), + indices.as_slice(), + )?; Ok(ExecutionPayloadProof { - state_root: block.body.execution_payload.state_root, - block_number: block.body.execution_payload.block_number, + state_root: beacon_state.latest_execution_payload_header.state_root.clone(), + block_number: beacon_state.latest_execution_payload_header.block_number, multi_proof: multi_proof .into_iter() .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect(), - execution_payload_branch: execution_payload_branch - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), + execution_payload_branch: ssz_rs::generate_proof( + beacon_state, + &[EXECUTION_PAYLOAD_INDEX as usize], + )? + .into_iter() + .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) + .collect(), }) } fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result> { - let indices = vec![NEXT_SYNC_COMMITTEE_INDEX as usize]; - let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; - + let proof = ssz_rs::generate_proof(state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } -fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { - let indices = [FINALIZED_ROOT_INDEX as usize]; //vec![get_subtree_index(FINALIZED_ROOT_INDEX) as usize]; +fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result { + let indices = [FINALIZED_ROOT_INDEX as usize]; let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; - Ok(proof) + Ok(FinalityProof { + finalized_epoch: state.finalized_checkpoint.epoch, + finality_branch: proof + .into_iter() + .map(|node| Hash32::try_from(node.as_ref()).expect("Node is always a 32 byte slice")) + .collect(), + }) } // function that generates block roots proof diff --git a/prover/src/test.rs b/prover/src/test.rs index eaf8a5721..ddd79bdb3 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -5,8 +5,11 @@ use sync_committee_primitives::{ types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, util::compute_sync_committee_period_at_slot, }; -//use sync_committee_verifier::light_client::EthLightClient; -use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized, get_generalized_index, SszVariableOrIndex}; + +use ssz_rs::{ + calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, + Merkleized, SszVariableOrIndex, +}; use std::{thread, time::Duration}; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; @@ -14,8 +17,9 @@ use tokio_stream::{wrappers::IntervalStream, StreamExt}; #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn fetch_block_header_works() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let mut block_header = sync_committee_prover.fetch_header("1000").await; while block_header.is_err() { @@ -28,8 +32,9 @@ async fn fetch_block_header_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn fetch_block_works() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let block = sync_committee_prover.fetch_block("100").await; assert!(block.is_ok()); @@ -38,8 +43,9 @@ async fn fetch_block_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn fetch_sync_committee_works() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let block = sync_committee_prover.fetch_sync_committee("117").await; assert!(block.is_ok()); @@ -48,8 +54,9 @@ async fn fetch_sync_committee_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn fetch_validator_works() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let validator = sync_committee_prover.fetch_validator("2561", "48").await; assert!(validator.is_ok()); @@ -58,8 +65,9 @@ async fn fetch_validator_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn fetch_processed_sync_committee_works() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let validator = sync_committee_prover.fetch_processed_sync_committee("2561").await; assert!(validator.is_ok()); @@ -68,8 +76,9 @@ async fn fetch_processed_sync_committee_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn fetch_beacon_state_works() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let beacon_state = sync_committee_prover.fetch_beacon_state("genesis").await; assert!(beacon_state.is_ok()); @@ -78,8 +87,9 @@ async fn fetch_beacon_state_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] +#[ignore] async fn state_root_and_block_header_root_matches() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let beacon_state = sync_committee_prover.fetch_beacon_state("100").await; assert!(beacon_state.is_ok()); @@ -98,7 +108,7 @@ async fn state_root_and_block_header_root_matches() { #[allow(non_snake_case)] #[actix_rt::test] async fn fetch_finality_checkpoints_work() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; assert!(finality_checkpoint.is_ok()); @@ -108,34 +118,41 @@ async fn fetch_finality_checkpoints_work() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_finalized_header() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let mut state = sync_committee_prover.fetch_beacon_state("finalized").await.unwrap(); - - let generalized_index = get_generalized_index(&state, &[SszVariableOrIndex::Name("finalized_checkpoint")]); - dbg!(generalized_index); - let proof = ssz_rs::generate_proof(state.clone(), &vec![generalized_index]); + let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let proof = ssz_rs::generate_proof(state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); - let leaves = vec![Node::from_bytes(state.finalized_checkpoint.hash_tree_root().unwrap().as_ref().try_into().unwrap())]; - let root = calculate_multi_merkle_root(&leaves, &proof.unwrap(), &[GeneralizedIndex(generalized_index)]); + let leaves = vec![Node::from_bytes( + state + .finalized_checkpoint + .hash_tree_root() + .unwrap() + .as_ref() + .try_into() + .unwrap(), + )]; + let root = calculate_multi_merkle_root( + &leaves, + &proof.unwrap(), + &[GeneralizedIndex(FINALIZED_ROOT_INDEX as usize)], + ); assert_eq!(root, state.hash_tree_root().unwrap()); } -/* #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] async fn test_execution_payload_proof() { - let node_url: String = "http://localhost:3500".to_string(); + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block_id = "finalized"; - let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); - - let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); + let finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let block_id = finalized_state.slot.to_string(); + let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); // verify the associated execution header of the finalized beacon header. let mut execution_payload = execution_payload_proof.clone(); @@ -156,12 +173,11 @@ async fn test_execution_payload_proof() { ], ); - println!("execution_payload_root {:?}", execution_payload_root); - - let execution_payload_hash_tree_root = - finalized_block.body.execution_payload.clone().hash_tree_root().unwrap(); - - println!("execution_payload_hash_tree_root {:?}", execution_payload_hash_tree_root); + let execution_payload_hash_tree_root = finalized_state + .latest_execution_payload_header + .clone() + .hash_tree_root() + .unwrap(); assert_eq!(execution_payload_root, execution_payload_hash_tree_root); @@ -176,143 +192,24 @@ async fn test_execution_payload_proof() { execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0, - &Node::from_bytes(finalized_header.clone().body_root.as_ref().try_into().unwrap()), - ); - - - - assert_eq!(is_merkle_branch_valid, true); -} - -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_finality_proof() { - let node_url: String = "http://localhost:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - - let block_id = "finalized"; - let finalized_block = sync_committee_prover.fetch_block(block_id).await.unwrap(); - - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); - - let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) - .await - .unwrap(); - - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_header_slot.to_string().as_str()) - .await - .unwrap(); - - let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); - - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - // purposely for waiting - //println!("sleeping"); - thread::sleep(time::Duration::from_secs(5)); - - let mut attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - - while attested_header.is_err() { - println!("I am running till i am ok. lol {}", &block_id); - attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - } - - let attested_header = attested_header.unwrap(); - - let finality_branch_proof = finality_branch_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); - - // verify the associated execution header of the finalized beacon header. - - // Verify that the `finality_branch` confirms `finalized_header` - // to match the finalized checkpoint root saved in the state of `attested_header`. - // Note that the genesis finalized checkpoint root is represented as a zero hash. - let finalized_root = &Node::from_bytes( - light_client_primitives::util::hash_tree_root(finalized_header.clone()) - .unwrap() - .as_ref() - .try_into() - .unwrap(), + &Node::from_bytes(finalized_header.clone().state_root.as_ref().try_into().unwrap()), ); - let branch = finality_branch_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - println!("finalized_root {:?}", finalized_root.clone()); - - let finality_hash_tree_root = finalized_block.clone().hash_tree_root().unwrap(); - - assert_eq!(finalized_root, &finality_hash_tree_root); - - println!("finalized_root {:?}", finality_hash_tree_root); - let is_merkle_branch_valid = is_valid_merkle_branch( - finalized_root, - branch.iter(), - FINALIZED_ROOT_INDEX.floor_log2() as usize, - get_subtree_index(FINALIZED_ROOT_INDEX) as usize, - &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), - ); - - println!("is_merkle_branch_valid {:?}", is_merkle_branch_valid.clone()); - - assert!(is_merkle_branch_valid, "{}", true); + assert!(is_merkle_branch_valid); } - #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -async fn test_sync_committee_proof() { - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); - - let node_url: String = "http://localhost:3500".to_string(); +async fn test_sync_committee_update_proof() { + let node_url: String = "http://localhost:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); - let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); - dbg!(&finality_checkpoint.root); - - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - dbg!(&block_id); - - let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - - let state = sync_committee_prover - .fetch_beacon_state(&block_header.slot.to_string()) - .await - .unwrap(); - - let block_id = "head"; - let mut finalized_block = sync_committee_prover.fetch_block(block_id).await; - - while finalized_block.is_err() { - println!("I am running finalized block till i am ok. lol {}", &block_id); - finalized_block = sync_committee_prover.fetch_block(block_id).await; - } - - let finalized_block = finalized_block.unwrap(); - - let finalized_header = sync_committee_prover.fetch_header(block_id).await.unwrap(); + let finalized_header = sync_committee_prover.fetch_header("head").await.unwrap(); + let block_id = finalized_header.slot.to_string(); let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) + .fetch_beacon_state(&finalized_header.slot.to_string()) .await .unwrap(); @@ -322,27 +219,10 @@ async fn test_sync_committee_proof() { .into_iter() .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect::>(); - let mut sync_committee = sync_committee_prover - .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) - .await; - - while sync_committee.is_err() { - println!("I am fetching sync committee till i am ok. lol {}", &block_id); - sync_committee = sync_committee_prover - .fetch_processed_sync_committee(finalized_header.slot.to_string().as_str()) - .await; - } - - let sync_committee = sync_committee.unwrap(); + let mut sync_committee = finalized_state.next_sync_committee; let calculated_finalized_root = calculate_multi_merkle_root( - &[Node::from_bytes( - light_client_primitives::util::hash_tree_root(sync_committee.clone()) - .unwrap() - .as_ref() - .try_into() - .unwrap(), - )], + &[Node::from_bytes(sync_committee.hash_tree_root().unwrap().as_ref().try_into().unwrap())], &sync_committee_proof .iter() .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) @@ -350,32 +230,21 @@ async fn test_sync_committee_proof() { &[GeneralizedIndex(NEXT_SYNC_COMMITTEE_INDEX as usize)], ); - let hash_tree_root = finalized_state.clone().hash_tree_root().unwrap(); - - println!("calculated_finalized_root {:?}", calculated_finalized_root); - println!("finalized_state_root {:?}", finalized_header.state_root); - println!("hash_tree_root {:?}", hash_tree_root); + assert_eq!(calculated_finalized_root.as_bytes(), finalized_header.state_root.as_bytes()); let next_sync_committee_branch = sync_committee_proof .iter() .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) .collect::>(); let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes( - sync_committee_primitives::util::hash_tree_root(sync_committee.clone()) - .unwrap() - .as_ref() - .try_into() - .unwrap(), - ), + &Node::from_bytes(sync_committee.hash_tree_root().unwrap().as_ref().try_into().unwrap()), next_sync_committee_branch.iter(), NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, NEXT_SYNC_COMMITTEE_INDEX as usize, &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), ); - println!("valid merkle branch for sync committee {}", is_merkle_branch_valid); - assert!(is_merkle_branch_valid, "{}", true); + assert!(is_merkle_branch_valid); } // use tokio interval(should run every 13 minutes) @@ -387,144 +256,144 @@ async fn test_sync_committee_proof() { // to prove sync comnmittee update, calculate state_period and the update_attested_period // ensure they are the same, and then prove sync committee -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_prover() { - // In test config an epoch is 6 slots and we expect finalization every two epochs, - // a slot is 12 seconds so that brings us to 144 seconds - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); - - let node_url: String = "http://127.0.0.1:3500".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - - let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); - dbg!(&finality_checkpoint.root); - - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - dbg!(&block_id); - - let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - - let state = sync_committee_prover - .fetch_beacon_state(&block_header.slot.to_string()) - .await - .unwrap(); - - let mut client_state = LightClientState { - finalized_header: block_header.clone(), - current_sync_committee: state.current_sync_committee, - next_sync_committee: state.next_sync_committee, - }; - - while let Some(_ts) = stream.next().await { - let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); - dbg!(&finality_checkpoint.root); - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - dbg!(&block_id); - let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); - - if finalized_block.slot <= client_state.finalized_header.slot { - println!("finalized_block slot is {}", &finalized_block.slot); - println!("finalized_header slot is {}", &client_state.finalized_header.slot); - continue - } - - let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - - let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); - - let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_block.slot.to_string().as_str()) - .await - .unwrap(); - - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_header_slot.to_string().as_str()) - .await - .unwrap(); - - let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); - let finality_branch_proof = finality_branch_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); - - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - // purposely for waiting - //println!("sleeping"); - thread::sleep(time::Duration::from_secs(5)); - - let mut attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - - while attested_header.is_err() { - println!("I am running till i am ok. lol {}", &block_id); - attested_header = sync_committee_prover - .fetch_header(attested_header_slot.to_string().as_str()) - .await; - } - - let attested_header = attested_header.unwrap(); - - let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); - - let sync_committee_update = if state_period == attested_header_slot { - let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); - - let sync_committee_proof = sync_committee_proof - .into_iter() - .map(|node| { - Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") - }) - .collect::>(); - - let sync_committee = sync_committee_prover - .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) - .await - .unwrap(); - - Some(SyncCommitteeUpdate { - next_sync_committee: sync_committee, - next_sync_committee_branch: sync_committee_proof, - }) - } else { - None - }; - - // construct light client - let light_client_update = LightClientUpdate { - attested_header, - sync_committee_update, - finalized_header, - execution_payload: execution_payload_proof, - finality_branch: finality_branch_proof, - sync_aggregate: finalized_block.body.sync_aggregate, - signature_slot: attested_header_slot, - ancestor_blocks: vec![], - }; - - client_state = EthLightClient::verify_sync_committee_attestation( - client_state.clone(), - light_client_update, - ) - .unwrap(); - println!( - "Sucessfully verified Ethereum block at slot {:?}", - client_state.finalized_header.slot - ); - } -}*/ +// #[cfg(test)] +// #[allow(non_snake_case)] +// #[actix_rt::test] +// async fn test_prover() { +// // In test config an epoch is 6 slots and we expect finalization every two epochs, +// // a slot is 12 seconds so that brings us to 144 seconds +// let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); +// +// let node_url: String = "http://127.0.0.1:5052".to_string(); +// let sync_committee_prover = SyncCommitteeProver::new(node_url); +// +// let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); +// dbg!(&finality_checkpoint.root); +// +// let block_id = { +// let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); +// block_id.insert_str(0, "0x"); +// block_id +// }; +// +// dbg!(&block_id); +// +// let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); +// +// let state = sync_committee_prover +// .fetch_beacon_state(&block_header.slot.to_string()) +// .await +// .unwrap(); +// +// let mut client_state = LightClientState { +// finalized_header: block_header.clone(), +// current_sync_committee: state.current_sync_committee, +// next_sync_committee: state.next_sync_committee, +// }; +// +// while let Some(_ts) = stream.next().await { +// let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); +// dbg!(&finality_checkpoint.root); +// let block_id = { +// let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); +// block_id.insert_str(0, "0x"); +// block_id +// }; +// +// dbg!(&block_id); +// let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); +// +// if finalized_block.slot <= client_state.finalized_header.slot { +// println!("finalized_block slot is {}", &finalized_block.slot); +// println!("finalized_header slot is {}", &client_state.finalized_header.slot); +// continue +// } +// +// let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); +// +// let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); +// +// let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); +// let finalized_state = sync_committee_prover +// .fetch_beacon_state(finalized_block.slot.to_string().as_str()) +// .await +// .unwrap(); +// +// let attested_state = sync_committee_prover +// .fetch_beacon_state(attested_header_slot.to_string().as_str()) +// .await +// .unwrap(); +// +// let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); +// let finality_branch_proof = finality_branch_proof +// .into_iter() +// .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) +// .collect::>(); +// +// let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); +// +// // purposely for waiting +// //println!("sleeping"); +// thread::sleep(time::Duration::from_secs(5)); +// +// let mut attested_header = sync_committee_prover +// .fetch_header(attested_header_slot.to_string().as_str()) +// .await; +// +// while attested_header.is_err() { +// println!("I am running till i am ok. lol {}", &block_id); +// attested_header = sync_committee_prover +// .fetch_header(attested_header_slot.to_string().as_str()) +// .await; +// } +// +// let attested_header = attested_header.unwrap(); +// +// let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); +// +// let sync_committee_update = if state_period == attested_header_slot { +// let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); +// +// let sync_committee_proof = sync_committee_proof +// .into_iter() +// .map(|node| { +// Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") +// }) +// .collect::>(); +// +// let sync_committee = sync_committee_prover +// .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) +// .await +// .unwrap(); +// +// Some(SyncCommitteeUpdate { +// next_sync_committee: sync_committee, +// next_sync_committee_branch: sync_committee_proof, +// }) +// } else { +// None +// }; +// +// // construct light client +// let light_client_update = LightClientUpdate { +// attested_header, +// sync_committee_update, +// finalized_header, +// execution_payload: execution_payload_proof, +// finality_branch: finality_branch_proof, +// sync_aggregate: finalized_block.body.sync_aggregate, +// signature_slot: attested_header_slot, +// ancestor_blocks: vec![], +// }; +// +// client_state = EthLightClient::verify_sync_committee_attestation( +// client_state.clone(), +// light_client_update, +// ) +// .unwrap(); +// println!( +// "Sucessfully verified Ethereum block at slot {:?}", +// client_state.finalized_header.slot +// ); +// } +// } diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 4fa8e4f74..8b68a4014 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version="0.2.2", default-features = false } sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch="dami/no-std-support" } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch="dami/seun-multi-proofs", default-features=false, features=["serde", "std"] } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features=false, features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } log = "0.4.17" \ No newline at end of file diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 9c5f0e35f..8bce18650 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -10,11 +10,7 @@ use alloc::vec::Vec; use base2::Base2; use core::{borrow::Borrow, fmt::Display}; use ethereum_consensus::{ - altair::{ - mainnet::SYNC_COMMITTEE_SIZE, FINALIZED_ROOT_INDEX_FLOOR_LOG_2, - NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, - }, - bellatrix::compute_domain, + bellatrix::{compute_domain, mainnet::SYNC_COMMITTEE_SIZE, Checkpoint}, primitives::Root, signing::compute_signing_root, state_transition::Context, @@ -35,6 +31,8 @@ use sync_committee_primitives::{ get_subtree_index, hash_tree_root, }, }; +// use sync_committee_primitives::types::{FINALIZED_ROOT_INDEX_FLOOR_LOG_2, +// NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2}; pub type LightClientState = sync_committee_primitives::types::LightClientState; pub type LightClientUpdate = @@ -45,23 +43,22 @@ pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { - if update.finality_branch.len() != FINALIZED_ROOT_INDEX_FLOOR_LOG_2 as usize && + if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && update.sync_committee_update.is_some() && update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() != - NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 as usize + NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize { log::debug!("Invalid update "); - log::debug!("update finality branch length {} ", update.finality_branch.len()); - log::debug!("FINALIZED_ROOT_INDEX_FLOOR_LOG_2 {}", FINALIZED_ROOT_INDEX_FLOOR_LOG_2); log::debug!( - "update next sync committee branch length {} ", - update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() + "update finality branch length {} ", + update.finality_proof.finality_branch.len() ); log::debug!( - "NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 {}", - NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2 + "update next sync committee branch length {} ", + update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() ); - //Err(Error::InvalidUpdate)? + + Err(Error::InvalidUpdate)? } // Verify sync committee has super majority participants @@ -71,7 +68,7 @@ pub fn verify_sync_committee_attestation( log::debug!("SyncCommitteeParticipantsTooLow "); log::debug!("sync_aggregate_participants {} ", { sync_aggregate_participants * 3 }); log::debug!("sync_committee_bits {}", { sync_committee_bits.clone().len() * 2 }); - //Err(Error::SyncCommitteeParticipantsTooLow)? + Err(Error::SyncCommitteeParticipantsTooLow)? } // Verify update does not skip a sync committee period @@ -89,7 +86,7 @@ pub fn verify_sync_committee_attestation( update.attested_header.slot, update.finalized_header.slot ); - //Err(Error::InvalidUpdate)? + Err(Error::InvalidUpdate)? } let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); @@ -98,7 +95,7 @@ pub fn verify_sync_committee_attestation( log::debug!("invalid update"); log::debug!("state_period is {}", state_period); log::debug!("update_signature_period is {}", update_signature_period); - //Err(Error::InvalidUpdate)? + Err(Error::InvalidUpdate)? } // Verify update is relevant @@ -149,39 +146,34 @@ pub fn verify_sync_committee_attestation( // Verify that the `finality_branch` confirms `finalized_header` // to match the finalized checkpoint root saved in the state of `attested_header`. // Note that the genesis finalized checkpoint root is represented as a zero hash. - let finalized_root = &Node::from_bytes( - hash_tree_root(update.finalized_header.clone()) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() + let mut finalized_checkpoint = Checkpoint { + epoch: update.finality_proof.finalized_epoch, + root: update + .finalized_header + .clone() + .hash_tree_root() .map_err(|_| Error::InvalidRoot)?, - ); + }; let branch = update + .finality_proof .finality_branch .iter() .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) .collect::>(); let is_merkle_branch_valid = is_valid_merkle_branch( - finalized_root, + &finalized_checkpoint.hash_tree_root().map_err(|_| Error::InvalidRoot)?, branch.iter(), FINALIZED_ROOT_INDEX.floor_log2() as usize, FINALIZED_ROOT_INDEX as usize, - &Node::from_bytes( - update - .attested_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &update.attested_header.state_root, ); log::debug!("valid merkle branch for finalized_root {}", is_merkle_branch_valid); if !is_merkle_branch_valid { log::debug!("invalid merkle branch for finalized root"); - //Err(Error::InvalidMerkleBranch)?; + Err(Error::InvalidMerkleBranch)?; } // verify the associated execution header of the finalized beacon header. @@ -223,30 +215,22 @@ pub fn verify_sync_committee_attestation( execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, EXECUTION_PAYLOAD_INDEX as usize, - &Node::from_bytes( - update - .finalized_header - .clone() - .body_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &update.finalized_header.state_root, ); log::debug!("valid merkle branch for execution_payload_branch"); if !is_merkle_branch_valid { log::debug!("invalid merkle branch for execution_payload_branch"); - //Err(Error::InvalidMerkleBranch)?; + Err(Error::InvalidMerkleBranch)?; } - if let Some(sync_committee_update) = update.sync_committee_update.clone() { + if let Some(mut sync_committee_update) = update.sync_committee_update.clone() { if update_attested_period == state_period && sync_committee_update.next_sync_committee != trusted_state.next_sync_committee.clone() { log::debug!("invalid update for sync committee update"); - //rr(Error::InvalidUpdate)? + Err(Error::InvalidUpdate)? } let next_sync_committee_branch = sync_committee_update @@ -255,35 +239,25 @@ pub fn verify_sync_committee_attestation( .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) .collect::>(); let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes( - hash_tree_root(sync_committee_update.next_sync_committee) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &sync_committee_update + .next_sync_committee + .hash_tree_root() + .map_err(|_| Error::MerkleizationError)?, next_sync_committee_branch.iter(), NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize, - &Node::from_bytes( - update - .attested_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &update.attested_header.state_root, ); log::debug!("valid merkle branch for sync committee {}", is_merkle_branch_valid); if !is_merkle_branch_valid { log::debug!("invalid merkle branch for sync committee"); - // Err(Error::InvalidMerkleBranch)?; + Err(Error::InvalidMerkleBranch)?; } } // verify the ancestry proofs - for ancestor in update.ancestor_blocks { + for mut ancestor in update.ancestor_blocks { match ancestor.ancestry_proof { AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => { let block_header_branch = block_roots_proof @@ -293,13 +267,7 @@ pub fn verify_sync_committee_attestation( .collect::>(); let block_roots_root = calculate_merkle_root( - &Node::from_bytes( - hash_tree_root(ancestor.header.clone()) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &ancestor.header.hash_tree_root().map_err(|_| Error::MerkleizationError)?, &*block_header_branch, &GeneralizedIndex(block_roots_proof.block_header_index as usize), ); @@ -314,14 +282,7 @@ pub fn verify_sync_committee_attestation( block_roots_branch_node.iter(), BLOCK_ROOTS_INDEX.floor_log2() as usize, BLOCK_ROOTS_INDEX as usize, - &Node::from_bytes( - update - .finalized_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &update.finalized_header.state_root, ); if !is_merkle_branch_valid { Err(Error::InvalidMerkleBranch)?; @@ -340,13 +301,11 @@ pub fn verify_sync_committee_attestation( .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) .collect::>(); let block_roots_root = calculate_merkle_root( - &Node::from_bytes( - hash_tree_root(ancestor.header.clone()) - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + &ancestor + .header + .clone() + .hash_tree_root() + .map_err(|_| Error::MerkleizationError)?, &block_header_branch, &GeneralizedIndex(block_roots_proof.block_header_index as usize), ); @@ -438,7 +397,7 @@ pub fn verify_sync_committee_attestation( EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, EXECUTION_PAYLOAD_INDEX as usize, &Node::from_bytes( - ancestor.header.body_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, + ancestor.header.state_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, ), ); From 790971699bd8fb51f23c883b86a288a840419db2 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 22 Feb 2023 22:00:14 +0100 Subject: [PATCH 055/182] refactor integration test --- primitives/src/types.rs | 2 +- prover/src/lib.rs | 96 ++++--- .../responses/finality_checkpoint_response.rs | 8 +- prover/src/test.rs | 264 ++++++++---------- verifier/src/lib.rs | 2 - 5 files changed, 174 insertions(+), 198 deletions(-) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 87d4ada6a..b292272c7 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -13,8 +13,8 @@ pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; pub const EXECUTION_PAYLOAD_INDEX: u64 = 56; pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; pub const BLOCK_ROOTS_INDEX: u64 = 37; -pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 0; pub const HISTORICAL_ROOTS_INDEX: u64 = 39; +pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); // pub const NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = NEXT_SYNC_COMMITTEE_INDEX.floor_log2() diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 802a3f87a..74d7223aa 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -14,7 +14,13 @@ use ethereum_consensus::{ }; use reqwest::Client; -use crate::{responses::sync_committee_response::NodeSyncCommittee, routes::*}; +use crate::{ + responses::{ + finality_checkpoint_response::FinalityCheckpoint, + sync_committee_response::NodeSyncCommittee, + }, + routes::*, +}; use ethereum_consensus::{ bellatrix::mainnet::{ BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, @@ -24,19 +30,21 @@ use ethereum_consensus::{ phase0::mainnet::{ EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, - MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, + MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }, primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}, }; -use ssz_rs::{get_generalized_index, GeneralizedIndex, List, Node, SszVariableOrIndex, Vector}; +use ssz_rs::{ + get_generalized_index, GeneralizedIndex, List, Merkleized, Node, SszVariableOrIndex, Vector, +}; use sync_committee_primitives::{ types::{ - BlockRootsProof, ExecutionPayloadProof, FinalityProof, + AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, BLOCK_ROOTS_INDEX, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, }, - util::get_subtree_index, + util::{compute_epoch_at_slot, get_subtree_index}, }; type BeaconBlockType = BeaconBlock< @@ -94,13 +102,13 @@ impl SyncCommitteeProver { SyncCommitteeProver { node_url, client } } - pub async fn fetch_finalized_checkpoint(&self) -> Result { + pub async fn fetch_finalized_checkpoint(&self) -> Result { let full_url = self.generate_route(&finality_checkpoints("head")); let response = self.client.get(full_url).send().await?; let response_data = response.json::().await?; - Ok(response_data.data.finalized) + Ok(response_data.data) } pub async fn fetch_header(&self, block_id: &str) -> Result { @@ -258,22 +266,6 @@ fn get_attestation_slots_for_finalized_header( attested_slot } -fn prove_beacon_state_values( - data: BeaconStateType, - indices: &[usize], -) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(data, indices)?; - Ok(proof) -} - -fn prove_beacon_block_values( - data: BeaconBlockType, - indices: &[usize], -) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(data, indices)?; - Ok(proof) -} - fn prove_execution_payload(beacon_state: BeaconStateType) -> anyhow::Result { let indices = [ EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, @@ -320,25 +312,47 @@ fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result anyhow::Result { - let indices = vec![get_subtree_index(block_number) as usize]; - let proof = ssz_rs::generate_proof(state.block_roots, indices.as_slice())?; - - Ok(BlockRootsProof { - block_header_index: block_number, - block_header_branch: proof + mut header: BeaconBlockHeader, +) -> anyhow::Result { + // Check if block root should still be part of the block roots vector on the beacon state + let next_epoch = (compute_epoch_at_slot(header.slot) + 1) as usize; + + if next_epoch % (SLOTS_PER_HISTORICAL_ROOT / SLOTS_PER_EPOCH as usize) == 0 { + // todo: Historical root proofs + unimplemented!() + } else { + // Get index of block root in the block roots + let block_root = header.hash_tree_root().expect("hash tree root should be valid"); + let block_index = state + .block_roots + .as_ref() .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), - }) -} - -// prove the block roots vec inside the beacon state which will be the block roots branch + .position(|root| root == &block_root) + .expect("Block root should exist in block_roots"); + + let proof = ssz_rs::generate_proof(state.block_roots.clone(), &[block_index])?; + + let block_roots_proof = BlockRootsProof { + block_header_index: block_index as u64, + block_header_branch: proof + .into_iter() + .map(|node| { + Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") + }) + .collect(), + }; -// when aggr sigs, create a new bit list + let block_roots_branch = ssz_rs::generate_proof(state, &[BLOCK_ROOTS_INDEX as usize])?; + Ok(AncestryProof::BlockRoots { + block_roots_proof, + block_roots_branch: block_roots_branch + .into_iter() + .map(|node| { + Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") + }) + .collect(), + }) + } +} diff --git a/prover/src/responses/finality_checkpoint_response.rs b/prover/src/responses/finality_checkpoint_response.rs index 17c276be7..db251def7 100644 --- a/prover/src/responses/finality_checkpoint_response.rs +++ b/prover/src/responses/finality_checkpoint_response.rs @@ -3,12 +3,12 @@ use ethereum_consensus::bellatrix::Checkpoint; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub(crate) struct Response { execution_optimistic: bool, - pub data: ResponseData, + pub data: FinalityCheckpoint, } #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct ResponseData { - previous_justified: Checkpoint, - current_justified: Checkpoint, +pub struct FinalityCheckpoint { + pub previous_justified: Checkpoint, + pub current_justified: Checkpoint, pub finalized: Checkpoint, } diff --git a/prover/src/test.rs b/prover/src/test.rs index ddd79bdb3..30c3949e0 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -6,11 +6,13 @@ use sync_committee_primitives::{ util::compute_sync_committee_period_at_slot, }; +use ethereum_consensus::bellatrix::mainnet::HistoricalBatch; use ssz_rs::{ calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, Merkleized, SszVariableOrIndex, }; use std::{thread, time::Duration}; +use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; @@ -247,153 +249,115 @@ async fn test_sync_committee_update_proof() { assert!(is_merkle_branch_valid); } -// use tokio interval(should run every 13 minutes) -// every 13 minutes, fetch latest finalized block -// then prove the execution payload -// prove the finality branch - -// prove sync committee if there is a sync committee update -// to prove sync comnmittee update, calculate state_period and the update_attested_period -// ensure they are the same, and then prove sync committee - -// #[cfg(test)] -// #[allow(non_snake_case)] -// #[actix_rt::test] -// async fn test_prover() { -// // In test config an epoch is 6 slots and we expect finalization every two epochs, -// // a slot is 12 seconds so that brings us to 144 seconds -// let mut stream = IntervalStream::new(time::interval(Duration::from_secs(160))); -// -// let node_url: String = "http://127.0.0.1:5052".to_string(); -// let sync_committee_prover = SyncCommitteeProver::new(node_url); -// -// let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); -// dbg!(&finality_checkpoint.root); -// -// let block_id = { -// let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); -// block_id.insert_str(0, "0x"); -// block_id -// }; -// -// dbg!(&block_id); -// -// let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); -// -// let state = sync_committee_prover -// .fetch_beacon_state(&block_header.slot.to_string()) -// .await -// .unwrap(); -// -// let mut client_state = LightClientState { -// finalized_header: block_header.clone(), -// current_sync_committee: state.current_sync_committee, -// next_sync_committee: state.next_sync_committee, -// }; -// -// while let Some(_ts) = stream.next().await { -// let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); -// dbg!(&finality_checkpoint.root); -// let block_id = { -// let mut block_id = hex::encode(finality_checkpoint.root.as_bytes()); -// block_id.insert_str(0, "0x"); -// block_id -// }; -// -// dbg!(&block_id); -// let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); -// -// if finalized_block.slot <= client_state.finalized_header.slot { -// println!("finalized_block slot is {}", &finalized_block.slot); -// println!("finalized_header slot is {}", &client_state.finalized_header.slot); -// continue -// } -// -// let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); -// -// let execution_payload_proof = prove_execution_payload(finalized_block.clone()).unwrap(); -// -// let attested_header_slot = get_attestation_slots_for_finalized_header(&finalized_header, 6); -// let finalized_state = sync_committee_prover -// .fetch_beacon_state(finalized_block.slot.to_string().as_str()) -// .await -// .unwrap(); -// -// let attested_state = sync_committee_prover -// .fetch_beacon_state(attested_header_slot.to_string().as_str()) -// .await -// .unwrap(); -// -// let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); -// let finality_branch_proof = finality_branch_proof -// .into_iter() -// .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) -// .collect::>(); -// -// let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); -// -// // purposely for waiting -// //println!("sleeping"); -// thread::sleep(time::Duration::from_secs(5)); -// -// let mut attested_header = sync_committee_prover -// .fetch_header(attested_header_slot.to_string().as_str()) -// .await; -// -// while attested_header.is_err() { -// println!("I am running till i am ok. lol {}", &block_id); -// attested_header = sync_committee_prover -// .fetch_header(attested_header_slot.to_string().as_str()) -// .await; -// } -// -// let attested_header = attested_header.unwrap(); -// -// let update_attested_period = compute_sync_committee_period_at_slot(attested_header_slot); -// -// let sync_committee_update = if state_period == attested_header_slot { -// let sync_committee_proof = prove_sync_committee_update(attested_state).unwrap(); -// -// let sync_committee_proof = sync_committee_proof -// .into_iter() -// .map(|node| { -// Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") -// }) -// .collect::>(); -// -// let sync_committee = sync_committee_prover -// .fetch_processed_sync_committee(attested_header.slot.to_string().as_str()) -// .await -// .unwrap(); -// -// Some(SyncCommitteeUpdate { -// next_sync_committee: sync_committee, -// next_sync_committee_branch: sync_committee_proof, -// }) -// } else { -// None -// }; -// -// // construct light client -// let light_client_update = LightClientUpdate { -// attested_header, -// sync_committee_update, -// finalized_header, -// execution_payload: execution_payload_proof, -// finality_branch: finality_branch_proof, -// sync_aggregate: finalized_block.body.sync_aggregate, -// signature_slot: attested_header_slot, -// ancestor_blocks: vec![], -// }; -// -// client_state = EthLightClient::verify_sync_committee_attestation( -// client_state.clone(), -// light_client_update, -// ) -// .unwrap(); -// println!( -// "Sucessfully verified Ethereum block at slot {:?}", -// client_state.finalized_header.slot -// ); -// } -// } +// todo: cloning the beacon state might expensive in production or testnet, if we can modify +// generate_proof function take a reference for the object that would be better. +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_prover() { + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(12 * 64))); + + let node_url: String = "http://127.0.0.1:5052".to_string(); + let sync_committee_prover = SyncCommitteeProver::new(node_url); + + let block_id = "head"; + + let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + let state = sync_committee_prover + .fetch_beacon_state(&block_header.slot.to_string()) + .await + .unwrap(); + + let mut client_state = LightClientState { + finalized_header: block_header.clone(), + current_sync_committee: state.current_sync_committee, + next_sync_committee: state.next_sync_committee, + }; + + while let Some(_ts) = stream.next().await { + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); + if finality_checkpoint.finalized.root == Node::default() || + finality_checkpoint.finalized.epoch <= + compute_epoch_at_slot(client_state.finalized_header.slot) + { + continue + } + + println!("A new epoch has been finalized {}", finality_checkpoint.finalized.epoch); + + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.finalized.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + let finalized_state = sync_committee_prover + .fetch_beacon_state(finalized_header.slot.to_string().as_str()) + .await + .unwrap(); + let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); + + let attested_block_id = { + let mut block_id = hex::encode(finality_checkpoint.current_justified.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + let attested_block_header = + sync_committee_prover.fetch_header(&attested_block_id).await.unwrap(); + + let attested_state = sync_committee_prover + .fetch_beacon_state(attested_block_header.slot.to_string().as_str()) + .await + .unwrap(); + + let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); + + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + let update_attested_period = + compute_sync_committee_period_at_slot(attested_block_header.slot); + + let sync_committee_update = if state_period == attested_block_header.slot { + let sync_committee_proof = prove_sync_committee_update(attested_state.clone()).unwrap(); + + let sync_committee_proof = sync_committee_proof + .into_iter() + .map(|node| { + Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") + }) + .collect::>(); + + Some(SyncCommitteeUpdate { + next_sync_committee: attested_state.next_sync_committee, + next_sync_committee_branch: sync_committee_proof, + }) + } else { + None + }; + + let signature_slot = attested_block_header.slot; + // construct light client + let light_client_update = LightClientUpdate { + attested_header: attested_block_header, + sync_committee_update, + finalized_header, + execution_payload: execution_payload_proof, + finality_proof: finality_branch_proof, + sync_aggregate: finalized_block.body.sync_aggregate, + signature_slot, + // todo: Prove some ancestry blocks + ancestor_blocks: vec![], + }; + + client_state = + verify_sync_committee_attestation(client_state.clone(), light_client_update).unwrap(); + println!( + "Sucessfully verified Ethereum block at slot {:?}", + client_state.finalized_header.slot + ); + } +} diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 8bce18650..573fe609e 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -31,8 +31,6 @@ use sync_committee_primitives::{ get_subtree_index, hash_tree_root, }, }; -// use sync_committee_primitives::types::{FINALIZED_ROOT_INDEX_FLOOR_LOG_2, -// NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2}; pub type LightClientState = sync_committee_primitives::types::LightClientState; pub type LightClientUpdate = From dabe7ef8148fecb7a5a8a7bdae8bca2edc4473f7 Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 23 Feb 2023 09:48:27 +0100 Subject: [PATCH 056/182] fix sync comittee aggregate participation bits count --- prover/src/lib.rs | 8 +++++--- prover/src/test.rs | 8 ++++---- verifier/src/lib.rs | 8 +++++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 74d7223aa..853da1ef1 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -115,7 +115,6 @@ impl SyncCommitteeProver { let path = header_route(block_id); let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; - let status = response.status().as_u16(); let response_data = response.json::().await?; @@ -317,9 +316,12 @@ fn prove_block_roots_proof( mut header: BeaconBlockHeader, ) -> anyhow::Result { // Check if block root should still be part of the block roots vector on the beacon state - let next_epoch = (compute_epoch_at_slot(header.slot) + 1) as usize; + let epoch_for_header = compute_epoch_at_slot(header.slot) as usize; + let epoch_for_state = compute_epoch_at_slot(state.slot) as usize; - if next_epoch % (SLOTS_PER_HISTORICAL_ROOT / SLOTS_PER_EPOCH as usize) == 0 { + if epoch_for_state.saturating_sub(epoch_for_header) >= + SLOTS_PER_HISTORICAL_ROOT / SLOTS_PER_EPOCH as usize + { // todo: Historical root proofs unimplemented!() } else { diff --git a/prover/src/test.rs b/prover/src/test.rs index 30c3949e0..81118a8b6 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -11,7 +11,7 @@ use ssz_rs::{ calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, Merkleized, SszVariableOrIndex, }; -use std::{thread, time::Duration}; +use std::time::Duration; use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; @@ -208,7 +208,6 @@ async fn test_sync_committee_update_proof() { let sync_committee_prover = SyncCommitteeProver::new(node_url); let finalized_header = sync_committee_prover.fetch_header("head").await.unwrap(); - let block_id = finalized_header.slot.to_string(); let finalized_state = sync_committee_prover .fetch_beacon_state(&finalized_header.slot.to_string()) @@ -255,7 +254,7 @@ async fn test_sync_committee_update_proof() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_prover() { - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(12 * 64))); + let mut stream = IntervalStream::new(time::interval(Duration::from_secs(12 * 12))); let node_url: String = "http://127.0.0.1:5052".to_string(); let sync_committee_prover = SyncCommitteeProver::new(node_url); @@ -281,6 +280,7 @@ async fn test_prover() { finality_checkpoint.finalized.epoch <= compute_epoch_at_slot(client_state.finalized_header.slot) { + println!("No new finalized checkpoint"); continue } @@ -321,7 +321,7 @@ async fn test_prover() { let update_attested_period = compute_sync_committee_period_at_slot(attested_block_header.slot); - let sync_committee_update = if state_period == attested_block_header.slot { + let sync_committee_update = if state_period == update_attested_period { let sync_committee_proof = prove_sync_committee_update(attested_state.clone()).unwrap(); let sync_committee_proof = sync_committee_proof diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 573fe609e..aac2d6930 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -43,7 +43,7 @@ pub fn verify_sync_committee_attestation( ) -> Result { if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && update.sync_committee_update.is_some() && - update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() != + update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize { log::debug!("Invalid update "); @@ -53,7 +53,7 @@ pub fn verify_sync_committee_attestation( ); log::debug!( "update next sync committee branch length {} ", - update.clone().sync_committee_update.unwrap().next_sync_committee_branch.len() + update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() ); Err(Error::InvalidUpdate)? @@ -61,7 +61,9 @@ pub fn verify_sync_committee_attestation( // Verify sync committee has super majority participants let sync_committee_bits = update.sync_aggregate.sync_committee_bits; - let sync_aggregate_participants: u64 = sync_committee_bits.iter().count() as u64; + let sync_aggregate_participants: u64 = + sync_committee_bits.iter().as_bitslice().count_ones() as u64; + if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { log::debug!("SyncCommitteeParticipantsTooLow "); log::debug!("sync_aggregate_participants {} ", { sync_aggregate_participants * 3 }); From 750c9f36b64a47271f6daf2f9847abb254b49f31 Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 24 Feb 2023 00:42:58 +0100 Subject: [PATCH 057/182] fetching attested header correctly, signature slot left --- Cargo.lock | 137 +++++++++++++- primitives/Cargo.toml | 15 +- primitives/src/lib.rs | 1 - primitives/src/types.rs | 19 +- primitives/src/util.rs | 24 +-- prover/Cargo.toml | 7 +- prover/src/lib.rs | 34 +--- prover/src/responses/beacon_block_response.rs | 19 +- prover/src/test.rs | 176 ++++++++++++------ verifier/Cargo.toml | 15 +- verifier/src/lib.rs | 69 ++++--- 11 files changed, 353 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc1219cb1..865fabfcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "amcl" version = "0.3.0" @@ -363,6 +372,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "error-chain" version = "0.12.4" @@ -569,6 +612,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -624,6 +673,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.24" @@ -717,12 +772,34 @@ dependencies = [ "num-traits", ] +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "ipnet" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -780,6 +857,12 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -811,6 +894,8 @@ version = "1.5.1" source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" dependencies = [ "amcl", + "hex", + "lazy_static", "rand", "zeroize", ] @@ -932,7 +1017,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1153,6 +1238,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -1226,6 +1328,20 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "ryu" version = "1.0.12" @@ -1482,6 +1598,7 @@ dependencies = [ "anyhow", "async-stream", "base2 0.2.2", + "env_logger", "ethereum-consensus", "hex", "reqwest", @@ -1538,6 +1655,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -1850,6 +1976,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 3638636cd..e9242033c 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -6,7 +6,14 @@ authors = ["Polytope Labs"] [dependencies] -base2 = { version = "0.3.1", default-features=false} -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features=false, features=["serde"] } -hex-literal = { package = "hex-literal", version = "0.3.3" } +base2 = { version = "0.3.1", default-features = false} +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2", default-features = false } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features = false, features=["serde"] } +hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } + +[features] +default = ["std"] +std = [ + "ssz-rs/std" +] +testing = [] \ No newline at end of file diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index d28e53bb2..7ff490608 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] extern crate alloc; pub mod types; diff --git a/primitives/src/types.rs b/primitives/src/types.rs index b292272c7..68484e68c 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -15,11 +15,12 @@ pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; pub const BLOCK_ROOTS_INDEX: u64 = 37; pub const HISTORICAL_ROOTS_INDEX: u64 = 39; pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; +#[cfg(not(feature = "testing"))] pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); -// pub const NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = NEXT_SYNC_COMMITTEE_INDEX.floor_log2() -// as usize; pub const FINALIZED_ROOT_INDEX_FLOOR_LOG_2: usize = FINALIZED_ROOT_INDEX.floor_log2() -// as usize; +#[cfg(feature = "testing")] +pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("6034f557b4560fc549ac0e2c63269deb07bfac7bf2bbd0b8b7d4d321240bffd9"); /// This holds the relevant data required to prove the state root in the execution payload. #[derive(Debug, Clone)] @@ -106,16 +107,6 @@ pub struct LightClientState { pub next_sync_committee: SyncCommittee, } -/// Minimum state required by the light client to validate new sync committee attestations -#[derive(Debug, Clone)] -pub struct FinalityProof { - /// Epoch that was finalized - pub finalized_epoch: Epoch, - /// the ssz merkle proof for the finalized checkpoint in the attested header, finalized headers - /// lag by 2 epochs. - pub finality_branch: Vec, -} - /// Data required to advance the state of the light client. #[derive(Debug, Clone)] pub struct LightClientUpdate { @@ -128,7 +119,7 @@ pub struct LightClientUpdate { /// execution payload of the finalized header pub execution_payload: ExecutionPayloadProof, /// Finalized header proof - pub finality_proof: FinalityProof, + pub finality_branch: Vec, /// signature & participation bits pub sync_aggregate: SyncAggregate, /// slot at which signature was produced diff --git a/primitives/src/util.rs b/primitives/src/util.rs index ab58d83be..e090e33b2 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -1,7 +1,10 @@ use base2::Base2; use ethereum_consensus::{ altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD, - configs::mainnet::{ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, GENESIS_FORK_VERSION}, + configs::mainnet::{ + ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, BELLATRIX_FORK_EPOCH, BELLATRIX_FORK_VERSION, + GENESIS_FORK_VERSION, + }, phase0::mainnet::SLOTS_PER_EPOCH, }; use ssz_rs::Node; @@ -21,25 +24,24 @@ pub fn compute_epoch_at_slot(slot: u64) -> u64 { slot / SLOTS_PER_EPOCH } +#[cfg(not(feature = "testing"))] /// Return the fork version at the given ``epoch``. pub fn compute_fork_version(epoch: u64) -> [u8; 4] { - if epoch >= ALTAIR_FORK_EPOCH { + if epoch >= BELLATRIX_FORK_EPOCH { + BELLATRIX_FORK_VERSION + } else if epoch >= ALTAIR_FORK_EPOCH { ALTAIR_FORK_VERSION } else { GENESIS_FORK_VERSION } } +#[cfg(feature = "testing")] +pub fn compute_fork_version(epoch: u64) -> [u8; 4] { + BELLATRIX_FORK_VERSION +} + /// Return the sync committee period at ``slot`` pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { compute_sync_committee_period(compute_epoch_at_slot(slot)) } - -/// method for hashing objects into a single root by utilizing a hash tree structure, as defined in -/// the SSZ spec. -pub fn hash_tree_root( - mut object: T, -) -> Result { - let root = object.hash_tree_root()?; - Ok(root) -} diff --git a/prover/Cargo.toml b/prover/Cargo.toml index f07ffedd3..8db82b6b2 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sync-committee-primitives = { path= "../primitives", default-features = false } -sync-committee-verifier = { path= "../verifier", default-features = false } +sync-committee-primitives = { path= "../primitives" } +sync-committee-verifier = { path= "../verifier" } ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", features=["serde", "std"] } reqwest = {version="0.11.14", features=["json"]} @@ -19,8 +19,11 @@ tokio = { version = "1.18.2", features = ["full"]} tokio-stream = { version = "0.1.8" } async-stream = { version = "0.3.3"} base2 = {version="0.2.2", default-features=false} +env_logger = "0.10.0" [dev-dependencies] hex = "0.4.3" +sync-committee-primitives = { path= "../primitives", features = ["testing"] } +sync-committee-verifier = { path= "../verifier", features = ["testing"] } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 853da1ef1..14e395445 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -8,9 +8,7 @@ mod test; use ethereum_consensus::{ altair::Validator, - bellatrix::{ - BeaconBlock, BeaconBlockHeader, BeaconState, Checkpoint, SignedBeaconBlock, SyncCommittee, - }, + bellatrix::{BeaconBlock, BeaconBlockHeader, BeaconState, SignedBeaconBlock, SyncCommittee}, }; use reqwest::Client; @@ -35,16 +33,14 @@ use ethereum_consensus::{ }, primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}, }; -use ssz_rs::{ - get_generalized_index, GeneralizedIndex, List, Merkleized, Node, SszVariableOrIndex, Vector, -}; +use ssz_rs::{get_generalized_index, GeneralizedIndex, List, Merkleized, Node, Vector}; use sync_committee_primitives::{ types::{ - AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, BLOCK_ROOTS_INDEX, + AncestryProof, BlockRootsProof, ExecutionPayloadProof, BLOCK_ROOTS_INDEX, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, }, - util::{compute_epoch_at_slot, get_subtree_index}, + util::compute_epoch_at_slot, }; type BeaconBlockType = BeaconBlock< @@ -238,15 +234,6 @@ impl SyncCommitteeProver { Ok(sync_committee) } - async fn fetch_latest_finalized_block( - &self, - ) -> Result<(BeaconBlockHeader, BeaconBlockType), reqwest::Error> { - let block_header = self.fetch_header("finalized").await?; - let block = self.fetch_block("finalized").await?; - - Ok((block_header, block)) - } - fn generate_route(&self, path: &str) -> String { format!("{}{}", self.node_url.clone(), path) } @@ -298,17 +285,14 @@ fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result anyhow::Result { +fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; - Ok(FinalityProof { - finalized_epoch: state.finalized_checkpoint.epoch, - finality_branch: proof - .into_iter() - .map(|node| Hash32::try_from(node.as_ref()).expect("Node is always a 32 byte slice")) - .collect(), - }) + Ok(proof + .into_iter() + .map(|node| Hash32::try_from(node.as_ref()).expect("Node is always a 32 byte slice")) + .collect()) } fn prove_block_roots_proof( diff --git a/prover/src/responses/beacon_block_response.rs b/prover/src/responses/beacon_block_response.rs index d90317c7a..62839e3c4 100644 --- a/prover/src/responses/beacon_block_response.rs +++ b/prover/src/responses/beacon_block_response.rs @@ -1,17 +1,10 @@ -use ethereum_consensus::{ - bellatrix::{ - mainnet::{ - BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, - MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, - }, - BeaconBlock, BeaconBlockHeader, - }, - phase0::mainnet::{ - EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, - HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, - MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, - SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, +use ethereum_consensus::bellatrix::{ + mainnet::{ + BYTES_PER_LOGS_BLOOM, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_BYTES_PER_TRANSACTION, + MAX_DEPOSITS, MAX_EXTRA_DATA_BYTES, MAX_PROPOSER_SLASHINGS, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SYNC_COMMITTEE_SIZE, }, + BeaconBlock, }; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/prover/src/test.rs b/prover/src/test.rs index 81118a8b6..687a62810 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -1,33 +1,33 @@ use super::*; use base2::Base2; -use ethereum_consensus::altair::NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2; use sync_committee_primitives::{ types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, util::compute_sync_committee_period_at_slot, }; -use ethereum_consensus::bellatrix::mainnet::HistoricalBatch; -use ssz_rs::{ - calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, - Merkleized, SszVariableOrIndex, +use ethereum_consensus::{ + bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, + state_transition::Context, }; +use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; use std::time::Duration; +use sync_committee_primitives::{ + types::{DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, + util::compute_fork_version, +}; use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; +const NODE_URL: &'static str = "http://localhost:5052"; + #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] #[ignore] async fn fetch_block_header_works() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let mut block_header = sync_committee_prover.fetch_header("1000").await; - while block_header.is_err() { - println!("I am running till i am ok. lol"); - block_header = sync_committee_prover.fetch_header("1000").await; - } + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let block_header = sync_committee_prover.fetch_header("head").await; assert!(block_header.is_ok()); } @@ -36,9 +36,8 @@ async fn fetch_block_header_works() { #[actix_rt::test] #[ignore] async fn fetch_block_works() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_block("100").await; + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let block = sync_committee_prover.fetch_block("head").await; assert!(block.is_ok()); } @@ -47,9 +46,8 @@ async fn fetch_block_works() { #[actix_rt::test] #[ignore] async fn fetch_sync_committee_works() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let block = sync_committee_prover.fetch_sync_committee("117").await; + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let block = sync_committee_prover.fetch_sync_committee("head").await; assert!(block.is_ok()); } @@ -58,9 +56,8 @@ async fn fetch_sync_committee_works() { #[actix_rt::test] #[ignore] async fn fetch_validator_works() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let validator = sync_committee_prover.fetch_validator("2561", "48").await; + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let validator = sync_committee_prover.fetch_validator("head", "48").await; assert!(validator.is_ok()); } @@ -69,9 +66,8 @@ async fn fetch_validator_works() { #[actix_rt::test] #[ignore] async fn fetch_processed_sync_committee_works() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let validator = sync_committee_prover.fetch_processed_sync_committee("2561").await; + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let validator = sync_committee_prover.fetch_processed_sync_committee("head").await; assert!(validator.is_ok()); } @@ -80,8 +76,7 @@ async fn fetch_processed_sync_committee_works() { #[actix_rt::test] #[ignore] async fn fetch_beacon_state_works() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let beacon_state = sync_committee_prover.fetch_beacon_state("genesis").await; assert!(beacon_state.is_ok()); } @@ -91,17 +86,14 @@ async fn fetch_beacon_state_works() { #[actix_rt::test] #[ignore] async fn state_root_and_block_header_root_matches() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); - let beacon_state = sync_committee_prover.fetch_beacon_state("100").await; - assert!(beacon_state.is_ok()); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let mut beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); - let block_header = sync_committee_prover.fetch_header("100").await; + let block_header = sync_committee_prover.fetch_header(&beacon_state.slot.to_string()).await; assert!(block_header.is_ok()); - let state = beacon_state.unwrap(); let block_header = block_header.unwrap(); - let hash_tree_root = state.clone().hash_tree_root(); + let hash_tree_root = beacon_state.hash_tree_root(); assert!(block_header.state_root == hash_tree_root.unwrap()); } @@ -110,8 +102,7 @@ async fn state_root_and_block_header_root_matches() { #[allow(non_snake_case)] #[actix_rt::test] async fn fetch_finality_checkpoints_work() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; assert!(finality_checkpoint.is_ok()); } @@ -120,8 +111,7 @@ async fn fetch_finality_checkpoints_work() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_finalized_header() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let proof = ssz_rs::generate_proof(state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); @@ -147,8 +137,7 @@ async fn test_finalized_header() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_execution_payload_proof() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let block_id = finalized_state.slot.to_string(); @@ -204,8 +193,7 @@ async fn test_execution_payload_proof() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_sync_committee_update_proof() { - let node_url: String = "http://localhost:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let finalized_header = sync_committee_prover.fetch_header("head").await.unwrap(); @@ -254,10 +242,10 @@ async fn test_sync_committee_update_proof() { #[allow(non_snake_case)] #[actix_rt::test] async fn test_prover() { + env_logger::init(); let mut stream = IntervalStream::new(time::interval(Duration::from_secs(12 * 12))); - let node_url: String = "http://127.0.0.1:5052".to_string(); - let sync_committee_prover = SyncCommitteeProver::new(node_url); + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let block_id = "head"; @@ -274,6 +262,7 @@ async fn test_prover() { next_sync_committee: state.next_sync_committee, }; + let mut count = 0; while let Some(_ts) = stream.next().await { let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); if finality_checkpoint.finalized.root == Node::default() || @@ -300,21 +289,36 @@ async fn test_prover() { .unwrap(); let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - let attested_block_id = { - let mut block_id = hex::encode(finality_checkpoint.current_justified.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id + let attested_epoch = finality_checkpoint.finalized.epoch + 2; + // Get available block from attested epoch + + let mut attested_slot = attested_epoch * SLOTS_PER_EPOCH; + let attested_block_header = loop { + if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH) == attested_slot { + panic!("Could not find any block from the attested epoch") + } + + if let Ok(header) = + sync_committee_prover.fetch_header(attested_slot.to_string().as_str()).await + { + break header + } + attested_slot += 1 }; - let attested_block_header = - sync_committee_prover.fetch_header(&attested_block_id).await.unwrap(); - let attested_state = sync_committee_prover .fetch_beacon_state(attested_block_header.slot.to_string().as_str()) .await .unwrap(); - let finality_branch_proof = prove_finalized_header(attested_state.clone()).unwrap(); + println!("{:?}", attested_state.finalized_checkpoint); + println!( + "{:?}, {:?}", + compute_epoch_at_slot(finalized_header.slot), + finalized_header.clone().hash_tree_root().unwrap() + ); + + let finality_branch = prove_finalized_header(attested_state.clone()).unwrap(); let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); @@ -339,15 +343,19 @@ async fn test_prover() { None }; - let signature_slot = attested_block_header.slot; + let signature_slot = attested_block_header.slot + 1; + let signature_block = sync_committee_prover + .fetch_block(signature_slot.to_string().as_str()) + .await + .unwrap(); // construct light client let light_client_update = LightClientUpdate { attested_header: attested_block_header, sync_committee_update, finalized_header, execution_payload: execution_payload_proof, - finality_proof: finality_branch_proof, - sync_aggregate: finalized_block.body.sync_aggregate, + finality_branch, + sync_aggregate: signature_block.body.sync_aggregate, signature_slot, // todo: Prove some ancestry blocks ancestor_blocks: vec![], @@ -359,5 +367,65 @@ async fn test_prover() { "Sucessfully verified Ethereum block at slot {:?}", client_state.finalized_header.slot ); + + count += 1; + if count == 100 { + break + } } } + +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_sync_committee_signature_verification() { + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let block = loop { + let block = sync_committee_prover.fetch_block("head").await.unwrap(); + if block.slot < 16 { + std::thread::sleep(Duration::from_secs(48)); + continue + } + break block + }; + let sync_committee = sync_committee_prover + .fetch_processed_sync_committee(block.slot.to_string().as_str()) + .await + .unwrap(); + + let mut attested_header = sync_committee_prover + .fetch_header((block.slot - 1).to_string().as_str()) + .await + .unwrap(); + + let sync_committee_pubkeys = sync_committee.public_keys; + + let participant_pubkeys = block + .body + .sync_aggregate + .sync_committee_bits + .iter() + .zip(sync_committee_pubkeys.iter()) + .filter_map(|(bit, key)| if *bit { Some(key) } else { None }) + .collect::>(); + + let fork_version = compute_fork_version(compute_epoch_at_slot(block.slot)); + + let context = Context::for_mainnet(); + let domain = compute_domain( + DOMAIN_SYNC_COMMITTEE, + Some(fork_version), + Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().unwrap())), + &context, + ) + .unwrap(); + + let signing_root = compute_signing_root(&mut attested_header, domain); + + ethereum_consensus::crypto::fast_aggregate_verify( + &*participant_pubkeys, + signing_root.unwrap().as_bytes(), + &block.body.sync_aggregate.sync_committee_signature, + ) + .unwrap(); +} diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 8b68a4014..3e65a2b1a 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -7,7 +7,16 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version="0.2.2", default-features = false } sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features=false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2", default-features = false } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features = false, features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } -log = "0.4.17" \ No newline at end of file +log = { version = "0.4.17", default-features = false } + +[features] +default = ["std"] +std = [ + "ssz-rs/std", + "milagro_bls/std", + "log/std" +] +testing = ["sync-committee-primitives/testing"] \ No newline at end of file diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index aac2d6930..2d13079ad 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] extern crate alloc; pub mod error; @@ -15,6 +14,7 @@ use ethereum_consensus::{ signing::compute_signing_root, state_transition::Context, }; + use ssz_rs::{ calculate_merkle_root, calculate_multi_merkle_root, prelude::is_valid_merkle_branch, GeneralizedIndex, Merkleized, Node, @@ -28,10 +28,13 @@ use sync_committee_primitives::{ }, util::{ compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, - get_subtree_index, hash_tree_root, + get_subtree_index, }, }; +#[cfg(not(feature = "std"))] +use log::debug as println; + pub type LightClientState = sync_committee_primitives::types::LightClientState; pub type LightClientUpdate = sync_committee_primitives::types::LightClientUpdate; @@ -41,17 +44,13 @@ pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { - if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && + if update.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && update.sync_committee_update.is_some() && update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize { - log::debug!("Invalid update "); - log::debug!( - "update finality branch length {} ", - update.finality_proof.finality_branch.len() - ); - log::debug!( + println!("update finality branch length {} ", update.finality_branch.len()); + println!( "update next sync committee branch length {} ", update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() ); @@ -64,10 +63,10 @@ pub fn verify_sync_committee_attestation( let sync_aggregate_participants: u64 = sync_committee_bits.iter().as_bitslice().count_ones() as u64; - if sync_aggregate_participants * 3 >= sync_committee_bits.clone().len() as u64 * 2 { - log::debug!("SyncCommitteeParticipantsTooLow "); - log::debug!("sync_aggregate_participants {} ", { sync_aggregate_participants * 3 }); - log::debug!("sync_committee_bits {}", { sync_committee_bits.clone().len() * 2 }); + if sync_aggregate_participants < (2 * sync_committee_bits.len() as u64) / 3 { + println!("SyncCommitteeParticipantsTooLow "); + println!("sync_aggregate_participants {} ", { sync_aggregate_participants }); + println!("sync_committee_bits {}", { sync_committee_bits.len() * 2 }); Err(Error::SyncCommitteeParticipantsTooLow)? } @@ -75,16 +74,14 @@ pub fn verify_sync_committee_attestation( let is_valid_update = update.signature_slot > update.attested_header.slot && update.attested_header.slot >= update.finalized_header.slot; if !is_valid_update { - log::debug!("is_valid_update {} ", is_valid_update); - log::debug!( + println!("is_valid_update {} ", is_valid_update); + println!( "update.signature_slot {} update.attested_header.slot {}", - update.signature_slot, - update.attested_header.slot + update.signature_slot, update.attested_header.slot ); - log::debug!( + println!( "update.attested_header.slot {} update.finalized_header.slot {}", - update.attested_header.slot, - update.finalized_header.slot + update.attested_header.slot, update.finalized_header.slot ); Err(Error::InvalidUpdate)? } @@ -92,9 +89,9 @@ pub fn verify_sync_committee_attestation( let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); if !(state_period..=state_period + 1).contains(&update_signature_period) { - log::debug!("invalid update"); - log::debug!("state_period is {}", state_period); - log::debug!("update_signature_period is {}", update_signature_period); + println!("invalid update"); + println!("state_period is {}", state_period); + println!("update_signature_period is {}", update_signature_period); Err(Error::InvalidUpdate)? } @@ -125,12 +122,13 @@ pub fn verify_sync_committee_attestation( .collect::>(); let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); - //TODO: we probably need to construct context + + let context = Context::for_mainnet(); let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().map_err(|_| Error::InvalidRoot)?)), - &Context::default(), + &context, ) .map_err(|_| Error::InvalidUpdate)?; @@ -147,7 +145,7 @@ pub fn verify_sync_committee_attestation( // to match the finalized checkpoint root saved in the state of `attested_header`. // Note that the genesis finalized checkpoint root is represented as a zero hash. let mut finalized_checkpoint = Checkpoint { - epoch: update.finality_proof.finalized_epoch, + epoch: compute_epoch_at_slot(update.finalized_header.slot), root: update .finalized_header .clone() @@ -156,7 +154,6 @@ pub fn verify_sync_committee_attestation( }; let branch = update - .finality_proof .finality_branch .iter() .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) @@ -170,9 +167,8 @@ pub fn verify_sync_committee_attestation( &update.attested_header.state_root, ); - log::debug!("valid merkle branch for finalized_root {}", is_merkle_branch_valid); if !is_merkle_branch_valid { - log::debug!("invalid merkle branch for finalized root"); + println!("invalid merkle branch for finalized block"); Err(Error::InvalidMerkleBranch)?; } @@ -218,9 +214,9 @@ pub fn verify_sync_committee_attestation( &update.finalized_header.state_root, ); - log::debug!("valid merkle branch for execution_payload_branch"); + println!("valid merkle branch for execution_payload_branch"); if !is_merkle_branch_valid { - log::debug!("invalid merkle branch for execution_payload_branch"); + println!("invalid merkle branch for execution_payload_branch"); Err(Error::InvalidMerkleBranch)?; } @@ -229,7 +225,7 @@ pub fn verify_sync_committee_attestation( sync_committee_update.next_sync_committee != trusted_state.next_sync_committee.clone() { - log::debug!("invalid update for sync committee update"); + println!("invalid update for sync committee update"); Err(Error::InvalidUpdate)? } @@ -249,9 +245,9 @@ pub fn verify_sync_committee_attestation( &update.attested_header.state_root, ); - log::debug!("valid merkle branch for sync committee {}", is_merkle_branch_valid); + println!("valid merkle branch for sync committee {}", is_merkle_branch_valid); if !is_merkle_branch_valid { - log::debug!("invalid merkle branch for sync committee"); + println!("invalid merkle branch for sync committee"); Err(Error::InvalidMerkleBranch)?; } } @@ -372,7 +368,10 @@ pub fn verify_sync_committee_attestation( .map_err(|_| Error::InvalidRoot)?, ), Node::from_bytes( - hash_tree_root(execution_payload.block_number) + execution_payload + .block_number + .clone() + .hash_tree_root() .map_err(|_| Error::MerkleizationError)? .as_ref() .try_into() From 5ac986249a1b92c66d3451573a4b5d084ccdde96 Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 24 Feb 2023 11:55:21 +0100 Subject: [PATCH 058/182] stuff works now --- prover/src/test.rs | 63 +++++++++++++++++++++++++++++++++++---------- verifier/src/lib.rs | 33 +++--------------------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index 687a62810..2c44f949b 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -6,7 +6,7 @@ use sync_committee_primitives::{ }; use ethereum_consensus::{ - bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, + altair::Checkpoint, bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, state_transition::Context, }; use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; @@ -19,6 +19,11 @@ use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; +// **NOTE** To run these tests make sure the latest fork version on your devnet is the +// BELLATRIX_FORK_VERSION as defined in the mainnet config Also modify +// `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing feature +// flag to match the one that is present in the devnet you are running the tests with + const NODE_URL: &'static str = "http://localhost:5052"; #[cfg(test)] @@ -269,7 +274,6 @@ async fn test_prover() { finality_checkpoint.finalized.epoch <= compute_epoch_at_slot(client_state.finalized_header.slot) { - println!("No new finalized checkpoint"); continue } @@ -281,7 +285,6 @@ async fn test_prover() { block_id }; - let finalized_block = sync_committee_prover.fetch_block(&block_id).await.unwrap(); let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); let finalized_state = sync_committee_prover .fetch_beacon_state(finalized_header.slot.to_string().as_str()) @@ -290,18 +293,46 @@ async fn test_prover() { let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); let attested_epoch = finality_checkpoint.finalized.epoch + 2; - // Get available block from attested epoch + // Get attested header and the signature slot let mut attested_slot = attested_epoch * SLOTS_PER_EPOCH; - let attested_block_header = loop { - if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH) == attested_slot { + let (attested_block_header, signature_block) = loop { + if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == + attested_slot + { panic!("Could not find any block from the attested epoch") } if let Ok(header) = sync_committee_prover.fetch_header(attested_slot.to_string().as_str()).await { - break header + let mut signature_slot = header.slot + 1; + let signature_block = loop { + if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == + signature_slot + { + panic!("Could not find any block after the attested header from the attested epoch") + } + if let Ok(signature_block) = + sync_committee_prover.fetch_block(signature_slot.to_string().as_str()).await + { + break signature_block + } + signature_slot += 1; + }; + // If the next block does not have sufficient sync committee participants + if signature_block + .body + .sync_aggregate + .sync_committee_bits + .as_bitslice() + .count_ones() < 2 / 3 * (SYNC_COMMITTEE_SIZE) + { + attested_slot = signature_slot + 1; + println!("Signature block does not have sufficient sync committee participants -> participants {}", signature_block.body.sync_aggregate.sync_committee_bits.as_bitslice().count_ones()); + continue + } + break (header, signature_block) } attested_slot += 1 }; @@ -311,11 +342,20 @@ async fn test_prover() { .await .unwrap(); + let finalized_hash_tree_root = finalized_header.clone().hash_tree_root().unwrap(); println!("{:?}", attested_state.finalized_checkpoint); println!( "{:?}, {:?}", compute_epoch_at_slot(finalized_header.slot), - finalized_header.clone().hash_tree_root().unwrap() + finalized_hash_tree_root + ); + + assert_eq!( + Checkpoint { + epoch: compute_epoch_at_slot(finalized_header.slot), + root: finalized_hash_tree_root, + }, + attested_state.finalized_checkpoint, ); let finality_branch = prove_finalized_header(attested_state.clone()).unwrap(); @@ -343,11 +383,6 @@ async fn test_prover() { None }; - let signature_slot = attested_block_header.slot + 1; - let signature_block = sync_committee_prover - .fetch_block(signature_slot.to_string().as_str()) - .await - .unwrap(); // construct light client let light_client_update = LightClientUpdate { attested_header: attested_block_header, @@ -356,7 +391,7 @@ async fn test_prover() { execution_payload: execution_payload_proof, finality_branch, sync_aggregate: signature_block.body.sync_aggregate, - signature_slot, + signature_slot: signature_block.slot, // todo: Prove some ancestry blocks ancestor_blocks: vec![], }; diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 2d13079ad..701b3174e 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -26,15 +26,9 @@ use sync_committee_primitives::{ EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, NEXT_SYNC_COMMITTEE_INDEX, }, - util::{ - compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot, - get_subtree_index, - }, + util::{compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot}, }; -#[cfg(not(feature = "std"))] -use log::debug as println; - pub type LightClientState = sync_committee_primitives::types::LightClientState; pub type LightClientUpdate = sync_committee_primitives::types::LightClientUpdate; @@ -64,9 +58,6 @@ pub fn verify_sync_committee_attestation( sync_committee_bits.iter().as_bitslice().count_ones() as u64; if sync_aggregate_participants < (2 * sync_committee_bits.len() as u64) / 3 { - println!("SyncCommitteeParticipantsTooLow "); - println!("sync_aggregate_participants {} ", { sync_aggregate_participants }); - println!("sync_committee_bits {}", { sync_committee_bits.len() * 2 }); Err(Error::SyncCommitteeParticipantsTooLow)? } @@ -74,24 +65,12 @@ pub fn verify_sync_committee_attestation( let is_valid_update = update.signature_slot > update.attested_header.slot && update.attested_header.slot >= update.finalized_header.slot; if !is_valid_update { - println!("is_valid_update {} ", is_valid_update); - println!( - "update.signature_slot {} update.attested_header.slot {}", - update.signature_slot, update.attested_header.slot - ); - println!( - "update.attested_header.slot {} update.finalized_header.slot {}", - update.attested_header.slot, update.finalized_header.slot - ); Err(Error::InvalidUpdate)? } let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); if !(state_period..=state_period + 1).contains(&update_signature_period) { - println!("invalid update"); - println!("state_period is {}", state_period); - println!("update_signature_period is {}", update_signature_period); Err(Error::InvalidUpdate)? } @@ -168,7 +147,6 @@ pub fn verify_sync_committee_attestation( ); if !is_merkle_branch_valid { - println!("invalid merkle branch for finalized block"); Err(Error::InvalidMerkleBranch)?; } @@ -214,9 +192,7 @@ pub fn verify_sync_committee_attestation( &update.finalized_header.state_root, ); - println!("valid merkle branch for execution_payload_branch"); if !is_merkle_branch_valid { - println!("invalid merkle branch for execution_payload_branch"); Err(Error::InvalidMerkleBranch)?; } @@ -225,7 +201,6 @@ pub fn verify_sync_committee_attestation( sync_committee_update.next_sync_committee != trusted_state.next_sync_committee.clone() { - println!("invalid update for sync committee update"); Err(Error::InvalidUpdate)? } @@ -241,13 +216,11 @@ pub fn verify_sync_committee_attestation( .map_err(|_| Error::MerkleizationError)?, next_sync_committee_branch.iter(), NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, - get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX) as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, &update.attested_header.state_root, ); - println!("valid merkle branch for sync committee {}", is_merkle_branch_valid); if !is_merkle_branch_valid { - println!("invalid merkle branch for sync committee"); Err(Error::InvalidMerkleBranch)?; } } @@ -334,7 +307,7 @@ pub fn verify_sync_committee_attestation( &historical_roots_root, historical_roots_branch_nodes.iter(), HISTORICAL_ROOTS_INDEX.floor_log2() as usize, - get_subtree_index(HISTORICAL_ROOTS_INDEX) as usize, + HISTORICAL_ROOTS_INDEX as usize, &Node::from_bytes( update .finalized_header From b576df7e1310b6fec6992923db68888447a99a0f Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 24 Feb 2023 11:56:14 +0100 Subject: [PATCH 059/182] nit --- verifier/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 701b3174e..135faff1e 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -43,12 +43,6 @@ pub fn verify_sync_committee_attestation( update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize { - println!("update finality branch length {} ", update.finality_branch.len()); - println!( - "update next sync committee branch length {} ", - update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() - ); - Err(Error::InvalidUpdate)? } From b03fa9d3635d26a001c8983642008d665ca8c6a3 Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 24 Feb 2023 18:06:43 +0100 Subject: [PATCH 060/182] some fixes --- prover/src/test.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index 2c44f949b..77855d542 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -19,10 +19,12 @@ use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; -// **NOTE** To run these tests make sure the latest fork version on your devnet is the -// BELLATRIX_FORK_VERSION as defined in the mainnet config Also modify -// `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing feature -// flag to match the one that is present in the devnet you are running the tests with +// **NOTE** +// 1. To run these tests make sure the latest fork version on your devnet is the +// BELLATRIX_FORK_VERSION as defined in the mainnet config +// 2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing +// feature flag to match the one that is present in the devnet you are running the tests with +// 3. Make sure the SLOTS_PER_EPOCH is set to 32 in your beacon node. const NODE_URL: &'static str = "http://localhost:5052"; @@ -307,11 +309,18 @@ async fn test_prover() { sync_committee_prover.fetch_header(attested_slot.to_string().as_str()).await { let mut signature_slot = header.slot + 1; + let mut loop_count = 0; let signature_block = loop { + if loop_count == 3 { + panic!("Could not find valid signature block for attested slot {} after three loops", attested_slot); + } if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == signature_slot { - panic!("Could not find any block after the attested header from the attested epoch") + println!("Waiting for signature block for attested header"); + std::thread::sleep(Duration::from_secs(24)); + signature_slot = header.slot + 1; + loop_count += 1; } if let Ok(signature_block) = sync_committee_prover.fetch_block(signature_slot.to_string().as_str()).await @@ -343,11 +352,12 @@ async fn test_prover() { .unwrap(); let finalized_hash_tree_root = finalized_header.clone().hash_tree_root().unwrap(); - println!("{:?}", attested_state.finalized_checkpoint); + println!("{:?}, {}", attested_state.finalized_checkpoint, attested_state.slot); println!( - "{:?}, {:?}", + "{:?}, {:?}, {}", compute_epoch_at_slot(finalized_header.slot), - finalized_hash_tree_root + finalized_hash_tree_root, + finalized_header.slot ); assert_eq!( From 91c4422f071fcfdf8eaf9d97638c8830853dc178 Mon Sep 17 00:00:00 2001 From: David Salami Date: Sat, 25 Feb 2023 17:19:07 +0100 Subject: [PATCH 061/182] hopefully stabilize tests --- primitives/src/types.rs | 11 +++++- prover/src/test.rs | 79 +++++++++++++++++++++++++---------------- verifier/src/lib.rs | 5 +-- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 68484e68c..e565e4bbc 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -107,6 +107,15 @@ pub struct LightClientState { pub next_sync_committee: SyncCommittee, } +/// Finalized header proof +#[derive(Debug, Clone)] +pub struct FinalityProof { + /// The latest finalized epoch + pub epoch: u64, + /// Finalized header proof + pub finality_branch: Vec, +} + /// Data required to advance the state of the light client. #[derive(Debug, Clone)] pub struct LightClientUpdate { @@ -119,7 +128,7 @@ pub struct LightClientUpdate { /// execution payload of the finalized header pub execution_payload: ExecutionPayloadProof, /// Finalized header proof - pub finality_branch: Vec, + pub finality_proof: FinalityProof, /// signature & participation bits pub sync_aggregate: SyncAggregate, /// slot at which signature was produced diff --git a/prover/src/test.rs b/prover/src/test.rs index 77855d542..263d21aef 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -12,7 +12,7 @@ use ethereum_consensus::{ use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; use std::time::Duration; use sync_committee_primitives::{ - types::{DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, + types::{FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, util::compute_fork_version, }; use sync_committee_verifier::verify_sync_committee_attestation; @@ -294,15 +294,31 @@ async fn test_prover() { .unwrap(); let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - let attested_epoch = finality_checkpoint.finalized.epoch + 2; + let mut attested_epoch = finality_checkpoint.finalized.epoch + 2; // Get attested header and the signature slot let mut attested_slot = attested_epoch * SLOTS_PER_EPOCH; + // Due to the fact that all slots in an epoch can be missed we are going to try and fetch + // the attested block from four possible epochs. + let mut attested_epoch_loop_count = 0; let (attested_block_header, signature_block) = loop { + if attested_epoch_loop_count == 4 { + panic!("Could not fetch any block from the attested epoch after going through four epochs, your Eth devnet is fucked") + } + // If we have maxed out the slots in the current epoch and still didn't find any block, + // we move to the next epoch if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == attested_slot { - panic!("Could not find any block from the attested epoch") + // No block was found in attested epoch we move to the next possible attested epoch + println!( + "No slots found in epoch {attested_epoch} Moving to the next possible epoch {}", + attested_epoch + 1 + ); + std::thread::sleep(Duration::from_secs(24)); + attested_epoch += 1; + attested_slot = attested_epoch * SLOTS_PER_EPOCH; + attested_epoch_loop_count += 1; } if let Ok(header) = @@ -311,8 +327,8 @@ async fn test_prover() { let mut signature_slot = header.slot + 1; let mut loop_count = 0; let signature_block = loop { - if loop_count == 3 { - panic!("Could not find valid signature block for attested slot {} after three loops", attested_slot); + if loop_count == 2 { + break None } if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == signature_slot @@ -325,23 +341,32 @@ async fn test_prover() { if let Ok(signature_block) = sync_committee_prover.fetch_block(signature_slot.to_string().as_str()).await { - break signature_block + break Some(signature_block) } signature_slot += 1; }; // If the next block does not have sufficient sync committee participants - if signature_block - .body - .sync_aggregate - .sync_committee_bits - .as_bitslice() - .count_ones() < 2 / 3 * (SYNC_COMMITTEE_SIZE) - { - attested_slot = signature_slot + 1; - println!("Signature block does not have sufficient sync committee participants -> participants {}", signature_block.body.sync_aggregate.sync_committee_bits.as_bitslice().count_ones()); + if let Some(signature_block) = signature_block { + if signature_block + .body + .sync_aggregate + .sync_committee_bits + .as_bitslice() + .count_ones() < (2 * (SYNC_COMMITTEE_SIZE)) / 3 + { + attested_slot += 1; + println!("Signature block does not have sufficient sync committee participants -> participants {}", signature_block.body.sync_aggregate.sync_committee_bits.as_bitslice().count_ones()); + continue + } + break (header, signature_block) + } else { + println!("No signature block found in {attested_epoch} Moving to the next possible epoch {}", attested_epoch + 1); + std::thread::sleep(Duration::from_secs(24)); + attested_epoch += 1; + attested_slot = attested_epoch * SLOTS_PER_EPOCH; + attested_epoch_loop_count += 1; continue } - break (header, signature_block) } attested_slot += 1 }; @@ -353,22 +378,14 @@ async fn test_prover() { let finalized_hash_tree_root = finalized_header.clone().hash_tree_root().unwrap(); println!("{:?}, {}", attested_state.finalized_checkpoint, attested_state.slot); - println!( - "{:?}, {:?}, {}", - compute_epoch_at_slot(finalized_header.slot), - finalized_hash_tree_root, - finalized_header.slot - ); + println!("{:?}, {}", finalized_hash_tree_root, finalized_header.slot); - assert_eq!( - Checkpoint { - epoch: compute_epoch_at_slot(finalized_header.slot), - root: finalized_hash_tree_root, - }, - attested_state.finalized_checkpoint, - ); + assert_eq!(finalized_hash_tree_root, attested_state.finalized_checkpoint.root); - let finality_branch = prove_finalized_header(attested_state.clone()).unwrap(); + let finality_proof = FinalityProof { + epoch: finality_checkpoint.finalized.epoch, + finality_branch: prove_finalized_header(attested_state.clone()).unwrap(), + }; let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); @@ -399,7 +416,7 @@ async fn test_prover() { sync_committee_update, finalized_header, execution_payload: execution_payload_proof, - finality_branch, + finality_proof, sync_aggregate: signature_block.body.sync_aggregate, signature_slot: signature_block.slot, // todo: Prove some ancestry blocks diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 135faff1e..e88e8a0bf 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -38,7 +38,7 @@ pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { - if update.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && + if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && update.sync_committee_update.is_some() && update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize @@ -118,7 +118,7 @@ pub fn verify_sync_committee_attestation( // to match the finalized checkpoint root saved in the state of `attested_header`. // Note that the genesis finalized checkpoint root is represented as a zero hash. let mut finalized_checkpoint = Checkpoint { - epoch: compute_epoch_at_slot(update.finalized_header.slot), + epoch: update.finality_proof.epoch, root: update .finalized_header .clone() @@ -127,6 +127,7 @@ pub fn verify_sync_committee_attestation( }; let branch = update + .finality_proof .finality_branch .iter() .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) From 97a94f160161d04c0ae34ef60121ce7e60589f85 Mon Sep 17 00:00:00 2001 From: David Salami Date: Sun, 26 Feb 2023 08:10:27 +0100 Subject: [PATCH 062/182] tests are stable, add readme --- README.md | 7 +++++++ primitives/src/types.rs | 2 ++ prover/src/test.rs | 13 ++++--------- verifier/src/lib.rs | 1 + 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index eb9389959..ffe672b74 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # ethereum-beacon-light-client Implementation of the Ethereum beacon chain light client in Rust + +# Running the prover tests +**NOTE** +1. To run these tests make sure the latest fork version on your devnet is the BELLATRIX_FORK_VERSION as defined in the mainnet config +2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing + feature flag to match the one that is present in the devnet you are running the tests with +3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. \ No newline at end of file diff --git a/primitives/src/types.rs b/primitives/src/types.rs index e565e4bbc..2d29c7db0 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -102,6 +102,8 @@ pub struct SyncCommitteeUpdate { pub struct LightClientState { /// The latest recorded finalized header pub finalized_header: BeaconBlockHeader, + /// Latest finalized epoch + pub latest_finalized_epoch: u64, // Sync committees corresponding to the finalized header pub current_sync_committee: SyncCommittee, pub next_sync_committee: SyncCommittee, diff --git a/prover/src/test.rs b/prover/src/test.rs index 263d21aef..6765028b0 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -19,13 +19,6 @@ use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; -// **NOTE** -// 1. To run these tests make sure the latest fork version on your devnet is the -// BELLATRIX_FORK_VERSION as defined in the mainnet config -// 2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing -// feature flag to match the one that is present in the devnet you are running the tests with -// 3. Make sure the SLOTS_PER_EPOCH is set to 32 in your beacon node. - const NODE_URL: &'static str = "http://localhost:5052"; #[cfg(test)] @@ -265,6 +258,7 @@ async fn test_prover() { let mut client_state = LightClientState { finalized_header: block_header.clone(), + latest_finalized_epoch: 0, current_sync_committee: state.current_sync_committee, next_sync_committee: state.next_sync_committee, }; @@ -273,8 +267,9 @@ async fn test_prover() { while let Some(_ts) = stream.next().await { let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); if finality_checkpoint.finalized.root == Node::default() || - finality_checkpoint.finalized.epoch <= - compute_epoch_at_slot(client_state.finalized_header.slot) + finality_checkpoint.finalized.epoch <= client_state.latest_finalized_epoch || + finality_checkpoint.finalized.root == + client_state.finalized_header.clone().hash_tree_root().unwrap() { continue } diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index e88e8a0bf..928f92406 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -376,6 +376,7 @@ pub fn verify_sync_committee_attestation( let new_light_client_state = if let Some(sync_committee_update) = update.sync_committee_update { LightClientState { finalized_header: update.finalized_header, + latest_finalized_epoch: update.finality_proof.epoch, current_sync_committee: trusted_state.next_sync_committee, next_sync_committee: sync_committee_update.next_sync_committee, } From a98547b0afef2d60f5e728c0879db9042e2ddce4 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 27 Feb 2023 10:34:39 +0100 Subject: [PATCH 063/182] chore: remove unused imports --- Cargo.lock | 66 +++++++++++++++++++++++++---------------- primitives/src/lib.rs | 3 +- primitives/src/types.rs | 3 +- primitives/src/util.rs | 1 - prover/Cargo.toml | 2 +- prover/src/lib.rs | 63 +++++++++------------------------------ verifier/src/error.rs | 2 +- verifier/src/lib.rs | 4 +-- 8 files changed, 62 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 865fabfcf..127a36bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,9 +132,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" @@ -210,9 +210,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-oid" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "core-foundation" @@ -418,7 +418,7 @@ dependencies = [ [[package]] name = "ethereum-consensus" version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#8be43ea5768297f83acfc22651121154fb1a5874" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#bd19d7c3e7a44810a7f57b5fd00ea55a816222f1" dependencies = [ "async-stream", "bs58", @@ -434,9 +434,9 @@ dependencies = [ "rand", "serde", "serde_json", + "serde_yaml", "sha2 0.9.9", "ssz-rs", - "thiserror", "tokio", "tokio-stream", ] @@ -790,9 +790,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", @@ -857,6 +857,12 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -933,7 +939,6 @@ dependencies = [ "serde", "static_assertions", "unsigned-varint", - "url", ] [[package]] @@ -1255,15 +1260,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "reqwest" version = "0.11.14" @@ -1443,6 +1439,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1571,9 +1579,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1643,16 +1651,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.42.0", ] [[package]] @@ -2090,6 +2097,15 @@ dependencies = [ "tap", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zeroize" version = "1.5.7" diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 7ff490608..10f9e5962 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] - +#[deny(unused_imports)] +#[deny(unused_variables)] extern crate alloc; pub mod types; diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 2d29c7db0..699d5ea48 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -1,9 +1,8 @@ use alloc::vec::Vec; -use base2::Base2; use ethereum_consensus::{ bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, domains::DomainType, - primitives::{Epoch, Hash32, Slot}, + primitives::{Hash32, Slot}, }; pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; diff --git a/primitives/src/util.rs b/primitives/src/util.rs index e090e33b2..9a285734f 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -7,7 +7,6 @@ use ethereum_consensus::{ }, phase0::mainnet::SLOTS_PER_EPOCH, }; -use ssz_rs::Node; /// Calculate the subtree index from the ``generalized_index`` pub fn get_subtree_index(generalized_index: u64) -> u64 { diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 8db82b6b2..133a46c67 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", features=["serde", "std"] } +ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", features=["serde"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 14e395445..c8a2495ba 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -1,14 +1,14 @@ +#[deny(unused_imports)] +#[deny(unused_variables)] mod error; mod responses; mod routes; #[cfg(test)] mod test; -// todo: split up this file - use ethereum_consensus::{ altair::Validator, - bellatrix::{BeaconBlock, BeaconBlockHeader, BeaconState, SignedBeaconBlock, SyncCommittee}, + bellatrix::{BeaconBlock, BeaconBlockHeader, BeaconState, SyncCommittee}, }; use reqwest::Client; @@ -31,9 +31,9 @@ use ethereum_consensus::{ MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, }, - primitives::{BlsPublicKey, Bytes32, Hash32, Slot, ValidatorIndex}, + primitives::{BlsPublicKey, Bytes32, Hash32, ValidatorIndex}, }; -use ssz_rs::{get_generalized_index, GeneralizedIndex, List, Merkleized, Node, Vector}; +use ssz_rs::{List, Merkleized, Node, Vector}; use sync_committee_primitives::{ types::{ AncestryProof, BlockRootsProof, ExecutionPayloadProof, BLOCK_ROOTS_INDEX, @@ -43,34 +43,6 @@ use sync_committee_primitives::{ util::compute_epoch_at_slot, }; -type BeaconBlockType = BeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, ->; - -type SignedBeaconBlockType = SignedBeaconBlock< - MAX_PROPOSER_SLASHINGS, - MAX_VALIDATORS_PER_COMMITTEE, - MAX_ATTESTER_SLASHINGS, - MAX_ATTESTATIONS, - MAX_DEPOSITS, - MAX_VOLUNTARY_EXITS, - SYNC_COMMITTEE_SIZE, - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - MAX_BYTES_PER_TRANSACTION, - MAX_TRANSACTIONS_PER_PAYLOAD, ->; - pub type BeaconStateType = BeaconState< SLOTS_PER_HISTORICAL_ROOT, HISTORICAL_ROOTS_LIMIT, @@ -208,7 +180,7 @@ impl SyncCommitteeProver { let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; let mut validators: List = Default::default(); - for mut validator_index in node_sync_committee.validators.clone() { + for validator_index in node_sync_committee.validators.clone() { // fetches validator based on validator index let validator = self.fetch_validator(state_id.clone(), &validator_index).await?; validators.push(validator); @@ -239,20 +211,13 @@ impl SyncCommitteeProver { } } -fn get_attestation_slots_for_finalized_header( - finalized_header: &BeaconBlockHeader, - slots_per_epoch: u64, -) -> Slot { - let finalized_header_slot = finalized_header.slot; - - // given that an epoch is 32 slots and blocks are finalized every 2 epochs - // so the attested slot for a finalized block is 64 slots away - let attested_slot = finalized_header_slot + (slots_per_epoch * 2); - - attested_slot +pub fn get_attested_epoch(finalized_epoch: u64) -> u64 { + finalized_epoch + 2 } -fn prove_execution_payload(beacon_state: BeaconStateType) -> anyhow::Result { +pub fn prove_execution_payload( + beacon_state: BeaconStateType, +) -> anyhow::Result { let indices = [ EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, @@ -280,12 +245,12 @@ fn prove_execution_payload(beacon_state: BeaconStateType) -> anyhow::Result anyhow::Result> { +pub fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result> { let proof = ssz_rs::generate_proof(state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } -fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { +pub fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; @@ -295,7 +260,7 @@ fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> .collect()) } -fn prove_block_roots_proof( +pub fn prove_block_roots_proof( state: BeaconStateType, mut header: BeaconBlockHeader, ) -> anyhow::Result { diff --git a/verifier/src/error.rs b/verifier/src/error.rs index dcd6e74b5..4fd88fabc 100644 --- a/verifier/src/error.rs +++ b/verifier/src/error.rs @@ -25,7 +25,7 @@ impl Display for Error { }, Error::InvalidUpdate => write!(f, "Invalid update"), Error::DomainError => write!(f, "Couldn't get domain"), - Error::FastAggregateError(err) => write!(f, "Fast aggregate error"), + Error::FastAggregateError(err) => write!(f, "Fast aggregate error {:?}", err), Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), Error::InvalidRoot => write!(f, "Invalid root"), Error::MerkleizationError => write!(f, "Merkleization error"), diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 928f92406..833715e81 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] - +#[deny(unused_imports)] +#[deny(unused_variables)] extern crate alloc; pub mod error; @@ -7,7 +8,6 @@ pub mod error; use crate::error::Error; use alloc::vec::Vec; use base2::Base2; -use core::{borrow::Borrow, fmt::Display}; use ethereum_consensus::{ bellatrix::{compute_domain, mainnet::SYNC_COMMITTEE_SIZE, Checkpoint}, primitives::Root, From a44c89fe8a8e6bd2e6cc238180b0dbab2eee0eea Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 27 Feb 2023 12:09:19 +0100 Subject: [PATCH 064/182] add a todo for ancestry blocks --- prover/src/test.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index 6765028b0..2bce45093 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -12,7 +12,7 @@ use ethereum_consensus::{ use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; use std::time::Duration; use sync_committee_primitives::{ - types::{FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, + types::{AncestorBlock, FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, util::compute_fork_version, }; use sync_committee_verifier::verify_sync_committee_attestation; @@ -405,6 +405,33 @@ async fn test_prover() { None }; + // todo: Reenable when proofs are implemented for Lists adn Vectors + // let mut i = finalized_header.slot - 1; + // let mut ancestor_blocks = vec![]; + // while ancestor_blocks.len() < 5 { + // if (finalized_header.slot - i) > 100 { + // break + // } + // if let Ok(ancestor_header) = + // sync_committee_prover.fetch_header(i.to_string().as_str()).await + // { + // let ancestry_proof = + // prove_block_roots_proof(finalized_state.clone(), ancestor_header.clone()) + // .unwrap(); + // let header_state = + // sync_committee_prover.fetch_beacon_state(i.to_string().as_str()).await.unwrap(); + // let execution_payload_proof = prove_execution_payload(header_state).unwrap(); + // ancestor_blocks.push(AncestorBlock { + // header: ancestor_header, + // execution_payload: execution_payload_proof, + // ancestry_proof, + // }) + // } + // i -= 1; + // } + // + // println!("Ancestor block count {}", ancestor_blocks.len()); + // construct light client let light_client_update = LightClientUpdate { attested_header: attested_block_header, @@ -414,7 +441,6 @@ async fn test_prover() { finality_proof, sync_aggregate: signature_block.body.sync_aggregate, signature_slot: signature_block.slot, - // todo: Prove some ancestry blocks ancestor_blocks: vec![], }; From a58f940f0ce6b003b2781925530f8da420f9d1e2 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 27 Feb 2023 12:13:44 +0100 Subject: [PATCH 065/182] chore --- primitives/src/lib.rs | 4 ++-- prover/src/lib.rs | 4 ++-- prover/src/test.rs | 2 +- verifier/src/lib.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 10f9e5962..23d346f32 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[deny(unused_imports)] -#[deny(unused_variables)] +#[warn(unused_imports)] +#[warn(unused_variables)] extern crate alloc; pub mod types; diff --git a/prover/src/lib.rs b/prover/src/lib.rs index c8a2495ba..28848958a 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -1,5 +1,5 @@ -#[deny(unused_imports)] -#[deny(unused_variables)] +#[warn(unused_imports)] +#[warn(unused_variables)] mod error; mod responses; mod routes; diff --git a/prover/src/test.rs b/prover/src/test.rs index 2bce45093..3b94e8014 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -405,7 +405,7 @@ async fn test_prover() { None }; - // todo: Reenable when proofs are implemented for Lists adn Vectors + // todo: Enable when proofs are implemented for Lists adn Vectors // let mut i = finalized_header.slot - 1; // let mut ancestor_blocks = vec![]; // while ancestor_blocks.len() < 5 { diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 833715e81..ac6a6df14 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[deny(unused_imports)] -#[deny(unused_variables)] +#[warn(unused_imports)] +#[warn(unused_variables)] extern crate alloc; pub mod error; From edac99d02fa1bcd9489fef73c76f5672ec6f512c Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 27 Feb 2023 12:15:44 +0100 Subject: [PATCH 066/182] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffe672b74..5e3496e7a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# ethereum-beacon-light-client -Implementation of the Ethereum beacon chain light client in Rust +# Ethereum beacon light client verifier +Implementation of the Ethereum beacon light client verifier in Rust # Running the prover tests **NOTE** From 502e3976b9f6015463adf483c67ea7b2f55df3c0 Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 2 Mar 2023 10:23:38 +0100 Subject: [PATCH 067/182] add some needed derives to types --- primitives/src/types.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 699d5ea48..3038dacdd 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -22,7 +22,7 @@ pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("6034f557b4560fc549ac0e2c63269deb07bfac7bf2bbd0b8b7d4d321240bffd9"); /// This holds the relevant data required to prove the state root in the execution payload. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct ExecutionPayloadProof { /// The state root in the `ExecutionPayload` which represents the commitment to /// the ethereum world state in the yellow paper. @@ -37,7 +37,7 @@ pub struct ExecutionPayloadProof { /// Holds the neccessary proofs required to verify a header in the `block_roots` field /// either in [`BeaconState`] or [`HistoricalBatch`]. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockRootsProof { /// Generalized index of the header in the `block_roots` list. pub block_header_index: u64, @@ -47,7 +47,7 @@ pub struct BlockRootsProof { /// The block header ancestry proof, this is an enum because the header may either exist in /// `state.block_roots` or `state.historical_roots`. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum AncestryProof { /// This variant defines the proof data for a beacon chain header in the `state.block_roots` BlockRoots { @@ -76,7 +76,7 @@ pub enum AncestryProof { /// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized /// header. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct AncestorBlock { /// The actual beacon chain header pub header: BeaconBlockHeader, @@ -88,7 +88,7 @@ pub struct AncestorBlock { /// Holds the latest sync committee as well as an ssz proof for it's existence /// in a finalized header. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct SyncCommitteeUpdate { // actual sync committee pub next_sync_committee: SyncCommittee, @@ -97,7 +97,7 @@ pub struct SyncCommitteeUpdate { } /// Minimum state required by the light client to validate new sync committee attestations -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct LightClientState { /// The latest recorded finalized header pub finalized_header: BeaconBlockHeader, @@ -109,7 +109,7 @@ pub struct LightClientState { } /// Finalized header proof -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct FinalityProof { /// The latest finalized epoch pub epoch: u64, @@ -118,7 +118,7 @@ pub struct FinalityProof { } /// Data required to advance the state of the light client. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct LightClientUpdate { /// the header that the sync committee signed pub attested_header: BeaconBlockHeader, From 471fe7682974fcc66585d0a6db2813ec393a0845 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 6 Mar 2023 16:10:50 +0100 Subject: [PATCH 068/182] enable ancestry proofd --- Cargo.lock | 46 +++++++++++++++++------------------ primitives/src/util.rs | 2 +- prover/src/lib.rs | 20 ++++++++-------- prover/src/test.rs | 54 +++++++++++++++++++++--------------------- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 127a36bf5..0d9d87dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ "bytes", "fnv", @@ -811,9 +811,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -1326,9 +1326,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.36.8" +version = "0.36.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" dependencies = [ "bitflags", "errno", @@ -1340,9 +1340,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" @@ -1418,9 +1418,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -1521,9 +1521,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1542,7 +1542,7 @@ dependencies = [ [[package]] name = "ssz-rs" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f85f088552ee45c4c6080eede3fb57d47bff92e3" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f25813944f80ba7c572a00900c6de316eb8a50c8" dependencies = [ "as-any", "bitvec", @@ -1558,7 +1558,7 @@ dependencies = [ [[package]] name = "ssz-rs-derive" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f85f088552ee45c4c6080eede3fb57d47bff92e3" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f25813944f80ba7c572a00900c6de316eb8a50c8" dependencies = [ "proc-macro2", "quote", @@ -1673,18 +1673,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", @@ -1708,9 +1708,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -1723,7 +1723,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1827,9 +1827,9 @@ checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" diff --git a/primitives/src/util.rs b/primitives/src/util.rs index 9a285734f..8be6d1474 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -36,7 +36,7 @@ pub fn compute_fork_version(epoch: u64) -> [u8; 4] { } #[cfg(feature = "testing")] -pub fn compute_fork_version(epoch: u64) -> [u8; 4] { +pub fn compute_fork_version(_epoch: u64) -> [u8; 4] { BELLATRIX_FORK_VERSION } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 28848958a..5b3b75cd3 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -216,7 +216,7 @@ pub fn get_attested_epoch(finalized_epoch: u64) -> u64 { } pub fn prove_execution_payload( - beacon_state: BeaconStateType, + mut beacon_state: BeaconStateType, ) -> anyhow::Result { let indices = [ EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, @@ -224,7 +224,7 @@ pub fn prove_execution_payload( ]; // generate multi proofs let multi_proof = ssz_rs::generate_proof( - beacon_state.latest_execution_payload_header.clone(), + &mut beacon_state.latest_execution_payload_header, indices.as_slice(), )?; @@ -236,7 +236,7 @@ pub fn prove_execution_payload( .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect(), execution_payload_branch: ssz_rs::generate_proof( - beacon_state, + &mut beacon_state, &[EXECUTION_PAYLOAD_INDEX as usize], )? .into_iter() @@ -245,14 +245,14 @@ pub fn prove_execution_payload( }) } -pub fn prove_sync_committee_update(state: BeaconStateType) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; +pub fn prove_sync_committee_update(mut state: BeaconStateType) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(&mut state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } -pub fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result> { +pub fn prove_finalized_header(mut state: BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; - let proof = ssz_rs::generate_proof(state.clone(), indices.as_slice())?; + let proof = ssz_rs::generate_proof(&mut state, indices.as_slice())?; Ok(proof .into_iter() @@ -261,7 +261,7 @@ pub fn prove_finalized_header(state: BeaconStateType) -> anyhow::Result anyhow::Result { // Check if block root should still be part of the block roots vector on the beacon state @@ -283,7 +283,7 @@ pub fn prove_block_roots_proof( .position(|root| root == &block_root) .expect("Block root should exist in block_roots"); - let proof = ssz_rs::generate_proof(state.block_roots.clone(), &[block_index])?; + let proof = ssz_rs::generate_proof(&mut state.block_roots, &[block_index])?; let block_roots_proof = BlockRootsProof { block_header_index: block_index as u64, @@ -295,7 +295,7 @@ pub fn prove_block_roots_proof( .collect(), }; - let block_roots_branch = ssz_rs::generate_proof(state, &[BLOCK_ROOTS_INDEX as usize])?; + let block_roots_branch = ssz_rs::generate_proof(&mut state, &[BLOCK_ROOTS_INDEX as usize])?; Ok(AncestryProof::BlockRoots { block_roots_proof, block_roots_branch: block_roots_branch diff --git a/prover/src/test.rs b/prover/src/test.rs index 3b94e8014..deaa5591e 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -114,7 +114,7 @@ async fn test_finalized_header() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); - let proof = ssz_rs::generate_proof(state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); + let proof = ssz_rs::generate_proof(&mut state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); let leaves = vec![Node::from_bytes( state @@ -405,32 +405,32 @@ async fn test_prover() { None }; - // todo: Enable when proofs are implemented for Lists adn Vectors - // let mut i = finalized_header.slot - 1; - // let mut ancestor_blocks = vec![]; - // while ancestor_blocks.len() < 5 { - // if (finalized_header.slot - i) > 100 { - // break - // } - // if let Ok(ancestor_header) = - // sync_committee_prover.fetch_header(i.to_string().as_str()).await - // { - // let ancestry_proof = - // prove_block_roots_proof(finalized_state.clone(), ancestor_header.clone()) - // .unwrap(); - // let header_state = - // sync_committee_prover.fetch_beacon_state(i.to_string().as_str()).await.unwrap(); - // let execution_payload_proof = prove_execution_payload(header_state).unwrap(); - // ancestor_blocks.push(AncestorBlock { - // header: ancestor_header, - // execution_payload: execution_payload_proof, - // ancestry_proof, - // }) - // } - // i -= 1; - // } - // - // println!("Ancestor block count {}", ancestor_blocks.len()); + let mut i = finalized_header.slot - 1; + let mut ancestor_blocks = vec![]; + while ancestor_blocks.len() < 5 { + if (finalized_header.slot - i) > 100 { + break + } + if let Ok(ancestor_header) = + sync_committee_prover.fetch_header(i.to_string().as_str()).await + { + let ancestry_proof = + prove_block_roots_proof(finalized_state.clone(), ancestor_header.clone()) + .unwrap(); + let header_state = + sync_committee_prover.fetch_beacon_state(i.to_string().as_str()).await.unwrap(); + let execution_payload_proof = prove_execution_payload(header_state).unwrap(); + ancestor_blocks.push(AncestorBlock { + header: ancestor_header, + execution_payload: execution_payload_proof, + ancestry_proof, + }) + } + i -= 1; + } + + println!("\nAncestor blocks count: \n {:?} \n", ancestor_blocks.len()); + println!("\nAncestor blocks count: \n {:#?} \n", ancestor_blocks); // construct light client let light_client_update = LightClientUpdate { From a033d10d9ad298151f51702135f152b4aaf57a52 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 6 Mar 2023 16:11:52 +0100 Subject: [PATCH 069/182] nit --- prover/src/test.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index deaa5591e..f8981455f 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -430,7 +430,6 @@ async fn test_prover() { } println!("\nAncestor blocks count: \n {:?} \n", ancestor_blocks.len()); - println!("\nAncestor blocks count: \n {:#?} \n", ancestor_blocks); // construct light client let light_client_update = LightClientUpdate { From 6e9d294d2e6f3965dbbc615a8dfad3f01c0ad23c Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 6 Mar 2023 16:12:27 +0100 Subject: [PATCH 070/182] chore --- prover/src/test.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index f8981455f..5d6367b3c 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -236,8 +236,6 @@ async fn test_sync_committee_update_proof() { assert!(is_merkle_branch_valid); } -// todo: cloning the beacon state might expensive in production or testnet, if we can modify -// generate_proof function take a reference for the object that would be better. #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] From ecd888b34edcbd51fcf97cb09906abffd6764ee6 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 7 Mar 2023 23:38:05 +0100 Subject: [PATCH 071/182] readme --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5e3496e7a..356b1cb22 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,52 @@ -# Ethereum beacon light client verifier -Implementation of the Ethereum beacon light client verifier in Rust +#

Sync-committee-rs ⚙️

+ +

+ Ethereum Beacon Chain Light Client SDK in Rust +
+ ⚠️ Beta Software ⚠️ +

+ +
+ +The sync-committee-rs is the implementation of the Ethereum beacon light client verifier in Rust. This follows the specifications +initially defined by Seun Lanlege [here](https://polytopelabs.notion.site/ICS-15-ethereum-beacon-chain-light-client-specification-for-IBC-9c28567b02424585b4deceeb21b9beaf) + + +This library consists of +- ✅ The primitives. +- ✅ The prover. +- ✅ The verifier + + +## The primitives +Consists of the types and structs as defined and described in the spec mentioned earlier. It also consists of the utility functions +to be used in the verifier and prover. + +## The prover +Consists of the various proof generations for the ethereum beacon chain structs/types such as: + +- Execution payload +- Finalized header +- Block roots +- Sync committee update + +The prover also defines the function for fetching various ethereum types from the beacon chain node which can be used to generate proofs. + + +## The verifier +This consist of the major function for verifying sync committee attestation. It also defines the different error that can occur while verifying. + + +# Major Depedencies +The major dependencies for this SDK/Library are: + +- [ssz-rs](https://github.com/ralexstokes/ssz-rs) +- [ethereum-consensus](https://github.com/ralexstokes/ethereum-consensus) + # Running the prover tests **NOTE** 1. To run these tests make sure the latest fork version on your devnet is the BELLATRIX_FORK_VERSION as defined in the mainnet config 2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing feature flag to match the one that is present in the devnet you are running the tests with -3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. \ No newline at end of file +3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. From 6e768854ca35a14cbc98d6cebc0c44dd0946bec0 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 11 Mar 2023 15:56:28 +0100 Subject: [PATCH 072/182] explain prover functions --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 356b1cb22..b99535924 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,16 @@ Consists of the various proof generations for the ethereum beacon chain structs/ - Block roots - Sync committee update -The prover also defines the function for fetching various ethereum types from the beacon chain node which can be used to generate proofs. - +The prover also defines the function for fetching various ethereum types from the beacon chain node using the `SyncCommitteeProver` which can be used to generate proofs. +The various function it defines are: + +- fetch_finalized_checkpoint: Fetches the finalized checkpoint for the `head` via this endpoint `eth/v1/beacon/states/{state_id}/finality_checkpoints` +- fetch_header: Fetches the header via the endpoint `/eth/v1/beacon/headers/{block_id}` +- fetch_block: Fetches the Beacon block via the endpoint `/eth/v2/beacon/blocks/{block_id}` +- fetch_sync_committee: Fetches the sync_committee via the endpoint `/eth/v1/beacon/states/{state_id}/sync_committees` +- fetch_validator: Fetches the node validator for a particular state via the endpoint `/eth/v1/beacon/states/{state_id}/validators/{validator_index}` +- fetch_beacon_state: Fetches the Beacon state via the endpoint `/eth/v2/debug/beacon/states/{state_id}` +- fetch_processed_sync_committee: Constructs the actual `SyncCommittee` after aggregating the validator `public_keys` ## The verifier This consist of the major function for verifying sync committee attestation. It also defines the different error that can occur while verifying. From 4e792163a3abedbefbc3146a2098d30980cdedd0 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 11 Mar 2023 16:22:12 +0100 Subject: [PATCH 073/182] explain verifier function --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index b99535924..c5858fed5 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,23 @@ The various function it defines are: ## The verifier This consist of the major function for verifying sync committee attestation. It also defines the different error that can occur while verifying. +This contains the `verify_sync_committee_attestation` function. The purpose of this function is to verify that a sync committee attestation, represented by the update argument, +is valid with respect to the current trusted state, represented by the trusted_state argument. +If the attestation is valid, the function returns the updated trusted state; otherwise, it returns an error. + +Detailed explanation of the `verify_sync_committee_attestation` goes as follows: + +- It checks whether the update contains the correct number of finality and sync committee branches. If not, it returns an error. +- It verifies whether the number of participants in the sync committee aggregate signature is greater than or equal to two-thirds of the total number of participants. If not, it returns an error. +- It verifies whether the update skips a sync committee period or not. If it does, it returns an error. +- It checks whether the update is relevant by checking whether it attests to a later slot than the trusted_state or contains the next sync committee. If not, it returns an error. +- It verifies the sync committee aggregate signature by checking that it is valid for the given sync committee participants and domain. If not, it returns an error. +- It verifies the finality_branch of the update by checking whether it confirms the finalized_header that matches the finalized checkpoint root saved in the trusted_state. If not, it returns an error. +- It verifies the ancestry proofs of a sync committee attestation update. It iterates over the ancestor blocks in the update and for each block, checks if its ancestry proof is either a BlockRoots proof or a HistoricalRoots proof. It then calculates the merkle roots and checks if the merkle branches are valid using the is_valid_merkle_branch function. +If any of the merkle branches are invalid, the function returns an error. +- It verifies the associated execution header of the finalized beacon header. +- If all the above checks pass, the function returns a new LightClientState object with the updated trusted_state. + # Major Depedencies The major dependencies for this SDK/Library are: From d8e693ecbe02ec3717fa5cdaebc8cf9c54a6be63 Mon Sep 17 00:00:00 2001 From: Damilare Date: Sat, 11 Mar 2023 16:41:35 +0100 Subject: [PATCH 074/182] include the primitive types --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index c5858fed5..a179ad62e 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,17 @@ This library consists of Consists of the types and structs as defined and described in the spec mentioned earlier. It also consists of the utility functions to be used in the verifier and prover. +The verious type primitives defined are: + +- [`ExecutionPayloadProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L26) +- [`BlockRootsProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L40) +- [`AncestryProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L51) +- [`AncestorBlock`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L80) +- [`SyncCommitteeUpdate`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L92) +- [`LightClientState`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L101) +- [`FinalityProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L113) +- [`LightClientUpdate`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L122) + ## The prover Consists of the various proof generations for the ethereum beacon chain structs/types such as: From 83135a53cc56d2d0a5ff75a2b73a2d513a4da0ee Mon Sep 17 00:00:00 2001 From: Damilare Date: Mon, 13 Mar 2023 10:39:59 +0100 Subject: [PATCH 075/182] fix readme --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a179ad62e..c845ddf24 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@
The sync-committee-rs is the implementation of the Ethereum beacon light client verifier in Rust. This follows the specifications -initially defined by Seun Lanlege [here](https://polytopelabs.notion.site/ICS-15-ethereum-beacon-chain-light-client-specification-for-IBC-9c28567b02424585b4deceeb21b9beaf) +initially defined [here](https://polytopelabs.notion.site/ICS-15-ethereum-beacon-chain-light-client-specification-for-IBC-9c28567b02424585b4deceeb21b9beaf) This library consists of @@ -67,10 +67,9 @@ Detailed explanation of the `verify_sync_committee_attestation` goes as follows: - It checks whether the update is relevant by checking whether it attests to a later slot than the trusted_state or contains the next sync committee. If not, it returns an error. - It verifies the sync committee aggregate signature by checking that it is valid for the given sync committee participants and domain. If not, it returns an error. - It verifies the finality_branch of the update by checking whether it confirms the finalized_header that matches the finalized checkpoint root saved in the trusted_state. If not, it returns an error. -- It verifies the ancestry proofs of a sync committee attestation update. It iterates over the ancestor blocks in the update and for each block, checks if its ancestry proof is either a BlockRoots proof or a HistoricalRoots proof. It then calculates the merkle roots and checks if the merkle branches are valid using the is_valid_merkle_branch function. -If any of the merkle branches are invalid, the function returns an error. -- It verifies the associated execution header of the finalized beacon header. -- If all the above checks pass, the function returns a new LightClientState object with the updated trusted_state. +- It optionally verifies ancestry proofs if they are present. +- It verifies the associated execution payload of the finalized beacon header. +- If all the checks pass, the function returns a new LightClientState. # Major Depedencies @@ -86,3 +85,7 @@ The major dependencies for this SDK/Library are: 2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing feature flag to match the one that is present in the devnet you are running the tests with 3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. + + +## License +This library is licensed under the [Apache 2.0 License](./LICENSE), Copyright (c) 2023 Polytope Labs. From 5fe2c738152adec862bc88db8e92fdaa22852d8a Mon Sep 17 00:00:00 2001 From: Damilare Date: Mon, 13 Mar 2023 11:50:52 +0100 Subject: [PATCH 076/182] generalized index generation test --- README.md | 2 +- prover/src/test.rs | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e3496e7a..ded874467 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ Implementation of the Ethereum beacon light client verifier in Rust 1. To run these tests make sure the latest fork version on your devnet is the BELLATRIX_FORK_VERSION as defined in the mainnet config 2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing feature flag to match the one that is present in the devnet you are running the tests with -3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. \ No newline at end of file +3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. diff --git a/prover/src/test.rs b/prover/src/test.rs index 5d6367b3c..4c0e23e2f 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -9,7 +9,7 @@ use ethereum_consensus::{ altair::Checkpoint, bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, state_transition::Context, }; -use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; +use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized, get_generalized_index, SszVariableOrIndex}; use std::time::Duration; use sync_committee_primitives::{ types::{AncestorBlock, FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, @@ -133,6 +133,23 @@ async fn test_finalized_header() { assert_eq!(root, state.hash_tree_root().unwrap()); } +#[cfg(test)] +#[allow(non_snake_case)] +#[actix_rt::test] +async fn test_execution_payload_header_timestamp() { + let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let mut state = sync_committee_prover.fetch_beacon_state("finalized").await.unwrap(); + + let generalized_index = get_generalized_index(&state.latest_execution_payload_header.timestamp, &[SszVariableOrIndex::Name("finalized_checkpoint")]); + dbg!(generalized_index); + let proof = ssz_rs::generate_proof(state.latest_execution_payload_header.timestamp.clone(), &vec![generalized_index]); + + + //let leaves = vec![Node::from_bytes(state.finalized_checkpoint.hash_tree_root().unwrap().as_ref().try_into().unwrap())]; + //let root = calculate_multi_merkle_root(&leaves, &proof.unwrap(), &[GeneralizedIndex(generalized_index)]); + //assert_eq!(root, state.hash_tree_root().unwrap()); +} + #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] From 9b47ef7b4d9aad13cbcc885bbd2b1ba8c56069db Mon Sep 17 00:00:00 2001 From: Damilare Date: Mon, 13 Mar 2023 11:53:18 +0100 Subject: [PATCH 077/182] remove unwanted explanations --- README.md | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/README.md b/README.md index c845ddf24..8251adc6a 100644 --- a/README.md +++ b/README.md @@ -20,18 +20,7 @@ This library consists of ## The primitives Consists of the types and structs as defined and described in the spec mentioned earlier. It also consists of the utility functions -to be used in the verifier and prover. - -The verious type primitives defined are: - -- [`ExecutionPayloadProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L26) -- [`BlockRootsProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L40) -- [`AncestryProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L51) -- [`AncestorBlock`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L80) -- [`SyncCommitteeUpdate`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L92) -- [`LightClientState`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L101) -- [`FinalityProof`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L113) -- [`LightClientUpdate`](https://github.com/polytope-labs/sync-committee-rs/blob/main/primitives/src/types.rs#L122) +to be used in the verifier and prover, which also defined in the [spec](https://polytopelabs.notion.site/ICS-15-ethereum-beacon-chain-light-client-specification-for-IBC-9c28567b02424585b4deceeb21b9beaf) ## The prover Consists of the various proof generations for the ethereum beacon chain structs/types such as: @@ -41,17 +30,6 @@ Consists of the various proof generations for the ethereum beacon chain structs/ - Block roots - Sync committee update -The prover also defines the function for fetching various ethereum types from the beacon chain node using the `SyncCommitteeProver` which can be used to generate proofs. -The various function it defines are: - -- fetch_finalized_checkpoint: Fetches the finalized checkpoint for the `head` via this endpoint `eth/v1/beacon/states/{state_id}/finality_checkpoints` -- fetch_header: Fetches the header via the endpoint `/eth/v1/beacon/headers/{block_id}` -- fetch_block: Fetches the Beacon block via the endpoint `/eth/v2/beacon/blocks/{block_id}` -- fetch_sync_committee: Fetches the sync_committee via the endpoint `/eth/v1/beacon/states/{state_id}/sync_committees` -- fetch_validator: Fetches the node validator for a particular state via the endpoint `/eth/v1/beacon/states/{state_id}/validators/{validator_index}` -- fetch_beacon_state: Fetches the Beacon state via the endpoint `/eth/v2/debug/beacon/states/{state_id}` -- fetch_processed_sync_committee: Constructs the actual `SyncCommittee` after aggregating the validator `public_keys` - ## The verifier This consist of the major function for verifying sync committee attestation. It also defines the different error that can occur while verifying. From 6df807a85700caa65200a2d20111c71de3536d74 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 13 Mar 2023 12:14:20 +0100 Subject: [PATCH 078/182] init --- .gitignore | 3 +++ Cargo.toml | 32 ++++++++++++++++++++++++ src/lib.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3ab5292b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +.idea diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..6b29d1ac1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "pallet-ismp" +version = "0.1.0" +edition = "2021" + + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..cabb1bf2a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,71 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; + +// Definition of the pallet logic, to be aggregated at runtime definition through +// `construct_runtime`. +#[frame_support::pallet] +pub mod pallet { + // Import various types used to declare pallet in scope. + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// Our pallet's configuration trait. All our types and constants go in here. If the + /// pallet is dependent on specific other pallets, then their configuration traits + /// should be added to our implied traits list. + /// + /// `frame_system::Config` should always be included. + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and + // method. + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> Weight { + Weight::zero() + } + + fn on_finalize(_n: T::BlockNumber) {} + + fn offchain_worker(_n: T::BlockNumber) {} + } + + #[pallet::call] + impl Pallet {} + + /// Events are a simple means of reporting specific conditions and + /// circumstances that have happened that users, Dapps and/or chain explorers would find + /// interesting and otherwise difficult to detect. + #[pallet::event] + /// This attribute generate the function `deposit_event` to deposit one of this pallet event, + /// it is optional, it is also possible to provide a custom implementation. + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event {} +} From 32f812f2ffdaa50aaf914a25a4b1cf60719cf160 Mon Sep 17 00:00:00 2001 From: Damilare Date: Mon, 13 Mar 2023 12:38:07 +0100 Subject: [PATCH 079/182] add apache license --- LICENSE | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..e83dc12de --- /dev/null +++ b/LICENSE @@ -0,0 +1,51 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS From b24e8a90e753dc259a2d9eb18156fa3e6536768e Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Mon, 13 Mar 2023 15:51:48 +0100 Subject: [PATCH 080/182] updates --- README.md | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 8251adc6a..6a8c328a5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#

Sync-committee-rs ⚙️

+#

sync-committee-rs ⚙️

Ethereum Beacon Chain Light Client SDK in Rust @@ -8,8 +8,7 @@
-The sync-committee-rs is the implementation of the Ethereum beacon light client verifier in Rust. This follows the specifications -initially defined [here](https://polytopelabs.notion.site/ICS-15-ethereum-beacon-chain-light-client-specification-for-IBC-9c28567b02424585b4deceeb21b9beaf) +The sync-committee-rs is the implementation of the Ethereum beacon light client verifier in Rust. This is based on the research done here: https://research.polytope.technology/ethereum-light-client This library consists of @@ -20,7 +19,7 @@ This library consists of ## The primitives Consists of the types and structs as defined and described in the spec mentioned earlier. It also consists of the utility functions -to be used in the verifier and prover, which also defined in the [spec](https://polytopelabs.notion.site/ICS-15-ethereum-beacon-chain-light-client-specification-for-IBC-9c28567b02424585b4deceeb21b9beaf) +to be used in the verifier and prover. ## The prover Consists of the various proof generations for the ethereum beacon chain structs/types such as: @@ -31,23 +30,7 @@ Consists of the various proof generations for the ethereum beacon chain structs/ - Sync committee update ## The verifier -This consist of the major function for verifying sync committee attestation. It also defines the different error that can occur while verifying. - -This contains the `verify_sync_committee_attestation` function. The purpose of this function is to verify that a sync committee attestation, represented by the update argument, -is valid with respect to the current trusted state, represented by the trusted_state argument. -If the attestation is valid, the function returns the updated trusted state; otherwise, it returns an error. - -Detailed explanation of the `verify_sync_committee_attestation` goes as follows: - -- It checks whether the update contains the correct number of finality and sync committee branches. If not, it returns an error. -- It verifies whether the number of participants in the sync committee aggregate signature is greater than or equal to two-thirds of the total number of participants. If not, it returns an error. -- It verifies whether the update skips a sync committee period or not. If it does, it returns an error. -- It checks whether the update is relevant by checking whether it attests to a later slot than the trusted_state or contains the next sync committee. If not, it returns an error. -- It verifies the sync committee aggregate signature by checking that it is valid for the given sync committee participants and domain. If not, it returns an error. -- It verifies the finality_branch of the update by checking whether it confirms the finalized_header that matches the finalized checkpoint root saved in the trusted_state. If not, it returns an error. -- It optionally verifies ancestry proofs if they are present. -- It verifies the associated execution payload of the finalized beacon header. -- If all the checks pass, the function returns a new LightClientState. +This exports a single function for verifying ethereum's sync committee attestation. # Major Depedencies From e8d08e7c8561fc6be97b9890ac8c9200f70488fe Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Mon, 13 Mar 2023 15:53:02 +0100 Subject: [PATCH 081/182] updates --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6a8c328a5..91c984208 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@
-The sync-committee-rs is the implementation of the Ethereum beacon light client verifier in Rust. This is based on the research done here: https://research.polytope.technology/ethereum-light-client +The sync-committee-rs is the implementation of the Ethereum beacon light client prover & verifier in Rust. This is based on the research done here: https://research.polytope.technology/ethereum-light-client This library consists of @@ -17,11 +17,11 @@ This library consists of - ✅ The verifier -## The primitives +## primitives Consists of the types and structs as defined and described in the spec mentioned earlier. It also consists of the utility functions to be used in the verifier and prover. -## The prover +## prover Consists of the various proof generations for the ethereum beacon chain structs/types such as: - Execution payload @@ -29,7 +29,7 @@ Consists of the various proof generations for the ethereum beacon chain structs/ - Block roots - Sync committee update -## The verifier +## verifier This exports a single function for verifying ethereum's sync committee attestation. From 4db966e265601ec1670a538aa8f1ce92d0718a7f Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 14 Mar 2023 00:31:26 +0100 Subject: [PATCH 082/182] include execution payload timestamp in execution payload proof --- Cargo.lock | 2113 --------------------------------------- Cargo.toml | 6 - primitives/Cargo.toml | 6 +- primitives/src/types.rs | 3 + prover/Cargo.toml | 4 +- prover/src/lib.rs | 18 +- prover/src/test.rs | 48 +- verifier/Cargo.toml | 6 +- verifier/src/lib.rs | 6 +- 9 files changed, 64 insertions(+), 2146 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 0d9d87dcb..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,2113 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-rt" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "amcl" -version = "0.3.0" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" - -[[package]] -name = "anyhow" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "as-any" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" - -[[package]] -name = "async-stream" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base2" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" -dependencies = [ - "int 0.2.11", -] - -[[package]] -name = "base2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" -dependencies = [ - "int 0.3.0", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.6", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enr" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" -dependencies = [ - "base64 0.13.1", - "bs58", - "bytes", - "hex", - "k256", - "log", - "rand", - "rlp", - "serde", - "sha3", - "zeroize", -] - -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - -[[package]] -name = "ethereum-consensus" -version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=dami/no-std-support#bd19d7c3e7a44810a7f57b5fd00ea55a816222f1" -dependencies = [ - "async-stream", - "bs58", - "enr", - "error-chain", - "getrandom", - "hashbrown 0.13.2", - "hex", - "integer-sqrt", - "milagro_bls", - "multiaddr", - "multihash", - "rand", - "serde", - "serde_json", - "serde_yaml", - "sha2 0.9.9", - "ssz-rs", - "tokio", - "tokio-stream", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures-channel" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" - -[[package]] -name = "futures-sink" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" - -[[package]] -name = "futures-task" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" - -[[package]] -name = "futures-util" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "int" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" -dependencies = [ - "num-traits", -] - -[[package]] -name = "int" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" -dependencies = [ - "num-traits", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" -dependencies = [ - "num-traits", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "ipnet" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" - -[[package]] -name = "is-terminal" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "milagro_bls" -version = "1.5.1" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" -dependencies = [ - "amcl", - "hex", - "lazy_static", - "rand", - "zeroize", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.45.0", -] - -[[package]] -name = "multiaddr" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" -dependencies = [ - "arrayref", - "bs58", - "byteorder", - "data-encoding", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", -] - -[[package]] -name = "multihash" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" -dependencies = [ - "core2", - "digest 0.10.6", - "multihash-derive", - "sha2 0.10.6", - "unsigned-varint", -] - -[[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "reqwest" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" -dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustix" -version = "0.36.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" -dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" -dependencies = [ - "digest 0.10.6", - "keccak", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core", -] - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "ssz-rs" -version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f25813944f80ba7c572a00900c6de316eb8a50c8" -dependencies = [ - "as-any", - "bitvec", - "hex", - "itertools", - "num-bigint", - "serde", - "sha2 0.9.9", - "ssz-rs-derive", - "thiserror", -] - -[[package]] -name = "ssz-rs-derive" -version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=seun/ssz-merkle-multi-proof-phase-1#f25813944f80ba7c572a00900c6de316eb8a50c8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync-committee-primitives" -version = "0.1.0" -dependencies = [ - "base2 0.3.1", - "ethereum-consensus", - "hex-literal", - "ssz-rs", -] - -[[package]] -name = "sync-committee-prover" -version = "0.1.0" -dependencies = [ - "actix-rt", - "anyhow", - "async-stream", - "base2 0.2.2", - "env_logger", - "ethereum-consensus", - "hex", - "reqwest", - "serde", - "serde_json", - "ssz-rs", - "sync-committee-primitives", - "sync-committee-verifier", - "tokio", - "tokio-stream", -] - -[[package]] -name = "sync-committee-verifier" -version = "0.1.0" -dependencies = [ - "base2 0.2.2", - "ethereum-consensus", - "log", - "milagro_bls", - "ssz-rs", - "sync-committee-primitives", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.45.0", -] - -[[package]] -name = "tokio-macros" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "unsigned-varint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/Cargo.toml b/Cargo.toml index 29417d48b..37dfd2d6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,3 @@ members = [ "primitives", "prover" ] - -[patch."https://github.com/ralexstokes/ethereum-consensus"] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "dami/no-std-support" } - -[patch."https://github.com/ralexstokes/ssz-rs"] -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "seun/ssz-merkle-multi-proof-phase-1" } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index e9242033c..8a3e39be1 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version = "0.3.1", default-features = false} -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2", default-features = false } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features = false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", default-features = false, features=["serde"] } hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } [features] @@ -16,4 +16,4 @@ default = ["std"] std = [ "ssz-rs/std" ] -testing = [] \ No newline at end of file +testing = [] diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 3038dacdd..643a9de52 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -14,6 +14,7 @@ pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; pub const BLOCK_ROOTS_INDEX: u64 = 37; pub const HISTORICAL_ROOTS_INDEX: u64 = 39; pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; +pub const EXECUTION_PAYLOAD_TIMESTAMP_INDEX: u64 = 25; #[cfg(not(feature = "testing"))] pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); @@ -33,6 +34,8 @@ pub struct ExecutionPayloadProof { pub multi_proof: Vec, /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. pub execution_payload_branch: Vec, + /// timestamp + pub timestamp: u64, } /// Holds the neccessary proofs required to verify a header in the `block_roots` field diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 133a46c67..24de19cdd 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2" } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", features=["serde"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 5b3b75cd3..0adc7ddcf 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -38,7 +38,8 @@ use sync_committee_primitives::{ types::{ AncestryProof, BlockRootsProof, ExecutionPayloadProof, BLOCK_ROOTS_INDEX, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, + FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, }, util::compute_epoch_at_slot, }; @@ -221,22 +222,24 @@ pub fn prove_execution_payload( let indices = [ EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, + EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize, ]; // generate multi proofs let multi_proof = ssz_rs::generate_proof( - &mut beacon_state.latest_execution_payload_header, + beacon_state.latest_execution_payload_header.clone(), indices.as_slice(), )?; Ok(ExecutionPayloadProof { state_root: beacon_state.latest_execution_payload_header.state_root.clone(), block_number: beacon_state.latest_execution_payload_header.block_number, + timestamp: beacon_state.latest_execution_payload_header.timestamp, multi_proof: multi_proof .into_iter() .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect(), execution_payload_branch: ssz_rs::generate_proof( - &mut beacon_state, + beacon_state.clone(), &[EXECUTION_PAYLOAD_INDEX as usize], )? .into_iter() @@ -246,13 +249,13 @@ pub fn prove_execution_payload( } pub fn prove_sync_committee_update(mut state: BeaconStateType) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(&mut state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; + let proof = ssz_rs::generate_proof(state.clone(), &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } pub fn prove_finalized_header(mut state: BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; - let proof = ssz_rs::generate_proof(&mut state, indices.as_slice())?; + let proof = ssz_rs::generate_proof(state, indices.as_slice())?; Ok(proof .into_iter() @@ -283,7 +286,7 @@ pub fn prove_block_roots_proof( .position(|root| root == &block_root) .expect("Block root should exist in block_roots"); - let proof = ssz_rs::generate_proof(&mut state.block_roots, &[block_index])?; + let proof = ssz_rs::generate_proof(state.block_roots.clone(), &[block_index])?; let block_roots_proof = BlockRootsProof { block_header_index: block_index as u64, @@ -295,7 +298,8 @@ pub fn prove_block_roots_proof( .collect(), }; - let block_roots_branch = ssz_rs::generate_proof(&mut state, &[BLOCK_ROOTS_INDEX as usize])?; + let block_roots_branch = + ssz_rs::generate_proof(state.clone(), &[BLOCK_ROOTS_INDEX as usize])?; Ok(AncestryProof::BlockRoots { block_roots_proof, block_roots_branch: block_roots_branch diff --git a/prover/src/test.rs b/prover/src/test.rs index 4c0e23e2f..18b0db3a0 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -9,7 +9,10 @@ use ethereum_consensus::{ altair::Checkpoint, bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, state_transition::Context, }; -use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized, get_generalized_index, SszVariableOrIndex}; +use ssz_rs::{ + calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, + Merkleized, SszVariableOrIndex, +}; use std::time::Duration; use sync_committee_primitives::{ types::{AncestorBlock, FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, @@ -19,7 +22,7 @@ use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; -const NODE_URL: &'static str = "http://localhost:5052"; +const NODE_URL: &'static str = "http://localhost:3500"; #[cfg(test)] #[allow(non_snake_case)] @@ -114,7 +117,7 @@ async fn test_finalized_header() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); - let proof = ssz_rs::generate_proof(&mut state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); + let proof = ssz_rs::generate_proof(state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); let leaves = vec![Node::from_bytes( state @@ -140,14 +143,32 @@ async fn test_execution_payload_header_timestamp() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("finalized").await.unwrap(); - let generalized_index = get_generalized_index(&state.latest_execution_payload_header.timestamp, &[SszVariableOrIndex::Name("finalized_checkpoint")]); + let generalized_index = get_generalized_index( + &state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("timestamp")], + ); dbg!(generalized_index); - let proof = ssz_rs::generate_proof(state.latest_execution_payload_header.timestamp.clone(), &vec![generalized_index]); - + let proof = ssz_rs::generate_proof( + state.latest_execution_payload_header.clone(), + &vec![generalized_index], + ); - //let leaves = vec![Node::from_bytes(state.finalized_checkpoint.hash_tree_root().unwrap().as_ref().try_into().unwrap())]; - //let root = calculate_multi_merkle_root(&leaves, &proof.unwrap(), &[GeneralizedIndex(generalized_index)]); - //assert_eq!(root, state.hash_tree_root().unwrap()); + let leaves = vec![Node::from_bytes( + state + .latest_execution_payload_header + .timestamp + .hash_tree_root() + .unwrap() + .as_ref() + .try_into() + .unwrap(), + )]; + let root = calculate_multi_merkle_root( + &leaves, + &proof.unwrap(), + &[GeneralizedIndex(generalized_index)], + ); + assert_eq!(root, state.latest_execution_payload_header.hash_tree_root().unwrap()); } #[cfg(test)] @@ -160,8 +181,13 @@ async fn test_execution_payload_proof() { let block_id = finalized_state.slot.to_string(); let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + let mut finalized_header = sync_committee_prover.fetch_header(&block_id).await; + + while finalized_header.is_err() { + finalized_header = sync_committee_prover.fetch_header(&block_id).await; + } + let finalized_header = finalized_header.unwrap(); // verify the associated execution header of the finalized beacon header. let mut execution_payload = execution_payload_proof.clone(); let multi_proof_vec = execution_payload.multi_proof; @@ -173,11 +199,13 @@ async fn test_execution_payload_proof() { &[ Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), execution_payload.block_number.hash_tree_root().unwrap(), + execution_payload.timestamp.hash_tree_root().unwrap(), ], &multi_proof_nodes, &[ GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize), ], ); diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 3e65a2b1a..df6374fbb 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version="0.2.2", default-features = false } sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "ef89b4a4ef97cdd53a66ddb52e554667aca0beb2", default-features = false } -ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "688a82389f60ed68c10404417c1f7f442ba748a1", default-features = false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", default-features = false, features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } log = { version = "0.4.17", default-features = false } @@ -19,4 +19,4 @@ std = [ "milagro_bls/std", "log/std" ] -testing = ["sync-committee-primitives/testing"] \ No newline at end of file +testing = ["sync-committee-primitives/testing"] diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index ac6a6df14..22e7291b0 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -23,8 +23,9 @@ use sync_committee_primitives::{ types::{ AncestryProof, BLOCK_ROOTS_INDEX, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, FINALIZED_ROOT_INDEX, GENESIS_VALIDATORS_ROOT, - HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, NEXT_SYNC_COMMITTEE_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, + FINALIZED_ROOT_INDEX, GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, + HISTORICAL_ROOTS_INDEX, NEXT_SYNC_COMMITTEE_INDEX, }, util::{compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot}, }; @@ -170,6 +171,7 @@ pub fn verify_sync_committee_attestation( &[ GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize), ], ); From 88f42e00cf6cfd16dbbb0a9b244f9760a1f506e3 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 14 Mar 2023 00:50:50 +0100 Subject: [PATCH 083/182] revert node url --- prover/src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index 18b0db3a0..97eb247a4 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -22,7 +22,7 @@ use sync_committee_verifier::verify_sync_committee_attestation; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; -const NODE_URL: &'static str = "http://localhost:3500"; +const NODE_URL: &'static str = "http://localhost:5052"; #[cfg(test)] #[allow(non_snake_case)] From f43b70e5c9dca8b61ec73643605c303c2aaab7f3 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 14 Mar 2023 10:12:09 +0100 Subject: [PATCH 084/182] use latest commit revision --- primitives/Cargo.toml | 4 ++-- prover/Cargo.toml | 4 ++-- prover/src/lib.rs | 13 ++++++------- prover/src/test.rs | 11 +++-------- verifier/Cargo.toml | 4 ++-- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 8a3e39be1..fe2536ece 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version = "0.3.1", default-features = false} -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", default-features = false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false, features=["serde"] } hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } [features] diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 24de19cdd..e1e8a0354 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0" } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", features=["serde"] } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 0adc7ddcf..b1ec1c001 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -226,7 +226,7 @@ pub fn prove_execution_payload( ]; // generate multi proofs let multi_proof = ssz_rs::generate_proof( - beacon_state.latest_execution_payload_header.clone(), + &mut beacon_state.latest_execution_payload_header, indices.as_slice(), )?; @@ -239,7 +239,7 @@ pub fn prove_execution_payload( .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect(), execution_payload_branch: ssz_rs::generate_proof( - beacon_state.clone(), + &mut beacon_state, &[EXECUTION_PAYLOAD_INDEX as usize], )? .into_iter() @@ -249,13 +249,13 @@ pub fn prove_execution_payload( } pub fn prove_sync_committee_update(mut state: BeaconStateType) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(state.clone(), &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; + let proof = ssz_rs::generate_proof(&mut state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } pub fn prove_finalized_header(mut state: BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; - let proof = ssz_rs::generate_proof(state, indices.as_slice())?; + let proof = ssz_rs::generate_proof(&mut state, indices.as_slice())?; Ok(proof .into_iter() @@ -286,7 +286,7 @@ pub fn prove_block_roots_proof( .position(|root| root == &block_root) .expect("Block root should exist in block_roots"); - let proof = ssz_rs::generate_proof(state.block_roots.clone(), &[block_index])?; + let proof = ssz_rs::generate_proof(&mut state.block_roots, &[block_index])?; let block_roots_proof = BlockRootsProof { block_header_index: block_index as u64, @@ -298,8 +298,7 @@ pub fn prove_block_roots_proof( .collect(), }; - let block_roots_branch = - ssz_rs::generate_proof(state.clone(), &[BLOCK_ROOTS_INDEX as usize])?; + let block_roots_branch = ssz_rs::generate_proof(&mut state, &[BLOCK_ROOTS_INDEX as usize])?; Ok(AncestryProof::BlockRoots { block_roots_proof, block_roots_branch: block_roots_branch diff --git a/prover/src/test.rs b/prover/src/test.rs index 97eb247a4..fa6e9ba7a 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -117,7 +117,7 @@ async fn test_finalized_header() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); - let proof = ssz_rs::generate_proof(state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); + let proof = ssz_rs::generate_proof(&mut state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); let leaves = vec![Node::from_bytes( state @@ -149,7 +149,7 @@ async fn test_execution_payload_header_timestamp() { ); dbg!(generalized_index); let proof = ssz_rs::generate_proof( - state.latest_execution_payload_header.clone(), + &mut state.latest_execution_payload_header, &vec![generalized_index], ); @@ -181,13 +181,8 @@ async fn test_execution_payload_proof() { let block_id = finalized_state.slot.to_string(); let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - let mut finalized_header = sync_committee_prover.fetch_header(&block_id).await; + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - while finalized_header.is_err() { - finalized_header = sync_committee_prover.fetch_header(&block_id).await; - } - - let finalized_header = finalized_header.unwrap(); // verify the associated execution header of the finalized beacon header. let mut execution_payload = execution_payload_proof.clone(); let multi_proof_vec = execution_payload.multi_proof; diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index df6374fbb..b09fc500c 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version="0.2.2", default-features = false } sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", default-features = false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false, features=["serde"] } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } log = { version = "0.4.17", default-features = false } From dc992ccb0828101fe4f5a12b83017a1bd4dce3ee Mon Sep 17 00:00:00 2001 From: David Salami Date: Tue, 14 Mar 2023 10:16:57 +0100 Subject: [PATCH 085/182] some fixes --- Cargo.lock | 2113 +++++++++++++++++++++++++++++++++++++++++ primitives/Cargo.toml | 4 +- prover/Cargo.toml | 4 +- prover/src/lib.rs | 13 +- prover/src/test.rs | 39 +- verifier/Cargo.toml | 4 +- 6 files changed, 2127 insertions(+), 50 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..3fee84a14 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "amcl" +version = "0.3.0" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "as-any" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" + +[[package]] +name = "async-stream" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +dependencies = [ + "int 0.2.11", +] + +[[package]] +name = "base2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" +dependencies = [ + "int 0.3.0", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +dependencies = [ + "base64 0.13.1", + "bs58", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "ethereum-consensus" +version = "0.1.1" +source = "git+https://github.com/polytope-labs/ethereum-consensus?rev=d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f#d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f" +dependencies = [ + "async-stream", + "bs58", + "enr", + "error-chain", + "getrandom", + "hashbrown 0.13.2", + "hex", + "integer-sqrt", + "milagro_bls", + "multiaddr", + "multihash", + "rand", + "serde", + "serde_json", + "serde_yaml", + "sha2 0.9.9", + "ssz-rs", + "tokio", + "tokio-stream", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "int" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +dependencies = [ + "num-traits", +] + +[[package]] +name = "int" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "milagro_bls" +version = "1.5.1" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +dependencies = [ + "amcl", + "hex", + "lazy_static", + "rand", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f8f1eaef6c65a8600d7#2e28a8800787392045fb3f8f1eaef6c65a8600d7" +dependencies = [ + "as-any", + "bitvec", + "hex", + "itertools", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive", + "thiserror", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f8f1eaef6c65a8600d7#2e28a8800787392045fb3f8f1eaef6c65a8600d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync-committee-primitives" +version = "0.1.0" +dependencies = [ + "base2 0.3.1", + "ethereum-consensus", + "hex-literal", + "ssz-rs", +] + +[[package]] +name = "sync-committee-prover" +version = "0.1.0" +dependencies = [ + "actix-rt", + "anyhow", + "async-stream", + "base2 0.2.2", + "env_logger", + "ethereum-consensus", + "hex", + "reqwest", + "serde", + "serde_json", + "ssz-rs", + "sync-committee-primitives", + "sync-committee-verifier", + "tokio", + "tokio-stream", +] + +[[package]] +name = "sync-committee-verifier" +version = "0.1.0" +dependencies = [ + "base2 0.2.2", + "ethereum-consensus", + "log", + "milagro_bls", + "ssz-rs", + "sync-committee-primitives", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 8a3e39be1..a6db5811b 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version = "0.3.1", default-features = false} -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", default-features = false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } [features] diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 24de19cdd..d6546c55d 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0" } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7" } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 0adc7ddcf..b1ec1c001 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -226,7 +226,7 @@ pub fn prove_execution_payload( ]; // generate multi proofs let multi_proof = ssz_rs::generate_proof( - beacon_state.latest_execution_payload_header.clone(), + &mut beacon_state.latest_execution_payload_header, indices.as_slice(), )?; @@ -239,7 +239,7 @@ pub fn prove_execution_payload( .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) .collect(), execution_payload_branch: ssz_rs::generate_proof( - beacon_state.clone(), + &mut beacon_state, &[EXECUTION_PAYLOAD_INDEX as usize], )? .into_iter() @@ -249,13 +249,13 @@ pub fn prove_execution_payload( } pub fn prove_sync_committee_update(mut state: BeaconStateType) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(state.clone(), &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; + let proof = ssz_rs::generate_proof(&mut state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } pub fn prove_finalized_header(mut state: BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; - let proof = ssz_rs::generate_proof(state, indices.as_slice())?; + let proof = ssz_rs::generate_proof(&mut state, indices.as_slice())?; Ok(proof .into_iter() @@ -286,7 +286,7 @@ pub fn prove_block_roots_proof( .position(|root| root == &block_root) .expect("Block root should exist in block_roots"); - let proof = ssz_rs::generate_proof(state.block_roots.clone(), &[block_index])?; + let proof = ssz_rs::generate_proof(&mut state.block_roots, &[block_index])?; let block_roots_proof = BlockRootsProof { block_header_index: block_index as u64, @@ -298,8 +298,7 @@ pub fn prove_block_roots_proof( .collect(), }; - let block_roots_branch = - ssz_rs::generate_proof(state.clone(), &[BLOCK_ROOTS_INDEX as usize])?; + let block_roots_branch = ssz_rs::generate_proof(&mut state, &[BLOCK_ROOTS_INDEX as usize])?; Ok(AncestryProof::BlockRoots { block_roots_proof, block_roots_branch: block_roots_branch diff --git a/prover/src/test.rs b/prover/src/test.rs index 97eb247a4..679d54042 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -117,7 +117,7 @@ async fn test_finalized_header() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); - let proof = ssz_rs::generate_proof(state.clone(), &vec![FINALIZED_ROOT_INDEX as usize]); + let proof = ssz_rs::generate_proof(&mut state, &vec![FINALIZED_ROOT_INDEX as usize]); let leaves = vec![Node::from_bytes( state @@ -136,41 +136,6 @@ async fn test_finalized_header() { assert_eq!(root, state.hash_tree_root().unwrap()); } -#[cfg(test)] -#[allow(non_snake_case)] -#[actix_rt::test] -async fn test_execution_payload_header_timestamp() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); - let mut state = sync_committee_prover.fetch_beacon_state("finalized").await.unwrap(); - - let generalized_index = get_generalized_index( - &state.latest_execution_payload_header, - &[SszVariableOrIndex::Name("timestamp")], - ); - dbg!(generalized_index); - let proof = ssz_rs::generate_proof( - state.latest_execution_payload_header.clone(), - &vec![generalized_index], - ); - - let leaves = vec![Node::from_bytes( - state - .latest_execution_payload_header - .timestamp - .hash_tree_root() - .unwrap() - .as_ref() - .try_into() - .unwrap(), - )]; - let root = calculate_multi_merkle_root( - &leaves, - &proof.unwrap(), - &[GeneralizedIndex(generalized_index)], - ); - assert_eq!(root, state.latest_execution_payload_header.hash_tree_root().unwrap()); -} - #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] @@ -494,7 +459,7 @@ async fn test_prover() { ); count += 1; - if count == 100 { + if count == 10 { break } } diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index df6374fbb..357c12a77 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Polytope Labs"] [dependencies] base2 = { version="0.2.2", default-features = false } sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "c26206f18fcecbd69ca5d505ae4211a7bc756ad0", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "a78a600c7751527b74e038094ead04b697caa4a5", default-features = false, features=["serde"] } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } log = { version = "0.4.17", default-features = false } From f0eb10c60a577b266db57d41f3ea1171c44fded6 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 14 Mar 2023 10:17:16 +0100 Subject: [PATCH 086/182] include cargo lock --- Cargo.lock | 2113 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2113 insertions(+) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..3fee84a14 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "amcl" +version = "0.3.0" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "as-any" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" + +[[package]] +name = "async-stream" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +dependencies = [ + "int 0.2.11", +] + +[[package]] +name = "base2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" +dependencies = [ + "int 0.3.0", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enr" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +dependencies = [ + "base64 0.13.1", + "bs58", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "ethereum-consensus" +version = "0.1.1" +source = "git+https://github.com/polytope-labs/ethereum-consensus?rev=d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f#d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f" +dependencies = [ + "async-stream", + "bs58", + "enr", + "error-chain", + "getrandom", + "hashbrown 0.13.2", + "hex", + "integer-sqrt", + "milagro_bls", + "multiaddr", + "multihash", + "rand", + "serde", + "serde_json", + "serde_yaml", + "sha2 0.9.9", + "ssz-rs", + "tokio", + "tokio-stream", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "int" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +dependencies = [ + "num-traits", +] + +[[package]] +name = "int" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "milagro_bls" +version = "1.5.1" +source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +dependencies = [ + "amcl", + "hex", + "lazy_static", + "rand", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssz-rs" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f8f1eaef6c65a8600d7#2e28a8800787392045fb3f8f1eaef6c65a8600d7" +dependencies = [ + "as-any", + "bitvec", + "hex", + "itertools", + "num-bigint", + "serde", + "sha2 0.9.9", + "ssz-rs-derive", + "thiserror", +] + +[[package]] +name = "ssz-rs-derive" +version = "0.8.0" +source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f8f1eaef6c65a8600d7#2e28a8800787392045fb3f8f1eaef6c65a8600d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync-committee-primitives" +version = "0.1.0" +dependencies = [ + "base2 0.3.1", + "ethereum-consensus", + "hex-literal", + "ssz-rs", +] + +[[package]] +name = "sync-committee-prover" +version = "0.1.0" +dependencies = [ + "actix-rt", + "anyhow", + "async-stream", + "base2 0.2.2", + "env_logger", + "ethereum-consensus", + "hex", + "reqwest", + "serde", + "serde_json", + "ssz-rs", + "sync-committee-primitives", + "sync-committee-verifier", + "tokio", + "tokio-stream", +] + +[[package]] +name = "sync-committee-verifier" +version = "0.1.0" +dependencies = [ + "base2 0.2.2", + "ethereum-consensus", + "log", + "milagro_bls", + "ssz-rs", + "sync-committee-primitives", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" From 0868b082f24fb66580c814de353e717196b8fea9 Mon Sep 17 00:00:00 2001 From: David Salami Date: Tue, 14 Mar 2023 10:44:47 +0100 Subject: [PATCH 087/182] fix tests --- verifier/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 22e7291b0..fff66eb0a 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -166,6 +166,7 @@ pub fn verify_sync_committee_attestation( .block_number .hash_tree_root() .map_err(|_| Error::InvalidRoot)?, + execution_payload.timestamp.hash_tree_root().map_err(|_| Error::InvalidRoot)?, ], &multi_proof_nodes, &[ @@ -347,11 +348,22 @@ pub fn verify_sync_committee_attestation( .try_into() .map_err(|_| Error::InvalidRoot)?, ), + Node::from_bytes( + execution_payload + .timestamp + .clone() + .hash_tree_root() + .map_err(|_| Error::MerkleizationError)? + .as_ref() + .try_into() + .map_err(|_| Error::InvalidRoot)?, + ), ], &multi_proof, &[ GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize), ], ); From c19b248a3d28f54386c598afd526b296b057b829 Mon Sep 17 00:00:00 2001 From: Damilare Date: Tue, 14 Mar 2023 12:06:04 +0100 Subject: [PATCH 088/182] remove while loop --- prover/src/test.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/prover/src/test.rs b/prover/src/test.rs index 679d54042..f234f468f 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -146,13 +146,8 @@ async fn test_execution_payload_proof() { let block_id = finalized_state.slot.to_string(); let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - let mut finalized_header = sync_committee_prover.fetch_header(&block_id).await; + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - while finalized_header.is_err() { - finalized_header = sync_committee_prover.fetch_header(&block_id).await; - } - - let finalized_header = finalized_header.unwrap(); // verify the associated execution header of the finalized beacon header. let mut execution_payload = execution_payload_proof.clone(); let multi_proof_vec = execution_payload.multi_proof; From 408d292f3f3099b52f8388115ef04c5027b9ecc0 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 15 Mar 2023 13:31:35 +0100 Subject: [PATCH 089/182] build mmr --- .gitignore | 1 + Cargo.toml | 7 ++ src/lib.rs | 245 ++++++++++++++++++++++++++++++++++++++++++++- src/mmr/mmr.rs | 180 +++++++++++++++++++++++++++++++++ src/mmr/mod.rs | 97 ++++++++++++++++++ src/mmr/storage.rs | 224 +++++++++++++++++++++++++++++++++++++++++ src/mmr/utils.rs | 59 +++++++++++ src/primitives.rs | 52 ++++++++++ 8 files changed, 864 insertions(+), 1 deletion(-) create mode 100644 src/mmr/mmr.rs create mode 100644 src/mmr/mod.rs create mode 100644 src/mmr/storage.rs create mode 100644 src/mmr/utils.rs create mode 100644 src/primitives.rs diff --git a/.gitignore b/.gitignore index 3ab5292b1..a24012e42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /Cargo.lock .idea +.cargo \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 6b29d1ac1..38fba9dac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,11 @@ frame-support = { git = "https://github.com/paritytech/substrate", branch = "pol frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", rev = "2fc20996d80a43aaf2bfff79cfa9e57ac53d7223", default-features = false } +mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } [features] default = ["std"] @@ -27,6 +31,9 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "ismp-rust/std", + "mmr-lib/std", + "sp-api/std" ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] diff --git a/src/lib.rs b/src/lib.rs index cabb1bf2a..f3dea6d4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,13 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +mod mmr; +mod primitives; + +use codec::Encode; // Re-export pallet items so that they can be accessed from the crate namespace. +use crate::mmr::storage::{OffchainKeyGenerator, StorageReadWrite}; +use crate::mmr::{FullLeaf, Leaf, LeafIndex, Node, NodeIndex, NodeOf}; pub use pallet::*; // Definition of the pallet logic, to be aggregated at runtime definition through @@ -25,8 +31,10 @@ pub use pallet::*; pub mod pallet { // Import various types used to declare pallet in scope. use super::*; + use crate::mmr::{LeafIndex, Mmr, NodeIndex}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_runtime::traits; /// Our pallet's configuration trait. All our types and constants go in here. If the /// pallet is dependent on specific other pallets, then their configuration traits @@ -37,6 +45,43 @@ pub mod pallet { pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Prefix for elements stored in the Off-chain DB via Indexing API. + /// + /// Each node of the MMR is inserted both on-chain and off-chain via Indexing API. + /// The former does not store full leaf content, just its compact version (hash), + /// and some of the inner mmr nodes might be pruned from on-chain storage. + /// The latter will contain all the entries in their full form. + /// + /// Each node is stored in the Off-chain DB under key derived from the + /// [`Self::INDEXING_PREFIX`] and its in-tree index (MMR position). + const INDEXING_PREFIX: &'static [u8]; + + /// A hasher type for MMR. + /// + /// To construct trie nodes that result in merging (bagging) two peaks, depending on the + /// node kind we take either: + /// - The node (hash) itself if it's an inner node. + /// - The hash of SCALE-encoding of the leaf data if it's a leaf node. + /// + /// Then we create a tuple of these two hashes, SCALE-encode it (concatenate) and + /// hash, to obtain a new MMR inner node - the new peak. + type Hashing: traits::Hash::Hash>; + /// The hashing output type. + /// + /// This type is actually going to be stored in the MMR. + /// Required to be provided again, to satisfy trait bounds for storage items. + type Hash: traits::Member + + traits::MaybeSerializeDeserialize + + sp_std::fmt::Debug + + sp_std::hash::Hash + + AsRef<[u8]> + + AsMut<[u8]> + + Copy + + Default + + codec::Codec + + codec::EncodeLike + + scale_info::TypeInfo + + MaxEncodedLen; } // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and @@ -45,14 +90,100 @@ pub mod pallet { #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); + /// Latest MMR Root hash for requests + #[pallet::storage] + #[pallet::getter(fn requests_root_hash)] + pub type RequestsRootHash = StorageValue<_, ::Hash, ValueQuery>; + + /// Latest MMR Root hash for responses + #[pallet::storage] + #[pallet::getter(fn responses_root_hash)] + pub type ResponsesRootHash = StorageValue<_, ::Hash, ValueQuery>; + + /// Current size of the MMR (number of leaves) for requests. + #[pallet::storage] + #[pallet::getter(fn number_of_request_leaves)] + pub type NumberOfRequestLeaves = StorageValue<_, LeafIndex, ValueQuery>; + + /// Current size of the MMR (number of leaves) for responses. + #[pallet::storage] + #[pallet::getter(fn number_of_response_leaves)] + pub type NumberOfResponseLeaves = StorageValue<_, LeafIndex, ValueQuery>; + + /// Hashes of the nodes in the MMR for requests. + /// + /// Note this collection only contains MMR peaks, the inner nodes (and leaves) + /// are pruned and only stored in the Offchain DB. + #[pallet::storage] + #[pallet::getter(fn request_peaks)] + pub type RequestNodes = + StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; + + /// Hashes of the nodes in the MMR for responses. + /// + /// Note this collection only contains MMR peaks, the inner nodes (and leaves) + /// are pruned and only stored in the Offchain DB. + #[pallet::storage] + #[pallet::getter(fn response_peaks)] + pub type ResponseNodes = + StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { + // return Mmr finalization weight here Weight::zero() } - fn on_finalize(_n: T::BlockNumber) {} + fn on_finalize(_n: T::BlockNumber) { + use crate::mmr; + // handle finalizing requests Mmr + let request_leaves = Self::number_of_request_leaves(); + + let request_mmr: Mmr< + mmr::storage::RuntimeStorage, + T, + Leaf, + RequestOffchainKey, + RequestsStore, + > = mmr::Mmr::new(request_leaves); + + // Update the size, `mmr.finalize()` should also never fail. + let (leaves, root) = match request_mmr.finalize() { + Ok((leaves, root)) => (leaves, root), + Err(e) => { + log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); + return; + } + }; + + >::put(leaves); + >::put(root); + + // handle finalizing response Mmr + let response_leaves = Self::number_of_response_leaves(); + + let response_mmr: Mmr< + mmr::storage::RuntimeStorage, + T, + Leaf, + ResponseOffchainKey, + ResponseStore, + > = mmr::Mmr::new(response_leaves); + + // Update the size, `mmr.finalize()` should also never fail. + let (leaves, root) = match response_mmr.finalize() { + Ok((leaves, root)) => (leaves, root), + Err(e) => { + log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); + return; + } + }; + + >::put(leaves); + >::put(root); + } fn offchain_worker(_n: T::BlockNumber) {} } @@ -69,3 +200,115 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event {} } + +impl Pallet { + /// Generate an MMR proof for the given `leaf_indices`. + /// Note this method can only be used from an off-chain context + /// (Offchain Worker or Runtime API call), since it requires + /// all the leaves to be present. + /// It may return an error or panic if used incorrectly. + pub fn generate_request_proof( + leaf_indices: Vec, + ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> { + let leaves_count = NumberOfRequestLeaves::::get(); + let mmr = mmr::Mmr::< + mmr::storage::OffchainStorage, + T, + Leaf, + RequestOffchainKey, + RequestsStore, + >::new(leaves_count); + mmr.generate_request_proof(leaf_indices) + } + + pub fn generate_response_proof( + leaf_indices: Vec, + ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> { + let leaves_count = NumberOfRequestLeaves::::get(); + let mmr = mmr::Mmr::< + mmr::storage::OffchainStorage, + T, + Leaf, + ResponseOffchainKey, + ResponseStore, + >::new(leaves_count); + mmr.generate_response_proof(leaf_indices) + } + + /// Return the on-chain MMR root hash. + pub fn requests_mmr_root() -> ::Hash { + Self::requests_root_hash() + } + /// Return the on-chain MMR root hash. + pub fn responses_mmr_root() -> ::Hash { + Self::responses_root_hash() + } +} + +pub struct RequestsStore(core::marker::PhantomData); + +impl::Hashing>> StorageReadWrite for RequestsStore { + fn get_node(pos: NodeIndex) -> Option> { + RequestNodes::::get(pos).map(Node::Hash) + } + + fn remove_node(pos: NodeIndex) { + RequestNodes::::remove(pos); + } + + fn insert_node(pos: NodeIndex, node: ::Hash) { + RequestNodes::::insert(pos, node) + } + + fn get_num_leaves() -> LeafIndex { + NumberOfRequestLeaves::::get() + } + + fn set_num_leaves(num_leaves: LeafIndex) { + NumberOfRequestLeaves::::put(num_leaves) + } +} + +pub struct ResponseStore(core::marker::PhantomData); + +impl::Hashing>> StorageReadWrite for ResponseStore { + fn get_node(pos: NodeIndex) -> Option> { + ResponseNodes::::get(pos).map(Node::Hash) + } + + fn remove_node(pos: NodeIndex) { + ResponseNodes::::remove(pos); + } + + fn insert_node(pos: NodeIndex, node: ::Hash) { + ResponseNodes::::insert(pos, node) + } + + fn get_num_leaves() -> LeafIndex { + NumberOfResponseLeaves::::get() + } + + fn set_num_leaves(num_leaves: LeafIndex) { + NumberOfResponseLeaves::::put(num_leaves) + } +} + +pub struct RequestOffchainKey(core::marker::PhantomData<(T, L)>); + +impl::Hashing>> OffchainKeyGenerator + for RequestOffchainKey +{ + fn offchain_key(pos: NodeIndex) -> Vec { + (T::INDEXING_PREFIX, "Requests", pos).encode() + } +} + +pub struct ResponseOffchainKey(core::marker::PhantomData<(T, L)>); + +impl::Hashing>> OffchainKeyGenerator + for ResponseOffchainKey +{ + fn offchain_key(pos: NodeIndex) -> Vec { + (T::INDEXING_PREFIX, "Responses", pos).encode() + } +} diff --git a/src/mmr/mmr.rs b/src/mmr/mmr.rs new file mode 100644 index 000000000..205cb8cfb --- /dev/null +++ b/src/mmr/mmr.rs @@ -0,0 +1,180 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::mmr::storage::{OffchainKeyGenerator, StorageReadWrite}; +use crate::mmr::utils::NodesUtils; +use crate::mmr::{FullLeaf, HashingOf, NodeIndex}; +use crate::primitives::Proof; +use crate::{ + mmr::{ + storage::{OffchainStorage, RuntimeStorage, Storage}, + Hasher, Node, NodeOf, + }, + primitives::Error, + Config, RequestOffchainKey, RequestsStore, ResponseOffchainKey, ResponseStore, +}; +use sp_std::prelude::*; + +/// A wrapper around an MMR library to expose limited functionality. +/// +/// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage) +/// vs [Off-chain](crate::mmr::storage::OffchainStorage)). +pub struct Mmr +where + T: Config, + L: FullLeaf<::Hashing>, + ReadWrite: StorageReadWrite, + K: OffchainKeyGenerator, + Storage: mmr_lib::MMRStore>, +{ + mmr: mmr_lib::MMR< + NodeOf, + Hasher, L>, + Storage, + >, + leaves: NodeIndex, +} + +impl Mmr +where + T: Config, + L: FullLeaf<::Hashing>, + ReadWrite: StorageReadWrite, + K: OffchainKeyGenerator, + Storage: mmr_lib::MMRStore>, +{ + /// Create a pointer to an existing MMR with given number of leaves. + pub fn new(leaves: NodeIndex) -> Self { + let size = NodesUtils::new(leaves).size(); + Self { + mmr: mmr_lib::MMR::new(size, Default::default()), + leaves, + } + } + + /// Return the internal size of the MMR (number of nodes). + #[cfg(test)] + pub fn size(&self) -> NodeIndex { + self.mmr.mmr_size() + } +} + +/// Runtime specific MMR functions. +impl Mmr +where + T: Config, + L: FullLeaf<::Hashing>, + ReadWrite: StorageReadWrite, + K: OffchainKeyGenerator, +{ + /// Push another item to the MMR. + /// + /// Returns element position (index) in the MMR. + pub fn push(&mut self, leaf: L) -> Option { + let position = self + .mmr + .push(Node::Data(leaf)) + .map_err(|_| Error::Push) + .ok()?; + + self.leaves += 1; + + Some(position) + } + + /// Commit the changes to underlying storage, return current number of leaves and + /// calculate the new MMR's root hash. + pub fn finalize(self) -> Result<(NodeIndex, ::Hash), Error> { + let root = self.mmr.get_root().map_err(|_| Error::GetRoot)?; + self.mmr.commit().map_err(|_| Error::Commit)?; + Ok((self.leaves, root.hash())) + } +} + +/// Off-chain specific MMR functions. +impl Mmr +where + T: Config, + L: FullLeaf<::Hashing> + codec::Decode, + ReadWrite: StorageReadWrite, + K: OffchainKeyGenerator, +{ + /// Generate a proof for given leaf indices. + /// + /// Proof generation requires all the nodes (or their hashes) to be available in the storage. + /// (i.e. you can't run the function in the pruned storage). + pub fn generate_request_proof( + &self, + leaf_indices: Vec, + ) -> Result<(Vec, Proof<::Hash>), Error> { + let positions = leaf_indices + .iter() + .map(|index| mmr_lib::leaf_index_to_pos(*index)) + .collect::>(); + let store = + , RequestsStore>>::default(); + let leaves = positions + .iter() + .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { + Ok(Some(Node::Data(leaf))) => Ok(leaf), + _ => Err(Error::LeafNotFound), + }) + .collect::, Error>>()?; + + let leaf_count = self.leaves; + self.mmr + .gen_proof(positions) + .map_err(|_| Error::GenerateProof) + .map(|p| Proof { + leaf_indices, + leaf_count, + items: p.proof_items().iter().map(|x| x.hash()).collect(), + }) + .map(|p| (leaves, p)) + } + + pub fn generate_response_proof( + &self, + leaf_indices: Vec, + ) -> Result<(Vec, Proof<::Hash>), Error> { + let positions = leaf_indices + .iter() + .map(|index| mmr_lib::leaf_index_to_pos(*index)) + .collect::>(); + let store = + , ResponseStore>>::default( + ); + let leaves = positions + .iter() + .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { + Ok(Some(Node::Data(leaf))) => Ok(leaf), + _ => Err(Error::LeafNotFound), + }) + .collect::, Error>>()?; + + let leaf_count = self.leaves; + self.mmr + .gen_proof(positions) + .map_err(|_| Error::GenerateProof) + .map(|p| Proof { + leaf_indices, + leaf_count, + items: p.proof_items().iter().map(|x| x.hash()).collect(), + }) + .map(|p| (leaves, p)) + } +} diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs new file mode 100644 index 000000000..b09085ddd --- /dev/null +++ b/src/mmr/mod.rs @@ -0,0 +1,97 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use core::fmt; +pub mod mmr; +pub mod storage; +mod utils; + +use crate::Config; +use codec::{Decode, Encode}; +use frame_support::RuntimeDebug; +use ismp_rust::router::{Request, Response}; +use sp_runtime::traits; + +pub use self::mmr::Mmr; +pub type LeafIndex = u64; +pub type NodeIndex = u64; + +pub(crate) type HashingOf = ::Hashing; + +#[derive(Debug, Clone, Decode, Encode, PartialEq, Eq)] +pub enum Leaf { + Request(Request), + Response(Response), +} + +/// A full leaf content stored in the offchain-db. +pub trait FullLeaf: Clone + fmt::Debug + PartialEq + Eq + codec::Codec { + /// Returns the hash of the leaf + fn hash(&self) -> H::Output; +} + +impl FullLeaf for Leaf { + fn hash(&self) -> H::Output { + todo!() + } +} + +/// An element representing either full data or its hash. +#[derive(RuntimeDebug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum DataOrHash { + /// Arbitrary data in its full form. + Data(L), + /// A hash of some data. + Hash(H::Output), +} + +impl From for DataOrHash { + fn from(l: L) -> Self { + Self::Data(l) + } +} + +impl> DataOrHash { + /// Retrieve a hash of this item. + /// + /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] + /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. + pub fn hash(&self) -> H::Output { + match *self { + Self::Data(ref leaf) => leaf.hash(), + Self::Hash(ref hash) => *hash, + } + } +} +/// Node type for runtime `T`. +pub type NodeOf = Node<::Hashing, L>; + +/// A node stored in the MMR. +pub type Node = DataOrHash; + +/// Default Merging & Hashing behavior for MMR. +pub struct Hasher(sp_std::marker::PhantomData<(H, L)>); + +impl> mmr_lib::Merge for Hasher { + type Item = Node; + + fn merge(left: &Self::Item, right: &Self::Item) -> mmr_lib::Result { + let mut concat = left.hash().as_ref().to_vec(); + concat.extend_from_slice(right.hash().as_ref()); + + Ok(Node::Hash(::hash(&concat))) + } +} diff --git a/src/mmr/storage.rs b/src/mmr/storage.rs new file mode 100644 index 000000000..483b986c5 --- /dev/null +++ b/src/mmr/storage.rs @@ -0,0 +1,224 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! An MMR storage implementation. +use codec::Encode; +use frame_support::log::{debug, trace}; +use mmr_lib::helper; +use sp_core::offchain::StorageKind; +use sp_io::offchain_index; +use sp_std::iter::Peekable; +#[cfg(not(feature = "std"))] +use sp_std::prelude::*; + +use crate::mmr::utils::NodesUtils; +use crate::mmr::{FullLeaf, LeafIndex, NodeIndex}; +use crate::{ + mmr::{Node, NodeOf}, + Config, +}; + +pub trait OffchainKeyGenerator { + fn offchain_key(pos: NodeIndex) -> Vec; +} + +pub trait StorageReadWrite { + fn get_node(pos: NodeIndex) -> Option>; + fn remove_node(pos: NodeIndex); + fn insert_node(pos: NodeIndex, node: ::Hash); + fn get_num_leaves() -> LeafIndex; + fn set_num_leaves(num_leaves: LeafIndex); +} + +/// A marker type for runtime-specific storage implementation. +/// +/// Allows appending new items to the MMR and proof verification. +/// MMR nodes are appended to two different storages: +/// 1. We add nodes (leaves) hashes to the on-chain storage (see [crate::Nodes]). +/// 2. We add full leaves (and all inner nodes as well) into the `IndexingAPI` during block +/// processing, so the values end up in the Offchain DB if indexing is enabled. +pub struct RuntimeStorage; + +/// A marker type for offchain-specific storage implementation. +/// +/// Allows proof generation and verification, but does not support appending new items. +/// MMR nodes are assumed to be stored in the Off-Chain DB. Note this storage type +/// DOES NOT support adding new items to the MMR. +pub struct OffchainStorage; + +/// A storage layer for MMR. +/// +/// There are two different implementations depending on the use case. +/// See docs for [RuntimeStorage] and [OffchainStorage]. +pub struct Storage( + sp_std::marker::PhantomData<(StorageType, T, L, K, ReadWrite)>, +); + +impl Default for Storage { + fn default() -> Self { + Self(Default::default()) + } +} + +impl mmr_lib::MMRStore> + for Storage +where + T: Config, + K: OffchainKeyGenerator, + L: FullLeaf<::Hashing> + codec::Decode, + ReadWrite: StorageReadWrite, +{ + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { + // Find out which leaf added node `pos` in the MMR. + let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos); + + let key = ::offchain_key(pos); + debug!( + target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, canon key {:?}", + pos, ancestor_leaf_idx, key + ); + // Try to retrieve the element from Off-chain DB. + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + return Ok(codec::Decode::decode(&mut &*elem).ok()); + } + + Ok(None) + } + + fn append(&mut self, _: NodeIndex, _: Vec>) -> mmr_lib::Result<()> { + panic!("MMR must not be altered in the off-chain context.") + } +} + +impl mmr_lib::MMRStore> + for Storage +where + T: Config, + K: OffchainKeyGenerator, + L: FullLeaf<::Hashing>, + ReadWrite: StorageReadWrite, +{ + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { + Ok(ReadWrite::get_node(pos)) + } + + fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { + if elems.is_empty() { + return Ok(()); + } + + trace!( + target: "runtime::mmr", "elems: {:?}", + elems.iter().map(|elem| elem.hash()).collect::>() + ); + + let leaves = ReadWrite::get_num_leaves(); + let size = NodesUtils::new(leaves).size(); + + if pos != size { + return Err(mmr_lib::Error::InconsistentStore); + } + + let new_size = size + elems.len() as NodeIndex; + + // A sorted (ascending) iterator over peak indices to prune and persist. + let (peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store(size, new_size); + + // Now we are going to iterate over elements to insert + // and keep track of the current `node_index` and `leaf_index`. + let mut leaf_index = leaves; + let mut node_index = size; + + for elem in elems { + // On-chain we are going to only store new peaks. + if peaks_to_store.next_if_eq(&node_index).is_some() { + ReadWrite::insert_node(node_index, elem.hash()); + } + // We are storing full node off-chain (using indexing API). + Self::store_to_offchain(node_index, &elem); + + // Increase the indices. + if let Node::Data(..) = elem { + leaf_index += 1; + } + node_index += 1; + } + + // Update current number of leaves. + ReadWrite::set_num_leaves(leaf_index); + + // And remove all remaining items from `peaks_before` collection. + for pos in peaks_to_prune { + ReadWrite::remove_node(pos); + } + + Ok(()) + } +} + +impl Storage +where + T: Config, + K: OffchainKeyGenerator, + L: FullLeaf<::Hashing>, + ReadWrite: StorageReadWrite, +{ + fn store_to_offchain(pos: NodeIndex, node: &NodeOf) { + let encoded_node = node.encode(); + + let key = ::offchain_key(pos); + debug!( + target: "runtime::mmr::offchain", "offchain db set: pos {} key {:?}", + pos, key + ); + // Indexing API is used to store the full node content. + offchain_index::set(&key, &encoded_node); + } +} + +fn peaks_to_prune_and_store( + old_size: NodeIndex, + new_size: NodeIndex, +) -> ( + impl Iterator, + Peekable>, +) { + // A sorted (ascending) collection of peak indices before and after insertion. + // both collections may share a common prefix. + let peaks_before = if old_size == 0 { + vec![] + } else { + helper::get_peaks(old_size) + }; + let peaks_after = helper::get_peaks(new_size); + trace!(target: "runtime::mmr", "peaks_before: {:?}", peaks_before); + trace!(target: "runtime::mmr", "peaks_after: {:?}", peaks_after); + let mut peaks_before = peaks_before.into_iter().peekable(); + let mut peaks_after = peaks_after.into_iter().peekable(); + + // Consume a common prefix between `peaks_before` and `peaks_after`, + // since that's something we will not be touching anyway. + while peaks_before.peek() == peaks_after.peek() { + peaks_before.next(); + peaks_after.next(); + } + + // what's left in both collections is: + // 1. Old peaks to remove from storage + // 2. New peaks to persist in storage + (peaks_before, peaks_after) +} diff --git a/src/mmr/utils.rs b/src/mmr/utils.rs new file mode 100644 index 000000000..493c8fe48 --- /dev/null +++ b/src/mmr/utils.rs @@ -0,0 +1,59 @@ +use crate::mmr::{LeafIndex, NodeIndex}; +use mmr_lib::helper; + +/// MMR nodes & size -related utilities. +pub struct NodesUtils { + no_of_leaves: LeafIndex, +} + +impl NodesUtils { + /// Create new instance of MMR nodes utilities for given number of leaves. + pub fn new(no_of_leaves: LeafIndex) -> Self { + Self { no_of_leaves } + } + + /// Calculate number of peaks in the MMR. + pub fn number_of_peaks(&self) -> NodeIndex { + self.number_of_leaves().count_ones() as NodeIndex + } + + /// Return the number of leaves in the MMR. + pub fn number_of_leaves(&self) -> LeafIndex { + self.no_of_leaves + } + + /// Calculate the total size of MMR (number of nodes). + pub fn size(&self) -> NodeIndex { + 2 * self.no_of_leaves - self.number_of_peaks() + } + + /// Calculate `LeafIndex` for the leaf that added `node_index` to the MMR. + pub fn leaf_index_that_added_node(node_index: NodeIndex) -> LeafIndex { + let rightmost_leaf_pos = Self::rightmost_leaf_node_index_from_pos(node_index); + Self::leaf_node_index_to_leaf_index(rightmost_leaf_pos) + } + + // Translate a _leaf_ `NodeIndex` to its `LeafIndex`. + fn leaf_node_index_to_leaf_index(pos: NodeIndex) -> LeafIndex { + if pos == 0 { + return 0; + } + let peaks = helper::get_peaks(pos); + (pos + peaks.len() as u64) >> 1 + } + + // Starting from any node position get position of rightmost leaf; this is the leaf + // responsible for the addition of node `pos`. + fn rightmost_leaf_node_index_from_pos(pos: NodeIndex) -> NodeIndex { + pos - (helper::pos_height_in_tree(pos) as u64) + } + + /// Starting from any leaf index, get the sequence of positions of the nodes added + /// to the mmr when this leaf was added (inclusive of the leaf's position itself). + /// That is, all of these nodes are right children of their respective parents. + pub fn _right_branch_ending_in_leaf(leaf_index: LeafIndex) -> Vec { + let pos = helper::leaf_index_to_pos(leaf_index); + let num_parents = leaf_index.trailing_ones() as u64; + return (pos..=pos + num_parents).collect(); + } +} diff --git a/src/primitives.rs b/src/primitives.rs new file mode 100644 index 000000000..4f21eee07 --- /dev/null +++ b/src/primitives.rs @@ -0,0 +1,52 @@ +use crate::mmr::{LeafIndex, NodeIndex}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; + +/// An MMR proof data for a group of leaves. +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct Proof { + /// The indices of the leaves the proof is for. + pub leaf_indices: Vec, + /// Number of leaves in MMR, when the proof was generated. + pub leaf_count: NodeIndex, + /// Proof elements (hashes of siblings of inner nodes on the path to the leaf). + pub items: Vec, +} + +/// Merkle Mountain Range operation error. +#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] +pub enum Error { + InvalidNumericOp, + Push, + GetRoot, + Commit, + GenerateProof, + Verify, + LeafNotFound, + PalletNotIncluded, + InvalidLeafIndex, + InvalidBestKnownBlock, +} + +sp_api::decl_runtime_apis! { + /// API to interact with pallet-ismp's Mmr. + pub trait IsmpMmrApi { + /// Return the on-chain MMR root hash for requests. + fn requests_mmr_root() -> Result; + + /// Return the number of MMR leaves for requests. + fn requests_mmr_leaf_count() -> Result; + + /// Return the on-chain MMR root hash for responses. + fn responses_mmr_root() -> Result; + + /// Return the number of MMR leaves for responses. + fn responses_mmr_leaf_count() -> Result; + + /// generate a proof + fn generate_request_proof( + leaf_indices: Vec + ) -> Result<(Vec, Proof), Error>; + + } +} From c23dc1fd7492e6e80d50f4c229394ccd8be6212e Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 15 Mar 2023 13:38:07 +0100 Subject: [PATCH 090/182] update ismp-rust --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 38fba9dac..093b5f245 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkad sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", rev = "2fc20996d80a43aaf2bfff79cfa9e57ac53d7223", default-features = false } +ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } [features] From 475292d8aabd7d9bd86333b70c320d0670fb8a25 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 15 Mar 2023 14:40:04 +0100 Subject: [PATCH 091/182] initial implementation for router --- src/host.rs | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 ++ src/router.rs | 71 +++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 src/host.rs create mode 100644 src/router.rs diff --git a/src/host.rs b/src/host.rs new file mode 100644 index 000000000..5150024dd --- /dev/null +++ b/src/host.rs @@ -0,0 +1,114 @@ +use crate::Config; +use ismp_rust::consensus_client::{ + ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, +}; +use ismp_rust::error::Error; +use ismp_rust::host::{ChainID, ISMPHost}; +use ismp_rust::router::{IISMPRouter, Request, Response}; +use std::time::Duration; + +#[derive(Clone)] +pub struct Host(core::marker::PhantomData); + +impl Default for Host { + fn default() -> Self { + Self(core::marker::PhantomData) + } +} + +impl ISMPHost for Host { + fn host(&self) -> ChainID { + ::CHAIN_ID + } + + fn latest_commitment_height(&self, id: StateMachineId) -> Result { + todo!() + } + + fn state_machine_commitment( + &self, + height: StateMachineHeight, + ) -> Result { + todo!() + } + + fn consensus_update_time(&self, id: ConsensusClientId) -> Result { + todo!() + } + + fn state_machine_update_time(&self, height: StateMachineHeight) -> Result { + todo!() + } + + fn consensus_state(&self, id: ConsensusClientId) -> Result, Error> { + todo!() + } + + fn host_timestamp(&self) -> Duration { + todo!() + } + + fn is_frozen(&self, height: StateMachineHeight) -> Result { + todo!() + } + + fn request_commitment(&self, req: &Request) -> Result, Error> { + todo!() + } + + fn response_commitment(&self, res: &Response) -> Result, Error> { + todo!() + } + + fn store_consensus_state(&self, id: ConsensusClientId, state: Vec) -> Result<(), Error> { + todo!() + } + + fn store_consensus_update_time( + &self, + id: ConsensusClientId, + timestamp: Duration, + ) -> Result<(), Error> { + todo!() + } + + fn store_state_machine_update_time( + &self, + height: StateMachineHeight, + timestamp: Duration, + ) -> Result<(), Error> { + todo!() + } + + fn store_state_machine_commitment( + &self, + height: StateMachineHeight, + state: StateCommitment, + ) -> Result<(), Error> { + todo!() + } + + fn freeze_state_machine(&self, height: StateMachineHeight) -> Result<(), Error> { + todo!() + } + + fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { + todo!() + } + + fn keccak256(&self, bytes: &[u8]) -> [u8; 32] { + todo!() + } + + fn delay_period(&self, id: StateMachineId) -> Duration { + todo!() + } + + fn client_id_from_state_id(&self, id: StateMachineId) -> Result { + todo!() + } + + fn ismp_router(&self) -> Box { + todo!() + } +} diff --git a/src/lib.rs b/src/lib.rs index f3dea6d4d..72e61bb0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,8 +16,10 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +pub mod host; mod mmr; mod primitives; +mod router; use codec::Encode; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -34,6 +36,7 @@ pub mod pallet { use crate::mmr::{LeafIndex, Mmr, NodeIndex}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use ismp_rust::host::ChainID; use sp_runtime::traits; /// Our pallet's configuration trait. All our types and constants go in here. If the @@ -66,6 +69,7 @@ pub mod pallet { /// Then we create a tuple of these two hashes, SCALE-encode it (concatenate) and /// hash, to obtain a new MMR inner node - the new peak. type Hashing: traits::Hash::Hash>; + const CHAIN_ID: ChainID; /// The hashing output type. /// /// This type is actually going to be stored in the MMR. diff --git a/src/router.rs b/src/router.rs new file mode 100644 index 000000000..f64157998 --- /dev/null +++ b/src/router.rs @@ -0,0 +1,71 @@ +use crate::host::Host; +use crate::mmr::{self, Leaf, Mmr}; +use crate::{ + Config, Pallet, RequestOffchainKey, RequestsStore, ResponseOffchainKey, ResponseStore, +}; +use core::marker::PhantomData; +use ismp_rust::error::Error; +use ismp_rust::host::ISMPHost; +use ismp_rust::router::{IISMPRouter, Request, Response}; + +#[derive(Default, Clone)] +pub struct Router(PhantomData); + +impl IISMPRouter for Router { + fn dispatch(&self, request: Request) -> Result<(), Error> { + let host = Host::::default(); + if host.host() != request.dest_chain { + let request_leaves = Pallet::::number_of_request_leaves(); + let (dest_chain, source_chain, nonce) = + (request.dest_chain, request.source_chain, request.nonce); + let mut request_mmr: Mmr< + mmr::storage::RuntimeStorage, + T, + Leaf, + RequestOffchainKey, + RequestsStore, + > = mmr::Mmr::new(request_leaves); + let leaf_index = request_mmr.push(Leaf::Request(request)).ok_or_else(|| { + Error::RequestVerificationFailed { + nonce, + source: source_chain, + dest: dest_chain, + } + })?; + // Deposit Event + // Store a map of request to leaf_index + } + + Ok(()) + } + + fn write_response(&self, response: Response) -> Result<(), Error> { + let host = Host::::default(); + if host.host() != response.request.dest_chain { + let response_leaves = Pallet::::number_of_response_leaves(); + let (dest_chain, source_chain, nonce) = ( + response.request.dest_chain, + response.request.source_chain, + response.request.nonce, + ); + let mut response_mmr: Mmr< + mmr::storage::RuntimeStorage, + T, + Leaf, + ResponseOffchainKey, + ResponseStore, + > = mmr::Mmr::new(response_leaves); + let leaf_index = response_mmr.push(Leaf::Response(response)).ok_or_else(|| { + Error::RequestVerificationFailed { + nonce, + source: source_chain, + dest: dest_chain, + } + })?; + // Deposit Event + // Store a map of response to leaf_index + } + + Ok(()) + } +} From 758c311e4d83a1a684f8f76b1161a03a34171951 Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 15 Mar 2023 17:14:42 +0100 Subject: [PATCH 092/182] enable retrieval of requests and responses from offchain storage --- src/lib.rs | 87 +++++++++++++++++++++++++++++++++++++++++++---- src/primitives.rs | 3 ++ src/router.rs | 5 ++- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 72e61bb0f..6d3a01fc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,10 +21,13 @@ mod mmr; mod primitives; mod router; -use codec::Encode; +use codec::{Decode, Encode}; +use frame_support::RuntimeDebug; +use ismp_rust::router::{Request, Response}; +use sp_core::offchain::StorageKind; // Re-export pallet items so that they can be accessed from the crate namespace. use crate::mmr::storage::{OffchainKeyGenerator, StorageReadWrite}; -use crate::mmr::{FullLeaf, Leaf, LeafIndex, Node, NodeIndex, NodeOf}; +use crate::mmr::{DataOrHash, FullLeaf, Leaf, LeafIndex, Node, NodeIndex, NodeOf}; pub use pallet::*; // Definition of the pallet logic, to be aggregated at runtime definition through @@ -34,6 +37,7 @@ pub mod pallet { // Import various types used to declare pallet in scope. use super::*; use crate::mmr::{LeafIndex, Mmr, NodeIndex}; + use crate::primitives::ISMP_ID; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use ismp_rust::host::ChainID; @@ -154,7 +158,7 @@ pub mod pallet { > = mmr::Mmr::new(request_leaves); // Update the size, `mmr.finalize()` should also never fail. - let (leaves, root) = match request_mmr.finalize() { + let (leaves, requests_root) = match request_mmr.finalize() { Ok((leaves, root)) => (leaves, root), Err(e) => { log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); @@ -163,7 +167,7 @@ pub mod pallet { }; >::put(leaves); - >::put(root); + >::put(requests_root); // handle finalizing response Mmr let response_leaves = Self::number_of_response_leaves(); @@ -177,7 +181,7 @@ pub mod pallet { > = mmr::Mmr::new(response_leaves); // Update the size, `mmr.finalize()` should also never fail. - let (leaves, root) = match response_mmr.finalize() { + let (leaves, responses_root) = match response_mmr.finalize() { Ok((leaves, root)) => (leaves, root), Err(e) => { log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); @@ -186,7 +190,15 @@ pub mod pallet { }; >::put(leaves); - >::put(root); + >::put(responses_root); + + let log = RequestResponseLog:: { + requests_root_hash: requests_root, + responses_root_hash: responses_root, + }; + + let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, log.encode()); + >::deposit_log(digest); } fn offchain_worker(_n: T::BlockNumber) {} @@ -316,3 +328,66 @@ impl::Hashing>> OffchainKeyGenerator (T::INDEXING_PREFIX, "Responses", pos).encode() } } + +#[derive(RuntimeDebug, Encode, Decode)] +pub struct RequestResponseLog { + requests_root_hash: ::Hash, + responses_root_hash: ::Hash, +} + +impl Pallet { + fn request_leaf_index_offchain_key(req: &Request) -> Vec { + ( + T::INDEXING_PREFIX, + "Requests/leaf_indices", + req.dest_chain, + req.nonce, + ) + .encode() + } + fn response_leaf_index_offchain_key(res: &Response) -> Vec { + ( + T::INDEXING_PREFIX, + "Responses/leaf_indices", + res.request.source_chain, + res.request.nonce, + ) + .encode() + } + + fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { + sp_io::offchain_index::set(&key, &leaf_index.encode()); + } + + fn get_request(leaf_index: LeafIndex) -> Option { + let key = RequestOffchainKey::::offchain_key(leaf_index); + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + let data_or_hash = + DataOrHash::<::Hashing, Leaf>::decode(&mut &*elem).ok()?; + return match data_or_hash { + DataOrHash::Data(leaf) => match leaf { + Leaf::Request(req) => Some(req), + _ => None, + }, + _ => None, + }; + } + None + } + + fn get_response(leaf_index: LeafIndex) -> Option { + let key = ResponseOffchainKey::::offchain_key(leaf_index); + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + let data_or_hash = + DataOrHash::<::Hashing, Leaf>::decode(&mut &*elem).ok()?; + return match data_or_hash { + DataOrHash::Data(leaf) => match leaf { + Leaf::Response(res) => Some(res), + _ => None, + }, + _ => None, + }; + } + None + } +} diff --git a/src/primitives.rs b/src/primitives.rs index 4f21eee07..da626ed06 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -2,6 +2,9 @@ use crate::mmr::{LeafIndex, NodeIndex}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; +/// The `ConsensusEngineId` of ISMP. +pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; + /// An MMR proof data for a group of leaves. #[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] pub struct Proof { diff --git a/src/router.rs b/src/router.rs index f64157998..21883d0aa 100644 --- a/src/router.rs +++ b/src/router.rs @@ -25,6 +25,7 @@ impl IISMPRouter for Router { RequestOffchainKey, RequestsStore, > = mmr::Mmr::new(request_leaves); + let offchain_key = Pallet::::request_leaf_index_offchain_key(&request); let leaf_index = request_mmr.push(Leaf::Request(request)).ok_or_else(|| { Error::RequestVerificationFailed { nonce, @@ -34,6 +35,7 @@ impl IISMPRouter for Router { })?; // Deposit Event // Store a map of request to leaf_index + Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } Ok(()) @@ -55,6 +57,7 @@ impl IISMPRouter for Router { ResponseOffchainKey, ResponseStore, > = mmr::Mmr::new(response_leaves); + let offchain_key = Pallet::::response_leaf_index_offchain_key(&response); let leaf_index = response_mmr.push(Leaf::Response(response)).ok_or_else(|| { Error::RequestVerificationFailed { nonce, @@ -63,7 +66,7 @@ impl IISMPRouter for Router { } })?; // Deposit Event - // Store a map of response to leaf_index + Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } Ok(()) From 58b8e9d4fb4e15f35e0a6c3ddd3f941a06db85ab Mon Sep 17 00:00:00 2001 From: David Salami Date: Wed, 15 Mar 2023 17:36:09 +0100 Subject: [PATCH 093/182] minor change --- src/router.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router.rs b/src/router.rs index 21883d0aa..714bda332 100644 --- a/src/router.rs +++ b/src/router.rs @@ -43,7 +43,7 @@ impl IISMPRouter for Router { fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); - if host.host() != response.request.dest_chain { + if host.host() != response.request.source_chain { let response_leaves = Pallet::::number_of_response_leaves(); let (dest_chain, source_chain, nonce) = ( response.request.dest_chain, From a919ca4df6eef2ccad0a98526f3d07eb2a404a2a Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 16 Mar 2023 13:07:52 +0100 Subject: [PATCH 094/182] use single mmr tree --- src/lib.rs | 183 ++++++++------------------------------------- src/mmr/mmr.rs | 81 +++++--------------- src/mmr/mod.rs | 67 +++++++++++------ src/mmr/storage.rs | 61 +++++---------- src/primitives.rs | 17 ++--- src/router.rs | 28 ++----- 6 files changed, 126 insertions(+), 311 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6d3a01fc5..3af345c97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,8 +26,7 @@ use frame_support::RuntimeDebug; use ismp_rust::router::{Request, Response}; use sp_core::offchain::StorageKind; // Re-export pallet items so that they can be accessed from the crate namespace. -use crate::mmr::storage::{OffchainKeyGenerator, StorageReadWrite}; -use crate::mmr::{DataOrHash, FullLeaf, Leaf, LeafIndex, Node, NodeIndex, NodeOf}; +use crate::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}; pub use pallet::*; // Definition of the pallet logic, to be aggregated at runtime definition through @@ -84,6 +83,7 @@ pub mod pallet { + sp_std::hash::Hash + AsRef<[u8]> + AsMut<[u8]> + + From<[u8; 32]> + Copy + Default + codec::Codec @@ -100,23 +100,13 @@ pub mod pallet { /// Latest MMR Root hash for requests #[pallet::storage] - #[pallet::getter(fn requests_root_hash)] - pub type RequestsRootHash = StorageValue<_, ::Hash, ValueQuery>; - - /// Latest MMR Root hash for responses - #[pallet::storage] - #[pallet::getter(fn responses_root_hash)] - pub type ResponsesRootHash = StorageValue<_, ::Hash, ValueQuery>; + #[pallet::getter(fn mmr_root_hash)] + pub type RootHash = StorageValue<_, ::Hash, ValueQuery>; /// Current size of the MMR (number of leaves) for requests. #[pallet::storage] - #[pallet::getter(fn number_of_request_leaves)] - pub type NumberOfRequestLeaves = StorageValue<_, LeafIndex, ValueQuery>; - - /// Current size of the MMR (number of leaves) for responses. - #[pallet::storage] - #[pallet::getter(fn number_of_response_leaves)] - pub type NumberOfResponseLeaves = StorageValue<_, LeafIndex, ValueQuery>; + #[pallet::getter(fn number_of_leaves)] + pub type NumberOfLeaves = StorageValue<_, LeafIndex, ValueQuery>; /// Hashes of the nodes in the MMR for requests. /// @@ -124,16 +114,7 @@ pub mod pallet { /// are pruned and only stored in the Offchain DB. #[pallet::storage] #[pallet::getter(fn request_peaks)] - pub type RequestNodes = - StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; - - /// Hashes of the nodes in the MMR for responses. - /// - /// Note this collection only contains MMR peaks, the inner nodes (and leaves) - /// are pruned and only stored in the Offchain DB. - #[pallet::storage] - #[pallet::getter(fn response_peaks)] - pub type ResponseNodes = + pub type Nodes = StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; // Pallet implements [`Hooks`] trait to define some logic to execute in some context. @@ -147,18 +128,12 @@ pub mod pallet { fn on_finalize(_n: T::BlockNumber) { use crate::mmr; // handle finalizing requests Mmr - let request_leaves = Self::number_of_request_leaves(); + let leaves = Self::number_of_leaves(); - let request_mmr: Mmr< - mmr::storage::RuntimeStorage, - T, - Leaf, - RequestOffchainKey, - RequestsStore, - > = mmr::Mmr::new(request_leaves); + let mmr: Mmr = mmr::Mmr::new(leaves); // Update the size, `mmr.finalize()` should also never fail. - let (leaves, requests_root) = match request_mmr.finalize() { + let (leaves, root) = match mmr.finalize() { Ok((leaves, root)) => (leaves, root), Err(e) => { log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); @@ -166,35 +141,11 @@ pub mod pallet { } }; - >::put(leaves); - >::put(requests_root); - - // handle finalizing response Mmr - let response_leaves = Self::number_of_response_leaves(); - - let response_mmr: Mmr< - mmr::storage::RuntimeStorage, - T, - Leaf, - ResponseOffchainKey, - ResponseStore, - > = mmr::Mmr::new(response_leaves); - - // Update the size, `mmr.finalize()` should also never fail. - let (leaves, responses_root) = match response_mmr.finalize() { - Ok((leaves, root)) => (leaves, root), - Err(e) => { - log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); - return; - } - }; - - >::put(leaves); - >::put(responses_root); + >::put(leaves); + >::put(root); let log = RequestResponseLog:: { - requests_root_hash: requests_root, - responses_root_hash: responses_root, + mmr_root_hash: root, }; let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, log.encode()); @@ -223,116 +174,49 @@ impl Pallet { /// (Offchain Worker or Runtime API call), since it requires /// all the leaves to be present. /// It may return an error or panic if used incorrectly. - pub fn generate_request_proof( + pub fn generate_proof( leaf_indices: Vec, ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> { - let leaves_count = NumberOfRequestLeaves::::get(); - let mmr = mmr::Mmr::< - mmr::storage::OffchainStorage, - T, - Leaf, - RequestOffchainKey, - RequestsStore, - >::new(leaves_count); - mmr.generate_request_proof(leaf_indices) + let leaves_count = NumberOfLeaves::::get(); + let mmr = mmr::Mmr::::new(leaves_count); + mmr.generate_proof(leaf_indices) } - pub fn generate_response_proof( - leaf_indices: Vec, - ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> { - let leaves_count = NumberOfRequestLeaves::::get(); - let mmr = mmr::Mmr::< - mmr::storage::OffchainStorage, - T, - Leaf, - ResponseOffchainKey, - ResponseStore, - >::new(leaves_count); - mmr.generate_response_proof(leaf_indices) - } - - /// Return the on-chain MMR root hash. - pub fn requests_mmr_root() -> ::Hash { - Self::requests_root_hash() - } /// Return the on-chain MMR root hash. - pub fn responses_mmr_root() -> ::Hash { - Self::responses_root_hash() + pub fn mmr_root() -> ::Hash { + Self::mmr_root_hash() } } -pub struct RequestsStore(core::marker::PhantomData); - -impl::Hashing>> StorageReadWrite for RequestsStore { - fn get_node(pos: NodeIndex) -> Option> { - RequestNodes::::get(pos).map(Node::Hash) - } - - fn remove_node(pos: NodeIndex) { - RequestNodes::::remove(pos); - } - - fn insert_node(pos: NodeIndex, node: ::Hash) { - RequestNodes::::insert(pos, node) - } - - fn get_num_leaves() -> LeafIndex { - NumberOfRequestLeaves::::get() - } - - fn set_num_leaves(num_leaves: LeafIndex) { - NumberOfRequestLeaves::::put(num_leaves) - } -} - -pub struct ResponseStore(core::marker::PhantomData); - -impl::Hashing>> StorageReadWrite for ResponseStore { - fn get_node(pos: NodeIndex) -> Option> { - ResponseNodes::::get(pos).map(Node::Hash) +impl Pallet { + fn get_node(pos: NodeIndex) -> Option> { + Nodes::::get(pos).map(NodeOf::Hash) } fn remove_node(pos: NodeIndex) { - ResponseNodes::::remove(pos); + Nodes::::remove(pos); } fn insert_node(pos: NodeIndex, node: ::Hash) { - ResponseNodes::::insert(pos, node) + Nodes::::insert(pos, node) } fn get_num_leaves() -> LeafIndex { - NumberOfResponseLeaves::::get() + NumberOfLeaves::::get() } fn set_num_leaves(num_leaves: LeafIndex) { - NumberOfResponseLeaves::::put(num_leaves) + NumberOfLeaves::::put(num_leaves) } -} - -pub struct RequestOffchainKey(core::marker::PhantomData<(T, L)>); -impl::Hashing>> OffchainKeyGenerator - for RequestOffchainKey -{ fn offchain_key(pos: NodeIndex) -> Vec { - (T::INDEXING_PREFIX, "Requests", pos).encode() - } -} - -pub struct ResponseOffchainKey(core::marker::PhantomData<(T, L)>); - -impl::Hashing>> OffchainKeyGenerator - for ResponseOffchainKey -{ - fn offchain_key(pos: NodeIndex) -> Vec { - (T::INDEXING_PREFIX, "Responses", pos).encode() + (T::INDEXING_PREFIX, "Requests/Responses", pos).encode() } } #[derive(RuntimeDebug, Encode, Decode)] pub struct RequestResponseLog { - requests_root_hash: ::Hash, - responses_root_hash: ::Hash, + mmr_root_hash: ::Hash, } impl Pallet { @@ -345,6 +229,7 @@ impl Pallet { ) .encode() } + fn response_leaf_index_offchain_key(res: &Response) -> Vec { ( T::INDEXING_PREFIX, @@ -360,10 +245,9 @@ impl Pallet { } fn get_request(leaf_index: LeafIndex) -> Option { - let key = RequestOffchainKey::::offchain_key(leaf_index); + let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - let data_or_hash = - DataOrHash::<::Hashing, Leaf>::decode(&mut &*elem).ok()?; + let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; return match data_or_hash { DataOrHash::Data(leaf) => match leaf { Leaf::Request(req) => Some(req), @@ -376,10 +260,9 @@ impl Pallet { } fn get_response(leaf_index: LeafIndex) -> Option { - let key = ResponseOffchainKey::::offchain_key(leaf_index); + let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - let data_or_hash = - DataOrHash::<::Hashing, Leaf>::decode(&mut &*elem).ok()?; + let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; return match data_or_hash { DataOrHash::Data(leaf) => match leaf { Leaf::Response(res) => Some(res), diff --git a/src/mmr/mmr.rs b/src/mmr/mmr.rs index 205cb8cfb..768925063 100644 --- a/src/mmr/mmr.rs +++ b/src/mmr/mmr.rs @@ -15,17 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::mmr::storage::{OffchainKeyGenerator, StorageReadWrite}; use crate::mmr::utils::NodesUtils; -use crate::mmr::{FullLeaf, HashingOf, NodeIndex}; +use crate::mmr::{FullLeaf, NodeIndex}; use crate::primitives::Proof; use crate::{ mmr::{ storage::{OffchainStorage, RuntimeStorage, Storage}, - Hasher, Node, NodeOf, + Hasher, NodeOf, }, primitives::Error, - Config, RequestOffchainKey, RequestsStore, ResponseOffchainKey, ResponseStore, + Config, }; use sp_std::prelude::*; @@ -33,29 +32,21 @@ use sp_std::prelude::*; /// /// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage) /// vs [Off-chain](crate::mmr::storage::OffchainStorage)). -pub struct Mmr +pub struct Mmr where T: Config, - L: FullLeaf<::Hashing>, - ReadWrite: StorageReadWrite, - K: OffchainKeyGenerator, - Storage: mmr_lib::MMRStore>, + L: FullLeaf, + Storage: mmr_lib::MMRStore>, { - mmr: mmr_lib::MMR< - NodeOf, - Hasher, L>, - Storage, - >, + mmr: mmr_lib::MMR, Hasher, Storage>, leaves: NodeIndex, } -impl Mmr +impl Mmr where T: Config, - L: FullLeaf<::Hashing>, - ReadWrite: StorageReadWrite, - K: OffchainKeyGenerator, - Storage: mmr_lib::MMRStore>, + L: FullLeaf, + Storage: mmr_lib::MMRStore>, { /// Create a pointer to an existing MMR with given number of leaves. pub fn new(leaves: NodeIndex) -> Self { @@ -74,12 +65,10 @@ where } /// Runtime specific MMR functions. -impl Mmr +impl Mmr where T: Config, - L: FullLeaf<::Hashing>, - ReadWrite: StorageReadWrite, - K: OffchainKeyGenerator, + L: FullLeaf, { /// Push another item to the MMR. /// @@ -87,7 +76,7 @@ where pub fn push(&mut self, leaf: L) -> Option { let position = self .mmr - .push(Node::Data(leaf)) + .push(NodeOf::Data(leaf)) .map_err(|_| Error::Push) .ok()?; @@ -106,18 +95,16 @@ where } /// Off-chain specific MMR functions. -impl Mmr +impl Mmr where T: Config, - L: FullLeaf<::Hashing> + codec::Decode, - ReadWrite: StorageReadWrite, - K: OffchainKeyGenerator, + L: FullLeaf + codec::Decode, { /// Generate a proof for given leaf indices. /// /// Proof generation requires all the nodes (or their hashes) to be available in the storage. /// (i.e. you can't run the function in the pruned storage). - pub fn generate_request_proof( + pub fn generate_proof( &self, leaf_indices: Vec, ) -> Result<(Vec, Proof<::Hash>), Error> { @@ -125,43 +112,11 @@ where .iter() .map(|index| mmr_lib::leaf_index_to_pos(*index)) .collect::>(); - let store = - , RequestsStore>>::default(); + let store = >::default(); let leaves = positions .iter() .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { - Ok(Some(Node::Data(leaf))) => Ok(leaf), - _ => Err(Error::LeafNotFound), - }) - .collect::, Error>>()?; - - let leaf_count = self.leaves; - self.mmr - .gen_proof(positions) - .map_err(|_| Error::GenerateProof) - .map(|p| Proof { - leaf_indices, - leaf_count, - items: p.proof_items().iter().map(|x| x.hash()).collect(), - }) - .map(|p| (leaves, p)) - } - - pub fn generate_response_proof( - &self, - leaf_indices: Vec, - ) -> Result<(Vec, Proof<::Hash>), Error> { - let positions = leaf_indices - .iter() - .map(|index| mmr_lib::leaf_index_to_pos(*index)) - .collect::>(); - let store = - , ResponseStore>>::default( - ); - let leaves = positions - .iter() - .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { - Ok(Some(Node::Data(leaf))) => Ok(leaf), + Ok(Some(NodeOf::Data(leaf))) => Ok(leaf), _ => Err(Error::LeafNotFound), }) .collect::, Error>>()?; diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs index b09085ddd..d9360c9f5 100644 --- a/src/mmr/mod.rs +++ b/src/mmr/mod.rs @@ -15,13 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. use core::fmt; +use std::fmt::Formatter; + pub mod mmr; pub mod storage; mod utils; +use crate::host::Host; use crate::Config; use codec::{Decode, Encode}; -use frame_support::RuntimeDebug; +use ismp_rust::host::ISMPHost; use ismp_rust::router::{Request, Response}; use sp_runtime::traits; @@ -29,8 +32,6 @@ pub use self::mmr::Mmr; pub type LeafIndex = u64; pub type NodeIndex = u64; -pub(crate) type HashingOf = ::Hashing; - #[derive(Debug, Clone, Decode, Encode, PartialEq, Eq)] pub enum Leaf { Request(Request), @@ -38,38 +39,61 @@ pub enum Leaf { } /// A full leaf content stored in the offchain-db. -pub trait FullLeaf: Clone + fmt::Debug + PartialEq + Eq + codec::Codec { +pub trait FullLeaf: Clone + fmt::Debug + PartialEq + Eq + codec::Codec { /// Returns the hash of the leaf - fn hash(&self) -> H::Output; + fn hash(&self) -> <::Hashing as traits::Hash>::Output; } -impl FullLeaf for Leaf { - fn hash(&self) -> H::Output { - todo!() +impl FullLeaf for Leaf { + fn hash(&self) -> <::Hashing as traits::Hash>::Output { + let host = Host::::default(); + match self { + Leaf::Request(req) => { + let commitment = host.get_request_commitment(req); + let mut hash = [0u8; 32]; + hash.copy_from_slice(&commitment[..]); + ::Hash::from(hash) + } + Leaf::Response(res) => { + let commitment = host.get_response_commitment(res); + let mut hash = [0u8; 32]; + hash.copy_from_slice(&commitment[..]); + ::Hash::from(hash) + } + } } } /// An element representing either full data or its hash. -#[derive(RuntimeDebug, Clone, PartialEq, Eq, Encode, Decode)] -pub enum DataOrHash { +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +pub enum DataOrHash { /// Arbitrary data in its full form. Data(L), /// A hash of some data. - Hash(H::Output), + Hash(<::Hashing as traits::Hash>::Output), +} + +impl core::fmt::Debug for DataOrHash { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + DataOrHash::Data(leaf) => f.debug_struct("DataOrHash").field("Data", leaf).finish(), + DataOrHash::Hash(hash) => f.debug_struct("DataOrHash").field("Hash", hash).finish(), + } + } } -impl From for DataOrHash { +impl From for DataOrHash { fn from(l: L) -> Self { Self::Data(l) } } -impl> DataOrHash { +impl> DataOrHash { /// Retrieve a hash of this item. /// /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. - pub fn hash(&self) -> H::Output { + pub fn hash(&self) -> <::Hashing as traits::Hash>::Output { match *self { Self::Data(ref leaf) => leaf.hash(), Self::Hash(ref hash) => *hash, @@ -77,21 +101,20 @@ impl> DataOrHash { } } /// Node type for runtime `T`. -pub type NodeOf = Node<::Hashing, L>; - -/// A node stored in the MMR. -pub type Node = DataOrHash; +pub type NodeOf = DataOrHash; /// Default Merging & Hashing behavior for MMR. -pub struct Hasher(sp_std::marker::PhantomData<(H, L)>); +pub struct Hasher(sp_std::marker::PhantomData<(T, L)>); -impl> mmr_lib::Merge for Hasher { - type Item = Node; +impl> mmr_lib::Merge for Hasher { + type Item = NodeOf; fn merge(left: &Self::Item, right: &Self::Item) -> mmr_lib::Result { let mut concat = left.hash().as_ref().to_vec(); concat.extend_from_slice(right.hash().as_ref()); - Ok(Node::Hash(::hash(&concat))) + Ok(NodeOf::Hash( + <::Hashing as traits::Hash>::hash(&concat), + )) } } diff --git a/src/mmr/storage.rs b/src/mmr/storage.rs index 483b986c5..42f34acc9 100644 --- a/src/mmr/storage.rs +++ b/src/mmr/storage.rs @@ -26,23 +26,8 @@ use sp_std::iter::Peekable; use sp_std::prelude::*; use crate::mmr::utils::NodesUtils; -use crate::mmr::{FullLeaf, LeafIndex, NodeIndex}; -use crate::{ - mmr::{Node, NodeOf}, - Config, -}; - -pub trait OffchainKeyGenerator { - fn offchain_key(pos: NodeIndex) -> Vec; -} - -pub trait StorageReadWrite { - fn get_node(pos: NodeIndex) -> Option>; - fn remove_node(pos: NodeIndex); - fn insert_node(pos: NodeIndex, node: ::Hash); - fn get_num_leaves() -> LeafIndex; - fn set_num_leaves(num_leaves: LeafIndex); -} +use crate::mmr::{FullLeaf, NodeIndex}; +use crate::{mmr::NodeOf, Config, Pallet}; /// A marker type for runtime-specific storage implementation. /// @@ -64,29 +49,24 @@ pub struct OffchainStorage; /// /// There are two different implementations depending on the use case. /// See docs for [RuntimeStorage] and [OffchainStorage]. -pub struct Storage( - sp_std::marker::PhantomData<(StorageType, T, L, K, ReadWrite)>, -); +pub struct Storage(sp_std::marker::PhantomData<(StorageType, T, L)>); -impl Default for Storage { +impl Default for Storage { fn default() -> Self { Self(Default::default()) } } -impl mmr_lib::MMRStore> - for Storage +impl mmr_lib::MMRStore> for Storage where T: Config, - K: OffchainKeyGenerator, - L: FullLeaf<::Hashing> + codec::Decode, - ReadWrite: StorageReadWrite, + L: FullLeaf + codec::Decode, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { // Find out which leaf added node `pos` in the MMR. let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos); - let key = ::offchain_key(pos); + let key = Pallet::::offchain_key(pos); debug!( target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, canon key {:?}", pos, ancestor_leaf_idx, key @@ -104,16 +84,13 @@ where } } -impl mmr_lib::MMRStore> - for Storage +impl mmr_lib::MMRStore> for Storage where T: Config, - K: OffchainKeyGenerator, - L: FullLeaf<::Hashing>, - ReadWrite: StorageReadWrite, + L: FullLeaf, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { - Ok(ReadWrite::get_node(pos)) + Ok(Pallet::::get_node(pos)) } fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { @@ -126,7 +103,7 @@ where elems.iter().map(|elem| elem.hash()).collect::>() ); - let leaves = ReadWrite::get_num_leaves(); + let leaves = Pallet::::get_num_leaves(); let size = NodesUtils::new(leaves).size(); if pos != size { @@ -146,41 +123,39 @@ where for elem in elems { // On-chain we are going to only store new peaks. if peaks_to_store.next_if_eq(&node_index).is_some() { - ReadWrite::insert_node(node_index, elem.hash()); + Pallet::::insert_node(node_index, elem.hash()); } // We are storing full node off-chain (using indexing API). Self::store_to_offchain(node_index, &elem); // Increase the indices. - if let Node::Data(..) = elem { + if let NodeOf::Data(..) = elem { leaf_index += 1; } node_index += 1; } // Update current number of leaves. - ReadWrite::set_num_leaves(leaf_index); + Pallet::::set_num_leaves(leaf_index); // And remove all remaining items from `peaks_before` collection. for pos in peaks_to_prune { - ReadWrite::remove_node(pos); + Pallet::::remove_node(pos); } Ok(()) } } -impl Storage +impl Storage where T: Config, - K: OffchainKeyGenerator, - L: FullLeaf<::Hashing>, - ReadWrite: StorageReadWrite, + L: FullLeaf, { fn store_to_offchain(pos: NodeIndex, node: &NodeOf) { let encoded_node = node.encode(); - let key = ::offchain_key(pos); + let key = Pallet::::offchain_key(pos); debug!( target: "runtime::mmr::offchain", "offchain db set: pos {} key {:?}", pos, key diff --git a/src/primitives.rs b/src/primitives.rs index da626ed06..0109f0f89 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -34,22 +34,15 @@ pub enum Error { sp_api::decl_runtime_apis! { /// API to interact with pallet-ismp's Mmr. pub trait IsmpMmrApi { - /// Return the on-chain MMR root hash for requests. - fn requests_mmr_root() -> Result; + /// Return the number of MMR leaves. + fn mmr_leaf_count() -> Result; - /// Return the number of MMR leaves for requests. - fn requests_mmr_leaf_count() -> Result; - - /// Return the on-chain MMR root hash for responses. - fn responses_mmr_root() -> Result; - - /// Return the number of MMR leaves for responses. - fn responses_mmr_leaf_count() -> Result; + /// Return the on-chain MMR root hash. + fn mmr_root() -> Result; /// generate a proof - fn generate_request_proof( + fn generate_proof( leaf_indices: Vec ) -> Result<(Vec, Proof), Error>; - } } diff --git a/src/router.rs b/src/router.rs index 714bda332..3fdf04553 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,8 +1,6 @@ use crate::host::Host; use crate::mmr::{self, Leaf, Mmr}; -use crate::{ - Config, Pallet, RequestOffchainKey, RequestsStore, ResponseOffchainKey, ResponseStore, -}; +use crate::{Config, Pallet}; use core::marker::PhantomData; use ismp_rust::error::Error; use ismp_rust::host::ISMPHost; @@ -15,18 +13,12 @@ impl IISMPRouter for Router { fn dispatch(&self, request: Request) -> Result<(), Error> { let host = Host::::default(); if host.host() != request.dest_chain { - let request_leaves = Pallet::::number_of_request_leaves(); + let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = (request.dest_chain, request.source_chain, request.nonce); - let mut request_mmr: Mmr< - mmr::storage::RuntimeStorage, - T, - Leaf, - RequestOffchainKey, - RequestsStore, - > = mmr::Mmr::new(request_leaves); + let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = Pallet::::request_leaf_index_offchain_key(&request); - let leaf_index = request_mmr.push(Leaf::Request(request)).ok_or_else(|| { + let leaf_index = mmr.push(Leaf::Request(request)).ok_or_else(|| { Error::RequestVerificationFailed { nonce, source: source_chain, @@ -44,21 +36,15 @@ impl IISMPRouter for Router { fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); if host.host() != response.request.source_chain { - let response_leaves = Pallet::::number_of_response_leaves(); + let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = ( response.request.dest_chain, response.request.source_chain, response.request.nonce, ); - let mut response_mmr: Mmr< - mmr::storage::RuntimeStorage, - T, - Leaf, - ResponseOffchainKey, - ResponseStore, - > = mmr::Mmr::new(response_leaves); + let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = Pallet::::response_leaf_index_offchain_key(&response); - let leaf_index = response_mmr.push(Leaf::Response(response)).ok_or_else(|| { + let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { Error::RequestVerificationFailed { nonce, source: source_chain, From 17e76f2fb215994cf02c8db6ee13bf998766f446 Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 16 Mar 2023 13:42:36 +0100 Subject: [PATCH 095/182] add some events --- src/lib.rs | 21 ++++++++++++++++++++- src/router.rs | 11 ++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3af345c97..a2832e9b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ pub mod pallet { use crate::primitives::ISMP_ID; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use ismp_rust::consensus_client::ConsensusClientId; use ismp_rust::host::ChainID; use sp_runtime::traits; @@ -165,7 +166,25 @@ pub mod pallet { /// This attribute generate the function `deposit_event` to deposit one of this pallet event, /// it is optional, it is also possible to provide a custom implementation. #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event {} + pub enum Event { + ConsensusClientUpdated { + id: ConsensusClientId, + height: u64, + }, + ResponseReceived { + // todo: Add scale info to ChainID type + // /// Chain that this reponse will be routed to + // dest_chain: ChainID, + /// Nonce for the request which this response is for + request_nonce: u64, + }, + RequestReceived { + // /// Chain that this reponse will be routed to + // dest_chain: ChainID, + /// Request nonce + request_nonce: u64, + }, + } } impl Pallet { diff --git a/src/router.rs b/src/router.rs index 3fdf04553..9dbc4ea2f 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,6 +1,6 @@ use crate::host::Host; use crate::mmr::{self, Leaf, Mmr}; -use crate::{Config, Pallet}; +use crate::{Config, Event, Pallet}; use core::marker::PhantomData; use ismp_rust::error::Error; use ismp_rust::host::ISMPHost; @@ -26,6 +26,9 @@ impl IISMPRouter for Router { } })?; // Deposit Event + Pallet::::deposit_event(Event::RequestReceived { + request_nonce: nonce, + }); // Store a map of request to leaf_index Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } @@ -45,13 +48,15 @@ impl IISMPRouter for Router { let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = Pallet::::response_leaf_index_offchain_key(&response); let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { - Error::RequestVerificationFailed { + Error::ResponseVerificationFailed { nonce, source: source_chain, dest: dest_chain, } })?; - // Deposit Event + Pallet::::deposit_event(Event::ResponseReceived { + request_nonce: nonce, + }); Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } From 90bb022594bda633c2e2513e852636e8add61176 Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 16 Mar 2023 14:12:37 +0100 Subject: [PATCH 096/182] refactor --- src/lib.rs | 26 +++++++++++++++++++------- src/router.rs | 4 ++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a2832e9b1..2223987a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ mod router; use codec::{Decode, Encode}; use frame_support::RuntimeDebug; +use ismp_rust::host::ChainID; use ismp_rust::router::{Request, Response}; use sp_core::offchain::StorageKind; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -128,7 +129,6 @@ pub mod pallet { fn on_finalize(_n: T::BlockNumber) { use crate::mmr; - // handle finalizing requests Mmr let leaves = Self::number_of_leaves(); let mmr: Mmr = mmr::Mmr::new(leaves); @@ -239,22 +239,22 @@ pub struct RequestResponseLog { } impl Pallet { - fn request_leaf_index_offchain_key(req: &Request) -> Vec { + fn request_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { ( T::INDEXING_PREFIX, "Requests/leaf_indices", - req.dest_chain, - req.nonce, + dest_chain, + nonce, ) .encode() } - fn response_leaf_index_offchain_key(res: &Response) -> Vec { + fn response_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { ( T::INDEXING_PREFIX, "Responses/leaf_indices", - res.request.source_chain, - res.request.nonce, + dest_chain, + nonce, ) .encode() } @@ -292,4 +292,16 @@ impl Pallet { } None } + + fn get_leaf_index(dest_chain: ChainID, nonce: u64, is_req: bool) -> Option { + let key = if is_req { + Self::request_leaf_index_offchain_key(dest_chain, nonce) + } else { + Self::response_leaf_index_offchain_key(dest_chain, nonce) + }; + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + return LeafIndex::decode(&mut &*elem).ok(); + } + None + } } diff --git a/src/router.rs b/src/router.rs index 9dbc4ea2f..6d89c4e71 100644 --- a/src/router.rs +++ b/src/router.rs @@ -17,7 +17,7 @@ impl IISMPRouter for Router { let (dest_chain, source_chain, nonce) = (request.dest_chain, request.source_chain, request.nonce); let mut mmr: Mmr = mmr::Mmr::new(leaves); - let offchain_key = Pallet::::request_leaf_index_offchain_key(&request); + let offchain_key = Pallet::::request_leaf_index_offchain_key(dest_chain, nonce); let leaf_index = mmr.push(Leaf::Request(request)).ok_or_else(|| { Error::RequestVerificationFailed { nonce, @@ -46,7 +46,7 @@ impl IISMPRouter for Router { response.request.nonce, ); let mut mmr: Mmr = mmr::Mmr::new(leaves); - let offchain_key = Pallet::::response_leaf_index_offchain_key(&response); + let offchain_key = Pallet::::response_leaf_index_offchain_key(source_chain, nonce); let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { Error::ResponseVerificationFailed { nonce, From 244e1c8fc472ef7f82c5f3e0125c1caa1a5cd226 Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 16 Mar 2023 14:38:30 +0100 Subject: [PATCH 097/182] update ismp-rust --- src/lib.rs | 19 +++++++++---------- src/mmr/storage.rs | 2 +- src/router.rs | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2223987a8..0e134142e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,15 +172,14 @@ pub mod pallet { height: u64, }, ResponseReceived { - // todo: Add scale info to ChainID type - // /// Chain that this reponse will be routed to - // dest_chain: ChainID, + /// Chain that this reponse will be routed to + dest_chain: ChainID, /// Nonce for the request which this response is for request_nonce: u64, }, RequestReceived { - // /// Chain that this reponse will be routed to - // dest_chain: ChainID, + /// Chain that this reponse will be routed to + dest_chain: ChainID, /// Request nonce request_nonce: u64, }, @@ -239,7 +238,7 @@ pub struct RequestResponseLog { } impl Pallet { - fn request_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { + pub fn request_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { ( T::INDEXING_PREFIX, "Requests/leaf_indices", @@ -249,7 +248,7 @@ impl Pallet { .encode() } - fn response_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { + pub fn response_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { ( T::INDEXING_PREFIX, "Responses/leaf_indices", @@ -263,7 +262,7 @@ impl Pallet { sp_io::offchain_index::set(&key, &leaf_index.encode()); } - fn get_request(leaf_index: LeafIndex) -> Option { + pub fn get_request(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; @@ -278,7 +277,7 @@ impl Pallet { None } - fn get_response(leaf_index: LeafIndex) -> Option { + pub fn get_response(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; @@ -293,7 +292,7 @@ impl Pallet { None } - fn get_leaf_index(dest_chain: ChainID, nonce: u64, is_req: bool) -> Option { + pub fn get_leaf_index(dest_chain: ChainID, nonce: u64, is_req: bool) -> Option { let key = if is_req { Self::request_leaf_index_offchain_key(dest_chain, nonce) } else { diff --git a/src/mmr/storage.rs b/src/mmr/storage.rs index 42f34acc9..f089eaa56 100644 --- a/src/mmr/storage.rs +++ b/src/mmr/storage.rs @@ -68,7 +68,7 @@ where let key = Pallet::::offchain_key(pos); debug!( - target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, canon key {:?}", + target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, key {:?}", pos, ancestor_leaf_idx, key ); // Try to retrieve the element from Off-chain DB. diff --git a/src/router.rs b/src/router.rs index 6d89c4e71..f070c6a6b 100644 --- a/src/router.rs +++ b/src/router.rs @@ -28,6 +28,7 @@ impl IISMPRouter for Router { // Deposit Event Pallet::::deposit_event(Event::RequestReceived { request_nonce: nonce, + dest_chain }); // Store a map of request to leaf_index Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) @@ -56,6 +57,7 @@ impl IISMPRouter for Router { })?; Pallet::::deposit_event(Event::ResponseReceived { request_nonce: nonce, + dest_chain: source_chain }); Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } From fbe592f94aab15e113fd7e4f58baa143e2fe104a Mon Sep 17 00:00:00 2001 From: Femi Bankole Date: Thu, 16 Mar 2023 14:40:41 +0100 Subject: [PATCH 098/182] CI for Testing (#16) --- .github/workflows/test.yml | 169 +++++++++++++++++++++++++++ prover/src/error.rs | 5 - prover/src/lib.rs | 1 - prover/src/test.rs | 18 +-- scripts/wait_for_tcp_port_opening.sh | 12 ++ 5 files changed, 186 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 prover/src/error.rs create mode 100755 scripts/wait_for_tcp_port_opening.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..1ad55a846 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,169 @@ +name: Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@main + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Run cargo check + run: cargo check + + test: + name: Test Suite + runs-on: ubuntu-latest + # defaults: + # run: + # shell: bash + env: + TUID: 123 + steps: + - name: set UID env + run: | + echo $UID + echo "TUID=$UID" >> $GITHUB_ENV + + - name: Checkout sources + uses: actions/checkout@master + + - name: Clone eth-testnet-runner repository + run: | + git clone https://github.com/ralexstokes/eth-testnet-runner.git + cd eth-testnet-runner + git checkout 5f43097a03f8ff37217c843407bf7729617f2dff + + - name: Install rust stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Install protoc + run: | + sudo apt update + sudo apt install protobuf-compiler + + - name: Fetch geth binary + run: | + cd eth-testnet-runner + mkdir bin + wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz -O ./geth-linux-amd64-1.11.3-5ed08c47.tar.gz + tar -xvf geth-linux-amd64-1.11.3-5ed08c47.tar.gz + cd geth-linux-amd64-1.11.3-5ed08c47 + cp geth ../bin + + - name: Build lighthouse from source + run: | + cd eth-testnet-runner + git clone https://github.com/sigp/lighthouse.git + cd lighthouse + git checkout 38514c07f222ff7783834c48cf5c0a6ee7f346d0 + cargo build -p lighthouse --release + mv target/release/lighthouse ../bin + + - name: Install go + uses: actions/setup-go@v3 + with: + go-version: "stable" + + - name: check go version + run: go version + + - name: Clone eth2-val-tools repository + run: | + git clone https://github.com/protolambda/eth2-val-tools.git + cd eth2-val-tools + git checkout 4bf01453537ad1a9323c7cd99afc4f8ba2a420b1 + + - name: build eth2-val-tools and move binary to eth-testnet-runner bin folder + run: | + cd eth2-val-tools + go build + cp eth2-val-tools ../eth-testnet-runner/bin + + # Make $UID available to the justfile + - name: set UID env variable as a local justfile variable + run: | + cd eth-testnet-runner + sed -i 's/$UID/{{UID}}/' justfile + sed -i 's/^NOW := `date +%s`/NOW := `date +%s`\nUID := "${{ env.TUID }}"/' justfile + + - name: install just + run: | + cargo install just + + - name: modify testnet config values + run: | + cd eth-testnet-runner/testnet-config + sed -i 's/GENESIS_FORK_VERSION=.*/GENESIS_FORK_VERSION="0x00000000"/' values.env + sed -i 's/ALTAIR_FORK_VERSION=.*/ALTAIR_FORK_VERSION="0x01000000"/' values.env + sed -i 's/BELLATRIX_FORK_VERSION=.*/BELLATRIX_FORK_VERSION="0x02000000"/' values.env + sed -i 's/CAPELLA_FORK_VERSION=.*/CAPELLA_FORK_VERSION="0x03000000"/' values.env + sed -i 's/EIP4844_FORK_VERSION=.*/EIP4844_FORK_VERSION="0x04000000"/' values.env + + - name: remove tty flag from docker command in create-config recipe + run: | + cd eth-testnet-runner + sed -i 's/-it/-i/' justfile + + - name: run create-config + run: | + cd eth-testnet-runner + just create-config & ../scripts/wait_for_tcp_port_opening.sh localhost 8000 + + - name: set shanghaiTime + run: | + cd eth-testnet-runner/config-data/custom_config_data + sed -i 's/"shanghaiTime":.*/"shanghaiTime": 167119236847838392/' genesis.json + + - name: Set MIN_GENESIS_TIME + run: | + cd eth-testnet-runner/config-data/custom_config_data/ + sed -i 's/^MIN_GENESIS_TIME:.*/MIN_GENESIS_TIME: 1678876062/' config.yaml + + - name: run generate-keys + run: | + cd eth-testnet-runner + just generate-keys + + - name: run init-geth + run: | + cd eth-testnet-runner + just init-geth + + - name: run run-el + run: | + cd eth-testnet-runner + just run-el & ../scripts/wait_for_tcp_port_opening.sh localhost 8545 + + - name: run run-cl + run: | + cd eth-testnet-runner + just run-cl & ../scripts/wait_for_tcp_port_opening.sh localhost 5052 + + - name: run run-validator + run: | + cd eth-testnet-runner + just run-validator & ../scripts/wait_for_tcp_port_opening.sh localhost 5062 + + - name: Run all tests + run: | + cargo test -p sync-committee-prover -- --nocapture diff --git a/prover/src/error.rs b/prover/src/error.rs deleted file mode 100644 index e6a6a5c49..000000000 --- a/prover/src/error.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug)] -pub enum Error { - AggregateSignatureError, - EmptySignedBeaconBlock, -} diff --git a/prover/src/lib.rs b/prover/src/lib.rs index b1ec1c001..863550ff7 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -1,6 +1,5 @@ #[warn(unused_imports)] #[warn(unused_variables)] -mod error; mod responses; mod routes; #[cfg(test)] diff --git a/prover/src/test.rs b/prover/src/test.rs index f234f468f..3618cb63e 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -6,13 +6,10 @@ use sync_committee_primitives::{ }; use ethereum_consensus::{ - altair::Checkpoint, bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, + bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, state_transition::Context, }; -use ssz_rs::{ - calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, - Merkleized, SszVariableOrIndex, -}; +use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; use std::time::Duration; use sync_committee_primitives::{ types::{AncestorBlock, FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, @@ -27,7 +24,6 @@ const NODE_URL: &'static str = "http://localhost:5052"; #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -#[ignore] async fn fetch_block_header_works() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let block_header = sync_committee_prover.fetch_header("head").await; @@ -37,7 +33,6 @@ async fn fetch_block_header_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -#[ignore] async fn fetch_block_works() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let block = sync_committee_prover.fetch_block("head").await; @@ -47,7 +42,6 @@ async fn fetch_block_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -#[ignore] async fn fetch_sync_committee_works() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let block = sync_committee_prover.fetch_sync_committee("head").await; @@ -57,7 +51,6 @@ async fn fetch_sync_committee_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -#[ignore] async fn fetch_validator_works() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let validator = sync_committee_prover.fetch_validator("head", "48").await; @@ -77,17 +70,15 @@ async fn fetch_processed_sync_committee_works() { #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -#[ignore] async fn fetch_beacon_state_works() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); - let beacon_state = sync_committee_prover.fetch_beacon_state("genesis").await; + let beacon_state = sync_committee_prover.fetch_beacon_state("head").await; assert!(beacon_state.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] #[actix_rt::test] -#[ignore] async fn state_root_and_block_header_root_matches() { let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); let mut beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); @@ -454,7 +445,8 @@ async fn test_prover() { ); count += 1; - if count == 10 { + // For CI purposes we test finalization of three epochs + if count == 3 { break } } diff --git a/scripts/wait_for_tcp_port_opening.sh b/scripts/wait_for_tcp_port_opening.sh new file mode 100755 index 000000000..d9e61a31e --- /dev/null +++ b/scripts/wait_for_tcp_port_opening.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +HOST=$1 +PORT=$2 + +echo "Trying to connect to ${HOST}:${PORT}..." + +while ! nc -z $HOST $PORT; do + sleep 0.5 +done + +echo "${HOST}:${PORT} is ready for requests." \ No newline at end of file From f3346d073e381cbd9d35d9650cde661737994b6f Mon Sep 17 00:00:00 2001 From: David Salami Date: Thu, 16 Mar 2023 16:09:17 +0100 Subject: [PATCH 099/182] update error --- src/router.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/router.rs b/src/router.rs index f070c6a6b..3bb4e6761 100644 --- a/src/router.rs +++ b/src/router.rs @@ -19,16 +19,12 @@ impl IISMPRouter for Router { let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = Pallet::::request_leaf_index_offchain_key(dest_chain, nonce); let leaf_index = mmr.push(Leaf::Request(request)).ok_or_else(|| { - Error::RequestVerificationFailed { - nonce, - source: source_chain, - dest: dest_chain, - } + Error::ImplementationSpecific("Failed to push request into mmr".to_string()) })?; // Deposit Event Pallet::::deposit_event(Event::RequestReceived { request_nonce: nonce, - dest_chain + dest_chain, }); // Store a map of request to leaf_index Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) @@ -49,15 +45,11 @@ impl IISMPRouter for Router { let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = Pallet::::response_leaf_index_offchain_key(source_chain, nonce); let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { - Error::ResponseVerificationFailed { - nonce, - source: source_chain, - dest: dest_chain, - } + Error::ImplementationSpecific("Failed to push response into mmr".to_string()) })?; Pallet::::deposit_event(Event::ResponseReceived { request_nonce: nonce, - dest_chain: source_chain + dest_chain: source_chain, }); Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } From 59447b948ef246f891dbd076eb6461fa386dbb9e Mon Sep 17 00:00:00 2001 From: David Salami Date: Fri, 17 Mar 2023 07:36:08 +0100 Subject: [PATCH 100/182] update licensing --- src/mmr/mmr.rs | 4 +--- src/mmr/mod.rs | 4 +--- src/mmr/storage.rs | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/mmr/mmr.rs b/src/mmr/mmr.rs index 768925063..86a823642 100644 --- a/src/mmr/mmr.rs +++ b/src/mmr/mmr.rs @@ -1,6 +1,4 @@ -// This file is part of Substrate. - -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Polytope Labs. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs index d9360c9f5..f4e9241f6 100644 --- a/src/mmr/mod.rs +++ b/src/mmr/mod.rs @@ -1,6 +1,4 @@ -// This file is part of Substrate. - -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Polytope Labs. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/mmr/storage.rs b/src/mmr/storage.rs index f089eaa56..fae2e4a5e 100644 --- a/src/mmr/storage.rs +++ b/src/mmr/storage.rs @@ -1,6 +1,4 @@ -// This file is part of Substrate. - -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Polytope Labs. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); From fffb395acbfaf0743bac23a5cb394f11c7cd6b2f Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Tue, 21 Mar 2023 11:24:23 +0100 Subject: [PATCH 101/182] Implement Scale Encode and Decode Traits for Light Client (#20) --- Cargo.lock | 142 +++++++--- primitives/Cargo.toml | 6 +- primitives/src/derived_types.rs | 488 ++++++++++++++++++++++++++++++++ primitives/src/error.rs | 24 ++ primitives/src/helpers.rs | 162 +++++++++++ primitives/src/lib.rs | 4 + 6 files changed, 785 insertions(+), 41 deletions(-) create mode 100644 primitives/src/derived_types.rs create mode 100644 primitives/src/error.rs create mode 100644 primitives/src/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index 3fee84a14..2a58f1bf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -50,15 +50,21 @@ source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee67 [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "as-any" @@ -85,7 +91,7 @@ checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -184,6 +190,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "byteorder" version = "1.4.3" @@ -726,6 +738,17 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -774,10 +797,11 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ + "hermit-abi 0.3.1", "libc", "windows-sys 0.45.0", ] @@ -790,9 +814,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", @@ -908,9 +932,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" @@ -964,7 +988,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] @@ -1040,9 +1064,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "d8b277f87dacc05a6b709965d1cbafac4649d6ce9f3ce9ceb88508b5666dfec9" dependencies = [ "bitflags", "cfg-if", @@ -1061,7 +1085,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1072,9 +1096,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" dependencies = [ "autocfg", "cc", @@ -1083,6 +1107,32 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parity-scale-codec" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1165,7 +1215,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1262,9 +1312,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" dependencies = [ "base64 0.21.0", "bytes", @@ -1326,9 +1376,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.36.9" +version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ "bitflags", "errno", @@ -1398,22 +1448,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.156" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.156" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.4", ] [[package]] @@ -1562,7 +1612,7 @@ source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1588,6 +1638,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync-committee-primitives" version = "0.1.0" @@ -1595,6 +1656,7 @@ dependencies = [ "base2 0.3.1", "ethereum-consensus", "hex-literal", + "parity-scale-codec", "ssz-rs", ] @@ -1639,7 +1701,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "unicode-xid", ] @@ -1673,22 +1735,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.4", ] [[package]] @@ -1734,7 +1796,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1821,9 +1883,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -1912,7 +1974,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1946,7 +2008,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index a6db5811b..cef917f0a 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -10,10 +10,14 @@ base2 = { version = "0.3.1", default-features = false} ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ + "derive" +] } [features] default = ["std"] std = [ - "ssz-rs/std" + "ssz-rs/std", + 'codec/std' ] testing = [] diff --git a/primitives/src/derived_types.rs b/primitives/src/derived_types.rs new file mode 100644 index 000000000..3610e22d2 --- /dev/null +++ b/primitives/src/derived_types.rs @@ -0,0 +1,488 @@ +use crate::{ + error::Error, + helpers::{ + to_codec_light_client_state, to_codec_light_client_update, to_no_codec_beacon_header, + to_no_codec_light_client_state, to_no_codec_light_client_update, + to_no_codec_sync_committee, + }, + types, +}; +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use ethereum_consensus::{bellatrix, primitives::Hash32}; + +/// Minimum state required by the light client to validate new sync committee attestations +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] +pub struct LightClientState { + /// The latest recorded finalized header + pub finalized_header: BeaconBlockHeader, + /// Latest finalized epoch + pub latest_finalized_epoch: u64, + // Sync committees corresponding to the finalized header + pub current_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, +} + +impl TryFrom> + for LightClientState +{ + type Error = Error; + fn try_from(state: types::LightClientState) -> Result { + to_codec_light_client_state(state) + } +} + +impl TryFrom + for types::LightClientState +{ + type Error = Error; + fn try_from(state: LightClientState) -> Result { + to_no_codec_light_client_state(state) + } +} + +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] +pub struct BeaconBlockHeader { + pub slot: u64, + pub proposer_index: u64, + pub parent_root: [u8; 32], + pub state_root: [u8; 32], + pub body_root: [u8; 32], +} + +impl TryFrom for BeaconBlockHeader { + type Error = Error; + + fn try_from(beacon_block_header: bellatrix::BeaconBlockHeader) -> Result { + Ok(BeaconBlockHeader { + slot: beacon_block_header.slot, + proposer_index: beacon_block_header.proposer_index as u64, + parent_root: beacon_block_header + .parent_root + .as_bytes() + .try_into() + .map_err(|_| Error::InvalidNodeBytes)?, + state_root: beacon_block_header + .state_root + .as_bytes() + .try_into() + .map_err(|_| Error::InvalidNodeBytes)?, + body_root: beacon_block_header + .body_root + .as_bytes() + .try_into() + .map_err(|_| Error::InvalidNodeBytes)?, + }) + } +} + +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] +pub struct SyncCommittee { + pub public_keys: Vec>, + pub aggregate_public_key: Vec, +} + +impl TryFrom> + for SyncCommittee +{ + type Error = Error; + + fn try_from( + sync_committee: bellatrix::SyncCommittee, + ) -> Result { + Ok(SyncCommittee { + public_keys: sync_committee + .public_keys + .iter() + .map(|public_key| public_key.to_vec()) + .collect(), + aggregate_public_key: sync_committee.aggregate_public_key.to_vec(), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] +pub struct LightClientUpdate { + /// the header that the sync committee signed + pub attested_header: BeaconBlockHeader, + /// the sync committee has potentially changed, here's an ssz proof for that. + pub sync_committee_update: Option, + /// the actual header which was finalized by the ethereum attestation protocol. + pub finalized_header: BeaconBlockHeader, + /// execution payload of the finalized header + pub execution_payload: ExecutionPayloadProof, + /// Finalized header proof + pub finality_proof: FinalityProof, + /// signature & participation bits + pub sync_aggregate: SyncAggregate, + /// slot at which signature was produced + pub signature_slot: u64, + /// ancestors of the finalized block to be verified, may be empty. + pub ancestor_blocks: Vec, +} + +impl TryFrom> + for LightClientUpdate +{ + type Error = Error; + fn try_from( + update: types::LightClientUpdate, + ) -> Result { + to_codec_light_client_update(update) + } +} + +impl TryFrom + for types::LightClientUpdate +{ + type Error = Error; + fn try_from(derived_update: LightClientUpdate) -> Result { + to_no_codec_light_client_update(derived_update) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] +pub struct SyncCommitteeUpdate { + // actual sync committee + pub next_sync_committee: SyncCommittee, + // sync committee, ssz merkle proof. + pub next_sync_committee_branch: Vec>, +} + +impl TryFrom> + for SyncCommitteeUpdate +{ + type Error = Error; + + fn try_from( + sync_committee_update: types::SyncCommitteeUpdate, + ) -> Result { + Ok(SyncCommitteeUpdate { + next_sync_committee: sync_committee_update.next_sync_committee.try_into()?, + next_sync_committee_branch: sync_committee_update + .next_sync_committee_branch + .iter() + .map(|hash| hash.to_vec()) + .collect(), + }) + } +} + +impl TryFrom + for types::SyncCommitteeUpdate +{ + type Error = Error; + + fn try_from(sync_committee_update: SyncCommitteeUpdate) -> Result { + let next_sync_committee = + to_no_codec_sync_committee(sync_committee_update.next_sync_committee)?; + Ok(types::SyncCommitteeUpdate { + next_sync_committee, + next_sync_committee_branch: sync_committee_update + .next_sync_committee_branch + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, Error>>()?, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] +pub struct ExecutionPayloadProof { + /// The state root in the `ExecutionPayload` which represents the commitment to + /// the ethereum world state in the yellow paper. + pub state_root: Vec, + /// the block number of the execution header. + pub block_number: u64, + /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. + pub multi_proof: Vec>, + /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. + pub execution_payload_branch: Vec>, + /// timestamp + pub timestamp: u64, +} + +impl TryFrom for ExecutionPayloadProof { + type Error = Error; + fn try_from( + execution_payload_proof: types::ExecutionPayloadProof, + ) -> Result { + Ok(ExecutionPayloadProof { + state_root: execution_payload_proof.state_root.to_vec(), + block_number: execution_payload_proof.block_number, + multi_proof: execution_payload_proof + .multi_proof + .iter() + .map(|proof| proof.to_vec()) + .collect(), + execution_payload_branch: execution_payload_proof + .execution_payload_branch + .iter() + .map(|branch| branch.to_vec()) + .collect(), + timestamp: execution_payload_proof.timestamp, + }) + } +} + +impl TryFrom for types::ExecutionPayloadProof { + type Error = Error; + fn try_from( + derived_execution_payload_proof: ExecutionPayloadProof, + ) -> Result { + let multi_proof = derived_execution_payload_proof + .multi_proof + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?; + + let execution_payload_branch = derived_execution_payload_proof + .execution_payload_branch + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?; + + Ok(types::ExecutionPayloadProof { + state_root: Hash32::try_from(derived_execution_payload_proof.state_root.as_slice()) + .map_err(|_| Error::InvalidRoot)?, + block_number: derived_execution_payload_proof.block_number, + multi_proof, + execution_payload_branch, + timestamp: derived_execution_payload_proof.timestamp, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] +pub struct FinalityProof { + /// The latest finalized epoch + pub epoch: u64, + /// Finalized header proof + pub finality_branch: Vec>, +} + +impl TryFrom for FinalityProof { + type Error = Error; + fn try_from(finality_proof: types::FinalityProof) -> Result { + Ok(FinalityProof { + epoch: finality_proof.epoch, + finality_branch: finality_proof + .finality_branch + .iter() + .map(|branch| branch.to_vec()) + .collect(), + }) + } +} + +impl TryFrom for types::FinalityProof { + type Error = Error; + fn try_from(derived_finality_proof: FinalityProof) -> Result { + Ok(types::FinalityProof { + epoch: derived_finality_proof.epoch, + finality_branch: derived_finality_proof + .finality_branch + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] +pub struct SyncAggregate { + pub sync_committee_bits: Vec, + pub sync_committee_signature: Vec, +} + +impl TryFrom> + for SyncAggregate +{ + type Error = Error; + fn try_from( + sync_aggregate: bellatrix::SyncAggregate, + ) -> Result { + Ok(SyncAggregate { + sync_committee_bits: sync_aggregate.sync_committee_bits.clone().to_bitvec().into_vec(), + sync_committee_signature: sync_aggregate.sync_committee_signature.clone().to_vec(), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct AncestorBlock { + /// The actual beacon chain header + pub header: BeaconBlockHeader, + /// Associated execution header proofs + pub execution_payload: ExecutionPayloadProof, + /// Ancestry proofs of the beacon chain header. + pub ancestry_proof: AncestryProof, +} + +impl TryFrom for AncestorBlock { + type Error = Error; + fn try_from(ancestor_block: types::AncestorBlock) -> Result { + Ok(AncestorBlock { + header: ancestor_block.header.try_into()?, + execution_payload: ancestor_block.execution_payload.try_into()?, + ancestry_proof: ancestor_block.ancestry_proof.try_into()?, + }) + } +} + +impl TryFrom for types::AncestorBlock { + type Error = Error; + fn try_from(derived_ancestor_block: AncestorBlock) -> Result { + let beacon_block_header = to_no_codec_beacon_header(derived_ancestor_block.header)?; + Ok(types::AncestorBlock { + header: beacon_block_header, + execution_payload: derived_ancestor_block.execution_payload.try_into()?, + ancestry_proof: derived_ancestor_block.ancestry_proof.try_into()?, + }) + } +} + +/// Holds the neccessary proofs required to verify a header in the `block_roots` field +/// either in [`BeaconState`] or [`HistoricalBatch`]. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct BlockRootsProof { + /// Generalized index of the header in the `block_roots` list. + pub block_header_index: u64, + /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` + pub block_header_branch: Vec>, +} + +impl TryFrom for BlockRootsProof { + type Error = Error; + fn try_from(beacon_block_header: types::BlockRootsProof) -> Result { + Ok(BlockRootsProof { + block_header_index: beacon_block_header.block_header_index, + block_header_branch: beacon_block_header + .block_header_branch + .iter() + .map(|hash| hash.to_vec()) + .collect(), + }) + } +} + +impl TryFrom for types::BlockRootsProof { + type Error = Error; + fn try_from(derived_beacon_block_header: BlockRootsProof) -> Result { + let branch = derived_beacon_block_header + .block_header_branch + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?; + + Ok(types::BlockRootsProof { + block_header_index: derived_beacon_block_header.block_header_index, + block_header_branch: branch, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub enum AncestryProof { + /// This variant defines the proof data for a beacon chain header in the `state.block_roots` + BlockRoots { + /// Proof for the header in `state.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] + block_roots_branch: Vec>, + }, + /// This variant defines the neccessary proofs for a beacon chain header in the + /// `state.historical_roots`. + HistoricalRoots { + /// Proof for the header in `historical_batch.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the `historical_batch.block_roots`, needed to reconstruct + /// `hash_tree_root(historical_batch)` + historical_batch_proof: Vec>, + /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` + historical_roots_proof: Vec>, + /// The generalized index for the historical_batch in `state.historical_roots`. + historical_roots_index: u64, + /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in + /// [`BeaconState`] + historical_roots_branch: Vec>, + }, +} + +impl TryFrom for AncestryProof { + type Error = Error; + fn try_from(ancestry_proof: types::AncestryProof) -> Result { + Ok(match ancestry_proof { + types::AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => + AncestryProof::BlockRoots { + block_roots_proof: block_roots_proof.try_into()?, + block_roots_branch: block_roots_branch + .iter() + .map(|hash| hash.to_vec()) + .collect(), + }, + types::AncestryProof::HistoricalRoots { + block_roots_proof, + historical_batch_proof, + historical_roots_proof, + historical_roots_index, + historical_roots_branch, + } => AncestryProof::HistoricalRoots { + block_roots_proof: block_roots_proof.try_into()?, + historical_batch_proof: historical_batch_proof + .iter() + .map(|hash| hash.to_vec()) + .collect(), + historical_roots_proof: historical_roots_proof + .iter() + .map(|hash| hash.to_vec()) + .collect(), + historical_roots_index, + historical_roots_branch: historical_roots_branch + .iter() + .map(|hash| hash.to_vec()) + .collect(), + }, + }) + } +} + +impl TryFrom for types::AncestryProof { + type Error = Error; + fn try_from(ancestry_proof: AncestryProof) -> Result { + Ok(match ancestry_proof { + AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => + types::AncestryProof::BlockRoots { + block_roots_proof: block_roots_proof.try_into()?, + block_roots_branch: block_roots_branch + .iter() + .map(|proof| { + Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof) + }) + .collect::, _>>()?, + }, + AncestryProof::HistoricalRoots { + block_roots_proof, + historical_batch_proof, + historical_roots_proof, + historical_roots_index, + historical_roots_branch, + } => types::AncestryProof::HistoricalRoots { + block_roots_proof: block_roots_proof.try_into()?, + historical_batch_proof: historical_batch_proof + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?, + historical_roots_proof: historical_roots_proof + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?, + historical_roots_index, + historical_roots_branch: historical_roots_branch + .iter() + .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) + .collect::, _>>()?, + }, + }) + } +} diff --git a/primitives/src/error.rs b/primitives/src/error.rs new file mode 100644 index 000000000..0188f669d --- /dev/null +++ b/primitives/src/error.rs @@ -0,0 +1,24 @@ +use core::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum Error { + InvalidRoot, + InvalidPublicKey, + InvalidProof, + InvalidBitVec, + ErrorConvertingAncestorBlock, + InvalidNodeBytes, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Error::InvalidRoot => write!(f, "Invalid root",), + Error::InvalidPublicKey => write!(f, "Invalid public key",), + Error::InvalidProof => write!(f, "Invalid proof",), + Error::InvalidBitVec => write!(f, "Invalid bit vec",), + Error::InvalidNodeBytes => write!(f, "Invalid node bytes",), + Error::ErrorConvertingAncestorBlock => write!(f, "Error deriving ancestor block",), + } + } +} diff --git a/primitives/src/helpers.rs b/primitives/src/helpers.rs new file mode 100644 index 000000000..b16e8b9ec --- /dev/null +++ b/primitives/src/helpers.rs @@ -0,0 +1,162 @@ +use crate::{ + derived_types, + error::Error, + types, + types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, +}; +use alloc::vec::Vec; +use ethereum_consensus::{ + bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, + crypto::PublicKey, + primitives::BlsSignature, +}; +use ssz_rs::{Bitvector, Deserialize, Node, Vector}; + +pub fn to_no_codec_beacon_header( + derived_header: derived_types::BeaconBlockHeader, +) -> Result { + let finalized_header = BeaconBlockHeader { + slot: derived_header.slot, + proposer_index: derived_header.proposer_index as usize, + parent_root: Node::from_bytes( + derived_header.parent_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, + ), + state_root: Node::from_bytes( + derived_header.state_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, + ), + body_root: Node::from_bytes( + derived_header.body_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, + ), + }; + + Ok(finalized_header) +} + +pub fn to_no_codec_sync_committee( + derived_sync_committee: derived_types::SyncCommittee, +) -> Result, Error> { + let public_keys_vector: Vec = derived_sync_committee + .public_keys + .iter() + .map(|public_key| { + PublicKey::try_from(public_key.as_slice()).map_err(|_| Error::InvalidPublicKey) + }) + .collect::, Error>>()?; + let sync_committee = SyncCommittee { + public_keys: Vector::try_from(public_keys_vector).unwrap(), + aggregate_public_key: PublicKey::try_from( + derived_sync_committee.aggregate_public_key.as_slice(), + ) + .map_err(|_| Error::InvalidPublicKey)?, + }; + + Ok(sync_committee) +} + +pub fn to_no_codec_sync_aggregate( + derived_sync_aggregate: derived_types::SyncAggregate, +) -> Result, Error> { + let derived_sync_committee_bits = derived_sync_aggregate.sync_committee_bits; + let bit_vector = Bitvector::::deserialize(&derived_sync_committee_bits) + .map_err(|_| Error::InvalidBitVec)?; + + let sync_aggregate = SyncAggregate { + sync_committee_bits: bit_vector, + sync_committee_signature: BlsSignature::try_from( + derived_sync_aggregate.sync_committee_signature.as_ref(), + ) + .map_err(|_| Error::InvalidPublicKey)?, + }; + + Ok(sync_aggregate) +} + +pub fn to_no_codec_light_client_state( + state: derived_types::LightClientState, +) -> Result, Error> { + let finalized_header = to_no_codec_beacon_header(state.finalized_header)?; + + let current_sync_committee = to_no_codec_sync_committee(state.current_sync_committee.clone())?; + let next_sync_committee = to_no_codec_sync_committee(state.next_sync_committee)?; + + Ok(LightClientState { + finalized_header, + latest_finalized_epoch: state.latest_finalized_epoch, + current_sync_committee, + next_sync_committee, + }) +} + +pub fn to_no_codec_light_client_update( + derived_update: derived_types::LightClientUpdate, +) -> Result, Error> { + let sync_committee_update_option: Option>; + + match derived_update.sync_committee_update { + Some(sync_committee_update) => + sync_committee_update_option = Some(sync_committee_update.try_into()?), + None => sync_committee_update_option = None, + } + Ok(LightClientUpdate { + attested_header: to_no_codec_beacon_header(derived_update.attested_header)?, + sync_committee_update: sync_committee_update_option, + finalized_header: to_no_codec_beacon_header(derived_update.finalized_header)?, + execution_payload: derived_update.execution_payload.try_into()?, + finality_proof: derived_update.finality_proof.try_into()?, + sync_aggregate: to_no_codec_sync_aggregate(derived_update.sync_aggregate)?, + signature_slot: derived_update.signature_slot, + ancestor_blocks: derived_update + .ancestor_blocks + .iter() + .map(|ancestor_block| { + ancestor_block + .clone() + .try_into() + .map_err(|_| Error::ErrorConvertingAncestorBlock) + }) + .collect::, Error>>()?, + }) +} + +pub fn to_codec_light_client_state( + state: types::LightClientState, +) -> Result { + Ok(derived_types::LightClientState { + finalized_header: state.finalized_header.try_into()?, + latest_finalized_epoch: state.latest_finalized_epoch, + current_sync_committee: state.current_sync_committee.try_into()?, + next_sync_committee: state.next_sync_committee.try_into()?, + }) +} + +pub fn to_codec_light_client_update( + update: types::LightClientUpdate, +) -> Result { + let sync_committee_update_option: Option; + + match update.sync_committee_update { + Some(sync_committee_update) => + sync_committee_update_option = Some(sync_committee_update.try_into()?), + + None => sync_committee_update_option = None, + } + Ok(derived_types::LightClientUpdate { + attested_header: update.attested_header.try_into()?, + sync_committee_update: sync_committee_update_option, + finalized_header: update.finalized_header.try_into()?, + execution_payload: update.execution_payload.try_into()?, + finality_proof: update.finality_proof.try_into()?, + sync_aggregate: update.sync_aggregate.try_into()?, + signature_slot: update.signature_slot, + ancestor_blocks: update + .ancestor_blocks + .iter() + .map(|ancestor_block| { + ancestor_block + .clone() + .try_into() + .map_err(|_| Error::ErrorConvertingAncestorBlock) + }) + .collect::, Error>>()?, + }) +} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 23d346f32..620e20b9b 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -2,6 +2,10 @@ #[warn(unused_imports)] #[warn(unused_variables)] extern crate alloc; +extern crate core; +pub mod derived_types; +pub mod error; +pub mod helpers; pub mod types; pub mod util; From 3b1a1ba9f2c95b4304815180f6d6578b2309fc21 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 21 Mar 2023 11:29:05 +0100 Subject: [PATCH 102/182] ISMP Host (#5) --- src/host.rs | 93 ++++++++++++++++++++++++++++++++--------------- src/lib.rs | 87 +++++++++++++++++++++++++++++++++++++++----- src/mmr/mod.rs | 4 +- src/mmr/utils.rs | 1 + src/primitives.rs | 1 + src/router.rs | 62 ++++++++++++++++++++++++++++--- 6 files changed, 202 insertions(+), 46 deletions(-) diff --git a/src/host.rs b/src/host.rs index 5150024dd..7cc1fa60c 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,11 +1,21 @@ -use crate::Config; +use crate::router::Router; +use crate::{ + Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, + RequestAcks, StateCommitments, StateMachineUpdateTime, +}; +use alloc::format; +use alloc::string::ToString; +use core::time::Duration; +use frame_support::traits::UnixTime; use ismp_rust::consensus_client::{ ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }; use ismp_rust::error::Error; use ismp_rust::host::{ChainID, ISMPHost}; -use ismp_rust::router::{IISMPRouter, Request, Response}; -use std::time::Duration; +use ismp_rust::paths::RequestPath; +use ismp_rust::router::{IISMPRouter, Request}; +use sp_runtime::SaturatedConversion; +use sp_std::prelude::*; #[derive(Clone)] pub struct Host(core::marker::PhantomData); @@ -22,46 +32,71 @@ impl ISMPHost for Host { } fn latest_commitment_height(&self, id: StateMachineId) -> Result { - todo!() + LatestStateMachineHeight::::get(id) + .map(|height| StateMachineHeight { id, height }) + .ok_or_else(|| { + Error::ImplementationSpecific("Missing latest state machine height".to_string()) + }) } fn state_machine_commitment( &self, height: StateMachineHeight, ) -> Result { - todo!() + StateCommitments::::get(height).ok_or_else(|| Error::StateCommitmentNotFound { height }) } fn consensus_update_time(&self, id: ConsensusClientId) -> Result { - todo!() + ConsensusClientUpdateTime::::get(id) + .map(|timestamp| Duration::from_nanos(timestamp)) + .ok_or_else(|| { + Error::ImplementationSpecific(format!("Update time not found for {:?}", id)) + }) } fn state_machine_update_time(&self, height: StateMachineHeight) -> Result { - todo!() + StateMachineUpdateTime::::get(height) + .map(|timestamp| Duration::from_nanos(timestamp)) + .ok_or_else(|| { + Error::ImplementationSpecific(format!("Update time not found for {:?}", height)) + }) } fn consensus_state(&self, id: ConsensusClientId) -> Result, Error> { - todo!() + ConsensusStates::::get(id).ok_or_else(|| Error::ConsensusStateNotFound { id }) } fn host_timestamp(&self) -> Duration { - todo!() + ::now() } fn is_frozen(&self, height: StateMachineHeight) -> Result { - todo!() + if let Some(frozen_height) = FrozenHeights::::get(height.id) { + Ok(height.height >= frozen_height) + } else { + Ok(false) + } } fn request_commitment(&self, req: &Request) -> Result, Error> { - todo!() - } - - fn response_commitment(&self, res: &Response) -> Result, Error> { - todo!() + let key = RequestPath { + dest_chain: req.dest_chain, + source_chain: req.source_chain, + nonce: req.nonce, + } + .to_string() + .as_bytes() + .to_vec(); + RequestAcks::::get(key).ok_or_else(|| Error::RequestCommitmentNotFound { + nonce: req.nonce, + source: req.source_chain, + dest: req.dest_chain, + }) } fn store_consensus_state(&self, id: ConsensusClientId, state: Vec) -> Result<(), Error> { - todo!() + ConsensusStates::::insert(id, state); + Ok(()) } fn store_consensus_update_time( @@ -69,7 +104,8 @@ impl ISMPHost for Host { id: ConsensusClientId, timestamp: Duration, ) -> Result<(), Error> { - todo!() + ConsensusClientUpdateTime::::insert(id, timestamp.as_nanos().saturated_into::()); + Ok(()) } fn store_state_machine_update_time( @@ -77,7 +113,8 @@ impl ISMPHost for Host { height: StateMachineHeight, timestamp: Duration, ) -> Result<(), Error> { - todo!() + StateMachineUpdateTime::::insert(height, timestamp.as_nanos().saturated_into::()); + Ok(()) } fn store_state_machine_commitment( @@ -85,30 +122,28 @@ impl ISMPHost for Host { height: StateMachineHeight, state: StateCommitment, ) -> Result<(), Error> { - todo!() + StateCommitments::::insert(height, state); + Ok(()) } fn freeze_state_machine(&self, height: StateMachineHeight) -> Result<(), Error> { - todo!() + FrozenHeights::::insert(height.id, height.height); + Ok(()) } - fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { + fn consensus_client(&self, _id: ConsensusClientId) -> Result, Error> { todo!() } fn keccak256(&self, bytes: &[u8]) -> [u8; 32] { - todo!() + sp_io::hashing::keccak_256(bytes) } - fn delay_period(&self, id: StateMachineId) -> Duration { - todo!() - } - - fn client_id_from_state_id(&self, id: StateMachineId) -> Result { - todo!() + fn delay_period(&self, _id: StateMachineId) -> Duration { + Duration::from_secs(5 * 60) } fn ismp_router(&self) -> Box { - todo!() + Box::new(Router::::default()) } } diff --git a/src/lib.rs b/src/lib.rs index 0e134142e..690548fef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,8 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod host; mod mmr; mod primitives; @@ -29,6 +31,7 @@ use sp_core::offchain::StorageKind; // Re-export pallet items so that they can be accessed from the crate namespace. use crate::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}; pub use pallet::*; +use sp_std::prelude::*; // Definition of the pallet logic, to be aggregated at runtime definition through // `construct_runtime`. @@ -39,8 +42,11 @@ pub mod pallet { use crate::mmr::{LeafIndex, Mmr, NodeIndex}; use crate::primitives::ISMP_ID; use frame_support::pallet_prelude::*; + use frame_support::traits::UnixTime; use frame_system::pallet_prelude::*; - use ismp_rust::consensus_client::ConsensusClientId; + use ismp_rust::consensus_client::{ + ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, + }; use ismp_rust::host::ChainID; use sp_runtime::traits; @@ -92,15 +98,17 @@ pub mod pallet { + codec::EncodeLike + scale_info::TypeInfo + MaxEncodedLen; + type TimeProvider: UnixTime; } // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and // method. #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::without_storage_info] pub struct Pallet(_); - /// Latest MMR Root hash for requests + /// Latest MMR Root hash #[pallet::storage] #[pallet::getter(fn mmr_root_hash)] pub type RootHash = StorageValue<_, ::Hash, ValueQuery>; @@ -119,6 +127,48 @@ pub mod pallet { pub type Nodes = StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn state_commitments)] + pub type StateCommitments = + StorageMap<_, Blake2_128Concat, StateMachineHeight, StateCommitment, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn consensus_states)] + pub type ConsensusStates = + StorageMap<_, Twox64Concat, ConsensusClientId, Vec, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn frozen_heights)] + pub type FrozenHeights = + StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn latest_state_height)] + pub type LatestStateMachineHeight = + StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn state_update_time)] + pub type StateMachineUpdateTime = + StorageMap<_, Blake2_128Concat, StateMachineHeight, u64, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn consensus_update_time)] + pub type ConsensusClientUpdateTime = + StorageMap<_, Twox64Concat, ConsensusClientId, u64, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn request_acks)] + /// Acknowledgements for receipt of requests + /// No hashing, just insert raw key in storage + pub type RequestAcks = StorageMap<_, Identity, Vec, Vec, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn response_acks)] + /// Acknowledgements for receipt of responses + /// No hashing, just insert raw key in storage + pub type ResponseAcks = StorageMap<_, Identity, Vec, Vec, OptionQuery>; + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet { @@ -172,14 +222,18 @@ pub mod pallet { height: u64, }, ResponseReceived { - /// Chain that this reponse will be routed to + /// Chain that this response will be routed to dest_chain: ChainID, + /// Source Chain for this response + source_chain: ChainID, /// Nonce for the request which this response is for request_nonce: u64, }, RequestReceived { - /// Chain that this reponse will be routed to + /// Chain that this request will be routed to dest_chain: ChainID, + /// Source Chain for request + source_chain: ChainID, /// Request nonce request_nonce: u64, }, @@ -238,20 +292,30 @@ pub struct RequestResponseLog { } impl Pallet { - pub fn request_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { + pub fn request_leaf_index_offchain_key( + source_chain: ChainID, + dest_chain: ChainID, + nonce: u64, + ) -> Vec { ( T::INDEXING_PREFIX, "Requests/leaf_indices", + source_chain, dest_chain, nonce, ) .encode() } - pub fn response_leaf_index_offchain_key(dest_chain: ChainID, nonce: u64) -> Vec { + pub fn response_leaf_index_offchain_key( + source_chain: ChainID, + dest_chain: ChainID, + nonce: u64, + ) -> Vec { ( T::INDEXING_PREFIX, "Responses/leaf_indices", + source_chain, dest_chain, nonce, ) @@ -292,11 +356,16 @@ impl Pallet { None } - pub fn get_leaf_index(dest_chain: ChainID, nonce: u64, is_req: bool) -> Option { + pub fn get_leaf_index( + source_chain: ChainID, + dest_chain: ChainID, + nonce: u64, + is_req: bool, + ) -> Option { let key = if is_req { - Self::request_leaf_index_offchain_key(dest_chain, nonce) + Self::request_leaf_index_offchain_key(source_chain, dest_chain, nonce) } else { - Self::response_leaf_index_offchain_key(dest_chain, nonce) + Self::response_leaf_index_offchain_key(source_chain, dest_chain, nonce) }; if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { return LeafIndex::decode(&mut &*elem).ok(); diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs index f4e9241f6..1dbd9fb54 100644 --- a/src/mmr/mod.rs +++ b/src/mmr/mod.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. use core::fmt; -use std::fmt::Formatter; +use core::fmt::Formatter; pub mod mmr; pub mod storage; @@ -72,7 +72,7 @@ pub enum DataOrHash { } impl core::fmt::Debug for DataOrHash { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { DataOrHash::Data(leaf) => f.debug_struct("DataOrHash").field("Data", leaf).finish(), DataOrHash::Hash(hash) => f.debug_struct("DataOrHash").field("Hash", hash).finish(), diff --git a/src/mmr/utils.rs b/src/mmr/utils.rs index 493c8fe48..1d3c209c7 100644 --- a/src/mmr/utils.rs +++ b/src/mmr/utils.rs @@ -1,4 +1,5 @@ use crate::mmr::{LeafIndex, NodeIndex}; +use alloc::vec::Vec; use mmr_lib::helper; /// MMR nodes & size -related utilities. diff --git a/src/primitives.rs b/src/primitives.rs index 0109f0f89..4bd040482 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,6 +1,7 @@ use crate::mmr::{LeafIndex, NodeIndex}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; +use sp_std::prelude::*; /// The `ConsensusEngineId` of ISMP. pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; diff --git a/src/router.rs b/src/router.rs index 3bb4e6761..d877c7f14 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,59 +1,109 @@ use crate::host::Host; use crate::mmr::{self, Leaf, Mmr}; -use crate::{Config, Event, Pallet}; +use crate::{Config, Event, Pallet, RequestAcks, ResponseAcks}; +use alloc::format; +use alloc::string::ToString; use core::marker::PhantomData; use ismp_rust::error::Error; use ismp_rust::host::ISMPHost; +use ismp_rust::paths::{RequestPath, ResponsePath}; use ismp_rust::router::{IISMPRouter, Request, Response}; -#[derive(Default, Clone)] +#[derive(Clone)] pub struct Router(PhantomData); +impl Default for Router { + fn default() -> Self { + Self(PhantomData) + } +} + impl IISMPRouter for Router { fn dispatch(&self, request: Request) -> Result<(), Error> { let host = Host::::default(); + let key = RequestPath { + dest_chain: request.dest_chain, + source_chain: request.source_chain, + nonce: request.nonce, + } + .to_string() + .as_bytes() + .to_vec(); + let commitment = host.get_request_commitment(&request); + + if RequestAcks::::contains_key(key.clone()) { + return Err(Error::ImplementationSpecific(format!( + "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", + request.nonce, request.source_chain, request.dest_chain + ))); + } + if host.host() != request.dest_chain { let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = (request.dest_chain, request.source_chain, request.nonce); let mut mmr: Mmr = mmr::Mmr::new(leaves); - let offchain_key = Pallet::::request_leaf_index_offchain_key(dest_chain, nonce); + let offchain_key = + Pallet::::request_leaf_index_offchain_key(source_chain, dest_chain, nonce); let leaf_index = mmr.push(Leaf::Request(request)).ok_or_else(|| { Error::ImplementationSpecific("Failed to push request into mmr".to_string()) })?; // Deposit Event Pallet::::deposit_event(Event::RequestReceived { request_nonce: nonce, + source_chain, dest_chain, }); // Store a map of request to leaf_index Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } + RequestAcks::::insert(key, commitment); Ok(()) } fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); + let key = ResponsePath { + dest_chain: response.request.source_chain, + source_chain: response.request.dest_chain, + nonce: response.request.nonce, + } + .to_string() + .as_bytes() + .to_vec(); + let commitment = host.get_response_commitment(&response); + + if ResponseAcks::::contains_key(key.clone()) { + return Err(Error::ImplementationSpecific(format!( + "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", + response.request.nonce, response.request.source_chain, response.request.dest_chain + ))); + } + if host.host() != response.request.source_chain { let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = ( - response.request.dest_chain, response.request.source_chain, + response.request.dest_chain, response.request.nonce, ); let mut mmr: Mmr = mmr::Mmr::new(leaves); - let offchain_key = Pallet::::response_leaf_index_offchain_key(source_chain, nonce); + let offchain_key = + Pallet::::response_leaf_index_offchain_key(source_chain, dest_chain, nonce); let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { Error::ImplementationSpecific("Failed to push response into mmr".to_string()) })?; Pallet::::deposit_event(Event::ResponseReceived { request_nonce: nonce, - dest_chain: source_chain, + dest_chain, + source_chain, }); Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } + ResponseAcks::::insert(key, commitment); + Ok(()) } } From ca050a45718f623d553c591bb292c0ca8b0ae494 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:54:14 +0100 Subject: [PATCH 103/182] RPC and runtime API (#11) * initialize rpc crate * defined important runtime apis * build erros * implement a couple rpc methods * add events rpc api --- Cargo.toml | 13 ++- rpc/Cargo.toml | 26 +++++ rpc/src/lib.rs | 243 +++++++++++++++++++++++++++++++++++++++++ runtime-api/Cargo.toml | 25 +++++ runtime-api/src/lib.rs | 56 ++++++++++ src/events.rs | 64 +++++++++++ src/host.rs | 27 ++--- src/lib.rs | 23 ++-- src/primitives.rs | 16 --- src/router.rs | 4 +- 10 files changed, 445 insertions(+), 52 deletions(-) create mode 100644 rpc/Cargo.toml create mode 100644 rpc/src/lib.rs create mode 100644 runtime-api/Cargo.toml create mode 100644 runtime-api/src/lib.rs create mode 100644 src/events.rs diff --git a/Cargo.toml b/Cargo.toml index 093b5f245..e1d119617 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } - +serde = { version = "1.0.136", features = ["derive"], optional = true } [features] default = ["std"] std = [ @@ -33,7 +33,16 @@ std = [ "sp-std/std", "ismp-rust/std", "mmr-lib/std", - "sp-api/std" + "sp-api/std", + "serde" ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] + +[workspace] +resolver = "2" + +members = [ + "rpc", + "runtime-api" +] \ No newline at end of file diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml new file mode 100644 index 000000000..3dc350e5a --- /dev/null +++ b/rpc/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "ismp-rpc" +description = "RPC apis for pallet-ismp" +edition = "2021" +version = "0.1.0" +authors = ["Polytope labs"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +hex-literal = { version = "0.3.3" } +jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.45" +ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main" } +pallet-ismp = { path = ".." } +ismp-runtime-api = { path = "../runtime-api" } + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } \ No newline at end of file diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs new file mode 100644 index 000000000..90eb90814 --- /dev/null +++ b/rpc/src/lib.rs @@ -0,0 +1,243 @@ +#![warn(missing_docs)] + +//! ISMP RPC Implementation. + +use jsonrpsee::{ + core::{Error as RpcError, RpcResult as Result}, + proc_macros::rpc, + types::{error::CallError, ErrorObject}, +}; + +use codec::Encode; +use ismp_runtime_api::{IsmpRuntimeApi, LeafIndexQuery}; +use ismp_rust::consensus_client::ConsensusClientId; +use ismp_rust::router::{Request, Response}; +use pallet_ismp::mmr::{Leaf, LeafIndex}; +use sc_client_api::{BlockBackend, ProofProvider}; +use serde::{Deserialize, Serialize}; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_runtime::generic::BlockId; +use sp_runtime::traits::Block as BlockT; +use std::collections::HashMap; +use std::{fmt::Display, sync::Arc}; + +/// A type that could be a block number or a block hash +#[derive(Clone, Hash, Debug, PartialEq, Eq, Copy, Serialize, Deserialize)] +#[serde(untagged)] +pub enum BlockNumberOrHash { + /// Block hash + Hash(Hash), + /// Block number + Number(u32), +} + +impl Display for BlockNumberOrHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlockNumberOrHash::Hash(hash) => write!(f, "{:?}", hash), + BlockNumberOrHash::Number(block_num) => write!(f, "{}", block_num), + } + } +} + +/// Contains a scale encoded Mmr Proof or Trie proof +#[derive(Serialize, Deserialize)] +pub struct Proof { + /// Scale encoded `pallet_ismp::primitives::Proof` or state trie proof `Vec>` + pub proof: Vec, + /// Optional scale encoded `Vec` for mmr proof + pub leaves: Option>, + /// Height at which proof was recovered + pub height: u32, +} + +/// Converts a runtime trap into an RPC error. +fn runtime_error_into_rpc_error(e: impl std::fmt::Display) -> RpcError { + RpcError::Call(CallError::Custom(ErrorObject::owned( + 9876, // no real reason for this value + "Something wrong", + Some(format!("{}", e)), + ))) +} + +/// ISMP RPC methods. +#[rpc(client, server)] +pub trait IsmpApi +where + Hash: PartialEq + Eq + std::hash::Hash, +{ + /// Query full request data from the ismp pallet + #[method(name = "ismp_queryRequests")] + fn query_requests(&self, query: Vec) -> Result>; + + /// Query full response data from the ismp pallet + #[method(name = "ismp_queryResponses")] + fn query_responses(&self, query: Vec) -> Result>; + + /// Query mmr proof for some requests + #[method(name = "ismp_queryRequestsMmrProof")] + fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result; + + /// Query mmr proof for some responses + #[method(name = "ismp_queryResponsesMmrProof")] + fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result; + + /// Query membership or non-membership proof for some keys + #[method(name = "ismp_queryStateProof")] + fn query_state_proof(&self, height: u32, keys: Vec>) -> Result; + + /// Query scale encoded consensus state + #[method(name = "ismp_queryConsensusState")] + fn query_consensus_state( + &self, + height: Option, + client_id: ConsensusClientId, + ) -> Result>; + + /// Query ISMP Events that were deposited in a series of blocks + /// Using String keys because HashMap fails to deserialize when key is not a String + #[method(name = "ibc_queryEvents")] + fn query_events( + &self, + block_numbers: Vec>, + ) -> Result>>; +} + +/// An implementation of ISMP specific RPC methods. +pub struct IsmpRpcHandler { + client: Arc, + _marker: std::marker::PhantomData, +} + +impl IsmpRpcHandler { + /// Create new `IsmpRpcHandler` with the given reference to the client. + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: Default::default(), + } + } +} + +impl IsmpApiServer for IsmpRpcHandler +where + Block: BlockT, + C: Send + + Sync + + 'static + + ProvideRuntimeApi + + HeaderBackend + + ProofProvider + + BlockBackend, + C::Api: IsmpRuntimeApi, +{ + fn query_requests(&self, query: Vec) -> Result> { + let api = self.client.runtime_api(); + let at = BlockId::Hash(self.client.info().best_hash); + let request_indices: Vec = api + .get_request_leaf_indices(&at, query) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching request leaf indices"))?; + + api.get_requests(&at, request_indices) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching requests")) + } + + fn query_responses(&self, query: Vec) -> Result> { + let api = self.client.runtime_api(); + let at = BlockId::Hash(self.client.info().best_hash); + let response_indices: Vec = api + .get_response_leaf_indices(&at, query) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + + api.get_responses(&at, response_indices) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching responses")) + } + + fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result { + let api = self.client.runtime_api(); + let at = BlockId::Number(height.into()); + let request_indices: Vec = api + .get_request_leaf_indices(&at, query) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + + let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api + .generate_proof(&at, request_indices) + .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? + .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; + Ok(Proof { + proof: proof.encode(), + leaves: Some(leaves.encode()), + height, + }) + } + + fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result { + let api = self.client.runtime_api(); + let at = BlockId::Number(height.into()); + let response_indices: Vec = api + .get_response_leaf_indices(&at, query) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + + let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api + .generate_proof(&at, response_indices) + .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? + .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; + Ok(Proof { + proof: proof.encode(), + leaves: Some(leaves.encode()), + height, + }) + } + + fn query_state_proof(&self, _height: u32, _keys: Vec>) -> Result { + unimplemented!() + } + + fn query_consensus_state( + &self, + height: Option, + client_id: ConsensusClientId, + ) -> Result> { + let api = self.client.runtime_api(); + let at = height + .map(|height| BlockId::Number(height.into())) + .unwrap_or(BlockId::Hash(self.client.info().best_hash)); + api.consensus_state(&at, client_id) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) + } + + fn query_events( + &self, + block_numbers: Vec>, + ) -> Result>> { + let api = self.client.runtime_api(); + let mut events = HashMap::new(); + for block_number_or_hash in block_numbers { + let at = match block_number_or_hash { + BlockNumberOrHash::Hash(block_hash) => BlockId::Hash(block_hash), + BlockNumberOrHash::Number(block_number) => BlockId::Number(block_number.into()), + }; + + let temp = api.block_events(&at).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("[ibc_rpc]: failed to read block events") + })?; + events.insert(block_number_or_hash.to_string(), temp); + } + Ok(events) + } +} diff --git a/runtime-api/Cargo.toml b/runtime-api/Cargo.toml new file mode 100644 index 000000000..4bc87b0e8 --- /dev/null +++ b/runtime-api/Cargo.toml @@ -0,0 +1,25 @@ +[package] +edition = "2021" +name = "ismp-runtime-api" +version = "0.1.0" +authors = ["Polytope Labs"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +pallet-ismp = { path = "..", default-features = false } +ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } + +[dependencies.codec] +package = "parity-scale-codec" +version = "3.0.0" +features = ["derive"] +default-features = false + +[features] +default = ['std'] +std = ['sp-api/std', 'sp-std/std', 'codec/std', "pallet-ismp/std", "ismp-rust/std", "serde"] diff --git a/runtime-api/src/lib.rs b/runtime-api/src/lib.rs new file mode 100644 index 000000000..0bde13c2d --- /dev/null +++ b/runtime-api/src/lib.rs @@ -0,0 +1,56 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::too_many_arguments)] + +use ismp_rust::{ + consensus_client::ConsensusClientId, + host::ChainID, + router::{Request, Response}, +}; +use pallet_ismp::{ + mmr::{Leaf, LeafIndex}, + primitives::{Error, Proof}, +}; +#[cfg(not(feature = "std"))] +use sp_std::vec::Vec; + +#[derive(codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub struct LeafIndexQuery { + pub source_chain: ChainID, + pub dest_chain: ChainID, + pub nonce: u64, +} + +sp_api::decl_runtime_apis! { + /// ISMP Runtime Apis + pub trait IsmpRuntimeApi { + /// Return the number of MMR leaves. + fn mmr_leaf_count() -> Result; + + /// Return the on-chain MMR root hash. + fn mmr_root() -> Result; + + /// Generate a proof for the provided leaf indices + fn generate_proof( + leaf_indices: Vec + ) -> Result<(Vec, Proof), Error>; + + /// Fetch all ISMP events + fn block_events() -> Option>; + + /// Return the scale encoded consensus state + fn consensus_state(id: ConsensusClientId) -> Option>; + + /// Get Request Leaf Indices + fn get_request_leaf_indices(leaf_queries: Vec) -> Option>; + + /// Get Response Leaf Indices + fn get_response_leaf_indices(leaf_queries: Vec) -> Option>; + + /// Get actual requests + fn get_requests(leaf_indices: Vec) -> Option>; + + /// Get actual requests + fn get_responses(leaf_indices: Vec) -> Option>; + } +} diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 000000000..1c6bdc0f9 --- /dev/null +++ b/src/events.rs @@ -0,0 +1,64 @@ +use crate::Config; +use crate::Event as PalletEvent; +use ismp_rust::consensus_client::StateMachineId; +use ismp_rust::host::ChainID; + +#[derive(codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum Event { + /// Event to be emitted when the challenge period for a state machine update has elapsed + StateMachineUpdated { + state_machine_id: StateMachineId, + latest_height: u64, + previous_height: u64, + }, + Response { + /// Chain that this response will be routed to + dest_chain: ChainID, + /// Source Chain for this response + source_chain: ChainID, + /// Nonce for the request which this response is for + request_nonce: u64, + }, + Request { + /// Chain that this request will be routed to + dest_chain: ChainID, + /// Source Chain for request + source_chain: ChainID, + /// Request nonce + request_nonce: u64, + }, +} + +pub fn to_core_protocol_events(event: PalletEvent) -> Option { + match event { + PalletEvent::StateMachineUpdated { + state_machine_id, + latest_height, + previous_height, + } => Some(Event::StateMachineUpdated { + state_machine_id, + latest_height, + previous_height, + }), + PalletEvent::Response { + dest_chain, + source_chain, + request_nonce, + } => Some(Event::Response { + dest_chain, + source_chain, + request_nonce, + }), + PalletEvent::Request { + dest_chain, + source_chain, + request_nonce, + } => Some(Event::Request { + dest_chain, + source_chain, + request_nonce, + }), + _ => None, + } +} diff --git a/src/host.rs b/src/host.rs index 7cc1fa60c..f7a73388d 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,7 +1,7 @@ use crate::router::Router; use crate::{ Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, - RequestAcks, StateCommitments, StateMachineUpdateTime, + RequestAcks, StateCommitments, }; use alloc::format; use alloc::string::ToString; @@ -9,6 +9,7 @@ use core::time::Duration; use frame_support::traits::UnixTime; use ismp_rust::consensus_client::{ ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, + ETHEREUM_CONSENSUS_CLIENT_ID, }; use ismp_rust::error::Error; use ismp_rust::host::{ChainID, ISMPHost}; @@ -54,14 +55,6 @@ impl ISMPHost for Host { }) } - fn state_machine_update_time(&self, height: StateMachineHeight) -> Result { - StateMachineUpdateTime::::get(height) - .map(|timestamp| Duration::from_nanos(timestamp)) - .ok_or_else(|| { - Error::ImplementationSpecific(format!("Update time not found for {:?}", height)) - }) - } - fn consensus_state(&self, id: ConsensusClientId) -> Result, Error> { ConsensusStates::::get(id).ok_or_else(|| Error::ConsensusStateNotFound { id }) } @@ -108,15 +101,6 @@ impl ISMPHost for Host { Ok(()) } - fn store_state_machine_update_time( - &self, - height: StateMachineHeight, - timestamp: Duration, - ) -> Result<(), Error> { - StateMachineUpdateTime::::insert(height, timestamp.as_nanos().saturated_into::()); - Ok(()) - } - fn store_state_machine_commitment( &self, height: StateMachineHeight, @@ -139,8 +123,11 @@ impl ISMPHost for Host { sp_io::hashing::keccak_256(bytes) } - fn delay_period(&self, _id: StateMachineId) -> Duration { - Duration::from_secs(5 * 60) + fn delay_period(&self, id: ConsensusClientId) -> Duration { + match id { + id if id == ETHEREUM_CONSENSUS_CLIENT_ID => Duration::from_secs(30 * 60), + _ => Duration::from_secs(15 * 60), + } } fn ismp_router(&self) -> Box { diff --git a/src/lib.rs b/src/lib.rs index 690548fef..e10ebfbfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,10 @@ extern crate alloc; +pub mod events; pub mod host; -mod mmr; -mod primitives; +pub mod mmr; +pub mod primitives; mod router; use codec::{Decode, Encode}; @@ -144,14 +145,10 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn latest_state_height)] + /// The latest accepted state machine height pub type LatestStateMachineHeight = StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; - #[pallet::storage] - #[pallet::getter(fn state_update_time)] - pub type StateMachineUpdateTime = - StorageMap<_, Blake2_128Concat, StateMachineHeight, u64, OptionQuery>; - #[pallet::storage] #[pallet::getter(fn consensus_update_time)] pub type ConsensusClientUpdateTime = @@ -217,11 +214,13 @@ pub mod pallet { /// it is optional, it is also possible to provide a custom implementation. #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - ConsensusClientUpdated { - id: ConsensusClientId, - height: u64, + /// Event to be emitted when the challenge period for a state machine update has elapsed + StateMachineUpdated { + state_machine_id: StateMachineId, + latest_height: u64, + previous_height: u64, }, - ResponseReceived { + Response { /// Chain that this response will be routed to dest_chain: ChainID, /// Source Chain for this response @@ -229,7 +228,7 @@ pub mod pallet { /// Nonce for the request which this response is for request_nonce: u64, }, - RequestReceived { + Request { /// Chain that this request will be routed to dest_chain: ChainID, /// Source Chain for request diff --git a/src/primitives.rs b/src/primitives.rs index 4bd040482..1fb37e40a 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -31,19 +31,3 @@ pub enum Error { InvalidLeafIndex, InvalidBestKnownBlock, } - -sp_api::decl_runtime_apis! { - /// API to interact with pallet-ismp's Mmr. - pub trait IsmpMmrApi { - /// Return the number of MMR leaves. - fn mmr_leaf_count() -> Result; - - /// Return the on-chain MMR root hash. - fn mmr_root() -> Result; - - /// generate a proof - fn generate_proof( - leaf_indices: Vec - ) -> Result<(Vec, Proof), Error>; - } -} diff --git a/src/router.rs b/src/router.rs index d877c7f14..2924ecbc5 100644 --- a/src/router.rs +++ b/src/router.rs @@ -49,7 +49,7 @@ impl IISMPRouter for Router { Error::ImplementationSpecific("Failed to push request into mmr".to_string()) })?; // Deposit Event - Pallet::::deposit_event(Event::RequestReceived { + Pallet::::deposit_event(Event::Request { request_nonce: nonce, source_chain, dest_chain, @@ -94,7 +94,7 @@ impl IISMPRouter for Router { let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { Error::ImplementationSpecific("Failed to push response into mmr".to_string()) })?; - Pallet::::deposit_event(Event::ResponseReceived { + Pallet::::deposit_event(Event::Response { request_nonce: nonce, dest_chain, source_chain, From 672a0519dcb5b747604da98e0b69afa07ef26289 Mon Sep 17 00:00:00 2001 From: Femi Bankole Date: Mon, 27 Mar 2023 08:50:02 +0100 Subject: [PATCH 104/182] CI on merge (#23) * run ci only on merge to main --- .github/workflows/test.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ad55a846..f5710f738 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,12 +1,9 @@ -name: Test +name: CI on: push: - branches: - - main + branches: [main] pull_request: - branches: - - main jobs: check: @@ -29,9 +26,7 @@ jobs: test: name: Test Suite runs-on: ubuntu-latest - # defaults: - # run: - # shell: bash + if: github.ref == 'refs/heads/main' env: TUID: 123 steps: @@ -118,7 +113,7 @@ jobs: sed -i 's/BELLATRIX_FORK_VERSION=.*/BELLATRIX_FORK_VERSION="0x02000000"/' values.env sed -i 's/CAPELLA_FORK_VERSION=.*/CAPELLA_FORK_VERSION="0x03000000"/' values.env sed -i 's/EIP4844_FORK_VERSION=.*/EIP4844_FORK_VERSION="0x04000000"/' values.env - + - name: remove tty flag from docker command in create-config recipe run: | cd eth-testnet-runner From 197c6ae34bc20248c912bc2c5f9d67cdfabcd565 Mon Sep 17 00:00:00 2001 From: Femi Bankole Date: Mon, 3 Apr 2023 11:58:54 +0100 Subject: [PATCH 105/182] ismp message handling extrinsic (#9) --- src/errors.rs | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/host.rs | 5 +++ src/lib.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 src/errors.rs diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 000000000..84da753a6 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,115 @@ +use codec::{Decode, Encode}; +use ismp_rust::consensus_client::{ConsensusClientId, StateMachineHeight}; +use ismp_rust::error::Error as IsmpError; +use ismp_rust::host::ChainID; +use sp_std::prelude::*; + +#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +pub enum HandlingError { + ChallengePeriodNotElapsed { + update_time: u64, + current_time: u64, + delay_period: Option, + consensus_client_id: Option, + }, + ConsensusStateNotFound { + id: ConsensusClientId, + }, + StateCommitmentNotFound { + height: StateMachineHeight, + }, + FrozenConsensusClient { + id: ConsensusClientId, + }, + FrozenStateMachine { + height: StateMachineHeight, + }, + RequestCommitmentNotFound { + nonce: u64, + source: ChainID, + dest: ChainID, + }, + RequestVerificationFailed { + nonce: u64, + source: ChainID, + dest: ChainID, + }, + ResponseVerificationFailed { + nonce: u64, + source: ChainID, + dest: ChainID, + }, + ConsensusProofVerificationFailed { + id: ConsensusClientId, + }, + ExpiredConsensusClient { + id: ConsensusClientId, + }, + CannotHandleConsensusMessage, + ImplementationSpecific { + msg: Vec, + }, +} + +impl From for HandlingError { + fn from(value: ismp_rust::error::Error) -> Self { + match value { + IsmpError::DelayNotElapsed { + current_time, + update_time, + } => HandlingError::ChallengePeriodNotElapsed { + update_time: update_time.as_secs(), + current_time: current_time.as_secs(), + delay_period: None, + consensus_client_id: None, + }, + IsmpError::ConsensusStateNotFound { id } => { + HandlingError::ConsensusStateNotFound { id } + } + IsmpError::StateCommitmentNotFound { height } => { + HandlingError::StateCommitmentNotFound { height } + } + IsmpError::FrozenConsensusClient { id } => HandlingError::FrozenConsensusClient { id }, + IsmpError::FrozenStateMachine { height } => { + HandlingError::FrozenStateMachine { height } + } + IsmpError::RequestCommitmentNotFound { + nonce, + source, + dest, + } => HandlingError::RequestCommitmentNotFound { + nonce, + source, + dest, + }, + IsmpError::RequestVerificationFailed { + nonce, + source, + dest, + } => HandlingError::ResponseVerificationFailed { + nonce, + source, + dest, + }, + IsmpError::ResponseVerificationFailed { + nonce, + source, + dest, + } => HandlingError::ResponseVerificationFailed { + nonce, + source, + dest, + }, + IsmpError::ConsensusProofVerificationFailed { id } => { + HandlingError::ConsensusProofVerificationFailed { id } + } + IsmpError::ExpiredConsensusClient { id } => { + HandlingError::ExpiredConsensusClient { id } + } + IsmpError::CannotHandleConsensusMessage => HandlingError::CannotHandleConsensusMessage, + IsmpError::ImplementationSpecific(msg) => HandlingError::ImplementationSpecific { + msg: msg.as_bytes().to_vec(), + }, + } + } +} diff --git a/src/host.rs b/src/host.rs index f7a73388d..31e4f373e 100644 --- a/src/host.rs +++ b/src/host.rs @@ -133,4 +133,9 @@ impl ISMPHost for Host { fn ismp_router(&self) -> Box { Box::new(Router::::default()) } + + fn store_latest_commitment_height(&self, height: StateMachineHeight) -> Result<(), Error> { + LatestStateMachineHeight::::insert(height.id, height.height); + Ok(()) + } } diff --git a/src/lib.rs b/src/lib.rs index e10ebfbfd..b77577522 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,19 +18,22 @@ extern crate alloc; +mod errors; pub mod events; pub mod host; pub mod mmr; pub mod primitives; mod router; +use crate::host::Host; +use crate::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}; use codec::{Decode, Encode}; -use frame_support::RuntimeDebug; -use ismp_rust::host::ChainID; +use frame_support::{log::debug, RuntimeDebug}; +use ismp_rust::host::{ChainID, ISMPHost}; +use ismp_rust::messaging::Message; use ismp_rust::router::{Request, Response}; use sp_core::offchain::StorageKind; // Re-export pallet items so that they can be accessed from the crate namespace. -use crate::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}; pub use pallet::*; use sp_std::prelude::*; @@ -40,14 +43,17 @@ use sp_std::prelude::*; pub mod pallet { // Import various types used to declare pallet in scope. use super::*; + use crate::errors::HandlingError; use crate::mmr::{LeafIndex, Mmr, NodeIndex}; use crate::primitives::ISMP_ID; + use alloc::collections::BTreeSet; use frame_support::pallet_prelude::*; use frame_support::traits::UnixTime; use frame_system::pallet_prelude::*; use ismp_rust::consensus_client::{ ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }; + use ismp_rust::handlers::{handle_incoming_message, MessageResult}; use ismp_rust::host::ChainID; use sp_runtime::traits; @@ -166,6 +172,18 @@ pub mod pallet { /// No hashing, just insert raw key in storage pub type ResponseAcks = StorageMap<_, Identity, Vec, Vec, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn consensus_update_results)] + /// Consensus update results still in challenge period + /// Set contains a tuple of previous height and latest height + pub type ConsensusUpdateResults = StorageMap< + _, + Twox64Concat, + ConsensusClientId, + BTreeSet<(StateMachineHeight, StateMachineHeight)>, + OptionQuery, + >; + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet { @@ -204,7 +222,81 @@ pub mod pallet { } #[pallet::call] - impl Pallet {} + impl Pallet { + /// Handles ismp messages + #[pallet::weight(0)] + #[pallet::call_index(0)] + pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResult { + let _ = ensure_signed(origin)?; + // Define a host + let host = Host::::default(); + let mut errors: Vec = vec![]; + for message in messages { + // Check that delay period is satisfied for consensus client before accepting any new update + match &message { + Message::Consensus(msg) => { + // check difference between last + if let Ok(consensus_update_time) = + host.consensus_update_time(msg.consensus_client_id) + { + let elapsed_time = host.host_timestamp() - consensus_update_time; + if host.delay_period(msg.consensus_client_id) > elapsed_time { + debug!(target: "ismp-rust", "Challenge period: Cannot handle consensus message for {:?}", msg.consensus_client_id); + errors.push(HandlingError::ChallengePeriodNotElapsed { + update_time: consensus_update_time.as_secs(), + current_time: host.host_timestamp().as_secs(), + delay_period: Some( + host.delay_period(msg.consensus_client_id).as_secs(), + ), + consensus_client_id: Some(msg.consensus_client_id), + }); + continue; + } + } else { + // If we can't find a previous update time for the consensus client we don't process it + continue; + } + } + _ => {} + } + match handle_incoming_message(&host, message) { + Ok(MessageResult::ConsensusMessage(res)) => { + // Deposit events for previous update result that has passed the challenge period + if let Some(pending_updates) = + ConsensusUpdateResults::::get(res.consensus_client_id) + { + for (prev_height, latest_height) in pending_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + previous_height: prev_height.height, + }) + } + } + + // Store the new update result that have just entered the challenge period + ConsensusUpdateResults::::insert( + res.consensus_client_id, + res.state_updates, + ); + } + Ok(_) => { + // Do nothing, event has been deposited in ismp router + } + Err(err) => { + errors.push(err.into()); + } + } + } + + if !errors.is_empty() { + debug!(target: "ismp-rust", "Handling Errors {:?}", errors); + Self::deposit_event(Event::::HandlingErrors { errors }) + } + + Ok(()) + } + } /// Events are a simple means of reporting specific conditions and /// circumstances that have happened that users, Dapps and/or chain explorers would find @@ -236,7 +328,13 @@ pub mod pallet { /// Request nonce request_nonce: u64, }, + HandlingErrors { + errors: Vec, + }, } + + #[pallet::error] + pub enum Error {} } impl Pallet { From f1404393d75b9a7483b96699a38fead15dcaf140 Mon Sep 17 00:00:00 2001 From: omadoyeabraham Date: Tue, 4 Apr 2023 11:02:29 +0100 Subject: [PATCH 106/182] Added CI/CD using Github actions (#13) --- .github/workflows/build-test-and-lint.yml | 68 ++++++++++++++++++++ rpc/src/lib.rs | 65 ++++++++----------- rust-toolchain.toml | 2 + rustfmt.toml | 9 +++ src/errors.rs | 67 ++++++++----------- src/events.rs | 42 ++++-------- src/host.rs | 24 +++---- src/lib.rs | 78 +++++++++++------------ src/mmr/mmr.rs | 25 ++------ src/mmr/mod.rs | 16 ++--- src/mmr/storage.rs | 24 +++---- src/mmr/utils.rs | 4 +- src/router.rs | 25 ++++---- 13 files changed, 227 insertions(+), 222 deletions(-) create mode 100644 .github/workflows/build-test-and-lint.yml create mode 100644 rust-toolchain.toml create mode 100644 rustfmt.toml diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml new file mode 100644 index 000000000..ed5ccd1e2 --- /dev/null +++ b/.github/workflows/build-test-and-lint.yml @@ -0,0 +1,68 @@ +name: CI suite + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build_and_test: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + with: + token: ${{ secrets.GH_TOKEN }} + submodules: recursive + + - name: Install toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly + targets: wasm32-unknown-unknown + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Build + run: | + eval `ssh-agent -s` + ssh-add - <<< '${{ secrets.SSH_KEY }}' + cargo +nightly check --all-targets --all-features --verbose + + - name: Build `no-std` + run: | + eval `ssh-agent -s` + ssh-add - <<< '${{ secrets.SSH_KEY }}' + cargo +nightly check --no-default-features --target=wasm32-unknown-unknown --verbose + + - name: Run tests + run: | + eval `ssh-agent -s` + ssh-add - <<< '${{ secrets.SSH_KEY }}' + cargo +nightly test --all-features --verbose + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + with: + token: ${{ secrets.GH_TOKEN }} + submodules: recursive + + - name: Install toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Check format + run: cargo +nightly fmt --all --check diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 90eb90814..65376645f 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -10,17 +10,17 @@ use jsonrpsee::{ use codec::Encode; use ismp_runtime_api::{IsmpRuntimeApi, LeafIndexQuery}; -use ismp_rust::consensus_client::ConsensusClientId; -use ismp_rust::router::{Request, Response}; +use ismp_rust::{ + consensus_client::ConsensusClientId, + router::{Request, Response}, +}; use pallet_ismp::mmr::{Leaf, LeafIndex}; use sc_client_api::{BlockBackend, ProofProvider}; use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; -use sp_runtime::generic::BlockId; -use sp_runtime::traits::Block as BlockT; -use std::collections::HashMap; -use std::{fmt::Display, sync::Arc}; +use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use std::{collections::HashMap, fmt::Display, sync::Arc}; /// A type that could be a block number or a block hash #[derive(Clone, Hash, Debug, PartialEq, Eq, Copy, Serialize, Deserialize)] @@ -113,10 +113,7 @@ pub struct IsmpRpcHandler { impl IsmpRpcHandler { /// Create new `IsmpRpcHandler` with the given reference to the client. pub fn new(client: Arc) -> Self { - Self { - client, - _marker: Default::default(), - } + Self { client, _marker: Default::default() } } } @@ -135,11 +132,10 @@ where fn query_requests(&self, query: Vec) -> Result> { let api = self.client.runtime_api(); let at = BlockId::Hash(self.client.info().best_hash); - let request_indices: Vec = api - .get_request_leaf_indices(&at, query) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching request leaf indices"))?; + let request_indices: Vec = + api.get_request_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching request leaf indices") + })?; api.get_requests(&at, request_indices) .ok() @@ -150,11 +146,10 @@ where fn query_responses(&self, query: Vec) -> Result> { let api = self.client.runtime_api(); let at = BlockId::Hash(self.client.info().best_hash); - let response_indices: Vec = api - .get_response_leaf_indices(&at, query) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + let response_indices: Vec = + api.get_response_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching response leaf indices") + })?; api.get_responses(&at, response_indices) .ok() @@ -165,41 +160,31 @@ where fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result { let api = self.client.runtime_api(); let at = BlockId::Number(height.into()); - let request_indices: Vec = api - .get_request_leaf_indices(&at, query) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + let request_indices: Vec = + api.get_request_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching response leaf indices") + })?; let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api .generate_proof(&at, request_indices) .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; - Ok(Proof { - proof: proof.encode(), - leaves: Some(leaves.encode()), - height, - }) + Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) } fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result { let api = self.client.runtime_api(); let at = BlockId::Number(height.into()); - let response_indices: Vec = api - .get_response_leaf_indices(&at, query) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + let response_indices: Vec = + api.get_response_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching response leaf indices") + })?; let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api .generate_proof(&at, response_indices) .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; - Ok(Proof { - proof: proof.encode(), - leaves: Some(leaves.encode()), - height, - }) + Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) } fn query_state_proof(&self, _height: u32, _keys: Vec>) -> Result { diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..99c6e11a1 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.66" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..e70aee8cc --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,9 @@ +reorder_imports = true +imports_granularity = "Crate" +use_small_heuristics = "Max" +comment_width = 100 +wrap_comments = true +binop_separator = "Back" +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true diff --git a/src/errors.rs b/src/errors.rs index 84da753a6..2860b703d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,7 +1,9 @@ use codec::{Decode, Encode}; -use ismp_rust::consensus_client::{ConsensusClientId, StateMachineHeight}; -use ismp_rust::error::Error as IsmpError; -use ismp_rust::host::ChainID; +use ismp_rust::{ + consensus_client::{ConsensusClientId, StateMachineHeight}, + error::Error as IsmpError, + host::ChainID, +}; use sp_std::prelude::*; #[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] @@ -54,15 +56,14 @@ pub enum HandlingError { impl From for HandlingError { fn from(value: ismp_rust::error::Error) -> Self { match value { - IsmpError::DelayNotElapsed { - current_time, - update_time, - } => HandlingError::ChallengePeriodNotElapsed { - update_time: update_time.as_secs(), - current_time: current_time.as_secs(), - delay_period: None, - consensus_client_id: None, - }, + IsmpError::DelayNotElapsed { current_time, update_time } => { + HandlingError::ChallengePeriodNotElapsed { + update_time: update_time.as_secs(), + current_time: current_time.as_secs(), + delay_period: None, + consensus_client_id: None, + } + } IsmpError::ConsensusStateNotFound { id } => { HandlingError::ConsensusStateNotFound { id } } @@ -73,33 +74,15 @@ impl From for HandlingError { IsmpError::FrozenStateMachine { height } => { HandlingError::FrozenStateMachine { height } } - IsmpError::RequestCommitmentNotFound { - nonce, - source, - dest, - } => HandlingError::RequestCommitmentNotFound { - nonce, - source, - dest, - }, - IsmpError::RequestVerificationFailed { - nonce, - source, - dest, - } => HandlingError::ResponseVerificationFailed { - nonce, - source, - dest, - }, - IsmpError::ResponseVerificationFailed { - nonce, - source, - dest, - } => HandlingError::ResponseVerificationFailed { - nonce, - source, - dest, - }, + IsmpError::RequestCommitmentNotFound { nonce, source, dest } => { + HandlingError::RequestCommitmentNotFound { nonce, source, dest } + } + IsmpError::RequestVerificationFailed { nonce, source, dest } => { + HandlingError::ResponseVerificationFailed { nonce, source, dest } + } + IsmpError::ResponseVerificationFailed { nonce, source, dest } => { + HandlingError::ResponseVerificationFailed { nonce, source, dest } + } IsmpError::ConsensusProofVerificationFailed { id } => { HandlingError::ConsensusProofVerificationFailed { id } } @@ -107,9 +90,9 @@ impl From for HandlingError { HandlingError::ExpiredConsensusClient { id } } IsmpError::CannotHandleConsensusMessage => HandlingError::CannotHandleConsensusMessage, - IsmpError::ImplementationSpecific(msg) => HandlingError::ImplementationSpecific { - msg: msg.as_bytes().to_vec(), - }, + IsmpError::ImplementationSpecific(msg) => { + HandlingError::ImplementationSpecific { msg: msg.as_bytes().to_vec() } + } } } } diff --git a/src/events.rs b/src/events.rs index 1c6bdc0f9..a5294021b 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,7 +1,5 @@ -use crate::Config; -use crate::Event as PalletEvent; -use ismp_rust::consensus_client::StateMachineId; -use ismp_rust::host::ChainID; +use crate::{Config, Event as PalletEvent}; +use ismp_rust::{consensus_client::StateMachineId, host::ChainID}; #[derive(codec::Encode, codec::Decode)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] @@ -32,33 +30,15 @@ pub enum Event { pub fn to_core_protocol_events(event: PalletEvent) -> Option { match event { - PalletEvent::StateMachineUpdated { - state_machine_id, - latest_height, - previous_height, - } => Some(Event::StateMachineUpdated { - state_machine_id, - latest_height, - previous_height, - }), - PalletEvent::Response { - dest_chain, - source_chain, - request_nonce, - } => Some(Event::Response { - dest_chain, - source_chain, - request_nonce, - }), - PalletEvent::Request { - dest_chain, - source_chain, - request_nonce, - } => Some(Event::Request { - dest_chain, - source_chain, - request_nonce, - }), + PalletEvent::StateMachineUpdated { state_machine_id, latest_height, previous_height } => { + Some(Event::StateMachineUpdated { state_machine_id, latest_height, previous_height }) + } + PalletEvent::Response { dest_chain, source_chain, request_nonce } => { + Some(Event::Response { dest_chain, source_chain, request_nonce }) + } + PalletEvent::Request { dest_chain, source_chain, request_nonce } => { + Some(Event::Request { dest_chain, source_chain, request_nonce }) + } _ => None, } } diff --git a/src/host.rs b/src/host.rs index 31e4f373e..bf87a3516 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,20 +1,20 @@ -use crate::router::Router; use crate::{ - Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, - RequestAcks, StateCommitments, + router::Router, Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, + LatestStateMachineHeight, RequestAcks, StateCommitments, }; -use alloc::format; -use alloc::string::ToString; +use alloc::{format, string::ToString}; use core::time::Duration; use frame_support::traits::UnixTime; -use ismp_rust::consensus_client::{ - ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, - ETHEREUM_CONSENSUS_CLIENT_ID, +use ismp_rust::{ + consensus_client::{ + ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, + ETHEREUM_CONSENSUS_CLIENT_ID, + }, + error::Error, + host::{ChainID, ISMPHost}, + paths::RequestPath, + router::{IISMPRouter, Request}, }; -use ismp_rust::error::Error; -use ismp_rust::host::{ChainID, ISMPHost}; -use ismp_rust::paths::RequestPath; -use ismp_rust::router::{IISMPRouter, Request}; use sp_runtime::SaturatedConversion; use sp_std::prelude::*; diff --git a/src/lib.rs b/src/lib.rs index b77577522..8676eca76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,13 +25,17 @@ pub mod mmr; pub mod primitives; mod router; -use crate::host::Host; -use crate::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}; +use crate::{ + host::Host, + mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}, +}; use codec::{Decode, Encode}; use frame_support::{log::debug, RuntimeDebug}; -use ismp_rust::host::{ChainID, ISMPHost}; -use ismp_rust::messaging::Message; -use ismp_rust::router::{Request, Response}; +use ismp_rust::{ + host::{ChainID, ISMPHost}, + messaging::Message, + router::{Request, Response}, +}; use sp_core::offchain::StorageKind; // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; @@ -43,18 +47,21 @@ use sp_std::prelude::*; pub mod pallet { // Import various types used to declare pallet in scope. use super::*; - use crate::errors::HandlingError; - use crate::mmr::{LeafIndex, Mmr, NodeIndex}; - use crate::primitives::ISMP_ID; + use crate::{ + errors::HandlingError, + mmr::{LeafIndex, Mmr, NodeIndex}, + primitives::ISMP_ID, + }; use alloc::collections::BTreeSet; - use frame_support::pallet_prelude::*; - use frame_support::traits::UnixTime; + use frame_support::{pallet_prelude::*, traits::UnixTime}; use frame_system::pallet_prelude::*; - use ismp_rust::consensus_client::{ - ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, + use ismp_rust::{ + consensus_client::{ + ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, + }, + handlers::{handle_incoming_message, MessageResult}, + host::ChainID, }; - use ismp_rust::handlers::{handle_incoming_message, MessageResult}; - use ismp_rust::host::ChainID; use sp_runtime::traits; /// Our pallet's configuration trait. All our types and constants go in here. If the @@ -203,16 +210,14 @@ pub mod pallet { Ok((leaves, root)) => (leaves, root), Err(e) => { log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); - return; + return } }; >::put(leaves); >::put(root); - let log = RequestResponseLog:: { - mmr_root_hash: root, - }; + let log = RequestResponseLog:: { mmr_root_hash: root }; let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, log.encode()); >::deposit_log(digest); @@ -232,7 +237,8 @@ pub mod pallet { let host = Host::::default(); let mut errors: Vec = vec![]; for message in messages { - // Check that delay period is satisfied for consensus client before accepting any new update + // Check that delay period is satisfied for consensus client before accepting any + // new update match &message { Message::Consensus(msg) => { // check difference between last @@ -250,18 +256,20 @@ pub mod pallet { ), consensus_client_id: Some(msg.consensus_client_id), }); - continue; + continue } } else { - // If we can't find a previous update time for the consensus client we don't process it - continue; + // If we can't find a previous update time for the consensus client we + // don't process it + continue } } _ => {} } match handle_incoming_message(&host, message) { Ok(MessageResult::ConsensusMessage(res)) => { - // Deposit events for previous update result that has passed the challenge period + // Deposit events for previous update result that has passed the challenge + // period if let Some(pending_updates) = ConsensusUpdateResults::::get(res.consensus_client_id) { @@ -394,14 +402,7 @@ impl Pallet { dest_chain: ChainID, nonce: u64, ) -> Vec { - ( - T::INDEXING_PREFIX, - "Requests/leaf_indices", - source_chain, - dest_chain, - nonce, - ) - .encode() + (T::INDEXING_PREFIX, "Requests/leaf_indices", source_chain, dest_chain, nonce).encode() } pub fn response_leaf_index_offchain_key( @@ -409,14 +410,7 @@ impl Pallet { dest_chain: ChainID, nonce: u64, ) -> Vec { - ( - T::INDEXING_PREFIX, - "Responses/leaf_indices", - source_chain, - dest_chain, - nonce, - ) - .encode() + (T::INDEXING_PREFIX, "Responses/leaf_indices", source_chain, dest_chain, nonce).encode() } fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { @@ -433,7 +427,7 @@ impl Pallet { _ => None, }, _ => None, - }; + } } None } @@ -448,7 +442,7 @@ impl Pallet { _ => None, }, _ => None, - }; + } } None } @@ -465,7 +459,7 @@ impl Pallet { Self::response_leaf_index_offchain_key(source_chain, dest_chain, nonce) }; if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - return LeafIndex::decode(&mut &*elem).ok(); + return LeafIndex::decode(&mut &*elem).ok() } None } diff --git a/src/mmr/mmr.rs b/src/mmr/mmr.rs index 86a823642..2c66069b4 100644 --- a/src/mmr/mmr.rs +++ b/src/mmr/mmr.rs @@ -13,15 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::mmr::utils::NodesUtils; -use crate::mmr::{FullLeaf, NodeIndex}; -use crate::primitives::Proof; use crate::{ mmr::{ storage::{OffchainStorage, RuntimeStorage, Storage}, - Hasher, NodeOf, + utils::NodesUtils, + FullLeaf, Hasher, NodeIndex, NodeOf, }, - primitives::Error, + primitives::{Error, Proof}, Config, }; use sp_std::prelude::*; @@ -49,10 +47,7 @@ where /// Create a pointer to an existing MMR with given number of leaves. pub fn new(leaves: NodeIndex) -> Self { let size = NodesUtils::new(leaves).size(); - Self { - mmr: mmr_lib::MMR::new(size, Default::default()), - leaves, - } + Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves } } /// Return the internal size of the MMR (number of nodes). @@ -72,11 +67,7 @@ where /// /// Returns element position (index) in the MMR. pub fn push(&mut self, leaf: L) -> Option { - let position = self - .mmr - .push(NodeOf::Data(leaf)) - .map_err(|_| Error::Push) - .ok()?; + let position = self.mmr.push(NodeOf::Data(leaf)).map_err(|_| Error::Push).ok()?; self.leaves += 1; @@ -106,10 +97,8 @@ where &self, leaf_indices: Vec, ) -> Result<(Vec, Proof<::Hash>), Error> { - let positions = leaf_indices - .iter() - .map(|index| mmr_lib::leaf_index_to_pos(*index)) - .collect::>(); + let positions = + leaf_indices.iter().map(|index| mmr_lib::leaf_index_to_pos(*index)).collect::>(); let store = >::default(); let leaves = positions .iter() diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs index 1dbd9fb54..739fa0392 100644 --- a/src/mmr/mod.rs +++ b/src/mmr/mod.rs @@ -12,18 +12,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use core::fmt; -use core::fmt::Formatter; +use core::{fmt, fmt::Formatter}; pub mod mmr; pub mod storage; mod utils; -use crate::host::Host; -use crate::Config; +use crate::{host::Host, Config}; use codec::{Decode, Encode}; -use ismp_rust::host::ISMPHost; -use ismp_rust::router::{Request, Response}; +use ismp_rust::{ + host::ISMPHost, + router::{Request, Response}, +}; use sp_runtime::traits; pub use self::mmr::Mmr; @@ -111,8 +111,6 @@ impl> mmr_lib::Merge for Hasher { let mut concat = left.hash().as_ref().to_vec(); concat.extend_from_slice(right.hash().as_ref()); - Ok(NodeOf::Hash( - <::Hashing as traits::Hash>::hash(&concat), - )) + Ok(NodeOf::Hash(<::Hashing as traits::Hash>::hash(&concat))) } } diff --git a/src/mmr/storage.rs b/src/mmr/storage.rs index fae2e4a5e..ad9ab44f0 100644 --- a/src/mmr/storage.rs +++ b/src/mmr/storage.rs @@ -23,9 +23,10 @@ use sp_std::iter::Peekable; #[cfg(not(feature = "std"))] use sp_std::prelude::*; -use crate::mmr::utils::NodesUtils; -use crate::mmr::{FullLeaf, NodeIndex}; -use crate::{mmr::NodeOf, Config, Pallet}; +use crate::{ + mmr::{utils::NodesUtils, FullLeaf, NodeIndex, NodeOf}, + Config, Pallet, +}; /// A marker type for runtime-specific storage implementation. /// @@ -71,7 +72,7 @@ where ); // Try to retrieve the element from Off-chain DB. if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - return Ok(codec::Decode::decode(&mut &*elem).ok()); + return Ok(codec::Decode::decode(&mut &*elem).ok()) } Ok(None) @@ -93,7 +94,7 @@ where fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { if elems.is_empty() { - return Ok(()); + return Ok(()) } trace!( @@ -105,7 +106,7 @@ where let size = NodesUtils::new(leaves).size(); if pos != size { - return Err(mmr_lib::Error::InconsistentStore); + return Err(mmr_lib::Error::InconsistentStore) } let new_size = size + elems.len() as NodeIndex; @@ -166,17 +167,10 @@ where fn peaks_to_prune_and_store( old_size: NodeIndex, new_size: NodeIndex, -) -> ( - impl Iterator, - Peekable>, -) { +) -> (impl Iterator, Peekable>) { // A sorted (ascending) collection of peak indices before and after insertion. // both collections may share a common prefix. - let peaks_before = if old_size == 0 { - vec![] - } else { - helper::get_peaks(old_size) - }; + let peaks_before = if old_size == 0 { vec![] } else { helper::get_peaks(old_size) }; let peaks_after = helper::get_peaks(new_size); trace!(target: "runtime::mmr", "peaks_before: {:?}", peaks_before); trace!(target: "runtime::mmr", "peaks_after: {:?}", peaks_after); diff --git a/src/mmr/utils.rs b/src/mmr/utils.rs index 1d3c209c7..cb8c466ee 100644 --- a/src/mmr/utils.rs +++ b/src/mmr/utils.rs @@ -37,7 +37,7 @@ impl NodesUtils { // Translate a _leaf_ `NodeIndex` to its `LeafIndex`. fn leaf_node_index_to_leaf_index(pos: NodeIndex) -> LeafIndex { if pos == 0 { - return 0; + return 0 } let peaks = helper::get_peaks(pos); (pos + peaks.len() as u64) >> 1 @@ -55,6 +55,6 @@ impl NodesUtils { pub fn _right_branch_ending_in_leaf(leaf_index: LeafIndex) -> Vec { let pos = helper::leaf_index_to_pos(leaf_index); let num_parents = leaf_index.trailing_ones() as u64; - return (pos..=pos + num_parents).collect(); + return (pos..=pos + num_parents).collect() } } diff --git a/src/router.rs b/src/router.rs index 2924ecbc5..9e461acc4 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,13 +1,16 @@ -use crate::host::Host; -use crate::mmr::{self, Leaf, Mmr}; -use crate::{Config, Event, Pallet, RequestAcks, ResponseAcks}; -use alloc::format; -use alloc::string::ToString; +use crate::{ + host::Host, + mmr::{self, Leaf, Mmr}, + Config, Event, Pallet, RequestAcks, ResponseAcks, +}; +use alloc::{format, string::ToString}; use core::marker::PhantomData; -use ismp_rust::error::Error; -use ismp_rust::host::ISMPHost; -use ismp_rust::paths::{RequestPath, ResponsePath}; -use ismp_rust::router::{IISMPRouter, Request, Response}; +use ismp_rust::{ + error::Error, + host::ISMPHost, + paths::{RequestPath, ResponsePath}, + router::{IISMPRouter, Request, Response}, +}; #[derive(Clone)] pub struct Router(PhantomData); @@ -35,7 +38,7 @@ impl IISMPRouter for Router { return Err(Error::ImplementationSpecific(format!( "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", request.nonce, request.source_chain, request.dest_chain - ))); + ))) } if host.host() != request.dest_chain { @@ -78,7 +81,7 @@ impl IISMPRouter for Router { return Err(Error::ImplementationSpecific(format!( "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", response.request.nonce, response.request.source_chain, response.request.dest_chain - ))); + ))) } if host.host() != response.request.source_chain { From d333fd93ba56c0aefc117d6187a534156a2f35d7 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Wed, 5 Apr 2023 19:24:34 +0100 Subject: [PATCH 107/182] Update ismp-rs (#16) * ismp update * updates to rpc --- .github/workflows/build-test-and-lint.yml | 6 +- .gitignore | 1 - Cargo.lock | 4471 +++++++++++++++++++++ Cargo.toml | 6 +- rpc/Cargo.toml | 2 +- rpc/src/lib.rs | 25 +- runtime-api/Cargo.toml | 4 +- runtime-api/src/lib.rs | 5 +- src/errors.rs | 12 +- src/events.rs | 12 +- src/host.rs | 30 +- src/lib.rs | 47 +- src/mmr/mod.rs | 2 +- src/primitives.rs | 2 + src/router.rs | 58 +- 15 files changed, 4597 insertions(+), 86 deletions(-) create mode 100644 Cargo.lock diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index ed5ccd1e2..83d6942c2 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -31,19 +31,19 @@ jobs: run: | eval `ssh-agent -s` ssh-add - <<< '${{ secrets.SSH_KEY }}' - cargo +nightly check --all-targets --all-features --verbose + cargo +nightly check --workspace --all-targets --all-features --verbose - name: Build `no-std` run: | eval `ssh-agent -s` ssh-add - <<< '${{ secrets.SSH_KEY }}' - cargo +nightly check --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose - name: Run tests run: | eval `ssh-agent -s` ssh-add - <<< '${{ secrets.SSH_KEY }}' - cargo +nightly test --all-features --verbose + cargo +nightly test --all-features --verbose lint: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index a24012e42..7a58aee45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target -/Cargo.lock .idea .cargo \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..f7fcacba3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4471 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.2", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.2", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line 0.19.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.30.3", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bstr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52056f6d0584484b57fa6c1a65c1fcb15f3780d8b6a758426d9e3084169b2ddd" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fed94c8770dc25d01154c3ffa64ed0b3ba9d583736f305fed7beebe5d9cf74" +dependencies = [ + "arrayvec 0.7.2", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c451b81faf237d11c7e4f3165eeb6bac61112762c5cfe7b4c0fb7241474358f" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c940133198426d26128f08be2b40b0bd117b84771fd36798969c4d712d81fc" + +[[package]] +name = "cranelift-entity" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a0f1b2fdc18776956370cf8d9b009ded3f855350c480c1c52142510961f352" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34897538b36b216cc8dd324e73263596d51b8cf610da6498322838b2546baf8a" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b2629a569fae540f16a76b70afcc87ad7decb38dc28fa6c648ac73b51e78470" + +[[package]] +name = "cranelift-native" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20937dab4e14d3e225c5adfc9c7106bafd4ac669bdb43027b911ff794c6fb318" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.88.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80fc2288957a94fd342a015811479de1837850924166d1f1856d8406e6f3609b" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.13", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array 0.14.7", + "group", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "frame-support", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", +] + +[[package]] +name = "frame-metadata" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "bitflags", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "Inflector", + "cfg-expr", + "frame-support-procedural-tools", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.7", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes 1.0.10", + "rustix 0.37.7", + "windows-sys 0.45.0", +] + +[[package]] +name = "ismp-rpc" +version = "0.1.0" +dependencies = [ + "frame-system", + "hex-literal", + "ismp-rs", + "ismp-runtime-api", + "jsonrpsee", + "pallet-ismp", + "parity-scale-codec", + "sc-client-api", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "ismp-rs" +version = "0.1.0" +source = "git+ssh://git@github.com/polytope-labs/ismp-rust.git?branch=main#dea54ad5e22ec4d35d0f076fe83a7c9afcd48052" +dependencies = [ + "derive_more", + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", +] + +[[package]] +name = "ismp-runtime-api" +version = "0.1.0" +dependencies = [ + "ismp-rs", + "pallet-ismp", + "parity-scale-codec", + "serde", + "sp-api", + "sp-std", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +dependencies = [ + "anyhow", + "arrayvec 0.7.2", + "async-trait", + "beef", + "futures-channel", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot", + "rand 0.8.5", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kvdb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9" +dependencies = [ + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linregress" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c601a85f5ecd1aba625247bca0031585fb1c446461b142878a16f8245ddeb8" +dependencies = [ + "nalgebra", + "statrs", +] + +[[package]] +name = "linux-raw-sys" +version = "0.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +dependencies = [ + "rustix 0.37.7", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +dependencies = [ + "hash-db", + "hashbrown", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "nalgebra" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "rand 0.8.5", + "rand_distr", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.2", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pallet-ismp" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "derive_more", + "frame-benchmarking", + "frame-support", + "frame-system", + "ismp-rs", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "parity-scale-codec" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "thiserror", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "regalloc2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.35.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +dependencies = [ + "bitflags", + "errno 0.2.8", + "io-lifetimes 0.7.5", + "libc", + "linux-raw-sys 0.0.46", + "windows-sys 0.42.0", +] + +[[package]] +name = "rustix" +version = "0.37.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +dependencies = [ + "bitflags", + "errno 0.3.0", + "io-lifetimes 1.0.10", + "libc", + "linux-raw-sys 0.3.1", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "log", + "sp-core", + "sp-wasm-interface", + "thiserror", +] + +[[package]] +name = "sc-client-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot", + "sc-executor", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-database", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "lru", + "parity-scale-codec", + "parking_lot", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-wasm-interface", + "thiserror", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "log", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "cfg-if", + "libc", + "log", + "once_cell", + "rustix 0.35.13", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmtime", +] + +[[package]] +name = "sc-transaction-pool-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "async-trait", + "futures", + "log", + "serde", + "sp-blockchain", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-utils" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "backtrace", + "futures", + "futures-timer", + "lazy_static", + "log", + "parking_lot", + "prometheus", +] + +[[package]] +name = "scale-info" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "serde_json" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1", +] + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "blake2", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-application-crypto" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-blockchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "futures", + "log", + "lru", + "parity-scale-codec", + "parking_lot", + "sp-api", + "sp-consensus", + "sp-database", + "sp-runtime", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "sp-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "async-trait", + "futures", + "log", + "parity-scale-codec", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-core" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "array-bytes", + "base58", + "bitflags", + "blake2", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing", + "syn 1.0.109", +] + +[[package]] +name = "sp-database" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "kvdb", + "parking_lot", +] + +[[package]] +name = "sp-debug-derive" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot", + "schnorrkel", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "thiserror", + "zstd", +] + +[[package]] +name = "sp-panic-handler" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-state-machine" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-std" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" + +[[package]] +name = "sp-storage" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-tracing" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-trie" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "ahash", + "hash-db", + "hashbrown", + "lazy_static", + "lru", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "scale-info", + "sp-core", + "sp-std", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ss58-registry" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "statrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" +dependencies = [ + "approx", + "lazy_static", + "nalgebra", + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "substrate-prometheus-endpoint" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +dependencies = [ + "hyper", + "log", + "prometheus", + "thiserror", + "tokio", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.6", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.6", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-instrument" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm", + "memory_units", + "num-rational", + "num-traits", +] + +[[package]] +name = "wasmparser" +version = "0.89.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +dependencies = [ + "indexmap", +] + +[[package]] +name = "wasmtime" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad5af6ba38311282f2a21670d96e78266e8c8e2f38cbcd52c254df6ccbc7731" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.36.1", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45de63ddfc8b9223d1adc8f7b2ee5f35d1f6d112833934ad7ea66e4f4339e597" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcd849399d17d2270141cfe47fa0d91ee52d5f8ea9b98cf7ddde0d53e5f79882" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix 0.35.13", + "serde", + "sha2 0.9.9", + "toml", + "windows-sys 0.36.1", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd91339b742ff20bfed4532a27b73c86b5bcbfedd6bea2dcdf2d64471e1b5c6" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", + "log", + "object 0.29.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb881c61f4f627b5d45c54e629724974f8a8890d455bcbe634330cc27309644" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1985c628011fe26adf5e23a5301bdc79b245e0e338f14bb58b39e4e25e4d8681" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "rustix 0.35.13", + "serde", + "target-lexicon", + "thiserror", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-runtime", + "windows-sys 0.36.1", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f671b588486f5ccec8c5a3dba6b4c07eac2e66ab8c60e6f4e53717c77f709731" +dependencies = [ + "object 0.29.0", + "once_cell", + "rustix 0.35.13", +] + +[[package]] +name = "wasmtime-runtime" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8f92ad4b61736339c29361da85769ebc200f184361959d1792832e592a1afd" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix 0.35.13", + "thiserror", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.36.1", +] + +[[package]] +name = "wasmtime-types" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23d61cb4c46e837b431196dd06abb11731541021916d03476a178b54dc07aeb" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index e1d119617..bae02aaf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,11 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkad sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } +ismp-rs = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } +derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display"] } + [features] default = ["std"] std = [ @@ -31,7 +33,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", - "ismp-rust/std", + "ismp-rs/std", "mmr-lib/std", "sp-api/std", "serde" diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 3dc350e5a..0243164f8 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -14,7 +14,7 @@ hex-literal = { version = "0.3.3" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.45" -ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main" } +ismp-rs = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main" } pallet-ismp = { path = ".." } ismp-runtime-api = { path = "../runtime-api" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 65376645f..5d81b05cc 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -9,11 +9,11 @@ use jsonrpsee::{ }; use codec::Encode; -use ismp_runtime_api::{IsmpRuntimeApi, LeafIndexQuery}; -use ismp_rust::{ +use ismp_rs::{ consensus_client::ConsensusClientId, router::{Request, Response}, }; +use ismp_runtime_api::{IsmpRuntimeApi, LeafIndexQuery}; use pallet_ismp::mmr::{Leaf, LeafIndex}; use sc_client_api::{BlockBackend, ProofProvider}; use serde::{Deserialize, Serialize}; @@ -95,6 +95,10 @@ where client_id: ConsensusClientId, ) -> Result>; + /// Query timestamp of when this client was last updated in seconds + #[method(name = "ismp_queryConsensusUpdateTime")] + fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result; + /// Query ISMP Events that were deposited in a series of blocks /// Using String keys because HashMap fails to deserialize when key is not a String #[method(name = "ibc_queryEvents")] @@ -206,6 +210,15 @@ where .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) } + fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result { + let api = self.client.runtime_api(); + let at = BlockId::Hash(self.client.info().best_hash); + api.consensus_update_time(&at, client_id) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) + } + fn query_events( &self, block_numbers: Vec>, @@ -218,9 +231,11 @@ where BlockNumberOrHash::Number(block_number) => BlockId::Number(block_number.into()), }; - let temp = api.block_events(&at).ok().flatten().ok_or_else(|| { - runtime_error_into_rpc_error("[ibc_rpc]: failed to read block events") - })?; + let temp = api + .block_events(&at) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("failed to read block events"))?; events.insert(block_number_or_hash.to_string(), temp); } Ok(events) diff --git a/runtime-api/Cargo.toml b/runtime-api/Cargo.toml index 4bc87b0e8..39eb91679 100644 --- a/runtime-api/Cargo.toml +++ b/runtime-api/Cargo.toml @@ -11,7 +11,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } pallet-ismp = { path = "..", default-features = false } -ismp-rust = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } +ismp-rs = { git = "ssh://git@github.com/polytope-labs/ismp-rust.git", branch = "main", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } [dependencies.codec] @@ -22,4 +22,4 @@ default-features = false [features] default = ['std'] -std = ['sp-api/std', 'sp-std/std', 'codec/std', "pallet-ismp/std", "ismp-rust/std", "serde"] +std = ['sp-api/std', 'sp-std/std', 'codec/std', "pallet-ismp/std", "ismp-rs/std", "serde"] diff --git a/runtime-api/src/lib.rs b/runtime-api/src/lib.rs index 0bde13c2d..611de0806 100644 --- a/runtime-api/src/lib.rs +++ b/runtime-api/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::too_many_arguments)] -use ismp_rust::{ +use ismp_rs::{ consensus_client::ConsensusClientId, host::ChainID, router::{Request, Response}, @@ -41,6 +41,9 @@ sp_api::decl_runtime_apis! { /// Return the scale encoded consensus state fn consensus_state(id: ConsensusClientId) -> Option>; + /// Return the timestamp this client was last updated in seconds + fn consensus_update_time(id: ConsensusClientId) -> Option; + /// Get Request Leaf Indices fn get_request_leaf_indices(leaf_queries: Vec) -> Option>; diff --git a/src/errors.rs b/src/errors.rs index 2860b703d..24b3d514b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,5 @@ use codec::{Decode, Encode}; -use ismp_rust::{ +use ismp_rs::{ consensus_client::{ConsensusClientId, StateMachineHeight}, error::Error as IsmpError, host::ChainID, @@ -51,10 +51,13 @@ pub enum HandlingError { ImplementationSpecific { msg: Vec, }, + UnbondingPeriodElapsed { + consensus_id: ConsensusClientId, + }, } -impl From for HandlingError { - fn from(value: ismp_rust::error::Error) -> Self { +impl From for HandlingError { + fn from(value: ismp_rs::error::Error) -> Self { match value { IsmpError::DelayNotElapsed { current_time, update_time } => { HandlingError::ChallengePeriodNotElapsed { @@ -93,6 +96,9 @@ impl From for HandlingError { IsmpError::ImplementationSpecific(msg) => { HandlingError::ImplementationSpecific { msg: msg.as_bytes().to_vec() } } + IsmpError::UnbondingPeriodElapsed { consensus_id } => { + HandlingError::UnbondingPeriodElapsed { consensus_id } + } } } } diff --git a/src/events.rs b/src/events.rs index a5294021b..45a1fd986 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,5 +1,9 @@ use crate::{Config, Event as PalletEvent}; -use ismp_rust::{consensus_client::StateMachineId, host::ChainID}; +use alloc::collections::BTreeSet; +use ismp_rs::{ + consensus_client::{ConsensusClientId, StateMachineHeight, StateMachineId}, + host::ChainID, +}; #[derive(codec::Encode, codec::Decode)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] @@ -10,6 +14,12 @@ pub enum Event { latest_height: u64, previous_height: u64, }, + ChallengePeriodStarted { + consensus_client_id: ConsensusClientId, + /// Tuple of previous height and latest height + state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, + }, + Response { /// Chain that this response will be routed to dest_chain: ChainID, diff --git a/src/host.rs b/src/host.rs index bf87a3516..14356209b 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,19 +1,19 @@ use crate::{ - router::Router, Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, - LatestStateMachineHeight, RequestAcks, StateCommitments, + primitives::ETHEREUM_CONSENSUS_CLIENT_ID, + router::{RequestPath, Router}, + Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, + RequestAcks, StateCommitments, }; use alloc::{format, string::ToString}; use core::time::Duration; use frame_support::traits::UnixTime; -use ismp_rust::{ +use ismp_rs::{ consensus_client::{ ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, - ETHEREUM_CONSENSUS_CLIENT_ID, }, error::Error, host::{ChainID, ISMPHost}, - paths::RequestPath, - router::{IISMPRouter, Request}, + router::{ISMPRouter, Request}, }; use sp_runtime::SaturatedConversion; use sp_std::prelude::*; @@ -59,7 +59,7 @@ impl ISMPHost for Host { ConsensusStates::::get(id).ok_or_else(|| Error::ConsensusStateNotFound { id }) } - fn host_timestamp(&self) -> Duration { + fn timestamp(&self) -> Duration { ::now() } @@ -73,17 +73,17 @@ impl ISMPHost for Host { fn request_commitment(&self, req: &Request) -> Result, Error> { let key = RequestPath { - dest_chain: req.dest_chain, - source_chain: req.source_chain, - nonce: req.nonce, + dest_chain: req.dest_chain(), + source_chain: req.source_chain(), + nonce: req.nonce(), } .to_string() .as_bytes() .to_vec(); RequestAcks::::get(key).ok_or_else(|| Error::RequestCommitmentNotFound { - nonce: req.nonce, - source: req.source_chain, - dest: req.dest_chain, + nonce: req.nonce(), + source: req.source_chain(), + dest: req.dest_chain(), }) } @@ -123,14 +123,14 @@ impl ISMPHost for Host { sp_io::hashing::keccak_256(bytes) } - fn delay_period(&self, id: ConsensusClientId) -> Duration { + fn challenge_period(&self, id: ConsensusClientId) -> Duration { match id { id if id == ETHEREUM_CONSENSUS_CLIENT_ID => Duration::from_secs(30 * 60), _ => Duration::from_secs(15 * 60), } } - fn ismp_router(&self) -> Box { + fn ismp_router(&self) -> Box { Box::new(Router::::default()) } diff --git a/src/lib.rs b/src/lib.rs index 8676eca76..c14c2f51f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,8 @@ use crate::{ }; use codec::{Decode, Encode}; use frame_support::{log::debug, RuntimeDebug}; -use ismp_rust::{ - host::{ChainID, ISMPHost}, +use ismp_rs::{ + host::ChainID, messaging::Message, router::{Request, Response}, }; @@ -55,7 +55,7 @@ pub mod pallet { use alloc::collections::BTreeSet; use frame_support::{pallet_prelude::*, traits::UnixTime}; use frame_system::pallet_prelude::*; - use ismp_rust::{ + use ismp_rs::{ consensus_client::{ ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }, @@ -237,35 +237,6 @@ pub mod pallet { let host = Host::::default(); let mut errors: Vec = vec![]; for message in messages { - // Check that delay period is satisfied for consensus client before accepting any - // new update - match &message { - Message::Consensus(msg) => { - // check difference between last - if let Ok(consensus_update_time) = - host.consensus_update_time(msg.consensus_client_id) - { - let elapsed_time = host.host_timestamp() - consensus_update_time; - if host.delay_period(msg.consensus_client_id) > elapsed_time { - debug!(target: "ismp-rust", "Challenge period: Cannot handle consensus message for {:?}", msg.consensus_client_id); - errors.push(HandlingError::ChallengePeriodNotElapsed { - update_time: consensus_update_time.as_secs(), - current_time: host.host_timestamp().as_secs(), - delay_period: Some( - host.delay_period(msg.consensus_client_id).as_secs(), - ), - consensus_client_id: Some(msg.consensus_client_id), - }); - continue - } - } else { - // If we can't find a previous update time for the consensus client we - // don't process it - continue - } - } - _ => {} - } match handle_incoming_message(&host, message) { Ok(MessageResult::ConsensusMessage(res)) => { // Deposit events for previous update result that has passed the challenge @@ -282,6 +253,11 @@ pub mod pallet { } } + Self::deposit_event(Event::::ChallengePeriodStarted { + consensus_client_id: res.consensus_client_id, + state_machines: res.state_updates.clone(), + }); + // Store the new update result that have just entered the challenge period ConsensusUpdateResults::::insert( res.consensus_client_id, @@ -320,6 +296,12 @@ pub mod pallet { latest_height: u64, previous_height: u64, }, + /// Signifies that a client has begun it's challenge period + ChallengePeriodStarted { + consensus_client_id: ConsensusClientId, + state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, + }, + /// Response was process successfully Response { /// Chain that this response will be routed to dest_chain: ChainID, @@ -328,6 +310,7 @@ pub mod pallet { /// Nonce for the request which this response is for request_nonce: u64, }, + /// Request processed successfully Request { /// Chain that this request will be routed to dest_chain: ChainID, diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs index 739fa0392..305ede1fe 100644 --- a/src/mmr/mod.rs +++ b/src/mmr/mod.rs @@ -20,7 +20,7 @@ mod utils; use crate::{host::Host, Config}; use codec::{Decode, Encode}; -use ismp_rust::{ +use ismp_rs::{ host::ISMPHost, router::{Request, Response}, }; diff --git a/src/primitives.rs b/src/primitives.rs index 1fb37e40a..c26632546 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -31,3 +31,5 @@ pub enum Error { InvalidLeafIndex, InvalidBestKnownBlock, } + +pub const ETHEREUM_CONSENSUS_CLIENT_ID: u64 = 100; diff --git a/src/router.rs b/src/router.rs index 9e461acc4..9bd853656 100644 --- a/src/router.rs +++ b/src/router.rs @@ -5,13 +5,29 @@ use crate::{ }; use alloc::{format, string::ToString}; use core::marker::PhantomData; -use ismp_rust::{ +use derive_more::Display; +use ismp_rs::{ error::Error, - host::ISMPHost, - paths::{RequestPath, ResponsePath}, - router::{IISMPRouter, Request, Response}, + host::{ChainID, ISMPHost}, + router::{ISMPRouter, Request, Response}, }; +#[derive(Clone, Debug, Display, PartialEq, Eq)] +#[display(fmt = "requests/{}-{}/{}", "source_chain", "dest_chain", "nonce")] +pub struct RequestPath { + pub dest_chain: ChainID, + pub source_chain: ChainID, + pub nonce: u64, +} + +#[derive(Clone, Debug, Display, PartialEq, Eq)] +#[display(fmt = "responses/{}-{}/{}", "source_chain", "dest_chain", "nonce")] +pub struct ResponsePath { + pub dest_chain: ChainID, + pub source_chain: ChainID, + pub nonce: u64, +} + #[derive(Clone)] pub struct Router(PhantomData); @@ -21,13 +37,13 @@ impl Default for Router { } } -impl IISMPRouter for Router { +impl ISMPRouter for Router { fn dispatch(&self, request: Request) -> Result<(), Error> { let host = Host::::default(); let key = RequestPath { - dest_chain: request.dest_chain, - source_chain: request.source_chain, - nonce: request.nonce, + dest_chain: request.dest_chain(), + source_chain: request.source_chain(), + nonce: request.nonce(), } .to_string() .as_bytes() @@ -37,14 +53,16 @@ impl IISMPRouter for Router { if RequestAcks::::contains_key(key.clone()) { return Err(Error::ImplementationSpecific(format!( "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", - request.nonce, request.source_chain, request.dest_chain + request.nonce(), + request.source_chain(), + request.dest_chain() ))) } - if host.host() != request.dest_chain { + if host.host() != request.dest_chain() { let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = - (request.dest_chain, request.source_chain, request.nonce); + (request.dest_chain(), request.source_chain(), request.nonce()); let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = Pallet::::request_leaf_index_offchain_key(source_chain, dest_chain, nonce); @@ -68,9 +86,9 @@ impl IISMPRouter for Router { fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); let key = ResponsePath { - dest_chain: response.request.source_chain, - source_chain: response.request.dest_chain, - nonce: response.request.nonce, + dest_chain: response.request.source_chain(), + source_chain: response.request.dest_chain(), + nonce: response.request.nonce(), } .to_string() .as_bytes() @@ -80,16 +98,18 @@ impl IISMPRouter for Router { if ResponseAcks::::contains_key(key.clone()) { return Err(Error::ImplementationSpecific(format!( "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", - response.request.nonce, response.request.source_chain, response.request.dest_chain + response.request.nonce(), + response.request.source_chain(), + response.request.dest_chain() ))) } - if host.host() != response.request.source_chain { + if host.host() != response.request.source_chain() { let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = ( - response.request.source_chain, - response.request.dest_chain, - response.request.nonce, + response.request.source_chain(), + response.request.dest_chain(), + response.request.nonce(), ); let mut mmr: Mmr = mmr::Mmr::new(leaves); let offchain_key = From 08ad74d42dcca190c2ba867fc7ec5f632db87585 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:00:01 +0100 Subject: [PATCH 108/182] Fix wasm build and add wasm build check to CI (#24) * fix wasm build * nit * fix workflow --- .github/workflows/test.yml | 21 +++++++++++---------- Cargo.lock | 25 +++---------------------- primitives/Cargo.toml | 1 - primitives/src/lib.rs | 1 - primitives/src/util.rs | 6 ------ 5 files changed, 14 insertions(+), 40 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f5710f738..c1e9accf7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,16 +13,19 @@ jobs: - name: Checkout sources uses: actions/checkout@main - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 + - name: Install toolchain + uses: dtolnay/rust-toolchain@nightly with: - profile: minimal - toolchain: stable - override: true + toolchain: nightly + targets: wasm32-unknown-unknown - name: Run cargo check run: cargo check + - name: Build `no-std` + run: | + cargo +nightly check -p sync-committee-verifier --no-default-features --target=wasm32-unknown-unknown --verbose + test: name: Test Suite runs-on: ubuntu-latest @@ -47,9 +50,7 @@ jobs: - name: Install rust stable toolchain uses: actions-rs/toolchain@v1 with: - profile: minimal - toolchain: stable - override: true + toolchain: nightly - name: Install protoc run: | @@ -71,7 +72,7 @@ jobs: git clone https://github.com/sigp/lighthouse.git cd lighthouse git checkout 38514c07f222ff7783834c48cf5c0a6ee7f346d0 - cargo build -p lighthouse --release + cargo +nightly build -p lighthouse --release mv target/release/lighthouse ../bin - name: Install go @@ -161,4 +162,4 @@ jobs: - name: Run all tests run: | - cargo test -p sync-committee-prover -- --nocapture + cargo +nightly test -p sync-committee-prover -- --nocapture diff --git a/Cargo.lock b/Cargo.lock index 2a58f1bf3..f06426e69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,16 +112,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" dependencies = [ - "int 0.2.11", -] - -[[package]] -name = "base2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" -dependencies = [ - "int 0.3.0", + "int", ] [[package]] @@ -777,15 +768,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "int" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" -dependencies = [ - "num-traits", -] - [[package]] name = "integer-sqrt" version = "0.1.5" @@ -1653,7 +1635,6 @@ dependencies = [ name = "sync-committee-primitives" version = "0.1.0" dependencies = [ - "base2 0.3.1", "ethereum-consensus", "hex-literal", "parity-scale-codec", @@ -1667,7 +1648,7 @@ dependencies = [ "actix-rt", "anyhow", "async-stream", - "base2 0.2.2", + "base2", "env_logger", "ethereum-consensus", "hex", @@ -1685,7 +1666,7 @@ dependencies = [ name = "sync-committee-verifier" version = "0.1.0" dependencies = [ - "base2 0.2.2", + "base2", "ethereum-consensus", "log", "milagro_bls", diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index cef917f0a..18e3e8b72 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -6,7 +6,6 @@ authors = ["Polytope Labs"] [dependencies] -base2 = { version = "0.3.1", default-features = false} ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 620e20b9b..b53869e0d 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -2,7 +2,6 @@ #[warn(unused_imports)] #[warn(unused_variables)] extern crate alloc; -extern crate core; pub mod derived_types; pub mod error; diff --git a/primitives/src/util.rs b/primitives/src/util.rs index 8be6d1474..20701c9e5 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -1,4 +1,3 @@ -use base2::Base2; use ethereum_consensus::{ altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD, configs::mainnet::{ @@ -8,11 +7,6 @@ use ethereum_consensus::{ phase0::mainnet::SLOTS_PER_EPOCH, }; -/// Calculate the subtree index from the ``generalized_index`` -pub fn get_subtree_index(generalized_index: u64) -> u64 { - generalized_index % 2 ^ (generalized_index.floor_log2() as u64) -} - /// Return the sync committe period at the given ``epoch`` pub fn compute_sync_committee_period(epoch: u64) -> u64 { epoch / EPOCHS_PER_SYNC_COMMITTEE_PERIOD From 9e5baa647e6325ad5c9f1638d49cb4f288e60c64 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:32:07 +0100 Subject: [PATCH 109/182] remove base2 package (#25) --- Cargo.lock | 1 - primitives/src/types.rs | 6 ++++++ verifier/Cargo.toml | 1 - verifier/src/lib.rs | 27 +++++++++++++-------------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f06426e69..2d2423b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1666,7 +1666,6 @@ dependencies = [ name = "sync-committee-verifier" version = "0.1.0" dependencies = [ - "base2", "ethereum-consensus", "log", "milagro_bls", diff --git a/primitives/src/types.rs b/primitives/src/types.rs index 643a9de52..d5fc44b7a 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -15,6 +15,12 @@ pub const BLOCK_ROOTS_INDEX: u64 = 37; pub const HISTORICAL_ROOTS_INDEX: u64 = 39; pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; pub const EXECUTION_PAYLOAD_TIMESTAMP_INDEX: u64 = 25; +pub const FINALIZED_ROOT_INDEX_LOG2: u64 = 5; +pub const EXECUTION_PAYLOAD_INDEX_LOG2: u64 = 5; +pub const NEXT_SYNC_COMMITTEE_INDEX_LOG2: u64 = 5; +pub const BLOCK_ROOTS_INDEX_LOG2: u64 = 5; +pub const HISTORICAL_ROOTS_INDEX_LOG2: u64 = 5; + #[cfg(not(feature = "testing"))] pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 357c12a77..7d50acb10 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" authors = ["Polytope Labs"] [dependencies] -base2 = { version="0.2.2", default-features = false } sync-committee-primitives = { path= "../primitives", default-features = false } ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index fff66eb0a..fa40bc9ad 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -7,25 +7,24 @@ pub mod error; use crate::error::Error; use alloc::vec::Vec; -use base2::Base2; use ethereum_consensus::{ bellatrix::{compute_domain, mainnet::SYNC_COMMITTEE_SIZE, Checkpoint}, primitives::Root, signing::compute_signing_root, state_transition::Context, }; - use ssz_rs::{ calculate_merkle_root, calculate_multi_merkle_root, prelude::is_valid_merkle_branch, GeneralizedIndex, Merkleized, Node, }; use sync_committee_primitives::{ types::{ - AncestryProof, BLOCK_ROOTS_INDEX, DOMAIN_SYNC_COMMITTEE, + AncestryProof, BLOCK_ROOTS_INDEX, BLOCK_ROOTS_INDEX_LOG2, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, - EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, - FINALIZED_ROOT_INDEX, GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, - HISTORICAL_ROOTS_INDEX, NEXT_SYNC_COMMITTEE_INDEX, + EXECUTION_PAYLOAD_INDEX_LOG2, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_TIMESTAMP_INDEX, FINALIZED_ROOT_INDEX, FINALIZED_ROOT_INDEX_LOG2, + GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, + HISTORICAL_ROOTS_INDEX_LOG2, NEXT_SYNC_COMMITTEE_INDEX, NEXT_SYNC_COMMITTEE_INDEX_LOG2, }, util::{compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot}, }; @@ -39,10 +38,10 @@ pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { - if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX.floor_log2() as usize && + if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX_LOG2 as usize && update.sync_committee_update.is_some() && update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize + NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize { Err(Error::InvalidUpdate)? } @@ -137,7 +136,7 @@ pub fn verify_sync_committee_attestation( let is_merkle_branch_valid = is_valid_merkle_branch( &finalized_checkpoint.hash_tree_root().map_err(|_| Error::InvalidRoot)?, branch.iter(), - FINALIZED_ROOT_INDEX.floor_log2() as usize, + FINALIZED_ROOT_INDEX_LOG2 as usize, FINALIZED_ROOT_INDEX as usize, &update.attested_header.state_root, ); @@ -185,7 +184,7 @@ pub fn verify_sync_committee_attestation( let is_merkle_branch_valid = is_valid_merkle_branch( &execution_payload_root, execution_payload_branch.iter(), - EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + EXECUTION_PAYLOAD_INDEX_LOG2 as usize, EXECUTION_PAYLOAD_INDEX as usize, &update.finalized_header.state_root, ); @@ -213,7 +212,7 @@ pub fn verify_sync_committee_attestation( .hash_tree_root() .map_err(|_| Error::MerkleizationError)?, next_sync_committee_branch.iter(), - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize, NEXT_SYNC_COMMITTEE_INDEX as usize, &update.attested_header.state_root, ); @@ -247,7 +246,7 @@ pub fn verify_sync_committee_attestation( let is_merkle_branch_valid = is_valid_merkle_branch( &block_roots_root, block_roots_branch_node.iter(), - BLOCK_ROOTS_INDEX.floor_log2() as usize, + BLOCK_ROOTS_INDEX_LOG2 as usize, BLOCK_ROOTS_INDEX as usize, &update.finalized_header.state_root, ); @@ -304,7 +303,7 @@ pub fn verify_sync_committee_attestation( let is_merkle_branch_valid = is_valid_merkle_branch( &historical_roots_root, historical_roots_branch_nodes.iter(), - HISTORICAL_ROOTS_INDEX.floor_log2() as usize, + HISTORICAL_ROOTS_INDEX_LOG2 as usize, HISTORICAL_ROOTS_INDEX as usize, &Node::from_bytes( update @@ -375,7 +374,7 @@ pub fn verify_sync_committee_attestation( let is_merkle_branch_valid = is_valid_merkle_branch( &execution_payload_root, execution_payload_branch.iter(), - EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, + EXECUTION_PAYLOAD_INDEX_LOG2 as usize, EXECUTION_PAYLOAD_INDEX as usize, &Node::from_bytes( ancestor.header.state_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, From 98d80ac0fe402d6f4b1ff36db6d39240539748e5 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 7 Apr 2023 16:49:55 +0100 Subject: [PATCH 110/182] update dependencies (#26) --- Cargo.lock | 339 +++++++++++++++++++++++++++++------------- primitives/Cargo.toml | 4 +- prover/Cargo.toml | 4 +- verifier/Cargo.toml | 4 +- 4 files changed, 241 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d2423b7b..38ed99daa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.8.3" @@ -74,9 +89,9 @@ checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -85,13 +100,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.13", ] [[package]] @@ -100,6 +115,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -229,9 +259,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core2" @@ -244,9 +274,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] @@ -390,13 +420,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -415,19 +445,19 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ + "backtrace", "version_check", ] [[package]] name = "ethereum-consensus" version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?rev=d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f#d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f" +source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=main#48335b5c8074d63553ee4681993e294eba947f88" dependencies = [ "async-stream", "bs58", "enr", "error-chain", - "getrandom", "hashbrown 0.13.2", "hex", "integer-sqrt", @@ -501,36 +531,36 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -540,9 +570,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -550,17 +580,21 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "group" version = "0.12.1" @@ -742,9 +776,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -779,31 +813,31 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -859,9 +893,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "linked-hash-map" @@ -871,9 +905,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "lock_api" @@ -918,6 +952,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.6" @@ -945,6 +988,7 @@ dependencies = [ "serde", "static_assertions", "unsigned-varint", + "url", ] [[package]] @@ -1032,6 +1076,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -1046,9 +1099,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.47" +version = "0.10.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b277f87dacc05a6b709965d1cbafac4649d6ce9f3ce9ceb88508b5666dfec9" +checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33" dependencies = [ "bitflags", "cfg-if", @@ -1061,13 +1114,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.13", ] [[package]] @@ -1078,11 +1131,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.82" +version = "0.9.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" +checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -1133,7 +1185,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -1214,9 +1266,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -1275,11 +1327,20 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -1288,15 +1349,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "reqwest" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64 0.21.0", "bytes", @@ -1350,6 +1411,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -1358,16 +1425,16 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "1aef160324be24d31a62147fae491c14d2204a3865c7ca8c3b0d7f7bcb3ea635" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1430,29 +1497,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.4", + "syn 2.0.13", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa", "ryu", @@ -1574,7 +1641,7 @@ dependencies = [ [[package]] name = "ssz-rs" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f8f1eaef6c65a8600d7#2e28a8800787392045fb3f8f1eaef6c65a8600d7" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#96f9a89ccc56ab2abb4c3a83eaedb415034ada49" dependencies = [ "as-any", "bitvec", @@ -1584,13 +1651,12 @@ dependencies = [ "serde", "sha2 0.9.9", "ssz-rs-derive", - "thiserror", ] [[package]] name = "ssz-rs-derive" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?rev=2e28a8800787392045fb3f8f1eaef6c65a8600d7#2e28a8800787392045fb3f8f1eaef6c65a8600d7" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#96f9a89ccc56ab2abb4c3a83eaedb415034ada49" dependencies = [ "proc-macro2", "quote", @@ -1622,9 +1688,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.4" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", @@ -1693,15 +1759,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1730,7 +1796,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.4", + "syn 2.0.13", ] [[package]] @@ -1750,14 +1816,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -1770,13 +1835,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.13", ] [[package]] @@ -2046,13 +2111,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -2061,7 +2126,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -2070,13 +2144,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -2085,42 +2174,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" @@ -2150,6 +2281,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 18e3e8b72..8fbf3d9a7 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Polytope Labs"] [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false } hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive" diff --git a/prover/Cargo.toml b/prover/Cargo.toml index d6546c55d..e81fafe4e 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f" } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7" } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" } reqwest = {version="0.11.14", features=["json"]} serde = { version = "1.0", features = ["derive"]} serde_json = { version = "1.0.81"} diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 7d50acb10..debc638e3 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Polytope Labs"] [dependencies] sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", rev = "d3fe0ad76613b52cdfbdea7c317ecdc35f271e4f", default-features = false } -ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", rev = "2e28a8800787392045fb3f8f1eaef6c65a8600d7", default-features = false } +ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" , default-features = false } milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } log = { version = "0.4.17", default-features = false } From 4c5e71e236b34af03ea668018273f76513995994 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Mon, 10 Apr 2023 09:52:39 +0100 Subject: [PATCH 111/182] implement ethereum consensus client (#10) --- .DS_Store | Bin 0 -> 6148 bytes Cargo.lock | 404 ++++++++++++++++-- Cargo.toml | 32 +- rpc/Cargo.toml | 2 +- runtime-api/Cargo.toml | 2 +- src/consensus_clients.rs | 1 + .../beacon_consensus_client.rs | 329 ++++++++++++++ src/errors.rs | 16 +- src/host.rs | 13 +- src/lib.rs | 1 + src/mmr/storage.rs | 3 +- 11 files changed, 752 insertions(+), 51 deletions(-) create mode 100644 .DS_Store create mode 100644 src/consensus_clients.rs create mode 100644 src/consensus_clients/beacon_consensus_client.rs diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1621d005cd27551d9a1bd910d647e0545a0fffa2 GIT binary patch literal 6148 zcmeHK%SyvQ6rE|SO({Ya3SADkE!c+?i5*%z?~4Wv*FHG#O z1Acp*C2YYS{QUd(M`4=vdY`;jZ|v+gt)|ts?%XGtyP20Q(}|bdpmix_F+Az7yJ9sS^t$4B zI9jh;*1_S?>Ba0hdWq$mrjrBPO7;wv@D7Su&8wTlk&GX~Q)ZW0gv0 Self::Out { + sp_io::hashing::keccak_256(x).into() + } +} + +#[derive(Debug, Encode, Decode, Clone)] +pub struct ConsensusState { + pub frozen_height: Option, + pub light_client_state: LightClientState, +} + +#[derive(Encode, Decode)] +pub struct Misbehaviour { + pub update_1: LightClientUpdate, + pub update_2: LightClientUpdate, +} + +#[derive(Encode, Decode)] +pub enum BeaconMessage { + ConsensusUpdate(LightClientUpdate), + Misbehaviour(Misbehaviour), +} + +/// Slot index for requests map +const REQ_SLOT: u8 = 1; +/// Slot index for responses map +const RESP_SLOT: u8 = 2; + +const CONTRACT_ADDRESS: [u8; 20] = hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); +#[derive(Encode, Decode, Clone)] +pub struct EvmStateProof { + pub contract_proof: Vec>, + pub storage_proof: Vec>, +} + +/// The ethereum account stored in the global state trie. +#[derive(RlpDecodable)] +struct Account { + _nonce: u64, + _balance: U256, + storage_root: H256, + _code_hash: H256, +} + +/// Unbonding period for ethereum after which unstaked validators can withdraw their funds +const UNBONDING_PERIOD_HOURS: u64 = 27; +/// State machine id used for the ethereum execution layer. +const EXECUTION_PAYLOAD_STATE_ID: u64 = 1; + +#[derive(Default, Clone)] +pub struct BeaconConsensusClient; + +impl ConsensusClient for BeaconConsensusClient { + fn verify_consensus( + &self, + _host: &dyn ISMPHost, + trusted_consensus_state: Vec, + consensus_proof: Vec, + ) -> Result<(Vec, Vec), Error> { + let beacon_message = BeaconMessage::decode(&mut &consensus_proof[..]).map_err(|_| { + Error::ImplementationSpecific("Cannot decode beacon message".to_string()) + })?; + + match beacon_message { + BeaconMessage::ConsensusUpdate(light_client_update) => { + let consensus_state = ConsensusState::decode(&mut &trusted_consensus_state[..]) + .map_err(|_| { + Error::ImplementationSpecific( + "Cannot decode trusted consensus state".to_string(), + ) + })?; + + let no_codec_light_client_state = + consensus_state.light_client_state.try_into().map_err(|_| { + Error::ImplementationSpecific(format!( + "Cannot convert light client state to no codec type", + )) + })?; + + let no_codec_light_client_update = + light_client_update.clone().try_into().map_err(|_| { + Error::ImplementationSpecific(format!( + "Cannot convert light client update to no codec type" + )) + })?; + + let new_light_client_state = + sync_committee_verifier::verify_sync_committee_attestation( + no_codec_light_client_state, + no_codec_light_client_update, + ) + .map_err(|_| Error::ConsensusProofVerificationFailed { + id: ETHEREUM_CONSENSUS_CLIENT_ID, + })?; + + let mut intermediate_states = vec![]; + + let state_root = light_client_update.execution_payload.state_root; + let intermediate_state = construct_intermediate_state( + EXECUTION_PAYLOAD_STATE_ID, + ETHEREUM_CONSENSUS_CLIENT_ID, + light_client_update.execution_payload.block_number, + light_client_update.execution_payload.timestamp, + state_root, + )?; + + intermediate_states.push(intermediate_state); + + let new_consensus_state = ConsensusState { + frozen_height: None, + light_client_state: new_light_client_state.try_into().map_err(|_| { + Error::ImplementationSpecific(format!( + "Cannot convert light client state to codec type" + )) + })?, + }; + + Ok((new_consensus_state.encode(), intermediate_states)) + } + _ => unimplemented!(), + } + } + + fn unbonding_period(&self) -> Duration { + Duration::from_secs(UNBONDING_PERIOD_HOURS * 60 * 60) + } + + fn verify_membership( + &self, + host: &dyn ISMPHost, + item: RequestResponse, + root: StateCommitment, + proof: &Proof, + ) -> Result<(), Error> { + let evm_state_proof = decode_evm_state_proof(proof)?; + let key = req_res_to_key(host, item); + let root = H256::from_slice(&root.state_root[..]); + let contract_root = + get_contract_storage_root(evm_state_proof.contract_proof, root.clone())?; + let _ = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)? + .ok_or_else(|| { + Error::MembershipProofVerificationFailed(format!("There is no DB value")) + })?; + + Ok(()) + } + + fn verify_state_proof( + &self, + _host: &dyn ISMPHost, + _key: Vec, + _root: StateCommitment, + _proof: &Proof, + ) -> Result, Error> { + unimplemented!() + } + + fn verify_non_membership( + &self, + host: &dyn ISMPHost, + item: RequestResponse, + root: StateCommitment, + proof: &Proof, + ) -> Result<(), Error> { + let evm_state_proof = decode_evm_state_proof(proof)?; + + let key = req_res_to_key(host, item); + let root = H256::from_slice(&root.state_root[..]); + let contract_root = get_contract_storage_root(evm_state_proof.contract_proof, root)?; + + let result = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)?; + + if result.is_some() { + return Err(Error::NonMembershipProofVerificationFailed( + "Invalid membership proof".to_string(), + )) + } + + Ok(()) + } + + fn is_frozen(&self, consensus_state: &[u8]) -> Result<(), Error> { + let consensus_state = ConsensusState::decode(&mut &consensus_state[..]).map_err(|_| { + Error::ImplementationSpecific("Cannot decode trusted consensus state".to_string()) + })?; + if consensus_state.frozen_height.is_some() { + Err(Error::FrozenConsensusClient { id: ETHEREUM_CONSENSUS_CLIENT_ID }) + } else { + Ok(()) + } + } +} + +fn construct_intermediate_state( + state_id: u64, + consensus_client_id: u64, + height: u64, + timestamp: u64, + state_root: Vec, +) -> Result { + let state_machine_id = StateMachineId { state_id, consensus_client: consensus_client_id }; + + let state_machine_height = StateMachineHeight { id: state_machine_id, height }; + + let state_commitment = StateCommitment { + timestamp, + ismp_root: [0u8; 32], + state_root: to_bytes_32(state_root)?.into(), + }; + + let intermediate_state = + IntermediateState { height: state_machine_height, commitment: state_commitment }; + + Ok(intermediate_state) +} + +fn decode_evm_state_proof(proof: &Proof) -> Result { + let proof_vec = proof.proof.clone(); + let evm_state_proof = EvmStateProof::decode(&mut &proof_vec[..]).map_err(|_| { + Error::ImplementationSpecific(format!("Cannot decode evm state proof {:?}", proof_vec)) + })?; + + Ok(evm_state_proof) +} + +fn req_res_to_key(host: &dyn ISMPHost, item: RequestResponse) -> Vec { + match item { + RequestResponse::Request(request) => { + let commitment = host.get_request_commitment(&request); + let unhashed = derive_unhashed_map_key(commitment, REQ_SLOT); + host.keccak256(&unhashed).to_vec() + } + RequestResponse::Response(response) => { + let commitment = host.get_response_commitment(&response); + let unhashed = derive_unhashed_map_key(commitment, RESP_SLOT); + host.keccak256(&unhashed).to_vec() + } + } +} + +fn to_bytes_32(vec: Vec) -> Result<[u8; 32], Error> { + if vec.len() != 32 { + return Err(Error::ImplementationSpecific(format!( + "Input vector must have exactly 32 elements {:?}", + vec + ))) + } + + let mut array = [0u8; 32]; + + array.copy_from_slice(&vec); + + Ok(array) +} + +fn get_contract_storage_root( + contract_account_proof: Vec>, + root: H256, +) -> Result { + use rlp::Decodable; + let db = StorageProof::new(contract_account_proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&db, &root).build(); + let contract_address = H160::from_slice(&CONTRACT_ADDRESS[..]); + let key = ethabi::encode(&[Token::Address(contract_address)]); + let result = trie + .get(&key) + .map_err(|_| Error::ImplementationSpecific("Invalid contract account proof".to_string()))? + .ok_or_else(|| { + Error::ImplementationSpecific("Contract account is not present in proof".to_string()) + })?; + + let contract_account = ::decode(&Rlp::new(&result)).map_err(|_| { + Error::ImplementationSpecific(format!( + "Error decoding contract account from key {:?}", + &result + )) + })?; + + Ok(contract_account.storage_root) +} + +fn derive_unhashed_map_key(key: Vec, slot: u8) -> Vec { + ethabi::encode(&[Token::FixedBytes(key), Token::Int(U256::from(slot))]) +} + +fn get_value_from_proof( + key: Vec, + root: H256, + proof: Vec>, +) -> Result, Error> { + let proof_db = StorageProof::new(proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&proof_db, &root).build(); + + trie.get(&key).map_err(|_| Error::ImplementationSpecific(format!("Error reading proof db"))) +} diff --git a/src/errors.rs b/src/errors.rs index 24b3d514b..d9913128c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -54,17 +54,23 @@ pub enum HandlingError { UnbondingPeriodElapsed { consensus_id: ConsensusClientId, }, + MembershipProofVerificationFailed { + msg: Vec, + }, + NonMembershipProofVerificationFailed { + msg: Vec, + }, } impl From for HandlingError { fn from(value: ismp_rs::error::Error) -> Self { match value { - IsmpError::DelayNotElapsed { current_time, update_time } => { + IsmpError::ChallengePeriodNotElapsed { consensus_id, current_time, update_time } => { HandlingError::ChallengePeriodNotElapsed { update_time: update_time.as_secs(), current_time: current_time.as_secs(), delay_period: None, - consensus_client_id: None, + consensus_client_id: Some(consensus_id), } } IsmpError::ConsensusStateNotFound { id } => { @@ -99,6 +105,12 @@ impl From for HandlingError { IsmpError::UnbondingPeriodElapsed { consensus_id } => { HandlingError::UnbondingPeriodElapsed { consensus_id } } + IsmpError::MembershipProofVerificationFailed(msg) => { + HandlingError::MembershipProofVerificationFailed { msg: msg.as_bytes().to_vec() } + } + IsmpError::NonMembershipProofVerificationFailed(msg) => { + HandlingError::NonMembershipProofVerificationFailed { msg: msg.as_bytes().to_vec() } + } } } } diff --git a/src/host.rs b/src/host.rs index 14356209b..1601a913d 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,4 +1,5 @@ use crate::{ + consensus_clients::beacon_consensus_client::BeaconConsensusClient, primitives::ETHEREUM_CONSENSUS_CLIENT_ID, router::{RequestPath, Router}, Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, @@ -115,8 +116,16 @@ impl ISMPHost for Host { Ok(()) } - fn consensus_client(&self, _id: ConsensusClientId) -> Result, Error> { - todo!() + fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { + match id { + id if id == ETHEREUM_CONSENSUS_CLIENT_ID => { + Ok(Box::new(BeaconConsensusClient::default())) + } + _ => Err(Error::ImplementationSpecific(format!( + "No consensus client found for consensus id {}", + id + ))), + } } fn keccak256(&self, bytes: &[u8]) -> [u8; 32] { diff --git a/src/lib.rs b/src/lib.rs index c14c2f51f..01ca417f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ extern crate alloc; +mod consensus_clients; mod errors; pub mod events; pub mod host; diff --git a/src/mmr/storage.rs b/src/mmr/storage.rs index ad9ab44f0..c6d08f3b3 100644 --- a/src/mmr/storage.rs +++ b/src/mmr/storage.rs @@ -18,7 +18,6 @@ use codec::Encode; use frame_support::log::{debug, trace}; use mmr_lib::helper; use sp_core::offchain::StorageKind; -use sp_io::offchain_index; use sp_std::iter::Peekable; #[cfg(not(feature = "std"))] use sp_std::prelude::*; @@ -160,7 +159,7 @@ where pos, key ); // Indexing API is used to store the full node content. - offchain_index::set(&key, &encoded_node); + sp_io::offchain_index::set(&key, &encoded_node); } } From 1bcb1d6c1cf794c2b62973a316c4c15d77ddc995 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Wed, 12 Apr 2023 18:36:40 +0100 Subject: [PATCH 112/182] Optimism support (#19) --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 +- Cargo.toml | 2 +- src/consensus_clients.rs | 1 + .../beacon_consensus_client.rs | 335 +----------------- .../beacon_consensus_client/beacon_client.rs | 190 ++++++++++ .../beacon_consensus_client/optimism.rs | 116 ++++++ .../beacon_consensus_client/presets.rs | 77 ++++ .../state_machine_ids.rs | 3 + .../beacon_consensus_client/types.rs | 64 ++++ .../beacon_consensus_client/utils.rs | 130 +++++++ src/consensus_clients/consensus_client_ids.rs | 1 + src/host.rs | 10 +- src/primitives.rs | 2 - 14 files changed, 597 insertions(+), 337 deletions(-) delete mode 100644 .DS_Store create mode 100644 src/consensus_clients/beacon_consensus_client/beacon_client.rs create mode 100644 src/consensus_clients/beacon_consensus_client/optimism.rs create mode 100644 src/consensus_clients/beacon_consensus_client/presets.rs create mode 100644 src/consensus_clients/beacon_consensus_client/state_machine_ids.rs create mode 100644 src/consensus_clients/beacon_consensus_client/types.rs create mode 100644 src/consensus_clients/beacon_consensus_client/utils.rs create mode 100644 src/consensus_clients/consensus_client_ids.rs diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 1621d005cd27551d9a1bd910d647e0545a0fffa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%SyvQ6rE|SO({Ya3SADkE!c+?i5*%z?~4Wv*FHG#O z1Acp*C2YYS{QUd(M`4=vdY`;jZ|v+gt)|ts?%XGtyP20Q(}|bdpmix_F+Az7yJ9sS^t$4B zI9jh;*1_S?>Ba0hdWq$mrjrBPO7;wv@D7Su&8wTlk&GX~Q)ZW0gv0 Self::Out { - sp_io::hashing::keccak_256(x).into() - } -} - -#[derive(Debug, Encode, Decode, Clone)] -pub struct ConsensusState { - pub frozen_height: Option, - pub light_client_state: LightClientState, -} - -#[derive(Encode, Decode)] -pub struct Misbehaviour { - pub update_1: LightClientUpdate, - pub update_2: LightClientUpdate, -} - -#[derive(Encode, Decode)] -pub enum BeaconMessage { - ConsensusUpdate(LightClientUpdate), - Misbehaviour(Misbehaviour), -} - -/// Slot index for requests map -const REQ_SLOT: u8 = 1; -/// Slot index for responses map -const RESP_SLOT: u8 = 2; - -const CONTRACT_ADDRESS: [u8; 20] = hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); -#[derive(Encode, Decode, Clone)] -pub struct EvmStateProof { - pub contract_proof: Vec>, - pub storage_proof: Vec>, -} - -/// The ethereum account stored in the global state trie. -#[derive(RlpDecodable)] -struct Account { - _nonce: u64, - _balance: U256, - storage_root: H256, - _code_hash: H256, -} - -/// Unbonding period for ethereum after which unstaked validators can withdraw their funds -const UNBONDING_PERIOD_HOURS: u64 = 27; -/// State machine id used for the ethereum execution layer. -const EXECUTION_PAYLOAD_STATE_ID: u64 = 1; - -#[derive(Default, Clone)] -pub struct BeaconConsensusClient; - -impl ConsensusClient for BeaconConsensusClient { - fn verify_consensus( - &self, - _host: &dyn ISMPHost, - trusted_consensus_state: Vec, - consensus_proof: Vec, - ) -> Result<(Vec, Vec), Error> { - let beacon_message = BeaconMessage::decode(&mut &consensus_proof[..]).map_err(|_| { - Error::ImplementationSpecific("Cannot decode beacon message".to_string()) - })?; - - match beacon_message { - BeaconMessage::ConsensusUpdate(light_client_update) => { - let consensus_state = ConsensusState::decode(&mut &trusted_consensus_state[..]) - .map_err(|_| { - Error::ImplementationSpecific( - "Cannot decode trusted consensus state".to_string(), - ) - })?; - - let no_codec_light_client_state = - consensus_state.light_client_state.try_into().map_err(|_| { - Error::ImplementationSpecific(format!( - "Cannot convert light client state to no codec type", - )) - })?; - - let no_codec_light_client_update = - light_client_update.clone().try_into().map_err(|_| { - Error::ImplementationSpecific(format!( - "Cannot convert light client update to no codec type" - )) - })?; - - let new_light_client_state = - sync_committee_verifier::verify_sync_committee_attestation( - no_codec_light_client_state, - no_codec_light_client_update, - ) - .map_err(|_| Error::ConsensusProofVerificationFailed { - id: ETHEREUM_CONSENSUS_CLIENT_ID, - })?; - - let mut intermediate_states = vec![]; - - let state_root = light_client_update.execution_payload.state_root; - let intermediate_state = construct_intermediate_state( - EXECUTION_PAYLOAD_STATE_ID, - ETHEREUM_CONSENSUS_CLIENT_ID, - light_client_update.execution_payload.block_number, - light_client_update.execution_payload.timestamp, - state_root, - )?; - - intermediate_states.push(intermediate_state); - - let new_consensus_state = ConsensusState { - frozen_height: None, - light_client_state: new_light_client_state.try_into().map_err(|_| { - Error::ImplementationSpecific(format!( - "Cannot convert light client state to codec type" - )) - })?, - }; - - Ok((new_consensus_state.encode(), intermediate_states)) - } - _ => unimplemented!(), - } - } - - fn unbonding_period(&self) -> Duration { - Duration::from_secs(UNBONDING_PERIOD_HOURS * 60 * 60) - } - - fn verify_membership( - &self, - host: &dyn ISMPHost, - item: RequestResponse, - root: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let evm_state_proof = decode_evm_state_proof(proof)?; - let key = req_res_to_key(host, item); - let root = H256::from_slice(&root.state_root[..]); - let contract_root = - get_contract_storage_root(evm_state_proof.contract_proof, root.clone())?; - let _ = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)? - .ok_or_else(|| { - Error::MembershipProofVerificationFailed(format!("There is no DB value")) - })?; - - Ok(()) - } - - fn verify_state_proof( - &self, - _host: &dyn ISMPHost, - _key: Vec, - _root: StateCommitment, - _proof: &Proof, - ) -> Result, Error> { - unimplemented!() - } - - fn verify_non_membership( - &self, - host: &dyn ISMPHost, - item: RequestResponse, - root: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let evm_state_proof = decode_evm_state_proof(proof)?; - - let key = req_res_to_key(host, item); - let root = H256::from_slice(&root.state_root[..]); - let contract_root = get_contract_storage_root(evm_state_proof.contract_proof, root)?; - - let result = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)?; - - if result.is_some() { - return Err(Error::NonMembershipProofVerificationFailed( - "Invalid membership proof".to_string(), - )) - } - - Ok(()) - } - - fn is_frozen(&self, consensus_state: &[u8]) -> Result<(), Error> { - let consensus_state = ConsensusState::decode(&mut &consensus_state[..]).map_err(|_| { - Error::ImplementationSpecific("Cannot decode trusted consensus state".to_string()) - })?; - if consensus_state.frozen_height.is_some() { - Err(Error::FrozenConsensusClient { id: ETHEREUM_CONSENSUS_CLIENT_ID }) - } else { - Ok(()) - } - } -} - -fn construct_intermediate_state( - state_id: u64, - consensus_client_id: u64, - height: u64, - timestamp: u64, - state_root: Vec, -) -> Result { - let state_machine_id = StateMachineId { state_id, consensus_client: consensus_client_id }; - - let state_machine_height = StateMachineHeight { id: state_machine_id, height }; - - let state_commitment = StateCommitment { - timestamp, - ismp_root: [0u8; 32], - state_root: to_bytes_32(state_root)?.into(), - }; - - let intermediate_state = - IntermediateState { height: state_machine_height, commitment: state_commitment }; - - Ok(intermediate_state) -} - -fn decode_evm_state_proof(proof: &Proof) -> Result { - let proof_vec = proof.proof.clone(); - let evm_state_proof = EvmStateProof::decode(&mut &proof_vec[..]).map_err(|_| { - Error::ImplementationSpecific(format!("Cannot decode evm state proof {:?}", proof_vec)) - })?; - - Ok(evm_state_proof) -} - -fn req_res_to_key(host: &dyn ISMPHost, item: RequestResponse) -> Vec { - match item { - RequestResponse::Request(request) => { - let commitment = host.get_request_commitment(&request); - let unhashed = derive_unhashed_map_key(commitment, REQ_SLOT); - host.keccak256(&unhashed).to_vec() - } - RequestResponse::Response(response) => { - let commitment = host.get_response_commitment(&response); - let unhashed = derive_unhashed_map_key(commitment, RESP_SLOT); - host.keccak256(&unhashed).to_vec() - } - } -} - -fn to_bytes_32(vec: Vec) -> Result<[u8; 32], Error> { - if vec.len() != 32 { - return Err(Error::ImplementationSpecific(format!( - "Input vector must have exactly 32 elements {:?}", - vec - ))) - } - - let mut array = [0u8; 32]; - - array.copy_from_slice(&vec); - - Ok(array) -} - -fn get_contract_storage_root( - contract_account_proof: Vec>, - root: H256, -) -> Result { - use rlp::Decodable; - let db = StorageProof::new(contract_account_proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&db, &root).build(); - let contract_address = H160::from_slice(&CONTRACT_ADDRESS[..]); - let key = ethabi::encode(&[Token::Address(contract_address)]); - let result = trie - .get(&key) - .map_err(|_| Error::ImplementationSpecific("Invalid contract account proof".to_string()))? - .ok_or_else(|| { - Error::ImplementationSpecific("Contract account is not present in proof".to_string()) - })?; - - let contract_account = ::decode(&Rlp::new(&result)).map_err(|_| { - Error::ImplementationSpecific(format!( - "Error decoding contract account from key {:?}", - &result - )) - })?; - - Ok(contract_account.storage_root) -} - -fn derive_unhashed_map_key(key: Vec, slot: u8) -> Vec { - ethabi::encode(&[Token::FixedBytes(key), Token::Int(U256::from(slot))]) -} - -fn get_value_from_proof( - key: Vec, - root: H256, - proof: Vec>, -) -> Result, Error> { - let proof_db = StorageProof::new(proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&proof_db, &root).build(); - - trie.get(&key).map_err(|_| Error::ImplementationSpecific(format!("Error reading proof db"))) -} +pub mod beacon_client; +pub mod optimism; +mod presets; +pub mod state_machine_ids; +pub mod types; +pub mod utils; diff --git a/src/consensus_clients/beacon_consensus_client/beacon_client.rs b/src/consensus_clients/beacon_consensus_client/beacon_client.rs new file mode 100644 index 000000000..c64808f7f --- /dev/null +++ b/src/consensus_clients/beacon_consensus_client/beacon_client.rs @@ -0,0 +1,190 @@ +use alloc::{format, string::ToString}; +use codec::{Decode, Encode}; +use core::time::Duration; +use ethabi::ethereum_types::H256; + +use crate::consensus_clients::{ + beacon_consensus_client::{ + presets::UNBONDING_PERIOD_HOURS, + state_machine_ids::EXECUTION_LAYER_ID, + types::{BeaconClientUpdate, BeaconMessage, ConsensusState}, + utils::{ + construct_intermediate_state, decode_evm_state_proof, get_contract_storage_root, + get_value_from_proof, req_res_to_key, + }, + }, + consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, +}; +use ismp_rs::{ + consensus_client::{ConsensusClient, IntermediateState, StateCommitment}, + error::Error, + host::ISMPHost, + messaging::Proof, + router::RequestResponse, +}; + +use crate::consensus_clients::beacon_consensus_client::{ + optimism::verify_optimism_payload, presets::ismp_contract_address, +}; +use sp_std::prelude::*; + +#[derive(Default, Clone)] +pub struct BeaconConsensusClient; + +impl ConsensusClient for BeaconConsensusClient { + fn verify_consensus( + &self, + _host: &dyn ISMPHost, + trusted_consensus_state: Vec, + consensus_proof: Vec, + ) -> Result<(Vec, Vec), Error> { + let beacon_message = BeaconMessage::decode(&mut &consensus_proof[..]).map_err(|_| { + Error::ImplementationSpecific("Cannot decode beacon message".to_string()) + })?; + + match beacon_message { + BeaconMessage::ConsensusUpdate(BeaconClientUpdate { + optimism_payload, + consensus_update, + }) => { + let consensus_state = ConsensusState::decode(&mut &trusted_consensus_state[..]) + .map_err(|_| { + Error::ImplementationSpecific( + "Cannot decode trusted consensus state".to_string(), + ) + })?; + + let no_codec_light_client_state = + consensus_state.light_client_state.try_into().map_err(|_| { + Error::ImplementationSpecific(format!( + "Cannot convert light client state to no codec type", + )) + })?; + + let no_codec_light_client_update = + consensus_update.clone().try_into().map_err(|_| { + Error::ImplementationSpecific(format!( + "Cannot convert light client update to no codec type" + )) + })?; + + let new_light_client_state = + sync_committee_verifier::verify_sync_committee_attestation( + no_codec_light_client_state, + no_codec_light_client_update, + ) + .map_err(|_| Error::ConsensusProofVerificationFailed { + id: ETHEREUM_CONSENSUS_CLIENT_ID, + })?; + + let mut intermediate_states = vec![]; + + let state_root = consensus_update.execution_payload.state_root; + let intermediate_state = construct_intermediate_state( + EXECUTION_LAYER_ID, + ETHEREUM_CONSENSUS_CLIENT_ID, + consensus_update.execution_payload.block_number, + consensus_update.execution_payload.timestamp, + &state_root, + )?; + + intermediate_states.push(intermediate_state); + + if let Some(optimism_payload) = optimism_payload { + let state = verify_optimism_payload(optimism_payload, &state_root)?; + intermediate_states.push(state) + } + + let new_consensus_state = ConsensusState { + frozen_height: None, + light_client_state: new_light_client_state.try_into().map_err(|_| { + Error::ImplementationSpecific(format!( + "Cannot convert light client state to codec type" + )) + })?, + }; + + Ok((new_consensus_state.encode(), intermediate_states)) + } + _ => unimplemented!(), + } + } + + fn unbonding_period(&self) -> Duration { + Duration::from_secs(UNBONDING_PERIOD_HOURS * 60 * 60) + } + + fn verify_membership( + &self, + host: &dyn ISMPHost, + item: RequestResponse, + root: StateCommitment, + proof: &Proof, + ) -> Result<(), Error> { + let evm_state_proof = decode_evm_state_proof(proof)?; + let contract_address = ismp_contract_address(&item).ok_or_else(|| { + Error::ImplementationSpecific("Ismp contract address not found".to_string()) + })?; + let key = req_res_to_key(host, item); + let root = H256::from_slice(&root.state_root[..]); + let contract_root = get_contract_storage_root( + evm_state_proof.contract_proof, + &contract_address, + root.clone(), + )?; + let _ = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)? + .ok_or_else(|| { + Error::MembershipProofVerificationFailed(format!("There is no DB value")) + })?; + + Ok(()) + } + + fn verify_state_proof( + &self, + _host: &dyn ISMPHost, + _key: Vec, + _root: StateCommitment, + _proof: &Proof, + ) -> Result, Error> { + unimplemented!() + } + + fn verify_non_membership( + &self, + host: &dyn ISMPHost, + item: RequestResponse, + root: StateCommitment, + proof: &Proof, + ) -> Result<(), Error> { + let evm_state_proof = decode_evm_state_proof(proof)?; + let contract_address = ismp_contract_address(&item).ok_or_else(|| { + Error::ImplementationSpecific("Ismp contract address not found".to_string()) + })?; + let key = req_res_to_key(host, item); + let root = H256::from_slice(&root.state_root[..]); + let contract_root = + get_contract_storage_root(evm_state_proof.contract_proof, &contract_address, root)?; + + let result = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)?; + + if result.is_some() { + return Err(Error::NonMembershipProofVerificationFailed( + "Invalid membership proof".to_string(), + )) + } + + Ok(()) + } + + fn is_frozen(&self, consensus_state: &[u8]) -> Result<(), Error> { + let consensus_state = ConsensusState::decode(&mut &consensus_state[..]).map_err(|_| { + Error::ImplementationSpecific("Cannot decode trusted consensus state".to_string()) + })?; + if consensus_state.frozen_height.is_some() { + Err(Error::FrozenConsensusClient { id: ETHEREUM_CONSENSUS_CLIENT_ID }) + } else { + Ok(()) + } + } +} diff --git a/src/consensus_clients/beacon_consensus_client/optimism.rs b/src/consensus_clients/beacon_consensus_client/optimism.rs new file mode 100644 index 000000000..7c372db77 --- /dev/null +++ b/src/consensus_clients/beacon_consensus_client/optimism.rs @@ -0,0 +1,116 @@ +use crate::consensus_clients::{ + beacon_consensus_client::{ + presets::L2_ORACLE_ADDRESS, + state_machine_ids::OPTIMISM_ID, + utils::{ + derive_array_item_key, get_contract_storage_root, get_value_from_proof, to_bytes_32, + }, + }, + consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, +}; +use alloc::string::ToString; +use ethabi::ethereum_types::{H256, U128}; +use ismp_rs::{ + consensus_client::{IntermediateState, StateCommitment, StateMachineHeight, StateMachineId}, + error::Error, +}; +use sp_std::prelude::*; + +#[derive(codec::Encode, codec::Decode)] +pub struct OptimismPayloadProof { + /// Actual state root of the optimism execution layer + pub state_root: [u8; 32], + /// Storage root hash of the optimism withdrawal contracts + pub withdrawal_storage_root: [u8; 32], + /// Optimism Block hash at which the values aboved were fetched + pub l2_block_hash: [u8; 32], + /// L2Oracle contract version + pub version: [u8; 32], + /// Membership Proof for the L2Oracle contract account in the ethereum world trie + pub l2_oracle_proof: Vec>, + /// Membership proof for output root in l2Outputs array + pub output_root_proof: Vec>, + /// Membership proof Timestamp and block number in the l2Outputs array + pub multi_proof: Vec>, + /// Index of the output root that needs to be proved in the l2Outputs array + pub output_root_index: u64, + /// Block number + pub block_number: u64, + /// Timestamp + pub timestamp: u64, +} + +/// Slot for the l2Outputs array in the L2Oracle contract +pub(super) const L2_OUTPUTS_SLOT: u8 = 3; + +pub(super) fn verify_optimism_payload( + payload: OptimismPayloadProof, + root: &[u8], +) -> Result { + let root = to_bytes_32(root)?; + let root = H256::from_slice(&root[..]); + let storage_root = + get_contract_storage_root(payload.l2_oracle_proof, &L2_ORACLE_ADDRESS, root)?; + + let mut buf = Vec::with_capacity(128); + buf.extend_from_slice(&payload.version[..]); + buf.extend_from_slice(&payload.state_root[..]); + buf.extend_from_slice(&payload.withdrawal_storage_root[..]); + buf.extend_from_slice(&payload.l2_block_hash[..]); + + let output_root = sp_io::hashing::keccak_256(&buf); + + let output_root_key = derive_array_item_key(L2_OUTPUTS_SLOT, payload.output_root_index); + + let proof_value = + get_value_from_proof(output_root_key, storage_root, payload.output_root_proof)? + .ok_or_else(|| { + Error::MembershipProofVerificationFailed("Value not found in proof".to_string()) + })?; + + if &proof_value != &output_root[..] { + return Err(Error::MembershipProofVerificationFailed( + "Invalid optimism output root proof".to_string(), + )) + } + + // verify timestamp and block number + let timestamp_block_number_key = + derive_array_item_key(L2_OUTPUTS_SLOT, payload.output_root_index + 1); + let block_and_timestamp = + get_value_from_proof(timestamp_block_number_key, storage_root, payload.multi_proof)? + .ok_or_else(|| { + Error::MembershipProofVerificationFailed("Value not found in proof".to_string()) + })?; + + let mut timestamp = Vec::with_capacity(16); + U128::from(payload.timestamp).to_big_endian(&mut timestamp); + + let mut block_number = Vec::with_capacity(16); + U128::from(payload.block_number).to_big_endian(&mut block_number); + + let mut concat = Vec::with_capacity(32); + concat.extend_from_slice(×tamp); + concat.extend_from_slice(&block_number); + + if block_and_timestamp != concat { + return Err(Error::MembershipProofVerificationFailed( + "Invalid optimism block and timestamp proof".to_string(), + )) + } + + Ok(IntermediateState { + height: StateMachineHeight { + id: StateMachineId { + state_id: OPTIMISM_ID, + consensus_client: ETHEREUM_CONSENSUS_CLIENT_ID, + }, + height: payload.block_number, + }, + commitment: StateCommitment { + timestamp: payload.timestamp, + ismp_root: [0u8; 32], + state_root: payload.state_root, + }, + }) +} diff --git a/src/consensus_clients/beacon_consensus_client/presets.rs b/src/consensus_clients/beacon_consensus_client/presets.rs new file mode 100644 index 000000000..fb7d50163 --- /dev/null +++ b/src/consensus_clients/beacon_consensus_client/presets.rs @@ -0,0 +1,77 @@ +use ismp_rs::{host::ChainID, router::RequestResponse}; +#[cfg(not(feature = "testnet"))] +pub use mainnet::*; +#[cfg(feature = "testnet")] +pub use testnet::*; + +#[cfg(not(feature = "testnet"))] +mod mainnet { + use hex_literal::hex; + + pub const L2_ORACLE_ADDRESS: [u8; 20] = hex!("47bBB9054823f27B9B6A71F5cb0eBc785692FF2E"); + /// Contract address on optimism + pub const ISMP_CONTRACT_ADDRESS_OPTIMISM: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on gnosis + pub const ISMP_CONTRACT_ADDRESS_GNOSIS: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on arbitrum + pub const ISMP_CONTRACT_ADDRESS_ARB: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on base + pub const ISMP_CONTRACT_ADDRESS_BASE: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on moonbeam + pub const ISMP_CONTRACT_ADDRESS_MOONBEAM: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on ethereum + pub const ISMP_CONTRACT_ADDRESS_ETHEREUM: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Unbonding period for ethereum after which unstaked validators can withdraw their funds + /// https://ethos.dev/beacon-chain + pub const UNBONDING_PERIOD_HOURS: u64 = 27; +} + +#[cfg(feature = "testnet")] +mod testnet { + use hex_literal::hex; + + pub const L2_ORACLE_ADDRESS: [u8; 20] = hex!("47bBB9054823f27B9B6A71F5cb0eBc785692FF2E"); + /// Contract address on optimism + pub const ISMP_CONTRACT_ADDRESS_OPTIMISM: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on gnosis + pub const ISMP_CONTRACT_ADDRESS_GNOSIS: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on arbitrum + pub const ISMP_CONTRACT_ADDRESS_ARB: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on base + pub const ISMP_CONTRACT_ADDRESS_BASE: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on moonbeam + pub const ISMP_CONTRACT_ADDRESS_MOONBEAM: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Contract address on ethereum + pub const ISMP_CONTRACT_ADDRESS_ETHEREUM: [u8; 20] = + hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); + /// Unbonding period for ethereum after which unstaked validators can withdraw their funds + pub const UNBONDING_PERIOD_HOURS: u64 = 27; +} + +pub fn ismp_contract_address(item: &RequestResponse) -> Option<[u8; 20]> { + let chain_id = match item { + RequestResponse::Request(req) => req.source_chain(), + RequestResponse::Response(res) => res.request.dest_chain(), + }; + + match chain_id { + ChainID::ETHEREUM => Some(ISMP_CONTRACT_ADDRESS_ETHEREUM), + ChainID::GNOSIS => Some(ISMP_CONTRACT_ADDRESS_GNOSIS), + ChainID::ARBITRUM => Some(ISMP_CONTRACT_ADDRESS_ARB), + ChainID::OPTIMISM => Some(ISMP_CONTRACT_ADDRESS_OPTIMISM), + ChainID::BASE => Some(ISMP_CONTRACT_ADDRESS_BASE), + ChainID::MOONBEAM => Some(ISMP_CONTRACT_ADDRESS_MOONBEAM), + _ => None, + } +} diff --git a/src/consensus_clients/beacon_consensus_client/state_machine_ids.rs b/src/consensus_clients/beacon_consensus_client/state_machine_ids.rs new file mode 100644 index 000000000..e8dfb38d6 --- /dev/null +++ b/src/consensus_clients/beacon_consensus_client/state_machine_ids.rs @@ -0,0 +1,3 @@ +/// State machine id used for the ethereum execution layer. +pub const EXECUTION_LAYER_ID: u64 = 1; +pub const OPTIMISM_ID: u64 = 2; diff --git a/src/consensus_clients/beacon_consensus_client/types.rs b/src/consensus_clients/beacon_consensus_client/types.rs new file mode 100644 index 000000000..c9809b6b4 --- /dev/null +++ b/src/consensus_clients/beacon_consensus_client/types.rs @@ -0,0 +1,64 @@ +use crate::consensus_clients::beacon_consensus_client::optimism::OptimismPayloadProof; +use codec::{Decode, Encode}; +use ethabi::ethereum_types::{H256, U256}; +use hash256_std_hasher::Hash256StdHasher; +use hash_db::Hasher; +use rlp_derive::RlpDecodable; +use sp_std::prelude::*; +use sync_committee_primitives::derived_types::{LightClientState, LightClientUpdate}; + +pub struct KeccakHasher; + +impl Hasher for KeccakHasher { + type Out = H256; + type StdHasher = Hash256StdHasher; + const LENGTH: usize = 32; + + fn hash(x: &[u8]) -> Self::Out { + sp_io::hashing::keccak_256(x).into() + } +} + +#[derive(Debug, Encode, Decode, Clone)] +pub struct ConsensusState { + pub frozen_height: Option, + pub light_client_state: LightClientState, +} + +#[derive(Encode, Decode)] +pub struct Misbehaviour { + pub update_1: LightClientUpdate, + pub update_2: LightClientUpdate, +} + +#[derive(Encode, Decode)] +pub struct BeaconClientUpdate { + pub consensus_update: LightClientUpdate, + pub optimism_payload: Option, +} + +#[derive(Encode, Decode)] +pub enum BeaconMessage { + ConsensusUpdate(BeaconClientUpdate), + Misbehaviour(Misbehaviour), +} + +/// Slot index for requests map +pub const REQ_SLOT: u8 = 1; +/// Slot index for responses map +pub const RESP_SLOT: u8 = 2; + +#[derive(Encode, Decode, Clone)] +pub struct EvmStateProof { + pub contract_proof: Vec>, + pub storage_proof: Vec>, +} + +/// The ethereum account stored in the global state trie. +#[derive(RlpDecodable)] +pub(super) struct Account { + _nonce: u64, + _balance: U256, + pub storage_root: H256, + _code_hash: H256, +} diff --git a/src/consensus_clients/beacon_consensus_client/utils.rs b/src/consensus_clients/beacon_consensus_client/utils.rs new file mode 100644 index 000000000..bf6ad7348 --- /dev/null +++ b/src/consensus_clients/beacon_consensus_client/utils.rs @@ -0,0 +1,130 @@ +use crate::consensus_clients::beacon_consensus_client::types::{ + Account, EvmStateProof, KeccakHasher, REQ_SLOT, RESP_SLOT, +}; +use alloc::{format, string::ToString}; +use codec::Decode; +use ethabi::{ + ethereum_types::{H160, H256, U256}, + Token, +}; +use ismp_rs::{ + consensus_client::{IntermediateState, StateCommitment, StateMachineHeight, StateMachineId}, + error::Error, + host::ISMPHost, + messaging::Proof, + router::RequestResponse, +}; +use patricia_merkle_trie::{EIP1186Layout, StorageProof}; +use rlp::Rlp; +use sp_std::prelude::*; +use trie_db::{DBValue, Trie, TrieDBBuilder}; + +pub fn construct_intermediate_state( + state_id: u64, + consensus_client_id: u64, + height: u64, + timestamp: u64, + state_root: &[u8], +) -> Result { + let state_machine_id = StateMachineId { state_id, consensus_client: consensus_client_id }; + + let state_machine_height = StateMachineHeight { id: state_machine_id, height }; + + let state_commitment = StateCommitment { + timestamp, + ismp_root: [0u8; 32], + state_root: to_bytes_32(&state_root[..])?.into(), + }; + + let intermediate_state = + IntermediateState { height: state_machine_height, commitment: state_commitment }; + + Ok(intermediate_state) +} + +pub(super) fn decode_evm_state_proof(proof: &Proof) -> Result { + let proof_vec = proof.proof.clone(); + let evm_state_proof = EvmStateProof::decode(&mut &proof_vec[..]).map_err(|_| { + Error::ImplementationSpecific(format!("Cannot decode evm state proof {:?}", proof_vec)) + })?; + + Ok(evm_state_proof) +} + +pub fn req_res_to_key(host: &dyn ISMPHost, item: RequestResponse) -> Vec { + match item { + RequestResponse::Request(request) => { + let commitment = host.get_request_commitment(&request); + let unhashed = derive_unhashed_map_key(commitment, REQ_SLOT); + host.keccak256(&unhashed).to_vec() + } + RequestResponse::Response(response) => { + let commitment = host.get_response_commitment(&response); + let unhashed = derive_unhashed_map_key(commitment, RESP_SLOT); + host.keccak256(&unhashed).to_vec() + } + } +} + +pub(super) fn to_bytes_32(bytes: &[u8]) -> Result<[u8; 32], Error> { + if bytes.len() != 32 { + return Err(Error::ImplementationSpecific(format!( + "Input vector must have exactly 32 elements {:?}", + bytes + ))) + } + + let mut array = [0u8; 32]; + + array.copy_from_slice(&bytes); + + Ok(array) +} + +pub(super) fn get_contract_storage_root( + contract_account_proof: Vec>, + contract_address: &[u8; 20], + root: H256, +) -> Result { + use rlp::Decodable; + let db = StorageProof::new(contract_account_proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&db, &root).build(); + let contract_address = H160::from_slice(contract_address); + let key = ethabi::encode(&[Token::Address(contract_address)]); + let result = trie + .get(&key) + .map_err(|_| Error::ImplementationSpecific("Invalid contract account proof".to_string()))? + .ok_or_else(|| { + Error::ImplementationSpecific("Contract account is not present in proof".to_string()) + })?; + + let contract_account = ::decode(&Rlp::new(&result)).map_err(|_| { + Error::ImplementationSpecific(format!( + "Error decoding contract account from key {:?}", + &result + )) + })?; + + Ok(contract_account.storage_root) +} + +pub(super) fn derive_unhashed_map_key(key: Vec, slot: u8) -> Vec { + ethabi::encode(&[Token::FixedBytes(key), Token::Int(U256::from(slot))]) +} + +pub(super) fn derive_array_item_key(slot: u8, index: u64) -> Vec { + let slot_hash = sp_io::hashing::keccak_256(ðabi::encode(&[Token::Uint(U256::from(slot))])); + let slot_index = U256::from_big_endian(&slot_hash[..]) + U256::from(index); + <[u8; 32]>::from(slot_index).to_vec() +} + +pub(super) fn get_value_from_proof( + key: Vec, + root: H256, + proof: Vec>, +) -> Result, Error> { + let proof_db = StorageProof::new(proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&proof_db, &root).build(); + + trie.get(&key).map_err(|_| Error::ImplementationSpecific(format!("Error reading proof db"))) +} diff --git a/src/consensus_clients/consensus_client_ids.rs b/src/consensus_clients/consensus_client_ids.rs new file mode 100644 index 000000000..4ebba4960 --- /dev/null +++ b/src/consensus_clients/consensus_client_ids.rs @@ -0,0 +1 @@ +pub const ETHEREUM_CONSENSUS_CLIENT_ID: u64 = 1; diff --git a/src/host.rs b/src/host.rs index 1601a913d..b052deeaf 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,6 +1,8 @@ use crate::{ - consensus_clients::beacon_consensus_client::BeaconConsensusClient, - primitives::ETHEREUM_CONSENSUS_CLIENT_ID, + consensus_clients::{ + beacon_consensus_client::beacon_client::BeaconConsensusClient, + consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, + }, router::{RequestPath, Router}, Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, @@ -50,7 +52,7 @@ impl ISMPHost for Host { fn consensus_update_time(&self, id: ConsensusClientId) -> Result { ConsensusClientUpdateTime::::get(id) - .map(|timestamp| Duration::from_nanos(timestamp)) + .map(|timestamp| Duration::from_secs(timestamp)) .ok_or_else(|| { Error::ImplementationSpecific(format!("Update time not found for {:?}", id)) }) @@ -98,7 +100,7 @@ impl ISMPHost for Host { id: ConsensusClientId, timestamp: Duration, ) -> Result<(), Error> { - ConsensusClientUpdateTime::::insert(id, timestamp.as_nanos().saturated_into::()); + ConsensusClientUpdateTime::::insert(id, timestamp.as_secs().saturated_into::()); Ok(()) } diff --git a/src/primitives.rs b/src/primitives.rs index c26632546..1fb37e40a 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -31,5 +31,3 @@ pub enum Error { InvalidLeafIndex, InvalidBestKnownBlock, } - -pub const ETHEREUM_CONSENSUS_CLIENT_ID: u64 = 100; From ae0aaa24ff542ad38f192ca1d071f196860fa630 Mon Sep 17 00:00:00 2001 From: omadoyeabraham Date: Thu, 13 Apr 2023 10:19:53 +0100 Subject: [PATCH 113/182] Added create consensus client extrinsic (#12) --- Cargo.lock | 4 ++-- src/errors.rs | 6 ++++++ src/lib.rs | 60 +++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe879c4cc..b981c6a2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1646,7 +1646,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#b5f327df4fd16046649417998f1c4a049c8aa873" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#ebcd551ede6c49e862409555237ad697934366da" dependencies = [ "derive_more", "parity-scale-codec", @@ -4136,7 +4136,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/src/errors.rs b/src/errors.rs index d9913128c..c4c0c07bf 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -60,6 +60,9 @@ pub enum HandlingError { NonMembershipProofVerificationFailed { msg: Vec, }, + CannotCreateAlreadyExistingConsensusClient { + id: ConsensusClientId, + }, } impl From for HandlingError { @@ -111,6 +114,9 @@ impl From for HandlingError { IsmpError::NonMembershipProofVerificationFailed(msg) => { HandlingError::NonMembershipProofVerificationFailed { msg: msg.as_bytes().to_vec() } } + IsmpError::CannotCreateAlreadyExistingConsensusClient { id } => { + HandlingError::CannotCreateAlreadyExistingConsensusClient { id } + } } } } diff --git a/src/lib.rs b/src/lib.rs index 01ca417f4..54bc0cc93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,6 @@ use codec::{Decode, Encode}; use frame_support::{log::debug, RuntimeDebug}; use ismp_rs::{ host::ChainID, - messaging::Message, router::{Request, Response}, }; use sp_core::offchain::StorageKind; @@ -46,6 +45,7 @@ use sp_std::prelude::*; // `construct_runtime`. #[frame_support::pallet] pub mod pallet { + // Import various types used to declare pallet in scope. use super::*; use crate::{ @@ -53,7 +53,7 @@ pub mod pallet { mmr::{LeafIndex, Mmr, NodeIndex}, primitives::ISMP_ID, }; - use alloc::collections::BTreeSet; + use alloc::{collections::BTreeSet, string::ToString}; use frame_support::{pallet_prelude::*, traits::UnixTime}; use frame_system::pallet_prelude::*; use ismp_rs::{ @@ -62,6 +62,7 @@ pub mod pallet { }, handlers::{handle_incoming_message, MessageResult}, host::ChainID, + messaging::Message, }; use sp_runtime::traits; @@ -84,6 +85,7 @@ pub mod pallet { /// Each node is stored in the Off-chain DB under key derived from the /// [`Self::INDEXING_PREFIX`] and its in-tree index (MMR position). const INDEXING_PREFIX: &'static [u8]; + type AdminOrigin: EnsureOrigin; /// A hasher type for MMR. /// @@ -237,11 +239,19 @@ pub mod pallet { // Define a host let host = Host::::default(); let mut errors: Vec = vec![]; + for message in messages { + if matches!(message, Message::CreateConsensusClient(_)) { + errors.push(HandlingError::ImplementationSpecific { + msg: "Invalid message for extrinsic".to_string().as_bytes().to_vec(), + }); + continue + } + match handle_incoming_message(&host, message) { Ok(MessageResult::ConsensusMessage(res)) => { - // Deposit events for previous update result that has passed the challenge - // period + // Deposit events for previous update result that has passed the + // challenge period if let Some(pending_updates) = ConsensusUpdateResults::::get(res.consensus_client_id) { @@ -259,7 +269,8 @@ pub mod pallet { state_machines: res.state_updates.clone(), }); - // Store the new update result that have just entered the challenge period + // Store the new update result that have just entered the challenge + // period ConsensusUpdateResults::::insert( res.consensus_client_id, res.state_updates, @@ -281,6 +292,33 @@ pub mod pallet { Ok(()) } + + /// Create consensus clients + #[pallet::weight(0)] + #[pallet::call_index(1)] + pub fn create_consensus_client(origin: OriginFor, message: Message) -> DispatchResult { + ::AdminOrigin::ensure_origin(origin)?; + + let host = Host::::default(); + + if !matches!(message, Message::CreateConsensusClient(_)) { + Err(Error::::InvalidMessage)? + } + + let result = handle_incoming_message(&host, message) + .map_err(|_| Error::::ConsensusClientCreationFailed)?; + + let result = match result { + MessageResult::ConsensusClientCreated(res) => res, + _ => Err(Error::::InvalidMessage)?, + }; + + Self::deposit_event(Event::::ConsensusClientCreated { + consensus_client_id: result.consensus_client_id, + }); + + Ok(()) + } } /// Events are a simple means of reporting specific conditions and @@ -302,6 +340,8 @@ pub mod pallet { consensus_client_id: ConsensusClientId, state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, }, + /// Indicates that a consensus client has been created + ConsensusClientCreated { consensus_client_id: ConsensusClientId }, /// Response was process successfully Response { /// Chain that this response will be routed to @@ -320,13 +360,15 @@ pub mod pallet { /// Request nonce request_nonce: u64, }, - HandlingErrors { - errors: Vec, - }, + /// Some errors handling some ismp messages + HandlingErrors { errors: Vec }, } #[pallet::error] - pub enum Error {} + pub enum Error { + InvalidMessage, + ConsensusClientCreationFailed, + } } impl Pallet { From 7305ecc36178a7cd15f48674859c02bde2e979c4 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 14 Apr 2023 13:05:53 +0100 Subject: [PATCH 114/182] Request and Response commitments as keys for acknowledgement (#20) * use request and response commitments as keys for acknowledgement * fmt --- src/host.rs | 26 ++++++++++++-------------- src/lib.rs | 11 +++++++---- src/router.rs | 47 +++++++++++------------------------------------ 3 files changed, 30 insertions(+), 54 deletions(-) diff --git a/src/host.rs b/src/host.rs index b052deeaf..d7c16a8cf 100644 --- a/src/host.rs +++ b/src/host.rs @@ -3,7 +3,7 @@ use crate::{ beacon_consensus_client::beacon_client::BeaconConsensusClient, consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, }, - router::{RequestPath, Router}, + router::Router, Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, }; @@ -75,19 +75,17 @@ impl ISMPHost for Host { } fn request_commitment(&self, req: &Request) -> Result, Error> { - let key = RequestPath { - dest_chain: req.dest_chain(), - source_chain: req.source_chain(), - nonce: req.nonce(), - } - .to_string() - .as_bytes() - .to_vec(); - RequestAcks::::get(key).ok_or_else(|| Error::RequestCommitmentNotFound { - nonce: req.nonce(), - source: req.source_chain(), - dest: req.dest_chain(), - }) + let commitment = self.get_request_commitment(req); + + let _ = RequestAcks::::get(commitment.clone()).ok_or_else(|| { + Error::RequestCommitmentNotFound { + nonce: req.nonce(), + source: req.source_chain(), + dest: req.dest_chain(), + } + })?; + + Ok(commitment) } fn store_consensus_state(&self, id: ConsensusClientId, state: Vec) -> Result<(), Error> { diff --git a/src/lib.rs b/src/lib.rs index 54bc0cc93..c40b70188 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ pub mod pallet { errors::HandlingError, mmr::{LeafIndex, Mmr, NodeIndex}, primitives::ISMP_ID, + router::Receipt, }; use alloc::{collections::BTreeSet, string::ToString}; use frame_support::{pallet_prelude::*, traits::UnixTime}; @@ -173,14 +174,16 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn request_acks)] /// Acknowledgements for receipt of requests - /// No hashing, just insert raw key in storage - pub type RequestAcks = StorageMap<_, Identity, Vec, Vec, OptionQuery>; + /// The key is the request commitment + pub type RequestAcks = + StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; #[pallet::storage] #[pallet::getter(fn response_acks)] /// Acknowledgements for receipt of responses - /// No hashing, just insert raw key in storage - pub type ResponseAcks = StorageMap<_, Identity, Vec, Vec, OptionQuery>; + /// The key is the response commitment + pub type ResponseAcks = + StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; #[pallet::storage] #[pallet::getter(fn consensus_update_results)] diff --git a/src/router.rs b/src/router.rs index 9bd853656..1856fba8b 100644 --- a/src/router.rs +++ b/src/router.rs @@ -4,28 +4,17 @@ use crate::{ Config, Event, Pallet, RequestAcks, ResponseAcks, }; use alloc::{format, string::ToString}; +use codec::{Decode, Encode}; use core::marker::PhantomData; -use derive_more::Display; use ismp_rs::{ error::Error, - host::{ChainID, ISMPHost}, + host::ISMPHost, router::{ISMPRouter, Request, Response}, }; -#[derive(Clone, Debug, Display, PartialEq, Eq)] -#[display(fmt = "requests/{}-{}/{}", "source_chain", "dest_chain", "nonce")] -pub struct RequestPath { - pub dest_chain: ChainID, - pub source_chain: ChainID, - pub nonce: u64, -} - -#[derive(Clone, Debug, Display, PartialEq, Eq)] -#[display(fmt = "responses/{}-{}/{}", "source_chain", "dest_chain", "nonce")] -pub struct ResponsePath { - pub dest_chain: ChainID, - pub source_chain: ChainID, - pub nonce: u64, +#[derive(Encode, Decode, scale_info::TypeInfo)] +pub enum Receipt { + Ok, } #[derive(Clone)] @@ -40,17 +29,10 @@ impl Default for Router { impl ISMPRouter for Router { fn dispatch(&self, request: Request) -> Result<(), Error> { let host = Host::::default(); - let key = RequestPath { - dest_chain: request.dest_chain(), - source_chain: request.source_chain(), - nonce: request.nonce(), - } - .to_string() - .as_bytes() - .to_vec(); + let commitment = host.get_request_commitment(&request); - if RequestAcks::::contains_key(key.clone()) { + if RequestAcks::::contains_key(commitment.clone()) { return Err(Error::ImplementationSpecific(format!( "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", request.nonce(), @@ -79,23 +61,16 @@ impl ISMPRouter for Router { Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } - RequestAcks::::insert(key, commitment); + RequestAcks::::insert(commitment, Receipt::Ok); Ok(()) } fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); - let key = ResponsePath { - dest_chain: response.request.source_chain(), - source_chain: response.request.dest_chain(), - nonce: response.request.nonce(), - } - .to_string() - .as_bytes() - .to_vec(); + let commitment = host.get_response_commitment(&response); - if ResponseAcks::::contains_key(key.clone()) { + if ResponseAcks::::contains_key(commitment.clone()) { return Err(Error::ImplementationSpecific(format!( "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", response.request.nonce(), @@ -125,7 +100,7 @@ impl ISMPRouter for Router { Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) } - ResponseAcks::::insert(key, commitment); + ResponseAcks::::insert(commitment, Receipt::Ok); Ok(()) } From 3257c798f90b0132b8019ece862c79a3f7e7d2f0 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 19 Apr 2023 09:58:10 +0100 Subject: [PATCH 115/182] Parachain Consensus Client (#25) * initial draft * wip * fix consensus client * update parachain consensus client * all green * fix build errors * chore * remove beacon consensus client * renames * ProxyRouter * pub mod router * install protoc * no-std * extern alloc --------- Co-authored-by: David Salami --- .github/workflows/build-test-and-lint.yml | 6 + Cargo.lock | 6973 ++++++++++++++--- Cargo.toml | 74 +- pallet-ismp/Cargo.toml | 52 + pallet-ismp/primitives/Cargo.toml | 29 + pallet-ismp/primitives/src/lib.rs | 18 + pallet-ismp/primitives/src/mmr.rs | 104 + {rpc => pallet-ismp/rpc}/Cargo.toml | 15 +- {rpc => pallet-ismp/rpc}/src/lib.rs | 58 +- .../runtime-api}/Cargo.toml | 9 +- .../runtime-api}/src/lib.rs | 7 +- {src => pallet-ismp/src}/errors.rs | 32 +- {src => pallet-ismp/src}/events.rs | 0 {src => pallet-ismp/src}/host.rs | 50 +- {src => pallet-ismp/src}/lib.rs | 93 +- pallet-ismp/src/mmr.rs | 3 + {src => pallet-ismp/src}/mmr/mmr.rs | 42 +- {src => pallet-ismp/src}/mmr/storage.rs | 38 +- {src => pallet-ismp/src}/mmr/utils.rs | 2 +- {src => pallet-ismp/src}/primitives.rs | 12 +- {src => pallet-ismp/src}/router.rs | 95 +- parachain-consensus/Cargo.toml | 55 + parachain-consensus/src/consensus.rs | 291 + parachain-consensus/src/lib.rs | 79 + src/consensus_clients.rs | 2 - .../beacon_consensus_client.rs | 6 - .../beacon_consensus_client/beacon_client.rs | 190 - .../beacon_consensus_client/optimism.rs | 116 - .../beacon_consensus_client/presets.rs | 77 - .../state_machine_ids.rs | 3 - .../beacon_consensus_client/types.rs | 64 - .../beacon_consensus_client/utils.rs | 130 - src/consensus_clients/consensus_client_ids.rs | 1 - src/mmr/mod.rs | 116 - 34 files changed, 6835 insertions(+), 2007 deletions(-) create mode 100644 pallet-ismp/Cargo.toml create mode 100644 pallet-ismp/primitives/Cargo.toml create mode 100644 pallet-ismp/primitives/src/lib.rs create mode 100644 pallet-ismp/primitives/src/mmr.rs rename {rpc => pallet-ismp/rpc}/Cargo.toml (78%) rename {rpc => pallet-ismp/rpc}/src/lib.rs (81%) rename {runtime-api => pallet-ismp/runtime-api}/Cargo.toml (72%) rename {runtime-api => pallet-ismp/runtime-api}/src/lib.rs (95%) rename {src => pallet-ismp/src}/errors.rs (80%) rename {src => pallet-ismp/src}/events.rs (100%) rename {src => pallet-ismp/src}/host.rs (75%) rename {src => pallet-ismp/src}/lib.rs (86%) create mode 100644 pallet-ismp/src/mmr.rs rename {src => pallet-ismp/src}/mmr/mmr.rs (70%) rename {src => pallet-ismp/src}/mmr/storage.rs (84%) rename {src => pallet-ismp/src}/mmr/utils.rs (97%) rename {src => pallet-ismp/src}/primitives.rs (72%) rename {src => pallet-ismp/src}/router.rs (50%) create mode 100644 parachain-consensus/Cargo.toml create mode 100644 parachain-consensus/src/consensus.rs create mode 100644 parachain-consensus/src/lib.rs delete mode 100644 src/consensus_clients.rs delete mode 100644 src/consensus_clients/beacon_consensus_client.rs delete mode 100644 src/consensus_clients/beacon_consensus_client/beacon_client.rs delete mode 100644 src/consensus_clients/beacon_consensus_client/optimism.rs delete mode 100644 src/consensus_clients/beacon_consensus_client/presets.rs delete mode 100644 src/consensus_clients/beacon_consensus_client/state_machine_ids.rs delete mode 100644 src/consensus_clients/beacon_consensus_client/types.rs delete mode 100644 src/consensus_clients/beacon_consensus_client/utils.rs delete mode 100644 src/consensus_clients/consensus_client_ids.rs delete mode 100644 src/mmr/mod.rs diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index 83d6942c2..6717eea85 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -22,6 +22,11 @@ jobs: toolchain: nightly targets: wasm32-unknown-unknown + - name: Install Protoc + uses: arduino/setup-protoc@v1 + with: + version: '3.9.1' + - name: Rust cache uses: Swatinem/rust-cache@v2 with: @@ -38,6 +43,7 @@ jobs: eval `ssh-agent -s` ssh-add - <<< '${{ secrets.SSH_KEY }}' cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly check -p ismp-parachain-consensus --no-default-features --target=wasm32-unknown-unknown --verbose - name: Run tests run: | diff --git a/Cargo.lock b/Cargo.lock index b981c6a2a..3574f5f34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,117 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", +] + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", + "subtle", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead 0.5.2", + "aes 0.8.2", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.0", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.6" @@ -54,6 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", + "getrandom 0.2.9", "once_cell", "version_check", ] @@ -67,11 +179,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "amcl" -version = "0.3.0" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -90,6 +197,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -105,6 +261,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "array-bytes" version = "4.2.0" @@ -130,10 +292,117 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] -name = "as-any" -version = "0.3.0" +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.11", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] [[package]] name = "async-trait" @@ -143,7 +412,37 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", +] + +[[package]] +name = "asynchronous-codec" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", ] [[package]] @@ -167,6 +466,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.1.1" @@ -185,6 +490,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.6.0" @@ -209,6 +520,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -236,13 +567,49 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "blake3" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.6", +] + [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding", + "block-padding 0.1.5", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -266,6 +633,16 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding 0.2.1", + "cipher 0.2.5", +] + [[package]] name = "block-padding" version = "0.1.5" @@ -276,9 +653,36 @@ dependencies = [ ] [[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bounded-collections" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bounded-vec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" +dependencies = [ + "thiserror", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] @@ -309,6 +713,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -321,6 +731,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cc" version = "1.0.79" @@ -330,6 +751,26 @@ dependencies = [ "jobserver", ] +[[package]] +name = "ccm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-expr" version = "0.10.3" @@ -345,6 +786,37 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead 0.4.3", + "chacha20", + "cipher 0.3.0", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.24" @@ -352,11 +824,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", + "js-sys", "num-integer", "num-traits", + "time 0.1.45", + "wasm-bindgen", "winapi", ] +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2", + "multibase", + "multihash 0.16.3", + "serde", + "unsigned-varint", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "ckb-merkle-mountain-range" version = "0.5.2" @@ -366,6 +882,71 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "coarsetime" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" +dependencies = [ + "libc", + "once_cell", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -376,12 +957,49 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +[[package]] +name = "constant_time_eq" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -417,18 +1035,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52056f6d0584484b57fa6c1a65c1fcb15f3780d8b6a758426d9e3084169b2ddd" +checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fed94c8770dc25d01154c3ffa64ed0b3ba9d583736f305fed7beebe5d9cf74" +checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -438,6 +1056,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli 0.26.2", + "hashbrown 0.12.3", "log", "regalloc2", "smallvec", @@ -446,33 +1065,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c451b81faf237d11c7e4f3165eeb6bac61112762c5cfe7b4c0fb7241474358f" +checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c940133198426d26128f08be2b40b0bd117b84771fd36798969c4d712d81fc" +checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" [[package]] name = "cranelift-entity" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0f1b2fdc18776956370cf8d9b009ded3f855350c480c1c52142510961f352" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34897538b36b216cc8dd324e73263596d51b8cf610da6498322838b2546baf8a" +checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" dependencies = [ "cranelift-codegen", "log", @@ -482,15 +1101,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2629a569fae540f16a76b70afcc87ad7decb38dc28fa6c648ac73b51e78470" +checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" [[package]] name = "cranelift-native" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20937dab4e14d3e225c5adfc9c7106bafd4ac669bdb43027b911ff794c6fb318" +checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" dependencies = [ "cranelift-codegen", "libc", @@ -499,9 +1118,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fc2288957a94fd342a015811479de1837850924166d1f1856d8406e6f3609b" +checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -513,6 +1132,21 @@ dependencies = [ "wasmtime-types", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crc32fast" version = "1.3.2" @@ -524,9 +1158,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -556,6 +1190,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.15" @@ -590,6 +1234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] @@ -614,96 +1259,389 @@ dependencies = [ ] [[package]] -name = "curve25519-dalek" -version = "2.1.3" +name = "ctr" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle", - "zeroize", + "cipher 0.3.0", ] [[package]] -name = "curve25519-dalek" -version = "3.2.0" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", + "cipher 0.4.4", ] [[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +name = "cumulus-pallet-parachain-system" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "bytes", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "environmental", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "xcm", ] [[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +name = "cumulus-pallet-parachain-system-proc-macro" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" dependencies = [ - "cc", - "codespan-reporting", - "once_cell", + "proc-macro-crate", "proc-macro2", "quote", - "scratch", - "syn 2.0.13", + "syn 1.0.109", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +name = "cumulus-primitives-core" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.13", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-primitives", + "sp-api", + "sp-runtime", + "sp-std", + "sp-trie", + "xcm", ] [[package]] -name = "der" +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", + "parity-scale-codec", + "sc-client-api", + "scale-info", + "sp-api", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-storage", + "sp-trie", + "tracing", +] + +[[package]] +name = "cumulus-relay-chain-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "futures", + "jsonrpsee-core", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "cumulus-test-relay-sproof-builder" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +dependencies = [ + "cumulus-primitives-core", + "parity-scale-codec", + "polkadot-primitives", + "sp-runtime", + "sp-state-machine", + "sp-std", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +dependencies = [ + "cfg-if", + "fiat-crypto", + "packed_simd_2", + "platforms", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.15", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "data-encoding-macro" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "der" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version", "syn 1.0.109", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.8.1" @@ -733,6 +1671,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + [[package]] name = "directories-next" version = "2.0.0" @@ -743,6 +1690,17 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -754,12 +1712,35 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "downcast-rs" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dtoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" + [[package]] name = "dyn-clonable" version = "0.9.0" @@ -816,6 +1797,8 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", "ed25519", + "rand 0.7.3", + "serde", "sha2 0.9.9", "zeroize", ] @@ -853,12 +1836,27 @@ dependencies = [ "ff", "generic-array 0.14.7", "group", + "hkdf", + "pem-rfc7468", + "pkcs8", "rand_core 0.6.4", "sec1", "subtle", "zeroize", ] +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -891,13 +1889,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -911,72 +1909,56 @@ dependencies = [ ] [[package]] -name = "ethabi" -version = "18.0.0" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "serde", - "sha3", - "thiserror", - "uint", -] +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "ethbloom" -version = "0.13.0" +name = "exit-future" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "crunchy", - "fixed-hash", - "impl-rlp", - "tiny-keccak", + "futures", ] [[package]] -name = "ethereum-consensus" -version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=main#48335b5c8074d63553ee4681993e294eba947f88" +name = "expander" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" dependencies = [ - "bs58", - "hashbrown 0.13.2", - "integer-sqrt", - "milagro_bls", - "multihash", - "rand 0.8.5", - "sha2 0.9.9", - "ssz-rs", + "blake3", + "fs-err", + "proc-macro2", + "quote", ] [[package]] -name = "ethereum-trie" -version = "0.1.0" -source = "git+https://github.com/polytope-labs/ethereum-trie?branch=main#824e16bc3f6666f12010acc5dd26a273ef622068" +name = "expander" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" dependencies = [ - "hash-db", - "hash256-std-hasher", - "memory-db 0.30.0", - "parity-scale-codec", - "primitive-types", - "rlp", - "tiny-keccak", - "trie-db", + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "ethereum-types" -version = "0.14.1" +name = "expander" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" dependencies = [ - "ethbloom", - "fixed-hash", - "impl-rlp", - "primitive-types", - "uint", + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -991,6 +1973,49 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fatality" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" +dependencies = [ + "fatality-proc-macro", + "thiserror", +] + +[[package]] +name = "fatality-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" +dependencies = [ + "expander 0.0.4", + "indexmap", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "fdlimit" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" +dependencies = [ + "libc", +] + [[package]] name = "ff" version = "0.12.1" @@ -1001,6 +2026,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "file-per-thread-logger" version = "0.1.6" @@ -1011,6 +2042,22 @@ dependencies = [ "log", ] +[[package]] +name = "finality-grandpa" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" +dependencies = [ + "either", + "futures", + "futures-timer", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1023,18 +2070,68 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fork-tree" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "frame-support", + "frame-support-procedural", "frame-system", "linregress", "log", @@ -1050,6 +2147,7 @@ dependencies = [ "sp-runtime-interface", "sp-std", "sp-storage", + "static_assertions", ] [[package]] @@ -1067,9 +2165,10 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "bitflags", + "environmental", "frame-metadata", "frame-support-procedural", "impl-trait-for-tuples", @@ -1099,10 +2198,11 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "Inflector", "cfg-expr", + "derive-syn-parse", "frame-support-procedural-tools", "itertools", "proc-macro2", @@ -1113,7 +2213,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -1125,7 +2225,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "proc-macro2", "quote", @@ -1135,7 +2235,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "frame-support", "log", @@ -1150,6 +2250,33 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fs4" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea55201cc351fdb478217c0fb641b59813da9b4efe4c414a9d8f989a657d149" +dependencies = [ + "libc", + "rustix 0.35.13", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -1205,6 +2332,21 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.9", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.28" @@ -1213,7 +2355,18 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", +] + +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls 0.20.8", + "webpki 0.22.0", ] [[package]] @@ -1247,7 +2400,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.9", "pin-utils", "slab", ] @@ -1302,6 +2455,26 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.5.3", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.6.0", +] + [[package]] name = "gimli" version = "0.26.2" @@ -1319,6 +2492,12 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "globset" version = "0.4.10" @@ -1345,9 +2524,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -1364,9 +2543,9 @@ dependencies = [ [[package]] name = "hash-db" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" [[package]] name = "hash256-std-hasher" @@ -1401,6 +2580,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.2.6" @@ -1428,6 +2616,21 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -1468,6 +2671,17 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.9" @@ -1487,9 +2701,15 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", - "pin-project-lite", + "pin-project-lite 0.2.9", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" version = "1.8.0" @@ -1510,9 +2730,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -1524,7 +2744,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite", + "pin-project-lite 0.2.9", "socket2", "tokio", "tower-service", @@ -1532,6 +2752,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.8", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + [[package]] name = "iana-time-zone" version = "0.1.56" @@ -1543,7 +2778,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows 0.48.0", ] [[package]] @@ -1557,21 +2792,68 @@ dependencies = [ ] [[package]] -name = "impl-codec" -version = "0.6.0" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ - "parity-scale-codec", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "impl-rlp" +name = "idna" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "if-watch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration", + "tokio", + "windows 0.34.0", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "rlp", + "parity-scale-codec", ] [[package]] @@ -1606,14 +2888,57 @@ dependencies = [ ] [[package]] -name = "integer-sqrt" -version = "0.1.5" +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "integer-sqrt" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" dependencies = [ "num-traits", ] +[[package]] +name = "interceptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + [[package]] name = "io-lifetimes" version = "0.7.5" @@ -1631,6 +2956,30 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ipconfig" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +dependencies = [ + "socket2", + "widestring", + "winapi", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + [[package]] name = "is-terminal" version = "0.4.7" @@ -1639,14 +2988,14 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes 1.0.10", - "rustix 0.37.8", + "rustix 0.37.11", "windows-sys 0.48.0", ] [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#ebcd551ede6c49e862409555237ad697934366da" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#26534dce3e5980eb33ff5ee1ce1e193cc53ebf1a" dependencies = [ "derive_more", "parity-scale-codec", @@ -1655,13 +3004,47 @@ dependencies = [ "serde", ] +[[package]] +name = "ismp-parachain-consensus" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "frame-support", + "frame-system", + "hex-literal 0.4.1", + "ismp", + "ismp-primitives", + "parity-scale-codec", + "primitive-types", + "scale-info", + "sp-consensus-aura", + "sp-io", + "sp-runtime", + "sp-trie", +] + +[[package]] +name = "ismp-primitives" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "frame-system", + "ismp", + "parity-scale-codec", + "primitive-types", + "sp-runtime", +] + [[package]] name = "ismp-rpc" version = "0.1.0" dependencies = [ "frame-system", - "hex-literal", + "hex-literal 0.3.4", "ismp", + "ismp-primitives", "ismp-runtime-api", "jsonrpsee", "pallet-ismp", @@ -1680,6 +3063,7 @@ name = "ismp-runtime-api" version = "0.1.0" dependencies = [ "ismp", + "ismp-primitives", "pallet-ismp", "parity-scale-codec", "serde", @@ -1748,7 +3132,7 @@ dependencies = [ "globset", "hyper", "jsonrpsee-types", - "parking_lot", + "parking_lot 0.12.1", "rand 0.8.5", "rustc-hash", "serde", @@ -1838,18 +3222,64 @@ dependencies = [ "smallvec", ] +[[package]] +name = "kvdb-memorydb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + +[[package]] +name = "kvdb-rocksdb" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2182b8219fee6bd83aacaab7344e840179ae079d5216aa4e249b4d704646a844" +dependencies = [ + "kvdb", + "num_cpus", + "parking_lot 0.12.1", + "regex", + "rocksdb", + "smallvec", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "libm" version = "0.2.6" @@ -1857,1061 +3287,3388 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] -name = "libsecp256k1" -version = "0.7.1" +name = "libp2p" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", - "typenum", + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.9", + "instant", + "libp2p-core 0.38.0", + "libp2p-dns", + "libp2p-identify", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-mplex", + "libp2p-noise", + "libp2p-ping", + "libp2p-quic", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-wasm-ext", + "libp2p-webrtc", + "libp2p-websocket", + "libp2p-yamux", + "multiaddr 0.16.0", + "parking_lot 0.12.1", + "pin-project", + "smallvec", ] [[package]] -name = "libsecp256k1-core" -version = "0.3.0" +name = "libp2p-core" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "log", + "multiaddr 0.16.0", + "multihash 0.16.3", + "multistream-select", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink", + "sec1", + "sha2 0.10.6", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "zeroize", ] [[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" +name = "libp2p-core" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +checksum = "9b7f8b7d65c070a5a1b5f8f0510648189da08f787b8963f8e21219e0710733af" dependencies = [ - "libsecp256k1-core", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "multistream-select", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", ] [[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" +name = "libp2p-dns" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" dependencies = [ - "libsecp256k1-core", + "futures", + "libp2p-core 0.38.0", + "log", + "parking_lot 0.12.1", + "smallvec", + "trust-dns-resolver", ] [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "libp2p-identify" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" dependencies = [ - "cc", + "asynchronous-codec", + "futures", + "futures-timer", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "lru 0.8.1", + "prost", + "prost-build", + "prost-codec", + "smallvec", + "thiserror", + "void", ] [[package]] -name = "linregress" -version = "0.4.4" +name = "libp2p-identity" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c601a85f5ecd1aba625247bca0031585fb1c446461b142878a16f8245ddeb8" +checksum = "8a8ea433ae0cea7e3315354305237b9897afe45278b2118a7a57ca744e70fd27" dependencies = [ - "nalgebra", - "statrs", + "bs58", + "ed25519-dalek", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "prost", + "quick-protobuf", + "rand 0.8.5", + "thiserror", + "zeroize", ] [[package]] -name = "linux-raw-sys" -version = "0.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" - -[[package]] -name = "linux-raw-sys" -version = "0.3.1" +name = "libp2p-kad" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" +dependencies = [ + "arrayvec 0.7.2", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.6", + "smallvec", + "thiserror", + "uint", + "unsigned-varint", + "void", +] [[package]] -name = "lock_api" -version = "0.4.9" +name = "libp2p-mdns" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" dependencies = [ - "autocfg", - "scopeguard", + "data-encoding", + "futures", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "socket2", + "tokio", + "trust-dns-proto", + "void", ] [[package]] -name = "log" -version = "0.4.17" +name = "libp2p-metrics" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" dependencies = [ - "cfg-if", + "libp2p-core 0.38.0", + "libp2p-identify", + "libp2p-kad", + "libp2p-ping", + "libp2p-swarm", + "prometheus-client", ] [[package]] -name = "lru" -version = "0.8.1" +name = "libp2p-mplex" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" dependencies = [ - "hashbrown 0.12.3", + "asynchronous-codec", + "bytes", + "futures", + "libp2p-core 0.38.0", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "unsigned-varint", ] [[package]] -name = "mach" -version = "0.3.2" +name = "libp2p-noise" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" dependencies = [ - "libc", + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "libp2p-core 0.38.0", + "log", + "once_cell", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.6", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek 1.1.1", + "zeroize", ] [[package]] -name = "matchers" -version = "0.0.1" +name = "libp2p-ping" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" dependencies = [ - "regex-automata", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "rand 0.8.5", + "void", ] [[package]] -name = "matrixmultiply" -version = "0.3.2" +name = "libp2p-quic" +version = "0.7.0-alpha" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" dependencies = [ - "rawpointer", + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-tls", + "log", + "parking_lot 0.12.1", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.8", + "thiserror", + "tokio", ] [[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memfd" -version = "0.6.3" +name = "libp2p-request-response" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" dependencies = [ - "rustix 0.37.8", + "async-trait", + "bytes", + "futures", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "unsigned-varint", ] [[package]] -name = "memoffset" -version = "0.6.5" +name = "libp2p-swarm" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" dependencies = [ - "autocfg", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm-derive", + "log", + "pin-project", + "rand 0.8.5", + "smallvec", + "thiserror", + "tokio", + "void", ] [[package]] -name = "memoffset" -version = "0.8.0" +name = "libp2p-swarm-derive" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ - "autocfg", + "heck", + "quote", + "syn 1.0.109", ] [[package]] -name = "memory-db" -version = "0.30.0" +name = "libp2p-tcp" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac11bb793c28fa095b7554466f53b3a60a2cd002afdac01bcf135cbd73a269" +checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" dependencies = [ - "hash-db", - "hashbrown 0.12.3", - "parity-util-mem", + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core 0.38.0", + "log", + "socket2", + "tokio", ] [[package]] -name = "memory-db" -version = "0.31.0" +name = "libp2p-tls" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ - "hash-db", - "hashbrown 0.12.3", + "futures", + "futures-rustls", + "libp2p-core 0.39.1", + "libp2p-identity", + "rcgen 0.10.0", + "ring", + "rustls 0.20.8", + "thiserror", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna", ] [[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - -[[package]] -name = "merlin" -version = "2.0.1" +name = "libp2p-wasm-ext" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" dependencies = [ - "byteorder", - "keccak", - "rand_core 0.5.1", - "zeroize", + "futures", + "js-sys", + "libp2p-core 0.38.0", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] -name = "milagro_bls" -version = "1.5.1" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +name = "libp2p-webrtc" +version = "0.4.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" dependencies = [ - "amcl", + "async-trait", + "asynchronous-codec", + "bytes", + "futures", + "futures-timer", "hex", - "lazy_static", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-noise", + "log", + "multihash 0.16.3", + "prost", + "prost-build", + "prost-codec", "rand 0.8.5", - "zeroize", + "rcgen 0.9.3", + "serde", + "stun", + "thiserror", + "tinytemplate", + "tokio", + "tokio-util", + "webrtc", ] [[package]] -name = "miniz_oxide" -version = "0.6.2" +name = "libp2p-websocket" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" dependencies = [ - "adler", + "either", + "futures", + "futures-rustls", + "libp2p-core 0.38.0", + "log", + "parking_lot 0.12.1", + "quicksink", + "rw-stream-sink", + "soketto", + "url", + "webpki-roots", ] [[package]] -name = "mio" -version = "0.8.6" +name = "libp2p-yamux" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" dependencies = [ - "libc", + "futures", + "libp2p-core 0.38.0", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "parking_lot 0.12.1", + "thiserror", + "yamux", ] [[package]] -name = "multihash" -version = "0.16.3" +name = "librocksdb-sys" +version = "0.8.3+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" dependencies = [ - "core2", - "digest 0.10.6", - "multihash-derive", - "sha2 0.10.6", - "unsigned-varint", + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "tikv-jemalloc-sys", ] [[package]] -name = "multihash-derive" -version = "0.8.0" +name = "libsecp256k1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", ] [[package]] -name = "nalgebra" -version = "0.27.1" +name = "libsecp256k1-core" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex", - "num-rational", - "num-traits", - "rand 0.8.5", - "rand_distr", - "simba", - "typenum", + "crunchy", + "digest 0.9.0", + "subtle", ] [[package]] -name = "nalgebra-macros" -version = "0.1.0" +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "libsecp256k1-core", ] [[package]] -name = "nohash-hasher" -version = "0.2.0" +name = "libsecp256k1-gen-genmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] [[package]] -name = "num-bigint" -version = "0.4.3" +name = "libz-sys" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "cc", + "pkg-config", + "vcpkg", ] [[package]] -name = "num-complex" -version = "0.4.3" +name = "link-cplusplus" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ - "num-traits", + "cc", ] [[package]] -name = "num-format" -version = "0.4.4" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec 0.7.2", - "itoa", -] +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] -name = "num-integer" -version = "0.1.45" +name = "linked_hash_set" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" dependencies = [ - "autocfg", - "num-traits", + "linked-hash-map", ] [[package]] -name = "num-rational" -version = "0.4.1" +name = "linregress" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", + "nalgebra", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "linux-raw-sys" +version = "0.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f508063cc7bb32987c71511216bd5a32be15bccb6a80b52df8b9d7f01fc3aa2" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", - "libm", + "scopeguard", ] [[package]] -name = "num_cpus" -version = "1.15.0" +name = "log" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "hermit-abi 0.2.6", - "libc", + "cfg-if", ] [[package]] -name = "object" -version = "0.29.0" +name = "lru" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "crc32fast", "hashbrown 0.12.3", - "indexmap", - "memchr", ] [[package]] -name = "object" -version = "0.30.3" +name = "lru" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" dependencies = [ - "memchr", + "hashbrown 0.13.2", ] [[package]] -name = "once_cell" -version = "1.17.1" +name = "lru-cache" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] [[package]] -name = "opaque-debug" -version = "0.2.3" +name = "lz4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "lz4-sys" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pallet-ismp" -version = "0.1.0" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" dependencies = [ - "ckb-merkle-mountain-range", - "derive_more", - "ethabi", - "ethereum-trie", - "frame-benchmarking", - "frame-support", - "frame-system", - "hash-db", - "hash256-std-hasher", - "hex", - "hex-literal", - "ismp", - "log", - "parity-scale-codec", - "rlp", - "rlp-derive", - "scale-info", - "serde", - "sp-api", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sync-committee-primitives", - "sync-committee-verifier", - "trie-db", + "cc", + "libc", ] [[package]] -name = "parity-scale-codec" -version = "3.4.0" +name = "mach" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ - "arrayvec 0.7.2", - "bitvec", - "byte-slice-cast", - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", + "libc", ] [[package]] -name = "parity-scale-codec-derive" -version = "3.1.4" +name = "match_cfg" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "regex-automata", ] [[package]] -name = "parity-util-mem" -version = "0.12.0" +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matrixmultiply" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" +checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "impl-trait-for-tuples", - "parity-util-mem-derive", - "winapi", + "rawpointer", ] [[package]] -name = "parity-util-mem-derive" -version = "0.1.0" +name = "md-5" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "proc-macro2", - "syn 1.0.109", - "synstructure", + "digest 0.10.6", ] [[package]] -name = "parity-wasm" -version = "0.45.0" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "parking_lot" -version = "0.12.1" +name = "memfd" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "lock_api", - "parking_lot_core", + "rustix 0.37.11", ] [[package]] -name = "parking_lot_core" -version = "0.9.7" +name = "memmap2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ - "cfg-if", "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", ] [[package]] -name = "paste" -version = "1.0.12" +name = "memoffset" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] [[package]] -name = "pbkdf2" +name = "memoffset" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ - "crypto-mac 0.11.1", + "autocfg", ] [[package]] -name = "pbkdf2" -version = "0.11.0" +name = "memory-db" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "digest 0.10.6", + "hash-db", ] [[package]] -name = "pin-project-lite" -version = "0.2.9" +name = "memory_units" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "merlin" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] [[package]] -name = "pkcs8" -version = "0.9.0" +name = "mick-jaeger" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" dependencies = [ - "der", - "spki", + "futures", + "rand 0.8.5", + "thrift", ] [[package]] -name = "pkg-config" -version = "0.3.26" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "miniz_oxide" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] [[package]] -name = "primitive-types" -version = "0.12.1" +name = "mio" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", ] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "mockall" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ - "once_cell", - "toml_edit", + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "mockall_derive" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ - "proc-macro-error-attr", + "cfg-if", "proc-macro2", "quote", "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "multiaddr" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "arrayref", + "byteorder", + "data-encoding", + "multibase", + "multihash 0.16.3", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", ] [[package]] -name = "proc-macro2" -version = "1.0.56" +name = "multiaddr" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" dependencies = [ - "unicode-ident", + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash 0.17.0", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", ] [[package]] -name = "prometheus" -version = "0.13.3" +name = "multibase" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "memchr", - "parking_lot", - "thiserror", + "base-x", + "data-encoding", + "data-encoding-macro", ] [[package]] -name = "psm" -version = "0.1.21" +name = "multihash" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" dependencies = [ - "cc", + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "sha3", + "unsigned-varint", ] [[package]] -name = "quote" -version = "1.0.26" +name = "multihash" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] [[package]] -name = "rand" -version = "0.7.3" +name = "multihash-derive" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", ] [[package]] -name = "rand" -version = "0.8.5" +name = "multimap" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", ] [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "nalgebra" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "nalgebra-macros" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "names" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" dependencies = [ - "getrandom 0.1.16", + "rand 0.8.5", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "nanorand" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" dependencies = [ - "getrandom 0.2.9", + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", ] [[package]] -name = "rand_distr" -version = "0.4.3" +name = "netlink-packet-route" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ - "num-traits", - "rand 0.8.5", + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "netlink-packet-utils" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ - "rand_core 0.5.1", + "anyhow", + "byteorder", + "paste", + "thiserror", ] [[package]] -name = "rawpointer" -version = "0.2.1" +name = "netlink-proto" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] [[package]] -name = "rayon" -version = "1.7.0" +name = "netlink-sys" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ - "either", - "rayon-core", + "bytes", + "futures", + "libc", + "log", + "tokio", ] [[package]] -name = "rayon-core" -version = "1.11.0" +name = "nix" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", + "bitflags", + "cfg-if", + "libc", + "memoffset 0.6.5", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "nohash-hasher" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "bitflags", + "memchr", + "minimal-lexical", ] [[package]] -name = "redox_users" +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-bigint" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "getrandom 0.2.9", - "redox_syscall", - "thiserror", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "ref-cast" -version = "1.0.16" +name = "num-complex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ - "ref-cast-impl", + "num-traits", ] [[package]] -name = "ref-cast-impl" -version = "1.0.16" +name = "num-format" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.13", + "arrayvec 0.7.2", + "itoa", ] [[package]] -name = "regalloc2" -version = "0.3.2" +name = "num-integer" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", + "autocfg", + "num-traits", ] [[package]] -name = "regex" -version = "1.7.3" +name = "num-rational" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "autocfg", + "num-bigint", + "num-integer", + "num-traits", ] [[package]] -name = "regex-automata" -version = "0.1.10" +name = "num-traits" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "regex-syntax", + "autocfg", ] [[package]] -name = "regex-syntax" -version = "0.6.29" +name = "num_cpus" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] [[package]] -name = "rfc6979" -version = "0.3.1" +name = "object" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ - "crypto-bigint", - "hmac 0.12.1", - "zeroize", + "crc32fast", + "hashbrown 0.12.3", + "indexmap", + "memchr", ] [[package]] -name = "rlp" -version = "0.5.2" +name = "object" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ - "bytes", - "rustc-hex", + "memchr", ] [[package]] -name = "rlp-derive" -version = "0.1.0" +name = "oid-registry" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "asn1-rs 0.3.1", ] [[package]] -name = "rustc-demangle" -version = "0.1.22" +name = "oid-registry" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.2", +] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "once_cell" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] -name = "rustc-hex" -version = "2.1.0" +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "orchestra" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0766f60d83cac01c6e3f3bc36aaa9056e48bea0deddb98a8c74de6021f3061" +dependencies = [ + "async-trait", + "dyn-clonable", + "futures", + "futures-timer", + "orchestra-proc-macro", + "pin-project", + "prioritized-metered-channel", + "thiserror", + "tracing", +] + +[[package]] +name = "orchestra-proc-macro" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8e83dbd049009426b445424a1104c78e6172a4c13e3614e52a38262785a5d7" +dependencies = [ + "expander 1.0.0", + "indexmap", + "itertools", + "petgraph", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "p384" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm 0.1.4", +] + +[[package]] +name = "pallet-ismp" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "derive_more", + "frame-benchmarking", + "frame-support", + "frame-system", + "ismp", + "ismp-primitives", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "parity-db" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bfb81cf5c90a222db2fb7b3a7cbf8cc7f38dfb6647aca4d98edf8281f56ed5" +dependencies = [ + "blake2", + "crc32fast", + "fs2", + "hex", + "libc", + "log", + "lz4", + "memmap2", + "parking_lot 0.12.1", + "rand 0.8.5", + "siphasher", + "snap", +] + +[[package]] +name = "parity-scale-codec" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-send-wrapper" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.7", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "polkadot-core-primitives" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "polkadot-node-jaeger" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "lazy_static", + "log", + "mick-jaeger", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-primitives", + "sc-network", + "sp-core", + "thiserror", + "tokio", +] + +[[package]] +name = "polkadot-node-metrics" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "bs58", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "polkadot-primitives", + "prioritized-metered-channel", + "sc-cli", + "sc-service", + "sc-tracing", + "substrate-prometheus-endpoint", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-network-protocol" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "async-trait", + "derive_more", + "fatality", + "futures", + "hex", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-primitives", + "rand 0.8.5", + "sc-authority-discovery", + "sc-network", + "strum", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-primitives" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "bounded-vec", + "futures", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "schnorrkel", + "serde", + "sp-application-crypto", + "sp-consensus-babe", + "sp-consensus-vrf", + "sp-core", + "sp-keystore", + "sp-maybe-compressed-blob", + "sp-runtime", + "thiserror", + "zstd", +] + +[[package]] +name = "polkadot-node-subsystem-types" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "async-trait", + "derive_more", + "futures", + "orchestra", + "polkadot-node-jaeger", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-primitives", + "polkadot-statement-table", + "sc-network", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "polkadot-overseer" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "lru 0.9.0", + "orchestra", + "parking_lot 0.12.1", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-core", + "tikv-jemalloc-ctl", + "tracing-gum", +] + +[[package]] +name = "polkadot-parachain" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "bounded-collections", + "derive_more", + "frame-support", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "polkadot-primitives" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "bitvec", + "hex-literal 0.3.4", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "polkadot-statement-table" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "parity-scale-codec", + "polkadot-primitives", + "sp-core", +] + +[[package]] +name = "polling" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite 0.2.9", + "windows-sys 0.48.0", +] + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.5.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "prioritized-metered-channel" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3caef72a78ca8e77cbdfa87dd516ebb79d4cbe5b42e3b8435b463a8261339ff" +dependencies = [ + "async-channel", + "coarsetime", + "crossbeam-queue", + "derive_more", + "futures", + "futures-timer", + "nanorand", + "thiserror", + "tracing", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.1", + "thiserror", +] + +[[package]] +name = "prometheus-client" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.1", + "prometheus-client-derive-text-encode", +] + +[[package]] +name = "prometheus-client-derive-text-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-codec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" +dependencies = [ + "asynchronous-codec", + "bytes", + "prost", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "quinn-proto" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls 0.20.8", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki 0.22.0", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.9", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem", + "ring", + "time 0.3.20", + "x509-parser 0.13.2", + "yasna", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring", + "time 0.3.20", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.9", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rocksdb" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rpassword" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi", +] + +[[package]] +name = "rtcp" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" +dependencies = [ + "bytes", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", + "tokio", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rtp" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes", + "rand 0.8.5", + "serde", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.35.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +dependencies = [ + "bitflags", + "errno 0.2.8", + "io-lifetimes 0.7.5", + "libc", + "linux-raw-sys 0.0.46", + "windows-sys 0.42.0", +] + +[[package]] +name = "rustix" +version = "0.36.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0af200a3324fa5bcd922e84e9b55a298ea9f431a489f01961acdebc6e908f25" +dependencies = [ + "bitflags", + "errno 0.3.1", + "io-lifetimes 1.0.10", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +dependencies = [ + "bitflags", + "errno 0.3.1", + "io-lifetimes 1.0.10", + "libc", + "linux-raw-sys 0.3.2", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.1", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "log", + "sp-core", + "sp-wasm-interface", + "thiserror", +] + +[[package]] +name = "sc-authority-discovery" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "log", + "parity-scale-codec", + "prost", + "prost-build", + "rand 0.8.5", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-block-builder" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-inherents", + "sp-runtime", +] + +[[package]] +name = "sc-chain-spec" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "memmap2", + "sc-chain-spec-derive", + "sc-client-api", + "sc-executor", + "sc-network", + "sc-telemetry", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "sc-chain-spec-derive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sc-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "chrono", + "clap", + "fdlimit", + "futures", + "libp2p", + "log", + "names", + "parity-scale-codec", + "rand 0.8.5", + "regex", + "rpassword", + "sc-client-api", + "sc-client-db", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-service", + "sc-telemetry", + "sc-tracing", + "sc-utils", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-panic-handler", + "sp-runtime", + "sp-version", + "thiserror", + "tiny-bip39", + "tokio", +] + +[[package]] +name = "sc-client-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-database", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-client-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "hash-db", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-state-db", + "schnellru", + "sp-arithmetic", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] + +[[package]] +name = "sc-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "libp2p", + "log", + "mockall", + "parking_lot 0.12.1", + "sc-client-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-wasm-interface", + "thiserror", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "log", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "log", + "once_cell", + "rustix 0.36.12", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmtime", +] + +[[package]] +name = "sc-informant" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "ansi_term", + "futures", + "futures-timer", + "log", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-keystore" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "async-trait", + "parking_lot 0.12.1", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror", +] + +[[package]] +name = "sc-network" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "async-channel", + "async-trait", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "linked_hash_set", + "log", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-network-common", + "sc-peerset", + "sc-utils", + "serde", + "serde_json", + "smallvec", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "unsigned-varint", + "zeroize", +] + +[[package]] +name = "sc-network-bitswap" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "cid", + "futures", + "libp2p", + "log", + "prost", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-blockchain", + "sp-runtime", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "sc-network-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "async-trait", + "bitflags", + "bytes", + "futures", + "futures-timer", + "libp2p", + "parity-scale-codec", + "prost-build", + "sc-consensus", + "sc-peerset", + "sc-utils", + "serde", + "smallvec", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "zeroize", +] + +[[package]] +name = "sc-network-light" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-peerset", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-network-sync" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "async-trait", + "fork-tree", + "futures", + "futures-timer", + "libp2p", + "log", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "smallvec", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-network-transactions" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "pin-project", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-consensus", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "array-bytes", + "bytes", + "fnv", + "futures", + "futures-timer", + "hyper", + "hyper-rustls", + "libp2p", + "num_cpus", + "once_cell", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-api", + "sp-core", + "sp-offchain", + "sp-runtime", + "threadpool", + "tracing", +] + +[[package]] +name = "sc-peerset" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "futures", + "libp2p", + "log", + "sc-utils", + "serde_json", + "wasm-timer", +] + +[[package]] +name = "sc-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-rpc-api", + "sc-tracing", + "sc-transaction-pool-api", + "sc-utils", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-version", + "tokio", +] [[package]] -name = "rustix" -version = "0.35.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +name = "sc-rpc-api" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "bitflags", - "errno 0.2.8", - "io-lifetimes 0.7.5", - "libc", - "linux-raw-sys 0.0.46", - "windows-sys 0.42.0", + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-transaction-pool-api", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-version", + "thiserror", ] [[package]] -name = "rustix" -version = "0.37.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aef160324be24d31a62147fae491c14d2204a3865c7ca8c3b0d7f7bcb3ea635" +name = "sc-rpc-server" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "bitflags", - "errno 0.3.0", - "io-lifetimes 1.0.10", - "libc", - "linux-raw-sys 0.3.1", - "windows-sys 0.48.0", + "http", + "jsonrpsee", + "log", + "serde_json", + "substrate-prometheus-endpoint", + "tokio", + "tower", + "tower-http", ] [[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "sc-allocator" -version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +name = "sc-rpc-spec-v2" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ + "array-bytes", + "futures", + "futures-util", + "hex", + "jsonrpsee", "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-chain-spec", + "sc-client-api", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-blockchain", "sp-core", - "sp-wasm-interface", + "sp-runtime", + "sp-version", "thiserror", + "tokio-stream", ] [[package]] -name = "sc-client-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +name = "sc-service" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "fnv", + "async-trait", + "directories", + "exit-future", "futures", + "futures-timer", + "jsonrpsee", "log", "parity-scale-codec", - "parking_lot", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", "sc-executor", + "sc-informant", + "sc-keystore", + "sc-network", + "sc-network-bitswap", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-network-transactions", + "sc-offchain", + "sc-rpc", + "sc-rpc-server", + "sc-rpc-spec-v2", + "sc-storage-monitor", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", + "serde", + "serde_json", "sp-api", "sp-blockchain", "sp-consensus", "sp-core", - "sp-database", "sp-externalities", "sp-keystore", "sp-runtime", + "sp-session", "sp-state-machine", "sp-storage", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "static_init", "substrate-prometheus-endpoint", + "tempfile", + "thiserror", + "tokio", + "tracing", + "tracing-futures", ] [[package]] -name = "sc-executor" +name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "lru", + "log", "parity-scale-codec", - "parking_lot", - "sc-executor-common", - "sc-executor-wasmi", - "sc-executor-wasmtime", - "sp-api", + "parking_lot 0.12.1", "sp-core", - "sp-externalities", - "sp-io", - "sp-panic-handler", - "sp-runtime-interface", - "sp-trie", - "sp-version", - "sp-wasm-interface", - "tracing", - "wasmi", ] [[package]] -name = "sc-executor-common" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +name = "sc-storage-monitor" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "sc-allocator", - "sp-maybe-compressed-blob", - "sp-wasm-interface", + "clap", + "fs4", + "futures", + "log", + "sc-client-db", + "sc-utils", + "sp-core", "thiserror", - "wasm-instrument", - "wasmi", + "tokio", ] [[package]] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +name = "sc-sysinfo" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ + "futures", + "libc", "log", - "sc-allocator", - "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", - "wasmi", + "rand 0.8.5", + "rand_pcg", + "regex", + "sc-telemetry", + "serde", + "serde_json", + "sp-core", + "sp-io", + "sp-std", ] [[package]] -name = "sc-executor-wasmtime" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +name = "sc-telemetry" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "cfg-if", + "chrono", + "futures", + "libp2p", + "log", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-utils", + "serde", + "serde_json", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "sc-tracing" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "ansi_term", + "atty", + "chrono", + "lazy_static", "libc", "log", "once_cell", - "rustix 0.35.13", - "sc-allocator", - "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", - "wasmtime", + "parking_lot 0.12.1", + "regex", + "rustc-hash", + "sc-client-api", + "sc-rpc-server", + "sc-tracing-proc-macro", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-tracing", + "thiserror", + "tracing", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "sc-tracing-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sc-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "linked-hash-map", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-transaction-pool-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-tracing", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "thiserror", ] [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "async-trait", "futures", @@ -2925,15 +6682,16 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "backtrace", + "async-channel", "futures", "futures-timer", "lazy_static", "log", - "parking_lot", + "parking_lot 0.12.1", "prometheus", + "sp-arithmetic", ] [[package]] @@ -2962,6 +6720,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "schnorrkel" version = "0.9.1" @@ -2992,6 +6770,38 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sdp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror", + "url", +] + [[package]] name = "sec1" version = "0.3.0" @@ -3025,39 +6835,68 @@ dependencies = [ ] [[package]] -name = "secrecy" -version = "0.8.0" +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" -dependencies = [ - "zeroize", -] +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.159" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -3077,6 +6916,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sha2" version = "0.8.2" @@ -3115,9 +6965,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" dependencies = [ "digest 0.10.6", "keccak", @@ -3132,6 +6982,21 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.6.4" @@ -3144,16 +7009,23 @@ dependencies = [ [[package]] name = "simba" -version = "0.5.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ "approx", "num-complex", "num-traits", "paste", + "wide", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.8" @@ -3175,6 +7047,29 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "snap" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" + +[[package]] +name = "snow" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" +dependencies = [ + "aes-gcm 0.9.4", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.0.0-rc.1", + "rand_core 0.6.4", + "ring", + "rustc_version", + "sha2 0.10.6", + "subtle", +] + [[package]] name = "socket2" version = "0.4.9" @@ -3191,8 +7086,9 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ - "base64", + "base64 0.13.1", "bytes", + "flate2", "futures", "http", "httparse", @@ -3204,7 +7100,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "hash-db", "log", @@ -3222,9 +7118,11 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ + "Inflector", "blake2", + "expander 1.0.0", "proc-macro-crate", "proc-macro2", "quote", @@ -3234,7 +7132,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "parity-scale-codec", "scale-info", @@ -3247,7 +7145,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "integer-sqrt", "num-traits", @@ -3258,16 +7156,41 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-block-builder" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", +] + [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "futures", "log", - "lru", + "lru 0.8.1", "parity-scale-codec", - "parking_lot", + "parking_lot 0.12.1", "sp-api", "sp-consensus", "sp-database", @@ -3279,30 +7202,112 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "async-trait", "futures", "log", - "parity-scale-codec", "sp-core", "sp-inherents", "sp-runtime", "sp-state-machine", - "sp-std", - "sp-version", "thiserror", ] +[[package]] +name = "sp-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "merlin", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-consensus-vrf", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-vrf" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", + "scale-info", + "schnorrkel", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "array-bytes", "base58", "bitflags", "blake2", + "bounded-collections", "dyn-clonable", "ed25519-zebra", "futures", @@ -3314,7 +7319,7 @@ dependencies = [ "log", "merlin", "parity-scale-codec", - "parking_lot", + "parking_lot 0.12.1", "primitive-types", "rand 0.8.5", "regex", @@ -3339,9 +7344,9 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "blake2", + "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", @@ -3353,7 +7358,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "proc-macro2", "quote", @@ -3364,16 +7369,16 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "kvdb", - "parking_lot", + "parking_lot 0.12.1", ] [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "proc-macro2", "quote", @@ -3383,7 +7388,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "environmental", "parity-scale-codec", @@ -3394,11 +7399,12 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", + "scale-info", "sp-core", "sp-runtime", "sp-std", @@ -3408,7 +7414,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "bytes", "ed25519", @@ -3430,17 +7436,29 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "sp-keyring" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "lazy_static", + "sp-core", + "sp-runtime", + "strum", +] + [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "async-trait", "futures", "merlin", "parity-scale-codec", - "parking_lot", + "parking_lot 0.12.1", "schnorrkel", + "serde", "sp-core", "sp-externalities", "thiserror", @@ -3449,26 +7467,46 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "thiserror", "zstd", ] +[[package]] +name = "sp-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "sp-api", + "sp-core", + "sp-runtime", +] + [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "backtrace", "lazy_static", "regex", ] +[[package]] +name = "sp-rpc" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "rustc-hash", + "serde", + "sp-core", +] + [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "either", "hash256-std-hasher", @@ -3490,7 +7528,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -3508,7 +7546,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "Inflector", "proc-macro-crate", @@ -3517,10 +7555,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sp-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", +] + [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "parity-scale-codec", "scale-info", @@ -3532,12 +7584,12 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "hash-db", "log", "parity-scale-codec", - "parking_lot", + "parking_lot 0.12.1", "rand 0.8.5", "smallvec", "sp-core", @@ -3552,12 +7604,12 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "impl-serde", "parity-scale-codec", @@ -3567,10 +7619,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "sp-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "futures-timer", + "log", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", + "thiserror", +] + [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "parity-scale-codec", "sp-std", @@ -3579,21 +7646,46 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "sp-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "sp-api", + "sp-runtime", +] + +[[package]] +name = "sp-transaction-storage-proof" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +dependencies = [ + "async-trait", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-trie", +] + [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ - "ahash 0.7.6", + "ahash 0.8.3", "hash-db", "hashbrown 0.12.3", "lazy_static", - "lru", - "memory-db 0.31.0", + "memory-db", "nohash-hasher", "parity-scale-codec", - "parking_lot", + "parking_lot 0.12.1", "scale-info", + "schnellru", "sp-core", "sp-std", "thiserror", @@ -3605,7 +7697,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "impl-serde", "parity-scale-codec", @@ -3622,7 +7714,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -3633,8 +7725,9 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -3646,7 +7739,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "parity-scale-codec", "scale-info", @@ -3658,6 +7751,12 @@ dependencies = [ "sp-std", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spki" version = "0.6.0" @@ -3684,51 +7783,90 @@ dependencies = [ ] [[package]] -name = "ssz-rs" -version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#96f9a89ccc56ab2abb4c3a83eaedb415034ada49" +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "static_init" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ - "as-any", - "bitvec", - "itertools", - "num-bigint", - "sha2 0.9.9", - "ssz-rs-derive", + "bitflags", + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "parking_lot_core 0.8.6", + "static_init_macro", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +dependencies = [ + "cfg_aliases", + "memchr", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", ] [[package]] -name = "ssz-rs-derive" -version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#96f9a89ccc56ab2abb4c3a83eaedb415034ada49" +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ + "heck", "proc-macro2", "quote", + "rustversion", "syn 1.0.109", ] [[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "statrs" -version = "0.15.0" +name = "stun" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" dependencies = [ - "approx", + "base64 0.13.1", + "crc", "lazy_static", - "nalgebra", - "num-traits", + "md-5", "rand 0.8.5", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", ] [[package]] @@ -3747,7 +7885,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.37#f38bd6671d460293c93062cc1e4fe9e9e490cb29" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" dependencies = [ "hyper", "log", @@ -3756,6 +7894,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + [[package]] name = "subtle" version = "2.4.1" @@ -3775,9 +7922,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -3785,38 +7932,36 @@ dependencies = [ ] [[package]] -name = "sync-committee-primitives" -version = "0.1.0" -source = "git+https://github.com/polytope-labs/sync-committee-rs?branch=main#4e45ae9797198a617abfcbff098253cedc8a1f2e" +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "ethereum-consensus", - "hex-literal", - "parity-scale-codec", - "ssz-rs", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", ] [[package]] -name = "sync-committee-verifier" -version = "0.1.0" -source = "git+https://github.com/polytope-labs/sync-committee-rs?branch=main#4e45ae9797198a617abfcbff098253cedc8a1f2e" +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" dependencies = [ - "ethereum-consensus", - "log", - "milagro_bls", - "ssz-rs", - "sync-committee-primitives", + "bitflags", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", + "core-foundation-sys", + "libc", ] [[package]] @@ -3831,6 +7976,19 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix 0.37.11", + "windows-sys 0.45.0", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -3840,6 +7998,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thiserror" version = "1.0.40" @@ -3857,7 +8021,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", ] [[package]] @@ -3870,6 +8034,87 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b82ca8f46f95b3ce96081fe3dd89160fdea970c254bb72925255d1b62aae692e" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float", + "threadpool", +] + +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.3+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + [[package]] name = "tiny-bip39" version = "1.0.0" @@ -3890,12 +8135,13 @@ dependencies = [ ] [[package]] -name = "tiny-keccak" -version = "2.0.2" +name = "tinytemplate" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "crunchy", + "serde", + "serde_json", ] [[package]] @@ -3924,8 +8170,9 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", - "pin-project-lite", + "parking_lot 0.12.1", + "pin-project-lite 0.2.9", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.45.0", @@ -3939,7 +8186,18 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.8", + "tokio", + "webpki 0.22.0", ] [[package]] @@ -3949,8 +8207,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.9", "tokio", + "tokio-util", ] [[package]] @@ -3963,7 +8222,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite", + "pin-project-lite 0.2.9", "tokio", "tracing", ] @@ -4005,6 +8264,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite 0.2.9", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" @@ -4025,7 +8302,7 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", - "pin-project-lite", + "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", ] @@ -4051,6 +8328,39 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-gum" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-primitives", + "tracing", + "tracing-gum-proc-macro", +] + +[[package]] +name = "tracing-gum-proc-macro" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "expander 0.0.6", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tracing-log" version = "0.1.3" @@ -4082,6 +8392,7 @@ dependencies = [ "chrono", "lazy_static", "matchers", + "parking_lot 0.11.2", "regex", "serde", "serde_json", @@ -4096,12 +8407,12 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.24.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ "hash-db", - "hashbrown 0.12.3", + "hashbrown 0.13.2", "log", "rustc-hex", "smallvec", @@ -4109,13 +8420,59 @@ dependencies = [ [[package]] name = "trie-root" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ "hash-db", ] +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand 0.8.5", + "smallvec", + "socket2", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot 0.12.1", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -4128,6 +8485,25 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -4136,7 +8512,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -4158,6 +8534,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.8" @@ -4185,11 +8567,69 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsigned-varint" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures-io", + "futures-util", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" +dependencies = [ + "getrandom 0.2.9", +] [[package]] name = "valuable" @@ -4197,12 +8637,39 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "want" version = "0.3.0" @@ -4219,6 +8686,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4250,6 +8723,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.84" @@ -4288,6 +8773,21 @@ dependencies = [ "parity-wasm", ] +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmi" version = "0.13.2" @@ -4309,196 +8809,485 @@ dependencies = [ ] [[package]] -name = "wasmi_core" -version = "0.2.1" +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm 0.2.6", + "memory_units", + "num-rational", + "num-traits", + "region", +] + +[[package]] +name = "wasmparser" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +dependencies = [ + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +dependencies = [ + "anyhow", + "base64 0.13.1", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix 0.36.12", + "serde", + "sha2 0.10.6", + "toml", + "windows-sys 0.42.0", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", + "log", + "object 0.29.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +dependencies = [ + "object 0.29.0", + "once_cell", + "rustix 0.36.12", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix 0.36.12", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-types" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki 0.22.0", +] + +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "hex", + "interceptor", + "lazy_static", + "log", + "rand 0.8.5", + "rcgen 0.9.3", + "regex", + "ring", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde", + "serde_json", + "sha2 0.10.6", + "stun", + "thiserror", + "time 0.3.20", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" dependencies = [ - "downcast-rs", - "libm", - "memory_units", - "num-rational", - "num-traits", + "bytes", + "derive_builder", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", ] [[package]] -name = "wasmparser" -version = "0.89.1" +name = "webrtc-dtls" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" dependencies = [ - "indexmap", + "aes 0.6.0", + "aes-gcm 0.10.1", + "async-trait", + "bincode", + "block-modes", + "byteorder", + "ccm", + "curve25519-dalek 3.2.0", + "der-parser 8.2.0", + "elliptic-curve", + "hkdf", + "hmac 0.12.1", + "log", + "oid-registry 0.6.1", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen 0.9.3", + "ring", + "rustls 0.19.1", + "sec1", + "serde", + "sha1", + "sha2 0.10.6", + "signature", + "subtle", + "thiserror", + "tokio", + "webpki 0.21.4", + "webrtc-util", + "x25519-dalek 2.0.0-pre.1", + "x509-parser 0.13.2", ] [[package]] -name = "wasmtime" -version = "1.0.2" +name = "webrtc-ice" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad5af6ba38311282f2a21670d96e78266e8c8e2f38cbcd52c254df6ccbc7731" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "indexmap", - "libc", + "arc-swap", + "async-trait", + "crc", "log", - "object 0.29.0", - "once_cell", - "paste", - "psm", - "rayon", + "rand 0.8.5", "serde", - "target-lexicon", - "wasmparser", - "wasmtime-cache", - "wasmtime-cranelift", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", - "windows-sys 0.36.1", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", ] [[package]] -name = "wasmtime-asm-macros" -version = "1.0.2" +name = "webrtc-mdns" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45de63ddfc8b9223d1adc8f7b2ee5f35d1f6d112833934ad7ea66e4f4339e597" +checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" dependencies = [ - "cfg-if", + "log", + "socket2", + "thiserror", + "tokio", + "webrtc-util", ] [[package]] -name = "wasmtime-cache" -version = "1.0.2" +name = "webrtc-media" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd849399d17d2270141cfe47fa0d91ee52d5f8ea9b98cf7ddde0d53e5f79882" +checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" dependencies = [ - "anyhow", - "base64", - "bincode", - "directories-next", - "file-per-thread-logger", - "log", - "rustix 0.35.13", - "serde", - "sha2 0.9.9", - "toml", - "windows-sys 0.36.1", - "zstd", + "byteorder", + "bytes", + "derive_builder", + "displaydoc", + "rand 0.8.5", + "rtp", + "thiserror", + "webrtc-util", ] [[package]] -name = "wasmtime-cranelift" -version = "1.0.2" +name = "webrtc-sctp" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd91339b742ff20bfed4532a27b73c86b5bcbfedd6bea2dcdf2d64471e1b5c6" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "gimli 0.26.2", + "arc-swap", + "async-trait", + "bytes", + "crc", "log", - "object 0.29.0", - "target-lexicon", + "rand 0.8.5", "thiserror", - "wasmparser", - "wasmtime-environ", + "tokio", + "webrtc-util", ] [[package]] -name = "wasmtime-environ" -version = "1.0.2" +name = "webrtc-srtp" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb881c61f4f627b5d45c54e629724974f8a8890d455bcbe634330cc27309644" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" dependencies = [ - "anyhow", - "cranelift-entity", - "gimli 0.26.2", - "indexmap", + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder", + "bytes", + "ctr 0.8.0", + "hmac 0.11.0", "log", - "object 0.29.0", - "serde", - "target-lexicon", + "rtcp", + "rtp", + "sha-1", + "subtle", "thiserror", - "wasmparser", - "wasmtime-types", + "tokio", + "webrtc-util", ] [[package]] -name = "wasmtime-jit" -version = "1.0.2" +name = "webrtc-util" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1985c628011fe26adf5e23a5301bdc79b245e0e338f14bb58b39e4e25e4d8681" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" dependencies = [ - "addr2line 0.17.0", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli 0.26.2", + "async-trait", + "bitflags", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", "log", - "object 0.29.0", - "rustc-demangle", - "rustix 0.35.13", - "serde", - "target-lexicon", + "nix", + "rand 0.8.5", "thiserror", - "wasmtime-environ", - "wasmtime-jit-debug", - "wasmtime-runtime", - "windows-sys 0.36.1", + "tokio", + "winapi", ] [[package]] -name = "wasmtime-jit-debug" -version = "1.0.2" +name = "which" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f671b588486f5ccec8c5a3dba6b4c07eac2e66ab8c60e6f4e53717c77f709731" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ - "object 0.29.0", + "either", + "libc", "once_cell", - "rustix 0.35.13", ] [[package]] -name = "wasmtime-runtime" -version = "1.0.2" +name = "wide" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8f92ad4b61736339c29361da85769ebc200f184361959d1792832e592a1afd" +checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap", - "libc", - "log", - "mach", - "memfd", - "memoffset 0.6.5", - "paste", - "rand 0.8.5", - "rustix 0.35.13", - "thiserror", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", - "windows-sys 0.36.1", + "bytemuck", + "safe_arch", ] [[package]] -name = "wasmtime-types" -version = "1.0.2" +name = "widestring" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23d61cb4c46e837b431196dd06abb11731541021916d03476a178b54dc07aeb" -dependencies = [ - "cranelift-entity", - "serde", - "thiserror", - "wasmparser", -] +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "winapi" @@ -4533,24 +9322,24 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" dependencies = [ - "windows-targets 0.48.0", + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", ] [[package]] -name = "windows-sys" -version = "0.36.1" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows-targets 0.48.0", ] [[package]] @@ -4630,9 +9419,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" @@ -4648,9 +9437,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" @@ -4666,9 +9455,9 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" @@ -4684,9 +9473,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" @@ -4714,9 +9503,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" @@ -4739,6 +9528,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wyz" version = "0.5.1" @@ -4748,6 +9546,115 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64 0.13.1", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs 0.5.2", + "base64 0.13.1", + "data-encoding", + "der-parser 8.2.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "xcm" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "bounded-collections", + "derivative", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-weights", + "xcm-procedural", +] + +[[package]] +name = "xcm-procedural" +version = "0.9.40" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "yamux" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time 0.3.20", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -4765,7 +9672,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 15c9392c7..3b4eed353 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,72 +1,10 @@ -[package] -name = "pallet-ismp" -version = "0.1.0" -edition = "2021" - - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } -log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false, features = ["disable_panic_handler"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } -mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } -serde = { version = "1.0.136", features = ["derive"], optional = true } -derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display"] } -sync-committee-primitives = { git="https://github.com/polytope-labs/sync-committee-rs", branch="main", default-features = false } -sync-committee-verifier = { git="https://github.com/polytope-labs/sync-committee-rs", branch="main", default-features = false } -patricia-merkle-trie = { package = "ethereum-trie", git ="https://github.com/polytope-labs/ethereum-trie", branch = "main", default-features=false } -trie-db = { version= "0.24.0", default-features = false } -hash-db = { version = "0.15.2", default-features = false } -rlp = { version = "0.5.1", default-features = false } -hex = { version = "0.4.3", default-features = false } -hex-literal = "0.3.4" -rlp-derive = "0.1.0" -ethabi = { version = "18.0.0", features = ["rlp"], default-features = false } -hash256-std-hasher = { version = "0.15.2", default-features = false } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-core/std", - "ismp-rs/std", - "mmr-lib/std", - "sp-api/std", - "serde", - "patricia-merkle-trie/std", - "trie-db/std", - "hash-db/std", - "rlp/std", - "ethabi/std", - "hash256-std-hasher/std", - "hex/std", - "sync-committee-primitives/std", - "sync-committee-verifier/std" -] - -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] -testnet = [] - [workspace] resolver = "2" members = [ - "rpc", - "runtime-api" -] \ No newline at end of file + "pallet-ismp/rpc", + "pallet-ismp/runtime-api", + "pallet-ismp/primitives", + "pallet-ismp", + "parachain-consensus", +] diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml new file mode 100644 index 000000000..632606c95 --- /dev/null +++ b/pallet-ismp/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "pallet-ismp" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, features = ["disable_panic_handler"] } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } + + # polytope labs +ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } + +# crates.io +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } +derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display"] } + +# local +ismp-primitives = { path = "./primitives", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", + "ismp-rs/std", + "mmr-lib/std", + "sp-api/std", + "serde", + "ismp-primitives/std", +] + +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml new file mode 100644 index 000000000..6aae9e1e3 --- /dev/null +++ b/pallet-ismp/primitives/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ismp-primitives" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } + +# polytope labs +ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } + +# crates.io +merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +primitive-types = { version = "0.12.1", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-system/std", + "ismp/std", + "merkle-mountain-range/std", + "codec/std", + "sp-runtime/std", + "primitive-types/std", +] diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs new file mode 100644 index 000000000..e0db97c1a --- /dev/null +++ b/pallet-ismp/primitives/src/lib.rs @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod mmr; diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs new file mode 100644 index 000000000..4d51ae836 --- /dev/null +++ b/pallet-ismp/primitives/src/mmr.rs @@ -0,0 +1,104 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use core::fmt::Formatter; + +use codec::{Decode, Encode}; +use ismp::{ + host::ISMPHost, + router::{Request, Response}, + util::{hash_request, hash_response}, +}; +use primitive_types::H256; +use sp_runtime::traits; + +pub type LeafIndex = u64; +pub type NodeIndex = u64; + +#[derive(Debug, Clone, Decode, Encode, PartialEq, Eq)] +pub enum Leaf { + Request(Request), + Response(Response), +} + +impl Leaf { + fn hash(&self) -> H256 { + match self { + Leaf::Request(req) => hash_request::(req), + Leaf::Response(res) => hash_response::(res), + } + } +} + +/// An element representing either full data or its hash. +#[derive(Clone, PartialEq, Eq, Encode, Decode)] +pub enum DataOrHash { + /// Arbitrary data in its full form. + Data(Leaf), + /// A hash of some data. + Hash(<::Hashing as traits::Hash>::Output), +} + +impl core::fmt::Debug for DataOrHash { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + DataOrHash::Data(leaf) => f.debug_struct("DataOrHash").field("Data", leaf).finish(), + DataOrHash::Hash(hash) => f.debug_struct("DataOrHash").field("Hash", hash).finish(), + } + } +} + +impl From for DataOrHash { + fn from(l: Leaf) -> Self { + Self::Data(l) + } +} + +impl DataOrHash +where + T: frame_system::Config, + T::Hash: From, +{ + /// Retrieve a hash of this item. + /// + /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] + /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. + pub fn hash( + &self, + ) -> <::Hashing as traits::Hash>::Output { + match *self { + Self::Data(ref leaf) => ::from(leaf.hash::()), + Self::Hash(ref hash) => *hash, + } + } +} + +/// Default Merging & Hashing behavior for MMR. +pub struct MmrHasher(core::marker::PhantomData<(T, H)>); + +impl merkle_mountain_range::Merge for MmrHasher +where + T: frame_system::Config, + T::Hash: From, + H: ISMPHost, +{ + type Item = DataOrHash; + + fn merge(left: &Self::Item, right: &Self::Item) -> merkle_mountain_range::Result { + let mut concat = left.hash::().as_ref().to_vec(); + concat.extend_from_slice(right.hash::().as_ref()); + + Ok(DataOrHash::Hash(<::Hashing as traits::Hash>::hash(&concat))) + } +} diff --git a/rpc/Cargo.toml b/pallet-ismp/rpc/Cargo.toml similarity index 78% rename from rpc/Cargo.toml rename to pallet-ismp/rpc/Cargo.toml index c449ab41b..b4969b8af 100644 --- a/rpc/Cargo.toml +++ b/pallet-ismp/rpc/Cargo.toml @@ -3,7 +3,7 @@ name = "ismp-rpc" description = "RPC apis for pallet-ismp" edition = "2021" version = "0.1.0" -authors = ["Polytope labs"] +authors = ["Polytope Labs "] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -17,10 +17,11 @@ serde_json = "1.0.45" ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } pallet-ismp = { path = ".." } ismp-runtime-api = { path = "../runtime-api" } +ismp-primitives = { path = "../primitives" } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } -sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37" } \ No newline at end of file +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } \ No newline at end of file diff --git a/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs similarity index 81% rename from rpc/src/lib.rs rename to pallet-ismp/rpc/src/lib.rs index 5d81b05cc..95506b877 100644 --- a/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -9,17 +9,17 @@ use jsonrpsee::{ }; use codec::Encode; +use ismp_primitives::mmr::{Leaf, LeafIndex}; use ismp_rs::{ consensus_client::ConsensusClientId, router::{Request, Response}, }; use ismp_runtime_api::{IsmpRuntimeApi, LeafIndexQuery}; -use pallet_ismp::mmr::{Leaf, LeafIndex}; use sc_client_api::{BlockBackend, ProofProvider}; use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; -use sp_runtime::{generic::BlockId, traits::Block as BlockT}; +use sp_runtime::traits::Block as BlockT; use std::{collections::HashMap, fmt::Display, sync::Arc}; /// A type that could be a block number or a block hash @@ -135,13 +135,13 @@ where { fn query_requests(&self, query: Vec) -> Result> { let api = self.client.runtime_api(); - let at = BlockId::Hash(self.client.info().best_hash); + let at = self.client.info().best_hash; let request_indices: Vec = - api.get_request_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + api.get_request_leaf_indices(at, query).ok().flatten().ok_or_else(|| { runtime_error_into_rpc_error("Error fetching request leaf indices") })?; - api.get_requests(&at, request_indices) + api.get_requests(at, request_indices) .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("Error fetching requests")) @@ -149,13 +149,13 @@ where fn query_responses(&self, query: Vec) -> Result> { let api = self.client.runtime_api(); - let at = BlockId::Hash(self.client.info().best_hash); + let at = self.client.info().best_hash; let response_indices: Vec = - api.get_response_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + api.get_response_leaf_indices(at, query).ok().flatten().ok_or_else(|| { runtime_error_into_rpc_error("Error fetching response leaf indices") })?; - api.get_responses(&at, response_indices) + api.get_responses(at, response_indices) .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("Error fetching responses")) @@ -163,14 +163,19 @@ where fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result { let api = self.client.runtime_api(); - let at = BlockId::Number(height.into()); + let at = self + .client + .block_hash(height.into()) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; let request_indices: Vec = - api.get_request_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + api.get_request_leaf_indices(at, query).ok().flatten().ok_or_else(|| { runtime_error_into_rpc_error("Error fetching response leaf indices") })?; let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api - .generate_proof(&at, request_indices) + .generate_proof(at, request_indices) .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) @@ -178,14 +183,19 @@ where fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result { let api = self.client.runtime_api(); - let at = BlockId::Number(height.into()); + let at = self + .client + .block_hash(height.into()) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; let response_indices: Vec = - api.get_response_leaf_indices(&at, query).ok().flatten().ok_or_else(|| { + api.get_response_leaf_indices(at, query).ok().flatten().ok_or_else(|| { runtime_error_into_rpc_error("Error fetching response leaf indices") })?; let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api - .generate_proof(&at, response_indices) + .generate_proof(at, response_indices) .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) @@ -202,9 +212,9 @@ where ) -> Result> { let api = self.client.runtime_api(); let at = height - .map(|height| BlockId::Number(height.into())) - .unwrap_or(BlockId::Hash(self.client.info().best_hash)); - api.consensus_state(&at, client_id) + .and_then(|height| self.client.block_hash(height.into()).ok().flatten()) + .unwrap_or(self.client.info().best_hash); + api.consensus_state(at, client_id) .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) @@ -212,8 +222,8 @@ where fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result { let api = self.client.runtime_api(); - let at = BlockId::Hash(self.client.info().best_hash); - api.consensus_update_time(&at, client_id) + let at = self.client.info().best_hash; + api.consensus_update_time(at, client_id) .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) @@ -227,12 +237,16 @@ where let mut events = HashMap::new(); for block_number_or_hash in block_numbers { let at = match block_number_or_hash { - BlockNumberOrHash::Hash(block_hash) => BlockId::Hash(block_hash), - BlockNumberOrHash::Number(block_number) => BlockId::Number(block_number.into()), + BlockNumberOrHash::Hash(block_hash) => block_hash, + BlockNumberOrHash::Number(block_number) => { + self.client.block_hash(block_number.into()).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Invalid block number provided") + })? + } }; let temp = api - .block_events(&at) + .block_events(at) .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("failed to read block events"))?; diff --git a/runtime-api/Cargo.toml b/pallet-ismp/runtime-api/Cargo.toml similarity index 72% rename from runtime-api/Cargo.toml rename to pallet-ismp/runtime-api/Cargo.toml index 810921fd0..5db40a613 100644 --- a/runtime-api/Cargo.toml +++ b/pallet-ismp/runtime-api/Cargo.toml @@ -2,15 +2,16 @@ edition = "2021" name = "ismp-runtime-api" version = "0.1.0" -authors = ["Polytope Labs"] +authors = ["Polytope Labs "] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.37", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } pallet-ismp = { path = "..", default-features = false } +ismp-primitives = { path = "../primitives", default-features = false } ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } @@ -22,4 +23,4 @@ default-features = false [features] default = ['std'] -std = ['sp-api/std', 'sp-std/std', 'codec/std', "pallet-ismp/std", "ismp-rs/std", "serde"] +std = ['sp-api/std', 'sp-std/std', 'codec/std', "pallet-ismp/std", "ismp-rs/std", "serde", "ismp-primitives/std"] diff --git a/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs similarity index 95% rename from runtime-api/src/lib.rs rename to pallet-ismp/runtime-api/src/lib.rs index 611de0806..b105b23ae 100644 --- a/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -6,10 +6,9 @@ use ismp_rs::{ host::ChainID, router::{Request, Response}, }; -use pallet_ismp::{ - mmr::{Leaf, LeafIndex}, - primitives::{Error, Proof}, -}; +use pallet_ismp::primitives::{Error, Proof}; + +use ismp_primitives::mmr::{Leaf, LeafIndex}; #[cfg(not(feature = "std"))] use sp_std::vec::Vec; diff --git a/src/errors.rs b/pallet-ismp/src/errors.rs similarity index 80% rename from src/errors.rs rename to pallet-ismp/src/errors.rs index c4c0c07bf..3f52b7f12 100644 --- a/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -47,7 +47,7 @@ pub enum HandlingError { ExpiredConsensusClient { id: ConsensusClientId, }, - CannotHandleConsensusMessage, + CannotHandleMessage, ImplementationSpecific { msg: Vec, }, @@ -63,6 +63,18 @@ pub enum HandlingError { CannotCreateAlreadyExistingConsensusClient { id: ConsensusClientId, }, + RequestTimeoutNotElapsed { + nonce: u64, + source: ChainID, + dest: ChainID, + timeout_timestamp: u64, + state_machine_time: u64, + }, + RequestTimeoutVerificationFailed { + nonce: u64, + source: ChainID, + dest: ChainID, + }, } impl From for HandlingError { @@ -101,7 +113,7 @@ impl From for HandlingError { IsmpError::ExpiredConsensusClient { id } => { HandlingError::ExpiredConsensusClient { id } } - IsmpError::CannotHandleConsensusMessage => HandlingError::CannotHandleConsensusMessage, + IsmpError::CannotHandleMessage => HandlingError::CannotHandleMessage, IsmpError::ImplementationSpecific(msg) => { HandlingError::ImplementationSpecific { msg: msg.as_bytes().to_vec() } } @@ -117,6 +129,22 @@ impl From for HandlingError { IsmpError::CannotCreateAlreadyExistingConsensusClient { id } => { HandlingError::CannotCreateAlreadyExistingConsensusClient { id } } + IsmpError::RequestTimeoutNotElapsed { + nonce, + source, + dest, + timeout_timestamp, + state_machine_time, + } => HandlingError::RequestTimeoutNotElapsed { + nonce, + source, + dest, + timeout_timestamp: timeout_timestamp.as_secs(), + state_machine_time: state_machine_time.as_secs(), + }, + IsmpError::RequestTimeoutVerificationFailed { nonce, source, dest } => { + HandlingError::RequestTimeoutVerificationFailed { nonce, source, dest } + } } } } diff --git a/src/events.rs b/pallet-ismp/src/events.rs similarity index 100% rename from src/events.rs rename to pallet-ismp/src/events.rs diff --git a/src/host.rs b/pallet-ismp/src/host.rs similarity index 75% rename from src/host.rs rename to pallet-ismp/src/host.rs index d7c16a8cf..2ef0fef2b 100644 --- a/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -1,11 +1,6 @@ use crate::{ - consensus_clients::{ - beacon_consensus_client::beacon_client::BeaconConsensusClient, - consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, - }, - router::Router, - Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, - RequestAcks, StateCommitments, + primitives::ConsensusClientProvider, Config, ConsensusClientUpdateTime, ConsensusStates, + FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -17,7 +12,9 @@ use ismp_rs::{ error::Error, host::{ChainID, ISMPHost}, router::{ISMPRouter, Request}, + util::hash_request, }; +use sp_core::H256; use sp_runtime::SaturatedConversion; use sp_std::prelude::*; @@ -30,7 +27,10 @@ impl Default for Host { } } -impl ISMPHost for Host { +impl ISMPHost for Host +where + ::Hash: From, +{ fn host(&self) -> ChainID { ::CHAIN_ID } @@ -74,10 +74,10 @@ impl ISMPHost for Host { } } - fn request_commitment(&self, req: &Request) -> Result, Error> { - let commitment = self.get_request_commitment(req); + fn request_commitment(&self, req: &Request) -> Result { + let commitment = hash_request::(req); - let _ = RequestAcks::::get(commitment.clone()).ok_or_else(|| { + let _ = RequestAcks::::get(commitment.0.to_vec()).ok_or_else(|| { Error::RequestCommitmentNotFound { nonce: req.nonce(), source: req.source_chain(), @@ -117,34 +117,26 @@ impl ISMPHost for Host { } fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { - match id { - id if id == ETHEREUM_CONSENSUS_CLIENT_ID => { - Ok(Box::new(BeaconConsensusClient::default())) - } - _ => Err(Error::ImplementationSpecific(format!( - "No consensus client found for consensus id {}", - id - ))), - } - } - - fn keccak256(&self, bytes: &[u8]) -> [u8; 32] { - sp_io::hashing::keccak_256(bytes) + ::ConsensusClientProvider::consensus_client(id) } fn challenge_period(&self, id: ConsensusClientId) -> Duration { - match id { - id if id == ETHEREUM_CONSENSUS_CLIENT_ID => Duration::from_secs(30 * 60), - _ => Duration::from_secs(15 * 60), - } + ::ConsensusClientProvider::challenge_period(id) } fn ismp_router(&self) -> Box { - Box::new(Router::::default()) + Box::new(T::IsmpRouter::default()) } fn store_latest_commitment_height(&self, height: StateMachineHeight) -> Result<(), Error> { LatestStateMachineHeight::::insert(height.id, height.height); Ok(()) } + + fn keccak256(bytes: &[u8]) -> H256 + where + Self: Sized, + { + sp_io::hashing::keccak_256(bytes).into() + } } diff --git a/src/lib.rs b/pallet-ismp/src/lib.rs similarity index 86% rename from src/lib.rs rename to pallet-ismp/src/lib.rs index c40b70188..322a8bd53 100644 --- a/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -18,26 +18,24 @@ extern crate alloc; -mod consensus_clients; mod errors; pub mod events; pub mod host; pub mod mmr; pub mod primitives; -mod router; +pub mod router; -use crate::{ - host::Host, - mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex, NodeOf}, -}; +use crate::host::Host; use codec::{Decode, Encode}; use frame_support::{log::debug, RuntimeDebug}; use ismp_rs::{ host::ChainID, router::{Request, Response}, }; -use sp_core::offchain::StorageKind; +use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. +use ismp_primitives::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}; +use mmr::mmr::Mmr; pub use pallet::*; use sp_std::prelude::*; @@ -50,13 +48,13 @@ pub mod pallet { use super::*; use crate::{ errors::HandlingError, - mmr::{LeafIndex, Mmr, NodeIndex}, - primitives::ISMP_ID, + primitives::{ConsensusClientProvider, ISMP_ID}, router::Receipt, }; use alloc::{collections::BTreeSet, string::ToString}; use frame_support::{pallet_prelude::*, traits::UnixTime}; use frame_system::pallet_prelude::*; + use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::{ consensus_client::{ ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, @@ -64,8 +62,9 @@ pub mod pallet { handlers::{handle_incoming_message, MessageResult}, host::ChainID, messaging::Message, + router::ISMPRouter, }; - use sp_runtime::traits; + use sp_core::H256; /// Our pallet's configuration trait. All our types and constants go in here. If the /// pallet is dependent on specific other pallets, then their configuration traits @@ -88,48 +87,25 @@ pub mod pallet { const INDEXING_PREFIX: &'static [u8]; type AdminOrigin: EnsureOrigin; - /// A hasher type for MMR. - /// - /// To construct trie nodes that result in merging (bagging) two peaks, depending on the - /// node kind we take either: - /// - The node (hash) itself if it's an inner node. - /// - The hash of SCALE-encoding of the leaf data if it's a leaf node. - /// - /// Then we create a tuple of these two hashes, SCALE-encode it (concatenate) and - /// hash, to obtain a new MMR inner node - the new peak. - type Hashing: traits::Hash::Hash>; const CHAIN_ID: ChainID; - /// The hashing output type. - /// - /// This type is actually going to be stored in the MMR. - /// Required to be provided again, to satisfy trait bounds for storage items. - type Hash: traits::Member - + traits::MaybeSerializeDeserialize - + sp_std::fmt::Debug - + sp_std::hash::Hash - + AsRef<[u8]> - + AsMut<[u8]> - + From<[u8; 32]> - + Copy - + Default - + codec::Codec - + codec::EncodeLike - + scale_info::TypeInfo - + MaxEncodedLen; type TimeProvider: UnixTime; + + /// Configurable router that dispatches calls to modules + type IsmpRouter: ISMPRouter + Default; + /// Provides concrete implementations of consensus clients + type ConsensusClientProvider: ConsensusClientProvider; } // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and // method. #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); /// Latest MMR Root hash #[pallet::storage] #[pallet::getter(fn mmr_root_hash)] - pub type RootHash = StorageValue<_, ::Hash, ValueQuery>; + pub type RootHash = StorageValue<_, ::Hash, ValueQuery>; /// Current size of the MMR (number of leaves) for requests. #[pallet::storage] @@ -143,7 +119,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn request_peaks)] pub type Nodes = - StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; + StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; #[pallet::storage] #[pallet::getter(fn state_commitments)] @@ -199,17 +175,19 @@ pub mod pallet { // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] - impl Hooks> for Pallet { + impl Hooks> for Pallet + where + ::Hash: From, + { fn on_initialize(_n: T::BlockNumber) -> Weight { // return Mmr finalization weight here Weight::zero() } fn on_finalize(_n: T::BlockNumber) { - use crate::mmr; let leaves = Self::number_of_leaves(); - let mmr: Mmr = mmr::Mmr::new(leaves); + let mmr: Mmr = Mmr::new(leaves); // Update the size, `mmr.finalize()` should also never fail. let (leaves, root) = match mmr.finalize() { @@ -233,7 +211,10 @@ pub mod pallet { } #[pallet::call] - impl Pallet { + impl Pallet + where + ::Hash: From, + { /// Handles ismp messages #[pallet::weight(0)] #[pallet::call_index(0)] @@ -374,7 +355,10 @@ pub mod pallet { } } -impl Pallet { +impl Pallet +where + ::Hash: From, +{ /// Generate an MMR proof for the given `leaf_indices`. /// Note this method can only be used from an off-chain context /// (Offchain Worker or Runtime API call), since it requires @@ -382,28 +366,29 @@ impl Pallet { /// It may return an error or panic if used incorrectly. pub fn generate_proof( leaf_indices: Vec, - ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> { + ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> + { let leaves_count = NumberOfLeaves::::get(); - let mmr = mmr::Mmr::::new(leaves_count); + let mmr = Mmr::::new(leaves_count); mmr.generate_proof(leaf_indices) } /// Return the on-chain MMR root hash. - pub fn mmr_root() -> ::Hash { + pub fn mmr_root() -> ::Hash { Self::mmr_root_hash() } } impl Pallet { - fn get_node(pos: NodeIndex) -> Option> { - Nodes::::get(pos).map(NodeOf::Hash) + fn get_node(pos: NodeIndex) -> Option> { + Nodes::::get(pos).map(DataOrHash::Hash) } fn remove_node(pos: NodeIndex) { Nodes::::remove(pos); } - fn insert_node(pos: NodeIndex, node: ::Hash) { + fn insert_node(pos: NodeIndex, node: ::Hash) { Nodes::::insert(pos, node) } @@ -422,7 +407,7 @@ impl Pallet { #[derive(RuntimeDebug, Encode, Decode)] pub struct RequestResponseLog { - mmr_root_hash: ::Hash, + mmr_root_hash: ::Hash, } impl Pallet { @@ -449,7 +434,7 @@ impl Pallet { pub fn get_request(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; + let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; return match data_or_hash { DataOrHash::Data(leaf) => match leaf { Leaf::Request(req) => Some(req), @@ -464,7 +449,7 @@ impl Pallet { pub fn get_response(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; + let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; return match data_or_hash { DataOrHash::Data(leaf) => match leaf { Leaf::Response(res) => Some(res), diff --git a/pallet-ismp/src/mmr.rs b/pallet-ismp/src/mmr.rs new file mode 100644 index 000000000..1ac76f215 --- /dev/null +++ b/pallet-ismp/src/mmr.rs @@ -0,0 +1,3 @@ +pub mod mmr; +pub mod storage; +mod utils; diff --git a/src/mmr/mmr.rs b/pallet-ismp/src/mmr/mmr.rs similarity index 70% rename from src/mmr/mmr.rs rename to pallet-ismp/src/mmr/mmr.rs index 2c66069b4..d7692f699 100644 --- a/src/mmr/mmr.rs +++ b/pallet-ismp/src/mmr/mmr.rs @@ -14,35 +14,37 @@ // limitations under the License. use crate::{ + host::Host, mmr::{ storage::{OffchainStorage, RuntimeStorage, Storage}, utils::NodesUtils, - FullLeaf, Hasher, NodeIndex, NodeOf, }, primitives::{Error, Proof}, Config, }; +use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher, NodeIndex}; +use sp_core::H256; use sp_std::prelude::*; /// A wrapper around an MMR library to expose limited functionality. /// /// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage) /// vs [Off-chain](crate::mmr::storage::OffchainStorage)). -pub struct Mmr +pub struct Mmr where T: Config, - L: FullLeaf, - Storage: mmr_lib::MMRStore>, + Storage: mmr_lib::MMRStore>, + ::Hash: From, { - mmr: mmr_lib::MMR, Hasher, Storage>, + mmr: mmr_lib::MMR, MmrHasher>, Storage>, leaves: NodeIndex, } -impl Mmr +impl Mmr where T: Config, - L: FullLeaf, - Storage: mmr_lib::MMRStore>, + Storage: mmr_lib::MMRStore>, + ::Hash: From, { /// Create a pointer to an existing MMR with given number of leaves. pub fn new(leaves: NodeIndex) -> Self { @@ -58,16 +60,16 @@ where } /// Runtime specific MMR functions. -impl Mmr +impl Mmr where T: Config, - L: FullLeaf, + ::Hash: From, { /// Push another item to the MMR. /// /// Returns element position (index) in the MMR. - pub fn push(&mut self, leaf: L) -> Option { - let position = self.mmr.push(NodeOf::Data(leaf)).map_err(|_| Error::Push).ok()?; + pub fn push(&mut self, leaf: Leaf) -> Option { + let position = self.mmr.push(DataOrHash::Data(leaf)).map_err(|_| Error::Push).ok()?; self.leaves += 1; @@ -76,18 +78,18 @@ where /// Commit the changes to underlying storage, return current number of leaves and /// calculate the new MMR's root hash. - pub fn finalize(self) -> Result<(NodeIndex, ::Hash), Error> { + pub fn finalize(self) -> Result<(NodeIndex, ::Hash), Error> { let root = self.mmr.get_root().map_err(|_| Error::GetRoot)?; self.mmr.commit().map_err(|_| Error::Commit)?; - Ok((self.leaves, root.hash())) + Ok((self.leaves, root.hash::>())) } } /// Off-chain specific MMR functions. -impl Mmr +impl Mmr where T: Config, - L: FullLeaf + codec::Decode, + ::Hash: From, { /// Generate a proof for given leaf indices. /// @@ -96,14 +98,14 @@ where pub fn generate_proof( &self, leaf_indices: Vec, - ) -> Result<(Vec, Proof<::Hash>), Error> { + ) -> Result<(Vec, Proof<::Hash>), Error> { let positions = leaf_indices.iter().map(|index| mmr_lib::leaf_index_to_pos(*index)).collect::>(); - let store = >::default(); + let store = >::default(); let leaves = positions .iter() .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { - Ok(Some(NodeOf::Data(leaf))) => Ok(leaf), + Ok(Some(DataOrHash::Data(leaf))) => Ok(leaf), _ => Err(Error::LeafNotFound), }) .collect::, Error>>()?; @@ -115,7 +117,7 @@ where .map(|p| Proof { leaf_indices, leaf_count, - items: p.proof_items().iter().map(|x| x.hash()).collect(), + items: p.proof_items().iter().map(|x| x.hash::>()).collect(), }) .map(|p| (leaves, p)) } diff --git a/src/mmr/storage.rs b/pallet-ismp/src/mmr/storage.rs similarity index 84% rename from src/mmr/storage.rs rename to pallet-ismp/src/mmr/storage.rs index c6d08f3b3..843a7da86 100644 --- a/src/mmr/storage.rs +++ b/pallet-ismp/src/mmr/storage.rs @@ -16,16 +16,14 @@ //! An MMR storage implementation. use codec::Encode; use frame_support::log::{debug, trace}; +use ismp_primitives::mmr::{DataOrHash, NodeIndex}; use mmr_lib::helper; -use sp_core::offchain::StorageKind; +use sp_core::{offchain::StorageKind, H256}; use sp_std::iter::Peekable; #[cfg(not(feature = "std"))] use sp_std::prelude::*; -use crate::{ - mmr::{utils::NodesUtils, FullLeaf, NodeIndex, NodeOf}, - Config, Pallet, -}; +use crate::{host::Host, mmr::utils::NodesUtils, Config, Pallet}; /// A marker type for runtime-specific storage implementation. /// @@ -47,20 +45,19 @@ pub struct OffchainStorage; /// /// There are two different implementations depending on the use case. /// See docs for [RuntimeStorage] and [OffchainStorage]. -pub struct Storage(sp_std::marker::PhantomData<(StorageType, T, L)>); +pub struct Storage(sp_std::marker::PhantomData<(StorageType, T)>); -impl Default for Storage { +impl Default for Storage { fn default() -> Self { Self(Default::default()) } } -impl mmr_lib::MMRStore> for Storage +impl mmr_lib::MMRStore> for Storage where T: Config, - L: FullLeaf + codec::Decode, { - fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { // Find out which leaf added node `pos` in the MMR. let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos); @@ -77,28 +74,28 @@ where Ok(None) } - fn append(&mut self, _: NodeIndex, _: Vec>) -> mmr_lib::Result<()> { + fn append(&mut self, _: NodeIndex, _: Vec>) -> mmr_lib::Result<()> { panic!("MMR must not be altered in the off-chain context.") } } -impl mmr_lib::MMRStore> for Storage +impl mmr_lib::MMRStore> for Storage where T: Config, - L: FullLeaf, + ::Hash: From, { - fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { Ok(Pallet::::get_node(pos)) } - fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { + fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { if elems.is_empty() { return Ok(()) } trace!( target: "runtime::mmr", "elems: {:?}", - elems.iter().map(|elem| elem.hash()).collect::>() + elems.iter().map(|elem| elem.hash::>()).collect::>() ); let leaves = Pallet::::get_num_leaves(); @@ -121,13 +118,13 @@ where for elem in elems { // On-chain we are going to only store new peaks. if peaks_to_store.next_if_eq(&node_index).is_some() { - Pallet::::insert_node(node_index, elem.hash()); + Pallet::::insert_node(node_index, elem.hash::>()); } // We are storing full node off-chain (using indexing API). Self::store_to_offchain(node_index, &elem); // Increase the indices. - if let NodeOf::Data(..) = elem { + if let DataOrHash::Data(..) = elem { leaf_index += 1; } node_index += 1; @@ -145,12 +142,11 @@ where } } -impl Storage +impl Storage where T: Config, - L: FullLeaf, { - fn store_to_offchain(pos: NodeIndex, node: &NodeOf) { + fn store_to_offchain(pos: NodeIndex, node: &DataOrHash) { let encoded_node = node.encode(); let key = Pallet::::offchain_key(pos); diff --git a/src/mmr/utils.rs b/pallet-ismp/src/mmr/utils.rs similarity index 97% rename from src/mmr/utils.rs rename to pallet-ismp/src/mmr/utils.rs index cb8c466ee..bdf4830ce 100644 --- a/src/mmr/utils.rs +++ b/pallet-ismp/src/mmr/utils.rs @@ -1,5 +1,5 @@ -use crate::mmr::{LeafIndex, NodeIndex}; use alloc::vec::Vec; +use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use mmr_lib::helper; /// MMR nodes & size -related utilities. diff --git a/src/primitives.rs b/pallet-ismp/src/primitives.rs similarity index 72% rename from src/primitives.rs rename to pallet-ismp/src/primitives.rs index 1fb37e40a..dcc443ec6 100644 --- a/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -1,5 +1,7 @@ -use crate::mmr::{LeafIndex, NodeIndex}; +use core::time::Duration; use frame_support::RuntimeDebug; +use ismp_primitives::mmr::{LeafIndex, NodeIndex}; +use ismp_rs::consensus_client::{ConsensusClient, ConsensusClientId}; use scale_info::TypeInfo; use sp_std::prelude::*; @@ -31,3 +33,11 @@ pub enum Error { InvalidLeafIndex, InvalidBestKnownBlock, } + +pub trait ConsensusClientProvider { + fn consensus_client( + id: ConsensusClientId, + ) -> Result, ismp_rs::error::Error>; + + fn challenge_period(id: ConsensusClientId) -> Duration; +} diff --git a/src/router.rs b/pallet-ismp/src/router.rs similarity index 50% rename from src/router.rs rename to pallet-ismp/src/router.rs index 1856fba8b..3fb3af924 100644 --- a/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -1,51 +1,67 @@ -use crate::{ - host::Host, - mmr::{self, Leaf, Mmr}, - Config, Event, Pallet, RequestAcks, ResponseAcks, -}; +use crate::{host::Host, mmr, mmr::mmr::Mmr, Config, Event, Pallet, RequestAcks, ResponseAcks}; use alloc::{format, string::ToString}; use codec::{Decode, Encode}; use core::marker::PhantomData; +use ismp_primitives::mmr::Leaf; use ismp_rs::{ error::Error, host::ISMPHost, router::{ISMPRouter, Request, Response}, + util::{hash_request, hash_response}, }; +use sp_core::H256; #[derive(Encode, Decode, scale_info::TypeInfo)] pub enum Receipt { Ok, } +/// The proxy router, This router allows for routing requests & responses from a source chain +/// to a destination chain. #[derive(Clone)] -pub struct Router(PhantomData); +pub struct ProxyRouter { + inner: Option, + _phantom: PhantomData, +} + +impl ProxyRouter { + /// Initialize the proxy router with an inner router. + pub fn new(router: R) -> Self { + Self { inner: Some(router), _phantom: PhantomData } + } +} -impl Default for Router { +impl Default for ProxyRouter { fn default() -> Self { - Self(PhantomData) + Self { inner: None, _phantom: PhantomData } } } -impl ISMPRouter for Router { +impl ISMPRouter for ProxyRouter +where + T: Config, + R: ISMPRouter, + ::Hash: From, +{ fn dispatch(&self, request: Request) -> Result<(), Error> { let host = Host::::default(); - let commitment = host.get_request_commitment(&request); + if host.host() != request.dest_chain() { + let commitment = hash_request::>(&request).0.to_vec(); - if RequestAcks::::contains_key(commitment.clone()) { - return Err(Error::ImplementationSpecific(format!( - "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", - request.nonce(), - request.source_chain(), - request.dest_chain() - ))) - } + if RequestAcks::::contains_key(commitment.clone()) { + return Err(Error::ImplementationSpecific(format!( + "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", + request.nonce(), + request.source_chain(), + request.dest_chain() + ))) + } - if host.host() != request.dest_chain() { let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = (request.dest_chain(), request.source_chain(), request.nonce()); - let mut mmr: Mmr = mmr::Mmr::new(leaves); + let mut mmr: Mmr = Mmr::new(leaves); let offchain_key = Pallet::::request_leaf_index_offchain_key(source_chain, dest_chain, nonce); let leaf_index = mmr.push(Leaf::Request(request)).ok_or_else(|| { @@ -58,35 +74,41 @@ impl ISMPRouter for Router { dest_chain, }); // Store a map of request to leaf_index - Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) + Pallet::::store_leaf_index_offchain(offchain_key, leaf_index); + RequestAcks::::insert(commitment, Receipt::Ok); + } else if let Some(ref router) = self.inner { + router.dispatch(request)? } - RequestAcks::::insert(commitment, Receipt::Ok); Ok(()) } + fn dispatch_timeout(&self, _request: Request) -> Result<(), Error> { + todo!() + } + fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); - let commitment = host.get_response_commitment(&response); + if host.host() != response.request.source_chain() { + let commitment = hash_response::>(&response).0.to_vec(); - if ResponseAcks::::contains_key(commitment.clone()) { - return Err(Error::ImplementationSpecific(format!( - "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", - response.request.nonce(), - response.request.source_chain(), - response.request.dest_chain() - ))) - } + if ResponseAcks::::contains_key(commitment.clone()) { + return Err(Error::ImplementationSpecific(format!( + "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", + response.request.nonce(), + response.request.source_chain(), + response.request.dest_chain() + ))) + } - if host.host() != response.request.source_chain() { let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = ( response.request.source_chain(), response.request.dest_chain(), response.request.nonce(), ); - let mut mmr: Mmr = mmr::Mmr::new(leaves); + let mut mmr: Mmr = Mmr::new(leaves); let offchain_key = Pallet::::response_leaf_index_offchain_key(source_chain, dest_chain, nonce); let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { @@ -97,11 +119,12 @@ impl ISMPRouter for Router { dest_chain, source_chain, }); - Pallet::::store_leaf_index_offchain(offchain_key, leaf_index) + Pallet::::store_leaf_index_offchain(offchain_key, leaf_index); + ResponseAcks::::insert(commitment, Receipt::Ok); + } else if let Some(ref router) = self.inner { + router.write_response(response)? } - ResponseAcks::::insert(commitment, Receipt::Ok); - Ok(()) } } diff --git a/parachain-consensus/Cargo.toml b/parachain-consensus/Cargo.toml new file mode 100644 index 000000000..050d92506 --- /dev/null +++ b/parachain-consensus/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "ismp-parachain-consensus" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +# crates.io +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +hex-literal = "0.4.1" +merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +primitive-types = { version = "0.12.1", default-features = false } + +# polytope labs +ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } + +# substrate +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } + +# cumulus +parachain-system = { package = "cumulus-pallet-parachain-system", git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400", default-features = false } + +# local +ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } + +[dev-dependencies] + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "parachain-system/std", + "cumulus-primitives-core/std", + "ismp/std", + "sp-trie/std", + "merkle-mountain-range/std", + "sp-consensus-aura/std", + "sp-runtime/std", + "sp-io/std", + "primitive-types/std", + "ismp-primitives/std", +] diff --git a/parachain-consensus/src/consensus.rs b/parachain-consensus/src/consensus.rs new file mode 100644 index 000000000..70bacea7f --- /dev/null +++ b/parachain-consensus/src/consensus.rs @@ -0,0 +1,291 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The parachain consensus client module + +use core::{marker::PhantomData, time::Duration}; + +use alloc::{format, vec, vec::Vec}; +use codec::{Decode, Encode}; +use hex_literal::hex; +use ismp::{ + consensus_client::{ + ConsensusClient, ConsensusClientId, IntermediateState, StateCommitment, StateMachineHeight, + StateMachineId, + }, + error::Error, + host::ISMPHost, + messaging::Proof, + router::RequestResponse, +}; +use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; +use merkle_mountain_range::MerkleProof; +use primitive_types::H256; +use sp_consensus_aura::AURA_ENGINE_ID; +use sp_runtime::{ + traits::{BlakeTwo256, Header, Keccak256}, + DigestItem, +}; +use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; + +use crate::RelayChainOracle; + +/// The parachain consensus client implementation for ISMP. +pub struct ParachainConsensusClient(PhantomData<(T, H)>); + +/// Information necessary to prove the sibling parachain's finalization to this +/// parachain. +#[derive(Debug, Encode, Decode)] +pub struct ParachainConsensusProof { + /// List of para ids contained in the proof + pub para_ids: Vec, + /// Height of the relay chain for the given proof + pub relay_height: u32, + /// Storage proof for the parachain headers + pub storage_proof: Vec>, +} + +/// Hashing algorithm for the state proof +#[derive(Debug, Encode, Decode)] +pub enum HashAlgorithm { + Keccak, + Blake2, +} + +/// Holds the relevant data needed for state proof verification +#[derive(Debug, Encode, Decode)] +pub struct ParachainStateProof { + /// Algorithm to use for state proof verification + pub hasher: HashAlgorithm, + /// Storage proof for the parachain headers + pub storage_proof: Vec>, +} + +/// Holds the relevant data needed for request/response proof verification +#[derive(Debug, Encode, Decode)] +pub struct MembershipProof { + /// Size of the mmr at the time this proof was generated + pub mmr_size: u64, + /// Mmr pos for this leaf + pub mmr_pos: u64, + /// Mmr proof + pub proof: Vec, +} + +/// Static key for parachain headers in the relay chain storage +const PARACHAIN_HEADS_KEY: [u8; 32] = + hex!("cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3"); + +/// The `ConsensusEngineId` of ISMP digest in the parachain header. +pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; + +/// ConsensusClientId for [`ParachainConsensusClient`] +pub const PARACHAIN_CONSENSUS_ID: ConsensusClientId = *b"PARA"; + +/// Slot duration in milliseconds +const SLOT_DURATION: u64 = 12_000; + +impl ConsensusClient for ParachainConsensusClient +where + H: ISMPHost, + T: frame_system::Config + RelayChainOracle, + T::BlockNumber: Into, + T::Hash: From, +{ + fn verify_consensus( + &self, + _host: &dyn ISMPHost, + state: Vec, + proof: Vec, + ) -> Result<(Vec, Vec), Error> { + let update: ParachainConsensusProof = + codec::Decode::decode(&mut &proof[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode parachain consensus proof: {e:?}" + )) + })?; + + let root = T::state_root(update.relay_height).ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Cannot find relay chain height: {}", + update.relay_height + )) + })?; + + let db = StorageProof::new(update.storage_proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&db, &root).build(); + + let parachain_heads_key = PARACHAIN_HEADS_KEY.to_vec(); + + let mut intermediates = vec![]; + + for id in update.para_ids { + let mut full_key = parachain_heads_key.clone(); + full_key.extend(sp_io::hashing::twox_64(&*id.encode())); + let header = trie + .get(&full_key) + .map_err(|e| { + Error::ImplementationSpecific( + format!("Error verifying parachain header {e:?}",), + ) + })? + .ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Cannot find parachain header for ParaId({id})", + )) + })?; + + // ideally all parachain headers are the same + let header = T::Header::decode(&mut &*header).map_err(|e| { + Error::ImplementationSpecific(format!("Error decoding parachain header: {e:?}",)) + })?; + + let (mut timestamp, mut ismp_root) = (0, H256::default()); + for digest in header.digest().logs.iter() { + match digest { + DigestItem::PreRuntime(consensus_engine_id, value) + if *consensus_engine_id == AURA_ENGINE_ID => + { + let slot = u64::decode(&mut &value[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode beacon message: {e:?}" + )) + })?; + timestamp = Duration::from_millis(slot * SLOT_DURATION).as_secs(); + } + DigestItem::Consensus(consensus_engine_id, value) + if *consensus_engine_id == ISMP_ID => + { + if value.len() != 32 { + Err(Error::ImplementationSpecific( + "Header contains an invalid ismp root".into(), + ))? + } + + ismp_root = H256::from_slice(&value); + } + // don't really care about the rest + _ => {} + }; + } + + if timestamp == 0 || ismp_root == H256::default() { + Err(Error::ImplementationSpecific("Timestamp or ismp root not found".into()))? + } + + let height: u32 = (*header.number()).into(); + + let intermediate = IntermediateState { + height: StateMachineHeight { + id: StateMachineId { + state_id: id as u64, + consensus_client: PARACHAIN_CONSENSUS_ID, + }, + height: height as u64, + }, + commitment: StateCommitment { + timestamp, + ismp_root: Some(ismp_root), + state_root: H256::from_slice(header.state_root().as_ref()), + }, + }; + + intermediates.push(intermediate); + } + + Ok((state, intermediates)) + } + + fn unbonding_period(&self) -> Duration { + // there's no notion of client expiry, since there's shared security. + Duration::from_secs(u64::MAX) + } + + fn verify_membership( + &self, + _host: &dyn ISMPHost, + _item: RequestResponse, + state: StateCommitment, + _proof: &Proof, + ) -> Result<(), Error> { + let membership = MembershipProof::decode(&mut &*_proof.proof).map_err(|e| { + Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) + })?; + let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = MerkleProof::, MmrHasher>::new(membership.mmr_size, nodes); + let leaf = match _item { + RequestResponse::Request(req) => Leaf::Request(req), + RequestResponse::Response(res) => Leaf::Response(res), + }; + let root = state + .ismp_root + .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; + + let valid = proof + .verify( + DataOrHash::Hash(root.into()), + vec![(membership.mmr_pos, DataOrHash::Data(leaf))], + ) + .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; + + if !valid { + Err(Error::ImplementationSpecific("Invalid membership proof".into()))? + } + + Ok(()) + } + + fn state_trie_key(&self, _request: RequestResponse) -> Vec { + todo!() + } + + fn verify_state_proof( + &self, + _host: &dyn ISMPHost, + key: Vec, + root: StateCommitment, + proof: &Proof, + ) -> Result>, Error> { + let state_proof: ParachainStateProof = codec::Decode::decode(&mut &*proof.proof) + .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; + + let data = match state_proof.hasher { + HashAlgorithm::Keccak => { + let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); + trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!("Error reading state proof: {e:?}")) + })? + } + HashAlgorithm::Blake2 => { + let db = + StorageProof::new(state_proof.storage_proof).into_memory_db::(); + + let trie = + TrieDBBuilder::>::new(&db, &root.state_root).build(); + trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!("Error reading state proof: {e:?}")) + })? + } + }; + + Ok(data) + } + + fn is_frozen(&self, _: &[u8]) -> Result<(), Error> { + // parachain consensus client can never be frozen. + Ok(()) + } +} diff --git a/parachain-consensus/src/lib.rs b/parachain-consensus/src/lib.rs new file mode 100644 index 000000000..eba63da14 --- /dev/null +++ b/parachain-consensus/src/lib.rs @@ -0,0 +1,79 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ISMP Parachain Consensus Client +//! +//! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +pub mod consensus; + +use cumulus_primitives_core::relay_chain; +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use cumulus_primitives_core::relay_chain; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + parachain_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + /// Mapping of relay chain heights to it's state root. Gotten from parachain-system. + #[pallet::storage] + #[pallet::getter(fn relay_chain_state)] + pub type RelayChainState = + StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NewRelayChainState { height: relay_chain::BlockNumber }, + } + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> Weight { + let state = RelaychainDataProvider::::current_relay_chain_state(); + if !RelayChainState::::contains_key(state.number) { + RelayChainState::::insert(state.number, state.state_root); + Self::deposit_event(Event::::NewRelayChainState { height: state.number }) + } + Weight::zero() + } + } +} + +/// Interface that exposes the relay chain state roots. +pub trait RelayChainOracle { + /// Returns the state root for a given height if it exists. + fn state_root(height: relay_chain::BlockNumber) -> Option; +} + +impl RelayChainOracle for Pallet { + fn state_root(height: relay_chain::BlockNumber) -> Option { + RelayChainState::::get(height) + } +} diff --git a/src/consensus_clients.rs b/src/consensus_clients.rs deleted file mode 100644 index 7459e7e0b..000000000 --- a/src/consensus_clients.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod beacon_consensus_client; -pub mod consensus_client_ids; diff --git a/src/consensus_clients/beacon_consensus_client.rs b/src/consensus_clients/beacon_consensus_client.rs deleted file mode 100644 index e089b3883..000000000 --- a/src/consensus_clients/beacon_consensus_client.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod beacon_client; -pub mod optimism; -mod presets; -pub mod state_machine_ids; -pub mod types; -pub mod utils; diff --git a/src/consensus_clients/beacon_consensus_client/beacon_client.rs b/src/consensus_clients/beacon_consensus_client/beacon_client.rs deleted file mode 100644 index c64808f7f..000000000 --- a/src/consensus_clients/beacon_consensus_client/beacon_client.rs +++ /dev/null @@ -1,190 +0,0 @@ -use alloc::{format, string::ToString}; -use codec::{Decode, Encode}; -use core::time::Duration; -use ethabi::ethereum_types::H256; - -use crate::consensus_clients::{ - beacon_consensus_client::{ - presets::UNBONDING_PERIOD_HOURS, - state_machine_ids::EXECUTION_LAYER_ID, - types::{BeaconClientUpdate, BeaconMessage, ConsensusState}, - utils::{ - construct_intermediate_state, decode_evm_state_proof, get_contract_storage_root, - get_value_from_proof, req_res_to_key, - }, - }, - consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, -}; -use ismp_rs::{ - consensus_client::{ConsensusClient, IntermediateState, StateCommitment}, - error::Error, - host::ISMPHost, - messaging::Proof, - router::RequestResponse, -}; - -use crate::consensus_clients::beacon_consensus_client::{ - optimism::verify_optimism_payload, presets::ismp_contract_address, -}; -use sp_std::prelude::*; - -#[derive(Default, Clone)] -pub struct BeaconConsensusClient; - -impl ConsensusClient for BeaconConsensusClient { - fn verify_consensus( - &self, - _host: &dyn ISMPHost, - trusted_consensus_state: Vec, - consensus_proof: Vec, - ) -> Result<(Vec, Vec), Error> { - let beacon_message = BeaconMessage::decode(&mut &consensus_proof[..]).map_err(|_| { - Error::ImplementationSpecific("Cannot decode beacon message".to_string()) - })?; - - match beacon_message { - BeaconMessage::ConsensusUpdate(BeaconClientUpdate { - optimism_payload, - consensus_update, - }) => { - let consensus_state = ConsensusState::decode(&mut &trusted_consensus_state[..]) - .map_err(|_| { - Error::ImplementationSpecific( - "Cannot decode trusted consensus state".to_string(), - ) - })?; - - let no_codec_light_client_state = - consensus_state.light_client_state.try_into().map_err(|_| { - Error::ImplementationSpecific(format!( - "Cannot convert light client state to no codec type", - )) - })?; - - let no_codec_light_client_update = - consensus_update.clone().try_into().map_err(|_| { - Error::ImplementationSpecific(format!( - "Cannot convert light client update to no codec type" - )) - })?; - - let new_light_client_state = - sync_committee_verifier::verify_sync_committee_attestation( - no_codec_light_client_state, - no_codec_light_client_update, - ) - .map_err(|_| Error::ConsensusProofVerificationFailed { - id: ETHEREUM_CONSENSUS_CLIENT_ID, - })?; - - let mut intermediate_states = vec![]; - - let state_root = consensus_update.execution_payload.state_root; - let intermediate_state = construct_intermediate_state( - EXECUTION_LAYER_ID, - ETHEREUM_CONSENSUS_CLIENT_ID, - consensus_update.execution_payload.block_number, - consensus_update.execution_payload.timestamp, - &state_root, - )?; - - intermediate_states.push(intermediate_state); - - if let Some(optimism_payload) = optimism_payload { - let state = verify_optimism_payload(optimism_payload, &state_root)?; - intermediate_states.push(state) - } - - let new_consensus_state = ConsensusState { - frozen_height: None, - light_client_state: new_light_client_state.try_into().map_err(|_| { - Error::ImplementationSpecific(format!( - "Cannot convert light client state to codec type" - )) - })?, - }; - - Ok((new_consensus_state.encode(), intermediate_states)) - } - _ => unimplemented!(), - } - } - - fn unbonding_period(&self) -> Duration { - Duration::from_secs(UNBONDING_PERIOD_HOURS * 60 * 60) - } - - fn verify_membership( - &self, - host: &dyn ISMPHost, - item: RequestResponse, - root: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let evm_state_proof = decode_evm_state_proof(proof)?; - let contract_address = ismp_contract_address(&item).ok_or_else(|| { - Error::ImplementationSpecific("Ismp contract address not found".to_string()) - })?; - let key = req_res_to_key(host, item); - let root = H256::from_slice(&root.state_root[..]); - let contract_root = get_contract_storage_root( - evm_state_proof.contract_proof, - &contract_address, - root.clone(), - )?; - let _ = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)? - .ok_or_else(|| { - Error::MembershipProofVerificationFailed(format!("There is no DB value")) - })?; - - Ok(()) - } - - fn verify_state_proof( - &self, - _host: &dyn ISMPHost, - _key: Vec, - _root: StateCommitment, - _proof: &Proof, - ) -> Result, Error> { - unimplemented!() - } - - fn verify_non_membership( - &self, - host: &dyn ISMPHost, - item: RequestResponse, - root: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let evm_state_proof = decode_evm_state_proof(proof)?; - let contract_address = ismp_contract_address(&item).ok_or_else(|| { - Error::ImplementationSpecific("Ismp contract address not found".to_string()) - })?; - let key = req_res_to_key(host, item); - let root = H256::from_slice(&root.state_root[..]); - let contract_root = - get_contract_storage_root(evm_state_proof.contract_proof, &contract_address, root)?; - - let result = get_value_from_proof(key, contract_root, evm_state_proof.storage_proof)?; - - if result.is_some() { - return Err(Error::NonMembershipProofVerificationFailed( - "Invalid membership proof".to_string(), - )) - } - - Ok(()) - } - - fn is_frozen(&self, consensus_state: &[u8]) -> Result<(), Error> { - let consensus_state = ConsensusState::decode(&mut &consensus_state[..]).map_err(|_| { - Error::ImplementationSpecific("Cannot decode trusted consensus state".to_string()) - })?; - if consensus_state.frozen_height.is_some() { - Err(Error::FrozenConsensusClient { id: ETHEREUM_CONSENSUS_CLIENT_ID }) - } else { - Ok(()) - } - } -} diff --git a/src/consensus_clients/beacon_consensus_client/optimism.rs b/src/consensus_clients/beacon_consensus_client/optimism.rs deleted file mode 100644 index 7c372db77..000000000 --- a/src/consensus_clients/beacon_consensus_client/optimism.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::consensus_clients::{ - beacon_consensus_client::{ - presets::L2_ORACLE_ADDRESS, - state_machine_ids::OPTIMISM_ID, - utils::{ - derive_array_item_key, get_contract_storage_root, get_value_from_proof, to_bytes_32, - }, - }, - consensus_client_ids::ETHEREUM_CONSENSUS_CLIENT_ID, -}; -use alloc::string::ToString; -use ethabi::ethereum_types::{H256, U128}; -use ismp_rs::{ - consensus_client::{IntermediateState, StateCommitment, StateMachineHeight, StateMachineId}, - error::Error, -}; -use sp_std::prelude::*; - -#[derive(codec::Encode, codec::Decode)] -pub struct OptimismPayloadProof { - /// Actual state root of the optimism execution layer - pub state_root: [u8; 32], - /// Storage root hash of the optimism withdrawal contracts - pub withdrawal_storage_root: [u8; 32], - /// Optimism Block hash at which the values aboved were fetched - pub l2_block_hash: [u8; 32], - /// L2Oracle contract version - pub version: [u8; 32], - /// Membership Proof for the L2Oracle contract account in the ethereum world trie - pub l2_oracle_proof: Vec>, - /// Membership proof for output root in l2Outputs array - pub output_root_proof: Vec>, - /// Membership proof Timestamp and block number in the l2Outputs array - pub multi_proof: Vec>, - /// Index of the output root that needs to be proved in the l2Outputs array - pub output_root_index: u64, - /// Block number - pub block_number: u64, - /// Timestamp - pub timestamp: u64, -} - -/// Slot for the l2Outputs array in the L2Oracle contract -pub(super) const L2_OUTPUTS_SLOT: u8 = 3; - -pub(super) fn verify_optimism_payload( - payload: OptimismPayloadProof, - root: &[u8], -) -> Result { - let root = to_bytes_32(root)?; - let root = H256::from_slice(&root[..]); - let storage_root = - get_contract_storage_root(payload.l2_oracle_proof, &L2_ORACLE_ADDRESS, root)?; - - let mut buf = Vec::with_capacity(128); - buf.extend_from_slice(&payload.version[..]); - buf.extend_from_slice(&payload.state_root[..]); - buf.extend_from_slice(&payload.withdrawal_storage_root[..]); - buf.extend_from_slice(&payload.l2_block_hash[..]); - - let output_root = sp_io::hashing::keccak_256(&buf); - - let output_root_key = derive_array_item_key(L2_OUTPUTS_SLOT, payload.output_root_index); - - let proof_value = - get_value_from_proof(output_root_key, storage_root, payload.output_root_proof)? - .ok_or_else(|| { - Error::MembershipProofVerificationFailed("Value not found in proof".to_string()) - })?; - - if &proof_value != &output_root[..] { - return Err(Error::MembershipProofVerificationFailed( - "Invalid optimism output root proof".to_string(), - )) - } - - // verify timestamp and block number - let timestamp_block_number_key = - derive_array_item_key(L2_OUTPUTS_SLOT, payload.output_root_index + 1); - let block_and_timestamp = - get_value_from_proof(timestamp_block_number_key, storage_root, payload.multi_proof)? - .ok_or_else(|| { - Error::MembershipProofVerificationFailed("Value not found in proof".to_string()) - })?; - - let mut timestamp = Vec::with_capacity(16); - U128::from(payload.timestamp).to_big_endian(&mut timestamp); - - let mut block_number = Vec::with_capacity(16); - U128::from(payload.block_number).to_big_endian(&mut block_number); - - let mut concat = Vec::with_capacity(32); - concat.extend_from_slice(×tamp); - concat.extend_from_slice(&block_number); - - if block_and_timestamp != concat { - return Err(Error::MembershipProofVerificationFailed( - "Invalid optimism block and timestamp proof".to_string(), - )) - } - - Ok(IntermediateState { - height: StateMachineHeight { - id: StateMachineId { - state_id: OPTIMISM_ID, - consensus_client: ETHEREUM_CONSENSUS_CLIENT_ID, - }, - height: payload.block_number, - }, - commitment: StateCommitment { - timestamp: payload.timestamp, - ismp_root: [0u8; 32], - state_root: payload.state_root, - }, - }) -} diff --git a/src/consensus_clients/beacon_consensus_client/presets.rs b/src/consensus_clients/beacon_consensus_client/presets.rs deleted file mode 100644 index fb7d50163..000000000 --- a/src/consensus_clients/beacon_consensus_client/presets.rs +++ /dev/null @@ -1,77 +0,0 @@ -use ismp_rs::{host::ChainID, router::RequestResponse}; -#[cfg(not(feature = "testnet"))] -pub use mainnet::*; -#[cfg(feature = "testnet")] -pub use testnet::*; - -#[cfg(not(feature = "testnet"))] -mod mainnet { - use hex_literal::hex; - - pub const L2_ORACLE_ADDRESS: [u8; 20] = hex!("47bBB9054823f27B9B6A71F5cb0eBc785692FF2E"); - /// Contract address on optimism - pub const ISMP_CONTRACT_ADDRESS_OPTIMISM: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on gnosis - pub const ISMP_CONTRACT_ADDRESS_GNOSIS: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on arbitrum - pub const ISMP_CONTRACT_ADDRESS_ARB: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on base - pub const ISMP_CONTRACT_ADDRESS_BASE: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on moonbeam - pub const ISMP_CONTRACT_ADDRESS_MOONBEAM: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on ethereum - pub const ISMP_CONTRACT_ADDRESS_ETHEREUM: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Unbonding period for ethereum after which unstaked validators can withdraw their funds - /// https://ethos.dev/beacon-chain - pub const UNBONDING_PERIOD_HOURS: u64 = 27; -} - -#[cfg(feature = "testnet")] -mod testnet { - use hex_literal::hex; - - pub const L2_ORACLE_ADDRESS: [u8; 20] = hex!("47bBB9054823f27B9B6A71F5cb0eBc785692FF2E"); - /// Contract address on optimism - pub const ISMP_CONTRACT_ADDRESS_OPTIMISM: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on gnosis - pub const ISMP_CONTRACT_ADDRESS_GNOSIS: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on arbitrum - pub const ISMP_CONTRACT_ADDRESS_ARB: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on base - pub const ISMP_CONTRACT_ADDRESS_BASE: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on moonbeam - pub const ISMP_CONTRACT_ADDRESS_MOONBEAM: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Contract address on ethereum - pub const ISMP_CONTRACT_ADDRESS_ETHEREUM: [u8; 20] = - hex!("b856af30b938b6f52e5bff365675f358cd52f91b"); - /// Unbonding period for ethereum after which unstaked validators can withdraw their funds - pub const UNBONDING_PERIOD_HOURS: u64 = 27; -} - -pub fn ismp_contract_address(item: &RequestResponse) -> Option<[u8; 20]> { - let chain_id = match item { - RequestResponse::Request(req) => req.source_chain(), - RequestResponse::Response(res) => res.request.dest_chain(), - }; - - match chain_id { - ChainID::ETHEREUM => Some(ISMP_CONTRACT_ADDRESS_ETHEREUM), - ChainID::GNOSIS => Some(ISMP_CONTRACT_ADDRESS_GNOSIS), - ChainID::ARBITRUM => Some(ISMP_CONTRACT_ADDRESS_ARB), - ChainID::OPTIMISM => Some(ISMP_CONTRACT_ADDRESS_OPTIMISM), - ChainID::BASE => Some(ISMP_CONTRACT_ADDRESS_BASE), - ChainID::MOONBEAM => Some(ISMP_CONTRACT_ADDRESS_MOONBEAM), - _ => None, - } -} diff --git a/src/consensus_clients/beacon_consensus_client/state_machine_ids.rs b/src/consensus_clients/beacon_consensus_client/state_machine_ids.rs deleted file mode 100644 index e8dfb38d6..000000000 --- a/src/consensus_clients/beacon_consensus_client/state_machine_ids.rs +++ /dev/null @@ -1,3 +0,0 @@ -/// State machine id used for the ethereum execution layer. -pub const EXECUTION_LAYER_ID: u64 = 1; -pub const OPTIMISM_ID: u64 = 2; diff --git a/src/consensus_clients/beacon_consensus_client/types.rs b/src/consensus_clients/beacon_consensus_client/types.rs deleted file mode 100644 index c9809b6b4..000000000 --- a/src/consensus_clients/beacon_consensus_client/types.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::consensus_clients::beacon_consensus_client::optimism::OptimismPayloadProof; -use codec::{Decode, Encode}; -use ethabi::ethereum_types::{H256, U256}; -use hash256_std_hasher::Hash256StdHasher; -use hash_db::Hasher; -use rlp_derive::RlpDecodable; -use sp_std::prelude::*; -use sync_committee_primitives::derived_types::{LightClientState, LightClientUpdate}; - -pub struct KeccakHasher; - -impl Hasher for KeccakHasher { - type Out = H256; - type StdHasher = Hash256StdHasher; - const LENGTH: usize = 32; - - fn hash(x: &[u8]) -> Self::Out { - sp_io::hashing::keccak_256(x).into() - } -} - -#[derive(Debug, Encode, Decode, Clone)] -pub struct ConsensusState { - pub frozen_height: Option, - pub light_client_state: LightClientState, -} - -#[derive(Encode, Decode)] -pub struct Misbehaviour { - pub update_1: LightClientUpdate, - pub update_2: LightClientUpdate, -} - -#[derive(Encode, Decode)] -pub struct BeaconClientUpdate { - pub consensus_update: LightClientUpdate, - pub optimism_payload: Option, -} - -#[derive(Encode, Decode)] -pub enum BeaconMessage { - ConsensusUpdate(BeaconClientUpdate), - Misbehaviour(Misbehaviour), -} - -/// Slot index for requests map -pub const REQ_SLOT: u8 = 1; -/// Slot index for responses map -pub const RESP_SLOT: u8 = 2; - -#[derive(Encode, Decode, Clone)] -pub struct EvmStateProof { - pub contract_proof: Vec>, - pub storage_proof: Vec>, -} - -/// The ethereum account stored in the global state trie. -#[derive(RlpDecodable)] -pub(super) struct Account { - _nonce: u64, - _balance: U256, - pub storage_root: H256, - _code_hash: H256, -} diff --git a/src/consensus_clients/beacon_consensus_client/utils.rs b/src/consensus_clients/beacon_consensus_client/utils.rs deleted file mode 100644 index bf6ad7348..000000000 --- a/src/consensus_clients/beacon_consensus_client/utils.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::consensus_clients::beacon_consensus_client::types::{ - Account, EvmStateProof, KeccakHasher, REQ_SLOT, RESP_SLOT, -}; -use alloc::{format, string::ToString}; -use codec::Decode; -use ethabi::{ - ethereum_types::{H160, H256, U256}, - Token, -}; -use ismp_rs::{ - consensus_client::{IntermediateState, StateCommitment, StateMachineHeight, StateMachineId}, - error::Error, - host::ISMPHost, - messaging::Proof, - router::RequestResponse, -}; -use patricia_merkle_trie::{EIP1186Layout, StorageProof}; -use rlp::Rlp; -use sp_std::prelude::*; -use trie_db::{DBValue, Trie, TrieDBBuilder}; - -pub fn construct_intermediate_state( - state_id: u64, - consensus_client_id: u64, - height: u64, - timestamp: u64, - state_root: &[u8], -) -> Result { - let state_machine_id = StateMachineId { state_id, consensus_client: consensus_client_id }; - - let state_machine_height = StateMachineHeight { id: state_machine_id, height }; - - let state_commitment = StateCommitment { - timestamp, - ismp_root: [0u8; 32], - state_root: to_bytes_32(&state_root[..])?.into(), - }; - - let intermediate_state = - IntermediateState { height: state_machine_height, commitment: state_commitment }; - - Ok(intermediate_state) -} - -pub(super) fn decode_evm_state_proof(proof: &Proof) -> Result { - let proof_vec = proof.proof.clone(); - let evm_state_proof = EvmStateProof::decode(&mut &proof_vec[..]).map_err(|_| { - Error::ImplementationSpecific(format!("Cannot decode evm state proof {:?}", proof_vec)) - })?; - - Ok(evm_state_proof) -} - -pub fn req_res_to_key(host: &dyn ISMPHost, item: RequestResponse) -> Vec { - match item { - RequestResponse::Request(request) => { - let commitment = host.get_request_commitment(&request); - let unhashed = derive_unhashed_map_key(commitment, REQ_SLOT); - host.keccak256(&unhashed).to_vec() - } - RequestResponse::Response(response) => { - let commitment = host.get_response_commitment(&response); - let unhashed = derive_unhashed_map_key(commitment, RESP_SLOT); - host.keccak256(&unhashed).to_vec() - } - } -} - -pub(super) fn to_bytes_32(bytes: &[u8]) -> Result<[u8; 32], Error> { - if bytes.len() != 32 { - return Err(Error::ImplementationSpecific(format!( - "Input vector must have exactly 32 elements {:?}", - bytes - ))) - } - - let mut array = [0u8; 32]; - - array.copy_from_slice(&bytes); - - Ok(array) -} - -pub(super) fn get_contract_storage_root( - contract_account_proof: Vec>, - contract_address: &[u8; 20], - root: H256, -) -> Result { - use rlp::Decodable; - let db = StorageProof::new(contract_account_proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&db, &root).build(); - let contract_address = H160::from_slice(contract_address); - let key = ethabi::encode(&[Token::Address(contract_address)]); - let result = trie - .get(&key) - .map_err(|_| Error::ImplementationSpecific("Invalid contract account proof".to_string()))? - .ok_or_else(|| { - Error::ImplementationSpecific("Contract account is not present in proof".to_string()) - })?; - - let contract_account = ::decode(&Rlp::new(&result)).map_err(|_| { - Error::ImplementationSpecific(format!( - "Error decoding contract account from key {:?}", - &result - )) - })?; - - Ok(contract_account.storage_root) -} - -pub(super) fn derive_unhashed_map_key(key: Vec, slot: u8) -> Vec { - ethabi::encode(&[Token::FixedBytes(key), Token::Int(U256::from(slot))]) -} - -pub(super) fn derive_array_item_key(slot: u8, index: u64) -> Vec { - let slot_hash = sp_io::hashing::keccak_256(ðabi::encode(&[Token::Uint(U256::from(slot))])); - let slot_index = U256::from_big_endian(&slot_hash[..]) + U256::from(index); - <[u8; 32]>::from(slot_index).to_vec() -} - -pub(super) fn get_value_from_proof( - key: Vec, - root: H256, - proof: Vec>, -) -> Result, Error> { - let proof_db = StorageProof::new(proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&proof_db, &root).build(); - - trie.get(&key).map_err(|_| Error::ImplementationSpecific(format!("Error reading proof db"))) -} diff --git a/src/consensus_clients/consensus_client_ids.rs b/src/consensus_clients/consensus_client_ids.rs deleted file mode 100644 index 4ebba4960..000000000 --- a/src/consensus_clients/consensus_client_ids.rs +++ /dev/null @@ -1 +0,0 @@ -pub const ETHEREUM_CONSENSUS_CLIENT_ID: u64 = 1; diff --git a/src/mmr/mod.rs b/src/mmr/mod.rs deleted file mode 100644 index 305ede1fe..000000000 --- a/src/mmr/mod.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use core::{fmt, fmt::Formatter}; - -pub mod mmr; -pub mod storage; -mod utils; - -use crate::{host::Host, Config}; -use codec::{Decode, Encode}; -use ismp_rs::{ - host::ISMPHost, - router::{Request, Response}, -}; -use sp_runtime::traits; - -pub use self::mmr::Mmr; -pub type LeafIndex = u64; -pub type NodeIndex = u64; - -#[derive(Debug, Clone, Decode, Encode, PartialEq, Eq)] -pub enum Leaf { - Request(Request), - Response(Response), -} - -/// A full leaf content stored in the offchain-db. -pub trait FullLeaf: Clone + fmt::Debug + PartialEq + Eq + codec::Codec { - /// Returns the hash of the leaf - fn hash(&self) -> <::Hashing as traits::Hash>::Output; -} - -impl FullLeaf for Leaf { - fn hash(&self) -> <::Hashing as traits::Hash>::Output { - let host = Host::::default(); - match self { - Leaf::Request(req) => { - let commitment = host.get_request_commitment(req); - let mut hash = [0u8; 32]; - hash.copy_from_slice(&commitment[..]); - ::Hash::from(hash) - } - Leaf::Response(res) => { - let commitment = host.get_response_commitment(res); - let mut hash = [0u8; 32]; - hash.copy_from_slice(&commitment[..]); - ::Hash::from(hash) - } - } - } -} - -/// An element representing either full data or its hash. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] -pub enum DataOrHash { - /// Arbitrary data in its full form. - Data(L), - /// A hash of some data. - Hash(<::Hashing as traits::Hash>::Output), -} - -impl core::fmt::Debug for DataOrHash { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - DataOrHash::Data(leaf) => f.debug_struct("DataOrHash").field("Data", leaf).finish(), - DataOrHash::Hash(hash) => f.debug_struct("DataOrHash").field("Hash", hash).finish(), - } - } -} - -impl From for DataOrHash { - fn from(l: L) -> Self { - Self::Data(l) - } -} - -impl> DataOrHash { - /// Retrieve a hash of this item. - /// - /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] - /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. - pub fn hash(&self) -> <::Hashing as traits::Hash>::Output { - match *self { - Self::Data(ref leaf) => leaf.hash(), - Self::Hash(ref hash) => *hash, - } - } -} -/// Node type for runtime `T`. -pub type NodeOf = DataOrHash; - -/// Default Merging & Hashing behavior for MMR. -pub struct Hasher(sp_std::marker::PhantomData<(T, L)>); - -impl> mmr_lib::Merge for Hasher { - type Item = NodeOf; - - fn merge(left: &Self::Item, right: &Self::Item) -> mmr_lib::Result { - let mut concat = left.hash().as_ref().to_vec(); - concat.extend_from_slice(right.hash().as_ref()); - - Ok(NodeOf::Hash(<::Hashing as traits::Hash>::hash(&concat))) - } -} From 0569c24f8ef94b7216a14976c303590529c53cc4 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 19 Apr 2023 11:05:44 +0100 Subject: [PATCH 116/182] use action for ssh key (#26) --- .github/workflows/build-test-and-lint.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index 6717eea85..6c80fd8de 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -16,6 +16,10 @@ jobs: token: ${{ secrets.GH_TOKEN }} submodules: recursive + - uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.SSH_KEY }} + - name: Install toolchain uses: dtolnay/rust-toolchain@nightly with: @@ -34,21 +38,15 @@ jobs: - name: Build run: | - eval `ssh-agent -s` - ssh-add - <<< '${{ secrets.SSH_KEY }}' cargo +nightly check --workspace --all-targets --all-features --verbose - name: Build `no-std` run: | - eval `ssh-agent -s` - ssh-add - <<< '${{ secrets.SSH_KEY }}' cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose cargo +nightly check -p ismp-parachain-consensus --no-default-features --target=wasm32-unknown-unknown --verbose - name: Run tests run: | - eval `ssh-agent -s` - ssh-add - <<< '${{ secrets.SSH_KEY }}' cargo +nightly test --all-features --verbose lint: From fbb0eaab4405c06c9083c185419371c924eec22e Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 19 Apr 2023 14:18:02 +0100 Subject: [PATCH 117/182] rework apis (#27) * rework apis * cargo fmt * Get * don't clone proxy router * ismp-parachain * use pallet_ismp::host::Host * import boxed * cargo fmt --- .github/workflows/build-test-and-lint.yml | 6 ++++- Cargo.lock | 5 ++-- pallet-ismp/Cargo.toml | 2 +- pallet-ismp/runtime-api/src/lib.rs | 6 ++--- pallet-ismp/src/errors.rs | 22 ++++++++-------- pallet-ismp/src/events.rs | 10 +++---- pallet-ismp/src/host.rs | 32 +++++++++++------------ pallet-ismp/src/lib.rs | 31 +++++++++++++--------- pallet-ismp/src/router.rs | 25 +++++++++--------- parachain-consensus/Cargo.toml | 4 ++- parachain-consensus/src/consensus.rs | 26 +++++++++++------- 11 files changed, 94 insertions(+), 75 deletions(-) diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index 6c80fd8de..cc30331f3 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -6,6 +6,10 @@ on: pull_request: branches: [ main ] +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + jobs: build_and_test: runs-on: ubuntu-latest @@ -43,7 +47,7 @@ jobs: - name: Build `no-std` run: | cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose - cargo +nightly check -p ismp-parachain-consensus --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose - name: Run tests run: | diff --git a/Cargo.lock b/Cargo.lock index 3574f5f34..bb9f7d16e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2995,7 +2995,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#26534dce3e5980eb33ff5ee1ce1e193cc53ebf1a" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#4b8013cada9f69ccbd550d1fc7e2c5107096559e" dependencies = [ "derive_more", "parity-scale-codec", @@ -3005,7 +3005,7 @@ dependencies = [ ] [[package]] -name = "ismp-parachain-consensus" +name = "ismp-parachain" version = "0.1.0" dependencies = [ "ckb-merkle-mountain-range", @@ -3016,6 +3016,7 @@ dependencies = [ "hex-literal 0.4.1", "ismp", "ismp-primitives", + "pallet-ismp", "parity-scale-codec", "primitive-types", "scale-info", diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index 632606c95..b455aba09 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Polytope Labs "] frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, optional = true } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, features = ["disable_panic_handler"] } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index b105b23ae..ab7b6592a 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -3,7 +3,7 @@ use ismp_rs::{ consensus_client::ConsensusClientId, - host::ChainID, + host::StateMachine, router::{Request, Response}, }; use pallet_ismp::primitives::{Error, Proof}; @@ -15,8 +15,8 @@ use sp_std::vec::Vec; #[derive(codec::Encode, codec::Decode)] #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] pub struct LeafIndexQuery { - pub source_chain: ChainID, - pub dest_chain: ChainID, + pub source_chain: StateMachine, + pub dest_chain: StateMachine, pub nonce: u64, } diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index 3f52b7f12..ebafc432c 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -2,7 +2,7 @@ use codec::{Decode, Encode}; use ismp_rs::{ consensus_client::{ConsensusClientId, StateMachineHeight}, error::Error as IsmpError, - host::ChainID, + host::StateMachine, }; use sp_std::prelude::*; @@ -28,18 +28,18 @@ pub enum HandlingError { }, RequestCommitmentNotFound { nonce: u64, - source: ChainID, - dest: ChainID, + source: StateMachine, + dest: StateMachine, }, RequestVerificationFailed { nonce: u64, - source: ChainID, - dest: ChainID, + source: StateMachine, + dest: StateMachine, }, ResponseVerificationFailed { nonce: u64, - source: ChainID, - dest: ChainID, + source: StateMachine, + dest: StateMachine, }, ConsensusProofVerificationFailed { id: ConsensusClientId, @@ -65,15 +65,15 @@ pub enum HandlingError { }, RequestTimeoutNotElapsed { nonce: u64, - source: ChainID, - dest: ChainID, + source: StateMachine, + dest: StateMachine, timeout_timestamp: u64, state_machine_time: u64, }, RequestTimeoutVerificationFailed { nonce: u64, - source: ChainID, - dest: ChainID, + source: StateMachine, + dest: StateMachine, }, } diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 45a1fd986..8490cb356 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -2,7 +2,7 @@ use crate::{Config, Event as PalletEvent}; use alloc::collections::BTreeSet; use ismp_rs::{ consensus_client::{ConsensusClientId, StateMachineHeight, StateMachineId}, - host::ChainID, + host::StateMachine, }; #[derive(codec::Encode, codec::Decode)] @@ -22,17 +22,17 @@ pub enum Event { Response { /// Chain that this response will be routed to - dest_chain: ChainID, + dest_chain: StateMachine, /// Source Chain for this response - source_chain: ChainID, + source_chain: StateMachine, /// Nonce for the request which this response is for request_nonce: u64, }, Request { /// Chain that this request will be routed to - dest_chain: ChainID, + dest_chain: StateMachine, /// Source Chain for request - source_chain: ChainID, + source_chain: StateMachine, /// Request nonce request_nonce: u64, }, diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 2ef0fef2b..b65ce0604 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -4,13 +4,13 @@ use crate::{ }; use alloc::{format, string::ToString}; use core::time::Duration; -use frame_support::traits::UnixTime; +use frame_support::traits::{Get, UnixTime}; use ismp_rs::{ consensus_client::{ ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }, error::Error, - host::{ChainID, ISMPHost}, + host::{ISMPHost, StateMachine}, router::{ISMPRouter, Request}, util::hash_request, }; @@ -31,8 +31,8 @@ impl ISMPHost for Host where ::Hash: From, { - fn host(&self) -> ChainID { - ::CHAIN_ID + fn host_state_machine(&self) -> StateMachine { + T::StateMachine::get() } fn latest_commitment_height(&self, id: StateMachineId) -> Result { @@ -116,27 +116,27 @@ where Ok(()) } - fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { - ::ConsensusClientProvider::consensus_client(id) - } - - fn challenge_period(&self, id: ConsensusClientId) -> Duration { - ::ConsensusClientProvider::challenge_period(id) - } - - fn ismp_router(&self) -> Box { - Box::new(T::IsmpRouter::default()) - } - fn store_latest_commitment_height(&self, height: StateMachineHeight) -> Result<(), Error> { LatestStateMachineHeight::::insert(height.id, height.height); Ok(()) } + fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { + ::ConsensusClientProvider::consensus_client(id) + } + fn keccak256(bytes: &[u8]) -> H256 where Self: Sized, { sp_io::hashing::keccak_256(bytes).into() } + + fn challenge_period(&self, id: ConsensusClientId) -> Duration { + ::ConsensusClientProvider::challenge_period(id) + } + + fn ismp_router(&self) -> Box { + Box::new(T::IsmpRouter::default()) + } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 322a8bd53..54601a3e2 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -29,7 +29,7 @@ use crate::host::Host; use codec::{Decode, Encode}; use frame_support::{log::debug, RuntimeDebug}; use ismp_rs::{ - host::ChainID, + host::StateMachine, router::{Request, Response}, }; use sp_core::{offchain::StorageKind, H256}; @@ -60,7 +60,7 @@ pub mod pallet { ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }, handlers::{handle_incoming_message, MessageResult}, - host::ChainID, + host::StateMachine, messaging::Message, router::ISMPRouter, }; @@ -85,9 +85,14 @@ pub mod pallet { /// Each node is stored in the Off-chain DB under key derived from the /// [`Self::INDEXING_PREFIX`] and its in-tree index (MMR position). const INDEXING_PREFIX: &'static [u8]; + + /// Admin origin for privileged actions type AdminOrigin: EnsureOrigin; - const CHAIN_ID: ChainID; + /// Host state machine identifier + type StateMachine: Get; + + /// Timestamp provider type TimeProvider: UnixTime; /// Configurable router that dispatches calls to modules @@ -329,18 +334,18 @@ pub mod pallet { /// Response was process successfully Response { /// Chain that this response will be routed to - dest_chain: ChainID, + dest_chain: StateMachine, /// Source Chain for this response - source_chain: ChainID, + source_chain: StateMachine, /// Nonce for the request which this response is for request_nonce: u64, }, /// Request processed successfully Request { /// Chain that this request will be routed to - dest_chain: ChainID, + dest_chain: StateMachine, /// Source Chain for request - source_chain: ChainID, + source_chain: StateMachine, /// Request nonce request_nonce: u64, }, @@ -412,16 +417,16 @@ pub struct RequestResponseLog { impl Pallet { pub fn request_leaf_index_offchain_key( - source_chain: ChainID, - dest_chain: ChainID, + source_chain: StateMachine, + dest_chain: StateMachine, nonce: u64, ) -> Vec { (T::INDEXING_PREFIX, "Requests/leaf_indices", source_chain, dest_chain, nonce).encode() } pub fn response_leaf_index_offchain_key( - source_chain: ChainID, - dest_chain: ChainID, + source_chain: StateMachine, + dest_chain: StateMachine, nonce: u64, ) -> Vec { (T::INDEXING_PREFIX, "Responses/leaf_indices", source_chain, dest_chain, nonce).encode() @@ -462,8 +467,8 @@ impl Pallet { } pub fn get_leaf_index( - source_chain: ChainID, - dest_chain: ChainID, + source_chain: StateMachine, + dest_chain: StateMachine, nonce: u64, is_req: bool, ) -> Option { diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs index 3fb3af924..417ac57b7 100644 --- a/pallet-ismp/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -1,5 +1,5 @@ use crate::{host::Host, mmr, mmr::mmr::Mmr, Config, Event, Pallet, RequestAcks, ResponseAcks}; -use alloc::{format, string::ToString}; +use alloc::{boxed::Box, format, string::ToString}; use codec::{Decode, Encode}; use core::marker::PhantomData; use ismp_primitives::mmr::Leaf; @@ -18,35 +18,36 @@ pub enum Receipt { /// The proxy router, This router allows for routing requests & responses from a source chain /// to a destination chain. -#[derive(Clone)] -pub struct ProxyRouter { - inner: Option, +pub struct ProxyRouter { + inner: Option>, _phantom: PhantomData, } -impl ProxyRouter { +impl ProxyRouter { /// Initialize the proxy router with an inner router. - pub fn new(router: R) -> Self { - Self { inner: Some(router), _phantom: PhantomData } + pub fn new(router: R) -> Self + where + R: ISMPRouter + 'static, + { + Self { inner: Some(Box::new(router)), _phantom: PhantomData } } } -impl Default for ProxyRouter { +impl Default for ProxyRouter { fn default() -> Self { Self { inner: None, _phantom: PhantomData } } } -impl ISMPRouter for ProxyRouter +impl ISMPRouter for ProxyRouter where T: Config, - R: ISMPRouter, ::Hash: From, { fn dispatch(&self, request: Request) -> Result<(), Error> { let host = Host::::default(); - if host.host() != request.dest_chain() { + if host.host_state_machine() != request.dest_chain() { let commitment = hash_request::>(&request).0.to_vec(); if RequestAcks::::contains_key(commitment.clone()) { @@ -90,7 +91,7 @@ where fn write_response(&self, response: Response) -> Result<(), Error> { let host = Host::::default(); - if host.host() != response.request.source_chain() { + if host.host_state_machine() != response.request.source_chain() { let commitment = hash_response::>(&response).0.to_vec(); if ResponseAcks::::contains_key(commitment.clone()) { diff --git a/parachain-consensus/Cargo.toml b/parachain-consensus/Cargo.toml index 050d92506..7fb0d6c8e 100644 --- a/parachain-consensus/Cargo.toml +++ b/parachain-consensus/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ismp-parachain-consensus" +name = "ismp-parachain" version = "0.1.0" edition = "2021" authors = ["Polytope Labs "] @@ -32,6 +32,7 @@ cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branc # local ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } +pallet-ismp = { path = "../pallet-ismp", default-features = false } [dev-dependencies] @@ -52,4 +53,5 @@ std = [ "sp-io/std", "primitive-types/std", "ismp-primitives/std", + "pallet-ismp/std", ] diff --git a/parachain-consensus/src/consensus.rs b/parachain-consensus/src/consensus.rs index 70bacea7f..970040cc8 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain-consensus/src/consensus.rs @@ -26,12 +26,13 @@ use ismp::{ StateMachineId, }, error::Error, - host::ISMPHost, + host::{ISMPHost, StateMachine}, messaging::Proof, router::RequestResponse, }; use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; use merkle_mountain_range::MerkleProof; +use pallet_ismp::host::Host; use primitive_types::H256; use sp_consensus_aura::AURA_ENGINE_ID; use sp_runtime::{ @@ -43,7 +44,7 @@ use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; use crate::RelayChainOracle; /// The parachain consensus client implementation for ISMP. -pub struct ParachainConsensusClient(PhantomData<(T, H)>); +pub struct ParachainConsensusClient(PhantomData); /// Information necessary to prove the sibling parachain's finalization to this /// parachain. @@ -97,10 +98,9 @@ pub const PARACHAIN_CONSENSUS_ID: ConsensusClientId = *b"PARA"; /// Slot duration in milliseconds const SLOT_DURATION: u64 = 12_000; -impl ConsensusClient for ParachainConsensusClient +impl ConsensusClient for ParachainConsensusClient where - H: ISMPHost, - T: frame_system::Config + RelayChainOracle, + T: pallet_ismp::Config + RelayChainOracle, T::BlockNumber: Into, T::Hash: From, { @@ -187,12 +187,17 @@ where let height: u32 = (*header.number()).into(); + let state_id = match _host.host_state_machine() { + StateMachine::Kusama(_) => StateMachine::Kusama(id), + StateMachine::Polkadot(_) => StateMachine::Polkadot(id), + _ => Err(Error::ImplementationSpecific( + "Host state machine should be a parachain".into(), + ))?, + }; + let intermediate = IntermediateState { height: StateMachineHeight { - id: StateMachineId { - state_id: id as u64, - consensus_client: PARACHAIN_CONSENSUS_ID, - }, + id: StateMachineId { state_id, consensus_client: PARACHAIN_CONSENSUS_ID }, height: height as u64, }, commitment: StateCommitment { @@ -224,7 +229,8 @@ where Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) })?; let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = MerkleProof::, MmrHasher>::new(membership.mmr_size, nodes); + let proof = + MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); let leaf = match _item { RequestResponse::Request(req) => Leaf::Request(req), RequestResponse::Response(res) => Leaf::Response(res), From 99c97cfe73e7c0fb8a364dbcebc13fe5ac3a6d5d Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 19 Apr 2023 16:09:47 +0100 Subject: [PATCH 118/182] protocol fixes (#28) * Some protocol fixes * remove CI tests --- .github/workflows/build-test-and-lint.yml | 4 ---- parachain-consensus/src/consensus.rs | 15 +++++++++++---- parachain-consensus/src/lib.rs | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index cc30331f3..cf81b12a4 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -49,10 +49,6 @@ jobs: cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose cargo +nightly check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose - - name: Run tests - run: | - cargo +nightly test --all-features --verbose - lint: runs-on: ubuntu-latest steps: diff --git a/parachain-consensus/src/consensus.rs b/parachain-consensus/src/consensus.rs index 970040cc8..2b84045ef 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain-consensus/src/consensus.rs @@ -44,7 +44,13 @@ use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; use crate::RelayChainOracle; /// The parachain consensus client implementation for ISMP. -pub struct ParachainConsensusClient(PhantomData); +pub struct ParachainConsensusClient(PhantomData<(T, R)>); + +impl Default for ParachainConsensusClient { + fn default() -> Self { + Self(PhantomData) + } +} /// Information necessary to prove the sibling parachain's finalization to this /// parachain. @@ -98,9 +104,10 @@ pub const PARACHAIN_CONSENSUS_ID: ConsensusClientId = *b"PARA"; /// Slot duration in milliseconds const SLOT_DURATION: u64 = 12_000; -impl ConsensusClient for ParachainConsensusClient +impl ConsensusClient for ParachainConsensusClient where - T: pallet_ismp::Config + RelayChainOracle, + R: RelayChainOracle, + T: pallet_ismp::Config, T::BlockNumber: Into, T::Hash: From, { @@ -117,7 +124,7 @@ where )) })?; - let root = T::state_root(update.relay_height).ok_or_else(|| { + let root = R::state_root(update.relay_height).ok_or_else(|| { Error::ImplementationSpecific(format!( "Cannot find relay chain height: {}", update.relay_height diff --git a/parachain-consensus/src/lib.rs b/parachain-consensus/src/lib.rs index eba63da14..58eb04d57 100644 --- a/parachain-consensus/src/lib.rs +++ b/parachain-consensus/src/lib.rs @@ -49,19 +49,19 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// A new relay chain state has been recorded. NewRelayChainState { height: relay_chain::BlockNumber }, } // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(_n: T::BlockNumber) -> Weight { + fn on_finalize(_n: T::BlockNumber) { let state = RelaychainDataProvider::::current_relay_chain_state(); if !RelayChainState::::contains_key(state.number) { RelayChainState::::insert(state.number, state.state_root); Self::deposit_event(Event::::NewRelayChainState { height: state.number }) } - Weight::zero() } } } From 736d0ea42e5816e9daa3e256b0287ac4ecb31a78 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:31:02 +0100 Subject: [PATCH 119/182] Batch messaging update (#29) * batch messaging update * rebase * implement runtime apis * use return result in router --- Cargo.lock | 33 +++++----- pallet-ismp/primitives/Cargo.toml | 2 + pallet-ismp/primitives/src/lib.rs | 10 +++ pallet-ismp/rpc/src/lib.rs | 59 +++++++++-------- pallet-ismp/runtime-api/src/lib.rs | 27 ++++---- pallet-ismp/src/events.rs | 3 + pallet-ismp/src/lib.rs | 53 ++++++++++++++- pallet-ismp/src/router.rs | 97 +++++++++++++++++++--------- parachain-consensus/src/consensus.rs | 61 +++++++++++------ 9 files changed, 234 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb9f7d16e..25ea80c6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,7 +389,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.11", + "rustix 0.37.13", "slab", "socket2", "waker-fn", @@ -895,9 +895,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.2" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ "clap_builder", "clap_derive", @@ -906,9 +906,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.2" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ "anstream", "anstyle", @@ -2988,14 +2988,14 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes 1.0.10", - "rustix 0.37.11", + "rustix 0.37.13", "windows-sys 0.48.0", ] [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#4b8013cada9f69ccbd550d1fc7e2c5107096559e" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#01724bb52d192022532a4a51953a4703a2914731" dependencies = [ "derive_more", "parity-scale-codec", @@ -3035,6 +3035,7 @@ dependencies = [ "ismp", "parity-scale-codec", "primitive-types", + "serde", "sp-runtime", ] @@ -3261,9 +3262,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "libloading" @@ -3861,9 +3862,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f508063cc7bb32987c71511216bd5a32be15bccb6a80b52df8b9d7f01fc3aa2" +checksum = "9b085a4f2cde5781fc4b1717f2e86c62f5cda49de7ba99a7c2eae02b61c9064c" [[package]] name = "lock_api" @@ -3991,7 +3992,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.11", + "rustix 0.37.13", ] [[package]] @@ -5774,15 +5775,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.11" +version = "0.37.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" dependencies = [ "bitflags", "errno 0.3.1", "io-lifetimes 1.0.10", "libc", - "linux-raw-sys 0.3.2", + "linux-raw-sys 0.3.3", "windows-sys 0.48.0", ] @@ -7986,7 +7987,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.11", + "rustix 0.37.13", "windows-sys 0.45.0", ] diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml index 6aae9e1e3..08c6fad6b 100644 --- a/pallet-ismp/primitives/Cargo.toml +++ b/pallet-ismp/primitives/Cargo.toml @@ -16,6 +16,7 @@ ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } primitive-types = { version = "0.12.1", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } [features] default = ["std"] @@ -26,4 +27,5 @@ std = [ "codec/std", "sp-runtime/std", "primitive-types/std", + "serde" ] diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs index e0db97c1a..cc2863fbc 100644 --- a/pallet-ismp/primitives/src/lib.rs +++ b/pallet-ismp/primitives/src/lib.rs @@ -15,4 +15,14 @@ #![cfg_attr(not(feature = "std"), no_std)] +use ismp::host::StateMachine; + pub mod mmr; + +#[derive(codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub struct LeafIndexQuery { + pub source_chain: StateMachine, + pub dest_chain: StateMachine, + pub nonce: u64, +} diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index 95506b877..92dd735ac 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -9,12 +9,15 @@ use jsonrpsee::{ }; use codec::Encode; -use ismp_primitives::mmr::{Leaf, LeafIndex}; +use ismp_primitives::{ + mmr::{Leaf, LeafIndex}, + LeafIndexQuery, +}; use ismp_rs::{ - consensus_client::ConsensusClientId, + consensus_client::{ConsensusClientId, StateMachineId}, router::{Request, Response}, }; -use ismp_runtime_api::{IsmpRuntimeApi, LeafIndexQuery}; +use ismp_runtime_api::IsmpRuntimeApi; use sc_client_api::{BlockBackend, ProofProvider}; use serde::{Deserialize, Serialize}; use sp_api::ProvideRuntimeApi; @@ -99,6 +102,10 @@ where #[method(name = "ismp_queryConsensusUpdateTime")] fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result; + /// Query the latest height for a state machine + #[method(name = "ismp_queryStateMachineLatestHeight")] + fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result; + /// Query ISMP Events that were deposited in a series of blocks /// Using String keys because HashMap fails to deserialize when key is not a String #[method(name = "ibc_queryEvents")] @@ -136,29 +143,23 @@ where fn query_requests(&self, query: Vec) -> Result> { let api = self.client.runtime_api(); let at = self.client.info().best_hash; - let request_indices: Vec = - api.get_request_leaf_indices(at, query).ok().flatten().ok_or_else(|| { - runtime_error_into_rpc_error("Error fetching request leaf indices") - })?; + let request_indices: Vec = api + .get_request_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching request leaf indices"))?; api.get_requests(at, request_indices) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching requests")) + .map_err(|_| runtime_error_into_rpc_error("Error fetching requests")) } fn query_responses(&self, query: Vec) -> Result> { let api = self.client.runtime_api(); let at = self.client.info().best_hash; - let response_indices: Vec = - api.get_response_leaf_indices(at, query).ok().flatten().ok_or_else(|| { - runtime_error_into_rpc_error("Error fetching response leaf indices") - })?; + let response_indices: Vec = api + .get_response_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; api.get_responses(at, response_indices) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching responses")) + .map_err(|_| runtime_error_into_rpc_error("Error fetching responses")) } fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result { @@ -169,10 +170,9 @@ where .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; - let request_indices: Vec = - api.get_request_leaf_indices(at, query).ok().flatten().ok_or_else(|| { - runtime_error_into_rpc_error("Error fetching response leaf indices") - })?; + let request_indices: Vec = api + .get_request_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api .generate_proof(at, request_indices) @@ -189,10 +189,9 @@ where .ok() .flatten() .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; - let response_indices: Vec = - api.get_response_leaf_indices(at, query).ok().flatten().ok_or_else(|| { - runtime_error_into_rpc_error("Error fetching response leaf indices") - })?; + let response_indices: Vec = api + .get_response_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api .generate_proof(at, response_indices) @@ -226,7 +225,15 @@ where api.consensus_update_time(at, client_id) .ok() .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus update time")) + } + + fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.latest_state_machine_height(at, id).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching latest state machine height") + }) } fn query_events( diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index ab7b6592a..16405c12a 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -2,24 +2,18 @@ #![allow(clippy::too_many_arguments)] use ismp_rs::{ - consensus_client::ConsensusClientId, - host::StateMachine, + consensus_client::{ConsensusClientId, StateMachineId}, router::{Request, Response}, }; use pallet_ismp::primitives::{Error, Proof}; -use ismp_primitives::mmr::{Leaf, LeafIndex}; +use ismp_primitives::{ + mmr::{Leaf, LeafIndex}, + LeafIndexQuery, +}; #[cfg(not(feature = "std"))] use sp_std::vec::Vec; -#[derive(codec::Encode, codec::Decode)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -pub struct LeafIndexQuery { - pub source_chain: StateMachine, - pub dest_chain: StateMachine, - pub nonce: u64, -} - sp_api::decl_runtime_apis! { /// ISMP Runtime Apis pub trait IsmpRuntimeApi { @@ -43,16 +37,19 @@ sp_api::decl_runtime_apis! { /// Return the timestamp this client was last updated in seconds fn consensus_update_time(id: ConsensusClientId) -> Option; + /// Return the latest height of the state machine + fn latest_state_machine_height(id: StateMachineId) -> Option; + /// Get Request Leaf Indices - fn get_request_leaf_indices(leaf_queries: Vec) -> Option>; + fn get_request_leaf_indices(leaf_queries: Vec) -> Vec; /// Get Response Leaf Indices - fn get_response_leaf_indices(leaf_queries: Vec) -> Option>; + fn get_response_leaf_indices(leaf_queries: Vec) -> Vec; /// Get actual requests - fn get_requests(leaf_indices: Vec) -> Option>; + fn get_requests(leaf_indices: Vec) -> Vec; /// Get actual requests - fn get_responses(leaf_indices: Vec) -> Option>; + fn get_responses(leaf_indices: Vec) -> Vec; } } diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 8490cb356..0e6496bc5 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -49,6 +49,9 @@ pub fn to_core_protocol_events(event: PalletEvent) -> Option { Some(Event::Request { dest_chain, source_chain, request_nonce }) } + PalletEvent::ChallengePeriodStarted { consensus_client_id, state_machines } => { + Some(Event::ChallengePeriodStarted { consensus_client_id, state_machines }) + } _ => None, } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 54601a3e2..adf0b6aa6 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -29,12 +29,16 @@ use crate::host::Host; use codec::{Decode, Encode}; use frame_support::{log::debug, RuntimeDebug}; use ismp_rs::{ + consensus_client::{ConsensusClientId, StateMachineId}, host::StateMachine, router::{Request, Response}, }; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. -use ismp_primitives::mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}; +use ismp_primitives::{ + mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, + LeafIndexQuery, +}; use mmr::mmr::Mmr; pub use pallet::*; use sp_std::prelude::*; @@ -266,7 +270,7 @@ pub mod pallet { ); } Ok(_) => { - // Do nothing, event has been deposited in ismp router + // Do nothing, event should have been deposited by the ismp router } Err(err) => { errors.push(err.into()); @@ -482,4 +486,49 @@ impl Pallet { } None } + + /// Return the scale encoded consensus state + pub fn get_consensus_state(id: ConsensusClientId) -> Option> { + ConsensusStates::::get(id) + } + + /// Return the timestamp this client was last updated in seconds + pub fn get_consensus_update_time(id: ConsensusClientId) -> Option { + ConsensusClientUpdateTime::::get(id) + } + + /// Return the latest height of the state machine + pub fn get_latest_state_machine_height(id: StateMachineId) -> Option { + LatestStateMachineHeight::::get(id) + } + + /// Get Request Leaf Indices + pub fn get_request_leaf_indices(leaf_queries: Vec) -> Vec { + leaf_queries + .into_iter() + .filter_map(|query| { + Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, true) + }) + .collect() + } + + /// Get Response Leaf Indices + pub fn get_response_leaf_indices(leaf_queries: Vec) -> Vec { + leaf_queries + .into_iter() + .filter_map(|query| { + Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, false) + }) + .collect() + } + + /// Get actual requests + pub fn get_requests(leaf_indices: Vec) -> Vec { + leaf_indices.into_iter().filter_map(|leaf_index| Self::get_request(leaf_index)).collect() + } + + /// Get actual requests + pub fn get_responses(leaf_indices: Vec) -> Vec { + leaf_indices.into_iter().filter_map(|leaf_index| Self::get_response(leaf_index)).collect() + } } diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs index 417ac57b7..21ebb7d81 100644 --- a/pallet-ismp/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -1,12 +1,11 @@ use crate::{host::Host, mmr, mmr::mmr::Mmr, Config, Event, Pallet, RequestAcks, ResponseAcks}; -use alloc::{boxed::Box, format, string::ToString}; +use alloc::{boxed::Box, string::ToString}; use codec::{Decode, Encode}; use core::marker::PhantomData; use ismp_primitives::mmr::Leaf; use ismp_rs::{ - error::Error, host::ISMPHost, - router::{ISMPRouter, Request, Response}, + router::{DispatchError, DispatchResult, DispatchSuccess, ISMPRouter, Request, Response}, util::{hash_request, hash_response}, }; use sp_core::H256; @@ -44,19 +43,19 @@ where T: Config, ::Hash: From, { - fn dispatch(&self, request: Request) -> Result<(), Error> { + fn dispatch(&self, request: Request) -> DispatchResult { let host = Host::::default(); if host.host_state_machine() != request.dest_chain() { let commitment = hash_request::>(&request).0.to_vec(); if RequestAcks::::contains_key(commitment.clone()) { - return Err(Error::ImplementationSpecific(format!( - "Duplicate request: nonce: {} , source: {:?} , dest: {:?}", - request.nonce(), - request.source_chain(), - request.dest_chain() - ))) + Err(DispatchError { + msg: "Duplicate request".to_string(), + nonce: request.nonce(), + source: request.source_chain(), + dest: request.dest_chain(), + })? } let leaves = Pallet::::number_of_leaves(); @@ -65,9 +64,16 @@ where let mut mmr: Mmr = Mmr::new(leaves); let offchain_key = Pallet::::request_leaf_index_offchain_key(source_chain, dest_chain, nonce); - let leaf_index = mmr.push(Leaf::Request(request)).ok_or_else(|| { - Error::ImplementationSpecific("Failed to push request into mmr".to_string()) - })?; + let leaf_index = if let Some(leaf_index) = mmr.push(Leaf::Request(request)) { + leaf_index + } else { + Err(DispatchError { + msg: "Failed to push request into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })? + }; // Deposit Event Pallet::::deposit_event(Event::Request { request_nonce: nonce, @@ -77,30 +83,45 @@ where // Store a map of request to leaf_index Pallet::::store_leaf_index_offchain(offchain_key, leaf_index); RequestAcks::::insert(commitment, Receipt::Ok); + Ok(DispatchSuccess { dest_chain, source_chain, nonce }) } else if let Some(ref router) = self.inner { - router.dispatch(request)? + router.dispatch(request) + } else { + Err(DispatchError { + msg: "Missing a module router".to_string(), + nonce: request.nonce(), + source: request.source_chain(), + dest: request.dest_chain(), + })? } - - Ok(()) } - fn dispatch_timeout(&self, _request: Request) -> Result<(), Error> { - todo!() + fn dispatch_timeout(&self, request: Request) -> DispatchResult { + if let Some(ref router) = self.inner { + router.dispatch(request) + } else { + Err(DispatchError { + msg: "Missing a module router".to_string(), + nonce: request.nonce(), + source: request.source_chain(), + dest: request.dest_chain(), + })? + } } - fn write_response(&self, response: Response) -> Result<(), Error> { + fn write_response(&self, response: Response) -> DispatchResult { let host = Host::::default(); if host.host_state_machine() != response.request.source_chain() { let commitment = hash_response::>(&response).0.to_vec(); if ResponseAcks::::contains_key(commitment.clone()) { - return Err(Error::ImplementationSpecific(format!( - "Duplicate response: nonce: {} , source: {:?} , dest: {:?}", - response.request.nonce(), - response.request.source_chain(), - response.request.dest_chain() - ))) + Err(DispatchError { + msg: "Duplicate response".to_string(), + nonce: response.request.nonce(), + source: response.request.source_chain(), + dest: response.request.dest_chain(), + })? } let leaves = Pallet::::number_of_leaves(); @@ -112,9 +133,17 @@ where let mut mmr: Mmr = Mmr::new(leaves); let offchain_key = Pallet::::response_leaf_index_offchain_key(source_chain, dest_chain, nonce); - let leaf_index = mmr.push(Leaf::Response(response)).ok_or_else(|| { - Error::ImplementationSpecific("Failed to push response into mmr".to_string()) - })?; + let leaf_index = if let Some(leaf_index) = mmr.push(Leaf::Response(response)) { + leaf_index + } else { + Err(DispatchError { + msg: "Failed to push response into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })? + }; + Pallet::::deposit_event(Event::Response { request_nonce: nonce, dest_chain, @@ -122,10 +151,16 @@ where }); Pallet::::store_leaf_index_offchain(offchain_key, leaf_index); ResponseAcks::::insert(commitment, Receipt::Ok); + Ok(DispatchSuccess { dest_chain, source_chain, nonce }) } else if let Some(ref router) = self.inner { - router.write_response(response)? + router.write_response(response) + } else { + Err(DispatchError { + msg: "Missing a module router".to_string(), + nonce: response.request.nonce(), + source: response.request.source_chain(), + dest: response.request.dest_chain(), + })? } - - Ok(()) } } diff --git a/parachain-consensus/src/consensus.rs b/parachain-consensus/src/consensus.rs index 2b84045ef..4597e1877 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain-consensus/src/consensus.rs @@ -85,8 +85,8 @@ pub struct ParachainStateProof { pub struct MembershipProof { /// Size of the mmr at the time this proof was generated pub mmr_size: u64, - /// Mmr pos for this leaf - pub mmr_pos: u64, + /// Leaf indices for the proof + pub leaf_indices: Vec, /// Mmr proof pub proof: Vec, } @@ -228,29 +228,36 @@ where fn verify_membership( &self, _host: &dyn ISMPHost, - _item: RequestResponse, + item: RequestResponse, state: StateCommitment, - _proof: &Proof, + proof: &Proof, ) -> Result<(), Error> { - let membership = MembershipProof::decode(&mut &*_proof.proof).map_err(|e| { + let membership = MembershipProof::decode(&mut &*proof.proof).map_err(|e| { Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) })?; let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); let proof = MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); - let leaf = match _item { - RequestResponse::Request(req) => Leaf::Request(req), - RequestResponse::Response(res) => Leaf::Response(res), + let leaves = match item { + RequestResponse::Request(req) => membership + .leaf_indices + .into_iter() + .zip(req.into_iter()) + .map(|(pos, req)| (pos, DataOrHash::Data(Leaf::Request(req)))) + .collect(), + RequestResponse::Response(res) => membership + .leaf_indices + .into_iter() + .zip(res.into_iter()) + .map(|(pos, res)| (pos, DataOrHash::Data(Leaf::Response(res)))) + .collect(), }; let root = state .ismp_root .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; let valid = proof - .verify( - DataOrHash::Hash(root.into()), - vec![(membership.mmr_pos, DataOrHash::Data(leaf))], - ) + .verify(DataOrHash::Hash(root.into()), leaves) .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; if !valid { @@ -260,17 +267,17 @@ where Ok(()) } - fn state_trie_key(&self, _request: RequestResponse) -> Vec { + fn state_trie_key(&self, _request: RequestResponse) -> Vec> { todo!() } fn verify_state_proof( &self, _host: &dyn ISMPHost, - key: Vec, + keys: Vec>, root: StateCommitment, proof: &Proof, - ) -> Result>, Error> { + ) -> Result>>, Error> { let state_proof: ParachainStateProof = codec::Decode::decode(&mut &*proof.proof) .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; @@ -278,9 +285,15 @@ where HashAlgorithm::Keccak => { let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); - trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!("Error reading state proof: {e:?}")) - })? + keys.into_iter() + .map(|key| { + trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!( + "Error reading state proof: {e:?}" + )) + }) + }) + .collect::, _>>()? } HashAlgorithm::Blake2 => { let db = @@ -288,9 +301,15 @@ where let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); - trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!("Error reading state proof: {e:?}")) - })? + keys.into_iter() + .map(|key| { + trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!( + "Error reading state proof: {e:?}" + )) + }) + }) + .collect::, _>>()? } }; From 9852302d68ec4050f0c1e7139dd705cf6f332cd3 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Sat, 22 Apr 2023 09:10:54 +0100 Subject: [PATCH 120/182] Finalize mmr only when leaves are added (#31) --- pallet-ismp/rpc/src/lib.rs | 2 +- pallet-ismp/src/lib.rs | 29 ++++++++++++++++++++++++++--- pallet-ismp/src/router.rs | 31 +++++++++++++++---------------- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index 92dd735ac..d286f54f1 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -108,7 +108,7 @@ where /// Query ISMP Events that were deposited in a series of blocks /// Using String keys because HashMap fails to deserialize when key is not a String - #[method(name = "ibc_queryEvents")] + #[method(name = "ismp_queryEvents")] fn query_events( &self, block_numbers: Vec>, diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index adf0b6aa6..3f8d0c8ed 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -21,7 +21,7 @@ extern crate alloc; mod errors; pub mod events; pub mod host; -pub mod mmr; +mod mmr; pub mod primitives; pub mod router; @@ -182,6 +182,11 @@ pub mod pallet { OptionQuery, >; + /// State variable that tells us if at least one new leaf was added to the mmr + #[pallet::storage] + #[pallet::getter(fn new_leaves)] + pub type NewLeavesAdded = StorageValue<_, LeafIndex, OptionQuery>; + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet @@ -194,6 +199,10 @@ pub mod pallet { } fn on_finalize(_n: T::BlockNumber) { + // Only finalize if mmr was modified + if !NewLeavesAdded::::exists() { + return + } let leaves = Self::number_of_leaves(); let mmr: Mmr = Mmr::new(leaves); @@ -214,6 +223,7 @@ pub mod pallet { let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, log.encode()); >::deposit_log(digest); + NewLeavesAdded::::kill(); } fn offchain_worker(_n: T::BlockNumber) {} @@ -419,7 +429,10 @@ pub struct RequestResponseLog { mmr_root_hash: ::Hash, } -impl Pallet { +impl Pallet +where + ::Hash: From, +{ pub fn request_leaf_index_offchain_key( source_chain: StateMachine, dest_chain: StateMachine, @@ -436,7 +449,7 @@ impl Pallet { (T::INDEXING_PREFIX, "Responses/leaf_indices", source_chain, dest_chain, nonce).encode() } - fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { + pub fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { sp_io::offchain_index::set(&key, &leaf_index.encode()); } @@ -531,4 +544,14 @@ impl Pallet { pub fn get_responses(leaf_indices: Vec) -> Vec { leaf_indices.into_iter().filter_map(|leaf_index| Self::get_response(leaf_index)).collect() } + + pub fn mmr_push(leaf: Leaf) -> Option { + let leaves = Self::number_of_leaves(); + let mut mmr: Mmr = Mmr::new(leaves); + let index = mmr.push(leaf); + if !NewLeavesAdded::::exists() && index.is_some() { + NewLeavesAdded::::put(index.unwrap()) + } + index + } } diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs index 21ebb7d81..50c912a1a 100644 --- a/pallet-ismp/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -1,4 +1,4 @@ -use crate::{host::Host, mmr, mmr::mmr::Mmr, Config, Event, Pallet, RequestAcks, ResponseAcks}; +use crate::{host::Host, Config, Event, Pallet, RequestAcks, ResponseAcks}; use alloc::{boxed::Box, string::ToString}; use codec::{Decode, Encode}; use core::marker::PhantomData; @@ -58,13 +58,12 @@ where })? } - let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = (request.dest_chain(), request.source_chain(), request.nonce()); - let mut mmr: Mmr = Mmr::new(leaves); let offchain_key = Pallet::::request_leaf_index_offchain_key(source_chain, dest_chain, nonce); - let leaf_index = if let Some(leaf_index) = mmr.push(Leaf::Request(request)) { + let leaf_index = if let Some(leaf_index) = Pallet::::mmr_push(Leaf::Request(request)) + { leaf_index } else { Err(DispatchError { @@ -124,25 +123,25 @@ where })? } - let leaves = Pallet::::number_of_leaves(); let (dest_chain, source_chain, nonce) = ( response.request.source_chain(), response.request.dest_chain(), response.request.nonce(), ); - let mut mmr: Mmr = Mmr::new(leaves); + let offchain_key = Pallet::::response_leaf_index_offchain_key(source_chain, dest_chain, nonce); - let leaf_index = if let Some(leaf_index) = mmr.push(Leaf::Response(response)) { - leaf_index - } else { - Err(DispatchError { - msg: "Failed to push response into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, - })? - }; + let leaf_index = + if let Some(leaf_index) = Pallet::::mmr_push(Leaf::Response(response)) { + leaf_index + } else { + Err(DispatchError { + msg: "Failed to push response into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })? + }; Pallet::::deposit_event(Event::Response { request_nonce: nonce, From ffd9578c1c71d626033bedf7c03dab9819fc8a2f Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Sat, 22 Apr 2023 12:03:33 +0100 Subject: [PATCH 121/182] add genesis config for ismp-parachain (#32) * add genesis config for ismp-parachain * remove alloc import * cargo fmt --- parachain-consensus/src/lib.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/parachain-consensus/src/lib.rs b/parachain-consensus/src/lib.rs index 58eb04d57..1ae83786f 100644 --- a/parachain-consensus/src/lib.rs +++ b/parachain-consensus/src/lib.rs @@ -27,6 +27,7 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { + use super::*; use cumulus_primitives_core::relay_chain; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -36,7 +37,9 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: frame_system::Config + parachain_system::Config { + pub trait Config: + frame_system::Config + pallet_ismp::Config + parachain_system::Config + { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -64,6 +67,27 @@ pub mod pallet { } } } + + #[pallet::genesis_config] + pub struct GenesisConfig; + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + // insert empty bytes + pallet_ismp::ConsensusStates::::insert( + consensus::PARACHAIN_CONSENSUS_ID, + Vec::::new(), + ); + } + } } /// Interface that exposes the relay chain state roots. From 0127439e28cb8a2c808eab33da3c121dc87d2c4a Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Mon, 24 Apr 2023 11:10:41 +0100 Subject: [PATCH 122/182] Parachain consensus (#33) * wip header decode * don't insert intermediate states * refactor call * bump ismp-rs * pin nightly * bump ismp-rs --- .github/workflows/build-test-and-lint.yml | 8 +- Cargo.lock | 254 ++++++++++++---------- pallet-ismp/rpc/src/lib.rs | 2 +- pallet-ismp/runtime-api/src/lib.rs | 2 +- pallet-ismp/src/errors.rs | 2 +- pallet-ismp/src/events.rs | 9 +- pallet-ismp/src/host.rs | 2 +- pallet-ismp/src/lib.rs | 132 ++++++----- pallet-ismp/src/primitives.rs | 2 +- parachain-consensus/Cargo.toml | 2 + parachain-consensus/src/consensus.rs | 114 ++++++---- parachain-consensus/src/lib.rs | 24 +- 12 files changed, 303 insertions(+), 250 deletions(-) diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index cf81b12a4..7ccf775ad 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -27,7 +27,7 @@ jobs: - name: Install toolchain uses: dtolnay/rust-toolchain@nightly with: - toolchain: nightly + toolchain: nightly-2022-10-28 targets: wasm32-unknown-unknown - name: Install Protoc @@ -42,12 +42,12 @@ jobs: - name: Build run: | - cargo +nightly check --workspace --all-targets --all-features --verbose + cargo +nightly-2022-10-28 check --workspace --all-targets --all-features --verbose - name: Build `no-std` run: | - cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose - cargo +nightly check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly-2022-10-28 check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose lint: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 25ea80c6a..fd70d610a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,6 +179,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -389,7 +398,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.13", + "rustix 0.37.14", "slab", "socket2", "waker-fn", @@ -697,9 +706,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "byte-slice-cast" @@ -1026,9 +1035,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -2105,7 +2114,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", ] @@ -2128,7 +2137,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "frame-support", "frame-support-procedural", @@ -2165,7 +2174,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "bitflags", "environmental", @@ -2198,7 +2207,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "Inflector", "cfg-expr", @@ -2213,7 +2222,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -2225,7 +2234,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "proc-macro2", "quote", @@ -2235,7 +2244,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "frame-support", "log", @@ -2504,7 +2513,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -2988,14 +2997,14 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes 1.0.10", - "rustix 0.37.13", + "rustix 0.37.14", "windows-sys 0.48.0", ] [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#01724bb52d192022532a4a51953a4703a2914731" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#32cc5cda31092b265b531dd263fa9d9f82c00065" dependencies = [ "derive_more", "parity-scale-codec", @@ -3013,6 +3022,7 @@ dependencies = [ "cumulus-primitives-core", "frame-support", "frame-system", + "hash-db", "hex-literal 0.4.1", "ismp", "ismp-primitives", @@ -3862,9 +3872,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b085a4f2cde5781fc4b1717f2e86c62f5cda49de7ba99a7c2eae02b61c9064c" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "lock_api" @@ -3964,9 +3974,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +checksum = "bb99c395ae250e1bf9133673f03ca9f97b7e71b705436bf8f089453445d1e9fe" dependencies = [ "rawpointer", ] @@ -3992,7 +4002,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.13", + "rustix 0.37.14", ] [[package]] @@ -5030,9 +5040,9 @@ dependencies = [ [[package]] name = "polling" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags", @@ -5566,13 +5576,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.1", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -5581,7 +5591,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] @@ -5590,6 +5600,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "region" version = "3.0.0" @@ -5761,9 +5777,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.12" +version = "0.36.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0af200a3324fa5bcd922e84e9b55a298ea9f431a489f01961acdebc6e908f25" +checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" dependencies = [ "bitflags", "errno 0.3.1", @@ -5775,15 +5791,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" dependencies = [ "bitflags", "errno 0.3.1", "io-lifetimes 1.0.10", "libc", - "linux-raw-sys 0.3.3", + "linux-raw-sys 0.3.4", "windows-sys 0.48.0", ] @@ -5868,7 +5884,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "log", "sp-core", @@ -5879,7 +5895,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures", @@ -5907,7 +5923,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -5922,7 +5938,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -5941,7 +5957,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5952,7 +5968,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "chrono", @@ -5992,7 +6008,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "fnv", "futures", @@ -6018,7 +6034,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "hash-db", "kvdb", @@ -6044,7 +6060,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures", @@ -6069,7 +6085,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "lru 0.8.1", "parity-scale-codec", @@ -6093,7 +6109,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -6106,7 +6122,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "log", "sc-allocator", @@ -6119,14 +6135,14 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "anyhow", "cfg-if", "libc", "log", "once_cell", - "rustix 0.36.12", + "rustix 0.36.13", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -6137,7 +6153,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "ansi_term", "futures", @@ -6153,7 +6169,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "async-trait", @@ -6168,7 +6184,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "async-channel", @@ -6212,7 +6228,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "cid", "futures", @@ -6232,7 +6248,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "async-trait", @@ -6260,7 +6276,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "futures", @@ -6282,7 +6298,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "async-trait", @@ -6316,7 +6332,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "futures", @@ -6336,7 +6352,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "bytes", @@ -6367,7 +6383,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "futures", "libp2p", @@ -6380,7 +6396,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "futures", "jsonrpsee", @@ -6410,7 +6426,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -6429,7 +6445,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "http", "jsonrpsee", @@ -6444,7 +6460,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "futures", @@ -6470,7 +6486,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "directories", @@ -6536,7 +6552,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "log", "parity-scale-codec", @@ -6547,7 +6563,7 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "clap", "fs4", @@ -6563,7 +6579,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "futures", "libc", @@ -6582,7 +6598,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "chrono", "futures", @@ -6601,7 +6617,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "ansi_term", "atty", @@ -6632,7 +6648,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6643,7 +6659,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures", @@ -6670,7 +6686,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures", @@ -6684,7 +6700,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-channel", "futures", @@ -7102,7 +7118,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "hash-db", "log", @@ -7120,7 +7136,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "Inflector", "blake2", @@ -7134,7 +7150,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7147,7 +7163,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "integer-sqrt", "num-traits", @@ -7161,7 +7177,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7174,7 +7190,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "sp-api", @@ -7186,7 +7202,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "futures", "log", @@ -7204,7 +7220,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures", @@ -7219,7 +7235,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "parity-scale-codec", @@ -7237,7 +7253,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "merlin", @@ -7260,7 +7276,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "finality-grandpa", "log", @@ -7278,7 +7294,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7290,7 +7306,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7303,7 +7319,7 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "array-bytes", "base58", @@ -7346,7 +7362,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "blake2b_simd", "byteorder", @@ -7360,7 +7376,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "proc-macro2", "quote", @@ -7371,7 +7387,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -7380,7 +7396,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "proc-macro2", "quote", @@ -7390,7 +7406,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "environmental", "parity-scale-codec", @@ -7401,7 +7417,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -7416,7 +7432,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "bytes", "ed25519", @@ -7441,7 +7457,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "lazy_static", "sp-core", @@ -7452,7 +7468,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures", @@ -7469,7 +7485,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "thiserror", "zstd", @@ -7478,7 +7494,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "sp-api", "sp-core", @@ -7488,7 +7504,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "backtrace", "lazy_static", @@ -7498,7 +7514,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "rustc-hash", "serde", @@ -7508,7 +7524,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "either", "hash256-std-hasher", @@ -7530,7 +7546,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -7548,7 +7564,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "Inflector", "proc-macro-crate", @@ -7560,7 +7576,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7574,7 +7590,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7586,7 +7602,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "hash-db", "log", @@ -7606,12 +7622,12 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "impl-serde", "parity-scale-codec", @@ -7624,7 +7640,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "futures-timer", @@ -7639,7 +7655,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "sp-std", @@ -7651,7 +7667,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "sp-api", "sp-runtime", @@ -7660,7 +7676,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "async-trait", "log", @@ -7676,7 +7692,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "ahash 0.8.3", "hash-db", @@ -7699,7 +7715,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "impl-serde", "parity-scale-codec", @@ -7716,7 +7732,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -7727,7 +7743,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -7741,7 +7757,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", "scale-info", @@ -7887,7 +7903,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#76fed9b9082daade5392663f25ed8968f8e8c11c" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "hyper", "log", @@ -7987,7 +8003,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.14", "windows-sys 0.45.0", ] @@ -8883,7 +8899,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.12", + "rustix 0.36.13", "serde", "sha2 0.10.6", "toml", @@ -8963,7 +8979,7 @@ checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.36.12", + "rustix 0.36.13", ] [[package]] @@ -8994,7 +9010,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.36.12", + "rustix 0.36.13", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index d286f54f1..751905008 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -14,7 +14,7 @@ use ismp_primitives::{ LeafIndexQuery, }; use ismp_rs::{ - consensus_client::{ConsensusClientId, StateMachineId}, + consensus::{ConsensusClientId, StateMachineId}, router::{Request, Response}, }; use ismp_runtime_api::IsmpRuntimeApi; diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index 16405c12a..7294903ff 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -2,7 +2,7 @@ #![allow(clippy::too_many_arguments)] use ismp_rs::{ - consensus_client::{ConsensusClientId, StateMachineId}, + consensus::{ConsensusClientId, StateMachineId}, router::{Request, Response}, }; use pallet_ismp::primitives::{Error, Proof}; diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index ebafc432c..b7bb323f2 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -1,6 +1,6 @@ use codec::{Decode, Encode}; use ismp_rs::{ - consensus_client::{ConsensusClientId, StateMachineHeight}, + consensus::{ConsensusClientId, StateMachineHeight}, error::Error as IsmpError, host::StateMachine, }; diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 0e6496bc5..28fc1621a 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -1,18 +1,17 @@ use crate::{Config, Event as PalletEvent}; use alloc::collections::BTreeSet; use ismp_rs::{ - consensus_client::{ConsensusClientId, StateMachineHeight, StateMachineId}, + consensus::{ConsensusClientId, StateMachineHeight, StateMachineId}, host::StateMachine, }; #[derive(codec::Encode, codec::Decode)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum Event { - /// Event to be emitted when the challenge period for a state machine update has elapsed + // Emitted when a state machine is successfully updated to a new height StateMachineUpdated { state_machine_id: StateMachineId, latest_height: u64, - previous_height: u64, }, ChallengePeriodStarted { consensus_client_id: ConsensusClientId, @@ -40,8 +39,8 @@ pub enum Event { pub fn to_core_protocol_events(event: PalletEvent) -> Option { match event { - PalletEvent::StateMachineUpdated { state_machine_id, latest_height, previous_height } => { - Some(Event::StateMachineUpdated { state_machine_id, latest_height, previous_height }) + PalletEvent::StateMachineUpdated { state_machine_id, latest_height } => { + Some(Event::StateMachineUpdated { state_machine_id, latest_height }) } PalletEvent::Response { dest_chain, source_chain, request_nonce } => { Some(Event::Response { dest_chain, source_chain, request_nonce }) diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index b65ce0604..e90b6d82b 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -6,7 +6,7 @@ use alloc::{format, string::ToString}; use core::time::Duration; use frame_support::traits::{Get, UnixTime}; use ismp_rs::{ - consensus_client::{ + consensus::{ ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }, error::Error, diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 3f8d0c8ed..d0a10b3e8 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -27,10 +27,12 @@ pub mod router; use crate::host::Host; use codec::{Decode, Encode}; +use core::time::Duration; use frame_support::{log::debug, RuntimeDebug}; use ismp_rs::{ - consensus_client::{ConsensusClientId, StateMachineId}, + consensus::{ConsensusClientId, StateMachineId}, host::StateMachine, + messaging::CreateConsensusClient, router::{Request, Response}, }; use sp_core::{offchain::StorageKind, H256}; @@ -39,6 +41,7 @@ use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; +use ismp_rs::host::ISMPHost; use mmr::mmr::Mmr; pub use pallet::*; use sp_std::prelude::*; @@ -55,15 +58,13 @@ pub mod pallet { primitives::{ConsensusClientProvider, ISMP_ID}, router::Receipt, }; - use alloc::{collections::BTreeSet, string::ToString}; + use alloc::collections::BTreeSet; use frame_support::{pallet_prelude::*, traits::UnixTime}; use frame_system::pallet_prelude::*; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::{ - consensus_client::{ - ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, - }, - handlers::{handle_incoming_message, MessageResult}, + consensus::{ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId}, + handlers::{self, handle_incoming_message, MessageResult}, host::StateMachine, messaging::Message, router::ISMPRouter, @@ -200,30 +201,30 @@ pub mod pallet { fn on_finalize(_n: T::BlockNumber) { // Only finalize if mmr was modified - if !NewLeavesAdded::::exists() { - return - } - let leaves = Self::number_of_leaves(); + let root = if !NewLeavesAdded::::exists() { + >::get() + } else { + let leaves = Self::number_of_leaves(); + let mmr: Mmr = Mmr::new(leaves); + + // Update the size, `mmr.finalize()` should also never fail. + let (leaves, root) = match mmr.finalize() { + Ok((leaves, root)) => (leaves, root), + Err(e) => { + log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); + return + } + }; - let mmr: Mmr = Mmr::new(leaves); + >::put(leaves); + >::put(root); + NewLeavesAdded::::kill(); - // Update the size, `mmr.finalize()` should also never fail. - let (leaves, root) = match mmr.finalize() { - Ok((leaves, root)) => (leaves, root), - Err(e) => { - log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); - return - } + root }; - >::put(leaves); - >::put(root); - - let log = RequestResponseLog:: { mmr_root_hash: root }; - - let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, log.encode()); + let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, root.encode()); >::deposit_log(digest); - NewLeavesAdded::::kill(); } fn offchain_worker(_n: T::BlockNumber) {} @@ -244,40 +245,44 @@ pub mod pallet { let mut errors: Vec = vec![]; for message in messages { - if matches!(message, Message::CreateConsensusClient(_)) { - errors.push(HandlingError::ImplementationSpecific { - msg: "Invalid message for extrinsic".to_string().as_bytes().to_vec(), - }); - continue - } - match handle_incoming_message(&host, message) { Ok(MessageResult::ConsensusMessage(res)) => { - // Deposit events for previous update result that has passed the - // challenge period - if let Some(pending_updates) = - ConsensusUpdateResults::::get(res.consensus_client_id) - { - for (prev_height, latest_height) in pending_updates.into_iter() { + // check if this is a trusted state machine + let is_trusted_state_machine = host + .challenge_period(res.consensus_client_id.clone()) == + Duration::from_secs(0); + + if is_trusted_state_machine { + for (_, latest_height) in res.state_updates.into_iter() { Self::deposit_event(Event::::StateMachineUpdated { state_machine_id: latest_height.id, latest_height: latest_height.height, - previous_height: prev_height.height, }) } - } + } else { + if let Some(pending_updates) = + ConsensusUpdateResults::::get(res.consensus_client_id) + { + for (_, latest_height) in pending_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } - Self::deposit_event(Event::::ChallengePeriodStarted { - consensus_client_id: res.consensus_client_id, - state_machines: res.state_updates.clone(), - }); - - // Store the new update result that have just entered the challenge - // period - ConsensusUpdateResults::::insert( - res.consensus_client_id, - res.state_updates, - ); + Self::deposit_event(Event::::ChallengePeriodStarted { + consensus_client_id: res.consensus_client_id, + state_machines: res.state_updates.clone(), + }); + + // Store the new update result that have just entered the challenge + // period + ConsensusUpdateResults::::insert( + res.consensus_client_id, + res.state_updates, + ); + } } Ok(_) => { // Do nothing, event should have been deposited by the ismp router @@ -299,23 +304,16 @@ pub mod pallet { /// Create consensus clients #[pallet::weight(0)] #[pallet::call_index(1)] - pub fn create_consensus_client(origin: OriginFor, message: Message) -> DispatchResult { + pub fn create_consensus_client( + origin: OriginFor, + message: CreateConsensusClient, + ) -> DispatchResult { ::AdminOrigin::ensure_origin(origin)?; - let host = Host::::default(); - if !matches!(message, Message::CreateConsensusClient(_)) { - Err(Error::::InvalidMessage)? - } - - let result = handle_incoming_message(&host, message) + let result = handlers::create_consensus_client(&host, message) .map_err(|_| Error::::ConsensusClientCreationFailed)?; - let result = match result { - MessageResult::ConsensusClientCreated(res) => res, - _ => Err(Error::::InvalidMessage)?, - }; - Self::deposit_event(Event::::ConsensusClientCreated { consensus_client_id: result.consensus_client_id, }); @@ -332,12 +330,8 @@ pub mod pallet { /// it is optional, it is also possible to provide a custom implementation. #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// Event to be emitted when the challenge period for a state machine update has elapsed - StateMachineUpdated { - state_machine_id: StateMachineId, - latest_height: u64, - previous_height: u64, - }, + /// Emitted when a state machine is successfully updated to a new height + StateMachineUpdated { state_machine_id: StateMachineId, latest_height: u64 }, /// Signifies that a client has begun it's challenge period ChallengePeriodStarted { consensus_client_id: ConsensusClientId, diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index dcc443ec6..45475c0db 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -1,7 +1,7 @@ use core::time::Duration; use frame_support::RuntimeDebug; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; -use ismp_rs::consensus_client::{ConsensusClient, ConsensusClientId}; +use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; use scale_info::TypeInfo; use sp_std::prelude::*; diff --git a/parachain-consensus/Cargo.toml b/parachain-consensus/Cargo.toml index 7fb0d6c8e..511a1e4cd 100644 --- a/parachain-consensus/Cargo.toml +++ b/parachain-consensus/Cargo.toml @@ -14,6 +14,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" hex-literal = "0.4.1" merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } primitive-types = { version = "0.12.1", default-features = false } +hash-db = { version = "0.16.0", default-features = false } # polytope labs ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } @@ -54,4 +55,5 @@ std = [ "primitive-types/std", "ismp-primitives/std", "pallet-ismp/std", + "hash-db/std", ] diff --git a/parachain-consensus/src/consensus.rs b/parachain-consensus/src/consensus.rs index 4597e1877..eb7406ed4 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain-consensus/src/consensus.rs @@ -17,11 +17,11 @@ use core::{marker::PhantomData, time::Duration}; -use alloc::{format, vec, vec::Vec}; +use alloc::{collections::BTreeMap, format, vec, vec::Vec}; use codec::{Decode, Encode}; -use hex_literal::hex; +use core::fmt::Debug; use ismp::{ - consensus_client::{ + consensus::{ ConsensusClient, ConsensusClientId, IntermediateState, StateCommitment, StateMachineHeight, StateMachineId, }, @@ -34,12 +34,14 @@ use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; use merkle_mountain_range::MerkleProof; use pallet_ismp::host::Host; use primitive_types::H256; -use sp_consensus_aura::AURA_ENGINE_ID; +use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; use sp_runtime::{ - traits::{BlakeTwo256, Header, Keccak256}, + app_crypto::sp_core::storage::StorageKey, + generic::Header, + traits::{BlakeTwo256, Header as _, Keccak256}, DigestItem, }; -use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; +use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; use crate::RelayChainOracle; @@ -91,10 +93,6 @@ pub struct MembershipProof { pub proof: Vec, } -/// Static key for parachain headers in the relay chain storage -const PARACHAIN_HEADS_KEY: [u8; 32] = - hex!("cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3"); - /// The `ConsensusEngineId` of ISMP digest in the parachain header. pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; @@ -113,7 +111,7 @@ where { fn verify_consensus( &self, - _host: &dyn ISMPHost, + host: &dyn ISMPHost, state: Vec, proof: Vec, ) -> Result<(Vec, Vec), Error> { @@ -131,32 +129,29 @@ where )) })?; - let db = StorageProof::new(update.storage_proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&db, &root).build(); - - let parachain_heads_key = PARACHAIN_HEADS_KEY.to_vec(); - + let storage_proof = StorageProof::new(update.storage_proof); let mut intermediates = vec![]; for id in update.para_ids { - let mut full_key = parachain_heads_key.clone(); - full_key.extend(sp_io::hashing::twox_64(&*id.encode())); - let header = trie - .get(&full_key) - .map_err(|e| { - Error::ImplementationSpecific( - format!("Error verifying parachain header {e:?}",), - ) - })? - .ok_or_else(|| { - Error::ImplementationSpecific(format!( - "Cannot find parachain header for ParaId({id})", - )) - })?; - + let full_key = parachain_header_storage_key(id); + let header = read_proof_check::( + &root, + storage_proof.clone(), + vec![full_key.as_ref()], + ) + .map_err(|e| { + Error::ImplementationSpecific(format!("Error verifying parachain header {e:?}",)) + })? + .remove(full_key.as_ref()) + .flatten() + .ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Cannot find parachain header for ParaId({id})", + )) + })?; // ideally all parachain headers are the same - let header = T::Header::decode(&mut &*header).map_err(|e| { - Error::ImplementationSpecific(format!("Error decoding parachain header: {e:?}",)) + let header = Header::::decode(&mut &*header).map_err(|e| { + Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) })?; let (mut timestamp, mut ismp_root) = (0, H256::default()); @@ -165,12 +160,10 @@ where DigestItem::PreRuntime(consensus_engine_id, value) if *consensus_engine_id == AURA_ENGINE_ID => { - let slot = u64::decode(&mut &value[..]).map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode beacon message: {e:?}" - )) + let slot = Slot::decode(&mut &value[..]).map_err(|e| { + Error::ImplementationSpecific(format!("Cannot slot: {e:?}")) })?; - timestamp = Duration::from_millis(slot * SLOT_DURATION).as_secs(); + timestamp = Duration::from_millis(*slot * SLOT_DURATION).as_secs(); } DigestItem::Consensus(consensus_engine_id, value) if *consensus_engine_id == ISMP_ID => @@ -188,13 +181,13 @@ where }; } - if timestamp == 0 || ismp_root == H256::default() { + if timestamp == 0 { Err(Error::ImplementationSpecific("Timestamp or ismp root not found".into()))? } let height: u32 = (*header.number()).into(); - let state_id = match _host.host_state_machine() { + let state_id = match host.host_state_machine() { StateMachine::Kusama(_) => StateMachine::Kusama(id), StateMachine::Polkadot(_) => StateMachine::Polkadot(id), _ => Err(Error::ImplementationSpecific( @@ -321,3 +314,44 @@ where Ok(()) } } + +/// This returns the storage key for a parachain header on the relay chain. +pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { + let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec(); + let encoded_para_id = para_id.encode(); + storage_key.extend_from_slice(sp_io::hashing::twox_64(&encoded_para_id).as_slice()); + storage_key.extend_from_slice(&encoded_para_id); + StorageKey(storage_key) +} + +/// Lifted directly from [`sp_state_machine::read_proof_check`](https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1075-L1094) +pub fn read_proof_check( + root: &H::Out, + proof: StorageProof, + keys: I, +) -> Result, Option>>, Error> +where + H: hash_db::Hasher, + H::Out: Debug, + I: IntoIterator, + I::Item: AsRef<[u8]>, +{ + let db = proof.into_memory_db(); + + if !db.contains(root, EMPTY_PREFIX) { + Err(Error::ImplementationSpecific("Invalid Proof".into()))? + } + + let trie = TrieDBBuilder::>::new(&db, root).build(); + let mut result = BTreeMap::new(); + + for key in keys.into_iter() { + let value = trie + .get(key.as_ref()) + .map_err(|e| Error::ImplementationSpecific(format!("Error reading from trie: {e:?}")))? + .and_then(|val| Decode::decode(&mut &val[..]).ok()); + result.insert(key.as_ref().to_vec(), value); + } + + Ok(result) +} diff --git a/parachain-consensus/src/lib.rs b/parachain-consensus/src/lib.rs index 1ae83786f..38158df37 100644 --- a/parachain-consensus/src/lib.rs +++ b/parachain-consensus/src/lib.rs @@ -19,6 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +extern crate core; pub mod consensus; @@ -50,11 +51,7 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// A new relay chain state has been recorded. - NewRelayChainState { height: relay_chain::BlockNumber }, - } + pub enum Event {} // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] @@ -63,18 +60,23 @@ pub mod pallet { let state = RelaychainDataProvider::::current_relay_chain_state(); if !RelayChainState::::contains_key(state.number) { RelayChainState::::insert(state.number, state.state_root); - Self::deposit_event(Event::::NewRelayChainState { height: state.number }) + + let digest = sp_runtime::generic::DigestItem::Consensus( + consensus::PARACHAIN_CONSENSUS_ID, + state.number.encode(), + ); + >::deposit_log(digest); } } } #[pallet::genesis_config] - pub struct GenesisConfig; + pub struct GenesisConfig {} #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { - GenesisConfig + GenesisConfig {} } } @@ -86,6 +88,12 @@ pub mod pallet { consensus::PARACHAIN_CONSENSUS_ID, Vec::::new(), ); + + pallet_ismp::ConsensusClientUpdateTime::::insert( + consensus::PARACHAIN_CONSENSUS_ID, + // parachains have no challenge period + 0, + ); } } } From 237b3bf1b37dd453c4131d2300f91143620fa494 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 28 Apr 2023 11:16:21 +0100 Subject: [PATCH 123/182] Demo ismp-assets (#34) --- .github/workflows/build-test-and-lint.yml | 4 + Cargo.lock | 37 ++++- Cargo.toml | 1 + ismp-assets/Cargo.toml | 38 +++++ ismp-assets/src/lib.rs | 184 ++++++++++++++++++++++ pallet-ismp/Cargo.toml | 4 + pallet-ismp/rpc/src/lib.rs | 1 - pallet-ismp/runtime-api/src/lib.rs | 2 +- pallet-ismp/src/events.rs | 4 +- pallet-ismp/src/lib.rs | 106 ++++++++++--- pallet-ismp/src/mmr.rs | 2 +- pallet-ismp/src/mmr/mmr.rs | 36 ++--- pallet-ismp/src/mmr/storage.rs | 7 +- pallet-ismp/src/mmr/utils.rs | 32 ---- pallet-ismp/src/mock.rs | 101 ++++++++++++ pallet-ismp/src/primitives.rs | 41 ++++- pallet-ismp/src/router.rs | 41 ++--- pallet-ismp/src/tests.rs | 161 +++++++++++++++++++ parachain-consensus/Cargo.toml | 1 - parachain-consensus/src/consensus.rs | 7 +- 20 files changed, 687 insertions(+), 123 deletions(-) create mode 100644 ismp-assets/Cargo.toml create mode 100644 ismp-assets/src/lib.rs create mode 100644 pallet-ismp/src/mock.rs create mode 100644 pallet-ismp/src/tests.rs diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index 7ccf775ad..8025286c5 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -49,6 +49,10 @@ jobs: cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose cargo +nightly-2022-10-28 check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose + - name: Test + run: | + cargo +nightly-2022-10-28 test -p pallet-ismp --all-targets --all-features --verbose + lint: runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index fd70d610a..94b2b0152 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3004,7 +3004,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#32cc5cda31092b265b531dd263fa9d9f82c00065" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#f20b0486956ded6f7148fafe6ab9700db7fc70dc" dependencies = [ "derive_more", "parity-scale-codec", @@ -3013,6 +3013,19 @@ dependencies = [ "serde", ] +[[package]] +name = "ismp-assets" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "ismp", + "pallet-ismp", + "parity-scale-codec", + "scale-info", + "sp-runtime", +] + [[package]] name = "ismp-parachain" version = "0.1.0" @@ -4592,12 +4605,14 @@ version = "0.1.0" dependencies = [ "ckb-merkle-mountain-range", "derive_more", + "env_logger", "frame-benchmarking", "frame-support", "frame-system", "ismp", "ismp-primitives", "log", + "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", @@ -4608,6 +4623,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + [[package]] name = "parity-db" version = "0.4.6" @@ -8530,7 +8563,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index 3b4eed353..062f308e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ members = [ "pallet-ismp/primitives", "pallet-ismp", "parachain-consensus", + "ismp-assets" ] diff --git a/ismp-assets/Cargo.toml b/ismp-assets/Cargo.toml new file mode 100644 index 000000000..185d7e6dd --- /dev/null +++ b/ismp-assets/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "ismp-assets" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +# crates.io +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# polytope labs +ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } + +# substrate +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } + +# local +pallet-ismp = { path = "../pallet-ismp", default-features = false } + +[dev-dependencies] + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "scale-info/std", + "ismp/std", + "pallet-ismp/std" +] diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs new file mode 100644 index 000000000..308f26f1c --- /dev/null +++ b/ismp-assets/src/lib.rs @@ -0,0 +1,184 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ISMP Assets +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::string::ToString; +use frame_support::{traits::fungible::Mutate, PalletId}; +use ismp::{ + module::ISMPModule, + router::{Request, Response}, +}; +pub use pallet::*; + +pub const PALLET_ID: PalletId = PalletId(*b"ismp-ast"); + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{Inspect, Mutate}, + tokens::Balance, + }, + }; + use frame_system::pallet_prelude::*; + use ismp::host::StateMachine; + use pallet_ismp::primitives::{IsmpDispatch, IsmpMessage}; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type Balance: Balance + Into<>::Balance>; + type NativeCurrency: Mutate; + type IsmpDispatch: IsmpDispatch; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + BalanceTransferred { + from: T::AccountId, + to: T::AccountId, + amount: T::Balance, + dest_chain: StateMachine, + }, + + BalanceReceived { + from: T::AccountId, + to: T::AccountId, + amount: T::Balance, + source_chain: StateMachine, + }, + } + + #[pallet::error] + pub enum Error { + TransferFailed, + } + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { + #[pallet::weight(1_000_000)] + #[pallet::call_index(0)] + pub fn transfer( + origin: OriginFor, + params: TransferParams, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; + let request = IsmpMessage::Post { + dest_chain: params.dest_chain, + from: PALLET_ID.0.to_vec(), + to: PALLET_ID.0.to_vec(), + timeout_timestamp: params.timeout, + data: payload.encode(), + }; + + T::IsmpDispatch::dispatch_message(request).map_err(|_| Error::::TransferFailed)?; + >::burn_from(&origin, params.amount.into())?; + Self::deposit_event(Event::::BalanceTransferred { + from: payload.from, + to: payload.to, + amount: payload.amount, + dest_chain: params.dest_chain, + }); + Ok(()) + } + } + + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct Payload { + pub to: AccountId, + pub from: AccountId, + pub amount: Balance, + } + + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct TransferParams { + pub to: AccountId, + pub amount: Balance, + pub dest_chain: StateMachine, + /// Timeout timestamp in seconds + pub timeout: u64, + } +} + +fn ismp_dispatch_error(msg: &'static str) -> ismp::error::Error { + ismp::error::Error::ImplementationSpecific(msg.to_string()) +} + +impl ISMPModule for Pallet { + fn on_accept(request: Request) -> Result<(), ismp::error::Error> { + let source_chain = request.source_chain(); + let data = match request { + Request::Post(post) => post.data, + _ => Err(ismp_dispatch_error("Only Post requests allowed, found Get"))?, + }; + + let payload = as codec::Decode>::decode(&mut &*data) + .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + >::mint_into(&payload.to, payload.amount.into()) + .map_err(|_| ismp_dispatch_error("Failed to mint funds"))?; + Pallet::::deposit_event(Event::::BalanceReceived { + from: payload.from, + to: payload.to, + amount: payload.amount, + source_chain, + }); + Ok(()) + } + + fn on_response(_response: Response) -> Result<(), ismp::error::Error> { + Err(ismp_dispatch_error("Balance transfer protocol does not accept responses")) + } + + fn on_timeout(request: Request) -> Result<(), ismp::error::Error> { + let source_chain = request.source_chain(); + let data = match request { + Request::Post(post) => post.data, + _ => Err(ismp_dispatch_error("Only Post requests allowed, found Get"))?, + }; + let payload = as codec::Decode>::decode(&mut &*data) + .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + >::mint_into( + &payload.from, + payload.amount.into(), + ) + .map_err(|_| ismp_dispatch_error("Failed to mint funds"))?; + Pallet::::deposit_event(Event::::BalanceReceived { + from: payload.from, + to: payload.to, + amount: payload.amount, + source_chain, + }); + Ok(()) + } +} diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index b455aba09..cfbe180e2 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -29,6 +29,10 @@ derive_more = { version = "0.99.17", default-features = false, features = ["from # local ismp-primitives = { path = "./primitives", default-features = false } +[dev-dependencies] +env_logger = "0.10.0" +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } + [features] default = ["std"] std = [ diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index 751905008..1175516c5 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -255,7 +255,6 @@ where let temp = api .block_events(at) .ok() - .flatten() .ok_or_else(|| runtime_error_into_rpc_error("failed to read block events"))?; events.insert(block_number_or_hash.to_string(), temp); } diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index 7294903ff..e325791d0 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -29,7 +29,7 @@ sp_api::decl_runtime_apis! { ) -> Result<(Vec, Proof), Error>; /// Fetch all ISMP events - fn block_events() -> Option>; + fn block_events() -> Vec; /// Return the scale encoded consensus state fn consensus_state(id: ConsensusClientId) -> Option>; diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 28fc1621a..74d0060b2 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -5,7 +5,7 @@ use ismp_rs::{ host::StateMachine, }; -#[derive(codec::Encode, codec::Decode)] +#[derive(Clone, codec::Encode, codec::Decode, Debug)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum Event { // Emitted when a state machine is successfully updated to a new height @@ -37,7 +37,7 @@ pub enum Event { }, } -pub fn to_core_protocol_events(event: PalletEvent) -> Option { +pub fn to_core_protocol_event(event: PalletEvent) -> Option { match event { PalletEvent::StateMachineUpdated { state_machine_id, latest_height } => { Some(Event::StateMachineUpdated { state_machine_id, latest_height }) diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index d0a10b3e8..d2ba2db37 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -22,13 +22,19 @@ mod errors; pub mod events; pub mod host; mod mmr; +#[cfg(test)] +mod mock; pub mod primitives; pub mod router; +#[cfg(test)] +mod tests; + +pub use mmr::utils::NodesUtils; use crate::host::Host; use codec::{Decode, Encode}; use core::time::Duration; -use frame_support::{log::debug, RuntimeDebug}; +use frame_support::{log::debug, traits::Get, RuntimeDebug}; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, host::StateMachine, @@ -37,11 +43,12 @@ use ismp_rs::{ }; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. +use crate::primitives::{IsmpDispatch, IsmpMessage}; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; -use ismp_rs::host::ISMPHost; +use ismp_rs::{host::ISMPHost, router::ISMPRouter}; use mmr::mmr::Mmr; pub use pallet::*; use sp_std::prelude::*; @@ -183,10 +190,10 @@ pub mod pallet { OptionQuery, >; - /// State variable that tells us if at least one new leaf was added to the mmr + /// Latest Nonce value for messages sent from this chain #[pallet::storage] - #[pallet::getter(fn new_leaves)] - pub type NewLeavesAdded = StorageValue<_, LeafIndex, OptionQuery>; + #[pallet::getter(fn nonce)] + pub type Nonce = StorageValue<_, u64, ValueQuery>; // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] @@ -201,26 +208,23 @@ pub mod pallet { fn on_finalize(_n: T::BlockNumber) { // Only finalize if mmr was modified - let root = if !NewLeavesAdded::::exists() { - >::get() - } else { - let leaves = Self::number_of_leaves(); + let leaves = Self::number_of_leaves(); + let root = if leaves != 0 { let mmr: Mmr = Mmr::new(leaves); - // Update the size, `mmr.finalize()` should also never fail. - let (leaves, root) = match mmr.finalize() { - Ok((leaves, root)) => (leaves, root), + let root = match mmr.finalize() { + Ok(root) => root, Err(e) => { log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); return } }; - >::put(leaves); >::put(root); - NewLeavesAdded::::kill(); root + } else { + H256::default().into() }; let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, root.encode()); @@ -390,6 +394,11 @@ where pub fn mmr_root() -> ::Hash { Self::mmr_root_hash() } + + /// Return mmr leaf count + pub fn mmr_leaf_count() -> LeafIndex { + Self::number_of_leaves() + } } impl Pallet { @@ -414,7 +423,7 @@ impl Pallet { } fn offchain_key(pos: NodeIndex) -> Vec { - (T::INDEXING_PREFIX, "Requests/Responses", pos).encode() + (T::INDEXING_PREFIX, "leaves", pos).encode() } } @@ -432,7 +441,7 @@ where dest_chain: StateMachine, nonce: u64, ) -> Vec { - (T::INDEXING_PREFIX, "Requests/leaf_indices", source_chain, dest_chain, nonce).encode() + (T::INDEXING_PREFIX, "requests_leaf_indices", source_chain, dest_chain, nonce).encode() } pub fn response_leaf_index_offchain_key( @@ -440,7 +449,7 @@ where dest_chain: StateMachine, nonce: u64, ) -> Vec { - (T::INDEXING_PREFIX, "Responses/leaf_indices", source_chain, dest_chain, nonce).encode() + (T::INDEXING_PREFIX, "responses_leaf_indices", source_chain, dest_chain, nonce).encode() } pub fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { @@ -540,12 +549,65 @@ where } pub fn mmr_push(leaf: Leaf) -> Option { + let offchain_key = match &leaf { + Leaf::Request(req) => Pallet::::request_leaf_index_offchain_key( + req.source_chain(), + req.dest_chain(), + req.nonce(), + ), + Leaf::Response(res) => Pallet::::response_leaf_index_offchain_key( + res.request.dest_chain(), + res.request.source_chain(), + res.request.nonce(), + ), + }; let leaves = Self::number_of_leaves(); - let mut mmr: Mmr = Mmr::new(leaves); - let index = mmr.push(leaf); - if !NewLeavesAdded::::exists() && index.is_some() { - NewLeavesAdded::::put(index.unwrap()) + let mmr: Mmr = Mmr::new(leaves); + let pos = mmr.push(leaf)?; + Pallet::::store_leaf_index_offchain(offchain_key, pos); + Some(pos) + } +} + +impl Pallet { + fn next_nonce() -> u64 { + let nonce = Nonce::::get(); + Nonce::::put(nonce + 1); + nonce + } +} + +impl IsmpDispatch for Pallet { + fn dispatch_message(msg: IsmpMessage) -> Result<(), ismp_rs::router::DispatchError> { + let router = T::IsmpRouter::default(); + match msg { + IsmpMessage::Post { timeout_timestamp, dest_chain, data, from, to } => { + let post = ismp_rs::router::Post { + source_chain: T::StateMachine::get(), + dest_chain, + nonce: Pallet::::next_nonce(), + from, + to, + timeout_timestamp, + data, + }; + router.dispatch(Request::Post(post)).map(|_| ()) + } + IsmpMessage::Get { timeout_timestamp, dest_chain, keys, height, from } => { + let get = ismp_rs::router::Get { + source_chain: T::StateMachine::get(), + dest_chain, + nonce: Pallet::::next_nonce(), + from, + keys, + height, + timeout_timestamp, + }; + router.dispatch(Request::Get(get)).map(|_| ()) + } + IsmpMessage::Response { response, request } => { + router.write_response(Response { request, response }).map(|_| ()) + } } - index } } diff --git a/pallet-ismp/src/mmr.rs b/pallet-ismp/src/mmr.rs index 1ac76f215..0951de5d9 100644 --- a/pallet-ismp/src/mmr.rs +++ b/pallet-ismp/src/mmr.rs @@ -1,3 +1,3 @@ pub mod mmr; pub mod storage; -mod utils; +pub mod utils; diff --git a/pallet-ismp/src/mmr/mmr.rs b/pallet-ismp/src/mmr/mmr.rs index d7692f699..d3249c488 100644 --- a/pallet-ismp/src/mmr/mmr.rs +++ b/pallet-ismp/src/mmr/mmr.rs @@ -51,12 +51,6 @@ where let size = NodesUtils::new(leaves).size(); Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves } } - - /// Return the internal size of the MMR (number of nodes). - #[cfg(test)] - pub fn size(&self) -> NodeIndex { - self.mmr.mmr_size() - } } /// Runtime specific MMR functions. @@ -65,23 +59,21 @@ where T: Config, ::Hash: From, { - /// Push another item to the MMR. + /// Push another item to the MMR and commit /// - /// Returns element position (index) in the MMR. - pub fn push(&mut self, leaf: Leaf) -> Option { + /// Returns number of leaves and the element position (index) in the MMR. + pub fn push(mut self, leaf: Leaf) -> Option { let position = self.mmr.push(DataOrHash::Data(leaf)).map_err(|_| Error::Push).ok()?; - - self.leaves += 1; - + let num_leaves = self.leaves + 1; + self.leaves = num_leaves; + self.mmr.commit().ok()?; Some(position) } - /// Commit the changes to underlying storage, return current number of leaves and - /// calculate the new MMR's root hash. - pub fn finalize(self) -> Result<(NodeIndex, ::Hash), Error> { + /// Calculate the new MMR's root hash. + pub fn finalize(self) -> Result<::Hash, Error> { let root = self.mmr.get_root().map_err(|_| Error::GetRoot)?; - self.mmr.commit().map_err(|_| Error::Commit)?; - Ok((self.leaves, root.hash::>())) + Ok(root.hash::>()) } } @@ -97,10 +89,8 @@ where /// (i.e. you can't run the function in the pruned storage). pub fn generate_proof( &self, - leaf_indices: Vec, + positions: Vec, ) -> Result<(Vec, Proof<::Hash>), Error> { - let positions = - leaf_indices.iter().map(|index| mmr_lib::leaf_index_to_pos(*index)).collect::>(); let store = >::default(); let leaves = positions .iter() @@ -109,13 +99,13 @@ where _ => Err(Error::LeafNotFound), }) .collect::, Error>>()?; - + log::trace!(target: "runtime::mmr", "Positions {:?}", positions); let leaf_count = self.leaves; self.mmr - .gen_proof(positions) + .gen_proof(positions.clone()) .map_err(|_| Error::GenerateProof) .map(|p| Proof { - leaf_indices, + leaf_indices: positions, leaf_count, items: p.proof_items().iter().map(|x| x.hash::>()).collect(), }) diff --git a/pallet-ismp/src/mmr/storage.rs b/pallet-ismp/src/mmr/storage.rs index 843a7da86..122222d89 100644 --- a/pallet-ismp/src/mmr/storage.rs +++ b/pallet-ismp/src/mmr/storage.rs @@ -58,13 +58,10 @@ where T: Config, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { - // Find out which leaf added node `pos` in the MMR. - let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos); - let key = Pallet::::offchain_key(pos); debug!( - target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, key {:?}", - pos, ancestor_leaf_idx, key + target: "runtime::mmr::offchain", "offchain db get {}: key {:?}", + pos, key ); // Try to retrieve the element from Off-chain DB. if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { diff --git a/pallet-ismp/src/mmr/utils.rs b/pallet-ismp/src/mmr/utils.rs index bdf4830ce..64c788826 100644 --- a/pallet-ismp/src/mmr/utils.rs +++ b/pallet-ismp/src/mmr/utils.rs @@ -1,6 +1,4 @@ -use alloc::vec::Vec; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; -use mmr_lib::helper; /// MMR nodes & size -related utilities. pub struct NodesUtils { @@ -27,34 +25,4 @@ impl NodesUtils { pub fn size(&self) -> NodeIndex { 2 * self.no_of_leaves - self.number_of_peaks() } - - /// Calculate `LeafIndex` for the leaf that added `node_index` to the MMR. - pub fn leaf_index_that_added_node(node_index: NodeIndex) -> LeafIndex { - let rightmost_leaf_pos = Self::rightmost_leaf_node_index_from_pos(node_index); - Self::leaf_node_index_to_leaf_index(rightmost_leaf_pos) - } - - // Translate a _leaf_ `NodeIndex` to its `LeafIndex`. - fn leaf_node_index_to_leaf_index(pos: NodeIndex) -> LeafIndex { - if pos == 0 { - return 0 - } - let peaks = helper::get_peaks(pos); - (pos + peaks.len() as u64) >> 1 - } - - // Starting from any node position get position of rightmost leaf; this is the leaf - // responsible for the addition of node `pos`. - fn rightmost_leaf_node_index_from_pos(pos: NodeIndex) -> NodeIndex { - pos - (helper::pos_height_in_tree(pos) as u64) - } - - /// Starting from any leaf index, get the sequence of positions of the nodes added - /// to the mmr when this leaf was added (inclusive of the leaf's position itself). - /// That is, all of these nodes are right children of their respective parents. - pub fn _right_branch_ending_in_leaf(leaf_index: LeafIndex) -> Vec { - let pos = helper::leaf_index_to_pos(leaf_index); - let num_parents = leaf_index.trailing_ones() as u64; - return (pos..=pos + num_parents).collect() - } } diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs new file mode 100644 index 000000000..ae4d971bf --- /dev/null +++ b/pallet-ismp/src/mock.rs @@ -0,0 +1,101 @@ +use crate as pallet_ismp; +use crate::*; + +use crate::{primitives::ConsensusClientProvider, router::ProxyRouter}; +use frame_support::traits::{ConstU32, ConstU64, Get}; +use frame_system::EnsureRoot; +use ismp_rs::consensus::ConsensusClient; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{IdentityLookup, Keccak256}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( +pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, + } +); + +pub struct StateMachineProvider; + +impl Get for StateMachineProvider { + fn get() -> StateMachine { + StateMachine::Kusama(2000) + } +} + +pub struct ConsensusProvider; + +impl ConsensusClientProvider for ConsensusProvider { + fn consensus_client( + id: ConsensusClientId, + ) -> Result, ismp_rs::error::Error> { + let client = match id { + _ => Err(ismp_rs::error::Error::ImplementationSpecific( + "Unknown consensus client".into(), + ))?, + }; + + Ok(client) + } + + fn challenge_period(id: ConsensusClientId) -> Duration { + match id { + _ => Duration::MAX, + } + } +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = Keccak256; + type AccountId = sp_core::sr25519::Public; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + const INDEXING_PREFIX: &'static [u8] = b"ISMP"; + type AdminOrigin = EnsureRoot; + type StateMachine = StateMachineProvider; + type TimeProvider = Timestamp; + type IsmpRouter = ProxyRouter; + type ConsensusClientProvider = ConsensusProvider; +} diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 45475c0db..13282062b 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -1,7 +1,11 @@ use core::time::Duration; use frame_support::RuntimeDebug; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; -use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; +use ismp_rs::{ + consensus::{ConsensusClient, ConsensusClientId, StateMachineHeight}, + host::StateMachine, + router::Request, +}; use scale_info::TypeInfo; use sp_std::prelude::*; @@ -41,3 +45,38 @@ pub trait ConsensusClientProvider { fn challenge_period(id: ConsensusClientId) -> Duration; } + +pub enum IsmpMessage { + Post { + /// The destination state machine of this request. + dest_chain: StateMachine, + /// Moudle Id of the sending module + from: Vec, + /// Module ID of the receiving module + to: Vec, + /// Timestamp which this request expires in seconds. + timeout_timestamp: u64, + /// Encoded Request. + data: Vec, + }, + Get { + /// The destination state machine of this request. + dest_chain: StateMachine, + /// Moudle Id of the sending module + from: Vec, + /// Storage keys that this request is interested in. + keys: Vec>, + /// Height at which to read the state machine. + height: StateMachineHeight, + /// Timestamp which this request expires in seconds + timeout_timestamp: u64, + }, + Response { + request: Request, + response: Vec, + }, +} + +pub trait IsmpDispatch { + fn dispatch_message(msg: IsmpMessage) -> Result<(), ismp_rs::router::DispatchError>; +} diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs index 50c912a1a..b0e594cb7 100644 --- a/pallet-ismp/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -60,27 +60,18 @@ where let (dest_chain, source_chain, nonce) = (request.dest_chain(), request.source_chain(), request.nonce()); - let offchain_key = - Pallet::::request_leaf_index_offchain_key(source_chain, dest_chain, nonce); - let leaf_index = if let Some(leaf_index) = Pallet::::mmr_push(Leaf::Request(request)) - { - leaf_index - } else { - Err(DispatchError { - msg: "Failed to push request into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, - })? - }; + Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError { + msg: "Failed to push request into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })?; // Deposit Event Pallet::::deposit_event(Event::Request { request_nonce: nonce, source_chain, dest_chain, }); - // Store a map of request to leaf_index - Pallet::::store_leaf_index_offchain(offchain_key, leaf_index); RequestAcks::::insert(commitment, Receipt::Ok); Ok(DispatchSuccess { dest_chain, source_chain, nonce }) } else if let Some(ref router) = self.inner { @@ -129,26 +120,18 @@ where response.request.nonce(), ); - let offchain_key = - Pallet::::response_leaf_index_offchain_key(source_chain, dest_chain, nonce); - let leaf_index = - if let Some(leaf_index) = Pallet::::mmr_push(Leaf::Response(response)) { - leaf_index - } else { - Err(DispatchError { - msg: "Failed to push response into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, - })? - }; + Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { + msg: "Failed to push response into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })?; Pallet::::deposit_event(Event::Response { request_nonce: nonce, dest_chain, source_chain, }); - Pallet::::store_leaf_index_offchain(offchain_key, leaf_index); ResponseAcks::::insert(commitment, Receipt::Ok); Ok(DispatchSuccess { dest_chain, source_chain, nonce }) } else if let Some(ref router) = self.inner { diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs new file mode 100644 index 000000000..a15b10501 --- /dev/null +++ b/pallet-ismp/src/tests.rs @@ -0,0 +1,161 @@ +use crate::{mock::*, *}; +use std::ops::Range; + +use frame_support::traits::OnFinalize; +use ismp_primitives::mmr::MmrHasher; +use mmr_lib::MerkleProof; +use sp_core::{ + offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, + H256, +}; + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} + +fn register_offchain_ext(ext: &mut sp_io::TestExternalities) { + let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); + ext.register_extension(OffchainDbExt::new(offchain.clone())); + ext.register_extension(OffchainWorkerExt::new(offchain)); +} + +fn new_block() { + let number = frame_system::Pallet::::block_number() + 1; + let hash = H256::repeat_byte(number as u8); + + frame_system::Pallet::::reset_events(); + frame_system::Pallet::::initialize(&number, &hash, &Default::default()); + Ismp::on_finalize(number) +} + +fn push_leaves(range: Range) -> Vec { + // given + let mut positions = vec![]; + for nonce in range { + let post = ismp_rs::router::Post { + source_chain: StateMachine::Kusama(2000), + dest_chain: StateMachine::Kusama(2001), + nonce, + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100 * nonce, + data: vec![2u8; 64], + }; + + let request = Request::Post(post); + let leaf = Leaf::Request(request); + + let pos = Pallet::::mmr_push(leaf.clone()).unwrap(); + positions.push(pos) + } + + positions +} + +#[test] +fn should_generate_proofs_correctly_for_single_leaf_mmr() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + let (root, positions) = ext.execute_with(|| { + // push some leaves into the mmr + let positions = push_leaves(0..1); + new_block(); + let root = Pallet::::mmr_root(); + (root, positions) + }); + ext.persist_offchain_overlay(); + + // Try to generate proofs now. This requires the offchain extensions to be present + // to retrieve full leaf data. + register_offchain_ext(&mut ext); + ext.execute_with(move || { + let (leaves, proof) = Pallet::::generate_proof(vec![positions[0]]).unwrap(); + + let mmr_size = NodesUtils::new(proof.leaf_count).size(); + let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = + MerkleProof::, MmrHasher>>::new(mmr_size, nodes); + let calculated_root = proof + .calculate_root(vec![(positions[0], DataOrHash::Data(leaves[0].clone()))]) + .unwrap(); + + assert_eq!(root, calculated_root.hash::>()) + }) +} + +#[test] +fn should_generate_and_verify_batch_proof_correctly() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + let (root, positions) = ext.execute_with(|| { + // push some leaves into the mmr + let positions = push_leaves(0..12); + new_block(); + let root = Pallet::::mmr_root(); + (root, positions) + }); + ext.persist_offchain_overlay(); + + // Try to generate proofs now. This requires the offchain extensions to be present + // to retrieve full leaf data. + register_offchain_ext(&mut ext); + ext.execute_with(move || { + let indices = vec![positions[0], positions[3], positions[2], positions[5]]; + let (leaves, proof) = Pallet::::generate_proof(indices.clone()).unwrap(); + + let mmr_size = NodesUtils::new(proof.leaf_count).size(); + let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = + MerkleProof::, MmrHasher>>::new(mmr_size, nodes); + let calculated_root = proof + .calculate_root( + indices + .into_iter() + .zip(leaves.into_iter().map(|leaf| DataOrHash::Data(leaf))) + .collect(), + ) + .unwrap(); + + assert_eq!(root, calculated_root.hash::>()) + }) +} + +#[test] +fn should_generate_and_verify_batch_proof_for_leaves_inserted_across_multiple_blocks_correctly() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + let (root, positions) = ext.execute_with(|| { + // push some leaves into the mmr + let mut positions = push_leaves(0..6); + new_block(); + let positions_second = push_leaves(6..12); + new_block(); + let root = Pallet::::mmr_root(); + positions.extend_from_slice(&positions_second); + (root, positions) + }); + ext.persist_offchain_overlay(); + + // Try to generate proofs now. This requires the offchain extensions to be present + // to retrieve full leaf data. + register_offchain_ext(&mut ext); + ext.execute_with(move || { + let indices = vec![positions[0], positions[9], positions[2], positions[8]]; + let (leaves, proof) = Pallet::::generate_proof(indices.clone()).unwrap(); + + let mmr_size = NodesUtils::new(proof.leaf_count).size(); + let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = + MerkleProof::, MmrHasher>>::new(mmr_size, nodes); + let calculated_root = proof + .calculate_root( + indices + .into_iter() + .zip(leaves.into_iter().map(|leaf| DataOrHash::Data(leaf))) + .collect(), + ) + .unwrap(); + + assert_eq!(root, calculated_root.hash::>()) + }) +} diff --git a/parachain-consensus/Cargo.toml b/parachain-consensus/Cargo.toml index 511a1e4cd..87663207b 100644 --- a/parachain-consensus/Cargo.toml +++ b/parachain-consensus/Cargo.toml @@ -15,7 +15,6 @@ hex-literal = "0.4.1" merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } primitive-types = { version = "0.12.1", default-features = false } hash-db = { version = "0.16.0", default-features = false } - # polytope labs ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } diff --git a/parachain-consensus/src/consensus.rs b/parachain-consensus/src/consensus.rs index eb7406ed4..ce8135a4e 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain-consensus/src/consensus.rs @@ -231,7 +231,7 @@ where let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); let proof = MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); - let leaves = match item { + let leaves: Vec<(u64, DataOrHash)> = match item { RequestResponse::Request(req) => membership .leaf_indices .into_iter() @@ -249,9 +249,10 @@ where .ismp_root .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; - let valid = proof - .verify(DataOrHash::Hash(root.into()), leaves) + let calc_root = proof + .calculate_root(leaves.clone()) .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; + let valid = calc_root.hash::>() == root.into(); if !valid { Err(Error::ImplementationSpecific("Invalid membership proof".into()))? From fa50fe425fbe8da740c8d0c67adb3e16dda5ec47 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Thu, 18 May 2023 13:27:34 +0100 Subject: [PATCH 124/182] Update ismp (#38) * update ismp-rs * nit --- Cargo.lock | 452 +++++++++++++++++++++++---------- ismp-assets/src/lib.rs | 1 + pallet-ismp/Cargo.toml | 1 + pallet-ismp/src/errors.rs | 2 + pallet-ismp/src/host.rs | 31 ++- pallet-ismp/src/lib.rs | 12 +- pallet-ismp/src/mock.rs | 18 +- pallet-ismp/src/primitives.rs | 12 +- pallet-ismp/src/router.rs | 23 +- pallet-ismp/src/tests.rs | 69 ++++- parachain-consensus/Cargo.toml | 1 + 11 files changed, 443 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94b2b0152..4c94515c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1302,13 +1302,13 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "scale-info", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "sp-inherents", "sp-io", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 5.0.0", "sp-trie", "sp-version", "xcm", @@ -1336,7 +1336,7 @@ dependencies = [ "polkadot-primitives", "sp-api", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-trie", "xcm", ] @@ -1354,12 +1354,12 @@ dependencies = [ "sc-client-api", "scale-info", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-inherents", "sp-runtime", "sp-state-machine", - "sp-std", - "sp-storage", + "sp-std 5.0.0", + "sp-storage 7.0.0", "sp-trie", "tracing", ] @@ -1392,7 +1392,7 @@ dependencies = [ "polkadot-primitives", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -2150,12 +2150,12 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core", + "sp-core 7.0.0", "sp-io", "sp-runtime", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-runtime-interface 7.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", "static_assertions", ] @@ -2191,15 +2191,15 @@ dependencies = [ "smallvec", "sp-api", "sp-arithmetic", - "sp-core", + "sp-core 7.0.0", "sp-core-hashing-proc-macro", "sp-inherents", "sp-io", "sp-runtime", "sp-staking", "sp-state-machine", - "sp-std", - "sp-tracing", + "sp-std 5.0.0", + "sp-tracing 6.0.0", "sp-weights", "tt-call", ] @@ -2251,10 +2251,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 7.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-version", "sp-weights", ] @@ -3004,7 +3004,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#f20b0486956ded6f7148fafe6ab9700db7fc70dc" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#f33ea2c2a376496ff7d3c644dc725e797d535e01" dependencies = [ "derive_more", "parity-scale-codec", @@ -3079,7 +3079,7 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-runtime", ] @@ -3093,7 +3093,18 @@ dependencies = [ "parity-scale-codec", "serde", "sp-api", - "sp-std", + "sp-std 5.0.0", +] + +[[package]] +name = "ismp-testsuite" +version = "0.1.0" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#f33ea2c2a376496ff7d3c644dc725e797d535e01" +dependencies = [ + "ismp", + "parity-scale-codec", + "primitive-types", + "sp-core 20.0.0", ] [[package]] @@ -4611,16 +4622,17 @@ dependencies = [ "frame-system", "ismp", "ismp-primitives", + "ismp-testsuite", "log", "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -4637,7 +4649,7 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-timestamp", ] @@ -4885,9 +4897,9 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe dependencies = [ "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -4903,7 +4915,7 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "sc-network", - "sp-core", + "sp-core 7.0.0", "thiserror", "tokio", ] @@ -4964,7 +4976,7 @@ dependencies = [ "sp-application-crypto", "sp-consensus-babe", "sp-consensus-vrf", - "sp-core", + "sp-core 7.0.0", "sp-keystore", "sp-maybe-compressed-blob", "sp-runtime", @@ -5013,7 +5025,7 @@ dependencies = [ "polkadot-primitives", "sc-client-api", "sp-api", - "sp-core", + "sp-core 7.0.0", "tikv-jemalloc-ctl", "tracing-gum", ] @@ -5030,9 +5042,9 @@ dependencies = [ "polkadot-core-primitives", "scale-info", "serde", - "sp-core", + "sp-core 7.0.0", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -5052,13 +5064,13 @@ dependencies = [ "sp-arithmetic", "sp-authority-discovery", "sp-consensus-slots", - "sp-core", + "sp-core 7.0.0", "sp-inherents", "sp-io", "sp-keystore", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -5068,7 +5080,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe dependencies = [ "parity-scale-codec", "polkadot-primitives", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -5920,8 +5932,8 @@ version = "4.1.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "log", - "sp-core", - "sp-wasm-interface", + "sp-core 7.0.0", + "sp-wasm-interface 7.0.0", "thiserror", ] @@ -5946,7 +5958,7 @@ dependencies = [ "sp-api", "sp-authority-discovery", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", @@ -5963,7 +5975,7 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-inherents", "sp-runtime", ] @@ -5982,7 +5994,7 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "sp-state-machine", ] @@ -6027,7 +6039,7 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-keyring", "sp-keystore", "sp-panic-handler", @@ -6054,13 +6066,13 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-database", - "sp-externalities", + "sp-externalities 0.13.0", "sp-keystore", "sp-runtime", "sp-state-machine", - "sp-storage", + "sp-storage 7.0.0", "substrate-prometheus-endpoint", ] @@ -6083,7 +6095,7 @@ dependencies = [ "schnellru", "sp-arithmetic", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-database", "sp-runtime", "sp-state-machine", @@ -6108,7 +6120,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "sp-state-machine", "substrate-prometheus-endpoint", @@ -6127,14 +6139,14 @@ dependencies = [ "sc-executor-wasmi", "sc-executor-wasmtime", "sp-api", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "sp-io", "sp-panic-handler", - "sp-runtime-interface", + "sp-runtime-interface 7.0.0", "sp-trie", "sp-version", - "sp-wasm-interface", + "sp-wasm-interface 7.0.0", "tracing", "wasmi", ] @@ -6146,7 +6158,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98 dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface", + "sp-wasm-interface 7.0.0", "thiserror", "wasm-instrument", "wasmi", @@ -6160,8 +6172,8 @@ dependencies = [ "log", "sc-allocator", "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", + "sp-runtime-interface 7.0.0", + "sp-wasm-interface 7.0.0", "wasmi", ] @@ -6178,8 +6190,8 @@ dependencies = [ "rustix 0.36.13", "sc-allocator", "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", + "sp-runtime-interface 7.0.0", + "sp-wasm-interface 7.0.0", "wasmtime", ] @@ -6209,7 +6221,7 @@ dependencies = [ "parking_lot 0.12.1", "serde_json", "sp-application-crypto", - "sp-core", + "sp-core 7.0.0", "sp-keystore", "thiserror", ] @@ -6250,7 +6262,7 @@ dependencies = [ "sp-arithmetic", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "substrate-prometheus-endpoint", "thiserror", @@ -6323,7 +6335,7 @@ dependencies = [ "sc-network-common", "sc-peerset", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "thiserror", ] @@ -6356,7 +6368,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-grandpa", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "substrate-prometheus-endpoint", "thiserror", @@ -6406,7 +6418,7 @@ dependencies = [ "sc-peerset", "sc-utils", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-offchain", "sp-runtime", "threadpool", @@ -6446,7 +6458,7 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-keystore", "sp-offchain", "sp-rpc", @@ -6468,7 +6480,7 @@ dependencies = [ "scale-info", "serde", "serde_json", - "sp-core", + "sp-core 7.0.0", "sp-rpc", "sp-runtime", "sp-version", @@ -6509,7 +6521,7 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "sp-version", "thiserror", @@ -6562,13 +6574,13 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "sp-keystore", "sp-runtime", "sp-session", "sp-state-machine", - "sp-storage", + "sp-storage 7.0.0", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", @@ -6590,7 +6602,7 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -6604,7 +6616,7 @@ dependencies = [ "log", "sc-client-db", "sc-utils", - "sp-core", + "sp-core 7.0.0", "thiserror", "tokio", ] @@ -6623,9 +6635,9 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", - "sp-core", + "sp-core 7.0.0", "sp-io", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -6668,10 +6680,10 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-rpc", "sp-runtime", - "sp-tracing", + "sp-tracing 6.0.0", "thiserror", "tracing", "tracing-log", @@ -6708,9 +6720,9 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-runtime", - "sp-tracing", + "sp-tracing 6.0.0", "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror", @@ -7157,10 +7169,10 @@ dependencies = [ "log", "parity-scale-codec", "sp-api-proc-macro", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 5.0.0", "sp-trie", "sp-version", "thiserror", @@ -7188,9 +7200,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 7.0.0", "sp-io", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7203,7 +7215,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 5.0.0", "static_assertions", ] @@ -7217,7 +7229,7 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7229,7 +7241,7 @@ dependencies = [ "sp-api", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7258,7 +7270,7 @@ dependencies = [ "async-trait", "futures", "log", - "sp-core", + "sp-core 7.0.0", "sp-inherents", "sp-runtime", "sp-state-machine", @@ -7279,7 +7291,7 @@ dependencies = [ "sp-consensus-slots", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-timestamp", ] @@ -7298,11 +7310,11 @@ dependencies = [ "sp-consensus", "sp-consensus-slots", "sp-consensus-vrf", - "sp-core", + "sp-core 7.0.0", "sp-inherents", "sp-keystore", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-timestamp", ] @@ -7318,10 +7330,10 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core", + "sp-core 7.0.0", "sp-keystore", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7332,7 +7344,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 5.0.0", "sp-timestamp", ] @@ -7344,9 +7356,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "schnorrkel", - "sp-core", + "sp-core 7.0.0", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7379,12 +7391,56 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-core-hashing", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-core-hashing 5.0.0", + "sp-debug-derive 5.0.0", + "sp-externalities 0.13.0", + "sp-runtime-interface 7.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" +dependencies = [ + "array-bytes", + "bitflags", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 8.0.0", + "sp-debug-derive 7.0.0", + "sp-externalities 0.18.0", + "sp-runtime-interface 16.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", "ss58-registry", "substrate-bip39", "thiserror", @@ -7402,7 +7458,22 @@ dependencies = [ "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std", + "sp-std 5.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 7.0.0", "twox-hash", ] @@ -7413,7 +7484,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98 dependencies = [ "proc-macro2", "quote", - "sp-core-hashing", + "sp-core-hashing 5.0.0", "syn 1.0.109", ] @@ -7436,6 +7507,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sp-debug-derive" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sp-externalities" version = "0.13.0" @@ -7443,8 +7525,20 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98 dependencies = [ "environmental", "parity-scale-codec", - "sp-std", - "sp-storage", + "sp-std 5.0.0", + "sp-storage 7.0.0", +] + +[[package]] +name = "sp-externalities" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 7.0.0", + "sp-storage 12.0.0", ] [[package]] @@ -7456,9 +7550,9 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "thiserror", ] @@ -7475,13 +7569,13 @@ dependencies = [ "log", "parity-scale-codec", "secp256k1", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "sp-keystore", - "sp-runtime-interface", + "sp-runtime-interface 7.0.0", "sp-state-machine", - "sp-std", - "sp-tracing", + "sp-std 5.0.0", + "sp-tracing 6.0.0", "sp-trie", "tracing", "tracing-core", @@ -7493,7 +7587,7 @@ version = "7.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "lazy_static", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "strum", ] @@ -7510,8 +7604,8 @@ dependencies = [ "parking_lot 0.12.1", "schnorrkel", "serde", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "thiserror", ] @@ -7530,7 +7624,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-runtime", ] @@ -7551,7 +7645,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98 dependencies = [ "rustc-hash", "serde", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -7570,9 +7664,9 @@ dependencies = [ "serde", "sp-application-crypto", "sp-arithmetic", - "sp-core", + "sp-core 7.0.0", "sp-io", - "sp-std", + "sp-std 5.0.0", "sp-weights", ] @@ -7585,12 +7679,31 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-storage", - "sp-tracing", - "sp-wasm-interface", + "sp-externalities 0.13.0", + "sp-runtime-interface-proc-macro 6.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-tracing 6.0.0", + "sp-wasm-interface 7.0.0", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5d0cd80200bf85b8b064238b2508b69b6146b13adf36066ec5d924825af737" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.18.0", + "sp-runtime-interface-proc-macro 10.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "sp-tracing 9.0.0", + "sp-wasm-interface 13.0.0", "static_assertions", ] @@ -7606,6 +7719,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sp-session" version = "4.0.0-dev" @@ -7614,10 +7740,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7627,9 +7753,9 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98 dependencies = [ "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-runtime", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -7643,10 +7769,10 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "smallvec", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "sp-panic-handler", - "sp-std", + "sp-std 5.0.0", "sp-trie", "thiserror", "tracing", @@ -7657,6 +7783,12 @@ name = "sp-std" version = "5.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +[[package]] +name = "sp-std" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" + [[package]] name = "sp-storage" version = "7.0.0" @@ -7666,8 +7798,22 @@ dependencies = [ "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 5.0.0", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-storage" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 7.0.0", + "sp-std 7.0.0", ] [[package]] @@ -7681,7 +7827,7 @@ dependencies = [ "parity-scale-codec", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "thiserror", ] @@ -7691,7 +7837,20 @@ version = "6.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" dependencies = [ "parity-scale-codec", - "sp-std", + "sp-std 5.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-tracing" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" +dependencies = [ + "parity-scale-codec", + "sp-std 7.0.0", "tracing", "tracing-core", "tracing-subscriber", @@ -7715,10 +7874,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-trie", ] @@ -7737,8 +7896,8 @@ dependencies = [ "parking_lot 0.12.1", "scale-info", "schnellru", - "sp-core", - "sp-std", + "sp-core 7.0.0", + "sp-std 5.0.0", "thiserror", "tracing", "trie-db", @@ -7757,7 +7916,7 @@ dependencies = [ "serde", "sp-core-hashing-proc-macro", "sp-runtime", - "sp-std", + "sp-std 5.0.0", "sp-version-proc-macro", "thiserror", ] @@ -7782,7 +7941,22 @@ dependencies = [ "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std", + "sp-std 5.0.0", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-wasm-interface" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 7.0.0", "wasmi", "wasmtime", ] @@ -7797,9 +7971,9 @@ dependencies = [ "serde", "smallvec", "sp-arithmetic", - "sp-core", - "sp-debug-derive", - "sp-std", + "sp-core 7.0.0", + "sp-debug-derive 5.0.0", + "sp-std 5.0.0", ] [[package]] @@ -8563,7 +8737,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index 308f26f1c..59ee9f89c 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -14,6 +14,7 @@ // limitations under the License. //! ISMP Assets +//! Simple Demo for Asset transfer over ISMP #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index cfbe180e2..23c4f00bc 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -32,6 +32,7 @@ ismp-primitives = { path = "./primitives", default-features = false } [dev-dependencies] env_logger = "0.10.0" pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +ismp-testsuite = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } [features] default = ["std"] diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index b7bb323f2..cdf16805b 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -75,6 +75,7 @@ pub enum HandlingError { source: StateMachine, dest: StateMachine, }, + InsufficientProofHeight, } impl From for HandlingError { @@ -145,6 +146,7 @@ impl From for HandlingError { IsmpError::RequestTimeoutVerificationFailed { nonce, source, dest } => { HandlingError::RequestTimeoutVerificationFailed { nonce, source, dest } } + IsmpError::InsufficientProofHeight => HandlingError::InsufficientProofHeight, } } } diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index e90b6d82b..705e9a554 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -1,6 +1,6 @@ use crate::{ - primitives::ConsensusClientProvider, Config, ConsensusClientUpdateTime, ConsensusStates, - FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, + primitives::ConsensusClientProvider, router::Receipt, Config, ConsensusClientUpdateTime, + ConsensusStates, FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -88,6 +88,20 @@ where Ok(commitment) } + fn get_request_receipt(&self, req: &Request) -> Option<()> { + let commitment = hash_request::(req); + + let _ = RequestAcks::::get(commitment.0.to_vec()) + .ok_or_else(|| Error::RequestCommitmentNotFound { + nonce: req.nonce(), + source: req.source_chain(), + dest: req.dest_chain(), + }) + .ok()?; + + Some(()) + } + fn store_consensus_state(&self, id: ConsensusClientId, state: Vec) -> Result<(), Error> { ConsensusStates::::insert(id, state); Ok(()) @@ -121,6 +135,19 @@ where Ok(()) } + fn delete_request_commitment(&self, req: &Request) -> Result<(), Error> { + let hash = hash_request::(req); + // We can't delete actual leaves in the mmr so this serves as a replacement for that + RequestAcks::::remove(hash.0.to_vec()); + Ok(()) + } + + fn store_request_receipt(&self, req: &Request) -> Result<(), Error> { + let hash = hash_request::(req); + RequestAcks::::insert(hash.0.to_vec(), Receipt::Ok); + Ok(()) + } + fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { ::ConsensusClientProvider::consensus_client(id) } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index d2ba2db37..fabeb0d97 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -166,7 +166,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn request_acks)] - /// Acknowledgements for receipt of requests + /// Acknowledgements for incoming and outgoing requests /// The key is the request commitment pub type RequestAcks = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; @@ -556,9 +556,9 @@ where req.nonce(), ), Leaf::Response(res) => Pallet::::response_leaf_index_offchain_key( - res.request.dest_chain(), - res.request.source_chain(), - res.request.nonce(), + res.dest_chain(), + res.source_chain(), + res.nonce(), ), }; let leaves = Self::number_of_leaves(); @@ -605,8 +605,8 @@ impl IsmpDispatch for Pallet { }; router.dispatch(Request::Get(get)).map(|_| ()) } - IsmpMessage::Response { response, request } => { - router.write_response(Response { request, response }).map(|_| ()) + IsmpMessage::Response { response, post } => { + router.write_response(Response::Post { post, response }).map(|_| ()) } } } diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index ae4d971bf..31da1b3c4 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -30,7 +30,7 @@ pub struct StateMachineProvider; impl Get for StateMachineProvider { fn get() -> StateMachine { - StateMachine::Kusama(2000) + StateMachine::Kusama(100) } } @@ -38,21 +38,13 @@ pub struct ConsensusProvider; impl ConsensusClientProvider for ConsensusProvider { fn consensus_client( - id: ConsensusClientId, + _id: ConsensusClientId, ) -> Result, ismp_rs::error::Error> { - let client = match id { - _ => Err(ismp_rs::error::Error::ImplementationSpecific( - "Unknown consensus client".into(), - ))?, - }; - - Ok(client) + Ok(Box::new(ismp_testsuite::mocks::MockClient)) } - fn challenge_period(id: ConsensusClientId) -> Duration { - match id { - _ => Duration::MAX, - } + fn challenge_period(_id: ConsensusClientId) -> Duration { + Duration::from_secs(60 * 60) } } diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 13282062b..dfb74e91a 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -4,7 +4,7 @@ use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::{ consensus::{ConsensusClient, ConsensusClientId, StateMachineHeight}, host::StateMachine, - router::Request, + router::Post, }; use scale_info::TypeInfo; use sp_std::prelude::*; @@ -50,7 +50,7 @@ pub enum IsmpMessage { Post { /// The destination state machine of this request. dest_chain: StateMachine, - /// Moudle Id of the sending module + /// Module Id of the sending module from: Vec, /// Module ID of the receiving module to: Vec, @@ -62,17 +62,17 @@ pub enum IsmpMessage { Get { /// The destination state machine of this request. dest_chain: StateMachine, - /// Moudle Id of the sending module + /// Module Id of the sending module from: Vec, - /// Storage keys that this request is interested in. + /// Raw Storage keys that this request is interested in. keys: Vec>, /// Height at which to read the state machine. height: StateMachineHeight, - /// Timestamp which this request expires in seconds + /// Host Timestamp which this request expires in seconds timeout_timestamp: u64, }, Response { - request: Request, + post: Post, response: Vec, }, } diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs index b0e594cb7..8eee6bbaa 100644 --- a/pallet-ismp/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -72,6 +72,8 @@ where source_chain, dest_chain, }); + // We have this step because we can't delete leaves from the mmr + // So this helps us prevent processing of duplicate outgoing requests RequestAcks::::insert(commitment, Receipt::Ok); Ok(DispatchSuccess { dest_chain, source_chain, nonce }) } else if let Some(ref router) = self.inner { @@ -102,23 +104,20 @@ where fn write_response(&self, response: Response) -> DispatchResult { let host = Host::::default(); - if host.host_state_machine() != response.request.source_chain() { + if host.host_state_machine() != response.dest_chain() { let commitment = hash_response::>(&response).0.to_vec(); if ResponseAcks::::contains_key(commitment.clone()) { Err(DispatchError { msg: "Duplicate response".to_string(), - nonce: response.request.nonce(), - source: response.request.source_chain(), - dest: response.request.dest_chain(), + nonce: response.nonce(), + source: response.source_chain(), + dest: response.dest_chain(), })? } - let (dest_chain, source_chain, nonce) = ( - response.request.source_chain(), - response.request.dest_chain(), - response.request.nonce(), - ); + let (dest_chain, source_chain, nonce) = + (response.dest_chain(), response.source_chain(), response.nonce()); Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { msg: "Failed to push response into mmr".to_string(), @@ -139,9 +138,9 @@ where } else { Err(DispatchError { msg: "Missing a module router".to_string(), - nonce: response.request.nonce(), - source: response.request.source_chain(), - dest: response.request.dest_chain(), + nonce: response.nonce(), + source: response.source_chain(), + dest: response.dest_chain(), })? } } diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index a15b10501..737c4c440 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -1,8 +1,15 @@ use crate::{mock::*, *}; -use std::ops::Range; +use std::{ + ops::Range, + time::{SystemTime, UNIX_EPOCH}, +}; use frame_support::traits::OnFinalize; use ismp_primitives::mmr::MmrHasher; +use ismp_testsuite::{ + check_challenge_period, check_client_expiry, frozen_check, timeout_post_processing_check, + write_outgoing_commitments, +}; use mmr_lib::MerkleProof; use sp_core::{ offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, @@ -159,3 +166,63 @@ fn should_generate_and_verify_batch_proof_for_leaves_inserted_across_multiple_bl assert_eq!(root, calculated_root.hash::>()) }) } + +fn set_timestamp() { + Timestamp::set_timestamp( + SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64 + ); +} + +#[test] +fn check_for_duplicate_requests_and_responses() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(); + let host = Host::::default(); + write_outgoing_commitments(&host).unwrap(); + }) +} + +#[test] +fn should_reject_updates_within_challenge_period() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(); + let host = Host::::default(); + check_challenge_period(&host).unwrap() + }) +} + +#[test] +fn should_reject_messages_for_frozen_state_machines() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(); + let host = Host::::default(); + frozen_check(&host).unwrap() + }) +} + +#[test] +fn should_reject_expired_check_clients() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(); + let host = Host::::default(); + check_client_expiry(&host).unwrap() + }) +} +#[test] +fn should_process_timeouts_correctly() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(); + let host = Host::::default(); + timeout_post_processing_check(&host).unwrap() + }) +} diff --git a/parachain-consensus/Cargo.toml b/parachain-consensus/Cargo.toml index 87663207b..f3022724b 100644 --- a/parachain-consensus/Cargo.toml +++ b/parachain-consensus/Cargo.toml @@ -36,6 +36,7 @@ pallet-ismp = { path = "../pallet-ismp", default-features = false } [dev-dependencies] + [features] default = ["std"] std = [ From dbd8d33d5356ac79f0e101e81db7c04c10472884 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 22 May 2023 18:17:24 +0100 Subject: [PATCH 125/182] Benchmarking (#39) * benchmarking frameowrk * add some information * progress on benchmarks * benchmark test: not running yet * add an assertion after benchmarks * added some comments --- pallet-ismp/Cargo.toml | 8 +- pallet-ismp/src/benchmarking.rs | 272 ++++++++++++++++++++++++++++++++ pallet-ismp/src/lib.rs | 23 ++- pallet-ismp/src/mock.rs | 61 ++++++- pallet-ismp/src/weight_info.rs | 224 ++++++++++++++++++++++++++ 5 files changed, 578 insertions(+), 10 deletions(-) create mode 100644 pallet-ismp/src/benchmarking.rs create mode 100644 pallet-ismp/src/weight_info.rs diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index 23c4f00bc..48ad0c646 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -14,6 +14,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkad sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, optional = true } # polytope labs ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } @@ -54,4 +55,9 @@ std = [ "ismp-primitives/std", ] -runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks" +] diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs new file mode 100644 index 000000000..a3ff0c1da --- /dev/null +++ b/pallet-ismp/src/benchmarking.rs @@ -0,0 +1,272 @@ +// Only enable this module for benchmarking. +#![cfg(feature = "runtime-benchmarks")] + +use crate::*; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +// Running the benchmarks correctly +// Add the [`BenchmarkClient`] as one of the consensus clients available to pallet-ismp in the +// runtime configuration +// In your module router configuration add the [`BenchmarkIsmpModule`] as one of the ismp modules +// using the pallet id defined here as it's module id. + +// Details on using the benchmarks macro can be seen at: +// https://paritytech.github.io/substrate/master/frame_benchmarking/trait.Benchmarking.html#tymethod.benchmarks +#[benchmarks( + where + ::Hash: From, + T: pallet_timestamp::Config, + ::Moment: From +)] +pub mod benchmarks { + use super::*; + use crate::router::Receipt; + use frame_support::PalletId; + use frame_system::EventRecord; + use ismp_rs::{ + consensus::{ConsensusClient, IntermediateState, StateCommitment, StateMachineHeight}, + error::Error as IsmpError, + messaging::{Message, Proof, RequestMessage, ResponseMessage, TimeoutMessage}, + module::ISMPModule, + router::{Post, RequestResponse}, + util::hash_request, + }; + + fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); + } + + #[derive(Default)] + pub struct BenchmarkClient; + + pub const BENCHMARK_CONSENSUS_CLIENT_ID: [u8; 4] = [1u8; 4]; + + impl ConsensusClient for BenchmarkClient { + fn verify_consensus( + &self, + _host: &dyn ISMPHost, + _trusted_consensus_state: Vec, + _proof: Vec, + ) -> Result<(Vec, Vec), IsmpError> { + Ok(Default::default()) + } + + fn unbonding_period(&self) -> Duration { + Duration::from_secs(60 * 60 * 60) + } + + fn verify_membership( + &self, + _host: &dyn ISMPHost, + _item: RequestResponse, + _root: StateCommitment, + _proof: &Proof, + ) -> Result<(), IsmpError> { + Ok(()) + } + + fn state_trie_key(&self, _request: RequestResponse) -> Vec> { + Default::default() + } + + fn verify_state_proof( + &self, + _host: &dyn ISMPHost, + _keys: Vec>, + _root: StateCommitment, + _proof: &Proof, + ) -> Result>>, IsmpError> { + Ok(Default::default()) + } + + fn is_frozen(&self, _trusted_consensus_state: &[u8]) -> Result<(), IsmpError> { + Ok(()) + } + } + + /// This module should be added to the module router in runtime for benchmarks to pass + pub struct BenchmarkIsmpModule; + pub const MODULE_ID: PalletId = PalletId(*b"benchmak"); + impl ISMPModule for BenchmarkIsmpModule { + fn on_accept(_request: Request) -> Result<(), IsmpError> { + Ok(()) + } + + fn on_response(_response: Response) -> Result<(), IsmpError> { + Ok(()) + } + + fn on_timeout(_request: Request) -> Result<(), IsmpError> { + Ok(()) + } + } + + fn set_timestamp() + where + ::Moment: From, + { + pallet_timestamp::Pallet::::set_timestamp(1000_000_000u64.into()); + } + + #[benchmark] + fn create_consensus_client() { + set_timestamp::(); + let intermediate_state = IntermediateState { + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Polkadot(1000), + consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID, + }, + height: 1, + }, + + commitment: StateCommitment { + timestamp: 1651280681, + ismp_root: None, + state_root: Default::default(), + }, + }; + + let message = CreateConsensusClient { + consensus_state: Default::default(), + consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID, + state_machine_commitments: vec![intermediate_state], + }; + + #[extrinsic_call] + _(RawOrigin::Root, message); + + assert_last_event::( + Event::ConsensusClientCreated { consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID } + .into(), + ); + } + + fn setup_mock_client(host: &H) -> IntermediateState { + let intermediate_state = IntermediateState { + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID, + }, + height: 1, + }, + commitment: StateCommitment { + timestamp: 1000, + ismp_root: None, + state_root: Default::default(), + }, + }; + + host.store_consensus_state(BENCHMARK_CONSENSUS_CLIENT_ID, vec![]).unwrap(); + host.store_consensus_update_time(BENCHMARK_CONSENSUS_CLIENT_ID, Duration::from_secs(1000)) + .unwrap(); + host.store_state_machine_commitment( + intermediate_state.height, + intermediate_state.commitment, + ) + .unwrap(); + + intermediate_state + } + + // The Benchmark consensus client should be added to the runtime for these benchmarks to work + #[benchmark] + fn handle_request_message() { + set_timestamp::(); + let host = Host::::default(); + let intermediate_state = setup_mock_client(&host); + let post = Post { + source_chain: StateMachine::Ethereum, + dest_chain: ::StateMachine::get(), + nonce: 0, + from: MODULE_ID.0.to_vec(), + to: MODULE_ID.0.to_vec(), + timeout_timestamp: 5000, + data: vec![], + }; + + let msg = RequestMessage { + requests: vec![Request::Post(post.clone())], + proof: Proof { height: intermediate_state.height, proof: vec![] }, + }; + let caller = whitelisted_caller(); + + #[extrinsic_call] + handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]); + + let commitment = hash_request::>(&Request::Post(post)); + assert!(RequestAcks::::get(commitment.0.to_vec()).is_some()); + } + + #[benchmark] + fn handle_response_message() { + set_timestamp::(); + let host = Host::::default(); + let intermediate_state = setup_mock_client(&host); + let post = Post { + source_chain: ::StateMachine::get(), + dest_chain: StateMachine::Ethereum, + nonce: 0, + from: MODULE_ID.0.to_vec(), + to: MODULE_ID.0.to_vec(), + timeout_timestamp: 5000, + data: vec![], + }; + let request = Request::Post(post.clone()); + + let commitment = hash_request::>(&request); + RequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); + + let response = Response::Post { post, response: vec![] }; + + let msg = ResponseMessage::Post { + responses: vec![response], + proof: Proof { height: intermediate_state.height, proof: vec![] }, + }; + + let caller = whitelisted_caller(); + + #[extrinsic_call] + handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]); + + assert!(RequestAcks::::get(commitment.0.to_vec()).is_none()); + } + + #[benchmark] + fn handle_timeout_message() { + set_timestamp::(); + let host = Host::::default(); + let intermediate_state = setup_mock_client(&host); + let post = Post { + source_chain: ::StateMachine::get(), + dest_chain: StateMachine::Ethereum, + nonce: 0, + from: MODULE_ID.0.to_vec(), + to: MODULE_ID.0.to_vec(), + timeout_timestamp: 500, + data: vec![], + }; + let request = Request::Post(post.clone()); + + let commitment = hash_request::>(&request); + RequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); + + let msg = TimeoutMessage::Post { + requests: vec![request], + timeout_proof: Proof { height: intermediate_state.height, proof: vec![] }, + }; + let caller = whitelisted_caller(); + + #[extrinsic_call] + handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]); + + assert!(RequestAcks::::get(commitment.0.to_vec()).is_none()); + } + + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Test); +} diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index fabeb0d97..9ed1d8ff3 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -18,16 +18,18 @@ extern crate alloc; +pub mod benchmarking; mod errors; pub mod events; pub mod host; mod mmr; #[cfg(test)] -mod mock; +pub mod mock; pub mod primitives; pub mod router; #[cfg(test)] -mod tests; +pub mod tests; +pub mod weight_info; pub use mmr::utils::NodesUtils; @@ -64,6 +66,7 @@ pub mod pallet { errors::HandlingError, primitives::{ConsensusClientProvider, ISMP_ID}, router::Receipt, + weight_info::{WeightInfo, WeightProvider}, }; use alloc::collections::BTreeSet; use frame_support::{pallet_prelude::*, traits::UnixTime}; @@ -77,6 +80,7 @@ pub mod pallet { router::ISMPRouter, }; use sp_core::H256; + use weight_info::get_weight; /// Our pallet's configuration trait. All our types and constants go in here. If the /// pallet is dependent on specific other pallets, then their configuration traits @@ -111,6 +115,10 @@ pub mod pallet { type IsmpRouter: ISMPRouter + Default; /// Provides concrete implementations of consensus clients type ConsensusClientProvider: ConsensusClientProvider; + /// Weight Info + type WeightInfo: WeightInfo; + /// Weight provider for consensus clients and module callbacks + type WeightProvider: WeightProvider; } // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and @@ -173,7 +181,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn response_acks)] - /// Acknowledgements for receipt of responses + /// Acknowledgements for incoming and outgoing responses /// The key is the response commitment pub type ResponseAcks = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; @@ -240,8 +248,9 @@ pub mod pallet { ::Hash: From, { /// Handles ismp messages - #[pallet::weight(0)] + #[pallet::weight(get_weight::(&messages))] #[pallet::call_index(0)] + #[frame_support::transactional] pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResult { let _ = ensure_signed(origin)?; // Define a host @@ -306,7 +315,7 @@ pub mod pallet { } /// Create consensus clients - #[pallet::weight(0)] + #[pallet::weight(::WeightInfo::create_consensus_client())] #[pallet::call_index(1)] pub fn create_consensus_client( origin: OriginFor, @@ -343,7 +352,7 @@ pub mod pallet { }, /// Indicates that a consensus client has been created ConsensusClientCreated { consensus_client_id: ConsensusClientId }, - /// Response was process successfully + /// An Outgoing Response has been deposited Response { /// Chain that this response will be routed to dest_chain: StateMachine, @@ -352,7 +361,7 @@ pub mod pallet { /// Nonce for the request which this response is for request_nonce: u64, }, - /// Request processed successfully + /// An Outgoing Request has been deposited Request { /// Chain that this request will be routed to dest_chain: StateMachine, diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index 31da1b3c4..a7d0987c2 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -4,7 +4,10 @@ use crate::*; use crate::{primitives::ConsensusClientProvider, router::ProxyRouter}; use frame_support::traits::{ConstU32, ConstU64, Get}; use frame_system::EnsureRoot; -use ismp_rs::consensus::ConsensusClient; +use ismp_rs::{ + consensus::ConsensusClient, + router::{DispatchResult, DispatchSuccess}, +}; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -88,6 +91,60 @@ impl Config for Test { type AdminOrigin = EnsureRoot; type StateMachine = StateMachineProvider; type TimeProvider = Timestamp; - type IsmpRouter = ProxyRouter; + type IsmpRouter = Router; type ConsensusClientProvider = ConsensusProvider; + type WeightInfo = (); + type WeightProvider = (); +} + +#[derive(Default)] +pub struct ModuleRouter; + +impl ISMPRouter for ModuleRouter { + fn dispatch(&self, request: Request) -> DispatchResult { + let dest = request.dest_chain(); + let source = request.source_chain(); + let nonce = request.nonce(); + + Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) + } + + fn dispatch_timeout(&self, request: Request) -> DispatchResult { + let dest = request.dest_chain(); + let source = request.source_chain(); + let nonce = request.nonce(); + Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) + } + + fn write_response(&self, response: Response) -> DispatchResult { + let request = &response.request(); + let dest = request.dest_chain(); + let source = request.source_chain(); + let nonce = request.nonce(); + Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) + } +} + +pub struct Router { + inner: ProxyRouter, +} + +impl Default for Router { + fn default() -> Self { + Self { inner: ProxyRouter::::new(ModuleRouter::default()) } + } +} + +impl ISMPRouter for Router { + fn dispatch(&self, request: Request) -> DispatchResult { + self.inner.dispatch(request) + } + + fn dispatch_timeout(&self, request: Request) -> DispatchResult { + self.inner.dispatch_timeout(request) + } + + fn write_response(&self, response: Response) -> DispatchResult { + self.inner.write_response(response) + } } diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs new file mode 100644 index 000000000..606bf7dcf --- /dev/null +++ b/pallet-ismp/src/weight_info.rs @@ -0,0 +1,224 @@ +//! Users of ismp should benchmark consensus clients and module callbacks + +use crate::Config; +use alloc::boxed::Box; +use frame_support::weights::Weight; +use ismp_rs::{ + consensus::ConsensusClientId, + messaging::{ConsensusMessage, Message, Proof, ResponseMessage, TimeoutMessage}, + router::{Request, Response}, +}; + +/// A trait that provides information about how consensus client execute in the runtime +pub trait ConsensusClientWeight { + /// Returns the weight that would be used in processing this consensus message + fn verify_consensus(&self, msg: ConsensusMessage) -> Weight; + /// Returns weight used in verifying this membership proof + /// `items` is the number of values being verified + /// The weight should ideally depend on the number of items being verified + fn verify_membership(&self, items: usize, proof: &Proof) -> Weight; + /// Returns weight used in verifying this state proof + /// `items` is the number of keys being verified + /// The weight should ideally depend on the number of items being verified + fn verify_state_proof(&self, items: usize, proof: &Proof) -> Weight; +} + +impl ConsensusClientWeight for () { + fn verify_consensus(&self, _msg: ConsensusMessage) -> Weight { + Weight::zero() + } + + fn verify_membership(&self, _items: usize, _proof: &Proof) -> Weight { + Weight::zero() + } + + fn verify_state_proof(&self, _items: usize, _proof: &Proof) -> Weight { + Weight::zero() + } +} + +/// A trait that provides weight information about how module callbacks execute +pub trait IsmpModuleWeight { + /// Returns the weight used in processing this request + fn on_accept(&self, request: &Request) -> Weight; + /// Returns the weight used in processing this timeout + fn on_timeout(&self, request: &Request) -> Weight; + /// Returns the weight used in processing this response + fn on_response(&self, response: &Response) -> Weight; +} + +impl IsmpModuleWeight for () { + fn on_accept(&self, _request: &Request) -> Weight { + Weight::zero() + } + + fn on_timeout(&self, _request: &Request) -> Weight { + Weight::zero() + } + + fn on_response(&self, _response: &Response) -> Weight { + Weight::zero() + } +} + +pub trait WeightProvider { + fn consensus_client(id: ConsensusClientId) -> Option>; + + fn module_callback(dest_module: &[u8]) -> Option>; +} + +impl WeightProvider for () { + fn consensus_client(_id: ConsensusClientId) -> Option> { + None + } + + fn module_callback(_dest_module: &[u8]) -> Option> { + None + } +} + +/// These functions account fot storage reads and writes in the ismp message handlers +pub trait WeightInfo { + fn create_consensus_client() -> Weight; + fn handle_request_message() -> Weight; + fn handle_response_message() -> Weight; + fn handle_timeout_message() -> Weight; +} + +impl WeightInfo for () { + fn create_consensus_client() -> Weight { + Weight::zero() + } + + fn handle_request_message() -> Weight { + Weight::zero() + } + + fn handle_response_message() -> Weight { + Weight::zero() + } + + fn handle_timeout_message() -> Weight { + Weight::zero() + } +} + +pub fn get_weight(messages: &[Message]) -> Weight { + messages.into_iter().fold(Weight::zero(), |acc, msg| { + match msg { + Message::Consensus(_) => acc + ::WeightInfo::create_consensus_client(), + Message::Request(msg) => { + let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Post(ref post) => post.to.as_slice(), + // Get requests are never submitted + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_accept(&req) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + msg.proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_membership(msg.requests.len(), &msg.proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_request_message() + } + Message::Response(msg) => match msg { + ResponseMessage::Post { responses, proof } => { + let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { + let dest_module = match res { + Response::Post { ref post, .. } => post.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_response(&res) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_membership(responses.len(), &proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + } + ResponseMessage::Get { requests, proof } => { + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Get(ref get) => get.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_response(&Response::Get { + get: req.get_request().unwrap(), + values: Default::default(), + }) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_state_proof(requests.len(), &proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + } + }, + Message::Timeout(msg) => match msg { + TimeoutMessage::Post { requests, timeout_proof } => { + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Post(ref post) => post.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_timeout(&req) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + timeout_proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_state_proof(requests.len(), &timeout_proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + } + TimeoutMessage::Get { requests } => { + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Get(ref get) => get.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_timeout(&req) + }); + acc + cb_weight + ::WeightInfo::handle_timeout_message() + } + }, + } + }) +} From 0e73b6924e0ffef8ca3656ae2da906b521aa82c9 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 23 May 2023 20:11:41 +0100 Subject: [PATCH 126/182] ParachainConsensusInherentProvider (#40) * adds inherent support to ismp_parachain * initial work for inherent provider * introduce ismp-parachain-runtime-api * fix ParachainConsensusClient * refactor verify_consensus * add parachains list to GenesisBuild * optional inherent * integration tests works --- Cargo.lock | 30 ++- Cargo.toml | 5 +- pallet-ismp/src/lib.rs | 136 +++++----- parachain-consensus/src/lib.rs | 111 -------- {parachain-consensus => parachain}/Cargo.toml | 2 + parachain/inherent/Cargo.toml | 25 ++ parachain/inherent/src/lib.rs | 94 +++++++ parachain/runtime-api/Cargo.toml | 15 ++ parachain/runtime-api/src/lib.rs | 29 +++ .../src/consensus.rs | 50 ++-- parachain/src/lib.rs | 245 ++++++++++++++++++ rust-toolchain.toml | 3 +- 12 files changed, 547 insertions(+), 198 deletions(-) delete mode 100644 parachain-consensus/src/lib.rs rename {parachain-consensus => parachain}/Cargo.toml (94%) create mode 100644 parachain/inherent/Cargo.toml create mode 100644 parachain/inherent/src/lib.rs create mode 100644 parachain/runtime-api/Cargo.toml create mode 100644 parachain/runtime-api/src/lib.rs rename {parachain-consensus => parachain}/src/consensus.rs (90%) create mode 100644 parachain/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4c94515c8..4961c16d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "approx" @@ -3044,11 +3044,37 @@ dependencies = [ "primitive-types", "scale-info", "sp-consensus-aura", + "sp-inherents", "sp-io", "sp-runtime", "sp-trie", ] +[[package]] +name = "ismp-parachain-inherent" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "ismp", + "ismp-parachain", + "ismp-parachain-runtime-api", + "parity-scale-codec", + "sp-api", + "sp-blockchain", + "sp-inherents", + "sp-runtime", +] + +[[package]] +name = "ismp-parachain-runtime-api" +version = "0.1.0" +dependencies = [ + "sp-api", +] + [[package]] name = "ismp-primitives" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 062f308e7..554140fef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,12 @@ [workspace] resolver = "2" - members = [ "pallet-ismp/rpc", "pallet-ismp/runtime-api", "pallet-ismp/primitives", "pallet-ismp", - "parachain-consensus", + "parachain/inherent", + "parachain/runtime-api", + "parachain", "ismp-assets" ] diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 9ed1d8ff3..cbf0af969 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -36,22 +36,26 @@ pub use mmr::utils::NodesUtils; use crate::host::Host; use codec::{Decode, Encode}; use core::time::Duration; -use frame_support::{log::debug, traits::Get, RuntimeDebug}; +use frame_support::{dispatch::DispatchResult, log::debug, traits::Get, RuntimeDebug}; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, + handlers::{handle_incoming_message, MessageResult}, host::StateMachine, messaging::CreateConsensusClient, router::{Request, Response}, }; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. -use crate::primitives::{IsmpDispatch, IsmpMessage}; +use crate::{ + errors::HandlingError, + mmr::mmr::Mmr, + primitives::{IsmpDispatch, IsmpMessage}, +}; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; -use ismp_rs::{host::ISMPHost, router::ISMPRouter}; -use mmr::mmr::Mmr; +use ismp_rs::{host::ISMPHost, messaging::Message, router::ISMPRouter}; pub use pallet::*; use sp_std::prelude::*; @@ -74,7 +78,7 @@ pub mod pallet { use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::{ consensus::{ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId}, - handlers::{self, handle_incoming_message, MessageResult}, + handlers::{self}, host::StateMachine, messaging::Message, router::ISMPRouter, @@ -253,65 +257,8 @@ pub mod pallet { #[frame_support::transactional] pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResult { let _ = ensure_signed(origin)?; - // Define a host - let host = Host::::default(); - let mut errors: Vec = vec![]; - - for message in messages { - match handle_incoming_message(&host, message) { - Ok(MessageResult::ConsensusMessage(res)) => { - // check if this is a trusted state machine - let is_trusted_state_machine = host - .challenge_period(res.consensus_client_id.clone()) == - Duration::from_secs(0); - - if is_trusted_state_machine { - for (_, latest_height) in res.state_updates.into_iter() { - Self::deposit_event(Event::::StateMachineUpdated { - state_machine_id: latest_height.id, - latest_height: latest_height.height, - }) - } - } else { - if let Some(pending_updates) = - ConsensusUpdateResults::::get(res.consensus_client_id) - { - for (_, latest_height) in pending_updates.into_iter() { - Self::deposit_event(Event::::StateMachineUpdated { - state_machine_id: latest_height.id, - latest_height: latest_height.height, - }) - } - } - Self::deposit_event(Event::::ChallengePeriodStarted { - consensus_client_id: res.consensus_client_id, - state_machines: res.state_updates.clone(), - }); - - // Store the new update result that have just entered the challenge - // period - ConsensusUpdateResults::::insert( - res.consensus_client_id, - res.state_updates, - ); - } - } - Ok(_) => { - // Do nothing, event should have been deposited by the ismp router - } - Err(err) => { - errors.push(err.into()); - } - } - } - - if !errors.is_empty() { - debug!(target: "ismp-rust", "Handling Errors {:?}", errors); - Self::deposit_event(Event::::HandlingErrors { errors }) - } - - Ok(()) + Self::handle_messages(messages) } /// Create consensus clients @@ -399,6 +346,69 @@ where mmr.generate_proof(leaf_indices) } + /// Provides a way to handle messages. + pub fn handle_messages(messages: Vec) -> DispatchResult { + // Define a host + let host = Host::::default(); + let mut errors: Vec = vec![]; + + for message in messages { + match handle_incoming_message(&host, message) { + Ok(MessageResult::ConsensusMessage(res)) => { + // check if this is a trusted state machine + let is_trusted_state_machine = host + .challenge_period(res.consensus_client_id.clone()) == + Duration::from_secs(0); + + if is_trusted_state_machine { + for (_, latest_height) in res.state_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } else { + if let Some(pending_updates) = + ConsensusUpdateResults::::get(res.consensus_client_id) + { + for (_, latest_height) in pending_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } + + Self::deposit_event(Event::::ChallengePeriodStarted { + consensus_client_id: res.consensus_client_id, + state_machines: res.state_updates.clone(), + }); + + // Store the new update result that have just entered the challenge + // period + ConsensusUpdateResults::::insert( + res.consensus_client_id, + res.state_updates, + ); + } + } + Ok(_) => { + // Do nothing, event should have been deposited by the ismp router + } + Err(err) => { + errors.push(err.into()); + } + } + } + + if !errors.is_empty() { + debug!(target: "ismp-rust", "Handling Errors {:?}", errors); + Self::deposit_event(Event::::HandlingErrors { errors }) + } + + Ok(()) + } + /// Return the on-chain MMR root hash. pub fn mmr_root() -> ::Hash { Self::mmr_root_hash() diff --git a/parachain-consensus/src/lib.rs b/parachain-consensus/src/lib.rs deleted file mode 100644 index 38158df37..000000000 --- a/parachain-consensus/src/lib.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! ISMP Parachain Consensus Client -//! -//! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; -extern crate core; - -pub mod consensus; - -use cumulus_primitives_core::relay_chain; -pub use pallet::*; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use cumulus_primitives_core::relay_chain; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_ismp::Config + parachain_system::Config - { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - /// Mapping of relay chain heights to it's state root. Gotten from parachain-system. - #[pallet::storage] - #[pallet::getter(fn relay_chain_state)] - pub type RelayChainState = - StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; - - #[pallet::event] - pub enum Event {} - - // Pallet implements [`Hooks`] trait to define some logic to execute in some context. - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: T::BlockNumber) { - let state = RelaychainDataProvider::::current_relay_chain_state(); - if !RelayChainState::::contains_key(state.number) { - RelayChainState::::insert(state.number, state.state_root); - - let digest = sp_runtime::generic::DigestItem::Consensus( - consensus::PARACHAIN_CONSENSUS_ID, - state.number.encode(), - ); - >::deposit_log(digest); - } - } - } - - #[pallet::genesis_config] - pub struct GenesisConfig {} - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig {} - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - // insert empty bytes - pallet_ismp::ConsensusStates::::insert( - consensus::PARACHAIN_CONSENSUS_ID, - Vec::::new(), - ); - - pallet_ismp::ConsensusClientUpdateTime::::insert( - consensus::PARACHAIN_CONSENSUS_ID, - // parachains have no challenge period - 0, - ); - } - } -} - -/// Interface that exposes the relay chain state roots. -pub trait RelayChainOracle { - /// Returns the state root for a given height if it exists. - fn state_root(height: relay_chain::BlockNumber) -> Option; -} - -impl RelayChainOracle for Pallet { - fn state_root(height: relay_chain::BlockNumber) -> Option { - RelayChainState::::get(height) - } -} diff --git a/parachain-consensus/Cargo.toml b/parachain/Cargo.toml similarity index 94% rename from parachain-consensus/Cargo.toml rename to parachain/Cargo.toml index f3022724b..067275931 100644 --- a/parachain-consensus/Cargo.toml +++ b/parachain/Cargo.toml @@ -22,6 +22,7 @@ ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-inherents = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } @@ -52,6 +53,7 @@ std = [ "sp-consensus-aura/std", "sp-runtime/std", "sp-io/std", + "sp-inherents/std", "primitive-types/std", "ismp-primitives/std", "pallet-ismp/std", diff --git a/parachain/inherent/Cargo.toml b/parachain/inherent/Cargo.toml new file mode 100644 index 000000000..c366eaed5 --- /dev/null +++ b/parachain/inherent/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "ismp-parachain-inherent" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +async-trait = { version = "0.1.63" } +codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] } +anyhow = "1.0.57" + +# Substrate +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } + +# Cumulus +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400" } + +# polytope-labs +ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } +ismp-parachain = { path = "../" } +ismp-parachain-runtime-api = { path = "../runtime-api" } \ No newline at end of file diff --git a/parachain/inherent/src/lib.rs b/parachain/inherent/src/lib.rs new file mode 100644 index 000000000..89f792e77 --- /dev/null +++ b/parachain/inherent/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ISMP Parachain Consensus Inherent Provider +//! +//! This exports the inherent provider for including ISMP parachain consensus updates as block +//! inherents. + +use codec::Encode; +use cumulus_primitives_core::PersistedValidationData; +use cumulus_relay_chain_interface::{PHash, RelayChainInterface}; +use ismp::messaging::ConsensusMessage; +use ismp_parachain::consensus::{parachain_header_storage_key, ParachainConsensusProof}; +use ismp_parachain_runtime_api::IsmpParachainApi; +use sp_runtime::traits::Block as BlockT; +use std::sync::Arc; + +/// Implements [`InherentDataProvider`] for providing parachain consensus updates as inherents. +pub struct ConsensusInherentProvider(Option); + +impl ConsensusInherentProvider { + /// Create the [`ConsensusInherentProvider`] at the given `relay_parent`. + pub async fn create( + client: Arc, + relay_parent: PHash, + relay_chain_interface: &impl RelayChainInterface, + validation_data: PersistedValidationData, + ) -> Result + where + C: sp_api::ProvideRuntimeApi + sp_blockchain::HeaderBackend, + C::Api: IsmpParachainApi, + B: BlockT, + { + let head = client.info().best_hash; + let para_ids = client.runtime_api().para_ids(head)?; + + if para_ids.is_empty() { + return Ok(ConsensusInherentProvider(None)) + } + + let keys = para_ids.iter().map(|id| parachain_header_storage_key(*id).0).collect(); + let storage_proof = relay_chain_interface + .prove_read(relay_parent, &keys) + .await? + .into_iter_nodes() + .collect(); + + let consensus_proof = ParachainConsensusProof { + para_ids, + relay_height: validation_data.relay_parent_number, + storage_proof, + }; + let message = ConsensusMessage { + consensus_client_id: ismp_parachain::consensus::PARACHAIN_CONSENSUS_ID, + consensus_proof: consensus_proof.encode(), + }; + + Ok(ConsensusInherentProvider(Some(message))) + } +} + +#[async_trait::async_trait] +impl sp_inherents::InherentDataProvider for ConsensusInherentProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + if let Some(ref message) = self.0 { + inherent_data.put_data(ismp_parachain::INHERENT_IDENTIFIER, message)?; + } + + Ok(()) + } + + async fn try_handle_error( + &self, + _: &sp_inherents::InherentIdentifier, + _: &[u8], + ) -> Option> { + None + } +} diff --git a/parachain/runtime-api/Cargo.toml b/parachain/runtime-api/Cargo.toml new file mode 100644 index 000000000..e96f50508 --- /dev/null +++ b/parachain/runtime-api/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ismp-parachain-runtime-api" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } + +[features] +default = ["std"] +std = ["sp-api/std"] diff --git a/parachain/runtime-api/src/lib.rs b/parachain/runtime-api/src/lib.rs new file mode 100644 index 000000000..a3e2ed908 --- /dev/null +++ b/parachain/runtime-api/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Runtime API for parachains. + +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use alloc::vec::Vec; + +sp_api::decl_runtime_apis! { + /// Ismp Parachain Runtime Apis + pub trait IsmpParachainApi { + /// Return all the para_ids this runtime is interested in. Used by the inherent provider + fn para_ids() -> Vec; + } +} diff --git a/parachain-consensus/src/consensus.rs b/parachain/src/consensus.rs similarity index 90% rename from parachain-consensus/src/consensus.rs rename to parachain/src/consensus.rs index ce8135a4e..5bf10f086 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -33,6 +33,7 @@ use ismp::{ use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; use merkle_mountain_range::MerkleProof; use pallet_ismp::host::Host; +use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use primitive_types::H256; use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; use sp_runtime::{ @@ -105,7 +106,7 @@ const SLOT_DURATION: u64 = 12_000; impl ConsensusClient for ParachainConsensusClient where R: RelayChainOracle, - T: pallet_ismp::Config, + T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, T::Hash: From, { @@ -122,29 +123,40 @@ where )) })?; - let root = R::state_root(update.relay_height).ok_or_else(|| { - Error::ImplementationSpecific(format!( - "Cannot find relay chain height: {}", - update.relay_height - )) - })?; + // first check our oracle's registry + let root = R::state_root(update.relay_height) + // not in our registry? ask parachain_system. + .or_else(|| { + let state = RelaychainDataProvider::::current_relay_chain_state(); + + if state.number == update.relay_height { + Some(state.state_root) + } else { + None + } + }) + // well, we couldn't find it + .ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Cannot find relay chain height: {}", + update.relay_height + )) + })?; let storage_proof = StorageProof::new(update.storage_proof); let mut intermediates = vec![]; - for id in update.para_ids { - let full_key = parachain_header_storage_key(id); - let header = read_proof_check::( - &root, - storage_proof.clone(), - vec![full_key.as_ref()], - ) - .map_err(|e| { + let keys = update.para_ids.iter().map(|id| parachain_header_storage_key(*id).0); + let headers = + read_proof_check::(&root, storage_proof, keys).map_err(|e| { Error::ImplementationSpecific(format!("Error verifying parachain header {e:?}",)) - })? - .remove(full_key.as_ref()) - .flatten() - .ok_or_else(|| { + })?; + + for (key, header) in headers { + let id = codec::Decode::decode(&mut &key[(key.len() - 4)..]).map_err(|e| { + Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) + })?; + let header = header.ok_or_else(|| { Error::ImplementationSpecific(format!( "Cannot find parachain header for ParaId({id})", )) diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs new file mode 100644 index 000000000..fc547f302 --- /dev/null +++ b/parachain/src/lib.rs @@ -0,0 +1,245 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ISMP Parachain Consensus Client +//! +//! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +extern crate core; + +pub mod consensus; + +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::relay_chain; +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use cumulus_primitives_core::relay_chain; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use ismp::{ + consensus::StateMachineId, + host::StateMachine, + messaging::{ConsensusMessage, Message}, + }; + use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; + use primitive_types::H256; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_ismp::Config + parachain_system::Config + { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + /// Mapping of relay chain heights to it's state root. Gotten from parachain-system. + #[pallet::storage] + #[pallet::getter(fn relay_chain_state)] + pub type RelayChainState = + StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; + + /// Tracks whether we've already seen the `update_parachain_consensus` inherent + #[pallet::storage] + pub type ConsensusUpdated = StorageValue<_, bool>; + + /// Tracks whether we've already seen the `update_parachain_consensus` inherent + #[pallet::storage] + pub type Parachains = StorageMap<_, Identity, u32, ()>; + + #[pallet::event] + pub enum Event {} + + #[pallet::call] + impl Pallet + where + ::Hash: From, + { + /// Rather than users manually submitting consensus updates for sibling parachains, we + /// instead make it the responsibility of the block builder to insert the consensus + /// updates as an inherent. + #[pallet::call_index(0)] + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn update_parachain_consensus( + origin: OriginFor, + data: ConsensusMessage, + ) -> DispatchResultWithPostInfo { + ensure_none(origin)?; + assert!( + !>::exists(), + "ValidationData must be updated only once in a block", + ); + + assert_eq!( + data.consensus_client_id, + consensus::PARACHAIN_CONSENSUS_ID, + "Only parachain consensus updates should be passed in the inherents!" + ); + + pallet_ismp::Pallet::::handle_messages(vec![Message::Consensus(data)])?; + + Ok(Pays::No.into()) + } + + /// Add some new parachains to the list of parachains we care about + #[pallet::call_index(1)] + #[pallet::weight(0)] // todo: fix weight + pub fn add_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { + ensure_root(origin)?; + for id in para_ids { + Parachains::::insert(id, ()); + } + + Ok(()) + } + + /// Remove some parachains from the list of parachains we care about + #[pallet::call_index(2)] + #[pallet::weight(0)] // todo: fix weight + pub fn remove_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { + ensure_root(origin)?; + for id in para_ids { + Parachains::::remove(id); + } + + Ok(()) + } + } + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: T::BlockNumber) { + let state = RelaychainDataProvider::::current_relay_chain_state(); + if !RelayChainState::::contains_key(state.number) { + RelayChainState::::insert(state.number, state.state_root); + + let digest = sp_runtime::generic::DigestItem::Consensus( + consensus::PARACHAIN_CONSENSUS_ID, + state.number.encode(), + ); + + >::deposit_log(digest); + } + } + + fn on_initialize(_n: T::BlockNumber) -> Weight { + // kill the storage, since this is the beginning of a new block. + ConsensusUpdated::::kill(); + + Weight::from_parts(0, 0) + } + } + + /// The identifier for the parachain consensus update inherent. + pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"paraismp"; + + #[pallet::inherent] + impl ProvideInherent for Pallet + where + ::Hash: From, + { + type Call = Call; + type Error = sp_inherents::MakeFatalError<()>; + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn create_inherent(data: &InherentData) -> Option { + let data: ConsensusMessage = + data.get_data(&Self::INHERENT_IDENTIFIER).ok().flatten()?; + + Some(Call::update_parachain_consensus { data }) + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::update_parachain_consensus { .. }) + } + } + + #[pallet::genesis_config] + pub struct GenesisConfig { + /// List of parachains to track at genesis + pub parachains: Vec, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { parachains: vec![] } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + // insert empty bytes + pallet_ismp::ConsensusStates::::insert( + consensus::PARACHAIN_CONSENSUS_ID, + Vec::::new(), + ); + + pallet_ismp::ConsensusClientUpdateTime::::insert( + consensus::PARACHAIN_CONSENSUS_ID, + // parachains have no challenge period + 0, + ); + + // insert the parachain ids + for id in &self.parachains { + Parachains::::insert(id, ()); + + let state_id = match T::StateMachine::get() { + StateMachine::Polkadot(_) => StateMachine::Polkadot(*id), + StateMachine::Kusama(_) => StateMachine::Kusama(*id), + _ => panic!("State machine should be configured as a parachain!"), + }; + + // insert the "latest" parachain height + pallet_ismp::LatestStateMachineHeight::::insert( + StateMachineId { + consensus_client: consensus::PARACHAIN_CONSENSUS_ID, + state_id, + }, + 0, + ); + } + } + } +} + +impl Pallet { + /// Returns the list of parachains who's consensus updates will be inserted by the inherent + /// data provider + pub fn para_ids() -> Vec { + Parachains::::iter_keys().collect() + } +} + +/// Interface that exposes the relay chain state roots. +pub trait RelayChainOracle { + /// Returns the state root for a given height if it exists. + fn state_root(height: relay_chain::BlockNumber) -> Option; +} + +impl RelayChainOracle for Pallet { + fn state_root(height: relay_chain::BlockNumber) -> Option { + RelayChainState::::get(height) + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 99c6e11a1..e26d01035 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] -channel = "1.66" +channel = "nightly-2022-11-16" +components = [ "rustfmt" ] \ No newline at end of file From ad946c080600a73e6fd29b9e978da4bb0ec8324a Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 24 May 2023 17:06:09 +0100 Subject: [PATCH 127/182] introduce docs (#41) * introduce docs * initial docs effort * add more unit tests and on_finalize benchmarks * add docs, license and readme * nit * fix doc errors * readme --------- Co-authored-by: David Salami --- .../{build-test-and-lint.yml => ci.yml} | 0 Cargo.lock | 4 +- LICENSE | 51 +++++ README.md | 25 +++ ismp-assets/src/lib.rs | 38 +++- pallet-ismp/primitives/src/lib.rs | 8 + pallet-ismp/primitives/src/mmr.rs | 17 +- pallet-ismp/rpc/src/lib.rs | 21 +- pallet-ismp/runtime-api/src/lib.rs | 17 ++ pallet-ismp/src/benchmarking.rs | 76 +++++-- pallet-ismp/src/errors.rs | 17 ++ pallet-ismp/src/events.rs | 27 ++- pallet-ismp/src/host.rs | 25 ++- pallet-ismp/src/lib.rs | 139 ++++++++----- pallet-ismp/src/mmr.rs | 15 ++ pallet-ismp/src/mmr/storage.rs | 2 + pallet-ismp/src/mmr/utils.rs | 15 ++ pallet-ismp/src/mock.rs | 19 +- pallet-ismp/src/primitives.rs | 29 +++ pallet-ismp/src/router.rs | 30 ++- pallet-ismp/src/tests.rs | 186 +++++++++++++++++- pallet-ismp/src/weight_info.rs | 35 +++- parachain/inherent/src/lib.rs | 4 +- parachain/runtime-api/src/lib.rs | 2 + parachain/src/consensus.rs | 30 ++- parachain/src/lib.rs | 12 +- 26 files changed, 727 insertions(+), 117 deletions(-) rename .github/workflows/{build-test-and-lint.yml => ci.yml} (100%) create mode 100644 LICENSE create mode 100644 README.md diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/workflows/build-test-and-lint.yml rename to .github/workflows/ci.yml diff --git a/Cargo.lock b/Cargo.lock index 4961c16d0..883c316ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3004,7 +3004,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#f33ea2c2a376496ff7d3c644dc725e797d535e01" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#c51850fdd6ff87e8ccf839704343d3c68987742c" dependencies = [ "derive_more", "parity-scale-codec", @@ -3125,7 +3125,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#f33ea2c2a376496ff7d3c644dc725e797d535e01" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#c51850fdd6ff87e8ccf839704343d3c68987742c" dependencies = [ "ismp", "parity-scale-codec", diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..3a23e7f2e --- /dev/null +++ b/LICENSE @@ -0,0 +1,51 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..0a8f64e82 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# pallet-ismp ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) + +Implementation of the Interoperable State Machine Protocol for substrate runtimes. This project is [funded by the web3 foundation](https://github.com/w3f/Grants-Program/blob/master/applications/ismp.md). + +## Overview + +This repo holds all the required components substrate runtimes need to interoperate together using [ISMP](https://github.com/polytope-labs/ismp) + +* [pallet-ismp](./) +* [ismp-runtime-api](./pallet-ismp/runtime-api) +* [ismp-rpc](./pallet-ismp/rpc) + +### Parachain Support + +* [ismp-parachain](./parachain) +* [ismp-parachain-inherent](./parachain/inherent) +* [ismp-parachain-runtime-api](./parachain/runtime-api) + +## Documentation + +Installation and integration guides can be found in the [book](https://substrate-ismp.polytope.technology). + +## License + +This library is licensed under the Apache 2.0 License, Copyright (c) 2023 Polytope Labs. \ No newline at end of file diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index 59ee9f89c..ae1286f90 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -16,17 +16,19 @@ //! ISMP Assets //! Simple Demo for Asset transfer over ISMP #![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] extern crate alloc; use alloc::string::ToString; use frame_support::{traits::fungible::Mutate, PalletId}; use ismp::{ - module::ISMPModule, + module::IsmpModule, router::{Request, Response}, }; pub use pallet::*; +/// Constant Pallet ID pub const PALLET_ID: PalletId = PalletId(*b"ismp-ast"); #[frame_support::pallet] @@ -46,34 +48,51 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + /// Pallet Configuration #[pallet::config] pub trait Config: frame_system::Config { + /// Overarching event type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Native balance type Balance: Balance + Into<>::Balance>; + /// Native currency implementation type NativeCurrency: Mutate; + /// Ismp message disptacher type IsmpDispatch: IsmpDispatch; } + /// Pallet events #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// Some balance has been transferred BalanceTransferred { + /// Source account from: T::AccountId, + /// Destination account to: T::AccountId, + /// Amount being transferred amount: T::Balance, + /// Destination chain's Id dest_chain: StateMachine, }, - + /// Some balance has been received BalanceReceived { + /// Source account from: T::AccountId, + /// Receiving account to: T::AccountId, + /// Amount that was received amount: T::Balance, + /// Source chain's Id source_chain: StateMachine, }, } + /// Pallet Errors #[pallet::error] pub enum Error { + /// Error encountered when initializing transfer TransferFailed, } @@ -83,6 +102,7 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// Transfer some funds over ISMP #[pallet::weight(1_000_000)] #[pallet::call_index(0)] pub fn transfer( @@ -111,32 +131,42 @@ pub mod pallet { } } + /// Transfer payload + /// This would be encoded to bytes as the request data #[derive( Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, )] pub struct Payload { + /// Destination account pub to: AccountId, + /// Source account pub from: AccountId, + /// Amount to be transferred pub amount: Balance, } + /// Extrinsic Parameters for initializing a cross chain transfer #[derive( Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, )] pub struct TransferParams { + /// Destination account pub to: AccountId, + /// Amount to transfer pub amount: Balance, + /// Destination chain's Id pub dest_chain: StateMachine, - /// Timeout timestamp in seconds + /// Timeout timestamp on destination chain in seconds pub timeout: u64, } } +/// Ismp dispatch error fn ismp_dispatch_error(msg: &'static str) -> ismp::error::Error { ismp::error::Error::ImplementationSpecific(msg.to_string()) } -impl ISMPModule for Pallet { +impl IsmpModule for Pallet { fn on_accept(request: Request) -> Result<(), ismp::error::Error> { let source_chain = request.source_chain(); let data = match request { diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs index cc2863fbc..07e574361 100644 --- a/pallet-ismp/primitives/src/lib.rs +++ b/pallet-ismp/primitives/src/lib.rs @@ -13,16 +13,24 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! The primitive types used by pallet-ismp + #![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] +//! Primitives for the MMR implementation use ismp::host::StateMachine; pub mod mmr; +/// Queries a request leaf in the mmr #[derive(codec::Encode, codec::Decode)] #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] pub struct LeafIndexQuery { + /// The source of the request pub source_chain: StateMachine, + /// the request destination pub dest_chain: StateMachine, + /// The request nonce pub nonce: u64, } diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs index 4d51ae836..c93286fa5 100644 --- a/pallet-ismp/primitives/src/mmr.rs +++ b/pallet-ismp/primitives/src/mmr.rs @@ -12,28 +12,37 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +//! MMR utilities + use core::fmt::Formatter; use codec::{Decode, Encode}; use ismp::{ - host::ISMPHost, + host::IsmpHost, router::{Request, Response}, util::{hash_request, hash_response}, }; use primitive_types::H256; use sp_runtime::traits; +/// Index of a leaf in the MMR pub type LeafIndex = u64; +/// Index of a node in the MMR pub type NodeIndex = u64; +/// A concrete Leaf for the MMR #[derive(Debug, Clone, Decode, Encode, PartialEq, Eq)] pub enum Leaf { + /// A request variant Request(Request), + /// A response variant Response(Response), } impl Leaf { - fn hash(&self) -> H256 { + /// Returns the hash of a leaf + fn hash(&self) -> H256 { match self { Leaf::Request(req) => hash_request::(req), Leaf::Response(res) => hash_response::(res), @@ -74,7 +83,7 @@ where /// /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. - pub fn hash( + pub fn hash( &self, ) -> <::Hashing as traits::Hash>::Output { match *self { @@ -91,7 +100,7 @@ impl merkle_mountain_range::Merge for MmrHasher where T: frame_system::Config, T::Hash: From, - H: ISMPHost, + H: IsmpHost, { type Item = DataOrHash; diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index 1175516c5..ab2e19a06 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -1,6 +1,21 @@ -#![warn(missing_docs)] - -//! ISMP RPC Implementation. +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![deny(missing_docs)] + +//! RPC Implementation for the Interoperable State Machine Protocol use jsonrpsee::{ core::{Error as RpcError, RpcResult as Result}, diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index e325791d0..e5c976c79 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -1,5 +1,22 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Pallet-ismp runtime Apis + #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::too_many_arguments)] +#![deny(missing_docs)] use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index a3ff0c1da..b8ae69c3e 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -1,3 +1,19 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarking // Only enable this module for benchmarking. #![cfg(feature = "runtime-benchmarks")] @@ -5,14 +21,11 @@ use crate::*; use frame_benchmarking::v2::*; use frame_system::RawOrigin; -// Running the benchmarks correctly -// Add the [`BenchmarkClient`] as one of the consensus clients available to pallet-ismp in the -// runtime configuration -// In your module router configuration add the [`BenchmarkIsmpModule`] as one of the ismp modules -// using the pallet id defined here as it's module id. - -// Details on using the benchmarks macro can be seen at: -// https://paritytech.github.io/substrate/master/frame_benchmarking/trait.Benchmarking.html#tymethod.benchmarks +/// Running the benchmarks correctly. +/// Add the [`BenchmarkClient`] as one of the consensus clients available to pallet-ismp in the +/// runtime configuration. +/// In your module router configuration add the [`BenchmarkIsmpModule`] as one of the ismp modules +/// using the pallet id defined here as it's module id. #[benchmarks( where ::Hash: From, @@ -22,17 +35,19 @@ use frame_system::RawOrigin; pub mod benchmarks { use super::*; use crate::router::Receipt; - use frame_support::PalletId; + use frame_support::{traits::Hooks, PalletId}; use frame_system::EventRecord; use ismp_rs::{ consensus::{ConsensusClient, IntermediateState, StateCommitment, StateMachineHeight}, error::Error as IsmpError, messaging::{Message, Proof, RequestMessage, ResponseMessage, TimeoutMessage}, - module::ISMPModule, + module::IsmpModule, router::{Post, RequestResponse}, util::hash_request, }; + use sp_std::prelude::Vec; + /// Verify the the last event emitted fn assert_last_event(generic_event: ::RuntimeEvent) { let events = frame_system::Pallet::::events(); let system_event: ::RuntimeEvent = generic_event.into(); @@ -40,15 +55,17 @@ pub mod benchmarks { assert_eq!(event, &system_event); } + /// A mock consensus client for benchmarking #[derive(Default)] pub struct BenchmarkClient; + /// Consensus client id for benchmarking consensus client pub const BENCHMARK_CONSENSUS_CLIENT_ID: [u8; 4] = [1u8; 4]; impl ConsensusClient for BenchmarkClient { fn verify_consensus( &self, - _host: &dyn ISMPHost, + _host: &dyn IsmpHost, _trusted_consensus_state: Vec, _proof: Vec, ) -> Result<(Vec, Vec), IsmpError> { @@ -61,7 +78,7 @@ pub mod benchmarks { fn verify_membership( &self, - _host: &dyn ISMPHost, + _host: &dyn IsmpHost, _item: RequestResponse, _root: StateCommitment, _proof: &Proof, @@ -69,13 +86,13 @@ pub mod benchmarks { Ok(()) } - fn state_trie_key(&self, _request: RequestResponse) -> Vec> { + fn state_trie_key(&self, _request: Vec) -> Vec> { Default::default() } fn verify_state_proof( &self, - _host: &dyn ISMPHost, + _host: &dyn IsmpHost, _keys: Vec>, _root: StateCommitment, _proof: &Proof, @@ -90,8 +107,9 @@ pub mod benchmarks { /// This module should be added to the module router in runtime for benchmarks to pass pub struct BenchmarkIsmpModule; + /// module id for the mock benchmarking module pub const MODULE_ID: PalletId = PalletId(*b"benchmak"); - impl ISMPModule for BenchmarkIsmpModule { + impl IsmpModule for BenchmarkIsmpModule { fn on_accept(_request: Request) -> Result<(), IsmpError> { Ok(()) } @@ -105,6 +123,7 @@ pub mod benchmarks { } } + /// Sets the current timestamp fn set_timestamp() where ::Moment: From, @@ -146,7 +165,7 @@ pub mod benchmarks { ); } - fn setup_mock_client(host: &H) -> IntermediateState { + fn setup_mock_client(host: &H) -> IntermediateState { let intermediate_state = IntermediateState { height: StateMachineHeight { id: StateMachineId { @@ -268,5 +287,30 @@ pub mod benchmarks { assert!(RequestAcks::::get(commitment.0.to_vec()).is_none()); } + #[benchmark] + fn on_finalize(x: Linear<1, 100>) { + for nonce in 0..x { + let post = ismp_rs::router::Post { + source_chain: StateMachine::Kusama(2000), + dest_chain: StateMachine::Kusama(2001), + nonce: nonce.into(), + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100, + data: vec![2u8; 64], + }; + + let request = Request::Post(post); + let leaf = Leaf::Request(request); + + Pallet::::mmr_push(leaf.clone()).unwrap(); + } + + #[block] + { + Pallet::::on_finalize(2u32.into()) + } + } + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Test); } diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index cdf16805b..f6e43ca6d 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -1,3 +1,19 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Ismp Errors conversions use codec::{Decode, Encode}; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineHeight}, @@ -7,6 +23,7 @@ use ismp_rs::{ use sp_std::prelude::*; #[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +#[allow(missing_docs)] pub enum HandlingError { ChallengePeriodNotElapsed { update_time: u64, diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 74d0060b2..5b47337d8 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -1,3 +1,19 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Core ISMP events + use crate::{Config, Event as PalletEvent}; use alloc::collections::BTreeSet; use ismp_rs::{ @@ -5,20 +21,25 @@ use ismp_rs::{ host::StateMachine, }; +/// Ismp Core Protocol Events #[derive(Clone, codec::Encode, codec::Decode, Debug)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum Event { - // Emitted when a state machine is successfully updated to a new height + /// Emitted when a state machine is successfully updated to a new height StateMachineUpdated { + /// State machine id state_machine_id: StateMachineId, + /// Latest height latest_height: u64, }, + /// Emitted when a challenge period has begun for a consensus client ChallengePeriodStarted { + /// Consensus client id consensus_client_id: ConsensusClientId, /// Tuple of previous height and latest height state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, }, - + /// Emitted for an outgoing response Response { /// Chain that this response will be routed to dest_chain: StateMachine, @@ -27,6 +48,7 @@ pub enum Event { /// Nonce for the request which this response is for request_nonce: u64, }, + /// Emitted for an outgoing request Request { /// Chain that this request will be routed to dest_chain: StateMachine, @@ -37,6 +59,7 @@ pub enum Event { }, } +/// Convert from pallet event to Ismp event pub fn to_core_protocol_event(event: PalletEvent) -> Option { match event { PalletEvent::StateMachineUpdated { state_machine_id, latest_height } => { diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 705e9a554..5502d199e 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -1,3 +1,19 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Host implementation for ISMP use crate::{ primitives::ConsensusClientProvider, router::Receipt, Config, ConsensusClientUpdateTime, ConsensusStates, FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, @@ -10,14 +26,15 @@ use ismp_rs::{ ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, }, error::Error, - host::{ISMPHost, StateMachine}, - router::{ISMPRouter, Request}, + host::{IsmpHost, StateMachine}, + router::{IsmpRouter, Request}, util::hash_request, }; use sp_core::H256; use sp_runtime::SaturatedConversion; use sp_std::prelude::*; +/// An implementation for the IsmpHost #[derive(Clone)] pub struct Host(core::marker::PhantomData); @@ -27,7 +44,7 @@ impl Default for Host { } } -impl ISMPHost for Host +impl IsmpHost for Host where ::Hash: From, { @@ -163,7 +180,7 @@ where ::ConsensusClientProvider::challenge_period(id) } - fn ismp_router(&self) -> Box { + fn ismp_router(&self) -> Box { Box::new(T::IsmpRouter::default()) } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index cbf0af969..f2d256c0e 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -13,8 +13,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! ISMP implementation for substrate-based chains. + // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] extern crate alloc; @@ -55,7 +58,7 @@ use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; -use ismp_rs::{host::ISMPHost, messaging::Message, router::ISMPRouter}; +use ismp_rs::{host::IsmpHost, messaging::Message, router::IsmpRouter}; pub use pallet::*; use sp_std::prelude::*; @@ -81,20 +84,16 @@ pub mod pallet { handlers::{self}, host::StateMachine, messaging::Message, - router::ISMPRouter, + router::IsmpRouter, }; use sp_core::H256; use weight_info::get_weight; - /// Our pallet's configuration trait. All our types and constants go in here. If the - /// pallet is dependent on specific other pallets, then their configuration traits - /// should be added to our implied traits list. - /// - /// `frame_system::Config` should always be included. #[pallet::config] pub trait Config: frame_system::Config { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Prefix for elements stored in the Off-chain DB via Indexing API. /// /// Each node of the MMR is inserted both on-chain and off-chain via Indexing API. @@ -116,11 +115,14 @@ pub mod pallet { type TimeProvider: UnixTime; /// Configurable router that dispatches calls to modules - type IsmpRouter: ISMPRouter + Default; + type IsmpRouter: IsmpRouter + Default; + /// Provides concrete implementations of consensus clients type ConsensusClientProvider: ConsensusClientProvider; + /// Weight Info type WeightInfo: WeightInfo; + /// Weight provider for consensus clients and module callbacks type WeightProvider: WeightProvider; } @@ -150,50 +152,56 @@ pub mod pallet { pub type Nodes = StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; + /// Holds a map of state machine heights to their verified state commitments #[pallet::storage] #[pallet::getter(fn state_commitments)] pub type StateCommitments = StorageMap<_, Blake2_128Concat, StateMachineHeight, StateCommitment, OptionQuery>; + /// Holds a map of consensus clients to their consensus state. #[pallet::storage] #[pallet::getter(fn consensus_states)] pub type ConsensusStates = StorageMap<_, Twox64Concat, ConsensusClientId, Vec, OptionQuery>; + /// Holds a map of state machines to the height at which they've been frozen due to byzantine + /// behaviour #[pallet::storage] #[pallet::getter(fn frozen_heights)] pub type FrozenHeights = StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + /// The latest verified height for a state machine #[pallet::storage] #[pallet::getter(fn latest_state_height)] - /// The latest accepted state machine height pub type LatestStateMachineHeight = StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + /// Holds the timestamp at which a consensus client was recently updated. + /// Used in ensuring that the configured challenge period elapses. #[pallet::storage] #[pallet::getter(fn consensus_update_time)] pub type ConsensusClientUpdateTime = StorageMap<_, Twox64Concat, ConsensusClientId, u64, OptionQuery>; - #[pallet::storage] - #[pallet::getter(fn request_acks)] /// Acknowledgements for incoming and outgoing requests /// The key is the request commitment + #[pallet::storage] + #[pallet::getter(fn request_acks)] pub type RequestAcks = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; - #[pallet::storage] - #[pallet::getter(fn response_acks)] /// Acknowledgements for incoming and outgoing responses /// The key is the response commitment + #[pallet::storage] + #[pallet::getter(fn response_acks)] pub type ResponseAcks = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; - #[pallet::storage] - #[pallet::getter(fn consensus_update_results)] /// Consensus update results still in challenge period /// Set contains a tuple of previous height and latest height + #[pallet::storage] + #[pallet::getter(fn consensus_update_results)] pub type ConsensusUpdateResults = StorageMap< _, Twox64Concat, @@ -202,7 +210,7 @@ pub mod pallet { OptionQuery, >; - /// Latest Nonce value for messages sent from this chain + /// Latest nonce for messages sent from this chain #[pallet::storage] #[pallet::getter(fn nonce)] pub type Nonce = StorageValue<_, u64, ValueQuery>; @@ -215,7 +223,7 @@ pub mod pallet { { fn on_initialize(_n: T::BlockNumber) -> Weight { // return Mmr finalization weight here - Weight::zero() + ::WeightInfo::on_finalize(Self::number_of_leaves() as u32) } fn on_finalize(_n: T::BlockNumber) { @@ -261,14 +269,14 @@ pub mod pallet { Self::handle_messages(messages) } - /// Create consensus clients + /// Create a consensus client, using a subjectively chosen consensus state. #[pallet::weight(::WeightInfo::create_consensus_client())] #[pallet::call_index(1)] pub fn create_consensus_client( origin: OriginFor, message: CreateConsensusClient, ) -> DispatchResult { - ::AdminOrigin::ensure_origin(origin)?; + T::AdminOrigin::ensure_origin(origin)?; let host = Host::::default(); let result = handlers::create_consensus_client(&host, message) @@ -282,23 +290,28 @@ pub mod pallet { } } - /// Events are a simple means of reporting specific conditions and - /// circumstances that have happened that users, Dapps and/or chain explorers would find - /// interesting and otherwise difficult to detect. #[pallet::event] - /// This attribute generate the function `deposit_event` to deposit one of this pallet event, - /// it is optional, it is also possible to provide a custom implementation. #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Emitted when a state machine is successfully updated to a new height - StateMachineUpdated { state_machine_id: StateMachineId, latest_height: u64 }, + StateMachineUpdated { + /// State machine height + state_machine_id: StateMachineId, + /// State machine latest height + latest_height: u64, + }, /// Signifies that a client has begun it's challenge period ChallengePeriodStarted { + /// Consensus client id consensus_client_id: ConsensusClientId, + /// Tuple of previous height and latest height for state machines state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, }, /// Indicates that a consensus client has been created - ConsensusClientCreated { consensus_client_id: ConsensusClientId }, + ConsensusClientCreated { + /// Consensus client id + consensus_client_id: ConsensusClientId, + }, /// An Outgoing Response has been deposited Response { /// Chain that this response will be routed to @@ -318,12 +331,18 @@ pub mod pallet { request_nonce: u64, }, /// Some errors handling some ismp messages - HandlingErrors { errors: Vec }, + HandlingErrors { + /// Message handling errors + errors: Vec, + }, } + /// Pallet errors #[pallet::error] pub enum Error { + /// Invalid ISMP message InvalidMessage, + /// Encountered an error while creating the consensus client. ConsensusClientCreationFailed, } } @@ -420,34 +439,10 @@ where } } -impl Pallet { - fn get_node(pos: NodeIndex) -> Option> { - Nodes::::get(pos).map(DataOrHash::Hash) - } - - fn remove_node(pos: NodeIndex) { - Nodes::::remove(pos); - } - - fn insert_node(pos: NodeIndex, node: ::Hash) { - Nodes::::insert(pos, node) - } - - fn get_num_leaves() -> LeafIndex { - NumberOfLeaves::::get() - } - - fn set_num_leaves(num_leaves: LeafIndex) { - NumberOfLeaves::::put(num_leaves) - } - - fn offchain_key(pos: NodeIndex) -> Vec { - (T::INDEXING_PREFIX, "leaves", pos).encode() - } -} - +/// Digest log for mmr root hash #[derive(RuntimeDebug, Encode, Decode)] pub struct RequestResponseLog { + /// The mmr root hash mmr_root_hash: ::Hash, } @@ -455,6 +450,7 @@ impl Pallet where ::Hash: From, { + /// Returns the offchain key for a request leaf index pub fn request_leaf_index_offchain_key( source_chain: StateMachine, dest_chain: StateMachine, @@ -463,6 +459,7 @@ where (T::INDEXING_PREFIX, "requests_leaf_indices", source_chain, dest_chain, nonce).encode() } + /// Returns the offchain key for a response leaf index pub fn response_leaf_index_offchain_key( source_chain: StateMachine, dest_chain: StateMachine, @@ -471,10 +468,12 @@ where (T::INDEXING_PREFIX, "responses_leaf_indices", source_chain, dest_chain, nonce).encode() } + /// Stores the leaf index or the given key pub fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { sp_io::offchain_index::set(&key, &leaf_index.encode()); } + /// Gets the request from the offchain storage pub fn get_request(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { @@ -490,6 +489,7 @@ where None } + /// Gets the response from the offchain storage pub fn get_response(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { @@ -505,6 +505,7 @@ where None } + /// Gets the leaf index for a request or response from the offchain storage pub fn get_leaf_index( source_chain: StateMachine, dest_chain: StateMachine, @@ -567,6 +568,7 @@ where leaf_indices.into_iter().filter_map(|leaf_index| Self::get_response(leaf_index)).collect() } + /// Insert a leaf into the mmr pub fn mmr_push(leaf: Leaf) -> Option { let offchain_key = match &leaf { Leaf::Request(req) => Pallet::::request_leaf_index_offchain_key( @@ -589,6 +591,37 @@ where } impl Pallet { + /// Get a node from runtime storage + fn get_node(pos: NodeIndex) -> Option> { + Nodes::::get(pos).map(DataOrHash::Hash) + } + + /// Remove a node from storage + fn remove_node(pos: NodeIndex) { + Nodes::::remove(pos); + } + + /// Insert a node into storage + fn insert_node(pos: NodeIndex, node: ::Hash) { + Nodes::::insert(pos, node) + } + + /// Returns the number of leaves in the mmr + fn get_num_leaves() -> LeafIndex { + NumberOfLeaves::::get() + } + + /// Set the number of leaves in the mmr + fn set_num_leaves(num_leaves: LeafIndex) { + NumberOfLeaves::::put(num_leaves) + } + + /// Returns the offchain key for an index + fn offchain_key(pos: NodeIndex) -> Vec { + (T::INDEXING_PREFIX, "leaves", pos).encode() + } + + /// Returns the next available nonce fn next_nonce() -> u64 { let nonce = Nonce::::get(); Nonce::::put(nonce + 1); diff --git a/pallet-ismp/src/mmr.rs b/pallet-ismp/src/mmr.rs index 0951de5d9..4bc7fdec3 100644 --- a/pallet-ismp/src/mmr.rs +++ b/pallet-ismp/src/mmr.rs @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + pub mod mmr; pub mod storage; pub mod utils; diff --git a/pallet-ismp/src/mmr/storage.rs b/pallet-ismp/src/mmr/storage.rs index 122222d89..34433b956 100644 --- a/pallet-ismp/src/mmr/storage.rs +++ b/pallet-ismp/src/mmr/storage.rs @@ -143,6 +143,7 @@ impl Storage where T: Config, { + /// Store a node in the offchain db fn store_to_offchain(pos: NodeIndex, node: &DataOrHash) { let encoded_node = node.encode(); @@ -156,6 +157,7 @@ where } } +/// Calculate peaks to prune and store fn peaks_to_prune_and_store( old_size: NodeIndex, new_size: NodeIndex, diff --git a/pallet-ismp/src/mmr/utils.rs b/pallet-ismp/src/mmr/utils.rs index 64c788826..dafb399cf 100644 --- a/pallet-ismp/src/mmr/utils.rs +++ b/pallet-ismp/src/mmr/utils.rs @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use ismp_primitives::mmr::{LeafIndex, NodeIndex}; /// MMR nodes & size -related utilities. diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index a7d0987c2..181722505 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use crate as pallet_ismp; use crate::*; @@ -100,7 +115,7 @@ impl Config for Test { #[derive(Default)] pub struct ModuleRouter; -impl ISMPRouter for ModuleRouter { +impl IsmpRouter for ModuleRouter { fn dispatch(&self, request: Request) -> DispatchResult { let dest = request.dest_chain(); let source = request.source_chain(); @@ -135,7 +150,7 @@ impl Default for Router { } } -impl ISMPRouter for Router { +impl IsmpRouter for Router { fn dispatch(&self, request: Request) -> DispatchResult { self.inner.dispatch(request) } diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index dfb74e91a..fd3f3917d 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -1,3 +1,19 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Pallet primitives use core::time::Duration; use frame_support::RuntimeDebug; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; @@ -25,6 +41,7 @@ pub struct Proof { /// Merkle Mountain Range operation error. #[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] +#[allow(missing_docs)] pub enum Error { InvalidNumericOp, Push, @@ -38,15 +55,21 @@ pub enum Error { InvalidBestKnownBlock, } +/// A trait that returns a reference to a consensus client based on its Id +/// This trait should be implemented in the runtime pub trait ConsensusClientProvider { + /// Returns a reference to a consensus client fn consensus_client( id: ConsensusClientId, ) -> Result, ismp_rs::error::Error>; + /// Returns the challenge period configured for a consensus client fn challenge_period(id: ConsensusClientId) -> Duration; } +/// An internal message type for pallet ISMP pub enum IsmpMessage { + /// A post request Post { /// The destination state machine of this request. dest_chain: StateMachine, @@ -59,6 +82,7 @@ pub enum IsmpMessage { /// Encoded Request. data: Vec, }, + /// A get request Get { /// The destination state machine of this request. dest_chain: StateMachine, @@ -71,12 +95,17 @@ pub enum IsmpMessage { /// Host Timestamp which this request expires in seconds timeout_timestamp: u64, }, + /// A response Response { + /// Post request post: Post, + /// Opaque response bytes response: Vec, }, } +/// A trait that exposes an interface for modules to dispatch ismp messages to the router pub trait IsmpDispatch { + /// Dispatch an ismp message to the router fn dispatch_message(msg: IsmpMessage) -> Result<(), ismp_rs::router::DispatchError>; } diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs index 8eee6bbaa..97b8a2c98 100644 --- a/pallet-ismp/src/router.rs +++ b/pallet-ismp/src/router.rs @@ -1,24 +1,44 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation for the ISMP Router use crate::{host::Host, Config, Event, Pallet, RequestAcks, ResponseAcks}; use alloc::{boxed::Box, string::ToString}; use codec::{Decode, Encode}; use core::marker::PhantomData; use ismp_primitives::mmr::Leaf; use ismp_rs::{ - host::ISMPHost, - router::{DispatchError, DispatchResult, DispatchSuccess, ISMPRouter, Request, Response}, + host::IsmpHost, + router::{DispatchError, DispatchResult, DispatchSuccess, IsmpRouter, Request, Response}, util::{hash_request, hash_response}, }; use sp_core::H256; +/// A receipt or an outgoing or incoming request or response #[derive(Encode, Decode, scale_info::TypeInfo)] pub enum Receipt { + /// Ok Ok, } /// The proxy router, This router allows for routing requests & responses from a source chain /// to a destination chain. pub struct ProxyRouter { - inner: Option>, + /// Module router + inner: Option>, + /// Phantom _phantom: PhantomData, } @@ -26,7 +46,7 @@ impl ProxyRouter { /// Initialize the proxy router with an inner router. pub fn new(router: R) -> Self where - R: ISMPRouter + 'static, + R: IsmpRouter + 'static, { Self { inner: Some(Box::new(router)), _phantom: PhantomData } } @@ -38,7 +58,7 @@ impl Default for ProxyRouter { } } -impl ISMPRouter for ProxyRouter +impl IsmpRouter for ProxyRouter where T: Config, ::Hash: From, diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index 737c4c440..bbf38e462 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use crate::{mock::*, *}; use std::{ ops::Range, @@ -6,9 +21,13 @@ use std::{ use frame_support::traits::OnFinalize; use ismp_primitives::mmr::MmrHasher; +use ismp_rs::{ + consensus::{IntermediateState, StateCommitment, StateMachineHeight}, + messaging::{Proof, ResponseMessage, TimeoutMessage}, +}; use ismp_testsuite::{ - check_challenge_period, check_client_expiry, frozen_check, timeout_post_processing_check, - write_outgoing_commitments, + check_challenge_period, check_client_expiry, frozen_check, mocks::MOCK_CONSENSUS_CLIENT_ID, + timeout_post_processing_check, write_outgoing_commitments, }; use mmr_lib::MerkleProof; use sp_core::{ @@ -167,9 +186,9 @@ fn should_generate_and_verify_batch_proof_for_leaves_inserted_across_multiple_bl }) } -fn set_timestamp() { +fn set_timestamp(now: Option) { Timestamp::set_timestamp( - SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64 + now.unwrap_or(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64), ); } @@ -178,7 +197,7 @@ fn check_for_duplicate_requests_and_responses() { let mut ext = new_test_ext(); ext.execute_with(|| { - set_timestamp(); + set_timestamp(None); let host = Host::::default(); write_outgoing_commitments(&host).unwrap(); }) @@ -189,7 +208,7 @@ fn should_reject_updates_within_challenge_period() { let mut ext = new_test_ext(); ext.execute_with(|| { - set_timestamp(); + set_timestamp(None); let host = Host::::default(); check_challenge_period(&host).unwrap() }) @@ -200,7 +219,7 @@ fn should_reject_messages_for_frozen_state_machines() { let mut ext = new_test_ext(); ext.execute_with(|| { - set_timestamp(); + set_timestamp(None); let host = Host::::default(); frozen_check(&host).unwrap() }) @@ -211,18 +230,165 @@ fn should_reject_expired_check_clients() { let mut ext = new_test_ext(); ext.execute_with(|| { - set_timestamp(); + set_timestamp(None); let host = Host::::default(); check_client_expiry(&host).unwrap() }) } + #[test] -fn should_process_timeouts_correctly() { +fn should_handle_post_request_timeouts_correctly() { let mut ext = new_test_ext(); ext.execute_with(|| { - set_timestamp(); + set_timestamp(None); let host = Host::::default(); timeout_post_processing_check(&host).unwrap() }) } + +fn setup_mock_client(host: &H) -> IntermediateState { + let intermediate_state = IntermediateState { + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: MOCK_CONSENSUS_CLIENT_ID, + }, + height: 3, + }, + commitment: StateCommitment { + timestamp: 1000, + ismp_root: None, + state_root: Default::default(), + }, + }; + + host.store_consensus_state(MOCK_CONSENSUS_CLIENT_ID, vec![]).unwrap(); + host.store_state_machine_commitment(intermediate_state.height, intermediate_state.commitment) + .unwrap(); + host.store_consensus_update_time(MOCK_CONSENSUS_CLIENT_ID, Duration::from_secs(1000)).unwrap(); + intermediate_state +} + +#[test] +fn should_handle_get_request_timeouts_correctly() { + let mut ext = new_test_ext(); + ext.execute_with(|| { + let host = Host::::default(); + let _ = setup_mock_client(&host); + let requests = (0..2) + .into_iter() + .map(|i| { + let msg = IsmpMessage::Get { + dest_chain: StateMachine::Ethereum, + from: vec![0u8; 32], + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: MOCK_CONSENSUS_CLIENT_ID, + }, + height: 2, + }, + timeout_timestamp: 1000, + }; + + as IsmpDispatch>::dispatch_message(msg).unwrap(); + let get = ismp_rs::router::Get { + source_chain: host.host_state_machine(), + dest_chain: StateMachine::Ethereum, + nonce: i, + from: vec![0u8; 32], + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: MOCK_CONSENSUS_CLIENT_ID, + }, + height: 2, + }, + timeout_timestamp: 1000, + }; + ismp_rs::router::Request::Get(get) + }) + .collect::>(); + + let timeout_msg = TimeoutMessage::Get { requests: requests.clone() }; + + set_timestamp(Some(Duration::from_secs(60 * 60 * 60).as_millis() as u64)); + Pallet::::handle_messages(vec![Message::Timeout(timeout_msg)]).unwrap(); + for request in requests { + // commitments should not be found in storage after timeout has been processed + assert!(host.request_commitment(&request).is_err()) + } + }) +} + +#[test] +fn should_handle_get_request_responses_correctly() { + let mut ext = new_test_ext(); + ext.execute_with(|| { + let host = Host::::default(); + let _ = setup_mock_client(&host); + let requests = (0..2) + .into_iter() + .map(|i| { + let msg = IsmpMessage::Get { + dest_chain: StateMachine::Ethereum, + from: vec![0u8; 32], + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: MOCK_CONSENSUS_CLIENT_ID, + }, + height: 2, + }, + timeout_timestamp: 1000, + }; + + as IsmpDispatch>::dispatch_message(msg).unwrap(); + let get = ismp_rs::router::Get { + source_chain: host.host_state_machine(), + dest_chain: StateMachine::Ethereum, + nonce: i, + from: vec![0u8; 32], + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: MOCK_CONSENSUS_CLIENT_ID, + }, + height: 2, + }, + timeout_timestamp: 1000, + }; + ismp_rs::router::Request::Get(get) + }) + .collect::>(); + + set_timestamp(Some(Duration::from_secs(60 * 60 * 60).as_millis() as u64)); + + let response = ResponseMessage::Get { + requests: requests.clone(), + proof: Proof { + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: MOCK_CONSENSUS_CLIENT_ID, + }, + height: 3, + }, + proof: vec![], + }, + }; + + Pallet::::handle_messages(vec![Message::Response(response)]).unwrap(); + + for request in requests { + // commitments should not be found in storage after response has been processed + // successfully + assert!(host.request_commitment(&request).is_err()) + } + }) +} diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index 606bf7dcf..34825ebca 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -1,4 +1,21 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Users of ismp should benchmark consensus clients and module callbacks +//! This module provides a guide on how to provide static weights for consensus clients and module +//! callbacks use crate::Config; use alloc::boxed::Box; @@ -61,9 +78,12 @@ impl IsmpModuleWeight for () { } } +/// Provides references to consensus and module weight providers pub trait WeightProvider { + /// Returns a reference to the weight provider for a consensus client fn consensus_client(id: ConsensusClientId) -> Option>; + /// Returns a reference to the weight provider for a module fn module_callback(dest_module: &[u8]) -> Option>; } @@ -77,15 +97,27 @@ impl WeightProvider for () { } } -/// These functions account fot storage reads and writes in the ismp message handlers +/// These functions account for storage reads and writes in the ismp message handlers +/// They do not take into account proof verification, that is delegated to the Consensus client +/// weight provider pub trait WeightInfo { + /// Returns the weight used in finalizing the mmr + fn on_finalize(n: u32) -> Weight; + /// Returns the weight consumed in creating a consensus client fn create_consensus_client() -> Weight; + /// Returns the weight consumed in handling a request fn handle_request_message() -> Weight; + /// Returns the weight consumed in handling a response fn handle_response_message() -> Weight; + /// Returns the weight consumed in handling a timeout fn handle_timeout_message() -> Weight; } impl WeightInfo for () { + fn on_finalize(_n: u32) -> Weight { + Weight::zero() + } + fn create_consensus_client() -> Weight { Weight::zero() } @@ -103,6 +135,7 @@ impl WeightInfo for () { } } +/// Returns the weight that would be consumed when executing a batch of messages pub fn get_weight(messages: &[Message]) -> Weight { messages.into_iter().fold(Weight::zero(), |acc, msg| { match msg { diff --git a/parachain/inherent/src/lib.rs b/parachain/inherent/src/lib.rs index 89f792e77..443e8292d 100644 --- a/parachain/inherent/src/lib.rs +++ b/parachain/inherent/src/lib.rs @@ -12,6 +12,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![deny(missing_docs)] //! ISMP Parachain Consensus Inherent Provider //! @@ -31,7 +32,8 @@ use std::sync::Arc; pub struct ConsensusInherentProvider(Option); impl ConsensusInherentProvider { - /// Create the [`ConsensusInherentProvider`] at the given `relay_parent`. + /// Create the [`ConsensusMessage`] at the given `relay_parent`. Will be [`None`] if no para ids + /// have been confguired. pub async fn create( client: Arc, relay_parent: PHash, diff --git a/parachain/runtime-api/src/lib.rs b/parachain/runtime-api/src/lib.rs index a3e2ed908..dfb57a03e 100644 --- a/parachain/runtime-api/src/lib.rs +++ b/parachain/runtime-api/src/lib.rs @@ -16,6 +16,8 @@ //! Runtime API for parachains. #![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] + extern crate alloc; use alloc::vec::Vec; diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 5bf10f086..baa334054 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -26,9 +26,10 @@ use ismp::{ StateMachineId, }, error::Error, - host::{ISMPHost, StateMachine}, + host::{IsmpHost, StateMachine}, messaging::Proof, - router::RequestResponse, + router::{Request, RequestResponse}, + util::hash_request, }; use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; use merkle_mountain_range::MerkleProof; @@ -70,7 +71,9 @@ pub struct ParachainConsensusProof { /// Hashing algorithm for the state proof #[derive(Debug, Encode, Decode)] pub enum HashAlgorithm { + /// For chains that use keccak as their hashing algo Keccak, + /// For chains that use blake2 as their hashing algo Blake2, } @@ -112,7 +115,7 @@ where { fn verify_consensus( &self, - host: &dyn ISMPHost, + host: &dyn IsmpHost, state: Vec, proof: Vec, ) -> Result<(Vec, Vec), Error> { @@ -232,7 +235,7 @@ where fn verify_membership( &self, - _host: &dyn ISMPHost, + _host: &dyn IsmpHost, item: RequestResponse, state: StateCommitment, proof: &Proof, @@ -273,13 +276,26 @@ where Ok(()) } - fn state_trie_key(&self, _request: RequestResponse) -> Vec> { - todo!() + fn state_trie_key(&self, requests: Vec) -> Vec> { + let mut keys = vec![]; + + for req in requests { + match req { + Request::Post(post) => { + let request = Request::Post(post); + let commitment = hash_request::>(&request).0.to_vec(); + keys.push(pallet_ismp::RequestAcks::::hashed_key_for(commitment)); + } + Request::Get(_) => continue, + } + } + + keys } fn verify_state_proof( &self, - _host: &dyn ISMPHost, + _host: &dyn IsmpHost, keys: Vec>, root: StateCommitment, proof: &Proof, diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index fc547f302..ac5286f10 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -17,6 +17,7 @@ //! //! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. #![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] extern crate alloc; extern crate core; @@ -44,10 +45,12 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + /// The config trait #[pallet::config] pub trait Config: frame_system::Config + pallet_ismp::Config + parachain_system::Config { + /// The overarching event type type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -61,10 +64,12 @@ pub mod pallet { #[pallet::storage] pub type ConsensusUpdated = StorageValue<_, bool>; - /// Tracks whether we've already seen the `update_parachain_consensus` inherent + /// List of parachains who's headers will be inserted in the `update_parachain_consensus` + /// inherent #[pallet::storage] pub type Parachains = StorageMap<_, Identity, u32, ()>; + /// Events emitted by this pallet #[pallet::event] pub enum Event {} @@ -101,7 +106,7 @@ pub mod pallet { /// Add some new parachains to the list of parachains we care about #[pallet::call_index(1)] - #[pallet::weight(0)] // todo: fix weight + #[pallet::weight(0)] pub fn add_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { ensure_root(origin)?; for id in para_ids { @@ -113,7 +118,7 @@ pub mod pallet { /// Remove some parachains from the list of parachains we care about #[pallet::call_index(2)] - #[pallet::weight(0)] // todo: fix weight + #[pallet::weight(0)] pub fn remove_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { ensure_root(origin)?; for id in para_ids { @@ -173,6 +178,7 @@ pub mod pallet { } } + /// The genesis config #[pallet::genesis_config] pub struct GenesisConfig { /// List of parachains to track at genesis From 3d686558253e5a1e66972b0b354d26b2684acea0 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 26 May 2023 15:40:01 +0100 Subject: [PATCH 128/182] Update ismp implementation (#43) * update ismp implementation * chore * fix faulty host implementation * fix no-std * updated failing tests * some changes --------- Co-authored-by: Seun Lanlege --- Cargo.lock | 4 +- ismp-assets/src/lib.rs | 15 ++- pallet-ismp/src/benchmarking.rs | 96 +++++++++++------- pallet-ismp/src/dispatcher.rs | 135 ++++++++++++++++++++++++++ pallet-ismp/src/host.rs | 78 ++++++++++----- pallet-ismp/src/lib.rs | 89 ++++++----------- pallet-ismp/src/mock.rs | 36 ++----- pallet-ismp/src/primitives.rs | 49 +--------- pallet-ismp/src/router.rs | 167 -------------------------------- pallet-ismp/src/tests.rs | 56 ++++------- pallet-ismp/src/weight_info.rs | 82 +++++++++++++--- parachain/src/consensus.rs | 70 ++++++++----- parachain/src/lib.rs | 47 +++------ 13 files changed, 446 insertions(+), 478 deletions(-) create mode 100644 pallet-ismp/src/dispatcher.rs delete mode 100644 pallet-ismp/src/router.rs diff --git a/Cargo.lock b/Cargo.lock index 883c316ef..97eb4a5e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3004,7 +3004,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#c51850fdd6ff87e8ccf839704343d3c68987742c" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#80acc1a30a89329d36f6ecfa05fb54698ab50035" dependencies = [ "derive_more", "parity-scale-codec", @@ -3125,7 +3125,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#c51850fdd6ff87e8ccf839704343d3c68987742c" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#80acc1a30a89329d36f6ecfa05fb54698ab50035" dependencies = [ "ismp", "parity-scale-codec", diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index ae1286f90..b9ceca5ff 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -42,8 +42,10 @@ pub mod pallet { }, }; use frame_system::pallet_prelude::*; - use ismp::host::StateMachine; - use pallet_ismp::primitives::{IsmpDispatch, IsmpMessage}; + use ismp::{ + host::StateMachine, + router::{DispatchPost, DispatchRequest, IsmpDispatcher}, + }; #[pallet::pallet] pub struct Pallet(_); @@ -58,7 +60,7 @@ pub mod pallet { /// Native currency implementation type NativeCurrency: Mutate; /// Ismp message disptacher - type IsmpDispatch: IsmpDispatch; + type IsmpDispatcher: IsmpDispatcher + Default; } /// Pallet events @@ -111,7 +113,7 @@ pub mod pallet { ) -> DispatchResult { let origin = ensure_signed(origin)?; let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; - let request = IsmpMessage::Post { + let post = DispatchPost { dest_chain: params.dest_chain, from: PALLET_ID.0.to_vec(), to: PALLET_ID.0.to_vec(), @@ -119,7 +121,10 @@ pub mod pallet { data: payload.encode(), }; - T::IsmpDispatch::dispatch_message(request).map_err(|_| Error::::TransferFailed)?; + let dispatcher = T::IsmpDispatcher::default(); + dispatcher + .dispatch_request(DispatchRequest::Post(post)) + .map_err(|_| Error::::TransferFailed)?; >::burn_from(&origin, params.amount.into())?; Self::deposit_event(Event::::BalanceTransferred { from: payload.from, diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index b8ae69c3e..888dbcfda 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -34,16 +34,22 @@ use frame_system::RawOrigin; )] pub mod benchmarks { use super::*; - use crate::router::Receipt; + use crate::dispatcher::Receipt; + use alloc::collections::BTreeMap; use frame_support::{traits::Hooks, PalletId}; use frame_system::EventRecord; use ismp_rs::{ - consensus::{ConsensusClient, IntermediateState, StateCommitment, StateMachineHeight}, + consensus::{ + ConsensusClient, IntermediateState, StateCommitment, StateMachineClient, + StateMachineHeight, + }, error::Error as IsmpError, - messaging::{Message, Proof, RequestMessage, ResponseMessage, TimeoutMessage}, + messaging::{ + Message, Proof, RequestMessage, ResponseMessage, StateCommitmentHeight, TimeoutMessage, + }, module::IsmpModule, - router::{Post, RequestResponse}, - util::hash_request, + router::{Post, PostResponse, RequestResponse}, + util::{hash_request, hash_response}, }; use sp_std::prelude::Vec; @@ -68,14 +74,36 @@ pub mod benchmarks { _host: &dyn IsmpHost, _trusted_consensus_state: Vec, _proof: Vec, - ) -> Result<(Vec, Vec), IsmpError> { + ) -> Result<(Vec, BTreeMap), IsmpError> { Ok(Default::default()) } + fn verify_fraud_proof( + &self, + _host: &dyn IsmpHost, + _trusted_consensus_state: Vec, + _proof_1: Vec, + _proof_2: Vec, + ) -> Result<(), IsmpError> { + Ok(()) + } + fn unbonding_period(&self) -> Duration { Duration::from_secs(60 * 60 * 60) } + fn state_machine( + &self, + _id: StateMachine, + ) -> Result, IsmpError> { + Ok(Box::new(BenchmarkStateMachine)) + } + } + + /// Mock State Machine + pub struct BenchmarkStateMachine; + + impl StateMachineClient for BenchmarkStateMachine { fn verify_membership( &self, _host: &dyn IsmpHost, @@ -99,10 +127,6 @@ pub mod benchmarks { ) -> Result>>, IsmpError> { Ok(Default::default()) } - - fn is_frozen(&self, _trusted_consensus_state: &[u8]) -> Result<(), IsmpError> { - Ok(()) - } } /// This module should be added to the module router in runtime for benchmarks to pass @@ -134,26 +158,24 @@ pub mod benchmarks { #[benchmark] fn create_consensus_client() { set_timestamp::(); - let intermediate_state = IntermediateState { - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Polkadot(1000), - consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID, - }, - height: 1, - }, - - commitment: StateCommitment { - timestamp: 1651280681, - ismp_root: None, - state_root: Default::default(), - }, - }; let message = CreateConsensusClient { consensus_state: Default::default(), consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID, - state_machine_commitments: vec![intermediate_state], + state_machine_commitments: vec![( + StateMachineId { + state_id: StateMachine::Ethereum, + consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID, + }, + StateCommitmentHeight { + commitment: StateCommitment { + timestamp: 1651280681, + overlay_root: None, + state_root: Default::default(), + }, + height: 1, + }, + )], }; #[extrinsic_call] @@ -176,7 +198,7 @@ pub mod benchmarks { }, commitment: StateCommitment { timestamp: 1000, - ismp_root: None, + overlay_root: None, state_root: Default::default(), }, }; @@ -206,7 +228,7 @@ pub mod benchmarks { from: MODULE_ID.0.to_vec(), to: MODULE_ID.0.to_vec(), timeout_timestamp: 5000, - data: vec![], + data: "handle_request_message".as_bytes().to_vec(), }; let msg = RequestMessage { @@ -219,7 +241,7 @@ pub mod benchmarks { handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]); let commitment = hash_request::>(&Request::Post(post)); - assert!(RequestAcks::::get(commitment.0.to_vec()).is_some()); + assert!(IncomingRequestAcks::::get(commitment.0.to_vec()).is_some()); } #[benchmark] @@ -234,15 +256,15 @@ pub mod benchmarks { from: MODULE_ID.0.to_vec(), to: MODULE_ID.0.to_vec(), timeout_timestamp: 5000, - data: vec![], + data: "handle_response_message".as_bytes().to_vec(), }; let request = Request::Post(post.clone()); let commitment = hash_request::>(&request); - RequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); - - let response = Response::Post { post, response: vec![] }; + OutgoingRequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); + let response = Response::Post(PostResponse { post, response: vec![] }); + let response_commitment = hash_response::>(&response); let msg = ResponseMessage::Post { responses: vec![response], proof: Proof { height: intermediate_state.height, proof: vec![] }, @@ -253,7 +275,7 @@ pub mod benchmarks { #[extrinsic_call] handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]); - assert!(RequestAcks::::get(commitment.0.to_vec()).is_none()); + assert!(IncomingResponseAcks::::get(response_commitment.0.to_vec()).is_some()); } #[benchmark] @@ -268,12 +290,12 @@ pub mod benchmarks { from: MODULE_ID.0.to_vec(), to: MODULE_ID.0.to_vec(), timeout_timestamp: 500, - data: vec![], + data: "handle_timeout_message".as_bytes().to_vec(), }; let request = Request::Post(post.clone()); let commitment = hash_request::>(&request); - RequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); + OutgoingRequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); let msg = TimeoutMessage::Post { requests: vec![request], @@ -284,7 +306,7 @@ pub mod benchmarks { #[extrinsic_call] handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]); - assert!(RequestAcks::::get(commitment.0.to_vec()).is_none()); + assert!(OutgoingRequestAcks::::get(commitment.0.to_vec()).is_none()); } #[benchmark] diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs new file mode 100644 index 000000000..5bcf0140f --- /dev/null +++ b/pallet-ismp/src/dispatcher.rs @@ -0,0 +1,135 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation for the ISMP Router +use crate::{host::Host, Config, Event, OutgoingRequestAcks, OutgoingResponseAcks, Pallet}; +use alloc::string::ToString; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use ismp_primitives::mmr::Leaf; +use ismp_rs::{ + host::IsmpHost, + router::{ + DispatchError, DispatchRequest, DispatchResult, DispatchSuccess, Get, IsmpDispatcher, Post, + PostResponse, Request, Response, + }, + util::{hash_request, hash_response}, +}; +use sp_core::H256; + +/// A receipt or an outgoing or incoming request or response +#[derive(Encode, Decode, scale_info::TypeInfo)] +pub enum Receipt { + /// Ok + Ok, +} + +/// The dispatcher commits outgoing requests and responses to the mmr +pub struct Dispatcher(PhantomData); + +impl Default for Dispatcher { + fn default() -> Self { + Self(PhantomData) + } +} + +impl IsmpDispatcher for Dispatcher +where + T: Config, + ::Hash: From, +{ + fn dispatch_request(&self, request: DispatchRequest) -> DispatchResult { + let host = Host::::default(); + let request = match request { + DispatchRequest::Get(dispatch_get) => { + let get = Get { + source_chain: host.host_state_machine(), + dest_chain: dispatch_get.dest_chain, + nonce: host.next_nonce(), + from: dispatch_get.from, + keys: dispatch_get.keys, + height: dispatch_get.height, + timeout_timestamp: dispatch_get.timeout_timestamp, + }; + Request::Get(get) + } + DispatchRequest::Post(dispatch_post) => { + let post = Post { + source_chain: host.host_state_machine(), + dest_chain: dispatch_post.dest_chain, + nonce: host.next_nonce(), + from: dispatch_post.from, + to: dispatch_post.to, + timeout_timestamp: dispatch_post.timeout_timestamp, + data: dispatch_post.data, + }; + Request::Post(post) + } + }; + + let commitment = hash_request::>(&request).0.to_vec(); + + let (dest_chain, source_chain, nonce) = + (request.dest_chain(), request.source_chain(), request.nonce()); + Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError { + msg: "Failed to push request into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })?; + // Deposit Event + Pallet::::deposit_event(Event::Request { + request_nonce: nonce, + source_chain, + dest_chain, + }); + // We need this step since it's not trivial to check the mmr for commitments on chain + OutgoingRequestAcks::::insert(commitment, Receipt::Ok); + Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + } + + fn dispatch_response(&self, response: PostResponse) -> DispatchResult { + let response = Response::Post(response); + + let commitment = hash_response::>(&response).0.to_vec(); + + if OutgoingResponseAcks::::contains_key(commitment.clone()) { + Err(DispatchError { + msg: "Duplicate response".to_string(), + nonce: response.nonce(), + source: response.source_chain(), + dest: response.dest_chain(), + })? + } + + let (dest_chain, source_chain, nonce) = + (response.dest_chain(), response.source_chain(), response.nonce()); + + Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { + msg: "Failed to push response into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })?; + + Pallet::::deposit_event(Event::Response { + request_nonce: nonce, + dest_chain, + source_chain, + }); + OutgoingResponseAcks::::insert(commitment, Receipt::Ok); + Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + } +} diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 5502d199e..a95b16ced 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -15,8 +15,9 @@ //! Host implementation for ISMP use crate::{ - primitives::ConsensusClientProvider, router::Receipt, Config, ConsensusClientUpdateTime, - ConsensusStates, FrozenHeights, LatestStateMachineHeight, RequestAcks, StateCommitments, + dispatcher::Receipt, primitives::ConsensusClientProvider, Config, ConsensusClientUpdateTime, + ConsensusStates, FrozenConsensusClients, FrozenHeights, IncomingRequestAcks, + IncomingResponseAcks, LatestStateMachineHeight, Nonce, OutgoingRequestAcks, StateCommitments, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -27,8 +28,8 @@ use ismp_rs::{ }, error::Error, host::{IsmpHost, StateMachine}, - router::{IsmpRouter, Request}, - util::hash_request, + router::{IsmpRouter, Request, Response}, + util::{hash_request, hash_response}, }; use sp_core::H256; use sp_runtime::SaturatedConversion; @@ -52,12 +53,8 @@ where T::StateMachine::get() } - fn latest_commitment_height(&self, id: StateMachineId) -> Result { - LatestStateMachineHeight::::get(id) - .map(|height| StateMachineHeight { id, height }) - .ok_or_else(|| { - Error::ImplementationSpecific("Missing latest state machine height".to_string()) - }) + fn latest_commitment_height(&self, id: StateMachineId) -> Result { + Ok(LatestStateMachineHeight::::get(id)) } fn state_machine_commitment( @@ -83,18 +80,10 @@ where ::now() } - fn is_frozen(&self, height: StateMachineHeight) -> Result { - if let Some(frozen_height) = FrozenHeights::::get(height.id) { - Ok(height.height >= frozen_height) - } else { - Ok(false) - } - } - fn request_commitment(&self, req: &Request) -> Result { let commitment = hash_request::(req); - let _ = RequestAcks::::get(commitment.0.to_vec()).ok_or_else(|| { + let _ = OutgoingRequestAcks::::get(commitment.0.to_vec()).ok_or_else(|| { Error::RequestCommitmentNotFound { nonce: req.nonce(), source: req.source_chain(), @@ -105,10 +94,10 @@ where Ok(commitment) } - fn get_request_receipt(&self, req: &Request) -> Option<()> { + fn request_receipt(&self, req: &Request) -> Option<()> { let commitment = hash_request::(req); - let _ = RequestAcks::::get(commitment.0.to_vec()) + let _ = IncomingRequestAcks::::get(commitment.0.to_vec()) .ok_or_else(|| Error::RequestCommitmentNotFound { nonce: req.nonce(), source: req.source_chain(), @@ -155,13 +144,13 @@ where fn delete_request_commitment(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); // We can't delete actual leaves in the mmr so this serves as a replacement for that - RequestAcks::::remove(hash.0.to_vec()); + OutgoingRequestAcks::::remove(hash.0.to_vec()); Ok(()) } fn store_request_receipt(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); - RequestAcks::::insert(hash.0.to_vec(), Receipt::Ok); + IncomingRequestAcks::::insert(hash.0.to_vec(), Receipt::Ok); Ok(()) } @@ -183,4 +172,47 @@ where fn ismp_router(&self) -> Box { Box::new(T::IsmpRouter::default()) } + + fn is_state_machine_frozen(&self, machine: StateMachineHeight) -> Result<(), Error> { + if let Some(frozen_height) = FrozenHeights::::get(machine.id) { + if machine.height >= frozen_height { + Err(Error::FrozenStateMachine { height: machine })? + } + } + Ok(()) + } + + fn is_consensus_client_frozen(&self, client: ConsensusClientId) -> Result<(), Error> { + if FrozenConsensusClients::::get(client) { + Err(Error::FrozenConsensusClient { id: client })? + } + Ok(()) + } + + fn next_nonce(&self) -> u64 { + let nonce = Nonce::::get(); + Nonce::::put(nonce + 1); + nonce + } + + fn response_receipt(&self, res: &Response) -> Option<()> { + let commitment = hash_response::(res); + + let _ = IncomingResponseAcks::::get(commitment.0.to_vec()) + .ok_or_else(|| Error::ImplementationSpecific("Response receipt not found".to_string())) + .ok()?; + + Some(()) + } + + fn freeze_consensus_client(&self, client: ConsensusClientId) -> Result<(), Error> { + FrozenConsensusClients::::insert(client, true); + Ok(()) + } + + fn store_response_receipt(&self, res: &Response) -> Result<(), Error> { + let hash = hash_response::(res); + IncomingResponseAcks::::insert(hash.0.to_vec(), Receipt::Ok); + Ok(()) + } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index f2d256c0e..8e0650748 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -22,6 +22,7 @@ extern crate alloc; pub mod benchmarking; +pub mod dispatcher; mod errors; pub mod events; pub mod host; @@ -29,7 +30,6 @@ mod mmr; #[cfg(test)] pub mod mock; pub mod primitives; -pub mod router; #[cfg(test)] pub mod tests; pub mod weight_info; @@ -49,16 +49,12 @@ use ismp_rs::{ }; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. -use crate::{ - errors::HandlingError, - mmr::mmr::Mmr, - primitives::{IsmpDispatch, IsmpMessage}, -}; +use crate::{errors::HandlingError, mmr::mmr::Mmr}; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; -use ismp_rs::{host::IsmpHost, messaging::Message, router::IsmpRouter}; +use ismp_rs::{host::IsmpHost, messaging::Message}; pub use pallet::*; use sp_std::prelude::*; @@ -70,9 +66,9 @@ pub mod pallet { // Import various types used to declare pallet in scope. use super::*; use crate::{ + dispatcher::Receipt, errors::HandlingError, primitives::{ConsensusClientProvider, ISMP_ID}, - router::Receipt, weight_info::{WeightInfo, WeightProvider}, }; use alloc::collections::BTreeSet; @@ -171,11 +167,18 @@ pub mod pallet { pub type FrozenHeights = StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + /// Holds a map of consensus clients frozen due to byzantine + /// behaviour + #[pallet::storage] + #[pallet::getter(fn frozen_consensus_clients)] + pub type FrozenConsensusClients = + StorageMap<_, Blake2_128Concat, ConsensusClientId, bool, ValueQuery>; + /// The latest verified height for a state machine #[pallet::storage] #[pallet::getter(fn latest_state_height)] pub type LatestStateMachineHeight = - StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + StorageMap<_, Blake2_128Concat, StateMachineId, u64, ValueQuery>; /// Holds the timestamp at which a consensus client was recently updated. /// Used in ensuring that the configured challenge period elapses. @@ -184,18 +187,32 @@ pub mod pallet { pub type ConsensusClientUpdateTime = StorageMap<_, Twox64Concat, ConsensusClientId, u64, OptionQuery>; - /// Acknowledgements for incoming and outgoing requests + /// Acknowledgements for outgoing requests + /// The key is the request commitment + #[pallet::storage] + #[pallet::getter(fn outgoing_request_acks)] + pub type OutgoingRequestAcks = + StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; + + /// Acknowledgements for outgoing responses + /// The key is the response commitment + #[pallet::storage] + #[pallet::getter(fn outgoing_response_acks)] + pub type OutgoingResponseAcks = + StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; + + /// Acknowledgements for incoming requests /// The key is the request commitment #[pallet::storage] #[pallet::getter(fn request_acks)] - pub type RequestAcks = + pub type IncomingRequestAcks = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; - /// Acknowledgements for incoming and outgoing responses + /// Acknowledgements for incoming responses /// The key is the response commitment #[pallet::storage] #[pallet::getter(fn response_acks)] - pub type ResponseAcks = + pub type IncomingResponseAcks = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; /// Consensus update results still in challenge period @@ -279,7 +296,7 @@ pub mod pallet { T::AdminOrigin::ensure_origin(origin)?; let host = Host::::default(); - let result = handlers::create_consensus_client(&host, message) + let result = handlers::create_client(&host, message) .map_err(|_| Error::::ConsensusClientCreationFailed)?; Self::deposit_event(Event::::ConsensusClientCreated { @@ -535,7 +552,7 @@ where /// Return the latest height of the state machine pub fn get_latest_state_machine_height(id: StateMachineId) -> Option { - LatestStateMachineHeight::::get(id) + Some(LatestStateMachineHeight::::get(id)) } /// Get Request Leaf Indices @@ -620,46 +637,4 @@ impl Pallet { fn offchain_key(pos: NodeIndex) -> Vec { (T::INDEXING_PREFIX, "leaves", pos).encode() } - - /// Returns the next available nonce - fn next_nonce() -> u64 { - let nonce = Nonce::::get(); - Nonce::::put(nonce + 1); - nonce - } -} - -impl IsmpDispatch for Pallet { - fn dispatch_message(msg: IsmpMessage) -> Result<(), ismp_rs::router::DispatchError> { - let router = T::IsmpRouter::default(); - match msg { - IsmpMessage::Post { timeout_timestamp, dest_chain, data, from, to } => { - let post = ismp_rs::router::Post { - source_chain: T::StateMachine::get(), - dest_chain, - nonce: Pallet::::next_nonce(), - from, - to, - timeout_timestamp, - data, - }; - router.dispatch(Request::Post(post)).map(|_| ()) - } - IsmpMessage::Get { timeout_timestamp, dest_chain, keys, height, from } => { - let get = ismp_rs::router::Get { - source_chain: T::StateMachine::get(), - dest_chain, - nonce: Pallet::::next_nonce(), - from, - keys, - height, - timeout_timestamp, - }; - router.dispatch(Request::Get(get)).map(|_| ()) - } - IsmpMessage::Response { response, post } => { - router.write_response(Response::Post { post, response }).map(|_| ()) - } - } - } } diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index 181722505..bd969a9a5 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -16,12 +16,12 @@ use crate as pallet_ismp; use crate::*; -use crate::{primitives::ConsensusClientProvider, router::ProxyRouter}; +use crate::primitives::ConsensusClientProvider; use frame_support::traits::{ConstU32, ConstU64, Get}; use frame_system::EnsureRoot; use ismp_rs::{ consensus::ConsensusClient, - router::{DispatchResult, DispatchSuccess}, + router::{DispatchResult, DispatchSuccess, IsmpRouter}, }; use sp_core::H256; use sp_runtime::{ @@ -106,7 +106,7 @@ impl Config for Test { type AdminOrigin = EnsureRoot; type StateMachine = StateMachineProvider; type TimeProvider = Timestamp; - type IsmpRouter = Router; + type IsmpRouter = ModuleRouter; type ConsensusClientProvider = ConsensusProvider; type WeightInfo = (); type WeightProvider = (); @@ -116,7 +116,7 @@ impl Config for Test { pub struct ModuleRouter; impl IsmpRouter for ModuleRouter { - fn dispatch(&self, request: Request) -> DispatchResult { + fn handle_request(&self, request: Request) -> DispatchResult { let dest = request.dest_chain(); let source = request.source_chain(); let nonce = request.nonce(); @@ -124,14 +124,14 @@ impl IsmpRouter for ModuleRouter { Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) } - fn dispatch_timeout(&self, request: Request) -> DispatchResult { + fn handle_timeout(&self, request: Request) -> DispatchResult { let dest = request.dest_chain(); let source = request.source_chain(); let nonce = request.nonce(); Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) } - fn write_response(&self, response: Response) -> DispatchResult { + fn handle_response(&self, response: Response) -> DispatchResult { let request = &response.request(); let dest = request.dest_chain(); let source = request.source_chain(); @@ -139,27 +139,3 @@ impl IsmpRouter for ModuleRouter { Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) } } - -pub struct Router { - inner: ProxyRouter, -} - -impl Default for Router { - fn default() -> Self { - Self { inner: ProxyRouter::::new(ModuleRouter::default()) } - } -} - -impl IsmpRouter for Router { - fn dispatch(&self, request: Request) -> DispatchResult { - self.inner.dispatch(request) - } - - fn dispatch_timeout(&self, request: Request) -> DispatchResult { - self.inner.dispatch_timeout(request) - } - - fn write_response(&self, response: Response) -> DispatchResult { - self.inner.write_response(response) - } -} diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index fd3f3917d..903900143 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -17,11 +17,7 @@ use core::time::Duration; use frame_support::RuntimeDebug; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; -use ismp_rs::{ - consensus::{ConsensusClient, ConsensusClientId, StateMachineHeight}, - host::StateMachine, - router::Post, -}; +use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; use scale_info::TypeInfo; use sp_std::prelude::*; @@ -66,46 +62,3 @@ pub trait ConsensusClientProvider { /// Returns the challenge period configured for a consensus client fn challenge_period(id: ConsensusClientId) -> Duration; } - -/// An internal message type for pallet ISMP -pub enum IsmpMessage { - /// A post request - Post { - /// The destination state machine of this request. - dest_chain: StateMachine, - /// Module Id of the sending module - from: Vec, - /// Module ID of the receiving module - to: Vec, - /// Timestamp which this request expires in seconds. - timeout_timestamp: u64, - /// Encoded Request. - data: Vec, - }, - /// A get request - Get { - /// The destination state machine of this request. - dest_chain: StateMachine, - /// Module Id of the sending module - from: Vec, - /// Raw Storage keys that this request is interested in. - keys: Vec>, - /// Height at which to read the state machine. - height: StateMachineHeight, - /// Host Timestamp which this request expires in seconds - timeout_timestamp: u64, - }, - /// A response - Response { - /// Post request - post: Post, - /// Opaque response bytes - response: Vec, - }, -} - -/// A trait that exposes an interface for modules to dispatch ismp messages to the router -pub trait IsmpDispatch { - /// Dispatch an ismp message to the router - fn dispatch_message(msg: IsmpMessage) -> Result<(), ismp_rs::router::DispatchError>; -} diff --git a/pallet-ismp/src/router.rs b/pallet-ismp/src/router.rs deleted file mode 100644 index 97b8a2c98..000000000 --- a/pallet-ismp/src/router.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementation for the ISMP Router -use crate::{host::Host, Config, Event, Pallet, RequestAcks, ResponseAcks}; -use alloc::{boxed::Box, string::ToString}; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use ismp_primitives::mmr::Leaf; -use ismp_rs::{ - host::IsmpHost, - router::{DispatchError, DispatchResult, DispatchSuccess, IsmpRouter, Request, Response}, - util::{hash_request, hash_response}, -}; -use sp_core::H256; - -/// A receipt or an outgoing or incoming request or response -#[derive(Encode, Decode, scale_info::TypeInfo)] -pub enum Receipt { - /// Ok - Ok, -} - -/// The proxy router, This router allows for routing requests & responses from a source chain -/// to a destination chain. -pub struct ProxyRouter { - /// Module router - inner: Option>, - /// Phantom - _phantom: PhantomData, -} - -impl ProxyRouter { - /// Initialize the proxy router with an inner router. - pub fn new(router: R) -> Self - where - R: IsmpRouter + 'static, - { - Self { inner: Some(Box::new(router)), _phantom: PhantomData } - } -} - -impl Default for ProxyRouter { - fn default() -> Self { - Self { inner: None, _phantom: PhantomData } - } -} - -impl IsmpRouter for ProxyRouter -where - T: Config, - ::Hash: From, -{ - fn dispatch(&self, request: Request) -> DispatchResult { - let host = Host::::default(); - - if host.host_state_machine() != request.dest_chain() { - let commitment = hash_request::>(&request).0.to_vec(); - - if RequestAcks::::contains_key(commitment.clone()) { - Err(DispatchError { - msg: "Duplicate request".to_string(), - nonce: request.nonce(), - source: request.source_chain(), - dest: request.dest_chain(), - })? - } - - let (dest_chain, source_chain, nonce) = - (request.dest_chain(), request.source_chain(), request.nonce()); - Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError { - msg: "Failed to push request into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, - })?; - // Deposit Event - Pallet::::deposit_event(Event::Request { - request_nonce: nonce, - source_chain, - dest_chain, - }); - // We have this step because we can't delete leaves from the mmr - // So this helps us prevent processing of duplicate outgoing requests - RequestAcks::::insert(commitment, Receipt::Ok); - Ok(DispatchSuccess { dest_chain, source_chain, nonce }) - } else if let Some(ref router) = self.inner { - router.dispatch(request) - } else { - Err(DispatchError { - msg: "Missing a module router".to_string(), - nonce: request.nonce(), - source: request.source_chain(), - dest: request.dest_chain(), - })? - } - } - - fn dispatch_timeout(&self, request: Request) -> DispatchResult { - if let Some(ref router) = self.inner { - router.dispatch(request) - } else { - Err(DispatchError { - msg: "Missing a module router".to_string(), - nonce: request.nonce(), - source: request.source_chain(), - dest: request.dest_chain(), - })? - } - } - - fn write_response(&self, response: Response) -> DispatchResult { - let host = Host::::default(); - - if host.host_state_machine() != response.dest_chain() { - let commitment = hash_response::>(&response).0.to_vec(); - - if ResponseAcks::::contains_key(commitment.clone()) { - Err(DispatchError { - msg: "Duplicate response".to_string(), - nonce: response.nonce(), - source: response.source_chain(), - dest: response.dest_chain(), - })? - } - - let (dest_chain, source_chain, nonce) = - (response.dest_chain(), response.source_chain(), response.nonce()); - - Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { - msg: "Failed to push response into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, - })?; - - Pallet::::deposit_event(Event::Response { - request_nonce: nonce, - dest_chain, - source_chain, - }); - ResponseAcks::::insert(commitment, Receipt::Ok); - Ok(DispatchSuccess { dest_chain, source_chain, nonce }) - } else if let Some(ref router) = self.inner { - router.write_response(response) - } else { - Err(DispatchError { - msg: "Missing a module router".to_string(), - nonce: response.nonce(), - source: response.source_chain(), - dest: response.dest_chain(), - })? - } - } -} diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index bbf38e462..b05d85023 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -19,11 +19,13 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +use crate::dispatcher::Dispatcher; use frame_support::traits::OnFinalize; use ismp_primitives::mmr::MmrHasher; use ismp_rs::{ consensus::{IntermediateState, StateCommitment, StateMachineHeight}, messaging::{Proof, ResponseMessage, TimeoutMessage}, + router::{DispatchGet, DispatchRequest, IsmpDispatcher}, }; use ismp_testsuite::{ check_challenge_period, check_client_expiry, frozen_check, mocks::MOCK_CONSENSUS_CLIENT_ID, @@ -193,13 +195,14 @@ fn set_timestamp(now: Option) { } #[test] -fn check_for_duplicate_requests_and_responses() { +fn dispatcher_should_write_receipts_for_outgoing_requests_and_responses() { let mut ext = new_test_ext(); ext.execute_with(|| { set_timestamp(None); let host = Host::::default(); - write_outgoing_commitments(&host).unwrap(); + let dispatcher = Dispatcher::::default(); + write_outgoing_commitments(&host, &dispatcher).unwrap(); }) } @@ -243,7 +246,8 @@ fn should_handle_post_request_timeouts_correctly() { ext.execute_with(|| { set_timestamp(None); let host = Host::::default(); - timeout_post_processing_check(&host).unwrap() + let dispatcher = Dispatcher::::default(); + timeout_post_processing_check(&host, &dispatcher).unwrap() }) } @@ -258,7 +262,7 @@ fn setup_mock_client(host: &H) -> IntermediateState { }, commitment: StateCommitment { timestamp: 1000, - ismp_root: None, + overlay_root: None, state_root: Default::default(), }, }; @@ -279,34 +283,23 @@ fn should_handle_get_request_timeouts_correctly() { let requests = (0..2) .into_iter() .map(|i| { - let msg = IsmpMessage::Get { + let msg = DispatchGet { dest_chain: StateMachine::Ethereum, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: MOCK_CONSENSUS_CLIENT_ID, - }, - height: 2, - }, + height: 2, timeout_timestamp: 1000, }; - as IsmpDispatch>::dispatch_message(msg).unwrap(); + let dispatcher = Dispatcher::::default(); + dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); let get = ismp_rs::router::Get { source_chain: host.host_state_machine(), dest_chain: StateMachine::Ethereum, nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: MOCK_CONSENSUS_CLIENT_ID, - }, - height: 2, - }, + height: 2, timeout_timestamp: 1000, }; ismp_rs::router::Request::Get(get) @@ -333,34 +326,23 @@ fn should_handle_get_request_responses_correctly() { let requests = (0..2) .into_iter() .map(|i| { - let msg = IsmpMessage::Get { + let msg = DispatchGet { dest_chain: StateMachine::Ethereum, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: MOCK_CONSENSUS_CLIENT_ID, - }, - height: 2, - }, + height: 2, timeout_timestamp: 1000, }; - as IsmpDispatch>::dispatch_message(msg).unwrap(); + let dispatcher = Dispatcher::::default(); + dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); let get = ismp_rs::router::Get { source_chain: host.host_state_machine(), dest_chain: StateMachine::Ethereum, nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: MOCK_CONSENSUS_CLIENT_ID, - }, - height: 2, - }, + height: 2, timeout_timestamp: 1000, }; ismp_rs::router::Request::Get(get) @@ -388,7 +370,7 @@ fn should_handle_get_request_responses_correctly() { for request in requests { // commitments should not be found in storage after response has been processed // successfully - assert!(host.request_commitment(&request).is_err()) + assert!(host.request_receipt(&request).is_none()) } }) } diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index 34825ebca..89fd57bcd 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -21,35 +21,63 @@ use crate::Config; use alloc::boxed::Box; use frame_support::weights::Weight; use ismp_rs::{ - consensus::ConsensusClientId, - messaging::{ConsensusMessage, Message, Proof, ResponseMessage, TimeoutMessage}, + consensus::{ConsensusClientId, StateMachineId}, + messaging::{ + ConsensusMessage, FraudProofMessage, Message, Proof, ResponseMessage, TimeoutMessage, + }, router::{Request, Response}, }; /// A trait that provides information about how consensus client execute in the runtime pub trait ConsensusClientWeight { /// Returns the weight that would be used in processing this consensus message - fn verify_consensus(&self, msg: ConsensusMessage) -> Weight; + fn verify_consensus(&self, msg: &ConsensusMessage) -> Weight; + /// Returns the weight that would be used in processing this fraud proof message + fn verify_fraud_proof(&self, msg: &FraudProofMessage) -> Weight; /// Returns weight used in verifying this membership proof /// `items` is the number of values being verified /// The weight should ideally depend on the number of items being verified - fn verify_membership(&self, items: usize, proof: &Proof) -> Weight; + fn verify_membership( + &self, + state_machine: StateMachineId, + items: usize, + proof: &Proof, + ) -> Weight; /// Returns weight used in verifying this state proof /// `items` is the number of keys being verified /// The weight should ideally depend on the number of items being verified - fn verify_state_proof(&self, items: usize, proof: &Proof) -> Weight; + fn verify_state_proof( + &self, + state_machine: StateMachineId, + items: usize, + proof: &Proof, + ) -> Weight; } impl ConsensusClientWeight for () { - fn verify_consensus(&self, _msg: ConsensusMessage) -> Weight { + fn verify_consensus(&self, _msg: &ConsensusMessage) -> Weight { Weight::zero() } - fn verify_membership(&self, _items: usize, _proof: &Proof) -> Weight { + fn verify_fraud_proof(&self, _msg: &FraudProofMessage) -> Weight { Weight::zero() } - fn verify_state_proof(&self, _items: usize, _proof: &Proof) -> Weight { + fn verify_membership( + &self, + _state_machine: StateMachineId, + _items: usize, + _proof: &Proof, + ) -> Weight { + Weight::zero() + } + + fn verify_state_proof( + &self, + _state_machine: StateMachineId, + _items: usize, + _proof: &Proof, + ) -> Weight { Weight::zero() } } @@ -139,8 +167,14 @@ impl WeightInfo for () { pub fn get_weight(messages: &[Message]) -> Weight { messages.into_iter().fold(Weight::zero(), |acc, msg| { match msg { - Message::Consensus(_) => acc + ::WeightInfo::create_consensus_client(), + Message::Consensus(msg) => { + let consensus_handler = + ::WeightProvider::consensus_client(msg.consensus_client_id) + .unwrap_or(Box::new(())); + consensus_handler.verify_consensus(msg) + } Message::Request(msg) => { + let state_machine = msg.proof.height.id; let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { Request::Post(ref post) => post.to.as_slice(), @@ -157,8 +191,11 @@ pub fn get_weight(messages: &[Message]) -> Weight { ) .unwrap_or(Box::new(())); - let proof_verification_weight = - consensus_handler.verify_membership(msg.requests.len(), &msg.proof); + let proof_verification_weight = consensus_handler.verify_membership( + state_machine, + msg.requests.len(), + &msg.proof, + ); acc + cb_weight + proof_verification_weight + @@ -166,9 +203,10 @@ pub fn get_weight(messages: &[Message]) -> Weight { } Message::Response(msg) => match msg { ResponseMessage::Post { responses, proof } => { + let state_machine = proof.height.id; let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { let dest_module = match res { - Response::Post { ref post, .. } => post.from.as_slice(), + Response::Post(ref post) => post.post.from.as_slice(), _ => return acc, }; let handle = ::WeightProvider::module_callback(dest_module) @@ -182,13 +220,14 @@ pub fn get_weight(messages: &[Message]) -> Weight { .unwrap_or(Box::new(())); let proof_verification_weight = - consensus_handler.verify_membership(responses.len(), &proof); + consensus_handler.verify_membership(state_machine, responses.len(), &proof); acc + cb_weight + proof_verification_weight + ::WeightInfo::handle_response_message() } ResponseMessage::Get { requests, proof } => { + let state_machine = proof.height.id; let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { Request::Get(ref get) => get.from.as_slice(), @@ -208,7 +247,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { .unwrap_or(Box::new(())); let proof_verification_weight = - consensus_handler.verify_state_proof(requests.len(), &proof); + consensus_handler.verify_state_proof(state_machine, requests.len(), &proof); acc + cb_weight + proof_verification_weight + @@ -217,6 +256,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { }, Message::Timeout(msg) => match msg { TimeoutMessage::Post { requests, timeout_proof } => { + let state_machine = timeout_proof.height.id; let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { Request::Post(ref post) => post.from.as_slice(), @@ -232,8 +272,11 @@ pub fn get_weight(messages: &[Message]) -> Weight { ) .unwrap_or(Box::new(())); - let proof_verification_weight = - consensus_handler.verify_state_proof(requests.len(), &timeout_proof); + let proof_verification_weight = consensus_handler.verify_state_proof( + state_machine, + requests.len(), + &timeout_proof, + ); acc + cb_weight + proof_verification_weight + @@ -252,6 +295,13 @@ pub fn get_weight(messages: &[Message]) -> Weight { acc + cb_weight + ::WeightInfo::handle_timeout_message() } }, + + Message::FraudProof(msg) => { + let consensus_handler = + ::WeightProvider::consensus_client(msg.consensus_client_id) + .unwrap_or(Box::new(())); + consensus_handler.verify_fraud_proof(msg) + } } }) } diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index baa334054..2244342a5 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -17,17 +17,14 @@ use core::{marker::PhantomData, time::Duration}; -use alloc::{collections::BTreeMap, format, vec, vec::Vec}; +use alloc::{boxed::Box, collections::BTreeMap, format, vec, vec::Vec}; use codec::{Decode, Encode}; use core::fmt::Debug; use ismp::{ - consensus::{ - ConsensusClient, ConsensusClientId, IntermediateState, StateCommitment, StateMachineHeight, - StateMachineId, - }, + consensus::{ConsensusClient, ConsensusClientId, StateCommitment, StateMachineClient}, error::Error, host::{IsmpHost, StateMachine}, - messaging::Proof, + messaging::{Proof, StateCommitmentHeight}, router::{Request, RequestResponse}, util::hash_request, }; @@ -50,12 +47,21 @@ use crate::RelayChainOracle; /// The parachain consensus client implementation for ISMP. pub struct ParachainConsensusClient(PhantomData<(T, R)>); +/// The parachain state machine implementation for ISMP. +pub struct ParachainStateMachine(PhantomData); + impl Default for ParachainConsensusClient { fn default() -> Self { Self(PhantomData) } } +impl Default for ParachainStateMachine { + fn default() -> Self { + Self(PhantomData) + } +} + /// Information necessary to prove the sibling parachain's finalization to this /// parachain. #[derive(Debug, Encode, Decode)] @@ -118,7 +124,7 @@ where host: &dyn IsmpHost, state: Vec, proof: Vec, - ) -> Result<(Vec, Vec), Error> { + ) -> Result<(Vec, BTreeMap), Error> { let update: ParachainConsensusProof = codec::Decode::decode(&mut &proof[..]).map_err(|e| { Error::ImplementationSpecific(format!( @@ -147,7 +153,7 @@ where })?; let storage_proof = StorageProof::new(update.storage_proof); - let mut intermediates = vec![]; + let mut intermediates = BTreeMap::new(); let keys = update.para_ids.iter().map(|id| parachain_header_storage_key(*id).0); let headers = @@ -169,7 +175,7 @@ where Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) })?; - let (mut timestamp, mut ismp_root) = (0, H256::default()); + let (mut timestamp, mut overlay_root) = (0, H256::default()); for digest in header.digest().logs.iter() { match digest { DigestItem::PreRuntime(consensus_engine_id, value) @@ -189,7 +195,7 @@ where ))? } - ismp_root = H256::from_slice(&value); + overlay_root = H256::from_slice(&value); } // don't really care about the rest _ => {} @@ -210,19 +216,16 @@ where ))?, }; - let intermediate = IntermediateState { - height: StateMachineHeight { - id: StateMachineId { state_id, consensus_client: PARACHAIN_CONSENSUS_ID }, - height: height as u64, - }, + let intermediate = StateCommitmentHeight { commitment: StateCommitment { timestamp, - ismp_root: Some(ismp_root), - state_root: H256::from_slice(header.state_root().as_ref()), + overlay_root: Some(overlay_root), + state_root: header.state_root, }, + height: height.into(), }; - intermediates.push(intermediate); + intermediates.insert(state_id, intermediate); } Ok((state, intermediates)) @@ -233,6 +236,28 @@ where Duration::from_secs(u64::MAX) } + fn verify_fraud_proof( + &self, + _host: &dyn IsmpHost, + _trusted_consensus_state: Vec, + _proof_1: Vec, + _proof_2: Vec, + ) -> Result<(), Error> { + // There are no fraud proofs for the parachain client + Ok(()) + } + + fn state_machine(&self, _id: StateMachine) -> Result, Error> { + Ok(Box::new(ParachainStateMachine::::default())) + } +} + +impl StateMachineClient for ParachainStateMachine +where + T: pallet_ismp::Config + super::Config, + T::BlockNumber: Into, + T::Hash: From, +{ fn verify_membership( &self, _host: &dyn IsmpHost, @@ -261,7 +286,7 @@ where .collect(), }; let root = state - .ismp_root + .overlay_root .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; let calc_root = proof @@ -284,7 +309,7 @@ where Request::Post(post) => { let request = Request::Post(post); let commitment = hash_request::>(&request).0.to_vec(); - keys.push(pallet_ismp::RequestAcks::::hashed_key_for(commitment)); + keys.push(pallet_ismp::OutgoingRequestAcks::::hashed_key_for(commitment)); } Request::Get(_) => continue, } @@ -337,11 +362,6 @@ where Ok(data) } - - fn is_frozen(&self, _: &[u8]) -> Result<(), Error> { - // parachain consensus client can never be frozen. - Ok(()) - } } /// This returns the storage key for a parachain header on the relay chain. diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index ac5286f10..7110b105c 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -26,7 +26,9 @@ pub mod consensus; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::relay_chain; +use ismp::{handlers, messaging::CreateConsensusClient}; pub use pallet::*; +use pallet_ismp::host::Host; #[frame_support::pallet] pub mod pallet { @@ -34,11 +36,7 @@ pub mod pallet { use cumulus_primitives_core::relay_chain; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use ismp::{ - consensus::StateMachineId, - host::StateMachine, - messaging::{ConsensusMessage, Message}, - }; + use ismp::messaging::{ConsensusMessage, Message}; use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use primitive_types::H256; @@ -193,38 +191,25 @@ pub mod pallet { } #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { + impl GenesisBuild for GenesisConfig + where + ::Hash: From, + { fn build(&self) { - // insert empty bytes - pallet_ismp::ConsensusStates::::insert( - consensus::PARACHAIN_CONSENSUS_ID, - Vec::::new(), - ); + let host = Host::::default(); - pallet_ismp::ConsensusClientUpdateTime::::insert( - consensus::PARACHAIN_CONSENSUS_ID, - // parachains have no challenge period - 0, - ); + let message = CreateConsensusClient { + // insert empty bytes + consensus_state: vec![], + consensus_client_id: consensus::PARACHAIN_CONSENSUS_ID, + state_machine_commitments: vec![], + }; + handlers::create_client(&host, message) + .expect("Failed to initialize parachain consensus client"); // insert the parachain ids for id in &self.parachains { Parachains::::insert(id, ()); - - let state_id = match T::StateMachine::get() { - StateMachine::Polkadot(_) => StateMachine::Polkadot(*id), - StateMachine::Kusama(_) => StateMachine::Kusama(*id), - _ => panic!("State machine should be configured as a parachain!"), - }; - - // insert the "latest" parachain height - pallet_ismp::LatestStateMachineHeight::::insert( - StateMachineId { - consensus_client: consensus::PARACHAIN_CONSENSUS_ID, - state_id, - }, - 0, - ); } } } From 17fe7dff612d20cd5adbb55fabcd182cad8d2fdb Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 29 May 2023 13:49:18 +0100 Subject: [PATCH 129/182] Ismp Update (#44) --- Cargo.lock | 4 ++-- pallet-ismp/src/benchmarking.rs | 2 +- pallet-ismp/src/tests.rs | 4 +--- pallet-ismp/src/weight_info.rs | 6 +++--- parachain/src/consensus.rs | 16 +++++++++------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97eb4a5e8..811b0845c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3004,7 +3004,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#80acc1a30a89329d36f6ecfa05fb54698ab50035" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" dependencies = [ "derive_more", "parity-scale-codec", @@ -3125,7 +3125,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#80acc1a30a89329d36f6ecfa05fb54698ab50035" +source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" dependencies = [ "ismp", "parity-scale-codec", diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 888dbcfda..c9d9fbd5e 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -124,7 +124,7 @@ pub mod benchmarks { _keys: Vec>, _root: StateCommitment, _proof: &Proof, - ) -> Result>>, IsmpError> { + ) -> Result, Option>>, IsmpError> { Ok(Default::default()) } } diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index b05d85023..bf7333140 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -368,9 +368,7 @@ fn should_handle_get_request_responses_correctly() { Pallet::::handle_messages(vec![Message::Response(response)]).unwrap(); for request in requests { - // commitments should not be found in storage after response has been processed - // successfully - assert!(host.request_receipt(&request).is_none()) + assert!(host.request_receipt(&request).is_some()) } }) } diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index 89fd57bcd..5925870d7 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -25,7 +25,7 @@ use ismp_rs::{ messaging::{ ConsensusMessage, FraudProofMessage, Message, Proof, ResponseMessage, TimeoutMessage, }, - router::{Request, Response}, + router::{GetResponse, Request, Response}, }; /// A trait that provides information about how consensus client execute in the runtime @@ -235,10 +235,10 @@ pub fn get_weight(messages: &[Message]) -> Weight { }; let handle = ::WeightProvider::module_callback(dest_module) .unwrap_or(Box::new(())); - acc + handle.on_response(&Response::Get { + acc + handle.on_response(&Response::Get(GetResponse { get: req.get_request().unwrap(), values: Default::default(), - }) + })) }); let consensus_handler = ::WeightProvider::consensus_client( diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 2244342a5..df4716c70 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -324,7 +324,7 @@ where keys: Vec>, root: StateCommitment, proof: &Proof, - ) -> Result>>, Error> { + ) -> Result, Option>>, Error> { let state_proof: ParachainStateProof = codec::Decode::decode(&mut &*proof.proof) .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; @@ -334,13 +334,14 @@ where let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); keys.into_iter() .map(|key| { - trie.get(&key).map_err(|e| { + let value = trie.get(&key).map_err(|e| { Error::ImplementationSpecific(format!( "Error reading state proof: {e:?}" )) - }) + })?; + Ok((key, value)) }) - .collect::, _>>()? + .collect::, _>>()? } HashAlgorithm::Blake2 => { let db = @@ -350,13 +351,14 @@ where TrieDBBuilder::>::new(&db, &root.state_root).build(); keys.into_iter() .map(|key| { - trie.get(&key).map_err(|e| { + let value = trie.get(&key).map_err(|e| { Error::ImplementationSpecific(format!( "Error reading state proof: {e:?}" )) - }) + })?; + Ok((key, value)) }) - .collect::, _>>()? + .collect::, _>>()? } }; From 836f90f59534ca6fe112d18619005fe391b1f669 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 30 May 2023 11:48:01 +0100 Subject: [PATCH 130/182] Some utility functions (#45) --- pallet-ismp/src/handlers.rs | 82 +++++++++++++++++++++++++++++++++++++ pallet-ismp/src/lib.rs | 3 +- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 pallet-ismp/src/handlers.rs diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs new file mode 100644 index 000000000..1df6f030c --- /dev/null +++ b/pallet-ismp/src/handlers.rs @@ -0,0 +1,82 @@ +//! Some extra utilities for pallet-ismp + +use crate::{ + dispatcher::Receipt, host::Host, Config, Event, IncomingRequestAcks, IncomingResponseAcks, + Pallet, +}; +use alloc::string::ToString; +use ismp_primitives::mmr::Leaf; +use ismp_rs::{ + router::{DispatchError, DispatchResult, DispatchSuccess, Request, Response}, + util::{hash_request, hash_response}, +}; +use sp_core::H256; + +impl Pallet +where + ::Hash: From, +{ + /// Handle an incoming request + pub fn handle_request(&self, request: Request) -> DispatchResult { + let commitment = hash_request::>(&request).0.to_vec(); + + if IncomingRequestAcks::::contains_key(commitment.clone()) { + Err(DispatchError { + msg: "Duplicate request".to_string(), + nonce: request.nonce(), + source: request.source_chain(), + dest: request.dest_chain(), + })? + } + + let (dest_chain, source_chain, nonce) = + (request.dest_chain(), request.source_chain(), request.nonce()); + Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError { + msg: "Failed to push request into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })?; + // Deposit Event + Pallet::::deposit_event(Event::Request { + request_nonce: nonce, + source_chain, + dest_chain, + }); + + IncomingRequestAcks::::insert(commitment, Receipt::Ok); + Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + } + + /// Handle an incoming response + pub fn handle_response(&self, response: Response) -> DispatchResult { + let commitment = hash_response::>(&response).0.to_vec(); + + if IncomingResponseAcks::::contains_key(commitment.clone()) { + Err(DispatchError { + msg: "Duplicate response".to_string(), + nonce: response.nonce(), + source: response.source_chain(), + dest: response.dest_chain(), + })? + } + + let (dest_chain, source_chain, nonce) = + (response.dest_chain(), response.source_chain(), response.nonce()); + + Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { + msg: "Failed to push response into mmr".to_string(), + nonce, + source: source_chain, + dest: dest_chain, + })?; + + Pallet::::deposit_event(Event::Response { + request_nonce: nonce, + dest_chain, + source_chain, + }); + IncomingResponseAcks::::insert(commitment, Receipt::Ok); + Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + } +} diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 8e0650748..52a80db9b 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -25,6 +25,7 @@ pub mod benchmarking; pub mod dispatcher; mod errors; pub mod events; +pub mod handlers; pub mod host; mod mmr; #[cfg(test)] @@ -586,7 +587,7 @@ where } /// Insert a leaf into the mmr - pub fn mmr_push(leaf: Leaf) -> Option { + pub(crate) fn mmr_push(leaf: Leaf) -> Option { let offchain_key = match &leaf { Leaf::Request(req) => Pallet::::request_leaf_index_offchain_key( req.source_chain(), From a2859c8c9bfcf4708c25cc456412e7597687627c Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 30 May 2023 12:16:29 +0100 Subject: [PATCH 131/182] Remove `&self` from handlers (#47) --- pallet-ismp/src/handlers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index 1df6f030c..d8b31d89b 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -17,7 +17,7 @@ where ::Hash: From, { /// Handle an incoming request - pub fn handle_request(&self, request: Request) -> DispatchResult { + pub fn handle_request(request: Request) -> DispatchResult { let commitment = hash_request::>(&request).0.to_vec(); if IncomingRequestAcks::::contains_key(commitment.clone()) { @@ -49,7 +49,7 @@ where } /// Handle an incoming response - pub fn handle_response(&self, response: Response) -> DispatchResult { + pub fn handle_response(response: Response) -> DispatchResult { let commitment = hash_response::>(&response).0.to_vec(); if IncomingResponseAcks::::contains_key(commitment.clone()) { From 443a461061be05e5a9436e7c506c598fe3816979 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 30 May 2023 12:56:33 +0100 Subject: [PATCH 132/182] polkadot-v0.9.42 (#46) --- Cargo.lock | 1268 +++++++++++++++------------- Cargo.toml | 3 + ismp-assets/Cargo.toml | 6 +- ismp-assets/src/lib.rs | 9 +- pallet-ismp/Cargo.toml | 21 +- pallet-ismp/primitives/Cargo.toml | 6 +- pallet-ismp/primitives/src/lib.rs | 2 +- pallet-ismp/rpc/Cargo.toml | 12 +- pallet-ismp/runtime-api/Cargo.toml | 4 +- pallet-ismp/src/events.rs | 2 +- pallet-ismp/src/primitives.rs | 2 +- parachain/Cargo.toml | 19 +- parachain/inherent/Cargo.toml | 12 +- parachain/runtime-api/Cargo.toml | 2 +- 14 files changed, 724 insertions(+), 644 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 811b0845c..b58a99c9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead 0.5.2", "aes 0.8.2", @@ -188,6 +188,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -208,9 +214,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", @@ -247,9 +253,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -313,7 +319,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -329,7 +335,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -398,7 +404,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.14", + "rustix 0.37.19", "slab", "socket2", "waker-fn", @@ -421,7 +427,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -470,7 +476,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.6.2", "object 0.30.3", "rustc-demangle", ] @@ -488,10 +494,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] -name = "base58" +name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" @@ -501,9 +507,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -573,7 +579,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -609,7 +615,7 @@ dependencies = [ "cc", "cfg-if", "constant_time_eq", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -669,9 +675,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bounded-collections" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +checksum = "07fbd1d11282a1eb134d3c3b7cf8ce213b5161c6e5f73fb1b98618482c606b64" dependencies = [ "log", "parity-scale-codec", @@ -696,9 +702,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" dependencies = [ "memchr", "serde", @@ -706,9 +712,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-slice-cast" @@ -828,13 +834,13 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "fdbc37d37da9e5bce8173f3a41b71d9bf3c674deebbaceacd0ebdabde76efb03" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time 0.1.45", "wasm-bindgen", @@ -904,9 +910,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.4" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" +checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" dependencies = [ "clap_builder", "clap_derive", @@ -915,9 +921,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.4" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" +checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" dependencies = [ "anstream", "anstyle", @@ -928,21 +934,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "coarsetime" @@ -956,16 +962,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -1044,18 +1040,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1074,33 +1070,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" +checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" [[package]] name = "cranelift-entity" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" dependencies = [ "cranelift-codegen", "log", @@ -1110,15 +1106,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" +checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" [[package]] name = "cranelift-native" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" dependencies = [ "cranelift-codegen", "libc", @@ -1127,9 +1123,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1236,6 +1232,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1288,7 +1296,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -1317,23 +1325,24 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "cumulus-primitives-core" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", + "scale-info", "sp-api", "sp-runtime", "sp-std 5.0.0", @@ -1344,7 +1353,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -1367,7 +1376,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -1385,7 +1394,7 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.400#269fb073caf398e74978990f611a1c45d04564d3" +source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", @@ -1435,50 +1444,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - [[package]] name = "darling" version = "0.14.4" @@ -1516,15 +1481,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "data-encoding-macro" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1532,9 +1497,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" dependencies = [ "data-encoding", "syn 1.0.109", @@ -1551,6 +1516,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der-parser" version = "7.0.0" @@ -1671,11 +1646,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1723,13 +1699,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -1783,10 +1759,24 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +dependencies = [ + "der 0.7.6", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -1795,7 +1785,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", ] [[package]] @@ -1838,18 +1828,37 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.6", - "ff", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", "generic-array 0.14.7", - "group", + "group 0.12.1", "hkdf", "pem-rfc7468", - "pkcs8", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.2", "subtle", "zeroize", ] @@ -1885,17 +1894,6 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - [[package]] name = "errno" version = "0.3.1" @@ -1954,7 +1952,6 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 1.0.109", ] [[package]] @@ -1970,6 +1967,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "expander" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -2035,6 +2045,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.1.20" @@ -2087,13 +2107,13 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -2114,7 +2134,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", ] @@ -2137,7 +2157,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "frame-support", "frame-support-procedural", @@ -2174,7 +2194,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "bitflags", "environmental", @@ -2207,44 +2227,45 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", "frame-support-procedural-tools", "itertools", + "proc-macro-warning", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "frame-support", "log", @@ -2277,13 +2298,12 @@ dependencies = [ [[package]] name = "fs4" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea55201cc351fdb478217c0fb641b59813da9b4efe4c414a9d8f989a657d149" +checksum = "a7f5b6908aecca5812a4569056285e58c666588c9573ee59765bf1d3692699e2" dependencies = [ - "libc", - "rustix 0.35.13", - "winapi", + "rustix 0.37.19", + "windows-sys 0.48.0", ] [[package]] @@ -2364,7 +2384,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -2440,6 +2460,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2526,16 +2547,27 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -2666,7 +2698,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -2792,12 +2824,11 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -2950,15 +2981,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" - -[[package]] -name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -2996,8 +3021,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", - "io-lifetimes 1.0.10", - "rustix 0.37.14", + "io-lifetimes", + "rustix 0.37.19", "windows-sys 0.48.0", ] @@ -3084,6 +3109,7 @@ dependencies = [ "ismp", "parity-scale-codec", "primitive-types", + "scale-info", "serde", "sp-runtime", ] @@ -3159,9 +3185,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] @@ -3256,21 +3282,22 @@ dependencies = [ [[package]] name = "k256" -version = "0.11.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.7", + "elliptic-curve 0.13.5", + "once_cell", "sha2 0.10.6", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -3296,9 +3323,9 @@ dependencies = [ [[package]] name = "kvdb-rocksdb" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2182b8219fee6bd83aacaab7344e840179ae079d5216aa4e249b4d704646a844" +checksum = "fe7a749456510c45f795e8b04a6a3e0976d0139213ecbf465843830ad55e2217" dependencies = [ "kvdb", "num_cpus", @@ -3322,9 +3349,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libloading" @@ -3344,9 +3371,9 @@ checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libp2p" @@ -3407,7 +3434,7 @@ dependencies = [ "prost-build", "rand 0.8.5", "rw-stream-sink", - "sec1", + "sec1 0.3.0", "sha2 0.10.6", "smallvec", "thiserror", @@ -3418,9 +3445,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.39.1" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f8b7d65c070a5a1b5f8f0510648189da08f787b8963f8e21219e0710733af" +checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" dependencies = [ "either", "fnv", @@ -3481,18 +3508,18 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8ea433ae0cea7e3315354305237b9897afe45278b2118a7a57ca744e70fd27" +checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" dependencies = [ "bs58", "ed25519-dalek", "log", "multiaddr 0.17.1", "multihash 0.17.0", - "prost", "quick-protobuf", "rand 0.8.5", + "sha2 0.10.6", "thiserror", "zeroize", ] @@ -3712,7 +3739,7 @@ checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ "futures", "futures-rustls", - "libp2p-core 0.39.1", + "libp2p-core 0.39.2", "libp2p-identity", "rcgen 0.10.0", "ring", @@ -3803,9 +3830,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.3+7.4.4" +version = "0.10.0+7.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" +checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b" dependencies = [ "bindgen", "bzip2-sys", @@ -3866,24 +3893,15 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "pkg-config", "vcpkg", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3908,12 +3926,6 @@ dependencies = [ "nalgebra", ] -[[package]] -name = "linux-raw-sys" -version = "0.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" - [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -3922,9 +3934,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" @@ -3938,12 +3950,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "lru" @@ -4024,10 +4033,11 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb99c395ae250e1bf9133673f03ca9f97b7e71b705436bf8f089453445d1e9fe" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" dependencies = [ + "autocfg", "rawpointer", ] @@ -4037,7 +4047,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4052,7 +4062,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.14", + "rustix 0.37.19", ] [[package]] @@ -4135,16 +4145,24 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "eebffdb73fe72e917997fad08bdbf31ac50b0fa91cec93e69a0662e4264d454c" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -4232,7 +4250,7 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", "sha2 0.10.6", "sha3", @@ -4246,9 +4264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" dependencies = [ "core2", - "digest 0.10.6", "multihash-derive", - "sha2 0.10.6", "unsigned-varint", ] @@ -4540,9 +4556,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" [[package]] name = "opaque-debug" @@ -4564,9 +4580,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "orchestra" -version = "0.2.1" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0766f60d83cac01c6e3f3bc36aaa9056e48bea0deddb98a8c74de6021f3061" +checksum = "227585216d05ba65c7ab0a0450a3cf2cbd81a98862a54c4df8e14d5ac6adb015" dependencies = [ "async-trait", "dyn-clonable", @@ -4581,12 +4597,11 @@ dependencies = [ [[package]] name = "orchestra-proc-macro" -version = "0.2.1" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8e83dbd049009426b445424a1104c78e6172a4c13e3614e52a38262785a5d7" +checksum = "2871aadd82a2c216ee68a69837a526dfe788ecbe74c4c5038a6acdbff6653066" dependencies = [ - "expander 1.0.0", - "indexmap", + "expander 0.0.6", "itertools", "petgraph", "proc-macro-crate", @@ -4610,8 +4625,8 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", ] @@ -4621,8 +4636,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", ] @@ -4642,6 +4657,7 @@ version = "0.1.0" dependencies = [ "ckb-merkle-mountain-range", "derive_more", + "enum-as-inner", "env_logger", "frame-benchmarking", "frame-support", @@ -4664,7 +4680,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "frame-benchmarking", "frame-support", @@ -4681,9 +4697,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bfb81cf5c90a222db2fb7b3a7cbf8cc7f38dfb6647aca4d98edf8281f56ed5" +checksum = "4890dcb9556136a4ec2b0c51fa4a08c8b733b829506af8fff2e853f3a065985b" dependencies = [ "blake2", "crc32fast", @@ -4701,9 +4717,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -4813,7 +4829,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4858,22 +4874,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -4900,15 +4916,25 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.6", + "spki 0.7.2", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" @@ -4918,8 +4944,8 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-core-primitives" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "parity-scale-codec", "scale-info", @@ -4930,8 +4956,8 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "lazy_static", "log", @@ -4948,8 +4974,8 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "bs58", "futures", @@ -4967,8 +4993,8 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "async-trait", "derive_more", @@ -4989,8 +5015,8 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "bounded-vec", "futures", @@ -5001,19 +5027,18 @@ dependencies = [ "serde", "sp-application-crypto", "sp-consensus-babe", - "sp-consensus-vrf", "sp-core 7.0.0", "sp-keystore", "sp-maybe-compressed-blob", "sp-runtime", "thiserror", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "polkadot-node-subsystem-types" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "async-trait", "derive_more", @@ -5035,8 +5060,8 @@ dependencies = [ [[package]] name = "polkadot-overseer" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "async-trait", "futures", @@ -5058,8 +5083,8 @@ dependencies = [ [[package]] name = "polkadot-parachain" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "bounded-collections", "derive_more", @@ -5075,11 +5100,11 @@ dependencies = [ [[package]] name = "polkadot-primitives" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "bitvec", - "hex-literal 0.3.4", + "hex-literal 0.4.1", "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain", @@ -5101,8 +5126,8 @@ dependencies = [ [[package]] name = "polkadot-statement-table" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -5157,7 +5182,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.5.0", + "universal-hash 0.5.1", ] [[package]] @@ -5221,11 +5246,10 @@ dependencies = [ [[package]] name = "prioritized-metered-channel" -version = "0.4.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3caef72a78ca8e77cbdfa87dd516ebb79d4cbe5b42e3b8435b463a8261339ff" +checksum = "382698e48a268c832d0b181ed438374a6bb708a82a8ca273bb0f61c74cf209c4" dependencies = [ - "async-channel", "coarsetime", "crossbeam-queue", "derive_more", @@ -5270,11 +5294,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-warning" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -5438,9 +5473,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -5567,7 +5602,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.20", + "time 0.3.21", "x509-parser 0.13.2", "yasna", ] @@ -5580,7 +5615,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.20", + "time 0.3.21", "yasna", ] @@ -5630,7 +5665,7 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -5647,13 +5682,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick 1.0.1", "memchr", - "regex-syntax 0.7.1", + "regex-syntax 0.7.2", ] [[package]] @@ -5673,9 +5708,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "region" @@ -5705,11 +5740,21 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -5727,9 +5772,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.19.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" +checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99" dependencies = [ "libc", "librocksdb-sys", @@ -5834,27 +5879,13 @@ dependencies = [ [[package]] name = "rustix" -version = "0.35.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" -dependencies = [ - "bitflags", - "errno 0.2.8", - "io-lifetimes 0.7.5", - "libc", - "linux-raw-sys 0.0.46", - "windows-sys 0.42.0", -] - -[[package]] -name = "rustix" -version = "0.36.13" +version = "0.36.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" +checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" dependencies = [ "bitflags", - "errno 0.3.1", - "io-lifetimes 1.0.10", + "errno", + "io-lifetimes", "libc", "linux-raw-sys 0.1.4", "windows-sys 0.45.0", @@ -5862,15 +5893,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.14" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", - "errno 0.3.1", - "io-lifetimes 1.0.10", + "errno", + "io-lifetimes", "libc", - "linux-raw-sys 0.3.4", + "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] @@ -5917,7 +5948,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", ] [[package]] @@ -5955,7 +5986,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "log", "sp-core 7.0.0", @@ -5966,7 +5997,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "futures", @@ -5994,7 +6025,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -6009,7 +6040,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -6028,18 +6059,18 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "chrono", @@ -6079,7 +6110,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "fnv", "futures", @@ -6105,7 +6136,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "hash-db", "kvdb", @@ -6131,7 +6162,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "futures", @@ -6156,7 +6187,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "lru 0.8.1", "parity-scale-codec", @@ -6180,7 +6211,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -6193,7 +6224,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "log", "sc-allocator", @@ -6206,14 +6237,14 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "anyhow", "cfg-if", "libc", "log", "once_cell", - "rustix 0.36.13", + "rustix 0.36.14", "sc-allocator", "sc-executor-common", "sp-runtime-interface 7.0.0", @@ -6224,7 +6255,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "ansi_term", "futures", @@ -6240,7 +6271,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "async-trait", @@ -6255,7 +6286,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "async-channel", @@ -6285,6 +6316,7 @@ dependencies = [ "serde", "serde_json", "smallvec", + "snow", "sp-arithmetic", "sp-blockchain", "sp-consensus", @@ -6299,7 +6331,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "cid", "futures", @@ -6319,7 +6351,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "async-trait", @@ -6347,7 +6379,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "futures", @@ -6369,7 +6401,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "async-trait", @@ -6403,7 +6435,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "futures", @@ -6423,7 +6455,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "bytes", @@ -6454,7 +6486,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "futures", "libp2p", @@ -6467,7 +6499,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "futures", "jsonrpsee", @@ -6497,7 +6529,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -6516,7 +6548,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "http", "jsonrpsee", @@ -6531,7 +6563,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", "futures", @@ -6557,7 +6589,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "directories", @@ -6623,7 +6655,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "log", "parity-scale-codec", @@ -6634,7 +6666,7 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "clap", "fs4", @@ -6650,7 +6682,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "futures", "libc", @@ -6669,7 +6701,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "chrono", "futures", @@ -6688,7 +6720,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "ansi_term", "atty", @@ -6719,18 +6751,18 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "futures", @@ -6757,7 +6789,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "futures", @@ -6771,7 +6803,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-channel", "futures", @@ -6785,9 +6817,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" +checksum = "b569c32c806ec3abdf3b5869fb8bf1e0d275a7c1c9b0b05603d9464632649edf" dependencies = [ "bitvec", "cfg-if", @@ -6799,9 +6831,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6853,12 +6885,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - [[package]] name = "sct" version = "0.6.1" @@ -6897,10 +6923,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.7", - "pkcs8", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.6", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -6934,9 +6974,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags", "core-foundation", @@ -6947,9 +6987,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -6963,22 +7003,22 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -7013,7 +7053,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -7049,16 +7089,16 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -7092,7 +7132,17 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -7126,9 +7176,9 @@ dependencies = [ [[package]] name = "slice-group-by" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" @@ -7189,13 +7239,15 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "hash-db", "log", "parity-scale-codec", + "scale-info", "sp-api-proc-macro", "sp-core 7.0.0", + "sp-metadata-ir", "sp-runtime", "sp-state-machine", "sp-std 5.0.0", @@ -7207,7 +7259,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "Inflector", "blake2", @@ -7215,13 +7267,13 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "scale-info", @@ -7234,7 +7286,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "integer-sqrt", "num-traits", @@ -7248,7 +7300,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "scale-info", @@ -7261,7 +7313,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "sp-api", @@ -7273,7 +7325,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "futures", "log", @@ -7291,7 +7343,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "futures", @@ -7306,7 +7358,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "parity-scale-codec", @@ -7324,10 +7376,9 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", - "merlin", "parity-scale-codec", "scale-info", "serde", @@ -7335,7 +7386,6 @@ dependencies = [ "sp-application-crypto", "sp-consensus", "sp-consensus-slots", - "sp-consensus-vrf", "sp-core 7.0.0", "sp-inherents", "sp-keystore", @@ -7347,7 +7397,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "finality-grandpa", "log", @@ -7365,7 +7415,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "scale-info", @@ -7374,29 +7424,16 @@ dependencies = [ "sp-timestamp", ] -[[package]] -name = "sp-consensus-vrf" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" -dependencies = [ - "parity-scale-codec", - "scale-info", - "schnorrkel", - "sp-core 7.0.0", - "sp-runtime", - "sp-std 5.0.0", -] - [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "array-bytes", - "base58", "bitflags", "blake2", "bounded-collections", + "bs58", "dyn-clonable", "ed25519-zebra", "futures", @@ -7409,6 +7446,7 @@ dependencies = [ "merlin", "parity-scale-codec", "parking_lot 0.12.1", + "paste", "primitive-types", "rand 0.8.5", "regex", @@ -7477,11 +7515,11 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "blake2b_simd", "byteorder", - "digest 0.10.6", + "digest 0.10.7", "sha2 0.10.6", "sha3", "sp-std 5.0.0", @@ -7496,7 +7534,7 @@ checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" dependencies = [ "blake2b_simd", "byteorder", - "digest 0.10.6", + "digest 0.10.7", "sha2 0.10.6", "sha3", "sp-std 7.0.0", @@ -7506,18 +7544,18 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "proc-macro2", "quote", "sp-core-hashing 5.0.0", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -7526,11 +7564,11 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -7547,7 +7585,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "environmental", "parity-scale-codec", @@ -7570,7 +7608,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -7585,7 +7623,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "bytes", "ed25519", @@ -7594,6 +7632,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", + "rustversion", "secp256k1", "sp-core 7.0.0", "sp-externalities 0.13.0", @@ -7610,7 +7649,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "lazy_static", "sp-core 7.0.0", @@ -7621,14 +7660,11 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ - "async-trait", "futures", - "merlin", "parity-scale-codec", "parking_lot 0.12.1", - "schnorrkel", "serde", "sp-core 7.0.0", "sp-externalities 0.13.0", @@ -7638,16 +7674,27 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "thiserror", - "zstd", + "zstd 0.12.3+zstd.1.5.2", +] + +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std 5.0.0", ] [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "sp-api", "sp-core 7.0.0", @@ -7657,7 +7704,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "backtrace", "lazy_static", @@ -7667,7 +7714,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "rustc-hash", "serde", @@ -7677,7 +7724,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "either", "hash256-std-hasher", @@ -7699,7 +7746,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -7736,13 +7783,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -7761,7 +7808,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "scale-info", @@ -7775,10 +7822,11 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "scale-info", + "serde", "sp-core 7.0.0", "sp-runtime", "sp-std 5.0.0", @@ -7787,7 +7835,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "hash-db", "log", @@ -7807,7 +7855,7 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" [[package]] name = "sp-std" @@ -7818,7 +7866,7 @@ checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "impl-serde", "parity-scale-codec", @@ -7845,7 +7893,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "futures-timer", @@ -7860,7 +7908,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "sp-std 5.0.0", @@ -7885,7 +7933,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "sp-api", "sp-runtime", @@ -7894,7 +7942,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "async-trait", "log", @@ -7910,11 +7958,11 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "ahash 0.8.3", "hash-db", - "hashbrown 0.12.3", + "hashbrown 0.13.2", "lazy_static", "memory-db", "nohash-hasher", @@ -7933,7 +7981,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "impl-serde", "parity-scale-codec", @@ -7950,18 +7998,18 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -7990,7 +8038,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "parity-scale-codec", "scale-info", @@ -8015,14 +8063,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.6", ] [[package]] name = "ss58-registry" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" dependencies = [ "Inflector", "num-format", @@ -8136,7 +8194,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.40#98f2e3451c9143278ec53c6718940aeabcd3b68a" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" dependencies = [ "hyper", "log", @@ -8173,9 +8231,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -8196,9 +8254,9 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags", "core-foundation", @@ -8223,9 +8281,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempfile" @@ -8236,7 +8294,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.14", + "rustix 0.37.19", "windows-sys 0.45.0", ] @@ -8272,7 +8330,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -8341,9 +8399,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa", "serde", @@ -8353,15 +8411,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] @@ -8412,9 +8470,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", @@ -8426,18 +8484,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -8453,9 +8511,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -8465,9 +8523,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -8489,15 +8547,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "toml_datetime", @@ -8560,20 +8618,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -8591,8 +8649,8 @@ dependencies = [ [[package]] name = "tracing-gum" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -8602,14 +8660,14 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ - "expander 0.0.6", + "expander 2.0.0", "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -8762,7 +8820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "digest 0.10.6", + "digest 0.10.7", "rand 0.8.5", "static_assertions", ] @@ -8793,9 +8851,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -8806,12 +8864,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -8830,9 +8882,9 @@ dependencies = [ [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle", @@ -8875,9 +8927,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" dependencies = [ "getrandom 0.2.9", ] @@ -8951,9 +9003,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -8961,24 +9013,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" dependencies = [ "cfg-if", "js-sys", @@ -8988,9 +9040,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8998,22 +9050,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "wasm-instrument" @@ -9066,7 +9118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ "downcast-rs", - "libm 0.2.6", + "libm 0.2.7", "memory_units", "num-rational", "num-traits", @@ -9085,9 +9137,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" dependencies = [ "anyhow", "bincode", @@ -9113,18 +9165,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" dependencies = [ "anyhow", "base64 0.13.1", @@ -9132,19 +9184,19 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.13", + "rustix 0.36.14", "serde", "sha2 0.10.6", "toml", "windows-sys 0.42.0", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "wasmtime-cranelift" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" dependencies = [ "anyhow", "cranelift-codegen", @@ -9163,9 +9215,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ "anyhow", "cranelift-entity", @@ -9182,9 +9234,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -9206,20 +9258,20 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.36.13", + "rustix 0.36.14", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" dependencies = [ "cfg-if", "libc", @@ -9228,9 +9280,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" dependencies = [ "anyhow", "cc", @@ -9243,7 +9295,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.36.13", + "rustix 0.36.14", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -9252,9 +9304,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ "cranelift-entity", "serde", @@ -9264,9 +9316,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -9327,7 +9379,7 @@ dependencies = [ "sha2 0.10.6", "stun", "thiserror", - "time 0.3.20", + "time 0.3.21", "tokio", "turn", "url", @@ -9364,7 +9416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" dependencies = [ "aes 0.6.0", - "aes-gcm 0.10.1", + "aes-gcm 0.10.2", "async-trait", "bincode", "block-modes", @@ -9372,7 +9424,7 @@ dependencies = [ "ccm", "curve25519-dalek 3.2.0", "der-parser 8.2.0", - "elliptic-curve", + "elliptic-curve 0.12.3", "hkdf", "hmac 0.12.1", "log", @@ -9384,11 +9436,11 @@ dependencies = [ "rcgen 0.9.3", "ring", "rustls 0.19.1", - "sec1", + "sec1 0.3.0", "serde", "sha1", "sha2 0.10.6", - "signature", + "signature 1.6.4", "subtle", "thiserror", "tokio", @@ -9437,18 +9489,15 @@ dependencies = [ [[package]] name = "webrtc-media" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" +checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" dependencies = [ "byteorder", "bytes", - "derive_builder", - "displaydoc", "rand 0.8.5", "rtp", "thiserror", - "webrtc-util", ] [[package]] @@ -9526,9 +9575,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +checksum = "5cd0496a71f3cc6bc4bf0ed91346426a5099e93d89807e663162dc5a1069ff65" dependencies = [ "bytemuck", "safe_arch", @@ -9772,9 +9821,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] @@ -9835,7 +9884,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -9853,13 +9902,13 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.20", + "time 0.3.21", ] [[package]] name = "xcm" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "bounded-collections", "derivative", @@ -9874,13 +9923,13 @@ dependencies = [ [[package]] name = "xcm-procedural" -version = "0.9.40" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.40#95fe4c8862810bffd68343231a517e62689c05c0" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -9903,7 +9952,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.20", + "time 0.3.21", ] [[package]] @@ -9923,7 +9972,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -9932,7 +9981,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe 6.0.5+zstd.1.5.4", ] [[package]] @@ -9945,6 +10003,16 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.8+zstd.1.5.5" diff --git a/Cargo.toml b/Cargo.toml index 554140fef..77502a0f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ members = [ "parachain", "ismp-assets" ] + +[workspace.dependencies] +enum-as-inner = "=0.5.1" \ No newline at end of file diff --git a/ismp-assets/Cargo.toml b/ismp-assets/Cargo.toml index 185d7e6dd..ea658cd5b 100644 --- a/ismp-assets/Cargo.toml +++ b/ismp-assets/Cargo.toml @@ -16,9 +16,9 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } # substrate -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } # local pallet-ismp = { path = "../pallet-ismp", default-features = false } diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index b9ceca5ff..047f7a1dd 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -38,7 +38,7 @@ pub mod pallet { pallet_prelude::*, traits::{ fungible::{Inspect, Mutate}, - tokens::Balance, + tokens::{Balance, Fortitude, Precision}, }, }; use frame_system::pallet_prelude::*; @@ -125,7 +125,12 @@ pub mod pallet { dispatcher .dispatch_request(DispatchRequest::Post(post)) .map_err(|_| Error::::TransferFailed)?; - >::burn_from(&origin, params.amount.into())?; + >::burn_from( + &origin, + params.amount.into(), + Precision::Exact, + Fortitude::Force, + )?; Self::deposit_event(Event::::BalanceTransferred { from: payload.from, to: payload.to, diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index 48ad0c646..8e4a5db39 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -6,15 +6,15 @@ authors = ["Polytope Labs "] [dependencies] # substrate -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, optional = true } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false, optional = true } # polytope labs ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } @@ -26,13 +26,14 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display"] } +enum-as-inner = "=0.5.1" # local ismp-primitives = { path = "./primitives", default-features = false } [dev-dependencies] env_logger = "0.10.0" -pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } ismp-testsuite = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } [features] diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml index 08c6fad6b..7580f618c 100644 --- a/pallet-ismp/primitives/Cargo.toml +++ b/pallet-ismp/primitives/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Polytope Labs "] [dependencies] # substrate -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } # polytope labs ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } @@ -17,6 +17,7 @@ merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5. codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } primitive-types = { version = "0.12.1", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } [features] default = ["std"] @@ -27,5 +28,6 @@ std = [ "codec/std", "sp-runtime/std", "primitive-types/std", + "scale-info/std", "serde" ] diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs index 07e574361..40e8d67c9 100644 --- a/pallet-ismp/primitives/src/lib.rs +++ b/pallet-ismp/primitives/src/lib.rs @@ -24,7 +24,7 @@ use ismp::host::StateMachine; pub mod mmr; /// Queries a request leaf in the mmr -#[derive(codec::Encode, codec::Decode)] +#[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] pub struct LeafIndexQuery { /// The source of the request diff --git a/pallet-ismp/rpc/Cargo.toml b/pallet-ismp/rpc/Cargo.toml index b4969b8af..f076d396e 100644 --- a/pallet-ismp/rpc/Cargo.toml +++ b/pallet-ismp/rpc/Cargo.toml @@ -19,9 +19,9 @@ pallet-ismp = { path = ".." } ismp-runtime-api = { path = "../runtime-api" } ismp-primitives = { path = "../primitives" } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } \ No newline at end of file +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } \ No newline at end of file diff --git a/pallet-ismp/runtime-api/Cargo.toml b/pallet-ismp/runtime-api/Cargo.toml index 5db40a613..a90af8575 100644 --- a/pallet-ismp/runtime-api/Cargo.toml +++ b/pallet-ismp/runtime-api/Cargo.toml @@ -8,8 +8,8 @@ authors = ["Polytope Labs "] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } pallet-ismp = { path = "..", default-features = false } ismp-primitives = { path = "../primitives", default-features = false } ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 5b47337d8..6822f462a 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -22,7 +22,7 @@ use ismp_rs::{ }; /// Ismp Core Protocol Events -#[derive(Clone, codec::Encode, codec::Decode, Debug)] +#[derive(Clone, codec::Encode, codec::Decode, Debug, scale_info::TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum Event { /// Emitted when a state machine is successfully updated to a new height diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 903900143..222469dfb 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -36,7 +36,7 @@ pub struct Proof { } /// Merkle Mountain Range operation error. -#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq)] +#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, scale_info::TypeInfo)] #[allow(missing_docs)] pub enum Error { InvalidNumericOp, diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 067275931..75822aa32 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -15,21 +15,22 @@ hex-literal = "0.4.1" merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } primitive-types = { version = "0.12.1", default-features = false } hash-db = { version = "0.16.0", default-features = false } + # polytope labs ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } # substrate -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -sp-inherents = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } -sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-inherents = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } # cumulus -parachain-system = { package = "cumulus-pallet-parachain-system", git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400", default-features = false } -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400", default-features = false } +parachain-system = { package = "cumulus-pallet-parachain-system", git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } # local ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } diff --git a/parachain/inherent/Cargo.toml b/parachain/inherent/Cargo.toml index c366eaed5..63b11191b 100644 --- a/parachain/inherent/Cargo.toml +++ b/parachain/inherent/Cargo.toml @@ -10,14 +10,14 @@ codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "deriv anyhow = "1.0.57" # Substrate -sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } # Cumulus -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400" } -cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420" } # polytope-labs ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } diff --git a/parachain/runtime-api/Cargo.toml b/parachain/runtime-api/Cargo.toml index e96f50508..d6aeb4430 100644 --- a/parachain/runtime-api/Cargo.toml +++ b/parachain/runtime-api/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Polytope Labs "] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } [features] default = ["std"] From 21b3dc10317bb7cbf4b785e6285017873efdaf05 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 30 May 2023 15:13:18 +0100 Subject: [PATCH 133/182] Replace ssh addresses with http addresses (#48) --- .github/workflows/ci.yml | 4 ---- Cargo.lock | 4 ++-- ismp-assets/Cargo.toml | 2 +- pallet-ismp/Cargo.toml | 4 ++-- pallet-ismp/primitives/Cargo.toml | 2 +- pallet-ismp/rpc/Cargo.toml | 2 +- pallet-ismp/runtime-api/Cargo.toml | 2 +- parachain/Cargo.toml | 2 +- parachain/inherent/Cargo.toml | 2 +- 9 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8025286c5..1ecdbd897 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,10 +20,6 @@ jobs: token: ${{ secrets.GH_TOKEN }} submodules: recursive - - uses: webfactory/ssh-agent@v0.7.0 - with: - ssh-private-key: ${{ secrets.SSH_KEY }} - - name: Install toolchain uses: dtolnay/rust-toolchain@nightly with: diff --git a/Cargo.lock b/Cargo.lock index b58a99c9f..0f94d1516 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" dependencies = [ "derive_more", "parity-scale-codec", @@ -3151,7 +3151,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+ssh://git@github.com/polytope-labs/ismp-rs.git?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" dependencies = [ "ismp", "parity-scale-codec", diff --git a/ismp-assets/Cargo.toml b/ismp-assets/Cargo.toml index ea658cd5b..ad8a71ab3 100644 --- a/ismp-assets/Cargo.toml +++ b/ismp-assets/Cargo.toml @@ -13,7 +13,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # polytope labs -ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } # substrate frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index 8e4a5db39..5a9a6c9e2 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -17,7 +17,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false, optional = true } # polytope labs -ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } # crates.io codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } @@ -34,7 +34,7 @@ ismp-primitives = { path = "./primitives", default-features = false } [dev-dependencies] env_logger = "0.10.0" pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -ismp-testsuite = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } +ismp-testsuite = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } [features] default = ["std"] diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml index 7580f618c..3fc71c5ce 100644 --- a/pallet-ismp/primitives/Cargo.toml +++ b/pallet-ismp/primitives/Cargo.toml @@ -10,7 +10,7 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "polk sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } # polytope labs -ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } # crates.io merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } diff --git a/pallet-ismp/rpc/Cargo.toml b/pallet-ismp/rpc/Cargo.toml index f076d396e..14af027b9 100644 --- a/pallet-ismp/rpc/Cargo.toml +++ b/pallet-ismp/rpc/Cargo.toml @@ -14,7 +14,7 @@ hex-literal = { version = "0.3.3" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.45" -ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } pallet-ismp = { path = ".." } ismp-runtime-api = { path = "../runtime-api" } ismp-primitives = { path = "../primitives" } diff --git a/pallet-ismp/runtime-api/Cargo.toml b/pallet-ismp/runtime-api/Cargo.toml index a90af8575..77161cf21 100644 --- a/pallet-ismp/runtime-api/Cargo.toml +++ b/pallet-ismp/runtime-api/Cargo.toml @@ -12,7 +12,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } pallet-ismp = { path = "..", default-features = false } ismp-primitives = { path = "../primitives", default-features = false } -ismp-rs = { package = "ismp", git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } [dependencies.codec] diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 75822aa32..1eca48147 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -17,7 +17,7 @@ primitive-types = { version = "0.12.1", default-features = false } hash-db = { version = "0.16.0", default-features = false } # polytope labs -ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } # substrate frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } diff --git a/parachain/inherent/Cargo.toml b/parachain/inherent/Cargo.toml index 63b11191b..06cffb4ec 100644 --- a/parachain/inherent/Cargo.toml +++ b/parachain/inherent/Cargo.toml @@ -20,6 +20,6 @@ cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", bran cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420" } # polytope-labs -ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } ismp-parachain = { path = "../" } ismp-parachain-runtime-api = { path = "../runtime-api" } \ No newline at end of file From 7905b387f0edc12a63fb3ab0978ab73f9725112a Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 30 May 2023 16:14:50 +0100 Subject: [PATCH 134/182] Fix type info (#49) --- pallet-ismp/primitives/src/mmr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs index c93286fa5..e9b52cbf2 100644 --- a/pallet-ismp/primitives/src/mmr.rs +++ b/pallet-ismp/primitives/src/mmr.rs @@ -32,7 +32,7 @@ pub type LeafIndex = u64; pub type NodeIndex = u64; /// A concrete Leaf for the MMR -#[derive(Debug, Clone, Decode, Encode, PartialEq, Eq)] +#[derive(Debug, Clone, Decode, Encode, PartialEq, Eq, scale_info::TypeInfo)] pub enum Leaf { /// A request variant Request(Request), @@ -51,7 +51,7 @@ impl Leaf { } /// An element representing either full data or its hash. -#[derive(Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, scale_info::TypeInfo)] pub enum DataOrHash { /// Arbitrary data in its full form. Data(Leaf), From 2bc59453973473f896575ab470a7556284440247 Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Thu, 1 Jun 2023 10:11:38 +0100 Subject: [PATCH 135/182] Run Unit Tests in Docker Container (#50) add docker --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 0a8f64e82..d7d5e08a3 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,14 @@ This repo holds all the required components substrate runtimes need to interoper Installation and integration guides can be found in the [book](https://substrate-ismp.polytope.technology). +## Testing and Testing Guide +Please see [CI](.github/workflows/ci.yml) for test coverage. + +## Run Test in Docker +```bash +docker run --memory="24g" --rm --user root -v "$PWD":/app -w /app rust:latest /bin/bash -c "apt update && apt install -y protobuf-compiler libclang-dev && cargo test --release --manifest-path=./Cargo.toml" +``` + ## License This library is licensed under the Apache 2.0 License, Copyright (c) 2023 Polytope Labs. \ No newline at end of file From 1a434aeecaf1c383eac521a65129dfc1e903df9c Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Mon, 5 Jun 2023 12:48:30 +0100 Subject: [PATCH 136/182] Addd Support Channels (#52) addd support channels --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d7d5e08a3..a15860b99 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# pallet-ismp ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) +# pallet-ismp ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) [![Telegram: YourGroup](https://img.shields.io/badge/-Telegram-blue?style=flat-square&logo=Telegram&logoColor=white&link=https://t.me/YourGroup)](https://t.me/ismp_support) [![Discord: YourServer](https://img.shields.io/badge/-Discord-7289DA?style=flat-square&logo=Discord&logoColor=white&link=https://discord.gg/YourServer)](https://discord.gg/vKAa3XcCBX) Implementation of the Interoperable State Machine Protocol for substrate runtimes. This project is [funded by the web3 foundation](https://github.com/w3f/Grants-Program/blob/master/applications/ismp.md). From 332f2f73d2f9aaab6179581d875481c328985d1e Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 5 Jun 2023 13:36:33 +0100 Subject: [PATCH 137/182] Add Get request demo (#51) * implement querying state proofs * add support for get requests to demo ismp assets Co-authored-by: Web3 Philosopher --- Cargo.lock | 20 ++++++++- ismp-assets/Cargo.toml | 4 +- ismp-assets/src/lib.rs | 92 ++++++++++++++++++++++++++++++++------ pallet-ismp/rpc/src/lib.rs | 12 ++++- pallet-ismp/src/errors.rs | 87 +++++++++++++++++++++++++++++++++++ pallet-ismp/src/lib.rs | 26 +++++++++-- pallet-ismp/src/tests.rs | 4 +- 7 files changed, 221 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f94d1516..124eed421 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1628c9d7519ae39b8badf02ed42b6c7fa23f28a0" dependencies = [ "derive_more", "parity-scale-codec", @@ -3045,6 +3045,7 @@ dependencies = [ "frame-support", "frame-system", "ismp", + "pallet-balances", "pallet-ismp", "parity-scale-codec", "scale-info", @@ -3151,7 +3152,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1932c2c45f7c6ff104dd27b87aa06769739697fb" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1628c9d7519ae39b8badf02ed42b6c7fa23f28a0" dependencies = [ "ismp", "parity-scale-codec", @@ -4651,6 +4652,21 @@ dependencies = [ "libm 0.1.4", ] +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-ismp" version = "0.1.0" diff --git a/ismp-assets/Cargo.toml b/ismp-assets/Cargo.toml index ad8a71ab3..b61f25a48 100644 --- a/ismp-assets/Cargo.toml +++ b/ismp-assets/Cargo.toml @@ -18,6 +18,7 @@ ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", defa # substrate frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } # local @@ -34,5 +35,6 @@ std = [ "sp-runtime/std", "scale-info/std", "ismp/std", - "pallet-ismp/std" + "pallet-ismp/std", + "pallet-balances/std" ] diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index 047f7a1dd..3ec6db017 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -34,6 +34,7 @@ pub const PALLET_ID: PalletId = PalletId(*b"ismp-ast"); #[frame_support::pallet] pub mod pallet { use super::*; + use alloc::vec; use frame_support::{ pallet_prelude::*, traits::{ @@ -44,7 +45,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use ismp::{ host::StateMachine, - router::{DispatchPost, DispatchRequest, IsmpDispatcher}, + router::{DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher}, }; #[pallet::pallet] @@ -52,7 +53,7 @@ pub mod pallet { /// Pallet Configuration #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_balances::Config { /// Overarching event type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Native balance @@ -74,7 +75,7 @@ pub mod pallet { /// Destination account to: T::AccountId, /// Amount being transferred - amount: T::Balance, + amount: ::Balance, /// Destination chain's Id dest_chain: StateMachine, }, @@ -85,10 +86,20 @@ pub mod pallet { /// Receiving account to: T::AccountId, /// Amount that was received - amount: T::Balance, + amount: ::Balance, /// Source chain's Id source_chain: StateMachine, }, + + /// Get request dispatched + GetRequestDispatched, + /// Token issuance on some counterparty parachain + CounterpartyIssuance { + /// Parachain Id + chain: StateMachine, + /// Total issuance on counterparty parachain + total_issuance: u128, + }, } /// Pallet Errors @@ -96,6 +107,8 @@ pub mod pallet { pub enum Error { /// Error encountered when initializing transfer TransferFailed, + /// Failed to dispatch get request + GetDispatchFailed, } // Pallet implements [`Hooks`] trait to define some logic to execute in some context. @@ -105,11 +118,11 @@ pub mod pallet { #[pallet::call] impl Pallet { /// Transfer some funds over ISMP - #[pallet::weight(1_000_000)] + #[pallet::weight(Weight::from_parts(1_000_000, 0))] #[pallet::call_index(0)] pub fn transfer( origin: OriginFor, - params: TransferParams, + params: TransferParams::Balance>, ) -> DispatchResult { let origin = ensure_signed(origin)?; let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; @@ -139,6 +152,33 @@ pub mod pallet { }); Ok(()) } + + /// Get the total issuance of the native token in a counterparty + /// parachain + #[pallet::weight(Weight::from_parts(1_000_000, 0))] + #[pallet::call_index(1)] + pub fn counterparty_issuance( + origin: OriginFor, + dest_chain: StateMachine, + height: u64, + timeout: u64, + ) -> DispatchResult { + ensure_signed(origin)?; + let get = DispatchGet { + dest_chain, + from: PALLET_ID.0.to_vec(), + keys: vec![pallet_balances::TotalIssuance::::hashed_key().to_vec()], + height, + timeout_timestamp: timeout, + }; + + let dispatcher = T::IsmpDispatcher::default(); + dispatcher + .dispatch_request(DispatchRequest::Get(get)) + .map_err(|_| Error::::GetDispatchFailed)?; + Self::deposit_event(Event::::GetRequestDispatched); + Ok(()) + } } /// Transfer payload @@ -172,7 +212,7 @@ pub mod pallet { } /// Ismp dispatch error -fn ismp_dispatch_error(msg: &'static str) -> ismp::error::Error { +fn ismp_dispatch_error(msg: &str) -> ismp::error::Error { ismp::error::Error::ImplementationSpecific(msg.to_string()) } @@ -184,8 +224,9 @@ impl IsmpModule for Pallet { _ => Err(ismp_dispatch_error("Only Post requests allowed, found Get"))?, }; - let payload = as codec::Decode>::decode(&mut &*data) - .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + let payload = + ::Balance> as codec::Decode>::decode(&mut &*data) + .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; >::mint_into(&payload.to, payload.amount.into()) .map_err(|_| ismp_dispatch_error("Failed to mint funds"))?; Pallet::::deposit_event(Event::::BalanceReceived { @@ -197,8 +238,32 @@ impl IsmpModule for Pallet { Ok(()) } - fn on_response(_response: Response) -> Result<(), ismp::error::Error> { - Err(ismp_dispatch_error("Balance transfer protocol does not accept responses")) + fn on_response(response: Response) -> Result<(), ismp::error::Error> { + match response { + Response::Post(_) => { + Err(ismp_dispatch_error("Balance transfer protocol does not accept post responses")) + } + Response::Get(get_res) => { + let total_issuance = get_res + .values + .get(pallet_balances::TotalIssuance::::hashed_key().to_vec().as_slice()) + .cloned() + .flatten(); + + match total_issuance { + Some(total_issuance) => { + let total_issuance: u128 = codec::Decode::decode(&mut &*total_issuance) + .map_err(|_| ismp_dispatch_error("Failed to decode total issuance"))?; + Pallet::::deposit_event(Event::::CounterpartyIssuance { + chain: get_res.get.dest_chain, + total_issuance, + }); + Ok(()) + } + _ => Err(ismp_dispatch_error("Received None")), + } + } + } } fn on_timeout(request: Request) -> Result<(), ismp::error::Error> { @@ -207,8 +272,9 @@ impl IsmpModule for Pallet { Request::Post(post) => post.data, _ => Err(ismp_dispatch_error("Only Post requests allowed, found Get"))?, }; - let payload = as codec::Decode>::decode(&mut &*data) - .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + let payload = + ::Balance> as codec::Decode>::decode(&mut &*data) + .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; >::mint_into( &payload.from, payload.amount.into(), diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index ab2e19a06..a40173271 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -215,8 +215,16 @@ where Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) } - fn query_state_proof(&self, _height: u32, _keys: Vec>) -> Result { - unimplemented!() + fn query_state_proof(&self, height: u32, keys: Vec>) -> Result { + let at = self.client.block_hash(height.into()).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Could not find valid blockhash for provided height") + })?; + let proof: Vec<_> = self + .client + .read_proof(at, &mut keys.iter().map(|key| key.as_slice())) + .map(|proof| proof.into_iter_nodes().collect()) + .map_err(|_| runtime_error_into_rpc_error("Error reading state proof"))?; + Ok(Proof { proof: proof.encode(), leaves: None, height }) } fn query_consensus_state( diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index f6e43ca6d..8cf11c9e2 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -19,6 +19,7 @@ use ismp_rs::{ consensus::{ConsensusClientId, StateMachineHeight}, error::Error as IsmpError, host::StateMachine, + router::DispatchResult, }; use sp_std::prelude::*; @@ -95,6 +96,92 @@ pub enum HandlingError { InsufficientProofHeight, } +#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +pub struct ModuleDispatchError { + /// Descriptive error message + pub msg: Vec, + /// Request nonce + pub nonce: u64, + /// Source chain for request or response + pub source_chain: StateMachine, + /// Destination chain for request or response + pub dest_chain: StateMachine, +} + +#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +pub struct ModuleDispatchSuccess { + /// Destination chain for request or response + pub dest_chain: StateMachine, + /// Source chain for request or response + pub source_chain: StateMachine, + /// Request nonce + pub nonce: u64, +} + +#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +pub enum ModuleCallbackResult { + Response(Result), + Request(Result), + Timeout(Result), +} + +pub fn to_response_results(values: Vec) -> Vec { + values + .into_iter() + .map(|res| match res { + Ok(res) => ModuleCallbackResult::Response(Ok(ModuleDispatchSuccess { + dest_chain: res.dest_chain, + source_chain: res.source_chain, + nonce: res.nonce, + })), + Err(res) => ModuleCallbackResult::Response(Err(ModuleDispatchError { + msg: res.msg.as_bytes().to_vec(), + dest_chain: res.dest, + source_chain: res.source, + nonce: res.nonce, + })), + }) + .collect() +} + +pub fn to_request_results(values: Vec) -> Vec { + values + .into_iter() + .map(|res| match res { + Ok(res) => ModuleCallbackResult::Request(Ok(ModuleDispatchSuccess { + dest_chain: res.dest_chain, + source_chain: res.source_chain, + nonce: res.nonce, + })), + Err(res) => ModuleCallbackResult::Request(Err(ModuleDispatchError { + msg: res.msg.as_bytes().to_vec(), + dest_chain: res.dest, + source_chain: res.source, + nonce: res.nonce, + })), + }) + .collect() +} + +pub fn to_timeout_results(values: Vec) -> Vec { + values + .into_iter() + .map(|res| match res { + Ok(res) => ModuleCallbackResult::Timeout(Ok(ModuleDispatchSuccess { + dest_chain: res.dest_chain, + source_chain: res.source_chain, + nonce: res.nonce, + })), + Err(res) => ModuleCallbackResult::Timeout(Err(ModuleDispatchError { + msg: res.msg.as_bytes().to_vec(), + dest_chain: res.dest, + source_chain: res.source, + nonce: res.nonce, + })), + }) + .collect() +} + impl From for HandlingError { fn from(value: ismp_rs::error::Error) -> Self { match value { diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 52a80db9b..989aacc71 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -50,7 +50,10 @@ use ismp_rs::{ }; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. -use crate::{errors::HandlingError, mmr::mmr::Mmr}; +use crate::{ + errors::{to_request_results, to_response_results, to_timeout_results, HandlingError}, + mmr::mmr::Mmr, +}; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, @@ -388,6 +391,7 @@ where // Define a host let host = Host::::default(); let mut errors: Vec = vec![]; + let mut module_dispatch_results = vec![]; for message in messages { match handle_incoming_message(&host, message) { @@ -429,20 +433,34 @@ where ); } } - Ok(_) => { - // Do nothing, event should have been deposited by the ismp router + Ok(MessageResult::Response(res)) => { + let results = to_response_results(res); + module_dispatch_results.extend(results); + } + Ok(MessageResult::Request(res)) => { + let results = to_request_results(res); + module_dispatch_results.extend(results); + } + Ok(MessageResult::Timeout(res)) => { + let results = to_timeout_results(res); + module_dispatch_results.extend(results); } Err(err) => { errors.push(err.into()); } + _ => {} } } if !errors.is_empty() { - debug!(target: "ismp-rust", "Handling Errors {:?}", errors); + debug!(target: "pallet-ismp", "Handling Errors {:?}", errors); Self::deposit_event(Event::::HandlingErrors { errors }) } + if !module_dispatch_results.is_empty() { + debug!(target: "ismp-modules", "Module Callback Results {:?}", module_dispatch_results); + } + Ok(()) } diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index bf7333140..8f3880806 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -330,7 +330,7 @@ fn should_handle_get_request_responses_correctly() { dest_chain: StateMachine::Ethereum, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], - height: 2, + height: 3, timeout_timestamp: 1000, }; @@ -342,7 +342,7 @@ fn should_handle_get_request_responses_correctly() { nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], - height: 2, + height: 3, timeout_timestamp: 1000, }; ismp_rs::router::Request::Get(get) From 5508e2db6248d0edd20bc224e760f7cfbd4cda75 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 6 Jun 2023 13:20:06 +0100 Subject: [PATCH 138/182] bump ismp (#55) * bump ismp * fix benchmarking * bump --- Cargo.lock | 60 ++++++++++++++++----------------- ismp-assets/src/lib.rs | 37 +++++++++++--------- pallet-ismp/src/benchmarking.rs | 2 +- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 124eed421..bb1df52bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,9 +181,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -834,9 +834,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbc37d37da9e5bce8173f3a41b71d9bf3c674deebbaceacd0ebdabde76efb03" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ "android-tzdata", "iana-time-zone", @@ -910,9 +910,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28" dependencies = [ "clap_builder", "clap_derive", @@ -921,9 +921,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" dependencies = [ "anstream", "anstyle", @@ -934,9 +934,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b" dependencies = [ "heck", "proc-macro2", @@ -2141,9 +2141,9 @@ dependencies = [ [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -2298,9 +2298,9 @@ dependencies = [ [[package]] name = "fs4" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f5b6908aecca5812a4569056285e58c666588c9573ee59765bf1d3692699e2" +checksum = "7672706608ecb74ab2e055c68327ffc25ae4cac1e12349204fd5fb0f3487cce2" dependencies = [ "rustix 0.37.19", "windows-sys 0.48.0", @@ -2850,9 +2850,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1628c9d7519ae39b8badf02ed42b6c7fa23f28a0" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#90e322bd235c78d929649076f831c2f555a0a028" dependencies = [ "derive_more", "parity-scale-codec", @@ -3152,7 +3152,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1628c9d7519ae39b8badf02ed42b6c7fa23f28a0" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#90e322bd235c78d929649076f831c2f555a0a028" dependencies = [ "ismp", "parity-scale-codec", @@ -3350,9 +3350,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" [[package]] name = "libloading" @@ -4157,9 +4157,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebffdb73fe72e917997fad08bdbf31ac50b0fa91cec93e69a0662e4264d454c" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -4557,9 +4557,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -4874,9 +4874,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" @@ -5702,7 +5702,7 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ - "aho-corasick 1.0.1", + "aho-corasick 1.0.2", "memchr", "regex-syntax 0.7.2", ] @@ -8926,12 +8926,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", ] diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index 3ec6db017..943fab868 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -24,7 +24,7 @@ use alloc::string::ToString; use frame_support::{traits::fungible::Mutate, PalletId}; use ismp::{ module::IsmpModule, - router::{Request, Response}, + router::{Post, Request, Response}, }; pub use pallet::*; @@ -125,6 +125,16 @@ pub mod pallet { params: TransferParams::Balance>, ) -> DispatchResult { let origin = ensure_signed(origin)?; + + // first, burn the requested amount + >::burn_from( + &origin, + params.amount.into(), + Precision::Exact, + Fortitude::Force, + )?; + + // next, construct the request to be sent out let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; let post = DispatchPost { dest_chain: params.dest_chain, @@ -134,22 +144,20 @@ pub mod pallet { data: payload.encode(), }; + // dispatch the request let dispatcher = T::IsmpDispatcher::default(); dispatcher .dispatch_request(DispatchRequest::Post(post)) .map_err(|_| Error::::TransferFailed)?; - >::burn_from( - &origin, - params.amount.into(), - Precision::Exact, - Fortitude::Force, - )?; + + // let the user know, they've successfully sent the funds Self::deposit_event(Event::::BalanceTransferred { from: payload.from, to: payload.to, amount: payload.amount, dest_chain: params.dest_chain, }); + Ok(()) } @@ -217,16 +225,13 @@ fn ismp_dispatch_error(msg: &str) -> ismp::error::Error { } impl IsmpModule for Pallet { - fn on_accept(request: Request) -> Result<(), ismp::error::Error> { - let source_chain = request.source_chain(); - let data = match request { - Request::Post(post) => post.data, - _ => Err(ismp_dispatch_error("Only Post requests allowed, found Get"))?, - }; + fn on_accept(request: Post) -> Result<(), ismp::error::Error> { + let source_chain = request.source_chain; - let payload = - ::Balance> as codec::Decode>::decode(&mut &*data) - .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + let payload = ::Balance> as codec::Decode>::decode( + &mut &*request.data, + ) + .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; >::mint_into(&payload.to, payload.amount.into()) .map_err(|_| ismp_dispatch_error("Failed to mint funds"))?; Pallet::::deposit_event(Event::::BalanceReceived { diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index c9d9fbd5e..aa8c891b0 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -134,7 +134,7 @@ pub mod benchmarks { /// module id for the mock benchmarking module pub const MODULE_ID: PalletId = PalletId(*b"benchmak"); impl IsmpModule for BenchmarkIsmpModule { - fn on_accept(_request: Request) -> Result<(), IsmpError> { + fn on_accept(_request: Post) -> Result<(), IsmpError> { Ok(()) } From 50b35320c3258f6570d812d3c2b21ab2efaefd4f Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 6 Jun 2023 15:42:45 +0100 Subject: [PATCH 139/182] bump ismp (#56) * bump ismp * update ismp * sigh * cargo fmt * fix tests * cargo fmt --- Cargo.lock | 4 +- ismp-assets/src/lib.rs | 15 ++- pallet-ismp/src/benchmarking.rs | 10 +- pallet-ismp/src/mock.rs | 10 +- pallet-ismp/src/weight_info.rs | 227 +++++++++++++++----------------- 5 files changed, 131 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb1df52bd..fdf7268d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#90e322bd235c78d929649076f831c2f555a0a028" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c36738c8b6504f53a7d5f656cd4596fea0f4fbdc" dependencies = [ "derive_more", "parity-scale-codec", @@ -3152,7 +3152,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#90e322bd235c78d929649076f831c2f555a0a028" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c36738c8b6504f53a7d5f656cd4596fea0f4fbdc" dependencies = [ "ismp", "parity-scale-codec", diff --git a/ismp-assets/src/lib.rs b/ismp-assets/src/lib.rs index 943fab868..d374c12b0 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-assets/src/lib.rs @@ -53,7 +53,7 @@ pub mod pallet { /// Pallet Configuration #[pallet::config] - pub trait Config: frame_system::Config + pallet_balances::Config { + pub trait Config: frame_system::Config + pallet_balances::Config + pallet_ismp::Config { /// Overarching event type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Native balance @@ -136,8 +136,13 @@ pub mod pallet { // next, construct the request to be sent out let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; + let dest = match T::StateMachine::get() { + StateMachine::Kusama(_) => StateMachine::Kusama(params.dest_id), + StateMachine::Polkadot(_) => StateMachine::Polkadot(params.dest_id), + _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, + }; let post = DispatchPost { - dest_chain: params.dest_chain, + dest_chain: dest, from: PALLET_ID.0.to_vec(), to: PALLET_ID.0.to_vec(), timeout_timestamp: params.timeout, @@ -155,7 +160,7 @@ pub mod pallet { from: payload.from, to: payload.to, amount: payload.amount, - dest_chain: params.dest_chain, + dest_chain: dest, }); Ok(()) @@ -212,8 +217,8 @@ pub mod pallet { pub to: AccountId, /// Amount to transfer pub amount: Balance, - /// Destination chain's Id - pub dest_chain: StateMachine, + /// Destination parachain Id + pub dest_id: u32, /// Timeout timestamp on destination chain in seconds pub timeout: u64, } diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index aa8c891b0..7c44e0022 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -27,10 +27,10 @@ use frame_system::RawOrigin; /// In your module router configuration add the [`BenchmarkIsmpModule`] as one of the ismp modules /// using the pallet id defined here as it's module id. #[benchmarks( - where - ::Hash: From, - T: pallet_timestamp::Config, - ::Moment: From +where +::Hash: From, +T: pallet_timestamp::Config, +::Moment: From )] pub mod benchmarks { use super::*; @@ -232,7 +232,7 @@ pub mod benchmarks { }; let msg = RequestMessage { - requests: vec![Request::Post(post.clone())], + requests: vec![post.clone()], proof: Proof { height: intermediate_state.height, proof: vec![] }, }; let caller = whitelisted_caller(); diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index bd969a9a5..e07dac44f 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -21,7 +21,7 @@ use frame_support::traits::{ConstU32, ConstU64, Get}; use frame_system::EnsureRoot; use ismp_rs::{ consensus::ConsensusClient, - router::{DispatchResult, DispatchSuccess, IsmpRouter}, + router::{DispatchResult, DispatchSuccess, IsmpRouter, Post}, }; use sp_core::H256; use sp_runtime::{ @@ -116,10 +116,10 @@ impl Config for Test { pub struct ModuleRouter; impl IsmpRouter for ModuleRouter { - fn handle_request(&self, request: Request) -> DispatchResult { - let dest = request.dest_chain(); - let source = request.source_chain(); - let nonce = request.nonce(); + fn handle_request(&self, request: Post) -> DispatchResult { + let dest = request.dest_chain; + let source = request.source_chain; + let nonce = request.nonce; Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) } diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index 5925870d7..ae9e087d1 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -25,7 +25,7 @@ use ismp_rs::{ messaging::{ ConsensusMessage, FraudProofMessage, Message, Proof, ResponseMessage, TimeoutMessage, }, - router::{GetResponse, Request, Response}, + router::{GetResponse, Post, Request, Response}, }; /// A trait that provides information about how consensus client execute in the runtime @@ -85,7 +85,7 @@ impl ConsensusClientWeight for () { /// A trait that provides weight information about how module callbacks execute pub trait IsmpModuleWeight { /// Returns the weight used in processing this request - fn on_accept(&self, request: &Request) -> Weight; + fn on_accept(&self, request: &Post) -> Weight; /// Returns the weight used in processing this timeout fn on_timeout(&self, request: &Request) -> Weight; /// Returns the weight used in processing this response @@ -93,7 +93,7 @@ pub trait IsmpModuleWeight { } impl IsmpModuleWeight for () { - fn on_accept(&self, _request: &Request) -> Weight { + fn on_accept(&self, _request: &Post) -> Weight { Weight::zero() } @@ -165,143 +165,134 @@ impl WeightInfo for () { /// Returns the weight that would be consumed when executing a batch of messages pub fn get_weight(messages: &[Message]) -> Weight { - messages.into_iter().fold(Weight::zero(), |acc, msg| { - match msg { - Message::Consensus(msg) => { - let consensus_handler = - ::WeightProvider::consensus_client(msg.consensus_client_id) + messages.into_iter().fold(Weight::zero(), |acc, msg| match msg { + Message::Consensus(msg) => { + let consensus_handler = + ::WeightProvider::consensus_client(msg.consensus_client_id) + .unwrap_or(Box::new(())); + consensus_handler.verify_consensus(msg) + } + Message::Request(msg) => { + let state_machine = msg.proof.height.id; + let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = req.to.as_slice(); + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_accept(&req) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + msg.proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_membership(state_machine, msg.requests.len(), &msg.proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_request_message() + } + Message::Response(msg) => match msg { + ResponseMessage::Post { responses, proof } => { + let state_machine = proof.height.id; + let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { + let dest_module = match res { + Response::Post(ref post) => post.post.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) .unwrap_or(Box::new(())); - consensus_handler.verify_consensus(msg) + acc + handle.on_response(&res) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_membership(state_machine, responses.len(), &proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() } - Message::Request(msg) => { - let state_machine = msg.proof.height.id; - let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { + ResponseMessage::Get { requests, proof } => { + let state_machine = proof.height.id; + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Post(ref post) => post.to.as_slice(), - // Get requests are never submitted + Request::Get(ref get) => get.from.as_slice(), _ => return acc, }; let handle = ::WeightProvider::module_callback(dest_module) .unwrap_or(Box::new(())); - acc + handle.on_accept(&req) + acc + handle.on_response(&Response::Get(GetResponse { + get: req.get_request().unwrap(), + values: Default::default(), + })) }); let consensus_handler = ::WeightProvider::consensus_client( - msg.proof.height.id.consensus_client, + proof.height.id.consensus_client, ) .unwrap_or(Box::new(())); - let proof_verification_weight = consensus_handler.verify_membership( - state_machine, - msg.requests.len(), - &msg.proof, - ); + let proof_verification_weight = + consensus_handler.verify_state_proof(state_machine, requests.len(), &proof); acc + cb_weight + proof_verification_weight + - ::WeightInfo::handle_request_message() + ::WeightInfo::handle_response_message() } - Message::Response(msg) => match msg { - ResponseMessage::Post { responses, proof } => { - let state_machine = proof.height.id; - let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { - let dest_module = match res { - Response::Post(ref post) => post.post.from.as_slice(), - _ => return acc, - }; - let handle = ::WeightProvider::module_callback(dest_module) - .unwrap_or(Box::new(())); - acc + handle.on_response(&res) - }); - - let consensus_handler = ::WeightProvider::consensus_client( - proof.height.id.consensus_client, - ) - .unwrap_or(Box::new(())); + }, + Message::Timeout(msg) => match msg { + TimeoutMessage::Post { requests, timeout_proof } => { + let state_machine = timeout_proof.height.id; + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Post(ref post) => post.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) + .unwrap_or(Box::new(())); + acc + handle.on_timeout(&req) + }); - let proof_verification_weight = - consensus_handler.verify_membership(state_machine, responses.len(), &proof); - - acc + cb_weight + - proof_verification_weight + - ::WeightInfo::handle_response_message() - } - ResponseMessage::Get { requests, proof } => { - let state_machine = proof.height.id; - let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = match req { - Request::Get(ref get) => get.from.as_slice(), - _ => return acc, - }; - let handle = ::WeightProvider::module_callback(dest_module) - .unwrap_or(Box::new(())); - acc + handle.on_response(&Response::Get(GetResponse { - get: req.get_request().unwrap(), - values: Default::default(), - })) - }); - - let consensus_handler = ::WeightProvider::consensus_client( - proof.height.id.consensus_client, - ) - .unwrap_or(Box::new(())); + let consensus_handler = ::WeightProvider::consensus_client( + timeout_proof.height.id.consensus_client, + ) + .unwrap_or(Box::new(())); - let proof_verification_weight = - consensus_handler.verify_state_proof(state_machine, requests.len(), &proof); - - acc + cb_weight + - proof_verification_weight + - ::WeightInfo::handle_response_message() - } - }, - Message::Timeout(msg) => match msg { - TimeoutMessage::Post { requests, timeout_proof } => { - let state_machine = timeout_proof.height.id; - let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = match req { - Request::Post(ref post) => post.from.as_slice(), - _ => return acc, - }; - let handle = ::WeightProvider::module_callback(dest_module) - .unwrap_or(Box::new(())); - acc + handle.on_timeout(&req) - }); - - let consensus_handler = ::WeightProvider::consensus_client( - timeout_proof.height.id.consensus_client, - ) - .unwrap_or(Box::new(())); + let proof_verification_weight = consensus_handler.verify_state_proof( + state_machine, + requests.len(), + &timeout_proof, + ); - let proof_verification_weight = consensus_handler.verify_state_proof( - state_machine, - requests.len(), - &timeout_proof, - ); - - acc + cb_weight + - proof_verification_weight + - ::WeightInfo::handle_response_message() - } - TimeoutMessage::Get { requests } => { - let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = match req { - Request::Get(ref get) => get.from.as_slice(), - _ => return acc, - }; - let handle = ::WeightProvider::module_callback(dest_module) - .unwrap_or(Box::new(())); - acc + handle.on_timeout(&req) - }); - acc + cb_weight + ::WeightInfo::handle_timeout_message() - } - }, - - Message::FraudProof(msg) => { - let consensus_handler = - ::WeightProvider::consensus_client(msg.consensus_client_id) + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + } + TimeoutMessage::Get { requests } => { + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Get(ref get) => get.from.as_slice(), + _ => return acc, + }; + let handle = ::WeightProvider::module_callback(dest_module) .unwrap_or(Box::new(())); - consensus_handler.verify_fraud_proof(msg) + acc + handle.on_timeout(&req) + }); + acc + cb_weight + ::WeightInfo::handle_timeout_message() } + }, + + Message::FraudProof(msg) => { + let consensus_handler = + ::WeightProvider::consensus_client(msg.consensus_client_id) + .unwrap_or(Box::new(())); + consensus_handler.verify_fraud_proof(msg) } }) } From a90667e5fb697730680ccdd0f79281d7942e5c3b Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 7 Jun 2023 10:59:47 +0100 Subject: [PATCH 140/182] Refactor GET request demo (#57) * don't use statemachine id' * don't hardcode keys --- Cargo.lock | 3 +- Cargo.toml | 2 +- {ismp-assets => ismp-demo}/Cargo.toml | 3 +- {ismp-assets => ismp-demo}/src/lib.rs | 83 +++++++++++++-------------- 4 files changed, 45 insertions(+), 46 deletions(-) rename {ismp-assets => ismp-demo}/Cargo.toml (90%) rename {ismp-assets => ismp-demo}/src/lib.rs (84%) diff --git a/Cargo.lock b/Cargo.lock index fdf7268d9..2dcda4fc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3039,7 +3039,7 @@ dependencies = [ ] [[package]] -name = "ismp-assets" +name = "ismp-demo" version = "0.1.0" dependencies = [ "frame-support", @@ -3049,6 +3049,7 @@ dependencies = [ "pallet-ismp", "parity-scale-codec", "scale-info", + "sp-core 7.0.0", "sp-runtime", ] diff --git a/Cargo.toml b/Cargo.toml index 77502a0f8..eddef0bc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "parachain/inherent", "parachain/runtime-api", "parachain", - "ismp-assets" + "ismp-demo" ] [workspace.dependencies] diff --git a/ismp-assets/Cargo.toml b/ismp-demo/Cargo.toml similarity index 90% rename from ismp-assets/Cargo.toml rename to ismp-demo/Cargo.toml index b61f25a48..5fa3ebe64 100644 --- a/ismp-assets/Cargo.toml +++ b/ismp-demo/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ismp-assets" +name = "ismp-demo" version = "0.1.0" edition = "2021" authors = ["Polytope Labs "] @@ -20,6 +20,7 @@ frame-support = { default-features = false, git = "https://github.com/paritytech frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } # local pallet-ismp = { path = "../pallet-ismp", default-features = false } diff --git a/ismp-assets/src/lib.rs b/ismp-demo/src/lib.rs similarity index 84% rename from ismp-assets/src/lib.rs rename to ismp-demo/src/lib.rs index d374c12b0..32ddf83d6 100644 --- a/ismp-assets/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -34,7 +34,7 @@ pub const PALLET_ID: PalletId = PalletId(*b"ismp-ast"); #[frame_support::pallet] pub mod pallet { use super::*; - use alloc::vec; + use alloc::{collections::BTreeMap, vec}; use frame_support::{ pallet_prelude::*, traits::{ @@ -94,12 +94,7 @@ pub mod pallet { /// Get request dispatched GetRequestDispatched, /// Token issuance on some counterparty parachain - CounterpartyIssuance { - /// Parachain Id - chain: StateMachine, - /// Total issuance on counterparty parachain - total_issuance: u128, - }, + GetResponse(BTreeMap, Option>>), } /// Pallet Errors @@ -137,8 +132,8 @@ pub mod pallet { // next, construct the request to be sent out let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; let dest = match T::StateMachine::get() { - StateMachine::Kusama(_) => StateMachine::Kusama(params.dest_id), - StateMachine::Polkadot(_) => StateMachine::Polkadot(params.dest_id), + StateMachine::Kusama(_) => StateMachine::Kusama(params.para_id), + StateMachine::Polkadot(_) => StateMachine::Polkadot(params.para_id), _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, }; let post = DispatchPost { @@ -170,19 +165,20 @@ pub mod pallet { /// parachain #[pallet::weight(Weight::from_parts(1_000_000, 0))] #[pallet::call_index(1)] - pub fn counterparty_issuance( - origin: OriginFor, - dest_chain: StateMachine, - height: u64, - timeout: u64, - ) -> DispatchResult { + pub fn get_request(origin: OriginFor, params: GetRequest) -> DispatchResult { ensure_signed(origin)?; + let dest_chain = match T::StateMachine::get() { + StateMachine::Kusama(_) => StateMachine::Kusama(params.para_id), + StateMachine::Polkadot(_) => StateMachine::Polkadot(params.para_id), + _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, + }; + let get = DispatchGet { dest_chain, from: PALLET_ID.0.to_vec(), - keys: vec![pallet_balances::TotalIssuance::::hashed_key().to_vec()], - height, - timeout_timestamp: timeout, + keys: params.keys, + height: params.height as u64, + timeout_timestamp: params.timeout, }; let dispatcher = T::IsmpDispatcher::default(); @@ -208,6 +204,21 @@ pub mod pallet { pub amount: Balance, } + /// The get request payload + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct GetRequest { + /// Destination parachain + para_id: u32, + /// Height at which to read state + height: u32, + /// request timeout + timeout: u64, + /// Storage keys to read + keys: Vec>, + } + /// Extrinsic Parameters for initializing a cross chain transfer #[derive( Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, @@ -215,10 +226,13 @@ pub mod pallet { pub struct TransferParams { /// Destination account pub to: AccountId, + /// Amount to transfer pub amount: Balance, + /// Destination parachain Id - pub dest_id: u32, + pub para_id: u32, + /// Timeout timestamp on destination chain in seconds pub timeout: u64, } @@ -250,30 +264,13 @@ impl IsmpModule for Pallet { fn on_response(response: Response) -> Result<(), ismp::error::Error> { match response { - Response::Post(_) => { - Err(ismp_dispatch_error("Balance transfer protocol does not accept post responses")) - } - Response::Get(get_res) => { - let total_issuance = get_res - .values - .get(pallet_balances::TotalIssuance::::hashed_key().to_vec().as_slice()) - .cloned() - .flatten(); - - match total_issuance { - Some(total_issuance) => { - let total_issuance: u128 = codec::Decode::decode(&mut &*total_issuance) - .map_err(|_| ismp_dispatch_error("Failed to decode total issuance"))?; - Pallet::::deposit_event(Event::::CounterpartyIssuance { - chain: get_res.get.dest_chain, - total_issuance, - }); - Ok(()) - } - _ => Err(ismp_dispatch_error("Received None")), - } - } - } + Response::Post(_) => Err(ismp_dispatch_error( + "Balance transfer protocol does not accept post responses", + ))?, + Response::Get(res) => Pallet::::deposit_event(Event::::GetResponse(res.values)), + }; + + Ok(()) } fn on_timeout(request: Request) -> Result<(), ismp::error::Error> { From 80dcc58750343bccb2976be39eb7b4694e367c1a Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Thu, 8 Jun 2023 12:55:29 +0100 Subject: [PATCH 141/182] Adds Manual Testing Steps (#58) * manual testing steps * fix main --------- Co-authored-by: Seun Lanlege --- .github/workflows/ci.yml | 1 + README.md | 9 ++++++++- ismp-demo/src/lib.rs | 13 ++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ecdbd897..1a3c41284 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: run: | cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose cargo +nightly-2022-10-28 check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly-2022-10-28 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose - name: Test run: | diff --git a/README.md b/README.md index a15860b99..6faa3e751 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# pallet-ismp ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) [![Telegram: YourGroup](https://img.shields.io/badge/-Telegram-blue?style=flat-square&logo=Telegram&logoColor=white&link=https://t.me/YourGroup)](https://t.me/ismp_support) [![Discord: YourServer](https://img.shields.io/badge/-Discord-7289DA?style=flat-square&logo=Discord&logoColor=white&link=https://discord.gg/YourServer)](https://discord.gg/vKAa3XcCBX) +# pallet-ismp ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) [![Telegram: YourGroup](https://img.shields.io/badge/-Telegram-blue?style=flat-square&logo=Telegram&logoColor=white&link=https://t.me/YourGroup)](https://t.me/ismp_guide) [![Discord: YourServer](https://img.shields.io/badge/-Discord-7289DA?style=flat-square&logo=Discord&logoColor=white&link=https://discord.gg/YourServer)](https://discord.gg/vKAa3XcCBX) Implementation of the Interoperable State Machine Protocol for substrate runtimes. This project is [funded by the web3 foundation](https://github.com/w3f/Grants-Program/blob/master/applications/ismp.md). @@ -21,6 +21,13 @@ This repo holds all the required components substrate runtimes need to interoper Installation and integration guides can be found in the [book](https://substrate-ismp.polytope.technology). ## Testing and Testing Guide +This guide assumes [Rust](https://www.rust-lang.org/tools/install) and it's [nightly](https://rust-lang.github.io/rustup/concepts/channels.html#:~:text=it%20just%20run-,rustup%20toolchain%20install%20nightly,-%3A) version is installed, followed by calling the [init](https://github.com/paritytech/polkadot/blob/master/scripts/init.sh) script from the official Polkadot repo to initailize a WASM build environment. + +To run the unit tests associated with this library; +``` +cargo +nightly test -p pallet-ismp --all-targets --all-features +``` + Please see [CI](.github/workflows/ci.yml) for test coverage. ## Run Test in Docker diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index 32ddf83d6..991e2c7de 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -34,7 +34,7 @@ pub const PALLET_ID: PalletId = PalletId(*b"ismp-ast"); #[frame_support::pallet] pub mod pallet { use super::*; - use alloc::{collections::BTreeMap, vec}; + use alloc::{vec, vec::Vec}; use frame_support::{ pallet_prelude::*, traits::{ @@ -91,10 +91,8 @@ pub mod pallet { source_chain: StateMachine, }, - /// Get request dispatched - GetRequestDispatched, - /// Token issuance on some counterparty parachain - GetResponse(BTreeMap, Option>>), + /// Get response recieved + GetResponse(Vec>>), } /// Pallet Errors @@ -185,7 +183,6 @@ pub mod pallet { dispatcher .dispatch_request(DispatchRequest::Get(get)) .map_err(|_| Error::::GetDispatchFailed)?; - Self::deposit_event(Event::::GetRequestDispatched); Ok(()) } } @@ -267,7 +264,9 @@ impl IsmpModule for Pallet { Response::Post(_) => Err(ismp_dispatch_error( "Balance transfer protocol does not accept post responses", ))?, - Response::Get(res) => Pallet::::deposit_event(Event::::GetResponse(res.values)), + Response::Get(res) => Pallet::::deposit_event(Event::::GetResponse( + res.values.into_values().collect(), + )), }; Ok(()) From 033e8ab95e825c3efea3aab5c668155e7a4ce4f9 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Thu, 8 Jun 2023 18:46:27 +0100 Subject: [PATCH 142/182] Implement IsmpModule and router changes (#59) --- Cargo.lock | 4 +- ismp-demo/src/lib.rs | 56 ++++++++++++-------- pallet-ismp/src/benchmarking.rs | 22 ++++---- pallet-ismp/src/dispatcher.rs | 35 ++++--------- pallet-ismp/src/errors.rs | 91 +++------------------------------ pallet-ismp/src/handlers.rs | 39 +++++--------- pallet-ismp/src/lib.rs | 16 ++---- pallet-ismp/src/mock.rs | 37 +++++++------- pallet-ismp/src/primitives.rs | 14 ++++- pallet-ismp/src/weight_info.rs | 45 +++++++++++----- 10 files changed, 144 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2dcda4fc4..9b371c756 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c36738c8b6504f53a7d5f656cd4596fea0f4fbdc" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1e410a56814f08e356b729c6ea0e56515c87a41c" dependencies = [ "derive_more", "parity-scale-codec", @@ -3153,7 +3153,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c36738c8b6504f53a7d5f656cd4596fea0f4fbdc" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1e410a56814f08e356b729c6ea0e56515c87a41c" dependencies = [ "ismp", "parity-scale-codec", diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index 991e2c7de..e146b8592 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -23,13 +23,15 @@ extern crate alloc; use alloc::string::ToString; use frame_support::{traits::fungible::Mutate, PalletId}; use ismp::{ + error::Error as IsmpError, module::IsmpModule, router::{Post, Request, Response}, }; pub use pallet::*; +use pallet_ismp::primitives::ModuleId; /// Constant Pallet ID -pub const PALLET_ID: PalletId = PalletId(*b"ismp-ast"); +pub const PALLET_ID: ModuleId = ModuleId::Pallet(PalletId(*b"ismp-ast")); #[frame_support::pallet] pub mod pallet { @@ -136,8 +138,8 @@ pub mod pallet { }; let post = DispatchPost { dest_chain: dest, - from: PALLET_ID.0.to_vec(), - to: PALLET_ID.0.to_vec(), + from: PALLET_ID.encode(), + to: PALLET_ID.encode(), timeout_timestamp: params.timeout, data: payload.encode(), }; @@ -173,7 +175,7 @@ pub mod pallet { let get = DispatchGet { dest_chain, - from: PALLET_ID.0.to_vec(), + from: PALLET_ID.encode(), keys: params.keys, height: params.height as u64, timeout_timestamp: params.timeout, @@ -207,13 +209,13 @@ pub mod pallet { )] pub struct GetRequest { /// Destination parachain - para_id: u32, + pub para_id: u32, /// Height at which to read state - height: u32, + pub height: u32, /// request timeout - timeout: u64, + pub timeout: u64, /// Storage keys to read - keys: Vec>, + pub keys: Vec>, } /// Extrinsic Parameters for initializing a cross chain transfer @@ -235,21 +237,27 @@ pub mod pallet { } } -/// Ismp dispatch error -fn ismp_dispatch_error(msg: &str) -> ismp::error::Error { - ismp::error::Error::ImplementationSpecific(msg.to_string()) +/// Module callback for the pallet +pub struct IsmpModuleCallback(core::marker::PhantomData); + +impl Default for IsmpModuleCallback { + fn default() -> Self { + Self(core::marker::PhantomData) + } } -impl IsmpModule for Pallet { - fn on_accept(request: Post) -> Result<(), ismp::error::Error> { +impl IsmpModule for IsmpModuleCallback { + fn on_accept(&self, request: Post) -> Result<(), IsmpError> { let source_chain = request.source_chain; let payload = ::Balance> as codec::Decode>::decode( &mut &*request.data, ) - .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + .map_err(|_| { + IsmpError::ImplementationSpecific("Failed to decode request data".to_string()) + })?; >::mint_into(&payload.to, payload.amount.into()) - .map_err(|_| ismp_dispatch_error("Failed to mint funds"))?; + .map_err(|_| IsmpError::ImplementationSpecific("Failed to mint funds".to_string()))?; Pallet::::deposit_event(Event::::BalanceReceived { from: payload.from, to: payload.to, @@ -259,10 +267,10 @@ impl IsmpModule for Pallet { Ok(()) } - fn on_response(response: Response) -> Result<(), ismp::error::Error> { + fn on_response(&self, response: Response) -> Result<(), IsmpError> { match response { - Response::Post(_) => Err(ismp_dispatch_error( - "Balance transfer protocol does not accept post responses", + Response::Post(_) => Err(IsmpError::ImplementationSpecific( + "Balance transfer protocol does not accept post responses".to_string(), ))?, Response::Get(res) => Pallet::::deposit_event(Event::::GetResponse( res.values.into_values().collect(), @@ -272,20 +280,24 @@ impl IsmpModule for Pallet { Ok(()) } - fn on_timeout(request: Request) -> Result<(), ismp::error::Error> { + fn on_timeout(&self, request: Request) -> Result<(), IsmpError> { let source_chain = request.source_chain(); let data = match request { Request::Post(post) => post.data, - _ => Err(ismp_dispatch_error("Only Post requests allowed, found Get"))?, + _ => Err(IsmpError::ImplementationSpecific( + "Only Post requests allowed, found Get".to_string(), + ))?, }; let payload = ::Balance> as codec::Decode>::decode(&mut &*data) - .map_err(|_| ismp_dispatch_error("Failed to decode request data"))?; + .map_err(|_| { + IsmpError::ImplementationSpecific("Failed to decode request data".to_string()) + })?; >::mint_into( &payload.from, payload.amount.into(), ) - .map_err(|_| ismp_dispatch_error("Failed to mint funds"))?; + .map_err(|_| IsmpError::ImplementationSpecific("Failed to mint funds".to_string()))?; Pallet::::deposit_event(Event::::BalanceReceived { from: payload.from, to: payload.to, diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 7c44e0022..dbf700ddc 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -34,7 +34,7 @@ T: pallet_timestamp::Config, )] pub mod benchmarks { use super::*; - use crate::dispatcher::Receipt; + use crate::{dispatcher::Receipt, primitives::ModuleId}; use alloc::collections::BTreeMap; use frame_support::{traits::Hooks, PalletId}; use frame_system::EventRecord; @@ -132,17 +132,17 @@ pub mod benchmarks { /// This module should be added to the module router in runtime for benchmarks to pass pub struct BenchmarkIsmpModule; /// module id for the mock benchmarking module - pub const MODULE_ID: PalletId = PalletId(*b"benchmak"); + pub const MODULE_ID: ModuleId = ModuleId::Pallet(PalletId(*b"benchmak")); impl IsmpModule for BenchmarkIsmpModule { - fn on_accept(_request: Post) -> Result<(), IsmpError> { + fn on_accept(&self, _request: Post) -> Result<(), ismp_rs::error::Error> { Ok(()) } - fn on_response(_response: Response) -> Result<(), IsmpError> { + fn on_response(&self, _response: Response) -> Result<(), ismp_rs::error::Error> { Ok(()) } - fn on_timeout(_request: Request) -> Result<(), IsmpError> { + fn on_timeout(&self, _request: Request) -> Result<(), ismp_rs::error::Error> { Ok(()) } } @@ -225,8 +225,8 @@ pub mod benchmarks { source_chain: StateMachine::Ethereum, dest_chain: ::StateMachine::get(), nonce: 0, - from: MODULE_ID.0.to_vec(), - to: MODULE_ID.0.to_vec(), + from: MODULE_ID.encode(), + to: MODULE_ID.encode(), timeout_timestamp: 5000, data: "handle_request_message".as_bytes().to_vec(), }; @@ -253,8 +253,8 @@ pub mod benchmarks { source_chain: ::StateMachine::get(), dest_chain: StateMachine::Ethereum, nonce: 0, - from: MODULE_ID.0.to_vec(), - to: MODULE_ID.0.to_vec(), + from: MODULE_ID.encode(), + to: MODULE_ID.encode(), timeout_timestamp: 5000, data: "handle_response_message".as_bytes().to_vec(), }; @@ -287,8 +287,8 @@ pub mod benchmarks { source_chain: ::StateMachine::get(), dest_chain: StateMachine::Ethereum, nonce: 0, - from: MODULE_ID.0.to_vec(), - to: MODULE_ID.0.to_vec(), + from: MODULE_ID.encode(), + to: MODULE_ID.encode(), timeout_timestamp: 500, data: "handle_timeout_message".as_bytes().to_vec(), }; diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index 5bcf0140f..d3613b0e6 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -20,11 +20,9 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use ismp_primitives::mmr::Leaf; use ismp_rs::{ + error::Error as IsmpError, host::IsmpHost, - router::{ - DispatchError, DispatchRequest, DispatchResult, DispatchSuccess, Get, IsmpDispatcher, Post, - PostResponse, Request, Response, - }, + router::{DispatchRequest, Get, IsmpDispatcher, Post, PostResponse, Request, Response}, util::{hash_request, hash_response}, }; use sp_core::H256; @@ -50,7 +48,7 @@ where T: Config, ::Hash: From, { - fn dispatch_request(&self, request: DispatchRequest) -> DispatchResult { + fn dispatch_request(&self, request: DispatchRequest) -> Result<(), IsmpError> { let host = Host::::default(); let request = match request { DispatchRequest::Get(dispatch_get) => { @@ -83,11 +81,8 @@ where let (dest_chain, source_chain, nonce) = (request.dest_chain(), request.source_chain(), request.nonce()); - Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError { - msg: "Failed to push request into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, + Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| { + IsmpError::ImplementationSpecific("Failed to push request into mmr".to_string()) })?; // Deposit Event Pallet::::deposit_event(Event::Request { @@ -97,31 +92,23 @@ where }); // We need this step since it's not trivial to check the mmr for commitments on chain OutgoingRequestAcks::::insert(commitment, Receipt::Ok); - Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + Ok(()) } - fn dispatch_response(&self, response: PostResponse) -> DispatchResult { + fn dispatch_response(&self, response: PostResponse) -> Result<(), IsmpError> { let response = Response::Post(response); let commitment = hash_response::>(&response).0.to_vec(); if OutgoingResponseAcks::::contains_key(commitment.clone()) { - Err(DispatchError { - msg: "Duplicate response".to_string(), - nonce: response.nonce(), - source: response.source_chain(), - dest: response.dest_chain(), - })? + Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? } let (dest_chain, source_chain, nonce) = (response.dest_chain(), response.source_chain(), response.nonce()); - Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { - msg: "Failed to push response into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, + Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| { + IsmpError::ImplementationSpecific("Failed to push response into mmr".to_string()) })?; Pallet::::deposit_event(Event::Response { @@ -130,6 +117,6 @@ where source_chain, }); OutgoingResponseAcks::::insert(commitment, Receipt::Ok); - Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + Ok(()) } } diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index 8cf11c9e2..7e38c618c 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -19,7 +19,7 @@ use ismp_rs::{ consensus::{ConsensusClientId, StateMachineHeight}, error::Error as IsmpError, host::StateMachine, - router::DispatchResult, + module::DispatchResult, }; use sp_std::prelude::*; @@ -94,92 +94,14 @@ pub enum HandlingError { dest: StateMachine, }, InsufficientProofHeight, + ModuleNotFound(Vec), } -#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] -pub struct ModuleDispatchError { - /// Descriptive error message - pub msg: Vec, - /// Request nonce - pub nonce: u64, - /// Source chain for request or response - pub source_chain: StateMachine, - /// Destination chain for request or response - pub dest_chain: StateMachine, -} - -#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] -pub struct ModuleDispatchSuccess { - /// Destination chain for request or response - pub dest_chain: StateMachine, - /// Source chain for request or response - pub source_chain: StateMachine, - /// Request nonce - pub nonce: u64, -} - -#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +#[derive(Debug)] pub enum ModuleCallbackResult { - Response(Result), - Request(Result), - Timeout(Result), -} - -pub fn to_response_results(values: Vec) -> Vec { - values - .into_iter() - .map(|res| match res { - Ok(res) => ModuleCallbackResult::Response(Ok(ModuleDispatchSuccess { - dest_chain: res.dest_chain, - source_chain: res.source_chain, - nonce: res.nonce, - })), - Err(res) => ModuleCallbackResult::Response(Err(ModuleDispatchError { - msg: res.msg.as_bytes().to_vec(), - dest_chain: res.dest, - source_chain: res.source, - nonce: res.nonce, - })), - }) - .collect() -} - -pub fn to_request_results(values: Vec) -> Vec { - values - .into_iter() - .map(|res| match res { - Ok(res) => ModuleCallbackResult::Request(Ok(ModuleDispatchSuccess { - dest_chain: res.dest_chain, - source_chain: res.source_chain, - nonce: res.nonce, - })), - Err(res) => ModuleCallbackResult::Request(Err(ModuleDispatchError { - msg: res.msg.as_bytes().to_vec(), - dest_chain: res.dest, - source_chain: res.source, - nonce: res.nonce, - })), - }) - .collect() -} - -pub fn to_timeout_results(values: Vec) -> Vec { - values - .into_iter() - .map(|res| match res { - Ok(res) => ModuleCallbackResult::Timeout(Ok(ModuleDispatchSuccess { - dest_chain: res.dest_chain, - source_chain: res.source_chain, - nonce: res.nonce, - })), - Err(res) => ModuleCallbackResult::Timeout(Err(ModuleDispatchError { - msg: res.msg.as_bytes().to_vec(), - dest_chain: res.dest, - source_chain: res.source, - nonce: res.nonce, - })), - }) - .collect() + Response(Vec), + Request(Vec), + Timeout(Vec), } impl From for HandlingError { @@ -251,6 +173,7 @@ impl From for HandlingError { HandlingError::RequestTimeoutVerificationFailed { nonce, source, dest } } IsmpError::InsufficientProofHeight => HandlingError::InsufficientProofHeight, + IsmpError::ModuleNotFound(id) => HandlingError::ModuleNotFound(id), } } } diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index d8b31d89b..ea2b648c2 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -7,7 +7,8 @@ use crate::{ use alloc::string::ToString; use ismp_primitives::mmr::Leaf; use ismp_rs::{ - router::{DispatchError, DispatchResult, DispatchSuccess, Request, Response}, + error::Error as IsmpError, + router::{Request, Response}, util::{hash_request, hash_response}, }; use sp_core::H256; @@ -17,25 +18,17 @@ where ::Hash: From, { /// Handle an incoming request - pub fn handle_request(request: Request) -> DispatchResult { + pub fn handle_request(request: Request) -> Result<(), IsmpError> { let commitment = hash_request::>(&request).0.to_vec(); if IncomingRequestAcks::::contains_key(commitment.clone()) { - Err(DispatchError { - msg: "Duplicate request".to_string(), - nonce: request.nonce(), - source: request.source_chain(), - dest: request.dest_chain(), - })? + Err(IsmpError::ImplementationSpecific("Duplicate request".to_string()))? } let (dest_chain, source_chain, nonce) = (request.dest_chain(), request.source_chain(), request.nonce()); - Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| DispatchError { - msg: "Failed to push request into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, + Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| { + IsmpError::ImplementationSpecific("Failed to push request into mmr".to_string()) })?; // Deposit Event Pallet::::deposit_event(Event::Request { @@ -45,30 +38,22 @@ where }); IncomingRequestAcks::::insert(commitment, Receipt::Ok); - Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + Ok(()) } /// Handle an incoming response - pub fn handle_response(response: Response) -> DispatchResult { + pub fn handle_response(response: Response) -> Result<(), IsmpError> { let commitment = hash_response::>(&response).0.to_vec(); if IncomingResponseAcks::::contains_key(commitment.clone()) { - Err(DispatchError { - msg: "Duplicate response".to_string(), - nonce: response.nonce(), - source: response.source_chain(), - dest: response.dest_chain(), - })? + Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? } let (dest_chain, source_chain, nonce) = (response.dest_chain(), response.source_chain(), response.nonce()); - Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| DispatchError { - msg: "Failed to push response into mmr".to_string(), - nonce, - source: source_chain, - dest: dest_chain, + Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| { + IsmpError::ImplementationSpecific("Failed to push response into mmr".to_string()) })?; Pallet::::deposit_event(Event::Response { @@ -77,6 +62,6 @@ where source_chain, }); IncomingResponseAcks::::insert(commitment, Receipt::Ok); - Ok(DispatchSuccess { dest_chain, source_chain, nonce }) + Ok(()) } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 989aacc71..0a9901c40 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -51,7 +51,7 @@ use ismp_rs::{ use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. use crate::{ - errors::{to_request_results, to_response_results, to_timeout_results, HandlingError}, + errors::{HandlingError, ModuleCallbackResult}, mmr::mmr::Mmr, }; use ismp_primitives::{ @@ -391,7 +391,6 @@ where // Define a host let host = Host::::default(); let mut errors: Vec = vec![]; - let mut module_dispatch_results = vec![]; for message in messages { match handle_incoming_message(&host, message) { @@ -434,16 +433,13 @@ where } } Ok(MessageResult::Response(res)) => { - let results = to_response_results(res); - module_dispatch_results.extend(results); + debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Response(res)); } Ok(MessageResult::Request(res)) => { - let results = to_request_results(res); - module_dispatch_results.extend(results); + debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Request(res)); } Ok(MessageResult::Timeout(res)) => { - let results = to_timeout_results(res); - module_dispatch_results.extend(results); + debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Timeout(res)); } Err(err) => { errors.push(err.into()); @@ -457,10 +453,6 @@ where Self::deposit_event(Event::::HandlingErrors { errors }) } - if !module_dispatch_results.is_empty() { - debug!(target: "ismp-modules", "Module Callback Results {:?}", module_dispatch_results); - } - Ok(()) } diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index e07dac44f..21396f9d7 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -21,7 +21,8 @@ use frame_support::traits::{ConstU32, ConstU64, Get}; use frame_system::EnsureRoot; use ismp_rs::{ consensus::ConsensusClient, - router::{DispatchResult, DispatchSuccess, IsmpRouter, Post}, + module::IsmpModule, + router::{IsmpRouter, Post}, }; use sp_core::H256; use sp_runtime::{ @@ -113,29 +114,27 @@ impl Config for Test { } #[derive(Default)] -pub struct ModuleRouter; +pub struct MockModule; -impl IsmpRouter for ModuleRouter { - fn handle_request(&self, request: Post) -> DispatchResult { - let dest = request.dest_chain; - let source = request.source_chain; - let nonce = request.nonce; +impl IsmpModule for MockModule { + fn on_accept(&self, _request: Post) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } - Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) + fn on_response(&self, _response: Response) -> Result<(), ismp_rs::error::Error> { + Ok(()) } - fn handle_timeout(&self, request: Request) -> DispatchResult { - let dest = request.dest_chain(); - let source = request.source_chain(); - let nonce = request.nonce(); - Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) + fn on_timeout(&self, _request: Request) -> Result<(), ismp_rs::error::Error> { + Ok(()) } +} - fn handle_response(&self, response: Response) -> DispatchResult { - let request = &response.request(); - let dest = request.dest_chain(); - let source = request.source_chain(); - let nonce = request.nonce(); - Ok(DispatchSuccess { dest_chain: dest, source_chain: source, nonce }) +#[derive(Default)] +pub struct ModuleRouter; + +impl IsmpRouter for ModuleRouter { + fn module_for_id(&self, _bytes: Vec) -> Result, ismp_rs::error::Error> { + Ok(Box::new(MockModule)) } } diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 222469dfb..0ad9fc789 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -15,10 +15,11 @@ //! Pallet primitives use core::time::Duration; -use frame_support::RuntimeDebug; +use frame_support::{PalletId, RuntimeDebug}; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; use scale_info::TypeInfo; +use sp_core::{crypto::AccountId32, H160}; use sp_std::prelude::*; /// The `ConsensusEngineId` of ISMP. @@ -62,3 +63,14 @@ pub trait ConsensusClientProvider { /// Returns the challenge period configured for a consensus client fn challenge_period(id: ConsensusClientId) -> Duration; } + +/// Module identification types supported by ismp +#[derive(codec::Encode, codec::Decode, PartialEq, Eq, scale_info::TypeInfo)] +pub enum ModuleId { + /// Unique Pallet identification in runtime + Pallet(PalletId), + /// Contract account id + Contract(AccountId32), + /// Evm contract + Evm(H160), +} diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index ae9e087d1..c34fa4a59 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -17,7 +17,7 @@ //! This module provides a guide on how to provide static weights for consensus clients and module //! callbacks -use crate::Config; +use crate::{primitives::ModuleId, Config}; use alloc::boxed::Box; use frame_support::weights::Weight; use ismp_rs::{ @@ -112,7 +112,7 @@ pub trait WeightProvider { fn consensus_client(id: ConsensusClientId) -> Option>; /// Returns a reference to the weight provider for a module - fn module_callback(dest_module: &[u8]) -> Option>; + fn module_callback(dest_module: ModuleId) -> Option>; } impl WeightProvider for () { @@ -120,7 +120,7 @@ impl WeightProvider for () { None } - fn module_callback(_dest_module: &[u8]) -> Option> { + fn module_callback(_dest_module: ModuleId) -> Option> { None } } @@ -175,8 +175,10 @@ pub fn get_weight(messages: &[Message]) -> Weight { Message::Request(msg) => { let state_machine = msg.proof.height.id; let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = req.to.as_slice(); - let handle = ::WeightProvider::module_callback(dest_module) + let dest_module = codec::Decode::decode(&mut req.to.as_slice()).ok(); + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() .unwrap_or(Box::new(())); acc + handle.on_accept(&req) }); @@ -198,10 +200,15 @@ pub fn get_weight(messages: &[Message]) -> Weight { let state_machine = proof.height.id; let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { let dest_module = match res { - Response::Post(ref post) => post.post.from.as_slice(), + Response::Post(ref post) => { + codec::Decode::decode(&mut post.post.from.as_slice()).ok() + } _ => return acc, }; - let handle = ::WeightProvider::module_callback(dest_module) + + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() .unwrap_or(Box::new(())); acc + handle.on_response(&res) }); @@ -222,10 +229,14 @@ pub fn get_weight(messages: &[Message]) -> Weight { let state_machine = proof.height.id; let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Get(ref get) => get.from.as_slice(), + Request::Get(ref get) => { + codec::Decode::decode(&mut get.from.as_slice()).ok() + } _ => return acc, }; - let handle = ::WeightProvider::module_callback(dest_module) + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() .unwrap_or(Box::new(())); acc + handle.on_response(&Response::Get(GetResponse { get: req.get_request().unwrap(), @@ -251,10 +262,14 @@ pub fn get_weight(messages: &[Message]) -> Weight { let state_machine = timeout_proof.height.id; let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Post(ref post) => post.from.as_slice(), + Request::Post(ref post) => { + codec::Decode::decode(&mut post.from.as_slice()).ok() + } _ => return acc, }; - let handle = ::WeightProvider::module_callback(dest_module) + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() .unwrap_or(Box::new(())); acc + handle.on_timeout(&req) }); @@ -277,10 +292,14 @@ pub fn get_weight(messages: &[Message]) -> Weight { TimeoutMessage::Get { requests } => { let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Get(ref get) => get.from.as_slice(), + Request::Get(ref get) => { + codec::Decode::decode(&mut get.from.as_slice()).ok() + } _ => return acc, }; - let handle = ::WeightProvider::module_callback(dest_module) + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() .unwrap_or(Box::new(())); acc + handle.on_timeout(&req) }); From 19152dea3df2d9d7719a16894a2c016bb2475888 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 23 Jun 2023 14:27:26 +0100 Subject: [PATCH 143/182] Rpc method for fetching unfulfilled get requests (#61) --- Cargo.lock | 4 +-- pallet-ismp/rpc/src/lib.rs | 15 ++++++++++- pallet-ismp/runtime-api/src/lib.rs | 5 +++- pallet-ismp/src/benchmarking.rs | 30 +++++++++++++++------ pallet-ismp/src/dispatcher.rs | 13 +++++---- pallet-ismp/src/handlers.rs | 16 +++++++----- pallet-ismp/src/host.rs | 40 ++++++++++++---------------- pallet-ismp/src/lib.rs | 42 ++++++++++++++++++++---------- pallet-ismp/src/tests.rs | 6 +++-- parachain/src/consensus.rs | 2 +- 10 files changed, 109 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b371c756..4e4ea834d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1e410a56814f08e356b729c6ea0e56515c87a41c" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#daa50081f1624f7820a15ca718112cdf9cb2ef1d" dependencies = [ "derive_more", "parity-scale-codec", @@ -3153,7 +3153,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#1e410a56814f08e356b729c6ea0e56515c87a41c" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#daa50081f1624f7820a15ca718112cdf9cb2ef1d" dependencies = [ "ismp", "parity-scale-codec", diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index a40173271..0bb05e0e0 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -30,7 +30,7 @@ use ismp_primitives::{ }; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, - router::{Request, Response}, + router::{Get, Request, Response}, }; use ismp_runtime_api::IsmpRuntimeApi; use sc_client_api::{BlockBackend, ProofProvider}; @@ -128,6 +128,10 @@ where &self, block_numbers: Vec>, ) -> Result>>; + + /// Query pending get requests that have a `state_machine_height` <= `height`. + #[method(name = "ismp_pendingGetRequests")] + fn pending_get_requests(&self, height: u64) -> Result>; } /// An implementation of ISMP specific RPC methods. @@ -259,6 +263,15 @@ where }) } + fn pending_get_requests(&self, height: u64) -> Result> { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + + api.pending_get_requests(at) + .map(|reqs| reqs.into_iter().filter(|req| req.height <= height).collect()) + .map_err(|_| runtime_error_into_rpc_error("Error fetching get requests")) + } + fn query_events( &self, block_numbers: Vec>, diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index e5c976c79..94f03391a 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -20,7 +20,7 @@ use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, - router::{Request, Response}, + router::{Get, Request, Response}, }; use pallet_ismp::primitives::{Error, Proof}; @@ -66,6 +66,9 @@ sp_api::decl_runtime_apis! { /// Get actual requests fn get_requests(leaf_indices: Vec) -> Vec; + /// Fetch all Get requests that have received no response + fn pending_get_requests() -> Vec; + /// Get actual requests fn get_responses(leaf_indices: Vec) -> Vec; } diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index dbf700ddc..7888cda55 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -34,7 +34,7 @@ T: pallet_timestamp::Config, )] pub mod benchmarks { use super::*; - use crate::{dispatcher::Receipt, primitives::ModuleId}; + use crate::primitives::ModuleId; use alloc::collections::BTreeMap; use frame_support::{traits::Hooks, PalletId}; use frame_system::EventRecord; @@ -49,7 +49,7 @@ pub mod benchmarks { }, module::IsmpModule, router::{Post, PostResponse, RequestResponse}, - util::{hash_request, hash_response}, + util::hash_request, }; use sp_std::prelude::Vec; @@ -241,7 +241,7 @@ pub mod benchmarks { handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]); let commitment = hash_request::>(&Request::Post(post)); - assert!(IncomingRequestAcks::::get(commitment.0.to_vec()).is_some()); + assert!(RequestReceipts::::get(commitment.0.to_vec()).is_some()); } #[benchmark] @@ -261,10 +261,17 @@ pub mod benchmarks { let request = Request::Post(post.clone()); let commitment = hash_request::>(&request); - OutgoingRequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); + RequestCommitments::::insert( + commitment.0.to_vec(), + LeafIndexQuery { + source_chain: post.source_chain, + dest_chain: post.dest_chain, + nonce: post.nonce, + }, + ); let response = Response::Post(PostResponse { post, response: vec![] }); - let response_commitment = hash_response::>(&response); + let request_commitment = hash_request::>(&response.request()); let msg = ResponseMessage::Post { responses: vec![response], proof: Proof { height: intermediate_state.height, proof: vec![] }, @@ -275,7 +282,7 @@ pub mod benchmarks { #[extrinsic_call] handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]); - assert!(IncomingResponseAcks::::get(response_commitment.0.to_vec()).is_some()); + assert!(ResponseReceipts::::get(request_commitment.0.to_vec()).is_some()); } #[benchmark] @@ -295,7 +302,14 @@ pub mod benchmarks { let request = Request::Post(post.clone()); let commitment = hash_request::>(&request); - OutgoingRequestAcks::::insert(commitment.0.to_vec(), Receipt::Ok); + RequestCommitments::::insert( + commitment.0.to_vec(), + LeafIndexQuery { + source_chain: post.source_chain, + dest_chain: post.dest_chain, + nonce: post.nonce, + }, + ); let msg = TimeoutMessage::Post { requests: vec![request], @@ -306,7 +320,7 @@ pub mod benchmarks { #[extrinsic_call] handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]); - assert!(OutgoingRequestAcks::::get(commitment.0.to_vec()).is_none()); + assert!(RequestCommitments::::get(commitment.0.to_vec()).is_none()); } #[benchmark] diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index d3613b0e6..c3195fd9b 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -14,11 +14,11 @@ // limitations under the License. //! Implementation for the ISMP Router -use crate::{host::Host, Config, Event, OutgoingRequestAcks, OutgoingResponseAcks, Pallet}; +use crate::{host::Host, Config, Event, Pallet, RequestCommitments, ResponseCommitments}; use alloc::string::ToString; use codec::{Decode, Encode}; use core::marker::PhantomData; -use ismp_primitives::mmr::Leaf; +use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; use ismp_rs::{ error::Error as IsmpError, host::IsmpHost, @@ -91,7 +91,10 @@ where dest_chain, }); // We need this step since it's not trivial to check the mmr for commitments on chain - OutgoingRequestAcks::::insert(commitment, Receipt::Ok); + RequestCommitments::::insert( + commitment, + LeafIndexQuery { source_chain, dest_chain, nonce }, + ); Ok(()) } @@ -100,7 +103,7 @@ where let commitment = hash_response::>(&response).0.to_vec(); - if OutgoingResponseAcks::::contains_key(commitment.clone()) { + if ResponseCommitments::::contains_key(commitment.clone()) { Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? } @@ -116,7 +119,7 @@ where dest_chain, source_chain, }); - OutgoingResponseAcks::::insert(commitment, Receipt::Ok); + ResponseCommitments::::insert(commitment, Receipt::Ok); Ok(()) } } diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index ea2b648c2..282d4be37 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -1,11 +1,10 @@ //! Some extra utilities for pallet-ismp use crate::{ - dispatcher::Receipt, host::Host, Config, Event, IncomingRequestAcks, IncomingResponseAcks, - Pallet, + dispatcher::Receipt, host::Host, Config, Event, Pallet, RequestCommitments, ResponseCommitments, }; use alloc::string::ToString; -use ismp_primitives::mmr::Leaf; +use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; use ismp_rs::{ error::Error as IsmpError, router::{Request, Response}, @@ -21,7 +20,7 @@ where pub fn handle_request(request: Request) -> Result<(), IsmpError> { let commitment = hash_request::>(&request).0.to_vec(); - if IncomingRequestAcks::::contains_key(commitment.clone()) { + if RequestCommitments::::contains_key(commitment.clone()) { Err(IsmpError::ImplementationSpecific("Duplicate request".to_string()))? } @@ -37,7 +36,10 @@ where dest_chain, }); - IncomingRequestAcks::::insert(commitment, Receipt::Ok); + RequestCommitments::::insert( + commitment, + LeafIndexQuery { source_chain, dest_chain, nonce }, + ); Ok(()) } @@ -45,7 +47,7 @@ where pub fn handle_response(response: Response) -> Result<(), IsmpError> { let commitment = hash_response::>(&response).0.to_vec(); - if IncomingResponseAcks::::contains_key(commitment.clone()) { + if ResponseCommitments::::contains_key(commitment.clone()) { Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? } @@ -61,7 +63,7 @@ where dest_chain, source_chain, }); - IncomingResponseAcks::::insert(commitment, Receipt::Ok); + ResponseCommitments::::insert(commitment, Receipt::Ok); Ok(()) } } diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index a95b16ced..7fb8605fa 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -16,8 +16,8 @@ //! Host implementation for ISMP use crate::{ dispatcher::Receipt, primitives::ConsensusClientProvider, Config, ConsensusClientUpdateTime, - ConsensusStates, FrozenConsensusClients, FrozenHeights, IncomingRequestAcks, - IncomingResponseAcks, LatestStateMachineHeight, Nonce, OutgoingRequestAcks, StateCommitments, + ConsensusStates, FrozenConsensusClients, FrozenHeights, LatestStateMachineHeight, Nonce, + RequestCommitments, RequestReceipts, ResponseReceipts, StateCommitments, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -28,8 +28,8 @@ use ismp_rs::{ }, error::Error, host::{IsmpHost, StateMachine}, - router::{IsmpRouter, Request, Response}, - util::{hash_request, hash_response}, + router::{IsmpRouter, Request}, + util::hash_request, }; use sp_core::H256; use sp_runtime::SaturatedConversion; @@ -80,24 +80,18 @@ where ::now() } - fn request_commitment(&self, req: &Request) -> Result { - let commitment = hash_request::(req); - - let _ = OutgoingRequestAcks::::get(commitment.0.to_vec()).ok_or_else(|| { - Error::RequestCommitmentNotFound { - nonce: req.nonce(), - source: req.source_chain(), - dest: req.dest_chain(), - } + fn request_commitment(&self, commitment: H256) -> Result<(), Error> { + let _ = RequestCommitments::::get(commitment.0.to_vec()).ok_or_else(|| { + Error::ImplementationSpecific("Request commitment not found".to_string()) })?; - Ok(commitment) + Ok(()) } fn request_receipt(&self, req: &Request) -> Option<()> { let commitment = hash_request::(req); - let _ = IncomingRequestAcks::::get(commitment.0.to_vec()) + let _ = RequestReceipts::::get(commitment.0.to_vec()) .ok_or_else(|| Error::RequestCommitmentNotFound { nonce: req.nonce(), source: req.source_chain(), @@ -144,13 +138,13 @@ where fn delete_request_commitment(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); // We can't delete actual leaves in the mmr so this serves as a replacement for that - OutgoingRequestAcks::::remove(hash.0.to_vec()); + RequestCommitments::::remove(hash.0.to_vec()); Ok(()) } fn store_request_receipt(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); - IncomingRequestAcks::::insert(hash.0.to_vec(), Receipt::Ok); + RequestReceipts::::insert(hash.0.to_vec(), Receipt::Ok); Ok(()) } @@ -195,10 +189,10 @@ where nonce } - fn response_receipt(&self, res: &Response) -> Option<()> { - let commitment = hash_response::(res); + fn response_receipt(&self, res: &Request) -> Option<()> { + let commitment = hash_request::(res); - let _ = IncomingResponseAcks::::get(commitment.0.to_vec()) + let _ = ResponseReceipts::::get(commitment.0.to_vec()) .ok_or_else(|| Error::ImplementationSpecific("Response receipt not found".to_string())) .ok()?; @@ -210,9 +204,9 @@ where Ok(()) } - fn store_response_receipt(&self, res: &Response) -> Result<(), Error> { - let hash = hash_response::(res); - IncomingResponseAcks::::insert(hash.0.to_vec(), Receipt::Ok); + fn store_response_receipt(&self, req: &Request) -> Result<(), Error> { + let hash = hash_request::(req); + ResponseReceipts::::insert(hash.0.to_vec(), Receipt::Ok); Ok(()) } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 0a9901c40..7737be032 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -191,32 +191,32 @@ pub mod pallet { pub type ConsensusClientUpdateTime = StorageMap<_, Twox64Concat, ConsensusClientId, u64, OptionQuery>; - /// Acknowledgements for outgoing requests + /// Commitments for outgoing requests /// The key is the request commitment #[pallet::storage] - #[pallet::getter(fn outgoing_request_acks)] - pub type OutgoingRequestAcks = - StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; + #[pallet::getter(fn request_commitments)] + pub type RequestCommitments = + StorageMap<_, Blake2_128Concat, Vec, LeafIndexQuery, OptionQuery>; - /// Acknowledgements for outgoing responses + /// Commitments for outgoing responses /// The key is the response commitment #[pallet::storage] - #[pallet::getter(fn outgoing_response_acks)] - pub type OutgoingResponseAcks = + #[pallet::getter(fn response_commitments)] + pub type ResponseCommitments = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; - /// Acknowledgements for incoming requests + /// Receipts for incoming requests /// The key is the request commitment #[pallet::storage] - #[pallet::getter(fn request_acks)] - pub type IncomingRequestAcks = + #[pallet::getter(fn request_receipts)] + pub type RequestReceipts = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; - /// Acknowledgements for incoming responses - /// The key is the response commitment + /// Receipts for incoming responses + /// The key is the request commitment #[pallet::storage] - #[pallet::getter(fn response_acks)] - pub type IncomingResponseAcks = + #[pallet::getter(fn response_receipts)] + pub type ResponseReceipts = StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; /// Consensus update results still in challenge period @@ -551,6 +551,20 @@ where None } + /// Get unfulfilled Get requests + pub fn pending_get_requests() -> Vec { + RequestCommitments::::iter() + .filter_map(|(key, query)| { + let leaf_index = + Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, true)?; + let req = Self::get_request(leaf_index)?; + (req.is_type_get() && !ResponseReceipts::::contains_key(key)) + .then(|| req.get_request().ok()) + .flatten() + }) + .collect() + } + /// Return the scale encoded consensus state pub fn get_consensus_state(id: ConsensusClientId) -> Option> { ConsensusStates::::get(id) diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index 8f3880806..c6cdd7756 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -26,6 +26,7 @@ use ismp_rs::{ consensus::{IntermediateState, StateCommitment, StateMachineHeight}, messaging::{Proof, ResponseMessage, TimeoutMessage}, router::{DispatchGet, DispatchRequest, IsmpDispatcher}, + util::hash_request, }; use ismp_testsuite::{ check_challenge_period, check_client_expiry, frozen_check, mocks::MOCK_CONSENSUS_CLIENT_ID, @@ -312,7 +313,8 @@ fn should_handle_get_request_timeouts_correctly() { Pallet::::handle_messages(vec![Message::Timeout(timeout_msg)]).unwrap(); for request in requests { // commitments should not be found in storage after timeout has been processed - assert!(host.request_commitment(&request).is_err()) + let commitment = hash_request::>(&request); + assert!(host.request_commitment(commitment).is_err()) } }) } @@ -368,7 +370,7 @@ fn should_handle_get_request_responses_correctly() { Pallet::::handle_messages(vec![Message::Response(response)]).unwrap(); for request in requests { - assert!(host.request_receipt(&request).is_some()) + assert!(host.response_receipt(&request).is_some()) } }) } diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index df4716c70..87ff54187 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -309,7 +309,7 @@ where Request::Post(post) => { let request = Request::Post(post); let commitment = hash_request::>(&request).0.to_vec(); - keys.push(pallet_ismp::OutgoingRequestAcks::::hashed_key_for(commitment)); + keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); } Request::Get(_) => continue, } From 379bd1e7edf21dff4e35431288dd982b5654c1ac Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 4 Jul 2023 11:48:12 +0200 Subject: [PATCH 144/182] add serde derive (#63) --- Cargo.lock | 1 + parachain/Cargo.toml | 2 ++ parachain/src/consensus.rs | 11 ++++++----- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e4ea834d..ceda32b36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3070,6 +3070,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "scale-info", + "serde", "sp-consensus-aura", "sp-inherents", "sp-io", diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 1eca48147..02d8a767e 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -9,6 +9,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # crates.io +serde = { version = "1.0.136", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } hex-literal = "0.4.1" @@ -59,4 +60,5 @@ std = [ "ismp-primitives/std", "pallet-ismp/std", "hash-db/std", + "serde", ] diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 87ff54187..30d653bdb 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -75,7 +75,8 @@ pub struct ParachainConsensusProof { } /// Hashing algorithm for the state proof -#[derive(Debug, Encode, Decode)] +#[derive(Debug, Encode, Decode, Clone)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] pub enum HashAlgorithm { /// For chains that use keccak as their hashing algo Keccak, @@ -84,8 +85,8 @@ pub enum HashAlgorithm { } /// Holds the relevant data needed for state proof verification -#[derive(Debug, Encode, Decode)] -pub struct ParachainStateProof { +#[derive(Debug, Encode, Decode, Clone)] +pub struct SubstrateStateProof { /// Algorithm to use for state proof verification pub hasher: HashAlgorithm, /// Storage proof for the parachain headers @@ -93,7 +94,7 @@ pub struct ParachainStateProof { } /// Holds the relevant data needed for request/response proof verification -#[derive(Debug, Encode, Decode)] +#[derive(Debug, Encode, Decode, Clone)] pub struct MembershipProof { /// Size of the mmr at the time this proof was generated pub mmr_size: u64, @@ -325,7 +326,7 @@ where root: StateCommitment, proof: &Proof, ) -> Result, Option>>, Error> { - let state_proof: ParachainStateProof = codec::Decode::decode(&mut &*proof.proof) + let state_proof: SubstrateStateProof = codec::Decode::decode(&mut &*proof.proof) .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; let data = match state_proof.hasher { From 594cef606ee1a54a56fa19efe5f2ea0adfbdbc1e Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Fri, 7 Jul 2023 13:46:30 +0200 Subject: [PATCH 145/182] Dynamic ConsensusStateIds (#65) * bump ismp * fixed * add extrinsic for modifying unbonding periods * maybe don't use Duration * update ismp * cargo fmt * fix benchmarking * cargo fmt * fix benchmarking * fix tests * fix tests * fix std --------- Co-authored-by: David Salami --- Cargo.lock | 982 +++++++++++++++++--------------- pallet-ismp/src/benchmarking.rs | 195 ++----- pallet-ismp/src/errors.rs | 42 +- pallet-ismp/src/host.rs | 48 +- pallet-ismp/src/ismp_mocks.rs | 143 +++++ pallet-ismp/src/lib.rs | 53 +- pallet-ismp/src/mock.rs | 31 +- pallet-ismp/src/primitives.rs | 2 +- pallet-ismp/src/tests.rs | 52 +- pallet-ismp/src/weight_info.rs | 18 +- parachain/inherent/src/lib.rs | 2 +- parachain/src/consensus.rs | 10 +- parachain/src/lib.rs | 10 +- 13 files changed, 857 insertions(+), 731 deletions(-) create mode 100644 pallet-ismp/src/ismp_mocks.rs diff --git a/Cargo.lock b/Cargo.lock index ceda32b36..0992c94fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,11 +23,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ - "gimli 0.27.2", + "gimli 0.27.3", ] [[package]] @@ -90,9 +90,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher 0.4.4", @@ -120,7 +120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead 0.5.2", - "aes 0.8.2", + "aes 0.8.3", "cipher 0.4.4", "ctr 0.9.2", "ghash 0.5.0", @@ -153,7 +153,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -165,7 +165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -229,15 +229,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -302,9 +302,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "asn1-rs" @@ -319,7 +319,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.21", + "time 0.3.22", ] [[package]] @@ -335,7 +335,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.21", + "time 0.3.22", ] [[package]] @@ -404,9 +404,9 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.19", + "rustix 0.37.23", "slab", - "socket2", + "socket2 0.4.9", "waker-fn", ] @@ -421,13 +421,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -440,7 +440,7 @@ dependencies = [ "futures-sink", "futures-util", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", ] [[package]] @@ -468,16 +468,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "addr2line 0.19.0", + "addr2line 0.20.0", "cc", "cfg-if", "libc", - "miniz_oxide 0.6.2", - "object 0.30.3", + "miniz_oxide", + "object 0.31.1", "rustc-demangle", ] @@ -541,7 +541,7 @@ version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -561,6 +561,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "1.0.1" @@ -589,7 +595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "constant_time_eq", ] @@ -600,18 +606,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "constant_time_eq", ] [[package]] name = "blake3" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "cc", "cfg-if", "constant_time_eq", @@ -675,9 +681,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bounded-collections" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbd1d11282a1eb134d3c3b7cf8ce213b5161c6e5f73fb1b98618482c606b64" +checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" dependencies = [ "log", "parity-scale-codec", @@ -702,9 +708,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -910,9 +916,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.1" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" dependencies = [ "clap_builder", "clap_derive", @@ -921,27 +927,26 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.1" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -979,15 +984,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" [[package]] name = "constant_time_eq" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "convert_case" @@ -1031,9 +1036,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -1053,7 +1058,7 @@ version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", @@ -1184,14 +1189,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.8.0", + "memoffset 0.9.0", "scopeguard", ] @@ -1207,9 +1212,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -1330,7 +1335,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1518,9 +1523,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ "const-oid", "zeroize", @@ -1705,7 +1710,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1722,9 +1727,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" +checksum = "519b83cd10f5f6e969625a409f735182bea5558cd8b64c655806ceaae36f1999" [[package]] name = "dyn-clonable" @@ -1771,7 +1776,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ - "der 0.7.6", + "der 0.7.7", "digest 0.10.7", "elliptic-curve 0.13.5", "rfc6979 0.4.0", @@ -1894,6 +1899,12 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.3.1" @@ -1977,7 +1988,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -2018,7 +2029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" dependencies = [ "expander 0.0.4", - "indexmap", + "indexmap 1.9.3", "proc-macro-crate", "proc-macro2", "quote", @@ -2113,7 +2124,7 @@ checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide 0.7.1", + "miniz_oxide", ] [[package]] @@ -2134,7 +2145,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", ] @@ -2157,7 +2168,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-support", "frame-support-procedural", @@ -2194,9 +2205,9 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "bitflags", + "bitflags 1.3.2", "environmental", "frame-metadata", "frame-support-procedural", @@ -2227,7 +2238,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "Inflector", "cfg-expr", @@ -2237,35 +2248,35 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-support", "log", @@ -2298,11 +2309,11 @@ dependencies = [ [[package]] name = "fs4" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7672706608ecb74ab2e055c68327ffc25ae4cac1e12349204fd5fb0f3487cce2" +checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" dependencies = [ - "rustix 0.37.19", + "rustix 0.38.3", "windows-sys 0.48.0", ] @@ -2372,7 +2383,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "waker-fn", ] @@ -2384,7 +2395,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -2429,7 +2440,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "pin-utils", "slab", ] @@ -2476,9 +2487,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -2502,7 +2513,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.6.0", + "polyval 0.6.1", ] [[package]] @@ -2512,15 +2523,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "glob" @@ -2565,9 +2576,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -2575,7 +2586,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2615,6 +2626,12 @@ dependencies = [ "ahash 0.8.3", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -2632,18 +2649,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2742,7 +2750,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", ] [[package]] @@ -2771,9 +2779,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2785,8 +2793,8 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.9", - "socket2", + "pin-project-lite 0.2.10", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2810,9 +2818,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2927,6 +2935,16 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "inout" version = "0.1.3" @@ -2985,7 +3003,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.2", "libc", "windows-sys 0.48.0", ] @@ -2998,44 +3016,44 @@ checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" [[package]] name = "ipconfig" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.3", "widestring", - "winapi", + "windows-sys 0.48.0", "winreg", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.19", + "hermit-abi 0.3.2", + "rustix 0.38.3", "windows-sys 0.48.0", ] [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#daa50081f1624f7820a15ca718112cdf9cb2ef1d" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#823da35f3e4ed27386ad775482ccd76e774edd5b" dependencies = [ "derive_more", "parity-scale-codec", "primitive-types", "scale-info", "serde", + "serde_json", ] [[package]] @@ -3154,7 +3172,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#daa50081f1624f7820a15ca718112cdf9cb2ef1d" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#823da35f3e4ed27386ad775482ccd76e774edd5b" dependencies = [ "ismp", "parity-scale-codec", @@ -3173,9 +3191,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "jobserver" @@ -3188,9 +3206,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -3215,7 +3233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "async-trait", "beef", "futures-channel", @@ -3293,7 +3311,7 @@ dependencies = [ "ecdsa 0.16.7", "elliptic-curve 0.13.5", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -3352,9 +3370,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.145" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libloading" @@ -3387,7 +3405,7 @@ dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.9", + "getrandom 0.2.10", "instant", "libp2p-core 0.38.0", "libp2p-dns", @@ -3438,7 +3456,7 @@ dependencies = [ "rand 0.8.5", "rw-stream-sink", "sec1 0.3.0", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "unsigned-varint", @@ -3522,7 +3540,7 @@ dependencies = [ "multihash 0.17.0", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "zeroize", ] @@ -3533,7 +3551,7 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "asynchronous-codec", "bytes", "either", @@ -3547,7 +3565,7 @@ dependencies = [ "prost", "prost-build", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "thiserror", "uint", @@ -3569,7 +3587,7 @@ dependencies = [ "log", "rand 0.8.5", "smallvec", - "socket2", + "socket2 0.4.9", "tokio", "trust-dns-proto", "void", @@ -3622,7 +3640,7 @@ dependencies = [ "prost", "prost-build", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "snow", "static_assertions", "thiserror", @@ -3730,7 +3748,7 @@ dependencies = [ "libc", "libp2p-core 0.38.0", "log", - "socket2", + "socket2 0.4.9", "tokio", ] @@ -3922,9 +3940,9 @@ dependencies = [ [[package]] name = "linregress" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" +checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" dependencies = [ "nalgebra", ] @@ -3941,11 +3959,17 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -3953,9 +3977,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lru" @@ -4025,7 +4049,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -4065,7 +4089,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.19", + "rustix 0.37.23", ] [[package]] @@ -4088,9 +4112,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -4139,15 +4163,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -4255,7 +4270,7 @@ dependencies = [ "core2", "digest 0.10.7", "multihash-derive", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "unsigned-varint", ] @@ -4366,7 +4381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "netlink-packet-core", @@ -4419,7 +4434,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.6.5", @@ -4473,7 +4488,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "itoa", ] @@ -4510,11 +4525,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.2", "libc", ] @@ -4526,15 +4541,15 @@ checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", "hashbrown 0.12.3", - "indexmap", + "indexmap 1.9.3", "memchr", ] [[package]] name = "object" -version = "0.30.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] @@ -4630,7 +4645,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4641,7 +4656,7 @@ checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4657,7 +4672,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-benchmarking", "frame-support", @@ -4698,7 +4713,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-benchmarking", "frame-support", @@ -4715,9 +4730,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4890dcb9556136a4ec2b0c51fa4a08c8b733b829506af8fff2e853f3a065985b" +checksum = "0dab3ac198341b2f0fec6e7f8a6eeed07a41201d98a124260611598c142e76df" dependencies = [ "blake2", "crc32fast", @@ -4735,11 +4750,11 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.5.0" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" +checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bitvec", "byte-slice-cast", "bytes", @@ -4750,9 +4765,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4796,7 +4811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -4815,22 +4830,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.1", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" [[package]] name = "pbkdf2" @@ -4887,27 +4902,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -4918,9 +4933,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -4944,7 +4959,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.6", + "der 0.7.7", "spki 0.7.2", ] @@ -4963,7 +4978,7 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-core-primitives" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "parity-scale-codec", "scale-info", @@ -4975,7 +4990,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "lazy_static", "log", @@ -4993,7 +5008,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bs58", "futures", @@ -5012,7 +5027,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "async-trait", "derive_more", @@ -5034,7 +5049,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bounded-vec", "futures", @@ -5056,7 +5071,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "async-trait", "derive_more", @@ -5079,7 +5094,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "async-trait", "futures", @@ -5102,7 +5117,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bounded-collections", "derive_more", @@ -5119,7 +5134,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bitvec", "hex-literal 0.4.1", @@ -5145,7 +5160,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -5159,12 +5174,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", "log", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "windows-sys 0.48.0", ] @@ -5193,9 +5208,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if", "cpufeatures", @@ -5320,14 +5335,14 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] @@ -5491,9 +5506,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -5563,7 +5578,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -5620,7 +5635,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.21", + "time 0.3.22", "x509-parser 0.13.2", "yasna", ] @@ -5633,7 +5648,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.21", + "time 0.3.22", "yasna", ] @@ -5643,7 +5658,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -5652,7 +5667,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -5661,29 +5676,29 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -5700,13 +5715,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.3" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" dependencies = [ "aho-corasick 1.0.2", "memchr", - "regex-syntax 0.7.2", + "regex-automata 0.3.0", + "regex-syntax 0.7.3", ] [[package]] @@ -5718,6 +5734,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" +dependencies = [ + "aho-corasick 1.0.2", + "memchr", + "regex-syntax 0.7.3", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -5726,9 +5753,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] name = "region" @@ -5736,7 +5763,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "mach", "winapi", @@ -5897,11 +5924,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.14" +version = "0.36.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -5911,11 +5938,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -5923,6 +5950,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", + "windows-sys 0.48.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -5950,9 +5990,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -5962,18 +6002,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.2", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" [[package]] name = "rw-stream-sink" @@ -5988,15 +6028,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "safe_arch" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" dependencies = [ "bytemuck", ] @@ -6004,7 +6044,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "log", "sp-core 7.0.0", @@ -6015,7 +6055,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures", @@ -6043,7 +6083,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -6058,7 +6098,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -6077,18 +6117,18 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "chrono", @@ -6128,7 +6168,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "fnv", "futures", @@ -6154,7 +6194,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "hash-db", "kvdb", @@ -6180,7 +6220,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures", @@ -6205,7 +6245,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "lru 0.8.1", "parity-scale-codec", @@ -6229,7 +6269,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -6242,7 +6282,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "log", "sc-allocator", @@ -6255,14 +6295,14 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "anyhow", "cfg-if", "libc", "log", "once_cell", - "rustix 0.36.14", + "rustix 0.36.15", "sc-allocator", "sc-executor-common", "sp-runtime-interface 7.0.0", @@ -6273,7 +6313,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "ansi_term", "futures", @@ -6289,7 +6329,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "async-trait", @@ -6304,7 +6344,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "async-channel", @@ -6349,7 +6389,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "cid", "futures", @@ -6369,11 +6409,11 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "futures", "futures-timer", @@ -6397,7 +6437,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "futures", @@ -6419,7 +6459,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "async-trait", @@ -6453,7 +6493,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "futures", @@ -6473,7 +6513,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "bytes", @@ -6504,7 +6544,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "futures", "libp2p", @@ -6517,7 +6557,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "futures", "jsonrpsee", @@ -6547,7 +6587,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -6566,7 +6606,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "http", "jsonrpsee", @@ -6581,7 +6621,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", "futures", @@ -6607,7 +6647,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "directories", @@ -6673,7 +6713,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "log", "parity-scale-codec", @@ -6684,7 +6724,7 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "clap", "fs4", @@ -6700,7 +6740,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "futures", "libc", @@ -6719,7 +6759,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "chrono", "futures", @@ -6738,7 +6778,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "ansi_term", "atty", @@ -6769,18 +6809,18 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures", @@ -6807,7 +6847,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures", @@ -6821,7 +6861,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-channel", "futures", @@ -6835,9 +6875,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b569c32c806ec3abdf3b5869fb8bf1e0d275a7c1c9b0b05603d9464632649edf" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "bitvec", "cfg-if", @@ -6849,9 +6889,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6861,11 +6901,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -6956,7 +6996,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ "base16ct 0.2.0", - "der 0.7.6", + "der 0.7.7", "generic-array 0.14.7", "pkcs8 0.10.2", "subtle", @@ -6996,7 +7036,7 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -7021,29 +7061,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", @@ -7101,9 +7141,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -7200,9 +7240,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "snap" @@ -7223,7 +7263,7 @@ dependencies = [ "rand_core 0.6.4", "ring", "rustc_version", - "sha2 0.10.6", + "sha2 0.10.7", "subtle", ] @@ -7237,6 +7277,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -7257,7 +7307,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "hash-db", "log", @@ -7277,7 +7327,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "Inflector", "blake2", @@ -7285,13 +7335,13 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "scale-info", @@ -7304,7 +7354,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "integer-sqrt", "num-traits", @@ -7318,7 +7368,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "scale-info", @@ -7331,7 +7381,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "sp-api", @@ -7343,7 +7393,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "futures", "log", @@ -7361,7 +7411,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures", @@ -7376,7 +7426,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "parity-scale-codec", @@ -7394,7 +7444,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "parity-scale-codec", @@ -7415,7 +7465,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "finality-grandpa", "log", @@ -7433,7 +7483,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "scale-info", @@ -7445,10 +7495,10 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "array-bytes", - "bitflags", + "bitflags 1.3.2", "blake2", "bounded-collections", "bs58", @@ -7493,7 +7543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" dependencies = [ "array-bytes", - "bitflags", + "bitflags 1.3.2", "blake2", "bounded-collections", "bs58", @@ -7533,12 +7583,12 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.7", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "sp-std 5.0.0", "twox-hash", @@ -7553,7 +7603,7 @@ dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.7", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "sp-std 7.0.0", "twox-hash", @@ -7562,18 +7612,18 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "proc-macro2", "quote", "sp-core-hashing 5.0.0", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -7582,11 +7632,11 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -7603,7 +7653,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "environmental", "parity-scale-codec", @@ -7626,7 +7676,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -7641,7 +7691,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "bytes", "ed25519", @@ -7667,7 +7717,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "lazy_static", "sp-core 7.0.0", @@ -7678,7 +7728,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "futures", "parity-scale-codec", @@ -7692,7 +7742,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "thiserror", "zstd 0.12.3+zstd.1.5.2", @@ -7701,7 +7751,7 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -7712,7 +7762,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "sp-api", "sp-core 7.0.0", @@ -7722,7 +7772,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "backtrace", "lazy_static", @@ -7732,7 +7782,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "rustc-hash", "serde", @@ -7742,7 +7792,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "either", "hash256-std-hasher", @@ -7764,7 +7814,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -7801,13 +7851,13 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -7826,7 +7876,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "scale-info", @@ -7840,7 +7890,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "scale-info", @@ -7853,7 +7903,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "hash-db", "log", @@ -7873,7 +7923,7 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" [[package]] name = "sp-std" @@ -7884,7 +7934,7 @@ checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "impl-serde", "parity-scale-codec", @@ -7911,7 +7961,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures-timer", @@ -7926,7 +7976,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "sp-std 5.0.0", @@ -7951,7 +8001,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "sp-api", "sp-runtime", @@ -7960,7 +8010,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "log", @@ -7976,7 +8026,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "ahash 0.8.3", "hash-db", @@ -7999,7 +8049,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "impl-serde", "parity-scale-codec", @@ -8016,18 +8066,18 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -8056,7 +8106,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", "scale-info", @@ -8091,14 +8141,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.6", + "der 0.7.7", ] [[package]] name = "ss58-registry" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" +checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" dependencies = [ "Inflector", "num-format", @@ -8127,7 +8177,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg_aliases", "libc", "parking_lot 0.11.2", @@ -8212,7 +8262,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#569aae5341ea0c1d10426fa1ec13a36c0b64393b" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "hyper", "log", @@ -8249,9 +8299,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", @@ -8276,7 +8326,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] @@ -8299,21 +8349,22 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" +checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.19", - "windows-sys 0.45.0", + "rustix 0.37.23", + "windows-sys 0.48.0", ] [[package]] @@ -8333,22 +8384,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -8417,9 +8468,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ "itoa", "serde", @@ -8454,7 +8505,7 @@ dependencies = [ "pbkdf2 0.11.0", "rand 0.8.5", "rustc-hash", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -8488,19 +8539,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", "parking_lot 0.12.1", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "signal-hook-registry", - "socket2", + "socket2 0.4.9", "tokio-macros", "windows-sys 0.48.0", ] @@ -8513,7 +8565,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -8534,7 +8586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "tokio", "tokio-util", ] @@ -8549,7 +8601,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "tokio", "tracing", ] @@ -8565,17 +8617,17 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] @@ -8597,14 +8649,14 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-util", "http", "http-body", "http-range-header", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "tower-layer", "tower-service", ] @@ -8629,20 +8681,20 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", - "pin-project-lite 0.2.9", + "pin-project-lite 0.2.10", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -8668,7 +8720,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -8679,13 +8731,13 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "expander 2.0.0", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -8772,7 +8824,7 @@ dependencies = [ "lazy_static", "rand 0.8.5", "smallvec", - "socket2", + "socket2 0.4.9", "thiserror", "tinyvec", "tokio", @@ -8869,9 +8921,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-normalization" @@ -8945,11 +8997,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -8993,11 +9045,10 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -9021,9 +9072,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -9031,24 +9082,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -9058,9 +9109,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9068,22 +9119,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-instrument" @@ -9149,7 +9200,7 @@ version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] @@ -9162,7 +9213,7 @@ dependencies = [ "anyhow", "bincode", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", "object 0.29.0", @@ -9202,9 +9253,9 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.14", + "rustix 0.36.15", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "toml", "windows-sys 0.42.0", "zstd 0.11.2+zstd.1.5.2", @@ -9240,7 +9291,7 @@ dependencies = [ "anyhow", "cranelift-entity", "gimli 0.26.2", - "indexmap", + "indexmap 1.9.3", "log", "object 0.29.0", "serde", @@ -9282,7 +9333,7 @@ checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.36.14", + "rustix 0.36.15", ] [[package]] @@ -9305,7 +9356,7 @@ dependencies = [ "anyhow", "cc", "cfg-if", - "indexmap", + "indexmap 1.9.3", "libc", "log", "mach", @@ -9313,7 +9364,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.36.14", + "rustix 0.36.15", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -9334,9 +9385,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -9394,10 +9445,10 @@ dependencies = [ "sdp", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "stun", "thiserror", - "time 0.3.21", + "time 0.3.22", "tokio", "turn", "url", @@ -9457,7 +9508,7 @@ dependencies = [ "sec1 0.3.0", "serde", "sha1", - "sha2 0.10.6", + "sha2 0.10.7", "signature 1.6.4", "subtle", "thiserror", @@ -9499,7 +9550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" dependencies = [ "log", - "socket2", + "socket2 0.4.9", "thiserror", "tokio", "webrtc-util", @@ -9566,7 +9617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" dependencies = [ "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "cc", "ipnet", @@ -9593,9 +9644,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd0496a71f3cc6bc4bf0ed91346426a5099e93d89807e663162dc5a1069ff65" +checksum = "40018623e2dba2602a9790faba8d33f2ebdebf4b86561b83928db735f8784728" dependencies = [ "bytemuck", "safe_arch", @@ -9603,9 +9654,9 @@ dependencies = [ [[package]] name = "widestring" -version = "0.5.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -9657,7 +9708,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.1", ] [[package]] @@ -9690,7 +9741,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.1", ] [[package]] @@ -9710,9 +9761,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -9839,20 +9890,21 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -9902,7 +9954,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.21", + "time 0.3.22", ] [[package]] @@ -9920,13 +9972,13 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.21", + "time 0.3.22", ] [[package]] name = "xcm" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "bounded-collections", "derivative", @@ -9942,12 +9994,12 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#9b1fc27cec47f01a2c229532ee7ab79cc5bb28ef" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -9970,7 +10022,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.21", + "time 0.3.22", ] [[package]] @@ -9990,7 +10042,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 7888cda55..0f1e1145b 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -22,10 +22,10 @@ use frame_benchmarking::v2::*; use frame_system::RawOrigin; /// Running the benchmarks correctly. -/// Add the [`BenchmarkClient`] as one of the consensus clients available to pallet-ismp in the -/// runtime configuration. -/// In your module router configuration add the [`BenchmarkIsmpModule`] as one of the ismp modules -/// using the pallet id defined here as it's module id. +/// Add the [`crate::ismp_mocks::MockConsensusClient`] as one of the consensus clients available to +/// pallet-ismp in the runtime configuration. +/// In your module router configuration add the [`crate::ismp_mocks::MockModule`] as one of the ismp +/// modules using the [`crate::ismp_mocks::ModuleId`] as it's module id #[benchmarks( where ::Hash: From, @@ -34,24 +34,25 @@ T: pallet_timestamp::Config, )] pub mod benchmarks { use super::*; - use crate::primitives::ModuleId; - use alloc::collections::BTreeMap; - use frame_support::{traits::Hooks, PalletId}; + use crate::{ + host::Host, + ismp_mocks::{setup_mock_client, MOCK_CONSENSUS_STATE_ID, MODULE_ID}, + Config, Event, Pallet, RequestCommitments, RequestReceipts, ResponseReceipts, + }; + use codec::Encode; + use frame_support::traits::{Get, Hooks}; use frame_system::EventRecord; + use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; use ismp_rs::{ - consensus::{ - ConsensusClient, IntermediateState, StateCommitment, StateMachineClient, - StateMachineHeight, - }, - error::Error as IsmpError, + consensus::{StateCommitment, StateMachineId}, + host::{Ethereum, StateMachine}, messaging::{ - Message, Proof, RequestMessage, ResponseMessage, StateCommitmentHeight, TimeoutMessage, + CreateConsensusState, Message, Proof, RequestMessage, ResponseMessage, + StateCommitmentHeight, TimeoutMessage, }, - module::IsmpModule, - router::{Post, PostResponse, RequestResponse}, + router::{Post, PostResponse, Request, Response}, util::hash_request, }; - use sp_std::prelude::Vec; /// Verify the the last event emitted fn assert_last_event(generic_event: ::RuntimeEvent) { @@ -61,111 +62,17 @@ pub mod benchmarks { assert_eq!(event, &system_event); } - /// A mock consensus client for benchmarking - #[derive(Default)] - pub struct BenchmarkClient; - - /// Consensus client id for benchmarking consensus client - pub const BENCHMARK_CONSENSUS_CLIENT_ID: [u8; 4] = [1u8; 4]; - - impl ConsensusClient for BenchmarkClient { - fn verify_consensus( - &self, - _host: &dyn IsmpHost, - _trusted_consensus_state: Vec, - _proof: Vec, - ) -> Result<(Vec, BTreeMap), IsmpError> { - Ok(Default::default()) - } - - fn verify_fraud_proof( - &self, - _host: &dyn IsmpHost, - _trusted_consensus_state: Vec, - _proof_1: Vec, - _proof_2: Vec, - ) -> Result<(), IsmpError> { - Ok(()) - } - - fn unbonding_period(&self) -> Duration { - Duration::from_secs(60 * 60 * 60) - } - - fn state_machine( - &self, - _id: StateMachine, - ) -> Result, IsmpError> { - Ok(Box::new(BenchmarkStateMachine)) - } - } - - /// Mock State Machine - pub struct BenchmarkStateMachine; - - impl StateMachineClient for BenchmarkStateMachine { - fn verify_membership( - &self, - _host: &dyn IsmpHost, - _item: RequestResponse, - _root: StateCommitment, - _proof: &Proof, - ) -> Result<(), IsmpError> { - Ok(()) - } - - fn state_trie_key(&self, _request: Vec) -> Vec> { - Default::default() - } - - fn verify_state_proof( - &self, - _host: &dyn IsmpHost, - _keys: Vec>, - _root: StateCommitment, - _proof: &Proof, - ) -> Result, Option>>, IsmpError> { - Ok(Default::default()) - } - } - - /// This module should be added to the module router in runtime for benchmarks to pass - pub struct BenchmarkIsmpModule; - /// module id for the mock benchmarking module - pub const MODULE_ID: ModuleId = ModuleId::Pallet(PalletId(*b"benchmak")); - impl IsmpModule for BenchmarkIsmpModule { - fn on_accept(&self, _request: Post) -> Result<(), ismp_rs::error::Error> { - Ok(()) - } - - fn on_response(&self, _response: Response) -> Result<(), ismp_rs::error::Error> { - Ok(()) - } - - fn on_timeout(&self, _request: Request) -> Result<(), ismp_rs::error::Error> { - Ok(()) - } - } - - /// Sets the current timestamp - fn set_timestamp() - where - ::Moment: From, - { - pallet_timestamp::Pallet::::set_timestamp(1000_000_000u64.into()); - } - #[benchmark] fn create_consensus_client() { - set_timestamp::(); - - let message = CreateConsensusClient { + let message = CreateConsensusState { consensus_state: Default::default(), - consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID, + consensus_client_id: MOCK_CONSENSUS_STATE_ID, + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + unbonding_period: u64::MAX, state_machine_commitments: vec![( StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID, + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, }, StateCommitmentHeight { commitment: StateCommitment { @@ -182,47 +89,17 @@ pub mod benchmarks { _(RawOrigin::Root, message); assert_last_event::( - Event::ConsensusClientCreated { consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID } - .into(), + Event::ConsensusClientCreated { consensus_client_id: MOCK_CONSENSUS_STATE_ID }.into(), ); } - fn setup_mock_client(host: &H) -> IntermediateState { - let intermediate_state = IntermediateState { - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID, - }, - height: 1, - }, - commitment: StateCommitment { - timestamp: 1000, - overlay_root: None, - state_root: Default::default(), - }, - }; - - host.store_consensus_state(BENCHMARK_CONSENSUS_CLIENT_ID, vec![]).unwrap(); - host.store_consensus_update_time(BENCHMARK_CONSENSUS_CLIENT_ID, Duration::from_secs(1000)) - .unwrap(); - host.store_state_machine_commitment( - intermediate_state.height, - intermediate_state.commitment, - ) - .unwrap(); - - intermediate_state - } - // The Benchmark consensus client should be added to the runtime for these benchmarks to work #[benchmark] fn handle_request_message() { - set_timestamp::(); let host = Host::::default(); - let intermediate_state = setup_mock_client(&host); + let height = setup_mock_client::<_, T>(&host); let post = Post { - source_chain: StateMachine::Ethereum, + source_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), dest_chain: ::StateMachine::get(), nonce: 0, from: MODULE_ID.encode(), @@ -231,10 +108,8 @@ pub mod benchmarks { data: "handle_request_message".as_bytes().to_vec(), }; - let msg = RequestMessage { - requests: vec![post.clone()], - proof: Proof { height: intermediate_state.height, proof: vec![] }, - }; + let msg = + RequestMessage { requests: vec![post.clone()], proof: Proof { height, proof: vec![] } }; let caller = whitelisted_caller(); #[extrinsic_call] @@ -246,12 +121,11 @@ pub mod benchmarks { #[benchmark] fn handle_response_message() { - set_timestamp::(); let host = Host::::default(); - let intermediate_state = setup_mock_client(&host); + let height = setup_mock_client::<_, T>(&host); let post = Post { source_chain: ::StateMachine::get(), - dest_chain: StateMachine::Ethereum, + dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, from: MODULE_ID.encode(), to: MODULE_ID.encode(), @@ -274,7 +148,7 @@ pub mod benchmarks { let request_commitment = hash_request::>(&response.request()); let msg = ResponseMessage::Post { responses: vec![response], - proof: Proof { height: intermediate_state.height, proof: vec![] }, + proof: Proof { height, proof: vec![] }, }; let caller = whitelisted_caller(); @@ -287,12 +161,11 @@ pub mod benchmarks { #[benchmark] fn handle_timeout_message() { - set_timestamp::(); let host = Host::::default(); - let intermediate_state = setup_mock_client(&host); + let height = setup_mock_client::<_, T>(&host); let post = Post { source_chain: ::StateMachine::get(), - dest_chain: StateMachine::Ethereum, + dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, from: MODULE_ID.encode(), to: MODULE_ID.encode(), @@ -313,7 +186,7 @@ pub mod benchmarks { let msg = TimeoutMessage::Post { requests: vec![request], - timeout_proof: Proof { height: intermediate_state.height, proof: vec![] }, + timeout_proof: Proof { height, proof: vec![] }, }; let caller = whitelisted_caller(); diff --git a/pallet-ismp/src/errors.rs b/pallet-ismp/src/errors.rs index 7e38c618c..578541479 100644 --- a/pallet-ismp/src/errors.rs +++ b/pallet-ismp/src/errors.rs @@ -70,7 +70,7 @@ pub enum HandlingError { msg: Vec, }, UnbondingPeriodElapsed { - consensus_id: ConsensusClientId, + id: ConsensusClientId, }, MembershipProofVerificationFailed { msg: Vec, @@ -107,21 +107,25 @@ pub enum ModuleCallbackResult { impl From for HandlingError { fn from(value: ismp_rs::error::Error) -> Self { match value { - IsmpError::ChallengePeriodNotElapsed { consensus_id, current_time, update_time } => { - HandlingError::ChallengePeriodNotElapsed { - update_time: update_time.as_secs(), - current_time: current_time.as_secs(), - delay_period: None, - consensus_client_id: Some(consensus_id), - } - } - IsmpError::ConsensusStateNotFound { id } => { - HandlingError::ConsensusStateNotFound { id } + IsmpError::ChallengePeriodNotElapsed { + consensus_state_id, + current_time, + update_time, + } => HandlingError::ChallengePeriodNotElapsed { + update_time: update_time.as_secs(), + current_time: current_time.as_secs(), + delay_period: None, + consensus_client_id: Some(consensus_state_id), + }, + IsmpError::ConsensusStateNotFound { consensus_state_id } => { + HandlingError::ConsensusStateNotFound { id: consensus_state_id } } IsmpError::StateCommitmentNotFound { height } => { HandlingError::StateCommitmentNotFound { height } } - IsmpError::FrozenConsensusClient { id } => HandlingError::FrozenConsensusClient { id }, + IsmpError::FrozenConsensusClient { consensus_state_id } => { + HandlingError::FrozenConsensusClient { id: consensus_state_id } + } IsmpError::FrozenStateMachine { height } => { HandlingError::FrozenStateMachine { height } } @@ -144,8 +148,8 @@ impl From for HandlingError { IsmpError::ImplementationSpecific(msg) => { HandlingError::ImplementationSpecific { msg: msg.as_bytes().to_vec() } } - IsmpError::UnbondingPeriodElapsed { consensus_id } => { - HandlingError::UnbondingPeriodElapsed { consensus_id } + IsmpError::UnbondingPeriodElapsed { consensus_state_id } => { + HandlingError::UnbondingPeriodElapsed { id: consensus_state_id } } IsmpError::MembershipProofVerificationFailed(msg) => { HandlingError::MembershipProofVerificationFailed { msg: msg.as_bytes().to_vec() } @@ -174,6 +178,16 @@ impl From for HandlingError { } IsmpError::InsufficientProofHeight => HandlingError::InsufficientProofHeight, IsmpError::ModuleNotFound(id) => HandlingError::ModuleNotFound(id), + IsmpError::ConsensusStateIdNotRecognized { .. } => { + HandlingError::InsufficientProofHeight + } + IsmpError::ChallengePeriodNotConfigured { .. } => { + HandlingError::InsufficientProofHeight + } + IsmpError::DuplicateConsensusStateId { .. } => HandlingError::InsufficientProofHeight, + IsmpError::UnnbondingPeriodNotConfigured { .. } => { + HandlingError::InsufficientProofHeight + } } } } diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 7fb8605fa..daa84a72a 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -16,15 +16,17 @@ //! Host implementation for ISMP use crate::{ dispatcher::Receipt, primitives::ConsensusClientProvider, Config, ConsensusClientUpdateTime, - ConsensusStates, FrozenConsensusClients, FrozenHeights, LatestStateMachineHeight, Nonce, - RequestCommitments, RequestReceipts, ResponseReceipts, StateCommitments, + ConsensusStateClient, ConsensusStates, FrozenConsensusClients, FrozenHeights, + LatestStateMachineHeight, Nonce, RequestCommitments, RequestReceipts, ResponseReceipts, + StateCommitments, UnbondingPeriod, }; use alloc::{format, string::ToString}; use core::time::Duration; use frame_support::traits::{Get, UnixTime}; use ismp_rs::{ consensus::{ - ConsensusClient, ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId, + ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineHeight, + StateMachineId, }, error::Error, host::{IsmpHost, StateMachine}, @@ -73,7 +75,8 @@ where } fn consensus_state(&self, id: ConsensusClientId) -> Result, Error> { - ConsensusStates::::get(id).ok_or_else(|| Error::ConsensusStateNotFound { id }) + ConsensusStates::::get(id) + .ok_or_else(|| Error::ConsensusStateNotFound { consensus_state_id: id }) } fn timestamp(&self) -> Duration { @@ -159,7 +162,7 @@ where sp_io::hashing::keccak_256(bytes).into() } - fn challenge_period(&self, id: ConsensusClientId) -> Duration { + fn challenge_period(&self, id: ConsensusClientId) -> Option { ::ConsensusClientProvider::challenge_period(id) } @@ -176,9 +179,9 @@ where Ok(()) } - fn is_consensus_client_frozen(&self, client: ConsensusClientId) -> Result<(), Error> { + fn is_consensus_client_frozen(&self, client: ConsensusStateId) -> Result<(), Error> { if FrozenConsensusClients::::get(client) { - Err(Error::FrozenConsensusClient { id: client })? + Err(Error::FrozenConsensusClient { consensus_state_id: client })? } Ok(()) } @@ -199,7 +202,7 @@ where Some(()) } - fn freeze_consensus_client(&self, client: ConsensusClientId) -> Result<(), Error> { + fn freeze_consensus_client(&self, client: ConsensusStateId) -> Result<(), Error> { FrozenConsensusClients::::insert(client, true); Ok(()) } @@ -209,4 +212,33 @@ where ResponseReceipts::::insert(hash.0.to_vec(), Receipt::Ok); Ok(()) } + + fn consensus_client_id( + &self, + consensus_state_id: ConsensusStateId, + ) -> Option { + ConsensusStateClient::::get(&consensus_state_id) + } + + fn store_consensus_state_id( + &self, + consensus_state_id: ConsensusStateId, + client_id: ConsensusClientId, + ) -> Result<(), Error> { + ConsensusStateClient::::insert(consensus_state_id, client_id); + Ok(()) + } + + fn unbonding_period(&self, consensus_state_id: ConsensusStateId) -> Option { + UnbondingPeriod::::get(&consensus_state_id).map(Duration::from_secs) + } + + fn store_unbonding_period( + &self, + consensus_state_id: ConsensusStateId, + period: u64, + ) -> Result<(), Error> { + UnbondingPeriod::::insert(consensus_state_id, period); + Ok(()) + } } diff --git a/pallet-ismp/src/ismp_mocks.rs b/pallet-ismp/src/ismp_mocks.rs new file mode 100644 index 000000000..1720364a6 --- /dev/null +++ b/pallet-ismp/src/ismp_mocks.rs @@ -0,0 +1,143 @@ +//! Mocks used by both tests and benchmarks +use crate::primitives::ModuleId; +use alloc::collections::BTreeMap; +use frame_support::PalletId; +use ismp_rs::{ + consensus::{ + ConsensusClient, StateCommitment, StateMachineClient, StateMachineHeight, StateMachineId, + }, + error::Error as IsmpError, + handlers, + host::{Ethereum, IsmpHost, StateMachine}, + messaging::{CreateConsensusState, Proof, StateCommitmentHeight}, + module::IsmpModule, + router::{Post, Request, RequestResponse, Response}, +}; + +pub const MOCK_CONSENSUS_STATE_ID: [u8; 4] = *b"mock"; + +/// module id for the mock benchmarking module +pub const MODULE_ID: ModuleId = ModuleId::Pallet(PalletId(*b"___mock_")); + +fn set_timestamp(value: u64) +where + ::Moment: From, +{ + pallet_timestamp::Pallet::::set_timestamp(value.into()); +} + +#[derive(Default)] +pub struct MockModule; + +impl IsmpModule for MockModule { + fn on_accept(&self, _request: Post) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } + + fn on_response(&self, _response: Response) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } + + fn on_timeout(&self, _request: Request) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } +} + +/// A mock consensus client for benchmarking +#[derive(Default)] +pub struct MockConsensusClient; + +impl ConsensusClient for MockConsensusClient { + fn verify_consensus( + &self, + _host: &dyn IsmpHost, + _cs_id: ismp_rs::consensus::ConsensusStateId, + _trusted_consensus_state: Vec, + _proof: Vec, + ) -> Result<(Vec, BTreeMap), IsmpError> { + Ok(Default::default()) + } + + fn verify_fraud_proof( + &self, + _host: &dyn IsmpHost, + _trusted_consensus_state: Vec, + _proof_1: Vec, + _proof_2: Vec, + ) -> Result<(), IsmpError> { + Ok(()) + } + + fn state_machine(&self, _id: StateMachine) -> Result, IsmpError> { + Ok(Box::new(MockStateMachine)) + } +} + +/// Mock State Machine +pub struct MockStateMachine; + +impl StateMachineClient for MockStateMachine { + fn verify_membership( + &self, + _host: &dyn IsmpHost, + _item: RequestResponse, + _root: StateCommitment, + _proof: &Proof, + ) -> Result<(), IsmpError> { + Ok(()) + } + + fn state_trie_key(&self, _request: Vec) -> Vec> { + Default::default() + } + + fn verify_state_proof( + &self, + _host: &dyn IsmpHost, + _keys: Vec>, + _root: StateCommitment, + _proof: &Proof, + ) -> Result, Option>>, IsmpError> { + Ok(Default::default()) + } +} + +pub fn setup_mock_client(host: &H) -> StateMachineHeight +where + ::Moment: From, +{ + set_timestamp::(1000_000); + handlers::create_client( + host, + CreateConsensusState { + consensus_state: vec![], + consensus_client_id: MOCK_CONSENSUS_STATE_ID, + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + unbonding_period: 1_000_000, + state_machine_commitments: vec![( + StateMachineId { + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + }, + StateCommitmentHeight { + commitment: StateCommitment { + timestamp: 1000, + overlay_root: None, + state_root: Default::default(), + }, + height: 3, + }, + )], + }, + ) + .unwrap(); + + set_timestamp::(1000_000_000); + StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + }, + height: 3, + } +} diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 7737be032..4a557d3c2 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -27,6 +27,8 @@ mod errors; pub mod events; pub mod handlers; pub mod host; +#[cfg(any(feature = "runtime-benchmarks", test))] +mod ismp_mocks; mod mmr; #[cfg(test)] pub mod mock; @@ -45,7 +47,7 @@ use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, handlers::{handle_incoming_message, MessageResult}, host::StateMachine, - messaging::CreateConsensusClient, + messaging::CreateConsensusState, router::{Request, Response}, }; use sp_core::{offchain::StorageKind, H256}; @@ -80,7 +82,10 @@ pub mod pallet { use frame_system::pallet_prelude::*; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::{ - consensus::{ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId}, + consensus::{ + ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineHeight, + StateMachineId, + }, handlers::{self}, host::StateMachine, messaging::Message, @@ -171,12 +176,22 @@ pub mod pallet { pub type FrozenHeights = StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + /// A mapping of ConsensusStateId to ConsensusClientId + #[pallet::storage] + pub type ConsensusStateClient = + StorageMap<_, Blake2_128Concat, ConsensusStateId, ConsensusClientId, OptionQuery>; + + /// A mapping of ConsensusStateId to Unbonding periods + #[pallet::storage] + pub type UnbondingPeriod = + StorageMap<_, Blake2_128Concat, ConsensusStateId, u64, OptionQuery>; + /// Holds a map of consensus clients frozen due to byzantine /// behaviour #[pallet::storage] #[pallet::getter(fn frozen_consensus_clients)] pub type FrozenConsensusClients = - StorageMap<_, Blake2_128Concat, ConsensusClientId, bool, ValueQuery>; + StorageMap<_, Blake2_128Concat, ConsensusStateId, bool, ValueQuery>; /// The latest verified height for a state machine #[pallet::storage] @@ -275,6 +290,15 @@ pub mod pallet { fn offchain_worker(_n: T::BlockNumber) {} } + /// Params to update the unbonding period for a consensus state + #[derive(Debug, Clone, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] + pub struct UnbondingUpdate { + /// Consensus state identifier + consensus_state_id: ConsensusStateId, + /// Unbonding duration + unbonding_period: u64, + } + #[pallet::call] impl Pallet where @@ -295,7 +319,7 @@ pub mod pallet { #[pallet::call_index(1)] pub fn create_consensus_client( origin: OriginFor, - message: CreateConsensusClient, + message: CreateConsensusState, ) -> DispatchResult { T::AdminOrigin::ensure_origin(origin)?; let host = Host::::default(); @@ -309,6 +333,23 @@ pub mod pallet { Ok(()) } + + /// Set the unbonding period for a consensus state. + #[pallet::weight(::WeightInfo::create_consensus_client())] + #[pallet::call_index(2)] + pub fn set_unbonding_period( + origin: OriginFor, + message: UnbondingUpdate, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + let host = Host::::default(); + + host.store_unbonding_period(message.consensus_state_id, message.unbonding_period) + .map_err(|_| Error::::UnbondingPeriodUpdateFailed)?; + + Ok(()) + } } #[pallet::event] @@ -365,6 +406,8 @@ pub mod pallet { InvalidMessage, /// Encountered an error while creating the consensus client. ConsensusClientCreationFailed, + /// Couldn't update unbonding period + UnbondingPeriodUpdateFailed, } } @@ -398,7 +441,7 @@ where // check if this is a trusted state machine let is_trusted_state_machine = host .challenge_period(res.consensus_client_id.clone()) == - Duration::from_secs(0); + Some(Duration::from_secs(0)); if is_trusted_state_machine { for (_, latest_height) in res.state_updates.into_iter() { diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index 21396f9d7..3c752eff1 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -19,11 +19,9 @@ use crate::*; use crate::primitives::ConsensusClientProvider; use frame_support::traits::{ConstU32, ConstU64, Get}; use frame_system::EnsureRoot; -use ismp_rs::{ - consensus::ConsensusClient, - module::IsmpModule, - router::{IsmpRouter, Post}, -}; +use ismp_rs::{consensus::ConsensusClient, module::IsmpModule, router::IsmpRouter}; + +use crate::ismp_mocks::{MockConsensusClient, MockModule}; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -59,11 +57,11 @@ impl ConsensusClientProvider for ConsensusProvider { fn consensus_client( _id: ConsensusClientId, ) -> Result, ismp_rs::error::Error> { - Ok(Box::new(ismp_testsuite::mocks::MockClient)) + Ok(Box::new(MockConsensusClient)) } - fn challenge_period(_id: ConsensusClientId) -> Duration { - Duration::from_secs(60 * 60) + fn challenge_period(_id: ConsensusClientId) -> Option { + Some(Duration::from_secs(60 * 60)) } } @@ -113,23 +111,6 @@ impl Config for Test { type WeightProvider = (); } -#[derive(Default)] -pub struct MockModule; - -impl IsmpModule for MockModule { - fn on_accept(&self, _request: Post) -> Result<(), ismp_rs::error::Error> { - Ok(()) - } - - fn on_response(&self, _response: Response) -> Result<(), ismp_rs::error::Error> { - Ok(()) - } - - fn on_timeout(&self, _request: Request) -> Result<(), ismp_rs::error::Error> { - Ok(()) - } -} - #[derive(Default)] pub struct ModuleRouter; diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 0ad9fc789..ad4210903 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -61,7 +61,7 @@ pub trait ConsensusClientProvider { ) -> Result, ismp_rs::error::Error>; /// Returns the challenge period configured for a consensus client - fn challenge_period(id: ConsensusClientId) -> Duration; + fn challenge_period(id: ConsensusClientId) -> Option; } /// Module identification types supported by ismp diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index c6cdd7756..20785c766 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -19,18 +19,22 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use crate::dispatcher::Dispatcher; +use crate::{ + dispatcher::Dispatcher, + ismp_mocks::{setup_mock_client, MOCK_CONSENSUS_STATE_ID}, +}; use frame_support::traits::OnFinalize; use ismp_primitives::mmr::MmrHasher; use ismp_rs::{ - consensus::{IntermediateState, StateCommitment, StateMachineHeight}, + consensus::StateMachineHeight, + host::Ethereum, messaging::{Proof, ResponseMessage, TimeoutMessage}, router::{DispatchGet, DispatchRequest, IsmpDispatcher}, util::hash_request, }; use ismp_testsuite::{ - check_challenge_period, check_client_expiry, frozen_check, mocks::MOCK_CONSENSUS_CLIENT_ID, - timeout_post_processing_check, write_outgoing_commitments, + check_challenge_period, check_client_expiry, frozen_check, timeout_post_processing_check, + write_outgoing_commitments, }; use mmr_lib::MerkleProof; use sp_core::{ @@ -236,6 +240,7 @@ fn should_reject_expired_check_clients() { ext.execute_with(|| { set_timestamp(None); let host = Host::::default(); + host.store_unbonding_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); check_client_expiry(&host).unwrap() }) } @@ -252,40 +257,17 @@ fn should_handle_post_request_timeouts_correctly() { }) } -fn setup_mock_client(host: &H) -> IntermediateState { - let intermediate_state = IntermediateState { - height: StateMachineHeight { - id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: MOCK_CONSENSUS_CLIENT_ID, - }, - height: 3, - }, - commitment: StateCommitment { - timestamp: 1000, - overlay_root: None, - state_root: Default::default(), - }, - }; - - host.store_consensus_state(MOCK_CONSENSUS_CLIENT_ID, vec![]).unwrap(); - host.store_state_machine_commitment(intermediate_state.height, intermediate_state.commitment) - .unwrap(); - host.store_consensus_update_time(MOCK_CONSENSUS_CLIENT_ID, Duration::from_secs(1000)).unwrap(); - intermediate_state -} - #[test] fn should_handle_get_request_timeouts_correctly() { let mut ext = new_test_ext(); ext.execute_with(|| { let host = Host::::default(); - let _ = setup_mock_client(&host); + setup_mock_client::<_, Test>(&host); let requests = (0..2) .into_iter() .map(|i| { let msg = DispatchGet { - dest_chain: StateMachine::Ethereum, + dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 2, @@ -296,7 +278,7 @@ fn should_handle_get_request_timeouts_correctly() { dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); let get = ismp_rs::router::Get { source_chain: host.host_state_machine(), - dest_chain: StateMachine::Ethereum, + dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], @@ -324,12 +306,12 @@ fn should_handle_get_request_responses_correctly() { let mut ext = new_test_ext(); ext.execute_with(|| { let host = Host::::default(); - let _ = setup_mock_client(&host); + setup_mock_client::<_, Test>(&host); let requests = (0..2) .into_iter() .map(|i| { let msg = DispatchGet { - dest_chain: StateMachine::Ethereum, + dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 3, @@ -340,7 +322,7 @@ fn should_handle_get_request_responses_correctly() { dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); let get = ismp_rs::router::Get { source_chain: host.host_state_machine(), - dest_chain: StateMachine::Ethereum, + dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], @@ -358,8 +340,8 @@ fn should_handle_get_request_responses_correctly() { proof: Proof { height: StateMachineHeight { id: StateMachineId { - state_id: StateMachine::Ethereum, - consensus_client: MOCK_CONSENSUS_CLIENT_ID, + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, }, height: 3, }, diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index c34fa4a59..938379f0d 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -133,6 +133,8 @@ pub trait WeightInfo { fn on_finalize(n: u32) -> Weight; /// Returns the weight consumed in creating a consensus client fn create_consensus_client() -> Weight; + /// Returns the weight consumed in setting the unbonding period + fn set_unbonding_period() -> Weight; /// Returns the weight consumed in handling a request fn handle_request_message() -> Weight; /// Returns the weight consumed in handling a response @@ -150,6 +152,10 @@ impl WeightInfo for () { Weight::zero() } + fn set_unbonding_period() -> Weight { + Weight::zero() + } + fn handle_request_message() -> Weight { Weight::zero() } @@ -168,7 +174,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { messages.into_iter().fold(Weight::zero(), |acc, msg| match msg { Message::Consensus(msg) => { let consensus_handler = - ::WeightProvider::consensus_client(msg.consensus_client_id) + ::WeightProvider::consensus_client(msg.consensus_state_id) .unwrap_or(Box::new(())); consensus_handler.verify_consensus(msg) } @@ -184,7 +190,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { }); let consensus_handler = ::WeightProvider::consensus_client( - msg.proof.height.id.consensus_client, + msg.proof.height.id.consensus_state_id, ) .unwrap_or(Box::new(())); @@ -214,7 +220,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { }); let consensus_handler = ::WeightProvider::consensus_client( - proof.height.id.consensus_client, + proof.height.id.consensus_state_id, ) .unwrap_or(Box::new(())); @@ -245,7 +251,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { }); let consensus_handler = ::WeightProvider::consensus_client( - proof.height.id.consensus_client, + proof.height.id.consensus_state_id, ) .unwrap_or(Box::new(())); @@ -275,7 +281,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { }); let consensus_handler = ::WeightProvider::consensus_client( - timeout_proof.height.id.consensus_client, + timeout_proof.height.id.consensus_state_id, // todo: consensus client id ) .unwrap_or(Box::new(())); @@ -309,7 +315,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { Message::FraudProof(msg) => { let consensus_handler = - ::WeightProvider::consensus_client(msg.consensus_client_id) + ::WeightProvider::consensus_client(msg.consensus_state_id) .unwrap_or(Box::new(())); consensus_handler.verify_fraud_proof(msg) } diff --git a/parachain/inherent/src/lib.rs b/parachain/inherent/src/lib.rs index 443e8292d..32912b2e6 100644 --- a/parachain/inherent/src/lib.rs +++ b/parachain/inherent/src/lib.rs @@ -65,7 +65,7 @@ impl ConsensusInherentProvider { storage_proof, }; let message = ConsensusMessage { - consensus_client_id: ismp_parachain::consensus::PARACHAIN_CONSENSUS_ID, + consensus_state_id: ismp_parachain::consensus::PARACHAIN_CONSENSUS_ID, consensus_proof: consensus_proof.encode(), }; diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 30d653bdb..26c59a7c7 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -21,7 +21,9 @@ use alloc::{boxed::Box, collections::BTreeMap, format, vec, vec::Vec}; use codec::{Decode, Encode}; use core::fmt::Debug; use ismp::{ - consensus::{ConsensusClient, ConsensusClientId, StateCommitment, StateMachineClient}, + consensus::{ + ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineClient, + }, error::Error, host::{IsmpHost, StateMachine}, messaging::{Proof, StateCommitmentHeight}, @@ -123,6 +125,7 @@ where fn verify_consensus( &self, host: &dyn IsmpHost, + _consensus_state_id: ConsensusStateId, state: Vec, proof: Vec, ) -> Result<(Vec, BTreeMap), Error> { @@ -232,11 +235,6 @@ where Ok((state, intermediates)) } - fn unbonding_period(&self) -> Duration { - // there's no notion of client expiry, since there's shared security. - Duration::from_secs(u64::MAX) - } - fn verify_fraud_proof( &self, _host: &dyn IsmpHost, diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index 7110b105c..7c86328f8 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -26,7 +26,7 @@ pub mod consensus; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::relay_chain; -use ismp::{handlers, messaging::CreateConsensusClient}; +use ismp::handlers; pub use pallet::*; use pallet_ismp::host::Host; @@ -36,7 +36,7 @@ pub mod pallet { use cumulus_primitives_core::relay_chain; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use ismp::messaging::{ConsensusMessage, Message}; + use ismp::messaging::{ConsensusMessage, CreateConsensusState, Message}; use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use primitive_types::H256; @@ -92,7 +92,7 @@ pub mod pallet { ); assert_eq!( - data.consensus_client_id, + data.consensus_state_id, consensus::PARACHAIN_CONSENSUS_ID, "Only parachain consensus updates should be passed in the inherents!" ); @@ -198,9 +198,11 @@ pub mod pallet { fn build(&self) { let host = Host::::default(); - let message = CreateConsensusClient { + let message = CreateConsensusState { // insert empty bytes consensus_state: vec![], + unbonding_period: u64::MAX, + consensus_state_id: consensus::PARACHAIN_CONSENSUS_ID, consensus_client_id: consensus::PARACHAIN_CONSENSUS_ID, state_machine_commitments: vec![], }; From 66fa3ea0e57c89123cac70e3152eff200e71908a Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Fri, 14 Jul 2023 18:54:31 +0100 Subject: [PATCH 146/182] Latest ismp updates (#68) --- Cargo.lock | 205 ++++++++++++++++---------------- ismp-demo/src/lib.rs | 8 +- pallet-ismp/src/benchmarking.rs | 31 ++--- pallet-ismp/src/dispatcher.rs | 8 +- pallet-ismp/src/host.rs | 21 +++- pallet-ismp/src/lib.rs | 37 +++++- pallet-ismp/src/mock.rs | 4 - pallet-ismp/src/primitives.rs | 4 - pallet-ismp/src/tests.rs | 22 ++-- parachain/src/lib.rs | 12 +- 10 files changed, 194 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0992c94fa..0c5608a71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,15 +170,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aho-corasick" version = "1.0.2" @@ -319,7 +310,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.22", + "time 0.3.23", ] [[package]] @@ -335,7 +326,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.22", + "time 0.3.23", ] [[package]] @@ -381,9 +372,9 @@ checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" [[package]] name = "async-channel" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", @@ -427,7 +418,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -596,7 +587,7 @@ checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq", + "constant_time_eq 0.2.6", ] [[package]] @@ -607,20 +598,20 @@ checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq", + "constant_time_eq 0.2.6", ] [[package]] name = "blake3" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" dependencies = [ "arrayref", "arrayvec 0.7.4", "cc", "cfg-if", - "constant_time_eq", + "constant_time_eq 0.3.0", "digest 0.10.7", ] @@ -946,7 +937,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -984,9 +975,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "constant_time_eq" @@ -994,6 +985,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "convert_case" version = "0.4.0" @@ -1335,7 +1332,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -1710,7 +1707,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -1901,9 +1898,9 @@ checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" @@ -1988,7 +1985,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -2248,7 +2245,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -2260,7 +2257,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -2270,7 +2267,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -2313,7 +2310,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" dependencies = [ - "rustix 0.38.3", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -2395,7 +2392,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -2541,11 +2538,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "bstr", "fnv", "log", @@ -3039,14 +3036,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.3", + "rustix 0.38.4", "windows-sys 0.48.0", ] [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#823da35f3e4ed27386ad775482ccd76e774edd5b" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#93271cb170d01ce743d9dcb6c2f0bc8bee16901a" dependencies = [ "derive_more", "parity-scale-codec", @@ -3172,7 +3169,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#823da35f3e4ed27386ad775482ccd76e774edd5b" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#93271cb170d01ce743d9dcb6c2f0bc8bee16901a" dependencies = [ "ismp", "parity-scale-codec", @@ -4322,9 +4319,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.2" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" dependencies = [ "approx", "matrixmultiply", @@ -4338,9 +4335,9 @@ dependencies = [ [[package]] name = "nalgebra-macros" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ "proc-macro2", "quote", @@ -4922,7 +4919,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -5335,14 +5332,14 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] @@ -5635,7 +5632,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.22", + "time 0.3.23", "x509-parser 0.13.2", "yasna", ] @@ -5648,7 +5645,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.22", + "time 0.3.23", "yasna", ] @@ -5698,7 +5695,7 @@ checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -5715,14 +5712,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", - "regex-automata 0.3.0", - "regex-syntax 0.7.3", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -5736,13 +5733,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", - "regex-syntax 0.7.3", + "regex-syntax 0.7.4", ] [[package]] @@ -5753,9 +5750,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "region" @@ -5952,9 +5949,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ "bitflags 2.3.3", "errno", @@ -6122,7 +6119,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -6814,7 +6811,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -7061,29 +7058,29 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.166" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.166" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" dependencies = [ "itoa", "ryu", @@ -7335,7 +7332,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -7617,7 +7614,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing 5.0.0", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -7636,7 +7633,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -7857,7 +7854,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -8071,7 +8068,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -8299,9 +8296,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote", @@ -8349,9 +8346,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" +checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" [[package]] name = "tempfile" @@ -8384,22 +8381,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.41" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.41" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -8468,9 +8465,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ "itoa", "serde", @@ -8486,9 +8483,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] @@ -8565,7 +8562,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -8694,7 +8691,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -8737,7 +8734,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -9091,7 +9088,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", "wasm-bindgen-shared", ] @@ -9125,7 +9122,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9448,7 +9445,7 @@ dependencies = [ "sha2 0.10.7", "stun", "thiserror", - "time 0.3.22", + "time 0.3.23", "tokio", "turn", "url", @@ -9644,9 +9641,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40018623e2dba2602a9790faba8d33f2ebdebf4b86561b83928db735f8784728" +checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f" dependencies = [ "bytemuck", "safe_arch", @@ -9890,9 +9887,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" dependencies = [ "memchr", ] @@ -9954,7 +9951,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.22", + "time 0.3.23", ] [[package]] @@ -9972,7 +9969,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.22", + "time 0.3.23", ] [[package]] @@ -9999,7 +9996,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] @@ -10022,7 +10019,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.22", + "time 0.3.23", ] [[package]] @@ -10042,7 +10039,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.25", ] [[package]] diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index e146b8592..cedf9bc4f 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -137,7 +137,7 @@ pub mod pallet { _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, }; let post = DispatchPost { - dest_chain: dest, + dest, from: PALLET_ID.encode(), to: PALLET_ID.encode(), timeout_timestamp: params.timeout, @@ -167,14 +167,14 @@ pub mod pallet { #[pallet::call_index(1)] pub fn get_request(origin: OriginFor, params: GetRequest) -> DispatchResult { ensure_signed(origin)?; - let dest_chain = match T::StateMachine::get() { + let dest = match T::StateMachine::get() { StateMachine::Kusama(_) => StateMachine::Kusama(params.para_id), StateMachine::Polkadot(_) => StateMachine::Polkadot(params.para_id), _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, }; let get = DispatchGet { - dest_chain, + dest, from: PALLET_ID.encode(), keys: params.keys, height: params.height as u64, @@ -248,7 +248,7 @@ impl Default for IsmpModuleCallback { impl IsmpModule for IsmpModuleCallback { fn on_accept(&self, request: Post) -> Result<(), IsmpError> { - let source_chain = request.source_chain; + let source_chain = request.source; let payload = ::Balance> as codec::Decode>::decode( &mut &*request.data, diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 0f1e1145b..3ccaccde5 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -97,10 +97,11 @@ pub mod benchmarks { #[benchmark] fn handle_request_message() { let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); let height = setup_mock_client::<_, T>(&host); let post = Post { - source_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), - dest_chain: ::StateMachine::get(), + source: StateMachine::Ethereum(Ethereum::ExecutionLayer), + dest: ::StateMachine::get(), nonce: 0, from: MODULE_ID.encode(), to: MODULE_ID.encode(), @@ -122,10 +123,11 @@ pub mod benchmarks { #[benchmark] fn handle_response_message() { let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); let height = setup_mock_client::<_, T>(&host); let post = Post { - source_chain: ::StateMachine::get(), - dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), + source: ::StateMachine::get(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, from: MODULE_ID.encode(), to: MODULE_ID.encode(), @@ -137,11 +139,7 @@ pub mod benchmarks { let commitment = hash_request::>(&request); RequestCommitments::::insert( commitment.0.to_vec(), - LeafIndexQuery { - source_chain: post.source_chain, - dest_chain: post.dest_chain, - nonce: post.nonce, - }, + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: post.nonce }, ); let response = Response::Post(PostResponse { post, response: vec![] }); @@ -162,10 +160,11 @@ pub mod benchmarks { #[benchmark] fn handle_timeout_message() { let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); let height = setup_mock_client::<_, T>(&host); let post = Post { - source_chain: ::StateMachine::get(), - dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), + source: ::StateMachine::get(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, from: MODULE_ID.encode(), to: MODULE_ID.encode(), @@ -177,11 +176,7 @@ pub mod benchmarks { let commitment = hash_request::>(&request); RequestCommitments::::insert( commitment.0.to_vec(), - LeafIndexQuery { - source_chain: post.source_chain, - dest_chain: post.dest_chain, - nonce: post.nonce, - }, + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: post.nonce }, ); let msg = TimeoutMessage::Post { @@ -200,8 +195,8 @@ pub mod benchmarks { fn on_finalize(x: Linear<1, 100>) { for nonce in 0..x { let post = ismp_rs::router::Post { - source_chain: StateMachine::Kusama(2000), - dest_chain: StateMachine::Kusama(2001), + source: StateMachine::Kusama(2000), + dest: StateMachine::Kusama(2001), nonce: nonce.into(), from: vec![0u8; 32], to: vec![1u8; 32], diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index c3195fd9b..17ab720e5 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -53,8 +53,8 @@ where let request = match request { DispatchRequest::Get(dispatch_get) => { let get = Get { - source_chain: host.host_state_machine(), - dest_chain: dispatch_get.dest_chain, + source: host.host_state_machine(), + dest: dispatch_get.dest, nonce: host.next_nonce(), from: dispatch_get.from, keys: dispatch_get.keys, @@ -65,8 +65,8 @@ where } DispatchRequest::Post(dispatch_post) => { let post = Post { - source_chain: host.host_state_machine(), - dest_chain: dispatch_post.dest_chain, + source: host.host_state_machine(), + dest: dispatch_post.dest, nonce: host.next_nonce(), from: dispatch_post.from, to: dispatch_post.to, diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index daa84a72a..057d3ad5d 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -15,10 +15,10 @@ //! Host implementation for ISMP use crate::{ - dispatcher::Receipt, primitives::ConsensusClientProvider, Config, ConsensusClientUpdateTime, - ConsensusStateClient, ConsensusStates, FrozenConsensusClients, FrozenHeights, - LatestStateMachineHeight, Nonce, RequestCommitments, RequestReceipts, ResponseReceipts, - StateCommitments, UnbondingPeriod, + dispatcher::Receipt, primitives::ConsensusClientProvider, ChallengePeriod, Config, + ConsensusClientUpdateTime, ConsensusStateClient, ConsensusStates, FrozenConsensusClients, + FrozenHeights, LatestStateMachineHeight, Nonce, RequestCommitments, RequestReceipts, + ResponseReceipts, StateCommitments, UnbondingPeriod, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -162,8 +162,8 @@ where sp_io::hashing::keccak_256(bytes).into() } - fn challenge_period(&self, id: ConsensusClientId) -> Option { - ::ConsensusClientProvider::challenge_period(id) + fn challenge_period(&self, id: ConsensusStateId) -> Option { + ChallengePeriod::::get(&id).map(Duration::from_secs) } fn ismp_router(&self) -> Box { @@ -241,4 +241,13 @@ where UnbondingPeriod::::insert(consensus_state_id, period); Ok(()) } + + fn store_challenge_period( + &self, + consensus_state_id: ConsensusStateId, + period: u64, + ) -> Result<(), Error> { + ChallengePeriod::::insert(consensus_state_id, period); + Ok(()) + } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 4a557d3c2..77cd21060 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -186,6 +186,11 @@ pub mod pallet { pub type UnbondingPeriod = StorageMap<_, Blake2_128Concat, ConsensusStateId, u64, OptionQuery>; + /// A mapping of ConsensusStateId to Challenge periods + #[pallet::storage] + pub type ChallengePeriod = + StorageMap<_, Blake2_128Concat, ConsensusStateId, u64, OptionQuery>; + /// Holds a map of consensus clients frozen due to byzantine /// behaviour #[pallet::storage] @@ -299,6 +304,15 @@ pub mod pallet { unbonding_period: u64, } + /// Params to update the challenge period for a consensus state + #[derive(Debug, Clone, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] + pub struct ChallengeUpdate { + /// Consensus state identifier + pub consensus_state_id: ConsensusStateId, + /// Challenge period duration + pub challenge_period: u64, + } + #[pallet::call] impl Pallet where @@ -335,7 +349,7 @@ pub mod pallet { } /// Set the unbonding period for a consensus state. - #[pallet::weight(::WeightInfo::create_consensus_client())] + #[pallet::weight(::DbWeight::get().writes(1))] #[pallet::call_index(2)] pub fn set_unbonding_period( origin: OriginFor, @@ -350,6 +364,23 @@ pub mod pallet { Ok(()) } + + /// Set the challenge period for a consensus state. + #[pallet::weight(::DbWeight::get().writes(1))] + #[pallet::call_index(3)] + pub fn set_challenge_period( + origin: OriginFor, + message: ChallengeUpdate, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + let host = Host::::default(); + + host.store_challenge_period(message.consensus_state_id, message.challenge_period) + .map_err(|_| Error::::ChallengePeriodUpdateFailed)?; + + Ok(()) + } } #[pallet::event] @@ -408,6 +439,8 @@ pub mod pallet { ConsensusClientCreationFailed, /// Couldn't update unbonding period UnbondingPeriodUpdateFailed, + /// Couldn't update challenge period + ChallengePeriodUpdateFailed, } } @@ -440,7 +473,7 @@ where Ok(MessageResult::ConsensusMessage(res)) => { // check if this is a trusted state machine let is_trusted_state_machine = host - .challenge_period(res.consensus_client_id.clone()) == + .challenge_period(res.consensus_state_id.clone()) == Some(Duration::from_secs(0)); if is_trusted_state_machine { diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mock.rs index 3c752eff1..c7a0af284 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mock.rs @@ -59,10 +59,6 @@ impl ConsensusClientProvider for ConsensusProvider { ) -> Result, ismp_rs::error::Error> { Ok(Box::new(MockConsensusClient)) } - - fn challenge_period(_id: ConsensusClientId) -> Option { - Some(Duration::from_secs(60 * 60)) - } } impl frame_system::Config for Test { diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index ad4210903..2482e1e8a 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -14,7 +14,6 @@ // limitations under the License. //! Pallet primitives -use core::time::Duration; use frame_support::{PalletId, RuntimeDebug}; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; @@ -59,9 +58,6 @@ pub trait ConsensusClientProvider { fn consensus_client( id: ConsensusClientId, ) -> Result, ismp_rs::error::Error>; - - /// Returns the challenge period configured for a consensus client - fn challenge_period(id: ConsensusClientId) -> Option; } /// Module identification types supported by ismp diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index 20785c766..8b39c1373 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -66,8 +66,8 @@ fn push_leaves(range: Range) -> Vec { let mut positions = vec![]; for nonce in range { let post = ismp_rs::router::Post { - source_chain: StateMachine::Kusama(2000), - dest_chain: StateMachine::Kusama(2001), + source: StateMachine::Kusama(2000), + dest: StateMachine::Kusama(2001), nonce, from: vec![0u8; 32], to: vec![1u8; 32], @@ -218,6 +218,7 @@ fn should_reject_updates_within_challenge_period() { ext.execute_with(|| { set_timestamp(None); let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); check_challenge_period(&host).unwrap() }) } @@ -229,6 +230,7 @@ fn should_reject_messages_for_frozen_state_machines() { ext.execute_with(|| { set_timestamp(None); let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); frozen_check(&host).unwrap() }) } @@ -241,6 +243,7 @@ fn should_reject_expired_check_clients() { set_timestamp(None); let host = Host::::default(); host.store_unbonding_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); check_client_expiry(&host).unwrap() }) } @@ -253,6 +256,7 @@ fn should_handle_post_request_timeouts_correctly() { set_timestamp(None); let host = Host::::default(); let dispatcher = Dispatcher::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); timeout_post_processing_check(&host, &dispatcher).unwrap() }) } @@ -263,11 +267,12 @@ fn should_handle_get_request_timeouts_correctly() { ext.execute_with(|| { let host = Host::::default(); setup_mock_client::<_, Test>(&host); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); let requests = (0..2) .into_iter() .map(|i| { let msg = DispatchGet { - dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 2, @@ -277,8 +282,8 @@ fn should_handle_get_request_timeouts_correctly() { let dispatcher = Dispatcher::::default(); dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); let get = ismp_rs::router::Get { - source_chain: host.host_state_machine(), - dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), + source: host.host_state_machine(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], @@ -307,11 +312,12 @@ fn should_handle_get_request_responses_correctly() { ext.execute_with(|| { let host = Host::::default(); setup_mock_client::<_, Test>(&host); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); let requests = (0..2) .into_iter() .map(|i| { let msg = DispatchGet { - dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 3, @@ -321,8 +327,8 @@ fn should_handle_get_request_responses_correctly() { let dispatcher = Dispatcher::::default(); dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); let get = ismp_rs::router::Get { - source_chain: host.host_state_machine(), - dest_chain: StateMachine::Ethereum(Ethereum::ExecutionLayer), + source: host.host_state_machine(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: i, from: vec![0u8; 32], keys: vec![vec![1u8; 32], vec![1u8; 32]], diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index 7c86328f8..c76f3ff98 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -36,7 +36,10 @@ pub mod pallet { use cumulus_primitives_core::relay_chain; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use ismp::messaging::{ConsensusMessage, CreateConsensusState, Message}; + use ismp::{ + host::IsmpHost, + messaging::{ConsensusMessage, CreateConsensusState, Message}, + }; use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use primitive_types::H256; @@ -104,7 +107,7 @@ pub mod pallet { /// Add some new parachains to the list of parachains we care about #[pallet::call_index(1)] - #[pallet::weight(0)] + #[pallet::weight(::DbWeight::get().writes(para_ids.len() as u64))] pub fn add_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { ensure_root(origin)?; for id in para_ids { @@ -116,7 +119,7 @@ pub mod pallet { /// Remove some parachains from the list of parachains we care about #[pallet::call_index(2)] - #[pallet::weight(0)] + #[pallet::weight(::DbWeight::get().writes(para_ids.len() as u64))] pub fn remove_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { ensure_root(origin)?; for id in para_ids { @@ -208,7 +211,8 @@ pub mod pallet { }; handlers::create_client(&host, message) .expect("Failed to initialize parachain consensus client"); - + host.store_challenge_period(consensus::PARACHAIN_CONSENSUS_ID, 0) + .expect("Failed to set parachain challenge period"); // insert the parachain ids for id in &self.parachains { Parachains::::insert(id, ()); From ab719aeb27b698974e9b58992fcffb695de95928 Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Mon, 24 Jul 2023 09:10:45 +0100 Subject: [PATCH 147/182] W3F Badge (#70) * W3F * W3F * update * position * width=250 * width=500 * auto --- README.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6faa3e751..64c20ca6d 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,32 @@ Implementation of the Interoperable State Machine Protocol for substrate runtimes. This project is [funded by the web3 foundation](https://github.com/w3f/Grants-Program/blob/master/applications/ismp.md). + + ## Overview -This repo holds all the required components substrate runtimes need to interoperate together using [ISMP](https://github.com/polytope-labs/ismp) +This repo holds all the required components substrate runtimes need to interoperate together using [ISMP](https://github.com/polytope-labs/ismp) -* [pallet-ismp](./) -* [ismp-runtime-api](./pallet-ismp/runtime-api) -* [ismp-rpc](./pallet-ismp/rpc) +- [pallet-ismp](./) +- [ismp-runtime-api](./pallet-ismp/runtime-api) +- [ismp-rpc](./pallet-ismp/rpc) ### Parachain Support -* [ismp-parachain](./parachain) -* [ismp-parachain-inherent](./parachain/inherent) -* [ismp-parachain-runtime-api](./parachain/runtime-api) +- [ismp-parachain](./parachain) +- [ismp-parachain-inherent](./parachain/inherent) +- [ismp-parachain-runtime-api](./parachain/runtime-api) ## Documentation Installation and integration guides can be found in the [book](https://substrate-ismp.polytope.technology). ## Testing and Testing Guide -This guide assumes [Rust](https://www.rust-lang.org/tools/install) and it's [nightly](https://rust-lang.github.io/rustup/concepts/channels.html#:~:text=it%20just%20run-,rustup%20toolchain%20install%20nightly,-%3A) version is installed, followed by calling the [init](https://github.com/paritytech/polkadot/blob/master/scripts/init.sh) script from the official Polkadot repo to initailize a WASM build environment. + +This guide assumes [Rust](https://www.rust-lang.org/tools/install) and it's [nightly](https://rust-lang.github.io/rustup/concepts/channels.html#:~:text=it%20just%20run-,rustup%20toolchain%20install%20nightly,-%3A) version is installed, followed by calling the [init](https://github.com/paritytech/polkadot/blob/master/scripts/init.sh) script from the official Polkadot repo to initailize a WASM build environment. To run the unit tests associated with this library; + ``` cargo +nightly test -p pallet-ismp --all-targets --all-features ``` @@ -31,10 +35,11 @@ cargo +nightly test -p pallet-ismp --all-targets --all-features Please see [CI](.github/workflows/ci.yml) for test coverage. ## Run Test in Docker + ```bash docker run --memory="24g" --rm --user root -v "$PWD":/app -w /app rust:latest /bin/bash -c "apt update && apt install -y protobuf-compiler libclang-dev && cargo test --release --manifest-path=./Cargo.toml" ``` ## License -This library is licensed under the Apache 2.0 License, Copyright (c) 2023 Polytope Labs. \ No newline at end of file +This library is licensed under the Apache 2.0 License, Copyright (c) 2023 Polytope Labs. From 0b05cff649d2a7dacf0ceca0664c65e95a46f41d Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Mon, 24 Jul 2023 10:37:57 +0100 Subject: [PATCH 148/182] Latest Ismp Updates (#71) --- Cargo.lock | 241 +++++++++++++++++----------------- pallet-ismp/src/ismp_mocks.rs | 3 +- parachain/src/consensus.rs | 8 +- 3 files changed, 131 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c5608a71..c12557e92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "approx" @@ -412,20 +412,20 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.71" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] name = "asynchronous-codec" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" dependencies = [ "bytes", "futures-sink", @@ -907,9 +907,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.11" +version = "4.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "5b0827b011f6f8ab38590295339817b0d26f344aa4932c3ced71b45b0c54b4a9" dependencies = [ "clap_builder", "clap_derive", @@ -918,9 +918,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "9441b403be87be858db6a23edb493e7f694761acdc3343d5a0fcaafd304cbc9e" dependencies = [ "anstream", "anstyle", @@ -930,14 +930,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -1332,7 +1332,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -1707,7 +1707,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -1724,9 +1724,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519b83cd10f5f6e969625a409f735182bea5558cd8b64c655806ceaae36f1999" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dyn-clonable" @@ -1751,9 +1751,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" @@ -1769,9 +1769,9 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der 0.7.7", "digest 0.10.7", @@ -1860,7 +1860,7 @@ dependencies = [ "group 0.13.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1 0.7.2", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -1985,7 +1985,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -2009,6 +2009,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fatality" version = "0.0.6" @@ -2245,7 +2251,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -2257,7 +2263,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -2267,7 +2273,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -2375,7 +2381,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", @@ -2392,7 +2398,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -2752,9 +2758,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" @@ -3043,7 +3049,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#93271cb170d01ce743d9dcb6c2f0bc8bee16901a" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#a5c64058503888bc38cd13755db2f717a139f7e1" dependencies = [ "derive_more", "parity-scale-codec", @@ -3169,7 +3175,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#93271cb170d01ce743d9dcb6c2f0bc8bee16901a" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#a5c64058503888bc38cd13755db2f717a139f7e1" dependencies = [ "ismp", "parity-scale-codec", @@ -3188,9 +3194,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -3305,7 +3311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.7", + "ecdsa 0.16.8", "elliptic-curve 0.13.5", "once_cell", "sha2 0.10.7", @@ -4513,9 +4519,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -4727,9 +4733,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dab3ac198341b2f0fec6e7f8a6eeed07a41201d98a124260611598c142e76df" +checksum = "78f19d20a0d2cc52327a88d131fa1c4ea81ea4a04714aedcfeca2dd410049cf8" dependencies = [ "blake2", "crc32fast", @@ -4747,9 +4753,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.3" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -4762,9 +4768,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.3" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4840,9 +4846,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -4919,7 +4925,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -5332,14 +5338,14 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -5503,9 +5509,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -5680,22 +5686,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -6008,9 +6014,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rw-stream-sink" @@ -6025,15 +6031,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safe_arch" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" dependencies = [ "bytemuck", ] @@ -6119,7 +6125,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -6811,7 +6817,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -6936,9 +6942,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -6988,9 +6994,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", "der 0.7.7", @@ -7052,35 +7058,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" dependencies = [ "itoa", "ryu", @@ -7332,7 +7338,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -7614,7 +7620,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing 5.0.0", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -7633,7 +7639,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -7742,7 +7748,7 @@ version = "4.1.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "thiserror", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] @@ -7854,7 +7860,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -8068,7 +8074,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -8296,9 +8302,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -8346,21 +8352,20 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.9" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" +checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e" [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "autocfg", "cfg-if", - "fastrand", + "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.37.23", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -8381,22 +8386,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -8562,7 +8567,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -8620,9 +8625,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "toml_datetime", @@ -8691,7 +8696,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -8734,7 +8739,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -8918,9 +8923,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -8994,9 +8999,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.10", ] @@ -9088,7 +9093,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", "wasm-bindgen-shared", ] @@ -9122,7 +9127,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9887,9 +9892,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] @@ -9996,7 +10001,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -10039,7 +10044,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.27", ] [[package]] @@ -10053,11 +10058,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe 6.0.6", ] [[package]] @@ -10072,9 +10077,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", diff --git a/pallet-ismp/src/ismp_mocks.rs b/pallet-ismp/src/ismp_mocks.rs index 1720364a6..db6dd2087 100644 --- a/pallet-ismp/src/ismp_mocks.rs +++ b/pallet-ismp/src/ismp_mocks.rs @@ -5,6 +5,7 @@ use frame_support::PalletId; use ismp_rs::{ consensus::{ ConsensusClient, StateCommitment, StateMachineClient, StateMachineHeight, StateMachineId, + VerifiedCommitments, }, error::Error as IsmpError, handlers, @@ -54,7 +55,7 @@ impl ConsensusClient for MockConsensusClient { _cs_id: ismp_rs::consensus::ConsensusStateId, _trusted_consensus_state: Vec, _proof: Vec, - ) -> Result<(Vec, BTreeMap), IsmpError> { + ) -> Result<(Vec, VerifiedCommitments), IsmpError> { Ok(Default::default()) } diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 26c59a7c7..1a6399a66 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -23,6 +23,7 @@ use core::fmt::Debug; use ismp::{ consensus::{ ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineClient, + VerifiedCommitments, }, error::Error, host::{IsmpHost, StateMachine}, @@ -128,7 +129,7 @@ where _consensus_state_id: ConsensusStateId, state: Vec, proof: Vec, - ) -> Result<(Vec, BTreeMap), Error> { + ) -> Result<(Vec, VerifiedCommitments), Error> { let update: ParachainConsensusProof = codec::Decode::decode(&mut &proof[..]).map_err(|e| { Error::ImplementationSpecific(format!( @@ -166,6 +167,8 @@ where })?; for (key, header) in headers { + let mut state_commitments_vec = Vec::new(); + let id = codec::Decode::decode(&mut &key[(key.len() - 4)..]).map_err(|e| { Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) })?; @@ -229,7 +232,8 @@ where height: height.into(), }; - intermediates.insert(state_id, intermediate); + state_commitments_vec.push(intermediate); + intermediates.insert(state_id, state_commitments_vec); } Ok((state, intermediates)) From bc62978edae3d33707246123bea804cea7c07c83 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Mon, 24 Jul 2023 15:23:32 +0100 Subject: [PATCH 149/182] Grandpa Client Implementation (#64) --- .github/workflows/ci.yml | 13 +- Cargo.lock | 2939 ++++++++++++++--- Cargo.toml | 9 +- grandpa/Cargo.toml | 59 + grandpa/primitives/Cargo.toml | 39 + grandpa/primitives/src/justification.rs | 347 ++ grandpa/primitives/src/lib.rs | 109 + grandpa/prover/Cargo.toml | 29 + grandpa/prover/src/lib.rs | 354 ++ grandpa/src/consensus.rs | 306 ++ grandpa/src/consensus_message.rs | 44 + grandpa/src/lib.rs | 141 + grandpa/verifier/Cargo.toml | 62 + grandpa/verifier/src/lib.rs | 167 + grandpa/verifier/src/state_machine.rs | 66 + grandpa/verifier/src/tests.rs | 146 + pallet-ismp/primitives/Cargo.toml | 8 +- pallet-ismp/primitives/src/lib.rs | 80 +- .../primitives/state-machine/Cargo.toml | 45 + .../primitives/state-machine/src/lib.rs | 165 + pallet-ismp/src/lib.rs | 7 +- pallet-ismp/src/primitives.rs | 3 - parachain/Cargo.toml | 2 + parachain/src/consensus.rs | 172 +- 24 files changed, 4693 insertions(+), 619 deletions(-) create mode 100644 grandpa/Cargo.toml create mode 100644 grandpa/primitives/Cargo.toml create mode 100644 grandpa/primitives/src/justification.rs create mode 100644 grandpa/primitives/src/lib.rs create mode 100644 grandpa/prover/Cargo.toml create mode 100644 grandpa/prover/src/lib.rs create mode 100644 grandpa/src/consensus.rs create mode 100644 grandpa/src/consensus_message.rs create mode 100644 grandpa/src/lib.rs create mode 100644 grandpa/verifier/Cargo.toml create mode 100644 grandpa/verifier/src/lib.rs create mode 100644 grandpa/verifier/src/state_machine.rs create mode 100644 grandpa/verifier/src/tests.rs create mode 100644 pallet-ismp/primitives/state-machine/Cargo.toml create mode 100644 pallet-ismp/primitives/state-machine/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a3c41284..5a210b715 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,17 +38,20 @@ jobs: - name: Build run: | - cargo +nightly-2022-10-28 check --workspace --all-targets --all-features --verbose + cargo +nightly-2022-10-28 check --workspace --all-targets --all-features --verbose --locked - name: Build `no-std` run: | - cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose - cargo +nightly-2022-10-28 check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose - cargo +nightly-2022-10-28 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose + cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p ismp-grandpa-primitives --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p ismp-grandpa-verifier --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p pallet-grandpa-client --no-default-features --target=wasm32-unknown-unknown --verbose --locked - name: Test run: | - cargo +nightly-2022-10-28 test -p pallet-ismp --all-targets --all-features --verbose + cargo +nightly-2022-10-28 test -p pallet-ismp --all-targets --all-features --verbose --locked lint: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index c12557e92..e13f94bda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,15 @@ dependencies = [ "gimli 0.26.2", ] +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.3", +] + [[package]] name = "addr2line" version = "0.20.0" @@ -490,6 +499,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + [[package]] name = "base64" version = "0.13.1" @@ -907,9 +922,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.17" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0827b011f6f8ab38590295339817b0d26f344aa4932c3ced71b45b0c54b4a9" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -918,9 +933,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.17" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9441b403be87be858db6a23edb493e7f694761acdc3343d5a0fcaafd304cbc9e" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -1046,7 +1061,7 @@ version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.93.2", ] [[package]] @@ -1060,7 +1075,7 @@ dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", - "cranelift-entity", + "cranelift-entity 0.93.2", "cranelift-isle", "gimli 0.26.2", "hashbrown 0.12.3", @@ -1094,6 +1109,15 @@ dependencies = [ "serde", ] +[[package]] +name = "cranelift-entity" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +dependencies = [ + "serde", +] + [[package]] name = "cranelift-frontend" version = "0.93.2" @@ -1130,13 +1154,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" dependencies = [ "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.93.2", "cranelift-frontend", "itertools", "log", "smallvec", - "wasmparser", - "wasmtime-types", + "wasmparser 0.100.0", + "wasmtime-types 6.0.2", ] [[package]] @@ -1314,13 +1338,13 @@ dependencies = [ "scale-info", "sp-core 7.0.0", "sp-externalities 0.13.0", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-state-machine", + "sp-inherents 4.0.0-dev", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "sp-std 5.0.0", - "sp-trie", - "sp-version", + "sp-trie 7.0.0", + "sp-version 5.0.0", "xcm", ] @@ -1345,10 +1369,10 @@ dependencies = [ "polkadot-parachain", "polkadot-primitives", "scale-info", - "sp-api", - "sp-runtime", + "sp-api 4.0.0-dev", + "sp-runtime 7.0.0", "sp-std 5.0.0", - "sp-trie", + "sp-trie 7.0.0", "xcm", ] @@ -1362,16 +1386,16 @@ dependencies = [ "cumulus-relay-chain-interface", "cumulus-test-relay-sproof-builder", "parity-scale-codec", - "sc-client-api", + "sc-client-api 4.0.0-dev", "scale-info", - "sp-api", + "sp-api 4.0.0-dev", "sp-core 7.0.0", - "sp-inherents", - "sp-runtime", - "sp-state-machine", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "sp-std 5.0.0", "sp-storage 7.0.0", - "sp-trie", + "sp-trie 7.0.0", "tracing", ] @@ -1386,10 +1410,10 @@ dependencies = [ "jsonrpsee-core", "parity-scale-codec", "polkadot-overseer", - "sc-client-api", - "sp-api", - "sp-blockchain", - "sp-state-machine", + "sc-client-api 4.0.0-dev", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-state-machine 0.13.0", "thiserror", ] @@ -1401,8 +1425,8 @@ dependencies = [ "cumulus-primitives-core", "parity-scale-codec", "polkadot-primitives", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "sp-std 5.0.0", ] @@ -1452,8 +1476,18 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] @@ -1470,17 +1504,42 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.27", +] + [[package]] name = "darling_macro" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core 0.20.3", + "quote", + "syn 2.0.27", +] + [[package]] name = "data-encoding" version = "2.4.0" @@ -1593,7 +1652,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro2", "quote", "syn 1.0.109", @@ -1820,9 +1879,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1877,6 +1936,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -2081,7 +2153,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger", + "env_logger 0.10.0", "log", ] @@ -2153,6 +2225,15 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "fork-tree" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4615fe5ff97489aa729b6f5133397efb0ba3075d11a50aa6c06d7bce9501f7b9" +dependencies = [ + "parity-scale-codec", +] + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -2182,11 +2263,11 @@ dependencies = [ "paste", "scale-info", "serde", - "sp-api", - "sp-application-crypto", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", "sp-core 7.0.0", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-runtime-interface 7.0.0", "sp-std 5.0.0", "sp-storage 7.0.0", @@ -2223,18 +2304,18 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-api", - "sp-arithmetic", + "sp-api 4.0.0-dev", + "sp-arithmetic 6.0.0", "sp-core 7.0.0", - "sp-core-hashing-proc-macro", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-staking", - "sp-state-machine", + "sp-core-hashing-proc-macro 5.0.0", + "sp-inherents 4.0.0-dev", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-staking 4.0.0-dev", + "sp-state-machine 0.13.0", "sp-std 5.0.0", "sp-tracing 6.0.0", - "sp-weights", + "sp-weights 4.0.0", "tt-call", ] @@ -2287,11 +2368,11 @@ dependencies = [ "scale-info", "serde", "sp-core 7.0.0", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", - "sp-version", - "sp-weights", + "sp-version 5.0.0", + "sp-weights 4.0.0", ] [[package]] @@ -2495,8 +2576,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2535,6 +2618,11 @@ name = "gimli" version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] [[package]] name = "glob" @@ -2596,6 +2684,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + [[package]] name = "hash-db" version = "0.16.0" @@ -2817,6 +2911,7 @@ dependencies = [ "rustls-native-certs", "tokio", "tokio-rustls", + "webpki-roots", ] [[package]] @@ -3071,7 +3166,82 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", +] + +[[package]] +name = "ismp-grandpa-primitives" +version = "0.1.0" +dependencies = [ + "anyhow", + "finality-grandpa", + "frame-support", + "ismp", + "parity-scale-codec", + "sp-consensus-grandpa", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-trie 7.0.0", +] + +[[package]] +name = "ismp-grandpa-prover" +version = "0.1.0" +dependencies = [ + "anyhow", + "derive_more", + "downcast-rs", + "finality-grandpa", + "hex", + "ismp", + "ismp-grandpa-primitives", + "jsonrpsee", + "jsonrpsee-ws-client", + "parity-scale-codec", + "sc-consensus-grandpa-rpc", + "serde", + "sp-consensus-grandpa", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-trie 7.0.0", + "subxt", +] + +[[package]] +name = "ismp-grandpa-verifier" +version = "0.1.0" +dependencies = [ + "anyhow", + "derive_more", + "env_logger 0.9.3", + "finality-grandpa", + "frame-support", + "futures", + "hash-db 0.16.0", + "hex", + "hex-literal 0.3.4", + "ismp", + "ismp-grandpa-primitives", + "ismp-grandpa-prover", + "log", + "parity-scale-codec", + "polkadot-core-primitives", + "sc-finality-grandpa-rpc", + "serde", + "sp-consensus-grandpa", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-trie 7.0.0", + "subxt", + "tokio", ] [[package]] @@ -3083,7 +3253,7 @@ dependencies = [ "cumulus-primitives-core", "frame-support", "frame-system", - "hash-db", + "hash-db 0.16.0", "hex-literal 0.4.1", "ismp", "ismp-primitives", @@ -3093,10 +3263,11 @@ dependencies = [ "scale-info", "serde", "sp-consensus-aura", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-trie", + "sp-inherents 4.0.0-dev", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-trie 7.0.0", + "substrate-state-machine", ] [[package]] @@ -3111,17 +3282,17 @@ dependencies = [ "ismp-parachain", "ismp-parachain-runtime-api", "parity-scale-codec", - "sp-api", - "sp-blockchain", - "sp-inherents", - "sp-runtime", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", ] [[package]] name = "ismp-parachain-runtime-api" version = "0.1.0" dependencies = [ - "sp-api", + "sp-api 4.0.0-dev", ] [[package]] @@ -3129,13 +3300,16 @@ name = "ismp-primitives" version = "0.1.0" dependencies = [ "ckb-merkle-mountain-range", + "frame-support", "frame-system", "ismp", "parity-scale-codec", "primitive-types", "scale-info", "serde", - "sp-runtime", + "sp-consensus-aura", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] @@ -3150,13 +3324,13 @@ dependencies = [ "jsonrpsee", "pallet-ismp", "parity-scale-codec", - "sc-client-api", + "sc-client-api 4.0.0-dev", "serde", "serde_json", - "sp-api", - "sp-blockchain", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] @@ -3168,7 +3342,7 @@ dependencies = [ "pallet-ismp", "parity-scale-codec", "serde", - "sp-api", + "sp-api 4.0.0-dev", "sp-std 5.0.0", ] @@ -3222,11 +3396,35 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ + "jsonrpsee-client-transport", "jsonrpsee-core", + "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", + "jsonrpsee-ws-client", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", "tracing", + "webpki-roots", ] [[package]] @@ -3237,9 +3435,11 @@ checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", "arrayvec 0.7.4", + "async-lock", "async-trait", "beef", "futures-channel", + "futures-timer", "futures-util", "globset", "hyper", @@ -3255,6 +3455,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee-proc-macros" version = "0.16.2" @@ -3304,6 +3523,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + [[package]] name = "k256" version = "0.13.1" @@ -3917,9 +4148,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "24e6ab01971eb092ffe6a7d42f49f9ff42662f17604681e2843ad65077ba47dc" dependencies = [ "cc", "pkg-config", @@ -4113,6 +4344,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -4122,13 +4362,23 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memory-db" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +dependencies = [ + "hash-db 0.15.2", + "hashbrown 0.12.3", +] + [[package]] name = "memory-db" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "hash-db", + "hash-db 0.16.0", ] [[package]] @@ -4548,6 +4798,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + [[package]] name = "object" version = "0.31.1" @@ -4683,10 +4945,35 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] +[[package]] +name = "pallet-grandpa-client" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "cumulus-primitives-core", + "finality-grandpa", + "frame-support", + "frame-system", + "ismp", + "ismp-grandpa-primitives", + "ismp-grandpa-verifier", + "ismp-primitives", + "pallet-ismp", + "parity-scale-codec", + "primitive-types", + "scale-info", + "sp-consensus-aura", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-trie 7.0.0", + "substrate-state-machine", +] + [[package]] name = "pallet-ismp" version = "0.1.0" @@ -4694,7 +4981,7 @@ dependencies = [ "ckb-merkle-mountain-range", "derive_more", "enum-as-inner", - "env_logger", + "env_logger 0.10.0", "frame-benchmarking", "frame-support", "frame-system", @@ -4706,10 +4993,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-api", + "sp-api 4.0.0-dev", "sp-core 7.0.0", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] @@ -4724,9 +5011,9 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-inherents", - "sp-io", - "sp-runtime", + "sp-inherents 4.0.0-dev", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", "sp-timestamp", ] @@ -4986,7 +5273,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] @@ -5002,7 +5289,7 @@ dependencies = [ "parking_lot 0.12.1", "polkadot-node-primitives", "polkadot-primitives", - "sc-network", + "sc-network 0.10.0-dev", "sp-core 7.0.0", "thiserror", "tokio", @@ -5022,8 +5309,8 @@ dependencies = [ "prioritized-metered-channel", "sc-cli", "sc-service", - "sc-tracing", - "substrate-prometheus-endpoint", + "sc-tracing 4.0.0-dev", + "substrate-prometheus-endpoint 0.10.0-dev", "tracing-gum", ] @@ -5043,7 +5330,7 @@ dependencies = [ "polkadot-primitives", "rand 0.8.5", "sc-authority-discovery", - "sc-network", + "sc-network 0.10.0-dev", "strum", "thiserror", "tracing-gum", @@ -5061,12 +5348,12 @@ dependencies = [ "polkadot-primitives", "schnorrkel", "serde", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus-babe", "sp-core 7.0.0", - "sp-keystore", - "sp-maybe-compressed-blob", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-maybe-compressed-blob 4.1.0-dev", + "sp-runtime 7.0.0", "thiserror", "zstd 0.11.2+zstd.1.5.2", ] @@ -5085,12 +5372,12 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "polkadot-statement-table", - "sc-network", + "sc-network 0.10.0-dev", "smallvec", - "sp-api", + "sp-api 4.0.0-dev", "sp-authority-discovery", "sp-consensus-babe", - "substrate-prometheus-endpoint", + "substrate-prometheus-endpoint 0.10.0-dev", "thiserror", ] @@ -5110,8 +5397,8 @@ dependencies = [ "polkadot-node-primitives", "polkadot-node-subsystem-types", "polkadot-primitives", - "sc-client-api", - "sp-api", + "sc-client-api 4.0.0-dev", + "sp-api 4.0.0-dev", "sp-core 7.0.0", "tikv-jemalloc-ctl", "tracing-gum", @@ -5130,7 +5417,7 @@ dependencies = [ "scale-info", "serde", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] @@ -5146,17 +5433,17 @@ dependencies = [ "polkadot-parachain", "scale-info", "serde", - "sp-api", - "sp-application-crypto", - "sp-arithmetic", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", "sp-authority-discovery", "sp-consensus-slots", "sp-core 7.0.0", - "sp-inherents", - "sp-io", - "sp-keystore", - "sp-runtime", - "sp-staking", + "sp-inherents 4.0.0-dev", + "sp-io 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-staking 4.0.0-dev", "sp-std 5.0.0", ] @@ -5491,9 +5778,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +checksum = "f31999cfc7927c4e212e60fd50934ab40e8e8bfd2d493d6095d2d306bc0764d9" dependencies = [ "bytes", "rand 0.8.5", @@ -5509,9 +5796,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -6055,6 +6342,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-allocator" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa8784b53aa48736a4df4c351162a63b17e7c28c77b6a2e92dfb9bc49709107" +dependencies = [ + "log", + "sp-core 18.0.0", + "sp-wasm-interface 12.0.0", + "thiserror", +] + [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" @@ -6070,16 +6369,16 @@ dependencies = [ "prost", "prost-build", "rand 0.8.5", - "sc-client-api", - "sc-network", - "sc-network-common", - "sp-api", + "sc-client-api 4.0.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sp-api 4.0.0-dev", "sp-authority-discovery", - "sp-blockchain", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-keystore", - "sp-runtime", - "substrate-prometheus-endpoint", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", "thiserror", ] @@ -6089,13 +6388,30 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", - "sc-client-api", - "sp-api", - "sp-block-builder", - "sp-blockchain", + "sc-client-api 4.0.0-dev", + "sp-api 4.0.0-dev", + "sp-block-builder 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-inherents", - "sp-runtime", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", +] + +[[package]] +name = "sc-block-builder" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b4d582801c5d5f8dcfcba46b4724958283fda6b6bb44540f0ba931da736add2" +dependencies = [ + "parity-scale-codec", + "sc-client-api 18.0.0", + "sp-api 16.0.0", + "sp-block-builder 16.0.0", + "sp-blockchain 18.0.0", + "sp-core 18.0.0", + "sp-inherents 16.0.0", + "sp-runtime 20.0.0", + "sp-state-machine 0.24.0", ] [[package]] @@ -6104,17 +6420,33 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "memmap2", - "sc-chain-spec-derive", - "sc-client-api", - "sc-executor", - "sc-network", - "sc-telemetry", + "sc-chain-spec-derive 4.0.0-dev", + "sc-client-api 4.0.0-dev", + "sc-executor 0.10.0-dev", + "sc-network 0.10.0-dev", + "sc-telemetry 4.0.0-dev", "serde", "serde_json", - "sp-blockchain", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", +] + +[[package]] +name = "sc-chain-spec" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09024be9bc50b0dd86dbf90f982ac83001b69459758db695663a14242ca8198" +dependencies = [ + "memmap2", + "sc-chain-spec-derive 5.0.0", + "sc-network-common 0.23.0", + "sc-telemetry 7.0.0", + "serde", + "serde_json", + "sp-core 18.0.0", + "sp-runtime 20.0.0", ] [[package]] @@ -6129,10 +6461,22 @@ dependencies = [ ] [[package]] -name = "sc-cli" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ +name = "sc-chain-spec-derive" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7185e052b4138f7023c6033926f458f2e46f215b6e02e0b5ac5863caa17b8cba" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sc-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ "array-bytes", "chrono", "clap", @@ -6145,24 +6489,24 @@ dependencies = [ "rand 0.8.5", "regex", "rpassword", - "sc-client-api", + "sc-client-api 4.0.0-dev", "sc-client-db", "sc-keystore", - "sc-network", - "sc-network-common", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", "sc-service", - "sc-telemetry", - "sc-tracing", - "sc-utils", + "sc-telemetry 4.0.0-dev", + "sc-tracing 4.0.0-dev", + "sc-utils 4.0.0-dev", "serde", "serde_json", - "sp-blockchain", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", "sp-keyring", - "sp-keystore", - "sp-panic-handler", - "sp-runtime", - "sp-version", + "sp-keystore 0.13.0", + "sp-panic-handler 5.0.0", + "sp-runtime 7.0.0", + "sp-version 5.0.0", "thiserror", "tiny-bip39", "tokio", @@ -6178,20 +6522,47 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sc-executor", - "sc-transaction-pool-api", - "sc-utils", - "sp-api", - "sp-blockchain", - "sp-consensus", + "sc-executor 0.10.0-dev", + "sc-transaction-pool-api 4.0.0-dev", + "sc-utils 4.0.0-dev", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", "sp-core 7.0.0", - "sp-database", + "sp-database 4.0.0-dev", "sp-externalities 0.13.0", - "sp-keystore", - "sp-runtime", - "sp-state-machine", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "sp-storage 7.0.0", - "substrate-prometheus-endpoint", + "substrate-prometheus-endpoint 0.10.0-dev", +] + +[[package]] +name = "sc-client-api" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd2bd00e841dfb8db0b477e7a03f60fa9e79b5cefd4ec2013e212a3a86ebcd3" +dependencies = [ + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor 0.22.0", + "sc-transaction-pool-api 18.0.0", + "sc-utils 6.0.0", + "sp-api 16.0.0", + "sp-blockchain 18.0.0", + "sp-consensus 0.22.0", + "sp-core 18.0.0", + "sp-database 5.0.0", + "sp-externalities 0.18.0", + "sp-keystore 0.24.0", + "sp-runtime 20.0.0", + "sp-state-machine 0.24.0", + "sp-storage 12.0.0", + "substrate-prometheus-endpoint 0.12.0", ] [[package]] @@ -6199,7 +6570,7 @@ name = "sc-client-db" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "hash-db", + "hash-db 0.16.0", "kvdb", "kvdb-memorydb", "kvdb-rocksdb", @@ -6208,16 +6579,16 @@ dependencies = [ "parity-db", "parity-scale-codec", "parking_lot 0.12.1", - "sc-client-api", + "sc-client-api 4.0.0-dev", "sc-state-db", "schnellru", - "sp-arithmetic", - "sp-blockchain", + "sp-arithmetic 6.0.0", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-database", - "sp-runtime", - "sp-state-machine", - "sp-trie", + "sp-database 4.0.0-dev", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-trie 7.0.0", ] [[package]] @@ -6232,16 +6603,102 @@ dependencies = [ "log", "mockall", "parking_lot 0.12.1", - "sc-client-api", - "sc-utils", + "sc-client-api 4.0.0-dev", + "sc-utils 4.0.0-dev", + "serde", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "substrate-prometheus-endpoint 0.10.0-dev", + "thiserror", +] + +[[package]] +name = "sc-consensus" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49fe228611617f1348a954e656b2b544eee5c0054000b712af8b0f05635b0015" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "libp2p", + "log", + "mockall", + "parking_lot 0.12.1", + "sc-client-api 18.0.0", + "sc-utils 6.0.0", + "serde", + "sp-api 16.0.0", + "sp-blockchain 18.0.0", + "sp-consensus 0.22.0", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "sp-state-machine 0.24.0", + "substrate-prometheus-endpoint 0.12.0", + "thiserror", +] + +[[package]] +name = "sc-consensus-grandpa" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "ahash 0.8.3", + "array-bytes", + "async-trait", + "dyn-clone", + "finality-grandpa", + "fork-tree 3.0.0", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-block-builder 0.10.0-dev", + "sc-chain-spec 4.0.0-dev", + "sc-client-api 4.0.0-dev", + "sc-consensus 0.10.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-network-gossip 0.10.0-dev", + "sc-telemetry 4.0.0-dev", + "sc-utils 4.0.0-dev", + "serde_json", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", + "sp-consensus-grandpa", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", + "thiserror", +] + +[[package]] +name = "sc-consensus-grandpa-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "finality-grandpa", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api 4.0.0-dev", + "sc-consensus-grandpa", + "sc-rpc 4.0.0-dev", "serde", - "sp-api", - "sp-blockchain", - "sp-consensus", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", - "sp-state-machine", - "substrate-prometheus-endpoint", + "sp-runtime 7.0.0", "thiserror", ] @@ -6253,48 +6710,101 @@ dependencies = [ "lru 0.8.1", "parity-scale-codec", "parking_lot 0.12.1", - "sc-executor-common", - "sc-executor-wasmi", - "sc-executor-wasmtime", - "sp-api", + "sc-executor-common 0.10.0-dev", + "sc-executor-wasmi 0.10.0-dev", + "sc-executor-wasmtime 0.10.0-dev", + "sp-api 4.0.0-dev", "sp-core 7.0.0", "sp-externalities 0.13.0", - "sp-io", - "sp-panic-handler", + "sp-io 7.0.0", + "sp-panic-handler 5.0.0", "sp-runtime-interface 7.0.0", - "sp-trie", - "sp-version", + "sp-trie 7.0.0", + "sp-version 5.0.0", "sp-wasm-interface 7.0.0", "tracing", "wasmi", ] +[[package]] +name = "sc-executor" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e73e3ece87f0d6ca9f3f1f903213ee8aab58287dc0b3921779afbf62d34411f" +dependencies = [ + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor-common 0.20.0", + "sc-executor-wasmi 0.20.0", + "sc-executor-wasmtime 0.20.0", + "sp-api 16.0.0", + "sp-core 18.0.0", + "sp-externalities 0.18.0", + "sp-io 19.0.0", + "sp-panic-handler 7.0.0", + "sp-runtime-interface 15.0.0", + "sp-trie 18.0.0", + "sp-version 18.0.0", + "sp-wasm-interface 12.0.0", + "tracing", + "wasmi", +] + [[package]] name = "sc-executor-common" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "sc-allocator", - "sp-maybe-compressed-blob", + "sc-allocator 4.1.0-dev", + "sp-maybe-compressed-blob 4.1.0-dev", "sp-wasm-interface 7.0.0", "thiserror", "wasm-instrument", "wasmi", ] +[[package]] +name = "sc-executor-common" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eff0b3bb1d41ca34481215297090496c35e91c6d66a71e3a26960c8fce917ea" +dependencies = [ + "sc-allocator 14.0.0", + "sp-maybe-compressed-blob 5.0.0", + "sp-wasm-interface 12.0.0", + "thiserror", + "wasm-instrument", + "wasmi", +] + [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "log", - "sc-allocator", - "sc-executor-common", + "sc-allocator 4.1.0-dev", + "sc-executor-common 0.10.0-dev", "sp-runtime-interface 7.0.0", "sp-wasm-interface 7.0.0", "wasmi", ] +[[package]] +name = "sc-executor-wasmi" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034a219dc830432961b377f28d50d8251b2f20c26471436c47cf5268126e8e9f" +dependencies = [ + "log", + "sc-allocator 14.0.0", + "sc-executor-common 0.20.0", + "sp-runtime-interface 15.0.0", + "sp-wasm-interface 12.0.0", + "wasmi", +] + [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" @@ -6306,11 +6816,92 @@ dependencies = [ "log", "once_cell", "rustix 0.36.15", - "sc-allocator", - "sc-executor-common", + "sc-allocator 4.1.0-dev", + "sc-executor-common 0.10.0-dev", "sp-runtime-interface 7.0.0", "sp-wasm-interface 7.0.0", - "wasmtime", + "wasmtime 6.0.2", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df03fdc79767bbc993cfcde07a8a09040e60fed1ca7341e3d17fc4c461e8457" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "log", + "once_cell", + "rustix 0.36.15", + "sc-allocator 14.0.0", + "sc-executor-common 0.20.0", + "sp-runtime-interface 15.0.0", + "sp-wasm-interface 12.0.0", + "wasmtime 6.0.2", +] + +[[package]] +name = "sc-finality-grandpa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5923b98c37d2b246db78aae954aa42903537dacfa1effadf5dadb68bd932b16d" +dependencies = [ + "ahash 0.8.3", + "array-bytes", + "async-trait", + "dyn-clone", + "finality-grandpa", + "fork-tree 7.0.0", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-block-builder 0.23.0", + "sc-chain-spec 17.0.0", + "sc-client-api 18.0.0", + "sc-consensus 0.23.0", + "sc-network 0.24.0", + "sc-network-common 0.23.0", + "sc-network-gossip 0.24.0", + "sc-telemetry 7.0.0", + "sc-utils 6.0.0", + "serde_json", + "sp-api 16.0.0", + "sp-application-crypto 19.0.0", + "sp-arithmetic 13.0.0", + "sp-blockchain 18.0.0", + "sp-consensus 0.22.0", + "sp-core 18.0.0", + "sp-finality-grandpa", + "sp-keystore 0.24.0", + "sp-runtime 20.0.0", + "substrate-prometheus-endpoint 0.12.0", + "thiserror", +] + +[[package]] +name = "sc-finality-grandpa-rpc" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb40df2000f91bdcfc30575e5ca51d0fbd13deeda23246be6221ffa01ecc9ca" +dependencies = [ + "finality-grandpa", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api 18.0.0", + "sc-finality-grandpa", + "sc-rpc 19.0.0", + "serde", + "sp-blockchain 18.0.0", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "thiserror", ] [[package]] @@ -6322,11 +6913,11 @@ dependencies = [ "futures", "futures-timer", "log", - "sc-client-api", - "sc-network", - "sc-network-common", - "sp-blockchain", - "sp-runtime", + "sc-client-api 4.0.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-runtime 7.0.0", ] [[package]] @@ -6338,9 +6929,9 @@ dependencies = [ "async-trait", "parking_lot 0.12.1", "serde_json", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-core 7.0.0", - "sp-keystore", + "sp-keystore 0.13.0", "thiserror", ] @@ -6368,22 +6959,66 @@ dependencies = [ "parking_lot 0.12.1", "pin-project", "rand 0.8.5", - "sc-block-builder", - "sc-client-api", - "sc-consensus", - "sc-network-common", - "sc-peerset", - "sc-utils", + "sc-block-builder 0.10.0-dev", + "sc-client-api 4.0.0-dev", + "sc-consensus 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sc-utils 4.0.0-dev", "serde", "serde_json", "smallvec", "snow", - "sp-arithmetic", - "sp-blockchain", - "sp-consensus", + "sp-arithmetic 6.0.0", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", "sp-core 7.0.0", - "sp-runtime", - "substrate-prometheus-endpoint", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", + "thiserror", + "unsigned-varint", + "zeroize", +] + +[[package]] +name = "sc-network" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86a525c3f43fd1e9e09665763d0f0823699f5e78e31909b8949b03ad60a12806" +dependencies = [ + "array-bytes", + "async-trait", + "asynchronous-codec", + "backtrace", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "log", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder 0.23.0", + "sc-client-api 18.0.0", + "sc-consensus 0.23.0", + "sc-network-common 0.23.0", + "sc-peerset 7.0.0", + "sc-utils 6.0.0", + "serde", + "serde_json", + "smallvec", + "sp-arithmetic 13.0.0", + "sp-blockchain 18.0.0", + "sp-consensus 0.22.0", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "substrate-prometheus-endpoint 0.12.0", "thiserror", "unsigned-varint", "zeroize", @@ -6400,11 +7035,11 @@ dependencies = [ "log", "prost", "prost-build", - "sc-client-api", - "sc-network", - "sc-network-common", - "sp-blockchain", - "sp-runtime", + "sc-client-api 4.0.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-runtime 7.0.0", "thiserror", "unsigned-varint", ] @@ -6423,20 +7058,85 @@ dependencies = [ "libp2p", "parity-scale-codec", "prost-build", - "sc-consensus", - "sc-peerset", - "sc-utils", + "sc-consensus 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sc-utils 4.0.0-dev", "serde", "smallvec", - "sp-blockchain", - "sp-consensus", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", "sp-consensus-grandpa", - "sp-runtime", - "substrate-prometheus-endpoint", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", "thiserror", "zeroize", ] +[[package]] +name = "sc-network-common" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39d56f7508b31ac31144903cb2e48268842ec87d3fc5237772e29473868b27" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes", + "futures", + "futures-timer", + "libp2p", + "linked_hash_set", + "parity-scale-codec", + "prost-build", + "sc-consensus 0.23.0", + "sc-peerset 7.0.0", + "serde", + "smallvec", + "sp-blockchain 18.0.0", + "sp-consensus 0.22.0", + "sp-finality-grandpa", + "sp-runtime 20.0.0", + "substrate-prometheus-endpoint 0.12.0", + "thiserror", +] + +[[package]] +name = "sc-network-gossip" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "ahash 0.8.3", + "futures", + "futures-timer", + "libp2p", + "log", + "lru 0.8.1", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", + "tracing", +] + +[[package]] +name = "sc-network-gossip" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b63eedbe7e08661777e6f5960952fcc8d79d98d691a99f9bc05d5850a9a9f2" +dependencies = [ + "ahash 0.8.3", + "futures", + "futures-timer", + "libp2p", + "log", + "lru 0.8.1", + "sc-network-common 0.23.0", + "sc-peerset 7.0.0", + "sp-runtime 20.0.0", + "substrate-prometheus-endpoint 0.12.0", + "tracing", +] + [[package]] name = "sc-network-light" version = "0.10.0-dev" @@ -6449,13 +7149,13 @@ dependencies = [ "parity-scale-codec", "prost", "prost-build", - "sc-client-api", - "sc-network", - "sc-network-common", - "sc-peerset", - "sp-blockchain", + "sc-client-api 4.0.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "thiserror", ] @@ -6466,7 +7166,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "array-bytes", "async-trait", - "fork-tree", + "fork-tree 3.0.0", "futures", "futures-timer", "libp2p", @@ -6476,20 +7176,20 @@ dependencies = [ "parity-scale-codec", "prost", "prost-build", - "sc-client-api", - "sc-consensus", - "sc-network", - "sc-network-common", - "sc-peerset", - "sc-utils", + "sc-client-api 4.0.0-dev", + "sc-consensus 0.10.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sc-utils 4.0.0-dev", "smallvec", - "sp-arithmetic", - "sp-blockchain", - "sp-consensus", + "sp-arithmetic 6.0.0", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", "sp-consensus-grandpa", "sp-core 7.0.0", - "sp-runtime", - "substrate-prometheus-endpoint", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", "thiserror", ] @@ -6504,13 +7204,13 @@ dependencies = [ "log", "parity-scale-codec", "pin-project", - "sc-network", - "sc-network-common", - "sc-peerset", - "sc-utils", - "sp-consensus", - "sp-runtime", - "substrate-prometheus-endpoint", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sc-utils 4.0.0-dev", + "sp-consensus 0.10.0-dev", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint 0.10.0-dev", ] [[package]] @@ -6531,15 +7231,15 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", "rand 0.8.5", - "sc-client-api", - "sc-network", - "sc-network-common", - "sc-peerset", - "sc-utils", - "sp-api", + "sc-client-api 4.0.0-dev", + "sc-network 0.10.0-dev", + "sc-network-common 0.10.0-dev", + "sc-peerset 4.0.0-dev", + "sc-utils 4.0.0-dev", + "sp-api 4.0.0-dev", "sp-core 7.0.0", - "sp-offchain", - "sp-runtime", + "sp-offchain 4.0.0-dev", + "sp-runtime 7.0.0", "threadpool", "tracing", ] @@ -6552,7 +7252,21 @@ dependencies = [ "futures", "libp2p", "log", - "sc-utils", + "sc-utils 4.0.0-dev", + "serde_json", + "wasm-timer", +] + +[[package]] +name = "sc-peerset" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3db7a2b9fba75b084ea9a51b4911a99d9387777c60e2cfacfd60a3676ea788" +dependencies = [ + "futures", + "libp2p", + "log", + "sc-utils 6.0.0", "serde_json", "wasm-timer", ] @@ -6567,23 +7281,54 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sc-block-builder", - "sc-chain-spec", - "sc-client-api", - "sc-rpc-api", - "sc-tracing", - "sc-transaction-pool-api", - "sc-utils", + "sc-block-builder 0.10.0-dev", + "sc-chain-spec 4.0.0-dev", + "sc-client-api 4.0.0-dev", + "sc-rpc-api 0.10.0-dev", + "sc-tracing 4.0.0-dev", + "sc-transaction-pool-api 4.0.0-dev", + "sc-utils 4.0.0-dev", "serde_json", - "sp-api", - "sp-blockchain", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-keystore", - "sp-offchain", - "sp-rpc", - "sp-runtime", - "sp-session", - "sp-version", + "sp-keystore 0.13.0", + "sp-offchain 4.0.0-dev", + "sp-rpc 6.0.0", + "sp-runtime 7.0.0", + "sp-session 4.0.0-dev", + "sp-version 5.0.0", + "tokio", +] + +[[package]] +name = "sc-rpc" +version = "19.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ac425927dbd042a503a4b1f66aa0c2bf8f928f123de04ce0e742e13187edc3" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-block-builder 0.23.0", + "sc-chain-spec 17.0.0", + "sc-client-api 18.0.0", + "sc-rpc-api 0.23.0", + "sc-tracing 18.0.0", + "sc-transaction-pool-api 18.0.0", + "sc-utils 6.0.0", + "serde_json", + "sp-api 16.0.0", + "sp-blockchain 18.0.0", + "sp-core 18.0.0", + "sp-keystore 0.24.0", + "sp-offchain 16.0.0", + "sp-rpc 17.0.0", + "sp-runtime 20.0.0", + "sp-session 17.0.0", + "sp-version 18.0.0", "tokio", ] @@ -6594,28 +7339,64 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "jsonrpsee", "parity-scale-codec", - "sc-chain-spec", - "sc-transaction-pool-api", + "sc-chain-spec 4.0.0-dev", + "sc-transaction-pool-api 4.0.0-dev", "scale-info", "serde", "serde_json", "sp-core 7.0.0", - "sp-rpc", - "sp-runtime", - "sp-version", + "sp-rpc 6.0.0", + "sp-runtime 7.0.0", + "sp-version 5.0.0", "thiserror", ] [[package]] -name = "sc-rpc-server" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "sc-rpc-api" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb049c6dea3c497059f4a8c6153cb6715d1f0d9af74635f503114b46692685a0" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec 17.0.0", + "sc-transaction-pool-api 18.0.0", + "scale-info", + "serde", + "serde_json", + "sp-core 18.0.0", + "sp-rpc 17.0.0", + "sp-runtime 20.0.0", + "sp-version 18.0.0", + "thiserror", +] + +[[package]] +name = "sc-rpc-server" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "http", "jsonrpsee", "log", "serde_json", - "substrate-prometheus-endpoint", + "substrate-prometheus-endpoint 0.10.0-dev", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "sc-rpc-server" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea79cd739532f851731af7917383bcca11f811f5e6f315e34f72873cfaebaac" +dependencies = [ + "http", + "jsonrpsee", + "log", + "serde_json", + "substrate-prometheus-endpoint 0.12.0", "tokio", "tower", "tower-http", @@ -6634,15 +7415,15 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sc-chain-spec", - "sc-client-api", - "sc-transaction-pool-api", + "sc-chain-spec 4.0.0-dev", + "sc-client-api 4.0.0-dev", + "sc-transaction-pool-api 4.0.0-dev", "serde", - "sp-api", - "sp-blockchain", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", - "sp-version", + "sp-runtime 7.0.0", + "sp-version 5.0.0", "thiserror", "tokio-stream", ] @@ -6663,49 +7444,49 @@ dependencies = [ "parking_lot 0.12.1", "pin-project", "rand 0.8.5", - "sc-block-builder", - "sc-chain-spec", - "sc-client-api", + "sc-block-builder 0.10.0-dev", + "sc-chain-spec 4.0.0-dev", + "sc-client-api 4.0.0-dev", "sc-client-db", - "sc-consensus", - "sc-executor", + "sc-consensus 0.10.0-dev", + "sc-executor 0.10.0-dev", "sc-informant", "sc-keystore", - "sc-network", + "sc-network 0.10.0-dev", "sc-network-bitswap", - "sc-network-common", + "sc-network-common 0.10.0-dev", "sc-network-light", "sc-network-sync", "sc-network-transactions", "sc-offchain", - "sc-rpc", - "sc-rpc-server", + "sc-rpc 4.0.0-dev", + "sc-rpc-server 4.0.0-dev", "sc-rpc-spec-v2", "sc-storage-monitor", "sc-sysinfo", - "sc-telemetry", - "sc-tracing", + "sc-telemetry 4.0.0-dev", + "sc-tracing 4.0.0-dev", "sc-transaction-pool", - "sc-transaction-pool-api", - "sc-utils", + "sc-transaction-pool-api 4.0.0-dev", + "sc-utils 4.0.0-dev", "serde", "serde_json", - "sp-api", - "sp-blockchain", - "sp-consensus", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", + "sp-consensus 0.10.0-dev", "sp-core 7.0.0", "sp-externalities 0.13.0", - "sp-keystore", - "sp-runtime", - "sp-session", - "sp-state-machine", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-session 4.0.0-dev", + "sp-state-machine 0.13.0", "sp-storage 7.0.0", "sp-transaction-pool", "sp-transaction-storage-proof", - "sp-trie", - "sp-version", + "sp-trie 7.0.0", + "sp-version 5.0.0", "static_init", - "substrate-prometheus-endpoint", + "substrate-prometheus-endpoint 0.10.0-dev", "tempfile", "thiserror", "tokio", @@ -6734,7 +7515,7 @@ dependencies = [ "futures", "log", "sc-client-db", - "sc-utils", + "sc-utils 4.0.0-dev", "sp-core 7.0.0", "thiserror", "tokio", @@ -6751,11 +7532,11 @@ dependencies = [ "rand 0.8.5", "rand_pcg", "regex", - "sc-telemetry", + "sc-telemetry 4.0.0-dev", "serde", "serde_json", "sp-core 7.0.0", - "sp-io", + "sp-io 7.0.0", "sp-std 5.0.0", ] @@ -6771,7 +7552,27 @@ dependencies = [ "parking_lot 0.12.1", "pin-project", "rand 0.8.5", - "sc-utils", + "sc-utils 4.0.0-dev", + "serde", + "serde_json", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "sc-telemetry" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b48c2f39c9ba57b56877bedb7cf726a4f378969e87db9fdbc1feb8f6e34161e2" +dependencies = [ + "chrono", + "futures", + "libp2p", + "log", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-utils 6.0.0", "serde", "serde_json", "thiserror", @@ -6793,15 +7594,15 @@ dependencies = [ "parking_lot 0.12.1", "regex", "rustc-hash", - "sc-client-api", - "sc-rpc-server", - "sc-tracing-proc-macro", + "sc-client-api 4.0.0-dev", + "sc-rpc-server 4.0.0-dev", + "sc-tracing-proc-macro 4.0.0-dev", "serde", - "sp-api", - "sp-blockchain", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-rpc", - "sp-runtime", + "sp-rpc 6.0.0", + "sp-runtime 7.0.0", "sp-tracing 6.0.0", "thiserror", "tracing", @@ -6809,6 +7610,38 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "sc-tracing" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535733abff4107ef013706053e4c6a9f6b308b097a55ba10aab160fd3163112a" +dependencies = [ + "ansi_term", + "atty", + "chrono", + "lazy_static", + "libc", + "log", + "once_cell", + "parking_lot 0.12.1", + "regex", + "rustc-hash", + "sc-client-api 18.0.0", + "sc-rpc-server 6.0.0", + "sc-tracing-proc-macro 5.0.0", + "serde", + "sp-api 16.0.0", + "sp-blockchain 18.0.0", + "sp-core 18.0.0", + "sp-rpc 17.0.0", + "sp-runtime 20.0.0", + "sp-tracing 9.0.0", + "thiserror", + "tracing", + "tracing-log", + "tracing-subscriber", +] + [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" @@ -6820,6 +7653,18 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "sc-tracing-proc-macro" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3124ec69b179bca71cd20babf10f2198a5fc0d55f3e06a8f284e90930c76d3df" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" @@ -6833,17 +7678,17 @@ dependencies = [ "num-traits", "parity-scale-codec", "parking_lot 0.12.1", - "sc-client-api", - "sc-transaction-pool-api", - "sc-utils", + "sc-client-api 4.0.0-dev", + "sc-transaction-pool-api 4.0.0-dev", + "sc-utils 4.0.0-dev", "serde", - "sp-api", - "sp-blockchain", + "sp-api 4.0.0-dev", + "sp-blockchain 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "sp-tracing 6.0.0", "sp-transaction-pool", - "substrate-prometheus-endpoint", + "substrate-prometheus-endpoint 0.10.0-dev", "thiserror", ] @@ -6856,8 +7701,23 @@ dependencies = [ "futures", "log", "serde", - "sp-blockchain", - "sp-runtime", + "sp-blockchain 4.0.0-dev", + "sp-runtime 7.0.0", + "thiserror", +] + +[[package]] +name = "sc-transaction-pool-api" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92f2fa27a44912eeed796712b4b79fd5bb3c3763d54721e3414270123828054e" +dependencies = [ + "async-trait", + "futures", + "log", + "serde", + "sp-blockchain 18.0.0", + "sp-runtime 20.0.0", "thiserror", ] @@ -6873,7 +7733,89 @@ dependencies = [ "log", "parking_lot 0.12.1", "prometheus", - "sp-arithmetic", + "sp-arithmetic 6.0.0", +] + +[[package]] +name = "sc-utils" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c227f2e96f3dca6f8a0dbd0d935ac273219365fc50d762d0328d66129fbd1c5e" +dependencies = [ + "backtrace", + "futures", + "futures-timer", + "lazy_static", + "log", + "parking_lot 0.12.1", + "prometheus", +] + +[[package]] +name = "scale-bits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "scale-decode" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0459d00b0dbd2e765009924a78ef36b2ff7ba116292d732f00eb0ed8e465d15" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode-derive", + "scale-info", + "smallvec", + "thiserror", +] + +[[package]] +name = "scale-decode-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4391f0dfbb6690f035f6d2a15d6a12f88cc5395c36bcc056db07ffa2a90870ec" +dependencies = [ + "darling 0.14.4", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0401b7cdae8b8aa33725f3611a051358d5b32887ecaa0fda5953a775b2d4d76" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-encode-derive", + "scale-info", + "smallvec", + "thiserror", +] + +[[package]] +name = "scale-encode-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "316e0fb10ec0fee266822bd641bab5e332a4ab80ef8c5b5ff35e5401a394f5a6" +dependencies = [ + "darling 0.14.4", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -6902,6 +7844,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "scale-value" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2096d36e94ce9bf87d8addb752423b6b19730dc88edd7cc452bb2b90573f7a7" +dependencies = [ + "base58", + "blake2", + "either", + "frame-metadata", + "parity-scale-codec", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", + "serde", + "thiserror", + "yap", +] + [[package]] name = "schannel" version = "0.1.22" @@ -7035,9 +7997,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -7048,9 +8010,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -7064,18 +8026,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.174" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" +checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.174" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" +checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" dependencies = [ "proc-macro2", "quote", @@ -7312,18 +8274,37 @@ name = "sp-api" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", "parity-scale-codec", "scale-info", - "sp-api-proc-macro", + "sp-api-proc-macro 4.0.0-dev", "sp-core 7.0.0", "sp-metadata-ir", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "sp-std 5.0.0", - "sp-trie", - "sp-version", + "sp-trie 7.0.0", + "sp-version 5.0.0", + "thiserror", +] + +[[package]] +name = "sp-api" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2243c0405c7fbb97bac008638001daf33fe155e15f8ba28f994908b7359f5b" +dependencies = [ + "hash-db 0.15.2", + "log", + "parity-scale-codec", + "sp-api-proc-macro 5.0.0", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "sp-state-machine 0.24.0", + "sp-std 7.0.0", + "sp-trie 18.0.0", + "sp-version 18.0.0", "thiserror", ] @@ -7341,6 +8322,19 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "sp-api-proc-macro" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1202abd8f0b1709386c29071539d57009a8c0ea8e841418dc114461a01a3040" +dependencies = [ + "blake2", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sp-application-crypto" version = "7.0.0" @@ -7350,10 +8344,38 @@ dependencies = [ "scale-info", "serde", "sp-core 7.0.0", - "sp-io", + "sp-io 7.0.0", "sp-std 5.0.0", ] +[[package]] +name = "sp-application-crypto" +version = "19.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e5d5ec374fc23f4e1b87219be18e01080d8a21a2dee3b49df8befeddbf5780" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 18.0.0", + "sp-io 19.0.0", + "sp-std 7.0.0", +] + +[[package]] +name = "sp-application-crypto" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899492ea547816d5dfe9a5a2ecc32f65a7110805af6da3380aa4902371b31dc2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 21.0.0", + "sp-io 23.0.0", + "sp-std 8.0.0", +] + [[package]] name = "sp-arithmetic" version = "6.0.0" @@ -7368,6 +8390,36 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-arithmetic" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3dd56a02ca86de62dc9485d95830a5fed56fd7e4a22b13c01e62e73bc2094d2" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std 7.0.0", + "static_assertions", +] + +[[package]] +name = "sp-arithmetic" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6020576e544c6824a51d651bc8df8e6ab67cd59f1c9ac09868bb81a5199ded" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std 8.0.0", + "static_assertions", +] + [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" @@ -7375,9 +8427,9 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "parity-scale-codec", "scale-info", - "sp-api", - "sp-application-crypto", - "sp-runtime", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] @@ -7387,12 +8439,25 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", - "sp-api", - "sp-inherents", - "sp-runtime", + "sp-api 4.0.0-dev", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] +[[package]] +name = "sp-block-builder" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36478fa5a773d732e35d22fa25d63f7982b2970a9048a25fcd947398ecc84e31" +dependencies = [ + "parity-scale-codec", + "sp-api 16.0.0", + "sp-inherents 16.0.0", + "sp-runtime 20.0.0", + "sp-std 7.0.0", +] + [[package]] name = "sp-blockchain" version = "4.0.0-dev" @@ -7403,26 +8468,64 @@ dependencies = [ "lru 0.8.1", "parity-scale-codec", "parking_lot 0.12.1", - "sp-api", - "sp-consensus", - "sp-database", - "sp-runtime", - "sp-state-machine", + "sp-api 4.0.0-dev", + "sp-consensus 0.10.0-dev", + "sp-database 4.0.0-dev", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "thiserror", ] [[package]] -name = "sp-consensus" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "sp-blockchain" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7c5d4228728601b7fa86499f6cec983dc8f1135fa1bad5e86fd31cfd660e32" +dependencies = [ + "futures", + "log", + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-api 16.0.0", + "sp-consensus 0.22.0", + "sp-database 5.0.0", + "sp-runtime 20.0.0", + "sp-state-machine 0.24.0", + "thiserror", +] + +[[package]] +name = "sp-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", "futures", "log", "sp-core 7.0.0", - "sp-inherents", - "sp-runtime", - "sp-state-machine", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "thiserror", +] + +[[package]] +name = "sp-consensus" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94dbbd8b6a52634c5920f27886bb9d0d6946393108e05fb8cc710950a87ad378" +dependencies = [ + "async-trait", + "futures", + "log", + "parity-scale-codec", + "sp-core 18.0.0", + "sp-inherents 16.0.0", + "sp-runtime 20.0.0", + "sp-state-machine 0.24.0", + "sp-std 7.0.0", + "sp-version 18.0.0", "thiserror", ] @@ -7434,12 +8537,12 @@ dependencies = [ "async-trait", "parity-scale-codec", "scale-info", - "sp-api", - "sp-application-crypto", - "sp-consensus", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", + "sp-consensus 0.10.0-dev", "sp-consensus-slots", - "sp-inherents", - "sp-runtime", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", "sp-std 5.0.0", "sp-timestamp", ] @@ -7453,14 +8556,14 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-api", - "sp-application-crypto", - "sp-consensus", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", + "sp-consensus 0.10.0-dev", "sp-consensus-slots", "sp-core 7.0.0", - "sp-inherents", - "sp-keystore", - "sp-runtime", + "sp-inherents 4.0.0-dev", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", "sp-timestamp", ] @@ -7475,11 +8578,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-api", - "sp-application-crypto", + "sp-api 4.0.0-dev", + "sp-application-crypto 7.0.0", "sp-core 7.0.0", - "sp-keystore", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] @@ -7508,7 +8611,7 @@ dependencies = [ "dyn-clonable", "ed25519-zebra", "futures", - "hash-db", + "hash-db 0.16.0", "hash256-std-hasher", "impl-serde", "lazy_static", @@ -7539,6 +8642,50 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sp-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea27a1d8de728306d17502ba13127a1b1149c66e0ef348f67dafad630b50c1d" +dependencies = [ + "array-bytes", + "base58", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db 0.15.2", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 7.0.0", + "sp-debug-derive 7.0.0", + "sp-externalities 0.18.0", + "sp-runtime-interface 15.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + [[package]] name = "sp-core" version = "20.0.0" @@ -7553,7 +8700,7 @@ dependencies = [ "dyn-clonable", "ed25519-zebra", "futures", - "hash-db", + "hash-db 0.16.0", "hash256-std-hasher", "impl-serde", "lazy_static", @@ -7583,6 +8730,51 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sp-core" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18d9e2f67d8661f9729f35347069ac29d92758b59135176799db966947a7336" +dependencies = [ + "array-bytes", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db 0.16.0", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "paste", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 9.0.0", + "sp-debug-derive 8.0.0", + "sp-externalities 0.19.0", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + [[package]] name = "sp-core-hashing" version = "5.0.0" @@ -7597,6 +8789,21 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "sp-core-hashing" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d607f7209b1b9571177fc3722a03312df03606bb65f89317ba686d5fa59d438f" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 7.0.0", + "twox-hash", +] + [[package]] name = "sp-core-hashing" version = "8.0.0" @@ -7612,6 +8819,21 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "sp-core-hashing" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee599a8399448e65197f9a6cee338ad192e9023e35e31f22382964c3c174c68" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 8.0.0", + "twox-hash", +] + [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" @@ -7623,6 +8845,18 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "sp-core-hashing-proc-macro" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86d231d36b86d5d433c3e439e0dcaa9192861eee30158ee12c7bc009e02bdbb" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing 7.0.0", + "syn 1.0.109", +] + [[package]] name = "sp-database" version = "4.0.0-dev" @@ -7632,6 +8866,16 @@ dependencies = [ "parking_lot 0.12.1", ] +[[package]] +name = "sp-database" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd6ef59a4a9e1945d5b49eb10a957b9d6b1c83af8379351baf0fa8ec12d8d64" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + [[package]] name = "sp-debug-derive" version = "5.0.0" @@ -7653,6 +8897,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sp-debug-derive" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f531814d2f16995144c74428830ccf7d94ff4a7749632b83ad8199b181140c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + [[package]] name = "sp-externalities" version = "0.13.0" @@ -7676,6 +8931,37 @@ dependencies = [ "sp-storage 12.0.0", ] +[[package]] +name = "sp-externalities" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f71c671e01a8ca60da925d43a1b351b69626e268b8837f8371e320cf1dd100" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 8.0.0", + "sp-storage 13.0.0", +] + +[[package]] +name = "sp-finality-grandpa" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "270f72b20608f1c49bf8e9f8c523764d1d7c9910aba9f48388f78f48d934ed05" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api 16.0.0", + "sp-application-crypto 19.0.0", + "sp-core 18.0.0", + "sp-keystore 0.24.0", + "sp-runtime 20.0.0", + "sp-std 7.0.0", +] + [[package]] name = "sp-inherents" version = "4.0.0-dev" @@ -7686,11 +8972,27 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "sp-std 5.0.0", "thiserror", ] +[[package]] +name = "sp-inherents" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b770e5c0a7deb764ae260cea66fcf52c74203d9af1342389b5f962bfe736f9b0" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "sp-std 7.0.0", + "thiserror", +] + [[package]] name = "sp-io" version = "7.0.0" @@ -7707,12 +9009,65 @@ dependencies = [ "secp256k1", "sp-core 7.0.0", "sp-externalities 0.13.0", - "sp-keystore", + "sp-keystore 0.13.0", "sp-runtime-interface 7.0.0", - "sp-state-machine", + "sp-state-machine 0.13.0", "sp-std 5.0.0", "sp-tracing 6.0.0", - "sp-trie", + "sp-trie 7.0.0", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-io" +version = "19.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be5c4b33aa06da7745be99da2380a500d2f5ccf9b2df5b344d5d1c675adedaa" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "secp256k1", + "sp-core 18.0.0", + "sp-externalities 0.18.0", + "sp-keystore 0.24.0", + "sp-runtime-interface 15.0.0", + "sp-state-machine 0.24.0", + "sp-std 7.0.0", + "sp-tracing 9.0.0", + "sp-trie 18.0.0", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-io" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d597e35a9628fe7454b08965b2442e3ec0f264b0a90d41328e87422cec02e99" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "rustversion", + "secp256k1", + "sp-core 21.0.0", + "sp-externalities 0.19.0", + "sp-keystore 0.27.0", + "sp-runtime-interface 17.0.0", + "sp-state-machine 0.28.0", + "sp-std 8.0.0", + "sp-tracing 10.0.0", + "sp-trie 22.0.0", "tracing", "tracing-core", ] @@ -7724,7 +9079,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "lazy_static", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "strum", ] @@ -7742,6 +9097,38 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sp-keystore" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "811b1f0e8fc5b71fa359f5b4b67adedeba5dc313415e2923f8055e72c172a6ce" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "schnorrkel", + "serde", + "sp-core 18.0.0", + "sp-externalities 0.18.0", + "thiserror", +] + +[[package]] +name = "sp-keystore" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be3cdd67cc1d9c1db17c5cbc4ec4924054a8437009d167f21f6590797e4aa45" +dependencies = [ + "futures", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-core 21.0.0", + "sp-externalities 0.19.0", + "thiserror", +] + [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" @@ -7751,6 +9138,16 @@ dependencies = [ "zstd 0.12.4", ] +[[package]] +name = "sp-maybe-compressed-blob" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df7732c6f130c3e819b142dc76bff0380133b65095567891c0a6a888c147fa3" +dependencies = [ + "thiserror", + "zstd 0.11.2+zstd.1.5.2", +] + [[package]] name = "sp-metadata-ir" version = "0.1.0" @@ -7767,9 +9164,20 @@ name = "sp-offchain" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "sp-api", + "sp-api 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", +] + +[[package]] +name = "sp-offchain" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a045eed6f08d9993fe797c37959db2fd0251198881cf98f8f606448da5c01da2" +dependencies = [ + "sp-api 16.0.0", + "sp-core 18.0.0", + "sp-runtime 20.0.0", ] [[package]] @@ -7782,6 +9190,28 @@ dependencies = [ "regex", ] +[[package]] +name = "sp-panic-handler" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75986cc917d897e0f6d0c848088064df4c74ccbb8f1c1848700b725f5ca7fe04" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-panic-handler" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd2de46003fa8212426838ca71cd42ee36a26480ba9ffea983506ce03131033" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + [[package]] name = "sp-rpc" version = "6.0.0" @@ -7792,6 +9222,17 @@ dependencies = [ "sp-core 7.0.0", ] +[[package]] +name = "sp-rpc" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8b955c29f9f078dd19dd7e4e6b57199b4206e468454ec3d6a7330886a1dd9b" +dependencies = [ + "rustc-hash", + "serde", + "sp-core 18.0.0", +] + [[package]] name = "sp-runtime" version = "7.0.0" @@ -7806,12 +9247,58 @@ dependencies = [ "rand 0.8.5", "scale-info", "serde", - "sp-application-crypto", - "sp-arithmetic", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", "sp-core 7.0.0", - "sp-io", + "sp-io 7.0.0", "sp-std 5.0.0", - "sp-weights", + "sp-weights 4.0.0", +] + +[[package]] +name = "sp-runtime" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02650b39d4bf5966fcd80a5b11e0cc871620952ab9be901edf1fdf1460b1ea9" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto 19.0.0", + "sp-arithmetic 13.0.0", + "sp-core 18.0.0", + "sp-io 19.0.0", + "sp-std 7.0.0", + "sp-weights 16.0.0", +] + +[[package]] +name = "sp-runtime" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21c5bfc764a1a8259d7e8f7cfd22c84006275a512c958d3ff966c92151e134d5" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto 23.0.0", + "sp-arithmetic 16.0.0", + "sp-core 21.0.0", + "sp-io 23.0.0", + "sp-std 8.0.0", + "sp-weights 20.0.0", ] [[package]] @@ -7832,6 +9319,25 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-runtime-interface" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2446ea08a1ae6dac4218b26e01c7aad6dbf47eb506f4f2b1efa821aa418a07d2" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.18.0", + "sp-runtime-interface-proc-macro 10.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "sp-tracing 9.0.0", + "sp-wasm-interface 12.0.0", + "static_assertions", +] + [[package]] name = "sp-runtime-interface" version = "16.0.0" @@ -7851,6 +9357,25 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-runtime-interface" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e676128182f90015e916f806cba635c8141e341e7abbc45d25525472e1bbce8" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.19.0", + "sp-runtime-interface-proc-macro 11.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", + "sp-tracing 10.0.0", + "sp-wasm-interface 14.0.0", + "static_assertions", +] + [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" @@ -7876,6 +9401,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d5bd5566fe5633ec48dfa35ab152fd29f8a577c21971e1c6db9f28afb9bbb9" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.27", +] + [[package]] name = "sp-session" version = "4.0.0-dev" @@ -7883,13 +9421,28 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "parity-scale-codec", "scale-info", - "sp-api", + "sp-api 4.0.0-dev", "sp-core 7.0.0", - "sp-runtime", - "sp-staking", + "sp-runtime 7.0.0", + "sp-staking 4.0.0-dev", "sp-std 5.0.0", ] +[[package]] +name = "sp-session" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e8aa7108c3cf19e257e1a69e4fd969e3aed8b9158580f730c6e2013497246b" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api 16.0.0", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "sp-staking 16.0.0", + "sp-std 7.0.0", +] + [[package]] name = "sp-staking" version = "4.0.0-dev" @@ -7899,16 +9452,29 @@ dependencies = [ "scale-info", "serde", "sp-core 7.0.0", - "sp-runtime", + "sp-runtime 7.0.0", "sp-std 5.0.0", ] +[[package]] +name = "sp-staking" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99dbd03cb38727b17b8276971f901a0e82b608c34a0f7ef24d9f8ad9b3070647" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core 18.0.0", + "sp-runtime 20.0.0", + "sp-std 7.0.0", +] + [[package]] name = "sp-state-machine" version = "0.13.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -7916,9 +9482,51 @@ dependencies = [ "smallvec", "sp-core 7.0.0", "sp-externalities 0.13.0", - "sp-panic-handler", + "sp-panic-handler 5.0.0", "sp-std 5.0.0", - "sp-trie", + "sp-trie 7.0.0", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-state-machine" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779f737342d849205b97e2aacd729695614d86ccb05604e34f0ffe6391d7a4ce" +dependencies = [ + "hash-db 0.15.2", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "sp-core 18.0.0", + "sp-externalities 0.18.0", + "sp-panic-handler 7.0.0", + "sp-std 7.0.0", + "sp-trie 18.0.0", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-state-machine" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef45d31f9e7ac648f8899a0cd038a3608f8499028bff55b6c799702592325b6" +dependencies = [ + "hash-db 0.16.0", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "sp-core 21.0.0", + "sp-externalities 0.19.0", + "sp-panic-handler 8.0.0", + "sp-std 8.0.0", + "sp-trie 22.0.0", "thiserror", "tracing", ] @@ -7934,6 +9542,12 @@ version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" +[[package]] +name = "sp-std" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" + [[package]] name = "sp-storage" version = "7.0.0" @@ -7961,6 +9575,20 @@ dependencies = [ "sp-std 7.0.0", ] +[[package]] +name = "sp-storage" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94294be83f11d4958cfea89ed5798f0b6605f5defc3a996948848458abbcc18e" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", +] + [[package]] name = "sp-timestamp" version = "4.0.0-dev" @@ -7970,8 +9598,8 @@ dependencies = [ "futures-timer", "log", "parity-scale-codec", - "sp-inherents", - "sp-runtime", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", "sp-std 5.0.0", "thiserror", ] @@ -8001,13 +9629,26 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "sp-tracing" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" +dependencies = [ + "parity-scale-codec", + "sp-std 8.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "sp-api", - "sp-runtime", + "sp-api 4.0.0-dev", + "sp-runtime 7.0.0", ] [[package]] @@ -8020,10 +9661,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 7.0.0", - "sp-inherents", - "sp-runtime", + "sp-inherents 4.0.0-dev", + "sp-runtime 7.0.0", "sp-std 5.0.0", - "sp-trie", + "sp-trie 7.0.0", ] [[package]] @@ -8032,10 +9673,10 @@ version = "7.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "ahash 0.8.3", - "hash-db", + "hash-db 0.16.0", "hashbrown 0.13.2", "lazy_static", - "memory-db", + "memory-db 0.32.0", "nohash-hasher", "parity-scale-codec", "parking_lot 0.12.1", @@ -8045,8 +9686,56 @@ dependencies = [ "sp-std 5.0.0", "thiserror", "tracing", - "trie-db", - "trie-root", + "trie-db 0.27.1", + "trie-root 0.18.0", +] + +[[package]] +name = "sp-trie" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b5f3e730d26923d699766a9ca065ec39161f7af815c19acfb89c73f0402bf9" +dependencies = [ + "ahash 0.8.3", + "hash-db 0.15.2", + "hashbrown 0.12.3", + "lazy_static", + "memory-db 0.31.0", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", + "schnellru", + "sp-core 18.0.0", + "sp-std 7.0.0", + "thiserror", + "tracing", + "trie-db 0.25.1", + "trie-root 0.17.0", +] + +[[package]] +name = "sp-trie" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4eeb7ef23f79eba8609db79ef9cef242f994f1f87a3c0387b4b5f177fda74" +dependencies = [ + "ahash 0.8.3", + "hash-db 0.16.0", + "hashbrown 0.13.2", + "lazy_static", + "memory-db 0.32.0", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", + "schnellru", + "sp-core 21.0.0", + "sp-std 8.0.0", + "thiserror", + "tracing", + "trie-db 0.27.1", + "trie-root 0.18.0", ] [[package]] @@ -8059,10 +9748,28 @@ dependencies = [ "parity-wasm", "scale-info", "serde", - "sp-core-hashing-proc-macro", - "sp-runtime", + "sp-core-hashing-proc-macro 5.0.0", + "sp-runtime 7.0.0", "sp-std 5.0.0", - "sp-version-proc-macro", + "sp-version-proc-macro 4.0.0-dev", + "thiserror", +] + +[[package]] +name = "sp-version" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53ebad12a51b507859dc2978f1a6b101b403d1544403a17a1b7c17eeed20cb0c" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro 7.0.0", + "sp-runtime 20.0.0", + "sp-std 7.0.0", + "sp-version-proc-macro 7.0.0", "thiserror", ] @@ -8072,23 +9779,50 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "parity-scale-codec", - "proc-macro2", - "quote", - "syn 2.0.27", + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "sp-version-proc-macro" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42f1acfd2bbaa92c4d97f7a0840e900a5dfa8e8d57b91c031c64f1df2112e90" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 5.0.0", + "wasmi", + "wasmtime 6.0.2", ] [[package]] name = "sp-wasm-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510bdd9ade55508e5aa05b99ab79aaa4b74a1f7476351b6ce0f3aab3b1cb2524" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 5.0.0", + "sp-std 7.0.0", "wasmi", - "wasmtime", + "wasmtime 6.0.2", ] [[package]] @@ -8103,7 +9837,21 @@ dependencies = [ "parity-scale-codec", "sp-std 7.0.0", "wasmi", - "wasmtime", + "wasmtime 6.0.2", +] + +[[package]] +name = "sp-wasm-interface" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19c122609ca5d8246be6386888596320d03c7bc880959eaa2c36bcd5acd6846" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 8.0.0", + "wasmtime 8.0.1", ] [[package]] @@ -8115,12 +9863,44 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-arithmetic", + "sp-arithmetic 6.0.0", "sp-core 7.0.0", "sp-debug-derive 5.0.0", "sp-std 5.0.0", ] +[[package]] +name = "sp-weights" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c4a96e53621ae435981fb6037d8b0be7cf32fae627780094a94ef89f194715" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic 13.0.0", + "sp-core 18.0.0", + "sp-debug-derive 7.0.0", + "sp-std 7.0.0", +] + +[[package]] +name = "sp-weights" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d084c735544f70625b821c3acdbc7a2fc1893ca98b85f1942631284692c75b" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic 16.0.0", + "sp-core 21.0.0", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -8274,6 +10054,38 @@ dependencies = [ "tokio", ] +[[package]] +name = "substrate-prometheus-endpoint" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ef64b8fac4ecd85e4fcc7d6a8dfb8dccaec8b6754a8cd4c8112d6dc3afd240" +dependencies = [ + "hyper", + "log", + "prometheus", + "thiserror", + "tokio", +] + +[[package]] +name = "substrate-state-machine" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "frame-support", + "frame-system", + "ismp", + "ismp-primitives", + "pallet-ismp", + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-trie 7.0.0", +] + [[package]] name = "substring" version = "1.4.5" @@ -8289,6 +10101,85 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "subxt" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a734d66fa935fbda56ba6a71d7e969f424c8c5608d416ba8499d71d8cbfc1f" +dependencies = [ + "base58", + "blake2", + "derivative", + "either", + "frame-metadata", + "futures", + "getrandom 0.2.10", + "hex", + "impl-serde", + "jsonrpsee", + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", + "scale-value", + "serde", + "serde_json", + "sp-core 21.0.0", + "sp-core-hashing 9.0.0", + "sp-runtime 24.0.0", + "subxt-macro", + "subxt-metadata", + "thiserror", + "tracing", +] + +[[package]] +name = "subxt-codegen" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2f231d97c145c564bd544212c0cc0c29c09ff516af199f4ce00c8e055f8138" +dependencies = [ + "frame-metadata", + "heck", + "hex", + "jsonrpsee", + "parity-scale-codec", + "proc-macro2", + "quote", + "scale-info", + "subxt-metadata", + "syn 2.0.27", + "thiserror", + "tokio", +] + +[[package]] +name = "subxt-macro" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e544e41e1c84b616632cd2f86862342868f62e11e4cd9062a9e3dbf5fc871f64" +dependencies = [ + "darling 0.20.3", + "proc-macro-error", + "subxt-codegen", + "syn 2.0.27", +] + +[[package]] +name = "subxt-metadata" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01ce5044c81db3404d38c56f1e69d72eff72c54e5913c9bba4c0b58d376031f" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-core-hashing 9.0.0", + "thiserror", +] + [[package]] name = "syn" version = "1.0.109" @@ -8786,26 +10677,48 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trie-db" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" +dependencies = [ + "hash-db 0.15.2", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + [[package]] name = "trie-db" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ - "hash-db", + "hash-db 0.16.0", "hashbrown 0.13.2", "log", "rustc-hex", "smallvec", ] +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db 0.15.2", +] + [[package]] name = "trie-root" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ - "hash-db", + "hash-db 0.16.0", ] [[package]] @@ -9206,6 +11119,16 @@ dependencies = [ "url", ] +[[package]] +name = "wasmparser" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +dependencies = [ + "indexmap 1.9.3", + "url", +] + [[package]] name = "wasmtime" version = "6.0.2" @@ -9225,15 +11148,40 @@ dependencies = [ "rayon", "serde", "target-lexicon", - "wasmparser", + "wasmparser 0.100.0", "wasmtime-cache", "wasmtime-cranelift", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", + "wasmtime-environ 6.0.2", + "wasmtime-jit 6.0.2", + "wasmtime-runtime 6.0.2", "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "object 0.30.4", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser 0.102.0", + "wasmtime-environ 8.0.1", + "wasmtime-jit 8.0.1", + "wasmtime-runtime 8.0.1", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-asm-macros" version = "6.0.2" @@ -9243,6 +11191,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "wasmtime-asm-macros" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" +dependencies = [ + "cfg-if", +] + [[package]] name = "wasmtime-cache" version = "6.0.2" @@ -9271,7 +11228,7 @@ checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" dependencies = [ "anyhow", "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.93.2", "cranelift-frontend", "cranelift-native", "cranelift-wasm", @@ -9280,8 +11237,8 @@ dependencies = [ "object 0.29.0", "target-lexicon", "thiserror", - "wasmparser", - "wasmtime-environ", + "wasmparser 0.100.0", + "wasmtime-environ 6.0.2", ] [[package]] @@ -9291,7 +11248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ "anyhow", - "cranelift-entity", + "cranelift-entity 0.93.2", "gimli 0.26.2", "indexmap 1.9.3", "log", @@ -9299,8 +11256,27 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasmparser", - "wasmtime-types", + "wasmparser 0.100.0", + "wasmtime-types 6.0.2", +] + +[[package]] +name = "wasmtime-environ" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +dependencies = [ + "anyhow", + "cranelift-entity 0.95.1", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object 0.30.4", + "serde", + "target-lexicon", + "thiserror", + "wasmparser 0.102.0", + "wasmtime-types 8.0.1", ] [[package]] @@ -9320,13 +11296,36 @@ dependencies = [ "rustc-demangle", "serde", "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-debug", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", + "wasmtime-environ 6.0.2", + "wasmtime-jit-debug 6.0.2", + "wasmtime-jit-icache-coherence 6.0.2", + "wasmtime-runtime 6.0.2", "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime-jit" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" +dependencies = [ + "addr2line 0.19.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "log", + "object 0.30.4", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ 8.0.1", + "wasmtime-jit-icache-coherence 8.0.1", + "wasmtime-runtime 8.0.1", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-jit-debug" version = "6.0.2" @@ -9338,6 +11337,15 @@ dependencies = [ "rustix 0.36.15", ] +[[package]] +name = "wasmtime-jit-debug" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "once_cell", +] + [[package]] name = "wasmtime-jit-icache-coherence" version = "6.0.2" @@ -9349,6 +11357,17 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-runtime" version = "6.0.2" @@ -9367,22 +11386,58 @@ dependencies = [ "paste", "rand 0.8.5", "rustix 0.36.15", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", + "wasmtime-asm-macros 6.0.2", + "wasmtime-environ 6.0.2", + "wasmtime-jit-debug 6.0.2", "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime-runtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.8.0", + "paste", + "rand 0.8.5", + "rustix 0.36.15", + "wasmtime-asm-macros 8.0.1", + "wasmtime-environ 8.0.1", + "wasmtime-jit-debug 8.0.1", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-types" version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.93.2", + "serde", + "thiserror", + "wasmparser 0.100.0", +] + +[[package]] +name = "wasmtime-types" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +dependencies = [ + "cranelift-entity 0.95.1", "serde", "thiserror", - "wasmparser", + "wasmparser 0.102.0", ] [[package]] @@ -9989,7 +12044,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-weights", + "sp-weights 4.0.0", "xcm-procedural", ] @@ -10018,6 +12073,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "yap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a7eb6d82a11e4d0b8e6bda8347169aff4ccd8235d039bba7c47482d977dcf7" + [[package]] name = "yasna" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index eddef0bc4..24aadecc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,17 @@ members = [ "pallet-ismp/rpc", "pallet-ismp/runtime-api", "pallet-ismp/primitives", + "pallet-ismp/primitives/state-machine", "pallet-ismp", "parachain/inherent", "parachain/runtime-api", "parachain", - "ismp-demo" + "ismp-demo", + "grandpa", + "grandpa/primitives", + "grandpa/verifier", + "grandpa/prover" ] [workspace.dependencies] -enum-as-inner = "=0.5.1" \ No newline at end of file +enum-as-inner = "=0.5.1" diff --git a/grandpa/Cargo.toml b/grandpa/Cargo.toml new file mode 100644 index 000000000..b1cb9b48f --- /dev/null +++ b/grandpa/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "pallet-grandpa-client" +version = "0.1.0" +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ + "derive" +] } +primitive-types = { version = "0.12.1", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } + +# polytope labs +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } +primitives = { package = "ismp-grandpa-primitives", path = "./primitives", default-features = false } +verifier = { package = "ismp-grandpa-verifier", path = "./verifier", default-features = false} +pallet-ismp = { path = "../pallet-ismp", default-features = false } + + +# substrate +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +# cumulus +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } + +ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } +substrate-state-machine = { path = "../pallet-ismp/primitives/state-machine", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "cumulus-primitives-core/std", + "ismp/std", + "sp-trie/std", + "sp-consensus-aura/std", + "sp-runtime/std", + "sp-io/std", + "primitive-types/std", + "pallet-ismp/std", + "sp-core/std", + "primitives/std", + "verifier/std", + "merkle-mountain-range/std", + "ismp-primitives/std", + "substrate-state-machine/std", + "finality-grandpa/std", +] diff --git a/grandpa/primitives/Cargo.toml b/grandpa/primitives/Cargo.toml new file mode 100644 index 000000000..985dd6b79 --- /dev/null +++ b/grandpa/primitives/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "ismp-grandpa-primitives" +version = "0.1.0" +edition = "2021" + +[dependencies] +# crates.io +anyhow = { version = "1.0.64", default-features = false } +finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } + +# substrate +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +# polytope +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +[features] +default = ["std"] +std = [ + "anyhow/std", + "sp-storage/std", + "finality-grandpa/std", + "codec/std", + "sp-core/std", + "sp-runtime/std", + "sp-io/std", + "frame-support/std", + "sp-consensus-grandpa/std", + "sp-std/std", + "sp-trie/std", + "ismp/std", +] diff --git a/grandpa/primitives/src/justification.rs b/grandpa/primitives/src/justification.rs new file mode 100644 index 000000000..a4787d395 --- /dev/null +++ b/grandpa/primitives/src/justification.rs @@ -0,0 +1,347 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::Commit; +use alloc::collections::{BTreeMap, BTreeSet}; +use anyhow::anyhow; +use codec::{Decode, Encode}; +use finality_grandpa::voter_set::VoterSet; +use frame_support::log; +use sp_consensus_grandpa::{ + AuthorityId, AuthorityList, AuthoritySignature, ConsensusLog, Equivocation, RoundNumber, + ScheduledChange, SetId, GRANDPA_ENGINE_ID, +}; +use sp_core::ed25519; +use sp_runtime::{generic::OpaqueDigestItemId, traits::Header as HeaderT}; +use sp_std::prelude::*; + +/// A GRANDPA justification for block finality, it includes a commit message and +/// an ancestry proof including all headers routing all precommit target blocks +/// to the commit target block. Due to the current voting strategy the precommit +/// targets should be the same as the commit target, since honest voters don't +/// vote past authority set change blocks. +/// +/// This is meant to be stored in the db and passed around the network to other +/// nodes, and are used by syncing nodes to prove authority set handoffs. +#[cfg_attr(any(feature = "std", test), derive(Debug))] +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +pub struct GrandpaJustification { + /// Current voting round number, monotonically increasing + pub round: u64, + /// Contains block hash & number that's being finalized and the signatures. + pub commit: Commit, + /// Contains the path from a [`PreCommit`]'s target hash to the GHOST finalized block. + pub votes_ancestries: Vec, +} + +impl GrandpaJustification +where + H: HeaderT, + H::Number: finality_grandpa::BlockNumberOps, +{ + /// Validate the commit and the votes' ancestry proofs. + pub fn verify(&self, set_id: u64, authorities: &AuthorityList) -> Result<(), anyhow::Error> { + // It's safe to assume that the authority list will not contain duplicates, + // since this list is extracted from a verified relaychain header. + let voters = + VoterSet::new(authorities.iter().cloned()).ok_or(anyhow!("Invalid AuthoritiesSet"))?; + + self.verify_with_voter_set(set_id, &voters) + } + + /// Validate the commit and the votes' ancestry proofs. + pub fn verify_with_voter_set( + &self, + set_id: u64, + voters: &VoterSet, + ) -> Result<(), anyhow::Error> { + use finality_grandpa::Chain; + + let ancestry_chain = AncestryChain::::new(&self.votes_ancestries); + + match finality_grandpa::validate_commit(&self.commit, voters, &ancestry_chain) { + Ok(ref result) if result.is_valid() => { + if result.num_duplicated_precommits() > 0 || + result.num_invalid_voters() > 0 || + result.num_equivocations() > 0 + { + Err(anyhow!("Invalid commit, found one of `duplicate precommits`, `invalid voters`, or `equivocations` {result:?}"))? + } + } + err => { + let result = err.map_err(|_| { + anyhow!("[verify_with_voter_set] Invalid ancestry while validating commit!") + })?; + Err(anyhow!("invalid commit in grandpa justification: {result:?}"))? + } + } + + // we pick the precommit for the lowest block as the base that + // should serve as the root block for populating ancestry (i.e. + // collect all headers from all precommit blocks to the base) + let base_hash = self + .commit + .precommits + .iter() + .map(|signed| &signed.precommit) + .min_by_key(|precommit| precommit.target_number) + .map(|precommit| precommit.target_hash.clone()) + .expect( + "can only fail if precommits is empty; \ + commit has been validated above; \ + valid commits must include precommits; \ + qed.", + ); + + let mut visited_hashes = BTreeSet::new(); + for signed in self.commit.precommits.iter() { + let message = finality_grandpa::Message::Precommit(signed.precommit.clone()); + + check_message_signature::<_, _>( + &message, + &signed.id, + &signed.signature, + self.round, + set_id, + )?; + + if base_hash == signed.precommit.target_hash { + continue + } + + let route = ancestry_chain + .ancestry(base_hash, signed.precommit.target_hash) + .map_err(|_| anyhow!("[verify_with_voter_set] Invalid ancestry!"))?; + // ancestry starts from parent hash but the precommit target hash has been + // visited + visited_hashes.insert(signed.precommit.target_hash); + for hash in route { + visited_hashes.insert(hash); + } + } + + let ancestry_hashes: BTreeSet<_> = + self.votes_ancestries.iter().map(|h: &H| h.hash()).collect(); + + if visited_hashes != ancestry_hashes { + Err(anyhow!( + "invalid precommit ancestries in grandpa justification with unused headers", + ))? + } + + Ok(()) + } + + /// The target block number and hash that this justifications proves finality for. + pub fn target(&self) -> (H::Number, H::Hash) { + (self.commit.target_number, self.commit.target_hash) + } +} + +/// A utility trait implementing `finality_grandpa::Chain` using a given set of headers. +/// This is useful when validating commits, using the given set of headers to +/// verify a valid ancestry route to the target commit block. +pub struct AncestryChain { + ancestry: BTreeMap, +} + +impl AncestryChain { + /// Initialize the ancestry chain given a set of relay chain headers. + pub fn new(ancestry: &[H]) -> AncestryChain { + let ancestry: BTreeMap<_, _> = ancestry.iter().cloned().map(|h: H| (h.hash(), h)).collect(); + + AncestryChain { ancestry } + } + + /// Fetch a header from the ancestry chain, given it's hash. Returns [`None`] if it doesn't + /// exist. + pub fn header(&self, hash: &H::Hash) -> Option<&H> { + self.ancestry.get(hash) + } +} + +impl finality_grandpa::Chain for AncestryChain +where + H::Number: finality_grandpa::BlockNumberOps, +{ + fn ancestry( + &self, + base: H::Hash, + block: H::Hash, + ) -> Result, finality_grandpa::Error> { + let mut route = vec![block]; + let mut current_hash = block; + while current_hash != base { + match self.ancestry.get(¤t_hash) { + Some(current_header) => { + current_hash = *current_header.parent_hash(); + route.push(current_hash); + } + _ => return Err(finality_grandpa::Error::NotDescendent), + }; + } + Ok(route) + } +} + +/// Checks the given header for a consensus digest signalling a **standard** scheduled change and +/// extracts it. +pub fn find_scheduled_change(header: &H) -> Option> { + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + + let filter_log = |log: ConsensusLog| match log { + ConsensusLog::ScheduledChange(change) => Some(change), + _ => None, + }; + + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) +} + +/// Checks the given header for a consensus digest signalling a **forced** scheduled change and +/// extracts it. +pub fn find_forced_change( + header: &H, +) -> Option<(H::Number, ScheduledChange)> { + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + + let filter_log = |log: ConsensusLog| match log { + ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), + _ => None, + }; + + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) +} + +/// Check a message signature by encoding the message and verifying the provided signature using the +/// expected authority id. +pub fn check_message_signature( + message: &finality_grandpa::Message, + id: &AuthorityId, + signature: &AuthoritySignature, + round: RoundNumber, + set_id: SetId, +) -> Result<(), anyhow::Error> +where + H: Encode, + N: Encode, +{ + log::trace!(target: "pallet_grandpa", "Justification Message {:?}", (round, set_id)); + let buf = (message, round, set_id).encode(); + + let signature_bytes: &[u8] = signature.as_ref(); + let sp_finality_signature: ed25519::Signature = + signature_bytes.try_into().map_err(|_| anyhow!("Could not fetch signature"))?; + + let id_bytes: &[u8] = id.as_ref(); + let pub_key: ed25519::Public = + id_bytes.try_into().map_err(|_| anyhow!("Could not fetch public key"))?; + + if sp_io::crypto::ed25519_verify(&sp_finality_signature, &buf, &pub_key) { + Err(anyhow!("invalid signature for precommit in grandpa justification"))? + } + + Ok(()) +} + +/// Verifies the equivocation proof by making sure that both votes target +/// different blocks and that its signatures are valid. +pub fn check_equivocation_proof( + set_id: u64, + equivocation: Equivocation, +) -> Result<(), anyhow::Error> +where + H: Clone + Encode + PartialEq, + N: Clone + Encode + PartialEq, +{ + // NOTE: the bare `Prevote` and `Precommit` types don't share any trait, + // this is implemented as a macro to avoid duplication. + macro_rules! check { + ( $equivocation:expr, $message:expr ) => { + // if both votes have the same target the equivocation is invalid. + if $equivocation.first.0.target_hash == $equivocation.second.0.target_hash && + $equivocation.first.0.target_number == $equivocation.second.0.target_number + { + return Err(anyhow!("both votes have the same target!")) + } + + // check signatures on both votes are valid + check_message_signature::<_, _>( + &$message($equivocation.first.0), + &$equivocation.identity, + &$equivocation.first.1, + $equivocation.round_number, + set_id, + )?; + + check_message_signature::<_, _>( + &$message($equivocation.second.0), + &$equivocation.identity, + &$equivocation.second.1, + $equivocation.round_number, + set_id, + )?; + + return Ok(()) + }; + } + + match equivocation { + Equivocation::Prevote(equivocation) => { + check!(equivocation, finality_grandpa::Message::Prevote); + } + Equivocation::Precommit(equivocation) => { + check!(equivocation, finality_grandpa::Message::Precommit); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use finality_grandpa::Chain; + use sp_runtime::{generic::Header, traits::BlakeTwo256}; + + #[test] + fn test_ancestry_route() { + let mut headers: Vec> = vec![]; + for (i, h) in (40u32..=50).enumerate() { + let mut header = Header::new( + h, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + if i != 0 { + header.parent_hash = headers[i - 1].hash(); + } + headers.push(header); + } + + let slice = &headers[3..=6]; + let ancestry = AncestryChain::new(&headers); + + let mut route = ancestry.ancestry(slice[0].hash(), slice[3].hash()).unwrap(); + route.sort(); + let mut expected = slice.iter().map(|h| h.hash()).collect::>(); + expected.sort(); + + assert_eq!(route, expected); + } +} diff --git a/grandpa/primitives/src/lib.rs b/grandpa/primitives/src/lib.rs new file mode 100644 index 000000000..7c2017019 --- /dev/null +++ b/grandpa/primitives/src/lib.rs @@ -0,0 +1,109 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Primitive types and traits used by the GRANDPA prover & verifier. + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::all)] +#![deny(missing_docs)] + +extern crate alloc; + +use alloc::collections::BTreeMap; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use ismp::host::StateMachine; +use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthoritySignature}; +use sp_core::{sp_std, H256}; +use sp_runtime::traits::Header; +use sp_std::prelude::*; +use sp_storage::StorageKey; + +/// GRANDPA justification utilities +pub mod justification; + +/// Represents a Hash in this library +pub type Hash = H256; +/// A commit message for this chain's block type. +pub type Commit = finality_grandpa::Commit< + ::Hash, + ::Number, + AuthoritySignature, + AuthorityId, +>; + +/// Finality for block B is proved by providing: +/// 1) the justification for the descendant block F; +/// 2) headers sub-chain (B; F] if B != F; +#[derive(Debug, PartialEq, Encode, Decode, Clone)] +pub struct FinalityProof { + /// The hash of block F for which justification is provided. + pub block: Hash, + /// Justification of the block F. + pub justification: Vec, + /// The set of headers in the range (B; F] that we believe are unknown to the caller. Ordered. + pub unknown_headers: Vec, +} + +/// Previous light client state. +#[derive(Debug, PartialEq, Encode, Decode, Clone)] +pub struct ConsensusState { + /// Current authority set + pub current_authorities: AuthorityList, + /// Id of the current authority set. + pub current_set_id: u64, + /// latest finalized height on relay chain or standalone chain + pub latest_height: u32, + /// State machine id StateMachine::Polkadot(0) or StateMachine::Kusama(0) or + ///StateMachine::Grandpa(ConsensusStateId) + pub state_machine: StateMachine, + /// latest finalized height on the parachains, this map will be empty for Standalone chains + /// Map of para_ids + pub para_ids: BTreeMap, + /// latest finalized hash on relay chain or standalone chain. + pub latest_hash: Hash, + /// slot duration for the chain + pub slot_duration: u64, +} + +/// Holds relavant parachain proofs for both header and timestamp extrinsic. +#[derive(Clone, Debug, Encode, Decode)] +pub struct ParachainHeaderProofs { + /// State proofs that prove a parachain headers exists at a given relay chain height + pub state_proof: Vec>, + /// The parachain ids + pub para_ids: Vec, +} + +/// Parachain headers with a Grandpa finality proof. +#[derive(Clone, Encode, Decode)] +pub struct ParachainHeadersWithFinalityProof { + /// The grandpa finality proof: contains relay chain headers from the + /// last known finalized grandpa block. + pub finality_proof: FinalityProof, + /// Contains a map of relay chain header hashes to parachain headers + /// finalzed at the relay chain height. We check for this parachain header finalization + /// via state proofs. Also contains extrinsic proof for timestamp. + pub parachain_headers: BTreeMap, +} + +/// This returns the storage key for a parachain header on the relay chain. +pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { + let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec(); + let encoded_para_id = para_id.encode(); + storage_key.extend_from_slice(sp_io::hashing::twox_64(&encoded_para_id).as_slice()); + storage_key.extend_from_slice(&encoded_para_id); + StorageKey(storage_key) +} diff --git a/grandpa/prover/Cargo.toml b/grandpa/prover/Cargo.toml new file mode 100644 index 000000000..b052897a9 --- /dev/null +++ b/grandpa/prover/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ismp-grandpa-prover" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + + +[dependencies] +hex = "0.4.3" +anyhow = "1.0.64" +serde = "1.0.144" +subxt = "0.29.0" +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } +derive_more = "0.99.17" +downcast-rs = "1.2.0" +jsonrpsee = { version = "0.16.2", features = ["async-client", "jsonrpsee-ws-client"] } +jsonrpsee-ws-client = "0.16.2" +finality-grandpa = "0.16.0" + +sc-consensus-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } + + +primitives = { package = "ismp-grandpa-primitives", path = "../primitives" } +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } diff --git a/grandpa/prover/src/lib.rs b/grandpa/prover/src/lib.rs new file mode 100644 index 000000000..38e10274f --- /dev/null +++ b/grandpa/prover/src/lib.rs @@ -0,0 +1,354 @@ +// Copyright (C) 2023 PolytopeLabs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![allow(clippy::all)] +#![deny(missing_docs)] + +//! GRANDPA prover utilities + +use anyhow::anyhow; +use codec::{Decode, Encode}; +use ismp::host::StateMachine; +use jsonrpsee::{async_client::Client, ws_client::WsClientBuilder}; +use primitives::{ + parachain_header_storage_key, ConsensusState, FinalityProof, ParachainHeaderProofs, + ParachainHeadersWithFinalityProof, +}; +use sc_consensus_grandpa_rpc::GrandpaApiClient; +use serde::{Deserialize, Serialize}; +use sp_consensus_grandpa::{AuthorityId, AuthoritySignature}; +use sp_core::H256; +use sp_runtime::traits::{One, Zero}; +use std::{ + collections::{BTreeMap, BTreeSet, HashMap}, + sync::Arc, +}; +use subxt::{config::Header, Config, OnlineClient}; + +/// Head data for parachain +#[derive(Decode, Encode)] +pub struct HeadData(pub Vec); + +/// Contains methods useful for proving parachain and standalone-chain header finality using GRANDPA +pub struct GrandpaProver { + /// Subxt client for the chain + pub client: OnlineClient, + /// Chain jsonrpsee client for typed rpc requests, which subxt lacks support for. + pub ws_client: Arc, + /// ParaId of the associated parachains + pub para_ids: Vec, + /// State machine identifier for the chain + pub state_machine: StateMachine, + /// Storage for babe epoch start + pub babe_epoch_start: Vec, + /// Storage key for current set id + pub current_set_id: Vec, +} + +// We redefine these here because we want the header to be bounded by subxt::config::Header in the +// prover +/// Commit +pub type Commit = finality_grandpa::Commit; + +/// Justification +#[cfg_attr(any(feature = "std", test), derive(Debug))] +#[derive(Clone, Encode, Decode)] +pub struct GrandpaJustification { + /// Current voting round number, monotonically increasing + pub round: u64, + /// Contains block hash & number that's being finalized and the signatures. + pub commit: Commit, + /// Contains the path from a [`PreCommit`]'s target hash to the GHOST finalized block. + pub votes_ancestries: Vec, +} + +/// An encoded justification proving that the given header has been finalized +#[derive(Clone, Serialize, Deserialize)] +pub struct JustificationNotification(pub sp_core::Bytes); + +impl GrandpaProver +where + T: Config, + ::Number: Ord + Zero, + u32: From<::Number>, + sp_core::H256: From, + T::Header: codec::Decode, +{ + /// Initializes the parachain and relay chain clients given the ws urls. + pub async fn new( + ws_url: &str, + para_ids: Vec, + state_machine: StateMachine, + babe_epoch_start: Vec, + current_set_id: Vec, + ) -> Result { + let ws_client = Arc::new(WsClientBuilder::default().build(ws_url).await?); + let client = OnlineClient::::from_rpc_client(ws_client.clone()).await?; + + Ok(Self { ws_client, client, para_ids, state_machine, babe_epoch_start, current_set_id }) + } + + /// Construct the initial consensus state. + pub async fn initialize_consensus_state( + &self, + slot_duration: u64, + ) -> Result { + use sp_consensus_grandpa::AuthorityList; + let latest_hash = self.client.rpc().finalized_head().await?; + let header = self + .client + .rpc() + .header(Some(latest_hash)) + .await? + .ok_or_else(|| anyhow!("Header not found for hash: {latest_hash:?}"))?; + + let current_set_id: u64 = { + let raw_id = self + .client + .storage() + .at(latest_hash) + .fetch_raw(&self.current_set_id[..]) + .await + .ok() + .flatten() + .expect("Failed to fetch current set id"); + codec::Decode::decode(&mut &*raw_id)? + }; + + let current_authorities = { + let bytes = self + .client + .rpc() + .request::( + "state_call", + subxt::rpc_params!( + "GrandpaApi_grandpa_authorities", + "0x", + Some(format!("{:?}", latest_hash)) + ), + ) + .await + .map(|res| hex::decode(&res[2..]))??; + + AuthorityList::decode(&mut &bytes[..])? + }; + + // Ensure there are no duplicates in authority list + let mut set = BTreeSet::new(); + for (id, ..) in ¤t_authorities { + if !set.insert(id) { + Err(anyhow!("Duplicate entries found in current authority set"))? + } + } + + let latest_height = u32::from(header.number()); + + Ok(ConsensusState { + current_authorities, + current_set_id: current_set_id + 1, + latest_height, + latest_hash: latest_hash.into(), + para_ids: self.para_ids.iter().map(|id| (*id, true)).collect(), + state_machine: self.state_machine, + slot_duration, + }) + } + + /// Returns the grandpa finality proof + pub async fn query_finality_proof( + &self, + previous_finalized_height: u32, + mut latest_finalized_height: u32, + ) -> Result, anyhow::Error> + where + H: Header + codec::Decode, + u32: From<::Number>, + ::Output: From, + T::Hash: From<::Output>, + H::Number: finality_grandpa::BlockNumberOps + One, + { + let encoded = GrandpaApiClient::::prove_finality( + &*self.ws_client, + latest_finalized_height, + ) + .await? + .ok_or_else(|| anyhow!("No justification found for block: {:?}", latest_finalized_height))? + .0; + + let mut finality_proof = FinalityProof::::decode(&mut &encoded[..])?; + + let justification = + GrandpaJustification::::decode(&mut &finality_proof.justification[..])?; + + finality_proof.block = justification.commit.target_hash; + + latest_finalized_height = u32::from(justification.commit.target_number); + + let mut unknown_headers = vec![]; + for height in previous_finalized_height..=latest_finalized_height { + let hash = self + .client + .rpc() + .block_hash(Some(height.into())) + .await? + .ok_or_else(|| anyhow!("Failed to fetch block has for height {height}"))?; + + let header = self + .client + .rpc() + .header(Some(hash)) + .await? + .ok_or_else(|| anyhow!("Header with hash: {hash:?} not found!"))?; + + unknown_headers.push(H::decode(&mut &header.encode()[..])?); + } + + // overwrite unknown headers + finality_proof.unknown_headers = unknown_headers; + Ok(finality_proof) + } + + /// Returns the proof for parachain headers finalized by the provided finality proof + pub async fn query_finalized_parachain_headers_with_proof( + &self, + previous_finalized_height: u32, + latest_finalized_height: u32, + finality_proof: FinalityProof, + ) -> Result, anyhow::Error> + where + H: Header + codec::Decode, + u32: From<::Number>, + ::Output: From, + T::Hash: From<::Output>, + H::Number: finality_grandpa::BlockNumberOps + One, + { + // we are interested only in the blocks where our parachain header changes. + let para_keys: Vec<_> = + self.para_ids.iter().map(|para_id| parachain_header_storage_key(*para_id)).collect(); + let keys = para_keys.iter().map(|key| key.as_ref()).collect::>(); + let mut parachain_headers_with_proof = BTreeMap::::default(); + + let start = self + .client + .rpc() + .block_hash(Some(previous_finalized_height.into())) + .await? + .ok_or_else(|| anyhow!("Failed to fetch previous finalized hash + 1"))?; + + let latest_finalized_hash = self + .client + .rpc() + .block_hash(Some(latest_finalized_height.into())) + .await? + .ok_or_else(|| anyhow!("Failed to fetch previous finalized hash + 1"))?; + + let change_set = + self.client.rpc().query_storage(keys, start, Some(latest_finalized_hash)).await?; + + for changes in change_set { + let header = self + .client + .rpc() + .header(Some(changes.block)) + .await? + .ok_or_else(|| anyhow!("block not found {:?}", changes.block))?; + let mut changed_keys = HashMap::new(); + for para_id in self.para_ids.clone() { + let (key, parachain_header_bytes) = { + let key = parachain_header_storage_key(para_id); + if let Some(raw) = + self.client.storage().at(header.hash()).fetch_raw(key.as_ref()).await? + { + let head_data: HeadData = codec::Decode::decode(&mut &*raw)?; + (key, head_data.0) + } else { + continue + } + }; + + let para_header: H = Decode::decode(&mut ¶chain_header_bytes[..])?; + let para_block_number = para_header.number(); + // skip genesis header or any unknown headers + if para_block_number == Zero::zero() { + continue + } + + changed_keys.insert(key, para_id); + } + + if !changed_keys.is_empty() { + let state_proof = self + .client + .rpc() + .read_proof( + changed_keys.keys().into_iter().map(|key| key.as_ref()), + Some(header.hash()), + ) + .await? + .proof + .into_iter() + .map(|p| p.0) + .collect(); + + let proofs = ParachainHeaderProofs { + state_proof, + para_ids: changed_keys.values().into_iter().map(|id| *id).collect(), + }; + parachain_headers_with_proof.insert(header.hash().into(), proofs); + } + } + + Ok(ParachainHeadersWithFinalityProof { + finality_proof, + parachain_headers: parachain_headers_with_proof, + }) + } + + /// Queries the block at which the epoch for the given block belongs to ends. + pub async fn session_start_and_end_for_block( + &self, + block: u32, + ) -> Result<(u32, u32), anyhow::Error> { + let block_hash = self + .client + .rpc() + .block_hash(Some(block.into())) + .await? + .ok_or(anyhow!("Failed to fetch block hash"))?; + let bytes = self + .client + .storage() + .at(block_hash) + .fetch_raw(&self.babe_epoch_start[..]) + .await? + .ok_or_else(|| anyhow!("Failed to fetch epoch information"))?; + + let (previous_epoch_start, current_epoch_start): (u32, u32) = + codec::Decode::decode(&mut &*bytes)?; + Ok(( + current_epoch_start, + current_epoch_start + (current_epoch_start - previous_epoch_start), + )) + } + + /// Returns the session length in blocks + pub async fn session_length(&self) -> Result { + let metadata = self.client.rpc().metadata().await?; + let metadata = metadata + .pallet_by_name_err("Babe")? + .constant_by_name("EpochDuration") + .ok_or(anyhow!("Failed to fetch constant"))?; + Ok(Decode::decode(&mut metadata.value())?) + } +} diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs new file mode 100644 index 000000000..a315245c0 --- /dev/null +++ b/grandpa/src/consensus.rs @@ -0,0 +1,306 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific lang + +use crate::consensus_message::ConsensusMessage; +use alloc::{boxed::Box, collections::BTreeMap, format, vec::Vec}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use finality_grandpa::Chain; +use ismp::{ + consensus::{ + ConsensusClient, ConsensusStateId, StateCommitment, StateMachineClient, VerifiedCommitments, + }, + error::Error, + host::{IsmpHost, StateMachine}, + messaging::StateCommitmentHeight, +}; +use ismp_primitives::fetch_overlay_root_and_timestamp; +use primitive_types::H256; +use primitives::{ + justification::{AncestryChain, GrandpaJustification}, + ConsensusState, FinalityProof, ParachainHeadersWithFinalityProof, +}; +use sp_runtime::traits::Header; +use substrate_state_machine::SubstrateStateMachine; +use verifier::{ + verify_grandpa_finality_proof, verify_parachain_headers_with_grandpa_finality_proof, +}; + +pub const POLKADOT_CONSENSUS_STATE_ID: [u8; 8] = *b"polkadot"; +pub const KUSAMA_CONSENSUS_STATE_ID: [u8; 8] = *b"_kusama_"; + +pub struct GrandpaConsensusClient(PhantomData<(T, H)>); + +impl Default for GrandpaConsensusClient { + fn default() -> Self { + Self(PhantomData) + } +} + +impl ConsensusClient for GrandpaConsensusClient +where + H: Header, + T: pallet_ismp::Config + super::Config, + T::BlockNumber: Into, + T::Hash: From, +{ + fn verify_consensus( + &self, + _host: &dyn IsmpHost, + _consensus_state_id: ConsensusStateId, + trusted_consensus_state: Vec, + proof: Vec, + ) -> Result<(Vec, VerifiedCommitments), Error> { + // decode the proof into consensus message + let consensus_message: ConsensusMessage = + codec::Decode::decode(&mut &proof[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode consensus message from proof: {e:?}", + )) + })?; + + // decode the consensus state + let consensus_state: ConsensusState = + codec::Decode::decode(&mut &trusted_consensus_state[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode consensus state from trusted consensus state bytes: {e:?}", + )) + })?; + + let mut intermediates = BTreeMap::new(); + + // match over the message + match consensus_message { + ConsensusMessage::RelayChainMessage(relay_chain_message) => { + let headers_with_finality_proof = ParachainHeadersWithFinalityProof { + finality_proof: relay_chain_message.finality_proof, + parachain_headers: relay_chain_message.parachain_headers, + }; + + let (consensus_state, parachain_headers) = + verify_parachain_headers_with_grandpa_finality_proof( + consensus_state, + headers_with_finality_proof, + ) + .map_err(|_| { + Error::ImplementationSpecific(format!("Error verifying parachain headers")) + })?; + + for (para_id, header_vec) in parachain_headers { + let mut state_commitments_vec = Vec::new(); + + let state_id: StateMachine = match consensus_state.state_machine { + StateMachine::Polkadot(_) => StateMachine::Polkadot(para_id), + StateMachine::Kusama(_) => StateMachine::Kusama(para_id), + _ => Err(Error::ImplementationSpecific( + "Host state machine should be a parachain".into(), + ))?, + }; + + for header in header_vec { + let (timestamp, overlay_root) = fetch_overlay_root_and_timestamp( + header.digest(), + consensus_state.slot_duration, + )?; + + if timestamp == 0 { + Err(Error::ImplementationSpecific( + "Timestamp or ismp root not found".into(), + ))? + } + + let height: u32 = (*header.number()).into(); + + let intermediate = StateCommitmentHeight { + commitment: StateCommitment { + timestamp, + overlay_root: Some(overlay_root), + state_root: header.state_root, + }, + height: height.into(), + }; + + state_commitments_vec.push(intermediate); + } + + intermediates.insert(state_id, state_commitments_vec); + } + + Ok((consensus_state.encode(), intermediates)) + } + + ConsensusMessage::StandaloneChainMessage(standalone_chain_message) => { + let (consensus_state, header, _, _) = verify_grandpa_finality_proof( + consensus_state, + standalone_chain_message.finality_proof, + ) + .map_err(|_| { + Error::ImplementationSpecific( + "Error verifying parachain headers".parse().unwrap(), + ) + })?; + let (timestamp, overlay_root) = fetch_overlay_root_and_timestamp( + header.digest(), + consensus_state.slot_duration, + )?; + + if timestamp == 0 { + Err(Error::ImplementationSpecific("Timestamp or ismp root not found".into()))? + } + + let height: u32 = (*header.number()).into(); + + let state_id = consensus_state.state_machine; + + let intermediate = StateCommitmentHeight { + commitment: StateCommitment { + timestamp, + overlay_root: Some(overlay_root), + state_root: header.state_root, + }, + height: height.into(), + }; + + let mut state_commitments_vec = Vec::new(); + state_commitments_vec.push(intermediate); + + intermediates.insert(state_id, state_commitments_vec); + + Ok((consensus_state.encode(), intermediates)) + } + } + } + + fn verify_fraud_proof( + &self, + _host: &dyn IsmpHost, + trusted_consensus_state: Vec, + proof_1: Vec, + proof_2: Vec, + ) -> Result<(), Error> { + // decode the consensus state + let consensus_state: ConsensusState = + codec::Decode::decode(&mut &trusted_consensus_state[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode consensus state from trusted consensus state bytes: {e:?}", + )) + })?; + + let first_proof: FinalityProof = + codec::Decode::decode(&mut &proof_1[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode first finality proof from proof_1 bytes: {e:?}", + )) + })?; + + let second_proof: FinalityProof = + codec::Decode::decode(&mut &proof_2[..]).map_err(|e| { + Error::ImplementationSpecific(format!( + "Cannot decode second finality proof from proof_2 bytes: {e:?}", + )) + })?; + + if first_proof.block == second_proof.block { + return Err(Error::ImplementationSpecific(format!( + "Fraud proofs are for the same block", + ))) + } + + let first_headers = AncestryChain::::new(&first_proof.unknown_headers); + let first_target = + first_proof.unknown_headers.iter().max_by_key(|h| *h.number()).ok_or_else(|| { + Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) + })?; + + let second_headers = AncestryChain::::new(&second_proof.unknown_headers); + let second_target = + second_proof.unknown_headers.iter().max_by_key(|h| *h.number()).ok_or_else(|| { + Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) + })?; + + if first_target.hash() != first_proof.block || second_target.hash() != second_proof.block { + return Err(Error::ImplementationSpecific(format!( + "Fraud proofs are not for the same chain" + ))) + } + + let first_base = + first_proof.unknown_headers.iter().min_by_key(|h| *h.number()).ok_or_else(|| { + Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) + })?; + first_headers + .ancestry(first_base.hash(), first_target.hash()) + .map_err(|_| Error::ImplementationSpecific(format!("Invalid ancestry!")))?; + + let second_base = + second_proof.unknown_headers.iter().min_by_key(|h| *h.number()).ok_or_else(|| { + Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) + })?; + second_headers + .ancestry(second_base.hash(), second_target.hash()) + .map_err(|_| Error::ImplementationSpecific(format!("Invalid ancestry!")))?; + + let first_parent = first_base.parent_hash(); + let second_parent = second_base.parent_hash(); + + if first_parent != second_parent { + return Err(Error::ImplementationSpecific(format!( + "Fraud proofs are not for the same ancestor" + ))) + } + + let first_justification = + GrandpaJustification::::decode(&mut &first_proof.justification[..]).map_err( + |_| Error::ImplementationSpecific(format!("Could not decode first justification")), + )?; + + let second_justification = + GrandpaJustification::::decode(&mut &second_proof.justification[..]).map_err( + |_| Error::ImplementationSpecific(format!("Could not decode second justification")), + )?; + + if first_proof.block != first_justification.commit.target_hash || + second_proof.block != second_justification.commit.target_hash + { + Err(Error::ImplementationSpecific( + format!("First or second finality proof block hash does not match justification target hash") + ))? + } + + if first_justification.commit.target_hash != consensus_state.latest_hash && + second_justification.commit.target_hash != consensus_state.latest_hash + { + Err(Error::ImplementationSpecific(format!( + "First or second justification does not match consensus latest hash" + )))? + } + + let first_valid = first_justification + .verify(consensus_state.current_set_id, &consensus_state.current_authorities) + .is_ok(); + let second_valid = second_justification + .verify(consensus_state.current_set_id, &consensus_state.current_authorities) + .is_ok(); + + if !first_valid || !second_valid { + Err(Error::ImplementationSpecific(format!("Invalid justification")))? + } + + Ok(()) + } + + fn state_machine(&self, _id: StateMachine) -> Result, Error> { + Ok(Box::new(SubstrateStateMachine::::default())) + } +} diff --git a/grandpa/src/consensus_message.rs b/grandpa/src/consensus_message.rs new file mode 100644 index 000000000..40b0f8aed --- /dev/null +++ b/grandpa/src/consensus_message.rs @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific lang +use alloc::collections::BTreeMap; +use codec::{Decode, Encode}; +use primitives::{FinalityProof, ParachainHeaderProofs}; +use sp_core::H256; +use sp_runtime::traits::BlakeTwo256; + +/// Relay chain substrate header type +pub type SubstrateHeader = sp_runtime::generic::Header; + +/// [`ClientMessage`] definition +#[derive(Clone, Debug, Encode, Decode)] +pub enum ConsensusMessage { + /// This is the variant representing the standalone chain + StandaloneChainMessage(StandaloneChainMessage), + /// This is the variant representing the relay chain + RelayChainMessage(RelayChainMessage), +} + +#[derive(Clone, Debug, Encode, Decode)] +pub struct StandaloneChainMessage { + /// finality proof + pub finality_proof: FinalityProof, +} + +#[derive(Clone, Debug, Encode, Decode)] +pub struct RelayChainMessage { + /// finality proof + pub finality_proof: FinalityProof, + /// parachain headers + pub parachain_headers: BTreeMap, +} diff --git a/grandpa/src/lib.rs b/grandpa/src/lib.rs new file mode 100644 index 000000000..0e0402979 --- /dev/null +++ b/grandpa/src/lib.rs @@ -0,0 +1,141 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific lang + +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +pub mod consensus; +pub mod consensus_message; + +use alloc::{vec, vec::Vec}; +pub use pallet::*; +use pallet_ismp::host::Host; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use ismp::host::IsmpHost; + use primitive_types::H256; + use primitives::ConsensusState; + + #[pallet::pallet] + pub struct Pallet(_); + + /// The config trait + #[pallet::config] + pub trait Config: frame_system::Config + pallet_ismp::Config { + /// The overarching event type + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Origin allowed to add or remove parachains in Consensus State + type AdminOrigin: EnsureOrigin; + } + + /// Events emitted by this pallet + #[pallet::event] + pub enum Event {} + + #[pallet::error] + pub enum Error { + /// Standalone Consensus State Already Exists + StandaloneConsensusStateAlreadyExists, + /// Standalone Consensus Does not Exist + StandaloneConsensusStateDontExists, + /// Error fetching consensus state + ErrorFetchingConsensusState, + /// Error decoding consensus state + ErrorDecodingConsensusState, + /// Incorrect consensus state id length + IncorrectConsensusStateIdLength, + /// Error storing consensus state + ErrorStoringConsensusState, + } + + #[pallet::call] + impl Pallet + where + ::Hash: From, + { + /// Add some new parachains to the list of parachains in the relay chain consensus state + #[pallet::call_index(0)] + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn add_parachains( + origin: OriginFor, + consensus_state_id_vec: Vec, + para_ids: Vec, + ) -> DispatchResult { + ::AdminOrigin::ensure_origin(origin)?; + + let ismp_host = Host::::default(); + let consensus_state_id = consensus_state_id_vec + .as_slice() + .try_into() + .map_err(|_| Error::::IncorrectConsensusStateIdLength)?; + + let encoded_consensus_state = ismp_host + .consensus_state(consensus_state_id) + .map_err(|_| Error::::ErrorFetchingConsensusState)?; + let mut consensus_state: ConsensusState = + codec::Decode::decode(&mut &encoded_consensus_state[..]) + .map_err(|_| Error::::ErrorDecodingConsensusState)?; + + let mut stored_para_ids = consensus_state.para_ids; + para_ids.iter().for_each(|para_id| { + stored_para_ids.entry(*para_id).or_insert(true); + }); + consensus_state.para_ids = stored_para_ids; + + let encoded_consensus_state = consensus_state.encode(); + ismp_host + .store_consensus_state(consensus_state_id, encoded_consensus_state) + .map_err(|_| Error::::ErrorStoringConsensusState)?; + Ok(()) + } + + /// Remove some parachains from the list of parachains in the relay chain consensus state + #[pallet::call_index(1)] + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn remove_parachains( + origin: OriginFor, + consensus_state_id_vec: Vec, + para_ids: Vec, + ) -> DispatchResult { + ::AdminOrigin::ensure_origin(origin)?; + + let ismp_host = Host::::default(); + let consensus_state_id = consensus_state_id_vec + .as_slice() + .try_into() + .map_err(|_| Error::::IncorrectConsensusStateIdLength)?; + + let encoded_consensus_state = ismp_host + .consensus_state(consensus_state_id) + .map_err(|_| Error::::ErrorFetchingConsensusState)?; + let mut consensus_state: ConsensusState = + codec::Decode::decode(&mut &encoded_consensus_state[..]) + .map_err(|_| Error::::ErrorDecodingConsensusState)?; + + let mut stored_para_ids = consensus_state.para_ids; + stored_para_ids.retain(|&key, _| !para_ids.contains(&key)); + consensus_state.para_ids = stored_para_ids; + + let encoded_consensus_state = consensus_state.encode(); + ismp_host + .store_consensus_state(consensus_state_id, encoded_consensus_state) + .map_err(|_| Error::::ErrorStoringConsensusState)?; + Ok(()) + } + } +} diff --git a/grandpa/verifier/Cargo.toml b/grandpa/verifier/Cargo.toml new file mode 100644 index 000000000..a7f3c7001 --- /dev/null +++ b/grandpa/verifier/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "ismp-grandpa-verifier" +version = "0.1.0" +edition = "2021" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +anyhow = { version = "1.0.64", default-features = false } +finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } +hash-db = { version = "0.16.0", default-features = false } +serde = { version = "1.0.144", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.17", default-features = false, features = ["from"] } + + +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +primitives = { package = "ismp-grandpa-primitives", path = "../primitives", default-features = false } + +[dev-dependencies] +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.42" } +subxt = { version = "0.29.0", features = ["substrate-compat"] } +futures = "0.3.24" +hex = "0.4.3" +env_logger = "0.9.0" +log = "0.4.17" +tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] } +hex-literal = "0.3.4" +grandpa-prover = { package = "ismp-grandpa-prover", path = "../prover" } +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main"} +sc-finality-grandpa-rpc = "0.25.0" + + + +[features] +default = ["std"] +std = [ + "codec/std", + "anyhow/std", + "finality-grandpa/std", + "hash-db/std", + "frame-support/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", + "sp-consensus-grandpa/std", + "sp-state-machine/std", + "sp-io/std", + "primitives/std", + "serde/std", + "sp-storage/std", +] diff --git a/grandpa/verifier/src/lib.rs b/grandpa/verifier/src/lib.rs new file mode 100644 index 000000000..28da555a7 --- /dev/null +++ b/grandpa/verifier/src/lib.rs @@ -0,0 +1,167 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GRANDPA consensus client verification function + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::all)] +#![deny(missing_docs)] + +mod state_machine; +#[cfg(test)] +mod tests; + +extern crate alloc; + +use alloc::collections::BTreeMap; +use anyhow::anyhow; +use codec::Decode; +use finality_grandpa::Chain; +use primitives::{ + justification::{find_scheduled_change, AncestryChain, GrandpaJustification}, + parachain_header_storage_key, ConsensusState, FinalityProof, ParachainHeadersWithFinalityProof, +}; +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, Header}; +use sp_std::prelude::*; +use sp_trie::StorageProof; + +/// This function verifies the GRANDPA finality proof for both standalone chain and parachain +/// headers. +pub fn verify_grandpa_finality_proof( + mut consensus_state: ConsensusState, + finality_proof: FinalityProof, +) -> Result<(ConsensusState, H, Vec, AncestryChain), anyhow::Error> +where + H: Header, + H::Number: finality_grandpa::BlockNumberOps + Into, +{ + // First validate unknown headers. + let headers = AncestryChain::::new(&finality_proof.unknown_headers); + + let target = finality_proof + .unknown_headers + .iter() + .max_by_key(|h| *h.number()) + .ok_or_else(|| anyhow!("Unknown headers can't be empty!"))?; + + // this is illegal + if target.hash() != finality_proof.block { + Err(anyhow!("Latest finalized block should be highest block in unknown_headers"))?; + } + + let justification = GrandpaJustification::::decode(&mut &finality_proof.justification[..]) + .map_err(|e| anyhow!("Failed to decode justificatio {:?}", e))?; + + if justification.commit.target_hash != finality_proof.block { + Err(anyhow!("Justification target hash and finality proof block hash mismatch"))?; + } + + let from = consensus_state.latest_hash; + + let base = finality_proof + .unknown_headers + .iter() + .min_by_key(|h| *h.number()) + .ok_or_else(|| anyhow!("Unknown headers can't be empty!"))?; + + if base.number() < &consensus_state.latest_height { + headers.ancestry(base.hash(), consensus_state.latest_hash).map_err(|_| { + anyhow!( + "[verify_grandpa_finality_proof] Invalid ancestry (base -> latest relay block)!" + ) + })?; + } + + let mut finalized = headers + .ancestry(from, target.hash()) + .map_err(|_| anyhow!("[verify_grandpa_finality_proof] Invalid ancestry!"))?; + finalized.sort(); + + // 2. verify justification. + justification.verify(consensus_state.current_set_id, &consensus_state.current_authorities)?; + + // Sets new consensus state, optionally rotating authorities + consensus_state.latest_hash = target.hash(); + consensus_state.latest_height = (*target.number()).into(); + if let Some(scheduled_change) = find_scheduled_change::(&target) { + consensus_state.current_set_id += 1; + consensus_state.current_authorities = scheduled_change.next_authorities; + } + + Ok((consensus_state, target.clone(), finalized, headers)) +} +/// This function verifies the GRANDPA finality proof for relay chain headers. +/// +/// Next, we prove the finality of parachain headers, by verifying patricia-merkle trie state proofs +/// of these headers, stored at the recently finalized relay chain heights. +/// Returns the new Consensus state alongside a map of para id to a vector that contains a tuple of +/// finalized parachain header and timestamp +pub fn verify_parachain_headers_with_grandpa_finality_proof( + consensus_state: ConsensusState, + proof: ParachainHeadersWithFinalityProof, +) -> Result<(ConsensusState, BTreeMap>), anyhow::Error> +where + H: Header, + H::Number: finality_grandpa::BlockNumberOps + Into, +{ + let ParachainHeadersWithFinalityProof { finality_proof, parachain_headers } = proof; + + let (consensus_state, _, finalized_hashes, headers) = + verify_grandpa_finality_proof(consensus_state, finality_proof)?; + // verifies state proofs of parachain headers in finalized relay chain headers. + let mut verified_parachain_headers: BTreeMap> = BTreeMap::new(); + for (hash, proof) in parachain_headers { + if finalized_hashes.binary_search(&hash).is_err() { + // seems relay hash isn't in the finalized chain. + continue + } + let relay_chain_header = + headers.header(&hash).expect("Headers have been checked by AncestryChain; qed"); + let state_proof = proof.state_proof; + let mut keys = BTreeMap::new(); + for para_id in proof.para_ids { + // ensure the para id is in the consensus state before proof verification + if !consensus_state.para_ids.contains_key(¶_id) { + continue + } + + let key = parachain_header_storage_key(para_id); + + keys.insert(key.0, para_id); + } + + let proof = StorageProof::new(state_proof); + + // verify patricia-merkle state proofs + let mut result = state_machine::read_proof_check::( + relay_chain_header.state_root(), + proof, + keys.keys().map(|key| key.as_slice()), + ) + .map_err(|err| anyhow!("error verifying parachain header state proof: {err:?}"))?; + for (key, para_id) in keys { + let header = result + .remove(&key) + .flatten() + .ok_or_else(|| anyhow!("Invalid proof, parachain header not found"))?; + let parachain_header = + H::decode(&mut &header[..]).map_err(|e| anyhow!("error decoding header: {e:?}"))?; + verified_parachain_headers.entry(para_id).or_default().push(parachain_header); + } + } + + Ok((consensus_state, verified_parachain_headers)) +} diff --git a/grandpa/verifier/src/state_machine.rs b/grandpa/verifier/src/state_machine.rs new file mode 100644 index 000000000..e85c2bf2a --- /dev/null +++ b/grandpa/verifier/src/state_machine.rs @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! State verification functions + +use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}; +use codec::Decode; +use core::fmt::Debug; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +use sp_core::H256; +use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; + +#[derive(Debug, derive_more::From, derive_more::Display)] +pub enum Error +where + H: Hasher, + H::Out: Debug, +{ + #[display(fmt = "Trie Error: {:?}", _0)] + Trie(Box>>), + #[display(fmt = "Error verifying key: {key:?}, Expected: {expected:?}, Got: {got:?}")] + ValueMismatch { key: Option, expected: Option>, got: Option> }, + #[display(fmt = "Invalid Proof")] + InvalidProof, +} + +/// Lifted directly from [`sp_state_machine::read_proof_check`](https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1075-L1094) +pub fn read_proof_check( + root: &H::Out, + proof: StorageProof, + keys: I, +) -> Result, Option>>, Error> +where + H: Hasher, + H::Out: Debug, + I: IntoIterator, + I::Item: AsRef<[u8]>, +{ + let db = proof.into_memory_db(); + + if !db.contains(root, EMPTY_PREFIX) { + Err(Error::InvalidProof)? + } + + let trie = TrieDBBuilder::>::new(&db, root).build(); + let mut result = BTreeMap::new(); + + for key in keys.into_iter() { + let value = trie.get(key.as_ref())?.and_then(|val| Decode::decode(&mut &val[..]).ok()); + result.insert(key.as_ref().to_vec(), value); + } + + Ok(result) +} diff --git a/grandpa/verifier/src/tests.rs b/grandpa/verifier/src/tests.rs new file mode 100644 index 000000000..c281cd028 --- /dev/null +++ b/grandpa/verifier/src/tests.rs @@ -0,0 +1,146 @@ +use crate::verify_parachain_headers_with_grandpa_finality_proof; +use codec::{Decode, Encode}; +use futures::StreamExt; +use grandpa_prover::GrandpaProver; +use ismp::host::StateMachine; +use polkadot_core_primitives::Header; +use primitives::{justification::GrandpaJustification, ParachainHeadersWithFinalityProof}; +use serde::{Deserialize, Serialize}; +use sp_core::{crypto::AccountId32, H256}; +use subxt::{ + config::{ + polkadot::PolkadotExtrinsicParams as ParachainExtrinsicParams, + substrate::{BlakeTwo256, SubstrateHeader}, + }, + rpc_params, +}; + +pub struct DefaultConfig; + +impl subxt::config::Config for DefaultConfig { + type Index = u32; + type Hash = H256; + type AccountId = AccountId32; + type Address = sp_runtime::MultiAddress; + type Signature = sp_runtime::MultiSignature; + type Hasher = subxt::config::substrate::BlakeTwo256; + type Header = + subxt::config::substrate::SubstrateHeader; + type ExtrinsicParams = ParachainExtrinsicParams; +} + +pub type Justification = GrandpaJustification

; + +/// An encoded justification proving that the given header has been finalized +#[derive(Clone, Serialize, Deserialize)] +pub struct JustificationNotification(sp_core::Bytes); + +#[ignore] +#[tokio::test] +async fn follow_grandpa_justifications() { + env_logger::builder() + .filter_module("grandpa", log::LevelFilter::Trace) + .format_module_path(false) + .init(); + + let relay = std::env::var("RELAY_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); + + let relay_ws_url = format!("ws://{relay}:9944"); + + let para_ids = vec![2000, 2001]; + let babe_epoch_start_key = + hex::decode("1cb6f36e027abb2091cfb5110ab5087fe90e2fbf2d792cb324bffa9427fe1f0e").unwrap(); + let current_set_id_key = + hex::decode("5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc").unwrap(); + + let prover = GrandpaProver::::new( + &relay_ws_url, + para_ids, + StateMachine::Polkadot(0), + babe_epoch_start_key, + current_set_id_key, + ) + .await + .unwrap(); + + println!("Waiting for grandpa proofs to become available"); + let session_length = prover.session_length().await.unwrap(); + prover + .client + .blocks() + .subscribe_finalized() + .await + .unwrap() + .filter_map(|result| futures::future::ready(result.ok())) + .skip_while(|h| futures::future::ready(h.number() < (session_length * 2) + 10)) + .take(1) + .collect::>() + .await; + + let mut subscription = prover + .client + .rpc() + .subscribe::( + "grandpa_subscribeJustifications", + rpc_params![], + "grandpa_unsubscribeJustifications", + ) + .await + .unwrap() + .take(100); + + // slot duration in milliseconds for parachains + let slot_duration = 12_000; + + let mut consensus_state = prover.initialize_consensus_state(slot_duration).await.unwrap(); + + println!("Grandpa proofs are now available"); + while let Some(Ok(_)) = subscription.next().await { + let next_relay_height = consensus_state.latest_height + 1; + + // prove finality should give us the justification for the highest finalized block of the + // authority set the block provided to it belongs + let finality_proof = prover + .query_finality_proof::>( + consensus_state.latest_height, + next_relay_height, + ) + .await + .unwrap(); + + let justification = Justification::decode(&mut &finality_proof.justification[..]).unwrap(); + + println!("current_set_id: {}", consensus_state.current_set_id); + println!("latest_relay_height: {}", consensus_state.latest_height); + println!( + "For relay chain header: Hash({:?}), Number({})", + justification.commit.target_hash, justification.commit.target_number + ); + + let proof = prover + .query_finalized_parachain_headers_with_proof::>( + consensus_state.latest_height, + justification.commit.target_number, + finality_proof.clone(), + ) + .await + .expect("Failed to fetch finalized parachain headers with proof"); + + let proof = proof.encode(); + let proof = ParachainHeadersWithFinalityProof::
::decode(&mut &*proof).unwrap(); + + let (new_consensus_state, _parachain_headers) = + verify_parachain_headers_with_grandpa_finality_proof::
( + consensus_state.clone(), + proof.clone(), + ) + .expect("Failed to verify parachain headers with grandpa finality_proof"); + + if !proof.parachain_headers.is_empty() { + assert!(new_consensus_state.latest_height > consensus_state.latest_height); + } + + consensus_state = new_consensus_state; + println!("========= Successfully verified grandpa justification ========="); + } +} diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml index 3fc71c5ce..fc5cb169c 100644 --- a/pallet-ismp/primitives/Cargo.toml +++ b/pallet-ismp/primitives/Cargo.toml @@ -18,6 +18,9 @@ codec = { package = "parity-scale-codec", version = "3.1.3", default-features = primitive-types = { version = "0.12.1", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } [features] default = ["std"] @@ -29,5 +32,8 @@ std = [ "sp-runtime/std", "primitive-types/std", "scale-info/std", - "serde" + "serde", + "frame-support/std", + "sp-core/std", + "sp-consensus-aura/std", ] diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs index 40e8d67c9..7118babfd 100644 --- a/pallet-ismp/primitives/src/lib.rs +++ b/pallet-ismp/primitives/src/lib.rs @@ -19,10 +19,23 @@ #![deny(missing_docs)] //! Primitives for the MMR implementation -use ismp::host::StateMachine; + +extern crate alloc; + +use alloc::{format, vec::Vec}; +use codec::{Decode, Encode}; +use core::{fmt::Debug, time::Duration}; +use frame_support::sp_runtime::Digest; +use ismp::{error::Error, host::StateMachine}; +use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; +use sp_core::H256; +use sp_runtime::DigestItem; pub mod mmr; +/// The `ConsensusEngineId` of ISMP digest in the parachain header. +pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; + /// Queries a request leaf in the mmr #[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] @@ -34,3 +47,68 @@ pub struct LeafIndexQuery { /// The request nonce pub nonce: u64, } + +/// Hashing algorithm for the state proof +#[derive(Debug, Encode, Decode, Clone)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub enum HashAlgorithm { + /// For chains that use keccak as their hashing algo + Keccak, + /// For chains that use blake2 as their hashing algo + Blake2, +} + +/// Holds the relevant data needed for state proof verification +#[derive(Debug, Encode, Decode, Clone)] +pub struct SubstrateStateProof { + /// Algorithm to use for state proof verification + pub hasher: HashAlgorithm, + /// Storage proof for the parachain headers + pub storage_proof: Vec>, +} + +/// Holds the relevant data needed for request/response proof verification +#[derive(Debug, Encode, Decode, Clone)] +pub struct MembershipProof { + /// Size of the mmr at the time this proof was generated + pub mmr_size: u64, + /// Leaf indices for the proof + pub leaf_indices: Vec, + /// Mmr proof + pub proof: Vec, +} + +/// Fetches the overlay(ismp) root and timestamp from the header digest +pub fn fetch_overlay_root_and_timestamp( + digest: &Digest, + slot_duration: u64, +) -> Result<(u64, H256), Error> { + let (mut timestamp, mut overlay_root) = (0, H256::default()); + + for digest in digest.logs.iter() { + match digest { + DigestItem::PreRuntime(consensus_engine_id, value) + if *consensus_engine_id == AURA_ENGINE_ID => + { + let slot = Slot::decode(&mut &value[..]) + .map_err(|e| Error::ImplementationSpecific(format!("Cannot slot: {e:?}")))?; + timestamp = Duration::from_millis(*slot * slot_duration).as_secs(); + } + DigestItem::Consensus(consensus_engine_id, value) + if *consensus_engine_id == ISMP_ID => + { + if value.len() != 32 { + Err(Error::ImplementationSpecific( + "Header contains an invalid ismp root".into(), + ))? + } + + overlay_root = H256::from_slice(&value); + } + // don't really care about the rest + _ => {} + }; + } + + Ok((timestamp, overlay_root)) +} diff --git a/pallet-ismp/primitives/state-machine/Cargo.toml b/pallet-ismp/primitives/state-machine/Cargo.toml new file mode 100644 index 000000000..0e124ce9d --- /dev/null +++ b/pallet-ismp/primitives/state-machine/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "substrate-state-machine" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +# polytope labs +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +# crates.io +merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +primitive-types = { version = "0.12.1", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + + +ismp-primitives = { path = "..", default-features = false } +pallet-ismp = { path = "../..", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-system/std", + "ismp/std", + "merkle-mountain-range/std", + "codec/std", + "sp-runtime/std", + "primitive-types/std", + "scale-info/std", + "serde", + "frame-support/std", + "sp-core/std", + "pallet-ismp/std", + "ismp-primitives/std", + "sp-trie/std" +] diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs new file mode 100644 index 000000000..18d31cec0 --- /dev/null +++ b/pallet-ismp/primitives/state-machine/src/lib.rs @@ -0,0 +1,165 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The state machine implementation in Substrate +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::all)] +#![deny(missing_docs)] + +extern crate alloc; + +use alloc::{collections::BTreeMap, format, vec, vec::Vec}; +use codec::Decode; +use core::marker::PhantomData; +use ismp::{ + consensus::{StateCommitment, StateMachineClient}, + error::Error, + host::IsmpHost, + messaging::Proof, + router::{Request, RequestResponse}, + util::hash_request, +}; +use ismp_primitives::{ + mmr::{DataOrHash, Leaf, MmrHasher}, + HashAlgorithm, MembershipProof, SubstrateStateProof, +}; +use merkle_mountain_range::MerkleProof; +use pallet_ismp::host::Host; +use primitive_types::H256; +use sp_runtime::traits::{BlakeTwo256, Keccak256}; +use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; + +/// The parachain and grandpa consensus client implementation for ISMP. +pub struct SubstrateStateMachine(PhantomData); + +impl Default for SubstrateStateMachine { + fn default() -> Self { + Self(PhantomData) + } +} + +impl StateMachineClient for SubstrateStateMachine +where + T: pallet_ismp::Config, + T::BlockNumber: Into, + T::Hash: From, +{ + fn verify_membership( + &self, + _host: &dyn IsmpHost, + item: RequestResponse, + state: StateCommitment, + proof: &Proof, + ) -> Result<(), Error> { + let membership = MembershipProof::decode(&mut &*proof.proof).map_err(|e| { + Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) + })?; + let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = + MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); + let leaves: Vec<(u64, DataOrHash)> = match item { + RequestResponse::Request(req) => membership + .leaf_indices + .into_iter() + .zip(req.into_iter()) + .map(|(pos, req)| (pos, DataOrHash::Data(Leaf::Request(req)))) + .collect(), + RequestResponse::Response(res) => membership + .leaf_indices + .into_iter() + .zip(res.into_iter()) + .map(|(pos, res)| (pos, DataOrHash::Data(Leaf::Response(res)))) + .collect(), + }; + let root = state + .overlay_root + .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; + + let calc_root = proof + .calculate_root(leaves.clone()) + .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; + let valid = calc_root.hash::>() == root.into(); + + if !valid { + Err(Error::ImplementationSpecific("Invalid membership proof".into()))? + } + + Ok(()) + } + + fn state_trie_key(&self, requests: Vec) -> Vec> { + let mut keys = vec![]; + + for req in requests { + match req { + Request::Post(post) => { + let request = Request::Post(post); + let commitment = hash_request::>(&request).0.to_vec(); + keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); + } + Request::Get(_) => continue, + } + } + + keys + } + + fn verify_state_proof( + &self, + _host: &dyn IsmpHost, + keys: Vec>, + root: StateCommitment, + proof: &Proof, + ) -> Result, Option>>, Error> { + let state_proof: SubstrateStateProof = codec::Decode::decode(&mut &*proof.proof) + .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; + + let data = match state_proof.hasher { + HashAlgorithm::Keccak => { + let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); + keys.into_iter() + .map(|key| { + let value = trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!( + "Error reading state proof: {e:?}" + )) + })?; + Ok((key, value)) + }) + .collect::, _>>()? + } + HashAlgorithm::Blake2 => { + let db = + StorageProof::new(state_proof.storage_proof).into_memory_db::(); + + let trie = + TrieDBBuilder::>::new(&db, &root.state_root).build(); + keys.into_iter() + .map(|key| { + let value = trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!( + "Error reading state proof: {e:?}" + )) + })?; + Ok((key, value)) + }) + .collect::, _>>()? + } + }; + + Ok(data) + } +} diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 77cd21060..4e27688a7 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -74,13 +74,16 @@ pub mod pallet { use crate::{ dispatcher::Receipt, errors::HandlingError, - primitives::{ConsensusClientProvider, ISMP_ID}, + primitives::ConsensusClientProvider, weight_info::{WeightInfo, WeightProvider}, }; use alloc::collections::BTreeSet; use frame_support::{pallet_prelude::*, traits::UnixTime}; use frame_system::pallet_prelude::*; - use ismp_primitives::mmr::{LeafIndex, NodeIndex}; + use ismp_primitives::{ + mmr::{LeafIndex, NodeIndex}, + ISMP_ID, + }; use ismp_rs::{ consensus::{ ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineHeight, diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 2482e1e8a..92f39a7fb 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -21,9 +21,6 @@ use scale_info::TypeInfo; use sp_core::{crypto::AccountId32, H160}; use sp_std::prelude::*; -/// The `ConsensusEngineId` of ISMP. -pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; - /// An MMR proof data for a group of leaves. #[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] pub struct Proof { diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 02d8a767e..2690b3b4f 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -36,6 +36,7 @@ cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branc # local ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } pallet-ismp = { path = "../pallet-ismp", default-features = false } +substrate-state-machine = { path = "../pallet-ismp/primitives/state-machine", default-features = false } [dev-dependencies] @@ -61,4 +62,5 @@ std = [ "pallet-ismp/std", "hash-db/std", "serde", + "substrate-state-machine/std" ] diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 1a6399a66..ae1a154dc 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -17,7 +17,7 @@ use core::{marker::PhantomData, time::Duration}; -use alloc::{boxed::Box, collections::BTreeMap, format, vec, vec::Vec}; +use alloc::{boxed::Box, collections::BTreeMap, format, vec::Vec}; use codec::{Decode, Encode}; use core::fmt::Debug; use ismp::{ @@ -27,44 +27,32 @@ use ismp::{ }, error::Error, host::{IsmpHost, StateMachine}, - messaging::{Proof, StateCommitmentHeight}, - router::{Request, RequestResponse}, - util::hash_request, + messaging::StateCommitmentHeight, }; -use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; -use merkle_mountain_range::MerkleProof; -use pallet_ismp::host::Host; +use ismp_primitives::ISMP_ID; use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use primitive_types::H256; use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; use sp_runtime::{ app_crypto::sp_core::storage::StorageKey, generic::Header, - traits::{BlakeTwo256, Header as _, Keccak256}, + traits::{BlakeTwo256, Header as _}, DigestItem, }; use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; +use substrate_state_machine::SubstrateStateMachine; use crate::RelayChainOracle; /// The parachain consensus client implementation for ISMP. pub struct ParachainConsensusClient(PhantomData<(T, R)>); -/// The parachain state machine implementation for ISMP. -pub struct ParachainStateMachine(PhantomData); - impl Default for ParachainConsensusClient { fn default() -> Self { Self(PhantomData) } } -impl Default for ParachainStateMachine { - fn default() -> Self { - Self(PhantomData) - } -} - /// Information necessary to prove the sibling parachain's finalization to this /// parachain. #[derive(Debug, Encode, Decode)] @@ -77,39 +65,6 @@ pub struct ParachainConsensusProof { pub storage_proof: Vec>, } -/// Hashing algorithm for the state proof -#[derive(Debug, Encode, Decode, Clone)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -pub enum HashAlgorithm { - /// For chains that use keccak as their hashing algo - Keccak, - /// For chains that use blake2 as their hashing algo - Blake2, -} - -/// Holds the relevant data needed for state proof verification -#[derive(Debug, Encode, Decode, Clone)] -pub struct SubstrateStateProof { - /// Algorithm to use for state proof verification - pub hasher: HashAlgorithm, - /// Storage proof for the parachain headers - pub storage_proof: Vec>, -} - -/// Holds the relevant data needed for request/response proof verification -#[derive(Debug, Encode, Decode, Clone)] -pub struct MembershipProof { - /// Size of the mmr at the time this proof was generated - pub mmr_size: u64, - /// Leaf indices for the proof - pub leaf_indices: Vec, - /// Mmr proof - pub proof: Vec, -} - -/// The `ConsensusEngineId` of ISMP digest in the parachain header. -pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; - /// ConsensusClientId for [`ParachainConsensusClient`] pub const PARACHAIN_CONSENSUS_ID: ConsensusClientId = *b"PARA"; @@ -251,124 +206,9 @@ where } fn state_machine(&self, _id: StateMachine) -> Result, Error> { - Ok(Box::new(ParachainStateMachine::::default())) - } -} - -impl StateMachineClient for ParachainStateMachine -where - T: pallet_ismp::Config + super::Config, - T::BlockNumber: Into, - T::Hash: From, -{ - fn verify_membership( - &self, - _host: &dyn IsmpHost, - item: RequestResponse, - state: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let membership = MembershipProof::decode(&mut &*proof.proof).map_err(|e| { - Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) - })?; - let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = - MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); - let leaves: Vec<(u64, DataOrHash)> = match item { - RequestResponse::Request(req) => membership - .leaf_indices - .into_iter() - .zip(req.into_iter()) - .map(|(pos, req)| (pos, DataOrHash::Data(Leaf::Request(req)))) - .collect(), - RequestResponse::Response(res) => membership - .leaf_indices - .into_iter() - .zip(res.into_iter()) - .map(|(pos, res)| (pos, DataOrHash::Data(Leaf::Response(res)))) - .collect(), - }; - let root = state - .overlay_root - .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; - - let calc_root = proof - .calculate_root(leaves.clone()) - .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; - let valid = calc_root.hash::>() == root.into(); - - if !valid { - Err(Error::ImplementationSpecific("Invalid membership proof".into()))? - } - - Ok(()) - } - - fn state_trie_key(&self, requests: Vec) -> Vec> { - let mut keys = vec![]; - - for req in requests { - match req { - Request::Post(post) => { - let request = Request::Post(post); - let commitment = hash_request::>(&request).0.to_vec(); - keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); - } - Request::Get(_) => continue, - } - } - - keys - } - - fn verify_state_proof( - &self, - _host: &dyn IsmpHost, - keys: Vec>, - root: StateCommitment, - proof: &Proof, - ) -> Result, Option>>, Error> { - let state_proof: SubstrateStateProof = codec::Decode::decode(&mut &*proof.proof) - .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; - - let data = match state_proof.hasher { - HashAlgorithm::Keccak => { - let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); - keys.into_iter() - .map(|key| { - let value = trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!( - "Error reading state proof: {e:?}" - )) - })?; - Ok((key, value)) - }) - .collect::, _>>()? - } - HashAlgorithm::Blake2 => { - let db = - StorageProof::new(state_proof.storage_proof).into_memory_db::(); - - let trie = - TrieDBBuilder::>::new(&db, &root.state_root).build(); - keys.into_iter() - .map(|key| { - let value = trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!( - "Error reading state proof: {e:?}" - )) - })?; - Ok((key, value)) - }) - .collect::, _>>()? - } - }; - - Ok(data) + Ok(Box::new(SubstrateStateMachine::::default())) } } - /// This returns the storage key for a parachain header on the relay chain. pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec(); From b5a859a26d27ada9839a5e1197610abb3302ef65 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 26 Jul 2023 12:42:11 +0200 Subject: [PATCH 150/182] update consensus state (#72) --- .github/workflows/ci.yml | 2 +- Cargo.lock | 62 +++++++++---------- grandpa/Cargo.toml | 2 +- grandpa/src/consensus.rs | 2 +- grandpa/src/lib.rs | 8 +-- .../src/{consensus_message.rs => messages.rs} | 0 ismp-demo/src/lib.rs | 2 + pallet-ismp/src/benchmarking.rs | 11 +++- pallet-ismp/src/dispatcher.rs | 2 + pallet-ismp/src/lib.rs | 53 +++++----------- .../src/{ismp_mocks.rs => mocks/ismp.rs} | 1 + pallet-ismp/src/{mock.rs => mocks/mod.rs} | 6 +- pallet-ismp/src/tests.rs | 10 ++- parachain/src/lib.rs | 55 ++++++++++------ 14 files changed, 117 insertions(+), 99 deletions(-) rename grandpa/src/{consensus_message.rs => messages.rs} (100%) rename pallet-ismp/src/{ismp_mocks.rs => mocks/ismp.rs} (99%) rename pallet-ismp/src/{mock.rs => mocks/mod.rs} (96%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a210b715..885e9c415 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: cargo +nightly-2022-10-28 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose --locked cargo +nightly-2022-10-28 check -p ismp-grandpa-primitives --no-default-features --target=wasm32-unknown-unknown --verbose --locked cargo +nightly-2022-10-28 check -p ismp-grandpa-verifier --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p pallet-grandpa-client --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p ismp-grandpa --no-default-features --target=wasm32-unknown-unknown --verbose --locked - name: Test run: | diff --git a/Cargo.lock b/Cargo.lock index e13f94bda..57cdd4850 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3144,7 +3144,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#a5c64058503888bc38cd13755db2f717a139f7e1" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#dca2cc6ef9ce28129e5f8c16de59f0970bd711f4" dependencies = [ "derive_more", "parity-scale-codec", @@ -3169,6 +3169,31 @@ dependencies = [ "sp-runtime 7.0.0", ] +[[package]] +name = "ismp-grandpa" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "cumulus-primitives-core", + "finality-grandpa", + "frame-support", + "frame-system", + "ismp", + "ismp-grandpa-primitives", + "ismp-grandpa-verifier", + "ismp-primitives", + "pallet-ismp", + "parity-scale-codec", + "primitive-types", + "scale-info", + "sp-consensus-aura", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-trie 7.0.0", + "substrate-state-machine", +] + [[package]] name = "ismp-grandpa-primitives" version = "0.1.0" @@ -3349,7 +3374,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#a5c64058503888bc38cd13755db2f717a139f7e1" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#dca2cc6ef9ce28129e5f8c16de59f0970bd711f4" dependencies = [ "ismp", "parity-scale-codec", @@ -4148,9 +4173,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e6ab01971eb092ffe6a7d42f49f9ff42662f17604681e2843ad65077ba47dc" +checksum = "121f7402cc6ab5821dad08d1b9d11618a9ea4da992343909fecf8e430e86364c" dependencies = [ "cc", "pkg-config", @@ -4949,31 +4974,6 @@ dependencies = [ "sp-std 5.0.0", ] -[[package]] -name = "pallet-grandpa-client" -version = "0.1.0" -dependencies = [ - "ckb-merkle-mountain-range", - "cumulus-primitives-core", - "finality-grandpa", - "frame-support", - "frame-system", - "ismp", - "ismp-grandpa-primitives", - "ismp-grandpa-verifier", - "ismp-primitives", - "pallet-ismp", - "parity-scale-codec", - "primitive-types", - "scale-info", - "sp-consensus-aura", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-trie 7.0.0", - "substrate-state-machine", -] - [[package]] name = "pallet-ismp" version = "0.1.0" @@ -11947,9 +11947,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] diff --git a/grandpa/Cargo.toml b/grandpa/Cargo.toml index b1cb9b48f..8e7ddbb06 100644 --- a/grandpa/Cargo.toml +++ b/grandpa/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pallet-grandpa-client" +name = "ismp-grandpa" version = "0.1.0" edition = "2021" diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs index a315245c0..342516476 100644 --- a/grandpa/src/consensus.rs +++ b/grandpa/src/consensus.rs @@ -12,7 +12,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific lang -use crate::consensus_message::ConsensusMessage; +use crate::messages::ConsensusMessage; use alloc::{boxed::Box, collections::BTreeMap, format, vec::Vec}; use codec::{Decode, Encode}; use core::marker::PhantomData; diff --git a/grandpa/src/lib.rs b/grandpa/src/lib.rs index 0e0402979..abd26cf2f 100644 --- a/grandpa/src/lib.rs +++ b/grandpa/src/lib.rs @@ -16,7 +16,7 @@ extern crate alloc; pub mod consensus; -pub mod consensus_message; +pub mod messages; use alloc::{vec, vec::Vec}; pub use pallet::*; @@ -39,8 +39,6 @@ pub mod pallet { pub trait Config: frame_system::Config + pallet_ismp::Config { /// The overarching event type type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Origin allowed to add or remove parachains in Consensus State - type AdminOrigin: EnsureOrigin; } /// Events emitted by this pallet @@ -76,7 +74,7 @@ pub mod pallet { consensus_state_id_vec: Vec, para_ids: Vec, ) -> DispatchResult { - ::AdminOrigin::ensure_origin(origin)?; + T::AdminOrigin::ensure_origin(origin)?; let ismp_host = Host::::default(); let consensus_state_id = consensus_state_id_vec @@ -112,7 +110,7 @@ pub mod pallet { consensus_state_id_vec: Vec, para_ids: Vec, ) -> DispatchResult { - ::AdminOrigin::ensure_origin(origin)?; + T::AdminOrigin::ensure_origin(origin)?; let ismp_host = Host::::default(); let consensus_state_id = consensus_state_id_vec diff --git a/grandpa/src/consensus_message.rs b/grandpa/src/messages.rs similarity index 100% rename from grandpa/src/consensus_message.rs rename to grandpa/src/messages.rs diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index cedf9bc4f..113ed0c6f 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -142,6 +142,7 @@ pub mod pallet { to: PALLET_ID.encode(), timeout_timestamp: params.timeout, data: payload.encode(), + gas_limit: 0, }; // dispatch the request @@ -179,6 +180,7 @@ pub mod pallet { keys: params.keys, height: params.height as u64, timeout_timestamp: params.timeout, + gas_limit: 0, }; let dispatcher = T::IsmpDispatcher::default(); diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 3ccaccde5..147077586 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -36,7 +36,7 @@ pub mod benchmarks { use super::*; use crate::{ host::Host, - ismp_mocks::{setup_mock_client, MOCK_CONSENSUS_STATE_ID, MODULE_ID}, + mocks::ismp::{setup_mock_client, MOCK_CONSENSUS_STATE_ID, MODULE_ID}, Config, Event, Pallet, RequestCommitments, RequestReceipts, ResponseReceipts, }; use codec::Encode; @@ -69,6 +69,7 @@ pub mod benchmarks { consensus_client_id: MOCK_CONSENSUS_STATE_ID, consensus_state_id: MOCK_CONSENSUS_STATE_ID, unbonding_period: u64::MAX, + challenge_period: 0, state_machine_commitments: vec![( StateMachineId { state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), @@ -103,6 +104,8 @@ pub mod benchmarks { source: StateMachine::Ethereum(Ethereum::ExecutionLayer), dest: ::StateMachine::get(), nonce: 0, + gas_limit: 0, + from: MODULE_ID.encode(), to: MODULE_ID.encode(), timeout_timestamp: 5000, @@ -132,6 +135,8 @@ pub mod benchmarks { from: MODULE_ID.encode(), to: MODULE_ID.encode(), timeout_timestamp: 5000, + gas_limit: 0, + data: "handle_response_message".as_bytes().to_vec(), }; let request = Request::Post(post.clone()); @@ -166,6 +171,7 @@ pub mod benchmarks { source: ::StateMachine::get(), dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, + gas_limit: 0, from: MODULE_ID.encode(), to: MODULE_ID.encode(), timeout_timestamp: 500, @@ -199,6 +205,7 @@ pub mod benchmarks { dest: StateMachine::Kusama(2001), nonce: nonce.into(), from: vec![0u8; 32], + gas_limit: 0, to: vec![1u8; 32], timeout_timestamp: 100, data: vec![2u8; 64], @@ -216,5 +223,5 @@ pub mod benchmarks { } } - impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mocks::Test); } diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index 17ab720e5..995456acd 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -57,6 +57,7 @@ where dest: dispatch_get.dest, nonce: host.next_nonce(), from: dispatch_get.from, + gas_limit: 0, keys: dispatch_get.keys, height: dispatch_get.height, timeout_timestamp: dispatch_get.timeout_timestamp, @@ -68,6 +69,7 @@ where source: host.host_state_machine(), dest: dispatch_post.dest, nonce: host.next_nonce(), + gas_limit: 0, from: dispatch_post.from, to: dispatch_post.to, timeout_timestamp: dispatch_post.timeout_timestamp, diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 4e27688a7..41eccd602 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -27,11 +27,9 @@ mod errors; pub mod events; pub mod handlers; pub mod host; -#[cfg(any(feature = "runtime-benchmarks", test))] -mod ismp_mocks; mod mmr; -#[cfg(test)] -pub mod mock; +#[cfg(any(feature = "runtime-benchmarks", test))] +pub mod mocks; pub mod primitives; #[cfg(test)] pub mod tests; @@ -300,20 +298,13 @@ pub mod pallet { /// Params to update the unbonding period for a consensus state #[derive(Debug, Clone, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] - pub struct UnbondingUpdate { - /// Consensus state identifier - consensus_state_id: ConsensusStateId, - /// Unbonding duration - unbonding_period: u64, - } - - /// Params to update the challenge period for a consensus state - #[derive(Debug, Clone, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] - pub struct ChallengeUpdate { + pub struct UpdateConsensusState { /// Consensus state identifier pub consensus_state_id: ConsensusStateId, + /// Unbonding duration + pub unbonding_period: Option, /// Challenge period duration - pub challenge_period: u64, + pub challenge_period: Option, } #[pallet::call] @@ -352,35 +343,25 @@ pub mod pallet { } /// Set the unbonding period for a consensus state. - #[pallet::weight(::DbWeight::get().writes(1))] + #[pallet::weight(::DbWeight::get().writes(2))] #[pallet::call_index(2)] - pub fn set_unbonding_period( + pub fn update_consensus_state( origin: OriginFor, - message: UnbondingUpdate, + message: UpdateConsensusState, ) -> DispatchResult { T::AdminOrigin::ensure_origin(origin)?; let host = Host::::default(); - host.store_unbonding_period(message.consensus_state_id, message.unbonding_period) - .map_err(|_| Error::::UnbondingPeriodUpdateFailed)?; - - Ok(()) - } - - /// Set the challenge period for a consensus state. - #[pallet::weight(::DbWeight::get().writes(1))] - #[pallet::call_index(3)] - pub fn set_challenge_period( - origin: OriginFor, - message: ChallengeUpdate, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - let host = Host::::default(); + if let Some(unbonding_period) = message.unbonding_period { + host.store_unbonding_period(message.consensus_state_id, unbonding_period) + .map_err(|_| Error::::UnbondingPeriodUpdateFailed)?; + } - host.store_challenge_period(message.consensus_state_id, message.challenge_period) - .map_err(|_| Error::::ChallengePeriodUpdateFailed)?; + if let Some(challenge_period) = message.challenge_period { + host.store_challenge_period(message.consensus_state_id, challenge_period) + .map_err(|_| Error::::UnbondingPeriodUpdateFailed)?; + } Ok(()) } diff --git a/pallet-ismp/src/ismp_mocks.rs b/pallet-ismp/src/mocks/ismp.rs similarity index 99% rename from pallet-ismp/src/ismp_mocks.rs rename to pallet-ismp/src/mocks/ismp.rs index db6dd2087..478509d2d 100644 --- a/pallet-ismp/src/ismp_mocks.rs +++ b/pallet-ismp/src/mocks/ismp.rs @@ -115,6 +115,7 @@ where consensus_client_id: MOCK_CONSENSUS_STATE_ID, consensus_state_id: MOCK_CONSENSUS_STATE_ID, unbonding_period: 1_000_000, + challenge_period: 0, state_machine_commitments: vec![( StateMachineId { state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), diff --git a/pallet-ismp/src/mock.rs b/pallet-ismp/src/mocks/mod.rs similarity index 96% rename from pallet-ismp/src/mock.rs rename to pallet-ismp/src/mocks/mod.rs index c7a0af284..548562f58 100644 --- a/pallet-ismp/src/mock.rs +++ b/pallet-ismp/src/mocks/mod.rs @@ -13,6 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Mock implementations for tests & benchmarks +#![allow(missing_docs)] +pub mod ismp; + use crate as pallet_ismp; use crate::*; @@ -21,7 +25,7 @@ use frame_support::traits::{ConstU32, ConstU64, Get}; use frame_system::EnsureRoot; use ismp_rs::{consensus::ConsensusClient, module::IsmpModule, router::IsmpRouter}; -use crate::ismp_mocks::{MockConsensusClient, MockModule}; +use ismp::{MockConsensusClient, MockModule}; use sp_core::H256; use sp_runtime::{ testing::Header, diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index 8b39c1373..b2f3bee9c 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{mock::*, *}; +use crate::{mocks::*, *}; use std::{ ops::Range, time::{SystemTime, UNIX_EPOCH}, @@ -21,7 +21,7 @@ use std::{ use crate::{ dispatcher::Dispatcher, - ismp_mocks::{setup_mock_client, MOCK_CONSENSUS_STATE_ID}, + mocks::ismp::{setup_mock_client, MOCK_CONSENSUS_STATE_ID}, }; use frame_support::traits::OnFinalize; use ismp_primitives::mmr::MmrHasher; @@ -73,6 +73,7 @@ fn push_leaves(range: Range) -> Vec { to: vec![1u8; 32], timeout_timestamp: 100 * nonce, data: vec![2u8; 64], + gas_limit: 0, }; let request = Request::Post(post); @@ -274,6 +275,7 @@ fn should_handle_get_request_timeouts_correctly() { let msg = DispatchGet { dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), from: vec![0u8; 32], + gas_limit: 0, keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 2, timeout_timestamp: 1000, @@ -289,6 +291,7 @@ fn should_handle_get_request_timeouts_correctly() { keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 2, timeout_timestamp: 1000, + gas_limit: 0, }; ismp_rs::router::Request::Get(get) }) @@ -319,6 +322,8 @@ fn should_handle_get_request_responses_correctly() { let msg = DispatchGet { dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), from: vec![0u8; 32], + gas_limit: 0, + keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 3, timeout_timestamp: 1000, @@ -331,6 +336,7 @@ fn should_handle_get_request_responses_correctly() { dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: i, from: vec![0u8; 32], + gas_limit: 0, keys: vec![vec![1u8; 32], vec![1u8; 32]], height: 3, timeout_timestamp: 1000, diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index c76f3ff98..1e5acdefa 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -26,9 +26,10 @@ pub mod consensus; use alloc::{vec, vec::Vec}; use cumulus_primitives_core::relay_chain; -use ismp::handlers; +use ismp::{handlers, messaging::CreateConsensusState}; pub use pallet::*; use pallet_ismp::host::Host; +use primitive_types::H256; #[frame_support::pallet] pub mod pallet { @@ -38,10 +39,9 @@ pub mod pallet { use frame_system::pallet_prelude::*; use ismp::{ host::IsmpHost, - messaging::{ConsensusMessage, CreateConsensusState, Message}, + messaging::{ConsensusMessage, Message}, }; use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; - use primitive_types::H256; #[pallet::pallet] pub struct Pallet(_); @@ -109,7 +109,7 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight(::DbWeight::get().writes(para_ids.len() as u64))] pub fn add_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { - ensure_root(origin)?; + T::AdminOrigin::ensure_origin(origin)?; for id in para_ids { Parachains::::insert(id, ()); } @@ -121,7 +121,7 @@ pub mod pallet { #[pallet::call_index(2)] #[pallet::weight(::DbWeight::get().writes(para_ids.len() as u64))] pub fn remove_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { - ensure_root(origin)?; + T::AdminOrigin::ensure_origin(origin)?; for id in para_ids { Parachains::::remove(id); } @@ -132,7 +132,10 @@ pub mod pallet { // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] - impl Hooks> for Pallet { + impl Hooks> for Pallet + where + ::Hash: From, + { fn on_finalize(_n: T::BlockNumber) { let state = RelaychainDataProvider::::current_relay_chain_state(); if !RelayChainState::::contains_key(state.number) { @@ -151,6 +154,11 @@ pub mod pallet { // kill the storage, since this is the beginning of a new block. ConsensusUpdated::::kill(); + let host = Host::::default(); + if let Err(_) = host.consensus_state(consensus::PARACHAIN_CONSENSUS_ID) { + Pallet::::initialize(host); + } + Weight::from_parts(0, 0) } } @@ -200,19 +208,8 @@ pub mod pallet { { fn build(&self) { let host = Host::::default(); + Pallet::::initialize(host); - let message = CreateConsensusState { - // insert empty bytes - consensus_state: vec![], - unbonding_period: u64::MAX, - consensus_state_id: consensus::PARACHAIN_CONSENSUS_ID, - consensus_client_id: consensus::PARACHAIN_CONSENSUS_ID, - state_machine_commitments: vec![], - }; - handlers::create_client(&host, message) - .expect("Failed to initialize parachain consensus client"); - host.store_challenge_period(consensus::PARACHAIN_CONSENSUS_ID, 0) - .expect("Failed to set parachain challenge period"); // insert the parachain ids for id in &self.parachains { Parachains::::insert(id, ()); @@ -221,12 +218,32 @@ pub mod pallet { } } -impl Pallet { +impl Pallet +where + ::Hash: From, +{ /// Returns the list of parachains who's consensus updates will be inserted by the inherent /// data provider pub fn para_ids() -> Vec { Parachains::::iter_keys().collect() } + + /// Initializes the parachain consensus state. Rather than requiring a seperate + /// `create_consensus_state` call, simply including this pallet in your runtime will create the + /// ismp parachain client consensus state, either through `genesis_build` or `on_initialize`. + pub fn initialize(host: Host) { + let message = CreateConsensusState { + // insert empty bytes + consensus_state: vec![], + unbonding_period: u64::MAX, + challenge_period: 0, + consensus_state_id: consensus::PARACHAIN_CONSENSUS_ID, + consensus_client_id: consensus::PARACHAIN_CONSENSUS_ID, + state_machine_commitments: vec![], + }; + handlers::create_client(&host, message) + .expect("Failed to initialize parachain consensus client"); + } } /// Interface that exposes the relay chain state roots. From c76c779f02f6f92e5d481d604032c9aaab5ca15b Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 26 Jul 2023 13:27:40 +0200 Subject: [PATCH 151/182] fix ismp book link (#73) --- README.md | 2 +- grandpa/src/consensus.rs | 47 ++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 64c20ca6d..fb27fe647 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ This repo holds all the required components substrate runtimes need to interoper ## Documentation -Installation and integration guides can be found in the [book](https://substrate-ismp.polytope.technology). +Installation and integration guides can be found in the [book](https://ismp.polytope.technology). ## Testing and Testing Guide diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs index 342516476..1790faa1f 100644 --- a/grandpa/src/consensus.rs +++ b/grandpa/src/consensus.rs @@ -19,7 +19,8 @@ use core::marker::PhantomData; use finality_grandpa::Chain; use ismp::{ consensus::{ - ConsensusClient, ConsensusStateId, StateCommitment, StateMachineClient, VerifiedCommitments, + ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineClient, + VerifiedCommitments, }, error::Error, host::{IsmpHost, StateMachine}, @@ -37,20 +38,26 @@ use verifier::{ verify_grandpa_finality_proof, verify_parachain_headers_with_grandpa_finality_proof, }; -pub const POLKADOT_CONSENSUS_STATE_ID: [u8; 8] = *b"polkadot"; -pub const KUSAMA_CONSENSUS_STATE_ID: [u8; 8] = *b"_kusama_"; +/// [`ConsensusStateId`] for the polkadot relay chain +pub const POLKADOT_CONSENSUS_STATE_ID: ConsensusStateId = *b"polk"; -pub struct GrandpaConsensusClient(PhantomData<(T, H)>); +/// [`ConsensusStateId`] for the kusama relay chain +pub const KUSAMA_CONSENSUS_STATE_ID: ConsensusStateId = *b"sama"; -impl Default for GrandpaConsensusClient { +/// [`ConsensusClientId`] for GRANDPA consensus +pub const GRANDPA_CONSENSUS_ID: ConsensusClientId = *b"GRAN"; + +pub struct GrandpaConsensusClient(PhantomData); + +impl Default for GrandpaConsensusClient { fn default() -> Self { Self(PhantomData) } } -impl ConsensusClient for GrandpaConsensusClient +impl ConsensusClient for GrandpaConsensusClient where - H: Header, + T::Header: Header, T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, T::Hash: From, @@ -197,15 +204,15 @@ where )) })?; - let first_proof: FinalityProof = - codec::Decode::decode(&mut &proof_1[..]).map_err(|e| { + let first_proof: FinalityProof = codec::Decode::decode(&mut &proof_1[..]) + .map_err(|e| { Error::ImplementationSpecific(format!( "Cannot decode first finality proof from proof_1 bytes: {e:?}", )) })?; - let second_proof: FinalityProof = - codec::Decode::decode(&mut &proof_2[..]).map_err(|e| { + let second_proof: FinalityProof = codec::Decode::decode(&mut &proof_2[..]) + .map_err(|e| { Error::ImplementationSpecific(format!( "Cannot decode second finality proof from proof_2 bytes: {e:?}", )) @@ -217,13 +224,13 @@ where ))) } - let first_headers = AncestryChain::::new(&first_proof.unknown_headers); + let first_headers = AncestryChain::::new(&first_proof.unknown_headers); let first_target = first_proof.unknown_headers.iter().max_by_key(|h| *h.number()).ok_or_else(|| { Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) })?; - let second_headers = AncestryChain::::new(&second_proof.unknown_headers); + let second_headers = AncestryChain::::new(&second_proof.unknown_headers); let second_target = second_proof.unknown_headers.iter().max_by_key(|h| *h.number()).ok_or_else(|| { Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) @@ -261,14 +268,16 @@ where } let first_justification = - GrandpaJustification::::decode(&mut &first_proof.justification[..]).map_err( - |_| Error::ImplementationSpecific(format!("Could not decode first justification")), - )?; + GrandpaJustification::::decode(&mut &first_proof.justification[..]) + .map_err(|_| { + Error::ImplementationSpecific(format!("Could not decode first justification")) + })?; let second_justification = - GrandpaJustification::::decode(&mut &second_proof.justification[..]).map_err( - |_| Error::ImplementationSpecific(format!("Could not decode second justification")), - )?; + GrandpaJustification::::decode(&mut &second_proof.justification[..]) + .map_err(|_| { + Error::ImplementationSpecific(format!("Could not decode second justification")) + })?; if first_proof.block != first_justification.commit.target_hash || second_proof.block != second_justification.commit.target_hash From 555b2019d51204c4ff759c8fcecc48f3bd03ea49 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Thu, 27 Jul 2023 13:58:13 +0100 Subject: [PATCH 152/182] update readme (#74) --- Cargo.lock | 24 ++++++++++++------------ README.md | 7 ++++++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57cdd4850..59836832e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2632,9 +2632,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" +checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" dependencies = [ "aho-corasick", "bstr", @@ -3144,7 +3144,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#dca2cc6ef9ce28129e5f8c16de59f0970bd711f4" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c72f00a984e0f53d842b0b378a7a9f19580042f9" dependencies = [ "derive_more", "parity-scale-codec", @@ -3374,7 +3374,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#dca2cc6ef9ce28129e5f8c16de59f0970bd711f4" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c72f00a984e0f53d842b0b378a7a9f19580042f9" dependencies = [ "ismp", "parity-scale-codec", @@ -4173,9 +4173,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.11" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121f7402cc6ab5821dad08d1b9d11618a9ea4da992343909fecf8e430e86364c" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "pkg-config", @@ -8026,18 +8026,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.175" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.175" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", @@ -8046,9 +8046,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", diff --git a/README.md b/README.md index fb27fe647..f1cb0ca8f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# pallet-ismp ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) [![Telegram: YourGroup](https://img.shields.io/badge/-Telegram-blue?style=flat-square&logo=Telegram&logoColor=white&link=https://t.me/YourGroup)](https://t.me/ismp_guide) [![Discord: YourServer](https://img.shields.io/badge/-Discord-7289DA?style=flat-square&logo=Discord&logoColor=white&link=https://discord.gg/YourServer)](https://discord.gg/vKAa3XcCBX) +# ismp-substrate ![Unit Tests](https://github.com/polytope-labs/substrate-ismp/actions/workflows/ci.yml/badge.svg) [![Telegram: YourGroup](https://img.shields.io/badge/-Telegram-blue?style=flat-square&logo=Telegram&logoColor=white&link=https://t.me/YourGroup)](https://t.me/ismp_guide) [![Discord: YourServer](https://img.shields.io/badge/-Discord-7289DA?style=flat-square&logo=Discord&logoColor=white&link=https://discord.gg/YourServer)](https://discord.gg/vKAa3XcCBX) Implementation of the Interoperable State Machine Protocol for substrate runtimes. This project is [funded by the web3 foundation](https://github.com/w3f/Grants-Program/blob/master/applications/ismp.md). @@ -18,6 +18,11 @@ This repo holds all the required components substrate runtimes need to interoper - [ismp-parachain-inherent](./parachain/inherent) - [ismp-parachain-runtime-api](./parachain/runtime-api) +### Solochain Support + +- [ismp-grandpa](./grandpa) +- [ismp-grandpa-prover](./grandpa/prover) + ## Documentation Installation and integration guides can be found in the [book](https://ismp.polytope.technology). From 218a8aae63dcc5ef9c9eb6788289b88cdded43d3 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Fri, 28 Jul 2023 17:10:17 +0100 Subject: [PATCH 153/182] allowed proxies (#75) * allowed proxies * set config --- Cargo.lock | 20 ++++++++++---------- pallet-ismp/src/host.rs | 16 ++++++++++++---- pallet-ismp/src/lib.rs | 18 +++++++++++++++++- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59836832e..f61055ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3144,7 +3144,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c72f00a984e0f53d842b0b378a7a9f19580042f9" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59cac5c7e9961bc0884641339dea0244b859892b" dependencies = [ "derive_more", "parity-scale-codec", @@ -3374,7 +3374,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#c72f00a984e0f53d842b0b378a7a9f19580042f9" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59cac5c7e9961bc0884641339dea0244b859892b" dependencies = [ "ismp", "parity-scale-codec", @@ -8026,18 +8026,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.176" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" +checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.176" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" +checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" dependencies = [ "proc-macro2", "quote", @@ -10329,9 +10329,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-ctl" -version = "0.5.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" +checksum = "619bfed27d807b54f7f776b9430d4f8060e66ee138a28632ca898584d462c31c" dependencies = [ "libc", "paste", @@ -10340,9 +10340,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.3+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" dependencies = [ "cc", "libc", diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 057d3ad5d..2cccd16da 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -15,10 +15,10 @@ //! Host implementation for ISMP use crate::{ - dispatcher::Receipt, primitives::ConsensusClientProvider, ChallengePeriod, Config, - ConsensusClientUpdateTime, ConsensusStateClient, ConsensusStates, FrozenConsensusClients, - FrozenHeights, LatestStateMachineHeight, Nonce, RequestCommitments, RequestReceipts, - ResponseReceipts, StateCommitments, UnbondingPeriod, + dispatcher::Receipt, primitives::ConsensusClientProvider, AllowedProxies, ChallengePeriod, + Config, ConsensusClientUpdateTime, ConsensusStateClient, ConsensusStates, + FrozenConsensusClients, FrozenHeights, LatestStateMachineHeight, Nonce, RequestCommitments, + RequestReceipts, ResponseReceipts, StateCommitments, UnbondingPeriod, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -250,4 +250,12 @@ where ChallengePeriod::::insert(consensus_state_id, period); Ok(()) } + + fn allowed_proxies(&self) -> Vec { + AllowedProxies::::get() + } + + fn store_allowed_proxies(&self, allowed: Vec) { + AllowedProxies::::set(allowed); + } } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 41eccd602..1d5333623 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -122,7 +122,6 @@ pub mod pallet { /// Configurable router that dispatches calls to modules type IsmpRouter: IsmpRouter + Default; - /// Provides concrete implementations of consensus clients type ConsensusClientProvider: ConsensusClientProvider; @@ -205,6 +204,11 @@ pub mod pallet { pub type LatestStateMachineHeight = StorageMap<_, Blake2_128Concat, StateMachineId, u64, ValueQuery>; + /// Bounded vec of allowed proxies + #[pallet::storage] + #[pallet::getter(fn allowed_proxies)] + pub type AllowedProxies = StorageValue<_, Vec, ValueQuery>; + /// Holds the timestamp at which a consensus client was recently updated. /// Used in ensuring that the configured challenge period elapses. #[pallet::storage] @@ -365,6 +369,18 @@ pub mod pallet { Ok(()) } + + /// Set the allowed proxies + #[pallet::weight(::DbWeight::get().writes(1))] + #[pallet::call_index(3)] + pub fn set_config(origin: OriginFor, allowed: Vec) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + let host = Host::::default(); + host.store_allowed_proxies(allowed); + + Ok(()) + } } #[pallet::event] From 5e89a3747740776aae0a036496e6bcec8939549d Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:00:29 +0100 Subject: [PATCH 154/182] EVM Contract Support (#66) * evm precompiles * minor fixes * refactor * implement all precompiles * started work on handlers * implement contracts module handler * add benchmarks * refunds for unused gas * rewrite * defaults * refactor ismp evm * example contract * test data * mocks * initial tests * changes to tests * nit * add tests for get dispatch * chore * remove unused dependencies * remove unused dependencies * still fixing abi decoding in get dispatch precompile * abi decoding bug fixed * precompiles * minor fixes * additional tests * update ismp * ismp update * unit tests * host address * remove unused dependencies * chore * chore * assert source module correctness request dispatch * fix conflicts * fix ci * adds addresses for the ismp precompiles * renames * consolidate handler and dispatcher * deduplicate read_proof_check * no-std patch * add todo * fix tests * chore: forge init * add ismp-solidity as sub module * nits * update ismp-solidity --------- Co-authored-by: Seun Lanlege --- .github/workflows/ci.yml | 1 + .gitignore | 4 +- .gitmodules | 3 + Cargo.lock | 718 ++++++++++++++---- Cargo.toml | 5 + grandpa/verifier/Cargo.toml | 6 +- grandpa/verifier/src/lib.rs | 4 +- grandpa/verifier/src/state_machine.rs | 66 -- ismp-demo/src/lib.rs | 6 +- pallet-ismp/Cargo.toml | 4 +- pallet-ismp/evm/Cargo.toml | 59 ++ pallet-ismp/evm/solidity/IsmpDemo.bin | 1 + pallet-ismp/evm/solidity/foundry.toml | 0 pallet-ismp/evm/solidity/lib/ismp-solidity | 1 + pallet-ismp/evm/solidity/remappings.txt | 3 + pallet-ismp/evm/solidity/src/example.sol | 156 ++++ pallet-ismp/evm/src/abi.rs | 99 +++ pallet-ismp/evm/src/lib.rs | 30 + pallet-ismp/evm/src/mocks.rs | 244 ++++++ pallet-ismp/evm/src/module.rs | 203 +++++ pallet-ismp/evm/src/precompiles.rs | 176 +++++ pallet-ismp/evm/src/tests.rs | 433 +++++++++++ pallet-ismp/evm/src/weight.rs | 45 ++ .../primitives/state-machine/Cargo.toml | 5 +- .../primitives/state-machine/src/lib.rs | 36 +- pallet-ismp/src/benchmarking.rs | 94 ++- pallet-ismp/src/dispatcher.rs | 46 +- pallet-ismp/src/handlers.rs | 14 +- pallet-ismp/src/lib.rs | 35 +- pallet-ismp/src/mocks/ismp.rs | 5 +- pallet-ismp/src/primitives.rs | 45 +- pallet-ismp/src/tests.rs | 18 +- pallet-ismp/src/weight_info.rs | 36 +- parachain/Cargo.toml | 4 - parachain/src/consensus.rs | 36 +- 35 files changed, 2290 insertions(+), 351 deletions(-) create mode 100644 .gitmodules delete mode 100644 grandpa/verifier/src/state_machine.rs create mode 100644 pallet-ismp/evm/Cargo.toml create mode 100644 pallet-ismp/evm/solidity/IsmpDemo.bin create mode 100644 pallet-ismp/evm/solidity/foundry.toml create mode 160000 pallet-ismp/evm/solidity/lib/ismp-solidity create mode 100644 pallet-ismp/evm/solidity/remappings.txt create mode 100644 pallet-ismp/evm/solidity/src/example.sol create mode 100644 pallet-ismp/evm/src/abi.rs create mode 100644 pallet-ismp/evm/src/lib.rs create mode 100644 pallet-ismp/evm/src/mocks.rs create mode 100644 pallet-ismp/evm/src/module.rs create mode 100644 pallet-ismp/evm/src/precompiles.rs create mode 100644 pallet-ismp/evm/src/tests.rs create mode 100644 pallet-ismp/evm/src/weight.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 885e9c415..d5757ffb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,7 @@ jobs: cargo +nightly-2022-10-28 check -p ismp-grandpa-primitives --no-default-features --target=wasm32-unknown-unknown --verbose --locked cargo +nightly-2022-10-28 check -p ismp-grandpa-verifier --no-default-features --target=wasm32-unknown-unknown --verbose --locked cargo +nightly-2022-10-28 check -p ismp-grandpa --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2022-10-28 check -p ismp-evm --no-default-features --target=wasm32-unknown-unknown --verbose --locked - name: Test run: | diff --git a/.gitignore b/.gitignore index 78176b0fb..3b21ab82d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target .idea .cargo -.DS_Store \ No newline at end of file +.DS_Store +/pallet-ismp/evm/solidity/out +/pallet-ismp/evm/solidity/cache \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..26647ca3e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ismp-solidity"] + path = pallet-ismp/evm/solidity/lib/ismp-solidity + url = https://github.com/polytope-labs/ismp-solidity.git diff --git a/Cargo.lock b/Cargo.lock index f61055ccf..827e94bb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,63 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-primitives" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffce219323b389e69f7448bc41a1913cc96dc584ca4bdae98bc46440fffdd3e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal 0.4.1", + "itoa", + "proptest", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f54319708cdf93563fe45b1afd475901cecbd0edb992305dc91eadc52d7717e" +dependencies = [ + "arrayvec 0.7.4", + "bytes", + "smol_str", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec1ae936329818ebb681464819d01d4d4381039505775baa476bec440b3a95c" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6871798f0c7ae860604b1bbbefb27593eb397094b3536a167c02ff25cd6b788b" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -263,9 +320,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "approx" @@ -421,20 +478,20 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] name = "asynchronous-codec" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" dependencies = [ "bytes", "futures-sink", @@ -460,6 +517,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -561,6 +630,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -922,9 +1006,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.19" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" dependencies = [ "clap_builder", "clap_derive", @@ -933,9 +1017,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" dependencies = [ "anstream", "anstyle", @@ -945,14 +1029,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -988,6 +1072,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + [[package]] name = "const-oid" version = "0.9.4" @@ -1356,7 +1452,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -1515,7 +1611,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -1537,7 +1633,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -1766,7 +1862,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -1783,9 +1879,15 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.9" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519b83cd10f5f6e969625a409f735182bea5558cd8b64c655806ceaae36f1999" + +[[package]] +name = "dunce" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clonable" @@ -1810,9 +1912,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.12" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" @@ -1828,9 +1930,9 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ "der 0.7.7", "digest 0.10.7", @@ -1879,9 +1981,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -1919,7 +2021,7 @@ dependencies = [ "group 0.13.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1 0.7.3", + "sec1 0.7.2", "subtle", "zeroize", ] @@ -1995,12 +2097,115 @@ dependencies = [ "libc", ] +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89fb87a9e103f71b903b80b670200b54cc67a07578f070681f1fffb7396fb7" +dependencies = [ + "bytes", + "ethereum-types", + "hash-db 0.15.2", + "hash256-std-hasher", + "parity-scale-codec", + "rlp", + "scale-info", + "serde", + "sha3", + "triehash", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", +] + [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "evm" +version = "0.39.1" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "environmental", + "ethereum", + "evm-core", + "evm-gasometer", + "evm-runtime", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "serde", + "sha3", +] + +[[package]] +name = "evm-core" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", +] + +[[package]] +name = "evm-gasometer" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "environmental", + "evm-core", + "evm-runtime", + "primitive-types", +] + +[[package]] +name = "evm-runtime" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "environmental", + "evm-core", + "primitive-types", + "sha3", +] + [[package]] name = "exit-future" version = "0.2.0" @@ -2057,7 +2262,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -2081,12 +2286,6 @@ dependencies = [ "instant", ] -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - [[package]] name = "fatality" version = "0.0.6" @@ -2243,6 +2442,40 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#1a718546085be20ce381b70e1f9e4c8b4d4b1f03" +dependencies = [ + "hex", + "impl-serde", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-runtime-interface 7.0.0", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#1a718546085be20ce381b70e1f9e4c8b4d4b1f03" +dependencies = [ + "evm", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", +] + [[package]] name = "fragile" version = "2.0.0" @@ -2332,7 +2565,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -2344,7 +2577,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -2354,7 +2587,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -2462,7 +2695,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand 1.9.0", + "fastrand", "futures-core", "futures-io", "memchr", @@ -2479,7 +2712,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -2632,9 +2865,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.12" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ "aho-corasick", "bstr", @@ -2852,9 +3085,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" @@ -3002,6 +3235,15 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + [[package]] name = "impl-serde" version = "0.4.0" @@ -3169,6 +3411,32 @@ dependencies = [ "sp-runtime 7.0.0", ] +[[package]] +name = "ismp-evm" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "hex-literal 0.4.1", + "ismp", + "ismp-primitives", + "ismp-testsuite", + "pallet-balances", + "pallet-evm", + "pallet-ismp", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", +] + [[package]] name = "ismp-grandpa" version = "0.1.0" @@ -3246,7 +3514,6 @@ dependencies = [ "finality-grandpa", "frame-support", "futures", - "hash-db 0.16.0", "hex", "hex-literal 0.3.4", "ismp", @@ -3261,10 +3528,10 @@ dependencies = [ "sp-core 7.0.0", "sp-io 7.0.0", "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", "sp-std 5.0.0", "sp-storage 7.0.0", "sp-trie 7.0.0", + "substrate-state-machine", "subxt", "tokio", ] @@ -3273,12 +3540,10 @@ dependencies = [ name = "ismp-parachain" version = "0.1.0" dependencies = [ - "ckb-merkle-mountain-range", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "frame-support", "frame-system", - "hash-db 0.16.0", "hex-literal 0.4.1", "ismp", "ismp-primitives", @@ -3393,9 +3658,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "jobserver" @@ -3567,7 +3832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.8", + "ecdsa 0.16.7", "elliptic-curve 0.13.5", "once_cell", "sha2 0.10.7", @@ -4173,9 +4438,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "pkg-config", @@ -4794,11 +5059,12 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm 0.2.7", ] [[package]] @@ -4974,6 +5240,31 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#1a718546085be20ce381b70e1f9e4c8b4d4b1f03" +dependencies = [ + "environmental", + "evm", + "fp-account", + "fp-evm", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal 0.4.1", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", +] + [[package]] name = "pallet-ismp" version = "0.1.0" @@ -5020,9 +5311,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f19d20a0d2cc52327a88d131fa1c4ea81ea4a04714aedcfeca2dd410049cf8" +checksum = "0dab3ac198341b2f0fec6e7f8a6eeed07a41201d98a124260611598c142e76df" dependencies = [ "blake2", "crc32fast", @@ -5040,9 +5331,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.4" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -5055,9 +5346,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.4" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -5133,9 +5424,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" [[package]] name = "pbkdf2" @@ -5212,7 +5503,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -5562,6 +5853,7 @@ checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ "fixed-hash", "impl-codec", + "impl-rlp", "impl-serde", "scale-info", "uint", @@ -5625,14 +5917,14 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] @@ -5674,6 +5966,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax 0.6.29", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "prost" version = "0.11.9" @@ -5778,9 +6090,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31999cfc7927c4e212e60fd50934ab40e8e8bfd2d493d6095d2d306bc0764d9" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" dependencies = [ "bytes", "rand 0.8.5", @@ -5796,9 +6108,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -5889,6 +6201,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -5973,22 +6294,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.19" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" +checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.19" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" +checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -6105,6 +6426,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rocksdb" version = "0.20.1" @@ -6176,6 +6519,19 @@ dependencies = [ "webrtc-util", ] +[[package]] +name = "ruint" +version = "1.9.0" +source = "git+https://github.com/recmo/uint.git?rev=e34200e114ac8fe93cf8e815e92601860ae4d7b8#e34200e114ac8fe93cf8e815e92601860ae4d7b8" +dependencies = [ + "ruint-macro", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "git+https://github.com/recmo/uint.git?rev=e34200e114ac8fe93cf8e815e92601860ae4d7b8#e34200e114ac8fe93cf8e815e92601860ae4d7b8" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -6301,9 +6657,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" + +[[package]] +name = "rusty-fork" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] [[package]] name = "rw-stream-sink" @@ -6318,15 +6686,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "safe_arch" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" dependencies = [ "bytemuck", ] @@ -6457,7 +6825,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -7650,7 +8018,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -7904,9 +8272,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sct" @@ -7956,9 +8324,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ "base16ct 0.2.0", "der 0.7.7", @@ -7997,9 +8365,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -8010,9 +8378,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -8020,35 +8388,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.177" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.177" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" dependencies = [ "itoa", "ryu", @@ -8209,6 +8577,15 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + [[package]] name = "snap" version = "1.1.0" @@ -8319,7 +8696,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -8842,7 +9219,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing 5.0.0", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -8883,7 +9260,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -8905,7 +9282,7 @@ checksum = "c7f531814d2f16995144c74428830ccf7d94ff4a7749632b83ad8199b181140c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -9135,7 +9512,7 @@ version = "4.1.0-dev" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "thiserror", - "zstd 0.12.4", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] @@ -9385,7 +9762,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -9411,7 +9788,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -9781,7 +10158,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -10074,6 +10451,7 @@ dependencies = [ "ckb-merkle-mountain-range", "frame-support", "frame-system", + "hash-db 0.16.0", "ismp", "ismp-primitives", "pallet-ismp", @@ -10150,7 +10528,7 @@ dependencies = [ "quote", "scale-info", "subxt-metadata", - "syn 2.0.27", + "syn 2.0.25", "thiserror", "tokio", ] @@ -10164,7 +10542,7 @@ dependencies = [ "darling 0.20.3", "proc-macro-error", "subxt-codegen", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -10193,15 +10571,26 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008a3d3dd194a54c0577395903600f51c5bcbf23d95b0243805caa7003dacfcf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -10243,20 +10632,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.10" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e" +checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" [[package]] name = "tempfile" -version = "3.7.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", - "fastrand 2.0.0", + "fastrand", "redox_syscall 0.3.5", - "rustix 0.38.4", + "rustix 0.37.23", "windows-sys 0.48.0", ] @@ -10277,22 +10667,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -10329,9 +10719,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-ctl" -version = "0.5.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619bfed27d807b54f7f776b9430d4f8060e66ee138a28632ca898584d462c31c" +checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" dependencies = [ "libc", "paste", @@ -10340,9 +10730,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.4+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", "libc", @@ -10405,6 +10795,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -10458,7 +10857,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -10516,9 +10915,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" dependencies = [ "indexmap 2.0.0", "toml_datetime", @@ -10587,7 +10986,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -10630,7 +11029,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -10721,6 +11120,16 @@ dependencies = [ "hash-db 0.16.0", ] +[[package]] +name = "triehash" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" +dependencies = [ + "hash-db 0.15.2", + "rlp", +] + [[package]] name = "trust-dns-proto" version = "0.22.0" @@ -10828,6 +11237,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -10836,9 +11251,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-normalization" @@ -10912,9 +11327,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" dependencies = [ "getrandom 0.2.10", ] @@ -10943,6 +11358,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "waitgroup" version = "0.1.2" @@ -11006,7 +11430,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", "wasm-bindgen-shared", ] @@ -11040,7 +11464,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -11947,9 +12371,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.1" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" +checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" dependencies = [ "memchr", ] @@ -12056,7 +12480,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -12105,7 +12529,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -12119,11 +12543,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.4" +version = "0.12.3+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" dependencies = [ - "zstd-safe 6.0.6", + "zstd-safe 6.0.5+zstd.1.5.4", ] [[package]] @@ -12138,9 +12562,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.6" +version = "6.0.5+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" dependencies = [ "libc", "zstd-sys", diff --git a/Cargo.toml b/Cargo.toml index 24aadecc3..b3ba9ab50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "pallet-ismp/runtime-api", "pallet-ismp/primitives", "pallet-ismp/primitives/state-machine", + "pallet-ismp/evm", "pallet-ismp", "parachain/inherent", "parachain/runtime-api", @@ -18,3 +19,7 @@ members = [ [workspace.dependencies] enum-as-inner = "=0.5.1" + +#todo: remove on alloy v0.4.0 release +[patch.crates-io] +ruint = { git = "https://github.com/recmo/uint.git", rev = "e34200e114ac8fe93cf8e815e92601860ae4d7b8" } diff --git a/grandpa/verifier/Cargo.toml b/grandpa/verifier/Cargo.toml index a7f3c7001..e6bfee18c 100644 --- a/grandpa/verifier/Cargo.toml +++ b/grandpa/verifier/Cargo.toml @@ -10,7 +10,6 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } anyhow = { version = "1.0.64", default-features = false } finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } -hash-db = { version = "0.16.0", default-features = false } serde = { version = "1.0.144", default-features = false, features = ["derive"] } derive_more = { version = "0.99.17", default-features = false, features = ["from"] } @@ -20,12 +19,12 @@ frame-support = { git = "https://github.com/paritytech/substrate", branch = "pol sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } primitives = { package = "ismp-grandpa-primitives", path = "../primitives", default-features = false } +substrate-state-machine = { path = "../../pallet-ismp/primitives/state-machine", default-features = false } [dev-dependencies] polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.42" } @@ -48,15 +47,14 @@ std = [ "codec/std", "anyhow/std", "finality-grandpa/std", - "hash-db/std", "frame-support/std", "sp-runtime/std", "sp-std/std", "sp-trie/std", "sp-consensus-grandpa/std", - "sp-state-machine/std", "sp-io/std", "primitives/std", "serde/std", "sp-storage/std", + "substrate-state-machine/std" ] diff --git a/grandpa/verifier/src/lib.rs b/grandpa/verifier/src/lib.rs index 28da555a7..b9a40f2f8 100644 --- a/grandpa/verifier/src/lib.rs +++ b/grandpa/verifier/src/lib.rs @@ -19,7 +19,6 @@ #![allow(clippy::all)] #![deny(missing_docs)] -mod state_machine; #[cfg(test)] mod tests; @@ -37,6 +36,7 @@ use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, Header}; use sp_std::prelude::*; use sp_trie::StorageProof; +use substrate_state_machine::read_proof_check; /// This function verifies the GRANDPA finality proof for both standalone chain and parachain /// headers. @@ -146,7 +146,7 @@ where let proof = StorageProof::new(state_proof); // verify patricia-merkle state proofs - let mut result = state_machine::read_proof_check::( + let mut result = read_proof_check::( relay_chain_header.state_root(), proof, keys.keys().map(|key| key.as_slice()), diff --git a/grandpa/verifier/src/state_machine.rs b/grandpa/verifier/src/state_machine.rs deleted file mode 100644 index e85c2bf2a..000000000 --- a/grandpa/verifier/src/state_machine.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! State verification functions - -use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}; -use codec::Decode; -use core::fmt::Debug; -use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use sp_core::H256; -use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; - -#[derive(Debug, derive_more::From, derive_more::Display)] -pub enum Error -where - H: Hasher, - H::Out: Debug, -{ - #[display(fmt = "Trie Error: {:?}", _0)] - Trie(Box>>), - #[display(fmt = "Error verifying key: {key:?}, Expected: {expected:?}, Got: {got:?}")] - ValueMismatch { key: Option, expected: Option>, got: Option> }, - #[display(fmt = "Invalid Proof")] - InvalidProof, -} - -/// Lifted directly from [`sp_state_machine::read_proof_check`](https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1075-L1094) -pub fn read_proof_check( - root: &H::Out, - proof: StorageProof, - keys: I, -) -> Result, Option>>, Error> -where - H: Hasher, - H::Out: Debug, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let db = proof.into_memory_db(); - - if !db.contains(root, EMPTY_PREFIX) { - Err(Error::InvalidProof)? - } - - let trie = TrieDBBuilder::>::new(&db, root).build(); - let mut result = BTreeMap::new(); - - for key in keys.into_iter() { - let value = trie.get(key.as_ref())?.and_then(|val| Decode::decode(&mut &val[..]).ok()); - result.insert(key.as_ref().to_vec(), value); - } - - Ok(result) -} diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index 113ed0c6f..21e2fe070 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -138,8 +138,8 @@ pub mod pallet { }; let post = DispatchPost { dest, - from: PALLET_ID.encode(), - to: PALLET_ID.encode(), + from: PALLET_ID.to_bytes(), + to: PALLET_ID.to_bytes(), timeout_timestamp: params.timeout, data: payload.encode(), gas_limit: 0, @@ -176,7 +176,7 @@ pub mod pallet { let get = DispatchGet { dest, - from: PALLET_ID.encode(), + from: PALLET_ID.to_bytes(), keys: params.keys, height: params.height as u64, timeout_timestamp: params.timeout, diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index 5a9a6c9e2..c326650e5 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -53,9 +53,11 @@ std = [ "mmr-lib/std", "sp-api/std", "serde", - "ismp-primitives/std", + "ismp-primitives/std" ] +testing = ["pallet-timestamp/std"] + runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", diff --git a/pallet-ismp/evm/Cargo.toml b/pallet-ismp/evm/Cargo.toml new file mode 100644 index 000000000..d43aabe09 --- /dev/null +++ b/pallet-ismp/evm/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "ismp-evm" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +# EVM support +pallet-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } +fp-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } +alloy-sol-types = { version = "0.3.0", default-features = false } +alloy-primitives = { version = "0.3.0", default-features = false } + + + # polytope labs +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +# crates.io +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +hex-literal = "0.4.1" + +# local +ismp-primitives = { path = "../primitives", default-features = false } +pallet-ismp = { path = "..", default-features = false } + +[dev-dependencies] +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +ismp-testsuite = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } +pallet-ismp = { path = "..", features = ["testing"] } +hex = "0.4.3" +scale-info = { version = "2.1.1", features = ["derive"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", + "ismp-rs/std", + "ismp-primitives/std", + "pallet-evm/std", + "fp-evm/std", + "alloy-primitives/std", + "alloy-sol-types/std", + "pallet-ismp/std" +] + diff --git a/pallet-ismp/evm/solidity/IsmpDemo.bin b/pallet-ismp/evm/solidity/IsmpDemo.bin new file mode 100644 index 000000000..061bdc0cc --- /dev/null +++ b/pallet-ismp/evm/solidity/IsmpDemo.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50600080546001600160401b031916633b9aca0017905561153d806100366000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c80634e87ba19116100665780634e87ba191461011857806393d756aa1461012b578063c52c28af1461013e578063c715f52b14610151578063f370fdbb1461016457600080fd5b806320fe202a1461009857806327e235e3146100ad5780632a9a1bc6146100f25780634c46c03514610105575b600080fd5b6100ab6100a6366004610b3a565b610177565b005b6100d66100bb366004610bc3565b6001602052600090815260409020546001600160401b031681565b6040516001600160401b03909116815260200160405180910390f35b6100ab610100366004610c99565b610277565b6100ab610113366004610df4565b6102e4565b6100ab610126366004610f36565b6103ac565b6100ab610139366004610f6a565b61048a565b6100ab61014c366004610fa3565b6104cc565b6100ab61015f366004610f36565b610543565b6100ab610172366004611033565b6105c7565b6101813384610693565b60408051606080820183526001600160a01b0388811683523360208085019182526001600160401b03898116868801908152875160a0810189528c81528851600360621b818601528951601481830301815260349091018a528185015293518751915189519187169482019490945294168488015216928201929092529192600092908201906080016040516020818303038152906040528152602001856001600160401b03168152602001846001600160401b0316815250905061024581610725565b6040517fb56b29fc7f72737821b2cf8a996ecebbea0d83daada768f5564a873f402cdd2190600090a150505050505050565b6040805160a0810182528681526001600160401b038086166020830152918101869052838216606082015290821660808201526102b381610823565b6040517f51a298a0eb173998598862ac7e847cb267f553d502347aec82bf8b81528dd7aa90600090a1505050505050565b3373843b131bd76419934dae248f6e5a195c0a3c324d14610318576040516351ab8de560e01b815260040160405180910390fd5b60005b8160a001515181101561037f5760008260a00151828151811061034057610340611190565b60200260200101519050805160000361036c57604051632b3f6d1160e21b815260040160405180910390fd5b5080610377816111bc565b91505061031b565b506040517f7265059b18e944f453138c2649bb3f8dbc4e555e5c0605b0ffb419cf8ae8c1ba90600090a150565b3373843b131bd76419934dae248f6e5a195c0a3c324d146103e0576040516351ab8de560e01b815260040160405180910390fd5b60006103ef8260c00151610915565b9050600060405180604001604052808481526020013360405160200161042d919060609190911b6bffffffffffffffffffffffff1916815260140190565b60405160208183030381529060405281525090506104538260000151836040015161094a565b61045c816109a0565b6040517f6cc69d45d4c21d1cb43e28df929c90259b7ad8d41e32f02f47ccda71aeb046ab90600090a1505050565b3373843b131bd76419934dae248f6e5a195c0a3c324d146104be576040516351ab8de560e01b815260040160405180910390fd5b6104c8828261094a565b5050565b3373843b131bd76419934dae248f6e5a195c0a3c324d14610500576040516351ab8de560e01b815260040160405180910390fd5b6000610513826000015160c00151610915565b6040519091507f2d87323053ce5d424f6bb99d64894f647db556f19f4319ee6bf817bf23bf520790600090a15050565b3373843b131bd76419934dae248f6e5a195c0a3c324d14610577576040516351ab8de560e01b815260040160405180910390fd5b60006105868260c00151610915565b905061059a8160200151826040015161094a565b6040517f6cc69d45d4c21d1cb43e28df929c90259b7ad8d41e32f02f47ccda71aeb046ab90600090a15050565b3373843b131bd76419934dae248f6e5a195c0a3c324d146105fb576040516351ab8de560e01b815260040160405180910390fd5b60005b8160200151518110156106665760008260200151828151811061062357610623611190565b6020026020010151905080602001515160000361065357604051632b3f6d1160e21b815260040160405180910390fd5b508061065e816111bc565b9150506105fe565b506040517f2d87323053ce5d424f6bb99d64894f647db556f19f4319ee6bf817bf23bf520790600090a150565b6000546106aa9082906001600160401b03166111d5565b6000805467ffffffffffffffff19166001600160401b039283161781556001600160a01b0384168152600160205260409020546106e9918391166111d5565b6001600160a01b03929092166000908152600160205260409020805467ffffffffffffffff19166001600160401b039093169290921790915550565b805160208083015160408085015160608601516080870151925160009661075096909594910161124c565b604051602081830303815290604052905060008073222a98a2832ae77e72a768bf5be1f82d8959f4ec6001600160a01b03168360405161079091906112ab565b600060405180830381855afa9150503d80600081146107cb576040519150601f19603f3d011682016040523d82523d6000602084013e6107d0565b606091505b50915091508161081d5760405162461bcd60e51b8152602060048201526013602482015272111a5cdc185d18da141bdcdd0819985a5b1959606a1b60448201526064015b60405180910390fd5b50505050565b805160208083015160408085015160608601516080870151925160009661084e9690959491016112c7565b604051602081830303815290604052905060008073f2d8dc5239ddc053ba5151302483fc48d7e24e606001600160a01b03168360405161088e91906112ab565b600060405180830381855afa9150503d80600081146108c9576040519150601f19603f3d011682016040523d82523d6000602084013e6108ce565b606091505b50915091508161081d5760405162461bcd60e51b8152602060048201526012602482015271111a5cdc185d18da11d95d0819985a5b195960721b6044820152606401610814565b60408051606081018252600080825260208083018290529282015282519091610944918401810190840161136f565b92915050565b6000546109619082906001600160401b03166113e0565b6000805467ffffffffffffffff19166001600160401b039283161781556001600160a01b0384168152600160205260409020546106e9918391166113e0565b6000816000015182602001516040516020016109bd929190611400565b604051602081830303815290604052905060008073eb928e2de75cb5ab60abe75f539c5312aeb46f386001600160a01b03168360405161079091906112ab565b6001600160a01b0381168114610a1257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b0381118282101715610a4e57610a4e610a15565b60405290565b604080519081016001600160401b0381118282101715610a4e57610a4e610a15565b604051601f8201601f191681016001600160401b0381118282101715610a9e57610a9e610a15565b604052919050565b600082601f830112610ab757600080fd5b81356001600160401b03811115610ad057610ad0610a15565b610ae3601f8201601f1916602001610a76565b818152846020838601011115610af857600080fd5b816020850160208301376000918101602001919091529392505050565b6001600160401b0381168114610a1257600080fd5b8035610b3581610b15565b919050565b600080600080600060a08688031215610b5257600080fd5b8535610b5d816109fd565b945060208601356001600160401b03811115610b7857600080fd5b610b8488828901610aa6565b9450506040860135610b9581610b15565b92506060860135610ba581610b15565b91506080860135610bb581610b15565b809150509295509295909350565b600060208284031215610bd557600080fd5b8135610be0816109fd565b9392505050565b60006001600160401b03821115610c0057610c00610a15565b5060051b60200190565b600082601f830112610c1b57600080fd5b81356020610c30610c2b83610be7565b610a76565b82815260059290921b84018101918181019086841115610c4f57600080fd5b8286015b84811015610c8e5780356001600160401b03811115610c725760008081fd5b610c808986838b0101610aa6565b845250918301918301610c53565b509695505050505050565b600080600080600060a08688031215610cb157600080fd5b85356001600160401b0380821115610cc857600080fd5b610cd489838a01610aa6565b96506020880135915080821115610cea57600080fd5b50610b8488828901610c0a565b60006101008284031215610d0a57600080fd5b610d12610a2b565b905081356001600160401b0380821115610d2b57600080fd5b610d3785838601610aa6565b83526020840135915080821115610d4d57600080fd5b610d5985838601610aa6565b6020840152610d6a60408501610b2a565b60408401526060840135915080821115610d8357600080fd5b610d8f85838601610aa6565b6060840152610da060808501610b2a565b608084015260a0840135915080821115610db957600080fd5b50610dc684828501610c0a565b60a083015250610dd860c08301610b2a565b60c0820152610de960e08301610b2a565b60e082015292915050565b600060208284031215610e0657600080fd5b81356001600160401b03811115610e1c57600080fd5b610e2884828501610cf7565b949350505050565b60006101008284031215610e4357600080fd5b610e4b610a2b565b905081356001600160401b0380821115610e6457600080fd5b610e7085838601610aa6565b83526020840135915080821115610e8657600080fd5b610e9285838601610aa6565b6020840152610ea360408501610b2a565b60408401526060840135915080821115610ebc57600080fd5b610ec885838601610aa6565b60608401526080840135915080821115610ee157600080fd5b610eed85838601610aa6565b6080840152610efe60a08501610b2a565b60a084015260c0840135915080821115610f1757600080fd5b50610f2484828501610aa6565b60c083015250610de960e08301610b2a565b600060208284031215610f4857600080fd5b81356001600160401b03811115610f5e57600080fd5b610e2884828501610e30565b60008060408385031215610f7d57600080fd5b8235610f88816109fd565b91506020830135610f9881610b15565b809150509250929050565b600060208284031215610fb557600080fd5b81356001600160401b0380821115610fcc57600080fd5b9083019060408286031215610fe057600080fd5b610fe8610a54565b823582811115610ff757600080fd5b61100387828601610e30565b82525060208301358281111561101857600080fd5b61102487828601610aa6565b60208301525095945050505050565b6000602080838503121561104657600080fd5b82356001600160401b038082111561105d57600080fd5b8185019150604080838803121561107357600080fd5b61107b610a54565b83358381111561108a57600080fd5b61109689828701610cf7565b82525084840135838111156110aa57600080fd5b80850194505087601f8501126110bf57600080fd5b83356110cd610c2b82610be7565b81815260059190911b8501860190868101908a8311156110ec57600080fd5b8787015b8381101561117c578035878111156111085760008081fd5b8801808d03601f190187131561111e5760008081fd5b611126610a54565b8a820135898111156111385760008081fd5b6111468f8d83860101610aa6565b825250878201358981111561115b5760008081fd5b6111698f8d83860101610aa6565b828d0152508452509188019188016110f0565b509683019690965250979650505050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016111ce576111ce6111a6565b5060010190565b6001600160401b038281168282160390808211156111f5576111f56111a6565b5092915050565b60005b838110156112175781810151838201526020016111ff565b50506000910152565b600081518084526112388160208601602086016111fc565b601f01601f19169290920160200192915050565b60a08152600061125f60a0830188611220565b82810360208401526112718188611220565b905082810360408401526112858187611220565b9150506001600160401b0380851660608401528084166080840152509695505050505050565b600082516112bd8184602087016111fc565b9190910192915050565b60a0815260006112da60a0830188611220565b60206001600160401b0388168185015283820360408501528187518084528284019150828160051b850101838a0160005b8381101561133957601f19878403018552611327838351611220565b9486019492509085019060010161130b565b50506001600160401b038916606088015294506113569350505050565b6001600160401b03831660808301529695505050505050565b60006060828403121561138157600080fd5b604051606081018181106001600160401b03821117156113a3576113a3610a15565b60405282516113b1816109fd565b815260208301516113c1816109fd565b602082015260408301516113d481610b15565b60408201529392505050565b6001600160401b038181168382160190808211156111f5576111f56111a6565b604081526000835161010080604085015261141f610140850183611220565b91506020860151603f198086850301606087015261143d8483611220565b93506040880151915061145b60808701836001600160401b03169052565b60608801519150808685030160a08701526114768483611220565b935060808801519150808685030160c08701526114938483611220565b935060a088015191506114b160e08701836001600160401b03169052565b60c08801519150808685030183870152506114cc8382611220565b9250505060e08501516114eb6101208501826001600160401b03169052565b5082810360208401526114fe8185611220565b9594505050505056fea26469706673582212208967a70afc3a4559f27a0324033b5443642a3061a7ed50b79e4fddb2645a434664736f6c63430008110033 \ No newline at end of file diff --git a/pallet-ismp/evm/solidity/foundry.toml b/pallet-ismp/evm/solidity/foundry.toml new file mode 100644 index 000000000..e69de29bb diff --git a/pallet-ismp/evm/solidity/lib/ismp-solidity b/pallet-ismp/evm/solidity/lib/ismp-solidity new file mode 160000 index 000000000..85510c619 --- /dev/null +++ b/pallet-ismp/evm/solidity/lib/ismp-solidity @@ -0,0 +1 @@ +Subproject commit 85510c619074a4f61aa0ae0ee40bbb92f4feaac6 diff --git a/pallet-ismp/evm/solidity/remappings.txt b/pallet-ismp/evm/solidity/remappings.txt new file mode 100644 index 000000000..4fec4129e --- /dev/null +++ b/pallet-ismp/evm/solidity/remappings.txt @@ -0,0 +1,3 @@ +openzeppelin/=lib/ismp-solidity/lib/openzeppelin-contracts/contracts/ +solidity-merkle-trees/=lib/ismp-solidity/lib/solidity-merkle-trees/src/ +ismp-solidity/=lib/ismp-solidity/src \ No newline at end of file diff --git a/pallet-ismp/evm/solidity/src/example.sol b/pallet-ismp/evm/solidity/src/example.sol new file mode 100644 index 000000000..407e9ee23 --- /dev/null +++ b/pallet-ismp/evm/solidity/src/example.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: UNLICENSED +// A Sample ISMP solidity contract for unit tests + +pragma solidity ^0.8.2; + +import "ismp-solidity/SubstrateHost.sol"; +import "ismp-solidity/interfaces/IIsmpDispatcher.sol"; +import "solidity-merkle-trees/MerklePatricia.sol"; + +address constant HOST = 0x843b131BD76419934dae248F6e5a195c0A3C324D; + +error NotIsmpHost(); +error ExecutionFailed(); + +struct Payload { + address to; + address from; + uint64 amount; +} + +contract IsmpDemo is IIsmpModule { + using SubstrateHost for *; + uint64 totalSupply; + + // Mapping of user address to balance + mapping(address => uint64) public balances; + event ResponseReceived(); + event TimeoutReceived(); + event BalanceMinted(); + event BalanceBurnt(); + event GetDispatched(); + + // restricts call to `IsmpHost` + modifier onlyIsmpHost() { + if (msg.sender != HOST) { + revert NotIsmpHost(); + } + _; + } + + constructor() { + totalSupply = 1000000000; + } + + function onAccept(PostRequest memory request) public onlyIsmpHost { + Payload memory payload = decodePayload(request.body); + PostResponse memory response = PostResponse({ + request: request, + response: abi.encodePacked(msg.sender) + }); + _mint(payload.to, payload.amount); + SubstrateHost.dispatch(response); + emit BalanceMinted(); + + } + + function onPostResponse(PostResponse memory response) public onlyIsmpHost { + // In this callback just try to decode the payload of the corresponding request + Payload memory payload = decodePayload(response.request.body); + emit ResponseReceived(); + } + + function onGetResponse(GetResponse memory response) public onlyIsmpHost { + // For the purpose of this test + // we just validate the responses in this callback + for (uint256 index = 0; index < response.values.length; index++) { + StorageValue memory storageValue = response.values[index]; + if (storageValue.value.length == 0) { + revert ExecutionFailed(); + } + } + emit ResponseReceived(); + } + + function onGetTimeout(GetRequest memory request) public onlyIsmpHost { + // We validate the keys in this callback + for (uint256 index = 0; index < request.keys.length; index++) { + bytes memory key = request.keys[index]; + // No keys should be empty + if (key.length == 0) { + revert ExecutionFailed(); + } + } + emit TimeoutReceived(); + } + + function onPostTimeout(PostRequest memory request) public onlyIsmpHost { + Payload memory payload = decodePayload(request.body); + _mint(payload.from, payload.amount); + emit BalanceMinted(); + } + + function decodePayload( + bytes memory data + ) internal pure returns (Payload memory payload) { + (payload) = abi.decode(data, (Payload)); + return payload; + } + + function transfer( + address to, + bytes memory dest, + uint64 amount, + uint64 timeout, + uint64 gasLimit + ) public { + _burn(msg.sender, amount); + Payload memory payload = Payload({ + from: msg.sender, + to: to, + amount: amount + }); + DispatchPost memory dispatchPost = DispatchPost({ + body: abi.encode(payload.from, payload.to, payload.amount), + dest: dest, + timeoutTimestamp: timeout, + to: abi.encodePacked(address(12)), + gaslimit: gasLimit + }); + SubstrateHost.dispatch(dispatchPost); + emit BalanceBurnt(); + } + + function dispatchGet( + bytes memory dest, + bytes[] memory keys, + uint64 height, + uint64 timeout, + uint64 gasLimit + ) public { + DispatchGet memory get = DispatchGet({ + keys: keys, + dest: dest, + height: height, + timeoutTimestamp: timeout, + gaslimit: gasLimit + }); + SubstrateHost.dispatch(get); + emit GetDispatched(); + + } + + function mintTo(address who, uint64 amount) public onlyIsmpHost { + _mint(who, amount); + } + + function _mint(address who, uint64 amount) internal { + totalSupply = totalSupply + amount; + balances[who] = balances[who] + amount; + } + + function _burn(address who, uint64 amount) internal { + totalSupply = totalSupply - amount; + balances[who] = balances[who] - amount; + } +} diff --git a/pallet-ismp/evm/src/abi.rs b/pallet-ismp/evm/src/abi.rs new file mode 100644 index 000000000..0fb7e51d3 --- /dev/null +++ b/pallet-ismp/evm/src/abi.rs @@ -0,0 +1,99 @@ +//! Solidity rust bindings +#![allow(missing_docs)] +use alloy_sol_types::sol; +use sp_std::prelude::*; + +sol! { + +struct PostRequest { + // the source state machine of this request + bytes source; + // the destination state machine of this request + bytes dest; + // request nonce + uint64 nonce; + // Module Id of this request origin + bytes from; + // destination module id + bytes to; + // timestamp by which this request times out. + uint64 timeoutTimestamp; + // request body + bytes body; + // gas limit for executing this request on destination & its response (if any) on the source. + uint64 gaslimit; +} + +struct GetRequest { + // the source state machine of this request + bytes source; + // the destination state machine of this request + bytes dest; + // request nonce + uint64 nonce; + // Module Id of this request origin + bytes from; + // timestamp by which this request times out. + uint64 timeoutTimestamp; + // Storage keys to read. + bytes[] keys; + // height at which to read destination state machine + uint64 height; + // gas limit for executing this request on destination & its response (if any) on the source. + uint64 gaslimit; +} + +struct StorageValue { + bytes key; + bytes value; +} + +struct GetResponse { + // The request that initiated this response + GetRequest request; + // storage values for get response + StorageValue[] values; +} + +struct PostResponse { + // The request that initiated this response + PostRequest request; + // bytes for post response + bytes response; +} + +// An object for dispatching post requests to the IsmpDispatcher +struct DispatchPost { + // bytes representation of the destination chain + bytes dest; + // the destination module + bytes to; + // the request body + bytes body; + // the timestamp at which this request should timeout + uint64 timeoutTimestamp; + // gas limit for executing this request on destination & its response (if any) on the source. + uint64 gaslimit; +} + +// An object for dispatching get requests to the IsmpDispatcher +struct DispatchGet { + // bytes representation of the destination chain + bytes dest; + // height at which to read the state machine + uint64 height; + // Storage keys to read + bytes[] keys; + // the timestamp at which this request should timeout + uint64 timeoutTimestamp; + // gas limit for executing this request on destination & its response (if any) on the source. + uint64 gaslimit; +} + + +function onAccept(PostRequest memory request) external; +function onPostResponse(PostResponse memory response) external; +function onGetResponse(GetResponse memory response) external; +function onPostTimeout(PostRequest memory request) external; +function onGetTimeout(GetRequest memory request) external; +} diff --git a/pallet-ismp/evm/src/lib.rs b/pallet-ismp/evm/src/lib.rs new file mode 100644 index 000000000..288c4b2d1 --- /dev/null +++ b/pallet-ismp/evm/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the [`IsmpModule`] for solidity contracts on substrate + +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] + +extern crate alloc; + +pub mod abi; +#[cfg(test)] +mod mocks; +pub mod module; +pub mod precompiles; +#[cfg(test)] +mod tests; +pub mod weight; diff --git a/pallet-ismp/evm/src/mocks.rs b/pallet-ismp/evm/src/mocks.rs new file mode 100644 index 000000000..0b32f17e9 --- /dev/null +++ b/pallet-ismp/evm/src/mocks.rs @@ -0,0 +1,244 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::str::FromStr; +use fp_evm::{ + FeeCalculator, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, + PrecompileSet, +}; +use frame_support::{dispatch::Weight, parameter_types}; + +use frame_support::traits::{ConstU32, ConstU64, FindAuthor, Get}; +use frame_system::EnsureRoot; +use ismp_rs::{ + consensus::{ConsensusClient, ConsensusClientId}, + error::Error, + host::StateMachine, + module::IsmpModule, + router::IsmpRouter, +}; +use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, IdentityAddressMapping}; + +use crate::{ + module::EvmIsmpModule, + precompiles::{ + IsmpGetDispatcher, IsmpPostDispatcher, IsmpResponseDispatcher, GET_REQUEST_DISPATCHER, + POST_REQUEST_DISPATCHER, POST_RESPONSE_DISPATCHER, + }, +}; +use pallet_ismp::{ + mocks::ismp::MockConsensusClient, + primitives::{ConsensusClientProvider, ModuleId}, +}; +use sp_core::{H160, H256, U256}; +use sp_runtime::{ + testing::Header, + traits::{IdentityLookup, Keccak256}, + ConsensusEngineId, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( +pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, + EVM: pallet_evm::{Pallet, Call, Storage, Config, Event}, + } +); + +pub struct StateMachineProvider; + +impl Get for StateMachineProvider { + fn get() -> StateMachine { + StateMachine::Kusama(100) + } +} + +pub struct ConsensusProvider; + +impl ConsensusClientProvider for ConsensusProvider { + fn consensus_client(_id: ConsensusClientId) -> Result, Error> { + Ok(Box::new(MockConsensusClient)) + } +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = Keccak256; + type AccountId = H160; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1000; +} +impl pallet_balances::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Balance = u64; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxLocks = (); + type MaxReserves = (); + type MaxHolds = (); + type MaxFreezes = (); +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +impl pallet_ismp::Config for Test { + type RuntimeEvent = RuntimeEvent; + const INDEXING_PREFIX: &'static [u8] = b"ISMP"; + type AdminOrigin = EnsureRoot; + type StateMachine = StateMachineProvider; + type TimeProvider = Timestamp; + type IsmpRouter = ModuleRouter; + type ConsensusClientProvider = ConsensusProvider; + type WeightInfo = (); + type WeightProvider = (); +} + +#[derive(Default)] +pub struct ModuleRouter; + +impl IsmpRouter for ModuleRouter { + fn module_for_id(&self, bytes: Vec) -> Result, Error> { + let module_id = ModuleId::from_bytes(&bytes).unwrap(); + match module_id { + ModuleId::Evm(_) => Ok(Box::new(EvmIsmpModule::::default())), + _ => Err(Error::ImplementationSpecific("Module handler not found".to_string())), + } + } +} + +pub struct FixedGasPrice; +impl FeeCalculator for FixedGasPrice { + fn min_gas_price() -> (U256, Weight) { + // Return some meaningful gas price and weight + (10u128.into(), Weight::from_parts(7u64, 0)) + } +} + +pub struct FindAuthorTruncated; +impl FindAuthor for FindAuthorTruncated { + fn find_author<'a, I>(_digests: I) -> Option + where + I: 'a + IntoIterator, + { + Some(H160::from_str("1234500000000000000000000000000000000000").unwrap()) + } +} +const BLOCK_GAS_LIMIT: u64 = 1_500_000_000; +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); + pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); + pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); + pub MockPrecompiles: MockPrecompileSet = MockPrecompileSet; +} +impl pallet_evm::Config for Test { + type FeeCalculator = FixedGasPrice; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + + type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; + type Currency = Balances; + + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = MockPrecompileSet; + type PrecompilesValue = MockPrecompiles; + type ChainId = (); + type BlockGasLimit = BlockGasLimit; + type Runner = pallet_evm::runner::stack::Runner; + type OnChargeTransaction = (); + type OnCreate = (); + type FindAuthor = FindAuthorTruncated; + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type Timestamp = Timestamp; + type WeightInfo = (); +} +/// Example PrecompileSet with only Identity precompile. +pub struct MockPrecompileSet; + +impl PrecompileSet for MockPrecompileSet { + /// Tries to execute a precompile in the precompile set. + /// If the provided address is not a precompile, returns None. + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + let address = handle.code_address(); + if address == POST_REQUEST_DISPATCHER { + return Some(IsmpPostDispatcher::::execute(handle)) + } else if address == GET_REQUEST_DISPATCHER { + return Some(IsmpGetDispatcher::::execute(handle)) + } else if address == POST_RESPONSE_DISPATCHER { + return Some(IsmpResponseDispatcher::::execute(handle)) + } + + None + } + + /// Check if the given address is a precompile. Should only be called to + /// perform the check while not executing the precompile afterward, since + /// `execute` already performs a check internally. + fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == POST_REQUEST_DISPATCHER || + address == GET_REQUEST_DISPATCHER || + address == POST_RESPONSE_DISPATCHER, + extra_cost: 0, + } + } +} diff --git a/pallet-ismp/evm/src/module.rs b/pallet-ismp/evm/src/module.rs new file mode 100644 index 000000000..40f38ce8b --- /dev/null +++ b/pallet-ismp/evm/src/module.rs @@ -0,0 +1,203 @@ +//! Module Handler for EVM contracts +use crate::abi::{ + onAcceptCall, onGetResponseCall, onGetTimeoutCall, onPostResponseCall, onPostTimeoutCall, + GetRequest as SolGetRequest, GetResponse as SolGetResponse, PostRequest, + PostResponse as SolPostResponse, StorageValue as SolStorageValue, +}; +use alloc::{format, string::ToString}; +use alloy_sol_types::SolCall; +use core::marker::PhantomData; +use fp_evm::{ExitReason, FeeCalculator}; +use hex_literal::hex; +use ismp_rs::{ + error::Error, + module::IsmpModule, + router::{Post, Request, Response}, +}; +use pallet_evm::GasWeightMapping; +use pallet_ismp::{primitives::ModuleId, WeightConsumed}; +use sp_core::H160; +use sp_std::prelude::*; + +/// Handler host address +/// Contracts should only allow ismp module callbacks to be executed by this address +pub const EVM_HOST_ADDRESS: [u8; 20] = hex!("843b131bd76419934dae248f6e5a195c0a3c324d"); + +/// [`IsmpModule`] implementation that routes requests & responses to EVM contracts. +pub struct EvmIsmpModule(PhantomData); + +impl Default for EvmIsmpModule { + fn default() -> Self { + Self(PhantomData) + } +} + +impl IsmpModule for EvmIsmpModule { + fn on_accept(&self, request: Post) -> Result<(), Error> { + let target_contract = parse_contract_id(&request.to)?; + let gaslimit = request.gas_limit; + let post = PostRequest { + source: request.source.to_string().as_bytes().to_vec(), + dest: request.dest.to_string().as_bytes().to_vec(), + nonce: request.nonce, + timeoutTimestamp: request.timeout_timestamp, + from: request.from, + to: request.to, + body: request.data, + gaslimit, + }; + let call_data = onAcceptCall { request: post }.encode(); + execute_call::(target_contract, call_data, gaslimit) + } + + fn on_response(&self, response: Response) -> Result<(), Error> { + let target_contract = parse_contract_id(&response.destination_module())?; + + let (call_data, gas_limit) = match response { + Response::Post(response) => { + // we set the gas limit for executing the contract to be the same as used in the + // request. we assume the request was dispatched with a gas limit + // that accounts for execution of the response on this source chain + let gaslimit = response.post.gas_limit; + let post_response = SolPostResponse { + request: PostRequest { + source: response.post.source.to_string().as_bytes().to_vec(), + dest: response.post.dest.to_string().as_bytes().to_vec(), + nonce: response.post.nonce, + timeoutTimestamp: response.post.timeout_timestamp, + from: response.post.from, + to: response.post.to, + body: response.post.data, + gaslimit, + }, + response: response.response, + }; + (onPostResponseCall { response: post_response }.encode(), gaslimit) + } + Response::Get(response) => { + let gaslimit = response.get.gas_limit; + let get_response = SolGetResponse { + request: SolGetRequest { + source: response.get.source.to_string().as_bytes().to_vec(), + dest: response.get.dest.to_string().as_bytes().to_vec(), + nonce: response.get.nonce, + height: response.get.height, + timeoutTimestamp: response.get.timeout_timestamp, + from: response.get.from, + keys: response.get.keys, + gaslimit, + }, + values: response + .values + .into_iter() + .map(|(key, value)| SolStorageValue { + key, + value: value.unwrap_or_default(), + }) + .collect(), + }; + (onGetResponseCall { response: get_response }.encode(), gaslimit) + } + }; + + execute_call::(target_contract, call_data, gas_limit) + } + + fn on_timeout(&self, request: Request) -> Result<(), Error> { + let target_contract = parse_contract_id(&request.source_module())?; + let (call_data, gas_limit) = match request { + Request::Post(post) => { + let gaslimit = post.gas_limit; + let request = PostRequest { + source: post.source.to_string().as_bytes().to_vec(), + dest: post.dest.to_string().as_bytes().to_vec(), + nonce: post.nonce, + timeoutTimestamp: post.timeout_timestamp, + from: post.from, + to: post.to, + body: post.data, + gaslimit, + }; + (onPostTimeoutCall { request }.encode(), gaslimit) + } + Request::Get(get) => { + let gaslimit = get.gas_limit; + let request = SolGetRequest { + source: get.source.to_string().as_bytes().to_vec(), + dest: get.dest.to_string().as_bytes().to_vec(), + nonce: get.nonce, + height: get.height, + timeoutTimestamp: get.timeout_timestamp, + from: get.from, + keys: get.keys, + gaslimit, + }; + (onGetTimeoutCall { request }.encode(), gaslimit) + } + }; + execute_call::(target_contract, call_data, gas_limit) + } +} + +/// Parse contract id from raw bytes +pub fn parse_contract_id(bytes: &[u8]) -> Result { + let module_id = + ModuleId::from_bytes(bytes).map_err(|e| Error::ImplementationSpecific(e.to_string()))?; + match module_id { + ModuleId::Evm(id) => Ok(id), + _ => Err(Error::ImplementationSpecific("Expected Evm contract id".to_string())), + } +} + +/// Call execute call data +fn execute_call( + target: H160, + call_data: Vec, + gas_limit: u64, +) -> Result<(), Error> { + let (weight_used, result) = + match <::Runner as pallet_evm::Runner>::call( + H160::from(EVM_HOST_ADDRESS), + target, + call_data, + Default::default(), + gas_limit, + Some(<::FeeCalculator as FeeCalculator>::min_gas_price().0), + Some(<::FeeCalculator as FeeCalculator>::min_gas_price().0), + None, + Default::default(), + true, + true, + None, + None, + ::config(), + ) { + Ok(info) => { + let weight = + T::GasWeightMapping::gas_to_weight(info.used_gas.standard.low_u64(), true); + let result = match info.exit_reason { + ExitReason::Succeed(_) => Ok(()), + _ => Err(Error::ImplementationSpecific( + "Contract call did not successfully execute".to_string(), + )), + }; + (weight, result) + } + Err(error) => { + let dispatch_error: sp_runtime::DispatchError = error.error.into(); + ( + error.weight, + Err(Error::ImplementationSpecific(format!( + "Contract call failed with error {:?}", + dispatch_error + ))), + ) + } + }; + let mut total_weight_used = WeightConsumed::::get(); + let weight_limit = T::GasWeightMapping::gas_to_weight(gas_limit, true); + total_weight_used.weight_used = total_weight_used.weight_used + weight_used; + total_weight_used.weight_limit = total_weight_used.weight_limit + weight_limit; + WeightConsumed::::put(total_weight_used); + result +} diff --git a/pallet-ismp/evm/src/precompiles.rs b/pallet-ismp/evm/src/precompiles.rs new file mode 100644 index 000000000..a745a5984 --- /dev/null +++ b/pallet-ismp/evm/src/precompiles.rs @@ -0,0 +1,176 @@ +//! IsmpDispatcher precompiles for pallet-evm + +use pallet_ismp::{dispatcher::Dispatcher, weight_info::WeightInfo}; + +use crate::abi::{ + DispatchGet as SolDispatchGet, DispatchPost as SolDispatchPost, PostResponse as SolPostResponse, +}; +use alloc::{format, str::FromStr, string::String}; +use alloy_sol_types::SolType; +use core::marker::PhantomData; +use fp_evm::{ + ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, +}; +use frame_support::traits::Get; +use hex_literal::hex; +use ismp_rs::{ + host::StateMachine, + router::{DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher, Post, PostResponse}, +}; +use pallet_evm::GasWeightMapping; +use sp_core::{H160, H256}; +use sp_std::prelude::*; + +/// Ismp Request Dispatcher precompile for evm contracts +pub struct IsmpPostDispatcher { + _marker: PhantomData, +} + +/// Address for the post request precompile +pub const POST_REQUEST_DISPATCHER: H160 = H160(hex!("222a98a2832ae77e72a768bf5be1f82d8959f4ec")); +/// Address for the post response precompile +pub const POST_RESPONSE_DISPATCHER: H160 = H160(hex!("eb928e2de75cb5ab60abe75f539c5312aeb46f38")); +/// Address for the get request precompile +pub const GET_REQUEST_DISPATCHER: H160 = H160(hex!("f2d8dc5239ddc053ba5151302483fc48d7e24e60")); + +impl Precompile for IsmpPostDispatcher +where + T: pallet_ismp::Config + pallet_evm::Config, + ::Hash: From, +{ + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let input = handle.input(); + let context = handle.context(); + let weight = ::WeightInfo::dispatch_post_request(); + + // The cost of a dispatch is the weight of calling the dispatcher plus an extra storage read + // and write + let cost = ::GasWeightMapping::weight_to_gas(weight); + + let dispatcher = Dispatcher::::default(); + let post_dispatch = + SolDispatchPost::decode(input, true).map_err(|e| PrecompileFailure::Error { + exit_status: ExitError::Other(format!("Failed to decode input: {:?}", e).into()), + })?; + + let post_dispatch = DispatchPost { + dest: parse_state_machine(post_dispatch.dest)?, + from: context.caller.0.to_vec(), + to: post_dispatch.to, + timeout_timestamp: post_dispatch.timeoutTimestamp, + data: post_dispatch.body, + gas_limit: post_dispatch.gaslimit, + }; + + handle.record_cost(cost)?; + match dispatcher.dispatch_request(DispatchRequest::Post(post_dispatch)) { + Ok(_) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![] }), + Err(e) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("dispatch execution failed: {:?}", e).into()), + }), + } + } +} + +/// Ismp Get Request Dispatcher precompile for evm contracts +pub struct IsmpGetDispatcher { + _marker: PhantomData, +} + +impl Precompile for IsmpGetDispatcher +where + T: pallet_ismp::Config + pallet_evm::Config, + ::Hash: From, +{ + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let input = handle.input(); + let context = handle.context(); + + let weight = ::WeightInfo::dispatch_get_request(); + + // The cost of a dispatch is the weight of calling the dispatcher plus an extra storage read + // and write + let cost = ::GasWeightMapping::weight_to_gas( + weight.saturating_add(::DbWeight::get().reads_writes(1, 1)), + ); + + let dispatcher = Dispatcher::::default(); + + let get_dispatch = + SolDispatchGet::decode(input, true).map_err(|e| PrecompileFailure::Error { + exit_status: ExitError::Other(format!("Failed to decode input: {:?}", e).into()), + })?; + let get_dispatch = DispatchGet { + dest: parse_state_machine(get_dispatch.dest)?, + from: context.caller.0.to_vec(), + keys: get_dispatch.keys, + height: get_dispatch.height, + timeout_timestamp: get_dispatch.timeoutTimestamp, + gas_limit: get_dispatch.gaslimit, + }; + + handle.record_cost(cost)?; + match dispatcher.dispatch_request(DispatchRequest::Get(get_dispatch)) { + Ok(_) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![] }), + Err(e) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("dispatch execution failed: {:?}", e).into()), + }), + } + } +} + +/// Ismp Response Dispatcher precompile for evm contracts +pub struct IsmpResponseDispatcher { + _marker: PhantomData, +} + +impl Precompile for IsmpResponseDispatcher +where + T: pallet_ismp::Config + pallet_evm::Config, + ::Hash: From, +{ + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let input = handle.input(); + + let weight = ::WeightInfo::dispatch_response(); + + let cost = ::GasWeightMapping::weight_to_gas(weight); + + let dispatcher = Dispatcher::::default(); + let response = + SolPostResponse::decode(input, true).map_err(|e| PrecompileFailure::Error { + exit_status: ExitError::Other(format!("Failed to decode input: {:?}", e).into()), + })?; + let post_response = PostResponse { + post: Post { + source: parse_state_machine(response.request.source)?, + dest: parse_state_machine(response.request.dest)?, + nonce: response.request.nonce, + from: response.request.from, + to: response.request.to, + timeout_timestamp: response.request.timeoutTimestamp, + data: response.request.body, + gas_limit: response.request.gaslimit, + }, + response: response.response, + }; + handle.record_cost(cost)?; + + match dispatcher.dispatch_response(post_response) { + Ok(_) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![] }), + Err(e) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("dispatch execution failed: {:?}", e).into()), + }), + } + } +} + +/// Parse state machine from utf8 bytes +fn parse_state_machine(bytes: Vec) -> Result { + StateMachine::from_str(&String::from_utf8(bytes).unwrap_or_default()).map_err(|e| { + PrecompileFailure::Error { + exit_status: ExitError::Other(format!("Failed to destination chain: {:?}", e).into()), + } + }) +} diff --git a/pallet-ismp/evm/src/tests.rs b/pallet-ismp/evm/src/tests.rs new file mode 100644 index 000000000..5c2eea85e --- /dev/null +++ b/pallet-ismp/evm/src/tests.rs @@ -0,0 +1,433 @@ +use crate::{ + mocks::*, + module::{EvmIsmpModule, EVM_HOST_ADDRESS}, +}; +use alloy_primitives::Address; +use alloy_sol_types::{sol, SolCall, SolType}; +use fp_evm::{CreateInfo, FeeCalculator, GenesisAccount}; +use frame_support::{ + traits::{GenesisBuild, Get}, + weights::Weight, +}; +use frame_system::EventRecord; +use hex_literal::hex; +use ismp_primitives::LeafIndexQuery; +use ismp_rs::{ + host::StateMachine, + module::IsmpModule, + router::{Get as GetRequest, GetResponse, Post, PostResponse, Request, Response}, + util::hash_request, +}; +use pallet_evm::{runner::Runner, FixedGasWeightMapping, GasWeightMapping}; +use pallet_ismp::{host::Host, Event, RequestCommitments}; +use sp_core::{ + offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, + H160, U256, +}; +use std::collections::BTreeMap; + +sol! { + function transfer( + address to, + bytes memory dest, + uint64 amount, + uint64 timeout, + uint64 gasLimit + ) public; + + function dispatchGet( + bytes memory dest, + bytes[] memory keys, + uint64 height, + uint64 timeout, + uint64 gasLimit + ) public; + + function mintTo(address who, uint64 amount) public; + + struct Payload { + address to; + address from; + uint64 amount; + } +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let mut accounts = BTreeMap::new(); + accounts.insert( + H160::from(USER.0 .0), + GenesisAccount { + nonce: U256::from(1), + balance: U256::max_value(), + storage: Default::default(), + code: vec![], + }, + ); + accounts.insert( + H160::from(EVM_HOST_ADDRESS), // root + GenesisAccount { + nonce: U256::from(1), + balance: U256::max_value(), + storage: Default::default(), + code: vec![], + }, + ); + + GenesisBuild::::assimilate_storage(&pallet_evm::GenesisConfig { accounts }, &mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + register_offchain_ext(&mut ext); + ext +} + +fn register_offchain_ext(ext: &mut sp_io::TestExternalities) { + let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); + ext.register_extension(OffchainDbExt::new(offchain.clone())); + ext.register_extension(OffchainWorkerExt::new(offchain)); +} + +pub const EXAMPLE_CONTRACT: &str = include_str!("../solidity/IsmpDemo.bin"); + +const USER: Address = Address::new(hex!("d8da6bf26964af9d7eed9e03e53415d37aa96045")); +const HOST: H160 = H160(EVM_HOST_ADDRESS); + +/// Verify the the last event emitted +fn assert_event_was_emitted( + generic_event: ::RuntimeEvent, +) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + for EventRecord { event, .. } in events { + if event == system_event { + return + } + } + panic!("Event was not emitted") +} + +fn deploy_contract(gas_limit: u64, weight_limit: Option) -> CreateInfo { + let info = ::Runner::create( + HOST, + hex::decode(EXAMPLE_CONTRACT.trim_end()).unwrap(), + U256::zero(), + gas_limit, + Some(FixedGasPrice::min_gas_price().0), + Some(FixedGasPrice::min_gas_price().0), + None, + Vec::new(), + true, // non-transactional + true, // must be validated + weight_limit, + None, + &::config().clone(), + ) + .expect("Deploy succeeds"); + + let call_data = mintToCall { who: USER, amount: 1_000_000_000 }.encode(); + + let contract_address = info.value; + + ::Runner::call( + HOST, + contract_address, + call_data, + U256::zero(), + gas_limit, + Some(FixedGasPrice::min_gas_price().0), + Some(FixedGasPrice::min_gas_price().0), + None, + Vec::new(), + true, // transactional + true, // must be validated + weight_limit, + None, + &::config().clone(), + ) + .expect("call succeeds"); + info +} + +#[test] +fn post_dispatch() { + let mut ext = new_test_ext(); + let contract_address = ext.execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let call_data = transferCall { + to: USER, + dest: StateMachine::Polkadot(1000).to_string().as_bytes().to_vec(), + amount: 10_000, + timeout: 223311228889, + gasLimit: gas_limit, + } + .encode(); + + ::Runner::call( + H160::from(USER.0 .0), + contract_address, + call_data, + U256::zero(), + gas_limit, + Some(FixedGasPrice::min_gas_price().0), + Some(FixedGasPrice::min_gas_price().0), + None, + Vec::new(), + true, // transactional + true, // must be validated + Some(weight_limit), + None, + &::config().clone(), + ) + .expect("call succeeds"); + // Check + assert_event_was_emitted::( + Event::Request { + dest_chain: StateMachine::Polkadot(1000), + source_chain: ::StateMachine::get(), + request_nonce: 0, + } + .into(), + ); + contract_address + }); + + ext.persist_offchain_overlay(); + + ext.execute_with(|| { + // Assert that the source module for the request is the contract address + let req = pallet_ismp::Pallet::::get_request(0).unwrap(); + assert_eq!(req.source_module().to_vec(), contract_address.as_bytes().to_vec()) + }) +} + +#[test] +fn get_dispatch() { + let mut ext = new_test_ext(); + let contract_address = ext.execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let call_data = dispatchGetCall { + dest: StateMachine::Polkadot(2000).to_string().as_bytes().to_vec(), + keys: vec![vec![1u8; 64]], + height: 10, + timeout: 2000, + gasLimit: gas_limit, + } + .encode(); + + ::Runner::call( + H160::from(USER.0 .0), + contract_address, + call_data, + U256::zero(), + gas_limit, + Some(FixedGasPrice::min_gas_price().0), + Some(FixedGasPrice::min_gas_price().0), + None, + Vec::new(), + true, // transactional + true, // must be validated + Some(weight_limit), + None, + &::config().clone(), + ) + .expect("call succeeds"); + // Check + assert_event_was_emitted::( + Event::Request { + dest_chain: StateMachine::Polkadot(2000), + source_chain: ::StateMachine::get(), + request_nonce: 0, + } + .into(), + ); + contract_address + }); + + ext.persist_offchain_overlay(); + + ext.execute_with(|| { + // Assert that the source module for the request is the contract address + let req = pallet_ismp::Pallet::::get_request(0).unwrap(); + assert_eq!(req.source_module().to_vec(), contract_address.as_bytes().to_vec()) + }) +} + +#[test] +fn on_accept_callback() { + new_test_ext().execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let handler = EvmIsmpModule::::default(); + + let payload = Payload { to: USER, from: USER, amount: 50000 }; + + let post = Post { + source: ::StateMachine::get(), + dest: StateMachine::Polkadot(2000), + nonce: 0, + from: contract_address.as_bytes().to_vec(), + to: contract_address.as_bytes().to_vec(), + timeout_timestamp: 1000, + data: Payload::encode(&payload), + gas_limit, + }; + + let request_commitment = hash_request::>(&Request::Post(post.clone())); + RequestCommitments::::insert( + request_commitment.0.to_vec(), + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, + ); + + handler.on_accept(post).expect("Call succeeds"); + + assert_event_was_emitted::( + Event::Response { + dest_chain: ::StateMachine::get(), + source_chain: StateMachine::Polkadot(2000), + request_nonce: 0, + } + .into(), + ); + }) +} + +#[test] +fn on_post_response() { + new_test_ext().execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let handler = EvmIsmpModule::::default(); + + let payload = Payload { to: USER, from: USER, amount: 50000 }; + + let post = Post { + source: ::StateMachine::get(), + dest: StateMachine::Polkadot(2000), + nonce: 0, + from: contract_address.as_bytes().to_vec(), + to: contract_address.as_bytes().to_vec(), + timeout_timestamp: 1000, + data: Payload::encode(&payload), + gas_limit, + }; + + let response = PostResponse { post, response: H160::from_low_u64_be(30).0.to_vec() }; + + handler.on_response(Response::Post(response)).expect("Call succeeds") + }) +} + +#[test] +fn on_get_response() { + new_test_ext().execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let handler = EvmIsmpModule::::default(); + + let get = GetRequest { + source: ::StateMachine::get(), + dest: StateMachine::Polkadot(2000), + nonce: 0, + from: contract_address.as_bytes().to_vec(), + keys: vec![ + H160::from_low_u64_be(10).as_bytes().to_vec(), + H160::from_low_u64_be(20).as_bytes().to_vec(), + ], + height: 10, + timeout_timestamp: 1000, + gas_limit, + }; + + let mut values = BTreeMap::new(); + values.insert( + H160::from_low_u64_be(10).as_bytes().to_vec(), + Some(H160::from_low_u64_be(10).as_bytes().to_vec()), + ); + values.insert( + H160::from_low_u64_be(20).as_bytes().to_vec(), + Some(H160::from_low_u64_be(20).as_bytes().to_vec()), + ); + let response = GetResponse { get, values }; + + handler.on_response(Response::Get(response)).expect("Call succeeds") + }) +} + +#[test] +fn on_get_timeout() { + new_test_ext().execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let handler = EvmIsmpModule::::default(); + + let get = GetRequest { + source: ::StateMachine::get(), + dest: StateMachine::Polkadot(2000), + nonce: 0, + from: contract_address.as_bytes().to_vec(), + keys: vec![ + H160::from_low_u64_be(10).as_bytes().to_vec(), + H160::from_low_u64_be(20).as_bytes().to_vec(), + ], + height: 10, + timeout_timestamp: 1000, + gas_limit, + }; + + handler.on_timeout(Request::Get(get)).expect("Call succeeds") + }) +} + +#[test] +fn on_post_timeout() { + new_test_ext().execute_with(|| { + let gas_limit: u64 = 1_500_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + let result = deploy_contract(gas_limit, Some(weight_limit)); + + let contract_address = result.value; + + let handler = EvmIsmpModule::::default(); + let payload = Payload { to: USER, from: USER, amount: 50000 }; + let post = Post { + source: ::StateMachine::get(), + dest: StateMachine::Polkadot(2000), + nonce: 0, + from: contract_address.as_bytes().to_vec(), + to: contract_address.as_bytes().to_vec(), + timeout_timestamp: 1000, + data: Payload::encode(&payload), + gas_limit, + }; + + handler.on_timeout(Request::Post(post)).expect("Call succeeds") + }) +} diff --git a/pallet-ismp/evm/src/weight.rs b/pallet-ismp/evm/src/weight.rs new file mode 100644 index 000000000..08fddbcc0 --- /dev/null +++ b/pallet-ismp/evm/src/weight.rs @@ -0,0 +1,45 @@ +//! Weight info utilities for evm contracts +use core::marker::PhantomData; +use frame_support::dispatch::Weight; +use ismp_rs::router::{Post, Request, Response}; +use pallet_evm::GasWeightMapping; +use pallet_ismp::{weight_info::IsmpModuleWeight, Config}; + +/// An implementation of IsmpModuleWeight for evm contract callbacks +pub struct EvmWeightCalculator(PhantomData); + +impl Default for EvmWeightCalculator { + fn default() -> Self { + Self(PhantomData) + } +} + +impl IsmpModuleWeight for EvmWeightCalculator { + fn on_accept(&self, request: &Post) -> Weight { + ::GasWeightMapping::gas_to_weight(request.gas_limit, true) + } + + fn on_timeout(&self, request: &Request) -> Weight { + match request { + Request::Post(post) => { + ::GasWeightMapping::gas_to_weight(post.gas_limit, true) + } + Request::Get(get) => { + ::GasWeightMapping::gas_to_weight(get.gas_limit, true) + } + } + } + + fn on_response(&self, response: &Response) -> Weight { + match response { + Response::Post(response) => ::GasWeightMapping::gas_to_weight( + response.post.gas_limit, + true, + ), + Response::Get(response) => ::GasWeightMapping::gas_to_weight( + response.get.gas_limit, + true, + ), + } + } +} diff --git a/pallet-ismp/primitives/state-machine/Cargo.toml b/pallet-ismp/primitives/state-machine/Cargo.toml index 0e124ce9d..472314a57 100644 --- a/pallet-ismp/primitives/state-machine/Cargo.toml +++ b/pallet-ismp/primitives/state-machine/Cargo.toml @@ -21,7 +21,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - +hash-db = { version = "0.16.0", default-features = false } ismp-primitives = { path = "..", default-features = false } pallet-ismp = { path = "../..", default-features = false } @@ -41,5 +41,6 @@ std = [ "sp-core/std", "pallet-ismp/std", "ismp-primitives/std", - "sp-trie/std" + "sp-trie/std", + "hash-db/std" ] diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs index 18d31cec0..9013ae081 100644 --- a/pallet-ismp/primitives/state-machine/src/lib.rs +++ b/pallet-ismp/primitives/state-machine/src/lib.rs @@ -22,7 +22,7 @@ extern crate alloc; use alloc::{collections::BTreeMap, format, vec, vec::Vec}; use codec::Decode; -use core::marker::PhantomData; +use core::{fmt::Debug, marker::PhantomData}; use ismp::{ consensus::{StateCommitment, StateMachineClient}, error::Error, @@ -39,7 +39,7 @@ use merkle_mountain_range::MerkleProof; use pallet_ismp::host::Host; use primitive_types::H256; use sp_runtime::traits::{BlakeTwo256, Keccak256}; -use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; +use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; /// The parachain and grandpa consensus client implementation for ISMP. pub struct SubstrateStateMachine(PhantomData); @@ -163,3 +163,35 @@ where Ok(data) } } + +/// Lifted directly from [`sp_state_machine::read_proof_check`](https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1075-L1094) +pub fn read_proof_check( + root: &H::Out, + proof: StorageProof, + keys: I, +) -> Result, Option>>, Error> +where + H: hash_db::Hasher, + H::Out: Debug, + I: IntoIterator, + I::Item: AsRef<[u8]>, +{ + let db = proof.into_memory_db(); + + if !db.contains(root, EMPTY_PREFIX) { + Err(Error::ImplementationSpecific("Invalid Proof".into()))? + } + + let trie = TrieDBBuilder::>::new(&db, root).build(); + let mut result = BTreeMap::new(); + + for key in keys.into_iter() { + let value = trie + .get(key.as_ref()) + .map_err(|e| Error::ImplementationSpecific(format!("Error reading from trie: {e:?}")))? + .and_then(|val| Decode::decode(&mut &val[..]).ok()); + result.insert(key.as_ref().to_vec(), value); + } + + Ok(result) +} diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 147077586..551305654 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -35,11 +35,11 @@ T: pallet_timestamp::Config, pub mod benchmarks { use super::*; use crate::{ + dispatcher::Dispatcher, host::Host, mocks::ismp::{setup_mock_client, MOCK_CONSENSUS_STATE_ID, MODULE_ID}, Config, Event, Pallet, RequestCommitments, RequestReceipts, ResponseReceipts, }; - use codec::Encode; use frame_support::traits::{Get, Hooks}; use frame_system::EventRecord; use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; @@ -50,7 +50,10 @@ pub mod benchmarks { CreateConsensusState, Message, Proof, RequestMessage, ResponseMessage, StateCommitmentHeight, TimeoutMessage, }, - router::{Post, PostResponse, Request, Response}, + router::{ + DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher, Post, PostResponse, + Request, Response, + }, util::hash_request, }; @@ -104,12 +107,11 @@ pub mod benchmarks { source: StateMachine::Ethereum(Ethereum::ExecutionLayer), dest: ::StateMachine::get(), nonce: 0, - gas_limit: 0, - - from: MODULE_ID.encode(), - to: MODULE_ID.encode(), + from: MODULE_ID.to_bytes(), + to: MODULE_ID.to_bytes(), timeout_timestamp: 5000, data: "handle_request_message".as_bytes().to_vec(), + gas_limit: 0, }; let msg = @@ -132,12 +134,11 @@ pub mod benchmarks { source: ::StateMachine::get(), dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, - from: MODULE_ID.encode(), - to: MODULE_ID.encode(), + from: MODULE_ID.to_bytes(), + to: MODULE_ID.to_bytes(), timeout_timestamp: 5000, - gas_limit: 0, - data: "handle_response_message".as_bytes().to_vec(), + gas_limit: 0, }; let request = Request::Post(post.clone()); @@ -171,11 +172,11 @@ pub mod benchmarks { source: ::StateMachine::get(), dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), nonce: 0, - gas_limit: 0, - from: MODULE_ID.encode(), - to: MODULE_ID.encode(), + from: MODULE_ID.to_bytes(), + to: MODULE_ID.to_bytes(), timeout_timestamp: 500, data: "handle_timeout_message".as_bytes().to_vec(), + gas_limit: 0, }; let request = Request::Post(post.clone()); @@ -200,15 +201,15 @@ pub mod benchmarks { #[benchmark] fn on_finalize(x: Linear<1, 100>) { for nonce in 0..x { - let post = ismp_rs::router::Post { + let post = Post { source: StateMachine::Kusama(2000), dest: StateMachine::Kusama(2001), nonce: nonce.into(), from: vec![0u8; 32], - gas_limit: 0, to: vec![1u8; 32], timeout_timestamp: 100, data: vec![2u8; 64], + gas_limit: 0, }; let request = Request::Post(post); @@ -223,5 +224,68 @@ pub mod benchmarks { } } + #[benchmark] + fn dispatch_post_request() { + let post = DispatchPost { + dest: StateMachine::Kusama(2000), + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100, + data: vec![2u8; 64], + gas_limit: 0, + }; + + let dispatcher = Dispatcher::::default(); + #[block] + { + dispatcher.dispatch_request(DispatchRequest::Post(post)).unwrap() + } + } + + #[benchmark] + fn dispatch_get_request() { + let get = DispatchGet { + dest: StateMachine::Kusama(2000), + from: vec![0u8; 32], + keys: vec![vec![1u8; 32]; 32], + height: 20, + timeout_timestamp: 100, + gas_limit: 0, + }; + + let dispatcher = Dispatcher::::default(); + #[block] + { + dispatcher.dispatch_request(DispatchRequest::Get(get)).unwrap() + } + } + + #[benchmark] + fn dispatch_response() { + let post = Post { + source: StateMachine::Kusama(2000), + dest: StateMachine::Kusama(2001), + nonce: 0, + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100, + data: vec![2u8; 64], + gas_limit: 0, + }; + let request_commitment = hash_request::>(&Request::Post(post.clone())); + RequestCommitments::::insert( + request_commitment.0.to_vec(), + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, + ); + + let response = PostResponse { post, response: vec![1u8; 64] }; + + let dispatcher = Dispatcher::::default(); + #[block] + { + dispatcher.dispatch_response(response).unwrap() + } + } + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mocks::Test); } diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index 995456acd..2a6e4d3aa 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -14,16 +14,13 @@ // limitations under the License. //! Implementation for the ISMP Router -use crate::{host::Host, Config, Event, Pallet, RequestCommitments, ResponseCommitments}; -use alloc::string::ToString; +use crate::{host::Host, Config, Pallet}; use codec::{Decode, Encode}; use core::marker::PhantomData; -use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; use ismp_rs::{ error::Error as IsmpError, host::IsmpHost, router::{DispatchRequest, Get, IsmpDispatcher, Post, PostResponse, Request, Response}, - util::{hash_request, hash_response}, }; use sp_core::H256; @@ -57,10 +54,10 @@ where dest: dispatch_get.dest, nonce: host.next_nonce(), from: dispatch_get.from, - gas_limit: 0, keys: dispatch_get.keys, height: dispatch_get.height, timeout_timestamp: dispatch_get.timeout_timestamp, + gas_limit: dispatch_get.gas_limit, }; Request::Get(get) } @@ -69,59 +66,26 @@ where source: host.host_state_machine(), dest: dispatch_post.dest, nonce: host.next_nonce(), - gas_limit: 0, from: dispatch_post.from, to: dispatch_post.to, timeout_timestamp: dispatch_post.timeout_timestamp, data: dispatch_post.data, + gas_limit: dispatch_post.gas_limit, }; Request::Post(post) } }; - let commitment = hash_request::>(&request).0.to_vec(); + Pallet::::dispatch_request(request)?; - let (dest_chain, source_chain, nonce) = - (request.dest_chain(), request.source_chain(), request.nonce()); - Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| { - IsmpError::ImplementationSpecific("Failed to push request into mmr".to_string()) - })?; - // Deposit Event - Pallet::::deposit_event(Event::Request { - request_nonce: nonce, - source_chain, - dest_chain, - }); - // We need this step since it's not trivial to check the mmr for commitments on chain - RequestCommitments::::insert( - commitment, - LeafIndexQuery { source_chain, dest_chain, nonce }, - ); Ok(()) } fn dispatch_response(&self, response: PostResponse) -> Result<(), IsmpError> { let response = Response::Post(response); - let commitment = hash_response::>(&response).0.to_vec(); + Pallet::::dispatch_response(response)?; - if ResponseCommitments::::contains_key(commitment.clone()) { - Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? - } - - let (dest_chain, source_chain, nonce) = - (response.dest_chain(), response.source_chain(), response.nonce()); - - Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| { - IsmpError::ImplementationSpecific("Failed to push response into mmr".to_string()) - })?; - - Pallet::::deposit_event(Event::Response { - request_nonce: nonce, - dest_chain, - source_chain, - }); - ResponseCommitments::::insert(commitment, Receipt::Ok); Ok(()) } } diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index 282d4be37..7d3bbf0d5 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -16,8 +16,8 @@ impl Pallet where ::Hash: From, { - /// Handle an incoming request - pub fn handle_request(request: Request) -> Result<(), IsmpError> { + /// Dispatch an outgoing request + pub fn dispatch_request(request: Request) -> Result<(), IsmpError> { let commitment = hash_request::>(&request).0.to_vec(); if RequestCommitments::::contains_key(commitment.clone()) { @@ -43,8 +43,14 @@ where Ok(()) } - /// Handle an incoming response - pub fn handle_response(response: Response) -> Result<(), IsmpError> { + /// Dispatch an outgoing response + pub fn dispatch_response(response: Response) -> Result<(), IsmpError> { + let commitment = hash_request::>(&response.request()).0.to_vec(); + + if !RequestCommitments::::contains_key(commitment.clone()) { + Err(IsmpError::ImplementationSpecific("Unknown request for response".to_string()))? + } + let commitment = hash_response::>(&response).0.to_vec(); if ResponseCommitments::::contains_key(commitment.clone()) { diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 1d5333623..9ef2e43e7 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -20,6 +20,7 @@ #![deny(missing_docs)] extern crate alloc; +extern crate core; pub mod benchmarking; pub mod dispatcher; @@ -28,7 +29,7 @@ pub mod events; pub mod handlers; pub mod host; mod mmr; -#[cfg(any(feature = "runtime-benchmarks", test))] +#[cfg(any(feature = "runtime-benchmarks", feature = "testing", test))] pub mod mocks; pub mod primitives; #[cfg(test)] @@ -40,7 +41,12 @@ pub use mmr::utils::NodesUtils; use crate::host::Host; use codec::{Decode, Encode}; use core::time::Duration; -use frame_support::{dispatch::DispatchResult, log::debug, traits::Get, RuntimeDebug}; +use frame_support::{ + dispatch::{DispatchResult, DispatchResultWithPostInfo, Pays, PostDispatchInfo}, + log::debug, + traits::Get, + RuntimeDebug, +}; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, handlers::{handle_incoming_message, MessageResult}, @@ -53,6 +59,7 @@ use sp_core::{offchain::StorageKind, H256}; use crate::{ errors::{HandlingError, ModuleCallbackResult}, mmr::mmr::Mmr, + weight_info::get_weight, }; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, @@ -72,7 +79,7 @@ pub mod pallet { use crate::{ dispatcher::Receipt, errors::HandlingError, - primitives::ConsensusClientProvider, + primitives::{ConsensusClientProvider, WeightUsed}, weight_info::{WeightInfo, WeightProvider}, }; use alloc::collections::BTreeSet; @@ -93,7 +100,6 @@ pub mod pallet { router::IsmpRouter, }; use sp_core::H256; - use weight_info::get_weight; #[pallet::config] pub trait Config: frame_system::Config { @@ -261,6 +267,12 @@ pub mod pallet { #[pallet::getter(fn nonce)] pub type Nonce = StorageValue<_, u64, ValueQuery>; + /// Contains a tuple of the weight consumed and weight limit in executing contract callbacks in + /// a transaction + #[pallet::storage] + #[pallet::getter(fn weight_consumed)] + pub type WeightConsumed = StorageValue<_, WeightUsed, ValueQuery>; + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet @@ -320,7 +332,7 @@ pub mod pallet { #[pallet::weight(get_weight::(&messages))] #[pallet::call_index(0)] #[frame_support::transactional] - pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResult { + pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResultWithPostInfo { let _ = ensure_signed(origin)?; Self::handle_messages(messages) @@ -463,11 +475,12 @@ where } /// Provides a way to handle messages. - pub fn handle_messages(messages: Vec) -> DispatchResult { + pub fn handle_messages(messages: Vec) -> DispatchResultWithPostInfo { // Define a host + WeightConsumed::::kill(); let host = Host::::default(); let mut errors: Vec = vec![]; - + let total_weight = get_weight::(&messages); for message in messages { match handle_incoming_message(&host, message) { Ok(MessageResult::ConsensusMessage(res)) => { @@ -529,7 +542,13 @@ where Self::deposit_event(Event::::HandlingErrors { errors }) } - Ok(()) + Ok(PostDispatchInfo { + actual_weight: { + let acc_weight = WeightConsumed::::get(); + Some((total_weight - acc_weight.weight_limit) + acc_weight.weight_used) + }, + pays_fee: Pays::Yes, + }) } /// Return the on-chain MMR root hash. diff --git a/pallet-ismp/src/mocks/ismp.rs b/pallet-ismp/src/mocks/ismp.rs index 478509d2d..890684ed8 100644 --- a/pallet-ismp/src/mocks/ismp.rs +++ b/pallet-ismp/src/mocks/ismp.rs @@ -15,10 +15,11 @@ use ismp_rs::{ router::{Post, Request, RequestResponse, Response}, }; +/// Mock consensus state id pub const MOCK_CONSENSUS_STATE_ID: [u8; 4] = *b"mock"; /// module id for the mock benchmarking module -pub const MODULE_ID: ModuleId = ModuleId::Pallet(PalletId(*b"___mock_")); +pub const MODULE_ID: ModuleId = ModuleId::Pallet(PalletId(*b"__mock__")); fn set_timestamp(value: u64) where @@ -27,6 +28,7 @@ where pallet_timestamp::Pallet::::set_timestamp(value.into()); } +/// Mock module #[derive(Default)] pub struct MockModule; @@ -103,6 +105,7 @@ impl StateMachineClient for MockStateMachine { } } +/// Mock client setup pub fn setup_mock_client(host: &H) -> StateMachineHeight where ::Moment: From, diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index 92f39a7fb..df985401a 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -14,11 +14,15 @@ // limitations under the License. //! Pallet primitives -use frame_support::{PalletId, RuntimeDebug}; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, PalletId, RuntimeDebug}; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; use scale_info::TypeInfo; -use sp_core::{crypto::AccountId32, H160}; +use sp_core::{ + crypto::{AccountId32, ByteArray}, + H160, +}; use sp_std::prelude::*; /// An MMR proof data for a group of leaves. @@ -58,7 +62,7 @@ pub trait ConsensusClientProvider { } /// Module identification types supported by ismp -#[derive(codec::Encode, codec::Decode, PartialEq, Eq, scale_info::TypeInfo)] +#[derive(PartialEq, Eq, scale_info::TypeInfo)] pub enum ModuleId { /// Unique Pallet identification in runtime Pallet(PalletId), @@ -67,3 +71,38 @@ pub enum ModuleId { /// Evm contract Evm(H160), } + +impl ModuleId { + /// Convert module id to raw bytes + pub fn to_bytes(&self) -> Vec { + match self { + ModuleId::Pallet(pallet_id) => pallet_id.0.to_vec(), + ModuleId::Contract(account_id) => account_id.as_slice().to_vec(), + ModuleId::Evm(account_id) => account_id.0.to_vec(), + } + } + + /// Derive module id from raw bytes + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() == 8 { + let mut inner = [0u8; 8]; + inner.copy_from_slice(bytes); + Ok(Self::Pallet(PalletId(inner))) + } else if bytes.len() == 32 { + Ok(Self::Contract(AccountId32::from_slice(bytes).expect("Infallible"))) + } else if bytes.len() == 20 { + Ok(Self::Evm(H160::from_slice(bytes))) + } else { + Err("Unknown Module ID format") + } + } +} + +/// Accumulated Weight consumed by contract callbacks in a transaction +#[derive(Default, scale_info::TypeInfo, Encode, Decode)] +pub struct WeightUsed { + /// Total weight used in executing contract callbacks in a transaction + pub weight_used: Weight, + /// Total weight limit used in executing contract callbacks in a transaction + pub weight_limit: Weight, +} diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index b2f3bee9c..ac3adb82f 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -29,7 +29,7 @@ use ismp_rs::{ consensus::StateMachineHeight, host::Ethereum, messaging::{Proof, ResponseMessage, TimeoutMessage}, - router::{DispatchGet, DispatchRequest, IsmpDispatcher}, + router::{DispatchGet, DispatchRequest, IsmpDispatcher, Post}, util::hash_request, }; use ismp_testsuite::{ @@ -208,6 +208,22 @@ fn dispatcher_should_write_receipts_for_outgoing_requests_and_responses() { set_timestamp(None); let host = Host::::default(); let dispatcher = Dispatcher::::default(); + let post = Post { + source: StateMachine::Kusama(2000), + dest: host.host_state_machine(), + nonce: 0, + from: vec![0u8; 32], + to: vec![0u8; 32], + timeout_timestamp: 0, + data: vec![0u8; 64], + gas_limit: 0, + }; + + let request_commitment = hash_request::>(&Request::Post(post.clone())); + RequestCommitments::::insert( + request_commitment.0.to_vec(), + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, + ); write_outgoing_commitments(&host, &dispatcher).unwrap(); }) } diff --git a/pallet-ismp/src/weight_info.rs b/pallet-ismp/src/weight_info.rs index 938379f0d..7cb7d867d 100644 --- a/pallet-ismp/src/weight_info.rs +++ b/pallet-ismp/src/weight_info.rs @@ -141,6 +141,12 @@ pub trait WeightInfo { fn handle_response_message() -> Weight; /// Returns the weight consumed in handling a timeout fn handle_timeout_message() -> Weight; + /// Returns the weight consumed in dispatching a post request + fn dispatch_post_request() -> Weight; + /// Returns the weight consumed in dispatching a get request + fn dispatch_get_request() -> Weight; + /// Returns the weight consumed in dispatching a response + fn dispatch_response() -> Weight; } impl WeightInfo for () { @@ -167,6 +173,18 @@ impl WeightInfo for () { fn handle_timeout_message() -> Weight { Weight::zero() } + + fn dispatch_post_request() -> Weight { + Weight::zero() + } + + fn dispatch_get_request() -> Weight { + Weight::zero() + } + + fn dispatch_response() -> Weight { + Weight::zero() + } } /// Returns the weight that would be consumed when executing a batch of messages @@ -181,7 +199,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { Message::Request(msg) => { let state_machine = msg.proof.height.id; let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = codec::Decode::decode(&mut req.to.as_slice()).ok(); + let dest_module = ModuleId::from_bytes(req.to.as_slice()).ok(); let handle = dest_module .map(|id| ::WeightProvider::module_callback(id)) .flatten() @@ -207,7 +225,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { let dest_module = match res { Response::Post(ref post) => { - codec::Decode::decode(&mut post.post.from.as_slice()).ok() + ModuleId::from_bytes(post.post.from.as_slice()).ok() } _ => return acc, }; @@ -235,9 +253,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { let state_machine = proof.height.id; let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Get(ref get) => { - codec::Decode::decode(&mut get.from.as_slice()).ok() - } + Request::Get(ref get) => ModuleId::from_bytes(get.from.as_slice()).ok(), _ => return acc, }; let handle = dest_module @@ -245,7 +261,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { .flatten() .unwrap_or(Box::new(())); acc + handle.on_response(&Response::Get(GetResponse { - get: req.get_request().unwrap(), + get: req.get_request().expect("Infallible"), values: Default::default(), })) }); @@ -268,9 +284,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { let state_machine = timeout_proof.height.id; let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Post(ref post) => { - codec::Decode::decode(&mut post.from.as_slice()).ok() - } + Request::Post(ref post) => ModuleId::from_bytes(post.from.as_slice()).ok(), _ => return acc, }; let handle = dest_module @@ -298,9 +312,7 @@ pub fn get_weight(messages: &[Message]) -> Weight { TimeoutMessage::Get { requests } => { let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { let dest_module = match req { - Request::Get(ref get) => { - codec::Decode::decode(&mut get.from.as_slice()).ok() - } + Request::Get(ref get) => ModuleId::from_bytes(get.from.as_slice()).ok(), _ => return acc, }; let handle = dest_module diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 2690b3b4f..e6b053467 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -13,9 +13,7 @@ serde = { version = "1.0.136", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } hex-literal = "0.4.1" -merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } primitive-types = { version = "0.12.1", default-features = false } -hash-db = { version = "0.16.0", default-features = false } # polytope labs ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } @@ -52,7 +50,6 @@ std = [ "cumulus-primitives-core/std", "ismp/std", "sp-trie/std", - "merkle-mountain-range/std", "sp-consensus-aura/std", "sp-runtime/std", "sp-io/std", @@ -60,7 +57,6 @@ std = [ "primitive-types/std", "ismp-primitives/std", "pallet-ismp/std", - "hash-db/std", "serde", "substrate-state-machine/std" ] diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index ae1a154dc..538351646 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -39,8 +39,8 @@ use sp_runtime::{ traits::{BlakeTwo256, Header as _}, DigestItem, }; -use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; -use substrate_state_machine::SubstrateStateMachine; +use sp_trie::StorageProof; +use substrate_state_machine::{read_proof_check, SubstrateStateMachine}; use crate::RelayChainOracle; @@ -217,35 +217,3 @@ pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { storage_key.extend_from_slice(&encoded_para_id); StorageKey(storage_key) } - -/// Lifted directly from [`sp_state_machine::read_proof_check`](https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1075-L1094) -pub fn read_proof_check( - root: &H::Out, - proof: StorageProof, - keys: I, -) -> Result, Option>>, Error> -where - H: hash_db::Hasher, - H::Out: Debug, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let db = proof.into_memory_db(); - - if !db.contains(root, EMPTY_PREFIX) { - Err(Error::ImplementationSpecific("Invalid Proof".into()))? - } - - let trie = TrieDBBuilder::>::new(&db, root).build(); - let mut result = BTreeMap::new(); - - for key in keys.into_iter() { - let value = trie - .get(key.as_ref()) - .map_err(|e| Error::ImplementationSpecific(format!("Error reading from trie: {e:?}")))? - .and_then(|val| Decode::decode(&mut &val[..]).ok()); - result.insert(key.as_ref().to_vec(), value); - } - - Ok(result) -} From b61709c9f043625e546af55baf5d20f59be37b8e Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Sat, 29 Jul 2023 11:19:06 +0100 Subject: [PATCH 155/182] make grandpa prover cloneable (#76) --- grandpa/prover/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/grandpa/prover/src/lib.rs b/grandpa/prover/src/lib.rs index 38e10274f..e68501264 100644 --- a/grandpa/prover/src/lib.rs +++ b/grandpa/prover/src/lib.rs @@ -41,6 +41,7 @@ use subxt::{config::Header, Config, OnlineClient}; pub struct HeadData(pub Vec); /// Contains methods useful for proving parachain and standalone-chain header finality using GRANDPA +#[derive(Clone)] pub struct GrandpaProver { /// Subxt client for the chain pub client: OnlineClient, From 734aca815b30e71b8586997be48cab286ed2f953 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:01:03 +0100 Subject: [PATCH 156/182] H256 as storage keys for commitments (#77) --- .github/workflows/ci.yml | 10 ---------- pallet-ismp/evm/src/tests.rs | 2 +- pallet-ismp/primitives/state-machine/src/lib.rs | 2 +- pallet-ismp/src/benchmarking.rs | 12 ++++++------ pallet-ismp/src/handlers.rs | 12 ++++++------ pallet-ismp/src/host.rs | 12 ++++++------ pallet-ismp/src/lib.rs | 11 ++++------- pallet-ismp/src/tests.rs | 2 +- 8 files changed, 25 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5757ffb1..f93a09227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,11 +31,6 @@ jobs: with: version: '3.9.1' - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - name: Build run: | cargo +nightly-2022-10-28 check --workspace --all-targets --all-features --verbose --locked @@ -68,10 +63,5 @@ jobs: with: components: rustfmt - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - name: Check format run: cargo +nightly fmt --all --check diff --git a/pallet-ismp/evm/src/tests.rs b/pallet-ismp/evm/src/tests.rs index 5c2eea85e..a8897cdc2 100644 --- a/pallet-ismp/evm/src/tests.rs +++ b/pallet-ismp/evm/src/tests.rs @@ -290,7 +290,7 @@ fn on_accept_callback() { let request_commitment = hash_request::>(&Request::Post(post.clone())); RequestCommitments::::insert( - request_commitment.0.to_vec(), + request_commitment, LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, ); diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs index 9013ae081..714d29106 100644 --- a/pallet-ismp/primitives/state-machine/src/lib.rs +++ b/pallet-ismp/primitives/state-machine/src/lib.rs @@ -106,7 +106,7 @@ where match req { Request::Post(post) => { let request = Request::Post(post); - let commitment = hash_request::>(&request).0.to_vec(); + let commitment = hash_request::>(&request); keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); } Request::Get(_) => continue, diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 551305654..91b733861 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -122,7 +122,7 @@ pub mod benchmarks { handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]); let commitment = hash_request::>(&Request::Post(post)); - assert!(RequestReceipts::::get(commitment.0.to_vec()).is_some()); + assert!(RequestReceipts::::get(commitment).is_some()); } #[benchmark] @@ -144,7 +144,7 @@ pub mod benchmarks { let commitment = hash_request::>(&request); RequestCommitments::::insert( - commitment.0.to_vec(), + commitment, LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: post.nonce }, ); @@ -160,7 +160,7 @@ pub mod benchmarks { #[extrinsic_call] handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]); - assert!(ResponseReceipts::::get(request_commitment.0.to_vec()).is_some()); + assert!(ResponseReceipts::::get(request_commitment).is_some()); } #[benchmark] @@ -182,7 +182,7 @@ pub mod benchmarks { let commitment = hash_request::>(&request); RequestCommitments::::insert( - commitment.0.to_vec(), + commitment, LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: post.nonce }, ); @@ -195,7 +195,7 @@ pub mod benchmarks { #[extrinsic_call] handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]); - assert!(RequestCommitments::::get(commitment.0.to_vec()).is_none()); + assert!(RequestCommitments::::get(commitment).is_none()); } #[benchmark] @@ -274,7 +274,7 @@ pub mod benchmarks { }; let request_commitment = hash_request::>(&Request::Post(post.clone())); RequestCommitments::::insert( - request_commitment.0.to_vec(), + request_commitment, LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, ); diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index 7d3bbf0d5..d38d15199 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -18,9 +18,9 @@ where { /// Dispatch an outgoing request pub fn dispatch_request(request: Request) -> Result<(), IsmpError> { - let commitment = hash_request::>(&request).0.to_vec(); + let commitment = hash_request::>(&request); - if RequestCommitments::::contains_key(commitment.clone()) { + if RequestCommitments::::contains_key(commitment) { Err(IsmpError::ImplementationSpecific("Duplicate request".to_string()))? } @@ -45,15 +45,15 @@ where /// Dispatch an outgoing response pub fn dispatch_response(response: Response) -> Result<(), IsmpError> { - let commitment = hash_request::>(&response.request()).0.to_vec(); + let commitment = hash_request::>(&response.request()); - if !RequestCommitments::::contains_key(commitment.clone()) { + if !RequestCommitments::::contains_key(commitment) { Err(IsmpError::ImplementationSpecific("Unknown request for response".to_string()))? } - let commitment = hash_response::>(&response).0.to_vec(); + let commitment = hash_response::>(&response); - if ResponseCommitments::::contains_key(commitment.clone()) { + if ResponseCommitments::::contains_key(commitment) { Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? } diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 2cccd16da..27e9466a6 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -84,7 +84,7 @@ where } fn request_commitment(&self, commitment: H256) -> Result<(), Error> { - let _ = RequestCommitments::::get(commitment.0.to_vec()).ok_or_else(|| { + let _ = RequestCommitments::::get(commitment).ok_or_else(|| { Error::ImplementationSpecific("Request commitment not found".to_string()) })?; @@ -94,7 +94,7 @@ where fn request_receipt(&self, req: &Request) -> Option<()> { let commitment = hash_request::(req); - let _ = RequestReceipts::::get(commitment.0.to_vec()) + let _ = RequestReceipts::::get(commitment) .ok_or_else(|| Error::RequestCommitmentNotFound { nonce: req.nonce(), source: req.source_chain(), @@ -141,13 +141,13 @@ where fn delete_request_commitment(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); // We can't delete actual leaves in the mmr so this serves as a replacement for that - RequestCommitments::::remove(hash.0.to_vec()); + RequestCommitments::::remove(hash); Ok(()) } fn store_request_receipt(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); - RequestReceipts::::insert(hash.0.to_vec(), Receipt::Ok); + RequestReceipts::::insert(hash, Receipt::Ok); Ok(()) } @@ -195,7 +195,7 @@ where fn response_receipt(&self, res: &Request) -> Option<()> { let commitment = hash_request::(res); - let _ = ResponseReceipts::::get(commitment.0.to_vec()) + let _ = ResponseReceipts::::get(commitment) .ok_or_else(|| Error::ImplementationSpecific("Response receipt not found".to_string())) .ok()?; @@ -209,7 +209,7 @@ where fn store_response_receipt(&self, req: &Request) -> Result<(), Error> { let hash = hash_request::(req); - ResponseReceipts::::insert(hash.0.to_vec(), Receipt::Ok); + ResponseReceipts::::insert(hash, Receipt::Ok); Ok(()) } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 9ef2e43e7..56ee37a93 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -227,28 +227,25 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn request_commitments)] pub type RequestCommitments = - StorageMap<_, Blake2_128Concat, Vec, LeafIndexQuery, OptionQuery>; + StorageMap<_, Identity, H256, LeafIndexQuery, OptionQuery>; /// Commitments for outgoing responses /// The key is the response commitment #[pallet::storage] #[pallet::getter(fn response_commitments)] - pub type ResponseCommitments = - StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; + pub type ResponseCommitments = StorageMap<_, Identity, H256, Receipt, OptionQuery>; /// Receipts for incoming requests /// The key is the request commitment #[pallet::storage] #[pallet::getter(fn request_receipts)] - pub type RequestReceipts = - StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; + pub type RequestReceipts = StorageMap<_, Identity, H256, Receipt, OptionQuery>; /// Receipts for incoming responses /// The key is the request commitment #[pallet::storage] #[pallet::getter(fn response_receipts)] - pub type ResponseReceipts = - StorageMap<_, Blake2_128Concat, Vec, Receipt, OptionQuery>; + pub type ResponseReceipts = StorageMap<_, Identity, H256, Receipt, OptionQuery>; /// Consensus update results still in challenge period /// Set contains a tuple of previous height and latest height diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index ac3adb82f..c14d51629 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -221,7 +221,7 @@ fn dispatcher_should_write_receipts_for_outgoing_requests_and_responses() { let request_commitment = hash_request::>(&Request::Post(post.clone())); RequestCommitments::::insert( - request_commitment.0.to_vec(), + request_commitment, LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, ); write_outgoing_commitments(&host, &dispatcher).unwrap(); From 8b745b8dfdddfc715642aee5aa3466f3edb2d9fa Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Tue, 1 Aug 2023 11:34:29 +0100 Subject: [PATCH 157/182] rpc methods (#78) Co-authored-by: David Salami <31099392+Wizdave97@users.noreply.github.com> --- pallet-ismp/rpc/src/lib.rs | 26 ++++++++++++++++++++++++++ pallet-ismp/runtime-api/src/lib.rs | 6 ++++++ pallet-ismp/src/lib.rs | 12 +++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index 0bb05e0e0..e21c781f9 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -117,6 +117,14 @@ where #[method(name = "ismp_queryConsensusUpdateTime")] fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result; + /// Query the challenge period for client + #[method(name = "ismp_queryChallengePeriod")] + fn query_challenge_period(&self, client_id: ConsensusClientId) -> Result; + + /// Query the latest timestamp for chain + #[method(name = "ismp_queryTimestamp")] + fn query_timestamp(&self) -> Result; + /// Query the latest height for a state machine #[method(name = "ismp_queryStateMachineLatestHeight")] fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result; @@ -255,6 +263,24 @@ where .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus update time")) } + fn query_challenge_period(&self, client_id: ConsensusClientId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.challenge_period(at, client_id) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Challenge period")) + } + + fn query_timestamp(&self) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.timestamp(at) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching latest timestamp")) + } + fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result { let api = self.client.runtime_api(); let at = self.client.info().best_hash; diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index 94f03391a..b558bd361 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -54,6 +54,12 @@ sp_api::decl_runtime_apis! { /// Return the timestamp this client was last updated in seconds fn consensus_update_time(id: ConsensusClientId) -> Option; + /// Return the latest timestamp for the chain + fn timestamp() -> Option; + + /// Return the challenge period timestamp + fn challenge_period(id: ConsensusClientId) -> Option; + /// Return the latest height of the state machine fn latest_state_machine_height(id: StateMachineId) -> Option; diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 56ee37a93..ce0b5b198 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -44,7 +44,7 @@ use core::time::Duration; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo, Pays, PostDispatchInfo}, log::debug, - traits::Get, + traits::{Get, UnixTime}, RuntimeDebug, }; use ismp_rs::{ @@ -667,6 +667,16 @@ where ConsensusClientUpdateTime::::get(id) } + /// Return the challenge period + pub fn get_challenge_period(id: ConsensusClientId) -> Option { + ChallengePeriod::::get(id) + } + + /// Return latest timestamp on chain + pub fn get_timestamp() -> Option { + Some(::now().as_secs()) + } + /// Return the latest height of the state machine pub fn get_latest_state_machine_height(id: StateMachineId) -> Option { Some(LatestStateMachineHeight::::get(id)) From 255a4fcd47c5c6d8af48d68a7e2ce3f02e96de6d Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 1 Aug 2023 19:13:28 +0100 Subject: [PATCH 158/182] Decouple `DataOrHash` from Runtime (#79) * refactor hashing in pallet * cargo update --- Cargo.lock | 4 +-- grandpa/src/consensus.rs | 1 + grandpa/src/lib.rs | 1 + pallet-ismp/evm/src/precompiles.rs | 3 ++ pallet-ismp/primitives/src/mmr.rs | 34 ++++++++----------- .../primitives/state-machine/src/lib.rs | 5 +-- pallet-ismp/src/benchmarking.rs | 1 + pallet-ismp/src/dispatcher.rs | 1 + pallet-ismp/src/handlers.rs | 1 + pallet-ismp/src/host.rs | 17 ++++++---- pallet-ismp/src/lib.rs | 24 +++++++------ pallet-ismp/src/mmr/mmr.rs | 14 +++++--- pallet-ismp/src/mmr/storage.rs | 14 ++++---- pallet-ismp/src/tests.rs | 9 ++--- parachain/src/consensus.rs | 1 + parachain/src/lib.rs | 5 +++ 16 files changed, 76 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 827e94bb7..d7df07eb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3386,7 +3386,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59cac5c7e9961bc0884641339dea0244b859892b" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59d83711eec3f567c5a5984d032fca2abbf2d559" dependencies = [ "derive_more", "parity-scale-codec", @@ -3639,7 +3639,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59cac5c7e9961bc0884641339dea0244b859892b" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59d83711eec3f567c5a5984d032fca2abbf2d559" dependencies = [ "ismp", "parity-scale-codec", diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs index 1790faa1f..fdaa26025 100644 --- a/grandpa/src/consensus.rs +++ b/grandpa/src/consensus.rs @@ -61,6 +61,7 @@ where T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, T::Hash: From, + H256: From, { fn verify_consensus( &self, diff --git a/grandpa/src/lib.rs b/grandpa/src/lib.rs index abd26cf2f..63cd96c57 100644 --- a/grandpa/src/lib.rs +++ b/grandpa/src/lib.rs @@ -65,6 +65,7 @@ pub mod pallet { impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Add some new parachains to the list of parachains in the relay chain consensus state #[pallet::call_index(0)] diff --git a/pallet-ismp/evm/src/precompiles.rs b/pallet-ismp/evm/src/precompiles.rs index a745a5984..da800489e 100644 --- a/pallet-ismp/evm/src/precompiles.rs +++ b/pallet-ismp/evm/src/precompiles.rs @@ -38,6 +38,7 @@ impl Precompile for IsmpPostDispatcher where T: pallet_ismp::Config + pallet_evm::Config, ::Hash: From, + H256: From<::Hash>, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let input = handle.input(); @@ -82,6 +83,7 @@ impl Precompile for IsmpGetDispatcher where T: pallet_ismp::Config + pallet_evm::Config, ::Hash: From, + H256: From<::Hash>, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let input = handle.input(); @@ -129,6 +131,7 @@ impl Precompile for IsmpResponseDispatcher where T: pallet_ismp::Config + pallet_evm::Config, ::Hash: From, + H256: From<::Hash>, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let input = handle.input(); diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs index e9b52cbf2..20ae5d672 100644 --- a/pallet-ismp/primitives/src/mmr.rs +++ b/pallet-ismp/primitives/src/mmr.rs @@ -19,9 +19,8 @@ use core::fmt::Formatter; use codec::{Decode, Encode}; use ismp::{ - host::IsmpHost, router::{Request, Response}, - util::{hash_request, hash_response}, + util::{hash_request, hash_response, Keccak256}, }; use primitive_types::H256; use sp_runtime::traits; @@ -42,7 +41,7 @@ pub enum Leaf { impl Leaf { /// Returns the hash of a leaf - fn hash(&self) -> H256 { + fn hash(&self) -> H256 { match self { Leaf::Request(req) => hash_request::(req), Leaf::Response(res) => hash_response::(res), @@ -52,14 +51,14 @@ impl Leaf { /// An element representing either full data or its hash. #[derive(Clone, PartialEq, Eq, Encode, Decode, scale_info::TypeInfo)] -pub enum DataOrHash { +pub enum DataOrHash { /// Arbitrary data in its full form. Data(Leaf), /// A hash of some data. - Hash(<::Hashing as traits::Hash>::Output), + Hash(H256), } -impl core::fmt::Debug for DataOrHash { +impl core::fmt::Debug for DataOrHash { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { DataOrHash::Data(leaf) => f.debug_struct("DataOrHash").field("Data", leaf).finish(), @@ -68,26 +67,20 @@ impl core::fmt::Debug for DataOrHash { } } -impl From for DataOrHash { +impl From for DataOrHash { fn from(l: Leaf) -> Self { Self::Data(l) } } -impl DataOrHash -where - T: frame_system::Config, - T::Hash: From, -{ +impl DataOrHash { /// Retrieve a hash of this item. /// /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. - pub fn hash( - &self, - ) -> <::Hashing as traits::Hash>::Output { + pub fn hash(&self) -> H256 { match *self { - Self::Data(ref leaf) => ::from(leaf.hash::()), + Self::Data(ref leaf) => leaf.hash::(), Self::Hash(ref hash) => *hash, } } @@ -100,14 +93,17 @@ impl merkle_mountain_range::Merge for MmrHasher where T: frame_system::Config, T::Hash: From, - H: IsmpHost, + H256: From, + H: Keccak256, { - type Item = DataOrHash; + type Item = DataOrHash; fn merge(left: &Self::Item, right: &Self::Item) -> merkle_mountain_range::Result { let mut concat = left.hash::().as_ref().to_vec(); concat.extend_from_slice(right.hash::().as_ref()); - Ok(DataOrHash::Hash(<::Hashing as traits::Hash>::hash(&concat))) + Ok(DataOrHash::Hash( + <::Hashing as traits::Hash>::hash(&concat).into(), + )) } } diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs index 714d29106..596f09958 100644 --- a/pallet-ismp/primitives/state-machine/src/lib.rs +++ b/pallet-ismp/primitives/state-machine/src/lib.rs @@ -55,6 +55,7 @@ where T: pallet_ismp::Config, T::BlockNumber: Into, T::Hash: From, + H256: From, { fn verify_membership( &self, @@ -68,8 +69,8 @@ where })?; let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); let proof = - MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); - let leaves: Vec<(u64, DataOrHash)> = match item { + MerkleProof::>>::new(membership.mmr_size, nodes); + let leaves: Vec<(u64, DataOrHash)> = match item { RequestResponse::Request(req) => membership .leaf_indices .into_iter() diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 91b733861..9cc6c1e2f 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -29,6 +29,7 @@ use frame_system::RawOrigin; #[benchmarks( where ::Hash: From, +H256: From<::Hash>, T: pallet_timestamp::Config, ::Moment: From )] diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index 2a6e4d3aa..d2d6186c2 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -44,6 +44,7 @@ impl IsmpDispatcher for Dispatcher where T: Config, ::Hash: From, + H256: From<::Hash>, { fn dispatch_request(&self, request: DispatchRequest) -> Result<(), IsmpError> { let host = Host::::default(); diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index d38d15199..43f84283a 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -15,6 +15,7 @@ use sp_core::H256; impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Dispatch an outgoing request pub fn dispatch_request(request: Request) -> Result<(), IsmpError> { diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 27e9466a6..358d7308b 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -50,6 +50,7 @@ impl Default for Host { impl IsmpHost for Host where ::Hash: From, + H256: From<::Hash>, { fn host_state_machine(&self) -> StateMachine { T::StateMachine::get() @@ -155,13 +156,6 @@ where ::ConsensusClientProvider::consensus_client(id) } - fn keccak256(bytes: &[u8]) -> H256 - where - Self: Sized, - { - sp_io::hashing::keccak_256(bytes).into() - } - fn challenge_period(&self, id: ConsensusStateId) -> Option { ChallengePeriod::::get(&id).map(Duration::from_secs) } @@ -259,3 +253,12 @@ where AllowedProxies::::set(allowed); } } + +impl ismp_rs::util::Keccak256 for Host { + fn keccak256(bytes: &[u8]) -> H256 + where + Self: Sized, + { + sp_io::hashing::keccak_256(bytes).into() + } +} diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index ce0b5b198..322c60ea9 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -147,7 +147,7 @@ pub mod pallet { /// Latest MMR Root hash #[pallet::storage] #[pallet::getter(fn mmr_root_hash)] - pub type RootHash = StorageValue<_, ::Hash, ValueQuery>; + pub type RootHash = StorageValue<_, H256, ValueQuery>; /// Current size of the MMR (number of leaves) for requests. #[pallet::storage] @@ -160,8 +160,7 @@ pub mod pallet { /// are pruned and only stored in the Offchain DB. #[pallet::storage] #[pallet::getter(fn request_peaks)] - pub type Nodes = - StorageMap<_, Identity, NodeIndex, ::Hash, OptionQuery>; + pub type Nodes = StorageMap<_, Identity, NodeIndex, H256, OptionQuery>; /// Holds a map of state machine heights to their verified state commitments #[pallet::storage] @@ -275,6 +274,7 @@ pub mod pallet { impl Hooks> for Pallet where ::Hash: From, + H256: From<::Hash>, { fn on_initialize(_n: T::BlockNumber) -> Weight { // return Mmr finalization weight here @@ -299,7 +299,7 @@ pub mod pallet { root } else { - H256::default().into() + H256::default() }; let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, root.encode()); @@ -324,6 +324,7 @@ pub mod pallet { impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Handles ismp messages #[pallet::weight(get_weight::(&messages))] @@ -456,6 +457,7 @@ pub mod pallet { impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Generate an MMR proof for the given `leaf_indices`. /// Note this method can only be used from an off-chain context @@ -464,8 +466,7 @@ where /// It may return an error or panic if used incorrectly. pub fn generate_proof( leaf_indices: Vec, - ) -> Result<(Vec, primitives::Proof<::Hash>), primitives::Error> - { + ) -> Result<(Vec, primitives::Proof), primitives::Error> { let leaves_count = NumberOfLeaves::::get(); let mmr = Mmr::::new(leaves_count); mmr.generate_proof(leaf_indices) @@ -549,7 +550,7 @@ where } /// Return the on-chain MMR root hash. - pub fn mmr_root() -> ::Hash { + pub fn mmr_root() -> H256 { Self::mmr_root_hash() } @@ -569,6 +570,7 @@ pub struct RequestResponseLog { impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Returns the offchain key for a request leaf index pub fn request_leaf_index_offchain_key( @@ -597,7 +599,7 @@ where pub fn get_request(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; + let data_or_hash = DataOrHash::decode(&mut &*elem).ok()?; return match data_or_hash { DataOrHash::Data(leaf) => match leaf { Leaf::Request(req) => Some(req), @@ -613,7 +615,7 @@ where pub fn get_response(leaf_index: LeafIndex) -> Option { let key = Pallet::::offchain_key(leaf_index); if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { - let data_or_hash = DataOrHash::::decode(&mut &*elem).ok()?; + let data_or_hash = DataOrHash::decode(&mut &*elem).ok()?; return match data_or_hash { DataOrHash::Data(leaf) => match leaf { Leaf::Response(res) => Some(res), @@ -736,7 +738,7 @@ where impl Pallet { /// Get a node from runtime storage - fn get_node(pos: NodeIndex) -> Option> { + fn get_node(pos: NodeIndex) -> Option { Nodes::::get(pos).map(DataOrHash::Hash) } @@ -746,7 +748,7 @@ impl Pallet { } /// Insert a node into storage - fn insert_node(pos: NodeIndex, node: ::Hash) { + fn insert_node(pos: NodeIndex, node: H256) { Nodes::::insert(pos, node) } diff --git a/pallet-ismp/src/mmr/mmr.rs b/pallet-ismp/src/mmr/mmr.rs index d3249c488..9ceee61c9 100644 --- a/pallet-ismp/src/mmr/mmr.rs +++ b/pallet-ismp/src/mmr/mmr.rs @@ -33,18 +33,20 @@ use sp_std::prelude::*; pub struct Mmr where T: Config, - Storage: mmr_lib::MMRStore>, + Storage: mmr_lib::MMRStore, ::Hash: From, + H256: From<::Hash>, { - mmr: mmr_lib::MMR, MmrHasher>, Storage>, + mmr: mmr_lib::MMR>, Storage>, leaves: NodeIndex, } impl Mmr where T: Config, - Storage: mmr_lib::MMRStore>, + Storage: mmr_lib::MMRStore, ::Hash: From, + H256: From<::Hash>, { /// Create a pointer to an existing MMR with given number of leaves. pub fn new(leaves: NodeIndex) -> Self { @@ -58,6 +60,7 @@ impl Mmr where T: Config, ::Hash: From, + H256: From<::Hash>, { /// Push another item to the MMR and commit /// @@ -71,7 +74,7 @@ where } /// Calculate the new MMR's root hash. - pub fn finalize(self) -> Result<::Hash, Error> { + pub fn finalize(self) -> Result { let root = self.mmr.get_root().map_err(|_| Error::GetRoot)?; Ok(root.hash::>()) } @@ -82,6 +85,7 @@ impl Mmr where T: Config, ::Hash: From, + H256: From<::Hash>, { /// Generate a proof for given leaf indices. /// @@ -90,7 +94,7 @@ where pub fn generate_proof( &self, positions: Vec, - ) -> Result<(Vec, Proof<::Hash>), Error> { + ) -> Result<(Vec, Proof), Error> { let store = >::default(); let leaves = positions .iter() diff --git a/pallet-ismp/src/mmr/storage.rs b/pallet-ismp/src/mmr/storage.rs index 34433b956..1b376a486 100644 --- a/pallet-ismp/src/mmr/storage.rs +++ b/pallet-ismp/src/mmr/storage.rs @@ -53,11 +53,11 @@ impl Default for Storage { } } -impl mmr_lib::MMRStore> for Storage +impl mmr_lib::MMRStore for Storage where T: Config, { - fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { let key = Pallet::::offchain_key(pos); debug!( target: "runtime::mmr::offchain", "offchain db get {}: key {:?}", @@ -71,21 +71,21 @@ where Ok(None) } - fn append(&mut self, _: NodeIndex, _: Vec>) -> mmr_lib::Result<()> { + fn append(&mut self, _: NodeIndex, _: Vec) -> mmr_lib::Result<()> { panic!("MMR must not be altered in the off-chain context.") } } -impl mmr_lib::MMRStore> for Storage +impl mmr_lib::MMRStore for Storage where T: Config, ::Hash: From, { - fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { Ok(Pallet::::get_node(pos)) } - fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { + fn append(&mut self, pos: NodeIndex, elems: Vec) -> mmr_lib::Result<()> { if elems.is_empty() { return Ok(()) } @@ -144,7 +144,7 @@ where T: Config, { /// Store a node in the offchain db - fn store_to_offchain(pos: NodeIndex, node: &DataOrHash) { + fn store_to_offchain(pos: NodeIndex, node: &DataOrHash) { let encoded_node = node.encode(); let key = Pallet::::offchain_key(pos); diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index c14d51629..f2923682f 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -107,8 +107,7 @@ fn should_generate_proofs_correctly_for_single_leaf_mmr() { let mmr_size = NodesUtils::new(proof.leaf_count).size(); let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = - MerkleProof::, MmrHasher>>::new(mmr_size, nodes); + let proof = MerkleProof::>>::new(mmr_size, nodes); let calculated_root = proof .calculate_root(vec![(positions[0], DataOrHash::Data(leaves[0].clone()))]) .unwrap(); @@ -139,8 +138,7 @@ fn should_generate_and_verify_batch_proof_correctly() { let mmr_size = NodesUtils::new(proof.leaf_count).size(); let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = - MerkleProof::, MmrHasher>>::new(mmr_size, nodes); + let proof = MerkleProof::>>::new(mmr_size, nodes); let calculated_root = proof .calculate_root( indices @@ -179,8 +177,7 @@ fn should_generate_and_verify_batch_proof_for_leaves_inserted_across_multiple_bl let mmr_size = NodesUtils::new(proof.leaf_count).size(); let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = - MerkleProof::, MmrHasher>>::new(mmr_size, nodes); + let proof = MerkleProof::>>::new(mmr_size, nodes); let calculated_root = proof .calculate_root( indices diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 538351646..4cacab5c3 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -77,6 +77,7 @@ where T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, T::Hash: From, + H256: From, { fn verify_consensus( &self, diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index 1e5acdefa..8edfe9221 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -78,6 +78,7 @@ pub mod pallet { impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Rather than users manually submitting consensus updates for sibling parachains, we /// instead make it the responsibility of the block builder to insert the consensus @@ -135,6 +136,7 @@ pub mod pallet { impl Hooks> for Pallet where ::Hash: From, + H256: From<::Hash>, { fn on_finalize(_n: T::BlockNumber) { let state = RelaychainDataProvider::::current_relay_chain_state(); @@ -170,6 +172,7 @@ pub mod pallet { impl ProvideInherent for Pallet where ::Hash: From, + H256: From<::Hash>, { type Call = Call; type Error = sp_inherents::MakeFatalError<()>; @@ -205,6 +208,7 @@ pub mod pallet { impl GenesisBuild for GenesisConfig where ::Hash: From, + H256: From<::Hash>, { fn build(&self) { let host = Host::::default(); @@ -221,6 +225,7 @@ pub mod pallet { impl Pallet where ::Hash: From, + H256: From<::Hash>, { /// Returns the list of parachains who's consensus updates will be inserted by the inherent /// data provider From 6e6272b290b2f48fe6f994921cd813ad9ae3c610 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 1 Aug 2023 19:55:57 +0100 Subject: [PATCH 159/182] Refactor mmr hasher (#80) --- grandpa/src/consensus.rs | 2 -- grandpa/src/lib.rs | 9 ++---- pallet-ismp/evm/src/precompiles.rs | 8 +---- pallet-ismp/primitives/src/mmr.rs | 13 +++----- .../primitives/state-machine/src/lib.rs | 6 +--- pallet-ismp/src/benchmarking.rs | 2 -- pallet-ismp/src/dispatcher.rs | 3 -- pallet-ismp/src/handlers.rs | 7 +---- pallet-ismp/src/host.rs | 6 +--- pallet-ismp/src/lib.rs | 24 +++----------- pallet-ismp/src/mmr/mmr.rs | 10 +----- pallet-ismp/src/mmr/storage.rs | 3 +- pallet-ismp/src/tests.rs | 6 ++-- parachain/src/consensus.rs | 2 -- parachain/src/lib.rs | 31 +++---------------- 15 files changed, 24 insertions(+), 108 deletions(-) diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs index fdaa26025..5c6c5d400 100644 --- a/grandpa/src/consensus.rs +++ b/grandpa/src/consensus.rs @@ -60,8 +60,6 @@ where T::Header: Header, T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, - T::Hash: From, - H256: From, { fn verify_consensus( &self, diff --git a/grandpa/src/lib.rs b/grandpa/src/lib.rs index 63cd96c57..a17ed4961 100644 --- a/grandpa/src/lib.rs +++ b/grandpa/src/lib.rs @@ -18,7 +18,7 @@ extern crate alloc; pub mod consensus; pub mod messages; -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; pub use pallet::*; use pallet_ismp::host::Host; @@ -28,7 +28,6 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use ismp::host::IsmpHost; - use primitive_types::H256; use primitives::ConsensusState; #[pallet::pallet] @@ -62,11 +61,7 @@ pub mod pallet { } #[pallet::call] - impl Pallet - where - ::Hash: From, - H256: From<::Hash>, - { + impl Pallet { /// Add some new parachains to the list of parachains in the relay chain consensus state #[pallet::call_index(0)] #[pallet::weight((0, DispatchClass::Mandatory))] diff --git a/pallet-ismp/evm/src/precompiles.rs b/pallet-ismp/evm/src/precompiles.rs index da800489e..6ecc00f28 100644 --- a/pallet-ismp/evm/src/precompiles.rs +++ b/pallet-ismp/evm/src/precompiles.rs @@ -19,7 +19,7 @@ use ismp_rs::{ router::{DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher, Post, PostResponse}, }; use pallet_evm::GasWeightMapping; -use sp_core::{H160, H256}; +use sp_core::H160; use sp_std::prelude::*; /// Ismp Request Dispatcher precompile for evm contracts @@ -37,8 +37,6 @@ pub const GET_REQUEST_DISPATCHER: H160 = H160(hex!("f2d8dc5239ddc053ba5151302483 impl Precompile for IsmpPostDispatcher where T: pallet_ismp::Config + pallet_evm::Config, - ::Hash: From, - H256: From<::Hash>, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let input = handle.input(); @@ -82,8 +80,6 @@ pub struct IsmpGetDispatcher { impl Precompile for IsmpGetDispatcher where T: pallet_ismp::Config + pallet_evm::Config, - ::Hash: From, - H256: From<::Hash>, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let input = handle.input(); @@ -130,8 +126,6 @@ pub struct IsmpResponseDispatcher { impl Precompile for IsmpResponseDispatcher where T: pallet_ismp::Config + pallet_evm::Config, - ::Hash: From, - H256: From<::Hash>, { fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let input = handle.input(); diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs index 20ae5d672..650887281 100644 --- a/pallet-ismp/primitives/src/mmr.rs +++ b/pallet-ismp/primitives/src/mmr.rs @@ -18,12 +18,12 @@ use core::fmt::Formatter; use codec::{Decode, Encode}; +use frame_support::sp_io; use ismp::{ router::{Request, Response}, util::{hash_request, hash_response, Keccak256}, }; use primitive_types::H256; -use sp_runtime::traits; /// Index of a leaf in the MMR pub type LeafIndex = u64; @@ -87,13 +87,10 @@ impl DataOrHash { } /// Default Merging & Hashing behavior for MMR. -pub struct MmrHasher(core::marker::PhantomData<(T, H)>); +pub struct MmrHasher(core::marker::PhantomData); -impl merkle_mountain_range::Merge for MmrHasher +impl merkle_mountain_range::Merge for MmrHasher where - T: frame_system::Config, - T::Hash: From, - H256: From, H: Keccak256, { type Item = DataOrHash; @@ -102,8 +99,6 @@ where let mut concat = left.hash::().as_ref().to_vec(); concat.extend_from_slice(right.hash::().as_ref()); - Ok(DataOrHash::Hash( - <::Hashing as traits::Hash>::hash(&concat).into(), - )) + Ok(DataOrHash::Hash(sp_io::hashing::keccak_256(&concat).into())) } } diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs index 596f09958..53652baad 100644 --- a/pallet-ismp/primitives/state-machine/src/lib.rs +++ b/pallet-ismp/primitives/state-machine/src/lib.rs @@ -37,7 +37,6 @@ use ismp_primitives::{ }; use merkle_mountain_range::MerkleProof; use pallet_ismp::host::Host; -use primitive_types::H256; use sp_runtime::traits::{BlakeTwo256, Keccak256}; use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; @@ -54,8 +53,6 @@ impl StateMachineClient for SubstrateStateMachine where T: pallet_ismp::Config, T::BlockNumber: Into, - T::Hash: From, - H256: From, { fn verify_membership( &self, @@ -68,8 +65,7 @@ where Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) })?; let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = - MerkleProof::>>::new(membership.mmr_size, nodes); + let proof = MerkleProof::>>::new(membership.mmr_size, nodes); let leaves: Vec<(u64, DataOrHash)> = match item { RequestResponse::Request(req) => membership .leaf_indices diff --git a/pallet-ismp/src/benchmarking.rs b/pallet-ismp/src/benchmarking.rs index 9cc6c1e2f..8469560b0 100644 --- a/pallet-ismp/src/benchmarking.rs +++ b/pallet-ismp/src/benchmarking.rs @@ -28,8 +28,6 @@ use frame_system::RawOrigin; /// modules using the [`crate::ismp_mocks::ModuleId`] as it's module id #[benchmarks( where -::Hash: From, -H256: From<::Hash>, T: pallet_timestamp::Config, ::Moment: From )] diff --git a/pallet-ismp/src/dispatcher.rs b/pallet-ismp/src/dispatcher.rs index d2d6186c2..71ea945ce 100644 --- a/pallet-ismp/src/dispatcher.rs +++ b/pallet-ismp/src/dispatcher.rs @@ -22,7 +22,6 @@ use ismp_rs::{ host::IsmpHost, router::{DispatchRequest, Get, IsmpDispatcher, Post, PostResponse, Request, Response}, }; -use sp_core::H256; /// A receipt or an outgoing or incoming request or response #[derive(Encode, Decode, scale_info::TypeInfo)] @@ -43,8 +42,6 @@ impl Default for Dispatcher { impl IsmpDispatcher for Dispatcher where T: Config, - ::Hash: From, - H256: From<::Hash>, { fn dispatch_request(&self, request: DispatchRequest) -> Result<(), IsmpError> { let host = Host::::default(); diff --git a/pallet-ismp/src/handlers.rs b/pallet-ismp/src/handlers.rs index 43f84283a..93cf4ea12 100644 --- a/pallet-ismp/src/handlers.rs +++ b/pallet-ismp/src/handlers.rs @@ -10,13 +10,8 @@ use ismp_rs::{ router::{Request, Response}, util::{hash_request, hash_response}, }; -use sp_core::H256; -impl Pallet -where - ::Hash: From, - H256: From<::Hash>, -{ +impl Pallet { /// Dispatch an outgoing request pub fn dispatch_request(request: Request) -> Result<(), IsmpError> { let commitment = hash_request::>(&request); diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 358d7308b..00c5a62a6 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -47,11 +47,7 @@ impl Default for Host { } } -impl IsmpHost for Host -where - ::Hash: From, - H256: From<::Hash>, -{ +impl IsmpHost for Host { fn host_state_machine(&self) -> StateMachine { T::StateMachine::get() } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 322c60ea9..7d2770794 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -271,11 +271,7 @@ pub mod pallet { // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] - impl Hooks> for Pallet - where - ::Hash: From, - H256: From<::Hash>, - { + impl Hooks> for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { // return Mmr finalization weight here ::WeightInfo::on_finalize(Self::number_of_leaves() as u32) @@ -321,11 +317,7 @@ pub mod pallet { } #[pallet::call] - impl Pallet - where - ::Hash: From, - H256: From<::Hash>, - { + impl Pallet { /// Handles ismp messages #[pallet::weight(get_weight::(&messages))] #[pallet::call_index(0)] @@ -454,11 +446,7 @@ pub mod pallet { } } -impl Pallet -where - ::Hash: From, - H256: From<::Hash>, -{ +impl Pallet { /// Generate an MMR proof for the given `leaf_indices`. /// Note this method can only be used from an off-chain context /// (Offchain Worker or Runtime API call), since it requires @@ -567,11 +555,7 @@ pub struct RequestResponseLog { mmr_root_hash: ::Hash, } -impl Pallet -where - ::Hash: From, - H256: From<::Hash>, -{ +impl Pallet { /// Returns the offchain key for a request leaf index pub fn request_leaf_index_offchain_key( source_chain: StateMachine, diff --git a/pallet-ismp/src/mmr/mmr.rs b/pallet-ismp/src/mmr/mmr.rs index 9ceee61c9..e53a011f4 100644 --- a/pallet-ismp/src/mmr/mmr.rs +++ b/pallet-ismp/src/mmr/mmr.rs @@ -34,10 +34,8 @@ pub struct Mmr where T: Config, Storage: mmr_lib::MMRStore, - ::Hash: From, - H256: From<::Hash>, { - mmr: mmr_lib::MMR>, Storage>, + mmr: mmr_lib::MMR>, Storage>, leaves: NodeIndex, } @@ -45,8 +43,6 @@ impl Mmr where T: Config, Storage: mmr_lib::MMRStore, - ::Hash: From, - H256: From<::Hash>, { /// Create a pointer to an existing MMR with given number of leaves. pub fn new(leaves: NodeIndex) -> Self { @@ -59,8 +55,6 @@ where impl Mmr where T: Config, - ::Hash: From, - H256: From<::Hash>, { /// Push another item to the MMR and commit /// @@ -84,8 +78,6 @@ where impl Mmr where T: Config, - ::Hash: From, - H256: From<::Hash>, { /// Generate a proof for given leaf indices. /// diff --git a/pallet-ismp/src/mmr/storage.rs b/pallet-ismp/src/mmr/storage.rs index 1b376a486..95628c376 100644 --- a/pallet-ismp/src/mmr/storage.rs +++ b/pallet-ismp/src/mmr/storage.rs @@ -18,7 +18,7 @@ use codec::Encode; use frame_support::log::{debug, trace}; use ismp_primitives::mmr::{DataOrHash, NodeIndex}; use mmr_lib::helper; -use sp_core::{offchain::StorageKind, H256}; +use sp_core::offchain::StorageKind; use sp_std::iter::Peekable; #[cfg(not(feature = "std"))] use sp_std::prelude::*; @@ -79,7 +79,6 @@ where impl mmr_lib::MMRStore for Storage where T: Config, - ::Hash: From, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { Ok(Pallet::::get_node(pos)) diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index f2923682f..855d90e06 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -107,7 +107,7 @@ fn should_generate_proofs_correctly_for_single_leaf_mmr() { let mmr_size = NodesUtils::new(proof.leaf_count).size(); let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = MerkleProof::>>::new(mmr_size, nodes); + let proof = MerkleProof::>>::new(mmr_size, nodes); let calculated_root = proof .calculate_root(vec![(positions[0], DataOrHash::Data(leaves[0].clone()))]) .unwrap(); @@ -138,7 +138,7 @@ fn should_generate_and_verify_batch_proof_correctly() { let mmr_size = NodesUtils::new(proof.leaf_count).size(); let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = MerkleProof::>>::new(mmr_size, nodes); + let proof = MerkleProof::>>::new(mmr_size, nodes); let calculated_root = proof .calculate_root( indices @@ -177,7 +177,7 @@ fn should_generate_and_verify_batch_proof_for_leaves_inserted_across_multiple_bl let mmr_size = NodesUtils::new(proof.leaf_count).size(); let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = MerkleProof::>>::new(mmr_size, nodes); + let proof = MerkleProof::>>::new(mmr_size, nodes); let calculated_root = proof .calculate_root( indices diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 4cacab5c3..730ab9c49 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -76,8 +76,6 @@ where R: RelayChainOracle, T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, - T::Hash: From, - H256: From, { fn verify_consensus( &self, diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs index 8edfe9221..f3a116d25 100644 --- a/parachain/src/lib.rs +++ b/parachain/src/lib.rs @@ -29,7 +29,6 @@ use cumulus_primitives_core::relay_chain; use ismp::{handlers, messaging::CreateConsensusState}; pub use pallet::*; use pallet_ismp::host::Host; -use primitive_types::H256; #[frame_support::pallet] pub mod pallet { @@ -75,11 +74,7 @@ pub mod pallet { pub enum Event {} #[pallet::call] - impl Pallet - where - ::Hash: From, - H256: From<::Hash>, - { + impl Pallet { /// Rather than users manually submitting consensus updates for sibling parachains, we /// instead make it the responsibility of the block builder to insert the consensus /// updates as an inherent. @@ -133,11 +128,7 @@ pub mod pallet { // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] - impl Hooks> for Pallet - where - ::Hash: From, - H256: From<::Hash>, - { + impl Hooks> for Pallet { fn on_finalize(_n: T::BlockNumber) { let state = RelaychainDataProvider::::current_relay_chain_state(); if !RelayChainState::::contains_key(state.number) { @@ -169,11 +160,7 @@ pub mod pallet { pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"paraismp"; #[pallet::inherent] - impl ProvideInherent for Pallet - where - ::Hash: From, - H256: From<::Hash>, - { + impl ProvideInherent for Pallet { type Call = Call; type Error = sp_inherents::MakeFatalError<()>; const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; @@ -205,11 +192,7 @@ pub mod pallet { } #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig - where - ::Hash: From, - H256: From<::Hash>, - { + impl GenesisBuild for GenesisConfig { fn build(&self) { let host = Host::::default(); Pallet::::initialize(host); @@ -222,11 +205,7 @@ pub mod pallet { } } -impl Pallet -where - ::Hash: From, - H256: From<::Hash>, -{ +impl Pallet { /// Returns the list of parachains who's consensus updates will be inserted by the inherent /// data provider pub fn para_ids() -> Vec { From ce0bdece7cb09d58fbc0688052f7b35695e6b84b Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 7 Aug 2023 19:13:03 +0100 Subject: [PATCH 160/182] Ismp updates (#81) --- Cargo.lock | 4 ++-- pallet-ismp/src/host.rs | 28 +++++++++++++++++++++++++++- pallet-ismp/src/lib.rs | 7 +++++++ pallet-ismp/src/mocks/ismp.rs | 11 +++++++---- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7df07eb1..e41adf992 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3386,7 +3386,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59d83711eec3f567c5a5984d032fca2abbf2d559" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#317cb2ffc16d4d5b0b50dfee282728bf70fc0641" dependencies = [ "derive_more", "parity-scale-codec", @@ -3639,7 +3639,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#59d83711eec3f567c5a5984d032fca2abbf2d559" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#317cb2ffc16d4d5b0b50dfee282728bf70fc0641" dependencies = [ "ismp", "parity-scale-codec", diff --git a/pallet-ismp/src/host.rs b/pallet-ismp/src/host.rs index 00c5a62a6..1f836b228 100644 --- a/pallet-ismp/src/host.rs +++ b/pallet-ismp/src/host.rs @@ -18,7 +18,7 @@ use crate::{ dispatcher::Receipt, primitives::ConsensusClientProvider, AllowedProxies, ChallengePeriod, Config, ConsensusClientUpdateTime, ConsensusStateClient, ConsensusStates, FrozenConsensusClients, FrozenHeights, LatestStateMachineHeight, Nonce, RequestCommitments, - RequestReceipts, ResponseReceipts, StateCommitments, UnbondingPeriod, + RequestReceipts, ResponseReceipts, StateCommitments, StateMachineUpdateTime, UnbondingPeriod, }; use alloc::{format, string::ToString}; use core::time::Duration; @@ -71,6 +71,20 @@ impl IsmpHost for Host { }) } + fn state_machine_update_time( + &self, + state_machine_height: StateMachineHeight, + ) -> Result { + StateMachineUpdateTime::::get(state_machine_height) + .map(|timestamp| Duration::from_secs(timestamp)) + .ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Update time not found for {:?}", + state_machine_height + )) + }) + } + fn consensus_state(&self, id: ConsensusClientId) -> Result, Error> { ConsensusStates::::get(id) .ok_or_else(|| Error::ConsensusStateNotFound { consensus_state_id: id }) @@ -116,6 +130,18 @@ impl IsmpHost for Host { Ok(()) } + fn store_state_machine_update_time( + &self, + state_machine_height: StateMachineHeight, + timestamp: Duration, + ) -> Result<(), Error> { + StateMachineUpdateTime::::insert( + state_machine_height, + timestamp.as_secs().saturated_into::(), + ); + Ok(()) + } + fn store_state_machine_commitment( &self, height: StateMachineHeight, diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 7d2770794..2f307e084 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -221,6 +221,13 @@ pub mod pallet { pub type ConsensusClientUpdateTime = StorageMap<_, Twox64Concat, ConsensusClientId, u64, OptionQuery>; + /// Holds the timestamp at which a state machine height was updated. + /// Used in ensuring that the configured challenge period elapses. + #[pallet::storage] + #[pallet::getter(fn state_machine_update_time)] + pub type StateMachineUpdateTime = + StorageMap<_, Twox64Concat, StateMachineHeight, u64, OptionQuery>; + /// Commitments for outgoing requests /// The key is the request commitment #[pallet::storage] diff --git a/pallet-ismp/src/mocks/ismp.rs b/pallet-ismp/src/mocks/ismp.rs index 890684ed8..81adcdd10 100644 --- a/pallet-ismp/src/mocks/ismp.rs +++ b/pallet-ismp/src/mocks/ismp.rs @@ -136,13 +136,16 @@ where }, ) .unwrap(); - - set_timestamp::(1000_000_000); - StateMachineHeight { + let height = StateMachineHeight { id: StateMachineId { state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), consensus_state_id: MOCK_CONSENSUS_STATE_ID, }, height: 3, - } + }; + host.store_state_machine_update_time(height, core::time::Duration::from_millis(1000_000)) + .unwrap(); + + set_timestamp::(1000_000_000); + height } From 788d2454763ed2faf8f58f34e96602b57700ab75 Mon Sep 17 00:00:00 2001 From: Doordashcon Date: Tue, 8 Aug 2023 11:02:10 +0100 Subject: [PATCH 161/182] Asset import (#82) import asset w3f badge --- README.md | 2 +- assets/web3 foundation_grants_badge_white.png | Bin 0 -> 21335 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 assets/web3 foundation_grants_badge_white.png diff --git a/README.md b/README.md index f1cb0ca8f..19e89d407 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Implementation of the Interoperable State Machine Protocol for substrate runtimes. This project is [funded by the web3 foundation](https://github.com/w3f/Grants-Program/blob/master/applications/ismp.md). - + ## Overview diff --git a/assets/web3 foundation_grants_badge_white.png b/assets/web3 foundation_grants_badge_white.png new file mode 100644 index 0000000000000000000000000000000000000000..55a57af9e6d9149741eb5758a4b5ff6603aeda56 GIT binary patch literal 21335 zcmeFZi#yZr{|7#&(t*w*bWjvIgq)2^D2J4@m_y4UjF7XLq~z_?K{-#^kh^8L^Gc_`US`{r-sGb$xeT>~-C{_qw02`@Wy|^L{=ak9$eCZ<&hhJ+T)A z0*TzbaorjO5+Z{@{EouAfRYH*xpTnVo`*M_LO>uf*_}VWo7U%+L7>y1o7b<}M&vDy zGwfzeIEMZk(JXN5Aq&0u2d0x0CfSP#9-+6RX`W0_}l7?m627vHz|v<+n-yyV!fMNnq#V z3Lk&?zk&-_CPn{U2nmzj&E37ppp7;z{;_#Gnl@!ZO_O^RaDluUyD$j6$mt!X)Fe1i091-fpCa_8ja+E6$MQ@59_G9|kcEGJsbZc2dmMg|B5R@~l< z5h#{4Xs5}4ZM~R`51LdyfpJTxIw__#JwLBkFzq#9*&AW#r@!ZyzSb?t&`K3o-XKEh z1Vt;ku=Uh(_Ubyhympt-EuKUtzIXpd9cGpK{#XgXWr4W20Ew9m{!8{?5$ODrk3aWfRUYIp`QADlcr2=9=UeNt2W=NdSZjZ0|!1$Z4?Lq#TriOs@*p%*DhIg zaD$IKpnGBIFXz~rjjK9o=y9rLc>i$-j65-H*;fYcuI3wr%-0uFIG&dD7m1f^}or#VQt!OPyG@1 z`ONi3`}-l><>A{{l(US6B?ecwy4Z(9V9u0Be-4nxNz>B@2a!>-LIal zZ6x}9LuOA}CTrte3R@WusLlDo9?{3RTv}V$-ThK1Bgl7e>e%ABo=);szKB1UynfQp z2cc!STFZ>qb|`{P2$L!brgwwh8BdC~X}TCY{)KdzA>=o+88X_D{xM=wucGD_6b^FF z3>&Y9lPi6Zsmrl#l_Oq1HV&#oa!J~$#z}rEsw5USf_ISmfPO9>Q=5F;?Q{G*W`xzK zdU1n0Ol`3ej7DWikoO7SjMLE+lftMV7^865NB$KTuVZ2L1CR>b*fht+>uofhh{qA9 zn*(UJr?$!KQ6c{Ig}0mh^26z2=5$J3sIpzZ7bP@hgB`M+kD_~0q z=})ua|F?-A|X+g|`7=?20BG5K7)Wq`{8 z9sFQ~^e-=Z5NSxPn`qq28sldAqQY_?)swPn*`eKA1+v%zZY5cp{rCTUEktDh7Hw13 zC-{GfBrFbEPY}Ak&3S*pMs*C{h+Vp{{em;0;3cPWB$R07EURP99IeO_;i6Y4g(i8X z-A?OPU{p)>U1Ho^ZTDS6uB_@l?0-&8-A+HynhIxz0_c%7xJ?f>rTj&v4W9+&Js;ZG;Lwm1=>@%I?uk*U1Kcimgtnt%^512hrRf1=41zcJA?snxp ze8AF3IUWsHJ)P%fs^_zHku|U2bxcKTIooJWXvWTzRLlwf(d2`$f|Kbm(b4L;hFiJ~ zs$S0UW=?6Kzh-@)^597|EwnG)*t}GngtVJ>L>@*_Fk8_-9D$T;)~y=L55j~Wsg6u= z^p@tEI9gHjH!}ia3c4fH^*B@{+j#6)Rv6EPNc8P~XP{-&FkgE|%=5<0Y!-Wyv!KUO zn#%KkmV=%idz*Vfv+*1(X0ah5=3MVYFUiI4evYCgPOV} z-y>Q$X1J*B9InocHX+8X@C+|s4;Of1n)x~C?1SMPk?OqtptciT4|ls4f=wo|d--A& zwe@Nq9Mj#DC=B%)*3+xIbx8<(aY= zW`5F;8Zy_KVT8W=!jDJ-CcfcH@R;hY?ttAO$JcGraYiAOtfW}6-_ghdrsH{48qa3% zCGN0(jgZk-M%Z%eE%&kgqt7xDA0Q~Y#eo5GgYf~+a*YJYFifEIs#?Iv5SObu_VD#p zdT`tNPoWx1T82n@deu1FC{3@v3f43~LVcg3^<-ElvqU_&^$)i)*W|%R-G^=s6T3m~ zB4!<+ho1`04(6CfCelQt2#HzDiA}|kgfr^zNV6x0gK|e)J6b}G{BCG(NPXGL>Mgsw z&u$o1ODZHbRyjdgk(gbZw#mZ{HtI`(dMx!&`VTPpk{5-LR;X}fM0C`CVa^JJ3nmbm zkyetT#7{Ip3n22lVsA{XLI^CBT198ji`o0Ck>N%>0 zz^`Nn!;L7tQzxolz5gBJM?%xu`g*&Wk#3aBt&Ba5wDa-4XwK?+0RznF0jt#IjoMK4 zKQbZEqW^;WUHro&QRTUXK@$VJ?WDdHrsixyNM*ZHy0L7+dfxzLb$ESofx%6~mPeNF z1G)19m;_-)_1Y7=k!7Qd1bLXok>zx}_w3hm;tS_cZF5D(mRc=@+XHm3HP}9kIX2zO zB0X5)7s`j<{rd_Ib~$>aW(9TR82PH z+(3Zc+Bd7z?SLf;^x}`L!G1>l>M+{&nlH|KW#YwFfU9W^ynM%%1Sv5*jvbfpVb|QD zu)Jfg_v`0%f608YNjT-J;@A3k% z54r{J8tKIQuVK>SSc+z98t|lEY+%Np^}dL0*`Cok>fqy`kZ0RA(|OL<^)wm>>Tz}z z-Gx(Gg-~ZBuVNKv^<@%IBc1W!*(|-miOz6Kf^|(fHh*P>;U*f=&|%}+NMnNTaW~z7 zx=Kt{;`Kk#L%Pp0O%UC>V?MKlG|8LYyoD2NNRHv^Vea?Vbc19=jS^i zU7@LEktwq*v~7B6WZrHFOm8isd8~-_%kk`Q9Tuad-jJ9r$+sF2Tp>SH3Igpv_i*>5 zQrU5*@xiHNt3&U|j|Aa;!<&UQv@}qixq`mrktT?|oK@yvgD<@gTzb(z zkU{K=Cl;56l?Nd{0<|^oA|$nCeu745K%`ghLEfUyPMD!^Dz!52Dii z90InH>I8eeCSA@qu(zr4)hjh{3$YRDe5=dPcW^mM@c zFb0}QqZzCq1FbPwM^M^)Z>S}&Ywn1oQ4bUZiURV$N!(k>yCX<@6=Chq_Lm7N$C^&< zy*eIHeC=;&23ahzc$8iC%K!vQ+3D3yBCP8&Pm{mYMIs?IuU!U?Ae`mWjraY2(Usz% z?Sm2~lJ<4+gD$-UpvEmCv}-Ak;E%ISCCl5R{%k+*)eX$xk29*t$=)s;9ynb#Mr(`R z7}HM7O~$+4U6cg9+YhW0A0fusDZm2g=RzzG*sMMj<0Aj_%j5D+S-!;iTM}nLalu=~ zN__7ez(;s89X^#?Jos-f?$PQ*VU@rxP5r3u((|Ha)ifUR_}kCQUji-{DrAjq$u@qE z^>Ymm_`rDciVt+*M^PE5rodQ`QK&f>8wwuZ$0rV?x>CD<)-6!wI3U7sF(UP|kS-Z) z6muuRuF(Q^O1FE|;@Rnvz5~tn4Tn$elqT+!PUw5J7Q!A5b2Ei0fo`ok@a$p&@c5b) zq7#Pxe{2eXWLl|i2U^wxoFa23oT%9*W>R2mM)M=d`r4@oMCQm5q6FSC{~|&TyIqxDJa~#tA=DoNjl?4!+y`lk zuL_U$YF*Bi9>E$5&O)^I;3EKxh7n}r5t`aG8svs#O=mi|34!9ymOj%1E#f>Ayb-2x zvi^zFHGA>f3Y#B0e8o0S=@rZ?`|k!#cB_4p2C)VzRdd01&-k=mDz0n}>|$@(xR~B@ zYehMsngaN~DNYZun!II;Kyi3df|95^bj9}GN62F8r2{X z7wF69t!>}ge&kH}zeflDjkHFIEkq7_>`EgpxNMhE{}`C|vDO#zw?oBdorA5uY#x0d zO=&Qj&na`}9wrQ79|PTOY6J`gMm_xnuPfzt#1XGUv8DR$%t?clSdv{OqfZOECDVWr zS#9?w)8Dm-fI$8`oJK=+LAPu0`|Ix8v%~izo{#>9dLs-?vka0Ct3g<2{$SzEWA8rq zCedNEJ;y;2s|tXJ`giMJt3_E%?viRGE3TrFitT%3Y-v$<=gxiIge6RrNha|djt8Krvy|%* z$qsHC)rs*a9J^S$v8sY{vb&ry6lxHF8IFxURj^N=e&?%VQh8Mc(GvB#ASsrRO< z(VCW)I@kFpf9&{vbF*f2d6??uV*VsT!3m0dru6%3$G&{kP4;v>7oSnSGK0Tex^kCJ z;db@v8)L|<4CB!R)Cj?I&ScaEO0nh<&xgVK2Mm<;(W)xFca5GGq1TL<6Pikhj@?P3*{m%}Fb+oD$>laBna%A#r{4 zFwZ`U;(I_ndv>az`Rj8;2T~Y|DvVbFq5#qGZK~-I;g`B9!fz`q8gJqf(hl#20 zx$AvC-v&eXfQDH+G*NQ%p?j~|nQ_&1{K$oXV^?A<{MRUcn=9LS5>F_|L-t^~ z`2R{RE)9l>eSs6hI4#%P>H@Ogkgf}J(e>S{>kL~`G5LFMaeP(AAiFdk6{`3l#V(Vf z&aFY(I;U%_>8htUxHVc3o8b=Tyx*7WVJi1h(;G0?KRHT{Q9^ytx7_M`b3?oDof@9U z(6rrXe$b^R;zqtks7qB$61)o%7{UeHm%Evz_d7`uH*&VCSC4YhOB%{VFUn^u!#9

ll^uwyoBJcf3^$_i2j{EociK!<4Wp{;f z@~*G1Yu{t7iKDdNx(%E`*b!o7koWxLgQkh)u&4c#B|5en<*LYW)n>v0&X9+}0wg>MNG zZQd)S8(flFaQ0!!_6#6Fl@Ba_W>KcpLr9h8X2tO{V{&4$-Py*u`QBsxvlyks zpJj&1_y#LCLs4nxPH_xGt0{+cmzDBlp-s#Z#+9PK(8F)Um0pPzKiJ%RuWJ@R3rV41 zb1!+QBYgi0W1qyufj(7( zM=Jxyo3WNmME%^oY0$D^Mh4$xm0q&px?+5X=D5j~K2M*vx^9NKkbhaV!yy+X6Tz1b zE4;E`z4JoezPRfTBPNbW*{y6DCm59(RA^pGj_I%c;VXS%{D5L8bNX=)*?_i(L`bP> z7W~Pq2;W+XO|-h;KW3gXj+HdGgL?cL)N%AIh<~tv+4D$xI`YP+$YXo~0fl(&&V(xH zSC&H0cccWZ-stX?(BTk~=vSg9ul_$RfRD?QXDJ_0kBMD}i^E+{Xbp471h#=EmXRff z6>>KPqH&8cbFqRDQK^4?v909e*RL+i<7_yj*zH*w14*|#WbsG{iXQy~eNr(NvS_N{ z!p$1(Rqcri5PO8C>y(6p8R?-C>Uz1=Zc})7g-ew~T1SKV#;`d|L2p`G6{?_*muaH& z8mhLo=XHt8?~$@8%)Mary6yn#BSYrsc;FEmI()N)1Ywm?Hg4_?-)j|n!43YlrNIbG9QRevO8*yheojRd+9sLev zMT~UoOkHu26qPr96+&6fxK>DxrKUAnEqNdKU6w>&)EJf@Cxe;p<;q z&{8(#a;sbL6x6Kk?sx5lC`HqMR8<7+fwhT<2aRA>*bRn7t)z@E0p0tK8yJc9iwG}w#3)SIWy zlf$fGXr=Ra)M#5Eze7}(7I6+b+wg{zX!J@q{MkZt+S<>0NSU9}l8I*_7th~LcUao> zO*Pju;FbQ-n7eawHeY460)k5>;yTA;y1^oRpjKesOFfB%&Mh#q4Npoeec8{hy7BZj zLk+Q9+JPJVZRDGazJ1otujyjn57>F;D9tSO3)$e~M==D%)B!VS4bvI68_eC7@|0dO z*_FyDO0f@MEnt5#{gB;+j?{s{$Z#KPYLDFPm@;urmYW{bFyw7FCuJ}{nx0Vx)jtko zKIed4t?1|jC=k03pE*bS3Er~QY0R#ZFI%pq(de8e2s0QHtITF99BP`0fBB2gMB%p1 ze0D8G_86^UEDkSUM%ROYdhlyNgG+MH&%E!n7I9mo=0+BAophL;w=?G?$DjS z$#L4O#c+&h=@t+64TlqLf*;pepSi9eJkN>J6KCx0_-C8#kL(H<*CD~G*cE~0Bp^~m ze45id|8sFf%{Ox#mWgz)kEzKJZznb>j*%Y_hY?ofRyL|pQ8rl>coSn3OG?Kjndu{k}{R(N;xx-%7T zw2*gNg&7?=oTKi|tCl`S*l) z?eft(%_#q#zZZ|;)U~!ELD>Ilur%suAob4PPn|bQaKEdZ@b#;`?KVarkRL$0*I4yT z9hk&HH8Dv2#XqDMG_e^COA1m%WI-vM#23N>5FNQyamu`2Z(9=cX$aC!K5B7pv?}Nuv31;juH@N=-@id&YA3HWcre zYadIav>%ay0qpT3p>;dC_UfV*Q-x2Qtsw_ZZ~yFjb3Y*+0w+Y}U~1HRVCuammnbCNlR{C;n>| z%qrWU(tPN%5hp6T41`JUKZkhq`9XwCufB= z-#lR}`DY+hU^AJ|gKLxU*CBA`?pr;4WAN+4p#N;Fcg4YSjDKu>3Rt*R8J|!nX?1L7 ztIvyTjq0Ge*HmnpyNUl|#swmBJd)!bdtjF3VPm#C7xM}ema&Kn z?U@}Y$YxQPm57=o_}FeVkU~WjkX)75cgH;ev={C+UaHAaOaj^MtZ#`m-ll7q+fXa6 z#I3q0RV|}$DU?9#dRQ&9e&gw>GTx_gZ!f;^?yr>N=Y`lRjrmB>#p80t91jSj*iY2h zfix5hGq4YEhEL-IMp-Sy-YW=C2s@vBKKfo)WCgb;%elai97Q036Rt| zAOaK*p+230{>DQ092Wha3CKMno}_<2^`v<&DifKVc|f4he*G_yhPde-PkI`yrHz=8XX0Fwwu%TI)#gw0FK{PMsTi zOZPImf$Ml7!@!apv-lb#uU7wgKtBi!K3W#5Cm#XtfSnJY5NNx)BgE)3S<>BJ{*7cj{4*R9X|e8DiUD(qvnLXf&xds?$jO`20okBW44s8NTN z&^4T=SBck~Ki|05%d)MxgBkl?v*D_J)OL{~h1k-WG}xzA3wLhcfJxcdy3MuK!-UT& z!90frN8-52()P`gw@#f;ekp?8@GwP9T)s&&(9Kn!~%BK>zk|hR}7gt*Pvsverheze}9R#=ka*Miyj0-M*}< zy>-chf{ZdVooR^9o+qUXUBeh#VP+;s=Nq%LXY5*Vx0=A|nm2?HR$!h3Q}79t8)(05 z(rNJXY2(g^ZLQCZiNrUOHa2FKvv}LkD}D+yNUEV_)_25E$m?@#H0WUs5R)E?7u?h2 zwgB^$@ZiR!oPFud`}h1hWnv&3G=w@;I4n$DP*X#@SGD{9;yi21f@91wSY`0}2UKc7 z9FthU4tcvWS~kg;ZHupRIaS!s478BQ^xU*qntQmU?uQNuvACdYic1fk5;cFjxaqkGJQbyRkdklGRjs+FeKs)^mO{H-8_$-yzq^50RT-|O&i4k6q zsxu2-)P}uu5^wykj}_aVLh}4fD9o;D)ol~XaDXju?8KDAgqWctEkr9mW_>TSEog}S z`3FPbD=A7}dNqDZ3tUg85SS&^RSTt>v5a}#dv3RdTL= zMKc)WAgQ?ak!~e-vP%t^XrN+xn|l)U3cJB!`YnJ#;`byxcFhhJizr8L9i(bk#iwkq zNsWes)ip`a{8zaeM(hE{ZywJ}h3_rE-P*AtdrqC2iW&&NMV9l*kNqilMP((VTNBbw z71_gy{j+B+M+}VW$FSyAU03mbqv}vhSnI6&MJD09?sNsCEPl-VLw>=B)Z+r&C65`! znbm^xmwZEE3sEh~>fE)AKb;spt)tdB1QA@ATe3Fm-)19Jj~Qlh@NA>GoR#j1>dmvE zFrV^3?B!~kPVE9u7e+ISCfnawxVvkAFB59!i?Qj&sS;N);uLnOrWwEPp~ztbl)ekVR#vVrOOS8E=07ZDjJs(QI-faR_cLt~OcRx{HI!82{GV}06) z=eZ}#rhA^&toqP?Q6cbe?)e6l;B<1erO?vQvuY}kxs`jTP&rW-fR$15J&@%r4lktl zQp(FYnqF`BkjkR)Uz*Qc3CKmWpEdpG#6|}T(l~Wq7sh7QdK#^V_mC>^DSa-H+4A*toIsRtD1D0Oe0!NW^obCO+}|sH;H)3J5sdH6Y`F!taCubUV72cZ zk+^EjvyT>+Xb@=ovV+D4kc}O+l1HNwZQZiFJD)C~*}=hvnbCotxLH@DAfG~Ar=)b0 zT<*^6H|WVeivCaHh0V1(-4sP>IpWp7&HHMWJ|z1v4r^Ulz~&x)L^&=TP`z*B#+Wx- z^<;>~c;ba2SJ}WY-tb7N?Q=5ovt?#-fw6gG)l#Fi)&;|TBp|s1e7pv%a_B@1$+7D| z^Ng6cOSmQnCU;tqdH;KeU^WHsXa#revFYk52wavz|99!ZV)x@x;k3D@0oM8e91Ih5 zE;Be)=f-@k=4~1!z|iC@5;UItOk5wehBq#WlweFIx9x}jM5IZ2Q>y#JbA770c*ncc zJnmucqluAHGFHTCa!qFoH%SDO7rkaF^5Radd>M`4eXXv4rr?C}Ecew*Dw5j*~ z2<8UsZQ%_)n9GMTm>tCe4Ql}um%w0Mdea70sjra$fbNI)0Wc!2nE&sd!C7@*T z#twwLYVRo;n?A|&bq71_6t)4RgnRTYV0Qge8&cLRNIR6t3LxQc3yAUQio#v`$IOTV zR6gig^vD(G)?{5qg%d-oR#<7Loj72GUa+n0hC=P^D0X)BpUM|V<>dwrcFYW388c=_ zkD0}c4R~n>RMuAf$4+PRUHYzeZ3>FoJE#JEIY4c%&HqmORuSG2k>tdv;5BKP0fz+K zae%p53nTn&n(!zniS7YG!+lVsZx4mKGYzknZ`bs4T#;0rnN zo68lU)65sCv+e%~mc9R!sAqOBg{Qu2{rr!iYT9T;LCq@nf-fcs-ncR|%pEXh} zt;=D?-mx2+-{rl-d;xMDfJgZEU0NHjST&a??)9)&=8@Z*+{o>PakU+`?oIBF0oVRj zs%YaWbq%|kptAw3qx=V~Q&25k02szN=)a8vY}b-Xt?-}kk`<-~et`URX$Ug_zHXR9 z-(d}pY!>1LCP9__zfXPW*%*~{*<97}DymM~PW)u&_KWK?@IBzb5ul!EuEfr>{FGQC z-cg%7c>L=4=Sb*qbeXZeo6IAGpVnod&O6~96|7c;5Ey|hU!WYiu#bcRn>@&O>e@p5 z*+d52D!RYJoz?7cnA|Dz4D)Q$jib!=M9hEL^F*Nr=7dc7&c`PKt!*E3i<{q%T8VKlI_SI`%`daNOExUO&5O6JiHXSfS+oErHGA$?r>J6?yy ze3fJUYGV8;TZNXL+iG2%6uok@Vsx>vqMQ@n<%Jt+c2-TU;o1B?kc;E&t+A}AKZ*@& zadGAYxm%U}7QM13{7!D`V0`|5^>kzJ-XcMZ2eRIsJ1*7#Zz5LxI_n&M;wt8f6woy5 zIp9>HMy-Dq7nrvS0Xy5a4gr1_>umDFwb@{YsJxv8)L`{0USoxzMu7^G4|n_(p(u0T zaJ*0E6HudV*N$7W7e*Hu%pF2eN*U^WR!23bUrYqG*`KE4NdYt?D zJTv;Y%^`=ajV%^+n8T^>tP9eI6grhxnct9R!xz0X$t&ZUX-dphqvD==GS*I-Fv#Y~ zql28%ldTE5gGE8S1z#?QGgNO_wps+B#NngIlkrSHF+!`t0e0VrJ*75%qY^o70ofgl zvs#km4l+?Xa3}*Cgyd`M=k9Q%Q4*jA>g8*!^xePaC@q}5q`JMz$|f1&QGhtSyu7^A zJ}yMkBa}oPiS1Chyn&i-#iU&+o*;S>ngt+~!nzd6JJ8M9KPefbB{KvrUaQ9L#w+FI8H9;to=c@$1r}Ra<;@3>vSt*GV z&yd#&-0to2@U{1OK!-!ZUs9X|5eQ^tY+ie!^x`>pD9 z#jrnZ)q$~;h#JAUnMhG?ekM>a$4!1YUE#Cn>^zyI?V8^U^xQ$_KirQuze|O+W7G_ zS3V6k04=w5%McH&Pgh+sJIS@nE2VMDpqf!hep3YS1uG$tB#_O<)p@0k1|ZV%?%+xG ziQ>g#y}Z`tuAvfL|H9^Kra$c$O?=b!!lUYRRC{;bYw5%R?|$WCy3NBdr{x?jcKP+5 z;<4?|eP|+UpuL8z5CF)N{>^bUv}sLEMg zEm*NJM!0@Yt&nFfP@l$s_tgKQItYnzTRPEio7f6X5BG;HmkdU(dKgh}mSLL~9t>pS z>nqT=eOlcgEF~4Ng^))&yY{cmp)Wo1A7qk4h>I)@Rpy4Jp)taLihUHcm;yYBdk*<) zIH9emjP&yEIucNiC2;}BOzX0aH2TKx=HwEC)V!7ayO%Y>yAB-EW_ITxjmm66kHGFF z(F@v`!F|tMW%-LZcxUC9n54eI8}A}tW@+U44rI7Ev!!i@B7)BohlhTAHy~=vrDvAn zYeVbVFSg3>-RV5zC-8ABa#3esD9tF~_7EYrxj*bgHFjDW(-%b9uu;i~RIfdiGYq+0 zf88yQMP6Umcfa#H^0TS(;#PRCW_edo!)(tghYiSS*Q0K>Ao893*B`ZsjGZAq0BK(U zrmA@0NrY6v`K@(*IevCG?PB2a;rI3qeG{{F-X(#s@wV1Z2vm~_^*e*7ronrqSHJy` zXh{BKKH+(z3B0C_YmcQi*mX)%I?|;>Ut%(Z$<$i+dzrPcaR)P>#niaa{k-W}UBFAy zV27r)j<;`Jde`+cYk5@xyN$l1f<;OoD76&$Y{|2Pmiq5;5@Uu3^7d?v3b2_?d2>eVIB3!x z*m#1z<>OBg5XbLUik27japmI+-ve6z-^0Dt*k%cpL)Vq{%$f#gcQ>=Z$J$ye@AOO= ze8nrDf66}@^=!3|*W9saQ~&gK)F1Cuoe~9XxWsf_XCl~9yur6COFGIww2CztWGCcj ziAW0J=*Jcgddxzn^ByqRkygSSsEa9HilG)il99|8+TN@7CFB_K{UCaxSQO)?J3&gI zX>ep5Iu=%c00$a0Eu;gJoG76eCPJkHqSF|bbSqMD`w=B7nCh^k3jhnIfOH`ewQjVc zdh(1__w^^CYeM!=f&MX1!RXwzUs*48O^|ZyeG<)pMDm4??~BzsujNqm7x)Qi!TLnv z^*zrGdckznk82%r@cB_(2DJc=a5xQ;j-D=gi;-uqo;VXY)D-z@@3_@2^8FulVx~r* zwSM6Irg*eX$=efWRgR=-F+&*p6bqs>|LP;U7Oe)TFSX(eqw$>v(3v38am70Qv2A%IbiPop0cRF@L-Rzk>%JQy|+YypS_iR5Z$vl@ZQZTKpkZl z!oxhEO-Pl^7QFo=7Z5!T*Xz``4AlynAjFu`O_0Vkdn?f#jg2Cv?%icCxAte`Tt$KsDiUXRIiJN($v0iWGp{(1> z`W;F>kvSu5GP8olB}ApvT`rq0yw|Y>-v&@X4WP=P)r9#AfY2bT+X#{xdhL%#maAD}xtJw(fJerd)aQ-a>EY3tC0`%GQW(FV|;0 zXAKgd#x|bYj16xw&9y_r%gzqSI7##aG@#%;PrrIQG+QPbplDseKBN zseDe-ya5;cA9fko-E!!2DZjwTGZ-Kt?X~zGN;lWmOAgC}`PBB5J&sH&JzBDmTibd< z5}~yKrVwa-{OiB?9(Cxdrx9VDFv!^jdT8p)*J-7y!Cbq)aszS!j`V>h*ULzcVsi^i zaL-_`A^m5l2#A#fRF}w((rgAOo(J>MM>L34k1bS9Z@f91RKGJn*ubd zJs{AEkCu=Rr7T7{4-v_<18J)P^DWJ(Rd@kCy14j$Fe@gC`G$v@mq_cx`$_(`?Wh%8 zO7AadJUjOH+}ByJw#}oYCVBl2EeXG%bu+Ke(3YG7&iwpE$lZ61q_sPQTYtbaBkaLM zb66{-ADi`5^qZp<`BJv7+pC-7w%&?3N>Ge-uXS@VFc*ll@IUK(}i6%DUZ%C}vDH4(l_A7l4FdfjK9;oOt>pPU7 z$kdH-6RoV;HeVwu^UnjRAf_Qn>c0jw2y}R5=XU`p2=qbRcp+NI-rV3lDv*d&_22@# z23i%elMfX57EqK)#e06ZwV?7fEbY2-g1pn&sKRR%P8s)pIK6qkafZJw#DjZloKVlM zyc1ti?cb~H-ISTpETLJ?;)$arj~AzFHMX+4apdl{SHxWSKC;TL1bPMY5H?lP)&We6 z{2wea;(!t+9sR>v?*C=ABRLi@*ILYm~yf*x8Z?PVFs7M8n4#hO*Y>SFcS9E0XBBTVC}8IGm?Y8;Am9VrN;iviKMzj=@{ONA+Q|qG2z84w-4f0wI%15+h@gt?9o1nJqfMWu#v~I)?M1C>?n<+f=l-9F-tkh)?kCEO-~{5PWrZ@WtA% zRLrgP?7K7@?=w`d9{>_MfG<$7^akT{gMYJ2$X=Y~`%`G_B3d;wcfz4 z46|%wNrvz5@jP-?K`<}MPs@~V(h`8NY94g13vdiqOg(+&q+9GVv+pk1b&UaMN3<(U zl1dEOmt%U~oX)mJZ7GgjIPV}jbue|$dhnT*?>QbG$Qgo-m0RE#S2&6Qh`R2KDb5BY zYSjgQVRN${7_cI&qP;#DGE?#uuKgin^V^v|HzF&eq%*N0kq*JosBuXLl=OHzA4t;z z2u*T>v(hK0)9G-YzzNou)woMMh`z*M@!vy{&5F zg443`fr=;ht%Qz-xH<$uI94vD%J*87rYY4^&8%obR>$~ras#nRmZ8PgE6 z5sDL7Q_0_Ta#Kn%Z;MHt!1U= zf>L!D!hh1bVI|~tMyeXb(n0z+CDOlw)*N&AHx05$h7mNWs%acEWXpXoY~YwVZGGaq z2xTxxX(|iQ9`%CN^BRB4u(Xc#4HLKr3DFeCl!$)qz-ODWTl&G8+fFeEUyAQ3bt-~n zzSeyTfYwsE;^t*5+wx=UE#8%bMC4!?AVmg5*zXchd{_6sa7g@EyztV^^hTo<6F8Z4 zt>U$#{|a#(%QacXrd+vrEDaD*W3J)K_L(o4>8%Gu6Z=m>c<@ta+?Jm8B--OX3oM@L zQPVQhr0WMQeNOkQq!rZ3lm3z;n&0`Q5GM#5sP5@1Hoc^8dv{Aai$!l~eV%(lz5=>k zamT()bb`9lQQsYylv|b>-ggO`wuDI%H(!zhGXMo7}aUYmtu?~FXUGnwa~ zjvB15(49R02#}at0}^w0+_=U5Z`K|mZt!KoS9Kjyfn;bSE%datEHvk&OkVS%_A?is zH+A#V7BEaTFE`-BspA5^&IcYmp$d!88Vu?3k^PU3PpdZ8(2+x0kKU$E(=NVTEe>*_ zHJg1#5Q%0@ft9d_6R1zV#M%k}rUR#-DOH#k0SZ1R|32y};T9R3r>$t*R~^Y*M_06K zfJ3W(F#Ai=)S0VLq%%Yl-YOK=y1b(?W(tCA6r;rsO+N&Ce!bh_bdG?c49I-mg z#2AE|o%G|X=a}VHINaH80nAV57ST2%10Xm6v;=-{r_Gk1JzZb@9$$;L>{5=sDYyd7 z`(A{HY$>(Ofr_^xMhTKP2mu->ZhE`wmcEBqG+k%q{#UAif}sGY<*F|*xbj~s=r_Pc z2sL-r);t%>ncW_Gm+l1MSs8u~$7F+nGhOA>5I#`YEg*mU_-H7rxWz@!%_3_yHoGio z$!qVWA!tbfhnvNROEaGH`fc8;aRlT}1w!zZ%=0MW#yPobAC-Yq<)@A`_o~&W``MU- zLAK!JF4xe?-F@B?TK~^jf;Zo8eamp*9&pr@AvS0;_syOdhJTuQbzjbk&2^P*|2=QR z-|aq<(hY{|jLdn$7ygX@_*-S`j;vh4+rNy3x9$INmxV!wse2id!Bp4a{_~d`3R*tB zTg>Bp&b#J*?P9jyHgU1s>$lmuZdh7p-LCcZc(-eCx%&2stjo@K-elX^ZtZy;zUoit z<$hb^SBwnbfJ^TiRu$#Tmar)@{!xz}llmU(W2kZ`$FMs}S@t#Am2W5Bm z-TZv0EMaok!XM{Z^QG*LPUe2T{vIR4jbva&zrd)uU_Eg2_LukSEbsLNx717jU6p3N zfp>c0(y#`KE3YXtxqO=wzwiPxYFXx4Zi${0&3Qb(Pte zW^ImU5d~`Kn*bbhH+!vj>e!u(bRgd%kEy)j|N6bv=6TXwOd49dnWn!HieS3Qc(w5g zc**EJLuspN0AwvKq=n>@KTc{XSV(KTnFh35*(z@|q?9+YJ` zw_SSS-v_#&K-$X!?AFh@oHP6HQE8A5nyeXW&OGLL>wgcp{e)qLFfhpf`vHx5%>jy( z4{|`=_rBY3zXfs_{;L3k>;5vq8|K?UlLHO?0zlRMk2~H}y9a>y=Yjl)-FI(fl!L9d z=mr)GQge@){k~=fo?H+FN>4s6c=P)*Q1o5_x@213tlZ7k;GnGmI<#SFmE6SI>_b4= z1F9VF7<5jW)QNxIcCpSN5-9Xw5=h5`w;Zp|3*MFmg)PH6C*b0|d2f&DC(rW{1oC&7 z0S&1xmYN6+iXEyz|9yW{*z#>}C(wlq+dY8lr}=Kq{T8nV(tpPYs6M(x256}jkb7V+ z&<8t2FaL-=6P;l{0~F&0`9Mq8y*0U=2JzI6XrPJLH%U+Ydy)kx-JlB6vE*{cn`lTF zECqUUN6Yib3{YB#Pyrgh-l+G$zKv%=US87(oZWeAq4fKnUc#o%*RN;eKVVtclb^d8IEllsS`Ann0fEDV-R>ak7#covz5~$=0zi8~w1NUug#+|t zvj$*_1OY~1%z*$C(7hnQ0<;SRIDp{_0;2|uhQVk$7|jc#Hhjn4V2G3UHx3vIVCg!0Dtnxg8%>k literal 0 HcmV?d00001 From c8076521257382b61b4eddd13c4a2674b8fa5db2 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 8 Aug 2023 16:51:06 +0100 Subject: [PATCH 162/182] Bump alloy dependencies (#83) chore --- Cargo.lock | 29 ++++++++++++++++++----------- Cargo.toml | 3 --- pallet-ismp/evm/Cargo.toml | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e41adf992..91db2f8a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,9 +190,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffce219323b389e69f7448bc41a1913cc96dc584ca4bdae98bc46440fffdd3e" +checksum = "66f73f11dcfbf8bb763d88fb1d082fe7cca0a00d3227d9921bdbd52ce5e013e2" dependencies = [ "alloy-rlp", "bytes", @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eec1ae936329818ebb681464819d01d4d4381039505775baa476bec440b3a95c" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" dependencies = [ "dunce", "heck", @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6871798f0c7ae860604b1bbbefb27593eb397094b3536a167c02ff25cd6b788b" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -6521,16 +6521,23 @@ dependencies = [ [[package]] name = "ruint" -version = "1.9.0" -source = "git+https://github.com/recmo/uint.git?rev=e34200e114ac8fe93cf8e815e92601860ae4d7b8#e34200e114ac8fe93cf8e815e92601860ae4d7b8" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" dependencies = [ + "proptest", + "rand 0.8.5", "ruint-macro", + "serde", + "valuable", + "zeroize", ] [[package]] name = "ruint-macro" version = "1.1.0" -source = "git+https://github.com/recmo/uint.git?rev=e34200e114ac8fe93cf8e815e92601860ae4d7b8#e34200e114ac8fe93cf8e815e92601860ae4d7b8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" [[package]] name = "rustc-demangle" @@ -10582,9 +10589,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008a3d3dd194a54c0577395903600f51c5bcbf23d95b0243805caa7003dacfcf" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index b3ba9ab50..88cbb0839 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,3 @@ members = [ [workspace.dependencies] enum-as-inner = "=0.5.1" -#todo: remove on alloy v0.4.0 release -[patch.crates-io] -ruint = { git = "https://github.com/recmo/uint.git", rev = "e34200e114ac8fe93cf8e815e92601860ae4d7b8" } diff --git a/pallet-ismp/evm/Cargo.toml b/pallet-ismp/evm/Cargo.toml index d43aabe09..19944fadc 100644 --- a/pallet-ismp/evm/Cargo.toml +++ b/pallet-ismp/evm/Cargo.toml @@ -15,8 +15,8 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v # EVM support pallet-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } fp-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } -alloy-sol-types = { version = "0.3.0", default-features = false } -alloy-primitives = { version = "0.3.0", default-features = false } +alloy-sol-types = { version = "0.3.1", default-features = false } +alloy-primitives = { version = "0.3.1", default-features = false } # polytope labs From ab46e4cd5237773e90aec6eede8feac2fe278eb3 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:38:35 +0100 Subject: [PATCH 163/182] Generic signature verification, clonable prover (#27) * generic signature verification * cargo update --- Cargo.lock | 601 ++++++++++++++++++------------------------ primitives/Cargo.toml | 2 +- prover/Cargo.toml | 2 +- prover/src/lib.rs | 1 + prover/src/test.rs | 9 +- verifier/Cargo.toml | 2 - verifier/src/lib.rs | 21 +- 7 files changed, 278 insertions(+), 360 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38ed99daa..c7cd1afab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,12 +4,12 @@ version = 3 [[package]] name = "actix-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ "gimli", ] @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] @@ -65,9 +65,9 @@ source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee67 [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "arrayref" @@ -77,9 +77,9 @@ checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "as-any" @@ -106,7 +106,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.28", ] [[package]] @@ -117,9 +117,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ "addr2line", "cc", @@ -138,9 +138,9 @@ checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] name = "base2" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd838cfd751f573f3ce2c7a959df55eed90f5cbdcfbacd1acf77eaffd51daa8c" +checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" dependencies = [ "int", ] @@ -153,9 +153,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -169,6 +169,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitvec" version = "1.0.1" @@ -207,9 +213,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-slice-cast" @@ -231,9 +237,12 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -243,9 +252,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "core-foundation" @@ -274,9 +283,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -305,9 +314,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "der" @@ -330,9 +339,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", @@ -353,9 +362,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -366,7 +375,7 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.6", + "digest 0.10.7", "ff", "generic-array", "group", @@ -420,13 +429,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -476,12 +485,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "ff" @@ -516,9 +522,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -580,9 +586,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -591,9 +597,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "group" @@ -608,9 +614,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -642,18 +648,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -663,9 +660,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-literal" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hmac" @@ -673,7 +670,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -706,9 +703,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -718,9 +715,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -733,7 +730,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -755,9 +752,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -784,20 +781,11 @@ dependencies = [ "hashbrown 0.12.3", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "int" -version = "0.2.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719740841ea8a9c2df2da3d37adb237fa85121ef91ef7e0aeda5afb2c79a2a85" +checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" dependencies = [ "num-traits", ] @@ -811,33 +799,21 @@ dependencies = [ "num-traits", ] -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", + "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -851,15 +827,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -873,14 +849,14 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -893,9 +869,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linked-hash-map" @@ -905,15 +881,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.1" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -921,12 +897,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" @@ -954,23 +927,22 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -998,9 +970,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" dependencies = [ "core2", - "digest 0.10.6", + "digest 0.10.7", "multihash-derive", - "sha2 0.10.6", + "sha2 0.10.7", "unsigned-varint", ] @@ -1059,37 +1031,37 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] [[package]] name = "object" -version = "0.30.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -1099,11 +1071,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.49" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -1120,7 +1092,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.28", ] [[package]] @@ -1131,9 +1103,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.84" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ "cc", "libc", @@ -1143,9 +1115,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ "arrayvec", "bitvec", @@ -1157,9 +1129,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1179,28 +1151,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -1220,9 +1192,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" @@ -1266,18 +1238,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -1320,27 +1292,30 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "regex" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ - "bitflags", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "regex" -version = "1.7.3" +name = "regex-automata" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", @@ -1349,17 +1324,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -1413,9 +1388,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hex" @@ -1425,38 +1400,37 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.37.8" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aef160324be24d31a62147fae491c14d2204a3865c7ca8c3b0d7f7bcb3ea635" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" @@ -1474,11 +1448,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1487,9 +1461,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -1497,29 +1471,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.159" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -1565,22 +1539,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -1599,7 +1573,7 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core", ] @@ -1614,9 +1588,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -1628,6 +1602,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spki" version = "0.6.0" @@ -1671,9 +1655,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -1688,9 +1672,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -1734,7 +1718,6 @@ version = "0.1.0" dependencies = [ "ethereum-consensus", "log", - "milagro_bls", "ssz-rs", "sync-committee-primitives", ] @@ -1759,15 +1742,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1781,22 +1764,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.28", ] [[package]] @@ -1816,11 +1799,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -1828,20 +1811,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.28", ] [[package]] @@ -1856,9 +1839,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -1867,9 +1850,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -1907,9 +1890,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1934,9 +1917,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1961,9 +1944,9 @@ checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1984,11 +1967,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2000,9 +1982,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2010,24 +1992,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -2037,9 +2019,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2047,28 +2029,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -2105,147 +2087,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 8fbf3d9a7..b5af4614e 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Polytope Labs"] [dependencies] ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false } -hex-literal = { package = "hex-literal", version = "0.3.3", default-features = false } +hex-literal = { package = "hex-literal", version = "0.4.1", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive" ] } diff --git a/prover/Cargo.toml b/prover/Cargo.toml index e81fafe4e..5ec1cae68 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -18,7 +18,7 @@ actix-rt = "*" tokio = { version = "1.18.2", features = ["full"]} tokio-stream = { version = "0.1.8" } async-stream = { version = "0.3.3"} -base2 = {version="0.2.2", default-features=false} +base2 = {version= "0.3.1", default-features=false} env_logger = "0.10.0" [dev-dependencies] diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 863550ff7..a519aca7b 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -58,6 +58,7 @@ pub type BeaconStateType = BeaconState< MAX_TRANSACTIONS_PER_PAYLOAD, >; +#[derive(Clone)] pub struct SyncCommitteeProver { pub node_url: String, pub client: Client, diff --git a/prover/src/test.rs b/prover/src/test.rs index 3618cb63e..9acc97fdd 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -15,7 +15,7 @@ use sync_committee_primitives::{ types::{AncestorBlock, FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, util::compute_fork_version, }; -use sync_committee_verifier::verify_sync_committee_attestation; +use sync_committee_verifier::{verify_sync_committee_attestation, SignatureVerifier}; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; @@ -437,8 +437,11 @@ async fn test_prover() { ancestor_blocks: vec![], }; - client_state = - verify_sync_committee_attestation(client_state.clone(), light_client_update).unwrap(); + client_state = verify_sync_committee_attestation::( + client_state.clone(), + light_client_update, + ) + .unwrap(); println!( "Sucessfully verified Ethereum block at slot {:?}", client_state.finalized_header.slot diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index debc638e3..e381b0445 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -8,14 +8,12 @@ authors = ["Polytope Labs"] sync-committee-primitives = { path= "../primitives", default-features = false } ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" , default-features = false } -milagro_bls = { git = "https://github.com/sigp/milagro_bls", default-features = false } log = { version = "0.4.17", default-features = false } [features] default = ["std"] std = [ "ssz-rs/std", - "milagro_bls/std", "log/std" ] testing = ["sync-committee-primitives/testing"] diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index fa40bc9ad..56958c01c 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -9,6 +9,7 @@ use crate::error::Error; use alloc::vec::Vec; use ethereum_consensus::{ bellatrix::{compute_domain, mainnet::SYNC_COMMITTEE_SIZE, Checkpoint}, + crypto::{PublicKey, Signature}, primitives::Root, signing::compute_signing_root, state_transition::Context, @@ -33,8 +34,13 @@ pub type LightClientState = sync_committee_primitives::types::LightClientState; +/// Verify sync committee signatures +pub trait BlsVerify { + fn verify(public_keys: &[&PublicKey], msg: &[u8], signature: &Signature) -> Result<(), Error>; +} + /// This function simply verifies a sync committee's attestation & it's finalized counterpart. -pub fn verify_sync_committee_attestation( +pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { @@ -107,8 +113,7 @@ pub fn verify_sync_committee_attestation( let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain); - // todo: should be generic - ethereum_consensus::crypto::fast_aggregate_verify( + V::verify( &*participant_pubkeys, signing_root.map_err(|_| Error::InvalidRoot)?.as_bytes(), &update.sync_aggregate.sync_committee_signature, @@ -399,3 +404,13 @@ pub fn verify_sync_committee_attestation( Ok(new_light_client_state) } + +pub struct SignatureVerifier; + +impl BlsVerify for SignatureVerifier { + fn verify(public_keys: &[&PublicKey], msg: &[u8], signature: &Signature) -> Result<(), Error> { + ethereum_consensus::crypto::fast_aggregate_verify(public_keys, msg, signature)?; + + Ok(()) + } +} From cf72347b32bd5a4d92e76b9d7cafa23d22fa22c0 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 21 Aug 2023 15:21:31 +0100 Subject: [PATCH 164/182] Return canonical ISMP events from RPC (#84) rework events rpc --- Cargo.lock | 4 +- pallet-ismp/rpc/src/lib.rs | 75 ++++++++++++++++++++++++++++++++++++-- pallet-ismp/src/events.rs | 11 ++++-- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91db2f8a9..78a03f588 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3386,7 +3386,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#317cb2ffc16d4d5b0b50dfee282728bf70fc0641" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#3a85db0d968eab5a88427a19a2a612332caec23f" dependencies = [ "derive_more", "parity-scale-codec", @@ -3639,7 +3639,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#317cb2ffc16d4d5b0b50dfee282728bf70fc0641" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#3a85db0d968eab5a88427a19a2a612332caec23f" dependencies = [ "ismp", "parity-scale-codec", diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index e21c781f9..baa676e3c 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -30,6 +30,7 @@ use ismp_primitives::{ }; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, + events::{ChallengePeriodStarted, Event, StateMachineUpdated}, router::{Get, Request, Response}, }; use ismp_runtime_api::IsmpRuntimeApi; @@ -135,7 +136,7 @@ where fn query_events( &self, block_numbers: Vec>, - ) -> Result>>; + ) -> Result>>; /// Query pending get requests that have a `state_machine_height` <= `height`. #[method(name = "ismp_pendingGetRequests")] @@ -301,7 +302,7 @@ where fn query_events( &self, block_numbers: Vec>, - ) -> Result>> { + ) -> Result>> { let api = self.client.runtime_api(); let mut events = HashMap::new(); for block_number_or_hash in block_numbers { @@ -314,10 +315,76 @@ where } }; - let temp = api + let mut request_indices = vec![]; + let mut response_indices = vec![]; + let mut temp: Vec = api .block_events(at) .ok() - .ok_or_else(|| runtime_error_into_rpc_error("failed to read block events"))?; + .ok_or_else(|| runtime_error_into_rpc_error("failed to read block events"))? + .into_iter() + .filter_map(|event| match event { + pallet_ismp::events::Event::Request { + source_chain, + dest_chain, + request_nonce, + } => { + let query = + LeafIndexQuery { source_chain, dest_chain, nonce: request_nonce }; + let indices: Vec = + api.get_request_leaf_indices(at, vec![query]).ok()?; + request_indices.extend_from_slice(&indices); + None + } + pallet_ismp::events::Event::Response { + source_chain, + dest_chain, + request_nonce, + } => { + let query = + LeafIndexQuery { source_chain, dest_chain, nonce: request_nonce }; + let indices: Vec = + api.get_response_leaf_indices(at, vec![query]).ok()?; + response_indices.extend_from_slice(&indices); + None + } + pallet_ismp::events::Event::ChallengePeriodStarted { + consensus_state_id, + state_machines, + } => Some(Event::ChallengePeriodStarted(ChallengePeriodStarted { + consensus_state_id, + state_machines, + })), + pallet_ismp::events::Event::StateMachineUpdated { + state_machine_id, + latest_height, + } => Some(Event::StateMachineUpdated(StateMachineUpdated { + state_machine_id, + latest_height, + })), + }) + .collect(); + + let request_events = api + .get_requests(at, request_indices) + .map_err(|_| runtime_error_into_rpc_error("Error fetching requests"))? + .into_iter() + .map(|req| match req { + Request::Post(post) => Event::PostRequest(post), + Request::Get(get) => Event::GetRequest(get), + }); + + let response_events = api + .get_responses(at, response_indices) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response"))? + .into_iter() + .filter_map(|res| match res { + Response::Post(post) => Some(Event::PostResponse(post)), + _ => None, + }); + + temp.extend(request_events); + temp.extend(response_events); + events.insert(block_number_or_hash.to_string(), temp); } Ok(events) diff --git a/pallet-ismp/src/events.rs b/pallet-ismp/src/events.rs index 6822f462a..fff810e96 100644 --- a/pallet-ismp/src/events.rs +++ b/pallet-ismp/src/events.rs @@ -17,7 +17,7 @@ use crate::{Config, Event as PalletEvent}; use alloc::collections::BTreeSet; use ismp_rs::{ - consensus::{ConsensusClientId, StateMachineHeight, StateMachineId}, + consensus::{ConsensusStateId, StateMachineHeight, StateMachineId}, host::StateMachine, }; @@ -34,8 +34,8 @@ pub enum Event { }, /// Emitted when a challenge period has begun for a consensus client ChallengePeriodStarted { - /// Consensus client id - consensus_client_id: ConsensusClientId, + /// Consensus state id + consensus_state_id: ConsensusStateId, /// Tuple of previous height and latest height state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, }, @@ -72,7 +72,10 @@ pub fn to_core_protocol_event(event: PalletEvent) -> Option Some(Event::Request { dest_chain, source_chain, request_nonce }) } PalletEvent::ChallengePeriodStarted { consensus_client_id, state_machines } => { - Some(Event::ChallengePeriodStarted { consensus_client_id, state_machines }) + Some(Event::ChallengePeriodStarted { + consensus_state_id: consensus_client_id, + state_machines, + }) } _ => None, } From f290b0da7ad1bd321b19474edce1f9bd86b0b248 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:12:19 +0100 Subject: [PATCH 165/182] Attributions (#85) atribution --- pallet-ismp/primitives/src/mmr.rs | 2 ++ pallet-ismp/src/lib.rs | 8 -------- pallet-ismp/src/mmr.rs | 2 ++ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs index 650887281..5f265fe8e 100644 --- a/pallet-ismp/primitives/src/mmr.rs +++ b/pallet-ismp/primitives/src/mmr.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// This file contains code adapted from https://github.com/paritytech/substrate/blob/master/frame/merkle-mountain-range/src/mmr/mod.rs + //! MMR utilities use core::fmt::Formatter; diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 2f307e084..38f5211c6 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -107,14 +107,6 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Prefix for elements stored in the Off-chain DB via Indexing API. - /// - /// Each node of the MMR is inserted both on-chain and off-chain via Indexing API. - /// The former does not store full leaf content, just its compact version (hash), - /// and some of the inner mmr nodes might be pruned from on-chain storage. - /// The latter will contain all the entries in their full form. - /// - /// Each node is stored in the Off-chain DB under key derived from the - /// [`Self::INDEXING_PREFIX`] and its in-tree index (MMR position). const INDEXING_PREFIX: &'static [u8]; /// Admin origin for privileged actions diff --git a/pallet-ismp/src/mmr.rs b/pallet-ismp/src/mmr.rs index 4bc7fdec3..64a241023 100644 --- a/pallet-ismp/src/mmr.rs +++ b/pallet-ismp/src/mmr.rs @@ -13,6 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// This module contains code adapted from https://github.com/paritytech/substrate/blob/master/frame/merkle-mountain-range/src/mmr/mod.rs + pub mod mmr; pub mod storage; pub mod utils; From cd1437ab4c66fa5b118266dffc127bab6de66d34 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Fri, 25 Aug 2023 07:11:59 +0100 Subject: [PATCH 166/182] bump subxt (#86) --- Cargo.lock | 544 ++++++++++++++++++++++++++++++---- grandpa/prover/Cargo.toml | 2 +- grandpa/verifier/Cargo.toml | 2 +- grandpa/verifier/src/tests.rs | 1 - 4 files changed, 491 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78a03f588..36dfe554d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,6 +351,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -447,6 +456,32 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-executor" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg", + "blocking", + "futures-lite", +] + [[package]] name = "async-io" version = "1.13.0" @@ -476,6 +511,42 @@ dependencies = [ "event-listener", ] +[[package]] +name = "async-net" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" +dependencies = [ + "async-io", + "autocfg", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +dependencies = [ + "async-io", + "async-lock", + "autocfg", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 0.37.23", + "signal-hook", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + [[package]] name = "async-trait" version = "0.1.71" @@ -500,6 +571,12 @@ dependencies = [ "pin-project-lite 0.2.10", ] +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + [[package]] name = "atomic-waker" version = "1.1.1" @@ -630,6 +707,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bip39" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +dependencies = [ + "bitcoin_hashes", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -645,6 +731,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitcoin_hashes" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" + [[package]] name = "bitflags" version = "1.3.2" @@ -678,6 +770,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq 0.1.5", +] + [[package]] name = "blake2b_simd" version = "1.0.1" @@ -769,6 +871,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + [[package]] name = "bounded-collections" version = "0.1.8" @@ -796,6 +913,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.6.0" @@ -1090,6 +1216,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "constant_time_eq" version = "0.2.6" @@ -1566,6 +1698,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + [[package]] name = "darling" version = "0.14.4" @@ -1981,9 +2126,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -2519,6 +2664,18 @@ dependencies = [ "serde", ] +[[package]] +name = "frame-metadata" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + [[package]] name = "frame-support" version = "4.0.0-dev" @@ -2526,7 +2683,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "bitflags 1.3.2", "environmental", - "frame-metadata", + "frame-metadata 15.1.0", "frame-support-procedural", "impl-trait-for-tuples", "k256", @@ -2809,10 +2966,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -2961,6 +3116,9 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "serde", +] [[package]] name = "heck" @@ -3285,6 +3443,12 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + [[package]] name = "inout" version = "0.1.3" @@ -3337,6 +3501,12 @@ dependencies = [ "webrtc-util", ] +[[package]] +name = "intx" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f38a50a899dc47a6d0ed5508e7f601a2e34c3a85303514b5d137f3c10a0c75" + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -3961,7 +4131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" dependencies = [ "asn1_der", - "bs58", + "bs58 0.4.0", "ed25519-dalek", "either", "fnv", @@ -4057,7 +4227,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" dependencies = [ - "bs58", + "bs58 0.4.0", "ed25519-dalek", "log", "multiaddr 0.17.1", @@ -4523,6 +4693,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "lru" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" + [[package]] name = "lru-cache" version = "0.1.2" @@ -4689,6 +4865,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "mick-jaeger" version = "0.1.8" @@ -4983,6 +5171,18 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -5446,6 +5646,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -5591,7 +5800,7 @@ name = "polkadot-node-metrics" version = "0.9.42" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" dependencies = [ - "bs58", + "bs58 0.4.0", "futures", "futures-timer", "log", @@ -5637,7 +5846,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "polkadot-primitives", - "schnorrkel", + "schnorrkel 0.9.1", "serde", "sp-application-crypto 7.0.0", "sp-consensus-babe", @@ -5922,9 +6131,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -6108,9 +6317,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -6420,7 +6629,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -6680,6 +6889,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ruzstd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc" +dependencies = [ + "byteorder", + "thiserror-core", + "twox-hash", +] + [[package]] name = "rw-stream-sink" version = "0.3.0" @@ -7098,7 +7318,7 @@ dependencies = [ "sp-version 5.0.0", "sp-wasm-interface 7.0.0", "tracing", - "wasmi", + "wasmi 0.13.2", ] [[package]] @@ -7123,7 +7343,7 @@ dependencies = [ "sp-version 18.0.0", "sp-wasm-interface 12.0.0", "tracing", - "wasmi", + "wasmi 0.13.2", ] [[package]] @@ -7136,7 +7356,7 @@ dependencies = [ "sp-wasm-interface 7.0.0", "thiserror", "wasm-instrument", - "wasmi", + "wasmi 0.13.2", ] [[package]] @@ -7150,7 +7370,7 @@ dependencies = [ "sp-wasm-interface 12.0.0", "thiserror", "wasm-instrument", - "wasmi", + "wasmi 0.13.2", ] [[package]] @@ -7163,7 +7383,7 @@ dependencies = [ "sc-executor-common 0.10.0-dev", "sp-runtime-interface 7.0.0", "sp-wasm-interface 7.0.0", - "wasmi", + "wasmi 0.13.2", ] [[package]] @@ -7177,7 +7397,7 @@ dependencies = [ "sc-executor-common 0.20.0", "sp-runtime-interface 15.0.0", "sp-wasm-interface 12.0.0", - "wasmi", + "wasmi 0.13.2", ] [[package]] @@ -8228,7 +8448,7 @@ dependencies = [ "base58", "blake2", "either", - "frame-metadata", + "frame-metadata 15.1.0", "parity-scale-codec", "scale-bits", "scale-decode", @@ -8269,7 +8489,7 @@ dependencies = [ "arrayvec 0.5.2", "curve25519-dalek 2.1.3", "getrandom 0.1.16", - "merlin", + "merlin 2.0.1", "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", @@ -8277,6 +8497,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "schnorrkel" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "844b7645371e6ecdf61ff246ba1958c29e802881a749ae3fb1993675d210d28d" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "curve25519-dalek-ng", + "merlin 3.0.0", + "rand_core 0.6.4", + "sha2 0.9.9", + "subtle-ng", + "zeroize", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -8421,9 +8657,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.102" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -8515,6 +8751,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -8584,6 +8830,23 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "smol" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + [[package]] name = "smol_str" version = "0.2.0" @@ -8593,6 +8856,87 @@ dependencies = [ "serde", ] +[[package]] +name = "smoldot" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cce5e2881b30bad7ef89f383a816ad0b22c45915911f28499026de4a76d20ee" +dependencies = [ + "arrayvec 0.7.4", + "async-lock", + "atomic", + "base64 0.21.2", + "bip39", + "blake2-rfc", + "bs58 0.5.0", + "crossbeam-queue", + "derive_more", + "ed25519-zebra", + "either", + "event-listener", + "fnv", + "futures-channel", + "futures-util", + "hashbrown 0.14.0", + "hex", + "hmac 0.12.1", + "itertools", + "libsecp256k1", + "merlin 3.0.0", + "no-std-net", + "nom", + "num-bigint", + "num-rational", + "num-traits", + "pbkdf2 0.12.2", + "pin-project", + "rand 0.8.5", + "rand_chacha 0.3.1", + "ruzstd", + "schnorrkel 0.10.2", + "serde", + "serde_json", + "sha2 0.10.7", + "siphasher", + "slab", + "smallvec", + "smol", + "snow", + "soketto", + "tiny-keccak", + "twox-hash", + "wasmi 0.30.0", +] + +[[package]] +name = "smoldot-light" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2f7b4687b83ff244ef6137735ed5716ad37dcdf3ee16c4eb1a32fb9808fa47" +dependencies = [ + "async-lock", + "blake2-rfc", + "derive_more", + "either", + "event-listener", + "fnv", + "futures-channel", + "futures-util", + "hashbrown 0.14.0", + "hex", + "itertools", + "log", + "lru 0.10.1", + "parking_lot 0.12.1", + "rand 0.8.5", + "serde", + "serde_json", + "siphasher", + "slab", + "smol", + "smoldot", +] + [[package]] name = "snap" version = "1.1.0" @@ -8991,7 +9335,7 @@ dependencies = [ "bitflags 1.3.2", "blake2", "bounded-collections", - "bs58", + "bs58 0.4.0", "dyn-clonable", "ed25519-zebra", "futures", @@ -9001,7 +9345,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", "paste", @@ -9009,7 +9353,7 @@ dependencies = [ "rand 0.8.5", "regex", "scale-info", - "schnorrkel", + "schnorrkel 0.9.1", "secp256k1", "secrecy", "serde", @@ -9046,14 +9390,14 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", "primitive-types", "rand 0.8.5", "regex", "scale-info", - "schnorrkel", + "schnorrkel 0.9.1", "secp256k1", "secrecy", "serde", @@ -9080,7 +9424,7 @@ dependencies = [ "bitflags 1.3.2", "blake2", "bounded-collections", - "bs58", + "bs58 0.4.0", "dyn-clonable", "ed25519-zebra", "futures", @@ -9090,14 +9434,14 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", "primitive-types", "rand 0.8.5", "regex", "scale-info", - "schnorrkel", + "schnorrkel 0.9.1", "secp256k1", "secrecy", "serde", @@ -9124,7 +9468,7 @@ dependencies = [ "bitflags 1.3.2", "blake2", "bounded-collections", - "bs58", + "bs58 0.4.0", "dyn-clonable", "ed25519-zebra", "futures", @@ -9134,7 +9478,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", "paste", @@ -9142,7 +9486,7 @@ dependencies = [ "rand 0.8.5", "regex", "scale-info", - "schnorrkel", + "schnorrkel 0.9.1", "secp256k1", "secrecy", "serde", @@ -9489,10 +9833,10 @@ checksum = "811b1f0e8fc5b71fa359f5b4b67adedeba5dc313415e2923f8055e72c172a6ce" dependencies = [ "async-trait", "futures", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", - "schnorrkel", + "schnorrkel 0.9.1", "serde", "sp-core 18.0.0", "sp-externalities 0.18.0", @@ -9537,7 +9881,7 @@ name = "sp-metadata-ir" version = "0.1.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "frame-metadata", + "frame-metadata 15.1.0", "parity-scale-codec", "scale-info", "sp-std 5.0.0", @@ -10190,7 +10534,7 @@ dependencies = [ "log", "parity-scale-codec", "sp-std 5.0.0", - "wasmi", + "wasmi 0.13.2", "wasmtime 6.0.2", ] @@ -10205,7 +10549,7 @@ dependencies = [ "log", "parity-scale-codec", "sp-std 7.0.0", - "wasmi", + "wasmi 0.13.2", "wasmtime 6.0.2", ] @@ -10220,7 +10564,7 @@ dependencies = [ "log", "parity-scale-codec", "sp-std 7.0.0", - "wasmi", + "wasmi 0.13.2", "wasmtime 6.0.2", ] @@ -10291,6 +10635,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.6.0" @@ -10421,7 +10771,7 @@ checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" dependencies = [ "hmac 0.11.0", "pbkdf2 0.8.0", - "schnorrkel", + "schnorrkel 0.9.1", "sha2 0.9.9", "zeroize", ] @@ -10486,19 +10836,24 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "subxt" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a734d66fa935fbda56ba6a71d7e969f424c8c5608d416ba8499d71d8cbfc1f" +checksum = "0ba02ada83ba2640c46e200a1758cc83ce876a16326d2c52ca5db41b7d6645ce" dependencies = [ "base58", "blake2", "derivative", "either", - "frame-metadata", + "frame-metadata 16.0.0", "futures", - "getrandom 0.2.10", "hex", "impl-serde", "jsonrpsee", @@ -10514,6 +10869,7 @@ dependencies = [ "sp-core 21.0.0", "sp-core-hashing 9.0.0", "sp-runtime 24.0.0", + "subxt-lightclient", "subxt-macro", "subxt-metadata", "thiserror", @@ -10522,11 +10878,11 @@ dependencies = [ [[package]] name = "subxt-codegen" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2f231d97c145c564bd544212c0cc0c29c09ff516af199f4ce00c8e055f8138" +checksum = "3213eb04567e710aa253b94de74337c7b663eea52114805b8723129763282779" dependencies = [ - "frame-metadata", + "frame-metadata 16.0.0", "heck", "hex", "jsonrpsee", @@ -10540,11 +10896,28 @@ dependencies = [ "tokio", ] +[[package]] +name = "subxt-lightclient" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a235bedd0e460c110e5341d919ec3a27f9be3dd4c1c944daad8a9b54d396d" +dependencies = [ + "futures", + "futures-util", + "serde", + "serde_json", + "smoldot-light", + "thiserror", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "subxt-macro" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e544e41e1c84b616632cd2f86862342868f62e11e4cd9062a9e3dbf5fc871f64" +checksum = "cfda460cc5f701785973382c589e9bb12c23bb8d825bfc3ac547b7c672aba1c0" dependencies = [ "darling 0.20.3", "proc-macro-error", @@ -10554,11 +10927,11 @@ dependencies = [ [[package]] name = "subxt-metadata" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01ce5044c81db3404d38c56f1e69d72eff72c54e5913c9bba4c0b58d376031f" +checksum = "0283bd02163913fbd0a5153d0b179533e48b239b953fa4e43baa27c73f18861c" dependencies = [ - "frame-metadata", + "frame-metadata 16.0.0", "parity-scale-codec", "scale-info", "sp-core-hashing 9.0.0", @@ -10681,6 +11054,26 @@ dependencies = [ "thiserror-impl", ] +[[package]] +name = "thiserror-core" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497" +dependencies = [ + "thiserror-core-impl", +] + +[[package]] +name = "thiserror-core-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "thiserror-impl" version = "1.0.43" @@ -11514,7 +11907,21 @@ checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" dependencies = [ "parity-wasm", "wasmi-validation", - "wasmi_core", + "wasmi_core 0.2.1", +] + +[[package]] +name = "wasmi" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51fb5c61993e71158abf5bb863df2674ca3ec39ed6471c64f07aeaf751d67b4" +dependencies = [ + "intx", + "smallvec", + "spin 0.9.8", + "wasmi_arena", + "wasmi_core 0.12.0", + "wasmparser-nostd", ] [[package]] @@ -11526,6 +11933,12 @@ dependencies = [ "parity-wasm", ] +[[package]] +name = "wasmi_arena" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" + [[package]] name = "wasmi_core" version = "0.2.1" @@ -11540,6 +11953,18 @@ dependencies = [ "region", ] +[[package]] +name = "wasmi_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624e6333e861ef49095d2d678b76ebf30b06bf37effca845be7e5b87c90071b7" +dependencies = [ + "downcast-rs", + "libm 0.2.7", + "num-traits", + "paste", +] + [[package]] name = "wasmparser" version = "0.100.0" @@ -11560,6 +11985,15 @@ dependencies = [ "url", ] +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +dependencies = [ + "indexmap-nostd", +] + [[package]] name = "wasmtime" version = "6.0.2" diff --git a/grandpa/prover/Cargo.toml b/grandpa/prover/Cargo.toml index b052897a9..623e9380a 100644 --- a/grandpa/prover/Cargo.toml +++ b/grandpa/prover/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Polytope Labs "] hex = "0.4.3" anyhow = "1.0.64" serde = "1.0.144" -subxt = "0.29.0" +subxt = "0.30.1" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } derive_more = "0.99.17" downcast-rs = "1.2.0" diff --git a/grandpa/verifier/Cargo.toml b/grandpa/verifier/Cargo.toml index e6bfee18c..4cc0df810 100644 --- a/grandpa/verifier/Cargo.toml +++ b/grandpa/verifier/Cargo.toml @@ -28,7 +28,7 @@ substrate-state-machine = { path = "../../pallet-ismp/primitives/state-machine", [dev-dependencies] polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.42" } -subxt = { version = "0.29.0", features = ["substrate-compat"] } +subxt = { version = "0.30.1", features = ["substrate-compat"] } futures = "0.3.24" hex = "0.4.3" env_logger = "0.9.0" diff --git a/grandpa/verifier/src/tests.rs b/grandpa/verifier/src/tests.rs index c281cd028..dc7ec3e54 100644 --- a/grandpa/verifier/src/tests.rs +++ b/grandpa/verifier/src/tests.rs @@ -18,7 +18,6 @@ use subxt::{ pub struct DefaultConfig; impl subxt::config::Config for DefaultConfig { - type Index = u32; type Hash = H256; type AccountId = AccountId32; type Address = sp_runtime::MultiAddress; From bb2f4d092f02063c7662272ff408073db8b11a5e Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Mon, 4 Sep 2023 13:37:17 +0100 Subject: [PATCH 167/182] deprecate unneeded modules (#88) * deprecate unneeded modules * don't check for ismp-parachain --- .github/workflows/ci.yml | 5 - Cargo.lock | 11803 +++------------- Cargo.toml | 9 - README.md | 13 +- grandpa/Cargo.toml | 59 - grandpa/primitives/Cargo.toml | 39 - grandpa/primitives/src/justification.rs | 347 - grandpa/primitives/src/lib.rs | 109 - grandpa/prover/Cargo.toml | 29 - grandpa/prover/src/lib.rs | 355 - grandpa/src/consensus.rs | 314 - grandpa/src/lib.rs | 135 - grandpa/src/messages.rs | 44 - grandpa/verifier/Cargo.toml | 60 - grandpa/verifier/src/lib.rs | 167 - grandpa/verifier/src/tests.rs | 145 - pallet-ismp/evm/Cargo.toml | 59 - pallet-ismp/evm/solidity/IsmpDemo.bin | 1 - pallet-ismp/evm/solidity/foundry.toml | 0 pallet-ismp/evm/solidity/lib/ismp-solidity | 1 - pallet-ismp/evm/solidity/remappings.txt | 3 - pallet-ismp/evm/solidity/src/example.sol | 156 - pallet-ismp/evm/src/abi.rs | 99 - pallet-ismp/evm/src/lib.rs | 30 - pallet-ismp/evm/src/mocks.rs | 244 - pallet-ismp/evm/src/module.rs | 203 - pallet-ismp/evm/src/precompiles.rs | 173 - pallet-ismp/evm/src/tests.rs | 433 - pallet-ismp/evm/src/weight.rs | 45 - .../primitives/state-machine/Cargo.toml | 46 - .../primitives/state-machine/src/lib.rs | 194 - parachain/Cargo.toml | 62 - parachain/inherent/Cargo.toml | 25 - parachain/inherent/src/lib.rs | 96 - parachain/runtime-api/Cargo.toml | 15 - parachain/runtime-api/src/lib.rs | 31 - parachain/src/consensus.rs | 218 - parachain/src/lib.rs | 243 - 38 files changed, 1868 insertions(+), 14142 deletions(-) delete mode 100644 grandpa/Cargo.toml delete mode 100644 grandpa/primitives/Cargo.toml delete mode 100644 grandpa/primitives/src/justification.rs delete mode 100644 grandpa/primitives/src/lib.rs delete mode 100644 grandpa/prover/Cargo.toml delete mode 100644 grandpa/prover/src/lib.rs delete mode 100644 grandpa/src/consensus.rs delete mode 100644 grandpa/src/lib.rs delete mode 100644 grandpa/src/messages.rs delete mode 100644 grandpa/verifier/Cargo.toml delete mode 100644 grandpa/verifier/src/lib.rs delete mode 100644 grandpa/verifier/src/tests.rs delete mode 100644 pallet-ismp/evm/Cargo.toml delete mode 100644 pallet-ismp/evm/solidity/IsmpDemo.bin delete mode 100644 pallet-ismp/evm/solidity/foundry.toml delete mode 160000 pallet-ismp/evm/solidity/lib/ismp-solidity delete mode 100644 pallet-ismp/evm/solidity/remappings.txt delete mode 100644 pallet-ismp/evm/solidity/src/example.sol delete mode 100644 pallet-ismp/evm/src/abi.rs delete mode 100644 pallet-ismp/evm/src/lib.rs delete mode 100644 pallet-ismp/evm/src/mocks.rs delete mode 100644 pallet-ismp/evm/src/module.rs delete mode 100644 pallet-ismp/evm/src/precompiles.rs delete mode 100644 pallet-ismp/evm/src/tests.rs delete mode 100644 pallet-ismp/evm/src/weight.rs delete mode 100644 pallet-ismp/primitives/state-machine/Cargo.toml delete mode 100644 pallet-ismp/primitives/state-machine/src/lib.rs delete mode 100644 parachain/Cargo.toml delete mode 100644 parachain/inherent/Cargo.toml delete mode 100644 parachain/inherent/src/lib.rs delete mode 100644 parachain/runtime-api/Cargo.toml delete mode 100644 parachain/runtime-api/src/lib.rs delete mode 100644 parachain/src/consensus.rs delete mode 100644 parachain/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f93a09227..a5f805882 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,12 +38,7 @@ jobs: - name: Build `no-std` run: | cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p ismp-parachain --no-default-features --target=wasm32-unknown-unknown --verbose --locked cargo +nightly-2022-10-28 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p ismp-grandpa-primitives --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p ismp-grandpa-verifier --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p ismp-grandpa --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p ismp-evm --no-default-features --target=wasm32-unknown-unknown --verbose --locked - name: Test run: | diff --git a/Cargo.lock b/Cargo.lock index 36dfe554d..f2e70b169 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,15 +21,6 @@ dependencies = [ "gimli 0.26.2", ] -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli 0.27.3", -] - [[package]] name = "addr2line" version = "0.20.0" @@ -45,117 +36,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", -] - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead 0.4.3", - "aes 0.7.5", - "cipher 0.3.0", - "ctr 0.8.0", - "ghash 0.4.4", - "subtle", -] - -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead 0.5.2", - "aes 0.8.3", - "cipher 0.4.4", - "ctr 0.9.2", - "ghash 0.5.0", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug 0.3.0", -] - [[package]] name = "ahash" version = "0.7.6" @@ -188,63 +68,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alloy-primitives" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66f73f11dcfbf8bb763d88fb1d082fe7cca0a00d3227d9921bdbd52ce5e013e2" -dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more", - "hex-literal 0.4.1", - "itoa", - "proptest", - "ruint", - "serde", - "tiny-keccak", -] - -[[package]] -name = "alloy-rlp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f54319708cdf93563fe45b1afd475901cecbd0edb992305dc91eadc52d7717e" -dependencies = [ - "arrayvec 0.7.4", - "bytes", - "smol_str", -] - -[[package]] -name = "alloy-sol-macro" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" -dependencies = [ - "dunce", - "heck", - "proc-macro2", - "quote", - "syn 2.0.25", - "syn-solidity", - "tiny-keccak", -] - -[[package]] -name = "alloy-sol-types" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" -dependencies = [ - "alloy-primitives", - "alloy-sol-macro", - "const-hex", - "serde", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -269,55 +92,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "anstream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" - -[[package]] -name = "anstyle-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - [[package]] name = "anyhow" version = "1.0.71" @@ -333,12 +107,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "arc-swap" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" - [[package]] name = "array-bytes" version = "4.2.0" @@ -351,15 +119,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "arrayvec" version = "0.5.2" @@ -373,381 +132,95 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] -name = "asn1-rs" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" -dependencies = [ - "asn1-rs-derive 0.1.0", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time 0.3.23", -] - -[[package]] -name = "asn1-rs" -version = "0.5.2" +name = "async-channel" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ - "asn1-rs-derive 0.4.0", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time 0.3.23", + "concurrent-queue", + "event-listener", + "futures-core", ] [[package]] -name = "asn1-rs-derive" -version = "0.1.0" +name = "async-trait" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.25", ] [[package]] -name = "asn1-rs-derive" -version = "0.4.0" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "asn1-rs-impl" -version = "0.1.0" +name = "backtrace" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "addr2line 0.20.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.31.1", + "rustc-demangle", ] [[package]] -name = "asn1_der" -version = "0.7.6" +name = "base16ct" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] -name = "async-channel" -version = "1.9.0" +name = "base64" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] -name = "async-executor" -version = "1.5.1" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" -dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "async-fs" -version = "1.6.0" +name = "beef" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" dependencies = [ - "async-lock", - "autocfg", - "blocking", - "futures-lite", + "serde", ] [[package]] -name = "async-io" -version = "1.13.0" +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "async-lock", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite", - "log", - "parking", - "polling", - "rustix 0.37.23", - "slab", - "socket2 0.4.9", - "waker-fn", + "serde", ] [[package]] -name = "async-lock" -version = "2.7.0" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "async-net" -version = "1.7.0" +name = "bitflags" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" -dependencies = [ - "async-io", - "autocfg", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 0.37.23", - "signal-hook", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" - -[[package]] -name = "async-trait" -version = "0.1.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "asynchronous-codec" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" -dependencies = [ - "bytes", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite 0.2.10", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line 0.20.0", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object 0.31.1", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bindgen" -version = "0.64.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 1.0.109", -] - -[[package]] -name = "bip39" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" -dependencies = [ - "bitcoin_hashes", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitcoin_hashes" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" [[package]] name = "bitvec" @@ -770,16 +243,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -dependencies = [ - "arrayvec 0.4.12", - "constant_time_eq 0.1.5", -] - [[package]] name = "blake2b_simd" version = "1.0.1" @@ -788,32 +251,7 @@ checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.4", - "constant_time_eq 0.2.6", -] - -[[package]] -name = "blake2s_simd" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" -dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "constant_time_eq 0.2.6", -] - -[[package]] -name = "blake3" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" -dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "cc", - "cfg-if", - "constant_time_eq 0.3.0", - "digest 0.10.7", + "constant_time_eq", ] [[package]] @@ -822,7 +260,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5", + "block-padding", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -846,16 +284,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-modes" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" -dependencies = [ - "block-padding 0.2.1", - "cipher 0.2.5", -] - [[package]] name = "block-padding" version = "0.1.5" @@ -865,27 +293,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "blocking" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", -] - [[package]] name = "bounded-collections" version = "0.1.8" @@ -898,30 +305,12 @@ dependencies = [ "serde", ] -[[package]] -name = "bounded-vec" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" -dependencies = [ - "thiserror", -] - [[package]] name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -[[package]] -name = "bs58" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" -dependencies = [ - "tinyvec", -] - [[package]] name = "bstr" version = "1.6.0" @@ -968,17 +357,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "cc" version = "1.0.79" @@ -988,26 +366,6 @@ dependencies = [ "jobserver", ] -[[package]] -name = "ccm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" -dependencies = [ - "aead 0.3.2", - "cipher 0.2.5", - "subtle", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-expr" version = "0.10.3" @@ -1024,92 +382,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chacha20" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "zeroize", -] - -[[package]] -name = "chacha20poly1305" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" -dependencies = [ - "aead 0.4.3", - "chacha20", - "cipher 0.3.0", - "poly1305", - "zeroize", -] - -[[package]] -name = "chrono" -version = "0.4.26" +name = "chrono" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", - "time 0.1.45", - "wasm-bindgen", "winapi", ] -[[package]] -name = "cid" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" -dependencies = [ - "core2", - "multibase", - "multihash 0.16.3", - "serde", - "unsigned-varint", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "ckb-merkle-mountain-range" version = "0.5.2" @@ -1119,76 +402,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" - -[[package]] -name = "coarsetime" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" -dependencies = [ - "libc", - "once_cell", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "concurrent-queue" version = "2.2.0" @@ -1198,73 +411,24 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "const-hex" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" -dependencies = [ - "cfg-if", - "cpufeatures", - "hex", - "serde", -] - [[package]] name = "const-oid" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "constant_time_eq" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "cpp_demangle" version = "0.3.5" @@ -1289,7 +453,7 @@ version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" dependencies = [ - "cranelift-entity 0.93.2", + "cranelift-entity", ] [[package]] @@ -1303,7 +467,7 @@ dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", - "cranelift-entity 0.93.2", + "cranelift-entity", "cranelift-isle", "gimli 0.26.2", "hashbrown 0.12.3", @@ -1337,15 +501,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cranelift-entity" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" -dependencies = [ - "serde", -] - [[package]] name = "cranelift-frontend" version = "0.93.2" @@ -1382,30 +537,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" dependencies = [ "cranelift-codegen", - "cranelift-entity 0.93.2", + "cranelift-entity", "cranelift-frontend", "itertools", "log", "smallvec", - "wasmparser 0.100.0", - "wasmtime-types 6.0.2", -] - -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog", + "wasmparser", + "wasmtime-types", ] -[[package]] -name = "crc-catalog" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" - [[package]] name = "crc32fast" version = "1.3.2" @@ -1449,16 +589,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -1474,18 +604,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.2" @@ -1505,7 +623,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", - "rand_core 0.6.4", "typenum", ] @@ -1529,135 +646,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher 0.3.0", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher 0.4.4", -] - -[[package]] -name = "cumulus-pallet-parachain-system" -version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" -dependencies = [ - "bytes", - "cumulus-pallet-parachain-system-proc-macro", - "cumulus-primitives-core", - "cumulus-primitives-parachain-inherent", - "environmental", - "frame-support", - "frame-system", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "polkadot-parachain", - "scale-info", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-inherents 4.0.0-dev", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", - "sp-trie 7.0.0", - "sp-version 5.0.0", - "xcm", -] - -[[package]] -name = "cumulus-pallet-parachain-system-proc-macro" -version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "cumulus-primitives-core" -version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" -dependencies = [ - "parity-scale-codec", - "polkadot-core-primitives", - "polkadot-parachain", - "polkadot-primitives", - "scale-info", - "sp-api 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-trie 7.0.0", - "xcm", -] - -[[package]] -name = "cumulus-primitives-parachain-inherent" -version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" -dependencies = [ - "async-trait", - "cumulus-primitives-core", - "cumulus-relay-chain-interface", - "cumulus-test-relay-sproof-builder", - "parity-scale-codec", - "sc-client-api 4.0.0-dev", - "scale-info", - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-trie 7.0.0", - "tracing", -] - -[[package]] -name = "cumulus-relay-chain-interface" -version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" -dependencies = [ - "async-trait", - "cumulus-primitives-core", - "futures", - "jsonrpsee-core", - "parity-scale-codec", - "polkadot-overseer", - "sc-client-api 4.0.0-dev", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-state-machine 0.13.0", - "thiserror", -] - -[[package]] -name = "cumulus-test-relay-sproof-builder" -version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=release-v0.9.420#843a5095544807c2e3aa68363c87ae52c40b243d" -dependencies = [ - "cumulus-primitives-core", - "parity-scale-codec", - "polkadot-primitives", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", -] - [[package]] name = "curve25519-dalek" version = "2.1.3" @@ -1685,249 +673,37 @@ dependencies = [ ] [[package]] -name = "curve25519-dalek" -version = "4.0.0-rc.1" +name = "der" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ - "cfg-if", - "fiat-crypto", - "packed_simd_2", - "platforms", - "subtle", + "const-oid", "zeroize", ] [[package]] -name = "curve25519-dalek-ng" -version = "4.1.1" +name = "derive-syn-parse" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.6.4", - "subtle-ng", - "zeroize", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "darling" -version = "0.14.4" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.25", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core 0.20.3", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "data-encoding-macro" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" -dependencies = [ - "data-encoding", - "data-encoding-macro-internal", -] - -[[package]] -name = "data-encoding-macro-internal" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" -dependencies = [ - "data-encoding", - "syn 1.0.109", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" -dependencies = [ - "asn1-rs 0.3.1", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs 0.5.2", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive-syn-parse" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" -dependencies = [ - "darling 0.14.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_builder_macro" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" -dependencies = [ - "derive_builder_core", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version", "syn 1.0.109", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" version = "0.8.1" @@ -1958,15 +734,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - [[package]] name = "directories-next" version = "2.0.0" @@ -1977,17 +744,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1999,41 +755,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - [[package]] name = "downcast-rs" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" -[[package]] -name = "dtoa" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519b83cd10f5f6e969625a409f735182bea5558cd8b64c655806ceaae36f1999" - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - [[package]] name = "dyn-clonable" version = "0.9.0" @@ -2061,30 +788,18 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - [[package]] name = "ecdsa" version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ - "der 0.7.7", + "der", "digest 0.10.7", - "elliptic-curve 0.13.5", - "rfc6979 0.4.0", + "elliptic-curve", + "rfc6979", "signature 2.1.0", - "spki 0.7.2", + "spki", ] [[package]] @@ -2104,8 +819,6 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", "ed25519", - "rand 0.7.3", - "serde", "sha2 0.9.9", "zeroize", ] @@ -2130,43 +843,21 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.7", - "ff 0.12.1", - "generic-array 0.14.7", - "group 0.12.1", - "hkdf", - "pem-rfc7468", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.2", + "base16ct", + "crypto-bigint", "digest 0.10.7", - "ff 0.13.0", + "ff", "generic-array 0.14.7", - "group 0.13.0", - "pkcs8 0.10.2", + "group", + "pkcs8", "rand_core 0.6.4", - "sec1 0.7.2", + "sec1", "subtle", "zeroize", ] @@ -2183,19 +874,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -2243,278 +921,54 @@ dependencies = [ ] [[package]] -name = "ethbloom" -version = "0.13.0" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", -] +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "ethereum" -version = "0.14.0" +name = "expander" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89fb87a9e103f71b903b80b670200b54cc67a07578f070681f1fffb7396fb7" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" dependencies = [ - "bytes", - "ethereum-types", - "hash-db 0.15.2", - "hash256-std-hasher", - "parity-scale-codec", - "rlp", - "scale-info", - "serde", - "sha3", - "triehash", + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "ethereum-types" -version = "0.14.1" +name = "fake-simd" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", -] +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] -name = "event-listener" -version = "2.5.3" +name = "fallible-iterator" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] -name = "evm" -version = "0.39.1" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" -dependencies = [ - "auto_impl", - "environmental", - "ethereum", - "evm-core", - "evm-gasometer", - "evm-runtime", - "log", - "parity-scale-codec", - "primitive-types", - "rlp", - "scale-info", - "serde", - "sha3", -] - -[[package]] -name = "evm-core" -version = "0.39.0" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" -dependencies = [ - "parity-scale-codec", - "primitive-types", - "scale-info", - "serde", -] - -[[package]] -name = "evm-gasometer" -version = "0.39.0" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" -dependencies = [ - "environmental", - "evm-core", - "evm-runtime", - "primitive-types", -] - -[[package]] -name = "evm-runtime" -version = "0.39.0" -source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" -dependencies = [ - "auto_impl", - "environmental", - "evm-core", - "primitive-types", - "sha3", -] - -[[package]] -name = "exit-future" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" -dependencies = [ - "futures", -] - -[[package]] -name = "expander" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" -dependencies = [ - "blake3", - "fs-err", - "proc-macro2", - "quote", -] - -[[package]] -name = "expander" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" -dependencies = [ - "blake2", - "fs-err", - "proc-macro2", - "quote", -] - -[[package]] -name = "expander" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" -dependencies = [ - "blake2", - "fs-err", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "expander" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" -dependencies = [ - "blake2", - "fs-err", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fatality" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" -dependencies = [ - "fatality-proc-macro", - "thiserror", -] - -[[package]] -name = "fatality-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" -dependencies = [ - "expander 0.0.4", - "indexmap 1.9.3", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", - "thiserror", -] - -[[package]] -name = "fdlimit" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" -dependencies = [ - "libc", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" - [[package]] name = "file-per-thread-logger" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger 0.10.0", - "log", -] - -[[package]] -name = "finality-grandpa" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" -dependencies = [ - "either", - "futures", - "futures-timer", + "env_logger", "log", - "num-traits", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", ] [[package]] @@ -2529,55 +983,12 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "libz-sys", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "fork-tree" -version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "fork-tree" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4615fe5ff97489aa729b6f5133397efb0ba3075d11a50aa6c06d7bce9501f7b9" -dependencies = [ - "parity-scale-codec", -] - [[package]] name = "form_urlencoded" version = "1.2.0" @@ -2587,46 +998,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fp-account" -version = "1.0.0-dev" -source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#1a718546085be20ce381b70e1f9e4c8b4d4b1f03" -dependencies = [ - "hex", - "impl-serde", - "libsecp256k1", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-runtime-interface 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "fp-evm" -version = "3.0.0-dev" -source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#1a718546085be20ce381b70e1f9e4c8b4d4b1f03" -dependencies = [ - "evm", - "frame-support", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - [[package]] name = "frame-benchmarking" version = "4.0.0-dev" @@ -2641,11 +1012,11 @@ dependencies = [ "paste", "scale-info", "serde", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", + "sp-api", + "sp-application-crypto", "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-runtime-interface 7.0.0", "sp-std 5.0.0", "sp-storage 7.0.0", @@ -2664,18 +1035,6 @@ dependencies = [ "serde", ] -[[package]] -name = "frame-metadata" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" -dependencies = [ - "cfg-if", - "parity-scale-codec", - "scale-info", - "serde", -] - [[package]] name = "frame-support" version = "4.0.0-dev" @@ -2683,7 +1042,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "bitflags 1.3.2", "environmental", - "frame-metadata 15.1.0", + "frame-metadata", "frame-support-procedural", "impl-trait-for-tuples", "k256", @@ -2694,18 +1053,18 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-api 4.0.0-dev", - "sp-arithmetic 6.0.0", + "sp-api", + "sp-arithmetic", "sp-core 7.0.0", - "sp-core-hashing-proc-macro 5.0.0", - "sp-inherents 4.0.0-dev", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-staking 4.0.0-dev", - "sp-state-machine 0.13.0", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", "sp-std 5.0.0", "sp-tracing 6.0.0", - "sp-weights 4.0.0", + "sp-weights", "tt-call", ] @@ -2758,11 +1117,11 @@ dependencies = [ "scale-info", "serde", "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-version 5.0.0", - "sp-weights 4.0.0", + "sp-version", + "sp-weights", ] [[package]] @@ -2771,26 +1130,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "fs4" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" -dependencies = [ - "rustix 0.38.4", - "windows-sys 0.48.0", -] - [[package]] name = "funty" version = "2.0.0" @@ -2846,21 +1185,6 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite 0.2.10", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.28" @@ -2872,17 +1196,6 @@ dependencies = [ "syn 2.0.25", ] -[[package]] -name = "futures-rustls" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" -dependencies = [ - "futures-io", - "rustls 0.20.8", - "webpki 0.22.0", -] - [[package]] name = "futures-sink" version = "0.3.28" @@ -2914,7 +1227,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.10", + "pin-project-lite", "pin-utils", "slab", ] @@ -2971,52 +1284,21 @@ dependencies = [ ] [[package]] -name = "ghash" -version = "0.4.4" +name = "gimli" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ - "opaque-debug 0.3.0", - "polyval 0.5.3", + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", ] [[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug 0.3.0", - "polyval 0.6.1", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - -[[package]] -name = "gimli" -version = "0.27.3" +name = "gimli" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" @@ -3031,24 +1313,13 @@ dependencies = [ "regex", ] -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", + "ff", "rand_core 0.6.4", "subtle", ] @@ -3072,12 +1343,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - [[package]] name = "hash-db" version = "0.16.0" @@ -3116,9 +1381,6 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "serde", -] [[package]] name = "heck" @@ -3126,15 +1388,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.2" @@ -3153,21 +1406,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - [[package]] name = "hmac" version = "0.8.1" @@ -3208,17 +1446,6 @@ dependencies = [ "hmac 0.8.1", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.9" @@ -3238,15 +1465,9 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.10", + "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -3281,30 +1502,14 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.10", - "socket2 0.4.9", + "pin-project-lite", + "socket2", "tokio", "tower-service", "tracing", "want", ] -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http", - "hyper", - "log", - "rustls 0.20.8", - "rustls-native-certs", - "tokio", - "tokio-rustls", - "webpki-roots", -] - [[package]] name = "iana-time-zone" version = "0.1.57" @@ -3316,7 +1521,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows", ] [[package]] @@ -3328,23 +1533,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.4.0" @@ -3355,35 +1543,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if-addrs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "if-watch" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" -dependencies = [ - "async-io", - "core-foundation", - "fnv", - "futures", - "if-addrs", - "ipnet", - "log", - "rtnetlink", - "system-configuration", - "tokio", - "windows 0.34.0", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -3393,15 +1552,6 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - [[package]] name = "impl-serde" version = "0.4.0" @@ -3443,36 +1593,6 @@ dependencies = [ "hashbrown 0.14.0", ] -[[package]] -name = "indexmap-nostd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - [[package]] name = "integer-sqrt" version = "0.1.5" @@ -3482,73 +1602,24 @@ dependencies = [ "num-traits", ] -[[package]] -name = "interceptor" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" -dependencies = [ - "async-trait", - "bytes", - "log", - "rand 0.8.5", - "rtcp", - "rtp", - "thiserror", - "tokio", - "waitgroup", - "webrtc-srtp", - "webrtc-util", -] - -[[package]] -name = "intx" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f38a50a899dc47a6d0ed5508e7f601a2e34c3a85303514b5d137f3c10a0c75" - [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "ip_network" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" - -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2 0.5.3", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -3578,259 +1649,85 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core 7.0.0", - "sp-runtime 7.0.0", -] - -[[package]] -name = "ismp-evm" -version = "0.1.0" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "fp-evm", - "frame-support", - "frame-system", - "hex", - "hex-literal 0.4.1", - "ismp", - "ismp-primitives", - "ismp-testsuite", - "pallet-balances", - "pallet-evm", - "pallet-ismp", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", + "sp-runtime", ] [[package]] -name = "ismp-grandpa" +name = "ismp-primitives" version = "0.1.0" dependencies = [ "ckb-merkle-mountain-range", - "cumulus-primitives-core", - "finality-grandpa", "frame-support", "frame-system", "ismp", - "ismp-grandpa-primitives", - "ismp-grandpa-verifier", - "ismp-primitives", - "pallet-ismp", "parity-scale-codec", "primitive-types", "scale-info", + "serde", "sp-consensus-aura", "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-trie 7.0.0", - "substrate-state-machine", -] - -[[package]] -name = "ismp-grandpa-primitives" -version = "0.1.0" -dependencies = [ - "anyhow", - "finality-grandpa", - "frame-support", - "ismp", - "parity-scale-codec", - "sp-consensus-grandpa", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-trie 7.0.0", + "sp-runtime", ] [[package]] -name = "ismp-grandpa-prover" +name = "ismp-rpc" version = "0.1.0" dependencies = [ - "anyhow", - "derive_more", - "downcast-rs", - "finality-grandpa", - "hex", + "frame-system", + "hex-literal", "ismp", - "ismp-grandpa-primitives", + "ismp-primitives", + "ismp-runtime-api", "jsonrpsee", - "jsonrpsee-ws-client", + "pallet-ismp", "parity-scale-codec", - "sc-consensus-grandpa-rpc", + "sc-client-api", "serde", - "sp-consensus-grandpa", + "serde_json", + "sp-api", + "sp-blockchain", "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-trie 7.0.0", - "subxt", + "sp-runtime", ] [[package]] -name = "ismp-grandpa-verifier" +name = "ismp-runtime-api" version = "0.1.0" dependencies = [ - "anyhow", - "derive_more", - "env_logger 0.9.3", - "finality-grandpa", - "frame-support", - "futures", - "hex", - "hex-literal 0.3.4", "ismp", - "ismp-grandpa-primitives", - "ismp-grandpa-prover", - "log", + "ismp-primitives", + "pallet-ismp", "parity-scale-codec", - "polkadot-core-primitives", - "sc-finality-grandpa-rpc", "serde", - "sp-consensus-grandpa", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-api", "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-trie 7.0.0", - "substrate-state-machine", - "subxt", - "tokio", ] [[package]] -name = "ismp-parachain" +name = "ismp-testsuite" version = "0.1.0" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#3a85db0d968eab5a88427a19a2a612332caec23f" dependencies = [ - "cumulus-pallet-parachain-system", - "cumulus-primitives-core", - "frame-support", - "frame-system", - "hex-literal 0.4.1", "ismp", - "ismp-primitives", - "pallet-ismp", "parity-scale-codec", "primitive-types", - "scale-info", - "serde", - "sp-consensus-aura", - "sp-inherents 4.0.0-dev", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-trie 7.0.0", - "substrate-state-machine", + "sp-core 20.0.0", ] [[package]] -name = "ismp-parachain-inherent" -version = "0.1.0" +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "anyhow", - "async-trait", - "cumulus-primitives-core", - "cumulus-relay-chain-interface", - "ismp", - "ismp-parachain", - "ismp-parachain-runtime-api", - "parity-scale-codec", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", + "either", ] [[package]] -name = "ismp-parachain-runtime-api" -version = "0.1.0" -dependencies = [ - "sp-api 4.0.0-dev", -] - -[[package]] -name = "ismp-primitives" -version = "0.1.0" -dependencies = [ - "ckb-merkle-mountain-range", - "frame-support", - "frame-system", - "ismp", - "parity-scale-codec", - "primitive-types", - "scale-info", - "serde", - "sp-consensus-aura", - "sp-core 7.0.0", - "sp-runtime 7.0.0", -] - -[[package]] -name = "ismp-rpc" -version = "0.1.0" -dependencies = [ - "frame-system", - "hex-literal 0.3.4", - "ismp", - "ismp-primitives", - "ismp-runtime-api", - "jsonrpsee", - "pallet-ismp", - "parity-scale-codec", - "sc-client-api 4.0.0-dev", - "serde", - "serde_json", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", -] - -[[package]] -name = "ismp-runtime-api" -version = "0.1.0" -dependencies = [ - "ismp", - "ismp-primitives", - "pallet-ismp", - "parity-scale-codec", - "serde", - "sp-api 4.0.0-dev", - "sp-std 5.0.0", -] - -[[package]] -name = "ismp-testsuite" -version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#3a85db0d968eab5a88427a19a2a612332caec23f" -dependencies = [ - "ismp", - "parity-scale-codec", - "primitive-types", - "sp-core 20.0.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "jobserver" @@ -3856,35 +1753,11 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ - "jsonrpsee-client-transport", "jsonrpsee-core", - "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", - "jsonrpsee-ws-client", - "tracing", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" -dependencies = [ - "futures-util", - "http", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "rustls-native-certs", - "soketto", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", "tracing", - "webpki-roots", ] [[package]] @@ -3895,16 +1768,14 @@ checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", "arrayvec 0.7.4", - "async-lock", "async-trait", "beef", "futures-channel", - "futures-timer", "futures-util", "globset", "hyper", "jsonrpsee-types", - "parking_lot 0.12.1", + "parking_lot", "rand 0.8.5", "rustc-hash", "serde", @@ -3915,25 +1786,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-http-client" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" -dependencies = [ - "async-trait", - "hyper", - "hyper-rustls", - "jsonrpsee-core", - "jsonrpsee-types", - "rustc-hash", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - [[package]] name = "jsonrpsee-proc-macros" version = "0.16.2" @@ -3983,18 +1835,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-ws-client" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" -dependencies = [ - "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", -] - [[package]] name = "k256" version = "0.13.1" @@ -4002,8 +1842,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.7", - "elliptic-curve 0.13.5", + "ecdsa", + "elliptic-curve", "once_cell", "sha2 0.10.7", ] @@ -4026,64 +1866,18 @@ dependencies = [ "smallvec", ] -[[package]] -name = "kvdb-memorydb" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" -dependencies = [ - "kvdb", - "parking_lot 0.12.1", -] - -[[package]] -name = "kvdb-rocksdb" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7a749456510c45f795e8b04a6a3e0976d0139213ecbf465843830ad55e2217" -dependencies = [ - "kvdb", - "num_cpus", - "parking_lot 0.12.1", - "regex", - "rocksdb", - "smallvec", -] - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "libm" version = "0.2.7" @@ -4091,1293 +1885,1076 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] -name = "libp2p" -version = "0.50.1" +name = "libsecp256k1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ - "bytes", - "futures", - "futures-timer", - "getrandom 0.2.10", - "instant", - "libp2p-core 0.38.0", - "libp2p-dns", - "libp2p-identify", - "libp2p-kad", - "libp2p-mdns", - "libp2p-metrics", - "libp2p-mplex", - "libp2p-noise", - "libp2p-ping", - "libp2p-quic", - "libp2p-request-response", - "libp2p-swarm", - "libp2p-tcp", - "libp2p-wasm-ext", - "libp2p-webrtc", - "libp2p-websocket", - "libp2p-yamux", - "multiaddr 0.16.0", - "parking_lot 0.12.1", - "pin-project", - "smallvec", + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", ] [[package]] -name = "libp2p-core" -version = "0.38.0" +name = "libsecp256k1-core" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ - "asn1_der", - "bs58 0.4.0", - "ed25519-dalek", - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "log", - "multiaddr 0.16.0", - "multihash 0.16.3", - "multistream-select", - "once_cell", - "parking_lot 0.12.1", - "pin-project", - "prost", - "prost-build", - "rand 0.8.5", - "rw-stream-sink", - "sec1 0.3.0", - "sha2 0.10.7", - "smallvec", - "thiserror", - "unsigned-varint", - "void", - "zeroize", + "crunchy", + "digest 0.9.0", + "subtle", ] [[package]] -name = "libp2p-core" -version = "0.39.2" +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" dependencies = [ - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "libp2p-identity", - "log", - "multiaddr 0.17.1", - "multihash 0.17.0", - "multistream-select", - "once_cell", - "parking_lot 0.12.1", - "pin-project", - "quick-protobuf", - "rand 0.8.5", - "rw-stream-sink", - "smallvec", - "thiserror", - "unsigned-varint", - "void", + "libsecp256k1-core", ] [[package]] -name = "libp2p-dns" -version = "0.38.0" +name = "libsecp256k1-gen-genmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" dependencies = [ - "futures", - "libp2p-core 0.38.0", - "log", - "parking_lot 0.12.1", - "smallvec", - "trust-dns-resolver", + "libsecp256k1-core", ] [[package]] -name = "libp2p-identify" -version = "0.41.1" +name = "linregress" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" +checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" dependencies = [ - "asynchronous-codec", - "futures", - "futures-timer", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "lru 0.8.1", - "prost", - "prost-build", - "prost-codec", - "smallvec", - "thiserror", - "void", + "nalgebra", ] [[package]] -name = "libp2p-identity" -version = "0.1.2" +name = "linux-raw-sys" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" -dependencies = [ - "bs58 0.4.0", - "ed25519-dalek", - "log", - "multiaddr 0.17.1", - "multihash 0.17.0", - "quick-protobuf", - "rand 0.8.5", - "sha2 0.10.7", - "thiserror", - "zeroize", -] +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] -name = "libp2p-kad" -version = "0.42.1" +name = "linux-raw-sys" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" -dependencies = [ - "arrayvec 0.7.4", - "asynchronous-codec", - "bytes", - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "prost", - "prost-build", - "rand 0.8.5", - "sha2 0.10.7", - "smallvec", - "thiserror", - "uint", - "unsigned-varint", - "void", -] +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] -name = "libp2p-mdns" -version = "0.42.0" +name = "linux-raw-sys" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" -dependencies = [ - "data-encoding", - "futures", - "if-watch", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "rand 0.8.5", - "smallvec", - "socket2 0.4.9", - "tokio", - "trust-dns-proto", - "void", -] +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] -name = "libp2p-metrics" -version = "0.11.0" +name = "lock_api" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ - "libp2p-core 0.38.0", - "libp2p-identify", - "libp2p-kad", - "libp2p-ping", - "libp2p-swarm", - "prometheus-client", + "autocfg", + "scopeguard", ] [[package]] -name = "libp2p-mplex" -version = "0.38.0" +name = "log" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" -dependencies = [ - "asynchronous-codec", - "bytes", - "futures", - "libp2p-core 0.38.0", - "log", - "nohash-hasher", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "unsigned-varint", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] -name = "libp2p-noise" -version = "0.41.0" +name = "lru" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "bytes", - "curve25519-dalek 3.2.0", - "futures", - "libp2p-core 0.38.0", - "log", - "once_cell", - "prost", - "prost-build", - "rand 0.8.5", - "sha2 0.10.7", - "snow", - "static_assertions", - "thiserror", - "x25519-dalek 1.1.1", - "zeroize", + "hashbrown 0.12.3", ] [[package]] -name = "libp2p-ping" -version = "0.41.0" +name = "mach" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ - "futures", - "futures-timer", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "rand 0.8.5", - "void", + "libc", ] [[package]] -name = "libp2p-quic" -version = "0.7.0-alpha" +name = "matchers" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" dependencies = [ - "bytes", - "futures", - "futures-timer", - "if-watch", - "libp2p-core 0.38.0", - "libp2p-tls", - "log", - "parking_lot 0.12.1", - "quinn-proto", - "rand 0.8.5", - "rustls 0.20.8", - "thiserror", - "tokio", + "regex-automata 0.1.10", ] [[package]] -name = "libp2p-request-response" -version = "0.23.0" +name = "matrixmultiply" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" dependencies = [ - "async-trait", - "bytes", - "futures", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm", - "log", - "rand 0.8.5", - "smallvec", - "unsigned-varint", + "autocfg", + "rawpointer", ] [[package]] -name = "libp2p-swarm" -version = "0.41.1" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" -dependencies = [ - "either", - "fnv", - "futures", - "futures-timer", - "instant", - "libp2p-core 0.38.0", - "libp2p-swarm-derive", - "log", - "pin-project", - "rand 0.8.5", - "smallvec", - "thiserror", - "tokio", - "void", -] +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "libp2p-swarm-derive" -version = "0.31.0" +name = "memfd" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "heck", - "quote", - "syn 1.0.109", + "rustix 0.37.23", ] [[package]] -name = "libp2p-tcp" -version = "0.38.0" +name = "memoffset" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "futures", - "futures-timer", - "if-watch", - "libc", - "libp2p-core 0.38.0", - "log", - "socket2 0.4.9", - "tokio", + "autocfg", ] [[package]] -name = "libp2p-tls" -version = "0.1.0" +name = "memoffset" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ - "futures", - "futures-rustls", - "libp2p-core 0.39.2", - "libp2p-identity", - "rcgen 0.10.0", - "ring", - "rustls 0.20.8", - "thiserror", - "webpki 0.22.0", - "x509-parser 0.14.0", - "yasna", + "autocfg", ] [[package]] -name = "libp2p-wasm-ext" -version = "0.38.0" +name = "memory-db" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "futures", - "js-sys", - "libp2p-core 0.38.0", - "parity-send-wrapper", - "wasm-bindgen", - "wasm-bindgen-futures", + "hash-db", ] [[package]] -name = "libp2p-webrtc" -version = "0.4.0-alpha" +name = "memory_units" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" -dependencies = [ - "async-trait", - "asynchronous-codec", - "bytes", - "futures", - "futures-timer", - "hex", - "if-watch", - "libp2p-core 0.38.0", - "libp2p-noise", - "log", - "multihash 0.16.3", - "prost", - "prost-build", - "prost-codec", - "rand 0.8.5", - "rcgen 0.9.3", - "serde", - "stun", - "thiserror", - "tinytemplate", - "tokio", - "tokio-util", - "webrtc", -] +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" [[package]] -name = "libp2p-websocket" -version = "0.40.0" +name = "merlin" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" dependencies = [ - "either", - "futures", - "futures-rustls", - "libp2p-core 0.38.0", - "log", - "parking_lot 0.12.1", - "quicksink", - "rw-stream-sink", - "soketto", - "url", - "webpki-roots", + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", ] [[package]] -name = "libp2p-yamux" -version = "0.42.0" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ - "futures", - "libp2p-core 0.38.0", - "log", - "parking_lot 0.12.1", - "thiserror", - "yamux", + "adler", ] [[package]] -name = "librocksdb-sys" -version = "0.10.0+7.9.2" +name = "mio" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", "libc", - "libz-sys", - "tikv-jemalloc-sys", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", ] [[package]] -name = "libsecp256k1" -version = "0.7.1" +name = "nalgebra" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", "typenum", ] [[package]] -name = "libsecp256k1-core" -version = "0.3.0" +name = "nalgebra-macros" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" +name = "nohash-hasher" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" +name = "num-bigint" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "libsecp256k1-core", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "libz-sys" -version = "1.1.9" +name = "num-complex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ - "cc", - "pkg-config", - "vcpkg", + "num-traits", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linked_hash_set" -version = "0.1.4" +name = "num-format" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "linked-hash-map", + "arrayvec 0.7.4", + "itoa", ] [[package]] -name = "linregress" -version = "0.5.2" +name = "num-integer" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "nalgebra", + "autocfg", + "num-traits", ] [[package]] -name = "linux-raw-sys" -version = "0.1.4" +name = "num-rational" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] [[package]] -name = "linux-raw-sys" -version = "0.3.8" +name = "num-traits" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] [[package]] -name = "linux-raw-sys" -version = "0.4.3" +name = "num_cpus" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] [[package]] -name = "lock_api" -version = "0.4.10" +name = "object" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ - "autocfg", - "scopeguard", + "crc32fast", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "memchr", ] [[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "lru" -version = "0.8.1" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ - "hashbrown 0.12.3", + "memchr", ] [[package]] -name = "lru" -version = "0.9.0" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" -dependencies = [ - "hashbrown 0.13.2", -] +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "lru" -version = "0.10.1" +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] -name = "lru-cache" -version = "0.1.2" +name = "opaque-debug" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] -name = "lz4" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "libc", - "lz4-sys", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", ] [[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +name = "pallet-ismp" +version = "0.1.0" dependencies = [ - "cc", - "libc", + "ckb-merkle-mountain-range", + "derive_more", + "enum-as-inner", + "env_logger", + "frame-benchmarking", + "frame-support", + "frame-system", + "ismp", + "ismp-primitives", + "ismp-testsuite", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core 7.0.0", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", ] [[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "libc", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", ] [[package]] -name = "match_cfg" -version = "0.1.0" +name = "parity-scale-codec" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] [[package]] -name = "matchers" -version = "0.0.1" +name = "parity-scale-codec-derive" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" dependencies = [ - "regex-automata 0.1.10", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "matches" -version = "0.1.10" +name = "parity-wasm" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] -name = "matrixmultiply" -version = "0.3.7" +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "autocfg", - "rawpointer", + "lock_api", + "parking_lot_core", ] [[package]] -name = "md-5" -version = "0.10.5" +name = "parking_lot_core" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ - "digest 0.10.7", + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets 0.48.1", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "paste" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" [[package]] -name = "memfd" -version = "0.6.3" +name = "pbkdf2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ - "rustix 0.37.23", + "crypto-mac 0.11.1", ] [[package]] -name = "memmap2" -version = "0.5.10" +name = "pbkdf2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "libc", + "digest 0.10.7", ] [[package]] -name = "memoffset" -version = "0.6.5" +name = "percent-encoding" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] -name = "memoffset" -version = "0.8.0" +name = "pin-project-lite" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] -name = "memoffset" -version = "0.9.0" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "memory-db" -version = "0.31.0" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "hash-db 0.15.2", - "hashbrown 0.12.3", + "der", + "spki", ] [[package]] -name = "memory-db" -version = "0.32.0" +name = "pkg-config" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" -dependencies = [ - "hash-db 0.16.0", -] +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] -name = "memory_units" -version = "0.4.0" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "merlin" -version = "2.0.1" +name = "primitive-types" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ - "byteorder", - "keccak", - "rand_core 0.5.1", - "zeroize", + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", ] [[package]] -name = "merlin" -version = "3.0.0" +name = "proc-macro-crate" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", + "once_cell", + "toml_edit", ] [[package]] -name = "mick-jaeger" -version = "0.1.8" +name = "proc-macro-warning" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" +checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ - "futures", - "rand 0.8.5", - "thrift", + "proc-macro2", + "quote", + "syn 2.0.25", ] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "proc-macro2" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] [[package]] -name = "miniz_oxide" -version = "0.7.1" +name = "prometheus" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" dependencies = [ - "adler", + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "thiserror", ] [[package]] -name = "mio" -version = "0.8.8" +name = "psm" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" dependencies = [ - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "cc", ] [[package]] -name = "mockall" -version = "0.11.4" +name = "quote" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", + "proc-macro2", ] [[package]] -name = "mockall_derive" -version = "0.11.4" +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 1.0.109", + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", ] [[package]] -name = "multiaddr" -version = "0.16.0" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "multibase", - "multihash 0.16.3", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] -name = "multiaddr" -version = "0.17.1" +name = "rand_chacha" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "log", - "multibase", - "multihash 0.17.0", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] -name = "multibase" -version = "0.9.1" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "base-x", - "data-encoding", - "data-encoding-macro", + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] -name = "multihash" -version = "0.16.3" +name = "rand_core" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "blake2b_simd", - "blake2s_simd", - "blake3", - "core2", - "digest 0.10.7", - "multihash-derive", - "sha2 0.10.7", - "sha3", - "unsigned-varint", + "getrandom 0.1.16", ] [[package]] -name = "multihash" -version = "0.17.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "core2", - "multihash-derive", - "unsigned-varint", + "getrandom 0.2.10", ] [[package]] -name = "multihash-derive" -version = "0.8.0" +name = "rand_hc" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", + "rand_core 0.5.1", ] [[package]] -name = "multimap" -version = "0.8.3" +name = "rawpointer" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] -name = "multistream-select" -version = "0.12.1" +name = "rayon" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ - "bytes", - "futures", - "log", - "pin-project", - "smallvec", - "unsigned-varint", + "either", + "rayon-core", ] [[package]] -name = "nalgebra" -version = "0.32.3" +name = "rayon-core" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex", - "num-rational", - "num-traits", - "simba", - "typenum", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", ] [[package]] -name = "nalgebra-macros" -version = "0.2.1" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "bitflags 1.3.2", ] [[package]] -name = "names" -version = "0.13.0" +name = "redox_syscall" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "rand 0.8.5", + "bitflags 1.3.2", ] [[package]] -name = "nanorand" -version = "0.7.0" +name = "redox_users" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.10", + "redox_syscall 0.2.16", + "thiserror", +] [[package]] -name = "netlink-packet-core" -version = "0.4.2" +name = "ref-cast" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" dependencies = [ - "anyhow", - "byteorder", - "libc", - "netlink-packet-utils", + "ref-cast-impl", ] [[package]] -name = "netlink-packet-route" -version = "0.12.0" +name = "ref-cast-impl" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" dependencies = [ - "anyhow", - "bitflags 1.3.2", - "byteorder", - "libc", - "netlink-packet-core", - "netlink-packet-utils", + "proc-macro2", + "quote", + "syn 2.0.25", ] [[package]] -name = "netlink-packet-utils" -version = "0.5.2" +name = "regalloc2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror", + "fxhash", + "log", + "slice-group-by", + "smallvec", ] [[package]] -name = "netlink-proto" -version = "0.10.0" +name = "regex" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "bytes", - "futures", - "log", - "netlink-packet-core", - "netlink-sys", - "thiserror", - "tokio", + "aho-corasick", + "memchr", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] -name = "netlink-sys" -version = "0.8.5" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "bytes", - "futures", - "libc", - "log", - "tokio", + "regex-syntax 0.6.29", ] [[package]] -name = "nix" -version = "0.24.3" +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] -name = "no-std-net" -version = "0.6.0" +name = "regex-syntax" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "nodrop" -version = "0.1.14" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] -name = "nohash-hasher" -version = "0.2.0" +name = "region" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach", + "winapi", +] [[package]] -name = "nom" -version = "7.1.3" +name = "rfc6979" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "memchr", - "minimal-lexical", + "hmac 0.12.1", + "subtle", ] [[package]] -name = "normalize-line-endings" -version = "0.3.0" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "num-bigint" -version = "0.4.3" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "num-complex" -version = "0.4.3" +name = "rustc-hex" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] -name = "num-format" -version = "0.4.4" +name = "rustix" +version = "0.36.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" dependencies = [ - "arrayvec 0.7.4", - "itoa", + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "rustix" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "autocfg", - "num-traits", + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", ] [[package]] -name = "num-rational" -version = "0.4.1" +name = "rustix" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", + "windows-sys 0.48.0", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "rustversion" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm 0.2.7", -] +checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" [[package]] -name = "num_cpus" -version = "1.16.0" +name = "ryu" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] -name = "object" -version = "0.29.0" +name = "safe_arch" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" dependencies = [ - "crc32fast", - "hashbrown 0.12.3", - "indexmap 1.9.3", - "memchr", + "bytemuck", ] [[package]] -name = "object" -version = "0.30.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "crc32fast", - "hashbrown 0.13.2", - "indexmap 1.9.3", - "memchr", + "log", + "sp-core 7.0.0", + "sp-wasm-interface 7.0.0", + "thiserror", ] [[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +name = "sc-client-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "memchr", + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot", + "sc-executor", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core 7.0.0", + "sp-database", + "sp-externalities 0.13.0", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage 7.0.0", + "substrate-prometheus-endpoint", ] [[package]] -name = "oid-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "asn1-rs 0.3.1", + "lru", + "parity-scale-codec", + "parking_lot", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface 7.0.0", + "sp-trie", + "sp-version", + "sp-wasm-interface 7.0.0", + "tracing", + "wasmi", ] [[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "asn1-rs 0.5.2", + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-wasm-interface 7.0.0", + "thiserror", + "wasm-instrument", + "wasmi", ] [[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "log", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface 7.0.0", + "sp-wasm-interface 7.0.0", + "wasmi", +] [[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "log", + "once_cell", + "rustix 0.36.15", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface 7.0.0", + "sp-wasm-interface 7.0.0", + "wasmtime", +] [[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +name = "sc-transaction-pool-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures", + "log", + "serde", + "sp-blockchain", + "sp-runtime", + "thiserror", +] [[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +name = "sc-utils" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-channel", + "futures", + "futures-timer", + "lazy_static", + "log", + "parking_lot", + "prometheus", + "sp-arithmetic", +] [[package]] -name = "orchestra" -version = "0.0.5" +name = "scale-info" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227585216d05ba65c7ab0a0450a3cf2cbd81a98862a54c4df8e14d5ac6adb015" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ - "async-trait", - "dyn-clonable", - "futures", - "futures-timer", - "orchestra-proc-macro", - "pin-project", - "prioritized-metered-channel", - "thiserror", - "tracing", + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", ] [[package]] -name = "orchestra-proc-macro" -version = "0.0.5" +name = "scale-info-derive" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2871aadd82a2c216ee68a69837a526dfe788ecbe74c4c5038a6acdbff6653066" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ - "expander 0.0.6", - "itertools", - "petgraph", "proc-macro-crate", "proc-macro2", "quote", @@ -5385,7183 +2962,1711 @@ dependencies = [ ] [[package]] -name = "ordered-float" -version = "1.1.1" +name = "schnellru" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" dependencies = [ - "num-traits", + "ahash 0.8.3", + "cfg-if", + "hashbrown 0.13.2", ] [[package]] -name = "p256" -version = "0.11.1" +name = "schnorrkel" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.7", + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", ] [[package]] -name = "p384" -version = "0.11.2" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" -dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.7", -] +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "packed_simd_2" -version = "0.3.8" +name = "sec1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ - "cfg-if", - "libm 0.1.4", + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", ] [[package]] -name = "pallet-balances" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-runtime 7.0.0", - "sp-std 5.0.0", + "secp256k1-sys", ] [[package]] -name = "pallet-evm" -version = "6.0.0-dev" -source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#1a718546085be20ce381b70e1f9e4c8b4d4b1f03" +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" dependencies = [ - "environmental", - "evm", - "fp-account", - "fp-evm", - "frame-benchmarking", - "frame-support", - "frame-system", - "hex", - "hex-literal 0.4.1", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "rlp", - "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", + "cc", ] [[package]] -name = "pallet-ismp" -version = "0.1.0" -dependencies = [ - "ckb-merkle-mountain-range", - "derive_more", - "enum-as-inner", - "env_logger 0.10.0", - "frame-benchmarking", - "frame-support", - "frame-system", - "ismp", - "ismp-primitives", - "ismp-testsuite", - "log", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "pallet-timestamp" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-inherents 4.0.0-dev", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-timestamp", -] - -[[package]] -name = "parity-db" -version = "0.4.9" +name = "secrecy" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dab3ac198341b2f0fec6e7f8a6eeed07a41201d98a124260611598c142e76df" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" dependencies = [ - "blake2", - "crc32fast", - "fs2", - "hex", - "libc", - "log", - "lz4", - "memmap2", - "parking_lot 0.12.1", - "rand 0.8.5", - "siphasher", - "snap", + "zeroize", ] [[package]] -name = "parity-scale-codec" -version = "3.6.3" +name = "serde" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ - "arrayvec 0.7.4", - "bitvec", - "byte-slice-cast", - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", + "serde_derive", ] [[package]] -name = "parity-scale-codec-derive" -version = "3.6.3" +name = "serde_derive" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ - "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "parity-send-wrapper" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" - -[[package]] -name = "parity-wasm" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" - -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.8", + "syn 2.0.25", ] [[package]] -name = "parking_lot_core" -version = "0.8.6" +name = "serde_json" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "itoa", + "ryu", + "serde", ] [[package]] -name = "parking_lot_core" +name = "sha-1" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ + "block-buffer 0.9.0", "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec", - "windows-targets 0.48.1", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] -name = "paste" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" - -[[package]] -name = "pbkdf2" -version = "0.8.0" +name = "sha2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ - "crypto-mac 0.11.1", + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] -name = "pbkdf2" -version = "0.11.0" +name = "sha2" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "digest 0.10.7", + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] -name = "pbkdf2" -version = "0.12.2" +name = "sha2" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ + "cfg-if", + "cpufeatures", "digest 0.10.7", ] [[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pem" -version = "1.1.1" +name = "sha3" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "base64 0.13.1", + "digest 0.10.7", + "keccak", ] [[package]] -name = "pem-rfc7468" -version = "0.6.0" +name = "sharded-slab" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ - "base64ct", + "lazy_static", ] [[package]] -name = "percent-encoding" -version = "2.3.0" +name = "signature" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] -name = "petgraph" -version = "0.6.3" +name = "signature" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ - "fixedbitset", - "indexmap 1.9.3", + "digest 0.10.7", + "rand_core 0.6.4", ] [[package]] -name = "pin-project" -version = "1.1.2" +name = "simba" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" dependencies = [ - "pin-project-internal", + "approx", + "num-complex", + "num-traits", + "paste", + "wide", ] [[package]] -name = "pin-project-internal" -version = "1.1.2" +name = "slab" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", + "autocfg", ] [[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - -[[package]] -name = "pin-project-lite" -version = "0.2.10" +name = "slice-group-by" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "smallvec" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] -name = "pkcs8" -version = "0.9.0" +name = "socket2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ - "der 0.6.1", - "spki 0.6.0", + "libc", + "winapi", ] [[package]] -name = "pkcs8" -version = "0.10.2" +name = "soketto" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ - "der 0.7.7", - "spki 0.7.2", + "base64", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1", ] [[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "platforms" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" - -[[package]] -name = "polkadot-core-primitives" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ + "hash-db", + "log", "parity-scale-codec", "scale-info", + "sp-api-proc-macro", "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-metadata-ir", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", + "sp-trie", + "sp-version", + "thiserror", ] [[package]] -name = "polkadot-node-jaeger" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "lazy_static", - "log", - "mick-jaeger", - "parity-scale-codec", - "parking_lot 0.12.1", - "polkadot-node-primitives", - "polkadot-primitives", - "sc-network 0.10.0-dev", - "sp-core 7.0.0", - "thiserror", - "tokio", + "Inflector", + "blake2", + "expander", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.25", ] [[package]] -name = "polkadot-node-metrics" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-application-crypto" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "bs58 0.4.0", - "futures", - "futures-timer", - "log", "parity-scale-codec", - "polkadot-primitives", - "prioritized-metered-channel", - "sc-cli", - "sc-service", - "sc-tracing 4.0.0-dev", - "substrate-prometheus-endpoint 0.10.0-dev", - "tracing-gum", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-io", + "sp-std 5.0.0", ] [[package]] -name = "polkadot-node-network-protocol" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-arithmetic" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "async-trait", - "derive_more", - "fatality", - "futures", - "hex", + "integer-sqrt", + "num-traits", "parity-scale-codec", - "polkadot-node-jaeger", - "polkadot-node-primitives", - "polkadot-primitives", - "rand 0.8.5", - "sc-authority-discovery", - "sc-network 0.10.0-dev", - "strum", - "thiserror", - "tracing-gum", + "scale-info", + "serde", + "sp-std 5.0.0", + "static_assertions", ] [[package]] -name = "polkadot-node-primitives" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-blockchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "bounded-vec", "futures", + "log", + "lru", "parity-scale-codec", - "polkadot-parachain", - "polkadot-primitives", - "schnorrkel 0.9.1", - "serde", - "sp-application-crypto 7.0.0", - "sp-consensus-babe", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-maybe-compressed-blob 4.1.0-dev", - "sp-runtime 7.0.0", + "parking_lot", + "sp-api", + "sp-consensus", + "sp-database", + "sp-runtime", + "sp-state-machine", "thiserror", - "zstd 0.11.2+zstd.1.5.2", ] [[package]] -name = "polkadot-node-subsystem-types" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "derive_more", "futures", - "orchestra", - "polkadot-node-jaeger", - "polkadot-node-network-protocol", - "polkadot-node-primitives", - "polkadot-primitives", - "polkadot-statement-table", - "sc-network 0.10.0-dev", - "smallvec", - "sp-api 4.0.0-dev", - "sp-authority-discovery", - "sp-consensus-babe", - "substrate-prometheus-endpoint 0.10.0-dev", + "log", + "sp-core 7.0.0", + "sp-inherents", + "sp-runtime", + "sp-state-machine", "thiserror", ] [[package]] -name = "polkadot-overseer" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "async-trait", - "futures", - "futures-timer", - "lru 0.9.0", - "orchestra", - "parking_lot 0.12.1", - "polkadot-node-metrics", - "polkadot-node-network-protocol", - "polkadot-node-primitives", - "polkadot-node-subsystem-types", - "polkadot-primitives", - "sc-client-api 4.0.0-dev", - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "tikv-jemalloc-ctl", - "tracing-gum", -] - -[[package]] -name = "polkadot-parachain" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "bounded-collections", - "derive_more", - "frame-support", "parity-scale-codec", - "polkadot-core-primitives", "scale-info", - "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-inherents", + "sp-runtime", "sp-std 5.0.0", + "sp-timestamp", ] [[package]] -name = "polkadot-primitives" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "bitvec", - "hex-literal 0.4.1", "parity-scale-codec", - "polkadot-core-primitives", - "polkadot-parachain", "scale-info", "serde", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", - "sp-authority-discovery", - "sp-consensus-slots", - "sp-core 7.0.0", - "sp-inherents 4.0.0-dev", - "sp-io 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-staking 4.0.0-dev", "sp-std 5.0.0", + "sp-timestamp", ] [[package]] -name = "polkadot-statement-table" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +name = "sp-core" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ + "array-bytes", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", "parity-scale-codec", - "polkadot-primitives", - "sp-core 7.0.0", + "parking_lot", + "paste", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 5.0.0", + "sp-debug-derive 5.0.0", + "sp-externalities 0.13.0", + "sp-runtime-interface 7.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", ] [[package]] -name = "polling" -version = "2.8.0" +name = "sp-core" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" dependencies = [ - "autocfg", + "array-bytes", "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", "log", - "pin-project-lite 0.2.10", - "windows-sys 0.48.0", + "merlin", + "parity-scale-codec", + "parking_lot", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 8.0.0", + "sp-debug-derive 7.0.0", + "sp-externalities 0.18.0", + "sp-runtime-interface 16.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", ] [[package]] -name = "poly1305" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +name = "sp-core-hashing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 5.0.0", + "twox-hash", ] [[package]] -name = "polyval" -version = "0.5.3" +name = "sp-core-hashing" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 7.0.0", + "twox-hash", ] [[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash 0.5.1", + "proc-macro2", + "quote", + "sp-core-hashing 5.0.0", + "syn 2.0.25", ] [[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "predicates" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +name = "sp-database" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "difflib", - "float-cmp", - "itertools", - "normalize-line-endings", - "predicates-core", - "regex", + "kvdb", + "parking_lot", ] [[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +name = "sp-debug-derive" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "predicates-core", - "termtree", + "proc-macro2", + "quote", + "syn 2.0.25", ] [[package]] -name = "prettyplease" -version = "0.1.25" +name = "sp-debug-derive" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" dependencies = [ "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "prioritized-metered-channel" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382698e48a268c832d0b181ed438374a6bb708a82a8ca273bb0f61c74cf209c4" +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "coarsetime", - "crossbeam-queue", - "derive_more", - "futures", - "futures-timer", - "nanorand", - "thiserror", - "tracing", + "environmental", + "parity-scale-codec", + "sp-std 5.0.0", + "sp-storage 7.0.0", ] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "sp-externalities" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" dependencies = [ - "once_cell", - "toml_edit", + "environmental", + "parity-scale-codec", + "sp-std 7.0.0", + "sp-storage 12.0.0", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core 7.0.0", + "sp-runtime", + "sp-std 5.0.0", + "thiserror", ] [[package]] -name = "proc-macro-warning" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +name = "sp-io" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "rustversion", + "secp256k1", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-keystore", + "sp-runtime-interface 7.0.0", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing 6.0.0", + "sp-trie", + "tracing", + "tracing-core", ] [[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +name = "sp-keystore" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "unicode-ident", + "futures", + "parity-scale-codec", + "parking_lot", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "thiserror", ] [[package]] -name = "prometheus" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "memchr", - "parking_lot 0.12.1", "thiserror", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] -name = "prometheus-client" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "dtoa", - "itoa", - "parking_lot 0.12.1", - "prometheus-client-derive-text-encode", + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std 5.0.0", ] [[package]] -name = "prometheus-client-derive-text-encode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +name = "sp-panic-handler" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "backtrace", + "lazy_static", + "regex", ] [[package]] -name = "proptest" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +name = "sp-runtime" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "bit-set", - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "num-traits", + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_xorshift", - "regex-syntax 0.6.29", - "rusty-fork", - "tempfile", - "unarray", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core 7.0.0", + "sp-io", + "sp-std 5.0.0", + "sp-weights", ] [[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +name = "sp-runtime-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ "bytes", - "prost-derive", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.13.0", + "sp-runtime-interface-proc-macro 6.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-tracing 6.0.0", + "sp-wasm-interface 7.0.0", + "static_assertions", ] [[package]] -name = "prost-build" -version = "0.11.9" +name = "sp-runtime-interface" +version = "16.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "ca5d0cd80200bf85b8b064238b2508b69b6146b13adf36066ec5d924825af737" dependencies = [ "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 1.0.109", - "tempfile", - "which", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.18.0", + "sp-runtime-interface-proc-macro 10.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "sp-tracing 9.0.0", + "sp-wasm-interface 13.0.0", + "static_assertions", ] [[package]] -name = "prost-codec" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "asynchronous-codec", - "bytes", - "prost", - "thiserror", - "unsigned-varint", + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.25", ] [[package]] -name = "prost-derive" -version = "0.11.9" +name = "sp-runtime-interface-proc-macro" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" dependencies = [ - "anyhow", - "itertools", + "Inflector", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "prost", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-runtime", + "sp-std 5.0.0", ] [[package]] -name = "psm" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +name = "sp-state-machine" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "cc", + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-panic-handler", + "sp-std 5.0.0", + "sp-trie", + "thiserror", + "tracing", ] [[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +name = "sp-std" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" [[package]] -name = "quick-protobuf" -version = "0.8.1" +name = "sp-std" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" + +[[package]] +name = "sp-storage" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "byteorder", + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 5.0.0", + "sp-std 5.0.0", ] [[package]] -name = "quicksink" -version = "0.1.2" +name = "sp-storage" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" dependencies = [ - "futures-core", - "futures-sink", - "pin-project-lite 0.1.12", + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 7.0.0", + "sp-std 7.0.0", ] [[package]] -name = "quinn-proto" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +name = "sp-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "bytes", - "rand 0.8.5", - "ring", - "rustc-hash", - "rustls 0.20.8", - "slab", + "async-trait", + "futures-timer", + "log", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", "thiserror", - "tinyvec", - "tracing", - "webpki 0.22.0", ] [[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +name = "sp-tracing" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "proc-macro2", + "parity-scale-codec", + "sp-std 5.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", ] [[package]] -name = "radium" -version = "0.7.0" +name = "sp-tracing" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "rcgen" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" -dependencies = [ - "pem", - "ring", - "time 0.3.23", - "x509-parser 0.13.2", - "yasna", -] - -[[package]] -name = "rcgen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" -dependencies = [ - "pem", - "ring", - "time 0.3.23", - "yasna", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "ref-cast" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "regalloc2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" -dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.3.3", - "regex-syntax 0.7.4", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.4", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] - -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac 0.12.1", - "zeroize", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac 0.12.1", - "subtle", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rocksdb" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rpassword" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" -dependencies = [ - "libc", - "rtoolbox", - "winapi", -] - -[[package]] -name = "rtcp" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" -dependencies = [ - "bytes", - "thiserror", - "webrtc-util", -] - -[[package]] -name = "rtnetlink" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" -dependencies = [ - "futures", - "log", - "netlink-packet-route", - "netlink-proto", - "nix", - "thiserror", - "tokio", -] - -[[package]] -name = "rtoolbox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "rtp" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" -dependencies = [ - "async-trait", - "bytes", - "rand 0.8.5", - "serde", - "thiserror", - "webrtc-util", -] - -[[package]] -name = "ruint" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" -dependencies = [ - "proptest", - "rand 0.8.5", - "ruint-macro", - "serde", - "valuable", - "zeroize", -] - -[[package]] -name = "ruint-macro" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "0.36.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustix" -version = "0.37.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" -dependencies = [ - "bitflags 2.3.3", - "errno", - "libc", - "linux-raw-sys 0.4.3", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.1", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct 0.7.0", - "webpki 0.22.0", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.2", -] - -[[package]] -name = "rustversion" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ruzstd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc" -dependencies = [ - "byteorder", - "thiserror-core", - "twox-hash", -] - -[[package]] -name = "rw-stream-sink" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" -dependencies = [ - "futures", - "pin-project", - "static_assertions", -] - -[[package]] -name = "ryu" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" - -[[package]] -name = "safe_arch" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "sc-allocator" -version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "log", - "sp-core 7.0.0", - "sp-wasm-interface 7.0.0", - "thiserror", -] - -[[package]] -name = "sc-allocator" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa8784b53aa48736a4df4c351162a63b17e7c28c77b6a2e92dfb9bc49709107" -dependencies = [ - "log", - "sp-core 18.0.0", - "sp-wasm-interface 12.0.0", - "thiserror", -] - -[[package]] -name = "sc-authority-discovery" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "futures", - "futures-timer", - "ip_network", - "libp2p", - "log", - "parity-scale-codec", - "prost", - "prost-build", - "rand 0.8.5", - "sc-client-api 4.0.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sp-api 4.0.0-dev", - "sp-authority-discovery", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", -] - -[[package]] -name = "sc-block-builder" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "sc-client-api 4.0.0-dev", - "sp-api 4.0.0-dev", - "sp-block-builder 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", -] - -[[package]] -name = "sc-block-builder" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b4d582801c5d5f8dcfcba46b4724958283fda6b6bb44540f0ba931da736add2" -dependencies = [ - "parity-scale-codec", - "sc-client-api 18.0.0", - "sp-api 16.0.0", - "sp-block-builder 16.0.0", - "sp-blockchain 18.0.0", - "sp-core 18.0.0", - "sp-inherents 16.0.0", - "sp-runtime 20.0.0", - "sp-state-machine 0.24.0", -] - -[[package]] -name = "sc-chain-spec" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "memmap2", - "sc-chain-spec-derive 4.0.0-dev", - "sc-client-api 4.0.0-dev", - "sc-executor 0.10.0-dev", - "sc-network 0.10.0-dev", - "sc-telemetry 4.0.0-dev", - "serde", - "serde_json", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", -] - -[[package]] -name = "sc-chain-spec" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09024be9bc50b0dd86dbf90f982ac83001b69459758db695663a14242ca8198" -dependencies = [ - "memmap2", - "sc-chain-spec-derive 5.0.0", - "sc-network-common 0.23.0", - "sc-telemetry 7.0.0", - "serde", - "serde_json", - "sp-core 18.0.0", - "sp-runtime 20.0.0", -] - -[[package]] -name = "sc-chain-spec-derive" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sc-chain-spec-derive" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7185e052b4138f7023c6033926f458f2e46f215b6e02e0b5ac5863caa17b8cba" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sc-cli" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "chrono", - "clap", - "fdlimit", - "futures", - "libp2p", - "log", - "names", - "parity-scale-codec", - "rand 0.8.5", - "regex", - "rpassword", - "sc-client-api 4.0.0-dev", - "sc-client-db", - "sc-keystore", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-service", - "sc-telemetry 4.0.0-dev", - "sc-tracing 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde", - "serde_json", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-keyring", - "sp-keystore 0.13.0", - "sp-panic-handler 5.0.0", - "sp-runtime 7.0.0", - "sp-version 5.0.0", - "thiserror", - "tiny-bip39", - "tokio", -] - -[[package]] -name = "sc-client-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "fnv", - "futures", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-executor 0.10.0-dev", - "sc-transaction-pool-api 4.0.0-dev", - "sc-utils 4.0.0-dev", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-core 7.0.0", - "sp-database 4.0.0-dev", - "sp-externalities 0.13.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-storage 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", -] - -[[package]] -name = "sc-client-api" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd2bd00e841dfb8db0b477e7a03f60fa9e79b5cefd4ec2013e212a3a86ebcd3" -dependencies = [ - "fnv", - "futures", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-executor 0.22.0", - "sc-transaction-pool-api 18.0.0", - "sc-utils 6.0.0", - "sp-api 16.0.0", - "sp-blockchain 18.0.0", - "sp-consensus 0.22.0", - "sp-core 18.0.0", - "sp-database 5.0.0", - "sp-externalities 0.18.0", - "sp-keystore 0.24.0", - "sp-runtime 20.0.0", - "sp-state-machine 0.24.0", - "sp-storage 12.0.0", - "substrate-prometheus-endpoint 0.12.0", -] - -[[package]] -name = "sc-client-db" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "hash-db 0.16.0", - "kvdb", - "kvdb-memorydb", - "kvdb-rocksdb", - "linked-hash-map", - "log", - "parity-db", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-client-api 4.0.0-dev", - "sc-state-db", - "schnellru", - "sp-arithmetic 6.0.0", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-database 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-trie 7.0.0", -] - -[[package]] -name = "sc-consensus" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "futures", - "futures-timer", - "libp2p", - "log", - "mockall", - "parking_lot 0.12.1", - "sc-client-api 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", -] - -[[package]] -name = "sc-consensus" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fe228611617f1348a954e656b2b544eee5c0054000b712af8b0f05635b0015" -dependencies = [ - "async-trait", - "futures", - "futures-timer", - "libp2p", - "log", - "mockall", - "parking_lot 0.12.1", - "sc-client-api 18.0.0", - "sc-utils 6.0.0", - "serde", - "sp-api 16.0.0", - "sp-blockchain 18.0.0", - "sp-consensus 0.22.0", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "sp-state-machine 0.24.0", - "substrate-prometheus-endpoint 0.12.0", - "thiserror", -] - -[[package]] -name = "sc-consensus-grandpa" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "ahash 0.8.3", - "array-bytes", - "async-trait", - "dyn-clone", - "finality-grandpa", - "fork-tree 3.0.0", - "futures", - "futures-timer", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "sc-block-builder 0.10.0-dev", - "sc-chain-spec 4.0.0-dev", - "sc-client-api 4.0.0-dev", - "sc-consensus 0.10.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-network-gossip 0.10.0-dev", - "sc-telemetry 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde_json", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-consensus-grandpa", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", -] - -[[package]] -name = "sc-consensus-grandpa-rpc" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "finality-grandpa", - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "sc-client-api 4.0.0-dev", - "sc-consensus-grandpa", - "sc-rpc 4.0.0-dev", - "serde", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "thiserror", -] - -[[package]] -name = "sc-executor" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "lru 0.8.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-executor-common 0.10.0-dev", - "sc-executor-wasmi 0.10.0-dev", - "sc-executor-wasmtime 0.10.0-dev", - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-io 7.0.0", - "sp-panic-handler 5.0.0", - "sp-runtime-interface 7.0.0", - "sp-trie 7.0.0", - "sp-version 5.0.0", - "sp-wasm-interface 7.0.0", - "tracing", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e73e3ece87f0d6ca9f3f1f903213ee8aab58287dc0b3921779afbf62d34411f" -dependencies = [ - "lru 0.8.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-executor-common 0.20.0", - "sc-executor-wasmi 0.20.0", - "sc-executor-wasmtime 0.20.0", - "sp-api 16.0.0", - "sp-core 18.0.0", - "sp-externalities 0.18.0", - "sp-io 19.0.0", - "sp-panic-handler 7.0.0", - "sp-runtime-interface 15.0.0", - "sp-trie 18.0.0", - "sp-version 18.0.0", - "sp-wasm-interface 12.0.0", - "tracing", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-common" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "sc-allocator 4.1.0-dev", - "sp-maybe-compressed-blob 4.1.0-dev", - "sp-wasm-interface 7.0.0", - "thiserror", - "wasm-instrument", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-common" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eff0b3bb1d41ca34481215297090496c35e91c6d66a71e3a26960c8fce917ea" -dependencies = [ - "sc-allocator 14.0.0", - "sp-maybe-compressed-blob 5.0.0", - "sp-wasm-interface 12.0.0", - "thiserror", - "wasm-instrument", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "log", - "sc-allocator 4.1.0-dev", - "sc-executor-common 0.10.0-dev", - "sp-runtime-interface 7.0.0", - "sp-wasm-interface 7.0.0", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-wasmi" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034a219dc830432961b377f28d50d8251b2f20c26471436c47cf5268126e8e9f" -dependencies = [ - "log", - "sc-allocator 14.0.0", - "sc-executor-common 0.20.0", - "sp-runtime-interface 15.0.0", - "sp-wasm-interface 12.0.0", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-wasmtime" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "anyhow", - "cfg-if", - "libc", - "log", - "once_cell", - "rustix 0.36.15", - "sc-allocator 4.1.0-dev", - "sc-executor-common 0.10.0-dev", - "sp-runtime-interface 7.0.0", - "sp-wasm-interface 7.0.0", - "wasmtime 6.0.2", -] - -[[package]] -name = "sc-executor-wasmtime" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df03fdc79767bbc993cfcde07a8a09040e60fed1ca7341e3d17fc4c461e8457" -dependencies = [ - "anyhow", - "cfg-if", - "libc", - "log", - "once_cell", - "rustix 0.36.15", - "sc-allocator 14.0.0", - "sc-executor-common 0.20.0", - "sp-runtime-interface 15.0.0", - "sp-wasm-interface 12.0.0", - "wasmtime 6.0.2", -] - -[[package]] -name = "sc-finality-grandpa" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5923b98c37d2b246db78aae954aa42903537dacfa1effadf5dadb68bd932b16d" -dependencies = [ - "ahash 0.8.3", - "array-bytes", - "async-trait", - "dyn-clone", - "finality-grandpa", - "fork-tree 7.0.0", - "futures", - "futures-timer", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "sc-block-builder 0.23.0", - "sc-chain-spec 17.0.0", - "sc-client-api 18.0.0", - "sc-consensus 0.23.0", - "sc-network 0.24.0", - "sc-network-common 0.23.0", - "sc-network-gossip 0.24.0", - "sc-telemetry 7.0.0", - "sc-utils 6.0.0", - "serde_json", - "sp-api 16.0.0", - "sp-application-crypto 19.0.0", - "sp-arithmetic 13.0.0", - "sp-blockchain 18.0.0", - "sp-consensus 0.22.0", - "sp-core 18.0.0", - "sp-finality-grandpa", - "sp-keystore 0.24.0", - "sp-runtime 20.0.0", - "substrate-prometheus-endpoint 0.12.0", - "thiserror", -] - -[[package]] -name = "sc-finality-grandpa-rpc" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb40df2000f91bdcfc30575e5ca51d0fbd13deeda23246be6221ffa01ecc9ca" -dependencies = [ - "finality-grandpa", - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "sc-client-api 18.0.0", - "sc-finality-grandpa", - "sc-rpc 19.0.0", - "serde", - "sp-blockchain 18.0.0", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "thiserror", -] - -[[package]] -name = "sc-informant" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "ansi_term", - "futures", - "futures-timer", - "log", - "sc-client-api 4.0.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-runtime 7.0.0", -] - -[[package]] -name = "sc-keystore" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "async-trait", - "parking_lot 0.12.1", - "serde_json", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "thiserror", -] - -[[package]] -name = "sc-network" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "async-channel", - "async-trait", - "asynchronous-codec", - "bytes", - "either", - "fnv", - "futures", - "futures-timer", - "ip_network", - "libp2p", - "linked_hash_set", - "log", - "lru 0.8.1", - "mockall", - "parity-scale-codec", - "parking_lot 0.12.1", - "pin-project", - "rand 0.8.5", - "sc-block-builder 0.10.0-dev", - "sc-client-api 4.0.0-dev", - "sc-consensus 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde", - "serde_json", - "smallvec", - "snow", - "sp-arithmetic 6.0.0", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", - "unsigned-varint", - "zeroize", -] - -[[package]] -name = "sc-network" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a525c3f43fd1e9e09665763d0f0823699f5e78e31909b8949b03ad60a12806" -dependencies = [ - "array-bytes", - "async-trait", - "asynchronous-codec", - "backtrace", - "bytes", - "either", - "fnv", - "futures", - "futures-timer", - "ip_network", - "libp2p", - "log", - "lru 0.8.1", - "mockall", - "parity-scale-codec", - "parking_lot 0.12.1", - "pin-project", - "rand 0.8.5", - "sc-block-builder 0.23.0", - "sc-client-api 18.0.0", - "sc-consensus 0.23.0", - "sc-network-common 0.23.0", - "sc-peerset 7.0.0", - "sc-utils 6.0.0", - "serde", - "serde_json", - "smallvec", - "sp-arithmetic 13.0.0", - "sp-blockchain 18.0.0", - "sp-consensus 0.22.0", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "substrate-prometheus-endpoint 0.12.0", - "thiserror", - "unsigned-varint", - "zeroize", -] - -[[package]] -name = "sc-network-bitswap" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "cid", - "futures", - "libp2p", - "log", - "prost", - "prost-build", - "sc-client-api 4.0.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-runtime 7.0.0", - "thiserror", - "unsigned-varint", -] - -[[package]] -name = "sc-network-common" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "async-trait", - "bitflags 1.3.2", - "bytes", - "futures", - "futures-timer", - "libp2p", - "parity-scale-codec", - "prost-build", - "sc-consensus 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde", - "smallvec", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-consensus-grandpa", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", - "zeroize", -] - -[[package]] -name = "sc-network-common" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39d56f7508b31ac31144903cb2e48268842ec87d3fc5237772e29473868b27" -dependencies = [ - "async-trait", - "bitflags 1.3.2", - "bytes", - "futures", - "futures-timer", - "libp2p", - "linked_hash_set", - "parity-scale-codec", - "prost-build", - "sc-consensus 0.23.0", - "sc-peerset 7.0.0", - "serde", - "smallvec", - "sp-blockchain 18.0.0", - "sp-consensus 0.22.0", - "sp-finality-grandpa", - "sp-runtime 20.0.0", - "substrate-prometheus-endpoint 0.12.0", - "thiserror", -] - -[[package]] -name = "sc-network-gossip" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "ahash 0.8.3", - "futures", - "futures-timer", - "libp2p", - "log", - "lru 0.8.1", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "tracing", -] - -[[package]] -name = "sc-network-gossip" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8b63eedbe7e08661777e6f5960952fcc8d79d98d691a99f9bc05d5850a9a9f2" -dependencies = [ - "ahash 0.8.3", - "futures", - "futures-timer", - "libp2p", - "log", - "lru 0.8.1", - "sc-network-common 0.23.0", - "sc-peerset 7.0.0", - "sp-runtime 20.0.0", - "substrate-prometheus-endpoint 0.12.0", - "tracing", -] - -[[package]] -name = "sc-network-light" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "futures", - "libp2p", - "log", - "parity-scale-codec", - "prost", - "prost-build", - "sc-client-api 4.0.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "thiserror", -] - -[[package]] -name = "sc-network-sync" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "async-trait", - "fork-tree 3.0.0", - "futures", - "futures-timer", - "libp2p", - "log", - "lru 0.8.1", - "mockall", - "parity-scale-codec", - "prost", - "prost-build", - "sc-client-api 4.0.0-dev", - "sc-consensus 0.10.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sc-utils 4.0.0-dev", - "smallvec", - "sp-arithmetic 6.0.0", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-consensus-grandpa", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", -] - -[[package]] -name = "sc-network-transactions" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "futures", - "libp2p", - "log", - "parity-scale-codec", - "pin-project", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sc-utils 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint 0.10.0-dev", -] - -[[package]] -name = "sc-offchain" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "bytes", - "fnv", - "futures", - "futures-timer", - "hyper", - "hyper-rustls", - "libp2p", - "num_cpus", - "once_cell", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "sc-client-api 4.0.0-dev", - "sc-network 0.10.0-dev", - "sc-network-common 0.10.0-dev", - "sc-peerset 4.0.0-dev", - "sc-utils 4.0.0-dev", - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "sp-offchain 4.0.0-dev", - "sp-runtime 7.0.0", - "threadpool", - "tracing", -] - -[[package]] -name = "sc-peerset" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "futures", - "libp2p", - "log", - "sc-utils 4.0.0-dev", - "serde_json", - "wasm-timer", -] - -[[package]] -name = "sc-peerset" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3db7a2b9fba75b084ea9a51b4911a99d9387777c60e2cfacfd60a3676ea788" -dependencies = [ - "futures", - "libp2p", - "log", - "sc-utils 6.0.0", - "serde_json", - "wasm-timer", -] - -[[package]] -name = "sc-rpc" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-block-builder 0.10.0-dev", - "sc-chain-spec 4.0.0-dev", - "sc-client-api 4.0.0-dev", - "sc-rpc-api 0.10.0-dev", - "sc-tracing 4.0.0-dev", - "sc-transaction-pool-api 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde_json", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-offchain 4.0.0-dev", - "sp-rpc 6.0.0", - "sp-runtime 7.0.0", - "sp-session 4.0.0-dev", - "sp-version 5.0.0", - "tokio", -] - -[[package]] -name = "sc-rpc" -version = "19.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ac425927dbd042a503a4b1f66aa0c2bf8f928f123de04ce0e742e13187edc3" -dependencies = [ - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-block-builder 0.23.0", - "sc-chain-spec 17.0.0", - "sc-client-api 18.0.0", - "sc-rpc-api 0.23.0", - "sc-tracing 18.0.0", - "sc-transaction-pool-api 18.0.0", - "sc-utils 6.0.0", - "serde_json", - "sp-api 16.0.0", - "sp-blockchain 18.0.0", - "sp-core 18.0.0", - "sp-keystore 0.24.0", - "sp-offchain 16.0.0", - "sp-rpc 17.0.0", - "sp-runtime 20.0.0", - "sp-session 17.0.0", - "sp-version 18.0.0", - "tokio", -] - -[[package]] -name = "sc-rpc-api" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "jsonrpsee", - "parity-scale-codec", - "sc-chain-spec 4.0.0-dev", - "sc-transaction-pool-api 4.0.0-dev", - "scale-info", - "serde", - "serde_json", - "sp-core 7.0.0", - "sp-rpc 6.0.0", - "sp-runtime 7.0.0", - "sp-version 5.0.0", - "thiserror", -] - -[[package]] -name = "sc-rpc-api" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb049c6dea3c497059f4a8c6153cb6715d1f0d9af74635f503114b46692685a0" -dependencies = [ - "jsonrpsee", - "parity-scale-codec", - "sc-chain-spec 17.0.0", - "sc-transaction-pool-api 18.0.0", - "scale-info", - "serde", - "serde_json", - "sp-core 18.0.0", - "sp-rpc 17.0.0", - "sp-runtime 20.0.0", - "sp-version 18.0.0", - "thiserror", -] - -[[package]] -name = "sc-rpc-server" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "http", - "jsonrpsee", - "log", - "serde_json", - "substrate-prometheus-endpoint 0.10.0-dev", - "tokio", - "tower", - "tower-http", -] - -[[package]] -name = "sc-rpc-server" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea79cd739532f851731af7917383bcca11f811f5e6f315e34f72873cfaebaac" -dependencies = [ - "http", - "jsonrpsee", - "log", - "serde_json", - "substrate-prometheus-endpoint 0.12.0", - "tokio", - "tower", - "tower-http", -] - -[[package]] -name = "sc-rpc-spec-v2" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "futures", - "futures-util", - "hex", - "jsonrpsee", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-chain-spec 4.0.0-dev", - "sc-client-api 4.0.0-dev", - "sc-transaction-pool-api 4.0.0-dev", - "serde", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-version 5.0.0", - "thiserror", - "tokio-stream", -] - -[[package]] -name = "sc-service" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "directories", - "exit-future", - "futures", - "futures-timer", - "jsonrpsee", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "pin-project", - "rand 0.8.5", - "sc-block-builder 0.10.0-dev", - "sc-chain-spec 4.0.0-dev", - "sc-client-api 4.0.0-dev", - "sc-client-db", - "sc-consensus 0.10.0-dev", - "sc-executor 0.10.0-dev", - "sc-informant", - "sc-keystore", - "sc-network 0.10.0-dev", - "sc-network-bitswap", - "sc-network-common 0.10.0-dev", - "sc-network-light", - "sc-network-sync", - "sc-network-transactions", - "sc-offchain", - "sc-rpc 4.0.0-dev", - "sc-rpc-server 4.0.0-dev", - "sc-rpc-spec-v2", - "sc-storage-monitor", - "sc-sysinfo", - "sc-telemetry 4.0.0-dev", - "sc-tracing 4.0.0-dev", - "sc-transaction-pool", - "sc-transaction-pool-api 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde", - "serde_json", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-session 4.0.0-dev", - "sp-state-machine 0.13.0", - "sp-storage 7.0.0", - "sp-transaction-pool", - "sp-transaction-storage-proof", - "sp-trie 7.0.0", - "sp-version 5.0.0", - "static_init", - "substrate-prometheus-endpoint 0.10.0-dev", - "tempfile", - "thiserror", - "tokio", - "tracing", - "tracing-futures", -] - -[[package]] -name = "sc-state-db" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sp-core 7.0.0", -] - -[[package]] -name = "sc-storage-monitor" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "clap", - "fs4", - "futures", - "log", - "sc-client-db", - "sc-utils 4.0.0-dev", - "sp-core 7.0.0", - "thiserror", - "tokio", -] - -[[package]] -name = "sc-sysinfo" -version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "futures", - "libc", - "log", - "rand 0.8.5", - "rand_pcg", - "regex", - "sc-telemetry 4.0.0-dev", - "serde", - "serde_json", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sc-telemetry" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "chrono", - "futures", - "libp2p", - "log", - "parking_lot 0.12.1", - "pin-project", - "rand 0.8.5", - "sc-utils 4.0.0-dev", - "serde", - "serde_json", - "thiserror", - "wasm-timer", -] - -[[package]] -name = "sc-telemetry" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b48c2f39c9ba57b56877bedb7cf726a4f378969e87db9fdbc1feb8f6e34161e2" -dependencies = [ - "chrono", - "futures", - "libp2p", - "log", - "parking_lot 0.12.1", - "pin-project", - "rand 0.8.5", - "sc-utils 6.0.0", - "serde", - "serde_json", - "thiserror", - "wasm-timer", -] - -[[package]] -name = "sc-tracing" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "ansi_term", - "atty", - "chrono", - "lazy_static", - "libc", - "log", - "once_cell", - "parking_lot 0.12.1", - "regex", - "rustc-hash", - "sc-client-api 4.0.0-dev", - "sc-rpc-server 4.0.0-dev", - "sc-tracing-proc-macro 4.0.0-dev", - "serde", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-rpc 6.0.0", - "sp-runtime 7.0.0", - "sp-tracing 6.0.0", - "thiserror", - "tracing", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "sc-tracing" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535733abff4107ef013706053e4c6a9f6b308b097a55ba10aab160fd3163112a" -dependencies = [ - "ansi_term", - "atty", - "chrono", - "lazy_static", - "libc", - "log", - "once_cell", - "parking_lot 0.12.1", - "regex", - "rustc-hash", - "sc-client-api 18.0.0", - "sc-rpc-server 6.0.0", - "sc-tracing-proc-macro 5.0.0", - "serde", - "sp-api 16.0.0", - "sp-blockchain 18.0.0", - "sp-core 18.0.0", - "sp-rpc 17.0.0", - "sp-runtime 20.0.0", - "sp-tracing 9.0.0", - "thiserror", - "tracing", - "tracing-log", - "tracing-subscriber", -] - -[[package]] -name = "sc-tracing-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sc-tracing-proc-macro" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3124ec69b179bca71cd20babf10f2198a5fc0d55f3e06a8f284e90930c76d3df" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sc-transaction-pool" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "futures", - "futures-timer", - "linked-hash-map", - "log", - "num-traits", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-client-api 4.0.0-dev", - "sc-transaction-pool-api 4.0.0-dev", - "sc-utils 4.0.0-dev", - "serde", - "sp-api 4.0.0-dev", - "sp-blockchain 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-tracing 6.0.0", - "sp-transaction-pool", - "substrate-prometheus-endpoint 0.10.0-dev", - "thiserror", -] - -[[package]] -name = "sc-transaction-pool-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "futures", - "log", - "serde", - "sp-blockchain 4.0.0-dev", - "sp-runtime 7.0.0", - "thiserror", -] - -[[package]] -name = "sc-transaction-pool-api" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92f2fa27a44912eeed796712b4b79fd5bb3c3763d54721e3414270123828054e" -dependencies = [ - "async-trait", - "futures", - "log", - "serde", - "sp-blockchain 18.0.0", - "sp-runtime 20.0.0", - "thiserror", -] - -[[package]] -name = "sc-utils" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-channel", - "futures", - "futures-timer", - "lazy_static", - "log", - "parking_lot 0.12.1", - "prometheus", - "sp-arithmetic 6.0.0", -] - -[[package]] -name = "sc-utils" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c227f2e96f3dca6f8a0dbd0d935ac273219365fc50d762d0328d66129fbd1c5e" -dependencies = [ - "backtrace", - "futures", - "futures-timer", - "lazy_static", - "log", - "parking_lot 0.12.1", - "prometheus", -] - -[[package]] -name = "scale-bits" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", -] - -[[package]] -name = "scale-decode" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0459d00b0dbd2e765009924a78ef36b2ff7ba116292d732f00eb0ed8e465d15" -dependencies = [ - "parity-scale-codec", - "primitive-types", - "scale-bits", - "scale-decode-derive", - "scale-info", - "smallvec", - "thiserror", -] - -[[package]] -name = "scale-decode-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4391f0dfbb6690f035f6d2a15d6a12f88cc5395c36bcc056db07ffa2a90870ec" -dependencies = [ - "darling 0.14.4", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "scale-encode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0401b7cdae8b8aa33725f3611a051358d5b32887ecaa0fda5953a775b2d4d76" -dependencies = [ - "parity-scale-codec", - "primitive-types", - "scale-bits", - "scale-encode-derive", - "scale-info", - "smallvec", - "thiserror", -] - -[[package]] -name = "scale-encode-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316e0fb10ec0fee266822bd641bab5e332a4ab80ef8c5b5ff35e5401a394f5a6" -dependencies = [ - "darling 0.14.4", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "scale-info" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" -dependencies = [ - "bitvec", - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", - "serde", -] - -[[package]] -name = "scale-info-derive" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "scale-value" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2096d36e94ce9bf87d8addb752423b6b19730dc88edd7cc452bb2b90573f7a7" -dependencies = [ - "base58", - "blake2", - "either", - "frame-metadata 15.1.0", - "parity-scale-codec", - "scale-bits", - "scale-decode", - "scale-encode", - "scale-info", - "serde", - "thiserror", - "yap", -] - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "schnellru" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" -dependencies = [ - "ahash 0.8.3", - "cfg-if", - "hashbrown 0.13.2", -] - -[[package]] -name = "schnorrkel" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "curve25519-dalek 2.1.3", - "getrandom 0.1.16", - "merlin 2.0.1", - "rand 0.7.3", - "rand_core 0.5.1", - "sha2 0.8.2", - "subtle", - "zeroize", -] - -[[package]] -name = "schnorrkel" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "844b7645371e6ecdf61ff246ba1958c29e802881a749ae3fb1993675d210d28d" -dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "curve25519-dalek-ng", - "merlin 3.0.0", - "rand_core 0.6.4", - "sha2 0.9.9", - "subtle-ng", - "zeroize", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sdp" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" -dependencies = [ - "rand 0.8.5", - "substring", - "thiserror", - "url", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array 0.14.7", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - -[[package]] -name = "sec1" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.7", - "generic-array 0.14.7", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" -dependencies = [ - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" -dependencies = [ - "cc", -] - -[[package]] -name = "secrecy" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" -dependencies = [ - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - -[[package]] -name = "serde" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "serde_json" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "simba" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" -dependencies = [ - "approx", - "num-complex", - "num-traits", - "paste", - "wide", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "smol" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" -dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", -] - -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - -[[package]] -name = "smoldot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cce5e2881b30bad7ef89f383a816ad0b22c45915911f28499026de4a76d20ee" -dependencies = [ - "arrayvec 0.7.4", - "async-lock", - "atomic", - "base64 0.21.2", - "bip39", - "blake2-rfc", - "bs58 0.5.0", - "crossbeam-queue", - "derive_more", - "ed25519-zebra", - "either", - "event-listener", - "fnv", - "futures-channel", - "futures-util", - "hashbrown 0.14.0", - "hex", - "hmac 0.12.1", - "itertools", - "libsecp256k1", - "merlin 3.0.0", - "no-std-net", - "nom", - "num-bigint", - "num-rational", - "num-traits", - "pbkdf2 0.12.2", - "pin-project", - "rand 0.8.5", - "rand_chacha 0.3.1", - "ruzstd", - "schnorrkel 0.10.2", - "serde", - "serde_json", - "sha2 0.10.7", - "siphasher", - "slab", - "smallvec", - "smol", - "snow", - "soketto", - "tiny-keccak", - "twox-hash", - "wasmi 0.30.0", -] - -[[package]] -name = "smoldot-light" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2f7b4687b83ff244ef6137735ed5716ad37dcdf3ee16c4eb1a32fb9808fa47" -dependencies = [ - "async-lock", - "blake2-rfc", - "derive_more", - "either", - "event-listener", - "fnv", - "futures-channel", - "futures-util", - "hashbrown 0.14.0", - "hex", - "itertools", - "log", - "lru 0.10.1", - "parking_lot 0.12.1", - "rand 0.8.5", - "serde", - "serde_json", - "siphasher", - "slab", - "smol", - "smoldot", -] - -[[package]] -name = "snap" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" - -[[package]] -name = "snow" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" -dependencies = [ - "aes-gcm 0.9.4", - "blake2", - "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.1", - "rand_core 0.6.4", - "ring", - "rustc_version", - "sha2 0.10.7", - "subtle", -] - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "soketto" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" -dependencies = [ - "base64 0.13.1", - "bytes", - "flate2", - "futures", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha-1", -] - -[[package]] -name = "sp-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "hash-db 0.16.0", - "log", - "parity-scale-codec", - "scale-info", - "sp-api-proc-macro 4.0.0-dev", - "sp-core 7.0.0", - "sp-metadata-ir", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", - "sp-trie 7.0.0", - "sp-version 5.0.0", - "thiserror", -] - -[[package]] -name = "sp-api" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2243c0405c7fbb97bac008638001daf33fe155e15f8ba28f994908b7359f5b" -dependencies = [ - "hash-db 0.15.2", - "log", - "parity-scale-codec", - "sp-api-proc-macro 5.0.0", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "sp-state-machine 0.24.0", - "sp-std 7.0.0", - "sp-trie 18.0.0", - "sp-version 18.0.0", - "thiserror", -] - -[[package]] -name = "sp-api-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "Inflector", - "blake2", - "expander 1.0.0", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sp-api-proc-macro" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1202abd8f0b1709386c29071539d57009a8c0ea8e841418dc114461a01a3040" -dependencies = [ - "blake2", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sp-application-crypto" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-application-crypto" -version = "19.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e5d5ec374fc23f4e1b87219be18e01080d8a21a2dee3b49df8befeddbf5780" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 18.0.0", - "sp-io 19.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-application-crypto" -version = "23.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899492ea547816d5dfe9a5a2ecc32f65a7110805af6da3380aa4902371b31dc2" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-std 8.0.0", -] - -[[package]] -name = "sp-arithmetic" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-std 5.0.0", - "static_assertions", -] - -[[package]] -name = "sp-arithmetic" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3dd56a02ca86de62dc9485d95830a5fed56fd7e4a22b13c01e62e73bc2094d2" -dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-std 7.0.0", - "static_assertions", -] - -[[package]] -name = "sp-arithmetic" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6020576e544c6824a51d651bc8df8e6ab67cd59f1c9ac09868bb81a5199ded" -dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-std 8.0.0", - "static_assertions", -] - -[[package]] -name = "sp-authority-discovery" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-block-builder" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "sp-api 4.0.0-dev", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-block-builder" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36478fa5a773d732e35d22fa25d63f7982b2970a9048a25fcd947398ecc84e31" -dependencies = [ - "parity-scale-codec", - "sp-api 16.0.0", - "sp-inherents 16.0.0", - "sp-runtime 20.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-blockchain" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "futures", - "log", - "lru 0.8.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "sp-api 4.0.0-dev", - "sp-consensus 0.10.0-dev", - "sp-database 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "thiserror", -] - -[[package]] -name = "sp-blockchain" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7c5d4228728601b7fa86499f6cec983dc8f1135fa1bad5e86fd31cfd660e32" -dependencies = [ - "futures", - "log", - "lru 0.8.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "sp-api 16.0.0", - "sp-consensus 0.22.0", - "sp-database 5.0.0", - "sp-runtime 20.0.0", - "sp-state-machine 0.24.0", - "thiserror", -] - -[[package]] -name = "sp-consensus" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "futures", - "log", - "sp-core 7.0.0", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "thiserror", -] - -[[package]] -name = "sp-consensus" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94dbbd8b6a52634c5920f27886bb9d0d6946393108e05fb8cc710950a87ad378" -dependencies = [ - "async-trait", - "futures", - "log", - "parity-scale-codec", - "sp-core 18.0.0", - "sp-inherents 16.0.0", - "sp-runtime 20.0.0", - "sp-state-machine 0.24.0", - "sp-std 7.0.0", - "sp-version 18.0.0", - "thiserror", -] - -[[package]] -name = "sp-consensus-aura" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "parity-scale-codec", - "scale-info", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", - "sp-consensus 0.10.0-dev", - "sp-consensus-slots", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-timestamp", -] - -[[package]] -name = "sp-consensus-babe" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", - "sp-consensus 0.10.0-dev", - "sp-consensus-slots", - "sp-core 7.0.0", - "sp-inherents 4.0.0-dev", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-timestamp", -] - -[[package]] -name = "sp-consensus-grandpa" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "finality-grandpa", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api 4.0.0-dev", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-consensus-slots" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-std 5.0.0", - "sp-timestamp", -] - -[[package]] -name = "sp-core" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "bitflags 1.3.2", - "blake2", - "bounded-collections", - "bs58 0.4.0", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db 0.16.0", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin 2.0.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "paste", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel 0.9.1", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing 5.0.0", - "sp-debug-derive 5.0.0", - "sp-externalities 0.13.0", - "sp-runtime-interface 7.0.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea27a1d8de728306d17502ba13127a1b1149c66e0ef348f67dafad630b50c1d" -dependencies = [ - "array-bytes", - "base58", - "bitflags 1.3.2", - "blake2", - "bounded-collections", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db 0.15.2", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin 2.0.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel 0.9.1", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing 7.0.0", - "sp-debug-derive 7.0.0", - "sp-externalities 0.18.0", - "sp-runtime-interface 15.0.0", - "sp-std 7.0.0", - "sp-storage 12.0.0", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "20.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" -dependencies = [ - "array-bytes", - "bitflags 1.3.2", - "blake2", - "bounded-collections", - "bs58 0.4.0", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db 0.16.0", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin 2.0.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel 0.9.1", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing 8.0.0", - "sp-debug-derive 7.0.0", - "sp-externalities 0.18.0", - "sp-runtime-interface 16.0.0", - "sp-std 7.0.0", - "sp-storage 12.0.0", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18d9e2f67d8661f9729f35347069ac29d92758b59135176799db966947a7336" -dependencies = [ - "array-bytes", - "bitflags 1.3.2", - "blake2", - "bounded-collections", - "bs58 0.4.0", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db 0.16.0", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin 2.0.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "paste", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel 0.9.1", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing 9.0.0", - "sp-debug-derive 8.0.0", - "sp-externalities 0.19.0", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - -[[package]] -name = "sp-core-hashing" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.7", - "sha3", - "sp-std 5.0.0", - "twox-hash", -] - -[[package]] -name = "sp-core-hashing" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d607f7209b1b9571177fc3722a03312df03606bb65f89317ba686d5fa59d438f" -dependencies = [ - "blake2", - "byteorder", - "digest 0.10.7", - "sha2 0.10.7", - "sha3", - "sp-std 7.0.0", - "twox-hash", -] - -[[package]] -name = "sp-core-hashing" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.7", - "sha3", - "sp-std 7.0.0", - "twox-hash", -] - -[[package]] -name = "sp-core-hashing" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee599a8399448e65197f9a6cee338ad192e9023e35e31f22382964c3c174c68" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.7", - "sha3", - "sp-std 8.0.0", - "twox-hash", -] - -[[package]] -name = "sp-core-hashing-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro2", - "quote", - "sp-core-hashing 5.0.0", - "syn 2.0.25", -] - -[[package]] -name = "sp-core-hashing-proc-macro" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86d231d36b86d5d433c3e439e0dcaa9192861eee30158ee12c7bc009e02bdbb" -dependencies = [ - "proc-macro2", - "quote", - "sp-core-hashing 7.0.0", - "syn 1.0.109", -] - -[[package]] -name = "sp-database" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "kvdb", - "parking_lot 0.12.1", -] - -[[package]] -name = "sp-database" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd6ef59a4a9e1945d5b49eb10a957b9d6b1c83af8379351baf0fa8ec12d8d64" -dependencies = [ - "kvdb", - "parking_lot 0.12.1", -] - -[[package]] -name = "sp-debug-derive" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sp-debug-derive" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sp-debug-derive" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f531814d2f16995144c74428830ccf7d94ff4a7749632b83ad8199b181140c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sp-externalities" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std 5.0.0", - "sp-storage 7.0.0", -] - -[[package]] -name = "sp-externalities" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" -dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std 7.0.0", - "sp-storage 12.0.0", -] - -[[package]] -name = "sp-externalities" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f71c671e01a8ca60da925d43a1b351b69626e268b8837f8371e320cf1dd100" -dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std 8.0.0", - "sp-storage 13.0.0", -] - -[[package]] -name = "sp-finality-grandpa" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "270f72b20608f1c49bf8e9f8c523764d1d7c9910aba9f48388f78f48d934ed05" -dependencies = [ - "finality-grandpa", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api 16.0.0", - "sp-application-crypto 19.0.0", - "sp-core 18.0.0", - "sp-keystore 0.24.0", - "sp-runtime 20.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-inherents" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "impl-trait-for-tuples", - "parity-scale-codec", - "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "thiserror", -] - -[[package]] -name = "sp-inherents" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b770e5c0a7deb764ae260cea66fcf52c74203d9af1342389b5f962bfe736f9b0" -dependencies = [ - "async-trait", - "impl-trait-for-tuples", - "parity-scale-codec", - "scale-info", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "sp-std 7.0.0", - "thiserror", -] - -[[package]] -name = "sp-io" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "bytes", - "ed25519", - "ed25519-dalek", - "futures", - "libsecp256k1", - "log", - "parity-scale-codec", - "rustversion", - "secp256k1", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-keystore 0.13.0", - "sp-runtime-interface 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", - "sp-tracing 6.0.0", - "sp-trie 7.0.0", - "tracing", - "tracing-core", -] - -[[package]] -name = "sp-io" -version = "19.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be5c4b33aa06da7745be99da2380a500d2f5ccf9b2df5b344d5d1c675adedaa" -dependencies = [ - "bytes", - "ed25519", - "ed25519-dalek", - "futures", - "libsecp256k1", - "log", - "parity-scale-codec", - "secp256k1", - "sp-core 18.0.0", - "sp-externalities 0.18.0", - "sp-keystore 0.24.0", - "sp-runtime-interface 15.0.0", - "sp-state-machine 0.24.0", - "sp-std 7.0.0", - "sp-tracing 9.0.0", - "sp-trie 18.0.0", - "tracing", - "tracing-core", -] - -[[package]] -name = "sp-io" -version = "23.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d597e35a9628fe7454b08965b2442e3ec0f264b0a90d41328e87422cec02e99" -dependencies = [ - "bytes", - "ed25519", - "ed25519-dalek", - "futures", - "libsecp256k1", - "log", - "parity-scale-codec", - "rustversion", - "secp256k1", - "sp-core 21.0.0", - "sp-externalities 0.19.0", - "sp-keystore 0.27.0", - "sp-runtime-interface 17.0.0", - "sp-state-machine 0.28.0", - "sp-std 8.0.0", - "sp-tracing 10.0.0", - "sp-trie 22.0.0", - "tracing", - "tracing-core", -] - -[[package]] -name = "sp-keyring" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "lazy_static", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "strum", -] - -[[package]] -name = "sp-keystore" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "futures", - "parity-scale-codec", - "parking_lot 0.12.1", - "serde", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "thiserror", -] - -[[package]] -name = "sp-keystore" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "811b1f0e8fc5b71fa359f5b4b67adedeba5dc313415e2923f8055e72c172a6ce" -dependencies = [ - "async-trait", - "futures", - "merlin 2.0.1", - "parity-scale-codec", - "parking_lot 0.12.1", - "schnorrkel 0.9.1", - "serde", - "sp-core 18.0.0", - "sp-externalities 0.18.0", - "thiserror", -] - -[[package]] -name = "sp-keystore" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be3cdd67cc1d9c1db17c5cbc4ec4924054a8437009d167f21f6590797e4aa45" -dependencies = [ - "futures", - "parity-scale-codec", - "parking_lot 0.12.1", - "sp-core 21.0.0", - "sp-externalities 0.19.0", - "thiserror", -] - -[[package]] -name = "sp-maybe-compressed-blob" -version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "thiserror", - "zstd 0.12.3+zstd.1.5.2", -] - -[[package]] -name = "sp-maybe-compressed-blob" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df7732c6f130c3e819b142dc76bff0380133b65095567891c0a6a888c147fa3" -dependencies = [ - "thiserror", - "zstd 0.11.2+zstd.1.5.2", -] - -[[package]] -name = "sp-metadata-ir" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-metadata 15.1.0", - "parity-scale-codec", - "scale-info", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-offchain" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", -] - -[[package]] -name = "sp-offchain" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a045eed6f08d9993fe797c37959db2fd0251198881cf98f8f606448da5c01da2" -dependencies = [ - "sp-api 16.0.0", - "sp-core 18.0.0", - "sp-runtime 20.0.0", -] - -[[package]] -name = "sp-panic-handler" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - -[[package]] -name = "sp-panic-handler" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75986cc917d897e0f6d0c848088064df4c74ccbb8f1c1848700b725f5ca7fe04" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - -[[package]] -name = "sp-panic-handler" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd2de46003fa8212426838ca71cd42ee36a26480ba9ffea983506ce03131033" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - -[[package]] -name = "sp-rpc" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "rustc-hash", - "serde", - "sp-core 7.0.0", -] - -[[package]] -name = "sp-rpc" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8b955c29f9f078dd19dd7e4e6b57199b4206e468454ec3d6a7330886a1dd9b" -dependencies = [ - "rustc-hash", - "serde", - "sp-core 18.0.0", -] - -[[package]] -name = "sp-runtime" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "paste", - "rand 0.8.5", - "scale-info", - "serde", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-std 5.0.0", - "sp-weights 4.0.0", -] - -[[package]] -name = "sp-runtime" -version = "20.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02650b39d4bf5966fcd80a5b11e0cc871620952ab9be901edf1fdf1460b1ea9" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "paste", - "rand 0.8.5", - "scale-info", - "serde", - "sp-application-crypto 19.0.0", - "sp-arithmetic 13.0.0", - "sp-core 18.0.0", - "sp-io 19.0.0", - "sp-std 7.0.0", - "sp-weights 16.0.0", -] - -[[package]] -name = "sp-runtime" -version = "24.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c5bfc764a1a8259d7e8f7cfd22c84006275a512c958d3ff966c92151e134d5" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "paste", - "rand 0.8.5", - "scale-info", - "serde", - "sp-application-crypto 23.0.0", - "sp-arithmetic 16.0.0", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-std 8.0.0", - "sp-weights 20.0.0", -] - -[[package]] -name = "sp-runtime-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.13.0", - "sp-runtime-interface-proc-macro 6.0.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-tracing 6.0.0", - "sp-wasm-interface 7.0.0", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2446ea08a1ae6dac4218b26e01c7aad6dbf47eb506f4f2b1efa821aa418a07d2" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.18.0", - "sp-runtime-interface-proc-macro 10.0.0", - "sp-std 7.0.0", - "sp-storage 12.0.0", - "sp-tracing 9.0.0", - "sp-wasm-interface 12.0.0", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5d0cd80200bf85b8b064238b2508b69b6146b13adf36066ec5d924825af737" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.18.0", - "sp-runtime-interface-proc-macro 10.0.0", - "sp-std 7.0.0", - "sp-storage 12.0.0", - "sp-tracing 9.0.0", - "sp-wasm-interface 13.0.0", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e676128182f90015e916f806cba635c8141e341e7abbc45d25525472e1bbce8" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.19.0", - "sp-runtime-interface-proc-macro 11.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", - "sp-wasm-interface 14.0.0", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d5bd5566fe5633ec48dfa35ab152fd29f8a577c21971e1c6db9f28afb9bbb9" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sp-session" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-api 4.0.0-dev", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-staking 4.0.0-dev", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-session" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e8aa7108c3cf19e257e1a69e4fd969e3aed8b9158580f730c6e2013497246b" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-api 16.0.0", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "sp-staking 16.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-staking" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-staking" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99dbd03cb38727b17b8276971f901a0e82b608c34a0f7ef24d9f8ad9b3070647" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-core 18.0.0", - "sp-runtime 20.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-state-machine" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "hash-db 0.16.0", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-panic-handler 5.0.0", - "sp-std 5.0.0", - "sp-trie 7.0.0", - "thiserror", - "tracing", -] - -[[package]] -name = "sp-state-machine" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779f737342d849205b97e2aacd729695614d86ccb05604e34f0ffe6391d7a4ce" -dependencies = [ - "hash-db 0.15.2", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "sp-core 18.0.0", - "sp-externalities 0.18.0", - "sp-panic-handler 7.0.0", - "sp-std 7.0.0", - "sp-trie 18.0.0", - "thiserror", - "tracing", -] - -[[package]] -name = "sp-state-machine" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef45d31f9e7ac648f8899a0cd038a3608f8499028bff55b6c799702592325b6" -dependencies = [ - "hash-db 0.16.0", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "sp-core 21.0.0", - "sp-externalities 0.19.0", - "sp-panic-handler 8.0.0", - "sp-std 8.0.0", - "sp-trie 22.0.0", - "thiserror", - "tracing", -] - -[[package]] -name = "sp-std" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" - -[[package]] -name = "sp-std" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" - -[[package]] -name = "sp-std" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" - -[[package]] -name = "sp-storage" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "ref-cast", - "serde", - "sp-debug-derive 5.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-storage" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "ref-cast", - "serde", - "sp-debug-derive 7.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-storage" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94294be83f11d4958cfea89ed5798f0b6605f5defc3a996948848458abbcc18e" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "ref-cast", - "serde", - "sp-debug-derive 8.0.0", - "sp-std 8.0.0", -] - -[[package]] -name = "sp-timestamp" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "futures-timer", - "log", - "parity-scale-codec", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "thiserror", -] - -[[package]] -name = "sp-tracing" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "sp-std 5.0.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sp-tracing" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" -dependencies = [ - "parity-scale-codec", - "sp-std 7.0.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sp-tracing" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" -dependencies = [ - "parity-scale-codec", - "sp-std 8.0.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sp-transaction-pool" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "sp-api 4.0.0-dev", - "sp-runtime 7.0.0", -] - -[[package]] -name = "sp-transaction-storage-proof" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "async-trait", - "log", - "parity-scale-codec", - "scale-info", - "sp-core 7.0.0", - "sp-inherents 4.0.0-dev", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-trie 7.0.0", -] - -[[package]] -name = "sp-trie" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "ahash 0.8.3", - "hash-db 0.16.0", - "hashbrown 0.13.2", - "lazy_static", - "memory-db 0.32.0", - "nohash-hasher", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", - "schnellru", - "sp-core 7.0.0", - "sp-std 5.0.0", - "thiserror", - "tracing", - "trie-db 0.27.1", - "trie-root 0.18.0", -] - -[[package]] -name = "sp-trie" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b5f3e730d26923d699766a9ca065ec39161f7af815c19acfb89c73f0402bf9" -dependencies = [ - "ahash 0.8.3", - "hash-db 0.15.2", - "hashbrown 0.12.3", - "lazy_static", - "memory-db 0.31.0", - "nohash-hasher", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", - "schnellru", - "sp-core 18.0.0", - "sp-std 7.0.0", - "thiserror", - "tracing", - "trie-db 0.25.1", - "trie-root 0.17.0", -] - -[[package]] -name = "sp-trie" -version = "22.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4eeb7ef23f79eba8609db79ef9cef242f994f1f87a3c0387b4b5f177fda74" -dependencies = [ - "ahash 0.8.3", - "hash-db 0.16.0", - "hashbrown 0.13.2", - "lazy_static", - "memory-db 0.32.0", - "nohash-hasher", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", - "schnellru", - "sp-core 21.0.0", - "sp-std 8.0.0", - "thiserror", - "tracing", - "trie-db 0.27.1", - "trie-root 0.18.0", -] - -[[package]] -name = "sp-version" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "parity-wasm", - "scale-info", - "serde", - "sp-core-hashing-proc-macro 5.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-version-proc-macro 4.0.0-dev", - "thiserror", -] - -[[package]] -name = "sp-version" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53ebad12a51b507859dc2978f1a6b101b403d1544403a17a1b7c17eeed20cb0c" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "parity-wasm", - "scale-info", - "serde", - "sp-core-hashing-proc-macro 7.0.0", - "sp-runtime 20.0.0", - "sp-std 7.0.0", - "sp-version-proc-macro 7.0.0", - "thiserror", -] - -[[package]] -name = "sp-version-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "sp-version-proc-macro" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a42f1acfd2bbaa92c4d97f7a0840e900a5dfa8e8d57b91c031c64f1df2112e90" -dependencies = [ - "parity-scale-codec", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sp-wasm-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 5.0.0", - "wasmi 0.13.2", - "wasmtime 6.0.2", -] - -[[package]] -name = "sp-wasm-interface" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510bdd9ade55508e5aa05b99ab79aaa4b74a1f7476351b6ce0f3aab3b1cb2524" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 7.0.0", - "wasmi 0.13.2", - "wasmtime 6.0.2", -] - -[[package]] -name = "sp-wasm-interface" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 7.0.0", - "wasmi 0.13.2", - "wasmtime 6.0.2", -] - -[[package]] -name = "sp-wasm-interface" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19c122609ca5d8246be6386888596320d03c7bc880959eaa2c36bcd5acd6846" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 8.0.0", - "wasmtime 8.0.1", -] - -[[package]] -name = "sp-weights" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-debug-derive 5.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-weights" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c4a96e53621ae435981fb6037d8b0be7cf32fae627780094a94ef89f194715" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic 13.0.0", - "sp-core 18.0.0", - "sp-debug-derive 7.0.0", - "sp-std 7.0.0", -] - -[[package]] -name = "sp-weights" -version = "20.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d084c735544f70625b821c3acdbc7a2fc1893ca98b85f1942631284692c75b" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic 16.0.0", - "sp-core 21.0.0", - "sp-debug-derive 8.0.0", - "sp-std 8.0.0", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der 0.7.7", -] - -[[package]] -name = "ss58-registry" -version = "1.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" -dependencies = [ - "Inflector", - "num-format", - "proc-macro2", - "quote", - "serde", - "serde_json", - "unicode-xid", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "static_init" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" -dependencies = [ - "bitflags 1.3.2", - "cfg_aliases", - "libc", - "parking_lot 0.11.2", - "parking_lot_core 0.8.6", - "static_init_macro", - "winapi", -] - -[[package]] -name = "static_init_macro" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" -dependencies = [ - "cfg_aliases", - "memchr", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "stun" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" -dependencies = [ - "base64 0.13.1", - "crc", - "lazy_static", - "md-5", - "rand 0.8.5", - "ring", - "subtle", - "thiserror", - "tokio", - "url", - "webrtc-util", -] - -[[package]] -name = "substrate-bip39" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" -dependencies = [ - "hmac 0.11.0", - "pbkdf2 0.8.0", - "schnorrkel 0.9.1", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "substrate-prometheus-endpoint" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "hyper", - "log", - "prometheus", - "thiserror", - "tokio", -] - -[[package]] -name = "substrate-prometheus-endpoint" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ef64b8fac4ecd85e4fcc7d6a8dfb8dccaec8b6754a8cd4c8112d6dc3afd240" -dependencies = [ - "hyper", - "log", - "prometheus", - "thiserror", - "tokio", -] - -[[package]] -name = "substrate-state-machine" -version = "0.1.0" -dependencies = [ - "ckb-merkle-mountain-range", - "frame-support", - "frame-system", - "hash-db 0.16.0", - "ismp", - "ismp-primitives", - "pallet-ismp", - "parity-scale-codec", - "primitive-types", - "scale-info", - "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-trie 7.0.0", -] - -[[package]] -name = "substring" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" -dependencies = [ - "autocfg", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-ng" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" - -[[package]] -name = "subxt" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba02ada83ba2640c46e200a1758cc83ce876a16326d2c52ca5db41b7d6645ce" -dependencies = [ - "base58", - "blake2", - "derivative", - "either", - "frame-metadata 16.0.0", - "futures", - "hex", - "impl-serde", - "jsonrpsee", - "parity-scale-codec", - "primitive-types", - "scale-bits", - "scale-decode", - "scale-encode", - "scale-info", - "scale-value", - "serde", - "serde_json", - "sp-core 21.0.0", - "sp-core-hashing 9.0.0", - "sp-runtime 24.0.0", - "subxt-lightclient", - "subxt-macro", - "subxt-metadata", - "thiserror", - "tracing", -] - -[[package]] -name = "subxt-codegen" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3213eb04567e710aa253b94de74337c7b663eea52114805b8723129763282779" -dependencies = [ - "frame-metadata 16.0.0", - "heck", - "hex", - "jsonrpsee", - "parity-scale-codec", - "proc-macro2", - "quote", - "scale-info", - "subxt-metadata", - "syn 2.0.25", - "thiserror", - "tokio", -] - -[[package]] -name = "subxt-lightclient" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a235bedd0e460c110e5341d919ec3a27f9be3dd4c1c944daad8a9b54d396d" -dependencies = [ - "futures", - "futures-util", - "serde", - "serde_json", - "smoldot-light", - "thiserror", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "subxt-macro" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfda460cc5f701785973382c589e9bb12c23bb8d825bfc3ac547b7c672aba1c0" -dependencies = [ - "darling 0.20.3", - "proc-macro-error", - "subxt-codegen", - "syn 2.0.25", -] - -[[package]] -name = "subxt-metadata" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283bd02163913fbd0a5153d0b179533e48b239b953fa4e43baa27c73f18861c" -dependencies = [ - "frame-metadata 16.0.0", - "parity-scale-codec", - "scale-info", - "sp-core-hashing 9.0.0", - "thiserror", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn-solidity" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "target-lexicon" -version = "0.12.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" - -[[package]] -name = "tempfile" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" -dependencies = [ - "autocfg", - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix 0.37.23", - "windows-sys 0.48.0", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - -[[package]] -name = "thiserror" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-core" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497" -dependencies = [ - "thiserror-core-impl", -] - -[[package]] -name = "thiserror-core-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "thrift" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b82ca8f46f95b3ce96081fe3dd89160fdea970c254bb72925255d1b62aae692e" -dependencies = [ - "byteorder", - "integer-encoding", - "log", - "ordered-float", - "threadpool", -] - -[[package]] -name = "tikv-jemalloc-ctl" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" -dependencies = [ - "libc", - "paste", - "tikv-jemalloc-sys", -] - -[[package]] -name = "tikv-jemalloc-sys" -version = "0.5.3+5.3.0-patched" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-bip39" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" -dependencies = [ - "anyhow", - "hmac 0.12.1", - "once_cell", - "pbkdf2 0.11.0", - "rand 0.8.5", - "rustc-hash", - "sha2 0.10.7", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite 0.2.10", - "signal-hook-registry", - "socket2 0.4.9", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.8", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite 0.2.10", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite 0.2.10", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" - -[[package]] -name = "toml_edit" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite 0.2.10", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite 0.2.10", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-gum" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "polkadot-node-jaeger", - "polkadot-primitives", - "tracing", - "tracing-gum-proc-macro", -] - -[[package]] -name = "tracing-gum-proc-macro" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "expander 2.0.0", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers", - "parking_lot 0.11.2", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", + "parity-scale-codec", + "sp-std 7.0.0", "tracing", "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "trie-db" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" -dependencies = [ - "hash-db 0.15.2", - "hashbrown 0.13.2", - "log", - "rustc-hex", - "smallvec", + "tracing-subscriber", ] [[package]] -name = "trie-db" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +name = "sp-trie" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "hash-db 0.16.0", + "ahash 0.8.3", + "hash-db", "hashbrown 0.13.2", - "log", - "rustc-hex", - "smallvec", -] - -[[package]] -name = "trie-root" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" -dependencies = [ - "hash-db 0.15.2", -] - -[[package]] -name = "trie-root" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" -dependencies = [ - "hash-db 0.16.0", -] - -[[package]] -name = "triehash" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" -dependencies = [ - "hash-db 0.15.2", - "rlp", -] - -[[package]] -name = "trust-dns-proto" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.2.3", - "ipnet", - "lazy_static", - "rand 0.8.5", - "smallvec", - "socket2 0.4.9", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", "lazy_static", - "lru-cache", - "parking_lot 0.12.1", - "resolv-conf", - "smallvec", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "scale-info", + "schnellru", + "sp-core 7.0.0", + "sp-std 5.0.0", "thiserror", - "tokio", "tracing", - "trust-dns-proto", + "trie-db", + "trie-root", ] [[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tt-call" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" - -[[package]] -name = "turn" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "async-trait", - "base64 0.13.1", - "futures", - "log", - "md-5", - "rand 0.8.5", - "ring", - "stun", + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std 5.0.0", + "sp-version-proc-macro", "thiserror", - "tokio", - "webrtc-util", ] [[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "cfg-if", - "digest 0.10.7", - "rand 0.8.5", - "static_assertions", + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 2.0.25", ] [[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 5.0.0", + "wasmi", + "wasmtime", ] [[package]] -name = "unarray" -version = "0.1.4" +name = "sp-wasm-interface" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 7.0.0", + "wasmi", + "wasmtime", +] [[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core 7.0.0", + "sp-debug-derive 5.0.0", + "sp-std 5.0.0", +] [[package]] -name = "unicode-ident" -version = "1.0.10" +name = "spki" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "ss58-registry" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" dependencies = [ - "tinyvec", + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", ] [[package]] -name = "unicode-xid" -version = "0.2.4" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "universal-hash" -version = "0.4.1" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "universal-hash" -version = "0.5.1" +name = "substrate-bip39" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" dependencies = [ - "crypto-common", - "subtle", + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", ] [[package]] -name = "unsigned-varint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +name = "substrate-prometheus-endpoint" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" dependencies = [ - "asynchronous-codec", - "bytes", - "futures-io", - "futures-util", + "hyper", + "log", + "prometheus", + "thiserror", + "tokio", ] [[package]] -name = "untrusted" -version = "0.7.1" +name = "subtle" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] -name = "url" -version = "2.4.0" +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "form_urlencoded", - "idna 0.4.0", - "percent-encoding", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.4.0" +name = "syn" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ - "getrandom 0.2.10", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "void" -version = "1.0.2" +name = "target-lexicon" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" [[package]] -name = "wait-timeout" -version = "0.2.0" +name = "termcolor" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ - "libc", + "winapi-util", ] [[package]] -name = "waitgroup" -version = "0.1.2" +name = "thiserror" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ - "atomic-waker", + "thiserror-impl", ] [[package]] -name = "waker-fn" -version = "1.1.0" +name = "thiserror-impl" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] [[package]] -name = "want" -version = "0.3.1" +name = "thread_local" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "try-lock", + "cfg-if", + "once_cell", ] [[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +name = "tiny-bip39" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.7", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] [[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "wasm-bindgen" -version = "0.2.87" +name = "tokio" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" +name = "tokio-macros" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "bumpalo", - "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.25", - "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" +name = "tokio-stream" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" +name = "tokio-util" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", - "wasm-bindgen-backend", - "wasm-bindgen-shared", + "serde", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" +name = "toml_datetime" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] -name = "wasm-instrument" -version = "0.3.0" +name = "toml_edit" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" dependencies = [ - "parity-wasm", + "indexmap 2.0.0", + "toml_datetime", + "winnow", ] [[package]] -name = "wasm-timer" -version = "0.2.5" +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "wasmi" -version = "0.13.2" +name = "tower-layer" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" -dependencies = [ - "parity-wasm", - "wasmi-validation", - "wasmi_core 0.2.1", -] +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] -name = "wasmi" -version = "0.30.0" +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51fb5c61993e71158abf5bb863df2674ca3ec39ed6471c64f07aeaf751d67b4" -dependencies = [ - "intx", - "smallvec", - "spin 0.9.8", - "wasmi_arena", - "wasmi_core 0.12.0", - "wasmparser-nostd", -] +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -name = "wasmi-validation" -version = "0.5.0" +name = "tracing" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "parity-wasm", + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "wasmi_arena" -version = "0.4.0" +name = "tracing-attributes" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] [[package]] -name = "wasmi_core" -version = "0.2.1" +name = "tracing-core" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ - "downcast-rs", - "libm 0.2.7", - "memory_units", - "num-rational", - "num-traits", - "region", + "once_cell", + "valuable", ] [[package]] -name = "wasmi_core" -version = "0.12.0" +name = "tracing-log" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624e6333e861ef49095d2d678b76ebf30b06bf37effca845be7e5b87c90071b7" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ - "downcast-rs", - "libm 0.2.7", - "num-traits", - "paste", + "lazy_static", + "log", + "tracing-core", ] [[package]] -name = "wasmparser" -version = "0.100.0" +name = "tracing-serde" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ - "indexmap 1.9.3", - "url", + "serde", + "tracing-core", ] [[package]] -name = "wasmparser" -version = "0.102.0" +name = "tracing-subscriber" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ - "indexmap 1.9.3", - "url", + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", ] [[package]] -name = "wasmparser-nostd" -version = "0.100.1" +name = "trie-db" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ - "indexmap-nostd", + "hash-db", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", ] [[package]] -name = "wasmtime" -version = "6.0.2" +name = "trie-root" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "object 0.29.0", - "once_cell", - "paste", - "psm", - "rayon", - "serde", - "target-lexicon", - "wasmparser 0.100.0", - "wasmtime-cache", - "wasmtime-cranelift", - "wasmtime-environ 6.0.2", - "wasmtime-jit 6.0.2", - "wasmtime-runtime 6.0.2", - "windows-sys 0.42.0", + "hash-db", ] [[package]] -name = "wasmtime" -version = "8.0.1" +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "twox-hash" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "anyhow", - "bincode", "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "object 0.30.4", - "once_cell", - "paste", - "psm", - "serde", - "target-lexicon", - "wasmparser 0.102.0", - "wasmtime-environ 8.0.1", - "wasmtime-jit 8.0.1", - "wasmtime-runtime 8.0.1", - "windows-sys 0.45.0", + "digest 0.10.7", + "rand 0.8.5", + "static_assertions", ] [[package]] -name = "wasmtime-asm-macros" -version = "6.0.2" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ - "cfg-if", + "byteorder", + "crunchy", + "hex", + "static_assertions", ] [[package]] -name = "wasmtime-asm-macros" -version = "8.0.1" +name = "unicode-bidi" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" -dependencies = [ - "cfg-if", -] +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] -name = "wasmtime-cache" -version = "6.0.2" +name = "unicode-ident" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" -dependencies = [ - "anyhow", - "base64 0.13.1", - "bincode", - "directories-next", - "file-per-thread-logger", - "log", - "rustix 0.36.15", - "serde", - "sha2 0.10.7", - "toml", - "windows-sys 0.42.0", - "zstd 0.11.2+zstd.1.5.2", -] +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] -name = "wasmtime-cranelift" -version = "6.0.2" +name = "unicode-normalization" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-entity 0.93.2", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "gimli 0.26.2", - "log", - "object 0.29.0", - "target-lexicon", - "thiserror", - "wasmparser 0.100.0", - "wasmtime-environ 6.0.2", + "tinyvec", ] [[package]] -name = "wasmtime-environ" -version = "6.0.2" +name = "unicode-xid" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" -dependencies = [ - "anyhow", - "cranelift-entity 0.93.2", - "gimli 0.26.2", - "indexmap 1.9.3", - "log", - "object 0.29.0", - "serde", - "target-lexicon", - "thiserror", - "wasmparser 0.100.0", - "wasmtime-types 6.0.2", -] +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "wasmtime-environ" -version = "8.0.1" +name = "url" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ - "anyhow", - "cranelift-entity 0.95.1", - "gimli 0.27.3", - "indexmap 1.9.3", - "log", - "object 0.30.4", - "serde", - "target-lexicon", - "thiserror", - "wasmparser 0.102.0", - "wasmtime-types 8.0.1", + "form_urlencoded", + "idna", + "percent-encoding", ] [[package]] -name = "wasmtime-jit" -version = "6.0.2" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" -dependencies = [ - "addr2line 0.17.0", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli 0.26.2", - "log", - "object 0.29.0", - "rustc-demangle", - "serde", - "target-lexicon", - "wasmtime-environ 6.0.2", - "wasmtime-jit-debug 6.0.2", - "wasmtime-jit-icache-coherence 6.0.2", - "wasmtime-runtime 6.0.2", - "windows-sys 0.42.0", -] +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] -name = "wasmtime-jit" -version = "8.0.1" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" -dependencies = [ - "addr2line 0.19.0", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli 0.27.3", - "log", - "object 0.30.4", - "rustc-demangle", - "serde", - "target-lexicon", - "wasmtime-environ 8.0.1", - "wasmtime-jit-icache-coherence 8.0.1", - "wasmtime-runtime 8.0.1", - "windows-sys 0.45.0", -] +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "wasmtime-jit-debug" -version = "6.0.2" +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "object 0.29.0", - "once_cell", - "rustix 0.36.15", + "try-lock", ] [[package]] -name = "wasmtime-jit-debug" -version = "8.0.1" +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" -dependencies = [ - "once_cell", -] +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] -name = "wasmtime-jit-icache-coherence" -version = "6.0.2" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.42.0", -] +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasmtime-jit-icache-coherence" -version = "8.0.1" +name = "wasm-bindgen" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", - "libc", - "windows-sys 0.45.0", + "wasm-bindgen-macro", ] [[package]] -name = "wasmtime-runtime" -version = "6.0.2" +name = "wasm-bindgen-backend" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap 1.9.3", - "libc", + "bumpalo", "log", - "mach", - "memfd", - "memoffset 0.6.5", - "paste", - "rand 0.8.5", - "rustix 0.36.15", - "wasmtime-asm-macros 6.0.2", - "wasmtime-environ 6.0.2", - "wasmtime-jit-debug 6.0.2", - "windows-sys 0.42.0", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.25", + "wasm-bindgen-shared", ] [[package]] -name = "wasmtime-runtime" -version = "8.0.1" +name = "wasm-bindgen-macro" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "mach", - "memfd", - "memoffset 0.8.0", - "paste", - "rand 0.8.5", - "rustix 0.36.15", - "wasmtime-asm-macros 8.0.1", - "wasmtime-environ 8.0.1", - "wasmtime-jit-debug 8.0.1", - "windows-sys 0.45.0", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "wasmtime-types" -version = "6.0.2" +name = "wasm-bindgen-macro-support" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "cranelift-entity 0.93.2", - "serde", - "thiserror", - "wasmparser 0.100.0", + "proc-macro2", + "quote", + "syn 2.0.25", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] -name = "wasmtime-types" -version = "8.0.1" +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-instrument" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" dependencies = [ - "cranelift-entity 0.95.1", - "serde", - "thiserror", - "wasmparser 0.102.0", + "parity-wasm", ] [[package]] -name = "web-sys" -version = "0.3.64" +name = "wasmi" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" dependencies = [ - "js-sys", - "wasm-bindgen", + "parity-wasm", + "wasmi-validation", + "wasmi_core", ] [[package]] -name = "webpki" -version = "0.21.4" +name = "wasmi-validation" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" dependencies = [ - "ring", - "untrusted", + "parity-wasm", ] [[package]] -name = "webpki" -version = "0.22.0" +name = "wasmi_core" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ - "ring", - "untrusted", + "downcast-rs", + "libm", + "memory_units", + "num-rational", + "num-traits", + "region", ] [[package]] -name = "webpki-roots" -version = "0.22.6" +name = "wasmparser" +version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" dependencies = [ - "webpki 0.22.0", + "indexmap 1.9.3", + "url", ] [[package]] -name = "webrtc" -version = "0.6.0" +name = "wasmtime" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "hex", - "interceptor", - "lazy_static", + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", "log", - "rand 0.8.5", - "rcgen 0.9.3", - "regex", - "ring", - "rtcp", - "rtp", - "rustls 0.19.1", - "sdp", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", "serde", - "serde_json", - "sha2 0.10.7", - "stun", - "thiserror", - "time 0.3.23", - "tokio", - "turn", - "url", - "waitgroup", - "webrtc-data", - "webrtc-dtls", - "webrtc-ice", - "webrtc-mdns", - "webrtc-media", - "webrtc-sctp", - "webrtc-srtp", - "webrtc-util", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", ] [[package]] -name = "webrtc-data" -version = "0.6.0" +name = "wasmtime-asm-macros" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" dependencies = [ - "bytes", - "derive_builder", - "log", - "thiserror", - "tokio", - "webrtc-sctp", - "webrtc-util", + "cfg-if", ] [[package]] -name = "webrtc-dtls" -version = "0.7.1" +name = "wasmtime-cache" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" +checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" dependencies = [ - "aes 0.6.0", - "aes-gcm 0.10.2", - "async-trait", + "anyhow", + "base64", "bincode", - "block-modes", - "byteorder", - "ccm", - "curve25519-dalek 3.2.0", - "der-parser 8.2.0", - "elliptic-curve 0.12.3", - "hkdf", - "hmac 0.12.1", + "directories-next", + "file-per-thread-logger", "log", - "oid-registry 0.6.1", - "p256", - "p384", - "rand 0.8.5", - "rand_core 0.6.4", - "rcgen 0.9.3", - "ring", - "rustls 0.19.1", - "sec1 0.3.0", + "rustix 0.36.15", "serde", - "sha1", "sha2 0.10.7", - "signature 1.6.4", - "subtle", - "thiserror", - "tokio", - "webpki 0.21.4", - "webrtc-util", - "x25519-dalek 2.0.0-pre.1", - "x509-parser 0.13.2", + "toml", + "windows-sys 0.42.0", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] -name = "webrtc-ice" -version = "0.9.1" +name = "wasmtime-cranelift" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" +checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" dependencies = [ - "arc-swap", - "async-trait", - "crc", + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", "log", - "rand 0.8.5", - "serde", - "serde_json", - "stun", + "object 0.29.0", + "target-lexicon", "thiserror", - "tokio", - "turn", - "url", - "uuid", - "waitgroup", - "webrtc-mdns", - "webrtc-util", + "wasmparser", + "wasmtime-environ", ] [[package]] -name = "webrtc-mdns" -version = "0.5.2" +name = "wasmtime-environ" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap 1.9.3", "log", - "socket2 0.4.9", + "object 0.29.0", + "serde", + "target-lexicon", "thiserror", - "tokio", - "webrtc-util", + "wasmparser", + "wasmtime-types", ] [[package]] -name = "webrtc-media" -version = "0.5.1" +name = "wasmtime-jit" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" dependencies = [ - "byteorder", - "bytes", - "rand 0.8.5", - "rtp", - "thiserror", + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", ] [[package]] -name = "webrtc-sctp" -version = "0.7.0" +name = "wasmtime-jit-debug" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "crc", - "log", - "rand 0.8.5", - "thiserror", - "tokio", - "webrtc-util", + "object 0.29.0", + "once_cell", + "rustix 0.36.15", ] [[package]] -name = "webrtc-srtp" -version = "0.9.1" +name = "wasmtime-jit-icache-coherence" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" dependencies = [ - "aead 0.4.3", - "aes 0.7.5", - "aes-gcm 0.9.4", - "async-trait", - "byteorder", - "bytes", - "ctr 0.8.0", - "hmac 0.11.0", - "log", - "rtcp", - "rtp", - "sha-1", - "subtle", - "thiserror", - "tokio", - "webrtc-util", + "cfg-if", + "libc", + "windows-sys 0.42.0", ] [[package]] -name = "webrtc-util" -version = "0.7.0" +name = "wasmtime-runtime" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" dependencies = [ - "async-trait", - "bitflags 1.3.2", - "bytes", + "anyhow", "cc", - "ipnet", - "lazy_static", + "cfg-if", + "indexmap 1.9.3", "libc", "log", - "nix", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", "rand 0.8.5", - "thiserror", - "tokio", - "winapi", + "rustix 0.36.15", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", ] [[package]] -name = "which" -version = "4.4.0" +name = "wasmtime-types" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ - "either", - "libc", - "once_cell", + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", ] [[package]] @@ -12574,12 +4679,6 @@ dependencies = [ "safe_arch", ] -[[package]] -name = "widestring" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" - [[package]] name = "winapi" version = "0.3.9" @@ -12611,19 +4710,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" -dependencies = [ - "windows_aarch64_msvc 0.34.0", - "windows_i686_gnu 0.34.0", - "windows_i686_msvc 0.34.0", - "windows_x86_64_gnu 0.34.0", - "windows_x86_64_msvc 0.34.0", -] - [[package]] name = "windows" version = "0.48.0" @@ -12708,12 +4794,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -12726,12 +4806,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -12744,12 +4818,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -12762,12 +4830,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -12792,12 +4854,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -12819,16 +4875,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wyz" version = "0.5.1" @@ -12838,121 +4884,6 @@ dependencies = [ "tap", ] -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek 3.2.0", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "x25519-dalek" -version = "2.0.0-pre.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" -dependencies = [ - "curve25519-dalek 3.2.0", - "rand_core 0.6.4", - "zeroize", -] - -[[package]] -name = "x509-parser" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" -dependencies = [ - "asn1-rs 0.3.1", - "base64 0.13.1", - "data-encoding", - "der-parser 7.0.0", - "lazy_static", - "nom", - "oid-registry 0.4.0", - "ring", - "rusticata-macros", - "thiserror", - "time 0.3.23", -] - -[[package]] -name = "x509-parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" -dependencies = [ - "asn1-rs 0.5.2", - "base64 0.13.1", - "data-encoding", - "der-parser 8.2.0", - "lazy_static", - "nom", - "oid-registry 0.6.1", - "rusticata-macros", - "thiserror", - "time 0.3.23", -] - -[[package]] -name = "xcm" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "bounded-collections", - "derivative", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-weights 4.0.0", - "xcm-procedural", -] - -[[package]] -name = "xcm-procedural" -version = "0.9.42" -source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "yamux" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" -dependencies = [ - "futures", - "log", - "nohash-hasher", - "parking_lot 0.12.1", - "rand 0.8.5", - "static_assertions", -] - -[[package]] -name = "yap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a7eb6d82a11e4d0b8e6bda8347169aff4ccd8235d039bba7c47482d977dcf7" - -[[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time 0.3.23", -] - [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 88cbb0839..0f6ca35bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,17 +4,8 @@ members = [ "pallet-ismp/rpc", "pallet-ismp/runtime-api", "pallet-ismp/primitives", - "pallet-ismp/primitives/state-machine", - "pallet-ismp/evm", "pallet-ismp", - "parachain/inherent", - "parachain/runtime-api", - "parachain", "ismp-demo", - "grandpa", - "grandpa/primitives", - "grandpa/verifier", - "grandpa/prover" ] [workspace.dependencies] diff --git a/README.md b/README.md index 19e89d407..ba54dc435 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,12 @@ Implementation of the Interoperable State Machine Protocol for substrate runtime ## Overview -This repo holds all the required components substrate runtimes need to interoperate together using [ISMP](https://github.com/polytope-labs/ismp) +This repo holds the foundational components substrate runtimes need to interoperate together using [ISMP](https://github.com/polytope-labs/ismp) - [pallet-ismp](./) - [ismp-runtime-api](./pallet-ismp/runtime-api) - [ismp-rpc](./pallet-ismp/rpc) -### Parachain Support - -- [ismp-parachain](./parachain) -- [ismp-parachain-inherent](./parachain/inherent) -- [ismp-parachain-runtime-api](./parachain/runtime-api) - -### Solochain Support - -- [ismp-grandpa](./grandpa) -- [ismp-grandpa-prover](./grandpa/prover) - ## Documentation Installation and integration guides can be found in the [book](https://ismp.polytope.technology). diff --git a/grandpa/Cargo.toml b/grandpa/Cargo.toml deleted file mode 100644 index 8e7ddbb06..000000000 --- a/grandpa/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "ismp-grandpa" -version = "0.1.0" -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ - "derive" -] } -primitive-types = { version = "0.12.1", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } -finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } - -# polytope labs -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } -primitives = { package = "ismp-grandpa-primitives", path = "./primitives", default-features = false } -verifier = { package = "ismp-grandpa-verifier", path = "./verifier", default-features = false} -pallet-ismp = { path = "../pallet-ismp", default-features = false } - - -# substrate -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } - -# cumulus -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } - -ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } -substrate-state-machine = { path = "../pallet-ismp/primitives/state-machine", default-features = false } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "cumulus-primitives-core/std", - "ismp/std", - "sp-trie/std", - "sp-consensus-aura/std", - "sp-runtime/std", - "sp-io/std", - "primitive-types/std", - "pallet-ismp/std", - "sp-core/std", - "primitives/std", - "verifier/std", - "merkle-mountain-range/std", - "ismp-primitives/std", - "substrate-state-machine/std", - "finality-grandpa/std", -] diff --git a/grandpa/primitives/Cargo.toml b/grandpa/primitives/Cargo.toml deleted file mode 100644 index 985dd6b79..000000000 --- a/grandpa/primitives/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "ismp-grandpa-primitives" -version = "0.1.0" -edition = "2021" - -[dependencies] -# crates.io -anyhow = { version = "1.0.64", default-features = false } -finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } - -# substrate -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -# polytope -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } - -[features] -default = ["std"] -std = [ - "anyhow/std", - "sp-storage/std", - "finality-grandpa/std", - "codec/std", - "sp-core/std", - "sp-runtime/std", - "sp-io/std", - "frame-support/std", - "sp-consensus-grandpa/std", - "sp-std/std", - "sp-trie/std", - "ismp/std", -] diff --git a/grandpa/primitives/src/justification.rs b/grandpa/primitives/src/justification.rs deleted file mode 100644 index a4787d395..000000000 --- a/grandpa/primitives/src/justification.rs +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::Commit; -use alloc::collections::{BTreeMap, BTreeSet}; -use anyhow::anyhow; -use codec::{Decode, Encode}; -use finality_grandpa::voter_set::VoterSet; -use frame_support::log; -use sp_consensus_grandpa::{ - AuthorityId, AuthorityList, AuthoritySignature, ConsensusLog, Equivocation, RoundNumber, - ScheduledChange, SetId, GRANDPA_ENGINE_ID, -}; -use sp_core::ed25519; -use sp_runtime::{generic::OpaqueDigestItemId, traits::Header as HeaderT}; -use sp_std::prelude::*; - -/// A GRANDPA justification for block finality, it includes a commit message and -/// an ancestry proof including all headers routing all precommit target blocks -/// to the commit target block. Due to the current voting strategy the precommit -/// targets should be the same as the commit target, since honest voters don't -/// vote past authority set change blocks. -/// -/// This is meant to be stored in the db and passed around the network to other -/// nodes, and are used by syncing nodes to prove authority set handoffs. -#[cfg_attr(any(feature = "std", test), derive(Debug))] -#[derive(Clone, Encode, Decode, PartialEq, Eq)] -pub struct GrandpaJustification { - /// Current voting round number, monotonically increasing - pub round: u64, - /// Contains block hash & number that's being finalized and the signatures. - pub commit: Commit, - /// Contains the path from a [`PreCommit`]'s target hash to the GHOST finalized block. - pub votes_ancestries: Vec, -} - -impl GrandpaJustification -where - H: HeaderT, - H::Number: finality_grandpa::BlockNumberOps, -{ - /// Validate the commit and the votes' ancestry proofs. - pub fn verify(&self, set_id: u64, authorities: &AuthorityList) -> Result<(), anyhow::Error> { - // It's safe to assume that the authority list will not contain duplicates, - // since this list is extracted from a verified relaychain header. - let voters = - VoterSet::new(authorities.iter().cloned()).ok_or(anyhow!("Invalid AuthoritiesSet"))?; - - self.verify_with_voter_set(set_id, &voters) - } - - /// Validate the commit and the votes' ancestry proofs. - pub fn verify_with_voter_set( - &self, - set_id: u64, - voters: &VoterSet, - ) -> Result<(), anyhow::Error> { - use finality_grandpa::Chain; - - let ancestry_chain = AncestryChain::::new(&self.votes_ancestries); - - match finality_grandpa::validate_commit(&self.commit, voters, &ancestry_chain) { - Ok(ref result) if result.is_valid() => { - if result.num_duplicated_precommits() > 0 || - result.num_invalid_voters() > 0 || - result.num_equivocations() > 0 - { - Err(anyhow!("Invalid commit, found one of `duplicate precommits`, `invalid voters`, or `equivocations` {result:?}"))? - } - } - err => { - let result = err.map_err(|_| { - anyhow!("[verify_with_voter_set] Invalid ancestry while validating commit!") - })?; - Err(anyhow!("invalid commit in grandpa justification: {result:?}"))? - } - } - - // we pick the precommit for the lowest block as the base that - // should serve as the root block for populating ancestry (i.e. - // collect all headers from all precommit blocks to the base) - let base_hash = self - .commit - .precommits - .iter() - .map(|signed| &signed.precommit) - .min_by_key(|precommit| precommit.target_number) - .map(|precommit| precommit.target_hash.clone()) - .expect( - "can only fail if precommits is empty; \ - commit has been validated above; \ - valid commits must include precommits; \ - qed.", - ); - - let mut visited_hashes = BTreeSet::new(); - for signed in self.commit.precommits.iter() { - let message = finality_grandpa::Message::Precommit(signed.precommit.clone()); - - check_message_signature::<_, _>( - &message, - &signed.id, - &signed.signature, - self.round, - set_id, - )?; - - if base_hash == signed.precommit.target_hash { - continue - } - - let route = ancestry_chain - .ancestry(base_hash, signed.precommit.target_hash) - .map_err(|_| anyhow!("[verify_with_voter_set] Invalid ancestry!"))?; - // ancestry starts from parent hash but the precommit target hash has been - // visited - visited_hashes.insert(signed.precommit.target_hash); - for hash in route { - visited_hashes.insert(hash); - } - } - - let ancestry_hashes: BTreeSet<_> = - self.votes_ancestries.iter().map(|h: &H| h.hash()).collect(); - - if visited_hashes != ancestry_hashes { - Err(anyhow!( - "invalid precommit ancestries in grandpa justification with unused headers", - ))? - } - - Ok(()) - } - - /// The target block number and hash that this justifications proves finality for. - pub fn target(&self) -> (H::Number, H::Hash) { - (self.commit.target_number, self.commit.target_hash) - } -} - -/// A utility trait implementing `finality_grandpa::Chain` using a given set of headers. -/// This is useful when validating commits, using the given set of headers to -/// verify a valid ancestry route to the target commit block. -pub struct AncestryChain { - ancestry: BTreeMap, -} - -impl AncestryChain { - /// Initialize the ancestry chain given a set of relay chain headers. - pub fn new(ancestry: &[H]) -> AncestryChain { - let ancestry: BTreeMap<_, _> = ancestry.iter().cloned().map(|h: H| (h.hash(), h)).collect(); - - AncestryChain { ancestry } - } - - /// Fetch a header from the ancestry chain, given it's hash. Returns [`None`] if it doesn't - /// exist. - pub fn header(&self, hash: &H::Hash) -> Option<&H> { - self.ancestry.get(hash) - } -} - -impl finality_grandpa::Chain for AncestryChain -where - H::Number: finality_grandpa::BlockNumberOps, -{ - fn ancestry( - &self, - base: H::Hash, - block: H::Hash, - ) -> Result, finality_grandpa::Error> { - let mut route = vec![block]; - let mut current_hash = block; - while current_hash != base { - match self.ancestry.get(¤t_hash) { - Some(current_header) => { - current_hash = *current_header.parent_hash(); - route.push(current_hash); - } - _ => return Err(finality_grandpa::Error::NotDescendent), - }; - } - Ok(route) - } -} - -/// Checks the given header for a consensus digest signalling a **standard** scheduled change and -/// extracts it. -pub fn find_scheduled_change(header: &H) -> Option> { - let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); - - let filter_log = |log: ConsensusLog| match log { - ConsensusLog::ScheduledChange(change) => Some(change), - _ => None, - }; - - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) -} - -/// Checks the given header for a consensus digest signalling a **forced** scheduled change and -/// extracts it. -pub fn find_forced_change( - header: &H, -) -> Option<(H::Number, ScheduledChange)> { - let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); - - let filter_log = |log: ConsensusLog| match log { - ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), - _ => None, - }; - - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) -} - -/// Check a message signature by encoding the message and verifying the provided signature using the -/// expected authority id. -pub fn check_message_signature( - message: &finality_grandpa::Message, - id: &AuthorityId, - signature: &AuthoritySignature, - round: RoundNumber, - set_id: SetId, -) -> Result<(), anyhow::Error> -where - H: Encode, - N: Encode, -{ - log::trace!(target: "pallet_grandpa", "Justification Message {:?}", (round, set_id)); - let buf = (message, round, set_id).encode(); - - let signature_bytes: &[u8] = signature.as_ref(); - let sp_finality_signature: ed25519::Signature = - signature_bytes.try_into().map_err(|_| anyhow!("Could not fetch signature"))?; - - let id_bytes: &[u8] = id.as_ref(); - let pub_key: ed25519::Public = - id_bytes.try_into().map_err(|_| anyhow!("Could not fetch public key"))?; - - if sp_io::crypto::ed25519_verify(&sp_finality_signature, &buf, &pub_key) { - Err(anyhow!("invalid signature for precommit in grandpa justification"))? - } - - Ok(()) -} - -/// Verifies the equivocation proof by making sure that both votes target -/// different blocks and that its signatures are valid. -pub fn check_equivocation_proof( - set_id: u64, - equivocation: Equivocation, -) -> Result<(), anyhow::Error> -where - H: Clone + Encode + PartialEq, - N: Clone + Encode + PartialEq, -{ - // NOTE: the bare `Prevote` and `Precommit` types don't share any trait, - // this is implemented as a macro to avoid duplication. - macro_rules! check { - ( $equivocation:expr, $message:expr ) => { - // if both votes have the same target the equivocation is invalid. - if $equivocation.first.0.target_hash == $equivocation.second.0.target_hash && - $equivocation.first.0.target_number == $equivocation.second.0.target_number - { - return Err(anyhow!("both votes have the same target!")) - } - - // check signatures on both votes are valid - check_message_signature::<_, _>( - &$message($equivocation.first.0), - &$equivocation.identity, - &$equivocation.first.1, - $equivocation.round_number, - set_id, - )?; - - check_message_signature::<_, _>( - &$message($equivocation.second.0), - &$equivocation.identity, - &$equivocation.second.1, - $equivocation.round_number, - set_id, - )?; - - return Ok(()) - }; - } - - match equivocation { - Equivocation::Prevote(equivocation) => { - check!(equivocation, finality_grandpa::Message::Prevote); - } - Equivocation::Precommit(equivocation) => { - check!(equivocation, finality_grandpa::Message::Precommit); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use finality_grandpa::Chain; - use sp_runtime::{generic::Header, traits::BlakeTwo256}; - - #[test] - fn test_ancestry_route() { - let mut headers: Vec> = vec![]; - for (i, h) in (40u32..=50).enumerate() { - let mut header = Header::new( - h, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); - if i != 0 { - header.parent_hash = headers[i - 1].hash(); - } - headers.push(header); - } - - let slice = &headers[3..=6]; - let ancestry = AncestryChain::new(&headers); - - let mut route = ancestry.ancestry(slice[0].hash(), slice[3].hash()).unwrap(); - route.sort(); - let mut expected = slice.iter().map(|h| h.hash()).collect::>(); - expected.sort(); - - assert_eq!(route, expected); - } -} diff --git a/grandpa/primitives/src/lib.rs b/grandpa/primitives/src/lib.rs deleted file mode 100644 index 7c2017019..000000000 --- a/grandpa/primitives/src/lib.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Primitive types and traits used by the GRANDPA prover & verifier. - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::all)] -#![deny(missing_docs)] - -extern crate alloc; - -use alloc::collections::BTreeMap; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use ismp::host::StateMachine; -use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthoritySignature}; -use sp_core::{sp_std, H256}; -use sp_runtime::traits::Header; -use sp_std::prelude::*; -use sp_storage::StorageKey; - -/// GRANDPA justification utilities -pub mod justification; - -/// Represents a Hash in this library -pub type Hash = H256; -/// A commit message for this chain's block type. -pub type Commit = finality_grandpa::Commit< - ::Hash, - ::Number, - AuthoritySignature, - AuthorityId, ->; - -/// Finality for block B is proved by providing: -/// 1) the justification for the descendant block F; -/// 2) headers sub-chain (B; F] if B != F; -#[derive(Debug, PartialEq, Encode, Decode, Clone)] -pub struct FinalityProof { - /// The hash of block F for which justification is provided. - pub block: Hash, - /// Justification of the block F. - pub justification: Vec, - /// The set of headers in the range (B; F] that we believe are unknown to the caller. Ordered. - pub unknown_headers: Vec, -} - -/// Previous light client state. -#[derive(Debug, PartialEq, Encode, Decode, Clone)] -pub struct ConsensusState { - /// Current authority set - pub current_authorities: AuthorityList, - /// Id of the current authority set. - pub current_set_id: u64, - /// latest finalized height on relay chain or standalone chain - pub latest_height: u32, - /// State machine id StateMachine::Polkadot(0) or StateMachine::Kusama(0) or - ///StateMachine::Grandpa(ConsensusStateId) - pub state_machine: StateMachine, - /// latest finalized height on the parachains, this map will be empty for Standalone chains - /// Map of para_ids - pub para_ids: BTreeMap, - /// latest finalized hash on relay chain or standalone chain. - pub latest_hash: Hash, - /// slot duration for the chain - pub slot_duration: u64, -} - -/// Holds relavant parachain proofs for both header and timestamp extrinsic. -#[derive(Clone, Debug, Encode, Decode)] -pub struct ParachainHeaderProofs { - /// State proofs that prove a parachain headers exists at a given relay chain height - pub state_proof: Vec>, - /// The parachain ids - pub para_ids: Vec, -} - -/// Parachain headers with a Grandpa finality proof. -#[derive(Clone, Encode, Decode)] -pub struct ParachainHeadersWithFinalityProof { - /// The grandpa finality proof: contains relay chain headers from the - /// last known finalized grandpa block. - pub finality_proof: FinalityProof, - /// Contains a map of relay chain header hashes to parachain headers - /// finalzed at the relay chain height. We check for this parachain header finalization - /// via state proofs. Also contains extrinsic proof for timestamp. - pub parachain_headers: BTreeMap, -} - -/// This returns the storage key for a parachain header on the relay chain. -pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { - let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec(); - let encoded_para_id = para_id.encode(); - storage_key.extend_from_slice(sp_io::hashing::twox_64(&encoded_para_id).as_slice()); - storage_key.extend_from_slice(&encoded_para_id); - StorageKey(storage_key) -} diff --git a/grandpa/prover/Cargo.toml b/grandpa/prover/Cargo.toml deleted file mode 100644 index 623e9380a..000000000 --- a/grandpa/prover/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "ismp-grandpa-prover" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs "] - - -[dependencies] -hex = "0.4.3" -anyhow = "1.0.64" -serde = "1.0.144" -subxt = "0.30.1" -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -derive_more = "0.99.17" -downcast-rs = "1.2.0" -jsonrpsee = { version = "0.16.2", features = ["async-client", "jsonrpsee-ws-client"] } -jsonrpsee-ws-client = "0.16.2" -finality-grandpa = "0.16.0" - -sc-consensus-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } - - -primitives = { package = "ismp-grandpa-primitives", path = "../primitives" } -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } diff --git a/grandpa/prover/src/lib.rs b/grandpa/prover/src/lib.rs deleted file mode 100644 index e68501264..000000000 --- a/grandpa/prover/src/lib.rs +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (C) 2023 PolytopeLabs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#![allow(clippy::all)] -#![deny(missing_docs)] - -//! GRANDPA prover utilities - -use anyhow::anyhow; -use codec::{Decode, Encode}; -use ismp::host::StateMachine; -use jsonrpsee::{async_client::Client, ws_client::WsClientBuilder}; -use primitives::{ - parachain_header_storage_key, ConsensusState, FinalityProof, ParachainHeaderProofs, - ParachainHeadersWithFinalityProof, -}; -use sc_consensus_grandpa_rpc::GrandpaApiClient; -use serde::{Deserialize, Serialize}; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature}; -use sp_core::H256; -use sp_runtime::traits::{One, Zero}; -use std::{ - collections::{BTreeMap, BTreeSet, HashMap}, - sync::Arc, -}; -use subxt::{config::Header, Config, OnlineClient}; - -/// Head data for parachain -#[derive(Decode, Encode)] -pub struct HeadData(pub Vec); - -/// Contains methods useful for proving parachain and standalone-chain header finality using GRANDPA -#[derive(Clone)] -pub struct GrandpaProver { - /// Subxt client for the chain - pub client: OnlineClient, - /// Chain jsonrpsee client for typed rpc requests, which subxt lacks support for. - pub ws_client: Arc, - /// ParaId of the associated parachains - pub para_ids: Vec, - /// State machine identifier for the chain - pub state_machine: StateMachine, - /// Storage for babe epoch start - pub babe_epoch_start: Vec, - /// Storage key for current set id - pub current_set_id: Vec, -} - -// We redefine these here because we want the header to be bounded by subxt::config::Header in the -// prover -/// Commit -pub type Commit = finality_grandpa::Commit; - -/// Justification -#[cfg_attr(any(feature = "std", test), derive(Debug))] -#[derive(Clone, Encode, Decode)] -pub struct GrandpaJustification { - /// Current voting round number, monotonically increasing - pub round: u64, - /// Contains block hash & number that's being finalized and the signatures. - pub commit: Commit, - /// Contains the path from a [`PreCommit`]'s target hash to the GHOST finalized block. - pub votes_ancestries: Vec, -} - -/// An encoded justification proving that the given header has been finalized -#[derive(Clone, Serialize, Deserialize)] -pub struct JustificationNotification(pub sp_core::Bytes); - -impl GrandpaProver -where - T: Config, - ::Number: Ord + Zero, - u32: From<::Number>, - sp_core::H256: From, - T::Header: codec::Decode, -{ - /// Initializes the parachain and relay chain clients given the ws urls. - pub async fn new( - ws_url: &str, - para_ids: Vec, - state_machine: StateMachine, - babe_epoch_start: Vec, - current_set_id: Vec, - ) -> Result { - let ws_client = Arc::new(WsClientBuilder::default().build(ws_url).await?); - let client = OnlineClient::::from_rpc_client(ws_client.clone()).await?; - - Ok(Self { ws_client, client, para_ids, state_machine, babe_epoch_start, current_set_id }) - } - - /// Construct the initial consensus state. - pub async fn initialize_consensus_state( - &self, - slot_duration: u64, - ) -> Result { - use sp_consensus_grandpa::AuthorityList; - let latest_hash = self.client.rpc().finalized_head().await?; - let header = self - .client - .rpc() - .header(Some(latest_hash)) - .await? - .ok_or_else(|| anyhow!("Header not found for hash: {latest_hash:?}"))?; - - let current_set_id: u64 = { - let raw_id = self - .client - .storage() - .at(latest_hash) - .fetch_raw(&self.current_set_id[..]) - .await - .ok() - .flatten() - .expect("Failed to fetch current set id"); - codec::Decode::decode(&mut &*raw_id)? - }; - - let current_authorities = { - let bytes = self - .client - .rpc() - .request::( - "state_call", - subxt::rpc_params!( - "GrandpaApi_grandpa_authorities", - "0x", - Some(format!("{:?}", latest_hash)) - ), - ) - .await - .map(|res| hex::decode(&res[2..]))??; - - AuthorityList::decode(&mut &bytes[..])? - }; - - // Ensure there are no duplicates in authority list - let mut set = BTreeSet::new(); - for (id, ..) in ¤t_authorities { - if !set.insert(id) { - Err(anyhow!("Duplicate entries found in current authority set"))? - } - } - - let latest_height = u32::from(header.number()); - - Ok(ConsensusState { - current_authorities, - current_set_id: current_set_id + 1, - latest_height, - latest_hash: latest_hash.into(), - para_ids: self.para_ids.iter().map(|id| (*id, true)).collect(), - state_machine: self.state_machine, - slot_duration, - }) - } - - /// Returns the grandpa finality proof - pub async fn query_finality_proof( - &self, - previous_finalized_height: u32, - mut latest_finalized_height: u32, - ) -> Result, anyhow::Error> - where - H: Header + codec::Decode, - u32: From<::Number>, - ::Output: From, - T::Hash: From<::Output>, - H::Number: finality_grandpa::BlockNumberOps + One, - { - let encoded = GrandpaApiClient::::prove_finality( - &*self.ws_client, - latest_finalized_height, - ) - .await? - .ok_or_else(|| anyhow!("No justification found for block: {:?}", latest_finalized_height))? - .0; - - let mut finality_proof = FinalityProof::::decode(&mut &encoded[..])?; - - let justification = - GrandpaJustification::::decode(&mut &finality_proof.justification[..])?; - - finality_proof.block = justification.commit.target_hash; - - latest_finalized_height = u32::from(justification.commit.target_number); - - let mut unknown_headers = vec![]; - for height in previous_finalized_height..=latest_finalized_height { - let hash = self - .client - .rpc() - .block_hash(Some(height.into())) - .await? - .ok_or_else(|| anyhow!("Failed to fetch block has for height {height}"))?; - - let header = self - .client - .rpc() - .header(Some(hash)) - .await? - .ok_or_else(|| anyhow!("Header with hash: {hash:?} not found!"))?; - - unknown_headers.push(H::decode(&mut &header.encode()[..])?); - } - - // overwrite unknown headers - finality_proof.unknown_headers = unknown_headers; - Ok(finality_proof) - } - - /// Returns the proof for parachain headers finalized by the provided finality proof - pub async fn query_finalized_parachain_headers_with_proof( - &self, - previous_finalized_height: u32, - latest_finalized_height: u32, - finality_proof: FinalityProof, - ) -> Result, anyhow::Error> - where - H: Header + codec::Decode, - u32: From<::Number>, - ::Output: From, - T::Hash: From<::Output>, - H::Number: finality_grandpa::BlockNumberOps + One, - { - // we are interested only in the blocks where our parachain header changes. - let para_keys: Vec<_> = - self.para_ids.iter().map(|para_id| parachain_header_storage_key(*para_id)).collect(); - let keys = para_keys.iter().map(|key| key.as_ref()).collect::>(); - let mut parachain_headers_with_proof = BTreeMap::::default(); - - let start = self - .client - .rpc() - .block_hash(Some(previous_finalized_height.into())) - .await? - .ok_or_else(|| anyhow!("Failed to fetch previous finalized hash + 1"))?; - - let latest_finalized_hash = self - .client - .rpc() - .block_hash(Some(latest_finalized_height.into())) - .await? - .ok_or_else(|| anyhow!("Failed to fetch previous finalized hash + 1"))?; - - let change_set = - self.client.rpc().query_storage(keys, start, Some(latest_finalized_hash)).await?; - - for changes in change_set { - let header = self - .client - .rpc() - .header(Some(changes.block)) - .await? - .ok_or_else(|| anyhow!("block not found {:?}", changes.block))?; - let mut changed_keys = HashMap::new(); - for para_id in self.para_ids.clone() { - let (key, parachain_header_bytes) = { - let key = parachain_header_storage_key(para_id); - if let Some(raw) = - self.client.storage().at(header.hash()).fetch_raw(key.as_ref()).await? - { - let head_data: HeadData = codec::Decode::decode(&mut &*raw)?; - (key, head_data.0) - } else { - continue - } - }; - - let para_header: H = Decode::decode(&mut ¶chain_header_bytes[..])?; - let para_block_number = para_header.number(); - // skip genesis header or any unknown headers - if para_block_number == Zero::zero() { - continue - } - - changed_keys.insert(key, para_id); - } - - if !changed_keys.is_empty() { - let state_proof = self - .client - .rpc() - .read_proof( - changed_keys.keys().into_iter().map(|key| key.as_ref()), - Some(header.hash()), - ) - .await? - .proof - .into_iter() - .map(|p| p.0) - .collect(); - - let proofs = ParachainHeaderProofs { - state_proof, - para_ids: changed_keys.values().into_iter().map(|id| *id).collect(), - }; - parachain_headers_with_proof.insert(header.hash().into(), proofs); - } - } - - Ok(ParachainHeadersWithFinalityProof { - finality_proof, - parachain_headers: parachain_headers_with_proof, - }) - } - - /// Queries the block at which the epoch for the given block belongs to ends. - pub async fn session_start_and_end_for_block( - &self, - block: u32, - ) -> Result<(u32, u32), anyhow::Error> { - let block_hash = self - .client - .rpc() - .block_hash(Some(block.into())) - .await? - .ok_or(anyhow!("Failed to fetch block hash"))?; - let bytes = self - .client - .storage() - .at(block_hash) - .fetch_raw(&self.babe_epoch_start[..]) - .await? - .ok_or_else(|| anyhow!("Failed to fetch epoch information"))?; - - let (previous_epoch_start, current_epoch_start): (u32, u32) = - codec::Decode::decode(&mut &*bytes)?; - Ok(( - current_epoch_start, - current_epoch_start + (current_epoch_start - previous_epoch_start), - )) - } - - /// Returns the session length in blocks - pub async fn session_length(&self) -> Result { - let metadata = self.client.rpc().metadata().await?; - let metadata = metadata - .pallet_by_name_err("Babe")? - .constant_by_name("EpochDuration") - .ok_or(anyhow!("Failed to fetch constant"))?; - Ok(Decode::decode(&mut metadata.value())?) - } -} diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs deleted file mode 100644 index 5c6c5d400..000000000 --- a/grandpa/src/consensus.rs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific lang - -use crate::messages::ConsensusMessage; -use alloc::{boxed::Box, collections::BTreeMap, format, vec::Vec}; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use finality_grandpa::Chain; -use ismp::{ - consensus::{ - ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineClient, - VerifiedCommitments, - }, - error::Error, - host::{IsmpHost, StateMachine}, - messaging::StateCommitmentHeight, -}; -use ismp_primitives::fetch_overlay_root_and_timestamp; -use primitive_types::H256; -use primitives::{ - justification::{AncestryChain, GrandpaJustification}, - ConsensusState, FinalityProof, ParachainHeadersWithFinalityProof, -}; -use sp_runtime::traits::Header; -use substrate_state_machine::SubstrateStateMachine; -use verifier::{ - verify_grandpa_finality_proof, verify_parachain_headers_with_grandpa_finality_proof, -}; - -/// [`ConsensusStateId`] for the polkadot relay chain -pub const POLKADOT_CONSENSUS_STATE_ID: ConsensusStateId = *b"polk"; - -/// [`ConsensusStateId`] for the kusama relay chain -pub const KUSAMA_CONSENSUS_STATE_ID: ConsensusStateId = *b"sama"; - -/// [`ConsensusClientId`] for GRANDPA consensus -pub const GRANDPA_CONSENSUS_ID: ConsensusClientId = *b"GRAN"; - -pub struct GrandpaConsensusClient(PhantomData); - -impl Default for GrandpaConsensusClient { - fn default() -> Self { - Self(PhantomData) - } -} - -impl ConsensusClient for GrandpaConsensusClient -where - T::Header: Header, - T: pallet_ismp::Config + super::Config, - T::BlockNumber: Into, -{ - fn verify_consensus( - &self, - _host: &dyn IsmpHost, - _consensus_state_id: ConsensusStateId, - trusted_consensus_state: Vec, - proof: Vec, - ) -> Result<(Vec, VerifiedCommitments), Error> { - // decode the proof into consensus message - let consensus_message: ConsensusMessage = - codec::Decode::decode(&mut &proof[..]).map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode consensus message from proof: {e:?}", - )) - })?; - - // decode the consensus state - let consensus_state: ConsensusState = - codec::Decode::decode(&mut &trusted_consensus_state[..]).map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode consensus state from trusted consensus state bytes: {e:?}", - )) - })?; - - let mut intermediates = BTreeMap::new(); - - // match over the message - match consensus_message { - ConsensusMessage::RelayChainMessage(relay_chain_message) => { - let headers_with_finality_proof = ParachainHeadersWithFinalityProof { - finality_proof: relay_chain_message.finality_proof, - parachain_headers: relay_chain_message.parachain_headers, - }; - - let (consensus_state, parachain_headers) = - verify_parachain_headers_with_grandpa_finality_proof( - consensus_state, - headers_with_finality_proof, - ) - .map_err(|_| { - Error::ImplementationSpecific(format!("Error verifying parachain headers")) - })?; - - for (para_id, header_vec) in parachain_headers { - let mut state_commitments_vec = Vec::new(); - - let state_id: StateMachine = match consensus_state.state_machine { - StateMachine::Polkadot(_) => StateMachine::Polkadot(para_id), - StateMachine::Kusama(_) => StateMachine::Kusama(para_id), - _ => Err(Error::ImplementationSpecific( - "Host state machine should be a parachain".into(), - ))?, - }; - - for header in header_vec { - let (timestamp, overlay_root) = fetch_overlay_root_and_timestamp( - header.digest(), - consensus_state.slot_duration, - )?; - - if timestamp == 0 { - Err(Error::ImplementationSpecific( - "Timestamp or ismp root not found".into(), - ))? - } - - let height: u32 = (*header.number()).into(); - - let intermediate = StateCommitmentHeight { - commitment: StateCommitment { - timestamp, - overlay_root: Some(overlay_root), - state_root: header.state_root, - }, - height: height.into(), - }; - - state_commitments_vec.push(intermediate); - } - - intermediates.insert(state_id, state_commitments_vec); - } - - Ok((consensus_state.encode(), intermediates)) - } - - ConsensusMessage::StandaloneChainMessage(standalone_chain_message) => { - let (consensus_state, header, _, _) = verify_grandpa_finality_proof( - consensus_state, - standalone_chain_message.finality_proof, - ) - .map_err(|_| { - Error::ImplementationSpecific( - "Error verifying parachain headers".parse().unwrap(), - ) - })?; - let (timestamp, overlay_root) = fetch_overlay_root_and_timestamp( - header.digest(), - consensus_state.slot_duration, - )?; - - if timestamp == 0 { - Err(Error::ImplementationSpecific("Timestamp or ismp root not found".into()))? - } - - let height: u32 = (*header.number()).into(); - - let state_id = consensus_state.state_machine; - - let intermediate = StateCommitmentHeight { - commitment: StateCommitment { - timestamp, - overlay_root: Some(overlay_root), - state_root: header.state_root, - }, - height: height.into(), - }; - - let mut state_commitments_vec = Vec::new(); - state_commitments_vec.push(intermediate); - - intermediates.insert(state_id, state_commitments_vec); - - Ok((consensus_state.encode(), intermediates)) - } - } - } - - fn verify_fraud_proof( - &self, - _host: &dyn IsmpHost, - trusted_consensus_state: Vec, - proof_1: Vec, - proof_2: Vec, - ) -> Result<(), Error> { - // decode the consensus state - let consensus_state: ConsensusState = - codec::Decode::decode(&mut &trusted_consensus_state[..]).map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode consensus state from trusted consensus state bytes: {e:?}", - )) - })?; - - let first_proof: FinalityProof = codec::Decode::decode(&mut &proof_1[..]) - .map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode first finality proof from proof_1 bytes: {e:?}", - )) - })?; - - let second_proof: FinalityProof = codec::Decode::decode(&mut &proof_2[..]) - .map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode second finality proof from proof_2 bytes: {e:?}", - )) - })?; - - if first_proof.block == second_proof.block { - return Err(Error::ImplementationSpecific(format!( - "Fraud proofs are for the same block", - ))) - } - - let first_headers = AncestryChain::::new(&first_proof.unknown_headers); - let first_target = - first_proof.unknown_headers.iter().max_by_key(|h| *h.number()).ok_or_else(|| { - Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) - })?; - - let second_headers = AncestryChain::::new(&second_proof.unknown_headers); - let second_target = - second_proof.unknown_headers.iter().max_by_key(|h| *h.number()).ok_or_else(|| { - Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) - })?; - - if first_target.hash() != first_proof.block || second_target.hash() != second_proof.block { - return Err(Error::ImplementationSpecific(format!( - "Fraud proofs are not for the same chain" - ))) - } - - let first_base = - first_proof.unknown_headers.iter().min_by_key(|h| *h.number()).ok_or_else(|| { - Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) - })?; - first_headers - .ancestry(first_base.hash(), first_target.hash()) - .map_err(|_| Error::ImplementationSpecific(format!("Invalid ancestry!")))?; - - let second_base = - second_proof.unknown_headers.iter().min_by_key(|h| *h.number()).ok_or_else(|| { - Error::ImplementationSpecific(format!("Unknown headers can't be empty!")) - })?; - second_headers - .ancestry(second_base.hash(), second_target.hash()) - .map_err(|_| Error::ImplementationSpecific(format!("Invalid ancestry!")))?; - - let first_parent = first_base.parent_hash(); - let second_parent = second_base.parent_hash(); - - if first_parent != second_parent { - return Err(Error::ImplementationSpecific(format!( - "Fraud proofs are not for the same ancestor" - ))) - } - - let first_justification = - GrandpaJustification::::decode(&mut &first_proof.justification[..]) - .map_err(|_| { - Error::ImplementationSpecific(format!("Could not decode first justification")) - })?; - - let second_justification = - GrandpaJustification::::decode(&mut &second_proof.justification[..]) - .map_err(|_| { - Error::ImplementationSpecific(format!("Could not decode second justification")) - })?; - - if first_proof.block != first_justification.commit.target_hash || - second_proof.block != second_justification.commit.target_hash - { - Err(Error::ImplementationSpecific( - format!("First or second finality proof block hash does not match justification target hash") - ))? - } - - if first_justification.commit.target_hash != consensus_state.latest_hash && - second_justification.commit.target_hash != consensus_state.latest_hash - { - Err(Error::ImplementationSpecific(format!( - "First or second justification does not match consensus latest hash" - )))? - } - - let first_valid = first_justification - .verify(consensus_state.current_set_id, &consensus_state.current_authorities) - .is_ok(); - let second_valid = second_justification - .verify(consensus_state.current_set_id, &consensus_state.current_authorities) - .is_ok(); - - if !first_valid || !second_valid { - Err(Error::ImplementationSpecific(format!("Invalid justification")))? - } - - Ok(()) - } - - fn state_machine(&self, _id: StateMachine) -> Result, Error> { - Ok(Box::new(SubstrateStateMachine::::default())) - } -} diff --git a/grandpa/src/lib.rs b/grandpa/src/lib.rs deleted file mode 100644 index a17ed4961..000000000 --- a/grandpa/src/lib.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific lang - -#![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; - -pub mod consensus; -pub mod messages; - -use alloc::vec::Vec; -pub use pallet::*; -use pallet_ismp::host::Host; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use ismp::host::IsmpHost; - use primitives::ConsensusState; - - #[pallet::pallet] - pub struct Pallet(_); - - /// The config trait - #[pallet::config] - pub trait Config: frame_system::Config + pallet_ismp::Config { - /// The overarching event type - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - /// Events emitted by this pallet - #[pallet::event] - pub enum Event {} - - #[pallet::error] - pub enum Error { - /// Standalone Consensus State Already Exists - StandaloneConsensusStateAlreadyExists, - /// Standalone Consensus Does not Exist - StandaloneConsensusStateDontExists, - /// Error fetching consensus state - ErrorFetchingConsensusState, - /// Error decoding consensus state - ErrorDecodingConsensusState, - /// Incorrect consensus state id length - IncorrectConsensusStateIdLength, - /// Error storing consensus state - ErrorStoringConsensusState, - } - - #[pallet::call] - impl Pallet { - /// Add some new parachains to the list of parachains in the relay chain consensus state - #[pallet::call_index(0)] - #[pallet::weight((0, DispatchClass::Mandatory))] - pub fn add_parachains( - origin: OriginFor, - consensus_state_id_vec: Vec, - para_ids: Vec, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - let ismp_host = Host::::default(); - let consensus_state_id = consensus_state_id_vec - .as_slice() - .try_into() - .map_err(|_| Error::::IncorrectConsensusStateIdLength)?; - - let encoded_consensus_state = ismp_host - .consensus_state(consensus_state_id) - .map_err(|_| Error::::ErrorFetchingConsensusState)?; - let mut consensus_state: ConsensusState = - codec::Decode::decode(&mut &encoded_consensus_state[..]) - .map_err(|_| Error::::ErrorDecodingConsensusState)?; - - let mut stored_para_ids = consensus_state.para_ids; - para_ids.iter().for_each(|para_id| { - stored_para_ids.entry(*para_id).or_insert(true); - }); - consensus_state.para_ids = stored_para_ids; - - let encoded_consensus_state = consensus_state.encode(); - ismp_host - .store_consensus_state(consensus_state_id, encoded_consensus_state) - .map_err(|_| Error::::ErrorStoringConsensusState)?; - Ok(()) - } - - /// Remove some parachains from the list of parachains in the relay chain consensus state - #[pallet::call_index(1)] - #[pallet::weight((0, DispatchClass::Mandatory))] - pub fn remove_parachains( - origin: OriginFor, - consensus_state_id_vec: Vec, - para_ids: Vec, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - let ismp_host = Host::::default(); - let consensus_state_id = consensus_state_id_vec - .as_slice() - .try_into() - .map_err(|_| Error::::IncorrectConsensusStateIdLength)?; - - let encoded_consensus_state = ismp_host - .consensus_state(consensus_state_id) - .map_err(|_| Error::::ErrorFetchingConsensusState)?; - let mut consensus_state: ConsensusState = - codec::Decode::decode(&mut &encoded_consensus_state[..]) - .map_err(|_| Error::::ErrorDecodingConsensusState)?; - - let mut stored_para_ids = consensus_state.para_ids; - stored_para_ids.retain(|&key, _| !para_ids.contains(&key)); - consensus_state.para_ids = stored_para_ids; - - let encoded_consensus_state = consensus_state.encode(); - ismp_host - .store_consensus_state(consensus_state_id, encoded_consensus_state) - .map_err(|_| Error::::ErrorStoringConsensusState)?; - Ok(()) - } - } -} diff --git a/grandpa/src/messages.rs b/grandpa/src/messages.rs deleted file mode 100644 index 40b0f8aed..000000000 --- a/grandpa/src/messages.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific lang -use alloc::collections::BTreeMap; -use codec::{Decode, Encode}; -use primitives::{FinalityProof, ParachainHeaderProofs}; -use sp_core::H256; -use sp_runtime::traits::BlakeTwo256; - -/// Relay chain substrate header type -pub type SubstrateHeader = sp_runtime::generic::Header; - -/// [`ClientMessage`] definition -#[derive(Clone, Debug, Encode, Decode)] -pub enum ConsensusMessage { - /// This is the variant representing the standalone chain - StandaloneChainMessage(StandaloneChainMessage), - /// This is the variant representing the relay chain - RelayChainMessage(RelayChainMessage), -} - -#[derive(Clone, Debug, Encode, Decode)] -pub struct StandaloneChainMessage { - /// finality proof - pub finality_proof: FinalityProof, -} - -#[derive(Clone, Debug, Encode, Decode)] -pub struct RelayChainMessage { - /// finality proof - pub finality_proof: FinalityProof, - /// parachain headers - pub parachain_headers: BTreeMap, -} diff --git a/grandpa/verifier/Cargo.toml b/grandpa/verifier/Cargo.toml deleted file mode 100644 index 4cc0df810..000000000 --- a/grandpa/verifier/Cargo.toml +++ /dev/null @@ -1,60 +0,0 @@ -[package] -name = "ismp-grandpa-verifier" -version = "0.1.0" -edition = "2021" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -anyhow = { version = "1.0.64", default-features = false } -finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } -serde = { version = "1.0.144", default-features = false, features = ["derive"] } -derive_more = { version = "0.99.17", default-features = false, features = ["from"] } - - -sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } - -primitives = { package = "ismp-grandpa-primitives", path = "../primitives", default-features = false } -substrate-state-machine = { path = "../../pallet-ismp/primitives/state-machine", default-features = false } - -[dev-dependencies] -polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.42" } -subxt = { version = "0.30.1", features = ["substrate-compat"] } -futures = "0.3.24" -hex = "0.4.3" -env_logger = "0.9.0" -log = "0.4.17" -tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] } -hex-literal = "0.3.4" -grandpa-prover = { package = "ismp-grandpa-prover", path = "../prover" } -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main"} -sc-finality-grandpa-rpc = "0.25.0" - - - -[features] -default = ["std"] -std = [ - "codec/std", - "anyhow/std", - "finality-grandpa/std", - "frame-support/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", - "sp-consensus-grandpa/std", - "sp-io/std", - "primitives/std", - "serde/std", - "sp-storage/std", - "substrate-state-machine/std" -] diff --git a/grandpa/verifier/src/lib.rs b/grandpa/verifier/src/lib.rs deleted file mode 100644 index b9a40f2f8..000000000 --- a/grandpa/verifier/src/lib.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! GRANDPA consensus client verification function - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::all)] -#![deny(missing_docs)] - -#[cfg(test)] -mod tests; - -extern crate alloc; - -use alloc::collections::BTreeMap; -use anyhow::anyhow; -use codec::Decode; -use finality_grandpa::Chain; -use primitives::{ - justification::{find_scheduled_change, AncestryChain, GrandpaJustification}, - parachain_header_storage_key, ConsensusState, FinalityProof, ParachainHeadersWithFinalityProof, -}; -use sp_core::H256; -use sp_runtime::traits::{BlakeTwo256, Header}; -use sp_std::prelude::*; -use sp_trie::StorageProof; -use substrate_state_machine::read_proof_check; - -/// This function verifies the GRANDPA finality proof for both standalone chain and parachain -/// headers. -pub fn verify_grandpa_finality_proof( - mut consensus_state: ConsensusState, - finality_proof: FinalityProof, -) -> Result<(ConsensusState, H, Vec, AncestryChain), anyhow::Error> -where - H: Header, - H::Number: finality_grandpa::BlockNumberOps + Into, -{ - // First validate unknown headers. - let headers = AncestryChain::::new(&finality_proof.unknown_headers); - - let target = finality_proof - .unknown_headers - .iter() - .max_by_key(|h| *h.number()) - .ok_or_else(|| anyhow!("Unknown headers can't be empty!"))?; - - // this is illegal - if target.hash() != finality_proof.block { - Err(anyhow!("Latest finalized block should be highest block in unknown_headers"))?; - } - - let justification = GrandpaJustification::::decode(&mut &finality_proof.justification[..]) - .map_err(|e| anyhow!("Failed to decode justificatio {:?}", e))?; - - if justification.commit.target_hash != finality_proof.block { - Err(anyhow!("Justification target hash and finality proof block hash mismatch"))?; - } - - let from = consensus_state.latest_hash; - - let base = finality_proof - .unknown_headers - .iter() - .min_by_key(|h| *h.number()) - .ok_or_else(|| anyhow!("Unknown headers can't be empty!"))?; - - if base.number() < &consensus_state.latest_height { - headers.ancestry(base.hash(), consensus_state.latest_hash).map_err(|_| { - anyhow!( - "[verify_grandpa_finality_proof] Invalid ancestry (base -> latest relay block)!" - ) - })?; - } - - let mut finalized = headers - .ancestry(from, target.hash()) - .map_err(|_| anyhow!("[verify_grandpa_finality_proof] Invalid ancestry!"))?; - finalized.sort(); - - // 2. verify justification. - justification.verify(consensus_state.current_set_id, &consensus_state.current_authorities)?; - - // Sets new consensus state, optionally rotating authorities - consensus_state.latest_hash = target.hash(); - consensus_state.latest_height = (*target.number()).into(); - if let Some(scheduled_change) = find_scheduled_change::(&target) { - consensus_state.current_set_id += 1; - consensus_state.current_authorities = scheduled_change.next_authorities; - } - - Ok((consensus_state, target.clone(), finalized, headers)) -} -/// This function verifies the GRANDPA finality proof for relay chain headers. -/// -/// Next, we prove the finality of parachain headers, by verifying patricia-merkle trie state proofs -/// of these headers, stored at the recently finalized relay chain heights. -/// Returns the new Consensus state alongside a map of para id to a vector that contains a tuple of -/// finalized parachain header and timestamp -pub fn verify_parachain_headers_with_grandpa_finality_proof( - consensus_state: ConsensusState, - proof: ParachainHeadersWithFinalityProof, -) -> Result<(ConsensusState, BTreeMap>), anyhow::Error> -where - H: Header, - H::Number: finality_grandpa::BlockNumberOps + Into, -{ - let ParachainHeadersWithFinalityProof { finality_proof, parachain_headers } = proof; - - let (consensus_state, _, finalized_hashes, headers) = - verify_grandpa_finality_proof(consensus_state, finality_proof)?; - // verifies state proofs of parachain headers in finalized relay chain headers. - let mut verified_parachain_headers: BTreeMap> = BTreeMap::new(); - for (hash, proof) in parachain_headers { - if finalized_hashes.binary_search(&hash).is_err() { - // seems relay hash isn't in the finalized chain. - continue - } - let relay_chain_header = - headers.header(&hash).expect("Headers have been checked by AncestryChain; qed"); - let state_proof = proof.state_proof; - let mut keys = BTreeMap::new(); - for para_id in proof.para_ids { - // ensure the para id is in the consensus state before proof verification - if !consensus_state.para_ids.contains_key(¶_id) { - continue - } - - let key = parachain_header_storage_key(para_id); - - keys.insert(key.0, para_id); - } - - let proof = StorageProof::new(state_proof); - - // verify patricia-merkle state proofs - let mut result = read_proof_check::( - relay_chain_header.state_root(), - proof, - keys.keys().map(|key| key.as_slice()), - ) - .map_err(|err| anyhow!("error verifying parachain header state proof: {err:?}"))?; - for (key, para_id) in keys { - let header = result - .remove(&key) - .flatten() - .ok_or_else(|| anyhow!("Invalid proof, parachain header not found"))?; - let parachain_header = - H::decode(&mut &header[..]).map_err(|e| anyhow!("error decoding header: {e:?}"))?; - verified_parachain_headers.entry(para_id).or_default().push(parachain_header); - } - } - - Ok((consensus_state, verified_parachain_headers)) -} diff --git a/grandpa/verifier/src/tests.rs b/grandpa/verifier/src/tests.rs deleted file mode 100644 index dc7ec3e54..000000000 --- a/grandpa/verifier/src/tests.rs +++ /dev/null @@ -1,145 +0,0 @@ -use crate::verify_parachain_headers_with_grandpa_finality_proof; -use codec::{Decode, Encode}; -use futures::StreamExt; -use grandpa_prover::GrandpaProver; -use ismp::host::StateMachine; -use polkadot_core_primitives::Header; -use primitives::{justification::GrandpaJustification, ParachainHeadersWithFinalityProof}; -use serde::{Deserialize, Serialize}; -use sp_core::{crypto::AccountId32, H256}; -use subxt::{ - config::{ - polkadot::PolkadotExtrinsicParams as ParachainExtrinsicParams, - substrate::{BlakeTwo256, SubstrateHeader}, - }, - rpc_params, -}; - -pub struct DefaultConfig; - -impl subxt::config::Config for DefaultConfig { - type Hash = H256; - type AccountId = AccountId32; - type Address = sp_runtime::MultiAddress; - type Signature = sp_runtime::MultiSignature; - type Hasher = subxt::config::substrate::BlakeTwo256; - type Header = - subxt::config::substrate::SubstrateHeader; - type ExtrinsicParams = ParachainExtrinsicParams; -} - -pub type Justification = GrandpaJustification

; - -/// An encoded justification proving that the given header has been finalized -#[derive(Clone, Serialize, Deserialize)] -pub struct JustificationNotification(sp_core::Bytes); - -#[ignore] -#[tokio::test] -async fn follow_grandpa_justifications() { - env_logger::builder() - .filter_module("grandpa", log::LevelFilter::Trace) - .format_module_path(false) - .init(); - - let relay = std::env::var("RELAY_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); - - let relay_ws_url = format!("ws://{relay}:9944"); - - let para_ids = vec![2000, 2001]; - let babe_epoch_start_key = - hex::decode("1cb6f36e027abb2091cfb5110ab5087fe90e2fbf2d792cb324bffa9427fe1f0e").unwrap(); - let current_set_id_key = - hex::decode("5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc").unwrap(); - - let prover = GrandpaProver::::new( - &relay_ws_url, - para_ids, - StateMachine::Polkadot(0), - babe_epoch_start_key, - current_set_id_key, - ) - .await - .unwrap(); - - println!("Waiting for grandpa proofs to become available"); - let session_length = prover.session_length().await.unwrap(); - prover - .client - .blocks() - .subscribe_finalized() - .await - .unwrap() - .filter_map(|result| futures::future::ready(result.ok())) - .skip_while(|h| futures::future::ready(h.number() < (session_length * 2) + 10)) - .take(1) - .collect::>() - .await; - - let mut subscription = prover - .client - .rpc() - .subscribe::( - "grandpa_subscribeJustifications", - rpc_params![], - "grandpa_unsubscribeJustifications", - ) - .await - .unwrap() - .take(100); - - // slot duration in milliseconds for parachains - let slot_duration = 12_000; - - let mut consensus_state = prover.initialize_consensus_state(slot_duration).await.unwrap(); - - println!("Grandpa proofs are now available"); - while let Some(Ok(_)) = subscription.next().await { - let next_relay_height = consensus_state.latest_height + 1; - - // prove finality should give us the justification for the highest finalized block of the - // authority set the block provided to it belongs - let finality_proof = prover - .query_finality_proof::>( - consensus_state.latest_height, - next_relay_height, - ) - .await - .unwrap(); - - let justification = Justification::decode(&mut &finality_proof.justification[..]).unwrap(); - - println!("current_set_id: {}", consensus_state.current_set_id); - println!("latest_relay_height: {}", consensus_state.latest_height); - println!( - "For relay chain header: Hash({:?}), Number({})", - justification.commit.target_hash, justification.commit.target_number - ); - - let proof = prover - .query_finalized_parachain_headers_with_proof::>( - consensus_state.latest_height, - justification.commit.target_number, - finality_proof.clone(), - ) - .await - .expect("Failed to fetch finalized parachain headers with proof"); - - let proof = proof.encode(); - let proof = ParachainHeadersWithFinalityProof::
::decode(&mut &*proof).unwrap(); - - let (new_consensus_state, _parachain_headers) = - verify_parachain_headers_with_grandpa_finality_proof::
( - consensus_state.clone(), - proof.clone(), - ) - .expect("Failed to verify parachain headers with grandpa finality_proof"); - - if !proof.parachain_headers.is_empty() { - assert!(new_consensus_state.latest_height > consensus_state.latest_height); - } - - consensus_state = new_consensus_state; - println!("========= Successfully verified grandpa justification ========="); - } -} diff --git a/pallet-ismp/evm/Cargo.toml b/pallet-ismp/evm/Cargo.toml deleted file mode 100644 index 19944fadc..000000000 --- a/pallet-ismp/evm/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "ismp-evm" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs "] - -[dependencies] -# substrate -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } - -# EVM support -pallet-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } -fp-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v0.9.42", default-features = false } -alloy-sol-types = { version = "0.3.1", default-features = false } -alloy-primitives = { version = "0.3.1", default-features = false } - - - # polytope labs -ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } - -# crates.io -codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } -hex-literal = "0.4.1" - -# local -ismp-primitives = { path = "../primitives", default-features = false } -pallet-ismp = { path = "..", default-features = false } - -[dev-dependencies] -pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -ismp-testsuite = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } -pallet-ismp = { path = "..", features = ["testing"] } -hex = "0.4.3" -scale-info = { version = "2.1.1", features = ["derive"] } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "sp-runtime/std", - "sp-std/std", - "sp-core/std", - "ismp-rs/std", - "ismp-primitives/std", - "pallet-evm/std", - "fp-evm/std", - "alloy-primitives/std", - "alloy-sol-types/std", - "pallet-ismp/std" -] - diff --git a/pallet-ismp/evm/solidity/IsmpDemo.bin b/pallet-ismp/evm/solidity/IsmpDemo.bin deleted file mode 100644 index 061bdc0cc..000000000 --- a/pallet-ismp/evm/solidity/IsmpDemo.bin +++ /dev/null @@ -1 +0,0 @@ -608060405234801561001057600080fd5b50600080546001600160401b031916633b9aca0017905561153d806100366000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c80634e87ba19116100665780634e87ba191461011857806393d756aa1461012b578063c52c28af1461013e578063c715f52b14610151578063f370fdbb1461016457600080fd5b806320fe202a1461009857806327e235e3146100ad5780632a9a1bc6146100f25780634c46c03514610105575b600080fd5b6100ab6100a6366004610b3a565b610177565b005b6100d66100bb366004610bc3565b6001602052600090815260409020546001600160401b031681565b6040516001600160401b03909116815260200160405180910390f35b6100ab610100366004610c99565b610277565b6100ab610113366004610df4565b6102e4565b6100ab610126366004610f36565b6103ac565b6100ab610139366004610f6a565b61048a565b6100ab61014c366004610fa3565b6104cc565b6100ab61015f366004610f36565b610543565b6100ab610172366004611033565b6105c7565b6101813384610693565b60408051606080820183526001600160a01b0388811683523360208085019182526001600160401b03898116868801908152875160a0810189528c81528851600360621b818601528951601481830301815260349091018a528185015293518751915189519187169482019490945294168488015216928201929092529192600092908201906080016040516020818303038152906040528152602001856001600160401b03168152602001846001600160401b0316815250905061024581610725565b6040517fb56b29fc7f72737821b2cf8a996ecebbea0d83daada768f5564a873f402cdd2190600090a150505050505050565b6040805160a0810182528681526001600160401b038086166020830152918101869052838216606082015290821660808201526102b381610823565b6040517f51a298a0eb173998598862ac7e847cb267f553d502347aec82bf8b81528dd7aa90600090a1505050505050565b3373843b131bd76419934dae248f6e5a195c0a3c324d14610318576040516351ab8de560e01b815260040160405180910390fd5b60005b8160a001515181101561037f5760008260a00151828151811061034057610340611190565b60200260200101519050805160000361036c57604051632b3f6d1160e21b815260040160405180910390fd5b5080610377816111bc565b91505061031b565b506040517f7265059b18e944f453138c2649bb3f8dbc4e555e5c0605b0ffb419cf8ae8c1ba90600090a150565b3373843b131bd76419934dae248f6e5a195c0a3c324d146103e0576040516351ab8de560e01b815260040160405180910390fd5b60006103ef8260c00151610915565b9050600060405180604001604052808481526020013360405160200161042d919060609190911b6bffffffffffffffffffffffff1916815260140190565b60405160208183030381529060405281525090506104538260000151836040015161094a565b61045c816109a0565b6040517f6cc69d45d4c21d1cb43e28df929c90259b7ad8d41e32f02f47ccda71aeb046ab90600090a1505050565b3373843b131bd76419934dae248f6e5a195c0a3c324d146104be576040516351ab8de560e01b815260040160405180910390fd5b6104c8828261094a565b5050565b3373843b131bd76419934dae248f6e5a195c0a3c324d14610500576040516351ab8de560e01b815260040160405180910390fd5b6000610513826000015160c00151610915565b6040519091507f2d87323053ce5d424f6bb99d64894f647db556f19f4319ee6bf817bf23bf520790600090a15050565b3373843b131bd76419934dae248f6e5a195c0a3c324d14610577576040516351ab8de560e01b815260040160405180910390fd5b60006105868260c00151610915565b905061059a8160200151826040015161094a565b6040517f6cc69d45d4c21d1cb43e28df929c90259b7ad8d41e32f02f47ccda71aeb046ab90600090a15050565b3373843b131bd76419934dae248f6e5a195c0a3c324d146105fb576040516351ab8de560e01b815260040160405180910390fd5b60005b8160200151518110156106665760008260200151828151811061062357610623611190565b6020026020010151905080602001515160000361065357604051632b3f6d1160e21b815260040160405180910390fd5b508061065e816111bc565b9150506105fe565b506040517f2d87323053ce5d424f6bb99d64894f647db556f19f4319ee6bf817bf23bf520790600090a150565b6000546106aa9082906001600160401b03166111d5565b6000805467ffffffffffffffff19166001600160401b039283161781556001600160a01b0384168152600160205260409020546106e9918391166111d5565b6001600160a01b03929092166000908152600160205260409020805467ffffffffffffffff19166001600160401b039093169290921790915550565b805160208083015160408085015160608601516080870151925160009661075096909594910161124c565b604051602081830303815290604052905060008073222a98a2832ae77e72a768bf5be1f82d8959f4ec6001600160a01b03168360405161079091906112ab565b600060405180830381855afa9150503d80600081146107cb576040519150601f19603f3d011682016040523d82523d6000602084013e6107d0565b606091505b50915091508161081d5760405162461bcd60e51b8152602060048201526013602482015272111a5cdc185d18da141bdcdd0819985a5b1959606a1b60448201526064015b60405180910390fd5b50505050565b805160208083015160408085015160608601516080870151925160009661084e9690959491016112c7565b604051602081830303815290604052905060008073f2d8dc5239ddc053ba5151302483fc48d7e24e606001600160a01b03168360405161088e91906112ab565b600060405180830381855afa9150503d80600081146108c9576040519150601f19603f3d011682016040523d82523d6000602084013e6108ce565b606091505b50915091508161081d5760405162461bcd60e51b8152602060048201526012602482015271111a5cdc185d18da11d95d0819985a5b195960721b6044820152606401610814565b60408051606081018252600080825260208083018290529282015282519091610944918401810190840161136f565b92915050565b6000546109619082906001600160401b03166113e0565b6000805467ffffffffffffffff19166001600160401b039283161781556001600160a01b0384168152600160205260409020546106e9918391166113e0565b6000816000015182602001516040516020016109bd929190611400565b604051602081830303815290604052905060008073eb928e2de75cb5ab60abe75f539c5312aeb46f386001600160a01b03168360405161079091906112ab565b6001600160a01b0381168114610a1257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b0381118282101715610a4e57610a4e610a15565b60405290565b604080519081016001600160401b0381118282101715610a4e57610a4e610a15565b604051601f8201601f191681016001600160401b0381118282101715610a9e57610a9e610a15565b604052919050565b600082601f830112610ab757600080fd5b81356001600160401b03811115610ad057610ad0610a15565b610ae3601f8201601f1916602001610a76565b818152846020838601011115610af857600080fd5b816020850160208301376000918101602001919091529392505050565b6001600160401b0381168114610a1257600080fd5b8035610b3581610b15565b919050565b600080600080600060a08688031215610b5257600080fd5b8535610b5d816109fd565b945060208601356001600160401b03811115610b7857600080fd5b610b8488828901610aa6565b9450506040860135610b9581610b15565b92506060860135610ba581610b15565b91506080860135610bb581610b15565b809150509295509295909350565b600060208284031215610bd557600080fd5b8135610be0816109fd565b9392505050565b60006001600160401b03821115610c0057610c00610a15565b5060051b60200190565b600082601f830112610c1b57600080fd5b81356020610c30610c2b83610be7565b610a76565b82815260059290921b84018101918181019086841115610c4f57600080fd5b8286015b84811015610c8e5780356001600160401b03811115610c725760008081fd5b610c808986838b0101610aa6565b845250918301918301610c53565b509695505050505050565b600080600080600060a08688031215610cb157600080fd5b85356001600160401b0380821115610cc857600080fd5b610cd489838a01610aa6565b96506020880135915080821115610cea57600080fd5b50610b8488828901610c0a565b60006101008284031215610d0a57600080fd5b610d12610a2b565b905081356001600160401b0380821115610d2b57600080fd5b610d3785838601610aa6565b83526020840135915080821115610d4d57600080fd5b610d5985838601610aa6565b6020840152610d6a60408501610b2a565b60408401526060840135915080821115610d8357600080fd5b610d8f85838601610aa6565b6060840152610da060808501610b2a565b608084015260a0840135915080821115610db957600080fd5b50610dc684828501610c0a565b60a083015250610dd860c08301610b2a565b60c0820152610de960e08301610b2a565b60e082015292915050565b600060208284031215610e0657600080fd5b81356001600160401b03811115610e1c57600080fd5b610e2884828501610cf7565b949350505050565b60006101008284031215610e4357600080fd5b610e4b610a2b565b905081356001600160401b0380821115610e6457600080fd5b610e7085838601610aa6565b83526020840135915080821115610e8657600080fd5b610e9285838601610aa6565b6020840152610ea360408501610b2a565b60408401526060840135915080821115610ebc57600080fd5b610ec885838601610aa6565b60608401526080840135915080821115610ee157600080fd5b610eed85838601610aa6565b6080840152610efe60a08501610b2a565b60a084015260c0840135915080821115610f1757600080fd5b50610f2484828501610aa6565b60c083015250610de960e08301610b2a565b600060208284031215610f4857600080fd5b81356001600160401b03811115610f5e57600080fd5b610e2884828501610e30565b60008060408385031215610f7d57600080fd5b8235610f88816109fd565b91506020830135610f9881610b15565b809150509250929050565b600060208284031215610fb557600080fd5b81356001600160401b0380821115610fcc57600080fd5b9083019060408286031215610fe057600080fd5b610fe8610a54565b823582811115610ff757600080fd5b61100387828601610e30565b82525060208301358281111561101857600080fd5b61102487828601610aa6565b60208301525095945050505050565b6000602080838503121561104657600080fd5b82356001600160401b038082111561105d57600080fd5b8185019150604080838803121561107357600080fd5b61107b610a54565b83358381111561108a57600080fd5b61109689828701610cf7565b82525084840135838111156110aa57600080fd5b80850194505087601f8501126110bf57600080fd5b83356110cd610c2b82610be7565b81815260059190911b8501860190868101908a8311156110ec57600080fd5b8787015b8381101561117c578035878111156111085760008081fd5b8801808d03601f190187131561111e5760008081fd5b611126610a54565b8a820135898111156111385760008081fd5b6111468f8d83860101610aa6565b825250878201358981111561115b5760008081fd5b6111698f8d83860101610aa6565b828d0152508452509188019188016110f0565b509683019690965250979650505050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016111ce576111ce6111a6565b5060010190565b6001600160401b038281168282160390808211156111f5576111f56111a6565b5092915050565b60005b838110156112175781810151838201526020016111ff565b50506000910152565b600081518084526112388160208601602086016111fc565b601f01601f19169290920160200192915050565b60a08152600061125f60a0830188611220565b82810360208401526112718188611220565b905082810360408401526112858187611220565b9150506001600160401b0380851660608401528084166080840152509695505050505050565b600082516112bd8184602087016111fc565b9190910192915050565b60a0815260006112da60a0830188611220565b60206001600160401b0388168185015283820360408501528187518084528284019150828160051b850101838a0160005b8381101561133957601f19878403018552611327838351611220565b9486019492509085019060010161130b565b50506001600160401b038916606088015294506113569350505050565b6001600160401b03831660808301529695505050505050565b60006060828403121561138157600080fd5b604051606081018181106001600160401b03821117156113a3576113a3610a15565b60405282516113b1816109fd565b815260208301516113c1816109fd565b602082015260408301516113d481610b15565b60408201529392505050565b6001600160401b038181168382160190808211156111f5576111f56111a6565b604081526000835161010080604085015261141f610140850183611220565b91506020860151603f198086850301606087015261143d8483611220565b93506040880151915061145b60808701836001600160401b03169052565b60608801519150808685030160a08701526114768483611220565b935060808801519150808685030160c08701526114938483611220565b935060a088015191506114b160e08701836001600160401b03169052565b60c08801519150808685030183870152506114cc8382611220565b9250505060e08501516114eb6101208501826001600160401b03169052565b5082810360208401526114fe8185611220565b9594505050505056fea26469706673582212208967a70afc3a4559f27a0324033b5443642a3061a7ed50b79e4fddb2645a434664736f6c63430008110033 \ No newline at end of file diff --git a/pallet-ismp/evm/solidity/foundry.toml b/pallet-ismp/evm/solidity/foundry.toml deleted file mode 100644 index e69de29bb..000000000 diff --git a/pallet-ismp/evm/solidity/lib/ismp-solidity b/pallet-ismp/evm/solidity/lib/ismp-solidity deleted file mode 160000 index 85510c619..000000000 --- a/pallet-ismp/evm/solidity/lib/ismp-solidity +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 85510c619074a4f61aa0ae0ee40bbb92f4feaac6 diff --git a/pallet-ismp/evm/solidity/remappings.txt b/pallet-ismp/evm/solidity/remappings.txt deleted file mode 100644 index 4fec4129e..000000000 --- a/pallet-ismp/evm/solidity/remappings.txt +++ /dev/null @@ -1,3 +0,0 @@ -openzeppelin/=lib/ismp-solidity/lib/openzeppelin-contracts/contracts/ -solidity-merkle-trees/=lib/ismp-solidity/lib/solidity-merkle-trees/src/ -ismp-solidity/=lib/ismp-solidity/src \ No newline at end of file diff --git a/pallet-ismp/evm/solidity/src/example.sol b/pallet-ismp/evm/solidity/src/example.sol deleted file mode 100644 index 407e9ee23..000000000 --- a/pallet-ismp/evm/solidity/src/example.sol +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// A Sample ISMP solidity contract for unit tests - -pragma solidity ^0.8.2; - -import "ismp-solidity/SubstrateHost.sol"; -import "ismp-solidity/interfaces/IIsmpDispatcher.sol"; -import "solidity-merkle-trees/MerklePatricia.sol"; - -address constant HOST = 0x843b131BD76419934dae248F6e5a195c0A3C324D; - -error NotIsmpHost(); -error ExecutionFailed(); - -struct Payload { - address to; - address from; - uint64 amount; -} - -contract IsmpDemo is IIsmpModule { - using SubstrateHost for *; - uint64 totalSupply; - - // Mapping of user address to balance - mapping(address => uint64) public balances; - event ResponseReceived(); - event TimeoutReceived(); - event BalanceMinted(); - event BalanceBurnt(); - event GetDispatched(); - - // restricts call to `IsmpHost` - modifier onlyIsmpHost() { - if (msg.sender != HOST) { - revert NotIsmpHost(); - } - _; - } - - constructor() { - totalSupply = 1000000000; - } - - function onAccept(PostRequest memory request) public onlyIsmpHost { - Payload memory payload = decodePayload(request.body); - PostResponse memory response = PostResponse({ - request: request, - response: abi.encodePacked(msg.sender) - }); - _mint(payload.to, payload.amount); - SubstrateHost.dispatch(response); - emit BalanceMinted(); - - } - - function onPostResponse(PostResponse memory response) public onlyIsmpHost { - // In this callback just try to decode the payload of the corresponding request - Payload memory payload = decodePayload(response.request.body); - emit ResponseReceived(); - } - - function onGetResponse(GetResponse memory response) public onlyIsmpHost { - // For the purpose of this test - // we just validate the responses in this callback - for (uint256 index = 0; index < response.values.length; index++) { - StorageValue memory storageValue = response.values[index]; - if (storageValue.value.length == 0) { - revert ExecutionFailed(); - } - } - emit ResponseReceived(); - } - - function onGetTimeout(GetRequest memory request) public onlyIsmpHost { - // We validate the keys in this callback - for (uint256 index = 0; index < request.keys.length; index++) { - bytes memory key = request.keys[index]; - // No keys should be empty - if (key.length == 0) { - revert ExecutionFailed(); - } - } - emit TimeoutReceived(); - } - - function onPostTimeout(PostRequest memory request) public onlyIsmpHost { - Payload memory payload = decodePayload(request.body); - _mint(payload.from, payload.amount); - emit BalanceMinted(); - } - - function decodePayload( - bytes memory data - ) internal pure returns (Payload memory payload) { - (payload) = abi.decode(data, (Payload)); - return payload; - } - - function transfer( - address to, - bytes memory dest, - uint64 amount, - uint64 timeout, - uint64 gasLimit - ) public { - _burn(msg.sender, amount); - Payload memory payload = Payload({ - from: msg.sender, - to: to, - amount: amount - }); - DispatchPost memory dispatchPost = DispatchPost({ - body: abi.encode(payload.from, payload.to, payload.amount), - dest: dest, - timeoutTimestamp: timeout, - to: abi.encodePacked(address(12)), - gaslimit: gasLimit - }); - SubstrateHost.dispatch(dispatchPost); - emit BalanceBurnt(); - } - - function dispatchGet( - bytes memory dest, - bytes[] memory keys, - uint64 height, - uint64 timeout, - uint64 gasLimit - ) public { - DispatchGet memory get = DispatchGet({ - keys: keys, - dest: dest, - height: height, - timeoutTimestamp: timeout, - gaslimit: gasLimit - }); - SubstrateHost.dispatch(get); - emit GetDispatched(); - - } - - function mintTo(address who, uint64 amount) public onlyIsmpHost { - _mint(who, amount); - } - - function _mint(address who, uint64 amount) internal { - totalSupply = totalSupply + amount; - balances[who] = balances[who] + amount; - } - - function _burn(address who, uint64 amount) internal { - totalSupply = totalSupply - amount; - balances[who] = balances[who] - amount; - } -} diff --git a/pallet-ismp/evm/src/abi.rs b/pallet-ismp/evm/src/abi.rs deleted file mode 100644 index 0fb7e51d3..000000000 --- a/pallet-ismp/evm/src/abi.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Solidity rust bindings -#![allow(missing_docs)] -use alloy_sol_types::sol; -use sp_std::prelude::*; - -sol! { - -struct PostRequest { - // the source state machine of this request - bytes source; - // the destination state machine of this request - bytes dest; - // request nonce - uint64 nonce; - // Module Id of this request origin - bytes from; - // destination module id - bytes to; - // timestamp by which this request times out. - uint64 timeoutTimestamp; - // request body - bytes body; - // gas limit for executing this request on destination & its response (if any) on the source. - uint64 gaslimit; -} - -struct GetRequest { - // the source state machine of this request - bytes source; - // the destination state machine of this request - bytes dest; - // request nonce - uint64 nonce; - // Module Id of this request origin - bytes from; - // timestamp by which this request times out. - uint64 timeoutTimestamp; - // Storage keys to read. - bytes[] keys; - // height at which to read destination state machine - uint64 height; - // gas limit for executing this request on destination & its response (if any) on the source. - uint64 gaslimit; -} - -struct StorageValue { - bytes key; - bytes value; -} - -struct GetResponse { - // The request that initiated this response - GetRequest request; - // storage values for get response - StorageValue[] values; -} - -struct PostResponse { - // The request that initiated this response - PostRequest request; - // bytes for post response - bytes response; -} - -// An object for dispatching post requests to the IsmpDispatcher -struct DispatchPost { - // bytes representation of the destination chain - bytes dest; - // the destination module - bytes to; - // the request body - bytes body; - // the timestamp at which this request should timeout - uint64 timeoutTimestamp; - // gas limit for executing this request on destination & its response (if any) on the source. - uint64 gaslimit; -} - -// An object for dispatching get requests to the IsmpDispatcher -struct DispatchGet { - // bytes representation of the destination chain - bytes dest; - // height at which to read the state machine - uint64 height; - // Storage keys to read - bytes[] keys; - // the timestamp at which this request should timeout - uint64 timeoutTimestamp; - // gas limit for executing this request on destination & its response (if any) on the source. - uint64 gaslimit; -} - - -function onAccept(PostRequest memory request) external; -function onPostResponse(PostResponse memory response) external; -function onGetResponse(GetResponse memory response) external; -function onPostTimeout(PostRequest memory request) external; -function onGetTimeout(GetRequest memory request) external; -} diff --git a/pallet-ismp/evm/src/lib.rs b/pallet-ismp/evm/src/lib.rs deleted file mode 100644 index 288c4b2d1..000000000 --- a/pallet-ismp/evm/src/lib.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implementation of the [`IsmpModule`] for solidity contracts on substrate - -#![cfg_attr(not(feature = "std"), no_std)] -#![deny(missing_docs)] - -extern crate alloc; - -pub mod abi; -#[cfg(test)] -mod mocks; -pub mod module; -pub mod precompiles; -#[cfg(test)] -mod tests; -pub mod weight; diff --git a/pallet-ismp/evm/src/mocks.rs b/pallet-ismp/evm/src/mocks.rs deleted file mode 100644 index 0b32f17e9..000000000 --- a/pallet-ismp/evm/src/mocks.rs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use core::str::FromStr; -use fp_evm::{ - FeeCalculator, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, - PrecompileSet, -}; -use frame_support::{dispatch::Weight, parameter_types}; - -use frame_support::traits::{ConstU32, ConstU64, FindAuthor, Get}; -use frame_system::EnsureRoot; -use ismp_rs::{ - consensus::{ConsensusClient, ConsensusClientId}, - error::Error, - host::StateMachine, - module::IsmpModule, - router::IsmpRouter, -}; -use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, IdentityAddressMapping}; - -use crate::{ - module::EvmIsmpModule, - precompiles::{ - IsmpGetDispatcher, IsmpPostDispatcher, IsmpResponseDispatcher, GET_REQUEST_DISPATCHER, - POST_REQUEST_DISPATCHER, POST_RESPONSE_DISPATCHER, - }, -}; -use pallet_ismp::{ - mocks::ismp::MockConsensusClient, - primitives::{ConsensusClientProvider, ModuleId}, -}; -use sp_core::{H160, H256, U256}; -use sp_runtime::{ - testing::Header, - traits::{IdentityLookup, Keccak256}, - ConsensusEngineId, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( -pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, - EVM: pallet_evm::{Pallet, Call, Storage, Config, Event}, - } -); - -pub struct StateMachineProvider; - -impl Get for StateMachineProvider { - fn get() -> StateMachine { - StateMachine::Kusama(100) - } -} - -pub struct ConsensusProvider; - -impl ConsensusClientProvider for ConsensusProvider { - fn consensus_client(_id: ConsensusClientId) -> Result, Error> { - Ok(Box::new(MockConsensusClient)) - } -} - -impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = Keccak256; - type AccountId = H160; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type DbWeight = (); - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} - -parameter_types! { - pub const ExistentialDeposit: u64 = 1000; -} -impl pallet_balances::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Balance = u64; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type ReserveIdentifier = (); - type HoldIdentifier = (); - type FreezeIdentifier = (); - type MaxLocks = (); - type MaxReserves = (); - type MaxHolds = (); - type MaxFreezes = (); -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<1>; - type WeightInfo = (); -} - -impl pallet_ismp::Config for Test { - type RuntimeEvent = RuntimeEvent; - const INDEXING_PREFIX: &'static [u8] = b"ISMP"; - type AdminOrigin = EnsureRoot; - type StateMachine = StateMachineProvider; - type TimeProvider = Timestamp; - type IsmpRouter = ModuleRouter; - type ConsensusClientProvider = ConsensusProvider; - type WeightInfo = (); - type WeightProvider = (); -} - -#[derive(Default)] -pub struct ModuleRouter; - -impl IsmpRouter for ModuleRouter { - fn module_for_id(&self, bytes: Vec) -> Result, Error> { - let module_id = ModuleId::from_bytes(&bytes).unwrap(); - match module_id { - ModuleId::Evm(_) => Ok(Box::new(EvmIsmpModule::::default())), - _ => Err(Error::ImplementationSpecific("Module handler not found".to_string())), - } - } -} - -pub struct FixedGasPrice; -impl FeeCalculator for FixedGasPrice { - fn min_gas_price() -> (U256, Weight) { - // Return some meaningful gas price and weight - (10u128.into(), Weight::from_parts(7u64, 0)) - } -} - -pub struct FindAuthorTruncated; -impl FindAuthor for FindAuthorTruncated { - fn find_author<'a, I>(_digests: I) -> Option - where - I: 'a + IntoIterator, - { - Some(H160::from_str("1234500000000000000000000000000000000000").unwrap()) - } -} -const BLOCK_GAS_LIMIT: u64 = 1_500_000_000; -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; - -parameter_types! { - pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); - pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); - pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); - pub MockPrecompiles: MockPrecompileSet = MockPrecompileSet; -} -impl pallet_evm::Config for Test { - type FeeCalculator = FixedGasPrice; - type GasWeightMapping = pallet_evm::FixedGasWeightMapping; - type WeightPerGas = WeightPerGas; - - type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; - type CallOrigin = EnsureAddressRoot; - - type WithdrawOrigin = EnsureAddressNever; - type AddressMapping = IdentityAddressMapping; - type Currency = Balances; - - type RuntimeEvent = RuntimeEvent; - type PrecompilesType = MockPrecompileSet; - type PrecompilesValue = MockPrecompiles; - type ChainId = (); - type BlockGasLimit = BlockGasLimit; - type Runner = pallet_evm::runner::stack::Runner; - type OnChargeTransaction = (); - type OnCreate = (); - type FindAuthor = FindAuthorTruncated; - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; - type Timestamp = Timestamp; - type WeightInfo = (); -} -/// Example PrecompileSet with only Identity precompile. -pub struct MockPrecompileSet; - -impl PrecompileSet for MockPrecompileSet { - /// Tries to execute a precompile in the precompile set. - /// If the provided address is not a precompile, returns None. - fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - let address = handle.code_address(); - if address == POST_REQUEST_DISPATCHER { - return Some(IsmpPostDispatcher::::execute(handle)) - } else if address == GET_REQUEST_DISPATCHER { - return Some(IsmpGetDispatcher::::execute(handle)) - } else if address == POST_RESPONSE_DISPATCHER { - return Some(IsmpResponseDispatcher::::execute(handle)) - } - - None - } - - /// Check if the given address is a precompile. Should only be called to - /// perform the check while not executing the precompile afterward, since - /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { - IsPrecompileResult::Answer { - is_precompile: address == POST_REQUEST_DISPATCHER || - address == GET_REQUEST_DISPATCHER || - address == POST_RESPONSE_DISPATCHER, - extra_cost: 0, - } - } -} diff --git a/pallet-ismp/evm/src/module.rs b/pallet-ismp/evm/src/module.rs deleted file mode 100644 index 40f38ce8b..000000000 --- a/pallet-ismp/evm/src/module.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! Module Handler for EVM contracts -use crate::abi::{ - onAcceptCall, onGetResponseCall, onGetTimeoutCall, onPostResponseCall, onPostTimeoutCall, - GetRequest as SolGetRequest, GetResponse as SolGetResponse, PostRequest, - PostResponse as SolPostResponse, StorageValue as SolStorageValue, -}; -use alloc::{format, string::ToString}; -use alloy_sol_types::SolCall; -use core::marker::PhantomData; -use fp_evm::{ExitReason, FeeCalculator}; -use hex_literal::hex; -use ismp_rs::{ - error::Error, - module::IsmpModule, - router::{Post, Request, Response}, -}; -use pallet_evm::GasWeightMapping; -use pallet_ismp::{primitives::ModuleId, WeightConsumed}; -use sp_core::H160; -use sp_std::prelude::*; - -/// Handler host address -/// Contracts should only allow ismp module callbacks to be executed by this address -pub const EVM_HOST_ADDRESS: [u8; 20] = hex!("843b131bd76419934dae248f6e5a195c0a3c324d"); - -/// [`IsmpModule`] implementation that routes requests & responses to EVM contracts. -pub struct EvmIsmpModule(PhantomData); - -impl Default for EvmIsmpModule { - fn default() -> Self { - Self(PhantomData) - } -} - -impl IsmpModule for EvmIsmpModule { - fn on_accept(&self, request: Post) -> Result<(), Error> { - let target_contract = parse_contract_id(&request.to)?; - let gaslimit = request.gas_limit; - let post = PostRequest { - source: request.source.to_string().as_bytes().to_vec(), - dest: request.dest.to_string().as_bytes().to_vec(), - nonce: request.nonce, - timeoutTimestamp: request.timeout_timestamp, - from: request.from, - to: request.to, - body: request.data, - gaslimit, - }; - let call_data = onAcceptCall { request: post }.encode(); - execute_call::(target_contract, call_data, gaslimit) - } - - fn on_response(&self, response: Response) -> Result<(), Error> { - let target_contract = parse_contract_id(&response.destination_module())?; - - let (call_data, gas_limit) = match response { - Response::Post(response) => { - // we set the gas limit for executing the contract to be the same as used in the - // request. we assume the request was dispatched with a gas limit - // that accounts for execution of the response on this source chain - let gaslimit = response.post.gas_limit; - let post_response = SolPostResponse { - request: PostRequest { - source: response.post.source.to_string().as_bytes().to_vec(), - dest: response.post.dest.to_string().as_bytes().to_vec(), - nonce: response.post.nonce, - timeoutTimestamp: response.post.timeout_timestamp, - from: response.post.from, - to: response.post.to, - body: response.post.data, - gaslimit, - }, - response: response.response, - }; - (onPostResponseCall { response: post_response }.encode(), gaslimit) - } - Response::Get(response) => { - let gaslimit = response.get.gas_limit; - let get_response = SolGetResponse { - request: SolGetRequest { - source: response.get.source.to_string().as_bytes().to_vec(), - dest: response.get.dest.to_string().as_bytes().to_vec(), - nonce: response.get.nonce, - height: response.get.height, - timeoutTimestamp: response.get.timeout_timestamp, - from: response.get.from, - keys: response.get.keys, - gaslimit, - }, - values: response - .values - .into_iter() - .map(|(key, value)| SolStorageValue { - key, - value: value.unwrap_or_default(), - }) - .collect(), - }; - (onGetResponseCall { response: get_response }.encode(), gaslimit) - } - }; - - execute_call::(target_contract, call_data, gas_limit) - } - - fn on_timeout(&self, request: Request) -> Result<(), Error> { - let target_contract = parse_contract_id(&request.source_module())?; - let (call_data, gas_limit) = match request { - Request::Post(post) => { - let gaslimit = post.gas_limit; - let request = PostRequest { - source: post.source.to_string().as_bytes().to_vec(), - dest: post.dest.to_string().as_bytes().to_vec(), - nonce: post.nonce, - timeoutTimestamp: post.timeout_timestamp, - from: post.from, - to: post.to, - body: post.data, - gaslimit, - }; - (onPostTimeoutCall { request }.encode(), gaslimit) - } - Request::Get(get) => { - let gaslimit = get.gas_limit; - let request = SolGetRequest { - source: get.source.to_string().as_bytes().to_vec(), - dest: get.dest.to_string().as_bytes().to_vec(), - nonce: get.nonce, - height: get.height, - timeoutTimestamp: get.timeout_timestamp, - from: get.from, - keys: get.keys, - gaslimit, - }; - (onGetTimeoutCall { request }.encode(), gaslimit) - } - }; - execute_call::(target_contract, call_data, gas_limit) - } -} - -/// Parse contract id from raw bytes -pub fn parse_contract_id(bytes: &[u8]) -> Result { - let module_id = - ModuleId::from_bytes(bytes).map_err(|e| Error::ImplementationSpecific(e.to_string()))?; - match module_id { - ModuleId::Evm(id) => Ok(id), - _ => Err(Error::ImplementationSpecific("Expected Evm contract id".to_string())), - } -} - -/// Call execute call data -fn execute_call( - target: H160, - call_data: Vec, - gas_limit: u64, -) -> Result<(), Error> { - let (weight_used, result) = - match <::Runner as pallet_evm::Runner>::call( - H160::from(EVM_HOST_ADDRESS), - target, - call_data, - Default::default(), - gas_limit, - Some(<::FeeCalculator as FeeCalculator>::min_gas_price().0), - Some(<::FeeCalculator as FeeCalculator>::min_gas_price().0), - None, - Default::default(), - true, - true, - None, - None, - ::config(), - ) { - Ok(info) => { - let weight = - T::GasWeightMapping::gas_to_weight(info.used_gas.standard.low_u64(), true); - let result = match info.exit_reason { - ExitReason::Succeed(_) => Ok(()), - _ => Err(Error::ImplementationSpecific( - "Contract call did not successfully execute".to_string(), - )), - }; - (weight, result) - } - Err(error) => { - let dispatch_error: sp_runtime::DispatchError = error.error.into(); - ( - error.weight, - Err(Error::ImplementationSpecific(format!( - "Contract call failed with error {:?}", - dispatch_error - ))), - ) - } - }; - let mut total_weight_used = WeightConsumed::::get(); - let weight_limit = T::GasWeightMapping::gas_to_weight(gas_limit, true); - total_weight_used.weight_used = total_weight_used.weight_used + weight_used; - total_weight_used.weight_limit = total_weight_used.weight_limit + weight_limit; - WeightConsumed::::put(total_weight_used); - result -} diff --git a/pallet-ismp/evm/src/precompiles.rs b/pallet-ismp/evm/src/precompiles.rs deleted file mode 100644 index 6ecc00f28..000000000 --- a/pallet-ismp/evm/src/precompiles.rs +++ /dev/null @@ -1,173 +0,0 @@ -//! IsmpDispatcher precompiles for pallet-evm - -use pallet_ismp::{dispatcher::Dispatcher, weight_info::WeightInfo}; - -use crate::abi::{ - DispatchGet as SolDispatchGet, DispatchPost as SolDispatchPost, PostResponse as SolPostResponse, -}; -use alloc::{format, str::FromStr, string::String}; -use alloy_sol_types::SolType; -use core::marker::PhantomData; -use fp_evm::{ - ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, - PrecompileResult, -}; -use frame_support::traits::Get; -use hex_literal::hex; -use ismp_rs::{ - host::StateMachine, - router::{DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher, Post, PostResponse}, -}; -use pallet_evm::GasWeightMapping; -use sp_core::H160; -use sp_std::prelude::*; - -/// Ismp Request Dispatcher precompile for evm contracts -pub struct IsmpPostDispatcher { - _marker: PhantomData, -} - -/// Address for the post request precompile -pub const POST_REQUEST_DISPATCHER: H160 = H160(hex!("222a98a2832ae77e72a768bf5be1f82d8959f4ec")); -/// Address for the post response precompile -pub const POST_RESPONSE_DISPATCHER: H160 = H160(hex!("eb928e2de75cb5ab60abe75f539c5312aeb46f38")); -/// Address for the get request precompile -pub const GET_REQUEST_DISPATCHER: H160 = H160(hex!("f2d8dc5239ddc053ba5151302483fc48d7e24e60")); - -impl Precompile for IsmpPostDispatcher -where - T: pallet_ismp::Config + pallet_evm::Config, -{ - fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { - let input = handle.input(); - let context = handle.context(); - let weight = ::WeightInfo::dispatch_post_request(); - - // The cost of a dispatch is the weight of calling the dispatcher plus an extra storage read - // and write - let cost = ::GasWeightMapping::weight_to_gas(weight); - - let dispatcher = Dispatcher::::default(); - let post_dispatch = - SolDispatchPost::decode(input, true).map_err(|e| PrecompileFailure::Error { - exit_status: ExitError::Other(format!("Failed to decode input: {:?}", e).into()), - })?; - - let post_dispatch = DispatchPost { - dest: parse_state_machine(post_dispatch.dest)?, - from: context.caller.0.to_vec(), - to: post_dispatch.to, - timeout_timestamp: post_dispatch.timeoutTimestamp, - data: post_dispatch.body, - gas_limit: post_dispatch.gaslimit, - }; - - handle.record_cost(cost)?; - match dispatcher.dispatch_request(DispatchRequest::Post(post_dispatch)) { - Ok(_) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![] }), - Err(e) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("dispatch execution failed: {:?}", e).into()), - }), - } - } -} - -/// Ismp Get Request Dispatcher precompile for evm contracts -pub struct IsmpGetDispatcher { - _marker: PhantomData, -} - -impl Precompile for IsmpGetDispatcher -where - T: pallet_ismp::Config + pallet_evm::Config, -{ - fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { - let input = handle.input(); - let context = handle.context(); - - let weight = ::WeightInfo::dispatch_get_request(); - - // The cost of a dispatch is the weight of calling the dispatcher plus an extra storage read - // and write - let cost = ::GasWeightMapping::weight_to_gas( - weight.saturating_add(::DbWeight::get().reads_writes(1, 1)), - ); - - let dispatcher = Dispatcher::::default(); - - let get_dispatch = - SolDispatchGet::decode(input, true).map_err(|e| PrecompileFailure::Error { - exit_status: ExitError::Other(format!("Failed to decode input: {:?}", e).into()), - })?; - let get_dispatch = DispatchGet { - dest: parse_state_machine(get_dispatch.dest)?, - from: context.caller.0.to_vec(), - keys: get_dispatch.keys, - height: get_dispatch.height, - timeout_timestamp: get_dispatch.timeoutTimestamp, - gas_limit: get_dispatch.gaslimit, - }; - - handle.record_cost(cost)?; - match dispatcher.dispatch_request(DispatchRequest::Get(get_dispatch)) { - Ok(_) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![] }), - Err(e) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("dispatch execution failed: {:?}", e).into()), - }), - } - } -} - -/// Ismp Response Dispatcher precompile for evm contracts -pub struct IsmpResponseDispatcher { - _marker: PhantomData, -} - -impl Precompile for IsmpResponseDispatcher -where - T: pallet_ismp::Config + pallet_evm::Config, -{ - fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { - let input = handle.input(); - - let weight = ::WeightInfo::dispatch_response(); - - let cost = ::GasWeightMapping::weight_to_gas(weight); - - let dispatcher = Dispatcher::::default(); - let response = - SolPostResponse::decode(input, true).map_err(|e| PrecompileFailure::Error { - exit_status: ExitError::Other(format!("Failed to decode input: {:?}", e).into()), - })?; - let post_response = PostResponse { - post: Post { - source: parse_state_machine(response.request.source)?, - dest: parse_state_machine(response.request.dest)?, - nonce: response.request.nonce, - from: response.request.from, - to: response.request.to, - timeout_timestamp: response.request.timeoutTimestamp, - data: response.request.body, - gas_limit: response.request.gaslimit, - }, - response: response.response, - }; - handle.record_cost(cost)?; - - match dispatcher.dispatch_response(post_response) { - Ok(_) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: vec![] }), - Err(e) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("dispatch execution failed: {:?}", e).into()), - }), - } - } -} - -/// Parse state machine from utf8 bytes -fn parse_state_machine(bytes: Vec) -> Result { - StateMachine::from_str(&String::from_utf8(bytes).unwrap_or_default()).map_err(|e| { - PrecompileFailure::Error { - exit_status: ExitError::Other(format!("Failed to destination chain: {:?}", e).into()), - } - }) -} diff --git a/pallet-ismp/evm/src/tests.rs b/pallet-ismp/evm/src/tests.rs deleted file mode 100644 index a8897cdc2..000000000 --- a/pallet-ismp/evm/src/tests.rs +++ /dev/null @@ -1,433 +0,0 @@ -use crate::{ - mocks::*, - module::{EvmIsmpModule, EVM_HOST_ADDRESS}, -}; -use alloy_primitives::Address; -use alloy_sol_types::{sol, SolCall, SolType}; -use fp_evm::{CreateInfo, FeeCalculator, GenesisAccount}; -use frame_support::{ - traits::{GenesisBuild, Get}, - weights::Weight, -}; -use frame_system::EventRecord; -use hex_literal::hex; -use ismp_primitives::LeafIndexQuery; -use ismp_rs::{ - host::StateMachine, - module::IsmpModule, - router::{Get as GetRequest, GetResponse, Post, PostResponse, Request, Response}, - util::hash_request, -}; -use pallet_evm::{runner::Runner, FixedGasWeightMapping, GasWeightMapping}; -use pallet_ismp::{host::Host, Event, RequestCommitments}; -use sp_core::{ - offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, - H160, U256, -}; -use std::collections::BTreeMap; - -sol! { - function transfer( - address to, - bytes memory dest, - uint64 amount, - uint64 timeout, - uint64 gasLimit - ) public; - - function dispatchGet( - bytes memory dest, - bytes[] memory keys, - uint64 height, - uint64 timeout, - uint64 gasLimit - ) public; - - function mintTo(address who, uint64 amount) public; - - struct Payload { - address to; - address from; - uint64 amount; - } -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - let mut accounts = BTreeMap::new(); - accounts.insert( - H160::from(USER.0 .0), - GenesisAccount { - nonce: U256::from(1), - balance: U256::max_value(), - storage: Default::default(), - code: vec![], - }, - ); - accounts.insert( - H160::from(EVM_HOST_ADDRESS), // root - GenesisAccount { - nonce: U256::from(1), - balance: U256::max_value(), - storage: Default::default(), - code: vec![], - }, - ); - - GenesisBuild::::assimilate_storage(&pallet_evm::GenesisConfig { accounts }, &mut t) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - register_offchain_ext(&mut ext); - ext -} - -fn register_offchain_ext(ext: &mut sp_io::TestExternalities) { - let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); - ext.register_extension(OffchainDbExt::new(offchain.clone())); - ext.register_extension(OffchainWorkerExt::new(offchain)); -} - -pub const EXAMPLE_CONTRACT: &str = include_str!("../solidity/IsmpDemo.bin"); - -const USER: Address = Address::new(hex!("d8da6bf26964af9d7eed9e03e53415d37aa96045")); -const HOST: H160 = H160(EVM_HOST_ADDRESS); - -/// Verify the the last event emitted -fn assert_event_was_emitted( - generic_event: ::RuntimeEvent, -) { - let events = frame_system::Pallet::::events(); - let system_event: ::RuntimeEvent = generic_event.into(); - for EventRecord { event, .. } in events { - if event == system_event { - return - } - } - panic!("Event was not emitted") -} - -fn deploy_contract(gas_limit: u64, weight_limit: Option) -> CreateInfo { - let info = ::Runner::create( - HOST, - hex::decode(EXAMPLE_CONTRACT.trim_end()).unwrap(), - U256::zero(), - gas_limit, - Some(FixedGasPrice::min_gas_price().0), - Some(FixedGasPrice::min_gas_price().0), - None, - Vec::new(), - true, // non-transactional - true, // must be validated - weight_limit, - None, - &::config().clone(), - ) - .expect("Deploy succeeds"); - - let call_data = mintToCall { who: USER, amount: 1_000_000_000 }.encode(); - - let contract_address = info.value; - - ::Runner::call( - HOST, - contract_address, - call_data, - U256::zero(), - gas_limit, - Some(FixedGasPrice::min_gas_price().0), - Some(FixedGasPrice::min_gas_price().0), - None, - Vec::new(), - true, // transactional - true, // must be validated - weight_limit, - None, - &::config().clone(), - ) - .expect("call succeeds"); - info -} - -#[test] -fn post_dispatch() { - let mut ext = new_test_ext(); - let contract_address = ext.execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let call_data = transferCall { - to: USER, - dest: StateMachine::Polkadot(1000).to_string().as_bytes().to_vec(), - amount: 10_000, - timeout: 223311228889, - gasLimit: gas_limit, - } - .encode(); - - ::Runner::call( - H160::from(USER.0 .0), - contract_address, - call_data, - U256::zero(), - gas_limit, - Some(FixedGasPrice::min_gas_price().0), - Some(FixedGasPrice::min_gas_price().0), - None, - Vec::new(), - true, // transactional - true, // must be validated - Some(weight_limit), - None, - &::config().clone(), - ) - .expect("call succeeds"); - // Check - assert_event_was_emitted::( - Event::Request { - dest_chain: StateMachine::Polkadot(1000), - source_chain: ::StateMachine::get(), - request_nonce: 0, - } - .into(), - ); - contract_address - }); - - ext.persist_offchain_overlay(); - - ext.execute_with(|| { - // Assert that the source module for the request is the contract address - let req = pallet_ismp::Pallet::::get_request(0).unwrap(); - assert_eq!(req.source_module().to_vec(), contract_address.as_bytes().to_vec()) - }) -} - -#[test] -fn get_dispatch() { - let mut ext = new_test_ext(); - let contract_address = ext.execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let call_data = dispatchGetCall { - dest: StateMachine::Polkadot(2000).to_string().as_bytes().to_vec(), - keys: vec![vec![1u8; 64]], - height: 10, - timeout: 2000, - gasLimit: gas_limit, - } - .encode(); - - ::Runner::call( - H160::from(USER.0 .0), - contract_address, - call_data, - U256::zero(), - gas_limit, - Some(FixedGasPrice::min_gas_price().0), - Some(FixedGasPrice::min_gas_price().0), - None, - Vec::new(), - true, // transactional - true, // must be validated - Some(weight_limit), - None, - &::config().clone(), - ) - .expect("call succeeds"); - // Check - assert_event_was_emitted::( - Event::Request { - dest_chain: StateMachine::Polkadot(2000), - source_chain: ::StateMachine::get(), - request_nonce: 0, - } - .into(), - ); - contract_address - }); - - ext.persist_offchain_overlay(); - - ext.execute_with(|| { - // Assert that the source module for the request is the contract address - let req = pallet_ismp::Pallet::::get_request(0).unwrap(); - assert_eq!(req.source_module().to_vec(), contract_address.as_bytes().to_vec()) - }) -} - -#[test] -fn on_accept_callback() { - new_test_ext().execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let handler = EvmIsmpModule::::default(); - - let payload = Payload { to: USER, from: USER, amount: 50000 }; - - let post = Post { - source: ::StateMachine::get(), - dest: StateMachine::Polkadot(2000), - nonce: 0, - from: contract_address.as_bytes().to_vec(), - to: contract_address.as_bytes().to_vec(), - timeout_timestamp: 1000, - data: Payload::encode(&payload), - gas_limit, - }; - - let request_commitment = hash_request::>(&Request::Post(post.clone())); - RequestCommitments::::insert( - request_commitment, - LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, - ); - - handler.on_accept(post).expect("Call succeeds"); - - assert_event_was_emitted::( - Event::Response { - dest_chain: ::StateMachine::get(), - source_chain: StateMachine::Polkadot(2000), - request_nonce: 0, - } - .into(), - ); - }) -} - -#[test] -fn on_post_response() { - new_test_ext().execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let handler = EvmIsmpModule::::default(); - - let payload = Payload { to: USER, from: USER, amount: 50000 }; - - let post = Post { - source: ::StateMachine::get(), - dest: StateMachine::Polkadot(2000), - nonce: 0, - from: contract_address.as_bytes().to_vec(), - to: contract_address.as_bytes().to_vec(), - timeout_timestamp: 1000, - data: Payload::encode(&payload), - gas_limit, - }; - - let response = PostResponse { post, response: H160::from_low_u64_be(30).0.to_vec() }; - - handler.on_response(Response::Post(response)).expect("Call succeeds") - }) -} - -#[test] -fn on_get_response() { - new_test_ext().execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let handler = EvmIsmpModule::::default(); - - let get = GetRequest { - source: ::StateMachine::get(), - dest: StateMachine::Polkadot(2000), - nonce: 0, - from: contract_address.as_bytes().to_vec(), - keys: vec![ - H160::from_low_u64_be(10).as_bytes().to_vec(), - H160::from_low_u64_be(20).as_bytes().to_vec(), - ], - height: 10, - timeout_timestamp: 1000, - gas_limit, - }; - - let mut values = BTreeMap::new(); - values.insert( - H160::from_low_u64_be(10).as_bytes().to_vec(), - Some(H160::from_low_u64_be(10).as_bytes().to_vec()), - ); - values.insert( - H160::from_low_u64_be(20).as_bytes().to_vec(), - Some(H160::from_low_u64_be(20).as_bytes().to_vec()), - ); - let response = GetResponse { get, values }; - - handler.on_response(Response::Get(response)).expect("Call succeeds") - }) -} - -#[test] -fn on_get_timeout() { - new_test_ext().execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let handler = EvmIsmpModule::::default(); - - let get = GetRequest { - source: ::StateMachine::get(), - dest: StateMachine::Polkadot(2000), - nonce: 0, - from: contract_address.as_bytes().to_vec(), - keys: vec![ - H160::from_low_u64_be(10).as_bytes().to_vec(), - H160::from_low_u64_be(20).as_bytes().to_vec(), - ], - height: 10, - timeout_timestamp: 1000, - gas_limit, - }; - - handler.on_timeout(Request::Get(get)).expect("Call succeeds") - }) -} - -#[test] -fn on_post_timeout() { - new_test_ext().execute_with(|| { - let gas_limit: u64 = 1_500_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - let result = deploy_contract(gas_limit, Some(weight_limit)); - - let contract_address = result.value; - - let handler = EvmIsmpModule::::default(); - let payload = Payload { to: USER, from: USER, amount: 50000 }; - let post = Post { - source: ::StateMachine::get(), - dest: StateMachine::Polkadot(2000), - nonce: 0, - from: contract_address.as_bytes().to_vec(), - to: contract_address.as_bytes().to_vec(), - timeout_timestamp: 1000, - data: Payload::encode(&payload), - gas_limit, - }; - - handler.on_timeout(Request::Post(post)).expect("Call succeeds") - }) -} diff --git a/pallet-ismp/evm/src/weight.rs b/pallet-ismp/evm/src/weight.rs deleted file mode 100644 index 08fddbcc0..000000000 --- a/pallet-ismp/evm/src/weight.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Weight info utilities for evm contracts -use core::marker::PhantomData; -use frame_support::dispatch::Weight; -use ismp_rs::router::{Post, Request, Response}; -use pallet_evm::GasWeightMapping; -use pallet_ismp::{weight_info::IsmpModuleWeight, Config}; - -/// An implementation of IsmpModuleWeight for evm contract callbacks -pub struct EvmWeightCalculator(PhantomData); - -impl Default for EvmWeightCalculator { - fn default() -> Self { - Self(PhantomData) - } -} - -impl IsmpModuleWeight for EvmWeightCalculator { - fn on_accept(&self, request: &Post) -> Weight { - ::GasWeightMapping::gas_to_weight(request.gas_limit, true) - } - - fn on_timeout(&self, request: &Request) -> Weight { - match request { - Request::Post(post) => { - ::GasWeightMapping::gas_to_weight(post.gas_limit, true) - } - Request::Get(get) => { - ::GasWeightMapping::gas_to_weight(get.gas_limit, true) - } - } - } - - fn on_response(&self, response: &Response) -> Weight { - match response { - Response::Post(response) => ::GasWeightMapping::gas_to_weight( - response.post.gas_limit, - true, - ), - Response::Get(response) => ::GasWeightMapping::gas_to_weight( - response.get.gas_limit, - true, - ), - } - } -} diff --git a/pallet-ismp/primitives/state-machine/Cargo.toml b/pallet-ismp/primitives/state-machine/Cargo.toml deleted file mode 100644 index 472314a57..000000000 --- a/pallet-ismp/primitives/state-machine/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "substrate-state-machine" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs "] - -[dependencies] -# substrate -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } - -# polytope labs -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } - -# crates.io -merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } -codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } -primitive-types = { version = "0.12.1", default-features = false } -serde = { version = "1.0.136", features = ["derive"], optional = true } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -hash-db = { version = "0.16.0", default-features = false } - -ismp-primitives = { path = "..", default-features = false } -pallet-ismp = { path = "../..", default-features = false } - -[features] -default = ["std"] -std = [ - "frame-system/std", - "ismp/std", - "merkle-mountain-range/std", - "codec/std", - "sp-runtime/std", - "primitive-types/std", - "scale-info/std", - "serde", - "frame-support/std", - "sp-core/std", - "pallet-ismp/std", - "ismp-primitives/std", - "sp-trie/std", - "hash-db/std" -] diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs deleted file mode 100644 index 53652baad..000000000 --- a/pallet-ismp/primitives/state-machine/src/lib.rs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The state machine implementation in Substrate -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::all)] -#![deny(missing_docs)] - -extern crate alloc; - -use alloc::{collections::BTreeMap, format, vec, vec::Vec}; -use codec::Decode; -use core::{fmt::Debug, marker::PhantomData}; -use ismp::{ - consensus::{StateCommitment, StateMachineClient}, - error::Error, - host::IsmpHost, - messaging::Proof, - router::{Request, RequestResponse}, - util::hash_request, -}; -use ismp_primitives::{ - mmr::{DataOrHash, Leaf, MmrHasher}, - HashAlgorithm, MembershipProof, SubstrateStateProof, -}; -use merkle_mountain_range::MerkleProof; -use pallet_ismp::host::Host; -use sp_runtime::traits::{BlakeTwo256, Keccak256}; -use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; - -/// The parachain and grandpa consensus client implementation for ISMP. -pub struct SubstrateStateMachine(PhantomData); - -impl Default for SubstrateStateMachine { - fn default() -> Self { - Self(PhantomData) - } -} - -impl StateMachineClient for SubstrateStateMachine -where - T: pallet_ismp::Config, - T::BlockNumber: Into, -{ - fn verify_membership( - &self, - _host: &dyn IsmpHost, - item: RequestResponse, - state: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let membership = MembershipProof::decode(&mut &*proof.proof).map_err(|e| { - Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) - })?; - let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = MerkleProof::>>::new(membership.mmr_size, nodes); - let leaves: Vec<(u64, DataOrHash)> = match item { - RequestResponse::Request(req) => membership - .leaf_indices - .into_iter() - .zip(req.into_iter()) - .map(|(pos, req)| (pos, DataOrHash::Data(Leaf::Request(req)))) - .collect(), - RequestResponse::Response(res) => membership - .leaf_indices - .into_iter() - .zip(res.into_iter()) - .map(|(pos, res)| (pos, DataOrHash::Data(Leaf::Response(res)))) - .collect(), - }; - let root = state - .overlay_root - .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; - - let calc_root = proof - .calculate_root(leaves.clone()) - .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; - let valid = calc_root.hash::>() == root.into(); - - if !valid { - Err(Error::ImplementationSpecific("Invalid membership proof".into()))? - } - - Ok(()) - } - - fn state_trie_key(&self, requests: Vec) -> Vec> { - let mut keys = vec![]; - - for req in requests { - match req { - Request::Post(post) => { - let request = Request::Post(post); - let commitment = hash_request::>(&request); - keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); - } - Request::Get(_) => continue, - } - } - - keys - } - - fn verify_state_proof( - &self, - _host: &dyn IsmpHost, - keys: Vec>, - root: StateCommitment, - proof: &Proof, - ) -> Result, Option>>, Error> { - let state_proof: SubstrateStateProof = codec::Decode::decode(&mut &*proof.proof) - .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; - - let data = match state_proof.hasher { - HashAlgorithm::Keccak => { - let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); - keys.into_iter() - .map(|key| { - let value = trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!( - "Error reading state proof: {e:?}" - )) - })?; - Ok((key, value)) - }) - .collect::, _>>()? - } - HashAlgorithm::Blake2 => { - let db = - StorageProof::new(state_proof.storage_proof).into_memory_db::(); - - let trie = - TrieDBBuilder::>::new(&db, &root.state_root).build(); - keys.into_iter() - .map(|key| { - let value = trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!( - "Error reading state proof: {e:?}" - )) - })?; - Ok((key, value)) - }) - .collect::, _>>()? - } - }; - - Ok(data) - } -} - -/// Lifted directly from [`sp_state_machine::read_proof_check`](https://github.com/paritytech/substrate/blob/b27c470eaff379f512d1dec052aff5d551ed3b03/primitives/state-machine/src/lib.rs#L1075-L1094) -pub fn read_proof_check( - root: &H::Out, - proof: StorageProof, - keys: I, -) -> Result, Option>>, Error> -where - H: hash_db::Hasher, - H::Out: Debug, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let db = proof.into_memory_db(); - - if !db.contains(root, EMPTY_PREFIX) { - Err(Error::ImplementationSpecific("Invalid Proof".into()))? - } - - let trie = TrieDBBuilder::>::new(&db, root).build(); - let mut result = BTreeMap::new(); - - for key in keys.into_iter() { - let value = trie - .get(key.as_ref()) - .map_err(|e| Error::ImplementationSpecific(format!("Error reading from trie: {e:?}")))? - .and_then(|val| Decode::decode(&mut &val[..]).ok()); - result.insert(key.as_ref().to_vec(), value); - } - - Ok(result) -} diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml deleted file mode 100644 index e6b053467..000000000 --- a/parachain/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "ismp-parachain" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs "] - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -# crates.io -serde = { version = "1.0.136", features = ["derive"], optional = true } -codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -hex-literal = "0.4.1" -primitive-types = { version = "0.12.1", default-features = false } - -# polytope labs -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } - -# substrate -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-inherents = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } - -# cumulus -parachain-system = { package = "cumulus-pallet-parachain-system", git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } - -# local -ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } -pallet-ismp = { path = "../pallet-ismp", default-features = false } -substrate-state-machine = { path = "../pallet-ismp/primitives/state-machine", default-features = false } - -[dev-dependencies] - - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "parachain-system/std", - "cumulus-primitives-core/std", - "ismp/std", - "sp-trie/std", - "sp-consensus-aura/std", - "sp-runtime/std", - "sp-io/std", - "sp-inherents/std", - "primitive-types/std", - "ismp-primitives/std", - "pallet-ismp/std", - "serde", - "substrate-state-machine/std" -] diff --git a/parachain/inherent/Cargo.toml b/parachain/inherent/Cargo.toml deleted file mode 100644 index 06cffb4ec..000000000 --- a/parachain/inherent/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "ismp-parachain-inherent" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs "] - -[dependencies] -async-trait = { version = "0.1.63" } -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] } -anyhow = "1.0.57" - -# Substrate -sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } - -# Cumulus -cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420" } -cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420" } - -# polytope-labs -ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } -ismp-parachain = { path = "../" } -ismp-parachain-runtime-api = { path = "../runtime-api" } \ No newline at end of file diff --git a/parachain/inherent/src/lib.rs b/parachain/inherent/src/lib.rs deleted file mode 100644 index 32912b2e6..000000000 --- a/parachain/inherent/src/lib.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#![deny(missing_docs)] - -//! ISMP Parachain Consensus Inherent Provider -//! -//! This exports the inherent provider for including ISMP parachain consensus updates as block -//! inherents. - -use codec::Encode; -use cumulus_primitives_core::PersistedValidationData; -use cumulus_relay_chain_interface::{PHash, RelayChainInterface}; -use ismp::messaging::ConsensusMessage; -use ismp_parachain::consensus::{parachain_header_storage_key, ParachainConsensusProof}; -use ismp_parachain_runtime_api::IsmpParachainApi; -use sp_runtime::traits::Block as BlockT; -use std::sync::Arc; - -/// Implements [`InherentDataProvider`] for providing parachain consensus updates as inherents. -pub struct ConsensusInherentProvider(Option); - -impl ConsensusInherentProvider { - /// Create the [`ConsensusMessage`] at the given `relay_parent`. Will be [`None`] if no para ids - /// have been confguired. - pub async fn create( - client: Arc, - relay_parent: PHash, - relay_chain_interface: &impl RelayChainInterface, - validation_data: PersistedValidationData, - ) -> Result - where - C: sp_api::ProvideRuntimeApi + sp_blockchain::HeaderBackend, - C::Api: IsmpParachainApi, - B: BlockT, - { - let head = client.info().best_hash; - let para_ids = client.runtime_api().para_ids(head)?; - - if para_ids.is_empty() { - return Ok(ConsensusInherentProvider(None)) - } - - let keys = para_ids.iter().map(|id| parachain_header_storage_key(*id).0).collect(); - let storage_proof = relay_chain_interface - .prove_read(relay_parent, &keys) - .await? - .into_iter_nodes() - .collect(); - - let consensus_proof = ParachainConsensusProof { - para_ids, - relay_height: validation_data.relay_parent_number, - storage_proof, - }; - let message = ConsensusMessage { - consensus_state_id: ismp_parachain::consensus::PARACHAIN_CONSENSUS_ID, - consensus_proof: consensus_proof.encode(), - }; - - Ok(ConsensusInherentProvider(Some(message))) - } -} - -#[async_trait::async_trait] -impl sp_inherents::InherentDataProvider for ConsensusInherentProvider { - async fn provide_inherent_data( - &self, - inherent_data: &mut sp_inherents::InherentData, - ) -> Result<(), sp_inherents::Error> { - if let Some(ref message) = self.0 { - inherent_data.put_data(ismp_parachain::INHERENT_IDENTIFIER, message)?; - } - - Ok(()) - } - - async fn try_handle_error( - &self, - _: &sp_inherents::InherentIdentifier, - _: &[u8], - ) -> Option> { - None - } -} diff --git a/parachain/runtime-api/Cargo.toml b/parachain/runtime-api/Cargo.toml deleted file mode 100644 index d6aeb4430..000000000 --- a/parachain/runtime-api/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "ismp-parachain-runtime-api" -version = "0.1.0" -edition = "2021" -authors = ["Polytope Labs "] - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } - -[features] -default = ["std"] -std = ["sp-api/std"] diff --git a/parachain/runtime-api/src/lib.rs b/parachain/runtime-api/src/lib.rs deleted file mode 100644 index dfb57a03e..000000000 --- a/parachain/runtime-api/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Runtime API for parachains. - -#![cfg_attr(not(feature = "std"), no_std)] -#![deny(missing_docs)] - -extern crate alloc; - -use alloc::vec::Vec; - -sp_api::decl_runtime_apis! { - /// Ismp Parachain Runtime Apis - pub trait IsmpParachainApi { - /// Return all the para_ids this runtime is interested in. Used by the inherent provider - fn para_ids() -> Vec; - } -} diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs deleted file mode 100644 index 730ab9c49..000000000 --- a/parachain/src/consensus.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The parachain consensus client module - -use core::{marker::PhantomData, time::Duration}; - -use alloc::{boxed::Box, collections::BTreeMap, format, vec::Vec}; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use ismp::{ - consensus::{ - ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineClient, - VerifiedCommitments, - }, - error::Error, - host::{IsmpHost, StateMachine}, - messaging::StateCommitmentHeight, -}; -use ismp_primitives::ISMP_ID; -use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; -use primitive_types::H256; -use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; -use sp_runtime::{ - app_crypto::sp_core::storage::StorageKey, - generic::Header, - traits::{BlakeTwo256, Header as _}, - DigestItem, -}; -use sp_trie::StorageProof; -use substrate_state_machine::{read_proof_check, SubstrateStateMachine}; - -use crate::RelayChainOracle; - -/// The parachain consensus client implementation for ISMP. -pub struct ParachainConsensusClient(PhantomData<(T, R)>); - -impl Default for ParachainConsensusClient { - fn default() -> Self { - Self(PhantomData) - } -} - -/// Information necessary to prove the sibling parachain's finalization to this -/// parachain. -#[derive(Debug, Encode, Decode)] -pub struct ParachainConsensusProof { - /// List of para ids contained in the proof - pub para_ids: Vec, - /// Height of the relay chain for the given proof - pub relay_height: u32, - /// Storage proof for the parachain headers - pub storage_proof: Vec>, -} - -/// ConsensusClientId for [`ParachainConsensusClient`] -pub const PARACHAIN_CONSENSUS_ID: ConsensusClientId = *b"PARA"; - -/// Slot duration in milliseconds -const SLOT_DURATION: u64 = 12_000; - -impl ConsensusClient for ParachainConsensusClient -where - R: RelayChainOracle, - T: pallet_ismp::Config + super::Config, - T::BlockNumber: Into, -{ - fn verify_consensus( - &self, - host: &dyn IsmpHost, - _consensus_state_id: ConsensusStateId, - state: Vec, - proof: Vec, - ) -> Result<(Vec, VerifiedCommitments), Error> { - let update: ParachainConsensusProof = - codec::Decode::decode(&mut &proof[..]).map_err(|e| { - Error::ImplementationSpecific(format!( - "Cannot decode parachain consensus proof: {e:?}" - )) - })?; - - // first check our oracle's registry - let root = R::state_root(update.relay_height) - // not in our registry? ask parachain_system. - .or_else(|| { - let state = RelaychainDataProvider::::current_relay_chain_state(); - - if state.number == update.relay_height { - Some(state.state_root) - } else { - None - } - }) - // well, we couldn't find it - .ok_or_else(|| { - Error::ImplementationSpecific(format!( - "Cannot find relay chain height: {}", - update.relay_height - )) - })?; - - let storage_proof = StorageProof::new(update.storage_proof); - let mut intermediates = BTreeMap::new(); - - let keys = update.para_ids.iter().map(|id| parachain_header_storage_key(*id).0); - let headers = - read_proof_check::(&root, storage_proof, keys).map_err(|e| { - Error::ImplementationSpecific(format!("Error verifying parachain header {e:?}",)) - })?; - - for (key, header) in headers { - let mut state_commitments_vec = Vec::new(); - - let id = codec::Decode::decode(&mut &key[(key.len() - 4)..]).map_err(|e| { - Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) - })?; - let header = header.ok_or_else(|| { - Error::ImplementationSpecific(format!( - "Cannot find parachain header for ParaId({id})", - )) - })?; - // ideally all parachain headers are the same - let header = Header::::decode(&mut &*header).map_err(|e| { - Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) - })?; - - let (mut timestamp, mut overlay_root) = (0, H256::default()); - for digest in header.digest().logs.iter() { - match digest { - DigestItem::PreRuntime(consensus_engine_id, value) - if *consensus_engine_id == AURA_ENGINE_ID => - { - let slot = Slot::decode(&mut &value[..]).map_err(|e| { - Error::ImplementationSpecific(format!("Cannot slot: {e:?}")) - })?; - timestamp = Duration::from_millis(*slot * SLOT_DURATION).as_secs(); - } - DigestItem::Consensus(consensus_engine_id, value) - if *consensus_engine_id == ISMP_ID => - { - if value.len() != 32 { - Err(Error::ImplementationSpecific( - "Header contains an invalid ismp root".into(), - ))? - } - - overlay_root = H256::from_slice(&value); - } - // don't really care about the rest - _ => {} - }; - } - - if timestamp == 0 { - Err(Error::ImplementationSpecific("Timestamp or ismp root not found".into()))? - } - - let height: u32 = (*header.number()).into(); - - let state_id = match host.host_state_machine() { - StateMachine::Kusama(_) => StateMachine::Kusama(id), - StateMachine::Polkadot(_) => StateMachine::Polkadot(id), - _ => Err(Error::ImplementationSpecific( - "Host state machine should be a parachain".into(), - ))?, - }; - - let intermediate = StateCommitmentHeight { - commitment: StateCommitment { - timestamp, - overlay_root: Some(overlay_root), - state_root: header.state_root, - }, - height: height.into(), - }; - - state_commitments_vec.push(intermediate); - intermediates.insert(state_id, state_commitments_vec); - } - - Ok((state, intermediates)) - } - - fn verify_fraud_proof( - &self, - _host: &dyn IsmpHost, - _trusted_consensus_state: Vec, - _proof_1: Vec, - _proof_2: Vec, - ) -> Result<(), Error> { - // There are no fraud proofs for the parachain client - Ok(()) - } - - fn state_machine(&self, _id: StateMachine) -> Result, Error> { - Ok(Box::new(SubstrateStateMachine::::default())) - } -} -/// This returns the storage key for a parachain header on the relay chain. -pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { - let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec(); - let encoded_para_id = para_id.encode(); - storage_key.extend_from_slice(sp_io::hashing::twox_64(&encoded_para_id).as_slice()); - storage_key.extend_from_slice(&encoded_para_id); - StorageKey(storage_key) -} diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs deleted file mode 100644 index f3a116d25..000000000 --- a/parachain/src/lib.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! ISMP Parachain Consensus Client -//! -//! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. -#![cfg_attr(not(feature = "std"), no_std)] -#![deny(missing_docs)] - -extern crate alloc; -extern crate core; - -pub mod consensus; - -use alloc::{vec, vec::Vec}; -use cumulus_primitives_core::relay_chain; -use ismp::{handlers, messaging::CreateConsensusState}; -pub use pallet::*; -use pallet_ismp::host::Host; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use cumulus_primitives_core::relay_chain; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use ismp::{ - host::IsmpHost, - messaging::{ConsensusMessage, Message}, - }; - use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; - - #[pallet::pallet] - pub struct Pallet(_); - - /// The config trait - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_ismp::Config + parachain_system::Config - { - /// The overarching event type - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - /// Mapping of relay chain heights to it's state root. Gotten from parachain-system. - #[pallet::storage] - #[pallet::getter(fn relay_chain_state)] - pub type RelayChainState = - StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; - - /// Tracks whether we've already seen the `update_parachain_consensus` inherent - #[pallet::storage] - pub type ConsensusUpdated = StorageValue<_, bool>; - - /// List of parachains who's headers will be inserted in the `update_parachain_consensus` - /// inherent - #[pallet::storage] - pub type Parachains = StorageMap<_, Identity, u32, ()>; - - /// Events emitted by this pallet - #[pallet::event] - pub enum Event {} - - #[pallet::call] - impl Pallet { - /// Rather than users manually submitting consensus updates for sibling parachains, we - /// instead make it the responsibility of the block builder to insert the consensus - /// updates as an inherent. - #[pallet::call_index(0)] - #[pallet::weight((0, DispatchClass::Mandatory))] - pub fn update_parachain_consensus( - origin: OriginFor, - data: ConsensusMessage, - ) -> DispatchResultWithPostInfo { - ensure_none(origin)?; - assert!( - !>::exists(), - "ValidationData must be updated only once in a block", - ); - - assert_eq!( - data.consensus_state_id, - consensus::PARACHAIN_CONSENSUS_ID, - "Only parachain consensus updates should be passed in the inherents!" - ); - - pallet_ismp::Pallet::::handle_messages(vec![Message::Consensus(data)])?; - - Ok(Pays::No.into()) - } - - /// Add some new parachains to the list of parachains we care about - #[pallet::call_index(1)] - #[pallet::weight(::DbWeight::get().writes(para_ids.len() as u64))] - pub fn add_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - for id in para_ids { - Parachains::::insert(id, ()); - } - - Ok(()) - } - - /// Remove some parachains from the list of parachains we care about - #[pallet::call_index(2)] - #[pallet::weight(::DbWeight::get().writes(para_ids.len() as u64))] - pub fn remove_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - for id in para_ids { - Parachains::::remove(id); - } - - Ok(()) - } - } - - // Pallet implements [`Hooks`] trait to define some logic to execute in some context. - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: T::BlockNumber) { - let state = RelaychainDataProvider::::current_relay_chain_state(); - if !RelayChainState::::contains_key(state.number) { - RelayChainState::::insert(state.number, state.state_root); - - let digest = sp_runtime::generic::DigestItem::Consensus( - consensus::PARACHAIN_CONSENSUS_ID, - state.number.encode(), - ); - - >::deposit_log(digest); - } - } - - fn on_initialize(_n: T::BlockNumber) -> Weight { - // kill the storage, since this is the beginning of a new block. - ConsensusUpdated::::kill(); - - let host = Host::::default(); - if let Err(_) = host.consensus_state(consensus::PARACHAIN_CONSENSUS_ID) { - Pallet::::initialize(host); - } - - Weight::from_parts(0, 0) - } - } - - /// The identifier for the parachain consensus update inherent. - pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"paraismp"; - - #[pallet::inherent] - impl ProvideInherent for Pallet { - type Call = Call; - type Error = sp_inherents::MakeFatalError<()>; - const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; - - fn create_inherent(data: &InherentData) -> Option { - let data: ConsensusMessage = - data.get_data(&Self::INHERENT_IDENTIFIER).ok().flatten()?; - - Some(Call::update_parachain_consensus { data }) - } - - fn is_inherent(call: &Self::Call) -> bool { - matches!(call, Call::update_parachain_consensus { .. }) - } - } - - /// The genesis config - #[pallet::genesis_config] - pub struct GenesisConfig { - /// List of parachains to track at genesis - pub parachains: Vec, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { parachains: vec![] } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - let host = Host::::default(); - Pallet::::initialize(host); - - // insert the parachain ids - for id in &self.parachains { - Parachains::::insert(id, ()); - } - } - } -} - -impl Pallet { - /// Returns the list of parachains who's consensus updates will be inserted by the inherent - /// data provider - pub fn para_ids() -> Vec { - Parachains::::iter_keys().collect() - } - - /// Initializes the parachain consensus state. Rather than requiring a seperate - /// `create_consensus_state` call, simply including this pallet in your runtime will create the - /// ismp parachain client consensus state, either through `genesis_build` or `on_initialize`. - pub fn initialize(host: Host) { - let message = CreateConsensusState { - // insert empty bytes - consensus_state: vec![], - unbonding_period: u64::MAX, - challenge_period: 0, - consensus_state_id: consensus::PARACHAIN_CONSENSUS_ID, - consensus_client_id: consensus::PARACHAIN_CONSENSUS_ID, - state_machine_commitments: vec![], - }; - handlers::create_client(&host, message) - .expect("Failed to initialize parachain consensus client"); - } -} - -/// Interface that exposes the relay chain state roots. -pub trait RelayChainOracle { - /// Returns the state root for a given height if it exists. - fn state_root(height: relay_chain::BlockNumber) -> Option; -} - -impl RelayChainOracle for Pallet { - fn state_root(height: relay_chain::BlockNumber) -> Option { - RelayChainState::::get(height) - } -} From 69b595f36d1d77b25accac5743911401745a6948 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Mon, 4 Sep 2023 14:42:49 +0100 Subject: [PATCH 168/182] dispatch to evm (#89) * dispatch to evm * cargo fmt * add GH token to protoc step --- .github/workflows/ci.yml | 1 + ismp-demo/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5f805882..eb9f3a46a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: uses: arduino/setup-protoc@v1 with: version: '3.9.1' + repo-token: ${{ secrets.GH_TOKEN }} - name: Build run: | diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index 21e2fe070..9ecad25aa 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -29,6 +29,7 @@ use ismp::{ }; pub use pallet::*; use pallet_ismp::primitives::ModuleId; +use sp_core::H160; /// Constant Pallet ID pub const PALLET_ID: ModuleId = ModuleId::Pallet(PalletId(*b"ismp-ast")); @@ -189,6 +190,30 @@ pub mod pallet { .map_err(|_| Error::::GetDispatchFailed)?; Ok(()) } + + /// Dispatch request to a connected EVM chain. + #[pallet::weight(Weight::from_parts(1_000_000, 0))] + #[pallet::call_index(2)] + pub fn disptach_to_evm(origin: OriginFor, params: EvmParams) -> DispatchResult { + ensure_signed(origin)?; + + let post = DispatchPost { + dest: params.destination, + from: PALLET_ID.to_bytes(), + to: params.module.0.to_vec(), + timeout_timestamp: params.timeout, + data: b"Hello from polkadot".to_vec(), + gas_limit: 10_000_000, + }; + + // dispatch the request + let dispatcher = T::IsmpDispatcher::default(); + dispatcher + .dispatch_request(DispatchRequest::Post(post)) + .map_err(|_| Error::::TransferFailed)?; + + Ok(()) + } } /// Transfer payload @@ -237,6 +262,21 @@ pub mod pallet { /// Timeout timestamp on destination chain in seconds pub timeout: u64, } + + /// Extrisnic params for evm dispatch + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct EvmParams { + /// Destination module + pub module: H160, + + /// Destination parachain + pub destination: StateMachine, + + /// Timeout timestamp on destination chain in seconds + pub timeout: u64, + } } /// Module callback for the pallet From c90701e01411e7422257d0f943ab8b24da1bcc70 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 5 Sep 2023 13:17:50 +0100 Subject: [PATCH 169/182] EVM demo (#90) --- ismp-demo/src/lib.rs | 70 ++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index 9ecad25aa..ebd3e3ee8 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -20,10 +20,14 @@ extern crate alloc; -use alloc::string::ToString; +use alloc::{ + format, + string::{String, ToString}, +}; use frame_support::{traits::fungible::Mutate, PalletId}; use ismp::{ error::Error as IsmpError, + host::StateMachine, module::IsmpModule, router::{Post, Request, Response}, }; @@ -47,7 +51,7 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use ismp::{ - host::StateMachine, + host::{Ethereum, StateMachine}, router::{DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher}, }; @@ -94,6 +98,14 @@ pub mod pallet { source_chain: StateMachine, }, + /// Request data receieved + Request { + /// Source of the request + source: StateMachine, + /// utf-8 decoded data + data: String, + }, + /// Get response recieved GetResponse(Vec>>), } @@ -198,7 +210,7 @@ pub mod pallet { ensure_signed(origin)?; let post = DispatchPost { - dest: params.destination, + dest: StateMachine::Ethereum(params.destination), from: PALLET_ID.to_bytes(), to: params.module.0.to_vec(), timeout_timestamp: params.timeout, @@ -271,8 +283,8 @@ pub mod pallet { /// Destination module pub module: H160, - /// Destination parachain - pub destination: StateMachine, + /// Destination EVM host + pub destination: Ethereum, /// Timeout timestamp on destination chain in seconds pub timeout: u64, @@ -292,20 +304,40 @@ impl IsmpModule for IsmpModuleCallback { fn on_accept(&self, request: Post) -> Result<(), IsmpError> { let source_chain = request.source; - let payload = ::Balance> as codec::Decode>::decode( - &mut &*request.data, - ) - .map_err(|_| { - IsmpError::ImplementationSpecific("Failed to decode request data".to_string()) - })?; - >::mint_into(&payload.to, payload.amount.into()) - .map_err(|_| IsmpError::ImplementationSpecific("Failed to mint funds".to_string()))?; - Pallet::::deposit_event(Event::::BalanceReceived { - from: payload.from, - to: payload.to, - amount: payload.amount, - source_chain, - }); + match source_chain { + StateMachine::Ethereum(_) => Pallet::::deposit_event(Event::Request { + source: source_chain, + data: unsafe { String::from_utf8_unchecked(request.data) }, + }), + StateMachine::Polkadot(_) | StateMachine::Kusama(_) => { + let payload = + ::Balance> as codec::Decode>::decode( + &mut &*request.data, + ) + .map_err(|_| { + IsmpError::ImplementationSpecific( + "Failed to decode request data".to_string(), + ) + })?; + >::mint_into( + &payload.to, + payload.amount.into(), + ) + .map_err(|_| { + IsmpError::ImplementationSpecific("Failed to mint funds".to_string()) + })?; + Pallet::::deposit_event(Event::::BalanceReceived { + from: payload.from, + to: payload.to, + amount: payload.amount, + source_chain, + }); + } + source => { + Err(IsmpError::ImplementationSpecific(format!("Unsupported source {source:?}")))? + } + } + Ok(()) } From cb1521643639ddb555dba31468c242a5973111fc Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:18:13 +0100 Subject: [PATCH 170/182] Arkworks for signature verification (#28) * signature verification * nit * chore * fix no-std * rebase (#29) Co-authored-by: David Salami * fix verifier * remove cloning of beacon state * cleanup verifier * provide function for generating light client updates * wait for block production on el * run docker in background * fix test failures * change port in ci --------- Co-authored-by: dharjeezy --- .github/workflows/test.yml | 115 +- Cargo.lock | 2649 ++++++++++++++--- primitives/Cargo.toml | 22 +- primitives/src/consensus_types.rs | 406 +++ primitives/src/constants.rs | 138 + primitives/src/derived_types.rs | 488 --- primitives/src/domains.rs | 29 + primitives/src/helpers.rs | 162 - primitives/src/lib.rs | 10 +- primitives/src/serde.rs | 142 + primitives/src/ssz/byte_list.rs | 88 + primitives/src/ssz/byte_vector.rs | 81 + primitives/src/ssz/mod.rs | 16 + primitives/src/types.rs | 82 +- primitives/src/util.rs | 61 +- prover/Cargo.toml | 35 +- prover/src/lib.rs | 295 +- .../responses/beacon_block_header_response.rs | 2 +- prover/src/responses/beacon_block_response.rs | 16 +- prover/src/responses/beacon_state_response.rs | 16 +- .../responses/finality_checkpoint_response.rs | 2 +- prover/src/responses/validator_response.rs | 2 +- prover/src/test.rs | 401 +-- verifier/Cargo.toml | 19 +- verifier/src/error.rs | 10 +- verifier/src/lib.rs | 266 +- verifier/src/signature_verification.rs | 108 + 27 files changed, 3856 insertions(+), 1805 deletions(-) create mode 100644 primitives/src/consensus_types.rs create mode 100644 primitives/src/constants.rs delete mode 100644 primitives/src/derived_types.rs create mode 100644 primitives/src/domains.rs delete mode 100644 primitives/src/helpers.rs create mode 100644 primitives/src/serde.rs create mode 100644 primitives/src/ssz/byte_list.rs create mode 100644 primitives/src/ssz/byte_vector.rs create mode 100644 primitives/src/ssz/mod.rs create mode 100644 verifier/src/signature_verification.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1e9accf7..7d506ab68 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: test: name: Test Suite runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' +# if: github.ref == 'refs/heads/main' env: TUID: 123 steps: @@ -41,12 +41,6 @@ jobs: - name: Checkout sources uses: actions/checkout@master - - name: Clone eth-testnet-runner repository - run: | - git clone https://github.com/ralexstokes/eth-testnet-runner.git - cd eth-testnet-runner - git checkout 5f43097a03f8ff37217c843407bf7729617f2dff - - name: Install rust stable toolchain uses: actions-rs/toolchain@v1 with: @@ -57,108 +51,13 @@ jobs: sudo apt update sudo apt install protobuf-compiler - - name: Fetch geth binary - run: | - cd eth-testnet-runner - mkdir bin - wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.11.3-5ed08c47.tar.gz -O ./geth-linux-amd64-1.11.3-5ed08c47.tar.gz - tar -xvf geth-linux-amd64-1.11.3-5ed08c47.tar.gz - cd geth-linux-amd64-1.11.3-5ed08c47 - cp geth ../bin - - - name: Build lighthouse from source - run: | - cd eth-testnet-runner - git clone https://github.com/sigp/lighthouse.git - cd lighthouse - git checkout 38514c07f222ff7783834c48cf5c0a6ee7f346d0 - cargo +nightly build -p lighthouse --release - mv target/release/lighthouse ../bin - - - name: Install go - uses: actions/setup-go@v3 - with: - go-version: "stable" - - - name: check go version - run: go version - - - name: Clone eth2-val-tools repository - run: | - git clone https://github.com/protolambda/eth2-val-tools.git - cd eth2-val-tools - git checkout 4bf01453537ad1a9323c7cd99afc4f8ba2a420b1 - - - name: build eth2-val-tools and move binary to eth-testnet-runner bin folder - run: | - cd eth2-val-tools - go build - cp eth2-val-tools ../eth-testnet-runner/bin - - # Make $UID available to the justfile - - name: set UID env variable as a local justfile variable - run: | - cd eth-testnet-runner - sed -i 's/$UID/{{UID}}/' justfile - sed -i 's/^NOW := `date +%s`/NOW := `date +%s`\nUID := "${{ env.TUID }}"/' justfile - - - name: install just - run: | - cargo install just - - - name: modify testnet config values - run: | - cd eth-testnet-runner/testnet-config - sed -i 's/GENESIS_FORK_VERSION=.*/GENESIS_FORK_VERSION="0x00000000"/' values.env - sed -i 's/ALTAIR_FORK_VERSION=.*/ALTAIR_FORK_VERSION="0x01000000"/' values.env - sed -i 's/BELLATRIX_FORK_VERSION=.*/BELLATRIX_FORK_VERSION="0x02000000"/' values.env - sed -i 's/CAPELLA_FORK_VERSION=.*/CAPELLA_FORK_VERSION="0x03000000"/' values.env - sed -i 's/EIP4844_FORK_VERSION=.*/EIP4844_FORK_VERSION="0x04000000"/' values.env - - - name: remove tty flag from docker command in create-config recipe - run: | - cd eth-testnet-runner - sed -i 's/-it/-i/' justfile - - - name: run create-config - run: | - cd eth-testnet-runner - just create-config & ../scripts/wait_for_tcp_port_opening.sh localhost 8000 - - - name: set shanghaiTime - run: | - cd eth-testnet-runner/config-data/custom_config_data - sed -i 's/"shanghaiTime":.*/"shanghaiTime": 167119236847838392/' genesis.json - - - name: Set MIN_GENESIS_TIME - run: | - cd eth-testnet-runner/config-data/custom_config_data/ - sed -i 's/^MIN_GENESIS_TIME:.*/MIN_GENESIS_TIME: 1678876062/' config.yaml - - - name: run generate-keys - run: | - cd eth-testnet-runner - just generate-keys - - - name: run init-geth - run: | - cd eth-testnet-runner - just init-geth - - - name: run run-el - run: | - cd eth-testnet-runner - just run-el & ../scripts/wait_for_tcp_port_opening.sh localhost 8545 - - - name: run run-cl - run: | - cd eth-testnet-runner - just run-cl & ../scripts/wait_for_tcp_port_opening.sh localhost 5052 - - - name: run run-validator + - name: Clone eth-pos-devnet repository run: | - cd eth-testnet-runner - just run-validator & ../scripts/wait_for_tcp_port_opening.sh localhost 5062 + git clone https://github.com/polytope-labs/eth-pos-devnet.git + cd eth-pos-devnet + docker compose up & + ../scripts/wait_for_tcp_port_opening.sh localhost 3500 + ../scripts/wait_for_tcp_port_opening.sh localhost 8545 - name: Run all tests run: | diff --git a/Cargo.lock b/Cargo.lock index c7cd1afab..00e32893f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,24 +3,13 @@ version = 3 [[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn 2.0.28", -] - -[[package]] -name = "actix-rt" -version = "2.8.0" +name = "Inflector" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" dependencies = [ - "actix-macros", - "futures-core", - "tokio", + "lazy_static", + "regex", ] [[package]] @@ -38,6 +27,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.3" @@ -59,21 +59,137 @@ dependencies = [ ] [[package]] -name = "amcl" -version = "0.3.0" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] -name = "anyhow" -version = "1.0.72" +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "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", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "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 = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] [[package]] -name = "arrayref" -version = "0.3.7" +name = "array-init" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" +dependencies = [ + "nodrop", +] [[package]] name = "arrayvec" @@ -87,6 +203,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3419eecc9f5967e6f0f29a0c3fefe22bda6ea34b15798f3c452cb81f2c3fa7" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -109,6 +234,40 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -132,9 +291,9 @@ dependencies = [ [[package]] name = "base16ct" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base2" @@ -163,6 +322,27 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -205,11 +385,32 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bls_on_arkworks" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc35f5286b3fa350a0a77df5166c034c5a12dc4a3ee13bf2126ac9e9109ef8e" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "hkdf", + "hmac", + "libm", + "sha2 0.10.7", +] + [[package]] name = "bs58" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "sha2 0.10.7", + "tinyvec", +] [[package]] name = "bumpalo" @@ -234,147 +435,435 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] -name = "cc" -version = "1.0.82" +name = "bzip2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ + "bzip2-sys", "libc", ] [[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "core-foundation" -version = "0.9.3" +name = "bzip2-sys" +version = "0.1.11+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" dependencies = [ - "core-foundation-sys", + "cc", "libc", + "pkg-config", ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core2" -version = "0.4.0" +name = "camino" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ - "memchr", + "serde", ] [[package]] -name = "cpufeatures" -version = "0.2.9" +name = "cargo-platform" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" dependencies = [ - "libc", + "serde", ] [[package]] -name = "crypto-bigint" -version = "0.4.9" +name = "cargo_metadata" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +checksum = "e7daec1a2a2129eeba1644b220b4647ec537b0b5d4bfd6876fcc5a540056b592" dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "cc" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ - "generic-array", - "typenum", + "jobserver", + "libc", ] [[package]] -name = "data-encoding" -version = "2.4.0" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "der" -version = "0.6.1" +name = "chrono" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +checksum = "d87d9d13be47a5b7c3907137f1290b0459a7f80efb26be8c52afb11963bccb02" dependencies = [ - "const-oid", - "zeroize", + "num-traits", ] [[package]] -name = "digest" -version = "0.9.0" +name = "cipher" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] -name = "digest" -version = "0.10.7" +name = "coins-bip32" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", + "bs58", + "coins-core", + "digest 0.10.7", + "hmac", + "k256", + "serde", + "sha2 0.10.7", + "thiserror", ] [[package]] -name = "ecdsa" -version = "0.14.8" +name = "coins-bip39" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "bitvec", + "coins-bip32", + "hmac", + "once_cell", + "pbkdf2 0.12.2", + "rand", + "sha2 0.10.7", + "thiserror", ] [[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" +name = "coins-core" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base64 0.21.2", + "bech32", + "bs58", + "digest 0.10.7", + "generic-array", + "hex", + "ripemd", + "serde", + "serde_derive", + "sha2 0.10.7", + "sha3", + "thiserror", +] + +[[package]] +name = "const-hex" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08849ed393c907c90016652a01465a12d86361cd38ad2a7de026c56a520cc259" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", "digest 0.10.7", "ff", "generic-array", @@ -386,6 +875,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ena" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.32" @@ -396,107 +894,434 @@ dependencies = [ ] [[package]] -name = "enr" -version = "0.6.2" +name = "enr" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be7b2ac146c1f99fe245c02d16af0696450d8e06c135db75e10eeb9e642c20d" +dependencies = [ + "base64 0.21.2", + "bytes", + "hex", + "k256", + "log", + "rand", + "rlp", + "serde", + "serde-hex", + "sha3", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "eth-keystore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" +dependencies = [ + "aes", + "ctr", + "digest 0.10.7", + "hex", + "hmac", + "pbkdf2 0.11.0", + "rand", + "scrypt", + "serde", + "serde_json", + "sha2 0.10.7", + "sha3", + "thiserror", + "uuid", +] + +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "ethers" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba3fd516c15a9a587135229466dbbfc85796de55c5660afbbb1b1c78517d85c" +dependencies = [ + "ethers-addressbook", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", + "ethers-providers", + "ethers-signers", + "ethers-solc", +] + +[[package]] +name = "ethers-addressbook" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0245617f11b8178fa50b52e433e2c34ac69f39116b62c8be2437decf2edf1986" +dependencies = [ + "ethers-core", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-contract" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bb80fd2c22631a5eb8a02cbf373cc5fd86937fc966bb670b9a884580c8e71c" +dependencies = [ + "const-hex", + "ethers-contract-abigen", + "ethers-contract-derive", + "ethers-core", + "ethers-providers", + "futures-util", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ethers-contract-abigen" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22c54db0d393393e732a5b20273e4f8ab89f0cce501c84e75fab9c126799a6e6" +dependencies = [ + "Inflector", + "const-hex", + "dunce", + "ethers-core", + "ethers-etherscan", + "eyre", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "reqwest", + "serde", + "serde_json", + "syn 2.0.28", + "toml 0.7.6", + "walkdir", +] + +[[package]] +name = "ethers-contract-derive" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ee4f216184a1304b707ed258f4f70aa40bf7e1522ab8963d127a8d516eaa1a" +dependencies = [ + "Inflector", + "const-hex", + "ethers-contract-abigen", + "ethers-core", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.28", +] + +[[package]] +name = "ethers-core" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c29523f73c12753165781c6e5dc11c84d3e44c080a15f7c6cfbd70b514cb6f1" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "const-hex", + "elliptic-curve", + "ethabi", + "generic-array", + "k256", + "num_enum", + "once_cell", + "open-fastrlp", + "rand", + "rlp", + "serde", + "serde_json", + "strum", + "syn 2.0.28", + "tempfile", + "thiserror", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "ethers-etherscan" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aab5af432b3fe5b7756b60df5c9ddeb85a13414575ad8a9acd707c24f0a77a5" +dependencies = [ + "ethers-core", + "reqwest", + "semver", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "ethers-middleware" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356151d5ded56d4918146366abc9dfc9df367cf0096492a7a5477b21b7693615" +dependencies = [ + "async-trait", + "auto_impl", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "ethers-signers", + "futures-channel", + "futures-locks", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "ethers-providers" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c84664b294e47fc2860d6db0db0246f79c4c724e552549631bb9505b834bee" +dependencies = [ + "async-trait", + "auto_impl", + "base64 0.21.2", + "bytes", + "const-hex", + "enr", + "ethers-core", + "futures-channel", + "futures-core", + "futures-timer", + "futures-util", + "hashers", + "http", + "instant", + "jsonwebtoken", + "once_cell", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "ws_stream_wasm", +] + +[[package]] +name = "ethers-signers" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fa0a0be8915790626d5759eb51fe47435a8eac92c2f212bd2da9aa7f30ea56" +checksum = "170b299698702ef1f53d2275af7d6d97409cfa4f9398ee9ff518f6bc9102d0ad" dependencies = [ - "base64 0.13.1", - "bs58", - "bytes", - "hex", - "k256", - "log", + "async-trait", + "coins-bip32", + "coins-bip39", + "const-hex", + "elliptic-curve", + "eth-keystore", + "ethers-core", "rand", - "rlp", - "serde", - "sha3", - "zeroize", + "sha2 0.10.7", + "thiserror", + "tracing", ] [[package]] -name = "env_logger" -version = "0.10.0" +name = "ethers-solc" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "66559c8f774712df303c907d087275a52a2046b256791aaa566d5abc8ea66731" dependencies = [ - "humantime", - "is-terminal", - "log", + "cfg-if", + "const-hex", + "dirs", + "dunce", + "ethers-core", + "glob", + "home", + "md-5", + "num_cpus", + "once_cell", + "path-slash", + "rayon", "regex", - "termcolor", + "semver", + "serde", + "serde_json", + "solang-parser", + "svm-rs", + "thiserror", + "tiny-keccak", + "tokio", + "tracing", + "walkdir", + "yansi", ] [[package]] -name = "errno" -version = "0.3.2" +name = "eyre" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", + "indenter", + "once_cell", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "fastrand" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] -name = "error-chain" -version = "0.12.4" +name = "ff" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "backtrace", - "version_check", + "rand_core", + "subtle", ] [[package]] -name = "ethereum-consensus" -version = "0.1.1" -source = "git+https://github.com/polytope-labs/ethereum-consensus?branch=main#48335b5c8074d63553ee4681993e294eba947f88" +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ - "async-stream", - "bs58", - "enr", - "error-chain", - "hashbrown 0.13.2", - "hex", - "integer-sqrt", - "milagro_bls", - "multiaddr", - "multihash", + "byteorder", "rand", - "serde", - "serde_json", - "serde_yaml", - "sha2 0.9.9", - "ssz-rs", - "tokio", - "tokio-stream", + "rustc-hex", + "static_assertions", ] [[package]] -name = "fastrand" -version = "2.0.0" +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] -name = "ff" -version = "0.12.1" +name = "flate2" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ - "rand_core", - "subtle", + "crc32fast", + "miniz_oxide", ] [[package]] @@ -529,12 +1354,37 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -542,6 +1392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -550,6 +1401,44 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-locks" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" +dependencies = [ + "futures-channel", + "futures-task", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -562,16 +1451,41 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] + [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", ] [[package]] @@ -582,6 +1496,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -601,11 +1516,29 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "group" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core", @@ -624,7 +1557,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -646,6 +1579,27 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "hashers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +dependencies = [ + "fxhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.2" @@ -664,6 +1618,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -673,6 +1636,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "http" version = "0.2.9" @@ -737,6 +1709,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -760,41 +1746,93 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "equivalent", + "hashbrown 0.14.0", ] [[package]] -name = "indexmap" -version = "1.9.3" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "generic-array", ] [[package]] -name = "int" -version = "0.3.0" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "num-traits", + "cfg-if", ] [[package]] -name = "integer-sqrt" -version = "0.1.5" +name = "int" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" dependencies = [ "num-traits", ] @@ -831,6 +1869,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -840,16 +1887,32 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.2", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" -version = "0.11.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "once_cell", "sha2 0.10.7", + "signature", ] [[package]] @@ -861,6 +1924,34 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lalrpop" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" +dependencies = [ + "ascii-canvas", + "bit-set", + "diff", + "ena", + "is-terminal", + "itertools", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" + [[package]] name = "lazy_static" version = "1.4.0" @@ -874,10 +1965,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libm" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "linux-raw-sys" @@ -901,6 +1992,21 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.5.0" @@ -908,15 +2014,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "milagro_bls" -version = "1.5.1" -source = "git+https://github.com/sigp/milagro_bls#d3fc0a40cfe8b72ccda46ba050ee6786a59ce753" +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ - "amcl", - "hex", - "lazy_static", - "rand", - "zeroize", + "autocfg", ] [[package]] @@ -945,51 +2048,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "multiaddr" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" -dependencies = [ - "arrayref", - "bs58", - "byteorder", - "data-encoding", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - -[[package]] -name = "multihash" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" -dependencies = [ - "core2", - "digest 0.10.7", - "multihash-derive", - "sha2 0.10.7", - "unsigned-varint", -] - -[[package]] -name = "multihash-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" -dependencies = [ - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - [[package]] name = "native-tls" version = "0.2.11" @@ -1008,6 +2066,18 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "num-bigint" version = "0.4.3" @@ -1048,6 +2118,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "object" version = "0.31.1" @@ -1069,6 +2160,31 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", + "ethereum-types", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "openssl" version = "0.10.56" @@ -1102,71 +2218,222 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] -name = "openssl-sys" -version = "0.9.91" +name = "openssl-sys" +version = "0.9.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parity-scale-codec" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec 1.11.0", + "windows-targets", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "path-slash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", + "hmac", + "password-hash", + "sha2 0.10.7", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.0.0", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version", +] + +[[package]] +name = "phf" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "phf_macros", + "phf_shared 0.11.2", ] [[package]] -name = "parity-scale-codec" -version = "3.6.4" +name = "phf_generator" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", + "phf_shared 0.11.2", + "rand", ] [[package]] -name = "parity-scale-codec-derive" -version = "3.6.4" +name = "phf_macros" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "proc-macro-crate", + "phf_generator", + "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "phf_shared" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "lock_api", - "parking_lot_core", + "siphasher", ] [[package]] -name = "parking_lot_core" -version = "0.9.8" +name = "phf_shared" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", + "siphasher", ] [[package]] -name = "percent-encoding" -version = "2.3.0" +name = "pin-project" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] [[package]] name = "pin-project-lite" @@ -1182,9 +2449,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", @@ -1202,6 +2469,36 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +dependencies = [ + "proc-macro2", + "syn 2.0.28", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", +] + [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -1209,7 +2506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", - "toml", + "toml 0.5.11", ] [[package]] @@ -1290,6 +2587,37 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1299,6 +2627,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.9.3" @@ -1330,9 +2669,9 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ "base64 0.21.2", "bytes", @@ -1343,6 +2682,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -1352,28 +2692,55 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.25.2", "winreg", ] [[package]] name = "rfc6979" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "crypto-bigint", "hmac", - "zeroize", + "subtle", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", ] [[package]] @@ -1383,9 +2750,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", + "rlp-derive", "rustc-hex", ] +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1398,6 +2777,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.8" @@ -1411,12 +2799,101 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.4", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-info" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "schannel" version = "0.1.22" @@ -1432,11 +2909,33 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +dependencies = [ + "hmac", + "pbkdf2 0.11.0", + "salsa20", + "sha2 0.10.7", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sec1" -version = "0.3.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -1469,20 +2968,52 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" -version = "1.0.183" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-hex" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" +dependencies = [ + "array-init", + "serde", + "smallvec 0.6.14", +] + [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", @@ -1500,6 +3031,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1513,15 +3053,14 @@ dependencies = [ ] [[package]] -name = "serde_yaml" -version = "0.8.26" +name = "sha1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", + "cfg-if", + "cpufeatures", + "digest 0.10.7", ] [[package]] @@ -1559,31 +3098,49 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "signature" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ - "libc", + "digest 0.10.7", + "rand_core", ] [[package]] -name = "signature" -version = "1.6.4" +name = "simple_asn1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "digest 0.10.7", - "rand_core", + "num-bigint", + "num-traits", + "thiserror", + "time", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" dependencies = [ - "autocfg", + "maybe-uninit", ] [[package]] @@ -1612,11 +3169,31 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "solang-parser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c792fe9fae2a2f716846f214ca10d5a1e21133e0bf36cef34bcc4a852467b21" +dependencies = [ + "itertools", + "lalrpop", + "lalrpop-util", + "phf", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spki" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -1625,13 +3202,14 @@ dependencies = [ [[package]] name = "ssz-rs" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#96f9a89ccc56ab2abb4c3a83eaedb415034ada49" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#81e9f63c93ca33f5f484ac301553f04912f2de23" dependencies = [ "as-any", "bitvec", "hex", "itertools", "num-bigint", + "parity-scale-codec", "serde", "sha2 0.9.9", "ssz-rs-derive", @@ -1640,7 +3218,7 @@ dependencies = [ [[package]] name = "ssz-rs-derive" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#96f9a89ccc56ab2abb4c3a83eaedb415034ada49" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#81e9f63c93ca33f5f484ac301553f04912f2de23" dependencies = [ "proc-macro2", "quote", @@ -1653,12 +3231,67 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.28", +] + [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "svm-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597e3a746727984cb7ea2487b6a40726cad0dbe86628e7d429aa6b8c4c153db4" +dependencies = [ + "dirs", + "fs2", + "hex", + "once_cell", + "reqwest", + "semver", + "serde", + "serde_json", + "sha2 0.10.7", + "thiserror", + "url", + "zip", +] + [[package]] name = "syn" version = "1.0.109" @@ -1685,9 +3318,15 @@ dependencies = [ name = "sync-committee-primitives" version = "0.1.0" dependencies = [ - "ethereum-consensus", + "anyhow", + "ark-bls12-381", + "ark-ec", + "bls_on_arkworks", + "hex", "hex-literal", "parity-scale-codec", + "primitive-types", + "serde", "ssz-rs", ] @@ -1695,13 +3334,17 @@ dependencies = [ name = "sync-committee-prover" version = "0.1.0" dependencies = [ - "actix-rt", "anyhow", + "ark-bls12-381", + "ark-ec", "async-stream", "base2", + "bls_on_arkworks", "env_logger", - "ethereum-consensus", + "ethers", "hex", + "log", + "primitive-types", "reqwest", "serde", "serde_json", @@ -1716,24 +3359,16 @@ dependencies = [ name = "sync-committee-verifier" version = "0.1.0" dependencies = [ - "ethereum-consensus", + "anyhow", + "ark-bls12-381", + "ark-ec", + "bls_on_arkworks", + "hex", "log", "ssz-rs", "sync-committee-primitives", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "tap" version = "1.0.1" @@ -1742,17 +3377,28 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.7.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1782,6 +3428,43 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "time" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1799,18 +3482,16 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.31.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2 0.5.3", "tokio-macros", "windows-sys", @@ -1837,6 +3518,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -1848,6 +3539,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" +dependencies = [ + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.23.1", +] + [[package]] name = "tokio-util" version = "0.7.8" @@ -1871,6 +3577,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -1885,9 +3625,21 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "tracing-core" version = "0.1.31" @@ -1897,18 +3649,60 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "try-lock" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tungstenite" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1937,10 +3731,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] -name = "unsigned-varint" +name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" @@ -1953,6 +3747,22 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -1965,6 +3775,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2056,6 +3876,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" +dependencies = [ + "rustls-webpki 0.100.2", +] + +[[package]] +name = "webpki-roots" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + [[package]] name = "winapi" version = "0.3.9" @@ -2153,13 +3988,42 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys", +] + +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version", + "send_wrapper 0.6.0", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] @@ -2172,16 +4036,77 @@ dependencies = [ ] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "yansi" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2 0.11.0", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index b5af4614e..f80ade770 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -6,17 +6,31 @@ authors = ["Polytope Labs"] [dependencies] -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false } hex-literal = { package = "hex-literal", version = "0.4.1", default-features = false } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive" ] } +primitive-types = { version = "0.12.1", default-features = false, features = ["serde_no_std", "impl-codec"] } +serde = { version = "1.0.185", optional = true, features = ["derive"] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +anyhow = {version = "1.0.75", default-features = false} +ark-ec = { version = "0.4.2", default-features = false } +ark-bls12-381 = { version = "0.4.0", default-features = false } +bls_on_arkworks = { version = "0.2.2", default-features = false } [features] default = ["std"] std = [ - "ssz-rs/std", - 'codec/std' + "ssz-rs/default", + 'codec/std', + "primitive-types/std", + "anyhow/std", + "ark-ec/std", + "bls_on_arkworks/std", + "ark-bls12-381/std", + "primitive-types/std", + "serde" ] -testing = [] +mainnet = [] +testnet = [] diff --git a/primitives/src/consensus_types.rs b/primitives/src/consensus_types.rs new file mode 100644 index 000000000..52d4af5f1 --- /dev/null +++ b/primitives/src/consensus_types.rs @@ -0,0 +1,406 @@ +use crate::{ + constants::{ + BlsPublicKey, BlsSignature, Bytes32, Epoch, ExecutionAddress, Gwei, Hash32, + ParticipationFlags, Root, Slot, ValidatorIndex, Version, WithdrawalIndex, + DEPOSIT_PROOF_LENGTH, JUSTIFICATION_BITS_LENGTH, + }, + ssz::{ByteList, ByteVector}, +}; +use alloc::{vec, vec::Vec}; +use ssz_rs::{prelude::*, Deserialize, List, Vector}; + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconBlockHeader { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub slot: u64, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub proposer_index: u64, + pub parent_root: Root, + pub state_root: Root, + pub body_root: Root, +} + +#[derive(Default, Clone, Debug, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Checkpoint { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub epoch: u64, + pub root: Root, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Eth1Data { + pub deposit_root: Root, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub deposit_count: u64, + pub block_hash: Hash32, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Validator { + #[cfg_attr(feature = "std", serde(rename = "pubkey"))] + pub public_key: BlsPublicKey, + pub withdrawal_credentials: Bytes32, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub effective_balance: Gwei, + pub slashed: bool, + // Status epochs + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub activation_eligibility_epoch: Epoch, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub activation_epoch: Epoch, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub exit_epoch: Epoch, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub withdrawable_epoch: Epoch, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ProposerSlashing { + pub signed_header_1: SignedBeaconBlockHeader, + pub signed_header_2: SignedBeaconBlockHeader, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedBeaconBlockHeader { + pub message: BeaconBlockHeader, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct IndexedAttestation { + #[cfg_attr(feature = "std", serde(with = "crate::serde::collection_over_string"))] + pub attesting_indices: List, + pub data: AttestationData, + pub signature: BlsSignature, +} + +#[derive(Default, Clone, Debug, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct AttestationData { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub slot: u64, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub index: u64, + pub beacon_block_root: Root, + pub source: Checkpoint, + pub target: Checkpoint, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct AttesterSlashing { + pub attestation_1: IndexedAttestation, + pub attestation_2: IndexedAttestation, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Attestation { + pub aggregation_bits: Bitlist, + pub data: AttestationData, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Deposit { + pub proof: Vector, + pub data: DepositData, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, codec::Encode, codec::Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct DepositData { + #[cfg_attr(feature = "std", serde(rename = "pubkey"))] + pub public_key: BlsPublicKey, + pub withdrawal_credentials: Hash32, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub amount: u64, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct VoluntaryExit { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub epoch: u64, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub validator_index: u64, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedVoluntaryExit { + pub message: VoluntaryExit, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, codec::Encode, codec::Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SyncAggregate { + pub sync_committee_bits: Bitvector, + pub sync_committee_signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SyncCommittee { + #[cfg_attr(feature = "std", serde(rename = "pubkeys"))] + pub public_keys: Vector, + #[cfg_attr(feature = "std", serde(rename = "aggregate_pubkey"))] + pub aggregate_public_key: BlsPublicKey, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Withdrawal { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub index: WithdrawalIndex, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub validator_index: ValidatorIndex, + pub address: ExecutionAddress, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub amount: Gwei, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BlsToExecutionChange { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub validator_index: ValidatorIndex, + #[cfg_attr(feature = "serde", serde(rename = "from_bls_pubkey"))] + pub from_bls_public_key: BlsPublicKey, + pub to_execution_address: ExecutionAddress, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedBlsToExecutionChange { + message: BlsToExecutionChange, + signature: BlsSignature, +} + +pub type Transaction = ByteList; + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ExecutionPayload< + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, + const MAX_WITHDRAWALS_PER_PAYLOAD: usize, +> { + pub parent_hash: Hash32, + pub fee_recipient: ExecutionAddress, + pub state_root: Bytes32, + pub receipts_root: Bytes32, + pub logs_bloom: ByteVector, + pub prev_randao: Bytes32, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub block_number: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_limit: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_used: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub timestamp: u64, + pub extra_data: ByteList, + pub base_fee_per_gas: U256, + pub block_hash: Hash32, + pub transactions: List, MAX_TRANSACTIONS_PER_PAYLOAD>, + pub withdrawals: List, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ExecutionPayloadHeader< + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, +> { + pub parent_hash: Hash32, + pub fee_recipient: ExecutionAddress, + pub state_root: Bytes32, + pub receipts_root: Bytes32, + pub logs_bloom: ByteVector, + pub prev_randao: Bytes32, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub block_number: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_limit: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_used: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub timestamp: u64, + pub extra_data: ByteList, + pub base_fee_per_gas: U256, + pub block_hash: Hash32, + pub transactions_root: Root, + pub withdrawals_root: Root, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconBlockBody< + const MAX_PROPOSER_SLASHINGS: usize, + const MAX_VALIDATORS_PER_COMMITTEE: usize, + const MAX_ATTESTER_SLASHINGS: usize, + const MAX_ATTESTATIONS: usize, + const MAX_DEPOSITS: usize, + const MAX_VOLUNTARY_EXITS: usize, + const SYNC_COMMITTEE_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, + const MAX_WITHDRAWALS_PER_PAYLOAD: usize, + const MAX_BLS_TO_EXECUTION_CHANGES: usize, +> { + pub randao_reveal: BlsSignature, + pub eth1_data: Eth1Data, + pub graffiti: Bytes32, + pub proposer_slashings: List, + pub attester_slashings: + List, MAX_ATTESTER_SLASHINGS>, + pub attestations: List, MAX_ATTESTATIONS>, + pub deposits: List, + pub voluntary_exits: List, + pub sync_aggregate: SyncAggregate, + pub execution_payload: ExecutionPayload< + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + >, + pub bls_to_execution_changes: List, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconBlock< + const MAX_PROPOSER_SLASHINGS: usize, + const MAX_VALIDATORS_PER_COMMITTEE: usize, + const MAX_ATTESTER_SLASHINGS: usize, + const MAX_ATTESTATIONS: usize, + const MAX_DEPOSITS: usize, + const MAX_VOLUNTARY_EXITS: usize, + const SYNC_COMMITTEE_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, + const MAX_WITHDRAWALS_PER_PAYLOAD: usize, + const MAX_BLS_TO_EXECUTION_CHANGES: usize, +> { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub slot: Slot, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub proposer_index: ValidatorIndex, + pub parent_root: Root, + pub state_root: Root, + pub body: BeaconBlockBody< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, + >, +} +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Fork { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_hex"))] + pub previous_version: Version, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_hex"))] + pub current_version: Version, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub epoch: Epoch, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ForkData { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_hex"))] + pub current_version: Version, + pub genesis_validators_root: Root, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct HistoricalSummary { + pub block_summary_root: Root, + pub state_summary_root: Root, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconState< + const SLOTS_PER_HISTORICAL_ROOT: usize, + const HISTORICAL_ROOTS_LIMIT: usize, + const ETH1_DATA_VOTES_BOUND: usize, + const VALIDATOR_REGISTRY_LIMIT: usize, + const EPOCHS_PER_HISTORICAL_VECTOR: usize, + const EPOCHS_PER_SLASHINGS_VECTOR: usize, + const MAX_VALIDATORS_PER_COMMITTEE: usize, + const SYNC_COMMITTEE_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, +> { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub genesis_time: u64, + pub genesis_validators_root: Root, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub slot: Slot, + pub fork: Fork, + pub latest_block_header: BeaconBlockHeader, + pub block_roots: Vector, + pub state_roots: Vector, + pub historical_roots: List, + pub eth1_data: Eth1Data, + pub eth1_data_votes: List, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub eth1_deposit_index: u64, + pub validators: List, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub balances: List, + pub randao_mixes: Vector, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub slashings: Vector, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub previous_epoch_participation: List, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub current_epoch_participation: List, + pub justification_bits: Bitvector, + pub previous_justified_checkpoint: Checkpoint, + pub current_justified_checkpoint: Checkpoint, + pub finalized_checkpoint: Checkpoint, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub inactivity_scores: List, + pub current_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, + pub latest_execution_payload_header: + ExecutionPayloadHeader, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub next_withdrawal_index: WithdrawalIndex, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub next_withdrawal_validator_index: ValidatorIndex, + pub historical_summaries: List, +} diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs new file mode 100644 index 000000000..df7a0247e --- /dev/null +++ b/primitives/src/constants.rs @@ -0,0 +1,138 @@ +use crate::domains::DomainType; +use ssz_rs::Node; + +pub type BlsPublicKey = ByteVector; +pub type BlsSignature = ByteVector; + +pub type Epoch = u64; +pub type Slot = u64; +pub type Root = Node; +pub type ParticipationFlags = u8; + +pub type CommitteeIndex = u64; +pub type ValidatorIndex = u64; +pub type WithdrawalIndex = u64; +pub type Gwei = u64; +pub type Hash32 = Bytes32; + +pub type Version = [u8; 4]; +pub type ForkDigest = [u8; 4]; +pub type Domain = [u8; 32]; + +pub type ExecutionAddress = ByteVector<20>; + +pub type ChainId = usize; +pub type NetworkId = usize; + +pub type RandaoReveal = BlsSignature; +pub type Bytes32 = ByteVector<32>; + +pub const BLS_PUBLIC_KEY_BYTES_LEN: usize = 48; +pub const BLS_SECRET_KEY_BYTES_LEN: usize = 32; +pub const BLS_SIGNATURE_BYTES_LEN: usize = 96; + +pub const SYNC_COMMITTEE_SIZE: usize = 512; +pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: Epoch = 256; + +pub const MAX_VALIDATORS_PER_COMMITTEE: usize = 2048; +pub const EPOCHS_PER_ETH1_VOTING_PERIOD: Epoch = 64; +pub const SLOTS_PER_HISTORICAL_ROOT: usize = 8192; +pub const EPOCHS_PER_HISTORICAL_VECTOR: usize = 65536; +pub const EPOCHS_PER_SLASHINGS_VECTOR: usize = 8192; +pub const HISTORICAL_ROOTS_LIMIT: usize = 16_777_216; +pub const VALIDATOR_REGISTRY_LIMIT: usize = 2usize.saturating_pow(40); +pub const MAX_PROPOSER_SLASHINGS: usize = 16; +pub const MAX_ATTESTER_SLASHINGS: usize = 2; +pub const MAX_ATTESTATIONS: usize = 128; +pub const MAX_DEPOSITS: usize = 16; +pub const MAX_VOLUNTARY_EXITS: usize = 16; +pub const JUSTIFICATION_BITS_LENGTH: usize = 4; + +pub const MAX_BYTES_PER_TRANSACTION: usize = 1_073_741_824; +pub const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 1_048_576; +pub const BYTES_PER_LOGS_BLOOM: usize = 256; +pub const MAX_EXTRA_DATA_BYTES: usize = 32; + +pub const DEPOSIT_PROOF_LENGTH: usize = 33; + +pub const ETH1_DATA_VOTES_BOUND: usize = (EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH) as usize; +pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; +pub const FINALIZED_ROOT_INDEX: u64 = 52; +pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; +pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; +pub const EXECUTION_PAYLOAD_INDEX: u64 = 56; +pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; +pub const BLOCK_ROOTS_INDEX: u64 = 37; +pub const HISTORICAL_ROOTS_INDEX: u64 = 39; +pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; +pub const EXECUTION_PAYLOAD_TIMESTAMP_INDEX: u64 = 25; +pub const FINALIZED_ROOT_INDEX_LOG2: u64 = 5; +pub const EXECUTION_PAYLOAD_INDEX_LOG2: u64 = 5; +pub const NEXT_SYNC_COMMITTEE_INDEX_LOG2: u64 = 5; +pub const BLOCK_ROOTS_INDEX_LOG2: u64 = 5; +pub const HISTORICAL_ROOTS_INDEX_LOG2: u64 = 5; + +#[cfg(feature = "testnet")] +pub use testnet::*; + +#[cfg(feature = "mainnet")] +pub use mainnet::*; + +use crate::ssz::ByteVector; +#[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] +pub use devnet::*; + +#[cfg(feature = "testnet")] +pub mod testnet { + use super::*; + pub const SLOTS_PER_EPOCH: Slot = 32; + pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); + pub const BELLATRIX_FORK_VERSION: Version = [3, 0, 16, 32]; + pub const ALTAIR_FORK_VERSION: Version = [1, 0, 16, 32]; + pub const GENESIS_FORK_VERSION: Version = [0; 4]; + pub const ALTAIR_FORK_EPOCH: Epoch = 36660; + pub const BELLATRIX_FORK_EPOCH: Epoch = 112260; + pub const CAPELLA_FORK_EPOCH: Epoch = u64::MAX; + pub const CAPELLA_FORK_VERSION: Version = [3, 0, 16, 32]; + pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; + pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; + pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; +} + +#[cfg(feature = "mainnet")] +pub mod mainnet { + use super::*; + pub const SLOTS_PER_EPOCH: Slot = 32; + pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); + pub const BELLATRIX_FORK_VERSION: Version = [2, 0, 0, 0]; + pub const ALTAIR_FORK_VERSION: Version = [1, 0, 0, 0]; + pub const GENESIS_FORK_VERSION: Version = [0, 0, 0, 0]; + pub const ALTAIR_FORK_EPOCH: Epoch = 74240; + pub const BELLATRIX_FORK_EPOCH: Epoch = 144896; + pub const CAPELLA_FORK_EPOCH: Epoch = u64::MAX; + pub const CAPELLA_FORK_VERSION: Version = [3, 0, 0, 0]; + pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; + pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; + pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; +} + +#[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] +pub mod devnet { + use super::*; + use hex_literal::hex; + pub const SLOTS_PER_EPOCH: Slot = 6; + pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("83431ec7fcf92cfc44947fc0418e831c25e1d0806590231c439830db7ad54fda"); + pub const BELLATRIX_FORK_VERSION: Version = hex!("52525502"); + pub const ALTAIR_FORK_VERSION: Version = hex!("52525501"); + pub const GENESIS_FORK_VERSION: Version = hex!("52525500"); + pub const ALTAIR_FORK_EPOCH: Epoch = 0; + pub const BELLATRIX_FORK_EPOCH: Epoch = 0; + pub const CAPELLA_FORK_EPOCH: Epoch = 2; + pub const CAPELLA_FORK_VERSION: Version = hex!("52525503"); + pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; + pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; + pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; +} diff --git a/primitives/src/derived_types.rs b/primitives/src/derived_types.rs deleted file mode 100644 index 3610e22d2..000000000 --- a/primitives/src/derived_types.rs +++ /dev/null @@ -1,488 +0,0 @@ -use crate::{ - error::Error, - helpers::{ - to_codec_light_client_state, to_codec_light_client_update, to_no_codec_beacon_header, - to_no_codec_light_client_state, to_no_codec_light_client_update, - to_no_codec_sync_committee, - }, - types, -}; -use alloc::vec::Vec; -use codec::{Decode, Encode}; -use ethereum_consensus::{bellatrix, primitives::Hash32}; - -/// Minimum state required by the light client to validate new sync committee attestations -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] -pub struct LightClientState { - /// The latest recorded finalized header - pub finalized_header: BeaconBlockHeader, - /// Latest finalized epoch - pub latest_finalized_epoch: u64, - // Sync committees corresponding to the finalized header - pub current_sync_committee: SyncCommittee, - pub next_sync_committee: SyncCommittee, -} - -impl TryFrom> - for LightClientState -{ - type Error = Error; - fn try_from(state: types::LightClientState) -> Result { - to_codec_light_client_state(state) - } -} - -impl TryFrom - for types::LightClientState -{ - type Error = Error; - fn try_from(state: LightClientState) -> Result { - to_no_codec_light_client_state(state) - } -} - -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] -pub struct BeaconBlockHeader { - pub slot: u64, - pub proposer_index: u64, - pub parent_root: [u8; 32], - pub state_root: [u8; 32], - pub body_root: [u8; 32], -} - -impl TryFrom for BeaconBlockHeader { - type Error = Error; - - fn try_from(beacon_block_header: bellatrix::BeaconBlockHeader) -> Result { - Ok(BeaconBlockHeader { - slot: beacon_block_header.slot, - proposer_index: beacon_block_header.proposer_index as u64, - parent_root: beacon_block_header - .parent_root - .as_bytes() - .try_into() - .map_err(|_| Error::InvalidNodeBytes)?, - state_root: beacon_block_header - .state_root - .as_bytes() - .try_into() - .map_err(|_| Error::InvalidNodeBytes)?, - body_root: beacon_block_header - .body_root - .as_bytes() - .try_into() - .map_err(|_| Error::InvalidNodeBytes)?, - }) - } -} - -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] -pub struct SyncCommittee { - pub public_keys: Vec>, - pub aggregate_public_key: Vec, -} - -impl TryFrom> - for SyncCommittee -{ - type Error = Error; - - fn try_from( - sync_committee: bellatrix::SyncCommittee, - ) -> Result { - Ok(SyncCommittee { - public_keys: sync_committee - .public_keys - .iter() - .map(|public_key| public_key.to_vec()) - .collect(), - aggregate_public_key: sync_committee.aggregate_public_key.to_vec(), - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] -pub struct LightClientUpdate { - /// the header that the sync committee signed - pub attested_header: BeaconBlockHeader, - /// the sync committee has potentially changed, here's an ssz proof for that. - pub sync_committee_update: Option, - /// the actual header which was finalized by the ethereum attestation protocol. - pub finalized_header: BeaconBlockHeader, - /// execution payload of the finalized header - pub execution_payload: ExecutionPayloadProof, - /// Finalized header proof - pub finality_proof: FinalityProof, - /// signature & participation bits - pub sync_aggregate: SyncAggregate, - /// slot at which signature was produced - pub signature_slot: u64, - /// ancestors of the finalized block to be verified, may be empty. - pub ancestor_blocks: Vec, -} - -impl TryFrom> - for LightClientUpdate -{ - type Error = Error; - fn try_from( - update: types::LightClientUpdate, - ) -> Result { - to_codec_light_client_update(update) - } -} - -impl TryFrom - for types::LightClientUpdate -{ - type Error = Error; - fn try_from(derived_update: LightClientUpdate) -> Result { - to_no_codec_light_client_update(derived_update) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] -pub struct SyncCommitteeUpdate { - // actual sync committee - pub next_sync_committee: SyncCommittee, - // sync committee, ssz merkle proof. - pub next_sync_committee_branch: Vec>, -} - -impl TryFrom> - for SyncCommitteeUpdate -{ - type Error = Error; - - fn try_from( - sync_committee_update: types::SyncCommitteeUpdate, - ) -> Result { - Ok(SyncCommitteeUpdate { - next_sync_committee: sync_committee_update.next_sync_committee.try_into()?, - next_sync_committee_branch: sync_committee_update - .next_sync_committee_branch - .iter() - .map(|hash| hash.to_vec()) - .collect(), - }) - } -} - -impl TryFrom - for types::SyncCommitteeUpdate -{ - type Error = Error; - - fn try_from(sync_committee_update: SyncCommitteeUpdate) -> Result { - let next_sync_committee = - to_no_codec_sync_committee(sync_committee_update.next_sync_committee)?; - Ok(types::SyncCommitteeUpdate { - next_sync_committee, - next_sync_committee_branch: sync_committee_update - .next_sync_committee_branch - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, Error>>()?, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] -pub struct ExecutionPayloadProof { - /// The state root in the `ExecutionPayload` which represents the commitment to - /// the ethereum world state in the yellow paper. - pub state_root: Vec, - /// the block number of the execution header. - pub block_number: u64, - /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. - pub multi_proof: Vec>, - /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. - pub execution_payload_branch: Vec>, - /// timestamp - pub timestamp: u64, -} - -impl TryFrom for ExecutionPayloadProof { - type Error = Error; - fn try_from( - execution_payload_proof: types::ExecutionPayloadProof, - ) -> Result { - Ok(ExecutionPayloadProof { - state_root: execution_payload_proof.state_root.to_vec(), - block_number: execution_payload_proof.block_number, - multi_proof: execution_payload_proof - .multi_proof - .iter() - .map(|proof| proof.to_vec()) - .collect(), - execution_payload_branch: execution_payload_proof - .execution_payload_branch - .iter() - .map(|branch| branch.to_vec()) - .collect(), - timestamp: execution_payload_proof.timestamp, - }) - } -} - -impl TryFrom for types::ExecutionPayloadProof { - type Error = Error; - fn try_from( - derived_execution_payload_proof: ExecutionPayloadProof, - ) -> Result { - let multi_proof = derived_execution_payload_proof - .multi_proof - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?; - - let execution_payload_branch = derived_execution_payload_proof - .execution_payload_branch - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?; - - Ok(types::ExecutionPayloadProof { - state_root: Hash32::try_from(derived_execution_payload_proof.state_root.as_slice()) - .map_err(|_| Error::InvalidRoot)?, - block_number: derived_execution_payload_proof.block_number, - multi_proof, - execution_payload_branch, - timestamp: derived_execution_payload_proof.timestamp, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] -pub struct FinalityProof { - /// The latest finalized epoch - pub epoch: u64, - /// Finalized header proof - pub finality_branch: Vec>, -} - -impl TryFrom for FinalityProof { - type Error = Error; - fn try_from(finality_proof: types::FinalityProof) -> Result { - Ok(FinalityProof { - epoch: finality_proof.epoch, - finality_branch: finality_proof - .finality_branch - .iter() - .map(|branch| branch.to_vec()) - .collect(), - }) - } -} - -impl TryFrom for types::FinalityProof { - type Error = Error; - fn try_from(derived_finality_proof: FinalityProof) -> Result { - Ok(types::FinalityProof { - epoch: derived_finality_proof.epoch, - finality_branch: derived_finality_proof - .finality_branch - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)] -pub struct SyncAggregate { - pub sync_committee_bits: Vec, - pub sync_committee_signature: Vec, -} - -impl TryFrom> - for SyncAggregate -{ - type Error = Error; - fn try_from( - sync_aggregate: bellatrix::SyncAggregate, - ) -> Result { - Ok(SyncAggregate { - sync_committee_bits: sync_aggregate.sync_committee_bits.clone().to_bitvec().into_vec(), - sync_committee_signature: sync_aggregate.sync_committee_signature.clone().to_vec(), - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] -pub struct AncestorBlock { - /// The actual beacon chain header - pub header: BeaconBlockHeader, - /// Associated execution header proofs - pub execution_payload: ExecutionPayloadProof, - /// Ancestry proofs of the beacon chain header. - pub ancestry_proof: AncestryProof, -} - -impl TryFrom for AncestorBlock { - type Error = Error; - fn try_from(ancestor_block: types::AncestorBlock) -> Result { - Ok(AncestorBlock { - header: ancestor_block.header.try_into()?, - execution_payload: ancestor_block.execution_payload.try_into()?, - ancestry_proof: ancestor_block.ancestry_proof.try_into()?, - }) - } -} - -impl TryFrom for types::AncestorBlock { - type Error = Error; - fn try_from(derived_ancestor_block: AncestorBlock) -> Result { - let beacon_block_header = to_no_codec_beacon_header(derived_ancestor_block.header)?; - Ok(types::AncestorBlock { - header: beacon_block_header, - execution_payload: derived_ancestor_block.execution_payload.try_into()?, - ancestry_proof: derived_ancestor_block.ancestry_proof.try_into()?, - }) - } -} - -/// Holds the neccessary proofs required to verify a header in the `block_roots` field -/// either in [`BeaconState`] or [`HistoricalBatch`]. -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] -pub struct BlockRootsProof { - /// Generalized index of the header in the `block_roots` list. - pub block_header_index: u64, - /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` - pub block_header_branch: Vec>, -} - -impl TryFrom for BlockRootsProof { - type Error = Error; - fn try_from(beacon_block_header: types::BlockRootsProof) -> Result { - Ok(BlockRootsProof { - block_header_index: beacon_block_header.block_header_index, - block_header_branch: beacon_block_header - .block_header_branch - .iter() - .map(|hash| hash.to_vec()) - .collect(), - }) - } -} - -impl TryFrom for types::BlockRootsProof { - type Error = Error; - fn try_from(derived_beacon_block_header: BlockRootsProof) -> Result { - let branch = derived_beacon_block_header - .block_header_branch - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?; - - Ok(types::BlockRootsProof { - block_header_index: derived_beacon_block_header.block_header_index, - block_header_branch: branch, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] -pub enum AncestryProof { - /// This variant defines the proof data for a beacon chain header in the `state.block_roots` - BlockRoots { - /// Proof for the header in `state.block_roots` - block_roots_proof: BlockRootsProof, - /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] - block_roots_branch: Vec>, - }, - /// This variant defines the neccessary proofs for a beacon chain header in the - /// `state.historical_roots`. - HistoricalRoots { - /// Proof for the header in `historical_batch.block_roots` - block_roots_proof: BlockRootsProof, - /// The proof for the `historical_batch.block_roots`, needed to reconstruct - /// `hash_tree_root(historical_batch)` - historical_batch_proof: Vec>, - /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` - historical_roots_proof: Vec>, - /// The generalized index for the historical_batch in `state.historical_roots`. - historical_roots_index: u64, - /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in - /// [`BeaconState`] - historical_roots_branch: Vec>, - }, -} - -impl TryFrom for AncestryProof { - type Error = Error; - fn try_from(ancestry_proof: types::AncestryProof) -> Result { - Ok(match ancestry_proof { - types::AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => - AncestryProof::BlockRoots { - block_roots_proof: block_roots_proof.try_into()?, - block_roots_branch: block_roots_branch - .iter() - .map(|hash| hash.to_vec()) - .collect(), - }, - types::AncestryProof::HistoricalRoots { - block_roots_proof, - historical_batch_proof, - historical_roots_proof, - historical_roots_index, - historical_roots_branch, - } => AncestryProof::HistoricalRoots { - block_roots_proof: block_roots_proof.try_into()?, - historical_batch_proof: historical_batch_proof - .iter() - .map(|hash| hash.to_vec()) - .collect(), - historical_roots_proof: historical_roots_proof - .iter() - .map(|hash| hash.to_vec()) - .collect(), - historical_roots_index, - historical_roots_branch: historical_roots_branch - .iter() - .map(|hash| hash.to_vec()) - .collect(), - }, - }) - } -} - -impl TryFrom for types::AncestryProof { - type Error = Error; - fn try_from(ancestry_proof: AncestryProof) -> Result { - Ok(match ancestry_proof { - AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => - types::AncestryProof::BlockRoots { - block_roots_proof: block_roots_proof.try_into()?, - block_roots_branch: block_roots_branch - .iter() - .map(|proof| { - Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof) - }) - .collect::, _>>()?, - }, - AncestryProof::HistoricalRoots { - block_roots_proof, - historical_batch_proof, - historical_roots_proof, - historical_roots_index, - historical_roots_branch, - } => types::AncestryProof::HistoricalRoots { - block_roots_proof: block_roots_proof.try_into()?, - historical_batch_proof: historical_batch_proof - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?, - historical_roots_proof: historical_roots_proof - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?, - historical_roots_index, - historical_roots_branch: historical_roots_branch - .iter() - .map(|proof| Hash32::try_from(proof.as_ref()).map_err(|_| Error::InvalidProof)) - .collect::, _>>()?, - }, - }) - } -} diff --git a/primitives/src/domains.rs b/primitives/src/domains.rs new file mode 100644 index 000000000..fd8bd651b --- /dev/null +++ b/primitives/src/domains.rs @@ -0,0 +1,29 @@ +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum DomainType { + BeaconProposer, + BeaconAttester, + Randao, + Deposit, + VoluntaryExit, + SelectionProof, + AggregateAndProof, + SyncCommittee, + SyncCommitteeSelectionProof, + ContributionAndProof, + BlsToExecutionChange, + ApplicationMask, + ApplicationBuilder, +} + +impl DomainType { + pub fn as_bytes(&self) -> [u8; 4] { + match self { + Self::ApplicationMask => [0, 0, 0, 1], + Self::ApplicationBuilder => [0, 0, 0, 1], + _ => { + let data = *self as u32; + data.to_le_bytes() + }, + } + } +} diff --git a/primitives/src/helpers.rs b/primitives/src/helpers.rs deleted file mode 100644 index b16e8b9ec..000000000 --- a/primitives/src/helpers.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate::{ - derived_types, - error::Error, - types, - types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, -}; -use alloc::vec::Vec; -use ethereum_consensus::{ - bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, - crypto::PublicKey, - primitives::BlsSignature, -}; -use ssz_rs::{Bitvector, Deserialize, Node, Vector}; - -pub fn to_no_codec_beacon_header( - derived_header: derived_types::BeaconBlockHeader, -) -> Result { - let finalized_header = BeaconBlockHeader { - slot: derived_header.slot, - proposer_index: derived_header.proposer_index as usize, - parent_root: Node::from_bytes( - derived_header.parent_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, - ), - state_root: Node::from_bytes( - derived_header.state_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, - ), - body_root: Node::from_bytes( - derived_header.body_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, - ), - }; - - Ok(finalized_header) -} - -pub fn to_no_codec_sync_committee( - derived_sync_committee: derived_types::SyncCommittee, -) -> Result, Error> { - let public_keys_vector: Vec = derived_sync_committee - .public_keys - .iter() - .map(|public_key| { - PublicKey::try_from(public_key.as_slice()).map_err(|_| Error::InvalidPublicKey) - }) - .collect::, Error>>()?; - let sync_committee = SyncCommittee { - public_keys: Vector::try_from(public_keys_vector).unwrap(), - aggregate_public_key: PublicKey::try_from( - derived_sync_committee.aggregate_public_key.as_slice(), - ) - .map_err(|_| Error::InvalidPublicKey)?, - }; - - Ok(sync_committee) -} - -pub fn to_no_codec_sync_aggregate( - derived_sync_aggregate: derived_types::SyncAggregate, -) -> Result, Error> { - let derived_sync_committee_bits = derived_sync_aggregate.sync_committee_bits; - let bit_vector = Bitvector::::deserialize(&derived_sync_committee_bits) - .map_err(|_| Error::InvalidBitVec)?; - - let sync_aggregate = SyncAggregate { - sync_committee_bits: bit_vector, - sync_committee_signature: BlsSignature::try_from( - derived_sync_aggregate.sync_committee_signature.as_ref(), - ) - .map_err(|_| Error::InvalidPublicKey)?, - }; - - Ok(sync_aggregate) -} - -pub fn to_no_codec_light_client_state( - state: derived_types::LightClientState, -) -> Result, Error> { - let finalized_header = to_no_codec_beacon_header(state.finalized_header)?; - - let current_sync_committee = to_no_codec_sync_committee(state.current_sync_committee.clone())?; - let next_sync_committee = to_no_codec_sync_committee(state.next_sync_committee)?; - - Ok(LightClientState { - finalized_header, - latest_finalized_epoch: state.latest_finalized_epoch, - current_sync_committee, - next_sync_committee, - }) -} - -pub fn to_no_codec_light_client_update( - derived_update: derived_types::LightClientUpdate, -) -> Result, Error> { - let sync_committee_update_option: Option>; - - match derived_update.sync_committee_update { - Some(sync_committee_update) => - sync_committee_update_option = Some(sync_committee_update.try_into()?), - None => sync_committee_update_option = None, - } - Ok(LightClientUpdate { - attested_header: to_no_codec_beacon_header(derived_update.attested_header)?, - sync_committee_update: sync_committee_update_option, - finalized_header: to_no_codec_beacon_header(derived_update.finalized_header)?, - execution_payload: derived_update.execution_payload.try_into()?, - finality_proof: derived_update.finality_proof.try_into()?, - sync_aggregate: to_no_codec_sync_aggregate(derived_update.sync_aggregate)?, - signature_slot: derived_update.signature_slot, - ancestor_blocks: derived_update - .ancestor_blocks - .iter() - .map(|ancestor_block| { - ancestor_block - .clone() - .try_into() - .map_err(|_| Error::ErrorConvertingAncestorBlock) - }) - .collect::, Error>>()?, - }) -} - -pub fn to_codec_light_client_state( - state: types::LightClientState, -) -> Result { - Ok(derived_types::LightClientState { - finalized_header: state.finalized_header.try_into()?, - latest_finalized_epoch: state.latest_finalized_epoch, - current_sync_committee: state.current_sync_committee.try_into()?, - next_sync_committee: state.next_sync_committee.try_into()?, - }) -} - -pub fn to_codec_light_client_update( - update: types::LightClientUpdate, -) -> Result { - let sync_committee_update_option: Option; - - match update.sync_committee_update { - Some(sync_committee_update) => - sync_committee_update_option = Some(sync_committee_update.try_into()?), - - None => sync_committee_update_option = None, - } - Ok(derived_types::LightClientUpdate { - attested_header: update.attested_header.try_into()?, - sync_committee_update: sync_committee_update_option, - finalized_header: update.finalized_header.try_into()?, - execution_payload: update.execution_payload.try_into()?, - finality_proof: update.finality_proof.try_into()?, - sync_aggregate: update.sync_aggregate.try_into()?, - signature_slot: update.signature_slot, - ancestor_blocks: update - .ancestor_blocks - .iter() - .map(|ancestor_block| { - ancestor_block - .clone() - .try_into() - .map_err(|_| Error::ErrorConvertingAncestorBlock) - }) - .collect::, Error>>()?, - }) -} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index b53869e0d..afce5f503 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,10 +1,16 @@ +//! Primitive types for sync committee verifier +//! This crate contains code adapted from https://github.com/ralexstokes/ethereum-consensus #![cfg_attr(not(feature = "std"), no_std)] #[warn(unused_imports)] #[warn(unused_variables)] extern crate alloc; -pub mod derived_types; +pub mod consensus_types; +pub mod constants; +pub mod domains; pub mod error; -pub mod helpers; +#[cfg(feature = "std")] +pub mod serde; +mod ssz; pub mod types; pub mod util; diff --git a/primitives/src/serde.rs b/primitives/src/serde.rs new file mode 100644 index 000000000..e1c59c77b --- /dev/null +++ b/primitives/src/serde.rs @@ -0,0 +1,142 @@ +use core::fmt::{Display, Formatter}; +use hex::FromHexError; + +const HEX_ENCODING_PREFIX: &str = "0x"; + +#[cfg_attr(feature = "serde", derive(Debug))] +pub enum HexError { + Hex, + MissingPrefix, +} + +impl From for HexError { + fn from(_: FromHexError) -> Self { + HexError::Hex + } +} + +impl Display for HexError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + HexError::Hex => write!(f, ""), + HexError::MissingPrefix => write!(f, "missing prefix when deserializing hex data"), + } + } +} + +pub fn try_bytes_from_hex_str(s: &str) -> Result, HexError> { + let target = s.strip_prefix(HEX_ENCODING_PREFIX).ok_or(HexError::MissingPrefix)?; + let data = hex::decode(target)?; + Ok(data) +} + +pub mod as_hex { + use super::*; + use alloc::format; + use serde::de::Deserialize; + + pub fn serialize>(data: T, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoding = hex::encode(data.as_ref()); + let output = format!("{HEX_ENCODING_PREFIX}{encoding}"); + serializer.collect_str(&output) + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + T: TryFrom>, + { + let s = ::deserialize(deserializer)?; + + let data = try_bytes_from_hex_str(&s).map_err(serde::de::Error::custom)?; + + let inner = T::try_from(data) + .map_err(|_| serde::de::Error::custom("type failed to parse bytes from hex data"))?; + Ok(inner) + } +} + +pub mod as_string { + use alloc::format; + use core::{fmt, str::FromStr}; + use serde::de::Deserialize; + + pub fn serialize(data: T, serializer: S) -> Result + where + S: serde::Serializer, + { + let output = format!("{data}"); + serializer.collect_str(&output) + } + + pub fn deserialize<'de, D, T: FromStr>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s: String = ::deserialize(deserializer)?; + let inner: T = s + .parse() + .map_err(|_| serde::de::Error::custom("failure to parse string data"))?; + Ok(inner) + } +} + +pub mod collection_over_string { + use core::{fmt, marker::PhantomData, str::FromStr}; + use serde::{ + de::{Deserializer, Error}, + ser::SerializeSeq, + }; + + pub fn serialize(data: T, serializer: S) -> Result + where + S: serde::Serializer, + T: AsRef<[U]>, + U: fmt::Display, + { + let mut seq = serializer.serialize_seq(None)?; + for elem in data.as_ref().iter() { + let rendered_elem = format!("{elem}"); + seq.serialize_element(&rendered_elem)?; + } + seq.end() + } + + struct Visitor(PhantomData>); + + impl<'de, T: FromStr> serde::de::Visitor<'de> for Visitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("array of string") + } + + fn visit_seq(self, mut access: S) -> Result + where + S: serde::de::SeqAccess<'de>, + { + let mut coll = Vec::with_capacity(access.size_hint().unwrap_or(0)); + + while let Some(elem) = access.next_element()? { + let recovered_elem = T::from_str(elem).map_err(|_| { + Error::custom("failure to parse element of sequence from string") + })?; + coll.push(recovered_elem); + } + Ok(coll) + } + } + + pub fn deserialize<'de, D, T, U>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: TryFrom>, + U: FromStr, + { + let data = deserializer.deserialize_seq(Visitor(PhantomData))?; + T::try_from(data).map_err(|_| serde::de::Error::custom("failure to parse collection")) + } +} diff --git a/primitives/src/ssz/byte_list.rs b/primitives/src/ssz/byte_list.rs new file mode 100644 index 000000000..a22b2c1b4 --- /dev/null +++ b/primitives/src/ssz/byte_list.rs @@ -0,0 +1,88 @@ +use super::write_bytes_to_lower_hex; +use alloc::{vec, vec::Vec}; +use core::{ + fmt, + hash::{Hash, Hasher}, + ops::{Deref, DerefMut}, +}; +use ssz_rs::prelude::*; + +#[derive(Default, Clone, Eq, SimpleSerialize, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ByteList( + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_hex"))] List, +); + +impl TryFrom<&[u8]> for ByteList { + type Error = ssz_rs::DeserializeError; + + fn try_from(bytes: &[u8]) -> Result { + ByteList::::deserialize(bytes) + } +} + +// impl here to satisfy clippy +impl PartialEq for ByteList { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for ByteList { + fn hash(&self, state: &mut H) { + self.as_ref().hash(state); + } +} + +impl fmt::LowerHex for ByteList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write_bytes_to_lower_hex(f, self) + } +} + +impl fmt::Debug for ByteList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ByteList<{N}>(len={})({:#x})", self.len(), self) + } +} + +impl fmt::Display for ByteList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:#x}") + } +} + +impl AsRef<[u8]> for ByteList { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for ByteList { + type Target = List; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ByteList { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_byte_list_serde() { + let list = ByteList::<32>::try_from([255u8, 255u8].as_ref()).unwrap(); + let encoding = ssz_rs::serialize(&list).unwrap(); + assert_eq!(encoding, [255, 255]); + + let recovered_list = ByteList::<32>::deserialize(&encoding).unwrap(); + assert_eq!(list, recovered_list); + } +} diff --git a/primitives/src/ssz/byte_vector.rs b/primitives/src/ssz/byte_vector.rs new file mode 100644 index 000000000..1b1b5ba7d --- /dev/null +++ b/primitives/src/ssz/byte_vector.rs @@ -0,0 +1,81 @@ +use super::write_bytes_to_lower_hex; +use alloc::{vec, vec::Vec}; +use core::{ + fmt, + hash::{Hash, Hasher}, + ops::{Deref, DerefMut}, +}; +use ssz_rs::prelude::*; + +#[derive(Default, Clone, Eq, SimpleSerialize, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ByteVector( + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_hex"))] Vector, +); + +impl TryFrom<&[u8]> for ByteVector { + type Error = ssz_rs::DeserializeError; + + fn try_from(bytes: &[u8]) -> Result { + ByteVector::::deserialize(bytes) + } +} + +impl TryFrom> for ByteVector { + type Error = ssz_rs::DeserializeError; + + fn try_from(bytes: Vec) -> Result { + ByteVector::::deserialize(&bytes) + } +} + +// impl here to satisfy clippy +impl PartialEq for ByteVector { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for ByteVector { + fn hash(&self, state: &mut H) { + self.as_ref().hash(state); + } +} + +impl fmt::LowerHex for ByteVector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write_bytes_to_lower_hex(f, self) + } +} + +impl fmt::Debug for ByteVector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ByteVector<{N}>({self:#x})") + } +} + +impl fmt::Display for ByteVector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:#x}") + } +} + +impl AsRef<[u8]> for ByteVector { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for ByteVector { + type Target = Vector; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ByteVector { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/primitives/src/ssz/mod.rs b/primitives/src/ssz/mod.rs new file mode 100644 index 000000000..ebeaa9b3b --- /dev/null +++ b/primitives/src/ssz/mod.rs @@ -0,0 +1,16 @@ +mod byte_list; +mod byte_vector; +use core::fmt; + +fn write_bytes_to_lower_hex>(f: &mut fmt::Formatter<'_>, data: T) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + for i in data.as_ref() { + write!(f, "{i:02x}")?; + } + Ok(()) +} + +pub use byte_list::ByteList; +pub use byte_vector::ByteVector; diff --git a/primitives/src/types.rs b/primitives/src/types.rs index d5fc44b7a..c0ca114a5 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -1,69 +1,47 @@ -use alloc::vec::Vec; -use ethereum_consensus::{ - bellatrix::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, - domains::DomainType, - primitives::{Hash32, Slot}, +use crate::{ + consensus_types::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, + constants::{Slot, SYNC_COMMITTEE_SIZE}, }; - -pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; -pub const FINALIZED_ROOT_INDEX: u64 = 52; -pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; -pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; -pub const EXECUTION_PAYLOAD_INDEX: u64 = 56; -pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; -pub const BLOCK_ROOTS_INDEX: u64 = 37; -pub const HISTORICAL_ROOTS_INDEX: u64 = 39; -pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; -pub const EXECUTION_PAYLOAD_TIMESTAMP_INDEX: u64 = 25; -pub const FINALIZED_ROOT_INDEX_LOG2: u64 = 5; -pub const EXECUTION_PAYLOAD_INDEX_LOG2: u64 = 5; -pub const NEXT_SYNC_COMMITTEE_INDEX_LOG2: u64 = 5; -pub const BLOCK_ROOTS_INDEX_LOG2: u64 = 5; -pub const HISTORICAL_ROOTS_INDEX_LOG2: u64 = 5; - -#[cfg(not(feature = "testing"))] -pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = - hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); -#[cfg(feature = "testing")] -pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = - hex_literal::hex!("6034f557b4560fc549ac0e2c63269deb07bfac7bf2bbd0b8b7d4d321240bffd9"); +use alloc::vec::Vec; +use primitive_types::H256; +use ssz_rs::Node; /// This holds the relevant data required to prove the state root in the execution payload. -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] pub struct ExecutionPayloadProof { /// The state root in the `ExecutionPayload` which represents the commitment to /// the ethereum world state in the yellow paper. - pub state_root: Hash32, + pub state_root: H256, /// the block number of the execution header. pub block_number: u64, /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. - pub multi_proof: Vec, + pub multi_proof: Vec, /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. - pub execution_payload_branch: Vec, + pub execution_payload_branch: Vec, /// timestamp pub timestamp: u64, } /// Holds the neccessary proofs required to verify a header in the `block_roots` field /// either in [`BeaconState`] or [`HistoricalBatch`]. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] pub struct BlockRootsProof { /// Generalized index of the header in the `block_roots` list. pub block_header_index: u64, /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` - pub block_header_branch: Vec, + pub block_header_branch: Vec, } /// The block header ancestry proof, this is an enum because the header may either exist in /// `state.block_roots` or `state.historical_roots`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] pub enum AncestryProof { /// This variant defines the proof data for a beacon chain header in the `state.block_roots` BlockRoots { /// Proof for the header in `state.block_roots` block_roots_proof: BlockRootsProof, /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] - block_roots_branch: Vec, + block_roots_branch: Vec, }, /// This variant defines the neccessary proofs for a beacon chain header in the /// `state.historical_roots`. @@ -72,20 +50,20 @@ pub enum AncestryProof { block_roots_proof: BlockRootsProof, /// The proof for the `historical_batch.block_roots`, needed to reconstruct /// `hash_tree_root(historical_batch)` - historical_batch_proof: Vec, + historical_batch_proof: Vec, /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` - historical_roots_proof: Vec, + historical_roots_proof: Vec, /// The generalized index for the historical_batch in `state.historical_roots`. historical_roots_index: u64, /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in /// [`BeaconState`] - historical_roots_branch: Vec, + historical_roots_branch: Vec, }, } /// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized /// header. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] pub struct AncestorBlock { /// The actual beacon chain header pub header: BeaconBlockHeader, @@ -97,17 +75,17 @@ pub struct AncestorBlock { /// Holds the latest sync committee as well as an ssz proof for it's existence /// in a finalized header. -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct SyncCommitteeUpdate { +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct SyncCommitteeUpdate { // actual sync committee pub next_sync_committee: SyncCommittee, // sync committee, ssz merkle proof. - pub next_sync_committee_branch: Vec, + pub next_sync_committee_branch: Vec, } /// Minimum state required by the light client to validate new sync committee attestations -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct LightClientState { +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct LightClientState { /// The latest recorded finalized header pub finalized_header: BeaconBlockHeader, /// Latest finalized epoch @@ -118,21 +96,21 @@ pub struct LightClientState { } /// Finalized header proof -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] pub struct FinalityProof { /// The latest finalized epoch pub epoch: u64, /// Finalized header proof - pub finality_branch: Vec, + pub finality_branch: Vec, } /// Data required to advance the state of the light client. -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct LightClientUpdate { +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct LightClientUpdate { /// the header that the sync committee signed pub attested_header: BeaconBlockHeader, /// the sync committee has potentially changed, here's an ssz proof for that. - pub sync_committee_update: Option>, + pub sync_committee_update: Option, /// the actual header which was finalized by the ethereum attestation protocol. pub finalized_header: BeaconBlockHeader, /// execution payload of the finalized header @@ -143,6 +121,6 @@ pub struct LightClientUpdate { pub sync_aggregate: SyncAggregate, /// slot at which signature was produced pub signature_slot: Slot, - /// ancestors of the finalized block to be verified, may be empty. - pub ancestor_blocks: Vec, + // ancestors of the finalized block to be verified, may be empty. + // pub ancestor_blocks: Vec, } diff --git a/primitives/src/util.rs b/primitives/src/util.rs index 20701c9e5..4c1abaab4 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -1,11 +1,15 @@ -use ethereum_consensus::{ - altair::mainnet::EPOCHS_PER_SYNC_COMMITTEE_PERIOD, - configs::mainnet::{ - ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, BELLATRIX_FORK_EPOCH, BELLATRIX_FORK_VERSION, - GENESIS_FORK_VERSION, +use crate::{ + consensus_types::ForkData, + constants::{ + Domain, Root, Version, ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, BELLATRIX_FORK_EPOCH, + BELLATRIX_FORK_VERSION, CAPELLA_FORK_EPOCH, CAPELLA_FORK_VERSION, + EPOCHS_PER_SYNC_COMMITTEE_PERIOD, GENESIS_FORK_VERSION, SLOTS_PER_EPOCH, }, - phase0::mainnet::SLOTS_PER_EPOCH, + domains::DomainType, }; +use alloc::{vec, vec::Vec}; +use anyhow::anyhow; +use ssz_rs::prelude::*; /// Return the sync committe period at the given ``epoch`` pub fn compute_sync_committee_period(epoch: u64) -> u64 { @@ -20,7 +24,9 @@ pub fn compute_epoch_at_slot(slot: u64) -> u64 { #[cfg(not(feature = "testing"))] /// Return the fork version at the given ``epoch``. pub fn compute_fork_version(epoch: u64) -> [u8; 4] { - if epoch >= BELLATRIX_FORK_EPOCH { + if epoch >= CAPELLA_FORK_EPOCH { + CAPELLA_FORK_VERSION + } else if epoch >= BELLATRIX_FORK_EPOCH { BELLATRIX_FORK_VERSION } else if epoch >= ALTAIR_FORK_EPOCH { ALTAIR_FORK_VERSION @@ -29,9 +35,44 @@ pub fn compute_fork_version(epoch: u64) -> [u8; 4] { } } -#[cfg(feature = "testing")] -pub fn compute_fork_version(_epoch: u64) -> [u8; 4] { - BELLATRIX_FORK_VERSION +pub fn compute_domain( + domain_type: DomainType, + fork_version: Option, + genesis_validators_root: Option, + genesis_fork_version: Version, +) -> Result { + let fork_version = fork_version.unwrap_or(genesis_fork_version); + let genesis_validators_root = genesis_validators_root.unwrap_or_default(); + let fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)?; + let mut domain = Domain::default(); + domain[..4].copy_from_slice(&domain_type.as_bytes()); + domain[4..].copy_from_slice(&fork_data_root.as_ref()[..28]); + Ok(domain) +} + +#[derive(Default, Debug, SimpleSerialize)] +pub struct SigningData { + pub object_root: Root, + pub domain: Domain, +} + +pub fn compute_signing_root( + ssz_object: &mut T, + domain: Domain, +) -> Result { + let object_root = ssz_object.hash_tree_root().map_err(|e| anyhow!("{:?}", e))?; + + let mut s = SigningData { object_root, domain }; + s.hash_tree_root().map_err(|e| anyhow!("{:?}", e)) +} + +pub fn compute_fork_data_root( + current_version: Version, + genesis_validators_root: Root, +) -> Result { + ForkData { current_version, genesis_validators_root } + .hash_tree_root() + .map_err(|e| anyhow!("{:?}", e)) } /// Return the sync committee period at ``slot`` diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 5ec1cae68..1abc687ed 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -8,22 +8,41 @@ edition = "2021" [dependencies] sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main" } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" } reqwest = {version="0.11.14", features=["json"]} -serde = { version = "1.0", features = ["derive"]} +serde = { version = "1.0.185", features = ["derive"] } serde_json = { version = "1.0.81"} anyhow = "1.0.68" -actix-rt = "*" -tokio = { version = "1.18.2", features = ["full"]} +tokio = { version = "1.32.0", features = ["sync"]} tokio-stream = { version = "0.1.8" } async-stream = { version = "0.3.3"} -base2 = {version= "0.3.1", default-features=false} +base2 = {version= "0.3.1" } env_logger = "0.10.0" +ark-ec = { version = "0.4.2" } +ark-bls12-381 = { version = "0.4.0" } +bls_on_arkworks = { version = "0.2.2" } +primitive-types = { version = "0.12.1", features = ["serde_no_std", "impl-codec"] } +log = "0.4.20" +hex = "0.4.3" [dev-dependencies] -hex = "0.4.3" -sync-committee-primitives = { path= "../primitives", features = ["testing"] } -sync-committee-verifier = { path= "../verifier", features = ["testing"] } +env_logger = "0.10.0" +sync-committee-primitives = { path= "../primitives" } +sync-committee-verifier = { path= "../verifier" } +ethers = { version = "2.0.8", features = ["ws"] } +tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"]} + +[features] +default = ["std"] +std = [ + "ssz-rs/default", + "sync-committee-primitives/std", + "anyhow/std", + "ark-ec/std", + "bls_on_arkworks/std", + "ark-bls12-381/std" +] +testnet = ["sync-committee-primitives/testnet", "sync-committee-verifier/testnet"] +mainnet = ["sync-committee-primitives/mainnet", "sync-committee-verifier/mainnet"] diff --git a/prover/src/lib.rs b/prover/src/lib.rs index a519aca7b..5fe4bfe4c 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -5,11 +5,14 @@ mod routes; #[cfg(test)] mod test; -use ethereum_consensus::{ - altair::Validator, - bellatrix::{BeaconBlock, BeaconBlockHeader, BeaconState, SyncCommittee}, -}; +use anyhow::anyhow; +use bls_on_arkworks::{point_to_pubkey, types::G1ProjectivePoint}; +use log::debug; use reqwest::Client; +use std::time::Duration; +use sync_committee_primitives::consensus_types::{ + BeaconBlock, BeaconBlockHeader, BeaconState, SyncCommittee, Validator, +}; use crate::{ responses::{ @@ -18,30 +21,28 @@ use crate::{ }, routes::*, }; -use ethereum_consensus::{ - bellatrix::mainnet::{ - BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, - MAX_TRANSACTIONS_PER_PAYLOAD, SYNC_COMMITTEE_SIZE, - }, - crypto::eth_aggregate_public_keys, - phase0::mainnet::{ - EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, - HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, - MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_EPOCH, - SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, - }, - primitives::{BlsPublicKey, Bytes32, Hash32, ValidatorIndex}, -}; +use primitive_types::H256; use ssz_rs::{List, Merkleized, Node, Vector}; use sync_committee_primitives::{ - types::{ - AncestryProof, BlockRootsProof, ExecutionPayloadProof, BLOCK_ROOTS_INDEX, + constants::{ + BlsPublicKey, ValidatorIndex, BLOCK_ROOTS_INDEX, BYTES_PER_LOGS_BLOOM, + EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, - FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX, + FINALIZED_ROOT_INDEX, HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, + MAX_BLS_TO_EXECUTION_CHANGES, MAX_BYTES_PER_TRANSACTION, MAX_DEPOSITS, + MAX_EXTRA_DATA_BYTES, MAX_PROPOSER_SLASHINGS, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, MAX_WITHDRAWALS_PER_PAYLOAD, + NEXT_SYNC_COMMITTEE_INDEX, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_SIZE, + VALIDATOR_REGISTRY_LIMIT, }, - util::compute_epoch_at_slot, + types::{ + AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, LightClientUpdate, + SyncCommitteeUpdate, + }, + util::{compute_epoch_at_slot, compute_sync_committee_period_at_slot}, }; +use sync_committee_verifier::{signature_verification::pubkey_to_projective, LightClientState}; pub type BeaconStateType = BeaconState< SLOTS_PER_HISTORICAL_ROOT, @@ -71,7 +72,7 @@ impl SyncCommitteeProver { SyncCommitteeProver { node_url, client } } - pub async fn fetch_finalized_checkpoint(&self) -> Result { + pub async fn fetch_finalized_checkpoint(&self) -> Result { let full_url = self.generate_route(&finality_checkpoints("head")); let response = self.client.get(full_url).send().await?; @@ -80,7 +81,7 @@ impl SyncCommitteeProver { Ok(response_data.data) } - pub async fn fetch_header(&self, block_id: &str) -> Result { + pub async fn fetch_header(&self, block_id: &str) -> Result { let path = header_route(block_id); let full_url = self.generate_route(&path); let response = self.client.get(full_url).send().await?; @@ -109,8 +110,10 @@ impl SyncCommitteeProver { MAX_EXTRA_DATA_BYTES, MAX_BYTES_PER_TRANSACTION, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, >, - reqwest::Error, + anyhow::Error, > { let path = block_route(block_id); let full_url = self.generate_route(&path); @@ -127,7 +130,7 @@ impl SyncCommitteeProver { pub async fn fetch_sync_committee( &self, state_id: &str, - ) -> Result { + ) -> Result { let path = sync_committee_route(state_id); let full_url = self.generate_route(&path); @@ -144,7 +147,7 @@ impl SyncCommitteeProver { &self, state_id: &str, validator_index: &str, - ) -> Result { + ) -> Result { let path = validator_route(state_id, validator_index); let full_url = self.generate_route(&path); @@ -160,7 +163,7 @@ impl SyncCommitteeProver { pub async fn fetch_beacon_state( &self, state_id: &str, - ) -> Result { + ) -> Result { let path = beacon_state_route(state_id); let full_url = self.generate_route(&path); @@ -176,14 +179,14 @@ impl SyncCommitteeProver { pub async fn fetch_processed_sync_committee( &self, state_id: &str, - ) -> Result, reqwest::Error> { + ) -> Result, anyhow::Error> { // fetches sync committee from Node - let node_sync_committee = self.fetch_sync_committee(state_id.clone()).await?; + let node_sync_committee = self.fetch_sync_committee(state_id).await?; let mut validators: List = Default::default(); - for validator_index in node_sync_committee.validators.clone() { + for validator_index in node_sync_committee.validators.iter() { // fetches validator based on validator index - let validator = self.fetch_validator(state_id.clone(), &validator_index).await?; + let validator = self.fetch_validator(state_id, validator_index).await?; validators.push(validator); } @@ -191,16 +194,16 @@ impl SyncCommitteeProver { .validators .into_iter() .map(|i| { - let validator_index: ValidatorIndex = i.parse().unwrap(); - validators[validator_index].public_key.clone() + let validator_index: ValidatorIndex = i.parse()?; + Ok(validators[validator_index as usize].public_key.clone()) }) - .collect::>(); + .collect::, anyhow::Error>>()?; - let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector).unwrap(); + let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector)?; let sync_committee = SyncCommittee:: { public_keys: Vector::::try_from(public_keys_vector) - .unwrap(), + .map_err(|e| anyhow!("{:?}", e))?, aggregate_public_key, }; @@ -210,6 +213,152 @@ impl SyncCommitteeProver { fn generate_route(&self, path: &str) -> String { format!("{}{}", self.node_url.clone(), path) } + + pub async fn fetch_light_client_update( + &self, + client_state: LightClientState, + debug_target: &str, + ) -> Result, anyhow::Error> { + let finality_checkpoint = self.fetch_finalized_checkpoint().await?; + if finality_checkpoint.finalized.root == Node::default() || + finality_checkpoint.finalized.epoch <= client_state.latest_finalized_epoch || + finality_checkpoint.finalized.root == + client_state.finalized_header.clone().hash_tree_root()? + { + return Ok(None) + } + + debug!(target: debug_target, "A new epoch has been finalized {}", finality_checkpoint.finalized.epoch); + + let block_id = { + let mut block_id = hex::encode(finality_checkpoint.finalized.root.as_bytes()); + block_id.insert_str(0, "0x"); + block_id + }; + + let finalized_header = self.fetch_header(&block_id).await?; + let mut finalized_state = + self.fetch_beacon_state(finalized_header.slot.to_string().as_str()).await?; + let execution_payload_proof = prove_execution_payload(&mut finalized_state)?; + + let mut attested_epoch = finality_checkpoint.finalized.epoch + 2; + // Get attested header and the signature slot + + let mut attested_slot = attested_epoch * SLOTS_PER_EPOCH; + // Due to the fact that all slots in an epoch can be missed we are going to try and fetch + // the attested block from four possible epochs. + let mut attested_epoch_loop_count = 0; + let (attested_block_header, signature_block) = loop { + if attested_epoch_loop_count == 4 { + Err(anyhow!("Could not fetch any block from the attested epoch after going through four epochs"))? + } + // If we have maxed out the slots in the current epoch and still didn't find any block, + // we move to the next epoch + if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == + attested_slot + { + // No block was found in attested epoch we move to the next possible attested epoch + debug!(target: debug_target, + "No slots found in epoch {attested_epoch} Moving to the next possible epoch {}", + attested_epoch + 1 + ); + tokio::time::sleep(Duration::from_secs(24)).await; + attested_epoch += 1; + attested_slot = attested_epoch * SLOTS_PER_EPOCH; + attested_epoch_loop_count += 1; + } + + if let Ok(header) = self.fetch_header(attested_slot.to_string().as_str()).await { + let mut signature_slot = header.slot + 1; + let mut loop_count = 0; + let signature_block = loop { + if loop_count == 2 { + break None + } + if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == + signature_slot + { + debug!(target: debug_target, "Waiting for signature block for attested header"); + tokio::time::sleep(Duration::from_secs(24)).await; + signature_slot = header.slot + 1; + loop_count += 1; + } + if let Ok(signature_block) = + self.fetch_block(signature_slot.to_string().as_str()).await + { + break Some(signature_block) + } + signature_slot += 1; + }; + // If the next block does not have sufficient sync committee participants + if let Some(signature_block) = signature_block { + if signature_block + .body + .sync_aggregate + .sync_committee_bits + .as_bitslice() + .count_ones() < (2 * (SYNC_COMMITTEE_SIZE)) / 3 + { + attested_slot += 1; + debug!(target:debug_target, "Signature block does not have sufficient sync committee participants -> participants {}", signature_block.body.sync_aggregate.sync_committee_bits.as_bitslice().count_ones()); + continue + } + break (header, signature_block) + } else { + debug!(target: debug_target,"No signature block found in {attested_epoch} Moving to the next possible epoch {}", attested_epoch + 1); + tokio::time::sleep(Duration::from_secs(24)).await; + attested_epoch += 1; + attested_slot = attested_epoch * SLOTS_PER_EPOCH; + attested_epoch_loop_count += 1; + continue + } + } + attested_slot += 1 + }; + + let mut attested_state = + self.fetch_beacon_state(attested_block_header.slot.to_string().as_str()).await?; + + let finalized_hash_tree_root = finalized_header.clone().hash_tree_root()?; + + if cfg!(test) { + assert_eq!(finalized_hash_tree_root, attested_state.finalized_checkpoint.root); + } + + let finality_proof = FinalityProof { + epoch: finality_checkpoint.finalized.epoch, + finality_branch: prove_finalized_header(&mut attested_state)?, + }; + + let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); + + let update_attested_period = + compute_sync_committee_period_at_slot(attested_block_header.slot); + + let sync_committee_update = if state_period == update_attested_period { + let sync_committee_proof = prove_sync_committee_update(&mut attested_state)?; + + Some(SyncCommitteeUpdate { + next_sync_committee: attested_state.next_sync_committee, + next_sync_committee_branch: sync_committee_proof, + }) + } else { + None + }; + + // construct light client + let light_client_update = LightClientUpdate { + attested_header: attested_block_header, + sync_committee_update, + finalized_header, + execution_payload: execution_payload_proof, + finality_proof, + sync_aggregate: signature_block.body.sync_aggregate, + signature_slot: signature_block.slot, + }; + + Ok(Some(light_client_update)) + } } pub fn get_attested_epoch(finalized_epoch: u64) -> u64 { @@ -217,7 +366,7 @@ pub fn get_attested_epoch(finalized_epoch: u64) -> u64 { } pub fn prove_execution_payload( - mut beacon_state: BeaconStateType, + beacon_state: &mut BeaconStateType, ) -> anyhow::Result { let indices = [ EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, @@ -231,40 +380,33 @@ pub fn prove_execution_payload( )?; Ok(ExecutionPayloadProof { - state_root: beacon_state.latest_execution_payload_header.state_root.clone(), + state_root: H256::from_slice( + beacon_state.latest_execution_payload_header.state_root.as_slice(), + ), block_number: beacon_state.latest_execution_payload_header.block_number, timestamp: beacon_state.latest_execution_payload_header.timestamp, - multi_proof: multi_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), + multi_proof, execution_payload_branch: ssz_rs::generate_proof( - &mut beacon_state, + beacon_state, &[EXECUTION_PAYLOAD_INDEX as usize], - )? - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect(), + )?, }) } -pub fn prove_sync_committee_update(mut state: BeaconStateType) -> anyhow::Result> { - let proof = ssz_rs::generate_proof(&mut state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; +pub fn prove_sync_committee_update(state: &mut BeaconStateType) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; Ok(proof) } -pub fn prove_finalized_header(mut state: BeaconStateType) -> anyhow::Result> { +pub fn prove_finalized_header(state: &mut BeaconStateType) -> anyhow::Result> { let indices = [FINALIZED_ROOT_INDEX as usize]; - let proof = ssz_rs::generate_proof(&mut state, indices.as_slice())?; + let proof = ssz_rs::generate_proof(state, indices.as_slice())?; - Ok(proof - .into_iter() - .map(|node| Hash32::try_from(node.as_ref()).expect("Node is always a 32 byte slice")) - .collect()) + Ok(proof) } pub fn prove_block_roots_proof( - mut state: BeaconStateType, + state: &mut BeaconStateType, mut header: BeaconBlockHeader, ) -> anyhow::Result { // Check if block root should still be part of the block roots vector on the beacon state @@ -288,25 +430,26 @@ pub fn prove_block_roots_proof( let proof = ssz_rs::generate_proof(&mut state.block_roots, &[block_index])?; - let block_roots_proof = BlockRootsProof { - block_header_index: block_index as u64, - block_header_branch: proof - .into_iter() - .map(|node| { - Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") - }) - .collect(), - }; + let block_roots_proof = + BlockRootsProof { block_header_index: block_index as u64, block_header_branch: proof }; - let block_roots_branch = ssz_rs::generate_proof(&mut state, &[BLOCK_ROOTS_INDEX as usize])?; - Ok(AncestryProof::BlockRoots { - block_roots_proof, - block_roots_branch: block_roots_branch - .into_iter() - .map(|node| { - Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") - }) - .collect(), - }) + let block_roots_branch = ssz_rs::generate_proof(state, &[BLOCK_ROOTS_INDEX as usize])?; + Ok(AncestryProof::BlockRoots { block_roots_proof, block_roots_branch }) } } + +pub fn eth_aggregate_public_keys(points: &[BlsPublicKey]) -> anyhow::Result { + let points = points + .iter() + .map(|point| pubkey_to_projective(point)) + .collect::, _>>()?; + let aggregate = points + .into_iter() + .fold(G1ProjectivePoint::default(), |acc, g1_point| acc + g1_point); + let public_key = point_to_pubkey(aggregate.into()); + + let bls_public_key = + BlsPublicKey::try_from(public_key.as_slice()).map_err(|e| anyhow!("{:?}", e))?; + + Ok(bls_public_key) +} diff --git a/prover/src/responses/beacon_block_header_response.rs b/prover/src/responses/beacon_block_header_response.rs index 3d6d3a533..9a3e6b167 100644 --- a/prover/src/responses/beacon_block_header_response.rs +++ b/prover/src/responses/beacon_block_header_response.rs @@ -1,4 +1,4 @@ -use ethereum_consensus::bellatrix::BeaconBlockHeader; +use sync_committee_primitives::consensus_types::BeaconBlockHeader; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { diff --git a/prover/src/responses/beacon_block_response.rs b/prover/src/responses/beacon_block_response.rs index 62839e3c4..ac3c2b290 100644 --- a/prover/src/responses/beacon_block_response.rs +++ b/prover/src/responses/beacon_block_response.rs @@ -1,10 +1,12 @@ -use ethereum_consensus::bellatrix::{ - mainnet::{ - BYTES_PER_LOGS_BLOOM, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_BYTES_PER_TRANSACTION, - MAX_DEPOSITS, MAX_EXTRA_DATA_BYTES, MAX_PROPOSER_SLASHINGS, MAX_TRANSACTIONS_PER_PAYLOAD, - MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SYNC_COMMITTEE_SIZE, +use sync_committee_primitives::{ + consensus_types::BeaconBlock, + constants::{ + BYTES_PER_LOGS_BLOOM, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, + MAX_BLS_TO_EXECUTION_CHANGES, MAX_BYTES_PER_TRANSACTION, MAX_DEPOSITS, + MAX_EXTRA_DATA_BYTES, MAX_PROPOSER_SLASHINGS, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, MAX_WITHDRAWALS_PER_PAYLOAD, + SYNC_COMMITTEE_SIZE, }, - BeaconBlock, }; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -28,6 +30,8 @@ pub struct ResponseData { MAX_EXTRA_DATA_BYTES, MAX_BYTES_PER_TRANSACTION, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, >, pub signature: String, } diff --git a/prover/src/responses/beacon_state_response.rs b/prover/src/responses/beacon_state_response.rs index 14b0b91ee..56c5059db 100644 --- a/prover/src/responses/beacon_state_response.rs +++ b/prover/src/responses/beacon_state_response.rs @@ -1,11 +1,13 @@ -use ethereum_consensus::bellatrix::BeaconState; - -use ethereum_consensus::bellatrix::mainnet::{ - BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, - ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, - MAX_TRANSACTIONS_PER_PAYLOAD, MAX_VALIDATORS_PER_COMMITTEE, SLOTS_PER_HISTORICAL_ROOT, - SYNC_COMMITTEE_SIZE, VALIDATOR_REGISTRY_LIMIT, +use sync_committee_primitives::{ + consensus_types::BeaconState, + constants::{ + BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, + ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_BYTES_PER_TRANSACTION, + MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, MAX_VALIDATORS_PER_COMMITTEE, + SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_SIZE, VALIDATOR_REGISTRY_LIMIT, + }, }; + #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { version: String, diff --git a/prover/src/responses/finality_checkpoint_response.rs b/prover/src/responses/finality_checkpoint_response.rs index db251def7..c0e439ea9 100644 --- a/prover/src/responses/finality_checkpoint_response.rs +++ b/prover/src/responses/finality_checkpoint_response.rs @@ -1,4 +1,4 @@ -use ethereum_consensus::bellatrix::Checkpoint; +use sync_committee_primitives::consensus_types::Checkpoint; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub(crate) struct Response { diff --git a/prover/src/responses/validator_response.rs b/prover/src/responses/validator_response.rs index 6a37e0afd..d4969cc7a 100644 --- a/prover/src/responses/validator_response.rs +++ b/prover/src/responses/validator_response.rs @@ -1,4 +1,4 @@ -use ethereum_consensus::bellatrix::Validator; +use sync_committee_primitives::consensus_types::Validator; #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Response { diff --git a/prover/src/test.rs b/prover/src/test.rs index 9acc97fdd..a509501f4 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -1,86 +1,126 @@ use super::*; use base2::Base2; -use sync_committee_primitives::{ - types::{LightClientState, LightClientUpdate, SyncCommitteeUpdate}, - util::compute_sync_committee_period_at_slot, +use ethers::{ + prelude::{Http, Middleware, ProviderExt}, + providers::Provider, }; - -use ethereum_consensus::{ - bellatrix::compute_domain, primitives::Root, signing::compute_signing_root, - state_transition::Context, +use ssz_rs::{ + calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, + Merkleized, SszVariableOrIndex, }; -use ssz_rs::{calculate_multi_merkle_root, is_valid_merkle_branch, GeneralizedIndex, Merkleized}; use std::time::Duration; use sync_committee_primitives::{ - types::{AncestorBlock, FinalityProof, DOMAIN_SYNC_COMMITTEE, GENESIS_VALIDATORS_ROOT}, - util::compute_fork_version, + constants::{Root, DOMAIN_SYNC_COMMITTEE, GENESIS_FORK_VERSION, GENESIS_VALIDATORS_ROOT}, + types::LightClientState, + util::{compute_domain, compute_fork_version, compute_signing_root}, +}; +use sync_committee_verifier::{ + signature_verification::verify_aggregate_signature, verify_sync_committee_attestation, }; -use sync_committee_verifier::{verify_sync_committee_attestation, SignatureVerifier}; use tokio::time; use tokio_stream::{wrappers::IntervalStream, StreamExt}; -const NODE_URL: &'static str = "http://localhost:5052"; +const CONSENSUS_NODE_URL: &'static str = "http://localhost:3500"; +const EL_NODE_URL: &'static str = "http://localhost:8545"; + +async fn wait_for_el() { + let provider = Provider::::connect(EL_NODE_URL).await; + let sub = provider.watch_blocks().await.unwrap(); + let _ = sub.take(10).collect::>(); +} #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn fetch_block_header_works() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block_header = sync_committee_prover.fetch_header("head").await; assert!(block_header.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn fetch_block_works() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block = sync_committee_prover.fetch_block("head").await; assert!(block.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_sync_committee_works() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); - let block = sync_committee_prover.fetch_sync_committee("head").await; - assert!(block.is_ok()); +#[tokio::test] +async fn fetch_validator_works() { + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let validator = sync_committee_prover.fetch_validator("head", "0").await; + assert!(validator.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] -async fn fetch_validator_works() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); - let validator = sync_committee_prover.fetch_validator("head", "48").await; +#[tokio::test] +async fn fetch_processed_sync_committee_works() { + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let validator = sync_committee_prover.fetch_processed_sync_committee("head").await; assert!(validator.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] #[ignore] -async fn fetch_processed_sync_committee_works() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); - let validator = sync_committee_prover.fetch_processed_sync_committee("head").await; - assert!(validator.is_ok()); +async fn generate_indexes() { + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let execution_payload_index = get_generalized_index( + &beacon_state, + &[SszVariableOrIndex::Name("latest_execution_payload_header")], + ); + let next_sync = + get_generalized_index(&beacon_state, &[SszVariableOrIndex::Name("next_sync_committee")]); + let finalized = + get_generalized_index(&beacon_state, &[SszVariableOrIndex::Name("finalized_checkpoint")]); + let execution_payload_root = get_generalized_index( + &beacon_state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("state_root")], + ); + let block_number = get_generalized_index( + &beacon_state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("block_number")], + ); + let timestamp = get_generalized_index( + &beacon_state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("timestamp")], + ); + + dbg!(execution_payload_index); + dbg!(next_sync); + dbg!(finalized); + dbg!(execution_payload_root); + dbg!(block_number); + dbg!(timestamp); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn fetch_beacon_state_works() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let beacon_state = sync_committee_prover.fetch_beacon_state("head").await; assert!(beacon_state.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn state_root_and_block_header_root_matches() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let mut beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let block_header = sync_committee_prover.fetch_header(&beacon_state.slot.to_string()).await; @@ -89,23 +129,25 @@ async fn state_root_and_block_header_root_matches() { let block_header = block_header.unwrap(); let hash_tree_root = beacon_state.hash_tree_root(); - assert!(block_header.state_root == hash_tree_root.unwrap()); + assert_eq!(block_header.state_root, hash_tree_root.unwrap()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn fetch_finality_checkpoints_work() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; assert!(finality_checkpoint.is_ok()); } #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn test_finalized_header() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let proof = ssz_rs::generate_proof(&mut state, &vec![FINALIZED_ROOT_INDEX as usize]); @@ -129,30 +171,27 @@ async fn test_finalized_header() { #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn test_execution_payload_proof() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); - let finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let block_id = finalized_state.slot.to_string(); - let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); + let execution_payload_proof = prove_execution_payload(&mut finalized_state).unwrap(); let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); // verify the associated execution header of the finalized beacon header. let mut execution_payload = execution_payload_proof.clone(); let multi_proof_vec = execution_payload.multi_proof; - let multi_proof_nodes = multi_proof_vec - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); let execution_payload_root = calculate_multi_merkle_root( &[ Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), execution_payload.block_number.hash_tree_root().unwrap(), execution_payload.timestamp.hash_tree_root().unwrap(), ], - &multi_proof_nodes, + &multi_proof_vec, &[ GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), @@ -168,18 +207,14 @@ async fn test_execution_payload_proof() { assert_eq!(execution_payload_root, execution_payload_hash_tree_root); - let execution_payload_branch = execution_payload - .execution_payload_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); + let execution_payload_branch = execution_payload.execution_payload_branch.iter(); let is_merkle_branch_valid = is_valid_merkle_branch( &execution_payload_root, - execution_payload_branch.iter(), + execution_payload_branch, EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0, - &Node::from_bytes(finalized_header.clone().state_root.as_ref().try_into().unwrap()), + &finalized_header.state_root, ); assert!(is_merkle_branch_valid); @@ -187,43 +222,30 @@ async fn test_execution_payload_proof() { #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn test_sync_committee_update_proof() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); - let finalized_header = sync_committee_prover.fetch_header("head").await.unwrap(); - - let finalized_state = sync_committee_prover - .fetch_beacon_state(&finalized_header.slot.to_string()) - .await - .unwrap(); + let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let block_id = finalized_state.slot.to_string(); + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - let sync_committee_proof = prove_sync_committee_update(finalized_state.clone()).unwrap(); + let sync_committee_proof = prove_sync_committee_update(&mut finalized_state).unwrap(); - let sync_committee_proof = sync_committee_proof - .into_iter() - .map(|node| Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice")) - .collect::>(); let mut sync_committee = finalized_state.next_sync_committee; let calculated_finalized_root = calculate_multi_merkle_root( - &[Node::from_bytes(sync_committee.hash_tree_root().unwrap().as_ref().try_into().unwrap())], - &sync_committee_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(), + &[sync_committee.hash_tree_root().unwrap()], + &sync_committee_proof, &[GeneralizedIndex(NEXT_SYNC_COMMITTEE_INDEX as usize)], ); assert_eq!(calculated_finalized_root.as_bytes(), finalized_header.state_root.as_bytes()); - let next_sync_committee_branch = sync_committee_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); let is_merkle_branch_valid = is_valid_merkle_branch( &Node::from_bytes(sync_committee.hash_tree_root().unwrap().as_ref().try_into().unwrap()), - next_sync_committee_branch.iter(), + sync_committee_proof.iter(), NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, NEXT_SYNC_COMMITTEE_INDEX as usize, &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), @@ -234,12 +256,17 @@ async fn test_sync_committee_update_proof() { #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn test_prover() { - env_logger::init(); + use log::LevelFilter; + env_logger::builder() + .filter_module("prover", LevelFilter::Debug) + .format_module_path(false) + .init(); + wait_for_el().await; let mut stream = IntervalStream::new(time::interval(Duration::from_secs(12 * 12))); - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block_id = "head"; @@ -259,190 +286,20 @@ async fn test_prover() { let mut count = 0; while let Some(_ts) = stream.next().await { - let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await.unwrap(); - if finality_checkpoint.finalized.root == Node::default() || - finality_checkpoint.finalized.epoch <= client_state.latest_finalized_epoch || - finality_checkpoint.finalized.root == - client_state.finalized_header.clone().hash_tree_root().unwrap() - { - continue - } - - println!("A new epoch has been finalized {}", finality_checkpoint.finalized.epoch); - - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.finalized.root.as_bytes()); - block_id.insert_str(0, "0x"); - block_id - }; - - let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); - let finalized_state = sync_committee_prover - .fetch_beacon_state(finalized_header.slot.to_string().as_str()) + let light_client_update = if let Some(update) = sync_committee_prover + .fetch_light_client_update(client_state.clone(), "prover") .await - .unwrap(); - let execution_payload_proof = prove_execution_payload(finalized_state.clone()).unwrap(); - - let mut attested_epoch = finality_checkpoint.finalized.epoch + 2; - // Get attested header and the signature slot - - let mut attested_slot = attested_epoch * SLOTS_PER_EPOCH; - // Due to the fact that all slots in an epoch can be missed we are going to try and fetch - // the attested block from four possible epochs. - let mut attested_epoch_loop_count = 0; - let (attested_block_header, signature_block) = loop { - if attested_epoch_loop_count == 4 { - panic!("Could not fetch any block from the attested epoch after going through four epochs, your Eth devnet is fucked") - } - // If we have maxed out the slots in the current epoch and still didn't find any block, - // we move to the next epoch - if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == - attested_slot - { - // No block was found in attested epoch we move to the next possible attested epoch - println!( - "No slots found in epoch {attested_epoch} Moving to the next possible epoch {}", - attested_epoch + 1 - ); - std::thread::sleep(Duration::from_secs(24)); - attested_epoch += 1; - attested_slot = attested_epoch * SLOTS_PER_EPOCH; - attested_epoch_loop_count += 1; - } - - if let Ok(header) = - sync_committee_prover.fetch_header(attested_slot.to_string().as_str()).await - { - let mut signature_slot = header.slot + 1; - let mut loop_count = 0; - let signature_block = loop { - if loop_count == 2 { - break None - } - if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == - signature_slot - { - println!("Waiting for signature block for attested header"); - std::thread::sleep(Duration::from_secs(24)); - signature_slot = header.slot + 1; - loop_count += 1; - } - if let Ok(signature_block) = - sync_committee_prover.fetch_block(signature_slot.to_string().as_str()).await - { - break Some(signature_block) - } - signature_slot += 1; - }; - // If the next block does not have sufficient sync committee participants - if let Some(signature_block) = signature_block { - if signature_block - .body - .sync_aggregate - .sync_committee_bits - .as_bitslice() - .count_ones() < (2 * (SYNC_COMMITTEE_SIZE)) / 3 - { - attested_slot += 1; - println!("Signature block does not have sufficient sync committee participants -> participants {}", signature_block.body.sync_aggregate.sync_committee_bits.as_bitslice().count_ones()); - continue - } - break (header, signature_block) - } else { - println!("No signature block found in {attested_epoch} Moving to the next possible epoch {}", attested_epoch + 1); - std::thread::sleep(Duration::from_secs(24)); - attested_epoch += 1; - attested_slot = attested_epoch * SLOTS_PER_EPOCH; - attested_epoch_loop_count += 1; - continue - } - } - attested_slot += 1 - }; - - let attested_state = sync_committee_prover - .fetch_beacon_state(attested_block_header.slot.to_string().as_str()) - .await - .unwrap(); - - let finalized_hash_tree_root = finalized_header.clone().hash_tree_root().unwrap(); - println!("{:?}, {}", attested_state.finalized_checkpoint, attested_state.slot); - println!("{:?}, {}", finalized_hash_tree_root, finalized_header.slot); - - assert_eq!(finalized_hash_tree_root, attested_state.finalized_checkpoint.root); - - let finality_proof = FinalityProof { - epoch: finality_checkpoint.finalized.epoch, - finality_branch: prove_finalized_header(attested_state.clone()).unwrap(), - }; - - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - let update_attested_period = - compute_sync_committee_period_at_slot(attested_block_header.slot); - - let sync_committee_update = if state_period == update_attested_period { - let sync_committee_proof = prove_sync_committee_update(attested_state.clone()).unwrap(); - - let sync_committee_proof = sync_committee_proof - .into_iter() - .map(|node| { - Bytes32::try_from(node.as_bytes()).expect("Node is always 32 byte slice") - }) - .collect::>(); - - Some(SyncCommitteeUpdate { - next_sync_committee: attested_state.next_sync_committee, - next_sync_committee_branch: sync_committee_proof, - }) + .unwrap() + { + update } else { - None - }; - - let mut i = finalized_header.slot - 1; - let mut ancestor_blocks = vec![]; - while ancestor_blocks.len() < 5 { - if (finalized_header.slot - i) > 100 { - break - } - if let Ok(ancestor_header) = - sync_committee_prover.fetch_header(i.to_string().as_str()).await - { - let ancestry_proof = - prove_block_roots_proof(finalized_state.clone(), ancestor_header.clone()) - .unwrap(); - let header_state = - sync_committee_prover.fetch_beacon_state(i.to_string().as_str()).await.unwrap(); - let execution_payload_proof = prove_execution_payload(header_state).unwrap(); - ancestor_blocks.push(AncestorBlock { - header: ancestor_header, - execution_payload: execution_payload_proof, - ancestry_proof, - }) - } - i -= 1; - } - - println!("\nAncestor blocks count: \n {:?} \n", ancestor_blocks.len()); - - // construct light client - let light_client_update = LightClientUpdate { - attested_header: attested_block_header, - sync_committee_update, - finalized_header, - execution_payload: execution_payload_proof, - finality_proof, - sync_aggregate: signature_block.body.sync_aggregate, - signature_slot: signature_block.slot, - ancestor_blocks: vec![], + continue }; - client_state = verify_sync_committee_attestation::( - client_state.clone(), - light_client_update, - ) - .unwrap(); - println!( + client_state = + verify_sync_committee_attestation(client_state.clone(), light_client_update).unwrap(); + debug!( + target: "prover", "Sucessfully verified Ethereum block at slot {:?}", client_state.finalized_header.slot ); @@ -455,11 +312,13 @@ async fn test_prover() { } } +#[ignore] #[cfg(test)] #[allow(non_snake_case)] -#[actix_rt::test] +#[tokio::test] async fn test_sync_committee_signature_verification() { - let sync_committee_prover = SyncCommitteeProver::new(NODE_URL.to_string()); + wait_for_el().await; + let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block = loop { let block = sync_committee_prover.fetch_block("head").await.unwrap(); if block.slot < 16 { @@ -480,31 +339,31 @@ async fn test_sync_committee_signature_verification() { let sync_committee_pubkeys = sync_committee.public_keys; - let participant_pubkeys = block + let non_participant_pubkeys = block .body .sync_aggregate .sync_committee_bits .iter() .zip(sync_committee_pubkeys.iter()) - .filter_map(|(bit, key)| if *bit { Some(key) } else { None }) + .filter_map(|(bit, key)| if !(*bit) { Some(key.clone()) } else { None }) .collect::>(); let fork_version = compute_fork_version(compute_epoch_at_slot(block.slot)); - let context = Context::for_mainnet(); let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().unwrap())), - &context, + GENESIS_FORK_VERSION, ) .unwrap(); - let signing_root = compute_signing_root(&mut attested_header, domain); + let signing_root = compute_signing_root(&mut attested_header, domain).unwrap(); - ethereum_consensus::crypto::fast_aggregate_verify( - &*participant_pubkeys, - signing_root.unwrap().as_bytes(), + verify_aggregate_signature( + &sync_committee.aggregate_public_key, + &non_participant_pubkeys, + signing_root.as_bytes().to_vec(), &block.body.sync_aggregate.sync_committee_signature, ) .unwrap(); diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index e381b0445..2bfc47504 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -6,14 +6,27 @@ authors = ["Polytope Labs"] [dependencies] sync-committee-primitives = { path= "../primitives", default-features = false } -ethereum-consensus = { git = "https://github.com/polytope-labs/ethereum-consensus", branch = "main", default-features = false } ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" , default-features = false } log = { version = "0.4.17", default-features = false } +anyhow = { version = "1.0.75", default-features = false } +ark-ec = { version = "0.4.2", default-features = false } +ark-bls12-381 = { version = "0.4.0", default-features = false } +bls_on_arkworks = { version = "0.2.2", default-features = false } [features] default = ["std"] std = [ "ssz-rs/std", - "log/std" + "log/std", + "sync-committee-primitives/std", + "log/std", + "anyhow/std", + "ark-ec/std", + "ark-bls12-381/std", + "bls_on_arkworks/std" ] -testing = ["sync-committee-primitives/testing"] +testnet = ["sync-committee-primitives/testnet"] +mainnet = ["sync-committee-primitives/mainnet"] + +[dev-dependencies] +hex = "0.4.3" \ No newline at end of file diff --git a/verifier/src/error.rs b/verifier/src/error.rs index 4fd88fabc..567e74c6a 100644 --- a/verifier/src/error.rs +++ b/verifier/src/error.rs @@ -5,16 +5,10 @@ pub enum Error { SyncCommitteeParticipantsTooLow, InvalidUpdate, DomainError, - FastAggregateError(ethereum_consensus::crypto::Error), InvalidMerkleBranch, InvalidRoot, MerkleizationError, -} - -impl From for Error { - fn from(error: ethereum_consensus::crypto::Error) -> Self { - Error::FastAggregateError(error) - } + SignatureVerification, } impl Display for Error { @@ -25,10 +19,10 @@ impl Display for Error { }, Error::InvalidUpdate => write!(f, "Invalid update"), Error::DomainError => write!(f, "Couldn't get domain"), - Error::FastAggregateError(err) => write!(f, "Fast aggregate error {:?}", err), Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), Error::InvalidRoot => write!(f, "Invalid root"), Error::MerkleizationError => write!(f, "Merkleization error"), + Error::SignatureVerification => write!(f, "Signature verification failed"), } } } diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 56958c01c..157da6a93 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -4,43 +4,34 @@ extern crate alloc; pub mod error; +pub mod signature_verification; -use crate::error::Error; +use crate::{error::Error, signature_verification::verify_aggregate_signature}; use alloc::vec::Vec; -use ethereum_consensus::{ - bellatrix::{compute_domain, mainnet::SYNC_COMMITTEE_SIZE, Checkpoint}, - crypto::{PublicKey, Signature}, - primitives::Root, - signing::compute_signing_root, - state_transition::Context, -}; use ssz_rs::{ - calculate_merkle_root, calculate_multi_merkle_root, prelude::is_valid_merkle_branch, - GeneralizedIndex, Merkleized, Node, + calculate_multi_merkle_root, prelude::is_valid_merkle_branch, GeneralizedIndex, Merkleized, + Node, }; use sync_committee_primitives::{ - types::{ - AncestryProof, BLOCK_ROOTS_INDEX, BLOCK_ROOTS_INDEX_LOG2, DOMAIN_SYNC_COMMITTEE, - EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, + consensus_types::Checkpoint, + constants::{ + Root, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_INDEX_LOG2, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, FINALIZED_ROOT_INDEX, FINALIZED_ROOT_INDEX_LOG2, - GENESIS_VALIDATORS_ROOT, HISTORICAL_BATCH_BLOCK_ROOTS_INDEX, HISTORICAL_ROOTS_INDEX, - HISTORICAL_ROOTS_INDEX_LOG2, NEXT_SYNC_COMMITTEE_INDEX, NEXT_SYNC_COMMITTEE_INDEX_LOG2, + GENESIS_FORK_VERSION, GENESIS_VALIDATORS_ROOT, NEXT_SYNC_COMMITTEE_INDEX, + NEXT_SYNC_COMMITTEE_INDEX_LOG2, + }, + util::{ + compute_domain, compute_epoch_at_slot, compute_fork_version, compute_signing_root, + compute_sync_committee_period_at_slot, }, - util::{compute_epoch_at_slot, compute_fork_version, compute_sync_committee_period_at_slot}, }; -pub type LightClientState = sync_committee_primitives::types::LightClientState; -pub type LightClientUpdate = - sync_committee_primitives::types::LightClientUpdate; - -/// Verify sync committee signatures -pub trait BlsVerify { - fn verify(public_keys: &[&PublicKey], msg: &[u8], signature: &Signature) -> Result<(), Error>; -} +pub type LightClientState = sync_committee_primitives::types::LightClientState; +pub type LightClientUpdate = sync_committee_primitives::types::LightClientUpdate; /// This function simply verifies a sync committee's attestation & it's finalized counterpart. -pub fn verify_sync_committee_attestation( +pub fn verify_sync_committee_attestation( trusted_state: LightClientState, update: LightClientUpdate, ) -> Result { @@ -94,30 +85,32 @@ pub fn verify_sync_committee_attestation( let sync_committee_pubkeys = sync_committee.public_keys; - let participant_pubkeys = sync_committee_bits + let non_participant_pubkeys = sync_committee_bits .iter() .zip(sync_committee_pubkeys.iter()) - .filter_map(|(bit, key)| if *bit { Some(key) } else { None }) + .filter_map(|(bit, key)| if !(*bit) { Some(key.clone()) } else { None }) .collect::>(); let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); - let context = Context::for_mainnet(); let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().map_err(|_| Error::InvalidRoot)?)), - &context, + GENESIS_FORK_VERSION, ) .map_err(|_| Error::InvalidUpdate)?; - let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain); + let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain) + .map_err(|_| Error::InvalidRoot)?; - V::verify( - &*participant_pubkeys, - signing_root.map_err(|_| Error::InvalidRoot)?.as_bytes(), + verify_aggregate_signature( + &trusted_state.current_sync_committee.aggregate_public_key, + &non_participant_pubkeys, + signing_root.as_bytes().to_vec(), &update.sync_aggregate.sync_committee_signature, - )?; + ) + .map_err(|_| Error::SignatureVerification)?; // Verify that the `finality_branch` confirms `finalized_header` // to match the finalized checkpoint root saved in the state of `attested_header`. @@ -131,16 +124,9 @@ pub fn verify_sync_committee_attestation( .map_err(|_| Error::InvalidRoot)?, }; - let branch = update - .finality_proof - .finality_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( &finalized_checkpoint.hash_tree_root().map_err(|_| Error::InvalidRoot)?, - branch.iter(), + update.finality_proof.finality_branch.iter(), FINALIZED_ROOT_INDEX_LOG2 as usize, FINALIZED_ROOT_INDEX as usize, &update.attested_header.state_root, @@ -152,11 +138,6 @@ pub fn verify_sync_committee_attestation( // verify the associated execution header of the finalized beacon header. let mut execution_payload = update.execution_payload; - let multi_proof_vec = execution_payload.multi_proof; - let multi_proof_nodes = multi_proof_vec - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); let execution_payload_root = calculate_multi_merkle_root( &[ Node::from_bytes( @@ -172,7 +153,7 @@ pub fn verify_sync_committee_attestation( .map_err(|_| Error::InvalidRoot)?, execution_payload.timestamp.hash_tree_root().map_err(|_| Error::InvalidRoot)?, ], - &multi_proof_nodes, + &execution_payload.multi_proof, &[ GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), @@ -180,15 +161,9 @@ pub fn verify_sync_committee_attestation( ], ); - let execution_payload_branch = execution_payload - .execution_payload_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( &execution_payload_root, - execution_payload_branch.iter(), + execution_payload.execution_payload_branch.iter(), EXECUTION_PAYLOAD_INDEX_LOG2 as usize, EXECUTION_PAYLOAD_INDEX as usize, &update.finalized_header.state_root, @@ -206,17 +181,12 @@ pub fn verify_sync_committee_attestation( Err(Error::InvalidUpdate)? } - let next_sync_committee_branch = sync_committee_update - .next_sync_committee_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); let is_merkle_branch_valid = is_valid_merkle_branch( &sync_committee_update .next_sync_committee .hash_tree_root() .map_err(|_| Error::MerkleizationError)?, - next_sync_committee_branch.iter(), + sync_committee_update.next_sync_committee_branch.iter(), NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize, NEXT_SYNC_COMMITTEE_INDEX as usize, &update.attested_header.state_root, @@ -227,170 +197,6 @@ pub fn verify_sync_committee_attestation( } } - // verify the ancestry proofs - for mut ancestor in update.ancestor_blocks { - match ancestor.ancestry_proof { - AncestryProof::BlockRoots { block_roots_proof, block_roots_branch } => { - let block_header_branch = block_roots_proof - .block_header_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let block_roots_root = calculate_merkle_root( - &ancestor.header.hash_tree_root().map_err(|_| Error::MerkleizationError)?, - &*block_header_branch, - &GeneralizedIndex(block_roots_proof.block_header_index as usize), - ); - - let block_roots_branch_node = block_roots_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - - let is_merkle_branch_valid = is_valid_merkle_branch( - &block_roots_root, - block_roots_branch_node.iter(), - BLOCK_ROOTS_INDEX_LOG2 as usize, - BLOCK_ROOTS_INDEX as usize, - &update.finalized_header.state_root, - ); - if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; - } - }, - AncestryProof::HistoricalRoots { - block_roots_proof, - historical_batch_proof, - historical_roots_proof, - historical_roots_index, - historical_roots_branch, - } => { - let block_header_branch = block_roots_proof - .block_header_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let block_roots_root = calculate_merkle_root( - &ancestor - .header - .clone() - .hash_tree_root() - .map_err(|_| Error::MerkleizationError)?, - &block_header_branch, - &GeneralizedIndex(block_roots_proof.block_header_index as usize), - ); - - let historical_batch_proof_nodes = historical_batch_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let historical_batch_root = calculate_merkle_root( - &block_roots_root, - &historical_batch_proof_nodes, - &GeneralizedIndex(HISTORICAL_BATCH_BLOCK_ROOTS_INDEX as usize), - ); - - let historical_roots_proof_nodes = historical_roots_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let historical_roots_root = calculate_merkle_root( - &historical_batch_root, - &historical_roots_proof_nodes, - &GeneralizedIndex(historical_roots_index as usize), - ); - - let historical_roots_branch_nodes = historical_roots_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &historical_roots_root, - historical_roots_branch_nodes.iter(), - HISTORICAL_ROOTS_INDEX_LOG2 as usize, - HISTORICAL_ROOTS_INDEX as usize, - &Node::from_bytes( - update - .finalized_header - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ); - - if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; - } - }, - }; - - // verify the associated execution paylaod header. - let execution_payload = ancestor.execution_payload; - let multi_proof = execution_payload - .multi_proof - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let execution_payload_root = calculate_multi_merkle_root( - &[ - Node::from_bytes( - execution_payload - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - Node::from_bytes( - execution_payload - .block_number - .clone() - .hash_tree_root() - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - Node::from_bytes( - execution_payload - .timestamp - .clone() - .hash_tree_root() - .map_err(|_| Error::MerkleizationError)? - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), - ], - &multi_proof, - &[ - GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), - GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), - GeneralizedIndex(EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize), - ], - ); - - let execution_payload_branch = execution_payload - .execution_payload_branch - .iter() - .map(|node| Node::from_bytes(node.as_ref().try_into().unwrap())) - .collect::>(); - let is_merkle_branch_valid = is_valid_merkle_branch( - &execution_payload_root, - execution_payload_branch.iter(), - EXECUTION_PAYLOAD_INDEX_LOG2 as usize, - EXECUTION_PAYLOAD_INDEX as usize, - &Node::from_bytes( - ancestor.header.state_root.as_ref().try_into().map_err(|_| Error::InvalidRoot)?, - ), - ); - - if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; - } - } - let new_light_client_state = if let Some(sync_committee_update) = update.sync_committee_update { LightClientState { finalized_header: update.finalized_header, @@ -404,13 +210,3 @@ pub fn verify_sync_committee_attestation( Ok(new_light_client_state) } - -pub struct SignatureVerifier; - -impl BlsVerify for SignatureVerifier { - fn verify(public_keys: &[&PublicKey], msg: &[u8], signature: &Signature) -> Result<(), Error> { - ethereum_consensus::crypto::fast_aggregate_verify(public_keys, msg, signature)?; - - Ok(()) - } -} diff --git a/verifier/src/signature_verification.rs b/verifier/src/signature_verification.rs new file mode 100644 index 000000000..06aa4f27c --- /dev/null +++ b/verifier/src/signature_verification.rs @@ -0,0 +1,108 @@ +use alloc::vec::Vec; +use anyhow::anyhow; +use ark_bls12_381::Bls12_381; +use ark_ec::{pairing::Pairing, AffineRepr}; +use bls_on_arkworks::{ + hash_to_point, pubkey_to_point, signature_to_point, + types::{BLS12381Pairing, G1AffinePoint, G1ProjectivePoint, G2AffinePoint, Signature}, + DST_ETHEREUM, +}; +use sync_committee_primitives::constants::BlsPublicKey; + +pub fn pubkey_to_projective(compressed_key: &BlsPublicKey) -> anyhow::Result { + let affine_point = pubkey_to_point(&compressed_key.to_vec()).map_err(|e| anyhow!("{:?}", e))?; + Ok(affine_point.into()) +} + +fn subtract_points_from_aggregate( + aggregate: &BlsPublicKey, + points: &[BlsPublicKey], +) -> anyhow::Result { + let aggregate = pubkey_to_projective(aggregate)?; + let points = points + .iter() + .map(|point| pubkey_to_projective(point)) + .collect::, _>>()?; + let subset_aggregate = points.into_iter().fold(aggregate, |acc, point| acc - point); + Ok(subset_aggregate) +} + +fn pairing(u: G2AffinePoint, v: G1AffinePoint) -> BLS12381Pairing { + Bls12_381::pairing(v, u) +} + +/// Adapted from https://github.com/ArnaudBrousseau/bls_on_arkworks/blob/main/src/lib.rs#L335 +/// Verifies an aggregate bls12-381 signature from ethereum sync-committee +/// Expects signature subgroup to be valid +pub fn verify_aggregate_signature( + aggregate: &BlsPublicKey, + non_participants: &[BlsPublicKey], + msg: Vec, + signature: &Signature, +) -> anyhow::Result<()> { + let subset_aggregate = subtract_points_from_aggregate(aggregate, non_participants)?; + let aggregate_key_point: G1AffinePoint = subset_aggregate.into(); + let signature = signature_to_point(signature).map_err(|e| anyhow!("{:?}", e))?; + let dst = DST_ETHEREUM.as_bytes().to_vec(); + + let q = hash_to_point(&msg, &dst); + + let c1 = pairing(q, aggregate_key_point); + + // From the spec: + // > When the signature variant is minimal-pubkey-size, P is the distinguished point P1 that + // > generates the group G1. + // + let p = G1AffinePoint::generator(); + + let c2 = pairing(signature, p); + + if c1 == c2 { + Ok(()) + } else { + Err(anyhow!("Aggregate signature verification failed")) + } +} + +#[cfg(test)] +mod tests { + use crate::signature_verification::verify_aggregate_signature; + + #[test] + fn test_signature_verification() { + let pks = vec![ + hex::decode("882417eb57b98c7dd8e4adb5d4c7b59cb46ad093072f10db99e02597e3432fe094e2698df4c3bf65ff757ac602182f87").unwrap(), + hex::decode("8ef016d09c49af41d028fdf6ef04972d11f6931bf57f0922df4e77a52847227c880581eebb6b485af1d68bb4895cc35c").unwrap(), + hex::decode("88b92def24f441be1eba41ff76182e0eb224cf06e751df45635db1530bf37765861c82a8f381f81f6ac6a2b3d3d9875b").unwrap(), + hex::decode("afc92546e835a4dbe31e2b3a4e6f44a94466a6f9b5752113b9b828349254582eb7b5b596a32b79fc936a82db8802af0c").unwrap(), + hex::decode("8391e3a00add4bcbe4c339fa7c35238855861cbbc89ceefa6832de6b28bc378a0d038a329636d53404e0deaa444bdfd0").unwrap(), + hex::decode("9102e77817e572a16fab849f7681d130d10876880d7fe05d40091af93592150ad4829145a7327d125e71a8847a368121").unwrap(), + hex::decode("8d966a5cfd601661bfb6e15b8c849d3bd85006aec628b44e88022b01054be5159de73f16504a969d6009a59d9214b043").unwrap(), + hex::decode("b6778f88f9df6d5d09baf9bccd2ea1e4cb88469239a0a14ffcca37fc1c29bad69711dc64fc4e1bb1be0792b005a1729a").unwrap(), + hex::decode("afc664d1160d2a55fab55fe9d94551b18aa2543f218b9fbdd733509463416c96ee13da6cf75f97165922ca61372c6fb7").unwrap(), + hex::decode("ad413282bc501315d2cccf8e2a5dd54a5baca851515a04e5f252c98cfeeb670604fa48c707127017e0b8cda218d98207").unwrap() + ]; + + let message = + hex::decode("813a89a296973e35545cfa74fe3efd172a7d19443c97c625d699e9737229b0a2") + .unwrap(); + let aggregate_signature = hex::decode("a1abfcf9bd54b7a003e1f45f7543b194d8d25b816577b02ee4f1c99aa9821c620be6ecedbc8c5fab64d343a6cc832040029040e591fa24db54f5441f28d73918775e8feeac6177c9e016d2576b982d1cce453896a8aace2bda7374e5a76ce213").unwrap(); + let aggregate_pub_key = hex::decode("a3f2da752bd1dfc7288b46cc061668856e0cefa93ba6e8ff4699f355138f63a541fdb3444ddebcdce695d6313fa4b244").unwrap().try_into().unwrap(); + + let bit_vector = hex::decode("01000100010001000100").unwrap(); + + let non_participants = pks + .into_iter() + .zip(bit_vector) + .filter_map(|(pk, bit)| if bit == 0 { Some(pk.try_into().unwrap()) } else { None }) + .collect::>(); + + verify_aggregate_signature( + &aggregate_pub_key, + &non_participants, + message, + &aggregate_signature, + ) + .unwrap() + } +} From e10e03b6a093e4e4391bc37d1af6b6f87aff3b5d Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:13:26 +0100 Subject: [PATCH 171/182] Rewrite of method for fetching client updates (#30) * refactor of prover * nit * some fixes * cargo update * fix lower bound for valid signature block search * doc * nit * nit * minor fix --- .github/workflows/test.yml | 2 +- Cargo.lock | 64 ++++++++++++++- primitives/src/constants.rs | 12 +-- primitives/src/types.rs | 8 +- primitives/src/util.rs | 10 ++- prover/Cargo.toml | 4 +- prover/src/lib.rs | 158 ++++++++++++------------------------ prover/src/test.rs | 134 ++++++++++++++++++------------ verifier/src/error.rs | 17 ++-- verifier/src/lib.rs | 89 ++++++++++---------- 10 files changed, 264 insertions(+), 234 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d506ab68..c704c3b1a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: run: | git clone https://github.com/polytope-labs/eth-pos-devnet.git cd eth-pos-devnet - docker compose up & + docker compose up -d ../scripts/wait_for_tcp_port_opening.sh localhost 3500 ../scripts/wait_for_tcp_port_opening.sh localhost 8545 diff --git a/Cargo.lock b/Cargo.lock index 00e32893f..881214b8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1270,6 +1270,17 @@ dependencies = [ "yansi", ] +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + [[package]] name = "eyre" version = "0.6.8" @@ -2028,6 +2039,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -2078,6 +2095,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -2700,15 +2727,33 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.25.2", "winreg", ] +[[package]] +name = "reqwest-eventsource" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f03f570355882dd8d15acc3a313841e6e90eddbc76a93c748fd82cc13ba9f51" +dependencies = [ + "eventsource-stream", + "futures-core", + "futures-timer", + "mime", + "nom", + "pin-project-lite", + "reqwest", + "thiserror", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -3202,7 +3247,7 @@ dependencies = [ [[package]] name = "ssz-rs" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#81e9f63c93ca33f5f484ac301553f04912f2de23" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#6f5d37b7f92875b5cbb6ef60e8c498ca013943cb" dependencies = [ "as-any", "bitvec", @@ -3218,7 +3263,7 @@ dependencies = [ [[package]] name = "ssz-rs-derive" version = "0.8.0" -source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#81e9f63c93ca33f5f484ac301553f04912f2de23" +source = "git+https://github.com/polytope-labs/ssz-rs?branch=main#6f5d37b7f92875b5cbb6ef60e8c498ca013943cb" dependencies = [ "proc-macro2", "quote", @@ -3344,8 +3389,10 @@ dependencies = [ "ethers", "hex", "log", + "parity-scale-codec", "primitive-types", "reqwest", + "reqwest-eventsource", "serde", "serde_json", "ssz-rs", @@ -3866,6 +3913,19 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.64" diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index df7a0247e..04c24300f 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -33,6 +33,9 @@ pub const BLS_SIGNATURE_BYTES_LEN: usize = 96; pub const SYNC_COMMITTEE_SIZE: usize = 512; pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: Epoch = 256; +pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; +pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; +pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; pub const MAX_VALIDATORS_PER_COMMITTEE: usize = 2048; pub const EPOCHS_PER_ETH1_VOTING_PERIOD: Epoch = 64; @@ -95,9 +98,6 @@ pub mod testnet { pub const BELLATRIX_FORK_EPOCH: Epoch = 112260; pub const CAPELLA_FORK_EPOCH: Epoch = u64::MAX; pub const CAPELLA_FORK_VERSION: Version = [3, 0, 16, 32]; - pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; - pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; - pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; } #[cfg(feature = "mainnet")] @@ -113,9 +113,6 @@ pub mod mainnet { pub const BELLATRIX_FORK_EPOCH: Epoch = 144896; pub const CAPELLA_FORK_EPOCH: Epoch = u64::MAX; pub const CAPELLA_FORK_VERSION: Version = [3, 0, 0, 0]; - pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; - pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; - pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; } #[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] @@ -132,7 +129,4 @@ pub mod devnet { pub const BELLATRIX_FORK_EPOCH: Epoch = 0; pub const CAPELLA_FORK_EPOCH: Epoch = 2; pub const CAPELLA_FORK_VERSION: Version = hex!("52525503"); - pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; - pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; - pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; } diff --git a/primitives/src/types.rs b/primitives/src/types.rs index c0ca114a5..de5ad4be4 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -74,12 +74,12 @@ pub struct AncestorBlock { } /// Holds the latest sync committee as well as an ssz proof for it's existence -/// in a finalized header. +/// in an attested header. #[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] pub struct SyncCommitteeUpdate { - // actual sync committee + /// actual sync committee pub next_sync_committee: SyncCommittee, - // sync committee, ssz merkle proof. + /// next sync committee, ssz merkle proof. pub next_sync_committee_branch: Vec, } @@ -90,7 +90,7 @@ pub struct LightClientState { pub finalized_header: BeaconBlockHeader, /// Latest finalized epoch pub latest_finalized_epoch: u64, - // Sync committees corresponding to the finalized header + /// Sync committees corresponding to the finalized header pub current_sync_committee: SyncCommittee, pub next_sync_committee: SyncCommittee, } diff --git a/primitives/src/util.rs b/primitives/src/util.rs index 4c1abaab4..227d5e503 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -1,7 +1,7 @@ use crate::{ consensus_types::ForkData, constants::{ - Domain, Root, Version, ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, BELLATRIX_FORK_EPOCH, + Domain, Root, Slot, Version, ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, BELLATRIX_FORK_EPOCH, BELLATRIX_FORK_VERSION, CAPELLA_FORK_EPOCH, CAPELLA_FORK_VERSION, EPOCHS_PER_SYNC_COMMITTEE_PERIOD, GENESIS_FORK_VERSION, SLOTS_PER_EPOCH, }, @@ -11,7 +11,13 @@ use alloc::{vec, vec::Vec}; use anyhow::anyhow; use ssz_rs::prelude::*; -/// Return the sync committe period at the given ``epoch`` +/// Returns true if the next epoch is the start of a new sync committee period +pub fn should_get_sync_committee_update(slot: Slot) -> bool { + let next_epoch = compute_epoch_at_slot(slot) + 1; + next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0 +} + +/// Return the sync committee period at the given ``epoch`` pub fn compute_sync_committee_period(epoch: u64) -> u64 { epoch / EPOCHS_PER_SYNC_COMMITTEE_PERIOD } diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 1abc687ed..21ae744da 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -25,13 +25,15 @@ primitive-types = { version = "0.12.1", features = ["serde_no_std", "impl-codec" log = "0.4.20" hex = "0.4.3" + [dev-dependencies] env_logger = "0.10.0" sync-committee-primitives = { path= "../primitives" } sync-committee-verifier = { path= "../verifier" } ethers = { version = "2.0.8", features = ["ws"] } tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"]} - +parity-scale-codec = "3.2.2" +reqwest-eventsource = "0.4.0" [features] default = ["std"] diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 5fe4bfe4c..6cfc7366a 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -9,9 +9,8 @@ use anyhow::anyhow; use bls_on_arkworks::{point_to_pubkey, types::G1ProjectivePoint}; use log::debug; use reqwest::Client; -use std::time::Duration; use sync_committee_primitives::consensus_types::{ - BeaconBlock, BeaconBlockHeader, BeaconState, SyncCommittee, Validator, + BeaconBlock, BeaconBlockHeader, BeaconState, Checkpoint, SyncCommittee, Validator, }; use crate::{ @@ -25,7 +24,7 @@ use primitive_types::H256; use ssz_rs::{List, Merkleized, Node, Vector}; use sync_committee_primitives::{ constants::{ - BlsPublicKey, ValidatorIndex, BLOCK_ROOTS_INDEX, BYTES_PER_LOGS_BLOOM, + BlsPublicKey, Root, ValidatorIndex, BLOCK_ROOTS_INDEX, BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, @@ -40,8 +39,12 @@ use sync_committee_primitives::{ AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, LightClientUpdate, SyncCommitteeUpdate, }, - util::{compute_epoch_at_slot, compute_sync_committee_period_at_slot}, + util::{ + compute_epoch_at_slot, compute_sync_committee_period_at_slot, + should_get_sync_committee_update, + }, }; + use sync_committee_verifier::{signature_verification::pubkey_to_projective, LightClientState}; pub type BeaconStateType = BeaconState< @@ -217,127 +220,70 @@ impl SyncCommitteeProver { pub async fn fetch_light_client_update( &self, client_state: LightClientState, + finality_checkpoint: Checkpoint, debug_target: &str, ) -> Result, anyhow::Error> { - let finality_checkpoint = self.fetch_finalized_checkpoint().await?; - if finality_checkpoint.finalized.root == Node::default() || - finality_checkpoint.finalized.epoch <= client_state.latest_finalized_epoch || - finality_checkpoint.finalized.root == - client_state.finalized_header.clone().hash_tree_root()? + if finality_checkpoint.root == Node::default() || + finality_checkpoint.epoch <= client_state.latest_finalized_epoch { return Ok(None) } - debug!(target: debug_target, "A new epoch has been finalized {}", finality_checkpoint.finalized.epoch); - - let block_id = { - let mut block_id = hex::encode(finality_checkpoint.finalized.root.as_bytes()); + debug!(target: debug_target, "A new epoch has been finalized {}", finality_checkpoint.epoch); + // Find the highest block with the a threshhold number of sync committee signatures + let latest_header = self.fetch_header("head").await?; + let latest_root = latest_header.clone().hash_tree_root()?; + let get_block_id = |root: Root| { + let mut block_id = hex::encode(root.0.to_vec()); block_id.insert_str(0, "0x"); block_id }; + let mut block = self.fetch_block(&get_block_id(latest_root)).await?; + let min_signatures = (2 * SYNC_COMMITTEE_SIZE) / 3; + let state_period = + compute_sync_committee_period_at_slot(client_state.finalized_header.slot); + loop { + // If we get to an epoch that is less than the attested epoch for the last known + // finalized header we exit + if compute_epoch_at_slot(block.slot) < client_state.latest_finalized_epoch + 2 { + return Ok(None) + } - let finalized_header = self.fetch_header(&block_id).await?; - let mut finalized_state = - self.fetch_beacon_state(finalized_header.slot.to_string().as_str()).await?; - let execution_payload_proof = prove_execution_payload(&mut finalized_state)?; + let parent_root = block.parent_root; + let block_id = get_block_id(parent_root); + block = self.fetch_block(&block_id).await?; - let mut attested_epoch = finality_checkpoint.finalized.epoch + 2; - // Get attested header and the signature slot + let num_signatures = block.body.sync_aggregate.sync_committee_bits.count_ones(); - let mut attested_slot = attested_epoch * SLOTS_PER_EPOCH; - // Due to the fact that all slots in an epoch can be missed we are going to try and fetch - // the attested block from four possible epochs. - let mut attested_epoch_loop_count = 0; - let (attested_block_header, signature_block) = loop { - if attested_epoch_loop_count == 4 { - Err(anyhow!("Could not fetch any block from the attested epoch after going through four epochs"))? - } - // If we have maxed out the slots in the current epoch and still didn't find any block, - // we move to the next epoch - if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == - attested_slot + let signature_period = compute_sync_committee_period_at_slot(block.slot); + if num_signatures >= min_signatures && + (state_period..=state_period + 1).contains(&signature_period) { - // No block was found in attested epoch we move to the next possible attested epoch - debug!(target: debug_target, - "No slots found in epoch {attested_epoch} Moving to the next possible epoch {}", - attested_epoch + 1 - ); - tokio::time::sleep(Duration::from_secs(24)).await; - attested_epoch += 1; - attested_slot = attested_epoch * SLOTS_PER_EPOCH; - attested_epoch_loop_count += 1; + break } + } - if let Ok(header) = self.fetch_header(attested_slot.to_string().as_str()).await { - let mut signature_slot = header.slot + 1; - let mut loop_count = 0; - let signature_block = loop { - if loop_count == 2 { - break None - } - if (attested_epoch * SLOTS_PER_EPOCH).saturating_add(SLOTS_PER_EPOCH - 1) == - signature_slot - { - debug!(target: debug_target, "Waiting for signature block for attested header"); - tokio::time::sleep(Duration::from_secs(24)).await; - signature_slot = header.slot + 1; - loop_count += 1; - } - if let Ok(signature_block) = - self.fetch_block(signature_slot.to_string().as_str()).await - { - break Some(signature_block) - } - signature_slot += 1; - }; - // If the next block does not have sufficient sync committee participants - if let Some(signature_block) = signature_block { - if signature_block - .body - .sync_aggregate - .sync_committee_bits - .as_bitslice() - .count_ones() < (2 * (SYNC_COMMITTEE_SIZE)) / 3 - { - attested_slot += 1; - debug!(target:debug_target, "Signature block does not have sufficient sync committee participants -> participants {}", signature_block.body.sync_aggregate.sync_committee_bits.as_bitslice().count_ones()); - continue - } - break (header, signature_block) - } else { - debug!(target: debug_target,"No signature block found in {attested_epoch} Moving to the next possible epoch {}", attested_epoch + 1); - tokio::time::sleep(Duration::from_secs(24)).await; - attested_epoch += 1; - attested_slot = attested_epoch * SLOTS_PER_EPOCH; - attested_epoch_loop_count += 1; - continue - } - } - attested_slot += 1 - }; - + let attested_block_id = get_block_id(block.parent_root); + let attested_header = self.fetch_header(&attested_block_id).await?; let mut attested_state = - self.fetch_beacon_state(attested_block_header.slot.to_string().as_str()).await?; - - let finalized_hash_tree_root = finalized_header.clone().hash_tree_root()?; + self.fetch_beacon_state(&get_block_id(attested_header.state_root)).await?; - if cfg!(test) { - assert_eq!(finalized_hash_tree_root, attested_state.finalized_checkpoint.root); + if attested_state.finalized_checkpoint.root == Node::default() { + return Ok(None) } - + let finalized_block_id = get_block_id(attested_state.finalized_checkpoint.root); + let finalized_header = self.fetch_header(&finalized_block_id).await?; + let mut finalized_state = + self.fetch_beacon_state(&get_block_id(finalized_header.state_root)).await?; let finality_proof = FinalityProof { - epoch: finality_checkpoint.finalized.epoch, + epoch: attested_state.finalized_checkpoint.epoch, finality_branch: prove_finalized_header(&mut attested_state)?, }; - let state_period = compute_sync_committee_period_at_slot(finalized_header.slot); - - let update_attested_period = - compute_sync_committee_period_at_slot(attested_block_header.slot); + let execution_payload_proof = prove_execution_payload(&mut finalized_state)?; - let sync_committee_update = if state_period == update_attested_period { + let sync_committee_update = if should_get_sync_committee_update(attested_state.slot) { let sync_committee_proof = prove_sync_committee_update(&mut attested_state)?; - Some(SyncCommitteeUpdate { next_sync_committee: attested_state.next_sync_committee, next_sync_committee_branch: sync_committee_proof, @@ -348,23 +294,19 @@ impl SyncCommitteeProver { // construct light client let light_client_update = LightClientUpdate { - attested_header: attested_block_header, + attested_header, sync_committee_update, finalized_header, execution_payload: execution_payload_proof, finality_proof, - sync_aggregate: signature_block.body.sync_aggregate, - signature_slot: signature_block.slot, + sync_aggregate: block.body.sync_aggregate, + signature_slot: block.slot, }; Ok(Some(light_client_update)) } } -pub fn get_attested_epoch(finalized_epoch: u64) -> u64 { - finalized_epoch + 2 -} - pub fn prove_execution_payload( beacon_state: &mut BeaconStateType, ) -> anyhow::Result { diff --git a/prover/src/test.rs b/prover/src/test.rs index a509501f4..de293a47a 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -4,36 +4,39 @@ use ethers::{ prelude::{Http, Middleware, ProviderExt}, providers::Provider, }; +use reqwest_eventsource::EventSource; use ssz_rs::{ calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, Merkleized, SszVariableOrIndex, }; use std::time::Duration; use sync_committee_primitives::{ - constants::{Root, DOMAIN_SYNC_COMMITTEE, GENESIS_FORK_VERSION, GENESIS_VALIDATORS_ROOT}, + constants::{ + Root, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_INDEX_LOG2, GENESIS_FORK_VERSION, + GENESIS_VALIDATORS_ROOT, NEXT_SYNC_COMMITTEE_INDEX_LOG2, + }, types::LightClientState, util::{compute_domain, compute_fork_version, compute_signing_root}, }; use sync_committee_verifier::{ signature_verification::verify_aggregate_signature, verify_sync_committee_attestation, }; -use tokio::time; -use tokio_stream::{wrappers::IntervalStream, StreamExt}; +use tokio_stream::StreamExt; const CONSENSUS_NODE_URL: &'static str = "http://localhost:3500"; const EL_NODE_URL: &'static str = "http://localhost:8545"; -async fn wait_for_el() { +async fn wait_for_el(blocks: usize) { let provider = Provider::::connect(EL_NODE_URL).await; let sub = provider.watch_blocks().await.unwrap(); - let _ = sub.take(10).collect::>(); + let _ = sub.take(blocks).collect::>(); } #[cfg(test)] #[allow(non_snake_case)] #[tokio::test] async fn fetch_block_header_works() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block_header = sync_committee_prover.fetch_header("head").await; assert!(block_header.is_ok()); @@ -43,7 +46,7 @@ async fn fetch_block_header_works() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_block_works() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block = sync_committee_prover.fetch_block("head").await; assert!(block.is_ok()); @@ -53,7 +56,7 @@ async fn fetch_block_works() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_validator_works() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let validator = sync_committee_prover.fetch_validator("head", "0").await; assert!(validator.is_ok()); @@ -62,8 +65,9 @@ async fn fetch_validator_works() { #[cfg(test)] #[allow(non_snake_case)] #[tokio::test] +#[ignore] async fn fetch_processed_sync_committee_works() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let validator = sync_committee_prover.fetch_processed_sync_committee("head").await; assert!(validator.is_ok()); @@ -96,20 +100,21 @@ async fn generate_indexes() { &beacon_state.latest_execution_payload_header, &[SszVariableOrIndex::Name("timestamp")], ); - dbg!(execution_payload_index); dbg!(next_sync); dbg!(finalized); dbg!(execution_payload_root); dbg!(block_number); dbg!(timestamp); + + dbg!(next_sync.floor_log2()); } #[cfg(test)] #[allow(non_snake_case)] #[tokio::test] async fn fetch_beacon_state_works() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let beacon_state = sync_committee_prover.fetch_beacon_state("head").await; assert!(beacon_state.is_ok()); @@ -119,7 +124,7 @@ async fn fetch_beacon_state_works() { #[allow(non_snake_case)] #[tokio::test] async fn state_root_and_block_header_root_matches() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let mut beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); @@ -136,7 +141,7 @@ async fn state_root_and_block_header_root_matches() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_finality_checkpoints_work() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; assert!(finality_checkpoint.is_ok()); @@ -146,11 +151,11 @@ async fn fetch_finality_checkpoints_work() { #[allow(non_snake_case)] #[tokio::test] async fn test_finalized_header() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); - let proof = ssz_rs::generate_proof(&mut state, &vec![FINALIZED_ROOT_INDEX as usize]); + let proof = ssz_rs::generate_proof(&mut state, &vec![FINALIZED_ROOT_INDEX as usize]).unwrap(); let leaves = vec![Node::from_bytes( state @@ -163,7 +168,7 @@ async fn test_finalized_header() { )]; let root = calculate_multi_merkle_root( &leaves, - &proof.unwrap(), + &proof, &[GeneralizedIndex(FINALIZED_ROOT_INDEX as usize)], ); assert_eq!(root, state.hash_tree_root().unwrap()); @@ -173,7 +178,7 @@ async fn test_finalized_header() { #[allow(non_snake_case)] #[tokio::test] async fn test_execution_payload_proof() { - wait_for_el().await; + wait_for_el(10).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); @@ -212,8 +217,8 @@ async fn test_execution_payload_proof() { let is_merkle_branch_valid = is_valid_merkle_branch( &execution_payload_root, execution_payload_branch, - EXECUTION_PAYLOAD_INDEX.floor_log2() as usize, - GeneralizedIndex(EXECUTION_PAYLOAD_INDEX as usize).0, + EXECUTION_PAYLOAD_INDEX_LOG2 as usize, + EXECUTION_PAYLOAD_INDEX as usize, &finalized_header.state_root, ); @@ -224,7 +229,7 @@ async fn test_execution_payload_proof() { #[allow(non_snake_case)] #[tokio::test] async fn test_sync_committee_update_proof() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); @@ -244,11 +249,11 @@ async fn test_sync_committee_update_proof() { assert_eq!(calculated_finalized_root.as_bytes(), finalized_header.state_root.as_bytes()); let is_merkle_branch_valid = is_valid_merkle_branch( - &Node::from_bytes(sync_committee.hash_tree_root().unwrap().as_ref().try_into().unwrap()), + &sync_committee.hash_tree_root().unwrap(), sync_committee_proof.iter(), - NEXT_SYNC_COMMITTEE_INDEX.floor_log2() as usize, + NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize, NEXT_SYNC_COMMITTEE_INDEX as usize, - &Node::from_bytes(finalized_header.state_root.as_ref().try_into().unwrap()), + &finalized_header.state_root, ); assert!(is_merkle_branch_valid); @@ -259,18 +264,17 @@ async fn test_sync_committee_update_proof() { #[tokio::test] async fn test_prover() { use log::LevelFilter; + use parity_scale_codec::{Decode, Encode}; env_logger::builder() .filter_module("prover", LevelFilter::Debug) .format_module_path(false) .init(); - wait_for_el().await; - let mut stream = IntervalStream::new(time::interval(Duration::from_secs(12 * 12))); + wait_for_el(1).await; + let node_url = format!("{}/eth/v1/events?topics=finalized_checkpoint", CONSENSUS_NODE_URL); let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); - let block_id = "head"; - - let block_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + let block_header = sync_committee_prover.fetch_header("head").await.unwrap(); let state = sync_committee_prover .fetch_beacon_state(&block_header.slot.to_string()) @@ -285,29 +289,47 @@ async fn test_prover() { }; let mut count = 0; - while let Some(_ts) = stream.next().await { - let light_client_update = if let Some(update) = sync_committee_prover - .fetch_light_client_update(client_state.clone(), "prover") - .await - .unwrap() - { - update - } else { - continue - }; - - client_state = - verify_sync_committee_attestation(client_state.clone(), light_client_update).unwrap(); - debug!( - target: "prover", - "Sucessfully verified Ethereum block at slot {:?}", - client_state.finalized_header.slot - ); - - count += 1; - // For CI purposes we test finalization of three epochs - if count == 3 { - break + + let mut es = EventSource::get(node_url); + while let Some(event) = es.next().await { + match event { + Ok(reqwest_eventsource::Event::Message(msg)) => { + let message: EventResponse = serde_json::from_str(&msg.data).unwrap(); + let checkpoint = + Checkpoint { epoch: message.epoch.parse().unwrap(), root: message.block }; + let light_client_update = if let Some(update) = sync_committee_prover + .fetch_light_client_update(client_state.clone(), checkpoint, "prover") + .await + .unwrap() + { + update + } else { + continue + }; + + let encoded = light_client_update.encode(); + let decoded = LightClientUpdate::decode(&mut &*encoded).unwrap(); + assert_eq!(light_client_update, decoded); + + client_state = + verify_sync_committee_attestation(client_state.clone(), light_client_update) + .unwrap(); + debug!( + target: "prover", + "Sucessfully verified Ethereum block at slot {:?}", + client_state.finalized_header.slot + ); + + count += 1; + // For CI purposes we test finalization of 3 epochs + if count == 4 { + break + } + }, + Err(err) => { + panic!("Encountered Error and closed stream {err:?}"); + }, + _ => continue, } } } @@ -317,7 +339,7 @@ async fn test_prover() { #[allow(non_snake_case)] #[tokio::test] async fn test_sync_committee_signature_verification() { - wait_for_el().await; + wait_for_el(1).await; let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); let block = loop { let block = sync_committee_prover.fetch_block("head").await.unwrap(); @@ -368,3 +390,11 @@ async fn test_sync_committee_signature_verification() { ) .unwrap(); } + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct EventResponse { + pub block: Root, + pub state: Root, + pub epoch: String, + pub execution_optimistic: bool, +} diff --git a/verifier/src/error.rs b/verifier/src/error.rs index 567e74c6a..a1181a531 100644 --- a/verifier/src/error.rs +++ b/verifier/src/error.rs @@ -1,13 +1,14 @@ +use alloc::string::String; use core::fmt::{Display, Formatter}; #[derive(Debug)] pub enum Error { SyncCommitteeParticipantsTooLow, - InvalidUpdate, + InvalidUpdate(String), DomainError, - InvalidMerkleBranch, - InvalidRoot, - MerkleizationError, + InvalidMerkleBranch(String), + InvalidRoot(String), + MerkleizationError(String), SignatureVerification, } @@ -17,11 +18,11 @@ impl Display for Error { Error::SyncCommitteeParticipantsTooLow => { write!(f, "Sync committee participants are too low") }, - Error::InvalidUpdate => write!(f, "Invalid update"), + Error::InvalidUpdate(err) => write!(f, "Invalid update {err:?}"), Error::DomainError => write!(f, "Couldn't get domain"), - Error::InvalidMerkleBranch => write!(f, "Invalid merkle branch"), - Error::InvalidRoot => write!(f, "Invalid root"), - Error::MerkleizationError => write!(f, "Merkleization error"), + Error::InvalidMerkleBranch(err) => write!(f, "Invalid merkle branch {err:?}"), + Error::InvalidRoot(err) => write!(f, "Invalid root {err:?}"), + Error::MerkleizationError(err) => write!(f, "Merkleization error {err:?}"), Error::SignatureVerification => write!(f, "Signature verification failed"), } } diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 157da6a93..589f1cafa 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -23,7 +23,7 @@ use sync_committee_primitives::{ }, util::{ compute_domain, compute_epoch_at_slot, compute_fork_version, compute_signing_root, - compute_sync_committee_period_at_slot, + compute_sync_committee_period_at_slot, should_get_sync_committee_update, }, }; @@ -33,14 +33,14 @@ pub type LightClientUpdate = sync_committee_primitives::types::LightClientUpdate /// This function simply verifies a sync committee's attestation & it's finalized counterpart. pub fn verify_sync_committee_attestation( trusted_state: LightClientState, - update: LightClientUpdate, + mut update: LightClientUpdate, ) -> Result { if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX_LOG2 as usize && update.sync_committee_update.is_some() && update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize { - Err(Error::InvalidUpdate)? + Err(Error::InvalidUpdate("Finality branch is incorrect".into()))? } // Verify sync committee has super majority participants @@ -52,28 +52,23 @@ pub fn verify_sync_committee_attestation( Err(Error::SyncCommitteeParticipantsTooLow)? } - // Verify update does not skip a sync committee period + // Verify update is valid let is_valid_update = update.signature_slot > update.attested_header.slot && - update.attested_header.slot >= update.finalized_header.slot; + update.attested_header.slot > update.finalized_header.slot; if !is_valid_update { - Err(Error::InvalidUpdate)? + Err(Error::InvalidUpdate( + "relationship between slots does not meet the requirements".into(), + ))? } let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); if !(state_period..=state_period + 1).contains(&update_signature_period) { - Err(Error::InvalidUpdate)? + Err(Error::InvalidUpdate("State period does not contain signature period".into()))? } - // Verify update is relevant - let update_attested_period = compute_sync_committee_period_at_slot(update.attested_header.slot); - let update_has_next_sync_committee = - update.sync_committee_update.is_some() && update_attested_period == state_period; - - if !(update.attested_header.slot > trusted_state.finalized_header.slot || - update_has_next_sync_committee) - { - Err(Error::InvalidUpdate)? + if update.attested_header.slot <= trusted_state.finalized_header.slot { + Err(Error::InvalidUpdate("Update is expired".into()))? } // Verify sync committee aggregate signature @@ -96,16 +91,16 @@ pub fn verify_sync_committee_attestation( let domain = compute_domain( DOMAIN_SYNC_COMMITTEE, Some(fork_version), - Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().map_err(|_| Error::InvalidRoot)?)), + Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().expect("Infallible"))), GENESIS_FORK_VERSION, ) - .map_err(|_| Error::InvalidUpdate)?; + .map_err(|_| Error::InvalidUpdate("Failed to compute domain".into()))?; - let signing_root = compute_signing_root(&mut update.attested_header.clone(), domain) - .map_err(|_| Error::InvalidRoot)?; + let signing_root = compute_signing_root(&mut update.attested_header, domain) + .map_err(|_| Error::InvalidRoot("Failed to compute signing root".into()))?; verify_aggregate_signature( - &trusted_state.current_sync_committee.aggregate_public_key, + &sync_committee.aggregate_public_key, &non_participant_pubkeys, signing_root.as_bytes().to_vec(), &update.sync_aggregate.sync_committee_signature, @@ -119,13 +114,14 @@ pub fn verify_sync_committee_attestation( epoch: update.finality_proof.epoch, root: update .finalized_header - .clone() .hash_tree_root() - .map_err(|_| Error::InvalidRoot)?, + .map_err(|_| Error::MerkleizationError("Error hashing finalized header".into()))?, }; let is_merkle_branch_valid = is_valid_merkle_branch( - &finalized_checkpoint.hash_tree_root().map_err(|_| Error::InvalidRoot)?, + &finalized_checkpoint + .hash_tree_root() + .map_err(|_| Error::MerkleizationError("Failed to hash finality checkpoint".into()))?, update.finality_proof.finality_branch.iter(), FINALIZED_ROOT_INDEX_LOG2 as usize, FINALIZED_ROOT_INDEX as usize, @@ -133,25 +129,21 @@ pub fn verify_sync_committee_attestation( ); if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; + Err(Error::InvalidMerkleBranch("Finality branch".into()))?; } // verify the associated execution header of the finalized beacon header. let mut execution_payload = update.execution_payload; let execution_payload_root = calculate_multi_merkle_root( &[ - Node::from_bytes( - execution_payload - .state_root - .as_ref() - .try_into() - .map_err(|_| Error::InvalidRoot)?, - ), + Node::from_bytes(execution_payload.state_root.as_ref().try_into().expect("Infallible")), + execution_payload.block_number.hash_tree_root().map_err(|_| { + Error::MerkleizationError("Failed to hash execution payload".into()) + })?, execution_payload - .block_number + .timestamp .hash_tree_root() - .map_err(|_| Error::InvalidRoot)?, - execution_payload.timestamp.hash_tree_root().map_err(|_| Error::InvalidRoot)?, + .map_err(|_| Error::MerkleizationError("Failed to hash timestamp".into()))?, ], &execution_payload.multi_proof, &[ @@ -170,22 +162,21 @@ pub fn verify_sync_committee_attestation( ); if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; + Err(Error::InvalidMerkleBranch("Execution payload branch".into()))?; } if let Some(mut sync_committee_update) = update.sync_committee_update.clone() { - if update_attested_period == state_period && - sync_committee_update.next_sync_committee != - trusted_state.next_sync_committee.clone() - { - Err(Error::InvalidUpdate)? + if !should_get_sync_committee_update(update.attested_header.slot) { + Err(Error::InvalidUpdate("Current sync committee period has not elapsed".into()))? } + let sync_root = sync_committee_update + .next_sync_committee + .hash_tree_root() + .map_err(|_| Error::MerkleizationError("Failed to hash next sync committee".into()))?; + let is_merkle_branch_valid = is_valid_merkle_branch( - &sync_committee_update - .next_sync_committee - .hash_tree_root() - .map_err(|_| Error::MerkleizationError)?, + &sync_root, sync_committee_update.next_sync_committee_branch.iter(), NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize, NEXT_SYNC_COMMITTEE_INDEX as usize, @@ -193,7 +184,7 @@ pub fn verify_sync_committee_attestation( ); if !is_merkle_branch_valid { - Err(Error::InvalidMerkleBranch)?; + Err(Error::InvalidMerkleBranch("Next sync committee branch".into()))?; } } @@ -205,7 +196,11 @@ pub fn verify_sync_committee_attestation( next_sync_committee: sync_committee_update.next_sync_committee, } } else { - LightClientState { finalized_header: update.finalized_header, ..trusted_state } + LightClientState { + finalized_header: update.finalized_header, + latest_finalized_epoch: update.finality_proof.epoch, + ..trusted_state + } }; Ok(new_light_client_state) From 6d51d927078ae435b70981deb7171fea0bc08f84 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:44:48 +0100 Subject: [PATCH 172/182] Ismp-rs Update (#92) * some updates * ismp-update * minor change --- .gitmodules | 3 --- Cargo.lock | 4 ++-- ismp-demo/src/lib.rs | 18 ++++++++++-------- pallet-ismp/rpc/src/lib.rs | 13 ------------- pallet-ismp/runtime-api/src/lib.rs | 3 --- 5 files changed, 12 insertions(+), 29 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 26647ca3e..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "ismp-solidity"] - path = pallet-ismp/evm/solidity/lib/ismp-solidity - url = https://github.com/polytope-labs/ismp-solidity.git diff --git a/Cargo.lock b/Cargo.lock index f2e70b169..38bea3837 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1627,7 +1627,7 @@ dependencies = [ [[package]] name = "ismp" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#3a85db0d968eab5a88427a19a2a612332caec23f" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#5c4e0f412de788b5d86de8146b49f6db6795ca12" dependencies = [ "derive_more", "parity-scale-codec", @@ -1706,7 +1706,7 @@ dependencies = [ [[package]] name = "ismp-testsuite" version = "0.1.0" -source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#3a85db0d968eab5a88427a19a2a612332caec23f" +source = "git+https://github.com/polytope-labs/ismp-rs?branch=main#5c4e0f412de788b5d86de8146b49f6db6795ca12" dependencies = [ "ismp", "parity-scale-codec", diff --git a/ismp-demo/src/lib.rs b/ismp-demo/src/lib.rs index ebd3e3ee8..82da7010e 100644 --- a/ismp-demo/src/lib.rs +++ b/ismp-demo/src/lib.rs @@ -206,9 +206,8 @@ pub mod pallet { /// Dispatch request to a connected EVM chain. #[pallet::weight(Weight::from_parts(1_000_000, 0))] #[pallet::call_index(2)] - pub fn disptach_to_evm(origin: OriginFor, params: EvmParams) -> DispatchResult { + pub fn dispatch_to_evm(origin: OriginFor, params: EvmParams) -> DispatchResult { ensure_signed(origin)?; - let post = DispatchPost { dest: StateMachine::Ethereum(params.destination), from: PALLET_ID.to_bytes(), @@ -217,13 +216,13 @@ pub mod pallet { data: b"Hello from polkadot".to_vec(), gas_limit: 10_000_000, }; - - // dispatch the request let dispatcher = T::IsmpDispatcher::default(); - dispatcher - .dispatch_request(DispatchRequest::Post(post)) - .map_err(|_| Error::::TransferFailed)?; - + for _ in 0..params.count { + // dispatch the request + dispatcher + .dispatch_request(DispatchRequest::Post(post.clone())) + .map_err(|_| Error::::TransferFailed)?; + } Ok(()) } } @@ -288,6 +287,9 @@ pub mod pallet { /// Timeout timestamp on destination chain in seconds pub timeout: u64, + + /// Request count + pub count: u64, } } diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index baa676e3c..3e82da881 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -122,10 +122,6 @@ where #[method(name = "ismp_queryChallengePeriod")] fn query_challenge_period(&self, client_id: ConsensusClientId) -> Result; - /// Query the latest timestamp for chain - #[method(name = "ismp_queryTimestamp")] - fn query_timestamp(&self) -> Result; - /// Query the latest height for a state machine #[method(name = "ismp_queryStateMachineLatestHeight")] fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result; @@ -273,15 +269,6 @@ where .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Challenge period")) } - fn query_timestamp(&self) -> Result { - let api = self.client.runtime_api(); - let at = self.client.info().best_hash; - api.timestamp(at) - .ok() - .flatten() - .ok_or_else(|| runtime_error_into_rpc_error("Error fetching latest timestamp")) - } - fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result { let api = self.client.runtime_api(); let at = self.client.info().best_hash; diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index b558bd361..a8364c5ec 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -54,9 +54,6 @@ sp_api::decl_runtime_apis! { /// Return the timestamp this client was last updated in seconds fn consensus_update_time(id: ConsensusClientId) -> Option; - /// Return the latest timestamp for the chain - fn timestamp() -> Option; - /// Return the challenge period timestamp fn challenge_period(id: ConsensusClientId) -> Option; From b476c91a370c02bf8e0e7c251420e68264503be0 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Fri, 15 Sep 2023 18:13:23 +0100 Subject: [PATCH 173/182] Polkadot v1.1.0 (#91) * poladot v1.1.0 * substrate ismp compiles * fix test * fmt * use another nightly toolchain * CI nightly toolchain changed * set rust flags * update ci * set rust flags with github action --------- Co-authored-by: David Salami <31099392+Wizdave97@users.noreply.github.com> Co-authored-by: David Salami --- .github/workflows/ci.yml | 12 +- Cargo.lock | 2006 ++++++++++++++++++++-------- ismp-demo/Cargo.toml | 10 +- pallet-ismp/Cargo.toml | 20 +- pallet-ismp/primitives/Cargo.toml | 12 +- pallet-ismp/primitives/src/lib.rs | 3 +- pallet-ismp/primitives/src/mmr.rs | 2 +- pallet-ismp/rpc/Cargo.toml | 12 +- pallet-ismp/runtime-api/Cargo.toml | 4 +- pallet-ismp/src/lib.rs | 11 +- pallet-ismp/src/mmr/storage.rs | 2 +- pallet-ismp/src/mocks/mod.rs | 7 +- pallet-ismp/src/primitives.rs | 3 +- pallet-ismp/src/tests.rs | 3 +- rust-toolchain.toml | 4 +- 15 files changed, 1511 insertions(+), 600 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb9f3a46a..6e9d2b1d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ concurrency: jobs: build_and_test: runs-on: ubuntu-latest + env: + RUSTFLAGS: "-C link-args=-Wl,--allow-multiple-definition" steps: - name: Checkout sources uses: actions/checkout@v3 @@ -23,7 +25,7 @@ jobs: - name: Install toolchain uses: dtolnay/rust-toolchain@nightly with: - toolchain: nightly-2022-10-28 + toolchain: nightly-2023-08-23 targets: wasm32-unknown-unknown - name: Install Protoc @@ -34,16 +36,16 @@ jobs: - name: Build run: | - cargo +nightly-2022-10-28 check --workspace --all-targets --all-features --verbose --locked + cargo +nightly-2023-08-23 check --workspace --all-targets --all-features --verbose --locked - name: Build `no-std` run: | - cargo +nightly-2022-10-28 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose --locked - cargo +nightly-2022-10-28 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2023-08-23 check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --verbose --locked + cargo +nightly-2023-08-23 check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --verbose --locked - name: Test run: | - cargo +nightly-2022-10-28 test -p pallet-ismp --all-targets --all-features --verbose --locked + cargo +nightly-2023-08-23 test -p pallet-ismp --all-targets --all-features --verbose --locked lint: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 38bea3837..6d6826509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,19 +23,63 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.20.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli 0.27.3", ] +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli 0.28.0", +] + [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.6" @@ -61,9 +105,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] @@ -94,9 +138,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "approx" @@ -107,12 +151,203 @@ dependencies = [ "num-traits", ] +[[package]] +name = "aquamarine" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df752953c49ce90719c7bf1fc587bc8227aed04732ea0c0f85e5397d7fdbd1a1" +dependencies = [ + "include_dir", + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "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", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ed-on-bls12-381-bandersnatch" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cde0f2aa063a2a5c28d39b47761aa102bda7c13c84fc118a61b87c7b2f785c" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "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-scale" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b08346a3e38e2be792ef53ee168623c9244d968ff00cd70fb9932f6fe36393" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "parity-scale-codec", +] + +[[package]] +name = "ark-secret-scalar" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "ark-transcript", + "digest 0.10.7", + "rand_core 0.6.4", + "zeroize", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-transcript" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "digest 0.10.7", + "rand_core 0.6.4", + "sha3", +] + [[package]] name = "array-bytes" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" +[[package]] +name = "array-bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" + [[package]] name = "arrayref" version = "0.3.7" @@ -144,13 +379,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.71" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] @@ -161,19 +396,41 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ - "addr2line 0.20.0", + "addr2line 0.21.0", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.31.1", + "object 0.32.1", "rustc-demangle", ] +[[package]] +name = "bandersnatch_vrfs" +version = "0.0.1" +source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ed-on-bls12-381-bandersnatch", + "ark-ff", + "ark-scale", + "ark-serialize", + "ark-std", + "dleq_vrf", + "fflonk", + "merlin 3.0.0", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "ring", + "sha2 0.10.7", + "zeroize", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -186,6 +443,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + [[package]] name = "base64ct" version = "1.6.0" @@ -218,9 +481,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bitvec" @@ -245,9 +508,9 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", "arrayvec 0.7.4", @@ -311,11 +574,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "serde", @@ -323,9 +595,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-slice-cast" @@ -341,9 +613,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" @@ -353,24 +625,25 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] name = "cfg-expr" -version = "0.10.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" dependencies = [ "smallvec", ] @@ -383,14 +656,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "winapi", + "windows-targets 0.48.5", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", ] [[package]] @@ -402,6 +685,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof?rev=0e948f3#0e948f3c28cbacecdd3020403c4841c0eb339213" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "fflonk", + "merlin 3.0.0", +] + +[[package]] +name = "common-path" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" + [[package]] name = "concurrent-queue" version = "2.2.0" @@ -413,15 +716,37 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.4" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "const-random" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "proc-macro-hack", + "tiny-keccak", +] [[package]] name = "constant_time_eq" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation-sys" @@ -449,28 +774,27 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" +checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.95.1", ] [[package]] name = "cranelift-codegen" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" +checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220" dependencies = [ - "arrayvec 0.7.4", "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", - "cranelift-entity", + "cranelift-entity 0.95.1", "cranelift-isle", - "gimli 0.26.2", - "hashbrown 0.12.3", + "gimli 0.27.3", + "hashbrown 0.13.2", "log", "regalloc2", "smallvec", @@ -479,18 +803,18 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" +checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" +checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8" [[package]] name = "cranelift-entity" @@ -501,11 +825,20 @@ dependencies = [ "serde", ] +[[package]] +name = "cranelift-entity" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +dependencies = [ + "serde", +] + [[package]] name = "cranelift-frontend" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" +checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d" dependencies = [ "cranelift-codegen", "log", @@ -515,15 +848,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" +checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba" [[package]] name = "cranelift-native" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" +checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00" dependencies = [ "cranelift-codegen", "libc", @@ -532,18 +865,18 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.2" +version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" +checksum = "ff3220489a3d928ad91e59dd7aeaa8b3de18afb554a6211213673a71c90737ac" dependencies = [ "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.95.1", "cranelift-frontend", "itertools", "log", "smallvec", - "wasmparser", - "wasmtime-types", + "wasmparser 0.102.0", + "wasmtime-types 8.0.1", ] [[package]] @@ -606,9 +939,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -623,6 +956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] @@ -646,6 +980,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "2.1.3" @@ -672,16 +1015,55 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + [[package]] name = "der" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive-syn-parse" version = "0.1.5" @@ -755,6 +1137,50 @@ dependencies = [ "winapi", ] +[[package]] +name = "dleq_vrf" +version = "0.0.2" +source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-scale", + "ark-secret-scalar", + "ark-serialize", + "ark-std", + "ark-transcript", + "arrayvec 0.7.4", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "docify" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff509d6aa8e7ca86b36eb3d593132e64204597de3ccb763ffd8bfd2264d54cf3" +dependencies = [ + "docify_macros", +] + +[[package]] +name = "docify_macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b135598b950330937f3a0ddfbe908106ee2ecd5fa95d8ae7952a3c3863efe8da" +dependencies = [ + "common-path", + "derive-syn-parse", + "once_cell", + "proc-macro2", + "quote", + "regex", + "syn 2.0.33", + "termcolor", + "toml 0.7.8", + "walkdir", +] + [[package]] name = "downcast-rs" version = "1.2.0" @@ -784,42 +1210,44 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der", "digest 0.10.7", "elliptic-curve", "rfc6979", - "signature 2.1.0", + "signature", "spki", ] [[package]] name = "ed25519" -version = "1.5.3" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ - "signature 1.6.4", + "pkcs8", + "signature", ] [[package]] name = "ed25519-dalek" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ - "curve25519-dalek 3.2.0", + "curve25519-dalek 4.1.0", "ed25519", - "sha2 0.9.9", + "serde", + "sha2 0.10.7", "zeroize", ] @@ -901,9 +1329,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -928,15 +1356,15 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "expander" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" dependencies = [ "blake2", "fs-err", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.33", ] [[package]] @@ -961,6 +1389,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "fflonk" +version = "0.1.0" +source = "git+https://github.com/w3f/fflonk#26a5045b24e169cffc1f9328ca83d71061145c40" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "merlin 3.0.0", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" + [[package]] name = "file-per-thread-logger" version = "0.1.6" @@ -1001,7 +1448,7 @@ dependencies = [ [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "frame-support", "frame-support-procedural", @@ -1014,20 +1461,20 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-runtime-interface 7.0.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "static_assertions", ] [[package]] name = "frame-metadata" -version = "15.1.0" +version = "16.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" dependencies = [ "cfg-if", "parity-scale-codec", @@ -1038,88 +1485,98 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ + "aquamarine", "bitflags 1.3.2", + "docify", "environmental", "frame-metadata", "frame-support-procedural", "impl-trait-for-tuples", "k256", "log", - "once_cell", + "macro_magic", "parity-scale-codec", "paste", "scale-info", "serde", + "serde_json", "smallvec", "sp-api", "sp-arithmetic", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-core-hashing-proc-macro", + "sp-debug-derive 8.0.0", + "sp-genesis-builder", "sp-inherents", "sp-io", + "sp-metadata-ir", "sp-runtime", "sp-staking", "sp-state-machine", - "sp-std 5.0.0", - "sp-tracing 6.0.0", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "sp-weights", + "static_assertions", "tt-call", ] [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", + "expander", "frame-support-procedural-tools", "itertools", + "macro_magic", "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ + "cfg-if", "frame-support", "log", "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", "sp-version", "sp-weights", ] @@ -1193,7 +1650,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] @@ -1283,6 +1740,16 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + [[package]] name = "gimli" version = "0.26.2" @@ -1290,7 +1757,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap 1.9.3", "stable_deref_trait", ] @@ -1299,12 +1765,23 @@ name = "gimli" version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "globset" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", "bstr", @@ -1326,9 +1803,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -1406,6 +1883,15 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -1476,9 +1962,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -1503,7 +1989,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1572,6 +2058,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1593,6 +2098,15 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "integer-sqrt" version = "0.1.5" @@ -1620,7 +2134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.4", + "rustix 0.38.13", "windows-sys 0.48.0", ] @@ -1648,7 +2162,7 @@ dependencies = [ "pallet-ismp", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-runtime", ] @@ -1665,7 +2179,8 @@ dependencies = [ "scale-info", "serde", "sp-consensus-aura", - "sp-core 7.0.0", + "sp-core 21.0.0", + "sp-io", "sp-runtime", ] @@ -1686,7 +2201,7 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-runtime", ] @@ -1700,7 +2215,7 @@ dependencies = [ "parity-scale-codec", "serde", "sp-api", - "sp-std 5.0.0", + "sp-std 8.0.0", ] [[package]] @@ -1725,9 +2240,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -1749,9 +2264,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +checksum = "367a292944c07385839818bb71c8d76611138e2dedb0677d035b8da21d29c78b" dependencies = [ "jsonrpsee-core", "jsonrpsee-proc-macros", @@ -1762,9 +2277,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +checksum = "2b5dde66c53d6dcdc8caea1874a45632ec0fcf5b437789f1e45766a1512ce803" dependencies = [ "anyhow", "arrayvec 0.7.4", @@ -1788,9 +2303,9 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" +checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a" dependencies = [ "heck", "proc-macro-crate", @@ -1801,9 +2316,9 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" +checksum = "cf4d945a6008c9b03db3354fb3c83ee02d2faa9f2e755ec1dfb69c3551b8f4ba" dependencies = [ "futures-channel", "futures-util", @@ -1823,9 +2338,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +checksum = "245ba8e5aa633dd1c1e4fae72bce06e71f42d34c14a2767c6b4d173b57bee5e5" dependencies = [ "anyhow", "beef", @@ -1874,9 +2389,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libm" @@ -1891,7 +2406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64", + "base64 0.13.1", "digest 0.9.0", "hmac-drbg", "libsecp256k1-core", @@ -1934,9 +2449,9 @@ dependencies = [ [[package]] name = "linregress" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" +checksum = "4de04dcecc58d366391f9920245b85ffa684558a5ef6e7736e754347c3aea9c2" dependencies = [ "nalgebra", ] @@ -1955,42 +2470,81 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "lock_api" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "macro_magic" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aee866bfee30d2d7e83835a4574aad5b45adba4cc807f2a3bbba974e5d4383c9" dependencies = [ - "autocfg", - "scopeguard", + "macro_magic_core", + "macro_magic_macros", + "quote", + "syn 2.0.33", ] [[package]] -name = "log" -version = "0.4.19" +name = "macro_magic_core" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "7e766a20fd9c72bab3e1e64ed63f36bd08410e75803813df210d1ce297d7ad00" +dependencies = [ + "const-random", + "derive-syn-parse", + "macro_magic_core_macros", + "proc-macro2", + "quote", + "syn 2.0.33", +] [[package]] -name = "lru" -version = "0.8.1" +name = "macro_magic_core_macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +checksum = "c12469fc165526520dff2807c2975310ab47cf7190a45b99b49a7dc8befab17b" dependencies = [ - "hashbrown 0.12.3", + "proc-macro2", + "quote", + "syn 2.0.33", ] [[package]] -name = "mach" -version = "0.3.2" +name = "macro_magic_macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +checksum = "b8fb85ec1620619edf2984a7693497d4ec88a9665d8b87e942856884c92dbf2a" dependencies = [ - "libc", + "macro_magic_core", + "quote", + "syn 2.0.33", ] [[package]] @@ -2014,9 +2568,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memfd" @@ -2036,6 +2590,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -2072,6 +2635,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -2127,9 +2702,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -2138,9 +2713,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", ] @@ -2179,9 +2754,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -2210,9 +2785,21 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + +[[package]] +name = "object" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -2238,7 +2825,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "frame-benchmarking", "frame-support", @@ -2247,7 +2834,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", ] [[package]] @@ -2270,16 +2857,16 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", ] [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "frame-benchmarking", "frame-support", @@ -2290,15 +2877,16 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-timestamp", ] [[package]] name = "parity-scale-codec" -version = "3.6.3" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756d439303e94fae44f288ba881ad29670c65b0c4b0e05674ca81061bb65f2c5" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" dependencies = [ "arrayvec 0.7.4", "bitvec", @@ -2311,9 +2899,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.3" +version = "3.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d884d78fcf214d70b1e239fcd1c6e5e95aa3be1881918da2e488cc946c7a476" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2347,14 +2935,14 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] name = "paste" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -2382,9 +2970,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2408,6 +2996,24 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2437,22 +3043,52 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro-warning" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -2625,29 +3261,29 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" +checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" +checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "regalloc2" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621" dependencies = [ "fxhash", "log", @@ -2657,14 +3293,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.3", - "regex-syntax 0.7.4", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", ] [[package]] @@ -2678,13 +3314,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.7.5", ] [[package]] @@ -2695,21 +3331,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rfc6979" @@ -2721,6 +3345,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.1.0" +source = "git+https://github.com/w3f/ring-proof?rev=0e948f3#0e948f3c28cbacecdd3020403c4841c0eb339213" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "common", + "fflonk", + "merlin 3.0.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2739,6 +3378,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.36.15" @@ -2769,53 +3417,62 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys 0.4.7", "windows-sys 0.48.0", ] [[package]] name = "rustversion" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safe_arch" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" dependencies = [ "bytemuck", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "log", - "sp-core 7.0.0", - "sp-wasm-interface 7.0.0", + "sp-core 21.0.0", + "sp-wasm-interface 14.0.0", "thiserror", ] [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "fnv", "futures", @@ -2828,94 +3485,79 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-database", - "sp-externalities 0.13.0", - "sp-keystore", + "sp-externalities 0.19.0", "sp-runtime", "sp-state-machine", - "sp-storage 7.0.0", + "sp-statement-store", + "sp-storage 13.0.0", "substrate-prometheus-endpoint", ] [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "lru", "parity-scale-codec", "parking_lot", "sc-executor-common", - "sc-executor-wasmi", "sc-executor-wasmtime", + "schnellru", "sp-api", - "sp-core 7.0.0", - "sp-externalities 0.13.0", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-io", "sp-panic-handler", - "sp-runtime-interface 7.0.0", + "sp-runtime-interface 17.0.0", "sp-trie", "sp-version", - "sp-wasm-interface 7.0.0", + "sp-wasm-interface 14.0.0", "tracing", - "wasmi", ] [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface 7.0.0", + "sp-wasm-interface 14.0.0", "thiserror", "wasm-instrument", - "wasmi", -] - -[[package]] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "log", - "sc-allocator", - "sc-executor-common", - "sp-runtime-interface 7.0.0", - "sp-wasm-interface 7.0.0", - "wasmi", ] [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "anyhow", "cfg-if", "libc", "log", - "once_cell", "rustix 0.36.15", "sc-allocator", "sc-executor-common", - "sp-runtime-interface 7.0.0", - "sp-wasm-interface 7.0.0", - "wasmtime", + "sp-runtime-interface 17.0.0", + "sp-wasm-interface 14.0.0", + "wasmtime 8.0.1", ] [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "async-trait", "futures", "log", + "parity-scale-codec", "serde", "sp-blockchain", + "sp-core 21.0.0", "sp-runtime", "thiserror", ] @@ -2923,7 +3565,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "async-channel", "futures", @@ -2982,7 +3624,7 @@ dependencies = [ "arrayvec 0.5.2", "curve25519-dalek 2.1.3", "getrandom 0.1.16", - "merlin", + "merlin 2.0.1", "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", @@ -2992,15 +3634,15 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -3037,37 +3679,52 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + [[package]] name = "serde" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -3136,12 +3793,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - [[package]] name = "signature" version = "2.1.0" @@ -3167,9 +3818,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -3196,13 +3847,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ - "base64", + "base64 0.13.1", "bytes", "futures", "http", @@ -3215,18 +3876,19 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "hash-db", "log", "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core 7.0.0", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-metadata-ir", "sp-runtime", "sp-state-machine", - "sp-std 5.0.0", + "sp-std 8.0.0", "sp-trie", "sp-version", "thiserror", @@ -3235,7 +3897,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "Inflector", "blake2", @@ -3243,46 +3905,46 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "sp-application-crypto" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "23.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-io", - "sp-std 5.0.0", + "sp-std 8.0.0", ] [[package]] name = "sp-arithmetic" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "16.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "integer-sqrt", "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-std 5.0.0", + "sp-std 8.0.0", "static_assertions", ] [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "futures", "log", - "lru", "parity-scale-codec", "parking_lot", + "schnellru", "sp-api", "sp-consensus", "sp-database", @@ -3294,12 +3956,12 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "async-trait", "futures", "log", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", "sp-state-machine", @@ -3309,43 +3971,43 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "async-trait", "parity-scale-codec", "scale-info", "sp-api", "sp-application-crypto", - "sp-consensus", "sp-consensus-slots", "sp-inherents", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std 5.0.0", + "sp-std 8.0.0", "sp-timestamp", ] [[package]] name = "sp-core" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "bitflags 1.3.2", "blake2", "bounded-collections", - "bs58", + "bs58 0.4.0", "dyn-clonable", "ed25519-zebra", "futures", @@ -3355,10 +4017,9 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot", - "paste", "primitive-types", "rand 0.8.5", "regex", @@ -3367,12 +4028,12 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-core-hashing 5.0.0", - "sp-debug-derive 5.0.0", - "sp-externalities 0.13.0", - "sp-runtime-interface 7.0.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", + "sp-core-hashing 8.0.0", + "sp-debug-derive 7.0.0", + "sp-externalities 0.18.0", + "sp-runtime-interface 16.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", "ss58-registry", "substrate-bip39", "thiserror", @@ -3382,15 +4043,16 @@ dependencies = [ [[package]] name = "sp-core" -version = "20.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" +version = "21.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", + "arrayvec 0.7.4", + "bandersnatch_vrfs", "bitflags 1.3.2", "blake2", "bounded-collections", - "bs58", + "bs58 0.5.0", "dyn-clonable", "ed25519-zebra", "futures", @@ -3400,9 +4062,10 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "merlin", + "merlin 2.0.1", "parity-scale-codec", "parking_lot", + "paste", "primitive-types", "rand 0.8.5", "regex", @@ -3411,63 +4074,62 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-core-hashing 8.0.0", - "sp-debug-derive 7.0.0", - "sp-externalities 0.18.0", - "sp-runtime-interface 16.0.0", - "sp-std 7.0.0", - "sp-storage 12.0.0", + "sp-core-hashing 9.0.0", + "sp-debug-derive 8.0.0", + "sp-externalities 0.19.0", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "ss58-registry", "substrate-bip39", "thiserror", "tiny-bip39", + "tracing", "zeroize", ] [[package]] name = "sp-core-hashing" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.7", "sha2 0.10.7", "sha3", - "sp-std 5.0.0", + "sp-std 7.0.0", "twox-hash", ] [[package]] name = "sp-core-hashing" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" +version = "9.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.7", "sha2 0.10.7", "sha3", - "sp-std 7.0.0", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "9.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "proc-macro2", "quote", - "sp-core-hashing 5.0.0", - "syn 2.0.25", + "sp-core-hashing 9.0.0", + "syn 2.0.33", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "kvdb", "parking_lot", @@ -3475,84 +4137,92 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 1.0.109", ] [[package]] name = "sp-debug-derive" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" +version = "8.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.33", ] [[package]] name = "sp-externalities" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 5.0.0", - "sp-storage 7.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", ] [[package]] name = "sp-externalities" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" +version = "0.19.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 7.0.0", - "sp-storage 12.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", +] + +[[package]] +name = "sp-genesis-builder" +version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +dependencies = [ + "serde_json", + "sp-api", + "sp-runtime", + "sp-std 8.0.0", ] [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", "thiserror", ] [[package]] name = "sp-io" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "23.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "bytes", - "ed25519", "ed25519-dalek", - "futures", "libsecp256k1", "log", "parity-scale-codec", "rustversion", "secp256k1", - "sp-core 7.0.0", - "sp-externalities 0.13.0", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-keystore", - "sp-runtime-interface 7.0.0", + "sp-runtime-interface 17.0.0", "sp-state-machine", - "sp-std 5.0.0", - "sp-tracing 6.0.0", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "sp-trie", "tracing", "tracing-core", @@ -3560,41 +4230,40 @@ dependencies = [ [[package]] name = "sp-keystore" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "0.27.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "futures", "parity-scale-codec", "parking_lot", - "sp-core 7.0.0", - "sp-externalities 0.13.0", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "thiserror", ] [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "thiserror", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-std 5.0.0", + "sp-std 8.0.0", ] [[package]] name = "sp-panic-handler" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "backtrace", "lazy_static", @@ -3603,8 +4272,8 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "24.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "either", "hash256-std-hasher", @@ -3617,28 +4286,10 @@ dependencies = [ "serde", "sp-application-crypto", "sp-arithmetic", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-io", - "sp-std 5.0.0", - "sp-weights", -] - -[[package]] -name = "sp-runtime-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.13.0", - "sp-runtime-interface-proc-macro 6.0.0", - "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-tracing 6.0.0", - "sp-wasm-interface 7.0.0", - "static_assertions", + "sp-std 8.0.0", + "sp-weights", ] [[package]] @@ -3660,48 +4311,67 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-runtime-interface" +version = "17.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.19.0", + "sp-runtime-interface-proc-macro 11.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", + "sp-tracing 10.0.0", + "sp-wasm-interface 14.0.0", + "static_assertions", +] + [[package]] name = "sp-runtime-interface-proc-macro" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.25", + "syn 1.0.109", ] [[package]] name = "sp-runtime-interface-proc-macro" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" +version = "11.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.33", ] [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ + "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", + "sp-core 21.0.0", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", ] [[package]] name = "sp-state-machine" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "0.28.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "hash-db", "log", @@ -3709,19 +4379,39 @@ dependencies = [ "parking_lot", "rand 0.8.5", "smallvec", - "sp-core 7.0.0", - "sp-externalities 0.13.0", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-panic-handler", - "sp-std 5.0.0", + "sp-std 8.0.0", "sp-trie", "thiserror", "tracing", + "trie-db", ] [[package]] -name = "sp-std" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +name = "sp-statement-store" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +dependencies = [ + "aes-gcm", + "curve25519-dalek 4.1.0", + "ed25519-dalek", + "hkdf", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sha2 0.10.7", + "sp-api", + "sp-application-crypto", + "sp-core 21.0.0", + "sp-externalities 0.19.0", + "sp-runtime", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "thiserror", + "x25519-dalek", +] [[package]] name = "sp-std" @@ -3729,55 +4419,59 @@ version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" +[[package]] +name = "sp-std" +version = "8.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" + [[package]] name = "sp-storage" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 5.0.0", - "sp-std 5.0.0", + "sp-debug-derive 7.0.0", + "sp-std 7.0.0", ] [[package]] name = "sp-storage" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" +version = "13.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 7.0.0", - "sp-std 7.0.0", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", ] [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "async-trait", - "futures-timer", - "log", "parity-scale-codec", "sp-inherents", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", "thiserror", ] [[package]] name = "sp-tracing" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" dependencies = [ "parity-scale-codec", - "sp-std 5.0.0", + "sp-std 7.0.0", "tracing", "tracing-core", "tracing-subscriber", @@ -3785,12 +4479,11 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" +version = "10.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "parity-scale-codec", - "sp-std 7.0.0", + "sp-std 8.0.0", "tracing", "tracing-core", "tracing-subscriber", @@ -3798,8 +4491,8 @@ dependencies = [ [[package]] name = "sp-trie" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "22.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "ahash 0.8.3", "hash-db", @@ -3811,8 +4504,8 @@ dependencies = [ "parking_lot", "scale-info", "schnellru", - "sp-core 7.0.0", - "sp-std 5.0.0", + "sp-core 21.0.0", + "sp-std 8.0.0", "thiserror", "tracing", "trie-db", @@ -3821,8 +4514,8 @@ dependencies = [ [[package]] name = "sp-version" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "22.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "impl-serde", "parity-scale-codec", @@ -3831,64 +4524,63 @@ dependencies = [ "serde", "sp-core-hashing-proc-macro", "sp-runtime", - "sp-std 5.0.0", + "sp-std 8.0.0", "sp-version-proc-macro", "thiserror", ] [[package]] name = "sp-version-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "8.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] name = "sp-wasm-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 5.0.0", + "sp-std 7.0.0", "wasmi", - "wasmtime", + "wasmtime 6.0.2", ] [[package]] name = "sp-wasm-interface" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" +version = "14.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 7.0.0", - "wasmi", - "wasmtime", + "sp-std 8.0.0", + "wasmtime 8.0.1", ] [[package]] name = "sp-weights" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +version = "20.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "parity-scale-codec", "scale-info", "serde", "smallvec", "sp-arithmetic", - "sp-core 7.0.0", - "sp-debug-derive 5.0.0", - "sp-std 5.0.0", + "sp-core 21.0.0", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", ] [[package]] @@ -3903,9 +4595,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.41.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" +checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" dependencies = [ "Inflector", "num-format", @@ -3944,7 +4636,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "hyper", "log", @@ -3972,9 +4664,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ "proc-macro2", "quote", @@ -3989,9 +4681,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.9" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8e77cb757a61f51b947ec4a7e3646efd825b73561db1c232a8ccb639e611a0" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "termcolor" @@ -4004,22 +4696,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] @@ -4051,6 +4743,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -4068,11 +4769,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -4080,7 +4780,7 @@ dependencies = [ "num_cpus", "parking_lot", "pin-project-lite", - "socket2", + "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", ] @@ -4093,7 +4793,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] @@ -4131,19 +4831,36 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.0.0", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -4192,7 +4909,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] @@ -4320,9 +5037,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -4339,11 +5056,21 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -4362,6 +5089,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -4404,7 +5141,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", "wasm-bindgen-shared", ] @@ -4426,7 +5163,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4477,7 +5214,6 @@ dependencies = [ "memory_units", "num-rational", "num-traits", - "region", ] [[package]] @@ -4490,6 +5226,16 @@ dependencies = [ "url", ] +[[package]] +name = "wasmparser" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +dependencies = [ + "indexmap 1.9.3", + "url", +] + [[package]] name = "wasmtime" version = "6.0.2" @@ -4506,16 +5252,41 @@ dependencies = [ "once_cell", "paste", "psm", + "serde", + "target-lexicon", + "wasmparser 0.100.0", + "wasmtime-environ 6.0.2", + "wasmtime-jit 6.0.2", + "wasmtime-runtime 6.0.2", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "object 0.30.4", + "once_cell", + "paste", + "psm", "rayon", "serde", "target-lexicon", - "wasmparser", + "wasmparser 0.102.0", "wasmtime-cache", "wasmtime-cranelift", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", - "windows-sys 0.42.0", + "wasmtime-environ 8.0.1", + "wasmtime-jit 8.0.1", + "wasmtime-runtime 8.0.1", + "windows-sys 0.45.0", ] [[package]] @@ -4527,14 +5298,23 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "wasmtime-asm-macros" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" +dependencies = [ + "cfg-if", +] + [[package]] name = "wasmtime-cache" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" +checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213" dependencies = [ "anyhow", - "base64", + "base64 0.21.4", "bincode", "directories-next", "file-per-thread-logger", @@ -4542,30 +5322,46 @@ dependencies = [ "rustix 0.36.15", "serde", "sha2 0.10.7", - "toml", - "windows-sys 0.42.0", + "toml 0.5.11", + "windows-sys 0.45.0", "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "wasmtime-cranelift" -version = "6.0.2" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" +checksum = "b1cefde0cce8cb700b1b21b6298a3837dba46521affd7b8c38a9ee2c869eee04" dependencies = [ "anyhow", "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.95.1", "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.26.2", + "gimli 0.27.3", "log", - "object 0.29.0", + "object 0.30.4", "target-lexicon", "thiserror", - "wasmparser", - "wasmtime-environ", + "wasmparser 0.102.0", + "wasmtime-cranelift-shared", + "wasmtime-environ 8.0.1", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd041e382ef5aea1b9fc78442394f1a4f6d676ce457e7076ca4cb3f397882f8b" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-native", + "gimli 0.27.3", + "object 0.30.4", + "target-lexicon", + "wasmtime-environ 8.0.1", ] [[package]] @@ -4575,7 +5371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ "anyhow", - "cranelift-entity", + "cranelift-entity 0.93.2", "gimli 0.26.2", "indexmap 1.9.3", "log", @@ -4583,8 +5379,27 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasmparser", - "wasmtime-types", + "wasmparser 0.100.0", + "wasmtime-types 6.0.2", +] + +[[package]] +name = "wasmtime-environ" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +dependencies = [ + "anyhow", + "cranelift-entity 0.95.1", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object 0.30.4", + "serde", + "target-lexicon", + "thiserror", + "wasmparser 0.102.0", + "wasmtime-types 8.0.1", ] [[package]] @@ -4604,20 +5419,52 @@ dependencies = [ "rustc-demangle", "serde", "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-debug", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", + "wasmtime-environ 6.0.2", + "wasmtime-jit-icache-coherence 6.0.2", + "wasmtime-runtime 6.0.2", "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime-jit" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" +dependencies = [ + "addr2line 0.19.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "log", + "object 0.30.4", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ 8.0.1", + "wasmtime-jit-debug 8.0.1", + "wasmtime-jit-icache-coherence 8.0.1", + "wasmtime-runtime 8.0.1", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-jit-debug" version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ - "object 0.29.0", + "once_cell", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "object 0.30.4", "once_cell", "rustix 0.36.15", ] @@ -4633,6 +5480,17 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-runtime" version = "6.0.2" @@ -4651,22 +5509,58 @@ dependencies = [ "paste", "rand 0.8.5", "rustix 0.36.15", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", + "wasmtime-asm-macros 6.0.2", + "wasmtime-environ 6.0.2", + "wasmtime-jit-debug 6.0.2", "windows-sys 0.42.0", ] +[[package]] +name = "wasmtime-runtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.8.0", + "paste", + "rand 0.8.5", + "rustix 0.36.15", + "wasmtime-asm-macros 8.0.1", + "wasmtime-environ 8.0.1", + "wasmtime-jit-debug 8.0.1", + "windows-sys 0.45.0", +] + [[package]] name = "wasmtime-types" version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.93.2", + "serde", + "thiserror", + "wasmparser 0.100.0", +] + +[[package]] +name = "wasmtime-types" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +dependencies = [ + "cranelift-entity 0.95.1", "serde", "thiserror", - "wasmparser", + "wasmparser 0.102.0", ] [[package]] @@ -4716,7 +5610,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -4749,7 +5643,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -4769,17 +5663,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -4790,9 +5684,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -4802,9 +5696,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -4814,9 +5708,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -4826,9 +5720,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -4838,9 +5732,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -4850,9 +5744,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -4862,15 +5756,15 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] @@ -4884,6 +5778,18 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +dependencies = [ + "curve25519-dalek 4.1.0", + "rand_core 0.6.4", + "serde", + "zeroize", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -4901,7 +5807,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.33", ] [[package]] @@ -4915,11 +5821,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe 6.0.6", ] [[package]] @@ -4934,9 +5840,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", diff --git a/ismp-demo/Cargo.toml b/ismp-demo/Cargo.toml index 5fa3ebe64..7b07c0e6b 100644 --- a/ismp-demo/Cargo.toml +++ b/ismp-demo/Cargo.toml @@ -16,11 +16,11 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } # substrate -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-support = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +frame-system = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-core = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } # local pallet-ismp = { path = "../pallet-ismp", default-features = false } diff --git a/pallet-ismp/Cargo.toml b/pallet-ismp/Cargo.toml index c326650e5..60804cfbe 100644 --- a/pallet-ismp/Cargo.toml +++ b/pallet-ismp/Cargo.toml @@ -6,15 +6,15 @@ authors = ["Polytope Labs "] [dependencies] # substrate -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false, optional = true } +frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, optional = true } # polytope labs ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } @@ -33,7 +33,7 @@ ismp-primitives = { path = "./primitives", default-features = false } [dev-dependencies] env_logger = "0.10.0" -pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } ismp-testsuite = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } [features] diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml index fc5cb169c..08d676949 100644 --- a/pallet-ismp/primitives/Cargo.toml +++ b/pallet-ismp/primitives/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Polytope Labs "] [dependencies] # substrate -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } # polytope labs ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } @@ -18,9 +18,10 @@ codec = { package = "parity-scale-codec", version = "3.1.3", default-features = primitive-types = { version = "0.12.1", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-core = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } [features] default = ["std"] @@ -36,4 +37,5 @@ std = [ "frame-support/std", "sp-core/std", "sp-consensus-aura/std", + "sp-io/std" ] diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs index 7118babfd..75799f0bb 100644 --- a/pallet-ismp/primitives/src/lib.rs +++ b/pallet-ismp/primitives/src/lib.rs @@ -25,11 +25,10 @@ extern crate alloc; use alloc::{format, vec::Vec}; use codec::{Decode, Encode}; use core::{fmt::Debug, time::Duration}; -use frame_support::sp_runtime::Digest; use ismp::{error::Error, host::StateMachine}; use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; use sp_core::H256; -use sp_runtime::DigestItem; +use sp_runtime::{Digest, DigestItem}; pub mod mmr; diff --git a/pallet-ismp/primitives/src/mmr.rs b/pallet-ismp/primitives/src/mmr.rs index 5f265fe8e..f68609bb6 100644 --- a/pallet-ismp/primitives/src/mmr.rs +++ b/pallet-ismp/primitives/src/mmr.rs @@ -20,7 +20,7 @@ use core::fmt::Formatter; use codec::{Decode, Encode}; -use frame_support::sp_io; +//use frame_support::sp_io; use ismp::{ router::{Request, Response}, util::{hash_request, hash_response, Keccak256}, diff --git a/pallet-ismp/rpc/Cargo.toml b/pallet-ismp/rpc/Cargo.toml index 14af027b9..11c5c3ac1 100644 --- a/pallet-ismp/rpc/Cargo.toml +++ b/pallet-ismp/rpc/Cargo.toml @@ -19,9 +19,9 @@ pallet-ismp = { path = ".." } ismp-runtime-api = { path = "../runtime-api" } ismp-primitives = { path = "../primitives" } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } \ No newline at end of file +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } diff --git a/pallet-ismp/runtime-api/Cargo.toml b/pallet-ismp/runtime-api/Cargo.toml index 77161cf21..28633deef 100644 --- a/pallet-ismp/runtime-api/Cargo.toml +++ b/pallet-ismp/runtime-api/Cargo.toml @@ -8,8 +8,8 @@ authors = ["Polytope Labs "] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } pallet-ismp = { path = "..", default-features = false } ismp-primitives = { path = "../primitives", default-features = false } ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 38f5211c6..a940dd822 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -43,9 +43,7 @@ use codec::{Decode, Encode}; use core::time::Duration; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo, Pays, PostDispatchInfo}, - log::debug, traits::{Get, UnixTime}, - RuntimeDebug, }; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, @@ -54,6 +52,7 @@ use ismp_rs::{ messaging::CreateConsensusState, router::{Request, Response}, }; +use log::debug; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. use crate::{ @@ -61,12 +60,14 @@ use crate::{ mmr::mmr::Mmr, weight_info::get_weight, }; +use frame_system::pallet_prelude::BlockNumberFor; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; use ismp_rs::{host::IsmpHost, messaging::Message}; pub use pallet::*; +use sp_runtime::RuntimeDebug; use sp_std::prelude::*; // Definition of the pallet logic, to be aggregated at runtime definition through @@ -271,12 +272,12 @@ pub mod pallet { // Pallet implements [`Hooks`] trait to define some logic to execute in some context. #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(_n: T::BlockNumber) -> Weight { + fn on_initialize(_n: BlockNumberFor) -> Weight { // return Mmr finalization weight here ::WeightInfo::on_finalize(Self::number_of_leaves() as u32) } - fn on_finalize(_n: T::BlockNumber) { + fn on_finalize(_n: BlockNumberFor) { // Only finalize if mmr was modified let leaves = Self::number_of_leaves(); let root = if leaves != 0 { @@ -301,7 +302,7 @@ pub mod pallet { >::deposit_log(digest); } - fn offchain_worker(_n: T::BlockNumber) {} + fn offchain_worker(_n: BlockNumberFor) {} } /// Params to update the unbonding period for a consensus state diff --git a/pallet-ismp/src/mmr/storage.rs b/pallet-ismp/src/mmr/storage.rs index 95628c376..edcfddbfc 100644 --- a/pallet-ismp/src/mmr/storage.rs +++ b/pallet-ismp/src/mmr/storage.rs @@ -15,8 +15,8 @@ //! An MMR storage implementation. use codec::Encode; -use frame_support::log::{debug, trace}; use ismp_primitives::mmr::{DataOrHash, NodeIndex}; +use log::{debug, trace}; use mmr_lib::helper; use sp_core::offchain::StorageKind; use sp_std::iter::Peekable; diff --git a/pallet-ismp/src/mocks/mod.rs b/pallet-ismp/src/mocks/mod.rs index 548562f58..2b230ce8f 100644 --- a/pallet-ismp/src/mocks/mod.rs +++ b/pallet-ismp/src/mocks/mod.rs @@ -41,7 +41,7 @@ pub enum Test where NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, + System: frame_system::{Pallet, Call, Config, Storage, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, } @@ -69,19 +69,18 @@ impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; type Hash = H256; type Hashing = Keccak256; type AccountId = sp_core::sr25519::Public; type Lookup = IdentityLookup; - type Header = Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; type DbWeight = (); type BlockWeights = (); type BlockLength = (); type Version = (); + type Nonce = u64; + type Block = Block; type PalletInfo = PalletInfo; type AccountData = (); type OnNewAccount = (); diff --git a/pallet-ismp/src/primitives.rs b/pallet-ismp/src/primitives.rs index df985401a..23350f3e1 100644 --- a/pallet-ismp/src/primitives.rs +++ b/pallet-ismp/src/primitives.rs @@ -15,7 +15,7 @@ //! Pallet primitives use codec::{Decode, Encode}; -use frame_support::{weights::Weight, PalletId, RuntimeDebug}; +use frame_support::{weights::Weight, PalletId}; use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; use scale_info::TypeInfo; @@ -23,6 +23,7 @@ use sp_core::{ crypto::{AccountId32, ByteArray}, H160, }; +use sp_runtime::RuntimeDebug; use sp_std::prelude::*; /// An MMR proof data for a group of leaves. diff --git a/pallet-ismp/src/tests.rs b/pallet-ismp/src/tests.rs index 855d90e06..200ec0422 100644 --- a/pallet-ismp/src/tests.rs +++ b/pallet-ismp/src/tests.rs @@ -41,9 +41,10 @@ use sp_core::{ offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, H256, }; +use sp_runtime::BuildStorage; pub(crate) fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() + frame_system::GenesisConfig::::default().build_storage().unwrap().into() } fn register_offchain_ext(ext: &mut sp_io::TestExternalities) { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e26d01035..283b8d4b0 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-11-16" -components = [ "rustfmt" ] \ No newline at end of file +channel = "nightly-2023-08-23" +components = [ "rustfmt" ] From 663d51a10d637b04416defc4b3e7152c3b1158e7 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Sun, 24 Sep 2023 18:21:01 +0100 Subject: [PATCH 174/182] Register offchain extension (#93) * fix offchain calls * unique runtime api instances in loops --- pallet-ismp/rpc/src/lib.rs | 48 ++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index 3e82da881..fc84fa097 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -36,8 +36,9 @@ use ismp_rs::{ use ismp_runtime_api::IsmpRuntimeApi; use sc_client_api::{BlockBackend, ProofProvider}; use serde::{Deserialize, Serialize}; -use sp_api::ProvideRuntimeApi; +use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; +use sp_core::offchain::{storage::OffchainDb, OffchainDbExt, OffchainStorage}; use sp_runtime::traits::Block as BlockT; use std::{collections::HashMap, fmt::Display, sync::Arc}; @@ -140,21 +141,23 @@ where } /// An implementation of ISMP specific RPC methods. -pub struct IsmpRpcHandler { +pub struct IsmpRpcHandler { client: Arc, + offchain_db: OffchainDb, _marker: std::marker::PhantomData, } -impl IsmpRpcHandler { +impl IsmpRpcHandler { /// Create new `IsmpRpcHandler` with the given reference to the client. - pub fn new(client: Arc) -> Self { - Self { client, _marker: Default::default() } + pub fn new(client: Arc, offchain_storage: S) -> Self { + Self { client, offchain_db: OffchainDb::new(offchain_storage), _marker: Default::default() } } } -impl IsmpApiServer for IsmpRpcHandler +impl IsmpApiServer for IsmpRpcHandler where Block: BlockT, + S: OffchainStorage + Clone + Send + Sync + 'static, C: Send + Sync + 'static @@ -165,18 +168,24 @@ where C::Api: IsmpRuntimeApi, { fn query_requests(&self, query: Vec) -> Result> { - let api = self.client.runtime_api(); + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self.client.info().best_hash; - let request_indices: Vec = api - .get_request_leaf_indices(at, query) - .map_err(|_| runtime_error_into_rpc_error("Error fetching request leaf indices"))?; + let request_indices: Vec = + api.get_request_leaf_indices(at, query).map_err(|e| { + runtime_error_into_rpc_error(format!( + "Error fetching request leaf indices, {:?}", + e + )) + })?; api.get_requests(at, request_indices) .map_err(|_| runtime_error_into_rpc_error("Error fetching requests")) } fn query_responses(&self, query: Vec) -> Result> { - let api = self.client.runtime_api(); + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self.client.info().best_hash; let response_indices: Vec = api .get_response_leaf_indices(at, query) @@ -187,7 +196,8 @@ where } fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result { - let api = self.client.runtime_api(); + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self .client .block_hash(height.into()) @@ -206,7 +216,8 @@ where } fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result { - let api = self.client.runtime_api(); + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self .client .block_hash(height.into()) @@ -278,7 +289,8 @@ where } fn pending_get_requests(&self, height: u64) -> Result> { - let api = self.client.runtime_api(); + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = self.client.info().best_hash; api.pending_get_requests(at) @@ -290,9 +302,10 @@ where &self, block_numbers: Vec>, ) -> Result>> { - let api = self.client.runtime_api(); let mut events = HashMap::new(); for block_number_or_hash in block_numbers { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); let at = match block_number_or_hash { BlockNumberOrHash::Hash(block_hash) => block_hash, BlockNumberOrHash::Number(block_number) => { @@ -306,8 +319,9 @@ where let mut response_indices = vec![]; let mut temp: Vec = api .block_events(at) - .ok() - .ok_or_else(|| runtime_error_into_rpc_error("failed to read block events"))? + .map_err(|e| { + runtime_error_into_rpc_error(format!("failed to read block events {:?}", e)) + })? .into_iter() .filter_map(|event| match event { pallet_ismp::events::Event::Request { From a6ff49011e1ecb2e42edff9eb4d2b68a32d13331 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Sat, 30 Sep 2023 16:04:19 +0100 Subject: [PATCH 175/182] Update constants for mainnet and goerli (#31) * fix constants * nit --- Cargo.lock | 7 +++++ primitives/Cargo.toml | 2 +- primitives/src/constants.rs | 28 ++++++++--------- prover/Cargo.toml | 3 +- prover/src/test.rs | 63 ++++++++++++++----------------------- verifier/Cargo.toml | 4 +-- 6 files changed, 49 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 881214b8e..c60772bbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -830,6 +830,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dunce" version = "1.0.4" @@ -3385,6 +3391,7 @@ dependencies = [ "async-stream", "base2", "bls_on_arkworks", + "dotenv", "env_logger", "ethers", "hex", diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index f80ade770..17fa0fa16 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -33,4 +33,4 @@ std = [ "serde" ] mainnet = [] -testnet = [] +goerli = [] diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index 04c24300f..0934398ea 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -76,7 +76,7 @@ pub const BLOCK_ROOTS_INDEX_LOG2: u64 = 5; pub const HISTORICAL_ROOTS_INDEX_LOG2: u64 = 5; #[cfg(feature = "testnet")] -pub use testnet::*; +pub use goerli::*; #[cfg(feature = "mainnet")] pub use mainnet::*; @@ -85,19 +85,19 @@ use crate::ssz::ByteVector; #[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] pub use devnet::*; -#[cfg(feature = "testnet")] -pub mod testnet { +#[cfg(feature = "goerli")] +pub mod goerli { use super::*; pub const SLOTS_PER_EPOCH: Slot = 32; pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = - hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); - pub const BELLATRIX_FORK_VERSION: Version = [3, 0, 16, 32]; - pub const ALTAIR_FORK_VERSION: Version = [1, 0, 16, 32]; - pub const GENESIS_FORK_VERSION: Version = [0; 4]; + hex_literal::hex!("043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb"); + pub const BELLATRIX_FORK_VERSION: Version = hex_literal::hex!("02001020"); + pub const ALTAIR_FORK_VERSION: Version = hex_literal::hex!("01001020"); + pub const GENESIS_FORK_VERSION: Version = hex_literal::hex!("00001020"); pub const ALTAIR_FORK_EPOCH: Epoch = 36660; pub const BELLATRIX_FORK_EPOCH: Epoch = 112260; - pub const CAPELLA_FORK_EPOCH: Epoch = u64::MAX; - pub const CAPELLA_FORK_VERSION: Version = [3, 0, 16, 32]; + pub const CAPELLA_FORK_EPOCH: Epoch = 162304; + pub const CAPELLA_FORK_VERSION: Version = hex_literal::hex!("03001020"); } #[cfg(feature = "mainnet")] @@ -106,13 +106,13 @@ pub mod mainnet { pub const SLOTS_PER_EPOCH: Slot = 32; pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); - pub const BELLATRIX_FORK_VERSION: Version = [2, 0, 0, 0]; - pub const ALTAIR_FORK_VERSION: Version = [1, 0, 0, 0]; - pub const GENESIS_FORK_VERSION: Version = [0, 0, 0, 0]; + pub const BELLATRIX_FORK_VERSION: Version = hex_literal::hex!("02000000"); + pub const ALTAIR_FORK_VERSION: Version = hex_literal::hex!("01000000"); + pub const GENESIS_FORK_VERSION: Version = hex_literal::hex!("00000000"); pub const ALTAIR_FORK_EPOCH: Epoch = 74240; pub const BELLATRIX_FORK_EPOCH: Epoch = 144896; - pub const CAPELLA_FORK_EPOCH: Epoch = u64::MAX; - pub const CAPELLA_FORK_VERSION: Version = [3, 0, 0, 0]; + pub const CAPELLA_FORK_EPOCH: Epoch = 194048; + pub const CAPELLA_FORK_VERSION: Version = hex_literal::hex!("03000000"); } #[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 21ae744da..5dca6e39b 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -34,6 +34,7 @@ ethers = { version = "2.0.8", features = ["ws"] } tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"]} parity-scale-codec = "3.2.2" reqwest-eventsource = "0.4.0" +dotenv = "0.15.0" [features] default = ["std"] @@ -45,6 +46,6 @@ std = [ "bls_on_arkworks/std", "ark-bls12-381/std" ] -testnet = ["sync-committee-primitives/testnet", "sync-committee-verifier/testnet"] +goerli = ["sync-committee-primitives/goerli", "sync-committee-verifier/goerli"] mainnet = ["sync-committee-primitives/mainnet", "sync-committee-verifier/mainnet"] diff --git a/prover/src/test.rs b/prover/src/test.rs index de293a47a..a8cd07e98 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -1,9 +1,5 @@ use super::*; use base2::Base2; -use ethers::{ - prelude::{Http, Middleware, ProviderExt}, - providers::Provider, -}; use reqwest_eventsource::EventSource; use ssz_rs::{ calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, @@ -23,21 +19,11 @@ use sync_committee_verifier::{ }; use tokio_stream::StreamExt; -const CONSENSUS_NODE_URL: &'static str = "http://localhost:3500"; -const EL_NODE_URL: &'static str = "http://localhost:8545"; - -async fn wait_for_el(blocks: usize) { - let provider = Provider::::connect(EL_NODE_URL).await; - let sub = provider.watch_blocks().await.unwrap(); - let _ = sub.take(blocks).collect::>(); -} - #[cfg(test)] #[allow(non_snake_case)] #[tokio::test] async fn fetch_block_header_works() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let block_header = sync_committee_prover.fetch_header("head").await; assert!(block_header.is_ok()); } @@ -46,8 +32,7 @@ async fn fetch_block_header_works() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_block_works() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let block = sync_committee_prover.fetch_block("head").await; assert!(block.is_ok()); } @@ -56,8 +41,7 @@ async fn fetch_block_works() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_validator_works() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let validator = sync_committee_prover.fetch_validator("head", "0").await; assert!(validator.is_ok()); } @@ -67,8 +51,7 @@ async fn fetch_validator_works() { #[tokio::test] #[ignore] async fn fetch_processed_sync_committee_works() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let validator = sync_committee_prover.fetch_processed_sync_committee("head").await; assert!(validator.is_ok()); } @@ -78,7 +61,7 @@ async fn fetch_processed_sync_committee_works() { #[tokio::test] #[ignore] async fn generate_indexes() { - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let execution_payload_index = get_generalized_index( &beacon_state, @@ -114,8 +97,7 @@ async fn generate_indexes() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_beacon_state_works() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let beacon_state = sync_committee_prover.fetch_beacon_state("head").await; assert!(beacon_state.is_ok()); } @@ -124,8 +106,7 @@ async fn fetch_beacon_state_works() { #[allow(non_snake_case)] #[tokio::test] async fn state_root_and_block_header_root_matches() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let mut beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let block_header = sync_committee_prover.fetch_header(&beacon_state.slot.to_string()).await; @@ -141,8 +122,7 @@ async fn state_root_and_block_header_root_matches() { #[allow(non_snake_case)] #[tokio::test] async fn fetch_finality_checkpoints_work() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; assert!(finality_checkpoint.is_ok()); } @@ -151,8 +131,7 @@ async fn fetch_finality_checkpoints_work() { #[allow(non_snake_case)] #[tokio::test] async fn test_finalized_header() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let proof = ssz_rs::generate_proof(&mut state, &vec![FINALIZED_ROOT_INDEX as usize]).unwrap(); @@ -177,9 +156,9 @@ async fn test_finalized_header() { #[cfg(test)] #[allow(non_snake_case)] #[tokio::test] +#[ignore] async fn test_execution_payload_proof() { - wait_for_el(10).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let block_id = finalized_state.slot.to_string(); @@ -229,8 +208,7 @@ async fn test_execution_payload_proof() { #[allow(non_snake_case)] #[tokio::test] async fn test_sync_committee_update_proof() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); let block_id = finalized_state.slot.to_string(); @@ -269,11 +247,10 @@ async fn test_prover() { .filter_module("prover", LevelFilter::Debug) .format_module_path(false) .init(); - wait_for_el(1).await; - let node_url = format!("{}/eth/v1/events?topics=finalized_checkpoint", CONSENSUS_NODE_URL); - - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); + let node_url = + format!("{}/eth/v1/events?topics=finalized_checkpoint", sync_committee_prover.node_url); let block_header = sync_committee_prover.fetch_header("head").await.unwrap(); let state = sync_committee_prover @@ -339,8 +316,7 @@ async fn test_prover() { #[allow(non_snake_case)] #[tokio::test] async fn test_sync_committee_signature_verification() { - wait_for_el(1).await; - let sync_committee_prover = SyncCommitteeProver::new(CONSENSUS_NODE_URL.to_string()); + let sync_committee_prover = setup_prover(); let block = loop { let block = sync_committee_prover.fetch_block("head").await.unwrap(); if block.slot < 16 { @@ -398,3 +374,10 @@ pub struct EventResponse { pub epoch: String, pub execution_optimistic: bool, } + +fn setup_prover() -> SyncCommitteeProver { + dotenv::dotenv().ok(); + let consensus_url = + std::env::var("CONSENSUS_NODE_URL").unwrap_or("http://localhost:3500".to_string()); + SyncCommitteeProver::new(consensus_url) +} diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 2bfc47504..8d2a6bb0f 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -10,7 +10,7 @@ ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" , de log = { version = "0.4.17", default-features = false } anyhow = { version = "1.0.75", default-features = false } ark-ec = { version = "0.4.2", default-features = false } -ark-bls12-381 = { version = "0.4.0", default-features = false } +ark-bls12-381 = { version = "0.4.0", default-features = false, features = ["curve"] } bls_on_arkworks = { version = "0.2.2", default-features = false } [features] @@ -25,7 +25,7 @@ std = [ "ark-bls12-381/std", "bls_on_arkworks/std" ] -testnet = ["sync-committee-primitives/testnet"] +goerli = ["sync-committee-primitives/goerli"] mainnet = ["sync-committee-primitives/mainnet"] [dev-dependencies] From 8b306281c416215ffc4ac374d87f9341a4c434e9 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:28:15 +0100 Subject: [PATCH 176/182] fix feature flags (#32) --- primitives/src/constants.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/primitives/src/constants.rs b/primitives/src/constants.rs index 0934398ea..f8491b49d 100644 --- a/primitives/src/constants.rs +++ b/primitives/src/constants.rs @@ -75,14 +75,14 @@ pub const NEXT_SYNC_COMMITTEE_INDEX_LOG2: u64 = 5; pub const BLOCK_ROOTS_INDEX_LOG2: u64 = 5; pub const HISTORICAL_ROOTS_INDEX_LOG2: u64 = 5; -#[cfg(feature = "testnet")] +#[cfg(feature = "goerli")] pub use goerli::*; #[cfg(feature = "mainnet")] pub use mainnet::*; use crate::ssz::ByteVector; -#[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] +#[cfg(all(not(feature = "mainnet"), not(feature = "goerli")))] pub use devnet::*; #[cfg(feature = "goerli")] @@ -115,7 +115,7 @@ pub mod mainnet { pub const CAPELLA_FORK_VERSION: Version = hex_literal::hex!("03000000"); } -#[cfg(all(not(feature = "mainnet"), not(feature = "testnet")))] +#[cfg(all(not(feature = "mainnet"), not(feature = "goerli")))] pub mod devnet { use super::*; use hex_literal::hex; From fac81fb05187f761a4c6bc5b96e19932684b4579 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Wed, 4 Oct 2023 16:26:14 +0100 Subject: [PATCH 177/182] store latest messaging heights (#94) * store latest messaging height * typo * typo --- pallet-ismp/rpc/src/lib.rs | 12 ++++++++++++ pallet-ismp/runtime-api/src/lib.rs | 3 +++ pallet-ismp/src/lib.rs | 28 +++++++++++++++++++++++++--- pallet-ismp/src/mocks/mod.rs | 16 ++++++---------- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/pallet-ismp/rpc/src/lib.rs b/pallet-ismp/rpc/src/lib.rs index fc84fa097..df39fe272 100644 --- a/pallet-ismp/rpc/src/lib.rs +++ b/pallet-ismp/rpc/src/lib.rs @@ -127,6 +127,10 @@ where #[method(name = "ismp_queryStateMachineLatestHeight")] fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result; + /// Query the most recent height at which we've processed requests for a state machine + #[method(name = "ismp_queryLatestMessagingHeight")] + fn query_latest_messaging_height(&self, id: StateMachineId) -> Result; + /// Query ISMP Events that were deposited in a series of blocks /// Using String keys because HashMap fails to deserialize when key is not a String #[method(name = "ismp_queryEvents")] @@ -390,4 +394,12 @@ where } Ok(events) } + + fn query_latest_messaging_height(&self, id: StateMachineId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.latest_messaging_height(at, id).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching latest state machine height") + }) + } } diff --git a/pallet-ismp/runtime-api/src/lib.rs b/pallet-ismp/runtime-api/src/lib.rs index a8364c5ec..7384add94 100644 --- a/pallet-ismp/runtime-api/src/lib.rs +++ b/pallet-ismp/runtime-api/src/lib.rs @@ -60,6 +60,9 @@ sp_api::decl_runtime_apis! { /// Return the latest height of the state machine fn latest_state_machine_height(id: StateMachineId) -> Option; + /// Return the most recent height we've processed requests for a state machine + fn latest_messaging_height(id: StateMachineId) -> Option; + /// Get Request Leaf Indices fn get_request_leaf_indices(leaf_queries: Vec) -> Vec; diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index a940dd822..360b6927b 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -65,7 +65,7 @@ use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; -use ismp_rs::{host::IsmpHost, messaging::Message}; +use ismp_rs::{consensus::StateMachineHeight, host::IsmpHost, messaging::Message}; pub use pallet::*; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; @@ -170,10 +170,16 @@ pub mod pallet { /// Holds a map of state machines to the height at which they've been frozen due to byzantine /// behaviour #[pallet::storage] - #[pallet::getter(fn frozen_heights)] + #[pallet::getter(fn latest_messaging_heights)] pub type FrozenHeights = StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + /// Holds a map of state machines to the latest height we've processed requests for + #[pallet::storage] + #[pallet::getter(fn frozen_heights)] + pub type LatestMessagingHeight = + StorageMap<_, Blake2_128Concat, StateMachineId, u64, ValueQuery>; + /// A mapping of ConsensusStateId to ConsensusClientId #[pallet::storage] pub type ConsensusStateClient = @@ -468,7 +474,7 @@ impl Pallet { let mut errors: Vec = vec![]; let total_weight = get_weight::(&messages); for message in messages { - match handle_incoming_message(&host, message) { + match handle_incoming_message(&host, message.clone()) { Ok(MessageResult::ConsensusMessage(res)) => { // check if this is a trusted state machine let is_trusted_state_machine = host @@ -508,9 +514,25 @@ impl Pallet { } } Ok(MessageResult::Response(res)) => { + let StateMachineHeight { id, height } = match message { + Message::Response(ref response) => response.proof().height.clone(), + _ => unreachable!(), + }; + // update the messaging heights + if LatestMessagingHeight::::get(&id) < height { + LatestMessagingHeight::::insert(id, height); + } debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Response(res)); } Ok(MessageResult::Request(res)) => { + let StateMachineHeight { id, height } = match message { + Message::Request(ref request) => request.proof.height.clone(), + _ => unreachable!(), + }; + // update the messaging heights + if LatestMessagingHeight::::get(&id) < height { + LatestMessagingHeight::::insert(id, height); + } debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Request(res)); } Ok(MessageResult::Timeout(res)) => { diff --git a/pallet-ismp/src/mocks/mod.rs b/pallet-ismp/src/mocks/mod.rs index 2b230ce8f..afbcfd0af 100644 --- a/pallet-ismp/src/mocks/mod.rs +++ b/pallet-ismp/src/mocks/mod.rs @@ -14,7 +14,7 @@ // limitations under the License. //! Mock implementations for tests & benchmarks -#![allow(missing_docs)] +#![allow(missing_docs, dead_code, unused_imports)] pub mod ismp; use crate as pallet_ismp; @@ -36,15 +36,11 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( -pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, - Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, - } + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, + } ); pub struct StateMachineProvider; From fce91d63ebc90d62cb4487c4dd81174cbc348370 Mon Sep 17 00:00:00 2001 From: David Salami <31099392+Wizdave97@users.noreply.github.com> Date: Wed, 4 Oct 2023 23:02:01 +0100 Subject: [PATCH 178/182] Update loop boundary for signature search (#33) * change loop exit case for signature block search * nit * nit --- prover/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 6cfc7366a..b537d2c22 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -243,9 +243,9 @@ impl SyncCommitteeProver { let state_period = compute_sync_committee_period_at_slot(client_state.finalized_header.slot); loop { - // If we get to an epoch that is less than the attested epoch for the last known - // finalized header we exit - if compute_epoch_at_slot(block.slot) < client_state.latest_finalized_epoch + 2 { + // If we get to an epoch that is less than the finalized epoch for the notification + if compute_epoch_at_slot(block.slot) <= finality_checkpoint.epoch { + debug!(target: "prover", "Signature block search has reached epoch <= finalized epoch {} block_epoch {}", finality_checkpoint.epoch, compute_epoch_at_slot(block.slot)); return Ok(None) } @@ -267,7 +267,6 @@ impl SyncCommitteeProver { let attested_header = self.fetch_header(&attested_block_id).await?; let mut attested_state = self.fetch_beacon_state(&get_block_id(attested_header.state_root)).await?; - if attested_state.finalized_checkpoint.root == Node::default() { return Ok(None) } From 7930c3a3983f1c571cca06a5e4826ac1b962870f Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Thu, 5 Oct 2023 21:11:16 +0100 Subject: [PATCH 179/182] dedupe proofs --- primitives/src/types.rs | 4 +-- prover/src/lib.rs | 28 +++++++++++-------- prover/src/test.rs | 8 +++--- verifier/Cargo.toml | 6 ++-- .../{signature_verification.rs => crypto.rs} | 12 ++++---- verifier/src/lib.rs | 22 +++++++-------- 6 files changed, 42 insertions(+), 38 deletions(-) rename verifier/src/{signature_verification.rs => crypto.rs} (92%) diff --git a/primitives/src/types.rs b/primitives/src/types.rs index de5ad4be4..16f2b4f7c 100644 --- a/primitives/src/types.rs +++ b/primitives/src/types.rs @@ -85,7 +85,7 @@ pub struct SyncCommitteeUpdate { /// Minimum state required by the light client to validate new sync committee attestations #[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] -pub struct LightClientState { +pub struct VerifierState { /// The latest recorded finalized header pub finalized_header: BeaconBlockHeader, /// Latest finalized epoch @@ -106,7 +106,7 @@ pub struct FinalityProof { /// Data required to advance the state of the light client. #[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] -pub struct LightClientUpdate { +pub struct VerifierStateUpdate { /// the header that the sync committee signed pub attested_header: BeaconBlockHeader, /// the sync committee has potentially changed, here's an ssz proof for that. diff --git a/prover/src/lib.rs b/prover/src/lib.rs index b537d2c22..d0c376ecd 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -9,8 +9,11 @@ use anyhow::anyhow; use bls_on_arkworks::{point_to_pubkey, types::G1ProjectivePoint}; use log::debug; use reqwest::Client; -use sync_committee_primitives::consensus_types::{ - BeaconBlock, BeaconBlockHeader, BeaconState, Checkpoint, SyncCommittee, Validator, +use sync_committee_primitives::{ + consensus_types::{ + BeaconBlock, BeaconBlockHeader, BeaconState, Checkpoint, SyncCommittee, Validator, + }, + types::VerifierState, }; use crate::{ @@ -36,8 +39,8 @@ use sync_committee_primitives::{ VALIDATOR_REGISTRY_LIMIT, }, types::{ - AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, LightClientUpdate, - SyncCommitteeUpdate, + AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, SyncCommitteeUpdate, + VerifierStateUpdate, }, util::{ compute_epoch_at_slot, compute_sync_committee_period_at_slot, @@ -45,7 +48,7 @@ use sync_committee_primitives::{ }, }; -use sync_committee_verifier::{signature_verification::pubkey_to_projective, LightClientState}; +use sync_committee_verifier::{crypto::pubkey_to_projective, VerifierState}; pub type BeaconStateType = BeaconState< SLOTS_PER_HISTORICAL_ROOT, @@ -219,12 +222,12 @@ impl SyncCommitteeProver { pub async fn fetch_light_client_update( &self, - client_state: LightClientState, + client_state: VerifierState, finality_checkpoint: Checkpoint, debug_target: &str, - ) -> Result, anyhow::Error> { + ) -> Result, anyhow::Error> { if finality_checkpoint.root == Node::default() || - finality_checkpoint.epoch <= client_state.latest_finalized_epoch + client_state.latest_finalized_epoch >= finality_checkpoint.epoch { return Ok(None) } @@ -244,8 +247,11 @@ impl SyncCommitteeProver { compute_sync_committee_period_at_slot(client_state.finalized_header.slot); loop { // If we get to an epoch that is less than the finalized epoch for the notification - if compute_epoch_at_slot(block.slot) <= finality_checkpoint.epoch { - debug!(target: "prover", "Signature block search has reached epoch <= finalized epoch {} block_epoch {}", finality_checkpoint.epoch, compute_epoch_at_slot(block.slot)); + let current_epoch = compute_epoch_at_slot(block.slot); + if current_epoch <= finality_checkpoint.epoch || + current_epoch == client_state.latest_finalized_epoch + { + debug!(target: "prover", "Signature block search has reached an invalid epoch {} finalized_block_epoch {}", compute_epoch_at_slot(block.slot), finality_checkpoint.epoch); return Ok(None) } @@ -292,7 +298,7 @@ impl SyncCommitteeProver { }; // construct light client - let light_client_update = LightClientUpdate { + let light_client_update = VerifierStateUpdate { attested_header, sync_committee_update, finalized_header, diff --git a/prover/src/test.rs b/prover/src/test.rs index a8cd07e98..ef77c9262 100644 --- a/prover/src/test.rs +++ b/prover/src/test.rs @@ -11,11 +11,11 @@ use sync_committee_primitives::{ Root, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_INDEX_LOG2, GENESIS_FORK_VERSION, GENESIS_VALIDATORS_ROOT, NEXT_SYNC_COMMITTEE_INDEX_LOG2, }, - types::LightClientState, + types::VerifierState, util::{compute_domain, compute_fork_version, compute_signing_root}, }; use sync_committee_verifier::{ - signature_verification::verify_aggregate_signature, verify_sync_committee_attestation, + crypto::verify_aggregate_signature, verify_sync_committee_attestation, }; use tokio_stream::StreamExt; @@ -258,7 +258,7 @@ async fn test_prover() { .await .unwrap(); - let mut client_state = LightClientState { + let mut client_state = VerifierState { finalized_header: block_header.clone(), latest_finalized_epoch: 0, current_sync_committee: state.current_sync_committee, @@ -285,7 +285,7 @@ async fn test_prover() { }; let encoded = light_client_update.encode(); - let decoded = LightClientUpdate::decode(&mut &*encoded).unwrap(); + let decoded = VerifierStateUpdate::decode(&mut &*encoded).unwrap(); assert_eq!(light_client_update, decoded); client_state = diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 8d2a6bb0f..a4fa84e76 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -11,7 +11,7 @@ log = { version = "0.4.17", default-features = false } anyhow = { version = "1.0.75", default-features = false } ark-ec = { version = "0.4.2", default-features = false } ark-bls12-381 = { version = "0.4.0", default-features = false, features = ["curve"] } -bls_on_arkworks = { version = "0.2.2", default-features = false } +bls = { package = "bls_on_arkworks", version = "0.2.2", default-features = false } [features] default = ["std"] @@ -23,10 +23,10 @@ std = [ "anyhow/std", "ark-ec/std", "ark-bls12-381/std", - "bls_on_arkworks/std" + "bls/std" ] goerli = ["sync-committee-primitives/goerli"] mainnet = ["sync-committee-primitives/mainnet"] [dev-dependencies] -hex = "0.4.3" \ No newline at end of file +hex = "0.4.3" diff --git a/verifier/src/signature_verification.rs b/verifier/src/crypto.rs similarity index 92% rename from verifier/src/signature_verification.rs rename to verifier/src/crypto.rs index 06aa4f27c..2cbaa1dee 100644 --- a/verifier/src/signature_verification.rs +++ b/verifier/src/crypto.rs @@ -2,15 +2,15 @@ use alloc::vec::Vec; use anyhow::anyhow; use ark_bls12_381::Bls12_381; use ark_ec::{pairing::Pairing, AffineRepr}; -use bls_on_arkworks::{ - hash_to_point, pubkey_to_point, signature_to_point, +use bls::{ types::{BLS12381Pairing, G1AffinePoint, G1ProjectivePoint, G2AffinePoint, Signature}, DST_ETHEREUM, }; use sync_committee_primitives::constants::BlsPublicKey; pub fn pubkey_to_projective(compressed_key: &BlsPublicKey) -> anyhow::Result { - let affine_point = pubkey_to_point(&compressed_key.to_vec()).map_err(|e| anyhow!("{:?}", e))?; + let affine_point = + bls::pubkey_to_point(&compressed_key.to_vec()).map_err(|e| anyhow!("{:?}", e))?; Ok(affine_point.into()) } @@ -42,10 +42,10 @@ pub fn verify_aggregate_signature( ) -> anyhow::Result<()> { let subset_aggregate = subtract_points_from_aggregate(aggregate, non_participants)?; let aggregate_key_point: G1AffinePoint = subset_aggregate.into(); - let signature = signature_to_point(signature).map_err(|e| anyhow!("{:?}", e))?; + let signature = bls::signature_to_point(signature).map_err(|e| anyhow!("{:?}", e))?; let dst = DST_ETHEREUM.as_bytes().to_vec(); - let q = hash_to_point(&msg, &dst); + let q = bls::hash_to_point(&msg, &dst); let c1 = pairing(q, aggregate_key_point); @@ -66,7 +66,7 @@ pub fn verify_aggregate_signature( #[cfg(test)] mod tests { - use crate::signature_verification::verify_aggregate_signature; + use crate::crypto::verify_aggregate_signature; #[test] fn test_signature_verification() { diff --git a/verifier/src/lib.rs b/verifier/src/lib.rs index 589f1cafa..f64f1b72e 100644 --- a/verifier/src/lib.rs +++ b/verifier/src/lib.rs @@ -3,10 +3,10 @@ #[warn(unused_variables)] extern crate alloc; +pub mod crypto; pub mod error; -pub mod signature_verification; -use crate::{error::Error, signature_verification::verify_aggregate_signature}; +use crate::{crypto::verify_aggregate_signature, error::Error}; use alloc::vec::Vec; use ssz_rs::{ calculate_multi_merkle_root, prelude::is_valid_merkle_branch, GeneralizedIndex, Merkleized, @@ -21,20 +21,18 @@ use sync_committee_primitives::{ GENESIS_FORK_VERSION, GENESIS_VALIDATORS_ROOT, NEXT_SYNC_COMMITTEE_INDEX, NEXT_SYNC_COMMITTEE_INDEX_LOG2, }, + types::{VerifierState, VerifierStateUpdate}, util::{ compute_domain, compute_epoch_at_slot, compute_fork_version, compute_signing_root, compute_sync_committee_period_at_slot, should_get_sync_committee_update, }, }; -pub type LightClientState = sync_committee_primitives::types::LightClientState; -pub type LightClientUpdate = sync_committee_primitives::types::LightClientUpdate; - /// This function simply verifies a sync committee's attestation & it's finalized counterpart. pub fn verify_sync_committee_attestation( - trusted_state: LightClientState, - mut update: LightClientUpdate, -) -> Result { + trusted_state: VerifierState, + mut update: VerifierStateUpdate, +) -> Result { if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX_LOG2 as usize && update.sync_committee_update.is_some() && update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != @@ -188,20 +186,20 @@ pub fn verify_sync_committee_attestation( } } - let new_light_client_state = if let Some(sync_committee_update) = update.sync_committee_update { - LightClientState { + let verifier_state = if let Some(sync_committee_update) = update.sync_committee_update { + VerifierState { finalized_header: update.finalized_header, latest_finalized_epoch: update.finality_proof.epoch, current_sync_committee: trusted_state.next_sync_committee, next_sync_committee: sync_committee_update.next_sync_committee, } } else { - LightClientState { + VerifierState { finalized_header: update.finalized_header, latest_finalized_epoch: update.finality_proof.epoch, ..trusted_state } }; - Ok(new_light_client_state) + Ok(verifier_state) } From b8154a35e55110dd8b73108232aa8b2deedfebec Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Thu, 5 Oct 2023 21:45:13 +0100 Subject: [PATCH 180/182] merge pallet-ismp & sync-committee-rs --- .github/workflows/test.yml | 49 +- Cargo.lock | 1433 ++++++++++++----- Cargo.toml | 16 +- .../consensus/sync-committee/README.md | 54 +- .../sync-committee/primitives/Cargo.toml | 36 + .../primitives/src/consensus_types.rs | 406 +++++ .../primitives/src/constants.rs | 132 ++ .../sync-committee/primitives/src/domains.rs | 29 + .../sync-committee/primitives/src/error.rs | 24 + .../sync-committee/primitives/src/lib.rs | 16 + .../sync-committee/primitives/src/serde.rs | 142 ++ .../primitives/src/ssz/byte_list.rs | 88 + .../primitives/src/ssz/byte_vector.rs | 81 + .../sync-committee/primitives/src/ssz/mod.rs | 16 + .../sync-committee/primitives/src/types.rs | 126 ++ .../sync-committee/primitives/src/util.rs | 87 + .../sync-committee/prover/Cargo.toml | 51 + .../sync-committee/prover/src/lib.rs | 402 +++++ .../responses/beacon_block_header_response.rs | 19 + .../src/responses/beacon_block_response.rs | 37 + .../src/responses/beacon_state_response.rs | 28 + .../responses/finality_checkpoint_response.rs | 14 + .../prover/src/responses/mod.rs | 8 + .../src/responses/sync_committee_response.rs | 11 + .../src/responses/validator_response.rs | 15 + .../sync-committee/prover/src/routes.rs | 21 + .../sync-committee/prover/src/test.rs | 383 +++++ .../sync-committee/verifier/Cargo.toml | 32 + .../sync-committee/verifier/src/crypto.rs | 108 ++ .../sync-committee/verifier/src/error.rs | 29 + .../sync-committee/verifier/src/lib.rs | 205 +++ parachain/modules/ismp/core/Cargo.toml | 66 + .../modules/ismp/core/src/benchmarking.rs | 290 ++++ parachain/modules/ismp/core/src/dispatcher.rs | 89 + parachain/modules/ismp/core/src/errors.rs | 175 ++ parachain/modules/ismp/core/src/events.rs | 78 + parachain/modules/ismp/core/src/handlers.rs | 71 + parachain/modules/ismp/core/src/host.rs | 286 ++++ parachain/modules/ismp/core/src/lib.rs | 781 +++++++++ parachain/modules/ismp/core/src/mmr.rs | 20 + parachain/modules/ismp/core/src/mmr/mmr.rs | 110 ++ .../modules/ismp/core/src/mmr/storage.rs | 184 +++ parachain/modules/ismp/core/src/mmr/utils.rs | 43 + parachain/modules/ismp/core/src/mocks/ismp.rs | 151 ++ parachain/modules/ismp/core/src/mocks/mod.rs | 116 ++ parachain/modules/ismp/core/src/primitives.rs | 109 ++ parachain/modules/ismp/core/src/tests.rs | 384 +++++ .../modules/ismp/core/src/weight_info.rs | 334 ++++ parachain/modules/ismp/demo/Cargo.toml | 41 + parachain/modules/ismp/demo/src/lib.rs | 384 +++++ .../{consensus => ismp}/parachain/Cargo.toml | 0 .../parachain/inherent/Cargo.toml | 0 .../parachain/inherent/src/lib.rs | 0 .../parachain/runtime-api/Cargo.toml | 0 .../parachain/runtime-api/src/lib.rs | 0 .../parachain/src/consensus.rs | 0 .../{consensus => ismp}/parachain/src/lib.rs | 0 parachain/modules/ismp/primitives/Cargo.toml | 41 + parachain/modules/ismp/primitives/src/lib.rs | 113 ++ parachain/modules/ismp/primitives/src/mmr.rs | 106 ++ parachain/modules/ismp/rpc/Cargo.toml | 27 + parachain/modules/ismp/rpc/src/lib.rs | 404 +++++ parachain/modules/ismp/runtime-api/Cargo.toml | 26 + parachain/modules/ismp/runtime-api/src/lib.rs | 81 + .../sync-committee/Cargo.toml | 6 +- .../modules/ismp/sync-committee/README.md | 2 + .../sync-committee/src/arbitrum.rs | 0 .../sync-committee/src/beacon_client.rs | 0 .../sync-committee/src/lib.rs | 0 .../sync-committee/src/optimism.rs | 0 .../sync-committee/src/pallet.rs | 0 .../sync-committee/src/presets.rs | 0 .../sync-committee/src/tests.rs | 0 .../sync-committee/src/types.rs | 6 +- .../sync-committee/src/utils.rs | 0 parachain/node/Cargo.toml | 4 +- parachain/runtime/Cargo.toml | 12 +- parachain/runtime/src/lib.rs | 2 +- scripts/wait_for_tcp_port_opening.sh | 12 + 79 files changed, 8237 insertions(+), 415 deletions(-) create mode 100644 parachain/modules/consensus/sync-committee/primitives/Cargo.toml create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/consensus_types.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/constants.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/domains.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/error.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/lib.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/serde.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_list.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_vector.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/ssz/mod.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/types.rs create mode 100644 parachain/modules/consensus/sync-committee/primitives/src/util.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/Cargo.toml create mode 100644 parachain/modules/consensus/sync-committee/prover/src/lib.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_header_response.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_response.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/beacon_state_response.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/finality_checkpoint_response.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/mod.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/sync_committee_response.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/responses/validator_response.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/routes.rs create mode 100644 parachain/modules/consensus/sync-committee/prover/src/test.rs create mode 100644 parachain/modules/consensus/sync-committee/verifier/Cargo.toml create mode 100644 parachain/modules/consensus/sync-committee/verifier/src/crypto.rs create mode 100644 parachain/modules/consensus/sync-committee/verifier/src/error.rs create mode 100644 parachain/modules/consensus/sync-committee/verifier/src/lib.rs create mode 100644 parachain/modules/ismp/core/Cargo.toml create mode 100644 parachain/modules/ismp/core/src/benchmarking.rs create mode 100644 parachain/modules/ismp/core/src/dispatcher.rs create mode 100644 parachain/modules/ismp/core/src/errors.rs create mode 100644 parachain/modules/ismp/core/src/events.rs create mode 100644 parachain/modules/ismp/core/src/handlers.rs create mode 100644 parachain/modules/ismp/core/src/host.rs create mode 100644 parachain/modules/ismp/core/src/lib.rs create mode 100644 parachain/modules/ismp/core/src/mmr.rs create mode 100644 parachain/modules/ismp/core/src/mmr/mmr.rs create mode 100644 parachain/modules/ismp/core/src/mmr/storage.rs create mode 100644 parachain/modules/ismp/core/src/mmr/utils.rs create mode 100644 parachain/modules/ismp/core/src/mocks/ismp.rs create mode 100644 parachain/modules/ismp/core/src/mocks/mod.rs create mode 100644 parachain/modules/ismp/core/src/primitives.rs create mode 100644 parachain/modules/ismp/core/src/tests.rs create mode 100644 parachain/modules/ismp/core/src/weight_info.rs create mode 100644 parachain/modules/ismp/demo/Cargo.toml create mode 100644 parachain/modules/ismp/demo/src/lib.rs rename parachain/modules/{consensus => ismp}/parachain/Cargo.toml (100%) rename parachain/modules/{consensus => ismp}/parachain/inherent/Cargo.toml (100%) rename parachain/modules/{consensus => ismp}/parachain/inherent/src/lib.rs (100%) rename parachain/modules/{consensus => ismp}/parachain/runtime-api/Cargo.toml (100%) rename parachain/modules/{consensus => ismp}/parachain/runtime-api/src/lib.rs (100%) rename parachain/modules/{consensus => ismp}/parachain/src/consensus.rs (100%) rename parachain/modules/{consensus => ismp}/parachain/src/lib.rs (100%) create mode 100644 parachain/modules/ismp/primitives/Cargo.toml create mode 100644 parachain/modules/ismp/primitives/src/lib.rs create mode 100644 parachain/modules/ismp/primitives/src/mmr.rs create mode 100644 parachain/modules/ismp/rpc/Cargo.toml create mode 100644 parachain/modules/ismp/rpc/src/lib.rs create mode 100644 parachain/modules/ismp/runtime-api/Cargo.toml create mode 100644 parachain/modules/ismp/runtime-api/src/lib.rs rename parachain/modules/{consensus => ismp}/sync-committee/Cargo.toml (87%) create mode 100644 parachain/modules/ismp/sync-committee/README.md rename parachain/modules/{consensus => ismp}/sync-committee/src/arbitrum.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/beacon_client.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/lib.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/optimism.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/pallet.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/presets.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/tests.rs (100%) rename parachain/modules/{consensus => ismp}/sync-committee/src/types.rs (90%) rename parachain/modules/{consensus => ismp}/sync-committee/src/utils.rs (100%) create mode 100755 scripts/wait_for_tcp_port_opening.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c7f4d919..d77f8d647 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,8 @@ jobs: check: name: Check Workspace runs-on: ubuntu-latest + env: + RUSTFLAGS: "-C link-args=-Wl,--allow-multiple-definition" steps: - uses: actions/checkout@v2 with: @@ -37,7 +39,11 @@ jobs: ssh-private-key: ${{ secrets.SSH_KEY }} - name: check workspace - run: cargo +nightly check --all --benches --locked + run: | + cargo +nightly check --all --benches --locked + cargo +nightly check -p sync-committee-verifier --no-default-features --target=wasm32-unknown-unknown --locked + cargo +nightly check -p pallet-ismp --no-default-features --target=wasm32-unknown-unknown --locked + cargo +nightly check -p ismp-demo --no-default-features --target=wasm32-unknown-unknown --locked check-runtime-wasm: name: Check Runtime Wasm @@ -74,3 +80,44 @@ jobs: - name: Cargo fmt run: cargo +nightly fmt --all --check + + test: + name: Test Suite + runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/main' + env: + TUID: 123 + steps: + - name: set UID env + run: | + echo $UID + echo "TUID=$UID" >> $GITHUB_ENV + + - name: Checkout sources + uses: actions/checkout@master + + - name: Install rust stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + + - name: Install protoc + run: | + sudo apt update + sudo apt install protobuf-compiler + + - name: Run pallet-ismp unit tests + run: | + cargo +nightly test -p pallet-ismp --all-targets --all-features --verbose --locked + + - name: Clone eth-pos-devnet repository + run: | + git clone https://github.com/polytope-labs/eth-pos-devnet.git + cd eth-pos-devnet + docker compose up -d + ../scripts/wait_for_tcp_port_opening.sh localhost 3500 + ../scripts/wait_for_tcp_port_opening.sh localhost 8545 + + - name: Run all tests + run: | + cargo +nightly test -p sync-committee-prover -- --nocapture diff --git a/Cargo.lock b/Cargo.lock index 3a2628e56..0c8d3e474 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,15 @@ dependencies = [ "regex", ] +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.2", +] + [[package]] name = "addr2line" version = "0.19.0" @@ -524,6 +533,12 @@ dependencies = [ "sha3", ] +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + [[package]] name = "array-bytes" version = "6.1.0" @@ -752,6 +767,28 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "async-task" version = "4.4.0" @@ -889,6 +926,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d6cf42565b8bd996c9f583069619124475caa645d598d75918923b240409be" +dependencies = [ + "int", +] + [[package]] name = "base64" version = "0.13.1" @@ -1770,7 +1816,7 @@ version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.95.1", ] [[package]] @@ -1783,7 +1829,7 @@ dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", - "cranelift-entity", + "cranelift-entity 0.95.1", "cranelift-isle", "gimli 0.27.3", "hashbrown 0.13.2", @@ -1808,6 +1854,15 @@ version = "0.95.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8" +[[package]] +name = "cranelift-entity" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" +dependencies = [ + "serde", +] + [[package]] name = "cranelift-entity" version = "0.95.1" @@ -1853,13 +1908,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff3220489a3d928ad91e59dd7aeaa8b3de18afb554a6211213673a71c90737ac" dependencies = [ "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.95.1", "cranelift-frontend", "itertools 0.10.5", "log", "smallvec", - "wasmparser", - "wasmtime-types", + "wasmparser 0.102.0", + "wasmtime-types 8.0.1", ] [[package]] @@ -2019,7 +2074,7 @@ dependencies = [ "sc-cli", "sc-client-api", "sc-service", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "url", ] @@ -2042,7 +2097,7 @@ dependencies = [ "sc-client-api", "sp-api", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "tracing", ] @@ -2079,7 +2134,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-aura", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-keystore", "sp-runtime", @@ -2110,7 +2165,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-slots", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-timestamp", "sp-trie", @@ -2150,7 +2205,7 @@ dependencies = [ "sc-client-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-state-machine", "tracing", @@ -2210,7 +2265,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-transaction-pool", ] @@ -2230,7 +2285,7 @@ dependencies = [ "sp-application-crypto", "sp-consensus-aura", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -2246,7 +2301,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "staging-xcm", ] @@ -2267,13 +2322,13 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "scale-info", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-inherents", "sp-io", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", "sp-trie", "sp-version", "staging-xcm", @@ -2302,7 +2357,7 @@ dependencies = [ "pallet-session", "parity-scale-codec", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -2317,7 +2372,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "staging-xcm", ] @@ -2337,7 +2392,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "staging-xcm", "staging-xcm-executor", ] @@ -2353,7 +2408,7 @@ dependencies = [ "sp-api", "sp-consensus-aura", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -2368,7 +2423,7 @@ dependencies = [ "scale-info", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-trie", "staging-xcm", ] @@ -2386,12 +2441,12 @@ dependencies = [ "sc-client-api", "scale-info", "sp-api", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", "sp-state-machine", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-trie", "tracing", ] @@ -2405,7 +2460,7 @@ dependencies = [ "futures", "parity-scale-codec", "sp-inherents", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] @@ -2421,7 +2476,7 @@ dependencies = [ "polkadot-runtime-common", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -2446,7 +2501,7 @@ dependencies = [ "sc-tracing", "sp-api", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-state-machine", ] @@ -2474,7 +2529,7 @@ name = "cumulus-relay-chain-minimal-node" version = "0.1.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", @@ -2531,10 +2586,10 @@ dependencies = [ "sp-api", "sp-authority-discovery", "sp-consensus-babe", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-state-machine", - "sp-storage", + "sp-storage 13.0.0", "thiserror", "tokio", "tokio-util", @@ -2552,7 +2607,7 @@ dependencies = [ "polkadot-primitives", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", "sp-trie", ] @@ -3018,6 +3073,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "downcast" version = "0.11.0" @@ -3596,6 +3657,7 @@ dependencies = [ "const-hex", "enr", "ethers-core", + "futures-channel", "futures-core", "futures-timer", "futures-util", @@ -3677,6 +3739,17 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite 0.2.13", +] + [[package]] name = "exit-future" version = "0.2.0" @@ -3915,6 +3988,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "fork-tree" version = "3.0.0" @@ -3954,12 +4042,12 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "static_assertions", ] @@ -3969,7 +4057,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "Inflector", - "array-bytes", + "array-bytes 6.1.0", "chrono", "clap", "comfy-table", @@ -3996,17 +4084,17 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-database", - "sp-externalities", + "sp-externalities 0.19.0", "sp-inherents", "sp-io", "sp-keystore", "sp-runtime", "sp-state-machine", - "sp-storage", + "sp-storage 13.0.0", "sp-trie", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "thiserror", "thousands", ] @@ -4033,10 +4121,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-npos-elections", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -4050,11 +4138,11 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] @@ -4081,7 +4169,7 @@ dependencies = [ "log", "parity-scale-codec", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-state-machine", @@ -4114,9 +4202,9 @@ dependencies = [ "smallvec", "sp-api", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-core-hashing-proc-macro", - "sp-debug-derive", + "sp-debug-derive 8.0.0", "sp-genesis-builder", "sp-inherents", "sp-io", @@ -4124,8 +4212,8 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-state-machine", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "sp-weights", "static_assertions", "tt-call", @@ -4182,10 +4270,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-version", "sp-weights", ] @@ -4200,9 +4288,9 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -4223,7 +4311,7 @@ dependencies = [ "parity-scale-codec", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -4475,6 +4563,16 @@ dependencies = [ "polyval 0.6.1", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "stable_deref_trait", +] + [[package]] name = "gimli" version = "0.27.3" @@ -4832,6 +4930,19 @@ dependencies = [ "webpki-roots 0.23.1", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyperbridge" version = "0.1.4" @@ -4882,7 +4993,7 @@ dependencies = [ "sp-block-builder", "sp-blockchain", "sp-consensus-aura", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-keystore", "sp-offchain", @@ -4920,7 +5031,7 @@ dependencies = [ "hex-literal 0.3.4", "ismp", "ismp-demo", - "ismp-primitives", + "ismp-primitives 0.1.0", "ismp-runtime-api", "ismp-sync-committee", "log", @@ -4928,7 +5039,7 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-collator-selection", - "pallet-ismp", + "pallet-ismp 0.1.0", "pallet-session", "pallet-sudo", "pallet-timestamp", @@ -4944,12 +5055,12 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-consensus-aura", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-offchain", "sp-runtime", "sp-session", - "sp-std", + "sp-std 8.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -5158,6 +5269,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "int" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d64bb35c7fc709fa8934dd85f3d0c0e418a3b067e62e6c6041dd19519c0899b" +dependencies = [ + "num-traits", +] + [[package]] name = "integer-encoding" version = "3.0.4" @@ -5263,41 +5383,34 @@ dependencies = [ [[package]] name = "ismp-demo" version = "0.1.0" -source = "git+https://github.com/polytope-labs/substrate-ismp.git?branch=main#2450eecec705a7dfdfe382ea6afdb0c4ef7f57c2" dependencies = [ "frame-support", "frame-system", "ismp", "pallet-balances", - "pallet-ismp", + "pallet-ismp 0.1.0", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-runtime", ] [[package]] -name = "ismp-parachain" +name = "ismp-primitives" version = "0.1.0" dependencies = [ - "cumulus-pallet-parachain-system", - "cumulus-primitives-core", + "ckb-merkle-mountain-range", "frame-support", "frame-system", - "hex-literal 0.4.1", "ismp", - "ismp-primitives", - "pallet-ismp", "parity-scale-codec", "primitive-types", "scale-info", "serde", "sp-consensus-aura", - "sp-inherents", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-trie", - "substrate-state-machine", ] [[package]] @@ -5314,7 +5427,7 @@ dependencies = [ "scale-info", "serde", "sp-consensus-aura", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", ] @@ -5322,37 +5435,35 @@ dependencies = [ [[package]] name = "ismp-rpc" version = "0.1.0" -source = "git+https://github.com/polytope-labs/substrate-ismp.git?branch=main#2450eecec705a7dfdfe382ea6afdb0c4ef7f57c2" dependencies = [ "frame-system", "hex-literal 0.3.4", "ismp", - "ismp-primitives", + "ismp-primitives 0.1.0", "ismp-runtime-api", "jsonrpsee", - "pallet-ismp", + "pallet-ismp 0.1.0", "parity-scale-codec", "sc-client-api", "serde", "serde_json", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", ] [[package]] name = "ismp-runtime-api" version = "0.1.0" -source = "git+https://github.com/polytope-labs/substrate-ismp.git?branch=main#2450eecec705a7dfdfe382ea6afdb0c4ef7f57c2" dependencies = [ "ismp", - "ismp-primitives", - "pallet-ismp", + "ismp-primitives 0.1.0", + "pallet-ismp 0.1.0", "parity-scale-codec", "serde", "sp-api", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -5372,10 +5483,10 @@ dependencies = [ "hex", "hex-literal 0.3.4", "ismp", - "pallet-ismp", + "pallet-ismp 0.1.0", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-trie", @@ -5385,6 +5496,17 @@ dependencies = [ "trie-db 0.24.0", ] +[[package]] +name = "ismp-testsuite" +version = "0.1.0" +source = "git+https://github.com/polytope-labs/ismp-rs.git?branch=main#5c4e0f412de788b5d86de8146b49f6db6795ca12" +dependencies = [ + "ismp", + "parity-scale-codec", + "primitive-types", + "sp-core 20.0.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -5617,7 +5739,7 @@ dependencies = [ "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-weights", ] @@ -6513,6 +6635,12 @@ dependencies = [ "hash-db 0.16.0", ] +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + [[package]] name = "merlin" version = "2.0.1" @@ -6594,7 +6722,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-mmr-primitives", "sp-runtime", ] @@ -6610,7 +6738,7 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-mmr-primitives", "sp-runtime", ] @@ -6765,6 +6893,24 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "netlink-packet-core" version = "0.4.2" @@ -6982,6 +7128,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "memchr", +] + [[package]] name = "object" version = "0.30.4" @@ -7064,12 +7222,50 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -7153,7 +7349,7 @@ dependencies = [ "sp-application-crypto", "sp-consensus-aura", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7169,7 +7365,7 @@ dependencies = [ "sp-application-crypto", "sp-authority-discovery", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7183,7 +7379,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7202,12 +7398,12 @@ dependencies = [ "scale-info", "sp-application-crypto", "sp-consensus-babe", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7225,11 +7421,11 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] @@ -7244,7 +7440,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7264,7 +7460,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7272,7 +7468,7 @@ name = "pallet-beefy-mmr" version = "4.0.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "binary-merkle-tree", "frame-support", "frame-system", @@ -7285,11 +7481,11 @@ dependencies = [ "serde", "sp-api", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7304,10 +7500,10 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7323,10 +7519,10 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7345,7 +7541,7 @@ dependencies = [ "scale-info", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7359,10 +7555,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7379,7 +7575,7 @@ dependencies = [ "serde", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7394,10 +7590,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7415,11 +7611,11 @@ dependencies = [ "rand 0.8.5", "scale-info", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-npos-elections", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "strum 0.24.1", ] @@ -7434,7 +7630,7 @@ dependencies = [ "parity-scale-codec", "sp-npos-elections", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7448,12 +7644,12 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-npos-elections", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7472,7 +7668,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7490,12 +7686,12 @@ dependencies = [ "scale-info", "sp-application-crypto", "sp-consensus-grandpa", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7511,7 +7707,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7527,11 +7723,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7544,11 +7740,37 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-keyring", "sp-runtime", - "sp-std", + "sp-std 8.0.0", +] + +[[package]] +name = "pallet-ismp" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "derive_more", + "enum-as-inner", + "env_logger 0.10.0", + "frame-benchmarking", + "frame-support", + "frame-system", + "ismp", + "ismp-primitives 0.1.0", + "ismp-testsuite", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core 21.0.0", + "sp-io", + "sp-runtime", + "sp-std 8.0.0", ] [[package]] @@ -7563,16 +7785,16 @@ dependencies = [ "frame-support", "frame-system", "ismp", - "ismp-primitives", + "ismp-primitives 0.1.0 (git+https://github.com/polytope-labs/substrate-ismp.git?branch=main)", "log", "parity-scale-codec", "scale-info", "serde", "sp-api", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7586,10 +7808,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7604,10 +7826,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-weights", ] @@ -7622,11 +7844,11 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-mmr-primitives", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7642,7 +7864,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7656,9 +7878,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7672,12 +7894,12 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-staking", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] @@ -7695,9 +7917,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-runtime-interface", + "sp-runtime-interface 17.0.0", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7708,7 +7930,7 @@ dependencies = [ "pallet-nomination-pools", "parity-scale-codec", "sp-api", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7725,7 +7947,7 @@ dependencies = [ "serde", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7749,7 +7971,7 @@ dependencies = [ "scale-info", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7763,10 +7985,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7781,7 +8003,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7796,10 +8018,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7814,7 +8036,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7833,7 +8055,7 @@ dependencies = [ "sp-arithmetic", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7850,7 +8072,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-weights", ] @@ -7866,13 +8088,13 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", "sp-session", "sp-staking", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", "sp-trie", ] @@ -7890,7 +8112,7 @@ dependencies = [ "rand 0.8.5", "sp-runtime", "sp-session", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7908,7 +8130,7 @@ dependencies = [ "sp-arithmetic", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7931,7 +8153,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7974,10 +8196,10 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -7992,7 +8214,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8009,8 +8231,8 @@ dependencies = [ "sp-inherents", "sp-io", "sp-runtime", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-timestamp", ] @@ -8027,10 +8249,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8043,10 +8265,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8059,7 +8281,7 @@ dependencies = [ "parity-scale-codec", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-rpc", "sp-runtime", "sp-weights", @@ -8091,7 +8313,7 @@ dependencies = [ "scale-info", "serde", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8104,10 +8326,10 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8122,7 +8344,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8137,7 +8359,7 @@ dependencies = [ "scale-info", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8153,10 +8375,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "staging-xcm", "staging-xcm-executor", ] @@ -8174,7 +8396,7 @@ dependencies = [ "scale-info", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", @@ -8191,7 +8413,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8658,7 +8880,7 @@ dependencies = [ "polkadot-primitives", "rand 0.8.5", "schnellru", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "thiserror", "tracing-gum", @@ -8703,7 +8925,7 @@ dependencies = [ "sc-storage-monitor", "sc-sysinfo", "sc-tracing", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-keyring", "sp-maybe-compressed-blob", @@ -8726,7 +8948,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-runtime", "thiserror", @@ -8741,9 +8963,9 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot dependencies = [ "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -8780,7 +9002,7 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "reed-solomon-novelpoly", - "sp-core", + "sp-core 21.0.0", "sp-trie", "thiserror", ] @@ -8801,7 +9023,7 @@ dependencies = [ "sc-network", "sc-network-common", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "tracing-gum", ] @@ -8841,7 +9063,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-core", + "sp-core 21.0.0", "sp-maybe-compressed-blob", "thiserror", "tracing-gum", @@ -9074,9 +9296,9 @@ dependencies = [ "polkadot-primitives", "rand 0.8.5", "slotmap", - "sp-core", + "sp-core 21.0.0", "sp-maybe-compressed-blob", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "substrate-build-script-utils", "tempfile", "tokio", @@ -9114,10 +9336,10 @@ dependencies = [ "sc-executor", "sc-executor-common", "sc-executor-wasmtime", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-io", - "sp-tracing", + "sp-tracing 10.0.0", "tokio", "tracing-gum", ] @@ -9139,7 +9361,7 @@ dependencies = [ "sc-executor-wasmtime", "sp-io", "sp-maybe-compressed-blob", - "sp-tracing", + "sp-tracing 10.0.0", "tikv-jemalloc-ctl", "tokio", "tracing-gum", @@ -9173,7 +9395,7 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "sc-network", - "sp-core", + "sp-core 21.0.0", "thiserror", "tokio", ] @@ -9235,7 +9457,7 @@ dependencies = [ "serde", "sp-application-crypto", "sp-consensus-babe", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-maybe-compressed-blob", "sp-runtime", @@ -9304,7 +9526,7 @@ dependencies = [ "rand 0.8.5", "schnellru", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "thiserror", "tracing-gum", @@ -9328,7 +9550,7 @@ dependencies = [ "sc-client-api", "schnellru", "sp-api", - "sp-core", + "sp-core 21.0.0", "tikv-jemalloc-ctl", "tracing-gum", ] @@ -9345,9 +9567,9 @@ dependencies = [ "polkadot-core-primitives", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -9385,13 +9607,13 @@ dependencies = [ "sp-arithmetic", "sp-authority-discovery", "sp-consensus-slots", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-keystore", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -9503,7 +9725,7 @@ dependencies = [ "sp-block-builder", "sp-consensus-babe", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-mmr-primitives", @@ -9512,8 +9734,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -9557,14 +9779,14 @@ dependencies = [ "serde_derive", "slot-range-helper", "sp-api", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-npos-elections", "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", "staging-xcm", "static_assertions", ] @@ -9578,7 +9800,7 @@ dependencies = [ "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-weights", ] @@ -9592,8 +9814,8 @@ dependencies = [ "frame-benchmarking", "parity-scale-codec", "polkadot-primitives", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", ] [[package]] @@ -9629,14 +9851,14 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-keystore", "sp-runtime", "sp-session", "sp-staking", - "sp-std", + "sp-std 8.0.0", "staging-xcm", "staging-xcm-executor", "static_assertions", @@ -9739,7 +9961,7 @@ dependencies = [ "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-keyring", @@ -9749,7 +9971,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-state-machine", - "sp-storage", + "sp-storage 13.0.0", "sp-timestamp", "sp-transaction-pool", "sp-version", @@ -9792,7 +10014,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot dependencies = [ "parity-scale-codec", "polkadot-primitives", - "sp-core", + "sp-core 21.0.0", ] [[package]] @@ -10481,10 +10703,12 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite 0.2.13", @@ -10494,16 +10718,35 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.25.2", "winreg", ] +[[package]] +name = "reqwest-eventsource" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f03f570355882dd8d15acc3a313841e6e90eddbc76a93c748fd82cc13ba9f51" +dependencies = [ + "eventsource-stream", + "futures-core", + "futures-timer", + "mime", + "nom", + "pin-project-lite 0.2.13", + "reqwest", + "thiserror", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -10675,7 +10918,7 @@ dependencies = [ "sp-block-builder", "sp-consensus-babe", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-mmr-primitives", @@ -10683,8 +10926,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -10703,7 +10946,7 @@ dependencies = [ "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-weights", ] @@ -11024,8 +11267,8 @@ version = "4.1.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "log", - "sp-core", - "sp-wasm-interface", + "sp-core 21.0.0", + "sp-wasm-interface 14.0.0", "thiserror", ] @@ -11050,7 +11293,7 @@ dependencies = [ "sp-api", "sp-authority-discovery", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", @@ -11074,7 +11317,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", "substrate-prometheus-endpoint", @@ -11090,7 +11333,7 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", ] @@ -11109,7 +11352,7 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-state-machine", ] @@ -11130,7 +11373,7 @@ name = "sc-cli" version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "chrono", "clap", "fdlimit", @@ -11153,7 +11396,7 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-keyring", "sp-keystore", "sp-panic-handler", @@ -11180,13 +11423,13 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-database", - "sp-externalities", + "sp-externalities 0.19.0", "sp-runtime", "sp-state-machine", "sp-statement-store", - "sp-storage", + "sp-storage 13.0.0", "substrate-prometheus-endpoint", ] @@ -11209,7 +11452,7 @@ dependencies = [ "schnellru", "sp-arithmetic", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-database", "sp-runtime", "sp-state-machine", @@ -11234,7 +11477,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-state-machine", "substrate-prometheus-endpoint", @@ -11262,7 +11505,7 @@ dependencies = [ "sp-consensus", "sp-consensus-aura", "sp-consensus-slots", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-keystore", "sp-runtime", @@ -11298,7 +11541,7 @@ dependencies = [ "sp-consensus", "sp-consensus-babe", "sp-consensus-slots", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-keystore", "sp-runtime", @@ -11322,7 +11565,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-runtime", "thiserror", @@ -11333,7 +11576,7 @@ name = "sc-consensus-beefy" version = "4.0.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "async-channel", "async-trait", "fnv", @@ -11353,7 +11596,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-mmr-primitives", "sp-runtime", @@ -11376,7 +11619,7 @@ dependencies = [ "sc-rpc", "serde", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "thiserror", ] @@ -11400,7 +11643,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "ahash 0.8.3", - "array-bytes", + "array-bytes 6.1.0", "async-trait", "dyn-clone", "finality-grandpa", @@ -11428,7 +11671,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-grandpa", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", @@ -11450,7 +11693,7 @@ dependencies = [ "sc-rpc", "serde", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "thiserror", ] @@ -11472,7 +11715,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-slots", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", "sp-state-machine", @@ -11489,14 +11732,14 @@ dependencies = [ "sc-executor-wasmtime", "schnellru", "sp-api", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-io", "sp-panic-handler", - "sp-runtime-interface", + "sp-runtime-interface 17.0.0", "sp-trie", "sp-version", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "tracing", ] @@ -11507,7 +11750,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface", + "sp-wasm-interface 14.0.0", "thiserror", "wasm-instrument", ] @@ -11524,9 +11767,9 @@ dependencies = [ "rustix 0.36.15", "sc-allocator", "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", - "wasmtime", + "sp-runtime-interface 17.0.0", + "sp-wasm-interface 14.0.0", + "wasmtime 8.0.1", ] [[package]] @@ -11550,11 +11793,11 @@ name = "sc-keystore" version = "4.0.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "parking_lot 0.12.1", "serde_json", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "thiserror", ] @@ -11564,7 +11807,7 @@ name = "sc-network" version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "async-channel", "async-trait", "asynchronous-codec", @@ -11591,7 +11834,7 @@ dependencies = [ "smallvec", "sp-arithmetic", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "substrate-prometheus-endpoint", "thiserror", @@ -11660,7 +11903,7 @@ name = "sc-network-light" version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "async-channel", "futures", "libp2p-identity", @@ -11671,7 +11914,7 @@ dependencies = [ "sc-client-api", "sc-network", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "thiserror", ] @@ -11681,7 +11924,7 @@ name = "sc-network-sync" version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "async-channel", "async-trait", "fork-tree", @@ -11704,7 +11947,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-grandpa", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "substrate-prometheus-endpoint", "thiserror", @@ -11715,7 +11958,7 @@ name = "sc-network-transactions" version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "futures", "libp2p", "log", @@ -11733,7 +11976,7 @@ name = "sc-offchain" version = "4.0.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "bytes", "fnv", "futures", @@ -11753,8 +11996,8 @@ dependencies = [ "sc-transaction-pool-api", "sc-utils", "sp-api", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-keystore", "sp-offchain", "sp-runtime", @@ -11791,7 +12034,7 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-offchain", "sp-rpc", @@ -11814,7 +12057,7 @@ dependencies = [ "scale-info", "serde", "serde_json", - "sp-core", + "sp-core 21.0.0", "sp-rpc", "sp-runtime", "sp-version", @@ -11841,7 +12084,7 @@ name = "sc-rpc-spec-v2" version = "0.10.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "futures", "futures-util", "hex", @@ -11856,7 +12099,7 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-version", "thiserror", @@ -11908,13 +12151,13 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-keystore", "sp-runtime", "sp-session", "sp-state-machine", - "sp-storage", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", @@ -11936,7 +12179,7 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sp-core", + "sp-core 21.0.0", ] [[package]] @@ -11948,7 +12191,7 @@ dependencies = [ "fs4", "log", "sc-client-db", - "sp-core", + "sp-core 21.0.0", "thiserror", "tokio", ] @@ -11986,9 +12229,9 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", - "sp-core", + "sp-core 21.0.0", "sp-io", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -12029,10 +12272,10 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-rpc", "sp-runtime", - "sp-tracing", + "sp-tracing 10.0.0", "thiserror", "tracing", "tracing-log", @@ -12068,9 +12311,9 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-tracing", + "sp-tracing 10.0.0", "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror", @@ -12087,7 +12330,7 @@ dependencies = [ "parity-scale-codec", "serde", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "thiserror", ] @@ -12588,7 +12831,7 @@ dependencies = [ "parity-scale-codec", "paste", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -12681,7 +12924,7 @@ dependencies = [ "smallvec", "soketto", "twox-hash", - "wasmi", + "wasmi 0.31.0", "x25519-dalek 2.0.0", "zeroize", ] @@ -12806,12 +13049,12 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-metadata-ir", "sp-runtime", "sp-state-machine", - "sp-std", + "sp-std 8.0.0", "sp-trie", "sp-version", "thiserror", @@ -12839,9 +13082,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-io", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -12854,7 +13097,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 8.0.0", "static_assertions", ] @@ -12868,7 +13111,7 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -12879,7 +13122,7 @@ dependencies = [ "sp-api", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -12908,7 +13151,7 @@ dependencies = [ "async-trait", "futures", "log", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", "sp-state-machine", @@ -12928,7 +13171,7 @@ dependencies = [ "sp-consensus-slots", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] @@ -12944,10 +13187,10 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-consensus-slots", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] @@ -12962,11 +13205,11 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-mmr-primitives", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "strum 0.24.1", ] @@ -12982,10 +13225,10 @@ dependencies = [ "serde", "sp-api", "sp-application-crypto", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -12996,16 +13239,60 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 8.0.0", "sp-timestamp", ] +[[package]] +name = "sp-core" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" +dependencies = [ + "array-bytes 4.2.0", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58 0.4.0", + "dyn-clonable", + "ed25519-zebra 3.1.0", + "futures", + "hash-db 0.16.0", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin 2.0.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel 0.9.1", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 8.0.0", + "sp-debug-derive 7.0.0", + "sp-externalities 0.18.0", + "sp-runtime-interface 16.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + [[package]] name = "sp-core" version = "21.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ - "array-bytes", + "array-bytes 6.1.0", "arrayvec 0.7.4", "bandersnatch_vrfs", "bitflags 1.3.2", @@ -13033,12 +13320,12 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-core-hashing", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-core-hashing 9.0.0", + "sp-debug-derive 8.0.0", + "sp-externalities 0.19.0", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", "ss58-registry", "substrate-bip39", "thiserror", @@ -13047,6 +13334,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sp-core-hashing" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 7.0.0", + "twox-hash", +] + [[package]] name = "sp-core-hashing" version = "9.0.0" @@ -13066,7 +13368,7 @@ version = "9.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "quote", - "sp-core-hashing", + "sp-core-hashing 9.0.0", "syn 2.0.37", ] @@ -13079,6 +13381,17 @@ dependencies = [ "parking_lot 0.12.1", ] +[[package]] +name = "sp-debug-derive" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62211eed9ef9dac4b9d837c56ccc9f8ee4fc49d9d9b7e6b9daf098fe173389ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sp-debug-derive" version = "8.0.0" @@ -13089,6 +13402,18 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "sp-externalities" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae0f275760689aaefe967943331d458cd99f5169d18364365d4cb584b246d1c" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 7.0.0", + "sp-storage 12.0.0", +] + [[package]] name = "sp-externalities" version = "0.19.0" @@ -13096,8 +13421,8 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot dependencies = [ "environmental", "parity-scale-codec", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", ] [[package]] @@ -13108,7 +13433,7 @@ dependencies = [ "serde_json", "sp-api", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -13121,7 +13446,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "thiserror", ] @@ -13137,13 +13462,13 @@ dependencies = [ "parity-scale-codec", "rustversion", "secp256k1", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-keystore", - "sp-runtime-interface", + "sp-runtime-interface 17.0.0", "sp-state-machine", - "sp-std", - "sp-tracing", + "sp-std 8.0.0", + "sp-tracing 10.0.0", "sp-trie", "tracing", "tracing-core", @@ -13155,7 +13480,7 @@ version = "24.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "lazy_static", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "strum 0.24.1", ] @@ -13167,8 +13492,8 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "thiserror", ] @@ -13189,7 +13514,7 @@ dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -13203,10 +13528,10 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core", - "sp-debug-derive", + "sp-core 21.0.0", + "sp-debug-derive 8.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "thiserror", ] @@ -13219,9 +13544,9 @@ dependencies = [ "scale-info", "serde", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -13230,7 +13555,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "sp-api", - "sp-core", + "sp-core 21.0.0", "sp-runtime", ] @@ -13251,7 +13576,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot dependencies = [ "rustc-hash", "serde", - "sp-core", + "sp-core 21.0.0", ] [[package]] @@ -13270,12 +13595,31 @@ dependencies = [ "serde", "sp-application-crypto", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-io", - "sp-std", + "sp-std 8.0.0", "sp-weights", ] +[[package]] +name = "sp-runtime-interface" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5d0cd80200bf85b8b064238b2508b69b6146b13adf36066ec5d924825af737" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.18.0", + "sp-runtime-interface-proc-macro 10.0.0", + "sp-std 7.0.0", + "sp-storage 12.0.0", + "sp-tracing 9.0.0", + "sp-wasm-interface 13.0.0", + "static_assertions", +] + [[package]] name = "sp-runtime-interface" version = "17.0.0" @@ -13285,15 +13629,28 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-storage", - "sp-tracing", - "sp-wasm-interface", + "sp-externalities 0.19.0", + "sp-runtime-interface-proc-macro 11.0.0", + "sp-std 8.0.0", + "sp-storage 13.0.0", + "sp-tracing 10.0.0", + "sp-wasm-interface 14.0.0", "static_assertions", ] +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae5b00aef477127ddb6177b3464ad1e2bdcc12ee913fc5dfc9d065c6cea89b" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" @@ -13314,11 +13671,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-core", + "sp-core 21.0.0", "sp-keystore", "sp-runtime", "sp-staking", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -13330,9 +13687,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-runtime", - "sp-std", + "sp-std 8.0.0", ] [[package]] @@ -13346,10 +13703,10 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "smallvec", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-panic-handler", - "sp-std", + "sp-std 8.0.0", "sp-trie", "thiserror", "tracing", @@ -13371,20 +13728,40 @@ dependencies = [ "sha2 0.10.7", "sp-api", "sp-application-crypto", - "sp-core", - "sp-externalities", + "sp-core 21.0.0", + "sp-externalities 0.19.0", "sp-runtime", - "sp-runtime-interface", - "sp-std", + "sp-runtime-interface 17.0.0", + "sp-std 8.0.0", "thiserror", "x25519-dalek 2.0.0", ] +[[package]] +name = "sp-std" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" + [[package]] name = "sp-std" version = "8.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" +[[package]] +name = "sp-storage" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad1f8c52d4700ac7bc42b3375679a6c6fc1fe876f4b40c6efdf36f933ef0291" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 7.0.0", + "sp-std 7.0.0", +] + [[package]] name = "sp-storage" version = "13.0.0" @@ -13394,8 +13771,8 @@ dependencies = [ "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", ] [[package]] @@ -13407,17 +13784,30 @@ dependencies = [ "parity-scale-codec", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "thiserror", ] +[[package]] +name = "sp-tracing" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00fab60bf3d42255ce3f678903d3a2564662371c75623de4a1ffc7cac46143df" +dependencies = [ + "parity-scale-codec", + "sp-std 7.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "sp-tracing" version = "10.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#f60318f68687e601c47de5ad5ca88e2c3f8139a7" dependencies = [ "parity-scale-codec", - "sp-std", + "sp-std 8.0.0", "tracing", "tracing-core", "tracing-subscriber", @@ -13440,10 +13830,10 @@ dependencies = [ "async-trait", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-trie", ] @@ -13462,8 +13852,8 @@ dependencies = [ "parking_lot 0.12.1", "scale-info", "schnellru", - "sp-core", - "sp-std", + "sp-core 21.0.0", + "sp-std 8.0.0", "thiserror", "tracing", "trie-db 0.27.1", @@ -13482,7 +13872,7 @@ dependencies = [ "serde", "sp-core-hashing-proc-macro", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-version-proc-macro", "thiserror", ] @@ -13498,6 +13888,21 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "sp-wasm-interface" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "153b7374179439e2aa783c66ed439bd86920c67bbc95d34c76390561972bc02f" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 7.0.0", + "wasmi 0.13.2", + "wasmtime 6.0.2", +] + [[package]] name = "sp-wasm-interface" version = "14.0.0" @@ -13507,8 +13912,8 @@ dependencies = [ "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std", - "wasmtime", + "sp-std 8.0.0", + "wasmtime 8.0.1", ] [[package]] @@ -13521,9 +13926,9 @@ dependencies = [ "serde", "smallvec", "sp-arithmetic", - "sp-core", - "sp-debug-derive", - "sp-std", + "sp-core 21.0.0", + "sp-debug-derive 8.0.0", + "sp-std 8.0.0", ] [[package]] @@ -13702,7 +14107,7 @@ dependencies = [ "sp-block-builder", "sp-consensus-babe", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-mmr-primitives", @@ -13711,8 +14116,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -13755,7 +14160,7 @@ dependencies = [ "sp-arithmetic", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-weights", "staging-xcm", "staging-xcm-executor", @@ -13773,10 +14178,10 @@ dependencies = [ "log", "parity-scale-codec", "sp-arithmetic", - "sp-core", + "sp-core 21.0.0", "sp-io", "sp-runtime", - "sp-std", + "sp-std 8.0.0", "sp-weights", "staging-xcm", ] @@ -13930,7 +14335,7 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core", + "sp-core 21.0.0", "sp-runtime", ] @@ -13968,13 +14373,13 @@ dependencies = [ "frame-system", "hash-db 0.16.0", "ismp", - "ismp-primitives", - "pallet-ismp", + "ismp-primitives 0.1.0 (git+https://github.com/polytope-labs/substrate-ismp.git?branch=main)", + "pallet-ismp 0.1.0 (git+https://github.com/polytope-labs/substrate-ismp.git?branch=main)", "parity-scale-codec", "primitive-types", "scale-info", "serde", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-trie", ] @@ -13989,7 +14394,7 @@ dependencies = [ "sc-client-api", "sc-rpc-api", "serde", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-state-machine", "sp-trie", @@ -14080,7 +14485,6 @@ dependencies = [ [[package]] name = "sync-committee-primitives" version = "0.1.0" -source = "git+https://github.com/polytope-labs/sync-committee-rs?branch=main#9438b0530d7c741bc72875b722c1d9c13c1a7dea" dependencies = [ "anyhow", "ark-bls12-381", @@ -14094,15 +14498,43 @@ dependencies = [ "ssz-rs", ] +[[package]] +name = "sync-committee-prover" +version = "0.1.0" +dependencies = [ + "anyhow", + "ark-bls12-381", + "ark-ec", + "async-stream", + "base2", + "bls_on_arkworks", + "dotenv", + "env_logger 0.10.0", + "ethers", + "hex", + "log", + "parity-scale-codec", + "primitive-types", + "reqwest", + "reqwest-eventsource", + "serde", + "serde_json", + "ssz-rs", + "sync-committee-primitives", + "sync-committee-verifier", + "tokio", + "tokio-stream", +] + [[package]] name = "sync-committee-verifier" version = "0.1.0" -source = "git+https://github.com/polytope-labs/sync-committee-rs?branch=main#9438b0530d7c741bc72875b722c1d9c13c1a7dea" dependencies = [ "anyhow", "ark-bls12-381", "ark-ec", "bls_on_arkworks", + "hex", "log", "ssz-rs", "sync-committee-primitives", @@ -14402,6 +14834,16 @@ dependencies = [ "syn 2.0.37", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-retry" version = "0.3.0" @@ -14767,9 +15209,9 @@ dependencies = [ "sp-api", "sp-consensus-aura", "sp-consensus-babe", - "sp-core", - "sp-debug-derive", - "sp-externalities", + "sp-core 21.0.0", + "sp-debug-derive 8.0.0", + "sp-externalities 0.19.0", "sp-inherents", "sp-io", "sp-keystore", @@ -14837,7 +15279,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -15178,6 +15620,19 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasm-timer" version = "0.2.5" @@ -15193,6 +15648,17 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core 0.2.1", +] + [[package]] name = "wasmi" version = "0.31.0" @@ -15202,16 +15668,38 @@ dependencies = [ "smallvec", "spin 0.9.8", "wasmi_arena", - "wasmi_core", + "wasmi_core 0.13.0", "wasmparser-nostd", ] +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + [[package]] name = "wasmi_arena" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm", + "memory_units", + "num-rational", + "num-traits", +] + [[package]] name = "wasmi_core" version = "0.13.0" @@ -15224,6 +15712,16 @@ dependencies = [ "paste", ] +[[package]] +name = "wasmparser" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +dependencies = [ + "indexmap 1.9.3", + "url", +] + [[package]] name = "wasmparser" version = "0.102.0" @@ -15243,6 +15741,31 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "wasmtime" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser 0.100.0", + "wasmtime-environ 6.0.2", + "wasmtime-jit 6.0.2", + "wasmtime-runtime 6.0.2", + "windows-sys 0.42.0", +] + [[package]] name = "wasmtime" version = "8.0.1" @@ -15262,15 +15785,24 @@ dependencies = [ "rayon", "serde", "target-lexicon", - "wasmparser", + "wasmparser 0.102.0", "wasmtime-cache", "wasmtime-cranelift", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", + "wasmtime-environ 8.0.1", + "wasmtime-jit 8.0.1", + "wasmtime-runtime 8.0.1", "windows-sys 0.45.0", ] +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" +dependencies = [ + "cfg-if", +] + [[package]] name = "wasmtime-asm-macros" version = "8.0.1" @@ -15308,7 +15840,7 @@ checksum = "b1cefde0cce8cb700b1b21b6298a3837dba46521affd7b8c38a9ee2c869eee04" dependencies = [ "anyhow", "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.95.1", "cranelift-frontend", "cranelift-native", "cranelift-wasm", @@ -15317,9 +15849,9 @@ dependencies = [ "object 0.30.4", "target-lexicon", "thiserror", - "wasmparser", + "wasmparser 0.102.0", "wasmtime-cranelift-shared", - "wasmtime-environ", + "wasmtime-environ 8.0.1", ] [[package]] @@ -15334,7 +15866,26 @@ dependencies = [ "gimli 0.27.3", "object 0.30.4", "target-lexicon", - "wasmtime-environ", + "wasmtime-environ 8.0.1", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" +dependencies = [ + "anyhow", + "cranelift-entity 0.93.2", + "gimli 0.26.2", + "indexmap 1.9.3", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser 0.100.0", + "wasmtime-types 6.0.2", ] [[package]] @@ -15344,7 +15895,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" dependencies = [ "anyhow", - "cranelift-entity", + "cranelift-entity 0.95.1", "gimli 0.27.3", "indexmap 1.9.3", "log", @@ -15352,8 +15903,31 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasmparser", - "wasmtime-types", + "wasmparser 0.102.0", + "wasmtime-types 8.0.1", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ 6.0.2", + "wasmtime-jit-icache-coherence 6.0.2", + "wasmtime-runtime 6.0.2", + "windows-sys 0.42.0", ] [[package]] @@ -15373,13 +15947,22 @@ dependencies = [ "rustc-demangle", "serde", "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-debug", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", + "wasmtime-environ 8.0.1", + "wasmtime-jit-debug 8.0.1", + "wasmtime-jit-icache-coherence 8.0.1", + "wasmtime-runtime 8.0.1", "windows-sys 0.45.0", ] +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" +dependencies = [ + "once_cell", +] + [[package]] name = "wasmtime-jit-debug" version = "8.0.1" @@ -15391,6 +15974,17 @@ dependencies = [ "rustix 0.36.15", ] +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.42.0", +] + [[package]] name = "wasmtime-jit-icache-coherence" version = "8.0.1" @@ -15402,6 +15996,30 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "wasmtime-runtime" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix 0.36.15", + "wasmtime-asm-macros 6.0.2", + "wasmtime-environ 6.0.2", + "wasmtime-jit-debug 6.0.2", + "windows-sys 0.42.0", +] + [[package]] name = "wasmtime-runtime" version = "8.0.1" @@ -15420,22 +16038,34 @@ dependencies = [ "paste", "rand 0.8.5", "rustix 0.36.15", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", + "wasmtime-asm-macros 8.0.1", + "wasmtime-environ 8.0.1", + "wasmtime-jit-debug 8.0.1", "windows-sys 0.45.0", ] +[[package]] +name = "wasmtime-types" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" +dependencies = [ + "cranelift-entity 0.93.2", + "serde", + "thiserror", + "wasmparser 0.100.0", +] + [[package]] name = "wasmtime-types" version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.95.1", "serde", "thiserror", - "wasmparser", + "wasmparser 0.102.0", ] [[package]] @@ -15779,7 +16409,7 @@ dependencies = [ "sp-block-builder", "sp-consensus-babe", "sp-consensus-beefy", - "sp-core", + "sp-core 21.0.0", "sp-inherents", "sp-io", "sp-mmr-primitives", @@ -15788,8 +16418,8 @@ dependencies = [ "sp-runtime", "sp-session", "sp-staking", - "sp-std", - "sp-storage", + "sp-std 8.0.0", + "sp-storage 13.0.0", "sp-transaction-pool", "sp-version", "staging-xcm", @@ -15808,7 +16438,7 @@ dependencies = [ "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core", + "sp-core 21.0.0", "sp-runtime", "sp-weights", ] @@ -15894,6 +16524,21 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 3b76a4532..3f20c730e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,18 @@ members = [ # Parachain "parachain/runtime", "parachain/node", + + # ismp + "parachain/modules/ismp/core", + "parachain/modules/ismp/primitives", + "parachain/modules/ismp/rpc", + "parachain/modules/ismp/runtime-api", + "parachain/modules/ismp/demo", + "parachain/modules/ismp/sync-committee", + # modules - "parachain/modules/consensus/sync-committee", - "parachain/modules/consensus/parachain", - "parachain/modules/state-machine" + "parachain/modules/state-machine", + "parachain/modules/consensus/sync-committee/prover", + "parachain/modules/consensus/sync-committee/verifier", + "parachain/modules/consensus/sync-committee/primitives", ] diff --git a/parachain/modules/consensus/sync-committee/README.md b/parachain/modules/consensus/sync-committee/README.md index 4ac529c2f..91c984208 100644 --- a/parachain/modules/consensus/sync-committee/README.md +++ b/parachain/modules/consensus/sync-committee/README.md @@ -1,2 +1,52 @@ -# ismp-sync-committee -Sync-committee consensus client implementation for ismp +#

sync-committee-rs ⚙️

+ +

+ Ethereum Beacon Chain Light Client SDK in Rust +
+ ⚠️ Beta Software ⚠️ +

+ +
+ +The sync-committee-rs is the implementation of the Ethereum beacon light client prover & verifier in Rust. This is based on the research done here: https://research.polytope.technology/ethereum-light-client + + +This library consists of +- ✅ The primitives. +- ✅ The prover. +- ✅ The verifier + + +## primitives +Consists of the types and structs as defined and described in the spec mentioned earlier. It also consists of the utility functions +to be used in the verifier and prover. + +## prover +Consists of the various proof generations for the ethereum beacon chain structs/types such as: + +- Execution payload +- Finalized header +- Block roots +- Sync committee update + +## verifier +This exports a single function for verifying ethereum's sync committee attestation. + + +# Major Depedencies +The major dependencies for this SDK/Library are: + +- [ssz-rs](https://github.com/ralexstokes/ssz-rs) +- [ethereum-consensus](https://github.com/ralexstokes/ethereum-consensus) + + +# Running the prover tests +**NOTE** +1. To run these tests make sure the latest fork version on your devnet is the BELLATRIX_FORK_VERSION as defined in the mainnet config +2. Modify `sync_committee_primitives::types::GENESIS_ROOT_VALIDATORS` defined under the testing + feature flag to match the one that is present in the devnet you are running the tests with +3. Make sure the SLOTS_PER_EPOCH is set to 32 in your devnet. + + +## License +This library is licensed under the [Apache 2.0 License](./LICENSE), Copyright (c) 2023 Polytope Labs. diff --git a/parachain/modules/consensus/sync-committee/primitives/Cargo.toml b/parachain/modules/consensus/sync-committee/primitives/Cargo.toml new file mode 100644 index 000000000..17fa0fa16 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "sync-committee-primitives" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs"] + + +[dependencies] +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main", default-features = false } +hex-literal = { package = "hex-literal", version = "0.4.1", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ + "derive" +] } +primitive-types = { version = "0.12.1", default-features = false, features = ["serde_no_std", "impl-codec"] } +serde = { version = "1.0.185", optional = true, features = ["derive"] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +anyhow = {version = "1.0.75", default-features = false} +ark-ec = { version = "0.4.2", default-features = false } +ark-bls12-381 = { version = "0.4.0", default-features = false } +bls_on_arkworks = { version = "0.2.2", default-features = false } + +[features] +default = ["std"] +std = [ + "ssz-rs/default", + 'codec/std', + "primitive-types/std", + "anyhow/std", + "ark-ec/std", + "bls_on_arkworks/std", + "ark-bls12-381/std", + "primitive-types/std", + "serde" +] +mainnet = [] +goerli = [] diff --git a/parachain/modules/consensus/sync-committee/primitives/src/consensus_types.rs b/parachain/modules/consensus/sync-committee/primitives/src/consensus_types.rs new file mode 100644 index 000000000..07cf3f4c6 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/consensus_types.rs @@ -0,0 +1,406 @@ +use crate::{ + constants::{ + BlsPublicKey, BlsSignature, Bytes32, Epoch, ExecutionAddress, Gwei, Hash32, + ParticipationFlags, Root, Slot, ValidatorIndex, Version, WithdrawalIndex, + DEPOSIT_PROOF_LENGTH, JUSTIFICATION_BITS_LENGTH, + }, + ssz::{ByteList, ByteVector}, +}; +use alloc::{vec, vec::Vec}; +use ssz_rs::{prelude::*, Deserialize, List, Vector}; + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconBlockHeader { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub slot: u64, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub proposer_index: u64, + pub parent_root: Root, + pub state_root: Root, + pub body_root: Root, +} + +#[derive(Default, Clone, Debug, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Checkpoint { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub epoch: u64, + pub root: Root, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Eth1Data { + pub deposit_root: Root, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub deposit_count: u64, + pub block_hash: Hash32, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Validator { + #[cfg_attr(feature = "std", serde(rename = "pubkey"))] + pub public_key: BlsPublicKey, + pub withdrawal_credentials: Bytes32, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub effective_balance: Gwei, + pub slashed: bool, + // Status epochs + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub activation_eligibility_epoch: Epoch, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub activation_epoch: Epoch, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub exit_epoch: Epoch, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub withdrawable_epoch: Epoch, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ProposerSlashing { + pub signed_header_1: SignedBeaconBlockHeader, + pub signed_header_2: SignedBeaconBlockHeader, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedBeaconBlockHeader { + pub message: BeaconBlockHeader, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct IndexedAttestation { + #[cfg_attr(feature = "std", serde(with = "crate::serde::collection_over_string"))] + pub attesting_indices: List, + pub data: AttestationData, + pub signature: BlsSignature, +} + +#[derive(Default, Clone, Debug, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct AttestationData { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub slot: u64, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub index: u64, + pub beacon_block_root: Root, + pub source: Checkpoint, + pub target: Checkpoint, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct AttesterSlashing { + pub attestation_1: IndexedAttestation, + pub attestation_2: IndexedAttestation, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Attestation { + pub aggregation_bits: Bitlist, + pub data: AttestationData, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Deposit { + pub proof: Vector, + pub data: DepositData, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, codec::Encode, codec::Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct DepositData { + #[cfg_attr(feature = "std", serde(rename = "pubkey"))] + pub public_key: BlsPublicKey, + pub withdrawal_credentials: Hash32, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub amount: u64, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct VoluntaryExit { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub epoch: u64, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub validator_index: u64, +} + +#[derive(Default, Debug, SimpleSerialize, codec::Encode, codec::Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedVoluntaryExit { + pub message: VoluntaryExit, + pub signature: BlsSignature, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, codec::Encode, codec::Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SyncAggregate { + pub sync_committee_bits: Bitvector, + pub sync_committee_signature: BlsSignature, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SyncCommittee { + #[cfg_attr(feature = "std", serde(rename = "pubkeys"))] + pub public_keys: Vector, + #[cfg_attr(feature = "std", serde(rename = "aggregate_pubkey"))] + pub aggregate_public_key: BlsPublicKey, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Withdrawal { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub index: WithdrawalIndex, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub validator_index: ValidatorIndex, + pub address: ExecutionAddress, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub amount: Gwei, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BlsToExecutionChange { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub validator_index: ValidatorIndex, + #[cfg_attr(feature = "serde", serde(rename = "from_bls_pubkey"))] + pub from_bls_public_key: BlsPublicKey, + pub to_execution_address: ExecutionAddress, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedBlsToExecutionChange { + message: BlsToExecutionChange, + signature: BlsSignature, +} + +pub type Transaction = ByteList; + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ExecutionPayload< + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, + const MAX_WITHDRAWALS_PER_PAYLOAD: usize, +> { + pub parent_hash: Hash32, + pub fee_recipient: ExecutionAddress, + pub state_root: Bytes32, + pub receipts_root: Bytes32, + pub logs_bloom: ByteVector, + pub prev_randao: Bytes32, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub block_number: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_limit: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_used: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub timestamp: u64, + pub extra_data: ByteList, + pub base_fee_per_gas: U256, + pub block_hash: Hash32, + pub transactions: List, MAX_TRANSACTIONS_PER_PAYLOAD>, + pub withdrawals: List, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ExecutionPayloadHeader< + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, +> { + pub parent_hash: Hash32, + pub fee_recipient: ExecutionAddress, + pub state_root: Bytes32, + pub receipts_root: Bytes32, + pub logs_bloom: ByteVector, + pub prev_randao: Bytes32, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub block_number: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_limit: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub gas_used: u64, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub timestamp: u64, + pub extra_data: ByteList, + pub base_fee_per_gas: U256, + pub block_hash: Hash32, + pub transactions_root: Root, + pub withdrawals_root: Root, +} + +#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconBlockBody< + const MAX_PROPOSER_SLASHINGS: usize, + const MAX_VALIDATORS_PER_COMMITTEE: usize, + const MAX_ATTESTER_SLASHINGS: usize, + const MAX_ATTESTATIONS: usize, + const MAX_DEPOSITS: usize, + const MAX_VOLUNTARY_EXITS: usize, + const SYNC_COMMITTEE_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, + const MAX_WITHDRAWALS_PER_PAYLOAD: usize, + const MAX_BLS_TO_EXECUTION_CHANGES: usize, +> { + pub randao_reveal: BlsSignature, + pub eth1_data: Eth1Data, + pub graffiti: Bytes32, + pub proposer_slashings: List, + pub attester_slashings: + List, MAX_ATTESTER_SLASHINGS>, + pub attestations: List, MAX_ATTESTATIONS>, + pub deposits: List, + pub voluntary_exits: List, + pub sync_aggregate: SyncAggregate, + pub execution_payload: ExecutionPayload< + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + >, + pub bls_to_execution_changes: List, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconBlock< + const MAX_PROPOSER_SLASHINGS: usize, + const MAX_VALIDATORS_PER_COMMITTEE: usize, + const MAX_ATTESTER_SLASHINGS: usize, + const MAX_ATTESTATIONS: usize, + const MAX_DEPOSITS: usize, + const MAX_VOLUNTARY_EXITS: usize, + const SYNC_COMMITTEE_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, + const MAX_WITHDRAWALS_PER_PAYLOAD: usize, + const MAX_BLS_TO_EXECUTION_CHANGES: usize, +> { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub slot: Slot, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub proposer_index: ValidatorIndex, + pub parent_root: Root, + pub state_root: Root, + pub body: BeaconBlockBody< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, + >, +} +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct Fork { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_hex"))] + pub previous_version: Version, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_hex"))] + pub current_version: Version, + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_string"))] + pub epoch: Epoch, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ForkData { + #[cfg_attr(feature = "std", serde(with = "crate::serde::as_hex"))] + pub current_version: Version, + pub genesis_validators_root: Root, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct HistoricalSummary { + pub block_summary_root: Root, + pub state_summary_root: Root, +} + +#[derive(Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct BeaconState< + const SLOTS_PER_HISTORICAL_ROOT: usize, + const HISTORICAL_ROOTS_LIMIT: usize, + const ETH1_DATA_VOTES_BOUND: usize, + const VALIDATOR_REGISTRY_LIMIT: usize, + const EPOCHS_PER_HISTORICAL_VECTOR: usize, + const EPOCHS_PER_SLASHINGS_VECTOR: usize, + const MAX_VALIDATORS_PER_COMMITTEE: usize, + const SYNC_COMMITTEE_SIZE: usize, + const BYTES_PER_LOGS_BLOOM: usize, + const MAX_EXTRA_DATA_BYTES: usize, + const MAX_BYTES_PER_TRANSACTION: usize, + const MAX_TRANSACTIONS_PER_PAYLOAD: usize, +> { + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub genesis_time: u64, + pub genesis_validators_root: Root, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub slot: Slot, + pub fork: Fork, + pub latest_block_header: BeaconBlockHeader, + pub block_roots: Vector, + pub state_roots: Vector, + pub historical_roots: List, + pub eth1_data: Eth1Data, + pub eth1_data_votes: List, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub eth1_deposit_index: u64, + pub validators: List, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub balances: List, + pub randao_mixes: Vector, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub slashings: Vector, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub previous_epoch_participation: List, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub current_epoch_participation: List, + pub justification_bits: Bitvector, + pub previous_justified_checkpoint: Checkpoint, + pub current_justified_checkpoint: Checkpoint, + pub finalized_checkpoint: Checkpoint, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::collection_over_string"))] + pub inactivity_scores: List, + pub current_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, + pub latest_execution_payload_header: + ExecutionPayloadHeader, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub next_withdrawal_index: WithdrawalIndex, + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_string"))] + pub next_withdrawal_validator_index: ValidatorIndex, + pub historical_summaries: List, +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/constants.rs b/parachain/modules/consensus/sync-committee/primitives/src/constants.rs new file mode 100644 index 000000000..3f0d33561 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/constants.rs @@ -0,0 +1,132 @@ +use crate::domains::DomainType; +use ssz_rs::Node; + +pub type BlsPublicKey = ByteVector; +pub type BlsSignature = ByteVector; + +pub type Epoch = u64; +pub type Slot = u64; +pub type Root = Node; +pub type ParticipationFlags = u8; + +pub type CommitteeIndex = u64; +pub type ValidatorIndex = u64; +pub type WithdrawalIndex = u64; +pub type Gwei = u64; +pub type Hash32 = Bytes32; + +pub type Version = [u8; 4]; +pub type ForkDigest = [u8; 4]; +pub type Domain = [u8; 32]; + +pub type ExecutionAddress = ByteVector<20>; + +pub type ChainId = usize; +pub type NetworkId = usize; + +pub type RandaoReveal = BlsSignature; +pub type Bytes32 = ByteVector<32>; + +pub const BLS_PUBLIC_KEY_BYTES_LEN: usize = 48; +pub const BLS_SECRET_KEY_BYTES_LEN: usize = 32; +pub const BLS_SIGNATURE_BYTES_LEN: usize = 96; + +pub const SYNC_COMMITTEE_SIZE: usize = 512; +pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: Epoch = 256; +pub const MAX_WITHDRAWALS_PER_PAYLOAD: usize = 16; +pub const MAX_BLS_TO_EXECUTION_CHANGES: usize = 16; +pub const MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: usize = 16384; + +pub const MAX_VALIDATORS_PER_COMMITTEE: usize = 2048; +pub const EPOCHS_PER_ETH1_VOTING_PERIOD: Epoch = 64; +pub const SLOTS_PER_HISTORICAL_ROOT: usize = 8192; +pub const EPOCHS_PER_HISTORICAL_VECTOR: usize = 65536; +pub const EPOCHS_PER_SLASHINGS_VECTOR: usize = 8192; +pub const HISTORICAL_ROOTS_LIMIT: usize = 16_777_216; +pub const VALIDATOR_REGISTRY_LIMIT: usize = 2usize.saturating_pow(40); +pub const MAX_PROPOSER_SLASHINGS: usize = 16; +pub const MAX_ATTESTER_SLASHINGS: usize = 2; +pub const MAX_ATTESTATIONS: usize = 128; +pub const MAX_DEPOSITS: usize = 16; +pub const MAX_VOLUNTARY_EXITS: usize = 16; +pub const JUSTIFICATION_BITS_LENGTH: usize = 4; + +pub const MAX_BYTES_PER_TRANSACTION: usize = 1_073_741_824; +pub const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 1_048_576; +pub const BYTES_PER_LOGS_BLOOM: usize = 256; +pub const MAX_EXTRA_DATA_BYTES: usize = 32; + +pub const DEPOSIT_PROOF_LENGTH: usize = 33; + +pub const ETH1_DATA_VOTES_BOUND: usize = (EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH) as usize; +pub const DOMAIN_SYNC_COMMITTEE: DomainType = DomainType::SyncCommittee; +pub const FINALIZED_ROOT_INDEX: u64 = 52; +pub const EXECUTION_PAYLOAD_STATE_ROOT_INDEX: u64 = 18; +pub const EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX: u64 = 22; +pub const EXECUTION_PAYLOAD_INDEX: u64 = 56; +pub const NEXT_SYNC_COMMITTEE_INDEX: u64 = 55; +pub const BLOCK_ROOTS_INDEX: u64 = 37; +pub const HISTORICAL_ROOTS_INDEX: u64 = 39; +pub const HISTORICAL_BATCH_BLOCK_ROOTS_INDEX: u64 = 2; +pub const EXECUTION_PAYLOAD_TIMESTAMP_INDEX: u64 = 25; +pub const FINALIZED_ROOT_INDEX_LOG2: u64 = 5; +pub const EXECUTION_PAYLOAD_INDEX_LOG2: u64 = 5; +pub const NEXT_SYNC_COMMITTEE_INDEX_LOG2: u64 = 5; +pub const BLOCK_ROOTS_INDEX_LOG2: u64 = 5; +pub const HISTORICAL_ROOTS_INDEX_LOG2: u64 = 5; + +#[cfg(feature = "goerli")] +pub use goerli::*; + +#[cfg(feature = "mainnet")] +pub use mainnet::*; + +use crate::ssz::ByteVector; +#[cfg(all(not(feature = "mainnet"), not(feature = "goerli")))] +pub use devnet::*; + +#[cfg(feature = "goerli")] +pub mod goerli { + use super::*; + pub const SLOTS_PER_EPOCH: Slot = 32; + pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb"); + pub const BELLATRIX_FORK_VERSION: Version = hex_literal::hex!("02001020"); + pub const ALTAIR_FORK_VERSION: Version = hex_literal::hex!("01001020"); + pub const GENESIS_FORK_VERSION: Version = hex_literal::hex!("00001020"); + pub const ALTAIR_FORK_EPOCH: Epoch = 36660; + pub const BELLATRIX_FORK_EPOCH: Epoch = 112260; + pub const CAPELLA_FORK_EPOCH: Epoch = 162304; + pub const CAPELLA_FORK_VERSION: Version = hex_literal::hex!("03001020"); +} + +#[cfg(feature = "mainnet")] +pub mod mainnet { + use super::*; + pub const SLOTS_PER_EPOCH: Slot = 32; + pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"); + pub const BELLATRIX_FORK_VERSION: Version = hex_literal::hex!("02000000"); + pub const ALTAIR_FORK_VERSION: Version = hex_literal::hex!("01000000"); + pub const GENESIS_FORK_VERSION: Version = hex_literal::hex!("00000000"); + pub const ALTAIR_FORK_EPOCH: Epoch = 74240; + pub const BELLATRIX_FORK_EPOCH: Epoch = 144896; + pub const CAPELLA_FORK_EPOCH: Epoch = 194048; + pub const CAPELLA_FORK_VERSION: Version = hex_literal::hex!("03000000"); +} + +#[cfg(all(not(feature = "mainnet"), not(feature = "goerli")))] +pub mod devnet { + use super::*; + use hex_literal::hex; + pub const SLOTS_PER_EPOCH: Slot = 6; + pub const GENESIS_VALIDATORS_ROOT: [u8; 32] = + hex_literal::hex!("83431ec7fcf92cfc44947fc0418e831c25e1d0806590231c439830db7ad54fda"); + pub const BELLATRIX_FORK_VERSION: Version = hex!("52525502"); + pub const ALTAIR_FORK_VERSION: Version = hex!("52525501"); + pub const GENESIS_FORK_VERSION: Version = hex!("52525500"); + pub const ALTAIR_FORK_EPOCH: Epoch = 0; + pub const BELLATRIX_FORK_EPOCH: Epoch = 0; + pub const CAPELLA_FORK_EPOCH: Epoch = 2; + pub const CAPELLA_FORK_VERSION: Version = hex!("52525503"); +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/domains.rs b/parachain/modules/consensus/sync-committee/primitives/src/domains.rs new file mode 100644 index 000000000..cdf88cfe2 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/domains.rs @@ -0,0 +1,29 @@ +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum DomainType { + BeaconProposer, + BeaconAttester, + Randao, + Deposit, + VoluntaryExit, + SelectionProof, + AggregateAndProof, + SyncCommittee, + SyncCommitteeSelectionProof, + ContributionAndProof, + BlsToExecutionChange, + ApplicationMask, + ApplicationBuilder, +} + +impl DomainType { + pub fn as_bytes(&self) -> [u8; 4] { + match self { + Self::ApplicationMask => [0, 0, 0, 1], + Self::ApplicationBuilder => [0, 0, 0, 1], + _ => { + let data = *self as u32; + data.to_le_bytes() + }, + } + } +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/error.rs b/parachain/modules/consensus/sync-committee/primitives/src/error.rs new file mode 100644 index 000000000..07d0986a0 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/error.rs @@ -0,0 +1,24 @@ +use core::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum Error { + InvalidRoot, + InvalidPublicKey, + InvalidProof, + InvalidBitVec, + ErrorConvertingAncestorBlock, + InvalidNodeBytes, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Error::InvalidRoot => write!(f, "Invalid root",), + Error::InvalidPublicKey => write!(f, "Invalid public key",), + Error::InvalidProof => write!(f, "Invalid proof",), + Error::InvalidBitVec => write!(f, "Invalid bit vec",), + Error::InvalidNodeBytes => write!(f, "Invalid node bytes",), + Error::ErrorConvertingAncestorBlock => write!(f, "Error deriving ancestor block",), + } + } +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/lib.rs b/parachain/modules/consensus/sync-committee/primitives/src/lib.rs new file mode 100644 index 000000000..afce5f503 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/lib.rs @@ -0,0 +1,16 @@ +//! Primitive types for sync committee verifier +//! This crate contains code adapted from https://github.com/ralexstokes/ethereum-consensus +#![cfg_attr(not(feature = "std"), no_std)] +#[warn(unused_imports)] +#[warn(unused_variables)] +extern crate alloc; + +pub mod consensus_types; +pub mod constants; +pub mod domains; +pub mod error; +#[cfg(feature = "std")] +pub mod serde; +mod ssz; +pub mod types; +pub mod util; diff --git a/parachain/modules/consensus/sync-committee/primitives/src/serde.rs b/parachain/modules/consensus/sync-committee/primitives/src/serde.rs new file mode 100644 index 000000000..f27592e11 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/serde.rs @@ -0,0 +1,142 @@ +use core::fmt::{Display, Formatter}; +use hex::FromHexError; + +const HEX_ENCODING_PREFIX: &str = "0x"; + +#[cfg_attr(feature = "serde", derive(Debug))] +pub enum HexError { + Hex, + MissingPrefix, +} + +impl From for HexError { + fn from(_: FromHexError) -> Self { + HexError::Hex + } +} + +impl Display for HexError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + HexError::Hex => write!(f, ""), + HexError::MissingPrefix => write!(f, "missing prefix when deserializing hex data"), + } + } +} + +pub fn try_bytes_from_hex_str(s: &str) -> Result, HexError> { + let target = s.strip_prefix(HEX_ENCODING_PREFIX).ok_or(HexError::MissingPrefix)?; + let data = hex::decode(target)?; + Ok(data) +} + +pub mod as_hex { + use super::*; + use alloc::format; + use serde::de::Deserialize; + + pub fn serialize>(data: T, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoding = hex::encode(data.as_ref()); + let output = format!("{HEX_ENCODING_PREFIX}{encoding}"); + serializer.collect_str(&output) + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + T: TryFrom>, + { + let s = ::deserialize(deserializer)?; + + let data = try_bytes_from_hex_str(&s).map_err(serde::de::Error::custom)?; + + let inner = T::try_from(data) + .map_err(|_| serde::de::Error::custom("type failed to parse bytes from hex data"))?; + Ok(inner) + } +} + +pub mod as_string { + use alloc::format; + use core::{fmt, str::FromStr}; + use serde::de::Deserialize; + + pub fn serialize(data: T, serializer: S) -> Result + where + S: serde::Serializer, + { + let output = format!("{data}"); + serializer.collect_str(&output) + } + + pub fn deserialize<'de, D, T: FromStr>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s: String = ::deserialize(deserializer)?; + let inner: T = s + .parse() + .map_err(|_| serde::de::Error::custom("failure to parse string data"))?; + Ok(inner) + } +} + +pub mod collection_over_string { + use core::{fmt, marker::PhantomData, str::FromStr}; + use serde::{ + de::{Deserializer, Error}, + ser::SerializeSeq, + }; + + pub fn serialize(data: T, serializer: S) -> Result + where + S: serde::Serializer, + T: AsRef<[U]>, + U: fmt::Display, + { + let mut seq = serializer.serialize_seq(None)?; + for elem in data.as_ref().iter() { + let rendered_elem = format!("{elem}"); + seq.serialize_element(&rendered_elem)?; + } + seq.end() + } + + struct Visitor(PhantomData>); + + impl<'de, T: FromStr> serde::de::Visitor<'de> for Visitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("array of string") + } + + fn visit_seq(self, mut access: S) -> Result + where + S: serde::de::SeqAccess<'de>, + { + let mut coll = Vec::with_capacity(access.size_hint().unwrap_or(0)); + + while let Some(elem) = access.next_element()? { + let recovered_elem = T::from_str(elem).map_err(|_| { + Error::custom("failure to parse element of sequence from string") + })?; + coll.push(recovered_elem); + } + Ok(coll) + } + } + + pub fn deserialize<'de, D, T, U>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: TryFrom>, + U: FromStr, + { + let data = deserializer.deserialize_seq(Visitor(PhantomData))?; + T::try_from(data).map_err(|_| serde::de::Error::custom("failure to parse collection")) + } +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_list.rs b/parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_list.rs new file mode 100644 index 000000000..46cd03b94 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_list.rs @@ -0,0 +1,88 @@ +use super::write_bytes_to_lower_hex; +use alloc::{vec, vec::Vec}; +use core::{ + fmt, + hash::{Hash, Hasher}, + ops::{Deref, DerefMut}, +}; +use ssz_rs::prelude::*; + +#[derive(Default, Clone, Eq, SimpleSerialize, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ByteList( + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_hex"))] List, +); + +impl TryFrom<&[u8]> for ByteList { + type Error = ssz_rs::DeserializeError; + + fn try_from(bytes: &[u8]) -> Result { + ByteList::::deserialize(bytes) + } +} + +// impl here to satisfy clippy +impl PartialEq for ByteList { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for ByteList { + fn hash(&self, state: &mut H) { + self.as_ref().hash(state); + } +} + +impl fmt::LowerHex for ByteList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write_bytes_to_lower_hex(f, self) + } +} + +impl fmt::Debug for ByteList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ByteList<{N}>(len={})({:#x})", self.len(), self) + } +} + +impl fmt::Display for ByteList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:#x}") + } +} + +impl AsRef<[u8]> for ByteList { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for ByteList { + type Target = List; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ByteList { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_byte_list_serde() { + let list = ByteList::<32>::try_from([255u8, 255u8].as_ref()).unwrap(); + let encoding = ssz_rs::serialize(&list).unwrap(); + assert_eq!(encoding, [255, 255]); + + let recovered_list = ByteList::<32>::deserialize(&encoding).unwrap(); + assert_eq!(list, recovered_list); + } +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_vector.rs b/parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_vector.rs new file mode 100644 index 000000000..38962758d --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/ssz/byte_vector.rs @@ -0,0 +1,81 @@ +use super::write_bytes_to_lower_hex; +use alloc::{vec, vec::Vec}; +use core::{ + fmt, + hash::{Hash, Hasher}, + ops::{Deref, DerefMut}, +}; +use ssz_rs::prelude::*; + +#[derive(Default, Clone, Eq, SimpleSerialize, codec::Encode, codec::Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct ByteVector( + #[cfg_attr(feature = "serde", serde(with = "crate::serde::as_hex"))] Vector, +); + +impl TryFrom<&[u8]> for ByteVector { + type Error = ssz_rs::DeserializeError; + + fn try_from(bytes: &[u8]) -> Result { + ByteVector::::deserialize(bytes) + } +} + +impl TryFrom> for ByteVector { + type Error = ssz_rs::DeserializeError; + + fn try_from(bytes: Vec) -> Result { + ByteVector::::deserialize(&bytes) + } +} + +// impl here to satisfy clippy +impl PartialEq for ByteVector { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Hash for ByteVector { + fn hash(&self, state: &mut H) { + self.as_ref().hash(state); + } +} + +impl fmt::LowerHex for ByteVector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write_bytes_to_lower_hex(f, self) + } +} + +impl fmt::Debug for ByteVector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ByteVector<{N}>({self:#x})") + } +} + +impl fmt::Display for ByteVector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:#x}") + } +} + +impl AsRef<[u8]> for ByteVector { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for ByteVector { + type Target = Vector; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ByteVector { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/ssz/mod.rs b/parachain/modules/consensus/sync-committee/primitives/src/ssz/mod.rs new file mode 100644 index 000000000..9c4ae06e6 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/ssz/mod.rs @@ -0,0 +1,16 @@ +mod byte_list; +mod byte_vector; +use core::fmt; + +fn write_bytes_to_lower_hex>(f: &mut fmt::Formatter<'_>, data: T) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + for i in data.as_ref() { + write!(f, "{i:02x}")?; + } + Ok(()) +} + +pub use byte_list::ByteList; +pub use byte_vector::ByteVector; diff --git a/parachain/modules/consensus/sync-committee/primitives/src/types.rs b/parachain/modules/consensus/sync-committee/primitives/src/types.rs new file mode 100644 index 000000000..29e91c2fd --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/types.rs @@ -0,0 +1,126 @@ +use crate::{ + consensus_types::{BeaconBlockHeader, SyncAggregate, SyncCommittee}, + constants::{Slot, SYNC_COMMITTEE_SIZE}, +}; +use alloc::vec::Vec; +use primitive_types::H256; +use ssz_rs::Node; + +/// This holds the relevant data required to prove the state root in the execution payload. +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct ExecutionPayloadProof { + /// The state root in the `ExecutionPayload` which represents the commitment to + /// the ethereum world state in the yellow paper. + pub state_root: H256, + /// the block number of the execution header. + pub block_number: u64, + /// merkle mutli proof for the state_root & block_number in the [`ExecutionPayload`]. + pub multi_proof: Vec, + /// merkle proof for the `ExecutionPayload` in the [`BeaconBlockBody`]. + pub execution_payload_branch: Vec, + /// timestamp + pub timestamp: u64, +} + +/// Holds the neccessary proofs required to verify a header in the `block_roots` field +/// either in [`BeaconState`] or [`HistoricalBatch`]. +#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +pub struct BlockRootsProof { + /// Generalized index of the header in the `block_roots` list. + pub block_header_index: u64, + /// The proof for the header, needed to reconstruct `hash_tree_root(state.block_roots)` + pub block_header_branch: Vec, +} + +/// The block header ancestry proof, this is an enum because the header may either exist in +/// `state.block_roots` or `state.historical_roots`. +#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +pub enum AncestryProof { + /// This variant defines the proof data for a beacon chain header in the `state.block_roots` + BlockRoots { + /// Proof for the header in `state.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the reconstructed `hash_tree_root(state.block_roots)` in [`BeaconState`] + block_roots_branch: Vec, + }, + /// This variant defines the neccessary proofs for a beacon chain header in the + /// `state.historical_roots`. + HistoricalRoots { + /// Proof for the header in `historical_batch.block_roots` + block_roots_proof: BlockRootsProof, + /// The proof for the `historical_batch.block_roots`, needed to reconstruct + /// `hash_tree_root(historical_batch)` + historical_batch_proof: Vec, + /// The proof for the `hash_tree_root(historical_batch)` in `state.historical_roots` + historical_roots_proof: Vec, + /// The generalized index for the historical_batch in `state.historical_roots`. + historical_roots_index: u64, + /// The proof for the reconstructed `hash_tree_root(state.historical_roots)` in + /// [`BeaconState`] + historical_roots_branch: Vec, + }, +} + +/// This defines the neccesary data needed to prove ancestor blocks, relative to the finalized +/// header. +#[derive(Debug, Clone, PartialEq, Eq, codec::Encode, codec::Decode)] +pub struct AncestorBlock { + /// The actual beacon chain header + pub header: BeaconBlockHeader, + /// Associated execution header proofs + pub execution_payload: ExecutionPayloadProof, + /// Ancestry proofs of the beacon chain header. + pub ancestry_proof: AncestryProof, +} + +/// Holds the latest sync committee as well as an ssz proof for it's existence +/// in an attested header. +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct SyncCommitteeUpdate { + /// actual sync committee + pub next_sync_committee: SyncCommittee, + /// next sync committee, ssz merkle proof. + pub next_sync_committee_branch: Vec, +} + +/// Minimum state required by the light client to validate new sync committee attestations +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct VerifierState { + /// The latest recorded finalized header + pub finalized_header: BeaconBlockHeader, + /// Latest finalized epoch + pub latest_finalized_epoch: u64, + /// Sync committees corresponding to the finalized header + pub current_sync_committee: SyncCommittee, + pub next_sync_committee: SyncCommittee, +} + +/// Finalized header proof +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct FinalityProof { + /// The latest finalized epoch + pub epoch: u64, + /// Finalized header proof + pub finality_branch: Vec, +} + +/// Data required to advance the state of the light client. +#[derive(Debug, Clone, PartialEq, Eq, Default, codec::Encode, codec::Decode)] +pub struct VerifierStateUpdate { + /// the header that the sync committee signed + pub attested_header: BeaconBlockHeader, + /// the sync committee has potentially changed, here's an ssz proof for that. + pub sync_committee_update: Option, + /// the actual header which was finalized by the ethereum attestation protocol. + pub finalized_header: BeaconBlockHeader, + /// execution payload of the finalized header + pub execution_payload: ExecutionPayloadProof, + /// Finalized header proof + pub finality_proof: FinalityProof, + /// signature & participation bits + pub sync_aggregate: SyncAggregate, + /// slot at which signature was produced + pub signature_slot: Slot, + // ancestors of the finalized block to be verified, may be empty. + // pub ancestor_blocks: Vec, +} diff --git a/parachain/modules/consensus/sync-committee/primitives/src/util.rs b/parachain/modules/consensus/sync-committee/primitives/src/util.rs new file mode 100644 index 000000000..a9a3cb71e --- /dev/null +++ b/parachain/modules/consensus/sync-committee/primitives/src/util.rs @@ -0,0 +1,87 @@ +use crate::{ + consensus_types::ForkData, + constants::{ + Domain, Root, Slot, Version, ALTAIR_FORK_EPOCH, ALTAIR_FORK_VERSION, BELLATRIX_FORK_EPOCH, + BELLATRIX_FORK_VERSION, CAPELLA_FORK_EPOCH, CAPELLA_FORK_VERSION, + EPOCHS_PER_SYNC_COMMITTEE_PERIOD, GENESIS_FORK_VERSION, SLOTS_PER_EPOCH, + }, + domains::DomainType, +}; +use alloc::{vec, vec::Vec}; +use anyhow::anyhow; +use ssz_rs::prelude::*; + +/// Returns true if the next epoch is the start of a new sync committee period +pub fn should_get_sync_committee_update(slot: Slot) -> bool { + let next_epoch = compute_epoch_at_slot(slot) + 1; + next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0 +} + +/// Return the sync committee period at the given ``epoch`` +pub fn compute_sync_committee_period(epoch: u64) -> u64 { + epoch / EPOCHS_PER_SYNC_COMMITTEE_PERIOD +} + +/// Return the epoch number at ``slot``. +pub fn compute_epoch_at_slot(slot: u64) -> u64 { + slot / SLOTS_PER_EPOCH +} + +#[cfg(not(feature = "testing"))] +/// Return the fork version at the given ``epoch``. +pub fn compute_fork_version(epoch: u64) -> [u8; 4] { + if epoch >= CAPELLA_FORK_EPOCH { + CAPELLA_FORK_VERSION + } else if epoch >= BELLATRIX_FORK_EPOCH { + BELLATRIX_FORK_VERSION + } else if epoch >= ALTAIR_FORK_EPOCH { + ALTAIR_FORK_VERSION + } else { + GENESIS_FORK_VERSION + } +} + +pub fn compute_domain( + domain_type: DomainType, + fork_version: Option, + genesis_validators_root: Option, + genesis_fork_version: Version, +) -> Result { + let fork_version = fork_version.unwrap_or(genesis_fork_version); + let genesis_validators_root = genesis_validators_root.unwrap_or_default(); + let fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)?; + let mut domain = Domain::default(); + domain[..4].copy_from_slice(&domain_type.as_bytes()); + domain[4..].copy_from_slice(&fork_data_root.as_ref()[..28]); + Ok(domain) +} + +#[derive(Default, Debug, SimpleSerialize)] +pub struct SigningData { + pub object_root: Root, + pub domain: Domain, +} + +pub fn compute_signing_root( + ssz_object: &mut T, + domain: Domain, +) -> Result { + let object_root = ssz_object.hash_tree_root().map_err(|e| anyhow!("{:?}", e))?; + + let mut s = SigningData { object_root, domain }; + s.hash_tree_root().map_err(|e| anyhow!("{:?}", e)) +} + +pub fn compute_fork_data_root( + current_version: Version, + genesis_validators_root: Root, +) -> Result { + ForkData { current_version, genesis_validators_root } + .hash_tree_root() + .map_err(|e| anyhow!("{:?}", e)) +} + +/// Return the sync committee period at ``slot`` +pub fn compute_sync_committee_period_at_slot(slot: u64) -> u64 { + compute_sync_committee_period(compute_epoch_at_slot(slot)) +} diff --git a/parachain/modules/consensus/sync-committee/prover/Cargo.toml b/parachain/modules/consensus/sync-committee/prover/Cargo.toml new file mode 100644 index 000000000..5dca6e39b --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "sync-committee-prover" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sync-committee-primitives = { path= "../primitives" } +sync-committee-verifier = { path= "../verifier" } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" } +reqwest = {version="0.11.14", features=["json"]} +serde = { version = "1.0.185", features = ["derive"] } +serde_json = { version = "1.0.81"} +anyhow = "1.0.68" +tokio = { version = "1.32.0", features = ["sync"]} +tokio-stream = { version = "0.1.8" } +async-stream = { version = "0.3.3"} +base2 = {version= "0.3.1" } +env_logger = "0.10.0" +ark-ec = { version = "0.4.2" } +ark-bls12-381 = { version = "0.4.0" } +bls_on_arkworks = { version = "0.2.2" } +primitive-types = { version = "0.12.1", features = ["serde_no_std", "impl-codec"] } +log = "0.4.20" +hex = "0.4.3" + + +[dev-dependencies] +env_logger = "0.10.0" +sync-committee-primitives = { path= "../primitives" } +sync-committee-verifier = { path= "../verifier" } +ethers = { version = "2.0.8", features = ["ws"] } +tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"]} +parity-scale-codec = "3.2.2" +reqwest-eventsource = "0.4.0" +dotenv = "0.15.0" + +[features] +default = ["std"] +std = [ + "ssz-rs/default", + "sync-committee-primitives/std", + "anyhow/std", + "ark-ec/std", + "bls_on_arkworks/std", + "ark-bls12-381/std" +] +goerli = ["sync-committee-primitives/goerli", "sync-committee-verifier/goerli"] +mainnet = ["sync-committee-primitives/mainnet", "sync-committee-verifier/mainnet"] + diff --git a/parachain/modules/consensus/sync-committee/prover/src/lib.rs b/parachain/modules/consensus/sync-committee/prover/src/lib.rs new file mode 100644 index 000000000..38bec605c --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/lib.rs @@ -0,0 +1,402 @@ +#[warn(unused_imports)] +#[warn(unused_variables)] +mod responses; +mod routes; +#[cfg(test)] +mod test; + +use anyhow::anyhow; +use bls_on_arkworks::{point_to_pubkey, types::G1ProjectivePoint}; +use log::debug; +use reqwest::Client; +use sync_committee_primitives::{ + consensus_types::{ + BeaconBlock, BeaconBlockHeader, BeaconState, Checkpoint, SyncCommittee, Validator, + }, + types::VerifierState, +}; + +use crate::{ + responses::{ + finality_checkpoint_response::FinalityCheckpoint, + sync_committee_response::NodeSyncCommittee, + }, + routes::*, +}; +use primitive_types::H256; +use ssz_rs::{List, Merkleized, Node, Vector}; +use sync_committee_primitives::{ + constants::{ + BlsPublicKey, Root, ValidatorIndex, BLOCK_ROOTS_INDEX, BYTES_PER_LOGS_BLOOM, + EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, + EXECUTION_PAYLOAD_STATE_ROOT_INDEX, EXECUTION_PAYLOAD_TIMESTAMP_INDEX, + FINALIZED_ROOT_INDEX, HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, + MAX_BLS_TO_EXECUTION_CHANGES, MAX_BYTES_PER_TRANSACTION, MAX_DEPOSITS, + MAX_EXTRA_DATA_BYTES, MAX_PROPOSER_SLASHINGS, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, MAX_WITHDRAWALS_PER_PAYLOAD, + NEXT_SYNC_COMMITTEE_INDEX, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_SIZE, + VALIDATOR_REGISTRY_LIMIT, + }, + types::{ + AncestryProof, BlockRootsProof, ExecutionPayloadProof, FinalityProof, SyncCommitteeUpdate, + VerifierStateUpdate, + }, + util::{ + compute_epoch_at_slot, compute_sync_committee_period_at_slot, + should_get_sync_committee_update, + }, +}; + +use sync_committee_verifier::crypto::pubkey_to_projective; + +pub type BeaconStateType = BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, +>; + +#[derive(Clone)] +pub struct SyncCommitteeProver { + pub node_url: String, + pub client: Client, +} + +impl SyncCommitteeProver { + pub fn new(node_url: String) -> Self { + let client = Client::new(); + + SyncCommitteeProver { node_url, client } + } + + pub async fn fetch_finalized_checkpoint(&self) -> Result { + let full_url = self.generate_route(&finality_checkpoints("head")); + let response = self.client.get(full_url).send().await?; + + let response_data = + response.json::().await?; + Ok(response_data.data) + } + + pub async fn fetch_header(&self, block_id: &str) -> Result { + let path = header_route(block_id); + let full_url = self.generate_route(&path); + let response = self.client.get(full_url).send().await?; + + let response_data = + response.json::().await?; + + let beacon_block_header = response_data.data.header.message; + + Ok(beacon_block_header) + } + + pub async fn fetch_block( + &self, + block_id: &str, + ) -> Result< + BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, + >, + anyhow::Error, + > { + let path = block_route(block_id); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let beacon_block = response_data.data.message; + + Ok(beacon_block) + } + + pub async fn fetch_sync_committee( + &self, + state_id: &str, + ) -> Result { + let path = sync_committee_route(state_id); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let sync_committee = response_data.data; + + Ok(sync_committee) + } + + pub async fn fetch_validator( + &self, + state_id: &str, + validator_index: &str, + ) -> Result { + let path = validator_route(state_id, validator_index); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let validator = response_data.data.validator; + + Ok(validator) + } + + pub async fn fetch_beacon_state( + &self, + state_id: &str, + ) -> Result { + let path = beacon_state_route(state_id); + let full_url = self.generate_route(&path); + + let response = self.client.get(full_url).send().await?; + + let response_data = response.json::().await?; + + let beacon_state = response_data.data; + + Ok(beacon_state) + } + + pub async fn fetch_processed_sync_committee( + &self, + state_id: &str, + ) -> Result, anyhow::Error> { + // fetches sync committee from Node + let node_sync_committee = self.fetch_sync_committee(state_id).await?; + + let mut validators: List = Default::default(); + for validator_index in node_sync_committee.validators.iter() { + // fetches validator based on validator index + let validator = self.fetch_validator(state_id, validator_index).await?; + validators.push(validator); + } + + let public_keys_vector = node_sync_committee + .validators + .into_iter() + .map(|i| { + let validator_index: ValidatorIndex = i.parse()?; + Ok(validators[validator_index as usize].public_key.clone()) + }) + .collect::, anyhow::Error>>()?; + + let aggregate_public_key = eth_aggregate_public_keys(&public_keys_vector)?; + + let sync_committee = SyncCommittee:: { + public_keys: Vector::::try_from(public_keys_vector) + .map_err(|e| anyhow!("{:?}", e))?, + aggregate_public_key, + }; + + Ok(sync_committee) + } + + fn generate_route(&self, path: &str) -> String { + format!("{}{}", self.node_url.clone(), path) + } + + pub async fn fetch_light_client_update( + &self, + client_state: VerifierState, + finality_checkpoint: Checkpoint, + debug_target: &str, + ) -> Result, anyhow::Error> { + if finality_checkpoint.root == Node::default() || + client_state.latest_finalized_epoch >= finality_checkpoint.epoch + { + return Ok(None) + } + + debug!(target: debug_target, "A new epoch has been finalized {}", finality_checkpoint.epoch); + // Find the highest block with the a threshhold number of sync committee signatures + let latest_header = self.fetch_header("head").await?; + let latest_root = latest_header.clone().hash_tree_root()?; + let get_block_id = |root: Root| { + let mut block_id = hex::encode(root.0.to_vec()); + block_id.insert_str(0, "0x"); + block_id + }; + let mut block = self.fetch_block(&get_block_id(latest_root)).await?; + let min_signatures = (2 * SYNC_COMMITTEE_SIZE) / 3; + let state_period = + compute_sync_committee_period_at_slot(client_state.finalized_header.slot); + loop { + // If we get to an epoch that is less than the finalized epoch for the notification + let current_epoch = compute_epoch_at_slot(block.slot); + if current_epoch <= finality_checkpoint.epoch || + current_epoch == client_state.latest_finalized_epoch + { + debug!(target: "prover", "Signature block search has reached an invalid epoch {} finalized_block_epoch {}", compute_epoch_at_slot(block.slot), finality_checkpoint.epoch); + return Ok(None) + } + + let parent_root = block.parent_root; + let block_id = get_block_id(parent_root); + block = self.fetch_block(&block_id).await?; + + let num_signatures = block.body.sync_aggregate.sync_committee_bits.count_ones(); + + let signature_period = compute_sync_committee_period_at_slot(block.slot); + if num_signatures >= min_signatures && + (state_period..=state_period + 1).contains(&signature_period) + { + break + } + } + + let attested_block_id = get_block_id(block.parent_root); + let attested_header = self.fetch_header(&attested_block_id).await?; + let mut attested_state = + self.fetch_beacon_state(&get_block_id(attested_header.state_root)).await?; + if attested_state.finalized_checkpoint.root == Node::default() { + return Ok(None) + } + let finalized_block_id = get_block_id(attested_state.finalized_checkpoint.root); + let finalized_header = self.fetch_header(&finalized_block_id).await?; + let mut finalized_state = + self.fetch_beacon_state(&get_block_id(finalized_header.state_root)).await?; + let finality_proof = FinalityProof { + epoch: attested_state.finalized_checkpoint.epoch, + finality_branch: prove_finalized_header(&mut attested_state)?, + }; + + let execution_payload_proof = prove_execution_payload(&mut finalized_state)?; + + let sync_committee_update = if should_get_sync_committee_update(attested_state.slot) { + let sync_committee_proof = prove_sync_committee_update(&mut attested_state)?; + Some(SyncCommitteeUpdate { + next_sync_committee: attested_state.next_sync_committee, + next_sync_committee_branch: sync_committee_proof, + }) + } else { + None + }; + + // construct light client + let light_client_update = VerifierStateUpdate { + attested_header, + sync_committee_update, + finalized_header, + execution_payload: execution_payload_proof, + finality_proof, + sync_aggregate: block.body.sync_aggregate, + signature_slot: block.slot, + }; + + Ok(Some(light_client_update)) + } +} + +pub fn prove_execution_payload( + beacon_state: &mut BeaconStateType, +) -> anyhow::Result { + let indices = [ + EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize, + EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize, + EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize, + ]; + // generate multi proofs + let multi_proof = ssz_rs::generate_proof( + &mut beacon_state.latest_execution_payload_header, + indices.as_slice(), + )?; + + Ok(ExecutionPayloadProof { + state_root: H256::from_slice( + beacon_state.latest_execution_payload_header.state_root.as_slice(), + ), + block_number: beacon_state.latest_execution_payload_header.block_number, + timestamp: beacon_state.latest_execution_payload_header.timestamp, + multi_proof, + execution_payload_branch: ssz_rs::generate_proof( + beacon_state, + &[EXECUTION_PAYLOAD_INDEX as usize], + )?, + }) +} + +pub fn prove_sync_committee_update(state: &mut BeaconStateType) -> anyhow::Result> { + let proof = ssz_rs::generate_proof(state, &[NEXT_SYNC_COMMITTEE_INDEX as usize])?; + Ok(proof) +} + +pub fn prove_finalized_header(state: &mut BeaconStateType) -> anyhow::Result> { + let indices = [FINALIZED_ROOT_INDEX as usize]; + let proof = ssz_rs::generate_proof(state, indices.as_slice())?; + + Ok(proof) +} + +pub fn prove_block_roots_proof( + state: &mut BeaconStateType, + mut header: BeaconBlockHeader, +) -> anyhow::Result { + // Check if block root should still be part of the block roots vector on the beacon state + let epoch_for_header = compute_epoch_at_slot(header.slot) as usize; + let epoch_for_state = compute_epoch_at_slot(state.slot) as usize; + + if epoch_for_state.saturating_sub(epoch_for_header) >= + SLOTS_PER_HISTORICAL_ROOT / SLOTS_PER_EPOCH as usize + { + // todo: Historical root proofs + unimplemented!() + } else { + // Get index of block root in the block roots + let block_root = header.hash_tree_root().expect("hash tree root should be valid"); + let block_index = state + .block_roots + .as_ref() + .into_iter() + .position(|root| root == &block_root) + .expect("Block root should exist in block_roots"); + + let proof = ssz_rs::generate_proof(&mut state.block_roots, &[block_index])?; + + let block_roots_proof = + BlockRootsProof { block_header_index: block_index as u64, block_header_branch: proof }; + + let block_roots_branch = ssz_rs::generate_proof(state, &[BLOCK_ROOTS_INDEX as usize])?; + Ok(AncestryProof::BlockRoots { block_roots_proof, block_roots_branch }) + } +} + +pub fn eth_aggregate_public_keys(points: &[BlsPublicKey]) -> anyhow::Result { + let points = points + .iter() + .map(|point| pubkey_to_projective(point)) + .collect::, _>>()?; + let aggregate = points + .into_iter() + .fold(G1ProjectivePoint::default(), |acc, g1_point| acc + g1_point); + let public_key = point_to_pubkey(aggregate.into()); + + let bls_public_key = + BlsPublicKey::try_from(public_key.as_slice()).map_err(|e| anyhow!("{:?}", e))?; + + Ok(bls_public_key) +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_header_response.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_header_response.rs new file mode 100644 index 000000000..f609d5bfc --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_header_response.rs @@ -0,0 +1,19 @@ +use sync_committee_primitives::consensus_types::BeaconBlockHeader; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: ResponseData, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseData { + root: String, + canonical: bool, + pub(crate) header: ResponseDataBeaconBlockHeaderMessage, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseDataBeaconBlockHeaderMessage { + pub message: BeaconBlockHeader, +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_response.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_response.rs new file mode 100644 index 000000000..dc6164011 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_block_response.rs @@ -0,0 +1,37 @@ +use sync_committee_primitives::{ + consensus_types::BeaconBlock, + constants::{ + BYTES_PER_LOGS_BLOOM, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, + MAX_BLS_TO_EXECUTION_CHANGES, MAX_BYTES_PER_TRANSACTION, MAX_DEPOSITS, + MAX_EXTRA_DATA_BYTES, MAX_PROPOSER_SLASHINGS, MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, MAX_WITHDRAWALS_PER_PAYLOAD, + SYNC_COMMITTEE_SIZE, + }, +}; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: ResponseData, + version: String, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ResponseData { + pub(crate) message: BeaconBlock< + MAX_PROPOSER_SLASHINGS, + MAX_VALIDATORS_PER_COMMITTEE, + MAX_ATTESTER_SLASHINGS, + MAX_ATTESTATIONS, + MAX_DEPOSITS, + MAX_VOLUNTARY_EXITS, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + MAX_WITHDRAWALS_PER_PAYLOAD, + MAX_BLS_TO_EXECUTION_CHANGES, + >, + pub signature: String, +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_state_response.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_state_response.rs new file mode 100644 index 000000000..97217b1ce --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/beacon_state_response.rs @@ -0,0 +1,28 @@ +use sync_committee_primitives::{ + consensus_types::BeaconState, + constants::{ + BYTES_PER_LOGS_BLOOM, EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, + ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, MAX_BYTES_PER_TRANSACTION, + MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, MAX_VALIDATORS_PER_COMMITTEE, + SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_SIZE, VALIDATOR_REGISTRY_LIMIT, + }, +}; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + version: String, + pub(crate) data: BeaconState< + SLOTS_PER_HISTORICAL_ROOT, + HISTORICAL_ROOTS_LIMIT, + ETH1_DATA_VOTES_BOUND, + VALIDATOR_REGISTRY_LIMIT, + EPOCHS_PER_HISTORICAL_VECTOR, + EPOCHS_PER_SLASHINGS_VECTOR, + MAX_VALIDATORS_PER_COMMITTEE, + SYNC_COMMITTEE_SIZE, + BYTES_PER_LOGS_BLOOM, + MAX_EXTRA_DATA_BYTES, + MAX_BYTES_PER_TRANSACTION, + MAX_TRANSACTIONS_PER_PAYLOAD, + >, +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/finality_checkpoint_response.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/finality_checkpoint_response.rs new file mode 100644 index 000000000..f925ba43c --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/finality_checkpoint_response.rs @@ -0,0 +1,14 @@ +use sync_committee_primitives::consensus_types::Checkpoint; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub(crate) struct Response { + execution_optimistic: bool, + pub data: FinalityCheckpoint, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct FinalityCheckpoint { + pub previous_justified: Checkpoint, + pub current_justified: Checkpoint, + pub finalized: Checkpoint, +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/mod.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/mod.rs new file mode 100644 index 000000000..bb7714c6f --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/mod.rs @@ -0,0 +1,8 @@ +pub mod beacon_block_header_response; +pub mod beacon_block_response; +pub mod beacon_state_response; +pub mod finality_checkpoint_response; +pub mod sync_committee_response; +pub mod validator_response; + +// todo: collapse into one file. diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/sync_committee_response.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/sync_committee_response.rs new file mode 100644 index 000000000..80bbcd7d8 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/sync_committee_response.rs @@ -0,0 +1,11 @@ +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: NodeSyncCommittee, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct NodeSyncCommittee { + pub validators: Vec, + pub validator_aggregates: Vec>, +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/responses/validator_response.rs b/parachain/modules/consensus/sync-committee/prover/src/responses/validator_response.rs new file mode 100644 index 000000000..2db92ecf1 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/responses/validator_response.rs @@ -0,0 +1,15 @@ +use sync_committee_primitives::consensus_types::Validator; + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct Response { + pub(crate) data: ValidatorData, + execution_optimistic: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct ValidatorData { + pub index: String, + pub balance: String, + pub status: String, + pub(crate) validator: Validator, +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/routes.rs b/parachain/modules/consensus/sync-committee/prover/src/routes.rs new file mode 100644 index 000000000..a7b398b28 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/routes.rs @@ -0,0 +1,21 @@ +pub fn header_route(block_id: &str) -> String { + format!("/eth/v1/beacon/headers/{block_id}") +} + +pub fn block_route(block_id: &str) -> String { + format!("/eth/v2/beacon/blocks/{block_id}") +} + +pub fn sync_committee_route(state_id: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/sync_committees") +} + +pub fn validator_route(state_id: &str, validator_index: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/validators/{validator_index}") +} +pub fn beacon_state_route(state_id: &str) -> String { + format!("/eth/v2/debug/beacon/states/{state_id}") +} +pub fn finality_checkpoints(state_id: &str) -> String { + format!("/eth/v1/beacon/states/{state_id}/finality_checkpoints") +} diff --git a/parachain/modules/consensus/sync-committee/prover/src/test.rs b/parachain/modules/consensus/sync-committee/prover/src/test.rs new file mode 100644 index 000000000..edd5c2b73 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/prover/src/test.rs @@ -0,0 +1,383 @@ +use super::*; +use base2::Base2; +use reqwest_eventsource::EventSource; +use ssz_rs::{ + calculate_multi_merkle_root, get_generalized_index, is_valid_merkle_branch, GeneralizedIndex, + Merkleized, SszVariableOrIndex, +}; +use std::time::Duration; +use sync_committee_primitives::{ + constants::{ + Root, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_INDEX_LOG2, GENESIS_FORK_VERSION, + GENESIS_VALIDATORS_ROOT, NEXT_SYNC_COMMITTEE_INDEX_LOG2, + }, + types::VerifierState, + util::{compute_domain, compute_fork_version, compute_signing_root}, +}; +use sync_committee_verifier::{ + crypto::verify_aggregate_signature, verify_sync_committee_attestation, +}; +use tokio_stream::StreamExt; + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn fetch_block_header_works() { + let sync_committee_prover = setup_prover(); + let block_header = sync_committee_prover.fetch_header("head").await; + assert!(block_header.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn fetch_block_works() { + let sync_committee_prover = setup_prover(); + let block = sync_committee_prover.fetch_block("head").await; + assert!(block.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn fetch_validator_works() { + let sync_committee_prover = setup_prover(); + let validator = sync_committee_prover.fetch_validator("head", "0").await; + assert!(validator.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +#[ignore] +async fn fetch_processed_sync_committee_works() { + let sync_committee_prover = setup_prover(); + let validator = sync_committee_prover.fetch_processed_sync_committee("head").await; + assert!(validator.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +#[ignore] +async fn generate_indexes() { + let sync_committee_prover = setup_prover(); + let beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let execution_payload_index = get_generalized_index( + &beacon_state, + &[SszVariableOrIndex::Name("latest_execution_payload_header")], + ); + let next_sync = + get_generalized_index(&beacon_state, &[SszVariableOrIndex::Name("next_sync_committee")]); + let finalized = + get_generalized_index(&beacon_state, &[SszVariableOrIndex::Name("finalized_checkpoint")]); + let execution_payload_root = get_generalized_index( + &beacon_state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("state_root")], + ); + let block_number = get_generalized_index( + &beacon_state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("block_number")], + ); + let timestamp = get_generalized_index( + &beacon_state.latest_execution_payload_header, + &[SszVariableOrIndex::Name("timestamp")], + ); + dbg!(execution_payload_index); + dbg!(next_sync); + dbg!(finalized); + dbg!(execution_payload_root); + dbg!(block_number); + dbg!(timestamp); + + dbg!(next_sync.floor_log2()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn fetch_beacon_state_works() { + let sync_committee_prover = setup_prover(); + let beacon_state = sync_committee_prover.fetch_beacon_state("head").await; + assert!(beacon_state.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn state_root_and_block_header_root_matches() { + let sync_committee_prover = setup_prover(); + let mut beacon_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + + let block_header = sync_committee_prover.fetch_header(&beacon_state.slot.to_string()).await; + assert!(block_header.is_ok()); + + let block_header = block_header.unwrap(); + let hash_tree_root = beacon_state.hash_tree_root(); + + assert_eq!(block_header.state_root, hash_tree_root.unwrap()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn fetch_finality_checkpoints_work() { + let sync_committee_prover = setup_prover(); + let finality_checkpoint = sync_committee_prover.fetch_finalized_checkpoint().await; + assert!(finality_checkpoint.is_ok()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn test_finalized_header() { + let sync_committee_prover = setup_prover(); + let mut state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + + let proof = ssz_rs::generate_proof(&mut state, &vec![FINALIZED_ROOT_INDEX as usize]).unwrap(); + + let leaves = vec![Node::from_bytes( + state + .finalized_checkpoint + .hash_tree_root() + .unwrap() + .as_ref() + .try_into() + .unwrap(), + )]; + let root = calculate_multi_merkle_root( + &leaves, + &proof, + &[GeneralizedIndex(FINALIZED_ROOT_INDEX as usize)], + ); + assert_eq!(root, state.hash_tree_root().unwrap()); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +#[ignore] +async fn test_execution_payload_proof() { + let sync_committee_prover = setup_prover(); + + let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let block_id = finalized_state.slot.to_string(); + let execution_payload_proof = prove_execution_payload(&mut finalized_state).unwrap(); + + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + // verify the associated execution header of the finalized beacon header. + let mut execution_payload = execution_payload_proof.clone(); + let multi_proof_vec = execution_payload.multi_proof; + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes(execution_payload.state_root.as_ref().try_into().unwrap()), + execution_payload.block_number.hash_tree_root().unwrap(), + execution_payload.timestamp.hash_tree_root().unwrap(), + ], + &multi_proof_vec, + &[ + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize), + ], + ); + + let execution_payload_hash_tree_root = finalized_state + .latest_execution_payload_header + .clone() + .hash_tree_root() + .unwrap(); + + assert_eq!(execution_payload_root, execution_payload_hash_tree_root); + + let execution_payload_branch = execution_payload.execution_payload_branch.iter(); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload_branch, + EXECUTION_PAYLOAD_INDEX_LOG2 as usize, + EXECUTION_PAYLOAD_INDEX as usize, + &finalized_header.state_root, + ); + + assert!(is_merkle_branch_valid); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn test_sync_committee_update_proof() { + let sync_committee_prover = setup_prover(); + + let mut finalized_state = sync_committee_prover.fetch_beacon_state("head").await.unwrap(); + let block_id = finalized_state.slot.to_string(); + let finalized_header = sync_committee_prover.fetch_header(&block_id).await.unwrap(); + + let sync_committee_proof = prove_sync_committee_update(&mut finalized_state).unwrap(); + + let mut sync_committee = finalized_state.next_sync_committee; + + let calculated_finalized_root = calculate_multi_merkle_root( + &[sync_committee.hash_tree_root().unwrap()], + &sync_committee_proof, + &[GeneralizedIndex(NEXT_SYNC_COMMITTEE_INDEX as usize)], + ); + + assert_eq!(calculated_finalized_root.as_bytes(), finalized_header.state_root.as_bytes()); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &sync_committee.hash_tree_root().unwrap(), + sync_committee_proof.iter(), + NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, + &finalized_header.state_root, + ); + + assert!(is_merkle_branch_valid); +} + +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn test_prover() { + use log::LevelFilter; + use parity_scale_codec::{Decode, Encode}; + env_logger::builder() + .filter_module("prover", LevelFilter::Debug) + .format_module_path(false) + .init(); + + let sync_committee_prover = setup_prover(); + let node_url = + format!("{}/eth/v1/events?topics=finalized_checkpoint", sync_committee_prover.node_url); + let block_header = sync_committee_prover.fetch_header("head").await.unwrap(); + + let state = sync_committee_prover + .fetch_beacon_state(&block_header.slot.to_string()) + .await + .unwrap(); + + let mut client_state = VerifierState { + finalized_header: block_header.clone(), + latest_finalized_epoch: 0, + current_sync_committee: state.current_sync_committee, + next_sync_committee: state.next_sync_committee, + }; + + let mut count = 0; + + let mut es = EventSource::get(node_url); + while let Some(event) = es.next().await { + match event { + Ok(reqwest_eventsource::Event::Message(msg)) => { + let message: EventResponse = serde_json::from_str(&msg.data).unwrap(); + let checkpoint = + Checkpoint { epoch: message.epoch.parse().unwrap(), root: message.block }; + let light_client_update = if let Some(update) = sync_committee_prover + .fetch_light_client_update(client_state.clone(), checkpoint, "prover") + .await + .unwrap() + { + update + } else { + continue + }; + + let encoded = light_client_update.encode(); + let decoded = VerifierStateUpdate::decode(&mut &*encoded).unwrap(); + assert_eq!(light_client_update, decoded); + + client_state = + verify_sync_committee_attestation(client_state.clone(), light_client_update) + .unwrap(); + debug!( + target: "prover", + "Sucessfully verified Ethereum block at slot {:?}", + client_state.finalized_header.slot + ); + + count += 1; + // For CI purposes we test finalization of 3 epochs + if count == 4 { + break + } + }, + Err(err) => { + panic!("Encountered Error and closed stream {err:?}"); + }, + _ => continue, + } + } +} + +#[ignore] +#[cfg(test)] +#[allow(non_snake_case)] +#[tokio::test] +async fn test_sync_committee_signature_verification() { + let sync_committee_prover = setup_prover(); + let block = loop { + let block = sync_committee_prover.fetch_block("head").await.unwrap(); + if block.slot < 16 { + std::thread::sleep(Duration::from_secs(48)); + continue + } + break block + }; + let sync_committee = sync_committee_prover + .fetch_processed_sync_committee(block.slot.to_string().as_str()) + .await + .unwrap(); + + let mut attested_header = sync_committee_prover + .fetch_header((block.slot - 1).to_string().as_str()) + .await + .unwrap(); + + let sync_committee_pubkeys = sync_committee.public_keys; + + let non_participant_pubkeys = block + .body + .sync_aggregate + .sync_committee_bits + .iter() + .zip(sync_committee_pubkeys.iter()) + .filter_map(|(bit, key)| if !(*bit) { Some(key.clone()) } else { None }) + .collect::>(); + + let fork_version = compute_fork_version(compute_epoch_at_slot(block.slot)); + + let domain = compute_domain( + DOMAIN_SYNC_COMMITTEE, + Some(fork_version), + Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().unwrap())), + GENESIS_FORK_VERSION, + ) + .unwrap(); + + let signing_root = compute_signing_root(&mut attested_header, domain).unwrap(); + + verify_aggregate_signature( + &sync_committee.aggregate_public_key, + &non_participant_pubkeys, + signing_root.as_bytes().to_vec(), + &block.body.sync_aggregate.sync_committee_signature, + ) + .unwrap(); +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct EventResponse { + pub block: Root, + pub state: Root, + pub epoch: String, + pub execution_optimistic: bool, +} + +fn setup_prover() -> SyncCommitteeProver { + dotenv::dotenv().ok(); + let consensus_url = + std::env::var("CONSENSUS_NODE_URL").unwrap_or("http://localhost:3500".to_string()); + SyncCommitteeProver::new(consensus_url) +} diff --git a/parachain/modules/consensus/sync-committee/verifier/Cargo.toml b/parachain/modules/consensus/sync-committee/verifier/Cargo.toml new file mode 100644 index 000000000..a4fa84e76 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/verifier/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "sync-committee-verifier" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs"] + +[dependencies] +sync-committee-primitives = { path= "../primitives", default-features = false } +ssz-rs = { git = "https://github.com/polytope-labs/ssz-rs", branch = "main" , default-features = false } +log = { version = "0.4.17", default-features = false } +anyhow = { version = "1.0.75", default-features = false } +ark-ec = { version = "0.4.2", default-features = false } +ark-bls12-381 = { version = "0.4.0", default-features = false, features = ["curve"] } +bls = { package = "bls_on_arkworks", version = "0.2.2", default-features = false } + +[features] +default = ["std"] +std = [ + "ssz-rs/std", + "log/std", + "sync-committee-primitives/std", + "log/std", + "anyhow/std", + "ark-ec/std", + "ark-bls12-381/std", + "bls/std" +] +goerli = ["sync-committee-primitives/goerli"] +mainnet = ["sync-committee-primitives/mainnet"] + +[dev-dependencies] +hex = "0.4.3" diff --git a/parachain/modules/consensus/sync-committee/verifier/src/crypto.rs b/parachain/modules/consensus/sync-committee/verifier/src/crypto.rs new file mode 100644 index 000000000..a7b6ced99 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/verifier/src/crypto.rs @@ -0,0 +1,108 @@ +use alloc::vec::Vec; +use anyhow::anyhow; +use ark_bls12_381::Bls12_381; +use ark_ec::{pairing::Pairing, AffineRepr}; +use bls::{ + types::{BLS12381Pairing, G1AffinePoint, G1ProjectivePoint, G2AffinePoint, Signature}, + DST_ETHEREUM, +}; +use sync_committee_primitives::constants::BlsPublicKey; + +pub fn pubkey_to_projective(compressed_key: &BlsPublicKey) -> anyhow::Result { + let affine_point = + bls::pubkey_to_point(&compressed_key.to_vec()).map_err(|e| anyhow!("{:?}", e))?; + Ok(affine_point.into()) +} + +fn subtract_points_from_aggregate( + aggregate: &BlsPublicKey, + points: &[BlsPublicKey], +) -> anyhow::Result { + let aggregate = pubkey_to_projective(aggregate)?; + let points = points + .iter() + .map(|point| pubkey_to_projective(point)) + .collect::, _>>()?; + let subset_aggregate = points.into_iter().fold(aggregate, |acc, point| acc - point); + Ok(subset_aggregate) +} + +fn pairing(u: G2AffinePoint, v: G1AffinePoint) -> BLS12381Pairing { + Bls12_381::pairing(v, u) +} + +/// Adapted from https://github.com/ArnaudBrousseau/bls_on_arkworks/blob/main/src/lib.rs#L335 +/// Verifies an aggregate bls12-381 signature from ethereum sync-committee +/// Expects signature subgroup to be valid +pub fn verify_aggregate_signature( + aggregate: &BlsPublicKey, + non_participants: &[BlsPublicKey], + msg: Vec, + signature: &Signature, +) -> anyhow::Result<()> { + let subset_aggregate = subtract_points_from_aggregate(aggregate, non_participants)?; + let aggregate_key_point: G1AffinePoint = subset_aggregate.into(); + let signature = bls::signature_to_point(signature).map_err(|e| anyhow!("{:?}", e))?; + let dst = DST_ETHEREUM.as_bytes().to_vec(); + + let q = bls::hash_to_point(&msg, &dst); + + let c1 = pairing(q, aggregate_key_point); + + // From the spec: + // > When the signature variant is minimal-pubkey-size, P is the distinguished point P1 that + // > generates the group G1. + // + let p = G1AffinePoint::generator(); + + let c2 = pairing(signature, p); + + if c1 == c2 { + Ok(()) + } else { + Err(anyhow!("Aggregate signature verification failed")) + } +} + +#[cfg(test)] +mod tests { + use crate::crypto::verify_aggregate_signature; + + #[test] + fn test_signature_verification() { + let pks = vec![ + hex::decode("882417eb57b98c7dd8e4adb5d4c7b59cb46ad093072f10db99e02597e3432fe094e2698df4c3bf65ff757ac602182f87").unwrap(), + hex::decode("8ef016d09c49af41d028fdf6ef04972d11f6931bf57f0922df4e77a52847227c880581eebb6b485af1d68bb4895cc35c").unwrap(), + hex::decode("88b92def24f441be1eba41ff76182e0eb224cf06e751df45635db1530bf37765861c82a8f381f81f6ac6a2b3d3d9875b").unwrap(), + hex::decode("afc92546e835a4dbe31e2b3a4e6f44a94466a6f9b5752113b9b828349254582eb7b5b596a32b79fc936a82db8802af0c").unwrap(), + hex::decode("8391e3a00add4bcbe4c339fa7c35238855861cbbc89ceefa6832de6b28bc378a0d038a329636d53404e0deaa444bdfd0").unwrap(), + hex::decode("9102e77817e572a16fab849f7681d130d10876880d7fe05d40091af93592150ad4829145a7327d125e71a8847a368121").unwrap(), + hex::decode("8d966a5cfd601661bfb6e15b8c849d3bd85006aec628b44e88022b01054be5159de73f16504a969d6009a59d9214b043").unwrap(), + hex::decode("b6778f88f9df6d5d09baf9bccd2ea1e4cb88469239a0a14ffcca37fc1c29bad69711dc64fc4e1bb1be0792b005a1729a").unwrap(), + hex::decode("afc664d1160d2a55fab55fe9d94551b18aa2543f218b9fbdd733509463416c96ee13da6cf75f97165922ca61372c6fb7").unwrap(), + hex::decode("ad413282bc501315d2cccf8e2a5dd54a5baca851515a04e5f252c98cfeeb670604fa48c707127017e0b8cda218d98207").unwrap() + ]; + + let message = + hex::decode("813a89a296973e35545cfa74fe3efd172a7d19443c97c625d699e9737229b0a2") + .unwrap(); + let aggregate_signature = hex::decode("a1abfcf9bd54b7a003e1f45f7543b194d8d25b816577b02ee4f1c99aa9821c620be6ecedbc8c5fab64d343a6cc832040029040e591fa24db54f5441f28d73918775e8feeac6177c9e016d2576b982d1cce453896a8aace2bda7374e5a76ce213").unwrap(); + let aggregate_pub_key = hex::decode("a3f2da752bd1dfc7288b46cc061668856e0cefa93ba6e8ff4699f355138f63a541fdb3444ddebcdce695d6313fa4b244").unwrap().try_into().unwrap(); + + let bit_vector = hex::decode("01000100010001000100").unwrap(); + + let non_participants = pks + .into_iter() + .zip(bit_vector) + .filter_map(|(pk, bit)| if bit == 0 { Some(pk.try_into().unwrap()) } else { None }) + .collect::>(); + + verify_aggregate_signature( + &aggregate_pub_key, + &non_participants, + message, + &aggregate_signature, + ) + .unwrap() + } +} diff --git a/parachain/modules/consensus/sync-committee/verifier/src/error.rs b/parachain/modules/consensus/sync-committee/verifier/src/error.rs new file mode 100644 index 000000000..240b0b71c --- /dev/null +++ b/parachain/modules/consensus/sync-committee/verifier/src/error.rs @@ -0,0 +1,29 @@ +use alloc::string::String; +use core::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum Error { + SyncCommitteeParticipantsTooLow, + InvalidUpdate(String), + DomainError, + InvalidMerkleBranch(String), + InvalidRoot(String), + MerkleizationError(String), + SignatureVerification, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Error::SyncCommitteeParticipantsTooLow => { + write!(f, "Sync committee participants are too low") + }, + Error::InvalidUpdate(err) => write!(f, "Invalid update {err:?}"), + Error::DomainError => write!(f, "Couldn't get domain"), + Error::InvalidMerkleBranch(err) => write!(f, "Invalid merkle branch {err:?}"), + Error::InvalidRoot(err) => write!(f, "Invalid root {err:?}"), + Error::MerkleizationError(err) => write!(f, "Merkleization error {err:?}"), + Error::SignatureVerification => write!(f, "Signature verification failed"), + } + } +} diff --git a/parachain/modules/consensus/sync-committee/verifier/src/lib.rs b/parachain/modules/consensus/sync-committee/verifier/src/lib.rs new file mode 100644 index 000000000..3f5328ad0 --- /dev/null +++ b/parachain/modules/consensus/sync-committee/verifier/src/lib.rs @@ -0,0 +1,205 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#[warn(unused_imports)] +#[warn(unused_variables)] +extern crate alloc; + +pub mod crypto; +pub mod error; + +use crate::{crypto::verify_aggregate_signature, error::Error}; +use alloc::vec::Vec; +use ssz_rs::{ + calculate_multi_merkle_root, prelude::is_valid_merkle_branch, GeneralizedIndex, Merkleized, + Node, +}; +use sync_committee_primitives::{ + consensus_types::Checkpoint, + constants::{ + Root, DOMAIN_SYNC_COMMITTEE, EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX, EXECUTION_PAYLOAD_INDEX, + EXECUTION_PAYLOAD_INDEX_LOG2, EXECUTION_PAYLOAD_STATE_ROOT_INDEX, + EXECUTION_PAYLOAD_TIMESTAMP_INDEX, FINALIZED_ROOT_INDEX, FINALIZED_ROOT_INDEX_LOG2, + GENESIS_FORK_VERSION, GENESIS_VALIDATORS_ROOT, NEXT_SYNC_COMMITTEE_INDEX, + NEXT_SYNC_COMMITTEE_INDEX_LOG2, + }, + types::{VerifierState, VerifierStateUpdate}, + util::{ + compute_domain, compute_epoch_at_slot, compute_fork_version, compute_signing_root, + compute_sync_committee_period_at_slot, should_get_sync_committee_update, + }, +}; + +/// This function simply verifies a sync committee's attestation & it's finalized counterpart. +pub fn verify_sync_committee_attestation( + trusted_state: VerifierState, + mut update: VerifierStateUpdate, +) -> Result { + if update.finality_proof.finality_branch.len() != FINALIZED_ROOT_INDEX_LOG2 as usize && + update.sync_committee_update.is_some() && + update.sync_committee_update.as_ref().unwrap().next_sync_committee_branch.len() != + NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize + { + Err(Error::InvalidUpdate("Finality branch is incorrect".into()))? + } + + // Verify sync committee has super majority participants + let sync_committee_bits = update.sync_aggregate.sync_committee_bits; + let sync_aggregate_participants: u64 = + sync_committee_bits.iter().as_bitslice().count_ones() as u64; + + if sync_aggregate_participants < (2 * sync_committee_bits.len() as u64) / 3 { + Err(Error::SyncCommitteeParticipantsTooLow)? + } + + // Verify update is valid + let is_valid_update = update.signature_slot > update.attested_header.slot && + update.attested_header.slot > update.finalized_header.slot; + if !is_valid_update { + Err(Error::InvalidUpdate( + "relationship between slots does not meet the requirements".into(), + ))? + } + + let state_period = compute_sync_committee_period_at_slot(trusted_state.finalized_header.slot); + let update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot); + if !(state_period..=state_period + 1).contains(&update_signature_period) { + Err(Error::InvalidUpdate("State period does not contain signature period".into()))? + } + + if update.attested_header.slot <= trusted_state.finalized_header.slot { + Err(Error::InvalidUpdate("Update is expired".into()))? + } + + // Verify sync committee aggregate signature + let sync_committee = if update_signature_period == state_period { + trusted_state.current_sync_committee.clone() + } else { + trusted_state.next_sync_committee.clone() + }; + + let sync_committee_pubkeys = sync_committee.public_keys; + + let non_participant_pubkeys = sync_committee_bits + .iter() + .zip(sync_committee_pubkeys.iter()) + .filter_map(|(bit, key)| if !(*bit) { Some(key.clone()) } else { None }) + .collect::>(); + + let fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot)); + + let domain = compute_domain( + DOMAIN_SYNC_COMMITTEE, + Some(fork_version), + Some(Root::from_bytes(GENESIS_VALIDATORS_ROOT.try_into().expect("Infallible"))), + GENESIS_FORK_VERSION, + ) + .map_err(|_| Error::InvalidUpdate("Failed to compute domain".into()))?; + + let signing_root = compute_signing_root(&mut update.attested_header, domain) + .map_err(|_| Error::InvalidRoot("Failed to compute signing root".into()))?; + + verify_aggregate_signature( + &sync_committee.aggregate_public_key, + &non_participant_pubkeys, + signing_root.as_bytes().to_vec(), + &update.sync_aggregate.sync_committee_signature, + ) + .map_err(|_| Error::SignatureVerification)?; + + // Verify that the `finality_branch` confirms `finalized_header` + // to match the finalized checkpoint root saved in the state of `attested_header`. + // Note that the genesis finalized checkpoint root is represented as a zero hash. + let mut finalized_checkpoint = Checkpoint { + epoch: update.finality_proof.epoch, + root: update + .finalized_header + .hash_tree_root() + .map_err(|_| Error::MerkleizationError("Error hashing finalized header".into()))?, + }; + + let is_merkle_branch_valid = is_valid_merkle_branch( + &finalized_checkpoint + .hash_tree_root() + .map_err(|_| Error::MerkleizationError("Failed to hash finality checkpoint".into()))?, + update.finality_proof.finality_branch.iter(), + FINALIZED_ROOT_INDEX_LOG2 as usize, + FINALIZED_ROOT_INDEX as usize, + &update.attested_header.state_root, + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch("Finality branch".into()))?; + } + + // verify the associated execution header of the finalized beacon header. + let mut execution_payload = update.execution_payload; + let execution_payload_root = calculate_multi_merkle_root( + &[ + Node::from_bytes(execution_payload.state_root.as_ref().try_into().expect("Infallible")), + execution_payload.block_number.hash_tree_root().map_err(|_| { + Error::MerkleizationError("Failed to hash execution payload".into()) + })?, + execution_payload + .timestamp + .hash_tree_root() + .map_err(|_| Error::MerkleizationError("Failed to hash timestamp".into()))?, + ], + &execution_payload.multi_proof, + &[ + GeneralizedIndex(EXECUTION_PAYLOAD_STATE_ROOT_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX as usize), + GeneralizedIndex(EXECUTION_PAYLOAD_TIMESTAMP_INDEX as usize), + ], + ); + + let is_merkle_branch_valid = is_valid_merkle_branch( + &execution_payload_root, + execution_payload.execution_payload_branch.iter(), + EXECUTION_PAYLOAD_INDEX_LOG2 as usize, + EXECUTION_PAYLOAD_INDEX as usize, + &update.finalized_header.state_root, + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch("Execution payload branch".into()))?; + } + + if let Some(mut sync_committee_update) = update.sync_committee_update.clone() { + if !should_get_sync_committee_update(update.attested_header.slot) { + Err(Error::InvalidUpdate("Current sync committee period has not elapsed".into()))? + } + + let sync_root = sync_committee_update + .next_sync_committee + .hash_tree_root() + .map_err(|_| Error::MerkleizationError("Failed to hash next sync committee".into()))?; + + let is_merkle_branch_valid = is_valid_merkle_branch( + &sync_root, + sync_committee_update.next_sync_committee_branch.iter(), + NEXT_SYNC_COMMITTEE_INDEX_LOG2 as usize, + NEXT_SYNC_COMMITTEE_INDEX as usize, + &update.attested_header.state_root, + ); + + if !is_merkle_branch_valid { + Err(Error::InvalidMerkleBranch("Next sync committee branch".into()))?; + } + } + + let verifier_state = if let Some(sync_committee_update) = update.sync_committee_update { + VerifierState { + finalized_header: update.finalized_header, + latest_finalized_epoch: update.finality_proof.epoch, + current_sync_committee: trusted_state.next_sync_committee, + next_sync_committee: sync_committee_update.next_sync_committee, + } + } else { + VerifierState { + finalized_header: update.finalized_header, + latest_finalized_epoch: update.finality_proof.epoch, + ..trusted_state + } + }; + + Ok(verifier_state) +} diff --git a/parachain/modules/ismp/core/Cargo.toml b/parachain/modules/ismp/core/Cargo.toml new file mode 100644 index 000000000..85baf3b6c --- /dev/null +++ b/parachain/modules/ismp/core/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "pallet-ismp" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, optional = true } + + # polytope labs +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +# crates.io +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } +derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display"] } +enum-as-inner = "=0.5.1" + +# local +ismp-primitives = { path = "../primitives", default-features = false } + +[dev-dependencies] +env_logger = "0.10.0" +pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +ismp-testsuite = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-core/std", + "ismp-rs/std", + "mmr-lib/std", + "sp-api/std", + "serde", + "ismp-primitives/std" +] + +testing = ["pallet-timestamp/std"] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks" +] diff --git a/parachain/modules/ismp/core/src/benchmarking.rs b/parachain/modules/ismp/core/src/benchmarking.rs new file mode 100644 index 000000000..8469560b0 --- /dev/null +++ b/parachain/modules/ismp/core/src/benchmarking.rs @@ -0,0 +1,290 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarking +// Only enable this module for benchmarking. +#![cfg(feature = "runtime-benchmarks")] + +use crate::*; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +/// Running the benchmarks correctly. +/// Add the [`crate::ismp_mocks::MockConsensusClient`] as one of the consensus clients available to +/// pallet-ismp in the runtime configuration. +/// In your module router configuration add the [`crate::ismp_mocks::MockModule`] as one of the ismp +/// modules using the [`crate::ismp_mocks::ModuleId`] as it's module id +#[benchmarks( +where +T: pallet_timestamp::Config, +::Moment: From +)] +pub mod benchmarks { + use super::*; + use crate::{ + dispatcher::Dispatcher, + host::Host, + mocks::ismp::{setup_mock_client, MOCK_CONSENSUS_STATE_ID, MODULE_ID}, + Config, Event, Pallet, RequestCommitments, RequestReceipts, ResponseReceipts, + }; + use frame_support::traits::{Get, Hooks}; + use frame_system::EventRecord; + use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; + use ismp_rs::{ + consensus::{StateCommitment, StateMachineId}, + host::{Ethereum, StateMachine}, + messaging::{ + CreateConsensusState, Message, Proof, RequestMessage, ResponseMessage, + StateCommitmentHeight, TimeoutMessage, + }, + router::{ + DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher, Post, PostResponse, + Request, Response, + }, + util::hash_request, + }; + + /// Verify the the last event emitted + fn assert_last_event(generic_event: ::RuntimeEvent) { + let events = frame_system::Pallet::::events(); + let system_event: ::RuntimeEvent = generic_event.into(); + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); + } + + #[benchmark] + fn create_consensus_client() { + let message = CreateConsensusState { + consensus_state: Default::default(), + consensus_client_id: MOCK_CONSENSUS_STATE_ID, + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + unbonding_period: u64::MAX, + challenge_period: 0, + state_machine_commitments: vec![( + StateMachineId { + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + }, + StateCommitmentHeight { + commitment: StateCommitment { + timestamp: 1651280681, + overlay_root: None, + state_root: Default::default(), + }, + height: 1, + }, + )], + }; + + #[extrinsic_call] + _(RawOrigin::Root, message); + + assert_last_event::( + Event::ConsensusClientCreated { consensus_client_id: MOCK_CONSENSUS_STATE_ID }.into(), + ); + } + + // The Benchmark consensus client should be added to the runtime for these benchmarks to work + #[benchmark] + fn handle_request_message() { + let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); + let height = setup_mock_client::<_, T>(&host); + let post = Post { + source: StateMachine::Ethereum(Ethereum::ExecutionLayer), + dest: ::StateMachine::get(), + nonce: 0, + from: MODULE_ID.to_bytes(), + to: MODULE_ID.to_bytes(), + timeout_timestamp: 5000, + data: "handle_request_message".as_bytes().to_vec(), + gas_limit: 0, + }; + + let msg = + RequestMessage { requests: vec![post.clone()], proof: Proof { height, proof: vec![] } }; + let caller = whitelisted_caller(); + + #[extrinsic_call] + handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]); + + let commitment = hash_request::>(&Request::Post(post)); + assert!(RequestReceipts::::get(commitment).is_some()); + } + + #[benchmark] + fn handle_response_message() { + let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); + let height = setup_mock_client::<_, T>(&host); + let post = Post { + source: ::StateMachine::get(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + nonce: 0, + from: MODULE_ID.to_bytes(), + to: MODULE_ID.to_bytes(), + timeout_timestamp: 5000, + data: "handle_response_message".as_bytes().to_vec(), + gas_limit: 0, + }; + let request = Request::Post(post.clone()); + + let commitment = hash_request::>(&request); + RequestCommitments::::insert( + commitment, + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: post.nonce }, + ); + + let response = Response::Post(PostResponse { post, response: vec![] }); + let request_commitment = hash_request::>(&response.request()); + let msg = ResponseMessage::Post { + responses: vec![response], + proof: Proof { height, proof: vec![] }, + }; + + let caller = whitelisted_caller(); + + #[extrinsic_call] + handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]); + + assert!(ResponseReceipts::::get(request_commitment).is_some()); + } + + #[benchmark] + fn handle_timeout_message() { + let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); + let height = setup_mock_client::<_, T>(&host); + let post = Post { + source: ::StateMachine::get(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + nonce: 0, + from: MODULE_ID.to_bytes(), + to: MODULE_ID.to_bytes(), + timeout_timestamp: 500, + data: "handle_timeout_message".as_bytes().to_vec(), + gas_limit: 0, + }; + let request = Request::Post(post.clone()); + + let commitment = hash_request::>(&request); + RequestCommitments::::insert( + commitment, + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: post.nonce }, + ); + + let msg = TimeoutMessage::Post { + requests: vec![request], + timeout_proof: Proof { height, proof: vec![] }, + }; + let caller = whitelisted_caller(); + + #[extrinsic_call] + handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]); + + assert!(RequestCommitments::::get(commitment).is_none()); + } + + #[benchmark] + fn on_finalize(x: Linear<1, 100>) { + for nonce in 0..x { + let post = Post { + source: StateMachine::Kusama(2000), + dest: StateMachine::Kusama(2001), + nonce: nonce.into(), + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100, + data: vec![2u8; 64], + gas_limit: 0, + }; + + let request = Request::Post(post); + let leaf = Leaf::Request(request); + + Pallet::::mmr_push(leaf.clone()).unwrap(); + } + + #[block] + { + Pallet::::on_finalize(2u32.into()) + } + } + + #[benchmark] + fn dispatch_post_request() { + let post = DispatchPost { + dest: StateMachine::Kusama(2000), + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100, + data: vec![2u8; 64], + gas_limit: 0, + }; + + let dispatcher = Dispatcher::::default(); + #[block] + { + dispatcher.dispatch_request(DispatchRequest::Post(post)).unwrap() + } + } + + #[benchmark] + fn dispatch_get_request() { + let get = DispatchGet { + dest: StateMachine::Kusama(2000), + from: vec![0u8; 32], + keys: vec![vec![1u8; 32]; 32], + height: 20, + timeout_timestamp: 100, + gas_limit: 0, + }; + + let dispatcher = Dispatcher::::default(); + #[block] + { + dispatcher.dispatch_request(DispatchRequest::Get(get)).unwrap() + } + } + + #[benchmark] + fn dispatch_response() { + let post = Post { + source: StateMachine::Kusama(2000), + dest: StateMachine::Kusama(2001), + nonce: 0, + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100, + data: vec![2u8; 64], + gas_limit: 0, + }; + let request_commitment = hash_request::>(&Request::Post(post.clone())); + RequestCommitments::::insert( + request_commitment, + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, + ); + + let response = PostResponse { post, response: vec![1u8; 64] }; + + let dispatcher = Dispatcher::::default(); + #[block] + { + dispatcher.dispatch_response(response).unwrap() + } + } + + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mocks::Test); +} diff --git a/parachain/modules/ismp/core/src/dispatcher.rs b/parachain/modules/ismp/core/src/dispatcher.rs new file mode 100644 index 000000000..0a519e040 --- /dev/null +++ b/parachain/modules/ismp/core/src/dispatcher.rs @@ -0,0 +1,89 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation for the ISMP Router +use crate::{host::Host, Config, Pallet}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use ismp_rs::{ + error::Error as IsmpError, + host::IsmpHost, + router::{DispatchRequest, Get, IsmpDispatcher, Post, PostResponse, Request, Response}, +}; + +/// A receipt or an outgoing or incoming request or response +#[derive(Encode, Decode, scale_info::TypeInfo)] +pub enum Receipt { + /// Ok + Ok, +} + +/// The dispatcher commits outgoing requests and responses to the mmr +pub struct Dispatcher(PhantomData); + +impl Default for Dispatcher { + fn default() -> Self { + Self(PhantomData) + } +} + +impl IsmpDispatcher for Dispatcher +where + T: Config, +{ + fn dispatch_request(&self, request: DispatchRequest) -> Result<(), IsmpError> { + let host = Host::::default(); + let request = match request { + DispatchRequest::Get(dispatch_get) => { + let get = Get { + source: host.host_state_machine(), + dest: dispatch_get.dest, + nonce: host.next_nonce(), + from: dispatch_get.from, + keys: dispatch_get.keys, + height: dispatch_get.height, + timeout_timestamp: dispatch_get.timeout_timestamp, + gas_limit: dispatch_get.gas_limit, + }; + Request::Get(get) + }, + DispatchRequest::Post(dispatch_post) => { + let post = Post { + source: host.host_state_machine(), + dest: dispatch_post.dest, + nonce: host.next_nonce(), + from: dispatch_post.from, + to: dispatch_post.to, + timeout_timestamp: dispatch_post.timeout_timestamp, + data: dispatch_post.data, + gas_limit: dispatch_post.gas_limit, + }; + Request::Post(post) + }, + }; + + Pallet::::dispatch_request(request)?; + + Ok(()) + } + + fn dispatch_response(&self, response: PostResponse) -> Result<(), IsmpError> { + let response = Response::Post(response); + + Pallet::::dispatch_response(response)?; + + Ok(()) + } +} diff --git a/parachain/modules/ismp/core/src/errors.rs b/parachain/modules/ismp/core/src/errors.rs new file mode 100644 index 000000000..3bac7235a --- /dev/null +++ b/parachain/modules/ismp/core/src/errors.rs @@ -0,0 +1,175 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Ismp Errors conversions +use codec::{Decode, Encode}; +use ismp_rs::{ + consensus::{ConsensusClientId, StateMachineHeight}, + error::Error as IsmpError, + host::StateMachine, + module::DispatchResult, +}; +use sp_std::prelude::*; + +#[derive(Clone, Debug, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] +#[allow(missing_docs)] +pub enum HandlingError { + ChallengePeriodNotElapsed { + update_time: u64, + current_time: u64, + delay_period: Option, + consensus_client_id: Option, + }, + ConsensusStateNotFound { + id: ConsensusClientId, + }, + StateCommitmentNotFound { + height: StateMachineHeight, + }, + FrozenConsensusClient { + id: ConsensusClientId, + }, + FrozenStateMachine { + height: StateMachineHeight, + }, + RequestCommitmentNotFound { + nonce: u64, + source: StateMachine, + dest: StateMachine, + }, + RequestVerificationFailed { + nonce: u64, + source: StateMachine, + dest: StateMachine, + }, + ResponseVerificationFailed { + nonce: u64, + source: StateMachine, + dest: StateMachine, + }, + ConsensusProofVerificationFailed { + id: ConsensusClientId, + }, + ExpiredConsensusClient { + id: ConsensusClientId, + }, + CannotHandleMessage, + ImplementationSpecific { + msg: Vec, + }, + UnbondingPeriodElapsed { + id: ConsensusClientId, + }, + MembershipProofVerificationFailed { + msg: Vec, + }, + NonMembershipProofVerificationFailed { + msg: Vec, + }, + CannotCreateAlreadyExistingConsensusClient { + id: ConsensusClientId, + }, + RequestTimeoutNotElapsed { + nonce: u64, + source: StateMachine, + dest: StateMachine, + timeout_timestamp: u64, + state_machine_time: u64, + }, + RequestTimeoutVerificationFailed { + nonce: u64, + source: StateMachine, + dest: StateMachine, + }, + InsufficientProofHeight, + ModuleNotFound(Vec), +} + +#[derive(Debug)] +pub enum ModuleCallbackResult { + Response(Vec), + Request(Vec), + Timeout(Vec), +} + +impl From for HandlingError { + fn from(value: ismp_rs::error::Error) -> Self { + match value { + IsmpError::ChallengePeriodNotElapsed { + consensus_state_id, + current_time, + update_time, + } => HandlingError::ChallengePeriodNotElapsed { + update_time: update_time.as_secs(), + current_time: current_time.as_secs(), + delay_period: None, + consensus_client_id: Some(consensus_state_id), + }, + IsmpError::ConsensusStateNotFound { consensus_state_id } => + HandlingError::ConsensusStateNotFound { id: consensus_state_id }, + IsmpError::StateCommitmentNotFound { height } => + HandlingError::StateCommitmentNotFound { height }, + IsmpError::FrozenConsensusClient { consensus_state_id } => + HandlingError::FrozenConsensusClient { id: consensus_state_id }, + IsmpError::FrozenStateMachine { height } => + HandlingError::FrozenStateMachine { height }, + IsmpError::RequestCommitmentNotFound { nonce, source, dest } => + HandlingError::RequestCommitmentNotFound { nonce, source, dest }, + IsmpError::RequestVerificationFailed { nonce, source, dest } => + HandlingError::ResponseVerificationFailed { nonce, source, dest }, + IsmpError::ResponseVerificationFailed { nonce, source, dest } => + HandlingError::ResponseVerificationFailed { nonce, source, dest }, + IsmpError::ConsensusProofVerificationFailed { id } => + HandlingError::ConsensusProofVerificationFailed { id }, + IsmpError::ExpiredConsensusClient { id } => + HandlingError::ExpiredConsensusClient { id }, + IsmpError::CannotHandleMessage => HandlingError::CannotHandleMessage, + IsmpError::ImplementationSpecific(msg) => + HandlingError::ImplementationSpecific { msg: msg.as_bytes().to_vec() }, + IsmpError::UnbondingPeriodElapsed { consensus_state_id } => + HandlingError::UnbondingPeriodElapsed { id: consensus_state_id }, + IsmpError::MembershipProofVerificationFailed(msg) => + HandlingError::MembershipProofVerificationFailed { msg: msg.as_bytes().to_vec() }, + IsmpError::NonMembershipProofVerificationFailed(msg) => + HandlingError::NonMembershipProofVerificationFailed { msg: msg.as_bytes().to_vec() }, + IsmpError::CannotCreateAlreadyExistingConsensusClient { id } => + HandlingError::CannotCreateAlreadyExistingConsensusClient { id }, + IsmpError::RequestTimeoutNotElapsed { + nonce, + source, + dest, + timeout_timestamp, + state_machine_time, + } => HandlingError::RequestTimeoutNotElapsed { + nonce, + source, + dest, + timeout_timestamp: timeout_timestamp.as_secs(), + state_machine_time: state_machine_time.as_secs(), + }, + IsmpError::RequestTimeoutVerificationFailed { nonce, source, dest } => + HandlingError::RequestTimeoutVerificationFailed { nonce, source, dest }, + IsmpError::InsufficientProofHeight => HandlingError::InsufficientProofHeight, + IsmpError::ModuleNotFound(id) => HandlingError::ModuleNotFound(id), + IsmpError::ConsensusStateIdNotRecognized { .. } => + HandlingError::InsufficientProofHeight, + IsmpError::ChallengePeriodNotConfigured { .. } => + HandlingError::InsufficientProofHeight, + IsmpError::DuplicateConsensusStateId { .. } => HandlingError::InsufficientProofHeight, + IsmpError::UnnbondingPeriodNotConfigured { .. } => + HandlingError::InsufficientProofHeight, + } + } +} diff --git a/parachain/modules/ismp/core/src/events.rs b/parachain/modules/ismp/core/src/events.rs new file mode 100644 index 000000000..73a32028e --- /dev/null +++ b/parachain/modules/ismp/core/src/events.rs @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Core ISMP events + +use crate::{Config, Event as PalletEvent}; +use alloc::collections::BTreeSet; +use ismp_rs::{ + consensus::{ConsensusStateId, StateMachineHeight, StateMachineId}, + host::StateMachine, +}; + +/// Ismp Core Protocol Events +#[derive(Clone, codec::Encode, codec::Decode, Debug, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum Event { + /// Emitted when a state machine is successfully updated to a new height + StateMachineUpdated { + /// State machine id + state_machine_id: StateMachineId, + /// Latest height + latest_height: u64, + }, + /// Emitted when a challenge period has begun for a consensus client + ChallengePeriodStarted { + /// Consensus state id + consensus_state_id: ConsensusStateId, + /// Tuple of previous height and latest height + state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, + }, + /// Emitted for an outgoing response + Response { + /// Chain that this response will be routed to + dest_chain: StateMachine, + /// Source Chain for this response + source_chain: StateMachine, + /// Nonce for the request which this response is for + request_nonce: u64, + }, + /// Emitted for an outgoing request + Request { + /// Chain that this request will be routed to + dest_chain: StateMachine, + /// Source Chain for request + source_chain: StateMachine, + /// Request nonce + request_nonce: u64, + }, +} + +/// Convert from pallet event to Ismp event +pub fn to_core_protocol_event(event: PalletEvent) -> Option { + match event { + PalletEvent::StateMachineUpdated { state_machine_id, latest_height } => + Some(Event::StateMachineUpdated { state_machine_id, latest_height }), + PalletEvent::Response { dest_chain, source_chain, request_nonce } => + Some(Event::Response { dest_chain, source_chain, request_nonce }), + PalletEvent::Request { dest_chain, source_chain, request_nonce } => + Some(Event::Request { dest_chain, source_chain, request_nonce }), + PalletEvent::ChallengePeriodStarted { consensus_client_id, state_machines } => + Some(Event::ChallengePeriodStarted { + consensus_state_id: consensus_client_id, + state_machines, + }), + _ => None, + } +} diff --git a/parachain/modules/ismp/core/src/handlers.rs b/parachain/modules/ismp/core/src/handlers.rs new file mode 100644 index 000000000..93cf4ea12 --- /dev/null +++ b/parachain/modules/ismp/core/src/handlers.rs @@ -0,0 +1,71 @@ +//! Some extra utilities for pallet-ismp + +use crate::{ + dispatcher::Receipt, host::Host, Config, Event, Pallet, RequestCommitments, ResponseCommitments, +}; +use alloc::string::ToString; +use ismp_primitives::{mmr::Leaf, LeafIndexQuery}; +use ismp_rs::{ + error::Error as IsmpError, + router::{Request, Response}, + util::{hash_request, hash_response}, +}; + +impl Pallet { + /// Dispatch an outgoing request + pub fn dispatch_request(request: Request) -> Result<(), IsmpError> { + let commitment = hash_request::>(&request); + + if RequestCommitments::::contains_key(commitment) { + Err(IsmpError::ImplementationSpecific("Duplicate request".to_string()))? + } + + let (dest_chain, source_chain, nonce) = + (request.dest_chain(), request.source_chain(), request.nonce()); + Pallet::::mmr_push(Leaf::Request(request)).ok_or_else(|| { + IsmpError::ImplementationSpecific("Failed to push request into mmr".to_string()) + })?; + // Deposit Event + Pallet::::deposit_event(Event::Request { + request_nonce: nonce, + source_chain, + dest_chain, + }); + + RequestCommitments::::insert( + commitment, + LeafIndexQuery { source_chain, dest_chain, nonce }, + ); + Ok(()) + } + + /// Dispatch an outgoing response + pub fn dispatch_response(response: Response) -> Result<(), IsmpError> { + let commitment = hash_request::>(&response.request()); + + if !RequestCommitments::::contains_key(commitment) { + Err(IsmpError::ImplementationSpecific("Unknown request for response".to_string()))? + } + + let commitment = hash_response::>(&response); + + if ResponseCommitments::::contains_key(commitment) { + Err(IsmpError::ImplementationSpecific("Duplicate response".to_string()))? + } + + let (dest_chain, source_chain, nonce) = + (response.dest_chain(), response.source_chain(), response.nonce()); + + Pallet::::mmr_push(Leaf::Response(response)).ok_or_else(|| { + IsmpError::ImplementationSpecific("Failed to push response into mmr".to_string()) + })?; + + Pallet::::deposit_event(Event::Response { + request_nonce: nonce, + dest_chain, + source_chain, + }); + ResponseCommitments::::insert(commitment, Receipt::Ok); + Ok(()) + } +} diff --git a/parachain/modules/ismp/core/src/host.rs b/parachain/modules/ismp/core/src/host.rs new file mode 100644 index 000000000..1f836b228 --- /dev/null +++ b/parachain/modules/ismp/core/src/host.rs @@ -0,0 +1,286 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Host implementation for ISMP +use crate::{ + dispatcher::Receipt, primitives::ConsensusClientProvider, AllowedProxies, ChallengePeriod, + Config, ConsensusClientUpdateTime, ConsensusStateClient, ConsensusStates, + FrozenConsensusClients, FrozenHeights, LatestStateMachineHeight, Nonce, RequestCommitments, + RequestReceipts, ResponseReceipts, StateCommitments, StateMachineUpdateTime, UnbondingPeriod, +}; +use alloc::{format, string::ToString}; +use core::time::Duration; +use frame_support::traits::{Get, UnixTime}; +use ismp_rs::{ + consensus::{ + ConsensusClient, ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineHeight, + StateMachineId, + }, + error::Error, + host::{IsmpHost, StateMachine}, + router::{IsmpRouter, Request}, + util::hash_request, +}; +use sp_core::H256; +use sp_runtime::SaturatedConversion; +use sp_std::prelude::*; + +/// An implementation for the IsmpHost +#[derive(Clone)] +pub struct Host(core::marker::PhantomData); + +impl Default for Host { + fn default() -> Self { + Self(core::marker::PhantomData) + } +} + +impl IsmpHost for Host { + fn host_state_machine(&self) -> StateMachine { + T::StateMachine::get() + } + + fn latest_commitment_height(&self, id: StateMachineId) -> Result { + Ok(LatestStateMachineHeight::::get(id)) + } + + fn state_machine_commitment( + &self, + height: StateMachineHeight, + ) -> Result { + StateCommitments::::get(height).ok_or_else(|| Error::StateCommitmentNotFound { height }) + } + + fn consensus_update_time(&self, id: ConsensusClientId) -> Result { + ConsensusClientUpdateTime::::get(id) + .map(|timestamp| Duration::from_secs(timestamp)) + .ok_or_else(|| { + Error::ImplementationSpecific(format!("Update time not found for {:?}", id)) + }) + } + + fn state_machine_update_time( + &self, + state_machine_height: StateMachineHeight, + ) -> Result { + StateMachineUpdateTime::::get(state_machine_height) + .map(|timestamp| Duration::from_secs(timestamp)) + .ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Update time not found for {:?}", + state_machine_height + )) + }) + } + + fn consensus_state(&self, id: ConsensusClientId) -> Result, Error> { + ConsensusStates::::get(id) + .ok_or_else(|| Error::ConsensusStateNotFound { consensus_state_id: id }) + } + + fn timestamp(&self) -> Duration { + ::now() + } + + fn request_commitment(&self, commitment: H256) -> Result<(), Error> { + let _ = RequestCommitments::::get(commitment).ok_or_else(|| { + Error::ImplementationSpecific("Request commitment not found".to_string()) + })?; + + Ok(()) + } + + fn request_receipt(&self, req: &Request) -> Option<()> { + let commitment = hash_request::(req); + + let _ = RequestReceipts::::get(commitment) + .ok_or_else(|| Error::RequestCommitmentNotFound { + nonce: req.nonce(), + source: req.source_chain(), + dest: req.dest_chain(), + }) + .ok()?; + + Some(()) + } + + fn store_consensus_state(&self, id: ConsensusClientId, state: Vec) -> Result<(), Error> { + ConsensusStates::::insert(id, state); + Ok(()) + } + + fn store_consensus_update_time( + &self, + id: ConsensusClientId, + timestamp: Duration, + ) -> Result<(), Error> { + ConsensusClientUpdateTime::::insert(id, timestamp.as_secs().saturated_into::()); + Ok(()) + } + + fn store_state_machine_update_time( + &self, + state_machine_height: StateMachineHeight, + timestamp: Duration, + ) -> Result<(), Error> { + StateMachineUpdateTime::::insert( + state_machine_height, + timestamp.as_secs().saturated_into::(), + ); + Ok(()) + } + + fn store_state_machine_commitment( + &self, + height: StateMachineHeight, + state: StateCommitment, + ) -> Result<(), Error> { + StateCommitments::::insert(height, state); + Ok(()) + } + + fn freeze_state_machine(&self, height: StateMachineHeight) -> Result<(), Error> { + FrozenHeights::::insert(height.id, height.height); + Ok(()) + } + + fn store_latest_commitment_height(&self, height: StateMachineHeight) -> Result<(), Error> { + LatestStateMachineHeight::::insert(height.id, height.height); + Ok(()) + } + + fn delete_request_commitment(&self, req: &Request) -> Result<(), Error> { + let hash = hash_request::(req); + // We can't delete actual leaves in the mmr so this serves as a replacement for that + RequestCommitments::::remove(hash); + Ok(()) + } + + fn store_request_receipt(&self, req: &Request) -> Result<(), Error> { + let hash = hash_request::(req); + RequestReceipts::::insert(hash, Receipt::Ok); + Ok(()) + } + + fn consensus_client(&self, id: ConsensusClientId) -> Result, Error> { + ::ConsensusClientProvider::consensus_client(id) + } + + fn challenge_period(&self, id: ConsensusStateId) -> Option { + ChallengePeriod::::get(&id).map(Duration::from_secs) + } + + fn ismp_router(&self) -> Box { + Box::new(T::IsmpRouter::default()) + } + + fn is_state_machine_frozen(&self, machine: StateMachineHeight) -> Result<(), Error> { + if let Some(frozen_height) = FrozenHeights::::get(machine.id) { + if machine.height >= frozen_height { + Err(Error::FrozenStateMachine { height: machine })? + } + } + Ok(()) + } + + fn is_consensus_client_frozen(&self, client: ConsensusStateId) -> Result<(), Error> { + if FrozenConsensusClients::::get(client) { + Err(Error::FrozenConsensusClient { consensus_state_id: client })? + } + Ok(()) + } + + fn next_nonce(&self) -> u64 { + let nonce = Nonce::::get(); + Nonce::::put(nonce + 1); + nonce + } + + fn response_receipt(&self, res: &Request) -> Option<()> { + let commitment = hash_request::(res); + + let _ = ResponseReceipts::::get(commitment) + .ok_or_else(|| Error::ImplementationSpecific("Response receipt not found".to_string())) + .ok()?; + + Some(()) + } + + fn freeze_consensus_client(&self, client: ConsensusStateId) -> Result<(), Error> { + FrozenConsensusClients::::insert(client, true); + Ok(()) + } + + fn store_response_receipt(&self, req: &Request) -> Result<(), Error> { + let hash = hash_request::(req); + ResponseReceipts::::insert(hash, Receipt::Ok); + Ok(()) + } + + fn consensus_client_id( + &self, + consensus_state_id: ConsensusStateId, + ) -> Option { + ConsensusStateClient::::get(&consensus_state_id) + } + + fn store_consensus_state_id( + &self, + consensus_state_id: ConsensusStateId, + client_id: ConsensusClientId, + ) -> Result<(), Error> { + ConsensusStateClient::::insert(consensus_state_id, client_id); + Ok(()) + } + + fn unbonding_period(&self, consensus_state_id: ConsensusStateId) -> Option { + UnbondingPeriod::::get(&consensus_state_id).map(Duration::from_secs) + } + + fn store_unbonding_period( + &self, + consensus_state_id: ConsensusStateId, + period: u64, + ) -> Result<(), Error> { + UnbondingPeriod::::insert(consensus_state_id, period); + Ok(()) + } + + fn store_challenge_period( + &self, + consensus_state_id: ConsensusStateId, + period: u64, + ) -> Result<(), Error> { + ChallengePeriod::::insert(consensus_state_id, period); + Ok(()) + } + + fn allowed_proxies(&self) -> Vec { + AllowedProxies::::get() + } + + fn store_allowed_proxies(&self, allowed: Vec) { + AllowedProxies::::set(allowed); + } +} + +impl ismp_rs::util::Keccak256 for Host { + fn keccak256(bytes: &[u8]) -> H256 + where + Self: Sized, + { + sp_io::hashing::keccak_256(bytes).into() + } +} diff --git a/parachain/modules/ismp/core/src/lib.rs b/parachain/modules/ismp/core/src/lib.rs new file mode 100644 index 000000000..1fb479f5d --- /dev/null +++ b/parachain/modules/ismp/core/src/lib.rs @@ -0,0 +1,781 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ISMP implementation for substrate-based chains. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] + +extern crate alloc; +extern crate core; + +pub mod benchmarking; +pub mod dispatcher; +mod errors; +pub mod events; +pub mod handlers; +pub mod host; +mod mmr; +#[cfg(any(feature = "runtime-benchmarks", feature = "testing", test))] +pub mod mocks; +pub mod primitives; +#[cfg(test)] +pub mod tests; +pub mod weight_info; + +pub use mmr::utils::NodesUtils; + +use crate::host::Host; +use codec::{Decode, Encode}; +use core::time::Duration; +use frame_support::{ + dispatch::{DispatchResult, DispatchResultWithPostInfo, Pays, PostDispatchInfo}, + traits::{Get, UnixTime}, +}; +use ismp_rs::{ + consensus::{ConsensusClientId, StateMachineId}, + handlers::{handle_incoming_message, MessageResult}, + host::StateMachine, + messaging::CreateConsensusState, + router::{Request, Response}, +}; +use log::debug; +use sp_core::{offchain::StorageKind, H256}; +// Re-export pallet items so that they can be accessed from the crate namespace. +use crate::{ + errors::{HandlingError, ModuleCallbackResult}, + mmr::mmr::Mmr, + weight_info::get_weight, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use ismp_primitives::{ + mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, + LeafIndexQuery, +}; +use ismp_rs::{consensus::StateMachineHeight, host::IsmpHost, messaging::Message}; +pub use pallet::*; +use sp_runtime::RuntimeDebug; +use sp_std::prelude::*; + +// Definition of the pallet logic, to be aggregated at runtime definition through +// `construct_runtime`. +#[frame_support::pallet] +pub mod pallet { + + // Import various types used to declare pallet in scope. + use super::*; + use crate::{ + dispatcher::Receipt, + errors::HandlingError, + primitives::{ConsensusClientProvider, WeightUsed}, + weight_info::{WeightInfo, WeightProvider}, + }; + use alloc::collections::BTreeSet; + use frame_support::{pallet_prelude::*, traits::UnixTime}; + use frame_system::pallet_prelude::*; + use ismp_primitives::{ + mmr::{LeafIndex, NodeIndex}, + ISMP_ID, + }; + use ismp_rs::{ + consensus::{ + ConsensusClientId, ConsensusStateId, StateCommitment, StateMachineHeight, + StateMachineId, + }, + handlers::{self}, + host::StateMachine, + messaging::Message, + router::IsmpRouter, + }; + use sp_core::H256; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Prefix for elements stored in the Off-chain DB via Indexing API. + const INDEXING_PREFIX: &'static [u8]; + + /// Admin origin for privileged actions + type AdminOrigin: EnsureOrigin; + + /// Host state machine identifier + type StateMachine: Get; + + /// Timestamp provider + type TimeProvider: UnixTime; + + /// Configurable router that dispatches calls to modules + type IsmpRouter: IsmpRouter + Default; + /// Provides concrete implementations of consensus clients + type ConsensusClientProvider: ConsensusClientProvider; + + /// Weight Info + type WeightInfo: WeightInfo; + + /// Weight provider for consensus clients and module callbacks + type WeightProvider: WeightProvider; + } + + // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and + // method. + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Latest MMR Root hash + #[pallet::storage] + #[pallet::getter(fn mmr_root_hash)] + pub type RootHash = StorageValue<_, H256, ValueQuery>; + + /// Current size of the MMR (number of leaves) for requests. + #[pallet::storage] + #[pallet::getter(fn number_of_leaves)] + pub type NumberOfLeaves = StorageValue<_, LeafIndex, ValueQuery>; + + /// Hashes of the nodes in the MMR for requests. + /// + /// Note this collection only contains MMR peaks, the inner nodes (and leaves) + /// are pruned and only stored in the Offchain DB. + #[pallet::storage] + #[pallet::getter(fn request_peaks)] + pub type Nodes = StorageMap<_, Identity, NodeIndex, H256, OptionQuery>; + + /// Holds a map of state machine heights to their verified state commitments + #[pallet::storage] + #[pallet::getter(fn state_commitments)] + pub type StateCommitments = + StorageMap<_, Blake2_128Concat, StateMachineHeight, StateCommitment, OptionQuery>; + + /// Holds a map of consensus clients to their consensus state. + #[pallet::storage] + #[pallet::getter(fn consensus_states)] + pub type ConsensusStates = + StorageMap<_, Twox64Concat, ConsensusClientId, Vec, OptionQuery>; + + /// Holds a map of state machines to the height at which they've been frozen due to byzantine + /// behaviour + #[pallet::storage] + #[pallet::getter(fn latest_messaging_heights)] + pub type FrozenHeights = + StorageMap<_, Blake2_128Concat, StateMachineId, u64, OptionQuery>; + + /// Holds a map of state machines to the latest height we've processed requests for + #[pallet::storage] + #[pallet::getter(fn frozen_heights)] + pub type LatestMessagingHeight = + StorageMap<_, Blake2_128Concat, StateMachineId, u64, ValueQuery>; + + /// A mapping of ConsensusStateId to ConsensusClientId + #[pallet::storage] + pub type ConsensusStateClient = + StorageMap<_, Blake2_128Concat, ConsensusStateId, ConsensusClientId, OptionQuery>; + + /// A mapping of ConsensusStateId to Unbonding periods + #[pallet::storage] + pub type UnbondingPeriod = + StorageMap<_, Blake2_128Concat, ConsensusStateId, u64, OptionQuery>; + + /// A mapping of ConsensusStateId to Challenge periods + #[pallet::storage] + pub type ChallengePeriod = + StorageMap<_, Blake2_128Concat, ConsensusStateId, u64, OptionQuery>; + + /// Holds a map of consensus clients frozen due to byzantine + /// behaviour + #[pallet::storage] + #[pallet::getter(fn frozen_consensus_clients)] + pub type FrozenConsensusClients = + StorageMap<_, Blake2_128Concat, ConsensusStateId, bool, ValueQuery>; + + /// The latest verified height for a state machine + #[pallet::storage] + #[pallet::getter(fn latest_state_height)] + pub type LatestStateMachineHeight = + StorageMap<_, Blake2_128Concat, StateMachineId, u64, ValueQuery>; + + /// Bounded vec of allowed proxies + #[pallet::storage] + #[pallet::getter(fn allowed_proxies)] + pub type AllowedProxies = StorageValue<_, Vec, ValueQuery>; + + /// Holds the timestamp at which a consensus client was recently updated. + /// Used in ensuring that the configured challenge period elapses. + #[pallet::storage] + #[pallet::getter(fn consensus_update_time)] + pub type ConsensusClientUpdateTime = + StorageMap<_, Twox64Concat, ConsensusClientId, u64, OptionQuery>; + + /// Holds the timestamp at which a state machine height was updated. + /// Used in ensuring that the configured challenge period elapses. + #[pallet::storage] + #[pallet::getter(fn state_machine_update_time)] + pub type StateMachineUpdateTime = + StorageMap<_, Twox64Concat, StateMachineHeight, u64, OptionQuery>; + + /// Commitments for outgoing requests + /// The key is the request commitment + #[pallet::storage] + #[pallet::getter(fn request_commitments)] + pub type RequestCommitments = + StorageMap<_, Identity, H256, LeafIndexQuery, OptionQuery>; + + /// Commitments for outgoing responses + /// The key is the response commitment + #[pallet::storage] + #[pallet::getter(fn response_commitments)] + pub type ResponseCommitments = StorageMap<_, Identity, H256, Receipt, OptionQuery>; + + /// Receipts for incoming requests + /// The key is the request commitment + #[pallet::storage] + #[pallet::getter(fn request_receipts)] + pub type RequestReceipts = StorageMap<_, Identity, H256, Receipt, OptionQuery>; + + /// Receipts for incoming responses + /// The key is the request commitment + #[pallet::storage] + #[pallet::getter(fn response_receipts)] + pub type ResponseReceipts = StorageMap<_, Identity, H256, Receipt, OptionQuery>; + + /// Consensus update results still in challenge period + /// Set contains a tuple of previous height and latest height + #[pallet::storage] + #[pallet::getter(fn consensus_update_results)] + pub type ConsensusUpdateResults = StorageMap< + _, + Twox64Concat, + ConsensusClientId, + BTreeSet<(StateMachineHeight, StateMachineHeight)>, + OptionQuery, + >; + + /// Latest nonce for messages sent from this chain + #[pallet::storage] + #[pallet::getter(fn nonce)] + pub type Nonce = StorageValue<_, u64, ValueQuery>; + + /// Contains a tuple of the weight consumed and weight limit in executing contract callbacks in + /// a transaction + #[pallet::storage] + #[pallet::getter(fn weight_consumed)] + pub type WeightConsumed = StorageValue<_, WeightUsed, ValueQuery>; + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + // return Mmr finalization weight here + ::WeightInfo::on_finalize(Self::number_of_leaves() as u32) + } + + fn on_finalize(_n: BlockNumberFor) { + // Only finalize if mmr was modified + let leaves = Self::number_of_leaves(); + let root = if leaves != 0 { + let mmr: Mmr = Mmr::new(leaves); + // Update the size, `mmr.finalize()` should also never fail. + let root = match mmr.finalize() { + Ok(root) => root, + Err(e) => { + log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e); + return + }, + }; + + >::put(root); + + root + } else { + H256::default() + }; + + let digest = sp_runtime::generic::DigestItem::Consensus(ISMP_ID, root.encode()); + >::deposit_log(digest); + } + + fn offchain_worker(_n: BlockNumberFor) {} + } + + /// Params to update the unbonding period for a consensus state + #[derive(Debug, Clone, Encode, Decode, scale_info::TypeInfo, PartialEq, Eq)] + pub struct UpdateConsensusState { + /// Consensus state identifier + pub consensus_state_id: ConsensusStateId, + /// Unbonding duration + pub unbonding_period: Option, + /// Challenge period duration + pub challenge_period: Option, + } + + #[pallet::call] + impl Pallet { + /// Handles ismp messages + #[pallet::weight(get_weight::(&messages))] + #[pallet::call_index(0)] + #[frame_support::transactional] + pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResultWithPostInfo { + let _ = ensure_signed(origin)?; + + Self::handle_messages(messages) + } + + /// Create a consensus client, using a subjectively chosen consensus state. + #[pallet::weight(::WeightInfo::create_consensus_client())] + #[pallet::call_index(1)] + pub fn create_consensus_client( + origin: OriginFor, + message: CreateConsensusState, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + let host = Host::::default(); + + let result = handlers::create_client(&host, message) + .map_err(|_| Error::::ConsensusClientCreationFailed)?; + + Self::deposit_event(Event::::ConsensusClientCreated { + consensus_client_id: result.consensus_client_id, + }); + + Ok(()) + } + + /// Set the unbonding period for a consensus state. + #[pallet::weight(::DbWeight::get().writes(2))] + #[pallet::call_index(2)] + pub fn update_consensus_state( + origin: OriginFor, + message: UpdateConsensusState, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + let host = Host::::default(); + + if let Some(unbonding_period) = message.unbonding_period { + host.store_unbonding_period(message.consensus_state_id, unbonding_period) + .map_err(|_| Error::::UnbondingPeriodUpdateFailed)?; + } + + if let Some(challenge_period) = message.challenge_period { + host.store_challenge_period(message.consensus_state_id, challenge_period) + .map_err(|_| Error::::UnbondingPeriodUpdateFailed)?; + } + + Ok(()) + } + + /// Set the allowed proxies + #[pallet::weight(::DbWeight::get().writes(1))] + #[pallet::call_index(3)] + pub fn set_config(origin: OriginFor, allowed: Vec) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + let host = Host::::default(); + host.store_allowed_proxies(allowed); + + Ok(()) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Emitted when a state machine is successfully updated to a new height + StateMachineUpdated { + /// State machine height + state_machine_id: StateMachineId, + /// State machine latest height + latest_height: u64, + }, + /// Signifies that a client has begun it's challenge period + ChallengePeriodStarted { + /// Consensus client id + consensus_client_id: ConsensusClientId, + /// Tuple of previous height and latest height for state machines + state_machines: BTreeSet<(StateMachineHeight, StateMachineHeight)>, + }, + /// Indicates that a consensus client has been created + ConsensusClientCreated { + /// Consensus client id + consensus_client_id: ConsensusClientId, + }, + /// An Outgoing Response has been deposited + Response { + /// Chain that this response will be routed to + dest_chain: StateMachine, + /// Source Chain for this response + source_chain: StateMachine, + /// Nonce for the request which this response is for + request_nonce: u64, + }, + /// An Outgoing Request has been deposited + Request { + /// Chain that this request will be routed to + dest_chain: StateMachine, + /// Source Chain for request + source_chain: StateMachine, + /// Request nonce + request_nonce: u64, + }, + /// Some errors handling some ismp messages + HandlingErrors { + /// Message handling errors + errors: Vec, + }, + } + + /// Pallet errors + #[pallet::error] + pub enum Error { + /// Invalid ISMP message + InvalidMessage, + /// Encountered an error while creating the consensus client. + ConsensusClientCreationFailed, + /// Couldn't update unbonding period + UnbondingPeriodUpdateFailed, + /// Couldn't update challenge period + ChallengePeriodUpdateFailed, + } +} + +impl Pallet { + /// Generate an MMR proof for the given `leaf_indices`. + /// Note this method can only be used from an off-chain context + /// (Offchain Worker or Runtime API call), since it requires + /// all the leaves to be present. + /// It may return an error or panic if used incorrectly. + pub fn generate_proof( + leaf_indices: Vec, + ) -> Result<(Vec, primitives::Proof), primitives::Error> { + let leaves_count = NumberOfLeaves::::get(); + let mmr = Mmr::::new(leaves_count); + mmr.generate_proof(leaf_indices) + } + + /// Provides a way to handle messages. + pub fn handle_messages(messages: Vec) -> DispatchResultWithPostInfo { + // Define a host + WeightConsumed::::kill(); + let host = Host::::default(); + let mut errors: Vec = vec![]; + let total_weight = get_weight::(&messages); + for message in messages { + match handle_incoming_message(&host, message.clone()) { + Ok(MessageResult::ConsensusMessage(res)) => { + // check if this is a trusted state machine + let is_trusted_state_machine = host + .challenge_period(res.consensus_state_id.clone()) == + Some(Duration::from_secs(0)); + + if is_trusted_state_machine { + for (_, latest_height) in res.state_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } else { + if let Some(pending_updates) = + ConsensusUpdateResults::::get(res.consensus_client_id) + { + for (_, latest_height) in pending_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } + + Self::deposit_event(Event::::ChallengePeriodStarted { + consensus_client_id: res.consensus_client_id, + state_machines: res.state_updates.clone(), + }); + + // Store the new update result that have just entered the challenge + // period + ConsensusUpdateResults::::insert( + res.consensus_client_id, + res.state_updates, + ); + } + }, + Ok(MessageResult::Response(res)) => { + let StateMachineHeight { id, height } = match message { + Message::Response(ref response) => response.proof().height.clone(), + _ => unreachable!(), + }; + // update the messaging heights + if LatestMessagingHeight::::get(&id) < height { + LatestMessagingHeight::::insert(id, height); + } + debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Response(res)); + }, + Ok(MessageResult::Request(res)) => { + let StateMachineHeight { id, height } = match message { + Message::Request(ref request) => request.proof.height.clone(), + _ => unreachable!(), + }; + // update the messaging heights + if LatestMessagingHeight::::get(&id) < height { + LatestMessagingHeight::::insert(id, height); + } + debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Request(res)); + }, + Ok(MessageResult::Timeout(res)) => { + debug!(target: "ismp-modules", "Module Callback Results {:?}", ModuleCallbackResult::Timeout(res)); + }, + Err(err) => { + errors.push(err.into()); + }, + _ => {}, + } + } + + if !errors.is_empty() { + debug!(target: "pallet-ismp", "Handling Errors {:?}", errors); + Self::deposit_event(Event::::HandlingErrors { errors }) + } + + Ok(PostDispatchInfo { + actual_weight: { + let acc_weight = WeightConsumed::::get(); + Some((total_weight - acc_weight.weight_limit) + acc_weight.weight_used) + }, + pays_fee: Pays::Yes, + }) + } + + /// Return the on-chain MMR root hash. + pub fn mmr_root() -> H256 { + Self::mmr_root_hash() + } + + /// Return mmr leaf count + pub fn mmr_leaf_count() -> LeafIndex { + Self::number_of_leaves() + } +} + +/// Digest log for mmr root hash +#[derive(RuntimeDebug, Encode, Decode)] +pub struct RequestResponseLog { + /// The mmr root hash + mmr_root_hash: ::Hash, +} + +impl Pallet { + /// Returns the offchain key for a request leaf index + pub fn request_leaf_index_offchain_key( + source_chain: StateMachine, + dest_chain: StateMachine, + nonce: u64, + ) -> Vec { + (T::INDEXING_PREFIX, "requests_leaf_indices", source_chain, dest_chain, nonce).encode() + } + + /// Returns the offchain key for a response leaf index + pub fn response_leaf_index_offchain_key( + source_chain: StateMachine, + dest_chain: StateMachine, + nonce: u64, + ) -> Vec { + (T::INDEXING_PREFIX, "responses_leaf_indices", source_chain, dest_chain, nonce).encode() + } + + /// Stores the leaf index or the given key + pub fn store_leaf_index_offchain(key: Vec, leaf_index: LeafIndex) { + sp_io::offchain_index::set(&key, &leaf_index.encode()); + } + + /// Gets the request from the offchain storage + pub fn get_request(leaf_index: LeafIndex) -> Option { + let key = Pallet::::offchain_key(leaf_index); + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + let data_or_hash = DataOrHash::decode(&mut &*elem).ok()?; + return match data_or_hash { + DataOrHash::Data(leaf) => match leaf { + Leaf::Request(req) => Some(req), + _ => None, + }, + _ => None, + } + } + None + } + + /// Gets the response from the offchain storage + pub fn get_response(leaf_index: LeafIndex) -> Option { + let key = Pallet::::offchain_key(leaf_index); + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + let data_or_hash = DataOrHash::decode(&mut &*elem).ok()?; + return match data_or_hash { + DataOrHash::Data(leaf) => match leaf { + Leaf::Response(res) => Some(res), + _ => None, + }, + _ => None, + } + } + None + } + + /// Gets the leaf index for a request or response from the offchain storage + pub fn get_leaf_index( + source_chain: StateMachine, + dest_chain: StateMachine, + nonce: u64, + is_req: bool, + ) -> Option { + let key = if is_req { + Self::request_leaf_index_offchain_key(source_chain, dest_chain, nonce) + } else { + Self::response_leaf_index_offchain_key(source_chain, dest_chain, nonce) + }; + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + return LeafIndex::decode(&mut &*elem).ok() + } + None + } + + /// Get unfulfilled Get requests + pub fn pending_get_requests() -> Vec { + RequestCommitments::::iter() + .filter_map(|(key, query)| { + let leaf_index = + Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, true)?; + let req = Self::get_request(leaf_index)?; + (req.is_type_get() && !ResponseReceipts::::contains_key(key)) + .then(|| req.get_request().ok()) + .flatten() + }) + .collect() + } + + /// Return the scale encoded consensus state + pub fn get_consensus_state(id: ConsensusClientId) -> Option> { + ConsensusStates::::get(id) + } + + /// Return the timestamp this client was last updated in seconds + pub fn get_consensus_update_time(id: ConsensusClientId) -> Option { + ConsensusClientUpdateTime::::get(id) + } + + /// Return the challenge period + pub fn get_challenge_period(id: ConsensusClientId) -> Option { + ChallengePeriod::::get(id) + } + + /// Return latest timestamp on chain + pub fn get_timestamp() -> Option { + Some(::now().as_secs()) + } + + /// Return the latest height of the state machine + pub fn get_latest_state_machine_height(id: StateMachineId) -> Option { + Some(LatestStateMachineHeight::::get(id)) + } + + /// Get Request Leaf Indices + pub fn get_request_leaf_indices(leaf_queries: Vec) -> Vec { + leaf_queries + .into_iter() + .filter_map(|query| { + Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, true) + }) + .collect() + } + + /// Get Response Leaf Indices + pub fn get_response_leaf_indices(leaf_queries: Vec) -> Vec { + leaf_queries + .into_iter() + .filter_map(|query| { + Self::get_leaf_index(query.source_chain, query.dest_chain, query.nonce, false) + }) + .collect() + } + + /// Get actual requests + pub fn get_requests(leaf_indices: Vec) -> Vec { + leaf_indices + .into_iter() + .filter_map(|leaf_index| Self::get_request(leaf_index)) + .collect() + } + + /// Get actual requests + pub fn get_responses(leaf_indices: Vec) -> Vec { + leaf_indices + .into_iter() + .filter_map(|leaf_index| Self::get_response(leaf_index)) + .collect() + } + + /// Insert a leaf into the mmr + pub(crate) fn mmr_push(leaf: Leaf) -> Option { + let offchain_key = match &leaf { + Leaf::Request(req) => Pallet::::request_leaf_index_offchain_key( + req.source_chain(), + req.dest_chain(), + req.nonce(), + ), + Leaf::Response(res) => Pallet::::response_leaf_index_offchain_key( + res.dest_chain(), + res.source_chain(), + res.nonce(), + ), + }; + let leaves = Self::number_of_leaves(); + let mmr: Mmr = Mmr::new(leaves); + let pos = mmr.push(leaf)?; + Pallet::::store_leaf_index_offchain(offchain_key, pos); + Some(pos) + } +} + +impl Pallet { + /// Get a node from runtime storage + fn get_node(pos: NodeIndex) -> Option { + Nodes::::get(pos).map(DataOrHash::Hash) + } + + /// Remove a node from storage + fn remove_node(pos: NodeIndex) { + Nodes::::remove(pos); + } + + /// Insert a node into storage + fn insert_node(pos: NodeIndex, node: H256) { + Nodes::::insert(pos, node) + } + + /// Returns the number of leaves in the mmr + fn get_num_leaves() -> LeafIndex { + NumberOfLeaves::::get() + } + + /// Set the number of leaves in the mmr + fn set_num_leaves(num_leaves: LeafIndex) { + NumberOfLeaves::::put(num_leaves) + } + + /// Returns the offchain key for an index + fn offchain_key(pos: NodeIndex) -> Vec { + (T::INDEXING_PREFIX, "leaves", pos).encode() + } +} diff --git a/parachain/modules/ismp/core/src/mmr.rs b/parachain/modules/ismp/core/src/mmr.rs new file mode 100644 index 000000000..64a241023 --- /dev/null +++ b/parachain/modules/ismp/core/src/mmr.rs @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This module contains code adapted from https://github.com/paritytech/substrate/blob/master/frame/merkle-mountain-range/src/mmr/mod.rs + +pub mod mmr; +pub mod storage; +pub mod utils; diff --git a/parachain/modules/ismp/core/src/mmr/mmr.rs b/parachain/modules/ismp/core/src/mmr/mmr.rs new file mode 100644 index 000000000..e53a011f4 --- /dev/null +++ b/parachain/modules/ismp/core/src/mmr/mmr.rs @@ -0,0 +1,110 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + host::Host, + mmr::{ + storage::{OffchainStorage, RuntimeStorage, Storage}, + utils::NodesUtils, + }, + primitives::{Error, Proof}, + Config, +}; +use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher, NodeIndex}; +use sp_core::H256; +use sp_std::prelude::*; + +/// A wrapper around an MMR library to expose limited functionality. +/// +/// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage) +/// vs [Off-chain](crate::mmr::storage::OffchainStorage)). +pub struct Mmr +where + T: Config, + Storage: mmr_lib::MMRStore, +{ + mmr: mmr_lib::MMR>, Storage>, + leaves: NodeIndex, +} + +impl Mmr +where + T: Config, + Storage: mmr_lib::MMRStore, +{ + /// Create a pointer to an existing MMR with given number of leaves. + pub fn new(leaves: NodeIndex) -> Self { + let size = NodesUtils::new(leaves).size(); + Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves } + } +} + +/// Runtime specific MMR functions. +impl Mmr +where + T: Config, +{ + /// Push another item to the MMR and commit + /// + /// Returns number of leaves and the element position (index) in the MMR. + pub fn push(mut self, leaf: Leaf) -> Option { + let position = self.mmr.push(DataOrHash::Data(leaf)).map_err(|_| Error::Push).ok()?; + let num_leaves = self.leaves + 1; + self.leaves = num_leaves; + self.mmr.commit().ok()?; + Some(position) + } + + /// Calculate the new MMR's root hash. + pub fn finalize(self) -> Result { + let root = self.mmr.get_root().map_err(|_| Error::GetRoot)?; + Ok(root.hash::>()) + } +} + +/// Off-chain specific MMR functions. +impl Mmr +where + T: Config, +{ + /// Generate a proof for given leaf indices. + /// + /// Proof generation requires all the nodes (or their hashes) to be available in the storage. + /// (i.e. you can't run the function in the pruned storage). + pub fn generate_proof( + &self, + positions: Vec, + ) -> Result<(Vec, Proof), Error> { + let store = >::default(); + let leaves = positions + .iter() + .map(|pos| match mmr_lib::MMRStore::get_elem(&store, *pos) { + Ok(Some(DataOrHash::Data(leaf))) => Ok(leaf), + _ => Err(Error::LeafNotFound), + }) + .collect::, Error>>()?; + log::trace!(target: "runtime::mmr", "Positions {:?}", positions); + let leaf_count = self.leaves; + self.mmr + .gen_proof(positions.clone()) + .map_err(|_| Error::GenerateProof) + .map(|p| Proof { + leaf_indices: positions, + leaf_count, + items: p.proof_items().iter().map(|x| x.hash::>()).collect(), + }) + .map(|p| (leaves, p)) + } +} diff --git a/parachain/modules/ismp/core/src/mmr/storage.rs b/parachain/modules/ismp/core/src/mmr/storage.rs new file mode 100644 index 000000000..edcfddbfc --- /dev/null +++ b/parachain/modules/ismp/core/src/mmr/storage.rs @@ -0,0 +1,184 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! An MMR storage implementation. +use codec::Encode; +use ismp_primitives::mmr::{DataOrHash, NodeIndex}; +use log::{debug, trace}; +use mmr_lib::helper; +use sp_core::offchain::StorageKind; +use sp_std::iter::Peekable; +#[cfg(not(feature = "std"))] +use sp_std::prelude::*; + +use crate::{host::Host, mmr::utils::NodesUtils, Config, Pallet}; + +/// A marker type for runtime-specific storage implementation. +/// +/// Allows appending new items to the MMR and proof verification. +/// MMR nodes are appended to two different storages: +/// 1. We add nodes (leaves) hashes to the on-chain storage (see [crate::Nodes]). +/// 2. We add full leaves (and all inner nodes as well) into the `IndexingAPI` during block +/// processing, so the values end up in the Offchain DB if indexing is enabled. +pub struct RuntimeStorage; + +/// A marker type for offchain-specific storage implementation. +/// +/// Allows proof generation and verification, but does not support appending new items. +/// MMR nodes are assumed to be stored in the Off-Chain DB. Note this storage type +/// DOES NOT support adding new items to the MMR. +pub struct OffchainStorage; + +/// A storage layer for MMR. +/// +/// There are two different implementations depending on the use case. +/// See docs for [RuntimeStorage] and [OffchainStorage]. +pub struct Storage(sp_std::marker::PhantomData<(StorageType, T)>); + +impl Default for Storage { + fn default() -> Self { + Self(Default::default()) + } +} + +impl mmr_lib::MMRStore for Storage +where + T: Config, +{ + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { + let key = Pallet::::offchain_key(pos); + debug!( + target: "runtime::mmr::offchain", "offchain db get {}: key {:?}", + pos, key + ); + // Try to retrieve the element from Off-chain DB. + if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) { + return Ok(codec::Decode::decode(&mut &*elem).ok()) + } + + Ok(None) + } + + fn append(&mut self, _: NodeIndex, _: Vec) -> mmr_lib::Result<()> { + panic!("MMR must not be altered in the off-chain context.") + } +} + +impl mmr_lib::MMRStore for Storage +where + T: Config, +{ + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { + Ok(Pallet::::get_node(pos)) + } + + fn append(&mut self, pos: NodeIndex, elems: Vec) -> mmr_lib::Result<()> { + if elems.is_empty() { + return Ok(()) + } + + trace!( + target: "runtime::mmr", "elems: {:?}", + elems.iter().map(|elem| elem.hash::>()).collect::>() + ); + + let leaves = Pallet::::get_num_leaves(); + let size = NodesUtils::new(leaves).size(); + + if pos != size { + return Err(mmr_lib::Error::InconsistentStore) + } + + let new_size = size + elems.len() as NodeIndex; + + // A sorted (ascending) iterator over peak indices to prune and persist. + let (peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store(size, new_size); + + // Now we are going to iterate over elements to insert + // and keep track of the current `node_index` and `leaf_index`. + let mut leaf_index = leaves; + let mut node_index = size; + + for elem in elems { + // On-chain we are going to only store new peaks. + if peaks_to_store.next_if_eq(&node_index).is_some() { + Pallet::::insert_node(node_index, elem.hash::>()); + } + // We are storing full node off-chain (using indexing API). + Self::store_to_offchain(node_index, &elem); + + // Increase the indices. + if let DataOrHash::Data(..) = elem { + leaf_index += 1; + } + node_index += 1; + } + + // Update current number of leaves. + Pallet::::set_num_leaves(leaf_index); + + // And remove all remaining items from `peaks_before` collection. + for pos in peaks_to_prune { + Pallet::::remove_node(pos); + } + + Ok(()) + } +} + +impl Storage +where + T: Config, +{ + /// Store a node in the offchain db + fn store_to_offchain(pos: NodeIndex, node: &DataOrHash) { + let encoded_node = node.encode(); + + let key = Pallet::::offchain_key(pos); + debug!( + target: "runtime::mmr::offchain", "offchain db set: pos {} key {:?}", + pos, key + ); + // Indexing API is used to store the full node content. + sp_io::offchain_index::set(&key, &encoded_node); + } +} + +/// Calculate peaks to prune and store +fn peaks_to_prune_and_store( + old_size: NodeIndex, + new_size: NodeIndex, +) -> (impl Iterator, Peekable>) { + // A sorted (ascending) collection of peak indices before and after insertion. + // both collections may share a common prefix. + let peaks_before = if old_size == 0 { vec![] } else { helper::get_peaks(old_size) }; + let peaks_after = helper::get_peaks(new_size); + trace!(target: "runtime::mmr", "peaks_before: {:?}", peaks_before); + trace!(target: "runtime::mmr", "peaks_after: {:?}", peaks_after); + let mut peaks_before = peaks_before.into_iter().peekable(); + let mut peaks_after = peaks_after.into_iter().peekable(); + + // Consume a common prefix between `peaks_before` and `peaks_after`, + // since that's something we will not be touching anyway. + while peaks_before.peek() == peaks_after.peek() { + peaks_before.next(); + peaks_after.next(); + } + + // what's left in both collections is: + // 1. Old peaks to remove from storage + // 2. New peaks to persist in storage + (peaks_before, peaks_after) +} diff --git a/parachain/modules/ismp/core/src/mmr/utils.rs b/parachain/modules/ismp/core/src/mmr/utils.rs new file mode 100644 index 000000000..dafb399cf --- /dev/null +++ b/parachain/modules/ismp/core/src/mmr/utils.rs @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ismp_primitives::mmr::{LeafIndex, NodeIndex}; + +/// MMR nodes & size -related utilities. +pub struct NodesUtils { + no_of_leaves: LeafIndex, +} + +impl NodesUtils { + /// Create new instance of MMR nodes utilities for given number of leaves. + pub fn new(no_of_leaves: LeafIndex) -> Self { + Self { no_of_leaves } + } + + /// Calculate number of peaks in the MMR. + pub fn number_of_peaks(&self) -> NodeIndex { + self.number_of_leaves().count_ones() as NodeIndex + } + + /// Return the number of leaves in the MMR. + pub fn number_of_leaves(&self) -> LeafIndex { + self.no_of_leaves + } + + /// Calculate the total size of MMR (number of nodes). + pub fn size(&self) -> NodeIndex { + 2 * self.no_of_leaves - self.number_of_peaks() + } +} diff --git a/parachain/modules/ismp/core/src/mocks/ismp.rs b/parachain/modules/ismp/core/src/mocks/ismp.rs new file mode 100644 index 000000000..81adcdd10 --- /dev/null +++ b/parachain/modules/ismp/core/src/mocks/ismp.rs @@ -0,0 +1,151 @@ +//! Mocks used by both tests and benchmarks +use crate::primitives::ModuleId; +use alloc::collections::BTreeMap; +use frame_support::PalletId; +use ismp_rs::{ + consensus::{ + ConsensusClient, StateCommitment, StateMachineClient, StateMachineHeight, StateMachineId, + VerifiedCommitments, + }, + error::Error as IsmpError, + handlers, + host::{Ethereum, IsmpHost, StateMachine}, + messaging::{CreateConsensusState, Proof, StateCommitmentHeight}, + module::IsmpModule, + router::{Post, Request, RequestResponse, Response}, +}; + +/// Mock consensus state id +pub const MOCK_CONSENSUS_STATE_ID: [u8; 4] = *b"mock"; + +/// module id for the mock benchmarking module +pub const MODULE_ID: ModuleId = ModuleId::Pallet(PalletId(*b"__mock__")); + +fn set_timestamp(value: u64) +where + ::Moment: From, +{ + pallet_timestamp::Pallet::::set_timestamp(value.into()); +} + +/// Mock module +#[derive(Default)] +pub struct MockModule; + +impl IsmpModule for MockModule { + fn on_accept(&self, _request: Post) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } + + fn on_response(&self, _response: Response) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } + + fn on_timeout(&self, _request: Request) -> Result<(), ismp_rs::error::Error> { + Ok(()) + } +} + +/// A mock consensus client for benchmarking +#[derive(Default)] +pub struct MockConsensusClient; + +impl ConsensusClient for MockConsensusClient { + fn verify_consensus( + &self, + _host: &dyn IsmpHost, + _cs_id: ismp_rs::consensus::ConsensusStateId, + _trusted_consensus_state: Vec, + _proof: Vec, + ) -> Result<(Vec, VerifiedCommitments), IsmpError> { + Ok(Default::default()) + } + + fn verify_fraud_proof( + &self, + _host: &dyn IsmpHost, + _trusted_consensus_state: Vec, + _proof_1: Vec, + _proof_2: Vec, + ) -> Result<(), IsmpError> { + Ok(()) + } + + fn state_machine(&self, _id: StateMachine) -> Result, IsmpError> { + Ok(Box::new(MockStateMachine)) + } +} + +/// Mock State Machine +pub struct MockStateMachine; + +impl StateMachineClient for MockStateMachine { + fn verify_membership( + &self, + _host: &dyn IsmpHost, + _item: RequestResponse, + _root: StateCommitment, + _proof: &Proof, + ) -> Result<(), IsmpError> { + Ok(()) + } + + fn state_trie_key(&self, _request: Vec) -> Vec> { + Default::default() + } + + fn verify_state_proof( + &self, + _host: &dyn IsmpHost, + _keys: Vec>, + _root: StateCommitment, + _proof: &Proof, + ) -> Result, Option>>, IsmpError> { + Ok(Default::default()) + } +} + +/// Mock client setup +pub fn setup_mock_client(host: &H) -> StateMachineHeight +where + ::Moment: From, +{ + set_timestamp::(1000_000); + handlers::create_client( + host, + CreateConsensusState { + consensus_state: vec![], + consensus_client_id: MOCK_CONSENSUS_STATE_ID, + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + unbonding_period: 1_000_000, + challenge_period: 0, + state_machine_commitments: vec![( + StateMachineId { + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + }, + StateCommitmentHeight { + commitment: StateCommitment { + timestamp: 1000, + overlay_root: None, + state_root: Default::default(), + }, + height: 3, + }, + )], + }, + ) + .unwrap(); + let height = StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + }, + height: 3, + }; + host.store_state_machine_update_time(height, core::time::Duration::from_millis(1000_000)) + .unwrap(); + + set_timestamp::(1000_000_000); + height +} diff --git a/parachain/modules/ismp/core/src/mocks/mod.rs b/parachain/modules/ismp/core/src/mocks/mod.rs new file mode 100644 index 000000000..afbcfd0af --- /dev/null +++ b/parachain/modules/ismp/core/src/mocks/mod.rs @@ -0,0 +1,116 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Mock implementations for tests & benchmarks +#![allow(missing_docs, dead_code, unused_imports)] +pub mod ismp; + +use crate as pallet_ismp; +use crate::*; + +use crate::primitives::ConsensusClientProvider; +use frame_support::traits::{ConstU32, ConstU64, Get}; +use frame_system::EnsureRoot; +use ismp_rs::{consensus::ConsensusClient, module::IsmpModule, router::IsmpRouter}; + +use ismp::{MockConsensusClient, MockModule}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{IdentityLookup, Keccak256}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Ismp: pallet_ismp::{Pallet, Storage, Call, Event}, + } +); + +pub struct StateMachineProvider; + +impl Get for StateMachineProvider { + fn get() -> StateMachine { + StateMachine::Kusama(100) + } +} + +pub struct ConsensusProvider; + +impl ConsensusClientProvider for ConsensusProvider { + fn consensus_client( + _id: ConsensusClientId, + ) -> Result, ismp_rs::error::Error> { + Ok(Box::new(MockConsensusClient)) + } +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = Keccak256; + type AccountId = sp_core::sr25519::Public; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type Nonce = u64; + type Block = Block; + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + const INDEXING_PREFIX: &'static [u8] = b"ISMP"; + type AdminOrigin = EnsureRoot; + type StateMachine = StateMachineProvider; + type TimeProvider = Timestamp; + type IsmpRouter = ModuleRouter; + type ConsensusClientProvider = ConsensusProvider; + type WeightInfo = (); + type WeightProvider = (); +} + +#[derive(Default)] +pub struct ModuleRouter; + +impl IsmpRouter for ModuleRouter { + fn module_for_id(&self, _bytes: Vec) -> Result, ismp_rs::error::Error> { + Ok(Box::new(MockModule)) + } +} diff --git a/parachain/modules/ismp/core/src/primitives.rs b/parachain/modules/ismp/core/src/primitives.rs new file mode 100644 index 000000000..23350f3e1 --- /dev/null +++ b/parachain/modules/ismp/core/src/primitives.rs @@ -0,0 +1,109 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Pallet primitives +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, PalletId}; +use ismp_primitives::mmr::{LeafIndex, NodeIndex}; +use ismp_rs::consensus::{ConsensusClient, ConsensusClientId}; +use scale_info::TypeInfo; +use sp_core::{ + crypto::{AccountId32, ByteArray}, + H160, +}; +use sp_runtime::RuntimeDebug; +use sp_std::prelude::*; + +/// An MMR proof data for a group of leaves. +#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct Proof { + /// The indices of the leaves the proof is for. + pub leaf_indices: Vec, + /// Number of leaves in MMR, when the proof was generated. + pub leaf_count: NodeIndex, + /// Proof elements (hashes of siblings of inner nodes on the path to the leaf). + pub items: Vec, +} + +/// Merkle Mountain Range operation error. +#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, scale_info::TypeInfo)] +#[allow(missing_docs)] +pub enum Error { + InvalidNumericOp, + Push, + GetRoot, + Commit, + GenerateProof, + Verify, + LeafNotFound, + PalletNotIncluded, + InvalidLeafIndex, + InvalidBestKnownBlock, +} + +/// A trait that returns a reference to a consensus client based on its Id +/// This trait should be implemented in the runtime +pub trait ConsensusClientProvider { + /// Returns a reference to a consensus client + fn consensus_client( + id: ConsensusClientId, + ) -> Result, ismp_rs::error::Error>; +} + +/// Module identification types supported by ismp +#[derive(PartialEq, Eq, scale_info::TypeInfo)] +pub enum ModuleId { + /// Unique Pallet identification in runtime + Pallet(PalletId), + /// Contract account id + Contract(AccountId32), + /// Evm contract + Evm(H160), +} + +impl ModuleId { + /// Convert module id to raw bytes + pub fn to_bytes(&self) -> Vec { + match self { + ModuleId::Pallet(pallet_id) => pallet_id.0.to_vec(), + ModuleId::Contract(account_id) => account_id.as_slice().to_vec(), + ModuleId::Evm(account_id) => account_id.0.to_vec(), + } + } + + /// Derive module id from raw bytes + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() == 8 { + let mut inner = [0u8; 8]; + inner.copy_from_slice(bytes); + Ok(Self::Pallet(PalletId(inner))) + } else if bytes.len() == 32 { + Ok(Self::Contract(AccountId32::from_slice(bytes).expect("Infallible"))) + } else if bytes.len() == 20 { + Ok(Self::Evm(H160::from_slice(bytes))) + } else { + Err("Unknown Module ID format") + } + } +} + +/// Accumulated Weight consumed by contract callbacks in a transaction +#[derive(Default, scale_info::TypeInfo, Encode, Decode)] +pub struct WeightUsed { + /// Total weight used in executing contract callbacks in a transaction + pub weight_used: Weight, + /// Total weight limit used in executing contract callbacks in a transaction + pub weight_limit: Weight, +} diff --git a/parachain/modules/ismp/core/src/tests.rs b/parachain/modules/ismp/core/src/tests.rs new file mode 100644 index 000000000..200ec0422 --- /dev/null +++ b/parachain/modules/ismp/core/src/tests.rs @@ -0,0 +1,384 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{mocks::*, *}; +use std::{ + ops::Range, + time::{SystemTime, UNIX_EPOCH}, +}; + +use crate::{ + dispatcher::Dispatcher, + mocks::ismp::{setup_mock_client, MOCK_CONSENSUS_STATE_ID}, +}; +use frame_support::traits::OnFinalize; +use ismp_primitives::mmr::MmrHasher; +use ismp_rs::{ + consensus::StateMachineHeight, + host::Ethereum, + messaging::{Proof, ResponseMessage, TimeoutMessage}, + router::{DispatchGet, DispatchRequest, IsmpDispatcher, Post}, + util::hash_request, +}; +use ismp_testsuite::{ + check_challenge_period, check_client_expiry, frozen_check, timeout_post_processing_check, + write_outgoing_commitments, +}; +use mmr_lib::MerkleProof; +use sp_core::{ + offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, + H256, +}; +use sp_runtime::BuildStorage; + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default().build_storage().unwrap().into() +} + +fn register_offchain_ext(ext: &mut sp_io::TestExternalities) { + let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); + ext.register_extension(OffchainDbExt::new(offchain.clone())); + ext.register_extension(OffchainWorkerExt::new(offchain)); +} + +fn new_block() { + let number = frame_system::Pallet::::block_number() + 1; + let hash = H256::repeat_byte(number as u8); + + frame_system::Pallet::::reset_events(); + frame_system::Pallet::::initialize(&number, &hash, &Default::default()); + Ismp::on_finalize(number) +} + +fn push_leaves(range: Range) -> Vec { + // given + let mut positions = vec![]; + for nonce in range { + let post = ismp_rs::router::Post { + source: StateMachine::Kusama(2000), + dest: StateMachine::Kusama(2001), + nonce, + from: vec![0u8; 32], + to: vec![1u8; 32], + timeout_timestamp: 100 * nonce, + data: vec![2u8; 64], + gas_limit: 0, + }; + + let request = Request::Post(post); + let leaf = Leaf::Request(request); + + let pos = Pallet::::mmr_push(leaf.clone()).unwrap(); + positions.push(pos) + } + + positions +} + +#[test] +fn should_generate_proofs_correctly_for_single_leaf_mmr() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + let (root, positions) = ext.execute_with(|| { + // push some leaves into the mmr + let positions = push_leaves(0..1); + new_block(); + let root = Pallet::::mmr_root(); + (root, positions) + }); + ext.persist_offchain_overlay(); + + // Try to generate proofs now. This requires the offchain extensions to be present + // to retrieve full leaf data. + register_offchain_ext(&mut ext); + ext.execute_with(move || { + let (leaves, proof) = Pallet::::generate_proof(vec![positions[0]]).unwrap(); + + let mmr_size = NodesUtils::new(proof.leaf_count).size(); + let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = MerkleProof::>>::new(mmr_size, nodes); + let calculated_root = proof + .calculate_root(vec![(positions[0], DataOrHash::Data(leaves[0].clone()))]) + .unwrap(); + + assert_eq!(root, calculated_root.hash::>()) + }) +} + +#[test] +fn should_generate_and_verify_batch_proof_correctly() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + let (root, positions) = ext.execute_with(|| { + // push some leaves into the mmr + let positions = push_leaves(0..12); + new_block(); + let root = Pallet::::mmr_root(); + (root, positions) + }); + ext.persist_offchain_overlay(); + + // Try to generate proofs now. This requires the offchain extensions to be present + // to retrieve full leaf data. + register_offchain_ext(&mut ext); + ext.execute_with(move || { + let indices = vec![positions[0], positions[3], positions[2], positions[5]]; + let (leaves, proof) = Pallet::::generate_proof(indices.clone()).unwrap(); + + let mmr_size = NodesUtils::new(proof.leaf_count).size(); + let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = MerkleProof::>>::new(mmr_size, nodes); + let calculated_root = proof + .calculate_root( + indices + .into_iter() + .zip(leaves.into_iter().map(|leaf| DataOrHash::Data(leaf))) + .collect(), + ) + .unwrap(); + + assert_eq!(root, calculated_root.hash::>()) + }) +} + +#[test] +fn should_generate_and_verify_batch_proof_for_leaves_inserted_across_multiple_blocks_correctly() { + let _ = env_logger::try_init(); + let mut ext = new_test_ext(); + let (root, positions) = ext.execute_with(|| { + // push some leaves into the mmr + let mut positions = push_leaves(0..6); + new_block(); + let positions_second = push_leaves(6..12); + new_block(); + let root = Pallet::::mmr_root(); + positions.extend_from_slice(&positions_second); + (root, positions) + }); + ext.persist_offchain_overlay(); + + // Try to generate proofs now. This requires the offchain extensions to be present + // to retrieve full leaf data. + register_offchain_ext(&mut ext); + ext.execute_with(move || { + let indices = vec![positions[0], positions[9], positions[2], positions[8]]; + let (leaves, proof) = Pallet::::generate_proof(indices.clone()).unwrap(); + + let mmr_size = NodesUtils::new(proof.leaf_count).size(); + let nodes = proof.items.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = MerkleProof::>>::new(mmr_size, nodes); + let calculated_root = proof + .calculate_root( + indices + .into_iter() + .zip(leaves.into_iter().map(|leaf| DataOrHash::Data(leaf))) + .collect(), + ) + .unwrap(); + + assert_eq!(root, calculated_root.hash::>()) + }) +} + +fn set_timestamp(now: Option) { + Timestamp::set_timestamp( + now.unwrap_or(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64), + ); +} + +#[test] +fn dispatcher_should_write_receipts_for_outgoing_requests_and_responses() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(None); + let host = Host::::default(); + let dispatcher = Dispatcher::::default(); + let post = Post { + source: StateMachine::Kusama(2000), + dest: host.host_state_machine(), + nonce: 0, + from: vec![0u8; 32], + to: vec![0u8; 32], + timeout_timestamp: 0, + data: vec![0u8; 64], + gas_limit: 0, + }; + + let request_commitment = hash_request::>(&Request::Post(post.clone())); + RequestCommitments::::insert( + request_commitment, + LeafIndexQuery { source_chain: post.source, dest_chain: post.dest, nonce: 0 }, + ); + write_outgoing_commitments(&host, &dispatcher).unwrap(); + }) +} + +#[test] +fn should_reject_updates_within_challenge_period() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(None); + let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + check_challenge_period(&host).unwrap() + }) +} + +#[test] +fn should_reject_messages_for_frozen_state_machines() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(None); + let host = Host::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + frozen_check(&host).unwrap() + }) +} + +#[test] +fn should_reject_expired_check_clients() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(None); + let host = Host::::default(); + host.store_unbonding_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + check_client_expiry(&host).unwrap() + }) +} + +#[test] +fn should_handle_post_request_timeouts_correctly() { + let mut ext = new_test_ext(); + + ext.execute_with(|| { + set_timestamp(None); + let host = Host::::default(); + let dispatcher = Dispatcher::::default(); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + timeout_post_processing_check(&host, &dispatcher).unwrap() + }) +} + +#[test] +fn should_handle_get_request_timeouts_correctly() { + let mut ext = new_test_ext(); + ext.execute_with(|| { + let host = Host::::default(); + setup_mock_client::<_, Test>(&host); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 1_000_000).unwrap(); + let requests = (0..2) + .into_iter() + .map(|i| { + let msg = DispatchGet { + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + from: vec![0u8; 32], + gas_limit: 0, + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: 2, + timeout_timestamp: 1000, + }; + + let dispatcher = Dispatcher::::default(); + dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); + let get = ismp_rs::router::Get { + source: host.host_state_machine(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + nonce: i, + from: vec![0u8; 32], + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: 2, + timeout_timestamp: 1000, + gas_limit: 0, + }; + ismp_rs::router::Request::Get(get) + }) + .collect::>(); + + let timeout_msg = TimeoutMessage::Get { requests: requests.clone() }; + + set_timestamp(Some(Duration::from_secs(60 * 60 * 60).as_millis() as u64)); + Pallet::::handle_messages(vec![Message::Timeout(timeout_msg)]).unwrap(); + for request in requests { + // commitments should not be found in storage after timeout has been processed + let commitment = hash_request::>(&request); + assert!(host.request_commitment(commitment).is_err()) + } + }) +} + +#[test] +fn should_handle_get_request_responses_correctly() { + let mut ext = new_test_ext(); + ext.execute_with(|| { + let host = Host::::default(); + setup_mock_client::<_, Test>(&host); + host.store_challenge_period(MOCK_CONSENSUS_STATE_ID, 60 * 60).unwrap(); + let requests = (0..2) + .into_iter() + .map(|i| { + let msg = DispatchGet { + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + from: vec![0u8; 32], + gas_limit: 0, + + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: 3, + timeout_timestamp: 1000, + }; + + let dispatcher = Dispatcher::::default(); + dispatcher.dispatch_request(DispatchRequest::Get(msg)).unwrap(); + let get = ismp_rs::router::Get { + source: host.host_state_machine(), + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + nonce: i, + from: vec![0u8; 32], + gas_limit: 0, + keys: vec![vec![1u8; 32], vec![1u8; 32]], + height: 3, + timeout_timestamp: 1000, + }; + ismp_rs::router::Request::Get(get) + }) + .collect::>(); + + set_timestamp(Some(Duration::from_secs(60 * 60 * 60).as_millis() as u64)); + + let response = ResponseMessage::Get { + requests: requests.clone(), + proof: Proof { + height: StateMachineHeight { + id: StateMachineId { + state_id: StateMachine::Ethereum(Ethereum::ExecutionLayer), + consensus_state_id: MOCK_CONSENSUS_STATE_ID, + }, + height: 3, + }, + proof: vec![], + }, + }; + + Pallet::::handle_messages(vec![Message::Response(response)]).unwrap(); + + for request in requests { + assert!(host.response_receipt(&request).is_some()) + } + }) +} diff --git a/parachain/modules/ismp/core/src/weight_info.rs b/parachain/modules/ismp/core/src/weight_info.rs new file mode 100644 index 000000000..95ba18309 --- /dev/null +++ b/parachain/modules/ismp/core/src/weight_info.rs @@ -0,0 +1,334 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Users of ismp should benchmark consensus clients and module callbacks +//! This module provides a guide on how to provide static weights for consensus clients and module +//! callbacks + +use crate::{primitives::ModuleId, Config}; +use alloc::boxed::Box; +use frame_support::weights::Weight; +use ismp_rs::{ + consensus::{ConsensusClientId, StateMachineId}, + messaging::{ + ConsensusMessage, FraudProofMessage, Message, Proof, ResponseMessage, TimeoutMessage, + }, + router::{GetResponse, Post, Request, Response}, +}; + +/// A trait that provides information about how consensus client execute in the runtime +pub trait ConsensusClientWeight { + /// Returns the weight that would be used in processing this consensus message + fn verify_consensus(&self, msg: &ConsensusMessage) -> Weight; + /// Returns the weight that would be used in processing this fraud proof message + fn verify_fraud_proof(&self, msg: &FraudProofMessage) -> Weight; + /// Returns weight used in verifying this membership proof + /// `items` is the number of values being verified + /// The weight should ideally depend on the number of items being verified + fn verify_membership( + &self, + state_machine: StateMachineId, + items: usize, + proof: &Proof, + ) -> Weight; + /// Returns weight used in verifying this state proof + /// `items` is the number of keys being verified + /// The weight should ideally depend on the number of items being verified + fn verify_state_proof( + &self, + state_machine: StateMachineId, + items: usize, + proof: &Proof, + ) -> Weight; +} + +impl ConsensusClientWeight for () { + fn verify_consensus(&self, _msg: &ConsensusMessage) -> Weight { + Weight::zero() + } + + fn verify_fraud_proof(&self, _msg: &FraudProofMessage) -> Weight { + Weight::zero() + } + + fn verify_membership( + &self, + _state_machine: StateMachineId, + _items: usize, + _proof: &Proof, + ) -> Weight { + Weight::zero() + } + + fn verify_state_proof( + &self, + _state_machine: StateMachineId, + _items: usize, + _proof: &Proof, + ) -> Weight { + Weight::zero() + } +} + +/// A trait that provides weight information about how module callbacks execute +pub trait IsmpModuleWeight { + /// Returns the weight used in processing this request + fn on_accept(&self, request: &Post) -> Weight; + /// Returns the weight used in processing this timeout + fn on_timeout(&self, request: &Request) -> Weight; + /// Returns the weight used in processing this response + fn on_response(&self, response: &Response) -> Weight; +} + +impl IsmpModuleWeight for () { + fn on_accept(&self, _request: &Post) -> Weight { + Weight::zero() + } + + fn on_timeout(&self, _request: &Request) -> Weight { + Weight::zero() + } + + fn on_response(&self, _response: &Response) -> Weight { + Weight::zero() + } +} + +/// Provides references to consensus and module weight providers +pub trait WeightProvider { + /// Returns a reference to the weight provider for a consensus client + fn consensus_client(id: ConsensusClientId) -> Option>; + + /// Returns a reference to the weight provider for a module + fn module_callback(dest_module: ModuleId) -> Option>; +} + +impl WeightProvider for () { + fn consensus_client(_id: ConsensusClientId) -> Option> { + None + } + + fn module_callback(_dest_module: ModuleId) -> Option> { + None + } +} + +/// These functions account for storage reads and writes in the ismp message handlers +/// They do not take into account proof verification, that is delegated to the Consensus client +/// weight provider +pub trait WeightInfo { + /// Returns the weight used in finalizing the mmr + fn on_finalize(n: u32) -> Weight; + /// Returns the weight consumed in creating a consensus client + fn create_consensus_client() -> Weight; + /// Returns the weight consumed in setting the unbonding period + fn set_unbonding_period() -> Weight; + /// Returns the weight consumed in handling a request + fn handle_request_message() -> Weight; + /// Returns the weight consumed in handling a response + fn handle_response_message() -> Weight; + /// Returns the weight consumed in handling a timeout + fn handle_timeout_message() -> Weight; + /// Returns the weight consumed in dispatching a post request + fn dispatch_post_request() -> Weight; + /// Returns the weight consumed in dispatching a get request + fn dispatch_get_request() -> Weight; + /// Returns the weight consumed in dispatching a response + fn dispatch_response() -> Weight; +} + +impl WeightInfo for () { + fn on_finalize(_n: u32) -> Weight { + Weight::zero() + } + + fn create_consensus_client() -> Weight { + Weight::zero() + } + + fn set_unbonding_period() -> Weight { + Weight::zero() + } + + fn handle_request_message() -> Weight { + Weight::zero() + } + + fn handle_response_message() -> Weight { + Weight::zero() + } + + fn handle_timeout_message() -> Weight { + Weight::zero() + } + + fn dispatch_post_request() -> Weight { + Weight::zero() + } + + fn dispatch_get_request() -> Weight { + Weight::zero() + } + + fn dispatch_response() -> Weight { + Weight::zero() + } +} + +/// Returns the weight that would be consumed when executing a batch of messages +pub fn get_weight(messages: &[Message]) -> Weight { + messages.into_iter().fold(Weight::zero(), |acc, msg| match msg { + Message::Consensus(msg) => { + let consensus_handler = + ::WeightProvider::consensus_client(msg.consensus_state_id) + .unwrap_or(Box::new(())); + consensus_handler.verify_consensus(msg) + }, + Message::Request(msg) => { + let state_machine = msg.proof.height.id; + let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = ModuleId::from_bytes(req.to.as_slice()).ok(); + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() + .unwrap_or(Box::new(())); + acc + handle.on_accept(&req) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + msg.proof.height.id.consensus_state_id, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_membership(state_machine, msg.requests.len(), &msg.proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_request_message() + }, + Message::Response(msg) => match msg { + ResponseMessage::Post { responses, proof } => { + let state_machine = proof.height.id; + let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { + let dest_module = match res { + Response::Post(ref post) => + ModuleId::from_bytes(post.post.from.as_slice()).ok(), + _ => return acc, + }; + + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() + .unwrap_or(Box::new(())); + acc + handle.on_response(&res) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + proof.height.id.consensus_state_id, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_membership(state_machine, responses.len(), &proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + }, + ResponseMessage::Get { requests, proof } => { + let state_machine = proof.height.id; + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Get(ref get) => ModuleId::from_bytes(get.from.as_slice()).ok(), + _ => return acc, + }; + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() + .unwrap_or(Box::new(())); + acc + handle.on_response(&Response::Get(GetResponse { + get: req.get_request().expect("Infallible"), + values: Default::default(), + })) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + proof.height.id.consensus_state_id, + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = + consensus_handler.verify_state_proof(state_machine, requests.len(), &proof); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + }, + }, + Message::Timeout(msg) => match msg { + TimeoutMessage::Post { requests, timeout_proof } => { + let state_machine = timeout_proof.height.id; + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Post(ref post) => ModuleId::from_bytes(post.from.as_slice()).ok(), + _ => return acc, + }; + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() + .unwrap_or(Box::new(())); + acc + handle.on_timeout(&req) + }); + + let consensus_handler = ::WeightProvider::consensus_client( + timeout_proof.height.id.consensus_state_id, // todo: consensus client id + ) + .unwrap_or(Box::new(())); + + let proof_verification_weight = consensus_handler.verify_state_proof( + state_machine, + requests.len(), + &timeout_proof, + ); + + acc + cb_weight + + proof_verification_weight + + ::WeightInfo::handle_response_message() + }, + TimeoutMessage::Get { requests } => { + let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { + let dest_module = match req { + Request::Get(ref get) => ModuleId::from_bytes(get.from.as_slice()).ok(), + _ => return acc, + }; + let handle = dest_module + .map(|id| ::WeightProvider::module_callback(id)) + .flatten() + .unwrap_or(Box::new(())); + acc + handle.on_timeout(&req) + }); + acc + cb_weight + ::WeightInfo::handle_timeout_message() + }, + }, + + Message::FraudProof(msg) => { + let consensus_handler = + ::WeightProvider::consensus_client(msg.consensus_state_id) + .unwrap_or(Box::new(())); + consensus_handler.verify_fraud_proof(msg) + }, + }) +} diff --git a/parachain/modules/ismp/demo/Cargo.toml b/parachain/modules/ismp/demo/Cargo.toml new file mode 100644 index 000000000..58bb25fc5 --- /dev/null +++ b/parachain/modules/ismp/demo/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "ismp-demo" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +# crates.io +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# polytope labs +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +# substrate +frame-support = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +frame-system = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-core = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } + +# local +pallet-ismp = { path = "../core", default-features = false } + +[dev-dependencies] + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "scale-info/std", + "ismp/std", + "pallet-ismp/std", + "pallet-balances/std" +] diff --git a/parachain/modules/ismp/demo/src/lib.rs b/parachain/modules/ismp/demo/src/lib.rs new file mode 100644 index 000000000..2a994957d --- /dev/null +++ b/parachain/modules/ismp/demo/src/lib.rs @@ -0,0 +1,384 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ISMP Assets +//! Simple Demo for Asset transfer over ISMP +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] + +extern crate alloc; + +use alloc::{ + format, + string::{String, ToString}, +}; +use frame_support::{traits::fungible::Mutate, PalletId}; +use ismp::{ + error::Error as IsmpError, + host::StateMachine, + module::IsmpModule, + router::{Post, Request, Response}, +}; +pub use pallet::*; +use pallet_ismp::primitives::ModuleId; +use sp_core::H160; + +/// Constant Pallet ID +pub const PALLET_ID: ModuleId = ModuleId::Pallet(PalletId(*b"ismp-ast")); + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use alloc::{vec, vec::Vec}; + use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{Inspect, Mutate}, + tokens::{Balance, Fortitude, Precision}, + }, + }; + use frame_system::pallet_prelude::*; + use ismp::{ + host::{Ethereum, StateMachine}, + router::{DispatchGet, DispatchPost, DispatchRequest, IsmpDispatcher}, + }; + + #[pallet::pallet] + pub struct Pallet(_); + + /// Pallet Configuration + #[pallet::config] + pub trait Config: frame_system::Config + pallet_balances::Config + pallet_ismp::Config { + /// Overarching event + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Native balance + type Balance: Balance + Into<>::Balance>; + /// Native currency implementation + type NativeCurrency: Mutate; + /// Ismp message disptacher + type IsmpDispatcher: IsmpDispatcher + Default; + } + + /// Pallet events + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Some balance has been transferred + BalanceTransferred { + /// Source account + from: T::AccountId, + /// Destination account + to: T::AccountId, + /// Amount being transferred + amount: ::Balance, + /// Destination chain's Id + dest_chain: StateMachine, + }, + /// Some balance has been received + BalanceReceived { + /// Source account + from: T::AccountId, + /// Receiving account + to: T::AccountId, + /// Amount that was received + amount: ::Balance, + /// Source chain's Id + source_chain: StateMachine, + }, + + /// Request data receieved + Request { + /// Source of the request + source: StateMachine, + /// utf-8 decoded data + data: String, + }, + + /// Get response recieved + GetResponse(Vec>>), + } + + /// Pallet Errors + #[pallet::error] + pub enum Error { + /// Error encountered when initializing transfer + TransferFailed, + /// Failed to dispatch get request + GetDispatchFailed, + } + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { + /// Transfer some funds over ISMP + #[pallet::weight(Weight::from_parts(1_000_000, 0))] + #[pallet::call_index(0)] + pub fn transfer( + origin: OriginFor, + params: TransferParams::Balance>, + ) -> DispatchResult { + let origin = ensure_signed(origin)?; + + // first, burn the requested amount + >::burn_from( + &origin, + params.amount.into(), + Precision::Exact, + Fortitude::Force, + )?; + + // next, construct the request to be sent out + let payload = Payload { to: params.to, from: origin.clone(), amount: params.amount }; + let dest = match T::StateMachine::get() { + StateMachine::Kusama(_) => StateMachine::Kusama(params.para_id), + StateMachine::Polkadot(_) => StateMachine::Polkadot(params.para_id), + _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, + }; + let post = DispatchPost { + dest, + from: PALLET_ID.to_bytes(), + to: PALLET_ID.to_bytes(), + timeout_timestamp: params.timeout, + data: payload.encode(), + gas_limit: 0, + }; + + // dispatch the request + let dispatcher = T::IsmpDispatcher::default(); + dispatcher + .dispatch_request(DispatchRequest::Post(post)) + .map_err(|_| Error::::TransferFailed)?; + + // let the user know, they've successfully sent the funds + Self::deposit_event(Event::::BalanceTransferred { + from: payload.from, + to: payload.to, + amount: payload.amount, + dest_chain: dest, + }); + + Ok(()) + } + + /// Get the total issuance of the native token in a counterparty + /// parachain + #[pallet::weight(Weight::from_parts(1_000_000, 0))] + #[pallet::call_index(1)] + pub fn get_request(origin: OriginFor, params: GetRequest) -> DispatchResult { + ensure_signed(origin)?; + let dest = match T::StateMachine::get() { + StateMachine::Kusama(_) => StateMachine::Kusama(params.para_id), + StateMachine::Polkadot(_) => StateMachine::Polkadot(params.para_id), + _ => Err(DispatchError::Other("Pallet only supports parachain hosts"))?, + }; + + let get = DispatchGet { + dest, + from: PALLET_ID.to_bytes(), + keys: params.keys, + height: params.height as u64, + timeout_timestamp: params.timeout, + gas_limit: 0, + }; + + let dispatcher = T::IsmpDispatcher::default(); + dispatcher + .dispatch_request(DispatchRequest::Get(get)) + .map_err(|_| Error::::GetDispatchFailed)?; + Ok(()) + } + + /// Dispatch request to a connected EVM chain. + #[pallet::weight(Weight::from_parts(1_000_000, 0))] + #[pallet::call_index(2)] + pub fn dispatch_to_evm(origin: OriginFor, params: EvmParams) -> DispatchResult { + ensure_signed(origin)?; + let post = DispatchPost { + dest: StateMachine::Ethereum(params.destination), + from: PALLET_ID.to_bytes(), + to: params.module.0.to_vec(), + timeout_timestamp: params.timeout, + data: b"Hello from polkadot".to_vec(), + gas_limit: 10_000_000, + }; + let dispatcher = T::IsmpDispatcher::default(); + for _ in 0..params.count { + // dispatch the request + dispatcher + .dispatch_request(DispatchRequest::Post(post.clone())) + .map_err(|_| Error::::TransferFailed)?; + } + Ok(()) + } + } + + /// Transfer payload + /// This would be encoded to bytes as the request data + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct Payload { + /// Destination account + pub to: AccountId, + /// Source account + pub from: AccountId, + /// Amount to be transferred + pub amount: Balance, + } + + /// The get request payload + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct GetRequest { + /// Destination parachain + pub para_id: u32, + /// Height at which to read state + pub height: u32, + /// request timeout + pub timeout: u64, + /// Storage keys to read + pub keys: Vec>, + } + + /// Extrinsic Parameters for initializing a cross chain transfer + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct TransferParams { + /// Destination account + pub to: AccountId, + + /// Amount to transfer + pub amount: Balance, + + /// Destination parachain Id + pub para_id: u32, + + /// Timeout timestamp on destination chain in seconds + pub timeout: u64, + } + + /// Extrisnic params for evm dispatch + #[derive( + Clone, codec::Encode, codec::Decode, scale_info::TypeInfo, PartialEq, Eq, RuntimeDebug, + )] + pub struct EvmParams { + /// Destination module + pub module: H160, + + /// Destination EVM host + pub destination: Ethereum, + + /// Timeout timestamp on destination chain in seconds + pub timeout: u64, + + /// Request count + pub count: u64, + } +} + +/// Module callback for the pallet +pub struct IsmpModuleCallback(core::marker::PhantomData); + +impl Default for IsmpModuleCallback { + fn default() -> Self { + Self(core::marker::PhantomData) + } +} + +impl IsmpModule for IsmpModuleCallback { + fn on_accept(&self, request: Post) -> Result<(), IsmpError> { + let source_chain = request.source; + + match source_chain { + StateMachine::Ethereum(_) => Pallet::::deposit_event(Event::Request { + source: source_chain, + data: unsafe { String::from_utf8_unchecked(request.data) }, + }), + StateMachine::Polkadot(_) | StateMachine::Kusama(_) => { + let payload = + ::Balance> as codec::Decode>::decode( + &mut &*request.data, + ) + .map_err(|_| { + IsmpError::ImplementationSpecific( + "Failed to decode request data".to_string(), + ) + })?; + >::mint_into( + &payload.to, + payload.amount.into(), + ) + .map_err(|_| { + IsmpError::ImplementationSpecific("Failed to mint funds".to_string()) + })?; + Pallet::::deposit_event(Event::::BalanceReceived { + from: payload.from, + to: payload.to, + amount: payload.amount, + source_chain, + }); + }, + source => + Err(IsmpError::ImplementationSpecific(format!("Unsupported source {source:?}")))?, + } + + Ok(()) + } + + fn on_response(&self, response: Response) -> Result<(), IsmpError> { + match response { + Response::Post(_) => Err(IsmpError::ImplementationSpecific( + "Balance transfer protocol does not accept post responses".to_string(), + ))?, + Response::Get(res) => Pallet::::deposit_event(Event::::GetResponse( + res.values.into_values().collect(), + )), + }; + + Ok(()) + } + + fn on_timeout(&self, request: Request) -> Result<(), IsmpError> { + let source_chain = request.source_chain(); + let data = match request { + Request::Post(post) => post.data, + _ => Err(IsmpError::ImplementationSpecific( + "Only Post requests allowed, found Get".to_string(), + ))?, + }; + let payload = + ::Balance> as codec::Decode>::decode(&mut &*data) + .map_err(|_| { + IsmpError::ImplementationSpecific("Failed to decode request data".to_string()) + })?; + >::mint_into( + &payload.from, + payload.amount.into(), + ) + .map_err(|_| IsmpError::ImplementationSpecific("Failed to mint funds".to_string()))?; + Pallet::::deposit_event(Event::::BalanceReceived { + from: payload.from, + to: payload.to, + amount: payload.amount, + source_chain, + }); + Ok(()) + } +} diff --git a/parachain/modules/consensus/parachain/Cargo.toml b/parachain/modules/ismp/parachain/Cargo.toml similarity index 100% rename from parachain/modules/consensus/parachain/Cargo.toml rename to parachain/modules/ismp/parachain/Cargo.toml diff --git a/parachain/modules/consensus/parachain/inherent/Cargo.toml b/parachain/modules/ismp/parachain/inherent/Cargo.toml similarity index 100% rename from parachain/modules/consensus/parachain/inherent/Cargo.toml rename to parachain/modules/ismp/parachain/inherent/Cargo.toml diff --git a/parachain/modules/consensus/parachain/inherent/src/lib.rs b/parachain/modules/ismp/parachain/inherent/src/lib.rs similarity index 100% rename from parachain/modules/consensus/parachain/inherent/src/lib.rs rename to parachain/modules/ismp/parachain/inherent/src/lib.rs diff --git a/parachain/modules/consensus/parachain/runtime-api/Cargo.toml b/parachain/modules/ismp/parachain/runtime-api/Cargo.toml similarity index 100% rename from parachain/modules/consensus/parachain/runtime-api/Cargo.toml rename to parachain/modules/ismp/parachain/runtime-api/Cargo.toml diff --git a/parachain/modules/consensus/parachain/runtime-api/src/lib.rs b/parachain/modules/ismp/parachain/runtime-api/src/lib.rs similarity index 100% rename from parachain/modules/consensus/parachain/runtime-api/src/lib.rs rename to parachain/modules/ismp/parachain/runtime-api/src/lib.rs diff --git a/parachain/modules/consensus/parachain/src/consensus.rs b/parachain/modules/ismp/parachain/src/consensus.rs similarity index 100% rename from parachain/modules/consensus/parachain/src/consensus.rs rename to parachain/modules/ismp/parachain/src/consensus.rs diff --git a/parachain/modules/consensus/parachain/src/lib.rs b/parachain/modules/ismp/parachain/src/lib.rs similarity index 100% rename from parachain/modules/consensus/parachain/src/lib.rs rename to parachain/modules/ismp/parachain/src/lib.rs diff --git a/parachain/modules/ismp/primitives/Cargo.toml b/parachain/modules/ismp/primitives/Cargo.toml new file mode 100644 index 000000000..08d676949 --- /dev/null +++ b/parachain/modules/ismp/primitives/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "ismp-primitives" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } + +# polytope labs +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +# crates.io +merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +primitive-types = { version = "0.12.1", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-core = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-system/std", + "ismp/std", + "merkle-mountain-range/std", + "codec/std", + "sp-runtime/std", + "primitive-types/std", + "scale-info/std", + "serde", + "frame-support/std", + "sp-core/std", + "sp-consensus-aura/std", + "sp-io/std" +] diff --git a/parachain/modules/ismp/primitives/src/lib.rs b/parachain/modules/ismp/primitives/src/lib.rs new file mode 100644 index 000000000..83e3eaac9 --- /dev/null +++ b/parachain/modules/ismp/primitives/src/lib.rs @@ -0,0 +1,113 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The primitive types used by pallet-ismp + +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] + +//! Primitives for the MMR implementation + +extern crate alloc; + +use alloc::{format, vec::Vec}; +use codec::{Decode, Encode}; +use core::{fmt::Debug, time::Duration}; +use ismp::{error::Error, host::StateMachine}; +use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; +use sp_core::H256; +use sp_runtime::{Digest, DigestItem}; + +pub mod mmr; + +/// The `ConsensusEngineId` of ISMP digest in the parachain header. +pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; + +/// Queries a request leaf in the mmr +#[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub struct LeafIndexQuery { + /// The source of the request + pub source_chain: StateMachine, + /// the request destination + pub dest_chain: StateMachine, + /// The request nonce + pub nonce: u64, +} + +/// Hashing algorithm for the state proof +#[derive(Debug, Encode, Decode, Clone)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub enum HashAlgorithm { + /// For chains that use keccak as their hashing algo + Keccak, + /// For chains that use blake2 as their hashing algo + Blake2, +} + +/// Holds the relevant data needed for state proof verification +#[derive(Debug, Encode, Decode, Clone)] +pub struct SubstrateStateProof { + /// Algorithm to use for state proof verification + pub hasher: HashAlgorithm, + /// Storage proof for the parachain headers + pub storage_proof: Vec>, +} + +/// Holds the relevant data needed for request/response proof verification +#[derive(Debug, Encode, Decode, Clone)] +pub struct MembershipProof { + /// Size of the mmr at the time this proof was generated + pub mmr_size: u64, + /// Leaf indices for the proof + pub leaf_indices: Vec, + /// Mmr proof + pub proof: Vec, +} + +/// Fetches the overlay(ismp) root and timestamp from the header digest +pub fn fetch_overlay_root_and_timestamp( + digest: &Digest, + slot_duration: u64, +) -> Result<(u64, H256), Error> { + let (mut timestamp, mut overlay_root) = (0, H256::default()); + + for digest in digest.logs.iter() { + match digest { + DigestItem::PreRuntime(consensus_engine_id, value) + if *consensus_engine_id == AURA_ENGINE_ID => + { + let slot = Slot::decode(&mut &value[..]) + .map_err(|e| Error::ImplementationSpecific(format!("Cannot slot: {e:?}")))?; + timestamp = Duration::from_millis(*slot * slot_duration).as_secs(); + }, + DigestItem::Consensus(consensus_engine_id, value) + if *consensus_engine_id == ISMP_ID => + { + if value.len() != 32 { + Err(Error::ImplementationSpecific( + "Header contains an invalid ismp root".into(), + ))? + } + + overlay_root = H256::from_slice(&value); + }, + // don't really care about the rest + _ => {}, + }; + } + + Ok((timestamp, overlay_root)) +} diff --git a/parachain/modules/ismp/primitives/src/mmr.rs b/parachain/modules/ismp/primitives/src/mmr.rs new file mode 100644 index 000000000..f68609bb6 --- /dev/null +++ b/parachain/modules/ismp/primitives/src/mmr.rs @@ -0,0 +1,106 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains code adapted from https://github.com/paritytech/substrate/blob/master/frame/merkle-mountain-range/src/mmr/mod.rs + +//! MMR utilities + +use core::fmt::Formatter; + +use codec::{Decode, Encode}; +//use frame_support::sp_io; +use ismp::{ + router::{Request, Response}, + util::{hash_request, hash_response, Keccak256}, +}; +use primitive_types::H256; + +/// Index of a leaf in the MMR +pub type LeafIndex = u64; +/// Index of a node in the MMR +pub type NodeIndex = u64; + +/// A concrete Leaf for the MMR +#[derive(Debug, Clone, Decode, Encode, PartialEq, Eq, scale_info::TypeInfo)] +pub enum Leaf { + /// A request variant + Request(Request), + /// A response variant + Response(Response), +} + +impl Leaf { + /// Returns the hash of a leaf + fn hash(&self) -> H256 { + match self { + Leaf::Request(req) => hash_request::(req), + Leaf::Response(res) => hash_response::(res), + } + } +} + +/// An element representing either full data or its hash. +#[derive(Clone, PartialEq, Eq, Encode, Decode, scale_info::TypeInfo)] +pub enum DataOrHash { + /// Arbitrary data in its full form. + Data(Leaf), + /// A hash of some data. + Hash(H256), +} + +impl core::fmt::Debug for DataOrHash { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + DataOrHash::Data(leaf) => f.debug_struct("DataOrHash").field("Data", leaf).finish(), + DataOrHash::Hash(hash) => f.debug_struct("DataOrHash").field("Hash", hash).finish(), + } + } +} + +impl From for DataOrHash { + fn from(l: Leaf) -> Self { + Self::Data(l) + } +} + +impl DataOrHash { + /// Retrieve a hash of this item. + /// + /// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash] + /// node, or a hash of SCALE-encoded [DataOrHash::Data] data. + pub fn hash(&self) -> H256 { + match *self { + Self::Data(ref leaf) => leaf.hash::(), + Self::Hash(ref hash) => *hash, + } + } +} + +/// Default Merging & Hashing behavior for MMR. +pub struct MmrHasher(core::marker::PhantomData); + +impl merkle_mountain_range::Merge for MmrHasher +where + H: Keccak256, +{ + type Item = DataOrHash; + + fn merge(left: &Self::Item, right: &Self::Item) -> merkle_mountain_range::Result { + let mut concat = left.hash::().as_ref().to_vec(); + concat.extend_from_slice(right.hash::().as_ref()); + + Ok(DataOrHash::Hash(sp_io::hashing::keccak_256(&concat).into())) + } +} diff --git a/parachain/modules/ismp/rpc/Cargo.toml b/parachain/modules/ismp/rpc/Cargo.toml new file mode 100644 index 000000000..ea44a1f42 --- /dev/null +++ b/parachain/modules/ismp/rpc/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "ismp-rpc" +description = "RPC apis for pallet-ismp" +edition = "2021" +version = "0.1.0" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +hex-literal = { version = "0.3.3" } +jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.45" +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main" } +pallet-ismp = { path = "../core" } +ismp-runtime-api = { path = "../runtime-api" } +ismp-primitives = { path = "../primitives" } + +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } diff --git a/parachain/modules/ismp/rpc/src/lib.rs b/parachain/modules/ismp/rpc/src/lib.rs new file mode 100644 index 000000000..696d101d7 --- /dev/null +++ b/parachain/modules/ismp/rpc/src/lib.rs @@ -0,0 +1,404 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![deny(missing_docs)] + +//! RPC Implementation for the Interoperable State Machine Protocol + +use jsonrpsee::{ + core::{Error as RpcError, RpcResult as Result}, + proc_macros::rpc, + types::{error::CallError, ErrorObject}, +}; + +use codec::Encode; +use ismp_primitives::{ + mmr::{Leaf, LeafIndex}, + LeafIndexQuery, +}; +use ismp_rs::{ + consensus::{ConsensusClientId, StateMachineId}, + events::{ChallengePeriodStarted, Event, StateMachineUpdated}, + router::{Get, Request, Response}, +}; +use ismp_runtime_api::IsmpRuntimeApi; +use sc_client_api::{BlockBackend, ProofProvider}; +use serde::{Deserialize, Serialize}; +use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_blockchain::HeaderBackend; +use sp_core::offchain::{storage::OffchainDb, OffchainDbExt, OffchainStorage}; +use sp_runtime::traits::Block as BlockT; +use std::{collections::HashMap, fmt::Display, sync::Arc}; + +/// A type that could be a block number or a block hash +#[derive(Clone, Hash, Debug, PartialEq, Eq, Copy, Serialize, Deserialize)] +#[serde(untagged)] +pub enum BlockNumberOrHash { + /// Block hash + Hash(Hash), + /// Block number + Number(u32), +} + +impl Display for BlockNumberOrHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BlockNumberOrHash::Hash(hash) => write!(f, "{:?}", hash), + BlockNumberOrHash::Number(block_num) => write!(f, "{}", block_num), + } + } +} + +/// Contains a scale encoded Mmr Proof or Trie proof +#[derive(Serialize, Deserialize)] +pub struct Proof { + /// Scale encoded `pallet_ismp::primitives::Proof` or state trie proof `Vec>` + pub proof: Vec, + /// Optional scale encoded `Vec` for mmr proof + pub leaves: Option>, + /// Height at which proof was recovered + pub height: u32, +} + +/// Converts a runtime trap into an RPC error. +fn runtime_error_into_rpc_error(e: impl std::fmt::Display) -> RpcError { + RpcError::Call(CallError::Custom(ErrorObject::owned( + 9876, // no real reason for this value + "Something wrong", + Some(format!("{}", e)), + ))) +} + +/// ISMP RPC methods. +#[rpc(client, server)] +pub trait IsmpApi +where + Hash: PartialEq + Eq + std::hash::Hash, +{ + /// Query full request data from the ismp pallet + #[method(name = "ismp_queryRequests")] + fn query_requests(&self, query: Vec) -> Result>; + + /// Query full response data from the ismp pallet + #[method(name = "ismp_queryResponses")] + fn query_responses(&self, query: Vec) -> Result>; + + /// Query mmr proof for some requests + #[method(name = "ismp_queryRequestsMmrProof")] + fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result; + + /// Query mmr proof for some responses + #[method(name = "ismp_queryResponsesMmrProof")] + fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result; + + /// Query membership or non-membership proof for some keys + #[method(name = "ismp_queryStateProof")] + fn query_state_proof(&self, height: u32, keys: Vec>) -> Result; + + /// Query scale encoded consensus state + #[method(name = "ismp_queryConsensusState")] + fn query_consensus_state( + &self, + height: Option, + client_id: ConsensusClientId, + ) -> Result>; + + /// Query timestamp of when this client was last updated in seconds + #[method(name = "ismp_queryConsensusUpdateTime")] + fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result; + + /// Query the challenge period for client + #[method(name = "ismp_queryChallengePeriod")] + fn query_challenge_period(&self, client_id: ConsensusClientId) -> Result; + + /// Query the latest height for a state machine + #[method(name = "ismp_queryStateMachineLatestHeight")] + fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result; + + /// Query the most recent height at which we've processed requests for a state machine + #[method(name = "ismp_queryLatestMessagingHeight")] + fn query_latest_messaging_height(&self, id: StateMachineId) -> Result; + + /// Query ISMP Events that were deposited in a series of blocks + /// Using String keys because HashMap fails to deserialize when key is not a String + #[method(name = "ismp_queryEvents")] + fn query_events( + &self, + block_numbers: Vec>, + ) -> Result>>; + + /// Query pending get requests that have a `state_machine_height` <= `height`. + #[method(name = "ismp_pendingGetRequests")] + fn pending_get_requests(&self, height: u64) -> Result>; +} + +/// An implementation of ISMP specific RPC methods. +pub struct IsmpRpcHandler { + client: Arc, + offchain_db: OffchainDb, + _marker: std::marker::PhantomData, +} + +impl IsmpRpcHandler { + /// Create new `IsmpRpcHandler` with the given reference to the client. + pub fn new(client: Arc, offchain_storage: S) -> Self { + Self { client, offchain_db: OffchainDb::new(offchain_storage), _marker: Default::default() } + } +} + +impl IsmpApiServer for IsmpRpcHandler +where + Block: BlockT, + S: OffchainStorage + Clone + Send + Sync + 'static, + C: Send + + Sync + + 'static + + ProvideRuntimeApi + + HeaderBackend + + ProofProvider + + BlockBackend, + C::Api: IsmpRuntimeApi, +{ + fn query_requests(&self, query: Vec) -> Result> { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); + let at = self.client.info().best_hash; + let request_indices: Vec = + api.get_request_leaf_indices(at, query).map_err(|e| { + runtime_error_into_rpc_error(format!( + "Error fetching request leaf indices, {:?}", + e + )) + })?; + + api.get_requests(at, request_indices) + .map_err(|_| runtime_error_into_rpc_error("Error fetching requests")) + } + + fn query_responses(&self, query: Vec) -> Result> { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); + let at = self.client.info().best_hash; + let response_indices: Vec = api + .get_response_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + + api.get_responses(at, response_indices) + .map_err(|_| runtime_error_into_rpc_error("Error fetching responses")) + } + + fn query_requests_mmr_proof(&self, height: u32, query: Vec) -> Result { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); + let at = self + .client + .block_hash(height.into()) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; + let request_indices: Vec = api + .get_request_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + + let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api + .generate_proof(at, request_indices) + .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? + .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; + Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) + } + + fn query_responses_mmr_proof(&self, height: u32, query: Vec) -> Result { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); + let at = self + .client + .block_hash(height.into()) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("invalid block height provided"))?; + let response_indices: Vec = api + .get_response_leaf_indices(at, query) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response leaf indices"))?; + + let (leaves, proof): (Vec, pallet_ismp::primitives::Proof) = api + .generate_proof(at, response_indices) + .map_err(|_| runtime_error_into_rpc_error("Error calling runtime api"))? + .map_err(|_| runtime_error_into_rpc_error("Error generating mmr proof"))?; + Ok(Proof { proof: proof.encode(), leaves: Some(leaves.encode()), height }) + } + + fn query_state_proof(&self, height: u32, keys: Vec>) -> Result { + let at = self.client.block_hash(height.into()).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Could not find valid blockhash for provided height") + })?; + let proof: Vec<_> = self + .client + .read_proof(at, &mut keys.iter().map(|key| key.as_slice())) + .map(|proof| proof.into_iter_nodes().collect()) + .map_err(|_| runtime_error_into_rpc_error("Error reading state proof"))?; + Ok(Proof { proof: proof.encode(), leaves: None, height }) + } + + fn query_consensus_state( + &self, + height: Option, + client_id: ConsensusClientId, + ) -> Result> { + let api = self.client.runtime_api(); + let at = height + .and_then(|height| self.client.block_hash(height.into()).ok().flatten()) + .unwrap_or(self.client.info().best_hash); + api.consensus_state(at, client_id) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus state")) + } + + fn query_consensus_update_time(&self, client_id: ConsensusClientId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.consensus_update_time(at, client_id) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Consensus update time")) + } + + fn query_challenge_period(&self, client_id: ConsensusClientId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.challenge_period(at, client_id) + .ok() + .flatten() + .ok_or_else(|| runtime_error_into_rpc_error("Error fetching Challenge period")) + } + + fn query_state_machine_latest_height(&self, id: StateMachineId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.latest_state_machine_height(at, id).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching latest state machine height") + }) + } + + fn pending_get_requests(&self, height: u64) -> Result> { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); + let at = self.client.info().best_hash; + + api.pending_get_requests(at) + .map(|reqs| reqs.into_iter().filter(|req| req.height <= height).collect()) + .map_err(|_| runtime_error_into_rpc_error("Error fetching get requests")) + } + + fn query_events( + &self, + block_numbers: Vec>, + ) -> Result>> { + let mut events = HashMap::new(); + for block_number_or_hash in block_numbers { + let mut api = self.client.runtime_api(); + api.register_extension(OffchainDbExt::new(self.offchain_db.clone())); + let at = match block_number_or_hash { + BlockNumberOrHash::Hash(block_hash) => block_hash, + BlockNumberOrHash::Number(block_number) => + self.client.block_hash(block_number.into()).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Invalid block number provided") + })?, + }; + + let mut request_indices = vec![]; + let mut response_indices = vec![]; + let mut temp: Vec = api + .block_events(at) + .map_err(|e| { + runtime_error_into_rpc_error(format!("failed to read block events {:?}", e)) + })? + .into_iter() + .filter_map(|event| match event { + pallet_ismp::events::Event::Request { + source_chain, + dest_chain, + request_nonce, + } => { + let query = + LeafIndexQuery { source_chain, dest_chain, nonce: request_nonce }; + let indices: Vec = + api.get_request_leaf_indices(at, vec![query]).ok()?; + request_indices.extend_from_slice(&indices); + None + }, + pallet_ismp::events::Event::Response { + source_chain, + dest_chain, + request_nonce, + } => { + let query = + LeafIndexQuery { source_chain, dest_chain, nonce: request_nonce }; + let indices: Vec = + api.get_response_leaf_indices(at, vec![query]).ok()?; + response_indices.extend_from_slice(&indices); + None + }, + pallet_ismp::events::Event::ChallengePeriodStarted { + consensus_state_id, + state_machines, + } => Some(Event::ChallengePeriodStarted(ChallengePeriodStarted { + consensus_state_id, + state_machines, + })), + pallet_ismp::events::Event::StateMachineUpdated { + state_machine_id, + latest_height, + } => Some(Event::StateMachineUpdated(StateMachineUpdated { + state_machine_id, + latest_height, + })), + }) + .collect(); + + let request_events = api + .get_requests(at, request_indices) + .map_err(|_| runtime_error_into_rpc_error("Error fetching requests"))? + .into_iter() + .map(|req| match req { + Request::Post(post) => Event::PostRequest(post), + Request::Get(get) => Event::GetRequest(get), + }); + + let response_events = api + .get_responses(at, response_indices) + .map_err(|_| runtime_error_into_rpc_error("Error fetching response"))? + .into_iter() + .filter_map(|res| match res { + Response::Post(post) => Some(Event::PostResponse(post)), + _ => None, + }); + + temp.extend(request_events); + temp.extend(response_events); + + events.insert(block_number_or_hash.to_string(), temp); + } + Ok(events) + } + + fn query_latest_messaging_height(&self, id: StateMachineId) -> Result { + let api = self.client.runtime_api(); + let at = self.client.info().best_hash; + api.latest_messaging_height(at, id).ok().flatten().ok_or_else(|| { + runtime_error_into_rpc_error("Error fetching latest state machine height") + }) + } +} diff --git a/parachain/modules/ismp/runtime-api/Cargo.toml b/parachain/modules/ismp/runtime-api/Cargo.toml new file mode 100644 index 000000000..46acc061d --- /dev/null +++ b/parachain/modules/ismp/runtime-api/Cargo.toml @@ -0,0 +1,26 @@ +[package] +edition = "2021" +name = "ismp-runtime-api" +version = "0.1.0" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-ismp = { path = "../core", default-features = false } +ismp-primitives = { path = "../primitives", default-features = false } +ismp-rs = { package = "ismp", git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } + +[dependencies.codec] +package = "parity-scale-codec" +version = "3.0.0" +features = ["derive"] +default-features = false + +[features] +default = ['std'] +std = ['sp-api/std', 'sp-std/std', 'codec/std', "pallet-ismp/std", "ismp-rs/std", "serde", "ismp-primitives/std"] diff --git a/parachain/modules/ismp/runtime-api/src/lib.rs b/parachain/modules/ismp/runtime-api/src/lib.rs new file mode 100644 index 000000000..7384add94 --- /dev/null +++ b/parachain/modules/ismp/runtime-api/src/lib.rs @@ -0,0 +1,81 @@ +// Copyright (C) 2023 Polytope Labs. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//! Pallet-ismp runtime Apis + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::too_many_arguments)] +#![deny(missing_docs)] + +use ismp_rs::{ + consensus::{ConsensusClientId, StateMachineId}, + router::{Get, Request, Response}, +}; +use pallet_ismp::primitives::{Error, Proof}; + +use ismp_primitives::{ + mmr::{Leaf, LeafIndex}, + LeafIndexQuery, +}; +#[cfg(not(feature = "std"))] +use sp_std::vec::Vec; + +sp_api::decl_runtime_apis! { + /// ISMP Runtime Apis + pub trait IsmpRuntimeApi { + /// Return the number of MMR leaves. + fn mmr_leaf_count() -> Result; + + /// Return the on-chain MMR root hash. + fn mmr_root() -> Result; + + /// Generate a proof for the provided leaf indices + fn generate_proof( + leaf_indices: Vec + ) -> Result<(Vec, Proof), Error>; + + /// Fetch all ISMP events + fn block_events() -> Vec; + + /// Return the scale encoded consensus state + fn consensus_state(id: ConsensusClientId) -> Option>; + + /// Return the timestamp this client was last updated in seconds + fn consensus_update_time(id: ConsensusClientId) -> Option; + + /// Return the challenge period timestamp + fn challenge_period(id: ConsensusClientId) -> Option; + + /// Return the latest height of the state machine + fn latest_state_machine_height(id: StateMachineId) -> Option; + + /// Return the most recent height we've processed requests for a state machine + fn latest_messaging_height(id: StateMachineId) -> Option; + + /// Get Request Leaf Indices + fn get_request_leaf_indices(leaf_queries: Vec) -> Vec; + + /// Get Response Leaf Indices + fn get_response_leaf_indices(leaf_queries: Vec) -> Vec; + + /// Get actual requests + fn get_requests(leaf_indices: Vec) -> Vec; + + /// Fetch all Get requests that have received no response + fn pending_get_requests() -> Vec; + + /// Get actual requests + fn get_responses(leaf_indices: Vec) -> Vec; + } +} diff --git a/parachain/modules/consensus/sync-committee/Cargo.toml b/parachain/modules/ismp/sync-committee/Cargo.toml similarity index 87% rename from parachain/modules/consensus/sync-committee/Cargo.toml rename to parachain/modules/ismp/sync-committee/Cargo.toml index 5455b4c78..8a1512e74 100644 --- a/parachain/modules/consensus/sync-committee/Cargo.toml +++ b/parachain/modules/ismp/sync-committee/Cargo.toml @@ -8,10 +8,10 @@ authors = ["Polytope Labs "] [dependencies] # polytope labs ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch="main", default-features = false } -sync-committee-primitives = { git="https://github.com/polytope-labs/sync-committee-rs", branch="main", default-features = false } -sync-committee-verifier = { git="https://github.com/polytope-labs/sync-committee-rs", branch="main", default-features = false } +sync-committee-primitives = { path = "../../consensus/sync-committee/primitives", default-features = false } +sync-committee-verifier = { path = "../../consensus/sync-committee/verifier", default-features = false } patricia-merkle-trie = { package = "ethereum-trie", git ="https://github.com/polytope-labs/ethereum-trie", branch = "main", default-features = false } -pallet-ismp = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main", default-features = false } +pallet-ismp = { path = "../core", default-features = false } # crates.io trie-db = { version= "0.24.0", default-features = false } diff --git a/parachain/modules/ismp/sync-committee/README.md b/parachain/modules/ismp/sync-committee/README.md new file mode 100644 index 000000000..4ac529c2f --- /dev/null +++ b/parachain/modules/ismp/sync-committee/README.md @@ -0,0 +1,2 @@ +# ismp-sync-committee +Sync-committee consensus client implementation for ismp diff --git a/parachain/modules/consensus/sync-committee/src/arbitrum.rs b/parachain/modules/ismp/sync-committee/src/arbitrum.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/arbitrum.rs rename to parachain/modules/ismp/sync-committee/src/arbitrum.rs diff --git a/parachain/modules/consensus/sync-committee/src/beacon_client.rs b/parachain/modules/ismp/sync-committee/src/beacon_client.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/beacon_client.rs rename to parachain/modules/ismp/sync-committee/src/beacon_client.rs diff --git a/parachain/modules/consensus/sync-committee/src/lib.rs b/parachain/modules/ismp/sync-committee/src/lib.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/lib.rs rename to parachain/modules/ismp/sync-committee/src/lib.rs diff --git a/parachain/modules/consensus/sync-committee/src/optimism.rs b/parachain/modules/ismp/sync-committee/src/optimism.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/optimism.rs rename to parachain/modules/ismp/sync-committee/src/optimism.rs diff --git a/parachain/modules/consensus/sync-committee/src/pallet.rs b/parachain/modules/ismp/sync-committee/src/pallet.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/pallet.rs rename to parachain/modules/ismp/sync-committee/src/pallet.rs diff --git a/parachain/modules/consensus/sync-committee/src/presets.rs b/parachain/modules/ismp/sync-committee/src/presets.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/presets.rs rename to parachain/modules/ismp/sync-committee/src/presets.rs diff --git a/parachain/modules/consensus/sync-committee/src/tests.rs b/parachain/modules/ismp/sync-committee/src/tests.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/tests.rs rename to parachain/modules/ismp/sync-committee/src/tests.rs diff --git a/parachain/modules/consensus/sync-committee/src/types.rs b/parachain/modules/ismp/sync-committee/src/types.rs similarity index 90% rename from parachain/modules/consensus/sync-committee/src/types.rs rename to parachain/modules/ismp/sync-committee/src/types.rs index 775b168bd..dcbc20c3d 100644 --- a/parachain/modules/consensus/sync-committee/src/types.rs +++ b/parachain/modules/ismp/sync-committee/src/types.rs @@ -6,7 +6,7 @@ use ethabi::ethereum_types::{H160, H256}; use hash256_std_hasher::Hash256StdHasher; use hash_db::Hasher; use ismp::host::{IsmpHost, StateMachine}; -use sync_committee_primitives::types::{LightClientState, LightClientUpdate}; +use sync_committee_primitives::types::{VerifierState, VerifierStateUpdate}; pub struct KeccakHasher(core::marker::PhantomData); @@ -23,7 +23,7 @@ impl Hasher for KeccakHasher { #[derive(Debug, Encode, Decode, Clone)] pub struct ConsensusState { pub frozen_height: Option, - pub light_client_state: LightClientState, + pub light_client_state: VerifierState, pub ismp_contract_addresses: BTreeMap, pub l2_oracle_address: BTreeMap, pub rollup_core_address: H160, @@ -31,7 +31,7 @@ pub struct ConsensusState { #[derive(Encode, Decode)] pub struct BeaconClientUpdate { - pub consensus_update: LightClientUpdate, + pub consensus_update: VerifierStateUpdate, pub op_stack_payload: BTreeMap, pub arbitrum_payload: Option, } diff --git a/parachain/modules/consensus/sync-committee/src/utils.rs b/parachain/modules/ismp/sync-committee/src/utils.rs similarity index 100% rename from parachain/modules/consensus/sync-committee/src/utils.rs rename to parachain/modules/ismp/sync-committee/src/utils.rs diff --git a/parachain/node/Cargo.toml b/parachain/node/Cargo.toml index 0fda35f20..1d85c01cd 100644 --- a/parachain/node/Cargo.toml +++ b/parachain/node/Cargo.toml @@ -73,8 +73,8 @@ cumulus-client-consensus-proposer = { git = "https://github.com/paritytech/polka cumulus-client-collator = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } # polytope-labs -ismp-rpc = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main" } -ismp-runtime-api = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main" } +ismp-rpc = { path = "../modules/ismp/rpc" } +ismp-runtime-api = { path = "../modules/ismp/runtime-api" } # local #ismp-parachain-inherent = { path = "../modules/consensus/parachain/inherent" } diff --git a/parachain/runtime/Cargo.toml b/parachain/runtime/Cargo.toml index 7e3444bf9..1b5934f73 100644 --- a/parachain/runtime/Cargo.toml +++ b/parachain/runtime/Cargo.toml @@ -69,14 +69,14 @@ pallet-collator-selection = { git = "https://github.com/paritytech/polkadot-sdk" parachain-info = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } # polytope-labs -pallet-ismp = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main", default-features = false } -ismp-demo = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main", default-features = false } -ismp-runtime-api = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main", default-features = false } -ismp-primitives = { git = "https://github.com/polytope-labs/substrate-ismp.git", branch = "main", default-features = false } ismp = { git = "https://github.com/polytope-labs/ismp-rs.git", branch = "main", default-features = false } -# local modules -ismp-sync-committee = { path = "../modules/consensus/sync-committee", default-features = false } +# ismp modules +pallet-ismp = { path = "../modules/ismp/core", default-features = false } +ismp-demo = { path = "../modules/ismp/demo", default-features = false } +ismp-runtime-api = { path = "../modules/ismp/runtime-api", default-features = false } +ismp-primitives = { path = "../modules/ismp/primitives", default-features = false } +ismp-sync-committee = { path = "../modules/ismp/sync-committee", default-features = false } [features] default = [ diff --git a/parachain/runtime/src/lib.rs b/parachain/runtime/src/lib.rs index 0b67b8343..d32cc7f63 100644 --- a/parachain/runtime/src/lib.rs +++ b/parachain/runtime/src/lib.rs @@ -193,7 +193,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hyperbridge"), impl_name: create_runtime_str!("hyperbridge"), authoring_version: 1, - spec_version: 103, + spec_version: 104, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/scripts/wait_for_tcp_port_opening.sh b/scripts/wait_for_tcp_port_opening.sh new file mode 100755 index 000000000..d9e61a31e --- /dev/null +++ b/scripts/wait_for_tcp_port_opening.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +HOST=$1 +PORT=$2 + +echo "Trying to connect to ${HOST}:${PORT}..." + +while ! nc -z $HOST $PORT; do + sleep 0.5 +done + +echo "${HOST}:${PORT} is ready for requests." \ No newline at end of file From 2e1daa8ac927e339f7daf1ffcecca0fa2bf6c4eb Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Thu, 5 Oct 2023 22:08:17 +0100 Subject: [PATCH 181/182] fix workflow --- .github/workflows/test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d77f8d647..ddfd8c5e8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,15 +8,18 @@ on: branches: - main +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always + RUSTFLAGS: "-C link-args=-Wl,--allow-multiple-definition" jobs: check: name: Check Workspace runs-on: ubuntu-latest - env: - RUSTFLAGS: "-C link-args=-Wl,--allow-multiple-definition" steps: - uses: actions/checkout@v2 with: @@ -121,3 +124,4 @@ jobs: - name: Run all tests run: | cargo +nightly test -p sync-committee-prover -- --nocapture + From c5834ca6435ab44241aa3e34ff9369add44f889c Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Fri, 6 Oct 2023 05:54:53 +0100 Subject: [PATCH 182/182] rename chainspec --- parachain/chainspec/gargantuan-raw.json | 73 -------------- parachain/chainspec/gargantuan.json | 126 ++++++++++-------------- parachain/node/src/command.rs | 2 +- 3 files changed, 55 insertions(+), 146 deletions(-) delete mode 100644 parachain/chainspec/gargantuan-raw.json diff --git a/parachain/chainspec/gargantuan-raw.json b/parachain/chainspec/gargantuan-raw.json deleted file mode 100644 index fb1b42fef..000000000 --- a/parachain/chainspec/gargantuan-raw.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "name": "Hyperbridge (Gargantuan)", - "id": "gargantuan", - "chainType": "Live", - "bootNodes": [ - ], - "telemetryEndpoints": null, - "protocolId": "gargantuan", - "properties": { - "ss58Format": 42, - "tokenDecimals": 12, - "tokenSymbol": "CORE" - }, - "relay_chain": "rococo", - "para_id": 4296, - "codeSubstitutes": {}, - "genesis": { - "raw": { - "top": { - "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xc8100000", - "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x103895530afb23bb607661426d55eb8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", - "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x01000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000", - "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da915ba7f8e948fe69399899872e1b0402518eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43": "0x0000000001000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da97eed8e9f29ef7599680859f048da355c7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121": "0x0000000001000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9afa172acf083421926d6cfe34805cb00588177cf191e890d83b10e9653cfd663c8a382d5afce8518636dd9565c22a631": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f3348a0d38f168e401926958f66bcf0b70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c": "0x0000000001000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x91012c6879706572627269646765", - "0x3a63": "0x", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd00580c64047efdc418105420580e710ef5c0df58674a9bdec1c8ff7b0a0466adfa1c7127ca5ff6de3ea7b0457227321a6f306c95e023f338c2ebc838fc05fd4df9cf7dfc028268fc1b8dd3c07ec80d5551d5e2bffddf5eb2b2bbbbb74c49ca8012260f440f229d21493d331034e4fafd34d4803dcf5c3fa66b68c378e6fa60fba0b99f8636d433d78fe9988ee998a624ed17fa34d5d88731d3169bae3d854bfbd2bef569fb28e9574cfb853e7d9b5a4b97c24e57a6dca5abb13e7deab9a451ab00042d3e97990b1c2e68b894e1320397285c705c9e70a1818b0c5ca47071c2050a971eb804c1650a172a5cac7061c225c765072e55b8f8c0450b971fb8c4c0e5e59285cb162e4db8e4c005072e357071b9dcc0c5065507f3a007157a54a1c71a3d9ed0038d1e69f4e8e901851eb21e53e821851e6af488428f580f327a30a187117a70d1a3083d8ed0a3093d603dc6e8f1440f1df4c049cd525a484921f546aa0a292ba46a90ca416a8d141ba936525768a942aa2785462a8d162aa49ed08246cb15525ea49490faa20506a919a47490e249e1a49690d249e120b5839415a9295267a4a248999182428a8a544eaa8a540f526aa48c48dd20d5454a8a940c524c523729225adc48c95ade48bd5ad2683942cb0b5a9668b1a2e58994ab45899628b434d1c28316275249686122f5440a8a140d52364839d182d3d2d352450a06a921a48290522275b674d1024ba9208584d4115264a460a9325a745a9c9002420b1529395270b41821e5825413292e52466849426a891413a956cb152d3d48c5006381b3e81fe01e6028701518073807d807f80abc058e0227818fc04c3011d808b20ba41da417483848299064906690539047207d402281849157904920cf20b920bb20b7207f406a4166418e416241e6802c83e401a943f680b4825c02190629049629587040f2903a205fe4105872c0d20252092c5a609983e50e961e963458d0603983c50c963258c860198325c6d2041626b02c81850d963558a4c01205163958e2e091021e274b139630b08081050e963758dc60f1618102cb13589cc0a206cb1758bcc06207cb0f4b1758ea60e1020b1d2c5b60e16141024b172c4560e182450b961fb064c142041623b01c81050b962a5876c042058b152c3560d101cb0dcb0a56b2b072c78a1d2bb3953356d25869c24a6c458c152f56ba58c162c589159c9527568c582942050e953654b4a0e2a322870a1654aaa042051529a8b8a11205952ba8bca112878a1554c450c9824a182a4b5099820a1b2a6ba8c854662a4e50b181ca0d545ca0d2021516a8ac00e5051523504d505640b58192a1a6800a0355059414506aa0a280f20245068a062817ea0b14135060a060a823a096809a02b5832a020a082828a0c4e8172a08a826a08480e2410d0125056a0b540d504e40290125039419a831503e4025018506ea06a8184a0bd40c5067a06c80e20295062a8b9429a44821250aa215c437c4364a4b987e20d2498989749072460a1a2969ace0a044462906241a27364e6b9c627062e2440594224e65a0a4e06406ca798aa14081e2c4690a28324099018a142834408902a50628ae934cc58e51eb84052a5f381143858c1325786078628c50a0ac3146e18571a5705a8234c6188394304021a8fc98a680fa81bf61a242680d540fea0a141637099531c020745eb81ba8394255f0b8406d418c82890dd310585860da3101a1f446098e921ba5384a3e252c94e43005615442b4423445a949090ca52f986890fa01480b5b460f248448108551c239f9a24483900961134a51a4c84a520820002519ace8a0c5e8304c7590cc30b9412ae36405230c4c4d4864987ec616193b51c1b804a90d938fe90b2630dc159064a6374c5a2041e1e408121a5e132a3c189b50e901a965f2416905a51b10139d1c271e510d6e0b5298309a9d2481c48293304847f88bd402d28d080762152b5a3813d107df16ee0b6c1c1809ab05bb8665e3e466b40382a261232cc22c58a220ad40b6415e21f4414989524b5405a809d10e4a2d486982880a5316610fc2170807f4842827858c93284ea4105d21ea818807a297e8062b44b0b820650c2f09e20c48364c5630b561eac265e22e115e119211c640320871c218844d844e80a2407902050724852905a6133403b1471c432c0364039411ae0e6a0d11cfc90a2e44b843b842b83b778b1b840b841323ba26a4175824bc2390e820cdb1d282eb84c80a9316a524422c4a4c945e5042a2e404c889508b70074584f007e116275e98e43821a2e4822b832bc5a5c19dc1e5b9aefbc5adc16d62058c3b450906273c3706251c94aa28d5a0e483520e4aae5216a59cd20e4a5a94b0280da1042b6d51ea41e9082524948c50fa41e98a92104a5494a628dda0a4532a4269a7c4454907251e9488507a9582500242c90a918fa80cd113443da22b88a420ca82080b2239446e88e010c5217a43a48648264243b486c809222a88cc105541d486280a22364450087d216447c80ba11f511aa22988ce10cd44561893185f3022717570777002c6cd4951e152e13286a823befc0a0fc3c1f02f9cc795e05e7812bc0be7c291e04770981bc18be044f021b8103c080e04dff12dfc07ae85a90aa425c4274837f0ba50e9028a0aa62d6017f4c8812807a9586a8b2f08242e90b640aae39404120e484e905f7c3b1029a1a5092c57a0d8f090e041d161a1d34237871fd1d9c1b245b7856f06b70acf065e0d3c97d771cfb83d378d9314884130cd61a2c37487e90a262c7471746f885a883fb8689045b865a044c114070b1058763a31c41e98c2808b405103c50a2c5298e0f038484a9060c00285b845a989d411a82f9cc8a00383f4e2a48448c5a906a49f9322441adc145832be9d93151d1ca33a4646189f5001c355c1880b27e76501ea095d18dd154cb312938ea7eba2f309d521a201b8634283c5255e7172adec9c74be1f90ec38f1c0c405b106a4113a258cdc1871506fb48cd1b9210241dc6929239585942c4c6984d4486161cac25703d31a274c2c182322785ea0dc3079c1baa04bc26523b5b3f273c91815c17446f78588859845cb19a26b658b963044218c4ca48890caa26444ca072d5e883c18bd31ea42a80b2b5fa452401261a50b2a7780406801e39a912a420b12569aa0ba30f5a0ca4869e1f1784a4091428b0f526e785fb47061b28304c324f3986879a592189d08412135460b162a5e6899a374048b0f4e36f8b6b07258376c15ec142c15c81d2bb359b04eb04fb068581efb85ed59c9817bc1c14082c1def81df608cb02bb02cbc4bfb0c203a7c3b7e07338175c0b2b5b58e981b7b142c7ca1ceee372f81556aef027b815fd05afc2d5f01e4fc3cb702738194e854fe139be03d7f1285694e0307026567ee03e58f1812be12d70167812be027781cb151c0a2bafbea3c1d03f6ec44a18be445da3b2d15ce82f1a8c953a56b8805bd05c3412ba8bbee9239a491bd145d49f1a855a852a85d6a2ca1a8bd6419d42ada372a162a1ca51dfa86af414bd4545a39e51cda84aa8655432ea1835569950c5a8615430ea17d58b1a840a842a84ba45dda947a8b46a4ea5a2eaa0e6a05651add861a6ee0089597fb0c4a6a64e0144088a9060080f3a04119203082200814c03eb01ed451f21060310e00003a859f09ab835d32a62c26b9f5122840440900899e90108905b5d07233340807c31c4803a620890233b18b1a148912310a0193253a4c811080800c5842ae1c108111d8ccc206941194195a8a08aa04a502822669418398244c90b4a0554c9cc10223a182142a364260820669200a199e10100a814d01e80cc1821d2c310213f00a151324323a4480050279df981080d922052294de80c8d101a25409404a1c4c88c12234786b0a484812aa1313223c40c10540a18e80e478e24417207d5e1089124334880ccf06064a64811213d0ce191f2054a73e4489293142f50203b0899290224c90f3f00218224df80e4072536aca4d80193f253a4889021458a08814017a892224484d0cce8006466660700a4d4d1030d0f46668a7081ceccec00e4080d109a1e7820325302550a1df4c80e46880031419299208000420449a63132f3830984fcf8942dd09921468e18012204081124796688912319a03112c4912143f24aca1c3a1c1932a3038f142d50252698f1018812252698b180912344942899e1009932a3252082242799f121c9110f68804cc902053213c44c101b90d16186071e84c80820050b54890f43667a1822075542636426c94c0f46887c4a1c4688ec804a81830e21a2c40891243443cc00314111244884004932a343112241b07c1872a4084b25e50daa2425c50d2a04884a8a0ffd0188101a213a08a199c14028e50a2720524408111a2246889452da0862c8cc1033a2142bd024472000a6b04195cc10a12192e4480f34410819a5ac418d1c2142232449103a00a139524409102248b2902441e4529806f561c89122240032432344072245840821d23a53840442688c04b103109a21334988e860840810202802b842e0c0586296a9cda5b0f3ccd5c65136cf09cf898aa472b663e906d00da0ebbaae63616169ea2eb6b7534ba96bad695ad5a8d6dd3f4aa5ba6d1dbb7ab55e9bf3d6b41ea5aaa6690e00addb354d73b79a6b96b49556aad1764ab55a29ad5a6bd469a594524aa9a659ad9b6a9c534a35cb51add6f6ae605beb6db9da96ba539072de9a53cb518dd2d63a4d73e7ae2d719c085aaa81406d5b7417bb6b8fa3d6bb37dbdb75d736aebda96dcdddb6c5c005a8bb0db9a6ce69ceb589d3a86beded9452d7685b517bb7f5ceba73ee946ad47bd4946a6dadf56ecb51cdd2d66853d76ca5d4bb6d5b4deb6e6a3f6f4dd3b4b09bb61d2d57b52a72d4decab553da54d36a777b3bedee3a6b9db3b641d082a0b57d6fb7b5ee9cbbe55450505050ec2dd95bd234d728c7518ea394f6bd20d84d43dab42fa5b6c74b45ddd2200aa2eeedd652da1e76536fb79abb37d7ee943aa77dc78d16e82cd761da59e7045081d005405bbb2e5a77aeb96eb1dee201f000b86dd7dc35ef16b5e69aa68521a5edae6d947688524a3d1b6853a7b4520a4c20a4946add4d29a59c7b48a9ad6dbdad6d5b29ddb4a61b75eb5e693775da755d67a9a5d6ada59d5bb7b629ed6cb7a55c538e132d78b25c5befda9b735b6a914824eabe9ab7a6b9bb3bd53a6c4aa904dc5aaebb1bc55a77eab5476c4d61d85c736dbb76addb5db3deeeeed76a5a7bd7b97356d3b40ebb3ba4b6bd29e72f206d0bbab5965a6b416aad05dba905bb41afc1452d0294da5a2bfd346b3bb79c75b7dd2e6a4e398ee3bab5118640776b9de5ba6d53eea4396a693bf53bb228c7dddbada2c2699cd6de9aa69d4e5a486953ef766a552677afd629d5c2d014d6a6b49d0b518d768356e88a37281402d515505dd13a34a9426b0a47dd3d6a4d36edb4a64dfdc582deb5d64a29a54ddba916e6510380eb6eafdeddb4d6ee6eb76dad6d8fe3b8cb711cc7596b39aeabb46ad45aaa6994769d46dddd9b52ebee314eb6b737a51af56e4b6d37a595d2b6cd719c77534a69731614397b4bdcb66dee5cb7e6f6daa6546b4b354deb2c673b4a3b4aa97bd3b6ee96524a29775b8576f749a396e3a8a6755377aab96b56b3d6baedbc6ab5d3aabbdbb6ee6dbb2fd79652aa756b3468284d7b80e6008e7377b79ad6719da6f5d8001706a0468b9a610132b8a06cad55ebba51734deb34ed579a8fda79edead8719a06ba7b6beeb5baa635a8eda6b9b5f65ab7d6a9e6dd6da973ed5dd3d6da72ae35a554d334adbb9b76b77bb7b7d65dbb76735abbd7dadd69b46a5ab7d3f6da9aa651aa51cddb6a54ab1aeddadddd5dabb746b54eeb34dadededdbdb5ed346f8dd25adb250d1727f888b609921cf961e6872d013e56863852021a161f0a38c0930e6000200798f14148111200e1c108911e9408e9e115a0c34c92191d921c01e243010700e283902143667c00324324094d00105084880f30090000025e0ef092328014001820003030273a1421d2c3102034458210b2c38c0eaa1f6246072344f0015a0a402324c98c91191e92f4f8e801669428994982e487234380249911e2c8909924408428d161c80c1003f8e043010740326446c9aac70e4492cc08016401065080020ea0c30c902334484c9064488b576446889922408610f2038f110f478a00e1c108111e78385204a5fe40a9016692ec0024c9111a21438e14292244850045840c395204881122427a18b2c30e475e667e2042632488243824c30e3441ac848698e901088d112249900019a284c80f4984d02801c0891214113dc0912349527c18228404458a1c4912801884cc244931c18c0f4068869831c2c2836686071492121a2544869851d9a8018c1c214233448812234292d000a121f2c30f475e52e8018620b2c34c1224389444c84c912128218cccf8093500102347882849322304101d8ccc20412204c80c0f42921ca151028466881915218ccc7800422134b5e982509bfad120d4660b42b5254b96d8d8588e2e59b2c497d8744bc22036d5a6da9c04b1a936a3203615b46489f5f62576c992251ec466894d0759526d962c714b97d8d8d8586d894d5d62e3416cea92254b96d8d4254b6cb420d4e606b1a94bea1784da7841e892251d84dad86c365d109bbaa47a90254b6a109bba64c9121b1b6bc305a1365b9025d5c66db4209406b1a936366d6383501b1b1b0f426d6c3a884db5a941a84d0d9201ed0695525a7f50228406aff9f8310d6e9a6f9a6b8e9f4965791fc9a4b2c2bf21f823787fbbdf6bdf0591db8ba4f6b7825df79d83f6169bf6358cda5e13475cb38dbfbd8e8cf82011d78c582787f1c76e7bed73581c7903edfe3ac87d4c83c8ed6d4c8bda0db7c16e5cc40d0dac7e2695f5e2aa900449195c55ff921f490157d577f287abea7be4075c55bf2339ba75dae86fc9226e6860393eb9aa3e93caea25a0fa61e1d2a7a9f26bf9caebba713c9dde0323f8d457a22f6540f994077d497bc4a7bf8111852c7d0a49fa6b223730f67b60ecdfc0587a2f1c46193ad8c8738abdac5ff4dc4eedb4a79f5dbe54f1413a32276f3ac13524acf3a3e6e447cda7e01a13d6c9c1f4226c8ade84c5d2933ef4a0bf161491570351c8f086ab812864e953c86b491fd32632bce13a0822c31c401fd3158453025043d82461b38449a6eac5eaaaad7a22a97ff227d80c521f05059b3fb069727955d775adfaa68ed4a5742902487979977fe9b3ceeaa7a460b392aa099b354622c1441c270bc94c33d32cc5d4b23699b0d9b1ae1fe25c55c426470255517d92a87e285445f5c517b1b98566e2836a7d91089bd625d60701e051eab7d427e11857d5fa260ce3aa5a3f05bfab6a7d01e0d155b57e00307555ed5eb473752dd097b53b412a6c56176c9a2e0fc2ed7255adff82cd6e61fc2e1cacc7e3ef71f68c6bf9b6c9ec6b7bf148bdcaa7eab760536ba160b3be00d0f23c6635fcb2c26a6cacb2599dc9ce58b7602ed8ec17c68f82bde5aa5abf0736fdc4dc493ecb932edb2c6c73a9fc8ffff553d8d45cddac85c7abc8dad5af867dc7b857cb3857cfb8d6c99d189bee626179146c4f57d5fa6465696db095151b2b3d6a6b9d7cfdc7a6f6ea78e031e62d77f90bc661338fc95c16f3198c059bb6351a3d0ab63357d5fa2bb53eaa843a61531bb1d9a960f3b42d9789c25ea4172ce48a895a56263e65a55494513551b0563ac1e669c41dcc55f551588bd959a99d2dd397f5e5227dd9cd5ea12f3b98a8a2d4940e962b4a096b3357d50a7aca4aa97fbf1b5fa58c1ee547a5d29f7cca8f235c531a8d7ffa933f8db86684757218fdf8a51fb1398ebebe271bbf8e46a5d2c9494a0a0acac734f5150a3675644e8ff2264c5dc57d8f9f824d1acbf451b049c2d455dd9ff0c9ebc8989ef47e42c266085357796fc2a5d0dbd286a9abbe27e1d79efa6a7445d8143175d5fd10d691117fc426088b18c655f4b7a7212e694fa6df2f3382d893f9eae20d97dd87cb31d3d770397ad8e2f2d461c7320ec38e70e598f65ebef264aeaabf7dd995a3fdf2f47ae5fad793f98a72e185264d9a3499e30c1eb0ea7b3ebe3a00149a8481076fd0a0091858f5bd99af383a92f8020a277410043758f5bd1f5f153163086de03c01461d5e60d5ff4e5f1d61c10d6ef0c611659c6eb0ea7f37beb24658c1c61a5db8a30826aceac93c99af628e88b98af66cb8ec641a2e471f8bcbd3cc71f9be8a39e2c755343796e5fa3455b10f5f2bd396252c366123200697fea53d319e4c9f5258a64fa9ab340d973264fa303025cc7df19559f3e22afaffa7d3386ec08655a323d35fbf735cd3b87315fd8a5138d4a72c1b167deaab9a8aa9abe87f0b6479896dad884b5365bbb23ff595e9e977be4a79faa3af504fffe42b94a7ffbe3a3dfd175f9d3cfd6b224d7f42fe484786f428245c7372f2a4d791293daa846b4eb04e0e279f82cdd29f98483f7ad187fed6d0f434bc818639943ea651640a19de701b4c214b8f22497f6b9803e951c80e6fa0610ea38fe9130d73107d4c9f901dde702b88428efe448afe840c3d0d73087d4c5f1a4e0026372ebbdc1f224d113647d82461b3844d13364dd397f00f5779ec74c231a31136615ce54f1261f35de53f0a61737495bfe8049bd455fea1a7ae324f5e4706e54fb0694a49c135e62a4f49c155264b49c175364b49c17dbaca535270b75ce5fe2838444a2191b0597a1446321a6193f4285c4f57b9bf4884cdd1a3706db9cafd43216c8a1e85abcb55ee7ff227d80c3d0ad797ab7c7c98ec277e92824dd3a3e020aef23ff99734d6ad93fbb2ceaaacc64a986c9fb2c62f2bacbeee97d5057e595be197f57cca12bfa4b3ec2798bacae29205f28f711349592592b24824658d48ca129194152229ab499a471286a464478ee4897c57f57fe40b495dd5f1e4fe8da4ae2a694fee7665dab2e4c4a63b945d9a2abd97f6f4af46565647477aa24f6132fdcecbf19539d344084850238bf38a3658356e5d59ccd1c40aa2b0c2183ad881e8385174a1f382d71a703ce1083f6a7464b40f5fecb06983738530aa9881318425d4606db846c33a3968bf7db861737c0da3469f0296102ace93092ebcce9365522f4ed8799e29a0e20cd6e8fb6f0d65bcf3861aca8c4fc31b6af834bca18632e2d3f0861acaf8d3f00619fb95f4727c65b16961b9fd4d1d19cde52b1f33d55cb9dfde189d71301c5ffdc8bd5cf544f222c9aa8c8bb57c05f3610cacad9373bdeaacca60276ed6314e76e3ab71f4260a8329cd62e1f8a2eaaa2d50e82f2c73ae303ceb0c045bb0ef45efbf9675ac7baf8bf95b96f733f76ff635e766dee9ab1136cd951fe11f266cc694527e45d3543ee555ea2bf78bb0595d2119ac5b7dbe32d7ea59eeffb0d9328f3b737f87cd6ef358eed7b0e9302bcbfd8e4dd3d49141fdd6cafd275496fb5970071b5dff828951d84c5bc1a6d64ad1de4c790d5798abbac272bf0a36ebcb64fa14edd464286f7a9476754b8b6d2f3f7bb6b9b8d3651edb669d6c74faf14f3596fb4fb01964c366d7429dfca89bd158488c1bdce18426aa5002152d3a8b9daf96cb05ab2f590a36b5d364fa0df7cb55ddafdc8f82cd7689446fb2332d567ad197bce5a706db5c3e73d9d6da6623e9c127352cf79fb05963ddec64f4a3acc26ca01842136250c869830a1ded94c9ea0c76b65aaf76c54cd8b43391e837ec2e57b5bb727f099bde02815e24d360a1077de8f499abb8bfda6b6b81e18befafdc4fc266c33699abfa47d8d491191fdccedc5ff68b0b5588c11352d471046c16e366b258cfb8f374790b26c2a6958140bf61db7255b7727f089bf6e41e14cbfd179bda4b149f7b5347e6bee8cafd21361dc4e63662534706fc5073b96c9098630c39ba60030d39ce90c1ce984cf67db9cd7ce67db9b5ceee4bed05c2a68db9aa9fc3a6a923b3bd49c41216f71bb62f57f58bd80cb1595a58ee07b176b175b53e2100c10c1913ba5881165894a57d696354e65f76ad1ab35f76b3867dd8c391f9cbc35e6b3b296bfbd2ba3aecdd68ae2a5894b5e125362bb0a349932634d9421330b0386c61afdcef128691b268b2a084368e4085180fbc60519686bdd355fda585e1600384264668d2a44918cea082e59d2c67346902070b9ad0a4c9cd1570b230e3055a7011070e581bf65a6c08818c5913a460220746606dd8c37155bf253bbce1692843c340c28d3ad88082074e48836571873740c1f2d748caba15b4a4e70a714839bb20c20dc268b28427588ebd1c57f5c7b40b1846984c614c6667fdae1bc7d3e973fdf273e99dae2a5f72fdf22597dee9bd326d59a262533b00605c3bcbfdd792540b81fd42bfbeffad608396a4f95e1a76ef24f794ecde9265cdf63bac54184bdac3c48c5c5231328de5fecaeaffd13e4a18990b7818a5bdf7d647fd7e19224067a0b9bf75e81565890193808eecfaa59f23c118b713a059c363bff4fbd3f72030ca864abf343ef54b7f874efd32807ee97fe997fef294fb2bed91a17d942f41da47294303da4798fb3f6045ee0f0165a35f30d697f6e1f8fb85e254bf846cbf7cf999ce326db1e9aa85e2d2be540b8dfd62fb85926e4c68041d353c4d953496fdefa53e802f7e097e7dee3bb2acb9fbdb498cdcb78f120646065fd1d8799e27cb3fc657cff2a7c005e80c35bb8a339122bb256b1e00d83eb47790ce50410f1b89d1660a63b244e6fe5ab0c31a1a8991cb14c6c405dbc7b4875d83f1cbdcd7b086ee69580310b549b6e2bd34c4407be4f797a455fc98fec8b2661197e27f7f1d14c9fe8fecf748f06928819abdbf2279df2369be349c8088cb9ac5bf7f1bf426f0654a6328c5f73ea663fc56d0bb1fd3a22776ce78dffb2ffbd3d0862e5fecc3f7ed91e5f7f5639c8616e8ff2e59730d81a84d7288ef83a4f7311ed397a4f92369f668e6c84d232d09d218288d65da6203560b1b63489bc1590682cb4288425004dd062b59334867e833d707db071dc170691bc1468b91bbbb5b08f7777f7737428aeceeee295752c44ffef6d16fc7f6517eaa0899e7fe12cce537ab04b310b34c614474910590298c089c7c413465db874d2efbeb5bc0878a5116b0b9becdf58738cff3cc6dedd3d08672886cdf62207ca84f73c9e5faa31197a64a985c76aea20918b7afb97661a430226e3285a940961190294c0556e40e6b18c7116c1ffdfdb47d74bf7d7fd7610dddb6fdad60cdb786dbdf8da4a940d460fbc5be0d63debe8614a0797b1ad640fba50473d5706993b5aefb18efc8d2e6d23e8d67ee695883f6317d2f0d1d9766f6efc8f273f71b59d27e1922775fd27e2991e4eecbed69bf943de4ee63dc490e036166ff8da4b9234b956cff56902369de489a69a8019b5d2381f0ec6f49da2f15db7ee1aa8371bb7efad749cdc92d375973254b13e4fa97d66eb14441ae4ed6a7a10d5aaeb9499a6dba02b9b4c221459d8d14a60217e497f651bee4fefe018c77e4cefdb4579555aae47e1a5660092ad018688e6910a6e65caa1ae9d70ac748612978e5b17d50d8f993e977ed83c2529093e983eda3ae28ab24651a73a98fb13e7dea5dda3e2aad99fe05d1f641fbcb2ed3b7eda33efd5b532ab0848cc640f3ad346c1aaa8012ae7ea1d9edfbd7f7b0868a690d5540899c7ea1b9dfc9eb60256beeb09f8616c7340d6b88e91a5ec026774cd72c5c9abbbf76636a53ab0db77e252b1546d829cba5cdfdb67dd0efefea779daf68abca31d72fc74cc95a7b70ff5a6b57ff98aeeedf6fdfddbdbed7eeef72639405faed77b5a17ee3b2dfbb56265c9a2a3ff75faa80f1d2048cb45fca2ef77b0d2530c4799e29c8feb741da3eeab70fda2ffd31eda4ed977e048cf5edf75fd2ade0d83e68bfd0ef27b58f9afb4fedc3bf9f8635b82585e09195d874b97e972b4659a0befdfa16d3d4ec98f64b69337d1ad650715969aab10f9f698b8dd72ac6a599a6ca31f7dfae7d94292e768cf54b30f797feb45f60c6fafef469a881fa3ed87ea11fd3b7929d413a43920acb426ce16d90d219ea97feb45fe877eda3acd99ffe75b2eb17fa0518fde9d37002b65fe83beefa857e4cdf062b6933486748526719082e0b21caf5fb433a43927a662068c84280b97ebf48674852cf0c040d59882dd7ef07d11992d433034143aedf7f1daca4e790ce90a49e19081ab2105baedf0fd2199254591602ccf5fb0364a5b97371b0ced51c1869b7a7d8e04dfbf6f7d5a7f6549e59fd396f5a3839e5fb7e7fb9cb71fc6607c613f39e5837ee9c1e994fff787b5fdcfbe22bffeabf61d48f9a7e6b9bfe68714d639d1cfaed6b8dbb6d5b4ddbb65aabbf56ad3766525915897b5ffa350ec6a23f6eaf61540edafb438145bf73ede9537faab9b66d1cd7ddf6cd1757d1c6e65b6c7ec79d4ea76d1c4d1d997e0d9b9dd9e9c8f4fb536c2ee1aa4582263372afe178fdf1ef1bffc6013193ca22f978e5f1af3d3e30f7eae338ddea9b3efdebcfcc71b2d799abbcfe643fb3df649719fbdb75fbe7be78bd7b3d1008821ff8dd1b8662f7f8eafb2efe3c1cdefef1950836799bc75761e9458c0a4320288a4239843e7c306432994050144ba5b17340b184797c259a70e941e4c9df4b9a46de8f4af7964e406f2a7d7f724ba5ef2b95be6b32914aff954e481339f2c87bfa107962327927a6f04fbcd3e9249e4e2f9edc0645d2741bbc41c63b912179429a48f096c8931f91a6bf2052fcf1150993be7ecf4411e4e32b90e965be1a81beee01954ae4c97fa4af441a91402249249148a0fef946b863ae32895fcb44b17d403dcbdf83e4e56958bf3ac7fa388e23d83cbde3aada3057d57ef7d72a4c75ca8151b31887b1b4b05aebd75b5b177441201886dfe7640953ebcee78939b9ba72ad2d57555c569c5cffd62a0e511f09d8c317c45715fcc03014c57beb8dafbc7a534f5fdd7a0ee12b710824487af015d8439020aeaa7f351cf0fb40e1f7150455d057d0f7dd2b8a9e27aadfade2d7d7706e7dcba4b2445fff5a9f91acf49e07027def81402ffe0732d52a9ac4eff3bc526934123fb1beed017d2d7df5476fc2f5af883cf91069fa0b92a5b1f4f7c7afafb54a180582a6ffd2d7d049f8a6afe089e8241cc39393bf63c87447d3c73458ba63e98e771cc17a120a55532dd55ba9ab2e89041f4486b747a3af6fef573ff07e05d5b73c602561f0eb5f17bd9108feb5208904df1b81465f8f1a1c7974f481a4080bab8db9aade6a3930d65a2b9555efc7f7af325f79d8f4fed61e51f4af315f85c217dfbff25498af44f8f3460d1491df7b63e85ad01b631a1c6b2833561f57f97f6428fadf1a7395bf47d61e577995b9ca653263d7d2beb1bf7dcd02fddacb8ca786375c5ad8f6dae330ee78afef2f468536c757e0bd0fbe75f90a74b14e0ef7c507dfdfe280fe5edbbaa00b02c130fcbe96af2ef69dcf135fa1e8e5647fea59577610b61627fb5f7b23fe7d4f1441f01341e1077e60188ae2bd9ebdf195676fece9ab6bcf1f5f893f335f85331f5f81d87d5ce6ab0fbbcc55fe153861b2f632a36bb884c9388c2e6b97abc2b2d732c586d1e2d2b185d9305a188c6cb4e99a0738ee8baf6abeafe9ff70cd86ffe99fbedf4ea7710395f517d77c9b876b3a0e6fd8d491b17feaf069d43087c7ae73153583a57deceddf1ae240bb6e1cb70d9f4e210ef6b5a7a18cc5da07fcfbc31c1cbfe0255fb560841e18cb3ad39e0363e9dbe33076cfa5801d2e6132874b985c7eb0ebcaf6cb0f06caae0b3c65e099ed7f2469df0323f71e18b7971965f565c60a8ce526cbf671184b181c467f8aa9b79d9e4cd67363de7f1815be8e0cc8f3a3267cfb1bcc5720aef9b0f8f6b79df1bfeff58ddf288a61e8792f5f7df8f678178c85e0e51131081bf1f67295ed70b9ed6c39e07f7f4150f440f143537ccfdb4e4ff4c43004c1efdb727c75b71c97af3e178eaf409c96af42dcc2dbcdc6a4b22ede4e57d9e770595baf8d6ea7630a8ca5e6ba1a8eafc0bf9ee785de381bc331fc3e10bc271efe30f8bd7d0dc757179b7ec44c2aebe4ed5febe3abfbe08364a2bf1875c711f4fe8640f0bd710320e8d550868637c8843c3cfe1d45d8b30ffa11665259a1b77f4fc8d293c8d15f9114e1f8ca24faef4d6f5f6bf9eac3e6f7228cfa51236a37be124bb86684c51f915ef4f6b5d3572411ebe4207ef8a31f5f85d80c5f2c9d94c2cf1496fe5af0842cfd67225d073fd348f49944b74193c9849f4965e164fb25924965913e248dec8fe8bbc9f6995496a8b5b2fd1b22c51fc9f0af47be775ad64fcecce583236b65ebddecc07862d97a3dd92d98d28191faf80a84bfbf0f7afb56e6ab0fa3c2ff3c11d78458e747cdfff7a3877572f01efcf0416c82ef89216cff6ae00581e2df0d0c91e283578b69ef5af082be8fe9db20ef9954964832a92cf141115b58f85d6c7b5c65b195b9aab42ef3535661eccb8cf44b3ffdacad7651590943ed97ede240b5612ca92c5bed4b2a2bebcc050c23cc6bb8acb04cdf62af301a43fd980e61201bae5339f7c5576ebbebdd8ff06fa793c3f6dc874fbf1bfd7672f2d4557434baf7c557d6dbe926ca40311792562826be069eb11bfe096b551909161a9d845e23516d69a2c8c1eef8deebe4eed65992c9f2272dd70c74b9d9669d872bc69d2f59cc25d364b2b3059bb9b6560cc6b5b4d84bd6dace19cc75c6b673a6bd4619ec76afd8279379df82cd342d045d1683bd28a62caf9d0ae3ccf55368c77c55a3fdcfccc7beb5d871d93db9dee6691e57d587c176765e2ffb359fe32b0e9b9de3aafa16a370f876f9aac6560c8c33ec62c7e89a4df7765e9a2a49af52748381b11cc5728b6e286c05ad5c8e623ba21eff5224cbfdf7ebfe728fcd7b710d87efc7619d1c38ee7ef7dd8fbc5ed77d4c73bedd4d3bbdc535c7555d7370706e6e86d8e84f0fd4e703aeeaae3bd51d0a501934195eb697dccf9dbc13a752b79494ee699ee6e99ddee99c1c1c57f5d6b5fe549fea537bb6dac3a1fc66b337fee33f9afb68eec3798fef58cfb19ee3388ed3591e6e27f77f1f36b51c2d47c3f1c9fd9e874ddb736d8fe7db8dcfd6e36d381f4fee2f7538db8dabba27f7b3b486e337de53795cd58dd351dad3519f13fd79a932541cca73c266dd3199de71fbb8aa7d72f7f4cfcd9370ec8df3f80e8fb6637faccf8fcf96b3e18c727287e3aae6b93d3567a771783aa7a7777c9ae7a77b6efa47247ac736c7559d932dce8ef36ca11e1eedc6fe70373f3b39c755e64eee72b81ed0a77dfac77b6edc07c77f72eccd8ec5719e1d10e81d6b3eae6acda7c7550dfac9da8d28723837aed23ee4c9bde3aaee7872b7e32a1f8efbb1397647c3e1d1727ab41d1f8dc7fe683d37dacfb6bd632ec755cde5e06c3b99e76dd7c3e3aafee9341fed67ebb9d97c70b69f1cee6687c3d978366c723ba6e3ce47eb7c3a6c7637dd4fd7c3e5703bb91f8727a767c78787fbc95dcf8d779dcf4fee17ddf8ca7ebfa8e52bedfb4538be1abf5fe4f215e8fb4539beeabe5ff4f2957fff1d49d18348ee6f476ed6be6f1dc77de847cd83704d877572e83a2b7aeeb777cdade6a3682445ef20cebb6d03f94dee3743a2d45bfce5b8aa733e9c0fe7bbf96e7eb89fdc1ffa10363d9fce27f78f2336bd1e5775a96fc7f376b69ddcdf79399c9703f270401e8ef3e40681f07fcfe5b93c7767dbc90dc2e6e56ed761f372dcfd7eb4efe7f3d93e9feeebe9be1e076fbaf0e607fc017d36d027f7731c36c11eabede4fe0dccd17272bfc5268803e2384f6e6d27f7731c36c59c9cdc1b36459fdc6f2d36c39eb08704baf1b1a09e15104ecb6b23cff6a0100ee8c6550dea09795cd5220e78e3aaeec9fd23fe787072ff087b375e4f8fe71e8ef7e35ece77e3dece87e31e4f0a9bdf0ec66ff1f57155fbe4db83efcfbde1f1244e7803f2803b3ce24ef813fafcf8807240382ba19c108eab9ac755fd2d78ebf972fcdbc1f97872be9e1dfbf95c1efbfddc1e7b6fee0f0f1eafe130c7559d93fb496c86383cc21d9047e5597a78c49bf067bcf901ed8072503b3994333ee67c727f797decfd017b34f006f4d1401cf0470373c21b0ddc0971349067e381cd7047e5372cfab8aa451f53ec7155ab883fe20d0ace7803e201ed9c787268c755a38fabfa5770f793fbcb500b71c29c700bb750dc444e059be28fabcc94e7f09833e68c38aeea949d0ce23199423d211e578d3fa752f4d9c49f1ed08d0f07c201fd70a09c1b0eb433e270209e6edc7195d9e1904fc807059ba19fd04da867cce1c61d9c912767f4e5d8b333faf0e4eec69f504fe826b729f413f2e93ef493fb2f891c912232448e2488bc152491a439a0308516aa50820541608d48d21c435841189ac082cb8725226d6cc0c40b2d28810e970b07ac1049635da8c0056b1421c71d56b046320076f0808105134d8471060b44daac80c91c43e0c1e28c1670c1a22185ddc8724cc7b4488a6e442d118ec825ca11bd48d10e2cf76f3cb95f8be57edb93fb5d241be1e4fe0d8c14d682572e617229bac92dba21619bda1a70c2a5758e91c25c1047ae59882d5f503f4d959a2bdb6ff203a306466abfffda99af6a369cd64d8d8e0cf7da6bda996d876b385b71697fb2bdd6c7fab8cabe4cd6d3138b3d0faed9b0e571150e9aadd1b085b9cadac6d67e8ab5d8c58eb15df9efad6093f66f18e9d7f087f64dda77b2e6940e8c37c90d94a561226e28e23c594c7480a0215b5cda64fb95e5efa106e8116ab0966c598a4b9391ca32cda5cbbeaf0ff3e57f79cab51c732d3b192ccc571fb6335f519687638ef879e970cc11b3e770cc113ea70dc71c211b35ec24cd32c09aa4b0cc13635998e6da6c6c7f4bb020960a4267f0efe989c57878b6fbd9a77f41e4f897f4fe7e64f7b7c16d7beb7df761d3f35e4746fbabe19aefbb17d77c582787efdbbceb2047da1bac0541e4f8f67a1fd35f673b7b6d874d0f9b23cb6ff887ab3a86c2bcf65a83401f8b36db621ff7daecbda85fa9aedaba7fb159bdfa611389e76d308f73b9bed37e7df6573d6c5619d771ad9637f366169b9ce761b3655d6fbf6193eb649bedb8e7b0e9b06d3b79b87bb9aabf7ecc03c774101a439d8dad7fc16c310ae3da6a26eaed8a76f229dbab672eeef418f7e64905933424dc88c1152e399020635196959dddd972b93a0e75729272a299fcdc5c2edb66e1c73ae4230a9d38da2002b68305ed94c56667abd57127a391b7461a4c1381204d34bf1422e190a5450b9a1c31051d4644318bc164dc7976dc681c45a3f6d21e249e2633ae508215af33b2d0052b8375af98d771a336044b0361530c41256a6ca1c5167500210b2a5894652f7ee95c5fc9c1bc8ec39dabaa06f3b3042eb61063075ff8b028cb620a3b336d59b262d3fdb27369aa74596e2acb25954d602cfd2c61b2bffcf4d34f3ffdfc52865cfaf97efae9e74bff04c6b25dedea98af5e5efe4fa771ec72fdfaa50c30180f0f8dc5ea97b4a7bfcebebfc25e7a3e76e209bfa4b0f04b19beecc6d3bf800fdefb7d62e86b44efa9af42d8c4352051ec1ec657352286b939738861402c436eaeb73e65fad95d9669cb126b03b657854b7dd57120d1f67f4f5f065fdda71fe3abf0e953c057e0d3fff1015ff9d3bf21393e486edff7bebd864ded378c023d5c33823fbe8e8cf7a1876b40ac9303f8179bf7c16ffccdafe6f7d3fc7e4c8763488e013a32e0f308fd47ee2fe277958f201a13a908a3e259455815cf166c7230927c0f7793b18de5df9e56a6bdfcb5c9fae4e13ff214064558b7c2b15d61ac5f61ab6161ac63618bc42617636179cfbdc56dae150d6667dbcc5fc555fc7938c6378c790c7c9781329f812e7b8232db025d9c0c857acf355343cdb617e7287fa29da1ce3f50d68271d5ebd76a31927ba6e93dccb95ce55c6b8399259f69b2cbcd621426f34ecf61de49f2af3bb997abba98f9e14ee69dadfa72f5ec75f2907fda2c96fde35e2114dccd5c35f2bf22d2f42712e52f880c8d240a0ae8447a101ea17ce8c76b4191e94fd7c111844d6c925caeec1fc226ea47f86bb9ca5bd941d8fc4e57f9777a3390373b61d39389fcc7582cfb9be689ff0d0d2cd4e8c2b2bf4884cdfbba2fd17589aeebb66eeb9ef73cc12336fd4b27936825963d844d1006c2c017e87265ffd363133562d36b8d1ee6a102cbfe26530a0a15c6b23f0a0a36435808c3668812beb2bfe9756452de844dd39f9cc45c2ce3e8bd3e5808a5c7a3c0b2bfcaab60537cb9cac5578864497993e99954564c96fd05cbfe8f53529e49659d32f39b79b0d3f7cafee363f3ce78ccb217f364deec3bbfd68dddd8ca0a2803632da5506686311ed84cc12678baca41a656fe66dfe3976527df6c657785422f580f514a2cfba7f0857dae7bded675ddd785813370d6329a65ff1236439989b14962d37c6c5e19283c411ed8955d05ebc8a8fce943d804bd5ce5cf8243269898828222cbfe2b1894dd18e8025f200c8c8132d28f58144b2f43f99809829dde0c5dd80c4f536c657f152c72657f140ec159188661483a4720192876c2a678824d5874b9cabf84c59928be4498181365e22cfb081a413f62d0cc55b3ec4fc2264826c2a016069dd95dd95fb01848967d1c4fd04c3c431878672dd1f57d399e61cb05c65eb27b7ab06f06b6b2872e11bf7caf10ff9581f8149ed92f1ec5d687591eee40adec23d8d61e68929b64d74983988371fb69aaf4b3c260c00af3b00659a6a1eb952b080333839106ae4c430a9bc12ca7a05df463bac99afdb44b4821ebae21091d17dd51a0b62b4ec56183d67c41f44954b6c9b78214c6572f2fffa7937f9344464b9db60f4abf524afa992b0f9d81d620350b31d2b7d516e1b6d56ec891de879e1772de775eb899da5b6c82dec6b8aa294ce7a6fb0d0d2c119bb6abadf304b13b77ba8abb1d363fd7c98164a28fc07b4550e899d86cd95357bd8ecc7d109b208c6b6d335775b885e3884d8a4d6c96fe62f3e5a3085e2dc63936b7d8e86142b65d27a0d126f605b593c4c93c0f0577a7136bbd55eaec6c936daecda523333e3713dd50c49f6bda3bd6c23034359795759dd6edee22366dabeb6c4c93757b7b7b7bfbe6dbbbd6610b93cdecd9b22eadd5796b2d4eb6c1b657a76ddeeeae0e637bd262956bd5fe2db4cc575e65d15975d59cfaaa3b155679725c2e1c9c56abe6c6572136fbc6558d438d87bbcf73b3b5e3360d89afba6df6765663f9d5610e6b78ec4ee39f5e28ab8635fc90e1c2f8ea82e1f781e0d71fae0171f8fde3078261288a303017c755bef240e2ff4d789adf3f0ef1d1194c731de66004895e087ae4f621a9fd05490b82db879f5fffaee791db7be8a0b59b6647727b10a9fd1549fbb7454d13b9aeb9eeb8e6b6ed410ebe2e4d955496bbbba97396c33a3f6a74646c7bf63bdbdddbc6715de7ae691a365fb4ffc00faf3b9d28c08d31db37d691f187a12618eb9b6094d5d6c0b834b57d47d6583ec914f6c44ebeb4ba3152d81339792553d81337f95aff5088a42c1ac2948ca575c95abe7298bb3cc75fbed32f0e6f5a67efe7717dca5e72bfeef7f2fac5f5ab5f508c659f7d8ef8548e629f619f7dd240f6f2f27f3a6db687cee0719bc35a8d23bac21cf032f17d8d95dd788ac95076212e47f07eb8ce60ac6f7a0fdbe77ed3de7e45e2aafed2d8914ee3e84f22dc4d7dc4e8411179b3c15550a150034707f96a20fd986bec4ee39f7c96fb7f663e32db93fbef06525f859efe0d91954559de637d34e7bcffe130574931f6e0ab9797ffa6699907f38ab5104959158ad1769a6f7fb91ad6f0c361d58933972d6b99affca57cf9f24fe5696c19ac87b17ef735ac41fb8e2432ba866fa02c22fe270be1d9c6da87c56d493fb3eda133f82a468a97143b52c0ac4b0a1e2962d625454f0c132d57519cce35e69c98783d133b0ebb0ed2efb0861f3d4831961d0e7a150303315a455d4a14d12f6adc61061d62c4d1051b4350238b181898d12a9a4b6b45eeef489a65f095cd19bb930c271972cbb001cab2f589b1b4305f85a0c9149a3469d2840a3458fdcee32b10154d9a702109533469c2eaf798af6cb010030a2a8e780215ea5082d5ef3dbeb2b942066eec4cc1041628310556f7f0828e2f5c81841356c0c1eaf0b4c10cc6b042933696c0ea1762ac383dd9710993ab06ea8fc1ed61ecdaaaad20305d378ea7ff0e3d38fe07fa4efccd7e758d417a184b98ec610e6bd8b108d38c7dbde7b4a7f703a71f997ef8ea74fad15792e9f4f5637c4522fdc95712c9f495f415e6861acad450868637d450e6f434bca186324fc31b446f9aa5173d8caf485f9f96b08972fa134f7772fa4c5f8e266c8eb059c214a62b5f5aa1119763ae205c9e721571f9b986b8d2a0b6aec7694e654dc482f8aab6a88ccaa88cca6aabe25457cda9afba535bb5e5ab18274e276e9c68d5961338b5e584abb69cc8a9ad0ae36c440db242072d2970328545318455a6b0288ac817f4cadd49b48fd2ceecac7d94daa99deda35f2ba27df8b0bdb70f0a93a20b3ada478a1523b704607cd0c8d26917e799399c04083a3bcf4c23cadec7340d98bdef9e861a40d2914980a08166cb5d92ee69b6ec3d0d2d0024731fd3942c3bd3f748207864ee3b12081a72a53150206a93cc3d25cb7a019bcc614a074071b9bde6bf611ad31a6967fdd26f494b47bff43b697ffae58ab1d4cedc2b18290c8a38726967b9dfcedcfeb40f4ab3f5f155d8e308de36c52ffcbef0b688c78bad70765b44fa30668e4a1fc2a688cd4f247a94bfd83cb9f7531ef52abff22cdfe6f7a41e85dd3a7b26de1316458f7ff02059c818b2bac83ec9969128a4892c91e3c3844624e2e984147a0b243395eaecec56bbfaf57a9142d0388e236ccfeedbdfdf0e61d30461db72d88b5659c71c66de0fbc67cb753f4c5fdaab6b9318973ed9498c95a23491054a2ec17ce45627c65a9b18eb6bb9c26ab8b5662aab5496579dabdaa7071a438f34d30c932dada54dae94c650d60682d2181c97f5eda3ead3906d72871280c9b59234d3b00620282cd32d089da133fd1a4a80ca60b263d789c11d3737beaa3f9dc3b2ffa5096b1882fa0e8dc13fa6eb6becfaa53eed97fafe4868da877dff21ccf6d1ef5fcff64161d99b7041a6b018c491cbfac3842cd73b7ad53eb45911d9ff6ae4169b428c2be41a7a0c8f18ada2313c66f40bcdb7825fc31afaab8fafac043a6b98f68b7f16a37f9169ae3fbe8ae1e9e19175d96ad861968c6bfb857694c7a5a9175f993a32d67aa3c771affd69bb0ec6378e3beb0cf41b82fd9cdfdb32cef40f1bacaf6e81e0f51fe3c3f4e6e7f7711cf7e2b2eeae8bc9de7c17bb6d9fdd72bd5e61f79decd96ddbdff6874d73b42d87bd689575cc61165bdfb0d9599fd9d3b6ac635b5b24051813b1aa8591c29660220b205398126ee4aeeeee2f8e4b9bdc7d9dc1582d8c85b16f7fa3b5d61a92941aa0ecfefb1ec692faf4e0abeab2d487fa7c64699361aacb57485ce53f82b8ec4ee39f5e3ec3bc380c8caffa64b1c6d3e71226bfe412c607927ee9154dabfcbd5fca2ac50006904aa9a8e492fa90b2b7aa24e50e3740593db8cabf3e3196d595fdabcb57e1fbd71c5fbd7c057affbae3abd00742e40f57f98f24055ce50f22635ce52f9232b8ca3f2483d01968068247b6b82c402e61ec57f223cb2e7f0f92e57df06f8a8b1de3fd0a06b161cbf57db8186543fe48902c3beb83c5281b0a90fb0b00e32bca727777cf95ce00beb5b82c95d864fbdcfbc08124a531545cdad7da87fa15974b727def2d5976fe32f8f63f92d218ec533ac37df0299d81d218405cd6ef9ec6f0e1b26aef913e00c13569d2240c99e6ba246f1c16a276d936b446bad831daee9a6beceb5bc7a54f53ed1bf89b7defc14dc33516ebe4605f7b6f7c2b128da3e78160b7bbb769be9be0268edebbb9534746f4de7ceeded37e9fcb425eb25bc81b615a350c4bf87beb2f586fdbacfb51e431a4eaea33140ac7eebf6f7bd9ad052ae1b0ab695f44f2fa2a89e20c1c3bd3bcdbd5629bb9c54cf8c7f83014566325ec9d246c8ab0a97da725bd292b7d77c2dc9b27676d55577d9970cb2c6193a46173844d3bd35e78c3e606c233fc21f6fa55637d76cb655f2f0f9b0e76e0b74c7b6e6f6a2ed3ca364ec4ee6a58fbe92d97d5beb1c75c5543109bb6e5cf6193c3e68735197698ab5eb4ca3ae630d324a286a57d57173699d498cc595d6b7f6bad17c79db32db6b9ba7a6b93c1b4d7d6da4e3b8b69b057bf8bc32fdab9e19f69f8e42a5a2d1e5d45bd31d73ad9d4e6018646627cc11d995258ab8b4c314d674a63a05f9ff6b87d83cec1583fd4b7aa3cd94c8c1496044ed6328525a144ce1496441279cb1496c44daed8f64bbd63ac5f18290c899f5c82727f3f81041db93e9da1d218680c15e7faf6975e7aab8d25cb2162bc92e05bf2d2b006f02bc97d0246edbb7ce9759086159726131a58a2b5b80c22f670535922131a58a0df7089241721fe4d650d91adf795bc1ddae0f97b4b7a2f9294e54fe90ce197f62d3982d14ec0e2eed6bf770455226a13964e887db0dfb96a7f1ba4e1042c2ebb6cbf7b9134e9d7df70699ffb0e2560bffb90ac4db2485256fd9be2d264d43eaca1e20b720f8635f477b986da7b3801cf1603e1790af1b5f007119525e290a4d90b7f885f4991a4a104bcafef95e43ec6af830e924078b67f4920bc49932677648bcb9a3f0c442d6d72bf47965d7f4c57967fc7912e768cdabd96dc9e86da46d278b6a19ee7795ec0266b98c62d409b3469d2246b24cd96ec96b8cd6d2e0922681fb67dd0dce0bb5f4d7b7f0f6bf07eb1e95ad5b8f44199c290c041ae0f52b2a118290c0926722953980beec8f53bfa4d895cdab76f30d6a74f29caa57f3fe03dfd1f2f560af8aab7af9b288260687883c671b5769de7d57c4df89f677a1fd66cde7f718df860ad191dd76cdf15fb6f4fc29ce8bb179ff4a3af6ac6d08f3fc235a27daabd08734f0a611d19f1bb1f4930dc8b4f9f3efddb91dc7fa489cddbd8ac169b3a32fe22367564c417b95bc30f700fe32a4d7b11bbc36ae5b97f5fc42079ff23bdef48ee37527b4bfa37599f8638781fde8ff470477298921aee486fb2dad425a3f660b77e2561fae4607693f9bb6038185803a3c0a864cbd56745d231a6d4cc88088200d000e315002038300e0a48a4619e68a20f14800c70a2545a401d8844d23c8971100621e69031060900c0000002000001e3aba77b3ca4e695fed0678f06ed936d92841417db54e554821306f293f621f1384221cb64382e468d365ced45b3efa5c5b8fd06d264f5837a126b07a94b59abc24c54b56a803ea8662fc3147a917577bc28606398867cd1338b071b26cb6bef0084ecc34ff5e4c1bbbf76c670beaff3796620fe444b8ea2bb7fad7960864243b3be48b5ad2b6259b706bd8b8903f0eb4f6d288c6c183ad1e16d2b9ef2988389de07e5109b5921d348d137a28e08c025197254b9ac93251266cf9f6068de1b622ed856a13077363647659eef051db80dde93453885bd1c357e81f9384fc4613dbb3e737ddfae1a736bc4c9796a5c9cdd48067fc9b5471c106de1c8f64bb64ceb3f790bb641da568c518f70651672effe8e09de1e76138989be96620d7ff6902a877b767a0eed0c1043d240db4464c1bc8f29ef3e729aba8b870f35226267455f683bd3961a09cb56f6f8e4cd6fa270645a945744a56c508350c92d58e0a1354a4a46f3012a265a399d7c784552f02a6e819fc49ba3033fd0ad11354965ca53f7fc0d7f65b2442222883ced46e2743a51effbdf5269c02a195f84d74ce79d56badf8a6c22d2b63907994e38bb2937793c9edb8342c5d13eb5530a0c646da49d537d2ca8dcf8131d99714f7cfbdee2504d3cba5414e0ef6998a1e0247e03dac637f5cc21ace2a3e28228acc14054016fb8394e6382381bb5a6d96441a7cfa5a45cf3e19fd28b2a3977594813cbb5ce3c861d4e2a1160bba2e80400c636914a89db5a3ef9e354adc4d54ef405248278feed2bd978139089d1b57037d6c4a293eb0ba9698c93b13f67bb1a9b7ed1f9e0c9fb46cc7807046d9958f40284c761c8c0da3398cb9c1c8c134fe450e2146457c3c8531650f1225c5825a9effd60a8766290573bb74bea426cb9f6c3efe2806f30ddc4df8fbeda7c9a65d587b6c051070511940a35614c00c01697febad1f41169bc687fdaa772ad482df2f7c85a0004a4755a403a683922dfcf05dec8d69c2f039c94c901a834f1cdbe07ca23e5c1c6aa57dd49459171a28aeb009a48711eca2d063cbdacfe29810e73b619b2f37b4d13871b40fb9e17dcb370d79a577cbca6b6cfa839b484e47dbe6dcebc5e9c3c166b32bc9826ac4be665f972aa1bf387ef41e044851b808289d68fecb64c0ac71dd9c3e56a7823200fbff289b37ed0b458852b337e01c30e0de0151669804003531035cd5cb6b45dc380fcd5b8c5eae92f7a26023e1996f673beed2a825cbf9ba8802f8d58e1b700c5e84bd3489235cab48006f0764f92ae2e52d452ffa1e1a392837304ede82b90226da06b868c58f1bfe8ce37476947cb64757347143f8ae444a8520a119a3fabf7c566fd05432b3ea70a91923afe79915d971db6878a1ef4ed7a946dc50ca08a01e2888dad523aae8052a0aafd50147cef64776b54cc82f21fdff83a8f7eb3a5528d18940c5db65ebff0782c9d30cb8ff9c1fdd365d3c3474141c4ebab0359f890ca2bb5704f48363bd25bd980cc661a52f218d7e9752128b0fc92391aa3c48de0c14a31f8b25292a3db6b449468b95ea30bcce857fbf024b57419b99278522fce084e24351a721b5285f309d584b30f895aea4f9495491f1cbe25210f63b1e59d57c9653c1f8a08cae10dc927d97b240dcd8351c9931362b5c607b7df92aa37adf0d473639575f0370cfc3a0490d21a57c3beb11eee69248e1b17d1e8c83e5ff9d0c6d968bc52942b8d4b791f1574cc9dad552e8f9c8d76c425e33c79e76309152a91f1d1d8fe890a2c29f3e93afa89d4222e660afa140068bc8d0bc4b0625736c9af69dac322a93383abb86578a731670cc3d36d03b514fb7d25277465a75409ba5a1722e5c9c945d24791a7519885e5b43dcd0eb32e086a8a6c1d7c5658484caa05d83942a413d9ae58b43c5ebd501b1bf37033109c81c38262d0014f4af8286b4765777bb0cb55205f99e8d646cbb1b3aacbf4e5b2d73f212bd3c4040feadb9e2d66465aece21d5eb252c172b1b6125164c42642635e441f021b07129269347ac3313ee4d7ce1fb174829fc931f20c328943def650af4e53506204b9445d1a14dc5b79bc7b735d934732de51dbc05c044103a47cc737548853a87083c6659235dcdf17d877f8d11150f2ef8a8a1bdf1245ef124ac7949c94ee46d1931a25e9f2df4c3e93a4ba544e9b75c317ead997f1302465b9f92ea52b51f12b8fa42d978158add958ae898ab3665f3dcba975644ec44f3305e30ee422250ec8b445aafcfb187e43750c6716a9f28edb516b07e3858179ce75d724aa6a417bb39032d8e4d3c6d24d4625caf51c4c2fb30ea89a3303603d43d91f342524a520f2823514411a83e08197aba806acc57142a493a7431a1d6407ace94e102297caa853658b0ac20a53593f54f0aea870a04120f7294e970ab8de143b4e8515264b2dfed106215a1dd6d6b50adea9285c25046c00c7d4d992884e291968b31a7eb4c2296f6e7a1fb89c362f54108b87a07791358eace564723afe5db7abf941cd34840a201a3fde08de31a89e5e1b850a6506309b36dc1a6ad6aed7ac59a17e2e421ea182933f0c1625a18b4fec6f27ea8379c8d044701c9cb225e583e27cab31e0c46e8e1a77746f55636e66f1fb350eff534606646e7e55cd5c5c07b39b0a0f51e5a2920ceba06ca4a0a91a1931e7031d028d19cebba2bd5fd010c271a88e336b28caf16d34599841400323bf825df9a5785a5376d4b309aa28b7ca22c902ed093b8b9702a90138ce85be4b156b80e3f28253c33a05719b98a53cd9a851f011054d39141bc95f431365f60190d670c3a0fae8a5fede77dc109309e5d5dd9b0003037546273130800f29abbcd7df90156511fc302d8e62a1d8b1e314a1d94333f4a4c0f718e982aa0a16d87ae856ce866d4bab6cade23e32da07f1050eaf9763b36e862e552e1e14703ceb1640d4cedf04c2f31d139ed08c8a18d05117f7abdced8f1930e3b6cb86c0d7eff4f3fe6a4c04f9eb275f6b0bfeecc0a9e2be5762ce6a540b618e0f576c0860bb724a3fe31295e74cb574a21c12581766a17e608142dbadd0406cab039e09f6a925eedf71525f8bad5a82ef1a3f985597c31ed49266bea6862fe6aba6d395f1af0025e5aa67dd578db039ae2fd7f0a8c4e0ab6a6c6a40a993ae2726ed6989b45673005d84de43e5a5516497052dad6b6c70d1026c486ea3ca2964eb01004aa7878fd0826885692cc5f8c8644669ca1e3f41a54a374ccc47e5518c97c9d042fdf63a24224a8e69e48a0e4f44654a1df6c7862727d5a1b55b991c5b2d740bfaa7211210f4a29921ebea8948e800d0cf9402e613f9ad27e1f4928e7110826d811504de88f68928f894638af86360416c096429d10f3dc0dff88c4936df59117d2bf07ff13a87baf21502111640cbaa275b0828193f94827cedf05b84ee5f21133bf4489be11605d9ef316a242910132702976a8f02c3f04f450b0c6ce5c8a6a63dd8d3362f8c14afe806fb4411c4beea4209c2db0942f0c7442fb5af3a9d3c585f75b664894eff4004b262b3d9363ed003ab2620e2248286da3b01ed3b37845ed7e591452920168e845eaf2cce7cf4ce43abbacfd482c857753da70aa5d336e0354bd1b227a14dcf127402653df55b158c27979512a2ddf40bf455db60d9bf9dd87a2a2299a9e3112f759362d410b048b1d49532aa6a5ef699796078d375304e8e094e88da72e286a3716348a2a36f7747a7cccba23c01c8e599023ef4fd875e4f2542761ce3dabc02e581be765a753ab93fe693f674091eeefae248cae7df84dbd30f94b75e1388b518abb23136d78a9e8f3da40a345c1847b5d53fd88f7a9c32f664cf22ec04b73e22f07c100bc649ecefbf67af857fb4b758b5fa3d2d5a65e36fc5f74420be53642ca4df1384d796e2d5303a3c7f28489052f0e0d2da7d53636ef370348f61bfe9e11fe76952342149ee4659be6e556eaeda1756a7a96586a87e6f6eda2efc9b0752087c7204c0e11e7227d6cada591ecd0fcd7daa7141c4e4c55be914ca3f20a40325d5e3a897d7d7de5c736aec7b829df0ea9201e647e3f355f466431ce43b855904078261570f874b5eb584542c43a9198f19316e447b6a7dd2debd2e1118644708d45a45a613ca282680fb3cac118e95dd29611c36184a74e59974969e7de0ab02b701319d140ef90fb96291d0d1c3aa1c0c33df512c39b93398e678fedae784b78381359404a3a898e12f1fcd0b9a4a2f74ce74f0936e90418e18b40061ff940029723ca2e3134f46f36decc330d0445f01b8213d0974eb1372689c72152945766b44318f49046f61553413d16021c936e534f0fa135c51e8da16e760ad85900083a2c710e438dd78fe71e3ffa8c344a8a381dec5e3d0a7ece113fa1d8753d47110bebb3e22108ec1f84f924ba069e87646ea46d45e1d3f19342a348e72551b3ef549545db3e0f645ae79e9fff8045935d401cfa0fdc3e2d309912362efc8294be3c4648f6a3138440ed3ff0e1c725060fe014c1eaf2820a49c88a72ea1bb416250029713ed927960ca22e36856199dd0f446befcd3eddae8210af370c24843f51d991ed899848d476a1803d372af991f94a15c76d7e4722681be234104e1e877e5e987396aec07d291ca6a8a75a9c5bcd470bd3e23fde706921aebec1ee23110e615d100f95f2a3e3e763b3c205ad7dd3833e87e74bdd3968081ebf0cf5bf64b14c07a03de4e011857e184cb0ae51acf8e4a50fc490b486c7d1b8c55c8b9de75514e1d3eb59d5d73b69eccf1da59731cd1b23e1ec0ba8d9d150f8f26093d12b0ff29ca06fbfbb61fbf24558e918018ee7672eea8467a6e6ce347af827e7b867551e7cac596358db30dd7b237909e58db3dc14f9fe0773fe178594c0ad99f882700010c44cce945acec02b075443cab27178e4a30407bb253d855d281b15ef98e26aedc1068e519c4ca39ed9bd744d2464247f9ee3a9912c757ab823c0964f6fb142b563810fe0f01522f82e6691ef3f6b78146d8708c0caff03b60924305e302aedc5443b3a0bd80c31bd95c1ae06c7dca01a9c19d3202cc2c5cb89b75772609a6fff15a9002fa28510fee87402831d0b0a97943a01d2e009407f65e82437f922858e0498ab1dcbb8e36be4646f54bddb7cb47eaf37f64bc55a56b9387a7e24c0a4be3938b77893381dc63cb63100f035fbdc437d391004b70a4ec74fae41412762f2c5b2189c775fa452188a1c29bb940a0064494bf35b89173d6eaef97baed4a9390f115223ca7f34333a97e877815ecda9fc86719d452942fdbff558b8eb96240231da846ff8da2e299f4b1b353462eaf902f5a3b6b903d76b3c5091c5d5de73cf745fc2ebc81e9e32b72e99def3291430574cf7e898e9cb9d426db52297fa8bcb64c5798c33cc2538cbaf060dc48e01e59e2ec98fa5f82ee8ef0b1207070d1f47de5e76befc78e15cd347fc9fdf866326be155dbde9c13261eb23100b69db0eccbabe255d71bf4021908fd79f2301edee4236ecad9cde5a08770437a175cc6a819b4e03ec386c4c7152fcf635cfa255408105b6f2ebc99b017ad517379f616498c8b21495dacf7234e881d13a72eba166d5a7812b9313a05b7428fb13a94659d76abdef45bff25b0da43e34cca802b3bae62e03a1f78a80574dae0a57def7efd7490fdcbfb4ef8498a3fd10fec0b0480e530374b00818805823aee00d5d34a215c1c963270724b80b82b3f02004ace9bf0df542623f4359ea4d17e560924767a8dbdc3c3a215bab891cdc6c338ed8093bac365a77d4068ce7e7f5106584c880c32ec3578e5a7892f74fd0a510d1907c2e31b17ced17bb22dad9b51f5683a70ebe79de2fe512735d83aa70cf02095e5e14f656cb571e974e8960f4be0e81c60809df57989327bc619802f17e74f90a862554a345c54e282b67b0d1941d7c1ddd55349041832b6698a3e85091052acd64ad1a3f004b9c8797758c764dc4656c2ac14e8a0b69005c02f2b322ca0352bc741202a910f1ea275bdd98a46bcb683152326943d831213a4245d2926002cb68b0960404859cbcdd08b95ecfc38d9ad9185ac1263403d81af08ba0769846f456bd3abc619c3e12b63df1c400ccce152c1b5c54eac84f054ccafea7fea9b796b78c499cd33f0f2971a315cd64abb2f54c949aad202cca67ced57b40d79c0aba2d4bb34798e03fee90db6b021a2a059e326c4fdaca5e4d7260a55d981364696080dbd70b07facd73bfa9a7019eecabbf20c3ad030821122c8686b1119a465e13791cedb354518184c3a13b2d30213ba5a45039a099672e37166c2cb049988b7266474d1541ea0a210a7dc304b12d25c75aaac6331a31772a264491309692adc4de312b64e103e94e19ab91f181601fcd32e6871db59425fa6661338348d256cf406abfdcc34d9452613c4f12c60a8b9095083180d9aba4f0354a276a484690e9e3e88c3e1699e3eb8a2532064a461cd710ab88478b8e0cc0a3c9bfc405b6235098e450eb19a285702243d8ee30c1484804b4ab545d6e367b9a1f05da05140f8e9e1df9cfade78e23eee0a102debc321d036475eaf8676284c3b523a1e4d6ec47291e392489e5a1bd4e37ffb555a13216fe8e04680bd1cee989dbdbfc5f9911cf7b0f975d6ecc695d63e9aa1355574c9ad3edf55922494240136d2e32d43e3254b1042857ab24c7300a90b31b4a75092324789f005d12da66e8a4b81840b32d1da3389d430fb388c24f416fde55b5005425192493c94005df547374efcead3d632321fe0b6767eed84b578b690c4b708c358e0810317482c9d74c8317795b5e52c08d404a1b240773ef13dca1516bebd9e44b279b85e5171be73148e3f81cd3103008ddc74e4e056f2ad6bf7d050675a41eb0b4e2d50bec8e181b084c25428721a87e332ab0432b7a7f01a1d427230c0a839df2040c14af646ca48a7abf0f0db112801d681df7e8d280c33c1ddac1e6e48fb02e008b544a7fd1d72d17026d2c3f6d66e6fa3dc577e33ad50d923ec94abaa3f0ea982edb4e2a8410dd727546de04c55009c01e8e3316ce2686ac9dde1ac00c6113da4ec6e3c081e6f900063144209793c263f79dc8469126e4614827774019e25df5a3d01326d00023d3c76e67ac3726eb7e7314c7c6d85bde2d41e131591c5e349f00d4ad86722533260443ca8e1b1d11fb4785d7c3be1314a6501153e35a60ed607fd4dba164e1ba12c773da5ac8cc787be1f5e683499431ebe43a97670bc84bb557d30ced8f8a1d06a42ddf067d4d6b4472095c7ed343b42342d70e3205a21a84d95512b13a37563e9cbfa6ee63cb66f7dafd009c054f0987752ed3b9456d1898bc4022e69eccd2a3b386b5c8090ba1b666c85a0947d3efdea52287a44beeb290ebfd398f5a4e5223cf2d4c6c913c76d3dac45e1a0a9227a3a2dff5d1c5b109a4fffb43b9ad921327a49e0178a3c48b4f335fbf8446ddb270f58b5528f761579c8175b48f0ee4b89613fbc35948db543db9642e0491ab76d7f4a46588edfcee1e380455c73761f8cabdf9b74f52f08b4123ea24f842a7d7e748cb645222222aab178d96077e51f0a8404cbc3b4fd7b00c2b141c662df94473c0cf612d4b8c1d7f42e2bf6c3ae18c794cb733d3cc5870ee47f95906dde3a9467c4fa711bb8f485a890b473f64dea6cddd0b13122ea4d6e828b14f04575f02cbac72f0baf173db6abf2b2cfd7560588d519d77ab0783559831b31b6e553d8a82aed3e33c9f026c2383e202e136ccfa5e784ce903ae3e013eab41b2d7bda096610e07bc3dc9f43f3be8519206f7639936fd5a99f4c0f91f83ef2c58b02cc0a89bb8ac90336933f00a5dbaede38382a0f7e05e8625feec0f17bc583ec102d5cc8e5abf6c42be22bd46c5e7e0e01debdbeac2dd1707959a9db37203e490477478b5d42ed79f91ba27a80e52deb3b62bf5e7ca7882a764adac6b821352ec7854c2fece70207cd59334b1329824cbec61e3ba99c4d040a9c4ca767f5930e64b94c85dee667cca2f7bea9d465035e469ce220bfb0b41bf1d7c7286a6f14a434a1d4c851cc2f325950e27bedd3b99fc05730f0d62dd44dad895433308b2a40675268f798da8d43c0850c4993c46c1e33818823d215d71ac69275e88005b5319651f10dd9e79509fd3ac98b775464ba00cd27675d2d31525d0020ad434eed312150961c8bc8479083c94c1db89319caef552147c8340212070bd6458617f50461cd58f839bb3fc86ca3d8a50f8966c74a7c605d2f3e208bac8bfe9824f4db0455e24ee5ba71345f6bb04fca9e1093cd7db2b24b78e583e8e9a84902cb2b1e37aa0cc41914bf600c05b16826461117ab0e708cd89efbc496e7b15c6413e92d76ea63ea3e7987954cb1a2b736e285d2b2c01f261253644a7d3ce5b4d10a7ce0474e842893126c342a49a566257940c9cbab3f3209978d89be1a7659c8724ccb29ece630dd4e1a0d6a607c375fc481ab9481c145fe95643c486b06d5e722cc3c09d10f579640b340ecc77cc68200be4f6e5d55f6a095d54ee258a9473e67c994e561bf4224cb8a138a7a0e4c2d27ae84abb0e1061666aa9c80f63506ac288dfb23288b7ea7dc7e48e0257b3c054ba31fd7c6517fb9202c34ade3be7544bf52d5072d7e11fe640604dfdf891664ceec840e5607bbbf6cd25fe7c2f395eb15260e6e37dda9a9e55f51e8b9365fe834c70094cc69bd750f982b6ad65a91c2789d9f0d4923e343e4290a8ae44622098975eb25c6f5ea06f22d5b6e15ddd115dc8366b220906ee538d10c7a285368d41c4621c71d838f9b89e1e6c39cf28456c152922b62dfa42dfc922dd9e22fc2c1fb271cadd492c09aa2e01314665fd08d58a1494ed084dc48bfa6f004a6bd0b60933f6fc2eca226d8c2a364c4c286fa26604ab08f441a6bed2c42cd6f84695121f1b3a2a06a6a411f498469127210c8e4ccbe41deac643c030b88476855c6dbd2c1f6b2015fac8ee60e7c92525094aa96e2a8cb9678c7b01f1ea1097e3395b13c84fb128a9d44c9c1970d6168ee157585ef9527ef9c61873a9b4b0884f6b0d213059d21a2b437a2e0b4ba4c42461b0af9a59969c178f9b33f37fd31e195b2102ea6123233800982e9574639e142fcf137682384464cfd7938ec71d04216335b97b56e9b1dacfed1714a76295314c0687800a9702c596e05178cbfddfad2ee4fd2fd06bcfa2f911cfabd968e344c32725a8b7b33e74cc2048f75addf5c78109a0420eb54a983cf38768a9df76c17814a72a6135eb7178d438c6f62165224b947fd6c3e8f992a9ff20bf61e250452482f699e4bce17586904cc6c355d6b35d168bee27ccdcee4d94ce28ddce5a2da49502bb0d94b563602c7c1ea93f74e49423b335f0da2bf3c2fda0e2db3567b4be740944ce4920b853beca51141ddb491142f644b82b7efb412063ef6493da7de64d80354825c94c9bdc9d3d4f4b1633209a934f11ad117d2248118619185265d4dceb64f0af6d1dc65c63c4dce100a7de54ee5649dae97a43c4049533d417ca9f4c39496401649e74c4ea4ded2f74eb91488593d3680aba680e5b1c01aa17ce86f2878a9b0777aec52e39459a06255da663bae84527626309c9b02957547d0dfc5cd1b3b5bb3b57830c17b2f8661b7091ce52096c0b8bcd83306dd3b942c8bcebd3a321a7771f5326d0502364bdf6d149d94f9b83eb62181fcd310c2e40e9d783d01335b1d7211f4948c8133278feb8e6f50798e965b07f29d5229e9c8fca627ed528167e9676e7293545888e61fcac94f9cc872227370295ccb144779024c35d7d7b1c718c6dbac34c8158585b25a9c44b7475131edb7522104a7b896626511b12ca9866b2e54a36eb304662ec97407944e2eaa1ec0bb7026c8ddfcd59523eddeda3cbf30353fac5ec0d6acc7be3e261868d828f697f4cbebad048924aac2cd3b94cf78669d91137c29c6e077dc494c5a22000765ace7e41d35a2df4d69d8e037ace917e2012cd79bd534e70c8d9e16f5a1419a55ed7f1a074cc5d0658e64055d3e3f617f4b67c92c2b88187506ff097b095270c45dba654b0aad3552b8169189fdfbc8ae2b1f1850e8c683cbe7ced2d6123047822c08c2fee6fef13648ee9efe60d1611a25bbe600968b8620cfc74f6ec1e7cd947e389d5b18b8cc54c3b0776fe9c5a7bb61c78a625043444a0240211661d48ad65ef158905862510d1bf13b1881a42ac815b78d4a97ac50cb99ca20752a9f11e0424d03ec9236551fb2a10de4e4b3aecd4ae68c5e923d1ad1d31713e4ca80affcb004544681fbe7fcef6f9286b69a82911dd0c37b69403d14382a61065da189c402e81fb3af38fa6a4ddb028876bd3a66f3ca0d82acaca1d73514503c10342e19d0273544106621729f0b9544210217b4cf7cd7f5c1eb010c97386dd33b31981846830d2539497f8640e903ce707f51d31adc4304dd5810e22d8182698508be516f150718adfcac0f24312d8e0f6d8cffef0a7918596a642991302ec68451ba0d23cc8cbfaf4580263efcfc2e85accb1dd28a39d2d9df2bf8636f820d9d401f4de1219b0a81bbdb7382ec19b3178cb7fca5dd8ae22f6da9de0a149e34951002a5315705e9b5b357b8daa6b2f2dfbdd8593eb7f0678c26c85a9d07bbeda094acdc669af21799807d1ae25fac207e5717a88f6f209a7dd59d19d8d3bdf5eda1117819c0bf0d2666f26441b0f7f51d320b56f78f9b92d10227210a48602463381ed4ee271077395ac02a793218cb1fd01478a59f10b437a8de5879bad5aa0084843828b221d4cde57f903b021a456005e8ef5c83a4e6140caec83fde50ffea8eb10925f29221a791363b6297ef63d1c1ee737ed5dcd9015c0aec806859dcbc501a265ad08f01ecc23918223204021cdf5992e012163a51bf0b73373a1fe1da56265c09c0610504ff68479e783f33e0adb0bc6cd0c0be19075814773147333fd56559ab92d1251e339a244544127df935a578c473e01946a827e0ea37cd9640a50e225a3806524a9d028465d6f139bdffaeb1456b1aabdef3ddec507688b7537097a5c701c6aa352aeb36cab13331b0b764f2bb1f764f84ec9b62244f5b288e47ce1f6a90c89c148afc0b1e24537cb280829c1d85e36c24328fc2ed77ddeeb135ca14d51bcfdf1eb3d64826a69f172a061b35cdf35138e1e6b590839f36338b5a2e32b2cd9dd0e1e2b8fa08845ec0c09a74cf3490c718592dded2e869758bac600ae95a1ac7e1f663735aba147f44770ba267db5fb479ae995b78852c221fbba899e8c1cf84f6c11d701ae2e51c1e910ac545dafdc437cc91176f78c2afe755bc2eb84b5ae467dae1b3a6952291d126cf1b8367d4be9fbbcfb8808fae8c69f3f9671ac3c6f01ee30da5a26210ccdd17a3fbce38122be99ca1aea8f9e8f949cf83c208a6c3369d28fecc81f71591082e49d7b22cd2a12492da3cf6875bfadd4e912230d99562105de2979ee57e25c76433f5e6528ec0498bf7df75b27d27bb5143c73ba2a68584d99d589dfd97e5cbfe7bbe2e980fff70642cb99300fe354fde898e944917bf133dfb35447832d2ad5189abdd1f5b73cc9c28b34f93e1be013594980e7f33625f9fe5afb8279e3593ccade934394888ccc205ae4c235c0d7fb5c65b7b41b61bc7bed3868066f18918da008a540064a1ac6000d0d63007c2dd87079e1aadf1785f402a5c29af996220c96d3e8132eeee6d1a0b49d0e975987f8bd11c2189089dfdf978ed3d5e817776d9117189979ce6c390727ca2609d5b11dad21f887542d50bd9efba07948bbaa7681a5478a0ae392f751fcfa0cf604cd340115ff28de180bdad287d02668e1ef9799c57fdea7cbe366a400747625cd9a0593e142932cad2511424b286de9791b1b5414121e3797e36f784eceb434744e481c0ea00a18cef858cc95b8103ebb9ccca654818e0c67d2ea5f98e87bbae3f74274208e82c5b01c5385ee5c4848914c05e608658d7f422ab18baec8ebde47c1d3ef044f41af8ac83021a9ffa1abfba74ddafcc4d0415b859a24650f2f14b7c161f8065b13e1c6242a04c8307d64ab0c902946bb16fc59fa6789622ce128d6d8452169a1ef5b95bf8f9d61e4cb12e4d5cb9576696153ebba717fec0cf24e8d3f8866533a350e12a390e777e3cc012627540fb23d9706f7c1353fc3472eb023b099ad8bc4995ea20d87426c359a3e29802b8ad0bc4da58d4c674a256dab3cb58cfb5d4c7b6b2d302ee28d0b253c682205177d45d41b73f9a6d652bc4e83f7d90db9c4b0d6eb4775c1d491d354b3b3a77eee0b7c0ccc6fa8b7a485c9f3e927e28cd2ab538414d13ab679b759cbae727e5ca6d4165bca8f39a3ad8ffb40f05e4a63e35c04783da2a403bfba33e11cbd619fe27668a1a6be650f8a952c3fa405c18d07c4154f706c8c9d2bb84702e254f731f420e08379784c742e5629294bd0c6619a65c69a54d44dfb1f11220e784219e81a372b192e6bf58706213ce9151aedf5e103d0885cf3e94b3241cbc11d3f15b711a91081e586d0cdcb86e8cb8b1c2ba1b4ca99a8626d955e325c27160ec2b1d1fffce5d9d4337ed570a1f4dad3097a61a324d32933fba9f2caa504a05a2272e2f7d66e48c97f6772149966cb3494472bddf860e54cc8c61ce347e6ec1859c8999a09c5c3984977658505d674ac35aa9ca8ed83e9e291bb28666c6a5cd1d2fc502b9503a6384fbe345781dab1144d9e7d21255802d4603e01f81d09cf1aaaf5afde646f80d7eedd5f24c15a82f2d7b30edd8089639703ae746dafc3bc074204a26086d3682440ec6f74a5d6d05c4e7141a3bd0795f531a00b3b5f705a9c0d067f6438e107221f80e5d7fbcb72ce48fdfc387b35c48b486ba07ce37a9e36db342f76f0d67a36f948c86c83137986eea784476e677c372ab607ddb03b878030eb2cfd015406b1b675c7c1cc38f079f41c7e1ff3379dc4f99437ccdc3e753e48f55a761279738c311ee4b5287385bc1bd1da51ccf23291190362aa486568f6e5a53eac627d4a3dc8372e938fc4715db18c163b84afce7fcbf5e5c55502de2e4fa97a87068d682c0b5c339e093c9fc020f7070e2978e79b7ffa00770cfb1ac6df9c2686b6ba1de16fba12af019a96b21f9a2e640362ba32dcab1e1182e83afb52209401b0080b5dd03bcf9150090de6e580f141f1fdfd98d687b13dc52b27239166bbccb036ef896b4a300ef45ad05b9613ef0b5477e484343be3e9e54c1d742d88515fc0866ede66ed65d5ac0f247d68224ab3384d2e15ce43f1379dc6907cd1ea76a22a6027be8bdb94821bd81525913653f6be84189d1691eef345a0ed08fbb4128354d3507542e0d4b80f16c05b300e32dfaff309158bc5ec0a54f20f8287a586bc1d7c6fbfa432b3565f8081c51144e945c93366002136021b94345441b8e2099a255cb24980149976c8cb69faa8eecc679062524918cf0b709b7f33e5ce34a86c51157eee4a37c49f86503599efa59043842281e0d9c8ac479b9a9ed03867afba394c808407b47e33793c1e123c9283b88357f58112d067267aec8ccba4c0d5a34ce5a18282d765f3c9b82e796d414281d8be8ff12c510dc320432dc26ea6ed502c7adf5b6097c2ce2bef843a81076908b61f296f247fa4ae1c7df92f4cbe2c5f5ce2e0d25e27ce16819cba791059f9e9aa4540ff2415a7726bd57549bedc13d1c23f4f0d3e339a0e490eb0a3df0582607ef252249144209d5c74a80b62dcbe980bd7f406a4fe253c9777c033787650ccd124a1b377d6db2aec6f623ad2c7b3e53bccc1a64bdc1c302b44b8020c5f5a46a4f2c06a121faa862c8e8fd4b1d2012b46d8c25c33f90fbb1f7f1e6ab937f0aafc82cf24590c7605b2270d76e700efa75e9696419b427c683f693392845a2a1703e1bec067c7b58e554bc4a8e10f676e18c9a46d44bd39e14f6a803e380232c87a4f86ec85c610f709402e741929abc184d7974a2d5ada1d85bf0c626186a2f582ffa42994a26c4e532ca837ae080c777da59e538946ca609282c2a31cd60f939fde1e3dc6721876ed9f22e3ebfb9d94edce993f57912ad640e680828e5105fd222f4e0d9de6625522e83bad10c32a5993c18c35240c487252b4c19f309b0744449b39ae2bce9cb7fc81064168df09a09e40fec0207c23c58254943195dfbbcf4ba35d3fa6c8612c67327e498031c006487666fdb48cc9cd4f4fe613a7b0bb7e39e545d6285103a3fe2dcf21fe62495f52d8bcf1558020d6943aa49b3daafcb0c11d6edc95814a554419c0e05e0274ac5380bc3f8814ddb5bee1a691043d6ef565c1350002f404029d6a7e3fa5f50907426580dd98d9be03564c753a0a9753d9b52760eb197f0a43590401cdabab486e841543f83c208d2e69b22fdc756fd4a588f7289a3e442d8fd0765dbcdabca44d3d71a0d21085e70ed6818c54a758ff4bf25e219387d41a123ed9c0d95b198a6cb131ce16e27b58c3249a446c341b924102f764e9187c7aaae8b9206c04daf59565758e45319b8b7d5d0b78120c494aa0f397313c00408a588dc5a976e16345bd247b3d8180e3f38aa9c5c70f87eb7630f78b376ae4f96bf4e861e795986245ddc729271a5d71c20e041367729c3039a3fcb33fb3a3260be3c431d147023c4e5f737f9ad0c74732407eda23434ab0c38e498604d2e1541654110bcd95ded40ed57cca9d5b814ca68d331f38b2986a72acbead8193366a1e7a1ddce6b92b980ddf94221c5f8921722b05a6053dcfad10a00368d3223ee9704cdb0d3ce4a2ff58d26503d02ac5cf55af33072afd4207d7907dba4cfc1fb52a73bb100d9b7b9654be6f915c4cce58c252bcca7695424bf20d29f5c874230bba45bf30fd053b566234138427a9cb0079039f61424127dcba23b0ad82dc63fe9ba625ad09358665ddc60de9b259e75c3968ea1786191828f2d2ed5163e54fdcd22d08c131b1dd8981da08000f64e9d8bd58a995796ba3c0aa567a19b150207b72cbd56db9ebb5663abbc4452210456efd894f97700bacd8e7275603f02832972da2842399c2dbfe47f27d60b528c8275be88c419db292a19005880bf550cd1e59bad460f3904f500aa27ef405f56309e0d988e580bfa8f14cc1c68e5939b2087851549d390d483a66e7b863bc881e510ec287f9bebd705ad1078809e091df21069235135c12a9af70e24f7d67332c60cd8c006e3ced8606223936409fd3f2038f59eec4c08d50171c99f7aaf187c5465fc974defb86ee193ff550755061683908d52f8f4f598131bc83832f0baac297fdfcc61b1e9b362d2cac30aadaac1027686095c36c9238d52c6f4ab735b9729e919037f78ff89734b12fc06b04119d8313e9c8e053420aad317e6363404c0d5560d0e52407920e98273a13411bbccdad803435a300f2f9d963ed198e1ab74e1ffbebbedb53fd6956c5ede32b68749abf06ff82abece82f35f0c0388df614d015f60cf08c9c698c0241a8be4728d482abc998b06e5c0654be2056b0e258d7012df36d720725a164656cc12837f44583d51847a19358e55b1a03869d8b1bfc30daa8ac94b41a6d702638fbf8a994f7a7efc586caa885dc1ebc853135fca8940d0f7890b644ab53db0d41684a64d0eab41a6f20b5638fa06ed3a44c9b600205e7405ce8ad4fc2b5f59a884a94bf6247a5909aec9985fb487461c90ea2670cc0edcd52388d7ba53463ff11448026f7b48a42ad6aa5e554147f0e3313b822e33772ea800de7a7ac82882943995004141493056b0012ad3eb3f13d6f9abad475652122ab4580f44ae41c994bb2ce81841e68684dd6b3005c5fe65d8daa6380cda32c63ed7863410ce2dc2fdb45eb546052585188214453b3f84460cc2cd8948b1cae56ea480e84a1768ef20dc5712cd7cb53dcf4bbc27baa699ec57123a72536c15878ede8abf4a0c42469b6654321eac8a0b7dc31e5712eff800939fc282145bd855e2e54a02781231c00659cf22df20bd23d2e2a7a03baa629f96a4640fe29c5f51672c8995d189741b1023dd5a79bf64816b9684f2caba55c1a0679475fc23c874b224e8b46fb2cf77fc9c2440f839f22e4b4219b623a2ae859f1562ca46a4078d13693e87ebc743d1956fabc7341c10b914938c2d10704d044c1ec84f28ca8426085a2d12d12e484017352e6a6ae8ff55b30c3e0d9631fedf2688f0a7ef711b8424a9834195702a104313e449e420b830d909904d3809c90c107a340a990c90792c0b190b904cae9ccfebde53239f060b1c619892910f411822347a81d438499221dc15321bf294fed7ae064aa2439e23734ae74390931ea5081184a2c73894f9c13ac65fd149c4ecfc418c1891b7a0be1e997cfd888cc16c978853545f2e1320f91391b33443a3a2b1af085aa322ea321f2a7eb788a2bc52942e994d926ef283b3cf371d998768c2efd3dbec0e0c5c77964690035c463eb605b511802d92671009d3c59135ef3566fc0b8547cb11486e94150ae8478b867f0212a323b91262ca4c78449e3c4ebc161fc9e5c3e9d70324caa4c07889ca358444e0af2fd1a721926a3f030588964fc8ad89a42738fb106b8fdb36e66c88ff16af5b16f9d1474d9d2188ed4d00ef01ce01c87ce51497a3e7a7673b620c0540f11a3a0b31b42a6432432c5c9b31bd65885528835a559c533b58a93db435611b10844edc73e7451ae5bb737dd9c9e0fefd6d6057b2da5be809d5a7175d55676a9a50f96ead733fc13410cb19c1ebec6419762de92696e263eda3e90803a7d41e83883002695a600cf802ddf277e216d7bd565a04a5696d4dce36add6aaf46738538af1b86027781a891f64cadb04b6fa6c06424fa10bff4de4c761a73fdc2818895a850e94bf81a15f569f24852a1b997933bd0a3b4b43fac0fa481f2dcf6253f0e1bea663077f5b45f84df4f00a17dcfa8e8665eb451a3b792f4035d5f6b6ed598e623023a05873052253d70c11f85a96ad991df7ba2251ce453b114a75de9d1a50a9eba524bab5fb56ba449479a8916947178c072852bc88d49fa2cbc08627f6864c95d908e0d60d0dc7d3de45d03b5f2a01f4d5a4fe167e2abbe2ad1ab1e200dff3cea97bbe6f2e34d27d832da954b120d663e7ed1f0ca66206080d0b9e843611c2a987b8cb2fc5e961e36dc75c96606482747626c0e86871e711d871ffd87c764d3e90f0ef202e79ac30aa39e0c25d770710da53d08d6d9b01f58462ba906371bf2e6c46946ff0fe9fb1a46f92d4e49eeab322255815aa8b0d439a63b9abd478e19d26a4a5b69a55a4c5ff56bfd5a304c2d5b4aa131f9298a24cc0a90165c42f3dbddfba6257a0f950df9ceba2c2aeb8756f2c552d12550727980af2a43daae27d2fa25853c272ba05adb8e1e6b0802e3a8bd95e0d3624ee08c858f44e26c465d1a2177142176bfbef745ba571c0b5b9e96ae6be058066d2cbaaf2c24d9b4e48394d709677feabc2fac60762a5abc1559d46326111a440aba7af6b5828d3c51e872288303a0c58214c6ae2da920bfe0c527f5a7967b16c679d5fbf17a4032ebb55cb3f866a3519258d885a633c75ba0821827632e5cc9bb26cbae8a6c82bfad6755e517985073317e9b4b430344e7dc48b34134620b3befecb2f1a5a230efd4dd29a90c6fce4d02f24ee8a87d5166f01fcf1de1b4481065fd5f0575b021465957a554947b8d3099799751137b745bf7ba42b2fa2d92149b438bd0e625c4096538ba05e86176b739482eb17afc09016bae0fabb16001ef173411048f15dedfe3aeaebc8ee6ae3a31ec66094f9d68fdaeab869e8e0e5a16f81e97abd95c210ff45d44860d9ca73d205b44a3993739b6a09a7e53d9328b740602df0dbf9e612d3802f286141ba410e80f5e403e584533e872b3c519ba817412198400abc141828a3e697d9770fa0212e56aebce424c3a411971c71c9d71619e2a1d6f8434fdcb8ad0b2e46220497cd7aad5153696d80e44d917b68e2e8cecf87d05b91072986b099477678c9cd3bdae933746bec826095fa3df409aa1efa2dc744f82a145ac36d6ef2337c0bbd290c9a8e370574d7504895ea9153874dc5083161fe9e3e1f6d8b8e2aa757d22be89af5364354cbdbff84aa7dceb448fd058dd5c64494db58f4878b8aef105c3425f341f2e340a192d033c0065cc0fc42c695b815198eb2dc6ffea77254a0e19c342194f79f25deff3fb7c2f7e5e10400bc90604e8f2f9cb33bc5864bdcc412b0f1f0c0de1ec24ca1234e0155156e5b33ba8465f375fb990d00708b7ff6986a28f0805338927ed8c520711cf935ef653b539cae4d3c792a39fc59ebac508355d2caeaf3129dd447d0998642da8cc94901b016c2dbac8d4f067f12632fab5c9e820e01cc813785da82dc3c441d9036c1d4e3fa01131a5c2d5789fa2f6ce4c90b923fe4d7ac7ce97108caed728b6387527b9e6a5699bb06a1c66eef972e7e97d6080d0c1ef6b5071c733b44e868b628f42d0def7ab34387c173122020ed228c189948a07005dc77d047d55d54abf174c4ea610bbb3916fd1d9a1d3cd72ad88196de7314c58e9b6a69ba02e109fb3f27d010ab13d0edcfb1c51c76c1ab60caffc8be9ad02b08fd80f12f2b5074a0169484ef39022880bca655dfd7359ef68ecb2ee73a46c3483ccaf94579f8575a39868588139a94d3ad30b17f423985958a41aebe8496ef03027bee2f19fa94fd65cda7d2212e408bc0afe1a7095bb850931f0201f9c1a6402d48324cc526aede5247a19f8f4b5ccd8289ccb0e70d1459f68deda3498b1e3c6e6e95343650835c6dafcc1d988187255a3ef8fe69c8434a648426375c7adec2a3d230a7b7e3c5384d7dba7b7299990e46694edf4c1a2d265445829985f16c78e93f27aa321523967021296373c81564ca5f3153b2b29d96e7b3d2a689fffdd58a9419e1de11e8179a037189ed7c8817005a5cd68643eb5f3d3b485c555ad19bf4421e6bafde1017aec14a6958bf597e46b68c51bf7a7fb730391a8c730931a15704f7279d115f5ba137f8a185626ef1134ab07d6c15fa782d1448c643e65b36535e8c4e5e0e455e3e1e2fc8192f11ba59b0169fd1ef30a0d9a71497051b82ae4faf9a2985dde3ab41412fac33b7d3de76745d87a50e762cafb2c1729117dce2143382d88e19f03d9c6324806139effe487556f21ad022013a7920d49ad677d867e383e1d89662bc0d9382ce416e0465f3ef012d653667c9dc27ea52f8fc71a70b57fa078fe28d67f54e0f053a73cc6b6b980912aa0db1511b5666dbc691df377d82efa73169aedfd808b126db21129be1b57f9add48d058a35814a6c4a24ba285f7b4b23b0488cb5bf75764e86f87a9a2e1a0a2543ff5b2dd0962129767a4178418677ea871a107d5680cd1c86b5294d477ba51faf2f0970c74d6b33f40ad7b8252bf63b2734613fa49687d468bc140694c257504a2297d8529234307af22297d875f1d92aae238c898066bfd7e5363b7ac58500ee35625e5d82c392d47f50606f02f58ac5e19d57811436373d5edd9090cacb2cac0a8ed6228eb322a0263b58181e831a510eed380e6c8a192c132d63386385bed725fdc3156ee2338c7b6e8225059effee196617d95d194019661dec72182e062ffa9034ef2ed169f1082d38dc75fdf187175ebf86e78e50f4a697b40f17177e1749c080fbd17f82bc1c4dd26d15d40040010fdb9225824a322969882e9dfc0723c4626f3010e0b63954c8f290c70bf8a6f8a742590492fc640d2491700ea62f91e45ed871330199e7c0f6d8b42891a1d4a796366e216d33d2d857e7e1b5079763c880767f1a9d3052b722bae5440960c57f9b408151a381ecca375843a78502addf63e74438811210ea8b7f23b88bce415ccf0709700247c571d858f5ded328b40593364251adf29a646d4c5ff41e91b71123243917868b9c2d0be55bcd8566058b71f02302036247ebab5c007a480502c66bcb101807e672ae283e282978f82f370f0a62000d91cc30e06c9826aff63deb2a35271141e92e36230ba50b2e78f9035ed765ffb3f6cca235efc9da0ec95270c0e2dfe950eaa72912f32e9c5e0c07b25fa11ca21ba58387cc3d0f2ce8d2d486c9017259093adc1986584763094d6dbe66bb22cb53ed257af0bbc6e3859702b6365cbcf5d2650e00509d8dcd7138276e3414f48a0e013b0828449b32e1cd8985f1254200425507a4879202c400942824afde6033ffae05a1e88b91d815f59dfe0e639d980b41c19f658c2018a4382c20a790b44de48a55dee5448d08aaa52c902db31a418ac460b5a6f8d54035739940466b51b4a548d3abd36093ee91067ba0f3326724047ecfa0c5e641be7b7ffad7e48600ce055f9910453a4eff0ffd62d2ce3815ac67029842c6e12845c9b184584242b7a3f8b3409c4baa1ecd1d659f53e14204a6032db711df90f80a663fd01f26ab59656028c25c232d0066996a044988dc34fcedf1bd0a164d1f68b3f7b79b262dfa07cad5c2c81e49d5d1d8025e0ae7d0c5b39dfd251fa89448f5982d7e911104b30b73319b0606d359c1e3d2e0171d9509a26ab2f819949d760603b624a076043dda531818db5be4cb4a84e263281ac010d01cc889fb7e43b6a37f1de78624d221304ba1845cbaf39abe55c7ed96582cb4941209e86e69f4688043f35c0f38edc4c10994f224e7ef5206eab0384aeea58561e510a45fceb3ce8d9a840fe33812b7dbe0b7e970071ef50736d76a4043fc38e6b6c5cae127c7a1d064b97bd128c55f88152a761090474ce6f08f02e97515488fe2d8ed21d8fb96308feee47b65dad7580ef7e83431c2ff01ec379a3f0a1cb7a67943be8f4ea65de63a74df361b633762089dd01b0e8712e2c4c4a575132260c8a383f82c1c488b2ee48b2f1b8ed3de6b7f50f839156da32c1e85425426a3af6ca62dcff23b4143b1237149460e84e6ac09a568adfd2a811de718fe44071712879ea474ccf30208ee2cf013e9d6d0053de0350803f80cdf3071048337c009cc9c9aa8404a8e8920ed290cf9b2fb91e9a92f501862ab3fa5dffbf094dae6ce2774faf5a288b2156b58af665400ca837c197273049ddbe2659565e3b5fe2b90c1ae9dde7ee86fb7d1f0939ddb0a9e6b8111bae3bc0e3895e647dbb9788bd699a9167d064fe55e8ce31f89feffbbdfb1f38827d9648df4ea2f2de8e1940beec0fe94583964012e3bbe6f6a2c3e3841d4a950ed02c77c71ff8c847808ed8c27de65c082367259630a32cfbfe9dc2bc061b46cd3b58e7d2cd311f8b7b0b58c3e2d4a04693e753bbee7b280c8dc7ea7f300dd5e89f460278d3a811d630a95e23205f3f19a708d7eb6f2b43fc0a4513f54ba920be8299e481ad762fdef0f1126192fc663439981bf2be47f7354b4feca29b05b3e9435516d36989096836e17c311d315a9d3a313457fa3ed4cba2e2b00c462df89f3693f521fcd636aba17334a6ef979682353d81855bb62fe6da8012aef579d29a2c8a2b1097251e55d0936be32e903e51315f8578f5aab746cefdabda3c4794a78c7083489a5d42c1fe4b46225c520ae1b0488d069b25fc955e43cbb8cc5befcde55bfac8055ce486255cd82d0fe202c8d530f739fb17398a43508afcacc33e8b1c122a2b8138b66d8b5cac4f074c98f1526426a267c7899a7cc8f361d062039b13a90a893746ed27f7fe07189f063334b65665f21bc69bb7089aa0d9a83507688895b390a2db15a00f5882c23240e3c3937aa7ec0b117501760a4a46114ae7cbb1c0b060c591ff58ec358602a7b971f9e8fe3cf2ae1d6b7c82a061866c5f16a1348a1ab64c57cdb1e4ffec3a16335a5a495114c65475a7e2985e019c10a4f1d114dd40264b311f711152c49f5ddb790f9d575d0625537851d133a14ae2983646c7432d472d888adad161e9ef1219b45056d44b5a2ab051c09e1dc49d6186bca1dde4c6b9f38d8c0f1b5664f69d57295aa4d38005874ff6a0d8367ce30e4ac6f3af5b05fd9ef065b837e4456bb163c3eff2a695dd77e3530668fa638e1bf41d7d348801204840dcea88cb96ace4080eb4a00fd66be1ea2776dd6d0867afa2b74fef4313cc407e95cab6bd5abd949740dc1e0905d618c4feb169b52d172cf8c8f23be304dc2a4d603957b3e77f9ca8a090fa21085780bda0b86f1b185a899774aa0088fa42d5a5046344ec07b6006ce3b639e16c91ca498bdb3845b288c260c593aef0e043b148ec88a08a6c288cc8a3f8e81e3bc03b5948c3b1ae07555a6a775f3733f0562636a21049666a93474fc3029dd95221e0dcb08f29f003fd563225d1586c061f6dc50fefcac62071c11dd9beb91c08190b4436432e94e1932042314df657d0ac80d0608a14a2cbf0901e6251837555a2b1237ebd7076f62782801d9394b9ebe1eb92468dc3d6914129d62a4ef3f23df90dc69ee06a58ea8d589c66fba6a30af04c9a0551a7d6187ec7613ad51cfc962f382bf8ccdd312c10597812a9547603eef50eeefb29207f5459739b28c70acb9b91b3cd428d0e777a8b30b08da53543e247a60cb6c30990a174bd46d229130dba76e70a4db44658492b3e20a25695f273e76c386deb1c509abadcc46815864603bc3c46cd6d679ca122e66ebb6a799e62a91e8d08dd7638502071b1e32064bd951a474408b212edf231ffc1abcd3c904db9571a44a10a10a0e176dc9b2988cf26f7f9e0de1d62a9921dac7b96fc112ff1d40c903b417712d6e89f708252fa0abb2dce7a38bbdc934c6fc89f4b9fb9b6702e2eb7e55e08b1fcde21a1aebcb6c50fe29a6b5d37fac2ec6c525e9b28f60c8ca78f10b4ef31b38ddf7953df13e9d5365c6734fd37ea595cdfb1dfaaa53a6070b2119a7fb903f3b5ca63f495ff84104ba64f24bbaa2a86082d4033af5eb68690f7698172b5397df33d8d800930ac55ced19472f33fe7471a91cdb17fc913b0aadad2a129c35177c9f6b03d7e84251c5d720392936431f124abe1f6f43f86265db43e78eb1d463d8bbb811aa625ca6f0e8c2220db07cc1dfae3fbcfaa43b3e9426172066a178d0162dc67852833287405a51e6764c65340e5f5915beec8b27fbf5021683d4eb939a6a538f640d05e7554d975ef5839f4aad7bf9b13175ea73018041d8f34e2c9e1c5f4eb4e1eb051cc050521b333eeef7055e60045c073404ed7101dbfa092df823e8c8318e0d60e10cf63ed5b71657243cfdcdbc3375cfc0da01f1518fc62adbd4bffeb72537f169587a8aeedc39172b6a2ee402e1d4bacaafadf5d8bf8ff6b075e94ba0a0b733b5e4b65cfacec4d78128015ebaf5c075fedc111b6fcdd52303dbfcd9929bc615dc2f6a4df7899e83918bfe2ea77a665e5bc2b1c4536508d6cb3fcab1764a8d9687a854f867f7035182bbf590906f3254e3bc160b7c46de98e67ea5b78138951a06b3652293f39e5ac3fc720241acde543db4f004c90a702e9f384e5a7210ce1e9a036f90ef807f103982a1b8905159f6766c5da6224471e02d24e2c66a00ab2e0c631020ead141520b55593c1b3da4dc1ce15ea9dd360feea6ce63e9e1b97a0f351dddf387c7c67538eaa9b32dde174622ca7fabb9ad6d628ea0b08d42b7d2e64fc29ac8c2b1b0e7bc419582bd49b1d993d4e43f558ca3b221423ba7c06ed265d641db47ab29441f98ea66fc913ba9f4910fce417d4ecf2f45aa4e874c482f8b375699fbf699baa5bb2c50415d14095edb00fdf88972baef5643e22e989ea8d7c3c7b5080ac15f221340e68d46ae1875f5eadaa3cbb552ec24470d4f68c83daad9d03cb37ba3239a32ca7496f9ae19c8a88d2fb540015e6e67f967e456efc7fc3b02efe5b46b9a725bfd2a76783d5065aa06c1aa8a0d3de2ed7df55422da419b5e00233d3831f4f06a2122ea4698a7a128023ca5a62353bcd6da46ac7a5020bbf24a4b7e8d5dca25c862b28db2f40995829fb0e0e229b34e936844e72f15ea952a7830d859bde6b591e03feaee3320255d38c52d55c953f80b89d4aade25bf5232dfca1ceb764fba2432f8eccbcb221bf46344b2b96272f565ba0abd702d4e1ea48fd6355c211c21aabcb48ab3fb70700ee9523cfd8c30a4b88010876b366e134e77b94e39e866f6b708d469569083a95a834e29a593a00be99cf1e289b77c24949580fd58e4a0f808e85c7802d7687c483216e8124a6385cff3613b8b9b91f71f650b444f124e943524a6e966c81aa08e08a03be443d21ba36264741f537175f937c6b64eb30ec117db960e03cd0f1f0c7af901870c42d90c45482eb4766023737f7a3c41e8a96285e6c6b61205471e01f6175160349f847daff2b1fae8b12401649857013d7c5daf6f61a181c7036b5b2581ed80063a08c5d8734ff1d371e6b40c6db2062aaf635d793c0e395ba4ccd7d10eec71a0e92960dbc11dd92fce03ef371f1ba358ff3121335351ee20048513e0b342b2b46fc55d9c6b2a41bcd6f4507e66eb0c73ac09de7b86a3a81f1ab2bc1e16eb35eb24857342a10c913b32dcbeba069e204b7e50a902652011c35c21d172578de7af1a6883ec82e7c7c1dcdc3f50ff1e2bfa0309db345274211e1d35a541615d79a938b667dd1a12d4547aff56b08d3b38aa93f8d1ec4661732315ad99dbd6d37b08f3b386e548edc8408c7362bf4bf65517e037df26a6dbb7690af36a321968fe801f49c2e7a19e167edfa45d0748495a0d8833618d10d28c6a614a0d9b35f9ef9642e7fa5176b5b2ae6b04c9a48665baff7c0a4af4ea721234db04693406eb410fee88a2e8c241da043f5c303c2767582d477a8029680d3c3283aa0ba344a38eeff67ab83ff42fffaa7acd89d87970b82fcb6cd06ec4125e48801e8c193e7e42aa9ec792562965a3aa3edcb8841d11d332fdfdfb54ceb2e2088f51806d26bf06c1cf8f96bd87a559af931ba1e57edd480f42da2e9fff4f4cea6ebef3989a3ec115eb68ddd2edf98505dcd2cd9f1e52efd15a528c8758b632d2427718290f25f6a283bbbb9b037d3cdb9a91eaffbab006d15f338a5bb7346d087c965b1532248f34e06fe1d081f877272b5b055b45841ec83d0b3644b26140c732c6b5de6f29a5ca55440b35e465e85fae89b127f01677eb53ec63959d7305e31ed51484256d2e02b31424a5a5b5b55170d13e250a71957afc833d6650093aca634bba530af85a569868147570f97f5027cab6c89c3edd7a88acc1557aad8fdfe808aa7034d6be87fdef5bf5978e00d04cb84c8367c0605a8cba6298946b2d97a6f9ec19dbc2b3c37322fe4b4fcae7d04aaef3101bf14851c38db221a7b6a467c7d200d87eb83cad9ff8fe9772596977b26fc6ee300310ab901d3f62fcb0745e6e069af1c0e5a2f86780fd96f5e57ef64df3f17fa105cfb45de6b11623a387cc610df5b6773a019fd8f3a652e50e61f8242a847e813daff838c37621f9a3e0a2f5372cb1a4545fa3fdbdfcf9560b9de8642e0553d7dfeb44cf79e7f596bff867043267631ed66b6729e920ac103dd4b488ba1777636ecf73af0e3343baeec23bb22d8d3c81b6c77c8f169608eecb4ab55c3d1500f09268ef421e2ef8f6f455bc652f8d5ac97f92703dc22edbf38c5a93c9828c41e526cc456fd3d8136f1e289e26ed92e0009dba145620935f112b6acd29b0eaeaee1bc0b547a7a1d5f7aba686e66446d5ee4d006110e8e285eb081b2098cbad284bd8916787158a07b2a1ab5438e3943b000e9429a5f71564e070280286e7ccab10a9dc3048de7959b7b971ac30c62cd2869522efeffb0457fe2ecb4ffdceddd5e60583a23043b1d1125594492a19d6a4b2a853fa64b1c516ceb95fd0a3e512fbf899e783f41c3851243807e0b70861c5b0909acde695cfd970c889230923e9ebb2ec1de06a78b9eafcb499bd1af107d6b8b4046f9e2f6f0ef7c51a5f6ab16abf98cb906e595ec53009264fa54813458f841ed7010d028f6c488cca278d26aa594b6ee3ace126815a5368fbe86faa7de26b1130439c06d47bd66769e7e0092d601f6094f02a5ffeab5042e461a57cc594258b9000afd30e5981223a32feabdd000b89dab64f2c030959a601b73813237cdd36ca8d163d56d2270a4e050918ef25ad4b75f47379978474dac0247907ee76ca138a6dc6c10f58a34c0714cdfdd98629691b395f9054e7bdc5642c4af6ce313e89d071c3f77b8f5646f3026cea7e8c010e7fdc1b58d7cc8abf4adbac6a68bd0a3b83c4f1d36459abf184f3aa0c94ac30cfa9df676917d81fb4cd079869bad12a4680088a5c789b3bf563930070f62bb3fdead54ef22687a75a585043348997ee555b41b922695c89db2b912accd8c45ec04d397f729a76fb3333d4dc09e876a030f8fc60cf5067ad2133ab219f95ca89bf2ade297504bb16a5c4f52ea0b4e38162a82a16e0a481231894ea6ad1619b3032b32a89c3c73314520e8a58013a09c3b8d210a1e2f2af8580b6ae5fb1903ec39e2e26f749f399d224b5ef17cb883802be4ac37a20008d24ff0471b3a6d17fcd0a490509b1c037fb0d84b1a844325b0e53ced0ef24c9369a97f7141456d58d1c661fc9f74f3be40502fb81249f6f18b7c21d76dd54e8b68fb2307bb4c5424868c818f5cc888f76ff73cebd3f3715049f096ceb1b8c94e230650479888894e47fcc752248b4d329948c9fac1fb3633ffc9f79b2b61791283c24996b1d6f3c4934f473edfdd7caa7c116f0b72617dc3c2590de3569c8771bb834c9048b1c3392884573396bf37e9380a50d699f387cb186fc86ec218378bc24b206b5c620c8fafb160b83901443a43e3e900ed934ebed2bfcad8a8c95721ddd760f25751ceec0d07a7046901acd0cc23e16be08c3cab17fd81d7b92231b3040d631fdb034aa693346e82c1975fe87bfe857cf69660f09f5780d024bfccbbe8fd6ad79f97ae178818fe848ba9660fa909deb15dc0465c588285ab6ac9f1645f40ad62c29b1946764657d20c306422e7fb450ce2b61204e35991a4c4119c01b6661926fc1fff121099aa0a94f98a74221c85b8b39a2609a182fe509da93cdd08cdd8a279bf19b0f11253cadcc1dc96df6fb3f852446f2818a625b0bbb667502ff47eb0e070a7b9aa2022da80d9dbb24c77fc3db418e2ed265065fb84212bab1e297608e16811b76f2301cacf32ae77f322846733101bc15bd1e006b7705b32b2c44f82f3ae508558679321ff9bdeb2387630e8a17383c8872bc368c01e4f3500d2292650d687a229b381e29d93823ca70db1ef4e4c00e6385fc1383403f15787b0e32486f312971a9ed5842271621becad926716a539867a0d06a1c308bc567f09d7d48c35938fb4be68330e78f143b4c42926108f93000972e412f725cfd4a2abb84ba0e1e78f22a501242b9105a7c8ec4b6888ab62a92667e241f8c16c8309f2367c27e520f2e0f726a454b02586c445c036a0587a6bdc232aa74c3da4add889f90dff6c5603aad3868b564062d1a6035935b37a227379d5af73a85f4d87b7a3e9e87e21b8ab996f3aea578bdb6d7029f30129dccc869500016237945594b36e1420213caed9e16c304a8e91490954f1ed6f37111122bbed2df7967b4b9964920193088308ad089d6bbf722ef735fa46cacec6a1c08d4dc6de8dbbd5733fbf5d4ded5cae63b9e77434dbdbef6d93794de6379e0c27f3f94db3e174381baab19273e8a268a2b6a3d476de6b38c31bc7464a87a379fbdc6fdbe6d1fc863377a3b1ece4a0eaa3ee857a154ff1f416861273c0f6d5a6ebafbf7e044749495c81851f4fddd789daceb1f738a6b4180112cc947f0eebb58a42bb75dc5bcdf78a9d271fc554e8e5539d8bd56a86655e7ed2e4230df6422f167b9fe2b0d7b94813c41e9526693ad97694e723e7e3c8c79f2a3e12f9d87df4dccedd6c1ef41d4ed96021365244704a878558a07b0ef6c1f9ce03facf77de0270a3c0c8c8dd6c4020506d6de46e5ee482bebb9f879b053a9c0d055d9f3b634aae36b9ea1558cf4b2b1869864fb186aaa82c94218aa5f311e4f9b8e17cdcf4f9b84af71cef38dd660302ddf0928f38187a3e18e28b62af243735994af1948a89f9ac3e301f5527a5f3e2a34f7ce9743a38a652294a6df7c554fc3c07f4d974626781e8e962e7068b8f3ad11341f163e4e04f87e3d7cd1d79d015f9fab9dd57d7522e822f546561ba958be01421d9dd10e25681859cee45447e04e4f94f6784f3756a1e8ea7f3d5d2389b91e78c3c4a044d8d235265173bd773412357a4bb1c10ee55c4b1dbc21f824fedd7a61cf1dbe42318db84638403b6847abc3c5a1ea73bb51dec0dfb61231061b8bc638ccde1601c8ce3b22c93d168308cc3b88ccb3259c6dda37ff48f2e858defdf52705ce56a966d5b268361b5ab59b59bd532da866d20ae8b279006da4020d006aab7733977d3e168329de7783c1dcf06d3601a4c9369321a0d763d17eb5cce8d3131155de2174f9125a2624bfc5c05021eb051606714dd5b2693c96459ad99cc56b7acd64cab64b20dc3188655ac66321a8e9aa94b64ba557ca4655e975ae52e91b949e4b8385c546bad5acd6addb6dad56a37bb6da04ef3cd6518c771d937863db7e170300ec6715996c96834d806e3b0ac3599b6da06fade3a1dceb7c7f31dbbf5d66db56d6b4debeeda9b7dee6a5f33d92fe5945e3555a3c9c8172c1b053c78f0905ac3f84883355b8665b07f9d9806b3afc13459959aa66aaa4693b117eb4f73d96ebfe056a1a0a56829a6084030b1aaa3b02da82c936d994c269365b566325bddb25a331908ace72a95142d858a070f1e3c340087294e5af4c97abdb2f31b051d85a57f45717d9facf52d850581f52c958ff0b01e56c25e52d896d1681ec3300da6d9b64ca6d62cc3b0ac66999a6d59d56099c582d5df340dd36c5a26b365364dc3b05ab32ccb6acdb0ac6a35dbb29ac95a0a0f6852d3bca6d9be7e66d368aaa666329a86615956bb9aa99cc66dd8a6651a48d369323825c35e6e32cd0bb9b1919c0d4ec9b066c39132e3641a4d268361d66638cb325806c3e4d7ee7e9ebb9eafdbed70be3a0873b40775b44dc31eee3b2fb7f77ce4575941b54eadbb9f07719b6b208d03e28040dae76e9e4ee5546d73b9d7dced6be66af5ab3cad98463a05167e9d9adc246623a5fe865330cd6b99ec378c935fad2cab97b6b9dc5b8da5d94c9d9ac5acb55cb6b9683c5a8a46816d14f098579471820a6578f294e757086778f4ad469b1e603d7a5932d4e22d2e9a8ff25145b1f8a8b29c7c544f292b3eaa194ec1b0afb8683ea52914cf1b7c5e49f662697e4b9e7f618f7e79be853deb92672acf973cbfb1178aa516580fae66953960106db21e35e5f9357e3b61e9f78def2ab034e246f14771ff7cb4fd74c9137e7de197fd657a0e93e73bca0d56be429962c545703e9cc2e42238a75071119c53945c04e70d625c04e7bbc7e459ca53254f539e2b92fa88fefc1c7046fc49e10d38004d5c0e30347f3e842610863ca845f6dca4c54bf6689edfdb103147ec2b18c47c7adde4a2f98d033be79c0f654cac439e36548d37b022c708557f83273e0815caf03cab1c3f0a940d4cb936fea10e1676fe6188ed2fc30cb5feb49506c56d4c9bd5c6c499361ad44a7cb9d9e096b02eb71a9eaffae6f972f4381367d256daca3279c07ad2e505d662e1a4324eca0556a252397e9fc0ae56311d532aa9a8984c7125ae602b1fb98b4b2af5f2a252354cc3b8c9b4b2723ab1b0a050ddd22d3030ab158c8131a552ab7854e5a3f842513e7a7949a55c5c28eae484526bbbee3fb2d8b096894ac0366762a99d928f52aec8725ab13aa61c330ca750ebc29d928b624725c7ca8959f9085bc1f8088351f90853bd705e38294e0a7b9c7f9fc0f67b9c940d6bdece239d02cbfd9b34258aa344cec4497126ee0b7dc799e8c79938537c6db59db495a6adb4d5fcb24bcad368bb740c27e541f9a87efc8eeabdce4beea43a2e9dafd302b3eac4943c2a393e27e5a38a516450ea947c240396982c8313672583150e8c0c4c1c950c54382f1d958ea9b3d23975583c2b1facd20767c00c49e27f7046ccfd654faae4e722179ab22f9dea3fe84253f6a553bdf5d3c272e2504976295d4a97d2a574e9d2dddd5d7e40caafe43d82cfda4c15caf038af39f928e4f25fd7925d83ca1edfdba84c2cd9ada9a24a28140a852a954aa59266cafe1c938f369b50cba33a167bda705628b5b6eb54b9c6edc646b582b131abd2264625964cdb11d96319c6b066c545a82e596f53426d501bd406b5416d50d9bf7254382a2ef2e7947ca4714a313ed262563ed2562e72181f691b1817b9ff46e5994dabd9a6e4a30cb69ba55a67eb7759e84b36ecca459ad4460be740919df344768e13d93940b26fb6c8be9943f6cd1bb27fdda83429a796b7891b958f2089626f0373616fb3b2b0b789c9137b9c92c41e4793d2bcf8487b7f8dca479cf7d7c0f8c8bebf66e5a3fafe9a181f619a26e5a38d4a93cafe1d85dda87c94bdf6147bf4c29eb56f61afab3fb1f772b3f2114a149f47e112452a8a97285451c0d85576f845f165ceedbcbd99aff56e5826636be76dcdb4cbe1dcce63f6d60ddbb08a3da7e3a387cfbc4ba524b00f1cd6621f991ee4b76d25fbd90fb3f771fc22ca2df6114f5bc6b46d32e5bfb94933655299149629adfc335ad5fc350dfb70576daafaa0ec344dfe5a57ccb554d4da4ca7fd35fb212a770dac5792d16a60e37bfd79b719ed9a5fe2a30fee8f528bbdee7f4e29a54f1943fdf2e6b0f16d6846386035b809eb7b78a67fbd7cdf24309f6225ac6c61ef47b63efa75610c79273dac3c8570ea5395bb57999861428532babbbbe5c468675ec328a59dbf3ad8dde964329a26e5fc86d7e45490ae3a1f79ea94846993a22aeba3b7b6d4973e53ab3c1f4d511fa0edb98e73dceacb64328ba2a55a27a59f8bdc478cefb00d944d07466fab0ba5a9b26b45e7e583a2be444e172e7ee26e940b92ee493e5a96cae3ea3a796a64f3ad7251c80457aa92a94d5f9f56d467a9ebfec2b1338140eeaeba4c2911fcb3d4aad48a964aa86852c59349ae3a9dbfb09b3a2a8bb359ebfb8fa322aa5bc5d41755abb83a95526e922a8de62f2c4bfe16caadcabf5455b92abb4cd847571a0bfbf0716169f998277932d11429a4ead4a7e297e1ee7314862d2a3b7551fb253f899ad2871c5428c36bea235a45ee8bef2e8c07dd7fcf75f1dc4da14ff29d9bd27d8a7fe739a0ef708a7ccf6f386b3ee8353785c322bfc129d6776f6f0ae7399c72bdc85792cbbdc8b5df5dcd83eee63d97e3e2ebfdd72e8cdfee8ba737857b92f78b7d4ae7fd25dda4701efb7953a866f3f5ad9b42e9ffc4299ad7feba2912539c627ffb4a72e96ff77aed5a5fef7cec4aac6a1bf61826a1cf3ddc8eb8fe7a6bbd66b391cf91cff1eff8772ae0a32ce5f34ff21b4e017d85318186f1dd6b1f1f05cb7ccac3c0292227239ec74a3c8f7df4803a11ecc3be0793c4ad02dc11f6af8f1bdc8ed0bcf571bba1796bb31dc179f971bbc139a2f3de79c7294226d0242129d93fc90b11c129f54130708a07c60b21c129da772f6c8c3cc90bb100f69e878153b617f9f83946fe054ec93c0926b981bdc7054ec1de83bde7e17644e6333ff2703b62fbed451e6e4768af7d07027db2ffe0a8399258a2557cf4f90852516920a087f1dd05d2fd8b17b94044dec58f5c20234ff2f563687ec3d97484dcd83cf68163601ff635bf790e87f34168903abfd9705ea3d9bcb59ae7b80f4283641f6e35b8cf8340a0efbaee4544447e64e43d9e11cf631fbf9ef828f331c6af808d0a371b4d5c9526f96757a581d487f1da05a2fd8bdf2e90ed5d7ce602c93cc95729fde7946f59f4faa731aeb7defa697d101aa4f9727e101a24c73f1ffba0d70bb971c5c03eae6fa24914c60d4243a549f55fdc203448f5b5abd224ed5ddc2034549ab43dc90d4243a54999c72e109526613b3aef007b94d87979393fefe6adabf9ebdaa777e4de7befb51792b80b49997b82d2001bb702d7b5eebcf2faedf8387c844245ca45f085aa2c4c47458c8b602e29a131c6f8dd1154450e4a5440685261087e37019ad07fe8593802d2440f215d4fbb3722626eec261844fceab91f861a7bde82f99eed948b62134c9448d785a4eb3aa9b708d80bcee88fef2d8026f20b30143f36168d45b7e4c6f2050611ffbabe3285f5da65259a64114de4cb68e2fde80f0631dfb2eebc5ebfbbc432fe98d1bf40c5153840e5ca69be0d017f7a9d657294274dd86b11b3c8e4eac2fce394a7e9c20cebbc5efcb26a0291a4e138af274dd9bf0611d74fac83a47eff7c587111fd137700aab28eeb63fcde86b87e9b3fef10f0ad6febdbfa71cad6f57d3b67d793d8f564f66216d93f5e0f09783ddcfa5162fc5a05492c2aa7585a8931f5ca9d257b0bbdf3da188a596e36967091ff12d646138748d0c6c2caf768d66c97ddbf9fb0be49eccd8743ccbfaceb5d1809a8ea1a00bfd3c94a8929f5a2513b2fcc19177ad085dc486c46645ce8011772d33c53fd038bd9117050fa22ae25117e3e8ec0252e8a2739c6ffef3a6b5d1429253942c288a7172a94e1651a05d6939f08ac5f4f31ec95645a4f5e3d4d4db56df3ebe9873a58eb5eef41d5345dd7f5e579410cafef6ab95017cb75ba562ed3656a41b19c2e13f6ae954bc5525d2a97ca55ba4a568c1563adac950563c1582a4be5a2f92a2f59f3e4013be72966e5a39aa9301c577ffe54f9c866e6cf17fb1c97e22c67b7add69989c953cbacf2ac1a4c9e9a2a4f3c532e9a1fa41b7bf325cfafd325abda969976db3e53719da7fab366321c974d171f65d3e5f391f6b5f8886b41a1587cb4e1c932e769ce39e79cd3a735bd789a3f32e7fc111e3676fccc89c6112862ec93a5739e2e6bcacc64ed4cf9c84bae9aa7799aa779a227d63ccd7992f3344fdf07bf930ff646f33df642797e5826ca47242ddf74f111c905fdc8edbe7aaec857ff5ccef59078b0100b78fe03faee453a1f124b23b9a01fe93c22221ccee7764058f711f9701c05310eabea541fba5ec98ba7dcc53f6f7194a35a4ecb51d7f70a65a15012854a398bb3889c4456444c9c14159cf2c1ae4272440a07536a6dd7c574dd0bb9210212e9de23d279917ff17ca793ea783a1e10a8eb3a9c0fd2a48f08ee308813719b4ed6699ee4e9b4a2bff2f86191526bfdbd92799a273a4f9365a266cb74c9f1ad9226622654ec2b766b5cd6ac2cb0f4743a9d4ea798196592f31eb55ee72847a19a5e31254d1b8de5a48983375883ad3cc5cb9a328337ace18a33dc2cb0fd5e5c81a55fa3c6de4c575f0faaaed82c39362a7ec501b38ae952fc9218638c31f677e2f21e4c752218650a18942954302b1853ca5e9bda24bf4db42c50292d9104972389254a2a38727c5470e4486289d2c702fbb18e5c2939940882945adb75ffbdeae744349ab79d46f3dd8b8c683edf8dbca647467abb01370adce030cbc90e4f0db65a0a0bf35b27c3f16c36990dd71ce7f1743a1cced69e4d7fbff8a8d3dd1ce66c9d0ea773355f37ae7138edd2dfd6d96c9bfe342e96eb1c367a382d85fd78d2907c7f8f7c73df5fede546baae1bf98e64040bb1c0c86b6cc7711ab85180a4468dbb24b7fbda9b05ba8f231cc9ed46b8cdde4cb7f9dc110e58b882145a105edf2b8a2d3cb1c4de3b1ee934f64734426ca47c44349ab71aec43e4394e44c4dacf67e4e146811bdd6b9ec31a6e8abcfc9a61147b0f7a4b73715cee8a60ff600c71f55ccd07296b06d044660186e44b2ce4d3140c42becb0d029de388b456866025768241c84789cd04eb65a52cbfe57b59497e2fc1461912ebd04e316e69e0e66b0fb71adba3c4cca3c4883d7c09a5eccd01be8a21f99d230f2cfc78b045a62f5f6e12904feb5fd8d3fcf5d6edef4d02f5afdfbcd7713a5efd768cbf0e9f430ccd4789b0c3d9e0d8bda51f6baddbeeab04675c3fbfc28d88cffd49c01914661df4bd9ee1efd12168644abb046738e611ff2dcd2da2fed5df5dd0fd5ccf456181b931d7abc4ccd7cbad08f8e5eb33a55d17134db09f8de18ee91514ec6798bdeecd8818597ecf219ac89fdfab68425f6e35a89cb0bbbbdd728bba6bdd4d3ba67367af1fa3177e3c88c9bd610f47eec7bccf1d69a4b163646a3b388a319a7cd4251a4a02ce80793a0ba55da2b8b1f75bddde3b50e8dd41e3eaf0cfdeba9ebfe6d953ed3dbaa38768df377bb815e158077df8e5ec6b3fd6584767daf4e3011d68e4feac9f3ef6955e8fbe07f3e09172631d1ecc439e1026c0c64caf07f3b0804cfda33ba5fed1bffad5419f7ea4feed1f377f8a7534ded14372127006fd4a432e9a2f01f865b77edf1d3432ed414b0f50dcd7eb659f098532eefb7afd1cf799f7fab1adbfdeccd3cb3ddc8ac8b08ec6497c997b94c84427c0620f2841b3fc1e344b2f1437237cc8902cb98739f3bdd5c8706f6d0ff891b9875b0d0e271143fd9db71b9f88a1f9dab56e5c8921fa5e5c89a1f9d11443f3fb0996be174de8cf8f2bd1e4b1b7de0727956802bf1eb4e4e983953cbf814493cef3617422cfffdcb94a8ddecfd1144db09f1f9f8823592a954a27d2a4d45a0847108ae863df5b0dece94338ead59b5058520df3ecb067595e58542c303fb0d867d8a3b69bf4e763f7873a588a77d0c8f4bd1d2899be95bd2524c34a58997ea55747f6f4adeb656f655847f6f0cbd67bd9630fb71a59f628d1ba3d60e08cec67f6de0e949c616f02d9c2de4793fa8475580fbf0c3f9655f6acc73c4db6586266a0f0a00483c85856500473863deb0790ad509fe677a959bcebac2dc1de3bc1baf42ea1143631dcaa3ce9778e9b45dfba9de1467b18d14332dd221b416348beb017234bcf0c0b4318840761603cf73dd44d9f2ad5cb4b2a7572427344d918ca018410622825a59a666da7d3752ea089073ca04497fb95a0b9df033d68dee17928e5fe1e5d6eec02a5fa5fefee0e0388ed1338babe799cd0bfac87d9eb3c310f1a596220419c84fdf5411240c2b010f9f4e22018e98a41ba38d2f5704697f2c49ec4283088fe7ef8c9385f70041a7832fc58a87891e1c762025086df0e5635d8812aa7a6b683af93e1b7832a76000527c36f072ad09ad263c32b136750b198842be21082905ae42bc34f0561c8cd65116b869f0a9a90fb4615d84c869f0a86101bca0c49119ee17b8678470fc91e133106351e2122210eb6630b6c4793ed478906a850468d2e29a6b5299a641e7e7f6f9803dc22c30692613b91e5f5f0714493ed3d212ccd620b2101bc0c179d2c9f88a31c2e8a0f05155d455b91a1d75764f85baf7ced95c75656bc5762bb64726cd8eb2d2af60490bd769191bd7681691717faf97c3e9fcfe703028140201008d4755dd7755d272222222222222232323232323232324242424242424242f2f1e5c9c9c9c9c9bb70e1c2850b172e5cbc78f1e2c58b172f5e9860c0800103060c18305662c4881123468c18314effffffff2cf7de7beffd2027a4fb3114b76061afb7e839f41b72a48dbdd622c76f31e4f8ed8231c61863acd2430f3df4d0430f3df460922143860c193264c858f1c1071f7cf0c1071f7c38fdf0c30f3ffcf0c30f1fe484f403104000010410400001c447298178184d98f880b8108ae4ff702914c9f7e15a28922fe3765024bf87fb50241fdf1014c9bfb7048ae4ff3d8122f9312e0a14c987711b0045f25f5c1b5024dfc5ad0014c927b938a048fec8cd0145f2456e125024bfbb4b40917cd0ed126c152892efb96d8222f99ddb2b50249f73fb0445f237b759a048bee6360a8ae4dbdb2d502471c55a4a29bf5bbe94dddddd4c4065e28b3a7484d87b500c19c22d6408b3c022c38751880cdf00717462a23dfc9268525fcb104208bf62d85fd7cff99e83e3388ee3384eab19462f6b4a6f8fc7e3f1783c9ecfe7f3f97c3e1f100804028140a0aeebbaaeeb3a11111111111111914c2693c96432998f2fd2813e9e0e67a3b15c0360e6c2a1451b04777969f0fb8b3c80dccd11594aa08a26dbc78f56c451f69e0064581115232ac62ba289f6f1a3167144bdf88638871cb77020b9bee74ee4f8dac967274f4fde3a797962596badb5d66a341a8d46a3d16c369bcd66b3d9b870381c0e87c3e1a43a9d4ea7d3e9745e3c1e8fc7e3f17c9093ed26d143c35e43e18532ec09e029f664741696d8eb640fc2cccd468e88fa7c3e9fcfe7f36901814020100804fabaaeebbaaeeb5c444444444444443ec8094964646464646464e4839c9046388ee3388ee33efe884807fa786e0914f977ee0914f9732e0a14f96f6e03a0c8de0a48e7a0eac789c20540127c08150049deccf021140292bccb61195cfbb1450d152a94d19d219c21b3962549eeec241c1880263d03188a0fe1bc272eea950662959879c7e77e2ae2083b7902a0efc9b8de7391bd3ec5ecf5295681124318664014c58ff80df4840e61bd3e599665599665b15cd7755dd775a128a594524a5b300cc3300cc3be2ccbb22ccb3ec80929fb080408109513d27c9510693e10958e345f8592e603011284929ae0624aa58ed4c4b50590202152139e8712697e901352133148f3e3679742913f762d14f9d3db4191ff751f8afcad1b82227fdf76c0cf013f72e30f74b9479777303184227fe818be8c269e8d7a6910cef04f076fc8fe14ced0647f6babe1328e1c552a956a40eafeb8d568ed8322e802e1084507296882a2032fc010cc1e283b49368200d9712abb428ddf31c6166019a6fd67d5baa829fd14ccafc43ee4955918c3eff0d2eb76ef22582313b56510ea35dd2d2ce4729c32b110ebdda960bb27f5d5a636b5476d472d3c9f7e95d36d603da8b272581bf88f2c516289735bcd86905787f5f46b7f5e679829dcda14efd947e2b1f084779d0b7f991b0061067f6a9bc18256dc49f15f462c6903d0deb0b001a40429642841ca54022990410b5209721684f0822c60ea40872690a88be4494639f9727c211810474d72019f303f626d6bac65f85d019525eec450bb0c2a845105ebd1dc33341a353a61650c69e2532764ff2b3891fd3bfe54bbc01650513e8f1aadb01108197e6a18418e5264f8a1e10c3994e187061714d1a38955f67e5c6165ef032d884286958b56587742865f1952b9b1c8f02383135900197e63b0a23722322ba8f20e1c791545dec144267b3dce40864e8aecf56091a29e217b3d527086993d1e5f2042009090ca1e8f96548645d052692b42964a190ec9f03fc387014c9e5ec8fd95dbd17933c23f62cf0d2b597eec47f178619ed7fb4162c4f442963fbd903d1e3fe2dd01870c19127fe4385772c3bce49b6fae518b37469e777a21f746c42be169dc231af1465c1df16513d143f28e1e321d9314d1ef4f44bfe31e35b7e189dc23e689bdf84a74f6ff91efcd7e6f8e17e6be3d6a86ff6376b4436503153983337a469600d8048023ab45fe900d4588094061831590003047ecf57b56f6dcd863222b11f3063cd0b91feb64b66a1add94901938e38389c93b380ece8078c8fe562700c6ec5919c2195d5a83edafbdb51a6c5fa104b1a8b143964a50255ba6182c2d3a010d23860c3f76d6099025315c94526a454abd29a53394e9bf9ac8dca166f85961953b2e2af4d1126b18a2861cb49cc1076920c131ace00a6540c214d290851c90e0c35ba2c9280122aca77183532363087b3d3cf7676fa349fdab5e18863decb3c7aeebfdb56882bd7fbd6ee78a5d1843bd11019b90a5f6d95f3d3c635f3bc2f60cccd895114d429e8bdcefdfd9dcdd34fae9ed31737772b41e5e8f7a144e8ddc20d56831f431e47fbd8b0bc721d6ede1197e5fbad5809a8b6802df5f463481b98b269ffd437086673d73d04d581d5e8a197e59802277eecf8213b94b198e2043ec75298fe0554266f8f21661337cbfde0f4f3e49115996780797e1432fe492ecc121d96382edef0ce301e0f7b6e377d8ecd8450cf597b4505d6529b5024c8eaf65a1caf13b71de4ed7aa0c02ed878fd8aff2de8d0879c4f54c90dce156811f241f41844492755d1f3f482d7b78961343305898e38dfe0bfb608234bf49f23af6daf32cdfba17096e379c44823ea2242b632cc2177af8ece161c8229567865f15e690e1a67aea42513bfb1b6289b5f0e1d72adf6a74273efa18bf010f80333c47cec54bee4c039621e3a8bd0d01c590e156230b6d6e353e6bab61c5a55942c41124b1581346ef21039f833320863008f71558af249f54c1891cbfa40aa61cdf002e444e4d35ac5a628a7d89da58b01eb2154afca3bb46124c9c011858a9208c1fb10e88773091e17bf11b62ef8f0aab0c3f2aa43215a8709b65ad727bc08f4c2d5c7dab31a335ff4a223a5b4f324467eb2d4c73db9883da58d011fbfe70876e480417b19cd6bf930c11dfdfadeff93afce55b6f3d8d26315b3fba7b0a15f68bad5c154af22bd756d8c14496b0b3c419a830bac7eb70f3d740a5daadb8a7b045f66c34f184c8fe2e8400a24901a6f08602844632865a477ceaf12586520363087a5a9e021619bac064488aff12d318d2bc5061ec263c16a00aaac460856e428c489464a7b987e7223c3de0820f72bfbc489464d90d86afdd1f83bce4ce26b5542a955e48f0fbc22f0a56e4fe7e71028ae013140a4b4557c55b015b0a48802298bd01e4a67164a94aa5521148d024734e50c9707bc928b1c6ad461358324a0c818d6640657f4f8b814216d95f9b83b64593c06a307006848931c618638c11539199878c31c618afc74789555e987774de5231e4afc3b2b8fd9096518a594bbbae246e35b01617f94f9796d4f792dd3155f53079311895ec5930dee572b9c04061259b64f861a1c22295e11432fc9eb092a340052a9ac00f0a5bc8fe95259ac02f0a5064ff5a4534692bb2bbb45469a9c0d8f880ca8e1e92250e81f52c156d65a9a020a9b22c61a58b3b4c76e9525960b057e2afad321798e82aae92b9647f97c8f2c5d417d74ad96b4cf60a93bdbed49aaa2e2edfd7525b5ce4a8ca52595ce45f59da455b35e125cb9f5b6a4b654aeebebda898564e1996ec3f02036126f64ab614558275424bae2cd9bfb26450170c15364cfd56653ae726b15f2f7b7fedeac85efb2e9a4c0f09a80a7558d87ecf76701743738652df6538fbd0479acdc3cf8aacbdf555bb3aacd73e2a98e0448edb10316f1f8a261bf63a73b66ddbb68df38d391c0ee6697fbdf528916e4534f6fcadaf9db79f1bec8542b97b4e13122579fbdf6ec809767bef89901143f3371cf2acf59e3f7daf4dda5b97d6d71f6e12f0af8f1243306bb7621a4333c3ae67fdfcebeab0deb3795a17cd872f238e2269425546899daebfb6c3cda37429e503c1f6e88c8485bbf4fe836b8a87cf05feb591af973f4ef9c2de7ceb9560e34becf16b6f35faba73d6b0e64502fe981da750618e10c8416308ae41d225cc217b32531a230680b0e22bb911609da5a5259a78fe4513affd144de0b70431e47ea7229a78eef72ba249007207207b5a54c2ca5d7cd42e1d4d5c62e8b35ebb3461db5d727fc3ef0a96ecb98be90ef194a99732cd958b252294be89f0350cac173dba4a8e3126c795130baae40aebefd912039c44936ed8565bd6b7f56d1901a6afccf47a7e49b2dc6a582fc3b2e67c6b4e6b5a0193a7ccbed598369a5c1fbfd35dd7755d17869f105cb2e5396e35acd3bc44c0dcf2a12ae65596df9fa7b2fc7965869b9732fc80e092e5e7de6a482fe591fe6010fdb42b4b8506b025b973e5fc6348465c818ddf7d3f70114d00e0c20696e692ec1f82a3d0c3d17770d45938b2148ebc2497b0a18a4feb20b66ba7dcf4035315ab5c5d76963f82ca9dbb8a538619e62b9ec8f04bc213190930392bf9e852a92a8d17962f2e929f69d809ce809d729375bab25207c17ad775954ef23d6bb371a95c247f09db57b5bcbacab27e232f15aa3276f2517dcc05fbb0160c85b160aa2c31982cb15596584c96994a7e35c9aa92652d6529a5ac2b5956962c2b2acbda92e56725f9192a63c9524a99a9b47c2ea9ec255b6b05cee897df01a05aa854add635410a959a110000002000c3150000381c0c88c462c1288a247d1f14000c7c92465e48208b44f240c9310821639021c6201010901100100009021e863b44a12458e5a6b4fdaaf8460e73777c1960dd1da6ba16c0beef246cc41521a4c6205ab8f4106195017b60518d9b86d8bca98505e0948675f906182d560bb8530f01b00351b4834790ea0c3e5c16f85550c43dbe272c23a7de1637b90b9e306fa144b32f39c9d0bf4e088b82f839456d6c1b2f23096b0f931fa196e80ee55a339f343b47de7f3356bf0c34cf82718a915b59a302810405cb137610906392e3b8ac11b46347a2df10e4fc427e1fbc028de946ee56b8457c7498d59f6ddcba197a257d8e600729e18d4d18ad2b9c4f76e1bd7335c3c13cb1a12dbf57651e8f893b983d68c9f17ec6e42e927cf47a4f16b34aab2fb6e09c29e73e4e6e762316bb9fbbd1e4ae04f108e2d07f98e0c76efe8ad2816aeefcfd3c8b1611f89f86c195cbc117434d45ceef5b9b9631bba61a56b08261f104b25d33be9854035d3ed8f19ffa41f0053dc7fcb811a740f4e4ab7e008959a05ce3ac0aa11c26699b0a991b86a11719656b2e0e780e00885e4e592e66152c23a39084b4c1c200c8ebfd9e7b459d79a1c670e287f73b0c2980f1b7eea3959e774fe0861246da0942271a6def766b89a3b4cdc38c7ac97bdab89fa084fc2085c1f6a319f3d406766d10e4450c64c38f7f490a74f6518b243b1b60d2232480d9cfbbb7f730bfbec44513943e3c7909d01a9a3aaafd18cc7b78d7dc745cdbc629c15fbb52f54cc2e5ad3e57de9b8dd3af255c6e4ef60ba98f3c2303f09c0786db4877dd214b35adf0cf282d20f6ad8db66305502068cc56a91d5c014e449c14d11a49f92c6e0ebd131e1254102970085d30247f2ed3561482fa314cb7ad324a5fd4828a974aa9ac55604a5bb4184e235473073ab8ac77d7a5db735e70d18bf1402105541271276b2f7e67423044af952d884decd2d919049a1aca7e158a284ad11d8374ae48afee69263c006147c6c418ca67277238233fa880c97e19a6adc249bf5df1b1d212943e616a4c465fa88afac19eb81d46ef7d6926470e1bf13f177dec6c6bb793e75cf0bd63eb094cb8a043a521be0163008a08551883d1eb2e1e9735c47564596569f07e67bd914baa29d3dcd0cf73a4d929dd00683d417a5fd64c9cf1cf898f4cfba12516c00ce18abe1877e58617026c3799204c7d1b955b130df8cd41337e6bf6904c389d7df81dc7ecd7eff3af006f75a2e3970b36dc496f3a1edf7ce66063188c6b5c009c1b235e403a66edf73e45e294cfb7ab1fd97ababc45e04b16ee6513a6c7109905b6010c1a5927e627c837840f0454423eba3fe958bfc199c75a890c71fecdd311e6ec6d4e178b42ce6d9d904391e19f66b5705e1ffd96fdc9496e2c5078772d4784fa909de72f78c31fe41778bdfe3474a80bff468fe89d6efb1ed7a2d909341c276a60b64560c3eb241a51737ca32372a71012965dbb83764fa591e31c77ffcbf7a0dd7c9c119e4e4c624cbb516fe39f0f6784db7682956d313e86bee92d98567da9f6a5a8b2086092ee62a69f99b99c05e85163361a768fbffb08ef62d7b6466f253b5719aecf15a57e6f7c8101723bd34f0cddde6d8075dd603b28531452de201859851d74158888411020145d5e0aff8c80838c80223188965455c9bb1e93bca0ad57683184f11d21463c5f95d2b11e4d21e66815e42ceb854d7826f5a929c7b89a2250367600905b524a53588a1f595af9eb4550ec5249e6d0ce6981cb58f363cea289433b955c26c4a224e178954808c3875f964ef762a725659f9dcdfb097e137b7a771f542a8edc1eca8977b45932ea41f497022eac4c20296b5bfb6e50c91ce2a20f7245b0140609567b508853f6e2bd0291ad49265051dc6425fbc0afb194ddd311cb1d124080b6ed74aaca3c4b443d57134ce7e62e5bf1f25d81a05fcca7b365455ee7bec02dd0721f1cf10f890e6f0c1d2be1c3d80613136669d4fc9711a379e29c5999cde9f82a3e417a222314ba5cd23fb5b1b4e19366e26cd48411afd5e99848fbd340f51ece3d930cf54bcc03f60bdf59a0a6580a357d08a6a53ae9de1a1ad16aa979756eb28f7528e6c8bad2213fc56afc26197bdd96e99980e571949eb013eddf23f7a42cb6bfdafdf208e47675f5c1b275cf69005f9d29323949f31739a811b67313c8921ad5c1550446178859526726d0372f39a6df94c4c1ffb946b22903f177389ead4fd1708f7c7562cc23eeb03edbdf8279ec94e0a7d099e8189f3a615ab4840b58aef4fd3c354a1b0d6d4553a5ea08d90c6d3721312980c0e796246c7b5594659fbe8a47e92a6e6ce9e16f53cd2efb5a43d931647b7278346b63032bc06bb109ad2d298dc7857debf20b58573b26fda4d554dc2b30ad9e9533d5c889d8c7a854856c4cc67531287f8ae11c81a95c2c97886f36570db47499bde998ecc7458d1e4bbd88b651c11d6ec92257782710945582b687e69c7a406554266c7dfa80bd4892fbacea686374a30f29ed08346a6035333e9f07ea0467326351ef8da2b4d70b21c202f1ccf41e92632eabef714982c227d3631783b24d1e47fef819b81298a781f19797c2f828557fa0db7fd0eb4fe8290dac8842db742ad2f510075aebc2fec9ef77f9cb55229318a6e7a68877587cc71847a65fb82f034f8e483212ffcec716dbc96c99e07ded3a1fe79aceaf169ff39af741f276ad84209dea2b2b94272e3c414bcfdd82b1bab5135f1c02d6f52a5b2b68d4390765a9a1c07488d14710a8c5af514782bc105e8fb8a5638785ea56f968ac9222bed17141718a36bdb16a27b6231a54a790747b1f1ed669a985638a78c7e9bbf8c41f7a31ab4f872304ebae335e41e7e46c52dd2aa92b9cf191d2b10204371b7d434ea98ca0828760f71573126b2a8042383d88bdf2ee592c4cda24ddc314d2e3d9ba14da282502a2fa4563795a9efebaeb3b31c0d30a005970ba45cec1628fae98d5d95e47dd00a8a9e898a08ea1fd829a5448efe1a090b0e42b9504b865f35561a5feb8662da90beaefcd3c3d0570e555d079b6b9aec33cda2c78d53d4aae6d21e728cbde51fa03a4d135445ac841bbf903bdab5aaf940e658ff151c9a2126b3c43fa54722a2074d5c5ac0d39366f69cdcde36688f5b85bce31bdd0253b4effeede2146491a3a2c78ee96520bc52925ae90f0ae132bc452354ebd9864138d29cf7201328722d8e6d16b6084b757106da160fdcbf3ae89d3e277b869c3e2f5024428d21f400fca7081a2a525b0046e887ee4f2df05b8b808d2cfbf4467bf21e7b5648f0f05002a1b820617a6a46b55a4b19c49e07c8447ed96fb3c3a37ab79ec8967cc6ebaef1af01e4574d6b2d55573ff3aeb2fe9fe3edf3cdaa186dc2a6d4e8f618e87c335eff7eea697712988a29fa0bd7264df92e6b4d92db3987ee20f41a67432bd0a8c3fec6048eed393f05f49ce608d1db8b3efdb9ca7a2d484186b969854386739dee800b2ed56e6ef36a0d2c1ae0e579f4ce4dea97a0a40e0a0fa2bcc740291f366491b0bdd3c29f27422b5ca8cba47aa5f66f15d9e574824f3aa3c176a3037ec9b49044d969596811b8898f41c47d09782f492942a4fb4c7d77aeecf89093450b189105f48f67404869f4d986ec7b5e9e119f0bb86e24d6a1a2361acc647fb390a851c9281508c0283d2debcf59cb4fd86c4da300545869bbcb4ac335cf2cf5ba1fa832c831b11d6bb430eab5358ffe9524971aa5623b25d87bf203fd38eedc9fb40f510e067b7a9f6a30dddfa44fcf8692198718708251f2b2d8407eed08fe42be305344e08ab3a4eaf1c79c2e0e6c2e691afaa2421ab5b94444f90c87e17d981ab60c949c819ebb67c4670d93678e6e63196cfe9cb9a89af4f26a0ca6ae647a46e4290efd298ec8abd17dca1c176fc4f4f31468f6fd6b3e9295e1c393875a89f3c9e83e156f5a8411e6a78f7e6f32fe09ae12a97857cacbefdad1f8d1b9466ad3d3fa09d9703535e864ee16282da49dd1663a581dca925a6057cfed92f5c976128266d3cf84e874bcb17c935ea66c248494969038ecb7827bc19f763e436bb12e188ab6f08f2575ca6f50ae136e4b5fc7e4cdc8ea71f280475de2300b621136ce5454b866c996177b8fa9dc21a9ebe37b72c92d78bbe0723f86ea0482248c12d1661bc9b47e5c97440c6e63d95b5f2b4494a5ec7a7977784f26f206bf581dc4fd322854941b19303c821e411d229dbe7fad7fb8c7a2ebbaebcdcbe591da3766ae9285ae10acbb4513cc67baf81f3f7792f0067b0c292c5bec419917de388c24c92f271d2fc7d0039d0e721edd14fd84093e055a3e64c436ffc50ecaf21c1ad6f04cae2108b1cf6d0671ba87197238e1ccf2505e780c413a76ef96d112020940512745d486579140659f505a1b72b32710b1da8f55a5935ea32350aa00e88a93831394edf007c1108396722b3896c8a3e7eb1f53209eae6850530a006783d63656cc9cf4299e1c8eba92f0ec354b7bbb4548b7a8cab1c143516a351b0801969241950cc2a4804f732a26903a40f8a8eae0f51fcca07581abcbb91b50fe58a6472aeceb6cc9b6bde7b8ac36857f2994df02fb970c4005c4faae669625ac9d7d8f970c2d63102b9ae4d53b7645d5f4ee8fa071bd93e245bb13a655a1d8251c9cf66dff564702e7759f60da7c4138727d83a3a97eef981d8c90583a7fef7b7972e6a673b8879339d6a281c2c55924d78a9284e60250b9d1e0a1f6e3e2e07fd1b191e457a3996c7143c6e8a120a194dda029c56d15839d6cb6835b2713a75b1ba6837da7a1772e1196cf9a6b89aaecf6e0f4b71c88c4ee8353089039b2d34b67cd146e168f93e82ac8169bc4038a199f2e7157a1ec48503e41a194a634758cae4e86fa8ebba3640e58ef7cdd13d89b36d3048808fd2b8cbc5a8d8d92bf9e3ec6b7dd8ea2b89fdd859977700bfb96450daca0138105cb36e4cd041a88f30a8ba7603b97faae7e047a11b36f91729f8ad3e378a6e7af91ef505cb416526619df4dc28ee90e3a299421707dcd9ac9b1ea229906397a78e2d1faec605eb757a0689008eb23cc49488a75986b8038dec0f595b6ec00e952cb394220aa8781a8120306482dd5331523ef91db5302efae73fbb1f1fad52cbc59be6b6c5b4f0c8658ed725ca6d9efb1bed97950704eaeb9e5970c3fb6612e4967418b99e8d08ac1e4996bc6d3353407f2aa0035c213d228217039bb9edf020a09ff4853573f71a3b81e8590e3e5fbd6ce922025631a4b0b2890ca164d36f664c80b2ab9d79952bc862d8dea8068f88601da9038bc33e0c02680313712ca6bd584b04abd98898488ad6667e5a49df173f37261e81ffb5f12d1e23b1fa78c8788cf66b039af125f508dda4727b620f7f4fa6a306d99b978b1dd9cf0eb2fe1919627aaf39377b86df2ad346e43c05e175531ef610215ed2ac25d281bb2858f003227e3ec8a5c399b66bb8398e852e13b9a96a01dafa5fcc232f8fbbd5e23854aa45715d5ccfb473017356ec504357f5b9698725c702530c044e98c7c156578704e3c85dc363e87dedfbc8e83be47ca3b8e5271257fcbdaccb41c7877809c42f5a79b1138b3d379c7a5fc389678053e5f6443e98c65ed9a096c40e6e45ccbf62fd40db76b49e8677a77bb401c6d31d817efaa97f21547cc59d5dbf830e2c5beb0d72cde0d5e41936e1a63e83d86a2e6a67b055026ea9abedb37ab88eab9e342800b523bc40571790f6ab3976ba1f7c1454e66b5b80caea11a14fc6ac60998030ecbf23e548d909b391eaeaafdc2df4bd82fb56495a5db301913fb02ff5c5bc5bdfe742d618aa408acbac8ac451aaf269092213381289fb9ab3655ee8a7ec1b631b03fa3937db6139a1385ab6349efba873f9fc0d72122247b8de4e91c8955fe940cfdfa92e59b865fc243a15321c11d7ad44e39ebb7e1b83b7c43e5a4d6557ba83d504e7915d2fb324c5c85976afe6c95b7982af2566c9f493548b112e37cd0490b5d570ca21b8f7e65a661b49d0161493bf9157b3327a229333c19b76cf982290d43b19fe7d4064a59f38c8cae12421b772d31c02e7110c87ad9c725572113eb81d86d8781eb62552f7a1ba51e1b588d88b7babc4f9d3b78c4b98a90b2b4f59bb883f2606939c70f26bd98eb397065a3b370b3d10bc9e83b65e48e8cb013846dccf6a23c94a3de518fa571ca03c2a3334f551965796d4787a67ca24512e7afe5e4f35e3e30b2e0fedc947c3f72de6d3e739eba3bda8fb6e6eef1cd485b0820657bcd0e999e4ddeea5928c4dde938da74dec05fc8e7c1473664aabee075985dc101fad2a9d994833571313c6fb754e4d5f496042c1ee8c0ea9b812cd616fdca038a3e5e4e8eecbc07a61ac46208bf81c5349ba67486559e0fea2e5c88f68c0366d70d73e525f3058fc713bc18aea0fd7bfa04029d7f654644c0a5be36ce0c0550e2bdacbeeffccfbf6ed2a6efa3c6fffe05300dac447ce21b6854fdcba53425471805260d445fe0bb608fff0e5b368b23b61974ea0fc8d8c22dddec788fcc9fdfb38c0970abb36aa6302b874030a15c6c6e20d2d8e4d8962e58d7e866c7e3c7b2a1bedeca228eca4175321fad35f0acda16c4a2b9ff38b116156ac65e402613f5cb153f98c2eb71070d06166d712a534970aaaed3bd3f18fc2066b869cfa596ec49ff84dcc9fab844aeb2878fa16cca3628fba685b1da06b3f9eaf85a04b930ca26848bce640d020ac48c439687e4e681a3964963a4e2b16e702df40acac58b6fb5711faceab9905969eaca5d6e34fb18f898588a0d11bd51f04395652a3bdd36f9497c12b3dc6588d161573bb10868bcaae679bf7ed08461e3a7423eb27ffb9b94427aec05ae9624cfa3be5126650d421de20397de4f8f448d4397290d11c226ac36c533571bb11609f7fd8566bc179ffe304733a6361d7e53a2c7703eeee25e714ba963747d3d130e3ae91ad59d1d25a5af20c52e5c66664c09e3690e3c76e8e933387cce58a33834a97c531588f270398c8ea647d8621cd9d2a38d2428bf4b1715e551e317154cc6ca9b78f6269a673339d9ac96ca66633490374af1b4245cc588157639c585866e572b532609d095d67b4e0e05f170171cc1efce87b2d5180024818b894b59319c2e455b1bed2b95058bd086787cbab4e4a9a8e4cec1042a913b368d26326262d2a362c7144c058b4254014904d4b3ec3272508d4d42039dd05db3d5f4c5ffec49eca8337051217eade7293d15902b1f677284a4dcc9a85fed1baf742e3cda254c8ab22c59496bb7f6ff855c8bced709542154e298cc2b1f8c2208bbf17f16e0d38587c6f9cf32e7ea3d88a2c48f576cfb8561f30e796d2a59b004ecb596bf9bba1dfdb6e30a96e77d570ae73c2f3aa052fbcbbed5bbfffd8b68ca0c4c7750e6d3e66d41bf866af1ca3d6537d93819ece497bbbd23c3969de95f7e3cbf76577582d1f3e39b2808474d5d795fe8d76735b361faaf4b1ccc7f25c24428d8760469e0ea37f56d022df674dc1f462cada860ab38bb1da835766597bdcf1277a15d3761fa65b5a9ae0f7dbb0d396b77e27903603f1dfb3466af672aac05dd90c44b77127b5c3568b61b19ce6dfe26e966c11f0967e17faea46b7c1e86b305e0dc66f83e1400309d5bccac2cc0c4dc34befb83c04b78a84ae3a23c06590c343e91ab571c3253ade884ceffaf215940e3373da0b7b8824221095e9e2d243ea54267404809b5da1c9e7eae989b040a219410ca121b0b081c026be0a318248555a7c12b2335381ff932c47a9a5e4aa945011bb9da1af71241c6f8d45b2a379c7a570f0fa78cf1a778b5b7b21be3d1f27d6ebe97f379abb3f58efbaf3e15a3a49393e7dc3121470f6af5ff94aadf7ad7fe988bc28fa37377230242c2083684397b5f40aff1d0bf06291709287cd4bdba6362bea67f5e8e08494948202649b12d5a8889e110904b4385c32069d82c6468069ce4409b4a20c7dd7aafd495187e94de64eeffd26bd6a8e8e0b72b0d1521905426a78a0f36d7d5225561bf7240874c35178e5e9ec5179da5df59539f7541885531e6a1dd7d61209a267f6c73bcce47a8271be4004642c922106b58402b104e187b729f3f1ce0b9f3483480406843400d0ab44cc5e5b59c2563398bc461745458614b1426c68d1094275185ce2fa8a4fb49b365c38a39dcd9df9653c817201eef40077cdc6d381ad9ec4f44f3a7e0d84dbe0e83718f288a506d31cde577402e0de8a2c22ab533334977140586146d0a209ca9ebc263087d095a9c2f273da554aba0941372ee2f4efdae38db446a286337f16fdfe914b0802afbd251f686bd6333bedcddd98f1854dd08ce9f5c3b6f1190364879f5c75f71808a13f1dd154caf0d40121a21ebc80030f7cb9a22776ce2e438a42390e56a462f3f0825e12422374ab33baca6bff16c50b0de00d98d902f249cefa9570ea511561cb51e19da768bac6cb9cb2ec591f44802d803c5a059dbf4296aec7b2e8c8606f757c3166c5665a6942bc1794b0d7540146659955774551d0901c5da84bd5a5779a5f9a721183efdda46939b10b4003b2f510673e1be4751794723df1821a0a6931b645837655948f1864b04820eeee9b18daf0da26c65406383ab6d6dcb29cb0c1f36d529c6be0cd55b22188b50d0d32a193761fa17cdd7f10eefe4cf51a0517d5ede9e19c93c65048011a670a9981756dd4a6827e1e27c3d0b5f0d634e21c4f7338ccaabce33ceb219e4aac7a167c5168422c615358a5bbf00b33eaa69e3daf3aa0021007d282e822cbb9c4a9e5e2795ace6f38eaf183c52aa5aadae1fd8425e0031b2ff8f9cfde81c15005e7aba3f91de7d9da9b4941517b65141e5cd6fd9dd1f9f5f9bcd941eb57534e0836be4de684e0d9093d77584d8c046d52eedd3110e25a283c473510efcd3b9eda1436a4655235b2593fa450f78ce65e70d36b7cb6d560785bde5935af59ce826bf7b6bbd154fb6a16d875012a9da916340bacb3b60b74809866169c84b3dd3d1f5a081cff10ddd39a5f6622399ffa4470be88c58707e21a89628041ac400340d9186d2a884f914404a2185e9a069c416b2ace2b8126e02e850062b1ce8a407cd203f7a5327ddce2c928f719587b0d50c870ec0b5f69859c462a5f5cfcf0a99778754aced1d02396054d17f3de326c63ffb3f4c236133f5ae158efa772c1dca2fc12ba1b382209debc9ddba67ae1fd84651c416becdb1f4179408ff81809a0a38c9f8b4f2d9338418ed0c93c3aeda4e3d2cda3cb745a047644fc08e99579f8f609da3cac17e4bc6c245f0b7350a1e74396d38a7e94a985393720e6648df976994a30c241495a6f466dd626b766131d68c55f3ca0208f1a35df036c33dce8eeb5e40b635f2e34ac1c34ebef337bd50083bd950a26cbfdc6b9b5aadc569f519a39494a1aaa42b219a174320fa42fe453735c5d342e08127e552c1ab7d90c634eda35e6a8b9f6189acc2904c527152f5adca13a6dc76e5766092b2e00ab8543ce1117858e22fd1000d8208f8ebe447560551b7f0da78b382e803e5c93b723fad4a1dd160ecdb31ca46eccac2733c5858cb5f555a7be76faade7ce7b3681762224ebef203bf5f4eb146c722b5eea55157b934b5747dbeba64d4ae20bfdf83c72398e0fe05be3ced947b47de473059ada0a000f409005e7eed0bab8a3f840b65440fb657a41e27b4727166a1f879e13babbc6351112e9437aaf83e683d06974e117a94d53309ce94199fe6b489bae1a3840b77f18eb8fab2533930138393fbdf5b6cbd6ed304b27f77f639ab8c0e53fd611d186b54e9fced50d020cc9d724c6c6b8a09963dea8a358c29319887037721fb0e1fa0149ba69535aeca4d2f1480cddfca5013fae5ce3b121362ade0a351403a26fe91ffae773617ea687f23e4b247d99a24061efd2f8e1e7cf1037be867ff2b677c0824096d4d19a9cf4c9b4e67a35bfd429c198f4330ab4fc5329e6b70988829fb70fc2efe88b3a02463fd0018b901c59776e01c1f0e2a2332bd6eec04d4006ad8140390263eae41a757290a52766d5b62c48b91066db03373d8ad352d64d439ce2f1316a63380da1152ad90782e373873e3a8a7c9bb6d3d3fb6777859f1ca2fed888ceee4166efb16f3eea39b9af4bb4507b8fbad6a62dd46c154f416d057d40ec472090982baf3d00e64b47cb2c4a18258db8d7581c501b1a71f910f3a133996960d7023094f9017d0a7ae76a0aae2594ceed8f2402567e0e1de3a8d6c99a5305c4adc40b967e746b639497ec99cd98320f8207b7feedb6777fab238a00d76894a12a15cb05b80baec3fb8d4801e3f3a0fcdeb3681657b3c4b2d3a06eaf1a8f9a53324f23cd869d8d8257d54e08742563a1149a3317997ee51049fdcc204485a484a1fb1ee3b9d16da692ae478cb3b74c7359de0091a51212c083607d5a782c04ad50112270df1782406c386d8a90b4cd02a3af5dc9ad4478bbf923698deeed7aca82c4c7398d43a453e47b07217d1399d44ecf6d4e7fdbf5965b8c29fd207abeebcf7681d0443ca8df1d8d49ede328bae1326988bab42c126c9f82a203efb04d84d5f237ab990b5d54da1e45701c884824330d05cf4c22f1bd5ec010a1dd092af835af176986c269aba7ba4b117b4f4f25f691b6160807490828de5a3da4518e44465192a5e27d32b521a2b0095324f0a520bc2a02f6216887d287654f0bfac81a0e25a588f5421f4663454ad1f61fbe890174081303c29edf28410ba1fd913a11df47b475516e6f1fb7347d120b96402312ca5f689fd77a2123bf5beaeee9f3d1d6c8a9e273b4e431b74850cb33b33ee8dd9d110122abcf7cc8ffe3cb862d70454d49f9209fe38d31c812f58c144ebaee02a7df28d5a970b24c666977afa4883ef2f9ee0564b424e14d1ff3747b106859ad7032ba7545499ac32c648a2f65690a34c7bb68afca3ec7d0d251168a7c99015b610241b99b48ec4a26140822d835993e710eaecc8c5f23b194917672d573ae22350d1ea8fb73c702137fab21a32d1bef88449f02e2d7a7dd9171e0502167f06fd9c49e05c4c1ef130d127b5f0b7edcc9a2c001894977004c77c2c7a382576440128399060a801b05a8cf6966e69c03f030eda76169271dd97e0f6a1918ca9c4a54b1ac066a4ec69288a5a9551d8512684efbd53e10c98ac4d7f13fa4b0a80c85de6536b3e6d3699a1f696182b36ec7aca9f52950b247d2a31576001876fa18122632e45eabd7a9490a1abb51303aba6f74d22bf11299d45030c411e2bc5660429eef9ed979e17b36487e8b43a0ca8913ae2ae5958e2a77664c201cefdb42072d736ff6f83faedacca4d23c1ab5f4061490e2f105f5642240dee11e89b46f4001e55dcb1d9940ba997ec967721d5e996bc8029a5871dd6979a0d3d76e9042f7c21ef9c14639afa31b4babfe76e6069adcac89b110a94d00bc61ac72d39f09b9527c9f60c7de6f73e72ebbcb6cd2fc9c0bcb55c27bef09176fe396a789b28e13027087126b8d701d61e97c164ac364171819dbc28d4e2d405276f8b642254e157cd330d9ad281cd3daf323a37e57dbe3a3a469165aeaab7774af905f3000d1312e0fe0abf2940948cc524ffba04b8013002ea6b8a3e180e7e8c037e484ba656b021ad00babbea0722f5e16734a77681b23a4cc1f4ec767eb12353def88e8f6877b4e99b719dfe504f30ab3da1ae611bee0a73c1e760ff78fd6d7fe7ead85cca1dc634b6fad847df45fdb402fe494ed5103ae80bc55bfa53a76e20e5f98e97fed22cdf5078ce4d0894187d02eff1c2206af9859beb0a5d24f39a0a4c08b580044d1a7167d7172800302126bc4a4bae309e5439a8e7445d979ae2e9090dc851278b8dbc18828e6ea0aed02180d0742bd0128f5ef190585ae43d3fb17e636b8937d575129fb4c8aa21c3401680ea2492d8674e86cdcd2ac8ba93bf86cdb0f59704e2968c7f7a27d3cb4be3f25300319e62aff0221e2d1aedd897f5e91df5c2a248c52ff43b90b9498f858e22efa84456a1bf814f1bcd62f4ccd9db5fccdc57e8d347e2d7d6da5a66747f8d417f19bac05c04b95f094d59ed06411e87460625eba72ba1c41f412e204e556ef5cf412487a4910b8a5ea7a7f0eaeb2768ba7ab36d92202e07db0325f5cf8faa000026176f8f86eeb119a7730f7ac205009dfa086d14c980c7284075e59841c97ff87a55dc0fda387f09e7befa8e3e4d5474331e960c688cfbad45dddf32b33991cf0af2d1ab402b8f0fa22f06156c0bb41d4343e941222a9ce33572064e86776f77ad5c12b6f20b6d456f6d3af4fd0c3a9b7c4f686413611e09c21c1938ad7bcb410f627c3bc3fab2f4eaaee44f704a4fde75cff543ce6880e64520bdc32c5c75f9762622bcf38c69f1fe6887b871bedb6f2c38578ad15b6ceb1e029441a0b1ab8a06d642299c49a44153534c6cbbc24dcca67907aa04172a541ba022c6eb206e970a825018d1201883943b7d94efa2e1616c1eb3d0ae4e7d9dd421cb46b60c93aef688fcec03d73b7f098cba962dee22f588c944aa1525b48e2b41df3aa594c63d56d40649114bf6630ac6ab60b545822817f5d1ca5fcc1060351e5d0e606fbe87f851a43694dca46b29875390f06a71a6cd74aeb9168889b976a7ec4a236d2ebb75dbed07f937aed7873f9df6237e020ace9ba29b2052d729d73311f0e9733f3dd2106240d14f90c395bbdec71608cdfde6a7782178a7597a46eb1447ce4ec249dd084c53bca6042622dc409e014d1881fb8ca9a3969e72f379a951c51dd1c824e477068edd211e215cee73aa937487361c06bd62afed593d0b67a18de5cc57e7521646eea2c9a49086bf2949b71a15a71409e8fecb32d08b34d59bd96559c1f5ee24f486f75f6e5098eb97facd0f1675f031aead1105d9048c7075fb37107e0c48e5ba71bd1b51cf0515d5f71902888205c4c1d06476a808c3e353d712e320d8baaae0915df1e623f644119f51a923e376cfdcfd137b3922ac2a40c6f29970340490c8b4473d4598be8a99a871fa62534dbc84dd74c41ead493dd7eccaa88f2e44391df85ee3d10b14e55e9c53fedc3666c1a4ffccd68ee455978204fccb7d97629b31bba3ccf3d0f5eb7f2c139e4bbd882a250706d207ac1b1f3284098ec1cc87f6c581d9b7cfb2b1a33360b21e0482f33dbda98f9a9ba08c99ca1408e8660ce8339cc78f29956426c81cac4e006cdf140dd035d76ad75a8b07f758b6770521cdae3fc589c90020c0002d3e0fd58809310c92616aabe858b175203d082a64f2ee3c5bc04d1b95c57fe92a8806319b62dd5cb105e3de351b7d7f3f5d7b38962963d80247a1766ccd167ec694a75ed4b7dd1c43ed03334dab6daf06c8c3d1ddf557f032044ac8534ad37445048655384abb969a823d0438755ffa60d69f4473195780e2daa5dd4a92b39e69a46f4008f6faa3ac7d075b3377367011c0b9e4dbfff58d1d12c2c3aec436da74547f1da439a0e030c3fcd8b8eb92fd4c0176c117462e82d7420e56d6e84f4061373373898445373e6e3d1b9b7cd8cf4b26b09cb9c046d2f74d82a98bce3e600cfc1e0a00b5afb686796f9e4b18655489926cc3e62e528c136712f174df045aab682720725d6807dc0dd63e8f11f1ee56565a670f8a41a897a4294b3f784c7285d05da8cc03cc6100701203c74c9bbeec0b155a0433b8f66171831390d2b412742527764af5ae0138d72f6dad486b149a0c8ee64831110d98c4a2d76cf900a374901920ffa3bbd45ef28b5ef3fe07abb06f87729686aa241d313659a0f0da906626c3bf8138ea07a8e604fc5109ca4550deffe01e9f7d47098c3a21e8d383c9ec46b514766a543a557c8643bcb210b920547f99f5a503e4977e91a4a8f3bbc93c331cb98a1df1e8ad63169c68db2eebe568404080c0c60061473113fa9c657e02bfe4ad18ccc47e96532e19891f0ea58f5680b40d602105516fa0160f1a407f0db82dbd70b31d1ea2f4d9e1e5004d9662d684c11e70e96a108d96eb7e31682cd2778bd346a6a9df34c274cf37cb9bb5cc25de0dc5dedbabb6b1d852e3f7ac1e863b63f5f2f457fb55433638290030f0483074b44b025a68df2a51ee4eb6387fa46d6c1b3d37d690877bd3e98772c1c6a23909436e0af65d35488179d50893c86ddea5f4a61593058f21162b2580fb89c6cb8c2c0ef4fbe67cb85ecbe6a05d5035326c4620099d9e318f9028717bc4098f938967376718030392282c7193118a3e2a06de891b25b1cf52b01308c7cdf6257fe32b49295d65dabff664ba309ee217d331d85c5045a1c625763518201c57b4355ef826d50196cc95de3338a33049b6990a678112ef06617e5d5cb7fe3dd533f4c89b5d4b7693366ba13a3af2daa4600b55977e6ea10c29e78ab85f42b6fdeb38249b00e0ea10f4669f5313d2b46b3cfd959202c59e15dfa09a0f8b30f19a76a8b042ab108a10a0961c42254f92310c42224914740108b08bcefb1c162ab8c7b3d6f1d2593b21ae9a5c2c45df7a6f25cf34b71390744d6aad38b018b73f5c1effa95a8af41afa948d2d30261c991e06800847ee7454cbb128f74289ceb7e5a5c3954a872b9d8ee70a8f2fe43727254f55801d22ff6bc5bd315476c5b80ef9aa338d4123f9852960f49196780073681773d51af849488983a62e6a3e7e9104b8ac34c2d9faf6b38965c383d6aacf59678313ddadf178b8ecc61528c0f2c8fca9728f34bb241ad83a9b5f752f95bf3216d70bbf5baff3690e500fabad1f79fa9856c3ccf11050f2d5adafd4b944a2e9c1a94dc5af788ea73dcec54050802ef9bb8695a006842e2966f532d1a9565105753e9843bf32341bc2b29bc9c619ba3219d55b9045c53edacd0f6a350f0e79a45844416d487248e461c38e565c47a41c2a2e3d66a6d7387d0d5cd360133fa8c1e472f95e2f727b0e9b508fe5712292313920914e5d38153e87f80b01cfcc4e850477e9565acace8c3bbb3c94945521b0f41902aa342119b27bdf9e40c87079f0a5d1fe47188f13dd5e9db0b70e6dc3d55b85baaefef4314f16818055bb0a633d9578bb904f4f1b71d89fcfa2eaaed2dcb7d60246a06bca9013f8a52a6b8acee4704953b2010ab94f6df7b92d18a1c6b556413e358f107f93333121fa22877a9ad060e1aa5e082d4b24b2d370fdc84329aba3e645389d81d220865633e4460d388aae8a185e30dcc7939cdc6b016dd4c1f221a4de3078bdb64690037f413cfbf768e8403a30972e18b455d2ce17fa944c470e7c9c8703988b5abb64676a1972c9b48ff6ece7a6e851d3704343a26eae0047b1ccb2714420a6e280aaf7d08e387e2a5eb2a59981a998b4e361bc7df234a58a04ea8c283cc24b61bad0365d92e40ddce8421eac3c24c6cd27f8e2b134ccc3fa904465ed49ffc1d10050234bf93d59a8a3d8e78a250333a293acd64829f78df1d5c4bd8535acf2c37aa3993b8e04c7730de5efe2b220b9654939416084ceb2a2b81b18df5a12a83d163ce77118d0f5ebb32851ef59f186272ab9bd0b1944e58a1ca5d59a4d435a4e87404eae69ecd0215b2a21d79dc44221c47fe49f0b62479b31cb01529fd1bd14de8968e2e87dfc83cef6865a4f49cc0be2720cce9f6235e3894b2aaa89f052a2f35c5e8859254940409904316daabcc7de48d9a165219d990ecdda855a8337bbb5137a8333d76a3209162b9a690b1c712e40e87555f2c17d9c85f49056258b85e92fb8dc5d422645dbc49aaaccec911e6ace27a67385ae5d0c3a55ad1cb8f797ec06851a11268292d278192cda2dd69168d5f4496d1f982654a146284bfdf1030dccd1aa379d6c22c0961831442ef16e23832c4f20b7c37ec39b3b6940d50127229f4199397aa90cd499abf42e49b44b49d44f7c0d373a18731c9d0abfe1c560d5d5a1b9de429dd6e361d17ea1c858ee5e694c76e87f3c36ffebce164c1bcb221c319be7885fdc2a19c88e1cd2b1c6b57e795398a1bd3ea5828446879a55b3d3ed41829e93bfeec4a3e4e639b7433253dcb19c50eb5a451184a9a0ba496312981b9e65117221e9019e8dad1e18dfeaf2e2853bf48bf14c65def8ba4dd95eecb18f54bce97c591c6fda92f336c5f38b320b37ce8ec8cec88f22339d605597724eba26cc7b0dba2f32d046996fd5505dc23442a7c1510744f40c02a7db607570c22f14bbc28252f3181ac20d6b0f9df053f9756e05a617e7b9905ab06a327f82660ec3e714b87aec9bfc6324ec81142b6eb2e539ae9cf4aba1e75052e98c049cd714576a025631a38a8c37e9816bfdcc2e3df5af85ca27474fb39722501301769c7f286ae2889858fcc270798fc4f680b5663c471d5c8fcc8f59fa69fde6b3af4dad4ffbc6f0e442c10a7506f4ed9a153964d0951ee94111bc7535406a45051868d3ebad2de3ecd937e5a0ea0c2a5a07a6b314a09159195d9c944fbf1311e335942bdafbeaf48acd9f2241124ce6c3c1b7a24ec088fef059036f6ce951ab17c6a98f0a369eeb102126c96a06106f76d3515160d879ecc728dab512b28242ba7376eff05beb32af7566d1b4a6668613900856c6014c606742ea1d2744b0ee4d178c64142d66ad0e9e0c8497865a524d42a941733fab449b900a66bdc9b9aaa47d2600f951f149db0e8ab8c73dc49ca8fe3fcd1f8d602ffc16b0a2264d4222a6ada1a5a6d561057d446aafce88682d0fd58f6543113584cdc69c805d8cba057bc50f59564c8476f729c4947319845d017a1076237f44c00ebf07d48b94aa9ec8782707e974107f90ec4397582728e50bcdf7679fe184bd7708ddfae61b2c94e79ab60cfbf00635fa4ede5190ffc647ca5b0d54c05248e15c049b182e9e2c7189806a08fe856739aab848306e0a01bd69abcfd7c873a0f5a428aee0e29fafaa9a5621dd4015065907c69f41aca6b4dc4819810dc21461a9174ae32cab82fc737f9aa81972a0adf766cf6ff8ef005eba336c3c4d617a553049edb6f8b812bee4c37802cbbedd17c6e3f194e499f9139c796c2feaf60454d1598392d7a6361f89cce61d5de16e4801d3fedc2584de0936fffcbdb1d15225c7892ba852500c0aaee5382ea3647fedb3e0be936e16ac5d5b26dc8d56f5f2f502cb8b2f7b44cfb6470071a0732ab2e3796b61bff69abb93814c00e62d2ff5f3cd42036d3893b1eea7e71eec9353c00ee79bfcb92f30803e0e4b1a05ed62a06452d0530c9e2d90d8ba856f3bc9f8e0fe5096960d914f02211f0f0998094d0a17187aa5d3c006e1318e0826393653ef50373c4509a4aa8b91ee00bc576ed11e6b2ae2e24f7c3091cc2b6309755215a8129560da2de97d06920983291e8a9cb7196bcfc03c7b739b58079a885819f1464c6aa402d008b618124a70869154a4c77a9b51680e7d8869497eed59516a08fd51fe53dcf476133b4ae9f8f06b4c5ee459be80e06864d03dc27a97511c18bc2135d559e6494e763d2f047d9ee2278c6494a3a62b83b0ca5e2c6a99aa1282439a2c586c2de733bf71c36577dc32900fc0276c2f094c4093450f4b62a0d3cd5b43aba8756466c3d0dc2f7c6292bada173068c0e378e26c6688e4bcf6a955f9f83bd2815725b81c3074a4db1e7a442c795a8901e25159552491b145f98e5e1f7298be2f3f323587f3070d27ab05d930126e91ba54411df9528001b78482c9f6cead10b9d4f57e306aee46cc77942783e999a0caf31fdca1b39e52e3ff915433682f161e10705731c04d2ad63fe01b46c0449624b0a8d9026a6888fc9ddffa0465376d0466c29e4bed829aa5f22a60a0ab080786061b320b580e6276b26bf90877e3361f60a4c085cc129f618053649d94ff485d2990be50df32c4c137859dbc2e164dc782d0083adee2cbb2b82d18037c4d03e3692bd35110eadf1fb4ae5341327c5b7c13d68c2baa4acc704dc6676ab48bdfa2fa53234546e9982d2cc9c79de74bf9e394332b208e39fb9af032b15769b2b21d538ab1291b38fef60091695489c388c4453897ccb675ff8a21fa2dc2535dacb995da98ebd3fb288ce32611be6b577c654c102eb192730ebd891498108950444cb8113d617d8f38c5bc31b716276af61fdffe5da5e20e06a4432ba273bb9b53975c0aa4c4fc0b5a999c70bc415aacd3436580999356e4a46e72092e7ebc93c080e1f94b7df7382dcf9275889be2fe584dacef20441870895932844def86e74d43eb6c2ea1f94b9800f0f1d27d4ca01200852b741ee12e69f01cd6819d3b4cf928dcb6a1a70ec31c3061c1fc5b501f88a63b4a4590705258a2b69188007b869ef23740ea13d8dd2e90aefd587123c6e0e00e2c978886f5b1ed36a6ce8d8646cd7573c21a2785a04d4de6812798a06bfbaf471601c8a9ee8fe3d3b3d0d143b436ec600f4e313642fb275b42c00a84019cb953f600861bc878236abcacd609cfbf218f8e87c0b9aea37b68686e9a3a4ecdca96f865d922ca9ba2057e8c032488a61b53431884b460c72e369b0ac7ae5992e6d60ee27203162a90c7a756a17a646fcd4e6b9ba65396a5b1a36d76623488c846f28d41d0fa226e2809802b7f7cae6924155f38f6a522a01e5d3cd86605e62d44691c875c34edc6563adbde84c4b52c15efd7f03fbfc5bb86f7e55b90a54c60ac79a1e564fce526060eee0dd6bf2070de33e88e7d286bc86732497c0d9eb1c26e7d4ce379d37ff768ae2f076409976525c52272cea5069679635a34f1ff42a4951a545d9d38e85e84e4e09205494b3a789cb692296d29e403ba0c5ccad93eeb1d088c6187e39469e8f04aa7d42cdab861c93ebb6ded89ea4a728d48b86f796465b34b006d4a81807b6700c29ce79676fabca389e13a93958146894ee5d9bf9f590d34d6b88690c566964c042df82c047fd567562553f621064f6d378059cd3bea90ef2a71f252534c816080f5bbceac844d29ec87d39098becf5ac91eeacf0ecf9a7a498b4c85efefc503298ecc1feeafb932cbb7d0e80281363629359d756fe0a29c895f140c80291ad58357d07905f7d5ff40e328e15dcc8822f9dc50a6aa94f5e1d214b11aee9007340b6c1eda4a0e3d4bfa2c0ae3e9f41884cabad932832ec8318008148a1f120eaed1300e8c7bebe41ecc2336f2aa3013a5cdf0478becd68bd53622ccebbbe6a70c7c6e71f461805c50a5cd11802b3810ec0ceff6c26532617ec41038e3b948806e49b35d552f1b71f0797068d35135963ab6cbd592b2fefc16c54be88774e6fcb177d436065224bde238a9b21b0e0f5ea1192ba1074a33a734733b4f1ffe85a927f103c3cb77cda6a931622c6d7650e1b6d2033e7fc83c9d5ca7d7b77063543f90364d5a365aa86525ca2b0493c89ab1773860535c1d4d2d0891416d0b7f4e7b7e790f7213437040de2f202f6b2f69df8bc24b424dc632a59cc2eb6978a8916953eac4a5bf3c9ebb4b8ba4654084982e0b216424e5b623e424f4a3409e49b97f190b6f589379e9cd07ebb856a0d40f99146c59f80c5e3428111a382b6fa3b8dd076f20eba25d8fa7738d7777e301953ecfc7bfa0cd48b7d2f2b3d1df46dbe023569e55c22729a2a8aea8f675f933fabc2667459a30cb7ee35731588b7d2e83c2c36dbce2e595a837f7385315863dd1af44843a282faf208229de2abc7d5e61bb45e783421f33bfa0defe5454923866ade8a22fd4287df63872cb39688ead077e6a2a1eec00ce6f8a6c00486a1e46076a2410b5cc318796f913ff5561845a225a3985fd1bb0fea15c22e2912e7f561dc5ecd23e299ba97197d3b0a71c9220565728af3a21c54a926307eecc38c078a4e11fc09db20539b62aa2d4c1cbdc4b8f68964935d0464a6a1d46cd26619be9519f5cec57d63a8ee01008b687e964cd9b2f415b1b75d6b935744d76eb69712d6139fed951484dffa000c5118dabc26594f33dd01f61bcd067be779cc6e43d0ae103e5db4fa8dfaec26c6307d6cdff4e4073b3ed71b463c339224423402e0d13cb1fd0e8dfc1549fbc546ebdb528ede3588a80e9fa0d33b96109f6063892c34a1cdbe386ab42f4ec5db89bff51107df62feb54b89f9b380760ba4e7541fc053a769b50f7bf13f6eca303612662b26f4a3281b3e4d9431101a20a31c368517812ed37da39fac4603a3c77d351ea5beea2295ca03c6a802b049f383a58a8ecbcfcaf532d11887a9ab908f76418c279aee2b4003d475b0c7b9ff9a8a632439306d09d7eec601e0881eaea71f594347cf046838defc7058f9bce0139c035f608e0378742a858c6a5aa33443a748725df52386ea18c2db7e0277808f65bfe849e329e60030a8349359450104ab320ea8b71eda15b861c0338f48d52341f787f69ba6f4b8c60b0d1fcf33e3ba7229e0f7e56fbeddbbff1a0dcdcf9f53183065d3369b6266c67f8637d037770e413eab6917eeca27948abae6f6c15d33d9ea8927d51d827efe2695a4c353f430a587d8ab17fb14fd0bfa770df96deb40954a7cd2e80f7affa9791b9dd4631acd7863fe29b9aea4fd7d525baa9f42fa781611f1ee50d66705b96f109633697e7b9f1bd1ca4a82bc9471dac72fa1aeec276474d9d40be96aa29f329e70a623ce3584f07c4a1b6e94eab5abd98c87a126323f455ddf520e3c10d69bd60dfb5852ac9416eba5cedb798b1d347bcbe11d9bd39abcd6d598e6b4615c35e3d58d891859d9fbf911c4d154bdd64352afa18f822873580fd17db62dc106fca5fba8679ab6cf523b6d177b227e3ddc413bbf6e8b8fdb6e16333eb8920f285d2c59dafb29fc4f08b62d76dba92eb1fcbd0cb2055b90fb21b15999d72966fe945b9f7dd9b29546986ba8f2f99d7671872ba5b4145b292ac542339b68d55a6b49282a94c8817945310d2104dd9cb6fb62941c408a154785994e4c6717eb8769751668c9b65129268cb35135a39bfc1ad5c5c3d8e62fa646e584354d00f9262283bd21836b4625107f4d5d1955346223c849fcf159468b99e1a861c84f3245e9197d7bc730d6e569ade69ba8bdb58f4e71915125c9d02346ad91f194c202af42ef34f3f159d6842a503c57210f897ec670f7713444281a2854711945c426c98c12117e3d46c55b01bad1a91174498b94b24ebe1dff94947b1aa4ef67d791736aa9dc76fca1cc1499f9269adad522754d39f96d568563334b65a1fcb6f739de1c49ea00ad51e2e65cb927086715555e28a0e72b3a400252cabd7c03298b73ce81e642623acdf7d7baf28a14b47b91a60395e4e2f7ffa05b61124534a353eb135ca353858a6a85afda5d87f13523b57c646d69099584ae73e42a505127af25846825a56177783588c7281be8f9789f18d7944b00bf2e7f72c73f954e516db47c4290ab7d542924e520115e26bb752c8104f43bf2e30b8cd9bd7bb07867b80276360afa9bb92fecfd8f18fd49703e697e6085dd4c822ad538b422ac80f69537c80d9dab7133cb836579ecbef6050bed3a34e45df47ceebab039bb7afded091861d4ec462a4d0bce9e18b38940e9ac13b6eb1beb463a3724f02e238f28e6eefa2415775e523b358461c41f4935fef41985455595057f24dc3a711f2c482a9b6f2ec2a398e23cb2c03ea68aed4ae92353be4471b0bf45268bdc26eae420f988dcf2bc64d1d949e761cb1fa9fe8387bcf9c09d063cc9d455f8dae6ea639a12a3c8e7fb7de6335e3248f453d4c73dc293c8b3da39a43515b0a502c0dc992b7bcdac06c6660239f62c05067c0049a532335f564b2516b4548b99a55225413bfc46f2a7e75e907f1c96aa872948df549774f34da29221ab48b5c64457aa54b12ca179e32a10b6cd10edc14a95fd876b1eec3f438682420656954acb41a5d5d321105db35af474d7bd0a1c484fd19f3becf1ba4a0fe2bd0dfcfd36f4c0ad4a55b2969f5178c2032b4e42a9d07ee062bd920a58dbd23d9653a96e0de62bc9532ad86f10f123b7874a256f919a5826c624ee37a5ba7f3fdd9f888d8c4a9650c1a5f7f3891986070e464aa517cb5d6cf9d0bebf3fbadfa354bc74cb445d9e75ab3bcf4343a9d6be77cb5d6f12a4fef354b36e559efb843871e856cba7756e2cb7eac17f88dc8a24e2f8712bcbf499a7885b9113ff76d27f0f42edd2db8a5274158376cc0fa00087f20fb23e47f477685b092be35b7ab5ea7ac20aa15b94f9cdb67a6ca07f92a10485c40c1eb615653841f08d3198d3e23e34dfc698581a436c5a9f9e6646b565d300aee59d6e9f0649526fb6795a56b0150db1adf40787469a1dfbd9ea29d613d5b68548a7977d6dc5dde9e5a1513d7836583c199cba88a95216e53590fe93c7f498be6c1ac87df9bb3bc6ebfeef4686e5e33cad3eb7deb5d5246bab7da26051b5952b7c65b583da8a971b47a147dc67dfc00e3738eef72962a86db74ae780e6ce859064a910e9e0422a5453cd81f2d996ceafc789014de944a08bd2233df808e4b27e02668b093d80ce6cda8ae093d1f844153c4f8ca14e2623b207fbc22159b59a044714a103c7d00b97c33e8619f6d31506c1b0269a6ca7e6d087d30414ec5b3975ba8c015ac9a82aa8d530e66ee252abe2a0a8fc97d2c203a6d2568451cc10c4272eab6903aac6d46f8ed7a8c44bc72eafb6e0f5c555b725cc56e84174491e2cc6a5ad86df1fb3d5319441f636176c6162972c57335382f2940976f3a59c7d90c03886905744b7b457c4b4d2b81f3a2e37e31209d6e2c290635bb261de4a920656855c234bd6fb2566072d5f2110ef028cf156f38fc4f24c93f977a3165e6896b40df9e8717f1dd2fc0eb35b81f7cd299b07a0b8d017fb865eeb86002b8a9561b227c7e5d1fc0f8d0310b2a0b8955cc10ad83561cdd4868f4a16e405a346b6aeb95014757d33e9be5b340f42f6fd9fe7f7cad6c1825bf07b6de499f699c63e71d3e472fa4270171acdb95e01a7ea32de4f511b690297bfd8f923ae3498ac83b8ea22983d5c51cfaa6ee45ec3151f7365b8fa75441a491efc8c0ea57b4ca72b5c2dd9617bd6f495e2dc4dc3fa56299065fd7501efdbdd8ea887c329d763e477d321a7af7a8aa418542daa6a156ffeb247119284088027373866ea703615c9b880f2dca09324d9dd329ea8268128ed7198444ba57a1528347eda07f475fbe0c4f98c718f0844d5e050158f310627f2cddc1cfc6637e8522245ec480b10aacc0861c65bd6d3d249c1ac286eccb036853f07975ef6201661361a904e127ac71e1aee04d5b7b99fff9f065357ea85395d366a5298b4d4d15d08b8b77a315adc9e4533b7f8cef28fd395c3806e2750d470bf50f3a074ef99e0f96b5166fa866bc0c2e3e58567adda5415829ce0abe0713e52a836a7b47de8e450cc8cfca8aed496cf3311e3f289c0b42df1da0c68cd7c242a094b97fb4a7d732462368060ad180a4a73f535d6a2d62bfd43b4fdc5cd39c19fe8e78dfbf5e4b5144e807cfdb2606f52bcde4499dbe28f4590cf0222455a5d04881c7bb76d6a39a677e345f9f33fc6c8b452ec4e3fe163cacbbf0609dc4a21c4ef0a27b58e19293a6dd1901e701b43ba17e3803d74514f64008fb048c98eb05a64f9f8b3c30da596604245f1141da1f250e4e41bb1e89260da186d23637e03d14df05d497f7bf40ab66923d5fcae227914246de4fa5fdb447c7fa625b8e869716ef5cf1d3b8ac32c41c295fb60f01194d3acbef642849e067938c45aa489c1f24e7f454e321c12528ec12c0441de24145e3f15cbc13236d3069aacd184e358c0e1b814a8dfb28eee433e1e51af2d63360d1a208bef182db3bb69885e62a3b8dc8524b8d75d6ecadd50f3bd9f5df9c78c36b66604c16988347619a9640a3d08e280b013f7eed93a6d82d65e475b900f8227e183e049fc78d7eb28861dd00e0928dd26e5605257181958d6e64c8656a4363f46982b4054dda1a66c7eb561a1353f6cb163ce9756f14087e7bfe081490107fa472fe6775dd23343af90569580fcb06f5ba5415a37b186f9e013f0aab144eea0ae4365a328b0dea2314815c91cdb735b10065426e7b75f7ad7086e51e93db2d606f1a1da42b25f6045693a8396bb031039da744edc44dc400d3e9238dd029ee81d4028ed9d5480f53469a3b7af4416e0129bfa8605980e22e3c71f3498004804d712671f20317ee6fd20fb516f83c0def8b02ebfbc1c139f000c1739f5504aab7163f729990c144ffe697da3a32a5299a48dd2526676ac6cf1ea0ec7e461d5d81a79bdf526cccf6d6fb71d687bf6808ae7cce77e141ed79aa2b7400710423e56bad04a2afe51023f92e81232d15f087d3ce2570fe7aae549dba054f9fc710c4d5a4a772bb027b9ea3207bcdfccb4b85304288b9a0c73ec456ad240b9d87f10a5d21f026422aec0254c686903716bda5f92eb7163647af446c680f6f53b50fbd813aecf645f995ee23472348250322dff32567ba171b1d4ae9c86eb55df280f1e910e247f77bd50fb58b94a3d21c9adf9b4e2d3b28e55f011ae71bafe72c88ba76492a414c12716e42438f55a204cfe295aac409ec0406d8bfd7592c3e54b37cda344237e3d9772951216f3640c71c09a090612a4cb724a6ce880d58077a1997a0ee6d4cdaeb14e6a8419a778278583d10ade6f8d66b8568058a1fc1e0f170df740a878539c0fa06a3bc2029b3820d4519b6a8456375923be6c1b7754bef15fe51af72aa7b45b8310addc1558837ca674ea5a5541d9ca2b8a265357129ed454f3fecadd4c04e28191157adfea3da5bfb5e2dd7f2cbe133fa0a1ea8bdc83d033a0cb119783163dfcf1d9c4d8b619b18dd7fdae2566b95439c4e4634052b719db9370c0c5704b38e80c924bc271ce37e447e1f81b2c8ab1dd930cd50bface035cbb85f3b96813a7e298686fa8f62788f2bcfaa5ed25504bd09300860fb7cf4e0858e41444b0235ca12a80c6585308592eae4a654b460a779a216528dd1a373b5f02687533ae4850396f163082b47c33d28aa076e02c340439499db1ce6c3908b12c01b5096383d244fbd8f7beb3e5f001753a4a9d538b15ebe92d36b1826b842fcfb0f966e8aa9b00103cfe9aeedd68036079a7c35e446f9f090ad5391bd649fe433466d5372e7d1d176312f9ee6e77447c2fa8662fcf110a42e8bc44a6e4021116c15e124e2ff1650578a3649c8a2904df0cb51a048f4e18b7858f4c9a9b1de3d48991c041ccc339db71b5033184aa15f228122cc68920f37b720ffe2e1f0fd332921accf9edf56c14a42a0e09faf30340f54ca192ec86043ae4e6084a96e78b0b582ec36f4f6711f3df22aa90fdf06b7367cd7cb16370a430d2b995fa13ddd5f637ff968ccb40b764d65342b90bc55f7e9da220c784b5d464d7f1c677c82557e6cc8b9e030c3a86848fce054ed3ed490c2e1bc93ac232f340183be16708e44c75144957a262fe5879665ae3679c6cc7013154a9b47a45b649c552b9444a9084dece78fe0b217f1886f68fd3ebaca8e0a22dc20c9d17fc080941f4f84eaf4dbdc710af8541d8e0cb2e6d8f3a6691cc63d59d894bc9167af7c330bb0b32c0ec2031ae5619481c43fb216963e81ba0afee753fd0989b42cb218969164feae3c8b1cb49e27591ee613b02613eebc6aa0426c69b35804f2a16c8807af229fc20978505cbfe8a5708cbed60c5455cd67c04b2628053d692643157a9fe9637fd589040166b666996b6d43c8ac0ff956a0d62dba9eb31ee5476bb7510e6aa817fb8335844ee15c3e79107ce4867dc3d1cb49e10d12d3598d8a08389908fafdaf8a23d365a60d25936ad872c035f2e27d668652896d32b5e42311ba3700ed07ca0793d6bfe2f3c03c5781fa1ae86772420310fb55ed6aa935445c02960439f3dafe501708bf72f64619534abeeb3eef86630705b7146434ec10b7f776def7d54f5ea9d7c86079ac3b9b57329901a7ed2b4a0364a8fc1c8de80b3c9385bc687108db00056709b491e7e269f79726d38be6813c7da0400ab17947b7fffa43fa105ee45e14b6442cc365d3ac2037cd0047e9004180e5620581aa5039a0c941ba5ba09822826cad2a324722bb14b46aa3b19225302a634710515e43db9c39fa3a51a83290af5b0b01fe203a0081bec146ca4449b08c43217f1db231f674180cfacb54dc8de9b6c29a59432a5147a0d5a0d280ed2f771d07c668c5408f5903a9944efbcc771d1cf6d37d87db413c5db923c9fe76ec341f379beeb1c1542bd93f7b66e9662053b4b769da3958b7e33215a91f7cff9aedb5d9d7586e262dedd1556a76b981ddddd52abd3cedb1955be62da0adb43e637e90c2c5fb3c6b55a301d971043efed077e8b06b15b4650f2ed03af0ef4a1f9423be62fdfcc07e62693ef05b9ad6d9f634668f2d8b626b7767588ba31e70c88e5985f0fe30c485e2f0b329d9de9ae0ed7a6bc3a5b6dab88dcea101345ee2e576b019d6bb6ca8f2ddf6e030ebd6a55affab5a2a2a2207c782c95ef59a3f4adb2c15ab50ebdb35c79eb21841915563d1e495998f44efbb4bf6d88db7ef4ead900847639ced7e5382d31987e5d40a44be9cffba7c7b5f59c738e06393ff96637385a9dd7f60add06b3fce43dbd55fe5a44e67f10aa2e60b10306e99d0b5517c62c79e735c79829bca34eb6227f9b0f3022c1c487a98d7376b6717402303089c10fc6f8b0ef9b867056f3d7d531c61863f476d23bc618f75b3ed9b2a547796a1968eb9587a757d569e8d5f429430dbdaada8d0dd3a7cbd0abcb6d80586fb9f5d74daffaca367edf57b91df6e9176743981a611f773bec337db3203cef888024c02608cf3bcae31dad4d31bf2276092102ff79633eb314fd0a9fe5e8adb77e3320d663fe36e7561091cc3b95bd1d616c5134e76bbf7684b145531e9e3fe6f27a498052f4ce55500092a4189de37811092610462498bcf39a2f8edefbe2492eee3177d9db78905cdef94cb65bde658ff9b5f5eff3cb9b87ddf2ce659eb74f1f0e9b59907622f0ddf46bdbbf2676350d22e2fce2c1f4ceb58bf2e09dfd38b731b9f5d6b53c908c88304110495aa2f8f0f8d9abe8d3b15e499f73fae24c1ab44b191a5763a1b51b1109263e6d24290049128fd911c616ee37890c36b67c7d4e0408a45f87b294072cb4fe4d22030cff3e94e79209ff1480440b9ba642fc9be4850bfe0813b9e6209091052443c943089d0c2effc26f1219542611235328e952f4cf7d9da911288ff2408d10f9b82c4e505234c4bd12386767f7e7ecaf8b303f06978fee86e4304616ced944771c950e3b4481557ed0053a6fe7909939bafb81e5a043af6a6aabd8d02b7e2886987f0fb74a9687be75b73c8c427af8f6651f8abf1b3f4eaf84788c0feb43314806be3db62f37ddd5c1f487adcc068c331fce49bfe45649f0d18d94a0e791f6d1eed6a2b53d68d21a63cc80385a82eecdf9c51dc405e14883b077aa57d2a1dfc82d6e9b84a5cbc70dbed6ab8d0edd78e38ddf6cccea588f1b8ff9cd8a56c77a4fc7ad8b7e33201dc4b3b7f3626d71551a58abe995c9d753bdba7cfa12bd69cb7c6e44d765babca7b1cd66bae59915fffccaf2cf4d7e2d47a18b7f6ec1e03515da266159f2d12f2c46ff3cf3cbc2e2f6fcba88386e153a66591bbc1883973f76a12e0fb209221dba945990e797cb930c32a53cc9cba75f96c9f72d13f531dd722b08ba452bbfe4b738f36172cb9409796ea241d0eb739809f981e8310fca843c97b446c7a8e57b99b0cd305a8d3ce671436dae89d82d00fb20413911bb0588ce3e3d0edb4c54c88873d4b63e9709436deb63725f89362117c4a241993fcf7cb39f18dcd03277411e90cb23ad995fd4e672e9cf336a73d120d363dc4c1e34e59441a69c409e5bb446bffc0a32d5e8189629fffcaa98db58be7f79dc6c2cea03fae517356d98577eec72cbafcb9d5b5488c758ee17ad268f17ffc6fc32541a26c6337410bf7ee2c1d32be744a6c69e5c9703728dc4e448a038bfac6d93baafc6b414156536bc4c887be717dc56e2a7101eebe5013d3fdd65411af5d22f86a77ae4e847cf4fef0c8887173a020473c1014452fcf441c4e48574e0d3a91e61b2aba34ee7e8ec7262a6f5b92cccb4ad8fd51ea9a91e792274f9e8ad156d453e57632eba739990e7ee83dcca4877755c10bb4d902a82d8b18808571c1190fcb0d42be9ce2fc70f81e0f3869b0fe7cfa7cbe83f107da43f40df56495a1fdc884c244204f1807817643e482b3ff4117d57a7426fffb8c1476d80b847831a725b71316fde46da7ac5767b7fc2bc7c911954b6f163db63bddd674522a6e58b60a7ce84f0fafef8f02400ff7a5727fa73ae990ff675d3f690edd32608bb747649d94dcfd474392cba6c8feba6157251d3ea34177ddceafcee201c522d00929b07571b69ecd7b57d85f6b03cd3876d03866d410dd4cedb4db44d0283cb9bf60c296f7accafcb619c014db7dcb455cb832e37f56693f97bf79b796ffdd9b5ab8372e8bd9d1cb5d5f6aaf9683fd1ca9df968d7bcc7699b4d90f693b79fe86613c4f4eda6ef853252733b1bca4f9910d363cec2f6700e332136abe31ca32caccec94f1b17fd66405ce444c67b5a733b3214153da8dde4b57d6d20d2c264eae9eb653f7abc1b48bbc9834cabe3fc5a1d57a404ed1505f1993bbfac2ba328dfd5b1507ec2dcfde5a7eb645d9e6d3e9c5098f7340af3d395616e5d18ddd57151cb4ddeecdb1b9491546b55a4f421ad6955bb9f5aa6e06a2330acb491067f93c0e8e219861cdddb174dbc736e99e96e73759656207eddb5e11c1a655996617e79966559e68b937510d7755dd7755d972fcec586655996655996e58b63a921a594524aa97197ad1bbe5641e7b93a6d4087edd0dfdc1ed6f6f871beac8626a55f28e7ce39e79c5be7a25f1ce7c661b44d02c38a87fe13e30614a537941c3972a8e038bcc6693c478e1c3972b441434333e33234343434345e5028d4c95128140aa584ebba4c6e5dd7755d635cbbcbce0fb42fc463b2d591d8ea84d1a2948e6d8f76e9d9f6d897de9b14c2b56e795cef85b422fd42ef6f6a16a7390044ba74f9567c964f8fefcd686d45f8df56847fba6c5f9d9d34687af477c3f468d14af4d67cce965f939999997dbef6c9cc3fdbead9b39d1bb5bed9bcde6c9ebfed47cfefea782ca50bf1181f86b01762c0730f40747000d1cfb55c6e757ad073f9ace9ce9a6eb27c1062c0f7ea3c1f8a6fb2361f4cd3f29e9e969bdeb3e8ae8e97f980becdb9716de7ecc7874727259a3c4d00fd82ce2fce1c6e4cc5e7e2daeea4942edd351532b2eeb67dc7be0939962e7423fd9a5da56cd5a432baad3a2fa23d915714c4d7e942524c1063bec8fce9bc552be879fb85cd29bd4e179a488408e27d28129fbd88fc1f885e3a673f32faac43fd268f262ac462fe39b501f2d31e37aed2a3f756a4a7c7aed185a6cb75de82fab9aefcd3d7a503715f7f207a1ffb8f0639774783d67b2d9643d4d4cf9af75ff75dedf0c6c560b43e81a184c1ac71a180448f1edd347d7d583ead4d336d57dc8af0577ecb792bc26f59decbdbefc4d42d1a34fdd27c7b6026fe6bb31f93f3f8a0e997c9375f1d1edfaa9d1f834cbf286aa70ce6af8fd09cd36d155b265a89dee4bc5566b7bcc7b55f97c99a9b8caba31dbed5d1efd9da5caf8ef6ea00222d9e5747bb0388b4f85d1ded9669db08cdb9f66e7fd3f9b8cdad7285ce2e371ba642fc71004f24396a71d003f01552366ad5be51aff69f73607bf8c0e39dac13ed129c7d69a44888d0f1899e84c687fd391bf5aaa61279366ad5731e5f6b1e49f422adb2d13fafab847f6e645d733952586203296c1045cbf3e1ade85591c41442b440073e4888d0f1614f42e3b38e8488f6919e64013eec714bb2001f235070603732c4e53ce7279f4b6a040a0ed1991af1f7760a4500c1091a635051c6107cd6e735d1850ea28842098b0cd4f0599ff5ca03072f2af261a35e05e09ff351f79be26a2156858b07116a5f08be77e49db319ff28acb11e79497988b44627e2b0f0f29046afcff9f96641f60b253e5227f3825e8f7c6577d089102d79c9c37e71f4d0eb083e4acac36e791879500092a48774e9c90e7684b105ad40bc730603c9dffc260d61097f3506372746d68fa0c68c19b3c4c7127263c6bc93a21690708112263ec0c02406558a867ad82defa46847185b141551009224208af68b239ff53a828742ec161f8b16ed169ff56ae421adec6e77d44db212c50ff13b2952e1085898e08ca2229fa546d68ff010050bc408821621292af259af3d6c36811d61288d99804c194778c11b261833c6a7e65dbce262bf789ff035ad226246c0b5ce6eb8f736e8cc12c2e8ce2584f08adb4dc668fc34e4486f5a05e9bed70dc6c53eb707532fa07c4dabbaf05b9d15e2314db755d33167c1737b40c76eba8beea23b9761469703deb42a3afce926f4747316dde6397fbfcd3b233a94f4a655f1f1bfb93adb5daef6b73d7a4b5a5ac2d7a0d8dff660ca84f79bd561ba0db40f5767899af904d73af4b73da2afc3edc11b8f5641dff23a5c9d0c4647caed98b687afce74e8aa5e69ad9a96bb9dd939d3733c74d839d3a16fd2e7d81efcd0e34daba6bb32e2e6d39d2f59f1a9564d1be9eefb6d244d5acaf29bb48484af69d5f428f4dea237ad9a7075d6377babb3fcb05d0c2e7e93dc32e8d7b5f920facd7c485f0f92be8e6d55ba252cff3cf3b1b5b5f1cfe9d64e5aa76f5fce139b5b5f98636eeacc31cb3913020289104114bd500d411b6e8cf91f76e98912237efd07a25fbad8856dfbd95681f8eb868c5620fe726c29e630f3617a37798f5b8c06994cbe54c8c857f7a639e77e9c6f731bd1c58f07707aafa0cb003d15e5a1dff4aa1fa6da3974e858af36a9888bcbd06a305f32f2c1d7bdf25c4ee82f3dca544fafeafcd1abaae9d02be93448cf81cb4bbf40afde4bb758785ef6e0eb76b137576ff209bfe0a5f7a79abcf46b9bd79d8478ccafd62be752faec15bb74ac57389206ad1787c5b5fc45db2430a67c9d9b74869877bedfeeda75633ef32ba3363ff63307dacf9ce8b1adfa63eeb6aa83f68c799b9cf46b237ab70dc13749b7b6da59eafc476b5af5fc23ada96f264e8b26c53d7fde51de19d765e35c3a14f6e8bb3dda04a83163c68cf191deaf739e5f9cfdbce75ace0d5b1de8712362fa20414284fbec1ea0ca3c742278f8ec16a0d23cf46c1100bdd7d06aca75a1d5d4c35e4012f791747d1cc6ed8caeedd13d604ae71786168a68cfd76b70ace26a57d3d7b5e59da1df7b3e0c69bbe379d7accedb9ce732b8a09382e15f0bff6adc8eea72a1baf0d04991f9cf556e6788cb813e5526d543bf86b8399768c5b10464fbf6ab97fbc4b46657e55c6e73dfaa56e588f8d0bebfbf9b0a197192c47d328a43ab86f40b34e7edb3a30baeed22a5228c5463b47616f80d6d95a0d0f215fb6e261a912270086a29021d0b31b4e1dec603cf0739c75cf78ff4e91dfd7925c2f3d32dbf261234e9fb6d6df3a79022fc4c1f0f3c1f69d073489dd6475cdbabad9b44648323a42c514943c9c9127caad63fc4ed3c9f751eb7f32400e5054f98b284832d463eebdb81e756d285db348206a1af2afaf6cac883d98fe48e91da0069488b745b3902a57d216ddf9f47db31235ddbf0b529d219df7b16c72ba92bfc3856b155627cef3907dd73ce3d27c605accb31cce45986694f622125848f2333e4e7c5ebeeac27a6bd4b663f1784cf2dc7b0f7f82f6c3ae79c73ae5fbb6e1e17cb2792ca7bec8bf764941bd0fe94f1f9625dc0e8f341289f8412c2171f7cefc1d7e43df8b6ac94d141f8e283ef3df886f0e206b41f657410c792e3ecb9a5e7e20b1e6c29bdce8a69ed462f5dfadc5a260501e993aa7079b76d1210a8fc4ce2a2e8638cb12946b5563d3af7dd3c8490a584102e417f10d6f770be39d7418e9cfa07df7b0cd9650ea27b10e6d0aae975cad48d673f10f2cf4d6e10ee7bcf41f79c733c22472d9c16a0fdd5697eec96bc3c7efdba1d373337fba061bff7de49e69d73b0fba173ae5fbbee76fd0573bb2e13c332e7ba1d3733374f71d161dc76caf07b2f7a74f1c5f716bae79c7b6e0bf87e7e207427b343abace91eb718c268f09ad94f9c6e675aceb984f3653fbc4bceb907fdb9d9393d39aec117ea395fcff9db7ee06799fdf24db91feb55f6989b566ededda6b506d2fa205cb7dcbcbcbbbbdbcb3e10024babb05e0921a5b118a79479eef53dd16c0c6b156f1623ad5c63652dbf4a415479e7daaf5210527e89b31bfc3dad55356fabefdf934a26f0f2ab244495779efa550a82c95f8e57442bb3ade207b10c412f2a2a1a826238e71cbb73dc2e0b0d8f86eed72dc6f3319af3fde9e6cc0df503914942658a67671a5e26099516bc67197053f6eca737632fa214c4512582bd73b8ff1e1bc5182174479777447f1bf4b8bd7621cf3dc92de55a9c7a4153c480903f8984e4cb23cfcb6e09c981cad0aae7f2a95af57c6e3a5af53c6e3c9e7711b4e937bd63a48722f76123f800eccf53b41aa1b0c709b9ef232f3dfa2c82b6cf31ad5750d2d72ade24e88226bffd26b15134852e50da6fcd6f86417e99f48bee1bc23e5f96339dd815ad6ed2ce9cceab27b96cdf8a16241d7ef5bf1c47a583870c3cd786b27cb3cba239b48adf4bdae3203dc52da5d5a4aede077b080f4276d86068d153bdb2a2f1d7ade1870d4370c8a157277795bfb211bc83288c97ff1186cb3baf61bbd8cc442bf3b3b20da3f5992a4b9fdb50432d199b987031b60b3db290874949dd6691928d5ef623a7f4b897fdc0b8bd82f4499983d13e2117cca155ce7bdcfee664c6519b9324ee33e357365da9e8e857f84d1af3e52f8741266f89436a25fc3944e7a69a79a4958d7cf043cbddfa8c3eb345cc353fb90ca33c5a945d2ead260d28c8f9fb130db2e849be0cbec46890e5273f35e6160d720edf6542e03b2cc2ebf293cbcda2157b29e58c4b89c968979bd968dcdae6942eb3d1f8663e30ca4fb3bd9e9ed964fcb4617e595bc5decab11cdb7c2cb73b65d926b3cdceb1b694a4d508ed713052e7124221f7687d7fc4119dfa34046ac22da846771b51a8eb6a878af2e43d2db730475d17eaba50dee350960ff5a3f61995a150966328c7accb6472944ce69857cd330bf6cc859ac92cdaab93d7ac6568fd81a86a74c32ecba2fb56d21ae1b3d6b18c315b5e00bf4963a48c79f2bcc46fd21b4afe42ed9b36edfbd267a42722233333d1679082aae5d08f7cdc2c9f9979511ef5c3991ba09bea7b199ff2067e8c8de01d11e533a7d3cce934e33d6e26ce386a0bca66b4d3c96766a2a3661c154f54c8fdc97b5adbea910f62cf7cc67bda1de595c6b5ed1d1c5e871c0ba7198fae398e6d3b47a351860ef55bd335df5ec940d75e0d1dea3fd1fa0351a5a143fd33b4f2a2e8299afc79b6b111d8a66541ae37d16a4d2a634dab183e8ab58afd0813c9b4c4c28d24ddd087faea7e03c91be037c98da52cb647e8b1b32d48c88813723e885efa15b32cb66759c7e71b7daf2cf318a33b4785dcf30d2ef3f5cd60e66e24b9819441cf3087310645fafc32399447d7e2890e598ec2e890fb8b56a2b766acc1b29654b421a68d2b7fa1e86b1fea67307f80df24315478b7aa35367390cbefb6a07d970171ff9c25adbc5e990ed1ca463f524c161f80df243146ff239d3319afd1515e7b4773e932db768ecc4e39153a6adb569d6846b14887dc47af308630d1eb506f16970155a22047dba2433ddbd9084983d8a3c30829d6aabdfe35ad393594c19b3c858dfe45a771b9cdf89543afd8314dc627cdea9b590674b9f337dffef3f9bccfce4658f6c3d3afece75dd7dcae49b52732329fe7d0ab24f355ee2de8e4477eae694ecee12ce43ec86404cfe09ceb44f9a495d67e93d1b3cf499d333947834c7ef2e9b56a3f25919123f24d46fa2afdc8f3c37278de958fbe4a375d05d997d7933b5ad9e8a59bdcb177d1a17e8c06713d61352877d4f97413c570f874ce6e38b13f3a3713539b930399ce3e9d3d875e25e119d81fed9ac3a5f30b95013d671ae4d8a5bbf96a0eea38cb80a6073d5a794e76774df98d4fd17934e62440d32f971bfbdba4abb0618e63cbbc46e699e6d02ae933349b8d09e533338ce21924bddc9bbc4aafd24d34c8f2e99857135b34189d337ea2d60cb531b9f4e9d2d93d9f5c3ae62cf0c951986bdbb64a7399edb54aa3d9361d45eb6679e9f1443323a6678e790e6c041bcdcdb4bd5645b7629c948de01d32cef0cc0ec06079ff4d5aa38b3048cfe337090c975f638a147e20e6b1cfbccabf36a83c027e93da306223cbab7e93d8c8828d2e7f3d3bd2bf67f2d0351bcb2fdfc74c8ed587d4b0480b7b6b5a2ad5a72c88e939cb807ac56edf39fb3b42326d2ffb713a381c345075f176f21572e0709a8b69e68edd6472ed2676931bea6c0b42f991e7bd61eebe119275650064b4f6a31c00b4366ff521316d4755a68c726ef8b5a9f61c898c1c61574bf0cffe9a7c8d3ed441ec47beb7ea80b8976e32bd8f9e39cfea5bf235baaffc9206bd4cc84dbff43ad4b43ea48f3ed4df261a93c941071d6c674763a2f1da5ae29c2693f30a5e670eaf1a0eaf29886a77527a539b163cbac979b81d1d5c7016dc539bab60e335aa1907e26c72e74f09cfd0eeb218c26843fd5d87ba3ea437f96943654099370d6a47f92933b90e00f03adfc6b777f8c837eaee2ed03adbb90b2697b9cc311eae3d33d1761700a042cad13a0459b8aaa0b66e61dbc00ead82cea3356fcb3bea5302c47ef2b8b5bb2dfacd86798fe3cb6db6eaa76ddf51ab163a0bb4ce15689d295ab51cb46a2ad09ac2416bea9d86567f95a6c99969cccc92064d37b9e604cf50f95f36b97cd33af42e16626803d58e79b310431bda107c140dca5cba096393c97483f48cce5635c6cc7d8349f38c561e823f5da336964797cec3ed5087ae83dba18b0506af4ebda65ef0aae1380c948f1c67ce17e83a484d1e9d85f73ef393a43184d1ea10fccca3631e3320f6a08c567e93a31c6642b067e7a3cc21d3fa03d19b1ce558d33af4dee4275a5bcb474fa5aaf670691d826ff2e8efa8574ef00cfb98bfec077b489b69d3a155d0ad6d03ad823e371e6db6ea7a499f13bc235266d242df80692f78669a008109223c63e0ec93d17547d1c5076e9c4009a3244cf8d4d43fd1a4a0c6d09145052888a20a33ce70814fa429bf49fd4dcaedf430993f5e0d3b299733e50a3392969220e1e2e3761640460a84a0a404174008c3074ae18887a6875268e2e1f5508c87d079bc293b7b63293654722d4eddae34fcaeaca177a6cffa90de995f9ebccc6f921a52889c14b5612589222e8aa0f0d9217cf4d7abd3c72f603e7ec4f2d1273a8f1a6c30250933c002052794f089b412e179489da6bed07c5886ce61771b8f563d1ff6a52db0a1a57e93be6879d36fd2972a29d9fdc5b51d052c5f677d5fa4ccc9e43fa0c693af530d26efb9ee77aeff837ce0efe19c73ef64fab55d45ab338d34bcb80c872ef2b1fcda2c26993e96b757e7b355bc39b16805e2a1d7e88eb62085c61f9de96c15070df53f5ac4bd739a7f599000bca34e4430bd331c5e515151110e5c5484c326f94cba2ee725a571c4bc8dad4ebbdbd228f212e6cfe8f2f5fd7a91c274def137c90b14a670b591d6bf49608079f63df148b11751befd0687b5b9db6a87f5e6e23b085f31272ed6a2614d30da02bc1780af44f0c83fafd60d6b06dcde061d165e7c262dda2f8e9e57eb5d007ebf38f209c0f3603076769278fe9c33211cc4afdb445f2496cb6db77a04872ef2996e6d44f0f8c822efa1db0061260f61257a9b48870450be7df6267d4ff7740b6d68f3578989265498508282240f37873584b01bbed79bf3b83d6f7f3bebc3ce79f7fce8bdb15125fae8be198ced98aef78a9dfaa302e609f0abb44415e8bd63f269c56d2143ffdc62376daf73a26f2684c8d0d775225825e235b2a0dc13da3efb72179749cc9b1c73a5a32e3f805fa5232641989f8518dad086eafc94a76c3c87abe02ca0501be6d9d6ecdad1d16bbec25191125ede8fde292991e49ddfe0bccb809cbc31cd5119b5c1289019198af2f6cc7b1caacef8ba036c9c06436d32bed903f86d502e436308a3a13cf387c5ec06cc35476d31bb21f39691a13634aed16a2364c6657c7fa8dad05020ed32aff9fed0b7cf78947198fde0b7716dab9a633486309a4689602fe3ed362e43836c9c8606ade01a11ec35efada22811ec6de8b62ae5d90abecec256dfabe02e0bc2ae79674056a0af5529ead0355a73d06d950afe5aa542df80e3061c38b67a2403926d9c05a99a673a3ccafd0db55d738f83d612fce597108da268853ee4feaaaf55590dad71e89d01e9aff16baba1fb7a857906b7ccb3ccad6d88b3a1f79703597eb97b429b6e13845f737ecd9b06614c6d4cafb48494d7f1abb484d107611910d36bbe9910cc4d6eda2e6d43b19b9494d0f27b41acb73af4334ee3db3b373454c667b66d950cadf3b3cc6fbcc765dbc97bab44b08f8eda8af06bfe3220fd9a9bdc442be6daa56d28ce84b4b6f577e643a3955f739477c53e7319baadcafab3ad662e51ae01d9569dbc29d6aa93f7348a0661ce5e334a047b8c9ab49f43bfeeb21fc844b02fc24070fc0a9d21011f7fa694c4956f8f010dadbd57d387e79709732743efcced7db63941b98572ed27c560f4f7fe59d4c6724831985974161142fdf493bba52c5cdc972223a42651a654b9a2a5cb0303915ca80d0994cc89701f282f6641a23f5a29c6d90d3532d3341f80f62d07e27ffd2ffb39d1dd1df5f2f6cc3b0b5282bf32779ed197058954e2143a52f4d22f8bcecbb12c88a3d2aa8c62dbfecc525e40af12b3208f3a2aad2ac232892ddf186737a0a4b3f49e76d6e6acac8ef3ebf1f23e787afe5c6e575a55827f8fbaa5d57142d05c92a3e2aaf46ae111d6d38a2424a2fc2a19251945e9e9cb72475adda41eafe147f4f79ca757267f9cf953692919fed1b09d32e8aa56b167db62d384032dcb3bdb56599656f34cf432da44baf53d2fb9a5ab5e4b7d5cdb691c0009274c3082092db82863c370c2890e9830e20d13b0c0cb13963ca1021578310331d4f0a9feec45454542f868bd5a287c048ad8620b31626c89420b7c2c2025892bb43821086920f9583963096f7061841645f8ccdfcc070ce8c1112b408a421419d0e0093bc611c4d8620c2fbca80005d785b31dc1f1cb0bce06e57294bfebf2cb19e55186e070f4b2f852df93f7047a0fd087e08a79bd823df07b498cde93ee25e915caa13f23a1dd990fe5d7f6aaa4521a4442eb2b4b1e7709c9e56c8fdde1cdb97cad152383e4eaa157a8cb695c887a3fec55fbdc305a100fa9573ec143c84bfe6d32fb110ae27fb9f48b378bfaa0288aa238adbafc7218c68db9d3aece6561d706a6872125b47660902eeabc37fe29a3b50ac9431aa7a10e8c13f390deeb25d84bbdd44b484e8c7a960b3dc498fd6c7e31ea594f9e14a7d2de93f7049502cd7286d8aeb63b4350cecfd951db1b43dbb7dcfd653f9b0fe1194c0e1d02dbe339f408ec4a3ac4f13a7760f0aaf5c0e1bb3b7273d871b8dcdcaa7807bbe5c2b3c3b0ede6e0a09efdf5b35b5bbddc84a271f62bcaf9020c2f784fbfb90599dc077e6b4a46a150285a51ee4126bad4889374937ee272bc24265ae7bfb07552aba0c3b03595ce817ec9b9c4628284f49ebc27bda20efd59d13b380efd25bd2a6fe965f1a8bc2e20f59e7eb35528fef7e43d79563c4a0443b910ea99deb8101a675a847da0547023bd7a8ea4871fbd42d1ea35f48ac6a15f56d0b68afa10bf89c63086d6d3ef89db716028adf32dec59d12af8a23c7c2e8966e32d3800dc795d2f3df4ea597ed42b00387446ead5ccd8a805aface4211294c730860684a241281fe2a7f1a0b7e421b91d076665c2965fd3d02a084f26a75b67d12ae8385b4b7139f0c9a7bcf6c64918c9c645ad82eec040495dd82abf0b26da43ab20bb3b0b6ee33d0c01d12b77e8467ac5535bc17757f09edeb667c58bd22ae82febc17a28eaf43d6915f4161c005edf93877e890008980939397bcc829cbc875e650e7d088899053939fb66428f56cc2fef712e6cfb00d826d62ae8379b131cb7d958d8a0afb0f9f0525b8ead622a6cd0716c3ec89a6d889f86d6d02ae843e00c95a129b78343b7739e437aca5ed560ab541d5d055de800bc11fdba16ebc1ede0f0e8d8b5028e8d37c7798dcc3ba7d97673aaf5ce05c1882d8cf448493e3a2fe9d572f92018713485ca476c7bb54f466b157cecd163e8a249dac3d0c59bd4a80faefcb21feb83e85f0f43bd92f1e85cc6ee682e277aea0c4fe363988fcb2e27fa9c9aa6b15ffca819cd655cc988c94bd7aef722e32ccdc76a6edee1bc8b6e9a6bc2f848520875cc689a8ce632b408ff0cad3e24447ae741da7baf0b84b2651c0f199722458a944bf54e0a26c5edec7292940be3a3a31d7857c711c1791b217ef622efa50ba15e6aae8310e91a2dc2cf7403ad8a98dbeaeb55658f3bb42ac62368553531998da7b42a3a0fb7c3492e27baa752da745290d06490712d7a5bd12b192ae93a77e9d5f428b9f04aa34f4baa89d64b76a257192c476aa38fd1af45a2c2a48b277be5a3639a46b7554e864bcacd62408256dfbba87484155c7ab54a4678f965c2c7553aa249e42e1ffd9a98e32dbd9a9657763eb31f76d7327cae97fd5ddbd259cbf6d88fde487ae5bc07dfdd8cb76d158f5631bd4036374cca9c7c954e8014bdb2169692658a97242abd8a95957cf4e8ac257a54d22b9447ef71359b0ffc958f7a703b53f377d1759017b0b6b70d89be3891067594ac01f0c5e5d76a35b55be5c7f6e8bf9c0810bd727e3990dd49b99ccb2f2b2a31fef2ddcbeb96f197efbc2ede4e90e4b7cbaf5292257f7136b3f730a6af55d22f88c9adcaf02c0374dc70613cd900893ffdfda4f5c8cb565d9894a9514a92e42bcf5f0e69e8d5e53d3fdcab6c0e3b462b3625ad35da4e7a35713d1331fdd1aacbb7a5dbd0ab489f5fbed9cf947e7151fcc1b57d2d47418909900441882fce4a5ae22d9dd33169d29ae22e8e085a0c6134cef295e1b3169702cd579079cedf436687d98f16d7298db570612fef8cd7d22b29c4f2a7654b10cb9f4f97b46eabb05649eabc44b9dc11bf9e5ae179d6d41b6c7776e573d7198423927e837044941fc06f108e40924d8ab5aa479d29c61cb6ea6d8f873b027b15da2a1501e64dee6fb2fc5a5ac1f22a6379b59e9d7787c7e558ce9cfd989cf9f26e667fdf19f338cd62bfb665301e9eedd16ff90576e89573cb13b03bd3e558ae290161ad5c966599361d1b8f5659aa5659ecd0799c855d6e4d8c5a97bb0cc89f4da6cb994d2eb381e84d6cdadee258b009d7fadcc24b9488b8f2970f5a85282ec7af12114f3c685222c65f25224e2067d698a733aca70493ccafd21057f8521a828af5ab34040e18c37e9586b802bb4cd694d9e939b19ac000d5f383e88a1e68a90116272df8c2022017480186081d3891c5116b684d8aa066a43c75333dae00ca11942aaae0c2074a65bc210528928e7014250a94004c71e585244cca012f86a012068a1329de58228021224b193a10828c27a638c2a658225386a5c2afd2104873052efceea43687777adcbe907a1e82ed58a905452ff3abc482a3ef301ab33b4e95b4a755ec4478fe47abb886564d21c4497a8fdc7e4829a5af1a2f5f2ab5e08997524ae7412b119e2c29b8f170d75b0aa773d859c7b3a1f5fc703b497c360901bf4a48ccf8177e9590242dc125f822cb57c50c72c083222dbc3db6077bf63e9affb18d0f7ef67e7e3868b1e76a32f0f0f88165880c327c37501b47b4c184d6c60abc8d29546e494dab1ac974f9f82b7c7e3989be3ecf07670bd02e73be07f0ed302c02daa7a061adda2a364487ecad6a4f7595f6ba5c2ed0abba5436a9a9705939a030536cce423a83ed1c6cce7651abd81f11d7e2ccce7e3493c35cf643c343034a8b15e3c97e68f8d12a77fda676274368fd76b13ace2b4cadb033987cdd667b58be7eb33d26e6eb34382fe99d1f2ec7f90e192a0cbf94a785dd495569f28ea90c8d0a3e64c88f21360c719d04478e4e12a50c0ac551a60a3333ded33fdcce1c32e39776a235dee382579e5a809c7fbe01c02bcfd082d7f86fbe8d57ecdd57d81d4ef9f3d4f660c11fad4468f8e73cadea3982f6bcf23c0d3d5dac748156171e001b0556c7790b5b057887f39bed02ad726eb3e9d02ae7be6da0739e054a64759ca7b61c56c779bfa1a5fc616992da7889557087e3bdb94af9f3dd98fdbcf97ce47670dcad40e9e6ed717394a7b66d550eef71a76d1f670362e388cdd97f6103628389cdd9a71b101b2bd89cfd6bdb362036a6d89cfdde726cb58b168d77aec28663abf36b361eb7a3b91c32b47d49b37117331b2f653f3294abb4ca798c5c6cce82b139fb6c90c18619dac65d7095311a3f1fe78cc4a555cbd3c356b1393b83cdd96723076cf040c7f35f8d35865dfea8966d36419e4b9734e8e46f9fbbb4ca5d272a04f43e88fb489d2471f6c25c2eb0815e11e955dd1c74d8a1875ed5adf2230c0fc27011068c3064bcf9bb7f410cc2b73de4bbdcf8e35b959091ef5d9574ea8edc0b341dffccd89c75c254430bd3c6e6ecee0e101baa5dc99fa7e37f6c68954bb95495c55265b1fc56713bebf39591b0cf58aab82ad2abe4e0c6b8339739b51cb4e690ee3d7d49376dbc6e75a4cfedad8e849b23dddd38c1f1d94b5781e7aca4557cd42ae7437eb43045abcc65bb1c7de9158be955e5e1521d0a927ce5e1e9d57320369cb039abc6b2d10613a0364cb039eb1cf6510b4ddbb89e5fd98f6381566bfef903a2576e6c8f7ee75d44c13860092fbf4a4d7cf9da47ff9cbd3c6729cfdf7b4fd352ee2a95251fd255897ea61007ef6521e607f0ab848517bf59560bcc7e782f22ded2aa7d2f09316ac9210971cb8b714b7c0fc2ed714ac3b4d5699f4a68ed1ea7b8e21839a5bc691516545e5a2fad089f6b2a138537c405db3d19a58c5dc51427449104a525326408b810e2065b2cd1051094e023b148f25262b18297392f198a97524a29a50c83081b84592285155f6a0082247a20c5921c2445e146962868bcb479c94b5e4a29a594329e21e60a2d5c86c08436d260720327d050e2096c349112a50632517e808357705303144ca1066110610506085082124ee0042e59caf8220a2a8a1439580530fc2a41f1028a14227e3a934f5f7fef99707093bbf7de7bd3caf965da36939b7068958976d5640eb40529769d87f48a779e4f7f91367560cfcb1b2e4a1028282df0d94b784da8e127ae4832aec962680f2505568cf9995f252bd08859b055831bd3b25b22614d9c183d3f9cac905446cabb25129a147c8ba1fad14d67770c94be3b065d7c77774be9de10c3096574f9f679e5756069a1c407446630c5922f52e02506432011e9808924cc980113905842ccaede733d437bddddf581bf477b5d1eea557b5d7fbb4e6b5d9f3e9b8bd615c1f3827841bcdab3fd7910d6aa21b49b89c1f71acc43f24c80060fd860620858b814214a0a9ce085173f6032460d6ef06587dde20b337890031c082107437002290b28784102064c30e18d2bbe5c618224216e52fec2125a24810d2e48980115221d5c018520d8a0c88d1810c1c739c74dbc73ee09c14a888ea050c414530c41e9095cf82c608a0a6630e60c235851811539b0628a1533487a275803670d9e171d6ce86278228c2a2e6092048b0ca8508a82125f88e901184d54e102ab8a2b26330166cca90a245260a828c3860a314e0c3041ace0091a544cd1e249156708c1c5142868946184122aaaa0a162072f300ac000008c0f8250c446942c982021091d74b104941f30a9828a221a2a98482d01c6881384d962861b6724c151430c124db008a186133040020a064f98e12330397e95ac586285124fc8852f6e35f4fc68154f7949359c84f31eb7fd70ce3907832bef9460600477aee2418407829950be85c254aa02cbd3fc2a55d103231858f14407d3c9c8e3ad725eb4f7ab5485d2a762153a40f2b6a6beb52aa6cc6e9cde793e3ae2af521535706b1c71841250345184162c2750824889194cb060091a4d30410d9ff5dd41034270021424a31a2c2125891e0cb1040d359660128509900b284b74210412682c01092fae8862892d88b04418644829a54fe9fba4ef0ca349dfe70ea9849b0429a42471c2105ee00618444e8030852fba68512529081fe9cb46916a60b4fdaa4d9fde010fcf96397978c02182e394f01e0f38ed869824929208638c0bf888368c9660828b215a90440b36428187670a169e2481c30918ed49122c5cb4314e3e9071128613a389148b09578d5e9f57e8627cd67b5b43d8adb91c673db7f57439bd051571ef4404d271fc6638bca222ed37c3818bb6d3cbec8f30516c37c414d30d9c113f44a0c00a162f579a78411a45558a48c30d2b6e1086932b2e27377046f098e186587044943158a00412a4198861a3064140a18283379c409139a1e29dc00356a0188214a0d04418242b461851b09841138cb0c60e9c1c65499c1461a9f0c4d46223149e7b4f08ca0cca60c1115eece00844574c610430549081c61626f0423382008a48b97b6a84f9024b124758c2091e407841982f662cb1e4cb14a88431113c6640aa3ddf28fffc29bda006ff9efb7395e3d1abe53163461cbf4a2e40021b7ec9d6606ef802262e88c16ff684e75e7d38eee60927f0793ecf7d7bf07b5d76b8e83d5a89e0bc4bc2832bfc2a3501e3b15fa5263f7829e0217c9e78f18518147899c20d315878c20c26b488c184164cf4a05d193cae0c9c1b29696cc1c9e6d434d50e0f8deed0aaf6ccd76d400cfdbeeb15652642d0b41cb569476bbf71de5a7a87975c4ebb739cc3535eb515bca6de99dcf90adb6e4e6adbcdc9b1ede6ccecc7cd784ff352af4e57b0f42a4baff855f076ae32e32d9dade81d1672789d3b29b79c858dc16cecc5e5748a6ee75c39e8768e0adb0c0d8d77f643e317c430cc81f6315a82288fb9bf83d8569dd7380edf0cc8398a088563c66bb6da4b3f938566f356f50cbd612f6ea7873a358d662737653f2794a3b618dcd0328799c6eef8f77f52adeaccf939cb3cb381e833c6a2c51f360c710e730ec37068550f6955fb8f0dadea1faddaa1575bd54ccc84b3f8a6f237df3f7a052105da773a6ca05790da7001ada6fd66a3f0f238fa244493264aa450c25fa440f2ab14c59787de6d032949ccae080db85c1156f31946081d663fa09ccc82e5fcd3dae474966539d1cfadba0eaf3db4b6e9d19d4bf8913e277887cbf264f6e39cdd99712d4e3531f986d612b654b374b384a3e37d11039140a35fa8e4d75f97764b6ec702638159b2c02c596096962c312625262313926f77d012d32b0bdea4264c4d8793fae8d7a2a552eef0e869e1e618859677ee4530b9e063c1b9405b8ad202e372da4d459a964a39b4c0b89de7dc3d95d2b4b7e589795feaebf2ed3103f28f6f4baf584b4d0c4dd024cd6cde511ffd9bf4d5dd9256fddb6961c1b540abcf02b3a457f5d5a784ca73afb4b4d7b08351942b0c6649526f4962c592fb82f4a66c8f7e2039ef64afdeaf0fc1a157f22d151a744025ad729cd40d13abd35ee5af63bd03917606b8c4ee685f67787445abda3354527383f350ca068f3638a555ed53ae9c5f49c12378d42b777f0d9574432bbac22635700990bfa34aa4731618667e51b2e66e07cea969a994bb4af531c65086e6a443af3013f2dcd904e18fce1f6990f34887f89f4b711b7cbc0d4f26262b645ebe7dc97b59e012a8e4b3382af039e87cda5d527b63aad88e4aabe63fea9288a055d6d2bebb53dbede172a4dc746e556e7787f375e7cccf87ff7b74332f91052cd002706d5bb162c58a152b56ac58b162c58a152b56ac58b162c58a152b56ac58b162c58a152b56ac58b1a22d7d4bc480c172258b28eec81975e152854a93665224260b16295928e1232f4456c74a173f56c78a0a6775ac2cdcc165131d62bb3dde745f68ecd32fcb995c0072c10513673eb417622fef2fc45a7ef20c1dc48599fcca223cc187c2767b5c6eb3dcb43d13ad9c0981fe7c6e0cf8cd8240affd400cf8493536b57ba71ffa8bc49fbe9106d18e10dd264874e8a817dd6a1ef373cc5b323af4b9a15e6efb1008f54492190755c4409a3de9aeaa3e214616beeb2987524feb933e34a304528220d055112349674044ff1cadfdaefba58cd0dd0d90be29d90901e415151515fda3bc2a48826fdf8ab40f3ba00fa5fe9f916f20efdbaf167263c400e9a267ea43f4a1f9bc3ab08f00791f5d91fec7fb4ecbaf578fca200aefbde72ee77977d12aea9f5777ce39c743461965aba1557f29351d382aad2615638c91470bb1842b0d217c8b86f6de7bcff97dd1aaf6ef794dea41087fc031c69f4e43bb711c55744ceb9564961cb14c399fc965741924bdbd4a6ad374ce39e79c734ecbb29c47efa079c02d43abfe1042c751cdd8113a61e905f0ab0445932440a1640997bf7a9bdd89090ab894a0e1bc7dc204ccd0ea7ca7a530009f13b4aa3d849ee276cf4b7069cd45aba86fd4192f5c40fbdc5b34f62d805be7db041c47816b71366b2868afa79fbf2964054f1ee757e90930906118cd06c88334087a7b85d44912f7898e637bd9201c51d1b8b98a6b71aacf566df46b1d12b4555ad2648911b4555a82c52d59bad61d415ba5157079a51568f9f7949e00f3cf6b7a27fb592076ac550b7d715e4371ad6a5550ce54bdc174541cdee1a872386d8e73816cbb711db02d75b3816bab49ed60dab41a22d6864d6ce8dd53415ff888327e485829bf69d52649e942440aba48a2010a7e9882491b63c630210963b48007590b965e9042164e0c2759f6abf48413e213aeafa9162cb98eac03fc2a3d21e5657e959e7002c6a5f40412cf12ad6a3d4ebb7c2ec66a7ad537cd6c315b966f865121f9d6b361df7f4c37883d083e91e479fc2a2161f98861980349c79ce89d9bee32b9cd8a58abd665c45ad5ad7daff3708e6235a9762e68150487705db365e421850683f059a41decdfa65ccef3356d9346f32b62bc450752e3ed4deb8cd738d1f734529597e1572905477f494caa80c35570a2b764f39c524a69796f6eab40bce53dceda30376d974f15b66d95cae435db7bcd6736d369838eda4e7e3186d33bda73ef95a669af693387e6241b172d6975b1bcf4e73db89d973ddf006adb772e529b20d2a303c9deea33f2ecda56e76b51c637e3d346f4bc0df5cbd076ae6d15887738ad7aaee570815e497faec30676e8d5c99fb7eab9353793e654a0551e3d437aa381cac053c30f8ef4ddc4e8444198da00a1759d2846a14d8774b60a6ed569d84486fffb811b49bebd66bfdc7c2f0943e3ce59dfb1e50a9526b278d4af929227bf33ccccccee21d3d739cf3951351ef6e73cb494521360feb9bb9df55182c2cbe3bc28525084daeda2ca9326488ab46499220549c9172f4b5d58f12409922d5a92a62c4102f3e5cad2f3d0d1052a61e79d4b5d58f1e4257948b668499ae2963824305fae2c45b1a28d3a09972d5492788918308b859735a6ae89b6aba3724d6855865f7f19900c39bfae090d3ef7dc73271eabb37e715cd7129e70706dcc989ba2ddbee79e6935c2ceb9d3b2936bfae2549e97bfedf2b9559c9f3e634d6933f3daff7c77860df00e11fc70cc67fca271d7449b286fbfdc14daf48bb9b19ece3d1b20f0b9fb5d9de93dfd339df0f29a6b37432b103fa7e41970788793a135e52ab6693edd8e0a8767a88177b89a72f32bcebb23afd11d56c71169957395f31a7886dd1d26b7fc94396bdb39b6f5bb2bda73dbaaedd5452bf6cfb45c5ab54e04c72688f6d1b58f44321c97de9196e7de32f41ed273e21d3d271ed24362c142081f8c51c61821840b21dc1a42716d39b4eaf2665704c4336740e443bf7663a35609c9e7c7bebeccdc46bbbdf5b7bd34aec55192a48ac4da4f34469fb1bc1d7b8f422b66414cff28cc805a9bd9522eb36936bc74d476f26c931bb655368ace378776a32b0acd44b147e343d646e33221d7f7ab9b65b334841eb31fa8c4df5fa8c69a085aad99ab4a047b1d0f991949aba97729976d15a3e1596e10e9d3270d92ecfdbc37488960ff9a071a3e88bd1d1457afb0d76168d2b5ce61c76a267dcf12923f8f7c943e6caba69c2ee9f20ee96f4bf2efb9343931bd0d7b0784a40853ae6537e59d17b8004c184a90020b13f8d4d4030123096d7c4185142b45f8b41adfdd59fc26c5b4e2d4d0ab7ef61f36f4aae27cf360c28a6f7fa96622a939a8f4ab7505a29c75ca2912490200005314002030180e898583e1804c1747f914000ba9bc4a5e228ac324c8510819630c218610002300200042184d0ca049abf97af5c612c2b01eb764022b6d667e7c3619868b68857fdbd84b25634489a97725ac7d1a6b7ac81a2e2f836c96e4694ccc25cc8d0f7d9a5e417697828b132304267ddce67302e23c95ea3877b91a40c4813949ee1345c9b119a37e9f5a9059b248b3b1ba841b6b478a1a12616e105dac42a20ec9d82249a5b17eb18651fdee2de5099964cfd6c65cb9bb212f87e0f0419249b33687e63c6f5b3ae4c4faa1914926bbc2918a26e39c656217be9be3c030e73f53c810ceee29a86626bb7aab6d04f536c0a13ee05483451518b24f295e13ab4301ea0b01b4b16aa392b8d8b7d19e62306c01031bf4c0e9b12841599e9650a68ac3cafa4498b8c416761deb6f1b7d19df983026bea39c0cb15d22c115953c23e1d87cae5a03517984fcfa2be7967307bf823c6bc3b701502ef159ea2373c8589d40fb0ff0b23906b313ad04a8906ac117903e9e8a01ed8e872bf0e302d8187e78b527db4611d19e51ca89bf7c1d5561d324c03e5776e18f10adc02ffee92109eb9ca4a14ca43d95c1d4e307fd36a390573a6a9e792d8ace868606b3410a506c569588bdd2122175afad077b8343e30961ecf0e70e90628e4e665b425cbbea7088eb3003256e91d556a86c9cebf4a7f0cf2d03514745a122d87d085834a363f8b4e81d03eb7ab86ac6fd02a69f9e42f2c5f3a280262d1a2dadabb9a1d41a858f664a8dee8005cdb83e04a3cf235eb80b8a371af9e3c68be086d48138a0395149a820f3b35b8c4e7aca15ecd2a2cf8bd962bb1f42fe1c41106a2bdf528271dd98404e31c489c2a4105ad26d9764726cdd06bbad1f7e2d9ead1943756042b7f63d8b125a5e6296c35feb5a00ab3d70ed428a11b928f1e3bab49f44394067e1b9e982cf8393081c86c47674de65f9b8280a6b4a8b65b3dd76b6ce7f359296c4ade481cf37d3a2fd7ce9a1aac52aaa1a3091ae5109d2e7894db85219f4eac89be5a175680712b4ee7337fd831815d78385fba90b8a9667dd3a88d159b64ab6ce49b13bdc88dbc148734e49df7cebfd802e3a07f478370d3b96be8ad043f56727d9671635747e23689c2a8af5710f68fa58f4030e275c8798dcaa12d5c40f48668a4b0ded41d6032a8e21a89b4058b901d7496bacbd72d3eddba6e1048bdbde1a256a2f6e8073758df76750349677506f0ae30b5d88833e721bfdf021cd5d9197ccc1f51560121ee16a6f9d4614c2f62f65b92ce1f59e64221bcef8db3e50724d2a98002befd9af863f5693ad0d40d65ddf64c46d8ce9e6e366be1830ef1622ec3a6db1d5b2d876998a48bbf1293dc67db35f48a93b0c3d37313d423a6c15c9f8b861435f72221dbdcf6bbaf53ebd911cd1c26da1837b41cbb055e70d2c374fb293173c97ae1e04f28b32bbe0105cc1bcb6bd98885a7e113001cea814f3964a8496122644315de32622a77c6de4b1e56d6633de030f64ec2ff4a17f029f990e6453b9a9a4944bf566eff8ca43805e17d743f00045e7b0a2d019a87d424345966714c9775f32ae5c136959ae1502489620ef0e684c5d9d9e80e7e52e13d1f2490354d8cf8f22321334f1133d637ed95ef101d44da8e8d6d60b9dc3de477593cb5c79bc8970c2786cd9636e3676156c859a1e7010cab8b8e1d02f6004a6021bf194681f173b868fdd684f4f9f44ac1e2f371ca88558e57ba55e37ca900cae38d1b8fdcdf53b9c47af295061c4e36485fe37c481a8cbf7bfa0bf610027821cd72f393ff46df41afab19b670afb0a41eb139c7d9389b25d88558069655df4fadc72fd5064d3e800a3983f5579bbfeb422b65c94c3ab9bdf0191b450c0c0f64b638d3028ed1a2e115267cf8b888f53f78529dc6419fd7cb8efb4c499b84ef8b6d1dedb44634ee20e1d3c65353837eceeed13c13842c4b511df5f83ed6d8d96c1dcd68e7e854ebf8a2df94f041b03a6afd8433e664c6b0448cb2a62a300ef8eeb4c3b94af4404bf3cc0ab58820bbb0ecd804e3cb71d2e682f0f58c0e6ade5815d03b6331d4b33681a6ca677d21e610856360895085be9851bc1e4ec7a0835de7c53838681a802810954f956fe03d4818c12a5e429ae4d23dad677abf0243811cb64658f01eb51b4195c5199cf78bc030fb8b8e6ba4964d451dc3da1674d31881e6435423b0a04309efb0968a85a79af0fd7d7f8570bb61995ab6bbc4b71fbb0059fb58d0f982a85f4bf72d03aa4bc4c7d260311e2a0b62907bb69492206c356ee2eb2fd44acb1254917a8796209ede893f31aaa48289caf04e094c46f6a64dcb444a56299b016d475a70ac199e6132e773f667c17aff6a9383d214fe348cba0f41edb2cfa2dd665ba3bd7877688e8a4d1a3c7ceefdb9e9bf67713f7e811e5fae5c2bfa57cfbc4619cef8668d771f8631d26f5ad484c3f8cc8124093d6f421e2fed0032139ddd4b5559dd3b691b0d60cbeab700b989cdc012286e583cbe307bd72d09b3470a13ec0f68564756e9c58d498c7302974bcc5743a2072390cacbe8d1fb302ee1e4a4f8f89f54c224158e14590321d3bbed136a8bfd44eb2b75403ead59a63f93e101b57cce4dbdb34f7297666e58e002dd3420387706a8a2e4fe2b81e7634c665c9e06c2fd36632d95622a8d71995f29fdb33d0a24a69ee0638307f82b2493347b3875fee109560a951251231ac4023b3e13b70a341bfca879a7f64f3faa03401ab1763ce2def4300d57b605d32847c021b3860dc97a89b1313dcd8557d4223d497014a44ab8ad6bf9cde8df7f800c3500804c38e1c0f147fb9a12219a95ced02175d14fff953b22a544c519709b5f10bfa2caab6a9989ac9db153b9b43dd03ec682157391d8b2cd6e0d69eedb6c3214a0bde95a72d844bf07bc7402b77920af9e260cf9743d826dece20183176e9cf569565047a7243f8c690de24758be047c538f99410da220e0ea0a5eb484aae68a8f18d8fc2d79846be5ce9757777e08b25ba99bbc0111a050ea6b15359989af14a5d0fad6ba0e6e4d2fc69edc7c2673a96b6ba0eaf8cf92d206e0e580d9d0314f71d9e9e6d3c84a2eddba40834ae77127010bd37d9c0ad063d69b29af902faddcd377aa47f5ac6775136e88602060f37c645481cac1bac1157d0fcf89bc5b5dbd6619f5e6470a4647505154b9fdd542e1f4ce9002413b0aa5a0fd0372887df51c1018f081aa9b2cd2ab89c40bb412bb91833a913adb5ffe828e9f693942442f016ff35bea11bf3cf0d45ba6c93d45576fb6a29471c281ef4415bec9b8b42cb7e1b9635c83fd5abdbcfa0ba51b01ce7d08404687c2d989dec5c84d30874d2d90f4f80f91131cf6d4a00513ad2d88513b140e5674525919a84248d621fdb93a45e893a21071729b78ca1549b99d0997a6c8c9aca26ad8a66aa16022f9a0e83779f27bb88e3a780e49f496ee20230ec777c0442eed287fe5e0e6193f436e883ed409d5b8fbf6e498577ddfd9b8b1dec700d9af3e31f8fb10bc21fdbe0baea00bbc36a68c820dbd9d2e165fc407478044c8b8b27edac50c7ea979c2f3b4f68446b4cab8be4c702017a1ba06f38e2044918964f8ecc9a3ce0afd7a195b5c09fa950f01b94522c7488271d802fda3b7a84f1daa087e28ba7a9a9982f456c49265b3628f52b0b8085fbfb917ec0a619eed67f2d3048d4cfae250101377b47d6ae1093c62398bd162621e84e97cbb4ecf1b09eb64a0ecce391e24adeb86a5156e37b0edb489c3e427bf56acc58dbb5b2cb0f8a95b2d274b4dde7bfc062ef9a1cd735e6ae04cf94eef890ba3404daad0014d61bb786fa06c175be5ec25793e3cddee2d1a288da41287b4fec19eecbd48a69ae02731a1447ed89dda00cc846ed79ec539287ea700c5118c9a8351c2406fcd3e6e58bda4f64e2184e1efc296dc0b7d627a572a5f250affb1913a48710e4753e9892d03b8883dedfcf80c32f27162c43041142d977dc029f871d8f2959ab139cd15fb9ce50c902c3e2f59421deac86fb32b8a9a447d8c8bc636f936f3fb4a21b70f2c48e23a1d381be62a4e8a5a8e047d31d524d6c8265a06fe195ae64781c06822121e980b053130536dbdd1c80344b8f2294dfc3afd778d0450f3da89fd9c5782eaeb5a107fd438aefeb740c598b6539ec256b092962886a75871c892feaebc013e48e29a55a77cc3751999171af61636fe763712293934f26ee4d9a9914dfda3646e3dbfa64ec17755286a96d517e26a3ba845289bc1f9f417b0062919ff2dba1c3d3e17fcc5ea85e341630fe0bab98644a4c8ee00902ba1458268353ad984b946bdc40c833b3d64788b605fabb2509d3689ad78ad9a7e6e199202345df4cc0b5f801d9cc32a565855980a54c55cbd27e3ec0f944f1e58bac1788fc41df4e3e773afcc2321b3d2877e632b3d302c6fb80bcf4c5710dd11ee64f3098809f4ed8405cd2f939b61594374628bffe0895e670097b19321ba31abd2f30b50302b0d09e2d84411ddb98cd3b451826a62d92dc6b70d3d5b0dbed5ed64f10bb295ca79e3b501a6c2c93e997928f8d33f63b7286af5db1421d1a3b497064789774ea34a1752440b4d05b007d03ec3a2bfd0fe8287f683b941d7114586744537e62f01bdd67003df3aafe0d93da6c547dc0c0fb9debf25796002e832dc85fc0b8eeedff0dd1cd5bd0ead01999870f13dde9baacd979e5ee55a5021f0102900a2f5674d43081c17f96e72205ca49d7d284a2c074912e9075ec9f83f3110e783d036192045095560b113e67c12536c5e953e636613f9b81eeebff619a581e64811fcaa372e8e0dde560456f6a1662de8193933552a0a01abaac5fcb15d19dc28f496cf161e6f44867b7fd361998645a1f66da1275cf59796d86e207610d992bfde41a4288c9d2459719e10ed661343002da57a0922a7134abf4b8e26f60adc6b33d724a841288f1c194ed597a2167b69c7edf9cfb8958c1f8d51828e4843866ee164ec4da0505761d1e9e633803d56e9f110a6844241bf45153f8608a64f0dd4a866890e05ad6cd081e07e83d8883715303ed615263b0e1da2cd5082f25ee5d2e0237ddb29129689ec05481287ef587b284c96ce605b9df343c827d804179fc43a1748505ce95634f114e737f25be085277a6a5d6465b458d401bb12f3ec2f1f633f1294e25b3536c1e206c20192e42fd50bba13ddaf50a7c7edea68e55ad2929ee28c03fb85ea99061cb07c857ffce4c8206c0684041d7ae12aa25732cb1f6b6be8a9a7a39cbeebcebe987c8609b7cb69c116d014e37a599d476dfb99ec8a3f65033efbd8c03730edfa83f4df94769ae38a8f0c62b14024f5de1982375571987bb86cee6f1570d57881cbb8124c79a0c7f7ff15e1c1ef92895389211c767f9073dc4c43c8de740b1b838d50f2c8e059117b28c27ebc8626ca5692c71943bf91630ec0e1bf820390224c29e1ed32a81de74acb25ff022805994fc803f595d0f041c905def7f1505213e150bf908f7bbb270dde22093ca1699cc1859d8fb98354f94047e9aac5b556bd226b6e46b0dcd8452d7b058982e29ec43f59f26b5ef39a5835836a1a867d2d8c6fd255facfbbc5227663f6fc98b391f56f262dce7953eb1fba1ed7dff312527f63f5ec989751fb664c5facf2935b1fff14a4e2ca5b42b464b3f74d300f45c21f5b00a867683dd42304c3e92ec0533cb606b993db06f080dcbb317cc2c836d860e3b8557a4a48d93ddfd7708b668080103e5669b0b4bcc1cc8b69aec79cf83bf4b4260422bf462ef4dc36a63aab2cfb59c47067933cd12f98ac4f1c1a856c0d0c81e1e0a0ce4668d3faacb4ccd03a3818d1a03fe6c32acd5b8a11207cd61422e2d06e5127101bf67ab1470ddfb717ef32ef346af8b656a832eae3de02875ca0525a9e5c6c8e3bf5c0c150039267705567deecb7cbd7cbe7ba8fe61028f6c089d05fd0dcf107dcf50d524456799a963c2c755a19690aff9e1399d2c0bc03b54fba21a89a40aebb98230102f03cb722c1d82132754364dd2684b96d832b56d40d105fca7eb878cac62085be8f0c0158327f7a211925d3bdeefbd0810bacd5b46a59d5abfa5587cc2a704739c1aa2bdcbe7c6c9a2ebc13b1987b8dfe39747ed9bf478ab3e0883055bee7d54476ddf70da14a4e2179974cff1983b743b42f99c41c76c4fbe9f5f8b41312756f0a5cc123d826922a0da226118e8e99783fe850647260beb8265038874227cdcb1e83c626f10ab8ebce5424d4649c956364bcdd3d11fbc1220b5cd5cc01348d63a1904a65fe0b259eecb73e89898e131eb2c2baf3ffdd60c4dd88985bcecebdafb949c11be93a50061bbc5c464be4e441eb5ab320da6f4037f8f616fad77f519864b4bfd156c067c738c5d34b11476c24a437afcfe16e15d6dcc6936c0596838ab504e2ab1baf75de36127cc0cdc14dcffbbf57c688d167fcafc8977b6f0e9f3320c81d63b5f0c251718c091af0536e98b31036d680f12c0c44cab61c0c6e557915dc6dbd802276b7476e7b75eb3beb3e96a21b7a9f69051d795d3547485d58a8f655d15021cd913e7b26b8da4f3d03e67144fae62efe05e88d72e94daf248c08b056972ef9f2f8dc69cf4629426f1ff271cb7aa7e0c98d5ab4ca72774f4964a2f5a29eaa9e0174cb7f6a115ccb59cc25a77957d949c05c719d9e324ef72e1e906fe70ef679211c430175865e70896b9db3213cde1fd354fb0d5ba732670f5bb8111231c32bf5d56626f6a06f6b2c6f3fda7cb5f6c489e989909fb2acc3730d16e44fe2202c2356cc795b833c8fdd9c2953dea6f56b9a85f998850d04d9c6996f78c82f0c256cb7da1ba48191a686ac38bdb566c6837a17a4cdd51d011cf9c345075420d73eb5531fd888bdb3baeb63063e2cf4cf01d1779f1de5a947ee33f97d82809cc6a746e2ce613d00173e6a3efebbf4a11120872283aae69f880c0a70bf87544da85901a55fd84d4561e549ca6c874909670721f433d1003ff5af35f98dfe229a75fe8ccf21e81ef3c336073d673567c02c534bf9e8da0483dc23b20fdd961335fc35d902bf4467dd0b1c1c6fd1fa17f9fb177d242dca668dd1acc00e61690766025e4f85f97b85765e2b8ba957daadf88186b14938796c21cc3536b7890d9c134439320b836141969a84305803305bc5e74118091ec116b6934e9728f1286050b3ae59c9ddfa199f9926fdf5e1498162571291911e23214de5e5980460296307408c396b9974382ccdab26ad61dba5ed10bbf0b28c6646b54adc4c2bcc958b61b82e4d30996bb20c7a27de03b0fab018e2bf4a9290602c0758096a24ce47f6dc577332db8db5a04b773e134750266dc68615ee96476f94912e2aca0ff55c5a95c93e9dc3ccdcc5731897d3b67f4c69290e88f307a07d7aa55b4ae7a2f07b9ae19a6e5087f95d5402b5c85701c782af8a7c009c06c5f9674925b14cd85aab6cd1cdd5940907b5e51493f3a435f72430efb28f520debc2b7c66f3a7c770a69d34873e5a60ec0aff7d9cb4d55cf3b833f63016456f5069c74ff9b531d3fa71d7cb1d607293c22ece489e544b3cfc62c4cf69fe61eb6397f954f2307b91856ced0d01c2dd815b6e9e808d5e5181019cadbfe70b750be6c8de5d56d8faf5884019d12bef79dec09e039ee6108a31006daf019944b047866d8b07f9748c5ae5f22d57331f913ace2592be1be2387a1a5d09f4e2ee6d3f990dc17b744550295faee00a6318c47480df538ffec3dd9de6a90d2c03bd207169b5df4a74944824b80d0d2b29ee1963c007538fdb3b081183b41c97e9cfe9dab47f74f48b4bcd86958851c5d8402231fe8849c7e8d3e85c607c4bc8d1b12e35823a6dadda6dfac4cf984ec0c0e67657793bfdc54207cb930136e71fd1d06d885de663ce1d29648b6d8472d4a19f967f898e2e9a03720b79379ac0adef254f4edae805f8caff10ae77db980d5828588f7f8d3aef20d8300537449333a846457610c4294af6de785ea5bccaacaf9333ad6631c709e383d9bb92ae0e348541f77d8c09b0ca1967a331c52ce2c3d156f990724a341bf5cda0f0314f6e4474bdfe5ecde9286c31d3b1558cc69d36ede2a1d39140620cec1fd9e5c5a06d0e70117997483ffa7397403f083d061159437e7b8053b873e1a9f99b1d7d768865b15dd75b491c777f5b3d0c69d8c55bc7d3ecf58eeeceff8916739c62bc8b0795818722cdde89dba5e9397ad105b5332b116878fe44acfa5a3eb1dc0f99d633c1016683cdc4835cea1a900081eeb78b5a49233985609372460379d237470a265b5a9fe7d7a263c6101f479588e4f2c53c3b3fb7da7654f49ec6766368666d6d4b80f0bca77680e82778ae5c6659bca36a9e922b1526ff1a256ae582ab5820ead0c66019475540c315090ab753999bbc14d8a2f095a02c5db41e415bec5eaa34ebb4751fdee49f293e990414446ab529afb4224fb6f586033667f5e5d120a6631e88f958440fff43430e9de9164262e6043d0e18907a2af6a9c57eca95d4d74e72114e02a9df6480e5f6855ba6cac6acecdbb1fc9ac30bf2ab72f981e29b393db8561eb29a1fee6e999d817e4d389afdfed9af9c480fc08a952fecf577251ef155afe50188c75c80c9b97c2d586254515630e64bdd9a23bcfdeab8eedac901d48f5f3d3a8b5fab5cb6bb9d0187d8208e41df7cfb83fac053553f475c02dc11e50e952805cca6022b1774f904d9647b2cbab8390bfb3a416a9f053aebb5dbf075524a60aa0b42dc9136933328f83cb1a24daa68272cfa498a3479114daa68132deaa48b3259d14f56441315ed8445ff44879e6da702016215f30ea600a043815256cd0856e72634ae527881c1475ba139f4359b9316d1fd460b932fd2c48a30ffd8b21317cd84a29fa808e4ff5a38c9a24fa648455ed5c53b5d283f9f4ad75a464c4c9fe1bd952012000cb014f930800c16986be2574130ec03c2e22c37f06f42caa9d1e450d053c95e0dddfade002589b406d3512761526682f89406a21bac17823c90fe381b6bd7429a0058103dd5fda25a360a133b9e172017f2971fc76890d488ba055ec5a03ab8d715aae00b38be14886d2271e54a144bdc1093c32b4b0243c75524897c1ffe1bf893000a8ad05c2baaf5c54b76b27ad702fca27afa71d5c09d9f00ab16b99166c43252c2762fb8fe795ff5204f2260d05a09b9003e3002f3ffb5bda964e1aabcc265c7b2213fda007b3e724ad23fe45cf0119f87a83a3b7188190c3bed20444ae9abfe821d5d4674fc2ff7e7a0e2ec850d5adae0bacd1d83cb7be14e69297774d8e1bdbd45423cafd709ba46dd96cfab74a92d0a0502bf66e16886b28b126dedb510e0a6f4c4cb8bb9c90d3aec1d94fb6774b7a20ae430d55aa0cb2e9744f950b2dc9f5bdc6cd1609f0d7978790f679052dcfffef98b6dc555ebe417965b89b831d429e3a61b514b9dd736bfff80a7fd8a5f1ab475a8b8beaa8b8629f4b5280f6d07c5f85f5645345f0a273abb63d8870f55eb28b5b662e67ba54ae914c699c3eb41dfa88037f208d1e835618027e6ff7f4a47dd1fb1cf5132965fa24a93b55346161c51f79f8b1c15d96368a290e6798c985fb52d5bda431b9dc3470e20603c45cbf0564fdd879d490b789d61a7faf32bcee1603cc1a092a7ac2ab15533038411a6f7f489b4ed4b6f82f74e584b37d0a262334a87f5f7291592dcf9a3638ecd517efb4ae049acf864205acd81453524155428990e58dd0a0a88df76b1ec0ec3513c949536722a9296d4fbaa7bd900f8ee0f9118000fcb5f1ca1f740da7a2b54627e9f7c18f4e4cfdefb51c98cc86aed1905f6e3cdcb5a5b8745c0afbe2196d29bd1781467491d176f4511dbe90278ee36069285cb5b9ff389f84ac4bc83a8f75e4485a582e5cec87acbe2aae655be08774f5fa200d2c1a8fddc3d41b4bd83e8a1c03f04d3cbedc43ce0931e480bee55f6d6328b05eb62dc6653e3376ff5d525c795e6d7ddf1c422a168a0fcc8ee266469d608ceb1f2b57fb33447d6e1a3370ce9bedcb9722be5c7fbc5f088c25140f6e462aea3e67fb2c116b161ce2770a106367dc61ad01556bca58f578958cfd5767e10e0e00ded6b55979dcd63cc3ca82d15a568e7340c9897d4cbb9b0af10d82723d81e771df7d3a7379ad6189f089d443d5f65d1000cc443783a649268ac508aa47f4e4b0a6073c904a19bd66617d7eb6f926b15d092b6fec76f158a467f155f483734a6aaccf03cb72d7525f53631050e3d11e06184a5c36504b24de7ca14f5b7c5332933b04998b90c035ebb2afe22947cfe8f816578ea50e19634bec2181b72a32112a20634cd40d5c94394b94a30f001be2b222376233781d6750fcc5cd1fe4faebb834c3b074c4b7c043376f678200a420b025ce62862cb6020f3e347874596ed9ab0f67bd490ae669eaa16a662c665e1ed4c6ea1d7bb0219462b38d5d7b9b64913f47bf2e675c9080f3c57c9280cd6ec730a3cde0d6c9cc0875ce453a835b9742571138325f4b8b6fc7e7de7e1da6fac43dc2e938c99d396b59d64fb8284c7252c13a48b902787287308a16d58c6fd96059fcfcd35e0c01454bccc1b71678d84e264326dea4e11dd5daf78c162803beefd609ab21862f6633e540dd998e278c164bba0f4e604fe8e2ea0a0c46060eedff70ade18e2a2cb031becac0562a0d0027cf0868e7ce3e69a8b8eb3112d07e14d8b2b08a17f4013d083cfac358da2c27529a8bae9b176cfcc62c861361f8014539119b8747dc377360b7d34150315094c1c8901c675358bedfd0eaf57331cc188594c75db1659f47930dabbc1f8f1fdd0aca3151d3db0cefb1e876c579e6991dd31ec22b7baacebde3caf5ba0aaf1bae0096512ec6c77e48eeec73b8c75218447224d5a8e983028e65e103f2f2a4d9e9e206778fba6be3626fb0ca7a9d09f62ae92dfa1784cf6edc48d0a49e921b9ddff2900d3b4c87efaf4841e4a57089029ddd024b114320215921fc3a58c98d25272d7e6603dfad36ec92b3a5831e18c8440bc8e54788be944ed48244926cc5294f967e129311e1a9ab1a824c00b88d17c56f60e689ba93b2c5ee9918870c62cac167c5209d18182658b01171e29a689f2c86a52681e7504d05e1b33a54f70bdf44a93f0f3b2e07468da4ed79f9ff45ae7d4717b491a93b39694ec27cc466c5e271b3ac4e75cb1aa0ec0d6f700cada03705ff90094ee96799e3c2cd4634cc6551528945b4021300a89ba0f099d7c6346f77b72089e4a494a3e81822e2dc172533f9b72723a2363b41fc21ccb7454d4eaf47640f8dc1f7e1c7846eca4650dfe9c5fce6cb94e8f780a42cd2940811e6e2ed396f63b29a6560fa9d86f40d60402c9992dcf939a6d0c70046e028732e60f8a48f68b484b44c022ea900a34fb8501f80575960f65a3f7282c0af1fdd72da0b77ae7fa6733fffd37e294828afe3584248572fa25ad4834b1a2b0d82aeea0838a5cd2393d11c6bcde8819079b50565dc539b16d2f076eee52be52433e89493f109dadc8b8d897d9f4d78005cfeddfd8f2faa2b6419488adbf87ce5c497775eeac41ee285ec72afe22f3b7149865edf9f8485e4cc330c1d12cfb49cadb4deb91db9065a2a42385b9ed62f67e98a28521afd877f204847aee0a35c89d64bbc51f283124606e0babd73a48959217237adff48c518db2bf5a7c1183f4bde3dbe45dc1895f141dcc7f77a98fd4d4143f1f27ca584ac2f710cf9f124b40fd71809a00b519e358a40d26a19f49a4a9df0a4cac39df2141f121bd4145bbbfe6cd90fb17ab27975cebd4a8a79884a30005594f3737ac13ecf60432a63000d0a5ef8aed8b21824cbcc2e5120b7458fc5a9f005b63f4d662e183267a2e9efeaa64874f09ad089ae81760f8968d6e3e9bfed97a19126795cbea2638375c812dc41ba4dfd458f86e74956160f933ca5a19b28b138197b97c439ec5ba3d1fccd4855e5fef2b2ee1b31334daf2c0f3f87ac527fc80c18e09195041da1edb2e55216dbc0e4c0f83b53c04b144925b5fee945457ca4a5ae9b2a4a14e87c739200d81a514815b872eb170d4ab898813206faaea9846d9f14e8044fdb4d524ede80ae7c04ef2fabde4d988b77b174394e14a2df3f93bdb42ca017cf36c3bb082f4adaf354acf57d7621b655325bc67e1961daba87ca40d0037f758c5b0f7e3660c106308a4ea74a1c845ce2f27c2520773fe9ae4770cab7e5295602a164fd6c235ab52e13dc7cd570e13e1c5992b98921816d6e2737c88ba2024bb317326ae2e21d6c41447b39e64835cb16b8e47a527db2487edd37bccc9bf939cf3a883c782c8141367bd8db0329744a1f249f8e94b16c91dbf6770198319699cd21cea1c2504f55fd5e82ab48cfe285f50acd6479050ca6574a878541434d3d9f2180456f5857941be39a0d72ffa2a00b5e4a59cbd9defd264cf8e0cfd145e5f28db2b4f50190514f2984dd17a29ec23758d77a714cf90b9899c96fed61249980c13dd70096e1e6d8638563c39a32169f5d896620cadd6d033b3b46264b64b73d177dce39c97abda5df677172f3b929d484dddfa4f934b5f4f0135fc4fb3df20b6e9afa7f0ae20bf9c30febed809abe2ad1533f99d3f76b901f494cf16bee5cad7732af8a1ec6cdfde3fef6ee22ee5c2cabe1b74a3fda08f688aba9ff0881d887bc847211cd45894ad948eeb8348a8a11a0d1c9b5e698a4fefa75be9e653306db2d97ed90cccbae00f23c56212029c6e0565ad1108201b25b84cdaca4bae498ff4d1193f10ddfa01d98b009da3679afebfcc41caaca3889e1412ff3d46b5a74d124c7f5ccc45c64ec06f8ed1367c2dabe9797648006e3cc304b0dae3237908f63b13c9b08bb59cf766606c2c2d64180c1b8517dc798622489541bf79c913ff6ea103b45bcae07fa8c8a93d229313e95e8a5f0305fb860b0683fca5b1345adc4cb3787f6943512915f62067086fe374da8b26ca02087e449ef12cdf3cfd6bde9c50696d189d6afb5190f53d5cdf8a25182c3e47ccc67c57953ac175f385f1d71d33d488780f2cea402b24f2010a224fb8bfddbdb56a091ea20c350dc72f894e03a5c00bf9a5ab5705dd062729e1c691d0c8a8b9f9b5a0ec36467bcac9f290dc9b0600660f90504eaee9d7c3df36fb5afd0e9eb60060eee433e94d04d98f586b6f9b8bd0876f282f988c4be98db85a76f526ac4f9df52e5acb3260130a29b00af11b780a05fcda0eeeda21c8ba7435113412fc65eb7ac2746513a0fa634142a2c89c2411de2ea769f93a18054a0203f2922d3b8db7cd207b68a007614f28ed6fa897dc847ea4b3b850454716eb428a69d559887667e6be7ee2cf2bca5379bc13d59fb73778b292bce43bcc7ada4833f7d423965f7858b808c502e754c2c67506c52b4b6bc8a9112338d92c219d9bc35a060e3f7cfe3bb47c9fd64e97a11cc886bfe3529360f998e503e25ba94f74634b7e12f36a614c5a0740f901199751a76139f42ffbbc10180edfd707dfdaea377f1a79b168309af82f21c9e96996e05879ff7af5bd072afac3511541609466903d9df7573576a554a7e041fc4501413bb9dd251d4dd91b4c7f85e181b2306a91f1a5925d04e982bd74d00a64cdfde6c1838f3b80a6678306be06ea0bb76e79926a1b197d6a729ce1f88b8f7f9a41c5159f3650301a1cc23adadd243b028f85a32242aa541fbc17844cdfafa9a9857e6b1a648c315adea07d40232ff1ca4bef67440c7f18ebdc477c96813210334684cb152b387de078b0db246b454337ba9ef7482c4665eadd880e1d3f7dd414408044d34179369e8c657517356ac88640ba44b384fa7a34b690be03d6ec6fe28e1da525f420cbcaeddc1ef269f044871fda9b39af5a92e724dbdf175baf0b66efb473902664e3619e71e819d4e51f92cc07aae08c0b95c7199bf2dfb923c29d238cd19ed0f2cb42b6b487813074fe3ac4123304b14cf60eddfa68d843b03b98a4c03dd6e8c4c160c0d5cbbfe2b02858247cb85f9e09009d891523074016cfd22041bcbfd9874d2a2e8b92316834cf1ba3df2418d8249a55a8c5f4edb0202736f61c60b83a92dbca0d21d88b630c046a766b37918c7d52823b097c03748013fe704325acb90f068a624391229d12d83f51f4473fa59960beb5dc1d8031916a8225cebb742257c70607053c47e822f27d88d87f6af99cfb0c0aa230afed2d382414f5def51408be0e974f84eeb02f829f34015afd99393094507d282ccd9374602ad0ec4ce2e504f9df68863fc069239d61ee104477ea0a42a64d135a62295376a48534b30981c31bcd08a701285984625bb74b3bf53545ed4d54e4bfc06d4e9bfe181062f64c119acdb190f50326e7e6b013d9329790659f0e41bc36fb4618b208329f367da1ca88780b165d14452d845cbcda55dde414f9d48aaf87915800e45ae238728895307747930ffaae621ce968ad9200bf6fd71758e1037d90b1a82e108b3f038b44020caa91a479f227aa710031f7aae7fa8f99c4980c5c3b9fab9f787763aa38804e83c30ea40a2b51b30bccea672a1fc17f7a01ae0ee198e21469b624c950962b1c7e921eff80ae69a816487839c61232d723bbed23d9cab010a1b4f634d5e5dab1974f2cf3451f8027f0c5f884a09b1bd3a2ab3dc8e8c323df4b98577a9d42bc6398acb18f5f7c6bff45cb6dac58040abd19a6b3fda2064fbd492195432750499ddcaa8a01215b60a42f58e3dc5c768fbc0eb62cc2fb51762d0660bfb29f522244fc5c59da7b0088b46c1d7816152bec7c857136cc18ba2dc7b772751f5815758b6c70d63c0d6fcb507c7442ead8a29131a5da971dae7944d1d865c1a6f96edf76d802cab85c64e548347f0dec9a14d63eaf26e7ae3f841cf7618f92f5f95a68d750ff990c36f2c5426893696a7185bb98dd60229105643570d2c85364a1f999da9ebf82acd6ad1969c4e8d63c77f72bb9e083323d68945b0a27f8905d25e39c1bf0325a7265baaa7c0d7826f12fc48e0b5b9bb4ab74d432ae4a500619075a75ec969d50ac1879d5710e1e801986fdb5992b9149a38d3ccddbcf0169882ef9c44805810d9fbd9a7ce074cb0c57177d91483a36d5fd9df08580716c548fb60904cf6b33377c6cc48888687a4773575177f073d3b1ad4135aa4f3ff0284074e29432ecedb07adf79b6adb87d607464a40dce4c1afb3e86f13a325085d620c1c0bd5bc13451e412d476da3009bb68e6b38c0eb5260b458b9a8d2bafeaa33311a49abb709b17089599a12aaafbfa1a54adc99a872a8870fbcee4c4f659af422b8d2a498bdf0a19854669f23538dd46770b861d44459b1b1b8c45a5c4842b8f46b49c7c531fccab212cc6f0740623a61087c3ce1cf5aa6ac9130a4d9fd84fe2bc99bb9ce85b6b4eec866ee743c44935b0b18f9c03ecb8dca1fd99d23b314acd3302d046c7ec5ed78b8e4f248830d36cbeb44d47336de285442a9b03f08e194d394e74052d5bf2e1f2c262244e595a35c9675168936296477c31e3da2837fca8d36d860fe34d38e3b75f868538ada2d6a2513c1ef6f1ecb2ca08707f39ac6c7c4275e94bd3eeb6c2e9bd06f8a2003b1a6f09c18684c84a62c47bd29523d9fa17c14ecae89f73d8d35d28f9b2720eda21701db0956e9d6b4f8091429bd1cd55f0010ff2e8940e133eed643126ecc9815ba4057df40496570c37a124c6b73a6c4a76ead4b8e9e97eae43130623d3f6b937ebdcb446e264bfa5c8053daa94fff599be1d62de95d97897e91cb5105d4ab6c414f49b3adb609e95a97dc25041a102fc03159c84ed90313a83aa9a6190143b76d15afcce118ef8c79a536eacfab3a36e11e5776961af183313068e14b45bb4ed245375c0125019e91a0c7df4057b8a04e4c93e53f342ca1cf14b8269f14ac4fdc99f28320a13373e3a40cde5cb8fcd2151f8aa663c673d0e668e8981403e2c27608c35f111080202be84724279b1fbaf52e6d3b88ed6f32ab73d17a4d188d601655645442e45a642da4437188380a2d6b64986a676cbf92441440a373e310f9a043a6b5cd18dd59780b35e3bd99cd397d71861f449eaba0a17ca4e5135625db7f99d388530a656f5aa2b94901dc6a4f538951afa523eff7851e9ad9e2539302d08b4950e7433ac7d4480109326eb7b169898ac905399f4b12177e6d066cb0bf1c81884bedc3ce4766b34b8dbc8a417996b8ef4f7800d18eb3a89eac88d899db07fe5b76b95909272985cdc3702a0acf3f9013135c05b3da1259eff5b36be2fcc0dc518b925692bf16b376f7c30bc7c284ec161b88730286a28876dcecc009303d3ca1d467e42e6184f30c30e0bc320614ac17d34b259a488c4d4af0d14529ebbfe1a9f9e7a7144d9ff500d5b06e0460555171c3391babd72517bcd1b538c36a2becd6cd7f90e2166f01101341abe0ac3cec95ae9ed57a9bdef89f01efb4087346367712111104a6e4c1fbabc63a13082d054f22c9047fc441547a03bde9897b04a53099a1624a7a9d7562478f0902d3a9238a40a9a04b81322df1b641336c4c240aa5684c839c250ed7b1228ce74730146595f81242ccdb124921429dfdcf1065d05a0452b30aa7deacc4966149c461ccf7986241797c8a064e8be19a8728a18c110d6cf034b8423dda8b482111d79238ed64a74fedf249e09971019abcd59e65292dbc39401225c0bb0e7e440c4ecb4fc66c411f901b89f4355b6812a9b73d60853e1137e5a6ee129edfe5baccb220b2c785b3c72dea82f519bafa5f0e4c7693a73f6f695a6fb90c06e402224ae49fc7db3429d11249b96512176077c13354d062b2c0c664dbdc1f94aba12a24164457d5bcfa8f20397e1c28dbbe513fdaefe120c72bd2372ad1c980cfc11ff563f978623e667b5566123a797f11da84b68be224debc803af5478f775e7e9b62e98b8bb49153b81a7e63ca9bf2cb25240b28572f6cd1e10ee4583ea27fa706befdc642ad1572ca4aed960864af0575e420846727a43735d5ca62890f04086dc6ba97462a576dbdb785190f9d88885224a6bce083b86200ba89b4ab09ed56ad704431f426db07c699407f21b37f368f1051ad5df9baf1a81cec86190a3edcb4ca56d14758afa1108787b8d5a82a07ff6084b5c75ed1e8ac55c4a05e57a5623cc03a8ae48a5412c42aaab3419ad52bfc3b63c70b269658e406688860b6142977b15006e1158a55b42d22164310a70594fe5a4ad96d05623d88fd7c3c6da4b68e187eb7140517ea7a1009879bb19ad89ca5c832d8d6bbc72a65d208b9644c43cbd831617e40531ad2ec9ee0c6f180b639e1a118da3660042a13c623238067351e6930b985b4e28fe079fc733585004b41e043247e6bd4d771eaca10ce1fe80d65cb1bea34929dbdb07d2bbfb0ddea07bc36ca5b8ec4bcd52f61e3db46b85bedb3886afc01bf4861bb90366eb791922b05b18818c0c03a0cc058cceffcf988a99bffc2447a0c083903d81df1beec061de0af6a166dbe492196d94c9a9ffc405f9370b0ee8836aff535cc88cc481ce7ffdf85f3352872f7d80b730c55958b8b74c6af64a190facaa6eaef15ab0067e88ee93e98681f04f4612b4c77386cc470401f0c26dd60484713bca868eb810595fb4e1e8231310daa63c5169354a4d4db379fe97cb223c2f7201b174ca1831a56930bb4fb447844cc624b61e5fba20bf20b33638ebc330ce8695a5a83b68ce2ebf5c7c58a39f432e50d7927b5d011215a7dbf6b18e829ab20449321ec4b7c93a6803ef9d123961c49b851d4d9fb0b6058d282861c11cb7c53b445730c337e88264602646d378b2d70109644a50f83691c52c9fb0576ed7909a8e6a38fa779498aa68c935e3e46de5502082f530eef4dc7c36b3c0108931044c66108258dd7100d81a84dce277cc094fb903ce958e61ce29e1e09266efec7996916089bab11f1b309c5c9ca19b023746fa24f1e175dde44c4f26095056146ac5b9e726a02901df9029904ed2389847ba35841b4dd4b26a0cf426fa7e744d2a216e00b0df33b14036f7607d1e4ebe4318a37073789678e0411389ab456dd2a9c59db67ff2577b9bf81e37cc271849282c2c312708d24671d13141d750463819a9ef790c0d46d08f8ec1538887097474d853a520e3c288a1aee59c57aa6be2d8c57bac3f12a6d7aff4ecafa0615f0d94b67e5546e50ff20a95cbb5bdfb5617c1d76f6b08ff2330aaa8a9a1a12405daa33c86746708ee13037a31f4a4c04f3d6932c9c3c59ecabbf27f4a95e79be5b5315fd5b0035d59d31cb586afe9dece37423e49525534825e9a1f76b47b146d0520d6bf9ac1449a873ce3f1045033db42a1c48aa08d50ca11ccc704e3428a87218d99c1c336d9ca6f7ceb116b3772e15046b55af6bcdc98890ed206b65c4b942ff4681abe51c50388aeedf813b71dda22810212b6ec4f74a69953d4cfa3ed6b4d2c29516a6b021f97d6d83ce4443d873b906bb2a111ac4ff081007472667bb320a82a747480b94590be65977eee48ed057cd9e40ac80327d463cbad96934512bb712cefe3b77c6c628fff5b1adbe142153e68a01c9971efc7efffb107e4a74ab170786f80343c9e07f0d9fc44fca14b4f9e1d5942257b330f71633f8e5433f499cf6857de26e1803c200a49020d2d2d924f9573f6a2becf589ad91b923d02428fb0ec3249615bcd6c7722e74b9d321db4b59459c5493a224d4204b56adbe67ad3b2e99085822fe593069d2fb347b88a9f4a261e60e8003c90e4a60bf1beefa3342bc271d282b6033a1306bd09400bbd328e52454acc4b141d386c3640af0fb123bff1c877aacb6ba4e4aa17d334fd304dbf7a19770f849ff6b3e9c32743ad6793a7602b9693fc49ece3239898faedb586f16cd27a9df2d74b9a300fab5e77aebf6a805c533bb5b4285c2a99ba679590d1d1a47a4d2d961cfe1f968aac2d0528a3e250dfee1fc7023eca403f12f0eea735a8583c2dcfb0198f0e4f694d341bd9db5183fce3548d9b4395572dea86a5ba00e2fe952d195061e8525eb1a6dc6cb2a3e011d86021fdea3acb25e83210418b1947371be19911dd36bc40217f90cf930cb7c6c239eecdc18eea346ef339bfd0042b8a912bdff6b47d1128853eee94cc8138c9be836e08f61a92ae6276acd8b5e3b25159b41c141be93b0be9cdf473c3087cb05622b00257048a7162da81017770cadcd602b721c6cce43e612936cb55404e7c5ee0d76672af95f87145a665cc9fc1a87d1dcce091131acb8fdc219c7398c8cab522966d131020403d742d723363a7c8d5a13274f08d883f1948c7f40626bc5d84335629a6ec3b144838d5cfae508f0bdd879e1334b1bcef5abf34e7249e95f9d1faf700f1d5a692c3a3b6a961043659f0ed32b48bd4d9970c36e897f4a11e6e8a2516bb6ae6c3d9075963cb072d5b8286a288a488dc523038db71e7d389892934503ff36ed7db63e6b9c6faaed2432f8ddf45cf603fcd37e96d938a49ab514e0387c2556f0ac98ba7f4a7f37ed673d2448057cc9b01aa155e2ec12bf51a336343debbaadb8da12d03148cd655c6b3874f4d1e176fe4dc0c08b452ecee6ad76724dde686e86033df23d1e60d036bbc46f61baf4a6531bd13caa63aecfe072242e757b8a2e775eae02c28ec08c39cab26047ce6987151f9ccfe1937c768db014c72b29c76bf92129830e0710b75e7bdfa6ff5686146a30605ff1648ae289bdefe8adbd74ccb997145b98e34deb89ad96145320432c91f8179eb1e7a3be5932fafacdcabe27ae19a426d059bbdad2e9c64dd0bd0b859c26b39980fc6a4ff7d9e936596549d133d3e46b7f0c2dba7c97846a16a5d4b0e46a30945cf71188887fce6230c0ee97c196161a2131143cff7f14728c8fdbaf39da8d61f22c8782c16293230e64add56bd3ab8fde1c3582fe8516349a6d4d007c439f3995091115ee5709736987cebbd05aa814cdaf435296cb6d71df50ad00e1f05b266f98469490f67dd87191a52d4d06ec7f902f8afe9114c397ffba6e898fe5fec63bd84d5d1ecf3d6c0a9292dd3942675ac34222cf0c0d11520f9fd3c0437f5c18ddcf974fec24ac69467433b085deafee72eaafc4aafad0d23aec146b43d0cc0ccf9efd95b63c87bbe2194735a1531d032007aca58747b441271016f514bf1efdc702a373cc59585a3dd3cae057a8d7f681810316a85f27b2019c8efd9c4212855d24b0e9cc3215a90dc11850485aded902685785798648f2a765945dd1906f0919e0579743f10a63554b04bbffe9786e6d6965d0d55c0165ad7fa3bcc3cda7bde2b759cb1a7db13d4d623919fe77b15082f8c8cfc8fb450d1197999e7b6dfbf6147bd2ac0df081d2ae2ddda5d03969b99bed9ceedcef4f51e90669953d14acc8f651ba37c0dde3e4454b9e286fca2c5703aa1d0cb9950a1892be122d9708d518032d0e38e984c9cd3fa6fbc1065347794a49c7c5c1a9d3b595ea2f5a5d868efa550c9bc4e7150acb583f26b3051a7cfa210e341554e667f0460d4e5994b742c0c9fda329bba2ae9f30fec34a2d7155bc298a7293f903677bdecb0e5787f0d34f6b045533e2560341fe32aed9e0f1349dceae120d53c2bbc5b6c1a522c3f66daca3328fb0534b2af07aa49bc249378ec9a078b12965c12209f9eb97111972467326a859b94a932ef48668d25085180241db8c0c40c06f53aad8e87e3969366230cb8da954821e453018dd6d62a5ea95ede89723211ffc06c5c7700636ba7f2f67d0745d69c172fa5aec587c380f921bd18a02b6622a61642b1e4a3c3a3e6e2aaf695686137ba9fb19474fbbaac35edb33f8b88e9eb0455c08597cbb13bce7cc7872d7493d04782e437c5f2bffe5c2f22c40011c278d8ca9f2d1ec2b44989892f08a99487d64bfb8739f3d9d3a8901f689d3f58d366f7f2e873379a35c57d9669f94f8d052b7e1c29d037a8b99f01952b58c2c9bdb794a084cbca5218739cf47ba22645418d1bc45833dd0110d7eef2514c28168dc6adacb6de27eab93369f6d2a904c6f8c8f17058d5e3e243cec629cd7c3df56bfdbd85ad9d7a2f653204eb3367afe5cb0d48bd0f4228f9c13068516a75f0568da3c69f5568d271aff9e4a7e871cf023241782f7ba279d17c53e1eef40c77512212847f7aefe6fe649c615d70a740e825988e376491abc0e1ac7a3a133fc28fae8f4b7ce5d54616d191e46d164ea487fbf8b76ca813b4194375860ef2cbc8768874fec9567ef643eaf14f8584ab4ae3e6260c2cfcbf5d6fc8d1e8392cd38ac590272efd05b89ddcea40a807d2ce8e7be0dfb1288a8ad1e9c716623790c5c431405c572f6804159e8033b06027bafea9f2274bbddfb70edcb4de168fa77390a583025de968f9bef565bc812657e4ee6eff006d8ee3d28e16a2462e98a3749c38fa64e5b5498045d944404cde79b8a7e07ea7b274a3666a121880991989a6a9526949934adc672765f872133f88298757e2c61b81edfba80b99a81fd48d7eceb2ffb3ebcf3d78541cc1cc04ce993815e3de77569b100e546369a632af76c1f77776a954b19886729e651ecfc5ec3eac6e2e38dc453534db8d2042cfe2f45039438d232c0f6075c1aa43a8432ed1789d0f723033a4bb80faea563e3eeca0d57ad1c222e1fc6db529a3641a181a650a5d399855fe33c4434a77f0431bba9816301d0e61bbf1eaf0fc32b514ae5759ba2fba95ba2639ec6a34616b5de33932f41e14e588e2a5a1dccc1ad769682868217da5559ae4bc9e45a7ac3c51482b28f0d197ae55e7a91f364d278880fd8a47c89e075d7cba78228e8be9f0c6a3c39a9d8c1e9b2e967b4f24194e765b86c6b5af83b5f2df05e0835ee5af3c412c1ace4dac7a09b6b56d671c96c95e046c7b291ebf1d34f2849035db082a896f401c53b04e2632c911d68447151e0e293e8f2ceac737065c2cbae950a78b0bd81921f6962f25d2f168386eb1f0f531b5570020770411e46c162beacb210cab259264a4a4db401b80f6f4d121d2698fe2f4d2898278dbce9633cd88ca796175e8257e2179e49b28a72c61ec34841bacee42649a0c599b995942bb7a9966447f7eac18cd654d2d7e9173572e830c665ede48140c183422aacd3eae029d48db24fea1de196424ce10c296bd8fa100028e5addbc50b947ce3478b7556b475bc967f47cc92da7cd126806327341e0c90b0907dae42e5c382bef4ac64a4f1fdf693703dcda2a9b7e163fb0d99647bd9813f6d9da40e3fe1a442caf86f86b1294357b426cbf79414aa33c911d45cb176db9ba1f33f0346ea122d7caa3c146af2d354840f95a1450c92198dc4af6135c18ea3a5a65cd78bd241270f4474393b368d652dc4a5b5e886f2a8954455056514c8111f24c7a0738ed58355bf7c1f3b11529625dae61e0f179419d8df147651e2fd077b77e106a9e2679f84504bc1069905bd48147d6029f23c899c39899e03628d35242b0b9873bef03eb027bbacb4a251394164b112fc174334284311ed6e328a36840ee20f0d1cd5766df50d047d264a502110bc0150eeb6611d2e8c6211671f860d3312d3f3bae9c351abd6b0e16d30e659ffb9044996e36d57925a141728f81be2b127a426b70e840e0632eb6dd7810669b1c9e808d93d99ab80fbe622a2a1297b00b5260b038ede9b67a5ad9cbd949a902bd8282ecc07ea774d295b41f9bd16027884e1495d42a42bc950bd5a71d729a61dd68b66a8cfaf43447cc2d624e94032000791319532e6441be32094f96fbb508bd84e6cfaafaec75a807ea8a267f35100ce3b0869913f0f2762e6c67e33afa6f852f4920dfdc9b09971cab84dd86fb6e2dcb3dbabd0336215c34921606b6d59ffacc891bea85ee0a17105692608df6476ebc094c9ce7253c3d47e4713143e78417282939d3787865d1945e919208d0ca8afbd549b3d5f789ac4bdbf5339b3efb0f5089dd2fef66ff61a19bfb6ce37858851e3603e43b19c38acaae335ed9683986e54eb13aec58507ee1abdd102c8d2def6e367a4ff3bba9ea86535559e1160fbdc424662a16f3bddf280755b4a650567491f56a568638c8959ea5805f6f7c6bd9fe5b26014410922c4410f4bd5d5cca1e5eeb11a3053f075633834523c31dea0c77d0c601c7f93796e022eee11294fe57a1a1376b51ab60f21a2079b600edeac4587d9a71e1bbf91eb186f270dd610297498140c0a83c343f9c69709cc902ea029b8a713875fb8137091d8b6417113d9d880d62d20f530a2fdc14d15a4b587369915e68518cb0fa5f429800304f8f4510550c4d8569104bfecce6de406e1a306183ee37ec4c832cf6ff971524ecba040675a62a1b89fa3e7a771614399bf55623a183e4699dd4102d0c6bfb3cce993ed36a38bca1a5593480301e4540372ce00ec04cf5b490b369412e6e3f291742d426028c9a046b7cedc8e004b0c24b6209791fadf28c342b82ac8a33f2cce06b3f9da4b6a363472d578f715f318ef0810368db5aaf0006a018d0978a8973faad51991afa8beaa3155302c43567ece46f17ec6dea81d331f81fb3d829b881cc3a87e665d784c67f1edbbc77c841303470fd0b97ff74331efcc3f30b9bb5be69ddb3632507eab11a4230044000f16ef0d08ea1e74ee9b84ff7f4d06af415af7599ab0abd24224a6e3460c422a9c535bc1f006f01924473ef4a8b4ba0ae25c4e0916c70822b6a9102a051b7ad08ecc350167c972ef4cffc384f09a38e6300d939e3240a58d362a0fe4682c0a636c736f41338e841bd6d59ca8cb9419643c71818006dac681c951bc0d6b53a88806889f781586c0f28a3916a941fe05479263012107f2f0ecaa82996992b4c178b7558719a1ae0e450381b91039dffe0a99918ab8953c867605200ea57bf6acc6791add0ba6934c74c97aa780d3c668b0d6e0206a00a33153f78f74ced6cec1f849e4614e49373004c8fcbbb5636a341fa5fd402f8671f68c1b80dcdc7449089ee2ce0f30a03f2d1e1ba311d08b9fae5d0481a790c3a271d6e8d246ef40877e1741a8a93bbe61ab5bc2de13593928a6b8e1dcef79f123c09b89db3e545636103c744ec3b83a3285495ea8e7ee902ae8bcc1fd1f45b385bc002d60daec4fa1fc02a1bd40dbd8763b666b7bd37843c260fa050e5ceec0ff635f58e82b891ac1f1268e25569db65faa9b7490ffbc8d87610ae816a6f6a6923e1f4aff005720efc9859b4cd38178dadcacb415b763001bb121e980f98a3e292501d6f2a685a917c502dd2657ef35796c818acd4423876858a7f3608701c4f2ccc4054149089d18178e56d6f34fbfe60f0b9fc16d8d722125ca793a948fe49c9833abfcce543b208f7d5bc92de9ed79cc4fda7818673ad8de745de254218ad29522d8afab028d58d3751317e730ac347200f7183a7ce4ba644351258c0ea5e5ae5f83046d9bc03e889936f7e4e18a0417ac38fc10fe3f30982280c01cb388e983b5ffc6299e522d5cd028232c5b77a0565282f15e0d025207b16aac86d582305552dcdaa85796fb98df00830cca0d210b3ea8b5ef2d0872c603cfcb52e01ce203bade446e286a12d98c15361055f2f0c3d80a73e19d8eb6dc1b6c43bc4e192f5510331413d7d8c17409ffc463036c07edc6267517c9977b4570cb58a1e1661015b1ac5ef03f9536624bc0971f8016471c971148f5c2911bb1a35f4f2b5be67a51038b03f98759b0866ecc3195f5addd3f5df52dfd68a3e0b4ccb64b4feb6856d654f208e7f1418c600c52d260c81497f7ca6c0b443e173e42713f9f9a598faef96a8ce9d4be563585b063d2e18358b57f7639c2da9180a460f519f2fc1f60cde247850a9002592799c21f1b0f5fe1a6b0b95bbbcc24fe2de670e16b01fa7482e7161821ef2071958295828a5fff10e421dd592a8d7b11e876d27366dd93b4bc5638cd7632bdd5165140a7b7e2defc437d69ca22fd37dd2caefc58dee8c5d81852984e2a26aecda700c2f5cc873ba8731f549e2a15d58f73b41d266f248f0ce5925c9a6e4fc731061f3d141bc4986b332177c57287120c456ed10d161d045cf8850dd131a61f2adf449bc5f3c825b6989f04e54927fd65b1be2990722928d941639bfa0c57c8408dfb82ebf4156026a9320083c0db9a44949dba0394228369969aceaf7a33f0a8145be4dcb422b72961ec32a68967b9d394738ed10f633979410e39402008b5174b0a7012aca0ce11eedeb34ccb5a88fe7e5db6d88b5bb54a22120b902c7631acc155bbafea04ddabcf3e5a6d9f45928dc8fed7c945d5c1ecda125414df4f7c0f9a5bff66ba0872745b1e1f3ad3675acf27093157ad3dad911f3d2b1035b7d5d8d97a9d209d3de83e1eee89928588f638c54b99fea20d70773b7f6e89dc3c44b6b5f15ad250eb8f5cbe3fba3b2237b8100e3f5d64f043309f9f2a3adb9864b8981e22bbface5e316f711814dcfcd0e3f2aad4d52771910a355cfe3948a443a9194c0ce0b621d2216c65891cdde63a6d9949ea1d4b6ea2f1606cf15672f271533cf16bc38a36a632b07a3b3720367523a48a09f49e5534aa856e1f790958704303ff60b922a3327d395d42d33eeb0c8a66d090f0a884485b578c53cfc6aba4462cc9240bedb2c161a691726dad9650e41ad07f88f3d413efe5df24a378468f1f54224675fa3e70d56dc590a8f691dc9d83f8f9037a7c79d27415433103f72b8a7f17fc3eb2478613b2c2fdb84c1bbaa558d14e77297aeb8c17a369b027de11482f0d381fc116cc1a86804abe000007367643878bc42dc29f1006ce4e5870eb0454dc7168456547229c0d491254b9a9d983612b54a2edc5f94f082b4781cd4e65d033f57495e21836da2b27370bd7713ebbc8e3bc4e66ee24af105af794319564021c82b80b4fd5b6157ca472a0b1e6afa314a87272f5e10a8355ad681a6b2ce5b687e2d9075514fc58842f52d1b37e2cd0331244256c1ee65be16e5f0bc9a3a483a4d836c1ed3b536c2fde41c50c4c6026e80a1e62444970d1bdcae8a0a82b9cd148f10c1e0aac0d16b633c5779485e5a63f2af49973f8d1f0dabdfedb368668d6cdf0c33f3b3c9725c18461d7aab85c4d9bcc28fe30d8e23778fb95dd8ace24dfaf6b24054c7f30e06938766d7e0452346e21a271c33a29491ada422f13f795f655cef706008d168b4a7629048195a930181099d2d79729d8cee36644ba8935b8d6c8a4de37f204032717541bc8fab2596fddb8c0568642771baee37e82e1e0e9f8433625d4632464a9055c2efe3610fff381dc014d935cd72003b3f4cf7832eb454c30af02b1d6edde3ac133fd82b6ce3e09a3f748a01da420e412606e77d7346679fce9c246b50623d231a458800b43ed9ca71c954e38c66cd8e53412e082bd2ea410f21f8ff64cbc18a7a20db60f0c902568a6703e5f76cffd323aefa03cf62266f41c6a2d0b01490e423172f3b62004480381fcbb63b0c7a470979c1b1e7e839a7d43ef6877a58014ee6d81230c8d1066ddb2fa19a3b6352af2e581f2428b176c7b87fe98bf4a16a7187adba38a1ba5ef4cc45d833fd64aaa6757a945d26894b8b8071023bd8071a28bcadb9b83909af6d09236b4180a6af181d5ecbf12e27b7708931091bf6f6248e2e5d2e60405ed85b1a9bc150ca2ee734f754db4aceec767655e9a09e83695f8080ca56833bc3a071128ed06f99d2ce4919ad745b1863fd26598626457b6c6109556ce1e4889c821390167575ab78917e2da206a94fb6fbf6d31b79e8bd95dd911e73a09aa9e19782c4dc46ef2dd4cddb912ebc38f10c1ad9fa4932e8af28e673d547a0b60b269cf13e98921905909f714d1f53adddc375869037a123e7111afbdbbc5d73cdc2255cdd1af507746494375fe444ead8e46a1a59a359728bb0f6fd637b0cf80f609610b3a07ff414c31c57847a071ef11e244427bbfc1b86efdcc12bcb591abd03bb029ec1d81abc7d55f6f515b8469744070f193970b61097caf01ec2c27639bb68a83e7108b47c0e14c5ea05fb90cf8c7984169566a4bb2d7b187f8bc15e23c2b06eed73c8c8512fc5e9f2b341899ca7842a430d0150979acdbf6277c84b94c4476b9e592571b8c3c37193279a2dd67fe01c21d32cb910f27c6206005d2251ccff108072eb8f1a50a97227f61b685a15c9e9d4c2aa14a1e4629ef40d6965f01d5b249c371d01986eff241f342abb29b97841bd096a6e62867b2992126153f925bf6812ae148ba5884d6f64f1526f919a844cb613a85587cc3166b68723593984d3dd91adbf2e089a83ff5c8839e2f2ff2199b492ec62658e1767f99a88571a8b55573f313a34a1b31eb94e328e2ac5f2b3e4c920f1f0bf6e92d63a8f1474f13e9cd9cf5d2f772a4e16778e3e29a00fe0427098edf9b80f43edef3d64c35cc8279fb35358d7349161d06894f0cecf976148a91c320f00b0d32fd0a27442d60f829977c885f187c2cc6f9422ea6bc6c233573f073af3b2951de2c10661050b3d2832ba4f2d2efde096886ce17928e3c900e5a59c3325d679173b3a3930f5414bcf7871ce345b6c7211ef463262ed3146c42536a5612add55a02320aeb6c405048bc23f5cc7246f4f3b038fceb245129b796f216c9bf58dfe5827ac10b4e5cee49719d28e317e7a0a55e08f70478fe5643c95e01101158460f2b3b7750698b246130e11459232d90a2e0cf182c27830c8e7e30d81b304000b74c80a10f1dcbec402952fe88e3c980701ef15d536b68fe75e63d5bf14b97c0dc342a6ddfd32139a45469c23d57d80a027ebc25601c49a77b3fe95c5b6c80af62630fa90047d74b314235c6ae0a08c02b78f0565de9f195f72304ecc4a3780adc0fe8a945855cf2c0deefc2dcc0b8a050c0aec6b5b5c869ff2848a0bbd48bf2e06b0649772f3c037ae7da0541c5b837134a6446f51ca1f962088832656a02a48e2a04d4c0292042d2b0ba3e3db1180b8cdd9b45ca8a571703e720f188e1eaf08f31f4bcfd810d4df609941bbe18cfc6c353b31704802119cd2b24d697e5f59e863244419fc06d7ea0cd20ac0c5fa78882c4097d1b318a8fad2335345fa9b4dbdb881f839e4a56ef8b64dcd298a590d2e562667b65f63d0615e5b419cc36f84c5823ce134ebbc5d353d493b27c5c5d2fa37529f929dfb5297b8948776586c100529d499e07edff1573525f7264e84fa3b9d7ba4a0cc5f91158e7b056515622218cc396a96e44f92d6c2d394cc14ceef4b6483c0961a9b18a1e5b968078c9f70219c35a15f4eb61845e8a242937fcb92404ba62d62102ba014b026f53911f5764d58bee2a221000e2f00a70db31414db63d2152a5cc710616327315a16e86963b9d94825562386463430ac91cc58243a5e632bf60e0464cc5ec8c0aaa58874affd03963c4e24f8bb5b260fcee848376f26e57c09f5b95700469e24ba9d5b1063f2c694a3a071f9e8e308d09212dd170588188502d0eddf71b334c27da53042669e9bb5198038172e106dcc1e5cb46dea31c1a20e7d2299e31a1706ee24c986efb7c424cca2b2ca19fcd7049cc41cdacec138835c0a56cd520f9542a07598a2c0c350963c023118e5934e6511ba0fb654fc441a3c8481cf4becdc215659fc1f89ddfc663f2338d25cd1332c301350388a0fb758533bb85a39ea460292b07550eb28a9c23c9707cf6268ce80ba2efe4304139c280fd381bcc4881c06c041465d239bc223e64792b5bd89b745125e08b25623e858a90697a52bec680485be43a87f3d0422d412298e45e94d5b42c9934c68b2d2e792930c597e30192426865e4261dc4a05d062e124627fe50f8aea47397c2643710ae699215708574a6985d1d1471750e662a90b0800970922b05e50f94e68a756199eead0039618a697c351bc40b03a38d5e7b75d5fd4cd55cf1d46e639847ac851b91f5271b73e8dd010f1908df30dac6ceeb32def354d70546b46db40e4f721fcd07da17d8cf3c353dab9773763be2d85b9677d0e42aed744ceab756ff70c6aabab5da04cb852657198dff1dab1a4efd4580d207cf5074441bcfa25b630e4abd484d48d64811f560b232cabe37634a109679f72ed9d913e5953ca0cb98ad5283f317899ee1d7703dde0a7db879264b7ad94e86068ede0458d58868303c13a4d710ae198c8e51aa31bdcca0389531126e75c6f57fd81c18e7c46f0cddae9e3c38c8f93cce66e2ed8bf620713ec2ab7da758625b338995898a9d008bbaba7f2f2bc0a3c5c289b561c1e830cf033e87ef63919b6c401d12e6871845881369a8852db802e531631aa4b24c8ff28f3290e0e37af8bfcbe2793d91fdbbe69fa3f915fcce7745c50f7817a5e6da0c878c430d75b449bfda414dfb1518c378ca8ef8c7ac944d0598bec8f66473f564ee282d83d50a9a6e43e64f413dd7d34b5499e551ea6f71053c6e7dc4b4044bb2ed0841ece9af2bb5cfbab3286593d58b6bc96f9daead2541a37478f13d1616abbc689e4cd657ba09bfd44eb65b1319848fdcaa178ab146520e09654f953f8a5024ca6149f2bd7962560a0122174c20b46fee36bf3410eda3dd2b9f8e5ccfebe28225d83d75cb152cc646f22388fe1a3fde5dadf277b38d3a8d9fdfbceea8662dd6a98f6b4e47242973f1b630e9b6d55e8e1a5450d2171fe4f75aa96cd51f5477314df4d52d42bc86bd2c2cb16ed49f6d107a0d7a561e9c2d47dc20917b22dbb4f6f4566428e110203023aa42534047a5df87069c236c55e83176ae3ebeab613c14c6256484bbc16c7908d871b83afd179e528df6fde779d5162c69ea4327a6e536a1fbc6a0cd325d82cc4b7880597843bb10950e0b3b9455905738d13729c62a35e1b27e6cd62646e063e502d90a1b9923af9f688a048e062c8cbacd6b1a1ca1e2cb3a5788de7e9ca4ca4d68f7e69747a3bae59af765220ea2948fd8b87f0c21fcf3acc7a2c9de6c4408281f3804975f7f68d185e980097f4c0d3f199a4e0c64f10b437e33f00ee0a08e8e53d67800e8f6fd28b0fd056931bb9e7c371cf59558bf0696565d2048fea63c657830560b7d2e757aee70eebeb466537ecd8043e6d09b8e29b2ed42430eb3aea81a4decfbc14f4c691436fc91d4fd027a5bc0ffd448fc0ee68c43ee029465d4b060b2ea9044b9dd2bbfc11a232a655a5ef3978cadc96297d0f0561620c17aafd6588a8c43c5cf422bfc5c8d8c3b8c2d2eb34e54379a36b262d1922c10f366a595e36529493145f0f6c6c972c6a4cab77c1296a7c3d145cb3b40b2279cf24bca19e66ea30d3b48614672662583357a58133ec8835e74a9c0e8766446310eb32e59f87a07812ea422635cd54c022385e92d4b89fa29259545d612ce51e11cb053f31a9c51a44243c4d88e0017192ab20202c388b208acbb1d8e33d91ad8948f7ae7a2ba8d91d86c55de637b43ab6120a25faddeb834277c1c21e0081787625fc86586e700d93869d461c5d0931614ef610b1180f4ece5c6523fbb052428be087ef0849a6c24ad0013192152c0ea244c851482f9f06c27cf09a81f44dda0f7da6e9c6f3056c47d2feba78e4e72909fc2fa1f1fc8400ddcbadff3a99103a9200cbcb72119cec35bca33bdc1ed93a36750b20fdaa5ae3cab7d989dec9e0ae3af43d1393f654f035fd0939fef6bbc009d07d208fcba62a6d77094c898dbb386d544616b23645aafbf9e079d9965715d630b7c7266220b641d41c3c2b1310d40e28ef9b208ab4df81f8741be2c998ec2ba16cd090df1bdc5ffd6248edd8014df4006388595f2199d9133b23d3b5ce76ee05ae24ab207044d342d0fc4392a1c79b183b1cead382d9f4f7bd57ded14ecd338d1a2fc1debed20c6b345a20055af6f9846b9f1ed0d692154b7813c4a4cbe42c995ea56bd2b9b863a2f15d20f9a5841c5c812ff738b5816ae380535bba3f7bd1cf245335fd2f27967bffdcbddfdc1c0be7075e05ab5b84082ed78b79675c8b1cf614f408eeaa6116c2979dfba42dde57ee95eee8d005778531819257d898a23abac94d255c48669425a3896d26886ea9b9b73aebf35de1cb1e9d192ba6b23631310e4003f9699b4e99996912b220774f3e1476837a5a32ae82251bba18ef085f42e84249d42c7b54d9000ee848564965bc80cdea1301f78da660e4475986bf22e581135c9206a814651e1ee327223037d1a536ff44b5339b4171ee2489cffcbc06df22c594d780ed6025b91706b650d8843481d3305c5135a659c8aeca6ad047676bb9118f8c53beaf4c3242a4cad64c6014799570a110cc84bf9fbf6b05c30f5ad25febd3df1e7e62adaa9bc9192850d71dbdbb7b810639a47abd7de85fdaafd28668d42608dd8a2e64325566d585a80c1e1ed9b49deae6c83d27e9b0d0133ba7319fb9e8795f0c57db966ccfa1e704d68c00e5b740b133c6e22431ea98a033375dad8e37cb66a9bdc7a3487c47cb7f98b1738f473ef19533711ddf1ea7e5793fc24c4502836599825031d7d2b9da9f934450fce907f081db38f9c3f98a159655bedccf9de829012036e7c992296c14e9de92052f6e28a38c12ca826074c5a3dd7a6a073373902f47f4085b47f019d3e63916374496c09a9d9108376c2a18d87fd88f182b7663f94b7d4ac8a2434cb711012b5c0a5e07b3cfe85e768b08641dd878554fffc43ba0f6f5593c997c9c659732fa00830abb609356872330f84df12fc38e2d4e4ff8482474f9e3013305006419e8916e0163f1e43d30e39a12b6bac0f1cb48d57904a0f898b341eba30fa55d68bf084836e78aff38867f73278a0c0fbabff92154da48fbfbd035460b5cbb4cc7e3da2e93a1a229e92daab1cb18830237016bfb7f7b4f2c1d415af4852da2efc3d8e7adbfc7a3aeffff24a3c144df52fcf9d30e021bb9e8af5cdbfe23835beef09d20db03a909444237d24b130dbba49e1114ceead5ed82a8e8f60961adc40d62e4764563f3db4b18c44face71bae1fb5f5660e7f4f2dd338d9c7118f07ab7d906240eaf2443c00dd411af52629dd056cb5286050b398742942b0e2cdff62ff4415130803e638186239ce22e95a5eb3c604a5b46bdc0789b11dda1c96100565fdfb7d43b5e1e98bc0c19ec7e530169f709b9b4b0af39058a051787c4278ee61004289c1205f8460b2feecfe036419dbcef8ca1b1222b5943c2a76b569cdacfb222941526572ba7733da4fa36c66ab50e2c65878e3733e049185e7e081ff00f9e495d6d3a84b25e8fd426b0282cd832b0f44d6a3a3a3e1795e461221f224991b772366f9b37e6cd1e7100b3da5439fa90cf4e16677e6ab1f9455ccf0dff323e8de6cbdb6c128fee349a6e546c17b03b766bcf217ab1ff84ad3ff08aa6d552f78ef4c9ff0bfa17cb0c6c6fb72b5fd524f707f96c9f1956a05476849d7aad71acb392134941c43f49c2d9154d058aae7b30de3f08f462edc3fe885946c1b885fc0a7b7968fc5e77dd240666027359400d4d148732a325b667cd2c387155bf89bcb369721d3d193df7c8942161966f8c0bc48cefb6db88f19c6df4040f11bbe8c46658a9d09cf88eb4764969984be2880a9fd52ac54b98c855176dba69275f2a9dbf8a1d184f7b9341a31ad8e27f23329af6f32803c300c3a11e1b7c78fcaedfaeb82208a65793c5f78f0ddf743d5555dea428af00e8777efeffe397facbc9ee1f7f0f428c3243f9cd27fea1265fa02ec8f146322b92b993f37a45de4a819a94fc3b639ee1f1846813532ad17af805d1be12cf485e8eb92c0ab68ad55e6ea84e836092946b41134dae9b57468e1fbe53ab00bf553993da04ac4f2810abddcf72eece18b0c61fcbeb68187d7ceafb738e1ae378b9dce96656c52426cbeb63f7e74f486a7c6b2f7fb1685ad327da38d8aa54a8ab0eccdc17d2bb68f95344bf032765fd51d44f4ab24b2dc75e6538a917ad3a7b1ee55828f73e4c3426f6ccbb499c2a79612e2582fa8b5abc8020a67f7531b2582d62bfd0d0032c53a2b282c7e768d64ad7578d61bb65f1eae9b721e13a74c8751c6f4564dc7c13ad833255c182f0f29f11eed0092d1a355d255130762d61ca254689a8a07d032f73c78ca3ca92029623d8f0ba52ba6af3bb4c5507b4a6001b22534e448b89e6c870a547d5b1422663d99c8fa800c62b52ef1f283b3594f7f198fae27db8e3244642527cbfcabdb169450b30048df786e8511362e63d332b3236c4c7011ac5b9de6ce4fca7ec3394e22a949b09d8ac1cc7e3a9ef7870bffe9ea5b1da5efab9e692ed32fc683c2b8f90eb59632131d2b105d43293dad57552566666861e98d338cf401e179163f8d0bc28632da6039c584909358c7bab15221f219bd793ad497f796ac131d962587736a7b3dfe167d099b75931bc7f467c75fcd1211507c713ad77220f8fd9ee20b446b1a8be839a9e9f7412ed2b0a6be00cd6de9a88401f432ce9484166c582c5933ca32b8289dcca23d8233d76e073d9653b6801751ee504842f1274324ca3315d189446b2a897adbc0de9774c999f3498c971364906e239a2a0668b90448da4c50ca7ceab342ca3002a110cf1650c72fe4ee7ca30ffce20d953610477866f126954a5ea65d05055a05ede3ab9f0e22a42060ce4743d8d300856a0e195cc0ad34b64c677a8102ae6d1bd731f302e3bac30ae245f01e455d0fbc73ef85c2ce3d255fc43f3250c336abc26188d4966e4b462c0a501dd995be95e4dff46c2a8093551154a31fae13043aef9903a86654f24eac4a430e0bdd466d1d02a1d9afba96d3667456df4a57b2a7fa7630ffb508b41f2df4c800ba83b9fc1762b8dbd2177eaa74cbef91da38660943e50524bb25c268593720eb6b5fedbaf95a5d1431007809a13cdbf68da181d29427a064c9b80f1bbecf698bd78d7de95efe39f2a60fcbf945e816cdd9bbc558319487c73d4b0cd67bceb0a3beaad656d3df19792b845c66af3196b53aa03c543701de37e3bf50d1498c623ea12315d7639c15a6ee0c3859fe9dba97b64d5b0370365533e48650a3964b9823622df96eff31a37217146a0f17fdc2d35f16f48f36d7753d9519a3bf6363e15e952e101c01169284887c7aed7eaa571f8fe6264bad97b2c2c3db56e57cffe5c24f4010cd92a1ababf58427cb595bd9c393130efdcbc8369e59f040f9c079b987394497d426117c7ec3a04fe9ef8f5337a8d548da74e14700007d12fd817fdfd2dfdf9b7074b853c58335199d8a3e56b654792bda0ca96ae7d47d4286bae4322150e793e336191660c175d2275e01b04b2c1d1197ca08be3ec3f8bd79d699384d34c37f71483f5f06548f830ccc562a109ef843f5a19f5cffe01a525450d67fdfec055ce466989c9daf0318b3365443ea63880d40c25c130daabe897d77ece278665bd8c8d28c7d7f4ac39f387f645693807cb1b8eefc0d4ee0f5ae92bc93c872d3121009b5d145d452a4077824d70e883a9fcefc778bedd1b46e14e2f90666a3ef920cbbfd70f263b887b4aad22a272a66ecfcfccfba0265774d22c80600613e4511ad74d61d52b530d6532450e5f36007cadc78a4ffacd39fe7a782bf7f93c69a8e2171b0c58bca4fd223b771e882b8149010b2deee3e86c2a601dcb55d5228fc21aaa6571486b8a554363e17cc8a39c28d39a785f58e0d8e51705d10e31eb74480cbe1e53603e3d4bff5133d0f8e735bdc7efea3b10b8f0f270e783f1c2c443418bdced5a5769c1fe0841ddd7d6b38e3ae01e05d3cc6829ebe8c685d81ea715b445bc4c4e8800cd06485760b4d8ba61af536bc92b40b6b25e19c164303bd6346a2336bdc6da67dac9ea38eb8febc5f166a60f2774f37d8ce47c581be6e8d58c48759a27db3f69f2b071a442f32dfd5d2fd96a6ea035a9f99f39db277bdafe73d377e4448df04a40297916f7a75fe63c334474dfd0459903eaa0af15e7803d8d1d211a656e0a6226b5aefe6e6bece08819fad20b964b9542815d8225c972051c133292873747fa37627ff003899fbb57cd17073448cf48e3f8140ff0efe07b78cc68d23b6bc47a9ca7a64005098a305424bc55ae1fb7ea3de6ee5a10807128f4caee2c83bf3d406b926460bb79a0971cb8b03c18a6ff17c49d9587d92aa2c959e1b35d52dfaf209f8673ca709f9e58e587fb960120837028cf9cf6bcb31bb2bb2161ae99de5409481eca7fe2e6358ecd632614703cee8bdc09a471cd3ca233ac69a23c23bb738fd5f22a70efea5c1ba1aaf07a35f5b781b2ed5b757930fb2ac7786bf8ac909d391d53a8ab3b93cd7bc21d7aac0d03282be0f2b7b14c75828036ebc09cded0ee73bc7b9d790debbb29a32bf4f387634d602edd17fca75f07ce3b81959c13897042d4b5f133694d8321efa020b9801c45404fa41f39f5c509419a5398593fe6a372566fcc311dc0acec33d1d36afef6d32c2592e42e19e9345185fc33501bc79dea93721eed000b9d6001c1473ac0e457d4fd3cbf3e159d8600d01b4c1316fc0dd281d33da2364adb5ad9b08e926424d48c80ed1097e0a8e09d443785b4122b1dc62b9c562815e412c96153a686459a183582ccb0a8d2c1048c5c22a2ab734150b04b24020ebd441af16c8aab7a292d05f0e55550e4f4e1255c83ee482f74ac9945cdd6278abd94839fb48db7ec8a351663909f46e125e31ade02dcba08b40771d53faca0ade6806e1ada4b97e11e1fbcc7da4c9ab681b0d29877e43a1d02bbcd9871ed2b0771150130a1d7ba689dedae8206d2365d061f6537ff4ac475884419d4121ad7329f413e8fd10aee10b594907d0fb20dcd73352b6b8ca1c8c1f48a4c68e1ca16336208e20fb68ca5b8b0e80a9f3c9e76ca0a2dd01e3618c3ac85ec708a183b3012e368897af527b645e1ad1c64c81c86a10a929bfcd396794a4ac6b4fcbef6cc8f1233a28262094d43729a5d4e196646fb4e449d9a28b91b68f2c5250f65ec07818a53a62f6ba0a6f31c555472d55db48b9622168ecfb089d22466a88b8281c1407c54191c9f1801f361a20b2bb1b22ef0059b5e28d013cdcc8ee260bd7ab0d59b968a4b59822ab2cd259ae72457f9be6fb2191d622d3a6799266ea977916cdf54b7d1706c6a8bc2f630269399c0df399d68036cd631a8e36cd5f1a03da347f35529be6ad36d3a6f9aa1da04df32b1aed97e9608c8d7e99f3cfa2306474444d8561ce39e9dd3cdd6432de64f2d43697956f3bb273771da3f20c6f3f64df723c20bb8f56b403f48b4a486b3145565bda94cd796cfe9a6ffecedbbb8ec9b083338d695bb691ec23d18d045f614c451d75288aa25040390d48b739e71c941405e3050f323254c80eb2779eee1c8f810e4aa64d0ee64584919d111e18f04424b5a4838e981d761bfd362fff3095eb1fae4a3a3c5c932b5cd2a1babdc55bccf6151622661a995cbd26577ae9c59666b52aadbdb44e677b76234a3f29b6d126f7723c409ba43c2571c31c3b96a8ec606eec6aeea32c6023bba882ec9d4626bbcc3d9eea5ca8cf381b4654a4282a6b40a61eb5cd25c7c71464ef91c625531465b2342a91bd53db8b176d72c72419851720c8de2384ce86aec9714b5ce096d8c8f1508b48b217a91262483c3803ec7036bc0d868c06f4cb939912450a153fe8a03a9751c7bc782da6c8689c41b6c164cb552d87371d5a3866772b0bd3d84c298e3a43aa8391811e73c5808e7936f0a5b2d131e49fe3a1aaeaee2191ab6f31383c1c7743f58d003030aa6a5655557527a9adbd6c553d245655352b0bebe8e4e058af5ea6150d22e415794628683e3923900c2708ae0ed222ad2a1d505555a057555555d9abaafaabfe743ac63b086fb402e568533feb634c2e984bcab66a34ce867e4c52555555595555ddb954d5ab4260679c01807036bc3b8cc268c93019144382f722bde81832379521c9209301598b46268a1bdac34c872f577bf932c9c098d0e161e04316853133d90d06e030bda0870e0ab4ea21aca791c9d6e1dda1082a28e10637c8340e4ab5defb84d2803883d8c717db6808f8225f6da69819f8225b44c9ec6fb489012da890b56842e6e66c2d89231da3b174b948c7303224fe863ccdb0c51b89a7c51499fc468a7936f29106d98d8e612fcf001c1d8348c7d848d4adcb243ac6ebf16cb04d965847cc1d5d9c0d7d4ab3b41653641d6f9049297ff8a263dc0c79873799c9bf251cc56283903a3c416a3a8aa2aab6c164776703f519f822bbf716e9e6925d8c97c26f937b51a31186b3c1490d8c600c6c7634d689eadca1731792bdc71823d43a098eb649b30d4fb913f5deeb8e60a8ce456af1496417d9eddca08a1b44b9c192243c7945763b498ee40a2d9544140283344e069271dfb469da87e5ccd65dc788469778bb3ee9af48733d8a14c6880e75c00c7b4882b02ae64939eaf7efd638bdc73cf1c9e2edc56f3aee8d50b9c2a7fefca4eae9bdbe91d241085dda044da43cff62e7f9438e34397ea62518b6eb40cadb0f99d22240ca11c399bec1112449728565da041fdbbab787be699d981bf1bd23f9b58ff8daa67b3c1c6cda47dfd8b48fbee9d1361dc3c7c3e126c6d6a1daa64df106947806caec641476e3d837ede3dd101f2929576738a848bc8691ba34b0e8834c469b5ea4a29473ce4cc6f73e2241e6f096e517713257ddbcb621f21235f2513bf2f42efec00629474cd11819f8a223d36420aad7106430326d99fa43c0180879902ff2d336ba653993594622c551c798af4e3b26768c4a93f4ee4a4a5d621ad20e0038821247c8142eed90295ca25e93df5a8b29b2cd4d31bd0fd1cfdc14c7c44d99c2439bfa07d9bb6b1e644f888c829401e3c5120bc0cc1d40e820e5b7296e32d9e918c26e23191ac743e10a2abca9be38a5bdd70de25927699678a371998156754e732ef23488979ddb62c7f81c75b996363df6abb91ec593e3a6b8294b962411050e2aa25852c40734d0832ab8293c504c197390f52be5d1cff1e41ab890399ebc65794846bfb91eab8a4c0911f3a4329e1d4fa80109c29029da2f1498afa791103bc6181b26472d02323239cab8e457630b327baca948bd701d03336d3099c242c4ac6d3632757869585586695c76d891c40a4d6042a672a5b90c1f01994ce153bc5e1c42a500954ff2f3ae06f66b6d2fdf5f56db64b27db59f6920a0d536984cdb64dd3acd16be14a0f2897a7c8cd3a1775da9ed79b6748ceb3da30832eba68e5161d55b2667af5edfcfb4ac4df69546db648f69b64df6208dd42fd6fed24634a8c4b8b18e393f48003882124820ddca3b79f99f24d64106e129f71fd4d80ebb148e79a3a4ec860f52d4dd85120709111bf4c857871b28082dd5ad515486d06af2c917d41a3c919fdd0e0da0009261b2dba9c14f8e926d7e8f0792636e924dd9ed1c49221f71926bb66ab5efbd37e0e57798eca41859820f24536001141e33dc2b66eba5bf978ea24a5c89d963ad8fe51b14850e7ac4146f0fbb3b58b9e2e7736b607d1c3dce87fa7b8e14f1166b76c46afdf5c4faa4cce8708cef617caa7ecdbf8ac5e36481b7ced5fbc96f733fd6b2e28b31c618e3b3680feda13db4474adee82ded146bad3d0f87fa09855a558c81e93cadeafbc9f43a9a5af1d62dcbb29c13cbb276985a35008b6f33c76f32b77ca3728b3b03c8f0f8e70c70ed57c5dbc3a5194e6ffdb731c69e3ae7cf4fb68e69a05be3bde253bdcbb3db83cdf1cf7a0663bea25eb8062e64f4176ff4546fc57a0bd75fd8de4be302c9daeb0c9a8ba94d2fe288b57b4d13fd5ae83268a0c7a0b1bc8596bd455b794953390b6de4b25b81dff52dc6874ff3d6a3cfe67a72a4b1795a16de68ecc4a7f877eb1bb51e5dac70d470c99efe3d8ba734365bdf686cae8ebd062ed05f1b56b4fcae22f3fb48cbef222da43d67c33b486b67c37ba64567c3c39b0e68dd1ad64f15be5edd1e6cb6fe0a6f98cfc3c18abfee71d04be36a776913c43259ac95525a6b7d0d972baee10bd99f765aa3265bf8f9bc1be8a94f4de2f87e72ccf0be1f1a1e23a5300d5a113bde0c1abe0306f1454c28336af8610c4c129c183051021b14818a19f0356814be4018dc2c8ce0282acf7eec4663ad452dcb7a3e14fb619e8fb599625485c2c018fb7c9e0f0c0c98e7a393b58a14538c9024c364b763049b4c04273c3c41b2cdf41d65e7083f3b4728b2f344153b4f3cb173841e9d936376d666201d9dd3786381787bf7ee60e5776bd44b25391de3d290d3aad57a7dc8baca47af3093353b5a7633c15ca81b931d12b5921076ba22c22b9d93257e23bc6599ea275fe313767a3921b46033c99b751d7d7a306331cb8703fc66cd0ef5ad39f1d639d96ad39cf824ff3e6350735a3bc8378942ac495c12ddfabc8d91ca2f1dde650feff2383a89943227cfecd6a097d8edc1bea932c2f2163c9d96acb1bd0b4f59798a2fb61d456a36441a0f98768389f5994e5203beded2de6720496339a497a7a80c24b5ecef2e809a81811786192c276510931ea6b283eecbde6d25afa2198d369a79ec1b364198961dc33a60c9babc8d56f2709897a3fcbed18c32f6eb1863b935de4f18dedebc3dd8ecdc0b762acbd8b70cc31bcd28cf5f78734e94b4121cad445e1a36c786c6834ca6c4f5849aa5094580428428b962efdd3c9becf04693211157b882cb1564aee0d242fa108ce4c0044de03165c7882b48a1093f96d80013274ee0c4104d0927445039d101cb66b7c3638316947478464a3a749c226b1ca7c8dc6b724d7eb8265708a1901823ce7b144551b86d80e00406e8e5822e54970b28e0812a88000c4610010b7a4011f991e427033e4faa68c284db24cae502dc240857132964143c2d904ed4cd7ddb9a9252c7a2a04c3b980d9a208490045fa256298aca288b82375c8655eceeb6015f1ccf10a6e84c8881220cd9c1902a3c01c7a602458ce044810b56f859d2a406b0091215227394dd0ecf8f5cb3dbe1d1804e472185ec2d05c6ccf07e4f81312e7d53c36978bf9398e04b976e72f17e0b69223006bfdf4660cc2fe5417151e03c21a077e8fd9764f4fe63f27870e48031d72f8bf71f0f18b3a22393ab0b4d869bb4185e9f6da1bd1926adc55b60226ac0d88546c303f0dfcd68a17da65bbcbad046a38f30112d2a2aa2128914d2de0c77690c6179487333449ae923cdc5dd5d80ab2eb48b4b3eb8f8fdc54480ee02977c5809c0574cda854b3e98eee22e3011d94db8e4038b9b6ec244bc9cc5dd0c3703d3de8cfaec9015bf817da65f66f80c0f80b603063ebc296cd0048f1900f8cb771461ba8b03e0051761c2ae09b07591fc4e44112e308f192e77215283f6a6b86825fb920fff5d70a9bee4c355ee824bd64b3e7ce52e7806ad64f10ded251f8e3f032e557c83e5251f4efa0cb864e11b2b5ef2e12c3e03c6ef9a764d062d06ed25812fef2db467447b44e0cb7b497b42e0cbcb5868d74824160d084d457b3c46da0f222d47e8d12c830d65a8a96deda052f78a11c92fe42e8e1df399045d2242bf7733a49d010acd8b6e04a4bdcba7a3c2721a43564032f43b6fefedb5b844fdc2734a7e0fdd8a4621a5078a10f0d7ce871ec244803eef66dce328f910bae8224c847ce86e461c5d04fae8eee290171d079452fce835332ad484e84be0e52084080287f5eb3486540fc2871df6f5d87dd8719fdd027dfe5d1cf2a1df30623e543f1fc225ea2c984889c23756f0bb0ae631d2704a11dfb079d9de0d6c1cf8f28e692d04be081184045e0e228805b819f30b7033420f0247c907ebd8314cc48c791c251fb0cb4b4cc43d763723f41a0e74f90ababb38ee6548a35aa69566dc05b8194658effa606fbd2f8e7bec1047c591e1b629c15f9f691bf0e5dd6ab56f4a10dfa830c934b552e31b30f0e5bd664611a19ad85406d5b58c8c162895c942143a708249a40208110a132da801146c40052000a10a466022454f1576f0440b459c9042e50416560c41c2c7069ee040912858610a31d4841430500513306182c78ce84410728c4e4091a328c716e4186310302085bb0d051821cc828f76c102844d4421c31e33e047ed82056cc309d995dd4e134ac89bd389541343c854763b4d34913718258f052cc86e0a3c921041093eaed0831bbc40a2090ca0000181901754f15c9c110aa420a4702ad229221542286113156428336c92820c6f23158ff49aa841ceb2db6962053c728a7352647707812636412041cf8df7668850e091342ed9069e894f68020579733c3726e669a204110b2e8a26906802484fe4818415c6fa1ee92550f3f0099e521a557d83596699658525dedeabda467d0281481346341326b999e8e4eeeedb03ccdbbba5050173788f9496d1c89355773d5497af68f54e9746b46d7a5de7a531e5d3e1044f9de0add4284a3bc5574ae1bb528a8b0b1162741982109a4990f8801a410808c10d0521e0079336b95cdbc6528cf0158fac050615f9f1fca00acc081329c8db7f5045de5ed02093df9228f22623c2aeb03bf76b74eedacff5d538329189b2dbf901063213aabb2331c1e867e364fab9c426cb532d3e91f51fc992d447f35675f90adb4ce9699ef6d5619ed6e0856ce2d3b46dba099078e2f7293e6e8170489527758c968f769624c9f1d41222790aa4e7b0e19636d95ba38210f0630a26e1937c7f93a7366a87bec4270a5f044b0859a209364b30c18819b2db592267091940f88610041997180419572961ca39e52604074d98c207826c41891be508103ab0822834610a3c9002e5c115620a9ad46420478a1d298450042460b8d1020b829083273c50d844412de13e21324de6bc6b92e7230f8a90e79469426d763b4b00915291deb2440736d02e4e2cc1a3033b5dc840b54305e981d73cc8d94193b80417171e70214f9739a79c73f2400b796260c674894ec4e86407093966b7b35304105478677a8720babdc83d6a530948862f513807265e6024c7cfc018a8638301396880e4be6b23228f9cbcd97cf5f0e0c183c70c18b81d1d27797351e814c979c6e6a6202004503404dce46808b8d17928c87d820c71698797e16bbc0c31024210a54d2ed7e4ecf01097366550fac6a742082929e7bc9b1653f751d2468110cef9be34289c529ed2240529ca658aa2a886f31dc46ccd554fa2a0b6cdeb9b8eb17be4f77e7d13e4b9c718638cdb73ee6da16eef05d916b3f68480d7113375874b3a3847e396e4f8e79eb37235379b490d23256d155c2c8211c9aebed0c64a59b29a368330b31b7d346ff034db3675cc2ec60378a8815b0fb2f79a6c39a15a31d9dd9b5169a7d1af72706d82556357cfbb5cda46ab8b2d2d1b65ab862f64b26f0dd72648a37aacb0835352cb7a4f4dba740cbb65a35c44d75c1dc180399736f59f466a534b471364a603d8380d29c3bfa8591c649b29b749a325c83693a9bb509b9a72ac34d72f73420821ec9c28f1887853e34dc469020eaa48d2932b06e30932cb5937937afb7837384aeb1e99bac36df33cc4ec1b179d36e3a5f6f20e8ae840487e57ced8d6c61544f969409b287caa990fe2a0d6533230f9469ba828de7befbdf79e6b138d1f668e1498f9d47f0f4aa1839b9c9e4c2355d5fb53db7ec893565965fd95b6912e6cce3b4bd6d65bafd60f799e8624b71ba4da7008158f4a72f053e500c9558e113948a2728be5d6f56be52b4ebaa18f1ed2440769fd1eca01911cfac519e5c026674a886478aa444886b723d19b0232646cf4907d0804c242a1872ed2b6ecee2bb7212db4f2990e8d0e127df490b6fd9043973701a06f3487b26f590edd754cc9be272043ceb692c508a8c9192e613fd967b7f6879ce1fc431e3db465a1958fb46d745168e5217c5ab9c5a7fb506bdb08d390f20a065d52e85e2c0f34e0ab4f63b963516ac96a7674cfe10d14d2cfd1f7608bc379a2f0c7c3611e921e7d2bfe56e0ad211011aedf20ec0bc29a1db24221799315d6c75bdf100792be4120cdf2ed8240b23cbd9b95059fe02f4ab3cc4188fbe7c1d71678425cba6658945a20ec81370f07f80a21dc5a5c7e8340f23c8b86e1137cd7195a39fc316b0b3c2b9ca15a7009e34d07c502c325f007bc28843f20bcc9f59de55283afd849da65d16cf750ddf5f007adafbfa7faf97bdd59e9adf0c7acac19f8e3e1502fffb46be607fcf16ee8813f2204327300f863c563a1c28c54e4882dc93a31546e8c910a78a3146f134f3b635c222ee9188fc6a45abc349efda857b46889e17d7380d8c311264fbcb980353bb089f19cea8bc8d18d302c469d392a4df82803d6b6b824cfc7805bf0f62fb4c0f1ce451c4bb3c6085f1fe7c4b466c7ebf917794ef145e41971bc633832a123bcc525b96fd25680aa4cffe8e1f1693e23fe055216322a647ae78425647ae7e4059d8f9f691d108b2c1e2b8e4be052cbbb69f1ea0e5e198b76d27c38cc3f0c8b4b9adeda1a29e91abe9401c7805be0d88276488b07dd5b23dab8445c8234cf329fcd5f55bd342e154f6bd11e908733df9f5127cf4ffbd6e2676049038db4d15968a1b3689976bd1b1ba794fdcd45967345e5efd68020797995cdc5ffa4ca57b452bdbdcbd55379af5259e2e1e084fe64511c150fd3bcc8d7ba758aad4c95632ab8547348172e7e12a947d2cf1719fb46f32257cf46b8062e64f7222df440d84f15c5fe6e60cc732feff20659ae20922548c8c31bcd8b4ce3120f07ecfa8d4bdccb4be32e7937da03d22649715ce2dd305b5e0c531e6c20f387c5eab1533452c11ae610283bc4ef6a16b01b421677245ab363bef9a450e84a10be57a2335cb40429f8091f841042787f1df63cc178fbf5d03b03bdbc2ddf9c3de7bc77ce396997600b6c0163389401fe503bc487507bb8e472f9f99f869ea6b4e9e53c0b16ef7a9086af80354c1802652b98ca4864219cf8f9d440c3815cdaf3d1ee627a0f8b19347bac85ae69a05fcb2e83b6f21834ec2d3495b768a39734d167200badb220611696c7f2d72ccf8752bcd16c149feea317b78b66f964a17ace1595912804cab039fff77a76bccbef42732e146e121251268c9223c5daad73281e604ec6dbbbb08db4928e71a06bf7ee208867cd0ed849e0ad6632a15b01e19b3acf5a81cdb702c2d2e12cc10721bea0854fd7b143ab66471146e475f40946cb7a4666b78eb47085ed5b0bde229df6b3787b4754de1b43cdc2251697b77e1833035fa8b77c650421fcfb33d23918f66b6e34b3dc35cbdd5b361dd1c216f8533ff853cfd14a565a4ad8b61216da016cc0077f5b935fa1d993b4cdde67843acd4418845bc99b6d59f2bab4183e599f98b597f5acf58eb49279391d8a878e71d6133a31da4b473d8712a7231ec9f246e2cc017474a4901e6908fd2a27551158ab7ceffa7b9354244f2c5bde5bc3924d85544a54452e8dce78aa5bb74054a06f0e4054e01dbe4309ab24b6a9908ac8ad8c5c888d2a25ec3d36c2ee67fac9f71e762fe57b97aad9d1555749f25675bc544845e4e1d0879591bc55f3e293bc6ff84a6e3401f2ef5d15c9d51d7505b81bce00a34f2c823413f66c4d83b55fa5c4d3361a5c5279f5d93acd04155211e9aac85615c913bb351a7b33048dacc8579fd52b4b5bc804d4e9c6f29591ae2cd5576aad9c484d45eb273a8aea15912c3f6f8dbef56aa45def7b91b6d91cd284b4ce74316d44b70e8c813a4cb818c884930c64b3ec3d5b2951d14bd9179f5e5d7a93a894c0a813a8039970312ceee5f558202a255c4f7e154be286b3e15e2cce4591c11805381612884a8987c3ab947837c84b93c8920ae9184ad45a15b19aa79f689e4a09b862c9d5329ac808a262e7ede7314922d1375b4a1948c808d2315ebfbebf6761f13cab426bdde269bc65d2e2c9d66bad6a76c0ee9181d33288b40b41d82d5f5542240c89648bae4a46902cdf8de561b7ecee866d5d29af9441246ff73afad40d2d6b5e068e0c2179b34eb2433290783282e4fb383fe79c73d667ad39e78494661685035facf7ebea129746bf9f998dca0a8b9497f2dd2303e741b8c90872c163d7b42ceb160bbc915658244bfefe6c3a28fbebf5c3e42ffc8c086ccb8237d296512a78a3966d22b58c49d624e3699375994c5a329078375c19385a637fb771f7b449e2c098cc06be50a770360a8762969424997a66036332529635a62e0389ea9a3f970c24a8201956f4fd7e2a7ca2ef51109581c4bc9b252553cf88c018122923625141a8205bffe46ba745e1648c860af29325751948c84062ced93d3290901124639235d198642a632203099595d7028a95d6b9ae94d4d54e2dab9494bcb56fda47bd176f7d53f1e96d34a47be96d9beed14d6a1f1da35ffbe89b8b9daecc9a1db76dbac7ed20533b81ee95b381d86b0f6a1f0f879b75ab4cd0556cdfd09c51903de80dba32a2db37f7de1a2b7ff8747bb85719ddfe95b922bc6538d43e6464586d46a67d6058fbb878a32191ee9b58d38dd2ab5f46a2bf7e5def5646e64a12e9caf42693e3956f1feda36dda878c0c1999f6217a222dd4f84ad14b73bd6cbce9184042ddddddd7bb9fbd3b58b96f0d8bfa73f9e1748c4b8392d5a5f15a68f294365f5d0559f9de3b7536a7a4b2f729e57c854f9489a4ad9052ee40d1c05c6dab20fb5890564614ae6a76508ffa8d49502dee8d37d51683dc16f854c5f0d782aa28194ec52003de9a7a2adfb44cadc8148886187eabdb0ae6e1920c1f02c32fcdf0168aeae186faa3a86769f6d6a02a855b3e03de64c051bbec6ff3b26ed594ac2dd44bd433188c4626f18821da142f83b6e3f1686a6afd194883e6f218b4ab8576dfa29534cda24c31c61116e14d061b7a3c82fa46f36808f4be35a81cc40b43be40d93719489466198904430f7a078170a9fa0d18334486371948998df1888d06264f1767c33b756bdc787bb0f97e081853dd39d308b4fc6e336fe211f0d2a843ec8853e2114fc87b8594f93e66dde52aa594a77e11f292725275ebdc14de5e8ffc6aacdd57ad2f77e3beede4a5215bbb77afb5353967dfcdd977735edac9be88ee76d7cd35337cba6e415937c85ef3babdfa92b8047aa8bbffa49cb3d698f65624aab746ff24f1f6acdb83cdfd4f290a553956035d3259cdfdfaf794f6b2b65ea3a5a8592b7ef59a6defc3113bd1cb6f0ffb85e1d27c1ca0b698ebe9b4bf1eaf8b67bdabf5f2d2a852bb4b9b2295144c08092018901f9dd7f37eaccaf3793eef07c88aa672d18ba4f5a49cb0cb82625da1e77361a3d0c87a3f8de5bb2dbb621da34f4adea845a97551abada52b166cba425730fc7cac52e8f4f330a25f0c9b222c4fe1b14929b4505e20520b693064a8f6540e31aaf27cfe636f877f493a468e559fbc1b68eda1afc83b92e1dfad4fde0d6f5a442c4864d25715fe230263306dd333529f4023b503d6554a720678d6c3d4d20ebc9969a39b6c733d397a280d6427eb9415c3060f37188063d64ce9ac0703a3656060c8748c7691ea80cb91a2280f64f44fa6636c8fd240f6723ce0071a20740cd1316afb78373c71f3438e8ee1769eb0c9f00f88d23eaaea6cfb7838bc3a4d3f1b781635786193404d8e0a53ad7dd85779d4f72822310928a4834020f65edad3c39bc35bf1b9f28d6485b01c2bb07ba998877bc1b07b09e18db4b2a239f712bad3409fa1518f0e4ab4e9a0e22d7c338b86f0f662e891c2d6378b2dbcf50f864fa157975b4d089fe89cef0a8542a150844062ae3212c5d04f4aca3436fc417be00f08a47be08f10de5eee8137b407fe8040e08f50a827047f1cc6ace01c21bc91584e62c150843752de9efb057e6455893797ec72d059033be899f6b2abb01c707b324a29fbe6602f9f1da4617f3c6856b3671aa6c5eb85d46060902004027f0819fd20c7f7737b00f20364733d234a299da7724e8a52207452cf474a49f1c8e59a6953ab959a12a980523a4a0fedf96913dddecf4fa67564610f7d7396fbb13ecaa00e483da439f742659f075196c2a80a660cc3a7b655fcf3a1a732c35bcd8567bdf54d13a534406c2db97ea3a1a31cf1a952d63aa60d11895ef136caf4204df4a95d9fa005843e3abc342e4cdb443f03abb499364f3fd343449736af80d0479f6911fdbb1363da1090ed975a7f6da35c3fba34daa67a9166fba5fedd1c9e1d1901030cd386804e351a96d452df03e1ac5f1ea53f3e3f3e3f3ecfe7dd104f3579797725850f62d273a4c9112647961cd139927324c9ddb0539fd72716c99add7a08264f4a3f2db678a379ad206c3369557caaf066f10693e38575f476b18e8e54e353f55af143f26ea070bf4292044924d1963f6abeea67a359449695a440cca77a186d824f82240992244892204982e4c894c9d62279491e14991998f8903c242fc943f292bc230fc94bf2a03c240f098c8e1091068c155f807948421770a18a442230891692630c9263c411479ee2b40824ebe3dd28945d2b3492e6a0061f7650946519e28d94e5b10b637184c9d1bafba46e610a261e71a2df21ba0a6f35149f280a479c23da0461641829cd3212e987312a2cdf9ee737d30cde686056984867d11c7c71f0251e116f6ee21111271e116f22909b7844c4a1f18878134f6d381baa80104208e31152a89a50635f90144f6c0299f492a793e77124c9c9c9117da3312adee83117e3babcc49b8b92fde4ecb5ba670fc9679a732fd6859dabbf309a51a6f617b63e371d30527cb2d88a5d953825394a6ae428c95192a32447498e121c49725c8d51e48d1995e0488243494e141c4a727024c1a124278a6c2538e261e00bfc8dead0ba2ac20ce36cf0024c9691487fce5192a32447498e921c259713ea9c73e2d7a33675e9bc9b71dede9e5655dda8c701ebf5781c8070146d1663104f878db8e560783de0e340dcece1cbe3c0b3791c783d1e0738703064f00e421835ab2f165478237945763b4900922bb4541251080cd23833decd58cd6ac6e7aa5fd5e9651c1014bec0c32cc73c85d372ae3abcb98ab31c99c3341b6643b50dbbf23157f8647175e1e8aaaa92b29aedc38259d4649431de1ce2650448f914f18a30c47703b7673341ce3977a3b22bc2f0785c0d0ac105218496822dedbdf7de7befa9de61ed04bbd65ef06af6c22c76dfb63ea94fea131a5888dd7befbdf75214f5db1262f75eebba17bb1abb7dad29a97743cc5e5752d7b517bb77fdcbfa581feb637da61030e7124fdb7c7c11d4a4a88bfa65e994b0b26e597b416bd96aadc5ec312babf5f65ef6b2ce627b36bfb20c6fa16fd947bfeefafac88a30d636843709d46cd7efb13b78f8aab58f77835551bce56cf600abd931a968a5643152998512ed8ffdb13ed6a76647dbd89e1028c687429f695028b33fdd0383369806caa82c943dcb1eef2088647dac8ff5b13ed6e704a02c4627b146c962cc5e9d64f527d69efa2466598d82c550963d462c1ecb4211db9e1541f291cab0870ed2b0080a81b2faa43ea94fea93fae4f1b8c04e128cd891228a1d2998ec4471c4d23bd01f8661ef1e1805d24e9b75e92c0ca3260582bd9e450d5cb0aa5377864020d0bb07a8839c2e4c04021dc342d84313848380792b22bbdbc375ec339d810ec2df9ecd412211de540ec2dbb3f9263a0b049d056fcf26b4ad845656f0a682439b046a44d94377a1c710de4098c66618c3f5d42fedda0a8f3eebc8c6d1a321a8aa5855d5498d32e79cf371b2b0735e17765d57766cce836c068542197669168baa3e0135fd0294d04a28e79c76ce9a1d765a228258734e6bdeb2d64e6bed2dccfa56fdc2be55d9acd5efaf9b55d7bc339bf3baae83b4cd1ebbe66d8b69ac55b58f91a5554041044209218c22892c80ec76a09002217db96ad894c69a1d6d637b2a2be544dd39abaa623f5a287983b1a2b6aa9234aa43d1034592aaa2af2e35124956186ceb2cb08e36bbd65a0b6abc65a291e8d84778a37d11f61086b70bcf8a71d5b73e238b75b5232477ed896d6975126bed1192ad9bfbcae6b2163f9bcce60c04baac66ad59ad5955cbaa4fea93faa43e11323530a9fda9dad277da36f4b6875694da1ffb43e9dfa52c70e0cb3cad878f876d9c8dd4bd4decb37a09f1c8933ea451fa4ca3b272b6062e6417cfaae28d8604bb4786b73ed6c7fa18b93d7d80842047a022574c4a2877a0c0c903c86e07093fb42d859492cfc51d243cb1838422a1cf793b036342d8a52b08932e8adda569e8187da520cc664af1c5f6dbfd35318dc5b53a59787bf69b755b7db3f021c5a74f0a62ac6706aabb73ce15c082a0d7c41ea05f28e8c347d6c3e95832d9e9157aadd7b27ae5c2dfb656ac43d27b05426bc56d1515eb7d12e99bf5d78f84696c5e4172dda383c4dba36fb0be99ffc49bf5f92d9b58aba84c3aef8c17ed430b931a975864ac0842d86d59cf328b64cdcb8c4658fb28416c61163760fb802f598859acc024ad7dc8b0683266647c642bb05e65a4659acb165f18839eaddfb03fd0c5343667f8b68fc6d8b7cc62cc6618033dc340b854dd3800c523f3eddae08b297a23c3ede3d93c3ce78d8c0c8a3acb235900b6bda93dd08893396b4f6fd17a105af549ede91816ac9786151ba735fb7a21b4ac0b6b271676d5271676a9a8d49e6c852c782d4aaf9a1db246b1dab22c270f07ebb0febc8e81c1da93e5e7e7c4a717a2a1f4b22c5c1259af4e2c086b8f557bf2bcb5f0fa757b98202864f47a45e55ab657a0905619612173da9bfaa4713a480ba94faa936c594b9333b03e44b38bfe5df5c983f67dbdefb5d7de7beb931bb3cdbbb1b49fb82af2fb3d36639ed7a27155d0faa43e7937c8cf6b9fcc597b1a27084ee3d42746b29ba126f4568a501f5c0281e498d9be1552af7ff0f6585efd0181d81bab35a8342db78541e9d08c00000000003315000030100c074483d1681cc9caf60114800f8c9e4e70501889c3284821658c31c41842880c008008c0c86800002320dd315cc7825dd6b40531b785067ee4e6461e40e7f9f49e6bdd874fad613c3d8874c6a19122504b798406790800ac88155b16a2be21cf7000ee421192b0c0763a10c26df02a0b5261c0dc965db4ee032b2dbd20e963fed1665b2a79db3f0b3c492642fc27cf0ea9b01593dda04261cf617969e5d8a51644c14d52ad608154090e8a96906d9cfe371b87e3159923caea6fc1ae1edbca829c7a47cd0807762d3ac9bbb7492c77c451024660de94b6da4b69caca96594c2bcf77af86d6b59adfdb6f06d3f7fcc1c7ae2981cfe0140a51d7602cc8964da2a36370c5aef5bdce695062876f754edf09d8d5b5994400bab6bebab60606815d7af2c65c5c466bf2ab1ee1e56c6443d8ced2fc1ea4213d7d3c2464a93ca64c2bc174e09caf0e32cbb89253ceaeaeb5f2f72af75319d55a5d3b414c8f84e040bfa8fb3c222c1e79cd4a805ed338afe1455dbbf3b2bff2184ce58d0fbc5a090b1ce724a2cdfe8ac7381f21d1ce7bf94507fdd3b5ddff721006c834342fff982ab77c5d7c670359852d0142d2bb49b3355a16a4a86522c4e05e5fd5929ac045005f907d397cb2a0278da888552d74c483a5f700452a081be247a6f513c2caedbb575d47ec76c6c93f4bdd86d72761d45705457004389919739c3da873e24988d802173650353329870fd3f30e9c68c637602b6328cd892c5c2a97262209d81c57ddc9e8d4a95505064b0693266cc330b70a3671f7ab9b2b13438a506f70561b8088e6be711ce94cce90b36693fbb6f5b73d0f21a9348f2034e644518cfde1f0728f334564029e87214404d3be9b9f5622493882a3d769361b740a598ac5aea0b18effb99c614202b0231b3f67aeb0c9c57583d89b600197936f5ca11421d79c6c4e37c860c004a3669d25915972db411b28d17bbe9dca8a14d1636c90f71159ce00deae5f5d240f13f6c455d6ef754e68b03279603300807a3a6849be45c0b9d6a16e0478ac981507f91408a502eb87ab719e8fd4b77ea13e58016fb08ae51e7dd0462a5d1c54dd903bd866bc2411397695e1e1d66f33857d137d438c864363f602f6da3c22ad5086d8515c17e89c981f42ff5f2f2eb738fa1aecab5046ac3757c1b8f074f5daef0466a8a1cce682bc62a98c7eb235ea9c2ea5e78cbbe1b80d8743aa92f530f3d5494b0db6188d349bb3087ca2ca9a0fe54c9a110e1ddc2213ea26ce5d7ebfa802e30c9c2257a47e5b4b30c27080114915771c2dd7d87cddc60f82eced129f1d76b91c6c2f447961531a3aecd37d19c360c3035a1ba21f1f301cbf5c117e440b6856d67a272770f2ec1b540bac3bdf0accf99714f759931267b65747b51c1a3446456bd2b36575f0906ae8f1263ce13ef894e1c9e93a820f4f8a46bb6a68a1eaf9baa23bc2538e34947bfc924a4dc7b61c644d0227ca90752f54a2b755a0eb9a995e44acc531b579ac573456cec071508f5864e4df9b67a82edb0d048a2148d9932a86415dcababb651c5d67b0db3e88da56a57b151d45d1ecf9384ea0480df7781399da66e62dab321ae7e7c1bad3fc9c9fc774469bb78568580c685f9cce0307c20ab64cae57578598372674209acfefc66a58abf1160d5548d13630d80b3041198c91f60f56ee3a01bfe4692f788a358911232d24f479ceed24456ed5ad742e781be5a5b6405cddd5fd00360d2f5bea0c0df45f4360128a6f8b9651d98cfa698f655bce5a0f6524a518820bd1b3c7ba625724df2a71a8c88f55c4d17ed40abe746ac3ed4286e0656bbc5a29815d40b53eaa1086ee712060dc9de61462d03b0fc0f0219ab8a0d47e18bd9e4b470265dc82453126c2eaa75771818ff6eccd86e22a39a1db4dcc9c3743ca03ab330507aa65701037e7bea611ed15cc1d0a1a18de001bcae07ec8c77bff5214347db383ba443601f26eadf1da73fea3abe5afc539e96dfdfc395bcb539ec87aec9ab992f23a72e7f37572e6cf27ea8b90832c0a8d5d83947795ad1535ac3b65b28dec690749c1767f717c00273b916b1756064c0f897921d33ce5f4592e517221d5ec40fe3ef6d69bb1b9d9b9086ccfc0d10db93c60f4caa1e07516ead6f243507375bccc5610a9556f1d121a35015634ec7ae9733e4c32fbba9e639905bcfbffa7d8e6b5594cfff0e8aa62f911922d24f00e3acab46b694fa5bd7b0af51142fac74dc5108229c2c0d157e0592ece932a82af9785c19cb1fe2b32f15e457e1de8fc6e5f31669cbe3108a3685f18e656cb0b6f3049f28ea866b68fee6b528018d130e3f9d3f6ee139acbff735658fc3d9b3e7148419287851e6000a3d6db50da4e283e44d5e60187e3367c1d83184adfd86246ae8fa6a6cee285ac72a330f1ad44e0280774eec0a154d2a7da47ba97baca44f437325bc92958bd90fb35d624f724ef9ee729d86a373bbc3aea1cf357b7036562da1a8274b2772017c5d415091955b70b3ee551ce6522e665f6a3c7183d66fe60a780a4dad698b3277da15b7432328a46c0e8d64f5e3e4e9c27ca081b583fe57baf5a56656342974cda3e2c11cff28ccc1d864fd50323c675e47d8b45d293323dac544c156091aa97621cc20e1a99e64ef2859ccb00eb1adbdfc267a10cdd741b9dc30c2009e73c61b8b575baf2369fb2df13dbf99862d979c44615bd14fd1d54ba2857d75bd55b219bd20271b79d15cc205830af8dec5189eb9ebb5d6908fe316d9cb4b9fa67a081acae3a9a695e692adc3a25418cd7526ce21f3d4d55ec787de90c43be3623e2f510d0add55667088f59bd6f9901aa8345c9947d376a88c9e9c8a1654497b23ab0baa8f0ab86c77b0d47e5cbebc176363602f43c6d10a070fce4a3a214c0d49c333949794e5360c14b4d15fa570f332c34679cd049d4406b0b7003da052c32e5da3d3ecd388c7d6f7c5602ba880bfff7e858bc34034118ab5127563fb6b6dfaac9bdadcbef5eb19cded8f195eac61e9111de28da65f754dcd8841c1a7e66d3da5339f3f37c079754f004e8d8f9e53766f39cd7713c8402af4016163292f0d152b7441f11bb8544d313b988f2c230b2b49189bcedc2674416282dc7834b0bf3d529867d36c30e5b924e7b07774725842aa64c5dd423b2569639e3b15506c72796273f220e2465e84972ad8a8dbc33b862e56ae4cc0c5692073693d11a4d80707c2ca2e7e53ed32a8b78f5f290e5cc3563f7f2352981ffb509d247fcf8aadef8556667e5771aa56434576ccb87df2ce869dc2da89ca79b4a23978785d291df2e2b5f53ae89213623d5c6c7480a0f7643303cc1b71b38ab6a94197ea3a6ac155a9bb7129064050e791c0f1ba844b44973b2533803aaf63b410f31b180801ed6acdbc8ba233cb29cb39463d0cfd4b55b3a4035954aeb64a63e3e575f10210d22f1abd066aea0617c977203467ccfd445d19bccc151719daec5c3f507a76768342a4c608f0c1365320431e60a7b822dd212848391a3d5463799b30f3307dd38b8390dcfd374d7a788ad253d47422e8c80434910993e749e3bba02038cf032f69b85f324507ff9d53515d75209dbca29bb826ddc82e810a61fe0a93d2e863169e3bd168af37c0b9641025132f703b7ffcb4b437bc1c5f334d5081d3dafa8d901e8ef6d305d67e210f3e48742b1ff6a1bc7e1c12a4d769139cde5a9a7b615faa74b3f35db81100142cc93f46f8bcdba13481d7179dd74ec3324137ebd980066a6eda90f04776f7dae54fad0c810c24f43f1db01c34385c7b3db6f58bf0f75e36c0c7125040bbe971beb0cd2147c27daffdfcb1d004600e91dad71eb27f756629fd6bbe1f6473cee5311033ed6e8330cf6d00f14a14a1f679fc8815e7fcd5c84f25614d8fe606ae8d9c2a56c525a8cc7abe7b2b0170697275ef579b3405f5bcebc1ecbd6384452e9833067511880ce262db49ebd120ce8c49d23f0bfea7299cff1fbd48ce2028715690f9551ab6605e74da9e16416590b2d038a3b7deedd9eca8bd7726ae814c8f058f60be414719d47afbd9de82a879dc346eb9bfe3587c36a08849b82207212d1126f5fe8ead3a78c5c362f871720b9273ea235857060a7c72429ce6a96a70e5da6d35967cbe280ca3081f3aea96c66faaa68a1ce8eca34c0a0d7d294121747cbcdcb9ac79f3329dfe110c49f302fc7eeb0e6ea72f3127c01f47179e96f234d3ba4f3bc444d02f0e354d3afda3fca8505f2aa640e33bc039b696c2cc4b27d05e9950a80d7a403d722ad868a7a5bbf9aa1fc5ecacb4fd676a7cc96f74386d2a3e46616fffe6a8e5e910168676aab7cce84c9d45bd428e24909a3a5dee78b7b88803521883146fdd03333a0c53d911530634f8271c3f5d09b8ef887ec3ccb9285310521d8bf309bf73675bf726e86a32dcb9504b8a7ee18bfd359f6e2d8d3a9a54aea8acb4aee32ab52739717940b088a09c60203be16af0620df9d2a9dd9b927347dd28d1e124022e765804304d171588e2f3369b11c6e38af920a535a076063487e7c33f75f870f73a9dc76903c9a621f573f535c5229082d17b84f8e7a4487a0523713bbc72ef2ec150f695cac5e9183a2e851017f22af485a256aff86e64a1c71c2781a8e146d0290f42cf3350185f0dcfca19c299ad26da37dc9fbc16fc4416aa0ffa8e5e143490c7b34005ec8394148623c3cd0bd47421a1eade8b9c01accfced014821ba4707277392d4e723174b84c8bc1c8c1352df372e4a0eadf6a872945353146c0ccf9c1e7d65b51850e2c85284755a718500f5880affc19e9ce89011c999ed4bad624697abf7a96b491fa5b1b1e9a30213814cde5058c6f9f4f191e0601cbf7c774159aeab8abe0022f5cc93eb823e923035b119399fc0dd40e706636d12ccefa0d7f71338db6bb077a94a2b9ee6319e5496ad55f9f8963aedeead768d5c3ef246311f7f1173413df3f17063cdc7e5b7f9f879ff76095b1b0ea95b6ee95258135ff41122e62b86f451ac04813793d2b7833e2bb1b691d888cb39dc6df7a22c16e2af79436e45c36bde9833f7afbcbd849ba0d6fa1fd2a13a16f2f9b0e1e3795458847d4005eee465efb69b897fdb4be5355469f820bb50f4f3070c6573f096c42b5f9f51afda508dca6c47125c56c65d3aae311cc1711dece2fdf8e70d8c5ba2566c3150a9718dd50ccf0ca708996e44401da2ad6cba44e5855ba5c33f40d69e463620431fc60bdd8378e7e5bbace6ca6ad08721f40b335d54a66d6efc82df35bfb25c666c92d31dd38d430fe1da28b319893b4b4ea47a13168d9c20a05c5bf00e5b9e27715ee91038d843ee64e3ba5c6a051fc5ea3157ef7da0d9322af9f98f3430a7d55b864a6af0318c155799b6682296466cb61e3cb2e73cb3d034a71a4d8ef2c9f123b2004fc95a6382258e7f5787d25831860244c9d1f516437c51994599ec137f4807f5c7ba71e011e12a471e58f38632511a89baece6576d538933e068a9e2ed5553ba5f791e507af5d1a1a5007a96cb30fc532b037e05556e18c85df65ec3de82ca6029a3edc014656b856a83092caeea19beb933589295c4ae221a7f0573ce60d9b6b13f9d0abda850023b90281167688a9b2eb07719311c4cd9f364d5ac2cd4f5a757ef6c9c408316b9051429445578688cabeef1355575d7484b28e982b7ea745c644b44b4c79b3d29972dcedf09b00f72c7e5002c6f2dc5db070f4a7d5f02796720023b4b65a73a4c0405f4414caefc08570047e0996b104156160f7d5824d56495e76ca6cde85b9a672802ad9e72f4da22cd0999929e585321c8f12e563e97e9409300b7f2a77183751f295edcc48e0681730707ded21427e29bd79281200fc2289b5c9e41d4b2dad781b7a89ee4a1f9625d5cc8c014b5e3f0593217135f0e0c0ec46b52dfe99dd5322c0781169d32aea060b38a89d8ea3bf2c9013041eed741f7ac7565e31f6fbfe69e2e62e00e58e1a7044a4b4d7a6ebf27dbf3c6715f296bb3936a66e0dd6a7688ff8a200ec46c72640cc03ade5c4765407729901c68f57cdb10e7c863edb7c55f846cd8369862486e302e79417ae8c131ffe5d3cae5aeb1c6801c752efef38566cb6106ec5e5d2ad25b9eac90fa2d1a2980964939e029756280f4cabacee030938b443c6a0ff347f53b39280231a0c5c039002a8af41c047a4860fd8828ddf24471e24102d3f43950e0635c66a2df7065bbca204a68bb7f8ea9842b026414258cba73d4461540263943312957892859739cdbf7c9f6f871dc1752b2fed4ded184a4e105d336bb562341a25259903813c5479b24fbf32b01a5989e49ac6fb935be1e12de25c4dec237f882e140e0142b07a8b70821dd3608213a224e03604fe16945b8fa51ee02d2746f150d9fda38b9b7b67e71a10a9e02bbabd94f668b20d47c072017a0d841f4263b4bf69c9a0de75d3163860d74ec79072c6479316dd28b24f7562f67b5db34ed60abdb04a26610e67ff8ec9976019140410aa80307f7ca459bf1d99c59fc91c625dd9611afdd1acb5d2463790c375eb31e9404f2c4b0055f0132078dedc7d065c44dbd8b126b9deb12e2d0cea0ca6a55598802e1652dbeed91a3cca49380171e4bdde1961356c64ad6e12addd582e8c8d651fc5ea1e23713a4227366399e50960342e411db3f39ee775009323a6e9390afe70fd1a2f00c5169b934a710a15cf3e81d3178e814b2ba2f092b40315bc8066524f88242d3f5d9258f22df18cd470b21c287f369240a43e266f15e7710c661e030776453aec671c2e8a99259ad423461947267112aae0435b2943159db6fa6ef0332a711fc9dc44093d0313390294fd95156513e7075fa5c89d40504c7b97e334ddcd35520a445a0d99e9582a075e9a9785ee5ea5082ab1449b5b2a815bcac2ba224ea3428158f843a8d214b19ac4c2b8d8c735fa85b5619b5c48d252b2a92fef1bc360d0162f8f68b64a66153c4287f4a58b4624263487cf9f44d94cae7a7f25be4d250cfdae2a2beeea96591b04c8f56d964c4193db27072942c0a1f85d7e02f1e1b4f868a97970490563a133350b342f640a94b3216bbfd7a42ae01105edab5efc91dba4862cf053a7fb877aea07858d1f96203f06fe65e5d22e42a9449574fee62708d9b1b43cd737968f6893d6b1f2c8d705bac9aa93c046306463ebd4d70e8f6bcc79f3ba4f81514f382b2ee2738aa4ab568f3b774138e94d903b5fffceb68973fc1f31febf0cfc6f90449944162d308f61af4ca20d79532522b574b2d4e77229ccf0039f9b186b9a711df42babeceebdc1afb90fcbc47614441c1bbdca645cb9addbb9f5e0bb2155c55fe6b4cce3b5af9d6c86b3a636905b5584daf7b650eddf926346695ea281b933379a9d53ac308571fccd1d8ea70d2910fb948fbc86a19ef21d96931fa9041e6f6ee854aa478c2bbdf0d4dad6157b6a514a484833f5b3d9385d3b1a4d622a19e6e9fe27a995d2025e63d9a9a4595c2b4d051ccc3c345d051931d108e0cb87046ac8df402955d244bef540aeb1f404c01c61ab87991814421d8bbfb1a32ae75a37d3da4823ea726f3f5c019ffc0e229eae02a1a2f2c464f1b1e9792803ed7bd9342cd448e65d6bcc30b75bd0fb1c89aa9da1359277588d22be0b8b2d172d478d6c93f742b5a25b85a70adc7c601d2ccd02c155d0b6fde04e61fc0a5054158619e2d234f9baf5e64f7078656c7ad1fc3619092f7d7a5d388a4d46e42655e5bee26a0a04ff66c83b4c3e976d45947bcc65c93fa587faa57ae216209a751ce0953933d97aed36ea0d66aa9459dc92e828d2ad7cec9870986e910ec46b90834d2d79cfa719323f05a12173c5ea7712cffb6f732115fed4b247d832ab1c68ae79e80440436b47d3c44b30e9897b38c89befaf0bd058558113e70837c2c352118683ec084193a23a17af0decb57644b75f1d65a7c4eec4fcfcdcf6c9f480a1a75dc88b6f012fb4fc0191568b73ab23b85e9edae17c995c2cd3eb5c36dda42a3e10aee0ff890a0dab9c0723f464744c98853661e1b929abea45cf5041a8219a1aabf3572991f2ec83443493f9bc5e3f77b13c095db3fb0306b567b23e5436da9965e1fe3bb5b71825395c3b36273666843fb6bfbd831c5621290c054827013accde2d489f3091e33c4a14513d5080ce31086e7ea42d4b1859a38430e33e306094be179a9300455b6cd553854d555db5b47a5bd6670c7c51c2cf9549e55cbd9f990c3feba678637ac53198d5ae1c11b482bbed23c0339cc83993cec24e72358c9604b74c3021660c80880c8872f70903b942bbc2adb621fad65975cfa6a0417547cb48121c9303700b6f7ce9ceafa1e21d15c7cd2b5f91f176e6e2543eb1b276d47936270d51ee4ef35046597314e915abfebab64dfcae1c2c3491e43adff28ddb846c8b27f5d7a494ece162490c1f07d313e610596b48ff0156c38929f6aa349d6674e229212244c956857915d308fd5f0dedf4567090c79bc1de6b4db54013af6063694b465efd749e67473b17a09502ee34bc60f17c2682fa3b2ffac9b3ba6c44e15daf9841b4daaed6a1d1f3d78e4719c0d2889ec59c61e04b9b24e20443e84cc08588c14615f93cc9d25e681cafd3902e90608cdfdd0d2b871d0a9e6f0d2f008892310c0394772f022c26195f5e103665d28da9d420f04bed61facf0b894b95ba56f950dd7d52f967c8125c87f2d9b56f0b7a8a15f60adbf66f4da3d6eb1b7a93c41fa9696d8e92e70147ee5b8bdcd4761f86ecaca14b8281ff5d1f6e94eeb899b88ca65f6a980f4a1e363913aed56d4c38fc11e5e4efe4872f422becc8276b84996c656ef588892506a4d2e6e115ed24c019b4fc7cbc20bbd47247614fc4441f8e1c2c4408fc9186a4e96c1511882924750161e41948d94aa6cb85dc45920057f40c01547a983e0df6f3939b4a2e7624de8157d714007099ed48b37098e9685f7aecdcc41c028ad59f2a2d6f2e4bdab5f355b7596f75c35f75edf88d2eec67b7eafad121c6e0305bde3d5244ea9d7e431322d89087badf3e2d33031310438eb5def06fa3203c9441d2c4a7bd8bc75cbe01514ff6a76e2ad5d1266b7798a87e3b53dbbf0e61cb96625153f56b18d3618e49a6db7c326619689e276154ddf658e3bb6880168602a22cb9e02dafa15109402c274d08eed446666fa00f84304972f8d6125b102fb63221c2e346143581f233f939a437945b2709a20bd285f1b9f834968dfa8587f2fb9658020458f705772ec1370e7ef1b324fc5ca6f3e080bbee9c113b6df3328ad393c8b52b0657a06a40bd9732699ed7ab5ecfcb943722bbfd14b90d1e90579fa3942100ffa819604b9ef966a818bf722e581965e02a5d8aeaec628969c37609f28c60c853d33956e08fe420918772dedc6a7582d69aab940b432b2298a13f608f263b7f2d1e3d19911968fca2b3ca3835e90b73e1737c4dd6261e202b71c59b936b2814b3eb3a790e2f409e81be38fe2a2ad2a6328fa67a3b24fb82079200fa706de8c1432bf781632471ad9617000c15fb8f0e6b2b92382a6161c38ca9fd0f9a56812b3441f0ad151230c9ed6c949d0b8602dd744fd8285c91978d1c773640307fd8eca0f76739af5185cac173e3c1ee089b293b5dcf263a15f50ebd3a1ce6b6bab7d49e651e9e5401e3aaa7ab5fff61aa070ba4a37d959a65541e7def26309058b941eb80356b284d34fe2567f5278a2c491919bea11ab76b9587d9bae1b31dcf7b0373f6a9527dfac41a3bdc4a117e8dbab5b2d5910aab1c30a02dd11404a7677c8e7818ce183780f6e0ebe9b68dfdb6c382e91b90da7277289b06d4edeb8b4419996d72c5c358586cfe2cab0c98f735ef0d19f0a8c6946ff771000904be0d1d18b102e26243476e375dfcc31c335e5e8e39130e815a0b5a5704b2d638b00655b8123b6f7200cbd5c15d0b8bc776319933b94a8570d0ecd5b0e829d00c2a156572b73ab10c225d69abaeab7cf32588f4eb2790344ca9deb5d48528f1693a79c1c89d42c10a0e5b029672e61899103e8a04d6174c1e2e5bfb009a6ffdf43e43ef79c93c4cf84f48bdaef0039276ef62740222096fec01b12e9507ece24f0cdc6b68d65f81cc67abd0e61580fbb776bdfeb66e3bce071f9fe5c7e90dc91c3e5807aec159da634871dab3b6574c2ad68a8d1289ee28593401651d95129fe3489b7fedaca9bc4cc9108a39abbd62cae34356a59396bc7d75d7372f073c17f2b71f2a9a3c7170719521d26e0950768c66dd7790f7bb24e1be2c8c261602aa5f06eb6e06d4f6a6d9b0078dc0b911f8ec5fe09b4ffa32d9fa85b7d776f229e470d9fbebbeeb52df12427e7d629f6a5e6b975ee9a2fc44e86bdd87fd0206d640d8c472d7e5cd77e707120caa8c92e9e2fc24829a3472aae21e718e5931356345762ca97f93345dcca4dcc1ca338632d9843dcdffa8c1a6a8ac92dd8952a5eecbf92f8525956e1d499e021bb89745c7b8a205bc57e611c8c74be00d945bb2f6c6b7021e56df93dedb10510c5ab67c1b1f8bb83e0051bff599284fc0b03df1687f51631967f2f3227c1dc01f05eb882534448fae16eaf18b8600c3187ca2a4a3fbd012b65752d6b28057176c5e0fcc6ea2e1faf42b47af1df3542502c2ec1518b7f5ed9c7c627fc52e739f4fe3d761289170e0bb1f881921cf1abb76eaf176eec3b7884684dd2ffec12a88c13831f963e67e9815b69fc3da9330aae9ea660010753011033d2edf54278ef32dfadc516ef5c4ed40ed46f16f841477ea8355b2a35de395c7eaf9aad4a3eb4d7e79661536f94ca454d62ebaf822921a2348625aba098207b9501b0085e69107ae3c0b5b532e808600e6e7df73f6d7e99059c01f91c114403bd82f5a720078f0e0860b9b254f95876190ef1c97f46c7ff815ebf48cb7716908106973152d8ace20dc969c8851d7fd11202301cb699b055eca58f0146cac163d8fe6545602d46f88bec777c73330d3bbecbba4a8ab1595f9b621ee164df28ca558c52eab0e7a074749e90c7f08e5e0f52da2e763bfa1e77e0b6c1153166aeb548a98686164fcf93fd8cf0743617cfc3d7b3dc3cf5e72ddaf0568a1a12bd7eced57ec475ad03bc02ac7daed0015a535af849763755ca8f88443218b3a2a4e4a00ee59c64302e673d2ce2120ec3f3962a166db67bc47ff0e35e64c910cbd1ca4776babb5af7111dc327ba0612c308bd242db2f82e27a6322dd6b0ecdda27c45a0aaa597a6e2bce80492ba1ad2d44577db42ca9299f3564242de299e25ce5556a46aa3f322dcb6acc45290ea5dbc2bfcb906ce07f0ea27631191cbf580a553b7d3542193df6c926ebb869b0667dc91876f738140164a63ff9f1362d19a51333db50a86910dcb8be1a0247738e9e01635a2747fee811a502b4f790f561f8965b789e58d0aea957920d0ce8a5970c2a754e9a0c0326abac2fbfdf422933655c3ca1b38c3a405a7c1f67aadf2a8a3af44aac7124dd9529bf207fca928ac5ac00158d8cabf9039f26207e443d1815c8d77c7222114a16f3190004b6da26c387746a861df16f40326a34bd2017185598b98e1402953713c2516bb6fa869d702b6e8d5ec8454f2a971e853a6627a9c5e646c62a9183b8e02198e721b69d7d0708d34b06c53e1b19701dfe4e86ccd1e6b4665c0b202945044c8055ed741c824b9bb00641b746c0f41a18545df979608599e7f4b677cd8d5e413dfda58c96ebb6476a8124e93421ad5e2c94ccca6fe05bc96ca06f727d005327df181958f81ed91267f4b52aae659f0af396873bcd2037433da3726b49d86d3daf2bca4549a066c5692cf03f60d83bb120898caf521025222d1a03ce76a3f17d0d362450398e9f09a2c73d3c6b4fa1db3fb09c2f92250bf3f3c01c08de51ce2c071dcc57ffab77fc72e76f24ab330238c0d702c03efac236e4320af088c4f363ef5abedfd4da6d480371003320df4463a64324fca7bac870b67f5e4f3159c189586ee9954a8897287add7f3f1ccbae2217c4d7fecad60348a35eb7fde3e32d2875cb8eae11cc6cef0b054a067ae43c82e8ffdab24f91ab6388be90c2d1498360f54ce535e1bab14cb432a7cf610de52ef3dc8e5aa54b0640171611b04e4faeb68cbea3be3251827aaf2aca39f088d67424e7c8a50269bd0fafd4dbf283fffc5b394d226d3e3d2aa77b040e640b1dd2a9231302f2d866a30a3a42245ca2f9bf5618605f07f8d531c394e7fefac92f122895c5b0c7f518d58589866eb247aabe2990c3516a25da60810777a847234ad026e4d884960ab112fc994b394b47bae962503d647df99fb2fd4dc76fe72420592a9cc7bfd87f09f31e0462db0f6221587aa16c4ca369f21f63957e36bd45eb30dec0773633efd8b6480f82ec012afc06f90786bb9aeb1cd55d9143c2a1029bc2f29ec665ed8a6bd230dc9f99b4f35682103f3c86f04ba95084ed930973a4377b8c84700a05337209ee7a43fcb840bcd3277b120f2bd5b7de440187da1e7771ee0ff08927c404b8dcd50929b4998c4daa6d3bad81086bb6e691e7e9724597c381ace85ce4f9b205002f246bb98259635470a74e0f96179e96a41ae10567dd388fb230ceedc3813517654bc9221d7913672453ac2e550459b1cf148910dceab6b599aacab8159d11f7e28166a87829c615d6a8489950e8d1a1e8b0ac6efaa04a29e3dab713c1b52bd29e4b2dd01c063c3a750511705affa2662f6c3ce03178d23eeb2b2f0c632d10802a76019fb78ca1f2c8f9383ec3582271ac805ebab921c29ca50d4631054fa559e6e0473b072eb636d026c105340fff64f368e43be81e147d1fdd9ed65f76613e7327be5018424d731193e6dabcb092d775d3162f1f5c815d0ed53ccc9e9c410775f7bd07c51f8b52c501206b9e5f1215d79398708b13042ad5bbd62548cfee48c29743ffa6b2db4cee4080785c3c32a5851c8303155dc07aa505a44edeafdc4fd370be9a97a4350cc83a38a8df0a672e9e04e4461d6463ecfbfe0f112d884924c1e317a76514cc5a24762ac3c2b8c306a63c623d51eb00e8e06ff1d6f633b5e28cac64f7ce0342a5d35bc10985038c2889233e94ec1002014d9517bb1254e8e1542e244cb275c216060c8ede28c28634b0817aa8076c23c7f893e3f4b242911db03f8cb6556b8a2e321b6f4d3174c953e498871322d7951719ded639411bece7af91cca75e8145f844b4b7fc26339c449135421b55e9ba294e3da5e1ce0e10a1e6104c81a748f82bd6c7d70c40c6f01e33512ae6a3bf3c533f04364297144fa2911b932c433a2460d4125bf80d65c44f6d94631ff1acace05181161c43cd19cf8b22d0e09318d07c654f8cb7c7e23079347477121966af5ee904bb0ca112468e7217cbba3cfbb98dc8faa7fd8cc3781cc3b9fb26f835932bc8ef14d27d6431ea1b8cdae4050a15059bbf441e45b812c63185dd325212e55651cec2e9f26eff3fb4c5f3e86a10f7ebb8aa55da60fa96b47f9e75150fba85840a857549c6141743f3581cd9fdde5bbb273a23dd4e6420587f20e018817d1ae7292afa3e3754ce340eb08cff01f4329bd19da81048d17d1963642266500f6a7ecd3035e25d392834e7a1c3ce484e90315eeca4506d6a3069c253886309ac5bab835dc9fddf2ba29476db60c845e33ec892a405749af06b476ee559c182fb662e273e5925fc174ab254134abf06920d9a8e1f3dec246ed34b23396293f2c9c6bb6b30a5f5b5e08ea574655bd7756378e40a1778e3304ce124770fb28c23615f5296ebccc8474c272e652b9f0835b8269abcc87691c2efe6995380a113f5a378d7fad954dfdaf5ad802cbda31ddcc3dedd656d9f539df29cec10487aea09a932b49ed6a6139feb5f5db1ffc621896fff7ff4198ea6b829bd8ca7d1777d65c3ddc8e8e868e7a54383fbd72d268789c24fbaf8dce8365ccda204f009ec723cc2c6168a43e1c298be737eaac54f9d9355277f8b9fc8833ef631e034d889ce4071a6466745439e16da59e3375db935f8f4cf3391a6117b3ff310ab4496f5ebb50c8f46309cddbd3538bd1f44cd110443ae25b51290769a2fb1734a347d7dbb0d97258ab224c54292b23df879b3ffefba361a3c235b058b6477e5bc388376ff4339eea21bfd7d26a1096a326b44eb4fc42f621107551648b817e8358c70c20cc501918e6beafec8583acec02152bdcefb1c60526f40694a9bb6a265e01d84ec88c7b28301f153d2845cfcd7ed3cdaa1e5405095233d57ffe7b29bd5f0cc9175d8f00aba499ed023a354be33561532f513f159bf2f4866d6043ea86656ecd2b474f5b5d2bd47fc9081c2a27af3e4f4054a67f2acc7a30f9422c216eaa3f03f123e8cb8284772f01f1250df3faae54c9a3c13ce8d64aa2e168ab15d4b185ae161e0ed9fc7104e2cf63e9b59bccf9c72d0588b6d33bb615fb382a8277638382017d4b67c8f0041e0467f42b744b27322ad90a392897917e085af2937d48e3526b646b60510ad892658486b5b28f7770a02e868c813b5ed2081ff3040619c0a6469432c1c7c45ed9d4a19ca11e8045d4461938fb976eba497d9d62c12151833b790a99df73a9c28f2b7cf6222067b41eee085057b3e97e60fca702f9c022d4cd5e52049d2611ce4eb359c054494119bd86877250b3e9a90889ec924d45c9583ddb2a20704114b1cf791aaa9c3f05c4ee7b2067d5b54158b65095172a660c3511ce3537e8b80f0edaa4083db6f559dc2076072372a813529635b03837b78c74c2468552f8d4f2ffc650ea24663f9f4ada76422527404111b4a5a8084e7d37105b56c4ae02b5b75a6f99ed4aaf702a521e56bb5801183d8510af158e5238b01c38f5b7d0c69316d27594af076167b45641b4996c30a3efbffb260083d7f1788f9d4d1fabd547b43d2c67f94d0c70469d77b9556d20982192a12b152e8b750a614b94bbb04dd0fd4608ebd1329b854eeea46926df6a0dbb59409d10f66938f6a6e200e244a2ee16f4e17e694a107b3b486c2661e44dc21af2b5ff798dc14f953768b3738f02415cf8bae03485e0066f0f80ad10bf90fca6d4e035bdb655c447ac6072d9e8146a1f2c38a82a4ed3f8d704401b7ff41755bbd786f2e7d495495be663cd6828343de29bc985144b735b83cf2ebae40ee30dcb694ad0a3f3e327b6cfeb5a6cd24bf2ab268a1da5845e44efc794887ca3abed3e95b6efb55cd8254f36ff2ca10d6c867eee10ac73a389e91d8efd0d70cb8849b34fead239e208aa018bffd50e64849fa0562c16a87399b27dc2a14eb62f5c5ae33c6d9e3df39a39b1d309133fac3492439a6e03c0ace818c365ba9c74d34adb757440725934ba3100eac9d0dbcb62d79b26dfd0050eb6e6db289ded8833395f2ee664ec01b32a90c5fdf957c1b98386a400389d46059f14c3a9f4fd080a6d5d38d9f4db1e393c50b91ee345e9bf2c4a11c8d1abb06ad8f2dd24b7587f1ee9f744ddd9cba632ef2fc2dbe36380185699e3c81ac5d6faa403c8da910cdb9f4771948b1fba5f35ff39c7fa92dc49d94585287ede6945d96800c305c91f478a9cd49c758cfe8254238e4505c55b14d43be2131212ce9ef08d6c54e0952db38e0027b5667557efe99115494da11f396075ccf203b945592353ec5d9a070f7c8036958e7f978712cf1ba5863e162dcbc4f90a89939fac0f9e8134f7a1dca777c6c88d6da43a14cb3ac842bd5e5763db30ba395ecea09915837d42e983da6c29682ce3aca3056ce324a4bffd58210dca8ed53272d414445d95b1ea25743aea61f3f4a223683ee0e7eab27272157d315f619b3e891ba76ea068dd9b0dc811c9fe255fe6b4a92acf612908fee5c58a83b6e75a869d42287d8d0daaeb2d12f27de156dbeb8ff9222c67adad85391b310cd4aa1e7547791f58150fd698292183e315d821639ebc776b70824d1daa71828a5a40fa21d2129470b202e22d584aa1e2ed9abc82a7e0691b688c8dba4074f022dbbb743be97837f2eb98e6fede58774ad0b7db1c93001ee2acd19ff1e96dbf2ece26ea356984538e59baefc1c87e37cd55774621e8d1089ddf29e8c10ffbaad15aad66436348a1329031dacd4c662d3b24a63ca27fc94fd208c41d5f29a4410f3df7b6a44d73da2a67075221d2053ee020b3c20653baa543044d55837c7cb971b4d876690d299be92ccb69b3bfdb359858b34a691bf3a8d00572bc4ba9ebaaba8f316252f6cb4489dfbd62023165467434c08287453e39d60d904beea4fbe33883a83c505b4acd9f71b5a6285be52092b1dfe1306b7fb68e5e50c072e67b8949b03e0cac3315dbb01c402526550ccbedf293d7b8cf99fa84a560f5d8bb4840f6162c23fe967314f649233baf482624820884eb81ae2a5fd0b12a8144aa76a6aad238301365686cd1c4a33cc481d6f3f745b8a629c04fc2ab0b2b95d6339cb6503b4ee08e34955d933aa04a1097b9be276f4cd42fb8d044893565b4c2b46fb5283e9ecd5e4969069e2f2ed7c0e6952960e81a8d8b5042481e6ea7b2db069c16d90c6ed2fc588c43c3aaae01a555a0d41013f2fa8ed84744ab9441957bf093dc88eaa99af23eb28ecf377a57e810b7df212149bc4892ddb6df2b5bd16d48a003af9aca022ce94ab243080b9c94794302308a2658733914ffbbdac6c15703318f40d7fca77643a827d9077d6b14408613ca08ebeeac6da4644c7bc7124e1a32af1ce752d2e0c0d29196636e1c5137afca80abeabebd8a503bb4cfccb0f7b17b3a7a6b38fd2ce86f7207a822d53ab1efb451e2a8504deb55c126565eab34da8cf88f3d333d2f761f22d064c3d124d471deecebdfdddac1a47054d4c1baaeaa22a06ad247950524f54d8f8348090434cd9326e65764754040f52250afe18ce46807c8730047edab256256cbb175edb215ccd4957d1a62472939abfcd188a4445c556467dfeb92377d6637eac444bb358446dccd7550a76737cf9ca446a608213a31df649676fc2df0c66e2253366b836ef9986161bcaa9df0f20124d37102ea980be7d65c5af32b416bd335c09072d2a91fbb565367ce2ded5a4599e6ec05278835bfd9e226fcb571c7e0581dbb7c22c9c96238f44d21ecb517b8818ab0dad468b28ba7ecb7595f1c4e437c0f8b523a2714064f53aad66d017053ad8f3d60ada422cf297fda4780d865ccc85290eda18bb18ed35a2989a2283c8ec4876d78d3c25f6b8cd690ea1658d3fa5c0724cac8e81787f57144fc98935bd4e5695331555083a01a364722c8ccf70b5ae967af8a9abe63a65738676dd1bd8dd46b5449abde33d19d1b700e21a1cbd5030af14c7bda9f3579cb1d5ef73386a918c66ae9d10309dc61829c40159a9804f08c4080194b76b4643a89ce479ab0139e2836fba2a084cafaaa01bbd92c030ec0d21454f1cd260e033719005e4f6dc8f9c38ce586a7c92e734525d80fa04c5f7d616a000bc77f429d07a7d92e3ba316caa6c21632eded742ddb645976c8db368eef3f3543691d2a33c088e7b536e4f54e3e126372016384ceb271d8d6109a564b243c3056008394467a33e68a22f3cf54c7617a2385f769b2438259fbc6184c346b228f3c17d718b1476ea0e4b0354aca3e97b95b528865e2838b349a9954a34b8579f67b626a59a0004e980b8ee7a77be16102702b3f1266a7f4542c553df322e6e9eb645a4ace7fa9ea3a8bfbc3369b0437fd815c0acdd6241376321211e412c9b6e736ff74f5f4cbde09c76a5631d618526f2d6fe175133c621eea8c8da2282ca4ce6e36a3847214fa1216b25bf3c7b6c209f2e149a2934019c32b6a9c39b24020249ae440cba9424065b4ca3d77b56a82158b8f4bdb55bbbacf132707c44b8282aa90cdc6871d61f18ef0d99a1220ac2a3697028f9a3cff2c36bc5b017269affd6b2d8e5d6d04287a0c592ebad032b251fc5ce928eda9a1bed1cdb18d6a97281b0452dafb65e65374996a7289b2944dd36dbca93627fe61f6983e0a5a9e81393df6a11cc8c3e1153fcb81c09a02addf057cd6da0a82df58ae3b543250e93fad48ddb679aebac14fe86fb608535cea12f6768930cb8c7822e4fbfef334460f160a846885ec527325714dfcaf4cdec251edbc3fc7aea068b71016a84c65ceae81186f47b7946076089aa0bc378d36e64bbb44cfc4a492f0f0adcef5d71ba8024cf1833958666db38686ce921081d11a9fb863c69e47b8e0c39311ccb543388638b4785b65c09162d3e52fdd97bc252a1bb750c932da46a6a6fd47cea1e0426820a101f24b6741d86857826a632a605bd04c4914e4db646a32e4de4a33e672e9c0ef67941c215018606f6c1f1c2c8c8273055404982a10be36e8626a0226ab1aa6400b154080e176936d01f5576542893e74f02b71fe2702300fc4193192c302de6eb9d1c294e00c464d6a694885514af485e929cd2a03a4f1d71a6a3cc191d1c7fdacf9c97f9a772414ea2619a438abcf11f94b612d81171c6b87b53baa2427aca8a6bfb4e395f683d80af3b0d3a4889ed888a4c40f0e54096d93607878d799970f3af4743702b0a67b74dc00698f49272589dc78c2d5db9d1a801beae7918fdd4679f0044e963e02308d7b0c2372e8c063fe4fb4a27b92e97f4da0016bd885f26ca5344d3d38bf57414d132657f68ecefffc8c7f78c3b6f6fdcf1444d3cb0bc15d2d7ece359bee8a57b09da1899880ed5d21be37372d3ca60c317747d12b457a51399f8f4057f149bac2eab292bf5ef10e45eade451995e35d16333a558a2724742351f392dc693a25b327926d2120a97b202792f4b9400975ff6bc420d12441242ae7d3e69bf619f5a77473bc62aa4dbfd31f2143f09429326263c5f1cdeea488ab6ef5f708f4294c3649668fd245608bbec261eda32027f318fc89e1cb4d31d0c90f18a5bbc8256f5f299fde8c6fc04cc21d22eb4255565937154922ade4de6d60e05d157cd6a04f06e13884dc72ae1c77ae442484676cb132b118268bd1180943783cb9be3f9027f7b39dbfdbe9662c25c17f82a623066c7a1d44bb52a671e2b96be2c3437a3cfeeaa91845aa75d15a9f6a26e86dc2448f35ec18ea40f8fa83506b8d519c856526557ad2efe30f494b34c05140373a96987340f82fda514ca102ed714f0bdfc0ae102a11e576c9888bae46e6db6a2f65fae2d321162b4ab33907a4c7a243e0d09da28cbf96e9e639cb035df0af91338e291353f2ee8712e3cdb36c2497fa89b7100204f693dea1933774700de56e811dd74c5bd12b176f0ee6453a0123a447b2ef4072af1c8126d4336dbaa52747a0c01bc9971cd88b6f9198ac777e32024505c98e67a1e2792f8e6e4aea850a266b88f40392ae7ad562ebbe8dfc600ddb4bdb8ec2feb779f50709161c862705c62be179bdea43b29e8e25620b5206d0be5c7980c57d9c2fdd374ccd43a3f1192b7c4c967220ef0bc37d90bbd0a191c1bd8a1203cace86ab4c953e3180f615106f2b8dc2629e4d5e641d990ccc41f9282a665371302500751c89b25f84bf41d0597734b4666a4e200ddb4731c4a1c892e78ce2f6c0c8c74221313d90658cd5e2867943895818192a905a48c111442d6e7d2af49a7658320766da52131ffe8b6588d134a72710aa2387d3fce1b89e52012a2603f4e39438a8e695c896d331e8429bb1db7a1a3f8eacc874f45f30b0d98bd95b70ae6571b55c46da5f6e52e9b58abfb3ce788f92b7740ddef4d499425747a7f3cc7c92e55540eb83a3ac555353de4525a0433694af074950d7e76144c31ee8931d879dd4695ad1bd1322fa56057e6ab3df806ef1209f3bad9ae7c2b001be2062cf72e19dfc4c0bdb480ca578f36227e8000bf30ccc0f73ce488d2be7775855f11cc5fb73a40975abc85d02ee15cb276cd463b5f044e31ad63a4ac8d61e95215964edb819c2be383af298c0f3703756c4227e2e9351f9715d0efddafc72246b870faab63f937da53a0db0152e45a274c9e00d744746e5892a1bf95fa39ba4a92363c63098b9af808358ea6574c1df00206d2daf21721b096d6b81cac4cf51e3194255e2106e9bb67293b8891cc0ca38bc7113955b78e229f693679f3cd76a30626e2f7c7e298139338f985b641748e68115b0007067dc62ff7c059c0917e6d49c67262303ad9a95ba97a9bb1cbd3bfa8473a90b3dcb468cbc4acda24eeabcf9f37236ccaaf243c49888960131e59588d61dee0371ca56329b55a14ba666852045672ee99144393f1835738dcf3b7f3f1db4e5487e70bc301f152c75231b54e4f916ac8aa4fb02a30fcbb75af9d817cd465c306f0e743e20aa608fd70a69a0b268d3631757e61409977c4a6a02424ba11c2726fcdcc29ecc913837684011c69f64c060bc6da1c57814f602376a5bb0e75954d5a45f833867df84765c167bdeb7579c83ead7dddbb087144176738b5468271725d58f9db61a899162e8c6a0a322e33c3837868c820ed2499371bb8dea5c4de396904789ad45ccea020078de7f6a4e5e44ede28d6a874363c278f5a9761843e4f1e570c350aab054a91631733917e7eb72fe515e54a1717e33e5d5614f7df07838c78815b88e55ed2ccfbc0af18de4513b2c98cb3dbc49430e7756b02826b90de59e5b7d853e6e903b0f81b3d2c72c424de38169eba3122f547610d130642462b31b9856dc0236b6a0cc9716f633fd355ff6cad341b6a042bfca0d22336dad8583d4c15b44a8b73106c7755c3ee5756c154224b653324cefde35a1d4eade4da784be219dc806957bead4ec565fb6bdcec302ef9b50b3d9c9bc882bed9cdb2072ecc29c966e96346ea0babce7716916f6f4a1fdef05351a9c9d7a4a98033ffe2d8d20d9edecce428ba225dc815041640c2830212c9cb211279af0cb140206eee2c012eb8e68bcd37a3f99da7b9b041f2166cd8faebfa6e23bcdc5aca147163727e8df882e7e137d4cf428abef21a65a1f7170324f9e3a0d07cb88d22603ecad12d88745adecda90dcf0f467e33c6c61dbd208136ef0d5b84d544175deda20d2a1653ecc5d2d99a281e3dbff756be99a10eb43ed2a33bc739bd1f38e4359581ddcaa0495a7a7ce9b2b7715d399ba06ff840f7592bbac03c7d1799ced9605a14f922d7ec5f04516674ad6513631d9ab78a27d095515a3b34f1ffaca3029e181350a00fe67df9b075d18bb2a3e967be784e03194066999194f41adacd30d837b614644a62cd21a3940b6ec9fc44a3b5517ce52685175d8ac5404f0a22ac4bf08768e32ab09b9264197284c071ea5f2ed4d78e6ddab78f28881039452d12dcc87616023a38c657d2d2639e58b8fda8cc303b941acfe7e64e54154833cc6c3f6388aad82708bae3808ca9aeec8d7c5cb53801993f3240de982f2cd5d1a6989c70a5cf178aeae01f44ca850393e4d87ea7810bc8d18053ecd5e60d2d4f21e63a4aa45ae9ede4acbacdc301ad494dcb1eb6167b59c69d8e9caf4ab52c8e23330356f0250820e436b4346b3b09ef66b614f28192d23a030fa9a9f9e54e3f5767c7f8c7bfc61d94bba8383fbd81eff7cc955a3ded137b0ea2db07a21391631eef6156083a7b08f01a7101e2b217d57ff8b9170b69e40e942fa567a2bfb1caad20a7c72e39d183bfdcdb170099528a8e870c0fa46b14fc87152a6d2af50d297e9c998dd9daea3f2f2a500d0bfd2f60406a8e3a9c41fe566894e9b31c7d27a0a69510a6456fceedc29fe8e8758d20f66f15d68df7735eaf6103cb48c8de67b70b37ceedbb9765a33c47bebc9d0d48d89cb70389ca1bd09d42e6266bf257d6d031273828d9e6c75dcf1f2b76fc9f206a9363dc4c3cd281c04148013edd8f52fd1bb59ec06250d79bc9ae33638332214b6a7cc7eef623a22d23d23274683ce50672063bdfd197c6e0ba2bd8a316049a90a1780aa33502dd23b4dd49d053e7bf54e98ce0c3a0b680375bf04cb27280d31ac41206bda6e8ca9b4b03a623b75fa3b2c00b863bc058bb3b37fc6eb85f060517e7c4f6feed7dab42bcfb85ca7701b8c1dfbf6755374f01611189bd45075f16b816761552499bc1063fb6d5996860efbf1c3ab675d62456899d68f6e5ac0a462b1746c95a84d9fa8c3a5a27883b328cb2d277b2748cc67cb2ad1f87827bc068e94728822892cf021d2c94552970c7751f981b4283cdbbd7c8e6fe4afae02eacea12d0496e58ee7c813e9e4ec18d2f78d521b6f5897eda06f17947d277e05aa421fa86fe5e7599bc75e828e459d3a3ad2ac2b0eca4a7ca91983a414cf38281614fb9569a089f16b564a6566306a67b6ccced07075268e3d0dd97e56c27f62554d676a015a9d61c5d42a72fb8c47b6a46d21fa7aa9d2ffdd0817eec5fea9f67fc88184b6b25867df574a192cfa0a6eeda0cd0d2342c3791928a13b75930dbafd7f82d974478afac5f286e30a2be6da594921e507e94466db93b2098c97dbd8b8e9e3170f2eb410c53fcb75dd4d3ace9dc0cb16e5c8a9411fdaca2eda0d4e7bd7c2c84b8af24513aa2c9a0015ba20a346d7246a742ca4aeeb38ebbdf5bb514c672e4595d9110bcc3e0172ea3879ee1925b8a89ab8d65f1404dc7995292288b4be090498df704efff4ca1f97b0ab4e6ee6ec8269e3d5a3a2ccea84950fbf95946a733389e170a853dd516bd556a1824659c01e2030c26cd830ca09dff344d5a65682dcd8b69e1db4c9608c11766b2677fa80bc50971ae5c0ed0a0f0f0c2e6f2df8931edc01baf9737e5cd847705cfd71f050dcdf0c2a791505fc208e32cfdbefbb093ac4f0a735e40b9f42b015ad245646fec69ffe10386a79f4a91570fdd365d5fa7d7ae187675c998aa30ff4422f824d173690ca1e4a2bb8baf2d0ac27bf2a475676f344163321b95984ef95694f84a4322b705bbf040a38936d8eeb59f214eb85c0be83241f1506d614c1714abac46f18bacce6a6b77ba55e13923f3af462e21e876a62399421b12514a6a26332492219eb7793a2c7177e9429cbbf3491557b5ea7d2fb6f3b10944b81e73a5e6dfbf27461a956a5984ff2b63df694479aa738260237a51fb4edd137dd50bc8cec536303af43c80af51b7bb1b7afab675e2fdc6ed0fd1bc389c5782cc6188734f87da89a9a53d94d27707136d474dee702272c2448159248a7b4b9d90fdefcb2c76b96532b8236f0efc568ace3d68bc4999b0eb69f41417504d7550f9494994bd81fcbdf6fa43144268d69d86194fb07ed1c8a28ad07524721f078011a628b6a5bd62cfcda9bb4d481dd7fff92d2a71b3fe07bad7f4e39658de45f2ed708f7c9752a28f7f7ab7ee312769d58c64145f148dbe93dd05f5fc94fdef645ba784736fafee21c3c6a72bc358abbe5d7a18ff4468936930ae9b685b71bbe6ff3b93cd99e5c5920a5c5da876db32aed369f51ac746df7fa118f3db843802d849dc274c5cc87aeef26d7c1d8a146e714c7e01a08fd144457c56e33ab244ec52dbe293e4eb38e24e8b10a59ac8788248c37bd2351444f7152a43552602b4f45ce15bc28bd0ce67f496e58e19b4178caf562906dcaa7ebabed21f1c17d86c128fc8ab1d26bde812e911bba388c69d9086a54300c3f90c72c80ae73c31e3513ac69afd5a00bb37cbd29dd210eaf07b30795f289dd28fbac920f0f6ea05b837a73aa96a02badc2152651083bc32c009b44b2047dc1b4deac5829f108c016a9587b7f0be19c23d8b4e26495fe12b508e69a747f160f3f883a8402a9def8c7953c7e113346f7c413427b20352fd7487a06a7fe53bf89b6396223915e5819ab82a2767c092ba7633e353f10b518cb85defe5e4d14d3e8f8738ad4d27b2d70a7f39b4178c7ea542c4d31bf3913100ef2c7b95fcf22e43997e34e00a4203cb097a219d9ab5e318dc67e324805fb3877685b343a9060a2735f2addfc1e17e21509fde3023778b6842675cf75f8f09db2623ececfa30a16499cd5bd06d60549089a884bc371cb29122fbccc9b58f4aff9630d86414c4fc21edc8cdbcc8ab5f40a17a0f6b929f53d46213bf6898e638be9fbe18dd1f02fec813b6bbdfe81f5fbed787bdf1d078645031425e0fd8db9016e1daade9758b04f8e853698da1eb66f3696ecdc1f66d6d0b238950a10b9798be5b76139848cb5c5a6361f4a6c08c558efbbb0490a59e1d9e3b2e0604b3ba13fbd758ab0412f2f4a149be167ea9647d13365224166f7f5ce8b71535056ca7347b7c55bcc7501e233d7207920cca32ba539f76ce70a0b1049c2fddc5962331aaf723048a8538e38944307bee78d14e1c6057775ab445b9bc23c05f35af35abe47ec0a30cd032e4e05442c5d9c905f306164c41b628a3954efe22d522aac4287bd530040facc326234b5281a6061619a82d1d3f1bff4e964b1edc04baea513295f829336281b5b60f527171a1bb6b0199417d8c08ec5cacd18d1f023c06c1e2ce4730a6d41130bc2b58e9d563bf9c3023f5895f7893a54e275ec3377299e1e01e3c81089bcaff9eeaffd8e663ab137b86e78aa8a632bd7cb2fd0efac1324a330edbf2bc2248ef0c0986d926bc760409418494f21900a92461aa267231ee0cbbcc4fd95787b16d877c60dab338b3d774b5fb99f133ad09a7ab2d13ceafdb2e1a653bb846ee2b8c8a972a2ecebd63be5c319a87eff6469558d41654baf34c2bd5e3ffc4db69706e7beb1d49bee0e774e7ca3adb794feb620877edd6ceb65d6fb6f2da1694beed4645fdd506081c0ded287833ca1f39bf8de8531546a137ce367fbd2670a68a90eb9f78e7cf85c805fb26f8a860c283fe62d7807aa810ab579cc18192b77c598edb1e984ecc4e42097efa3949cbfc2e3775cceed48e2ef243424bd89fe41d2af98a6d4f11cad68503aafc436b0d4df6f6f4e257eef55eceddbfc2b592376900a2b47241b7e6ed95a64fccb0b3e469b0a7dcfff76ad51a790f2c8c074c620507882492f2be3d67a1a75659dd0f95d0b5ff1e7e96d50bc8f532fdca80e76cfacb8cc82e232e25e496b774527991ce4f069fdf00ba3dd5b0626ee6793d787234cfe22077d0726d11822e17f65f64b2047fec5e9a46d91900effaa7ab56c294c1b411b69c5577023121fb05e35de2a64870d610135fda474cb3d3dd704bc07db8ffcff1c23ed87f11d2474421a5b48b5f903cda5c06abc09a2c4cd27db94ff36d9b47d53de8279cd6afc178e07609190c8eb4e0d39090693c671ea940aaf873af96d08752883aa1449c86608f05a8d5434582d37dff0e110204b4a25680fbd095308f329011a7629d682b08947afc5860e144125031f3edfd88c6c4aa13fd4ff1747fd59e865d608ed0898d300d76f0fef230bfda38e4040194a0e9ea2211e10d06f20cfa603cb945620a0a16d02e5e6b0a80d48980b30b6e4da40a794632ea28537577ba662bf858741391c55a5c0850e0dca4203da6a629b1487a5d4c00afa65ae29636c9af121f67c7691370031b52dcff7c0344388e2dc35b04b5820ed7468c5636d57f4f82df8f98d3cfebf5fada642e0d5e722c3b655442249cc989224c25e8e9a7dc608bbf2fd6cdc69019165d29f6cb731897e26bda6ede995219bd8a2fead9cff3f021d5731e46c8b981a031d610a708a2c31e0ff5081b5900169d8cff44a24f8b04b0f1e317416bac87e86a349d0032eb0832b0c11e6bb2a272939422fe234414264fe685387ac26f81ef0c3da377a02a8ed2be9c9aea092302df8eb7a0c0b4d0e78f4d060c51e7309e4140b95c2e0c15a4b54125708b3c70e72aafcf8a9a6bd15d4f7285f7efc2cbc1fe4b5dbc900a2021f4bf82790325a37325d89f0f1faab52e1662617d919c1e0f226fbab30236426d391594b7c6f10d0b419803d25ec5f91178a4cf88a320e09f86c224455b9a298d13a71dc0cc26b76149e8f186881f2245b4852a8c1ffd1d7f039f435fc173d3dbe435f47f87058581067d0a502306f5fdef805b1dca3d4583655122d20108359b19465cf47220dd431b71b9320c532e0f826955196cd4c3801bf7b593e43ca617972cbe400959ff01e66754fa807947bed1ff2caba0cdd69a7b08d788d81ac6fec0592e8c530a3b8d78730924e3b187a70d17f8c8432440e87debfd9abca7c07907a887fb20e3298232a4d38c28dd439e15316621377cd19e241819cd40e96637c93ae850ca4c160f91b1350a8aef2fee0cf308093b8e1899b3820696086fe10a2e60921b241317bee457f10d33b8762a9190b225667bd29549b8e82b504e081aeab934eedd92adec628fb13a63bc2a183bec75cc31fd7748c7e993b0878364256412a6231e92fd5d0f2b98491a2086f6ecb2a10b12f94d519483a1987ad61b7e013410de4eadd88b2b2ae3162b5f8507a56937a0373c269844d75220a152f0368d189bce832ce893327271096c3839e56e306d05c3678f6a42ca863987f9d8b4657cc54e109d93f424f04a5f7bb331ccd30bcb6388b476b3b12d635a570d8aee1dfd9cb2affb4b081587a7c4a536f688e1716893e6fd448151ca1e6a0814a28751d0cefde9c407bd26b429837b591a4b8f107d56ba1764518d3fedb560135459920229ce67c6b9cd6ac946882fd717e33e94486536d89cdcf265fd489d8421e739132454226a89604980f567ed7261bbf4072bdbe337d653f90360394627d330f91d6d8c85fa661ebc561e9dfeacb87dd880aaa83a719511b1cf693112f324bdcb01eb05b454509dfbd0452d8be7ca7d68911f67eee420ae47f38b413612370c3cd9f34a133086be2af8c3d71c132418045208a4c585aa792ecd1fdc8abca364fae5afe2f59e3cd52cc018fe2a8e9f0c28609534e2aa19fe39a71865d333554188be5c2ea2cf0e1b2c2aadbb2c1fca212a6278feaf410624128b7dc157735cfb1c414eaec523ccd658f57a0f6def4b99f55b37f3d2719de242d94c143c40ba63548361de5ad51ed424bfb2f4386068e9d24462bd3aa1690990cb6732d6430a27b22f4485f22b20f9821c316ed0762dcf1c323eb6ddeb0eae1ece35bc5baa3391494842058b106f85ffa5e6dd96823757f53eacabb462488dcfbd5c46ab7c7dd9b180039677c0076698b04a4f375221d2b48869b80be7d0c06c643900200a793e66fb9a8e6a53c60b1ba8de94ddd4b8aa15ed1b3896eeee2bc930938f9f0f405421937208b4cf08c629deea0aff0fdadf4c85cfd2e154a4b783be7268cd9d9ca1631a9c7eda2b1512772b036eb05f2d9fd99274128ab3d87de7354fb92098109a0ee74a396213361fdcd9515124c0b553e708f16012e69d1a585f951fda80cef7391e0d99e5c9908c962711eea181ced428c4ee0b7cf6d7ce8b10486935bab3e60fb07e1cad0ad82ada22e5c878f83ce5efe1c00e26c31aea221e5a57f914dd3b57ff8dc809f55ca287a01060223ab93452f70e536bdb5671525179f70cbf400dd67154a2b4931abb070bf372dcfff90156cef77376ae2784d8e9f85dd330d1eecf0bbb279982a0bca3e573a80a982ee52e500d9182b962faf4703f37b9664fe00f4a332563fb87cf5d9ae648eff8388e988f3755e6ba46c2620d5e954a0abccf3a552a4d632a811478c3f5d34c0694d93c2289aabedfa4087da074fdabf650ad447eaedb28e3acea0cc97b062031aef0c0085644de6b782940bca47b08a82de970a40acb6e4d5a29eef9c10201409b9f9271b3ccc273ecc7f099a6f01201abd56aebd20f82c8ea9026fe34ec0a038e0b70e9d001a11623700d540cfd2147dfa4841a320ef2c6115f97b78a85c488e3098b398b43f52fa27074ed5c5393fa5ccae4b7df8ee218441c437bd2bea246961e11e9d13a74e0959fd5be658a48efdbf4b1cb5ffe1ce0b6d26d28fa8b5126a8a024c356378a901af2b11868646fad8ff02acc169e89d06cc0ad11d61a45633e38e6c743ca3ca4ec77331c1b82adbf529108020ef93a589cec1b554925ec6f0be2e6f92baaf5fcc46a0b0dbda7f5b1d78e3699296410a8323f09a81c2fc6a23a1c0487d8679f0cc81a9bec089177aeee46ed5a886ceafa651bc05493350bebf22da107c2d0451ed33ef6a570dc84fea69ce74a42225374e2d07ab94808d292173203a218455c2daa2d99be4e4954e01bba1251e0a6aa782c24783613657098e921ff135029fd246fd1b4ee511e5dcce589c30bddeda56f173c0ff5a8e6d259e5602ca578555840c9e68bfd5b17746577552b52c13abf69816345b10a2820e132c7019e9600990e0e8cd7895c5a3c8756e7be3a4b52ebc34d7190faddf388bf3e4eefc5c18804e94f8c342817a5fe4f3948621aa95dd213e809c1f82893d08b024740bdade47fec9a2a02974d1f430d5f96202d33b0b820cac42f492b34daa746e56c22bc847a8e480599487bace9076162bfc3d184f46071773c579071ae49cc943b31463ec90b4803e816194b3c2d345e785f088f2e4ce402970f2dc52fd33e28c9a3538af6ccd69726c7f5c063e39f9661f9d60b361f6ec60f00f59e624871fb3f7a1c3aef6cbc516270d538a4f2b7869cef6047a387e5cf0480579e23534301ff5214ed5002c3e787ec336b966bea08841c16705024aa275463411829f9a315a07e615542635ef8dad4160b5e2885a0e6e5abb9634d98d26cd84a0d9b9d3cbb123e1fb2bec854533a79946818a85aa0a36926e6efc8acdbc2f7d5042e1ba83c42baa6c4cb3aea2fc8991c42971c74a72b0c6f7500cbecea02389d5a55007e35a182af72aa476863be75a00b4c04f8ea110a521e82ca642e9b2d958a8105333eac92d740407a04043e58843aa13bb885c8624d3f665e59836da311e5b960c3b0ce6a67e629c3be4127d9673b0a36b89f222544ab6d50b67da34a4da189bbb98cf5fb22b837e6e7aa400f323a889712121f2b1a38b2f8781c9596fb47070e29c960da8ea999ab98553ddd1a468a6c6de6ffb6417fb3256f423a6bd9d9d43c0ead745207456e545047aca0b4a11c04f58a465f56c43a082e50085c2eda93e4cbeaf6aca99d91502061f5aee7c6199d88eb87704e19b2b4c0316deeee4e565f4261e341b882adfa8f00fff3e11d954036a854014e35e27f6e0471cb22e8076db20253d6c8d839080516858a181384a8cf025435c38ef961f9f444ce42cafde0a92d3fb43cde49f91e797a9fea8b9be7c03333cc1fba0dcc7c53c6ee8bc8c222973061bd0e5fbd5b66604d4bcde8223cf89daeda420a91dc576958ecf85632eb2b09e48b856d63104dcac7d04b0078d2e0352f7d44e4e153740f0fee11f429dbbbf2b043ee7589ecaac672b998eb4ccef975328557a078c57771e4c4bc041a8fdbf50b3fc36fd70ffc309a288f2415270c72e4ae8744734dd92089bb2df03831acd4463a30f9cbd298b3f26b4ada316c42583f5e0945e910304c11c5e5fe25ddc0689a989c30ae7062b438e79af7fb8b3ca1bfc5d93b392596ed2b4608237213400b2718e5c5b81832229248ce16805b9fc0746ad90b89c0e144776884a88600bfa2042a090a58fd7aee32785390eefb88840589bf008faf70db8a535b5e33a4582dbca119ea2281bda116f7c77d205d1e225615bfe0a2c0e1a900fb064befb3a2e37393474f9cd7552c433572ffc3c2727fb6752c2e2ac211a3a6f9895e5f3259f8522fab96c48e2e12c30509b1e684ada48ed40cc12acf374979981c63df9a2c998e0f2c6c12988707770702e716c0e233817fd0e25605778a8a53598660d926b1f70480dddad671000c4815102ed5df0068059f2c8a830d3655a8801ccc22b9690457504b57666ba608d1a6572f1ff1d5d49d8b9d76f94f8f4606d6cef7e8119640053cf322a421e26b4cb693266e93648051e8be52adc79b15c51a374cfca41db8468ba5821d114e915f09f24756144bd91807dc52fdd1c0138ff6464c3110eb937b1e13c60fc4eb4cbdad1f5ccb2544653d15ff4af89921ea0746d75fd3298f3df742e30732788a606b55e7ce276cdea542f030aa8e49b608e93de71ffe864808906787da745da7f247807d2bcef7c673a6980ab5d3d46ad9637d7ecaaae386594ab28e927d419680e242081a880e2170ad92646a6f3b1a06bec030b671fb6c11d1efa331d66198cc033083436187027b8f70a388fd744f217c6665349efc467de9a7e2a8f077febef795c4baf88470fb92f8e243e9fe5186f9931394fcc832aa458368820c730c16d30b4a4ce47aba38e66c2b52bef588c9e0aa28e9c921d1c417661632d9c3bed932a93730e4f5e5cdaf04577026ba5ec964cd03c66e8e2c057c3384adc05da94cc693d8761e60d27bb06d208c7c6b9663a2ffee2516b774fcc7a0f69143818703c12e10745ec16948074c22782fbb3b015f9cf0ef91576179150e500f79d89f69236547667608e4ce111356d02999439da57d171a285ac7c563ee52a3295113d2f93dbe56f33658d71540533a280764dc36487d2ee305bd923e0aeac916910b8b7af093fa6a761d74b55092c9e9a6d42de26f330e963a830c9d43c5ba881e260f1fcd7007e89826cbadb1ef0da2fc0e5efd7854ec2aaca35f91b7acf0538c4b55458f99c6f88f0fa4f317b2c003a18f3b949eec77a230aad8c0229b1804a01a26f017b689a6aca9e813daaa6d1756075b4b510703ac26e2f0c2d93535cdc52f8e72feaba8dde4c538cf7cb56d8ac363a217159d537414463671eaec0c10fee6af2b74a23cb7ad078fa98a1462b0bf8fb136ecee600c36afbe1740a4e35d70ec560adc6acd4ee5bde5c7cfb5ca3928de9b36d3d04bc42687c32b058f9a932248a310ac5a5833801410465e6fd9473338101c62adfede1342c81ae0557570b6c6a45707754104d178cff6ac551f147a907dc2dcf6b58b114692ae120835e134550d02bbbb68d5a5c4427b20b08b2bef55be100a185dea9b03da8d054ae24d217201a11fea3dd368d23fa9c206421919023c2d8fb2c3bf7ff48698f1c8eeb530b067bc65c61bd081d92e1c9afe247bbcd9d2e296207ab90c158c5d35b08f4f82058c0d046159b34c15e1a4178bce128810beb8d9812458a2d3c2ddf86ae7850a067a4fa8e45073f5387267c2c1fd4e39f484810b6001d3963954bdff2499a46b48442ee9fa1c8003aa79974a4741829a42b0273b1b58fe36d7591e89c1222d742755622fac88b8b81ce336486f9c8f717b40ba2718ac54dbd56005626a90cbb4218cbd2c00fe16eec0e31f4721085655fd4cb9f47e9e87863bec6601083cb06ff3ca4806b4b227903ed1a46dd23b5b83d122e7bd8acedf192c6ce28487962033bad442c9cdb51166bd7ea5dc9ebf9f7adc54cd5a30972f908de2a89f60676ff6136a89541ea14ba307cf7058326606f69907a540253aa1a30ff12a71a22546ca0266c1859f685ea77e84a57c0216a8b0eac7b7c68e094b1c80a0f9b87aea5c9ada889e2788c9d507eb9437bccfea8031fc9d5b6be17e78e51ac70665892b95eaff553eb4faa0368f57ad94cba86242891205defc670d937d9824619b3a7e498e62b4996682fbe87b1aaeaa1acb8ba5d665004a34a7e2febbe17dcd51e433a3cfbbb2b2007c4be98b8e57b3394cd379961af51c6c0131e94d4dfc2ac41c21bce225efc727687131923751272b8bb90a1a9890b2dd08591b13de361c9f33d3b4eeb00ad5491f7d3573acf84318874e54ba49fd04ea13fc2d2dd2af284431a97d1b526377bd02c7caddd9cabf54bcbf78688d2ebdcd1ae6d0d0bf1c23edd734dcda223ff335ba9d192624067bb47ed7b179658485b8788bbadc82c45014782e9e0d3d47dd4a1e13cbeaa0168b67d72560617adfca2fd201d6f44534c6dd77c901abd04162cf558fa0f5caa724e90c4bad67d94520347ac6bb0da033fe513ee572a6e9200f33b3f1951e26b241f18b07d441489e14be3a8abf438ff2abeff2d36aaba3c9e628c1261b4431ce02517f946fa6c50500c70aabff92bd2585f0b4f05dabd2f96e2eec7f4a332ae4f6f0e29f5fd3be84e62771609ad1d2bd68c2ff0935c0a56394eeb95505e8fb79655171f36b60b779635c5e9b40340630b94587cc372ae28a6c41f7a7b67c6a46bb0cdda3936ffd3a47991882f89ec9737561f1d54ed6c890a7e7662549fda29c3d5b14a05d9f4a140ed9c4d728a993147ea4ed1ce828e4e3be139d98a34a28f17eda482d59a52913d7c7a33b4d39943503ea51737218136477568f1b602ca6a9dd4873439f75c094c31d08ad3881ab220a000f8b2b622266604ca74fe609b3ea6d9859fd2e1623c6b28de02e7d54975419f622f08ca7724ace9da17c302252c3f65f4a50993eff4492a7fb0ec0cbecb25c9615c1373eb8fa17885ef3f11263a51677abb4d052e809f0d72b5e1fbb9f7e0e5cc21f04e696a78ce21969b330f3b2f9fd580cc9a04ede29dd0f4ab523b5fe7365400088dc8922f09592cd1cd5ae658f61d2fa706d69a81c81b9156072f9823a9ca1b3c5cba19c57b10fb13607e48280d36e933b0e083857f44fb0a4e47edb06cc9526e2a7e9bacdb2db976aa0b1206f9f39ba87457b4ccbfec5856204a1bcaff70979ad2a3aef6f17fa5ce1610cddc9f7036a57d10bad639a16071c00c61b42c0e979f4be057b2ee0f718fb5062f592d14d1b0506a024b318a753f88c99708a2055866cdaeacd025c74d2d58ccceba811b7273e7dc38677fa02e53472114ea95e4dc4226d013551995b7149f883f5760d913d3a837a0056d31e813dea8903ce4e8e1f82d282bcee09c89f46d0210b4bbc20a1dd00a24743b0edd5640f36b915ee03b8f311ae3b3f8078a5723221187e14f8a2c1e659e89f1d7b3369b2eb054ecc18c53e50a4b9fc5ac769936e5096f00bdfbff87d7b50712932070902183e8302146fe639ca84b29628e18812ffec5868725c3474babb789d52603d32d724a074ae6ac4d88ea87f63dec4b704a05d23402ff74603e32d191a73500951041f9197c74d21c862f82b6e8b6cb9b5b0c1c8d1b0faa5607634248d637505f73cda3f4488eae06b8fa8c67c883d6e10e4405cbbcfbd87cc44f8d1d7d061e61eeafdd73bb3344bf6832552b6c038f60c343282686021e3e176cfc93e460a9975de0965a723efab2fa5c641848df36061559d3c55f73cb0c7a45783c9fde4fe94185d867c911ab913a8d67d3812aa32acc6d9578323f949c64a9a361a36eb0559959705f65615c57fb87b18d02a832a981ec400942eaa659a8dfcf3532a85a829107c32d38e38c36c5452fbac0718bc2e46948668422b6adba125cbb8190a233214c4bf0e7f254f8e4e31511dabfdadace19d9c64346a52d6a49eed5032933b0de74021dc9eda2dd370289353b16657245f35aaf93d87ab0f2b46a64ad5ccc02e62015413c6d46b73e07501e5f61ab295f3ad4a5fd612860db7e31eeaef19edc66cdea8a19e8ae369ace3f01e928d4019a63474cd7dc93e1ab2de4cb7dab1178e4ab1b124f3be299477ad63b82d161bc9556ef145135219b5e736d2965e50724d97f3ca20c36f8e44e630115cef1a2e0d7efe05e4e464488697538d04530a4f8a878c70b4a5da497dd6ae8693cc999f457e00030bad4c1fff7b34ca9e7665f3915cbe6513c64f77d822c57281590b502f5eef9b12033a640d16f7d8f282b8b319ee66928f4c6965b534d7c276ce95a191e54621c7ca167aedb855ad2d0d3b262b1820feacb4f79e45cc1262a6a4ccfa067c087df3f90e56a6a0b9bf031beefb9399b0f3ab2cd597dca71860753250a4fe1fc8dde4b1c04cf767d7a2ec2c0e0829f58c3a85b0d3bb8c5212dcd9c0405543d0ca76ceb8d1509168db67ba1946e15f5ef19b498bb8868342b7cf4e26e4bee46a88b2a0cc4a6e349114888047c3815d110fb2454673612c7cf4bb7c98748579e05cb7b5cafa5171611cde1e464dcb74f620f11374f14cfaa31147a0aa2e82b172972ce70227cdbd596448c50ef77ef84ba698d1cd766945a80a252eafae37c7d1c4a51fc80ce8942e4d94f5f807e8c30ad5334e2ccf38380d20ce18870834e37753df601085fc01f5c6b15acde542a2c729cf19063fabfac6f3490bce3234bba5d9393cf0a2b0bee2f2d6d4a023728154d09b62cb8e18daead3eff64514ef048aecb8de361f23110cfd9500e4c7d1cb4ca1976b11c3a7fffe79c984a343d657a6a354006f085f7317d7f2f5ce7f8298409dffe188b9dc0b9509104d96dbd079171517f17269295a19e3b32e4b581c9d1a807b13d711df60f5642b0e496f44c4482730bfbfa8cae3211d7a907ac04b5f27572c1e08b8a05757f6321de5f39a2cd3f3a3aa17d0a03b0d85c69084c471655323e4e475ca1c6b96b601eb3d4e03aea86bd8425b9468ced4ade51c3c2cd8981f11deb4823f15eb74365573c21937a6c7a748c97c063c4cda8cf912b8aa074d8a8629ec004534d68784184872fe747179fbea71823d8a10c6f2f41907c09f6b8f1bed2f90de680a8f12c901b20af473b9c0150ceed18b9211dd9b772f3efaa9a0783d8c4af0a37715352f14fd528d1afef63d940ce84f968c8c4c63f2592273fff1f8aecdbfc8f1cb114b0781276eee4a76cc08059f3373a29e77f7743b1d4f13e66a217ececedf61e16693b26931a2970a0427e9ce1461c2d9c5b298d4a8ea53707891dfa3ab186445ed3e595b8a94e21b49d00480edf750140b076711131834d92d9388278d668699603b46eb5f17c623668e0c61380006e9566f6ec405985f314cfdbdca6e194341f6fc3c483e349242252381d717fe0387bf09847479d667eecde2d50ca2273d5eb59403b925c693c0c7bc80e5c9c394be41629386c61b680db0aaba7a3e8caf4ff25b312303ad3451664d9ba8be3eac89507f56a6fe004cc2cd6d50f61e1ae87e4181200216e6bee658e70a4e1dfa6d4812d43ddc9e6b6173924ac6d030390ba0d8c164f25b0d71f81eb257acbdfd2868a6bc48f2e9b552d523c94aa6fbe30a85e78159c112f579a2bb28fccd8d51ed414b678cf5fd9df9311d6128256eecd0024226a05e1b8fcf84f8e51b278421754ef88f2e7c9990c3d98026147c1d8a5164c1b9b0ae52f62192c3d75253025e9885d1ad2fcf43ff6a268d7926d4da9d3713346003d8c15055c1ac328e7b716bfd8c2cc082dfb400e2819691f6b8348a1646272e4da4aa8dd950899607faeef60f81991747a3aeb8048e61ee30098a96f731e8944841c03896b988b229f70514e3bb44fa23504afc2e79d394c9977d24512cfc07d527c6dc73b4942d6406c201d20b1932cab90fa31e9d8d679ae7b4184928ef0e456b598007a6828434b2cebd7edfa6b76ad355570363e87340fdf97391898d4fbf93f1acd110db8893b515e70bc86a051ec9e38c0ea5902193a4dd92897cedd51edbd34bd623ebe419242637b8eac9221577926b3ad02d3c17ec269991dc558fec89f5b5838eeb5997d7663b1047c28f08e0db999ad0df2483db3226341a0ae23a04d29434f938baa97bd9bdc08b082a17d9911fb431ee574cc921d34866665a51b41a763b4503f1e5546b8766c6aed405d99e26bb2985d0bd17857b824ab30d76f33e6db404302c52def2da7e3f5d0259f35ed9beeb058ef1f9232e7c37eba758f3991f61f7d890ac8b1207e18324e4369875912f5badb1800e4bb238b33e856acad2bb785e80e06d3850862e2c97a797ecae4a8b1208a5a7fceb821a117fbba2315a96e336b6233335919b36474e904251dfd9cb44a2382892e43af3ab0b6aaf90d88a7cc99019a529d4d5440d00cab0ee6a0eeaaba6ceefc6c5e51d58586b5f542ac250a7bbe84ba342c6bd5e551c42f3d6264422d3f00ad7d48253d3394fe0c18733f5950a96fde678085667db7e1db8ede96cd91eb4ed7df769272efbd77a404cd04d5044bc118a0e438a3384e23edd25344d5b47c182d21b5254fae9bb446aa6348e9bd39e79c0fe5f87cf40f1d79ca0a4fba39e779664b6c0d4857ca49cc1b9e547be2519465bc9987841e06b1321740f8f4947ee99996a992b1815b6ae7729e5d546178226c6f478dd719bb1ff96b72586e8ba73a2884672e4c4ca21d0b08168d0d8bfb84ea43584f44426ad0a459235b1a309249cdef99667b6e255578332a5d1830829e9915b7451aaab8bb36313e930775896e429145cbcb23aa8cfb707a616240705c323834f289f4e1a3eecb8b3afaa159191afb806dcd1846834392cd96cdd0923478a9b7b3f20145b2477594117a56edb8e0b0743db331382642233ee49ce6fc9d29facd39e7fc4ae9f3d18fc539e79c73ce39e79cf3990833a1386f64508ccaabaa2e58ca564ae8aeaaa8096864e62d3d994656b2b7116757126fea9a735641e4fbfe34c5ffff635139f7d1586bf4b34ccfba43c7a372287c092d288432092fc7d27a30e1dc3bf47f28cef17eacb8febed9dd79618361d21e5c17c6439363e92bcd29134268cd494583727acf1d9e698a9883cfeeb6fd3680dfbdf7ae116695335e09c4f45fbc6234782c2458212c2b1a1b167787d654efdea148bdbbf72d279916840f97aad19377eff46ccf36adc104d921b1371072405b24501a19eed0299588a843ca3024fd6178722839d56d8b76544696de292b249b2349d24a18b4cbbc418085304ca9604ebaa7053ea40fe943fa8e748e2fe80967e3143a5ef2d9c0df1cac406d03e50e1678ef1abebf96831d381d34a1be00b0bee850db6f7ff00275af31de570420ed7b4590ed7ec17db02c42d374b0bef74cb890b8e8f0de12b5fd063530cfb6048604c6868484dde1bd253032fc6c1648d8ddfe49c0c18b5f0ddb408edb77d1020bf8e50002df1abe900052a8255eba7e8314b67cb181805bae4071e1010eec0a3b0e8280038c57b635dc16ec39d87b6dc1ee17b7053bf8af7d5bb09f03fb820d9c6b090e302e4cd8c099db133670e2a2c3840d3461c2a66da0ecebf0de1336f0a781f79ec020c008e17cff807270031318166cda06d63db881098cdb820db47d768709db277151e17d45906d0a37c74de1a20508b825306698608db171f1c3d8f0d1af081716f3c50d648e0a718f7bdf587eed9f2ddb25c824f86c79a3849b828410e29f8fa6e3130ec8184f4311a7086984b1fdcef48b857599e319a3d6d65aac333ec13a8573c7d050d759adabf5966d27b7f1a0ce5ad4591b215d0bb14af0f0c6786bc081d879e6efed9c678cb7e59e31d7f74cc7f008b1cd2f3ac6060cbcde330c5fcc62f0083346acc4682e6fd9de105200c56005c0b66c0b4f10834798e51b6c70dcbc4618db33edec305597ff9afb318437fd18ef5cf240fef08a70036ec6d56ab3daacfeb01a73e05ad62bac8d39fc36e60073fb0d02ffebac0cffe2e111a93c22956fb564feafb7afd27ca24e559543210cff1375aa5a676bed7fd3596d2e3dd5d91a7bcff1f05dee0db8597dfa9f6eca4d8b3a5bcd01be028087c29b9e4fde1b063e8b70038e95f03156c2c758091f49842ca06d0e8c9531875fc61c7ccd9843bd1473003ae3a7980395e3ed3708f43d8d0114fbc1c50bb826a71873986b72de999fbcf7cc81da1461b7e19c98c306cc4984f011092be9461490b8322dc5d3e2e3ff7fd88d33de8e38e3254183641867bcf786d0c2e5feecff73695f73722421634cd28c3921246ec4bbf7a48fa01e6d08895259b0e928d7c90f2c704d89514cd22ceecc5dd2da5e4b938f5b895645760abcbb4a6c18dec74f52093ba5b161719b6c4945e064beee44c2863460b2ceafd6ff196806a1e9d641620625b97a3ad9a2beacacce8ea476d80eb05a90b56c19c88748d501664c789c82f448408c311591897838396dd56481a03a5276d31477d55121068286d5c343a70a262a86b743a7a3d2ccd919ba3f4c0a648ab2c8c176a0b3ed1f00e3d6a5247d41bb314271a3b45e4747c52b13ef9c73ce79c5cd39e7fc86e36e39e79c1b0597440a34cef37e2053ce51d8e52bcad163cf51f1c95112d967db8dc9bbf7a4b968bbfcfa91503c65b95f53cf235a5171ba56b3b42c191f281ea75c5c0c589536a1c1bee9240075c4af8e9f3121a91101885cb36c96604b4d9dbc0a5fb054f300c1e50505b4b25c5230b3942b8e1ba4da98d26ec7c34ef4a37b7f9b57efbd772daacf477fefdda616b1f48c7520d52c29698dc8ead1366c0745c21a2483034ee1dcc851a196923b6d91b4646d490a8aecc8381576f5de7b9695d8edbaf35b8c2a7de13971c22162c22ad3a95064810164455022bb0fd42fae18c834956fbe288ea4744ed12747af6c8e96e8953b22644e26fa1d7ec2f4ca8aaa9c4f8da76bc3d89abe4b4a68b45923eb3ea660f4e18889a5ccc7e69dbeb0782c91946d35e5bc46d0ba1002514e178a4d1777fb8225956c8cdececa5b57ef832c58dabcbbb8ead894632e6152ae146f0e810893cf1eda905a150bc61aceefbf0ba842a8289d864a452b005a890ab9aef19078e022f7c357f57f7f1ebdb2617055712c453ce70796301d649577ce39e7dc7773ce398f12f1f0aa3c5b4dd691b4675853b7414d154b5095ca5916b692955942723631622c5533625e579948376844f1eeaeaaad1eba5f6e92d255df54c2bf066ec7ac1fa72d11685a9268654acf0525854a057a44a4b1690de6ba00bdec36ccc3960e199c0232bd36470a8c848d40f239dea0dc0c81372d12b088a68fb0ab3430abd49a3db3a5e2f6e6d452051536d6834581b2d844bd570f38446c32321e255f5a253144b0e9b10acaed9f19c3c5789e9f476486b376ea4cc371618ee38f9ecd9e901709d75ba0ce46c4054af58ad1f865f4fe7fc9deffd3fb6d0d21cc22b09c5b70910d1c205ab5999d159d15a81be648d1f37835538785414a7dab3914c29aa8ceacff3950efbd77227604636f451617921f18e6dded79e578f70e65d593e576345ead42f0ce39e79c73ce39471e4f7c66ce39af939df7de7befbdf7deafaadd9e7b674e6458227dee8cd0e258a2d72397b8af956e99871a05f874b29d3bc6efe05090a35d6709284775888049fd69929e646ed17fca44cd2faa137b6bf158d22e8b96c79bdbf58bce0808d38b8c920687de9cf308a0619570974e8a3ade6a92dc8f197647c64b83cf352d14f09373cecba82837e7579e9bdb354cc2cb667c48ee93c1735c182ba40acb534d449ba662d7b503027412a097d0ddff6f81e8331983f5a6f4a1011b34250f761e504fabd7e172e2231862e2e4406cad106354ae536a5a6243acb1331821b9890a73ceb906e79c73b1ed3cc3503856e3b241bacc81407ba560dcb241c924b3a809119069eec172c4d88861b75eceb04c5462b8312114fe8b44676536111a85688b13335afbe1766990d5f559ba9d68b96290de8492b9d1a81b41c8499c737e876d7fd636b322155457b63b8444c6f534aa50054131bfec0901f1879d7727a5c6bc8d401aa54d6f4b96bd96a8dd1d46ec318f39b5dbd77283730360467c302202f57465a0c7adab84653a83974242c71e60376a055ab421742f685ed33722b8e1ce90f583c926ee8869b1989e41526e4619b56186a5993c8d7298c5a3ab2a6dadb097c3144effffed94dcae29bca299ef8766efbd77ce39df4e4f38e79c732eddaba72affff764339e3fd31bb891050a8f342a30f3ea0f335f3ac42a581a06064eb0828872c2e19f598d19bfecf5f87805e181a78b221c03cc4f2628e9cd79e6a728407085bb3c123baca350808c7c72be67d23931efd187111b180a54d80e5c179f7ff3f880b781d88025d4f9cf1de74485af8ec996a7951431982404982534229b902ead2d00b783903386145cbe828ed58a7c23142e32343d366dceeb8dd5d3892f6aca3efffffffff3f642a442be46a412134819be820824bbfffdfa350caa993319835ece3979cb586b23306493e54e987ed13f3b556af944e65d3b1e99cd3819a8c43fda599cc604a1ce0b5aeb6cac8e3f0972009876ac9c3889c470a3ca6a6cbab882397bc1437bb0d0e10e1baee89589911da409273f960a3563de29560e86ad821ebbfe67374e0a821f390a8ba46355913ac1768b7c4a78e2a73b4d7aac8f23093a6a331a42ced8ca3e7e891a723857213479bd0743fc0b4e01493ac825dd976f35ea89282d45688bf1253e1043d972e90a21da327784ef6e286a158807d9ac6a0ce3bd0e5b3f4563613f365c4b93a1699d5e5dc8ce08a90eb6068e29b5e4eea43644a05c9c83157b0ce8626494e714f0e740a95babdf7dead82422319aed96c5abac02d8d2f5870dd3b2bb1a32d5eddd387883218b9b5be28cd714d95673c2541a198fa036777516745353426382ec42321441454d2404a99116bb169199a81c28a256d8965c140f178d6b8521626e426debb8253ffdfd48dffff1d5bdec1e5dce25efaa198a4ccdc8fd2acd039fa8e621e6e521c82079af949bc73ce39e79c73ced99a6d756d7b37e7642fd5f6e6f6cc39fc9239398f6548a816cb0625b7983db538981914b6fa2d76e3ca196f0820223233e3d517b2bf29a98189c100254752dcdab88aef58f14b2669e498656b5b4255700bfafaf0425744d2d5f61611678ae708f40eb4d6aac83eb1bd5ae50bee9e9aac687bdbae19aa1d514101a70f90b493a79a28cc26233b3e3991a49c6f32acf7e29ca3d1d9370b6b730aed2cd649796bdb7be769519b8abbc38248d2a1368984e6cc921aff2ff7ab759cb60b5d285fd29232a11ab3107227d038eb94480bccfaa59938e597aa9c1394292c8ac4582893f0c250eb7a0ad37db1fe605e8baf195882ef5f6e4747c70885f0eebdf7fea1e41f4dfee1fca05a7c54ed0fab860f6c9cf1f2126e09c43efaffffd538987749b33c3427534aaae7a54c85ed0664eb74c24651a9a29f105e781fa28c734166c6a3a32c5ad31bed3e753c7ddcbd375530f200e8a412000a47f5e89a60af23bce66c8cd7302e5cdd9a0767ff518709bbb0f7de9fe49ef284291e68261c2cd4edbdf704c4add81d7598786adcba5d4f0177e7e98a2d41894a6744b34e8565be919373ce3d9c3e0faacfa3ca390f2e85640d1aeadd1c0e5bb1ec2b6e59f353f3e1e6dc4a521249723f66d81d19afe7ffffc8928f2c8d3c092d5052eaff693c66fdb96a65c380a127298b0b6578f5597aa170dd30d93ed03c10c53d1dacfbfb3951eb64028842ac9c2c242b5b6230642f67395237314f2c83e1114b5d93cc4f8f07271fb84c20c3a25b7764841df859d5785a85cfb93de350d9b24e3dcee9f0ab98e46a728282c8c8317910eb70a32da778ce1facc9de7b171b8ee1d5c0ac5f5a09e79ccf117d7345611ae79cd3551fbb287766c0a64aa5a7363d1baa1b242e3325979e988e8998960a71ce4cc734e52505a66aefbd5b5928516a94341f5aac93c40af3ffffe36139de56587f4dafdb2bb7fe5d8881089e1196ce4b69b4a0891a119d123bb4e6ee2a70dee5127ca5566c6c8d9c2e3a7875341b5694ae3c7a44ba5a735719f1cd06eaaf592b75c41c285a2428b565caca14170236b54cd24e75f3f2e46ce6b68e362a77ba3ad1215b2311097b8a73baaae0d444c535f15aefbddf78275a486d06a8aa6712dfb0a22c668b84a42af164855d696ef0b622ac4be89e151d0d5454f9ffff5675f26906faff2772eea333b8cc4f2dce78a61894677ac6097b20c53a923c011aa17af9d24e7141efd88a4561f12811cf505814abafcd43feffffffe14ee05ce06a2c98b353e8075c8e5968aaf23ac939e7db0fe69c50544f669a0e93e9e65c0a4d2e0f99a4f613cb6470f9c00396912724232c1da9298e8d54ca6a044c083ba48cb8286b3b9273527c43318db794f6cbadce53b90eb98b38b920c2498d673a0e0b0ba34f3385055c950612f2971c98e3448964c48810562c42dcac961fa68a89db0cb5cf54598863667aa62b1bf56eef2e1714b65cedb169460bc70360d62f27f2e844bbabd1001363ffffa1f3ff3fe1ebccfd3f9358fb053c998273ceebb8bebbae2bfc71ce834b1be1537d70aaebb73ade1e6271be73de77d418e5ff1feb94335eac84fc1fd3b33618e217e09c972176c9ce2d5c18678c7586d87bef6ac7defbecb4f6ffff27b36a9061855820d98a6cfbffffff758570da7beffd58bcbb3d935c7c927cc585f3e6f7ff8f82cbfeeff4c109342f5611361f163b181421779c9a34584b744119718b14a5eb4b5a101ae9bcdd5fe14b6eea4295d7b94e122f404089d393b5a5e256153455f22ce57062983a42b0b1002770b41a99a2dd37b8978d46cde9415d1615cdfd6c58e9bdf7bed78c335e27b56ac5de7b5bfaffff834bb5b800cea1350b7b57a7e5f8580181b323968440dd48ef27bec317e18cb541eae83ceab1d8803065d80afad11b8144668c8a3cc52ad43804d5a711a22ae49c73ce69e6097cec5610b4f05a1b55455bb60208a556702ad649d8ea846567ce09b52582a48ccd9688286f9bb204df189d31756b4d3d403f373a77bff990213d5a6942facae2c528177c751a0240ad8116794a58a44898b2d680f05142ba898298a5c5275378c39a968b4c84196700eb8e47b3a52936149b28c93413c30c9ea0cdb099ee16119c0b54d3c8d48f925d4d8692a79ae5681776f5d0cb620e2dbf51c51a00ff1363a36a6929027dd08a8b7252316bab9982ea6563126a07325da0db6694204a341346b336e60102631eb645555b3c6b77630618be53c3873f5981810384954cf428a9b3cd9e4089a677e503133542a792211e217670a4fc4ccfdc3aee903fc42d4de4b2d16849a9c019db62912c9461d949138f0c6b8e08aec8a346a48e2eab839014a884ec592d396580520b000003190000022010c4912c1014b9e40114000730b8bc98884c34140c46027128100002006040101000008080400010080402e12090d7da780090f10bc9548a63c5a811a94f5301f5d5049dda0aa0622c6839e4efdae35ce66234959ed5e66df6f878bcd7222337477272c53f68383922e926daae85f4489305dc8d5890e35b6af1ed820aeb02d7f85f6505f03908bb29705d3f9761a8a9dd65be92e3278a1ef166cc6006bcb3becaa9dda2d16d6b6c39a28722290ed794ffeaab35a1a8d25c4f2b5d1a4ac20a500642d0e36e416d42fdc75b2fe1b7451a1fe65f0093fb9376f07bc440f643d6a5420afa04661c36720bd97ff876e4457d19f6bdca068b958a3a5c3b1f8b410dfdc385dfe23e444577e0a5ce7d87d5ce7f61c51d0d78dff69df06f089add823de2b8ea180423867dca9c091e86d747c9493bae587dff74b3bb72a54efe484d30e8f79f3767f44c327b3037553f7ed39f3453de93d0da33043f4033feae8140dc1261d6784b3ff5e567600ed0929f58c3eba994dc06c108748c478066c109eb8f3daf3ccaf14aa2829b0b2a8f806e76e124c6bb5ff2bd52edb25818d1a267795c9b07ea832745ac76c832cb4d5a6c9171199216780dccd65b222bf2b3d6e9488b72af6a1be70b8d2c6b5e0891d1a98dc19bff98a0a8eda848774872e6806bff7ea37900665f6832cbd14c0ecf0a49adfc17391e3dc0b38e900eb01a0d89b135fce8de6f313aa8f6188abf2ae1e872b0a59b2fd1d16a5980f32eda3595bad4df09612ef3f4f0985bf2b46940fc41765d52ac1b0da0e606218efb7d1f68e4683bf0b290883b9992d33695e9d2245b18e1d9690c439b2ecb82f2bb42f65d52cde1d88161ddf48051a21103bab9805582b624b5c250d90e879f3721449aa651426b295cd9936226aded346b2bca62448e310d86c54814696aa76b4f3f4e2b203f66a293d4546161edac68d0388641a5329d0be0f497af127a8f954ae24c96d7cc71f978e3ca2bb5d6dce26ab7dcf26ce6ce3d9138cfe407a15de1b867d5a56a01f05350fdf55b55ab3d0bdd156eba01630cab87a60fff607e4b0820f5135c353cebc3dc3749c1985b4f173a2e55b9de7cc1d40a09a144a4b35552bb5ed1bd4b09937b2c87c8fe8925662318dc2c1d44bb3889a0cb497eed7dfd053407b2fe53e801c03feb66a0c0a203d133faa661fd538407ccd57c15f259de106cbc15aa477e75c19e3b1520a098586cc4c3a072106aa0f637f496c75741bf613d471aca4c988520a45504b8a3277df0f9859716bf8aa05fc51abe00785b73f347e72850f74ccfa36b1a2452dfa643e0a11d3369e6a477999b0036d1e8accb3fc9e3cc282bcebd0a0a8d846d8d3cce8951e0bef747b32482cabfb3bced7f6284c0f3e83328ebfad7cf0f8ab82d02b7b61fff1a13f0c4b7ccbdb15272158c5dc16d30bbdfb33c74dcd0798437e7cb6723ce0e610b34068868e31088c037006044047bdb499e2f9311b3bd79d2039849e7303440c47ecd98b5606929b4db02e89f90d98186168b251996a5b9594841b0bee7a98a9049c8a151329b4fb023beac7c8c59f6543f298d4ddb63a34f02219b4d41eac82dd8920e5f6e8de87236dd8f7d442073ca0500bf19e4407d3c0d79cb8525f91dd05105f71ad1106a8055e13112aa2244c3fcac214588a69f00fba0873e7cf688b4c0c809bc5d34ec719d44a4a5d7f1f936a3901fe018c3a16db634f518c82623cdf3fc17854ba6f55c8a732b6461ee5bfbf8e4ef0dc0945271a7bdb4b5edde7f2cf2d9d68cbf8d3e5fc1d181a39448f1be14c723a038a8c3d7f2f33fbfcb5588fb7c469640ad9704174f5accfebe837607dc7b9efe3d3e55e9f125bbe312fdce079afdec0bc7a2560f7cb2704d91b22cb77b2636a7f1a5d64034fbd4cbe1883f606e64c1deb3a9872e4225f2cb1e71407d66c921f52b1113ceb49ba45c7132e4a3d40f46acad5415933aef75d0e14ea0ce4591c6b4c74f5ec07d4df46b7772203793a9893d4ec69e1da42d2d1ac28eca4b224ab47108182ca2e33a213a4bd0373cdd79049017976bb7ccde789581b460fcda3df4aa4e8c11dda40f552fbde8b5fa0ddd89b0bc54dfa52702cc3d9d6c0ddb454cf60ddfe53092ad6d8e3e2e71b29fbaae86be0d37409832ad3e2e32a491cd4616bdadd44ba1dc2c6a9f24282af4bde4f09b09c3450dfc099c81f822266303aad0f1255ae2f8c4650bb96088b10a78f468349e121a2c226f8d385ab95805c92af3a2ebb63de5db9d7e79ee4746ea9e986d3be539631aee44f00ac1fd8d22fbe4e77d90fd62c2df6550ada09df47b88852960be4dddaddc99496263a878eb69a30dbed0ff34b547ab9073c705937bc24d42809da8dc4f7bd151410915acf9ce360d6bd082c14726801cca4b1aef2455b4bb39b49fd5eafd33b16e6125bede58fb2d2802389231aaa71e947584252548297997899d917d58d18721b89a861b73000ebd5363a1a45c5a177f5548e9ca14d095839b9a4424704b6b4cc185d98dda3a84173266b92ac3ea1b31cf077fce495daa747146ad2e2b80d243a194a502b646fe228603fdba9a04e75dbf2632510844633e30622ed45f1f0da58714119371d37c316760dbcbd6d43c4e2ccfee5b89da86926d4f6c7ba46e77d1c3861ce6ccaa34478dd3213894937b3ff06444a6075be4dd0beafa62ddc27af7925f1a633d3bc9433bf0f56604da6476c659fb209af907412d0f52db07219b2d96d60c98366f217180b70aa0a966d1b87fda148d875ffdc9f2b47d3bbc3d0bfc08eda1e04fbde39c2c093e5a19d4cc5b84342a421035f3e305c83e328ab593f72fad2f73600c2d9df4ef04a72825474ad3d817edfa275c44a81038124e88ffc52c186bd787460414c2309fd5619d55354ebf85bb2ae89a890ff6b77b9eb4c848961f3029dde1a077e541896c41da7c8449a0199cce299aaf7a8868dece31fe2814137d3f2f706db9eab0074d915d4b3fc268cfda95245999e79ae7208ccaf8ac0dbbb77c1a2a407bbdc418a0d2fb86de154495eac9c227538a06a2c8ea5a97f4932478ce9520def8480c0125a356f422cbd69cbd0ee78f24ee4b15a74d997a8b8e2047cdded922fc212724ed0b5d551aed866c46e50ac51a86d00efff15ce1d20c1e660cb30a66eaf9aa4498f2c7d7dfd517932cb1f210c63b23dac34defa3edc4ddbad1245b6f0f9fa01cdf90967b9bf8ec3cda22c8e712553880359df1d9e6f30746da77bdc981637d6d48ba0143b4c0b244e95a2befe9732cc9d7caa40e8612042c04d49cabae3dd8847518011c17394e3ee695981456d06126e0b71711eebb665f3346c6a0732d2bb1990f531cb4fd019ccddf5e50a6c341124aa74bdde3bbda98ee5a3d7f064ae769453030b153fc208387a55aafd1671c304f7bc40b0755ac57695a22e155d3b2bdfba2af1bc41e7ca9e207c00319088021169dda6416456f2e5fcec7472d4d00160108cc6356d9ebb5b0571ed87ca3b36d5480b63badfd6c55e63da8c7b641fd5efac6cc747bbc57ec833172dc7de7789abfd2d67021795b8b907b7f4231450bbc9575f333fb021c8fe9fda3986694a5de4e971c55ffe6f19f6fc3d7b6970f03470264908629c60fa5c8c895ff1f74b647b147cf7a4ae95a3fff77b7209668e268f523906b9c73710a0176b6f4376adedaf12b06f702ff75c50f7b4908f13f4bce5083755d217dc1b839057d0931af9ab10beab6098f1e91f86e74ec57bc9756972def22e35224a445162bf6a13f51283414318a9b98adb12d368f24bb9b5b4628199016859acf7d7302afab1037cbe81f58740c8cfe52a00b0e7a70c587c0b9cb678ae20bf7c9557edd04dcbf3c4e8efddbe3a95228dbd07dbfe4213cb6c710ebe56c45f166accc5bd7c294894a6477bb932f47a71eb673ab530b20827524041cfeaf89667d12eb8e46bd4b9adc966188cbf17b9b0aa0e36006ff7802af67450995e1b5aa58b7d0915b247de6a0f6538c04ff869703c12106c773b42fd769a956cc6efa877ae0a62aaef7382fab0f90d6b8f4ef2bfeb6d32613ef73094834c8c41b010439f229eea2846eabf604d62ac37b1d4b82674b212e764795ac1fbb0519af688eecba66350582d42092b3724140dcfb3a7c33d07efb1d3acdba9ba9b7b005178c370af2f46ec8770dbcf2467f2be47c67452794a3e41983fcd29b5d25f3098d37f640e2bed86087be8de84b93589136bc478e57725ebda633decbbdf97b61163dc4cc1be5831932ca7593c5cb78fe4b8abf206bd1aac441a0cc04c00721a3f066f694b1d50d278e1c5ecfd1434c87b30529fa5a94dee33afdbb911217b3c67168e7a70956db4eb0f4faa23c143196c7a888644fa018cf4473bbb99d08c564140c9721f2f5e065917fc807242862f0156e2386c081e2ced1695fa47634365d293411c32ac8443a5ef6561d978871a4bb48bffaf08ca8eb17ddf01199a12f11e4f55a7c291ee4f61ea74aff0dd2e2e108b33342961893cbbf67be41e59b849a9bf2a03e76179395afe1c87df4d5ac513147d41de0f6dbcca9b79d8a743aa2425afbc9dbe86a2e17b10dedf06f6af9ee239c951c51724032488381d12fccdbf582a799cb8af88793d83aad79385f5f8da2706e25a9cd298933351911de5668bded360ad6058dce22ec84b7b06e686c16d71455503cda12a1678f878b61566c8bd3f56a29026a9dc0b647a4a440b52bf0506f02d1ca5aa0be39e630bc2b691ebe6d75fd9caad38cc15a8fa81987ed188b6caa6948a582cb38ac843ab917701c9c2391695d95df35cdd3ebe9a3b44fe91f52c04adda91dde33a721eab6c9610b77aa707aad2ed4cf4d7bde536191d8e8c64cfc4c4d6b5e0adcf4f57bb0a977e314f4f4ee3b133a85b85abb7a11235e3e6e765841635d550393b69806b8fc8a58572026c7d6896a5be6512608be3ac40793ef9d7a036c603ca4976e2458810ccdb678a416b73701f722f27236bdbb74240b5a4104a2aaa8be561ddfd054c03efffe1e25899328723b16a28072d2f5c1c4aee7fbde42d82372b3c92902c526cf36f9213dbb7ed61498ce0d18d2cb16e66100ac4d0fd5541fc6f9a6124de4157e12466527b0f3fd83b2d30f6c2d05f17cf24cdc05e3560a0fccdd1cdaf04066e2982e631b62ded1ae688df665801ec9fc03a5a03aa115995a6849cc54d0090477d2292a8be6995f1e8514532accb4f382613088964e17ed5c653b8238a41179df22db212fa692ab87b46d7617cd714af09a8c64b3fe87e23b66ffbd74d5838aafbcc2e4fe1881557595e11113969d45e166627c6265be9eea6aed795903301672dcc3e0eb22dc650843fd5a9027e7e341438e0cc79aa8009adecb0b8eb9461656efc0467f2d0c9c97359c56a13bc5e171bb81f73621cd3719869cd5f8d97470c28403982cc3b10190bf720162dc668ea4073c51df582bf9cad8485088864404ed8a873d2110d496b122457800a0fa10ad03d62ebc86ed2a61d35104d8b84451283d254155069e8229519413d5474369b464f91a1d03807ffb14b0a74e0dfa41025575c2b7f8175c77fdc23a45a759a736ee054ae35742e995977d876946568673ceea05d9ba1ca2cbf8a16a42e49a34f03d71bf05f28a65a96366d128231be8ea79794d3a900256808dc76bd10d5d581d8f99050abfa9da8184be7c7341b80f3f6e82a6f5fb454549669505ab44058b21578592a6200521d1d4d9ffbff182619372be5773b2874a3bd9f109a68eaeea70b882b1d73bd2b36dba55a571b6c1b87c4aabced10bd8d4a6d8de53e2885a0d6deb4ce4c0ea13112ae5b1c385fb9a258a6bad03885a5d013ee8deeee6a0b25b385972af92f767f7cb50c39c98bb50b333705b3d8f33797f0530a51a62f7c64e6918383fdcec284c3e033b8ea8a7c6c4e74e5126baf2487345ce5af2ce960922d99371afbffb0d0d9159e9f30fecf4ff21f10844db6d2d4df00244a0c1c57ede463b7587b773d52109b728a13e2f34763b077a65c17ffbcd07c00f40c6bf0e1e87728b7f11cf257fdfbf4bc40c892d9d7cff24511ca22e23539652994b7199114f0757651e3647cb1eefa6caef0b2897d2373540daa920d5c9339a86006a3ccda333cf8b977acf7a1e3c6938e2b718ce20173a2804db1f8e3cd5524d234ea16b96df1172a5fe10b200440ac36054a45d5c68fdabf4bc76db469cb5682eeb22774a4e513f62e4011a103116d2f7e7db75d93138d85bb99b505313772ab83f02e9db0a7cddc8827d70867c79192b470ea03d06621db36c3e7ef7b04ae52593cd9143dcd2ed636f1572e00e6658afe4d25ffba3b4bacec2bbfad8765f63ed40eb81a0041617b29018896bf076cabbfee3bba3980f3d7ebd4cde51b2dd03a0b76a2b29ad8f8d6cde42dc8fba7747d64971a509e45169fd93cfdd956fe83da7333071e7d5779ed0fd86edcd82588ad6a4c0dbc151e27402561c32834edf4b2d721b59b1022e8c4094187ccb0a53058d9fac626b0a5577b4e8c1e4a977826ca29ebbcc0c263c3e38946f3a69ef88a292e9b1cc407e8165181546788194fbd03006da6d388f04b11a4d912660d4a93d201c3204b4fa8996f09393e2249a375088da1bea76c6a24904e06af44531f989b35d8a5897fc8641287adaa6cd4b00016f9b28737688d5d8e9a252656fbdf2de7d4d039b2f1a339c48341b3622e1ab683c211247cb763ccaed08460f0d70ffd2ff977fafa0e7ef8ba9030639b8241f22669cd2ef717cca460abda22b5f6b538c9807cbd2c72980b233c5dd20981f1043d5c1bafbfd40fa646893cd4f80f56ba04ed08017abc62e56bda80c5cc1f100b8b1b6a57b5a6038e1db755f0aa2448805bb378fa6eb33f5ddc543eb446f38e318665811104f0f1900428dd9e6d4afbff8eb84818338c3c0581fd2a639b44454ec6c49127c216c86287458479dbf3a496bba9a7e013b9cd6a4e3fea40a3107c500f9d78947158d64ee4009022ae6fdc907da47bbb6758c1b2f2a3132443ff32e22563e1b0db5798a4b966d13b9dd13ac45549f6406ba2646c8ca7ef1240ace1c67bf7e41dc629e4fae52d1b9edbc18ed07e72213f8c91ec6054597c5a7159078e012e678fc6fc1b8483804b1ca64ae597775b88bdac45e1111006666f4bf9a7b336303299a296565f12652ea27e04ef74671b8f2d4756bde0cab946a7e9a706887865713e1e47b15254c4cfce0c870230e97bff12751b2c444f3829d0356cbf9552f266205572e45c5a7195a7b4af37f92e9ab21921f585c179c6f93c03437d108bde831f41c149362a9f4af225e42f684784c5e248f192545aaf6e459eaf0758383ba16b1ac6f22c750b4a1c74ffaf48c164b579582b5c08110452c88089c65188689224aaff7304ff4695a547a7636acae46a0175234327fd0c479c6c66f4c48916034b77ae4a3645ca2248e8bac870ab6f149a74a795b779d08fcd7b684946eaba230c842e3e9e7e3443f3e3266008df1800b3d2aadd4c89179af2a273832e68aa0e80ad911964406bd58b12298e96e1bbddfcc1ba45f3e918a0683b3fe4b4a00d2e70377369302e6d8aa5104e27553d9811ff33b1bf4b4bfaa730c9c7fa42106098d37afd31e851183a1544ba68590b29cae09d5c67f7b737061cbf6ddc371c11d6f7ca199f759fa6a8fd29b05122132012deec4037d05ace74c9ee672716418383e20676ff500b2fa965d2d27f26b4f2cbe1f165269fc07b006b32b17e530065be4da34d2c52b03382abfa8afa62d5c14edc3c9cfd5559ed33f29c22b32e0e1de1d2933b96511bdc56dcc9c226bcf9186c183e90b1645e2602d796a07cdf159a7e5608d6b072fcce9a2b88481721a4f347a40b9a9ee309be91777e520e69826bb34d6c28cc19b0d1a58257156b6ddd3e4981ca48b7d71452aa039f6482363cec1ef2ebe0b1d7c032bbf030541eb91692f5215492f0d6d625cf467668ecee7cc8fe33b2488a6cd88fcc801060c95f2a5262130a580cd0d2020a6278dc7b8e4e4d71d9f62941610710255c1fde1627f7c388226deafdcb55d3338e378511f65b8341ccc3a129f81650741da03f91c807664be9de86304f33b2208d097b0f5573bab4006ae11fc841439f9b3f1fdb71827e410087057063cf0b9b735e71e00fcd5690ecfbee0308468eef1e6ab04a2306b4de87b936ae23295f3af1fbfe31dc38531beaf442a905e4ab1ffa54c91b487bd7170852a63f685a662880647accaf29d92c780d8a805e0e3626165a27f532280933f8a9a99f71eb2ca81035027c4da8175b595496a7f24c5dd2de205fcab778c47617e6c9dffb9aafbdcdb2f7c84b265938f5fa146ca11df3a0918c0f759295bca13b405d347daa9f433851429b16d08783bf89712a410ee096dd3c7fe6f782257a65fb31f644a2a56fbd673ab091c5da688b007ab25fbad4690bcdc3794332ef3650340cf31629983f68395f4b13cd82fc3132f18d970280dc7bc4a28995de601c138e2f69f24b5e88cddccdb8e6f7627bd5a478d04ffd9069021c8baf8fcd20be7dc3641cb4e9185b8b2753b8d4f5861c0b1705cdee8136e7f75e20531ce756ff8b766f3798cee01ab9926d5904a58a0181038af907dfe91b6d38636aa05598f0ccbc3d015070c4ec25520dc3e217e494beba0b30839baddcf6f5207175bbe9c182133ccca04dbd9e795d9848cd9eb39efceb20c46e7561535615195b4e3e16741459fecf428a774479aabddcc6f88a8a15d1d248854477c0d0e41c96205908ccc79bcaf5a3b4329b925c7aaf4909e3fcee476a8c0ef145048a712be278347e23552384fd678a24c8a0a51d0be829f406a0279859cb547f6eda6e4d13df604683cf488b01144e93ebdfa4e6e00cae13d3484d79e542009c9b9f09348d4bc39619e900d2bfbce9e1a51698068fc5b085ee488411ca2dce1bbd6b7e1b16d8463a317ebc81893adec91385fde9b5ec896642c91417a3c5238c40347ccd38a634fb31fcddbdc94e0b847df3422b530fe008f9371077c5847086c776f52497b3af5ec3fc6662700b46bcb0ce52e17d837ffccac6753c3ae2ad4ba9a52aa5e39fd185bd46019a4959a33157708001c1f141377ba9e4fee79c73dee56575a464cb198622a4c347e42a8d892aee15125bab9dcbc75f14ae43e9db5cfb24708af1d3a23c95648b9b5e2ecd5b414fa2ba2afd44b79cd5926e3e902067922a8bd11120eb2a9d59f2285d82c3c2a0a19acc59d5ee148985ba97fe5ebd23204b08008c5033d2f1656fb39d70a83de7a011bef69287dc4c97d89537f4b3fa11421728560e0846daf7081018e4e9fef1d89960437262d82edc9db24de154bcc5c04eb60186eed24447b2b82652ec04481b4b51a4030388d0470da3f72d8e922875dd0635679ff8b46c3e5fc179a4036b6747cc57be3702ce874fc7097d6381c0d3c819dbd20f554a717f85e07d079b02857ffbb5072ff1ef842138453ad9e3b70c8d70f7c8e11037a8f36cddd5637c9ea3ac0ecfe46846658e50625c7aee153b5c1ff4fd7fb3c6844ed748982ad615359828f552b205a0722ee9b3f55730149d9cbd329e9b8e7f7f9713db0eca5ed458cd51316292dd9e38530faa23b4e7f25ff233dfdf11769de561ad32e9c6ae15e7e607d3cea138bd521882e13d775836ace2732ab41a89bc8e42947a459ab00f94a59a99bbed3314d860d751926de91dd0a623d97264537b21db74b09ceea872857a03db5fc347ccfd0e75e1422c05bc793fe66f51e71fdde0477e317e570f2167bb5a4dec2cb6c3a1da1c824063bc44f6cc02eb7c36fb9ed441d8377c6673bb305f8aa225169750162d7c2ffc8b73b9516eda5f51418c77c0e3b623f5b22754afb17fec3ee3b97b54d04c1450e1c6d63e1ff8e1f47720ccf823a99147dceefda35420ab56b446b340c3f802fc7b86930b447d8f5b2fe7eb377aca90f952a7de9fcdf98ba076c117be4b7f3391b6a2c33952ee06aaeb38b933f3bd15d5fe2612671647175558ca8a32f1cf000234d136126b186ba91e85b54fb34311d65d50adce5a3753e4da1bd0411fca2eb3299e2f20521e77d350d53dfbcbf291ac9bbe7583f109052ad95f2d542935006f23c71aeba18af602fbf2197235e0c6d40f50531ab4bfba6198e8d5479f5abb924beee2e2f4a5175f6cecb9fa06b3ff8b7c45259c5b7b356f28032401d850cfd74fd4e1c36982c9e7f46ed32ebb688c9ef9ca90bd943459db57e1e4da9f22acc9d19ff99a66d35ec3e114645f42dac20976c8aba8001a97b2a38579d60bab2076ff99123d7a7fc9c9f93c7443777929ee048f9442fe909052345fd4daadb11123e400782d973a5c9a05945c9fef367e821cebd533c09a5e9836627e27cd5e6b00205306a1a238093ec3bab185889f45a6d6681174b3df3d2af25d30444fa32a71e0457fb0154970405b8e04747348dd19f5206b4ffc2efef11559310098069454a16a42ba7bd40415eb5c109c336895da73000a8adb87617c3f788a24c2e80d6806cde889698102c1cf746c18828ee3ce541d21f01193176bb21805c3b8aeae689f37ff6ed077e54200d32630707abf786ed5c922bce899818129c67d811cbd3ca94ba3204b14ccc104a86f2f65bc117d1930cd1a55c3bae793c1be34c4a4c652d41379471e995c84f4d26974ed253667cedb5203f170891657036bc59226588011466feb12ec001425ef67e780624bd5d83dfffa4c88d47b1f6994595965140dec8dc1d5854f72e1323733b4b03f4649076170485c740e04ab6f8163398d996588f41da45def07dc76d19a097fcb90b9f55d52eaae8b5d48b8a498a4bec61bd23fe5f8eb792cbb394142a4a3e00eb3a208f3219c5a104044cb1cfc7a3d936e447e9527e464bb33443e8ef5492e127e464ce68a6cf649bff3f036ed094a165f89d2f0fac94da82cf1706846b28648f73bcd5ec5e5a879d6b07b6492c4de0844c6920ae4a2d046594505abf569b68aed43b9614ea7d1e12bce14f54a4a9fa5ba1f94f30084c0e0c838ed64705676f2f21ead568e75b2cf768e770d4644b47033875877e328510b9b233dc8e06eeddd82bbdcc876a6e160f98ebb130f31515ac416634e0ceb0e9b19f3cc70b3f7964d1d84ba761159e8eae464f34f86694c092016e26d16c7c96eb8a5239456325aa09ffa354353a4a31afd56fca2597326526d3bfbfe314ab550ab21e11854cb710f79c5fd97c1ac310c6574b08bca0d38235a49d34032beede9d3c6599bcf48d4c6d12f84d817fce9c8aeb6c32ea305d225e7ababc86ce1f00302e2ec45c70f0e5d6ba1c16673335d807982bffd6a7ca38b93e6a8574f134ef65487506b71c8c883f959954020158607330290d5931f392b10d87fc29edf19a57ebb87328a106379ab84c5db22db10027c3d94ed8173c94457e86dc9f3cf5950c9cd31729ba8bd029b8e9643271d013ba62ddb095e8ea80986ea84b9e3cc0ceb8a2071493038afac698511050ea2a1d04d61de0e6c8eb9be827da665a8d4a1ddaa2ee5bb7dff86b7e2fac4c24f888e00b34d693d657afefb28e9ca0adf4047f71e431ea39f856ca21f294474bc3bcf7ede5277b6fb2a59452ca94648d093e09980946ad774e54de789b774e54cca081d529e65f16cbebe98b2965bee6dd1752d8d0a94227fd01043052938c5a63247750e195b8834a93665031262ff56ab2a2c2774e5f8c89aaf39dd31750734aad319237a89449a96a4df34aa5799253d4eb9d9313d45ff72493aa7ce7e424f550874abdee89080e37ef9cbc08f3d83b273135f8eb03157380511a04c08caa05270a15c8084019d5b99b449412513a825a93a815090ecead4bec9cd5803bad3142c7a23b1783c047e28a201331ef38571ddb6ae963226cc7f90ebbfb9d17f8d9f6f3f737bbd39c2bf280a24b774272738e8788c621f4351f0169fc79b703f1cf91bc3ec29e3b1ad7d3ee34b6d08cca7f699e46020f3a089d7351628c31bef75efb0076379296514668879e9321b7ac5b303aed16acdd823c608570747bd163348e5d8ff67ec17d1164265118c5de63c26c96a8ed906dbaa87ddb9d06c34be29e2e8ac6658fd26feccb46bfecae5b94bd5db7200fa1db75cd622b6147210d0c2073846940b560d7f8a0c584a75884e7b2c20d3a849a9017043a742cc68804be1c4e62b0476f28ad8b7000672ac7a7514013e2ce747b10b6430fb21b5c301b2c5862ea58234295196a0328f452653e9fa459f9f4763d349f73ce19dbf544cf36e87aa263aa2b3a8db1098d324e39678c32be37ae5f72bb475e6299765d9abdb10ad1c0476ffff12e7a515ae98343690b749406218067d3c172618b0a1dd25a6b0b27aaf23848a10b31982207182730e93c515178a60baf78e890c94aa3881426b6e01fd34b23007cb4b0a9e6cc8557fcad4832104211480f5561946ab45c3192ec03bb3cfae57aa8dc8b630193180b139b125e49b2ec4d10e910cecbeea65830d7028d934fc64d3a433bd40f996510cb884270d2c5ee860eba18a38b31c6182384537a11e3a0994604daf350000f517858731c10111cd743023509ba22ab449a3c20560a2938a7a2532b6048ac1e9ad5201b2c33b002833e092ca5c4e68c524a29a58c475e7af4979342b913c4a56512ffd9a8025303369019b51dda019ca9ce6f470812f8c4844142cec9a5cec90fe10b451f65943228a31b46d1d2e68b301c175813a4cc92a0d680cc70c71808e5647668444641847034b1a0a3ae8beea9b304748194735a87f34533233a48bbabd0666b060d4918e078ebb67c7c826a6c6e5496c803c2b1372c968dadb197f5339d2fbf362a69e4430b91a2126a478e31720bf3baa8472afd4d0897a206a4fd551cd60caac8aa9043703142689deb89914a274d20918700e072e687daf99d26c4e65b02dfb395706b7b086bb01f497c961cd6e4544dee6a72b489001f5d134e4e0e609412fa4879a1c308f235d103d8451186e8020a6903e6496a21258f0c4966478836314802d349591815315e50ce19393ee4f7be28e120936241e52bc148262d241709864766856462a5272d9229937a493565f884fc6469625ab6726ca565b2e7cb300c661bbb9e20f1a516c37b9515e252cd222884c6b80623e94c519fbbd0c20a58a61119924e4430f714a37bcaf185a4a19231866430e8e4a562659599a184526e97cef6f2bec0e8364220edae078e8c83f489f87aca29e38c51ca29cfc0cab185c3eaeee830ca68a5cdaaeb111de27c91ac6ef9b051b58f16ba1e08a3732de8ac01990ea775cddc145da06a6a814fddc109e4637a08e1cb1ee8e01335e7610bbe2d0fcb4327541066d03b8e067a4d3d651f6caa10bea6ec3561d78b4e7d7c7eea6f9dcaf1d2bea31bd427e5313de4d2c50a7d4c524a5ef17130c69e31f6b774b82911e2a3a3d11f90db6103e8206d661f2dfd1e77907666de62cae0084d545249afda3fb05b549b5a90482945624514380fbb21f77629b3832be77a92e0fc78d13a9c2f1a1907298e029818a306c48150f2c945f722153e98aa5ed08cea7f7d7c58ef208d2970fc1d21d0a3478b64474874e9dd62cbe3c541a111cd74a2a810467dbf839aa185686d2af590a1a0f3d3036a249543db5e780a8c467ab49158482c2fff845496a19e581c45b3ca5310e98b239e8246b3e3160e0a8aeb90e0a0b270693824489b26a8591d74ab3757db9325b9a4f228546869c992a1fa4af8cc673ec47acc970cd1d7fc5d1a463b6a41e2c3e82a955320ec8d1fdbb1101ddb187e9c9a90166252a6d6bd9c33caf7d0c217d5a0b4d6a1a7a38ae92838053c8fe223948243c0731d6e0151685a9643c0731b2d5834ab43efcc9933672093d7501256773d046ab1b8a0347f5bd2562898b2c480acac32b3f4081b42a60ea1970a21852e9d3dce08dd7d8c10460825ad382cce843833a830830a9839945249dd08263275895c2298a9838dc8c45d80e92232a9a488401f92c42203815754c2b3bb5b08ea7e21da9b844af7d43d6db1f9177f8af9db46ff42fc368241659b7f0e99898bca8e45760d99b5f8e79299fe45f89066520e924e0bea5591a16ab242e5858a0c9fca45d5e47ce01445d5948334663a8d764553357f3c98eaaad231aa63a9527f4fdbb2cd399ea78a7ec1230ffda1cb28e7b4497c84e4506106151fa5087c1e5a9fab6195f7da99a5f7a6448867774ab856164b7ab477087ef41a371616353ed6b514ddd1e893c619639068337b51515bb12d04d23852da21f80f3873e6cc47f8107e0ba09c9bc14347c643a783aff177404d1ecf7318f4d007fa43ed87eb219d951ecf8bf2c239567a4035853a15e394c64a95c5baee137459e99fb4445ccf7395d375647cd4c546962ba21e424928949978b86ebda2bfbc658c97cf5b7b217eb4d5f53cb7f9b284238416baa74c8d2c98a0eee961b7986016d8aa2a3ce4f110e76192d6bf40151414634d1a75098454b684e818a346160fa1dd092282d623a9dd6218c6f98a5457e9428a1750312515a60a1c8081042a566ae025e98c55921a09e82d4c9cd172230a23fca08c2962d4c0053b44a12507680ca1c61a467084d9a96246819424798c1c8161841760dc800830538411050cb8b8224a119ca088f1061532e034a4d2987a6bcc39671b46cde38ebe0d24e88e68c291f2240c2a86d6a5075924bd37e6498c2af28c324a6850a1452990a4144ff41435f0a2075c4ca14284182820c24dad80094d65aa8c81f2e5484a8b11d660411296a04286084544b821e54c10b4306306644c99c1d359c4ac622095e30515a9a7f4f2781c7d5c628cb10653d745678c31469af8656ace396da014e345c59873ce39a70dced0a8e69c2f905fcc3967d3e3715a645f48d51773e280d9e2c916522810a818b105181fb8c08c151ad83895f9d94fb8818413658c31c68cd06c297ece99719164a8d0b06204217c618224369218ea82040e90b4485931c38da3143a0a9e330d2d524a29796074f1e0a8aab834c109397124a59466782a534293128c945443d861c5cdeb62ced9c511549f04262967a7204a3e050cb5204af8ccf428261679f9e9da048a56600d421c60ae8b17a66e2d5bb0748bbd00f6f9f9a1cc7cfaa7fd3d23e7ec4d22b7cbca6a37f4d7fc2ea95bdccd80a9cb513cf4151f6590a5ccc7c8d1b12c835986e1e0f05801a89f34cf2a5bcd4805118ec71d61a58927a86815e0262bf2c439f6d146ef9a8072c417ef9c4603b57d74f478f8e8b28cf4785010c34857d889a9234b75ff6c96cb2ffd2b545e8f997d54ac599ee2299f6a692f3cdb5a7a502f1b3d3df50f990662369a277ac4c9a26350c9c8c8e8892d8ec775a129f852531086e503022918e358707fdd3e3f3d208e7a3cd1a39e807054b7a25334eae5275aa65e8662233eea960e0e6a8c43bacf09378e436e6dd42d8e7a622616a24ffed7076a031c70401089fbc9dc081c50753cd2a31f611fec58901efd8c5bc2333232324a12a2f28e088ec725c141a009381c8fd3803c9dacb522dd8afedccf7357668d2b483f70eb2ddd8aae098ea7e5d19da7a0f3ce691c501be178a24fc1e17a2203ea65a5a7feb9eee9a3c7d84c1f9d530ab0f0961fd243915191970914ad208914c1416e35b06a3714095df082531c47f3445fe9a05e07a5141563c4b2e59906a93a216ad9284ef5195352ec471cd00fc7c31ed48aa2ee870372c70374c4f1f0d4e3891e59530fc32bb3c695a46fdb5ac8d4eba0aecfd47392d2b31f79d17574ce46fbeb005821a19cf2bd77d18cba9b336b18e564a6fe712a9f1a104cb572156e4a84f8cc9df642c64c467cf480aa738db33396fed132d9881e55244ea25155ea39899d68566226eec25e6ab6ea7856fee3017113eb8c4a9d18a5ba72e6a16e7445828f3c2b775a909516334c95498dce0ba7b696c6d2ad6e257a04a482d222a3070521148482500eea4de7a6a6fef1acc462f9115fa500a3fde39cb3f429a5c3fa80daa7add1395a6a7b2b824274f712ce0e283adb2a84e691bcdea8bb1ebebaa8ed197c08fdf98e9c9b686dde0984387ae8cc38d1dba57bed0f493becd6ab943ebf59e6204dc253df59309bc7d826c9de1bf0d8eb1de77a2e0ff9fc7688681f7f87c8f5bcf178b645db12f843f276f7b379a7bdc3dea95652d44bd91e51ed11f5b2986374176fac2e55858c66bc6d358fa932dee292f843317b2fcb9addbc32de545854e798aad657662708b3dd09c2354906b3e7aac7b422d0c8283ea63d4c2be21c9665bc5d18e4c2a2c73a49c6db12f8495830e238fa5cd47d2d614e854ba13bbffd0e5ab60f52a367d5201f4a6b6d5177474794562029bb35d2e37167b1eced240d4874ec4e25cda9466737fa68a7966e3d9f4adbc402a686a165d43b95e6f389a57fb4f48fe3c7ec777adca1c1095deec43509fd40fd87cb6b8af08eb4442eeba5991ecf6c2dfc140715dadb49ece34535d203a2fe1e11546f8d7fcf5f14fb885ef811c1f5885b501bfc9d4a6b4825a50724ab703c2a2dccc2f33763e0c4925cfc9b4a3753e99fc72630fe2d817fe5138d828e51a317f621c9702c3c7f1eb7703d6419aee7f97b627951a512a52c07a5124ba57f3109ae0710be090e2889e391621f3a381694e07809ee273a139e9fbb369efdded0312a3b77f7c55f7e72c4f5ecd042542a5b03730fa8014f9a9c0f1b60d1a4030afa781c8d192e28410547459c34e04993d0b345fc8e7560a997fe0e7b3301875494503fa1210425a11db63c4742ce0c15f74df5d2bf9cc43c3df760729b3c743dec9008226adc18a732a0843033849c57f6d13854aa734729729429008e132afbe3f9eb9c73eeb1a4686539436c51a410a99c53d278c10c05a50adfb36358a6d344ed67c7b24ca789aa65aba6ca8e8252a57f296dd769a2ba67c71cbd00c0ca42c7cc6adcc841332c4ace95a9aeb86942c3ab0beaf0bd77e354d99f73ce51d6dc6b5e317995193cf5fc7dd9ab8e31f5390f7916095b56b7baaf1874f6dd8f5f90ac7fd8d26e9581b1c2d4aecdaee307b5fdf293a6a18558ccf3e3a937f5fd748049bdf77610c77b2c2cf0a903ba5cb5b5dc831e4ffbb0e391d74ba28f7e7d94f2bd27a2175e7811043e47f8ccfe7c6a41e0f3cc22845a10e885733cb0c96b41e843784536dd016596951e4fbb92d2d6669a87c532622821541edd622d55a3de3e6ae0a13f08a158c13c0521538c314230508ac70bd8bb35281f955a4402e72443db56970acba664d9ab2c3434ab0c535d92be2983f95c7bd6b91e2e5a6a03c3685a5881a7a86dfb757775ce654a2cd02953db56b7b173aed3820a75aaa8cebe511b32b99eb62a3a6554a956b7e8bb3518daa91b15e3b713e47d7b5b209e2d753dce332030463927a5d7b5021055cb615896d16898a32ba901a1e941bd8e4cd310f48ae39c06aa3a0ae46a7eab5bf453035271682e9d346adb9ba9b08c46732b9eab5bf4180dd58040d677337ba41a0d2d84b98b5ef39211beab2fd6560fbfed65f26c75a25ed1d3e8fc18628cf826f2e3f1b40e5c8f1cef9a055e8403a2340549204169ad2c160e1c0fe604f920400b2eafc2324dcb3055abe5e3c38202469c2c0ee8ba6c1fb116ccc5a8fee81ca378f3eefab47828657b9bcb5f2a2ccb6a65b1dc5bad151669f30fbaf3ea580e48e5cedd015d6f7b7f3d0ae753558c7073416a41416f8b0fb716a6e6e3031f6e2b682d2e2afbcc8e2e8d0627d4468fa77df5c535d697cb5dabb7f5d3ac26ad691474151616ae4765197cb3920e56723c5038200a446d3f699e1ff6cb357876622e380bae0293425557eae5c4b737966f6fbf2dad8dba55d3c6a5a269f9f64e471616bc0af34bf3b78d30158bd58415a1c23267a4e28265591a4ea908d703879639d763624fb3d02baa266db49ae73f7aa5aa3546ec80dac8f118e1802eefcb52bf8d82cb6f57f956fa71f3455dd168d90a5ba9aed56ab5a2abd594abb882afd9002be77aaab4895ea3018950b277e0c05ed163308333b4d0ca3149c7a8343be27a4467dfa17ffa673aaf9cedd373ce39962ae3a87cfb754078e73a381f2c85596877be2304224102fdb518d7837a2fe15fc24f8b1894225d1ff6d157feb2946fc7565bf765a4bec23e3a0cb3d0ded4af30ed5596ea4eb38185aa752284104d52bb97efcc5e96966fa8f4edaa7e5dc43ca66eb55fdbdbf2b4b89ef60ec33ede148e85f6aec2f5b4d30ef3ed3bc454f6fbb4b0188d0245df424e8301463bfbf2654565cf9a66e127372f087590babb75c8e09a32c207d9e8fbaf0a5cfe065d7f5d182be22f7d519736ae2c712404252e5f08c1d18511ac606982811742d73d7d472d81053ae881540ba0baf41288b88114229ea0050ea1eb5600e6db3d8a6fff31c5b753152cbe9d76f61ef1264cf852136f626f0d4ce0e083259a3e04e13858225a83bd38b80e49350861ef1126f636711f9298f8920f41d83b02d721c99770b057045e43928552832fe1e047ec65e242f852137b7df020887af06a896894da240a519426be44a4c42b09a2e8e04b479a381b1179912fb182021b821e7c499556680ff62671952fb14475882f69c0ed6522ac2c1952a23a2f61912871eda54189217ec2455425aa2ff5c043f89012be34e443f6d2e054890ccca10c5822ca43f8120df60eb14494087b33e04396285b62ef90574b942df1a5ea3c84bd489c8628be4483d360eff5215168f0a5214b9c89f0a5217babf3103d24b1570319f02595bd54e54b74a887215feac13360afca12d510f892ca8a067c290343ce4f7a702ec293f845e58ae5c15e0c5091e1fac15ee9900a0481cb6089aa0c2e83bd18701515197c8987cbde1f9c52f12569efb5daa050e724ec074ae04b1eb07706282b7762af04c25e0885fa92aa043e0394197c05e502be04c45c01618968097c89fee02b4b942d5df6ae9c5aa26c897a09ec9dc18a7fc09766f019ec95c1ca0cbe24c30ff65e5e025f5ad94bfd07f70008ece5c11251207c894a5f415759a26ce54b2a07c25e0c58a20a025fc28063c0de0b58a28a015fba80dbbb7227be0484f3e0714a3c81a531444b94dd606f9c223b60ef0e9e83c710832f7580a7f852b437ce8d684a5f82ce489603f61aa9b2017b895491556615e84bf206af42c4ab18f1a50dcc0a6cc012d11b7c89dab0142195e84bf0067b8970c0978838117b63a042c49762b0c1dee83640bfc137e04bd212cdcf72e44215e88025a23bf8122de215b044d9d2b4b7022e2d51367d49fa0ef6e66089ea77c09772f01cec356289ea67e129164a0ebe64c49f4471a1a522f64e2fe24b15b0577a057c07fff1f80917c144f0106c542444104e8028c10f4d361d27da58ce64d3e13e6c3b3eb4e9780f1bcb976c3abc6e3bbeda745cb5b1fcda7438dd767c6e3a2e3796c74d87c36dc7956c384e822dc747b0e570116c3b3c041b8e27d9721cc996c3efb6c341b0e1380f5b8e7f60cbe11ed8767807361cdf61cb710e6c397c03db0ed761cbc18f6c465c035b0e9e81cd886360cb01007e81cd8875dd4ac12db0a91e878d4675ebf90d9b44028c5410de86ed31318418237c05b65983a31d60f14536a9832168d0c6d7b0b96ea1e0344cf119aa3811a6e243b62c3c658acbb065e12ad6490bf1148f61cbc254ac9327c453ec131ad50b78ce526c141772bd00149a9e02db9d5dfeb990edce36fef904b64bbbfcf320db956cfc73096c776ef9e7306c77b2f1cf5fd82eddf2cf816c579af9e711d820b05dd9c63f17daae9cfae7bf5d19c73fffb1dd89e59f3f60bb53cb3f77c076a7977fde80edce2fffdc85ed4e33ffdcc7767b6c774efd73066c77c6f1cf83b64bb1fc73a0ed522dfffc67bbd4cb3f5fc076e9977fdec27625997fae80edca33fe7902b62bcbfc73046c773ef9e73edb9d47ff9c85ed4e29fffc00db9d57fe79cf7667d23f5f61bbd3897f6e80edce30ffbc00db9d62fe39cf76e7987fdedaee24f3cf09b0dd79c63f1fc07667997f2e80edd227ff5c85edd2a37f1e80ed5229ffdc6e975ef9e7db7669d23ff7ed5227fe790a1b00b64bdbf8e7286c97b2f1cf796cee77362a66c746cf60e9e428f3cf73a0fe39ce664609f5e998cd0604867eea98cd769dcc201eba837e87fe36f9e8f0e37be8377b3f37244a9abce6eee316a992a984cfbcaa66abd9a2d39afac7893983463d2027f4c8fca366fe5d3ae6dfd3bf3254a88ddefd954d609c04c3b59ecb19fcf369837f4ec9f8e79719ff7ca5837f5e251aff569b7b4d36b91e1d4b58f945a7ea1d1870a4e0a16c3262937cb4b109363e22ea9f331252ea1f3f23b1412323213e627334684a03c948884db0b5101f6d4e888dd8e4142521a70121229b1e8f14ab8b4e0c8a66a5986e3d19a65b2fcae684188a954ddd7a414d11aab61d5154d9345971f52e8c21869093a38889e8215f24845b65754b95542fcdbb1ef2877b1babbe76efc27c10d0490963dc92649fc5fe78157e65fdf8e1ee7ab00701732a503315622cffd1d573dc69406a1cf3d64b5695b2e6b84e55c22f574ba8fed745e560ee1e9daccada70d4a8978f5ad1090a2f2f9d0543ab0083daca5142c5f1da3f361e3d49ffd478744c4727c75db7745c6e588e4e8eebe8788ea5ddd2b15787d76e6943a465396b435640a8eeab3bd763878701a4c6716cc0ae5735363540aaebd9e14f0ba2ed702c6672a3711d873aaee3d5f570afa3a3a3e34bd5910e5d8f1b9714d3f19c0d89741dc724cb5f0e2bc7ee08c9f1ea30e4ecb057874d82f32c7ba533e1cff11c5e75b69c1c5bcdb12c9c8d9d7bab1b9b9aadd26c3bf1b56d475a22bd430ae7b077611091f413ab0f080a45a5ea3c3c20773c91c69a77609c79e3a533520ec698c20ce1cb07e05d98a8195cf5b2c9a22279ce7e9364cf2251d12443af7295843dd45e4a915097d431b6d93f66161cb3708158e25ccff377fde5d39fbfcda7631fcfc669b21d1a9afae4211571e2db044a12784037aee39d13145776e81fe77434e6db79a0a919675996b18fe7ec927d5067a73f35f39b7dad0efb67e5b57add2816b57ad36ed178b6539dbafbb7ed54fb0256eb7b31d40a352036d9cecaf9576eb3edacdcbd8d636e65b3e10c516bac4fb71ccec66aa15bad1aa26a7e7d5e90550b41396dd4e7da9095a5cfdd5bd59b2454cdde7ab91ef6b7d1cd69d90bd3a9ca7f3cd576f9cdf6446b9a62c463db556dee69cc549ffee9b9131d3e7efcd379ce9d202ced4eb43e1fd4c214d57d1ccff37996c60dcc35507c02ea2cd19d3f1c1e0c9ba7fd6d3bec910636500bb9a7bd80bd733dbc13e4a14502037f34723d2fa0f00f32b91e2830b91e7621baa89bd768fa66776bdee978e7375a56f8769a28d7c3c7b31c4ffbb3d7b7407b5b5dbe1d4f7704bad541dd6a2728e2f804bc738242ccc3fe617e07a68a26bc03b3e549f4ebe563dcf4ec3a2da8ec42b774aaa84be0dfa0964776e8d68a09d563dc8ef0b043b71a8b0b2a9c396d54e8cf7750bfd19b6c24d88670e027c1d010d7a30161c93ea496c3842237c6770c9059608756320bec3ffa085b1eb0300bad24371e8e78a97287a17fbeddecd92316b72c971f7940979550471e0facb4638aea6f8d9ccd3b309ea0c6b335ea967b21ec39c01b24d466867647da2141ec4111b296cbc2f24475d12e899f6427cbe592e9554d7f59e8eaea0d9f56cb9dc57a8e2d03ea2b930393737242cb3f77e185496a29901b83db127eb8c1a70161c870731a0caa2f13e53bf73110fd64527db0cd72d41252b970e4422a77ee00950f89f14743d686047db459a06a7bc24e5bcce059c341652d7d3c8d4403d2de3f12c935e442b22384e8a71769f1443f3d3e2d488c2ff0c7191df5d0639492d237a0cf6ce2e0541654a534d3c283e551aceb253d74ccbd513ba395ddebb7ba48a02aa36f540ccbe81b15cbb29a699af55546dfa818a55181a6a5028d06038fc76aada9b179ef49f9e493af657353536f6856373737373a9248a66fe7614483d1383e6229fdb4c02d6fd4e8654b649a349499991976ebd26a76f5a652358631166fb8545ddebda7f2d29b8ae96295cbb1ba45afc38149040c3ba3c99479e7ef8c2663a6a1bcb174616a25a9f7de7bef3d18e5a490d2d74ce5a417bd545806b3ec3567adc2326d0557abd7bc6a97692b1a9fcc6d2b2cab2def9c46cb3bc768b0bcf35a33b7ddeef2ce6b986ca08dcd6bb669a56e399f6a238ea936fa491b49890933464c18324f4d57d8094ee22b8c8591da4cb3e1ccb42175a4856ec185165c189fee0cc2aca38452be66c9fd1e8431ca09e77ccd134629e7a4f4524195ea35ab26bd2e950ac3320d6ada6bd654589669da6a455361adafb96a59370d4dd6b5665d5393b5cd0dbcb979cd3736997338396ebb3047e6745890c57acd2cb62eebed06de64bde1e064ed391a0c39596f393418f8ab0ed4d179cd3a74b513e4fa76feb6d7c93f9b43573b41680fcfb69fbae5fc0043c0e81895a505e1bdf79e7d999886efbdf7ded6ef3922fca35bce3515b1fe74d4412ccfffa5cbcb18a4a51e2dec16fc72a55daf7874ab1f8503732a60aefbc1eeee39bb7bce0efa37a7864665b5faa7f91b4785fe71dfdd2eae6e115dc2cb5e8c2dbbe1a6045e21206c0821430861a651e057625c8f73965f60fc0e628f9aa9cf7b01ae090aeb9e2d46842a43536a87dd97bc41df3e93c770ba82899b774e5744a1585c3dbb79748bb3b8d932c3a0746670cc22e820f5ec6e874c4ddfdddd0b00a3740412ba40011c50bc21841e1c818b0bd620e38b34ce685250e82e1418a332298132dde055ef9c98c4b0e40c9e7450e52f6542e5f1ce0909256051e62f7d1c549d774e48806105124c4f2aeb9d13124945549c774e483c995273de391d710449e60834989ae03ba72a5138d11f325efd3aba4e954ab38a13e4d3821542f85845b5316332559a30a411ea15832eaa2a8b38860b97bfae2982a9f39d13172d7f29172529af085fbba61f11900005a69e9c68a2a18e87817c88f4cf7427553714e749d7a9526986d3d64c15a8246f60e5897146a43e63a234ad34ab50d0873671bb4fb782daafcfb7d036688ca9c7e380509f0744a3e8f2dd8ee3ae8753c1bdd018621e8f7beab8a69d3e7db581206085d49caa4d85e6a9990e159f16825ce81f9693156368656def9ca4a8f1f19d9315b40933a21149a83b4248bf3ced1f0643a63ebdf41b2df53213b57179e3b2baf4367a40470f08e9d14cb91e3e5eb682a424ad59e374009e6e05f8a007c4a37924859665b345bbcd6d6569a4d7ad6663a3cd66bbd9260f41d06beb5b2fe801d5b8947eb3d58d323153eb9fbdfcf4d2bac7c3c3e3914c2bcb4c8cb4ed0479ff1c87a7968fba95c383ea849c5074e8d299c9f5c05e3a77e1e101516a250e3934d0caa5634f0322a579a495d22df9b6ab52aaefa85bd2f57c512f331d354525914c2b7f0e5831b1171e5c0f9ea7a23b2afb42bfea72367892619eb84cff38272c5d5ebafb7259cc65323afef2192f5fd44b575d9407795dd4cb2c5e3ae6a8c3009f5ef4b2ccd4a55bd22b967a99e9bd70174a77667da2ad4501f52f8a3b28eefc3d87107309a16bd335c724ec9b24c94fe7797190449f1f5b8b1022d19ca5bce6d06910697dba3565e32ace5436b6e21874a13a272c65debd8f8fca5b3e5354e9b7f52b48eb6385a9384fa18e5120542984c6a97394cd094781b2396128ce541cbbacd3802c71a701a13eb8d38034610a032a9da0d4a767469cdf090b1bbf72238ebd8c6e40563692993285a96c3b45980a13d6893ca2e24542d8acb97cc7c8ca651b48d786d3ad1d23f6857e2397cb31cfa5197786d47329062aaa7f74fc73c946ff38a728b6fc9491d52591fae7caa42bfdd3585cd7e530f05f32e901c5a9a91b2d3dff5c8a79408cf48098097f2ecf78409c8499fe19f2e73947ae878f7f7e0da126e172e83262c49f3f20272cc449d89d953b214ec2579cc4b6630af5219e03cdbb30861863c42fafc131aa01a9e146a340510c457ce53fba868d5f6a42566ec4ee0859b91137629114b1914cb71e83da9019f56ecab65384a73827e19c847582b921d524c453bc4848871bd5ffca242243eca53db80cf6d618ec65e5602f0be71d187164f91eb02c3ec52467269c351b58096724249c9938c21989894d1e3d9ee7183f6676717573f838d52d1a841a0453714e52c26f8c7a1ea31e503ce3c5a87846b76a0bea954ddf3f373efdbbb6f8d4ad179da92851a58a3b214eda766c602bce539ca7d820188a10c6194fa1514aafe35d9828a61ff39aabb07ca91957d9788ab3958da93c8c64ba45e3d3f348463651a3aa427cf21b9f30199d7cabcb7f747cea1f1ffc792463244cb736e944b79efbb0edd06da75a99f42b2b9586a84672945087ec954dd0f550eb561953a19b7be93f7ac97631d6706877c247ce56ac13a6e2ec4ed808e72ad6094ff1296ec427f020988ab7476d0627c4277038433b5371aeb2b1b395ad9da728e981ffa2d8d30c5188aa51af4c4a22f2cb7a26447eeb337122bff499103993a607c4555c8fcb9f634f1819213c0d3e83632fbb56ae66b5b25e045b71a7d9d0426cc53a114288abf88ffee17ac4c431dd8a425cc5beaa8bda7f65936c723db09f36eb96ca31cd55b6d52de9d087a8de08dfb181a33cad081ff98e0ded5cc5b98a0d82a178bb13faf178cae684988ae644dbbd68d82ecf0cdbe5792632e99ff7b0ddfa579e6073c2426c026ff54f8d3f5fc147f3e83c361e0eda1a973eb7a17e1e5ee3ad09b1716849a0f9e67e324df121fe7c4a557119fc3965a2e231f87309d584d212132a09aa84e43bfcb96c0ac29fcb3042f87329a6c89fcb3146fe5cfae0cfa5140efe7cf21048d59fcfa61ffcf97c32ea9f7b833fa748d01f0d4e81195c8813f109b86b07a0e034aa1d00fd79c4f12028701c97c015c771184ee038fe42139ec38124798e47a00687c01117d2c17f09cfe13f50e039fe800cb80334e00d48c273b80b573cc77db8bb80f7c0803300c9737850163e81e7389011ffc9c117c0c45b20720534f10430118e002ec27df889b3b0c40fd083f70cf90a25700300e10570e23c97b7544e80950fc0061740055c85221e001d1eddb25cfae6ae7fdc31a759c0dea2b780fbb0114da28d68d68d68f6b0114dba114dd54634e14634e5463499444952622f0e514860af0e51aa0f4144e9214ad5c1077b8588c2a409133e3837614760ef114b4471f02511d85b03134d8c8aec85ee8325ca7cf02522c7219f69a8c42cf684558c3286664000010000003315002028140c88c442917040cf6248fb0114800b7c9c4c724e1c0964490ee32886619031c600438031041842883134366503436df8d0d8ef2eef85b3fe55d982b8ff599699b823027d486fbbdb25253af70b13bb3d5bc70d5f748e5e2fb66dbd4cab1198204a7c3b74c60d2e76ea553b9c168e3c0cc20d3f7152a1a4384553a7fc0deb6a00ddd548cc32e6cd4f0fc18b17523296086396d5d66fff4395b711da4daeec72b46003c026e0a6d3e15984abc5e863602f57c6b32b349a60876a4c6ed9e96a0d38b84233fe7c0730bb25c0b7b3e0b5c8d80635a2037fda2d02d9b6d9b2b65130a5d0f1b04b74476024ba628c7749f77a9dd580dbb1489ef9d185961db4f99cff3ba4c4ce7d73a4d14f1ebbc9eee5086c7f76d25ffffb69df51746d430405f166abe66d3f9c8589106d72941c751baa8562fc5c2199950f6f290235a3c28dc931d317840760ef7b89cfdda6f91d23052afbadb323cfa2cd83216319dfc259b8b79766d9886c86061eb1fa08002b3e49b415278a68a18d42f4cc2286d94e1b689f6cf35c2b7050b922014e6557df1959313bae5c836a6e9b25092bb253f353590ac9e5bc8bd816c62e13a21c1176abd68c4ae0f1076252984d37c411253e432f1e45bd9d5cb1e3850e86f6281f42b13229c7bbf52428efef8c7e0569097a04492baf1242e5ceaf05027b5d0aaa6784a16d871448b2d64cba6518d91873a0538f55d2fb449cddc363fc6c060c4eb835156713c3b1017796b12ba72df4e01bfb89fb71257040843ea21311fe5c35e67ca797243d132f21a13438d4ebe8688129a8b72f5b45cb59b1ed97fb534e0d39eeae319ff23a29da5617d51813844ef36f985a94bfe81a5a8ac338988d3eb6fc4e6df79ad070bd2fc7699c2a467856542ea8ae91b655761890220fb4f4a060838b4afbd3b60fa13d852312269b44ed0d660d90168ab797cd7f25a653d09105c2c487f14cf4d2f0f19ba0f4d0d89d198c4217d6b2823ecba30ce6cff45bd616c780013133d26d15b5479959b0d126bf9596b9090ba8f572d75cf3e3f4d0f0012d15094a6b43ba3df2a2f7a2eb44d4ad07d01b6a4e4063b228b50af18cce7a70155c3387577f5f88a04bf32ddfc005b1c79553e36416cc9508e6dfa043bd0d2038f3a744580fe28e4d0bfe356ad7b213a830012cbd4b44f2f173107a98c4d0830fab6abd1b1ec513ad62e2d31a69d370a1877dc0d682723bb70bb306d87fd0b622bf2fb65381ed5fb845c9ddbdedd520fb07d6d6e5b7c276ab9173c5b57e31ac102b4b319fb4d0e3606e4de195862c250c27bf32d02207e3ca534f1024d7e60497e056d720dfbd0e41cb479d605497a7679f26683fd00abeed71dbd78c43087702323a7a36a7081dbcbc4a9b18d29169f6cd24ced3ec0b1b55996f2a319299f78808ff829beb1b4f85a94cda10f4a5d6246221f4a7b9f61ae7781cd1e122f00bc15b437ac75b1a8b86d845216015fe11106df7f038fbc34d4680814d081373d0bae024658dce4b0a8f766013e364bbc2898c4b0f817a77a06881af5af8d144f76c592e6338d88adc1102ed27031eea3db6aac1ac9aeb31408d67add752b75f38314b9cb378dca4b0450288bfe21a64d3ae449b3b067c91d7fe8b9e4b5d395e5db09ac5fd1ee725db1ccaeb034e309d87b730e80fad89d4ef978fbca70cc9fe6e0abca2d2967a1b62ead72e8984b0b824875db5f78d4666f2fe25c211b02b3f000e2a9afe95da66c9f80b4da668e7d0c985f40703552468e39717ab7a8d0681280c30b65051c6734fddb74972e6527e0cfc28411c17a102a7ae2c12a5074af7e3649e23df178d0a238a6981a01747ead2a0b2135d7b5136027b9d9bde307aefbd73d8ae959f6810b1ddfa04b7b3e3cd5ea042fd8ca39615cd5830da323cf44788e0cfd2dee023b33ae3647cc393719c1bcb588de3b3d7db53cd362e41c144a52359b5e74aab755fb42a72d3128e5423147d2bb2fe4c97caca18d02320bc2fad7264b7832f20e71345d898299fe6f1c0b05af9eacfc4643301aadd7cdb0d1bb6cfe391fdb78f9e5130667853a6eb23f29f0f447b86080aba8800c6479c0c0832d48c995286c5c5d190dba69d212dd3609b7709bceed00b153c3ea2017cab58139a943f0da25dcf051d11e6e4c4a4060a82350cfbdaf82442cdec876fc61ac27e762c3e378c07feda2bbfb4f0029a83d9d8df67390600611f95dde33f8964cec67c30fdf8716ee0958959d8c8a939701b78e4c99aa04dad2c9f51e9c993b79b5a0fa78026bb3101af7b245b1ddf1e574ca8529ccedd50819a761d3343c34f3bdb0e2057e1dfa0947f918f55391300f6468e3124d34b27b78406a2a5c10bd57d0a7b3ba2a77863fac3264ba18e668b9105c45b179177a29b88c8b10ddf61d72388122884d3eed48696b9eee05a28761c2681e559c99f1a06c4e04f56afef5abce57193ed91ce8ad216e44de5600f710bd3492263ed3a39a680a1cbe69b9b729cfb8925d805d8338ac13439905e0781843382a1063fa59859df292f893d7c7fda9c3ee30b25a564bcb92b9b70c6996282192b5a41249de941e017ee21c524ad4b5fe49c2c25d1645c37c77564f438eee0cfa2f7c8649f8add2787b0158bbe1ca24cfad6f6c9f1c55a1d729ffe226b5dcad4f9fa1d4a1b47b497fdab5898351900ba7800cf685dcd7edfdc64180ee94f43682367d133c50703ab609ca77aa6fdec6ffef72dccf44c21a2b2332a3e2a98dba8d8c7122d55128a7de481b5d4aa0814833d54ce0f32bc32eda071242db4c986102a32132d2772e77cb7e1c2344ab9e3ced9cbad4aaf57756f0469cd216b5d621de6472dd99b09e7ba081c5eccfd3e66be2cb15fb665c7a9907ef106c326b2335017d85c5341138d033a5f2ba5c70b4ccdf75d9582822588869ba682727a2d4f9e5804926fb6b006d3be8f034c7134d44a49af55d4a0aa6144869f892a54d09b6c268cfb60e1a967b79ef079911b357f2d3685420c4fc821f47b36901d1c6a8e8874ca8944d3d8a7e386aa9eb77599953318a4a3c803ac23299ae89c4db8ac5af2a0c39baded8a1eae62a9a18455cafba7c1ef1f9d60b18a978b93aa2c698270d64b65d0b0020603b22bcda3d3ed5c74ccad5eb25f517c19768c7d3684bbe5384a5560709dd16ca516b49aca3669fde3fafde299d1ba0180281033bdfa159a3d86209881c6a4476813947d259a1a2729020f59e48aa710acf0548bc643ec9e60035960ffa65a53b3951aa47e9b9fc935f1f9fad149655ace9b25250c2bfd3c8ab25b451598bee8a56c7e0abfe3f6178a269c0f60cf42d55120b61d41c6274174040ffc23f22b0630636ab40d48e30749842643400020311ec6102b16afff46c7c5d8c4852d22f56301ce4099da9684d29e79af43111fb0b2f90b274f9ee993fc48c262620d6ad5a676d546c720f135f46b59f496d67ffd925c21be8efa317546033dfa033d4e148b0cfa6050e8c6de78077cecd800f5f20cc3b0be41d21665a57929beea1b77d850194b69f40be9dc318d1aaf80ee664a9f3d20f1c6cf4feb6d64c5e33f4f480fe3218dc5c1167e20c3639785085b4a1940b742c31e111ac3ed0bd7f34f99db295276cef3e85a0770326fd8a7c207fec2b1ace0f5f13214e64859b42dac6b4d970b8cc63a933f75cf0a2062537cfc9deeba723c37d34203115a92c20e6512f56177b72f551c596c18bd7cde1542dda2e6e69b184fa8b8f22b0ef53a55592e3dd2760ed3837ae5b76a8b5870523df321681b1c5f8c1e911df11bccb8764ecdb0a4e4f5e678ab3016033bab8f9d8c7fc181e37ad47ad6e3daa8f809935a5a163095f4a68e9e89452bf0f5f6ea6511dcc9a74e4536db2f15cc4cfd356ff2ec8f7e0dc3dc76ffa2afe51ae88bd55519b8f61f4025eb805374efec84675002c0cde04d6993291a9f205541dac26bfc712f91585a6a841c7c25d95409e1d1e7f16067cc084464483fe947304eab8a75b6f18ab9acb6591ba2a3a0f2b779104799400f32c3a650beac7bb849620aa1b3fa304faac600151cd662f0654131570e188ceb661e43ac64640e1dd98f28cd7389a5297069309721544cd2b744ea43fe2d031dd5198b638c19f3e318d6528362279678f2c568491820aa2b283e6749763e8422471700ff04c2731f0929a212ccb438d6a2e699f9c03c5a2b9b03e10adcd2e0f5f48c460a673a742a88cddf1bf4255a965ca01c4fc1af4e6318e66c287a771f4fbe9ee0bd9dae9322a83ec27bbe7a5435a12f0497f45e76d9b4ed7e1d081ddbe63e7177d0492cabb37bf2e8c6a8df040eeceac0e26b2cd7ed3cfdf51a4a30711a1c866cd8d720f31c6672eac0047f2368430e3ec7a919a2cf616783ee305dcf1a55bbdb926b664691466967bde2c6a53e8c0b987bb8ccb0d3829dabd64b2e5d9a42ee77316302834f99345c4890514f05f06e18dc58106ee594ff78c2118be75a4a59e220ea84482a1af9d9546dd820462222bffbe027558b20e282c5852b138c9d23302555a5074132a47c98b839ba4d53c3c8f5d1c17e46336198691632cd403f8cb9f5987006a4791517bd243b87001200b96033f9439a414ee04e98a2608134f429f98f32f03ce1f6a827bbd0ec5229a71d322d8f1e3c132ea214929b50d5a98ef87985ca4dd6332369be701c7914249da703087bb89b6cc0275bce28bb63ad045e6e6f2337d0eb171ffb1ebc18d94b66f54428082db4eba4f50161b0f99d3446af6bdd818582cf8e6816e4e9bcfcf72db137876f26f8fd203d819dcd833f6844a5c2cedddb1fcb78e57970ddcbfb7654d3d16449b4fb47d364b260d7e375200a7b691ad3d77cb962c95ce0f432e7aebcf0ff85017c8d24f5f159cb84efd5d0047570ace5d57b17ddf868dd1069f0260b91f63acc32a8c0e8528d2720dc62336d49c9d8712208d40fb09c50f7e26d984c3c4a4bc737918a80ee1917b35c7c732c149fadf556a59920e025eab5e9f15b822170c863963b83d6b81dae424ced4aa19e84783ad52cd4b17056f5e62a60e7e04fb067e8079fce38e9f29d4172ea7177e4cc22244a5c8b0bb3daffbf2d215845ba953733774a8273a9a02b949dd97615c30c6661cd142fbdf0c3287c83fbb881784e0ba99bec75232084ae64baef986ede2040516779618f6a3d9a4a375e301bbace8d7e4e72d2647412892ab6d7a147894e91187ccc9f779fc70c0ca4828fa5a071304f1c1c52b57f3ff4e9c0f3940e98697ca3f5327d3867d803d6c20792e56650fe81bbee30b71f30d6c92665e87387e2f74b5a19febf8d2c8125de5478935522cff99ce0b554d28ec530e4905e97c3fecb0ed8f2a122956702690501f2b4843dac9498a090115601fb72e0ab4f5d6d511257e8ac395453d7d7e5f44f817ee842e4965053ca3b118d417b552fed7f4f5cb84b6a40d383b70b1fc18537f2f03aa857409c6f2a7d34cc8cf33b7a70ab65c1c6eed12ec5f5dcfd4613e2fc6800077376ceb7124ec9e48816f96e88e3935b12fee08709672db8d943bed97f549697e6130e7838a628e62780b4293eae002c0ed04193d5fd29e89ee2d8661bf8a00bf78805f74422f75e8cdc9d67342428af05f4924e19ff52229f5e896eb3a36b2cc9a540c4696aa21b28b2794f0ef68ccaa569023a1e940584aabc84296a93cd47bae23d1c9916c89a25d06d59a60f0eb7c9d4cb27840c8a318d6a29de2d7b77a78d6d4ac1317aa6d5efcdb136a47375ea87ad48c82d2e4c79873bdf8722e039bf9c9c389fb1dbe8f732db049d8019d7dc573ab5fcb56962c906955f8b887ff34acf95b5eca6f62ebbf8620b6c887f8dab82a46049d46c1370fc44efa4a924fd9bc534175494e6fe4e1c2a2a7fb9b55182df46226c31b0b1afd33584c82329ded7208c58fafa4933c41a2a30fc0779d2cb5f45c95a422b63b7ccb34bdd75c847267c3a2441f8254f5ef9af7725fa8b34247245090f2f86dfba100b7350bc7afc0c0a99df84ff062107bf0ac7672638d3ed7a64035dfb85abe2dd430dea00be5810cd2b46915a72ea1a7146d88e4b14a1a8452f7be2a0fb130431f3a1207bee43b1e1286c085d311afbb065ec6b9faf0361ba1cab81d3545d6cb5398ee3514d2a93dac5ad0f7b7b3cc838657bbec2e30f6ed412cc54a4798e47c1c9590ed6dd0e097e64be46e14af2665b6b852eba20c46199c8f91a6e44fd5f4716fe20a6bb98fa8e76d2281ca1154bf1c5b5ef10c11bcbdf9072cfd059a8a366ef716db32605b23140276f5e370961f00198d47fbe6bca5e155a123f8f67e39849334ab159764d0e1303f0fe550396847fa642d344b43d4d0caf7b0cc3893f457fd853dea38cd573810636f5e1e66f1f30f130e3cb8d1a6ca367201be2cab425514ce3dfca2821e7758cc14195d872bcd174e3d783c4cc84dbdb7910e87f3295308e9a0b82f5b8d82feee225dcb9d58f3679b31a028b48b407a506ca13d090d29bf1c9d1462d32717df12ca324e71fe8e1ccff5ac02346372889a29f8845531c8b036d1a14d96793067b4e72c8fc6f15e04acb48b93ab90d8969483ed6c0f921863ff7f4873e96beb6bc69c37c7e28f3dc2be66d83fcd119b9f5345c733be26c23ed3a86e9265906246509fedbbf45da625eb199f6982d4d022eb75895c8b1650c417d635449b0d18565a6e4a4fc0b7706d88477b0cfa430622b61df55f7d550f0010b77c60b5a041cf4623ccdc4a55e4a1b6eca24259cd65eb3425a2818e287d5cfc3ac97240259c18a506ceaae9b69ebdc044b9a7f5394531755f42c52e441c969bf8ef9edb664166f00391a3568a9650e2d5214339562daca31cdeb71628ede44df36a704fea53ebfefd13197d4824c65fd5b34678488d88add0d1318d2d4585bb36d850c116645703a1fe39b1772a4f50426318f58d153fb3596d8b86760f6a00c9c253564a4026f44b97496dff035dc27b3735a244df866444f0c522c6d246a69288dad724e612e26a2275df2493e0523734bf7895c6e7a31fe74438126d988d73b6ada43049f28c7a5895da4a1dd1c294654f22a9cf45c9109d4d4abaaf795e6083abef838539e70ae72af4441a0339eb4b5bffb5339134f8a5be5541ebf63c75f279013d3b341d3812f903edbccc03f35e1dd87885aa6dc391c0b6e5425efd4e7783bc98f3f05ba82e2584872510751f12635bde10b8e0257e2483fc5450ec6255c1cadd775a1f1f49a4eea180b37485b8f2a83c504e2f6058c7ece07566b34f10308540abc62b266230ab6af8ecfdbc8a3dd88e2f64ce2dc3a08da298ea20c2badb27df2528efe040c52a7b0a64847705555b9d36c830c38d062f7ddcbd0dff392fd1a2afa6d815367054087765cb2ff0a32f9cf8abad383b646177bd899826b2429bf51c83c6ce5229ead81582a2dd19a3614efe9867096d1d467527b321780a056226b781ec753a7db2b01f27bf2b1a00983e15c31fcd5b0e2440deda9a2dd9760b99dd70ee62fe9eb57fc8cd5811fa49f028a3bca1a97b29f68b3f43a6727a3388334fbfcd080c81c7be7b2a8479811a75e145d8b591f0a7e8b8d3534c7e0cf68e1ea45672b597763c4efaa89f049e43864604b28c382348d100f1aaab8f8023b824819ddf036003778ccf451aed8cd29d0b23034497526836b6cfcb2b9338bd2677761210245496e227b42bbe4329b73bf872f791bf1c9b8675c64ffa06c865bb2624f38b63de38dc642c8b0f61fd122fc4faa8114ac8967db1609123f33a086cc379bec302a85feb59117e3760461cbc45b081cd6afdccf8d961fa0b02151ef1d11419f9c8b84dc0314226231d0e7308292d14a99565032546b7913fbab48e3e91900ab2f2b06ea189b111ce074c67b306dd162ba5ae2cb41d56c299187c956a5e7b5da001ecbdbf470c8c708054678bdf19f183b6b0c5e03b68bf85f8909d5d207b797557469c3868ef6e366a4e3b290f4f407bcf8cf25880fec6d6bb538f57a62a7d6f95f64e1694d333e480b718aab76c819249f9f28055789c9003dfd974ae07b0bca2d1fb3dde65a246d84f68a3f2b6f4ae06be0261fd1c48a8de83e5268d78286c62842714a4c65130459ea81fbec17103b7cb7e5be4d74e45255e1e59d7cccb8bccc0235b9c4097e84961318e3e6d18e21a0592244f22d166b9c64e45d0a226ffacbaafc7bc2a7df63db5c326af635b64d7758a146832ce7405f24d64e2c9204e3b955b3db64a0c49d8240b78b8dea200a0b27140f885cb83b9c52d138df84db581110a14b60302d79751080427672cfd4d1fa3bb06fa4dd6b42045e9fc3f3f03896745ead9eae8abc49fa7f31077276a17c02647eb733e02ee2d57092d58fe6a37c24671edbcb8e401e2f4ca94970de8538d4762de1e8fab6a3e5442d0b09b1ba37b155e8e386a7c549604a1c5aad956409bd461f69710f379c98e25cbc6c14065054c7edb623c974449dfaae4bd6ba3a83cba6e06bd470e747446259b89466667005bbb04ca19bc90f2923f5155f7300591cce231534359f19ef57867dc09b411888c8d8c73e3a66219592c7d7a5ea3a15c42cc7362b68541c2b38a7a95b7237e7259d7b6d2ecdc02c928dcebf9033b7f207fa54bba168b6ce82aaead1041b8f9e18cd515b25087e0d38e4191407ecbc34165b53f4a577fc8ced000d73c547d854eb8e102297ba4aa4b2344fee60eee500bcd7966d77381d2c2e1ed18a973df8bcb2afd7ce388cecdda178eb6222e5a589357c882123d1a80dc09558ec9ccdb5abe5597449b421734b73412d71a02a2cf20dd76499b5d0a2570a940745d68e846b4356c2456be625d1e9f560c6c9de8142c3bc3c050da3f8348bb8f82cde1fc72f3d198775b8528774631fb6eb4b30fac55cf10f755106a3811277b5c8e9e4d24716c6a34b08e5d70792864c927519f73d9c8327fcbdd776df2aae6933065b59f44a9d36e6b59b43bc98d41aeb1aae15a555ce807a8be5bb2024d57348adcb9fc428375043655e2d17b93bac4dd1901cbd4b527792864e46b0422b2543a37347f7a7ef32860cb33d1153e46b483cee74411a47c572e2f928076d1362b9f712e8d2e731c4d7051f42ffbad977eff9028353acff6a5e16f6b1be1ed2bac3f08fd45fd9d6c72833d8a90cb18143b710a9c96764eb45f313cb037ba42fbf425d974b0745a693f824424d4e686a95411a9c429f525eba665fa691181d3a6d7517c38578333c3b2caee9d700c3861eb4a284305000fc04a9493fc555954bd1d879aec93dca13893a6cabc6fc9287c122ee973ff09385804b4229c5765558a3a7ada69ae053e625053067932007f1e0d93c99631bdc8358cc2ba321bfe306d47408a8e1d86711950ca5c0fe1f1d29b122f2ea130a6c636f419d3fc71d04a987af8ad4b4422def234c0b4e6de3651becac2b7c6e8ee10eaafd08251a41daaedf86d6fdfcb5179b5d73ce94654f56b134e4a03ba5e0077ddbfed1d8fb9406eae6e248355925b152fc08d82596c95223e02ea7b0a880a9f52c5d20b8ea8ad82b592a4125e691039ecac049ec35aca41aed8249350a2a7efb2abaa87dd33c704b3fde12aed9c45dd62cb715e594cb639599c39e12944c79c3d9d29c5947c7d891b1cef52264ecffaefbc328b8981eb6939442cf1ed6f282a7415939488b046261cc5a2bd80efef74bcf7027adea11871cc58111cf85eb080382735a8f8a440240d135c8357af6d3e4425b05fc7cfff0dc64b35983a90c362136f99b6e4579d4c8d61cb0986c763631507a5029d8d4e4376efbe270118b3892c57b5ac99e22ca398fb864db3fcbff67d36b00d7aa27e84823c7defb3e8f7dc7ceff56e927c2a1d1989d7f6f3b91046afaf6ad90eb5b896a5c084615fca23c9d361afd7b11b89d5498682709ebb180c4bead9a566d6b3283e408f497561a9bed6f8f33a6970c1f5fc30c6b8a301bd1b0e6262f6bbe11e0b62f75f745ba20871413e44598673cc59abe6d015f17031f58e67534a389687cbd01689cc9ced1148bccddea19a050080d39ba09a473f8d6ea8bf9f2938e5cd037df4c3aac7b794325c84206d1567eb5b82ac4b9a054f653d3e94b3fb632cca68224e6720c26aea2cb3e518ff7a4eea5c5c7fcd25462d9a56a5729c9832b21038c02e54c43f5985d13719cd994c352c94f7488b172f61b0889c23be202334c51fa155d5999a17afec9c891e3d3960c8b82f1432db8d028cac46806537fff1df5d0efe0d64d4d2fb4fbd31714e34ca9d138c868200dbdc403189ace399c3f1c3e25af2b5864941cfa51b5d2721daf9664a500ac44f147bb111acca9b23ca84b6843dd296583126e6e83838d84a5bfc0bdc9d907926f4a33eefa0f7d33787c229add3bfef8bbec2a83e5ea8e49931abc23fa7ebafc9730e7e32a4af4dbc2db786a3166b78adb07326f9a10f590654d0d770c05f4594b413e21ba2a81c3653867c501ade0a7756176e549fd9e352f39d93385cffcfe654c635ae351b18ce8b54ffeaa219d988bb216495c89f4e591c0f2d1a002749ea9b4f8e003fde01a1b5c0be556f9d7b80d00b1f9783a81fdd6ca2fd82bb57d38e89ae44f07aba8fe9a9654b27667e514883140b80a2972a83440b5c8cbee275e30cde3a12d80b1b21aad8c0dc647064b5259f433025720926117e9c43e12feab7dfc8be06abb348793cc45f33c9d080d8bf405d75e68652e803ad814bfe7e3880690f85e021572ea747323b84e687fad93db96c1d6c941ba4c23e23ad13d0b1c281743ca783f50cd02c713ba5220d59335db6174560868a00c9f29a0592588b32dd6ba3e69168dce0acab783a5939d230cc2ce72a03ffcc4620f4b40cc240ce1517276a481e1d6ab90e61f5c842804aeff879eb1dae164cb6feae0bb99bdc23a0a0e50365804c67dd5be3970ce122e54cbbe0029f30910844382e95e65432c0db75d2bc04a2b4a297bd17559fdd7276650ac8f8ec98af4351fca83c3adf579214787576c8b8069176e4c4486a52cf1e4e8d34e60189128becc3bce34bd72637e697cf3ed54fe38a31acb0404ba5d9bf0190b329dd339761bc4ce0850d1f8ee83f2a3db136b50dab89c496aa83ca5f895aec8f2ad2a54ea633aceedfb2d46d438f21c27d43a22fa9a5c70ade9181d36079fb0d324bf5ede444b1a10218963ed162849ad8c53628a08afbb158ddceebe1f43facf352a8e0927e19f48e5623d53aaf724ab097e6047646d06faa4295753d628ddd055216e5e3e9678ef026763c97fbc95452a94de42d54d3af21a63a6c404c697c0b13a311bc24ab2bc8c707d9ba65060572f12eee7ccea3a11c4635cefe596aafb389a7edd480216708e6677ae3bf32c11021bb503720f558466cd8f36715c00aedde1a5ac86f8cb2e440ff660bbda85e0164a86f48aa2411951aebd10ea8ad49a2228b3a41107e064c44918d109dc01e1cb9f39a9d80e56c6bd8c1ad5665ff0da3a744ab128b8b82ede036f3b7da62042292642a87d55fd541b78051c103760bdbaf76b5a163ef0c47d94e1f251c704d09cfd7ba42f0a64b7c957f87312b8b1c0e7fb008b39d4d23d0c3c557859f4beb12cfbe52cad091c6acc756418f0f255e9bf105aa1079bcc93faab512839d81b46d8b69f2ac7a22f6e70bd6eac3e55c88b6d9023067fe84a51017db08b6ae358dbf226154f350c77f2b9c57f3dceda10e9b305ad3f5651823d74dcfbf73be147d1410706c0ab0e50483b7eaabe43b9d2e3475ad07aeb96ea82563e2e5b3e5662dfc9622492bccd9359a9aa64c78202ff444212a2dc38534e50b5fac8c92759a34a0bb8f09b524f40efc33b536013259e22301312a0abec5647bebbb6d9741e139af75ea3e6533a4444b450aa771f59a357bca6b4e486ce91c0e7dd6c4c93fa5d65ce2277bbf87c218f1c54ebb7d5ac0885ca156e14a4efa1328128232b8314240f2397ef1b4c52fb22d1c11e8159e72ec9dc610a85f81ffd38af1a86286fc0237864d7c9353ef7ccc7fb6b0cec46bcba8f43e8cc88509f313399387791bf6a00670112fc994e29e06bfc9b4dd0c5ece9c2b37f67715cd7489a5fd1b1011e2992f532a6546451c5a33d540b3c6ad4ad742367621e707c6cd061a3a4ad49cf2d620ab048fd5cea53774de9999b60b82d24bc0b15bba2a11f4f1cf7b1e1a74c157382524fbae2a7649af6bc2388922b6c30793abb49290b333f055c3dab12d3f60af907701418ca005e2e95762f993f2692b0658f6646e87c30544deba760ed23b5a10b0ede33594c00d90a048de6d974a39db8c95994157312b7bd70cc94eb5244f7a7cb14bff425ef2c3f9222c40535c2ddd1bda6f18f1a3c829f1bb069382dabd71464cbef5c3ce54d41ea16c91711a41928f1257cea734e72374aa29b282b1f48e36c2549d458716a5bb0e9ff76b76da600a450424bb4fe0658bf1f52e6f38d39ba661b27f581ee504eb74f3008d3f4a20048401a6aa04a0a98acc66584f494838d9a5685d05d4712b95a2f08c869cb325bd5e61e8ddd4e71312795d6ba9511adc010e689a74db72200910d738e0e4399d013a8736021e1f2ba3f72065ac3f1a029c573de6f69943fdf5b2080cce424fde4c6f2d3e044110e89ec1a198a18009591be233bfa914208db04ab0cff0a85eac5108846d79c1406aeea11688614f10805e42b33540694df75b548bed1a798472088492499b9d2e8393511ed76242c7e2ee3148e0f07d66f8625a264078aff1f3b9623e221b3f1498bc9aa0102ffff47ec08978f1a6d80e84eb0f6c7a6808afb32754fdd58f4053259192e3d88236d6022ef852fe8ff18ed3ff862d5ac80e9cffddf4a319cd83311f212f13a57209d4c3fe019d62708221f48777c530eac498eb7180b734c93c463d05675c5e57a599389f754c05b6f96898188261600f9d53ffddd43e349a6341c6b886626011023acdc3a8115ad8a5d8ca61120d2f945efe4265a0083a1780dc73cab1607b4fa8890922ee11100d3758f6d70f64e577eb4fd16502b2570965d8677f8552c737327194d9c5abb24c043b020cc2ee65e1bd715cd169aecfbfca53bc98a6c0ac1c08fcea8e73908cafa2e6d80ad1a9bd34ad02ac58ffe09e5d0d9ea53c09794926024b1bca0f69c2b4cd25ca54e61aeaa2e2d0660d064a0aa981bc8154abee902cee7b67820a000fb2915c61110d34eae6b562a85fe7792f056ba245c7ee58e9913278317b0240785855553c18df869a0941eef11ba3362f76616b6ea8db3c95b345f5e1fe75e5e4bde5a234610a0884d60e9f49ce42d4e2b397d06b67eaeff6cfe6852c3ec104e63b7a408dfd6a7c4fcd0bebc54970cc1601d6383cec6763789ac52028756db22a56ea97e65b3651aec18cd9b3223af8e4b605313befd28f5e957a098beb09de222bb9edb474f54a82801a69b2fde9ac9e805a6dff44ec711927652aec18c39796d64213b8c3b6fd06f1190d161cd252d09f09869e97b8ca390bd74ef192e4ba3ffe85883fa5ea9840d56469a6f12c4120f79e8e22d2a864e061ce6656c207253a0b2a8f9bc0891bb0ef249c0876ba3818daf7e7f772a92067a3e609ae84775fb66f88343eb2abee46a7f83e95bc8b7f363cacbc2568c84d4810c7806e620712ad0e33bc4a98993500ca5ad10df0b673856aee7b6cea53a27e645a0ea484519ee6a72b10c5e45c94d95335a506cdeabdedbc12aa1ec5c5fa79c5f6b3cf23212d4932d9002525ce48b5aca74e335321b4f2eac9e1319830485af47809789eaf4f84888ea42ac2a49f976723ffa5ee108731c7333df67ae398f5549c05b4201acdc259a76d33549430aa3616b9e2aba6b192f599a8d261d4ec8ad1be6a0080f6196447a425e6eb741ac15e4ba4268df97ec1ef393ca08ba07a64199282c70d427b4da360cf21ac14c263c6f46ff89116a9e8f0f81909408dfa6767942175ea22269fd870a884c1f57f86f5cd9e0b904b1c6e820d2f0951cb2de2b642199d55933fb1dd8ece01d9fca26b81cbec10da27dd4a5282ea9b1c27bb185145cac526c59dfb08974553adbb428b01dab0f40498a10a1e1f84b026073de8bcfbe4595c646cefe869b08cae523d47e597ce7d2663c60696a9d1eb6fc43227fe8bee6514acb0faaf0e56635b7cffe686cf02696cd5c06dd6cf4ef0e97607618cf1a8337169698a065b0937d8516b13b9fb86134b7d463da388472e88e45674003e32107b6d00e3a13a9dc972fb6f1f7839300f727fb01b40bce387c375e96f73a9b1d85519cc7f470001593577a65b3a83d82fe92d184c3b97b307793532959a007414043bfcbe81fcc8ba20ab640e93925ed64ec57cf564bfb80d5fd1808235c4a16fdc2f08ae9da7e70d832d654d502fd41d219940ca2f64f0519dd7bc68ef4a111ac43d44f5dc8d6ae9d6fbf1653ff1d3dba72ca270da5e32d23724809f05d94f3b002e37b006b4373cabc5870941ac4e5a8476fa724b81a027d385cab060cb95fdaa9eb34f24395ae5d6d76ca685ea6972097b7e9fd81cb0603bee168b67209fba35929c234a3e80ade8da15420b6419adedf87bea48211ed9095aa8ee8cb3eca9b12ed6131feec43cf005cd4025ffcd3410f64ea7c7e9067c3b8135628414e27ae96e1a2d9b4b592108ed3f94ee442cd231e737324981f2d1966eb97df41daccedef17f41f4afb01bc7f99293363b4f730f6fab2f83ca4926216a7e08b912df66ad72577a8dfbe471d1ce2cd70b9b6311c67e31e97b1443d4db35d7773e2ee8dd18de5d36f287071254daea2336fbe7bae6435ee45f25e2489bc43960f28eb3949e7abb562ab0f73deef41bfd247f80d9c488a3e555d8c223eaa8910dd16baa7ee545bdce81d04b55aff0d1e033edc6a673a32e74d67015fc59ba59b770bbd80fb7983270eaea12712ae62e8a74fd7620770aba1cc4d60bc91e2a16ee1d8c6e8a883761cf6f214bb8a7d24a9a0518522768b86495a81f871a20cc7633b7bb640924f086db6254e8cb3cc553000338f592e39395bdf87fc42389013bdb474b8077ebd30fe87b16beebc643e5a7067f354299427caf94db49f5330ef9f445b8581dc4301cdfd987673cc8a2cc341208e50fc1d6b39064ad404fd7e5cef54f7e8f06c2f4fda0ea4ef5f4c2252b37e7efa664b4c7d3d75fdafb481cc00202b5be456ba6b9a024ded6d133f6f530829aaa41fab9caa7cd4e529a9a1d112c02b9fffbc1ab785267eeac681a8cc515af6ba976c3cccc92e908894603526836fe3f45e44aae818039c8e6026679d4375b3c32c5d96a4eb44a2c19f71973a547a91a3be11f6c7313ec0192b525cb9fb6167521f02731fd2e0564ac4fbe58a65f740e38783845c6e6883dc25a77d46a6aa8b8ae50f558bc93efea4a572cc5fb6b627a98038e5c42967ac1ab80627c1118c055928ab1c4f8df6b9e47ed8d464d50e99eae0b496373d5c557fe0eeffc81385c8f98c4bc293b92c261f4ace59cba18f25709a255cab90bdb0033152431b4b8dc2e7c1f74217004442efd5e2393e7cff2b5abed6b987bb4d2b30a1208a69a5d4e54e699b36e9a680c9476fe1a6004a905f37858f9b655c50e2ddb6da90c34985499c3c982450ae3291c9f659bd075b561c2237f5c966a986208c2fd3114118e360d3169f42c5012b198eff43c90f90306d9f66f201977a45f36bb78dcc1540d136828676987072794867f59d9b839506bfc17da2bc53b253659ecfd72893f785f56790cfdda4fdf68bfdfdd6d8d5c29de963c3c4781db1cf2099198c63b65e3f5e5e76ec9b11bbc7b1c1e06546f860806e6d60eb0824e57aca787340c0d8529f497a5eb796c86523ed333e52e5331a178ca8dd8fac0bbfa2c6f128ec24e42158b48ec50900574e7cd877ec206fec7f6779874ef95cdade5179ef4e67cf02896e9a9dcb63244c123884e98751f1f15ced5b4001f6ca73254d1f10088482ac7f890cf2258cb4151c303e565bae59e2a066dce97a0c1591f3f77705d2fc3998de879e5cfe011dc2cc967fba5908403cec062436c2d8497fcba45e721b63d58d54f16d9e69a8749992d1d6a21528acde261ad9e71ec0de100e4661b0c4260716ba4300a24fb8a072d115065611cd610355df8903ce3ea1ba2c966e707a017e44365c60a1404503defcef4dbd6062adfbb3036aff1dffd760a34c5282b05cd87eeb00b368453a34dd3c44a8d088b1b69c740d1db232a2e33a73689e4791c7bfd8421f43d8748995cfb8906db57e2b338a4f0ac4cca900a50a4ebb8f9a13de7b5d93e256dd0d2b2c0c28e9f910e331db43fa331aeea69e01dc33cb17dadd5a86e395ea4fd2b76a8d5c472f0cc31e4d11b473cb99270ac20b0538228e5b457fd49140a108b47c2d9f08cdf6e5a5fe6eb233863ed7cc0a3580d0b7e993393df0503a206c9a83b1c5f49a5dec75af06bf9a6316268f85abeac4a7c0e31c54eff08b1f946026d43787babfd6c9a59db068ce63c7a0eb366832e13a151d05046536daed2e33a0c8c0b22ba2a90dc8b55d3039077ea281c4d63fb516f47533431f7f9f84449f7dcb7e2531b3650a52f265f72a5340d296119b4c81d4ae0c61b6acc1e10fe9c768c49e1247d11a289121b6eeadd97e3010b6fef7d29c8b704b012d38b87cb08be1f6416963f598018ca71a0f85d102d9502416171125e81bf74f4350816a9c3139be973450bdc9f49b632829edcbd9cadb43de482a78444d63f71ed084806b1f8d892b2326bafa33b8ffb2532a647fb875113b258003ec7b3a995083e2716230983e0785415c9d3c60a847b03ca8cc7615c5faa1db8935dbaaa742f7ac7434fd86536e542686b82e782e13ab8b972596351cf6da4d4380a6174805d4a1950dd79cbfa896c0799c477c95017b0f1cf592dc005b750df981b47b323ccd575f06fd3e789a83788194b733c3f450c0d565fb2c4f590fa707684b9ddf248f40753aaaa618f5a7fae59a851ff70b6fda34b846c6f46b99459f4daa3d17244a0b67a1223049a6b442b2870e225ba47ba246a11698aac04af94931e0b5b0c1957b0919b935aec10d19f33dfbd937e928f7c9f7986de55699a696807f0ff1ec5b30fd85db3f1b85949484a65a33bb1212e74ae2c7981c89fee0b23c16014ee5fd2335a2951cc68d6a231b0551f75163cde919620684289b26863ad478fc9f211c4d8eb1d133773413e47865a41ca00f19d222dde9c3d15899335e87b2db014041cc37c78e1eb3158a8ca5308bc537bd30ee26340d5a94d3504b2fc151fed2a8d803b53468618761603975df0a2289c1c4ac4d0bc85d41567b2380f092ad01aaadf48e324aabc5029c2dae1862dbe155c41d3094fd5e3b14075f1467358ec066bdb597c6ecb7c8f1b55fc6218fe833db0965761167383ec164d62d0e270110d7e322e1939ed4a71031930c449f5e548e5ffa9f0fa9f00bc993d0d94a859ba359340b58eb2474dd8a86a55922c98c1c57bc9bd48a5c0bef2a89025e1b46f87622001b326751b8fbb7c7ab97881784e5024b38d646d078b1cc2d7eeef2afd101723bed92f4f1edcb38f7628b36fb190be560993f45dd62f015c49ee6afdc748a19b4ead24d01548b2c868beb7190e9483e486788b2c21ecccc073bfea12cc60ebcc8c3e8840e6d9ec8d75ddc4a8f0411940485a406dbf1e919e4a134f87923d5f27cc5835a3789ef84d72b8aa126c204b34aab41ab8c18d24d0dede9f0f459503ea72c6e93e92c7056b9e2b6f8d25869207d36347ba821f32fab0e9a86f6652ccc9adad9158f345a1723a80f9a5f21f65e652aa06825f1d19899adc2a34c0437c090319cd72ac205fa285ae66cc511719c655b7e0f73da90e9281f626008b29addac1337230cf25cd403efdc0e9c870105506e32d727b5305d6aa8388a5bd2743ac643ad3e44a8844e296025fe0f86c7f20715bc0ede91d05a2bbada87ed15492f0aec3ab625f078f4106f77528ce3d0b8bb8b2dd4a10e7d2bf5a2513d5b961ebcfa05d33d4d1c61355914a606ed990bee5483109b13c48f9bf4a2208ac8bc2c73d7920d0c2ac7c58744fbc094f8954c61a34fb6ecfb23501d0087690f9705999a5716854673f1adf3c9f286cb375c2e93d747c61c5fc001ffd7f03cd56366ab31d89cc78c7e4d6181348e84d752ba7203aab1210355ba6af7e828b3eb10e69bcb1d5514b6ce14aefdc8b1e289d863c9ee88c3ae8d6d10a2ee5851a708ed55c28dd9a3d23839f62aa382959a28a6239c075a39692836e6abde6ca4aa7c50cbe7d261362343bb1dadf28a7e56c2bf7ebc3cb743b379c3d59af6c8012a7e0e40efdfc7ed884e4c3f3229a08e094cb9f33a65b97b4948c2393070b9fc28502f782dd0755ba0a9f653e6b1bf9dfb4aa555c57a4ba4b7ef93f555847be90d89eac7d46a2cc91c6e789a26483b3e6c34853263ec3a83257ea8437f58a1a7a35d7706aadf5e340793fc8a3b8adbfce770a7e062054ef5204c9ae7e6a7d08469b116986209ec705b1c51af41f3683cf47e5431ec5aef87334581bef1dc8f593149d26209d214c0d6d16c015c769ce3eb53f3a29f5b15f9d887a6a467c2286b221de38a6c069d3820551fa336f5d5e029fbeda77510bb3c994d4dfbbe7e128437be4db6b809f061a803d0e76e01171927f20a85ef1ac387545df3be6d761510492a18c0b09f779a28f216223af51197bcc2260e247ce17bc155b5a5ce7eaee066916746b9854fc612ecfb02106d02d50ad977914f16ddd16d603a23fa4a2611313e0c26b8a49d77cb0e79dc8c9d7e1ce469cf6be7b70bfa0034207d281e9156dd8169d1455f853a394dafd79d196747d42ad690ff13a110afc6e6304b4e790dd3f5e2c90c68554a5079a55caa9775e2ee402d3d9a2c2c064095519d71749aeec205911b8e567252cadc229dbec09ff91a7ed05e424fa1c1fc24100e6823d4c9e4cf61199f47fc452da2d031d398bbf5a87a7c0484f85bfa3add1bf98778bb52426c5aa83f450988fb453b1ed53a634fab4553cd064cdcf22b59afe09447f89424ebe22a999c6f7e2221c58440d90d48f08ad51ae4118c0a4fa20caeda8a7af557866d0570513e00763d48064c38b818eb05792b978cdea03bb9d40b713f17241ea7f90b22e21c5ff643460e91e3fb2b6a06a4d751f0b388ad2dce971ebbe333912c908b8ce0fa0f309beaa16f01d9baaaa758d8a0e36f43c90359a0a8897d8b42f506016f0a4524d926abf76280a0db792216b4f5312c074a3331b6221ad320e5eac00572ec302886f93dedba3268fd1c64b3b243d449b7bb9ab9074008e6e5dd57ce9417e3ac6ce2cc79407632f2d59739fb14ce8b8df6dc21d68200cd366f8577d0afdd86e761597b04f7e34be6afe784fe71ebda18d4ed426aea21f09e69843b65619da01e6e0444937c5136e9031602e7c349ed72816b81a7834c50e82d90ea1398dc273ae0af574758a8d3e7050a11f78163b113e5c95e6201987680f14f223839c3f15de97da839a48dab24b721d4a9fdd95fc06f927a8382800c392eba47c5609507962ef66d6a242aa80e967a70112b59c93eb7bdc97a02693b0187b9ee22433a871a952b928c3f3bbff06aa19fdebf61533c16a88c984e0500f5b515c81805185d47691619accbc001b633505439c592cf6acf3b95ee9e7a1b96531a926158f17c86638e4f3d601861d8bb0d1f1542d79f734a087c16002d771b1cc30d6829346c786304d036fc29dab3ffe01622255d9d27dda22199b07e2a23918b26e0688b2e24a599b04743603b2d2e1ebc75694eb0915f1f19163e1849aaa68311e90bf21a21b58873ae8fb21003392df8de72296c105a93e99400725556055eb7c629e9a920639818fb7e4995bfde83dc974e554f5054257fd0c91732700bc953cc2e881d35cf305a2fb34af84f7b4eae09adf67d9315f114afb7c9ac8807f3cd32ea6425927faab834d8ecf8024c0b4d569a3e6b44cf659087dfae3b5f21a824b40db6185020f1e51c6f9077c73b85a8afff6688602122d07a5d46a3c24a1d47d86d9227bd33335f00b5a01b251bc5d3d514db63ada79646225f0c682cc253e03f7a1867d3c390ee122e60de2331a59f3aa7d3f0524590b951ac2e0b71bfe66b8527d66379b3ec1c9cbec8714b75aba55b1899500b56e68e97347c0965d79a8e4ed34c26d962af97b571d2f7d10d79f8b43c0d53c222543fb21e2714234e0ca99d164bb136c5e3187085be0ef67fde64fb28bed98372d16f277b6a5f9e45b4f642cb266d3e631c601ed404e332c3e711bb88e7548bb2e52df1f3ea1cd451ca27bd3f75465c0eae83d3b1f1a44d62e06a69fc3c880323295f4d14efe637b450d115698e3f49cfb45f5de49c620e086551b248c380937f7c6fe59c11a7e6260e9b549ae3e37b7ee3e70bf78635cf87e7ea8ce41de759ab35eb7a447bdd8ab43cb0ff01c60109457031a83c4cba817f2eb9ce134ca617d5a20a1a898e3736fcecd9f6409dfd59a38ea2b14303a9ce6316a231c137ccaec1d8977af7683fca4f0af9d6c5eb49011968d692b655515a26a46fb23ce73b1aaac987f9ee332605d07070edc6e2f9e6916e4d33d5b41256d3e831ba9a78b5b1f6da87b540972986e6df56eb2473f8526704aeaf6a13eeaa2b10c6650d5907acb6c0bbabadc1892d9634cd343e181ad4095f3b0ca4a217d3a962e0be8e5475808664c80093d63f431275b7dced6d79607172a98f3aff11264412ec963bbc0856759fdeb238bda0c249ce0c6e0addff7fbeffefffc83c9ea9d0fa35beb894f2373aa04aa7a71a84dc0d98b8d65940425c56044923154c3487644ba2084d2671413527b087a337167fc87ed92ba211997e677444b0c73dd2d4e3989431f4408f098f8d3833fc9839d54583c0481d0a19165ef0daf514c4a8d3bd44e52df6a1d19663c6dd05d2d3444c528fb3374a49ac686abc3064ba1c339c6927d254f11e08e51ac97b2f0795a4eed0486a50db1e9068e15838aa6b77f8a66604dbea434e264a5308bbc98d9b0524add15aa976e6e9c203a766af6a2aad8d321c620d4ab58781e50e5763c080a955130f7972fe41fa85fd4e8f7cb4b64a0a0fb26d1edd99ab7569e77bdfd7ae6dbe54da1e0b67cf679c55f99e560beacd8a674f07bbe5360c9e9ff6525b10da0c5d5a058caf188c2125519de2a13c14ab201c1685abca7434812d1485360a62dbf90f9c2d172284819315531d8842e938e2a83b64240e295280abefe2ed2fce14fbda619804a7feb9b61801c2d7002da7ff60bd85761c99dcea21cdb131a789a8052c203f6b4eae1f5008fa38810261748b15d287a714ad574e14341c3915487085e966712438dcd2747d401536ea944097cc87a3a3bc7ef9a2ca553343f9363ef8da14ca0e203516d61ee6c06433b6f4994cad916c150fa065a6bfcb35f96844f89692c39cadee189ecacfcd1e193185ee224618a1e6ffe0d926d3528b782c145991a05e6348c0d1247bf82a8a94a624ebe4e6086fccb2cc053012470cb7c244a48f8d1ae466fafb9783f42d0a0c730925dd60c8e794e83e8d4a67e555474e700e5b33be912c86626fcb75f34626c8ae5a1e888c2f11be70104bfb2fc9aaccaccc57c8f577c815fed131a24f901bd2f886756c6d4c0cebad7304ff097701396c96312cc73a9bd2acbb9446da7a73364462be210521c14dfb6cf609c3b7bfd86c8f7f8bfa5fd51c14b1e808cb67fd52dd8c1bd7b64cab72db5848ee65649e636b950b5a91b0e9ed8399a6e0ae79f8d1ae390123a20f40ae4e7cbe6058e5107917ddcef696f6e2f28de8f713f155e9fb84d130618132e30bf70e7353992c14423cfb5d51005f98fceb813c086451432fe98bd995f44fbf2b1f184fe7860140e951b69733767518b8942dd1e64ea31d47d8c7c128a6968a749593ae7dbcf5a4cda77c0d7ef86a9df08fc15d198ee22e7e201f114456f08a481034f660a83a6c03a36fa027b2e80e98ad1f1173e5f58e5f931e377a43c419156c56085a2e50d164ed59103b788ff3da2b9d15454ef23635a0fc625bfb2d38629caf3f642388e45d804e28251a7c5672d99265ee4220f06bf8b6d0755066505d7f04fd2cf25233548634d6d0ca0d793572b7ac922f19347be3eaa740daaf474a44791873f78ff06fd4bc16b2efae7a924b3bafb6e5d97e94233f2015dd9fc18743235f989977677b7ce78b32f6f5224eb54c4f0ae39ecfff28efbff10caf80e928d42b58dc88dd31c05abf4d3574ef008afbcaa01ae5ec4f74cfe6bb6716ef99d77ba25704f2ade2366d567f72b220deb8bcfeec24fbfaf567ce6e33a143e924be18fdae94d4deac9aaf5faac9e462660094fedc6f50155207f6af3883a5ca20a36b2656713df7823453bd207672a6a8fc4c25f43660cca412b5baa399feac0dfd55f7037fcbe9448de74a2de1b6d984bffbc154df830a3bac1dfd21bf9f752a569e87a4b99ee6fcaaa3ab001145194347b45578aaf2077471e69aafa45ba9bb53102ea8ef2461d6134c62b63b04e4c0e9f924fd33bdafa5b06cf9318b32c287fbe8139e3c90a44ddb41e8150c33d0b5bd635f6f1c72982cd9d793d385f96271ff2772ac480c6272e1311bac5ab8b872ee943cc384e4a18950c749e25419975a50e0751ad8a719bcfd4051125485f284064e2adcb96a5d52d9963286a0aa9642f16b1806f68b5dbb883448a0f822b24a4cef29914ae54b63be387946054de885d1a70525358e42b3e725fd503d731b34696d2a4710588e12024deb1dad1548047b7c46584a124a889bd4f1ae71e647cd74df5c89c3eceb31b7cf3cc892522fad9e3995866f56d79b19d2add4206eef839b86a70782220a815a994e79ebd14b5f043bb57d24a70639f54815efef00976b62e7e48fa80980e9e833397a0b2a6dfcf2b36d704bea4655a63fae7678d4c8a0c2c90c2725566406cddd885531efc64efa726bd804af61d856847263cf53083637e77e7a72747ecab3e69402f1a3702c16bf9ce311973f29b8a285f1a6668532570a7c966dc7b598e27c2c063eabc9471dda05b4f6861ad22b731233a6f67290ab686fc4d4cc2cce585b32e05f47c43c360d2197b709833773cb06fa41377b38cb32cee46655dbaa4e28f955289d73262bb1468dac7125f02ba3fb4a8a0ebb846a29292571c021d146fa08059af7ab64db0878fabf45023cc892b2ba1f1b750df8544b4c2b07df6ad46a894c9cd9f8874df14c8a68e9c0848923ae851afc1e1d680d9286fc1e93d6091a22cb9e94b84049af8ac97778b2d919c5a657790fe94efc618203819313239745fbc64b50825e653074b287d007fc7fabd76825a6e657861446f49601b86da4a8c58bc0b62db440228060cce5c89d3f82e9b3459d9f392c2a13d47131eca54135e46a959ad403bfc55c0e7c8501d2878be286bf0047033f0a56e49e3cc5f54497cec10d80cee1e3c892395d213c6cffb15d90afd4f0cf18fb4a8bc2fcb47b42f38f76fec1706659c579a415722b72615e18cf295409a129ac9ea0d38e91eb40a0b7cc39e6ee30cf16790ac3d4d0488a74091fda56c79af1eb0b8a46ed1a58bb34cc7508fb11488070381ac8654508b9e1c7cd60cff3da0848c77bae729c944d230d36e06b3d3bf39f8f66f05cd69333faeee234053aee68c7e145a31fd19f078ea9666baf02605a25cca58084ea1b4f8fcd52acddde0bc8addd7d6cc04f1b127bb2de3dab44141aa83c4f82dc21d68b3169f35694b0998c1a4244a5c0ded9f62ad4ba42d3eb9120016c1a877b13d97fd2e7fa950c39671b438f3e86cce6d01e331e32fe87b44fab247fea277b463b27e3923ea950849f0e41a7581a37407fc0aedb78c9c780c3a07311d1e77ed7619fe0c4a73eb4dce3e183b0201a502685b4e4bcd91bb07dc1767b75ca262b537691c16a13b072329fb20446d5b28a5e67227ae3a025634923db9c4263eeef0c616e3cfe6a467f872184d24e747fbc7c01c34862d10bae1896d0e300aa5b58d2d779a1663e495cb8202e8d2aecc079ef537bc4affb16f49efcea6600dcd7154f675ff5c7411489c4224487f8b5fb2bcc916492424a5560347121b3ef30f6033d503e5bd454f7b5e30cc544700a4e08042ff56c16e300aeeb5986caf337e38e4be660efb4182a6bc40f76d6931dc754420e18bbee7a6d785ffd614b17e52b32773b979657e8e07d89bf0cfda881f067441ec3753800fe468079e918e806979b629a67dbb80b6d19c1111be6d20961f33b6b8dc145dc4b40e0ca3d48d600de4a5f01849b11b4d158a4f0c99665e21cc43c40fecd3de3fee8b7c7a5337713d5038da809268e851d211193ccd0132f2cc07aaacea25523859e036c4eefcf97f39205d2c1b80995c15163c25b81fbd8f48c2532e17ae8526c5d997d2dd7a7b1578ea5499b9d8ba5b7dc1e613449b00bb08d54b18d8cc0fd2a0bd79b659b9bb322766650b2cd3c6fb15f0eda11e9e60c710fc068443ea6a3f04b71b9dfb90136957370ce29b3ddee13ee3c51ed2dce23906f51017b5fba93ab84352e87ac8f9fb42e911e0aa9ac842b733cba6f8641ef6267f660aefe98730dab0964d5145857a966e0e0729ddcd8b7c62648906f90f5f1a8bf850b34a4162b07ab8b7f64d8503198325e24806182c6596fbe3d0356aeda7184fee26397672093458dd0de8f29d03eb0bc6232476944d80d8b8347e3c2dac541b6ffe58258fc033bfd55868ecd68c1bd68c98ec3c07caeb28eb3b35687c55810cef5d47f9b448cbda96f17e657c04c4a069022020c8d2f6f342d2673a055d66bbcfc7c211c829a3ae01960edf75909f9c8a90000e10cb61b76bfcbf2ad4f13b8a03d00909893b020bea375e2e10f4e2597c502f0602eb387723e1d180f72a80a592542eec97cd6430680a46dbd116fe1ad2e590487f417d05fab0a81113c3c5208b257dca6ab90a838d3a04c1ecbf46cb956f5c5e601e2b5fe63b85308874bf80f179b1ce75c2a2cb0734e868d970f3b124393a1b3dd850d0de3496ae5bdec142e92dead53c9d8333d125e91c389729ae0cad4991435f66600075a49ae8c1ea79ab161cdd8af666c8c29d9cbb0f59a98caf384391f649db0a501a26da212567b4df3d80c14072820cb2a631d419e206a05aad0f81783c818260982802329eb444d930749b740a619d5875d4cf3846fa681f3f07513fa161a00c94001582ed08e8d359ad9856af649fd9f25089ebc9c88bd0844f2a4eede79474781ce4450c82411486e8a67fdd11744ccf02dde51dc3e9df92171319af25b2524da5202527d33429514887d9c801ce367976525215635c0cb1c874a7448b74b43449e5e33222edaacc4ff50ddc9b0525cb695987b61a36d6519cda53cd94ef40a09b105083419d71c2d657a86f7f31a470a09734dc1933c2dcb818656791f9d2dd3fe977de1d117893c3061e841734b1c8400325ec08b2a3a02331e6323699a1fe83af84d7d9386cccde48404c015720d00a289a50d0995eb88253479929167bfbd67444b2b13e7fa9f13747c377e67e86631687033e83761a46aeaa707c1136151bb8cfadc45cca111cd81d4dfe1017c7f5b5744ada666b8c30ca5c0240e2315f9085c5ca41ebc769d5a698e9a6859787c2c330314d805b99cdf557a6f4b3eb1c344d7c4c1afa3d9dae727cab05d351ddd0fa24c80ab1dcc31f6d2061aad5538adb3ff77bf9e97b2aa3832024c289cff749993c29ff414f112d108b1a3e2e50fb541e91fbe51bbc192d8f37ae9965d3f4c6b66372570bd294ae317da876eb56e8d536560a2a55a57b2aff10b0b74145ab26cff5c4b63c003ff99c44530c833c6d454abcb7d4926b19be89a0ceada16c83587d595a3e143768d81c529c611774be0400308835ae471e1464a30f6634e0ee36d6407c3de8233956d23d39ae554e487ca6d0433a1e6d4f88e4640c24a0b1bc0673caf18fe370c26f0295280c5e4123455bbd91b2cba8839e28615f449036420fa880201741417405a316480a6c4f5b120bd72db7f1c668ff222e3aa256ce39d137d9397007c9038e72b886820857e6f9e944a04181328894385d1b968987469bd76d3ebc1713007adb3ae936b8c9c4e1021a6ff66d6b1ecad30452269402c169280e68a13473aaedf7c2b4c0e39866bf45847e4e9e815d20a589130aa8691b4bee7ec0d0913668fd51ca81e185dc0881ee0200ba3af4b8e3c2c1b3111796f2af286439756d1fc4b827041563ea917f12b1326dc4ba1415670d72621b9ba1184cecf254bb78d209d339921e3296a855fd45d93634b3878cc51bd4a4842947db2ea2fb1812feefacbafc04b68a9107c6ce3af538b87559fd2310671f7cf00036ec9d161e566c974b5d8b71124018368037e18ee2298cd42b7b1fa270b701d7445065e54bf8aa80660e4b3f02be6f5c2eec9311fb01d0952de884e198b5b4dd88fa9996e104efb708db3467cdd9045cd60ad094aa2af56fdb358bae337a56dfaad4f4ab9e830db9a31c94dae4ec268c91644b0466ab3fc0d652375275a5d22003283c9fddaa02f95d53781b4b4145c1a642325548bfbe706e2362826dc37066233f9a0c4079bf399821809a66a29eb45adaea068e0e3a13d68de3fb3f40e8f7d9094dc541608334be9e320db39d823e0ac2d59d97dfc75459d69dfa76665cb126d695fbd1862eb6d87b5c4bba4900ab0972058d9c738f7dce54e5a23af896b6996886056fa8f9dba502da1a2f0516ac17adf8a35f7f73e0ec478808a80f38da300fa01ee66168a2690f618cf44daccb00d3a749d7c237863232e4de1c859dccc2fdc55bfa138392d84d6d17b4d3932dd4496c4dbb2d67ed4b13e37c0989f1384d95287ed8256510ce8a91b9216f3539f1d3a813d35321f9f98cf202074bde8815bbd2f4ce0a2a1612ee0962eb20a25120dbd02699c461af0aa0d93f8d248b99d444ea9eb471cf272e08448e2547f09ae9095eee387ba5db06bb27d0da6f756a35c393b84284a69ff3316298797b5071dacdbb3ec9c52c8bc6154eeb278e3776887bcec0a2ba4c9efe05a94f37ef89931b1f2623b321ef8aad21c5c157afd873833d3e4e7e379dbca470f2323c9a9993314268b6e6fc1d1359c0f0bd0f41c6df2e1750c72d1ebb9cada386d78c528923e70e96e2123cd3d44696c446e5405bd8492edad9cad97a93e26d2ea4859e35d932a799c4dc81474ab6837215dd8e39556509aea78b131938e01dcba1ae1024af2dbf5b708e04c01dfcb6e829325ba02fd26af4929ed30bb63f03cf3433d99f8348d77364684c9bdb52006d87ec08510a10d1bc704bd58371c362e26f4f0763caecefaf97a104023645ddcf17be89318947d94f111d255c1d970a6b0ce1ce803218e15fdc7a84e6cfdb0729cb5048940f658857abb8abf70c631b56ac4867d106572efe443a87446c3e811e786c2cd69ef9bb7ba4f952c1e325db2140cc8420d5e7e227d6387db5f7d05c1775c7a16350c7a55e62fa8b6cc3eace5cb86a9a110cd9e93dc2b825645f11f3013da6c48dcb6246fac2f05a9c3fec5cdfc013d9dc0c1591f37fc3a5415a9df1eb1b13fccab8da7be3020eaf70eac2104aa8737b65620d0b29a0eaa896de0077c00dbcc5d71035e5f58890f7e47cec10bc1a3d806a385c9577a4c1a024f715255ce3c33ef16f9c95255e9cdad1e9e5ab775e93b2e96b6cde10f7ba505636abfb55ed2e4dedde4de52a62465090b4b0c1c0b2cd6c8127a668daca267d6c8427a665121875031e6799565956515e6d957ab714534ae56abd56a5c093dafc615d2f3ea8a1c7265ccf337fb665fcfbe0fca377e4efcf835f1e3f77ddff8093d7fe357f4fc7d43c821438c795675914ad545aaa767576141a41a55a36a54a954aa5125f4ac1a5545cfaa5185f4acea27a97e92f21f5362be71e09be963ea8d6f1c52619e3d8545512a688c1f5333fc984aa566a9a667979c1a5348cf29382e1c37c88fd7a9e979bcf71e753fdef1d6f0e39db31fefbd77bc42cf77bc45cf77bc48cf97061e0d7efae88d79f69c9ebda767f762a3377aa3e7799ed7e48d9ed0b3377a45cfdee8213d7bb22cba2cba1f3f764a9d52a7d4755dbab11bbbb1ebba6eec849ebbb12b7aeec60ee9b99331158ea970ad1fb923ee883be2b8258ee3388ee3f80714ff80f2f1238a0845842242a1ca7846a15028140a85871c82c79867cb316badc562f6eca31ded68476bad1dadd0b31d6dd1b31d2dd2b365430e6163ccf3d6b4356d738bad7edcc68d881fb771dbb66ddc849eb7712b7adec60de9799375a1cd2eb4991fb5256d495bd29e9e5de3e179d482344dd3b4d9b31272c85462cc57a36a548d6aad354b96eae5d92b0cfec71a34d6a35a8d9ac821788c79a6629ebd6f9e9e7ea1609e9d76e19d09856fa64f28d4cbb3d323fb237df2231de948472af44c475af44c478af44c65259043e018f3dc6296347bc7c6263aeaee9e75d373bff1ecb287282087e021c718f3ec238b51d2374e4f8f3306470e993e4e3c9e736693530eab7ee4a06797231379b1223b33318f2cf4cc6cf4ec9259f62c4672f1f2ecb30bef94c037b3fe646f221150e510dea1e238bf807996e314527a1e2711b33379fa0c3008c61a2f3b357c333d29c929871d0576cbce3cdc2a6266cc9702e2e31ace27384851675d18954b54ecf8f6d5857517c974860c5d545f3f0bfed854c8f87629e6db551f77c7f4a2c78f354d94b1650ad19436be7aeac2aa1760072e8eeab023890674c888c186195464b9c1151ea68f7289916620ea544fbea9e550c4e2741aeb1da7433d9d12c56262133d7c3b6dbf17d656c691348752aa52eaddccc1ebdbb90b6b0b64b446142b424568d1032970c490411b4f64e182cb1a534c99415926b0fc906fdfd490b3e9e5478f8c97a38d90af9ec6d732beba9562e5ab7f4fbe7acba8065fddad7cf517ebc8a425eef8ea5ace9312409421c6072218cc40cca1a20589389294b023688b58fd655fa7dd51e89d2e7249af7c751ed699556881c4d2b35305f9a97a98909fbaa8777a86b2e21bbb975867ec2c3f5d52b1520470333ce518107d6bf13d03e6d30193a4041d01f8eac44fad26a6b7b6f85a417c2313fdf4151d5fec03a2c5c72222cb4f670df1714d7c507e3ac7c4f425bdd3333a6b90f8250e9dea25a0505d857076c5277fecd94f303deb2699301f1029be913e90283e192abe91becc956fb4cf32b1a743e63ca2a993369d8c0d5e325f64c054a7355a1f1a32b396619a6d9ecc1994676c6a932763c6a7d45a72a63a43793269709e0c0de8f892a941e779326a7ce34b469d3e2d427999b4bb9b524abbbbf98610eef8661e12fcb142c9ae55ab55ab95524a65efccaf8b007fa0e6777733336b61598437a06ddba62829d257a197e3cd4bb72af44e8542deb0bbb75a150a0148286472a70e852d01fc58a1f896aad2f374ebb5d5a24eb3eef0d4c95a16a953efd4a1a64eecdcd505981494ffb4880a99c4205ad4cd4f2aea939f5e87a6af889e6bd17335ea9bbad5d033513bd8d86c9962b3856e7982864eda8c75f6d3e5174d2fb77881839cbddcf28593ef2159a4651267fa6ca4be71e3f357ef7061f0b0a34b4c9c9ed33bd36905292dc290c1504c9c2c9b44bdd3607809834b4c9c3e8b44c6238b0c3c724c88b514049898389d897869e9a98967b3b9c44ed3a7d13c9a8df4d37da62b91620f75c77a787a678a529c8cc4483d14fb39a367f448d9b66d613841a30739a86802a3a8034b7018518710321c7d09238445181c7e23e137b6f2dbb659590f0a2e084389f61ed6ab63fa9c93e7cc82075f9ec3f1cbd1828a75a87bd2ee66669e73cea0eec2f18daf6f179243c408eb4806b2c637db1bcacba42b825ec8cb242d63bced9d1ff2a6bd4575f8c657f5f69cded1c4eea42baabc4cba228927c0cb242d48fc8d2cc29b46c314a9c89b9f196b5108bffac5aff6975b828a5e7289b9b174d2a64bb9e1752ecfe3fc35e45f0d11d6914959c4fcd6311e7e9cf96d5bb262891fdd8aa01f85fcf672f3b86e765dd779ce71389c77e0880259c7de756ebf43f5300a64cf79d7c92bfbcee7d07717e6b2efd1a5ef9cef09b3167de75d73cfee6e2f35b6e0a5c4e9bc03d9b99612472eb5d14922946bd5088119a99ab6e4ab1643cd5038859c03270e3afc16f6f0f08b4e91fa1c6a0d519f4114e542b9fccf013e3f19508175504e1d042db0ce8b097398a795521ae65895fb135ff51a7e51ab39aaa26a481d067e3047fbb1c4277f7cbd84b04ef5be30cd3b6dbb9badce30f0d7568d771a38bf360cf26a2919be2944c43a46aca34d1c86a250aa859e4dc1f11d7166749b4fff92b080f9cf59deb1c717769def0929964bda52f6ab70a4ff39ab8b9ede5141ce687c6c61e6592e5d8599cfe93d42e35f38b29c66e5dbaaba8ad5e51ba55bf7bcbd8705ce2f61dbbceb407f19d09f03fda584b12c4d38daef0bc7efef95f1a09f710ef3d3cba73c090b974fb92a094b95a771f66de33c568ac655e0a8f2158d6f2dfcc702c799135c98639ec6ddde08ccb07cd607b2c00aafcaeb45325de53420cb7bfa822dccb854a10929671628df7e2d7fe5fcd6811e38b27b2abf1eab8b6ff346e9f04956385a7a91a45ce55dca039fbecd5960a7f1917dc6db59bef90c288fb0d3841fe8bf5aad9c5ed88a05fadf0868ce7216e8358b15c63e16cb43c2aef2bed4bb1baa40d711957fae920947a09fcf9a301affc0912634414a9c957fded32bd0f31950c63fc9afd07584e52967adae5b4ff5b40a4c85a035410b333f27a7a66faa79ab3095cca10c2d73cbdbe2d37ef44711f18db24912cdf08b8675885f2c71b8d690490c221f31e744a307732aa16952e4e41ce1be91a01aa55650d0bee5e85bae6ada6b60fd0d64f9d2beb2e0bbdb3814ca8a9bca7df599826f9f1bc8ff84a6d918a594861b08bf7d4a9c1a7444efd03429bea9c9a8d391bc2c177cd3e5fc2c659d9902219f2f9d1ed51885e2a70ca7b387f468f6499f4e2bedd07e8dd5e1e455d3e5653d814d253be79ce11215d3c7acfa40e2e3576dd58c2f9e3961d37dcc3e2d9c204dc1a03d4d81f6348a39b99b525aab45a12eac88e3b8aef3bc7b2f2c75612a55f5efe9a998d6cab2eeebabb669329e36a3d1d0f4e8317bccccd0f8a0f1e1c3c78c0ccb878fd5e7c3870f958fd4f5e1c3c725b2613eae3ee79cb376adb5d63ab260aed17db3270e3a596b2e4c5e5bb5cd56d048e2709df7dd6b6994a72e2591c4a9201418f5ef9e40bf0e499c0ab6565e0559b5ba0f23beea3d6dbdd595b9deb7d519af823417d6d1781544caf2d47b5c5815f6e163faa829d711ad8663059eba27d3c39bf1e1d1dc164dabd59a9161b55aabafd56aa95aa9db6ab504d00227e3641c1b15c53c39668e67a7c4711cc7711cc709597ace3b97a9eb5dea3d2fa0e49438e75c72dec31c67250ee75ad882b4fc6213b49e85b6592a53ad27f70c4de0fd0ce7cca3ebae7cd3a5d7499b518bfd0401bfe65441e2ccea3dcc4f73693a13d36cd6349da6874a5f7c238779622789d3cdf41d018ef1ab9f7ea4422dd65efa468e7d07b19016eb2c9db659ef9b73935aadb5875f1c525f7a16a473385a203b1b899999970022bd72872c4816ee033a64a17a7b7d000b1d7afec3ca37c3d9431145d82fbe96384dbf289de48ebdfbe6643ea27d8756e23491ee580eada99a56d3f22df3a87201a71257af09afafecde160f9c4c77caacad32a52efce2b9545f7f3d50f6cde633655a64368cc9b40e9cb2a93497fa86697e91c9a6923699a64ce2b04c268bc8cd166166e76e59b1292559e400337e5114c7633b275cb7f39c7bce63ede7751c38679f5bea73d6ba9da7f3349c14de3580b5dfd76ab9bf667db389b9447efe9c499c2abee9e34c79292559446e495def761ccade7bef76af562fedcb778e909a338933c66cb68ab5a6484384d68b860650a6c9a997320a5235cdda78dda86dce4bc93e2fe99bced34ac9946c14df74bff24347a3f222e967a176d31e9d100daba8e3aa7032783016e9d9ef2ed64d4f86ae8f5d4a248e7419c6cb3166ce747d12473af7f75dadfaf65bc87957ea57754aed6982da5f8636397cd281beef11fe9102d49b606a890f1ce7dbefa6c7f665c71e338536415fcbbe93cd1cc74db9a55c4736259ab679fc6192d2d3c3c026a45e8654e2a0784ab03e099eb429ceca3741d79438d2e783cf73d2969a4c964f7a27dbe329ef96dfb4e8a48d521273ce2d9ca6ec223b9652fa09f3a5d7d2d56d64168bc564a03fbd04d691495a88f9afcb4f3eba62b6b1050e3f1af92995587eac33cff82965e842425ffa09f4e5943772b2ec9409fd1cce9e9a16cde88b6f1513431bc325073694e1c5a6f4ad6255cac6447dbaa262f1d91f5b461f855299f82c5105e3e38ade98c126c416c4b78adde063c140cb678fea179f6a6933a24b3e4a94a4f83167b6a5d336df643d286c2f9394a6fc98d332384f6572dbe2517afad6ca7ccedeba357adb7a2a9de8d38fda1cfe29336b9008e8991c325df61a4e5e021151110329b660c3488e178852481334107a7242062836489f45d38815860758987060861148375c88949863075a909881384ed91746981962d0658a229ac4712ebd74d93b38522819b1431833d47012c7c964c68f73c6f492cb906ea56cc91c89825441fa0c9245e84b67b28225d29548ef91fe92fec9a3582ce6830c23b3c1d63ed3721dbe7d7c0979225bbadb5520c2e35bbe41e3a30c1333e3bb470be1d0c3c70985e8db67910e627c1bb58fb34a4f2edf3dbb7cb7baa3a0e1db1b888f33ccf753146c7c07b1100e50be653b171df1cd49f0946faef2cd59be9d653fc4be99cbf7522c16cb411cb9cbb3976f9e1dc1c53783f96e7ae3dbe52387f97e3ac28777b9cb49605764fc2a88af562e5fadbad0e286176c29e30533f041bc63883150a0f1c315316ec0e35709f871e65d22fc58f3aea238dee5721901c4bb8c10e35dbec285b93c4890204182f8082e2c888be0c2563c355074f0351e820babd981e96b1de32be8d573869e7cb5d54bada18f12cc8f328c183f87261400f838a34ca321a739258c1fa7d25cb2a9d5e74cca575575595d4101f091711822e2810e9a1f394a902c21f8c85c58e9abf3520f4e7c652fac53e3d5398c007c643131a11ede70fdd83880f1631719b1ce07535e3e761524d994d68fcda599be7acf1acc8d8ffd463b7df57e4ae3477539d2218f22828f3409a3a31bbc1f6995a51fa9d257a74bac338257a75e84f848bf54a74d5dccfc48dff8e2c71afbea55a877ac58bde2f858a1107df55a04458daf3589af5ec85867005ebd72a9437cac5dbed6a5af54e6a882c41847c880431ae27581173dd400872e74a0e1460d3954b9c11c398c1147290b811b32440963071b8e98a3b214335419a3c505691051dbd0e109283448b1832e62d40d04ed50831769c0c08921b608838731547a108a8138cee95464c44c3c9364204501861051e430c306e2d84317788206279098ee88630a716ca256ea91c6be3a1502ea21091e7020068f1e28d183385224b9032a7470c50a14258c8881385219d05118724859830b29d4c8421ca9d3521c413cd8c11b76e0e08a38d2a7af3970238c39644046102e88a2fed0033ad4f8d284143371ac475f87f81007c1850d19c00006308001f8072e6c00aef3385082781cf7c085e108f1c18e17e21db83021238c30c20823b80a173682085c5e04e7c08589e0eeeebe810bf31b1f7cf81bd7c085ddd823f230c3873bb22c71460f4000a18b07610a182090e89294850e5d8820f8e0e441f00c5c1808af1eb4fccb3170612ffbc107defac0ed071ff8052eec0301f420f402700b5c98006a6a6a6a6a5cc985d524e043e0e1ca87e015b8b01082040912248827b9b0201e80a13c3e004e810b0b80cb5b2eb72e31eff29f0b73b9cd196fe313b8301b000c0df10070095c1800402b5e76f0860e6338c1c3e32e40423c6909c389063be89285b81ae2e1579ec285adc21ddaf8d07d2e2cb4e083533ce811b830100217562b93144b7ffddeeb364258a7c6afe754c1e16f8f2b837b3d03f7837befbd2ebf17847befbd41fc5ebff7de7befbd55d67877f7075c98db2041820409e20eb8b020205439e241702417068275b95c2e9737e0c25c38ff811435f80ffcc8857db05aad56abd56ab55a390a17b60ae36b3c8faff1132eac863ec183af6e02ecc26e14443378cddd868a9a13e189e288d78cc8e095644082594285b682122589d7a208e335194d89755caef9eca27979edcb6b5d5431c30f3898a28e2e5088e3f7151883a8075a4011a58c2822d00f76d45144105958a165898c20bac883083ce4f0421ca52c36915c51747997cbe5f2122eccb5f2d6caed2a0a297ee50cb8b0d5bdf7de7befbd2e5ed8ed5a5b16c50f51f4f0d5bfe7c2b4993f10286cf0407c011706e4c78f1f3f7e78ce8f1f4ec6ff802206afe5bce60ab830ed6b59288af8ea09b8b0ea462eec074fcb0548df72045c58ebc99467b1582cb761790ecb054a3c8bc5056be52c96c662b19c25c293284bdcc1041e36c8c1fad2c41552f030431d5a583b3fce3c510ae1890e5ef32217a6b5be5fadbcb572bb7ab2e5577e800b5b39162a2192c88f44be56b74e78f0d5bf2c5fbde5af1ca1a7af4eac7c759e0bab3c405894e0828d2b3ec4918497abf4370f21ee10a3498d1cd40146bc4ea0f8eb3b17765de7c2583e74a8e27db8012ecc879df1d6cccccc8ce7ccb89df97e4687267e26873b5e23c06b4ee4c2b45a6bad5e800bab4e800b9be1e95164c3f770122eac879d79992d5ec69d753497711b1919cfe19d392463833cac783247173680618628e3b2773a2c45fc00851b7128f184387e2fe3728e97f1193182fc288408183f1ab9f951728982c557af51be0c6185081b8830e3abe710f1f4d57b8ae0e2abe75c5855e21f6d5e93a9f150949658434a93961b3c11a55cd2e235f72baf794f14a4d79460d9c1172ba0d0428a2aa216e5e8351f7261da6c001726f3e569888c781ac7b9301a207c784ab3b84edd46480e0522cbd314e54257940661c45319ca4994e554c6c719c588a887a773ca532c7e9c4a5d28651ad3f082081db64822cb90486de0822cb228a38c2494a8439452098916317964b121cc0c35883518a28a1ad040071959d0a52eb63851258934ae884e7ed072061a72d810451c27521056fccc183fe3422e6cc656973227082a46b8b0fac5cb0011c6cbb808172663991421c41077609932a3228edfb3585b9ee5b677540047163c7061a4a58e2e224b06cff29b0b63f9ca251047fcca5f17b6b29a46b51fa2780d88d80f41bce602b830ed066f3c619aa531e6872571fcfe5ed95fb7bd83c21bb228716061a20739c41f74f8eb2178002e8cce64ff5521c37f6e73619f9dc2caa752de4ab9bf52292a823e2593e22b52ae2403a99a7299a2c2e953ab94969498fa144da5dca652a1103c6043d6c4a4c4450a074e0ce1c6952d339821c58225de888244110e539018810675208da13247186988e38c7dcaa9cb209e52ea1f15b2a70e800ba3ad2d92063fd6dc4cd1069294af5358f1d5c30babfe99f19a5bcdc10bd300f0778a1efeba5fd80d726129a5575dd1e2550ec285a9acd4e03dcf5bacc356bc277afe1292c333e63d239ee719b89ee74baebc17e63d973e8758074aca9b45efc9d1431732a41031c35312a2e7b277209003142a519498b2040cc4f193792e47f9f4de151c3ee5947544f8947f7061a924ccb8438b1b88a484e1248e5f96a73eba78a2d4f404a6072d449a031d4fbde6c2e88fbf6dfc75d7855d357010031de461061e5bc4f1c322812ba06003065e3c216208b1ba0ca2ca5707626305cb6bfee3c2b40bf38c06f09d4b2bdfb98f0bebb82a9e737f719ec3398f104c3c67e4392a5e49ef5891e3b8de914e6ec8e0258c114a349821722f9081164e3838030733c4f17b4e45c30c585cd0841c46e4605af14116c711b320c488a3547ac10e9e4b128206013cb5ded799af2fa0f282275f9de6c26aebc238a78775acc1f0abbd4361e479ebd36edee058c31678befad64dfcdac29e61d1851cd2c59867d6126b8915e6d9595b9c58236b64cda71f592c56bcc43a3e5e3a0ad4bef08d06002cdf883262424de19d8ed2441b1c9fd744543e5e629d564f4e49efc8aad1496f9fdddd526a3274fff6d955869fa46de58ddddaca9774465dd1ddddd429a5a82bbe294fd8be86b4fa4829eac5af2913e59b462f6537734bd4a2388b02c3f518a72d16ce39eb0c9db8977076055fa5bc69261dca231cc771af9695d694389bf36f9443592bdf74dea46cda541a061f534ab7ccc9cd1a8aeb505c0e3ad5137b3b594b2b7de25826a8f7c8276fa833656debbaae7bb5aeb4b4704a1ceaf43b0e75e56ba0ee18134d5437e6be2881a5ac996a9b95b2991b4c4dfc62ea44deec2049e056a23137a40ea54189893ab1e3353ae40e17e386b41e703138b89885028401362774076d0cf5d80b51a1ded19eeaa73df58e14354b63aca339f18bd21da813b9f3b2395ec56938352759444e21fb556928bcd34a7dc3acd3ce94c46c4a5315a72c4f5c688ce669ac773427271ad39c74f88c68a829282e7cc3dec50bd3971998a6379cc23c694e34d6214a56c4375d739238ec9a93e6c419c59e5deb36c22f1418dfc8f3cc4facd3c3a2dc062b723e7e2df4525b7a4643bed847fa9a52dfb08ec66487ec319204764da93526d669270d0da9d374c8576b4a6d8713794383dcd1663d9dfe86e719bfd87d347dd383826c7051ce028c34e8d96dc8a1361860db829063e4b6c3b3f35684cccab89f7a6713ea1bb6587ce316b349edc43a5b6cbbf68acf3bcde3c01e237738676f3be4ce4b4a0bb5a56a28250e779168db73a1bd52e414df74eeed22d1bc6bd51a07de69590b35922c525d09ca5214a6247a36a59baab413cdb753ef6cb12d1633fa729c90f50eca0bdfb07f5102b3f4c6b3a3c23ca3c43cfbd28f5bec699e86a8257eb1cb08f14d1fb798564d9892531acf98c61d139db41999bd98f6d25f124894fefa6e9289f43debb6d45d47aab373371fd9a64b7d44c72f6dc62f8b6f7a0a28096cae3541b96db2390a74fd585408ba7eb42de47003b56ff27cd2bde0c5464d06ca9771a368da2afb8975a6d621955fdfc824165cd2d2476b7d7cfd289d7684d87438d6fc6859a9e6391c679ec39ad148170af9d148121e094c9106d598312e28217af6d792126da86fd89b5c3fd7554c0d2402894992489fdbb66f3cefbc50fea8dc0312ab2ab47da3725528fba676d3d84ccfde299a0a6f088ed5c8e989e83dd0d5d5a27e72fda0dc732f64022476a0eb876342650e2452e750a0ed1b39957a877ebdd4fabcd54a4aadd43b525c629a353df71310213e1f5b63ce038daf3f7ce32c42c2c587a58781bfad4fb7a18b7fd4f1793fc91bf6229659f04089affdc4fdbdd464a93022d8e0535df00ce16592972cdf6111c6c6cc5aa55c020a0ef0b28690974963e4f0dfcba4a528c6906748b7e10ba9bd944e9eb8c1bbe0461e57ee7882450e99f8b14b76cbde6ac1280f222f578801134664671faa68e3790e26ce88411173cc40658cd8012aca98010b2743d431061b930a76d9066563f467369e5dd681652604135c7460c41d225014789cb114c41330f0e248649e37e8a40d9897eef38394a32e5ae8c052244a252fb6b0516486165f441f3a7cd3a5b561362103a22744066dbe10996886318610196c37da106717647891f10e557a30db41638a8c090f972d48620cd4872251b201464f93db8e96219e440665941c266fd5d868f9645217a51f5b5d907eb4ad1ab771f9d898d839c781414256ecba90730ba2e49202bcbc48e40d5e8634167349ab7175a22bd2de3e4a29a56c29a5949dd4c5e847dba5e8d9bd99bfe21b6dd20c84969266c04441212ba2a8942eaf09d2694be9322f650fcf58cc8668c8f66ddb361f79369f5bc8dbb655efe131c2d3ce63a447ab3c467a3acce1d7ab7946c9c4d453023b537777f3f058cb9344c7163c3c4474d2665439bdd63b4da5504b1f5711c9223aaf6992e8b539a4390acc37b7effbe197bd301f353e5904e5089073c898d343ce9bd44b9773c7e78df62c1f2ec52efcf19c498f954b911f056e2f2fcce5c3ead13913ea9e77613b8b03e756c2ca87cbba12d1035d36f441a187bd636fde0dfc9cfcbcae1452a99feb2e1bba52f09ce53f9eb3422654ac33f97a844c54ce7220be301790ccb72e0129f6f00d64272faf4f0fef61aec766391fcf2520452f6c42c57a13ea9e27e92f6ef8304664854dda57dec32cffa1ce0a5d9cb35c8a3fed2e2e74a5e0c3599ec487b3c21ede44c67d78921e4ee3346113d606724ebe6381ae175c40327f73094891c65f8071ee43e32e1f197749408a32ee4ac27296a368409fbed19c8ba239ca27c887f7706d3ee35294015d5be84a41c6693c09cb67c22633ce729ab049131a977129f6f03143c39291a2dbf0a77de51bb8fda6b992bed13e507b8af900f1bd9c5f9aa3dc950275cf7fa87ba1cbba147fda5d36b4e207e486cf7f744dde686eb5fb528b198e5e2e7d10f5769507a4f17da3724e37f56ee7f1d79cbbbb793630678244fce511159e6e0ee610a1e18b5f348d6f944b3e3f4a5887fa53676fa23e279d838c9c0ffa3997be71caa64c16e19fbe42ef7010174c2889d397740b2e7609ae80caf2c9b98294692fb318e3f41285deb152494909cbc71f64c302c4ea4253dcdc670d99006dcee216326182049437626275795d3e938b4bfa1251035d32f459a16fa68b4080f8a612bfa6f3745415dff89ab269ce170cbc23c5e9720649313a9a0d7848038e974ee58e64c30a931359c0008c34964429a55b2ba59d338dc9d9b7988d611299a0a8cc301363904954dc1007932d0d31869926c40e251b4066b1c31046a22463880a9128c1402ae3489c3f5c4942486430a78ad86955d350a9114896c18617422283354fc41878ce26727cfcdea3d5ebba177cf34e05a82d94e205a5b8791e48c11b8627583d757a8f7cae14527e5d49ea7ab6fb822e27765d6f52f40ae07920951e433d908a1562853cbb10b70980666fe77920159ac3f1e64417af5e277aa014eb064ef9aa2da8ff42a7b5cf23a86fb17a5d05bd505bfd09fec8eadb95c275cf955caf43510f37d07e419ecbbee13497a294246c6ea9143b508a1c38b6340ad68ba4006fbd93b36f0a15a680bf9b56d3b46eb69ad69de54dabb4db515cc1c8e9ae9d5f546cf085cdda90c5972b05945b57820a95c810c47c2ec827d9f4402290c81b18b4f90706590f81079f0bf2498a2e4960a7eee2064a1149017eebdf5c7377e67addab0d3eb4369fcaf4f6cef28b8ef1516b9b6ad5c371066924b0f7f3452253696b9aadd6da89804ed13005ac75876d0695ed087dcebbea94e3252813d53bda8552de4c7991a8e02beaa9cf7bc43e0bdd759b7ca0ef340fd5a1dc8539a524e16b3701689322ca5e2432f7013d664c5fd4131f7d2a1ddd9cbd631433739d953c2d4dd52f3ad93b5428edd843815434a7e00b47e894af5ad9677d067fc2296f6af5c9a8672542545ce079165e0f24a2da5d9863506010ca6da0e202cfc3d024681b3bd0f3fc31068a44e637efb8a66c9802fecd8fd0586ca39452a93e9b680926ba4a59a7253dc25e9d851a7ea1a5b2887c0ea293369a77ec017d57bd6a43df2692167c3d429f05cddb3bcd3b4243efa29e609d6d86de453df1f1576bbda2bc0acab7d66a9ab78ff3042a535c98d75e8347682cf6ed1ddafe17e65b5984f603e698a7a1f6c2fc517b764d16a1ceae55af53c0b3998655e2b0cb0babcf5e99983e6ef8d0f8e93edd882c227f7a77f3f8491e5b764eef243aa2b8b70c949b522a2913bfa47603173950e3d9bb10ee6867a425fc9a9399999596f06b6a9aa669cc9aac21bd70f4ab036742ea7d352e81acf1796fd1499b71356533da8306937b01d67e5fabd592d254014d04a09981d604e0fc9cf14be60655f3544544fe334a49d136af8c4f71f108f83bab5ac7b9cba61c39e4ab775d05877cd75e05074029ce2f49408a96ca2aada010ad82236c4b22bc0672e12eec85bf282da98a8a023dbc89cd6c45e7b2486db572523e0620e5a30086bc747fdd489dcea5835201a930086539b7c1451ab63163c688381201ecb60367535a7973964336ef40185a3f254e0bc6fa9b6bf7487d1e798ce1b9db3a945ac7a8efa62095435a30d623f5e9c8630cd784798fb80e9851b02e6d46d5515354dde4e7f304f9766ee07c19a36f25bf14932c42e4f9c6351de85f90adbe6109c62fd77d28518252fcbe237a65f294dc49a9923547eea424cf21626338fd94524a29c1f87cc3cb49a9ec9c74ce5a86a4298f436d9e026ddf80afbe9928295a1028c6cd6885f9b43400cfd4b8b398edb2889c911247fbba85339c6fad3c7bc733d5d99b793a10193e1a4aef1ca813797a3da52e7b897ee92d253ba5ddb576d76e0e5f128bac543addd13e56ec1bbf9916acbae8f730289f3ee9ce45523695524a29e5cc1a45f2d2e06bbd6c88788df49f955d8235778d6f6c79ab465a19a2f0f8a48f9693503f5f4e39795afff8d5b261ab4af985f44fa29e8b7dd27b88ae932830260a8c6fac432f5da2642ffd456bdca6865fb5ce3adde79c944e3aa90d8335fcaaf9295bd4076ee89b546b85a928dffc6652437cd37b58da3a660d7ed8f1bdbe5d4e418104216d886c39dbb66d1ba5b3e9a473ced934698e359a80e4f0b553275202bf78db362943e1fb50e057093d25a0407b4a40812767a602085668a147deb07ffd7d3bf896b25a2402c57a7a478a39763a6ddfb6cdaaf059cf7ed47adf14e217cfd884c23a44338a534a2955a177ac9d71d1944da497ce73671ecd9b76c90ccea53902bdd4b1afbee956dff44cbd8cd137a1903b521ec922f49b88a5620b174f27a5acc32c0c9d3de79c14b4de17f6c2b6d51b6413890beb2c9551e57bca9cf48d91e1c38f53698ca71fa717a69f01d356e9a4946e33cc800a2008261020be4a435415dfb7e4c6a669afb18544a75eeaa85e532a499d2ae5ceb793f2e93972c7f3e93d72875bfaf9f2cea5efe159ea821c8ae97b2dd4be86adbe611a7ea14555618136ee26ea80d2c3c343354a6d0f8f111e1e233d3c467a725ed3f24826233c3d3c467a787a787a8cf0f018e9e961070f4f0ceab8224c140a856e73d639e79cb3565a69a594d2979c51cdc7e934e4e9e199618f117ef14b09927eca1a338799a2cd524a28990d3f9bfa55121d4c14ca101d4d733851a58e21baa3a69452ea4d29a5b4677777cf6eb64f96afe9637b0ebf783a8c7d634e0e111e97f6a8efe19c9c1c995487949c1ca53039bcaccb1a8bc56246a27cedb8d71ebe5966653ef5e936168bc564a2740a768ecb64cf76776fa64194d2ee1f2f93e87082c78da43a6a087383eea8e99c734eef39e79cdddddd19a04e698f974961c000630e2c73abdba64d6d6ab3ce39bf7c616e66ae49df2c9b53cb6161f3deeab6693db5d6ba76f7f422bd24d151c315375032942c073c7938ccd1c19cd385f2d79a7ea1fdc29c1cd80c72eae1e1b16cd9f6f0308f911e233dcd94995700410b4c4a989b999d64d289a51023fc929ae491da9cdad498c7088fec611e233d3933155867e45901042d289993e79c3c4e336a7dbc400796c812c161adb53ee658afd65a6b6bd7aeb5d666cacc44d8a777989b99e915765a570de861c62f69436464d7bc7dd4b41f5d3e764d9b52d3c2b13a4fce9c92d2e94478fc39f434be1c261ed69973bacf9c3c93e6488292c13af878f8c5eec6e73f7eae762017e7f2511405d23a3eb75f48c3b185cac10744c72ae613b8b969a59456add65ab57b82a6699ab6dd13b66fef549d15df488fbeb342f6500fb1ec9d9c669115f63e607b17e61839b969d5b69185ef50ba56834ad6d5a09f5e65903ab6c6c219cdd36edacd15f18b2ba595be60028c76b709dc515889f875276c3a1034be5185f4ec1344357d5c1d3a41e8c73a748214eb5234efa8a7dd9e95d28a42fd5887bebb5b85c42fe68ec24a2471787ac81985ae9f16379f2f650b99c440f48d9cd1f3c8190d1dbdaacaf3a882e2d939a5c6ec6856452e0823956c063a296dcff64e8aa3da92950389d457d03b2afffc47e51f132a5bf8f37d9e72262a955f071235bb799f0a742e4485e1166a75057d43b5bb0405e69329a7a23993feb9cea4863c620f755ac3ad897a61e057efdc95d01215922ca2f3579b2bdf7c49efb8a8f30a7ac74543570a2c4f7992760e9b5c6f4f757b9395b34297e75264b914ad734e3dd05fde855c6843a7edaeee12941a5f75f9d759ee4ae1faca7f6ee84279fbf555e8a23facd0b5b90b15feb4df1ff695b3afdc45431e7e69371acf6beed33b4be48de6d6babcae2d4ca1fdfa05da6fe8da2ec0be0a597e3d09cb6fb8f22629677992957ffe854deee72fc050eef3b9cb2795725792ebd7b525a065f9ca7bf8ba2a05baaaa7fcf324d7556113955fffc226547e547ebdc9e7a91f56116b89121f7e491b6de3ee86ba6d9bad16942fb7cd51edd693b0a3c22661132a4dac7717cabeb93e5fdfb0cfeb135483f5f61ae675f974e8a2de0e24862e1a5267d23f282695855e52272f9d9badb7ff58ef900995cd99843f28e790093bcab539cab5b975ad7e7d537d037d2c0804886f0515f855a914433201422eb68f2ba11cb17d945140fb785f7652d885acf8fdbc27a444f6d94d224e8a750d5c0114b24136f0881dcabe11b2a20d95f40d9531125a82b402a7f143a1c2570f9883025de283817546bb843ab9349716ba52e866666f82f2762952ef2ce87a417b0156ddc76e42966c0cba345f81d8a04b0b7d96f48d68c19c97d38b7e439437273a69d3dea12b85ce374fc2b91636d19cf32d6cd264f3cea5e8f2d9367725d190cc9f2e01295a9f4c7df23461fa589f96faf4096ab71be872a5b039ca93686ec326b609ca3797a21441c02ff00351a04645cdf60d5d41057ed1d94b27665aa59336a81a703e9d08d8e44b97ae1450de9e84ebb009951f76ce9b88e3a7f48dadf75cf356ef74ae75a1f78dd6b2c1d7613bca396cc2cef9f4b1d58ef29f0e5dd53d1afe70bbab863f5ed8ae9fce36b7c60703bf34ef61ce51a0cb863eadbed17c054b5ab1e6c642c5f6947a6773ea4a7a5e9f2d74f95477fd682f65731f59646a4e699d3246df0aed435d49ef58a7f4f57593e5ec94146bc3af6f5e600e58412ac3c4377f58c767b440b824bea972ea309373c9de7992e95cd8c4f3e95dd884ca4f13d9f2e9b94b7ae74062fb0abde392a12b05f6e93f33644245863fd3bb9089e7b37326d339745197223b90282dd8766bd933977418e6bbaaff74f39b50a1de84bdf32654b4f087f3e94d3a97a1ab8629b077fec3de8534fc99ce79132ecce99bd9c79e33b52f6b501ad494cebee95c9f50439aa0d6982cd21ea3471287c6060dd618100df5190a3dbb3c92453e38e1831743f632290c2e4fc2cba430861e48f715499c761a363ee664e0966491e93d9d9341b52471bacaa08a491c0e006e7c334b5d65f8765b978a6cd1b7576e4905c6b76fdcd2f75865a8315944b33fbaebc84feab110eef86aecb9c624ce0ce9114d91165fa5146c218923bdb353d65a6b9589d23189436bad26f4f89e321c992cbd448dd1472f293dc1c7777d01ddb6cda2ac45d9102571d856b1d6365d1368de86dd930c11dc949cf2cd844d4eeef11feb6cdebe69d7848ae226c7cd392d131fbd957eb1f99c73ce39e79c73ce39e93629cd011da2569f53abb4d269ed9cd3fec044b56578534816b13bad2f0e86b943a9f459fb7dadd657df78f628dd7e84df8652e2ccfa06ef68454dac53dd28cc51158314f49a4cc34153d2a0684b5a14262290e635a6de41191915f54e27866fd8838cdec3413bf2a068485e144de625a1297953b4a5ae61f754f44d1f51461247eb1edfb5bda3eda8244c57a5514d2ae566044000001315003028140e88c442b1509886ba5a7d14800f869844644c9b4ac45196c31832c818630c01010100100090c18409027dad87020609f18f77443826a562cc59149cab18c8c59d57454730dace730e2d55618e989bac3ae9efd0c08857d41878c379ef3c28fb8162b1b5a99d276625802265a46054331d5c275bb1eec448f5615148e0bea79bef3bc0c62b3bac51699d0ad8a16c5e45b84a2f5e307da5ff1a7b4f108b4a19a2b2cfb0b85e29fb62a0bbaf1e18152228cb891c31b251abc47d9888c3301bb9a4de6b119b042aba430d274108087b0360a3a69ca83d88a8f49b3c62c59be7b60046c8fad07e96481c5961738695b49d270c014ba39ed12493d9b38ac9a6cf71697c822d251ceaa4e2a9573dc7e4452b96a6bc81373634221f455be591fcba64e99f3d28f5609688822e9d5c27c5459dfbcaf868a3fa935780e1de41467d86f582d7cceae5c18e382e535d6f78846b96c859602384aae4355e0b0bd625727c7b2374909e8ef3c2a59008c0dd97ff4fd33b0ed0c652f9d1279f78e68210a488215d1c2a874b25d266aabdd4686d1ee260978db902a58bf5cfb9492d3307234f908f53f291c81489a1a61ec2e04dbf5d26ac24008c74611e2bf9be353295dc129e480041390ea911b83ae43ff729344f8878d313714e3d19bf30bc39d7349a0e8cbfd5712aece840a689c0035ff8398ebf0150f1e5c60b7510bbd8560e379c7c87ef3d7323d06e106a3fea22d81271c714aa509ed71432f904a24c1ad3beb76c7b21474e3f8101caa67c7c425b5fdc580a9070d37bee848a9595fd957e0a43376caedb0961a1822993f19ac3823fa0c99113c60162c8dfe06bf90dc445e1c1f7880e3d509706626281ae4700196880409803c110b3102d3ef0dd835fa13519038aa1acca6c6afd16636a56ee01c7a245bba77e8b838e283c510f3873aafe950a71d630729a9739e1db6d382d9abaeeafb4c1594735e3e3753361fe6336e88ff47eb816d86665ac57867e09e6642644608d636c4d99b0668dcb94589d63c2c1d203592f119025f436ce4a7cddeba5d96067c06c44d600209c2caeb6e3745e74b686bfa0e823dad7ba72701cb44d5bf6b0b27fc3e1c03906fac866b7d6f7d91aca9243519e695972a60e1a6675a52997eda2e72943e80de0cd6ef2d0edb3453895d9704ac7f07b3fd7acce5f345284870eff870fd0a31bb257d3009825bc17f05df90936feeb91e8cf4ad1963f6dd6027b4da2cf6e276d9c2897ab07eb51602166d57e02fb03f94b3359adf5e78231742027f1fea7350eed40dcc8d0df108c8f1172a03faa8e8ecf830a17f5f33dbc7e1ec9609b8e25752a19a19f77a6b07e47b4695ba6a40ddb7711fc0796c5aa23fb6de476019f1d0f4ced4e863ae2889720df1dfc1ddbe888653e9e576f98c2fd1c9e0f202acc8c088ab4436985225224aa092efa9656abf1b8f4b89b48ee4bd97fe873c434d577b47677f7051005424d14e8bda187b7dde6c914e1cd940381807a1f483016dae046bb147976ad27372d620f2ff84e6f491d3e464c8ef89f849455aec1978a03c02a7b402f95a6b7fa0e556b0da50a8590eb93f07d645248c6235a40c7cf061dc40bf152d1e6c0d8a0db1aa26bfb7ee280dd9bb32fefd9da61dcd86ef475825c4b79a4ef8d4f89669204a29844afa9c5a88b4b1e74cf390e459cdf5ae8d3bff79fcb984b22c4623218100417ce4cbb73f9bfff2b0c19492feb6c52b064fee55a4f7af46c52466f045015b7e56d61c54caf043d887f4553808b9ca516f3734f642eac5d64a157a0c30bff980a28bda517fea417227417468c78ef40d69e1e9c9030af23a412368737622c78cf6b8b6b3a516e664b42b5faeb4ea53c9ebac00135bfbec2a2cecf7ae556500441a45ab15ec8f1c89e0f1f4f491f60b50d72aa49cd8b3ab7d3991f4355e39d7e6a3943ce320fc3bf8c0eef38c4381a4061b915a81e6a40b3df2f6a89c69485aec90b30263f5548ed0e8c7cd033ef15bf2c9822e56dfd34fd101e36ba0083fb4eb211cb41615524e6373e4204d4fb02dd890ab70b752c32a6e39922bfc97b5e72106644681906687e33cdb11cf74792988c1b5062970f6450f81ec850256ce64c16791bf0e993f17b176fc6c13dedb6ce564de09272125ad5581c42009647af792e2683e4ae581ec54c305ef041c7eb637c3c7e9760841578dd233f74f4658916f6a8c56628fcbd4dd9d9c4ec976d9438a8a659123569a777165feb4b2c79b8a6b7bb89147bd6633cd5b8a83fdefbcb56fb229c537a60ebad0c68f93afc545d426010a6b44e9c3c99ed2e451497b7d550490f87207e22f9eadf99b256e3b5d335b988504a14413ff08f264224ef3134cea398db7f134e03e6fb3e74c4a55a5d71e2aa9fb9ad971f5cb5ba1a822fdf2246bd909292f52ff8bb08d7a33139c60e4e13843b67047c007ce5caa8cf7c118adb3be9a24ecf0543cd11143809cb0510d66d2614d55c169076c8762b6e2eed153653ae1ba79c2af6310b1e1a2f2c59331a31cd389b928d08c3d0040308bd73cc0bfc05023436ed16dbc75e3944dfd847fc7f9183274eb1a3055add9a8bdc42eece1898b203f8007ce3d4f029e3f86c3086e34504b068b593df117d531ad96e11980c4d295a2a7dd9ebc93dcdd0c1aec68c0410b5ea6b7372ab733ae4eee06e0b7ee1cb8d25ae61ce61b2d3952aa39480c13d308bb9fdaf8ad2350c112be64de98f111d90d036ff82a5e9a3ce6a851ea8639eecd12c8aa30cdc91182e4cc3bfafe5e701da28325752adeb766a757be41e47dc9d42f110e300dda9c5e48b91f30a521342ef4644daed33d5c5acccb4230287d8ec52ccef9c9c2946d7381e909b9e293bfc1394ebc7c260218b3052dd024c1e44880379c24ccab3d194686e6803bab29492a5329e258f82ad7e12302805a4123b2ce5f95adce0472027d697d1573e8497755152a0b7c39ac4dccb2b35c59bfd625d56ea59363e9bd7422a2c71860bcb30745d88e0488223db42aaa57ec5bba23f11053aa3088d9d2b8ccbd54d17402cd49a2ba8ad197802c61189cba6053452c1e97ace3a0facd5151daa3c30aea50988dddcb7425d7d70da4e7240b7be4213ae936392f8656a9244a56a85954cc1be62093a08dd72354998d675e39e6669ea07c54334d221e997d4239adf0fe8732d4816cc1a316475814253d8cdc5ceccf4963174d2fdee991d839abba8eef55defeeec4a1b4a687f72b024703e93606879d1d17aef0b3c5f72b90610f4cecb1e10027b76a935344e959a7a560b1996651d76f48315d00dd9ec76cb1151e80cc149179ee56e8070d807f6d9902c25d95163d6600d0243d646d74b0820b8c0d482e8f58d4b6adee81dc6ef582c87e1b92374884c37ee396398c9880c921efe5a1c100dc8da584c132fa4d65361b83dd470eff2920b3fedecc4a4e56728bc9006de921bf6863d290e1c1d80456ddd610bebc658df7b90a38d0ef3e471efecb90e3621294571d481babfd365fe99dac3eafbc8f6aa6d66958cdbed17fe71d01a74a463f6a0fca2aa8c0ab64baad27c165e4cfdc74baa24d9d2aaeba2e32c1b9c33d33c5f7dcee4fe4ff3e31e5ee2484631332173c708fd0c45092865f7f6ce2dfa24ef2d63c25dc525190286a8a3d977f9273a42f7cda4e576d233c5d9b4e66b43430325f683ad3a0573492143db47997d6bbcc9b5e2b4b77513afdcaab3af17fb94d04f4480ec3b4c2519317859c598d9ff6949aea57aaf9952d834e33d8609911ccef4ea2cce0a3cd64d19b3ea9e6c51bb456a7cc7e54e4db82c4b9e9bfa1c83663cef26a4c20829cc4979dda333c7840ba897c3a0c79c3c62aa3d53ffd2eeea96ad2ad19e4f6d766fcf98de7ebf0452472ac6d603631db3aef79718627f1e0a9387e994e9924bd0a9c48f99a53e1368cc9b71fcdab4c07c0c78824f9dd849cc9390de68e61631631ce1d5261b52c2237002e802c1b76c3a510a25903e28cf1d9b80ae941397fedab0c60db252ecb6defd8f3e9564eb34aaced606afb1039fc1b7f3c8a1e5c73cc4dab98def026eea22696c57baf148f76b6e2e0e130e6c7eced7482e89b1eabd46b3fe283f60c6ec0c7c6c03878232a4e3b2acbc167543895c84689b46d839781224a48e21dea7a4224471cc05995b74a2ae2a509ffe986a4c160dd082b3132d73d7f40600ecc38056f64534a48fa22775482b7f1e8d0ffb6ee743da09078adf9471ed190316003201de72e6637078f8c74e2afdf05ba71814f7b178c518319d4618813490250c719b0851d5ae693206a9b7cbd3633baf559329647b123f01a6206572ea62031bb453be8764b1e5ac2580c5129fef8922bce0ecb4581502d3fc8a436c61f9ca7702f64d7d6fba4f1da1eb9cff3b4c6610ffd8f139d4e4975251e73c59ff038c54dd9033e8953974d1ec2ec34a453c08da34f5e5ae6142a9fcad4711e548905b7c6de572c1584934a4cf3ff1fb37a0bac22dfd845693f6a905bbc95da10ffc767cc37bb4e7abdd4a8779b6f1adbf2445d5e4a41777a12475b86358e102e983bc5ae10ab95b5ad8a85c17133f87498f7bc2faeefdc79d070a49770a1f22068d62ddd1ae7ab8e3674340cd2407144fbc05682a9ad4720d7c2dfca01de85acf0a6d8bf5fbeed647558c657ffbfb61b69c91941dbd92fcfc8b5ce67b04752ce76848a7b4c198cc7e7ee3a89e886e70529d5d3b0d0a682d3f5138181d9cdba6ac536eb04bf62e4ec09bfbfab75b64b652a04e35898afa2799588e5e5e9401e4f82ada74be70cc1e5c4be1f836ca8132fe63ad08899a0e9ad2275e769dde5692fb349cf18467312679188923ddd889b4dc45d21b21baed551f9cc26a695dedd45de554bd7df6ebfef61ed0991fa08ed453d5332698bb74a7613ee8f579560c7d9fb8fdb7a697f3c73eed90327b1ae39f4ba63f05bd3cc681141869664a54b38f6c7c7ab86cc0db95985618a43a64daa4dff38b73721a657bbc592d999fc8c7802603cefa087f893094f134b8673aa6f37086c9b18ab4c65bd74c262525a41ff9be556b04cd59fbc668eacbcb588cbe3a3abe78f63625cee2f60e76b5f8c412d5a39f15fd7016b73a5a10b18169e0600d8a9e408831b03e2895e8c704a6c5087515872699c6af3e3044f63ddc7138acb8535c0ec4f60452f29854983f67c5ad442cf97e0fbe15bbffdc4376ed69d218ab80b3e4a1a3580597a83bb5b05dc44d7db8bcbe6411c4e587b22d99f8a402d8bf19306e72ab080693b00138b120ac858d4a0c1d67b1a0987ea486b8536d294506ffd942cabf0830a882b5ad733540de5129e9d2feb6cbbfd453113e6aaa0826324ff6801ca1f8a66e58615e77fbcabfa22a9d3c47b924b11087a944e7de847bab27b571857005f5155a6ae6ad955c02655a87ee885d3de2b081aa6efa958c781f6a5fc378c1b27aa11e293c781d0ad02cb6e9d0ecf4cc071660f53a9982d6b9fa6805b927cd155e34d4b58c391582ab8f5b3e53183cc14a0de88061087747dfcbfbc44722340da0ad450207033edffcfa47c3f8d8aadbb2e67e31e5226e6c7eaa9048b3a556a5178019c53aa21df01c7cb51a6f2895c33a3309047c69821c083139d7975482b258a34ae6801ce946c44cd8ec5ff8b056d692261f09423972579f307970beba2cf40158ea9548857ab4529cc5976d14be17c2cbd60ab90c7ce0b651708ca2422ed904afc1abc969f54bf70d0d068462ae1e7bc96fd807630291586f10749e3ce8b05742424e4726aaf3d82d502e00d67ca678d374b42fde185d2dfccd67a500c282fde6c38758ff50ed64541a5dfd4caa95814d7ccf977214abf620f12ba032f394f1b07c85115a4ead47e539b58477343683383990a17cacd178e8b558e396a7ddd7efe45dd743b7d8b9320a9be569d89493161f47cf681347c76ae16436f5122d1f164896942bdd92102b100f1ba0b1f99a3e75212ef97b162632df79c7969cd2fb548956efc336085a50f1145e5f5a8fb3b0493170d6e7b3d8b54a81b587c678972519ad017139695fb4cacfc03db8f8b18edd8fe95a426085ef1e1293edc68e52b1cab47497b776c9069716f6283132d4d5e0a012ea9c6846dd0f72a3a386cfa364a6ad0bb61ac388a6449b9e9a3a0ad1b5312bd728018204e3dbd8ade3c1dc730a58364cb71914ef2eb8f0f3410286e8b34884518f5ad270e0cb119a9c331a31b382d5179bea7afd69f09f016f5d9afba78ea004fb20b0c5ab88a694dc505e5c8868127a78444360854ae607aafc0af72276277d6fd89d7a28c0f2602685998f41c34c8dedce57dd611052ffcfe2c1ba17c7f0b57da9148f82ee49c0362f994f5bbad3c6158be261fc050b01c916e7a16c0a96807cb06e74950f32314acd487e0a95565f71debc2d5dc41f4a8e6d5554197f0d56bc0f80ea6e2e4bf2350f142f68e5d420c56e4851b0cdd2ed24bd63d0d9dd4415ea40f0de60e2da20b46690f504db5101627581bef3786c68d87ff6e57f8e5580c7a6433ebd614d55be5de97f77979a905b39c486980b37f0ae69579100b242d5e8199db27260df4f6548cde89a1909e43d091d32123ebc10d4922d93605add70c44b4ea28ba7bd3e26cd3d5cc0551a2768f16e222021bc1454d1006537a2cc926c894212a524893d703ba3d372ec4f35857c647b921cb3930d084cf47dcf03e737c5bd9e6c7ce4b5ecfb39f1ca3c5db767c64cc2a0f1e51384ca8b9f0f73803a01129780f6cdb380b4ec48e7c7de8fa196f7bf708aa134fd2f5aa842fcb9179d5ea284b2ac38731f101602a3cf41a7c8e17471416ab8c16b45d3d3caa596531634f597bed881cd2db7f4373cf915fe2bd864d790115f44e3c95d19632538195fc625ea693d8670eb7c362dfb23ffbd79cdccfa20837482a4786fbabcebad1235668ae559b2be460e929ad037609cf6a076ee4228dc0719bd9972bdac7ba0805f518f0ed8c6dfa86584fe7ee1f91c74c1f78d36b298cf1e369e445953606bccab3f1fbdf83b538eb0006ffb82b6711d73b271bd162fb58e5c733c7340fc9dcdc32a9ddbf1392537fa193d8053f4da304fa9ea015c3fefbf3d4d222c61e208a06fe0b095f1b8b82a9ea77872d9f14441275a60060098dd39bb61cc7cac7da512dc00f02438a6c04dd5556368ba0100f6663f6ab52e21d55484d2f6d8290400b84f98e62272e399a44edfd368dc3924d088e7803550f576be239461925533cacf7e6369feb0590a19177fb966bd410d9c82b0d8d2459eda9b04079ae6168835f24af3bda1077d52030f843d1f982eb73a0581373d16c8743ab480baad1a87cf75825cedccbd2ca2c1855c495b19d10ddb0e6af5d07e3d95e00e326a5746b0bad6793b9225d8527c73506485c661d3941ae347fe786f20075ac932337b6a852ec3d81e51944e1da49119f7fd9580d7b609af0780f88aafc44e16f1cd3cd2afaebb0cb6ece8087b4849373ceb5d5a343daac0287f198fcbceeacc7c627b0e4f3e7978ba760f07f004c8c3217fcdbf1143f8329c54008e1940eee85f375532038737f2031195f6b15e099ad48ea75690d1a0e373d126abee85b911f9669e50e948deb9df7d2f6dad1cd481779ee850cf878a0077479029a5673555280f06099153c55e0c6f5f54914d3d75940acd7ac80a33a7c79af8770361a972dd9fb4b36fdec90e5eb0b8a2acc079dba2e1165bb53445150be01a432b1e1b8cc4481522dec096607fedfc19febc9a45039d7550c99d659f787503bb51d05b2a39961176552227afa813035967c9c28156bb6ad9d5b39b5d4a73e62b80598ba19e620037d129106279752a4b250f54a1030f7c187b121aca90b3f0dcb3d0cf3823c099b807ac2cdb3d2a962f483a3765d3f5f50a00aa1eddcea1ff9dfe64877419907889e5bc508d849bdd06f12bbf82121c73b7cd5750c431751fbefd5f27581265621dc87426b2264622edfce43ef5ddf86cb15db8ab4e0a5c638be256fdce328b1228c5e74bbe4b4a2924dd1f349cfcf2a1d6851eebef4dec60c15e5c39543397e6f80ac4d01d9414750eddddf821a78de8fa1f09aacc19caf24948c0fa46e3320de3d6824105d61638218770c20b40ce899b9af45e00463f8ebbbed0a59caf732e41005bc9c6351a0817bac881420ed2cfd281bfbb916b0db01fd7e23ac0419f77661957ebd16f8783e7485a2cd39f70b9b3ffdbc52b998f277b59e5b1691a5d425f4f6079e0d53dbf7a0c430d4dec35dbe36409af25888342a4a5daedee8127dacc0a93421f352deda57d33ce449a9979271866651d776ce7d3c5104ad290ecf39a62a766cca15578e38c54fdcab06ed92ba3ec5b7cd15e406a7e505f3a3716d440e8eaffc4d5df27d6d3b0bbe6f7037b62696152d955e932c335b3fbf36ac7473bb8ac087851b6087e77c3d90351cd6ca8fe546e14e22746153d58e6c2eb32db9a1c70b77a23a48806dcf4658610a0bf8b66b7767988743c23ede774928a19bcf09d401e6188af5711bdb4b9700ed0fc27e19fb4e980c24a204e72819a86062274a3ae91caa885baaf708c93d40141a87850a30530df2925e69e4c4165482591392bdf6932789591bde47ea2f843bada4c390c0e83087e34c368963de66323b8c2ee027756e005e6e159b644642ddcdbe02775976eb04590bd1583ff80a892afc9a88acb3524d236883bee786791b9b7e95708a7d0a7668e9792806c0080ca024d17d9e878eb69ffc3dc7163c4f2d83f09a72e0ee6e326eebb7aeb64ad04617b17edfad3f138d0fe9a69d68fe61897ea2cd8a01df69e4450e9490821ec4156c584bb0a864191b7dda4b06b542d02b600ea10127a1fcdbc6a3d247b25c7aec3f1dc0b5d62f84e35142681979d07f1344e2f6cd856d77798f6f15945a4a7d19c9c83520fefe6b5ee271207d4903e6585883cd394884d49bd61236df5c2c996d41f4c2aabe7a3c6d77c2cadaf3e2df6133e058412024ee03775b811446b23070c2a14e467781913f8ad8f85c6a24afd78111745d979d2e41b98875fe7f56b4c27a7203cec2bd8503df2a9e00a8d150065160c8380570d59bfc6a7d1b3616c148b8856e7ca99de2c0eb5c0a95a1c10e30a2bf400197cd8943de970d1bc2c8afec4c5b69660bf1c50673d1312f21792a71eb13dbfd0635bf02420fdb6a2cdc2712f9bbfed51c6319d9805d08de65e2d9f8e5487ac28cb9a00daf843106fe5ebcd8117e2f0aa2cbdff1d6ea4148ec00349788256633cf03b68b0aac85dbdd174dffc05080c534c719d13e3262051320d8cd733ac5d26d2664811edd5c219f1a08e7f8d28e554db3566c16ed60c098f21cb8a7ddda7b3126b3f14db9c872fef9c7c23620bac4e576a944257fc1d17e073b4a16978874929e23b08e08d2a3de3a955c7c399371b3775c90ec371ebd141ae6053cd4387e0e24469f4fc07c200758acfb30ba3b4fbd4d0658613d622d8819dfe6c7125f2733bb0846480a66a4597236ec2682998bea700a6655f99e98e4a6bb9989b40aefd6d8595e3674873c1be2bdfc548972879e7868e347b6a8cd0adf552fb02d95b7a7496d7e42ffb175d59bc5ccc2f91cafec57bb2a21d6cea90ef98b92aa4691298b7afa9d02158a8f947b7887e4dd70c0e8e16f3042116a46ad61e2a78d88c21a04e15ccab768c326bd12f4a10b483d730edd12020d56165c430fb096c1b8d806c594ae1792a3964c0655814c32b5549fcb45bce576803e9a7a671dc837a7ccf30b6eba0ac1ea0a090638e27eb31c0c6b579b2f3d11dd0cea5c58df31dd6d2ca1b67a2138358d5b3453e2e32d8e4683fceb8fb4688362b61a9442842cb84e35169d83f4a32c63214d3b92ea0cd5157e60755f8bca6875db06df40b6c15e101e78984952b31e417d058f936ee47d029614a8f32530130922a6e362df53f1a9b15b501569d1911b1ab86fa3514184aa326fcce03a1db11fd24c86e87de897e2f54cb846dd830aa72bf1f1215cb9639f988428918b9f235ddf3770ae50ebdbf3f74519029291537bfef559efa24846f322c6c1159d956df7551ae72f126b17b71f1b9d35d33730b7088e1162792ea6e61a57eb4920a86de1399dda3cd1c85a23c377f76146a49b0284dde9e1a62849c9d8b3e6e28bfab17b2bea070ecc55f3c055ced0c1f3be0dd06a823fb9f55cc1f46de52e86efc5073f5eabb48f8d38d247d19191d171701405486be0f87fa886848ac2037a5e6eedfcfe784ac07d4c5b9540ca02e637d40e163da76036147bbcb27d3f077a69ed448e1920b4d03794d80a78acaafc4247ba9ea6c89f9fa2362dce0630905c89018ae31e0c15a32dc63967a051430104a424b26a8f5e9e0ad732355528fa5d82c79b4b85f26b965762373fa6ad4970d46961b721ec009be0c038cc606e5d7dfe29be8d988b00ef1c5e87578a797ceff10207df1dfd68c505cbdb765203c73d447e7ac644471d38508ab1da66f95ff4faf27f966af12a7e3a2d6a2abf72e962164caa74ea90b82adf50da9f29a5c5a84b3a93ff59418b4da3ec3b8f19dd6b541ed0ca9531514746c794198c53f96c14116298f11284fe5d12c3d6cf40136407da4397e24b5c8108cba36db96f06f1322ce13ab920bf7d64699a60e7598f84173e486ffa79f0cb6104c7dc75e299bea74e2a5169f651fcfaea6d16c6b9e9061d6e456ca3b8d1ac76dfbdb2f889148a009180fc270135721119c96d16d581f9c9320a9ec2b0131863f13fb65658a9efbb328f780df8dd9c45b1011c925e6e09938228195171684cec348edece8fbdb8ff6314a54e4ddc0e7c8ac44ee635d9f95ae0f83ff758668de3443c24f2f2c553a5dee3dbeed3e2e8e988844cb3acee1803adf27f80773cb85eaf1524ba11b652fe3add2ff26349a82717719a1ad90c000cc59625be32dada6804ba60b50fd12a155f88a098854b8704f7e2133b7945327ece13b119a610824244bea8d115724d5d5015a5888807b689e01dd899c28a1d046b561fdee436b6ac0eb951392a32f669781c9158bbbe3f5103159c7ae67fc26ad50d46c06abbb34b17dc938447cf6c3e9badf144a0ba77025d0e4e4322b87f9b0ed49ed96de1f2f862779be799916fed7eaf05062b7b75e8dd2c5b3dca66b5e5140b1c3e67cf68aa9ab784241a35743d50c8544d7c84219394efaa338ae80f950b3b51af8a775478186302fb9529a739c68408eecc23feb97c331944a984f56651ee69d27e4ed0a8cb4a7da6f0a0400e9e91b2bb91168670c756ef0bfdf20216d00ad2c44ee80badb04e04310c4fd895d535e263681be2606077741629619c21977bb9d93493e7c6dac932baa5ede7edce437e4c174d4a612a3a8f5e837124e8062f03ebd346fe93d2b0876b5ee4844c434ad1f914e23c015411de85d52cd65e6953d2c0cfd670a1be5d53f23421bf99c1102bca3e679caa359cdfcb5cd4b5dfc9c5f84719884acf6b37c5f8a15db0697ee58b774a2f94636ade5e0104c195699de07056896c2a371da5ad18c3fe8813baf8eabd0517c9c3e0bb0dc4c38e7725d274cac43d11b8ad7d99371626cc6700674c267cafb5cfe05dad0b0563f1f2456f091a822783f6e1964e89ce340cb3016e62ce5917ec16f8a8eebbec0cb2c776ac973b07e9d0c0f0806a0a5cc67515e1f905164b861cf23fd83e21c94ed7ee1c8298afd2880055bd7f78d35b0a61ae2e9e262802665d94313fc6e1a15a2f2a867b435a7c4dc9a5fdfc45544f62b209aaaf88856104820ec5b1b699c4fcb6e1c5ef0c2e00aa61ee21e2895ac20adf36eca1611cacef0a571f0f290441656d9e46e166e283cf1df6be65f44e986f3e89e30b6d129bd3a3b5086d83d0175d4d8faae937806fc81d0419a242311e3e62f603de5a3021c33296a9a26629ce0c67d90392d5bc18b3be97ed66a38e1ae4c94a40fae4d3440cf384d64522d392450d7af0d092437bfbf83f5dfa1702452a039e0da85898b5bfc88039c55ad4c78e8b7b6bdf5a53503a990f8a7742a364b5ae5c83c4728002fe27640e9584a8c8bf30234285d551ebb0a51244acc13f2cb5a6d2c6db76c8d5d2a3687f95bb10ad4350a793d6129643762b3366d9e79c9d7e0cc3535c7d5fd7632a1609f8c9a24726c13f900214b26b0b8f8ea467c0675a02a150d12a090d1711d6aa571052f622ba38ef81a1ca286510b943c98a88bd769ed38abf20eba3a09bf9e0cd7aa6abead82a943247c66b46c26c458b785c6ceca502afd7291de5cd18151ac191445b9c4a88d26401851cb7635eff142a9e6e84428158431877b2d869c5a513dbffba2730a4183fe211a399c8a615ac1042efcc0df1b2ba6cd13151f76b3eacc6893d7a24e535cb24669292fad7aaf9e877ad113327c3f673ec4996e02f4a543e1a8c9e26185c112991c84075f83b154dc5abefc6d9772905359d18884c7f363839951a725476bd4eb87bd4e5f4f87dff76cb0069bf94e39cb853ab32695dde24937f7530e44bd18d506eb71deff5705c210e768fe773a0b07cf1c8bdff22e617fafab88a0c47953f98e0194f3991c288fd138d86480cd994539977b4e915618bb242783d59de58008302f91e8a316d2e138bd4d8e0be52ea34c2b4b596ed769709c4f0a792ef2afccf3432363df10c4d27570abdecca3a6330fcce64c4b01ac015a2c1b152134a3c708e4350d0f79a255e6535c9d92e4be4bf4ca558dc8d9cd2c2e094c27a26393aebe658c7e939eb743985c5aebc0803f849ac3038c4a18cc01cb5ecb308f77d6483a6e4ee7d72f21c8521c67092aac6e7317fd27e97a884ea10f5e1c3dfeebdad262c06fb5a2082d2ba8534fc599dfb34d995e06e5f8da6ba1100b54ef4ec9051b5eeb4bb53c543380fd0c50d28cc5cb93d53bf07588f641e91169ba57d5ea19e009044302f57fff6ac0875b24ee1cc1d7583ea920949940f9fd7b08dd2fc0bf762ce664229c7015f66eb9d0d0b59ef44e4a5390088e244a8d4836bf359604b51a8a12671f9350092cc84a40da33582e8a1b9b29beef9a310ac25d7d12e945e492753ed6552c2c6d8990582c268c71626e5955102cbbe8e3224043698dfeec35dbfc88b0e6452ed0418ae8c4d2fcebde79b51fd0ef0b079690aa99df4a37a3a7c3298cfadc56d6cb6ed04d754ae998f097ec86c0b51ddcde95ebea9ddafe14387297b56e56e451663315b392f6231fd841e2a4df80ee68c4b43749d62d0ee1681eab1a354c8f1872030e5575ec7a6739e067d74f9460be05a588c78f7ca4b27a5a87a938603287f4d28f2e0340b4444d10811893553a5b4a94c20e8312417a19895a0759e8103d08a5a6b8718cf56e76c7adf1f3816b3d64c3629439ba65ca5a83d6fe4ba6a5f17ae7dabebed065d68058423a1356863aa6325d07058a56eea304295682e21d2ea8e23b341e50244bdc6d60e120596aa3316d528aa8dd8280811c6527730e01fbf9326045c09b11303c039355daab0694c0b058212c0a8826639e9ecc76915a1d141a6b53844a2fc262598698baa4584c896b39edd6fd2b04787e97bb19696e41b7a7f3bbf477c609543749d8aed08f483473af0816f42298c0b4dc7fcc896b677f8285081eab212bc02c360945b6cfdc61011c73bdbe36e124da8f0e2c74a686e64ef6d83e76ab96764f5b7cb1246a6051e3305489b47a898ce16818d1ad67d81faf2192e933780797a53fe91d7729efdfc1ee9873e0073bc6ce3d1306d743353a65fddcfd36e3c91294c000727f00534d0c44c4fcc1c375d8534c05169401c454a29f4f638d18367c2d1e119194becc0be58920f4b2bfe40e683a5360c5b72dda963d91fb641830d51928e2823ccc10cd562f42daf4bbbd38408e47cf3889ed84bb788403daba9403b6ed9aae9d24b3cd29bac3546baaa9ebfc9a005fecd88bda22f798304dc60f2fa2627e09934bbb91383423bf0158bd2e3297b86d816d150b8d4549c1052406c237751b1bfc9692c0dffa1cd02877dbddf495c129eb46c2c0dd09a633822c3b46a12299afff808a8a48b6126bfe25fbd8c15c1a8824ef1806d4b1be8b1d50e358bf55b2d29bc01cc268fa9faa8b714d568b7251787f0ec47cf228a4e185657c12d39d3017f8ca66739013e0774a2942c937e9cd50f2c93a10cbed94209446f1987189c888265cac7aa0bf57b7926af21eeab389bcd6ce2ea9758abd7dee5e4d5a9651547a7f331ffc0ebbe47b791e47f3ae703e18bdd9cb12f1c45c9ebd0e5b4f872e1da49274352a014ef01f533d90dd84f8b3ca45967ee130c9c8ab4d31aeec0efa01a7351f441507763ba8de959da4c11cb62525f868d083b547038f54ef91dde3e9d732cd7b7c8bce1a5e487b70fb5ce55a8b4fd685f60b51979229d2e955a230a82adb0d29b264816883f0cd04f3724aa1091329a6dc1fcd90e766539b042db6ff4be2c6f48c59a09afc210802cfab9d42469dd3ef3103fe59932c69d85ac2635f79ef18eb10f8536e04a82ef5e4c39dfd5f56845153b05a292a3fc7e35a83e59d343f72abf1ffacb2c2b5e226a0675c14485340c4a569cbe98e263b21549b6bb30c5db273fda5fc2816ec40a14930c28ee9d0d13da1753e8349f58f38725296e2e3eb60d7e605ee7c4a8dbfc94600da033b1d67db0b6071ec28514591deafd11d67d8637abd8ea6e069c04484084144cb4df72b98dd2f60a2d30758f959e1a6b243313379bf04654f86a4b86a90934e6c0bcccc092964bfc4c92e69e2897f12a74049a9502db63230549200860935f6794facf42a991af5b85e8835ac19d3f2a9faa35525aaaaa8530fd0eadb873cd8e417ca4526828bf08b44946c6b3d5a8c104ae1cbd8a9b59a8a9fe4e4043c4b612f885d0ca7c50fdda7ae16e0a00dedbfe3654b6a9952ce922f0f9c9d399e5bc829e7a520227432e2722637c375211f630690ecfd7cb120097aa97905c8e8999426e47eee27ce858236f9a5d9ad83aea8b935de2035e6a8819e6c4f597899c171d08157c8321b3a5169fa49d0dfb3b154620efa2f878a6f1d783e5c3295fb432552ef3967fc0b409bcdfeea3dd2e596d099dd163aa8b35b18d7ff2aa66a49070ac09a05c59a99e84a4aefa08d8e9978e192a1cc96da648d1d66e59cc3be608f9bc885d0f50588e9e10dce7c7ea33c7c6146b14ed506932ef2d6986b4a98c7b4bcb7b00feafa5ceef2eff4f0738d58c79f4277e6e62a95f5224067b3f6a032db28ad8e70e32db47126093b964ce6ab29f45a41517498fa0bbec8c556266796ae1f22847ca1235ea978aa27becfb3611707093bfc23160ef5f42c48d928223896089793d2b39184ee601dc1f81286c0adbb313d2f3578d92f43da73bee66f32ea23f09df01d7fe65caef5ed4bd7b21b93d216ec849bdbb6db8841c1b068f61d202a6566454b8f68c97c670d9daee8bc6ba28848911597930cc29db2609719710f9fd1dc0c8139ebcd5bed9ad367ed79d3e6cf9e647994aed803bbdfbbd59289892c79262760c6eb112864cd6f466a9a4e77bcc2df0cb6e00e1c6885c69f1c0dcb56aff3a57276e2eb0a4371bcab20a997806a3df59793332e50958c6da45a243836dbd09d927bfce3dab858517db12a96674bda34292bce4c7d98ec8ee33833037a9e4f75db10d78ba798b8bdcb38556a4aef7a46ea6d6c33e52cff226100a01c972d94d20da75140fbfb5ee72732676382b41bfa217fdd0a54681ecb95389c21b035220c91e82131012b6b8f7bc9ebec8f0e620d350792017d52e265bc0f43a240b7c7d31b790eaa90d5eb1f53500732d8186441f7deb99a6095cc0f14b05b34f00afaef3700413bec2bc418a56415e7997915b75abcef6b50326146faa5cba5ee2ab3fa98b7ffba5f36616c3396f99fe3cc077e7fd231c323eaa33f9445830b102e500bead2c51297880f1c40eca5251baf379b3d2a1e717a06212521971f9f926a2d532c17c8101c8d8bc15ede5eb9aac1b95b320ded3275295135abb4121253e21d4035458e2819c57f410bcf6c03442526f72afeec94d71cc155b08b35e64199faaabaea87583672c010a28048b1d4c55dff6241f48c3520d1211a460168a644c5d0e0a17d50c17ff271a74a19fa402b0bbec8fc85c56a54ac2f8171be472d77215df68e946639279816cb32c444eafba5a9018d1622b54f1398c3835d6f3add446e26ac6909aa84fd5b80a65eac0ff6c63cd47e5c764a00371c770e82d5bec4b4938cf3b9545258d5d35c5ee19f5b4b6a3bd838eea34423ff2c15346c924340db94a4a1869b6230a84f12609e9d19bec467ad3e52b5117b65d283578f7fcc0f4de4c88a5667f8781f8edd02c12e781552eeb75dd6e9ee345e73dbdc80249a454101870adf286c006e931ec3a54a424a4b1588d2e49be36653c589283ddefb74eaacfa73f48c72723e2a51a0e9000b4a0b20ba4bd75ce8b90b4d9f3cfb55eb6885f97b9c53c8f4b243d43469e421bd423197d4547e2044405e3fac7047dc1e75e9783e718f1933d39e50aea2803938cdc82602716e52efb27ec3c21ba5a9b8f12449c5c7c072b3b884cf8a81cc1dc5c8b435943f96d18b6abbb29e0d09e0acf0ab8712adc8d3c69657fc40f1ff307a369e53b026e4549cdbeb63f63e5895b91301c3cabc8d032c91e6b9017788011cd26871f4e7cfdcbea39801a14d9b6351480abed6101b83c7e7ed9ffbb6009c66862052b4f771a38f275327a49d2b42cba3c10a7db44f5bedf23995e0b4cd381a2cc11dcf19992bd31547d7c9ca52d0c33108f42f646e8acdca346e6cd4e641afe16ad509a84a656e15a9d28b235689aa5168e8b25fbecb5207dd5433a98396d79a341f7a84c629f060ece23c5819363561baa745c901531026c07a88d6c313cd7c53f8a34dcb07e5c6203a9b7cf77f385a88df68de7734bcd3e1793b3a01414bfd53cde47273d497bc90df29fffc01c8c51c512da256f5b49ed3519d2f224abd2650c0dd27f829459fdefc72862fa5492c8df34432449b4cb6b66e170929c0e786ad21088758e755d9f5a385bbdcc244e74496efb4ad8a23420967c3fcff42f8d852eed8d6b720370ccf5180f2526f88719a63787e9d8416aef0f8aa3bbdb2948cab15187585de9d469a3218aca23d1883586e40127ce178abb67cbb3e12db6b2ed2b4e2496e5341067ae6fe0113e2c88fcfad43817253da464d7ce0d476f532e17e43d810abbabc4acf49e7acc4205e179ed0d3a150d5615365c65c2da2571c6bf2349ea0d0a1b515ab6575660d86098bab058f998b15942cda56b6d83d923197cafb7c0e254e6e31e2863595c247a815aaa0444235074a9e338dfa5996d18bdd14e2a6485eec63cd23ab89ba13b583e7d483c0c79407bb1b444f88b449ece507e0c3a36627e92916f07038aa80f9a68ab22c19e4d096093af0967bc2d64f8ca2bfc41363a1464921a47f49b3fe517389465c64f7c6b39cbbf42d7561ae397814b31ec1316356f91b9bb09a8e3a079e3ad8cddf8add56254323ee108bbc627d57a784688c3e10b26c00d285e3785896d24785f92a043e6c75aef3cc3730203d3f954b77b2a207630ad8fe370edc73f61f1174d70d56dce1264d3fedf728cf879d87987eff8208ecb86be775402426fafbfa8788f51e4fdb765d3e2a954fa99944a33af50007ca04fe06404dbf44ef330ab42f782afa1fd3b1117d8da6c176111bf4d630dd4f899f99c8ba317de21b0b9ab61dbb6e2559ebc5179db45a191f4f5ba8e1588a9d3354b090e8fd10b774a8311a741f35670d4ad9415a7ca7f7b0ea04a30319aa54110e08b117a04104e6cb200f2a9d70aa677c214c3a5c534bf1abbe357eae2a3a06fceeea9804ca26819ea9931fb8e6f7bc64100bc405f8284b74230244bff31eb164c9da7b19143840070edf6028c3a240965326cb85d8f64e5860cc85df855a0d16a2052f7d7368033953d70e3c0b2f795e0d7c11b3e9a961513beef661d7162b4617d199a3128e5a872184480eea70f15bb13ac64504f27105fdcabe4144defefd7a71106e5f446c75b1cd58ac6b24d26dcc5c525f565b37a39bf53e0d5472b39f90cdff00651ddaabccf6dd1b05b89665d2a3cf8a74d90d4e053304fd18079a3bb80a3e64fdafaac0d46c8079baa7134e39ca454d6e44f135f5e8000515e03084307594f818b3c5ba603ba6d3b46651864462e5a7e9a85fb0a95bf2d53d9d6d4e4145e1e74049c067cadf8017547ae8cbb9a63da3d598fad485a178ff6ab59905d2798a1f1fb32e0ede2f82ebe063b36f1657753f1553161ae2290622e471c6ffcd9e7870838de162843b6d78246b99f9eb960e5bbd3c179702372275030dcb09fc65333e5451f8978d79e73a43bf4c7a195c5b18c4689401ac7c414ea81c62a57088011c35ade4b5aae3ce96c9d88f49ef3002f7399c7911c5d8d271703e9d3dec1194c4fdbae48a1db1a31d3901f7a912eec2c7821a615b93be7783fc54f5656dc4d80afbe512fa4caaee952ee72c2c5f8cb051c2b95d50db6d1838303b987c5f55e8f3ee2e7cc4f264adff4155a2b288134bb323c4dc407a88d2c19280bce4209e16a113b29ea9880b0565dd35eb377984415a35b4046ece121abb43b3608f0461f8838780bfb6a2c1eef9218372017ad9b43b91b063130f521ffe901b33f31a0b09f326a22088afac0572c801ccf2e8a770efd7ba73f5ce344cd60fce70b5288acfd45929087b67ec56a93c0bd6bd9d41775f360916f2808753d6231c4c4f6633a8b4cd451256fe22e972bdfb41c802cf4647b52b38f6e5e02e2e9983025d1b61e92bac06c7c12d5e26d1390401776b597a9ec37b970ddb807ed07d8f50d79ae7f9a56e17f259093f2515c053f4b839ec5dc3e6265eba11ea5361ad3d84c1886e401d2028d7ee984ec1f6f0f9fe2fde31621ab5858074796208a728359fe1741a93b390a792ed17124289d950a4777518a003a2630e9dd82dc4129db470cca0f029a220feb5c68df1d16fee6887106b9a4027ebe2399ec69deee2721ff4e5120c36a5265a73e61dd5ab5b34e8a0169114fbc3f51482904cc1bc978be53018321bc27a6b6f3ec299b0dc8a330b0a359353ec09e7130b2877c4b55e60a680bbba25ca95cc16d242eaebd1d6b7c854ee6db2b16f8fe8be075996915bea6ff6c10f575c85c20a182e6a7047fcf77311e260dd3bf830e74d60f48c9d402f8bfddfe2b7d2cbc6a0503f33b073b4df29001608a1a7428629ae4632f880335f309213a0cf2689d793b44eed29311b0068cb60b161a91347065fe6a2a25ce0c8928bc8263377e3d50853de7057ecb364af75e9046a8895364eabd6959987e4efcdc242e91181c5eaa57b982b97d4798aa32b5da6de9a33ef49d84f8987d5e76b2c4f86ed6a74719dd7b1e60a5663131372d2961bbc19bc4ce81ead8c58c760fdd9ce1ee7b45ba7c75807f67a1658d0d5f35b45b62742c410a9abcfd19932e8c1062b8f1936500becf52c08c3ad41d1943f9f8259215828f5066b9ba728151068a868cab6496e4576c87e2c6b131085e96fea10054ed45262a16ee757f871efa221a001c1f9d7554b2673bb2cebaa394012f658e5668d7db0c7765b8806d15b79a1a9314b6cf037a60fb308a17ed4951d5f8e3875dcb0e0e9a46aac3f3d953166d544141a44372e66694911d66f02c4400795e1fef02f7f83f3c96665864acb4e52174622be42cb4346afd2741a99ae65cc99aa38fbf82646f6ba3eb076e52c1bb9a60e53aade68dc331b5e1f95e4f02e1cbf31a6fbcd7872ef5db3174e2967ca5ad8367f7cca9b918fd368f6a07efaa79fbeede61bb64292ca80f2f41fb6ef1f689997cadf8e697c00a73c5fbbba3f63319fc31ed302fcba5be8a63219b0d3debb9f162191e9abf0830f87a959914c03f54a63a610d3ae4a5f14432c810809345f027f132c6ded5829e6a252261b4ca1361fd54a2179ea617e97d20093188aeaaef632e3e6c8037375d288952afc17a04d8e7e60850748c5b75e7d8ab0461b2d69536a48fb920e63a19a63f1cfbd6d49bcd0c67dfe95214812866a2b1f691befd707a8a3ee48f88943fec7e2719c0304c22aa755a304f1a54ee12950431f7b47867fbef480e79f7efd279cb9ba765ef1a1cb7dca83e7d57e07dae4e7225764bed808a00aefe2db42064f2df4c68ce02c46b3afe2a341a58f82652660f2e2214dfac884f9a659dcef1b6373fa831a381fad0718c0aa243e3a3db774064912f26f262c75c0a5e986cfc2229a6c24e46df40daa3948e8c7dd0054a48640c6cc21acd6c9fe04b77733a4d6e45c66fd9b9657f5a88d11e705fd7e5949822d999eac0b6f89df039ff1b598b92e6764a3478bf196f6dc79abf89657dc1ebba89bb5d59ed50d1633a84a7bc6cff7b5d392af0654760d1a6375efc031b170f54302998de1d55002cc36497484d37306a217c4dc8f6b754fddfd077c6b2785258b047119016c47430dfa70394e2f32da87a67408ece13ff95fbc4cb2379e1a0be8bd2b62e57219b4eb1ace75706425c44e6a6a9dec132ada8df3283d59880dc600c74d0d8ec180d984aaabb6d9478f7db7e42f32ca9ce678a2d6db6fce1c3ecad46933bbcf74693845d97a40e2a38173cdf273114a9803530f65042243ad1c8c95d823e24ff3fb03cc19ddf4eb924a70e813cc9f4b25d3d5582d67773cb8479763f52f1d33afd3cb0af5e92b2ba8d88a97f62cec2f7c72c22ffade8f873035075518f4d07be640b9e626e4f8a7f8026505a757c2a753135817a04822fea57f1afddfdb9f96e1bfa3858e32922e7baf90cc5571179c922b79e07c130ab8933578197f2b7fc1af475a1eba5d1ac47c3ca25b688b9f0b4c37d923ceb8c5b5ec7163533e6bb6b486fc1d027d1d22b9a32b2283f1240d01a8856bbc08a78b17cb434f3033f209b0cc2ba8cbb9bc8e9bbdc611c42a82f3e06945170d6705af2c1facf7ba415898a8d321ecfbc68371849fb08c307cdceb56566b4b4f5ec6084bb4b77817dbe04a167d0b3965b812991b041bbf059620840080ac80b2bfd0d1afa62fdc07fd120c8eb8277412f45c24ac526a820026322f289b0721be021bd2baa2a7e3ee58b762fee314e50e9ce3be2931d638105de318d7f40e716c8cd195871d1fa37778b83775d924751bac73872e92b0d785de7ab8402644950d1897e0ed618280d97e212416d21ff82be56fe18b26030b7974a5e4700e177b22ba46a3789eebd6a5061f9b194229f238a79107b1dbbec04591c68c337043faa3d68eea2047604f62903598740fed10a0cc5af5596bcefcbda9860de58958350f995471d7dc52c7e15fccb537218b1e7f04864d5b8fac843d2133afa012f36be528e13fbea0304354e33423ff856d72ac571d61c7619a13566ea4b09463a203d051c7d5cf51c9c6984971a9873cf80f091b1770a6799c3b35729f72160d60038153251a37e54585634a3c9c01748ff8fe9ea41d1aebcd70dbbd3f59f2aa81022dbdb65acb363e4e02a755c9096f09df743a485e60c718a2366b039cfb5a40bdea3a728d2349563791d23aad0251bf5199784de5b8435189ab886bae423061ab10aba1a6d40818722aac6542dec5f1a49b3fbef87617754368f539fc6cefa63c153ca23f43ef24f69a7a95d0c4cbc7aa0d09784d48768d5920616523f88cdd421d5cc6c275238248dbfeae9487b736cdb7e27b656aebd64d439f20166da1f39a7008062161f2c3b863183096e01fa44faa6076da69e9e2c33f47375491e70e2bd62cb86a88e3793712aff00069832a626a881805277670e3a7d2c24eec5aa7c1ff143791a1d523071926b1f6d321c28bee168ee97dd6a979f1f22fdc3a97fc3595f72ad14218cc37c0f979d1e18bcbf937483676d838070a1ebdd6489da356cc09f14fbd9b9b647d507ea5dbd4c1296253126737d9b230e216a9a411ffd43bf2e3dc1d863c485c2f28b9388159f85ad8497928834449b6939753d3f80b43ec12d2817666c60074ba8d0a6638bc31c0ba5cf0a97cc909fc90d310ad98c37049a70f319bb105fce7d91d7e8c085750200331a17a16eca4f366fb04c69ab24b7dd82ec67ff0436098b74866eadf7319bd35f7d37471a55beee4eeec4b93ef6933561f8473844e723db4ec94024aa19860283a97248d7e747a78b32e0d808e855856f8c8e90fdfeb5ec8b8ae555c4d1f06eba403bd9b5cf7d585bf1093d2ac81ffd51540ff0c4689a30eaaa6be10fa2442ed057f23f8857f65bc04ac43082d71233615fe8240bb1e102ce47b853e33c4abfb82fef960b0d0601e0e4ba705e752ca16c0e14962d2379dc0ccbfb366844125a83070b31c5b36ea418487373281acaf13a147972c95aed7575e2fa1b25c07155af632080d9c4c24928193b4fdb3d77d9cf9f9597d75405cb60d234e205d5d9eb18f153371afe9442e19d35e6c4a54fe4ebbdc3b9868012c1f1e0c8ac9986e2417d3ac3c08c2c031adbaf60be28415c2cd6223f1231df4410e777ce84ab7eaa27a8b824051b26bdfc5d1c71018b25628e921ab5cf0d4e2a766e8b9776ed4cabed8521632c0ab2a062d326e96848f65a75fe83152209bc87b757a97104f4f0f9c9f08a9d6aa1301209361a354d3509fa9cc4537e207c92be21451d02bb4a23afa31e1a87f49883524873cb85e7e36a031226d367e3fc5a8405309aa941f0c7a7a97aa4e57f75963534b81e203d03037befc524259de9b0d6fbfacde02c88aa320ab902f86911b5f5490810aa05cfe8d3a5f2af98840eca513d6299f7f1322d90493446988a3ead3ec2c3bb82c025ea619c3b92703590067cae28a2ff58bfb4e86bf45d2adb1ea06f133be180c633643602655fe630ecbbf3a61a979b7fca8fe3de66832db51de31ab84568752d492021ea0db2e9c3ef2cde34993dc03ba334ecdbbca49e0401ca455a06d0ca83c49de9f23d30a84c2945a99da4ce94d0dee2dfc8301e3183a3bb618c13c806a8619c842184597388b818e8c1b30ac9e42031fde3f3aeafbd55ac3eff4a53d54fe5f303abc8fc08fd4c745970c66a30680a0a76152478e309e309098092f6f0609f03020522923fe43094315172f6227682b65e3cc7893d2aa05982841aba1f8837ee3ca64da9afc01a9094584ba9d1468bcff80767a9c971de29c2f593be587e86a164703a189fffb31bb0abf264b55fc3172c6e8c239725d58965d024bcd1c38ae670c7f618ca1a0e3eeae24fd40e25be53749a838c75fc039552057f1f121a2582c7e0ffd7375a4983dbd45dc7de35d0994deeb2f662e31e5928f9f13e1306ce11120a832020539ca75a82f244ee58bd683d81c24b94fac911326fdc28c4ef08006aa98416866397f05438a17303e4fab037fee9da97dcdc21b86c1d5880ae607cdc2db750accda6c27a639952921a7e7ec588c4cc136db79dd1429e0320c0f39f5e9908fca0fa82b614105118018cc0a211b0fdd5704d2f6ca459c3a4a085182d41f8ac0c647ff61df2161b097d990f83548e07864256022bc0874dabd2dad863182dc4d205f0a352c87ac44e1d793b9da4e70ac4629b0dd673f7ab3cadb254b557224ac4afdc13ae590440f134a8758c26b9d2a99896af4b99d1a1b05d283731d2afb0184515351fc65cd39a2fa0ac29fc4b7cad253a90d41e69022d1886cf4107d0e1522eaada2197f9f02c2f6796f0b5d989db2f8449bd24d5f8409bc1254e9011d26cea003f35b1713f7ed4763fbe458c2e5940ac364fbb884c8c16fedfd751b6cb5d6fb726138ced692429f0602d685c4aa4d40fdbe3d23bf5f78c4113e4998e21d33e4503eac5a8dba27d8a92c2584a393d462182e1a865d395bee89aa244da6ea664e8dd23301911813d4a03b7ec1ed54a95c2d824344220855be674ca7507420790900e5b3d9af9c2a5d7e7e32a53c9697d08293e57c5fcb91f6f5ad21fcccd7ca055eb51241c74d0ba50011bca42c207c8e97564f1195dfbeed57512c321f51aac1a1993da055d1e8832f6daf8d12ef04808fc9d206fa101fcaa5fd25f8a1b0a21080db7eff2adb90da0bad1c026e125558c253a28076c714c663042edba330de475df25094b248acc2ab39539f90371c72b41239bc737f0e20851aabd48d6d4a55df49d9cbbac2df522921d3518b650b2c981665033e88967aacced2c10e19fd778cc8658eb9dd8392a3bc824db75d61eeb12b72b2655794dc9fa33e2e2345052835f633fc2d58d7a8897793157bdae08f6b889f887f8a9fad0d231798174aa12c5d6db960cdd05e1c7bbf8291b43fd61036671c8b8c8b2d986e92097637c3df03aaa22b2e897fa666ae0a3a680e42f99014a6c051b0a52b77bca07ac438707f0519bc6b123d5d41d9ade25f3ae4cb578eb5423f5447029802ca4898ca09f20ca54f7d903ab6562252d4ea522806620b280335a2449e054c984c4877c65a0e445242c88f54a390e71b61cebd08fa6beca3e53f08d67562784757661977bf55cc12a88cbbc2f08c086310283bec4a947131d59b05772a8631497341a9f1a0c1017f01fe34f7c5b1c64ba3a94bbd3bd60e39cd954685cf3da1c2c4d69c014e855d4c9b4c245959faf2049637fd86be135b9c14c48d9158b431510e597bd49066010df948189180a738656914c7ddc06ca37c1698333748823ae8a29a23994e9ee73cfea33123e764fbf88e2151256784ad6037b8e3a599260b26654414da5f1e042da36b55435e4de98107b21120dce7f3279b6cf3fdf68c24f3d8b47caca93d7ba122a03d5cb4000f41b835f6079442bea719c92a5b8f11c924c079254a30bfda82c82a6c7a780de40bcc9080f24a69f8440de102c82da5cddd79021b6e8d5f62a5834a29b48694fbebdccc5673e99c1776ccad47d3d6dc1f37935f52b6b18926124dcaa53198a3f469df14863abb5b6b027e2f1b359e3f7b780b5a5f740ddf1eab61cabc496ee84bada188e4758208004dbda5dc0dd7f47fafd705a24995af88e2050aeda6f4df7244a0b867154a86432dddfca20e22676676e39879b3deed97861b7eff260719b211e22df739b9e1f7706695c461869ef1472a10d17dcc001b28936a14e8dd583ba1a5ac1c9e3e4a194b086004b127dcef6490c76d7d449ba16b20aa8ee4ebae33ce5c0a98af1a67de8d03721a18897f16e3cc259feefb26209ef84332829ee17d53510b8dad9f81e2b466d6fc3e72a982f722e57595073ba48e47a2ff3e1e200e41780dcc8de0527b10b9e24efae8a4b7112b6ee68aebc7011087e7b872f14a79bcdeba5157aa514c28d1abc5b6269781686dd2dc5410a3ba9ed349409efca050ce206db88fa2f03dfb4633205a5357616afc86d84b83e0ce8160c9fedb9b8ac460b24f491bd1908b214b09e89022eafa462a689b1f126cd017c2f8b5824b1f285ee7b4867d46e06c7762e9ad95b1719caf8d001404a0621667d52518740f8a808055e06f8c713ce09d9fb130451026cb90cdc429de296de597230db40a428c5f89cc1b41160793d982cdc3f22764776237f53fbfcaed2bb519a0a8c6c1be1099275daeed359a72f1cf816f7d71ee1bf4f7660b4248d0878dc96acf685daac70343b1d5025309f5784d4fdee8952d092d9823389f6489c376e2744ea98332eabb5500d55b6df27c115e685e187042d4c4cf488a10fdc256f9983bee00c069fab4b725be23b19a1a8eae08dd363c7e8f2f8edd13d91163d6fb6949d178486f58e822faf500c032ab8763c34410c72ed699068c2183106b84a8b98a851737fd53d11c0d8fb252e0dc1639473f21f33dfda718e26321465edb262293ad01a4a6df65b6870b797748d3108695bddb4bff95fcc125663d7c254826044ffef1366cebfaa19a88bf01551ad768e9b05ef8db74e2961a81eb3a588199aaa74f26bbf790d556843874da46bf7352ff48a4a866b4ef58e39d836b9ef3308d9ca146055ae842030ecda5deb5697e00998e32cd402ac07e4e0c3712c44dbfa58b73fcf4881ab161c1a3f22c3582051e095275b8b614e5c180818116a2008a81b340840eee27ad36ddda09fef8c94fc005ea0e7f06df561fdbe77ccdaddc3dfa969a39f97a970bef2bebe79cf8f2f8e3342e13a54adac52ce377bd89c4b8d50946722a17f64701a93020f9c2121a81ace7fd504731b30a994e11cf7ab905f91de7618c17660dcce0ea16596f3cbdd84fed728afe093ca7cd527a2aa3f41b10b232a4d0b6450d13e8fb6f810231b746f79867b97cfc5d24d6ffc1d375d2391e6a1559ca8522ca7c3a5399229577b3610311843d1d933ad0359d59d10fc0a19d88fa00c4bdf3a078221006753a46d8cabc8872a2d9ac75c232d21953e4ea01768c2c98a68272cb3b4fa6f23f6ba5ea0eb9e27bffba444d2a4be1cddb747b15e728e9604e3fa39f5714811883fcb056679c89fe23e3a446188164b8341c1426a6c4fa8b39868190d820fcfc198802d4cfd4ebeae11474540f2056331ba269c11007838fed9668e1262e9f3d5040cad52b5aa43313c91131169e2d015281be27f6e65d859916a0c7a4958f91dbd8b9af0d6a7837cffa469e73d4bb897c34161e188c661de37055ca7e979723a370d17f9adc69757de5959ace8f15c4ad77e8e259625446331c7af482884dbf14767e9faa069d9bfb1055196b5af8e4e0e6e72c58d82bc9f8053937a7b35b1719cb0c1e3a691a049f6c58eb65c8b9466220ce48b1ee0448e4b78c4945d9c393d873269e5ebc2d136d319d4653614bcd03f0ada3fce9a33f3ed2159161d6cd887a2ba529c2a97a04d05b88631d7a61d83aa60a61d78e2f7cbacb438a592e44a7892e064edb939a06ff724317b5aacf21ea8042bd3fc577c8464ec3e1b327a0184c4ac37511fe2e058ad0621ee51ba5432202137ca46dd8080a13e2c767545306e306b01a0f41efaae0b28d613f202c34a88c047d4a6dffde24885ce460595ca4c1f1bd2d92c38c93c6c6d0f2e256b5da4d28db5b15eccb471f37a187675ca5991db3d6c44cfce9cdad21680dab5508de7735c3fa39a2d50d5f82e3a819081fa3f749e93f270607ab566913a4db8c97b89d43e3cdbb467c0f28ed855498070955413eb8aaad1ad8784227b93bd87b0f694140ef459d4640f2110d81915d743d9176d1a8190d71d9d527a0b753a2365504d39122967b6142f4244c54ed63327500fea1766abeea8397573e4719b20c7d879c97a090930e54cb67169739dfb44f340bdf9913a69387b90d7abc729a71c8c531c9df2d6f9a2cfb59fee49905f292e0aee9207b055aae35e6aa52a64e3c81a8c0102eeabd2f9f4b0bb7e4fa46a3fa0c2060124e6f5bd33937bd56b1985bcb164b2cb0dc08a15c49d753434117b5c09de0559740c320a231d0e981b2a9bcf0ce7f3ffe4aca759f7f5f0deb62b7318aa44fa77376f4886caa2ed1ecf30fddfca58725e98fdeac7d94bfd10888dfbdb8e38f5ddfdb0a8b2ff8a9246011e9e59e40e412dbd629e782b5b048a80ef8853c002512df0a9e8f285730029c5c6155ea684c9de551894384a261a6e3021d1a69123c545fb42b958dbe105451a079d9c09bbaa964c8757c9aadda715cb4c22b19fb436c8ba8640ba6673aad9cb76dd722895fe5374d05083e667ff033e40c9d1f7df12dbec8214b40362fdc78f77c274108bab98b872ff0755a5c26761d4d12666899dc8c4583de16f332227e78160a53b19d9348ef6054a8d939b0cf72d616e91ce94fe2e0bd05bf0c37b9464b5a4cae853a1be91769c45623b8727d9295175c07756e9b4f1b07138e5b718526a483f7d0b85b0b1f5ea3a7a5621094450d1e34a50f2ec0b0ec5aaa53e517cbb14facdf5e9f75e8a8c3a9f54b74756babb225abadd5c7cc2cb5b467ffaf00c358c166e04f9e8f4e980d4c0a6e626213164116944fecee7243a267a3fa8308830c8646afe32871bd8b773d5510cdc066a2bc48d99011cf4aa78d1e27dac354f529dd5c66c4e3dc3773832e82d0d8d7c4b49de9e2e5a81e47bc009456693526572a65c1873c8afcd7931a4552f39477228d5b58615d49d3ad9f799ed1bebec82a8df5b16c8726ea74c6c600c08e9a4a6d8006823a56bfc02e3fdd96142e43257e5cbe842295632ff0337a31e6a364deb49de9ece13aa61d75209ac0e4010193ab108f6679f3be5c015d03a54edc8c18bb6b8e71fb9d6b380f5e17eace1afcffd2c9021d077c53ead4253a7a0dc013745b29e699cb219be9208a0237caf6632853e9a9abc8c5ac5f421e9b92de5f864256f3d3959d565736631c7c11eb1e0f579ec9e7c40977c3f344fdbe220551e2b2f8ac8a12444b1698dbb72ce1b6a3e073628c135ec349e6d8ac03d99d6716dbb40fe9d84cc404667e279cbdaaa3e9af957dac16b2de553de3968c800bf59a745ee237a2d0485adc884a423359a426f6098b675b973a0d7c103ed7370891ad74dbec93c413d4c797a0ee0b94f39510dd7a39420dd7301f2b01b597488ad93c034493f695a2a447cb201f7eefebf0ee6daebb08b3a84534345b94b908c3ed87278e29acefa79bcca89aac41f232892a598318628aab03b742ae5074f936faf5c3e2e09bff9678f4709c29206863323e18fab567c50909f4fa6522290aae3ea02bfa6609ea6c2970b7958ba3240e9c8e6ef700411c4665ecaa913fe17ce71b311b6b6d1c41e0c8aa324efc2a6af15618c480a60acbfeb764e34c0280ca6ee45f47629659b47a16c54d81c535cf5f5302d7cc238e39bceca6ff82201707032cbf57285863e8e689bb3a56eb614ab136bfcdb0b67a5f812ee5ea660015c8e221d2108653633135fa355168454b80172d8b6d482432586bdc25b032806e756c0b7b65fd20f410d3b5b2b5e6a3fddc47ad052781b375fb8be63489f9a1d5eb5bd7cf99febe841db22d2c893719ce5ba28816a60b18a18d5aabd13a66ffc8b3554d3dcbd28a90d1ae0451216bb431f0675ceb9bd4f4f334c2fc6c0ecf4b0d69f7dc76580468b491f80bccf1e7282154d6975ec4487f8ab6778fce07f52ff726dd0abdfd35e51feb43fc6acac3572bbf65b1483c6e35a1d698d015a40b5c5b0d57396e61ecd596c2f98b6cb1d4da687dd16a8594081de6fd3fef25b4cb9fb0338a45d0e3df76f16e6e78aa7f59237ecb305afacb1e436a6ab652884fd94c47566eb1202269942de96bbd00237a24429ce23c242eb4c6b8ce70c688c4299e9f0305ed81e2817fe50590a9c54bbe0eeba309f9e99f11225e03fa2b9ae05c47d53b40529d35b544ab637165c7804e1cbfb9309d3fb727c23622aadd60dbb32643ecaf4d98abcee95b513838011bd9b636107e05e8fcd0e484ba586009b92046764ede7b316335b253c34d814cf2452c8e0c0594b2e1165a4420ee585ea42772199b1b17ead46aad241a559864d154bab50c306f52bc7b7c69ab0158b04e1824eb09ffc79b1fa0d12027997bee517216d17d8e066f97e07e182369bfe60c44933a7f051fc88555f40b50de42021ca18e35834cdcfe8acf6e9c39a29290aca24cc113fe99e78f5586482bcaba248c63cd398f95ba87c8d53b7e3cf3b5711c28b18bd93a485660c0e5f5c5f407d41c472076620b9f6018025b92a854cd3d1702672cda5ec3e356cc0b5301531cf9bd82a1905d56a1a5afd3979a45468554832409050e822a2de2ce02fe99478197f2010c1aeb086fe1e6bd7478fcdd8993200c25fed62ef0450859bd1a013783be2c08af937e3ff56a14091b441bc33220592c2f63dd1f12ef94ccc0ef20171c45b05e2b5b6bbc3e051e35cc81ffd7624ad65d02879bff9854ccee30e350f353792841ebe799cae7dd693c83df8458182086dfa50ec227782b54bf5f4d278d2fe10db009a41cc8fa9d63269457dc6ba60ff2249ca53fb4ff9581496378155b3d39b34fb10ce461063140d8bbce30fc1785be1ae576472acbe33869ac8a489a05f2ba0fd35e7da0c03222e0d23fb1e29db15abea61f94e3607c58a57bcb457a58b6799608300a5c2eabc4ded0d418446f0ebffd333e87a112132006cadae34615bec7560ce5ffbbd7ce01bea23df6b0d1574c572159e204d55c9407bfeb008dd941f13f487e168a8f2d4c27af42a0057ff022612c3e8a27ab1a051dfdb05d1e1addb91d187f0ce40609f8103a8100da1ee108493078a68645c3875a70e10bd24ec7698787ba950eed05e6cf2731ca4efaa8f18ee094b96c5c6b453f11ef6b84e1cd45573850aac29b159f2aa40d054f7db802977f957b2d453713e18f5e5787c5505e1c0c4ab92c0a8b78f4a32e466714c4ba1e0fe5ef3a9a277b5e01031b1cc4f5c043c86c9730363c4e05e0d01fdb29620338ace48a4322e62052f90c1a571558446357f8278a65f233e81c68052af89022a6604b6d000abe5b642ddaeb9812f0f988630a9b884da8f75144d8f230bb302351c4b3ba064cab12180ca43a642c4e5ed39090f1ec6929b34231bd8360fd31d9e5aeb857fa5c4c80c5ed33d935c20cd6ff26db130b34d14d44eb9757bfa472386687b7cbc7281913144543bb4b852c414fb152d5020e6641849f90b5efb4085f32baf57f5d58b64f0ef73cdf10158110c736de2f9d9d3fb483df0aa79cd7b3a04bc4801da2778ecea4b80b5110d1a5fb78e03647a02a5953a8a9257888a4d3f4740a26e4754bfc3574958bdcc6f59564e92ffbaa6a05324016c7e29a177ffc2df86002aa9ca42e4161d872bff04fdf72b598e399018d39553c3eaa184fae2e2dc2ba22dba82f71a23186a6e2d0e65414be5abe26f74cd9cf51970c001be0bbc14d9d6c966ef710f617537e2abb40e2658e53cc0e91e2b98bb5130615cd164da1c96754e6691045fd00d63498b357dd183f4efc982ee03d2f13d1f6a2beb9f4736a17803dc24da4c92d044d721b72a0b6ee3c4d5f62e501daf2f1fa925769e4dee2681d1f2f256965b3128cb90df10db014ebf47638c0825c1548ea356d2a5fb42c02a1d8cf21ae22fbc486da45fb05aa0f8f6ba158c97bebc74c94b5f5f91d794cc1c1c2405d23c0bcbe7ee57bee8bac24e9f0cc8f1c69d2acce58a8abbd2c85a935cabd1047f24360d3de007ae627eedcc7a632c645c0a65de10370a2178629c261347add766806206ac385e911339b713685a0dbcff334ab333ab846a97c70a95156119ebea8785a4f89df8ae888e76b5f5a518c8b0defd0f06445632302721df001a90ac49abba89967d5b6a489a7cd1d12740284036e5ea65bd52f6df5d3f06c19eff3d0ad892ea054656e286245f4247e8b8bdb4dd632afbfeee9c9ac622004efc4c7b086c2c175243d8da7a008a739656a9bf220c546949743afc924cc480d7844a3b6a726ef502dc7a96c80704228f8339f6e964ee132891f19be8ff1ac20092085e4cb03eadbba925b3052b29a0e6a71b4e032c0e89b58075dc1fce72d688bd1ed2f93e8d4371af46ba82ed25a559809d88002b853b1465ba0f573fef9335c37ce0dcbdf748b28caf2a54f352a77d430bddb97171aa9c37534e92664ef2db794322599021d09f408f70841541f1e9c208a6d66eb1354084e6812cbd1607b81a53d52d8ca33c1b07507082b3f50e1616362e9e5275672985368a13ad87a6508bb81b1e5c0be68b14111658c2fa0dcb056f92207449ccce08ba12fbec85921c703926ef84128ddd4b83829344b8b15a4805b62c8d32205446a52b7e587cdc98ccbe24a0f5489a09a4510543095d8d96e6e16433e38f9f9896561c4912578b20802889f5a16b314fc207981b46d1d9dd26f6e52c0c1e105941bfe8b1415085534b961bd11a30a255b0facbdfca40a209654b1c3a4624b65cc262c4c1767d8d3e5273d9072c35aebc113da834d5d7ed28323b88af35e040d69f15308de1418cf4b7537cc8b4bb77577f793bb9b4a5e77772aaebbeb66bb73788b8740dab3bba5b36439e79452764479186395524a30ec7efe4255774ccc890990c58bed299713664a9ff4856e2e9bada81c6fd920ae73a1b9cc413c7499a8f34c5ea954327953bae9d44de9271487b2d5ba6c2e2f3487ea06022e012ed7eea5e6f2835d13b3d14a9f2f30734a8749c9548cc73007e1bc6ab3fdb5ca1c6ff55798973e6566e28c6a5371dfe47cf65fcaa4605e5cec29e76d20c163e60e521a724b432e839312ede84ea1596c837fc371771f22f03be59360becb95574e508231f79b3c4ff2861ee2ed9151e3a652311832fbb309061382cccbffdaa894524aa6e12294524ac91fd906bb7467d1d9db8c2c1c2e07345cf817b238216cb55dceda9974ea77eef956bd8e4ee9754bd48d4e597d0baf7ec94dbe55afa3537add1275a35372be85e9d4752e28e2743aa168dde8945b6c1e7dab9bb5269dfad65d7cab5e47a7f4ba25ea46a7f42d267df1ad7add94d4eb969073ca8e6e51b7295f5e4c3af55f1cc6b7ea75a692a9b484d72d5137ceb7808131e9d487f1946fd5ebe8945eb744dde8949c6f916a01b327eb0fb9dbe97442713c3b3b3b91889624156bd2f1b7d2454eefb8bad18e73e2b36ed424a9b8bc705c8c3c34787931e9cc7f913054d2c9451b78a90d86edebbb985ef040685db0d7878376d2219f2196345460604c3af36152914e08f48d2793c9a453273da552261dff948c91d33bae6eb4e39cf8ac1b2d492a29930efd549591d33b6ea3b5e39cd06da3a54ac5e74665644c3afe3272464eefb89257f29c749c139f9ea4323363d2f19f912a39bde3ea463bce89cfba514f525179c9dbbae6098f6bc87639b7da759dc7f1ecec54af542a99ea904f9cc562ece854e278c4ad2f3a30fa78abe3183e956685d0703a9d5094bb8fe3386e0855429974eaa3e48d796529ba2a51ecebeeee34d659d4496bad8b1cf291a1a847674bd6a433df6e2fbe83892c030915c0e784a942148a28f4f63e413b82724c3a24342474fbbbcd27888261cbec04c3a825e8e5e505a6716aa6ef59890fe70606c6a4231fc6d4425fff947ff5fee66e330e459f8842bfb7ec1246b2d2acfe2bddf2892ac42a8c427fcba296db4fe31733facceb603c621bfd344536c575dc89569bc68a58e7a5bcafa46a06782fdf56cff37a6488cd6845dee77427b7ee3ed68e8d4a18232c13e76a4ad50ce8dedffa8bbe7f95fe32f8acd5dddddddd5f06f7f63c1c56ceb7e393fe3032f0586c9902296b764707186781831b3fc6fff827b4cb8fa0b8910706aaf870a31729922d5611c28d91495047212e3ef1020737c6a496b51763fc1a077152cc2746a42a45b8de4bef3b938ec799743aaecacee5be9a74b81ae57634c5dd9e9a74b63a4d3a54be6e585ac0510f5df4e04e1702ae7cf72b9cc8af7000388e1be2b8feb25dd51a7d71fd5544d0c1f5ff220871fd594508e3fadf7817468c5870fddbee5c2f725b6aacd5c8550e51824516a32a6500b1822c1c06194244c8a00844499e80458974e59529ae94524a19534815e69205bcfd76470fa46041055216242d60518a006205261812a28b30c65001a34c3a529e8470bbdfb4811c643f8c6146104c1001059a14498486aef8c20726ad11ae18a1da191b949e1587a0c4edefef6a9618104a4c998203241821e5b3fc859a32840fb12a589e3c81c9f939a404e3ca20cc9b7b938234e978890b1c18800b2bb77f33e97433f1840f543005133920011b92821d191451041354928004ebe79089dacaed5f21470cddddcdcc373f4d3ab3ab7142dce18629e060a090adb062622d57d2f048841631892b9fc3e8441070b8f2e557ab7ad61321ec70e5e3e4e4d841e5c895cfa3c7932b5f8619a8b8f26908421631a2191522b41faefcb8c343054b4f11141f22aefcf8d32e7eb28511aeac45618613d6429183dbdf4f93524a69ba52beacfc62989d22c4ed669a2d1e5f96de1f68c38c752f9f4707ae7654eeb64a29a5ddd6d599d80fadac3e7d19f7ddefe0b1d5046155dc0d2cacdfbba718317ec9767818fdd9c3aa0aaa1bf955a35b525e5a11b8a4fea9142443cf6dfbee41c3169bcda6a1470fdf70d558118d16b93bce0e3a95d8908b38d0a35df1654c42a19452ee29dd3a4a69f53c4a29ad94524a2955954a94d2f77ad40da6c90769177dfa3330a5325a2ea54f7f866fcca72fc337e44f1d18e06f6dba259fd50ce897ff2a46eae71d0dae76508e7bee87e9e168bf6fb2ebc09bc6f1967ceedbd1c35bf2b71bd65c72330392adb1659147812130f9f13d989e16df38ddf82724bee1baf1eb011470e3832518222cf7f16f1cc773f0f097f7918b3c90270201e1532405eb44030a828bdad64745ed62a0207e6e7c9451bbfa8b1bbdb861e986a8a21b1f15e42fcbc2e9da99e5d58eaefb5505b8afe95860f9596c847473e3132d9262230d6971c3146ee49962e7865c9b32bb210b0d35a382da153e4aa85d5da59de54e7fd6df64f2d93401864994d077b3d54925cb5b113504a28258a8a06e88372786c7b6c6cd3f44dd78637371f2f94dac823f7f2cb1fca3030962b92f88ea72f1b9cffb9a51f0efdcabcb19ab87f34af9526e5f9f7e415497d6707b4af9db2761e80643afca215da44978252a2cf7dd7bddb7e2afafc31098f7dcb7e2e75e8721b0ce7e4ee987c0e2e7e8def46ff2be040aa9ef81ab8dfb0e5c6d4360cff63f7e5825e8aa58ebc0227f82cecd5f328b18a5db94de29e0e090b9fc840a174841a2d6883ceafe1c65d50b70bf8142be1bc2fa8fae0663986d970f7f3682818d90eeed5f61c7c78821a21ddcf08688e78638a4c73eba7d751cb1e261785ca3328c8b617b0a0a91b1580cf439500895f23bfa1b2844f56db2de720930ac1bc2f64bf911c62e48b0728479f4ab5b6cc92ea7b00401dd6ab08bcd8cd1c9be6e36c600be489b47d73f7a2c26057303d2f5977864adb528d4c78016063c7de844210f9d1e7c7139b99cec490027d42900a793cb7ce6449ce6487cc793384fb3da6347b0f1438f1de1ce213f788c731eeff11fef77e9318f3991f498074d97d3e594130095864afb7a8584daedeed4cdee9d3d676667a423eb2d74f92350bf5e9e5dfe058cf5f5f20cf39c7ab6f6a2f8c34874f9e3904dbb36effe6442fdb8a8d44581d267b08254d4ac22d4d09315a42bcde2eda7fbcfd9c523a36659418a5762d10c52df0cdc9ad58ef52c16795191eaa7e73612edcafadcae2a3fdeec80f9aa8d2d4ac39532c25cbec8adf923b8ce5d87d54e7da261c9014ca453d4886589aaaaa2142c2de85c9246c68284b3dd81b5ebacb513844969fd1561f2072c3ab41019a88e9d0f6b41c2183f52c49082040ce72cb45ce9c286499e7841112964544153b18d1e6ca8bade9d8f512c168b0d0d3942464d0758ac406184a3d849b862b389224a1133bc3872c5143029a594aa67dde0e4f81131f210a59bc9952464a6900cc8d811e201071c7a60a1aacb884730a9e0111f9820820d7a6ec0c5e441123bc504a68002832945c85284056f91e58a0f281f56d4a6b060883384b0d0299020f3ec527a16e5830dad9d552a90e40a10367892840c32889034069195488cc042085ba050628a113d060d68789a878b5453b102a32ec698a26527053a8085372c05094f9e8c2a39c03286124eac80c9ea2fd5ac48d850252ff7840d5527bc24aca5743f2232d1449d421115f214d595373786ee5e016741032b9fe5b179c04ff4735fa5e4406e5665e9fecd3282c729d8d48d375567ad31a66e84b93e22d18c439d4a828d6f4a82ed0fe38fa908db2fbbe5675a621bd503db1f5bba0be1ca8579338493ae044309d67070b583bd156be18fd47550485604db1f79766861cf6efc6e241b8bd04f2b27c064fd099595b13aa8bafd95ce183fd9cfeeaa2e584e4e00aa0a1b4d5aacf4c832b6143a6d8ece0a1bd61cff8123e9b64dfe28a574db36ba510a0291778241e68df20a29e50b141b65cf295dca292537cb49f4688db0fdf3638c6cbfb7c7b73fca79c31f2bf7b9d3e7aee40fb9120ce305c011ac4f767195ccaaf136724a295d35e59ca966f1a4d2b9e90f518232c61b7f3e15262db6bfe5ed97f2abd259e83b411bb6b1baf33ab87241be833f4a3e34293871e85723287f7a35c69741289d1394b54a29dfe7fc6dc666713570153318523a5d7a758ffd35c66e261e33335b1ed42884a762f9d766f177c7dea97c63fa26eb8704b84f67f77ceab8ba994c4b94ea57b9aff3d8467bcfb2be2cc1759be101e10712a4de0fc42d9f8ef7737c8e36783c38dd71dbe43a2f47a5b2b63fc76b35efe1adfe1ab3b687ab54563a4a4a97a520d7519fd342a494def48deba49c40825859c3f558e7267d725ba2b3fc15d4fc62b160f0973f7db0dcdbdbfb191505f519d63a7446f732e6f08dfa1eeb4f7fe3249feb19a24825e55fa5eac01ac1cbaf18b35c0edf68eebb8f2618b8ef7e9a5e1841585da89c05b8e5af1b6f751ce208aa2ab77f8bdb5fadaa2bc8836df477516cd3bf8e80a99fbe5043287ddcb08d172776c86bea032f876ff4bbdc78796e425fb64385edc08235d9629e5fd5afd4a39e3fe46ee7b1148ec2502eff8df99e5b2eac90c7b530ee2e3060d8e3da8f6f3fc75f5f3fff7795cac5c5c604aefce8bd80253659300441ea93dc2a05b9a72f9a4a39ed9e9c9ecf29e5acdfc628f89c734a973509afc25c2ec0e50f5f2e7f488099e7af93d68dfb57f94bf5b22616f53b54d88b0bcc7f2096c220b7bb717afed0e5ca3c3fbbea93dcf24f79ea650a0c85dcaebdbe6dc3e57397a813a3e0e281eb5d8326b6d1cf2720d7511634a1c01004339fe45629c8e963b6d16ffa22dbe82f7dcd36babbe7ee9a73971b4521faa4ed74fb98ae47cc60d84e279573ceb942b32828a5946005739a25a3f0b806bd92a8b7db7d3f67b9b7a19315e06ce8e740cb15a836b80dde731f7fd6aceeb32db01eb88a81eb0f5938ab1ddd4b80611dd8fd926ecec711558877fecd8c5ffdaa7f2a6fcd67dd49c3e706478e18ae4c6f4e2a236d67ced28cb519049e4b6bbb5297ffa483941ff772d659ff24b321d77ab46b7bfa75851c3c3030932efda9e5d279a559f446a490ca86c0fcbbdf41550c9e107f0fb46cd953c97ecd2bdda29f324da400c4c0b2e8e7f00d7ae9c7d0238f461efffe6c0b76b46b250186c92692b7e8af563bfc25c030a7b34bbf02fe4b1ca4316fd1bff463882a449e9c1c250461196680a0266e1083312cd620ca113a58e989c5600c63583891eafc55addbe7f8cb269623861e3fc2987ce5524aa9479d7edd2652bb424b63ed0a6bcc2bed6a322efd8905e9d22b37a4b24b6bb46b7e539e8a25e5c337e2f58729e21b32d779080a223cf04099903f453764a2eb42d87016dd227fcd1c70cb5f8aa32a90eec42209c80b5c08aeec24100aced90fdb7020f1caaf6e349ad80e113e4e8881a40888d693c44f2da8a85d613d6a57b819156d7ca38bccc983df06c359e47a9d5d963a60e2b2ece90fbdcb0d02892db0f3a377a5651bf28bdfa4b10d972d6584a111317776da155a9e243b4832154b4835e6f5c70146123e6358581a185b6ec80203e886295cc7e15a7485a8c94402496c88f2f1f117c36689056f7ea273c2f687281f5002466c6863ca07a6e874245343e2391d9d8e84b0218a86a2f96bf678cb5fa5b276a69241d76124141a289f1e9822b6e13dfe3a55e196ff117c4e59ae3b0a87da3d523961c3d3d1f5af73c236fcfd5773f6f86bf24c1f276650bbe6133288aeff4cb95eb483d92343c590acdc70d6907c6e388526d1f5eea75d21caa7d6aeb073d239d1ae1ee3ba18ddcff5f8c50f513ed7bf73c237fafd517ca353261b9c41e4f2132b980c5d7e624613d754eabe3f3564f9460bb9210e31c50647b026dedf1073008415c4604d7c74ef7dc798a97bcef4217d8ffbe2ac59311fd8eeb9ef628cfb9652fe46b9ffe453ee9de3c09cd387a35931e6af08eb0dc479014e1465f470e356e576a62fc69a2553c2e6e8e68f9beb812b9fe0ea5390f734bb5d4b34586aa55f0e4ef7e1a0e1dcc6d96238671871a3e7388ee32fb682e70160c424ad74aa5581832861dca3295da14d98c2cade548d23d060b13811c76ffc16fc469b76c52f055184188a920391213184e08ea835541bceed90757b0b135c3c620b14b7bf6e61b2f9b0453ae79c73ce39e794ee3e7bce189f9b5537e65291223ae50f39f52aa7fb94564a9fb572954cb7de6a8d53fa1051ad9c14390ee0d7b681967960df6e4696898d95cb611b916382ce19294f76777fcaedeef3a9cf39dddddde79c939fe336b4b9bb7becf17e7906ebeeef32fa9ceeb4269bf734c2db289d73ce2f03dfa5574a175ceee459c3bc73db5c5084b4e26d19f89e9b5e2e03df7b89fdb2bb7cc1f48207ee7ca67d257bdd7c174eddf8ab826ff33c5fabd7d5a46c4dd89acb4fb618b969b2c5042cb0a72bd40823189c3174c4194fec9cc144e88c24392be47840d2115e162941d46a6bc94aa1753759a050c14634e3b220f103bda256b3f01c9944455b162248ae40ca122342e493844493f29324450558f4240515c182c80b554709038a4552a96c68398a2d4b9864ad76c8005abed0f2c47555554591050b4953b7842228745cd5720c295ca67777cbf825e105954aaa0c24b248820fb72d106c96306ebf6c6e0b8f99c1ca42ab424bc3baf9b92e979f241171bd58b38a2ce0570d1891587948640bb0f5b9f766b7c586aceb0ae057dc711cbeb171fee4ba471fcac51d16b85b411a128efb5aabd7af773eee4624f8258184ae7f189ba8b7ebb4f4cecee4a92fd9e32dff5a9bd63b487aa759ee39c7a13650d63c88de50eb23be51dfebd736f7232091f3dabf91b4abbe67e99e7b96edb68fd52c1a1958be153ca159ee42f7f539f0c604c329b4a88f794f964e2f5f93c75b5edffb6670beedf6114edca9f1f651dc893b366f576f63b35ad97c74f999f73c5a5700ebe3346b051c49b015c401aed02c1c1288c75fabf715c8dd7a91e00a31d8fc0afc123ec2bc9b1b0ff2ea2ac85aebb3ea77533d5e0ee6430f9240d1e7a527f59e7a0af670a0ac9994d8d5db9c76d5ea269dd2d7124a89a51fca1a959c5cd1d50b6952393094e1d65fbd9b74aacb7b6d8a80bc3e562ed0f79eaa7e45e35732e00bf141d0805b5fc81058096f430960fc8645188db7a16134dee669a8409c66b93cd00788e37239cdf2520e0e0e4fe6bdd5c7defa52e9fb928ffa7c77700491ebfd7fa05e11d0dc5aaf34d50f225f6880bd5e046cbee62f1bae7de27ec5fdf56abb66c0f6519a51d170a90c4b1f4b2ad00519301ce2b1e90236cf7d2530ac979b52e6ab1f47b98ff5a7ccd72ffc5b552a6beb579919a7a04e048378579aa957268cdee774f53ea769a5c1e68bf9626ecc67fa54ad165697ef87ea86d5f49f4db3bc7ed4c2d27ca8aa757a5cdd9e4d11d85634dcea7df1729c8c9ef749c983f5d101f97ff9bb5a036e5d7d5d3df72d21c16ab55a7d0597d46043cddf0063d38e152824c26a4016e86f3f9fca1dccad49c31ff31023c9349885c7c95929a6240edcea990aee76250efc721eb8d5cf71a1cf686ee8b350c67cc6369a61a1cf7c03fc2a8135b077434e429224bb7ef90912ceb81e2d719c62b71ea6d090c445dc75d06beed6c02f29eb164b22397814dce1b516cb83874feffcc85b3b782061bd85501e790786759a886ce8339f4d0f652c82e18fae876f84f14e7047b31a48bc7506246e32d6d7bbc75fa567ef815f2550cada03b95bb12337bc771a8a9a49b3dadda7cffc9a06ef4702fa0cc84a24ed0a7d1693cd2491768541b62b8bc99916d6fb50c62228634aacbceeb1b71c0c65b8fed2e30fd52d49eed3126ffa707daab9bb374b66668eceed31698608dbcc36ba670d0bf84503ba13ddf2d0816210db67d27cab979fb56cc3b5d819c2d44bc82fa7757737cd5f72e7050c516058fde58ebf7ca7fb064354bd13f41f22bb62a1de9787f9150bf5fec0bcff0c7ce3e54b3125af7d6aa65a44c13f15559046315d7b81914632312fdbb66d2ff288e64379d43edef3aaa901bd279a58e0ee0b0c0cb8c4869aa769130f9a9a1a9a1c2fbd7cf5c2cc3ccc0ccdbf3ccde7b44d598bf21e193015035e976756411a4514fc5dde83f9b8ca236ff9a784fca55154210e45145ea451b750852ef7330e7d0a7c01b7ed72edba1fbe118b3a1f6ccdbb13cdf2f71e7fd5bcbf3cf2570dc8dd3abdbb3c6a81bb0d3a4a899defa51e8614b8c4061a5ff3fd3c6a5e48131aefb129061a207beba5e687ead63bbff047834b6cb0f99a186c405efd0e4d3ed0802bff0618576b55df3fdef2573561fb7fdce583a001b73fa7579f9008a379cfabf9fe96d4a0c31058cdcf6f09096a4021112664071b4850f30d800d81d57c8e7bd1c4c21212d47c834b6ab081e66d80b169470d28a46134ef349fea5d3e94fd0112a6aeffa46ee83f40e4d1f58f13e418d3303dcf249052c414504881d464c6670ce9f9a20647f800e5080fa2a852b354cb2b2c250a2d4b52fcb500978fee901f7aa0c8a89ca007275fb4196210d15df9968746450c8a82d8810e3570b0a1ea6f64191ed7089f61dbaccf7d9bd5d9f1a3fb384429bd69163fdd362aa7b452d250fe46299d9482b53a159694d6671695f4e76dc26406b4eb74a4bce1b7f485f9b4eb5ea5b25c4f08743f7f9a5ec8c0ddbedb36c91302f41bfc52b62489889cbb577102083d4d40b9020a285b9011230387db5fb154b933979f60f101e7030f5545ca966ac78ee251676666fefcebf7811b6eadd5c59552a04ab55d5115f136359a7582aa59a619a86e98a0ddcee999cf6953dd4a949656cf269d9a5fad40100469d8d0a0f17ddf67420926702513bef4cffdaf9e69ac5d2f1f9fcada05f3f1e9ac5d311f9f126997ccc7a7b476a53eaa76fc45009e76a18868974b4f013e3e1df2d7003eb63e3e2da2f4a85df6e3532bed3acd7c7c7aa55d4b660f637dfc8dc85f00f8f85b91bf6a7cfc1ae4af007cfc1abf12f9eb848f5f8bda65d4aea376596997e9e357a47aa55da58f5f93dae57dfcaaa55ddd37e13e7ec7d32e133e7e4744bb4af8f85d4fbbfee3774cda45e3e3773eedb2f9f85d12edfa3e7ef7d3aed5c79a8fe0c7ef80da45b3c3cd6edc8e6efca41b9ffaf82b88a888c63ce6ad26c0daedff92b8fd6f420944dc7e1a5fb36a3e279ab573c31a7d87c766be25ac473dc17131a828e8888b9df031b76666663ebeea73249e692bf12d9974a4e7f232ff02f32cc4fbf2392d4d10a87706848991497d4e6b562f29c09b7a0af0b9ac5b28d009f0556eb93cbba03e7eeb636eb914f9cb9d2b72aea85d68b828d64713042aebeb640141d72f4751c6cea54f8a6ed841db26ffe3dcbded6b77ecf8bbbbe3cfda95525a29a594520a863fb6cff10d27a759eccdf9d5fefd5a4bad83826a41b51996156ef1951576d0b2c209726016df70e6f6af8860c30e8a43cd725a5d68de69405634ddc0863f585ada85a3590de2346b86829f1b3691eb2622580a4621202a3aa85d7dd4aed942eda2cfb7e9e710b26a543336cffcbde32f1b70f51f7bc73a9d325e699647a1e6766dafe5278bf51f544b2a7ff5abbee687c0560dce3ccdaf9ee7af40d5f304599897fe0c48c3ba6169eaef5ab33ca8e4c2403a583183b7925a4bbb5edebf87dc5f33eff6e43d7bf7ecdcb3bbd80e9aa9c2863873a80233bfda41f334e08a857929a80287c03cce6bb1582c098cff86b9e0167bc12dbe2ca0400c6ef165010565308b6d6c6e8d7612206cf3502ec9861d4403dbffa33f9c9c990c18fec780a18a0664df692ddfccd741def298b76afe289fb1de0b02831af4246a5a60fdc77d9ae55dccbaeafd7dc75f341f5a7f9a2f87b754e08e99164a6a967f0735cb1fb5a483660e7f56d04d7b2dd5ad14540be39f758b9168478ed7759d2ce6bbd4cbbbbcc97ea934bb2fe693a53e194cf7e0e7d2bd457ddd9f3ed357f2badad508aee348417707e9fb905bfdc21fa528249dbef421fd8b529e3ef43e99e9bdf7a494b2e47d32d47ba7aef42ed6fb642fef3dea64fa642574947e321a5ffa994fb67a9b97f964354ff3319f6ce6559ffa64312ff3309f6c0716ece53d17fba8ef64fa12ac8cc627b3f964abd283ec35a5a729bd6a46e62b7dcc57fad4577a986f86037bb1f559267b19f5a01c1ceedc874edd873f65953ef497a0a4117a5aab572fa5d5a3bf812cfaf33f998c2cf5318ffa6430fff2a74f66dfe54d9fecf428d37b946fabbc4f26f3c9623e59ea93cd7060de83d6fb976f071c98f72edf0e2a580e9b16fbed506139a09262a8cffbd3e7bde9cbc1cf88c1662c1c9807aea0b02b28f8de901ac2f58b149107134aa061b3aaa151cdc8c4a4605e5c2cea642a791d47cdd8e894de6758de01961f1bb13492b47202ab593200f7846631a1448912254a942851a2448912254a942851e2a14c3553cd54f362e0c5204231a376198a1950cc7021304902250b110f8ad1e546eec6fff8e53a18f256c79f40fc8a462ea13b7da8fad090072433450b7cfda9119e37c4c61e145c69fa4c1f9fb5e9337d4c2dcc1b8bc0f01a50bf260f579b3e93c7bd82e1eca9dc267dfa18f56b26d1adfef631b5302f074e1f2836821ed4ac7ef99b8f73d3c7bad7bcf6c34c5c9fe93393983f469ed702cb75d3f9ddddf26737edf61ccd7277777ffe30cee0fabbd10062b11b647ebc135cb9a0baf32f4b7181c438830679b08dd51136ac448d811e3c72b0f3f9c67c070224deb07a4eef0479b00d7fd58ead1fbf5437b007069a05e35124be20b12113f1e841c3d0f51d308174dd4ab7d180e3164ef172b3562ec41a41fa43524d6c2ac5c4364b3e4c8fa5d771aeff07f8c5ca723de2701d878753cadbe6c73b7ffb82ccbb9acf97866f35c11982ac5ef051f302fbee9bbb6f6068659095d360b134ccd02c7fce09db2fbfafd55a4bdb92109932817c108a13f068104941860bfe6b2496a12831aa97a1289972198a12a11bced8e5df2e435112c5355d86a2e4a702366c83e3cb6ac3375c7519647120a572361d126ffd22b3e89c74ca9b211e82c08f2a7a70400608206d2d8f3cae914a25c0860d008dcb31fcc2e918e5e3f4d74a44f43c26137d5766e0db4010c75b29bed15ed8106708dbe83755b1211371604b8f217c834bcd62d69447b8a072e2028cbf823b5d069221b175510100842e80aef0e08415306a3fe861084020a18c2eaae0820715172f0080efd8a8fc953364882c8cc0a4064760c28a99068cd07c504512b4f8e8b08518272e629cab00062a68d0841142b41f2a5664618318f0c00a10847eb8003a71e1039307e12be478800592c56ebd0c944392185e64a02dd88a1e1dff5e3908d2cd59b77b35c38fdbb33766221364c3a65d7e94163dbbec2d4371e1ced39d44b28c14162585dd63129767f42598c4764c30b3b9fc640b940b8443d265201c8e6ec81d3dd942840b94c3d1b5711928879f5bc41110104c8a2842c221ddfe9d66c524d19b73ce19c31f290f8c3bcd8a3ccdaadd26d2ac49bb21b231ac97882399b2194bbafd5782ae1014b6817a810d9dc786eee33d423e7487b0a113214922436285e6f9aa9eefccf39579be314f8451122d53644952cf17e6f9be3c5f1d4576ae7dbe28a26b7abea5bf0264051121232b5a2eaddd6d76b9a23b9faf8e23b4abc348d2f501039137ac47eff960435a85ad47d54abbbc0fe791136917039960e7369d596957e83d1729aac0347f318c7f6a4cfa2561fdb568e321d22efec070eb7930ac484c00c34a540218d62934c0704bc2060cb7da0a0cabac06d46184d62e07e2f2a5f97418a121416264a75d8ee4f2557d3a8cec1849622449ac5d7ee4f29df9741489c9644568ed722397afcca7a308ad089222488edae5452edf984f07edc88a151a52bb9c76f9a63e1d3424da15da95223cedf21f2e5f984f47119e224414218256d42e4fc1e5fbf2e9a015d18c68463bed721f2e5f974f47919d24498accdae5442e5ffbe928322b42a408115a52bbbc87cb17f5e9a025d1b4d0b418216a97a3e0f23d7d3a8c10199962644aac5dcec3e56bfa741c89c9644776dae5b3cbb7f4e938b27324c9912448edf2135cbef4d36104e9ca955abb7c87cb577e3a8cd480808c0cb5cb75b87cb74f879121235618b1e2c8ac5d2ebb7ceba7e3c8ec0891234482dae5395cbedca7c34890905051bbdc04976ff7e93052646464e4a85d8ec3e53b3f1d468e8c583162e508ad5d1ebb476847901c41a2c34852bb7acb35a2c38896cb261d46b448a076f18492e4867416693b3cb4270968083f37dc6473c85b51a4dc49c5a85fdcb406fc9a47cea45ddcaba0581d3051add82b21d5727ba39bdc6af5573d420da954d672746692269870c37ae42fa7825bfd4098729bcebc09b7fa95a84fdc7e8f62e64cbc8705fc0ac0378d01003ea446801f52246e7f3df25700405aa3c86d0008615f701b0807a24b690c8403911293216c58baa1f7d04b63806c0786f147697bf7e9e7cb4059866ea8f2249ece9ae3dee9cc7b7efeb9f7cdef3e16a2903b2ff375f7ef474cbadcac08765258e37b76ddc77b9a456364c3b8d393a442ba219d3d4d17fed32eae47d2859b554b471b916f936db366d1199d790f9366952e4c1836f49eff41fab6d1e3b1058a32e58675ce208a107d12a5b64414239404a376a8e4ec5765390b95a111400000007315002028100c868462914812c7ba2ef514000e719c407458974aa44114a4288a8130641402060040002086080d0dd1080080ec7c535445e532f93884fb0eb83982229da9ebe42df32581b4ec2f3cdf23efa6832929dd809b771201b0cac6e6e7ea8f85872ff038cc3483bcb1f4e2dc1ad0a85cb0c164668849af71b46098dd60fb1abdb43634a28c7e2f2518fbd58ae1136b77ae8a86d91dfcdeecd98b9964f161c22ba06e3cea35a1875af43d9521a34b0fece34e24b679bc287213abcfa2240f8ee0c3325ff95e5a83a8a07343a300082c3ebbc8445a3015f4e4562390396b24379290783bb61153be83f28b969cd2b4b0b1a67fb50f89f3fe51ee713861a9fcb7a4ccf70c1424334afa6dfb0774f90748b5658d4413ac6a00be8862e7615cf83296f9deb7e3ae6fc3477d1216a632437897363eccf444aa60c0beebfbeee071eefd1bdadaecd75a30586708b0febed6e6ff5b27b23bb79fb7df7e51633403af25519bded79d84a5ade66f45070c9f9b5cb095946a8304c644273173c273e62564053ffc691c993c8dbd624bdc9eb9d08e2d4dd0587b9d02d005a4fd3c79627f97e5625cd07275fec3dc82035431fc33cb20d46c91b2adeb1bca3a7e3c75d29d01857f935acdb75f0dd6838e0a7a9290915508f93bc43139415c6b6a6f8c9f8db7b91820d1bd7f3b147aa77c44379542500518dd1362b8a9bd26bc4083495159e04a56ae82d3a457387a9b0ad41c398725caece50c4b5a38de0646b69a40140d90c4ee70e68f2504772f70394c1e5e986a2e612d9f868a72f05e9417978b83b90237a2fef1693aedf6cc9b3d478591a465c01b9049c59f2b9388e18f74419c5b069357d24027412b2cd620ee2b391ce0ee6a9623cfd1290f11913d54d866a2596aa6e68c1d70d3de6394a2081e5d7fb519fd1738253f5cb5233b63236c45bac0fd7f5cf1e9ffac1cc23eb6662f9e86d8eeaf96b6b8d3a0ee9a4754ce578f7a5ae1c635c7c5fad91e993f72ac716a5deb1841b65c2f1494953d85257a1c79114b1ab148884232df06071d9d6d4c002949aea5b53f2b35c6dfd7bda3ceced5e47bdb7c7659a302462d94baa9203198c336896a54d43a491952164730036d7c6f6eae6f58b6cf0d5af4290876e6c7b95d6fbff0ddeb9b3378fe59eda2553e8322060f6832bf76f2a4c6d4d14550580bba5b311078a46dab54d0e8b90b047776b1f91acddfd2b04353f29c14ca5ffcc8b21daf376e8fb2dc971d48eeaf2122d23873849cd7a1d5506e69a3f4bb06ced8a6487e2f2b87d13b3aba4a70f51ada9d91effcadbfe1e5e285baeb857cb4f6a5721c68a11012e1b035a88220e481f4ca57b210a70a2573a2ef2a850b79e6bdca225dfcf686b2f251804a66d86433ee06fee373022f150fca6fab387ac62473434bd1e06b9c0585aa65be5a6fc77f6835b841b263df1d864d681ce10112c34b4ef04274c6ac8d32058208aa8616a2166766e5da85e4b617449cd2830781b1bd67758821be9016c8d9e59434b25c2a25d0e01b3105a5b06d82ce6cd5c71d53ca48a41fbb9429be943daea053391598c07d5b86fce768bc0f9c902d3721454a6b6a52807eb59bd5213843ea54f201340c48489d821887b6b19a634718b310524026082952f0f8983467c01416eebf4bc954404a45d3ae28bf008a68f6e3de1abc481bc4aee9f9120595fa910310d9d1a6f23ca0d7ea7b966e82b33c708fd2ca8edd0b709123ec4f1cac2f52be1255882ca7db0d48886c5089e7302e0e24d79c3ff204e1dc475874895df93ef83dc4e7c2d2acfba2f687969ff43ec734fb701295b075d72b821bafcd68a535620632c4de16c64c755f45b8c7c9fc64d8aa9446d818876577289f7385148ac94ecf7eba978e995a5ea99c0b6ea35dae8cc557e062a99f6f50c6ec9471fafb3d47153539d92760a07769b4dcdf3200dfe884c0b613b0609c64a286e36c8cde97971a45e4d101181a4fa4ecee5cd672f9516825a0d50a2ec440d707942fe1327474d276a4ea827b21c4f72b2cf3eb8408e973e329be52b170fdcfef02c04e0fe2c2257174b1ac6a5c35b2706f7e4b82ce9e67acc02c3a36e1a09470f708cdefd666a8fa488db83c0e414721892eb660071fa64ca7810e6074810560c36b84e36a1095dc445b8cb32e8ffb5e010bea2e06d10071c37de89cc2dad34a8798c36c7c266a6e22d0be81d7c006a8a2eef167f68b102d35853d824274ce909ab59d8f91dcefb90dedcd4db093292d03352e08afc99104481fbde097b6b1db05ef3c867c2fd651a3fefd778e15d73fad1df65e0b9ca534eceda5be05569ddecb8f9c1345a822177fb8760ff9f2faa88bf1455cba78d1176dc9fa70b904c2f72c8a72ee7b735b97e3f2e160c4af054602b0029019d0089163f65e46179c881b81aaa9f14061f218a033316e504d372ee84d19c7101056de626a3cd03415636f05b30a67ec6ccc5735d6e7a50ab0e5f1a6c9318062621a7b11ed2b29659b50502a943384a7067d2fc34b3d64b6c211cc6e9dae907907d3f44fca0b8a92c60234274863811a1354a3203408ae99409a04ae5120cd0ad64080c605692448d69008fcdf05ec0b374f4edfb1b6a8375ca56a8152156a9b4dd7d9feb572d5288046026b109406c135174853813510a079411a09d0b8608d04791a3281c4f4cdd3e8843382b879c224913046fc2795cfcd536eb786c83438a7b5438712d014e5407810c414380e981881d0018871601e2062eac33e807d84ffb46f472d5d9f934682342f5833816a164413813508a699c09a05a6a940cd08d25c20cd04654d1281fe4bc057525f959a5682b86681342b5813813417a429011a0bd69c809a05a24960cd04d32470cd82695220d09008fca6714c4c673cc1c444d61f44e80647f13321aec5f2702c6a56c4b7b204ce8bcc8488d697c2599137fb4244abcb727517e2e4e956114ffb78eda877c15d22d4e9d3b9c7f186b8dbbda2b414dcd801f91b1ab52004e9f9ea6ab009fa79ffe24ab96aa8019ced05a9c5ac9a9b62175f8a80a86ebb37833544931ed5b0dd45ac2f7dceef0805952ab285ff76be2eff6b54b787087943cdb86a1030785c6c6299b0278cab5d36402b3e6be9da866c566811e38a40a2ccd77436eaad57d61f6c2bdd56bd0377e215b15aad7c5d5291f1dab489ac5fd615b89e6b429a3e37597bfee3678f07d55fad56e562e27ac2e1011faafa48dfeab037ee323fa65e91e8088ccdef6df3d48c297699f066735e323bc380bceeca1c9295f02e6a2447d5322fe794c448e9c3f9231a99930ae8113e4e65904487416f95fa52e199ed9298480f564e64cfbf69c5c0e604ce60585e57a5f4535c0b9d59cb30c1de76ea58aad3e4c67900efe1875706dab48a7604f0a93c16b86fd2321c1ccf8c37e58a260b5a98b1ad5911e1b4a61515b94e6f8e9a567f6df7b96a0f6c095c96f2b77f4126f7466da43d154ee77d99fd9457731f4ecaa21fbfd18b4687997c8b44ba384ef76674e96d0b41306f0b3bffa537df2ca90ac0cb95a58712eabfc365068726d471658efd7d63fff1c73cf5f46acf4d59917fb1771a1bdb735b04a9d73b35260828d90d5ffa790a4e6d52310c88fc55e6dc17b9a31eec9c7afb15d81a3f9b62bb198a76d96a3190beafa70e0c515c5b2496771995553e183b32f8dd87b064633764871acaf9007baaa985eeb1005d0221d958c725211b1eeb817d661440bb15072958a232829855743d4f4b68763efdc9be5a203f31198c07bc36b780d0f306f0e0bfcd6290c0d98febfb8f9d970a52a0e44ddba7929443d990c3ef2acf1180c81e3e90326d608e8ae98a369c26c75035ae6e6b6b0eb07b2485ceda3b534055d324cd86415890ab93ece7e8644ffc8184f12fee0869a95e8b338aa2da091ddff6421e8dc5f95083b40aacf555f03d9ac64bf43b5d45aca8fea554631eb324f30d98dbe683ef10d71ec7bfdc0ff18ab747c95f0bd58c9b28a2189848e4a371f862a006f37597a722968ea229f354060336555d621fe7b9a40646c90a36fe2b8c7185b55f705bc2ef9e6d8dd5d9694880ff4f0abbe85df5a29ce3510788e118f119822af680e19ec400aa96ab8841b00e2097bf72bcad47f498317fc58c490df52eda314508f94ef3de25244cbb1b2d07da4985aa9180a03043cf5a39dff0c21daec4d78e685257239c69bedaa945a0e26d52e5b558ee100f885d9b77e221c1a7d0db468f55167f235446a090ffdb397d37c7474a3ff7db872c5dce1131f41f58bc1950f579d0df52eb8e73a126b1367c339b60ae6ea6da66c18ca686cdada50010c4d205c1db2e7ae80a479424db4f3ec1dc85b355aa8d2633564a1671d59555e47abac5667166ff4e6eb3b6cacc8292ad064ce0db22843e2cce2d8ede74ed1c403c25a903454e71e484f45ad51604a3b86866aa1f60cd9ab15f0ee1ad825e5bd14c4f37d25aa3c73266b996fcd818c3ae354c2784419f394b578dade8b78f4a991ca5871ba7a1995ddaaee826cc84cb6844e072cb6bfacb9392b894a5ca30f9cb9cbbb8004f74f1ff332e972395ab903d44ae550407637079006ecfed71ecf8d1180e9a8510604cb17efee6e236685c76d3f4b2fbdb5e8ca9a46a21e0c083082b5c0c93d6d542ddae26c3fa3b648fab07b2db82d3a1a6862078015ed82f05670eb09196b341ea369e6de0087b38c09deae250cb28776ddc25da2b59f54d878d006ab4826baab53cb3b88e363ea8a9c68d1a7f8c3b56a5605d5e0ac830b3c37ec29bcb3c66e3b40c6dda660d47ff618c9b26ddf0dba9065dfc1c5a20ac36d8cbf7ea6781aacc18b448650ddc472490c942ee73fde1d69464d44f61b67ed05a8c565696c894efdf35feca3503a7fe7c65af171aca0d6792212071b640143c1ac4c43777dc1db1a7ccefd3d38b9ac72e0227f49b99eb8b7947111586ec57a0b8af5e872407614cfd5ae67b0e816dfce7711c4deb921e63544554d62e16913b877b6fe85a38fd04ed7eb8ddff991cf93b2747b580f1ae0b95aa887cd33878e7dd7c3a0014c316ec1604a012d03458579b26bf682d090488a86020645413425af66c1bd5f949126b4875a23c8bc30f031bf28c992f62a89dfede0cd2810faad75fd02545bacda18eebb0991f9b74349ab9d16084cd69a140790deef14f3eafc5975a37280c2ff7cc3fa9f93957d186be84d48b6798a99ece454de60d528ce4418b03299ecb24d4a13a5c5f9db7ffa26c1b76862b6bcbb91ec8fb542bdc48833386d5aaa0707c3c2e126d72556ebd12f87be338fa9a7a98619dcf57da42afe514318e87939d84d53cb8997e192a19656a8d99a3af3ff84725fdbf1064770128624e0487bfc97eb87495ad6cddd784885ca3cc9ff59cd8b8d28c4955d28ea33bd9346b068ae9b6623a017ef96b5fa436794367024c9fe0a94d57ea4018b08a960538a8759cc4276b33b95100eea8b98eaacfa32a02acd90bfdc71eb8ce8a3712fd0ce2b1ac7687917a1c0b53b216d09b5cf63b7dccdc331f157278f9129e98cec23dd977ff3e118fb71f8443242ba14aba5349bf02567d472475af399ed4e8e77a28646a472f830e54f071c4280f0707d84b08cef9213a1c8ba15e36144682ce5287629ca7a590f01e4ad964c0a55588fe21b106a0875222645714f7b895a802b39a37b1f213857f923f3b7851d83a9dfd47bf2882bc4f9301c8b940d9630ad2e5509d82bb415df32a768750d097f5d38405896ba32c5671c49269d391d39264574b926343d134a4e61b1a9c687846031f0dc9b417a7bdedd3f6875f2dc281595007d5b8b7230d620f3b92d68256c0c8478f2e6e4478f219c2e64be2ea8e0c3d5af60aa2bb219cc88af458b6e8dac96029b6b1f3b7cc53f3bf0afaf5ca584a341cae660fc2dfb38077dafdff4c7ec10026541258822567acfd17e46dcd7994ac12c8b397e9a8364b42760f8aa3b18defd176457b34c41bc8b791d151f6c57d34da641f658a1764764d3cba5c13c86a99838cdec98f6613c7a331d2433ec118f23662898eea16bb8080e4ed44511106f2c28ba2f39a91c2b9fe7a29dd377688ca761376549e4a4bce38e8e4e5e313ca2ea1211c4f152717029673e75328ea5a15cbc2730ce087805d4dc174b0ffb5929b60f748746c28df86b6a9d0d6ea7de1086733036a47e5d22c1e0d4136a607b6a38230cdf0b0b4f6ed02dc624fcbff4dd5c817d2ada25f6efaf6c51e1bee58a4651666822d9fc51b29fa3492a7d5c27f0b35f072758abca541e00cb274b134ce4cbdbb8185b82a01706826c5f103795b24c66383ea4754bf40ebe132abfd7c8c2a44e2a412d51b2824b7f9e8ac7e90f6522e708bfa7823cc7c085ee8319eea9db770d9fe23cc11afcaa8cc16d8237fc7355f640aa553d0dd0e4ed7ff1858126ea7810c9b2968d92a21446ce4377f89c9ee3b64547203e1451b792adb03bf9f19fec9e40e2d7f1d23a79343f1773442b1077183a15a8ec2f3a2411c9b6b2ff41a65ab4aa2e68201ecd394c67d96abee5a36130109b1f39f18afe8821eef96dc687dc00c0bf5707075538ca4379ce8ca48523022b08b0dcca2a189bd0f4d10716a88be74f8740dbfca1cdd2045866ab2507739ff9c201c49b00eaca7ae7410ca72628205ea91c132e4af79f68780ad9b180a5fb6d66b2d043da4aa340e5ebf9321c65122fa58b832a89291662cfafa5baf975f19b61df77a60629e652cef4f84e5cd966cb2fd0423428596290bb24db5de4a4018c83ab2cefbbfec3044e81264ac8cb48b41524920a628b76b0794e0938ee26fb28c7bab2277733d2501629b167decaf8a56343c3437277dc6c21f1bb836fbca073679202a1d18ee998dc5b3ee266e45d4a2c5bf3177ad7e4b784b1437ec5ba7b4f89550d84721ee57c52607696646178d8f50d9dd147f3d22127ff1e64b27992a9a9e39dbf1780fb983d7fee6e384f072e3205bde0b5135bfbbbb73518444fd5828dc44e4a6005f01b8f4d5777d9d60ac47253aee53d2c5348d19fea8474b42167056e18e4767005408e9c10185001c5c5e21dbb1723e01c8ee9f7a001263a5d1820a1b0b5b9d851eba3c4c971f6cf49b51723ea8028d02e0cfdb390a7f859cd87a06bc2bb54856086ceebce38dca85ca45b1b1a526da3eb0b323d77f83659f0ead440e52f82df4f6beb00ebdf5176e68f2a7d007ee2bec57946f0a3f901a95f5a6d80dfd47856922e49b73c9d1e1041a575046c24a309a70a0072226f182ac4ef769e960e17971b272c712ad2c9bcb6c32129a512553df47c04335795424b660421991d3801f8f40fa12cf5a4c1fc39b62f7462f13189cba2408ea54a97d4fb17b2d81153b30401a2450bb5ea7eefeea4a7e14370e180950a2cd8957f9b161a8809a013cb9315a52420df675324577670f960d451a4cd6e326228bc74c6470075a9e2480d8890ff524b639bc99e71f775240aff488bfb8663a1f2a4e269804acd5f91629aaa20fcec856add00bc8bb0b732b929612879f307b6a0c442e8fa96ecd68d12ce6047416c45617314ef552645588a78605d55aabace2dc06334c5744031d862311e6aac0e11c822fe4aa924e6f3e2af1df66c6569e2f5bcfe92540b0a9d94368be78955f68310d61519e3ccf150f950941c530ca6189d00b3d4debf4230765ba211557d49471e76b79e21ee279804d047106d111fa47508d04ba83bf191cf8748619ba35eaa1a73fd83a7f738c21b66ab79a35dee6c11f9b1d3dcf8ef92128464aefc50f8a9a753db4c2929c0d8a610cbe618acaf8871ab54d03032c9452fd276003b4b56127914a2b25b151cdc6935771349e9f632f235387b46b32a5a914e3b930ae6a6c820056c050e36f16a1c31078b72043521fda6990207d0715fe6c2daeb85ba9763848899b02b2a331a768c41df80d9c3af52818f3aa24eb5997db0d97a7b961990990045156293b93b59a4b18e14051eb8f1c87ce22052d839030e184018da9c89c2322b45c9ca74dae5250583cbea126a0d9feab64f63132d9eb3fe80927cf3e2658dec9b4e0efecff2f25e61d14b6ad803e3471e8e57b12e8d5d59cc06da432cdc1acac974be240be549f50b17efc601427720dcf4df1e8b5703c4a8698dae7afbcd350f8916d4e7fb65d24db08ead29be09247b9836f7d12d0571664db802c88b13e81ae90264a8f1642c373049cc40749f52c325c248513e267db97c1ee16a4d212c5fffc25c7fe12d5d78de01d1af2b43f2cbf078722c0c42b7070f95ba41aaf7846a48321010c3adddd0d151adcf379bd3d3e04f45f6c95ca4ea14383d60c19b5c48cb2f6ade26769d312aa7220f5c3764abfe444593bb61b3c365d49d64af3d8248c492ba92e98f6ab4aad53e2e30e07d71858bfad3a2002bed61b78a578fc13665b6952550f609c562ce3f0b4c9fd98523d4a4b0ad28b0cd028a010137efef678be3723cb1ec182cc63647a22ac42f53eb2d840b08913e888cf63c187dc5a80809871ceb0c09f606b000562b4ddc351d6e4090b8a74e188254a7b09f62233b1cf7290484b0233be94baeacea64fd7928eb4b60f348df17e3e24b0408db4c67d2e248fbeaf0224cb9ed75225e02e395345e84c81778e40a350cb2fc6342acf11b918a84436e45557bc467251d96beff25167210601ab44d22659665ff1c7c37fc8bc1bf4aac016cad86103ae38e570bda2e21916a43f5eb2738e255a09da4095cd176ea4311bb70044d166a2159016cd33b068624cbb60256c957c4256690958f26931790ac9910b2be31852f5db5ed71993cf51256e44bca856a9a691544fd358d875891a0c34c6ac2976f164a209524fd7df515fb4aae00d57036c063935124c9e9309a4b4e144d5dca9e724abcdb177e835e20ba8663cde7a23b14c4ebd807d50bd53098c85b1f1132c99141e5de4a1c83dd050e33c3269462c10cb24b8b8d45e689eb07d72467eabfdc6b65cbcd4344c11b3622edf0e9c07517994d5004fe53fd59229eaab9196a4595bdd6de968a149a00e3ac8572ed4c6d9482a39667b7fc31a1cc006f9613de3b976313fc27bd15457a8a0251a13ab41f4c0cfd2dbae20b6b518e20a92c0d282529b8ade767e5b17f154f00b7655ca8dc08165315c83885697a2426f5a2ad82f5aa36d64f3f50abdf68a8c08c8a919e30938e310632c94a5a20d5503701e8daad811da38ab96e73d859b472bdb9350c090ada364f9c37e351aa4b08a02fb5dc5c0576f778a10e8f78682f4ee9f1838fef174d4e2450651ccca386e8a5ae09e3c34d98613af03769684707e803bc55eae377dec401aaa5ae960f5331c97f8711f26b579e8b9e21d730204a82123c9d306c831628d4f01873d919ec8559c5380839de19623ebca391bdb5052e5f7dd52735c4ff978059da176302cbc19bad255932802510abad83b4240930f50792aa2fdb3d9214a1543281d5311d3af008028a302f4a3e97d971e06c05fb3dc3ee93aaf4682c0f34b0cb4ef579ef48bccbeed5c1fe82cb0c422e095ef3e19bd3b8ddb836dd839624f2b0a407dfca198a7a4427d95f9ecdf3811787da9f83638d3d0a0936cb1e250ae08229580e8531c00800805fa54544b3219540c34fa65bee2ecb3a5b26ff6195a538ffeb3de99d4f5d4ab6b42f52429b9e6bbb6b6ae88d72012f2ea7f63921acab3e02efdeaf5348a41dcc39b1822f74b6d66451000bb841e46eb0d41bf6d4d4e9b3551629a4d33194540578fff5cedb8e66072db8594d611a4048fa2754c519e4587ab70b0422dfd4d1c3fc2a12ef41807a7a117833df949db3ed5bec1da82a2546fb9621299c23bc340f5d111df60c802f42b8ae1d17c83d7010ee4d6f7aa90f479f537913ea015ae4798d25e1929011534b3d3f167af9b06c5679890dad87545d5f87f5dbd1cc77e8cacec93a14833ceb948779086792aacdbc5caa0bed9a918099266c23eac3402fcd4c30d10f05904909cec1d379167e1b3361b7922f3ac073ee60cb95e3abcb5d1ed56b205f437f99a8216f31384cf801e243b3e2827ea0c1e268dbb06d1f660cc69318d00be9613819f5ef4e55a45eec410eaac4da2331e70bf4d17d1387ef199b51fdd96228d25c35f95bed35e08d2b2aa9737c5ec640226fc6d719b0317b19fea6bbb637275590da5c690e5e9ada835cb424f4b1ab221098243985468fdf653a3e9fc09e485a202a166d63f016e5c563e8789993aad0b758176cce5042d0b0f56cd8ae483bee5250e78e6e8becb374f139d3fd4df3c4846f6922796b8963f460c5b734245a911c7f2340201c1a48fa15ec7082ff61fa3e153fd8f1784debd606118ae51f66e91ade0840f6a66ba48b33f4cdb911dbc69dfd54fd31cb2c23ea20bed949c19e4464d19c1d3fa605e425e826e9a291271caee502c342299160712819e67a4346b86559887ceb1449297629727fd5dc7c9c09c054e9a35078112bf8525571202f80a75c6bbf18c909f56ecab8b8ceb2b90c43a60ceefefd3a7b914a83c249e495a7a38554dfc1eabc4b37036f1174c29cb5a2510188b7929a3aa6f955bc7d0873a00d05b35846e876d9c3f77231f0feab991dc160bdbd0c9bcc90c922700d22305605d0a856f49665040866ad617483522bfb385248ca337886ed8d3cdc6029152d896086a6a9b0323857c536297792fb01ebf43861572d55cc99bf9ee5248a9f7f2067768cbcf552341cac6665386d10dca4e960b71e0dd50b4abc4cc42b97074cab42fe7f10374ba2df06367039c371e330c47f743b1f784a4b38a621e53c845e761ddb8edfa408779f3e3e65108c1e4f7246f1a75d6985b46e9a33ad38e984f421482936c15a53effdc0410255e681d9dc82ffa0a00df4ee67114630e40d90d10e23d8a2538bcb198859396516dc6e066baf63f9275aa843c23c9c4cf1da18d8fc09bbfbac76da11ca92915606aa440bfbaf4c5aeb7c2e230a4ebcd6ac4e83e45c6b017e7a6c7fc262a53326fa9b6cc2194e8544782d0d965ba7886ce57811be7b37319d5120e91db153b5494d318879acceb6d6dadfbe6bd4b1adaeb4d2eba56fc81251b3829e0e2609743dcb12699a242371f57daf56f32977cd09c19093c0529cd498df46575128272eae13b6b89846d6635884aeefaf69dc676b8bf63c5214f29f2ff6e6233c439f0bd4f8ab259adc019d4594a0cb76816babf7b37d2a26a94d2e28c9678229b26c0dc50c6ae9c97e87ecfc0f7afd506078e14f7324b9f3a5fc537e2167e1025f2a82caa2ada84a5f34a8fdad0583420b384ef7b10ad31abc75ef0857c5083498a0920e89a861954e48608fd508aab04cd04682e0daa2440391d8b2fb98d5ef58076102673952fbb6274396c87c1c92747eea2d9f762db69251ac4a4be8c7b6b59af3fb39458729b48269f2954ecd0020559b1086b329d5d9ffeec9ba1023d7d7aa1df47fc068dc8e86a44e5b8a9ec4bc1666ed6cb5d46a9404cd403606a61d33b040d9c7ed75872c496cd90dfe789e00a1a63c475390a7b70c10361ad350399bd326c13e17cd475115ec9a906fc935a7c1f9e1a9aee698f45abf89938d14ad7433ed378209ccd2939f5b419854b4eca15283102152016efa3c500448e5f367af48486437339d0c4c522269afc67947d7ce5cd8f25fc30035d073d858a41f5cd7025487ecd349374f8254acc53c4415ae160cf4ccb787d11333241950b405b06b42b12aca04595f45db6ea180ba636f0c0e1ecf0889b6568572eb0abe7ec56d387cfa833f65076df38f636d1f4883c4355940de07f68af7cb632e7162016cea1d2a53d211c74866522b74f8e681615b45ef266ac8eaed1f3504241c74d2399edb27daa71c191075ee05f071245b9773d6b91c1f5046dc9174abeac8b2d9606e95e53ce2f8532188e6c850d396844eb1f590fd5658d8eccb2a571183de918b215a89abf50c86a38eb0134569a0336d5a411a47df326f15fb04d37ba355699377974b3206e1596209809bd7de853233a81d6ad4c4c6ad544abcd463b06ddc783de1143d40f2e8d65730d6f1d21f21d7e7de5db757e19c02f8bb1fc7c6f5b5b56d38b693205e9fdbafaf85dab131024b43842c9440e07d886f31be59929dc25343815648158e708683fd992c2b136db326927c83208af7755d4f73a3e7320c489dd20a64e10328523b1b96821bf01f50a86a99c5162b0a0ee851bd5962b36b7c5cd7419c349c41c57f423a83c14170ed80b021a4a716055763026d6b8777f3aa26ad00ed87378a291b0bf5c76fcdf86c70ff65e92d7c88b52db0c9ca46cf5a505d81be43578bb8480ae6564ccb323f05d27a0b56048ba84528109eaa7766d12ed0b42da253064a1c54faedd59f227199f0399ea5f77a71c2418fd0b9b1682d902a79b7a9665d7dde66e99e1b4bbf247d944b8351169403cfb041049963800da9754a6a3d69e418f49344fa09e77ebcabcf89ca3a7b0f313e1e14bd84ac7215ab7f68380b10110f4e2b743abc505d46650f5a5a2cf70c948fe868821ec9125f89e5b49d475c26685ab77a6be71a9d9ba734ff8269871193bae14d92ec87edeb5c233571c6f2b4b9a45e6f9bd8adbabe6d48e9b4807cb8e0dcc1bbc5c845b70f31481395bbf1bbc1c2ea4dfe1473bd747a90b4dcc2b6943eed0a084a7ab566c612226851cb0093fd4a6a940b0f0c7bd7512bb561be4ec4d808ae1bbac633b4e6f98b29189dc180376055facb31deb183eb3108bdd19cee34b1fc3664fe90880ed48e06cdcfebee01ace667b76b2497b534ccae812a43285524d0549ce21d975e51f9fe79bbcfed5dcb808d275a7c9d3d0bbfd5e9f5fcb4ece3518d7b9d019fd831c3ee7fbabff901065666765e0558dcaadacca96a61acea83a69574de1556d0e51261824bf069e6646ab8d8b42f533e284ee826514c292721e175650c3461efad0fbf80e6cb30e9c2e957dd6a1c0d62a0ec415365453e408d23d12413154364961c083b74a6ac0350d41b5a613c108a4fb257cbb9e1ff70972b7108a3bc5f7a2b2f8d20074334eb7bdbc45df8b38b7a44b7189424e087352e9ad16744a7a4f9e7b497d80a429588eb24c79480f7c99cf18b2e8026913ed46ea48e43eaf1b316a29adba5fe2a917db08aed527aa3c2947b09f203c5f1c0cf55a30bb48b1b764e407b4deb76163c85d7f8213e287576cc9e0db198a489c8b3517ea731af08ab7ee9a1938772715c40bc632b2bb55253c0a8a1f949b64f30c84c6ef8c908ff4136e5564eeb59eafebaad2d51f11c686d2b59efebaaaa2440f04dd6b35643221e876f89327940cb3dd0d1f9dfa575f65d3cdbddfa3a2f8e1a059912dbd592c0328b05a27bc991b15ef5abc0259709c20cadabc03836f32631602feadab9b1dc260c33741094bde9339d508fdc6486677efd7cf8afb058bd5b44a1c26937dfbe51b38444bc1c38914817d6997e48c37244a5709e03d8a9cf1403e66dd590cf045e34a2e8d27e4ba5d332da67db1da71e55f46a2180260a2272b74d4758ed017ec3be8adcb84a741805a60f6e2889366436f1f8ec8a890713e311c3627bc339329b6a6ed89ce3db599349c48d877481fd2faebc7da5c540ca449a2335ddf4effe26a100a223f814a70dbf39e1b975227d7750b5cddaeb045e2777de695c125193e37bbd629be40437ac504b8147691855ddae66af7413d38385899d570999b51f40c745ca3f803abae28214299958406f037574825fdaa72d40d44e0c372dc82d2230657c603462ab0a3e6319184678ba7f6a6ba3e46958ad83115f9a23f64a5ee64ef9c2ac427d5f20138d56e46f3838c7dc8cb096214a71af14bad5a198964fc1b1e26c1373840b35a9150c2b3067e072f134366c3ff47b261a7d9c0b58ea723de8219750c4a2cd7ef5465b6c9ab81a01a9c16b8b2c7219b30d60d16fbd7050220f0909f22dcaff9de6646d91a77854dc54c9e9386091cf514dacf68bb2e0ccafb3512014ccdd1fe201052f4d4dfd707794101b3470137807fd5e20226382e8811dd2cf60bb2e9d4596583b6630cfc774ac317fb8d1d06d446247fe1d810ec4ae37af2276f4f9e43824f82b364d09eb7d799a521ed3196c1b87dbf6f597d49259f884ccf57cffcf092423d695241c7a28dcc25419e528f1b911d7454f69cec6d4563a2930326c73d6c9dcc5ac03098ca1113443cfe1380ddd7078d02d267c84785217c21ebc56f6ebcc58df9592cd542aa6a3feafa99de88d8c43562f817d2a0fa33d2ee615a837d0cdec29044d4c83005f8292f55f71018836e39c267fabc88c0a4027a6c90874cda9d97d0de4b6235a45c310b526cb647c55fa2a720d9d1791d3588539fdd25482d096a6cef135acb87de3d7dd00cde158a4752ecb389ec85d412897dd657b66013280b6ed46593df7089074cf5bf784852f3947b72a3941b62632a6249be9a49ff50202a3655269eb47b687e867cea10e49f7e31c5b5aac2f8270385f46d5e5da7c04f7efe05f2bda08ba2603877858fde91fb285157ea2f08faab0262e49f24536089c900a73c789e91440e470b419dc28d6c18092b0699ac5f63ff7660a8ef2d45e1a46e14e2c72397974ebc38d353e49e6606235d09783314c0ed1eaabc23d1f40d52ae09e0bbcb640dc4775e8a6bf1e8ffb2e82318823da155a6d359ea88f9279a6c5a959ccdc751c9277043b849ee2b5e4d58d0a149c4c3417baf15a9580e359a3938b8987e3e225c98e107e8a237042406c02f4fd07b46e68a46bb4341681abce0d3d01451fe6c1c6453df4db7924808adf9d955ca0c3c11c4539290d830b94ab7c45ee58feae67988e0ba494f60796eb3da83a42c3bc37eb8c3377b3abd5fce12ac9991993c6e5c08b13d9efa7542000fc5332f65c80642485653b8db3bb9e25d7fc59d78ae362176a052cbdb5dc25883864306039b5d3c07e7f404e9d30247b2db58d1316912c2e09c2c76598dd8c910b2473063ad82b277cf5efa88b2d1fdb1bb91787b1fb220e828de6e9565dd3be5332349e13595f2aeab830ec44f27cb7174afa4895108141464782e785fdd5850e628343b0f2a4de4c273fce5dc3e4c69f6c3950a6f9e57a28b501bb4d30e40ec47cdba891252f347fcbfda53cc99ffa99d98a7aeafbc62bbe895edfff2101a6ee13fae7656e2530524d34508a785c2333aeaeb0422092cd6a728f7e77f36c3478315ef605da4857dd94684406aa970550325cf6207a2a6b684a9569c29d9d082a8c4906a97f6600383753d5b45f53ba4899a18e0bf9150d4cd20fc80fea9b0be1940794e365c716cf68d76c3920e30209e66dbdea52aad8638a401feba9f31623419960eadc07de831846130f2912d2622f68c0f070369d700a0310563b14bb3b5f2f8fa1c771d3a70698c4a672bab3a00a21e9d18222543d7de3bc5cd437d83862420e594b94549940b4ef0f1b502e7b5765804b88d4c2529cb916e8c3e2b5d817558c65a1e437fdd02d433b2eb9908512b6113b5d6fe316116f28626cdfd57532ab274421336b089a94d6cfc44dfc3163ed9f4593fdcef82b5a6d086efc9236d7694c4090ff6332ce38204f4fb0fcc5c2dc333e38d4c41d1db62a64db29e5b9424689422cc8a49ce0ecddba4c2464417f69d4533b2ca89d3ac596d792c02d5e3123375d757986d5cb64381c1d679320b878a3967bbda72f48e23dafae87150f532bd4e4afa6f720ad328378df77225258a165e850a426960e96e1248f02b431ca67691ddaf5ee4cb46aecd42650237cc98319c8dd381e22610bc673e9b65acdbd4f01e23889324d0f1094db25440179b3c29be324ec7d5d3a86e2edf7bfc81aa406da74624275f3614e683bdc9d5a6080f65edebde3d5e0a2bb15240c4069946bde08dff15526823bede9a40f55242b0d88ca8d189d96c80da1760ee447f31c20cdbcab769bf05993360d9853711d0daa948dab74621f688bd8c239f3f7c4dfe832f5dd612ae38c3fd49797cfa906d945a82590583ae243076c91430791eff1e7ff28aa2295088eb0f5ceaf65ce5cf154eaa2ddc5cfb1859eb1d19cecf0f33cc555ef54ed23e4e56bc7bffaea7cebbec3916f2299b206fd866e0b7d909314c3c330c2a4db91ac8b90e97c4f7174384d4b434e66dca70565cef321b60d8f32f1c51bbdabdd358a6235ca519a5929eae0bc106a139aa186b391a35ffef071675e86574a86a837934d71f8d9bda423396bf1075be573052ff3e4c6422a7fff01e6ceaaa0aadc9f61f257b5313f6d0896d52df8796397a2255cbd3770c25022e223d4385e3c6eb9740da043c293923cce0bfaee7831f827cbc5e3de43b16c885bba4084181ba3b877758ee69b014865e2d162d6e02c38e5b9ee0bdead289b53f02608a2b1fad39771c570db1eb037a632cc791e16a353f62b1e696623623c71d25a1518560583b0676b68e21d07c288c2d4c7e9af21b73185ffd1e2cb78e64040ab4e6e5ea1cc7daea2f861fd3bbb54cc36345e1a26754748de2d2dd40546c0c2f82a13422ecb39e1e0a9d3038c6f79e5cf4b1d402516c0ce48ad413de5188c34154f67b21d1e9a1a5503408cdcdba8bac77898489607ed6c305d3d8d77dbf21516433cc90b790dea9eb0bd71618cccf4311d1d03d401917885228799bf13e6ef3ffa58adf877536500519e5e017eac9eb81530193c97e394b43169f51f8be3c5d60827e0b16546b25d0bc9643adc4a5a64b71df901f8a259422826824d127aa3b3b6c5dfe4f2dbf9ca58e7222c5a28bbec292ec07a1bdcbf46c8591734354f3838cd3d64aa818ed57c91b97d96acbb1e4ee547db21b544839987d9ae60aef004517b98e53408d289ad050bfcc5caf2548f19a8b1db1344cb8a16d82be9488755127f20583d47b6d1080457dc252235e6f659e220fa012319dd1dac78eb8c3205eb44f24a6391829336dc1624f2004ec43083c193dc613de26b861e4900bfbb97b7993dd6ef273bd7b0838d883c8de25bae630e14e4ab3cd75ba87f4bb9f67537c4f572b03a6dcf6735223441eab2d1aefefaf9a4f64858f57d3ab4b68094664250255f8796072c1729b9b8301676082810eb9b3642357ba18870cfde6e4a84c12a9f4fb1d288fb6942c22c90126b8f02ec5a94305e5efe5cb4c5e97eb7bd70f65d20db02a6104f9df7381eb5c94b52b3e49eae323e91dd51fca6fada70ea1d051cef73822483c1afc7e7efad684085679bcbfb1472a8a00b77e5aab0f45cdc45e5821e1a8b91764c884140d030db7150a7e0a1c88ec968dce979cd8cffcba5251e04dc0e3fb3ce86e06d556e431555cffb3547a082af158a4747f5b21bca8ab62d6fcfbf9c8b160337f0309e3e322efeb28245675e55ed4c4bb4022b197c47007f6dfe4de599e86b64a3e3c469c708f3cdf0ff7d4001ce285030c99b4045bf846318a8d0f2ea40de7e07b7c67d15fcecea44be28af6f3eb3f49e9ecd427651f17f772b702939ad19e88c71eae5e4a3c4132e2cc6f4722415e10de3a0e6e3d0c66e019666cd82fbde6590f9f8bfcf7cb1e33a7e72a29f54136659bad1508c099251fe06db1f26c11d82b0cf9579ea09869daf49c7b1121d3e20d63c46c4a012492ea8b22b9379225626ec8067f1b2989b02f9ec7c39ff43f179d87e0927b450e3476af99efbd072ee66cb328501c890632e9d0bc8a25810560f9b848dc37d9ac7909b3b16486d1c35b77c8272c392d9a272134614e17895501befb839e463385d43b4c2ce3563dba6cfc1d8019a0c9c58410b38f983c750abceea909dc77eb08ee774fac79b68c99d094e95ad86041abac2b3db65a87399f7d92860c77a2a820f9e0bfe71b72ce020e83af529b8b16eb3cb1dd0d602ac341f27c5501874172cfb1ad14f8934d7ff0daf8819ab8efb8fcd72ddffbe3ff6e4ac629aa570a93f87eeaf850b5200e9bcfbd02377eadeb6245fb7795796240e330f288199b21813eb02dcf877362a176ecf630ded65d16e70f04e7adb1ef9295085f0473f921d63d03daf059cefd5dff5d9eb8b4f4617f56420022c11159fbdf098ef3f0e2f990039a0ff123aae1ab14052b406494b543d5bf26a6e9f7d3f8bf0815183e5bddb342f42b273c7e1684c714764f222ed47075ab0526f237b7f10d75496279d6df6191322c51762a9810a3282cbcad16af2ea9dd4ec0185a25d763e4760f629a33ee6fb487116851fd023ed6abf2677957105ed49f427d3cb05a80daef4d5440b72f3280e4396d8c252210f263eccfd60ed772536dc5bf6b8bb81dff8b0de17672281357bceed64476f49c66ee1d956a90f4c5c54bcb63e6242ac2cce61da569302f59a5cc6d3f2cf9717bb2f5208763512c95d77aac787944ba793b544bc0edc99184a667d3563ab0883698c0a7822e72afa05c1d8206298cc7fa124d7cf83a8e59850fd63ab6d3210d95c2c8ef18dacc6d1246c40ffd0eb77ef08d7b36809342c14fab1f94d1a5d25ffd0b5d117affbc0bf5d3ebd6199914a7b00839b5d75611f2bd08758e92df09b48ecc5e06b2784a32ac15b51482c7e338f4068803bb51c485cce282d5d5b6904e58fe077c466b3fb19e5c77b31733ae8d581047c7745b54e02275568726dd7a222bc6cff5657577c98d14100482ebdeb4803dbba99fd38a41d716ff8ea8c8a48dbc9ece2daca1925c37e65bbcf11de843e1d5a5d2698395dc80fcb27c1db4d909af7d567c904028c58260f420577c052ecb560922ea499a211e34112399f4c0111fa5049043fce20332912bd2389d5b22fb4faad18f5a529c68a8e18fe4d77bb2945225ecab574f031e6b9d11ae622d4b770081becbcb58b826a76a53d9cad2e6c5081ea66a41f4b227af299d955ccf3c3fd0021a38283fb480c4c7bc977066c4cf87d67ad675139ee365a9c26b438772bc71d6062be6ccdb57e64e3fc9f73da61799d3d75be94131f366ce60e241161365ac3433fe024c4e269cda7821c238cdb17bdb7adff1da244e5b41dd4e6bb24d92a5aa4347d3bf1a06482e25d25f1e11cee35231a40fe86fd924b1170a1b048cd6bd6c165b01ab11f2e69101c746127a36a1fd588c461cb894f9ca81b5d62dbcc3ed4018ba416b185a20d575f3b0bcef1bbf3fb0f5a76c452045e77daba010aa61e99db2096bb4b86a4e180ddf89ecdd92ceed25e0546b1dbb73a1f3ac63a36661869aac8a5c1a0c7db7d962bc7ce2df770c59d9af7f8c56888f22f122b81d080b7e7f3f5057179cb8a781140ae08769eb75c1405dc3b14cbd7ff3add8e50c1c16d2200830aa4c1e0e1481c6e08e0281efaeb48d6709e975ef5b1b7fab4424ac4b52893b012eac2dbc9018bbad34359f9d5876e6806f281326a19f138352c3fc2b5744fb72ad5f22170e947d295bfd0f5fc01fa99db277f2b65f4af667a70142f25ab72578afaa27c71dea4e6d0010c4833b79da59110be48d79c23df2df35911da18b905601a7cb43f73078591c224e79036bf9bfe4779a18eec492ae40657692870d59a6e3d04501ee2bc12416f56642d70130664d1be012b8204ece050a2735111243e109369158c3ad6b3a069760d4daac5ac4a31fa6340e34506fb8e8265c4a8c6287eebcc405bac80fbb4e19da0d82c00861bf215c5a2292035d8a7022534e7263e2bb5dc06726e70623a40a3f9f3a08cecb554a5aee23317518a42ca27b511245720a641b262108ab3dca49d8613f159558ea29a4d8cf1ef6526907f8888c78f25a6fb2290c6764ecf139b31e6cb256663b873931e469314ab2aaf09019485259b3ce366eed91e281c89d72450392eb4c03fe944918f7ac90d67687d256b26cea2388a76ac1d80c15a145c4b378168c8760a5c855bdcea1db3cc55bfb9aee3ddddd9701acc02de060d8835ea6f38bb79d3b5d63bcb1743092e1c264dced238e8010dbd1aa0580565fd0edba204bedcc1262c7a84fa186120ad9e59bfbb412725b78652cb3ea4bae67dacec2d48c10109a3ce23f2ac6a58c1fdefe95244fe468961f3b6624b598b60b6269ad7ec67d018670541e4284e7b106ab54c3f5d0bbe70ffa482069f946778af30596ec5856dd8e63f980905bc3f969a58b721444268aeb4506a6e7d6c4c86bca9eaa16e7fe7a148b84000f53fd3ed98f49dd30a4b9ab95c72eee62fa3ef26bf61608ecc741749385cb5180b3ef5f6b0cb51c75485f2b58637d3e8fba43f21e4612a47c55c559c3ea1b6d2a555f3bfac0adfa7a1ef55b9222a0baf6361e4f3dbde3f6d724ff4997f3dc846952e3d213c03f95b8a82b9c20dc3f361e23a689f8459678d613f25b4351bf22e6eefec997b1f11173462c09a8a8e97c8015e6600b2a0ece207ff734d36e9b24610d77538ed5c5419681764c312fa2ee7d8666630bcb2ba25cb04731f50e11fea8856d21207a413e21fa38117923e96b055971d1a0c2808091434ea1a8f0a0a60b66e7e8c7c8a137de7bb4f7299fcac6a09ae147852a5c0af7d332c53e4a6da33d02d31427a981ccc117e42fbcd68683aacdc96424dff0035ce0aa28e6f05c840b9e73718d485b8da3974de37c34c185d7fca386938909972302cbe5303cfb60b34978257c7d68ee3f832944cba429018ab04ce44e6ead98e6a99aa8946f6d4dbab47fc03a5acbc7945777a51213df9d5d956cda3dd3021f0c9262a84681ad934fdb297df6c3b5f014982d406661b4fdc2ad2b087f83601fba92b185a04a46b717dec2bb972f096b384660fe044c149d68a90040690e6cc95e79af303362407813319df750d3d3fbd91f720314b1fe7c5123e9d1738d626dec4a36c412d5f092372be7c47ce56d35aae131137bbf12662bcb5e2d77481e5196be7d69fe339672961d41c311d0aaec7fe0dccef89c47f0f25bda018436ecb6bd9066e906536400cb74f4a3f9bb428bb8ff0d0a51511b6a9cfc327a319cba8fc6f0a09f13051f24cc55956ad35349ff86c1189ac13c86a8032f5ac8239afb555ad73eb6c848c8c3f890c98e7f6d1e8bfe805e7308237cf90c23f37946207ed281506109d68090b99ad1a58cc317080c62b22a8fe055cbe8f2d54ee8c2064dfccc28578d51eed781d3dd74921de2bce5395ed2cf32636b7f70d78c287d4c584fadc81c2ce1c6e9c151699b421331977abc547c0b2aebaa56ddd75321bd9fcb19604d3a29ceda264b3ec93d14eb7ded144418459c103604bca45c422b7341a36e5ee14ccabaef1f0320704a5769735742baaf2ce90d1b1945e4789f970c864db7046568adf398aa7ff2a6d0ae3a73b222bf13c7861e6640017d29ddf731f20b04a23523847a3fc6263d28ceefd92971baecb7f2654eb5b7c2ee22b62f61e0944deb8a3d29656ac0b2703f266e5a05a7f5aeb6b88fc100019c83ac1aed58688308706cfe04557368075bb937297d931b0c7abcfc4c921cf3b35ebd263b3257ddf4d37aeb7920b633983d95eed16e541ff65934e700c3d1db8c2c151469d6004353b4253b6435a97db256dcfdf7877d9890a229f6d54555ad0407fe90ecce862c0fc47138c403eaf65467374c6b45bd4e1012a23537f36f436bce40c8789d20a53dc928ec067aebae83c7006b0162bc99fc12a2be49c17a0eb52e4a94c3e93421d496452823d4319526a1a67a4d56b93a766f0c85b0db2e3787d86d1ce6a4e00e4e5a57b32c41c31a65c11a4a81e2302db2a8f7fd2799577525fad2ab15a181019d4a9dd896e37144f23c944dcd3a66c20a511310d0424bb6a98c2ca6371df250dcecdd75343202a154ba5bf264ce2fa09f080b69d7b7051dcfa2934db2d85a002e839d5a94a232a0f2df08a59ba9b61929147bf6ea1eaba90e468b702cd17b204b1583073355622681c167a584dd82254de9fbeb6d4e445142946a804d3add60b94be63471fbac84ca76779ec9fc83189b9f9c590274dedee12a768db41a01809255c4018d22a60ec70d5e0df01bd8357a6b9a6b2660df5d1da6101fa8d2d8f4e3dcf21bae00794bb08878abc96d6fb985faa704aa605fdc2687121427b48bffc946fb506efe432c342b6fd40fc0a94b7c59281d9b4ced291a16bf43170be28289318cd65574316b1a992555ece8913f2307fa4f044ea379a71be270e3c81e987f80a6f701b855dac24bf0b863f4c11a3162ae750498f9e2d73cb79e629f08bcbed2bf4ee92ec37fa893e0f5217adf3314485c7cb82b56192da4b418ece62bef6245c23d3b301c741023ef931e7144c758895c97983738b3a08b47949bb9140807121f6c82630a69beb9f74807ed7a6c4eaf2095eb2c918a9c9a56e16732e6c6097b263f34c501c48187c94a5bc8c2bbbc6f315f5098108e6593c8be4cd66d3288096e1fe4c83762563f632ee1402346cffb5d861f4d5f724508783f040e1f4b88a3aa6989a76e667f0bc70cf15bdc9b04de39a7d73354af6eaf541592b3995ea1e1396471a706540073b33a8998e26d4d0d51e53aa1c6edfc79fb7afc74b7a085eb99709b71bd0229bfaae15ae0ce9450d9a81fd483c3792ac0fbf24240f75db6a8701726eaea65f06dfece16a6fa31b26991b4d39528daa053b4f8f835b0f8a67d082879cb70cace48c67dd399f65d97bb3a497fa458b945b2316e79f3194e4922d469139647093684f69462ef548971d74efaa6e51f93c172f4e1d317a4f528d462041d537c887423737b655ef6cd1783545e3aa73bdfad5659aeeac5a34d92eb424668d3dab9a0067ce6c8a9fa43976e29187804e6182e0716ff5aa12e48d92f71ebb50a743a2ced1d70d53187c41b7ecc37932acf69c08d1076b32b3ec3fac6e45c6658877840008da485102ae7f7056f4368e4c42ccd1d68a00f03e5a532966f28c99d54fc81bb846fdbe12737fe0930f1d4b80fa8e4e3798b291c81eb97f03f77f0a14d0199b0e933eb2197b0085c3b38ea4250c82881208b79f3000ab9ea44cd64989c8a141ddd44744c87ff492aa438dd0fdfae4402ed1cc3872851e535e7c34076929742480f88fedc7dc3b73a889dcefb84728ae071800548275c4bd5b91a418b9165a8855b4b596313ffae820b4b48e6b9175cb4cdcfd9c405d30332d88302f88e9db299af2bf6b30ff1849443639f1c6bb456d31bdc8b463abc07aa9cdf500e739d871425c0aba74349da862c643e067a0ee5af1420756b301f6604a6856af9939cb89c95d88934a2c5f4fde3c5235a86a94a99759997cb4a284a59fbb147e95ba33969d5629124f9fca8380d7f9e1f558bfa5036d95b2510992d79e750db7be25ce2c5a2bd6f5163c5ec6aa17c4f50219e5a7ec032c65e3ad715238a037e66888dfb82944f1e275ecc3e53dccaf7992633454a574d0841bc931f7ef9f94d86b0953b7312270c8af8bc1c55170817d41ea3d640b491bd8d4a6e40e033d00db79751cb8ed88ae4e5a8ba11518bb5b25d47fc749b4d42628e07411fe6239506f9fc23660530488c59dfb0a0cf996540c9d04ba606dce774db9b5051472d0ba19ea9219f67aab8879f1db3eccebe5ac615067171f3d3460a13ac6dd0bc4035eb54e7fa4d762d9687cc16bdb4893a9bfe1ce95ab19ecd20e8b2baafb52659bd92907ddc4646e1c29ce58bbfa5907531d341c127ae88ac9202ec0f66977e9af5cb699047d20a3ac39d06996aa1ce61b43680cee52e4609592fb1622ec2eb50965ce6a2937b0e5bd66f3839c125e89586cf195b59a8f2ffc6dff78644498d9c91b148971cea19f2399a863f53b30a0c42e65fcf0a7920c415939329536bc868ddfceff13f7aa5091e39f58215a7f27c532accfe441b2cf19f7bd33300f1475864a79276fb9343c1669bb0f75a823410600a9c8cf31d2d95ff979e2b3b253fb75242f6881a2092ffdf13e1e7e55e52095c8597f3db91d0dadea52f207ac9aa04c3cd92bebde6af1bf91484f0d0b4a124c9b4c4943cfdd26281a7247fdc0b0305b900ee8bfa4644e503f223b9c6e700e560957d8bd76b6194c3e2c472ae9561e2c5e8c584b7d3aaff828c3c6ebb16819244668dd7de81c94e70cb5e644c33b0014eef8833555b80091e8408e760a1df165eea20d18c80d22a9209ef4c8b09602d9f408023ad57f144a8f5b005db31945c7e000083b2f56f25001f64313c45f881e10a71bd1c633ce894eef23c1e6526816f4a156d35ed50fc7dab8c1973d823f86be41b9993a7535790bf278f0416298a1dc60440d5b93fd6725c692a340280c1f9ded6066637dee2e4f85aadd43710155867903bc19ec0aa8cab3190b76c6d7072b535039f28a578bd38cb46e7776c9f32dbc6997f23edd6b3e6194032105ccd3ac87af82d128e314c438e13870232df81c0ffcf475f79651ce4e81a9d388094978e045005a2ebccff58927509ceb17bbc9f6420b7d8cae087fb8fd9630576b3fa2884b72e91db6f60bd68a639f6d7cd1093425db92a2b94f8495aea502e1c6296a0758a8f70c4b4eee51e73e938143add4b4261d0690db67ad0a8b5e036ba5e225610a6b8f54612dda7efc4d3faa51c6f1f5298ea02947d6b6b77f204471ad5c06da43d94bc7c0c20839c38c83a627f860b2e8d43ca982b9745df34d2ded65abeea4ad16f46ddae0f0a9906f9bb03b4c2a70ef36b7362775cbf72a557b261c982f6f0abec06ca3c3cb2de26834fe2afa3134fc1554ee0ad23701da71f95889887ae185b277067738015a2018deff8dfd5e4a6d9ef340b5af998c380dc1f969122b9fadd4a707a19212c7702176252021cbc20631c3ade18e25adcc18c5dce2318a7006626c3db3722d1f91c9dbee0219d7339045b67acf9915b248042d8808bc31fc7d41f72a293cc157b2ee75d8628b7a89bde3c28d1ac36717afb8d69ee969f49020326f3e4a70a1fd4e45bb07a50cb2a57d3473c289560a97917e2a54caf77660552ace0c24023e66c0732be3423c802661a4909b6496f165afd346dd064c5454630fcdf7ae422d22c30092a8f8a5bd3f1e79c56ce0a262784a5d258e5049f9ca49ea459a2b00d7eed2d69f03c11a8d09e2df4d9a29f4ac6b502a24fd6d49086054936e06e1b30ad800298d777bef02f2dfc74567a0fdc728bf3892e08808e1eac339fae9712401a38ae22cfb04b61ace687f922a7e33a7154bc52211f305d3d6229cfffef79c0e0e3f91ca147d5dd2b4ffa3e24e303d4c35c3f162dbdd86ccb8d477a94a664703aa4847f93407273cda27e789293fb83312a68e489ab9795e3eebed0a69db1d94c14a79f2d3cbfc90d489eab14d1e14a79a2a015d421ccfd374a4b0b9b92ebe819e24c242fcd344050c3b5551cc517d082be72fd840b8588899b0070cf07bf79b859b363341e34d4c419bdc94b6efc4ff5ed452873d3b9751e816de7fe92f0a4a4e3f9f04fde1b5508ba9dbceecdb3e5eda3a7d6c918342a2b81953716a0b9f53e8bec34aa4ae5aebcb47eaa2e300b686d9aaafe36150a171e51a79df988e37fd108bbf0fc6d4c08d6c04e77be1755473399ce1b34644e6ddd98c8759f896ada975f9c467aeb7658db35c03d6743a6a08356384637c9da5898f6d38318b26588e7fe4765636df38c7a1de2168eff4462a8f9a9a352817b091bca2c86b6ac507d05bdf8627ba93271a66b8c3bd901aae84e2440a0c18d4d19c08d3ac6c5bc899bd7cb94693f80269a7727d6a61f5d0425e194db40082708808f8a3cefcf5c58d076707322bbae9e553c89e147bd830dc465a45d44069877380dea69645ae8fc0f51a997134dd2b4534c8c92a5e998e9cd937b9f329b61e9ef0efd3561f960e5f9d3fbcd888bd6d5534ab7d919ae216ea00a70fe102ed4f1586b789bcc7518c8ce216546139b68c166b43050e9acfe47d4de2953eb9afced8cef31ab9c782e4d2f860ec474d90ef8eb80ef523df4fffa73e9f43dee16998b4e9c86767c908e6276c86e36dae56d9597162e482d183b0d7de4213f88599d19be71568fbf5bfde39f526b840542d9834828f8c78fc2218d1cecb8c9a41af6cfcf49333d4a1bbe751ae41eb32027e0a92b10539860651ca6c884b58aa80ee7fc7951ecdb6d2ea5de80dce4831f117c25cbf5b3d3a5f843921098406278fa3edbe96cd6be8e14e4b5fcba7eba2f0e62f8ef60c3d15728e0e78f5a50759ccc881595721476ce2a0f2faa622f8a999261a7ffb5685f47aa1b89134937e455cb69672c2b33428fdab083027b40772be148de0def08e4928fbe6d37d0a219e13a84870b1aac919288660b94d8ff3304cbe7b429289cc57120d56ab01739eb33915c4db90e53b3007561a1f39a1e4621cfb0a1318f83828a0ef12b74d09393e1943b69a4cf1bea231c5e4cdd34c3da7222343dd481ca26937747ad8534773ff61f6a32edd8927aa6c344a379aabdf91cf0fd4c22168e55e4fb779ae0a0b10bf2d87c9e4fee6727eefcad29805dba623e8f2a3e99280ff4bfd982c51a419d126f84c3d1909e107e72c97b0f6046fa539596e7edc662c1200431e79ef53b6ba8ab890f3994fe2ef6feefce11c0d3a534ce2c255205c7023f072ff0acee5f39e92e1016f1137d7cf0e37a12985c081e16b3acf1cd13b5ee28091347ca7e513715ef32b538a39ffc7f7f05ab196ad987302f817c45c173dfe0e3f718cf34f1d2ae6b4dc9fc823e1ac81eab2ec91e4cd18a12632962381a0c5b7f945d0c49af95bd4c001ce123c2a9901dba3267134c6dbf6aca598231cf3dd1431e125a16248c8aae6485af098c2a0e7058d7a2a0bfa83646956f46ba8ada80296f384f4c15e0df263008e6225a3d38a92fe5b698c8ee41083a85d1949fc5cfd889893d974fe9111ffee4fbd14e5045df88775c659d5841c973978583ac6697bd4f9330d3adb01712ebf85b9d577d5d9a808db944728764ff193d67b377bf860ff70715d5aeb3682856b1220b7b1eee88c50e18ec043054a6858871f496bdd639060bcb1118f672d4b746449c55fdc25bc6026459527fd45427c2792f1ca9ee7d9c2fb81dc9149500323c294e2b066736f7d69c97c2a2418c9772eeeafc8fce9289f8a240db3c75cb347f805a655312b47d02ec0e374bc17804900d6bb8c384008b074053e5eae81597f2ac11aed7e5d4e17b94a5ce618a4c54232c7b3352fd069be4edc11eff11704b9de3022cafb5e2c755330a90316ea73cf2ff0cdb51e40895ac82c66135edcbf5c18e8d5a4e56825a5fd573c707692c3c5678e855fda1aa0cc3c67ae15072732610d4ccba8917209779f2b94fcf8e0eb0c16aad4b534ca2e2d0cc66da713892882489c65a7fec9781b740a380ce349529db15f58cc03cdcef4264c2ae9e15fec12ab07ec9fbdc14018c3ffc917f5ebd9243bf0407b37615372cba1cab61f326fb38a7c4cedf731cb557cab3c536516449b9be0398a4f3e0a370f9bd54d760f634354373735eef37d66f4983202359071ae2819c7358fff37748dad66214bb18f8f30a741c07aa0d506c95c809617958297caa986cc916434b5513fe526894f3158e26e425b30d4a5e701a7481fb80e7972c66c0c391672c1544d0ab480e0c3e35b44de5240ba258593178714a901f536e208b5d93100ee095211faf64187239a4c54ca86f7ba1dc35bd35787fe751f9f113f9a494bf28eb69224a30bb64d24f76a07b881fdb6b15e64c641e6caf2dca9ff62ad1286364264c2fd91f325c0b971843e65e201b6f8ec9c2e11d19b21cb68f85013f4d8ce3b7fd5d4b7d4faf78a535d5900d9f4e5cd7a4cbf181a5a4624f2556b4b68b39ea86451199adedeae92088fffa5961305f16ce24a05cbd218469fb0cfa17bcb040f8b2cc936460a55774d045c1a24094e9c6b1288c027ed7383f87082ae599459a247e3d2394f3f38a4795908b06f86c99b1415b5875fb9ce85f516aa412d01025727d692288d8ad6c7d708e17a9f2f8057c2a4775b46673620fd4d441b9ea779d25887bf34b070d92d87d1504f4b8cd8e196c6d9f7b061042fe9b5889a4a4cf2a3ed8b72ee6072158e2a1c53ab946f7ea016b5d500a2bba8f3cec94fc269d9bae1df6d4d0566fa16491cae3c28387750f79c2c7224843b576f89bfdbf1b92446f7db1ef99528a59d2eba0c0d0ca54dcd11a0bbe54e1881b29dd824a4ccec630495495080d7d70582212ca2d07d6c3d1164bc9d1e08278d770a89eebc63f1374ff7253d1ba71b4c97c460266abe012d60c4452c5979ff750ad78a04d551b0523b4108213226e1726d8a87461898bff29302bdf15ac13358e7b6110f67b85328b94f524232b71a4a9a165e67ec100dbc0831850c89aeb5aed9ed7783879624b4779cfadf8c7629483488820668c5cf6cd6100872581997784f06a61fb3c26f8460d72b5303baadd3c638403c68bb35f0e6b9e7c345a547a5fa670e99e1d111ea8560aba2a56d40ae73bc70e2016f5586d35fcf2b09644b242104f4f016761c4c682611d31020ed47904e5d009f67019ba06417c040bf00347f4b665e895a9280f729329038ad213f8a7a15c6a039ea9b59e7c01858b0dfe2a1ae8101d3274945bcfe207a73aece7f43256e9b2b6ecf6ec11712548a196718752c748df6811b9de087cb3f3b68e3057436fc9011ffddeea135832387d153d16d05db4c8e610a33e74cf7e0c793405cddb75b29b34e9e10dd0ed51ee3f37a5e0a538eafd255da260640f9f4f59924018d48440020ff1ccfbd5a96318941f032950a3277519c0bf4e5f96e35d5064b54f176a6641ed8d134659d237d8b559d1e21c8572e3110f02d35eed391d005099b4ab714efbd178dbd08263ce517f9f2cc31614f7baa185c6d9609636923bb00efd637c52c76b75c6f3d1710df2f4387de90a7550a8ed4cf00eccf671c713f3c490e46a05ea23e410538073faa2048aff2ccbc425552170ce5973df59c694c68359cca18fcade6805d230d64549cfb9b8d6c38a43d0723d3e028ed375a6e286e3cc0bb19338ee1e0c3c04e72476ac41529a70d19298139bd273f4e8768c7c51864131009f60f2bf8a0322685808dadc49413ed0129f99ae9a1cd009cb76d92d36979201f27ec95abdf1427bbc218d22c1e08a89909b81ba40f7a693d57ccde08114c5cb03ee359658957b5e4f08542488c008e58ba594b2f0883f92f55e38b11282229221890064bdd3c2d73241965e555bda1fb8272fd988aaf17d3e48cef92528ba6410a0c9d4e2f6ca40606b209960cf6806b0a0d1c09323fdeb6ac8c24a3350ebfd2b8b1b4a99da9873b0ac5eae9cd4ba0d4df9dbc0714b63d38d2076638e6e17599e618780822acd94fb18ebcb10cd1f0d8f0193476326cabaa37f48b404f66124535d583cccb4ecdc02a2bceb3013bbc9a0722333692862fcc691dfd20128e008049213e32293353bc702144e7486ec2792007da11051a47be1219266961d3b0e1dfa4882f30fb745be7036584543d889efb24d998654d8e8b6ddb7a32eefd331555574981401ad4285986dcc16f46af11b223609aab77d5c22687df174be1d314209557948e434b5d07256768369c239d24dfb4bf5aa4c483a2681929ab2c4d79f57ffb1b62989ca05ba7385939ad6d19547c950be4276300b4d842c9b15de1353589c3b0109679614f3e16ed32b4018bd1b49ba2f1256f13d7a4207c45be7b0fcd72a02dc53bdd0eb4316495a16923ed9cbd6fbb6e90d6865981f95eb81aba2372e5e33b6695407792dfbb8f362eb17ab7664e3554bb710fa084c6f0d3fefcc66c759cff83a1f6e3dcea53eefe2a9c161702ce27b71222d14f2649d7fb1422a6dbb8ebeaf35f851228cb6a9345adefde1c7d607d0f410824c1849d661a72890697c8b9faa9d0b06fd9382f2e34303804210d215255520003d1ab8a1204613b6e84dc2373942bf5b709260ceabf8bbd56710f59316f641c3c5370f45ca59a4b2b201de24f74a49821fda237abec93bc3c5e6496f717c7a83618a790cd40af5db24da0e504507ae302fe0cdf5fac8638ba0a90fea8a26ecc5e5cfbaef2ebd573cb945e64ad42b99c487e1823dbf09a443e79af5f2a1c937116b6486bad831f96ba40301288dab40f875ba2983595289eb1db8d64a0aa98321cb82aebd7b2a88238b938bbd657dceac26dc9a08ec48496f20b21fe18e5f6a2b0ae241123aeaeed981e9ed289ee49bce8fa92bccfd952f2769ba3210c8f499cc0aa52044a1bfa3a3ccd983c85bb61553a9674396d840c2e30c1fc231dd1f01615b3e411d3e71191572bebcb813551bc6c0aa88974ca0ea60560c7bb093e1a2d58f505a31b6468ce57c0c0ccf13e611da6fe38ff7cf2bb1975cb4877c22c0af8bdc522c27c25d7ccf5d2e7798e61708b91638042d7c647e2fe5991c7c3cf3e09504de1b91ac581c9ac38e9dda2afb74c0113e9897650ec20d7b87718889916d7424d9b3920a3d4c67408e21253f4ca2f0f5fdb4a292d06fb12f2d9c6dcdcb99982171252e0e665442ef0382edc88a1c1fe0ae1cc490d03bae67c0178b40c43fde071260eb8aea1f8cd1e498432971b5cfad0f7d494fc86d40ac1f9b2799bb3886d8ef53d26434aebfb8d666b3b62a83d26f733971c1b20183278781ab092e1155bbd80eb82be4909343180ba251a8054f36f878dd7494dc0ad50ec04c682f6607434382bd59558912da700d04de733739727f27cc4e897176b9f6e9cda1d338f46c23f443d9becafe7dea86f777c87e02ae8ca6ad096e105bc636e6221ba6940d9b730a9ec33f33728b42784e5be98a1c281c4cc48dbeb1ea17bdc1030181a5fbf8908a00211aecd9bcb48b02dadbed5f5b409655510719e8511fe6f0564bf945f482aa9697f6529b0ef5a582d74add2d97ff843f2c0ee2271ddd8a06be6de3194d8d3709abaecd9cb1583048e1fe418de5dbbd6c7d90b45f23682f97970606f26b4a235b24d97b4b29539232d005db05d305b3fb2da00770c87c414a33d22bfa3d0d13d21eebae041cae5a7ca9b72d2e5b64ea8bd18485aaa4320be0e3c5bbf8ac37c3524a795bcc90597c31aa7ae48af36ea8c9f75331e612704867b4206839a4351a693025995221d3a7b58689b4207e502359ce294390bdf4853297e67d5a537d8c2a9c551fa3eae737ffa55713749f5e717123a17ca4055124875f7b1f55c35037f222d382c587a6d2d352a9572caffa6e4837d230913e6e01869c090bf76998293be6f4390e632aa594d6efb3d4ce3bb20f173ff2390e6393e97fb5622191482fe9bc2db8d07af1a98fdd72f174e6ad17204d5902ffbb37f3fdcb03b43c7db025c2a89e72d984634c9135dff4aacf638c0160c9f35f7cf35d7c31b224cb17b27c8b0c53454b8113d9e4e51bb123bc5aad565506197ec562d5964f8796bf21b38a24e79c263fef4b1d5ac0d8ab9657813864a63449679b4dc3b478fadb8db75c3cfdd3d7e9336b79175fb3be5e7dbd4386af71f024b913643c0a1e86e8e35bc86e85d8e33fde04ff6979daf22dfed3f2343eab5cf52d1ffa4f8b162fd353b67c32fb8ffff811519d177c93fbd19b71579e1aa13f0d338fb47caaaf756cd7d430170c5bfe3efd9235d121f64af538646e015b4c8143ff9169fff196f7a8c090cbd6e4c6172e3e24e5160c18fa445c5118208d225a41c98b2804b9009790e9870e74005a930130c2ac39c7d1f82cfdfebcdf620a1ce219a8c37d1c329bbc0e2d8f43661abca7a5e7e4c64955dfe26b9b5ea13eec58a62fbf1c6cd4e8493d4da940995e51ff6998fa74e62cfaa5960fb95c4a7994a73395a72adf02861cfd073fdc2bfa317ca1cc317cf4682d65092cff472f7a5a2b427da89186e9567cecd6af10f991cf817a45439eacd1e7f00569cd27e83ff443ff91c227c6c806906c20bdac350c7efab448c3f890bc6779198f79c517e2d47763a2bed317de155abbb1a97c3766ca179632cacf8d79f2e190d9042cf5ea3ee99ba32fc6e7354b3e95a16e93b1e80478812a3142ad1a59c20b476c8ec9179418c1e69cfcb0b1c8d5bd5f6050fb033034653a84c27ad51cc7bdca94c2a59e229136030c252185d5e846610d23039c1fc78165eec79d2002a927ba9a88ad5b7ac2891e4bc3a104ea09b9155297a50a46a5d20b9ae2e42607124928a199008925c22489124bc44909ac0f94885e47660f9fe1f0c2600d537fa653f9f31234fb47fe98e285e51dddf2822eac619c5e58a9df0b923f5e9094b3ded92d79a4594d1d943f324b6f822e7a6d332c31ac48a14e2b26951494d28909098f44deb5a18edba24b74e9e8125da24b74691f0e2a61c6ec957c79f11a721cbc4ae9fe34fadddf01329c15df4ea7956eb34423945b7a34b6bc7938c8dc5ebf9d3de79c53464ee4d1a0df14cc3d9a21a38b7c59c3860424e09c15378fc6175de47fd185ebee68c6e53c1aabd0a35192614dde3c1a188f1cb0e5f9768443c8a32167e6dbdbf7de7b65e444d3a3c145975094a190887e0529189279826107b28354764d58ae5b36d3a6be0d8f7c1f56fef16b7228bfdb87af45d81863ba50aeb365046c0c76762bba2210b480934f1677cbc68627ec97efc38b6b0492faf02e9ef93579b2048540ca09d828e363518ce17070a026e3d840cc35397a331b90619061c835525ac0c62f627be4901f74f2cd91f6438f2c9bf6838e48bb59924b5070dacda4ddd032ce9176232437096c6c18cfa1ddd072e4c8d6ef4ffbe3eeddb37f7ea13592fdfb9d86a4d15cbcfe63fa47eaee36d4d5eff6cabfda50c78121e6f2dcb0734f410e5f8e03319da8054cb0265312ec6ca3ce499ca494e3700fa1dbb10dcd76efaeb1721e614facf74b529ebf71ef7d14d178d786bacdbd3e37ab778d2727126c13217bf7c46e22b656feb27a227b0d711551abd5c9a4b232ab3415b09173c8fd610d91dccfd15aa357bda4068ec3d864fa9fb1a9f00087f226a7616e74348c7d9f1f723f1ba68777330308b099651f2008c106e452f915f0b501b9a22b757a95ed51bff2f1f1ca9f50354c6f025114f52a6ffa1a577fa98feb06515fb434e8035026d76b0372bdb37c53319d4e80bf27064f23e61ba6e8d24f8a2ef2b97f7fef7e513ecac67dfb17b42087b261b9fa8571e69c63e7220eedda4633aea69b12f112bd15faa42bf475df0c70cca81a36fdf2495966147d9afba1a72007d2589bd05f70966dbab7a08cb3b60f7df66b5c3250b47b1ba10769782868a37b0bd244676d4f53e3da5c263d308e2ef449d1a5caf6294c3a37ae3b896e1a6ab8e194230d0815d994238d070e7268caa1a9369158ceb6941d65b70c75e7dc8c3f65d74ab7c979d72119ada87f82dddddc102d675987f8b33bd239a5bbbfc89873a680acc3fc0efde98c54084a3b236183d71c91449e48c8adca513480007264d950e38388449cb5f45c43e554726d8ca1183baebbbbbb6b8c74c6c8354c7f777fec6e19638c2251478417df866a27c1d1a87259c618efeca028bab428db6d4624126d367459829e97fb634cac217b6396fe31267a72c6e7442391c8dadc2f3f46d51308fa052124164c5095681b9e2a821bc4294a1084828107403f3ffcd4905dadbb09ec39d278dc60c604ee1c693c64b51a3672908094b51ccac8fd02a4791491fb391a0f9d1cbefcfc60b977020e911c693c70721339d2709cc82a9f1263eaf7a3c498edfd4b3186fbfed923fb471a0f9064077174ee8029873cbc5eaf8cc334caa28c6f4ce806976fd00f759386e7396974a95b74e9982918e6f719945ae2068053a6c0f644cde92f33d8e8ee8e359fe9a6fe544b296577d08e9d3304a80748577de9aae0cd756d7f335d36f57de06c4cd706fa308291ab7ebb645aa6470ce8f79f33e6f7db519ca951c11805103aa64d4468c61da687a40de75143379c2d82c38983d303a76146a12ff4d92080f6e67bfa3b7c1fc625df87d2e67bbbdd0dc4168da9501595516806fccd9bd93ef4ad7af5a97c5f4876e80b5926b0bdf940f0e6c6f610c9d1ec62f8eacdfc5b3981c8b423cd9bc8a22fe98b09aa4f1564a7294c00adfcc8d8b1fc572a954aff8db69b15a9efdb4ea70fb59d9c4e3d8480c3899382f3e19c4e3a603737372b52dbe984da4e50beed7412c267714e9ffa152b52a793106e361d3737a793ca17430cf25956fe0413599e3e8dc96492e96749ad48a14e2b26951494d2c9b6fd643aea58ea6bd4d5fa5ae1e74c130cc184ea2b643d148a398618971a86071a69535f4a78a0a9afaa4334eb0bd3d4d7fcaa4383689239379fb587d86a9f49d32bfaed437f44795002cf1db0af183b6dc9c842d181f261dd8162b93b555f7b60f1b473640dd3cffd8083c894c58795471e7d587532ad3fea6bc2542fd3a31942198de66481325150ba1bfa6eeaabda3481edd70f270cc576942ea265aa5281f3659303079bb0099bb0d114894411c827b672187572c4fad0d3206fb1ecca06d5570e1088c08ad5d58eb5eac2edeb3c79f9f67a22195832b80eb58668ce393db0d624920609d986f8a9be0c4e78229a917cf1c0438b6fad7464431a869b3fa69019cb3427d36f316b8c649eb0099b3b260ed8b57c585f2bddd3b9a346bda1614ec0b9a3c514b867c2bc355fb0d10e8c0383591e9806b09b37380dd34fbfc6aa8e476b4ea6159669dd4103d873f23a541a1ebc355f933e4864e6acf97216200f5d104a4551f994a741d67e0d6af96a6061e9c0fa02bfbeaa4dbda93a5229534e7d8ca9a73b7af5a2dfd5d70884d5970facfe70e3b3be6eeab6e22bf58a7eea0b714889dc515ff515ebabbe6033871376430e2430c418a45f83b77a30c61873786b69d612ac123262d09658e23c96585284a682d98e21d4866083d05c0927a5e22611700843080d889804021623060a96d05080848682d79cb41320f166b24ce7ad31d4bae5bcb5c5d40286abe7529c4e4866129e62ce8ad25b305abc098b87f1c9c89233191a62de9269a14135323428490ba9f2b445f52e7e82219660c8f5a54a9eb6bcfc66cbfc796fafeecbf8aba494561621d22344189367f1219542f5214d42e943ca0418950fe9133295c275f1d9f0ccf7e15d2e5a3ef5f75b2609f78a254eeee29229f52a5a59a84a2a959bbd8adbfd0b76c350696949b57c4a050ce7df2b5f871690d27a25271229a9090b554945b5aa1b2e9952ffe2db66dc88bbcc806adedbf2342bd85902a7ee0811e447909d9dbb13e4d2e00a57b83ad7c79dc195c1cdb931b844dc216eecc260e70a715f707b5c17dc165c1e17e7b2c0e50b16640c5321872c5a12e6381906fb8104c95dc1dd71557053708390c94c2129a56c5a5d958b457a0aa7b4561914ee930869babbfb9cb14969e7689ee9ddeb8322091e28a6bbbbcf299dd25a3d87f34cbf318923143e48d9eeee3ee76cea312826134e6b954d97d305f3652ee8361e1e9e1777fa9f09cb1c944093e33a47f36c7573010f1d82ecfab2d5edeb5679622093c95af2c48d0a9e0b84bbcb2e0adca1e00e857b82abe39ae096e0fe706f6e8eebc325c1b571779e3b822b82dbc37d85010c5f58927a994abce5a71c7f7e9cb330600a79915eb5fbe49612f45a74e957b2242bc9ed437c66440e57e2fbf6c98eee92e5707b754a6aa11c1dcf54314237938d33e5ce67369bcda8d3ae36a340ea64b9df46d9dd15d72a6456190daa319aa45b9d4d9579acca3c56651eab328f5599c7baea744e8fc9281552b6fb8c4f5932166b1899b3e3536b2e845a962e13e2435ce6f54569ca77e43015655d11dad56807eb66dd8e6edd9cae56675dadceba5a9d75b53aabe5beb1ae56f2eaac83dd5ab501a631ce39e774138eb53105aa3814493f5d92c46649aed526f6701c1a14bb9e0db01c7acc946950affa7d625b905d1e1cae72df4d27c6ccef6ae3b295cb562b97ad5632af2f97552973221d2c956dce91c3aec25e397418ece7a405b00ef6816ea672484a652ac6c455b77e46c7e4f97097d15b055dbc3c78a674e430255bddbb4ea535ab81c05727c62ce956b7a359fd468c1c39820449923aabb30a76b02aa40ea932d7f9d1ad3a2bcd3010dccd28175c567d86c618634ccd0903731d487c471224b99774921cbef492f822417fb5a03e07e292bbcb8660a32a8cb3d49c31c6186b2afb636ed571ab2f06cbb501966f72960db0fcd0347311b48cb9d4ac54b6d293207ec92d5baa1d202988417501cb151376960db0e50296afb2a5a4505452a50a2c3ffc925582e5db2d60f9560b587eb459c0f26d122cdf6201cbb7556069af40ba36c8e4e45a2b946673a9dc01b15548b9960a8b04cbb75458b9760aa76ba580ba1628756d14f08a1b634c2ac7b7a915a8d38a492525258ad1e559688e8daa983922dc903b84b216836a45fcfbfdfb91af4e42081fe4464110db0e54a81c6942fcc85e8e342160a0058f14b4f7a391a961666be410aacda0912f67f9cb9b4ebe603833baad05b51c693c7c4239d2780821b78fb71ac759fed884dfb4f2091bb6eac9de8b644cea38cb65ecf6ca8232cd4d0a5c60fb9faeb3d60978071eb480630c7df54dc332aa081c8ad125f432bdb9dc9b09993edf0f9bb010e27b3fef4b5581ef47f39786c67bd1a7928432cba605471fcde85f9ce52ffa68442fe34b6501875ee6f065d4bde88baed0573b707a6c4945757e5ef4fa61a126643d64b26c471555839452e10f355f34e7046de0178134a8f92bfc34a237791ca2eb694c5fe37a19ceaa5fe39ae00d1ca20b3babfe08a4e164842619d4155d261f72134529780387e8aa01d2d450e36a01061c5d22106503bfe83188aa711f85ffe4a54b04f268c0856a100501d193bedf0369781ab42172897ef435ae4efdd44abb02acfac8e1ad22895c452c873227d7ca6df56ba4558193ebfb8faa851cdeeeeee8d9784ffa482b2247aeef4ed0ef457a5c437a943df95e5e0d0f44d9b8effdadefdd97aefb32dddf0224488448c6d1a40f65033fe931e8b1074897f7d2e57db7026c1f25f1a3fc034a4f024140fad2a38020e0b141fa12ca83a0f4184475745da057f551be1d9c855fa15aab247d25f9ea551dbdc8e453be495f7a1bfc2828f9295fe332f94c4c5ebe490a488302ca217ff2d5b82cfe489f0e4aca48a4c72f256803bf0948834179e3acfa3424b0be4c7ae009e2e8329f145de68f409073d67cb9ada9ededc0def8af5f7e1ee0f206b63080e195fb81624c2bd37f6f85be7b8ec3184c1101beeffdf620ca419483343cfe34a1f71781343938ab5ffed7d9da0f155f848a2f8af6e35b1148df822b67d1a789ce0a81b6c63b8bca0de3e5f8dbb6999884de4fead68572076403523fcf1d900d48e70ec80644e60d48059f7b8ee4d9c0fd06863263903b01c1d1ac067748893cd24909252574730ced90226528248d50b991cb33c6442f5bd3cae42e7e959058b1020994949108af5cefc6cc3c853c6483c99978337e22b37745de0c8abad49598563bb0b078338e83875eb5c908131bbade0c94941248eadad82848ca76efc68c8feeebdfec5c3733f74920079f0a488f029eb9ef069e1adbd799fb669021830699598c9717ef0df48ecbb62bcddcbc3cd2c69b99a02814726a39dfe1f5e2ba1cbdd57538b8518642de4c5595629c74935ecad2e7b4d7bb31e3bf99bc668a4b9da574fb62a6d2a6659c5344c17bbd99fadeea27053d37b5f16628284d3a18b58c3e4513e4bc1b1b66462f77c3789d0866842d0857a83f66eede5bb79214a0b61c001981e79122e0508eb421a6c843d44aa0225343d72386004275028f72601a820627280101df17b0608820362130ce9136440fb50b58650a4dc05e122a143126a2c037475aec881e6c73a4c58200c566d0b4980b4cf03126a442027c2660931c69b11fa811f08916b6805792b01981b92328814b48d41cec095102f672a4c1c047119c92230d0641e4d084038c92234d88259942015b61e6f688ed02167810e1072b48928319e88852bca00829b8a04415456081929accf6c41021b0260c310218126a7e50515392049d1a1489814b882aacf02d0821851d31915d092a2cd7302b2695146e43299d9890f048e45d1beab8add2e92de38ae924729647c49f7c72f35bc9a6ac92b98abf948c92398e2382e5f79c73ce39e79c73ce9972e79c7382abceb3c19b5193e96f5bfd21966b98d376caf15bc9a6ac5253324ade3e1a59d3e340ee0570b996f249b6ddf5cf24dbd0f58f94adbdfee16ceff56f94ad4894ade8fae7653bbafedd6cf1f5cf664bbafe85b23db95b97edc9f58fcbb674fddbb245b12a56e59aae7f9eed8a4dada04e22afd3bb5d094284e4b0d3af11b84fd9e94f4a7dce193f4a9f73ce99e75b204f1dd8a80a718cf11463af4c934a8cb5d294ed03f5dea05694ee08e9331fd21d01cb9fa1bbbbcfee08bae91c61b2713644baf64e1acba1bf17fdded94921f2a2bbfbd411559c1ca5c0f2a9e738eedd11f5ddc668d2c51863f41ec41823ad2a1fa857484d319df4883d55b9273d60734ed3ed8eb841b0fcf9dd118e4f2b8d3a49f794bba3622ad5a2c3b7bacd6de66035bfd356a50948e5b6c944946d5a392bc60c271c9cb25f6eb582d1add446858987099487caba072948a18886618981660e95f188c84c65ae15e3e505452fd7302c7426a193ca2ec7733a9d2e366d50b7cf497d525a63b5b9aedace13792292c9490925c5b47242a552a81531b05cce4f5debe9f9699ff6c9fd96a5673da487fc7fa616d2b29d96812d293f5aa77554284868e7c4acaac4a2cb61c1824517eb7a3a27ba9e8bd382eb285017ebbec66918cc652979d4f30496ff7ddecc2d02b6b47833b7884a06932ea624c85941826208ba319ba45b1ca594520ab4a26f529e8f7eb5ac484880805229140b163ef2f34381502716cbc58b17305c4869c4c7e7b47a418144b78b359206924ef4aabf47f650a7441aa8811aa8811aa8e990069245563c591bc55ebcb83517176fe6167177777a7d6e0d00a3188c5b73ae0867e4080e6d46814cdc100b8302b950a0ee933a32c60de180620c916e8d729ad56fc4c8912348909828388ac9212a9e0f29932e00b814e86585dc5decd66c100e0002705f687773b303267bf40aa7573bf645dac89715c01d00cdc0058070ec40d32b1e064000e782281005b25123d62890f27ce48092d18552d400eee5c13128109db786a4cb895d8c0699516d94d32d9b24766bb15b8bdd5aecd662b716cb8db241315423e9a006bab55bbb45bad5e574b11bba580d36c68de1cd4c5a003a2b4784a3714e50200a44815c07e68674399d4eafa89fbc199fe1e4c928c02d8037530b40614231f4bda9fb28d0479350257489378325539006bdac783e562b986ec92861dc92320c703919b1cb6996016224d8c528504cb66e915bbbb51c0d401f2b60274810d3a4d2458344d7137d84205104318214450f7670a2e8c14e1494d2b943297863a59d287a98b13b77a20042101c974174e074dbcece4eca7dbe766690d892ed0b3b380198a3a894acd816b96488660000000823160000201008070443b14012a6a12ee70014800e6c963c685e32124843498ea22006c22008628c01801802903146196768e6280000aafe0b16ca9f0fe7ff24adacc234b6cfbd6c9edd433e03e56ebe0f126bc79cc1865cf081bebb175de0bb4fd18f8aeac66fc0bfcbfb23ea48c52ca888019b2c995bdd2963ef6b3d0e0518b3372fd08ce1d39bec19dd924ca2033e2617e83e364dbe88f2860ba87f7d721851c7f76cc9a0958e8550d9219c98a4e1bf156a7759d01321138866966c6637dc9283c7c281fff4693c4727dabff28336b68d6ed1e1ac3274c3eb8ee791bb294b6263d8aff139c03cd260d2db90a6df7caa0a6002729ce14e6544e4ef27a6191a24626caa9af72b1d00c7ba90dfb2aaf98ba80cd8b1aecd0cd5271d2004cdc58bf7dc283ab69ff3ba7c821df6c56074ca2a4a3e4056b11f25320d774400fa53638d967486f0e332be53fd851120e34d543d0f3eb7e5e8f730f40648e3afcdc5e9b001a2d459b16e29181b8fb9dd064dd40c9381ecfc9cb11bbef0bbe65585d95ac28ef4a5b7add0042a70fd1c5f6da253356f6f67c47d47af9749a49dcf5f29601f9073e8bbfb3f1b6b9fe05ae593bd67fc44cecb66c8e5a179228971bc7bbaf0c63d29cca6d4887603f39f0074a799ca66439ff62bba3dd1df2337a3d727a9520fc0e60bed7748f83edbde3f8bdd4b63dc17ba2e1b36a7173ecea3c043e4d44db0fd34db1680fb19e8e9f9094e0c8a59f7ff210b32e243f9da34764f3a8803a5b68bc7aa7de426d4a24e95811ebd842361a9e3f58783c467db9211c40d7d9e29db13407477a28209d6136dcea46020418e4310fca9b1094b7a1b812dc5d35cb9327e83bd07042f4301403c820d3fc0a8ac14a849d87f14ac4079b8096722bf2d88e87a9b441a8dc009130f8a5de8094a746ffa92a5e8d585bec80da835f631d263adc0dc0085752eb090abfd4435a35eabe8d2ba422612616168a1d328cee15794445e35aa2827162c65ebf75dc07305a9241af3c37bf421219bdab763c6465bddade390eb734a432d68017d88133bc3486cd11ce7f08b679f7731a91b6579b4c0a2c800063734ed18a596892c1b0a0af29b7e95a684d07d9007addc0e349aadc6fffabd49f43207216de01fa587afe38f06850651db99bdd305fc992439319c7a4d8be7c834019421527347fde41d90aa33290a731c77896174cdd4b8dc348d35a90679a5a065d238e524dd2a25c82ec208c084ed3de2d5631daf850ccf43bddd670f7c0c824e748d7159a711b21af5950d83dd349fb8c0147a894712ab77e37fa6274a87cc48754210fb914da6a3f82109bbabb5596cf29999c86d9381fe442d096e91d3fc8d416141a690a5b7b08103248556fa2b8c02039ebb011166ba38cd09eadf98319156f743b72dd198e68031f4064444f7ace13cb7522951b7a765bf34fcbd092abecab831678fbb98dda55dc9aa05fea6a6608f7200b9d10cb24e0ce28b46cbf04c72870f9bc352642dae91a69b2b244db70fe9b8dc44c5c094337029602526ab2cdc34c97f2a0b5a3493c0e4ff6d3ea498bc2f9240846f0bbd2b0cfca6c294df899ca03b514f66c2df98c7bcc8ac0010569139f4f70bed972c8e9e89fae3aa66becede2d24b3a927164686511b0017443ca03c0512b8a8f280dc0c1e43bee78e8aeced82f4d301522ff782a3b56a2f385a23f68a4bd665af38545d768a43d47557e24aa893793d17fcf2f36ec9e9274f9c35267431cfebdd035f5ae24e24f59f96022d1da2b047b751b1d21627a870733bde82a884fe3132ea20dbf372605d692814c79812a0be0f730b2d52585ea6a93ea01f7942d4d12cadf0b0b1ff2aec8877830e101842196998e6468c6949caaabe48abbcaa5376a1a2abba84a50f745da41e6df79a1439c87f172df5be059d4997af159b8fb5cf238be23451608aa91139dc4e5563d9f4525abc39203627fae312bf7028eb5a36a55c30f4a7aeb2b6d3c83c81cdbf3ca4479b6f64315b1966cafd0c99561da39269d4cc9fa0f481a2eb75ab8d4a77425c5fead7173abd98099e36b58ea75291d3d37fb3284653035a68d89b1d7a64df6f2e4943ad83b9c34a70273b49187fe8cce104013ee76cf6e1a1f19ebc7537f4183836a41c4f5c5cc47ac61216a6e047a0fdc43cc2fd634ff5b46fdc239bcbafe012c276237e5165f3037532c2be332d7588293f922e690ef8ff9facb9b945876457c68b01d6884b183a4d2a3dde55d1c44d199563da205dd393aedaa69298c90f9c7997b68bd8655c964d942ee1f916dc3de382e71ccaae71b2da81bb614bb95a049b1263f9b742fe862db6773787144553a806300f2d76d65b9b39cdb09ecd804f3bc029ca2769557e5f76770593436418e8619eab60e2feff131ddecf092139ddaa783dae57463dd1496038e0f55d0bc0dc562465efba5ff7a98969eb4c29dcfdc31e439aa40e7a95ebe4602d6354f3758cd5e39e4c135290e6c8ea2808225f98b393b5f71f49576b8608b8a5e8b4b0e9cbbc8a889796e4542af4ba70232398fb925e18336cbe0c61efe511478cfd933ab2c23f63431a1c2814a8fd8c3d58ee141e69bef1e4e05cb3dd5728ed456458767ef11f4780a341287eccc8c1aba473459f06deb55a3c493f516bbabc6dc286ae1ee0bf9763f4bedf4e80b2b7b682c40d6bacc7a04783b916ba83274e95a759ad581b144199c0316bc2a1a7e0760a4e3b8f90abc07990385744d24a6410872f5aef7571999a5296631988166eda865eb37a1bad5f14cda648dee3222c5d555b579914537a938f5929218a1931cd555f497a72a4d1bdb279768292f15d9c02e525f6a2a92ba8eefd171c14263615e9527bed54af751b20a356a6db5bde96a0bf2e4294e33a13cd5b3f22fac5a215cccf1746b20f4f94e69eb86c0d716e6eafea55c8365ab0b223df2046f8f48d1de6e33bb7c76a65b95fa02269e6cf8780ddc5f05a41ebf078d13d7dd8eaf58f56ee370fc30a3b3eae059aacea5a6757bed88668401d60abcd4e4d6796d9081860730c29f25cabafb7abe176391cbc899661dd49ea9b23a40aa2422fc1d54d78f8fdbb6c2b2158189c87a2162b14058e6c36877aa6c26bac76577300bc29681e845fd54e2635730e31f20cfaa31fa755482308d9cf0853212726070b1326fb01c916852b0a4bb18dab2027b1f4817dd4458bf7ffb09847e0fa48e4d2ecb7a4ce745abaa79f96898e364ab26cd614dfea1fce5c882851117266e4cbf4838fb74e45921911c4ff71781cbf5ff6f6f836cdabb9e5adc8840e540f1069f35eac69599eadeecceb451c7a11c83d80577ea9face3ae6a6ceab350012817a8bc6be7f6d348ac6419b7b24d80b80f05ec1c88c62a7b6058dbdd8719c4c60f350dde97464754d6fc87eb011a1ccd811b35d1b0d57c0be743a52f0f8901b53d907eeac1c2e5be4d8ac1dbc19ad097f4ba9c1d0e3020681d39a7a6d1c6dea7028aa49dcdaf56b212294804e2b66cec2b8aecdb0ad9d03934a6beee3b06350ea5375a210ff65d8e342654196f7574e11f22d4072821d8fefc4a2c8bc08d917a05df36eb30ce045706923df9705eba84c9d2645a6fde58b73f6b5e0f1ab05556f828ae80a5b09bd0ba89e86e5600d60b8d51b1000a47020d4dac55677f0616d978463bf00940a0a8cfeb17f29a2668f45ecb599b9c8ef420810163250366583da6810bbda49b4a61ee4cd145e3af5271984db5b592c6380e4513d3fa3fa01028f8b2a638112e8b3431c9c1db6059e812a79a8302f567239b4b4ee6629b652bd6f2332c32bdd326c5a9bee41f098b03a313edbc44809012dc2017ddbcf1ebae50dfd60fb5c24b0b0b785689209849427928dc3b48a6d5c3e617b0183ad6c7951576aadef88accaba60230c7273699c3623cb0d3216313dd71214d8c86ce3aa446f71712df8bf117c1cc34255eee5dbf05efd77f597203ab54bf62a07da5ca51c29a79a2564132134b137887f9a5e1f9e45c32ad87183d43a363bd4acef460614e51b79a4e6c483680bd00d99d5508578aa344550c82399de944e12a3c418a05a797c1cddb3e851e992fd8a38493a7c223ad9f2d92e8da8f2a55cd5f5bc2a92b951eff927ca66c76d9f322e5996cbeeac5fffa6ffad3efed466db60b7c059a9a0c8e71c67610217ae924ab5321676911df751a659bbc1b9c9163c47fc03ba4018c7582099f37582d6c7c4a28f87adba4d1ef23a1e0ba3e2c0ba981df0de69af2d1c101ac38904fd4a3e0d2cb7e1de5b4bf6dd87176c4e0ccb52df31012416dc193ad9d2f06191705363a1fa86532dcdef89b1ed70ebe12a1b1d96500199d58f3defed4302c9b24c36bc6b57288ee2fbf287e45b9b67de9918dd09cb10338cebf35d3f33b046e10c4998ea6067e194487e15b927bf1f7090b3e98ee94d2797d02c00edf29f42499a63308a24fc610835d7e37cb1733b3a6517fecaefdcb027d82f98d178b1935ef6c7b02ee59a5df03cbb69f58d829295b1f7b2448ffb7f1d9efff0f6e3b7ebb593ff7af7f93f4aeba1694a1e020c99f8d272974099e74b61f10580de1f3641881849e07beeff37d4a684fbc3b2ef6299f5a4370771aff28f001c587eee914247d886b77bd67ddc877be4115bcd1b294c38c83c2fbbe606790ad7a1130675f0eebf620b3847463771feabe07c020926adb7e8b3cfddd65399601f9c3c84c8342aaf277324fb3dc57aeeaeb146ffc4c8591641d465a2319b0b49973163d1cd3094e86290e29d9fa4b22e7838c930a4c4ca7872e894282949a41bfec77bb3b4e4819fb7d6e60c0c31b3c1ef0dd692459ca3f1f8a3c6ca3191b40392f164de5182d7ca163e9ba0de4cdfa9cfb230de610e757e54f5a98a8b17c2092e831e788305df287b5a70137d81749fd34f7802163c6882d4a07910e4c39204ab9232e6c3ff2215d788de2976dd99225a3ac9eb5c89689595af7bebdc56f55b421e55a256f4c6b6a75535b6b23b4700c23fd10e54728c69f407c463c60fd2721709dbd078c508c41bf3e8e35a2c6521a31722a4095e3e600c3b2e9b6f1f5d5a4a67f13d7fe37455112e3a383c3a90a603e9cb86f4811e7c39a8e330d1f0d73936336ca07d8919cccd00cd454108617ff5fde086cf5be005b5ad8e748ac07555e71065424b77dfb6b9046ea7f47fe07dd74071a833a6ed0550c0e02135641a206af835b05c751a4ca67c5b658cde12d69a589dce14557d7de5564b7eabb8aeb320f43b406dde5131d1c3f6397448a0c127dfec4646d955dd8ebb5143a7e3923a8b496715fbacfaf12b6d455838f6cd2a61e9c2ea29617e5d929e80785cb0986282cdb266796a997b2b52c8a67519d686e2c09f1f85ae41b8cc0e40198aac2e8be0eae414fbc1b78c640f896ea41643c0eab68a758519708153d7c313a00305d7076a5bd3447715b62e01f916293fc11ac5373fb2a28ba4dd880203ee1149beb476761d76ca52dc95adc42c791e053139530b13f5cab962cbef26bbd2108a491b8336e6453ac44c048b46d57acd6bc6ee1e394a011dfed8a8c3ba2c1ad694fe38e91396981da7db60bb4ac1b5bae0337fca32f7166f4866fd29adc90dc55a2fa181eb31e3d4e707cc568b7887bd2458d2959f2fa86ff4f73e5195ae1596334c0ddd00f65ac1f674f1e1a9ee27e3b6c1d13eefa2d440b57e67b802c0c2fe51bedd0636fc47533df7de81f5b75fb1aab0ba547241cc18d724c2f8df173de32f79b1e69fe587ff6da166aef253729cd45569516138a7c68c1697ec75ccaa103ecabfa746ccd59d9279124d7cd311334931b6d5eb211f83aa6faf0c66941d3d7fd17a375c1cb8fc179567be6ec660877e3c004ee57082068466f8c29d8e5f908752892c8d07e2a8d776d8ad41503a0f43484a2544412f310cd737c33de5089301e1c88c6b33e41a5b9103e520ec2a369010541ae41838fe36f418f48e8b0b5e98e70c09d03f3d022862cfb252ab2ee52e5345a3dc6b843cb7893f932592a9c2dfcac45fb05a450dc0ea627c9ac8cd8233971165c56cc032a82373575e62ca191580a98526dc3d51998d02366147dbdb843c6b988f865018abfd9542c07120140932963881884249844888bd054670a6c50f86f408894827450795835f7917eb4708de98e50809e4b7684380252888159c3de6ee7caab8d0bd0e66c80fd7feecbb7068dd4d9966fd48eb23003c862425d47a477c781ddf4d8f45c02dfa1a6d46c611f7452ff857ed206a6464ddb26280abc3d473abeb82f48d2830e032e2644b6367c544961a38c7d55178ab56f407e325b2af52e65265b1d57aa667cd01e2ba45194a1d286f5d5abe90ce3d66318c6f788b27db14541ffae72f35f4f96e4a4866a4d16015721c6590d717d8d87f7e33b92f4163e0c30f9304b314d791e81a2ace5bdf77e2dea9a706be81c354550446f3143daf5acd208c7d6da78e45375b66eb1fdb481e2ac4b8d54b13c21974914671165bf43e6cabc84abcdd0be56d937853d84014e2dc39f8cba6b97d9ebb5a964124b4da455a2a713a71389bbbc036446919e0655781ccd7271cb4fe2ab2648b3eaf583518d97be7758bf73a3221096fcffd83609ab22f6cb6482a13823aae33a80d62bde1ebabffac36dbb48aa849df933989915abe1c057a038cc6c088d18c2ae07487ea7005d1d7fedc4a6fe2e425ae4659de378f73606d32fa882e72492b903c75bb94486e7d13e6c8e164441b6a3a33d49db8b5de4fdaecf1534245c61c2d90c148f55202a9a37a722cf327acffd4f9306859f86aae97f5d3e01ff14e4846e580e4a55d033f36c97f33cdcf192e2e5aeb76ac5688acf389742b6215681826020ae6eff03ea609d04898785cb92f8a33cc416bdd9c685a3967bfecb9d8b48c110a92c968be785346ab768fd3d88d057f23cf55c39258629de974d578529ca70769e5a16b6e14786e6efcfc2ee398b4bf57da95176f79ecc2940c8cbdffe040f890df26f82e318c58801c009bdd33a1997091d9f22424bab12484791bc9a58ee7d61e6901b7e69f920f3b6dd86f0f7bfe75085cccd25c42c0f957909e9b1b5bed5eb0df59302277f313c7df8f68e191f67086cbc995d61942485b9090a337603e7fd21a965afffd5e2aae43de91735b63d68ffc5a0a2d2fd3d9a6d0ece5fbca44d006a3b5ae414273dde0b6377a50360c6e7dab432bf2b20e5ec64a19036adf900ae1cc7f8c4840084e40c20249f6b22739e23039c7ccf14f04f6c0b5927d7b8ee5b6c972a6b26df4683343e5cf42f01adba382a7eb16138bf0aeb91b3839a90d44d49a0d254d690dceabe1f59aad34493db8ea9dc0740634862db6fcabd0e0e3d6e70611d11fb455d05a8e909643c0faa15e097ba0da3869e6a3c15ec7fa6f047987d921cc22ca31d75b5eaae999703fdae23b9447215bc02c3733812c51aacfdc34447035114c5c8846d183a81738ec424331d9b352ef6b042609afaddbe115587fbe93c6f12614bf504f6b325ae1938b622cb9f134dc6313751e6ce833d88421038704ca704cfe82a679fe437946185944689d29af0e6b2fec292de6538bfba0fb1a2fdb7b3c3101bf0053930da46ec26b2e4c5d4cdb8d1b17d52bf81017b7fb1452498052da4d4871667d570a776e16e9f410091025977745559651c65c5f5defebcca3d21d29c9ad549facc36cbd9038d4e1a0dd83a2d456bbb07c917cce68c8d4c667e6fef29c07cef4cadfb5d0ecb96c1fb237dc41517303bea93c59c55f18a649770a6b2ca8949ba9cb383bfe74d60369f1322c1f2107a21ce51d0486f658cdf24e7efeb424db616fb5580599960e8c95993a705ef3713465bded3324a493058a7b8898557a9010d4ef4d833032c7e57182e96c7af5b22f7c38ef24bcef26b9bdf86d1e45967936fb30c2003affb514c726721eee379138ff6be264e591d129e7f609e733d867d20fbc2118155e87f3d482d75b38f2453dc8797e64900cb0748394050f1440452a114da40082fc5a18ba0a6193ce1cae6ccf0baae27129ea6dd79288ef9bda9df332df7eed26361a8c992f810e6ac2447dd1464a45f041675c827285fcb40127ecb4d3ca1a70818c33d36f66fc1947eebb64dc71d4eb3206146266af88d6a267567a520fff31215c1ebc3e7f9b6951ad17663bb2df9d7716874b81af6f10f2e2fbc514f7d8babef573908953f3f51269cdc8975aa374081332d1c6c61c0dc7c5d5d49025708aea266696a38b7577a7cf23aaadae6382f3d837dc39f246c4ce95b68ef69f6262382e4ea996f7d2eac681324ba63d7fb9b3b11edbb3997d97462b6d77c90e9817483021dbbaf778a3e572596ee4bbb20f7339191deca0e75d268d7f9d586fb145f0abceea45e97b428c46c49d9160562aad18aaaac47c97991750a82aaea2a77e231a012977c0e2a23c05e5b03b383dc1226c5f4a2beb86736ae6795228f6b9787e7227888a73538a1bd8d9f7b5482df5360882ce548ba50d0638ab300b37d067ad4150b1fd221e44642e4883912b88a3d687790193d6548c4af48bf878fde6dd88fc630c85509a60c8cf80f8cd67c6a8d7689ed209d7134d5c656e7e8a9d5d582fba5322d2c9f17933546369a9d7f5cf5a8df095ce168f921d23d96a70a57a49dfc1425d166d86455917eb1fca63235513ed78222e0d8960d201daf0a4b4febb04fcfc9814d56203e972e7cabb3364edc28ea01dde680229a978c99fca3f979c408ae6ce6cbd8dc09ead1b0a64be41cdf6c2c66a313d10089376fa04a58ad81323d06b700b4a144da354f0cacc140eabfb863650e6c1dc8de7ae5ba0408b209a7881a749658f27c19ef82c1d5aad451a15f46483c834c257d76e70228b72cb7ded6340aa58e09444438930eec62525f46642793027305c3367707d1aba085200d7ab9af984b2883aa67f987a414400469f198c11c488b12ed32a0a47e501d0c3a848a602638e2a2a2148ba3eed498960b35473987aac207b3f972d9115e5e51cb773bb109609ff941f15063f1f4af7d4a11502e01bec960e567b246780b203548861ec109133cb8cef9262bad88b1207b2a1a1916bdd03dc8b9d6b9ddc17e356fd653ec3bcf655d46b7ae09d7288e63600f288ba458da86202f02b53ed0c402403c74308291368f60e197c303271685b4e92e4127b5d0785f5b7bef89bbd0f6af224e856db85f50125dd7d1720c8d3e7bb3736f6dfd005f515602c63f69e8008680e66a28876e7a8036223200846a8772084ee8e46455af891878350caac7a8bf4e77a0da31c8d1e6abdc760a4a6e7cf82a998ab329d138091ca57232143d0230133d091fe24ab327d3592ee3947edd6952d9634c6d00e5197f869e0128dee19c962de9d01aba17f86e161e47f803a22c0ffd82da6b538afc531ec8446a1558512206bff3e4e8a70123efbdb33a72b6d94090ff6ee21abc0aee75f111d7f95d61a3296082ba24a6dc9418529ca094705389475133c2e3429cf2bd02cbd15ee889a798b202de4308302a3fd434af75781efd9cbbec0fb852a3586654f7bf95520584fe1f7e2ce2e123eca643407cc452ec491cbd5026d09742b4c82f59ca01f3d2986174817acb87b95eadd4a9127920c465f913691bd9d91b7da84d2fc2c1f84ad1dc91a237ea8cab37ae77696de535c5d2fccee2b75dc4870e5a9c4bac52695c630be3f238f01ba9438e4a37028345435cbac7424c29d3911f3f0d491ba21bf07720a29e6cd4a267953f286e253578ed07cc60845f35d3d37f29446dc83a87d82a5100ca46ec90d02baafe30f299312ec1d006f94f7e4744b84faed20da75fb60de429ac1b463c89f293a7856e983b3719053134f81ad085c33f07a51d4fcdeab2a0a743c708da99005a5367cf3f6276e90eaddec323091dc5a6e979769e8be2cf8effe2e13cfc2220281fbb343b3f429d395511cf3b77b6c0ef7d7add6b9736898adcdc4eba18c92a02e97d5b59be7cfda54f952fa556b8e249dc238cd1bb1e4bac24b928fbb28c097ab77b9c6edfb2f5b9b6dd31664bca888ac8107b64fcb34a22eb1d4c993070673a3d169bbb57897cc242bead083f8ed4b727767113af599e88ec6656d8b73a0abdcaf5ee6ab3a9c09c9ae3a33bec0b2d2ca4c5b0bb2b4158a4544f949c14750b717cd9ea4b019929ea2e3ae4f2741ad99d555c2d8d7501877bbcc339350acb0101f0900317bcd7c8485111ab0a40c4bd3b2f85b094631867322afd0876e236236f7258aa325df587aaa89d00f86bab3144dc2791e8569646dce1889063b923304588bd9a728d4c5698b49588e662f74cf2eb3d0681c8d4b87176217b9c675172ea8920f9b002eb5ae4a630f2b4a04dd0049d7458c45c133686bc397c851dfda07f51504486eda62a1d6bde32f99b426735c335d66be382841926a3c50f3af4d37b6e1b67a6ba4b3e18bca4b65617580eb6c05526ab6dba152564c421da381b3fd4dc0ed78aa0bec32cb940a31c1fd3670015088c64859f79807a23420874834888b7a7e99c4d703c3e438a193c99bd4e7060f1c08828785b492d77649fae7adf8802f534a237cb7db1552c919285443723966bf22d0d5ea3a78972d4971554213f9b670291fb39e9dc013da2c1f87b664fb4b5a0cbba8d0256316aba86ee081220a9f6127d24c127e254636657ce2b4ea0da0e602c6adc866cd4285ab3e7479ca7519a9b8a7da04528d98fdec90814c9a326e768d70c0a1ac6a517577debced12223c45b4e19238aa0432d76465079dec6f97e089c37de1f5386b521ab3531d06ddbae087a1db558f4f3731702893ca1f404dbd0c69b580204d822db2eb3bc50e55ce12d1686a1296317b3293823f02740905ce37059051b3726c373299abd916c22986e27d38cc18dc3137bbb8f45ba5556d0019e22c0b13da9d9bdcd3b4b3168d817d3042593b0b1ad012d031867e3c9e2425c3290fcb5418e46190c4e151d6bd5f50cdad4e013b12f4d9444e2cd61848af061416c869b3169983514f67897787d7b6c780a40188ac78098867edc7457d54eca998f8946932d30380a964006a7a078e328e3011b5a41c25f60384b254f3b21855087fe0cee486bc3a740203463701e09591feec3bf87dc20a48d9e4d54512959e72fb1465e97bf00e5e9f611c6c160bda6029feabb62bdd871435a2e8b677a6450bd5671c6639b22f40f9633dc1ee969cb557c31fa86cdfee49a6af3f5e1a21fdb1e997864f2b0e9a4dc574863bb91496f1222e4a4bc940e97424690a5820a631e2652be3b047629c0eb074877f4e3cbe93c2758d0fd5f655af467063b82b17cf9acc19a0ee46f1378ba9e9d66c4d7489c5da23abd714175a92b0715f2179f6ef707b283b8fca62ea5ee065b7d5b2f7cdade7520599fe44bec72f954a09444543f25095999a95bbdb79173f0582992e978fe8253414fef7131cadc52f140a351255664646e0505fae1f9b2f04be63960caa378cd7a688dcf71d3e7d7d80a734298fd2c9b1e90139652a9890d3c2e9e467c5f4197babf7327aad91b7df338c5e53299968e36265e4be21a249e12232063d0f94705fa9cebd3e9c372c855925dc2abac51eab8c59f3879ff095111a9cb2f5d176301f2d5296fd545614ab0ef38429de6ae07389d4b1c5caf26bdb162897ca247f576103ea0db009225ccc0b9fed50d118356ed5bfeb201e8c0ee4eab1b9133d9f8149d3accdf0bae4b5d0b7cbdaea0ddc85e07e586fb7fa245b447106a8115f80ac4c80db12ea5cbbacb017218d5ae1c4aa11bfe465f62a09ead24f12d5a97b739aa3a27e6a907a54d0bd5bcd9a13947e32fda153242fa7965e14aa4f0c3cf2505fe97f7c087a5668f2ea73794f781f390c664f88648da84e934dee99fea5652c64e0a8324663df259bf7beb2d1c56e1f91371065acbe60f9d4d183f1405d3d1ab911fe685de5f173d6fce6b3e3dfbaf5dd2e2ac7b34216fe912e5802a7c1d3d46960b891029e3564d69d2f6bf0066d119b79ac2c90e95edd570ec28073e5e383c3c13090e0f19ece1846c754dde0282fcbc5544a6079866b753c04cc3478618a93d6714c476253bf6d915dddc596269418eb446c4eb6f627e612012e360a7346dad5b5eb9b3077c6ef62a7b8a349ec2107d9acac9e6c6e9205633fd93668fd9eed95dfa6b21e83078cfe0f298139a2857f6c7df4c87dbd23b71d24ed517864458f68a5818b56a65aae13fcf8db1ffab9fad0af0154feebd68c7f0b59e33bacf3c8eb6cfdd186ed609a7bb7de892d1c292d80efa36bee8d2ea6a3aaf0f57d25cfa5c7a19271794a83f07c754133af09624a03fea4c4a713c9d9d00630c9989fe24c218fc79428bc4972df0ef00ca83b8c54d69e485058c465706b1155c331c6de12d0f017499c10f72f836e9ecb3c0827f954bf0db0fc72c3144fa5bb91a5d608dd973c225cedc4c50d6538c052602288f0bf93d2010a26310fcbd6bc7146010bc155c01a02896396911b7f0b58b7175652466244aed4b599582c43536257808d52b0c4d7a49989caf4565f32c32d784fe69cd5cdebb2d579a4533c729411c0277fa345c8d52e4323caa2427ddb549da82601affa81e4b673bbacf6aea478a43c66fc8b97b0c21e2e1d077565fccb01895d0b0b2930800b777ec7e04c88c9899d215a07178db455587d2e29b574dd162ddff6814ab9af36a257283b29a1d43cf4dc96d48729e0f00ee16815ede7bde0f0455ac4760300a209fb516addc148518e57a8c53ea7e01712b42c94c9df211c3dcabc9c90d9fff48f681a15627f760897456e6ad7cf1e4f1ea7508bdae2d6516736bf06ea49cf4c9c04b794309d07ce4b9f3dc2bcf8269d3d6b00b4541bb7bd64068307fb7944b8caf14c3815bce272e4812c18b1b95421d8a808c7a975552e1ba4b6c67752d8f139e106c0229cad5e9162bbdd85720da5a8081776d11b9f16e11640a210e88546fe0febaadaa632034833b0b4f7632297e6a93793d23a5504c00a160fb1878998314a8ff476702774cdbf528ee41c116ee1b239d1e727616b7f4683df45b85bf2e46424de02a93933c1d4dd8f5e481e23dcd924a7680f668406dfcdf0e07d769921753f659b9eb846ca1171e0cbe9c60897834482dd22289ebdaa3e1d83ca1140e3d3c5b359562fc711083df5d99df62c0a7714180d5614603a7e14412e76a36399cc55e3e0d3a26b627b8f22806d1847084afbd9df21ac2ae62fc5351eacc0f8bc245516528b14c8fece8a20f81bc5c70857bd6bf22c49d252c8b8c0c4af5fe205f7665825936e0e6ab6c24b31d32607917a132bfec57e4322660bf5f2673b96e103940337496ffca4ff9931c219539ebe27b0865b99f24b0ffd4afb92931cabd0cc1bd1adbb16512750cdbeb240497c8925cbe5637647f7deafc96809bc63ac886e90161a06e60d592bcadae9e649658e68489a2ce87a7ca281841036a0fed65502869be60c6ff38bb40ca98c085fb95456a483bb9ee34c9c423544082f32a1c3bb56bb0f9d58b4439f388fdbdd3c6acc42a9a51e1bded6e1f27a89d8df78ec63b5759d83f10cc3dbb2d6d516e1b74b69e75cf7013d892227c5aa5248ea648f9b6a0e2738bfd33990f803cf9bff4eefc65187d0abb6d160d1884e7ebb953251e7c1f5d2e26404e83ab029ae2cfcfa7213e737f9996b4de0071056a09d50a87b7c17376c6482b61741fa106844d5c88eedfa799ba5a947898fd3fc22c20cc782c5886d650f888705434b587a54a80fe38f7cb52111c050ffd484397340b394150faf0c5c5e0afb13df87d350a0c1db8a9138e5ce9a0efba6d25f8a5ac47eeaa725f4937f32b1b2e7388917691c56c7522659986864de596b353c0b4c0d84ef079bd6b46ac1d011f1f155cb3c12b20881b771a96a4e13436a27fea4226413f6a59f1f3970d1958653ba9837c101de56f64e202ed8cd471d84cb1b0814129287eefb26af04ab083b443486c18c9fd58265178eda76e80fcf5bb8c88bb8e2be2328d1d782429754bb08a14d2a171652df41aa9ae2c77728908f3fdc3bf4529a87977a18193b49c4072f74e2f50ee7f1189c699b7b6f9abc627cc1dd412422ba0ba7306b5ef9af798b24f0e9eee0876a999afbe0780a7ba571ef8edaf44cef2cade5156932b3b2d5b0b1726a8ae3f9d3fe995ee12b1cbc816c1a845408bfc02199d21f8709dc28b61154cbeb6c03321b323cd7c7805ceb7398fb14508eea32c3d482838203370acb804866819bfbdc0746b686ca037520199f3fdc7a568630e71999d138539847133d4083e2c1e8574986bd78811b9f333efd19c80f1c69571d5267adf2571c42d9b6751139a13db8bdc835cc7df950cc31f3056ee27944fc8315d539a9a27f19062f1ea34f200a7208ce16b8152db71d5973c9bc1513f99e142482cb44cc479748d46df376454e11d2abafc02d1e209e64d170b4b04b6dfe9c1abee3cfdd03c523d71ec1370a7a98f3135053becfeaeae7ad573d622c5f4701e15f02b7821a0f4be0c676c408d820b93d81db42822bb8e101b93e6056e6ff0515a040fd48b4242a90d9f0652120a9abc5f81f21171724b78e7290603fe2c910b734bd34857bd290fa0ab871031ba4fc82edecfa48ec90aa3183fac8e9ab907cf766c25c8815f3a199d4a910be25c993685e1fa97a6eaf44279ad8431f33fa08e3577d5b2d78af38446d75df36473bd4eb86e27738fc03ab79be8df7d8331de08e78366aefa8da1bbf89e64427817c9554c8c51345157b5889ceb38e30160766dfcb77be4d3de4aa239544e5498e0ba1ccb791c47c1bea0ecfe365d9b91e59be4d3478d6f1d17c1b8aadc9c1cec1c5de74f3e146c2f9f42c1788dfda6d249e907f344692089009dfa63c8fa0502b36ca4fb6b19f2cce7201edffd5897f63866f4be3c729286a690a39457397a62338e9591f4003b7e08a06eb415701dcc2cf4491e9b6a7fcbdc83f4362ea79147c5bbd154ceedd6629b7d69b0c7c254a986cea6f227f0a71bbf40338b8ae4a4dfc83a0e92ea657a591deae25e482c833fe4befc10a517de83260f3226ac632e46b082585c6ac16afde12552603366cec0c8f5e0ee19d60507e0cd83a1c284a293c596f568e859a0440a129abd438a1481504f492c980ad1bc1aaeebeed695f20f7344bf738c4b275cda64bdc0227b18b70f3eedcc998d98ee95b82c881905bc10c9c116d4b7b095bcb41872554677b6c12011cbd4ecabd88c771e6b8671510e7a8d44c199b0c59703350f038262946a65fddee5eb98376fb11bb3b379882910c2c08e27f665525cd27b1b97a03b7322844877661c99ad4731cbe8022fbbb438c6cfda70560693d9c68cfc7d9397697700ce02a5ad902b4c8b21a22365e89a81620480d17655b9f899919a8ab055c8471a2adb49805ead19ae7f45d6d3d4a128ba8c762e2a2e456edbc4baad5cbeb3eb8d171a309a3491cd9544268c016f5905f5e981b4a0bd145ecf7e735873c134927b78526afae6f754e5e848ddb1326dd1c7a77eea279e2632b7c6b24c25101fe4e9530f3bf96305b31ec060ea6759e756e9166cc6287460c1238c7bbf14f1ecfaa30d1517b11c6c74930c6dc156795ff88c37e073071ca6f33dd06fd14192ae93132bd588beeb0457d68ac6bcae98222095543401c0ff8892de1ce651b15e116e25d30a81bd0899abbd1c2e2ba69ab74104b366bf29adcf6c855f28fea16cc6c60c403a4ecc99e9cb45bf76d6ace05ec824eedb54c68255a13671bbd9e7c93be331c7da18f4b5c95ea2e1f869641f7621599f0ee5086e9f8359085c209628e93955f59f0328a3243dc6cdb0faf6d4080f0f099b615b9b327129a4feedf2f48f4336be21bb942e5229e4a2023e1004f69396f452bcd559fdb5a12b083e7d1293a0707ef9c4329feac452b170674b9fc9d8af80162d2e9be2c8e02c42e65ae8fafb8e45af6cc66c7dff81d0abec3d320bb0b032f8da85339a2c7f5b2026c098fb31f7454479dca25f25f39067d120ad5ae6b2cdb1fded0d94d945f1aa404c0c878e39bf8e4d83feb88f0f23f2ca95a22673de1bda6492c0e513527798e3b90782ec5442e397ce9ce6fc4a334b9572ea09e901443c5a071f74336deedb332fef4013e37f3c99392b83e41d2f053c637c41dc6d8e49412fdb30657c4438cb61efcfb95bd3452f97bdf49b0dd6415cea6405048a9e9a6842a212b468c497d86c23fd76427070781c463e3f4d4e82a8e52388355f3b4832f3918af1a2098a662bad7f1474bb319508d53cff3d2fff3be13fd3fa65151a435bc0358cc355348a5c9f7df60848640ae87ae03401ad936d7f1e0511cecdd479d9285bcce5fc8956bdb672f3cfe961ac956738b9db24d332ecc69ecf2aaffb0ebaa561c69f00d5ba893af08081b369d469fd3aa06b601026bab7c3aa7049344693b8ceca92e6b085a77acb0694bb4ac76595438be3e1485a1604ec699904e2e0d3e9b9321866463e85c216e513159a405f8a6c21b102a862ed84ee0998711638cce617048d502a4c3257c2fb9cd8327cb72bb31c1acf426812392713620fbc91755f0e6f4bc5da761f5ed707439359786385315af35b2ae5afc82ea78242e51223080750b5f463a9cd1569b3a34299e3c9d18c1531eff0cd563f9f7ea59273f457c18cff0895af944f2d24a37474787b9a0f40377d8b442b2b9f7c9e53997b85e7f8dad7e8b4506e37c0dd62abaa31150d1bac02b95111166af9e3384b31d594c78c8243f600204ab4a7ca0392f50e0950e296b555f830a381b7eb2a48d9034f6410cea803281026cbec0b814075acba36955a08c9f6782f018ea30aa9e686777a0887e91dcf87a897716f92913ae9e72d9ce450df8a4cb7e55115f5e47b7f167ce02ba22d6108893a773e08eed119e880e700f3683bd8af6551b1d42c85b2ca44422eedc7a775ad0a137921f12dde0290a14c45ab4ded1559dc0c141e606904e78fd051608e767b7304464de5d8e119c91b408313a994b3cfde8c9aef12a89ab9c28474fc6c94925f02df5ee65f128f8d9ff222ceca4a0e1927b73284226ded6291716990dd3e5afbf5744742bde6165f22245b9f3fec8fb502a39e84c4c15b2cbc9073e78f221279dc4849039535f67103f88ee5f650b0da12c352ae597c45335a8074550e6ba2cb4ed4886ee835f15148fc77d8a810684c30df74b3bcfe3843f1e8d47e2af59ae0ccd84af72d3fa60c633ee08e3a872e2c0ae62f0a33db40e4eb79686e88ec14ea1ae1f38dadc9e307c11ba6f2109d1a85a9558f867022b925100daf3ba4ee482fd87fabccfd0cacac97627a7035f391dabd97416c9485caff551cc1488960622b60742fe8661c75b6e4ae9172e6811050a3760075f7684494f903f64197f628576d3f2e5dec5684f904092919d66ac28643ea37d327c5422fdf97df42487bc49cb420d00f531e2e0eac6cdac8db3261381adcaa9fcffd1aac29e40e7e21d1516f71d6658ab1cec59f1f23115c5c96a516bb242d171e29a3965563a36cdc880a835190da4f20bf2d0259f8cda75e63a69b4db00c26bf8f6ab02f5d5648e476113be3559b478f807f9479dea3e8e3ffb316dbdb6b9265b383c0a21db99673494b68a903fcdc8fd6f3355f75be416e34dc64d3a161ac54d316d4db2940512c1d11c4813c95e20cda019bd678f36e08d08244f4107afc1217a05ff978c19a0c51610a7600897480febac02402dd13cddc3c94ab86fd2f7d912748fb8a5da7834f192f5b6cdd7633fc5775bb06e1953239ad74f1ca8de627a128c0b13308bddcf578b92d92da130098cc2de083c3752fd5ee5d64cf1330d5442e4afa9b654d2999aee07c7db4280cb5cf84d5dbc76a0a2a60e0956c729b4db33aae96243f5f1cf9348cd3e50897591a9e443f5c9b948022423badb7fa15f769fae9e13309a5a2c0a01af928c74b818aec11bcb0d19d3ef29cc574676841538a227f6ee85a0b2eb3fa2b49437e4fcca4a57e927624a18bc2cb926a4c66e8dced625fd9c0cd1cccc556e496d2d3cfa32bf0db064fbb1d450a8b3c384daa79c85a0123b9362e92fe858265500e3a02f2c595a971211381cd7e059f10edb07aa3e5891009068dd513f2829ad8011e87bc0fd5f0103841f69531f0e007cfd107aaaa637a6fc5a92096cdf1980670ce8dbdd42e83ac1be53b07dab9a636541caa012ac4fe463060d64b61b566c122f7c80f13e0e518f10836ac56939c84d76bc242b6a6eca809ef004921925cc8e4f8a555bb480844d9d90ba41d2ec549c07e26fc31c50c4d7b1ebe52ebdf3f5e267e54fc44150e014f50f30fa3ea1feff84501f1c81a3d82bc587ed87bc37d6a237f5d5dc009579dfcd57abeda2faac2165fe714f795a28127415a02e30ea4ea2e57783cb943628f4e643a35b86db00d4a660a88add1a654f1d6e175723fdc0a2400890058de7a340c5426b35b851b0969fd8c1ed9246e95b9e801d061efb6dccb0093b3a8cd3a00a52abf1cf428357282a79dd02288aaa45198cd1c4c09f619b11323692573fa4a18521db360d751b602f5e3b6728bc1d98797b4aca21213b5cdb04a096731dbb18f549aa542eed354515752b415145bd24f97399bb1a1d4e61de7c08fa881316eee3b98f23446d61e2ac74bdda263335c3101eb829ad8f1761d7634786d712d0d19fe49c6a5f94858ef5ee223db067c154810348763841bca801e43bb29f341193e09a1d2a4a8b662cc718a82bb83ab25f49ed32a425f37fe79e030c69413e4f88546de9463b841b433eba3e78d7f75ab7f97fca48b77b955e198d9d365d20fe5ee0803233cba8796676fc939d9a138b7c769f341b06986b59fa229f63fea6d1009af6f1e398a5d55eec1c9c840b6942784510019004f9afb962b7929696000fe4ce82600f1d18cd93ace2e9f4f56f38f4c04cdee868aad75140cac5a82155cbe22726332f49d4031db2168b32fca50ebe0029d8404498e055f9045e7792aba12118e2144431eae59c590135fd275fa0d2a1c4c97a746be34a2d79640e84d1b15b68c4b2345c926a38752288ca3649f02a3a2b24a6b34edcf8719cca736b2003f43e2dff63875acfa91948311509f2cb50c6b2bfc78846e9edad08bf8fa5ca874321999a286990dc2723745251b06283c76a2651fa8224eb157824e16c9ae3f9433d774700069a25429b6be5a0e3150df9fbbaf508f62688053e01be658487ce19e1534b9968af449dbd86f5e3dc101b4b051e4ffe73b3b58c79d26c0112fdb7f038e8de0679e07f3d19c82838f934902fb77c2e7c687de203e53f73070282780e31c829ca1c1027b1a84a05b6ff3e5867544639cb7f808bd01a9ac004f2430b123e235501529bc2a5ba5dc3a24ea82406686b293a09cc3049bfedd343685499ac6307bfbb602d1e83d77c3fca1e114103e7b0c68c45d71b1b5a54d3919eece7ebb15ac916d250263139cd294290ba851712048e03d41048f6c91646186a30bdb0a289bf3d3c894556d396bcfa148a91527afda85a0387e145b4608cc352a1101f2594d5d9f3e5c2371951505bf477ecf3ae213d5b2bb030842c1db81fb1f1006ab9cedd322c742c31973aeed0dee1f181ec02d0b1f3e271f6f76d0e93497b9d41ce1f009d1d1395f82cb7e6589c6925a3f4e132e48a45f1aeeb6bf9f9978b004217b27245d6f2273b3eb931f9eec3c0a87568b311e793274199384673d9fa6586cf7185391869b0c294b716a8ef4b154662a712f4e44dc904b97a9ff3fee3195af66dec07b03390b0c6f7a3b3bc1a250110fce6647ba0f19dbc20881a77e5edcce18e79c8d6d7c468015cfa054dc9e4f06d4397c3f8f482cc757eaa25812b7c1eed8773b0b27414ce1ef4d20d650e6a1abe3006e7ba5e353c9ab38b6ac04234526ebc8621c30dc39b61a80614a9085108fea7b3ea13c9b34ba928390943d6645998ecd46ce56b01e7f4b2979384624b5e6a5aee602c823e36d161e97c94e1dc0c87af77003ec287893db14f8ae9cf4dc7617cf458786507e96c690e580fd4bf29441f1fa01c98e77ae6292e4186db76059de685417b57902d99d03d0e300b314dea270f9acd36fd6033af2713ea431d006420016373f2d943264e4723ddb0a861dc72b0fc2846726e863f96fb30d2ae5c40823cc6d5a4b778b90c87647276c2f44d8a88f4165c2c5946f4ed8caf10d56ea2c6a8b1c19a4e44c1594a0214927e4b1bd899859414a1806272273232341ef8eb4141357ea23600408552511fe30869e8ff493dfd57680300d4985194918fc19a06fe2085c7f01f75643879c5fe5ff7fd4f52c9c80008ce2269d60ebd6614ffb4ffb108d58f1c1fa598b8b86fe958d8e953fdcf20487c1f6af2ed44dd7a84bf6b04509e6bf7e7c782e6a5108e2679bef7db5b9bc624042d6b94da0b0ab184f9982b0bb32621611eaf2b156893b2c40bdfa1c8ad2ac4a79315600d4c18e29c7fc819ab02cb6e67976036e59a3ad89899a7664b6a09ce27a74de80efe119533ee3b82dd37ed508fe52b7f57b73feec7ff761108ed34a1161a6c878db368e2357b724adc3d4d6438c18cd34cc974f83fb4bd756511ed9f71cf205419437a413827ddd855dc2811a03173211b34acf35753e4052e6070d52a649f254df432e48b4cbf8452a90b07ba9be7dc97a2df654329721ef98ec5d3a7be0ca2a8737e9403e29198544496ba928546ec7e26bda21e520571e8abba617673c03f1c6fb9d5b96eeda1fb96bfd633f3e26e5c395c2cbadb06ab74cdb51c39ca3daec804cbdf3e7139db3cf15e66614e19cc0e33dc2f01ac4f045a16c39b5720d8193707b9667e647ee4761b957c5356faaf1e6b871af3b6cfee721653e47ec4fd88f5e141ad56024c3686eae10040afb3fd273ff25134a6a0fe131658ff9effa6a5b393359d9827466fa396365ada24644b99924c01ad09d60996098efa9ee38e60476f7de0dc714e44dfd3c93e6793624752d0ed27f5743f0ac71a0a474bea91a4a03ee1d4ef5d94b932e3c499b9a3b78cb0a3eb78159cfa8144915c085bb8126e3f4b8782ffe856b8540c7099478a2ceec4e1d475fb27149c0a81cde37a524f158563b51fa9e78a7774d284c231513866ea409afa7b36c10609354398ab20044d1fdc0e7d0aab33a577a879d517ae24df4da6fee2267a68818d7fe8370743f25e216739f2ef7b817d7b6002470b3358b90e4ed1457899478a272a0f484c9c964bd9e191a2d31d8f27df93707038255980386580dbff7de8e9651e299adced03e7170a278e111367db68385ba5cb5a94a1841be29c6099478a2554dc7ebe13c7657b48fa4c1c2edc89e3286fb9949fcb3c5340b9355ce6e101950ba5797a0085a7074e80c6c0828c8bc513ab3b4e9c9c25b7b1602171fb27947a5daeb780aceabe302f7ad1b9c9ed65280b61534a9f6a56b1acfbc217ad2bd28fcc9b7d28dbbc2c9439ad2227ced4e99367a0140f25949c293b9d512b6296659c659c655d8f100f7de57b8b53a10f7db7053b7a4f158ee9c73a1efabace09ecdfd175e6685bac8519ce70434e05a764e85e704c8feeba1f61bd2bece8ad3e5127b07062c4edf72f38c54ab8fdda13ecece9935fa190ba802da0b7b2fc48dc71e24c9c9585a1cf57aefc71e254f156d602c2721c1544e8a9cc0c4d9c3ef96ff3a22109337375781183fd9927bdd5a20c3db822161db269a1ee3959530a47f1fbf0270e9bfa59c251387e2eb8a29e38467ab4fd118b48c422128958442216d173376a69e1185108a455382a3e565aece2e74a8b2dfa458b72e24c1c8f55002bda8ea6737be5285ae34fae1dedf2e54b667ad3a24f7ea923bbdfa412a7c6298b535c58ed8e14e70235286d6a0da7454d63d91c0de709c734adb9e9d3a83d61723ddde17aaa3de1149ba6dbd4b4d8b4a6a64f4bfa24bf332dd891d6dcfe1b5a436bb49dac0872fe8d94724a2943b77b3ad857ca90d6200951dc04e1270841266c07d3f964c743885382f5af9dd999dd3925d869e4fa23f562dfb56cfdae65c7ab75fbafb4ac7461e5f60f95ebb3d3228f4b297da745dad398131a3821c66a51b3624550d81163dd5c61b0bc6237dba4c21db33b7ebd859f9bbe60478c85614efa44755aeca782637ac039f45f2095c231fd6156861d3f2f0a3b7e1f56838d6f8c74677b29254bc9d2ffe98da3d8b518038c4bc30986aebfe48e87178505e98dfc66bad3b3b1151781469a73091a09dd2395723c3dfa4a496f9833e6c1821e2aa0c51d40b07caf108797e148adb0c137f009eb193f7b09b2832d25cbe9a1ec392e1c6913577ebf33c627cd9d5cf923a9378dfbaebbd09ca1d09ca14ef43fb44a446f40da84b2e81490ee501efa5339e6bfebba85e9047b8db8c5101356ae6704cbd299c3b0324b1d99ec7818e9c1339bf34a90b768c1ea6ecdc98e87ac527667a407df27dad324515d14a4372dca496dae510f2db86991af3cc3f57012c1b640e5f23944c22ff9650893c9ec45ed3fa382957f0036f563d12a027371fb6738460b3bcecc48144cf6ace2af169c09b1300cfd57b52f195cb38382c5b4ebc71b777e8e8c67f7732422738590cf97068d21e68f455c190ec165dc272d3fcae7fe1b8d46a3d128f442db62136ce826ce8751863e0296e780ccf7e7fefd86e110a39fcfd5a73d97522fd203df913ef71b0d89c85c218620c2ef13d2f3f567018f60791a252a9627812360791b02f15858c02394ac5447d02429e30a2a7c810c6620a30c15cb27a144c51226517a2f0c87982f9f1b854fbc9f21f009cb77e0939a57b1bc832390cb85164c22573b2c9b1a244a6084ea082e43d512d6bca8bc5009ef4b9ef725d14dedf24978218d4683277ad0840bae30024bd5f249b8842c9f444b487af943aab514e284df06ca8b6dfdd7f659c7630ba2200e32ecf5f2c320c021e0bc2e7b473b4a2fecc84045fab4ead30c7bf0b5f922686ae412e6f9018bc6151d9e3bca5c25583e09120be9dd248a32f359be7fc6514a943e09afe491d84d2ca1129f2a099627854a54551249d80a0e194954b9c2843344512511853e4bc823dbe2c572c4b57885735e17eda10557fa04339fbe937b855c7756ab3b7b68c1955ed92641d9f8c628e54b1d331c2273a0610638863e7d1eda679e3d6fdb10f26ef3319e7362cf9cd46184fb23f572cf7f3990ef4639f0077694f9914af0a92ec1bebe5c492464cd104a7fc6514a54f477b80f1f8470ea02e50d817c45d5fdecbca018e0d1dd211c439fc484757697b303e7e538890458448bf43530abe011b0c8469560917e286bbe9d2bfce1be2ca44ddd828bc245f9ae8aba73e76f92abd5daeffb174599f9e117043bf64ddff40ef3a7daef865321d972d37c5bbf20f993a9c0cecf6260fba6fbf9431c55fad93460d3fc4904f18aa7e2dbc19dfde47bb1a3b9851dd98752d2121608c780adaa3a88a8b7d60eb323d82a75f8d7b083640e935335ec3270ccfcd9346014e9673f19fdd83bf8b1a9b8b3afb8b389206d6a07398af4f3b9edb3fa25d03d50e74e52d8416e9a3f6b1d85d6daf0137d28146f173ace6785f5ef97393e5fdab828fe7f5fceb47937c9af3b976d24c99464cd21b1f18d9181ae9481e11fb4dffe872c43c22a9fefbf0f888f8c4c739885350472306a10c6949e293fccac83515848248c2ca4bcdfa74939e4808762ccb069fc6417848f1deb0614c6088eda13b67f870751a016fd87b40884107f1a8df2a14dfe583883fd5869f868d1b9b023056a51207fa735e0b2e82cdeddddddddabe08003002ef78946ef6853a3e6f717e1a8190ef17e663884c302071cbedfd921de1d1c16ff3332220e38cc961eda31657083c4d13630dfb8a3bbbbdba0e105625f6bc868d06a6c3438986e46e845f45997510b338a7ef5eaa6923bc9f348259696918bfd442fa1191d0c4763aba1d190d5501f03af90da9837dc86c6216f606631e45de1c6ad8e92c28a3793a21b033788386cb82c042b9f6d84e0d77069a8418367709f5e2e9fbecb7d6aa99ec72c9747dc2711f729c47dca9ee33ed53e5d7d9a36887d124d37e0b0e1868d10fc1a68a82186308cb2a67466e7322b24f36c91dfc5099b5d17276c6dcec5098bb93861afebe2849d189755ee1b4216aad2f9caaea0f327c74cea79246fda3ae79cf3e23979cef9cd393166e639e7ac2edb27c792fd7734a5945ac1934b580cd183af74ac46814551a3c0a2a8516051605178fba417c65170facda2b824739a9cf4c2a4c4b2cc07953fcb3a1e98b6dda89cf588782d56d3b6297a6e2dd97b7bea7532064a24ba1fe1f567e9fc07567a4b29a3e8b28e0c16b6bdd76b7e7742b9a0f89d502e287d27940bca05a5761807e508a613727a6175dba4dc3629a588d434ecfaeac9d5b66d93629b14467860efdf615fbf48735016f686519d191629c22986122a52a49b62e31b01342ca972fb6d60a153a345830e0d3be04e086463c7464f9fb8efb741a54fa23f526df8f409b3c1138271412cee480316b76b78f152c5ed7f69825123269c78625403284650bce48c9608b9c08ea29f4b43137cf298340d379cdade83018bfd230d4dae17831a3d9ccadedbc2eda771c329ed5f6e3855351434fdd09516f8645455a3aedb093d0b12ece8cf89309088b6c323f45b08f4ae038ff8fdc155c8e20417fba9615ffb09f71cd02772d0c4dd9d7932ff70aa453f1df71bc685dc2e4f58efc26ac331525e70fa348e6c5e9c8c9e6bd23b8c6e5e58231b6ee431e993a8a64518f0499402162d1103d1cf16c6914d131a88bda8469402d112a21f2e6c7647d10f88d5102d4143f473fb3590c638b279b9b9b2b046fb1be97ad4b859387330a74b34148c340a4fb87d842c436543ba72d392db3bb84664146eff45c3262f37b75f6e5e6ec41d16216c2c1d2939db4dc52ac6c4d296fbe61bcdb9724a8e149d9999edc2c971e33fbf1877e53584cf39a713b2766fd8a807267c5a6cc730c73059abe73d0ed782ae93525e977bd7b8bc7161b7db5c5898ec76b7c5eba1a6d3221356d3b99c6a824f544a8b4d4593db1408b3084e9f4ae9133582452ada882b252d0210ee0eeee84da0c06f38e6abe2e3981e8ee9b711063b6a3a5f1977be1179359d2b645f718cfcb027c81b5ab58fb39aa6d12cfb1dad0d216f565d88a36ae55488e25cf9da6754fb38ab695a46a15cf94e5a64ae7dc7eaeebfa381dcd0db3fa40d80bd4bbef2d426fb5132d1317215eeb869adbd773cb45ac5605fca4dd3368ed134a9699dc6711a94166f9ab09ce0b4c85684705b00b5a4fb755d184665180408f7a3cc0074c8100095853c22efb5d6cac8008911258c2b6cae9052b23eb6c7ad75940f37f5d76aedf7fd6f4170aaed730ff7d88f4626d184282b55922c065654e12802e21414dc3cb850da00fd5c186cdf915b3ea4cc710eb00e09858e1c990b128763fa3d29ec289fdcfe2717e687f4a2d8fedaf3a2f4a2946214c3c1b13e5a1266dc351ccb6eaa5877b76bdbb66d1af73fb48a03e94d13d69416bf2b09ee476e939c64ecba2e0ca31b376713172580fbb6fdff604898736476771fc5cb94d5a446486f680b8687d0f5fff0fd0ad9434bd891fa3869b1096b8602512bf0cbff962cc11b1c63039bfa0b10039a6ecbf8734f9fe49398b0d2ffe715516bcb7aae0f1b4e1900cb5898b02f8a542c83192eff60662e4677e41ee62adcceaaa665329332abdb865d4d7c88802e2a6aebc2f10e6d734e0a068b1102b2e3554381a84edda9a13d353e3e4daf392ff739316cea5c39598844d8acd7f6320757be1a68a8f155caa90c460b40ccc7626360cd412f3033c335b77a5dd767e056277671120399270761dc09faa5d8f5d3410d6c8ea19f4d6e825de802459c33fdbe3e8932a11f4eedf085eec3b14fde3f37390d69d01936b0b67e5ea8bda3896177e4aa566b9f25b26ab5f6bf99594f3885037903cd09a73296a35ad552142f129fe63b7dca27faee2f44cb9cd5e2548ae528211573232c0c63b16ac5b04edb382ce3364d0b3310880a58e0025a3844881020ba724fe735f30414b7bff2e0f67b15b71f63bd9041089bfad90abdae8bb2582ccbf5280bb626b491706c10a21c4e9d6ecf7c359becba97df21b12ffa2e84711a24519084fc99fe64014760106ceaaf2e5b3748a20411bd7d245d108e42227afb30fe4142df3d77301efe100ab9c56efac18ac219377194ced9a30b051169318828769cf966420fa4fb50c82136e3933660b8063e551c6effd69247fdee7a345fcfaaf6cd71cd715bd765ac293bad20b6e64e90832325cb51d7f7db3158e63a3518b048ddc77a59b5e6784e155278aa50c2659e2a782e7bc8830c5d00780babe5e60aaa5790a36cbd9ae02b08f3a952dae114b68371c13abaef1f311edcc6a0b8fd23f6c46dcc88db9812f7476c89db23d6731b5b42ad1816acbc2de5155e402ab02d375710a71a6b82d9efbfc6b8c4601d5d4844bdccc0291912f15de6a9e20ab71fc381d25c387e33325a18be0ce50ebe3a1e1264374dcca64f4cc2a8c2ca0d9c70440fb2e0854afeacf50a6253ff75ed5c41e115745d2c47d19f4be75e3832bcc6e8202cd01ff71afed01f5a330ccb300cc3302c14c274aed499d2e2ce157405b5b04256784eb14c8e1bd21d9420d762c8bea3e5e518e61856eb1c63c69d1486c6ae1db2f6ae87bcbc93859a691814a09904ec8080191da4e84764e5b6cc7590862bc51c3101b8fc3964b77f2c80e862b1bfe31c3a295d4a175dd9342a17c3300c7b6dd3b28a6593e562e0c42e90beccba1e7ee56b608e188e09400e321473d4bfa294fc508d27dc936ea6a6aa82845ef4a2d02b51cdf42900b77f87e8fb452b4ee9b8fd223003b2ec3b7a8877060bd6c875c4defad84783533ad4601402d8d49f85335b582e54409b5a077800ce816372dc58998c0b569c999911fd681dda9148e89fb6e2523fd6417bdea1a6aa34048c35f8331997605f01b0a9ff00ddf110ba3294e1987ed219ec0db177c8be5fd65aabd434d69491b97a2cd0cc07589c3287d4cbe5cfc129ff98310039dcff007667a48e50a87d9cd0044e659fd25efa6f94dbc24ad06fb655bfe8bcfe1b78040444543004d7158e1e165144329000f85484c57e664eddfed1c6e50759c70ce7d0cf72092cf60b9fa4cf88c5258c231f1f4e71951b2630aa8894c22de38a108e9140ab8c194e7173f184db7fc92adc7e29bdb8fdeec331fd333eb7480824c34fca4f7ef2a3d77561d7855dd7459344c55d373e3e404d08c1999d99c5f304ca107cd0d333041fecb464f780154be147a53d4c55d5135ce9844b25a76415b5ef0f42130020d5f61a4e0b080fce4f95adbb07b06a0bdf4de294a2a37c8455bcdca345195ab83234d2a36505fb766bdaf4918333e9a461dcce6acdaefa57569bb06eb6efc76aa4d4dcb51beca64fb3c9e574ba7f1f96a38931b2d9da63e991ccc6dccfa99e0112124892815363e054fd54f6fd1248fb7e6e0b2b433aa5c53999b0527a26b1fcb5a252386a199270ec288164189cd2e154eaf64b3174342882635a046cea97354608715336033b4a209b9a054b0109b0bc180cbe227325f872f96f30d96e7f8e1c6e07e076cc6d5935ffac0339b0bb1ef36ee1e89709c7b880635ec03130e098de4279236d98705bd87951f710068614e3f6674adcfe0ac4312dc451d246dab095211851c5752afd33610f59b55ab55a3946ca2a65ad478844f586639ab0a648200934b2e97af4844d86535e0939023c3d98c201d91ecbbbb9eda89654f62e534c19c0651e2982aeb780eccd40791d64d465539b2efbb8803430eff22d20cd8c6f11d26283342eff7d0b48d3f22f63fc30ddfe0da8c50ee3b6bb036541ad2b0da573a24829a54539258c16dbbfa825b01a68d15b2df6f781d6083b4a1b1c920f894425097624f590a8342a872acb916941460e6e86d3af83a383a383a38323a5e5861414e42852cf5849414330c20637347f022d6b6c2e779dafb55e3c469b275ed9834562efd56abdc3eb3fd7baa331ec7aa51908233b1e583e7b1abfa369bc75add20c093be3e52dbdf61f4853e3bf6f9046aafab7ed0ab6e5e56c79ee78b8f8d75a2bb75c9750566901674f8b934a8bf3a7ecb438e794351c336b8dd203bb35942b8e995f04b528c768717e0f4cdc00c9568bf37b15d4e2ac30de54fa3476cd9d3f613ce4a1afdfc9f233bfa1f42947a74f730a933bbf77fad472e7374f9f98270648dcd9aaf95ed3a7b6e9d32883ee3c7111ee742fee94418eea9a1a8d8a1dbba66b7a49a366a8e68cfb73597d621e27aedcf9eda4714267b07306d82aed05f6c6513fca55e8c7bea9359e3b07d47850aa360bd600d94da29fd143f4f6c98cdfd135c097df2457037c79ee84b46a06ade0c73f170bb59fdacff934fa540a4722e1fc0a4e1850d6b84d8b4ce6b380b2498bf3678092e5eca61128a291044bd31f85fe03fa9190e08361d52a1c2b1716e1d6703e8ca66d3b734e7a2714ab3ba74d0ca2981c500cb29850d4cc89d5be992ec69dff37c40a6220f38ae74a5e0c9b73eec0eac06a217779ff7c4998b5b1b0f64ff99347d959d828a594ce3927dda460c7ca1be59abefd5a7b2432eba5dff42b052db5b3e7a4b3f341c985f9417f524a2911ee46773fdafd4ad9891c231f8b0dc4b947faca9772728c7c1beac2b2bc6033cbb64fb4f341c99d23bd17cc0f23afe4b6146a9582ed1fa120ab872abd93caee4763a05fee24c7482b804623778646ccb60be026a5eddddd19ed6984de23f48a6d04a96c5e9f26f7c9660fbb7940e52422034bc4bb729c978877c1c0eae5d5867be36658bdc23ba24811257a371b7070d5ef7603574531e493fae5235ed7db27fdec55ada81756336de3ba90c88e58481e6513fdaed4fd28b5b87c2f336068d4a0a18607431b376cc07183e9063134c3c931f449e01d8fbf7830b5f73e8937dc0dc371b7ca6536dcb8da0358f5d9b8db16de8d03efd6fddd4235dc4d44c3dd6c8dbb8d68dc8d05e66ea41977f35eee566a71b99bcb774411db3d99f2e2dd6d06e96e302c77a331ba5b0d7b371a4477ab2174b7efee0672770bb7bbd9d0ee7623bb9b0df56e38b0bbdd70dd4da47733cdbb0100871c5c06b24a8c1143934ee98dd56c0d7aadf57b300632380eb9dce44a1c6458c6945c068fd04b431d9c903920e9b93d651036e7c9be955c06c73a1691e37a8631061ef17bcd19562555ca2b2eb6383b9c7d79785d53077694c121863ffff4e9026094e42042643885fdfc7f79198d5ce6ba3ffff80b7410d107c7be724722b4c9f65b68b79bcd860c3bda1f4a7f0773012e45d9267de572f672dfe5b8b7e0e5e4e5de3b1e9c0ebfa3b91f051899f24a2edc31b22dce1d54e8e97ba8caa67ecb0598d1c1e6b8b40ab13da64313ae431c64d85772c17104573a8ce3402577035be76a3bbc82b3c58d75cdc0491856b07a341b044d09b262498be49bc96b3ce2435ee691bdf6f24a0d1c8ff4adaf64d3b4efc1577b25370325380eb9f65e1a983df6150be70bba8b6bb928cef29b50c76d5a3823b2dd0cf7bf69337f5ddb425c6773db6bdc3443e99c4280708a514354a155a4bf16d981bdbed22ccb324da3374d5840459c553b5b8274a7b4efc563fec5b2847d82bd5f665872af70b4a1cbe4ca6f7753124c44f572081b0de10760b835ae78698b7368192ca12dfa054e79a10b9ccabebee609486d81535ce01411f5728a3a388531cceb35697dacd649a7502b3826081c7305dda13a54678acb568bfd455f373c76a4a59a56d32ad40afde9935fe9d3a42c1caa934373a4b42877785a94393593d913ef0adbcfd20648c8eeee661658973a67f522b8ec0083fd6f77c7300c53824f8f3ff7fd38c5dd60ab48389653f3f6878064fefb651c753d28de1645d1149bfb749feebf008fd2022ecab8d87337011baea2f82789229fbb05d40d467362af5772af7074795dd2b718d5b0c8a228d66aedf7fd6f45ac86b45c4d51fc112f8bb7d2481eac563faa241c0a8054dbffb46c4eafa4952963e5768eebbae4fd298253ac7d0e196904eb30491b1c38460b45297694adbeb969d2a7515ae91a4771dff26686a338ad388a0b61ae67557513cc15b28a432dcaf0816ce590115b943a3a9c9237b20a46c926d8d4ff7da391bc913a43aefb97fc02067b79b1a758285b12e80977c900184aa3c51731c896942d295bb2f537fee6e6c6e79cdbb66dadd6880bdd711df7f239eebb900689e843cf8d9074cfbdb51a968dac866548b8ef422f0a22fa50f7dcffd071a210d741e9b82ee4ae8a75b737614dd17a23a3f453b252abb5dff77fe97cdffff8398a55d91d67741cd52a01b0404593c400aaeb93d8a0a2aff384203801828a2609f6858dea0aa3249148f0818a865160fcf94a9dffbe4be7d2790f8af5a25c158b2a4a2b4ee970eafa2e2eae22542c2e1d29acfc7b00465d55b0090b2c6e5fd2c34b670737970ec75c50ae9c0be7d2b96a2e25bcb8359ca2d78a753541c15862e693b9742e9d4ba7f4a34dd9f8c658b229d5b45a1fa7ba69cb51dff685c1f6c728ea059bfa5b5f8bb6bc60dae254df994bc110e332e2f673485b1cc3032bc391f6c8ca7951ec58af67ad17859569b5ba25058387735bb3e913937c3805805b23bf3406cbd7cb0183cfedf90518c008801620b0e83fd218d938c829ac65b48d76fb2db43fbebd6c951cb500814f340b165b74fa4477da8aa3684fcff5b7386ef2b73b2dd22cf864b3b03d6e729c5b3febb9360b3e8571fdbd0cd2b4d3270b068bfefeb6c751f6678482eba3125cff0be77acff57781c27a386214ccaebdb14deccdb514b4411ce38f81560c8ef1bfc0d1126bc54dfe9f0cecd8a263ad5cff161d47b5e0b4e45c6fd161550b4e8bde02e54261c7169ceb524ab9d5708cd339e9dc566e726bd3a2bfbbc4d956d75f0372140d7188614736239b161f1193255c86aee4d43c430fee0c99a594fe5314e07657f0368f585e1836a59c94c745ac955cad82b418c2965c1886611708e4ea1f2b96854272ac480c1602119841384529f884a397bb1f8c85eb1bc6554eb536a71686d9f1d8c43e35cf6fb1a3b0fee3df90ab59a55c61b75fd4333bb9aa4584455accc2604706d29f0c9be4d7ba855fc67161b0f4c9f6137cc285353fc32a5eb9cefddcc9d56ac5e29c68268d4dfd208be0f52bb2993e7a442a8a34ec99604f0b200708104778aa4459dd917f7440e59ec091c7b83dbdaf7f8fbefe03f3e24e2f8a6b0c2a5248bdf266d5bdba6f218d1295d6f508dd2a598640b86985282b1546d327f9bde32252e521bd0d3441556d7c824b013f60c149013f78018b2212572fab20b300aec732f7ccab7b75edb9d35ce3989b262c27b2f6d685ee45516764453f378e1a85a21f1b8a5e6e7e382e2c8cbfbc303c5cd7c3d16511fdbcdc38e1862adcfe1b7e3815623f9d1819e8ca8edcce0e47b1cc6c9e524a4eff40ea90dd53ba1c7d927dc2b89f5fe75befd3dc27adbe355825d76ddf7d08bca410daf5d968abe8b9931ba8e44a51d77d05c722b7fb1d1e02bb906b91f9e21639f00617de685114bb6fb4d8bffdfc19d98f1b287e8b93eff8cde3ba535e8def08b6ec7870329b727554582ac04796d58a85cf030719763e5feca2d281b6edf2ae877e1c6268bfd58e87d65757a7e0147729e5f7e02b4398eb8508f9afc7b87bc44a56721d24c2dd4e4a198b5caa712d4a2d1cfdb1907d03e7dcc229ddfdb39f19a8bd509917b89ed5daf9a0e48ee0756f4bc6c69eb5efd9f74aa5d2676dc98eacb5d6b39e673d6badb5d696bcb7de5bfb9ef7b6e495acb5d67bffcd82f53d505eaeb55697b7a5f7feea7e944a2fbb0694defb165a6ab1a0bcd65a6b7fe3acb5d57ab664378e23913c12f83d7756f34a5eb67d090c026e2291b5f54bde5b6b7774add67adedb92f73bba7a5ff2acb5d6daf78e8795e9ce725cc9abed795f72992df37aca712e588b170a6147fba51f69d00ffbde6fb6e4f108bd07765f0243bfc912c82d8ade039feb93e8ad7d2659900773e0ac9b9655eca2d363820e937be3320f1540f7e7f2e54bc170656bf1b1d2a7e50876fcae3b4af2b449be53b9b2050956086eb1b90b1e38bab0558eb6a58abdfa24efd7a7c9bd068e44c44b430dcbb6f7dfb6fa98765d1bcdb4fab9edbf40212eedb10270aabebf013895bdff9681438834ad63c0fcebfa393fab1324425eca9f753cba2f2a998660fae75445e99f204cb30ff6c48dacd5da50e60c769459ad988b4c1a8ef64a49ed3702997f1a45299d379b913944037df2f79f4f04883e5105344af493bb1225573a344ad6b849144a9bf923cda520c65d0f7c3b64c96d604b0cc3be075f2c4492a3e43638fe6d3af6771f0295dc1adabedbb6dfe1dc36a557d17e6c92ddd535f69dbdfff598ccd95a63b281e1ca8531d6d8754dbfe6c48105dbdd2f39555f88f90e0e21a2d47f8b1254b95e7f60434c9008796788a30a56b65a740e6bc98ed1d014ece4f26df5495edf02aa257308651772c751b3257548cea1dffb12e1b6bbbbdce19a05a32687fc016147f2f489bedb16a5d7d90b1cb97b85dca2b6ddfdd8970b2d9139f873e128824ba98e948bfdfc56ddac281c3f9e255db8e40b4d298a55d49f4aca05f6ef50ee00c18eb2255b21e50e19913b5db4285f64d93d3426b9d94e6516c9225964c743ca2ebc507bc9d2bade7b5e9225d74b62733d123844924b7a0f24b1905e92dc07ed820a1fef49de9392a07249cf3265bbb80b75d95cbfe181748c4e0b1b468b4e7affbcd1f5247ba4a75db4e8f40a6de1b88215e288bcb3e6715cc10a41fa23f27a8fc30a76fcb964d6b488c30ab6db6968aec6b9e2c10b3f25d77b2ffc8860b1200ca845af1d0348ef3dd6fd20bde73d89f41e0b8be7fd0d1f3dd6fb9106015d1a447aed49a0bccc281af4ccb58ab48e08b6e5bd9758c7009627fdd5fd60795212502ee9595e760d607952890211216f29f40184f961baa427853e3a6e763de8cffffa14c62522c3b89e3704eb7543b0e35f7fbe1ec8f48b169f8ac131419d16769c2bd9b2cc43c5cee53bfbe4bdb5e37b41bf6d9ce7799ef7ddf1f0de82635ffbf40b52d8a3dfd12e3fc1d173e7fd8ea6634399ab8984680a9d7ba42fe9b7a7419cea2ab7fbb1a10469d1ad17c4aa104b388aa4707c8d84a30b96f42c3f577d6a79af0fd402540a5b8a6049a110de939e251cbd67f991065def49200f2ca49085bbd306b10fed22a341a3778dcc39557e4bd26b62a30f5e904df34d3e02b873ce7f8e994bf8d09f1e96e869916bb57127367e2a57fa60cae50d6c7c63ac39d7e79c5306714afedcc46054b3a4cffcd94bdcd94a1871e79ce1165e356ef2cbe65b3f61fb15ce1d27382d3a65718c5f35d77f8633a7561f3bce9ceb3347a74fb2e6fc8c31c6175f74d105152a524a9652b2949225675184b4f6fb1e0be6918878b7f9cccc5232cff9ecd287d75a6baddd332e3b76641f22407c6494eddbb8996516b071f3e86871e304a01cc336b9238d19d20c96797cb0e4d6262c5fbed11e18d2811b9e1d1c16076ca4ccb09acd8bebb2e7fa3242ac3cd7d1e72e4a29e52e2a1289bc1012cd16c1221c66c572428068b188e74b650884900844c18edc6227442003f9d61b2cd69719bf4a5ff607d1715b588110eb7338ce5b694824081a9f4d486a5a587550f001481020b0bf7efbba6ddb5635add64cc32efa7ddff7f9d0557c8cc102f1d1021fa80f2af8f0390a08077d3d5fcfd7f3f91843cfeab2829458e57cc8b41d0af051bbb6edafdf4686d99179afcfb8b71eb76ddd17388e9340dc6c91878040b40885ad4c3a28d820fa74c9d6009122a164c23692051cd3df3dc146b240b2c0ce1a517c2b62606547e0c1942bac5c11d4d2ddb9ac12e1ac0e42e7ebbafc19e33b29db95a58e4da1be94dd103ce87bb6ae6c145e0da4155bcf68d9ddbfcfda79fd65669ed36bc58270136a0ea149643b193d3636407de29e1554b9fe57ab4fdcc3822faefff505c69a2f67f843f7fdddb7fc504843a3a4c80e90213b7e7ed82788fd5018a4d4f2a557a21a19a8c1fed0ff20a528948f0466f6b36a6445a11795429a28414a25191a1a6ef9d0cf0fbd8b7eb4f267e87fb02d5f7a24a3b7ff430949cb979e55dccd1108334ba0f71624bdcb2b51b1c89c6ff512d248964d0e6b256d5c3c12cb4ada7cac97877925aa19cf2ad28f3247028dbee569a20429fda864df25a4913f3f0ae5289436a4913b3ea39d914f8bb4305482300ee8be1f86863f74f51be687ec617ea200258c31b0481f2508cb73cf71df7d10eebb6e16f7a466d9703ff68ab361b9bcaf5858bc1f924b48e3f25d1096ef421a97b08142ce0b4d2055a41066c6cb377afe198530746ca09667550bcb7baf4445fad1572fff2d342edf852f21cdcb775d48f312b68439a109a4cae56a95c6ce298530f495a878c786303404828412d515e426a7e105f65b3e4a90d277300ee8de7e90ee6d4813a5cb09d27d4b29f4401278b1743fba8dfd8e85c486304144a1ef42981f441f7a514813050648f70f605517fe7005bd800b329041cf6aa5f29d9a56d94fd27de8458f230c96553e3e3b5352d0032420e1ca6a8524c98a0b6168f863ae260c0c901f6bff890dfde7b91fdd070148eca8a6ca6ea10a55584ac8b95a3673250a61b8178530d8abc66973fd71846159e5406ef2ee43df7d2884c1422057cb4dfe17d0e840d7bfe7ea71d4d5baae9eebdd8f970ecc0ca74e8b2eb0e364b166563515ab3aa0284df801106c3016ed0f0a229f7185b226f4053bcad67cff16417f79d55c17f663832365e4fcd92f7aaa39fc79e4e69ddf63de296a546c8e46cd50f98ea795a8d42d1665492c158d040000008314000028100a07442291683c284f84693d14800b809c4a765a9c09d32887819442c610438c2200000000406608940400c098a877360ec60a339d1151f91f721bb2615dfaa0679e6187c4ee3c9c7257315c352a47cd775bc2553a006fe50aff73fbba59e8f90c995251f6d262f78e46ec275928da5c4f8473378a8ab5e9982455fefa73ad310541d872c86c5916e64d26df5e44e96d193f98c6c541650851fdb61f99304d3023e272e040feaa1e03c5af589d0ac4582778eac66f2d45cc30bd83754c9670c7b12895f5c6cead7956867075ae50d24acf671323304684f773311152f00ff0bca23ba155ec57dc7483a214b71ca2f1aa00a1aca239cfa1a949cc27c776004a87566d16d22a83549cee53aefeab583c14b1dde6fa43d2eaf14ad410e7cc3e5bf4844ee2f947ac6400b8adbb41a6f7258322d429093e75a6d4c0362d4b6b4c12f73b6472f62fb5bb6c530710eb826b60b397df60210c35cb5ad6f80c98c629c396ff4c654364a22e304de2a61716d9cfcab4e439585162b1c9e69c1dbb43a0ed8cfff1ad72a07aae2adc7a5ed7295073148e2cddc52c5595353491fea949046ec0bffef91686ee38384c01468ec563e9edfba7b150dbe4762ca7bc3486af3e800175ac9eb2cfd9adfbd7e1e68632dc1e3e981b0a3b49db06c68f0eadd5d067a008f845c71263d6ee678ae6d43ac7f5346d7b4c465b2bf2c0da4d1c44994dbeed595517bf7f197e7e199063adde96ab55ee97e1ddf8ed5310dc92185fdc5aa844d1e824873f25daadaeadb7656ed9835deaebe43d328ff43e2638e5c757878eb2605aa13297b6b0681d30c182ec016272f6a7669e3219b272523f2874004b27d0b31f325f88df87c4f54a47e9960fb78633138100148b2ff447d4c50f05f5fc5a3dc42a3c9d8623690d247ee4585e64ed1a356949dd0516603f7c0c9388c27fca43d48915ab1ef215f52206ed5535ed9e9d12ce6fb82dafc01870f4b1da60ce7b9eb4347e0bfab067fc047940da2f8bfc85dbbc48eee716a2065445f3fb37eb2f9e755669b1d8371b6c0e2989c9afbb04a66fbe680580dd2a9e2d17275cda44fbcbf8806a54e01dd58649d4781f4e18514811ca128554afd7cc28bdec4accebfb90f7400caaa298c0fb1f444f844bdccc11c6d7f57ec0c20403e2c3227e151d6cf277a97ee6e0aeac6ed4da3547e186820f8575606c8944f1bbaae45478482d708b73e7bb0d0b31431d37982893e924bcc9ac51257c41dcbbf5ae04985e0094831551996cfb437dbdb6efd605521296de501051f55d527ef2eae0be5a7aa5ffa516e13df16f28b40bdfa977e854a822bd62b691eb66de8835a84c6e40ee5661e4ca239dcb99b6ae655ac9500d0ac981bc46a1764e2dc608d51f742894d7e9a468bf4be9f364e9aec59c4c7d7b1f9eb025e9f474d041f7ef656cae648929c49025609fa42cf5116dce4a614e33d7455db322784110aa8cd375d4dce92c4be9c77f1c77d2adcee84e3dae653baaba17aa6e7237509c3dc91677321ee67a3d804bd2d574bb4a8c6be051a728ea7c0113d096940afecc673f0357c24a983e64c5d9b0bc4ccc0cdfd8a9f77ad770e959082e2a084fa89919a65d7ad4ab8fa833ef2595c02cc64d3719cddd9e4c3c742c0c5650e9f82f6f4d4f5a12c77bad8098e0b6e073a776b8da44a5ddd9aff688c896a0085505b2813648b1fb737943bb3998ce69e88bc7a11a159e194f05db83510a7cf54deeea4e8966f966302e28f9fba77bc6420c6b0eb0a5f15537251373de466061456ebba0d4b7320694f436ba5ff141e0f195c66ecaa761ff80091dbb64c2128428ca6b9d74ba824f5edbcd2beee308c39a06efe9f40ea325a80be9763b667e74dee42c156d3c93590a132df310e1a4363ac8d6e439ad3eaf916d6e7ff368c07359e9c1d89aa9d6368a77e144bdf0786878f1c404ad132bf18cad66a6cef9ec01fd6d6d4073de3a86d2b8e205311c272fdbc1d23959d39853c3b3cc1adf9e0e9f492bc320c14249fab29d30f6c536930b25172b8eeb03e15268795ee8ddf6d19b45117ba7c8e9ff05b261104203b8d68c2665c4d5e49380498a9cf87033809d4801af77574d52af07f53f5e419d83a5c314905a1da38158c6a4e171063fc9b3dc7f0160983afae5ca48ee23b6a179b351c13a721801b85a7c4144b26a46cf6a4666c32be3cb5daac2b818bb072962bb94dd1d868acca41b53dbeeca296bbdaee36bb14bb501bb2cf1260936a1065ef048aaa5c72a79000c4938a42d3d00536f7029459f12e95e447cda0f4562ca6eeb549f06674573d56bf5b4c2ca5610b3553c01a0b9d20309a1e692995fe596dc73b0332eb7f31f464934cf4531edf1194edcb8d887b46f634aaf35cfff8fab41f181fa8362a50f509eabbb3f656c6eebed68f66550e75152a88fb6f1ded03e4a758fdfbc766bc92e478451d9786a812cfbc580fc9588e2eccd25d0efd2509ce9c777661f87d9f7837c88f263038124ecc2fd2b89e04723636696a4f9cede8dc38c63b8073e9e44a033cf016a98faa9c3a3cf8de3096d153d924406952da13c129f2e62ad52f9d15cac8596b6879b2a4b096f03f65c11bf85c7bb7bf82c4486e3f89be0f719ab14ad15695f6b9aa30d66e9e773815e414e458dab2f3b834568335964fd7081ae077ce9173b09380a5f4782888295b16fea45342accbb84fd773144a10696b35c463a2962df68bf3e23918ac8339b910b8aef7e4e80d9163d33f4694e72785068956aa7c3805db5618d0d98a94b9227bf4ec53c3b491ad07a5d970fb8fe831b1077d8b32ff4e43307da2ed6a177f0799968f887c0ee984d2b4722d16346fa4556d6bc0035f43015cedf87997aeffadea2761ee51a175aa535b40f7034af0219a9c669dffb3b06c42ce32d06e28634e51abd5040e9f9d3f7cdbb789f23faa36487e43ae3d99a5b70424cd198b36025a5c7ea64fc90028ae3326c9b3d925c9c2e6213b7ff2646843b8d86195969014b167d0081030bd3049ad4147700132e0c2ee48dfb06624584bdf6919f05e507e426608a4f3fe2ca61f8f98e1d4c1f7c8fc3ee3b280058784326c66503697a3d03b41203f943da4b993a1ef168c80fc5a6aac55e4e8717a92420a5efd2d0dcb65a258bbca3d598837822af4c3c0a7f9cce0efde7e0996978482f8c37e5e83bfd660b8f71d4f68599a5b6c5057a4d10c34e8852b696c0583521e657ecc91a091458e231321fd162225e8149a24b31aa12f3e773c8304e7de62d429ca0b196e04c2f7a2b68fa768822e55544c12b87e2fab83b9c88a609ad787a3cb7a289d54477b314e707250788bfde7ed950026947feb91c1c695086e104a5e4ef30c92020287ee0623409e16c904b23bba8ac4743a9c3707d9c3e9832e2f8bf1c67447f7ba7d53bdbe992359e9b13f5804310fd4c4518f660d81b7e96d549663ce283b5c799ae6cf36a95fa68160c471836ea3305e9cb20789f803d9d5e7d61b8c534a91a27ec886b843b763ed03752ac9de7e0b965ed6b3b3162c9d448daf20700a54fceb437392e8e86befc50a5dde19cf5c680e182810a1aa27144358baddd09392da2a214b39bd5b31535f7a1056316885a9dff3a5ea8141d66d9e2b8b7cc8359918ac81d94d2e83feb5aa820abfa214459174f43963e4d2ad281cf5e9252777df8f1433960609c45231adc526c94c9691d99c45283b6450e5a8cf36019edecaca335e5296cca6214fe73613d2fae809cec90cc1291e5c0f18b5fd3ca69d29a81cacf8c0934d9dd5a8ebb922fe3f22a874c693125eebb7da3f22daf9ffb4d611aa21c6c41f98cf313c97d3eb7801f2fa2517d4224600855d2e993945fb919891c95588d4f02ea9583f85dd7478c4878e2925d63dbaa726298877d1c7fc446aa5f8472b1e2b2bbb6ae781037f7c6891a41b7e0bcebdeb04125d5b774e673dffbbb98015edd8e60d14123517084b39b2903ff8512cc6656304bb8e9a3b173526505f7ef1b28e4b1073ca2f7e06535f94ab79103540719574c833938cc7cd9cd7a34151e1a231ce1d0d9604b5ba79a979513f9b9d54a9eb04912d65a36b69b7ef669c3e21ce759d2b072cd1cd709bcd1f9747016e47b09c0f2c62751f6b62e3155e286e4ff316b1e985aa8043309876b5a04da432b308100b79bc4da6d8272ac78bcdf8807543c595d23c17089383b34ba2c81e3ff8302d074c812f712ece4bb07377d0e859e08a3b7a3d1f777f1988e0c7ce5d6f2f323cb18c9830e26aa0be70984f5a76e340bce3b8ee307d27363bd5d8e8e3d0f678b42a4d1cefec472c4e7bee41519b28e0585c17775111fca645a7587f5e3c23e841cdf35df528f9aea17fb2e148743c3101c5e588b0bf1c423f4b9d07162e875b969569aa861b5c0d3209866cccabc5cc068d5bd4c0e040ba59f7c2d28aca34a4d4eeae766af19a7459b0c07759cf0f138d9a49000077bcfd0b6beb3c19d809bd4081cac203b17239d5f5596b70e547fdff94323a9c8a4e15362bbe6d4192eb6fa55212db04ec276c57601d8c87804ea163c4809b0591b4cde41d0d639d23f91454fb70a0f81702b894454ad6ab77211ff6a6cb88333b6efbeb2d38cb3434ce31abe297e6c8fddcac14fe4ae252e11a33d2b372c62728e8472abd20dacb7df4503884ca615d1850fba427b43cc58335f9d593fe91e4d57ae25abf94d0d8f69ece7063f7e677d0830a8215ddcfe3d26312b1a2bcb0df36e9f997c81f4a333699fb19b756f6e99d3f80aea1c34cb548bab83b191401497a0a41b996bb158d4c71d9051e4095570b6b89e8187e5ad0fb9257d16674c3fb14d898bc5341956033afeea69863e747150d3913a2ad6371c496b6bc9e824c367c295a6fb19471586822c100d593cd17325a507dd747b9674b93b20b6346cee9b06b6a70876444eb783929051c8d8b2ebbe1297a63f8119dc84c05533659669e081d9df8d613070cce6d17293368a6a73637e4e5d00eafe855ac6928f1c7f02ba466744e7de6af0f6e9f0db30cb6e221d724f29c666df84a7f73ea09c074debbc42bd965b743181c768f90b8b3f4e3c1dd5b030d385b391f8269d4b082ff397e5fc1b5974ab1b5cbddab1c892fd48d371618f6a5babad66d7b7e613aea25486a016c848a14ef53e70316225e596ef47d03ee153726e5e8d2d765ac0bbe8cf6518fe9abb4b0957727499d6aec32b8399f31359cdeb74cddec18db60fec3417de6bedc2af2131408440e314f24f5d87e37cf92bdf4d5bb3dfe91889803a3a83000188230074e465d54808fc906edba3ae0f4170a15a77a0e5b7a04f068f4a639e090f9d498952b39cc8badacfec5534acd618799e33239e885306eac5692cc5908433920dd9bdd11abd492ee63de5750afb70bf013bd9ed729cce1d1fab5e05681608bd27888364828e74a9e37ee9da30a9563f5cdd05e8f3c573531e3b4d68cee0a1e2330a80269367340725cdc507965ae96601eb3c2f3de5678ba7dc793903ac078c5ffc959ee946a93e04be7c5e2c916c3d6adf8934eb8f6903ef292b35dc95282530645f8edd8c74bec0164422151dc9156acf991165a46771810e944832f2c0ae96d5b98b0bc3361232d84c30107e2b493366c012f5e28abde6de74c943bd63b2de4d72e7d84531d6d6e107e09f2b1c3dc79cbb69f400e6c9fe737f94eb42510266e20b700388fcd9eb199bf6c08ee86d123368ee12e0c18d3cff70e56ee7d1c877409a014abd31f905c96c4bc48883e72fe5562e7c471e7b0fe4f14851ae016e1b1b44858ab5b9090d6ce6635f9347178127aaf93fb036a8f75794db365bcf86c423ffd2433597080063682d263e35f1027e8f0faa2dc6c36caa8fb88486112bfa6ef26778565beca66abdc0706894dc694613990e2e7272c3390bd839f0daa0dc2e1015168176f9df11d6add820285197b32a574ae46b2af5099a3637d704fd68fb867f6fcf7657e32f5dd1a38acc7348a63e3f10f881b411f5552ca060ab193c27bb4f411b95e5249b1ed340a7effae51066bd8d499c49ee66cf626b94cf419de1025fe07e4ab2fd85248c4a4da95847ef08991d3b9a4e4813cbf1597a027358f531a5cc3b47d1c3a70a2ba045ea01c03f87f666f10e716c2c53c7ef96e321fece1417a80337b6218f366333d033e9433254ee853e4c31bfc167b5787e55204535f808c4743adf363e0c0be0d2a8fcd0dd98ef37c1241c90d014755ce92caf46d2b080da6ebd384904821d203dc8607926eb15025f6b75f827e591f82a34ae0fcea90a729b0a2901608cf1a63556f72ed1d8f43da20b644e44ca883f490b0b6ef9dc0199dce50948f791f090ea2ae46f85a6d15536dcf8a6bb2ebf465af923501a0817e8ca932c1bfaef0598f0d969da6f7a64091e39d5fd9de192e1cb63d5780c2d6685b713db0772a65d1c533bf1030c1fa3fda702f5e910d93e19e419f34daf27a42108f4f384a3b9db93a7d1037df0bbe4001782264c2836b98c01b40da21ce96a8fe67233938318286e8dbb72ec1f17e224822680b4b4ab0e60c7d8c724ed44c412db8a8816b4237089c946a3101db31604ed8631fce3c7f4c2611d748a32c174e94e9bbfe01891361ddd9323ac229eae06093e1098feb82a6269ba42448ead9d65aaebf36874aa09a90bc6b16fa66a8b2a0242346b6761862c526e35b9cb3bb10240c506b7528f232151dcbdad66b61d9e976279bd942d901da280f34caf69bbba12c90e7eaa466c2d1a3210ea4c0ab558f65405acd88366d48ea860d68a0e90a010b3f62c0df576b191923273ed4d882aefd0c32bcfb4b7b1fc038696f2d10da548921a767b240397f902f41ebbda963fbb0e4a9dea00d72a3bf1b944de7ab0350a45793ac01bd854891c859c26acc1a24a5490fc6bcfd0752ce03aff815f2367ad4299d44dbbd7f4b339bd2bbce36b626f0afbda73680407255b8a1dcfb6903d6a2a2b492ea3f3e06052cc81ecbe821ae14a9008916eebb14cc39f4248133d643cfaa16186426d74f172cd206017e25df335219c6c08866c63733785577857f38d9503f9320a30027b16a1b604a7a8f0d9e1169b4b957f70a7461cd12e0d59764a98c1ac704df24cc4da1c1eab0d09386d03fe1e6db440597dafd367e4182d65a11952c4928b9823cc0c284cabd0c1d091f4283906ac52c73b96cdb96d5175a59dca08b904c1066e58651c76063d2690be934487c2bd7010425d6cd035904889becaa31c0c33f4141986fee8dce2bd8c759e55a470c739b610b996a84c0d2328f77edc1b00b22b4cc3b05c2503621cf252e160035e80d783896d97f4ab13cdefee2a0e2bc4eb2d2a3d0d634371a2a028580b5de7c3caa055b99568531c0ea0bd4da0e0b8e2010e3099ef9e6089cd81c29d016848ed1f04f428a509f9767e54415f067653f3a613c2128c2199fe163f38aaa17f0bc7a08326fce7976ecc358e13c7613afb8c6e8621e7370538b2d82773231d823e3daa530abdd500897228f536ae04d3eb2162532c01823c9bfa782f86be1b04cf97b4f906816b980dc3a288605235cad0bd232ac18fe87cfb98af128a8ba4160b2f9635f483cf00f04bad9c663a9860f88dcaec752b8197b30dd410c4f9179f5eec4c698ca9a6f293aa296c00bd984119b76a56063b2be5bde658b030b94dd7eb8a1873daedafb978eab83787ad6acd68d0074a1a3157c699e8d9862e2a9134746d4db8a113d9d8406bba9fe36a3d6974921336e5cc00e01aa1f96e46f682716fafdd3e1a8d763351d7abfee783ae28f8c4e89ce75701a6370a6e150946eaa0464b1d8a79d836def8110d4e35f3f4e73b2bc9c1d785696db0da9933d4726cecc860ec6fae0382973d6d895fa28acbf34e0bd38f95d773e0342554af930e9e181f72d6a7a1a25265e2e469f5fa15e8c21b318999e23c427ea30b2842c09a0a8f5e37e1ad79bd3d7bd9f74530e6d5a46902cc7e8c722595c56b899b584cefc39a4f404cea7ad60c602ba5d6608dfbd3049afa77a83e481e50e7e97a33c919745dc004ceba068a4f8a2094115c2b375d75ca35a21805e8e773a4f3ce6b4066996a5bf88c5c5189f6dee7eb5556d35c48d41d2ca889fff830ad00296618045d1101ba4acb5f293d62b69e1db474eacf7d3231fc56134898a4f056d33a7558ddd70afca1155902ba65f53a8e63e72c892dcfda2ff793fcde7c971b26e3b12609f12c2fe7e01ac8b7bb455390cde091b8308a3358379a3c522d97232fc5a4c9f0eba54a40135643a6fd04c3cc22d05204515ce7e3d5d541105e12e967b1fd2c251b1def95b7a33f21dc1d0c16d8f320023fed47728e467ba694e49c508f2c655bd7e7d84139ff0e9ad0a4d55729e72263ab9f405bef38ac773d1d7dbb6bd5acbc5ad6c3bb3116466b73362327cea4987ae610b7923eadd7fb523bb4fcb60e97eb700ec5523e9c0b97c896100d9669d01880d8d90c11313f91bc66b6317aad62cab4cc068c7ed79441dbda49b5b3f4e531b2ad03bf2364aa807d7c30fae361e023e495bb9e7a04a45769f2dc2bbfb3fc0a642d3f5da55ca426c1856b21135358e172ef2bbdd37b7f55bb60dbb52d28b6559b07f1ee3e13188d4d01e70a6ddd81f892b39976cbe9402d32e4ad17547baee4fd0a42ccf894a5391f4d34162d122320381133353e93ff57f75b77be15b07419a891836f8e0db958096b7a5c17e76900c277761216255cac72418d0e7cb23ba23044ac8c3664045726f3e9c69d77fcfcc661992e0b147c2a4263ffeff66bd86a97f9adca62d1c9856496143ae505ec03636c8db476d6219eaf822658a219a97378fa4151060644925914078ab37820756c0bbb92e93d9f1111dd9889f025152f668e2a78f8b67cf5faf076d75d13e77866606afbb73784326435bc2a8d62e954b906060c28d47d98026962385c54ca4a2f0434dc059427c82a40cc6b2562500ab0e7b06c40e17191ed2cce5484ed9f916612fc208b7157d231de54a4a3ed8b5c1b955ab9fe65575ba1e28314eb8ceeeaec2513f1f006a283865692229aa5610f5ba40109f1ba36b411a4e26c0dd20ac82eb3816f9a6ce6048e6d4b6ed9c88b83127b884979950f27efe9a607de2be1653f63a6023be17582e648ad15a5fe40be8cbb13689ec6e0326fe1a9c435d8ff9a10cb3c5affe3685bf4d5841d4f6b60b808d3e1f27bfc0d71060c69320bc720709e4a86942c2dd8044dbe08b07c2b1b69fa9746009b2d86c5c06bc1a00e5586c099ee9266155f4d0b0483c31718712d0517e1d0ce13da25985f18a8255d613b830068473c5b611f61cd00c1288f81c130dd92dc8906c6facd86db2a2ecb5e5281ab3986015a97a72d303a2cc8c12b30c037d5c85e63958ede4708cdb49c29664498881aa41289b2a899a86da8d12b13a984920502e49f085598657720e30bff45b1de51003a7767c8e44500eb6757780d36d88f99c92b2df037e80dda7671c34c35642aee1766371333a8d51ffc2daf53333892bb01062bb2dcc3906b6170af8770a1ff54e9e8e214d72acf0ceee2077f6cb2fd5b661fcab1580286ccd40cb0b44ddf52f9a2d8f8faee27285996f21da9af64df7565f216d8400173b898a7102b23471cc2d82b81a501cb00b24294eb767319f1843ea32144e27bbbc3b1f5f6f42510731036680247e79dbc53f04c00dd436fb354ed38434eee417fef45cadbbc09f5ca4d35d9595932293bd8d71287951d400f70b51b1561aa9f9e84faf930ab9402a4e2065d2dc47fd4cfdcdd30842a9870414f663bd468d89b59b2ec83f93419befa4d40d1f54358ae6d74ab17a17f227292981247c29ae706abfc4a18a41a380d52a1c796c361b3bbd375c38b3276c1d94455ef89391f4e6365e05e1c31767fc57fc0941e826706da89cfdf2fac88dc48b4734e7c8499e94374a950d7e11ad6e65fc046689457f8479955fd4e8a8ac56bef5e601a7bc7be09a7d3c802e8eb13ae074b7d2fea657b0be452612dfb9697f512a4012a1a4d411f3b25f922fcdb6e805216be65fd231106bc1c005134c093b0fa5da67169b6188483600ec83d8943b18315083b291728ae28063f0b1dd1edcba7cb0ca395d5a6a81da46dc6889d4980641562dc072639e5d70a2c34999e28a7f9f3ed133e8fb41e9cf77a1000213716880abe7f7fbc61514e88a2c72b954244b8da99164362517d7bcb2cb4fe94d7fe21c4c863318b083d78ae2803125b1d98be246413b2c3fecd8d61fd387ab0703bfe48a3d12e2f654909d47faacf7dd85f88a9ee2b65e1db1ab98e4ad9bbffca0e36930c4984d27f639fe07c37ea0e472e96b247e297b45f78895ee11355f17314e5e4834217f1f5857e4dd04ef6829b3d8c452cd32907f88f64b79f411d8174578b9719c8949c071e7f037d13139b0219f45db54a30faaf4c46e661f4d5b04548979fe3d2279f4fcbb6afa1249d059cade88882518f4519c63d952ff77b519887dbe2825f5d088809859d21064b8fc9a7de479740a911d5c9b00f9972fd98a8d1a61e434026f5b3742e7a1514032d1451f63388d03699429fa33680c60d50c07dc31b2965367863516ed49e3e49e85f3b3c25c0c25160c89b27127160d85a2104061d70199a7498fbe1462c4126eb0d345d080ba83dd0fd8d95078551bc52cf4b976cafe8aed624968dd092c424f19655635362d46fa1219db4d24311e8ccbf32b4b5f854035ad1ba8236ecc0f2117df45034b24061469a54970a783fd5c935e24d108c57377a0a6f1716c0c3644c02956631549daf82d013edf4d542f47397ffa2f9a46c0e4e5d79a23c780038e405f041cf2a567d6d34f1b3061145e39b94fa5983b08deb12903450c21b8a641e30da76e4c6fb3e66215200a3ebc8a483029681124e253f435d7169133c48faea8f6c2f99ec309ce2a77a986b87e5642ba3e89a7cb290360975a74f0257c87130c4eea8037b6e6187be052990c43ef1c456687877618c1811629451282eef80937af44da7da24ba72e8ba13a4751600d6b74f0bfb54152aa9b180d24f7d8390b59b041749a1205593e1b1bca2fcc9766e611e62a532fee017ffb039e4f0c2bdfa0621a1eac35ffe49f02b93904cd99ca1eee79a9c050a74a00e6109a571ef8c19f08127ceae7a88b0077d310ea253cc7d36dafd25dbe76daffa0af6f44b0f09ce28d4a6a82d5b327fd5e7fdcfe35498e4894824fe430c858c8fedf8e99143e63c91609c81809a665f1e9b1f258d6916f99198386ff88b43b41fc7f2311ad0bf39f8691cfadb8df8549d09a49857d687dfe68c4a180b8b3ac698280b560a8a2b60eb2e838b65d97a3dc5806265a061d899e89166c65a3419ae33d220609e1dc61b4d545292c951dfcbae3ec1f6c7d06994b7385661f10e9c04da979e55831ae7b358ac6b8633a086e6ee33d9d92dfed2f3a7a20d55cb1fb2ab03ba086573880edb0e7578318f53615a3c47ded9e782aa0507a8fa5d5d3040d6b8779acd5bdbf1c43398c5797479201012570dc7bde21ce9b2e7376dcca3dc3ebfe194e9d5a63452d460e137fdee622425d7c338563221bd79ef43398a5362a10b2c60590a2cb716b63a793bef4ea132b95469cb2bd383f7a6c065c4d3a71e10b049825f168fbb5a4cc1cfb42e4d850c14eb455eb63778c12fe4ddf5df57629592b6ca3ce5b9f4b514356e019f2bec66622d09c1b060417f1789989b046bd9b2e30af0cce5f8aea3293df6706df53de21f0caaa381d9147e838a3eb37e4e8f81171ebcd7dde823ce093dbaf58b11ca96b8f88b0da72b387d4ca6de144e1d5863c4b992c724360cba4f8a699c1a8ba71aac8edf1b55cd7b3fede2ae87374373b63d440609f8e11f367ebc2635f48c93356d4a0948f019ab574687afb216445497133acee77a90b9638095eabbe598efc205a99b33992a9ac64c0f796dd5c448ba13a8a0309048e0eed5c9d08cad887ec9c9ec55b645bed594cc2701f16609fb730deee617c4598de2642147d6fd713b305ad5b81e44225db40848ab547559ebd689caf505f893ae2a582761a20a5d2cc6000022bc39a8a90758a9880c6419e5a2ad25cb3c3e73acccbf887ba4ca3134e63f66f0fd0757f19da65c98982483ddc1471133bd06a86b8a3453b99893698ef5a6a3fad5ebe1abb5145906bfe74b555cd90d7427fa1a501e2e56ec6849351dc9535d497a62457168e1e0b345540436a3bda15865d9a75f9e1639725bb736796fcda15df3246884a5ba6bebf444e5919734dfc366bacaaf390606f283ff7a2b0e13b11f1afb32fe111523bd71e9c673727b6a2ea734d75c834f4d7f7c34c93c605767220e235790611eacee4d0cc7ddb66c3bac4834ce69f0189234d1e15dcfb5360ef61a953e53943ac8d38d5d49ce231cc15e2f9906dd5f2ccbe5f523fbfb848599440f2ad0b4a6776f86722a9bf1b1db1cf890c0eaf5b279360e1204b1053e7c5468ee43a47b36eb1e6b461e908f967f093eba4b701936a1b76cc04e825c5127d57f5a0d1b013ea050b40248c67fcd2293b430c7ad36b42f448fcb27b1124f59cb67b792728eb0d3126de49b62f33b18f1a127052332bae2245b2607b716759a429877997b843709ca9b94706663d896e893924482dc3372325c6a9ab3ae8b5f1153dd9e79b86bea0407b99586b82fc64468877c6525ffca33288457de43033879d2a76c3c8d906067e118216cebe16d52b393d8a28539806b4218b09c4c05af3766e5451c745aa08d4767eef089b75a93736e0cc7c6ed2773265ffd4b5f3f898a580c48f40439001106c357e8f10f77935ce47f248fc1ddd07481886d1814dc9176a01204811f46da3ad4050aa0b1b090f691b36bcac3512a49b441a82c30b43ba0cef2f5cc99f75ee16fee4692fa2eb756d14c647d98bd2c3239270e8b7599855dbb890252002ca74309c2a67c3d8b9fd124f23b38033c5f7cb253fdf378e1bc52e95c7a63cae90926566b766d43d048ffe7cd38b6e5f67163248114d4f742e0dce374030794862fb02319ea6f61563cbf4748e6e2c389ed4a4f88c77dda3cf93f9ff2b047d2a6c36fc68e5dd73b339bc04a16d6e93911030ecf3e79162cf2873f6801fddb1970fc9fb05ff369a5fb992111450c10d079c69d8e7582b0fa08c9c9532062fbd988b1eb796e3b98fe229e00ee65386d6922c90eabeab4f2eacfdc1833eb4ee5adb84bb460de533906265d715ca546c7285979a5efe643f58a5522c0ee002d5fe9b1b0c64818883102af2f1d09c72c71d90bbdccc81c9b81614431f3b0b142c5de1d64848fe8d31308e27f822d076ae35cdb62d108e0daf538a4f7d8c4c21b87a1bfe386089332d2ef82b42bada6a1840d872a40c9cb3427c0a2e33c3daed134cfd74d88b616dcf55a335a8dda4c134d137066105c99856e84f547e07af2ab823f491e95c3c9010d5c87ba23a176696d66910b054148d6d4391e9db4479b35ae90c8c733dcda18071318d20732cfb00cc06226601f5ef66b1000de8d0c76a0441c60e9139763fb1473623983144c382c5062f15e23188af5c643e54f16eaccebad26afca7e3ec1f33c5776f9ddaf510c7a29c21e901e8a620e27443ba2e83b3a4d3965f14b117563e39822883e9dd3eb3d1b07a81e243d5f9d1084da6900a2dad800a74be8eafbbf4c57f70f837050c1233abb64cea87da3a8abaa6ecaa6d32c99edbb59c84359c563dcca6245859b796de59e4072988b97a9dfe8003454e04954d8a03cf5943d7a058ef426a25bebcbd9d10e11754ead34ef970c53b05eb80823dae0f100b4126d4f824e5dc7d61be843a1b9a463f79d8abbe06aac1da6fbb20b632095164184f4cb0aa3070d18611f9dfb1b69f35c4c9db07709eba576e59d55dee8a8fc5e4c2ea4ba93e04b1cef1c071598f3623d578471edc968b9a681d9ee3c76f1d62a1ab09741113588c4dd57d0aa41cfb108f6ed0d3854b957fa23f6d3871dec0d58295456a39bca756fe610b5edab9d52df0f0277e12e72d09f6f73cdce4cc6283a9ea220b589554a2d081ddd357aba7a312bcf49c651bb5dabb25976f9caf3969764ce1c2e2f2c9d541efcc9cf7af6e163496b3c17f5f642ada12801b1d28c55c57379c96cc8d27e372e5a93f05ef895b162813863fdd30aefffd12de30eac45ed3b9f4057f2258b3b8bac98611681f16241a1878b3474884a5f11839e5104cf349576ca3affce13a25032219a8437dec7f18a44e34b22108c20af25c55f3c1cb751d77b28260b8f889c2bc6888383754d9e8e95bf413c9fdc5d38003375b11053496b06aa834f027f0d93463d9c283f590299a6cacdbcd862c73b18cb76012bf908aec0848b734359ce779a253127ed5c0b03defd412c4906e55adce5832cb78686d21db3a6b182b508707073996c16b9970387bd2d8a74c92c7dde788343f758c742e0bc9a6feb4d6d4c17d7976f3d6ce0db35ca2aa323eb0789be68628fba14e6bb7232f4f38706817984c53813680d3ae6638aac61977d53df2d521a273ff21a2dd2efa1273dc23b0ef108e28c492511bc0a75bc643868985c813f6e303feea81fdbed0712c2fae08fc1e596c2f7818252ed12780b5d8701bb84d0842d5c13b5e5cc39cb461234f5eccf90d49d98fdbb41a4ee05030b068192b57a20af30f2c195b2364a36466126c719324847d84dbd630f5ae442e6c6fbdf304fc4ad6acda7f0e7771ab1f8adec76c979b0cd0f8e6bfc6d12e6c83e1693a376f774f127e74b171086095090744da9e9d767862c3cfcba78e02ebc9984eae9b13d328a49affbe1fbc0d9238aea83ef28fd82938a08a3387df9559d1a742fc222d160529472c3c8baf4f20422e43a0566adc9b0741afcfe1fb59e3e6c07a89d0de0a70ed5e544e93dc7a7f6e93a2f424bebc307744636186177d612a2e72bbe8311c6a89a9fcf85c7f2306605a60ea731c6dd45ba96189bd4ccb36a2f1a81aea75c9dc46e7c2c5958f403e85709d2912428319c8259c117e462f1282bab85149af89115b0b4fd070a245b56c7cfd05858fc488630973a5c5e91fdc84e368fc16ae7069e15d60c1e62b13ee15032610919412b0ae4f620a7fd614711b252cbf393dc563d6b35784ac6c1e0762c14ff09851d909c8d312b5f754feed1adc5e396d275ab380a6e7282a49ce5a69ac9dfa92e11ee9dc45b78e3e8ef4ee1ff6319f46d71aa2e4e315a250e2dca7eee493891b3ed1a86c9f0705932f3af38b4bf105cea8ab7dea7bd02493c681267a0fbacb5b5fdcbeef61d95944cfc4c100be647467df6b1945e4782e593de431040b46f0cd807153c4b79138b92ca990a841af2f6ccae8af08ca7fa7440c43700d2dcf38ec07cfa03b7e2c4d26fa7302b2ca998ef6df30eaddab06bead9cc0efc1b6c8c57c28618e3fccdb1c8c035061fae70b276bbd501113b3ec83a36495ea3d5bf6f6ac096aadd7a31059c7d6c8f3654fbd6f1cc7f3b15743be6a0a1db1ac3e2b286931649b6a413589f65bf20c6c471ca86411e11633a40cb790b7df3d60ae23095c73259a1c6b3927faf27f12331970117f356aeb61860fb655a644744de70db96dfd930924d25441f1b1143190dd7c2e649f71237cce4151d599425ed84c33973c7171f0bd3110d7f8515d77650795279b32817cb23ab3514d874726241afef6b55582bd205985762bf0756528ac90d379688b4c5c21fe48836f2ea4bfac51d3f4f72e2b3d3f38b53ab1c57df60c6cca6cb0f5d33f21d78f88af1581b4d2b336bbec5abff1029c62d210ac100de593ced9ce8a9e0e560a9a21213d090b749c532220c3dd8255c2f05b4f5cac63d1d11f5ddb73572fe859b143e7fe81d48b71464317686443c91fea2467419bd4b860cd749240164c522580b6e205b93a2557e0337b06197aac0997d707ce65ec13453993e8792a4247207d9fe8b95834d4bba043df8ed086803e601ade89b16b0479e881483a405cc21c04efa982539a457d5cc582384454b7b9fe311ee7cf7c6ccf62826e2c27b3edbce4b05838a94145014a8f5bfe04b214c88ca76d325c653042f808e520ab71aa56df2305b79eac202704c3bd8f48da054ff240d08fe4373383d7f4e364ec2ffc864bd822472d22a9bd058191a48ae3cf5811479ba321eff9ac974ec36d02959c7e36d10a913c97c71ae59c274322b39dd799f38dd50a145a85be83fff04b0a93f19059d883c09d3d59cccafbad0549015d71e0ea98eb89071ed7ea376e72b38442e139990705255136d67f2e15b6b2ec8eb80f2c847a2bf4c29f61712d4b40c70632c4019e10badf07495bf6ecce1d08bd030754580775d44340aa2b6a35ff9fb7c6c8c96078e3e8b29461de08ddf778892939cbb589cba7d989c98c12f3ce53af9999c644366dbb9ec730e36934e94821f97420f112a9e336f36a350a673264e653f05a6205b9c50ff70081accaa737034f57f7549b96868e61b114ceb9dbe096d9c53636c5c4d600b79d75c30910bcda72fa4cb24626be473767be6bafceb186de9767509528ac8d48931802689cd92d2cc610466f23fa136793096518593c962869c38f9b82911680ab0d2d4628217ea4984f4975a90efbf9e4480b5d74fdb2611a0de28604e3a1632448c039a296e1ef70bb2bb8ad5d835a8148dcc8b71cedeac2107004d3d4ab9db0b30a004ffda01cdaa6563a48388fa40b358eec44875d8b9ad6ac5d2e8d86b4467d3bc7d4278ad77a284f250936e1e0fa7f0f86797c55c00cbca138127411a203f7ca121c48b46ad6455b99396d3c5ec12a49bf6dd7958ff072e00bf01141657af830c46d742b6d73e51d00a35a2f7ec33b8723268d85476e22db4c75ef3922ea74394b7937162246d092f5d9c45e97a83615325e7d3283d6fdc78f2fd44dc0242ac7ac59140eb7cad87aaf82f5f1e7cadd8fe070a1a63a0c8eeb48b3b55d9e38c83823020a23383a41c7a2c28d71772439c3114e7423c302df9c9c0190e1731ebf2e3a69e0eef43599576cd9a5560d4cb606831033b4d3726dbf6fa5eaba82fee0f5adf84d858f5e2202bb4a0215fc0f429aca9e112b1dab3011b7e7537d3b89b5de333ce8c7fc466311c88511aa70dbe3f181ed081c58e42cc019e924dff181fef80af6db421150d8897066d8d02b57cefa025d16f1826eca02ddfb8cf847f543c6b355fe3c980416c8d164b4972905b2844f4d4b328d9c8b7ff17b44df05e214e0c1df515c260ac3898e4e7ed4f21341d089d90ecadd4ff3acc151761a8641d99d13913178782fbbe2919913bc781a500a2f3ebce517e5836b17439e60111bf089c289cf9bc4281746ec9c516eb631f717f1b34fbdf55f18e86412a5cdcfad955f1f136436c34142a187f8440129283d67f2be18bc0f9ca80cf306fb1ac2be6644887bcd7b8fcd23f34dc9414e32851ba570439eec187d9e011dfe098cbbf6a3d6a1289b33699f82b88893f151b26b18046d6857b153c5c41ffdc61052b40210b118db8e970cf32c974f99a98ca48605aca7cd1edc8d78360c0eade6c536d458e145ce0cc14b04732890bc6eccf117b1930f7a2f090e5cfa40e1ca146b5207d13b89b8593337f8d10ebbb9e1542f9e358246d3454d008a92d7b17d998c710ca6580a234f9f352dcba144ebebc9933b44898e9f8342ef648afe2293cf9a52bc46764c0466d5c7618d547db3a5a3a8fb12add96a485de3bb490a142c1335f705fc75703058781939ce8cded80a47399360510f2e2fbd54892ea819263dd400e85fc0bcf1d8780187f00be6f29fe5f1c2f4992945b50a6f176fb8a821a4acb886b9d5b7eaf8eea552751bec2000c8e3a1b1a09882e75358dc0c8e62203c52d5549f72ba69754e6facb4bf1dd6987102fba2f4a1e7604dd04eee3b9d5eda969b024108b049550c69e608649c86e0d7bb3a0dee899d9af5867944695928a809350be3295fbddcc56c19b1b78e1013f3b0d021bd6074417a98c7dd513c639a79c75f349e641d24af0ff09c3e53f6d0bc751544054222aadece8d2dadb24127e5e3941b16646b50f144b500332a38eb7479c824ce8d26b3835aa00c9fb652eadd4f2b1108a40987fcf0ad5b52593282658c624cf457589b029cee78f17194af1490c23c2e772e32ca2c9e812a132a1b471ab0fd22aea39fc39f575aded353923e9388335ad6330d04f71a2d468b1ff9e9c68632d97e4e3637c58291fd10358665977b06dc6579c8e2bc4401a7850d33afbcd551825feea79dbb2a321986b9c58e7bfec629db6a4a58a3c43c4a71bfd4f2987fbde72f4a27f673eaf05c2f419d2bf2d118edc55b67e34841ab09c77aebee9ff3509d9acf9a8eff72eadee1e6cf0e68f18a0ff3d17082e2d120786e75511b137a4d496e663502924602d766d16c3c37d34e8566613e34ff83549103d0e36399b9804ba12d5d2011684ce37af3d4a209871565495cf20a73689615e465d02e23a12b5a917cb587c935b91263a431806f921d8787e13da159cfea74d06b8945a2f9619e6dc0722e08d9d596a17f62b5cfa1524843c33989b1073497c90f89197fbc874778228fa819612c4bab50b9ffb6c2fd7fd81110941cca2dbea75872925ab807524d31d91c9ce4b7b11cc13cdf52297a9a347b7b380c5ef86ae23df9cd5dea37a2c57d7b1fa557673a159de58e36962e1e9696d6e730410489461b19000ba53b07a65e34b4ddceb70e4936c3e555760cc3149f205023cea3a965e7e398ceccbc61bdd0791c5cf23a6a54e20d19bd8ee02268ec7a7aa97174e2f265188ee0598a083239bf483c4e769bef83e631e3db75fddb65109159da094e90d349394c32d75aec2885297db049624164a504cc7076e48ce74ed349e346c6e5e1546aba1aac02aa8f2c7b675e850a829dc6c25175ca633d77a62dbb2ca7558570fe062b7f27169cc7df0a897bfb4f599630c81778c013863b4477b52c3aa9413bb7ef7bf02dea5281c01f191a635b2517b385ff15a379a9c820ccb219e924196ff64073aaad6fa11b9cfa379868775c54566bc40d8b9a5c28cc3c82a658da89d2684607b276b368d3bf72b5e14032f0ee1e1947af21117219311703379a7c0fa5c95be5eb350d56605ce90415365156699c425cb2f2afca9ac98940f656e394b2799418b7adf7a29904b2da26bfa50f7d111e553c8c5a3a510744c1c032d58614021da5a1a26fe837c67617037a0bfef0480e671a70b91cfd9396fc04c92397a84459acb1e671e287c8a6736d3fc53c00501fa2256b6a487d4697b88a11f93824b8974f1e7910ad59fbf88064dd9518be1eb572957a294a4c310909866b42069f8003f79b03024660ebe02342a137facd32d52a0e81c924f6fd2fa5a3c7336dbc2c19feb3e38a382a90f9e2a366610c76864db64ef838138fa59bc6191708b83487c2ca15fab07ee79329d9a5c3a7acd49b2981197fb5c38cc0486a49762b5ff098e5746681b3890f46e9544c29342c246f0d20716159b5c366f1267af8b18560a95fe61aed15218b55a9b3126a918cdae46dd116c645699b89a7cf6995102a72d57b12ccee103d616572dcde604679662444e3082640edfd5c292367c6e61c1037f32b88b0c51240a51f132df15698c3ec26c3ed1b819b4a998b78b54d3e6f75eb5c9911e2aa1f3da0c996cc31c770625b58c45fba9c2940f48667be7af9a72ac595bcc732a0c25c8c9b3fea741dee592b0c3895161baf3bce29dda8b4b7c1af5f981456625ec17f5e4a974e3111e5fff235fdfe8d44055d908ef0bffdbb97f7f514e9ff9251bd88c9bc4f08e0e213e6c035b2c4a9301bbdfa9abfcf044ecbadc5c7292a148144478605feec7cb6329ee25414c85426018e5d79803de39aa3fe7315d2c04f9aebe6c3c32f69ddf406c2c8fd1acf56568762bc0949973bf2c41c4eeb35465b34dd5b09d375d1a6b05793a1a147f28204bb1d28213d545fb33acb3c34c4667d8a81cf9e4e360941d396681ce8c5210314c616b94a0a6035014a00e1ce52c69af5bb82ae2020dd880b24e690a3a7c2ce08ad6565db958a4608a95a21109a54c2985786c202affd1e140b3bb7d6dbd24b6cb3912b8d643f11fcc53572135d09036502a414c51298174e1817268a006ccd74a02d81d118b44013cd98e7af2cf1fcaea9b0e1ef31f989ee05358f5e9b5998d2939524cdfbc63ab9ced220d9b6d2301659d35184e825cedae38139c402408e4673ef66c668004afa14eb33369ede0ccc18f153888ebd8b167fbaf990534be49033142bf1be03db4799a40886acf406fe682fb6e047e3d948f3c89b8fc48d6164aa1108e13b345f9510c8b68b96295135c0dba11c18976ba6e764c881d48c8149d8bb1e51fb0a3c1656b9cd720196ccde00c4857f9ee378b82215688d85c019475f7863b50a3febbcc7f10667dcc7f593b905120090c1b76fad50f9813237b9f38882b51824449cc8d2e2bc0b4aa4f805d31dc62b420e64121ee5f8990c678bbff24f3160f4b64154894eafa214e2878e6468e167e1a816a953a71c2a9eeb0b53281760885f1826ec500c3a5b84a453615cbd9b225ee7b9a3b51be4bfc43fc17d47fa044f58b7e90a9e26e9c945a280ac972ce00dabfb718f2d50d4034d0b87df03d665ec2d9a3ec54d00ca7632e8e6ea88d6212bcd07ed5457211974fd042010ae700348d953289941014e3a046ac3031ff75bb851254f5c44cda346982ae981c75da8176c5c2afe825fa33757429ebec1abe851d3a8ae8313f678019afe831854dd45525121480118bc06fa5e76b29289cdcb57bf6ad6eb09ce2a916579a12593dc9d964c8c1a30f14ffa27be15b4fdc686cb6923c59237fe32271cdbbf1acae07b317678bd369982afd812fc0226cb059a591b0d0e24925c4029870d233630a81f9b1a2fe5b0e105fca1fdda8cf4b137a297c92c4262acbde5323c82332129898f981b7d7580083b9e8bca39c22c56ef56537f9675da5255c4c24061083205c1ba0b6041c8c02227b43164a3235d1ed83d23aa2067c07600c94d66c858d2524356d2ee101670772ac7bdbe40f32bd27e5be0260de9b2e0d549fb0273c31bfa364cfe80456dd33541b223d65a37c69749dec46bcf157b6615c4b7e1318821949d2784a3733eae44e3932ca937487f3c976f0ed89f1456e25870c00313bf1d18d518ee99f6144d8a4aecb18d2199653c42d11d10fa611b0797d773dd7c1155ba9a6808e67093c1721b3b2e23b880100f5d96844b3be8de9270019d096394ec3bc40d4a7312006cfd1e77800888a7f6def898125a4e9cb8a098634a847b0a3038b3523df73f5a9f29e28ecc052ea6159f8aaa41b72a9a35394406262ce10b094b08020186097660a77616e3ed618a3ef7a0c9438b7caa43139284c99201c9650b213635a2f39c0620d45f0f173c8e016c1031f1cf41bc0439af6f12bf23e1b5c6c2ad7be73a570a5c735d727caf37d58a3d478a13388bc06441e99cfc16b37666392edd5ccc911df55c5f15dd5deb75c4ca5de157251304ce00055b24055d05e710eaa9bd61c476ccb9eec149fe7045539ac5f79e3f47ab59ab281d47de19efd78410c2e19a4cb55d22da7604d1b81e125499b0c53d82263b80f91f28bff8346bd897ad9f144de3bed3b58a635d3a242cfb2cfc13dfc958d47a63e67ef0d60d8d5d261a390648ac13674ff2a860d7c86869c80cd3641a5748705dc1169eb01a6a18b8a1960e28cdb9360ff4017b6e02eb27de06aab63ac14e15eaf4c3875bf6060bee5f812c6edb6b08228a37274e1cfd11862dda991e8f20ab91eb786c9700ef4b4ce462683058713017dcb48869dae428fcebc434c1a03a44ddc9e86126a2596a9dbced4bce9dd8c3f775f08c9690ab6a0be60ac964325c5a13294bf9c1349ccb60c05e44fce93aa3b64c617b7b0cd227ed4b613d956522b23694ea7e66604f502bc2ef05b87eff9640070eeb2bc203ea254cba5d6c55e0425e7822ea8d8cf002114b48262b3893a21078d96afd37091e34b1cc7fa1677a20c1233e1520140fa5cc3bb4d6cf82a0b96277a642691575c34ef1c13e25076bd2faf8b210e7d4b2b7cdb455306327214626e6155a2d7a7abc37f0629442db112f8bd779af10427cde6b5c74680a4cda0cf7c92cdce7d08a884efd5432254cd688b2d05474f1168970825c841728d7cf2c33cdc6c7ea10572630e0246396548f3d959ea88b320ae2161afe744a2567309113e7a3a16aae0c118008d158c61bdabe5cc023ffbdb1c267b6c34f0eb13a2fe65c452e91f739b73768b999fe900047ced5bbd2b2127269e851944f1de6d052a108292aa450bff0da3b0e102c7ef792f259aee0202bbbbbec930c91dc826ba6bf4938d02942c47a63ab3a705e67983a4bdc1878b6a10404217cf30a36151f3a9a75d06f55d5fe101c0cdb305823dea48a259501704d6ad60c37e9b409a4d5ac816e08655efb4daf4b185418ff3ec3e2fcad69920574a2de15937f3b35d85c2065a8e50814c53b750976f577e5d41774fb9a0f8bd19fa1e631b90c16a4fc8c3889910c40a149133635623f84579dcac91e72cce2d56196b6d7bd777da7405cb2f6f569121f05226bb823a6ef4ae47c7451dc77c540b85dca09654b8209b7e5db194ad4518d9af62b571a26b3eb4d1f559d9111d8fbe8e51538e011da3771b021d94932edcb679fc7fce2b4fdaaf40fd96bb5f5fcb431b44b53bd822e72cb6be983f51c9f03730d5c92b20dd7a3714033d703f875322435c0a9c039ca9ff8127f2f5199abee8b0569e5833ebfad26347db1f1ce837b58bcd723507e063b18bcbf2f00ff4e811adaa5b35abacd9c2075aba9820d5a03f9582cf06ca729569bd55e2c48f67ca95b657d20c58045e791d1f3ebb4ab41d93b890d140e0530ba65cea4d29cbe8752c432ab734da0f85346694454dbdbf7e10c56695e39a3837e50dd0b3440371c3eef9fbe33d2ab4031f1b2d8149a8973fed5884454be9b4d1136ea1ec83b45b438fbd93ffad7d7c8ed61f649c4acd40954db24794c31cbe9cb6234bfaed428a0155b2fb2c362517be867d5e399a8ad3ce8feda09e8d5768c0e9e09589124afeeca5722fb4266639465b81ce80af28b4b58033432f1fa3c2ffb482460fa61690109cefce2687275a7cfccf3d3b449f1009bef80fd4464e57990d825bab7ec8855a5d278e4567756dc3ea1c0e73b66948ed77c13cf887253575f9963285137cffc75f544cbf21cb8ea633fb6b4cda6cff999ec4a5d12129a1e5c6b7f917c9cdb5dbbabf7481bcd4f6e05aeb0824a80c379c6ebc039487e45657d651d2d59c41f2dcdc160c10910fff9851b34a63fb6f55fd9922d1c9aeeb98fefbd5d144d96d9646f62493d879f25f53a48a89d28f13abf0f4daab1e3386b41ce93f0da146185caf3c60b9723de2627258e58a34841a1b621bcc0e80dad4ada7a25b0f515c10ac3c109398af9f179acd8ab9b2a946bf49be5a782a1766f56f81670a7310d016432df99e45acafa09026f108749274b9ffe8eb173b76cbeefcac50899e494245f9a07caa561c3b6c4d9656e7e8554b945f9863d5892d7597ae8f5ba4a4fdbb5c97d9ba6d681bd49014eb1c935d703977959e95d71dca470e9b2a85e218b1a2ed86815191a2f1595a2b3762f90f95fa941edeb88621225677c1aaf153b8302309ff239055c6974984afc6de1fd93fd799e1cf3a96c2b837fdbb02b29066d643969696487ac0ed178ea08a81e2b3eb620290342e94052458dc79e9152564cd205dc101893aca719fe7103d43e61e6b6039e2ba72c9ac6f450ef834782d94e4f6ab2fe6dc251c655bdb5dcffa3e0b624cea834489975e7e15b9dbe0f5bad450a120c430969421fa3086501c90340e30bec0dab7222bffd3b2da633ce276737a360489c1cd77bd0091350005876eaba9b6e0d79079358a711a3eed86851cebf1c6cd9107effeb2d4e2d9ede96613298b5e1aa1c4ba01fab933db48a23d0747e828c63a0e12985ee0c58ed50795cedb045dea7aa7f15638172ed5a452f930007b74bad33a6305be0bd4be380790fe3ee9ef89fa550592e47d1d5fd27fe43b75f57dd12f8f2157c961defc1fe3e0c002a450ec2c005133bf9cd0cbb49d956de7ecb702556d308b9173bbce216ccfce180d174c5a58ae95aa9c984a11360d90bb8d744206942f986f7a92c053a1004445eeb936bc401ba97153270c140119944e7bc604d0c33867320142398f2d8257f5cba7a35963412cc60b7439e0786dd1535ced3fcadbede4131eabe34535d3e811a6ab726a67e38765c8caadf35e30625375067d0fc0d4ee43d8fe045cca161639ffaa29b8de8130f4ed4e64b6ecd62065a3242f17cac45aa1c250255a20e0588959fae4ee6ba48fbf7c1a1b8c9f398059c217ccaa711643a810ac857219477a8a9aa1e38c0e86cf0a419f68ff0ee4d324906d658e31303c456d3e334792bd0f31be40eeebe8aae58de6ece783a4584b5ddbda73c55a79ef9e1a638f22def020f287b2a63c4dc99ac367079029cba1d25834f59b183085b51a12533189140f6062aa1baf291ba9e3fe711a75d71f2cb61d5c06b6893403e09ca558a9773802115633a903e43b937a62532766617d1f78c8be23680e3c27a66224c639f57118bceeba7c26c1b8d9e53e50af3933f23e5a134a4d938869ea5313496a1b87333fcf9098c01707ec3d17d80fb38ef72f30a72096f5815c3d7df439e272426ef95b3cbd2d2decb798017d072a15bf1892c6627056d84a4f26803165ee1f5200a16765eac1d124cb3350c96ee774af212dfdbcf02c5e08cdc5a92f5a65535938b3edf545c283f2d8125a5bdcf93f3b145629a5afe1fd1c1e590b66653f86681a901169ce1aa1ea55849b7b368ce9d09dd6d6dc1539259f7a4bf7cfd530aa9061907aac35420896a76ec637c1f179c67b49c94609f8365f4855994c97df1c5f5fbf09e582722a5d854b846cd14b2a8616e27ac935a225bb60372844e5cbba6bc759fc83f62711064291eaec90223b442e1fc4622c95e26cefe464cb57ee50d9e27a19353081bdd1e85eaafd9302c1af5f5e56db81d69bfa8d51488bad37b20bafbf48beca2a6adc8c0b59d2dc4cbc683101fd150e20f121ef9b81083a29ca3f6bce7e03b8be5919861e9f01cced75708d546c3af552d8a6eba0d35353ceebb45af7413c5a64f44973bac1037cb908a41452ec8052b66d73ae67190b182ff368733dc7e322874fcfda2f062024c626731ca669c69baadfd028e23fb57c868321677be59499cfc2c1bdf7f3290e60b55c2ec63908b4686cee8f4b38e62ae65c9a9b8c0be15ae95a86241d52cc64ba671df36427446086dc17af12305d0377acfd1da793f9041484ee04c4f84f9754b32edd4a80649c9bc131cabd53cb17437920ed2226593786baabc0af4286466b3ee0311bf0cdcf8ba6e2d0d98726110ae81657156e91d3dd14d274fc8a0e8a69eea26cbdc2d7034625ca615e4ec2c81b4845a347b5aad87c1e21bd1af7071ee05dd9849b002aa50024417e04cd10dcedb430eab0dcb6c23aab2058eb88ccfc60037515ec56036996b84b62825878a78b4e0f2dfec4756cb514516d09b8b7aa3e777fdb2738e095bd21179661b992cdc99e0d7ade0245a39649d2da4de4b7f48ba18fe54222ee67acca9c150e532c7d94434f581d92927cd783cd8fcef8287103820b280e0123bbaaf0257af85086718eb6ebbe628f06815a0a1389814b424b91d22206fc87b01ccd1f824c75f47c0aa115392f42ea8383d0c6943e5a1d84356aebbf71caa3c9af4e6017e89735c71b491a7937307422da745e7660ec09007b628790d372ecfae0d6651c16e3647eec9c8c59275dfa8192ab13456ebc9a098ea28699569d038bca852f25e0657021a808d3f38806551c7d178527dc2bcfba59314a6e4f71caee12c169bff636a4b0fef307939e545a127d758e6297b6b02e9a78dfa9d679974eb0f24c332feab9e0e48b64416a092525dcbc55055e55c795dd19b0916ad78c7deb9adf8ba370113fa4ca839024d7e4dcebae8055e6053e3618b10e96bb9d07f1541b36c3b722eb9a5c604671831a48333da4cb5a6afc0e96f30cf5dff04f4fe82148408ee77b99efb7745059b819d49de61f90a2fe264376626c18c7b5b2eae61c234575d87fc8c996b87069e99a6ecbd6ec5bd693dedcd69eabe04da76f4c88d698a2e34287d2f742b94b920b4b192aca0792ab7ca36d8bfb84bb7d4360fd2de21b83fb35b7c0e908b2af9158631f5da2e0799079e0eb68c6f06cef702057301e09eb75320463de4253a62e163f2749e931ad6b8215ca5052c67c57dce6d91eb2d0e4fd7563c4cac58eb773e7c3073c83161045a93550b0f9364edc958d425005410b2eb81682cf31d00cb2fd39dc13e1b1b304685d53ee66153eb34505effd9cf3413d134d39c1ce68f8d057f77120df7be2f8cea61902c7b53819247941a162401b750967ca9d01bab8561d1b70234c39224fa7991a1bc48c1b39a077fd3644e6b13691a34119461698722c9148eeeb387f2f4ffecdc2f039475e56e42f5731145eda830355ab998f0bc698af82fe3df03390690dfdd77dca45efe481cfffbc4155fd8450d8a7378908631ae6cce4aac8828675655bc9459d4dbab533b30d55ff2d8444cdf4bfc79b62c9b444a49123884ae1f4e800ae0adf2e087c8c77e095a8156247203299fde0ff0186cb793d21df2494f3b4d5763eb78dc53ab2da1ed626c3f229813d7d867e9de09eb0bde1616df1c45e2beeb42bc3e2284d2e520631f16d4dfe325eabc0de7daf4ede2ea928d72a85015ed17e4973c72667476beb03b391c50009c434ff9c5f14a351c306af26cee6c1ed227a74c9a4c5b814c9d72041727fb1b01592d7db2ceaa8fae62633eeb17979edf860ce7956790de2a56ee828f8a61cc858338526ebe4c2d3d4baf1d42feac37c12df00c7b51b2b9b6ce2ea669ac5c4a14f3a3370a699cea937c8ec342b95dcb34e0e4a796737a9b266f4941efa455c5d29e0139292d2f4fee9d00158f7a9bda123a1f2ec646d0cf95500ece29f2043c4fb36fda2f14a7d8e1d16d057f8cc7ff5468ca331c458963a8b0bd443815df07afe684d93c873df63f16529ed204b12735946395701139e48310b5e75945b4178346f7ad1525fac62180f1937c067959f6e604494a89032925b13ba47ba16b4d0a7cb57ad07692338b7f594c666a09e5d4e5f5abe4f7faf7e427e93eaab110ffab9270cc36343e6f21987ac8d02c33ec16842f585ef211d086bb49bdff3b13aab3503a09422b08585130ce02ef883f5d21818109f73959d9d43eabd319c92e01e73889beb32321869c3b3ca16962378d27b7b845b72a0870be22666e6f7d02fee0ba03cc70bee45824cf004b778d10120c318c11a90d5f8ae9f4c8902bdb6ae428b57847f21e6957c6c79f0fed966e8519f782cfa3d6ecfec6ddd771e8a716cb63b0f5b2db5ef417aff39d327ddbd2cbded5fc7f40973999efce6e31d9463b23903c204ae86f56564a0eb6475f651287cd71dde4d8af5fdc830a2948a249b35ec8587d174e9f630b36beb3458574b6a0e8ffb12c3cdebe329d0ddb5df31a339082def9134ac9c3f102676737ecc759bc90df35a213125dfc7c156e5d3c3be2c45c196c55838e6602d5429ab119335b02ed15198b56d1acd8149a15b829b331f4764d7175b50547a86f11746f59afc13b8aa2a0abfe0e51fe2a15fe5c69abcd45b5ca7dd1f6f21c30e199fe7489cf087b7d7bfd8a4f6cb89e6ca0a2c191c48cc074c493838be0d69416743ec3173ab92ee9be624221ebd098dcd3fc9307545cd7467d9eb0827e39ce427f79f493e7eb881202ebbfb5640b7a7761556e74f069c5127a213d039136d8242dcfafc427b402a4120d3b3098ebd8c991523a4961b0f91bd2691d13d8159accdaa4623a21e2ff184ace783e200e4c916e627f768eab0d204580e3e1ed662090a99f81f4dcca06a7dcc83461533e46406e015d5ba8cdd94aea4cc10538b3ae5e5181dcd4b7af4cd7705a72bd644ebe3150f4df1631804d07819bb5df3146f9365d4079a9b78d9718dad7cb7695cc1a80110a0e9ce4f087607873b5bd2d40b308f5c99ad9dbdad7c4dfd91a3aa6e906236c24c2481376f08b3d97f4abdab5f00b7db802b2338699b0de8bb3b217a731745c9038fa4f810f0517096c58368d7814093cdd6e54191fc09d4d0c4ecd726e692d643df474f3baefee869f145d93ba19820ce7d65e7be53b1187f8d3e9db991515369e14abf91beac0aec23cfce0c89eab2906891989cab6823064637251544c008f41a28c53567d361c4f39774fed8e631aacb5aaa7543a380370e675681fc01eb88ce66824d122c45369c8ab4064154e846c2651c311a6c07d5cfb3e177b6b2712b6b4638f1d1d4ac894c652253a23bfe8db3f2aa50db09848413dcfc06bdcfd4c9ca688f6706bd2cf7f034374d6e7cf5d68dad450ef9d3e801766f2296dff7df40d7889553dcd6d195afc06fe720924b01e39a6e768e9f95303cfdd39291ff386824e3cba0cb8dc5ca77642f7258a99cea563139f1640cb42897fa9c7ca1f9e578f64b1862b117aeb552c2e0a3bb1cf7b3e49a72f66d75a27c132488d1a0caf4e70f26abdfcc3a2034cd2a209867fa317b3ecc065b7cb0a9bcfba42c6d613338a06949df697171a92ceaf572314289cb14f92d2db49a8590d4d2760444c15d01b1d78571a30ffb65466400947514d24a07cc5c01479ed7c3b9f234de2dd707b04c6176d2de0dad20234ea8a1731fb5b53e31ec4e9627cdedbad6da0dacc62c8374785f11b64ef420acc62441b99280d971810ba0e1fb11ec697aa2fb7251175ff83d53714b7529c8760e688f853391f9998a4ece3e16709d897a45c675b0101d127ef2c9b036c723b524f4fad50d767a279f019a54285fa648ede6a2814670ad29e482b56af3632fdaa88d08609aa8a5491ce4f07d80429d24084c35478c538ebcd759e22ea50b214671007a0d03c52cbecf183c13824b1f42889c3fc01216fa2493fed234b2e883156eb1d3ab4b0f9d33a57ddc4e7f8e50796e3fa60e295183da3cc2e7377494fea9b8c40fe0b938b4ee1810d861f8de481e58ef5bbce7e637c1e2d5700c9f9bf438238ba9a476290cc51f53beea11a4ebab5b8d3bb4dc0c10433dc90b5a1067d634e8646a68992804522a915b4b2bc41bca4bf4e8956a2129e9b88c8ed1b77964255d02e45cf040421b655a0127064f8a9dac384c220ed4a1e13d1cb284f1d8a22ac0461c32080eda69dbc0328e4c0160bdbe9f485a299e18e3aaf8db2b2d5b64a094e9f62f6c671c321b1b510c8e8ac2c4f52c5d39cb1268ac20590f0e236e4295360b4063dd6e43983d9c18311778ac99b95f7597b0085ca831b57c0a4488076dbc47f8e06037ddd6efcda412cb0aa25625299914495f86649faf7f61260479fb565cf8c9585656886dfc6c8b0a92098320e8265c42d8a4ca9ed8d38a5267883f07808e30106bda6eba82885559018e21bd99ed90307a0edc65e45f0ec8c9b113dd0cfa2c32816a1294add8b448b12aa50c879ed4be94740f8cccfdc5c5938e6fd0717cc23a4e742726ffe908499679720da89e37c6623c4123b68045b586bd2be2bf8bce758303ca4f71348c45cbd9c2b5078db867d3a6f2900ad80a89272d38c5d40459e69c9c01b9c203237af3f2b36053cdf67b482116051d1ed31607407bd0eb79ed6b733272d35e05183b8835722af6754867a193f7b23daa04b02acf4119d8175048507a7ba725a830c9eb4b566a863711a11b6c80b42190f40f81adad56ab767f2c5be39c79c7df555481572430ba708c622cee7ff94713ac5d33b1f898be15df5b7705a1416fb4d7a19d298372f79e0df22146fef6f285cc1ea68becf6748f08bddea789170cdb4e052037e4d585bdd85fddcb2ccbf1c6a63f6b04368d016fbad9f4728ffa3f1d3ff9ef0e0ef8e1ebf1cc130513a57d0854c18042bce9c8f3a1b53b27bf493cad8ed4258ed194a93a30ba67c5687f06bd39c52937a340260b90cf6b258d97ef402c1883839ef30ce83a2922aac1f118485577a78c7e4f66e75e9f2e8b6b525adbcb5bbd3658cb702737a2101d5250c5fb99621d3cb8d2ac8a826690f81bb453d340416755a39261531ae374d6b1e6b693bf3ae4c3aa161586166184ea50422e23c188bebdc3b30942a021ee9764529add7912839732668ae1b700d79994aa20660534716707c9bccbca9e510525dae1d3207f0c4a84bf971f5265e4bba1781b9acb8f12df03fba6683afe245e5bc7a33f7b46fcfd085a6e9a09a3c9bd60dd5d881c095eaac14479ee981e7c1aeb0b7567a9f61f8d2a24145e1d34d4794edbf39117bdf429e0e07d4386b577a8379f8a330ec16913eebc809738536dce35a780acf2318e14ed845142d2bc67d2b9e1ac403f53c7775ad7c0d15213cbca5989df9906537dd78a4e7a599fcb22e1c4300032ccc72be66b31949096cc470ea90ab2abf414f142b1097714e47eb742c129806be3d6eb05d9072ac37595c833f84db02ae2766fd8451e5a250f386e1b51326c50b4611cb68999020282fc5b3896757c125ee5810fc3d889fc2882beeabdebc7a78ebaa92e1909d079bdd105ae11ee6e9c9ab3ded24e56543e246cfc647bd081c7edd7600b77fb62b5e304fa09319334e0ed8db70205c1d0872efd22c5494dcc9fe74a9aa1848cce786110a9381464b5ddf20e84c3a7851613298a5b2c4b8e997380cf7e9f8a40923e61ec70d26764dc62c2e0833ca9be0c1d9fcfeabb8c96161c828c6644ed458e7924a02bbcb00e61f87dc839aaeff0a84ab094651a0274389d129db13a4c62c663a7678b8bc7ea17ae07d691b8e43e6d82c70d49b1dadfecb665c818ab769107f91886628064ee75cc28e86a0def5b66aca7b623954d6c5145f72da091b3b5fc9b61a1fc1944fa850b230cc7010f5b31e12848444f2bf988ee071a17beed9f3b000f43f354e5ba2c0b79f7d96768914ec189ffa1ffcc3453a32e2984361261032acc306b897aa18a2e9e19662516a9a80c73bb1915b62d8c9d25e299e9405c8ceddfa8c3f5675040d3d3b6034f512934edfc14fd7b495d5636676bf14023768461ebc99fb7e4f3e0da381169520b8d5bca460afd18ecefe5a621522a52dcb687dbcc40edb90a2b4e09a9ade98d60f8894e06b9d01bf8cc9f9467bf05471790339c181d7896eabba13df40e4aceca1d124da1c4e6d95efe12fa32611d0dc15c280b18c4450763cfa490a221f93522f35d8ba43619870c64c36ca0435bf68f04282d686bcaf953f5d9a93b287b3b7a625ed8bcee81492fd412a187dd6c3702404ba8166bcc6d54aff670d201aa46a2df27c191632ed0eae96c633175a1820e41f26c8a9f1f18d9e6a91a86a6be6ce42f93f1480e5e4617858c11abfcdd7038e33d62bcd5df068413bf56879cf4c366f47caae116892d45cbf0805e7e92e110e36b241ee83c98b084bc6be9bdde1cd5173b6e8db2e54e00b6830203bb20d771e133a87c546f4c657eb7a0d278df54985850ff0de9fc148f3c2a5643ff68407eb017cbfc9a3632083ea7f14be7c967cc042114bf2e46b628adb0222444693e5568f893ee6f5dbf7011c671af7210e90cba1d7004bfa99d30f67f6a058e179a9393faa15f50952963fb8dc62199d6dc8d5c2e079e76ecd514d65244e4db244203ae984c05a9725f088cab364be0ce45e7f5b37b87af8457a03f3050dd95617d59f8d176db18f20c2894ce5f51ebcbd7770bd68970184a11e00113b34d40c0181c4f1eb8420d859f0c620340ade5ce6ebda57319cfe813f13827467cc8a4ac527b81ef5b4313b2ee8364d92228318a3693aa4b0d38147b957e6f25d4527a14ed12264bc6385a239e33d1d768e20badebe75d9896e63c9d4123e66b6220b45b6012cd2dc49a8c63dfabd4df22b25f58149b61743552648838c78a911e2ae752d4e8d9d88d6007a98869e2363227800547868d070e09c404879cbdbf55df50ff823ee5615aea98bcf2ecbbf770b996a55f0a37933b5ad5c6df28a5d83b7a87e91d3a369a1edfc96aaa3f8e914ba1beaf2625740082cfec46d052135a477f2522544172da1623f1b0ebab5d2e33c4425535dc43300e2258d1cf6f28b1c5971a44d7a1aa825dfebc33815c75a20198fc59dc45f9c16d97bb4473373be6f2eed6a04ff6d4ed2d238ab5d6a16bcd29dae486c2de864bee0c3faf42c4a81a4147b9ce1db21dfa4a98ab9877b81de620ffaac73ec1877e75dca2f52421266f8dd500cb02bdbc6c88b15ca3ef78831cd04a8ec8ccab0e63c6e7c30633e9a5aa1c9f1a3af1281d05abd1f8d286cd594dfa7782dcd6fbba134275dc56cc4e35c36a3db454a26733c69614949db812863fe5ed019e0dd829895b3c5184b71159a35ccbd7527b50b5a64e757be956e900eeb6e17ed5b12cb21e4e4c23ce32390d27d17b40f822173f0e032292c37d8540fae6401170d1a97e734e9599124366719f08a35e5194ca0fc01d2e7ad5efe7ed769179e9d5a03f9a3a79361b35add8738387364f2bf78291c2819ccd1fcd769c02ccc30c5e2a578081c2af18c18e0ecf65150a0bfaf853a2ac3d13b9441fee994d616c6b203c267088c337f3c54d909eebd7a1a88b4d9963ea165822044a8a168b21340629b28494314fb85e27f55c7130b0d671888cb6b0e94c597731feae0be76a476bf05698ac530f32e9e870cdef94bc9ef5943fe97c6b3b945708683384d5b0a619d1bb1f55df3a0cde26276649e06a9ba3be91d3e18b98994cfced4f8aa65373dcf63310c2a9a758c19af66c921092ba0fd0b9fc2bbce37e9cc4ad98cc29a406c3f897813e88056670651b391289d5d363a1aaef48e5589174c83803e967040e8db930f21246083404ad15793a18a1a531042ad9a8df7d062ce466219fc7b11c5e5af96536bc5a19fd384997140d2a353a2df214684542c6403dda68d3df1d2fba0ae17b63403dd06fff7a0b70b5fcb55a0a3264560ddedd051d4aa42024e5f2dfa000ab140240e242a49031f17c36f2a40b3c9162800bff29907f8501b652ecfecc94c0c089c769a7af6606e98bb877e71076ccc5b9920a9898399511eb553a608d791bfaa903a3ab993715147d8dc8d1b30c3b184a5524f79d2504928621897966861477cc6591868814924e1eaa2eaffeafb12322161178e24d20d355f1e0cfa2e07c00a0ad92d1092d91e573defdee1f3aa2ae7653fed245db5f6b2e3fc2fa45618813f155fcd13937a1997e2800d2e6f2882fa9903c720f51ce1413dc4eedc4db119d406899dbc6f5904972254ee9ac813a4563c353e832a193530e0e3f047bb271a2157494d0fe6ebcb344e78eb85e8ae750c919041e55cb09fd42fd6f4e6946301d9cd7563d79cd60df6f716405351dd75874c85d646facfcdbde084327f883b36a82ae80ebdef8a4ac812d3ec70e287acf340e87d584acc809ea60a9552132c67fddef9bd398b9d7fe8b739bed1546c372476e781b17c220fb05fd578be04f886b16294730c410e5c9110294c4d6e08ce4444f59130c6d5050bb1f660ab1986455049c8b330570f812014b4b8387c34e98857a4c8da8a0acc3ba44b0cef081232b93fa9c900bd5237b16c79f7bada4418b69bfdf9f0358a1ab2a9c726206ab5712a755556ad0e99f92be4393f1f42068a78d93b55d556a0e489c1755f83d4a789840862a46e79bdd1d29b9e4712583415d30db571dd3b9e3e9c019f64aa131d26435d794001e04a4679e62b14aa57f8ae3d041914a803d9793500e6c6a29f852c51ecbc4ac8f1e65001869e15476bb547e3c9f414b2fb96444d6fc830048429c5d509ceae3433062b08f8f9f82df7b66940e38af7a0e27717e2f2eb7e514dd280492ee8e65cfb3064f67cfdc00ae06ad23e00ce54282d29d1f13270de99dcfdfc0202bf0a56aa2aeb650aa6f64958d5e6d22551ba8ea03bdca40afb6e8ab37caea85a47ad0546f04a8bed62c6c80ff4a526d47085340e2c02715c04af6c6eacdc802e30a7e4ce369d493dd7a32bb98ccaa243b85647645b25d4eb68b93cdb264a3967c54271fe5c9b638b95d4c7615929942b2bb90fcae93db7594ecfe095d428dd7ea2242e37ccd1c56f1921ff754142c3266bf6f59d7b067bf5244b978b28f391b41cc601c48d8cf8bafc6d21462e7407c5c4a2184b7bce0d5cdb69e7fd27918cae6545ee69795670a89a44c492e38ca3c17f7ca915b56b8a86fa5b5a825faa7bc600856df80a00b620f94d7d456df655a26c55e362ec3cbdd368731ea7c30558e1155a7c3fcba86ab3d487a9f011e6af08fed1ff297a9e6b75d8b14bc465480d85908722534c84fde1d2147c921e5ea3f40cc1063717a4548fae758c15723c5eb2fa03226a9c34110279cc056a2c3da69fdc75d0bd382a1b2015ba3d4df3028641c9da4cab8eff65dfe73394d3c751f20da4d49aef189dd5ce0dfb779a9c1ce6868ed9a65f5fdc62b24930126adf0ad70f471b2280eb156f67bb347c0aa945761cc57a701fbfb14c43463c430104a6e3c16c45c84754c4e1dff2968dfd5669ccca98d5d63bbe3e1726ad79857c1e75483e1e2964cabe2ff0f8bc3e4769f957a567ee5fcf3fb99d7a18c5189fa6e0b28f904b8b10d7b54c73e7de9aad54e9ee72c19d1ae1bfb630015b9e82313fd750c26756d41db8bd8691872151026936c112167dad0487a7af3cc81f517214e901686f6bd3e301b0eedeb547a6a2da8f21919a01f654689dfd0229a23ce1190958090dd0f71ced0324cf597b9bfad087702a5f0a4ea942ddab1b4139a264fcf697bc880739eef43f318d8f2885a05bdaa362e120cc3047599fbb458f36d78b5c0b95629ff1cdd736e84c453682ddb312a58a19959e5254c500495a1e656ef8f7b5fc61138d42fcdc5be450d075ea82e078d393b2e21908443a2011eca66ac060dd7a2f6a7a95022759b8cbba16f97eae15992d69bfed3ab9828536b8c5984f920c8d5ae6e83ca5c33063c88d1403bee74531ad8ec75a6833b77e40c8ca9b629f4cd5cb226ee019d41fea7d244efba3d296728a2991881e2c1a5462e9fdcea98f192b5694375c6017f29e45f5bc6ae8443761f21f440b9eb7d6c752f59c6928bd4ab4b94a3438cdf890b64f3ed517f4e507f8180bd3bd67875a92cb15c8e93cb7475b18160efd31847ee79a6cba1072cc91c977a0c83139f88b7a92e45c01d3215ea19ddb5b210bd4bf3687501f8493989c8d193c7c62de95b87b20c6d39a5278a347c6876d91bb999b48d1bb8b9c5dd824ce2b64c734be5e1fed16b09e24c6d98db290dd217d0b36b8bdc2c148ee0dcc964a79b3497030572f2e8caca5432eccae7907c497ab13b92a1acc6f66849a8e9bd78a65528569f3dea4e5a85e36971945f2c36526c69e2e0cd39a77f19619e3dfca56a13e03eac961b9e1e8625aaffe737abe722ec603b37d790bfd494f42b06a65d88050c1b7996617ac0b792b1baa11f1e31ab8cacbe89287324da078b7b1dad55eacc31f40741bbfe634e832e24c83a8122a60ec24e1ab4432104d9e5e049b41ff5e9e5aadbc456a204cac07e80b880306fbb30ec252eb0dcf97484307d0caf3e3e6594bb2b4f6db6c7912ecfe83cfae957a50039f431ad201ca2e748906c215a7f769c14c545b2ff03766d5df98c2326dda4be41f06403112080ff0e3d9a358b299850834ae6fbe0566615174a202cf3eaba7a038539ad653636dbfbd1d1a5d253c844cc82b96214aedb622e5445f38e297a0bf8637190fa111425925b74ae7f99c67ec29657178ceab2184c1403aa19226d5d4014564d07e9fde9fbb9bd1f05f29cd6ce78fbe5a72ffc9a3aa129f2e708562f7a96a85f4385da38cffb33c861b0b3f7c1601c30ef7cc0c28b7c06d5faf1320179092fcc1c347fa009b637b3cc309683458cb2dbde7b6f29a594294919940675061e07f7b520b2993604871b1090b7648e4da801f5553e7a0da08f41188c7c75dfa65550301c2dc1a1496833017ee9a1decd4b4f9cb9bf92030e6566323399a2e8eafebe8c162f632433c33827125ed1d5f93e0c526038c23cb8e7845fc94e290698a507869de8fa7c9f00a41f38c43c4cc8f2e54837602933858c93ef3f949b94882a5350290a8b1226a03873fd1b2c19c599fb2816a038b3e5008391c4d134e4fb7406a522871b10e74326bd3037e3002691090bf27de9e9c12b5f8e897fe822e366e4ee4c42f95e8f7403a63871064964c9ccde4528bab697b122aaee872e497228038c7c5fc65174cd7fe93921c9f74f42d1f5fd8622fd87da5e66d646e4fbf765ac30926f15241acb910910f97ee822cb318a28f97ef8d293ef7f280618c599fee6b74dae3b21dfd70090ef871828dfc73cf7628c79f2f54c429784211c8f4463486628cedc5945be1fcafcc8f7655ed1f5fd3522df0fc129f27df0becc5074f9df90c650beefa00378e2ccfd9410f0bc1f38cbf73ec963f9beb7cab7074bc027ba68e40bc551be0f8391b77aeb40195c90f75d402173a010f84f278c2f0c525c4a8061386ad5fd0d84c1a855f73550f302f787110aa3fc04f6e2050e1c3870e0c08103078e1baf1b39375e3784dc80c5907c48299bda80c30f16fb86348bd6fa3386a24b95a750c65dabe8d0ec2f27cfcf69d64b6c95cc538617d8822f8036da14a7f3b72770f8c1668871a95b1f18de1cc2e4f9291e1c7eb03c9f8419e0f0837db066b5cece10176226d2acaf48b3eccfff62cd7a911f3f23cdaa1f7eb01f20176250fe84be24797eb23c3bfc966419626256086944facc1cf2e83aa76141cc1cf28853382751a84319cb210ffa9a376037726ebc6e08b901bb01b3810bb09c54d29026fbf81c31e223a4b4c50c6ded0e6b6d9782b8cd39c1189de4f8f7892218f2286d6088f3bd2b3378aed65a1b39fb3586ed93a38d315a5bedac94c64a69143d8367fb9488e8c96cc71322ba84bbbbbb67c3e68181e5dbd73490b5c8c1344edb60dbf6178cb286f614d539f7de7b2f4a8699bb3bcac3944f828b21c86c70aa0116c508aa19d0155266d475a4943e826e87f5cc47e03aae2304d79991a025e521e33af30838fc1f8a71de1cfef4a3ddc6df51dea82e53ba514a2b7d8dd264eae373c4483c227d7c3669a3f8ecd858818519e6118923661f283188f049ec209a11113d99ed782289884c4004834b3fe0120c2e3d219a43c0e1bfd0bcd1070eff93000214f992393eb060d3421786d0e925eb9f46a27d1a89fee99f1f1e542dbc90001d303b7e8a8ac084f48ab2c0e1a3d0ff45971442be1ad6b016d2421ad644c3667487165e48808e530b8145c1a14a2582d917f2a0b1ecdf92524a298e9ddaf4250a874e963f270e58ab248e1e640d4e6a00c74eab6cbc7bba4cc18604cbbf7f35d03e6775e8dc1112b3286d5e4a414ae9bdf75e4d7b3a357b5f4cbcb42f72a4d704cf177e4e10e6149b758ae1cd708cf91ee30b14ed6e4a6b9d3dc81af51de6df71d63a77c21eb976bff42888019b9b3eb9463dd044570d79d064ff19041cc6a23ee9700a8147bb1f10f1804726933932877e4b203247e6444dd612c81c59b36074702893fb25955d86e98a805f7a8143954a750435634b621949f077edd9ddef0e62d0b1c4ee2edb9d085c9443c03168063939061d29924bc83108899ecc7df5e547806886f9b8148122ea1e03c843ca192dc05c4e0c7a61476748ee3701c1a1847994337e945a705126e62a701887401b3878fb300aa970fa277e7c411ae9966ada347e9b7ea159dff727a059a4f73a1afccd6af9e8cd2b1fbf5f79cea377b372801ed9419be67029ec915b50a58f9e4aadf55d88b9822fa6b7407dfa158ca6394ba830b27cf4bc477616fc13ff0a2abc40c66fe32a1faa7482e96c7c43859daacb9124bb73d2af3f5132fbf409525429cf4ec6ae0fdac6755e84399d3a8ed4258cb96d5269abdb22aacfba69b3a70815eb4384399d6a9730aed637ed6e8eba16d53e3d8b16dc40233bc29c4eb54b187bbe5d6c59d7b428995ef5acd4dddfd3228dd33bc638b3e00e1023e83d81e54715b817f9a18efc22411dfd1fb3fd0dc440fcb07e063c0bc181fe986f0dfd1ee89123d4c0f0367d0a7abf8f1e391af4319f822006e2574d47b6f3252347833ffabdfb3dbabf0f8c7d15df870f1f600782d93347c6d953477b0790e08bf49ec0343dc4a594dca749a95139a5d4bc8f700788394a19a3acd1658e1e93fe89eeeeee34d25db674d92ea5e4640eff3871e964376e4a6badb5afd9d7de825a8f2c51fd94ca6e8f7bc93d45851ee0341963fbe6beb977bb3b6dedb9abb96bafe5b66edf03bd9a3f3f9e0dfc7c25bfd8276bd5fc96b46abafb37f47db24f26cb53fe37f396ac01ce578483738ae4f9f1e8e4c92384647a30257472f81de529f493436c037ee5896179e29d3cbf52c9d3acf083c13e59b3e62bdf90c34eba30b3dc74901f6eb37cc37c1edd3284b0cc94e26441ba01c7202676f20c9c3c411a38db0fb0d8799d176ddaebb48de3388edbb627f0269b7a92e6d0d78be6dc1cece7d0752f077073e83877fa8aaef81ea3af19bbb01f13a594529ff26d9a52225a44675e6059f4cae996fdfe524f74a590fb7b458bf0d0190f9d650a44797a663c72894d9f66d80b1c9eae0987062cf7e31c93b9f6d953c6e9d392e6dbf49739efb61cf0f67448e290af6ffb8cbea3cfc8a859534ab38e764c43bac54517cdcf946e5122bdeaefd26744bb9b0a7d4642cdc2be79eb45079763035f34ff8c8ceece4e9763134548922718629b3f29dfd16764841bd3b045968865fb34804c38f99ee01df704b655d6aad49256d9970d8529157c4c5460955f49cdbe1fb2fdd667e4ad94cc573635cbf65776c0b155322553ada8c8b6b79f5a625d76f2e632644b0d215b195b29a3b8b2bffd06247134117953c99bc906d32b6fdb4b08b1954a85b1d100fabeeffb4824cfdb9064c9f2d2f4b2f4f237f92129ca8ff2b5e8c22b2fdf4d38d984baf85eef2615d42afb31c699a3ccc1a588284d15b5cabe4d9b50353ba51a9e6c6b601b0f0e53b3d42c45647d8849f2a68324cbc8b2cc922c6584b21c024696a9a32c4310490685b20c6920c9f26900459797250d21179f2c5d80b29411cbf643532c4b534fcac857f643134f0e4d42b294998064f91b50746d59b2e40f4d3fb2acf1a1061657569a60599a76b2fc9a1d624d09b24d019d80935346deaa81f92a06a855f6694a96ed7335306fc9e0f1956d568ccfcd618c4fb61fcefcbd4db380d15735c503945924eeedcbbc302a1525cea48e7c653fc6a759a92e649438525fc81af6c3989f02d8fc81292313608a93c3124f4d8fb762807c656b74882eecad95b026962dfe3eacf1c9f657505705157dc600d1b732aa681823946bc6aa4c41faf2b9df019350f287f033ca5606959fe16621569e7b02df1c3307daf40aaa3bd20bf773eeb93023df18b76ddb36da2a0ec54599a34c49e9954d19b52a65d4aa949456c9e04951816b789a15cae0a919e252a707a002abb12f83c7ca4da77d18b26d91c38e04d93ed7a138778f01f250dc775393dcd397e6135ddcdbdf7e44566ab6bda24bbefd1859292ba20df4150344c37d4597f63248fb9c4475afa1e4a766cda2344455bffd940db9df3650d75222db0f4b3cd9b274c8f6c318a06c5f43b16071c6cae0a981c519fbcda58480b59741e6306594eda78ca26b7bcbf56b32db1a58b61fc6a16c2fd8028a33f6e58735b0256239a640407b6c3d83072edac0d323784fad8a57a301f60fbbec9ea74651b3d2596705bd8c5b8badc674d923e5d8accd67cb19a725dd4f3549307c439a5c9fc65bfeb5d55fff05961e4c87a893271d45578d5c5132bdbaacedebafc042b7ba5ed5af9526d7fa214c0e69be7e67031cd2e4fa3436acbffd93b764d13523dba73dcdf26c51c840f9da6ceb0a34adaa21cd27a3262eb07ccf0ae0e4479930c66b55a95593a728ba6aa20e9a6b448fc28ab343c790fe6f48b3be513a76b48a24730cbd4347abeee5f18ae4d94d29a5f4eb69d5fc62797e445a356538fc7888c49f58603a5356604f8744c5ac23b504df8fe7e3f1166e9027cf6fcf85f0ebc953524c714addfa184b80381d70dcc7915e38bca4178679eeeeeeeddfd9c1d001d2dddddd65cbe850384e76bb5fc145191b51ca76c73e492f5c79508023184ece3dc65377bbb777935ed841fffc0581bf20dabd414aa9b5f6deab695a8c1ce779f192eaf5b8707732f4b81414df6bd28d728f495cb650c6254da3023b1876dbc6715d4f1deeeef9d57d3678ca625eeb3300397e37a54f991d75a2333a40a1ebf209397e8c2538ecb2ec6eb35639befff47929df91efc877e43bf21d7902cb972b404c26bd482f1cd2e49b83b5e7a247d993b471270ea6bd9bfb7e63cf2927e780a366914e307d81499f39e7e6ece6dc70911a8be5fb46f26d5ca455b7ad9596925e0c501d90714fabeecf8e840386010c195982ed587a502f12a35e2428047dfc556ad1a5bd7c9250746d2897215fdd6f94cbcc573662a96c2366c387d3ba46c51c18157370241cf07c1210ccd28355fefe0270bc45fffe025edeaa7f7f010b58c00fd1a53d98afcb500be0be448524c050b9cef2fd7629caf7498091b0d31e0c9d83822e325f05b30a50a088067bebc357155dd5e5608e8f51271c49f1051b05e8903d60470a9d165d1295e34b948c17092c5b85ff7b313e9d6ae46ad4aa292e57d3d15774551ad3e7d6225fddcafd608a9d1ce4764eb6d6298baef9d2a9ad3159467b720ce71339be29e62d171f5ff587a658688af95c179febe2f2837201f26e28e8e223c50e3d30cd498c162d31455560f9a18b8f8b4f8b8b8f8b4f744501ccb817d7a25af44500c8b528089f62906a51517445201b3278971bf56f1d275f4d77e7dc638c119c53669a698e602a0abe61b70c74fae4f13243f95f8cf27b649941207b78773beeeebe494d258b7bdddd567777a7d54484259d1813c1f2a75ff74904968f3b9c022c1f178123d729e9e964ce3967fd3ed38cf9320335419912563300d6caa46aa4b025e0051045e363bc9b79820a50962c8961c7eb024533a3d705882aaa8556f5c7783633605c5e54a8a9993666103c657837d38600280bad921474912180e8b4c65affc54340078060c9ea628c73b6de08956a05073c6b56f0128ed1e2ad04297937d4e4ad046139b931a9bc33d07e60f92bde4a10ecad04c173de98d33573307def86067191bc2094524ae99c73ce49e99c73ced9d3d33c1697934b4caa841597659ce9133a140adbd3888199f142c20c9d57a8a60764d0991b801a3eb0b778375e838603d6d2a440e9ac1fc38a18decdfc16efa6bac7e8d15d053fe288166fe515801a3795565854529873ce396dd03941ed85e5636fc507a637f4f36c084008a6de0dc5de8a0fec2f0540e389f67ce1f91c6dabadc480e9228d8ecb32baa494527a2ec1adc5e4d2029a5e525d110a321d1d43b7eef70ad1c9ab60f1aae90116cf0033afc4b0a4565297049b4b88998412dcfda509934e89039ad014e7098e33943a8b7733250f559d70d78aa157aa0e03c122b372951256562a2674d28be104cb133ae9547ede0d05351cb05c4def669ef09dd0ad8800cf972b3660bff1977300e8e2a67154362326fb64531c512bf035b226c0a72124704b8e41532c6179a628c2ee0093720c9a82871e610a1c2a0cb08c2e6a12b8e483306ef063e8055ba80006433b580cd140b9c038ca1178858ada0366496204939f2130311204f6720c62224414263c0081890e30ace518c4e4c50467c86a7171cb0d7cb06821649a41ce8a50c908202a59f8b09c60077c05ce0a0e46f019e1a8e0e005840465084fc9f18030f45d1a245be8d05d7c3b8cc58cd40321dc0f80bc4b83048aede2dbe1a29ea25887858068190a0e7769661024564490fc031e21990ba112f8c8443d41486ad8c958c0a2e4a2222d5e19082f1fe01004a22040c1424a70e8e955cc5bc47a157378716e410a198d8d1558e88b698e4152087151465c238c730c92c2871c5e1a2152e0d42f308d2aec0f7e252c0e8ec931288a20681458468e415100a922c02a60d426b0ede2054b7890c36b715892e48625422d2c01fa22484917465a8f1f22dc7084922749bec0222261811127b8f01104249af48006ad07406c4c1c6a4b18dd5409274bb484018234a83d089a40362dbc90802e2a59b2a2040b971c83942001032e25c3e1cd9d659412f4cc352aecfcd2df03958a823b85047794597e8f2cc154142c5348b094ddc36507715cc442f811edfe7de81e6762fd8e3352ba808bc0d22669e1a44f4f4b52654b72bfbd57de18354d03a2ded27c0c20739c6ae015dc249a2c6b495c809140de2209f9ca471bca7d22693e34324966bb762f4c85340d725316a1fe95a3e89a4020c9fd2a43ba6592b98a90ca4eb3dc741464f45364645624b7694885087d712cb2dc47508a86e804674c9a64de6a274344332845598a91a93ad8b26aa50501e985e973a4178e5e71589f324f70bb019615ac2b47252056c060423f263be815c519fab8087daec6985c272d019129ad4f29fd5ed22d16fa8e135dfef45bc67234c5484a119459a6946528534a12ca9e1640e4ee64f0ea7cba23224543d32bb2c2fe8ccc22f35697d3ab7e9da121332245b9885136a2f94cb07bf9e47e47759ca14fabacd149618f2ce3cc740dc724dd5dc656b91066271db2eeae01a639062981412d02fe720c52b2821c46590cb0cd3148090aa4129d951c83940841e6720c52f223cf2be875975c771c285aa101d70aca40b30479d09d3cbf534002dc7fe94f14f822a78c1dbb174f1a4efe30020c4c9f77e3335038c9fd24efa687081a22e2430ea838c28b26c278ed2071920220945c9f4098e8e28b6f27080a227985dc2a4678a2880bfaa107079c1f998e133d46a0b38323042133c24310395e903f7fe449f2018718472572f76fb7748574129676a0c514b2259e38c13cb5143149d041d1dd41509e7f718b0b827420449e3fe7dbdb1273620b16f2fc3ac5942824b983a1dc414e0c4912941b439913260735609ea01c1421cff7df8ab02c05219bf2bc4d927411074972abe8e0e4f97d3c3026190ebfd927a55b9fcc574653aa88ac0d482cc1a2ab7f7e69f6313dd1a5429e5b10a23c75d021cf59a9c84b4679fe8c28c692baf3f1786bfb84bea00fa8559f91ad16b2f8915904c92c70d092654c7654ebb46afe4dc35a355f0afc739672bf7c51e002cd8ad9bb24f3493e68a3120aec73ddde93a2d7ca3d06b63ae7dce6fc39e78c3cbca74f69adf4b78dfb95efd694f5cafe50695664b9228e5968c592f6aca05a72b70487f767d0586599bd962cf3d791620481350d8567b8364bfbcb42abec974abdb21faf0c0707c7043e4042d80953ae6f67781d89090ea7916cbf48ab6cacc90f6a932e6a1322b4c0031ae2aa56231c1c1c14d4a1b88ab35b848d4e571cbdab052c70e24ccc779557502b1c6750a4914ea1564d9a665597716a31951cc542ab649c629c4eff212d755e110eddc84724db2139766c6bdfa6db8656d5bac55a6baddbcf5a351c1ce2e9376ee3182431c145684fac06fb1c1704d42a8b5bdea621600121404093a305b78b1efd12aa86d29bbe963e66f7408f1ca51258fa8a9a1fc0c95e2aa17c80b7d4b52a16bb3d1515d29e6c8b8034d62a6b573a9fb88a49d015949755ba8a5ae92a2a9429c9526992ba89b88a4e44152c9490b2ca77de8d4a4bbeb1f6495260faf8432a64a9908ccea0342b06616183942e093ec2d1a9af29cdaa395527ac74c849b354de3e9d1135abead457cdb19d77e345c061ddc9f6655755402a5341d12420158a02d3167076388fb2c52090cf4f500f8dcd236f4d23b3e7680aa126106a06b52ac6b77c784daf57b631c0967b4d18af405a82c329a3471446759a1ed99f326ff9cc57f6f13de129cbf64950e0f00291de7626548cabfa2554ed6810b848b6dfd32aeb3dd9feecf116c6f649fe52045c3fbcb221af4b6f4d6fbf71bac7df1ab560b39950d85716a657f6ad0d1b28a0202303634be0f595f5a16cdf879aa5bd75fbf61d855d45a655fee1ad3cb587d734576837e010672958ca9f5f0eb8d65a6b7577f7ea35c729460b96dcdddd055468719665a5c2c635fad7b9d949cf051829875ad5efa89885845ad5fdd3aa86bd83218feb837dde8635ac617d1476ec74c17076f87d2fc6d2c857fd5216814309a32f61d1e5357b9f9a2583cc30f24aef052428b0b3c0fd48233f3f48202163fdf3c3d1d70c13bc21e4c62b165d31375e2d6858c360205f5326e703ce498c7706278a4e88889ecc763c21ba3b885ec0f1a0452e91dc6943683d1088aebe3c2ee0a6e45347d43de966fdea97774434e03aa262af7c7c8e1839d2eddffeeeed9336fd66b3a2cfdb74773d8226873cbab054fd4f218f530be957bfc0a0ed39d6ba75ebb3eb10548e32e246359483210f9a39e79c73ce89834881c84e7fa03f62eb3a7d81258481e92bfbd357cdf5475cf9eb90f10fd927187e8f09554a6dceab5dc751a9d339a93b759f3866b45034f8ad12026ad27389f08833ad88d67379b421b422f7462cf682171c71448eebadf946c81c9a1113a05364164418119b0c4189a493215c20c320c212d141541d2412438ca08710617d8cb080061da94eb8806a21a918e2884c470c3b203044ed410bb149c85c25e6dd6ca5f3c482190d1d2b34cbb2a0e5d0b7fbd629829a00a1812713b4244e7fa95953e62a31ef662b9d0eca74c4b003023834f1e134a33dfdb1ffeadcd3c39c60891da6f8d8c006407270600591239814551c8942889c4172201bf2440f4954d0050f3942554e610754dc30050b8e5043124d78e0a2490fb983d8e0faf46a6d8f2270a0a2861930b185500f076012032082e0c44e9126664eeeee7b65641335f1416980459fb1a7d54a948c0d38be047b6c12e43a89ea7133e7913a894a45c19d4485539ba86e6d36aa31a594b290bac8650e8732f7a0522563030e6f0178d058ae2409b6446912c56303cab4271505879f234b27513241707c2e964ab3a4927bcb20903d66773e3011d8dd3dd2588e1f61262f4d161a5b71f718ddedb793e3ab587c71115fc7c4b5946787b4d157f4380ec864704163ace9f129e5e9bed3282c52aa5dd056ba93e3bb7bdc48946a5d8cd27d835d7ca355713fade20c55512dcebcdf3813e374c74260f9dbbb7b6bbb31befab63c0d1c04963fb758dd7a32e5c9b26b220649257b0a5dc8f649abbddac6751ee9c32a2b2c25534b8c930c9717981931346a66c04caf3f632a8046f4d5ad5ae43366a050600a33606040304503f3f2924a793754a6488d4dbb9b76a97d717191d97672fc0d165df5254cceaa0e4c7ebcda2bc08c2c43c2482861e69e4e2574a4173601851c93710b4c9842d1b5135dd55b1d00845c64b5e8aa28ce94b189aa45ad9ab528468c957763ff04af2fe9ea69111aa3469a755fce5c7cbc559fab506a51d134994e00802a9700062609c5226fc9592d3adac1b0ee954d313c049631193353cc575d32c54c179b90230a3325984c331b6c92a02991e38c6322b91d1ae24ccfc81267a5066c2a9502504306031730da80068e6ad0a42410819d8ca8a4eeb515bd643433120010007315002028100e090563a14812e5a9a2e90314000b6584427252409607438120c6611004511043410c31c820a09021ca3844451d050c803b9df8a5c48849680d890d3a6f12ed117667a20793e62231adb02abd74538834903badaf290f57adee6fcefc7063ab255e2783e12ec4e47f51be3857ed122b7d84ddc36399be272992f7218d01a27b1c7fcb6eafe9ca1cc591c6abb0b0d665f731fb8bf0b469aebd04f4152a111ca80e6dcf9d57a9568fd5c149d766e24126e9c5e33188d006da729885f0df8125327acb7f174e5dce99afcdbe3f2800069946a4d1c96b10dc9ac8c97e96c00f424760b0e3a250e839d48b8c6f6ec1a9958279f684b49d2e268cd20ac8b37699506214c4934dcdcacfe8c02e6d1010cb6766f3683025000360b82b48ef28884bd19869649a3c8d7a3156403f420521071a6fb8d54a3ee8b040a88725d03c249aefbcb207861a7fb1f06239ca3ecd29460bbac0efe3e1fd1672298c837a5aa05fe7064907b1666f57db73c856bf0751e26fe94dcf19789a1b01a2be72dea412c1b2fa676fdff0bff1dd9f318d09d3dec0932cdce51f12d39bac0036eb3485567f65318059d150fa30e367f24b428b24e9f7f77a3bdf1e4ec5c70eda481a13f18d161a8dabbbfc43e5478a6b683adfff8becfda785e30ddbb11517c4f724c94102e4303f5065d68a48819a6266489c7944789be77b22333dddcd3a148d046834a4da7802bc5cace50ec8e25e7201a3f21a7e39db9962d272895bb7ddff560e93afba2d2d818834a7422753cc5ccb3f5585180ac910276e41256868db609643c3d923d5d27bd2ffc1996602de1e43dbeacbbb5c33f712ec390794e3a7e2935518a65fddd3de47e7755464154df6becb3f4eefffc5e61bbf43e6bec16b76c3fac3106bb1aaba643ddcbad7da1497fff24c0b636c45eac77f1a7c67ceaa953b79fc4aa86c4250271ebd9763cf0eef2e084413f06803d536567a2bc19ee5daf6db7bfdd9bda0b78e1d40211dc746f422a05dc504dc872e1b751edf383d18bfe99c65ffd04eddb515260679f9510df6196e448dd24cc88b1007db519ed3ec50eeb6967b3cde760d0192c699c9d352bc1b58af32ae1081d3ece4b3006009e19f32a3201bf626a0900bd1e6677eaa262fd4bb8a7f58d3b47e41ab4bf289ab0adf26797f2291352181d8e7be2abd43ac9db2043a9bfc474eced425314e05a339201bc57fe964b469969f9ef1f8ac679b3b4c9b7b769d76747def91eee2a8c475172f1c258de039f5bb545afa25e112dcbcb4bf90b0ec23b8c5545c12f1020a767f26a9d0a9dc955a61bee0ca7e2ab70bfc28ed0c404b6ae589800e7ae8217bd89651a1312bf751fe2bd77948dba0f46db75009f6c815faf06ddcc289f93ea480d7ec59a8969ed64b43b8caf14e6789ce0e8baaf89122f62917d9fe311ed41ae9e001c04f9aa433b3203ba02bc67aa100fa58bbeb5c3057e08b9c45035bdf0f14136b9e4a28e20928163aa3a77c0ab2ec9c22811edd117372dd52bb2dcd0d527d25ffe16e9bae5334b892f01689c312225c399b630b48ab129b1973e2cd33bc7b144d0f763d62b818975e2d23bb3c617adcbcf3938a78e60a758b4d6d688921d6b513c586a37caebad80ae621223ee867f12abed8a6f596931619efa2d9b729f23e33cc8d5499ea802f573c7b7ed4dab76e95cb6539d3fb9d5ced3c60dd418f08ec9f29a444fc319473bfbf8c508cc80847aa1731e8bcf867404c4ee8be929446e3ca4e638755a87ba22934fa87fb84803dc7326415e6082a252f91e0bed7a5884b9a8806525cfcf34b6cff6759d725524ebea865a309a0f926e5f40e57193ae39b6b6b8cb5291262a76a5168e2438c8127c108cf446235b38edf812aa0ef94b4744485ff369e37da566d7ccdcdeebd07f46a9376a5887cd4c5c47b8d26a4283e65c5dcd6646ef5d88be97b8a304676951191e081b874f6fab9f6e3d6de1d762ee89cb7ec7462764a84f4269b91db42e65ac4cd229a30ca7d9b7110a7267fb9136b3135871625b189b6d653adda26ad1f60d2fda83890ef9ba882ca45ef5e4fb72d56af7e79d23bc1f9596325a030f58da4b60c21d0a1049b173435e6fa8cbbbf32dbf4d27e927cc45d2c6ae187060092d46863ef60db6dc97ce4371166c30898f99e5ae3b15a2fee15b85f4572eee02e1ead6b464bc02f5553723f8abb732dd96aeaf3a5336e58d86296e121937d85905346c3e2f31fdac61c4485c92eb7258571d72d8088e73ed4497b4fca00190224c18177ffe95eb24fc698617186478540bf2de2912c69e4bb77c5e2ed14878f380e182b3380f8c8c3866b288204fe88461d1f7b55c6e0804aaf6069fc2980a5022763a11786758c7d1644e10200eef2110272eb041c515191377e52a249bc276e59d3d9a2ff56d61f168f0ffc720f7d7fa5174fcbea3f5713509cbf49aaf4c05e0c1e5ea5293ff9d95a9aacda1470b35e28fd2fb0a1a09e22f102a1f7ff400702936c431d199e628046e35bff77c13caa9ad09447e8f93797052490b833b1e2cbc6662cd0bbad3696d2430a59726b5398a33db17a21ba8054307805155ab9642603a0f7b9e1b2427aab54520284ec6a36c5589a566a31e50b909360d48cd18c9463a9c16f15927890ebcfd86647ce537c2c2acf48788d42786f2d28737e4c4df3159937d96ffee8162d130867c2067073d697603e94f79afe34769a15031cfec4ed5e69d4fc86288651b71b88f001a91fb5e48be6d952abdde1362ae5b73515f20cddf8665b2eca0fdcd2115d44afa843c4f4032267cfcf30025c1fc64601036d224601a21a13e517fc119de434924d337c4acb8c33d312734d41f78022d13496caaf9bb320454c546646da91b9779ed7b4cb6cd3e37ef6d9a421589348523c6399e976f3073cb146a5bbb7363acdbb5a0b886a7a174ed0c40535cae72fe9f3b0c256535dbb4237d5947205f5331fb61a78ca149ed9d185923b2cb010a0872a6b53d2ddba9b22a931b4d4d40fca7cb53dea74d756087f938167afc537445158302ea0f233b9230cd72ee534c2e87794840f1a0863b52aed054f5c9485490c2dad420085d3c67c19b92a0b9771e047c75562b78fbc8dfd393a4f4ef81180461b89bbfce380b34551364d2bb7a6ed14846750194130dd92bec74c086a76bc036e5b4c7526b6a9aff6eb4fb92110c990e2cbafecb52d91d476cd9b59e344267f115ba161c6fc3774e0913144a5f718c90de96c3777589e6ffb9e8b1b56654bf6ec80c54e972fb9d96ee355970392933be55be9975b90c99daef840bbafbc2f07257945ebd8343fed13b444b3e62772441e98f880e7d132310ff0ce91618031da83ff6965da780314808674835c056e6611317e88149efa5b5beb8664fd9962e54526e917d8cdf8ad854f7fe5d3763292ef3d417559a47564445c516cdf2fc6e3935aed2a4e5c14240fe50e312f12cc8a2402610b960cda313e30d2e217be79f24ab7548a6b38db2dddfaefe1122f46a6d2f002e8c3efc4606fadd7f68d1abd705edbe397bacf16f46abd744db2f64bb4f14f4dfc28662c977f6762bd646e602ead2fafd5f77c58ef6ac9b33b4cf8b49eaeb4c7a79e47672ecb46e8e246d748402bedcadb16777b68dcfddd4a2e6274f602edc13f788ad87e84ddf38a3bcdb611eabeb55a4ddbbbeedbf09e6d580aa98a00cdc1aa109bb95b412a18dc45795056f4b8fd91f4444fab8acdfe9f662aac222952f8a251aa09080035dce035e0947b6d97308949640d86222896b9674e76e63c366b73da4c488138c403cf4981b6d1f027ff4d220cb1ea9c023c3b6f1084cb51f91cb17d7392936b36d9601066768123321ec305998fed433c5680d6b7d1fc621724210c79436125901ce411050534d4e950b01e9535c2c2d3548610e72b131d40db0804ca9fd2000a452ec835ca90fefe846966f894fcb4818835abad86c9b1816ccbded7f8d933c5b89a72e9d454da4d71f0ad0141d3560f5d70a2f536dc80b060ca971a65adf698a1b547e891356b4cd872cb9e9b0a481291b540f558586d38eaa41ac3b44fea9d8f586f25ba958b73a81e427f3751029045a5f5eebbfcde2926012fd67a34e87aa4916d9341ce0df6ef0b80eaa406c8631012070484f4e488e4e2ade0cb83ef208df52e0b111248d78921a4e86d9ef5ef3e6cf09085e9336f1695fc8291eb752d112802c83780af5011091f159579453d918a7a425f811a242f89e13b2937111a70a0bd6c3416050c87ec3a9379a0ecfa58a0f0024bf5517b32ff74497b76f770db75738ae85be1af80687757e2961e4d3cdcd7bb15c50a5740d8cc68187c53cce8e22fa39d585188c22a2bb601062477ad6af197b9bab1a35044f7f0ba3db0eeefbaeec1b2f728bcaf8e4857aa9856fa607e124fa6ddd8a20885f55a80f5e0bc57a1c3574fb79f1b75dd545c5c75e2eae362902b7a6c98bedca873535191f0c6acb409ad6497038c45ef1536c4c44a4d9b57a857332880b0e80d85afad98f8d2cf6db1e6d1cf81df4fbbdf720be0a5ee17690072d113b0632fda46996962dccbc81f57ebdc4a5184e8d50f30af8ca51f9f8d857b93aba07f66adba1e2656dfd26f24e716bc744e98ebfa339ff7ebf16fa06f25be9b758dc3476b98eb2b319eca05ee0c295aacb7890bcee3491ce5c199de12fb342a9e6daccdea6aa3d02b874adb2278e13e22264cc33c1f5c60772b2b72bdd0a8d58e2da167d30e1398010c0562281eb99d25be080ac694cad6854e0a7da90b6980cc392282c2f4bed31f0f25bf6880eb1add9035800fcdd10142e5542db8108783a9b2fe8b36564f975864e0f3a28377aa7c083b5188c3b853a5788cfe1e3cddd6125bea57976c893435bcc5f16e6117aa898807026564f95780e5780589e8b100ad3623c6686a71a2baafa10311d71f0278a6ca8545c5e679907f8c3ad14f222ae839d93661594eb3ba089eb784e8767b176df71561608ac82288085b88ce831ad71804a7dec0110f812243de10374000228ac83109e1e588cc6d36ca3901fd893b0e3999fe6145e32d6268897364200bb34f076e8046717a9d05c29b0aa56f354fbe071879171f6e05e87eaf3409e8cd3683945d52c93398fe439053f043692522a881bf8f833d1b0ebc8aae5ef200eca222717db9b02316ada6e566ba065ea3ba8faea3fc13a848d7172674f14f81d355b6a90715e5b27d73a0155d5e3471ab1baf0ae2e562bdc663e1677f999af0cec02382b0a54b3cfbb146205479f6c1ec65c7931909b53ca488c05ef7202252ee64438f79fc018cd4ec7731aa957cb200b28957f39dfa35e9edd4854849b4aab0adf6e64f8456dbbe92333830f77f2654f25cd0bae1c5ddd36caae038c9b77c6f418e2b3a56f05ed629c562cb3f4b060700f1242a4195cadef6591b115014ec581508a56708e2caeec020a9c152deb659518b93d124e58c56871188378ec620e44d5caf2c16dbf1856c2f553eb04d07cb43064124d095cd91070591cb94a9646284b69ed24e1e9be4005704dc4b2d8d9cbacd9f582fe148627d82168cccc0b06d8ea8797ba55919352d438796cf0e62680e71ec81c039a4fe0a6f862807e446a7eda272670985cc5dd26d924f9f54c9b7bd5747f9325ca61dafd567b08fa9cad7769906792712a5838c6c95b8e1534989df73bb9f4ad4f0c2972ce1be26c8bf28d1999b8dcf58d8e24547a9654ca5ab26603a0089a0990a3a586846012892af280ed0de8bb2eb088f3f545a50e9e4964c24747c3e3662d85aa2d19d47d5cce214580cd6faa4782984fb88c7a462e78e2756605727e7bde174b285116f8e66f3d2d04851003651fe5000642569fd5b8fdfd49c99f0140ff6d75e4709ff6ae84e0abfb47121fc945519992356e0bc6905581a6e007b84dbe24f52a1205268d6f8dab92fcb5c453bedb520d475c29058857817f7abd117bdcfd1f278601ce87ebe838d1c41186b3d526ddf23c68d4ac23a2218648100b34eb5035971e1ed73593e72f30efcb15650a27e2e891f82f985b19d62d582dabcfb7308b93d74f85837460392fb502ba2677851c05e85a1b457a13bec9c5e6107f75db0c3909fa5aa84e157d2178083434ffbe9b3e49127148717c899f795f844b5627f841670cbcb6e4ca402a0ecb4b321dd00a3776eb15f301b3fbaf79a82f0be3bcca35a1ad400dd81631606ed403dbb993211821b0345b7bd45297728ef13b4ba85aa5caf627804a390547bbb6f5a344b9146bce8554d62824fd1071c8e61b302b2a82cfe7b064d735cfd3a634c598242c71595884460e2a66bc87f9dacb784742c6ecec5a2535985d25f0954d4a3aae97f4f14f7e78806604f5fdf192c2f07898336d7c667fed51fa51096ea5632a867379a12506109dc885f7a5b26bdc72d2efd3d937537f81f6094d48c80160cb1270c2de3bb9ca61c0341c7bebef7f7e9f77df9debfbfe7f93ffa167f5edfa291e31625b64cdbe9a195799eed5655ab5fb7e8ce8175d4d0496a8320db9ab6d9ad8dfaa266577b14045673cd7c481de68bd353c64135546553a7ad7ef4d532959e646efa7c1b3508771c39433efe7437e57c19200c7acba8b497654451736f229c8703df53aa359ebbc7443e0e3589acce086dab250a48f11b3cdc5df38c09c9a1f47435e75d85496fb3e05fd1bd76604c0cee61b123bba45543bd9e361b05f88e405a95757cdd9847ca98fb4a0c28a30dbf8cde2e05513ae53c4c3f3e0e886d50f789101d4f82770e31b5124488667e4615edc93b8621caa6bca9732ebc8910d9ab76594367053adb53642b790f94d3ff1e6c4fea5fbca87c1203c63167d048c6afd6a57cc715718869df4114898de0cb50c8572a307dfa73ed172e8411fc11f24828a90906f7d125a60fb62b84d47dab95169a020458665c5c5bf1c23a1deb9eb4c1d2e228f583a49948f7b33414b2b6edc28541cff1b957036f83764393afc1405cce9146834bd377b44ee39a601a673a08e0ee24e4bf8060b028221735b97c2134571b129603d53abe89710a63c986a276578fcdf68eb681eaada0348eae765578ba87960f4a2bfd9bde464a13355de0983486c4391d4016ad72ef794513bcf7e91a7483b0d164eee273140f50759c7b0b1a17ce0b465a4b03349df6184274688067287067d08ad0edd3047d3f0602fb05cc54cd64d7ae15ddb4592f6a41e2c60758da3c8b4fa0f44904535e79ea491144e2b76f7a7b62b9e9f723539c719a2ecc5364191d41b819cd9a4fd8108bce85d01a2d11ed986b4f7b59e353180ad9c22d4abb9ed378251560012e2449667366723b005c9c1c39f824c770ce370e7d61a43fa3bb35ac3aaf0f31681704584b30876cadb705eeaadff54b8d36777a8fd2fffc247180f235dace2ec876cd7916088efebf663632f9b083ac7e4a721031b9352b639c1e48f2254a188133291976e4a5cd016ddb059932ae53b91d88d96371fd66f66bbaf576680f1438cc469f1c49793495f22c0085acea56d8c8555a73c5777dc705c92fd75127a4508120043b14233e1c4b5bca037a819fa3f1f41d21eb1cb4e707d97c3661d5d91c0d2396837b42bf5d4126849c5b94bb799b192f4268ceb9749ffba9a7b3b526349c904b0b4d854411a76d08f5566e0a6f5447430bb78256ee81c5f7cfd6c63955fb46a6a063a088e857a729fce5b53a469a03829386eaac64a41be350c1b4ca956ef5b7b7123bcc1cba7bcbc08c5058f46838c78329ded70a8ca377f5be6f1e11558bc29f6a2d203d2545f0807fa043872669d1c073b0e33e77938fd989d4e506f207a08073605c517eb3e46fa8724695efba30fc54e27fb2b8b0e11678db33307dd9732e3faf193fff094b284704328d62df1c0ebffe62ab3a145410743ea79b3423803a59c752087fba8ff3f29219465ff5f0356adea7ff7c8c000196ca5ef116ede232c1f6f3f58ced565cb05cff13d79a47adaa79548f3163685f7a349f1ee01a2088098d0f8c70aa6b7927580ec084e854859bbd18908d9393289f32390b60a10f56c0f44524b3edfd6d243becabd9a910eda0c10224ad5d9f5bbd2132a77bdadf9401ace783035639e47b06e027017f45edb641932d8ac9fe4b568c6e79bf8b5a47e15b4b33c5e73db967a4a64d5a1789bf2233a27a34a93213420484fd31618d22458e14f6949e65949fb8843063876128b384c3f03e7235071a225724e7907d3c8072986c439781d37ae574f5f25658ef9667b50b504c5f06d4b2ec5cd1c2189ca55690c5f76496b7c1a850bd4ce7e5265202e1d9c8031d217eaa05f2e33dd464d975290cc35ee3f55ad6565aa51b1a30892b7b56c27c606b92bf533234f2795618a2dd1ab272db33832e6884370c3be9f4cd2ecf805c31df2c56403aba0ec742fe70efeffbfba73e1cfe05872cdd27d260dbe51dab9b26c9df177ddda263715d9c5d4053f7bb2cc6798c40b2bd9d1083fe11224677d1ab8ea30f9ba4990fb9d50149a79e59a84490336b631cdb81e943bb84206bb8ff460a8b9d672c89566058e2b8bff73459055fb281a213321dd811d1692901611bc2eee6662238d95ec4cc22400f7dac4158db4dafa2587c68e14dad4623c7b8424d70ce2cacd5e781f13ff172f0c8bbc5e638e535c3e2ebc023e0804052bc3fbeeb2f9ba77c2c478d7cceeb6bcc048adfdcaeed50a2a2e89312a77805c7cc50ec94a69f7831640ed3e61450c317e1f05ad44e6ccf77edf56b05322920062cfecb1ad5bfb6c32d84c209224568dad75c52d7b34652c62998037ea6d496be76bcc041b292a05882a43b3b3052404ef8106995ae6ede317368ec20cad8d33895d0af48901dd61954767aad7f403b3ba3324cdb18155c6d1e022e6ce267d7fb9f8f5f109026b0b5d7f9968a5ecad52ea044cc57ea207cda936034e5f30b69b59c6f199cb59064aeea5e4206c3c0a3032ab403aa416fd6c961bddca53d5be7d4f8e97510069c5e08d6fb6b808f518c10c1549a7034c290c572e7df2ff7a6210f97bdcfd88c01863de2d3bee125274b927c32f62fdf6cad8b3fe4a4cea00bbd7d3e8612acb65c6c77fe99cc32fa7b9fb0cd8796e20a10df1902b2583afc5cf486f8b66f82ede86717dbb1c303290e31ffb016e8f4b1a9a6e13ef3d269bd9a289ee84e9f661b9105623cac992628d38309cf3baa46a6b342b9323855cd1125e89b626271d64fc1fb681b5eb53dfab0a7bf739a10c9e8a1f46379fc9d77d07d6a4d8f56885117a5d91b7847224727c2e4cbdc75305a9c9f927f2b0659b0a71b364a565c55d80592ab648a32ffd882b04bc0bb8e5955b73875a044783555b658de92425df7ff5d003af0232ada08c4128f971c971e326f4eb3e79b06d258749a0a41a4505537ac3d683474d77a98ca9984e0166f02b322cd00fb2aefa2e822c2dc3cd5cd5dd6489f43ab8961c2f80ad7c86580aaf3130bb87113973a820d35625612f7773d3bef2268b5b9d37427e0deccd73207bf85b4c347324a8df91208ed6ee1b22ff31e74264bfef89a9652db55b9f35ba9979a5c72c7dd28321b9441e1e8a72f727c0bb68e8acecb5e729a6750271ee7535c50a8d21ca1a301f55ac755f7447802cc28befcd76440134058d9a66506b2184f4445114debb21b5608daa9ce068df200048b86514fd03c956002ece60fe85efc3c38e3570164ee171f48926afc35f454c5b62709f3f81e92edcb5a898168221e31b8b14808aea8a2479f5ab8d1e32d6e3ccee9f544e303466349faa07fc777b5448401fec4d81e4d211e061607836bf72d35b5bec887030a168ca54cd781118eeafa5eb7e9da2924316f46362fa197e6db49e843a20e46b36738248010a5807d1d68421f4150b0b6dec93a14c2c807c4a9979d3bce98c1f71936123051ecf6dd3137deb48816ae2efe68fdfa18b4b2ccedd5d998d57213cab25727a9917f095ddd3eceb7a149c77611cd66c16ac215bdf6349aad3e63a09af7e1197784193d85ad9c91e6e3d165f41e9fac85c91ffbecf3cb376caeb18899a7e92407d5ad0dddc4e5213bbc80d6c3474c7946e18a2c31b6ad299d4782d20830b6452f1c33f7f7cd4f9fd8ae9b564863aacd32fc8e837496e80524746e0438f04ec68a19aa5708759e9541cb4a9ac01d258b9f21cdce9ae1ac49b8292c259323f759e36992d7334e4e61ef7aafb04c36b2e8417545b2e8932cdcb2da87eaa5033499091517afd550706d921e1d60359f626b44312c376753db58538502b6b9957d1de05b6a8f937a5d53f86c91697ed65ac8023b7eee277746dac7f18f9bb70d2ab8bf22db7e44187a7ae51fab162192ad78e01c19be3b00c3f06b2a48d34c5bfcc3fee6ed2a1532b85fa01e2c7f12bedab43175c286ad486e5055c188ed0c0af1ea76d8f52b4e6f2f653ea14fa6971332d55191d317b284547d08f38bb6018d590c3460ecb56706b82c24dc22e200f3f51a427722a727fc8f88e7e7a24eff776b82e60b7d2a0209d9d026ca2ef6fc41133d531a5d579b2783067eaa2bff9132615ec835112e4c93999cf079b8e683e3460528835bcc4488ff89edb056891328b68c1edd04b8d862875b03474533b6411a13b17ebd9be280bdcbe2f6457434907aee90ac33b56a0fe65152cbd4a893f9490905fba75502ca195d5edc0aa4c8baff7e172dbe5c05e651baefe9426fc48237b3144a4a05fb71f9a4b40e7cd1d5ab78937e477340dc668e295960bfbdd2ac09b9ce49390326e408bac4f2b104d8ab1bd439941c61d1fb785709be9205344e43357ea1a05c8d009edf4f3904d31af8cda27b67e2a70a3dc1e098b705571fea38802da056b4b10cb94c9f48690e9016f0bccfc70664395b39f799e1b26ab75eec93666363f8817b56565bf6c32e891ed8a8b83158c7f25442cd7a104d9bfd6197accc677f66c86a8890fbc9347413c46086d79f035d57b2a2beb6dd906a88730e4da9c6bc94639691ca4aa9e88056cb54f0e48c977aa39bbd898e51ed9fce80ed8581f31cd410b8fe34142955cb5086ff204543edc7eebd28a90f670e3c6adc7c0de995d4f2e13219435053dc70fe41fa06dfbfe978112886c133a5e243b094a6161f500c297291e5cb436fb7004e8a7c4059c1d2b5c285fffca122db6ae2ede9055979ffd0dc0614cec56146dd8413938948c73eac299daea95f920b2d3a9e2cb083ccb61962144037e8f1626b1aeb43ba1b540735b2f40d5320e01cb27db793236800dad04bdc8aaf2d7c490a37a6ecb12e168f5bd471c93b1a1195983803429d582853b9aa4e8a488489ecf475c98426852e79dad307d2450f40cba5635126dbe09bfbc0c1694615b734be17223bdd438e471cc83569d98f4f505193164f6612aceffd0eb0aaacf39b3186385f3dc65b62cfe2908280e255b0e1261043edd5956b0be299590d8308329df33901b6fea44a0aca593eb164179ce6bf704133c222a312897f786cbbd16418f833960f1188eb26353ab67aecc765880c74c85dc1189502f41fe7bf4dc7f5618edfdb3c502a48688081198805838fd207b128833b0d6938caa19f078e27d8164b20b7aabe63ef3f3c3669c37beabce0a3e62638241ce72d72edc2e09e7592f6278521b7ed1a06650b561f9618817116f9f7540932facf1ba62544e1bd9e6f80246d260ea61ffdb25fc0ecba1407ecd413a0a81f456724a86e1e284e10adfd6f84146ad040039f0b4f738c340f7512a8c54bf1ac621c1735c768b646f61c20584a329027d32adfc9e9454f6dcd7b0643f8e084b43682dd8be106956b0405983486704a07980480de663d32b435ad8591a56b2518c26233731a311ca18322032b2077b0b72f10d26e7003b179d7eb2ca74430cba3fb848f5281a72e10af4258f03f05880be688216d0f5596b92e0a7a12b4f7306545eb16bbd86286010a455d87cf85a67a4d0c64bff5f5889b5c2588e6154000986f6031d69b7cda673810d87a5f12906743b5c8d3810bafc5661201df1c0d89520169efa16de28acc112c6a28c5716a4454bda9d4f50643edaa94cb7d95a1baf091727456814e77f2b270099fe9d10fa49ce7e870747761660ec0ccb27ddd1f1854c6eaaf5c95c6d9d983fcaba857f23a02f53e9cec13eebd89bfda189688e1b922ac9f172b94dd5d6a3519d5482ffd6aefbfc2196ee81c1da25e9eea082e67e43ef2137fcb389817041e791f4328870d8421d1b885225820a48ea8745cb8e82fac700743928ca9e7d9b461ccf7b2c43b0b60c46d3d3a61df0a7683283b6dc9de4cbff49c5aad0dd06135fa4067a5226d7fead970adf43b8593580d0c2a37450e35f757b9a30f488028b78694d936fbc5ab5e37d0107878b20a2f6756204b14c00a9b0635a0e1241970e4a27802c8a216715b5e3e95da6a6100f9ed0f730c166f2b9cf4e6bae2df36e9ce6938086fbf786ae78a9c5d6e06c3bd4f4b73c8532cd1394a386090c2f167afc85b07aab4d2eb388e6b56d84af9028b69c5a139745b6c5f1aff225ebd4c1d8ed74185eee5af42735965e3b41d3a61a0cc8cc7bdafe173e655e417af33ac64e29d97e2fa79512be3d34a3d8de26e86fcac12e20e441a08f475a613919fa8496f1f0b88ec4f6f4591ed8f87bc67c85a7322a22d1a5c4ad50f7ca75077eea2ee10368a7996b7375b813675f8cab25d37b38c1708325138581c2a4bbbfed032696505e1522872469cfe517587c501c608bf855c2a2f6c38279eb3f35e02391c7e562577b663f7b0c2955b46c1b85a45f86315a13819c2527e398bb9aa65cc1cf1a5b0374c113088526acd8c1522d98daa4abc474436af132fd276adfad9e26cdaf141bfaeb6c9dbb160db7c96fcd1aba7c0f6e422bc22f7884040a106b81af6ec57a4a088076b896668d1ab03ddfa1ab2b3d5d1aaa9cb9015dd476b1bc7019b941b753da04ad604225d457bf0c850be48952b3ea5847b081b772c33636f8d0badd7b050414acbbd65a5a9738dd216060842e2a8488739ee176086c2f76d1da585ef9587cd2d010dbdd83f313e7858af173e30354a5cdafaaa51973b052be0c30d90b83bd87cf8bb2eff1b38f2439703ea35a8044ea6cfbe52e5ff6d07674ad25ffebcf58714d2ecc813e55903b07af153e7b7ae79f451640f960359f1141c3845f9ccd4cb22e993290acb16d186eb4a13dab848633b4975e3bc71d03264941248a47f69b43e66de57221f5b3f3bf77fd32055ca628f087998b21daf730b6367638b97acf1fcad607a26db09581c515af84af56eb9795ba4b264e7cd5d894a6f144644dbf1a6c043f94d0897f4938021b08f49b75487a7b4079bcb9c775f964264443962015773caa5c229f43ab5f9daed829126ce7d097cd73b85960891f8eecda0e08b11818e76254a23acdd98794bd5f40e308cab7b64afcef6a862ec26706b30b267118c6d8c6dee0cb8ea76c786b4194aaf19e7a9de070d1f4fb9a2d0dc18e5affada516d41e40be9f81bca15cdc7d5d84fcaa2831ac8ef93aa0627e915a4ebe0133988c486aaee5d91e49f1abc857eed1304024815836bf5eb7f030ea7774acc14e888ac233468cbd07a7429cf25f22bd1dad113dccfe9703a361a0b2013afaf923dff97d09c65d675e76e0fea1c276272f102b009c100ec2a97caea6d2e98f60a85db9822d10998b79705426370ba59f732a60a5f2d168745dcd338a7a2172fadafe248c2de7b15130847ccfbe143f958047a0400083fc4dad2699383f3e6c5fa7796d007ab382664f8801d2a94115cf4cb25a374a46fe3fd418ec7d05803499d886d7f1c815852ffa1a6b352f40125e2a0229bd1448a6606c859c51a4a0cd272d0d975850e65674dfd5ab1ed4c79ea3384e13805b3010593c1b56ed06b31f074901c2ba897afc5730a8dbcd907f4614ce9f52c883b40289345c0058f0a055c93076d8a2e0f6b2e5375d74fe60f358e9e0de4f094b0f3570abe03f4335ff67353872157acd099c687897378f3541113a6d2a262bab5faf6c03028cdc27f7b54f5fc37cabd6a8a379690ee8b905733734e3c78884b7818d4d2dba36b6f4ae465e5c237d196b10db31e20b9a1e9eae66e91262b96b3fcdf2d7754852522d81b5b6310acb53e3d006048bf23ca11a3e85de291fa1170cbfb2d8542087c132e2a48e6c610574c26d2af72d230fe638fe0c273114715c91a0ccc13bd884d7ed052a9b229f9dba582a80a93f160029c62976083519ca7ae4360124bbda9830d620b81a128f78edb4e780c0027c42864f826b09a51dad0b227a6edbff257da1c187847c5660282c3aab235cddf68cde409aa0fe8bb25d6175b992e104509bdc8c5445f2ffc777464521e81ca5c8aaebbd138cc4ed2c78fb1ca47cd39ea4626a2311885efd59f0bc7a2f180977061e0d8c6dd6dd392e5ea5630a0b2a9300c48b8f7005c489f710f520a57fe6497e5ad826e5162476712521d9104501d841c24c9c8a0eb5c00ea381fc1e3eab973727547a0e86c23cbbecccf62813b62b894e4f9176eba4962b85e8adaae8b64d2b821bab8fb15da7ab68df55e53093f04b8d52483ad28df2b9fe65980011f3732dbc860ac48da736937f8c9bc518c72c4a424a1d04d92ad9994dfd8c587ee9844b47cecea28804f150c5c2c37d0b2ea9d1528ec81a7a10233eb4d5432747d0c33ec4e3826503b6ac944390245d2c86e26a6cc7a9cf82c1f1211c3de0fca7c4bd7b6d9dee443ae1654b5011d2ab712f57735fb28f979e36dc0caef1ccbfb37f247ec5000c6a179b34f8a4192d5226d5c2929eb885160cb1dd462b7c2598673893c2458be282802b69a5334f3de64c2e521eeb8f11ce491670618434af65135e90f03af2d776711628ba3af05cc17582fe35ca6d54020fe60236a096e6cb1ffeebe1d30d404485447fc1d1046b0ba7a5e3bbf50568683f88e88fcc1707a7ad70ee6f4ae7d743a705995f815d48e891771fa647ed8241c3b64ddf008e6c32c41d67283a33c786d925e4d8a30fd0e51852f7d66990e19a702ddd120442655268405d6e1d24ccbb0a55d75b7d7e6b8717c517238c136880de0a24d49b0c44878e6502fa6b7d51363a38236aa8c8bda740f03699dde1408ddf6b013a56cbba9b7a9d054540971962093c0f8f194727a0bf8c4702b70e755bd1727260cadfec22a9b72a284efb31142e44886eb471ea1640c5d1e65af5b7ee343a09b2b68d1cdf92ecbbd4f4ca2895807d18d689bf1e7b0d5143b3a92a0931d98507e00bd127d5180d4796e6bd36c0249c5584816e38309fd3bb64d868f2eb2e9936706c9f8e8e20857ab53c57cf083b9833e8a94126308298b68d6c5aa9812fe7b9c7e3219804c4017fa7ae66649222b296164494d168397a47686743ae0627c8d8a978e2cb6c3fafef5f40126a38617e37a7590b0c68665048fb6abf862e277e9629fc64aad131af848ade0b93475b94c190a6dc3f0c29fcbb2581109b841432f3f3d59beced20c8f808c931c5205e5c17c3990eff2d4c9b7974b0c496714cc79e9611618772f9b22a4d9838e9131efdbe19716dceb1ff34f650f4c1a081e096ea0f50dba876ec0c0d6d2ccc64f16263ea8c4deb008280240470b27e3663fc810a3a7cbe3ed4b1b9c0cb589acc366ef963d3c388b747647bb59bbae85771dab6bb2e1f655b9cfc57246771b35d91edb76d7c4ac218565ab2cb2585a044b9b52b14bae8a7eec57de3916ac125f8bef1f6c138562394b24a677f493d280c4ca03124aca64d0198842a65aec0e07b229aa1163a289700a1060539986241d356f391f52500d3d2e9fb27dafb9a41b11c09a84d62abdf719bb642c5f3a82a3ab85c32da3e466afb994b30e68affc10cc91cb5175893f1972447030b1bb25046d218f0b62b538c9470dea1cb6f0a9b3f90426561e4732eebd0099470ae10674d4ccbe051e47c801d36e1a47cbceabfe0997530c9c015d206239cca84f24354612e29c3509471b36c5ed10775f24d0a83ca8062c23d1557ae3e958e774e91bb9617cb66086e7215dc25c9da4a18757b75892a43dfd10854523a12b8af2ae76cbbd1cd646198075902d769d6d21b2a846bf05832e43f13153cc9d80f40c8af3bb4e290a6791db9411767e4f22bcc098c747b6bca2dbc16bfb0a86509ae084db896439ebc88958b11d527ec97c3255dbbea4428000dc94515b332ad9e22a923c9e24d09496a2fb83c376ede3cc04c974175f0aae49a7aea695040dc20e5ac08175d4937922be4785368af019caeb6e791a69a1b85fac083245955a92799e103dad5472f60c8f39548edc5d31322f3fd5631b4fdf862af2d48e1b22b0608f8488f0b640af1364cfc13c2ef9bfc3c558e053c83c7064747b3ad76a47997865939cb7ebe2db5326c5b5c3b313e0f9e6ec8530a72d903e5455327f69af50a47f25aabbe62c7650c1c397dd53faba1d4f3e69c57b7acf14a0352e14b2d2d2bbdd8a5a848cc4fff6df3f53dde8f30168e4bbe47d0e1e6ad13b53731ac8c3e1b6b1f3a5da19ed270e7948200f57c3a6435eab5b6a605057147c9534ac6e6d5615e609dca0bcd6edf103d585ce63d2c6f622c4454d0dbbb184260fdd2b6e4d77d872fe66ce7ca5abdb345bdcf7bebd695908c818bd7953663a6e7babb74c1b4a19ac3f0a655048bb441c55345d9984cd6dc31f9d538e464008c14bd5583caef3b68e2ae5acd58348a385356779a703db3c776c54b0df14679003e285fd6ac682e52fcdd301d809758c763ceb10d37ac8ae4dd4cffcc11746f55dd7fbe8872542d0e6d0c76324f24640c0f5bad431ed40071424f90c5398b3821172846b4f780a796b331497474cf2eb2393f13b1ce5c07fe7ba9ae25eecd6629f2987e3a3ddbea2b5be3a31c9fa605a3b0e6f2b711d93290a12c82daf0ffd359579d97ff4a5ce4fe008a4a8332c2f547e05b225b1498ab5ce48a70d48e39e173b9f4f021005527e3f3e70353028bf9aaf2c2347e2c462c3cc29381f83898717413a4b47de00a4982a1ebbc222b265a5ab1ee7f830a2849d7b9603ff7e73be7af7ede2a4838d9ad489aeeac9db7b308b73f98838d3560cf11c34456aabacd8faa7b97df706c4e3fe2ab609b91804b214ea376355488a3b5c2c628144ded441542585d5d66d4efe4fd76a403469b9ec5057e0791769204756a65442f4f4f17d2e09b90fac02a1d6ccf78b9f8b373b7bb196a0717abb352b671fe5f7511cbfff797036ea89f458dacaaf8c3d76e34dac0dff671db28850c3b20e5e5d19d3d3d4e7bf8765b7253a278aedba404ab63397639d1988700ffce6d884343f040bba438ddef97c365b3dabedb4f1b624f64448a2e686f09d0d8a519952a4daec431ae111b618a93bd50cc0b69e4271376e74363dd28a2f5f63d0f624b0cf8cc2620ff5385120b5f87a73f1f31adf80871e9d05f71439ebf669b47f02bdd8dae18f82992c893d0c23cb2e9394df6f395f90c8d010b0845cee9761610b70f82e9bbdd6032694b353e257ab3744622305d41c4da8c0c99afea014eade6b02300b2120a22c01af57d209c1c0ca3614e15509ae6eb376c486d2f79f94efac85a9cb6f659f17aa3b9927603800168442bebbef0a7ffad1590abb4b97643b1fa1aba078fe7394652ed84d122006c493acfe465ea357798876d451f22eb4334139874cde26d9b878184018b34db22a95c9250f5404b6c063d149b6bb5ad43044e71715a0a9136ae3b5a81bf0e5df54dc83dc850d5aee2f35d509f9c734a85693b410754611bdc950ef66a25e210366abf5f71cd64ab4e15c7c4f4aa2671730cb554a9192a1e9a43f647862e424be5740ad1f769c1a1b92517a20475b398a1d48a6916f68629a9b061213cf638bec49cf6be34860e0e61f730f83ff3320d5be8b9d30e5214c730630cea0a5323e81216859256ae60c8bc7baa32b0bdb1559890a5360e64a7dba69643ca1ae41bbdb2de0952d1d365aede69e8c4881b19d99306010c21d72633d8e83f63c4516330aa3c0a8cc93f979b62f9c00250a0cf7d485217f9ebb31d1505f951a60cb428c0a141835afde075c359454d0426d741bac119fc09083980a5404d5d0fee3cd0c894a3a2443710389d099264097b79d6455ff7769360f3781b7131a833c2c97ee45617baaa6652bba6f68dd682a2bdd8f61f00b318e24d6b2655f6f010ab28d69dc5b11fcfc06624d4fe109d085e4231808ce7e36de0b9128873ee6bfda09fd32d10536fa944a05188a58bf4009748ce36472f84dfc73875b978ba8dafd9ce1c3f7abb4b189a35d507ad856dfa3236ae6d14918d3171b68d11d0f8b89136084be758a05d34f96b0ed12720968c54e8143a02e7dc761cad6305453e1f164b37741598bbcb935a50dad314d80f1e592c0321dff7cafa248b2de0326a4eda68485e4b773d4b3fad370c41955ca72536597070c3b04e845bdd5d16a48bd4e1a69a3aad50e6251156000f1d0472c7f4c891eec40dc34e78c4ab19f91dcd8279a3e69bc8a64bc48ad0985a9c275bc199f4c0be16d3b7c508a82bca3ca5dc4c818a772fd90b0d82b8177a1f91ddb9bc3f3646d75bd87da10323f68f74a56dbdcce1d087b3dc60f81b215d4d68947314e2e5f8fecc00db5f6325ad2eb1a8bd4bc91cc71e1dc5c622be6faeddb6000feab8bebd78a0da70311be0c9fda9737e0fb685c3f874d631e57bbf9764e3f3dfb7ca30edf56558682edf6d062e17099313d9e63558dde311aef416366459d821231dfce41b443ded581f7b0ed296978da8af55b84b9fada5035e7a9acb916ead322091255685f90bd54573d604115fee2977fc54849881bed73605980108b6302c831990ce8a1e7c919ac20170031f0457a061bd70a0270a6493f3907e356fdf97ebc28d2460893a3a3843ff46e8970687df34ffc3c00b290a59930a01a85eebbf21d821b348c793a1ad4bac01f025b6d1fab1e310fc86a99e1b1988a946cc898b96db98855bf1c835614b2877e7cb5ad932eba4785b67c9c5b446f127f91faf4a24eb15ca477f572831ec5f671dbe502d28bd4ca64f40ef8614c5d180605647fce468239bd32b094a7768e4cb85e95cdfd2fb2a384e51417b0b6285e42d595a62481213fa1a1410b411a99fdada415d213b12030702b7f4d98ad8657048ce5eeab092bf1186221c09cd9f71246b1480e83c444ca0ff66226558fdb24087ca939f779a459048d96b104129df4e47d838636afa0fa85265f7a96d59aefbb4a6d93f889ae5f9f542432cd9850c5cf9a59ca07ac19740edb682cea72a8e879e841ba51041099d6fb029f8bf5f362dc04a3083a296c0d88a7cafd29880a30461fed6b952577f91ab53964feeca5b7b9503f824e35d5800a920ae51d731a02ef38a948147cb1e98a4469cdb202ad437c16a5f1a5dce13d8d946b37f1a034607d761b605f1320c90e665af441628f46f334a335c247ffeccb33b8ca8be536910c2e154bb3dca0f56727f1acd471fbe9c08459cad7f8b5a2f6b5160419e79925e21559a060b643eb60344ec748f7c72ce7a2c30e1606e34dc1eaef7976c874491a32dabe3cbbfafba3a755e7ed10158d13f9905890f33677f1ccb5eaaab0370063e9f2524b7a528828d2a1608e0e1f004e57879845c0db205cf1de762ed65a24d8567a4a899cfe761e2ac5f3d75d993d3fee0e06461af1055be87da6bd62556e90e2278926d05b4027093fc5784866eeccaa3c43d5de37cc860273f9ce1e75044626e3a1352da3c65b53fde4521cbf8d75fa81da3b14ba0ef65bd2492b545d4e2289ba118e0742cea84380ee30fe18e4a5e756eda14b58b2397e6035cc6ea2b70c89fac17d1f521a934627d60e3f94bdd939aecc5668d2215993a6834a2a755e14b023ba0614b367a227d06a5bb92ec5837590a0beb73b08cde923479fea6c23f70fd60d6ba5a639cc175ca5b5f9b1368aa6ec31eca3d508a5e618d0192b90431c1ad16e41ad0c9a37196c7a5b05c6e756488ad2e71e519dc362b03ed0efd50fa613eea23eddcd90280622c1441ea541d64791625c3e41435fbb39832aa540664affb34acb1f489532b767543c14a85594bda9987851bb5b1ab31af8960e6e630283daf57e362a3cff8a8d004b73264f0c8ab6d0117f57a7093c5f6c2424d3435c06967158d4d9ef7519db66ae3e0b2f2fc3a87e6ca58ab2bcfe7d8bd9b538ec24dc61387cb53a88cd951796ac4f05ca8b8fb892faf428c8470850f2915779a574ecf99756e4f0b80a5b636fe4e152bb7fbdb06871a5e7cbb82711923b79768e93cc93016ecf90f71894a3a180f856e6970a239c9e337524e528c5c36ae403fc6e6befa171abd163a17eb3dd6726058a8856bcc568655658b04c7be2f2e3c2a76197cddd8127318a86e48f56cf6eb1d6150c5e2e560d30b7109bb5b633bb34a9b048729f120443781c08a2d87f47a4a11684bb2fb3be4391eb19c35251146638d7844ede1715309d00357a562707746a189f41b5f998effb29b320a91614da1d81cda3b3fcb7b20468a1888638a7690d0477cd067c50acd3076b382fb000dd3a65fa78cd3c78654d966a2c50189b8f4a9136b2accb150460c3ce1587e1f24a20533b5724418d1c8fb3c58a541a1eb3442fdba380e9f7ac5d868b865fec5eaea2044f2f489d58cf48672d9269c2f190e4a46c1f7de86ea226c9d267839d9ad854457c679cfa93e77402302f0b691bc4b3635277c314b707bf4164eea09338683aad35dad7bfa6bb716912731910ed8d66e0a81444d0b5674a01151496d4f5ff64b767a6ee3ad9a4fc5beb4db7e62d8f7c1ccf4f45ae3f4036e2ecc4d3756b823d92574841f20ac5c48bcf2fc40803116978818dbfe42042802aad30970b0034f14e6dc6836c37959de804f80cb40f7fabe871fa2cc1486d2c9f4dff806d33473051c7496ffcf816150b7b92327f772041028da2c281a23505425c5153f0b91873636e346eb40d77a93df8d74ecfc04c20015e7f72fee6fcb1b14ab0a9f76da10354d3a41fa67d2af427ac9fcedf4293d64fc9d76751eb07e4b07b74d145a7d6af71de5ec996ae56a5bdbc005c321c1bd3087c03c365965318cfb564628a62a5cb28efe8ae72d5c5949b51f4fae2ee67720cd30d6f3eeb17e18cf1c4f664035cad33bdec8fae493511f448b8a6cb1ec82e2fd3a6406442fd502b3e9201131ae83ed12e5f17e9df9641f3566c75e0504628ed8827a7526e06a219ac8a3055d37936973eee7fbd618c478a44e5ad9a731ff60d55186ff07fb5500a043d7e2daa124a40340a5729fbbe44fe6310d2e9d8df306db152099e0e8e55678adea9a7dabd98c0ac5f09b24cc5558b8019a41ffb44fc5305e6cfa5db0cecc0ee0674febe34de4277bd49974b6fb50bebcc21f1bf55e74d90e70179fdf62e91b29c321d3fae2bfc8d4684a657d41119a9aee5b964e587afedaa751d9caabb44c1a2d02178343454e76d47b3140cd355b8344ab95bef33973a7caf7b170e80eedb33700d84f40a84a4d8862d5dd52698c1eeb1080626789bc7bb959f65073f4416eeaae6e88d37b1b1e186f85858fa541a22b80b9f0dc2d958d0768953ae4d5beca55f9a36dc5ce122e04b03ab07a5334cbd63bb7608eaeed360e627cabbe8718f537aefee6304712e874c098e594b0a7f31ca5cb1f552a91ecf2995569ca5f149e8cb68ade5ddb33d9f8e4e514b8a17b9e4b4060c199c7e6ac2870ff43b9629f422caedd55a959c6e698afcd1d125ba61f600148beb5c890b304517131a991aeb2162b7f3561cb750441cc01a016afe0821af723bc64e3995fe25603c0b34234b9614e481fef7be471bc9cd3862b2a4111cd173a648a4fdec6decb7c30fbce46d788645ec305e765fec04ccd8d040d0dc7c5d72b64f3fa42bf7718f08ae1d4e84fdfb93cdc3308540fc52c25bff21929a22300c0ad3790443796143f13b1066a385db5c7ccd2d5657a2daac5e02ba4c6592f0a354947875675ed95cbaaf0ff956480ac42bdae2f20efd884762c52a48ff3c81dda0637d93abec67d4965cbda7e6d63cadde194d510de0533d20e4c4c6d1a59480469da1903ef7b1e3900a2244c4200d49ee13ce865489f617b25b9b17babe5a8901a020f3bc7be9813c96fe8596cdd8bc7bc305ef8e11ff09269931b4864ec24da535c83dcecd5ae8de37aeeef13e823d2a4dfd9ea697542bef54efbd7b5c34c84db372e1c42f5d30f776b6100fd571fb2e1fe0c7415c0a27ece4add67df38a70350d30121d341a6f740b070c5b229188dae0ce7d2ab4c1baf8904ef10d9e6908176893f527c11414ca28e22c90c10723da6434908d2c7302849a6b2b48c4a34505890c815020985664431e59244e4dff4c001d96225a8366656e2a6d10ae134cbe2bca6f501b42437c1d57ee696925530977db1c7b90f0675ab943d0b3b28d3cc2c842f2545d8b6d7850491fd43b307674fffb43e4d0f07a490848bea2e10d698d38de320b43c8661641790756096ce967d6410a181711437cc89c8075add3e0d94b3764a8ca02463a0bf6d9e83a5e9e65de1b4390328bb7d51fc8517fa061fa1992ccc329152a1725cbbfd96846a05fda044793d0fff96b6816d1307f8928393058b3a530cad199db78cc17b73db21b3a4013f4397cc0b0716b31812010490c92ac28ec79918360d60b49615036497cc9a2921320529e10b51ba8884d8b7888396efa3b658cb6eb814427ed311362747213e11f36180891a7e16e089a0f392d67cd05990b3ef6948c106e91ebc996199cd468cf267328ad7147b6ce1647c513293785d6cd22dcc66f1a4ac20a28f255a4e3c225436dd11e405df15407dac00d6ab791c9f637813c726db3b2fbeed8b282318ae02a0847480b4263383bb6d44b6f6ee2f6cabdbc4ccbaac776aa4db887150b4a82a85067059994277f3db6f72cc1bad78f0197ceaf58e03b324d6fa5c17d46eff68c02137c7806207bf4f2d66dc175a405555bcc477b044edd1d9d6b70f8500e410c7614923c9244e68d366c83dba6ab481b6ba20405851adb1100929303c88aa7fea2ddec6047cd293822a9dea9b8a0290265e3a30fe8fee38956f3c0a1aa0ca970d3d711ff780f352015b3a4b98401dd9cca38622dbfa43584c50885ad8dd4d0854338f80cd19a8f27badc75f45da1a27b0e072b7c7b6e538892b59286c8308d6baa5b56c4ed823dad05b7a425e6d59fa1308d30a0c306d550b8b2c0a4a04590813272b18fecd3a4b8dae0bcc9eccc12338e12d9da96026046addab6f7443f5c451a45401392f0dbd042ed3c0fece32ee249070f808a2495cabafc68d80dda42cf632f77d65741894df2fc4339e41764546d18223f2dd363100abc492a3b7db457112c631df2fe9c64f891cd8578b6ed5bd25732216e082d84e16469e60367d4e75869b93ce16e4c0d036dc22073ed880fa0f491bd84d9ec2591160148fd144a2c1da09dd220337463931a4f5a4cf2994e2c1e2e1c59eb5f3c259a918d7604643254d886235b537a28c6ddf0ef3eed3b1176db2ab63a5a9360c4e551952b9f5371640bb1a35702d22e29e12a7c62d9f94e71644d5e64a55ce99a0ac0d8c3071fba4b64e3c882e62915364711362a0c5f19bf82f4bfc185f489924363b9d5e8d47ad6c0d423d855cf3a9b30ed7e183e5fe891e26dfadbfd5ef516cd9da7e4e6b15cf9a2b56c473101ebb051acbd6ed1bb5428d68fd32c3faf845604ecd999cccd80401f4107112d7956ab5a85d6f8ce67bba2703d5cd41dc934b778d40045f71a8b1d5fd2cfe92c4534a7c97837a0dca60bad148d42012efac25c870f3258f04c874fb194dd34f181fd0dfdb2ed207e5cb029dba7cebd7c0841b35b5b5d12019ba999800198035c9af184e83f24665759a06456dbd7d162fcd7d5f297fa18e79c2aeef1650f07fbf6189f4ac9d63e05f6a16d23306d38450abb1e05da1cf78f0a64f65929c39e5c2a0d9d80605ddfb6f29732a9d767fa63e2989f28ad8cb5e93c67abe52a5950acef5c66e2da58507c12861fa1cdd26f1c4b80355d87d50d115650de17a36b54b8d7eb724d0679d28c09d418d90ab3ae2b9a161f8f633fbdc8641af6a4d8211b7860817e112f2e2c778098a1a5d42f4e8ed3b412b71f959922c93ae200462966582be7e80b329470747d27764f0fdcf57736649b56257969f78462ac24c3ebf6e1a962501f9f1072ebc9426425b00e038c667c84fd70cf9245b21b674d78b5f8dff999debf37172efd767dc98204df0de805759567ad8c3242017c5faf85d86bd27ffe3a17b35938c38a70dd02afc3bb0d8e75b15be4c98613ac9bb9ef4cc41c437586cbc680d852e5b72f816813d803b63b41bbb5b0e5c6b2c81509be80b0a70f3a19620ec8435c686ce56c6c43d937caf6ca33f0429ab1ace94fbf55a5a6998cd849835c7cf2002c2e5a0fb6fbbe9d46de5baa8de0ff0389d5f428d0bc1dd272b2d89f9b794680028d32f4257f75f13b54a1bba2dc498375400a5e12f5d340cb92053f6c490bc8077cb10d0e72648fdd6e54c85b467846443275062d63574a5cd913b2fa1704dfcc04ac669bd49c1764db40a76abeb464105e79bbae8fe1328409683e5d8afa719a499abc0f60ede3104f5da2d39082b4c20d9b9c7f89978cb1906ae95a28b6f8934aceae9fc0b1ad479170f7fa090e6bf9744876bf52711aa7731b878cfdec4a7afa87f28e306ec9ec4cd27d8b487c2479adb66cc24459e953e4c8c0e60adf721452b2d9bb80e81889b2aaafddfa80d31922fedef8f7976856055e3f1d5e8aba56e0e5d961dfc76ac433d2b76b28667053e4f0e647144d8d288fbaf54d8ecd353b55db88a6eaa6ba44066013aca9a3dfb2e3fd2f18c4dc71d86a18626164178644200ae2484b856370d679b38d965acc8428eeae5fef92680da0f1dffd128d85536a1d35e2328544657e569f91fa90e137fabae56715dc0eb03d8882bbe2be881efc6d4702ed5e17c09a51d7e5cb00cb159d2797f151bf13243df42f6cf322b4527944acc7f1f1bd3ee9854877b23b73b5c8312440aedde935016015a730a03a71cf8f2afe894be2d7dea691740731519fcf2884dcadc406f610ad4bd88721dc4e1f0bf0637b5990cbe8951e91b4c3c978f43715bd768f67d476a1dba1b00030bc82005bb751300421554815aa56e90d1afd101959ee864652b081debbcffbc8453f6f94807894a477e93b8cb78bbcd493e0b29955d7d42216960e380484976754824ceb7c36c4a9f98dd0dcd73fbf5ac2cb74bf87e79580c20fe1b391778035888542dd48b3f97f7ef0c304c7105a3cc86b52261d710bc885488535edff2d400a082a8153bb73408b62beecaa154ab8c7136b5082b4ae76e749f08b5db09dc31a38ad1f8a26c1febc8eea4e1356342816981bbe6f92862cb0105937cb507dec670002a3c0666d96ef9dab070e0796a2cd66d9ccb8466549fce3a58879d61a4e45f88af30d998b5ac236c931f7ac9382032c73972c04ff433cd8b77640928e7a476388178f3b4711ff9d10c91101321d7362f845878f3aff532bf66512ddbdcefd1ed2519f6deaa85ffbe13b48f13fda133bfa55eec26acbb0dbb9b62c445d84f0ed556a67f4fe94334968909d47fe21590445d4026d951f4cb506e48b9051c223bb0075909e8e2ac2c6274fe4d734a5f5ad0ddb364f166a4178821e0259eee31d3e3a45a7b117879f36539778de86fdd3d3937d984d12bb60eeb0fd43479edcc9f56afe7e16839b0a01f664447d9071c9b0c5985700cd714b92b51fe9c01340eb7f47d7fd1e1fecc004ae28c40e0d50107855a5e9a06959ba79610ccf35c4354770d3b093031dab61c7b8923270345f2ab43f2f697727e918ed1baf36d181f0402fdd309116b76678828d42755b9c577facb5b5d6da7613111111217bcbbd030f0d560d0f0d5d7b00787f36aa87d8a6e52b19ab700503739ae64133cce76703739aee62043103c6f719330e86e146e4751f76e1a17966efdb8eb868d637ba4c975657c233a78a609ce6ae76b392ac1a9fa12143c6e4596304420142012a552da79f0dcb5b5a8000020820fee27f7979193d6f2c799ee78d075d5c662e611e34bb9ca6b130f2b038f296e8784b76bc2554b464cfb3443a3a3a42f060a43c100e17c4e2ded416f3da6923ea7e3a214da29af44a9cf12122a224493a9e4aa2884a0a74c21139e1849c704160bc3ce5723bdab8e07402f092f3b123e325f7238b5c8b03c209c0b378080f6122dc5349d5c9a121dcd3827b5ec438ebea05f710315edcc3018d3e9c13f010878411071bd1588510a9222052452fa9229754514baa2855c4f9607c41168f713d1bd1490e6e890224f664f195441dd595f8139d2c9eec609d66a52e1efb6816cac5e39d66a55c3cfed1ac938bc7ad66a12e1e036996cac5639e4a1671d1f664f1e50ba32cfe4551b34e17ffa249b34c17cbeb87288b17879a752f5e142f1669723352c59e4a1271f1a24fb3ba8b1789348bbb7851bc9254922c22d98c1cc922111f7f680db0e8962a415709aa24ebe2cb1815219556255180ea4abcea2999a031666654d1182f33ceba5a698db3ae867879555a413c54c8e25582b298e28417882ac8a237c458de95b14c798f19cb0b31962aefa5b18cc943982bf1a5ca1eb2f81e537c268c78164e15d59598352cee73e9e2e35dfc6cef054816fff2a3552f1710ee4482055136cae2bda39a1c356bbc788e8b66e18bef749ae572f19d8f66fde2bb9d66bdb8f8ee47b35e2ebe6b35abe5e23b20cd62b9f88ea759405c7c17a4592b3c2da926ad129f2a02df70cb8dc8a5952aaae48b91ba12b58c02446bcc2c96f47ba9ca5e79215ea672cccb9b3dcfbb5649f2de58ea984339250a77be18a9a45623be18a12c4c8bea4abc8b4fcc85184b2dc7cc98548e71f169b9f09cb42a89e25357e249ab95c50b00972742b278188c42d42af1214681815190b44a7c0c8c92a455e283c02842ad121f008ca2a455e2571865a855e265609425ad120f008cc2a455e25518a5a855e2517c2a89d2a457220a1759fc87e29351b6c8e23d940bcb34cfc321b05c6dcacd0b2c73b24b4b3c8df1a759a58b4fcaa859e5f853a459f60645597caa89f85251464152a200cd6fc688020463e7961b51890294c56f441b9366c9b8f8ada859df8db62d3c23951cc0c573ad4abeec6c4795b4f194cf4624bee47eb2f8720e117148b2388bb8244242bce494c8bcf47c68bcf47ec47b40cd82715149164b2f894be921c9a237b4248b1e11eb6567847cd90165f1253794c50f0057a3ba120f822f954a9e782ae4c5a75562f65ebef864f1f4bb7921f22ec94b5050abc483fff0cb91668934cde305c984f95e809a3573abc489852b82e40ff3981306fc3703a67410bf1c6995f80fbf004d18f11e9e79062e6f2e1d06975a2e8d21b0b97c31f262a455e267462a1d8479f962248b8fc1654c57c2a211345b1be4f9cd286171c66881645b83dc51f0256c6950c2b68a3c3faff41080ef51b30d38f2c8720ef0333e3f25c031c7772dcf384dcbf8fee2d3acf0e25f88bcfc84637933338f2c970e45cda5b1fc461e5996f119231435cb186990568994e37c64f1c3251818eff3bcb1a43c3630fda92bf121a6b906165f121fc3a3a010bf84f825c4e76723c4287ee6cf47160f000c630ff77bf7799fb7f256abcfcf6635ea64916610d33c83432cfe65a7923488f268b7a42f3b59bc4b1150161fe4e253855c7cea108d22f12e419574f1116f44e627c647c5936addba93459f2cbe838137a2562537262ead2c6e4db2f8d285278b57011f4410ef82f882c0a5cfef2ba96e3fd5adea1df53ed528b6b2f8ad8978733896db16593cf83d3c087edf8f2cd21c04a639c43eb2789432dcb21e894ba0202eb182b8840be21231889fb3c601966031678830ea51abc47b78e6d148fc5010915051165fce21128b6761a32c7e14e2113ffe54b25b75253e48c627c688aaa43ca99e3b2b298e3fb9bc279f4ab22efef45349f2e24f469ae55dfc89a89232174b2f27900ce678ea4abc8737a3568927f15674a4c58b447c2f63802f5f66bf5cc9e5c9278bcf3e05740d4b4c1565517caaa85933d5e4ba7820f0a9a755e25ff00948abc4bbe0d34eabc48ff8a4d32af1189f7cb44afcf1e947abc4bfc0a756abc4b7e0134fabc4b3e053107c12d22a712c15d59d2cbb0a0e537018d4aaec291ca2e0f004874942a156652f02874375951dfce9014d1824d2aa6c2797c672025d3f393b0973415a95fd628ea755d9168ee58a068ee5cbfdc632c656b435c19b91026a0d11e36674cbd34694b39f7c9aa58db355449aa5b50a7b11cad325055c0e4c23e008211648b0488285122cb0689a0e89e439615a081d84d0210991e7eb746a26bf99f925460ccd115850f223971389662f564ea6425410654b69466df54a2d7be991ba6f17f89da6af0fed186e1f60dac3612a246cf0da2ac97a2fb16bd8bd4b4fa7d3e974c25e3336f2b899794e9853674b77e60c3d928dd4223fada269224768cb7a16e5f6b339bdb39e874fb806d524556804aa4230fc70b39f8edd7e37194a926c6f91b4ca9e2aaa0bb7a4ad9924f3d020b4e787da3991b0412897b44816b3dffc342bab468a342b3b92ed6b56eb4fed7925d2ac2ac4d6aa53c953afec577662f09c300aa5354e2f290c32e625e10d5d2d27a94a7a0a3ac3e2217b63b760c2d89fdce0d656105a83d25c759ab553b2c187b6ae531dea83eecc99eff6f405b76782753aca4d27a5dbe36816e9f7b6bb45dd622fe751b6d8ef59af5c418d144528d39b8632fd095e92e94bb8883ee4a1ffba275322991a4d2c24d36f3fc84934696b680d9a6d4d8c97956c2dcd70fb20d3d3209406a944283d8059d021739803902b1011c2913c910401482e2750107cdc69c12c6aae2badb2585b65fb00d0c83665ae6c0a24bad526c164d63cc18623b99c4cb2c53e33f64b4f546b96fd029077d26250196b2d900cb6193df80212b8c0032eb8c00a3bcc20c80678f84218c460861b5c4136dbf2c151b1cd33cc200848003b894c96052ddbb0cbd624b00882c5820a2c5e1087263296b276bcc7e6755dd7659fe3ba72e8e68d10211323466573eabeb50f1d5886db75d3eeeeeeee49cfd0cd5a6bd7d6d9b2b17ca7d5d1bbb74ba5b85f07285b1cad64da0db3603bb771db316cdb7adb1ad32d7a45b968af2db5d68273c2d4b18fd0b8650365fbb2878e040d2d69969d01901b7e9024c9f67da459748b56d96b63134fece81ff3c8477dd93a44576f442016373b7886ecf3b3555b6fdd4d9649d3d8a548bcbefacab29fb8e63ad5aaac0c17a5a4e79cfd3aeb50ab32a136603bcd9aaf46adcadec4a859d867ebcc111b6bd1c6825bd6a39cc534eb622f2e23fe0becc565c47fb169b58be8e6cdefc6fab0748b16dcd3895219174d5a26bdcac45984ca629ad5d7d55ddef5d9f4afec7572c1f292faf890ffe4c569fad812d555263211512e7e05973979e5d867b3f2ebba7e33f3358a29cf319ff293e76494a76014dca2828273545ac463b8b4593c4d5dc1b589578b9ad524bb8ad5c32daf169115d2acd92c6cbca6b8592dcad9b393932f9e4d71ac45578ba5d52da4a3a36383338ce10c6838c31ae66aa23c205e5c46fc0e42544920c6174d73b9774ca5f0ffb1c812358f196e6985ae1e1efb9352117f61b5366be5d9ad922669935e655f1969518f434e8efaa9242d12c732a5627f52c69f7cf5f05c3da667a6a7c652331d652cafe9d7d883f2ec3688ed11d22863b86515dac9cf739230ec944f4e7e613b55276fac4dd5c98fbdb93a19f2637c61f02838fcc903726a5e7cc8c79dd7bc500006c4821d2f3e9b13ab83c2a577c2265c6a36f5a87261759a852dd68a8caa502569d1bdd9e91d9b279c4bbe7a5c69938f1665afde8b67d72af967bf323626500be91e7a4c8d9aa45c10112306cd8a14d0352fb2735adcd212e5cc125926d96951ab32a30b28d5308670cdf9eeba2eedd7a5514ac575fd98545c576b528d627af081840e45f244b2061e246bd8e9ba40ab90122419e74905253df6b35abb7d5afa053161b418adb28fb157cf2d830862ce4c7bab751df534ed599689e073c0f0c31571bed77faf07df5ed903c41f2eb5bc795d4eeeb00e2f736353416768df709d423d35dbe7300bb87773729f997b37c77de23a525a396ed44e954847478707356d8f549729c464d593797ec624bdf798a737d2a2bac2c65ec184b13db60e14662b61aeec39dcadbaea29ccd50ac65c764b87d6e81f3a03c85a1ed29ec62d3ba89b34cb96dd44a85965f74fb6efa037f8c8f69dc4b6926cadb576c864619fcf26eb9a2ff2a41b5dc39cd9c63961b4b189bab183eacade66d7dae7e81fadea160d54f517cea6aa1ec375aac2d944d40ed9b27303799eadd2722a29c51457f090d9c6924714198917b96233dbf66920ad2a6ed93b5def54ebd597b4761d8770b71f1224c8951b7d701cc7711cc7515abffaeb436207cba1fdc2d1c6ebc23cfadda0df0d3ab6aa25ff001244488ac85c4d4ab5636f9c434382ea01472667e390eef673808f6eec565d59ed39ba653f1c8dea21d7fa9c0f871ac9759c13a6b9d3234d72b7a7cd936d6b54d3de5064ae6630a73e9802a743b08c5321e0099c0601cf38158225700a041c73fa031c81531fe0009cf660089cf6000be094079889d31de097d320f801a73ac0439ce6003be014079888531edc80d31be0224eadc04b9cda00b34e8160069cd60093a734c00b38ad02cb9cb6b0024e6780699c5281719cca00cf9cfec0374e63806b9cc20027e0f405d8c6e90e56e2d4057800a753e0244e5b800970ea032371ca025c80d31560049caa00d39cea609ce7e0fc14e0238e025c7329f0cd4f800d70136023aec2077810d8e61c0e330a16227343001982678856ab54340b388a532c60129c5e0143718a048fe0d40ab8c76915b0084ea98043700a84799c4e01ef38950206c16914f0074e8f600f9c4201eb387d02ce71ea045c9e1ac11d386d027ee294099803a74bc01b382d823570aa04ecc481c0304e938033708a040c80d31f8c81d32360214e8d802f707a058e714a0437715a040c734a046c81d3216019a73eb802a743f08c5321600a9c0601c79c0ac1133805020ec0e90fb0044e7d800570da832370da03fc72ca030c81d31de0214e8360264e75808938cd017ec0290e7011a73cd801a737c0ac532b70034e6d80c953207889d31a6099531a60069c5681699cb6f0024e6780674ea9c00a389501ae71fa03e3388d01b6710a037ce3f4057800a73b3801a72ec004389d022b71da025c80531f38895316609ad31560244e5580f3a90e46c07370cd5380718e026c804b818ff809f0016e027c7315b67910d888a760d453f8e41c3e1d05aff26c97a1d145a855dd47d5e196ac56e92294fb430c31444f1dc2a75935ab8e4a8065b800d5fe103aa4bb009186d0c96d74c232844eb00c25282c6348a587d5aa6414efb3782a4982f7593d9584e27d964f2547f03eeba7923dde6719a9a408de6701553204efb3822ac9e37d96502577bccf1aaa2408de671155f203efb38a2ae981f7594695d4f13eeba89239de27752a59be4fee54b203ef93ad4a3ef13ec953490ebc4ff6547203ef933e95d4c0fbe44f259d789f3452c90cbc4f02551203ef934195bcc0fba450259b789f1caaa405de27892a5981f7c9a24a52e07dd2a89213789f3caaa404de97d1a96404de97d9a92404de9769559289f765782af980f7657a2ae980f7657c2ad980f7657e2ab9c4fb32462ac980f765802ab980f765822aa980f765842a89e37d99a14ade785f86a89209785fa6a8924abc2f6354c924de9739aa2412efd3d0a92402dea7b153499cf769b42a79c4fb34782a79f33e8d9e4a1af13e0d9f4adabc4fe3a79207789f86914a1ae07d1a4095ac799f465025f3fb34842a49f33e8da14a16e07d1a449524c0fb348a2a3980f7691855d2c6fb348e2a59e3fd199d4acebc3fb353491aefcfb42a29f3fe0c4f25c9f7677a2ac97a7fc6a79245bc3ff3534922de9f3152c9212af972fa145acdeafd99a04a0ae0fd19a14a06e0fd99a14ac6bc3f4354c919efcf145552c6fb33469584797fe6a89231deafa1534921deafb1534900bc5fa3a5d2a32284bba63a17c4398ee39e7d361cfd6ec09d5baa30f72830abc5e261f5b07c583f2c232c2056104b8835c4226215b18c5847a40eb943b6481eb287f4217f4823241019440a9143241159441a9147323a323b322d191e991e191f991f192332403241324232433244324532463247347468ecd068d1e0a1d143c387c60f0d2334806804d110a23144838846110d231a47333a333b33ad199e999e199f999f1923334133423343334433453346334735746aecd468e1212f5fe11dade6659c01aaab1366c95d7b5160951f12601523506015a0116095a01e5845480458652804588588075629da81558c4080558e3e80451d0f60714707165b39b0c85362b1a70358f479028b3f1cc0a2910d60114803580c72028b4219c0e21006b04874012c16358145230b60f1a802784587027865670278a52501bcc21301bcd20301bce2c3045ef979005e31e200bc02d400bc12b4045e1162005e195a005e2152005e29c281578c6ee095a30460161d2530cb4e1298a5850466e1410066e9c1c12c3e4760969f1bcc62c408cc02648359820e8059840c8059866a300b51c62c3498a500988500b86500b8c5066ea9815b66700b0ddc22835b48dcc2c22d4075d52f02b710815b86c02d02c02d01c02d31b8e5a8aefa33f00b19f8050c7e1103bf1002bfe8a9ab3e00f00b18d8c7cb5990029d1a185fe1d2a788a9993300a6b764318be8d378875bba18d168cd00b818e566a1d19a33a7f76780e60c4beebb78e1962a2cc4d18469189f09d357619520b00a1058a555572aaa23132e5bde50b6b421f749328472ffb25a658d23658da072223143ee7bb89461945b728a4f1c6e2943e748864e2565e8b082c8d0610541f1e1563c338a0fb7e652c6f1aca15ff70ef38e3bce3b9e51862ae87855eb8c0beee8bebc0c8db8a2175c83a7aeb8a20e0206102f5c50108b0b0ae2e24e2f081840346aa7555d102f35ee25081840bcd08cf271d4aa1317adea088cff282ef1bffc6594e2cfa9e9507ab8dccb0ba85b885cc2706b2e657e6af0b4aa5934f578d6f4b70457e332033461528aeee9a89230dea556ded3118c91d5aa408cb3ae5844fe70672e592d4b6705b9bbe01f1dae46c6a7ae582d2f73b3e6059e352a34b8cfa5cacf4f25592dd6f8a3e5aaf2c36afd604105477265b558ad2bfc0c2187a5214045372b396f966d5ce76536f2cc70ea62d52f131373040d1375d538549787d19aab6b0c2ccd72d79e6dd6f52141829c0dd94e37bb6ddbb66ddbb66ddba55d9bf64bbbd5a6b6a5526eed33cd5eb68380e40e4a8081d1134860e023db3b619b647bab69f7da399ba8e4ed8fb844ec1b1395e44629aeb8dea3f86be481a38e52d09a6d14af73ef8e8d4b6cd74629ea3705cc9a6c9482d6644bf4bd518a2be85b5579e0881997d0be8d524c416bfa9aa6699aa6699f42a6461b65eaca8eafaba35425a790a9b9e2529dcd0754d3bcfaa35d3d6cd5346f07b597529baf5f3dada2d86f67d726b93d1bcb5a6fae8dbb4cd3def7b4acb499f31171ae0bab5849cad778d9cbd26f4113e6da021804dcc1a59f130608a7076d48a597615dddd7f55a63ac56e53074d137a5da785bd5d6562cbb5766cb2ddbd1ab503851828dac651d364eb30a25f6d2be5e572a63187655cde68a5bd3344d1b675d5525689a57a9d5b07b3b8f342dbb75d5a79daf2cd677192442d76a310cc3b8abe28a75b3c11e0eb7fb74598e5a7b599ad95f17b5f6d602a1d9af6398c56e2e5afbe5edaff465ed65eb088453934fb341ed6870d9157bd99c5ed24dc7f6baccc9a9bac26c3d21855a5d81797e386a137b6159dba6a51bdc3ea5252b6e9f963797a470fbdd6dfbbab26c6e712f3db44629d6e6b58dda9859cc5ed65abb021b6beb6b113a29adf6aa576ad27c9b3ab9c8e2f6359b75786ae8e6ac3169538cd119353a5f57e7eb74a433eccc75691386656ded2c7a787cf03c2ddb7a2dc3e6b5399f8d76ceb2c79d30f3f35a8047b416f5f56e62184531a15dc75c6dec5dea70af929e5651e735ce7a8a4333c9658f3e35abec71d5cf59a6206397f66de346ed16973957a581662f4cab40304dd3b06759a6651866ebadfd92ed2805233e363a2fcbde7dc904354ff3d4eb4231aacba4626e555dd749ebdab7908e948a955dd3eebd4cab2e6a93e1fa105f9a66d1b4aa7732ea03529fec3219a669d5f51429dceca58c8deb1c76f232158263491ab5aad3aa2bcc32a1aa532f1fed23c32da711c6c3cd5ef64ebe0e62ea3361aeef888951a9feee652a5fddcb3b8d4863a9795e0dae7d3983a6922392c0b104131368c28927ba13c66ec2d841dbaaec21d65a956570cc48156f44401208d6fb0cbc619865ef2c9c390b33bc8507ff7d59883dd0620ffb943449498b7819be0ccdb4d7295fda58a6b2976bcc514df466c1d3869d1ce68984894e9ea5da632d8661d895eb965b02b3b29868555327544c8d4c18244860b2689149ab80e52c869a90bbaf5c72c2a53fb98eb40485ae743648274c1db3e84a37b8f49396acb8f4938625293496e784e91e74b76fbb354cd3bceec32d53da53cdea34ed1cf6288f9baf6c2c3b67df6c145248c1712f75ecc8ad23db973d726fda6db44c47282c1226180311c670847c8d39e81bc85ad1693f6ae8b980d4208650480d5da801c864b5fbaee9e86744cd77c2d4ccefc674d574ad5dcb1acd97ced8caa2070b408a64ee5c25b5cd1b65be25ba310a1c3b6e4c373aead5cf886de4ae61ce04468cd8ce9da6660d9777276fef284edfe8fae1583a53c03961366e1471aeacfdcada338d288ba2fa0d370ac07fd7bc83a34c5d65dae7175357d99fc8be438a2b67a74082b22a84063ccca29c19a581066828ca25fd415b99ad29694fcece6dda11cd2a5f7034ab5431d1ac32a6896695134912cdb23b38983c19dbbfa862c4c81cc076a53f2112184bd0902d694336870c0f591fb22c8341dbd075adba7376731e359944d50b43435160d2e39c4c86eca46ab651dbc32db552b5dd5e653d5a27d0e4ecf6d19f42abd92896bd3bbbf014a99a952ec27e7ea92b08b76ff1106d9cbd02ea17a19c7db6b4fcfbff81a2e87d04bd7f17b128969d3d71fce7b9b8d8f0087f3373cb81c8349fa04e4e545454545228a9544a4aca69da543a2d79423a3a3a415862c5121e2c11c235424a092cd93b05e22b7d87f9cb8fe83d7cd779a82659448598e6efa549945611a4cc399dcbe0eda8aeb622f05684d2aa24ebac91f3d12bf131c6128527a3f464f11d915689016349b70f092e6bdf0d865db3c1342c7531acb63eafbb76e88f7a24779b92cb8d654ea73585c14561d08d4591310a033a63fb6cae90662b38b1ccce5a16ee3f47d4553d750255c124b57a017a84e2a08a7ce1309bf8ce5d199a232ac97d5e776a92cb744aad87b665444418868d445c6092dae4b1a389ae6e916b10517ea259417235dac9b5d2fa091c0293f5a615bc461dacb91f3fb9be0b6770fb097335aba06de186217586af4145663a3a3a3a466aa676596bb35fb6c3a1393381727dcac7ed24324f44b133b4a347b3ae79f5eb75482193657e7cd43a6776cd39e7b47376d381091d9ad0618b4b072e522a606920ac8f06bac2fe54332f042368c184a93a795abcc06a5185d50207560b1e58cb42792a59c9edf521cf9c994059013375eb1ecc90ab9d70c8f5f4c3e1860bbeabdeeb65682ad91d9c4141341f96c1317385dd6bb27b9d424e34f9595718b6c2541328f3d8f1cca389fe018e6823ae3c4323acb73a843de7fc8b6cc4aeb1bbbbbb31da5974739298a6655a5da580e6d8a661e86a7777dd42676da55ae8ea290d43add75fd01ee79cbd825badb5d6d689a4d65ae93542bdcd3283ad39e7a2ed02ea69ad9a359c307d16d8b062cfecafd70f0912945bbe9e037b4dd77493769661bf3605ddd40dd7e754b16a4c554a265553007040b6cd650c0a2edb6daeb0d7c84e9a2baccb6363ad5d6bad954e0ebc46ca75b976a143049ae99b88b5b55e612d45c2ed1a5a63bbc16cc2744d461b8a6e56b2e697ed9a9c9a02da94081468d6cc22bd42927fe8cf192e7d97d408ad017242a761594a4e65147b79169bab0ae61a82eb943bccb3d8944ba49b3bcef3bcccf33ecf62afcb5de891bc8ce459ace5aee4599c6197cddd098a97ea4415d08ed784a9c77235416e3a8bb0974ab3446b83eb288f2738b8f4dd35b5d6603ad405978efd9933b4fb74b49765e99f39d352bbb10f89faebd7916ebee4a654e47be78c15dd7c292fa3ad1e554b67dbcf469be49cf4e8199b7409256a1693f959be7a7450825dde9ca4ecd43baa4d9a3533f6595399c55eb7b054ccee97e90cd555e522632ffb47c6eed5a14a56a3baeaa1fa3bc96935ef12b3db777d13ae1b6b110e01cdd84fb4a083bd4cc918296323f6ce6a586b15f654ea5e9d8c5db3a4ed2fb4c676ac1e7b95c36471c75eb59864782c44c9652d7a91cb5a94b18738c375aa666807b1f60f6b156baff579c355fbb6d5daaa4be8fbc2494458cde26c5759c6864dc9949e0e486610fd6ca6f699ef78b1db5f31c8c1c7026c5b6bade583d5b4c672726b3755d65963ce19deb154aeb69322733d9487b6e8ce1117464593680e4d2519c3b0989cbd8c91310cd3308cc33ca4c85add308f9ab5b1ccb1a50ab3b1182e1ba8798c8268cfd1142a5519e31145ce5ea6804796b3b1cce9eb164b49e28622b9df9a92a9f9d01edb456bd6794dc89cdda359fda3fa3ebd4d4f7ab273679d535420adea7151cc04c6c1821b02ab01d18ca5964f534c68406bd0496bbd2663bb5144027bff06b3a51479f63673f2c434b5cef266f43a424fd93b5ab557768d1d51ab6ca6b53adc3f7678704bad5e6b6dd77575776f6f4c53b3f1eaf1a80d52da3ce97bc76372559d5cb690dcd3aafa0c7790560169550f6eadf7f1e307b76d8581457279f7c6bb825b6af9a77c5d9ee7799f8077cf3b13b60ab406cddeb5e2715f67af69df58de9b4a7163f9ac4dea619887395c4691b91820fe7069b3f7edf3e3bc9ccc6dd8350c84874ec5fb90f013c7f66df4bab8a59763625eed3ddd360c630fd76ed7843439abd6b31181d6a0f9b2dea59f1f860fd02bfbd0bba5a9ab4bd3b42ccb0e1ed384e4eb3435b37d517b599109651e1f6ee9cd67f759f62dfbf1ef0dbe337c805ef50130bdae77e3752fc31b2e7be40dcf8dc33979c33abcac8d140674467d86696ab67845a70dc3e74b4e7171cb944aa69253b572394a7b878aaa791ea679d27715db9011a251222b68c7d1871d9a766f2a65d4444f251be568473dfafd2d6b85e5c1b1041395e462346b356bacd02369c94db51ead6a6c7291b7eacd3b4f5345bfb2426b6c73468c2ed3972d59673ba0b9b726a7a3be6e93eb13b8cec4750101a89b2fa5ed3962ced48a4da9ca4ccc4ed3ee4da5fe36aaadd1ba8448abaa4ff5f9b9d427759f525d2553b2c22d67517ec228d72e9a45b3a8b3607849a5b149463ed7a78d7c7ac827cbb49b532f554b5a551a499a76ef0d532923eddaeb11aacfac4598e4d74af8c6ea03855bb6cf916615a927e2f3d3ac1e99781b6924cda2b9d227c9f5b6c72379adda8e040e4932bdf6a10f4ca19cc3de7bd945727d13e1de79580a27723f91fb03532897393f39c7750c48ceca9c9f8c6157566263fdb163f5a944c070772af91d7c7dfb54121cbbf452f55b1aa78a34de27e4469f3a5457b512559fea33a4ddd45f7d54421688e5b1ad56554ee7d6a34a86af5a0dc718709cb57ec395481daa3e43651d1aca52f729d5551d53bac22d6fbedddd6d5e090bf7b3d69e133974f3a59b3ef2e4549306d59e23ab833c70719d6ed131f2cbcb9c4725eda13a514851df552079722c15e4e95d0a0d5f9c7dd6703a63141af66bcb70ea071ae7d16583c36c9499ce4ef952a3a8ab0bd32d82a2a8b5090f93d61225b9569f6a73902b1662c0936badef344f6b419e36c81cbec2bee33cb00d17e85242ccbe00eede69da7e4b70f746d511eeccdebf77ff469daca8a61b45999a1e999e4a52a09e1e0ad433621c75558f6855cd1c15b7a44040cdb21fc21dab4c78b8099f9ed9825b52a00a1481ee9cf70eefa8ab7a15196c7fb8b4d38705e1622305dae25a2b6428525480e25e215fefef6656815261aa2ad084a9e77edc721e45d12c9d9b832640d9e6691a335c9e7e79ef1fb446f7eef71af6eff6df58a24e428da56e2c69f721c165cb7d37f6e1bbd9547c365eeade316cb31bb661de67efbdd33e1bafda994b240d48a6adac85fd43d3b4dbcf460b8570c1b15badfaba91bbe5c6f23bb6dd6ee3f6aca479bbd1e5ed7aa7b27d36daa54581353b7291fb5be43e4d6dd95cfd8d677b769ab19757e96596cb8ff432cc53bb93fc480f444d163759d9646193d5cd99c4646d3f9908502200e9f35d00ae2cd3d359043da9fc322dc322ca2d5f19cb1ce088ba6acc61d8e221a5316608694c52c76b046d1078ed1563f984afd828f8d95cdb97444ec557ceb08c0d4c7380234e18071e523a77bac5434ea57726cc4417d10f8570e96d0d0e144619c32d6564bc8c6218ab78c83dc543c2a3b08f2bb24ff19a2bec4fd8c715daeba7788d69bc82be8477b41dad267ce77d36553bee3e9b0ed35c354c839dfb6cb8cdd21cda839f5a773f1b4ceb0cc3347f1e4e556a8dc828d75f17c2300cc3300cc3300cc3300cc3300cc3300cc3300cc3300cc30fe803029f00c280b6b52f383dea8891ed582992564d8ae4b2b60af6baac0562c256c15a241609bdc54d87ee0e855c1b41300cb311e4b291031b3c36726023888d202eb87cd0a5f2d4a62aecc1ade9ba0cb320bb8661d967cede9d65d97565a7e9ac7603e96677ddcca19446f9a9852022225e8e892288688b4c3f41390891801c80c4618b4cef358bda4cb55645a09b2fe5bcc24f7782c2cdb6ecbb398da573dc3b941f5cee945deb5ef0d4430edaa0531acb9c5ce25459e58706b764b56adc61aafaa1d13da98e68b4e64cf853884d0f8d5a05e11db873dc4bd5d1492773a6fbd2c5c83e849901e24e261d235af269e4aa0a70596f6902a35c66d771b57269c88bbfd4e8421280c23ddc527656ab5998fb0bcc62b5a8b8dfae4d2ce8e421d79314ae0ca1669dde97a144c650b34aefcb58d22c0edb7c8594d425ad6aa256554ac19ddb7de08b49860704c4e13fdcf2ce5eb54b61366121b2f8d25f308c09aff2ca65e89cbc7f3a3a21914a2de1103a43e80ce1e3050610f5b3e1ae5a2bf9f218a777ed7181d2391db1b4b8a5eae874040308964fb36ab3c29b9a4b87399a33a6b7156a4ef99c2c17a0d4617ce60c771942b8868b15509a35a70ae57d161942a89f8eb831c56788960966881637727731c2308c542f8cbc78c12f4af885d08b9438dcd245c845a892ac56a9ddeb22c4877b7fc28282c2a54502561d95970a72a93a2a513f725f54f1aa77c273083e8787bca07672efbc069f6ede0977962143a7553e4eef463be4ceccc90092b18c1ff393a64d3eb764b558ad6671409a95e269564a0968162816e81528126a055a054a050a44a740a540a3408f5028d02750275023b40994097409b40855024d02fda146a0442811a80f150215427f407b280f68109a03ca43ada040280d688b52417f5018d01d3a05f5415740755220850982509d00053954059405b405d405f405340654067406b40a5a036a037a038a03aa03ba03da03ea030a041a043a840e8116e1fd1a3c95a457bc5fa3a792f408efd7f0a92445c2fb357e6a18199a3328efaf95c1b087d079d7711c07e6eeaa57d36b5e19783a4d670193e02580e2f40a7804a748708f532b60119c560187e0940a98c72910de713a050c825329e00f9c46017be0f408d6710a059ce3f409b83c7502eec0a911fcc4691330074e99803770ba04ac81d322d8895325e00c9c260163e0140918c6e90fbec0e91130004e8d809b38bd020b714a045be0b40838c6291170054e878003ddf72ee52721ca49b851dea87b93e9245c9e328954de7c7a1f85290f9a4d46944cf87494d33d914ea787e5cd27d34fef4cb8df99f01d454033ea33df37468d3c7066ab4c9fad32a27494c9841aef4da77b027f1a7380478d39421da77c7ae312248d27a750e534962927279f12dfd7754894a7db9dbeb14441ee7e75a7db75b6c3a9eeeb3a0ff7a036a4ce7bb7c5dd2e22914ddfb987b71fce77eefb4c1f6a2c7332ea1797a77cf3e9f366d20a0acac94f2f7da5049e980e1e65fa34e198ce8d37fad967bf33e16b3abd84511f4ee986f4d38743fae9766e5c82d7ee4db873573f9c12291c4973ccf19d5ec7297f23f7797387a76ad2743824559ef2762fe4b87b397bcf79bdcafee12cec9e48e1d091b25fd7258476d0344d0bed104514da65521d16e9deb8bbc51d69bbb66cdbb6d6b47b53a97fe39e2d00e527a7df1228e3c9e70782e0078e22ea208eccf564ee1d0ae368d7c63961b6d1f48e5e1486e245de0ee29b99b79c2164ee98bd3c14c6d96eb61c2299fb69db727c323796e1a7fdc59a778227cd7503c3bb55d2957dde0ded22d3508b1b6a71b7b1c491b56b63a9e3656ad31163ea4ca81397c07e7d01b96ba55d88f55dbbae7476adef4aa7bd6e6c9dddad8591f80de59bca554efe3db5d23d3c783a883d534c7f37ba87372108822058228d60aaaec2b38ca9baf2c297aaec61af54c2397c89b3179ee47961e879e196bd52297cb965ef226913c67b18862727855d77f0f648bd366d4d39953872784fd36e2af597aadc8da578d3dd338207ed0247f61e2ed172962fa0c52b9fc8dd73a288a254e2c1a354e2c183078f52a9478f52a9478f1d3b16d07296d174338f68227b2f9bc8dd2fa65de4f9dd5124755e479b84a1789167268d10c8de4b08e4ce5e1e09e3a04efac53728efa409e4eef5da9046d358e2c8dd696a189ebcc365f7d378f270cc7172d2982345474c0e1f934f6256706991a0292a9f37a27c3b8a77f01d88121eec4094b0f3405114bf6111044110048dc27b61f82ecc89c9e03d5ce2c81eca4f4e2cbfd9f37e721a4b606a4419cb0b9ac6f2e04963a9ca153c0ba6a9a58e981b8621781386231863c4c591bd7b770cb5b8658e91ecbd7c227b21f810779e38d4e27ee1186a71bf97315947cc3cc21193a1a02a2d16d963bf2edb8dfd0d671bfd6eeca0a91bbede592ebb87eda665d865e7c071dde307865e48fac0df0e3daf41efe3388ee338ee86dffbe3b0299cc36d9630fcc2ae22dbc8af355c5eafc5ee39b673a36da2b51013cae1844a3d051ee527295fca0b8f9ba1107378a3e48de20d6dab4abf636982f730af543ae9d6f4af74ec5382f4d6b46f47fe3ef0a611c61eee37822f8d39c07b638e948b7bd85c7aa74e4af9bcca494a0a85727282429d3b0a45fa48a88f443a9d4ee7f0894422914824395ced3b728bbf78f68a04beb17fdffd6cab4eb787cddeb8ddf9926a900bd73b904b650e6b7f97bdc3a51359d3b697292e4c004e3e61df8dd9aa32897c7ae9bb31274c89934f47ddf46d2c8fc8a79bee443e8d65f789af9bf09c30f625d2b50969da72df3a6e2c35562aa77e823bfadda013863b9d30d5ebacd95669375d37ce09c38da51359fb36ce09a3659fcfc60e01d5b192db96fa1a82c3ddb67b29e73850e5df67f8772146f9c538276fd30dd24311d06cbaf75e53cd263c338e0d4f71582ae1f0f33302f5d269da844ba59fe0d2f47904a48e2aa54ed3a5dfdb52e92794931ee2d2891c1efb94b8ef52a9f432951fbe2be17e473f1b936944404e1ec18b5c1a1bf512268da578c3f412e98d4b1135e298fa250d64d2677622b789844b37a1c6d2583a91492f4dc74aa99370191e6584b1875b7adf48bd34e6481d35e638bd7413d6b1128e3a5672ea8ea58e957cfabcf89d3c95eff394f0ce9da63def86d70bef4d4949398753eebdf7de100ea4d34c227d7e37e8bd6f1d0987db762fc4e54a0e5fc2a513b9745328d44a7f0575574e2f1df51554f812c67ec2a58e954cfa47226ddfc6d20b6f34e6d9a675da9e24d258d28bcb947c71ee8733274c89827c53f27d376b5b3a2fb0749aeed24bddf9e2f27bf6f0a4236686e1870be2d8568107df59effa70e884015932d86f9c95ec8ddbf1cd1c9e4eaca434ca08a6ac9d346b787446bda669d79ae55dbb77e70c1786e1bbbee93a9ceff737dfcdc9dd28e274bfbfe97e9f9331bc75df67633a463a8d38d8bd635eb97d1e1b31ec9443ace578b446f7eae18d346760df4c1933957087b39dbbb977142f8934ce09133e27df51c4b927fde69ea491f0c561debcf7e78d38ddb777da36de09a39d1bef84c1b6679e8e8cfdfa1cc15c4b14f3f91bb2ec5ceadaca8b8b576179cbe9a69f70786ac22724507eba498993a34ed3a5928e5c7a1762ef28ecfd8449228e481a6fdc879f594736dd87d87414f672dccf8cfa099bc652472e4dd771ca77148df80e1ec7081eb48bccfd1bc191a63b3b614e2261d27b984ca7e987d71bc36c3afd8ea039fc09a384e15934ef7435afebe887533aaa3beaf3bb61fa490434773f95a752cb272727ef428c7a8769eee87783f47b7a47228d22e9220e78ee37e0c88d3999348a38a473bf219de3481f683f94879e1762ef441aef84391d85efe8b5ea74d497c48b937ec261f813ef84517e7d09a0f9c54d28a8170fc71c2f4e1a73b03c2c759c72f879142eefa8e3945f8cb3552b3f61f97c0b2e6d5679fd90a00fdf1f0ecb685bb532fe525149b9f6f9299192a2a5681aeaf744d350bf299aa6a14eb47b53da984a5dc329ed5eeddefb4dbb9af686d2692e3d1cbd538e7b56e29ee1ae84bf2c3bc1285ceac8a887bf3fe179f3bd9f1de293c0a366af4e63d9fde434f504cfa3caeea60fa7f46c2cb9973ed4c3cbab1f0acacdecfcbdf4ce7e38a8ebf493df9c48179fa048b8b4b9fbfc4ca6109bba11e7e4e14fc6b1d491effc6e50e39d30a58e532e73c63b612e387aadba63a9e3944bf71e62f034b5843bf78753f3f712eefbd008da05ea9d88ba8863fac96f4ce39c30a69fe464d428e2a07ef21bd4c5a5768267abc2a3f06cd5fd0997368737e1d2e6fb913eb1f71269c4b90f6fee78278cf7f0257ba769301cbd56790747ef1b7370a72fbb51c7297363b6e15bead071ca50d0963e3903932c234f2479289237ab5de3c6926a18db71aedbba1d3dec8e1eb67f7cda7079dac303123c1841b24513d33714959f1c959203fc3d78ef1d88bf7b38a4bf4bf27d2ac91647f2efb024d73b28c9f52b2fe2c3310441f1033dd2455c963e8f00950ef3404cfafc1240b30a78d208630f1754e2fbdda184378addef588a374a277de61da5d88d38a571b68a34decfbc235750e5eb6133e9e10d9593c61c2aefc61ca9df1e36df87b804c71e36ab8cb35528f7529f4f41a546db2a94f1d7ef58a26e5b851acb1e957e4a84dbe9db690befbdf34edbe681db16fede3b6ddb16829bf7d0336da3c9f40d6fdebdb1f4ee9dc33d68f6b6365c11099aef69be0fc7ef343c97bb3c00c931f2448287a2dcb5a7bdbbdabb97ddefbb8b417a0dc91644b2f62ec4e58eccf2d267932b0f3f7b2cb51c9e74163cdbf3be8267afbcef212e7bd8ac1d3ba86963d98120f81093eee1f2662ff43c5c7ae0785bf581638eeeb4f4c61e3677a3c6cd3c752c8fac8b2eba30ca3d2a0e2aa7f4a793615956eb7551fae3baa86845a1b318bbd6d32a8b6159a65df3c656612759b8a597b18766c832ed54c3b20ccb66ee8464cd276b716062dbe28dc8a66d1a76ecf3d34ed3f5f616d32e0cc3ac76d5d32f1b7964b962f6eabe6cedcc0bf4e7489123479e6084fe3ce1089233a0e1d302f4715dd7d584dec17ed894c7a477bc35784c9a7c7348d9eb695713f46a623396d598cc46a7565417cd2e8f2271e59d2bcb3aa22e0d1dd1335bb16ba39c10d882399bfab8ae223f3b49e8326d1b4beda6c700b92eb044ba21d7dd66f6d8f5dd74ffde5ddd95fa6c15d625d20db9cd06c9f4a0d5beb780d8a0fc6164b89ee5d16e74b96b5cb5cc0a3ae33afd6e582bb86eebb4aeebc652474cee7e9da6dadcb88cc91ccd4695b5e2fa6c2e21ad0bcdf3b6efbbb0cc03c3f0bbde4622910f998459e4b08ab0f565f11165ea835b92e92707c42161615c92ca097143995a10a553e2f96cacbd78ae9c1a0de7d470479733d2ac22adea5aad112be852d27d15c9dd433dfdf212ba862a57843b423157a45908a841930a24d352a58a4cef4d963da5b4a644f928513f32a5da4b952099aab44a54c82940a92a326d654a4f9e86c525a418b50eb569373cd1b21044f1b6ecc3340ccbb00cfc529e86736a3aecbb6abaebbb61c9768876154c55290b202cd76686ac0d210b9f09b30dd51e7db621faf2f2f93e1b4bf18abaa1d5407ad55d5f60c274bfec960f918a5dc0bb2e1f1736587b33be2929b5535252522c9d295d4c11e891e6bc30bce79d748b4b3b9bb5436b64c77ab4763b8259a802a8b4cc7496554d32846646000000080800b314003028140a078462c158349a47d2623b14000f8ba64e684e9a88c32487510819438831861802000003020382d90600503c869f77da58b2d8e5bd9898327e8c918d794c7a6954ec9e76207a2f51458ccb1d2b8dd94ae68a468d93feb6d571b5dadabdf5859cac9e564ba554909e68464009e1b6f13b95e963b6734ba22adce917986d926e0129f1ec9e0a8edf7204c6295aae04d4219baea36fd080a5106eba8e6582a4d753d2fc0e197b40803a8321eb6000be3de69b80086a357f3d800ba983a3916f0cdd976e3e2bef696330bf1ef7f47e61abd500d57d43fe1ae466b71860827033a81b36d27a5c64ec8f28c75eb3d8f94c3d7694b4fb16f3f335e533241aa22770c8674fc301bb0f210eed5c859b12dbe8f04bca102ff35981e6a92d568c6db84cac238e0ea179ee0ccc5da27d99eedbb9a27d30f471b403d34e9d93991436f8b671b60f5e847ff04c7935fc98e76368ad68a5e359d8529934f6eef0635c608720664ede95b5d65319e76d8502575059b84684727c0dbded33bdd229e94c3711f9952a5cacdb40e11f8029f61429f16aea6e2c009dc7ce32e5d37958806ac5a3f21de4fd0b942b20cad2c8c10009bde03e0f284440275290a9ee3c82620a57b60d1a278f6af13ba66abfb71ce38835c0fffdd00e35f2bece0096f7034b2a947828aa901843bad0703084c4f197504b49337988c5eab193a9b0bf88ff26ce2dc38836c269259aa9b1f963b649c6bdbdf55be5320cfad262dae86f15439c188ed75ef37a7cc8c6c10e1662cb3a46112712dcdd24109689847b49ce9e2e92b1227dc340f1e6d36bea793abded844b0c6e084949a310e1abfdf8b382b726288ff00f87d5ca0af0292c008a29014785287c00227e1dee833ab4070afdd93045e137732fea2266437b87f4f235ce89ad542ac3b054317666fc2405c455278d63cc5fc1452a04481245da84ab013efb77c48111c3b711389cb6d88e28ce4560cb715ad227b60ef03db7af5d18dfcfae6028b0f3d5c4f5c5c7968c3ee92ca043669dd5e3b2ab1f10f8790f55af17a5e6f8b88edf7d6e7c931041e4ac892282ce089b4ab2330912ef7e8f9076d914c858a5e12b908d5bfe9342d23460792d86b0ac159395f0dd319a2d453d9e90448113d702df86ee4b2f5008cd6d1ec97ba27dffcd7e4b7355c985029eb12868015ccf0f648d008409385c2fb2e6c049c80d6c7418155ef1b3443415d666e10293d2d8412a341031a5c2926648e0e2368fcee2d5d892dbe36b0c870c06d26e6d0bf27ac9791c400adae3df62b1678d897d08b14ac132f6ba9126c0f62fdf9c4c7718b51df5f2ea6b61a836fdcc24bd8245f79ae6fd1c267ecf1659cec161be71661942265c988b08eca1c481a5821951c761a2680ffa1cc348e2d099063720255fe03e13625d81ad0d77cd534d1afacb65b554a225a033e31e9d070d1e4a1211b83e0196414a10009e2cacd3b60b00d079be1d45163c178e5666101242fa1be6ddb5a4decd62e4d0606575cc48baf8213bd033ebc5d98e319ca32555ee090332da639efddb3613c09bec3a935e9c61cedecc312e0474b0278436cf609f7f80643279dbb844e3c3c0754c30f00d49bfbe20a467fbe1de1838f98ced1739d5a60be30705d3ffd6f3f27c429617110bdb311b7017f7305d1113e029c8e4d0c9019d057dae8fd6609e7f559ecac4aa9135832034a0f40728403f24645a3b44309746e64a6bfa48f1646db3bdbcb43d6075306fbf09846d0db0bdfedf64eab083627cade099169bbbc2e41e2247e8c300ba30d206c127b825914fd1d3f2ff65408dfea2fcc5755fc9732afa3b6b6f5b85982c0f6d777fd9a0e22f4cac17b9c85964023610d0f09db6720e8dbf758520b8fcfd4674d295097eadfd7cb07b6cff1b7ecf275280d630dec4dda0756318b21343a92ace23f903b1c25e1060c2cb039ecf9bcf2f9a98081b0e8175da0aaefe338642636913685f43d485ac8703afb54244e6ff78274f0801b138beb43e9273a18b789652f45792ff9d04a3e8fee3fe05aa0d087a0bfbf6f36361aaa488b03815348a02cd50b36a244878878b9c2a8f8d76659f304385b101035a26ff7a23f74ed5ea71e798c3e083e4b44dd590a0c9622a24c5c55b0f28d602371fcab80af2a1427c2fccfbdacff753c9f19950806df127c509fc49ae72dd2b8a4970b866090e5df03af6d7fad53dc0b7d2c875f6239b02f8cadcbd717a2a9b02c7d749ec63956cb40442c6cc04bf90d6d2e285b25b3728d72e0db8e6978a7ef443bd0b80f975b516d028d40184b2ea7c4e2bfd6a09bd0ca0d02cf0aa9757864eb4a860d34dc2ee2c1c32097838941c4215566f2a27c2b1fc8182c1eabf7a87972f1ffb6482cfc2edcee0bdc3ae681345b1d17e4e0b8254ac6e1c8de98bb0be1925e250a8982df60e6f2d1defcb80ed3e6503589f94f631b6692c52bf9c0816e4c05985e0193dd97a123d3f631b8e0dc48616e18b6d7f4580355670162a855463cb8fcb58635f412cc13cc2c5ac3a5a90a1f045a1854a21303978668d751d3549e02980f9562fbf8bcd631779cf06936f6965257cd5420b890d5a5221077b4043f2b2ecf0a641c342a56c66f9713d010b843ad2d698cebc2023ccb25680f14ea5f9d30bd2843face046dafff8d3982437c7c659a814fb6ab6be027d0b95022d9e9c8aa4157945170d8ba00c5491b1664832a55871cd9874d2927bcc17fb749b08105982e8b31971631ca3d82599571bf41c5a97281a973413ad1f65268b908c8ab2c61e7342485e49848a709b1554129378625b61cee948df0bd15e68d7b27ebd71b21de33203f24472e4b6348a8fc9acb3484a922ab67afc132343f09f46b2f3b8591d146c660b130bbda6c6a9126b24c459ae0c138b884cb08cd63e006c69367b7b4b994bd1f462ef2434167a398ea923c80f89f7381037eea1a1979d5ad30b4723a317270ddd70b51ba5ce5bd26ae9d079df440b508af106f3478762c1c4473ce96b997685355a1b89106e1c8a05fdf4bfc5cd00523a0b73f8eedce929319df7da59db9e5c0590be424395859dc756ff6abb2659162ef33673652d167d6104e3555d612d58d248e554d9ff6ab8bac5a02146d5ffe283275715c15407bd7f0092e1c413ea7b2c7e37f50e34da485278c1a7f8190ad65032e7c35741a46dff6111212ee6db91aa52175e6cc1007b1d84cf1cc86211bbec65b1d0e3702c61e21ca3c41dfee381bb3282784cdfd8853b0b6ee21d8bd7fc09324e7429e365b340a43c4d9742e892c4091346299ff8c5d0569325610586da2ceb98bf9a679ac84481cb29d2e3e00ebd2dcf038bdd355fcf4d7d8a91060e15375dca18c8f8bebc8f04d8d382d9abbf83a5d28fd02853ce4224c22dba3e37a97c9e03f6f26789bd1c817868a90ed091480285aab2c776306916d04ea7171ca46160b711ecba3feeab12339c5f19762269a220ae22ff4e84ee5c9d9ceb43f051d7832d9a79c15b9812887c46f901cd04581c9ac5dacb6a15d391318d0b794a5a302f98dba4c149161df4bb18f2cec91a8ddd455051c291abb2ab819a298b99e3747ebd7c2de38433ef6dee61773b07003a8744e615ca25516e303c6ec04f964435b7a06b900bc02b2ba1f84974c21a1318ddc04551f8261b37bdadd51051ddb606100e034ff54a24e05725e7c97767df0b901dd1110fb48518e50dacb9102d8e668dfdc8e28b82e24bc776e610f7b5b5c1140594ee61d27dd717a7812ed8e1188eeab83e0312d010024547c2b2c061c1eee05756e2d82fe20f36d12c85acacf1c220959b11d035be21f0f3865182fa94437272a63f6a4a51d07c0ad126a5f399237403236c86b3a09ebb2ff20a580c37f2d234de1f5ef8c35537f800b1f1f49f46ab6825852908220fe615a81d1a060335427f18a279311709364bb452a62e0f98877f862e5a8596a5084520406d008f5ee48ab7cd8be4089290abd720f21a55e07e5d051c1da782def99060326dcf220594e9de1150d0f66fc2e4a8ae959123cd2a3a61d0551cb4706d27748a420fcbfe638891acaebdb852d4d302d9e4a47bd127d75f732a61769759951c730f873604485d86ba61903ec7486554fb7f304cfc6e13210cad78265146ef329d9e0cd5b92ddd5b322c62a3473fdafa46580a672e762d866657d10f2846724fe5eae4f93c23f38b50f9565f8117f262a82720e48e5026ab2226d28123626dcade9cb507d936dd310f2b63ee6a9845361d002fa9e1ea6ce31b7012bde57ee21dc5c94a1e637679dc91633236b10ee74b7427f27757356666236d840e033bf28b97fccff1f14cf34c63909f4b29ff01be249b94fd648737f5559188707aa4016681d95bb63dd06750d4fc2e3311de1fb43ab5965896ea752dcb3f35d667767e80bfa7c114676833932e2d787ad12d7777479b821641075dbaf6e18f182525e00e7afdad4def0570b02c26cab47834c208e8c05e0ae5144e39bf2c3980bbaf8e4c922d5ee3f62cd845d9b394a0a4968af38b4d75cd411692529752659d46ef5dbe976332bad87b9c331c54349faeda4711a0df9f94aca290d0b01951818e3ae01d8b6b260781e85f235c5d6bc15a81f04f9658d4812d724275156c5d152353593289a4733e12e5bbdd7e81ecbcca75d92d40236932c8e789d435cd9b8e48de25c56c22c8a972bfbfe1423666186e323960ab75f172a66a55eba17b91536c64dbc0a117fc6b464fecb01c4143ca9f9d6e66390602dc26e4271884c9d9fd2989efc1beffbd6c9b83818f20a89628141831a387317a74483ac9109a7a1cadacd902113b2a09936f82837b08f15c04134f429be0a53bee3e6e8491805dd18f48a289524ad1f7d69c649ab80f5878b2dee5cd81946ba69c565a38ef0d0663ccd8bdf42111062f1291c4983ccafed54a65e3213be1ef360300c19a9df86a3963ec0af59ee8b4065b859aebe0cfbd84292cb7cb357d03a88d4b942a7c0145376564b25bba59dbae8ed1a6967c4b08233482ca9043010647af23741f10f90fd214ed45052a3ff8b463c6419c07528773cd348a884df8d411f26753f79a16b9285850b98949c7b0d003521722119d3999dabafb6d7c2f68f72ed2e94af2e3e0090a8ed724d3f2ad1d66c48f032e804ccb564abd8ebee5c78cb63ecab1c5f42499e843870377bc25902a4111ffe630a2b595475b066499f0c3bd74db7fb6656e4627f1f40c9aedcf0a8680d7161ea27b5f66c49304f2458a6e478c8f65fd27aafcde38183a3a8add8f0f5e4ee186bdbf7268a27316dce7995bf2b0ab9e967e71f4bd5210ebc504159ac38d2053e504c562ca095d1782d5c9ef9c8e3d81066ad6bc587542bfe078fad9710c83a2f92ee07660f8e989f327380ac87f6e11c0ed2b01a297a79d474382d160df360effb31ca04332dff64c07bfb53527979e9dc7f3554fb8065caeea19f22067e37c87022d6b2f217540a24788d522337210f9021c7b9234908039ac5b5847918b82f522d8bfd2a03884cc2edcf5c5d9ddfe49a5747dda9479784f663945f62c30349af484640782e77de935b6c6ae85f1886252d3354df4911bb88d0f181ff89070477362d1c7ae54d429647a23da81060b70fd70983d716a61fc0aeb21016d888c79fd3a88c7b8f6b5c679e7144d1a754896ee89f358c473dbefe387bb89dad04413326178dd13f3855d88e032194fe5b16e951f3f3d4d1a33bf51a3169a47d1840ecb679fbdca6421d37374ebf5e6c3ec5b45db719e45b8fbb52488bb25019a28c55eac319a4ebbee89ab0a48d3d5289a68026975bbf030956d9ac9bb22bc021e9d95410b9f081e181c3420643a218c73e4b903c3a0976714c343601d4d09b99f7f5e9fa8775d0eb945a71838574d509cfd15e49b54097649bed0dffe3f9d377c8ca249b8cffb67fd9fdc4edf359961400846d1a443b9ca32133e11afba888d4f1112e1f2b5628d66c4812271d741988ccce107b8196112260d7f144dee33477e004d610e273b0a72c666f72f1b2f6be992b15070841a26afca91419f0f5f1435691002cb40f7480fad62fc6d3646d0df0bb5381700bdae2e91dc4ef96f05d466c246286001db5cd73a364d46aede108618f7c0b1b28d865e90ca31a560c222b38abbb7050b836597be318ab0ffe14ab635cd4e48d4370d1245b57584243be7b282c95083803b8b8a261475b88320c5d988bb23010c08f7af612d9453adfc45255d1cad362f283fd07d597e4c6bb6171db8b8a6cef5695c9bfa0b77a49bdf3a8e83321ec70ff9268c0b0da143a003144d20a544deefb586fa193cdf97911c451a8181bdf9246897e20633ff504a023ae33117fd8d169ca7da365c7d1dafde8d2f47aace41176c1ac763c6796d696e87375e89f6e67a370d174dd139480b391b4b4cb80dfaefd6b879c5965b8d8cd0b2c20fdedd6379fdc0020fc35d39639808dbbf7869d770bc6559b130fc7d234b155221b8e2efd0599f0dbb3085a0724a953d2aff484fdbb65a9419d244dbd0482ce3762d9053e74689a582e7611608d128567225268ded4fa7bd5b78b05398abe9fbf53b4cd20d883fd075b3ef31631eadad72f3b30f5ca971d5b17d29c4ebfe0d46e3359e48f949a47094b636db0a6d71c82fa2c45cee392bcf7ab9c8f7fee715a4b3a2702480fe446466af0c810911a43e6266bd76502a7da4cc982aa0ad8bb55567e932c76133923e982485ee90e60ac00406cadfafd4c3426c4ab35ac319ba656a7111b21fe64699e8fca54f76c5e6d0ff2b4595426e732121bd5c4ae3effe2fa52554a57347506f03a3d79cf0d5dd1926f7063caf4401ce36ba0eca6a0cc4064415401281384baf017f1aaea7ed23bbbfe2ba84fd70b66e94a0644d6bc0fbeddc65db5ecef9c38db62e59c7be2c9dbea5af19d0ecb70f8c2f738a71e6ad9c78e78b78596ed2df8805f124e69fe0ee320795fc78ccffe6eeb774554af559de3f1197d82259f376d07b7ee39fc4a997cce34acb8d6f53e050627ab5f35a3c40b2c295b154125c877cb338de2af5e7a0ae436d469b199e396487df56f41a8d056af50205e10a31faade7052e65f5121678abdfe969dad92f0f1be4829eb488d765bae7958996dc9caa560e60d786580d9bb48e99bc841cc2055043096163970a059c2c390af04f4979bfa950dfcebe2538ad3ab606fe861814e40a7ae2d3a757a8e4f1f3fa257fbb13283aa51ededd3f9ce83420faafc0d8710d9bbc0c23184047a9056eaad4b23b8fb1e05ef40e2b1d070b3c0fab29b456521f03e200fb12b0744145e67ed116006f16a1e2bf2a6c1a1210aa44d1f8423fecfb7739fb9d3ebcd832bbd975278a02b88d4e667c91127cf740169ef4a51780f7e5eb451abad562d257bd24decb0c0589ee6787286d059679df62dc4f4a5c0670008939b0a5ef9619dc240204056e9868a94c774bf9f04249f2c61d66e8d73915c3b879e779f1c33701425f34197d166c67f6407de55e47de7995302323a78214251569b7d2b18b1396c7d21032a3aa8e1c0932623c5231e7f0bc68c572eaac09d482e686e0424062b4540294710a7f421581a604508c01f776698daf06cc9ef7193ca3a504729b3c1623162a54caa31d636878e9f048c0cf4860d147a608daf27cda82b06b4b53445740ce03c9732754670523cfaf481a1c8b99275307ba7a3fc00d38a11467850d0ec2e0c45d57a6349487592d7ba1a786b21ec9a977311d7b5b1d7813da70df3cc0873c4d4f51989f296dee39cf788977624bbc78aa007235eae82c5dd24b36f0ba24ebfed7c9a2282425e87d6984095172efa5368a68c117d3308802be44e07b1d848458cd9d3478101487083c6a15d427eea6143e8444d9c569206a4bd66f8814e70e4adbcdadaed688ad31dc8383bbe14725c2f5d76a403162789b0936299a41faa9ede72c24173381b85678f0baeac4c2d28dac71983d9126722c272cab6f8fffc2cd2b79cb2bb7ccbfca8d40953e61e38b94a5b7c6c032b52247c549d259ca36a040ed0583e41fc4f730b3e5078976e7e74b73b7f302d728df4127c129860537dab0b2cb1a7ac8cbc918221b500b422050f96faa0d2b9e7931aa7559231b2ae62f7abc7f18af6b0a9835944e581830db03b8d0150875e56a7011cc1a8a9e725fe6a51ebfee1c48e19ad630020be22c69db37ec872f877daec32780fbbc1fd4c06c93dc8c5626fe9095d8c56995f99c12567791c2d8d17aae4aba932d2999beafcdfe4498d6d9d285a5469340fe6c27ba0186850079b7487d9d0ff05845664f8f1bacd00a5ae63d26945dd27aa0db92c01ffe1238301a2f2026c1281bc080fa345c5ced1f857d742a68912bfb4561dfec17c0e29069f0be89bfa6a1ef0abba38abeb0f48e22f629e179bf6c368034c06595ae9639425b6c06714f09976977dc62b5f174f19eb4163e54d4c149c470f2ac3688cce458d4ace294152184f518075252c8b32caf5438b58f6520fc0d31c31bb19856aa169bbd9783c110db3d8fca7a5f1e5f491a43359f85cd8194907bc1c3eab3765000df5592f42144c38fe0fac85f0e283b8838f8bcac4465d0ede10345999dd610566ad84158c99e5c9f10f95144137a215718276df399ad004e8ef57942fbfb2813d8d1c779939feee54bd600d77adcd858fcef0552a46d63daad1446f4be9fac396c284001ab7a88d694f82ceea0d90c88de30e5cf90376bcbd2a80aec85875043f0610251fc1c6627d137efb9865a034e252481fd17fd06499e0c6893bddd9c4357e98ac5714c0b4c65e575dbb467b2ce5b498dd256ea3ab47e71f0c7dd11ab117786f40403eec5f1b6ba3f36392ff34c1fe7b081ffbb28c1bcd0ccf6a342fd663476875d41ba4238b1041e08141ff889cf16a22ec63e0bd85ae5f897579b2f23ca6a35f2767b35a48be436babd94b0c0c8e0fe38d410ea4950e03ecea673368d7bc9f81061bd0c982e68d812e5b9971a1c5be4cce09b7dc921f7929dc2583c35104c0d245d523d7d9a8269c4b498ee1266b21dea0ca9910d20837f99e97466e6a61169b73b63fb8c9621f44c4e5c914793e82bb2f57e71c9eb7a32e5d52b8c35633ce847ad78924f0c37829f48153d89c6745efafbea83cd189ddcf7f22608fbcc8622422aa106711561c24944ff02bd1b19cf1d303e2323d478e2f37a2cf0bb5facb9ceff0ce6845a9c3e827e64a5945d06bc7433e6266b3795d3090369c78f613d2f301362930b3a997a07ebe6801a2fa4a05f86e8a6cb1c05bf62503cd11361cacac01a1e93c2b05e0262e60b938c8aa75f1ab2b0b8b01ab6990bd09b5b57d0413434eff75f5ebf0a079d0df84dc859344fd608890d508233c9f947557439b18a51c942d176ef187978e44841657f95e80f1b73a6b840611e490f6b0fca9a9a99265ba449317dbe51b5aef3da006e81111b414ada2dbb42837250b5ee01c1bf469b211efcd7ae89d8f52676664c37e82e7a04e088feaa9de4a2a5a9208a9243528c48c32154c5ddae6a70a944a15a00b0f6c2fc6d8074e1a2d44fe9b3da21f442d818b28e9682a54830c600411be15074aa046c0dfe3d3f6138e3b4445c14f85cfe00ab2c4ac677bc8f840b6b5f6d9a5955a5b8e8228e6f2ab9b1da815168c44210b3a0dcb84b4d0b5137bfec3af6d3d014b7272412f2ce6b584ee8af19d2dec7a0396ac0c3cb8841d69be5ec9ce0873090bc48ff826423fc447232fcc51b8cd88510b550d3c39250819fec044ec461b4e324f30a3e41b4d38ea6f54a18e72ca2138a2d7e38216fc76ed28e4e28e7ffe6a58b61306882e31cb9fca34b0929e38b18119dbcaeb9b8e843d68ae36dcc2c356ea842982a1158ed35853a511944552c1d26399a359b4174ab25759a157863b4863798383b90a1167c0192ef989e2119239ea2e08ebeafc29f12bce1b4ab179a6ea722d2d9368d2ea165837420144f58f36c09cf8be6eaea3918440f86d79f213bb3b2a1a5f4a0b5eb88fb501c6393dad595b26a084df50ff760e7c6b67e4a8111607c57321446f0a5f82883ef2bbd8874662be760ce7ff8b6694f2105d46083aa4441f44cdd97bf287d2ff32dce52ad519f8e4ec6530e2360cd779c4ca1ef34273c5edfa9ec2d22188736455696d525297f76745ec230b301198960c512d6726fdfdfa983d114672c63c0d2f76d0c3dbafc02d126af38f8773f1e008e64bb0b243791fa3e1db32ca4ac931df1c9efd585595b2266bdc06068c726185a4a327ed04ea2c0d32e5fbeb599045d4f349445109d40c97a230f7e9f337596b0041e84f65ce291da09235a0546c4573c7e6eae29e01fc209ee0cce23202a50029c43f508d83fb9b5d07c0f9a8358ecf8fa631951d49908f824fc306c486b9b0ae2397eed7897c739101bcd46badeff6100871f5631b7602e1afe9841a0d8b7f6406cde3f344ef5badf5404bfe1865da29a46ffa5f9f92c3eb4a93441c003c47db07854afb77a0318d0b15eb5ef7c9083dab38d5d8fb38076511cf61638c87e6f90e593067ef8c9b43c7ba2a052c894e744a47af6547e8a6520c56b161982e36cfd30169d86273662a7e8b6d2dc36494eb9e829c0b018bd31645e40d566388d0367936e227076993710b8392217260d893cc6655064f9ee91e625eee27ab33d07e9f2297aca3ac4845b9b169d8c1e92b9ac7a8ced5e5789e699ae0ceb4356dfb1261f066713638246f8b38a61d37a5ee280d28776b34c96b877d8201952be01afa2be5366593b902dba8a24d566ed8a6312368ad4b778ca970e3e71de45b71dd571ebb3e2c8067a555743a768704a7e844d6abe6224241307a8594f63805ac13b4744925737a0ae4757c67c7dcd9e68e4e10da76855234a116ecbb3d9385cf6e4f4b9dbf0299496eb73c855ff6d446ca25119698d660c04b8001aa9ebaa6f751ccbb948c6ead330c0addac6575776131b95079892002b90d1b30a99184e0a2a1d04e525f1ecb7a10446f6b0b4f955917a14f64469e171f62a81d8260f1a8a0f61a997ac9fbba071871d90dd1bfbfdc4b1ed64156269616855f3ca7a177e4fed6c423052d93581279116a8a55da5b0fc326357d00cc9cf4bb82edce793e2625b34dbb954b5c9cc6669647b4ff21bfe10209c301ed90e419d56b412947aac0639cb9abbdf88f67a0311768e2991d03f6214ecd017dea757419153d77d2a00a647eb2bab3c72965c99f41b30ecab8c151a0d14c2e38f76e591b357115aad682c0ab7d449851a5437049698895bc009a319112911a234781008c58800214d242dcbf8cb49f34a3087fb399be2f17a0459801a94c9b613fbcdf9f75404d62a6555068566dd63f7549d45f37926da2ad51e529edb554d3592bb4221e946a96ca3b29413c5a86cd57e661a8bc8fe79a4dac539e1582f15a88551854107cbd72488c925e45155cec24ab9ae91f09b9d97c13981f1a77cbf89e30ae6d846172d8be274660ccaa5963fe5c8ddad2e67f7eb72c918944b5fa5b8bb1c6ec96b3e2857df8c4dbcae978bf7057607460f2e3962ece79c307c8dfc344f12de98bfe70a7f1b3565d1c966843eb6ab0a649c5e220f340b54630031c684a3598126aea7312270d430d851df0c3377d592000cd2551eeff4723483e8e5831b58d4443dd3cde8a6001dbeb788b9559adec2dc1d026062a40a8d7632e3f3ff83c1c00412e237549a6375753466395acc71a7a5d3f4ee482e371b6bdfd21653505062ab66929da68a341b65a438a80670d68dee058a617fd3e60ec75639114a6f9ff5accdd8fa9f72a8a4907e6693e054521d48d61414322976e05e1149a27e2edafc641563428278e6e4c13f09903f3a8be9e94685660912139932515b0ad83e5e22260af11d644f12e64c9e4afc59c397e5a4bb31db36569cabb207a8e4e5718ae3732149a72a13013eea6bdf0cf49898fb5675d6cf3fa679bfb8e62b23ff623ea0e21e1e62866ded18c95fbe5a0671a25d05a0f208fe12be3a827add00e656dfaa82fc69c4625214baba9e3ed79cb159a1f2aeec4e90e48b757835c62cde1e1a8f4263f7ae903bff7092f6ea8f3965bbbeee9478ec085c81a68b99e7e200641af962ec86fd4f892cb8b270d9091b61d8a703e3a08bec4538c54a6b5e0efd696a2dd8947b15315357e8f849046dbb6912a6a920fa6183a17fb056145914a68452e26fd9412da2809f7797c4e4506a5778280fd193e81831b8ac0db822db0a4696ffb78d026fc02d41c8ae4471e3255a91d58382be993c75f5582f5d9e218c8c6a6681ec5fd9c186708acc018432c54fe3dc313d9f94c81b3c0ac2cb2f0c832c69a00f9edc5bbc9842937257640e38414e7f7964404940d35ce339019209e9ed7a731f3d5afec533c659cb87aba231bd35c884d89c1f9c279bd2157e381e251f38c393dae8db12131f9060eddd8d388c0efef8d4d214a4545372241e22a57c6c3f22841141e77a10cc8c7e466455737ec880860ba76590951b123c2392019bcabc97ce11fd39924d1b9627ed08e3891b0305961ad838f53b3ab9492300cf56cf1a39c2c40b28e22edac948785ddcba17fb5aeea035277bdc04bee0cced59a3b187d9ed21b5322cd21cb3c0ac1e3fd4112c0ee8c32fb849eb4936ac585ee93c3f1d919e17ff9f1415ae8dfe13ce79145d08d1a536513cb5ebbf0afa6d298393ddcb00bbc4c1ea82858190f06cfd77dbf0948b6edec6bb3691e89e0327844892b8d21deeafd3d7f217de134f75174d5b21e5a79cef26428f0c33ff2a3471ca64360e2e460bf431c25ec960df44a4d12fc7534612ba28c8139c58928cb8e3aed8d2bbc69ee04c1fdddb2c708b94cefa45205f8dd8920c28935fb2f4214a7b7a747e7ec7efbd8acbf7988fb433986577cdc0647fccc7f4d14406016dc5c97e08e400ff575e00b506bc438162aae4c70200ab5d4148f69c692f332b76e8a18ddbd9e0c0d147ed34c5774b62216d72e2f18793ac6fc24b6b8d2160f346c85c2351d861388baf557535af314643a5a5c74d1253062d2b4b567864e684df7a5a909a2d363a1b2bff08cf56cdb78d508aba87008bde16de93985f404e03bd71b4dd1a20714e818c6bcdd5401a9fb076e6bc256650197a8379aca07c4980c5a1a88480caf6d44e8694cb39855079088f0cfb3479386971f68486f10eb0d1f621e192c40389bc5766266c69b04056ea01d41119d63a5b40ecadbba368bdc3a018fa4e0af0d6e6896ca1bd3c71ede8d3e47b9db46f413b24b99ae2463c02ab6682130e1f8cdf1e8f7a08fac79b3e1c962ba2f8a241570dbe00f60bdf3ce8bdba78d1bd55c73e20e2de09b02fa2fd17b6c5eaa7796fa583c6e487ebac780e35663705b39692104535ddbb0e3f98c96c0368ebd7dfe15a6b809c810485d53ffc8210edf99b07dace4dceb4e6f3ff88dce90cfea8af024c1a3ba068b9f4cd9533de29512eff71932e86fe1d8e43a5404b7816b42f324585c35e019ca91b2f7be7f597a8cbd4d549cef7034df772419bb69f56081968635341102aec73e16cd7f51f2c2091d3818005272d63297f37ca151be529064ef27cf19fd45045112e6330a4b0c0153fdccb68d60a9c1370216d6d893297c77be0be208f805c662cebe6115ee1c0a0750891c31932979d4c638ceda3d6e03642ecfedb477e7fd7fde153923c21cc387049f6cb20e2196dc85eea686dcf040f734495ad8527eb54112efdaeb80d26d9537ee891a22f32c238f9f1c2ff7951bd243aa72a1cab32a4fe79fb32164de068e6218eda6d8d1091e8eba47e13d7b04857002e1b566671434ab1031c3701da3c160bde7be539936105ab11a55b82f00ddcff64bbca437226d5a0720cf786e716abdea35ce9164d4b1d7e1fa0d925d6576f3ba0b372e064e2576bfb0b874a155557ab4b1cbcba717a972bb6a0050f043e444bba7d211128990e0a0af069716d498058f165e138d3ba5c72fe1027abf152fdf0c34bb0082a90b8ea8510ddfd8b10e4be015aa0242a41c042328ba9395d10708ac63690f9d8b027d834c3b2dd81bd43006245e33f0c2dd177de33ddfb06990897ef110f464a73b305c1a92c9c3f21eda57f00ff2a78dd00f622fc1f7ac4b7ceb01031a0fdf844efa99cd0f229adce6165d21a45a9e915e040738f3a464d71ca189461ceff1808b6ef8708e0c09f171515e99596e1f0760bd21f265c9e9340c6239fa627b0449a1cb33040e408164500ba837c8172f62393dbf4b095e80a0410fea3f0eb554ab9c45d6194735bd1ffe65b00725aa8530d245a1c40216fcb555a44d1f97e8f56a8958b8234b2bd8023e144526ad487b7ada52808955bb3f4c28f8e1d9b940d27f1749ef724727b93fa2b771ef52d2ff4ac4e1bd117c7eb1422743ece130f3059acc6f58128b3fc6ea0f5a72ff0d283e8bfc0e66061b61a7d3637adae2b1d4c69ec3555e77aaa5216f5f4918f99de0ff451979640cb7bff61c1820ec8fe2e8f8960eaa153fe6e7a4c085a83ae1820583e2cb776d6b874cf8dbdb26af1ef9c7c408f96c834c8d20f73e23d6043627ee5de15854dc95e630d34e0aabe5112a0665d5d8f43ba9be17b14701928dc9988abb0fdc501adf6999bac1cdc9570704374c94f123bb8bf9920f10a8e3e31c0e3baf56907430084104876a6eb215cc7214417aba8e81cb4b6eaf2e66604ff5c614b33facf368837e9dffe9ce043e04d58cee1c547fb2361fc1fe01a329f47b4c86b9cc64145e3e42676447ddce524cbe1308d0fbcee8ee08d02a27f980458aa5eaf2a20c5739fb6025d89d1411a23c3d2be1bad2c3e585e96949cf2c80c9874c427e52fc41f2fb9947a4aa076f9298e335f392d7e22be8fe0e17ffead0bf8956d01573d3bbe67f8adbd72259050ce8157607eb2f3dc3411f4c148b3fb2d8053c4abfe2fd703a839d16ac2674ad6eb70f83579149bdbd0ac30a0d12006e0abf4e7a1e67db5eb9f2da26c24e72ea09092c1c2b6c283e41c4c409c2983111cd16bc8fe0cc63afc56414dee82c6a2a09186007c028abb890d8c8412ffb99885b040aa6c118a3c6801cf9c024602876e427c59ec843400811c1498c118c66cc2b3710bb5f1e1c626e2081882bc08bfdcf5f2234e1de8842db46de41d1cc13671d147b05faa758121274982478a3f5308e846ccd84650122d9336b6c26b52fe69ff6867a517c8e485830a9a5e770ad3913bb4cc5b5b1650fbff11bdb076b306a3843655f011f36c48de4a7383f8772d293b64cc5dd499a462ae87503fc2d53aa902acc0743eeeff56be4df9a272cba74930522a041233bd4acad95ce90685a3c6f6cb12be5cdab2e3e49046c17d8ce33b0f4305d0900988bec18f14e145c83c950cfe4651fbfc5dfc50e89812b49fb2c03d45fcf1a9c2c2d2c75dfff661c7ace431a8aa2b436b02a0dbb60187bba9006ae2039a33391e90506c8ea3e253c2b3e7b38637594b8d900b3907951116141a67fcc18a60c88a1dc0ddbbdefe9fd9249fae8c87bb3e0be4304005f225acf5468a74fcaec2d4cae4104a70569196becdd17269c3f993eb17aefc7e5f05133c07eff92d946b0526b47895656eb08523b71d2a073e65aacc9ce30e58ab294d780b2b57cab0e1b2fad97d67426e607303811080022edf89e239f8eef41af06f2998b10c80e1e344583b2827d098060c64db74751a38718084dc95d82b21d1dcfd08c05897a092681182b4cc8eafc8fc9c6b68f9d90b8a579b31c09c90abc6a6e3a6cf21c417e46974980c4fc7b95ffd0f0a0522ee2d22fb4d7612e4736208f958c3715b06d84aeb59bb2aad002ab531ec25f92f832976bb59da50a34bf64e5cff4b520b09c574a3299bddb2bfe0466583b5c036aa5dee88792b66d09f39f5f62aed3461550d74bd40fd2567e481826321af297debfb4afc5b4498ccf73f632687608cc7b4ee9165ffc910582418b60d261d372fba1f3c3e36873869902258d10a9a1898f603d515cf8f812b3ee8c16329ba6baa802b12194cc53bb1d4039e00ea9a5dc2a3bc2e1c325a942bdb5ac9211f0be277487008885387b2395c0b1183309febcc7f5298d79d630b971229e116c736c22988838e1251800a1ff8d7e972d1de19cf58b73178e714ecfeafeb2e98dbfd877d7cf77bdceffb1cd024cc5a0bb191002446e144ffe5a01ceb5f8b1cb1038171a31d29082433a28f533b30b4be1f0f711a288303bacb15e616a68e5d74b1b7229b62b1d260b3841d42c098056705851452de2e602d19e12168621a92600b6ebbf86e90a82db87f446c443cc3d4c39643440acb322265d2cc75405c34ced70710881f2de6e9891a71272db1807c0d38ad0c68b0b53ef2b957d2333446e3d82f9f784164bcc53e7dadb19956d36c2557ad885afc970a818b63f562a8bc38a86f7bc3b167cbb09413571db9f1bb172a7e57a7c179dc5fc225082fc3d9e04c47ca886053feb23679b74fd0e50ddbe41ab6283280254bdcb17c8f475a08a0bbc3f23b1ed102d722fc3cc03773a454418e07393e0bf1370985b8fc53e25271907ee287650a7f4aff077c4ed0ade2d4e63345a7bf66dd24460380092346b479bbd358e9fd98d962506dff4769a8c97101ada24788a890d58f9a6146d7d2d0758d88d904941e2a423444dc5b53a482ff699ad749523cbc31060204123c3cc56a400e47d17e0400a483f8d1fddd4682a24394ad23d8f8e1db677e6c33b0213713eab2f2f4543ade9e798bdc26659e145152303aaabdfa045db490f6d81ec7b421db7f0863499c001fda6359db8570db50fa5c32cfb6e99e2a1e09b5cb1aea8a83cf227d1fc1d544ae7fcb7d1d63ea4fa1f7d5be7fb14dd911fe3aebfc3550b9ec71f61808466923a0fb80de7c8bbf0907ed49bdc2a84becc6e07e434a18c59fbe3d3efd0f020cd98d2a2d9b60a7d8821267ad150b42606dc818c7e1a3877686c9224499b033d4d35a44ce67b0b7f1398f77559d58a94039675dd14515d9c38cf78bbaf0758f9c25233c28e737c6acb68cb9d9c49a6535ef6cb0c3e8dbb897a47286621631c499b0ba10a0b835611fd06c4e15f40d2efde10b070b90d1316d7509a67efe6ecbb358afa0d51753be54df8ad48c055e7062d968b558323f1779040bb5e099eb061955bbc42c2f9a467aa9670a51e68edddda3eff0e145fa15f5b28560136e2a5eb8d5493a73607f3b67879f36218c602b5641f689fe99bc70519b041cd8b37753295bde91cdd61bdc0c3a33e23930b32101f081dca6a179443f047f497a5d9f9b8877b28ab8f368592e8bde9b98ed83a62925f52eb8c8d01f086d5040b4d9b023d1ce3e867eb2e90533289318bdd6e59892903169ddb4e00e1dcb5c3ee3416364ba89d10c9fcf28c3c62b01f0a8d258bab266626c1c3d8dbf31be3cd78c1ee78b17e3b02fab80cb6498ab55c5eb8285ef65092baa413157adad822791a0227ea36d694edba8cefc7d3e4d9d84b32e873d717f057c113b011993dea66e94602c1f21329c1e65120e5485827acc11195c14e9c28cc9c491d8a62b27145da3a604fb4a41f5d2b05f5d8e807b29e1ee7301fc82eb75b9c51d3ae9d2180ed63fcbc945fe331566dca4650f748421ec7be71ce5bc273f7a5748cfe0926ca58b29aa8c5c61991c782b4eec4562d198098b7be15e1af9d0c85b4c4553348764de2d060419950fbac66092a01201b9014dfdad446e920d0793518b0a13017d7ae3c3bf27a304865f5b52e9ca0b780e3eb24fb8c7a35808765ab3c252cf6fdbecc83dc80432e771e8891b768fb82340338acc95dfd0407115f6784eb6df07ec49ba9ac5a9855e1617d3ca5929f817794d401c29599e9adeb4d18aed09feb8d62216679876ccbeac216bd3b0a8d64aa87ecaf3a072ac056e99418ec124745467276e030c19ae6e4fc086462d8c6a19da06c49e8866cb73f4d42fc77a9d83e61ad18d2ceea03c54c19a1c53350573556c03483f9073e36811d8d8e7a6c9fa3eb5bbd828db275dd62d220e83327f1b78811d8742e1aeeaef45845d36ccd552fbdf1315604aeac41c17e096779c2a87d147bb1945b835413e546d10bd9c229666e31ae60cca007be45567b2f99bde54a8eca43ab3eb33b8e846565f6353c0e72fe0ede3d4c48c8bb63f16f5d3458f6718867dc34b0c2239047507e9aec43b528f41237b306f1f4c64280ca1eb5f49f0231bb9e064bd22334a1c3766fb8fab255d9ab3c90fb3790762dcfd163dcc029fe5443c3083c001ba4666883ef1a139340b85c20b6f9624e588da5f3dba4359d103bd5cc33b8d15a1de6a6468491ee0103be3f7e2d6808467332c1b7846ad6f9fce0e991b3207bfc74b4fef234a01f40749cdae8c59ecba3803fbcb0856f7d199e3e328d1fcbf138512babe71c5811583b654c9a50e1f0929f33d2b6c294ad08d0907bd411876ad2404c6620d0a240929875d8b4d6241863c2a97e1f3a87ffaa9b00943f61370793a3e1880ce6c37628f4a2e6049142a22e3b2e2b2c4d422bcbd0bbc9a6b5ec06e02828e5f8987f337e6f42e71f4911986cc55ae1d2cb6687b6808c49824eed1ace01e4bbab13f8f5635eed4554e2be39e4255443049923ca4daed90295468e6e46e483f185603473841aa9700722ac13c7248dd3e337bdf4118dc93dd4c758cf7a81479a7f6e51cea34ae1e066a9e70d41182e8f7625dea21d7364d61194f61f400c19c8cbe4bf7e1ccb247a7f9405b688ba9187a18f8886dc58e9648d4150ed297dd5e8abd428be2004877b4c85d9eebafdd3f867b0264d4a92266ed31c0597f021ec35957dca420aabf503ae0ab950ec5c86fb4ebf457370f4e971dd007f2c1c717eb050572fb2a3e41e00dc7bf2e8dc36dd89f792ceb2110e0fd5966b2624b0a6f227f52ec40fd01611c2cfce990e21780d4524dbfc5dc626b542476939ae106d2d81876336cd69ee311e2b5b223a237e2000d713031ddf208a349c072e6cf1655a8c1f0bedf21adae1d1e513956225ca293b1470e326101888662d6dc488a4fbdf1e283fc881740b0206829c8ebf32fa4bb27c4e6a09f6e7d5316f21e16e762df33d04910fabf2250aae2be41d3708118701affcb9aeba59a3d0b7c8aadf1d5b19d00f72ef6887d6257cc891db146ac1133314f8c11bb628f98893d62375755c751ee3bb147cc8a396227e6887d62578c89356247ac113b314e8c1123311fa529b192282ea397ac7d879e61542d5511190b346097b0c23b3d442a96fd9b8dde3963a6ff5d6d74cba3c0df9f9e9977b1f13e29da3ab80cc1084ad331b6544f0b31c74265a78825479feabfe00e04f75347b495f1980911f9869fba0f1b3a99231a674675df6079b669b3baf3a6b495c37674d86e5aac16f9a8190e26701f3a11430efc8bd14b3d2d95f2f9889fe111f4f9a7fef1700b5ff96a3ebcdf879bca926fe53266ade1c3438f77a917225dd691cb6d72b86fdb3264a7935dac62d6bd14905c8223c39ca0925858079bcb526744bffbff87b6441c99f286eef50a815b11447a9511ed9d20f053e55cd0e4ab49e088186511d395eace57aafdd88f333833ab5e0b28f5d89921b51fbcfe1b71886463d07a8d2c1ffbcedf43f4efe3361ed2a283e3f193f79320ca8c585c08dbc9f955535048bd9b1f218b52d286254cf34047016db0a9287eabbc95c833b837872aa7e162cdafd519ee34fea4d271575550d0f24a994bb212b6d46c8d89629df40bf4e307136f6af13218f73230831955c29025b58524a95bd63e20ea9f4674a469b1caa5c04a69d4e88a9a5c7a821b091a952bd298044bd6e9fe531e6a75b2c60733af7a59fc4fc4ccb1e89a82f06e35b11419e216833fe710f2f6deef13372bf94c524de3d6f351ec3497cb546a34bbc3ceded7f7773399d469e2c76ea2f3e84aaecafc30d99b17497b997702830214b89db396c3fca89bbb6e683fdafdfd2cfab957506eb4c99d8668182097bceebed42c0b5a2f5c0ab5e5096e96c52ecc8581fb90063f0f84feb51978364c80629af2b6e5def4c05d14edda5e0de66ceadf9cb0412f39ffabf37d27fb15474deff6808c6aa750e3cf7bdcc317a71240b581dd0469681128b53b87138e6f300e5d2ce705c55f9d1f5793b4c9403b25ee50e98d3cde04c363ab5abc6cf51a722b0d3e0b350f926f257b9e25e4d7ead09b46506214947ad4df38c69fa657a276edadae72b1679ebfc371b9796991245ad044cea148fad3e045e58cb1dfbce6894fb2e7131f09a022b287f94d61bffe43c1719d009636842782dd3367146f3e6eede187b88949e9e3c02ed79ee4380866add45f76b3b6b1c205852da9aa3110adc79f204e531843dfdedd057c2dab43dab73b7d4dba786a5177c00821c28f23d6136df58d45a21e05d101869f0142856129024a14d775e70a90f8b103261c79f412b613c859d992b90d157a2fc4409191c87a14eef20eb02d52991026e56e3051f44ac734200ebafcf4b744233a5981270d2ec6f473af1fd2b92fb4f451647f9503831cc327c16a59ff9974c4d8dfe2f195880f663d4fd72b396478fbd016e4060010d970b82b0fa792e21a590e60d4c4da0412b9f41db1f104d9a4375aa7ba45f3571ceb5e003b39b83cb3821dbacb05d1af859217c5ce664d30f666546fd0a79741f3fe61ab04930472002340df0d8df36fd4961c78c7bd0cb0576fc7fdbf24dc65b20f80ae36edc249ddf5c124a45c16d6ebdcbea000d8d4b1b7057f1bd846936a00c63ffb9dc0a2e27a4648741526c20a2c2450c4302bc3ae6c64ce342c9441792169410b54db59a2927b9b585218d569f43148ce9e0002c7e8bff95a46bdb7c8d609d2dc76715115612868d7b39cee78fab83102b405377baec777571c3008b94d49491ce0a41b3d07e4c97a7370685a77173aa47fe61a3c33e8ce3819ce28b11963d64ce064860a3083e8cbac8d6500a23223a68ce34c460d9231e8c8840c6428f1186476cce23406081833f4629c8d6214911823464c700e430d8641e8c2ac0c61003198310bc6e10446058031f45fc2c62f14d11724e6cb72f6020c7a19745e9c0c5e94b88b31db2570ea42057441f45cd6462e00119762942d8e44b16126854b67bd252c6ea1f8b62061b62cb3166057cb20a7c549d0a2fc598cb1590253162a8f0b22c3652d6f0178b78c705b1cc316b5afc520ab252469a1fc27b4201f8582009f6da1551e74b2e98b4e363ef9c8177447125bbd025916544a62e33aac9506289c1896e126b68e5f1c4f44c6b3dd9c02549aee7ce9ed313b9410d5b788358fec117ce47dc8accf33299436e285f36d4719212054cbe0af94838f472c5044880687cc02ad93f1ee04689089d15b46783a782f2e26026847435cb6e37027d8fd380c7c44637e803b1166dbb4e501d1230442aa04a8146452df422d8e892b8ca289864c2b9e48501680d01ee10829016b18dc8e1580536880765503cc22bae8e5134e6decb9c69627e0696328fc7c567a973a9517ab9c733e3e2b5001928b8099532f9881070cc127a3e82353f88b657cc2387ce510bc64101c72469d1983ce59896736c137d3842b63c08161f8c4387ac41e7a648f1c33871e38032f188247cecaa7fbcf717fc55fa01937a509faac4248f8a102c9ffb8e244fd46b9247d4281c47fae72a2bea358923ea142f23f5738519f51964a9f5022f19f2b9caaef284ba44f2890fccf154ed6679425d22f9448fee3122256cbd218641292fdbb767c1193086144c04cf092f28d29046bb6838057d16ca89f6ca51da9c60fde9130a25e6c1d40fcf572840ea7b7406140b81c4d49828f866a7bcc34fa51385569a9cc5fe7d682b737cb897bba50c01b39a645f9213d069de1295de99f06524abe03ece326e430be35bc0ea7bf177dfde51ce0fe909e77c5d0fcd29e22be3433e79885899489dfd664b832ee8549b77678acd27a9459e02bb78c3db81a67336b9e6db79f5c956e4ab2fb702fe53a37401f66be56e3dbdb11732a7ac7955e3c7a7fa948bcd6b450c43d05533a4b05fa9e835c437da7a19d90a11d4eac04aa40ac33eac604141806a34c1b0b490ed983d6b90085ec1526d884148758ff581a8fe9ad198e82083f54d5a348b756e0801af7f5e00d353828cd5a4cfa47f025c3dd24db9f5997ee2f1468680c44e136b84db3afaf8af83a16de7064fef1854aef6104768976e42cce58bf899ba11bc2c178f21f226fdb15faa2efac89a9b56256c9f1c362731e27db9d9286da7a2dc1f3cd8681d622c6fb01ee9f4a201f5451cdbc2923eba30f346a2757518b7d0b6d581c605d609e54141999e91eff6ae0a36a90afc1d7710da5ab36b9debc417304d454ff20888ac8bc967f74d2404e3f0b87f13415aff258ac5c1d07f9f22db664fe6ff019fdf47dffcc15e3a1bcb17c8a73e59dc36db35d4c09adfefaeee69da374bf6fdde38fe484569fff1d9db3cd7d60513297e589c8f034563ce205ea35b491a46612b919eeb5bae442914a3391e73761702937a53f934ba52a88c0517c8b9fbb69c96100f8e6fc8fed407cceb61f2fb442cb62ce8262c5b6683f2cd9284771b74c59208a357b0a097034b8bd84e2eaa370ead5bdab2ad86d38613677607edde6e596667fe434eafbfae91528da217bc6602fff7b71aa810d1e33793500969293eee2172629b7472c0309f81bbcb84ce761325d73479cb023764896908a8a3aa7ba9b15ed0d8989384f5a51d3dff23f844ab920481633dc0ef04bb8d051066113d39b1cb4e41cd03697f87aa3afd6b0af63d2c40b14e92362d2409ce47c5b9fac174098a399dd24421f95b393e7c08c0521f0279a88c423940e585b6476a0e5b1e254e17d6ba4a16dd34b69e6113eed70d2e55f206b5344d2921ca1be4d29c5403edfb9f288cd2dcec60ec13b6960014dd4b003cdbf2d84a24d9c5be341af19253ecf27bd145167680e93f32530cca73e9e0e550c5dd1ccc4cfe4f5cd864dc901f68d705fa7beff79344637c21dfc130c4b25dec7571d0fee6bc49579e29d8b684a1dd3e9a15ad3bae0275bd7ab2985f0402902cde3e43bca2c4cb11a7e068dfc86457a2452a92932cc0709f1b17c76aec7a8e7bd7bb2b7ae5fbac3a876758ae77a41190c6c9b9915c518c0981f24d251b94649f0ae5bda72a3135956ab45645d94b9e6d664a21e614dfbaf272e91e17d5ed9e4d894dfc69442aec726129ce37cbdc942c935c0861d5d5138a60fdfb1199c5f321c47fc6457ae848651062258f7b1e6998028aca9f6f278a5e79adecfdcba4e13e38744f71671cdc4f67043f4be3456e7604dfb624948b195644a323e0962be480117deae4fc506d7413948fedfd4ca6d14bbc0a4c089fbde2a2692db21723b23f5e4371355d1a39c91bf6fec99b259084455e9e561e41e0d599c5b6fe2c46eda0e89069ad66c72dba801dc3dab7b88a4026060d34760ca1f4d4a373246a5b4c10fb00f7ff1ee7ff58aaf8956b951f0d204c698ef233635b97c35708b4ce7de8e4e021afeb59551be4d95b23e0058ca0b73c7a9ec16267fe6daf6a227e68c1ecaf895bd39e996e76bc805005362c61df0a6c1154c069af28088cdc627da17bb63310c3c6a30bcc11f33101715010bacb93c8dec180e107dd0bfda4ae3826180d100c78d40b0d7729cef2f56bc8ac62fece7a36402355e117d7c5876111455d6b9919f1c3fa5be5dada3a7fc2fa4741745e197ba6c723595353501247fb334a759bf1a149fc60f0985b66224b1d58fa7a42f003204a99a959d04d7fb13b4130a4a468ec0e97f304ae103c4639699d38042f3d3c4c90a256b93f115cb8071f6af7ca25437829129cd05e208d47ed3dff7bdc04edbc807f85ddd39204c0c889b931d18a9d17f48403c52ac000c136d544dfd3e3dd5ce44dac5da4587609abfaa1d0f84727b249122d0d2442c3f8b843b892b3a08befccc4ba79478b40f4fdca92ed707011143d8ba6aed23c266d1720530feb3da2c0e8e5b5454502790c9faf1b1cc0734d69e6b28a81d957fbda8741f4aaa5c6ce30b3b6985d3c233c8adb6a110f786f8aa92f163e685056a5c669e91a16f22315f3b7a980ba3e26ac8bdb99a91b893053658834445f77381349596adc3be6d3fbe299d2f2b9ba2512e48406c9455ff49a1ed072c0389beed4748e9c359a3f3b82590a4381955ef23f7611b66170b85c3d247818a7a6c5c913d56c056ce50e76050a3c56805086a8aabb4a66e664771004ece756867099f3b5d6c04890f2414450cdf9a5c76370e9129847abc7c9a1533ad40622abd6d5f16485545217aae465f0c5c19695798b53050aaab4bda04eea70b5414fc41bb57aeb5bfce7b817a821254c8bfdf3b50ab633f81ea6865fa154624a384e1a9498836de631e92355e2d4c6620084389838b70ba8e28ee2b02a33d3f57cec00ef858175fc6ee907631ba2ff1c4c3f90b73fc6922b7e89922735d07f2798de066a49a54908ab431e2f05a43ac551c0d55c170115b552b12d720a19b699384721aca5ecedab21ad9940f6810a4da8ea1414f04f93445f5bd1461e6dd1bf637ca17fa44a5e8089b920d07a8609668322b475914c718a76c4b56a83ec1a6791d3547548fd3f28bcd10a4df0ba96e84c071320c5a8d3163011fdb73a8753f8225ea895a5bf5fbe3b98901cdda12273d233c49a8d8b69b64ee98b8244aedd8d4a2298e032e362e03ca1c8c656e4aebcfd04e618af27ba17af3e28ea29763ddc957fd8f8158ab857c2109a479f827255a0f50cf5d3160ed461f58bb000fbe9ddd5c469d36720184d8a35ea0736e0244988781bd35b429cd3b868abe759f08810bac9f604ebd337151187715aa9c604d35984c98a5578d93db5202084c36680490e2f8b785c1189c8b2c2680ee4fae5ad09ecaf85cda296735f22fce092f5d3910263f03a034778dbea3abb90067506363d220be04cfe208200dd2e3dace8ad7a7cc7edc7d203dcda02fa07a8291728523ad7cd88fc7ee726618be68a958485a35b34dfa76127b9fd75039a5e1d7a6bb981a4da394f65b5724281663f7e1f5d314fa3f9422b260cd147d775b6cd2b12a7aa0b53a6d680e7071c87093ae67b6f5f40110568d3d3955085ec1f9747c4bdd9481b4c86a5bb43011bd4e354be58033309eaa6b4f8e09ae510b0423963b73968854b432e54faac47ecfcd6a0f5c8ac32e0b96e3a7ad3f6a84d608e90d1feae13b8539cb2e38186e9032373a83c53ca0d5e1467b0d0cbea8503b4056355449812fb1a9ba7eb09912b859d05612ecc3426772db50fff5a2c34f0ead69c4968306cf74aaebd143b4ed66b369b7a09fa06d84ff4996559a850a27d46201f6a38063514ec85564a731a80f2a1fe8462610969f550b65b1f528d341492696247360d3d0ba9d377fc4d49417141f6a52b8ab2492bfe018fa3c94c876bd7aa46b08df6a26e9107bfe700cc89b44e77a6b1c46848a947a5b8b7437f5881765e0367e712e363f955ab5542a2708fe8f9cfaedffeac07d71b60a9ce2d4b148487ed84e4cf41dd13b9596448b29a34c1ca4eb77ec2e4766f77dc9e62274a801da426857b97b38a07e287163ff6fa1414ed0af553cd8f27627fc0a02a8e34cc24390d2e99406498fdfc761de21b1f8db9101578744f5c5dfd852915120798e435ed866557a0cf783c687c72816eec21dfaccc6b60a5e4a9ed89f80933111bd020a36d9459074c8ed7a54020689403b9bbab01b6ab7a6a5fdeff4af82a93ae82be7727aba4b3c688c58e2b4a2aefc26caf2706be483acf435c9f817b1b101bf11aa89fb8722a080969c9c0ab19f7f5400e9af7ab17e8b2239a7a4f3465903c54145fe33c386fa1466848df19869c861d9a2cf7174a89f951ef67f4185ce954d00fce025b93d327cd98dacbb04f8cf1d0df402ff2e89cdd8e7ae4307eac0424f21da53cb6346341862b5026775d81fda2e290583d0b4ad142907d42cb0839c738283da118dbf78c97c277db6f12bea9108d45c43d4cf9c66dbe3d0552ac827f510be2efe5fb8fffb73fe80d89613e3c181b27b35916673630dd3b24c7b9cf514623af862896648e4ac214d14d7a43b26b0762508cf5c3d1d4d239907f036c46c8e20c22a563837a715319a23485f2f0f50b3e80f95e70f8f9947331572d9f063bec220db55125450ffc18e7d2e5c1b5171faf5b2b77b3cce45a24aa98d3be1085ccc4b78cb6190276838b77fe2501f751d94864b7fc4b23d2abc2de649cd2a29e836e27ae78f9314191d993a2a80a02db89e264a130c8aee1717b280cd9021ae2d76231596461bb9b8791a79988996134fd7a694553c474872fa5051925e9e0551d448bc91e8b6d5a55231ff8fe2abed6f569057c17f0bd760e20287bd44ca030db537e49642bfa294b2b08e6c5f904cdb6374507aba4860bc7093157d5eb125647ddaa9b7468ff296e9230dac2d69052c9fe98c782fd2887aa20912f13c74a782d7601e7a63218f695fba596a3ee1b6f5ee43344c3764b8108516b673c15cd216197e0b96df894d664a795f05468b164faf66ef74990c8477cbcf0aba8ca88d7436cea62ace074f452d06628cf1ec32c9533ff5a40de32169301e788190f47db783586c0dd94683815c9e4f7c1a296ab79e0521241b40d38f77516975367d2695200212de03df3cf1b7d283d39a6b3577e56768ea7dd96ac2015fed0c07245f30d453c0c141dae6b8b9623c09ac0d96c35c2b8e88a72241715b05e721a3806cf60185d8bf2481543a5222cad45236ed4079cd675536135a216a67e0575f20f0d20682f8e14a17e54ad33a777ff280286374984b92687b3206564023398dd81e0285b5d662c0790dff7a528ad8735737af40ea8146b831d9329cbf72e9e48e7c4cd2ddb2082039565350c04de9694e90489c21229a5a4bf135c7aa73886b8331ee4a8689bd9e1b9c400b621ce56051c16d8e1dbdad7119a2c54e52ce3007e2368a92bab7f5fed193a342a3f13645a1498b12ec34de8a81a278a9320b18285d3046328e25d84312052880c056018d125a0af95bc04a5cdaac1e29f9819892425a81088e79373a54ca5ef13bcc6073bd3a4aa5bbf46c667f4833d108e1b881d44b1bbf01379ca57207c26f7490e9e1c5cb73b8004a6e4870e895aa10144be52a05dbb6b6b514ed6902f2842b42bb3e45d220c3068da298f7473e3f3f6e931490f0163d679c980cdb74755112aa44ec3ccce0c5630ca918dc981751c35ade119c176ec04a1181a0925500e60f278c9a040b1aa1e017fd3a2093d752236d9bf8fd7f3657b6f89e301ac7f9cbcb7dc7f46e3dac5895e4c7cb146e47d67bafa0ac6b464bdc45c5ca70c04805485c7f18b31fb4e3411ae8dc517418dcca8192fafcb10801357f1601c8d50e7c191ae4c6274019344032a2c9b96ef8971c659bc17c3b89150459188fd65869d59ca998d15bf0d1ecd1f4829cd5b92c562b23b99cedfb9e42d4e7b732a35ce89d98f3936c3c1f84b5b30a7438b362154d6cd0e36d943622baa8302b26b48396e71daf2ea39ff9fe53107ae85a2547ab868fa11c31b8a24551a73769fae96c8c9fa71073c3e5029fb1fb2512b164390d22ff400f8ba69fa50176f5c8daa9271a5b792fc55cdde38102613b08ffa9bb1b83d7d7210f912f6efb8958f44e09a19278c34a7c82470aa252a76ef3ea66af7244298a4279075aa2a37e193b42745847955d35bd5a13af93c4704c4e9797958161b2701a881afae04152a73167ee2d211157921556d389f8442075d680bf35c53e24c2e818ee4db34011a7149bd459998afd709cc1b7f240745f20b6a8ada50a5d04387458a90fea517d425feeded29798d1ae7e935609444e45783914f2305152bf3b7e2079b799e73bc21eea52444452e2a8f478c7a302765c2cb309d1e3e54a0deacb385e694505c04605dfd12191c466155b8047323f1ac940feeb08abb4671be027a0bbc32aa78145937847934b6fbe2a82119314b691223bb23016e183ddf92f68e660a530790b15a976f1146b9f460775f4b1dca620baedb0faa086f37f535c8fd7b87a5a8b717ba71ef364a674d9ee093f777f0204d79bacf899a2ad4f39ec13619b6658b3d68eec197320729b4f3cfb8a1dfbc66b403f1a15ab8bfc92b9684374128cb850f1234174a5255e5eadc0dfff331ec709ff1799f6bfe28d3eb084c82cfc8d5262178e5f60b9e54908c34627ce6674e40104fc1cd459325ea6c051fa4726a178ada3d22c580a57a82c4e2b757707389f2cdd7269f67d592347a21281c1657e920e328bc783ad3a62c4696ef45aa4bf1d074f57f722c2832a956df52ebd38b8db0276c0f2fd9091d40199ca1744e1e182deb62013a8f396c9fb7ddf63954c14720ad11aebd01682b6a6a0c001fa879bd0d4fc84d4ed357bb6a4a9ff957898f40615040e1a4cff918ffe02d72d602fa8bd04ca46b5c7111c1cdc22d3a7e33db46c8dcdb2d433e3fac6c197f5691618676958095632bd9241b80a95e34e6ab435568091821ec1e574aa69683a56845401b031eb76c3c27aa817b7533057736dd61330c82303afb906b0e1c2c26eebbe66b6bd7396248340a2f0ebfdc0f2d4202a8c87cb585de10d41c2a98367441729577ddfd29d9067d83e73e79ef1320a3e1f21c4a66665d6bee09f4cb3e8a80dfeaa6234468ffa0f79427d630758da4dd92f81a89c7e0e354079793bbf2f6a6448542231b8cc4fd2c1ec4732d4b71038bbd85e437f5a0999fdf0a88448978724d437c110b828c40eb215a730676747661de9c7e019399f5856a73d2cc7e07f17b2bd6537cc4dbe6b91a94b630c5445231d0d1a0be8774c8bedee7a94dafd1ba5b4d44cd1ac175f8bf079e8e4393a690c6bda7200619714cca773004ab56466d9b9d5cc05bbe6e9e00ef12365d1c102e6e508f7177eaac873f933d7f5a2d22a21e01524bff57d89243e03b78f00021549b8d116f3d87d35b551bb161b509e93dd10fc05b5c4854836ee17196413fc6f5f57e69ed561b644de5e30693bd187ba1ddf9572c4484cf53b2ce5a3a4d9bccb9f723b282fc85ad659bdc3bd8a02bc8f0846530463e149a84fb9e6ab20d550252f12b0449b19c3cc2230c7d4096dc7e81898be0617a0f33bc2da874b7e5823ee646c59e1626b350fdf9e6fdf406e973a45a9b867ebb6e8bb76a656793937191b6418124337ca6b998019ea8bcd9ea307a3b619d9f27a21b3b8e5a34c2ac0bff36b33a77c284bbe90177c1877f3015beec951ee1a8061d028ea6af6d7b3ac18c7f1c4df258be357f58c747b0fca5a87871f642f17846c71ee5a244e094cd12635e70e3ed760d912f33017c365139849dc09a10e9f709a231f0ba97a15989bb5721cc32fa93f29d764778c34a1cd9e058706520d31c0b32e82dff82caf58fc9e2851881d19f9e971fd12d0601d56d47deedd7d33b83b52b211d09c63c22b05f5e452cf6775f07acc8ad6d06460f82de88a8cf19dabc1c64e0b6763af51f674e3ce48202c14f40502d8487159a0e7032016cb4f62b0bfe685300220b85e960b01f53d340e77f522d644da47030a769e4a5ff3aa55aec8622094b9c52d0feeb7bb818d54bb5e84f52db8bd08ac7f7b6dfc84c570a6f327592be16c22a05e843015771d1516c29396ceeaa05bf9170b1e103daa38ee9f30afb56672e2bcff6dc5de681be562cb709b71db77bf9d997f5bbab88d003eabb5c12299078d279233caef5a72656bce30e274c5e6eadaef3ba1636e2406855abb67da4ff4503c736fec911f9bc80b96d3b9fa9716437cd10e3e695a47ce6edda41d3cbc1459f9f70f3ed245092dc742667c2f35924439dfd01df022525342b2fe45533397ccfa2f4285e78823d0da46b12757b0ce468df8c376070634a8972a403d82015e9f6641c60119819a22c7be771b5c4315d45699feed4f1ed56f10176e59e0370b01895742fb55395c3f3b24221ca8d449569f26b376c9fe72f0c01ac02589b4b1924191dcbd7a69aec4bb387576e31a4f60f3553981adaf0256f16c40c7e7e76b2c057ba18f79b226402176e745f9f8987a94aaa4f43da0ccc13d285bfe974babc327c9631b11e5f128e62e4ba9304df238e21784c821114dcd6c5fe77af5a29673cc061540677a217bfc2bb8ba7ceb73d7e1e579a6146be3970cab403d7f1872739cec923cb1b1d9483a2cc26057e4937265a1e4cf8ffdd3d1505ba28130973fda3cad7d1f1627d45fd2f30e6053096e6c8f2d0a65c8d82a816c2a55999dd88020e92e1cdbb14847e13583186725bd94c78c174d85cbbd73767eac8edaf0f55356cf9a5d97c951248b941dbbce809a9b74a051262bd98dec1d2e81554f082f80581b3bc3e40b4b221d55505c8009817a0d101c8f8d3bf837bb105d8007b80c9ad0f35090d6c81c417ea02dcaa2d92459f7c87b40c67cde83884022eba239fe1d1c2b6215ca8170379ed7f8cee553cac02edeba8874d7abb2c8c6534b2f1012a083e0cd8778e0244ed831300c9b55ae9c6c1e57bd592a280fdfeed8acaea0f2e53e8395cbd0e64a6ca4a81b3c848302f6fb7d49bce25fdca5ae6f53f3a1089f6e934d36931c0f3077a6d8c421910bab202a3ae18c574e248b51926f2cc380c3219c7685035ef89468fc4e831b1df4d25ea9f4e441d7604827158e00cd5af1f27ef2cb89c07e394833509f3ef8ca365c06591a3d4eb79c0120221e65a252a9b8824a02a158c81790ea9fdc466f045609d35c9d77f22ccdffc9d0a9ef9259c12e732df5147086a34ed75dab47d1e21a9875c9669956f5fcac352c66ac4291c11c09c9b37ab96f9abd30ea23f9745517f777ff85fbc4465e90fb47026db61d16ecae812535a0d1208862778beb0f92004384101f7cef859756933980dd8cd2c7ca2b8d1dbe01b42b0510af84c37931e329180ccd1f6caead9bc676a4b0f6cd468f5b36bda6e6f1129410a95baa462d7c1017e1d263fe5c0b0531d6741c6527fb51183b9b9ad59ad8ac0e645848f5c4c17e5aa5499b61dad037236a35dd914421ee74f0d595ca929027bed379d95ae7918f4df433f7d0c19f515f745c8be9ba820a2980636ccfca81d9fb09ab9e6426c4ccb2639bb4735305ce5b2df1ac977b9080609fda08b8e173287b805ae9c84f6d37da39265b7b06d58db9ac47e430dd04a8e2e82f24bec37bf6af2bc20ca7bb7e45d54b91c3542888fc0964ecafe033318e6aabe76f2b410bf05b7dc5461c9656ac0ea8b3d3a1c66bb153a4d7e0ad0544945eb3064d18706c6add2f00be8710990895c1216d17d7967757f6f245941b584af8509273edb30299702b5517a6d60353cb0ab17bf547c5b1f4550c2be1a26a8924d120d22bcbae5c6c5d0712e5e3cb45c0cf5092bf154128cca02d005075a16ff1c898dd8bd8270991b8ee01acf2437ad90527a3e4e994c56e7c63748e237e2758e0bc704405469496673323973cc737efefdb9a92e78a73756aa87a1223dcedf69680667e669d8d4a311ec05c28e1e2b726ad828aaf1f23d1ce73a48e9f6357a35b6f8b5f2ac3a3126d94890b2a32f19b175d0a2973e28f945cb57ef4dbba85b019bdf9364e2beda7b949cc1b33618cb3fe57962dc61b943f6fb755a8fd1e6fc590c611906d931878c243366382a5538bfb1c4cf0968ecf6ec034cf0984e2fac6ac855a6f891b9a0a660ab60eeb79ec425903bf511be63da0490c9477ee231d254eca1d552964145c8915528d8ca778cc94788d640b6023bc88fb7fc5c9d93e26ba9b023e920c40d39f1b462702618807b53417093bbe91b1707bc2880b197f275b04efc2822b66b31af36697c1baca240ef9010518cce5105cae44c7439ca78924cbd63ef35d4fda8eccf1cbfaa43e0984ec8e26cdeb2300c88a7d014beec70acf84c3e522989dae9411c939de137299e1de3956ff04fa727a8ce7e071e2f22ceb581f5d22b16fca079c99c7285decaea6880f1016a2aba2ff1c75a8d0f88ad1e52bbf385c8b0322f774b554269eea3e6f2ee7ef7170538feabf0bbf8eaed6f45b19b04411d2cc67f46d1d12a8c2af54d08cc4f8aa9ceeb953954372c734d65f70e21b08940c825d46748b7318ec01fc82a981e3c60547186b8cac13ecd80c30da8670404cccc37f99b99d33a220159c083b1b513cf529303abf3580a9cddf1ee92410700bc3e4eb144b4be4a694f206c50fb6ea04fa2c13ae1048e1b3989a3cba59c78d262df668e3f5d80a978a0dbd073c9b16603cb8ccc410ac34d69630edfe6fb1699e83e17708376178edac060bec4d60cc00d2d0d7a0fd60a86dc00c0a5893b79df401aa833989dfb90865f021a8692839739f2b40b785ce454d193bad764d806a2f7b6583d8713bc932cf039287dec3315c78fe422d98f74b93ee81e51e7f656283156dcaf596769ddf17f34519ef9440fda41104920bc8dd6438ceb71650571f96620742f65e14bbad0e9e26c0cd283ef444982c0d1f94f6e4184875ba04b03c1eeb4dc17fba1fd332a6d0f154df730f36fa452d04bd096e0b53e54102084b2421f37fdeea89d78d8c2948e4c849de08acf937c69610ee0f8a07308ea17a9729d58e3d8a40452608c374af01607a6afe8396b270269c92b133b612b972b546c772ad9e777ea0ca2fff6b38b88242d410b56318a24c302f75bd829b60c3c908e5060b99c66ced7e4d447cc4c40459807045247e755c941376417ee9b69c34b4d55154c87c8535757f8fcc343b87b450bc15b251145b830ce2da6bc9690ff4c8050e8386da57250e77ba25ae7d0164f9d862f68f6acf8accc2a21a9ff8b32e71c2bf7d9012dfa172dbe4e00d601ff3d076c2f44d516e5878c2c5c8ce83fa6a734215f60ff9285968e7a1bf2812fda5fadff471ecb01c62545ddd67c6d342da23ad9e8b37561f00e2351f17f6aaf1e1dfb4073eb110d735c87ca52f4f4c0ea61878a1ad01a3c827452df906e7ae9f1bfb8c47374f0e74baa75639dee76f81094586d1a59808e6c044426cabf364928f37c8f23908b008e27d5da056c49dad3abb627cf84d76190c765d240be504d8c2a2aea2e4a48feef830ea94cb16c7c21375692fd481c50177b3952ac72a9ad50dc6a3755c6283c833348d7eb0a32c807cdb1277c20bb9da7faf0710bfcf0d64d1101a64bee0207237e115c3f2a938edd184e74fcb9b18f8d5393ff524b299a6de076de72682d45444cfe7a7edea3341dc7802b547b52233c255fff7178a87bbddc9d6ae11b27dd08d8c8bd699516eafdd6e936d181e847a17aaeb2c8d7161999f7ee07f7586b3792a21ece5d6fe2599961cb21429190ac520e1f305ec4589ea1015a2754f7366d0225958c4dfdc048e81b6cd125a24e212b9e9349a3c832aece32f56fa67b2f023bf135986e9490481f544e32b84123af6f879a52017a64a281162d3ab860d82020217ff304bebc098af7046f7daa3b7ce5c2ee18d91768bd9024fe422251559cf35ad2596594c23d6a22cdef25c3a05e165b9b8915a1ff1b6ee3bdb4f1a630493c898d7fec6c18321f881b685157409e0242bd7e313d76694b2bef2b00ad28351b999b2f44b7f2fd16614a109afd39aede51fdac0872316072bc0ce50c7823de13cea94669feda7b89948023802f710086be76398bc164efac9a8646a052f56f8e3f307a76159203ab50385378b3f0218f536ee253de61824e591ddada9b425f38688a47c229574a9fa84efa5ab3b62dbf578bbf64c6b9d32ea27217801b05ce00432701818491c1c59a8b42fbd4593c3f2ed6df6b457c442a015e968575d09010378582b5d6be91ebfc662d077b24f277edd21364e546130532e0ed2ed5ce8b1fa3f18bbf5925a1bd903870bb9e0c12c488cee50d696a9dbd812dce977670988579877ff9498a737aaed83cdf1d4c16e1da05872b84bdf1e1dbc31704b9513e0bd94ee3b1586ca39687a5519b8b30363dd0fc3e0c08b6c00a223ffce5bd3b09b2f7974e55139986a86e1d9cf43a43dda3db328fb8e14ece3740668452fe4a1e1dced5b299e7166019b0062c9e14237fb54f4352697e53e1d6ece73f7bb3189f05374f608159b2ff1024541ee14782527736a469bfb9e139c92efcb810fea3dc2864ade6cc3ac38512055667763e38f32d27db3a1f91a3624625d464e9843d4fd400a1b957880c9acdbf5dc7c2c3ac97f4482b6aa2cf9aba17d92625d88e49d166255d66f9bb0aa408d52b6408e9e8d922fe933038c8273bba6865a0bd468f33a809f4c855e4b5527b1cb333ef978aa4cc12efc4e60b0ec5a5c2e5000ee5b1836a8c99a916ee3ff51c12e6becae458d8caad82b88bcd2a5d2ee85c08628c29395c59c18759d13c84c2688a1c447e4c1415ccd0555f1c2733ee8372233d679fb8db4fd61e285e4d76aade80f07b2b9e0e8cb71b585974d19346ca949332e9436639d10896ed8dfaa92c04c85f1c83caf2005ebcb31fdd98204fd08b5c09df38913088e602fe7833fe8017bd8a9f836b745d2e5ade00207553c27ae32a26ea8a3d0217e68e9c38a24d14a763282ec071b71c9ae0f42d9e3038dc80aaae2ccc1f11ba59e6092a246e9bb15442bcf3c4309750e933ccff43a6cc928d914817187262d2855ea716a434e2a9d9c3ef512f9c5e16f39c1af9c4abe9304ff3819529b5ebeec93d47257754eca49498a904eb58a2007a8e4f33f3a66d455ff5127816dd2f9f00e4bc86ec838293c25c2fe7db0309e7fbd53bf93443879894d775232316a9f33ed8217e7b8574e169cca4bf8b2f477419d0e89d037f0592bb260ee4680ec3e07fb8f095fbcfe06bb615ce8b1f9c98accce3e8b01b20ff628acebf997177fe33fb6f6d5c1619db6800827d8122f3d80a3dc34e6cb817999f21b16dee67ebfdbb7002c6a44ac52020180c8705fc0e7ff8e35add46ddfc2f558061478cd1640252ec4cdd30f2b0588f498f286c9959debc497e93689528610a3a1bb062825a904415614a2200cecb04dd4b414b8b45a53c769dfab6ae6cb6b5d9b1a78f8c5af50b3b9b7e0507993b25bcab0fe9bd5182c76da582263381590c3f31ae1038dc839abd7bd8dce37cc7ff8c90aadf6b73b7dc437c3643050d174d250173b366a2145d24c78a3f88910c792d52914a3b505f28da9bd201726e7fc91f602b38a58952217e5e9b6f491e9f28f621e2938cd06394b34a44148a22d5018d00904d0e464188b6fe9ed084e60d4f9b1baa51967b0691a05da696fd75099468d13cc3bedd8660708196a95b18b66bc7b61a0a1f256384ed0686a1b2a1f4e12f742c850a59e4a3cacc253eb27aab4b04a6c287f35bc1604f12f39a58c86b183a6aac029950a1345810d69ff39a8c6671a73695b575fd1c165dc48f689f037dc35547511826807e14b02cb0ab843485c869520c8b530c8c1def9325e21b864adcd9c47370c6e883945b14292bee2a17d60023440a21e8053b055ebea69dd48b66010a507d85ceed7eccb4d833ab47fdc4d53131a14d644ab52e578cf8fb20435657b7557659efe383bccab3126ed64b78c312afef40c27be7c89854f434e5f1b555e8659f45640aa1eaccf31633cf91a812b4d67a2f87003b981f5bfa0f7bd1f69b8b1a4ad7a122884d940b90c77f85268fe678d2354e9880d2fd4188a98039ee7e005e631ad05d40bb926aa16e94982925e6dcd554b17d91d12ff7d694859c3bb19d0a2b011656184cb3b92d4bc0925fbc44428a629360475eb8b96c2f722f2fc330b4a9cfac1324349fbb2c44480f7dd53f2781f4c3703d73e4f6072c5b09a0ea23dc7045f46a3a30e4c0abf12b4a7aff917e029a82f2875701bf1b9e5f9cff407b73bdb58c39c5473cd8c7f2c4a457fd4b7b1e9aca03c61d647731340c7ebef4e420a9390c1a80135fb46371339db3ead528d0f8050213bffa502060d5fa62af9f539022680cd041a542bdae9d25eb46ecc6d474a7da8c3305c33d951dc48aa90c3b2bf98feca3eacad426ac9cd07608c1ef10e2db99da40ea5302cff7786f7f6e8920656a0e25e3792b6a5d0d155341b4a0d43666e124383affcdec5a4fb062b91709eef2dff80c2008b51122ef2df512d4b320de313230acc60eb06526bb48194b792ea4d84de837f28c1021ca4a055fc5a9f43de35a98601aa690b60ae5ab14c66e40b4311b08b750a5f20bef0928956e7e8bb4803c934dc396e00534d34e9021a310e735f4010b2da1c5afb5d76ef31d4e5f8096b657cb4da1ce889b812425532bea59eeb44a137a379da8ca8d646245b0a22f72893b82740dd7cc0f6c8ad5f80f9719dfb41b49aa8623056fe5afb4be122cac76aebf5b7b525a1252bbbbbb794524a199506c5067007bffdb8d2650f1c220ef0bfdffec471f5ad0472fb86630be95badae2e5657a737a75f4941ffca5b506b0aee48a570fa95f2c4e92b2b9c3b3bfda7d77f9232242548afdb57c11ca75ecb9b8dbe3eeb6ac535056d545f9e2ef4af600e507351b449e510cbb386fc7421f51bfd5416bdfe89eb13b743f5f4ba8025a75d737ec11cdc87af395e2db84355860fee1976dd427efac0d1a5bf21fdf62a6ef5f79a6f5cf5a2de4a2590daebf9e39eb5831dbc017ec857db8bdb56daa852220fc53f490fbee62b91b422bdf8fa43be0a9ff4a5dff86afc133781a0e6a63f716dd2f4417e8620f86069a3223ddd7c257e897f2b9e32f1aff4d1ef718ea5cdea57e2ab3ef5a758daa8e8872b9efa50c553bf71f137df7ec5554f411b5549af0b0dcfdac3144ad85e731eb76f2510dbeb9661d7a71b03bb7e0bdb6bdfd5b77137eb32d9140f1ad58a4e1c9eaf308ecb9316750aeed0ca4aa455ddeb8d7bed713b7078faf61a87c7baf4f617ffc66957a9b45a79ed38aee332c6bfe113f073277dcedbb0905dc3de364d7b1b1607dd81b156fb09557b8febcffca44536deeb6a7be6678579a50af87a24d09a629e392ef9b94f187a255da86fcb7d5d68dd714b8b7363b0b63ab1bdf65ae31ab8630377705b79eb129def0f8a74da2dc6b56e38c45ab74dd36cb52777bbc5b1e14a8dd7ae5d6007d9f71360f145a13e8efa7687ad3d9ff0f5f173bcc3dc86fbaeacf7f273775a7236474caf5d07b4a7b4d65c7bd47e1fffb6658e6951d7722e4f1e56d3b4b2056e2b7d685ae943d332bef61ec1433e20ea95d25a71bdaffd0fb06bfc729f9fa39fcb9b158efab96c612b813ce0c4d14a1ff97de40732f65cfaa8ff03ec15972d00a957bc7ed13b10dc7e85de3d87bb6e23c3155a566861c176915e7cd2dbd724bef2968f266d5cf99194e364f9d962cbd54a89e35496ec7ed426b75df915fa7e0d4c80fd91b43abdb8c2f8a41556e8e3ef93b4c915f27779437ea9bc31b5f4fd634bcbdee3388ee3388ee3b85d5dd7755dd775ddd972aed0c571b42f561ef5a872e5348ee3388af4c596528eb1a58ff6492328dab285470b255aa1a503a93c60edb78d2bcfdab9b79fb7af5ef7f4f373d9cc1df7a3e7e7f19d10f87074b5445c6c0d7e9b7cd1520212a387df939ef4233fbf510cc52d82afc1ffbeefc77e4a122710a38bbfb528963ffafe9ef61ffd133fd20df8babc493d89c7d749bf411edfdefbdbfbdb5b731c17bbeb74d76d7efee8bbb437f89b6bffe9507481146efe7de2dfecff5efc4417be70ef127c6a4df0fbf0046215adb5d65a6badb5d6dca4b5eeb4d6553ccff33ccffbba2df1f83ceffbbed2a39bfcf1c18038bcb210adbcf9a5e2f20dd6d4b41c5c795660d6b5dfcafc9a80513fe9f3c82f1b8416b5d89a547a6fe54cfde94d1f57a19ea0f853fd06c5ffbe0ff787e3abf8a9bfd4835feabf1f4bd58bff7d2b3087fe51044b1c9bf60accfafeefd4a91755630a2c6f502ff2d861897afaa409b5523a994ca552a9f4855fe90bb74822799cb4396f7b9e165dd7755dd7759df88d3c84293bbffaf797477ef5aeebf2ab7fa1900ebee8e5e8b2db7de3a7c85381597ee557a720e090f593e239eebae06de3eaf6fc347dde6f6557e2cd65e34c2a98a41ca1c0d4a7c82fbd6afcad477e7a91dbd00289d14fe38ba2c8d91f5f34fdcaf7a7919bb649dc1a9800f1ad9d408c7df2e87b8ffc247da7ef4de5cdf7a7f226fccdc334963c4c5d0c9fbeaa14a640b2540241101447111c45118542bdc751a2288aa27864fa713c897f12c59578fa15f8246bf9f8f734756bb91efe6932997898c0a2df36ab697f3bb07b4aa9a64bafd39ed3b42ddbc87f7dfe07fa3bf2bbd5af50ff7d4af5a530e45ba5edf26ce13b10553ffd7e4dfc909fdfc3300c4ffbc50f57f869d2504fd2506fc38a1f3e3efdc8c2e6389389962b52ee1388d14dbf229507c0e9a6f26481853ebe6842fdc8cff14f650853f658a25e2c6f504f2a6f542ffec87d2c7fe48e2aa93557b9ab9e7e29a55a915f2ab5c90490df919b1c37b9c7adb5fe8eebfd83bd4d7ce4270b7d0cc3f28cd1c3df7fdae51903060c1696172f562b952a952249146a65e57432994a2512691cf716c53004c1efd3daf3ba1e767da350e5e9f4e7d652fdeacfdc37b92a4fad879f7a55aa3cb5be7f65ebfb2959ae3c8a53ca5a294fdcc33fe51e3ea532a70fc3f22cfdf8db34bd8d6be2f447ae7d899f3f3269f3f1effeb13cbf6fdbb701683fbf67168cfa0f9caaa6a1adbfae7f5b19efb5d6afcfdaf19347efb4d6af2d0a1f8ffefdc64f15fa579edd5390d78e3b1d6a5d350ae0eeba3c79f4ed3dcbade9079dc5cee1b99e10b53dde8345d4d65e18cab230198a2d88918f51950cf493cab41b6cdbe910151bfa496558a6c346851586b4e0426b11458b1c18f9e0a7f4d62fab3032313b7d8b2dbdf7de7b33367db56b1c10fa8504d25612c6d4c3bd65a5ac6a2fcb042df6fd7a4bd3be38678acf1730d7c5d2a15bcef8e14584fe82fc0069020c37c4be5f4dfd962e9d5a99974dd3862082081e228eec27764e7d5d17fa72aa823e2581b6589d52192388a0514ae91572850bf7da28a5f40ab9c2e45edc1501f03a13915d3f87905fdb2404005a28fd4000bf6e89255e5873fb9628c3ca6366ed66be3f9394f143fdb069afd89a2fa660d7bf31c60ba39f94524a4b24165309bc419fbeb877452956e5154a51dfa594528ac97b494a55a995d3bda6301663c9a92b58be541659db3412c71303c6f831364e65b14b7eefbdf815ee9c531594527a535980f7664dfbee3d550180100260d2d746f844385591711ee13b559131c618cbf8c427767d4cee9d5d5fab9a04967d21a1c2bcd4171258324610c1c50c400830218d2128f5eebdb7de7befb53033da7f5c49c93e5f7cb7abd54c0b161b538bed0c8fedb5a823b66fc90fa383965ebf59d75e73dc3ff7551a9aab5b1cdb734f632e97ab484df74f65b6afb5d61bb5a5dfe83af75be980950eeeba68b4ab7ad8102859b0a6f6496cfb2735c21bd03de8eb550af4b7b7fd769a7fdbde0465f35853889539bfde909f746108c7ed4d92dd6ff9d5737e6e084984db1cb99f247bc671b9684d266f5600ad31c075c95f852acc9ab94b62d7dc44d396adf4f8aa6a2e8adf87250ce17f30e8077f01deef1206f1bd074b187e6cc4f71e86f1757db0865f3f0cff2b5ff85e2c57557c1bed3d58be20bef72f8c3835143cd5ffeb726bd9c3e6fbcd9121dd3835237fe1c7663c77c45f40ed810852e0aa812186d225be58c600838deb72c7fb7d5872db0e803b61381dd7250b2ae338351fc7a91979f89b8b5f790a1fafae1724e0725db82ef77b2880d6b4a0005a337e7f56d7873ffcf14f3ac32ffe7e5a037ed5b92ef7a593a3f3e2e17f5cfcee75e55d09a4b6606b0cec61237e58bef042f8615961d7e5be02684d902a248a4f6b421bd7d61a0ad6d244f6edaef0d75cae70894b1664dc90a62d9ddebe1ba5efb84e879fb84ba4f14f13fa0c2b73ff9e3817d3ae476bed577b5f5350013840c06488d2030a7e6050831a6a5dbcb71c2c2dbf7cc7f4a4b2c5bae01f79ed19bcb185d81f2782e0c731064ddcbfa7e0a7b9d955dc62be6b93785b8d089cf136c3da0f50e12a13dda665acc40253746f4807321b9bb5f6372d631bb35100032040c90c329801e854c90c16e85fa743538460804d5f49aed3d751846b686ce740654045c5c13e55c18f0e4f5800072b7050639f34b77dcab5c049b74f39fba4d9826e9f72f6491c683cd217a1765ad63e6b9aa6695bf9a376d5c55a0eed2fc6da6fdba651a1dacd8ff18e7c1fdbe09c31d6f8cdf8deab84aec1f42593c67354beed0c80e0ef0c6cfcfaed6fdb06e4ae706c5f7f03752d7d046851bf5979fddca903716c4f5f3565035f8361dfaad5da07ceae9b7ea5bf9fea18706c0fd2edfbca936e31742aebfad35a87347cca3f1f3839f6f7e0d33eae4e085f87b48a62597bc4e0ea3e70faa67de0f4ad0c3f6b7a03fe87c3b369fdfa2b3f6daeadb53c6dea59bdd7fc8441a7871e2dcfead592d2a2ef13ad51d7fff16bcfef2bcf1de4a7edff38287ea0b712df13ebd3174b1cdf8311a0455b89a3c28afa56deec0f576a75f590df94def42b65c9644295de549e381c2dbdfed2f7a1fedb5e453eea374e491e21a5281e220dbf5da2be8f8740657faf5f1791dda924555df7df47c3f7815a6f1548edf50b53e4a342be81e0f621ffe817fe2986e24a0cc3d7fff1d56f7c45faf13710d45a872208aec0fdf44dabf0c7075f9b78e9e3a537f1cf64fa9e6edea3924a9bd4afc2271ff56758da908fe2418bc24f71d48b2447fdc6c31ff9f6294e3e056df6a37609bdbe056eb8309255b98a88528ee8b948cf7f87f4fa39db6b6dce91ff5a8b69cff4354e2f8eada4d7e55e3047ada70dbd2ed7a53c6dbfb7fc51fb75925fef20bb67e54f43529674fd2b55740e27e0f42baff11c5cffe55bd7fdb661e3df4e1c0ec85de1d0b52bcfd303b9fd06f5db736fc376afc317450a8227dc881bdfc014177fe3357c31fca7e7ee600c38f47ffaeb83e0156fc0a75dbf2ec350ace0cdf7a00f9cfe7dba82ff3dede3091faf95b2c8d71f3fb7df3efc522d95e7f6f4ce9cb5ebf2dcce1e1270f51fb7fbc0e97a8ba1935ed78fb7503b69f381d3357dd293aafeed757913bef85b79f33de88278bad0f5b370392b8ae569235657dfaff9c942b8c5f2ac2e6e2bbde6e79377db1eb56f0f96e70ed583f9b717fa383ec8bf32ff76fb0896f9bf9bb10452fbf69baf48235f910218df272690190111040840403c403c4040f762ced35dd6b411cf4867a49be55826d2b4cc03e444d6340c94cb8d31d0b5cf536bdd382d73db0c1f60cc7ead09449dac012f27a783d1c524b2a8c304880990112346c895930a36d6364ee3b5867b92cc18df274a5c8c374ebb50acb5b633825d23e3f6bc7bafa639cd3d1eef3178c3eb3a264050ae14186dd3b04c33da360dca927b2fde625bd39ee3b64d8382e47e1ba7618e52da0476b1b53b30020202020202c2196b1bcfb6cd7fce3e81b58c392ccb465dd165028471c8187346f932017a92639ba7bbd80cec84b2923052672040878db108c63462c6b2bd5630016202c404e8052e80a15c6c635c022c6b010b56f0840a52e0045113285032c4c4098c248999a0044b20512209248448300211001d1174c4082c04403ff79a2000242b5801ce3d414195c868c4d833167171d6b658f8d2c21765d9f015be7a9871b3ba84082ea6154130c6312b8244d1c4071d10807c7c72f0c9c1c72767ac1d397224e79c878872f0c9e13bd8811be6cedc5bc2fc3803226aad59c3b97c982fc90c3b00fb159c31c3cad019946567cc00e2e2db049ccd688b8233960ecec099067c7c8e1c3942fa004511c5c5b92663d2c8c8c80aebc2e2c8912318f6837163e103c76df94559f6f5a22dea15f178a6f860e144144d724ecec1c287582c16c3a2d4c5a7d14c9bc0f0056ae2e3e3e3e3e39383cf913ff236623c642de3289cc8d99a744646b445bb255d91cf0b72c65a93a72c5b7d80ba0fd41eb76d4d320c465936d3163df21dd19e0cba23b986293e537ca6f8ec8c20a0b74bfdc3944bec9188b1c838c4f881518851670c62f4c0d88191c808c4f8c3e8c3983372e0c8d8c3c8c33864dc61dcc0a8c3f81a3530e630e230de306660c480cf68c32864ac61a461bcc018649c61b4c0f7df84d036aef362f7e27c2fe775b7e606d55a2b91082e260f3cd90b99c97d0ad41c2263006bcafe6b5003994cc775b16f53a0322050d33e495692bc34bb74cc807e2d05fabd1c75c10f42def4f35bac15f100085a6e02c348f4f4f4f4f4685b5050d02c96897a7cb8f8f6dc1e29b5568c61cc9b43ad55db342a2c4bef404f4f505010090514386b30da93240f32102528280826c3b8a314e938cee3e17aa25c25504429128b723110141444051254f4f4f4f4f4f4e0ac693d390807dd6b3f70f1962fd6a0702267a3a22bc2b787c9c5176b19df9e4fdc21a83d8f0addd5cab1f46e07894121f805651e7a7a465717324680e95c6c51858a16538ea4fcc0075944c102caacd61a74450fac78c2831de8c0490e7070832aa8a8b5f6d8a089510d68c0640a2936d8ca49c900ba25696b08e5e956a7ea602b840c8a62f07aaa7367b4082f4625348119256912be60b1f0a5ab57c99ad2eb224142bfa010dc0cc7401d6e565720828b5992dd2e00b6e3b0a8c3cd70ac9be1188e9564438c3c22fa26a2d7e572a850a7ef11dd190350226f0a6f0a2b737a52785264a223922449eb7503fc40e419611900406350614615c682a9032050db7d57fb165b39f578c76d2f482dc3b4fccb30ad69da27def38bc9c745995219d83fa96942c9b1faea66b18d67a23c946304b0a6fd193362d703f56fb6b556d3fdeaeab7c4b8fc5a2546a75446db3e6f25a9951908d2acdb87e91be78ddde2241d49882495a403ce3a65696f593a69663fc0f2826618bed5561ec89553dfdd4767eedb92bc66a9897d12800056e6a441630bdd99bc6512f4d325c36cb027497c61dd1af560d7dfb1178907969a7949f40ba6c132cc4ba25fb14bc3b8b1882b57bf2ac33de5f20d23a5916fa82f96c79ad62b624dfbbad6ba554f27bfbc571ef2d29e089733e4864a23ebb0430eaf2822a57546e4d1bc22b6a7ded12e424427f70044a79f7da034f290fa6273449e3b9373724ece119120b132a7a884a844e715d1ad77b4951d514e2e9287541ad9870c44ee21e7e0224a2e1005dcef0b09c124e8670d6558a8d3cf43ac0ccee94ab043a79f75a0343a24f5c5825062b26ec1596cd372907e764f087544dd10924aa34bc20465a566bbfed9c54a2e0dd4afcf685bbe4d601a2c7f13182ec10b46f7ae769a5fdeab8b51d6c9065984f9f9f981f9f9f95a03cc6d216d5cf0860dcdfc14f0b576c7a85b2ba32d0d1fd14f93139d931b9d5113fbec649dccca9c5d0bba16843a74fb2776c286afaea85bd2c93a2359b755d899ae0aa3cee8fe70a1910d328cc532b1176d5dec8224b2d75ed32ebef7b38e4d7223637c236b7893e2459263df532ca32dedd25d09bd33b241a52cefa9f75b0c9dfb8d5296c7d23d6ccd6d73726f8d6724433c138127bcf45b66a1186cfb202ce3689b7fb015e6de6f792a0f030398bd64d767c9e894d25b4b9c47d0b5b650bbb05d6bdf8662d7df2fd8f5b70b76fd2ddbf5770b76fd360b76dd2bd8f5f713e2b755b0bf9d825d7f3bb1eb6fa25d7f37b1eb6f149cb612d4bd7b6833b14ff06227d9f5772cc6b74db0ebef12f06f2fb1eb6f24bbfe56a2e5db49ecfa1b8910b690792f07d3bb9f4d0211be3d8211be2d825d7f1fb1ebef2012be7de4e5db4660be0ddb257c94b6604a80d98eacb5d6da1291ebaad65a0dc0b0b25210f6a1e85b61dcfad755df2eb1535451abf45a667bed25e2561a6bf9dfa66c6caa5b896dab0e022726eaf471122a9399c0483a7d8c0495099ff4f4b1106d8d4f3351f7347ccd0f17e4c77b1abe26f5f4696b7b4d6bb516638c7b0e676116e1eca4ad984b380ebcb1a37aaec46984a2c08a10c287ae830d244dd579f6cb21b7bf18a5eef19d189f394d1165d13ca171426344338535ed1344b18eef04513d0d5c0d0c29ba7d1a19d0171b83fca701a626f5309c184aa282a31965d94fc120da6a541ed8e7cceb0a55d8ccabdb279b4482c183c000e16980a9299fc710a92ff56b8c4ecc105e0c4f4c0e628c62663151ee16abc631474efa69f6b3150381b7945001d82f79ab095a827dce5b49e80088ac69745d2c8cb215a32580603c8a211233448c8e17432486272607314631516266378627862786278647c7f0e0189e98a37d5dc85c2f0f8944ba7c64f1b08c60c15857b09cb08e5854502ba7ade34870b18c60f1b060ac2b584e5854584728166c85053bb1601b0bd6b1601c0b46824b974c26ef020000b052a9f4b167e289780ddcc55f7c0712b572da3a9e439f898f3d11af81bbf80efc453e11ea89569ee8f444db13754fc4734ca7130a5c12faf2848905863f1d16587fb10c6179757b5ae14dec29fe93bfe33b2c1eef81c5ff5888b0e8b00cc18d47a19470c6c2c362c48bf9e391293387ac8b8d593388dd8ad55e89866294c542a4d2a049c2320412161d2b43830413dd3ec742a45bd76c3c0aa5b018c1c2e38d47773c02c7a33d1e8d47e391c85b4c505692b21503c13e55aed79d6179bd54a7ef04dec87f0251c0ddb4729ae2c8831db3a63984326287dde33bff1ddfe1cff11d1e6486ad8b2ebaa8f9b99fe27866654eafd30ea79c530ca84bfd7a929d8c4e5450b37e45d5957aaa5bed6a3d39e9b596459c87595817aee7b7e00d5d86336bda3763668c46680724fdf4b2323448849cc6e5e4947367686438c5e0e824bb3335a09ca8787232b232357400ebf651e5f9d2cf53ce4a799afd3c9d4e39db29a73be5b4624f2a6b84b3a3f1289c15f5b37544d462a29564e6c4688a226b5ad9164d65cb948d606465ee0cb779c6a39570d62d4af5281005dcb5eff8cebfc77738f79fb266acbed423fe3d7eec8e5f8fe38cb246593f69b85a2ea02ff663f0d6d1ecba38a982b78c5a45ad252d1965b5885a43d66c41d1ed9bb14aa395a4c58419ebb615dbd9e77874129213ceee4ba350df57d2085d178b9ff4001fe9f8a0830d3298b25d49cdf221a71142594794a562a1b181ba7a8d29b406149a4517da31a253841047ad986ac55bb2eb32d2cc4e9a12a75d05ded8617955c777589e637991efacb890af493d7e52697455eacb0c47a132de0cf74fdb4ffca45b4fc875b1335e13d4c57a314f66331354c683829af6c13f5fcc93dcd52b4a8d475b279cd108d11c4153e2343c552a0d1a23f465061a9f6edfd2144113d4edd955a961c46b1409b93335a034419d74fbb64613ea02458d296456a6860ca879bef8d0ad0e6f43b7343278426a4c1192e315f5b0ac311b8f4690519635652c7cac625d78edf5c537f26093a02ec723964fc5d2afc777540f83b762ad27784bc65b45d7c5a6b890af51bd093ce1a57f65ab0568efeb42561de3058f5182ba34652dabae01b0ba5cd53bbea37a4f95e23b8454c9c2f2626bd9fc542c2c2f5e7c2fc01be612b3c894ad5027d04630752da3637d2aae6b8e49f8549cd6d4efe55ba9b0cbd52fd77ac77756cfad5e65ca76d08ab9f0dc191a4efad97281d1592b567fc5b995153366c6cc5838bb33dbbad40f67afe5d3783456318137c2d97814ce70c61fce4e53cba51289441a4799b904bc311ed5609fad98acdb6fc9accc391e751b1b6a11b59eb03f1ed101a0549cd6a438adb9dceb2798691c946d802864bf668b92d5374db0818057af7449100c8bf5f31395c84085121960fda443bdd68c2d75d2eb7fa65e3dd0cf17bd5e9315db5e2b0ad11e45ce5a86691996731581c638e3a08c83f085717766b335376c0d0551a83608e2419bc14887d671f75e6b2d06b6d8a9125712598a102871118101d60694b83ab06960eb4e95b836b0f5a0c475819e1d763ad485949d2746764785d006a338a28b26ba38628716265de820e9628876451736146d930f72b0f50e9eec8f061a145b433165772928b910d2a182cc46052d082e78645c14e92795e51b70f1834c07172f1d2ac86c5490828b0bc840b0c50ffa49653a645be86089fd31d176b04199165bc43424b62882680b2221d8c20620aa6cd14f2adb8854c142e653e506fda4321db22a33a842d44f2aab22822a44b6229beb74a8ca900e5471b9a21081831da8c83212fbd4e9101525fa49653f1b04c25ac657a6c3468515f41046a3cecfcfcfcfcf8f8f8f4f0e3e39f8f8000101f100f10001dd5bb7493f7df205ed76f4773d5a0a305d2b5f5873c7fbaec704eb92024c4fcff5a1e597b0b86224707c701832640917078a40829381293bb8401313e04027a84a0f1888620327ba6802060fa044512444055448d1a1b2828f5009025379f5dbe910151c6a0ed7de8a6f78cbbd6330a60c93a3c1fc381fcec44f6ebcef8c0c134802bc699ad4a42daa89609a32ec8c0c738a89a94959f64d9a71c098076ce221db5a37186c8c81b00cafc3305ac7d50a838134cd0371e4bfdf65bbcd60c8907b39237eb08c9a61eebd303f1a14b2934c96b1b671dc36ff5f10050c9688af3ba36d3a5cc813eab633ba1b0be16beb14115c4c8c739c3aa8248c998c6aea14cd000000007316000018140a074442c1348dc33ceff10114000e6d984460503014c6035192a2400882208a8118638c218630630c32c850911100ba255741a962dd396d142aad22cdc62a4aecc8fef5495d80907ac7765614fb9b1fe42104ef2d2624ec8cd91f9fe6fa803cc00f765782640460471cefa6bf4a7ef47b0dff8e3100ec06d08aca9416f1ca3f945ee1e04c4ac16625b503cec88dcf58a7bec2d76050be03a17dfd9dca3419b15e2433009d00b953f02e05935ebcf2bbafef4263be6692e3a68f47012100106e8857ce8c08b5c75bb567dcf9fa8a9d77d6761df10de046b6a7a885f1d64d7fe853b4a032ab1ced4b18a7e66da6a76d8859f99ae818a91a2ed4703ff30d841103e3a54a551dd818b0de4a5a3acb1433658b2bb832817c58b9da6e637cd2e222e51bba2ae88f6e6a87f0fb349a2e33b755b06eaf4f4d7aa9db331a14f0555759c6f4c58e23dfd939d70109ba147ab4db76fc5a26a7afe6e2392152b40fe340e85d9a25c2e7ee3a32a078ebc8a81e21ec5eb59234bb5b58416d67fd227d16238fcb0469fd2d71ca8a471d978cf573c0078a98bf6684e60dc035ee5ce58f56308a4932b10a9eab8e94f4d6af54e44e243b814ed47e5f162ec7012deae362db91bad29c7d6e0eb509e8cf356abae7315cd1f58058247eecc8b111a34ee0528e01ddd1ae4c825521680b561857c455f51595d5ba9c16e2c4fb6502f044093239063e359ade3df92e620bf02a916d9bc4a28c777bc53baa80bddf5ebc7f799d239606411c811f011c097f4cfe031156b269cfcaf164c669363479342516fda0e51e069c97fdc2d4e63bfa07dbaf42d233ef07aa9b00ed6fe9c73d4fa5524f38f1a4d14e9474f2504e967084b8d2091e9d788b596e83ef184f2e04799e0823a97ad64a502e27a97306576211a19911b48798ba1af26bcc938561850dfb4e50122af0073ae26a13d59f5b759f139ec54fb9d0a4f5040e4f9c2a96a1f01d6d5e6215576e6ba39607dad0b32883019ba772e021118b4107b32a3c4e47adf78cd06ebb088dc323559329dfa080d1dd4fd0622a30f79a4cc732508d367a1e0072f62314388dc545f3186312a968dac22ae69a1cd7844963bb6125d91631cb563c57022f135a9745a806de3e13c2ce9d8e72123fd989d1e3907641de120bacba236a2d7cfc0f8a5d0b10e5887373466e3ee8946afe6fc676ff01bab0a87152dd61c0938ac5cdf05055575541d57b13a3ed60ab1e1899c1e85864c0170111f799be25a3ef0f601abaf3431e30da931278c0a82f62ed71a40477a39729bf2e6084ca703553fb1ea3f6f42b63ff8f811500f7fa0a0629f92a435bda2f3d032ed691612c36d7563d49339c4498b14463838e1a94fb497d0b9fe216b7f32577fa06188c59c1ea8e8949651eaa3931ca05c5444e0787eb5e791f350f0db8334426ad655201c1dda33170934696e09610157874e07e3ce5fe8d2324caa9a883c988ef5634b95001d369f0c330a27fd671013071f7f614db9793a2ec898d05eb7e44bcd89657d8801bd35f67143e4d891f36fc43a3864623cb47137f7fda91a15673128c514125cd377e375a847b83aac638386323ed8cf1a0f4b37ac9bd12292e3ac0cbebe92f0452c79d372dff4c5f33073c6d4893b4f4a2cfd19a5ff2b3b127e012fb87b56c4c3365ba72c14c23da8efbd614f0200096ff7be4d154dbbe95411198b2c6ff5ef22a221a094b8f400710e4003bd870284892d1b80364f3b1c7c0f11ef6f0aaf95ccc1425d9e5e3a416fefac6800188e6448f89777d6bf14e891be90b0b68ecbc6347be53e1d5f10586888ba7544e34420f994a5666c8c55397022a70c837cebd21d1a0c1d18f925d06238aa38cc46dac23722129984a5b3946cffbcf14711026a378c9869308253fb4f33eb02e91c4ae1d1c409d57caa5e24b2f9c5a9b82720d74e6bc3f961fdcdfec7d46bc2f2330778abfd0a7bbc18c10f89aad995c3db46f86f17ba39835ef81a7dd7c6f197937b30e56587edf4bdfc49fe0d68e7482138a75a373f4a32b2e9cdd6a90e2416f159aacdc2afeff430168562990408164ff6ffdf8431fa061d2f8efc8d13bb669e3e6b8102ce4183d1bba65f81e948a1b7472988c3e2d44242d029a55149f21e503137df2644fb33b06c6d48219fdfb214693a8f5c882869dfcd4f8e797737e972bae40afec95a245beedadb7292e5918969f07f550677645d5ba4c3f5f98541ee1af2da46809cb84f26a954afbf8614505b4640ebeeadfc54e20a1a0466b7f12c1724cf1d000cb6f80218b77d567f3ba31602a43a0fafa46cea5334eb6034d3704776f2d0fb5c5db892b744906f156305f76303fa9ac9273c7057e2ca24f23f5d5d323503e0f615ebd86b3d25c4503363cf2cc9d11c722f5415e63592106f55eb7135abf7e79084da62743b678981400f69dc33448192754e1e2179938586751be791a747cd84325a8aa40c4583c2e0e682be8d35c97e9fc4fa06973a6522061dd6babf1ac9b7894b5869b13a4b811fcbf4d982f271281e55bd675182c89e9c6007e6fed249d07f719febee935e548c7f932d57e29acccdc909267f62f2e2a9e18f52691700b2b6315621cd38bdadd78b432a77cd32f903f274e22c89ffcf9815eb30e24e29e14796661cd5a7fc7e62eb43e1b4b5e1498ffd0f2730bcbb7db3350058b92ca805d0dfed9eb29d633725feb1d60a81adf3c3431a97a5bf6a90b9b0fc17e446d23ce680012a2ee89fe292d3731f4103091bcb87c8ecf243a816cd094f37120c10b28815c35817dd6d0af57d04771192347e42f1ab312c55935276c7b66abfd8241ef4e03da06d04ab3d4ab699cb0ea9252562a940fac586bc816b1488e938082970bd781d07fae8f49afef311bd0d672ee5b4613fbc5ab559bc230ba2b2e895b0ada08b1b7251d98b9e03bad664085af63b49056bdf2d584b6fa3be25ae692d0ee5d9bf8736c3836a0cc58a74950e0baea5b6f2cd5118be603e3b8ff1eed9240b292ef62753f1cdde21f1425d740932d4a9f5088270887d27dba432f2484aed0dccad1fdb4d0998d363a48b89965a2f6bb3c31914b64aabdb87744564993a2d4c52d9d09eabf703be46a4d100d9dff340f5dd4fc046301e77f8ab7e56957394ec263c3fd547614f5d60e8810d163350e9d02bfedc7408f400f9a5ff771283fd5fe31129c79a9c1b4d2070d7f443d09f8afb1146ecce7fce937220768426f405497259f2b977d3819cf08038015fdcbd6fdedbb34b1fd3710237ce1bf2f527c07269fbd34eae7b468351972202afd926115cd6b214be48734ab95636a0f5349f995010bcc1970d1b7489735d7136f00eafd0866c5d4dd01d5a25331ec33b7c952aa39e5738595122489034b6d90210603a3f05fc6cfb0c06e10dde48bf6c40e032e0fd43841b30f82bc542cea7050168be9d6f6e455b8d094bf54fada9ea970825e0dec399480790c91b34fb5b71e34fb51f66221310368f63f933e639012161f72f58b9d8d60cca4627fe55d0be0061a0f2c940cd4812b79d842f81b9dadef341dd5e7ada622b179f9fdd9a242925fdcf9aa3d4e856172e49c3d82997d8291e828df3e1b708835fb0590d25b8e7d36ea080bf614019e22a280ede6bc4059df2e08e33ca8f3835055a6560328f579f7e8ea34453764912467b85b03691ce3469e9058a69f2148c2e914439a584a338cb0781e68a255dd83301e9d5917b4a5f152bf35c851dc391f61e2c91a418b9e6468066398d729c3ba485bdec46d3000ce29702e3b8642d8c37e053741c4f0b147f044bb632c4433b7c013d0100275b328f5c0ab7b135ad5345c437f79a58b817419be0f11e3cdab34765e1d163d6ff16d3e4e3bb61acdbc1f3c744c0a23fa190ee70a6cbd8c44be276ea6f7ff279489a3ce0c2a6e33832b7fa2d977eb70598e00e943c99fd0decda8d96828153ad44879112e45493b93e49f8a3da32ea344218cce771e03359f83b18384d92c7398a80476d60fb2aeb7d8a19195b4f882e22358904bfa15dffd2e44a41537b841bfad2c0b16feb02c443a37cef8bbedb60d2e77058683ee9c35a20ae41b54b4c013bac0cb358a4914ddcfe3773eb05e9e102cce4eb4aa9b60e1bcfb4e7e0b38d1c44f1796abe6713633c0703468789bafc2364dbf651d15833cf70683afe9e1ac15f9531a890c6eecfe961ef8a64e18f48126b985fe8303e8b4186add0ee465c72c1dc805054dde914f37347be0625f598c836eb6695b3e9c415d90e737cab0bd08b308ff3ebca6c255b42116c4d6d27fbb4e985da04efa3f41834fcbe6702bac7e52304bc15266cf208a71146c4c4b5741c1086a6f07b6058b539fab89813de70ef45bc49d9a7ea7c05acef373c34e2e2de040fd62ecf5e94d864f1c7fd1b6602c0df7106ef71fb09a65eb67b5c529555a9ac3b62d15a16b94e5804704460a141b539e96ea984f5846bf16a541a351a271765f17abe7e0bd0faa7d665bd9cb5b4566dfe5c427f8d605abec8c4a66157764903c4e76ab61d037544682c1a393c7831521b96c48ebefa33971bd1177e8b578ca0f788625d70687977e3181157184441f7f90e610d2f057986d7a256677a3b9bc7b49fa3d0b9e95ca79d63834984d6c420733b4e80bff2e51f54c932375b66392524cc83dc81c223738fd40a39057e63dddf4f49e2f91d8d34fbd28fa0c9cedece2ce9d2b53a3ad1c90fef2ff657fbfeb8bd18b72b49aa9180edca29529e82c88ed7f1b548076a337df4fc5267d7186220a2dd09ab408d427c54d90b7458b7e36e6192d6d206dc623c3633f0a5383e619dc1d45b3dcbc327498ef8ad49d924f6ae0dd7378c1a79974da2dcdea3e113e0a5a51c0b4e1bf8641e95302d4e50202a1257302941aa926081a808e719477aef80a7dc945446274761f365434dd202c5546bddc1ef69f09ce5c55b4eed02fc5790670927614555e3a028768a128e3345d837c3d7939ea3b08165f98deb6e94bbe2074322c683aa0e11b4fb930255119dcd197d2c0a862f9faf5b3683ea1414cfdee4a68b99123d9d26b363f60ddc7d29e3043847cf034100b170b97b33bae64f198656618c7f9ab4071701509f3f0c9fd1bae0775299af96db73ae453cdd959162f023cedc5d81925347242b19603f0c56da051a89c52b4a250f46483084b226191ec3df51c853a2f2b8d9d859e8b3fb0a57805d4033db6c18b31a5d82c45abd5c4f8b5adceea5571033dc069c616cc88cc4f392b31fe241cc68c4ef39d4f8f8aa01ed02f781b76fe011fd19c9eae7f448d71a9d907dd5743cc3b80bd12d06803f64d7dff076a6572ad618e8233010bd4c7796a6fdb3e0885ac6652dd01e039656cda462232ae725d4c65c6810b1d8c6ca03ddf490271398b1d7a59ad107697710337e95f907050dcb267105e7908025b756838218d815281608d28aa9200f209a30e9fe8717f6cc3f2ef4051ce5bf31b831e78f1d7fc4c22b341938dd6745132181e8c277a96387ff452b4f302d034a76141b47525a38e27d301b8173b280eb074a067aabb722474a0ef759902852657001d90ed8b0f6727ce414d0352588cbaa7fa57c0ad5f3ed4f8ec71c4f20666c81d8cef173784b6619f689f2bc86357fa264fe9a63e33bc2123705d52ccbe017e20928b69783f7ed8c2f05df56ea4510e44dc41824358f07f7e8b69709ea0b3e3d70018f8a04b2ab6c0ffcc2020e2da40c1d34d530023ad0fd24c042cc77a24f254ef38a72f5df46a29104f248fcd6698460865228c848983ddd055bd667ee92216e52aaea3acaa7e4793f0ec8b08319cfac1a6a50f1e79dac5272826048cc27ec0717ca934295f411ee54fa22483f4109becf8177470f63062c9c5b465a02aad8ec39a70f29b7f29476530b7c2e09315cb38577da7fa1c6d62d5a5e2a0b5d17dce1a0447a7aa5a95a4ba5797a3b3fffe520a36db78788446e3c79d2eaa5b3d9fb932ffff8242fe0da09bd3e7b804d70bdaf4fa572911eb0af15e40bf9ee00e2fd810dc5f37ffa95a5841a46d30eeb3f991cc9b6817f7ca462a8969be2dc65e5bfdb73c05b6a8bfb49673e3c79589c8d2e8c6c2799c988f9840bd02ec4e482096cb812c100d65bd10c0d6667fed17198a2917d003b7881265d3079e133b4f3c840ece6ad666173dfbe9caa4291ea502aca1f8ef27f87dad7c3375cc9384f6877b2282cc341bafc0643be1d1c2849ef4651dc0e6a17ba0d5231b47894bcd14749a40b95a1c18746e5131b589d0832a366a36fe59e5704f244bb111e919d8989170d2c8b366c31a46cd9f4d13fee939330a40b70429f5e72d1893cc5b883452d7a02643344c83a0b0f9678f53aecb3c17648f34b62c3b9dadfb2c2e63c01505b38873caae59f136aaaf4b71e8bf93a82f393ccebf94d54d5b4348877d2256e68f923e02def83a6b2ea17d9974b18ce22b3e93c1511d1e9a70c30d5ca22b8f28cdaa24c56d36271a51bde6c0e879fefeb080205a3e8228dd3c021860f1b8834185a3cd646d7406b7e6306a7d608232587a36992f6948b6238b05acf11840abf44706bbc242ed6b996ce3caf540cfe2894de9bd8556e9ccd06c331795289548308fc121b9e4600e871c8e5850a744bd2d35ab3fc0cb83b800514049becdf126b140832686f7c13f8c0db01e5de598d8b15777274666318e10283ad54317c41616f0010094ce7119928ca5364380affc55438df737f909c88540d001641240d051afb5b4a60b64d18a6e491dd11a45e328c28dba2296cc0b25956c6ce8474be988f3630e3c952b0371721f745491e6239c8c91a42e386b1e4c136427ed869facfeb3e23e357d12877e8e7f074a353f5bd057b4f210c882873fe50d16a69f794c9516f2b1451f912da30d9354716686dfa242cad1aeaf98ec17922c3fb485c8f20e1718109a1c5895d899d2324c4c9672f88bba394b9d5f0741a67778e00b5f2939c2f6406ca45abfbe7e36cafa8951e3a38b4286a4c34ea017816e19d76cd10736989b098ee1a36b24e2acf2353b410a2bc3a0e2692e6068ee8f9436001e2bdcf62e32050c028fba41e75bb802104ba8ed9d8dc442e3503a939aab45e03584966032a2cee7bb787e92a054e3876483f2364962908edc1e8195067911ca1e25217db1e7e967d9b717aa2f01885ce983a930d6737b571461b312803d3ada025144820155387ae803c816de9a9da7ae07fec86963afaa8707a8c4b4413dec0143f0fcc4117fd8d1cf77086f7b8065c538c06906fcfce81f36dcdb10f52d29d25db0f30d5ce9d169cbfcbd04ea47a6ecee1cf79c8db3b2385a542c088656cbbe58874320203b6b4fb8d1172e8f1dc1cfb00d6ca310e185804b07175f3c59245659f4ecae46068299c3d6878896d59f90cbcad528226168b9f2d6c05bcb161ad377b281fd91950159eb70874f317d4ebbc359ef21a5abecbaa43d392ab86830926235c21d21e0d07a6da9aaa8c579ae2e7136ad095f716d8de75cf0012e60749b25bb1e8189607ebca59a1afa6f3c21c1b55fab28270c2c901b7789e2a0864bb035804f3e1d9424c61f63e08bb67ab409b9f25aebc829960f5a85219cacca6d3a8d859005f0f621e6872a8be7f0b988c2423afe2dc88011413accd46f0fd2d6c5df8312072c814a9ceb25442e38c32980475f9a0d28c7c7d31548effc3e9aeb7db97074eda2c5caeaea10e6c58cc02d5931994628c109d6d1d252cbf82f84f53160fa89c302d7b9341ddc47bc197904b547337f706f32db249424e697c8b7467fab8ba082e142448a856959576d792d6411c19024c5b83fa740dc4a648cb880adb4c25ff68f30f5c9ffc6b83ee945cc1143c06a92f25fa703d8bf3c4cc6f2b9dc9823c90e697bbcc4b903a44f5adcc607b81174e9edaa3485db3464aad1c6dd9787d81ef582f45ed3b6b2fcda689622d8de6888cd17e6d93687cf88de1c88ca1e13e4823f7e956d609a5e727bb02013718ab73bac4bc1df6b7205d2e1a7bc5ca9f3284be01b7521a78e210e65f9acd03bb84f0134373a1fe2185852d4d49748526f42085d17f78864536b257597c5eebbacf9000e466f62588776611203b1accd34f59cddc37dac9413fec962a241c8c6567523cf688a272f943612c789ed8ac3ae9f4514ec2c4ead5623e401252b8cb9e405d248025f05bead46e2eeee12a662ed69e23bf2b4344e50d8715119f1d0f1efb07a0697555c98ddc55d48b7c0b6a258f5d34718ece08d1fb7bb52508fe74632774ff0ed899017f878372ca7abcc30fe5271f942d7380669cafee45b2d80ddbba40febf65e2c6a44ed048071af10ec24d384d269fc267aa81cc9e16d51fd4024c550601a91af9203529a37990b4aa634a1d60ce577405e540c78878d66d08c503cde17a6e17982bbba5e08e90aed08a0380fb9681320e291818a4a97e3c4135a3e147aab7db9129e7c71bce4b6010d171cfc6bd23f22eefaf1fd3b801173edbc0911673f73cd59d254ec0fd874bc3433976c5ea5482baa3027bae5107c6e6693c004210c0704596eb15058d37292b3d9b885420ae91401085124c028e231ce5327bb5012087ebab84adf11158f7e8e1a1b6430815f56ef5456aed8d0e217ac6feb7909d1fb746939382836285ce89186484b268e2e5b01da5bdbf67f07cef7cce7a97ea98093f7f53b3018eb1579fed213ebc03a3eb92a5ee9000fce136e0fa93b0c15be2254a0e33054767287127a24d03fb5d032b4496fc9534d0b2825ab847da516663b9a13a52bb29c01b5ea612e2424c21be8ff60955b421fdc0973368459a85b92f38fabe84f8bb2ec7a47000b36051307f5c17c892c3e482f06650a3b89cd72b345aa77d280fbe2797d6fc07343b79e97b98ae34b656a6317e1a1d1934438f679013913e009fe5eec7a8f15b575ba495ecf58a31e769737f17e2a4d88e71cb0421e3fbe61fb4ecacbdf76c342e8c7aa5e3339222a173a1fd5604b2202cfd070604680f21bbee7392b4cad3816c7aac61d037db4b8190ad1053c12a7339fa72dccab5c0df3936091713b9ad9362732117bb9de977b621fdd9835ec661edabab8f1f3fd985b48b09ab1ed4d42645b1fd0c8714b2950abc8587656d8b8cd7482e7e5f6aef59fbe01e8cae62e1b92fdeeb43e5c32ed112546e8563491bc328b624391948e799d65a6fe8fb225dc2e7e2a7ef45ea4dafe55f08e3d09f535149a1a0f9559f83a0eae96420dc1203343b1ab7c9d9bf8aeeeda22f8a224971a5b6fa171779d36b809ea42fce1d691758e4348113034c7f1deb9929de3ac869713c6109aec82c0ffd271441d06c7ad0f004f8bde96d52f22dc6051f6f7ca4d28d586db4e81b2932bd295a21cb4b717bcde59d4a44153fef6e7b5e26560990f3323d55969ba88c58b704edaa52016981bec304494ed7763b8fddab23643195e3665fd601469595b280e96ac9eb68e381c7ae52b836d2462a2d4f6997c126fb4030a9261c0bf6af6cd16b8f9ff2f21e50d365be14ab948580dea07e3470dd7e015e2f9ebcdec7334da3faac1a5a8cd085d0559b25ce76f52c31473ab2151571b621cac42c441e07473204f3050e6e857ee95441958638321e61ae0aa468b13fcaa33b2f0ee33bf05d90f3306e018335690e32a8799d5b4c854017d86cbadbfbe41a3ea69bc1fb05fd4806b19697532d9d68da0e63172b7ce95c4bf5fed865a5c0ea17c064c61c1a12a830d1ec2e75286872ba2628bbb8d16b57f85185c1a307cad2e20a73b4466724ee757b01f665e538b9ec788b7b40fec9316f6397bf29cadf88b079b80b8949fe59033538e4ff64d2c963467b07ef304522f43f0bd13584ae68afba23557725256731afc64b27631ac460396786b2a105b47e6e05ce0c64d86f0e84aa96751441f18b0a6d2c904c6c88782d758eb9406d53556c92ef42e2ec953a059ec3102755d80d428d9dce6f203cafcde921f01cfdd152682c3f9b3b1424c6ade902f3db57eed96955b7a395059a40410e3a32396a8c11d8ca22aa9cda24d0a1e3b35fd2cf0b8f3a8e5ff23e80dd60df39015b77a20e6ce0b9ad881d3f6cf975c3d05dbf029506636e924a8677e82f06cab34a96741cc587b440515a68a01d0df0557167737224fb5df9208cbd3e770985cb6fedfafe77607cc2a6886c9af6b783ad59ca20ede9bb8c6186113c3e771b1064e51a6e4ac68516fc216d57803be9080735a2238b89edd5427e134da0e461fbd0591320c3e364fa3690db7add5cbd8d8af5c3c309771fde56984913d17e8bc381745922c736fc15c4d29ac1cb8d39b6b62f95855db756bc6a9458d5180a12a151c6b61358da12aa3ea0add176b988860423bf30696bbf7e699640f0849ca164aa6a6878d10bebc0c90dd907e9fbf880d1f6fe6fa8291f708641e131fb488290ffb21580ef428e540a288ff18985144ccc27c5ce9c5b6b09d8b4a4c826ccde64e267efd792bda9a974d7f214c8d46e1f020096e08f8d72cb73dc0c87673e73d3ba1c935040bad751cfc4067099ea7ae75dac229eac93eb8a60ff7f21a1091aa78d5b263a0b9d42db9d4dea0d46469df0e2658f077a79e9d725f1cd783900162c60dc3b4f571747404eba101ade32957cd3a384406d030cd72dcad3fa6864891e03a60a44051ba44df9cb34a54611363cbc29fec42b283b511dbfd36da49bad11f8667de91fff9649cf88960dae8fc38f56d8670c36569cab1d936ff8aecbbe74e956d749d9932b6627a59cd25c8ddae170ed27c5393e4959f23dd8762070e9b75863537767dfbdc8dee6574824f097d6afd79a2495ee1cd4c64ba0003c72bb636b585510bbea85dccbb88206ad5434d10bc6dd7329bbed0a1a7d0203d1fced722b356859507aa42e3533b74d8153cd4d6f3cc388c58e7a832d9735823a526b04e614be919653ca3b735f821c99a9748afd0a658b559d16c418cf11ffbbbda5f918a6e29181d7b66cb940ce22e1c25caf7f5d9c4f92303d0c0cfc8bc8772e1ac402ecbf896d78e795a9056e64e93a985f83e6c7a6634f46e38a777a6e3451531246114f31bea107d9a0681cb7c9b579df2c959e4395ef82f1e696b8813345b795a535b1905050f7b7570788165f51aea0e9e4d2cd99769646f46236b02e28de378e96c9e93306c2e6ca9b60c2d7332cf62f1897b8cf116790b2bd6632f7fda3e763869c0f4100c9d7f94599c6d443f7508484ab5b5a21ce81638361605cd2db62e1734522471f6cf7d4eb9f9c453704518e6b76b568f2607a54b6d46932af7f516beb03486f4ecb199781180e7dfc86b9edeeaca039ab354dd50f32f782a3ec2958d3d768eb4cf490b35abec0bbfb394672deec9a255bc12ba5c8ca113dbbc9b80bd9f633f90412fb8b4affe315f0f0a67b28ad23d563897f7bc2adff692e1b9414698b477d7aa80d3e8a4fe820ac9454d48d7db55924a1d87ab0a26d7ee17c7f1e36993b2ac5ae6c97f6b34442275f7178e9ad4ec7d82aef6505031af943725d05807d69c52cf3f9916a8cfdb22ac82483d58a9786277abf15093c1e95edce3a85e6ad0de89e471ecb9ae02b362007246562e49744914dbb580d3adf7ff7447e04437929fe81e84a2db479338d0df3bf31531138542d687769a53f7ee4f2faa09f0b80103edd03f79b5e0413c08a5ccd07536a7becf123f0dd653ce19bf0d8e42c7ebed0152079b6691a267454e6abdec6a3f2ba5460ae3b466ccbf55d4b15bef4715bc06b760635123b8ab3f005ff8a715062ec03921f58a3b3e45747d3b24a7e0c4adef73380c547077b2bb0caacde49669a8dd980372aed328f75cc67e7053dc9a606097f9df72d4a604bbd08a76ae0e22461cae7b348804ec6507d217f2a261d5608849ea421f613b75eb804b318c8699c6601bd4c756b02aaa7c4f12d04ec85f3da17200b395f576c2910b14597cac50bb975e9f102c3cebc72dc2555c9aa0260426c3d5111eec55c3adb26d88728a5f5029af3569f8fccce56ad4299e06c8266043c5f35d0505ae7cb841e4f233d4a5839ef701400fb84c2e11e6e77e46ac555c89dfec3e98430cea64b0f7bf5d012a53f9956db252f59f4df854c8cb4c526f41cbf1717c540a4101c969dfce1fe43660a95fc1674a243a41e92e8523f49f69a169c242d689049e7ced5e2c355a3f8ce3931981f5787f9f4337fdf41a65fa18d7439a26bfff5a012915794af02cf5b1bd72bd2da94d4a57e25e0ad744f3809b658c666e65995e476b78aece5b8c924819c1ef4737cfe0a395768203c69d789d2479e4b05f8a06eecfc979a72d8bf6dd66b83f88ab5a20dffeaa292030152327e7f79020c57303903b857be8ad0c842b822a962a2d6426484573cdb804d2781cd37b71cbb838c261488ecd0bd44e7907b6f41e5d7be1105e46b6944189654fb4285a933f9e7ceb79308216ab9ab7dbdf445e095ed0a727a03e7270c974cc91a710e8465eec0e4fd48ed14879241ed7e0da219c1ba8691cbf6664d6a29487582bd79c2278d2007331ada38e6ce5349fe9116386608732a62c665e96ab6244c0afafb30a522d12aa5723945eace3bfdee88bad18901c2f6d66b889bc1cb61202644db54cce408eda155c92c2e5a7332973c0b398ced405e405cbecd1fea6a04def31bc1b8088a5be331d5cf0d837db4047409dcabc1cbcba61189d719f806148f31e3da11f3a7954baf5d68cc4a0fdb511f2293356980bad37559ff070404e2724dc01c1771bffaf05ea0dd929f32c9555b927aa66271bce2430dfe754091dbb7af8c679259b07dbbeb1b69f98f6574fbcfa1fb65242d65df7ad3657dcfc6451f7446f873dabad3fe018902e62eee4d88fd3af484c41fd43f2b26429ba53be23239f0722345d2defc2dffee416e2dc968c2b1feff61fece4ca95b6abf732b7060461919f28b187dd4753212ea43fd8d4c074affa76d49d1c183a54d2182710d80411a201096f73a0928aa7ed8c94409016a4deec41413a1e764315c668be3f9a99cb2f94f00e2ebfe6eb2fce5866dfcebc97d9cefee5bb1412cb3fa9ac8bd159ac8e136c05c196cec863432fc44620d256ccf2d3ff2a109469c829e8750e05aa38ad15da475459ec13d550d886858632a124eb060a709118984b18b65de7ef997a203de7cff4ffcbc9c0d8bd8a5f9bbaaaee368bdd0cfd6068f390d21d43403869af13f4c2dbefeb8353554ce175499303b93280cb681c5d283bf0f49541b43fdf8d98524c9eadc6fdb469daafca1199c5accca4002a40daf2abd5d427ff0f9010a05c4058b302a8e6290a209abeb7d27009506c76dfc882042be7c30185c2e650179335480295cce6f6c25b31b5e7d47beec69f2da0393b709edd1b84e4916e1e09d8f9541317d7d2e3781f84aec8396abd9f214f9806641965ba8e96f1cdc61bab02e10d59c14707d1ebfa05791d9a6e9fd2b7b658597baaa06268cc96e326a7eb0f678d0e4448dc3c572fa387232d39dde07b35bd08bfc383d8d5a4889744c6a1f2dfe80e39ac5e4edc318fa19cd3aa48b25177003e8b112dee2fe0d8ce418ed5f7c9f76b036a85af9745e6ec2fb6a4591fcc77190f60c378b4351ae9d76a9fba0075f253e2c026ac79fe0465e0d172ee639dd1000540bf0bf4910498b31b7ca5bda387380f2f56ce3ab67cb4cfe2d55a4116826b4b5b234b3f4c5d955137e592214fd24d89b264769154458c3d92d2b9e4f07ec9183dfbc7dec626ed5909200670076aed0e485bd1cc0e12d64c9e18665bcb3a0c59e411e334b145e9ed85c41b549326fd308f65481fadc6973d60440bb4b5d0d1515c873eda4ccc5a411d95781dd20ff1fc3cbb2404323eda44ea44e19dad904818d4f8a3a024f788fe8f038970c5935a8be02a4687a33e969240fdb127e57dd820c93ee7d805df7ada475ca13944878eb22796924087ac33342a12e197f993614a7d901ed10ed104b94650968513037c06807018176fb84b850fd5df6f745bbbca1f4b2aa37c1c2463610b35b167c3b42ade6407b9bb7df3c6c8bdeb79158b2ca2cca54f7ecfc7d926f2b1887f92b3c3d180842515e8bb7aff85f57395adefa86c169d8e8667d390b0513d5c6f2ad9981315f193036e82397b8bbcdfad234c9d75bec21c477f4f76b0eaad8e3c38de0e6719e18c4212bf0051522b16f6b3175abc21474332c2bf210cb02c3eb2fe21f39d569f5ef39bdce89d2bcd4182dd36166c6dca0687a08472c4404bc5f36e0b9ed0120287ac2f84a34cdabfc4e6f5afb88eb757963f2d546cee91dc4b4008bdcd9333cccf9566768f36caab089153e99adf2cfe2f4902fbb946c8f7c7f3bbe91681f1eb3baf3f6e9100474a5009b7f4add909b0d074c0b8e69c2a62712d86d2eba8110c028f57601fb6695ebe082436b817b7c5c60e8147d1999865d76d93a6d1334ccf0ba27c8f266924c291d87f9915db4310b5aa6b4c903ecfe9fa1dad2f0e06d08f8b993f53860935d878f5fd0a521e9b0fd30242cf63359a7ce3e76efd61bd2d2fc7b8416ec8d592b20589cd9ca7a85b059438bbd9283de53f1a5e5b2048dbd00bb2a057bb58d9510095cfd7f3e5e7a7870defce5a86acb6a8476889ca643642c4c3dc8bdc7cee6a1805dba400783ecd93cbd1b1cba7a51a576399f20af7edcd7cd0c3217ddfecbe555964001df9dc0dec26b24c7fe6c3f81c8d135415aa7dfae00ad21a5b1a09fa25df204c96bf01a6db2c9f6feaef78e9a5c938667090b2e47e66df704062412d272df4495c29a9bc098313dae2fbfd6160c44349d31f4922f6c42ae072f703c52d25f0158329c9734fbfaa28dface0be232d909d94f391866ff136f76cd3948852e807ea7e054e7c8d0f2e216cd23ecfd077f874ea174a6f5fead5adf161bc5eec43904693026c29f34dd5d2117d1f188204e753f635b6d0df1a39c4a88fc4b000f98c7305b616d200c49c37a3818674c03d6fa6202bb58ede6ed4a279e6a67fa44f142842b4c3b00a26b4d4a1012e90c0e9442b60200cf6b14c14c1a063030efd92bcc215369bbd6c03651799b608aaa6491a6ca2d11c802df2e762d323a16b8a174f46832cca3ebf576a56874aabbf08a0e8022397c6f4dec636053cd06cdcfbcee0d5c50a67acfe5ff67867709f411853ab059d05f2d2487726846d708251e6b37d26865bec23271e20b0d5614ed2bf2c158c36dd034f185eadddb1d7ac379209199a28738ecae6d9cabc6d5dc3f0a49463f89720cff043f376c36d427dd5b0d9f9f372790d4182361c9ed304b6a9dbe73b1c729ac2dd38e40f0c9d0eda7a5a8659c8f9e4e0767a03c6df42b5d431b154a1729f2b421a46dd9dff89fb237b8ad9d530ac1004c83041e6ee7431c5a5ace665142d1a5ffa4abcad8be4e4cc5e2bf6946e256757aa1dd4243b3ea169c51de54844f87d3e5edb8ad542d98267162190fdbe596a728e16a9345d853e68bd505786640df76c835383fe0cc5c2da3392d0a8c8a183c6725d6ee5169eb3dddc21ef6b065a37e5ccefb3c58ea57ef67e2fa69fc5f4921286c179414a27e55b238571a107fe1aaff1f230a09ea2a49746d3d3c04a1f9413c99ec9b99b11de8ac18f791e9b0a8f8ca4dc24061770d6553b1ac1c0d0715fb1c162c9fe697e9a89f13510b1e3199b77dd197030bc2308d0cf06196e8b10dbd0421c22c2408a2f6f287ca4a1785eb30ebc03e7d9dfc8064f0c284f5b9825ee9498d146a7e9bbb418934088855a41a2bd2b8d8b858277561add8e600dbafd38a5dcdc12cc0e68f242c399decd0252f825c505e478e33229dc6470332a74e7049e9b8ec0f1ed5dfaaafb0ab00655c769209fed3e4254163348be081359a05d4ae73e9083bddb726b6640f8a8053da3d9ce70cb984d17261ab5e3d2f821b2055e6dfdc7352fd6858b6783d68562ad59884770dd6395faf056a392e726b04be8cc3c60fc56e0e128d4152d50c0ab56ae7ac3d83207e758a4835d7a52a52868a0868bb3e80aeb705cc7583a1cc1e6469118630abe6aa548191cdc4af96d6ed31173eebfacf4b50726ba465241989a6da99d7aacde5d4c94ea5de86d22ace714d6e64685ce07b3cd52b5a08734ec535dbc2834659f61f7b287a7ba30347879cfc8abae34ffe047fe35a47912b131f4985c907a09bc3898800f79469b8de3fc149eb1a0b9f82d77e7b0b20100715828e6006507f6e2db51552b5925a55c275556ef3d5d254167f729fc37448feaa000a395bdec2bbee79b2985fb3b046d9d6d9d43f8eeddd04b42e8839a7369769eca46f6eeafc319261ae73c758bf67591fc86a801a2ac5f381e0e45df56ddea27469b26e41ca729977ad7b7bda7b326856863d02e5466c949803006a7bba3ea398eb5d0b4193d16e14219193e012a140c4c4726719f2ab0640f01a3f54335b179bfa4c5bc144e5d74f9ecefddfdee2c57a20b824c756613fbe0cdcb8a86d6dd5b4b5c6264504cbd9e1b5573d98017a834149fee719447c8b8c4eadd79dadf056166d1b1232dba06de3301771aae55c0afd599869beaadae3218e38a33c402836e1463b3a3a5aab74c51ce75a025108a260956e76330348140fb54178c1b22b116c86aafcc527306746fdcfbacc81be9bbcee96d2e25e39595b7a5952bf9161d98d4b2da35d814b8280ab15007e10638c0d1ccb507dd353ea14f602a37522043266ff2be64669c8550595a56ea355b1da67621cdc0206bb62f1aeebdd3031e4c9a41c4ef40390437007ff314f3cda5445a405c71f36c114374412e22ace41f361e99217f3dc685acd07b026adda3149f447e0521cb73978a3d73c4aba2d54ec20f32892d98a12536eac7e7784ce4e310a9bb19ba2ceb4db7e0db5141b60eae3a51a0fbf07631f863e635ef76c2e15a9d0c3533ca531821a3b94b21a197b729a669ddb7603524fe64bd27a5caade65920cd019fd95d5e74d377df15f9558f850ce7ae9a0fdc82f2743e90303da21852132710b7312f430068d76c3c0dc5aaa8fe42a2b93af50c699e536ac56eb7bc305d32b9080782324d03c0c964b90456e137a02db84c74f0cf76b5ae44f2737efe3cc64bd267be7c4d72ce673e0d221256e7682d7cf26b3b4282965200362570d1517817d0354115e5893a2f024dc380b1d76bc010ca137349e027c1ae99f1233e11116af88da39a6b5c762d4bbd710c04d5af18f5152890e63d18a24f319f65895d2292a48a595eea096e2831b29d39008e04c75b7079609f7303bfa524d846af9ea82ed7319767db73a1b5e0b4e50cea95ce815086a2854716a9d424b2de6466c4e688bae5c13fb3a9956444ead0cad28d5ab226cca7149ee50d398cc1cc36e6e1ace2a09fca06376636123a832973b4cbff9d702665e704f6040ad75dee347a36d7dfb7fa44d9325d440403ea433fc33af482c5c3a756d0a5a00b57eb2a9326bd6ea99e15aac7db40e0e749d684fee43bee9b3a6fedec455f5ddaa67cba306bfb1799e4d39d60e6b7731695e0e777b93b817b657ab10044cd89818026b9e5b47ac7ac1728f4eff30b4609df84db2d521671a5e99cad48da6313b7239ec3ea8458bf02aefb8e4909ec47e701bc5e685555e5282b86eaad32c5c503de97842342cfdd5de295d1ff6d3304119ee95212271f63cbc91136f09752d29134ecac9915d4d69b89e1991650a594b188666e3ead27b990d4326704f26ff8841025fac615dbb8172d366440ab5754666271470844dd7283e6750a4de43dc634fd050709b1a1840d58b94462e64558333845cdc4ca201811175e0dacd7c838dc15b8844e3e779fdbc21e2184480f7c4f2665a5a4b449b3e6f5939848ced3bac6fc3a766b10463ef39e9b42f939230445c0230fb09d396dd0580925ad17c0f1c614ab1acbe3864a8855aa703d22447154eaa42da21094c9a4653e3fb7f7ccad57356ff1f33a42bdf148a0fcafc8c2ae80aa33bb668a616bf4c13e2e2aa0b153a0c0a56a8c1204150bf2f73c25be484c5ff5aa78917dd70b4a4832bd8f130078aa355aa0127becf8072a232ed199faab11d0b85ecf802c1d2d59aeb652b9dd9c25c19d2b3241872f40087c2b58602bc56684d27a9a49013905f2fa64e8e64e0718c7afb9b85375cd7fac5f1c5004c880dd66a129dbbf512cca34211d080c21938240323197dd743c611d0b412c10740ab642a2499451d7955f1b66598094de941450bb7c49a09807518b7fd883e94f7e65e70e8d03bb6ff3b17fb18323802d037f0cac0ebac0d2cf80bb77aace764aab56174abfba603141ddb7da2b21f5fb9169acbe1b3c0433a59f76e7bf466857ca9848aa4a9d9fb26ed2dcabd826042ec62c2265c31aa7b1f2ada7051d5acbdac7c72177298fc3edd8034bba491673947b257d72034f38d1e37f2104ce27e0ce45368da049b39888e0c9e8f59b7164151f9f302c1ca02506f81f62416721fe0a3ba40f44334f7c910d6739d0b51cd1c160ddc8f30e53a3308dd98939ce392f9ddb8df02206fcc61295435e2611712850a5e5ad66280c67714faad0039e51870b63d43411467acc46572ac25108bf6c723d2115c2241dbae818888f9963c5cec7387c0b832bebe158fed933c744d8fc02618cc5a7ca8c19023a4cdfdf73076c51083bf83a4f3e978d47c853fefc51f280375fd37670fed8bc82266e1af8ffe4d1ca2a2c8a19f4dad717c9917d544e16d26f448a04842ac153a901cbcf7704f55d212af35a905044240824b85d80342e9b6d99bc40fd76578759258b384481ad841e8a165da19bae4e9988a9b13c977b6a309412d05f2fde5631909e315fbbd8e9b9f1aa6772f4f466274b981da0daf1845aaee11d82d755a4214334cdacbfa59d4592b72afe09d9d70acd83f4bb72e934b2fad225e0686c702662348597e2d4049577fccf39ec68c0780f96989f36f2890f3dfab988d04ac2ae3e94b9dc5435a53027a2464f54b3f0931150c93c1eccefd22bfa108c0912e864d296ea7e20ef687a5a0b6beda81537a725e1d00f06ae7e9665193a0301bfcb6ea86fcdcc00ccf630a0a340196d5c774fc7b8e9af2b56e166d3d25de61cd5d4b1e61f216bcdbfbafa920ac216a34af4c1bdf4916c3afc60432ec83b059d80b3e761c8fb8f290aa4ea1fb56907244ae66ec24b692ac309b432d20f679789732ac09cc8dc67384c4826f6840cb778c927896797c37883766f6c81908db4c624750098a17f041e80ec8440115bada1c4c3a1538955d8c8cb0b2ab0dad60cdbfc032de4705260fc628bd03a4a48523a2f9922ebc6b0d3cf782b941240d482b6383aa04c722d702e0515024affe9aeefe5d45454cfb181e0d8d31c7ad0825a32418fcb8c72618ffec8b3f3d91d828b4f5694c4482bcabfe35ca2ff783664ddee5de503de698650afa576e494f76e988e91f1a06b094137dd48bc294a454f7461db799384fa0a6bd410526f33d0d45a22ab47bcc0b8add1226ca5bc0af6799529323826db0fbc525456407a374f8ba068810eae8fbb59567a570370bb2b2c47d134ea71e368c23b46e8f0272c727851eeffe18945b965baf85c47aa467440c5ef002bead627ffc431b70fe1e7ff22fe44b787afb03fbdc5da6627fb727b6f2157b5aa8f1d9808e8b3d459ed4238e9a80dc35ddc6a1494854cefc459281688dcf51c2ab33957c4ed03a08d836ef157acaa3cf3088c04927240cfb9d617de4bc2a62cb86e02d5daadd96e078230e25fbe9b37727727c54cba63187127f590f903c55fc31f7a1e099eba5f3f0844d693b8e7bae64c854c9ef260060c954ec345850156a17424011aa5d4ed281cbbca439fa583d9881c9be6b768aca362136f2827573e10ea4821e885a36ba0f3437f40b8e3f6011081215c4d5df44ba2a652d0dfb91db2eb22355ffd611aa613e368262fd80b6e195ad6ad979ba7597360a6e028084e8fea6991cc4996b1d9298c000b180d230c6428cedef96cd9a8687218e4b5f3b5fb9cbb1fbe8668f454c3916c02b48cf96fa9138c7c2801d1b64477f94898e5607685e197e959caed8c7c4457a9ce9108caaa21258291d53a51a3b3d3142e16ad52e3cfdde88adc2fe878bd15be5c41f8abebc2d513760d189f461ac523fd655270001a21bffb13d291c72d373164fc5b1612450dca3bc4d5ea88b963b867c5b3e6439a50d40478212a16bc6162afcf7d30e467576b9ed8e879eee4ba92547c96b167e5aecf46c44b102bfe74cc1c9e5984069225eefd53849100ff0eed01ff56620d7473d3e193429d605d862722d7680c42f94411a7bb2bd6955f215ea051f49b3aa71ce20f631fb315eafafcf7fde677d837a9b0a1ce2a10a79fffece62bd3761f3a36924a38686b4670aa02d66478c6c3bfa500d82fbb991143fb253a529e2716554db09c259a2c89904aa2cb7b9fa47d58f5e9ce4386da6afeb738aff28e5e94d04b82164cce2cb2209ad0f2145488a2927cf45069bb2f3591d10d50f1a7abfb7e19216685e7d8b9c4465c000f156690ecd8402dc3f04a446f28680d01a1461628fb21fb5e4e9178c8b1e5565a141c91e37a9d92129c231cd4ea0f48f64b638887105c02db7f3b152e831ab75752554b2f14ea0ec2c3dbf028047fb8209eff18b85cc4beeb040e214272a79d06abe2545002132d515f339966aae4a6efd87753476730d1aeaeca85d35cbc4ac4d41906430c837e009e2a0874d1eb7828bc5493b5921b82b7b3ef0d527a52899c1f80c8b58043d0487c9e0ebe20dd061c055aed59337027d36dfa97fd3cc244b4205289e91f03cdf53704f20c50aba4bc3a95f45a72217266791eba5e5e193f7da4c244303ba39e2a37f692edaf3041952a8539c2f89a08e2d70084c0489cd741ca066905956925e19d4b58e6186bec93ef5324218d29c83703c0034e6460d9259e2d6122fd1c8cc60863c02c9d523db308dfa5bd4f78236b9f23f9411604412490dd797c32ca0fa9b9f9f9f140e0946a3c8fcf44276266a745c2bf3133f15f7f49e16dedf497f3ae714233dccb529484ff9d1ec4f44aeb9e7636760c4923f2ba8782194799deeb12ab513043c35f872a496dbdb72233eedc02e718f30e0601861c089c2f10f495129e44705f75207efb6c608e602369856c9f0360d00a662e1792e1919845308c3e635cf1bdc3bede1e31feef5ed3a06c7f21984de76636610bb5154cb24a0dba6359785b13bce7e980f250f02c740025a1a1d05ec2bfff6998cd495e35978330430940ba610b0f0ce1db555691b1979878435374f681921e83d91be2c1ec98d2a930a2914887d1c33032f1863e87e310ca9458b4dcbf4f6b181adcc21fdc43fd6c010bb3c5d39a0775aacd8e6bb6a5d8c723725ac92b3005bace5b6183f3967db4b6d150af76d4886fb1b6dc5d5e7c83c99be81cbf848c0a2ce2987c00d082bfadec8edf3c342dbe1216d092a0343f23c63e0823246447cd3693f53a2aea0f6efbd01271aa2013178591dda14804eaa84c44a32576ee6cb5d0017406ea8181918b551d130de587a82f944e35b309d7d119472a7571b347ebc673e2165d50b792bf6894cd7b21a95522dc9db404ed1b151926764d40624e9593ff513ab8298074e697ec00b72ee8b1036e90afc18e2913171555223960c6f253b1b587414773e596350aba3b7bf0f49d432ebe56471a6ac2b72320052318e4ff43a88f052c1dafadb49941cfa736a0813b8b88ead84d05d620376399dba728fb2381103d96394d42a7757cc63d48645067983c8cd8bfaeb92e1aa13fce8d3e24e95e41a38c5bc80158b8be53ad5dfe81505d13b046406eba817212dd76bc855e908f17d5c10c6afa7835869ceef96ef2bf15a495fd91013ec1f8b8a4a1ac80b4a06da60d2df882c9081081d966ac85e5a8089f8d5f4d034e01d4860a7c9052a94e1d3255ba4696cde346f32b642323136e30f77381234a3f953b4279c52997cb6eb4e06029f7c9e707178ddf3db0fbb1a6345a96275dd1b5583554fec14ebc08549008d9e32153f4bf77da371135748a06da8898d95d00fa8489ec34740dd2e40e1daa235a3555a85941798203162bacbd836d57a951addc89629e00d9cc39073116aae33c3ad9e4ba361caa8b536fd33227166e558d35883612efba88820706bda88bdcadb96d0886786eb4ba86384dce17abd6ed3f0feedf7d235257cdb7a494cc1c021a42fdaad2c04166611474fdf14add9580223f6a62c91259f654bf1fed63c75cca284cf8531bafa5ce4d4bf093fb4db56e33c414653e587cdacd145caf07504fc32931610a092635cbe013cf3536f2168fd088156d7410a7fc6f7d608b434f7c0486019ed48d96b9a6d48eae20d0fce6e8c4d2b6b04d83ae8a710e285260f0d7e08d6f21f07450b490e923ce98fa2b43d2e844002d14c4f907ff50747c21a3cd99f946f8c53f3e5fc84914a1cea259560c745d768a8dcbcc600bfccb9416f8109339cd92ea9ce60d8452666f279c5116722d295b026e3a4a390697791b708bc4a95f53863e8474fa5d5a4e0347eac4c9a78f227b8a1157b3f4c18a664ada8df8268c0920d7d5695d76c2506957d6d2ae32e2282abb8dd19d08092cff3f4e1a44f78d9bddbefa165444c5c485f2bda5eb3c66f7bd71997851e312c0c57ea7e6e525f8b88b36e42379bc53c03e82523f617495e44717c8067c5b4f5e2166026c64dc4039267efa5ec0058c6ec155889802e4821095542a3fcf1e61c2f39a8e15c6c7e7c2ad25346d72592c4a305316a50d4740f73ee802e240f02f73b3373f2c91d19f8b5f178c3f556b9d9f30a18506844e10c2a19632d1839f1b39a112ecefc479048032e1722720243915de62f7cbdc573d8064465f34b6c920bf5f5c255069a1f85a59303f791871f763df038b02a11b205a4ed0224330443f5ad9558460c817467906ca287798c2490823dc61ea33a10cb884aace8133e20ea93c07c390238cf20492417e2885d3851bfc82085c1afb8def8951af9612463da228944d87ecc85cf8f7516b766f4c8c3c92c6143950cc6e1fd4c0ea91789eb635806c78970d2e03447730b0c4004e570259d1a8012d287ed1d2eec1a10bbc740e6859d77071512a8b80b63ae4330dd0ae0e29f62bb730cd04fa8fbdc4b6d62007ef0ff2bfef22112ed17ea4909f9a1d2e7494bba800e97b113910067339b15c28d731361e02dba0853d132fceaa547b6939e387988790d628b4624dd366f9720996372b13cf050c5dcd020799ab543bb2638c59998a3e979f2623600528178e66a5d13fc712942fa0301edac7d0f375e1790a8c7cb43734cd742972862ccd44167fe5714a5baf8d5d5e6f8a8f8561032c0d047acfce8e5b30161aa703d5fa793ba811768a60977e2f327768e651a82eda723f2d5e825c80d12811a7398b270484bb901f74a73f1cdd12f39d5f9d09c2d1ee2140077097f26f1d07315f8cd29ebdc4d04031c67ed242dade7b6fb9b79429c9141009f508b408628aeec8de35aa65a0537ea1457e5a6d2ed3b428adafecfc0177378dd6597bcddab3a73273cef973ce6e98ec474abb524ab3a735d09f7649cdb2945d626b0c13db62c62a194619778641bceba136845aad0ced9f5aeede4dbfd2acbb555575e91d6728ada0531a49ab02fe72a048a5aa6739a0db3e846da3bf7d4741ff8c9d0beb16f25fbefe73ce9fef383ebd8f8a9c5a447c12a18b502d495cceb62da6262a4c657394737a388bd8e9a7dd1add7826a34e4b72ec2ebddca277f9933e7fd277d0bb9d7bee4d2567d9f7cdc23102a11da20a0d0a4ee974a2ac6ad2270ab4ac67e61d86500d22cb7490696034f148bae9eec4ddb5253891a2a37432414fc08453ca3ba0bc849d590b77769967fa8cba74accefccddfcc728b6e3ebf672faded6cb3f3958e99999999999959c8a96bfbe11f698b79d250137266a6469dff8fbf743158f4771c98258f7c5ede3dde3cdeae6e09358bdffbc705d5d56250139262549ddbdf1507f6b9d2e32a4226636eb7bc46abe2b2d23cda95eed1b05c2d4bfbd056ff70a8b5186119ec23ccea9fd425c30af596242ebb6558e1e7392790ccbe724d7f16faca57dd846e526f779792f60cb9bbbb89690beb355ccd922f3aeda747bc076669aeee2a5da3f1b88b750cfdc0a206fbc9e283a5b51e0da8b3013d729be8e321fae143725dcdfa8e302c8b4f0f49f60864ccd578bcc6b3e25dd17a3c2cdacbcba2f97c3adacfb7a3c1b4197a441c4fbb74a63c590f1c882520832f74c0831f04210539e08ca93b5de07241122cb828820267a4e1319d90c1ce0e86f839f204132845c07c80438023472a78051185161138dc4f8bcd3db93d52701bdac28ecf3c5363c78e768875d4706192e0faf7bd699781090adc775decaeae11077487ecf6ec0d8ef54a2f943ea15ce221a122245d48e44b36914fe40e37398e0d60a7275b99b7a9b6cdfbea356c050ff716665eee73c0c6172ed74251ee88d22101fd70c50f4077bc71391a6ef3bac5cd185cae23dd70bb7b9981b7135656da545ae4845ea2583ab690275bd563defe1df3f6e7bd090ab6ebbaaeeb703462c30ebe320c9ee13e067a3be45e7bf9edd02ac8571eb10e0985b99fb4c8fdb458fa9be636af96be7e39b49b5e053b0ad814eca6d7dc774ce872dbd31d5be97b4a8b5c4769b1815ae45ec60eecc73d4db32ceae8729ffa179ba5ead7e59efb1b1ce48395cb7dd781aaeef98efdb3836ff7fdd3ac2e1c2bec49b33a2813757f6abf9ad5df4b9ac5413e085d6e7b4ec25ae47e933f2d72dff25abb258f78867be9b3848272498bdc43b9dccb3058c7f6dccb274817ea73ef8143664b30240eaee5173cc3bd0b11dc73f3b96f9d66d1e7be5d9d44bac097eb1dae7bbaffbedaa277f3f0e7cf853534d1298b5b4ae504a263999719d8f9fdfe047d8981752fdd2ce49815625cfe6428dd28537af8f999628716103d01440b7c524044134443b4a0150bf2e1f585e52e47393ac21d19e8054743c8f1a1a776b5a9305270a792ea02495f524ae953bed265da7b971ba66b6e2acac2c8dcd1cec04437219d99cba6938da6a8e294765ebe95392b413d94e0ba572158518bebfedc97e3b59ffb4777770d6e078931c5edd3edeeee1b8dbafe3cc2c8dc0f43e8fa5b1e7eae7f6a8cebff2e1e5ce7e1cb71916ff08409095ec00316604431b6a0882ac2400284114970e693eb4da6cd1dbd1d7ae8c10eb207137c3953ae7870fb2b110984b05ebdc58377fd79346b3e8bef9c3574b0dcaed5a7a7dce512aeff70fd616816778bae7b14d63de535e284c2be70e5bf206f74cdc4d1f9818ddbd76b2a4ee988cf04764a0e0556be11d92a9283156b78b0d0d8c2652c82808067422057fd2d8f60e1c3d06d99435784677a8642686871f3c27677bf6cd0f96702ebf737776739e5cc323ae7cc40b16760cd9fe1901cf933cc49e4a3c8339e011a5a747f2e07ebffe37dbab879d1caa8e858865439005cb385417a3512e0faa8e3ba092cbdcc8395fdd9202f0778c6c4851d39c6c36be598bdaaece93fcc070dbef2972162bf712a65328df50239948ebda6bd4127392a192ea536c83186aebc418ec05be6d07a65cff4c423c4c894c672bba71c533c7a92c0405e1d537787a4494d196cebd21f6f5c1aaae80bac8386b6c5a6b675b3b033cd708531ac6d5dbef3b9757b421b5f2281a718938b0c318c1e8b6ae111eb8f00b830cfaf02bbb0559138dadb6a7cf7c5c4c46c3131a8a1a198a1989850ac5265800980bd6df961f2ea49b97e0004d4d050ccd016862512789a12031262a21303c284afa500f320aa052616f3364050075c418597d1383f6d0438aad95941051952e96d84e0508c8b0c0dd5c2f9c9b4d5ecd4ecd8a1144c2e333f33309881cd3861c68885160b3ab0e02240cf90cc360383999f19d88c13668c58d08185d60c6c0636039b81cdc066602cb808d043809e1b9b128b4d89995c5043a613482abd8dd005168bc14c2eda731f7b1dbc101804e8027b4023542f3452333cf0a5c2933a24b3c562327410b32d4241b88ce85109bd08d1104a091db1de0b812f159ea8f0e4f6c7a6c46253622617d490663a81a4d2db085d60b118ac8607033aa15f4126ffa737bd963069d2028df39218f384244ee9558bb1588c8b8c179325afdb6f512d2d68a2715e3a99629e909e5c934b6c4acce4129b124be12bc844e63590490ca8e3c4052685171aa9191e0f8d2c12873624b3c562326ccb0b8dd40c2f95424c880a8d2cd2051d3f00c2edd76864b9a8a1d0c69748e0c9e43254148b0db9c45490490a5a0a0f233314b3289914b616195b68e34b24f014e3128bb9c830c5688f6a29caa2ca15af228ef48043b5435f23c6a21ee65df0fa8fe97846c710b020c26dd97efbaa9940263560aae6a7911bdaaa481748576495db0f13e322a3a25a343f99483c578663056a310acff493ae48177404e18074c50e0dfd36b4c5c4dc1a60e96d68351e268614530306b4117e205faf06d8c9a4f06da138d462cc0a5e0cc838352ac824e6b918981a00f0622ac8a4c66b356ad488198aa951e3cb11ab24413414f364a84a124443304f866e7831335bcc06ce98adc662b1a18cb6b49c4e6215d3d09058a53454b58c9e6240c68179690bbc3a366d61f97bb2508b5d98b6306df12717184d26cc2b12c7f482c5a63d306b1ab5d85d6303a86b60605d13e3d5787eba8459b646a7f4fc1409b3502dcf4f93a04a5026af309e0b9e24d0d6a4826be691affa9de0557feede937f6067ad5c7453b2383b8c6e4a66b96471b6ecbe32943c33678966d35b5292efd07dd7710378e45ebe46ba69120706f12e73931ad0cf610dfd3c7a2f7f76231d6948a4ef7c0f0ce2813564cf779bde73b3bc5abff74059fa7be31b58830d466ee9f96ae058c320446ebffcd207f1db35f47377197d19ad04553248ba437bae9b610d63378029a5e158fbb7d7b857f2d24f86beb25a90be46c1b17f6b00bffde6dbd00bbb905bbce9094ab0895c0e12d6c0cfddf466f661102f88776be06f4077553394e15377e4ff5727838edba4848672840e95227ce4cb40ba2c05099d2b8fc072c77ac495dbda6529471c815b74495172e5b2142555ee682f2ba172598a9228f7b214254f5cd365294a7c302045c99194d7912bb7cb525eae2b3fac4027f38227d8feaee555e9251ed452c17c4d91f305452704cd9cc8e80b484ed0b22657ea6bc94bc936abbc90d8978e922c9d922bd51be3c957aae251516194ec80c6e47f30500253d2c42a4932515f9894ec28e1140522b454819a324e577e0b18339eccf032234b83e37999aad7d18481421260902042820a0c892840f2af7c30a842352c908d268f44122490c8243c48ec58703eb13d613d220b8fcc1157aa0a4754a92b744158c1db621e15a71800bc2d26df83e2d9cb414734a9471c8144feab280a2997a29a64a89b222b92004071d87cf20258722f072519228cec7250129eebc50d2f494b3bfa22ae8a9d12e5f68b0cc37290114fee7839c88826b70a613f24b0e0228483c6635066b1fd2f520ccb414624e92e42c2f4a03b31b1de092c4b7915ddcb525e4497a5bc846e94cb525e4f2e7f7659cacbc9e52e4b792971e57b1aa8fd29317be8d6388b0a030f05481b978388b801114cdc4c8916d05ddd4d9f86b0dc9f7efbef4bdfe263859460aca305c93a4c5f356e0756aba18d0d0dcdb7fc98ba327eace14863a05f4f9f81dc53ff725e9332d8562b2492f5288d31c61897a33981628d0c6ccded9185d86824423f4e874abe54c9e74b44254321b7c80e309fe29eb063dff936448df33fe91169b185b850880e2e6de07e475f8e7bce0755cbe81438fc304122d9dd4022d9cdfe9b377bad75656be848c8285644045463f2459a9b3b7fb4b9a175e77746eef6f3dbfc6e2aaf89a8bc97d70b85c8fbb2848da40aec776527c46b980745248f4ca283d16db9e4b624d271ddd1d3f9789c80e54e1d8eee2891f40b3569701b2524a49359688a160f57869f10920ba705641c1e53bbe8d0f8d478be0d2db22eece97210113d77a4218227141ae20846f8dc7927efe04a09c4955f5d7ad7610e7047992aee7823c70bd7fd8e4cc5ca15df01c21d251299e4fa8f72491043d7adb8fe95a789eb6f533c4f5cffdfb91e7444caf5a74972fd6d8410428cebefe387eb0f648626ae3f0d2690e2fa0be1e1fa1781b99040e1e1b9feb2a7591c74022dae070d11c575e9a3834abac44a61c40909af2cb21051459423b8b8c98288264b841084129ee0cc400c2392bbbb33c00a1550a1850a84f0410d70469ec255523a50f283128e644184209c27a08002d3f5e7513371bc364e73b59ef1dc6a834353fb2b2532fd0318c714b2af4ccfc41452a3facdc357d97735a5cafe061c8ff92a0b7de838cc57f5bb5904272d1cc67122df3e105fd537e2c488931ffda537e2c48893fae3abff7d29fc422336beaa1d1af1ea936c60e2334efdea17aab6908616eb0bf155fdf1e507fbd561bcaa5f53190aae1446dcd161b2e5abfa1e58df1e61553fbceff7be7f74586c555f0581ef997ca17cf9aafeadef53a40bb3a759f2d6779803557a82fa3235ac21e3995d59d0b18c4909566acf3a1ed03832567dd0bf01a7ab200555fddbdf8063a4439503e8d7ff41bf1a71d2e18ffa5b0ddf57fe46d85734f41c1f295f39cff86b21fb2ac538e0042dcf50b1f2895c8a6e5649e34e959c3fc3f6dcffd8b82e34c2852aeedec816aaf819677bee6fc0696f03c2abf95ad6137c271a6d6c5887045ae09a09c8d5fc69a304295c843b7f641b00e199f939b88d868780784dea67862045a44e0986e40e0fb11167912de9923c343964bf04d268d0b14cb3ea1dbdcb76484e3ae4ab498dacdc99c49dcfcd56b3b49f4fb1d0a3faf3b9a309d4f154c159ee14e2c2b8b36bdde9bab3ffeb37f2bdf783f4de7ba111ef4943cdaa32bd4fca9d34a33dd42ca279e54e233a44ab601615028bf327d09d34f33e2118d1a1e9a437281dda2b51574e4f52afc1962b334f36a8ca34d0bbf274a58a525483dc95f41bd4be63affb9844a25878663efd826fcc6ff0bb92824777fe175223efd96bbe2f3d0448ef8522f09ef4a550044e7e784f0a552d02d27fa1aa1967aba0f5d57cf9e135a911afe6cf2a292551a316e71256debede7f3f8a57641d948b15d7d02ee46afecc7ab833cbe1ce1f5bb8933ee1ce1f0770a7914c8bd36b295bf60b0f3dca74329e2c49ebce315b72e79c73663e773e6d295fd267ceef5e968beee752eddb5a243073f98a7e0b3cb782342ef54824609612dc9e332dd39e6659cb41ee25282fdf4e226ba6d32bfacdaa9c454d1966ad130a2c7dcf74bc70f4c016ea5484a5204b69d2e47699a7d1a216afd0a317157eec98b5b0fcc8b9e0d2e77e58473fa5afd1faf43bdaf95c2ae4ab971178e752a2ae874bbb9e4b497f038e7c26fd177ea191ef4b21cbf0b5227aa56b2ce9255f71a9241ae50fd0952f7fb6972fa1b4e06aa104d205295be0a951d42c9ae5d2ace52ba266d1a2a6466016dd8245faf48a2b7fe42b5e970b479b2b3a587390c02d529245f9347349d05271bbea491075a1b8dde6fddc8ef324d872c4ed3a2fb6022c97634c287125a8ca427f1c97753b8fc90f14b904d1e520262e0fb85c22f6fa77a4267ef0c6382d21e6e48aabbd0455da9f3c09723db85d8b9781da4bb07ec7282691fabf9b811c737283ab814cbeaf5fe6ba547e3bb21da4db6198e9b45aa4363e74be27d9b4906a1b9b4b5b29281695621d190e3c439f3e0daec972902bfa94c61db3168ddbe1980921575938663e5c9abab4c533f45350ac6db16a5fafa66599067a2dd2ecb396d7c0f0642e24cd7a7150104063f6e4d29d22dcd1330282873b663d979660873b664b64dc3b663f978e19ecd2af5af6d32c4a47947cd2ac09f3ae8b66a07ccfa29ae5dd4fcb0cbe232bf94f4358f9534a771f2df29f6ae0eeee9d02d1dd9d7207375a9c2dcef79f30e836540a1cb71055dd94c56a9fdded41171d2c7f6fff2203bb7dddb694d875dba3649ad57916a6dbfea6c5ad57808275af610d342423ab9a41c61544280f4a3a4e98fa7368e32b5f5de1b343958ff96c836bba37dd8417111144536787c7c95e7b034803c81b601c8369569de190793d0cc2b1db2fa980b175f137af0130c56cc53a0270b5f7f6da796771743fd21eeeabd671a4cfe475e011fbc5905d2da43c151c29ac455d303a6cd456cbd81ab027aefc8e71f13adaeacdb35bc30aa698851795c7cc346e5cc93a98c757f27b7a5e2f0aaba2860a42363e85ec3797a1af7d37e3cbd1aa8c2fa7ca3bbde6ca0acaf019ee29782d1d7f79861576ac3154548051d9171aa90008c0e5868c19164e309f1534f9594113d84f9315c07e9af87c60e5361c261a3b80d29b50292a2894e933942580f5e92922c98c2aec5644129f5792227c5e34090d694f26bb0230ed695991c8ab1db9f2d593fd4d339290f2780eaf7a0d7706f09875749729ab669b5547fd4e4a991b5f8e164a1c32ac60aa73c1a3947ead95b6b4d6e6372a0d7d1a5aef0bf332b507f0842eb7749c68dffdd02777a44034caeda8b8f2fbbfef401d84501ff72850140fa42d5fb9acb02385397f5938c3033bd22737ae06521fcfbf1de29d60006e06d216d5e955d3279a96ad2e87804e8037bb86b67a5e1c2ba82093020b33370410809508001a52ed1c2b98b55923a535d0f1c6ed6f7e9637eedf7e19d629d977ddcd3fdf059e773e4b10242287c857b1ec19582c675f3d6b621ddc672f83756c368c20fbcb3c236d9037b4a2b84146ec6459100a786e96912ca9df06a8da9e6f0d55b5853d1070fb87dcdc1aaa6440c0ed47c0edbfe1ba4603eba4ecc1e99af965a5742576c8948d6a6b93b0e3bf8cd841742110a23f949dd908b3ff5c7f8f9999dd06ed01a09103ea09d2819e52106c575454545454345b4e265b9a2d618b54d42a6161399a1c5ecafbac34c1c6d2a5a2a2a222a36ef534a2757cc76b9c9c80a6595b929247d32c97401938dee03376b6414fd2a217555b545404644b2f2526e44f1b1db5dce53c5cf6b2dec0d62fbda9d4452622e7e119ffda547a286ca11685ea8400fbaa27609f49494bbd96aadc675f1884876e7b35f49e13d8329eb0a4f71f6c0d1be8ef226e7418074aa3168b7ce5df8112cb518b2e8b483ca8df9f7d0d77f0a53224aba83a94ae16775a847d5158d1579b8c17d825bdf2225e53fad297c22e4240e9c8f6783d48b0a304bab38b322acdaabf85def3b2f3e5571cbe1e2aeee601d686f45b58badbd66d6d1476514b2bc23185aa6dc352027df8aa5b5ed345ecddeaa25658d44533f8ca81b458023b76d1eda2ad04d2206473828eabd5a2f79536eaa26e3533771258f42fea56d1f5b06b1827742cf372e5ede4473f87cb1410455114c58c022dba288aa24833f29522428870964bfdd56ab55090d3938557ac536b728ad8e115dfd1ba90530412c6c12f579b379e662f9d607df502841579e52f1ab9785b488b3b8028c1ca2bc46b3c94911311d222a5524ab962d145230f5332be39e9af3595b2f168901185b52dba0c29ac7c4ea645af390569d16d7c6573ddc6266573d382bffb3469d63442ee20b9fe397a5e903e2592189e91e44e8beed2c76b54f619a7a515e1b4bc3f09942650f2b46863e335a7175bc0f7954b974d6692ae8d3b6249efd9db78cd77c56d2a6edfbaa5d85ff41a0e6d5fd268e36ae1c85904e12250863341ad2f5d5e73b2b9018323c272ed62606f6c5af46d0c3b840dcff84bd7751aa25a9ccf5e6a0f089e416469d879dd55fb5eceaea3de979379c3784503ae01d2525cff8e73bc6f7f52033b4a98a4d22c189428343645a8a812b903f11a7751a116a95cc9bd6b8752aafdd6514a358d526d04b746b99bf3f41577b52865d8454ed8b1c70a6b564f92972f711f6fe23f45545e19d6746a918112c5967fda791ef7e5f09516bda1b4e82f5f6a4f29a9549f524a27e94b2450d334edbbd382b05057650b2442c8cf015668fc4b7faee65f8ec6bd0525cc575a17ca222e944450ec288d6496fa1a96666d46f2a85b1226611226611246d4a25b69d16f98a184a54492ca272dd9922dd98a41fe784f101156ae7ca176cdf0811d3267c0ac07d1b6f7a7c16bb53695faef1d7cbdd0c66bec9bde14ca96affc65fc288e7f12bf254c8516ec225fb911ac36acd66a535b38a6607f61e26d229ef197405a286130ef09bde742611a76518bfe0d835907027f5cab93faa49956ebf4af95522dcb3277772b19137456cd9ff0a9d549678b093a6990031e8d54e947631b57ec74665a2dcd40c4463c06adb3fe945dcee7c50f1fa46e062e32ddd1b2d1952dc903cc67373f27a5aa6593ce50b4a2e1311c4a26eb1733f8aa6bc7d16c33d170813c2b226ebf44bec12cd72add381e5ce3bd10d932b98260f1fbe01aebab7e3ae7a4efdd7704db354a468672498bfdf225a5922fec685f28259ad762fde143013c60b859b906c06343c40c68c69681fc68f1a6459b164dbcea07a52b886b353ab927c8ecca9edb3d3e78a6ef690656caacb33e4191b91321f1b19b16334a39078d9865762ed1b18c4f4aabf6e5544b2a655a37e9d6160a17fe8ca21737f92da03ef7dad7e303777b0eb433f5dca4ffff344fbae997537aae661ab77d243bbd52cd723c2ba5906dc9d36276de0206f5d7bee551de577a7ef1060dc99e4e60ca92482530fcbc56edbcc5d45b11bae772e8675ac64d1b46502f572b934a367bd69eddc4158a95cffdd6c96f01db775fbf1e1fe0421b569b1c48b0322d768e16c51b2db67fe23ba53d3e3069684e4ed0709d25e9987e4f9d8aa6e767130d1a976db36a5c15d2027a11ffea03134cd4ca93a79c1c3884c8f4dcab077660338ef95c36e7acb49b9352f749e9578d2e607a0e7e52ff7248ee7ed352faf799e7699ee835a59a69943bb5a058a4e787a159396c28bb505dc89e1f5592a62f59930d4b255b4ad92880a816eda9de6ddb36afcbbcdfb6cdebb68ddbc2ae3f19e815b3779f4388580a12495d4a6ff6cd383219e89ddf02fcd230d36c735e74cf2bd9eb32afeb8ee31eddc33fb3dbd7a3ceefd19fce4981e8a4772b0df36ffaa63f15589edc44d7c912b3943497672983d16856513bf06b2814d1ad9e0f0c4c91f75c961e6a072bbf6baf3d6e71eca1cb63ad2d4cb05262413d6a075b2a7d3b60aca9c4dcedeeeedcacb5b4b4b4649406493ecc95a1cb162b3aee6ab6c3e37874f3bc66a134b0fe37ff936100777bce7efb7232adf332505ee979fdf5a0b9f35f560843deec5f3622acdd39007ae78e4d1372e7b4d2d59ae5c8364a27d5b43949255a6d577b785ae4f9f155131f964472844567524ba06a7bd2976e60a054b5551b7289f20c0d55a1dc97545c68e9adf629c8c4964814d5e2943cf225a744b9db46e9466d3a4a6bf55c1e58b76df3b8dfbed3be9c2d07f7b5524a376ef6943c3c487ab64d6e5a388a5d97bc68c141b2662681240eafde89ba13c86bea9c19902e5c8071ccc700cfcc1fb33bdb0649834be4eba15d0966c0a751110a6af7025cb3f16afeb6994c34644c028561ebcfc03333c802b4908908b6af0fa4be1127db465f86230ded14ac41523706218ae5148553149ee86afd5ab3acd65a6b142638ca6854d3268979e6cba9415f087552f834d0759a4929e9cf8cd2eefe8ef0fe427e00a140068230c855eafc101212120232332a97b4e84637b2c05c79bb2c333d7f5e279de01a5328ad8da294c201e97bf08940082516a18f16b7caced3582dd3bec43273263d6221a36e9ede696196c4c2058bce8466dfbde7e3c717aa320a866c3dd94a35ecbaf4bea980169d26259412127de5ef819fe2bc9822bda022bda8c2caf1c20a8b7c1d4635df53cd2faccf81f46eaf814cbe50e5008aa3831c03c70b59f268d159c80710d6a1bdbfbfba1f07b00269947e346d3fbeb2a1e48257fea5ad924cf6a391f2c01f2591165a288d8c7eb4e85f65cb07919546cdaab26a99b603cb5150174fa4d1c842d3dd478bfe2a9f1d4028287eb2f440e8a78504b69f080c2d02f1c8226b36259ba8d7949eb9cbbacf76ae67912d224734b4e8576e78c6935f8ebc13ac3ad851be543fb6259084d2a2bf074a9818cc924b64122c3a93d397def4249004438b73ceaf77bebce6640a6b687dd52dd8b11e59cf72bd8b26d7dff31a927d01089033500ab4a52f85bf811ec8a41492c2cb26cb37af256c912c7220220d337e6047cef258bafae5649dd9b4686f78f80032030d4184102962a459f3bdcb3ef3ea7ffebef2b1421836fb2ecb62f25a30755337b0d266183cf91a399359d6524a95fc5154c950bc36a712b089af8d4fd13049f617fddd63a0f1e1ab399f7b0aeed06e57ebdc7eb471e7edba1008283ec18e4c348ab7fb49b970c81079bdb0e240d3581e45f5aaa8094d7bd77cd04227337bcea6cf6c32d3ec25a54f6b0cac94609cc0387d71fa22fbec0486cdb22ccbb24cd3b22cabe131ffd4f2a72f6c4b188425cab52ddbc8cdbe7a17455f6e561766aa1ef66a6faf16d616399c99155d0dcac77a2d6e4d7c0d984650a1e220bf5461c7da43247f6ded19aca1d68ac147d0ba446a903a7996109ad31bf4405435829dedeed91e62b774398a9516d0123ff2aba11748baa872a7e862cae529ba806273798a261c5df9e5b8fc60f9d2a74b58ede5779efc6d7b77ce9d7beef9eb421bb4cba11c0c828f7f27360bdc649216535e2371a4ec69b13f971dcb37fb1baf3ab787d0b18c12464bbc2e072dc17347e9ba1cb4a4753bfe725e806c2b4174394809a13bcaa0cb414ab8ae7f392f30db5b290bcf6654ebc051e6ba25fd7c6b3d9f5fb27fd3260f05a6605ee6064d8b36e00b372dcabf819577f6a0d7dfc30e258f16c47439e79c2f403578a18165ce659326f451b75f2e6951368162f93b6c72a59437e8988485e4859d5dc73263f3dc9d5ed16769ed734080b4ec58ee47e9fae9cdaed1d9e17166bccfc964c6773f3f48ea92545dd883deeebbb0488bf3678b3364e27a225bedb6edcb69f9edcb4175dd9c5d57e4a15ab4d94dfb9b0d5573fef42fa7050b7d0d94595aa44f3f697402dbbdfcfe6fbe7dff20a9cbfd66ed5bf719aae6cf700528d8d75ee6a0a6dc913c4826e8dc37e78fe0bae5eccbefac6db6cf63bd0d447d055bba0e747907657c2797b84c363d7db9a445ba3db78bfb2c289148977c590b4a243d21fdd2cee9a551ef902850efb824a2efa02caad22cbe555aa4bf0d9940974619a58f953bbeeb89149941bfebbe03c7794bdf7d4fea883a929cf66710ef03e7db6e7e17b2c4d175dffd0c5087db0063bd12d5227d1b033b32d0a447a5ffbc6f161bf18d16e990f9527e31d8803764401aee900b3997917419d1ea02324aa34bdffb667107c2b4483fd5c5c03ef7445c2f4f6ca0dc69917e45225d2dd27739816d9e53cf5841f762033bff3487a68b10d6ffc5899719586dd3344dd37e035fa2b0fd55c3a16319af7b996a71dbb6ad3ef72f50c86c2488655ad7a6dde198c9aad1c86a69bc90c657f32b0d69b630957a1f2f50ac7b9414c028a5ee3de79c4e29a594524a29a5944e4a9f524a29a5f45d84b0f3e9a44e744ce2c2ce1625517d0d3c3d129f528cca93cb592ec77cd0a4ebbe1c9274d9433af9775d27bf1cade51b94db97d312aab486a1451e0af86e8a60fdb7afddcf7d909416aa3aec416f734f3f1e7c6bf0cfbefdb3ed494ba4e7bde46e7afec9e09b76b7bf697faf9cbf07763504fdab74ae65f0525fce101a9efc7698de7b69dfc1d273d504ca78353aa8dfe17150a193d20a61d8d294a52ec9d2bc3939c16e3f32d0182475733c54c9b007bdf26558a4c5ef678bdf9d250b96408bf27ebe247544ee242939d0bbdc4bd7e5e473dc3b289feb9692e3c09c21ddfb8e1dddac35e277620e0f1c821d1988eb5740b35afeb468675604262af5d08159d87e91c27e0a5cfca1c5c2f64b70870e1dfdce68523094d2fe6cb6f441c77e838ebb66e21c492adde508fd72a6949e0427101dcb8cfd925acd5a488bce46359431fea6102f621433ea84645d630c2479dc6d8c8d62228d7b385a4f75617f64231b1e30c82497e6851bd963df8369561652213e5fb65c37fdaa437c50124597839218a2b91c9484cf9d36cd6af71a8a53569b9bae3b729bdb02afe6dbc04a996ec333f3495858176718a617a8a4ed366541eb14cd08000000d314002020140c074442b1482c1ea8c1b67714800b81a0506c501a8ac324c7611884102386114200c000080000cccc3620007ac903a33bb4bdcd5b4ec13477137d9d6ae94d9b50ff85e17abdd964cd1268069f320e9340e9b67c99eeb99731cb35c8ea7fac9016d4b3db0d6cc0e45ab3e111506efd3d3f86957524ec59734349e6ff6a3ade76ac2907f43e91c5872d8871e8b840a558ccf8320abd9139ca3c0b433a5fe720f3ae9ee4550b47a932b7cbeb3ff767aa553404c27ed323fd7d71f8d65f5767ea375b4d8921173195a7b7e70e967f346d244bfb0037e8b553d76ee1eed71a6aaef8a09bbd56751cc50fa1939f568e488dfc354928f469d84b89b9ce1932a2dc7b2697ffdc422272722f8e13d17ea5a0b2d4cb64afa2dc1dc5ee20192aefd658588cede5647ebe03f65f01d0f9853fef3c7211ae4cef4f3f3ef3cf5b93cf92142f9d1a1e27269bba86aa02170ac32f62772f427730d55524e4b5863a6864db69012f678045bc81b29082612ed36eaf0e94af236d0fac24e64af55413fd7036f573edfcba88c6b991fbf840138b3aeaa819ed86758ea9f3597f0d87a24100e4e39d3c6df0cf43bac0ee65bc19ecfcf9f0ffee0c48e259344f649d6b1c58464391019cc2bbf56c3f4e192b7969816311c830e4be00b9042cd83e4538273e97d97c163ef389dab6fe045e72c81ad121d9807b43c3e350b9a3e34d1f0fa0bcaabe108aeef0c35d807f3461f94a36822bc1429ff3986a6d1024683ecfc425a85cf6887b440688833984e8d7d2cf72aa8fb0915cea0785ba0698d7064551c7ee67fdde5e3c65d3b7a25d7c81a5ae4a042e8fbcd982c825d3d90cf175544ac1c6e0baf41eb9f0636721cbfbeb3d6c7c0f89ea9c2571e585595f087230a8f933312b40c6f8151988b823b96f808612ab3430d143cdb186cb2989785b74f1dd63f7a8296d186d6a4534f3747cd7f9c3bb919c3ee671906a8f3e224148104b580ff5e43b1c17ea5346d6848400efed7431d4523d1db647e698badff670c05149b64d06637469edca26d43516afb29da37f29e94926f1c12474bd73ba2e643fbfac26ddf6842f580d61261cac6258499380e9e0ad209d622c13574c5692a2d882998c7889973be61797441201b939ee1aad07a50acff297d2e68ad46d4dd977628f24812c7bfc1b19a0a1249e16ee2dcf75593524decd3f070b6eead110d8aa29ece2ce66a2347b90e890ef1433408ee08bd34eccb60fb1fbd0e964453eb544e2500dd1fc53225ded950cf70c30605dfd8879a2dbdd51015060519a2b7eea3cd2e2431c414df1f7ac2111c1642843e56b2d78a6fa6a5fccd1b6e44a4a48a873d94745c5bd58eea8a86a66c4bd0e616328d8a748e1fa00d874d491c3214ae73cf0727ab1219efa2383611c9be8c369728c281885bb685fe043280aed17c415822ba321285e63f3946c95035b4b8fdb75377c43c0eec7730ffa11bd2f164ccfe8413978ec5de02d1976b0545077102455c9b5301058928b56e4597ad21507452275ce7558c2807d12b3323af39ec7d3fc330d36fd912bf73615827b579e05162189c1038c28d4004aacf0ab136e469ad61e5ebe2db6cddbe231064129ff4028c52624cbe4fa0eeae5858c90790833e3c88805ee3d36d8c0ab4dc2e39384224f0b138abe4b3b9a862f697ca7e76f1adfe9fb4bd3471a7e69f795a65ffafda7f39b063f69864f9a264c16c0b47923f54169faa4ef733a9f69f848cf7f5a3ed3f09db65f1afda4e193b67fe9faa49d3eb2b4b6fb23719a6f406fbaf8d64dea83f39e10f4a48928730f52acdf1dae8b7f981b725e0c03f34f99c1fa4fecd98100baa4d2ac979cf99b79503188d384917053c0fc45f51e31d0a81734326e293ba76f5d52caaa4f207609d9ea2b7faaaadc96bb1af7d28701584a789f691e283c37e26054c3f2705236670f16898ccdbebbb1be9ac8955345447509d6943a385a2c6f174a92a9763c680396d21dd941434cf4592298d44285f05f7030fac7c6a51c6b9a801b38228ecfe22cd436cdcc78dccb727960909d20e0fbf97dcdb77a687aa60c185905028ced29d7bb894fcfc0d5369f08c5aa7128bbd8ce9f12b8f95969392d030a2d97afdc3ff4b34a2230f7076f347dad4adf272069f0ec024fe9570826712d7bef2e4c6097e9236d4ff46b3c250a93cb849f18e816eb06d962dd444cc5804b2f75e96654dd59f52b72aebd86bbe40037d9cb85543a599a71d166f1c5e26a0dec5f6f8cd776efb7c028afae5851aa538096365be9f3ab0c92629c578474acc4fa0d9a7bc640788489f710810a58349e874257efe70268550dc7ec436042f2a71e48a98004d4d3cab47a2e5e652c14d01d8705fc48f069e24e0d1a757b0fbde66cec5f2e149013c608515feee9697aa93ef1ab87ed8ba22d7ded7ca0feda6015c4c5e38a31b8ef79f5ad86cc9db373299e0b2c73ad9267cd517ecb14292b1b23f0ee79b8d17b7cdd41fb4a4368b9a9e79d2decf3945ad1b8779a0dd3b2b91b1195082265d90f15cf8b154ca45dca1a425d9ec0da57560425c1f7094ac2c0c2459fb51654c407928d7ecf30d02457f14180e5c01161277681ed76c6b6256292b1ed0639634efa47c9180c96e668ecb7c6bdbd44ab4ab39b6fff558371f1789df64b90b0ed469c2c46d09e529327a1d23102a07bbbe295adfa08b9f30b3a57495b80d01cd31ffe427e9053c5060e5ce99bb86a4a9c31654434b4cebc1ee4f63c33eee46fa6ad6940f6fa826380831e782cc3b0b3a10245cf6a5bf3cfeccef451bc868ea6a3b4ca80b53c87a160bb9cc57606e2e8b812195eb25c55ff4a7d6663946b05ccca2f638c84aa2e3bfd6ebc08d559e0adb245596827e94226d6ac33053c98829d1ab1368dfc0b7d80c8ee1586eb94201dfd1133c630d64df9b33b0a136c10824b26fe8d2164996aa802075ef7a06e0ee1409d01abed0233a6d1e7d3daf51f7acd602f1e9e99f90934f0b792aac76a8875e5f44543d3f2edfe804dcfe77f1261447cae6038c2affece318ca3154053743f2c370d1605905942386339c936be2a8c80f01b9e921ce9179826d32bf0d1f9ebd1cb4c7869883cba1e9415c62e4b1011a5bd8bae0e0aacfbb3b70d476284a34059997c1753aecb5e6a8b4888b11c30ae48034937a6304000fa9387b141968b6e0e97ca8389b2d70a157270bd17ea4b58347405ed32262f2f4ba7e2700485e56eb8adfe8f659caaa81646aaf6c241719ab8eee980c5a18dc506a0ffcc1892e7a6ca1e6c54eca966075519bc3fc0ff96bbdc25f964929a50a4388f5f8abb4500c69ac9b69679be8877520e023195524f52d1e6d5a00d40e311a1836cddcefdadd3ae47872cbe80899784c65d142ebd117bfcfd46f697332887f4f0c6ab1e48a498e2db3619c6ddd3050f2e7c013f0d52cd36bf1fc1cfa3c7b44a7666d0e1ea3da5c9e5b3e01590075de02a2bb89e88695bde828d0dfc59aec4b993551939e0de820297d05d6d3eeea7b8eb51cc6114e11207bd9922e2ae1beb0f844ca2d19812ebf6322480776ff5428a3720dd27271fb77ace8296301a575dcf89502ba349bad047ad17450f7104dfbf58270e1371281775aadbc09714bd651be040e331558efc9e24711f36a029812b96b2f4980896ef342c2449160b4f2d3f6f191a4251c6ac958cb8dc13e46ee88e9265a5f6d16505a27d6bb9a8d24d19be73bf2682d2f162666c8052e0e6c6bb2506e22dd4a38ee72a621fb1c674c64ad4c1b46e98ef722921310affcbfe993bdcd1c5821ed52e1754ed8283e71b5f34a7c323693ef02b96c0c324dfb3edc61450e188703aca6107ba66ed8d95a94c4caca6a570ca323364f0a4c86b929cbe1fb5069f1daf9d81ee3a96a0f8991e603390b3ac6804110103a97ca7b9122388ff81f4df2fb31aa868c42272150334519d039f32355007b154e32f0712ce3e1d3dc70d8d0037d3724cf351ec87c03d0683df1da88504b67e45a72093d17efefd09054f883a3703943f2efbc7301a38e003740dc0db014f43e74d60e08aeb584bbab315835aab449fd03776da264d7816828c54bd5cab206ec8fd2579226e427b640d8bc1d7932e505ca795b1e17574b94fdde8ca570327da271a854e4130e72b3dd632b6164c65415ceed463689512935e77827add16f2a80e613d120a7f6b363b5136e45735bb609205f779fbda89068b17a1a1491c497098882f3189645c0fdd3c0680e09f2de240709cdeb5a7eaa888356e18845b6a56c42466576de55dd4fb88fa2f51672fbda936a58698b4662d33206968bd21a791c498b3cffc7e26ee33bdb7c97ecdec61b267537b9aedd9fc1e937b98b3cfb4efc94c682c8213b4f48d4ccce41e26f633bf97c93d4cef6db647337b98ec6ddebe267b9bbcc7e43e26c8bedc04ed5e260eae2a31d4904df93ebe196e18f16b405c9b6ddf6ace5bfbb98cbc958bf877b57927e93ac6b9fd4d097d830323cb72286fd97d8c137a5ff66633d1ed6f2805e56ae9789a7946b1e68b8d6fb08ccba883b7e09d8d1ce19880a7adf77b740fe3326a6790a2c9cd65e6312526cd322ea3681e5920c9f9ad027b90cf4e822c31ae538b4254f8cd2d76550719cde83d3202de78c00b06c24d33f392aee2e70fadb431e215261062333486d331047d2f8f66f088b1f25a045d81780b9f15c6d0371916ccc6791547628315a3745961c728bd48c8b00219a343805fdc01a4c0612cc24ce090cc14b570f6c14e5dc4a875cce4baa0f3b6fbb1f89f1b04f32faa4adde10efe1b0733cb32a14408b5306f5bc62fb20a8544e03119f13cb8299c9f24d782c76a66b2ba3fbcae61b95a1bf749743791436f952172acd68042834e235de1f2940a508ae0264ccf1231f75510ea15917112becd2020197c1ccbeb79b8ededad1227d51f0cfe49e6112192b334b5c7033b9c8abf47c4dce9919ab256d1850e67502d60d138d3dea981d0ffee6e41cd6266e1a603cda9a885039e8d0e2b5c4e656d764acdbd1bfaeb25071fd200f128bd9a633b50bfadb2ca4f97c337057d7330c7f88cad95aa68dbe08025a6c5a2c8dd4bcf0146303af0797344a0258a081df64d77ed58aa888a116b828400d2e48771df47ab1cba71498514487d757bc43c18ab0894cdbaa3e68d09e555e1771ab73c8825054cc1d3c1258ac229b1d75f238efd601b7ae984bee47c0bd871190c1b11d5f15becfef669ff5c2b6130b006bfc095dec2302858afad4542dc487638b23316edbef1fccd873cf8cbe7f757cc0069c4e6aa10500f004e979b5ae5f52766f4f9d84e4cd376451609039d67e47c1491e4df27de8c234001b70ddae8e972029319b732a6e488c5e592ef50aeb0607634e76a4def19a3202c5f674e36518d26dd30b0a5d61c278a2a7677ef744009f42a195119fd8b44562cbb0af852e20fe90f71942fef6b5d7d5dffe79587211401e73cd969aa456cd241287fffc61086696d7ef92f98d6b92949ed04eb06e56a236b758a22168ec5a6ffa2819580990498940a02a85c87511ed1f1cd2e1ee0da31937da01c9a5115e0e4dd2fe488348f4001cf06cadddf1361ca20ce8ba710d9b4b66e80198b65706a8e12727e81a5042f9edff82d978a2cf581422175c2bc4017fa32d6cd9a86e14e5028d850be2aef5e11f4283147ad13ad9f6d01d10291c8786888c69a1b5239ae9a6b0ff354ca099fc065ab8d81cdd0f83d7db285beff16b3c236c8d05b4014f81665b136e9e9e15f39224682d11a34d685516b8de44c432feafce8300f9993a2bbbf648b2501969f54c92813518071960e145e74d4050d34837d9dc0cbcbe52da3800dee1144f3120fd344d0e7e324e3a115c3cba06900254e40e8fe7634d887b16c3fb9cfe6024301ad46e49998f6704ee75c4064df5fb03bdb48b7476b97d29572e320eb90211748888b7c3016562c4b29d7d1213e703b99fc843ad89c30a7c3aab7b32f5772561230a0f290fef840975349581db7ef4b786b10485b79c19df99ad27f00117020eac26239ed6443cfbc0e36a61233b93e06b025d9d9992981d9f7a5acafc19bcb44bc0526942e74697759619228e7487ad61c1b70955822d932c00ec8d37835107aea313c8d761f612094175877f716fc7e05194b69d5ac80fb1368e597043d3b623fdf666246b4af8b280e7bb737439cc797a594210e360205d18a469744804fe9387d8645baf9855e3f9aff7b74c5626a08e6911d76725643680e517b44b79218be40d0d5504af38500f88fba6b054a5aedf857b2bd7f908e1d449477551ff776a88e4b8265a43a5c109c431f63bebd492a38877239244c1e1019ff5304356b692e8f5a4d78346dd07bf780149baa507a6fa847a1f9f26e2e6c36281f1917640faac4470b198bcc612777412f5dc8229592323c766b88e595152834f9da4141e9947df69db7db79b610c424b7ac031a7f89b304325b2190fb5d5f7a324e4de55cbd7b6c9783401d91b1da12e501cc70a326a08d7106f488766d81ac853643ef40b451d067a2f3e74f7271206f31f10d0122a8a24fab74adbe1c2d0c0bfa3950f1e86c0baaed0e0ce740177f73fc533c6e291328a5fd235cfc617d558d5f33cfb42100010476d41fbde8b1967313f1736bc4526c5de4e6296c3b4ae65a9bd579ea743491b9ce25f890fb0b75411eda65b2ff52a14c7468bba34c28ab574ee19a512002b35e505915ae04d670e3ce65131439c72c0edec46900df79ed05534bbafc085253b23a4bbbe731e3a005525aff422095f45a35bc91359981943ee5dfdc4b65f6e2a5c134110f7713d64ab57b76cc3e929193d95078766e7f59ad2223ba9574e256c361be651d96b4a4c567739e26913dcefefa799bfdc1216e1b28521c27b0cdc3a79e96cff588f3c78aa8f5d93f69743c37f15e0ed846db37ebdc026ec4582ec4785d3e5f580eaf4b75af2c61ad555f89eaec78d7dfba2711e49734427287eff015c299cb76187d2a7980e1d552c313796d28e1def11bafc13dbc638f710c900d2e04a433828f5574c9fa8a95cf353b260da4dd8eecd2ea04617a960d10762daec82edbf87ea1021ac7ef2598d2cf93a27e7c29d10d4b94d74b78266f295a1b27797752850b2354274c72dd4484cb993f40514d48d8e6dcd5408d2f24e235b3a3591a7ecfa409880a449b3f2df78bcd5907d92d8b750aa391418dfd79c27045de0728183c91b9019aa1113012ccdac31c40793ada58a21099835910ef8da2e40aa356d1c0e52b8754f1d2da00516230d401c073cadc33e43865f7e69114dcc8bc2f11715e6a0362d654a9248779e9e6a1b95643572c5a4e449c695c0e9845db63b211e1b00e3b6658c0ad0e03038e2bd1bf62e72fc941ba2518e1c8bdad62ad1cab57f5495f68e555edb462e61fd4281614e1f18873d2ffa32a26a0da48549e3fa7de4e7e469f7b87d8d2de0ae81a0e7fd1292025bb52963040b8e16c119685899205c4b44e2ae7d1528314b9999e062b0ae51569ac86285be417116307fa8ad72f5b27bdcb53bd2569d50f18d4c889ac70d63e72deb368cf235d71c2b910e3fa32d79e8a14bf3b8f73e5ebd162699be5fb4fe21e4107cf38b28af9ec69dce392375c289f6def5a783aba1961100e0b407403ebfc52b07091203645627b3ccf2fbc97b46a7dce0b16cbc46e57e32afcd898143c79799718350e02cf06d1d2e1b377408234b186e5bad7612e2c5948e2a4463843b3c3718afd4671dc1962c817644c595d0f5584233fcc13c32ec9bce9ff806045dc679966e4662174fba557baba570bbcfa7d4cd1a8e30fe01ea7809c4c247018561723fead9587951a382939e4cbf8db9e3de7ec86196eecac0662846cf336db02bf3cccf1b4219cf2a33b104ea1f9723cc76ca546f7674f88ccb3c849cbb0bdd4f5ba4d256193ebb169f50ff2166ad48023681597b369faaa008023ba9fa838738d5157be50fe5e238f6c0258d3a446d7b553a80faf5bdc5e4bae01ae1e39407961a20ffa5abef690aa6cde7c212ec7e813884f17a10435fbf098e1afef6082fb3a3890c6f1efd5d697df36b15618cc1660b64a8a000219d223ada0307a3a85443fb25d1bdc9b081199a5470e993c1633519bf2f2dbeee0f0e3a4291398cb623616e4eec80ee063a1e1f7941a9e9f20c6c7b980c6d7006dd61bf7f8b506fc8b3ad856305c00122e4b0ef5fdfa0de8261691e9f3400b71abadccfea98b48a6468f56baea04ddb7f9bdb2e7c572c0d602ed3048352848e639fb7eaf03cd36f6ef1574812587c8528d8ce4888a9c910901e90d67938aaa0c3bcc1f974942d55e6cbe8d3ffa9dcd21a7e888c15d10ba1a6d99507ca1bf60132e1a627a1f88086b3b172c3b4a4fd13a1aa6e1fa969de59d877d61852f101bdcabd6fb7f8cce20a29e51a1d4e723c5ff7f158886190999066c57c43ea13f1472792078aa5784cc408e025ee7712bfb20cd8ccd48e8b36ad61f060a87b99102389555eefd3e0acde42e8dc9b51cca8d30a0ac2af0d697ed67075610cac490da6559fee0f43200bd5f68ee3a207ba48df57042ea40676b75077c9b2bfc9de9a1bc053a9f51c344161593280c2252e1b2e438903373fdbe2537b5730f270e30c1c226144a52d7cda6370bf3a54688ea3c0a0886c28b871c6eed9a87002ef8addac57a2f1b44c7bbef9caef81608a0bb08407c1fcdae1c2882ac6ff65e0a599965e700d79d6e95877917b8835645b0ee364654c03ca6d79f1bb03b71578e7df9cd7eabc98ac3eb5bd9ae5f91c3d4d7091c4d5afce60493f479f75ec6a290502483d33bc7c70631c8d88e648a1a936e6ffeda43370f317080c106904499d4e5136d99226ec2288a32ef8f187351e85b0ba0f9dde50af463ecca6c785457a58ef1eb5927a3a9d168850b4ac8bf2d1c3903a1f67a65edc476737a42b7489d9ce42d82eea4753049c9631657b6118b42f0dbbcde081726ddb77050770bde5dfc109a6a8ebc590fe0ff12350a4c4c9d68d85155b3859b5264207a6202184389e7f638601ec2e2a9c95e9b93e2063acdadc6d04a0a62220df4e049134b9df25fdf6183a37a9c6de2ad03278a82d996bc3d30695fbdab679d711e2f088c269a858ea50eea313eda379157824c0309261a34e331683d3a03b756239f4a569b8ff53e91e9f0f937b6383b568b74654b8f9b765960bedd7fabfc28a73036356642bf5326b5501af886296283737cab309d79f123d5639e8017510d423512c55f5eb00e9d9c7af085b65c9c8825a27ec6008f5084f71d4a5613ca11eea82544142172c3acaf9287f22da96aaf7de016365149e8b8427dc28d56762080246882fc81d54743d9a79f38883c52a4b025cad2c630f1afa24487703a279624077f47b878144cfbd9d8c19866fa2188a8b4b2010f0d651383be867f730aacd932557a1fb37b66c6891e850ee781b42064b79d8282008221c670781e507ce02433afd25fddf3a2d35b4f818f472bee33d4626c6599b7b4c54bfb3684c40ad077e1960318f3b8b39c1db5e3b9e88df96d531812cba1bfe793377eda59adc1ddf58f076eb4d26b250a7c19b0b35c22313c4301fffaa20e18eabb76adc006bc07ef1312c02c70f3496380bdd9e45bc5737509562470d7744b10eba141ddfb34b32ace994e099f3ac47fb5ae225f3c50e8e925720bdcac918ac81278843a6142923806d920cfe6ddc94205164577cf032d0c3296db6db0d20dc6df603f11a8691f5efe19a10de44efc3da113ee84b6ff8d7caeba80c179ae2749f6bdaad16f1c1ce19845f3e80e0283af22eb251edb93dca004ce41b91330d7134e5d4ab24ab3e3f09e4bc4ff53c97c1471ef76f0dc4582d8611e4604bad0130c05a5c87736b81319a1c8ec71b44fb909f424f6a1a13a76c5118c163dd02ef5b2dd66c055dee34215bcf13c25eb128a2f288633821d0539d86e23f9009832db98073119542067e5279fa915de900af41cf10c98d648ddb8abaecc8bcb398f759bd8bdb6e3fda847c0366ab8f399494534642b6c1fef83044bfa45d993e3fe1124e08a8f7be4eb3066b88f87ac87815644898a60a828e823c4231252107cf18f04a1739472784ceff2e1ee6cab2216d5d6a2ea6c6481c7f59033fb318fd752168848958df4066c2589a87e784cc4840caa8e25815908bb8090e9a85017939f2af9b55d4f6f5f2d901113a428e817e8dd4ef4cd7c7005ad093d421c18e0586b0deb24eae50a4ce0bada4b681fb48639a0ffb80deecc6eac1f0a2c54bbe117776fa72b52f0df14280d83d49a08b9a725260b2882c023427ba6de1b0d39c93e018703095410f05408c2c04aeabaf361e9b64d3f2cc117acd315b4837f6aa40c387a2b713c7b7786745e29d5a77825b47a8082ca48bb11969cd6c44b463df9e0c6a994b27a5d965a748864f846f5d63895acc204372338440588d77e3bb0a56c77d76ebea52a7eb764c5055e441a959c815c44fca583bbbde92abe08ef60611e0c8e34e893946abe6d0fc549a4c301a6821773bff13bf895a185b529179326d28a3270926cf77c57ef29e0c27aed6f60e8dae3ac13484f002696ba1a1382f791275763f5d5e73ec5fef509fc748d7a82e1e255ad0d296025fb5bc5aa7be8bb887afdfbd65cc213a11558c0a8b543b782e554a82bbd1d9014cb72a920ca6e9322625ba62a6502bfe97927115f4f004f21dc168141a51f020e75aa93a87a60a203234beb1b61a1cdaf5806470465a5190c989d6fb5c023533da88e4a1d66028c0d40dcf094ab74be83e0e14dcd4de96290697d7c9f632d98cef6cd32e3b819a93854f4214a9a7a05628d73696fc439d9b649b8e385fa1881e6f4c3935c1428e62fa29a35acc625e8a369cb5cc8974b35c8ed0503025e91baf6784b46df18e493478d54b3eccf6ff0c585058897b44b4c8d68f3c41cbbff8f2db8880bed43f161b3dcb1cd77807a8925a4e35132ebea4184ede8ea0f45764ad9a10a27f744fd23d186792b907d31c247a270cc40c889218396fe5b2489f75c890a53087c1cea18fe36f7f940a412145d554ddc6ee8db052c0162f187bc4afd20561a4f0981a79537a12886866bc87ed06a3985ccfb87ec7408c65e961c23bf44a29988c53a8233783f622e186e783f1a243be95441869d499d1c81ea83bd4661bc501e0533773a55687fc6f990c85bfb98a902c79deb7fdc6c7690aa11c16e096265f6c310a204e5d346f64bca7328f0d238ed8295a248a749aa836797d6105098d600c1d8ce8c61aee4c3b800f1d63b45e9b9cac96839463bf76e5d530ed5e4af459e47b2b271aca1457a712336c342b67c54b969f4f32194469629893ef05d90dfe535c68e9256b042f778c5bc973813dd5951caeb0cbbdc99482510f1156f2b6f68fae7367dcd8f00bee85a477ad019eaa450e216086bd4186692c74c3c31951bb77388746b9df04fac34d1f2f4d0b0f831660b345e5dd96b7b01f1936bcb3a115ec82b8a4f814c6d8ab5b5694f2a3e97adac0b9ba7dad44205819f23cbff4dbda1ee73b4b48989eefe3d99fc34bea6a2e5216c9d81f9cd133364b3166c6163a8fae682e468c5a6e008dff8b6fd048093eb73bac666c66e36a6758340e18be44c6b8d27e8bef60d3f1a98969eaf53ed495fbbeb30b4730d01d08547250b03ec98c35dfd2a1b12b480ca4e2a1fd978636c816b0ece3680fccd032d2108057fefeff547db559fd02b85a81b696ffe514c69f4b1d51c93be5a60adb4d00c3efba66a42092416aff6eae54c440b8c387845c4efc5c573f3cacbef13ec92a9949431b61f4e970b3a60caf1903b4e21a79be11b5e0381d2f6837957d5207bc006afab7f7746a5c2e09370aec354bd99d9feed39257ae2c95731066d97a492b35f00e71d5467a4517547c0a8713fab5ba0168b7494ea428f410553157bb42c1747ca391f21543adaec92f6abd8ead56975618af5884cb26f59f70d4d3e86083e2a7df400d6e2df4e4c0a604bf05d2b68f01ab5bbfd85a7d94d1aecfc737bb08bf623d3c948fc41f817998428170b70b333108dd70b62889e5a7525e844a769f7152668a922c9811d23e9c49d781d07d9f4e0d52cde232818bcba91682e825a23d0e355b7b9e25b752d850340b0ee93e849795fa507a6cdd0c51121c571ab9e3e7a01c8c9bc595375294081e8c850196b79e483e2c111497f56d95396fe98a3f1b94ded4deef3550c7f52f85bd0100064934163659e6455c2cf9881abad939a17d8eec9420d43a0cd3fbac44de5a0dbfab92d1782df44ba628b62d7efb7fc3a8ff4cdb12e3d94568bbcacc89ab2eb5c9185b7b323d18bfbb2c141eb6cb7dc2de9e48046f5888c514a0d2d024439bc2484f6e0088467e8f1ab8626b131874191deb032725005a4809b423e79667521eccedbb84a878de7db902043310b82899273b77bd3a679d5e4875003fd1a10079e03557da748385fc71394b1a6b75b49c24af653ce4e0b792ce2ebd1f6233ac2f9c9464a5e1ed58349db26fad721d049440851ae1dcf54b881b2061cce5a7c019817e130133b8dc3239917e33f3e11f210ec93fb1343f9e762de57ce96828f46fbe616b13618953f4185bcc2cf58b8c4e23f9d108c6eed0762072f7f44459743f00abb41f6afd72b89f2d3a505603c49ee1eef60f75a7242c75a25dd45bc9756d600c06f6c40c18d60dc8da109222798f76c42ac22a50033d70018015102aa2cd24897755d855ccd5dff8519a94377d807166c12877769b365cc811df100eaccb26efbe4c85d43a55d69855af6bfc05cdd95a7e3e768182b80321116c03237118dfbe9e62c6202a9588127a6122f0e0149ca584ccacbf08fdcd479c06c23cc7ab1d58979e19b225ebaa2fa0a48b595653dc3019d7f50f79cb2d1929727bcddccccb8ddf6764772376749a5aa6fbf67f03ef080404988f19c1e98d0df98e9caca369bbf83451a36413ce2e1baa42de128453c226b8aa206afe45c8ed86df5c76c77157e074030294e7ed01ee2ac5a8b56db866be11b3fdb909948d781aa2023b63f11db8a665aee08a42520a2b42fd301c46d19fc3cdf16eaa3b772a7306c50953ff698d13befba835eb203d017f0bb142beb756c157c4d6c3ff4d7284629bd2421a5cf73429021e9e9d49ed2dd622638542ed247c2f860978f027e24e9fc48376ae8ac4b3c70426a9f261e41c66b022bf4d77b792ea1a54cada6637fde0a5f271a2c54c7594b1325ab48b5e29247099a5280d4d2e1fc8f2a8997e09a1adabddcfdcb7bb74afd4b256f226ad910b217aa24be99ff8092c11c260e41d97930d95ef00902f44cfb3ce2f562b2b63af4d446be66229154399a5b5620a0aa00aa3736354d24b8528cb94d231c50a0f1e523e18269076ce4c9505a6b8216e64a0451a6fa017d308437fd9df903b140ae04afc623e46a416537cf4281ce15d019681a30e0d9a8896f6602a9e684f991a4dedee040231d339f9c5982f648664bb7a6d3c9d4d8110eb64688a797604463985d3eb0ccff3df020ff724baf38db8b4017bccac9c46e6d071dd2d94a98753dd51adcfce7f0f7b298a6a74cb5a312ae1c68aa12e1cdc5f238ddaaa17f02026730745416e6b1a2266630229d96888ffd8ba20d72703ff4d54cbf2f0ac043e5ab8bf80c2dba12d161883705f70a85d6dc56884db5d6b2e3c843b4316d050d203fb4f5bb77534d2bebb9a4a22a4fd134e2aa0bdf74c18f053fd70ab15fa46688658de0d149eaacad53b11fc41dc0f3294bff957b35fdf0083c359603cafaea218f68f32869f4706334b30a84baa934487b2ea8873af756127dc84dcb73d517b9fee42cfbf02a2e50c0ffd9c918f7cf535f358e024f30b9492882ebc9917651cf0781bed72613a12dfc6f341a1b92bd13aa1810f201d6cea7a12b77466065344b2222e05f299e4fe3403dd056058c1044042c8983ea2f5bc71a9683e87bb4843efdd211be672de0b0e3f9f6543d9d3500a495204a19481c1360ed5d18c9a3939e817766bd8bd82eb49e45991b94fddcab4b64d8345b240ce676646b901ce3f9d3634328651e7923e3390102fb4623e0b9cfcebbb168845ba486d7ba20907bcea61aaf33b687804043d6f19228680da41a870ca073d932003a7855f0e47d7300a07cb3d8116dd6d9f527240e25370a9d293e1c0c35c5b5b1984e74d52a6b978a52ff26be76779c95deeda7b7d85a32e8a002f1f2930fb1440f09db2a9a6c682750c82877816d7fb816b424e4e1dd85c682a102cc9dc081546be0a85d8a45084df47ae83bbd7c18a7801b3c850fe3597cf1ec009e07fd9a855005d5ba836afee06360e7dc5540e35decdc95800b14de880c2f43afe7d7bc2417146784386af23aa88082e1e0ec4bd7b77512dbd7aaaa8ba1dcb12f1f22704e10884d06947488809c5aa2763e28fa22884ffe0867113ce400a6867eda6c66a44c67924aca564fcc69bda50cb24bde662e530147b1b909a665739ee115d6ff5ccd33c95060267cc2294d281c8f250a19c89d183635e5c88e858fef1848853859a11d6f8af53decb689271d588c0152f55599376ebcd26ecf361cc6a92fdea2f8f61f703279b7219ff4138e043ef40329fc952a81c1bd9636f31045bb508ec2233a3aebe67bdaffc993313911cf350847d9f60d4b4e0e3e3b4926c5c2642528b4d8bd27718dd6430b13f8a4fc7df46829fd2653208d2a734b3f438d27c56a434c7e103f0cda010622a0b8d9dc606b8692a2fd013c1ef2ed9c1e9362a302e2157db2c450fe7b7e94291328ef40d6fca16f084c51e5a6304f4d2dfbcaf365758a22923da612b473465dd0a6831d414a248ee50da15a1aa374b343d0d2e2d826567cae6049e7d203f843773e772e1beef8b1d9a74291703f27735fb2b886e0926b3c6f3c8103a71fe81b7c89b844f7a6a03eaf4635b24d4191dbac3e1c81c5d4157763c84b0dfc0526dc69f5c862eae2cd8def4b3eaa47dc247a7f81a628cdc4715552c82e3c6d97d2b5c02e2b82b754bb39f4939c9cf42e913234c5ea32b668a94d29020f8c8c743dec802b2a432f846ad5e255c845f5c27d9891d92d99baffb75b45bb48ac579954266a28cea89631e14332f40cefde55efffafbc6b141addac954e83c332e52162d5eb1b24f2f92119fae1ce04db3a0e64b01b67ecca1b0b708fbddeb7d9a50e33dbd53fdeead5bcf404465ad2a5894bc636fa137e2413d43f3a190eb4086f9ad79657268bcfdcd75b22959f975e6067af8cf5acf927dd5fa7ac98b0217afd9fe05b9011e3b35c51c4a29348f693a4800408d8aac0f00b0932b9d835449865d7cafbf14877b28cc4f8295e93e17a945584c99a631b1032623db4fd84a09f9b2594c952f75a885824903ee6f370a2885e903ab9746854b27f79383056026820bac01e56cd3d15a4dac96f495833909021910ae2a48020d5cda588d62dd184b9f8928672315abf80ef7db1db747326d3518aa9408ee60eab5bbd39869157a35c109219bb8baf6385484e8e62aa02f0563cea51fd26f1b34b217badefcadf3073c31c1dea92982a79784239a49fd7e83ad25c851fc47d41a8e78fdb1b0affa4c3d6e85a82635a34e598ee8715cc17490a6c9bfed49513384f98833fd279e7d125715cf0f3c97fc8e1c2ccafda58a0474f00b878a0f8bd2e126613e094e1431506c0f1ab10f4ac9f8ff58a736304377fb61c2ed8f9a304004e2f047ca3e157b1b59ad0301b13a21fe464ac2c12c1939c003806c377a221d128c4fa5dd5d3ef19440377135bb0dce4498c483ecdd7d4d94e763151fb704062d46e4a8e19012203f5f8aa7f56149d27323a77c9f954f01c1fdaeef741805dda5c028433a72d2f0dd00458995cf1defeab0f52aad50ba1592bc2ca336c87812a9ddaec49c9db062a34188d5027e8d06a0f6e8a9ead38c1a34b643d6308c07d2355edb902925841bf7e5e03ed6be2db8636a609bfbc93690246d73ccb0eb2d6a4bb7d96afeaf5b343085c50ff7a21cdd0d0da20d08bac4f8cb8d5088ba8c361f60e6715886194a14f2b9ad79fc11357b4b928fefc71dc51f519bac500ee9e2cda1bf26d3d55af2f83764da0d01784eb82a5ded18a2646aae48f7ce184dcee9eaf0d058077b4b6f4492fde2219ee7e251555595232ff704e8bc65a15e6f5b1c4c6d9eb0527a21127ecf5007493aae7fe80c21f3c3e28a75db884e3a876001e0a9f897aafa3d70ef21b4014f55489c133e9a9b0f461624b5c3cad607e1d5906f0dace61989b86f5ede4b3ceb44fef6e7a3ca4c3ee4b4944f74201ca7246b53a6dea3741c2ab90195f114fd3128b9d26b710744400c97ac8d1a960928023056a78a2b0926bf225918967d1dc24fe77c44b5bc4119c1ba2fae008dd7cc233cfbc52d097f64dc4403da5921ada70d4e36863fd059be641290848b64dd2cd0244b330a2d52cb18905228a1ec4a4aec48b987b4bb3ec3c17ec4654496b36492011849eefde0daf40ca5c5e1db438ee547ef7687dc6c205b28965ad72cacd77286eb482ab86a3029ce3afd554603520d47215043baf9ec076fd53e29d1540079549fbb17a880635cb76dfa4b23f9256b88ef875c236cdfbe3d23633720b7f2238f064b78cd98c7e9364e112c8a40c7c5afe2eeccd9633bdde3acba94073839c8dffb463ced20fd4cf2015e91b3d541d6672e7bc09946fce06b22be5f8ee143d7373b2ea6243e14564b1e78ae2962a552a3332e54e69309f131bac5a26c95dce6ac01a3f84d3b412b69a87ea813b6d3fc55b1743b1b91120aa070765440b23bf7e453a6a4067670e2bd0a628ceccf2f1340c55c8dc0959a25b4301065df9b96be0befd84501b020679b6455c774474a71ffe8bb9dda481037e5f90354e09ecade1774eadf171a2e5895debe206da3069d7291ddde45587d172407ab893931fb19b8d18ed7f4af9242ad9bebfa30b7fcbc57d03091ff405dcacee28a8f8825fb60dda58fba19042a2020b12f67f45ef00636488a1fa3b3bb53fa562a0596838d6f7ca4204bfae4a773b88619ac94f1e59b2f0b14caf54959392a0e8931c5f8fbccb33d49d3764a65e982ead2a79e41961d220e621e6564931cbb43181a3a85a96c31652643b27a2823628d37b1fc0ef8f190c0c737d478139e549ecb98d0a6f8d488432fe7e779502dba46fc2e6fa5be6abf5ca0be099b508aac3f5bf8f0cca1cb33e11d446fb6fa3242e1d8c794efd8a09f0a8aa47bb005babdc472f7a6b3fe8e2c93fe33b14ab1a687b989b2ccdf8368e25c1a2826ebd6a4cc4b69357e6874a1463559d46d3c2c039bd598fd872f06d0e2da6337808dd3117a7df3c052a3a3c8b86683a2d1602d6cb33fe97005d63cf506b4ecfdb9aeae9848272f98fc3e8f3a9116028369dcf921e2b126f9390a2674d448a7da881a01ae08cb404609f6e228637cc6ff7fd9cfd5ab34b505060e110ba1be6e327d8b8c20efb34604abfd629e17d62838290b28f920516549549d716b529709b21f8ca5b618e22780621fad0b20efd71091232170f4c77918f558b381a0ec335675a524705a769b236eb535be04d8b268393a8b81787ecf2a10ca9a5d41f6519dc043296a1cf3f6f5d6030738eca0268911bce42bb86718cabf2999579ea9cfa0fb08d208d69888b226911b346b51da7709ab2bb5122bdce2508e720c8c0a952ab14c0b7e94c7462eb982306c6c69a4c990efb459b3700d399e09577749644c318e5b71c7bbcc7322086fe747210d49a67f695adc13cb1f881047da5bb03aa15f49f83eb8e83bc14d506a58b1703f268be028b6fa6bbeffdeab2a1f5cbcbba2e0d1a80033957db69ab370b38e70f5bbab1347d7bf2a5a44a3546aac590fd0432fe4d35727a02d114dec9ace0dbca7e15a042e118fd33957de2612ac80967c1094dc261f65f1c645fd9177cc324fd3896eaf4e92147bd502d6c55bd2375e66b775295b5661eb856652d6b054b246dc0a89d4bacd519405ae8dd1b62642f2c711571112f202be04841e72b5ae130304c91cd4813cd7ac5fdf608bbbcbe6762bdd63e4de1861b269e3d8097af085b538e3382eab95782f5a088c60538b6cb7e0746293b055992d09ac159514a6917879a510d9c5b70bfc477b5093d3ebe1c739995a8e8ce4e2efe257321e6035f8eb5863db0ad571c39f048f977733f83a23389b6ca94f1f9e74bd47a593c5f1fc5d27418659dea9046af87b1735c8a3a04c2d6908446af3cb551447f30e2a9de29b063a781f0f1020ffe66637e6704d56f87f501091cc244b230e2778a09244ce0228d4c409a038ce39b15b0d0d2177cd7902accfdb24dda3c0871bd4da87a48ec3ba48a9b19777cc3d7db4cf136b3a84c999a131fcb0a4cce3142ccffc6a71c81ec0826bdc04b44d21aa707e4105a1f2444c5c884482a8f77f264b6dd5ee20136e0a1e98769fef1f9c70bd92ff1c0dcdae0181e1a169d37ec0e02482f6bca60bfba193622cdd1839c19322f0eb843a8ddde102a071adff04e5ad8b843b3cacb1aaace723f4e483ca283c411fe85b60ac1e72ba8662b17d9b312108675fc3d2ce29c8a3648b6e990bb4e08f0b52b33bf0287e6f04dad9e55490e2df712aa7110107f14cdacc2798dd5c9d17a1971817f63b977d792c9b09cd486fb0cd1d190ee880b540c062232301c6bfdc720ff3e61c24ea8f5db51eadc5cdb5bacce8b6f4296615df03a9f298d94d89828d224640257954042fd10f7c5c1b8ffe1802a13f4cae88b8951689677312934b40b65406d8469ca0b74438b619db0271b43efbb7ea0d90dd42c8f53bb37216fd08fc043863ae5392ade1e96feade194a75d0330d0e9be91916ad7599b0515cd165061d7b4d1f3a7e5b4dab5bfea125786f4e98cd6e0826e0e17b5978a43408060387d8829bb99835ffb015fc32c57f92d14cee5e3148d5b0ee2acbc8b2d4e31af1b40d042dd6d8b0b5b43a74cd319befc65db60e33d0bdd3369de9a558b06d76496a6d0c12cc7704626a0557430089b98fcff721f23c326f68b988083af6f390f72c13bd02b12f9a1cfc210a92761a6f7525aa80976a5b7d4b569056e40b38d844172c29eb54f5fec6eb594e0661345b59d0aa5e401ce74d480d7fee2327d529c9eb7275fa8515a038408d624e3b7aba150c449200c3d2aa19ddaf31a52312515c8ff2c08f2fcbd717543d393c580348c44d8df39b94e9253cce69fe08c28a4b0d273dffc2604e89458ca8920efe03d542e00b3ea3f8cf51dae30c6a4107b876a2791bdde5be58febd9ad6d9a880f5cdd742327effb16126a5742b0a200a2c7d340dd34601780070a26a3bc148c9ad1bd4c763c8d64ec07bc9c3c10401245bfec1ffcf585d7ed0788e4c021c15152845d8e7d754b6fe6be07ef6b6fb0c1ba634bb2fd445e612f28174ebf7f3a1c0e9324fb95eb8daef004149cd0318e7658736f2f060d5d9b3e34369d496af6769401cce11d4f211c8d37d3ac4640a6b400945d774b985a5d7515661a0ba26d28e16d0a5cd3cf38ddf5146968cd264a09027c233bb5f8984f8154d7283e3e0e6207b9ad05fd0fe0d2d2a8162a70171d8170ac69e313c0f55358556cef5c2c902f4d764b28773824fffb8c397821f3c73a590475a1d2a915246d63db90c7e7d70df5f291af46c43d3ed22ca99c8aef268e31524b8a19b098c10f9b5a798cb77292546c3fc703fc1101eac74efcf81fe4cbb3ff4311ea62043102076da305ef7a0be91afd8af8f420e0110dec8bbe1642aa78f93933dc3c3e77752fcb823c80dbc2c6aaadfb2398fabd8b0f24caa16f1cf5bff46b7519ccffb3488b5f91c95b56f50ce04c213feb3eb65dfa35aeafaa1e5a96c5b5ad2847bbba24ba8c7383637d86773333066d08863ef3112cb62e99f93a99691f740ec2287dbe90f88fa0dfcfc3fb6bd238c84d13179a4a39e90e7a809307d02786867125095972ac1939a3de1b17f3c6284f7b6019d5cf4c2febc8d260a5c3e8ed210205812c982fa2dc5ccaaafa67f06ce6e6085cf9bb1c9dccb52c941157d10ea0263c6d8e77a8e70015084ec6ebf3f614ccbe359e4c1524b48e19762916b6244e564554c1d7cec947e0bfc88fdb41f27102989c6783819f9e1e1697ee4ec422eaf727f8c77554e19b615b7cbb71f2cffa8ba16fb91d993030bc3e6e948133faad48788369a9858663ace6a334e8ac970904be025529312874a6582d7617ef55ad6dff120f0e6bbe90c34b84964009e146df0716fd64a21d524c72c918c7d71df5a6c7e405b24eb61869a3b8ff4fd289c9185bba0cdc62196249ee90fb95d2cea863a34968cd59dbd3289588824ecf7364bf19d400e50410f1b6f326d4403f1eb72da43744384f412c8e5e2ec9009fbad8109e0494e73fa03e1d611f11e54bdd0e7043ca9318166c7b1b5c4f6e3509baf9bc40f10297599e4fe53b98dc8acf2f49d2d2ffdd6510963a29600a4e22c329240cc130c952c0a92fb769130d64f403bad4b90a65a72a2f87fa247c969cd45b42cec86424cc5f3d1584b030f0415cbceba8e513bbbed3d9e07af6475671dd348273f4582306a18feee53db3cc3b019c232c0b66edf4bc7d02be2777577e2f9e11028ac24c76b5c3249589f68b80084ec363195c0420e34009feb088594f6d20a9bce2722380c84f55ca7b6e02222736a8a8134298e7cdc15c91bc2d641fdd7b209b0c2217bf593dafa39196c87b5d0e8a8918cf26fb50b9837e7912f350e427f739b7df9470f693f83367c837375898312d2efb80b4b04623db9429671e835f3ff9b691c569d80620ba0392c536ea075932effe58daa647b809804e358e582d8f82700b667781eca6780a421d9d21480ef6d3b2f9ddc5935bf64da404babddf985afbb5fa59362855cc9f8e35e4b5a6a8ff4bc3335ed2ea76b7149258513ad6dccc5b7cf11a6e0367e595bef8e24b1dfb5aa75816da4b7df29472f08479c02ab19e066b31935ce121152ac9d729b9d585ea05d2ce8b159c54c05d922bf4c5bcd82b491d103bc3cdaabdd42ab98bfe29543b0b13140b43094f6b7a6ba8d558c9e474f18650379e9e79535186e5d0643abcd7075f5340b996d98f573ca22866edff3cb95960eefa6ad06924082133c1f1d80b0ec7a7f03130c5f29db2583f99ff1deb84d912047d59a3acb532a609c05c226ba3d13f9d463087be0a6183b198fa713e76c217e3a3b30b6a3f95dfdefd05d7b0f029e3abd618012015b7da247896a413856dc95ac12e1d68c14b28a9d1db040fa19e69ed3ab5c3a57e062575d1e7810c9c4751573601363682344ad2d9f99ff302e89af92f5b579ccc5829f882d2debe6d7cfd031204c7599928a01aa5fdf484ee69237f3d78768cbbc449eb1aec8bf698858c53e9245a8f6a410ba2f2ea5fb74a3b2d1ad8eb05d913fbb3a7c7c0398e392f705d79048eca7e68bce27d4092276776cf7fa0e757adc5f46b89d82f6e70a8f71c9e1cc57e76d1a92d4d8ab4b2067f638093c1478a10ef8c1ffb2ec75d49744ffd6ece20a802027e4050da8ca0450f0d4ba26fc7805e0e9fe78403592f0245e6e9e912ae983ef937945ad9e3f707358cbba86f4894353c5ccdb244f83f0c185af88b76cbc298136b7e4b32042ee278aea4e543c79fabf244fe100105b1c8b39fd60cdfd02b16f3a47475ba8be39fff9ec1708ca57fd81989550783c46fd370ffb5681893c68470964beeb8cefe81d80771ff4db252634856d6ea945905923e8f50fc900f70f6786b215f8293b84d95207ba02bfccccec13cd1c7bc184c99c54079aa9ef445e70dcea6d160fd41f2642ac6a92adfe9c40a01468f92189a47a53d0fc2085a106315495a34b09e92aabb650a48bab7b6883873197a4b5e1aedc0a966296b048a1b74312367d29eccfb156b2764b777fcd40f010615a0aee394b65e0bb4fd85b6b49e659788346bee599579e686e9c58e53393210ba610f66a9056aa430ad3384aa5d593b3bf3598d912f8fa6dd5f844903958f5add4261e6753074650b5a14170a87a838d5f966ca27c29c7f4b55fe7466744dd1724daf9219507d088ad31a0dd31adbbea6d4912fa1085dabdff1fe0244880413b8426e474b1e219cce5e5600de4013ee54d1497c7d5acd018c129739093bf11344b13e9db72ca4425d1d3779efd8a094c4c1a310a6f3020b3a6d119dd7d8a110067d30d0b33248f7b6f2291c14e9faa8065170422b32838090886a913c8f5d30f76aef6c4b4071c612d158aab5ba16181fb5c41fbce29fa679def7794de1e26b3201b36c73e80c56317dd8b6d150f628642ddf8c0e1fa21a8cf95b893ae86c4283a11ac4d9d94d90a36de22e97ccbe32ababe04f7b2ae1adfab42815b5c33b2c1a4ffd9d159fdc7323c298e53076048d52ef22395d890e375083045276e433169c3674664b4cb2b86c3bb0414397f23e6f8817ef84c773109ff4cdd3534d16a6b1cdc576a4a1929c4ab7cb5a391395ca60c69498eee965c54caf5787c65981db86891d178dba6ca184682292c043d4fb44ab21259c68812f52ec514a39fd7bc933ceb9a4bb7067c05861a58b68c9781de603d4653fd065bffb0ff2ec96b053f725ff5d4f7bea2bfe4bf124c34a43b9214ae589cc7ca88c5195e7c51eac7ac76d24388966a702994bcc6bcc1b0b052521444e0e455f4fadf2742945529c79cc78b724d5cec788ddd15e11f7179a260a3ac0abee3324446ce6e2e4854d413a25b295af81406ec4e6a458bf19bfe74ecbf971ce1f9dcbf70acc9b803bc1ea30251eb05efa9c38df30851d767e679bcb7c64e185fb79196eb9dd4151114fec59df37a0b6bce9a6b6ce9a0aa37a19e8ed2167efc4df6440e3e5681a8797090d250ce2792294be4693e3b8163334857e7cf0d4c72014cc6c4ad16ee40a32969dc0ea8a317f21145abf1c4383fc1bdb293b62d816fd0b163d90664ca8303ce14e1dfc31452527f00cad8ee1e662eb37ffc57252fc619ae1b0aff566ea49e05bb82416fc4dcf4a4e6e0cfaa1d712aeeb8521ef730871db57f770cdc4a2d7ebade0440aacf4e2844c3f5cd2893d4820b0f24e3c50d2766943bec8e3170f028c7c637c323d1b52e365a2e32ee563eb4f2e20b01d1a865da4c875ca8bc0c4cd4cea84abd365fa67934218bd1d44f40e681d5f55b903d1ab8e2e40c6e9104a409acbf2e02f28ab60bf391154c612278d0c7cde133b0bae0a9174659993243dc382325a30f7e4c0ae6ff3dbefbf27a9075adfa9dbc09610dd851bd6d5958ad70e148058b7bd76443f948234b685d75f97a9be2ea11ab6f01b819e2507a0ee9862a85a694bdc2af95be8436ec62cef72ad3bafb341f85e6585648421f99fde9018771dc13cc9f71c2531ec0525098317b00658b54d725c1a43c6b2e8684b31892ce566091d0fdcccc50f0993fba2c00fc90c5af2696eca80fd32f05e6ef2e7c4ccb0a86880aca9098d19026668d16a7421fe444bade76339739105c74b71539394d02566d538206872c33e9064725b8969a212487ca963ec029a7b1058c5847089ccccc1e1c50c1a68c9c18973405391062d4eb713e26c56777528debe8500986149b243383b4c2c1f0b6128a1169bb0375e71f7de51b637a9213c670312befee9f4c51584ff0c29903b673fdc00baa9d877374e3546ad001c43a111643345efe404f4a3d690d833da1ed564cfc3dba3ae6e2be00ebf36a4401012b447236ca4771a9296daa368547c3d0a1649e3a3b64382b1d19cf2d4885071a63eaad4c15896a189f516bde265fab55a2b763f0a5a6394715058c11c9a603ef6b9d7e8f9b3ecf928e77e147812f051803133d1fe93af97e033fd5ef3a0eee45901acfa64c8883741481cbfb7ace30b8c70f9f38b4ffe28a7f9e3f1717d518432bda2524c422ff76ec885bdde848566bdc54c669c5f6c78851e5959ab3f31affb854da78ea320770a04d8368af42fa2e3bfd5154596cd0157c76af2eaa0fd9356fec3091f665c087f42b0df9fec75a959a8f8a7053cd8d342115d373a4e63f071321784680a1e6bb09b220a8fbd3a4d6d9d87251bcb32deb9961ff1742ac640521c1af81d7d8822e3415e2088a24dfa67e0f2fb92b10ac47d6973c7fccb592d41770b05a3b410aa632450e6be561fb5834fd7902694565ac4961febc8f57e221b6b6fb5539dd91ea3cb011d695365994380f2e539dc771f56cc17ab5cffb6f92a6abfc410feab997175010e3d2addf4a89e13e801827ccb4e3f42475ec4ac4995a19ebd6ab016c8c6169a331a2ae3090ca2d211a16fdc8ad3ff8feb8491ecb952111064a04b992aa356e7c83dc8766aeed7a9aa87443b838e3086f012867bdc862976b752220382ccf9d3ff777e33be0c359fd1b39d9336f7d08e339994c4349657ab303686e34dc082c70fd2fbeebe99b10f6cc4f689c10b8e13219dc144fe4dd14a4ce23baf3d19f0c13548e987e105653bb1ee779730816af83039c5af49b296767e5e97a7a06824e3ea6f76fef834b2103fac8b7f6853b48d1bd7f8f6d73a073e0ebd224d9fafa2b698f41cc4b018ef2c806500399ce1b5a4fca281aa7be940a819b19cfa6c21c266fcf2e44ba9f651cb76e7e83300b90a3d360613533a8f525752b78503201695d0c285973337694a5bafd748ca0eca225d7ffdaed0a4ee3df1fae90234f5505244bf368c34508d9723a4b8fa96ba8bf1f1ea5c69a5f15ff75d0f5ae24dcdb0bb0b7d0fb024570041f822e01065cd5a7c15420b83b37f021fb3017919e2677fcd566767889ab0d01b8a24778e9911e9a24040d5a602181a37dfa23152a7e3799aa34f8e2906ddfc15e60f7ec707e3040d91f3a0d450afe8773bc59ed61e4e91d25242c711279c67ecec7d9a645233826e40d7d5ba8dbb43d0909544fac6770adea8008fd0acdcea6e54d0ede2f964361dd8f7f5a9ef4eead1c1a5160c891595a4f0d3565151ccc0a43be51f4440a313859b75d72c285e0d4b3212ccf52ef0c5b974efc1b6acf3910d742e34440dde5077fba0f901825766ef5e75108bb6209c10c22f02aac08daf68d4856aae4f7aea24c6a7ffa9a18e1e3fada5a369ba64be505b075f9dec3ef0fe0fd4cd1d6e6c4dde5a99aaa01d23633162caa75da35f17b00904f1bdb78930692819a6ab183bc7509f442c5d070eacf9ea4056c43efb33896344dd9358e28d89383d39499735c606b9ee5e6a7a5d475ea9d9bf2047e8589757705c9a1c640f22b01aa3da8deef77733a1cb6c3c233ee1b59d6bd3b0a8f9ef8e0393b480ed5d4207735c4e3d7f781b0daac1816306a916ac9feb500264053a5e099960d575cc82c64c9c89bc8653cc7b5af9146b9ce3af58f6af33ce220860fd0a6246ca3cd56120d84fdcfa8b60bfead927f1bfdf5df00f1a8d7aa2863d89bbd9d631ae0352d1a31acd01d18d0291f6d740ac455857e850f9b15f416cea59e527bd7a28ac8313347e53ba3f620b1ccab77c9bc04547f6960ce6b43c1c3160e7203d627f99810a371ca00806f95250f63d378191e190abfdd0f912f41ce8fd59b30f828f9c924477be2ab5555e5e0799ccfd3b4d74cbf4e0a68114994fae4d0190052ae81f6913382c1bda6bfe6cee105426e8c465dbc5119e078aee86c22090ca04401a9b81a717f683a9e63eee92527fc4db675522360465c5a40cd1443ff3d7fc0ed07a2b56f220ee51b6ec962d5caff238e47605caa71cffc3efea7d275e70d613ceb4b304002b8e6b4c137718b284c8c4ac9bbb1c9d3f43917f22d08a3dab50213c14d718497359b286d0deab066c4d373c4b8cafa3f0e28bb80551bfcb021dd1323a6b9d6c71cde601a10f9ee9afa64091c9aefae8a961c0e79a9f5bab2b95be5a075bae926ba33244ec887658d09470d0ae4b795784a497b78d477fc20394e35ccc0ba04251787c683d08fe7423d8505653dfb1d66098f5d844fc3fdc4c8a3965741c13b8409bec829c610880d2de21bf3a00149217cd9b0154d7b8b57c68817a56bb73b8e4708387083be92a9af46ddc8a66aa733e61fbc4d1946d575b83cb8c038721189d2946673653006c0c6d28298ce92ddf23ad4a0a300ea052adad7f02843e17281c6ee8e112a56b4fe0cb6ad39f83f1a6c36bd060689c05d80cb40daac9fff90c504d19b2b8ed6000c647e21717dc4ae765d697e6470666d1a4546299e43874554adfd3efe89a00c4d4bcc76aade84d590a8e5aa626c0e5c21fefddc8bd1f8e556e9c76376716286362ae2e4e4634e012f6a2787d0d435fcc9cd56d7fd3f117fdc046d007b7c3ee6fbe5b7474a4ab797f34c7739f01a0571e66fef3891c77bdf1ee67adb6639a3c5bc8840ee83186dfa04b82673c4bda1ae69917f17d3a98183078080b9ce221b408726df0b5a0000af55301f897ec072a11a057263ac35c741051ccc108488ac6baea7d3d9d132f5b4a018a7f7bb71e7edc23859590e256388b0cbac28919981aa00a0a55bb1db3d4e02f3a921153a3d0db7338cc3fbff94ed85f9f04a6a926df267893a968c2207940831f7cfea054bd26ed64e64da88c07ef455b68381d9dfcefc59f1ddfe8bb7f952288c48e68e5d4811af6a8ba88ca3c83f476bccbccd0aee4510a9aab13de8aa5524006dfbb7f6a067efbd927a6ec566a3f7659fed19f33ca26526c94e5b88aee4bf9eb18fadeab51c01f82657d7908a97d8022916feec8c394e5ee9cb103da773f218bcaeee0bc704aac385b45530961b6557bcfc27bc0260675f73e15b96051c05233abd97c2f97a84eba0f117b404b6ee3bb5a333a233c0550e2ada9094d99fb00cdd740aaa543f94c5a85057842d2f22f0f3395b117abcb96e4182720e02a247c199e6a2c2094617613d378a473c567a84b0ef3f555bfca2620a821f06e243f447a1e205db764ce29b18a6102f3890c064ed3480042c4ea0bcfee8046fdef0a1219586657584b59d87db9feeb8a9ba39aecd0ec8aaf1abfba466272620f648c6eeb147d2311fda6f8a4e18270249369f270a4799000b1005d8958ab7fb4722d874105d0a3d797094d1f57a81a80624514db3012d1eb157a56764217a3ccbf2b91217b6c2181b0826f4b0775080fb50bd64091d8daeeed24ec284b48842b67883d3c8872334ca6d5a650986e1253ad6f2f70214db39fb14c55ebd7f67781cb58c388dd3300bd87f0bf4eafa70b9fc97d6de033f577d31a4710d2cd997af7cfa33cdacdf9e2a9c532003fdded5a790709ac2144d462161a1c7a03cc7c4576e16157cd864063570eb68f47938440d1b68053ffc689c890a7402d87616715648251aab294a69515898125e84ac3bb841a8ed0fec23b94304b7e3e2aba3dedbe7a1bc5054ba9be3dc0520dbe3f71caedeae023e2bc5446cce18a21fb42cbd322199deb982c726e4e99bc720ab414b3388a489b060207239975df86d1b7f43cf9a376a56703c75d6b11ec3c217e0695ce97ffe5259d31621fb6d490b69767029f5e695268d175a2a32e4025d2296758fd6ed4360b72ded173a4ce9861a3ecdde86f85ecd7b04a0baea9a841f85d144297cf0919f132473efa1b4372ab6ea61aac046b70e7bfb0142e33b4817f5a55cf63da55762f0822dfbac5eb090abf5b246441096ecfe21297dcceed6c23f86f8ce698b846854f28448cd0a26b6ba066e504985f447b1d2239d4619834239536d4c514b9d2799a8602d52aa0f09cb554c688c73df83bc5b53c904eee18fb4ad45c64171ca6ae2148930e742808d2cc22f108d4480bccb972bfcdcbb8203d026c00e46bf12ea9dc82298f36810c84ef7c130aa8749d84e857dca096413651ab60095525f32f957e8eef9f3301327cd0bef2c20ec7557116790870564adb48653c5d8883191947648a5308f1240b0376980ae390d7b5fac2097018231ac0699104b371f9b4545daacada7e457aacc57cc35b263bb38e1905fc6952fcfdc9b35c3a67bc04b12995709b34d061f161f380927a3bc66583e4b5e9650a1c286d23a95d9f0394d244715d613d1a7888c1050ea0cc06d780b2fd1f922aa3951512a93b26a811542257c228216cd6546ddb988f8dcf9e9ed5e49e6dd49227e6b0c8aad28fb4babfb28e5a68be17ab7b31dce24e8f898f32b22732832b1d0db6a22da6a24b0ab36597681a3c7aa038695b4b2505f100a44fba77c8aaec84344cf10c1ac1352352aaa21f99c2a5536b4b200c6a5ee0ece5738e4292b884741ac8c9301d32d1b1af4b4594294a624e5b659906b09d9ac279e9047f866ddcbcb98268677e9d19aefeb2a865bca9f915ae9c231b8a65af5ec78ea78f95b94d2eca456c033bd82a49f49beb1f61c6e5adf541867dd374758c65377e0050c760a67b561dd2c6cde541878c8250b453d40108439c7c4007776b0bcf4296596c821ed44fa750d9947509b8b849ebddb900c5d9ac1621745ad959933f891523bfdd4a03e3c2facd8f7ee2a2a173ebef761429d3802497e4faae632c870ac6c9669da139b6c8cae71656f073da5411e1b2a76c8da261ac7c1d99d14201d5c872b5a2e18362dbb2603001eb305949e24a2d05347a83cf986df90eb8cfe0b7c372016f6de3b452bef950ec609aba810807497f4b6db517458e1678dc85d93dbec7203c1a918a36d1c861042577f3c10491dd488d7e8bb112b0d4880c08ab2d04b47e8000d13259d48092bffe8817148bed613e6e58190dc1742f0f11eeabc2d2dc9262c5e8b66992d1045755a009f80c03aa5d466c5b83594ed5cb04a03d82fa98557c9c3d36f7c98e08a727ee48731bbe43b3863f860f8046647f6db88c0b0be971df18eb6ad08bf6a103ea03ed0c3b2ecd62d2c0508a680c8e783c13dd499d768725c7816e35e0f8d06e962958cad8041f67d88a1c4a3fa13725aad14781bacffd2ccff5b3a4cd7927a11ef9647a0ea727e2a62f69d341adc9d34ef3f110124a80b9c0d198019a9a48518a018d12c1c309b40c209841b7263d067799faf9608c3784d0d1c970cc15186311213ac40e4c84c6e182eb51569f3cc7170e9d2aa3542374962d27bc740849b7d75eec52a7e1f3208d4396ecff45f11db378cca4139d36d2162c089e33c832bf86d76421b244bcb18968305c52a0dd8877d765cf95f95b094400eb6086389b3bf964d94292f09bc64c2b3c8fbd2e2ad97c4e9bc0ff08c3e78257009fac80103de544e55f36a9441bd27d735ac73eb4ef895321ce8661d561c9cbd1640b0380b1606404b72530c809ea89363489cb4ffb930bf2291e7a084ee7066f6b832a157ec56f18c26f822aaf38a1a286c433af8f88ca5c09e9b83bf0183b708692d55d34bc5a8865e251eab1b04ae4859e788ea61d36d7e232bceda03293e7e47e7659180c5b443a8e053cd698735b461d96bc91d9ba7eea37f7838b715137e144ddcbf23070ccdacef8289260d5e47a82b22a32974b0133381048c3efa249a3d19d49d2c5e56d421afacfb2688222601a8fb84456ed93ac07028d893c6f628afdcda9ccaca7746b81c04bcadb5fd58158f53e6f130f80f6cf094547305318f6c1f8b8475f2f7af773229698771c3b01794cc5198340b3130c5e41deaee2a3a3d4d5e8553e265828669cd869c10082a9832f00424631875700fe8b3bf830f04453bde1f6e49dbb1b665b19fb62ccb327a21f05975caa01e09e63b7b937007499d126a888d4008e700bebd751f2615ed574e8fddf849b9aba1d02cab812f9106546e3ec950af3bb5bb8f21aa5d72f7c232dac4df0cbd1acdb99147390e19b8a148bded0c51e52050e853bbd5ff07c53aefdeef8278fda0670d722ee29d137c600046be5686aced5035c32a1c4626383c8ba5ea85af50741ac6a1e91e9d875097f83ec971403215a2b6d2b1f73d07973273af966f6b508dfc12b47d36fcf644940a40635402e0bd257bec83e8579ac95290bf536dc6477ae163a865340daed0d7b5e9cc1efc65e9fd0af1a18ae27a2e6eb8eeec4506fe0e6f56ceee403557e5e0a9e56aae004eac9d23aba65f67f9d3e2be5afaff634b2e195fc62d7e4e5e4003a1ab16bf075024097bbd8aff99d0aa3f78fa1f58c38b1b4fae1b982d20b04a509fdb191330ca02bac4da9c381e452b71edeea61d8953849fefb937b727b850a4d52cf5145f6f9c33ff107cc4f9ca775ed2cf02a56981919ecd8c48b5f2d8af3b04ad38878e9962ef0c0e226bf241d3fcc2e2d36c487ff6d29a8263819ae7066ed29aab8b2219d1c09965d5654036d59e1d14d5e95d691983bf6f5e825382cdbbe23689b98ac6bb02e36e88c6273b18e1b5db91561b5c6f43684bea00c620ceef8c4f059267dd62eff236d43effa562f1cf9dfe6ed70aba918e9dc9f6e6459817ed2fa36d4048857ebff596ae339410427bd5c18e376942375d413127f43f74afb1669166e7350b65139e0d0ee3c9a5d91189400e193787490dd257cb9c8285e01a4145bd6df026f4f7e8faa6d0920de89da199a6858af7091be71345dfcb6e3067831560f6e9d6f210448637a046d4aaa7092a09afcc440580fb66803990b511211088bf26584ad6354e0ace8b60835eb58882dc78fc1c5d6753feaa23fb2ebb2cb53c34d042abbeebe748898fb3365d836e1c5195d5a32a8f7668e8c110232fe081fbce6e343bfd114a406b380c904c1ec05866d738dbea29a959b1b6adc9cf580b17100fe4d5f7fab7bbdb37eb95506f026d806c19c9e03b4c852126547e009132809a1917063568ecbacd750d8ddc488c75843b773c48497c22274c290260a50de1328e889458af15e521058c8f4f4c456ba64ee0f9e50ff910cbedba0df7858af009bed4a5d25b1627cec6ff4bf0e20d1b58fb711536d23670174d725ca0143936378cccaa516b58c59fa8311ff9a0d83694df1110f66c6949d5da0e57d000c7caa740e94dac7a04259d837c67c91cdf3c067119276c6d2a559184567f61f8e2d633bab659549d982d08d4e1aeda91f38fe610c61c0f89b3c6ef7b02d03bfbaa89b23f475fc5739f4a3ac3da3a876d731debef1b5b1f1b06f6c7d36a547b97854a65e78c616abe672519d34e2013219a3f52c96d609de3e424a66964fbd8c7702e12a38c19080a05d971dec52bad74ce51d3f8180104c9a353f66c2cb59d50a5c9f91f36900d8a834bd4f6a55ea60f738d5583239c676e57ce51d24de97ae4fdce94351b2c04a9daa4cd434706bbb10d75bd0acca207c8fed18adfd29cc845756d2305ffd91289a25a5e9b58b0497387d207fa71800092cd73c2c5ffb57c53efa7d8bb0407abd132300f44d445caea5cd328af5cfb9044143ae0d66ef9f9e36168fdc4f83c7fdfe00a338c080b043f492284c459f9ec35d8d8617d9af5e90173eb68f4c0daa48cdcde2522464063dac72d403adcc0bd3bf1150b382aa7c7cd3320e5d09ca00805774be3a281cfe87e757255f0f7addcfcac08098f1a0ab87d409a7f1529b30acd28526475d426c788826542b26c844ecdd80bed260111af3637a035ca5b1a6af3c3f37976f347ed6d862b18d7317058360358ffb8ac0afee326a9d2b83bb0a3a648b95832fabaaba5ff3a3951bfc28dde8e06fece95891612656e9ab3e63ca9b4e6740802bb8448f3d6a4c2d30e3e4d9d98e238957f3103eca5b25bcc072d778f3dbf3865bb90de040f59a47097aaa53a77798a42c41f1df93228b5c9a874ec9e495b08249463712e6e2d55e6a205d75efdca59a2adacb03c39583474fcb92ab9a86ab23ecd38b48e58442a8f22a56d1e1fe3dba046faf01b7325262a116b053bef056cdd93dec93ccc90a437785a04b07e241b6ba2cbd99a0e44e85e6d5554a7a98ae5bbac1d68eb075283a164755296d4d3a394336481c1849684daeaa63f3dd7449e0b9e994629a3644e86bd5319d38deb5744d049ad14b4b05802a616ba685b36d448b0a06df1e40c62e266f6eae709092f419a30fd29e5580e55e0a187c0c97980bc0a00159a9d3fe0be2df0527ed10b10b00d7822d5dc959b096632da5f3c33c23ce8da2e9e9379e1635546616e5beb12525facaec7428263e3f6aac6415c3657b2a22e435d6c8cfc82d556f00711424602a6e8744c0eb6b1eb6c496d0a2b1702b2f009910c382c75f8905cd8514a078fd019250be4511815aefdcf3f00a3b6f2989e3976456dd278760013b3094870c6029bdc5c80abde209950b30ee9f68bb903e24cf4faf4f9935be8e1e243922be9c19bf9eb01f96314ff1d3ce634fee6402ddb78fc305d179801008e55f40f84edc761815b83e17803ea6414f5126c39156e1ca879c0423c7cada0e2b9053fbc22e44e1e1e8ce479b862a8bc7d386c8cb0268201f0172fa18988c8bb584bda9b7dd376d27835e4e08a3cee1ec6e05c76733816a0883f839126f6da0c0efc8ceb8712f503cb94de26161ebb26909ebe53640ca12c2b00a27957949f83b1765d3e5e55c42fd47e1e833e6a0f75d08dce7c4269ccf492a44149543088f230bdd42184accb41e54a99c2a7d8fa78c5fe60af7321ac91c42cc04aebe88ccff7f776069bdd6e3528916b6ccf84ac11167388525894e703938e808f7ef1a07efc0a498b8306303232656453b10b2c644afdd08ee1e0650b4fdc3a5e2633effdb1321ff7a2ec1d87d39ba5568f83486ee56dd8685947f94ce141e51c946d07f57d3a0dda7a17779d4c79935ab4117d9c0e9c82cd48b538c95586f0d0ff8b9f49582c7037d6097210976a444db5b371679a565042f68899d0ebb155b635f0de70b47391385ff06ce0792d12dfe8faa8315d25bb529687969bae88fcf859bb0482745203eff954aa3e8da58099d0f221735ec4cdb40b69a3b169499f40832849b13c6bbb09326b4383144683876dff5d4c732a9d15f0c82a4ac4121950e63ca2db25e2630c8eba40f3948caad2ec72eacc1ff387379a107ff9080ffb94875acf4d2bd3ae6ddc2ee0ab0a6f3baab926cba32b6e96958d5da607f37be3356ae2452476728210fd236048ae56c49ca1b1b0c196eee51cbc44780ab4390421a8aba0841c5f6586a8a8f62c833d0000b74e60cb750cd2c34d49ecab63788d8c76e924e5699bf19385247a90b697a56d28a9cc1a6692429a5d05f0f005bf94f16bfbafd5660f13e64f16f42233124d4a91cf76ff5f3140f898002b1bd0a8dce07fd4a6685b8e92175115b47c821666d48da24a4fec1d85e38b812f439e14d920501ad01b888fb578db6660767c4b608cb3325f0adc6a70d7c9bf916246f469c1f7e04933a8be81dd404acf128116641a6c89ba66ac996c09c48bff8e03d496433bb7afcfee7732274df423f7617add25c66df2eabfa363946a2278be363a23911d30cd90ddcadf9f4a00b48449eccf297c210e1582f1b16a2648305be7035237006be9fd04ce67317a28436203994cf21d09033dd436ee3ca12fa9d138e265b4d9c6ca58cc826dca12c13ffe82ad2effd4792e8f61ebf7f2c5b3dd436411ddbc1437a6f52be39cb14dfa00a1fe4b26354101eb1f63d8ac535bc0618023c596b32c090d8161338706870d001f4775f1e981030043b00ccc7f7e2054434ab139f27d403e5c9b3f0ad7e47ed4d3c260776aac26af09a76bce402a13e98f9a607f5a4c96615d8c2ce4d210423cacdf16b380cb10d873266b1911f1c5e23c5942dbcbd430b8454eb4db53f09edc94048552834d1d00321c1199da2c0de88d57d58ee9888e8cab1f281a425f1b0c662eec65d434207c33d07be19d57f6d5f5291188e76afd388341b14114a20117df276c18f8e4744417ad82ac29dc8166390598786bbd8a7f813f7f80b4504c143291be28c5a965fa01ddc389127e7f4943382f9001ca8d0a95ba48ce18ec01ab7ca3f02a953ab4da683706143a76a37e96fcffab537f37fe91276e28ef22ebf7d9ad36d2344d03165b2a0c93c005642e0693b107589b7e1c97537b6aec2dfa785924f99874c1312a91012b974ca9ac63d50b1dbe1b0c703eac964973ece42ee07efc9ed67fd004be60801d549ce1dabb081f1aa5176ee00e748753cb253170c37bdd4b22d705cb7022a06ae695b301612710e1923f4a663b3c8eaa737b373b371921373c9d69e7b54e0ae379bbac8f030643eb67c5b0068e32278681b0314633b6f79f81a38cc2fce9b2c0cc5e26115c96b39605d112b9140b5d55fe0bfa8acfdee6ea14d649252069b0663065d06e326daadfba25761b2163896f91bcb1cc6b6562bb6c562a15d1db4eb4d1909413776614a9cf9d90e400104a5c0d660e975b95c9b0b0673c15c34970d3430178c862324f11086a2835380a5f88b2ae31b88054b392a40d6ac46dbf6b99bbbaa772644162486b32f0764775bd06eee64b1ccb9b823b65a5872ae24775b71bfcd68b59e0f78fa3575c13304ddf5895d7990d8627f2230b42be84b900ad5357df9ed1252e9f4f5692a8cf8f573541894afafa3c29cac5ef2872b7be0b207ae9c2e397bb129c5940282fcd8866d31de267cc3435084273b38100211e5c51e789f9ef4f9fdf3246bed87f41bf7a7df7efcac4b0b81eeed9372dd23a83df0f6938e53e8abd761f2edfd9e46d358e6f08ea5b8f3287bd92c7b0bd1d66587581c6d49da02d116b800067406aa8b4b08b484eb62735c753b5055f76f0badf6860a326056f062dff7404984e57f9f056d92b7d9599779dfb07990d0625bab7bf8e646bb042b00ffcddda658910e6c306b36d87b7b663b5a2edae5b0bb6f34368c188dcc19e02ede009970a3c23098c5f612ba5cae168425e7e27238cee572752edbe25cd6e50ab3cb9a2b084bcef5a55d076eda5a6b6da72f59722ece456efab32b4802360b7b3b6fca76dc86732ce50846084bcef47fc9dccb2dc786f925ccd955202d2c3933e54c9990d990adc8ab0775cc9935ad01071e6f24212c39f3e34ef081619cae2da541c10961b9d1cac7ff59dc5ea4994e500873664186b95aa84b074d61e5d368341a8d46a3d168341a080561b9d1ae906dbffc58a89782ef2535a540488479971889b0db25f62f41ab1154957fb9edd0c6e3e806cec36eef295332aaeae2ef7fedaa05abf2779389c9f6afd6f3b6a7aa8bcf204c79c5e3b51e85bffbc3fbe63a737375b13ff2212c73bdd7e25069b32f0ed88a7daf48828a1548a1882454b10539811f5821c515c478028602f2be9804844395b1a01cae683ae5bc6531c2722b51a6ff5fbf0206638b71a01bdc8139876129c65cf592f2150edf62617d5759d10547bb30ee41b6ad95413d7ca4f08557bb307873ed6ddb1623c2bea90d0b61b9c53c9aafb657955b902f54da9c273db37dff1bc34b7b03831ffbdb971708f0ff560875662ff4c294f58b22df911d773623adc65fbe501f6b31c71ec047d34988bf640693842597f3c5907cb20b83bfac5c0e675eeeccc9590c5336e69b91f16da70f3a6711bed8133e6cf1840f694dd12b18e13baf308659f2c1c10fbe1a0e8480e3cdc0404d21f08011051e30749ac8c9deb2411660cc063d4db44c5a6992161339a45da01f0a04864f27803821756205297002482cfd88d01c10c9413a0577d6748a5cd144e72921a40970f68412563ca1c4907c854b6c06dd27a56209b304c74e2d7848b5a0410b3c1be64d2b76240c2aecf2250c2043989862a096143968be3573160391134128e244105829e824c535225908b58ef02226129948dc7c220b2aa04f6021e4892cb0b0f63f7c3176d379245d028f0b0c1ed71427d861ec932ecce562db7befad6ddb42127c5737f97f3c994722dbbf3ba8e37edb2e374260dbdc73e30d37b5b97b0d09246e8ae403244652d711244ca0be7495158d1a36528ffadaa5b47e1b7ac6d7d02b4f43b7bc8a4ef90b8aa1dba547e912e4d2f8cdd0e3afe819225074f216f49de636ca3bca97be9db4f2e3ebf77f5fb5fc8cf7924824fabc4522d1b7a271d0e86899a16b7015fe9206796cbb47dea707c11311283a01c1ee4b5fab0c68a613e991498f1b6a4cd083df5b1e9e112c9fa28d58097b649648ae54d08c09cbaf6814d432ca28a30c964721bdd6a97715c6b8ebbacfbbb3c10061b63943f0db20175b3dae3c7e1b56b4e82afca3d6d955312494aeebfef340339d773d6ea8e94aff7d4033a5aef35dd276d76eb4e1a6f08dee4b249188135d9108d589ffd9ae2bbd682a3de9bbae54ea3af14b263d026b8188dade60515bf3a6f0cd4d618c312ec9202cb38b83171483f8b67a0ca647d1767f780b95def42ffe57500cffa5b1561752e9f3a9fbb4a4451dd555279aff3f75237e71a7765949145d77298588b034da538cf086af56c64cb28c2bffe38564f96ff9198f5f87afc6c7bfc357fa31fe109c3cfe1e17a6f4f86fa821dd5816298441d0963ce93f07cd9044273ffa52e9c4ce9c8c2f35d57d49db4dd276b7e85c55ddcfd06155753f6ab1aabad7faabaafb914e55176aaafb120d434d751fea989aea5ea4115053dd87f48d9aea1ea4195053dd831a478d40ad05e89ea5fb15dde96e7b6329762618a4390bfb7979473a84659da11b7ffe782218fe7b9fb125c483b44e5c0304aec51702dbdec6d0f69acb62f3b81aa82eb8d35c6982cd8d3baa4b789ce1de1b22c48fc7cae3d48d34a8af3b0779fa9347f99bd2abbc505dfcadbe3100abf2fff4cd8f27ab254019dae54b014a99ed5f5faa8b3f0827ec6440bdf83a4e6ffa8e26e55137e5a617eb4d8927edc3f4dfa3b40f229ff791f2e0db2d3ecae7101f65ac2192bf26e5c5cf630e9457196b4c8f82a2c557d1288fd2e09ff4498694073f47caf81973987c639df727ead0a368d09fe8f04b5af423edbd892e794ecf50c99b9bf0437f237ad06ffa7b92aea4a82b89a22b79a22b59d29524694b6ebaeed18d0e3625e18b420fba01ff23610889cc12edefe97095ff8d1bf342b2e9aec88aafb6784184fe12937a498562b6b7e4013ec0f246b3715b8a0bae18c21148b79eb2a3ca0120bad845f8d285e8ff29e960291500c44d07d68d17c51f618c63d164600325cc0b52c177084b8e0552c1b6b1582c562cc68ab16aac1b74c45831568d63d164c1b91d1c4b0395093cd0d05cb08cb07561ca9c6b979af47cb5a5dcaab050b8b95632c2b2c6685908588b0d8bd43187a4db369a92512542dd6a7599659a9aa289c5625509b40b8323164ae1a68f86f03c1381a2129311a9844b25bfb654da66285b92ad8b7d5fdc68a6ed8b7d3f65ab9df0e9e4d79eb6590e4e909c5348eb841d9902967248b858caf5705f7063703d5cedc6e46a2d979021ae5611b4d5c93a241d169dac8b7997a49bddd4ed622ae075bcd94f4dddb78fadb5f625664375605b4c96a6699aa6e7799ee7b9cd7291bbcd66b3573e124e2122d9170bd0c663286e78e3ba344dd3349fe7799e9f6db6cdb619cb078240a190481496e09212bfb684b4379375e6b4b22c9c89b4afa57ab8f474d5799ee7a9b3a5b0d826034121519aa6691a9ee7799ee7beff262312a9543a394119e11896cd68b59e56abd56ab5b85aba99699aa6e9c6dace2d676b6dae11a9d56ab55aa593344dd35434a5a49c4e2894ca0a5e59f16b574c366eb930aa93c2b05a8fd9b5b81a8ed56ab51a96d16a3da69456abd56a9db81a57e36a5c8dab71b57d7f54d12535efc12666e173e39c2ed6c2ad56abd5c22e8c621d9c62d80a4babd56ab5340dd37499e933d3695dac964afd8b627776a7a3a1b9b7c84273b2698e5d2c77b1ae8b715d6ceb62b88bedfb2d99956f3290ccca66c6c96716ed94d357d9f434f562e8b0ed3197f98cb64488fa2a7f63193e97cd2da3d9dcf7696434a33bb46ff8ca730e43d1cf1a7efaf909b98a9f55a6fae9a79f7906da928c929b2321dcad796d3ebc715dfee48f3dd3da6dc572776f9dd933addd1657beec3ab3675adbb88db3678acb973ab327763cbbf7de34b47fefbd379683b8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf5eafd7ebf57abd5eafd7ebf57abd5eafd7cbfdf57abd5eeeafd7ebe55ee385fd85fde55ec3f10bfb8b468d570d1b5260014110fc6860df71eced38feec38de71bce378c7f18ee39dd7a3d16834b2b1e3d8db71bce378c7f18ee31dc73ba3288aa20dec3b8e3f3b8e771cef38de71bc3343454545658585e55feb71069e31c3af9d91da71bce378c7f18ee39d16adf53863464b0b0d1a356c601b36fc5a1b29ec3b8e771cef38dea191da71bce378a7460d1ba9944ad5420b007001bbe0825feb420afb8ee31d1ba99d940afc3c2084bdd77589ed6667f0d516eb78e37ce3bafcc19f8f5ffbe1f2c7fb400c827e2d58b31e080a89be10180a4b4c46244c22f9b5a41476640a588a2496d67abe18a3a7b6dd6c660d1c9937272c149c7d5f3c4dd864f26b4da64e91970e7a24459de649bc0ba7f9ccbff09acfce1c9c2039a790d6798f5c3cc5beef18e616c9bebf8162b01bc76eeae2f4a66e1061896f0ea9789ef7b9adebbae8d5b9692d85c5643347cd1b334dd334af6c46abf574288aa2286a9aa6699a2b2c25252525a4ad657bcccb3a7352584c364bd3344dd3f33ccff3f4197a5b288aa2ae8b5e9d9b5e58f6344dd3f43ccff344511445d11f0511d218a516269b9d699aa6e9799ee779faac36ebb9e665ddf3e674699aa6697a9ee7799e3ef399cf7ce6339fe9d309a5a2b2b2c2c2f21a6bedd76a137be69c96755974479aa6699a9eae3acff33c2d2c6665335a9aa6699a9ee7799e270bcb6b3d8e3366b4d0c03468f8b5344a76549a896bb55aeaf64cd3544cc36c5bd66551ab93a6699ae274c638a3a585068d1a356ca4702ae5d7a6c22dc784a908fb56cd55a9d4bf28d6322dac896fe650c4355cc3b5ad8671cd71ada505b45bcbdeaf21a4c6907ddf86eba6ace68153a018acd52ab405dc420b7e6d0b397472c0c706dbb3222c6b12456c7f061c91847dc3f3de7b5fdda82aff9d3a1e33111f783ff022a0c84dd52368e82b181a7da8fca7827db03c389ef05712fc4afe76edfd4014e53634a39d456332140b0de9a4f21f8a0eccb6d034062a02d241515011900e8a9e5650df4a252f28e5c1d7c1f4dfbfb6e4afe4133d3357032d26068370c20d55d1d5abab3ee25b57c639671e382c52657450571f98c24ecf91ed359c6440bdcaeb38fdca5b100dcb7fec0dcb8a4a6a59d6acd58506fc461ad4e88348f73e521e1c7d80dfe1223647074563dd3551027f2ccb6561513941a14e291f8a0928964c26d39b581ef5292cff8120f82c608ab6e4f54124c7e94d0fd23e4e6f620159b68d0359f0e733ce70cfc242029bed3f43256fc447f93c9ebaa761f4294f034a9f36167ddac6dfb6ff7ee56dc8efb37d3e59ac2181b6f18bb59f0c538f502167632eb66dfb18ecde4e3280cff239c06759196b54c6d3f6a34f79f0f398e37bd458731a4fdd28c3e853feb48d39469f72ca417ad393def49f0ebd49835ed4e1a368d197fc64979c3cb1e16eb8040ffc3c7ae43d025bc6defec793df932731db44f4524027a451a85402722007722017d2a60785f417124d27d924de10831bc771dcd6656ed315b002b0c0557901ca9a64db3a1ea73cd298b88d75b7ab0e3a5457e193bf714bf4e7473afc8e33e9387b7323fa1bd07f7f13fa1bf0bddf6ccc1deea8890d7741555cf1e545bc2fe2897a1bf274ef458d3529f664b9ab6b0e535fd9d39edb88efe8202ca67242a590bc128aa98596368d5d9c9c3bdaf3bcfef7c4e12bd48ffeb3e048e5514f52794fa350ffbd0aea49da92275f52413d96d1c000ec6a03b397d90b7772dc9923047d85e891d3554427e5acce8509d1b0488886a9af6ee809d39e30bda167479da1aa4c7eff17eacab6e07c39cf333c7253bef2d8f62f11b0f1e681f78c0aeab1f8277f83f2a5ff5434cba3f4ca9ff4e85334e97471a78c84ead8f47b96cff13dcb5843847b6ecce1fdca584352d1dfa3b4f7275dc9944a96684b964e342413fd79930e5fd4a247d1a13fd1a091893e92d372a13a292c76c2d2339d7177c4c52c5b51bc31e1487c31e5dd23bd97fc67752ccc02b12c2c66e073c0b2fc39f1b3e0d5a6173fc5f49e16bf4ffcef4d9ff829272fdc184002f0c5d88ead223d2fcbb22d3b04d5b137d4f4ecf0150ed80c47757110575fc4c77687ea926379b02cebc3f6b779b42c16f799fb3cce6cf9f3486332894f021baef4287f43fa93177df599f4f7a2f61e84133aabca844e329c3ee56938a58c27ee53be9234545775294fc3f729a30f2239c4f7de878906ef4d6f1a7d885c2541920ebd89fe7c890e7fd3335412eb93aea425b1aedba492674ecb85eaa4309665cd4636dc1545812a11a6612912e988641726ff5798680a25c2fa1574ca9be56439b71da38c7d4f0df0cd3df777776f5f04d3f55336054868c730c87891acaa44325b45b23b96a148c682ee3ac326dbcd08e97659cca6312979ef41ebf6bd20a14552fa4a96de4b9ac80c95bc21bd09e93d6df21634f37da4cfe44fb4253f0b92e14443229994bc31c6c33f597fcbfabed05853f29ef735a3ffbeb721731886315506cb7c40c6ae4a935d565a195369658ca522b43f43254b7f6bb8a013f6aa0cf7fe06a82b8b430c375a737beeb931e6c2acbe5f79ff7da10bdd9b07de33271ad2d79dc341273cd27c9f674daf32dd769812b72eb434dedc841e14620f7059dbff7bd2e7f89e34d610d9be66347ebf8d39bc37196b4af0e743d187400f0ab9297fcb02c34a56f263cdd015b66ecac3d64e4336dc151d813fd6c464744d62c2d1674dde83c4c7253884513009638c87f857108d48074b96b48eaf4483efe9cfe44716c4a37b9168f4a237118ddea42d59a24dded3a3bf333cbaafa40d2fc67e72c7d831c618638c31c6db07f3e09e04763b05aa0bfeab4df08f30fe8c3aaa0bce5ec96e869307952c88e6c67b51973c8a0eff448bbea42de99c3ed198986c18638c31c618631cda701714830fffcd8f4c8624f837ac1712fc1f21f97d196e1e295abb2661579e289cd82814e99efe5de54be0bb65c93228c300d23a72e8552eea4f2fea4aa2fed327196c48f067b021c1b1c686ac2192e3f4e0a33e470d0d70bac08104c71af2f4a2b6e4d575dfa0bcca57d0a9439dc2fb794284f7abcdb6b56fb83037ee2814bb045d907ae76eef0b0eff08f885428559fbe22b51ccb5ee10debf633d759f2704056aa84095d5256c7faba3baf87b29082d686604f971fedb3ed71372bbf218013b327393b4f1ae3c4770320e5e7ff015af2e2b8f1566d5a5fe7731c618ffc761e7a8a8b5565cf58b62ded8bd74ecee165b8cb1b56f31c618cbdc5a5518a7f67784d0fe6637f131fe8a31c61873b5bad43762eda7a466caaa7dd6d7ff15dbd36b22c69cbf3d6d8e6d5917777eceaafaa1ae6c48a96c48837d87b8aad4f5f185971b31343874b8ea7e03425b4775b9ff1921e4aac98ef6761c67cf473f4c48dd108cdd3aab6fc7b82bf1aaecdfccfcf06108b21a12206a4096f7be2ef89787bdcb1b64dfbf393cec1b23c56c6553d3348190b726b1e1887deb17fb5a96c5b936884b8c7cb598028995d160df243b37dfabb29fe280594cc18228b8081204079e2d48182b5450c448ac071a5841566bd610841fa9ec0816319802f9054e0fce0f446881853b051fa628821131c8a14708594e60053844821431c4064390a5ad99643a78c105921d802003f20e80f6c412b6e019c38718785e9d6297a8ededb2a58923b6f79ef7611348b6f7e20cb6f7ef4414b6f71e68c64b1da0850dfe0734035e544b1339eceff185d1fbfb0c9af93c04dc94f55a6e4a34016201e9175755d331aeaaee9b1490179c9820575599107255759715c684cefefc67f5b5ae3e2bd452323f079ac96167c54c9761dedc1c6e216dedadc2dcd43453a669e250bba0190c827775d30bee321b99c17045588a291037b6413258e203fbac8c48151c1128acec23f21cc9b128bbf24431248a20f5b34478bdfce5e7bf9cf3e7f3795759fb7ddff77ddff77ddff77d8f3fbb438c84d056932b48a18717183c48f8a1364144cf8e174ac8610724701278c22023544fa39125b67f4ac6114dd6e40a234396b062fb8ba019bf444a16a58a2572d8fe270438028c287ed061410f2eb20c770d70e0a2871e7001042a58e4c558ecdb84499529592b76886c7f52f6586c073a80811023501041962fdb617a50228822aa48c1ec0664cb344d1edcdddd1d09dbdddddd7588a8117e134be30ec100b508bb44c0bef5884e4f143d6062df0fc51e3861df7f1ff860df4ffd4005fbfe4be5f9c1919710fbf2bcbcd8f74b626394e1599810eb48b1fd45a42cec5f120cbb44ed5b2b1076e982027609c3be3b80416adfcfe10e78f67d31b62b0f0fd2caa3a3827d3f14b38512f6bda42b74c083daf7c1326a9c85f970133a40c10d535bb6ef44157400e118ad7d0be81051b6c8f6c7fe1d173c4558b0efccdd6214116e395862fbe3185ef0821b307c9041153c364000a9b1c0c88d49440fe4ddf8085940c237e241588ad6a63908b2c7edf9de7bef95b936003888c216619957210e8ac8e4f0a9a004ba2b8f122dfb2eecca837eb1a531abecca18effb622565593803ebeaca5c65ed573be4a65cd9e59c5b175af2801f5879c84d65d4f5232c3350864b028fc3c6ff436b97d9b599e169fb3183262ba0794876edf0c91db7b93e7d5ee6ab6c83aab2af44eec1b64c6cfb577665d9555dac37c4478721f6625ecc57368996274b32c685a93c28906d2d0c5ecca36ddbb24b2fb6add7b3ed979fb92d48961f6b5b7701414f1d9c34089810b1216441aed8f66db9dd6cfbb6dc8c6cfbb6ecba30621b6187bc8eb420816197d695a40bfb85117669c7d856b6cb7bb3453125a63eb54f4f76e5d8b6dfc585e96a5dadab75b5aed6d5ba5aceb12eebb2aecd480c8e1818e8c19243b77d1b4e674b5ddbcd85294396af32eacef255257328b236f48a2a73df26d946847ee7b826191a75b8ca869ea6b249c84ad2400356104d683cddda4dd9b7213f4084e595a1750c72615af6c3eb1243eb7a596bdb96af2ae94275d2f2c2b67d21b2ebde21aa8cfbe7339e6ea849b273c76d58882a93b316c14def4d4d73dff164cd1b33882af3d12208414db2bd9a696e6f3c5916909bb24976d635c966e19c38278f1845ec7be604b17f815c98936d5921d6c56de9b6a5d9b5a5d985f1d7fa6e0d239fbbcddd8643646cffc09baa6506b2ddb7ff16fb8e0bb38d270c8306ea2a5555f72d0efb8611def1090af8aaaa5fa980625f021c607bfd934bd1634ba2b62fcebe36bd410fbb049de706e6bd77c3b56281afd8171761a776e4faf505b02f57c243b4df7dfe3cd65457bd0d6943dafffcdeb0bad8ff00119675d6e5aefb3c76e3c947192ec98dd543135f8ff5b9c9beeeeef7bafb758cddddddddadbd957581b863bc6da019d28f9117a157efed3c9d5dd19baadf87bfcfef67bf7a122a6df6fd5a5deebdee8e4111d8770cc1aa8b8744adb5be9d42127b6dfd5ac3975224630adb8e65d8524710d4d8572b084578bf4211faae5b579bb0b9bf35c83072c53e7d1714c3b73fa894c61e24f4d761010d8ea9f320dd18b07dadf74758da6c0f8b310428c81339f8e08df847bbf224b1139b1ba8d48c99eb59e810cd0000800000e3160020280c0a068482a12cc9a14053eb0314800d5b823e605846180863c1481004390c83280c31c60060102000188490d09c0d00fe6654fbcafe8a32b8318da0a780744aadeeef32000d4c868daaee5804d031121b2b101d952bf36c09916756e6ac185eccf01e32bf7c77cc18c6b88825e554185b8c477e3fac52b64337c0322740bf61f7926db237d67d5ce5426cf5beed8842e62aabcd4bd7908d76e44e26cefef79817800a4a3af3bb6b4f4e99965f44428da5b6d583246ebd807256d0da01bfe5f74fb3cb003d5456c53b11e0f61645b8ae922ad9da39dde9b314829536264e4a0d9ff80d3003c72d9b574d5af077384d4cba9fa1c1322f1c759597d5951a6c95fa4b894890d8c8b2eae7becc248b3c3898914b41835dc5cbe428cd9534cd5c9a80b438ea8acdc80fccff77e181c4ab93a50dbd44b51c4641ee799eb7f3e71e83ab25644dd4b5d425476e21e4ae568a0faf92232f16972add148208ffcded5f1cfb42ded053f4416b737a3cfc11a3ea898eb5f052b9e959ee87449b82392847689a40744da0590332edae50e14e67d0c8989b431fff71388844c222ca4d1f90514d3035179299e71817f3ca64be2fc558f8131fc2a5b941c01126fb604d9f7b5ff2f0a86dfb8cc850b8247d5ce43b4197236d58dc5a6e67817d47c4446191aa80cf85d88a8da5b2891aa79ad79ecbcad87379edef99f00ce80b0073540cc9ba318e9508ab6ca996c1c88875b1039d8a8984f20276986b540b18da32527f83d1f1593ede8630a8b6a202fddf4a859f6eb2a73cf897a5f498e4e5f274cce5b6ebfb435d1f2abe4b9b724d4fd978f244dcf92e69d978e3d4f6fe2120fae8d137e9cbd221de0329f14b992fd02e9f143be553d88eab88794c22759d759a946320035b1529e1107c5b7bc2de3ba43337f44ffc331f30f10526e650a002e96011142d037314834211af170a418c6a02b9775f39e2cb4047d52586a8c14903447f79d75ea1f393f6ba9eea6025e57ea1cf24618158f015eddabc683219169cebcbe01950be310023cb64fa71b8e089b4fbf99a69ac7bd6049051a76f77477ef18bb4a65a0b5d1007745b8623f12cff29de9343ed81ae4fe43b3b035e712fa1abc268ebb733242dbc0c3e0a0ed5a5eb8186def88f0d295e591f174ae4cf32391e6fc3122347caed9b6ade595e01ea6922280650c4ec5e867282256768cecf084d03fc03c10f1ed8bb350d0a658ab37941c701c6b34e89a8186888f2c709744922e34a6630f96242e785dfc51318d373f1b37a20f5dc7cf5cedae0326b2e2c29f446739694cf4622329bcf1294941dc661a4896ee6e0c93bffcb2416d67ff3debfb33f53ed961d16a72b8316fad42858e7f2def1c2a6a441386e756984d72e969ef2bd002ef6af009102469c77f37a4914eb1c1619f7f226c0b85cf2999f1eff080d968bcc27a609f2f638e5e5f618f02b38bc56ee521929ef1796ce69d03fe8945920d1d224b774e2c230193d6f7223474aa7fe0bebd803478caf9351a44ee9c6846711f12591b4b47e4029b432ecc79b3c4aeeaa5ce417111284f9f552787ea029aa0bdf807fead5a6695c8a9bce4c102d992b32f911b64f4804e590de5fad51cd9bd77694c5894e82bdf0b7e403ce6f64bf6bca03778e00dd3d5daba136418058cb50ff0c4b8742c7318d762c5a0d8d1ab63c67df31b20866207e476f01630604aac1052145c9342c8bde20c36b2f61816ecfef6d5d9356597fcdcf01a3021a33ccad4f344b7aa275e87f6a50fca6bd8a027873dbcff79073416b38c7411ae1b8282f99b49bda4caa1bfff4457947b464700726d84d78e8bcf6997a052f336da2ca4a9c37716b559a7d1d29258840634db98bf3951faa9838c328e6f99b95b110b589443a021926e2f62b3ea4c49f388b7b803b04a1f244982a4a5d66fce52601dfecc29b5c3a58636df3b8b2f87460cf9cc3a6041ae01d8213f8b5d9ee04e5b8f0eecfc57d320c3c41c8ffc95c8a121670e612bd6e88b1e711fe14e978ece3b94217485b92635dd05f4162c042695c8685b61043daee6004d3e62f54122268b05694f3f14250f75b8c06f71b0b8a062ecaa061a7735bc9560c3fd02603c20e00dd55a065b875c0bf638ab2d93f5c6e0301f7f062b6c5eb049c02ed93a040ed3d61eff25014640162043c2a9e02b4dd269aff3fbe10697833e62fe54be51a84008b4dd66d9e9131c030f5f341d55d6e3d64b5c44765526bf43f4d650c96b9ef293e82106d978e2dad3d7dbe2466d1280e17007af4ca2068e0057dc54ce7828be0c1209e8e404fd8e8e50f960d0d9ce37e4fb33a065c6ca41b76887ac0c8d610648910f63406e8f4c1ddd75df022cc10ec457ef3a5e23304a6f58076ec30ec9a83e4a75f67dc5cc891dca83fbf7f0a9ecdc70fe20a33ec1621689a4d9f16fb37b4e4625c74b257c9c54a401f6d6b17a48fd44f2dbaec80a0c6e2ae3938dd160200c644960c5e72795df705f00bc14d7f9841a22fd84bb4b3f46cea235c5751ca3137b2b0798642db4e9b2eac526e2078051606e13ae11d15a2ce08d149033792164c182f6108b1d8f0506871d1bf680877ef0d97b92ddb72d789d8f807a75aee64ae01e04801a9160c96752d80c42b96ad9a0a607fc1e9850b9a0813e995bc7ac76bf3a97d12f623affca875bdbccd2686868783ac05893808f6eb0a7c61cc4f1b6a81d5f10c5fe62d6ebe68591b6d38e5afcba33aaeaf3151b81bf03e10868ff3d79d3f1c8af99df3e99872892f516951d4473414bc1b8a1af7886fb71f85b40fb6e7365c8c6b930996cb087e25b696d6cb754bedab00f235b9f5c705d57973405f73c0329b83b6c07fcca5eb3ccd1954b84249c7692ce454f4889087d9abc7b0bf25e679f8537c391a3f6ad89b31c84e0b8ca35cb615ce1afb8c7d5da7c346686920606b3696304845a4f7c732447203ecd33d6915b706588c4c2d2ba5ce906ca8752034a6cd87a1e4a04703ec7e60855239412089bd843b471061040041728353f9af8a228e2b4b9a0869f763afbf3c06f62d19651b5673fe8f5d94f69e61e992dddbbdd7e9e1bf631361cb8cf75d6277fb0367b43f85a4118d11c3da0b0aa63f83b692439c5fc8c1cfeda9616d86aeaa614b513732b032cd9ba81c2ff7430f81953fb4e9faea47118985a3b95cd26c93b69591576c75ec3d24eb14420e2741ae55e6ad7faa1dea7f0a096d35fdc20994ca1dad5b7ee8d13a8aae1ad7a255c42815777ac6fa8a0f947787a9a2ec69f9ad5dafbc98682f7e2bdf630cd83e0b6a925b88e349910ff2604a32ad28069542e125408e87f73adbbe678ccd5364789d42d36a919171bf4a42e48f7d58b2af6204fcb0e22d208b1da6f553154692007ff1cd47c38bd07e7311cc0ffdec252a2cb9ef4a73f95f642c141ffdb8940214593e0a6b7d05b786e1b180b7ddb6da6509897c25022dd0015f6bd717bc9a53edd313021f4472977250b9f5fb6d40dcd29eb1ad2c56328fa43922c7a9840e52a3f6d88dd6cb646eb4ec0c0480c592ed72875a2c0c15cab82a4679f1914d867964497dcbd5ec1e425fa404595b96b46082d88dde4803796a0c43d04d0aaa705692e9970ae6dfc3de3fc10bd94337c1e4e2f3584cd990d8f61328d0d1c140e8081a13a5b0557d548046799c3b0ec6471b4720f4f40c42482e86cb34b203ae07cec719914d224051d53503f521bd4c408fdcec3c560923025c2ef8537caad5102d51d146e54343140fb9b8090ee254363d022e15166812ab2b51644336bd24a1695e6c0a6cd2a40f4198fc2ac67e5e8d5508950e964f0fc67b6cdba2cd188d54d2f5af46077fc10a2054b6fea12bcd9578cd77b06fac79b97c5f18114d8050c359b68597c7d5008e422458c2613a30578590606e408fa60edf5b5cf5a70bcfca5578a522947b811adf2b15190bc774e4cf116b68cc4cdd1583562437fd7cf0163894c00e2fe6d00183ae0cd5efdd23fa53a11f068a89a698f10c5cc785e97a3cae4e3238946203a0f445d6d95fe4d830c5d36c8c88ee87ea28939dfbd0459a5488335d3c21830c90094d9eb333b654495fda6f84e19b972f8809940191749a19ea4333eff7f40c1290139b3f0d0c8b8be4ffccca0d179c57500ff598988bec87e4420403c8b79a72b143d65ac48210176bdd0d40a1da444c54d2cd7a90969daedc7c488071d51ba9db1c647f4a019ce47200a482456e8038c38b63aed044a6bb184f7d2740d1a72c97434e305716ded1657e61d0b4ecc8998ad193a36be4363b1bc9ce2275a43eec2cc544bb8069b16a26572f28f3addc2f8b3591140544c192b54293c0b7953b9bfdc0cedc9d2028f0a5350fde4ad0aaafac2474289f86bd0235d8d27d7ddbc41253ba9935063f0a2f2d78d7143201db0b8990724ea39460d1cf59308fb9271e34fb95c5c17b8164e3d01aefb5592675892a1defd9cad510b49f4c4116b62768393072c233e124b78ac6d9d3ce173d7ba9fa62849bfc42f909a8dec016e9d7ff894cf83ccd2cdce98c0fd767e594ad00ca13262213350f08499c985232e597934ccdbd94e4b93054593127f69533cae5958842a09629777afcbde371b0c0cc0ce4c4a7a5732fc9ed4ee92c258d7b9224e4124159cc3dd5ada5f2477e30828149aa1092d96855525ff41603e584912a7fe8ab392fe8dfec955ccfdf57566d48b3fd4ac2fd416ffa04f15addd2dd6aba7facf7446d25c6a49cd390a8a6ff6c82e1986fe0f37d283a484474105b86dc3af1bc8109b77ae7e3515e75ce5cb2de96a8093f683ca462b66f61ad3927cb27f4d205f6e23910e2739ad16fd00485c988fcbc35e49621c9b0ce2ac02f7474a7542f7e392745497b7eaf498262536430493c212abe6821e439dad33e99b6e04ec3f350f9a63eb79c5445c4bc9d9b2ad724161e898d405b051b154b66909d98a5e68800e248c43c3a144f82ba2623fcbd01045d871dad2a0d912e046cb64c53b04d61a88a770e988b7099db890ae504e92c862056d1828e58f66fc1530f860d29650f75c6d1319817100eeca694c2cbb0429613a09068a8a6d847ef8414cabb2e8f7ec82a54ff833d8e42b3e9c34121733ecbc43078ca5495543ad5a45253060567dd9c4bf9b14f66a1489f05302efddc218fd6609955b0ca4f03017fdd1414c365cd3049f3852d52480a5dc6d1090e6112710bb416315b8308ffd8218fb0a5afc131a34f496b13a41b8cfb7774143eb8745a56b28720e1d957cec7560a3cb266049756733706671ce7e55496c66e5599cfca7c99c1e9a826d7529e7956ce43f9492139fa40e291b18e87a042f13fafc9567ad6b2a0bea5b51252c36a75104c07c7de0ca6f6d077b6b9fb9700ca6e0cc1e1b692783633eef810ca2d5a10d2245a6d698261f8c5c207d562d6ae3033dc0adba0a183388e9741bfbe43751067e6afff94e040d50433c7adc73505248ec7ab4ab347fffbc7710370a36d623ecd7dff71ff0123e4a2226db836ca2206186c0c13689197c2cbec8bbeb0f8c0fb43ae5919920c44c647e1bec65416c827937056f3f028e82a2c5a5e874889542d5551216ee61f73541d87a0b065be7706558147bd101d7355003d82af7907fe1c4125d60a86cd54c70c966325db24b42c41542707897136b0505385e1ab9de3ff4089f77c9d83243e3da294b2675e33f4e9d545b25c78e68c964b631afa236a5527016032996448364afe43aee34229b8a011c53225aa06f815165189c1860e7ba87957ee4eb4a6202b96bf19fb7a6cc4ec7e080a6f6980ce9d5ca161b7961c98aef8607e1b0fb864cfe04721cd96c8b7d314242f3dcae042e0964b6daac80757fab51fae962b6b4e63a8773513efe057351c15c5f979e283ac4d7c82e3c4bc533100838d83ec148897c36e899c1da2f68a4a063c7531354244b9faa2fcb0215ae00de7cd144bbac885b97a457709eb93dffbefb31b4bb931e39d9325d23686c53766ca9bee6f9a234f93381ba1f0930e34daa949c1f0aaa9cc584dcd683e7ba7442c008a074acff5c03c8d58b35e72d45d1c8b1a811ba696a316c8fececed93950a4f422cd2a21060a624a255d55b6398371c5aaf24822f4a1e7c02535399884cd8e66bd02dd423958c1e470a1b2d7e69365f7f96558cd339fe1a435510920a41c2e9a96bed4c5006a1587b4baca8463a9db4d21ea0f90273f593f1f998408c48e1d6af8ca8151497ff5a6f1f3d347c5a72f2d4e86a5d47333cc75269037ac06d0f45fa5950a1e944428c58d4701e9eaf7f6c49b980956a69199dbb419711c7fdb2c0182eb645e49e6530f77d406c2d4f3262820a63ace567f320553363014e4f642bef944a3104f2ac39c55ded5cac922e044d7d8f0d86f7523e9727571b373a601ae17bd12f61973c60b8ce3843a8744b3776ed181fcd0a580a0297fce8d3d462ef60c1ecc07b4d1562a9a8f7c91ce96e10367a9449d156dceb0e774fe2923105d69c4b207b0a0022244d2f938ff8449725b9043d9acdff7d1089791b055a7e9066c727832b187d7588e541afda2450b9ab71e1802a6151606f45e3a3a6008198263d2e6632075efddcdc583255090294599b966eb2a9b694437e0b30d0d983dfa3f8f4eb17216011a71f68fe2d7a1cfcb6a570681121024b3548d97f283b8cb1ac4fe015fcb1fb5643db9d41ddd9b64e7a3da8734411b6b4b2bf2173567dbd51511e50a5a5a12b57d359889414b42a04ab1485a6a2ebab9c90d0f7e368001ded7046cf446f7ce18661790262358ed75bd7872fd9eff53fe5b137b47fa8c77b1289f1128413dabc96478a2ffaba673f46d3a6130a86304986253959c99e989db04ad4799bfd7954db0e44f1570b2e99bb3423484d70e87b9721ce8df33ba581192e424941f185cff49513134f94ddbcdb323128ad81022615108ae3b6008fca4518d8d21623e8c6118223fbc88169099ac8304c4befac28587048d4593902416c3471865e4998e5ca6dbcb4ea8923547eb1758354ea769e2a91ff86b44ab57045336a7af36d8232e8b858f60f67a4c4d974de35350a0c146f36dd000726a8247aa633fb0e147571d64ded1b1960d0f40e66d6e365705b01e20308420ab86247d422a2a5443e4c37e9c5cb56bbe3ab7e9dde8d4c51f816285581bf683838b97a23b1ba38d1d43d5f86763d33462efca6f52c86a40e373fac891f9f17e7c2031258c364313e0456ddb502c8ab02c2ba85003b78f922d4e604a0b55866209fccc6b5529aaeec5483c156a3f0f543d2296281f3614594c6cf791035aa4bb1a7c752eae4ba34fff015a9eb3013a39d9053a506179b165bb94434b2d68316c279c01e1673fc41da6f1423d321b40de20db2bef4ebe13bf35a4aa77771a6ef039a5d0406fa77c78ac186a112655df2996da9fe60c6dc424e2b1bba80c679fcb09a98cc844e5f880f3f9e0170ae4aef161a8c61e4df622028531c3af58b9b4320edc27c24ca11dc18c8aba93e2eace91d15b62b540dc798083e04d3f3b908a6604074650b5132c90ce78a8c3225dc13f1b34285790eeb2155686424c5f44701713954426e100500af14802652ea145a49b587189323339a34bd2d2ff7530e53db036d7de168a0d08c28b076334c61f7280ad8111e890c56e492319a39346b2d2c334b5f708d87c2245de626e233ecc5ff19af5ba834e1b02318ae19ebe5508c472b1bbe3ca8f7fd86eaef86ef32f58ee4950b787fe56db27a12460e83ef94e269f4cc9067925686bd17f60e6bd85edba8fa8c307b9d4ba2a618f21a8b710792ae0563df5e88405c2fe1ae2799f3705dec0b56d609aaf5e0103d9583a33895ba7e62c87c2b99534cde2d233110e1f11843775fe098f7b762abe927f6f76243d377a97a1a3658c24c63e88d8a2bc86182542fe182d35c624d533406584e31a809924337b5dc193ddb0f6ed9b157c0521d3c53a04fa03ca5a9a16893661ab00894c5ad0f0a221840d02518f84b8de9a18b4ff2fbbc97d003e22818010ffdc200e31292f484c94521826853150681dcbbe802c203149c932187f54ce973bc3bfa7aa701a711ed6c55bb2532172b6566fa9b141af098c11cd311855a7e417ba7a4a51d220c7c5cf8a80224a830cc59b5ba2d36f6488086ae1a53eff1154d6106d599235a5996491c97075815e2a561ae78a4b82e89b6efbb3d117434a636666f30f6ddb410bb9aaa20a9549d40f65a376924f45c88f1e2dfd7ccbd6dacd18c38cbc394cb50016b1f5710083d2cf8948dcf718dd0112dce7ea2ab4175f27a077fc5c75432fac123028ef184748cbd1da5a4f7b2a95b59bd75ded420afa15464879753fe9bf8c67f597c6a7aba569083af41a85d1a75e586718ed9595cad3afe53a1f48c60da2dc0be4eeb3adae99146fd234643be6338118115df1fa8ca2b333a7c515bc5ef5813461dac9b529840aecd938b1339cc2d6fd733e04351c7d760c9dcb4246bf1197786f740367b3a190e1d580d8d402b184540d51045ba49f3d55b8bca9700312808a973e1562f31a00984a83fb3ae9b34069dff72d62c2addaf4c10682a47d66ed855a808046c1f93991b92962830b9c4d5a3449e5b66a57af67765ab43d3bdacec016e6ee35d9e817e89e0e5a5a28fea8533dae20fe041d87371e316444feef84821919ba3d87481f2a41049d68b3fbe09dde514f5630654d17a3bfc550d99aa7b718e91c75f4467021573da641b8950ce29ecf8f7e951080e43e573ca76ee1889462dab7302b0136550e4c4620e03ebe9d296ad3b922e17bcbf5f98b22d3deb273a14192c7ccc6c0f18d3101fc65c4ccf448e03a7e5825065ee0678f1d8fe6b07d07612709646ba62a9a82291c9c3442670208d7da518440aa27faa5401185b5cb8eae04682c2c6a4aba5bf364ffd72d0934fc589e15b38ff65faacdc9a02092b31fa230ae32c71a4091843873a112f01ad3bc8d242d13438b8b9b709f90973ead73f3ea2206f59259e89604c6ea65eecc4fb6b5d8b02058beeae529fa01d354f27f4ada90ac6bbe8b537163c9389ab8cd48808e35d986e751c9d9120fcb779ccdec4517cf9f248c49a543a5af8e2ab2eae578712b4980ca1307bc5950ae562fc08467d979b5b308c2cea24a9bf4b73968d2055294dbcb5f1998086853d510ca35155f9dd9b49efa7ac1ad9bbc9dd87727b2047815500edd6a65ce22202f7494970e1740ce11985d8d3012dbda89e112a9c9ad205b41304e604dfc1067f88535c14e035e6402136230928a14a07a38a76be4f6de03bb0152081a0ca5f236967404d552db122f119458df0a43a4246d0fa86849b5153827450fb02b8023c5b39d489c4f8edf34aac6beed92fcece886890b95e04ae355eb9fac6d0c6b30681b9876ec2a358b87cd1eb333453f0fa02bda1ffd976679b7b6cab22df5b9d6cbf336a98936084435f7fa73d6f672aa3544c7de19402a651dd48266e9e1db89f4b4ae20558c055511abbd667d4677f139d60fdbc1705fe389de7b15f5c8fb7b1421a38a1a8291a642336c9959accb3df1cdaa7673ac713347a79454fe4c2044eacd61b6401ab0a86d83d46b977f1230c165482a49557ba44e305721f9fcc4fa35ab81e4ebc7ba453cf0d173601c3520f5d2be0b665c5cbe578aa54e2195d5464b3b6c3006f16496ad7ebb55f06ed9ea8958386674e7b970d2ebcbf60b82839167013f9c725194da0fdb11b2d1ff5687b8d88915969410ad6b54a9b3878bcd945b9c567b3693c5e7b5c8248c761d716beb32c35fb9ab8c07c038711dac0b8b1a028e501f4cd6e5b52b00fca01d7b79c44f7318ea78c176018d5d343eb6d5c26d31205c93c3028d4757e0722a04387dba32d1897d71dfdac0b340ba6e4f80ff98846f8f9c3f14d6b7b135a33b800ffb0dbce8118a809b855859beae4124e25e05cac3b6074ed4680f05fd8ffcb8e43736e51c8cbb8e51164ac375e42ca03d4756604f51fc2d3d12e0675ffe2ef8c4266f8d8733593a327957db5404551b67cdc50f3dff4a114f903122018af45ca713ae60ce07bce889964cadc68873798b631668c2601cc4650491403e108a0f14ec1bb41ef6edf3c501bef1c57c3ad506055f9afcf9add35982f9d4e94e963eeae3795f446f5e55188e8ea043587f39bdeb445ce3a2be9398fabfae5713012d4cec9d64dd470921987d8d3071ac2890b4af53ca15bf7a949c42e81a360faaa605ef6932d2526b5d39a842f74a4120634f2a814dd950c5823dbb613cf58c6f23f918fb2a01cda49c46b31751c54a113144c8f5dc3d3d150256182266d9ff37faeda072aedc0b75f6180f79a1377a31fe40238844650bbc12ad887b98bb7d40415696b85fb4b7104720f8acf75fc321991c243defb4a0a9bccef0366144aae3eec48930b159cce51122b9947a9b49d0253b51ee5634877b96aabd48d540ed3eb81b6bd7bf72ad689d1fdbd12f6de92019b1962a47fd731987432b252cbaaeccb167d559e320fd2eb0a29d45cb17481a59458e7daf60499ce341261e6469c21cec3a29666997d6ced9df2c43f00b6a4330d9a9d211c53f67e0aa2ea6d384eb8db3a8c7918855d8a09b7b860d5a26863d0bdbb77501f932a1763122cbf5c861cf8d0ca21993c9faea924e78f106060d55394144448e093c563f35f8132b24873b14992c607e30fc00e3462067728aac201c218d1277dc879e90881d5dee81936443e2c44c44a94dea00d8cbbe1188a01adeb9470cd43c232960088963dd57e8dcc5a0dc623e3a2f240b6c2f8fa71814c1f2a9343af421e0fbb71937dd0418b4e139aa3e8e4705198fd19e7f2dd17789c99b583054232e9f4c86630419b4a98a927b4e144bd6d6457eb6d2394dc88cfd670b53ce52f6d07031187e483423a4b0d71d151d06ad9271568b44dfc17bd4ba9ce8662fcf1f6502a7218d95157061089a3c59ec244fd20dc4b013db5c930345ee9ef47532094c28bedd55c1ed6cb7266d218673bc4059527f4d7b2228152f3072f9008ebd928aa00defc0a36b41200fbb560a5afd9bbb75421a716a50cf73bbf931298b9ce11fffb8b66d340357fbdaf052e3d7d780707466c34ca7244a2176aeb3af7ac399e4c01f78a3a2bb08b03f372d1facef0724630ee5d02a4c4c9a472e9111c41fd15bf7cd4e53fbd6b46ef9081384fc9e26878f64eea046850ea6542641afbd9f6e5f099805f07d653805569e9d255d8082cf9b17ed7ae09324e2fee676a229a31cfaec2cd25076964b8159f8000daeff0fd5b3d05b46bd16a9d442ab4d10bba1ef9b3d09cbd7076acabe8b14ab7b104a2f5c3d605c555229c4cf0ee0059d88ccb70ff64aeead66c123383a1963c564feeb173a2bc5a8f0269204d9f3e800e81a26e4770d86a3cf0a01696cab4eafc63c5319fac728cee1de8a6aadc7318769f446a1c3a615d56895c0c083b9e4cec9d9a031eda7a2a6323483a191a00cee20634568a8196fc31c4a9202670b849c29eeb77d4151953488093c08307d65bb803a178032356e31b126dc44d5114d1abb88ff8a7b269bfc44cec857dd367d0a34834303dc3b267f92c24a5bbce128eb2aad36f083c5bf9f57086b8b188d9a2e1f3629f69cc015f46a7ce6b695751057f662b66f2a39149adf8eaf49f1cc26e1f077d777049c11b04973dd0b6874ae0e16fc4c9257132df8e94497050e5d0e3eacf7102ada960c1f60e21ec88f602368f8460618481171765be32d006f0368d410f8af37de3dcec3874db970dfdbafbcfdf9dea6aac3715fe65de2ffbc6d4948ac046d117c4ffaa52959b0f6ce7dfc9d886c57066109a70cd915650ffcf72cdc8a3688205c1bb20b21f671c09fb370abda008a726c58b32fedd53af0f774648b720023969b17a261dce36e9d91804e48edd92dd78489301a040b3c6ed9f7bd0c164cc8168b76f3dfeebcc4149282287cf1a9f045284adf5d2424744f5e0b45a99b9b84d4b6e84b19e2f590a67056ad0388e1a1fca3cf45226921a16ae77a2d0677442ba868a8da5d47ac1d97e1d64a06bbf432e0bf21192196dc857570e960989f649f0b847e1b29a8254335fb07435fe24c56de20537132b9c295d79ced8ee178f3378fa57f5922a86bde1de787fd66bd3b5899924320e717add0580cb8470bb87e4d1ee031923bbf90d281c53d0170dd388b356dc5abb73763c33e735bf1e5ed98db100fe76b60b192dd1a5468e354c945412f2e5cb0bf5703b259e8457cfa4efb02589eaf906368746905fc7a46eb50423dc11ed40308db9ed0419884e00b564c1d260d26cff42199eda448e399c1032192bd99a5018d2b599d304a3f9fae12827d40dad045f7ffbc428845ce5f1058f9a036bacd7f9a15a58cc7de3c30cf578779f885174716fea7115c100a0be667273000756c534b0c0f5d3930b7b8cd34de5402484ac27a25fdea40369aba21deba86a8b92236f3a8bc7cdb49eb56382e87acf016916cf121b810c1dca811310710ac196751d362b176f1670e9817f39aafe0c752e89fc4743f083eda8199c6b786b65bdab24903659e7618e0dd4dbcdb780f923a17130a188c156c919502bdcdcd3dd8a47b374eed8af56d118980510496da1e1f7b35d7d97ccc4e092974227e40c755faa256d0bc8af05000b4f1fc0065ec1b702694fbed9d5bc24e66a77c55c1ee1aaa346289c2f6d74297a45f9a95690104c7bfb5171c387f2e011bbd830ddef552c26427aa4c0026577a4b19b5ef9bb60a28f3b037c71435cabd2118d915116d85f7cf1807164387f70542ba94f8ecd69ca9ea8e143e0d891478103fa0e3dbaab2bd14a33a96d3969353c09d3c388eba7517504155331889a5270b990828a6a0141f4ca2fe3369c610ceb0c1780bd64aa88bce158dc8161d6a18eb1e36242664e9fc2975afa7b705ae4e8713fd9bba8447d888cb85e56d2c3b6a5a0edc0691f01bd91bfc6ea5a2c446ba37a5a5ac9036f49971d505fdeb744e7b3a3a25048f12551041bc1b0281bf5bdd0fb304536aa57301d93d52e32dcfd35529e2cda1173f41e9afbf64da329b2191c5060e54da2a706eacef46da7740d9e4cd5bd5d9e9794f6d396644eca0cfff3831eac420c56f201a7581a067053ff808bee963b8fce781f55539cde603dbda54be8fec6baaa5a1a8945095b95a8aacf39f714afebdca4ca94f730bfaea55da094b3d6a62cddcc1e4b8c70b5c151ab23160006b842db4db43869cfe23f091af6f25d3d94d2205e345124dce37a45f5dd94d9573645825eb0b4905d842b1588cf1fc66460cb1ca0082fe812749a30685f13a752684d0fe31f261dd69319b1934868b3ac8bb9549f5d46857400ec90d3278e8b80a01c29f739e08f161c7246e63868975b228eaf0b4b48f1cd7b1b68a50f2714285e81687212c90bb24df5fb3b03c4b1d71256ada02b18e62a4b40dbd0a51c1abf89e0509c0a0290570a91105008dd46e828958e065df346437418b08602eccc5a8a8706e9c391fc367aab9414b41fbbdaca8a36c6f7db75127984e238df3c6afa989c8c133661319ee09ebe147da64031b26a6d72843472b14ddd1787173b2deda6b76911626f0115c7d0f90364e5ba954011b7721e67d3db42dcdb629a5f397d4a6928ac968e1310b7aaae734c4eacbd7198a4c29b22a2c0c1cfb05def9bceade972f96dee57983f90e4f7359591fcd83633638f7c8729177ab9b629d82a2d8988f2f55b9cd77f61d61526b2358c3e23fc8158b088a2903fa3e2112260a7ffc6775196d592a6dbace5ef9d554273bb2c081a834c502f10567f7b69a96bd7becb8d24799022d20b3fa49b246059b5dd03dc650fe488f5d3f49fb7ddf00b10a03a0459f0ebe3316b5003c91df43bb81970a4e68b3a993c7efb4936a88ca0563f8a924dd4f20fd912f36556f0ab1fd5355daba33bd672c1ec8d4fda8840f5cc50e76d8c92366dfb6de505d9576d2b645089f68b54a04d4e7157ffc93025434e9d6438042756240aff5600cd34d5899f589d85ab7c544dd44ecd62c3152b38a4dfd44ecd46db1b26e894d8d25b635ac98a95b6253b7c5ccba2536f59698d6b162a56e89499d2d366b9618a959625b6702cc57a14fe844e835ba489a0a5d849ed3f4cea522ef15b7c86dc52d7aa335f5abd02a7411ba1a5d24ed432f75dea157971079abb845ee29eee2e6551f8bbb1217714995585a53d7e97ec9a96735464632c49c0dd06bc24af3ab2904bf894d674c80c031de7bdfcd86ca3ec43ca386b8713290fc08e1d5ccdc147878863165f25f5f736f3c909bb334273290be44843eeba8b7a152280e23634e762f05b3d426ba39755d4ae6d58d6872eaf8e7070b84332d374affd5145b8e1b5f70e8de0acd521be872eeba2a995337d0e6b4e39d2f36a61c345106fa1323f4ac4bfd864ac5a951826f8d6b53245243fcacb0409a6e5792d045f8813e3faabc69e142ff6a88906c143313e8d5185192216e16382819b6c2c38375419f0743c04c385661f63800e1b2d0722b35a36aa0c9b9d3d10bab5be897468e9edd64449221ee2c40580ad95a2793bdc9dfa0ca4432f4a740089e31fdf9ad2954f49cce0f85a95a67bc9b1041d0e3c9795687366a0335aa228c0ddaf3a873de05f89f4ce59739e6d5c0a16ca01badbceb4a6039052944add5e5620965b3c23b1b3bb12dc4ac1af48fb0ebdeb4d9f407c489545fdbe61a87d8767d6a59789731a7b7d6425554e2de8d3a8d9146644a5de8a8160bd35407d2001b1fc9d0970bf406800ca8c46a3e2372353b809ec0f5512865b8d4ce35e8757128356c3537ca6bae487f6fe9ef4aee86b33593c7ad3f91f1a69ea4d320f980f6d11df228859522c4ef016de1ed5a0fd83c75c77ff44c4490162eb7b7aeb9d09e67ba2d5e1e10e8b9333dee2f8827a3a599f5c8f92d2616d18c70c8792dc47e8f9e57c37997c3bb18faeb9b094bcdb2223f7cc928d50d7903eed5ed075db4fbf7da038d2e35bacaee360e32530e6bf7148023782b8d6c2a57677e0b7b7331290f195a1c553ac6258077f302802ad719b061e51704909413ee991b023e803f4ac73d463b70752e01abe0f47fd40abc1066b47a193e96e19a84166099ec8db72757ac0b70db750eaf60d1f4e0b14e6e6059f7d603111b222323446e590afcecccc5d912b68199cbd2e3626df3795b0aa3d51592429a3864720e0ce282dae72ef46bb6457a62114aba1d87f68333fbb528d0c5c4968b8a2e967b513d1584c02f9db3054013d6ab21876e0b2ed850f6bfb65446ef258fc5f9a2742c26a79385a8e013ce7ebcf0bca9d4b23eb9102a3a863705258ad421e65ace59f25094421a05c7d78ef1551db34c33860d41dce86cc3b29478d8cfb1b32437b5ec83b564c8fbab262f57f1dc35494cc05d173fca3e7d66b4c44306d745d3e493c6cbbb376741af9dbf79edea6bdab8e0f66b4ae4a0c2cd6f29b35cac8a8a1b10a578d45cae45465d7e0cd4cc6ba4bb633be6bc58c2349be8b44ab0c04c610c8360eee658a387b86bb9cdc26f0f62b646448888dc22ae5f4eee2427afa7e02a41ec52bb9b102506bd5a5004890a0b92013359d4bc787f172e65f6e90b9966ea3ca141c553537f855ca03aff035c15f5fca98db19b3bced55eba99a64d5740a753c647199cefad8af3391d75d3e04085de2ded4991928a7a11bb96cb6ce5ef50ecccc33344c0b997f876422e19345549854a245566188e46f6212eb703924afe3a9f24d6235d185c2d4b5646f2b631b722a6471533dd4099bd7be764e63584efec65ea484059de0d8cdae4c8a03f6888662a2cde9f7235669b4a6d54305042988c217b52b457a6dc513c576d5fb8c75d948f70edbeedfa7e6b42ec5ace66841f55544cbe5626d23ff39eb34e867625bc2a916da3683153071c5a9dd0b8fc40ea1d2fb085220c619b2352c9e7dd7a6050d69c29bd0aa4468f65d3cad12a9be8a603207ba370bc94dc61da06d45b251416ce0c40e8e64a751646cb880cbc98f8d352084c08ded7ecbe4d348ab6a4bca70ec3d8d4524ea6750c93f8df84d7723389bd07e1f002822e2c8f0385e29081a034e686bd7812741d422e41ea80c07a48e8ebf893f919dba523de4f0cac17dccf0a9e8f0e2a68702c7d0b24c953b28c09e948c76da2173022f685d952f02d3166d3218ade69f9f3bf12822175575016f2cda1516510316f748de4883025b685e6cfa9707f969ec0d5100584eb5b9892741d7986c6cd376ca6476a2394078babf3812f4ab54bbce4249f8feade6890920451a433e8792f4b9d3d0fe2638737e6864f2f6c41330aa5348ac098081a79ef3e5427beed1af3697aebb94d1232050c5908897e83c50228cf2fb64e4226d96716b69e74c3e624870fca14826145c9932d8c35691b06ef841cd63ff64e60164385c595cc387a97d61e6d37eb66d655976211618115bd582221bee00e457ac6c51f1af2e318a8d3db74c04220e655425f7d5e79ad87b1522d8ec2687ef7262089f29a03e213a986c5a24dcf1795788a44931beef620c7659a92e05d29e28969989b0a51448d7fea1e3e71d3c9f19614e6dec02d751837cb4622b1365fe6af4e26fddb5ea527b91c911de62bc14a1be8264c3a9b1ad5f9eeb8ef4111d93bcddf21bb19976c40ac267e1d3ad036e8d2789827b454d19e0044e6ead73380fca78521072b3f1150f11593560de3221535989ff4fbd945210a165e7b3b64dc10b81eff070991fee0638b922e850edd1beda89cb89a235d815776eb4e97bdc29580e06f9a4c8eea1d38dfaa57c00ba66d9261c95dfad769fc1d093d384fb409da460a661d46bbcbb8528a15e0431987b0e127fdf3ed5fbcbf8c8f7f247f498dafc241e1bc15a2a1c3100f583b6c3cce3b1a01c411351d67c115e5c9684631d62f3cce1a3b3a715fb98c47e16a27231a26525b169058074f8266e8c13f0066448258119704a3ce38ff69275266c476e813ed9e8e3ac15280445e9ca5ac79ac9e8697f3511adad38445996637a0a9d991c210e0e6635da14ed27e2341765f9acd31be276460e5889258b8b0e944a14a1183f9545e8a269e65418a1bcd797e68006336e8f9b3878a5cb1c249be645db5ae082b96da1a733fc80893211e26879096c7875191e1c0525ad47553e3fdfd95669f615cb633714446f277209305e944150e51b25c4241c4fa5a78dc36a43ef59311fa037c2c7ba36b7e496d79b8f367a9719bc7d3dcaf6b4ab91241777a96633d3848459e67c97f6221b6d930da93c077ac00673c02050be1c0e377d3221ceca73ba604032b79a11ed52f060337b93fed98880aa59b62deba0a5b643c28a3c2bb828e4c7013896b428e77de38a8be6b7bb1a6dcc5bf5123fbc5131719be4fb7b3472c0b5c7926012f66815426c5237780304f0f7cfd97a5b3e6876954ab7364979a684fce05238cef14278fef58151b2b15b5f174efc8191f0f62af8a39127d03e202d151ef9dfdbd8ca88e78e6a4a21d7d0518455a808094a242da9880c5aea13daf2a738156671645633f2f97bf3c989ef67d268208288105615e815bfd2c3cda7becf26987372987ac78477d41d8c72411f0398372f62e76334786bd69f513618e5574069e9907780bb7d3fc30dff107a634554901da38ad7e55c59e4154aec379ab9bf44a37698a4803dac685de8fbb3836148fcf7508ccfaaaff4965a17e0d3ec3e5daf83e21182e220283132958438f5d3ca8d8e13f4dd58aa8497c77361315a7e3f32002ba45edd71dfe908219df54a224404c0050f039452e666dc8f7525863d500660dc310638c05c64ebb6bef42a987ea4aa8282096201303faf3198094d0bb39f4f6d3fb96e3c58005902cbcc16183307121e874c40be7c1ed49a4c8c9d1ea93a9d41715c3d49b982ff2548a7a48294eebf0d0965f05aebaddcb9e5e7c87a800cc784eb93ae714b1fdc371e40be72ef83935014be7c2394bf1f3a3b9e8439b198fe18278a192de50598e897c820dc1af316aee480fe3882b29c798770df08142bc576b3d1c06c8e64ba35732c9a665441a78072f17c06a3b5c1d90c16032cd11922e52fd58ee4e5d1d0fedeba112c8c2e6bfbe8fddd5e18d45d1504a78ec7bf01afffc2f1e2972b76cdfe31cac7feba73ac5d6f67e364e28286035b596f9e76257752e60820136d13ce2d1f1a0200a4e7c8b7c49e1886bd08b6fe299509d12874f6f27571293275c8f8c0df05d2546b0df69de7613c570dcb44bf939993bb6661c94466cfab4b3229b5a24d745862949e11a01927c8487a81f7b942105fe39119c19d04fd431c7c54830be4cc7e221b9a59ae01559099f518435f0e51d5e0afa885bcd9ab37a51483c7f1c35950853656bfedcb44c8617300abd99b4a8434c46d4bb048216699c050b0467a540aa709abc2cd28e027f4c4844d9e4d13a82f2221472e5d0dbd9aef8af873e78f31b0bb3c8312961af5d64a5b508950507f232488aa07efdf02af175a054a8a3c8443831aca351b99623203e4d3dcdabaca5c22396f5f87549a8ec40bf58dcd877f62fb119da903ae2e71bdedb01a6fb8f199069896726380b88f630f1167f9ff29288086a2ab2afc2798ecbc2a57829249db882508595b70678706b80b75cc9a66507442c5664dd1c4238a852a8db5a74f5d1d9df259180a32417a7202ab1ee297e2cb6495069d31dfe1a86a540ba952db40b11f4d2f490af9338608fd523bc03a7cf0d8c104b383d333d3c5a5ebf67058274ac029e3ee3782040100ab81c9af3b6788dbee636d45fd691ea47a038da93c463ddb2d1e1a10fcda4dbecabaa55d211e45f049c73684fb4028c6830b1a27df6ff1f50e3bd5c2cd1a9277bac1a482324dca204381651b19ce072e7e2943c0fcfb2a48bab5061383413e4c5e9bea6dbdbadc515c37b946d91a89e2de55a0b410e4b2d5921c2369d018989463e207d31ba878fe1bceb11c834073d721fbc811a0e693cad916aafdf754f660623eafd885b1486fc17b9700b83c67f09634764c8c1d5d2f71b6dcea5af1cbcb5c5716547c9663ea590cb04b030b7d0c265a1836b3e7e532d931fd3daced5bb9a71cca5dd349d865eb76f5d0bcf8a3f9362cf903a21bb371df78f910e71ff308d47123e8c0022688a6779110752990aad3505327d1be1d2c5065eb5d227c30835f3c3df99eb04c495c1e0fdc788a062931650d0ce2eb8f8f0e331c9608636723d693f65838d7423a9fea7cdba58549649b70c6c88d315afd410446d2a3f8fd314aeb4924a64994f43234894ee83ccb833ee726c456e54e789f31da0e5ea8e42e5efdbadf820fe436e63a745295d99d256107c5f4f309b269d15962d6552bc05f1d6d7ca96687a34b538962deddb71bc0b8828009f2bee16c1bb13d69003f8f8d5d293543a3db074890456ffaf462c3671d31d814e9818704810266a0077cfe8e80f2e601049100110820a768df4a8ad59b86348c8d9340289d1b783b72261862acb04590f1cb942e7880bf9d15fb82a1bff0a297398be56104559ef9dc02923429d44c7d48dabe16a2bf9c2d9fb6948af4b07eeb1796bb3a2ea3bf03042f8fe08e8a5cba98a3c2bdc1aa8daa966b4ce33f2091be29702d01ff03793bbb28a90ae6174683eb42546a662bca6d45b3a0c2c2754947b0b2cc71d0ce55de3db4fa06c44f5fbb7e86c7866f7b0f69525d0032943aae4f8c6da68513b1854fc7d33f39f1eb793e391bcec960ed8fa13e72a4708cd422cd8667435409c23c086316d6e95f9cbcd24484e0200c51f1345f19e7ce399158a101d2b996024a9e618a633bf60e69ef6662b698bae64ca1169e618e4a131c1473c3aebb7bb51c5b56cc45974c56543e4e429043eb184b33585b6f65b46c5a7a24609c0294e42b61f033c4883d6f4bd45ff29866c1cbd16d9e2b9792c058150f08ded1555344293a6c60aad9cee94754b5d1df7edf6c27d9d2bcb68ee031c1d0fe2f9a3e09325081949f595cdc6328db56478d05d3d9993583d30dfc5abaf6955d62b9c88af3ade3a616fe43d6c96581d94ded6777bd93142fc7c5beadd9de98e9164b5db95f901e04956bbe3697c2588dbd3641262c62614d76c71aec8551ad2c0ffe446ca543dbc9fcaba7393cad052dde5a40981ba3797876f3c494523481a2ec864994311ef17e399ed0950688361b92b3add8fe1f6594f4266f7308a201fc996e70ff0e3c49fd222b25888e41ea59931fc5c8f500698c7e7ecbaffd4d1d515531db8e36daa17ff1f8f20b3523b123141cb6d0d01f88548302b9b0e91d695e9295c4531f06f232dde5b8e478938fae1f343c10f56bc1fa5671ea762422d141e3252dd1bbe47fa8587e7c86849128987700bd7be02e7ea9dfce78b4a29340ce32e46ab2af33350eb33545b19f9a755d8151c2c0f1105f55efbf77242939a5422e311824fd6a2216786d2e021caa4859343ffef76c92da05a6284a00e851db55a6e0174eb6dc6ca93bd78040d22b741bafdbde02048cfc9230db634352e0aa6ccf64a79fe8f70429d03c4f5b877c0718152a8a1f983d493b0f511857623817b413fdf4a45849f3dd54661280a1abd82b0b5bea0f5120a3d882cad98dd27576a9496ade2ade57ed3ca8d906d387426e25251e49cc6f234974cc01bf65833faa73fd2ba24b24a0d7e778e7f3c50c776208b311deeef1d38d17bd2abbb81992fbf9c7417789aaefd83533548d51e8cd9638a41b837c5154c59d3695546c6ee9547a9b709057dad62f2835c1a9b5c8f39a06780190a6ff1bff3866d9abb75c1f992dec621172dd6d832b9ab4dcb12cd563b3ef61acb9f1af8cf0a54bacededa0c057faae705153bc41a1cbccab13022456710f8b6b4bb6b553aa69c6e1e8a321d794ead798037d1ab123001c72c5473b584bf1d6b93f7dd808e53ead9e2ffcd3ede3645a8eba1ed7a46c4f4765f03ceb87e320e00c017ee7c85a8f30618c78e9d4c2649a0917fe973484f6530d90130de0a7f654e95e0b580cc8b88fa4ea3369cb61da05cbd20bb200775028583e4851203e59e47852f474733a9eb5179fe8c6a9a7d1744df22ae769fda20f3e30d18e587c3073d34491f720f55182a197b01268e9b02edad66a27bd66e4adaf2a7ad956dc130177a5d8c4227053e5de363245e81ce08fab9221998b240710603c4c86a86d79374bb309d4512e812bae47ca6bc8b25cd0d6cdacc01d9727966e583d578a74403700d705b738812e0bccb420a82b6748e5295fd24490dc83fa927b04117d0c9706a5bf6097eb62d58cca6049aeaddedb2ef1445b709db420d023e9cd847fa0ea99760562fad678c74c4dd83989245ce4b84c598052466726e3b3bef80f625234defda029f7e19dd6b6cd121f5fc18a4e8e76b042276eac58ab749115d048274832bfff6a8b97cd3be4b2623bca3969e9d03ee0ce84e29f67772a7e9f627cbad04cc6cd8755fb05e4bc3c08e29af6fbad1b27fa5c66f6085d40950935fb29764383bf533cebd11a92723873eb76d89704e6c636fa07fcc8297d66cf05b8319362c75a1a9f437c6ec6e3917edc73d70814a9f820e52e40b0e9ee4391fc36e0e29790522c16749e1fe1dbbba8250b578052b7f2c47e5006487eba13664beec85dbb9416a3b577b9b5817044955317a49dc8febb45a71e44b9255858dcc0d1f08bf2b325beba7ef415587676ef18ac132e8c2c7600e0ed81f5ad4850ba7004dd3dd7dcd972107ce5decce04530a23b89db9aea6c2b5b9d81d8fda65db27d4fad00898b57e3389df915fc6ceab1a969089c58f17ac435ff2ffe7efe682c914f251ca201fe7bc942acc96eb24ddfb055651a4079abf77a171d3e7845f016312241e63428d5e877128b064905a7c5a9c53ce0c88aaf83d6d0c0ab58ddefb34858cc5547f657c2203daffa9685c30ce3a4e078073228bafdf81b1b00014ec4af400aec164b05dc7f1cb4fa7eb5df4f3296fe219494623e3803409198098c5327ace08148090ca9c89f92236482eebd448e7951815ac6ef9beb520b2819fb02fddc8eeb6e59652a624657e069d066b06d66edb867d45e9f595f49ea3156c2f94ce022cb2e490c7cb822c2f2cc902c392642d57a9f70e0f7a8020732c346bb74abd59387474c878a8c28d3a4f86e74b770f4717dd7dfe6740b3cc1d8e12c894688a58a7fb680079c810793464b673e003f05236a4a4377aa8c9a186c9efd9832483e4370b7585c61531c370af76bb232bfbdd6c058504f1d57ccda4a323241fda910fede8e8082929c9c260a825c1504b4a4a8259ebbe1a826febdcf0f3551778944716d62d9ba43f8420e0f5d511d210f78a34441e5d6cdbb46436a3d13034a36168369bd188888a740022d201888888a8c85ad7b1d6da7babad3ab3039be34a81475695453a3aa51f6aa84108db5325a94a92edb13122a2a22640444d808888888a6638063ce219fef087b1bc427444decd0ef200e9c46cc70e17f058935e5e5d27697650c3047450cb1037b05f2e7a5fe0b18a99a8265527bd9a36dab57d65d2ab69ea882cac26d524d4113bec80f232f27443445e4d3f62424a8a845a443f62432ddef080eb4b2cb1a12c4d3c7ec4a85cd38eff88fd8811c59cc49cc49cc4866254624331a21665368a51f99850bbbe58502c28e624e62406f4c5624e6a1b1149425ebcf09099765eb75a05c479b2773f0f95b3bb9b52e972f694534a29a56c392b125e44e1c2858931b2942d61f44496b225602de4342559ca684994c852a60409f20cea56891b4ab994d41da7f2f8a69ff9a016857ae5342f69c21baa6838835ac439017f45a5d1c2f52eae28a03e852aaaa24f55b44756061738d5a2a3610483e52e54d997790ea9ec53950d679050d0dca4c814e704dc44ee9a41214e034ba6e18893f0cd173c7ed91b5349679d4e83b2d8c498d244134d54c952d604953c5e19cd49beee6a25bdeaa7f9ca184d38c963c7a4ac0926b967330983c768992add0a3fe8dc45cda45d4936241a1254dc6513ebc980dc52b665e6aef9aab06e754791fbe991bb28edd2230e4c22b3c491d1290b1e69104dcaead543caa4ca2a83fd63321a122dddddbdc56ae232cf98b3a9a5d659a525c99d42ee47e5804d61b00375cb29508bdd4aba259d745033a9f4a7c576226fc88093c46078a441aafbf20b175c83e850b724fe3a41dab18e551a963145ae61c7362979c0d5bdd4cf7729a9ac3db27f5259f6aaa594524a59633362ae0d519658a2889bc3142d5a4a52071b2dceeff6496bb576dbee05e2b8a052a9eb3c2ff551d3a5a7f9a7130d18d81f65da91f2c6fc70adb8bb323b3461f058836868523538344f6089c5a74aaeb1039e2dfad79befe6e6c6a646cdcd4d8ae6e6e666e64606dfdcdc707783936d4029d39147cc151c67a6404adb069956ab7d123adbb05b63fee133eb399b69e1624469ad5c5a94f987cf142dcaecde0f1fa34b6b97d4a26536abd2a2fce1a3a54599b118636986055b4b29a558706996fb9d5a6b2dc5b36ef1504aa9879452a7ee4ec3120d8c2866b31560eaffc3874abec2857a3c962754f918936d388a30c7dcb06fc3fbc387ca0f1f2a57bec869b2e1b83d099d6b671bfef0a1d2a2cc3f7cdaff878f50fee133448b32bbfff0216a51e61f3ed4fbe133049576e15ef550fee123d42d9e2c739b7c82f00f9f210f7ff808b528f37d52058c922cab60f173a50a4f77c5497b35ac26cf0af65a9c1f6ef1e3016fcf6393b5e26f9e91a5cc0a954cade8906760a6a9014f6b6a1ba9ac5696840b59caae50c9546685859acaf34ed922901a16619a405b6c23da801c688b7960e01a03ba338c0724d4ad2d66823c7f02017d6d648b89b0826d86a310429037b008228ac562dbfc4ed67ab698bb86d4b6586d8b4dd8160ba10b46cae3044ad2ae08349876e9e8554fe142ae3246eeb749d616d999a5e521ac5195ac25cf1fedd1043262922535c1bc33baad25a9534aa9a5f6474a29957588cdcfb7235f3aa55e7bbb77dba4eca2d0f7bf2f370bf6688005d201ea8174ba6d3185d62553aa0d9a62b7a02b399033dd2e3785bbd6fab83eee590aadb44e0f5a6c2969a020458afb3d72a5dc4d89d4a1a3d3dda21ee060d16a3910abad4970ceff46777c2d46d9aea7f2a5cf652a298cbb944a9a199be5bc28a5c3aa465839611b01366529938dd123f322cbac68a12fbe1e169a2d91d1869015c988b6ad642bf51d1ef4004116a3d9a0ef0655a46d490cf4c5c3900542051faf97d5050e7df1f57094a45e4958af647ef5d0d32b9947fc31614350cf2b2826fba12806282f28d75df43577643d2c1c3a3aea919744db61abecf278b72d58264b59100ed64ad00c5b16ec65290b52a1528162461e6f0dca1725f866298332451eafd5019fb2944159e206210c757a9ab720cde3d016f0cca37ec0f6c7f9a39279d4cf4c4195ccd3c7dfe16e6df6539f8dfffaf56da4c036eda4be9b3ebee4d273749ce3f65dcdfb06ca5c13fa0cb300dd14b25f577f061ce9083e30fd1d94e951de1749cd20f9e3ae54d79f09545398d5bfb5ce59ae5b58512bc02336fd9109d4d9d162fd0f861e428614c911b80008412810c2f65b78fa001022a5ae14aa80949e7b1ea52e1cb72f61e6124fbe3b7c9a76b6d30730fe5aff86f5535f8ed82dfb154946cc501e511985d42d49fdbf5b439ac91be21356eb7f58c3af3e77f108b0aa7bef250d067ba9ba30c8e94d3feaf4e8baee640285e028b921cd0a8c0a9a203c4a1acd8e80cf1f8a7bc5f7a1905048ee0a2257fe9286424221653f0305b9f481a31acae40055ffbc824413d86ba2107ba2e895ba82871099f55358f04c143d8c9f3cce9e30624af21058ca9080907eb24f977cf90d001add90b90d915b18f6f498dd7b45774d19ee92ec3f75c82eba286ffc1029bf19ce9e540fd81fca0d6f1e6187863c649e3dfe812ce34b9a4f2f9fa25e7592efd1dcbd67fa9c6ed1bc2bf5a3587a99b99af0dd756fbeecf85af0a057fd3427508c3a0579510f0f5ac4e9f1127839d0dbb1b56f46987ac0dc9f68dc2df93541e96eb6a284aee79a873c8482db69460db9ebfaf54bbb3fa092c0f35d4a29e574fb19d8efba7b75779752ca3ab77b65db6cd6c161cdb474bddb4aa77c79b8c3efbff835100d04ad75b33b6692268dec36efd692d76e2799439e8052777707e50df97576bb098deb83e90a3cbfd4023cdf3f536a6bbe6ddbbd41532e57ba5b98e9bbddb6ed96ba7b83a6749e90e7a3be5341739409ca1b94facf5a6b057bdbe68c32a572e9640ecd9e949ec3ddb17f1fc66e931b4b1ae9aadf3534e2ab5621c9298511291288090563893c5e1b032388cd070c1cc600c3853062dcd05f24f94aeedc5bc33759cac2188529fa0953b46d255ba9eff0a007085cee2dba2527647061d362c65d96133019ecc5d7c346beba5bc63822dae205872e625b88b0818ba02d42a8d08517b07b595b9a28995b04f94ade2d311f2e927c25efb6956ca5cec2a1a303658526852df2288b4e20e58923f097a54c0a112c609a2c6552a24849438a8be9f7cfd2a157fc5848a8ee4ea385eeeeee141820b0803923fb3f066828628b100c7df18404a92e431c35a14594275624e5c20c99131a3247834992fd33a0e2e68b295d30c05288343ac30b0e0a04c8a3e7050f5fc0b2ff05a61dc7663c015b36a80e28ba08d38592ecafc2473547b3ec4ffd714c3b7e8574f390bb6d86fc650747380ce0e6336a2ec6e8feef0332d9885c68c9fe354c3bfea161d4ddddaaa11d7021c316567c41f43c81644498254280021e3b8491a4061fc22051f1ea973db7684177b74905a80f40f16df1427677f7a7f9d4145eca86150ad08249eeffb42023f7cf3c15113cc9c24a085810449754143d44fcc068c8f2450b347a909132a61d3ab1e4fe8bbf17e5171a723fee0008295e78d2857682130ec0f0d0840b492990408c97bbfb97ec35b2bbbbcf156652367238c0914594dc4fbfa090fbf1f7226ba775b248d91839b802c8005734818510166358ee3f672c394d2f6523c795fb2ffebed645eef7aca4e0a48a1db4b8028d987b0b59b660c1a5cb962d7c7839cd22bb1946d9dddddd3b2395145f1e71505105942ac6586183152ca8518125f797ca08e286d22b54a090fdef18586ace29b2647f6bdaf16b44b138818653834066496597c2082956907d8a22b27f2ed3c0e2ee6ee38516063672a0d8c24b1444442145ee7f29a8e47e2afa4b21f3045b6a8450a3f9458e86508e8929460eba7841f222c6680685132f36e46661cc0324c58c274060c941c919af9fa12c8248e28408885ed2a896a71442af4823b72ab7115ee00f86671e534f5889a20b50f6c75d5890fde747050d1579cce1c0091c726f71220c2e57b844f184103a5e38aa96fbbbd9013d41ca4b13845b85dcdfdf73fbb13467e688931b556467286a318f2821f0a38adc25af4ccc51dec21153ea40ee8c823f8f2854915cf55f1410aaa8075411aa08558ba1ca16efed16103ab30eef2ccf00ca4a5cc8ad8492095342498f93c938b76ebad18d52ba81f41da46fab8d7b34f3d9d1cc8f72ba3ad179168813f177138f3926cfafcf637e2da223df70f42fe1793c9c6ab17164c09f6b35b2b47242934cc34ee529a59473db40992738278fda038c20e916ac5d1cae2f93e69759a3f768d676a84d30f5a8c51a174da34ca45448a9f0291646ff5a697d8f869845c3e3bd53acc76da51fb7bf2de2dfb6108b4dfb3dbca9726ab661112f53e7c946747fbdb4fd589f721c27e652a9e4953a1b3c3935e79c1c374b9d6732795d89bba1a6860dbd76ab4eb72953b62953b62976cac5c19ac2ba1a9037fa87c81b549c525a634acd748dc0dd57c3144d8dcd8db7bdad352913100bb88fedfd67b61b21705079f60735b953d9d88a58ab99a4c35d90320121924ad1ccd8b8c02b477fb89b754c654af964fae0020cb6b982d85ef31a0bbfdf8bf1f76d30b71b7029a34e18645063f26237b5a139a727bee9b1299c4eb426edc834413102b33857af9bad9bfb74c2d26cfd0a84349cd62bbd9ba59556eab508072b0a6b4e580928c89687455a8c22c5faf68694525a27373f5f9580ba4f1fa0e4206b9031a48aa4ee6669d57117f7e24a06c682cd1564299c3ee4e6a0dccdd2ea43b09eee219d345ac368301a8cf6419727ed085a9769b7a8acc95c145cd3355d748c8aab07bf1b2c7344f2d4dc550a3d1c27941c6a30cd154ef79a30f6beaf9bb3366b6a4ab0c934adbbd5c80eeacb7c7d7abe146893100f76b80e1d293a34048f7c0764cb43125a6c232daf7e7bca2e9db650d66de6d67037325386ca1b25d104e57d15446976b3f3663aac1578c3a58aa4b8bbd9ead578ab2c4d25f0bb9badd424672cd16eb2e6aba63f67cd5d73d6e6accd59db20cfa1138858c8fd405e3e28654fd980e7fc5357933929185513b38171f34ab9d94abda5bcd7451f67f9534aee96ba372f3f7c2f31ca048403dc9d73ce10753201b100f827132b0c76f7a14fc47fb1af684996326532afdf52e680c0b65aba719e3f7edc18e3ae54ea24c6df8ba5ae7462f57432477e0f77a9e4bddcdfbbc957d1c577cec95d07019d1f850bc563a53e7cf813a929742648f177001d83fda50fff241dd69c916145d6c96eb5e80bf3bff9374bd3645941c18c238ac4c061280805af118bc9f4e50117c600e3890a5048317ac99719e8a4cc9143e6574741240ec692a397099d0f9d8489a197093a76b86186177d07914cd8cb0469e5454329aaf93c7986e374169af879995080316292a0496a3f2f1358204082f2a2605f89e10b31885e48e609518c5e26b80af3ca7b0a33cb02e9e560a7c4abc1994de8a2211f0f789448464838e2ae9a9e997719ec427d0d98a45f2afc4b8acae4a542d5e9e52bf527f0a640ac53fa6c7c5d89bb5b0df0ce80acdc5f7209c97cd50855decb578d07c5df64496072b3708c2c2348dd35a0bcbec29faf5eec557f0559d96b6e8c5acdf83c4c0306ad94d2f9b5bda52a0221125388c4840e9400a2e825e57ef7def3bc4ecff3c2cff39a799e970ccf4b3ece0bfea3a47d281a440c7e96d7f9b0ccec80276702fc807a132448b76ea82ac92898db2ab5dc0c771d7863a8210da4d76645d110482f6e4707d9425a08c5fc2f092c3f65023c5ff3468ccc0c1b72b720371248c8f03fa99fa6116015f72aee7d85d302feafa7a7e74fc9e427faddedbd3d4de2bb87664686fe97bae071fed4bcca42e6e68fcb84f65f14e74f95094b4be0b18b28aecd235fcdbf5e38dea2b0049a4f322d43c8b450b142468f152da8e469850cd87492b37cc95296854b98a52c8b0f79fe748bc904ea96936ed1a00f6cf670329960ba5593d4fca4f34d5e601a8e3369fef894f963f7501a4e30d37d84aafa32dbf750855f5e1c2c16e73a3a746a920b7f98c81c44c81cf7a751f774cbfb8e47e74e726f1299a3f3fc89348fe60fce0a3dee60f02882917c0a896cdfbffdf61b47254d4d2e1c8d6422fef403ffd148a6a8efb8d9cefd3681b670fe4c265760958ff6a73b5af4007c021ae90142153c4a30fda9247017b9ab6bb756542baaad80618e031e8be8c853a8c5d93369e06422430771d764d2abf94eae10a692e79c43e439abccaefdf474ad6b5dcbb367739eb9249d9774cffc49c13afc1e77cd9f39bb7b382ee68fc37286f8be2959b848b020f7cfd41b6032488cc9fd325509b521f7631a83c1603f3ff749ee47cd0054aa4244ee3f81600e754ce47e530d06832d29e29a30c9fd9ec76030580b5a681528727fe7b4adca97dc5f6a24180cd6c2115e681533723fd735180c06c3a642eebf4b1c26a324f76f30184c06194a5272bf1d82c16036dc305344eeaf310c5d188a4c5ce47ecf0106a3a16464e4fe8ed53825b97ff6d39b255cde2b3712569ce4be36b210839625972c65486720e52aff76a6fba612f71b388a79bb77db4cd7c32615e0131993e8f1cba02691d78a0426afb2942115e5ae6b6f9a78e8c8d3ebcc7cd4f4709d41d1cc99bb4c0dea073ca7b5d6a65276ce223a72ffb4a9015e2cb06a7be92bd41606c1beaafff9aa9edef4f57194cc84e3bdae1a9a70fc6402244042a27f235d228cdf27739c70b7a9ea9f7af278eac9f72f6bbb17e3efa36211567629d87b9a806c5c645a82826988a34589d449d35ffad5fe0e671531e2fdb85bd2482321c538328a5ab4f901e79eb3670d9e220166e1d099261e276f0598e694cc71ca2e84c5f27bb952d07c1f836422f447565741fb4fdfd222acb63a385a741ac3cfbab9ba7b95b57a82d410eeebe3067838fbfff3b7bfa500a0819f847abf6ee0f662ceb435fc162595e172d062081df6d750648959648d2208dd9adfa35b93356b3709357b383fd1b3984378641571acdbffc91ca76dfbaee6ed3bd901f6f727610b9eedb5fc3e70d0e60fbc3639407ec99d3401a144e8e63ce68f2454ba7da5140af7d75d5c18c24b06c684d5a58c16a6fdce823e280c56bd7f763842eda88f3a4d3c64a5750333ab7cd40ebd347a908139d1c99c51760dcadcdfe108f64728c07c12be7c0079637e3f10f7d000529cef0dcbe108f649b8d900f2c6f4ba846e9f929e44f06c4396bc315f55330d6f9519c7ec09d4c99739ed94be04e703ea1678ac3f79c9d7597fc8b3f290a70cc7aa4318795ce5d95d6958632d4a1450f66d88c48497f4557520b8ef55dbdce6b3dc15e4d292702ac9812a59fafb52dee790f0bca40902f3f5f94a7220045e4856a828dbef5ed25dfdf3f15b59a4c312b952874389fa744e986559beb2d6daaf41f9ba4ebcb73f874a6fe71fcd997f0dc349ce42ba346ce45f0d2c3d074eef39b07bcfe12824d77f51af038d804772f29016790820d79a2ab0b4af20116817f7f6bb3900215aa5e420db471dc91c0bc857085311873071e054d22b3b63ddf6292d1501d9580cf6c71943b66f7f0a5527bd000e2868c62073c87b017088af6c18428b335830be18db47b2d6beccb12111ec5d2a412773f0cb7c77b77fd9473dc7a166d9fb9295148c366f33ed98baaef4a7fb5dd7954a7d53f974812121b45829d02d2225213f6da6e0cba92004d49f88a0def3817a6f47064422f31844821ffbaae29d20a920dc7df7f5551368ac9ac0fc999f093d20739fa2c0d37320095405a44444843926df2f65994b5ef7b4f45c17ca7c294e0f78481802a7026c7f875f50ca1b8eb4653165609902e0e5c66d5cfdaf4f65ab65777457c371a135ea9cb5ce0a4ea316c1496b917eb1f101cfa3c9a900d7841cb43869ee62d1a7cfc1b6d9fa5bf8f7dda5c1d3e75e049a4bb8206f802773b7d3d16991fedbb7dfd5803a3740e09a1f5999470d947f69b234412092f9d2a000431a706034f859fb7f02642a3efd1e72d7eae93751f87fefccccf7e3a7fa1afa1b0a45f3a86775eb82a736701e830c70de0607070794d90614c1846cf316031b9933b0b1c12064b5484d3b17d8b041b38d117c6c4799b6487f47b62864830f5f48001e7c5504725e002f801049cef3bc8254f4e60bc7ef0c2819c77a7c4513847190ccb1802303642af33add9238ba65f3f4a9acf91bd567fbf6a5ad0165c6b1e098ca37aa5067e726647d1ccd36cf99803060cc9831d9e6a5a901dc9768b60155404a44b82fbdcc1df572402439df0309003e886927c88e71e6d4af42d5041c00ef2f5f62a89a403ff860e8811c205cca86ac07e7975ed1af014e302dd277d3cefc0ad6e89439056600ce80a30c3802c5200a1c693e81a368da0054d9ffa730d82b00e183af01900f33d8ab4d1fdcafaf81074afa38ba557a4a3729b6483f0704008889d49b5350f0984108ceda3779fa94d287807ccd9aaf28133c67ad6605c625672073481958c763a3554a50afeed59ba0a0490a2c8d3569a236e932dbe88a164ac59457885961b691989e9931ca4081511750e890e58cad887a75afab273bb0e0c5125788c464e0436556050c445f8cb852b59468195d2cd1f5a129b6c9448e981c72cbc4b020c67d42db21e906000f8250afee2a5010740de6fbcfc6e7ae1d435d9843e582aebbfbcd15ecee9286ec4f8f686243f6f7fcdfdd2bc8f2f1eb1a8c1cc5243172ff2df2b29425d5929e58a156ce6e97e3386eb35e8746545096cf3d91eeee4ea974a74aa4984829256d77226e7344e0f9a3572f10783ecda3cca57493d2414a29e58c90524a4929a594529a4ad1e838a5b47329a594524a1d4adf47568a09fe52e6884085f13ccf7442618c3a99bc25d56e4e5d48484848484888ba10129d3579a3d6c8d84a7b62473c8fca911d9d5489f3f0bdf3c8e4646e40393bf247ee662b5d7244de68ee84aa39f247640e19ab91a4a3789e0fa8742e3715ad746648000080022315000020100a088522a14024cb1455563e14000c62884076663c1548634192e43008a220a48c218410028021c420a486860c086f66b9fac23c2701129a5229b11d3ba68969f3f0383200b2b35f5dee2961c9ab2316986b001ac094c1ba0542d172347c963f6e220d80cbe75ccc1ddf5d66437beb964fb07f26a55c56df2c728199e2043c96618c75f1d42d510472d8069f0530ed14f9c3e2128d74e5234e96c87b1386cdc2c6610185149911211b0e3893a6154d0e26ac6044be4f41a7a3d53bb462fbb7c60297d380a9fde85831ce4205c1986a77262768609779f6c605716e989c1e5e52201e03dfa68e6b3a67cb18dc9d315fee6b766b66493c1457002f007a29796dc5f17d6703e7aeaef5be19e5a5246a4089358d09e2cd87a2397b955a3bc8b29c09a10e5fe97835724cb3a5e4c6692350acaa45da1d0a0dabe7d3b7cb3aa04c618fa4491c990e35b9dba641fd91f5e29c5178b6c7acdebca8126f5cd511be74f4ddec324969cde0aebe341e566f2bbc65f693909bae2e5ddec5a485929b02fd4d6afebc6246c7dea9e3734d313067a6a53fa82b6146094fcfb04ae166a0edbfa8016ad48c2bee52a4277dfa614f0b6dbe4c89691b34dc58d1b03a3c2296826fbbf0667cc6e98b0d8abe3985d98a09c452ab58c254d1d009ba62fea2d3625a52980f2faa83e509b668a9ba7dfd86417f512030732fca48b74882168dc343411db62963b075948bfdddb21ee6f286854dae0ae2a214a85db69fba457df78606fdfb70c19790c3c147704eedff0617703b3c36e4c8cc674d00dfd45c56088d57f27fb8a4fdcd9b6f7b54f905f628910839b33f87c8d6b8c78c021da57366959d1f8310f45015a73e3d658799dd2339546f1e103480908f476d29a08d25b04e15002bd56f08009d2ad0bb48334f232a8ba9d012b960a9138e8eac38b0c314a4d20885e03a113e10260515c8126ffc919cc6ee3fa90854807f93c3968aeb7fa828f476b491115f5b91d6fae710d305555c5b6314ee790309558b8501fe9f2ea4c5e32b35b895608114e0cec61e63ff427951d282368053c90c8cc51b24d26fd900b255201ad50edc1c9405ac8c338dd88c8ce24c6c27a2543848a7d4aaa48895a776cd4a4d734f944c9234ce58fce0be69d8cc1f1748ee506ac511a9674cf69a531b69eaa104e078f1320924568a7e59b8f6b7d042d35e61bb09de3034aacbd0981481fe008d354625620387de9e144b076b42d628ee98dcc9551d018728fad62804474c5c8e6606bee35a54fb1dd5dc92c1c9fe3ec80e0de2fd4ad074cbd97f320c024e8ed51cb94e50364111054d44628a84fb1479d2a44df47bebd6325cb8b24447233fa783968ccef680b18ba4c648264e8c85a9c80705cda256059cf7242b717657859debbcae2047585cc11626e3a7fc87ed2167857fb1c9ea01608d82a940dfbb19c27fe381397131f375b1aca3ffbacad074ecb17a58a50970600b612aa86d31467b4fc2f33309aa92e6da66940939239f188838d3b39749e0eda46a9394d9ed97c99a4d389ef79d770e096fa33894904c741839c4f32934fafa0bc9f19fd8e10b9c1e1af406a31fb73a859bb7c5e14173556f500eb127d26af1ee0c42bb727ae11125812b0e8ca4460ad8a1ff524949fe229c85ef82481ed70191e9e6dca87d1040728d526c939255ae64aa978868096f4b96e72d27f01d6f8f4552ab1014d769325b02eb83739e19edfa0371b4039e39139d6433ea5e6471b2ef817f4352dfa2c0041e3fef14185623d9282da0f38ae188f750fd94fe1edd5b64b071308cf5d85a398aaf967f242b1ed0d62c5869014e3b9334859aee46c6ffe45e5a14c555413780a93996a779e0e067745d5f47cf3176be45c0ec8f0cf2ca5cf0bd63920a633715101c6e28a5121f3d19b5c0336b82a465de65562d78a5cb9c6b27e6d4f56a9310cd47d14c0b5e8b8f9cf8c807444e4043be35c9d13e3fc1bbf74aca3081cd51050fe484398adb62721aa85fcd3633b4ff26bda5242b624f7214bb0772beef0598ff3b8c2938159e21bcefbd909a979c05361a466cb303ebaa9bbf1de3f9433a192080120847e9e6badd6cc072330af6403b49be887d5eed94883f3a6fddb062a5d4bc17c59c29b363fbe4342589d4c4dc678a4fb613f965b7fd978a50d13383533be49c03e1102e1c4871d0e67d1a9674bc5e06cdad1cbb1160dbf9a79c2a2c4722508d74c74dacf8c9b052cc9d134d1e23046341ae144ca331568408eee1d21aacba33df03db60d816d96494b6b55bcd914ebeec90ef65c72ac1ef4724ce3fc6ae08e96990025c723854399741d79e8a72e7a77a1e5f7d11d578ff2b0100f8fcdbf2712d41c8dfc3a1bf32afb8967b7df81b92b8d5a59183d83067589c366250cf699861b69546e465d9c6ec23cdb0c514043725a6aa7cf25e739ec2eeb0959ae964087bc30e84351990aae9839c3d35a4e01ed6746e07532e46990c71f86d9bd7ae3c0f08548c0c2b607be97c7e7a0ebd302f11f6bf61e9cb32ad5c0bdf505e11fadd7f6b38656567d76c4c68221e07b31749ca1d423701f5aeada3233ff830964450ba7f80620f93afdf39d21112c90a2301113d8733ca4dc6c9a4e188a5d40e59070cecffb8348a4eb5a1b597fb3c4222e6a20405aea8085adfbff39424898331dd5ec6c941dff5e6b430ff2d958208ee2d970c444714241a6db3a69e4494ee19de4eee45b6add1ea1c51f0fbf4e0fb5049badc9492faa667e1040c9cd2a1f02862ae7c5fa2dbd4cda92f75f2d537d142c75a520a35ad6a028ae2bd893bcb73e72f067a6820c9d86175a0f9416c0007533e3dd2a77bebbfa6ac8c1f768cc95ac145f7053f80320a7a7ab36a1a5d4d5d30942cf13bd86ab36d4ffe670a18e00a8515c92382b76d43a67946ecf34be5af09336468c4ea9695816f087e61e11d04b738ebffc2a5e89a30c0591de379232dcca48bbe1f9b415fb787923f86bc12e47a85f16772cf38ff993b88bc5cb2d27367923210129b0642117ddb93455e1435b747e7f1d288275cf8465ef52df29d153076743d1411264b72af8118e294666f9ed1da7d0cf085671d92c462ede49edd7f11599e1c0a7ab8268b09dfc90bed8dc6d988a2c78cfe5f625dfb3cdccf9caf220c0f14834cfbc66afbaa0318e4077f247eb1c7901cb3e0dd2529aa1e3d76e5a596d00def6896f8a55b4a9703e1bc46e01832b2639986f92786d74c8dbfd86852c4b9d92bdc9a854f4cae30353fd2211d7895ca86d9f577c66c16ea4a493ba01670729f1b3be2cd40466503131797280459c44cfd020a09bb7b6dae7053780431724995ea916f8c19120e49f91a8c1c2588d8c5b6db5a3a7667416f4c32179572b0978a1cd0dddff604712db5bf6c99c57ae1da7d1dea931cf3cf264392c3d76f153e4b506af1279a2e1409f4c1c8e4866cc20d15aeaf19cb039d2f25344f3f4d0fba26b0f5bc71949b14ba63a16b8bd1521607c04a3d84df1e53564a05145ff5dbbd6f2ff0157de6227f97e610c3c3412cc771c77d9e478e255dc494067103f28d1ad7cdea5de5cfbed76df8137c1e1f3ebd36abe77408e10b69d2a931ec356ae87e871bba66b0d058bd81bdfed24adcfe5f0b3a2f0b54c25fd4e0fb0f107711874371ebd15c75a0b03cebf934a96b08e2cd902de7927bbee825ee59a15e864ae60acee7736e7740c27113d2cd295fea23082a42506675df14bf8c8466e23843aed60b8e284dde9c85110831f602533edb820937e7a6a517e4b4015bc358a57598668b4900e0079f265f472f031a820af7d44229c844400293608a86e28994588f88fbd5334bcbe977e30580d7d651ea38138302f6dca2c160f6c84ec83323a23089eb2203f526c3bed3ea12256b7d2feb5795e6d41049a913d993a30bc4bfec32861efb417f23d7c66f648a38fe141842310315357209eee6722e200473c9cb664f9404a9760e15c058726cb8c498b47a9a939f63388b801bf14009164060ea26f7a2361cb2cabb9586bd59237a3a17d863cc720db4a81700209209c4e343ee796ee5051b2c8afe90435ac678c195cf09f42f2a8a030f2db87e79c8f117879621408e5c7f27f299de42061cae227c1ce4207a482559d67f2df2d90c0596078764affee4f9a013a81243a3d044289ac50e630d135ff69272baa8140fc7451d99b1b44d960ac612a1a432441f90128fa7b029665a0031d0ba8a40babc7de7214f08a1c8c7d84f2e57a6c9e71c4d816f73f4ede30f2db7f21fe09f1d428aac347cfcdb114c12034d9893e3a7bb0b6e8cd4ba6925cfddc9861f20ecb97e53b5ebc81841f5e822ae30096bc5044e2f67c04018596e83159d83c447747915f11d6b68bd8a9afef73b048ab693abd22771380ee6e82cc09e922926a3e15c1f0fcd604c273839d47ccad8e387126f8acd82ab17c9416148732777fa8ef18941451867af59a6840d80781422d7b9d424378b0f50fbfe593c48479ad52b1de7bb7ffb1264095e24d460dfd1131f7232347166e363c87a33e4bbb0b5f71cbf8df8e181eb0326e1f89df5b8ca70a3144b113aaf2ac253b9f8e4907949fd176bea2e06b4af12dd3b53177a617231a91eedd49809f3e6a682eca6d4d17b134bb0fa0d56d0609c72ad7f24649b3af616c12243eefceaa4c4c9ac0ba44efc09fadc5faf8c8fa15f224505606038512ef44f175f0d822020043ff48fe88aac0264105e0611f5fd13dd0138c309cc47002499c30fe5750c0b61627413c30de3b48263611912fb0201af8219fafe3e6e043ef091a4a0da37059ea04da8e2bc276447eb4af9eb3cb1deb2ad86f3a74e3b9f1feeadc1d8ca539b0970038cceb7351f4f68d71ce0b39fd9fcbbe06438c78f87c56e492dc9a517dc0c7454dba1c4b939efa13a299a900f0e9c82bc7f9cac559f4883ba67a07101adca7cca6314407d94458dba75be2c3cdeaf84aaf601a80c604970334d814010e5d2f4fa86604f9c4a86a64d12ac12a21e8e628206f654d69bfb2ff65878889e3c9845d6b3790bdbfd0566d1bb922254db07ec3134ba702b34342c48702c21a2afbdcdd4d628868ba34812f9e05aad52152937f4ee491b16aef82882af2e9f119a2b77b9f6b5eac91749316ca1bc54e0ed4b192dfcd70e38aa9bc4f3e3d00614e55632dcab0b6ea8c5449461e4c6fc40c16c0097a55922feff7782758b4edf85bb89e2437a93f4394404003d35e025a9bb82320d70690292f2d7da2d479195213e4880ec0231c15bfb9b88228b3dc0da3d548579a509257f2883e2038771797469204470d0974db671e748537abeb4bbe34b3b25cebfebc7df6bee258b357d626440d916823bc328b440bdb72efa58a6c7454ec789cd3dabd86f85d0efa140d013c6538ed7fa1b6c59ca7350d56d95493039dffa7f4328de02531a8af4845d4e7f5cd32f0befad5246e8420a6535f1a5e92771f828122bee08335f926fda81ed190d316051bf3a4f27ca852ff521cb169ab56f5ef68fa8b74a3f123ba5d6864399b6e3ebe5dd44af2d825900527692c310a5318468e38447867c2783ea51edd383f78ed3afdeba80253ed322b1b56bfac38433adb759422e33ef992369b666acf15bddbdf9856529b3a319a07634e9a195167080c2c99506e221fc5c3f236c9c18e9028952cd6b453561a5b88c24455a21fa22aabf9d662a013391a8b516137d7002045ff07641882c56dca245482ad1052b48fbb53af6a95b4eeb521dba93d9eb9278f123bfbce642c64fe461e87f74a95df65f263826490552389ff9c1f8fc3a8586c21268fcb3a5da1046a4a20e35dc342383a93b493df5c193c41afbd6f6031024b7e7c6bbbcc641aa639b625840b8488accdd7df82fe89d68915885dded95744e9001cb133915c750b467072fdd4bb150bd8f5932f73b173503bcff8f0f960133a50041909e723ee75cf7ec8784fdd55920a92021f6193258df7005bd2de5ae9011f9b54fc6d426bd68ff874232ec2b7020eef28c8467b8b6a6530eeee41c3ad034e4a8bd61c0232ed9035107188580020848a0e6323d57060c1a0b794e516184ca5ceffc3e9281b98188bf2893d118e8c2e8032fdf433b3c14074464153ffa8d27cd8f005c3b493333072075ed5eff26f626e7bd245c11dffb687c4514c9e6b13e7ad7a199d73c3adc18b56895e64ec5d00cca50271453b40845ae29ba9d2de3958011639d1b098f773179b8b574fe31b4e05241e6da4826f2f784c1c35fba79e2c46c61726800afecf79feb97c8a44938c75c0abe45d4cb273b9d39c782a5b638abaa641c7aa6eec0b12a7bb095a3c555c7733bebd926da700a518d26b50030bfd26564521575aa14c21fc51ef84786fe573418dcde2524a9bd09094c8319b95a92933bd225914c0becc2525536dc8c32322ebed56b24752dd799be07b1781fc9ce556d0d6018613eb52300fccbc2cf8550a0f337cd1b96675b9b4c0e2900a21750d014708e21004196e8c08d13f0857b55df8b00fe169fb4eba860ce502d8e1f46a4dbf650a2437fa34872c081b2e6e237466e7f5bd38b4230815039addd4ad0f95b0677ba5c6b8965ad5de19911a6a1df7487aaae4a629e1f67b1c23cc0ab5b0bb307aa19c6fcdbec7f6c05a1aea1e0f06d4eabe40f8a8e613ea696ec5b8be794f161ea5ace5d716c306b722bcbd707c39fce89c052d46ade41ac412fcfb08e3cdec95da00f3d87935c499fa5de23b965bd01016aa8894920e09b8321aabb66da6f62d14f756c432e730937d575ab40713fc20986a360a3818864fcbc1e1342038e17ae522a3f9c5ecf83f1cbafc151a9d920747ed562da71ba401638d8bca560ea56731d1320b49f13bdd4d87c16ba6c808d185818acf81bb97e782558f1083b79cdee1ce43bd26e0d4425bd400c8dcdcfeb0b8945f7272f0c0c8267fd43af2a619a32bd9478f659d62539c61bdb188b534d5d0af03646ee464a058d881537be4e2095b8ae945e7d09501a4ea6dad6810039cb62672da98adb5b902a37e143af71b157a06f85e0154dfabff1738bfde1656c3a1ed390629b78040f58352fd02aa4f97b8484313d5dde1312c229be24952c9a022a679145e081083afd3a6aaa68a9a35abf83f368c81ca6891837fc21b3142373ed53c460cb0013def72aa84d2e8acca6f4af542cb49bbae9c605d86cc4fd4fac75f9dbc9230f9168709a045a90cd44ec9a9848ecbf102650062af390c250ec00e69a8a5e4082044381554d079ea551745d02f6ab1f2b5964a6db67340aa8cfaf67d5e328a851d1a593178a36d7cad12d401ca2dc45f54f821703f5f999d2a1df2885ac5ef9c04e2f2958e6d088c885bab4d75de135ca95897b94ddc5e658514516f72224faee5e2a270d4c0517bb88614ecceddefa710246f4621e956e896970041f28e40a0303f7423aef2522f0c682aa2422d4690ac4d621aeb6e34c43ae5c22bb063cc70183b99c4b312ee23b62f5dda24eaa35e20c1305378d79956690b66ee9c08d83d2062cbb45910edbb5e585a2c0324db20116936b4b612d0dfa378a825397e7fd691478184bb56d1649baabed8879a1fa0f337863a1e522bb6529113d6e0ffbf5a2cc4865ecced69c1a715f6b76b9aa4b304df8ddf0b6d038237c7f81cea7da1eb7894d67974244c787235574b0dcca98172bce3a924600b31cd4c0b6dfd6362c8f507bed45234b21b98129decbae23c9a8b53bf16224ac4a685cd39f6cd90f17348a4cf96bf8245664ca700523f8854ec1e5c8c9a90cd10fed4e6985225e2ffe8d06c35f58b5cc865695e0c834cdbe4eb3b927fd2b572db36e7c71089ea1219c8173afd7e73f1a843f35ec0f5dc3a3fcc7ecdedfa5cc6937b41ac9176a6f7338d8b0d63b631000c130f3540bdc3a42eb93019cb6d5bdb38c1e484e0fc20ad4a99ab065c69908d1def21f0f2497d54078811f0082a70c8551d53148be6708492a0d4d7043b2380df3fc64e16006a6130f967fb4aef00015faa8bdd20b9363da43cd750f1fc539b399a283cba5a64000440ea68e935926ecf8e82b420db34af0820745a0d9c7a34139a6e1b93185f0d65e9f2a52a4011e263f875be94bd201cf888e295e9ce25a004a1f910d747a9007948b08210251d30500d56915cc619c9a020122f0279385000f20caed7b169c1ba0a0cc534717b820c1581018c1260894bfb7ce8c9ec1ce5e177f6de681d65a6c2abc063a351917afb1ff71e315ac2de2290abfa1d915ffe67df21736e40181b489b7e0331d2431ecefcf5c23df1534792e7a9084bd35d6acffbe68485bbce5d0190ef81c184d7d8a24553fbd26f968a5557304ede20aa62907068c6bbb2957122b49d56341f0de148db1543253a608d811c2d53907a5ccce1ab5506831712c069e1782a1706a23f7b7b5aed0b578ab3639641194cf035e0779904c354762af4baa07c7795830fc59b9d158759293b005aef08ba8183527ab0827c2268663587bc2377d951917018a8a914e7f69e23625e976898fcdd415fd34652db624aa33cbf40eccd19c9530111fdab13bfbfae38a7c370b83bab76fd80c12f667d32651a0d1d644d83e728727c2703c1f8f0371a403f78cfe92e163820897e99ba99da4641befa6dea30094e656b790434f8845f0bef3cb5787ba6abb1b06deea73a371e218b1ba2f2afad4d2190ca836b0610c03a49caf62800821c797ed667a376166d7912dabccd4bc90aa5522b6fa79358cdc619126fa95ab96b529fa5272a81b3a078967006bacb2072f78f46b81abbbeb03886c194c6032fd934d345d5c0932b62b086f06c152433214ffe58b3c255bc5ce1ac00da142e14832aae76ab3500f33f1205201e46818d2146fc1c4894fb3b1ab0bc85dc2e5497f2e6e305b56f23f907bc7bfec476a92e8bf9d0d9e59b5f9b1578fa628aac7d579f2f4694ba538cd91667f28b640008d9ebd942ace3a5a386e5274fe54037315c35eed5f2eabff20895fe615189725526b9868e77190c05a2a9529112c4ddb49a5d0dd15109bb6574d0590dbd0bc803f82ce96b928d04312dc2c29a3c63965dc046b9b0a6b99b94ae6663798067b03f560fef3c699a839140c5de794bb931577006764cf36a3d90832d8ccfb7dd8af2af1d3c9d5200496fbb05515301f6402d7715e6ab55da57138503afd3d8882d5d2078e86eb9ca0f1687fcf75692fae5e496e0cf04fd2ef1e49efeb732ec67e2c098c9311707aa359d659403987afdbb4304d6f1fd863409dac4887fd948d5345c74e13d81db3722dd5517835d42fc66382d3f0c6cbb386a0de0c020686b31a2ca9dde146961bb2327e7960cf4a79ec17b1a51838af7581b306a3d3c3349a272c2014db0805a0a569e3782df580cd96bcd9422996fe50968271cb194957065b8a0ef582f00fd5e260ce7f4fe461f91c883a4e3aad50c0435adc3a4563ad6512778b097f986d77c490ef84f4ca3fc2795e2ca3982134e3642e3945a1a5dc2e14cf85120a6ec8cb802d4fe284bf393ad894edb2df7a85bc5edef27f877c963fe211f599bce4ad47e35b4efaa0289ff4f1579e43c3e1671564919fe084343f7f1538fb26417b9a1ce24559196428428ccc4176632fd4ec13061ae3a3c489621890f20f421bb39c39387565c0a060d89c25227a37658711ee71921bca121ba1c1cbe426612a427f73b9ad6bc4121fc4ce269ea48471ff360b27e4e01a41ff92253641cb14fc12784d5a47ed531e9c58f76d4c75725932eeae1196bb6379f048a1f41530c3d6a2fb068afcbba5fa31dc77911aedff099960e1f3b50110be57339fc3f425d4604772dc57e63c09718ff35602f6732fb5dbd4dfedbb3111b947fb3545370537e0ecb5cf3f82b35e80245a5267c9f833968ff3b9e4ce579f91bf121b811fdd92878a551985ea18d54c06370cc991de602f125914cac5495e49d19e4de03e38eef2b2255bce0165d008aa0f11744bc83b0fd4239c6b043083d988151fdd750ce2db63537491072fac22616f8f36611521333f0109613b186dba3de70ca3763c7b34615b52b6150946d984694f0da5c143c972a2e994138ab5fc7a8ec0a29971fd304a8535a36f35fd33a838ed9ea6631eca9e2fd88128e91b2641704da23c84f666d69045dcf1689e7026b24400dafec7c3e0daa67268529953fcec88d9da6d0977d4c3405616ddfe4781747653b303198d7d463c236e0fa083d69351dd8a5413c8cde39c6477bc32c8f2cd5963184c0ff4a8b463bdd6c7850f1136107f2f0fea22d4e935c414ddf781d6b9c94ebae65cb31f7d110ce8421b132966566c088629e192a66df8caa4770ad01c1b0f8d6554dc96310f485bea07359da3375690e5fc467750aea265ac8a0604761c15e8de2e6b0bda4d9ab2072b69e244452d734a7480f291bca8006577e9a2a18833ee35550ea22fb3ee28885fa5eb19ec55d69e3abb798c181526f63c4956d1a0e954875aa74a3dfc3240549b9ab50f6c600637630dbeae0432927f400132eb9701d5e4acfcef9e1608a709d76681a59f3ed827118669afb2bf76a904215386b7a3ae978a95d43b7e291dbd9926829ffd3b8c796626056409c969b9fe172eaaf90d744772062b54894a09056e965b993a81c2066102bdc359cb0a1c761e47952111e67fbda85eb99fb64a82a52ce7801de599206f78420bc7573000ee58175a6a57ea5842a9583e655775014f5673f23d9850412e41f438bfc75b2928f0d7306b38f1a5a00b99ed558827c50221299b420037bc69d91452e34484e422408aca03b3b4039e0f66d87133008bd9f18e598c533dbf501f5fc91e74c24e81a8ba873b526c4558aee3e64a9f12ca807977b5160604b1cda6320e4629887ad937bf5d7c7065169cef2ce33001a0ca305a71a6b66883d0e46d32db504645089bdd0437cfb4752cd72c45141e83ef7bbc9440ce9ca121931c260f9b3d01fa8c1b051a929e7c41c92cb90cef6cbe45c8d9738a3947849aabb44bbb1df092713ccba268f6e9d4e322dacb9a7c574a275222cb251638be9e9433a4be8c0558d764b2289738b4e42b139c73427a8824ce91f126956fb714240cc3f81e24aa8740067eded4a91013015f592bba7186440556d59fd4e90624c00a230fea569e7288b899ec0c8306d0782a3059b1b28993f44dc6f7e79b7f536f3f81643a26348511abacacd8e791b6b90c328b8f966bd9a58fe3d2b4786528c9454987cf7dec73a7ab972f0ed7e0e0c8e309c1c7b95bf178029334bb2c74ef60bf364c52a4a756340763ea65bc7b0be1f658afa87e771265784138d15acb71c818ecef831a7ccb62cc70715312122150d8ff85315aaaa8b24559eeceb15180ad08c63fc4cc8a236a0898714774ddd6f9284008c4e8c68af668eb22cf1640c3faacee70615077a5be564e9278457663e6195044f7b98f97715826047968c084d24e17b49ad50b4cda30ef3bbade51fddf0b2fbd1c5b6e55dfa8208f781e17164cadc276c7e90d9ab331e7b1f67771f83bdf327b4cfc914b8ecc8cd11b855874d90b897743b1f9574418f1556db11280729181d711db429baf8c4ea8ba735c0580025147ece4dc3276dad1445fcc77948d8cc9164959762119d337e30910adb87571bcb9cc125a530414eed023282c92d2131fee6184871b8d0d1441afaeaad694066d2b0deea1b953e870bf0ff1f22f20c2a5ede4648ba7eca0c02d34a0926657d58a9da139ce242ebf302ffea353e7a1adb83e9959e15d8550d96c8bd64899d51a2b99013d232c7ca0edfeeae3cff4cfeb6881e87cef8143313d05a8d6a015c4468ba5d4ca0fe8b325b8361f5c5df789dfac5c58e2fcfb8bd6e966922f7cf8c0a2b8572b59d271118b6932c680ea66a19e80f18783d43a01dae55a7cc065709f35f8c99a82d1a2f6fdc09302a6625051609afecb31bec0d0eac864651813e72eb68b9fb9e029d83808321a6cf39dd1338123f8a71faa1585ca07da20ccc2c61fc0f59d2d7baf75643a7a0e88a952abf7c1eab199e9e2752912c1c7317c1d0f52eace48c2dc307a19127b5db13838267a5a8164aab9512da55c7f86360980b0b10f60a9de67bc1279895b48783cff6a9d3cb58c1b2c6eb6e78430edaee3cdc44e239b3befd49381958fc145ff776ff4195aa64594580abf894826300aa2dead7d219e244c7c26ef8ef5edeea5f3f2035d3391c84e27b6e35374d20b9713b280e43fd144c54b97c8a832f96572bb01e557b35e8b2c2d8ec1de12c1cf8b55b2c25a67703bb9bbf02655e4a2979c5a4ad5e528481ec195e974a0805d8663c5539932077ead0865fc501d033706d96d226dd36de28238f6165c5a0861544d8c68a5c5ba114dad5b16eae535a25dab60358de07899da64a10c3c40946125a2abac2590fddd5073d301c190a11e0551293ab86967e524fe1a573cbe3b07511ed4ef7445cf00177f39c05162d099f24c0347ddb80d6c44b63b510ca63eb04c3b2ca8b5d07b416fdaa7fd42283555bac2e995e29371e77d1b57caa7e87787ba4f64d5b87a696e0f7e09ebf62770f460d423f44720e11d347848ebd3c8c26a9147637f21c078c686e999bfd2fa637d71002875019d493525641fc7f14ba538286b38c7a7f2bfe4d3363dcfdc3e7c91ea6088c8232e3db4ba56f8920de31be8e4895cc8bdc181f3ae294aafa3c108bad00a470c97c649fdf100f16d7649be583bc4bc4855dc2aa2c44c40d3128efcd4d90c59ff6e0dcc4e538a9df772004d296a85312d4833da1e7a96d9fcc8af86dd80d16a1440e795eb910699163f2dd93fd49b55437534c3b19cc4e5c558b676358bb0efae247b57c68563c666c63f917b319c2e40f17ee46ae5ba034f23b75ec984e5bb386653b12ae9def854fe118ad01927ed35b8807fdf02971d689050e0bdfb3cdefd0cc09fd09c0d2009dbcf53366918ebd0c6fad9e49efb620319e27e6bb2b324a38bc549f830fdd08a33d03b64d73e4459c3c963dfd967c5fff2b4a22be69f9bfd6afa23d95cf452d94d8d67d7c1e24479db57fe4a0840b5bab760e9ce54dc224e40dcefe3722b25199032a0a50877083612adf09d9f8406b0713448401ffacb720018dfa1c59697d8536001dfb05f1e7ba9909f96018c9bd1d8cc5ccdc1d0d06b63b897421e6a54c723ac9899a4214def7518fea3278a703ed110f723d0b055961cbe62834c7af25f17821dd325c80df36b3a90d6614e8aaa48870fea5f258108c39725fca16c4eb0a92f6fe7b704b1a9a92096c1ef30eef2559da342aac76812075db40933cfc7bd74c2e2736a735d44b0b95b1c02284073f752820802cf03a5e94e9df7ad9f2a8e16c0834901e9bb5b2f7a38ea3cad1747eff53ad5f8d9fcf7c6586f12b88ae33fab3e002e3cc9a90c5bb77f725a22dc534c8adec096832d919b0efec1be03b07880f27d8a305cbefd26da8fff9956494419dd2d6590cd708e1c4c813db1fa51355450f0d286f00e074753b27ccbe384fe69d146889a292defb651b05b4b4a66c6cacf208af365e86f98453fc1bcd5c402d9e88294d7bc3ece6477adf0c35f8742cd51f7cb053b71a950a84e60ebc8c9b52bd7fc2afc2d2f5905f2414aafa9864c9a5a28b709853849fbb6e55c487f3ae0a379d30a071bc616f5a5433b74c737ee8226834b7e54b474d7989c4004f5a22468590096839c91ca51acc13c3b92cc07ae50d4229101b2b748e52a6956608286d3eaf2465c0bd272474466b9262d97bfece39a0909950aace9b971519ba4a5f78cec9e2de01170080073e5567372d479a04a20c54f438bb9a05cb44c55114e894c7b5e5f45d0346f50417cb440e265cb96e5209c50bef9776a3b943e524c751edc1082fdc2ca89e1e504238af46e12003f0c2a329dfb73c349dfac14ea8223f5c23dce95375c4ce5dfad33ccfaf0406f7337773df37c92fbf80cf36fcac244449321eb718276b01c50f623d39b8ef02af18f416996fc44e62ffda892380e310e3bc8449f5589b997762dd43e17454f0a7f8f97821207a62df7b56a91f438611e774bb342c2f7b0e83e3e7452033091e48beb52fa33bedf6b9acc4d835a70f849bfd2898efc7f7bdaa67039818ca535a72b5c90f6035421c38e82da01d18cc990d6940c652f983960f161bf7e63c35eb0cdfcf2273cbbe219c37e6270aba11ba922761a53a1793edd416b990dc95e43d00b4b27c95735fff08a1377788b2cd059171605c460b43ea1b7e5172b61b645f8f0faf4b213ab629092ae847207db38ff09b731183064656a0e392ace1443ef872eac05aa63925a146ff0e7c554c192456d9e18e365bfbf8b5da4552ea8939deb837067962124469899b0eaf8391e7ae1487fe1b7dae9eab83c0e5229898e156ba7ddaa0c37eb48b8d953dfd811236c9d1e31784936c5468d3a0a61267814bff909cfefa3b2c671d4d2a09a11fb33c479057760b0520f247e1ffb5f6f33cf7b59fc0fb8c64d2148478642b840bcceba5982a6b9f81c58c00baabff79be29ba454e3b8bbfb23f1358ba4413014246e167247aa5aeaadf2efbc751b9dfcdb3bf6935aa1e0fe64a2507bb8004b2d7af01c6757f88c85751dbb50e7a729c6bc429dcd1c51d38666cfe5cd5de6036cb57d78b8ae2ea2571f4e39c2f9928aa497b24d9cd3ce2b67a3a9ae63713540ad9c8ac226c3e997c892973726cfbc70f3f15eca229b2b12f3f26e798cd6802a87a80c671e81aa013b68c980c8719c2765b6e91d629f59f294bae62f60b84e8838247a0e10843794676e1790ee08f264adf0ba480667fb2cca82175b4564089a856cf4aee5f961912a547f7d3cbe3742124814cbfb24926594353da5cefa6d574a2f59ee2635a0f274d458fbb4fa81cc28f4620819b92167498bd846ead8d4d73fa53f53dd9ea54038d783993f9dee83b7af66d768c60c5c96fd58352dadef09cbafc0e5553492b6485670210e715daf566da12e8ceb23777e7ca53179c0061b9297d3c53b294313e61e6f20601102949507b2599a9e449784744bf0fe65b2ace59b70943b08337cf3fc35b863ee7f442d4df3cadc3c67674931914dc11a9b315aa09af599c5747b88ef9740c4ad094878db7513f168d0021ebb8135436354ad35fdeca1740bc4996e35d32658f5adf4d341440afa34935d6797420cd1b2d72f31809619b84b0287a56cd1cfa3924deb53958efba71701b11d18809148fc58322e2b59c7bbffe89789406af6b68198d61c0476b679bb1d170492d0cdc50587e136d077d84ad61d64b5c92ec9146141ec3392398e36c5f74d765ec5c2dbc2a1976e4e8cbd07de502d8bb53044ae35a24084bf537a339ad6a279cca8506e6894aa8f81cba2033ecea40c921f4047880f530ad89ad1b134a5ad5a209f4891ed919d0fc89d0a97dc1622fbaf9914102841c9f149012e506511035014efaf8898701443604f033c05bb07d4177bb471f93d26ac5ec95e254584fa00a5bad4d5fe8c89410765b59cc3636148f5706d033a8582c85009da586ba4998de74efb912c2b14bbedaa1e123e9e00c0370709cc3ea03a2dbdd7df395f0110627137bf1855d767a36d33831d31ef3b9adabc507fd466fa09fe1703072bc6edf24c9b56193278b33c62af2af440a81b1345ec024d073c8369f098d5f1e92e38f78e9e7466441604d5c4251aee0a52ca0f4f90c7140ca4f251f2ec136c024c0c4201c7fb8fb18609594dd22d40d242e4dc5d91295ec76e62b438ce4338cc8599415f72a1515b021ab02f5a99532e2325ebae36a0f16b6b90a61d791d2a96c431b6abe37e670208c0de5bd0e60496075c4085e5dae124a66151fbca6f3a1a4eac1b0ea309d8a34ac05005bb975dd1e0e5c4bb3164f98852f7420aebf375dea49de089684bee3016f4fd3d64aacd9998a03139b3b0e0a68071ba523b5b8b8376bc1ab8cebdc9a798115b8bd9262783bf4212f2207b51cc55559221a68968b935e9f91cd095eaead3e8fa3a6ee8a8896f8ebcabec9d16db63e0bbdf360d376da189af5921cd3059a1f7d0e04ff42145b32dbbd8fd0466a9e0bc3a116cdc7e557f94b5c93a360865cd4728fe99fe4cac6baea82be1b86d4699aa1c5cca2bce6fe92670c976a1e2d1aa0ab6577c5528ea120eda83f9f979c1077c1943536f9a91f8c04f13a4ab83ae88e4ef2afe90d8f3682f0c05d399c09726ac02caeefb52e399ed9614d32d326c691e3e655063881eb3b85a98e0d07a832977a84637c96e70779e8194de99ca038c94a37639703c16333b602f8f380e2e722eb8b17b1c1b5c9370581fdfd7f58b8ab5158a6e740e382d41b63eae2ad69f5dee17f5c6597cc8171471e63479ef185da791cc11351507eedbec9ea9b88127982bcf033581f023a72011b385c45f223cd1fab1e86d2031dd3a434ce06ea835087cd1146b26b752970845c054d66327a918ebb0cbe30fc39d322c839686e831112b7e66fbf4bdb288edffb9fb55506de8770ac010ae429f2bf00660e17103ad95e1132bf028e4513243c401dc0d4bd866a4958ca1bcdf0b4d0b6a5b26748de746ae9079807b79492620eee34c1020d2400fc8cbf45e749a2060f89abe2b471de0ee4b078a7e6bf231ccec563b3caa8ecd51dc171f6a08ca3f94527bdb44dbbcb72f1762a2c1c68ba6d78a9bd7963ba0db397245bcd190ba050c981bc1761475ef8ab5e2bd7bc78d378fd66dec4d9636bb69036e8b45a0c898dfa4ff4487663a9fe900f64d1a89e304fcddb0879224f5d2f87e266a958fcf0f0cd69f5f788407f8b446e264514ff811f1e40508a583408b6eacec91230642c731ee5d14daff591f21d4dd6728d1576ebc9eebfd1f01823cc863e7157a82b74745a81132bb7730e48ee63697e49d68245d85748220ca71318eea26fbc7da1f48678008dcf7808a065d32e3c4d3261c0232d28a93c26a3f0ae7f3a2117e5205749951690b4e188a381d260fcc631f1c2fdbfcfbec90360fec824712f85059a56f8d8aa0cdd2a2924b38302df8b027d6eb9bde320b80c8dc513d5b569e9735a00450b16609e50019142ce0412f1ea0701014464f601f4423c15f1abedfdcc4bdcd00e413ab955ef47809c0bf9cbcd43360f1a8b3963138b770a9c481b419d1482a5a6db6cc64e378c00fe133d149505729b722b0517391d3fd67ae5dd0535612322b50c81e3110f618472c51638bf0ef907423b948643848f7313f43c180def9c24d0498ec8c60eeeb1d6e14eb27f5e2c56dc0091adcd9fc4970ca13f593216708b893b87c9455cdf599dd0d880c7d69ce39bd4dea88a5a21372ba3580ac27cdfa248887db923460427b46870c4dfc211cd2bbfbed83f7ac2e0062579c7f92b44481fec6397d16ee4371db1349d6adcd122c56fff37190be182c699d5ada6ff2e86543d4b82ef7c51121bc9c8f0998a28f9721b3e0316d47beb17507192baff49fbcb4a957cc04632756c3ac5384c9e8174377d2126579c27bc0b7411e23770802d75824a4d8ae234faf05bb03f89f636b181bbe4b2e421c8d03f0ce619f8df2b6cf19715da2ad120825b02ea5d0894f80fb2d6a84bef9f5adfdc267d7a7e35b2b713c88bf2300912f8c02488055ed55d28cc63b1fcfbabbb9d844f6234adfff4104bf6028f45729412cf779a2010684007d6fd09924bf1be1c3990bf821cbe8fa8dd24c39c92528f3384ae2c7ea6300ff284f55f9b9deaf75de5944b6beafd93901910808d55cee99b3f0931cbe08857abff5b67b6a4ea507fc6d14fbc7becbfbf7756600f747e57f8a3501d022ddc6f0e31a28227fad930cc95d2b19b48dd571523b9374d1e8ae21f46ffd25adf97e8eaf85ff10ff2abb5a30b8790d1c7ddf9f1e6fe68069bc8ef753cb6fd58fdaa62eec2051d2dabc9ab3caf68e220285217c9261aa4d0a03f5024844af5a23db4c1b264b07700c4389a4e1561539408ee754b0390e373e40dbe89a198c218afa650d607bed039fc94cd8c81cea2615d5dba1695e52c5305b70907bab877a9f1a06cc2a4bbd1ccd9671e9effd90438bff7c0a5a71dee15ed3443350f79ceb1b2daf3a133352ef21d20c5992eaefd7b1b2cf79f9f3ca2669f83a5b6ad0b2fc28f68278736fcfa7636a7413946ff83cd66e5cd9046a7e44a6d794bbf8d574b20b1c720ae69e93bd11dc64f7159625ef0d1eeef3e3d301e00813451eda8f678fe1f93b9b61689dbf4fdd400ff8d6354dbcb5332fe63cf35e6829be2b3084f6859916bdadff2a8b1fd527c94db95f61fc9c279d863f7c0c2bb10b969e4221f92aac4e15060a8cf1182deb4e0df12db9971a563f0a074ce8a82654329d8426ca370e8e5779153390e3dfe47ecda3a06b8c3010a5128cbff9afc2318bf38a45c708fb5340e2ec6e27cb845d4e80c4534722bbc5958567109485710d61d681c0363e2a817dfa71697a168f356b5806b36594a49612add15d03eb495af82eba91aaa54dc5e3965fcea79a727fc055b06b16e2a102d0199b0388661bcdc6a624d67216d59162548123301210726ca0c2ae5d1e49f4817bd2a8b6c641e446bbd5d2ae02e6984cef1924e44b1a8358e29668100ea1adc5ec9e00dda993273c810fbc63ff6f26bfe57e3b61fe8784a19315b5e4990bd2c5285c46f88c6ea0dbb9a6c236ca16f3ef5a5e447e019deb9d358b5aedf731692f4054a51d89c0c377c1ff9633fa1c23c87d4d85fe7d6add8c02d020c0fc3edc047d7561c25e7a82a1518b1ef8357bf060e88345fc482d3a75f2f9e3acf54b2334b12b52ea136fe9d0a7478dd7758be10aa9b1d2eb1f877956f11fcb603910e5d63660e40419a511fb99c994226351d38b1944935b0b77f03d6b7b6d410c3599fb1a4835301a546ca45e9260f94f6681f67f34d2979b23922978777d78692fe2486e9790876d7dfdb6a32d122dc802624f4817f2404062e60744f0c9b12af97cee77652cbc3de99914ae4fbb85986e29f8555e839b2c0375cbb246ee1f09d742cca1bd96c0c46626873f87f5fbe1afe35dced52d279c4b51e362f7652660bde0855b8921ed8fdd19bdad21c1b81001845d99ad29c9078ee9a2d73b3b8410d7f94c6cdd02cff475a4322a1fe12a22cbbc019dfbd416f34be17efbccf100c986f2cd13c60e8ea9c91c8468600c191e3ac590b3391eb329e9d57ac3a55ad17264c18504c2bd134989eaac933a939d56592fdfb79fdfb03c16f174fe5a5d3913f1a9f20878829b0b834154c929bd3a0c98494276579e207eb299e5168306d13d452ea1fd36f2208d9aedc38720c0ddad9c6a8b9144870d29b232c2eb29cbbef7403948bb14539ef476facbd826d3b16bcdcf76b47217de23833da874e9163091eba817a53e26adeefb7cc45eb040aa2a1f0bfc177bfed005ceae8d93eaf9639d365b8d2750aa4256b8408ef2043853d262852830c8468832e542d53b6e7724823b4d4f6a7699b52978483eec2c81a48c51599f4cbc4c70cb29b8673315da23c4b9d0809773084062c7f28a3bf73cea51c17129952bd0e249ebee5b7ba64cce94d107f1ba134c200538222861c88ec4ad9411bc3044801d63f56208bc1fa6ec048c37c01d2d10cad53d623f90d1a14f34715c0afbeb1d607d55377e6ec12d271ee2e7ba37b1d9c3e3600e360d8dc3f08e0c47ead496fc173f0022ccb72e52b90010458de161a5acf3eb554bae304347954a41077e8242d7c9774014642f6534a4079ff223148f8d43b5ac1da1a406fcb46ca840fd15edc149191081350d431292d4b83fc31e78c4e401efc0172276c6b64c4e47678b35264fd0bad540f4f9150b6585462881b2def3684633c255908e1eba658474e153f15849aae9ddc974fb4a7aacaaa66593b9c8f90a7da3cbef0ac442d0df0e9e82aab923c332f43ac4409181d6883c55299db80aa4db603562afa99fa1696f8d40a4be2171c7d87e5f8df7197a15bac6b0b9352be37eed5dfd1ee8a2490e58667ad207ec3e0339859570e829fccffb6b9ec7e3c972db5f194f0a025dc89605d763aaa97c106587e5565ac710a9088a3cb61ef3a0d7fdc68d6118e0a7e31556ae7fb81e6341b06bd22fe6bc1ecfa8b824990bbfecaa802b3a513655408dead489e9bc965770cb23731a5570a1434f2d46a225198a22898f48783e6ba1e11b99ee14dbd6cbe859f7265b8d8964f5f6f050c1bd09bdf0f0cd015015e0bc5b95f1801bb2dd63ef76c1fd632fe7aea94d139e27d954dc2e9971fbd371c318e5867d48ce6dbfe3c1becb71bd4c704b77a540ce30d8bb91cd617fc440e7435083c1e0411ca0fa77cec291b74f99a5ebb84108da04417e49801fa8615d827a364590ce40c874cfa06bbd850d4989c9316e32dcfb0949137629810546a065d7a4fa50f1c656ccd999d93fbe5fa3dcbb0c1872cb8be0e9a789ef97da90bc71a59ad0a0e51a0dc65aaa28488a154217cb2862bd136f2c0af3929356aa1648c40d478f4d5471bd85a39fd38de012c2d58c72d808ad2d2e19f8ff5d63d56ea6792048ff46adc76d214db7d876d4d9041a4b03e3920f9b4c04e44082f4d5547d3b98316fb9617be939f0cc51742cd3ed972a6dbceb637031d7606b599fe8ca6a0a9ba6e0a8ca77f8521ef7cfc6da88dfd40d5ac4aefc8bd7834730d187bf312a4d564ff10af5f6cfa4eb3152f36c570f88c9544419a9feddfafc965ada98b58d1edb12e2217d0c643b0566d855a6eae963705a6d34745ac6b4c3dd3710aa5aaab97de1c860d2524581c4f8109a588a12eb3de92736983f95a6094483ea8a12b185d1c62e5a6cafc9bc59a31f4a67fa4fed7fb675c1e0b5fd718385210da6d66626b7cd1225a4ddb6b61b06ce3a2ce8d66a86617e2c2510db2ec202e164cbc3c34a262c038a430698c4753819966af5345bae7a83aec709fc428d83563914166aed3b5bdb2a1782faa7a7a986fae3bb411a798c09b5de976ead0768ebcbf1c7452b456dc297c4b8d329d073f08bd5f15e1af19e23f0049a05c8ff04136ff1880473210f4aee4d1206f077cf48f795307a4afcededcc29354f601a545594882f84361651627b4ce64df9b9d2f2c1e46ec9778ab8b06720f4569636f8781dbed9811a5308c361d20e6405c6db6e48f8c58fffdaf6ceff313a72a313ae6b079795b5ba526f683943150d40431c120e1f8485c637b20ae0179f83b7ff0d00f71576c6c59223467cf86c8e10c46594e66d115ab6780e16e857fc3480a42bdd8c301aeadaf3285d301e5fa6bcfb201ce92f0a43771d43d56249d0f31c89601f107a0c83f188484f9525803102585d8b81c12911bffc20ce7c0565265df355ca475ff99a74d0da9486fe5753d71d0fe268e8a0ea13435dd0e121fd5e1b1e030b8f029af008358307b0ebb82a02d24ae18a0ef58a49789edf9f739e1d38a26ea1fcb216847ef4f100d09c80d31a1c10595891c957c5420e22ba53765f8894e5816e9a2f076456a4d0aafe007c48f7d50710e8139bf7cfb75ea1a761222447e7ba4a307b8f94d019a83491de99d480bf55a35b6f57776ef0a60cd7518a08f773c1c458e3a313310e75f8ca0d19f818f2b9491fdd615c7ecb65e7b7a59d76ddd42095750c9466ce0eb281c05913529b63f642cdfc26f7a0880b0866fdf2d92f5ebc88c85f57fe43832ecb3f106de9d8cea211c45a0bf404bb76fe015f7545567dd89adfd8f522638bb28781b9d7bcb10c7e5f586b85b9e43de27d57bf1567a9aa0bde83450ca1fbdf3cf4a820e1eb01ad379b3a8373930ae3201dc48ae77a8481af53316552c93abcc8009e3023678c49dd0b90e43168e360c173c24fe55f075f422104ae59c9eb4e869d2fbd48e471edd4300a3245f281a32ee6f1800163d69a74aacdd66d986f42f76e8102b2cf95e63fb9e6fb0a15946dd32b2de7053523016b856d5da8c4da9affd875119b2f587afb9fcd928ff7db722991f53942f45118d1d29e24cf49035171854e057d9aa4df14ef1b2632c95f7d118c728e073a1878b4fb3594534cb9756b0725aa977724b90fa05c4225cdc65e8c8d394784536241c95e47ae1d7e76db9a2dd22f41f6c8598ff733745bcd836fbaca327597eabc39e113e8e08604846fc95ac64a77060007252acc9e7366784f6ac97c5ac2693f2e2aa8b37cca8c8b05b362defcc4c17341b1a88c373ae00e912aa3713898fcb8690f1cd82087d9f8128a200fbf1ca9f97581d0170fe4cc716f877f7b6683bc381d9b8854c07f2a4961fc7ded15d34a77f771e543a90e191154d89f0ce22516c5504db48641e2370d4f103b92a84bde845f39fa1e5a8ec71c99096916a72a3db2a26f19690d16e1379bcd0ff2fd63a22e9d1f10df4b8407dfb1b36660066f6bde4abc8b37dd928caaba65900f5b4349d0ec42f4d076cba0516918e765a8f965631ac842cd5968cfeccbe0d95a4e7e41dde4d4430b94ecea01aa311970a2929eacba67a00ba2e879c09f95d051a1025d7b3cc04349ed874a0a79051501cae02d1e1bcb7e73e6e6bc661c8a52ed7c3863a8529a11d59282acdf4aacadf11bdd2e58ec51ca60f37ebf7b23b791cefe5ede6b8b7b4589e3858317fe93d91ca11bdd5038e5a48b8aabb9bf33e0a736d30e6799f83f67a7d124cb7ab86ad55c334aed17b56b54745ad34b302584bb1e6d9e1885b0ed433ec07eb31cc3540f844aab23c950665e7f16d7a238e47bb5b97e400ab6e9949d8060f6383fffcb1e80b2d68d7790ed2fa7e1579cfbce4e8741dc6371f29e277bbcb8c319e3274b7634d75abef9aee53e60f7661f9db67b3fb0c48010aaf9515fc68c8c75f8daa30f1eade3a8fe36a7827bc4264b4ad936bf437675c6bf13ca937f04929ef9f03123f6060f64f9c9b63255ae2cde91b02c167e00c29be001ddc2b0a9664caca5ca980da3a683565b19a4aef40a0cdcb9d1088f1b87b28beb94654fd17aad7477ed8a8f0374580bdb386348eb51612c6fc38658c78c92da5d319617fcf483ce0e33456738cf777b437dacd01dd4cb437d9fb0a14f8bfe61f96c37cb817b6f5262c425e333e726ac1df63d2ac379eac319a2cf2d85813c9b5ea9096024bc4854a823cc318eccfc7963ad51d78386e4c7f2f7700dc654adba7b478fb8c5fe991f4c359643b309923503a7285b06071b8199c3c540037fffbfb26c8f163f50e2f3b741ce70e81a9bf299677be8b5648779244235823fea3ee7c8047ce2396cc7409acba0cb89f3f7239354cf8a02258a372a15ee9a39a9b77d84dbefe7c4986becdc7056a9eba4cb6986e75482734820c10d7235d4127f92e922cc57189fb6134ba7965698a289333cd4f20c914c9befe7348aadcbad0395bc9f6aceaaf50100018ace8f69f3f5e5f69466557e803a9d531bb5d89adb0d95f606f3a8ec58519ad54a3039a790f74d2c0bb7c9a47dc69c643bcf7d1e1621ed8b37a9accd18c754107bed1bb6eb636fc08e12458124f352106d60422ca18599316fca082bfcb84ca210195802d2fa5975cda6f0d109efc473fde4ba8cb6b98b0b61fdc828b678b20b7a7afb70ebfc46d745c636c926882bf822666bfc8fae4b8f4d9566a919b76b7989d660f2bafd4f576aced07de194298dd5609ff203e7d23fe1b6852cd895814b6a87cec00abcfd120518f4f4b6250f6f90f8fa51408bbb552d3b9629e7940c98e856070f0c1751cab7aef17b6f710a153bd1a233a70a023870d4772f036f890fbe6bb0e1b5b3642ddcc607f57f910c59c06715a937cdb16440d3716c3e557ce1ef867c019eeb683f4eb2467d83346054b4879448fbcc91b7480b09fa6b775d50f67491cae2301371004492d674709e43c67f0802ab0c298210b03f246473f9175815e416f5db37600d5ff4ce2733955c167a47409358b1fada52165f9df473d22f56b005b2efe4a93cb67627d1b305ab10a6dfccd6b9cdfbbd6b4ae705f6df6f4a07ac82e7b2c58595dbe4adf06dff99c8bb3123eda931320b91d5c85d6cd25ffd5f65a88ed32675fbf2cbe89c56cb4393bf4a67211b665be6091c98a2f47800e30dd74f355e6f9c4137ddd1cd23bc0acb95f4da7577caea8a765ccb2478539608d0a955c1dd85e37491510604f00295244d0ede8abde553b7b46952c67833dc73b707692fab530c6c55f3f08e9d0758d7ea2f47d6fb2dee6eb3e3c7ae0c94fd8811b1460ddf7bdd990cecd529858cb6d313c051eac1fd32527ea1911781d78ebd57f413b0fc56148c1749142f331c42a3130911214ea5d8b50276ca3e4ac2d140b4dbf161149b40ded54b99a8661c849c2ea330e5b9a5696c3fe7684a5679cb935c36a5a20aa77545a1db3777a67482db5a9bba07c4f5e24becc24432944f22269a35d110df54121ac0ed28912efd9361149527180bc1bb3d0d5c783e974e6f14759d6de560adcae13954ab44a19c91e5110e2a823edbb8d51a19a7f844ec829168d978cb765d9ccb6c933178ba67601e4dc4453781f03bd1d765ec84674d3f15ef1191dc68072448d31156058d7da4471fc776ec90dab18afb204622c15ed9f13479a3721d15491daec7fe05e91218ff910d6e2a50c3682c01d83e1ebc7f1edd609191ec5fd4b25295a843ee393639711e8f3082179ae7735d5ba2bdaa4f6e5610163989846ef31a094c32e54ae670b6721131d5ce9304feb654fef1bc2d5ccfeccd15e56e8f3cd14264b72b85df9589de0fdda3adca541627bc69141affa990691dd5d37df0c834c212d34f0a0dd4cd5cbdcbbada8b319afa4b6a5d0e9215191a028de6466afaa2d3352b483662c27ee9f3b6a2474285a6ec05036a669be84d3ccc5b6e17dcdfd4e36d34687cf3459fc7d6008f6714ff53a4370ff488a2f39ab366823c559c9cd9e0f75b98087f7ff6b5e94c1850de7f0d89a3ce6ec96fc2ad95a75cba7f99b3c76b8c5588051d32d5bc9d4044d784cfc0248fa53858df5c6d414cbfc44f93d54878da618175aee924e8bb6c50c37ab0a6e2911e01f583f0dc499545b5da3e15ae462d0c883ceb5607ac85245f2b757364e25d8d9af6e5b20f174ed15c9c6878d9e969f0d3be6ad8ef1fc6440ad33b240ee35be38bfc21e6a0a34e9591e1eccc43b04a39a347e4f97812e3cc33a7ad9fc4b6333df14087669e45732816ab12e0073c371c69cfdca3f629db27b0e87b21bf0e2048250b3e02bb5e600b39b5146e708782e3247a98ec991f80b1900054cf8955a956620e8865401c0061c2673d0c2e858a0924e93a4cfbe9cf382c00f89cddf16ddcd4c0ba4bd1c0e90690d7cd38d887aa61a69451043dbdfe1584489afaf2f2ce2fee34cb41e416bc9077c7ec4f8b07307ef4c4a6084dc14e06381039c1746e1d6e53155b01703f1754c7b9ef2d63720d1ba638ebc6a67813254e719576f769d6cadf6a7825a0a104408bc9f7ea7982c9e94d2f7a5770d5a2755570f605b2d8850b7d5f3ce9eddda394718dfa1716d380c715773ed9a4059c8c71844e802dfa42d021f07ae38130759c32624c085b6aa3079d539e1f09cb85e32ca0972214306a4fa8fa7ee3f73a866d9edd31d026fac9c4cad5a6da4ce9ad154c63e8ebf8486819800aaef8f5062896db657352b28a2b82c15bfb2d04149ed6046b0f688003266db4e2d872b3504f8f821cf429b7e071c72fcb44c83047272c6271596041088b04f5f7c4adb1e0b229a6b4997931b48d379550d38d6a4ad250de45e2d4ee96ef0a0cb600230c859b0ebe0dc16754b448ad4af546d171ec01abcdd23514286b3387015e3428534ab1b807f3f02706bdcab12c3db3b80c83ef1319a48006539ad9eee8d9ff3e712316f9f7707b3cc10b56dd6124a6e76a1d0d232d71710ec399876fd83683bd683e8f47445c92d52ce8a33cd8a228e57009a2663888e94105ab548149693b8adf404cfa52a762850c1b63c78afcfd41d8817d59aba05947f2e50cdc5b51b5f97d9e03e77eb74fd9b33b4b53c30cf41798743f1a5480d4773c1160e918d2624a50a742fe6d1b356077c47afffb1a5d7b9147764816bae9e6901c038959eb904d8b0c32c39035691e129e8965d28e48770e19f957cd9679fa42addba57d1c7be4e84101c877116eb1d6e837ab2353e2f32e16f6fbf7a6586d7feba4fcc1e0e8ab02a709a203c099b632f60aaf3ee410142a7300dfe346a39337c64606a48300cb5e87dc938ed3b53add121a69aa5e346a22ab3cbe35aa1ebb24d801561ff4f6a958156c89dfa763ac779f00406e2659a5837d6ad805ce251a5369683ab40b101406e45125c9f6aaf329367623ef920b8d34002e7b5f96bc689529cf65300e3a65ca05a5261d6315d72684cf58595505792f7313a708ee38c5aaf5850e4680d1e26828125622e59726657db165c370c39ba2ace47736056dec0326b9070bf811e8d96c69baea48a4d87f463338d9a7a9343374804216e434010124d42204b557144dcaa611fbf15be68fbe28fb9b7c065eee2521b2c2aa5c8cb9e524b9457eada49c24cd36f549f4061c5241e4acea1a3e09f1aa4e4d97f6e740161dc3698487221216899f34a97e686abb7a8aa809d2b4177cf93ba92690ba1396361617e0784c0949315ad18e3bec9c5ebce9046889a7227d1aa6361e3b69ce0e06ac26e4abcf8375e5e774e0b60f28f31a50ffc6971aca6d53e8d47c3754b178c1eb0788fedcb642f53265e38f291ca3e82d098f676ce72c412ae3ce4eef423287163290b7d30464e6fbd9e8d4fa5ffb9153fbdc61f161511649ad56e4360ad2f258a285cf3cd2fb643d8e69376d1484373f872f6898e5d931f294754b454c74e55d6c0f8e6821b0525380a4fde4471dca0f01b3bac9500a4a6dbd9ab23a2c48266d423034f19b920e354473021252c363ec1f44873f843a6214b0036aa192875c3052b29a0eaae9682e3b428694162176a589566bb5380b2033e181f5ab864a7531cc6f45735624a910eb78a7cccc700c1611f5b88e618e415875a49f9d0360ef8c9d471717beb88b857be92c3e3611111112d2f6967b4b29a59449ca160934080a08b8e3a1b35269f85d2e3a75f62a4735a9ad54866013e3982a9f096176583f18ccaeb9e1201176a84517c3a65001c51a73ba092a16869f4c580dec460d9526180684c4b1c69c766266318681c7d68755afa67e08bb51bf1a2ab1196361b21eed9a04a7beaa5e1c2ab63eae3d43d8eb8e443d39ecd095a76013cef34e5badad50887a7aa220e2f1c266a1533b12f118e5e0b142e99d4e7724e2f929aaf22d14c95244871990c080441637602183a203221840e1821c6c240184961c4431e5065018d939e20a12a01e9a48e901d20d443120a9810f2cb072452808a1228cf84110a6b002071008c12305862331d810748226560004911810b1840cb8a8d4526ab728a1210536388125083b3d62dc4087a22c52700425ea0d58f8e8e0892931d0c10f13b2ed065b0421893205143d2841a5072168a82284113f5f24f153441158a468c1c1061726c0f09141083a6009a306113cc962891fa2c85cb042ca8f1f8038029220308192a58693201f78a0c40a2f6a28d2012a508adc80c89222b39f0b041d81e4861b5060842c92c871eeb889343058a4d131764f28fe797f6a299fab724e77f726534f59659caea7add55d506bf8e11a3fce38e3a71d9cf3ce39ef0c9e95c31a578f93914e269986946f6ae2342621293ad42621279ac3314ad933927cd0f14d42d4249427fd4c42719b846824991ad3a961a83f6dfc289d5bb4c0e53edde78cee24bba28d73bafb984d00770f11a0837e2e970deca98a60ccd1f989b493c48a23238cc8c288e80434bc70020b032d3630c2891f183901921744a596529b64c42729082d2ed08ad080892e908c11c5c80c31466a80c4e8871910662002068f93150451031fa218c28a164942206a4113488e541fa258118c38c1119e00a248173050e00462c8f0e4ca0d474821c396229c52a872854f922c4d8851439448b62c619a1d810220d8100430963cb9a24370cf0c331f26c3c15218da15b118230967408807258c00b4829f246218630bd44e13141421c2072d3bf080e8051b60b121071ef0c87e767c48e248500c536068a244ede9810b487a66d0021918b1a2f2c41d2059428b2d43fc64992d416154a5821f9608b500071e92c01586318250c580832176d002868c89185440082f7030c30d2f88a286178660a1e881912b726045910e2c8840962b5a8a30c2173fb8a0ba200691139622285baee850041617a0d0225aa094fc082f62288a9105083df041a202a9871adb216717848045125f0c7591c30d47495a72882206407021bf08e30743a19144f4440c0c2122665b7aa08a3ca84106556e10a28959102cd09bee26feb518af40b2e9eb53d3b40f743d5d934995514a6b34a5f49f524a698d8f6c01d10b3df81d895e68626f3b12bd2003257cc95196179e6062862470423d271d63cf398fc8c0980113404d70e1029c885d2e4d06ee3e3dca10b113d1898691a1c82379a4c41af9184ec5b03bb12b4f28e1c20a27a29072845331542085082ab4c0e0899e2240c043d81e8af011824881124a7890d2242a11050fb020e2420f482e205d23502d48582d9070420727ae10696209096c7164c90e8698c2083330722c251ac6117cd85b7cb0b0e383859ddbc456eab43371f97625d3142a262fe2943cc7e93c5b64f6846b5464e908b4564c1fcb04c81d9f84d1638c3132b9300afb60f5085ba9536e964cfaba0db0e643537c06d9b6d9918808155bfb74c785c68f3d26e9300efda8a9af7351f099fbd63b1cab8da34ca8aa7f336b36a537218e9386e2a4e08fc80d7050ca136a98d01fb5b0e0337286fdeb4cef33269ad5965fcaff40109fe9dea5ddcf9df7e2d1792f1e9c0e7630e270ffb0c765558e7f61a8daaa47793aeea97714ea7bf140bd7e1da82f3aa8fac21f27ecd417fa0b911bf5dbebd27b9c274fe5a0d463e8b066fbabe02ef99da05fbffc6a5008f2811d2bef048fc1ae685423a39435352ad78cf01af937537a53e87f8fa14c58c20e67acb67c55b49135fb4e1acdc69fb068af8b11bef94dbae74b0af4630c8b325ec991c3818e537aa6ecf09340e289884cd951ca8e444462b07790c747c69e89c9f4c5ec458f5f3f47af7efcb8d0f7b3ed746c9fdf76397ce79749c84022dae44f469491db278b88322dd0a18c4923d1a6f4920335b63bfe2b559366c5014d66c7c725ecf8dd8c1d9ffb6aec30e660c31f35db9fca586c28c686b412fd49836c9df83f38fc510e209403b8f150fac7ffc16d4aff8738c616ff710ad5cf0764322499c481a5805b48416b01bff6459cf7bce6ad7f84cd188739ccb5dd65f51a04cd465701fabe7d1fa45dafbd6fa594b2e5245621edd70ec7b665cebbef71d7e3be7ab83eaeb55ef9dbf679dbbc7b398ea33af66ffdcdc39f6bf682f8ce893600c20641eceeadecde769f69f7ded9bf5d8efdfb2d215c52feed78dcb71f646f6f734efe5e39328cbfb34b2fbb1ced7b69bfb3b59b1906d93fb87d734a8f43ab3e7a682ef9375419ae77b9ea17ea9d4e9672b0953aed4a339bf4ed4a267d3ddfda642b9d1e692047f07dc044445f598b2a92cc2f63a906ffa63b0f4fd93cad6e934763319c92179f18e2812c86e338d9b3456678c6488c918eecf8859a52e1554c23afa28ba3bff7ca7befbdf7de48eb0973626dcb558aaa505154a50a2b91eb4a26ce2b39310979a147d9f14f4fbcc98eef312dcee3312dfee33e494555a828aa5285152a7296442e913399443a9147bc8b2c48b10b2d4973283c61c73056d9f1ab8933e9933c9d4e5a9f4ea7130a954aa95417634ddbb6ac6d39735cd7954cd4649a2e4d5c572a994c5a9f4cfa7442a15229158a9baab99aacd66ab6a66bbab870f3e5858b5ef4421d67d0f0cf34b858c3e5fbf442eed5e990fbc54d4f46a743eee9cde874cce0a647a3d321b7ab06ad5163baac814baf9c3dbf90857d3f3c037f2c56abe572b9b896153dbf90eb70bc70d15f2ff9aa2e2f2f2ff4e565fa8b7c399d4e28542aa54aa53c954ac9542a954ac0124d5452f47c988a0749e9123dbf10bfea967b670fc1b9c8b3e455c937fc468d7f34b06fb2207638e2471df41d1dbcb1430ced8e0eeaf02d778cb0185fd2229c4e0ae3ffe0fc6a1c94afe3300eca10f6d9e77ccf89437b3a0c461453623b7521feccbd04489bbefba35ed0b5fea8b22c3464af5f87d55aeb9cd3febcd3de1d975082bad05dd160cd0e136027dc4cea529a7085a633eed29d892d4b6c07c119b43907bf1822bfe4c3602f3af84239c2642971e2fb3f510c03b48478e32f2e2e72ab38eeedc6435afb9b1737abcbe1de7eec56ef512b772ad5e5f0bdfaa283dbcac6157af5a5a73eb3fa5e42e85e3d10d35efdb6fd6af5f34fdd03e8af3e2172a71ed5e548bdfe1c004e461b1b3ad02195c9aee99aaee9e6fba97bdff4c468dffba115cad607d1068844da4672df4f750fa06f1fd5f130f29999853d42753ce85b9510b9555fa87ad75b2a7c26dcac159fd1d96c91cfe88e077ecef4c48f938d9fe34c5dcee6e25e68debf56480b5d0a59df7a1dd6afa28dcbdfa74aa2cd8b07c4b47deb592e9edc5de780ed57abdfb67ff1e25efd8dbb7871b3364f88dc2c299305c0459980b8e36f2fd3e5c8319cb49d8ed417ba3ef5fa6fa7c3a5d4d9b842653aac0f22f7ea5d9f8d2b3aa4b27d3ff418eb6b7d21ebb5bfab4f87f5abbfdfad9eeb72582b027032dae48857a60d231d7a6cdf0fe9cc676c687aedefd3253ea363fa8e07ce317d8e659cb5841db46f1fdb0fc7b0846d676c1baeb635eab868c3bdfdc21f305048dba72f17e3c77f33555edc292f6e9477f242acbd20d212d9f74bf8c6e75f6a89c498fb37ecfb214ec1bedf5dabe43ee7599f2873bf250e563aab5f1df41a71b090cbfe7dd98f07b86f6cf937b6bef762fcdd10efb72df93648a418a5348a094874090d4da26f4ca31d7a94ed8fb49d278afca2e989963b4a299ab243179211bbcc65a0dff8b4522d061cac9a763a39911853da1663978b0a913843bf9e16a05f1e691e73e4311ee3435ee74454c167e4a61f2d067c26d4c1810f031470b0627c33b7af2037f759a1eed07d82d4eced70b07ec95301034e232b3d921fbbfbeb88eed910433f97bd0d7ff67636fd280a3857aec19452f72f626e15a429a594fa94bbda41e7971c9d60fd79a4627d84829bca582591287fba184a23bb7638b4d055dae15e0f07ebbb00a11d85af878343a6eff3239fe6b9cc93e8f8ed3307eb910e872771b0beacb5e9c72ea7b5dd48fc59816ece0bb5fb34a13bfa0dad88746b47221a88d8398cf9c6ce1926e7e79cc673fe7cb9182fd68f9fdd67d238951953613010bc5ff85f574ce8c88111d030db3b7ad81043d7a7a9af0b407d0873fa50c69ee2607d70d2a430ea5da7af82430e7653747df9a1d31973f97cb17b8443bf179e3077022603045ddfa3ab91c714ff08a3d6faee614f3382e6a1b51ef90e96fab055851e2e17d27421a90238c054ac312cca0419c107a2951d672c084c3ebe57ea61c81a7aba093885b115d4459a9ae48e38cb06f2a64354cdd78aa03d94b34e9f1fbe0ca245681c0267421f7503139fc7a13fa58cf12839fa17c28a38e033613cfad1c347ac82495551e1b4bade415cb16b180e39c26c3f3ae4b81264cce498018c2863e328e32f39f8ef9f40d08f59ecf97a7a3784040d2f54624c2cca31839504e498c1280e206e302491234c11a820407321688256c9d8ee2e97f897b7d5a04319d8ff7964f9302f2ad06068d803727497b645fbfff8746ac39cf1632a6b0fd3be17fe88a3bd148169580b2a9d73ea806027b6e327646e9df92ec336993e7aa553b4299d3a54b4e9505c2ada70a96813b36a156db695c68a361aab65eddf6813a38c67a34db4b12ef525dad49728e3f4156de294e133a28dcf8832fe34a24d7c7f196d6844991af184fb1e65fcdd15e511f68ced304ab963ec74786ccf2076cf38b14f579422cf899d827856ea8558eee8e0f482cc7d4770bb1cdac728136a1ef682cc7c3909e4eefb8fabfc6294911f411bdc695de2de0eef260ec5a138148750f013b60f0b3c3f3aec570ab605fcf67bd1c761a2e11d37274cd78c9934e8300e590b031d9ca0a452d26f68a6c52a8889bbdca1521a9971d2e09f0820c08e5b18938ee6cf1889c38618ffd91d63e66b52ca89f19cd5290bb2932718c87e440ca41070b002b303239e0cc15ce150a2bc80688699cb000977e146f61e9c873a631723f049d24410a8ec245bfafc3401842ab647a47a64875cb6dc3c61077738e88f8283fed86502cc8ee98ae0ed78d4bf737e9df5efdc1163fd21a9e8f93caaa44f75e87ffdb48372460f770b0ac3575fd0a70a3d288d451189a77aa1553b12c9c0c5121a65e54ad128a21b8536195185b46652850c3948a1bb1d8964f091217663c0019dd118a42002450c4dc8008550a562da6a6da582288622982882143ce4509fb80d6bee4e832a4f38b1b3536aa48246bd7e74624a54a27e3ba2949de7075344474abddf43e40e75ea8339b536a736a786b1cb45298d484877908a9538a7c4d8d648a5d60a524aab9cd3e239e72be7ced290943e69b5171760efdf326ce7e8d28f9febb42d0f206f3e58c35d891b8271c9f8f82af4d83169b6974202d035b248ca3c1f170adfc3c71c123e5236637cb4beb600cf01a498147b97918ff4299a3ed3c7c7c7c7c7c7a44d781056a30b702ac04d41c1944e4dc4bd51a3d3a5c471a04d8291c465cfaf5510c5404387635aa00822560491c50d80a2200205b12d72f882083440c1d1810a8d6a0f63636c829617c4889203212250a61157847e20f3811638a88233a59e15057bbed0c39eda9b98341bceb6e583b2e7687b0e72183f946fedbd2f1f3286835feeb046f7a676570c718c47e8cca34d664cf47fe5d46c7f1d21736fc0738c268432467da82c4988cbb0c321326cff4226248833260c3237086610bbfd8fb6378dfc68c7f8d20ba7951d7fc8dca94ea750c519638c739a68ad54bac7a2cadddd2d0ab2d76969a5b3565ae74b9f95d63a71f867cddd5a5be7cb9526a5a53f632e581f4f6d85203a886bd3f992d5da32ad955b8518af74567b7ebd126bc92729afcb77f71f35769d31699ea1dd94524b7f06716d5b82ed2fedf6fb7994a1e16cc1876c7f39e76965728f1541bf9c6cc501b8f68b06b6bfe46ac7636eff58ad0e6cbb8a32f45961b8da5467fbca6425eec9738bb3fefc6d7ad402d123805700cf0051a662d847020c06f3191260b6fc37600ee698e2e10e028eb37da624562a180c46b5529560d126a69648cd949729a594d2d2ffe8b167a8fd8f1e2a956af53dacef697dcf64cd2f9dbcb0472a552a71dafba71e6f2a3b4fa530a77a80f6a92fe43eed63a2fad80d81e108512a614e552af540f917f698a999eae12a95cae5552e2eaf52b9ac5ee5d231e6f4abef393debf4adef893186f517e5853d7a6c13ccd5f2589ecbebbcb0874aa552a95437ccb6c69ccae4a92756abc779e22f7da6ee5e8efbaacf25d56b547b201cfd84b8bca6711af7f4b5cc7995edeeeeaa8fb1db11555ffa1b4765495738ead87fe5e0cdfd8fbb85ccad7aeed33c1b5bb4f672cb0efdb9ef72ee962d5b8eb057bffa42ed63c2fad80d89453842a8fe9543b73fde74abbc506e0fdcedbfb3bdcb296dff540f70f7efc5437b7f6d04b158cc858db3e94ba57737b9fe2cb9d3873b421b9bb66cfd996e6d6e3addb7ffeae35ee5c528c37da1fc42ed4b3b425a7e20f7c3116cee13e2b2b9a714c87d10e8686f7a1dedfdb37bb14321f40fc8fd903eeb7bd00fc87d4f637d3126d43e6b1f10ee634284f53d437a76bed56ab55c3326460284318a8d7ad6c7ae87eb6f9ce5f50c9149428b1f9410020d78b082d3fa1d263878018c209000e1420d70582c16cbf5a11009807ad6a7674c7d1691ae9f7a3d6988b4707a5820d2c2e919c26ab55aad56abd5727dabd56ab55aad8fa106a7f5f598c009247480820508404238ae2f461d6350df33a4470447529c0c0188c88722e0b47e08ebebf97a5a5f8c3b4b78a4e0e1a18a162c8a70aa0e484c51039623724024f5838509232452b6988253ff66fe46e9984e61fe1f3b42db14dba6cf3fff8d873b423be3cf21feecdc90adbdf3c431b6e0b01e0689e373717a767058f40bdd814cfc9af3109283f7fd1f776b9f90b9f117ca13c8767d2c7bb2eb53ea5fe86f9f841764f9d483817e8d917edcf4a5a494bac72f72432028c31b5bd2a21823f1f5a1940a79cd955b6127b62df5c2fab43ede81925313a3094d664cb489c36a3f9df9698fb76bd2bc3f8ef6943ed53e7a64c583fc150f3ac4f2b5c7b6fdd8961d52e97fc40e29a54fe4116b6d92ab524636a5945a4a29b5d65a6bed5f2bd5ea39bf90fefdb8dff59bed903e0e6bb6a4f6665bfbf45fae7dfa3a54e30febdfc72e5574eed38d3d9dfb09997b03f64db05d3eb3c20a425cdbdaafd996c33129a59f6a0c2dbfbe0c2eb4162dfb29e2c9307424b2418ba740c3f80372b49ecac1a90a42df384b4eb3fdc4326ee6e7e0fc1b3730186ca4fd259ddcb6ebf69bc7237184c04f3b1cf565a731d928fd96126d6f340f3a8c43370dd851bf15eafeb10d6dfb810fe354485a5a37158a32928b701bdaf15b4a34554955ac34d762fc591ee8d0b59df318f92ee58c529cccb0a757777639a3d3524e94f128250822ece9695b7638525e48a74e8f64df89edf93b49ecfa2bdcecf93b5126be6adc3af363f47c67f7e2a4f134b63f0d06d05863e798e3c718eeeceac92ddbbd70880bdb63b5c3417fcb7ccd38b4e347b7ec4cdd71cf8acafccbc35a21542bc69a96ddb55b6bc57fe355abb5625c7ddf6f3253a26ddaad72b6c467c2fa76166d94441b2df426404cb573007d4c5fc30dd8e160d5ba3c6516d27814a0e5fb88d9314e8cb576b9feb57dadb2ad2b3ad4dbeaccfff1f4c3d2a5898ebb7e2a07ad3fcd3cb4bf9f4339b9eb8541e4d6a99f6b07edcf20d2e5a07deda0854094b173ce5a3f1cd23565d4f09733bffefcdff1e1a095b1bf22f2e1a055a1060603c17f97ab07137907a42a7d768c6247a2224e76112d5beffab7fe357daddf39b91dfd6afadae256f7e9adf5627cb78b73ce9fea72d07dbf78e91782cf4dfce1b81fb681850ee5c6a2b1d03a41e4d679b9b4582b552a15a983f8a5eb6aada79757e8faf66d60d1f627b7c38e8769d79d67442cefbdf9c59900432383bbf7de7b8f501d0e377223cfc2678ebcfbf9a7ce01f7ebeb2ee77e7d00ad79ddb7cf39783f1ee0be7fbf1b4e85be1d0ff9b5d4e5745f6f77f5ec7eb5bb4e46f7b4fb6ad20fc4b4a9dca5ce01f776de7d7d4d9d10b93b9deebbd7cfe52ea7c315d73aab8d2b74e4b4d61c1765bab7a1850ee350037c263c3dea754e4f7ffecd8e362a15ea4ff553f529fd7af2eaab6aea543d21729f3eeae9d0af8ff2c28e87e9eb6f5d0efdea25aa2169c71fec4854c3cfce247051a6e3680e65c7a39463dab405df610bbe5f39738743362e759cc9544b35735e3a7d610b9b7e986af8ce5238646307ef675be99ca53877e7612f8bee4b5e8877f77d5d61f3e4d1fdc21d5b1d2567cc20cbf730d6dae57a70cf1f717eac3466524a292c7a8cd8a30c9def4314092a6f6cfa31cef93948a38df1e3ea09d980a19a4dbf1046291da294092d635976ae949e758d07a123519124fbfe6f2a895025cad6ea735d4eccae7170e65c6d893771707e6e3242872ee442d106e3d0d5e9f87d7fe36ebe17e3a771468b3334ced0fc1886c37638b4cfd903f201ed35ede96b40e4ae2ffba1aedf091e7dfaa998707fb9cff7e7fb136dfbf979bbdaa65d97b9118fb1a1850e5dc8be26146d868836f9a7fdfa99bef6d67300fd5a9fd206783b7a5cea0991db7e2838883fe771309ba634392d853bfb863e35c1c1f9186bed72adb6d0e16b7bfcf7b57d3cc08d41ef466e0585f6974a728409edbd3976b21963dfe293fdce7e42288e6b59fb9c7deef34a26c063443f4e36fdd5675794443042dffe809f8bb2b9df80e07e5bfdb65a3dabcac62f9ff8b1a18b1d69b023910d4776f63869e30a6ddaa5ff38196d22f8795ee8fa1a3468bc5a1fc2b4bef42e2faf97cb8b27f7ebf5d26ab55aad9fefd2ca315cf4f3f5ea535ae58e2ffa395e14d3d9e5f097de95127b2f0cc6177e103fb8e98e2ffc810ec6b75f08d26fd503ad232710791a459bd597563bf68836a9972f67d106156d4e2fdf87a28d7e59ad0f7bfe955fecf9d8a7ecf91a0e7bfe36444e79aa4779a9cf270ff5597ba7cf264f7f5e953cbaf24ad55eac6d992b791488dc22e37e187150f25e3ebc681fca2f4a9ecbf6a14f2979aefce1c481e3529eea4b5e8bfb7062d9258fb5bb8c578ff7eabde8f58832f2519e9c4519f9274f22f95094916ff266c99b465146ca48832d956c39049712ce1bf6bc6255634c645aedc51ad6f09db269448568b5175f3c43f2a12933b2d7bacf907c68ca68b5b56886e443b4d219924a8996efeeee2a25eeb148a54428ca30090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a9a2e573c4040404040404040404040404040404040404040404040404040404040404040404040402b223e69b5176b5be6ba92499f5029d58ad572b9bcbc64cc98bd662f3397996bd69ab166abd90c1a3566acd96aa69aa566a8d96956e367a7999e9966a559377b6fd6cdb8599e6d33ef9b6d336d86671f09333cbb33126066770653c26a464ce545963e69b5176b5be6ba92499f5029d58ad572b9bcbc6818cd623364bc5e5c5c35de933165b35a64e46ab156aaea7dafd994a550276dfa487891328be443a652c79100e32243e22e4c092ef9f15f3ed9f6f74d56828dd636da9a90d16a6b422ad08400c0a9540080151159dc4dc3f2e28ba58fecc8bdf7de7befbd17df8b2a9258beeb44a59e8628bd17df7bebb5f7627cf1c51ecaefa38cc8db3d96f7defbd2435999a6dded6e72bbbfdd7cb187a2e2e0fdd3d0d0fd1d9c7bf1758cefbdf7de7befbd8eefdf7bef957705baddd7eebdf7de7befbdf7de7befbdf7de7bef939695ab5d7ce5bdf75efcb9b67bf2de8b3fdf5bffde1973ff7ef6b0f79ad4347cefbdf7de7befbd58caeead4d56fb3ebef765f80cf7f77f52ed63781c21b6e7ec76af9417dffb19df0ddf7f6d9b76efbdf7de7befbdf7370ddfbf57d2406a6d413fcbe8e08fecc7488c67c6c57cb6ec89b17a3949cc47ecdb6c649778ecdb2c7350e69f2db338aa59ae1763cc36e70d36c5caf2664315ad40b80b5c53fb42abf2197dedcbc7f4c1029e3114068d3fac3966ecfbf8ab46f7c36a2480593daa59eaa69148c90fad499b5a9e4dadcfa6daadf119fa37f2ab7fb1e90f3be4e2944d574822cb3eaf14fce7cfaf853c347138ef95c2cc54660ccec6830eab516e7224b4e5e72725a8408735fb04919b0b655b7e921dc22a2ccad0df8674f75c11f7bd5268c164b232695e3e3816baef9542f7faf5d702273b15b8144c5f2efaf2504765672b5be612cf1038a3d6e39fa51237e2e03c12e78c651e07372db55a7b2fc69a36e396e0d04db659ce1cd7e59f2ccb3e5166963a1c39b669e171d08883f348944599f9e3a096753ae8d63e9a6339d1dbeb0e47ce48591c9c3ef9c7c169ad129d7d1c9c5916cb28548723c7509d16727082bb56224a9e76a285b892a78588d04e1c64b124abce57ad565a1ccc3107672b02a0b4254599f9decb8729f5a6dfc1d1a78f85d36b1fa83fa13e763e4e9ffa583825b1623366aeb6241dae505ef67129bda222ceaac86776ecaf8a1c9c9a96a39f6ac75a520350b4d2e26344cb119fe1b2ecf9ac19ab4987ebea68d585cfb80bf62c9a34ab27aba2d593d51370a79cd0e1eac99ebf2a3a7ae233e1aae8855e564798fb2d29dad49f1246bf0fdcdc96b425e55511b84df8b10dbda5bf429a343664ab241e966c3e2b090f51f6dc92f6fc90b5644f96cf9e443c54b14396913d5947f60c57487bb29aecf9b6d22de96514f3993087b968cb7c26cc465bfefc4d8bcfd4267b16614bfad9331bd9738873160da48d7fdb98986c4859427e310afaa95476a432712fac111f766893a06693a662d48c7361d7af76a04daf3f72b31dda188e4ed3b80db3e3bf0bffe713959d978ffaf73b0dcd186cbdd3d18cc1d73b19a17834edd33cd40f4ae6207e941107b9ee35af84f2f199888a3988bff3503c0ee2770fe5e3207e2bdb3654916b675d349b31f8b10c358acad093a1139549f3f2c1026ae83b519931f87770306ae6203e75291e9f09eb91e6a91c7c755f0edf9d8c2a8eb224f9cc4b50948d3f15f319b9f1a77cbaa9a166b50b9fa92b5855a143d40c3f7685f8664a5451688d8427a3f08484e3917e1c95e464848a4d1a8c83ff343469224e929123948f955d4465e36c05dc4d367e1b3be233a7d9a489384d849eb4ace03ff9f84c6c196ddc3adab88524dbf89464632eb6f16f5a8d790191bb0e45192c250215a71ec9b4f6dcd7a38dbf0a0d451beb5d4fbbbcdf7cdb349758928d634352a08a84889260214a42c4cb9c9be6a08fcf842e99cf84d8f2f88c7bb1f1126c8de0d793c64ab4616dfc15778fff8be86bd3f308b1751ed53cebe3d91f0771f628f603786cc6e024ade14715e1cf9a57851cc49f4354d1c6ffca797b54d1a4a14856b24021f94cddf8515a7c262e61e5c8c6618a676354151b6354968d51491ba7621ba77c36febc69f868637ffc11067f3a34905a59b29c33461b63a434ce1923a5b386525a77a043bc5da02777f4549c8c8fb8624d732d853b39a6cb9d86c6181953db1fc72a6b691ae5892f2a36e79c1d163af19cb3ea39e79c4e815232463fcd395d9ace49e934d592ed2e9d73d298b78d4e0dfb0ce66e674b1c2a868a55d78e26aae79cae39e79426284c5268f9d5ea49f18c9933aa5c46a44f5aedc5da96b9ae6452edd40651a9d309eb94de9903b107d33018acd4a956a615ab04f3cf6a750990e16ab938970bcd76583440a4c18fbf9b19d8ae41bbbcbcb4970c2c63c65da966d0b0aa140d550d0c9b3155f360a81af4df9bd1e6b55d01a205a894ed775439cb10d5000000002315002020100c084422a150280c6441563e14000b79924478561f4b83498ee32808820c01c0180408210618c00809cd1015024d20986ec6730df53dcc7ceb8301fedcb8f6e2c22d1f688c9f98d85af88b36b4094de2fd6d30f8d12931a5ad4172038285ce6b9e89e0a7275b32d3444c1fce82a2a5a29422110750b264d5f0e1db7557f635a8247285e968e83cbeb619a08bd6c488a03185ce218848d6c3ecee5260100650b0c597fb5e2b1ef1e769f1076215aeeeea45c462453524ef0baeffc017e2f70499490a9941c599f1779e04db08e767efc74c0c1a22d4db00adb53e362a26acc955c486273b8b4c993ccbc04f399dfc47146a08f32e069efeefdde6c1d91c11a9ed008c7ae57b3a44383cb9794a5a91b19084842029ca1ea6f801add44edaa654540fea2bb02070d740111a74cb6f20923ef7a68c1d24cc44c94a1212423ecdc6c1abf35f453a37b77fcc8b3dcd02f03ee2c8209d29b34501582d91efd4dd1a7bcc27ffa8e9080db3720529e8dd873c7cd14347e2cb8d2c4b23d3d92d0760e1b034511a1e3ea06bb5d9ec154097e9e1ae24ce268bd1cdef046f422c30c417b2cae288e8c47eb7092b4c4840b76116b0908d41421ddb01d1b6317877e4923223da94620516853140fa67402177959024fd292f7f9b44778994cb30ec87d7eac0d3da93b83c98b3e3d2e796adf5bc23a556378c2c5bd2809b00dcd8f34e69d4e8b8f9441b31e72d4471e0a38b668c396ea65d45c50f7ce3e67ff2e99bcc7b5fef3db2b65fdd5ef7e248a67bd7e57d323fe856efdb64bbef3a06b7c34d76fb5a9fadecfe35796772f56e32c392d5454e245179f56341ede941eba7077963c52e5faf32df4fcdde93d9ded6f7dc1667d9d95fad7793f948af5adf9799fdd563b80d6765db6fbd7c357b57ef1d93ab6fc914965e3778a2a3ee04bbaa5c90057a379ef86594b7659fc0c7a401da5d7a24c45369f1cff12839569bb02c06f9f6f82abcd54d608ac0fadceeda44cd55253d37675f3b7773eecdd96e67ba3c73f32e61ed73d0edf69ccbb36fcebb39efeab98b67815b227ad7cebc9df57e8eabb3eeae15ae56b763007439ebe6b9ab6785cb91bb58005e9d7bbfa871094abb78e6f25ae17265429b9a0d274ab591dee189d7f24489f03bc4b945a9223dfadf732825140fa14f6807c7d3a4e57c518f9eb7ca58db046511d4dfc555e32d37825904eee5f09e38f594fa30a8be02f0d3d5ba2ca6a8aa9fba81fdf6cc8ef3aaa7c2872c6e24a32cf1ea7f974c6cef715cb7aea36ba5db290f69065b90f44da00c5fbcbeab2ae28433c7e14e76df9753ae6b0eb1becbb6c516d1113947d0b894cf29bc951b917438fd4370ae32c1d857b6ebad34ca1316ed0db635a3e9271b69f98e921d715f9fa4f7c88998c4377fea147b8d71057caf38d84b8ba5c01d0d1e564cc46e33553fed7bf0bd359aff1673b02944fe034077f783804203e5c6b85409935da5dd9f1815ade42dd0d707ca3d1b495d1ce208e2c720a7ba728588c80afca3f39337cd993b8e80658ba414d2157d92c06274618eaf3ab9536ae1111a75ee4ecdbcc36bc51f0d670971b102be781f8ce0202508a022dd7f01a1d30a3cd9cd20083c893fe7efb1f2eac2a7a3f51b97ed9aff4127eddd5ebe011af89c06f0105896b33aed15127171a4e08b24b27ebf6442eae8803904faf147f386ebc543b6af71eb0b4ffdc6d29ac63e9436acd46f14f04382f912b54f22480e8a4bee259196202b5dd6c843d26f76c60f8a93e56be0c5b5a7ec6482d35d9c81c52fd6efaed6efc7ef0e18f27e1b91084f6359be056f8b4fbb3df658bfc98e488a69ad1488df1801fed4effa80d6a42eb92ca731e69b777e3e9d7e400ea955ebf78ee41351cd475da098dca40601ac7d76279d4066fd16546214aa1e8cf785e783495a18b4c98572c03820c53dc7b8ccae2ee928c49cb62fc9e86ffe34c6261690a4a8e45f120a8b7e5377a415628da6e5b7a2c737783d57cf8a75df104ae150fdd871b294f8e977757efde6515707ad353f9201119aa1a8ba45a3857e3b2d7b0fa864e0cc9c82b0cf63cb948a3fa4b3a6c3c3db3e3b7c2b4999eae79462d6197e08a67e23869617fcf87e6c13c5f97e934eeb1351df732e2007c80e0e3261e73357fffdcd2dbae8844e6137030ab97ed7bfe7fc252f1900b07e7b443f23c06d2a11c0291c128dd8d860bd0f3a8255f8312e22ce08ce6d7d7107f29d34f7505e5dbcb13c82fb2c7b123fb31927ccf8a8798cb08cbe982093097a02423bab63bffa37b1365fb98ed82e721f0a8f88e065712f34b722f1da2c58592cdfa93708c1e8521c02223716a8f8010125ef97d41068c194f351bcb130a21811b47a3f9111a981a1deeda9d713b75b73189083559f026d48ab5d0762fe22c624008183b35fb9e0492954d2db13e5d336adf221c3fd42b2f88645ef5d8a2d3f4cfb3bbf33906b9f257a27929e6187bb91746c622c8e2a2a5a60131083d1472f2b6793934d0b79c77f66cf04c9f8077371d13dedf4d29fe9e4ee5d87085fb662573a595f87614533fc2c9d1e4480fcd8111f036395532cade9340b86d23496c1aa839a9696720d532fce091c646eb816f0e500246c001e0f521a6ded015b4bb6ff2ea3c944e225461a9a7ef4e9f877218f296f6234e7b231ee9d4b3bf1d47d3b53f538bbe55e95e1b2683e805b37d40be58593f764d10d83fbad6f486d501837dc81e580796c35dfc37333e4415f043c444f107d69ba1d384ce3641f25c1d23c5af169a4f958e2dd87dd8e86d87d2f2438b31c63a2c17efdc2250acc0d5c135642b4568c366b00d10cde3c7f684a2cae739b95a43dfe53f53eab09a80b28b2315e0e97d5b083048a6334de954ac948eaf8044a57639f0bdcecc287a638cb041d38a39a81f2d79be49da6246ab5df9259ce45dfbe5202f7833bd27df9fbb75afb775fd0fec92f9c0f277a6625f96aeb81df45348108d29e5792ab2ad179906dbe97c274e9f5a904b1b7bca16557b06cde4d60577f41f107e5422bcb3eb845b1ecb064a3bf5504b758e7e3c004e21d1d986173cc7f54b36ad1cb453f7cdf76a58f60b3c3e07328a2f386bc11022a0787c3b57dcd3bb2f574bc81a993340ec7cae7f1a4c4e93031c1ea35fd4ef8a9180f8b5aa54a059e481e405c078db09c4155b3608568d58edf02173339f93cefc1244d51c250d8050706ca5831f19501fed41394fa824b3af6179e321373b29325295295969f50095967b0af39ced70119448e8289c21a87cd62e46dab5070294a32bae07eb0b2c70d09e8522b77d77c87135c2516c6a4a953a176fad79cca238f585178e13950abe86e3d27413a878b2e13cca354634cba90e139bc18a3cffb1c26391683b004695058e8ae71b0748bafe18d1dcfb64f1faea3412cb212dbee02b12d09a334c24a8726a0ffab7d6416f9e27d5c4d234c92caa3bafa40089ad91b621880f3b676e1fb9bbf77978e932d467753174024ce90795b0565e7560638b10f6e1b0fdb6443d09d1276d3819d84bfd3cb440143dcc9ef347583cbe428c58890908241646930806009aeac0971d6df98fd1e07ec09748314ea22808e2a659076c5948eab87aba78a509a4b0b4fe260149f0085bf956000e12483b6500afef9680535195a564f9c2d4ea12ae034aae4577ab0efc65dfa5504d0b83854c96138d2aaa3591709805636dc06cb90c425f2596863e4ba41fe7b2c5452fd6bcd8454a3d3798fbe1fdc0f5d3304fcf83bc604a7697abacdd10d12b41f631212e0449f5f97859fa16798a900a8a9a9c1a26b58e451d0f434dd5625d6c62aae08adad09eba25282f84092e45de8bbc2e06156eae4758048fc03a1b086325be881d4124c4839bc6461d1120df999da8023534f2692f81c510d28d0f8649d50aa18b8dc519901dfdbe361d00988a1f5f8fe9260d8566e892eb0421b68c199cca150905812f101dac7156d7d0a285ae10b0a06b47760b484be317f90035ab80103490046a49021222aead5e539ee2cc6d39d9deb102a9161e6de106ef441cd5f85e79bb44fdc98dfc2c5228c2a3b916ae47e521dc11b02510c8e9515ef928e2553e080be8c4f3314ba31b44411b4698ee6a88ddd37183178b8d7f8b1618004445c50d67c12bc71cf1ea45eee07193e08938db43d41ec8756e3316f4079d77b15f8a8eaf9f67e1656801c1714aa2c59716c4a8e540e60bb27024323d587c001fe4bf4a5a4a20533991748817aaadc4e9c2d4a58b16dcbf05b13555368810099849094a36ae977623407119f74cab499551dfa4019ef67645d3b5079c210cf5dba14dccc1b7c5cae41402d9cd9f785cdbd140b63109a28b3fbde752a57d7b73e84fbecaed08639e429985bb8e5346b5506bd6b2109e153be0a45059f8f9947fb2e0e336890eaf5884c6c89fec23e450355e457f9f65af1b3100d9f98815c2f88945c73b09ddac436b044f552cf07e56b5e7ecdd3fab2c3521cb33b0ac7ddf28238131c93b50df84f88fa7a277793f033bd9da2ec261e7210c94fe6a196ac59f16805031a7690fc06701e8eeae9eb1c3f8ab4703e15b215369b20006d3d843239b9d7811f469e58f85d6de584c5f06b6558132240959df88d0a77ba84a44590da093747e4837b29feccb9b104b3ddb6446498cf2ec8101e488297cd35f763edf14e133f1b6f9fdae07815c4b48a3251825dade46fe1b2d46843c87e87f6d3b50942ff5fd564aabb4e389706ead0bf97045347ad04df090445fed493147d83a29473721e985cdf8b8c098ae7c4fc2dfe2c319abaa48733594b74bb9fa20c671979b32ceb71e2bf802c5a0361e2f5b0672ab392035b072ac4a8d82eb73d783b9f3556a1c39f3b6348849ef20ae1c1780c1ccc879d7d3b5b48fb74e9b10514f06a6161950c337fcf82accc50b2dad750f7ec8c9fb484cfcbc4f8a81edb00cdb9d61bfe15f4a0500e62dd1df2e9988cd3620fbf86c440b06a601e589e6c01f3bd1cf43db0e628e658258b6fc4ee361f545446a75ced175385bdead4863954b68549cf585ccbea7e9b0f4cf627ca94b01a30f599426eddd490da013b1aa624be82d11826fbc029764b3bc752d95a3e6122ca6928ad618c6085954c9df4e394f4412f47618b1922a81990549f4215c1aa11abcd77bd36825ae065f9b366dd709f248597213004c168340c58f78e31c3435e84d4db2e0325d0f2a757c04577db7d2c1e2bb95ce5742540d87b04316c489b57156b0653b156f38899af785dae0f9dd0a2a8098b1bc9657f9ef0cb2f8f138760caba6e4d58e3d72682f7d268c61d6e29e8a427f2431dc5241d8ab856e16aa83270874aa3a94c4acd37da07029a4f06d7fc9c5959867115f145a1ac01f89b00acb9e6d3db288cf3e6a76f6fa0215ce9c84a0d8ead14da2a5a4052c00469aa3cb201395ba09dfb52326044411768ec0a2abef490186472d6f155f354deef92a3523c89704a3159def04db7470b8b63859617dac0a2dff230fdf8252cfd9a5729904febe0b49503ec667a4cf3d836352691a33cee271760df27195a8748a144a7cb1f2a90565c4b0f510b3925a7aa1d1f5f2d0d82e1788ebc02014daab1bd7db224a53ce74e8a008ed7ea6eb6efdd4f44e1a70fabd5bb1ad2af04103017a1b845b06cba30f7dddb7418895e80b7b26feba7d7d1ce9ad1bd46e9d16dfcb5057b2b366bcaf2f7d865365b743d9f039991d314d099d0ce70057a87b0e92f3961be396572d3f7ad23ec0efb557c34bfc8256c6cb97441c8fb53ff927d14fbb3f791509d6fa77daf67bd951bc31d1f3aaa1af126423a5ab68075cd399dccbf430f3f987c8cb6b7b57f79349a6115e3bb3dce18fdffb2c85ceaf36a21f439267d5e68d7384735aad8c1863d7c8ad981984fd363d08c9d79a59d1cef07c17bc7d0508a8b5143971abead550c9d17622248b637cb771ed1beff61e885a94859697f4a18842d84d81a282dca0f6a6e2ca7a771907afbbfcb9d9cf09abe4569bd7ef163383d1d9f552e755dd85f0806a322c3242b94a4c2014cd61f17edc789b24defaa45aad7e8cfe0ab97b3bc355e93c386f9440a7ef871457421ec0e5219f42a9db45eff6bc3b89ad6a69c221fbfe1608d62aba0934847bf4b65c8724117669dff1aa253f299bf1d16198c720492f29fb979e386d5264094f983616bcc8bf3671d2173c81968aa1191557a89c8c12d320eb04a9d4953fd2ac8cb626360e316db49e9d98063cf1dd285c5d69fb24ca1c137a8caf4b7a25405b529563ed65baddb3c92c1026781300ebefb00356546f85487cc6334b26b06ef9876755bbcc9f4adc3aac8ed0ecae4d5618f5f4fa5038a5a394dfc7242451cc8e7c6af3887535a26d326d324bba343552ab90c8185e55de13943a6584013f1bc196d81a004fcca584eb84917edc806f36ab9a16f78853e13cc10badd4b9b306d52b101f9c29f0a269c9e735664667910168b47de4f8a569de2dd72417b0af1fa1c8bab808f05c193d15cb8dd2ecd273c20b54ac782a73307fa5bbeb547dd454e363c9914c41deb5668d04b46cfe08212fd3a27de01e313a11de6df3151e2c8347cc9421b3df7c4b3205b04574556271a21efb5f274f5d54b4847f9100ef6c82ea91325eb867fa483cc80de196e8e929d1a70162eed40c716b50542818ddacec3569a9be2d603a684a5bd1f64dae4466a3f84ac14c2d396340a021f817206a43631336c426f1afb83fa57a6f095267fa6f589301fbc48ad4ad5ab1a8062bd22a5a837668e153ce0704e7cac0e102abee6ac5219cee14dc8a1f123b51e33e73c39caba8157f99b97b851025e370318f94138100b19de411ad31e5cb68a32af253799c930d9805a97fcabf225e5f20b1e7d5c3126c5ca7eec8df46e6077217f70dea813375cb409e4c2a06c614f5b08fd3a33638a28190f44a50bacff0a1cecee77310eb8526d8a0e9dca518ef22cf0b50a11115c0ab663c738a1b748feea6f6830a321dcfe43aa78b36f1f63fe63443fc698a5c24186f4ba0712dc3007724f2c48d93fe23607aee9e880318aad97f083babd89896ec0e3ae5168bd2c2b7e8e0e83f0470109275304f06614da4d1bbb06cf82fcf95984607089523dd853b29381713327b1b0b515dce3809c4d7b4cf7577d4312ab800cfaf0bfd85fe2aeaa1db1de97c0a48644226b4717c7193e537606633be62059cdb3e3cbd31ec1917a64b527d1bacfceeb9dab5343e9a42d270f78f7e9a30e380e8ff6c47fac81e290237a2904e594ca5198e57f67f935feac22542a9fb0711058a55cd6c895fbeee531606ee382eb6da09698397a8a3333ff6c99a12a8dbe24184586dbf0b09c2a16fe231755c979e1f025698b182ff9edef1067db8099b640602050a2b84e58a6985792a3b508bd121032c40ae3bc465125f00eb28bbe2850e035302981368723280846212056dfe8470734f6633b3133a370a31eaa3c91226abf82fa795802224f2c7e56c4da85ee54279b47f3c1c62eb2a38e1b324db6112c7bf41e545400c0a86c8499fc86ecd63d6787f322d5c2f6a8f14f6dd1527ae600f1251d4b1e8a98ed3de2a132fcead4d80365acf8019e7c6dc4c718f096c373c6664c41c4926a63de3ede7ef4a2ba7f0379ba279bad822e70d95a299c8d2d0ed1ba976d683d35ac385d9bf88abb17b745b4c22fc649846cdc849368207e3f0a68833f550397a5d0d2872d3facfbbf7ab7fa07478febf0780295f41ec6c486deff1230343ec214ce406b175b4cb5dfeeaac7c7285d2f6f37d78f007a0e264676a33df2ff117177324ef33add877534850002ae7f553bc76bef8d2d9c0d2ed7d426c068570b4e9bb443e19ed8bdfe9049a8026d3a73ccad71b4b90047a99ad9a504b8c021a7f73bef215d5ce87fe87e21ac6eb2cbcb7c91c7d31b4cd2802e16335907b4c6ac86d1bf085fa43d54c0805af15383b14ab4ac8853e6393f1ed6aaffcac17d3332bc00def75e212ab0a823054438e43607361e15d8549225bf5dd7ee848f31ddb9d7d66f1d352e394c07450774453f838f1fc2204688cc4d4c2b8e34e21bb4b720e9d80af7f8d6fef6c3da55fedbc8580f56441c26abcdfc6cbe627eeb027285b631eedbae6c37616d34ff9957d4b763087ffe919b4217d1276948d159b8178ed67681c804147cfe814d63a3bb9f0e0a320eb7a2b00e69c1fe8d38c0fe41e544fe14124280cc927521feb3b0d93e1009205e3f8658bb7d6651d8d63b254fea185e2edd1b9ed339685f15437c3288e95d27d41bc0e80222f516b7a30b61d7f5668d6d6cd217283b721c419e5e7a388b4358a9bba77794ba1a10b6f410d42d8b1e345baa5b413d6d7af83d7e6087afab79e71d8a3aa946b99cc85537655c6740ecb0f2ec9c446565426450a381823ae1109b67acad25da80158094790ba012738544ea458f9cc60f1f0d1757cb188cc49946537215e1af41ac2095bd6d929bc44a11a7ba53d6333830c36acbfc8ab0fe2a8aedf5739bea84619d6f9ee532bc8b5a896dc6e1ce32f42c9f3e72421aeb5bcf7b6ca396f97a2bc943f87bb135412e0a02bb90d4d9f48d256e82b98c58f3d5c206f3bce3aadba66946d112f9ef6933e638025fd6dcc3f641b88d000f48eb2aa3d4c14d5fb12fc4927a372ed61dd7913c821e1e9da1ab6090d0e036b85f4ef1f9081b698191f23b4d555c5c9b5830e84362f031a50ae653dc6e980419149ddc4d17957db1cbd89af5cac8fac2973d0ec268d31b5bad94be2e5d4df81d39ce4b63152be458ab53740e3a20132d380809caf790a9bfcfc196c7f2a44a11fcce0a10dd73f9de58751d30dbb6bf1edc22d1fe59caeb9e4bd9075539800c5047e6c18867c20fef3536c3a8fed978e065b9281a4de5a37e91d389659e0f8aba588f2ba99d27d7de35a6363fef59153b156583228634816e98d6adcd60b0a75b48de2fdaf4129287761432a2a5d64bce3ba73cfacf8bec6d46e0b2e1277ea872a056c0f0b12f41e83116728ebab450f5ac1a019b6c69cd4eef9773c0acd27bba93ced7ce96f5d1d2f611e807096b5d800ba38132f7ff4c1217187e9fbe5424d267df789766331102de3c4767712cfb8b3eeaddb1a1a28d76c43979a26c461d3add8e6d05d0653bfc8d541ac761f4ee84290041dc4259dec2288af195e482dc2ee196a69aa04b1d301aecf106972cb4972090e7a393ac7a5b469b0dbc1605f3296a01171722899ec7a57b24c9b12db7358549101dc6797e8394dc9e6d58bc2494095869c27eae88320f2a4b9debeb6451a8eaa591504b0d996fde1b7654ba4492ac4e8f1111d874c32732b12749e63031fbca31b9c1f84140979a99f801d836859c38e440804c4941b54387d4235e4be0e691ae60e13e87bed558238ca686922b38bc1ee983475d24e22380d7d865872a45d64b48aa8834470c43369ceb946b16a4c5ebcc77f183ea82589642fabdb39adaa5a15794c6202cc02ae95b0b47a4b5bf8b36cfbcab869ad12105e61b44347c617e9ecb21c0a8db108365de164ccf8fa4da7521c801ad37d7c9552cb46f3f32777cc50ee7829c99eda3cb91eb045f8f52bc58eb6d4288018b13a0b72f0a0e443153b27e06ff059cb43ee382893bd1d697f812c71b47c7aef3a8b90a2b8c023eacb61d993369cfc6dc78c243be2e2f019fec96da4a8547cefaa37a2922e3bdf987c161bd0c629a250bb99e89e23482c01eca431c5a9ebe7fb35b77b08a24771979d36e67d315f420900eb91ec16c578339dc81efea720fbcbcf79b0643ff08f65eeaa3c1a716d0e7e09d1136320173401ab9dc29a98a01d4b50654407200c4881b66bcdfe2ec0b8a47906244609204bcd844ea2bb50d4a3a44d2ce7a6a58cc608bc9127e898b7796070221d5f6f508036a42db20eec0dc796884d6eb2024ad929d9e91196b6cd3b35b61d9c96516f0c735391f60b8885356207cfa462fc66fc9bac7e413c00467e9a9048a5b2ae5a1975dd2100e6536cd111e0b9ed78d628f4cbee5559826b539efc4f156991d06a6410cf2fc64bbed62fd126e5b7e830b6ada452574c0d1395a8dc263352d258c1502670e7f5d3c5d9a7d85c6c50d06c6f581432ce9defdeb83263323a87900e18b1e329a84e507325a090f6a537b046f6eb48c341092e70535669ccddbe3ce19b999ee1e44159b273bede2a4856ab59a9475fb237589b4f3f1170f464504a5bc597f4f4f3306ace757cda8b802b9e5ddbad487acd36fd03c4853739ee7c7c675cdc558e0f138d3a0fd3b5cd05cb85bfe600fbd4100e22df540e9c45722498c8be33fad29f03a1c28df4a15a2bebab7dc6aace170b540fa756b65c02ebd7345008a385d331ebc0674630aa3297343f88dbea023beae04c325520ab0e0b4df3b32606522f2620d59ab950579a6322be7f50d4126aca148e8d9727315bba3321c5cdca9744b00d216a223eea7f922cc9897f770dbaf75592cf3049a762831c01505102a348a393513007ca3569778d54a0e4949bff69290b8096881229a1d8fdb55c85afaa9fe0130b4fbb461ea210b9d5c2f479777b6f647ab5b7f10c148a080f407aa01139f630bbb1e68cd81f4e131bdfa6631f9ebbb6e5ced9e033c2518b126ddf770a5a3f4b5f17cd456d3aff47ca54229c74f172c621903f249e61f4cc0330810dc35b0ca4dcd28893702809791edee965a4ca07f52554030e1358a2d86dad8b72f027ddf3a8eace365819be14f4081b187a8b58a2d3e64d9828582fa85ca5d426615c4c2a293815234968e977ee364c7dea2b26e2b6a1d782e31c0341f20e8cb97944088f77a3e994e601568be3ffa06be509b8e28563d8a5a7193be685637d6e30a295910f8ed89e5f6e5c144e1e086ecd925a3a4217df72166634e712730c02aabaee7dbcba646bf07db51e9b795bcf67687397039ff51b7d4964707768b456f8703d9283c46578c4e30b502514191638e51f9a45e984b78c660f8cbb901b3c34c1b0ab4ad99d82ccb6687d2b5965518adef2812303f8f0e76076b070f45029219876378f5917cab64dafeb1f5838c3ba0f6a708130897508f08998b6105331ac610dc43e6a7a4953355b10b78b9c0f2970d291fc134d72a5fecd119eca363489bfd377ab725c2b88262529512c0640815b29257c28698c46ad4dd19f7a9126eb73c19e726de12f5422090f976cbd3f8b90259ec36c60fc0c564fa3106857c83f97c3d6c0ac3896a4b7be16b82c243b306e186f72538d9b8a39b52caba80101f0411e1d571c027515eb4a28241015690219ee9f7386eeb5962cd9aadd2a2928a2b614da8d9043fe11655f6e6a2bf864eee334c67af14edf68d06598da92961598d11bd0e292042b95869977e4457c37a56ccd5da56bcb2b3850237be137a2e0d442ca534e113a14c16039ec6a5dbb2cc6233a6de94e5d3edffcd96fb5baf73dd430e5e70ebde1eccf654f464d202f27b3824d22589b517137cb16cab839b9ae42f24b14dc23055d697f2467c463e57fdc20c88f2ffc2ea70581e67d3bb001066ebd90a36d8609f98aee6a15df946bb612357adb3d1c74fd0883acf623505e45dfb5cf1eeba25c3c1424148feac7fadaa5aacad8737542706e29329183e9ab8e45e0a960c662e56ad23453dbb58eb50f92156d913d9364cba1b711db54a286a08f912c2b5229f761fe44894960bb0dea23a1b62522ec6f7b8671703f5bac8f765414940fe3eddf7f1903dd8f469187a3dae97da4e59418cbfda4271e03bfef20f17cc36004f19e94bf97b1c5fa8aef5e2c52e384e93be1049ae5ac407a62fa955de02201e7a50ab4c06db54b4dfaa1db8be3d0dd73918363b92d1e403c59cb40a46db256be86a803d98ba343339d98cd59ed4dc34a12493cdfe893f243e71f9911d2a3035bcd96c355d0a0473f5bb2bc4e6e07513222f5acfd7868b26f6a77c45700128751c2327bcdd0685943c5bbb6346721914c67e3f2ceeb0f13d57451b734134e1570cdcbdf4e78080a834a5dda4aeea11f64f5d0a48a4cfd3096c9445c2bf689711565b39b7b3ede0bce7562a18c3285511be051306ea1344a2c2e8cff6f05904dbc39e8e5c886f59aa37076e57e07facbcc0d681fc96ecfe1d4eab5ce0387c50b1d30b57719e9aa6e6373b648e245618680fb23647220b9650637ceee4ab40c7c2dfce1f2a41f5985bc063a27d3369e5fb9338991cfa902e3ef6cb2810e1393339264a4bf9aa5e57ec88ea97b25da51986044dc251c0a41e164df6e03ed6313c0159189b69cf80c12cf361828985d2fff0474f342e236abb36e3a46b2910d10f0395b00ee97afe4d26385418ba2eeb620c9b0ff695dad81e0a5a255234294887b67cba3bd03c665329e730656be0fa18b4a09417b44bb46a1accf7e2b273649ecea68bba6674073193e4e7c3c5e95acc88de4871119a765fe0cd10a7efd1baff33c49994f8aa55a7e0e5d1e5df5243e79281bb7192b3e11d16ea4205b6d6b324d46b0328499f3cd410a8bca7bbb9d780eb8bee12370cb7c628a21a95e9d24b6f1a625485e85818dcc4a5ec56e47b1a6c6aeb4c4f27f3c2150d5efdfc977de9fd307161076a57aed0604089e35113f4bdaad2d377955ce28cf6e034fd2330e2214a3c1afa83050198d8bc9f16538028a9bb07718880227955e47bac11c6b150221a4237ba32cb8d16c8fd85e8ede0937a9c1680a1596dc2323bd744fcaee3d0c542b0011f18db78f53d102e238c17b45e27fb6edc0b537952397edbb3d7e2a76c0b25f2b6763a6cae2dfbd0a13bdf39c41d24323d2bbb64bf2988cdb8c240db97a186e4463af091aa55b5154c481e87b1406157ecfb4921ef625d69654866c39316de09aa2d9c71f325118398a1b072047318c36b148b4f5ae8270b6382f539e01d89fa9bb3e8c64720e6f89154f52e4de6f1812d797f29c40319e82c4c857581fe56b20cd61f7ba7f0ff66649fb035726517e05f2187fd265896c02f5447202a69ddb63f1ef79f1cf618761f2313307e4c0a7aa29023a10012a04757a5f065c1511ec9cd43cff395418b4085120148de9cc74a9960bc1d515ceca014ff998fa69eaf7a51f0dc32060f2251f68fa144986f6e7c53035f3f7aa3dd8032196d9a7218b0eef4d684003a4eda4fa2085c18b7dc11ff88b77cf5778dc24f8471290a6167258f4970238b90b40dd977cda03432ca504133e968c32d344c6468bf4c089cb899f03aaebfd0faa74a776d6631b6cc3aaf2c37d8920d508a04cf8b5354dd6b424a9f4264f4a402b19f8599120064ea1e8815cc3a087e001d624f7260feac4a8fa024c0c7d82f62354cd81462662b407690d48f67ce09e85c4e7919677902fb5277d940ca291594974aae8d3255a8c8cd3ee9fb8a38227d5b0288fdde3ee20043e60a938154e55488c2be69aa8e074b5f6a95450b3b681f880e8c0aee3629270bf17d7d478edda1e694533f5d36d22143779659b033bc31360a45231812a7269c8682b168ab2c1ea95f0a56081e0f0d394d889a0ad63cdc3836073d14c18a6354e4ccc3c106783e82b69740f5c2b3240192e8290d52bfd4bc519bb0d6ae6d2a5ecc86953114b0bda16e21bf8734e98290ebe92b101d81c1446ce6ca4b2eab4b1cfd7bf70c1d86c483ab512dd862a8c55658b4ead35d542805b6433082b8386038e70c45919be4b3a12911b2fd181deed46a54a2e0d554951c2c2a1fd2fcf0c6dce7f7b4688a8f4681b9a4deb11ffd7a1c004c8fb20d2171f9c372435871b0df8228af21d9feb2f40c3392980688330314a3d2d3b2ebc4ccc317f12e4b987a3a165a69d8106fe3d23ab69b78189415084315d5041a991c889102a245a4dfa23a513b763852dc7cdaa7ce184809f14faa92fb2aaecaacb48326c3d406097f83006a37724ce2759d49742a18c75a36e44f9f9af78a8a3a4df6f31803351d519700942e35299f6383acf19fa0f183e077adf4d74e6e49cd032fe2fa095cf219b290e82025ba09e015c8d1004732bfa176cd4d5f50129f6c17b5f7e89308d230cc32c8980737fe7bc07230fa0f0c3280b0dcfe05c6a1b6de3d8f6fbb9480c36e8cc18e39d8ad286a4329bb0e980e3258c311a14c85bad875267d890f4c24832b7021530f0616015b47b06672e139a44230aa7b91075a5219038d7a8fa2488c2cefece19a970c479ca4581b7a35866d299d8a449f63b8759e50583a779a8dc48361cb541d7dc4a6286d0c75aa2eababfddaa0fa816927b712937a3a4d9e56e15bb99b918ebf9c7972d78ad71d1f1a58beaf0a07f469cce6b4024a92747e6f1bad2c8bf6f7a43a8b6e823b2e12fe018ae1f07cbafc68a8e33fc327e4c6b7141fd600a2ec8118d095280fecb75c604563483070e892880857ee5947933dd391fce463efe2675737458074e00774e28a71d15f762fe4c6c5d8d7aa6c2859fa0a012fe035ad51ab8dac4d43d90280b5d818cdca62ceec08b16239007f1c94f07d0dee4f26519376d7898af585604929615609818d28a836adf17da53aec38c1891d0d6c318664e99608fc995bf56824c1845f263ad758ef4b27aafec5ef6cf469baa0c83831331b0e1554c86048b2dee7ee09635a794459c5b48c37ca692d0ded7a7f5b52d7682ba0fa3c096a64086c2ec35db6a7e45f0b0cf29bfbe7793c3410fa6eea172fb7c23cf5c4c38a0a0d4095bc64a3a59b46a06099a8daaee30c421af62990fe19e31a30b1463115fd62c140a127dfc70ae309aa2341c777b7cd41f167ca7fa868073f1af1102e1f6e0aed6c1838f903ebcb4d20678906f6ac9e7420c8fd6019319b5bb3d3799330f50d94e9c529ab6b9dee89247c6949673e5a30ddb5c9137213099d1b5fcd34b0a9dafde881328cb36d71b58187a9f7208c9a594b5ec5df2bc1a61d789bbdb309486e15e35addd887e2ca2aadaa858cc1d88646d79a2f4e8211a4902ab7722c97a560755d1fa474bf52f4b7018841c9ea2234468686eda8e415217256b2928d0a7af87cb0067573d0923a09830994006630e02b8b61a635b8480e26a35492c38ed022178a702005e21dd0dfebf9d47cf167ce6e0d8797811ff6cf946d788fec53dd05c8b27f2bca0167f21c61d748cbbd1542ed4a82f523c637fcb324a0855ab77eb30a0dc1caff66a5f0c2dbee2eb1dbd2e84b7ca488d2f3d72569e1328118dc543c485895eec457521069aaf51854bfc3e97190393021cc555267650fac02f1880d1cef7023698b7d7744c60b512989c1917d3da17b304c9fb92aa2c6ba86bd977c2bfaa4829e84c2c29aee9faef4eeb278214838edec6693ad4bd94064c7558be5b990d1f14ed5b992803ec83e6219b5ad4df97087617e7c18a91fedaabb7d659f4bd74bc02087872d7a8b56af3322aac7741288528aa41ddc940d95517403ac01d4c03a1caab90f68029e88a96283d542cd55f0c52b16d7069d5a7f243037f6d00c556046fbd2e17fa9db82464891efe62313fab307682f17c78aa449014d79d25825d4926f398e558e035b292a01077388b492f33da7076d91876591f280663ebf1cd0c660a388ed329641c77d156c49b7f1e2e4b27b944ee6366889af4bf3ce58ac458903cf08b3e4e645830525450d7dbc535e7ac16105001b9d5619758832ce2256a94b71058f4ea0bd0d29bf889e65204d9a58053c366f753576f13bc75c035f5b3212601419aedafb66b2785ceda6f2078d2765919d62c65833279351dce8e1e716da48145414fc8faeb4287ac1394d725fe697652c1f79051b7c693a8d049c47c3756e09512821aa981f6fd2c93b029181c660d0b3015bd554703669d5b01b849dce2ce5d88265c2cbc60e145857d1ae35a9d5c032574c12043d634dd06d6afd8a789e774666ecc85291565cd58e3910598ab2264685c745576cd48846bf454434b402a8790e9cb0572913ab72e1af542d9329d9507c0357146069c465f1b89253c158b9afe39387b01501577c1c965b11c3fc4d7135fd2bccdc395ab79fea386c9664afa380c42072f0ae07d45ade71b0914ac35656c51399c265ae513a8d9bbac7a44f32711206d4abf650317d4acd40608509bd6c7d190cc85f769747df984bf8295ba5c3ecf1e48da1121359a5c312f9e8196812207ce9160d796908ff1ca4a879ea6f9b742c0b8286572f8c41efc3e6b5f610d800021a25a1cd31a8830e1e7f70a455c9586143c570b01c3524fb1f4746efc3f6b69aa525d2e2e1c03e993b76b601acffd6a9202aa5f35032c0143cd091a7233db2cd9947e832112e5663b20bf600771f480f1b1de9d224703103d23132065d2ab1cd448cfd609663c8f98dc9309cfd45893b9591acd03d843222e4c9615afbacb8e8ca3b4b98d5cdfde823a756a35df8b96e3e52086e4995a4b9d81fe3db44e6b1bfa42661c91c73a972955c0358f50e52c63a907e8d338b21efa0afe42508bd180fa14dddd058ed90e72bdc8c6ac7b4d1051c288b48e5a7708310842e5636253f9c8dd6e0b2ed27e20e9ef079b4d3ed8664765f0f102abdd4d71ab13170cb9e101920d1d8419eb607f34d9fc327e867366e233b062283d3e7cbd0b58c57f269bdd31b2ced8b715e0d9f8d26608cc265b7fa2f9820aba9b5f4109cbdc116f2b8d3c096f258e083b6439fa99a7696dd5555fbacde12a494e2316f17f0254a9fb0108dba7cc4c3b6f09b196d2e1c327aa613da28ad7044d45d6598d13f041da4988a4ebbbe999565c3b1ad6dd86a79c6557570c24109cbbb222f0da88cccbd6564d11098e25d6e964e6c83a85ff6ca07d38a45e51e80b962407e54d1c190c4a7e112749150049cd19185ade9560753974483c1d392fae8322cc1095f7910f2c750b241c3fc8a8abbe5d303744b4266ccc6ebbf74487b4b18e786e4622e181702fa4f3f42c8ea939e4a6a26952ddd475cd7cca4a8977b2079652972b95bb62babf4c73c30905d546a9d4994d73d570b0addc5bbb2a7473e3a2702d10d7620fb07d654460c21d7694dd83ab5de908152aef75a287fde22bbf473dd84c5c21aff06857b2c809ce7ca8603d39d3880acac5c407971429d615c035bcc58203544b39fe6070c8cc0de6fc384e1bd23c64be3b80f9d3eb662cc1ab588025c8f78805202f818ac2c9650c965b9fe518d5122cf124e6bd78b77240cee5570d855e4be407cd1c0bf0c5f04b7376b1603f6b0575440a7e79b8e5f3ca50fadd38e91fa08fbef2beb35e5d09830226a69c8b4b6676dfd6afff05a236f7a972aa861fb295ecf7b03047c6cbf0d47ce814d5708e4c4c8bd218454d81211a5107290d27e37cad2efb5bfe5f9b64e81ef904d9370048796df9de58bdbe9464ae56b748dc2a85b5f6f991a2e9f187cad9f217982674ec4c4e63a500b24b89dad69da905d45b0dc7165cdc1db5a4a87a3c9a26ad784be5af3d01c0cde83e95b583a2efa7e07bca5d8d136b34e5fa9542ac7170776bb737d5977d786306c61011c4194e254ac55219e541d396447e3a742d540f8ecdb2feeedddaf40423eb5042805239b12bc5f78438571b3b4d21ad8d6217f8266b876ba0683a421a28b3d8cb9c2e89df6e6bb8aa86f6d1cacc2a1f9b00197891bbf80d7ec035e29b04be0671f26828f90fb0464cc5f1dc3c9ce1ad1f0cd5ca02775a7f42477bfc92059eb2f8e36a28d323b873172138bce71491aa15ab677f5729ea30cabf3e63074357d7f32287d675b4c43b4adcd37e29946956153f6c72fe30c2a36bb3a8ca5a6d8ca5e6d596434b25f828c1cdc3bd0f75289386d5cfa0ab05a9887503deceac797714cbf7893d3117c13e486adab5f4f17aff163beaf2f8ad9ca975018b1616578ac5cf1e50b26819a63084265e89ba11ad963acdd23e240eacc4a6dc86a495798d35fdafbb66ad507dd39d647cec88f2ed903c0e7ba551b870f90a3806648d3c9afca54f9ec2fc30490b440047b763c09aa11a48d4f0e8282652c10990812b82d7dc1eeafd37218c9364a9ab853eec397780a4a6d8a172365344b3ac82f860c528cded83ad3581fa022b4d7b1e4535f0b938e9f81d0d361d985d33b5355f1f787875f941ef59c51ba1b9e83df4b38506fd810c09f50fb0397cd14fe90a909cbe571299a22ea91f2102f4e7c220dcda3d44ada13e970c874720da5963a1a0f762a8c1390dffc82cb68b52bf5261a9f6172ed11871fb1a3026a39caa6a96cd3a3ad7d1e40efe8d2a4e9618e7efe2af157adbc57192ad0a0c3a5c2c42a4a7bbb3959bde3de24ba141ae0da18519a6ec2dac8fc156a14a2ac9071a50b4e906e29a8ad0439dfb328243eabe9c723e1f7ee2cc59ec3a327520df6b64f630e25687c39d7aac6f247bae1a9f155da5bbd93e30b629b50c3cc1edee9189d93309034e82326c24fe6d32a962b2fd24b8fe5044cb8e0b37de7bb6305f461c8d7a6944f2a897c82e308f2015a2d8816624c45fc0b38573a8748b3cb0871c3bcefdf803ec2798d84f4978487d638bdcd33431630f46629220cb0a953c81794fd875a92e9928211386d0131593d21610325958d77a3213e70e0b2c1200c18e837f951c4ba11315e6b603abffe678a3b9008a7305872fa7e922f007103ca3c41576407e89a57022179da9ecdd98dfa8c17303dd4190c79065bc61335b2b5107636bd9b0c9003ceeaff3004fa92b4abf1553596074544262d989f7aabaf253c10aae3c5624f1d0e421475882a45d58c6679f3d8784eabeb32e61b7858721c6a3159fcb55af774c6fe2462ce176ef960b45c1d4f9d29360c40136c3be9d32148e7e2398d081710dd6b88ce3e5dee7748c10a6848ca645a97649b22c0dba219287c4c4dda9bfceafabdc7ac744e8663803e98a1810e197b186b9b2fbbc3d7b2f11081d1788e833692f521c34cd555924d8024ff767ccbe4dfc4e871851638c5f80f8a01c62d3ec091061153ece0597c124842a509292b4e1803026c1b1ca700859cc24cd5b761f5284f1ac1132a4e2efaa4afa16dce83721d1c4e27b7d76022e484b3d694edf9ee14f2b069d18fa1c5ef03ebf23ee5f215f797f3d5d894bd191a9dd5cf7e14bda998c03785f3892dc0d55ae6b3352373e6d8b32a2bd9d34da011a317e69b1f7a9b34c49d83c440351b1abb705f00385520edeed130fc3c0c3a58757cf5b8f90d68e74eb958b20bcdedb011d16a6a0f02649eb86dc5252d221532121de940fdc793970b4bc303b401e162f8fc802786554b77dc73b13b0d2cacdaba98c077d51f89ea95ae8ac7159dd9a7c0f534796892842fa8193cfbaf6a368e322d68d871d21e3f5b486d1ea2d5852777017b87dc754e87e4d6863416028a31609a0a01b387cc3c74486b26aebfe2f09d0579b87fc772970d167501056646b0792aa26e720fed91a377fd1982c2c8efcc3f000d870d0cf1c7d34f481f0cb72ac406879db5e216f8b34d1ca01f91f5a5c23a2bbf2a2f848d33a7c98bc6bee24e28c9325e66d010583c9c0d942749382e3ba0ed5dad3780df5a661273a02aa6db8082adc129978d34dc43ae980be1032e977bb3ec26a5211846150ccba7d6eede110da40daf72c886d872a00b9281823351ada6341d3c227a665e3d9491b496e16a3122ea758bcbadfae76e5adeab34b100e764a808c80dc22c6c43cab62d35ac09d3bb09c32be822aa4a5f953fbd20d6a3d0d88ada741cfd7cd75a9068f7a59d8c245700edbeb946ea30c19768bdd889edfdf8e72d733c42552acffcfbfda91adde53e66c4c3d7412774f6aa7eacf9527c521a4125bcc7f03fd4d4bdef14bb994217f80820a2598be1b7800ba021010df1234178f2d5dc53e1a571cc30f41187823bf6047f0d4b32e6e3bed8692a64a7e80585e273ae4c920b8f5dd475c2e1ee5ae3f1776bc4039889aca5db42b9d80af1a77273714765ce6a771aa2ee3271496cfaba7f5618da25bfc83ab6e0e5d5008a3997fbe48e797c7f8321f44409859424797c977341ea9da875f6bd973ff74ea5c5dced18c6f9e5b0ccf3c85550e8b92c901fee68d815371de7b49467333ef771c38300cb7ca9f2578788a2cef5f5c67591f0d395cd54f24d7f7dcc8ac8a05cf3d906eea02370b7af21598564e973c70d44c3bfdc0a797808ae0a2f7e692d65de9a006e10b7e362634f40591c0f70f3fde8c8bcb350d72322a2abbdb000e0175c690f84707820e4b435b5fca2e73da9340f3761ce860295572b81981e1daaa15464f84161f4c1211a4a2d8066d0c7568ca55840c8d7a834eed7a4d1f541dc5e223f7aefe3b06ad8e681dc0d5dde225738b4e7c3a0d9e808569d923cf8a19241be1ecd6f24eb4d78d93e4025e01c38714aa32e2ad0e262e34178b5d773b617e1609370a05b986547111f36d25f030bffafeb3d8a27c44d7f70ba01e5090dcbdaa1e0148bcfcd1f24497fe5d541becf5daae4aa6048c714c9a78f4c8a6f7e762a651799a79bdfe8a0303846c22a8c71fcba068599f79ba359086eca8b8a1888b05f6dffda2f296b50b019818538446a4b006a8450dfbd2a05ac7f495b323795491289e885e6dc663363b4f1cc7f6a7bc19143ac72c61fb4d46a2ade7d63802c517c66ccbe6444221b5705798b856e12b4c5df0c25f8e4d40a576769f1f2c352a0a9eb220dadf3e95431322ce393bca827efb5e0aaf4f0999f246576887dd3290ba8d3c0127c77ed816a4df7d019f24c0fdfb57dcc499f8525df47d122f059e175ee3da060477784761e95565056891c909150501f0252ce427f78e5e123ea29d70c5abd013b2b19946e3f7e3863c155a1ba5a438fb492cf777391c26b8b70bddba74b1fb29e40df659d17bdf19bcb409cf1e25ae24c0562df6b88b61e356af26a5899a28600096cb9feb26b47da751805bbe2f27ce6ff755287c94337e042309c3e4b1e0237d3215d920e5f7e805c62afd4d362685879562eb159a06c5c2fc986101397d86177714183900d7aa4aabb2571f7d20c4d4f583dbda9c8d8f645ffac911850024b125fa7c304d8f5bdf39cfb358b16b795ab39268b57ed1aceb5c8e1e749d5879beff9b6c03a8a709450635796254bf0a46f53417606e512e59b0663a927c7136cc80846f481486cb4fc3bdec2ca89de44464ac16f8d1d25452501e9ed4f1004fc123d5b6cb91375875bd6f5d272fd15387d33bf0fc430bfd3da81ebe2a7da04fd8841721954f2b1fa9b985c7cc081adfdf7c539b9b143039e9f6e8447fb7ff32753269610eaecfe337ee45c74a4039b9c5bb6a5fc384756f47b9f108c5332f56b6f213a3566441354310d4ad6b83c2ef212a1c29c023f43a3567b0a0dd26e2c42b04b7af4cdfcf15aeed45f5a26bb5f1d24f062181bbad71e0d13857636dd0f38fba24c417b166db8d36c981499b14dd18fc6024fb0b59224f32c0aea35e1438c142dcfd6bd121b2d580385503704a41e8715c6528d6084aa84279e8ab076bd6619a435cd9f7aea48dc29f035e5e58cc17ef93bc969f0f0d0d069fd41ef8f22eace75469510ab733f2529731a60104c83532988daa2d252c777b29c5cebbeb81d07b5084511510a042bae960ce4b100760714312797e5e4b8399b55eaca6cc840adae89d55f50f880257f8f729c552284253a0941bb3e2b95a80348c559b9582a4cab1e712dbf577733c54cf77b192f009a2b155b8866cfdbdf94a835e558fce429222e81d08359affa1a9d01abf395ab3440f7aaed80fbe41c4ebe6efd1795a7117d1faccf65e6d5f2434c5f088b7a04077673391daca91aad650947a2c4ead37635c410c1bfb52b5ad152e3325a7311525d4e7e762d4243c1c954e4df67c016f8b267c1928615301cd36c660a5c3155901c10752499f64c5ab040b29243deabb717dc7f52d57f78637a5afe7aa89a8727e924ec8702009a0074d6366a3f655a798617a8cfb6466d718f13731a35a662d2943fcce3ec8614186b6166c6303a538f18262f9a54e13b59a9f2d1d95aa205e65dc246fd8dabf48e92d24880c164e977e57f7e6160716af42ba4d5349706960cafb9c232d0b6d5579249bd27e353f2c233f958c76502099a8e931a0fd00fe00268e6d839ab718943213b3f93fae6e0866b7b617bdefb1f52bdcc38f5d9e444e83771681780409e3353f702e8969ad8fa59ae59d8d10d1ab059be96b448badad337ea5dd5e45879007864742daf71f63584a85baa12494431e4d8fceacb3396fcf10e8fa3978155be16bf3513e18046dfb156fe4c8fdab05a90a2649f9ae652c6b7a6290db687a1032b6cb308af694b6e83a818d0a5f0d66a62c7ba79868aef37dd27c0263f835d3ed46dbd526cc021d058150f40f06f6a9debeea90bc96fbc71542ee9769a02da59fc015bb09f86cd1fd41a849427e705a63b05fd5a8fc1df2fc4128d57f020c73c52eb5a1d1135aa4323cd4aef5d690ae59a946ea32c2c774d40a196ebae3e961036e576a5ae732763d93615ad59482d95217bc1c1abd3b101f964a3a662ec57fc5c54dea4928f132692e9270ed1a06eefffb46c9f314e899cd21e08e535ea074ff0fbccb9a1b8462263d49c1b9a74ebbd15169fee6a1e69a586faed43df8ce8ae6ebdb7222fbe33dbc632d87529dcc77eec9265cd0ef7b43056a94e2e8fd2a81b6aa87e4cedf7488feac5875ca51158caabc58334fd5b387147d7d75be10237ae2111257d9abef1e06ebdbf422ebecf0f57c25464631eb6205a20fcf4303f24bd3ee1a255762fb2849bc0213804dcc6a462c181cefe4a04d5625a61673e131746d3d148fe8815ecc2b16875cff18896145a1df3eb308d1c4828bd182ac89a82e8822290b427ba99bc3ca68d29b473bd7a7887e6c99d42ca47db15647c5366672f9be1dcf7b065259b090439bb84c2b34542eaca4b8dca8810e2e6793b02ae1b677c2a108956621c8343007c6cea904277df9cf724dc022a26dea276b08b022735059b8ef6dfabfe0321571c813b26066182379f0f7d14e4710ffe8026992262181e8c357e8e7ee7a17b112b927e652880649634ca09d2cc2ebbf8954eaf47a6ed290c037b54c3c5316416d8b9c09a8ee8492adbeb7a083017709ce3d9d2a88999daf63e6429e120a57ba2ee375a07609e512caf5961d5e7203948795cafa90cc5ca889e8cc275e4007498c11c858ceb91e505ef35913b32646a9ec6a01c1736ec8680289a20eec69e130ab0db41ea41e225843ba397e8e961801f10afd8fe17f2186b80ab75c21d6fdf85fc948bc803fcb2f28059501a51c0600d019eb999a0eb696f167afae676bdfbea51e9910f445ab7b928d501e0f439d084993f831c68af6fe889551c70d3abf52ea7240e18c07d793f7ba69622911bb4768ed8d0e75525eff46ede59d4c9e86993122016f3d66a87fc666ce32eff0184bc19ace9d30fa2f495111f3bd52cb67d402ee0073c81d99be64afa804c21e92ca6aa2068753e4bae2efd068c28cc16caaea97733244f02c0124aae319a962493dd637a889b836947b654da59527583d672c46c250671ad84d030bd1889b181e6efe14b1b458bf44e8f58c45e667bf8228687ac6cfa2bc5012c5d4dccc39faba252c932a21ebe7ca9675abc3cf1bbfa37053e9f84418e476e0d8d829baace55280fe0ee338319806bf21b1a30878b1aa7e6d33aa74e0898aeed644aa4998e222e8a6a69314bba0727e7513ec3bbc031e931b4ffb1007b1a6f1b01475fd6882ee93421cb5426bc158309f518def010cb32e68660cfc22c3ed4266b2ea39dd1d0defa3f1cc1e20f620f0539ac772a2918541c751b23b80b8139e5f31a71b2292d8883c5bace1a0c724932cfe303bad78b79b6102aac5abe1ae7d4f90f356fae4238c2babebe10578005ce90688c695a81e84d3d68df1256c65557b506358023c5d6e354a63c58d097eba4e73f4ec13c99bd086c5bc0dcb129a2025e25767448b609cd8bd83babec13d61a1baabf942f9bced173b4a6e8eb21cf14d5ffc9e33098b074d5c2fa534fc9b4dfe1304d27cf85c540bd4c2f7a5f3d4e05980990dbd62db7c56d720bfd2109e9a34aa6dc8910c0d747fe2dbaf2e91bb2527dc69a9e0b4503e8bb912c824bff4091744af11aa14c7a10558173d1d91e11b9f135ab22b26b01484a6cbc5ef0577888959e893b9ab80274f2d9d60029e52c76feef37205dec3e76a1af6e1f1a6447386cfb2441d7381f17ca0835758ad22818a14b1acebe74a2581b485e46593c02925e636f859dbbf3c455a6d447d8bd73983b90ff0c262676694e66ca737fa1c4b274d277966b6979814475c3258a709d86f0a120604a4ec50035aff0444b72d1f88d5cc0de1b57831bf53a40a6692aee85a17a0bc732c86c9ecabe04557a5f5b415a2115f38296aa0d470447559eeba42d36cf293c0f17f93980a91636518b3b2b3611867668cd27f2738d54706285160f1f68a58c01d278fbbb50687b6ee5d84be22eb4f08812d849804d7afaf331831efbb0952c0418baac33a8186fa196bbfcc7fcbfc1bbe7af78b04fa2a7518f54bf5265517fa9c8e873adad217f64f95d34d6c5da9ab34a7112ab86f5d516bf7de312f980feb1f6f2d5f586fa6d41668081ba1cf61b88eff17a99c3a2b00fdf79db32d065bfb5060d1f5eae44a47da800e377b1af922dcdee7f0738a21977d17cc28ea7d7a7f54b852f50cce1e55ca925e10a81d2276e724439be16f6107df6e8e520ce008f3e28b315bd0651aa7cb43db3db9354934d07d51cd6e0400ce1fc4d90ba15ff0b01a9f5248de0fe23e988f25243f4977ab98655db78f41a0f1f27380efa89309546088e8d1e04bdd463461357b4fb5ad03309cb6971e63a4bffc8d4d8bf5b7f9908659f6c3fc23481ca5dc0d53f369d8df108637dd8e313197621e0da7018e11654d3c3dc9d4e7e43d36108f32b778da35804f41e15668e32503b8f17d94ef772bb2fbd2937bc8553cecc8e39223026ea54494e5bd7ad58ce56c6c46a0d7200a0b5541a378a0820a1cbbc02e9c5a3037822db7fd41c3fbfc4f0de5ef29a919a439443fd8ed5c0e9132dc9a110707d7c6296a8d45810bb7621308360daab13f2ff412e1100a7317f57562ae0f0adf6418706162db17f19284578e64320696c0f3640261808f1f48a6e5ddb468c0693309937c3b5bc59ef5f2971094e5dec6072743b6f21bf8acf79622753971bc703f36153e694e86ec83e05944e1673500cc5a82eb9c2863df4489d9f04e1392b85578c1e2076c4090d16f5a23044675c1080f4ca4830e6c45781266bc582d38dbf83703106e72c8491c0e6cf4364b97dd8170291ebb088ac5a14bf2bc074dab48e123aae41cf8030b439f5f8c955dc6bf3317dbf7d1302beab4160e2e6b7d826d96c33514f67c3ed68108b2ab6128210d27984f9af5f253408f6af2cc8edca209834cdf2f801e17e18ac6ab0694eefbd10a7af2a52550f4a8624f03fc0bed8111c555db7625cab62b3c1aeef5f1bcba51ee05420419f1bbcdc8e57c4ad3d3182ebaf8123a4e88c67ba0c18225c637337390dfa02470fa25e5abdc91b929ef0825379c4481de7349e28d59a61668045aa396f36cb917d4b9acf0b2e6e4405d0d0d1d8edade7c96dc681e5329ce636d9568f6686a790b92ec145f5fc9284f80b8aa506bcd5257b661b5afe044495225e5eead1ce021069c6c1e486ec1ec237daa98ce60c579500348624f163078c784f120f94a0545d0be1966c8270f05a24b85de0b5cd1c5c1ca7aa818a1616f9ebd190abd61940ac498dd33f93d70806de8cd2b536186a7f19565b95de441c0bc70efe06a572d028c52a7246913f31c6f1803eee116a13d3cf9fc9879aab42710566170e8f474c7b1cc2134149a21e244253e30b209f297b6401aa5e666e26589fda60e9968409210f0ab6577b93ea4f0ad393f67fa01d1a4290a248e299dcca3b7b26e31a40d8b2daaf6ebeecb43a8e6e8512616d1fedbb9276722c0044bce8945ebf44b7d7a1228681d9bc8db3a87c00be76d200afd5242165ae4770002ab40e00fd0afd451db85574614e0d7250c15267b2ec7f510c9f139b2e9e253704e50c73254d4ac2e274928a72eadd0788e53a2b7d4348970a1498f415d538190e97e9d3bb9c5cbd09e5249dce8f9efb78e4bb73844ed145ee8f0f3e38bddecfbc936282973c104453c8be1b8290888fa3d103d4f800713c8c9abf4985069e93855af0e917bb20d1a432acf1b56a7da12cdc0266f13689d05b58a48b6da9d118ef7a1d1c3d06280d15d25872749d1d30c05c1ce8abb08779603e12643a48f03e636a15909f525aaf94611c37ddb9838d7a6921ca8f035b23c1280bab27c57827ed30ffa0143babfaf80d72fa53b21900870d62c638babb764a3ef88962163a7cf4be2277a28f1098d5c60a044f6884bd59a6d47e91cb3ded8c005db4cbe8cf201849f6c88e577016464500a92ba9d025f3db42f8ae2fc5ae12d14968488afe7e3fd04f7dec2f3ab60b445bf6389822fefb211589f3f77ec215ee4cf187da0a02a85f6f572bbca407c2f533ea13f288a309b2335c43b7ebec76f79821ccc26ed3d764567af635307c504eca9e52ca9d1ce1c1f3ae92f47f5cf8bd0dc1283c99ad7cb548d8cb169c4cc7cf4195b03328b4b491aa921e4ad682904ea51308d7cf4a0b7d240c159537837baa487b56b464eb268c31e5b3a01d105d14643f4873b3621d406792d7fd18c0e3a86eb7b5915f01c086f33c7a3791fdf2b5db020084e73f8d0de17cfb4ddb9d95854f1bc88a56d71960fa607d97b2b46e7d84a2f350bf45b3c0b64c4d0f2bf7acbee6afde321912b7e2255b36fff142e4b124dec030141392dacdf289efa1083c98a06037627dad5302746cb45704160b3bf12d9fb874eb6ef6a8c8709ca00c39ab58c1254c243eba63f5e30818977b60f35aa0f06892351d8eeccf4651987dfd295b6d233989f96e821b949a0dc3463f3f24232a32b89292d57c0ff94cfb07a97d1ed8fd68e8fc0b062d7b8a722024f6a0d623ed7a57476ecd8bdbf4473b1a9f7986b6085592681cc14e13924c02fee5fdcfbcc6a9ca9a13866f7f92592463cd47004decd82068cfa18a3535e1e1b5d1888a3f461b35fe7698164d2871a8a9a69315a5e148c7b18b5927403d1e319850031464ce76338dc78dff48f82fd0d4c833aecd3c14fe677931594956c52af349482b079062ee9b398b63b938240d5a16a0c825125a07f0afc393134ec641d637307629fc641d8915441fc22da584a34e19d98b9679ddb9967810fe9f9ed767a8b669d3b3f8e54cf8559e3102eba0992107f22ce4cff7d8c4db749510a4b05157f19178cc0782de3d56b908004cb568d90077cbd6882fd003c9188fd76cf6b8f4fefa534cd81b37372577412701fc833720f4fa8474538ef974e388685bc256e38c900d8072fb45bcc6bb608f3587ce2c4306e7e4ff8c748836236214fa77f6ad1e3266f3352ace5763489122bca6ebf7bae3b79540a24055f39f1699df84bde8bf474cc9587cb2e16f18966800f8e09a0da7afe43569bf3e80620ba762ffe9072d29f731413a09ba57dae9185fcceff1bcc709c9a48137e0ab6a8a5a0c5f477279fa225d59a60a486e8f326b9b373c0939e13ed1e251814caff2c609138e5b8f455333cd39b51662682c349ae726ab55109dacacfaa2a50db967c6b72ed7caf503dece1bb0710d8eeb1344b5d654b527380955e1fa411e99c3df979155982d354e6876a75641b582868fcf8c49c3a3dc0e3d444d4a690f046c6ae98d2685d9c5a29404797124e0e70b799f1fb5e257e0cfed2fcc8b61f71a1539e3035a41f178084172086444d11a897689354ba3847c75f0b92152b50b148fef7dee888833bfefd7896329a05e3f49eb15de48434296828b8408dc46ab82df37964015a2896fcc2b29541f269d154a7410880067b32281e9c4978338eea1624ff2a06a2b274cb0aca62d462872fb12e4633bc7788abf84b7decaced7da5d2df03f1a03a79b583b6338452759a6620249db2cffba1f3fe3f0a57a393211b3f02052c213adf6e970d405ab88ae89937aad99a64162f0ad88e1e49b124975581f2dbd41e2ae8492833691aa56b03fdd95a146cd353ee45efdb150d0b9c4997e4e80121265e63bf544117a724cba0f2b9d16531fd43660051115bb2bf432a8ee82a5e3e7ed7d3bca69e35a8ec9d2f487e082ac961cd89881a1bf0d717fb8482f0d8752e4dd74c74d43f2d00584d177e8aa0b7ed631b2b0d60b88e50dc622c95b4fb2899185486f728161dafc39b9c5cb30d2dc04b190714698d3e6382e7d64bf80307c64d6a233b0993997fc2e0934b338c42653c44a9d8c787493bf40d3d465378c2ed4b54677486e20777d8249c0e29dfdd6da26e9a297841a245ddf40b2a2f78ad03b8cbb996ef914e48e094903b0de164dcd1540c3162b6edcc5864698d8988d9846956af8c9623eb8dbce9e0bf457b3825f3895e35e18a01b23f5ec7ef7d9874eced8200094586bae6129a6f745a694b30549dc78d564f6a1c8d6c76bc62ad18d825a90cc56229377057be7a516b6f5691abc61e3f446cef72ce7774cad26451a444b86324e48da916eca7db53ed4e5928194e0c1476df0d5f8ace0252deb1748b65d1114a0a694e760fbeb257caf507b821af1c0772a5967a4fbeb859ff921bed2fa0120a3a198adca81f3872fa0d787dcc095068fbe72db8d7c4e0d7baf797ee583e9f88e4682ac9c5bedceac54afc0ab8022a458b57653236a0d9025dd1116a502ae6120888d59a6e3cb826e8bd72a82e6af0d0c88ab16f2fdd6b1f93ec14869721ffafacca450c7f337c0e949593f2fd41e917809020ed49a37d913f71a208b60915a2b3d7c56cee45abbbff4c5c2285a59db35425b6bcdee6641d7df6c9125c29e3a5f0949eddcc179b5a0b1beacb8019eaa1d8d68cef09351644fb596d0fee520d9cfc30c4482f4be94b6f3f4ca5f8112d3b40e56a66a0b7f0935de6eb933766d43c8c1127f888a9cb6359bf558d6c0ca059ad2600ee08f3312514dc00fc51b2571f4cd6948e2aa5276f17476cdd3108eee19ce51abf6bf22fd9a611bd1709ae06f4192e91b18cd25a8e99afb4a02c3f00d370c3bc85cf6e48d10a2fa83ab814c39f4198f58e60eeeef71128e535adc21ed4452a34e89f5273e2198665013920e0838207899a9dca78da6c133f7753d25c64c32996e4b8bb1920314e6b4608ddbe55dc3c048f88552b8f4de720eb007370bf9c03071de7298ed74fe047070196f0d4eac5203fb7641eed5fabb964c90961b62cf5a5b6514a488396a349d9e19e4f4ef5a6abbec6b4cb416f945716dc590fad2087d5b0a73fcb586d7b971981e56655450c650ea2170b4b5f0dea6df04d242c4c20d045a602698848bac8eda0ae4202b161228a77cd2492d45d1d02dedf37cc59b1fb9a9ccaac4aa4b7b59a3a23028b0af6aa9c87cde4d91537ad4f11400ce618a6b342c9509e2580f7a6ee41ef71e0c90d6473c6760667f25c89744ac38bafac5950657066df4df9172292e2d462f48181df0966656be413839e4f4f5dea142ee418cd671fcbcdf38d45361c90e5d5f153642317e142384c5a501556fabe45301f25b4af4ad019e7d888249596830ea1ddb431b9f013678237d4758b136686166d247ab02ff5163e95280b561d9f486fa0f5543881d254f1e469efd189d34209e020147a1acc83c711681134f8c0489433fa04d17b41da780064fd9d45539a8a53aa633449c29f3f99496024d27e162e16ab3cec39bdcdbf0ce07e9e9b2c57771e18ac85ea3f44f0bfd0a43480362ddb2d1011e20b80aa4a4bd65e48c89ab2a161c9c7823d40295b26262e4d1e32ac365d5467fe72d59d7cd0f7f28ddda16d4f621f844af8735019aca16e20080d9e0fb79b41e4a040dbb48033399db1c03d3a60b42b88eb8568705307c4a2a7c647c37ac281229aec276904878bebf4495665d66f0f7c75c1b121bbc5039468045321ebd44a554c3b92d9affb1ae983e330e44e58f18ecef8e6cc6308ce3c476b684bd6c3bc2b2c388cf68e9ce1c4a7e681f917954a927243d367900e8abb350f5920cacf16a423b7029de7e037cf0d4d49cabe5a419f560144c78f47397bea7dff12f382da44b19e587250a36bf202640e1bab6834209fea4e726443fa00f29ff181527de7853325e9d6e303dee1a92a4e95b85385f2e1e5635b4b953b5039fd6f5bb64be8d4c77993fedd5c71726555488f2de6a1ee2a931589ded05f5c41c93750074a0874cb21ddf7a2f8232a2f7f8aee69402b26c8605425acacedf51c4a685eb676c1e6c997f740b1d197d6e239a2b700427996528a831c015ef49c3d8890f4b9684055efcd67d0094f8718cc7a44b65a4c9485fc616130e330321fac14440101e5afd4746d38610265076ad168abd653d9a76943d120d814f58d41fdc8c208a260d28c0f4cf2bdc4a19ed579e705fee33e6484c8d4de9ce63e7263fd5d402ac64de052403040ac2f68b59309a19a7036cc1df3b100bb5ac11c02c034c79f7d9c1c55e8df1d20113212d36641402f48dc034f392b32b6a63e4aed206b5af55419026ddb9194b8ae27f350665ba1ba6338ace3e4add9335e29316d2f6de7bcb2da59449a67708bc08030930b5650851bee0c9752ade8bf18bd2c7e145e9ac397c4a25777777f973888b54c260f443102b818ffc11d23054f0e9eca323c76ba624ece5a00e38601d8e57c2bab0032d43403fa42fa461e877252a5801778f115124ec5e8c4ba57f189de3055f70d0cf54afd73588748216228cc0c9feaf2342a235759f312d446029a59432c42146ff4667ffee31224a9e11225f06c5889c3f8388e402f61fdf1e11b9e843ff3524baf7d3b4bfa1e7624cc28ae0fa7247d3b0466f75577d7a6be86be8b9ef42603fb769212a128944a2d744afbd28c4ef8d41f28741d1f744f2e75dea71d665d8d2c74ad1d1dff9a6804285c30e1d4a789ac06e5e3a394558475a405c1c2244c70e1f3d9ee4d8c163c37c3e6ced94767e3432fc1e5387428547a2c7dfab5c340a4dd6cb211289382e07cb087ba26f47794458f90b4d32fcde86448ce4ef3f9f1c7772dcd78fa67bfc3c8430c4804d39d8976fc37efc9e1724d3ffbe9e1488974361f784229953e168d54cc5843150f8887cf4828efe0b8c649984dc3d2e252e8a82c7ab6387b43f7cb828a1481faab3b3c48fb8705e3ec4452392e5a2fb0f19a5071378ec183599e4dbd62c92270d4737926738276c677a3a76f8477ffbbbfd1804bc02cbb76fc32bc369c31e41f208a4943b9a2e34c9b00747fd2425f726f9f3a51017454be0fadc03f1f2e4c0366d211017fd7df49801fe6862e60c674c0f766f5aebdd500b6df5324b6e06e8e062bf6955814442c0c9dee3bac9fede9c92ca6f8a5a654f21b9e9b72cd5550d8bd0da51d7e7569faeb4d734cb0009c4f3fa9de65124ac47c28520f06692dcaf9f8dfbda5d85d76fae9409910e2ed2c8d130cf22bac4bcc8c880cdf01a5ed75948b7754f93a669c34e70f4e808c4cbf74df3ebbbb8189ae6d76c675872b1a5049886a6559d3f964a35f692d40ba279be7cf2d35066cc8caee12517b1e7a4ee6ec36a9ae1aaca77297c80321d8394f27dfb0e20f5e703f723fcdd08eccc0969b0a5c8e851a114e9ed7724b008e985f8195298e4fe0cf843a119baedb978f3f8637b91ca935892bb278727c7e4eec96165d37d99ef9340d30d67b8685f262614057c47cf6da64e888bdbf631d9e2173d102f873e668a409c4734b46d2ef247fa14c1a30bd7242677ec774cb6bf5d2fdfa71d90ccb9efe2a87efbfff2a2e8bbef44607f07de97df7d993bf7fca3b9df018930c67fa3330e4da3e7e13ec67f7ff4dc18649418fcbebf10911cfaba8ebaa8c96bef9512c8f32d0b4014a44fb414d9b4ed2f0e1d726cdbf6dbf6dc6be00d4badad0fa494b7774d7bcd1b716e3ce307bf055d1c556bc8a2cdce340898a803adc0d6daa02f3f88676bc3f147b774b19bc57edb70dc6cf5384b73373f6b1fc617ec776f9fa3d9ecb7b2b6946b491343d6be858430be80b51048a919a0752112efed0782d24c12edb5cf8666b3a669da0d53f826ad3f3469af7dcf32cd26ad7f0bfb86a52cc4c5fec7e9ecf7b400a867052bc81d66c95cd852e4bef6f6bb0b6a4066910ebebfbdf740bcdc7d0f47715fdfc70f0fc4a61bae6abe7f438f036ff4d51e48e96a9afc3c7c423e1772d807fac48b3238b29c511f87971153e329183a1fdc26f773df11b9df75a18efb5a3864fe35794434f0ce13f71be89925092c339ffb9899f940981c60ffbea94d3aeae44c1deab07fdffe64eebb3b3ddcfd959929ede74340fb39a7f63df39fc7fde45ee37a703f0ac78b391157e2e697437b395dce90a93f88cca6ef7568bf7de865062289f7406caf3d104942a10efaf781d07efb099a3a347da18eedb5d0d43fe3853aeed3078286a60e635ce45e669e380e096cea5efb9e3175da43609b7f1f08fadbcfbf634ce6b45739776f5ecad9a0ccd36b9a4ec1f8827df6148294d2a35f0ac6972cbb0a6de2286f91d65a6badb56a4f48026bddfb3cac14eca05651825c59fe536bceabe5526097490a8aa855b43c2017ebd75a6badb5d65aabadb5a9b5535c1e9557ae3945768cf0b4601e50abe9c52ee20c9697112353438a5a5a1474f04dd5021e6572adfed53553b7453c4f55868b17fc0fc9e6041ec5ee5395350d9ea6a17dfdfa04ad822d6207b9fe34e2290dc6922901d2f9c1c9b5c2e6112da80b65e1895c3f26b6884f82329d9f5828a861eab38882a6c166ea3be2a7fa37311c1f9d9f5c95e4fadf1259aecf6941b9be7f2b2e3c72e0a861246986a3c60b64ff6a3180dd34270ddae143962b1441f5e50345501e3b4aaeb5c94fadd4a5148922d8f5c212debb4cf93346f99aa6c5c8689a4ccc8ccea14796efd3c6f867198a50dc7dfa74af4d74576a170758b32f2f28f30c30be30f6fbf61d96b6ef1db6e7a19fc5c5d9ab76b1b797dfcaf31662fc2db5f0b2a0e25bb8374da58cef1a97863872e8d0fd5ad8ddddddddb2310c0bb2457f869438d030870e9f0ebbbba594524a295b4ad9b2314c56e19476e19d275b69d8e3870f2019faf8f1a307d08fffff1f3d7c00f9e8f1a3878f1f3854455a3e2da023ae1bc99236402e19a887a346d9f2d102226d642b4fd903e81fe8815c34134c61ec29544022804587bee888a131a04ccc0c17279d523e95f74ad9522666460c8d91a13466860cdd213e489f20236546cb6a4891f2543667f7d0c027d3a905165ac07eefbd9a4629ddeebd97e3b8ed6a2c2c2652460d1c523e0e09e690af838b5386239d41faf5ebd7afe7d0212671e4903874c82155322dec3fcac470e4d0c1513aa64c0112bb3776f14d2c5697b8ad38a446ed7df4cf01ee681f1d3b28a53b1c4569e9b7ef2eb82307a8630580ec3976780e47c9ec61882346f504ea3f437761023c7af409d80635270bb74f0f0d58998638f82352be73ca825d9c9409df133c9fd2eea6361f11f09c53015dbf7c4e30fdf9f5fdabd719fa9d023c7ea6ffb2bf21e011e7a761cca43d484cc9dd83449418b97b90d8c9b0fcb97b6af0caddd44e1eadf26b40e76a1bd036fc355f892b7ffb9403161889649577dec19f071b9aecd7af2fe61a8af465ce9e1ab4f228770f125572e79ef6e3fd312e8a42076dae20ed0430803b00af67cacef47f8cd78ebac16ba1011e1b26695c33dc70c347a3cd50004f7e37b8f7707c7144f7fdedd0dd8482a0b47e4f6dc8865e255389545354412a2a8eaa6fe328fcf3731147d55015e4e254510955535433505505cc362ef6679b7952cd5441f32423b9006f3dab6911e0b3a165ca44a6e0ec5840ea4e8c4fc1caf3b78f7e054d34f496de795f688a30ad23d20a0b8b961625196630b9b8a001000f5a2dace1e5058c6b83780ac01643068c000610d3fe32956ca610a00a725495ccca47647952c1e426cf8651c9530641796c2646f26c26ad3c99b82693579e6f2bad69d51c7154b5419e5280422e9138a10367c8a1206e49ac2cd4733144591c65edbd2ec5fc220678bcd3633cc8512e19e555c906d4345a58b2d729af4ff2595c70f2e8c9ee450aec375ce40d88d61d8e27dc6a988279b8252159d368e178389e99f23c8e87e3f1381ecf13c172d740644aca0a813c2de45c6ec3f6936be45cad2f4fbab331e15c7214ddb8f24a2c04459e4fc116339d15d1cd4accc5f92f526077c99d0b9b8699ff52043caec4425613b4893c830b9b1aa660ffd1c5cf8f8b282d2564358d9659f2f19929173f2e7e4a3eb2e4e3e2a7e4235dfcb8f85989e5e92287403df36b78e1b1b64a27c0636dc919dee82c236686bdd1594e197a2e4a391dec172fccd252b297e75358d368c913e316c575e7072f589e5fa34963a025a80cdfea072f53ff76d09e7a7dad678912685ad57620a283fc83fd76d0acb5b56ed6dab05b460dfb3ba2eb97d183e5da41a05152805f2f878b75de1d1b1b9b200421774f0c8c90676c0681fbe6771d8d61617171c9fd22a34af153fd683a4a416b84a7e309792ee7742b68010c2f11bc4cf04ac1eb05af19f4003a8f322f1b1b1b1e0c41231fa14f3dc585029e43d078a9ab46dad5325944439dabcbd5b93a57cb0d1e3b96ac697837c553de8d7bdde61de953df5c1cac5352f24bc4267dea2386d0a7ce232a6b403636363f98e95740153ae5b9c07379aeb19379aeb19379ae86a9df62041ebd56ae1dcbc5238eea5ade42092ca7d3b2ea4f8d52813cd7d8c9ee0592c9a65ceffab3fa74898b1577aabbd0c13a9ee7e996d42f75eace80f5679e6437c5c52a338de5efbb1f3b195d499152a3bf5fea1476b0937553bab09bb2c2c2c2c2c2c2c2c2f2a495959595959595951f9148241289447a21646648cf82050b162c58b07821646658d052e7612773710440a66117e462bddecd0d47a3d168341abd103233a377bf8e418c31c618bf103233f8bd57bdb77567f3e4ee9dd7cdd32ee8bd3c9777e362bddf85decdebcbf76b0ca2a8b98b41933e75eec2f186decbfb72e7d173792f5917e428b0f2d459c3c8675921e19fb0f42f8a313195a75bd2c1c28ec7c5fa2c908083f2288a40592a61dcd595eb3b385299956fdf5405579e799a91be3ee79aa9d197bee3f9b936ac5c37d84c4dd96ba6666c039a9b6ca6445f7f0b9aa9d0d7e75e36b35950908fcfcfcf587f7072e57272dd5eb39914a86aac5cedec562b8b599f599fd3f1faf655dfd6259c925cab65e5ea55ef474b02e54cc73353b74ad991c09ea1b03ec99726c628940be668be1294e55399e222ad52da1b8886e7d28bd648fd21dd3bc218974aa2ff5014f301f0d8f1386aec6419e6a8d17379378ef226ba25b56b922b06298bb6a86b9ee45b90c65c94a5129e9605058f1d8fe7fac9f53dd74cd5297e925fa964c9533b9e3c7f8aa3641e6cf319ada0ecd227f536daf4bbcaac69b44aeeaf5ff04a25f7d729dd3765d1d64c8d5c33457ae2e3a85b77ea4fe5e95657a0ed5b7bd6cbd5348855c2b3dcfd5272d328b99f42a1af9b333ba6bf7dee4edd69118465ddc0aac445af3b41587e0bb0071264dad2b4566d6581884c5f6e2fc31c0d53251399facc54ddb9f8451f9fecdf02e2e8532dc9e05038c6c46231d93cbdcf0e0b50d5a7da3c9c6c964a18779c8c72ffc5b48d0b29b5f972b6d64c7dac790a9a294f364ffe3939a59cede5ca77b6d9783e72b2209f99e262b19f18504cc6e5e4f0e4c072b8ebda82e6c93f68f39927ff8b6db69d79f2ff7c643d77c4ba83bf98cff4037c8625fc623ff83ed54ab5ff72be9c2fe7cbc9ae7d3e2efaf7e48b5921968bf4a91af900951138abd0221fa8024e2ab64a918f552a61fcb1b24f2ab90e31e2c93c9927f36413ca101d4e3695e43ae4499e2fe973312e26953cc9a194cba15c0ee572e411104ce1581c8b63712c2a4682e816b4054121a2a3fde633e7d630559e3ba512c65d7b7cc85db0343be95b6b434e0b78bcd96a9aa669219e5f7d5bc2de8ff6af8bd8daeffbbeef35cffbf9dff785a307624db35c38e22d2cf5a9dfceab69a129076b43530e23b521901ab9522b85f6201b681aa1ef977194f655f4ddfda4eb9632a586a7648ece12ea89c022a217e267446112fb33c8afd56e60f620c7905d1ac6ad8b86d1c14ff3bf70bc51462c25c767cc67ec188eae8aa4e262108b28308b27d8be943d2e9c1c7bdd88b7fc25dd65b2ef38262de846d09d492a8eba3faff6520b721490a3e4cb2952e64b59e7f54e168eb981a6e112e8937c3c854a96e39764544082883395c39c53b4218e2130f5d0abbbbbdb033e3ede92d2a36b6181d5a4944ee379609124f285f89976038c324aae2fa1c89f2aa1481f2260194a28d247fe287ff4b27d3181e5dff8dfd75e2e176bcb7cbfbebc097d34f7937646b6325eec7b1f8d8581128a2b4614af4bfab8585fb2aacc4010212d332256fddbc31e12f0fad377e0d0e1a8ab83927c4551944660c2ade2ca2a5ce9d3303e2d518f15415604b90dcdfcda4f9a946a6019b9ffa5bee499a7fa77e3995db57171888bdd5d9273ce39e74fe9e7e7fec84086859e0d115279ead3212ed69fa02c226d7e7e64f875252c5c31cbf5e58f953fd75ff2dc8b71e91a79a1431c659f9aacdd31efaddf32440acba8b5942d1118cc32cb5c9f04458541d1c1c55a2550c5ad42aeb5bf906b047a4708a8de68e97294fd7ad382f2caa354e23872e8000587155b9c2959444d22949e650b669dd00983b85865b85865f8a98aa52cb8065e218e1a4b41ead4ecbbcd809643e53966ead4a7fab5fe560f90eb5fcd866207524aaf73ef2ec9ebeef2bbe1b7f4a0a5085d170a028f2fb908ee3eb5e0b4e0b4e0b4e0b4e084360897524a29a5045c9c52ce294306879a72b9b2d65a2b955eab54d14c991f99214c29e97c2a6a478912e6c205ae2c235b6326ae0d0a26796e5090660d3343b19b50291473c19a259bd24f298aa368e882d5345a42b1ebadf8cc54e9a7f4b3e253fa59f129fde4f92e64d0f5cb28a395e9cb8ec8a4d29bcedddaa2ee5d1abc9c43b9dd8656a63f7a3618116da3910d25e813b581e533c110e7e5a20bfa2e8ab860b930e2a9f088cb2c34323d1ab2f7e5509e2fcafddd98f986616b82a10d181609592ed27799b954717761e3327399b9cc5caad49cba33a7fd973ce75ffcf37faa1c831b7724d3a73f6e3ec8f469c83604cbf44959a03b08c16a6ba656627ea24fa9d36a47521632a5da8f2db800e6340c75993dc9f42fe8327391fe8b14b8c2d86918fa2f45c02eb3183c14c64e7d1b58f46d30e228d3a51f83a769b464faa14d68335336b0fa44ffded026531b5899be064a30a769684f7f86d16596a98b3c65b44246d0ef28d8a20496dfd4450f365ce419d29dd006912973e8fa656c2940797ea739c65d475fb680bd4ef9996808d329bfe9b52481a910b8cacf230fc79a535fc84c559e1178e7e9bb9a9c9ad73cb9e6c96a1f0d87f1878270dfd9f9dea9888ae46ba7e688eba606c7515da3e32819d6846ccdab06a726c7c5e9fdd7c72090ba93a9ffe67156dacc34ad93b213d2ddae9bde539a1a70be70f4804ffee8d79dedbd564a29a5d946257361ce3e900abe98c1b0c1b264a29cf55a92c0869820b062f25f7916bff23d332299203002c2347a961f7dcf90543355904ae60698df9dd7a283bbd5ccc5d94eaae4161ddc043cb6945726d97a73903679256db267cf2450880cb0dffb2f8ab4e3d17203cc67e1caf359c0295e79f2700165d28f42551004467fa1e01547cdf2ace6c65342fc345f64245b95733dc095a769f8d7d4b4fc343f0a0b8a67f364d62468894c09509edd4fee766c38368c4a50138d5255a1c5d928d59416e728b7ac93029dedb2d3ae375220aef3e836056d59a2e6c8ca55d3aaaaa0ce896e47d4b204ee9c582d59b9562e274beacae528bf79ad721ce58e32d190ab8993274e7c9c4071f2e3244a9e4f473b77b4131289eeeac643d18b30e017b3900b49d630cc4a251cea96886c4cbb5895886ab7a489ad15c9e8472db03c9feb680becc511b8fe4cc74fd3f09faf036865448bf3c799256ae57271067d87434160d193409a9511dff75cb9b087344141c0a2ef42456ed0904352c8c6da9a1987634391853c5fd43dac3c3fe483b4507e2815853ce4306f527a5273e4c68614b17b5b52c472b7e322c0a1ec7662b8bee7b1db097da7e445976b78e1b1a968499114e1ef5bf288eb9392c7111e372290871117c1172fecfff2225848050ef69086aac927cfe7443fd87b9113300cd68b999f78e0185d817d4489b178491fb1263e3e9ef880d2c44794175b7811e4c3c5f92c4d458e833c585760ff517c11e426bc7ae22816174d38b4e119af628ef296d50b67fe4a67b5335345422820018fa3ea1221f2fc156c05c5103a79fe6a3e468d2d250a0c1b478d2f82f29c0fa388a3288ff411c551b257c954414e5a1cf9b0998443a9a69583c776e27a317b316b8102fc6296b98ee3aacce7604d83cb7e85bfec944ad6aa2b74aaa6b585ec30c063cf30f6e1c447cc53e9ec2899824734c59229605334c9b383c5ba262e3ec91d8f8bd33dec94743b373496042bab61bc55d37a6da18dbc08725409b2ffccf9a2803f1e3a1ef61803907f081c6716853ec8ec4da1d34c6c1ce5465a475c8ef29b170f1c1e3948483f9f878ea3ec8e1247f1f058e228cb03c6a3098f98a3648864f4f3793c7194c9c36ec9d8ed744a768e90c29d07e2bad903e529ce6063ec5e2d9f516882000b102bcff23db3321a420a4d10208d9ef43d331a3d698585341a918064b3888d0d0b4846f2a4b481113453a497dfcd666af452e619c992522a649e26ac0b9a27f92b61075b79966719bb5996bf121299f170ec6a5af334f3ce54ab4ad655ea3c9cb878187154cd74e5a242e40a4b880596a58f067fb5316634f435c62ccf17d95058e3026b8ea00057af2c162c5a8488c076e529fdd1da5c6aaab645293404aed5a661bc46e183a0dc512c092a40ee28962cc932848270972f7aa7964a29ad08eca4a4e295e787c0b1a9c0c9f345e0d854e4642dd7b4641e7b26a469d0ecc9f0d1d41c71318bb9ff850c3015ae194c4ad0ecf420694cf273800d318bb9fe031e307a3a2bfd6ed4774badb4d6da76098d3f648ae406bd715d74ece23245b67c0ff9b7b8943ed0ec1f0c3160faaa29df0d4a2423193d95cdd428a441793e0d34742d49e09a16f862f6d1bca8e2e2fc1c3ca27666a81d94927a77304ff6d20d491821bb7c7f02d864f78711c026cf2830c9f3c71b9270d19065480158c3c400c3f8e8bdbc524d71ef95670b12bc62791b50c83ccd3ac59a237e9a6fc19a9b9a170d6b5a2d8050657c31cbf39dfe08a494ebbb98835432d514f1c56ca6280c1a68904196660d1f0dfe6ed5831c43be377a9863e07e7b0e8618bcb7eed9dcd406a6a0554d918d0083d8a5f34a2fa26002c8d3575d0b2560f9d24314ccf35a816ad8d5d0bd3aa2db09926e274ce4f9dd8bd773ce956ba6564295ec7a8eaa01460090b7bc704dce4c753bf3341bf5220c2d16d1a817b39ab943c54e9e54f0846167a6fec5cc7fce6e274f8a002019bab20dc79a535b0de3ae9777d334b62a4655a859c8950959c2906f830d1f8df6272f001e0f223ab512d6b8e6693e09b7608a236a5a9e9099c2e2ec0620000188e1cd8f11e3bb41b3908699dfddfd620c9d5a0957af799a4fc224c83374028388bc72b520d1edf0382a00797eb7c4532a196aec9a944e80dd433f3f1b71147ef1080e6f74a64c5ecd0487490e0d573879a5e3a2ce2a67f55ab956372ece1f40642a99281cafec050e1e7be6c49567cbbfbe62c8f0bc66690f55321960208f0194670c997bcd114fad84aaa0d01b854c6c3820e01831d28fdd2b466ca654533c86cf6c5838b2c4887556c95ecc00770d48e011868b5479e6c961b8f2fc4e068c27801b2d38531c65ca4010958cc6c583355342fa347f0a96f70cc68371942877b594caa076018f3d05aba6e528930d1799d61c30d760c58cc6d534443f7f5301564d9935aae4f950b8c0c5c42633c0f802eeafb78e40beefd5c03b4fef4229f5df2ea5f4de074057973dad7322d0aea5a14f1446a63c5afa74699840cb58b57ba218c8b3deed873c658bb26ef56a1b68803e511f1390e9026e68f11a6095a776ca3764ba439e2fed77db28885faa95b20061b11a6d29d469e7a42207769fe6977a52ba81154f5c9a5a1828d5a64846d3d0be24131333c2348456f4e5f0d92032b668ef7d2b1e58b0b4cb5f01dfbba4c5cf9b1d575b66aac40de3928a5b725d5cbc3dd0de729b7643c372d07ebea635d016f37941925610b096bb27490976a0030611e8c8808450c5d31c147185ac47276788a0d72b66864c8d1e1d967d32441254c07097bb270928950abeb97b92786243c786872ab934b6318982e409162479777777b7e2086d32c1dd5d9352b83b0a669b64c2563a4b4be06c4b2419e5ee498112722877cf122cc81b94cee557596ba55ef6424dabb5d651862735e481d212c52ed25ac3928bb4d65a2b286dad95524d033bcf92a364bf50bb8422484104aef3b7d6deeeef8aebbaefe67d110c22b37c8ee3fc391e386bad8c0cfd760073c95afce3e76ddbe4672b41d9dc5fce723bf0c079e5aeb5d65afb356ed5386badb5b6b3a173dcdc36fbd6dab7b7e4a22d65ebe1bb68efe5b6cb5d9b378e6bceab9ce5eebdefd97779bbddab352c4485ae3d09eda52ccbe766f9f3fd964d83fbbbd2564064ee6538bef84083c83cdf8273bc5d25d7fb60be9f63de8b33b65f01d06236eabee31f3d3b7e7f4d645d1cf1b95e1b337b586a98faa52758fb208e1a3b87900e340d9489f4a1bf5f49e14acca42785325cfc7e547f045e17bfffee76b7bb6dee83ccb8541a69fe424f0b29962e250ff23d89dfdd73b1d6920c5f03694b8931f7e3cd5c885dacefdb36efedae3d6f870c4fb0d411ee70b1bafb0eb23bdfa677a9e7efc168b6d43466be1bcddd7af3aa8760dba4d730f2594881c7f6294904c85cdf25878e7a350ec8ec61488ebaf093e95f4ae9f70bed41e2ea41d24a40773bca3467b8124517bf5fc60a23091c8ba3b60e097e4e2ac9de8f1268c6605a9124b43eadd5eae4f95cb660f72831cb151ce5ce9590e5ce9d3b6a9f68a067edbba17d34f7ed7703ac4bea4ecda92f9cbbb7a0f6171cbd7c7ffb0a561d7bbba7239536b9070984dc0df3adb617fdf6e5a0bd281c6b7d559cedbf1febab2e7171d68ac3fd386113067bf223256856611d79e9f034f18922a3541ce59cd464e76d6fff7e376c7de599c23a8eb23baff9c4c5267e9ab287cf74bcaf85e3f6dd73a0a9079c6928f3fc682a78df82170a1eab8df6ad72b0cf85637dab19014fd88c62538bd4397d260c8333369bb8a885138604dc595adb38348220abbccb349c3017e76c12cbf35d1c61fe38615936f819861892b022cbcf3e3f0fd1a0eb97514a57a243c293fd3bed83e531e6da0edcb4176f7cae4ff38b8b5ae8650ef420caf2bd981a3874f40022a448eb264709ecc98f94021649d2f9b70ab3fd8f63e9f2f6614ce3c30d9941cf3d80d1bef4efdebf1510dd73df85a367fbad389b93fdde29a5e5e80dc7d7e4cbe842bfd21278fcf0c5450ed81e24b1ec4f9b46e76680ec41e62dec418a743a0522d35a043cfebf0475be25114591d7ba4b49fbbd9aa7bb9c4287672a1c3390b96f707b6b41a794524a29ddc21b76e198012d744a29a594d239e79c734e1a0ab9417078a6e8cf39a7b5f6859099b11fc490faa57992411099b16110f6277d0f7b9e64b701faed90ee02f45b2279ece7381b76dfd02f9fcef4b79299338143e6537777777777774ae90b213343c3206698841333f71c76105cf782506bd7d9b4fbee9eb5d6ae697777775dd73d471f77b7ebef3ace56ae6fd3ef9f739a72e8d09443fd0e81d4c8d7454aa70deaa89489115dbc4d4cf9026a0150edbf734ef12708a47d72ad3ebdeb28973967adb5d6d973629cafd28edb34cff3bce955ea795658c1bb8115b6daebd149a715bc1b5841e79c56f06e4027142aad4a50cf0bc26ddab787035a2f124a7895d2aa84129e376d94e02ae72b2da094ab7306ca90fad144f40f637071d426696903a912d57fa6384b3d8e7a54f83e19479df8a81092ed216eeb3e1b5f087375d23aa98c73820a75525aa98c7362b688826c0f51519d947ea268039108568c131304e262d5b8805ffe31f53e06dc5a353b6d406d30e2b8ed5b7d2053cafdb001a95a7b359626a4a74225196630e5fe512be0fbfeaf062b66e60b5977e6878ba3a8aca80cd01963f11c07ad5ecd5376859d1508a7beef7f991b62c04e89f3e473878b12e8e1a2bf945d133b68ae98de04a2818b3f660bd90f8d814e4924ddac16796c5996f2819285a408db35315b544147f193856c88e4b3fdd90ffc314f366c255409f8e5ff6ab682405ca49e0c27c1fdbdfd76a040fcf443566d5d89d2f58b8b1b029edd2da329a5945a4a9f053ba594d2f0fe044b93089c129d12dcc66285e5863e4f441a618d854501fe9aa5fbedb1a36cb7792f8eb2dfea07adfbfa5823812350368cf618f486d1c251fb97aefbc13ef7f245dd77dd7ba0374c17765f0edcea039707edeb4b91f35e346b736c50c3a05d16c9deb02916853eb75fbf869d44d6499f2f2edef78894b7a681bf5b5ae495f0258db6044cebd77dba7704de9b81fc22f0fe777f5e21defdeedeab7de7faf2c5c56b8395b822a74f74ed2d3a3b5d9547648b4a9edf6ed355e84f149994096be2f364eaf0ccd91d3a6bb66837add4be89be265b261aae6a769619fc0583cec5e70923513c512477144c7247c144963b0a263f3977144c8c90bfdc51306992e57b35c83ae4b4806f4f7ac3eb62ffbdf76af76a9a9d75d2f9f5882953c3ff3ea61a0cb0e72fe28e972f12c85f2b2a044c80b913efa1c10d76f727de242850b0820a54b0a4892b6688ec400c2c30028e0ca7073633c664fa3aee4eae4c3c0e8e62c94d901ce104227878002509222178a2c7c809ba70e352c20ca53434063c93dd085dd7a283bbeb9824b087ee58d4ed4929dda5cba58ca8c48dd6453dea10cd00000001058314002028100c078442d168349c07aba23b14000c8798447c5e1acbb32486610c19638c014200040040000064a46940003675463f8a85534a1376020bbac5f64302230b8959d35335064ebdd0d425d63472d8348a07918d6ca5dffc4ac3bb4054e55c6b8156a8d031db0c43dc418007d6709d956e846a960722ba704608be2534757cb50bcfacfc928c377858558a40145459d0a6ea1206a03fe2e6e01cc204029470940944e3865d0268c3d35e851faa13806c5e2b3eaaaca9cfcdbc2abbfffa959477b58df61d91f457d0bebf587e19db226b4d04f261a922c302d9d5e24efd3c66511b29a5d103d527661379f45c007ffba73129a171a19672b77f824a2dca7df29c50d379030f00701967910cdb0fe201067730473d4575e79beb514e358691828e42dacb39c30096c7cf7f2231fe4225c588608e7f4d04f8397970e47380f246a200c1190729cdea0eb56d7b59659e9baf15076244a243ef74ad2cf0192f3eb258ffafa3e6a3df5b1b030c5e670200b9c26b5f9b950c2c0e448ac13af2bcbe99cdf269ebea212bff4aa45ce7223e1e60f91223cd918d330870fe89f13699e3ce30bf621e28e34f6d0ddda0a5012336436f751852666f7f919e32d10f67288017fec028c25105fac173e2950b2cbb009b1ba21e7cc0a4e4c22ba6c22c56d2ceee5aeee997a276201a98dd1d4700807c6b5a01869d3a8189b68f31b6a12ec70ff4b9db551df26937237403cc81dd19eaeac43faf4e2a2d28ed398e33d62301c231e7720398f89c35b322c6c33c5f3ae6114e1cc191d817f9eccc32f78dc9497210114a7b6f8ddff50d4eedac19c7ed3cd555c90fb68adda62acb142c3dba83b157533f1c32328047c7f6ba889ae65c9e524e98a727837f779d7e33356879c0895371993018f3310c9c412ecfc0d14eac67dd9a06217e21cee2589b9cde796ae8f1e254fb3c6144ab0ab40704423b01f92e8e34f19cc3e1da8b1353a0bfcb95c2025aa655c66dc29da44022be16f88d8a433868d6828033588275ba5a713a4de8632288bd8055ee2d9a50c291d80e70d4c9a25ee905e83863dba47767b471d99e3c2bc46aa44a2d45151f1d80cfb083e2d6865e528f6681a50cc994f0b1c9506f8fb828fd55018514b6ef0c884a9014f7c92b4da5a7cf69a91224e1943c80890989862a18d678ef9ac1030db2232635ed748161402c0064f200cde507ed89b18939fb9ab0e9bbf0b23d883baae833e90ba93cab02d884ccfb5b869f67792b8538cdecf01b1af880cda6b1f9541c68e59cff6c559491a0c336c01e0c32b60dfc304fefa30ef46e66ff101985998d829579cd9942526ae67a43674b2214a217a8d613baf9650bdf036eb2a6db29518a20281b732acf47637023571c459ab3bed92fefa252494e9391657b30d131965ee9b5a2d0c4193cb1767022353873d3f2edff9c678b35ece6f45ae1cc78c16c66bcd9116cdc9f7e633b21d6cb076e59220b8bfaae12f8b9f69e88927a0ec1cae7f84a8089ad99a5678dab46b16c1a159cc3a71ec054f1fbf4f7ec873b10ee3ea2e1850b10f407a7193dfb91c56a840bc490aa3da8c75a464a8414810130ab6400d768ba55f70068bcf2e66455e84f8dfe7757c740c17d69ac3233402e67df75add01b04f9f0e398e9eb17a4f3179fe1237baea32f5d254f880e8ef1105eab27efd097f20c052f055567b008e88a404cc32dde9bc004d1c16b926921dc482582533d3893d4c0779befe17fcaa5af52e9a4a362ee69c1e15de594b16e691535a2cf5c23e518043571c4f30735764d02677699069d9015db8bc8e515287a05bb5c021547ae4b9946a1c50eaedf2915c28521b7ea95201e74f79d8383e9448795941c5452b337d45ceade7725aae41efcf74119c153cd83a403186fa294d8da844dad3f84ecea0652f7ad924fc8af8dae1e93c454cc13dc5ad8e674615d9e27f0fe729060d3268456f2691980a2f345ca0ee48b62f39ad2a8546efcc7f7e39b5cb97ca3c4fca1924192f9fb8fed2994486518ca60b69dc03a897f051185d6c71df0885ee9e912fb7924f2749d2ef7f0900cf3d7a4a1e26398330f90fa7b2ee505ec46f89441216d9b9b7de1fa48420644f3dee79b009b1d2f8916b048538f033cd0b7997ed798b600fe2e44741ada1689e6bf03468304d0048004545af595d933a48d302930557586e6af1b3892f1fed99875a565cc5fb282741e1193c6434d32f1688fd9e037b31298889e709d00e4ebb030fb15e38fa7b7e7daecf1c08c276892cb20606e784f341cba83f0dafcae88ac20b2006427a939dd1f9b3c68079c0b49eeb30c5572ae8875eb332b1388ddd44798863d168a7b7ac0afe5fbb6d0c4740f51ea1d14eb2b61cd0d8207131d0c123e125c81167495b7a0617bf1b9d59ee9de7a1fcd39353c6350d3dea0d9b4994c7da0e01764d3df2794205b1578f6d90808defe60e0c4fd6cc36b1fb2c19c3538fb1e34597058f2108b45f01e2b4d82b80ef30e01203fa1df8b14ff4b164d41e4b46a782879710752820e38059fc1db2bd11435800c57d886c4116cf204cc35d2e9c8cd1d90330bcaa7b36409fb3851d35dc918242e9287e0c0dfe97b44db837582095b29589c2fbde774c057c4ed7688758f77cb534328c7b4c01d0db35521c7c8cc9abc533b9e423d7da7e82592a215a784ad3e880d772752e303973fe94f5f9e4c358edc24acb39a96dab3db664322d9440289ecc78f397d0ad843da7d46462f4c81ccb54be022b6bb95643aa0badfdb305401d70928d11b3d874e7cd63f210d61379eb510db89313be4a573624a8cb5f8b0cda3a2f3e6dd74805e33ee1f8a4d79642839347dd84509f346ea1d41eb5bd7d09300afde12427ef97905e66dabf41aa46706f8d92f89656945ddf5abf8d94b5cc788829d8ed7d5de0978b006f6882ebe1e5b17b41fcd06d19325db6d44cfd94733acd86eee3532ee037fb84794254662c0629a3d7d8cd298a22a63498a0d7243a9ef9fc4465c926145ace6dd6663cad91a2a7b137486bd2b7fbbf3bd044b2012b09e962eb47fb1b548207ae7084ab542b6444cf07f5c6c16ab5ff9c40610bb4509541ae2190f0bfc9086da3b4e60ffa6dc41cc22669070f9abf8286edd3aada10a6ece2c1fce798296b822e460cccfdaa5f57d6c57f7e6049cc582ff13a17a5474d57cb76ec836cb35f963d496433ef6537b87297f56a94bd30318a54b7242bfb1bb54d430fc04ce563e447bac528454bba73a8980d5a62a85e909248404a9d9c4dadf0c0b93ff023b5169614cc6436d8f48f0fadb520998c68787b282a56f56c25c29bc34e6fa7675f31c5449bd028050021a5dc23813344b0f9bc6b3637d9139ef6a66f5d5db4945182fe50ebb76d48c189538b5083cab66b3611e83fe81aa37755c09b3ef021cf1d10843c88a761645a6d1ce24c9157ac58b49a340a4f61f5aeeee5641e2b3a972c91ba1cb50bcf27c6d8aba6e1413b5303db4a8ec30aef6e2135c704c35a1e17f406f85b782bace92ad5b663053380c7ecb269c3b5b9e4f2d7c3ef99203844a5f40a5531485d1f116d88ae6010548400864c7cfd89a5e6912deb1cd5041674012f7121ad63922dc7f00b6b762eb465c3856febf2a35c783bce389dc27554a0ca1548b207f7060f4fbb6f4cba666f2c1d68957f3efc95ec71785640bbbcebdfa4d8030ea0418c59cbe0e8843ef14072eb7d5307d54468626cc312873234456b0fa53ef85f3371a32c9cc732d497dbaa37e0d45cea3979bd1629ff1fde2a715451a1260f8122a62c80a7d0ee33a781dacaefb6804f84ec35293140abd055473ebfe7fc8afde1b2dff05547f080ac3430eee28608d0cfa6b475f360426734d06cdd24e6ec678fef198d978e0461fca209bd3b318db116346ccc472bbf15be81501b6c077b063b426e93ac0b7b52ed0d48516716e652d984879b51e6b7cc426703bcb2d029155daa7fb1abe32ca8a86cc02511294ff1d7aed718392d8a9e5cc40e081d6a71e8088882ef3012d504e6e1505ff8b4aea8e23247fafc35607ec6b7eed6c387387131fb0365cd920369f5a8f59f9281534549794e938977e9aae8c177950ced45c84154913c0869f9229d7b5e559aaf1106cc2696fefc39bd826544d8cfa440976591226bd48d574f3e70981a64b1870e0ae153889b9cb96b650fa3bb7a7a4c737b156f8ed8798d7a7d4003b701aa90773e4aa6ac50f832aa407ad43b3032513a6ace5c53dc899e071a79bfa1214e2bb896d06c6ab5a4c078ec55e137cc0a3193864cde6f5092b73f511a65c06183d20368b9e682867a041e116436d1ec81364d622d7e3b637553bb7e06c799360af4ae2e027d891dbf185300c1ed1253ffe39b9405af750a5659b10e8ea0eafe6cff4cf63aac0d9c540711e42c59e3ddf6a630b28392c1b720cd5827fdb8ec24be15393222c3d68289ad8b620fdc1032a0e6976d55b7ac35aa5243bc87b263e1f6154e045373f3191b140f022372953e30fabc9c6c10628a6159bdd7445530e365de938b36f358cd26df9b33ee651862927015333d91405ec4449470450b8032f91c16ad132772a2b607b7215efca1bc1128d57374ded1ea037b4b69c84a4c7223da5a6454a080961c47c7c3e324c71d4ee3aefd35e114bfb82d70b8770f4f4493e64f4a9901d9d6cc66e45d4ffed9f091e176b5e0c779cecafaa541e2f9ea725d98ffe401e50f6595cb4bd6b6795f99b1996d7ec4a8fec6856730885b4268efeae34b91c4b3fa6a6889d0c1d5e2e94821c5c8c0c93a77af93c3af969fb52f668c9ebe7cc238ec9bb5f8a0ac23a0cf9f4b9b4d1d09783cf790bc69eb08040a2f21a04dba6cf3b52e08aeeeb7d7ea0351df441b9f9b71eb0c27da18ae15117c7b116501e7764c1ecd133dde82e33944cf9e637768a528d4a44fbf2a135a9a650c43555ff91b10636eb6909ea3853f9b6d8a299bc2c2c8130581c8d149647324f5e9f6a9a83816db3f9b63f0a0e506c1123a293568b885a9e997f0d56a63f8d9211922e1a9342ce5340b19d0d2bdec37e694771c8f411d18cbbeb4e9ec100645af50c631df49f3c09286a7bdaecc9ee62daedaa89b77e119d926734e94e339a05495ccec4e05179a8956fac93d0d2b7bb29c978a77b0e0f6718c0e1c97498c7c17358b4c650bdb0da66761624e7dea9472da65998efa82982e8a73a915ae42ffb3dc8b18e89c255c9158b699f1e30e95c6728d798ee4fe13ff8fa389c3c6a8ba3e5711e5c6e90d1ef9bc67d0ab41d32de1775d4af4729581a9808dd02460d09bca72faead2112a864d803539fcf5759adafe3ef7c3520f94fd3960c42bcce5bc1c63208140c5cc2d24080b69d838f40826b4720c046e94b1f24cab42225938f194cb0a0835b045f896f0382864783dd1b745a6b339a8e759c7398ce16ad235b53a51d7bc1ca7100e132451da89e80847070703fe575ee04daf37a59221145a0d803a3d52af30a665bae86bcb7401092d9a89eff2d68fe408a2d5f1d72c8b1d942f9c348813ca14c091cbef87504912cb09c2f54788cf2fdeae4026713bf7bfcf6dfe2a17b1ec03685612d1186ee7802f3cc69747aac8b71c2de6f5d3596dceb779d9b06fa5fcfed0cc425eb3600a9ad7c7dfbc6b5d8d0717621b7b9237401bd4ae933344eb9e6a6f15f655e67c8a53e6759561eecee8a3e3e3f89a1ca9d5ec98f2214a4a460fd4803a6f2e0a79949155761d19cfee75e96cfb6e5fd49ac8f06e6a7b1ea85a90ca6fb5ecbcb0f3dbf4d6607040213007be9c868d081ed6b62ee0c54d60a2d7bd7e48cb1189a15132aa89431dcc0288cb5ea3db1024741dd8e8d728d5f3f0a3828240149fa27bd494acaa170ebbe0dae786e43b14f6eacc45e271f9f892868e736e1f9a47e9fb82e11a5cdbb754c2d4afaa05653008c87fc4983c30002807e0019011b89666841278a377666b1bc528625545b9dd010845e13d366623fa7933846ffad53a271c167e75a6e122c0f4b02d1703d1ab78ce41c0d067661b5952076a050c96d66033e408569dfd0f61e87f61176f43f865ccd8dd00fe890f418bfc10d516ee79661e8d0cc5d807c2b620743b7f0e70ee9e1afd590fbfce16c770b432f3aaa819174aefd36d4c6a8b8f4ceda85485a9604de7db82a9d1b4d67cf84726adfe04e560a869ae715a91bc8047d060229d9cef73f3acfb67fc5b73ec7d03eef9499dc7a92275f022f826e0146f4cdf782671fd8a0c48070851f68c5a1d1281a4874c12537158b90d27a2c60281ff7ddcfe5ad7f8980387f920add43c2251e829d3f0996f8ecc24c42da7c4b1fb1f0889997125befc33c147e7bc2b4b970add2190ac789d1a29e2cb279011e6a908574583e0a5b9350782c30095f1e48e39fef885012d4f57c0c88f63f6b7dea95a8e5af99b5fa244d190b4c58966b1e3ebb54273bc7bacc6b8b138fdf4947aacea48de9f1d4fbee23d3530aecc358ab93c74b9a4b30d39d58c719b9e2fcba07c5cc99adc8e619535a1274bbe1fc985636aa34bfa83d4df480fd57ba8e731f0bc820bbddb61bc0b627b3ce5fe9e6a441b7b7fa2ff0f740839a73a9104f33b6bea04040cd8fa016c4784a1e95e1f1ba887ccaab09adbc77356e857b3891845ef27e7b056e5b2100a2807b9b146bbae0c101b8cd9a1fbbbed9c85a847a8c7fcf500351f0caab74d7a13c1c4bb419d14753069b389dac23a26723573c49c8826da3b46821147edd0cc03f5c68d471936736e0b6ea382cf700584c63b3c0b54b66c03442c7fcd0a91d93fe9e003dc84d33625b8d81564b36ddcebe65eb805a0379baa5e467bff9107750b860d26397f7a858575e1d500bfa0df25d60667e9105ecb36825a6f1f0613973757c16f3f3e1ecf8d124a067360c8d1a1f03f5ec84291b96f0d277e6b7bd89cb6689ced78f4c3b55ac7a958ae28b2a300a2a4e4cdfbdd9fe2ce90d99ab38f0a00186576fb5c2374812e40c1ee6e252c31624e4c1b22fd5595eb6ef9c7322eb3ab6fe56a2783ec4d06c7f591c24517dc2af791b1c433daef5841c485fd8c76abd55fa599f573b96a2906b13ce3f3750746bef12c4f493b97481d59d4fe66081254af4a06c939d5d63937392b94c2f0b3d43a3cd1897f48914ec432c40ee22dd6e2fc248a7f6bb9f1c40da145d2d1fc303fdfd27e8120942bd6151964b97f8ed88944a176d574b900f3bd79e3dfb0fac16f1670512c8b24c05be3258bc2de86193d7b674a981c0994ed487069b18c0467d33f146316f5b9366dbf1443866bfb89c1a64542eda48fa0244157b390be064d1d0c9f3d99a7836d21a620e9e27563c410b883258aa1e8673419789e787dfe84fff295fc248b51c37750b4bb73b02486ce4cb21e1d0e1d12b295de82e0a58ca0d5bcff667624562c64fd8fcaea72cf9ab994431a5686486ccf30f699e43e0b25c32f1b0de857835eff39ed34c082902bdb5efaecd68d0c4d07e904653c5e589378ad10207d8160695ea35f352c130c0335a2108a901a279c09c99e8d46607543cc0f3e497318d2f1848f2506432107a4f0da0aa80a386b00f1e99fa278057840040c45febfeeeb4f594f4f715b6f227e6db8b5d154639937816a0300e0047173fcaeb124976feb8fbfb6a51c29970a199c0ef4651c73e59ed80fabe735aefeb2d0c909d02f29a5c01d153f5003a103e82d377226bd489daca4201a406fa115752d19ba8454b0deb4740b6387be2d343305d3ae83f5e255c64a2a60d5259d78fe6fd10239a93ec4f9294265c89072b6613d368dc4ebb22ad60eb4d5aacaf580d65d742f6b90f62c818b5ec32444b4234dd4f0ee7f9fc342ca8d70ae05600a76e3037bf04244d027c0422bd249091b70e1d9cb2dd5271a3ad23b27d8d3b6b44488395ebdaca8f96ea79bf08cfd6772cd8768b77333733760edd3c6c7a5acecdf6d6cf97b2b8c60e9c34b5fe207ca9676af9fad38307dc04ea49476423d966556a8a11690e107abf446b8d0cf7e5bda7274e36b10d8030706969ef8751f6c77d97b52fbdaaa61d9c1ae8f01f6b91cf419a2b2dba291282788e10753b339080f3c0efa2bbcf904a3c2914b1d1b80ced23baa96bc47552e9151f7fd345e1b3244736f3f11e4b833ff142bf630f0a58d946d74742fe01e6dcbadd14ddd541d5c35214c88753f4417e47f15fa4cf1a5aa31e550fd96a13813b630cc06505360061b315550b05dc08d3f30e8e28970665fd443faf11b0581767a13d73b5327bcb77b7cd3ec9888a7bc09be81f434bcd1613b93608fe9722a5a7b41b7ca74786447d7ee5090d6d2b7370decd7659582cb0e8debf804cd38f6e7329de7b3f085044336c83a8db6811b1ed497b79b6f5d12a2321ef19f4b2fe4f944047873a0b509869137ecb0cd9f392d38257119ec66fb7be393c2e304f0a5d1fc5a2a3d128b97b7574c6f7be5c90b864560a041e02a93e911d0992b7cb6895cbb72de5f57ed23c104627a0fbd268c1d764ab08953c033dec4501f3d4c909b4a04fe16d15380315b99c596114da5d13d20982f6828f0b95fd5859a8a3f160cdc58d3349b6c03d07d632521ccc9a8108916a460bd3797e2823b47f0b1f1938be093be260a2a2a6cb83026e54655f205a268be44acb5c43eff8230aba4dc46f0e3208327d8759d7706430d84f8630519c15cf627073448e2d54bac4ee16c0cb091e80b8c439809bec0d0e3ef9e818cdc81acdb22ea51fc35297496ff41d94cd2e6c4a8ad226a691deba108b1d23e15c92b932862c3bde66b2ad3e348e17829e00a15f523a7c7a7ee1948222b0615756f666dd33c48c2cc9130e18a3b9bf8f0bc333deb514d165a53f6272a7f305eaf33da4f0cc8bf8df33d87e4196d20e77673f99b5b24f015f5af94b54f8d26ba32b63372c84e3f183b68af4857900fb99c3e69659331e725aef35a343a253097cfb15959759af61116061c98fb77be3723f858f90c4038abaf2aaa0fc1a0eb5862a4db1a0c01442308e63f9c472e3ae0556a70b61c9f2c8c8ba33592dda60e52085337a1393ce32d87927bddbc5040d671e0daaaf897faec7c60c87f26205609db12767684763d382bd909cb9abd769d0bf17bc6e198ea323580a999b580775a3d90b8501d0bf42c907471a01dc2b1080408f0f81673c7a04705f7038cc5ee1c9204e71c5c84df0ac99c399e01dbf9582239423be39e33951f1ca7259a543ae36d6b374fa6d154e4bc6f9f00f27e7db51b76a09a51801f387f89d2fa3ca4970b9f4475c83a1b0d8f56bca23b5d25a25ff83aba024e0d1125621732536fb55d113ed022eaa60c7ea985e2eb5150216fd6ed3389bf89ce65e0700c7581062a608c69e2c991667313972baf701c40d294125217359573c7924bed5204aa70090e2fa5a77e32c80912977d6a2731c905edc25da89ceff76d95d26e8acbdc97585a8a1bd17dcbfa6fc203795ebefc64b74c3ed2053ed632e2f50496482d85ccdb103cde64052bdf939f0d119c15bfbb860f7807def5fb506df14fdf9cf2d66727440129939c7e13253fd047d9d97028f6666ac0896de83bd487ff235413ddaa3036c1aac3ce52c6616a39b2a3566f76eea205445a8137ddc14c99d0e1b2727dafe9ff0d90e178693d041ea712ef4828cb2a4c37de6aa45eddfb152ee33cda5847bdf77e2040758d4b69f0bf5a9fde2ed5c82b3854d9e39dc0fc94b96e239d6dd993b2efaad18d3d1a5daca502b913b47c73b112d72dee0accca7c5677b4e68a9eb96d3dee3679b5007a9efcc690f5ea689a69f0396473960eb400eb5eea6418311928db5ad5ce29f8bc852d6d173b0418f3162d5882c8b4f042497f11edcf3742d3745c9351a4143620e40a3d3c6ce4414f76a881f19c5ef5dbf0f2350a73ea5bbb85163518669734828c0ce85750d0d98d34e43a607e5db7f384be6f244d031ea91887d2a285b161270a63f0470ba117187436cec5901a28bd3d18821b4a0f24dfcaff6cf4eaac9c898c6493e27ba2ee6f3ad6581dea7a2dc10622c455d2554c87710e076107fb064e47ab1ec002a42004e150b7f04a0a9605d087b914e13fecc567a4e68108516536c238753be5583b6a659b8d24c8b524883345fa06e12d97f144acbf07b9fc7a970603cf9b5d589a8212040d1d0191137c324834335da8f0314f109482fca4478b74bacbf97781407c7b02fd9a8e27c3c68437238c162256c4b8dce6abff53dcf36585a4905716cbb03fb61839382a0cf0794e921a883561273c1a689ddb1102793978efd4b7e70d4e68ff43bace91094ab809bc376fe29a711fecfb9b38effa6ed45f6acdf61e820fcf67ca4a54d17db74d05b04251f32f628fbebae3c61c0e5b386dbcc4fe5e07ca1c837207ca5e29af9c5fdb2c97d4a0cc5470fb8b9667ed43c954ca8657ebd7d3d6ea9fc10e9268ece38d42b955f11820719bc2e451ed8623bf1839dea63c7242d50eb058290a62b95dfc01ab0c6f7a724b4db8ebb21da5134bd6367a8841e11739f97436cbfd32f634580e055202a9c8e2e63514097d31c7b12e84d1011f58f157f139e0474e8512512255e3eb779ddacf86e167976cea4c7fa4945beccd92facf7fc6f4db97479356e4a89ef23afb79a827dd4dd13f8e20fa42f87b7581865c6fe639d6edc5ca3be77688d7400125a77bb20623e4acd666a9f25d936c2cce33b00b4b170c570e2faf7939337363ba39925acf53efd23fd94744850532ad1f0fd5f5d2c3135527e2e8c5c2be441465445a30edf00eadda8dfa6d32192b69b04160391426be38648e4e2b703daf71614557a8ccc3eb8abe4a33ee693349e06587b701163fd71cce5c27cfcee3c32e1d905962440a28062eb7044da9fe9a28fa05d39ab1b82c9e7b38b98fc80816537250dbf0adcb4e1bcba4ae263a8d44e16077a7f3d55536cf1fd956dc81fc2e7e81026acc048a4f661a69b8756c3795de268895a00403bdd21ac4ffdc66964702f62bea92a336d637adf20979cdb5cb8bc2fc2d64f4522098fe4306905e8b80053bce4eb9d0c368a33812541cc0c13f89063b4c7299e48f903817f5df27e2f4fca22f919973189d9b5963714e00a328b5eb16e29cbef6fbf6f61da8e9127b723c8fc0053b8e700f8040809deba0c4e15f527de5eee398fc0e6f9ee5e423ecfc924b7d5f54f6bf793c68e79e86d4047fbf5bec3fd869ddc438b404f3596709fac138b64170c3781e34f1c3d649c417d66abe94efb9490e00a23fad9faecb6e29c4d46b022d05d57622a2d8a14374536167b095714c9c4df010e4b94af79d5b42894d006ef75d80209042b960eb66182814581f8e612342b2e196fdadfe07131fcadbe21910285c2ee58100f19811655e066ccae50e3b05db69675bfcd36d3163a8a086de6d726b3c85836d3d3e5a87a73b003459d1be5ca2c22b2f07d912b2c2793bf069707a1972f24ff49e5b4cd53ba34a7b3f99d80de59b2a35f05b0a2f5a94798f630cdb26caba57477cf0e9f224c726dae79a0cac329579f77a8e8f5974bf1c953c0bdb3cf821dc2fae0a430be1288d632d2214def5dba4a4720f096d3b847a74900efa4550832e8457de05b43094c8adaf324f464666f7f8e27f1ac9fe817d6017ffa24398c5b0d2fb83fb4409d0e1810f1bbdd7cd5a018a5065c3960320ea3ad98ac7fc6958e1a8687cfa0fc04c82b24728749d038d7b2a33cf124ab66e3674d446306849628d8c616ab927bacb0041a5129a3dda8799812741b3e30b47b6a8ef69219de0964abb86c9895437f325ba7c0d4cae23c673adb70ba4e3de2c2f06e74732b3522084371721a71a808f938f033c2801cffb05eb1f9d788414837b44c541f3c712e77a16af0004fcae18646bc047b887e0f9c0c5244e715273cab7316c9d2385ecc8b915d72546262142324c517464cbb3254ee762602a58225cf38d8daa96ad4b7b5439cf9a5c307404c00fbd4616320f31f5ee5242b1a8598932a689e5c92231a6f28d5088e112e77e3861bee69a5d4567c3f0500a47d207317131d3b121ba92c523b30533e8d9d8a4b684bc8b5b0a6c3d448216cd1cdcd93b13e85bf4c6cf4cebe04464764e71e7a7fbbdce2280f1d103d1d9691142fa42436ca7281326ea5182eb168e78c83918187e1807d4261cad0b75d6ad189836508349d592c7dfba2c5c5b8c2d465a4011576d563c9f587e150ddf2be635249502cc9bb0c714e14ac608769583ca1e368bbf6d30c01b88dd1f0444161e3bd16b1d63774ad8a2543a206adcce035cbe45c0de063780dbee931d9d59482e30d0b077fdf1b20c36b718ff669bde807cdc6a3d0d0634c45452ac4bf9b5e43390ca945e16e7c1ef837a97a8de0c531c5da538d80065b58feb4fa889b0efd0af013300d28e4d60aca7e0db857c95d156b79cf3c03880738232f61daa6f206d97aae23d25b429eab8b3fd6571dca8193f0a7c18851834d51b5f83ef1e8cba89304d2bc724f751dae3c22aafa4474a3eb17a7811e6e5435a789d943f12ecfe5f29011ab8587a6ef07879ee973f5e7cf0c41638b1b2bc3218ddbc6fe877e2bd5a5195940e0519a916593598669c09716027c8ede89e96f9a45253091c5e57879b4fdf5870129352f9cafdabac7c8b8bc11c704afe51a9ef902777c565c684cd8ef19a75cc2f1902b77d0a36bbf1f8bdd0005c839f4be527cd5dfa2b1b2ccd2fea728f5ec98699aa1c9c77196b87d9bff8497ddf4440839ecdfda391738e0e611c03652f038a5c959401601e4c221c4bd56201ec1fddcb2342d71077d5c43d84f8bdcfda7b69940d86415c7d17e4ca2af9aec6b0b3562aec09ec1482cdd1bdec74b7b8cfbec7d86ab95de731c96c799bfea842cad706f3458d185688777bc04510bb72ccf3ff703a1e3fe055e63c27542a2d9608ec344d5eed1d33e473e4c4e51d1402a90644aa5dafe0dbb10dfb4047f518a3144a01d67f2720cf037bd9d7f24d57638635f7b9aa7815af6d5dec89a6f54ad4eccb2884916263b87cad94ed6d7e934313b178e0a1b7b5af36b5268b4072be4de66b46c99c610b7b881023c73e115ad7a5a9f03edf4bf25eb9a49de58e824f6d11cfc5fcfdca567964e0364a5a328da65d4e8e6c1ceebeebb7df44a1cbed0c46a72d60e04f558626286c995210e1159b74f29e103b1c42225eee2a1e0b756f4d210c4b9c920bc07a0746e09b37320253e075754d8aa59b9bca0020c56c1631f6d9fec53256a63a14409d06ffe15bd567be012d657d6a9fca8d2f184474f512a38c9b81ebebbebdf27b3980d5ca66901865ae24ddcf7aca14358da06dce2f377f11a0ed9a550b546f479ed980b3967c3eb33cd02593dfd8deb814818dcab27b52a2273075cd8a06134259fbac0f75e8522e1dd16a63fd10c67170190016d6f2f012073b88abaffe3aaa2b22cb5963895a4e71c8d70ba00c50a753d8047759ced33965951785d6854f8c2dca5c7ec96e0e9b18e0cb0b41742cc6bd2db651a0218d25339d13d0457822a78fdc3b94aca5bac2ba296eac7c697f2c52688489b7352f69d0f4250fcf43c72dc55e5bdc5805183b13e4c9981fdb2ce0c92feffc00124f9e31a4e316fc3bce35331a961fc3d6892851e3d0ffc40de26cb809bef5e8d95ae48c005452a55d20e7ee94425eb246f203c98a6e4702ee413f19ce53d3f7b95d703d6dda7659ee8352d33b8e0190371cab5d026dbbd4093ddd28723371a3d6ea4ad4fb91c8b784060832a1e651c3517603d230c487b10a8726391fac79a232c8d72787477e3cc73ccef33fae5639bd99fb66800918ddf3e64804a0eaae11ece10c82381f37ca3c1bbbd0f00e8c494005a11691a4e9a288da618c5f2df303318800c2d65d3882c2e6acf642907ef9e7b44133968a097695fbf52effdba76db8e01bc90181a2fa5efdc2f737be6cc2550b90bc487d99572a75c7921622bb00f188c3524d4411d65f426971247d58444f141d7b76e81c4c666ad0c7fc762f047857844c0b232dd9df6969b44620d491452be1136e463659d0cae9c208cb0ae9ced4b1801c6cabc2c948b0362621120e1fea3edcbfdbd71f458fdb08cf614bec142108311b7d5709714f3cc3012dc2992ffd609cccbbb07464f31acf9400b66c549b02e8fa9315f8f0fdb42c9f2760b4837da24b7b3b6d64c50b21853b13c677704ae4d84ab985b28b776b16663c8ab8c75e6007eff6cc47a843f9744a95bb56f8bec636c15e64aa50924fa0ea3373ecea89c182dbd8c2596506c0d71e5b7d35beb68c910e298ca47ae3d57b17ebd1dda4971e9ed0005c77d370b21cc42bff06124e54475dc4b6b452a7b3c969f382d982d5e4db948eb302cb461a293ad7e3bcf78cd14fe93c5c95eb07d5cb60a949cd2e3375ffd01895febe127e8165b8b63f7ae5f1a5fda2313cff8645ca68f729fa434f74a80aabb327e05f4995c4acc10f1ba3873f64110ab713a4977a2dbea3579b9904115f2aba42e70362bbb7ac575fabf01ccdb6e40431ee1358bc7bfc31d5d226e7666ae423190a86cf464b39996ed626bdfb2af82a3b52ac5bd05e11c994dc7b8ef84103afe75ea24c181a7392e65f3feb6aaf315ee59a32ff5e6a91ab3ba11d061632f372efe9362148d9bfba7de702a2e73fe645dbd0c6bc161355c1a634ad888c0b2239620f81ff02310c0e76e6e449547ee8f0bcfb952b63d2e3cea8b83013206dc7cebacd9bf42a739104514765bc9e093d7fff538cd7b168f4cafbb88936c5d8fb3d063f3303ef01ca2378706af5c917e2dd59f80358c63e97b3eeface233ac5852348ef74e9834be11c398f367a80789117a7e3e0c7c5f57c4e14fb8964e5bee19d05e5423d96ee2f8152957d1fd706911e4919c80fab5cea3ca85b4f14f2a53d7c9f5612d9ed8027363d63660a4c15fe12e571af4e135be5fd8254ebc62924fb970aaf13505161df839730744b0cca4a770874054a728e0462f5a23c990db3e44877831e750a41fca4f85770c05dd4c23cfdeb04be181c78ddb1f0fa9c534eb74e0b122be0976d2ca8f14f10db163a1263eb9fb0ca0327f41bb723dc400be5d27c4329f4a28d95e47fff07aede851ff90946d69db907bf01a80b9e1cc153cf7919316b81657162608cdf2858f002486e201a73b0605b2731a0032fef1c2941d180749bb7b35112e9b38c2a7c013dc61918367647120b9d7d27799fdad56f78f0423df5d66be53a9b2aec1243e1e5422842ec17addaf50648f1fa1d04ab331fa5166249b8656cd104db715a0bb116bccb9d96225e8d8698c4eb70b37ffb5d796f3a7b1f246754cc0f93bf8e90b3ae37706157569487af20ab829a62634ee89de5e655dea78f303bb555fb700478e775e32e8df7792ad95222b1c23c3f3d0c30b9192dc5f1761f88e58b31858a0b6b5bfcef9577208a0c1a1b8ef89240b51b390e3dcd525148fc2023e508aba2d6ceef1906558d0d7d005b0d6c3fff262d1474b2f87b20543177dc145573ecaec7bfd5ecde4a6468a1c67598f77ef4e8ab16a1cc77ccebefe0546c5c8fee8c472342dec338acc67640f04995666cbba8f92ea4150d37a7b4eab1092f547482969d07857b2098e3df91e2c439aba056248c301760a765ec99ce8a3d1b2c02423cbd257f61331cbd087bb2e2e5636285d040a84b3ec28aa1683d15aa7c958d9d476e87c848d9980cc15095474a8ec0aca073314407a75675d4dce116b259d3776a4ea5a366d60238ad4712adba1361e7bb0c42f09f508548e295296d899d3c096d2f5f0a0bcd4213fa158e1f5bc3b8175b2b94398435803a00ba30024b9dc96d6ce420151bc7e54e8526e819a467d678717503e125197c1f267d907649277b990eafc8a7634a94e3c66d1935ed0df9c794a7e9ec31956266997ac259b36c361b4d44e915878dd8bc549f3e76fb18a9e521816ffda9423ee93bda87dc20cc2070e00a13ecf124026000ae15204f2c204b02d5a657c134913734cf91814ecce6410e60e853e4bf25560a351be439b090d5b0fe54ca5da6cf2d79a4d601b53e0f5361df3ae663805a9bb923e393bde61c122b23218da3c3b35c644305b5d1554499ac34fc55e21c909740f524ad7bfd8aa1562e76e250a90745e25983622a88c6081c4aac3041c823a7a40354b601ba246bb73044522d8e41f59cf0019653fd70452afb77e9795d3a8ef7b07c8b87caf6f4ce04cca95803ceb2b77b36df5de08283305c92c818abdfce8724d4759af11688bab8d596356068e4990830112ddbc71f351dde95268734b59e1781fe75237a6a0862dd5b0eae72309e2e573556ae80bb86f431ccbb798866072a9ce8e6bf860b7cbee809f614957d5a13d05d481cfb23d5dc5149ab7d7fac3c61ba288882dd487b4066b97c0a672103bd223811388309ecb49258e94278b7c404cbee3ca01bc41d021171f4d691c58d2070377067a131da06bc383f9085268efa26f9005771bf51bdb9018b238b4b95943abd29ca0f9f6b3feccdc5f035bb48566cc02c7d89dae5bf2c7a164a0e1f0dfd9a070fdc4219c9c2434a1a4be119afd705bd0acd474bde080d192b75d5b6349a1c9d5df005bbccd77b529d3928bc9ff8bb932b7c8248544a037471cc5873296921e4a5d20ceb430b2c662beb1d254db1274105ce20ca83498f24c6d1989c2f82bcb36e4c510c6b09abefdc36b0a9162e251aa0e1b5c4962ceb8c91bbfd5f0d42f3b42cf72ad89efed35fba5ff9901aba2b0f6f046a5f973d73c8482964ed4baf10a87eb5a1a086dc056fa455de531276aa3a55796f7599ac38187533e97e8e085ae495fd66ee332d7f7b5c48e45509d767ed4f015abb9c9ec3d76d0da9057222617ee0891720b01550e2e7f8c1d24c4785fcc95f4a3f1dd1132f6cb2441c2135b085d390d8b73148ce8c8ac94a058bed74f9c27355f615e63d6eecdae7b810f031be427d567b78535cbf00dfeb9f188b3b51a8a95a53111bcf2b5497816dd85639bbc395c44eaa118efb62fba3ec12bdf41fbc24b0f32ecde08e9bebf8bc1c30fec1802380768d76e91a2f4783862d652f8d7df3ef040fd69a525b9f5d06c184db09e35885c5fac6adae758da667e73d0293a2855e24c229c9b78448e5eb81ea84e0155c3ea065fa9c464309093b2233041cab9f3f1602eeac3d27744ba0c05fbe493b6b3d8590183e5cfe477b4497480e35b757c8263ec317f48bebfd273cabba69651f08ed47f38adf2d09faf20623753a60136d1b5e031be56594206ee818a87f78a13ef45e61b54fc958f0504e47cbdbebbc8b6db46f16209f08abf41be8d61f07f05f37280a23a3a8728253b8c3daac840354c8b7bd0fca22e05506882130dd83e6753d19a48356b42a728dd3291ceefc8a2220503cd8883a31ecb0a8684e490b15b2ab2513c451cc672a636968ade4a605136176227774a1f8b9f89279b666d8b1435891c28db28c2d48bd6147358184338cacbdfb2a1e2d675402f5e632abf501ce9dbb59e5d9006cdd31709065f5c253e155713ae23f03604a75f06f18fd41c0b57d357484cde8411bad657becbe737126d60d80c1cb41f76803ecd20657607f43017907c005f39f19dba1086b26a05119d2ecbc98d79de29ccf707b45b51fcbee27542c0faa72dad4b8ebd9453c21c509806ba2c20edab7f345b6fd045ae4e5fa5c945fee8e661857cbbf6d21568afc0bb071ca785605ff55db68fe061770f5f6a2a90da105c9f4d4c05ed6dcfa6066636f9d111eeecae7e6db2ed73be5906ca541d286fc7eb41bc47b1b997d9bec8abde86cbb1c9c2d33fae4569811f0df273af5ba54c2fce46b24a5f2293ed9de3a83961150c900b5171e40355a3b9289c10dfddcd7484fa2f9d821604b1aafbe658e0cd9cb7099a701cd3ba7469feb72834c102e73c2856a87105f540d2205fbb0755268fdaa1cd73abc9dcf10fab1761da22414a139fe00d822dc0c086f84f758ecc5a0d95deee54b2e7bb38d2de66cd45463c6942c487174cbb884bce2e371970e463684325296eae6fc584ba68e33d1a80a1bbbdb0b2d47d10719ca5650f9bef314146c8f70c0732309bb3af3ed3d579dd03f910055bd75d58901fd12e2e0b016cb1730b4fad1f192a99b0d4b518ae0b7cca1d3e5bef8c394a71ee1de5a11f579302296922d03b3c16a90435600130d59102f2505222c574a6ad9359ffbfbb6402a1c8baa30b87becb11c45da1a5533556a6a2cb0bd894debce724fa06a5b57a32e3d97ec6a7b3240511c0cb781397b274266dc1464cbb2619d5feea9d93161e12cb64e6983d9d0e5dcb3b467af42e65213d056bf9c979ba2f8569e75351658cba915e30f6967ae7537798bdddedd65e0c0dc5cbf7781b6babf0bec79f508c8cc4a70d86b975e77635d0670ef7b320bb361339a1a5f5ad33d2aefec89e6ed3c7864eb94cdc364cb5e09520008f9ead5809e797b0fd65298165849c9efd8e11564672e73b0e351642f6133669cf8dd090a639aab600e9e74b3169a6ac2128219f6e97c89f47af520616b971be997695b736949087d689b0d79b8cad709c7c62ee0cd9de85ad4fd9b14719ef769dd82d9282e34c27f42647b639e1c1276cf815f672d3ce5b00e1ce2762bf93ab2c036e18ad84ea553b0cc2574133566a847617a1fb528391385c541eba4f77abf4772206241762bfff40185c5a8ae9fd9915e15a35c5a5414168ceb852a28f605874ddd11a5c389527639a385b12594be26b61b0df7c29f95286832905209e35ece62b5abf6f5d7cc233c0e0118fe6ce5eeb5bffdbb118e0def1ad102d3cd3a1fa58ae65421f50e50342a5f9c4deb487f9d3f9be68dc346ad24af41394400e54f7a5ce656535e5632ce9e5cdc209a69bc505dd13dbcb238a91133be43ac206d4a3d65d42a1d87ca6e9ba6ac037d695cdc31fbc9de5fb166c8ca205d681e8c3c088b494d4227fb446288c001475770187f95532fafe3f051829d120fc278746fc0da993927047e232ea023ed657ac3f2fbd52756c79960011b188204607027b6c63b88d51ca3734b22868f82dd5c6cf60f6bcb8dd204e172a6f9fe1473b2e08a62ad8e20bd6e492a9598dcaae8d6a940fd6ca3ef39aea4422ddcdec10ce0a2015015deef1c6d68df89b2f3bf281c412ef9679f0f6aef7dacc2d1b146059885004a5aef64241db8322ddad09964411555728760d0af2f6e9aa326f2654922c2c5a12453b25092c0a5ff01118b68511e3085ecda43b924b104d3aee4e1534b0aee0e519b69bfa13b4fbd48f6fda38365c8337fd608604d7a77b41b1d24506bbc417f1f8fe34936d201eaf1c672ca0725c594a61fbae7d4e8c0f538cbc934c986abd2954b1070f8272700f1127c94293d899d6a18232b3726e170aa8e4e51ab529f5377c9907f34ef4845d89e16b0f6e84d618c0e1e045dbf6f8a2b2b10f185e60df9495f1976b3aa9ab8262964fc07638b1800dd6d0f277ec1ecf40453167afa9612cff2526090e7a47775016730c90ddbb3d7c0886b9b6c9da415e57ce5c583a48ee45b641fc6f717e43b20b03f02dd65a5f6a724001cda004b43335011ee3998ccf93525d454df192636ecfe9ece0380b06cfc0173806e671c1e05804e8dc18dfdc6f6f42e16bc87e7b47d632ab0fb1f445c5e009dac9a1d8a7dc99b2e0e8bdfec70a6946f806402879bfd39ddbfca385516d69579ffc736a68e36df2b5339ed8d4c68bb4b60c96455cdb686f2b8fa0b166ef8ca665eeb0411d07dbc4607b3c5bb0b603f27bf2c7c8fd7d700b54102c94c7ee568be7f7ab37c86adab26b625cabf2b001c866e5701ae7c042bb76de9ec1adf26479b0227bbf5d140b5879cf88198a3e28b336dba6b359c4dac2e1287a8b3de86a96dea0d0a1a44bea3af89e84c58c6e2dd79e175138f7bd1898894deec67a355018f910bb7c8761a34808cc0dacb7d8d4428e0f325326eabc640efd1637c8fab004e3a1e85588ced3415532b4896b943c25e449ec9af5972cc8a073195edb342ec89819bd1ac3e2788f6bd2e1ca2df66892e2c6f840e3d3fa47329dc2945cafc707339e926ddf0a22f689ecd08179c45300da0b7761e8dc3353b4c01c10f1c88338f7a39b57809de5de4e03e1fcd7861fa940aacb579bb7119d6911d2613815f9428a703b870ff8b91535683a42505d8c76c5736bf26ce32d90f20b4b2e7a3ff1be51fe6bfcff633cf515022b192202aaac8afcd206f1433253ac3771315ccb90d411d2d36a9c0053c82afbd0b20ef5a9615baecc082ab4310c8614f0d8f893d6d3afd2c60db3a9b0edeacd0124799b4298036aca5c20c12ee76ba9e8c5880f9106261ab662d33d0ec19edf11fa11c868892670d5fd6a5f2a18a209d0cc74a7542cd1d7e54ff1e490f3eed2fd4ebc62a39a48a040348bd5e45172b973dad000246a8f612bcf85388af203541f82c26d57b54fb1d6400e4c91b04ad51a6deb88123764c6f0e8e685249631940f33ac719564229bbd65e7ec872ef3bb2c2a299a9677cd7406a57ccb9297bd4a4e19430d94b447e30ac2d6dcf863ac3b438945f4973d4773dc1c5a8889e06be07fad8732ab8bf770ef9c5d6f0a8508026e3456da3aad56da262d0eade6d98edfe81e698db35e00dc777a2d896757b4f5fb5754dd3e34a0dbe5d66ef249a83bffd28ed4cb1788014e9ac3ca8bfdc0f86939a0c542c20581a8cf7302f9f0893ad7507f3e70677bfd747c2c2ec593ca4d0c281aad68ff5008646d447d0d87b6d58dac5194ef94793c51e2cdc12d607ee42d926022a45dc746a4b806ce02d851078bb2b7fd7a66a7437d8e990f609a81eacb324541ed194a9f9f699816e659a61a86bfc9f401811c5a5e0e8a4f9423c8e386ed765487756f797774cb4db66c344dbe9d1a19fc7e95bd19a99843311052b3d14d6c6b83a06eae3d01c19432024895a9b4248cf10db54f6c8f3e169a605418c4c47b4ad842b7f0666454c961a3727068b10a22f4c76f9d022cd076e5e3367a5728d1bbc66bbc27e1b66a8721620a19fe7a0e62463f2ed21d39bf849748df862187e16b20d9a36057ebfc0bcd7c84524476e41134b691eada6f6c03191b1ca97a14f5ab6013c8432354a7de8d076c989a0f196e561e97a99ceec3831ea8ad8b2093faf229b17a20475bf21cff67572f6f3a15d38062ec59ce3e4fd35a5655fce087a39163167c26c7a22c3d8d76af1fa2638c81b003d4259aa9b01f3303b38363a9ee58eb96db9e355fe533e2a9018b840dc257777b66568fe6001bfdf6cc1f12cdb735b2dd0c40c18ce4e821b95f9b508aa59694c3347e162ad0392eecb5094410b2ab81777eacd92bbb00a1b96c245da7bddd867fbeed1fa2889c371884fbf512ceaaa9ec0b6a14734f6e7bb6d87a0403a6167793c4127a590fc9bc942955ebfa46c2c69cf5471835e6f821b0c90e90f3086fc01f4dc5e305f173620de8800c6153fd43e75c9fd8246621342d4f48672202426f9e8c6b95b153ee9d4c7e43872e6fbf5262faf8224d0cad8a3d946a65bb59819c526cac0eadd7b7687d80a4c07f3e0a44594c8102e37f3f02dc7158897da04d0cea41e6428e4b0c7f7706ef9aa81b1ac948eb40e83be3678d3c40de8e03114b61e46ae31eb2ef587456e2981b720a7cae9a5e4fa815e8e9dbfb94962ef158af18204183a46aa37ccab9ddedcd0d6d65dc247d2b7406a9a0492e9bff57b7aeb09bed41273521ad28b0beb9fd16662b089a3c0d388f4df4762c39a839ce3d17105d9f12681a0a85454310bead6d2ab120fcd2c84a8a79dc16a1f6541b832810bc71cb42eeaff17803c1b07390261cfe64b32e0fd463a3fb84994a6485ccbd924d7da5c688658272883913dc8d7bc0e9e734602325c66fce0e7e6ecda3235e7fabb1dbf796779a1c63b548ad6697896015697e60155c93f24e4797f04270dea21c13e2eb3ae8d564497edcf439f8c362195d2f5799364743df024d09efd333b2b789a60796b20a1ad4f57fbfeb0dab0db86deac2cb5e03d981d8c595cc8dc30a1a8a6478c0b604f6f8dbb58be0f0d93504f72ab248fe5ea7a3bbae5082fd2997c3c11704ddc099844a5a651b0e060b99a929bd87b8fb037c8e9d4a31634f2d00742bbcb1c0a329d20a48644c8210f5721c01fbc1a9c5d89200a02acc4b3c129eb1bc297500558bf0100bc66dc843e2664586ee83ac1f114f17d4f84763db3f78b2324a13a78331ed973c7faa53088d95ec59f2a8c2d74040512cbb2eb2cd975d672e8c741144cc44105f202234591297998a1d1d38d2094d899e368ea86802a4531a726f0def7a0e6db7fe89d499ca2a8762245d31deb8c059e4df2884a28d1e3896b67884ec40a10b351957a6e1cd43546af51f3cd3316d3ca1c17d120ad6367066d4ce80b248c0c89193b33414659074834b4b4476454628e20c8041c9bb3c7093e00d3e5606c977153dc485757ca8c2b0730b6ebc2e4c135e64846043520bc6f78b9b4f6c1b37055ed63181485806217a979a1cd0e452ffb3449626d58bd68da3bb1eaa45ba9efbe074a1e71de4d860044853f15c15e02326ed40ca7ac64621b875ec7c30c02ee633fa2a0d796dd00d3206871e2abec6f1616a06ea49838eb816dd7acf4a311927ddf7335b849f31288032be4902708df9da42354f28e0ebef7e460dc3f5154b5b69b8c2a5fdc8d90e8e20593118d701b8c121bb0e7bec491c6d05b92af57d92e91507d546baff499962de52ee60ababba775bdfa0fa81927314901ee83be7c5b173ee1a53c038330a34a1e9c80f71bf27c3b637a1ec95a43524c919350bfaea82c8fdcb9d3dcbbbbcd49a434de6dedd78ec51401e8c01efb3683d4e3df29c5238048234dff205a0749f0841f02c54a07a5d4dd159527f334a02f3274e7a8d37b6122a31f5a5bd0c91678831f4558657ad5283ac5e6de1b395a641a9bae7a320711720345249574a420d7a16f0e0002d08389d7906df078a375f98d0612f41c968189be08210b84078e5f6d55485e841aefa48d58c4ca0f891449e66c2e1d6994d6631df856ad2d1d2382ecc0cc2049f52772b6d11bbe7b1c9518fa0f1812dfd8db0d55c783b285a0fb97016a58ced64c99c6d29cbcdf5a8af46d784a461130d11c83a4200e6d170fb39ff59c05216217104121a199feb3bb1b3d4e182a33a5b4acc2039e68521d25cc27bea0962c78b00bd56e6ae02d60243471dbf6397168402352f28e5af6d50430573db630e829e5e06b68798d8d0d00c35ce0ab93f82dc2237fd20a4cead15ecc920ffbef3cd167f268595a8c72f1f4533e00ec440b20711a53562ff93de758a9f7ddf20222da1c846b6ac4991fe7124931308712ca548511bac64d382efc0dc3796b2e844ebc394b071063c6c198f0f5dac53e92ccae9a9ede514f6f1e26dab8dcb21cc0c0c3204df5c305d93325352bb9b98af1cb648712c9e2f6d01bb7f6f0297723f97fd576213669283dc70ed27e96949bf8deb7593d0fc040ce718ae0b0a8c49e22178c28ae06d95f6641c9056958bcda4c23da0f8c38b15a5cc65dbf9aa1ca15a5c7e308dc78a8e7033356a561a029e8bbba73eb3afd3ae3ba076e08d18dfd6d7f0cf9c5ef19e26e5636d48ca2cda3e270acef74fdd73009d92be90da9024d17819396c223237eb459c683857ff023cc689483aaf0e342cf41a5bacd3d0742c0ab261317e9abda56a10267a569655666814fc4e50968506936092c17b0e5c514d0a8cf8a7c237abd4c607d9dc2a2bffa9152ac0c6d15f350b7fcf66c3793710f3fab10ee90a6a780d8d6c65c38528113fb8f18a22db93f224281df8ff9ae7aaded1d3bb35bd65288559b744aacf36bec9de086b8b9e3f593a7f1b10bb35cae92bc59b73040a0ff91d5b1cf7c6138d1024da726182b8ea8c41edc369dd1966d1a7727a6c43f4a8118e86ec5fae99170a6694b87bc9432ebc6a6186a3ea985ba3a88d80a655a8705efff175a6c9ead042aa09fa40830cfa68cd5cbc22ff4be95ce52d677770ae2416f36b95637f04be9fbd5cd4729e34c1813b2e35dcf41531d9feb4e8d951b1ff6965b745b9bfdc4f685caa532bf016822bbf24f162e6deadada15c68a881739754bf80a8ace5d162426508aeac4f521b5b84a081a4da086a05f7cc48b11161c3ca96ab9f28d66fdb3cf2889453063f148968734ce384685f3abd2dd1aa6b15538d337bf683f03a8236dfefb93b23539878bc57ebdbd721b107cbd5a59b70696b1d92a2dba6f76cec40a9d082e47a105b6c1d708bdb84b5dde4dc3efb6fd3adbce7997d3c95d2019581178e689510dd7cbd352bd3b681c2f3eac456d8b3f1dda2db1ddd5e2e4091e7fa4397c383325e722c663fd619e5c82b00ada84652affa9adfe522643034d8b51f75824adbc9804f9dd9b77bcd30494901ad1937af3db8727746123a699194d9de069fa94e65296a09f1e43619e38ba96f8c79d3244f7bcffa61d14de7a0fbfe485bb8af00d9cdc1e1cb23dfbed27467d87642b6437927b302ad44119b2aec2b7d2560b7c6c8138f35ed3b37c007dfcacc0eba45cf9349017a5f5a3318e039dba54c88120c12db3858d8335004c148d6c43fea9ad54e6d2ee86e9e46fab4626bc4c655637a5242a6c4e62018f63226259ce0b60cb8c0c988fab82a2e6bd5dc757bdc15296b652ec751704f86b1955f233c615343f071938c450e41b1b8fce4f3dac3b064cc287358468a5f1272bb8e4a3c358393c624ad04c4e957d4f20b121ea0dbe5e09a1a911b481360d8e6a3aa8fadd40e46acdefe2d0100a93bd7275cd0643d0ecef6a201b53bf9d5e3455ebcda4e2ed0a791329765ad743aa3dc6f04143d121069c8dfd766ef4a29edcadcf522f692848551b3b456f431bcd7d959bc38b08f20b3d132944615858118e22c80b29ee7e17fc4b013fa3d8b4bdcd3a9a6826b046fda374cd9d25f3ba7ba86217967bebec5b1f289913bc8ffb84bb204f1f1dc1872161ff53aca2d911f2f0222b782414e6406d144d311a03af324ee099c6608870a8823e558c809af7ff26f0bf6bdf84f7e40a98efd3b8448eca2f3e6fd2b0e2c4f0c0c53817705df83a2cbc0be509ec8dc0606512d468ddd3aac39ec411c54ff1572db8245fcdc141a5124ec3c2ab4dc70b656a7df7d68d5e3d76c07c898661549dec06bb392cf0a9ef8543c298e8535526c4dc006c93a2063cb2c6c497bdca79a08170452f0fcb474670d5666900f8506d540d7f97417fc0987b4bf1217605eb93f9232eb862f17cbfcb3c2893760d03cba5b3fd6887a41c9e36eebbcb0c0a4e3e8303243d9d79e5bc5b86b0b5389118860d8cba53137c720e066fa8bbf6cf2fc28b487b579ad8ff7c722ae71ba9a80183f48c2041ae35593ed8a90e0f2ec33d875a84556e84e50816e96effbc2e9597798eaf057df3776e3e7a2037271bbd6fc5278649d8219bcaee213376d07c4267c4f694262bf008462320982d3dc801ffec6d417fc6b38458ff1ca8ac177b0e377129d327bff245640f891c1f3a1025bbcd811470e3584f0f420b8d30ff19194ea0ffca71f53aba1b43585e6f3f9f334e14f1cb579f593b8a38b098201ba283e83c3d41ddd15202a5564d4536b0314ccf08dadef7f07c79146675e010e5d7c071793426b1f00d39ee9d9ababe0cdbfa94e1100b352ab3645c0bc2124ea7e28e208b866324858cc11187377621d66cf5366532e64bef20ba764508daa47f273ebc86f231fb3ce85f8047349b02a6a606396a8c2ed18e2214ae31e8b4455c8b2330cdc0478821501cb586c85437605cc9c414a1d4f07ac5508abdd717e9be7c8162b3b23de7885c54c3de6a315230c2cff9a7421315b07858703fd4e52bc3850cb9099e32d33f072c49585c8409487209f3c4a5860f044cb8ee20a17819a11bf5cc82fb8c8f3d8378c716fc65ab894a3b36f1ecbdd9976740bdd9986be2ad89b92246350a0661f6272d2d18f39e94aabde616c88f0f2a0f714a1e1737ab2cb3637d35ddc437cfd39f10e996568619cdf0c1aa1f1190f22bc04bb7e90795a32deab25ce570c64fd414b8996f25afc0c21b8aa55648b6ee50915d6c3f2ef784871e5ab90809eeff5b6a9197e276b70ff09bdf764c525b085d598c6f95ecc40d112b38e23de1e30cc2d8b88f39caecadaa2af859ab0aa2aa1a2e4622716c7a32dc858451aeecc09e837985d67ef55822463910f963e6738a6747e176669bfd343faba57038e7fd074394fe42035b6833a0746cb028a5c106220d74dc1950b9e3cbdde4c1106b059ea6803fda4b00ab1da85ed9b388e159d11c0e89bce281390ef097c96d78320e8e68f15a5ae1177f731fa095252eec6ef8690630f0af9bf45f63cc26321fd79b45c0c88f0da1d5e206c87951b503a46d91bd6fd009793647e344ee5054ae73ca302f6152d03ba69f5403891ba0e9d0acba90afed64c87bb8d84c9c9782cf7cd377ac1dd807f4e867d086ceabeadbc44014361eba67da6840510fd2cd88c28dc250337a40816072515053e6f90f0df9275b78e75286146c199c2908be8d57093502d8bb2bc5f88183a037dfcacd1dd5426cdeeb7ade2ee4630d91731078bf0d816e757d688bc4201fa2a541ccff77a84836a56e0048528140a79e09732854ee966f3659716e77a151dcf36a16b436b2ba8108f9b95a552ae9b16601aee63c48b63d2354df4b7be0b2e5d501e1ff08658b0d46ddc1992d3e932aa5f8b034a237e91a8a1b8cf57b7cc5354263dabcb2d99dbbaa6b892f4ceeab8ee2c06da373b2d6d56b450af98ac7fc574238f0c9eb517d6555f2ecb1a6cbe5e07e3f15ce88eeacc33f247d27cac7c8ee415024e36c935da6567925798e6107af48bed45e67d2efc0c78db5156606711abde288360dc336fe7be904696e8d534befa0cb00cd1d6bdd947732830f7343c5aefc2060bcc02f4aebbc21cfa542913b6230f2400e55fe00b1cf059745898eaf856fd20d0931e72180990c94220e1f2ea4c130a80e5c90aae38cf060aef1b4f95557c1f429eb8c083e42e3a89298e094a9b7913aeeb9f814f5f412c0733cec7b27c4121bec35471e1e8d76d70825e38ec8b43dca8cd09ae9ab263445f6575f706e3faedf3bdb5be87a7d1e99d3cdb9e25cf3b5e725592851739e620fc3d504a97b66245c96163d993a9b357b1a1b70da97d850586e6cbe45d372e2bc0a3f508a50c44fb848497ecfdfc39892f049140079dc6b77ec4ef17578b7fc3ff3f69ae3df6cbdf1241b4e8552bcd34c209b86e9f6b1a18bd36a980ab290ac9304a00f2da43e9884589308d21c1b41efc3b9f5164d3e41167691c24614d51de6baab1e49fc78edac4aa138413bed093b739bee16051f25021bd3e231ed1afcc9356913baba85015b492a3920bc75fe09d0711b5b4f31ba1e66e0751e73dea320018ec168e545baac57513ec722c237bb1abec47432dd97f37a74f7c563ef098ec99fd6890e005877fb9dbb13abb6a84a830c47f2376aae8d09aba7b0730b7246a909e213f423a49dd8d7501bef4dd7808cbdb19d0de9820f3aa1b86ab1c7d6f96a4e76540d4501e10d1ea631daa675d5007f2963c769705f90ed5fad2b215cf0b06b7a62de7dc37395ef0ab241feb39b91f1ac1c3968c89c05d711ac92ccc57479fac1a528e02131f5d550201c07b24a119b3a201c0aa203517f4367a1f22a84c387e09d99df6538454b7d8fec6d18595515cc5316eaee12d3b6e175ecf54d804a11789c1dd3051b1138c0b1b0aab443d6410f05c45d93ada95fdd3345f8da9fbdb1e1ec4340dd40f55ea88d1b1c7d12ccf28f28b92b8e92ecaf7550ad55f995a72af5930e1cc4290a227358946fb03ba5712656db82cbf68325d958fe787dd3f9583bde1f6af8f552e3c4a8d8e427e000dddd36f3f1d5800657ae7b4dda6808c771f2f56b3de954d3d51efd9f7c983581d9fc0a54bbfec5c200eccea840bfb961a7b7f69b76c13fea8a4460acd9c5ba2d16e013cf65bea750a6ba2184cc41a12bc2e3f0fa32ddac57e622b1fc467acc88b09b51a9df944195d0c873103de489ac84a043e5d2ab5bb668000f3e5a6ac56c6640ab0661c6a7e707487b316fab93d17e9e639cd87911bff06e820f75674db1613873878781169dac2dd761e682b31e25cf0f8c2488130112cc9b464155fd168a71b43e3549ff57a6da470f2874838b8f013c9160dd205ada3cad3f9b4373efed709e11dd2b80384a608b8634a2aaa53f86cab28926256b0dc559e835d785447411bfa29760867f9ad4e6756e046229ad3062c1ba0132f6046ac9c23da2825cf9a2f10d1dcbfd514874cd54c54d24b3ad149d4778d93dfc51c97c8ad148d64b9cee9a7e74da115e620805345c552c9cc2e8318070c8d969127d178fa3e0959ed5fde416d1e0bac0735e88aaa7258085167e728e039694370eb8ba17e93472253242ea80e7978b165a5d80673bc095b93b7fa1272344816e3856127d55dbd206846443ade1e823814aca06c0d79a684f1dc8220e98a0d54167954694d41e43a7c6333b97711e7f758e6b373ff9b4d20929599f3dcd6ebf58d04b43a17726d9af5e343b9af3d1e7a86a8aa739a20f6fec104846cf529cd7fa0a2162d8647d510f2f806624211df4519c639078d8f5f287c20ad0fe4f84344fdba40a287596bbe9ec4dbcd2e65c05a771bac75b759aa0bdcf82e8cd88ba2a5e929ffff5e2910c9845b63ed78a2ee13f6b91ea89876c5bd2138ba63c775619ca7531a05096ec9019d6c932615aea390e90d6ef7fb23b6078ad5452908a851ad134607300d65c228c14ee944d88b61a0955b7c481edbb38cc063975b6b22726342edfac9974a641e38e482df9925b5334b147c4821dfa425b0d779b5099599cb9a1db808b9aea92e1e55776a8e41e99e212a1e2200d0e15599642d0ba65448852838bb1120eef8365dafda85a64fb128fd1adbb46cadf1e9a693702441e94246a693627b6fd4861f0aa4fee14e7427f52bf679df175bcb530633f66def5c0a57b33f928d3454299f2ba86bc4f216e495a2f2856e3aafe24f4e16759f39766115109806d9094a718e9ba4e54897b110af3b62aacc50e98a11a6764316ccb4e5fa9d830deca7fc84db54677e1022f4dddb32fb40e753ec46a1240930c06d777fa03e159aa7faf2bf45ee1a15327a989e0f683d790f7404fc5c1ee30cf16004899a48701d48925edfc279a97002cb86b6ec90920bce0f091079b09f3e7774b3da904b70532e31cebc244c015f1e5c7fd95d0763b683ddb53914bdc4e15cb16f1f6302765a570492c3470d475e012b5b561fc3e0c4356a6969d33b3fb3a2e0129cd57580a557d5f2d4c5f4f87d62d75cef83d60b67082501df82182212b0019511f0542cbf1f7c30fc2118a837efb120ff3c86888273c3d005b0ea1df1120309b953b86da3b94bab41c404d4102361e4b3c92763b9b814983373d5d4bf1152052da1fe41a765f2aad6cf342e86b7a80714264ac81682b88148d3a1e7c18c6e7d20e227ab248bd30a5ccc3af1410acc83488a3c4b89e017f18a7de94d515765a7604b9d7b038b0e321b41740e74f25411d7a16fb806bb2aa1d90bab52c989023f5af5a3cd6bd4e5fbefd62f03443cdea16d3768ff7c63624b7f4c4d45e5d188c2d4623336538d7670c536acd607cec713e855c501de1d6e0785f52876af4b3f910408634df12e4b3f865f26675d7e392554db03efc7335c460aead5452e6a595ea6ae20dac3507fd0fcae0a145d5d9b3a7ffd466a298fa9e34733942764a7eaba2c9185c2892d0d9f061105b21fbe4e55bbabcf1ac5b7e3b777032fbbbf8cf09c3bb3b99c7c98a2905777a1d8e72c598f0f281997c9c206cc547794047f32718ff84e672b8c9851f61090eae7970390a7faa2af683db8c8eafe6e96cfb8a33a0002e2495dd7da87b91fef0f5725c2657edd0754750a2296799b89579c113947feba38d46372ec210a4efe4ab7b65d47198ad9adbd5c1acc1589d20a25ed5301be6c4d72412983888d60c10485584a25f7349450cd3e62a9e34fed3e8c3213f7bd756214d0582036bdd23f68c9ffaa637bbbc411e05c5620ac34962e7321b1c6c38b785c0c9e458e8505cb5e7f03b2fc2b0ded8f795d844447eaf00bae397da02c83fac833ee6ad79c1923dd2d02bcd56e8e16d793ece69ecf4c261eeb63eb1261fb480451d97753703bba35fd1d394515de88f50a5648c097eaba2a5491b4d80f48aa7db34020753b5ca7d5629f33aa0834b83d68ff4d8ade791b9c88887ecda3ef8255166145c8b86a96b363b1964035cee6d53eeed4086efe3ffb7ee26f2a84a319614af5df95a532dfc8b4a09a83b673b09523ccc6cdc8a30af30dd978de48cd3734d58debeef1ab5d286764b223840f6be8cc333850cd51f221b861a04e7d7fcf3553fb26d9d44b963494fa7e6546cb2fa366f3fd1af735e9d726e2b94a98e7ac8402ee204c7bb225fd5422621642e34b5b8c5c5b4e313d5945dbb802b55fbd7cfc820a4ae800dd6fb836145f2f105295bc7d54268d11c43b29625773ba64f633357a9f34fca8c0258f532cc3e4b252e1a0f0905173b23dc4f695f006f9aad9c8104f7b2480cd1724dc9edd7ef304bc3a17fdabb95c6dc004f401a38186378c134aff379239defe76a3e28def7fe0581ae5f03ee1b5d9035e51fe2e702e53df568fa65afe5c9a4fa3ab7a362550abf9c80f7a019bac313c7b6441c5d31287977c39567c0b998639fc6363c1655bb1ebaf57d1b865398b2fe334933e7f55acf4ae2726231a3b9d4291e374d9739722010fc6dbb13ec10e0fb287da1e73b6cb49f8c23b831eea9fbebbda8531cdb63eaf6c63138fa84ba02b81eb12758920bf16d064639889e310d8b1b2c7c4fbd886203ff37099464fae85ec4b2f5351e610a312f27785096ce96522d07ea22ff0bd0629cdae4039e69dcae4be3661a2a5ef35279343e5526efbae8bab05ca2cf9a10ce91b2aa9c47c20f73b2672e23d114a24f5803f85adb7f96072389ee175e2d4127ec95ed05eae72cd4066348382780aaf5a69706533dbd0decaa85e136a49693ec974d0feec67609354f9b31d520e3f691b89131143b85357b67d3069dca42b60a4b3c5475327d6d43fc57a81ee4426ba948faf064ca48b7d5c90a511e4f64aeaf8b6c120525f14bebd3a6bb6aaf6aa9b2489016d619de7843779af3f60195a2160f090a1d1c6c8c4947aae57da135f66e154cd1ece0939f0dc45ae87024edce10693f72d1902b62cc78eb4aa2f29d18a878f1f20ce8bfac9c3dc984b14189f1f06683b999018312e7979abf9d6a790e0add24042c79d0c478258252d7e147f6953245f8ccff4941059ca4f64ff94ce9877727450112c907df52f67aba93c90507744f7d674e2764a039ace7cf01472ec1cb4f662e21bb1fa9d66af57163ca263548a3c0ce6dd5a89bf5e2e8c3c1e36f39078a3f2b49643e56cb697b90aa1817f89bcb740284fd29ac65d03217965d3f3f9338c1ac173e0255cc5b3207bd65f864f826fa71a7ad4c4e5e38f2a6c5a2a9c7780c85845d147800403f9dbaa5e70c76acceffce2aa8317bdd01b4ef024edc661ec96b484692b5605df5e9d869a2e31e0ef3d4a0bcdacae97f990a21a3d119905f82dbc3ab01b5266b9f4dd665668153cb58f3ad5bc0605f0023f12514a29f04652a28a1015044aac004b9da608abbb7397160670b6626e25ffc2f5857327801887ac8cfc20ae7b4e55f1e834ad81a14517cfa8dbfc689cdc34d43f40529b271659b770c3f95a1e44977fb1267f9ec3bbef8f794a787807484f29b68b518cdb9b2842f8bad337dccd18fa8d89f74a2c1389d1bada41f32f2fa19cab09d2f865479649e3c80d2b1078a23f5f2fea7072be13b3133cf25ab6bb7df6f2a8c8e4d18265fa4835527114316ed11bdcf0bfadde7b5ca5af4dfd15bb103f433b710af76c968dd9e24de5d520958f2e19e95fad5b8f705b30909437ca113fb8fdd7f1bf4a3707e41472163cf662aa0f6eecc396ef33b6f27f73b6df3a63af7811cd28c86f94290130f14b135a857660ae55650f52573c7aaeb1871aea41dc63183dcf73f622d6e04ee75bc53200752df1791b7dd2f9c943bac70c2edac918b32be6a029866185641a4322662c9ce70e0cc017ffd3614dd8e795ce180bd7d26fea5005625c37558af10c80c7969d1f55388bb5d50fa304d5c8efed30c2c335017811b2ba5b8a0b9857845934631363384c404840d20c552ad56fc8bb6d037e1cdcfa32af8ba2420ccd0cfd47d2e0ada4b41b7a85cf0e08d0722848b19e5b13be7b8ae90cd3934f861c2550ee9f530ddd5fe44c90b4d52176ab980f6d42c9b9001c9df5708a58d26ab82f2a20a625c3d382c5869b0889a284ffbbd335c3166bff9b9fc2082efffd43684cb3d37d787a4f83e43afda4e778fa83499b9e9ba7f7122ddc9bfe6386cc372d32b39ecca27e69d6e740d05ac3bed5315eca03505100b59b53810a4d7a1ee50082b71006857d13edbc31fdffec94cceebda5da4231a634a912547f021e19283ea0f80664dbdee012f70cf43c70e8b36834772387153163e54b33940a24d75c810494c1a6627f092fffcb58d467bf60b0926402d208d65fc61896371eacea2428f098a74ed17de6daba12abdbdec5a0d7d6f12f47a51762dc67c7a01f8ce513ed41844726cb203624be0b24e169e5311775dc2a133d4f5117e00a669a7db2781eea17ac600f8aafd0b7eb7062cc82a530ea66dd40c15ec72b8582e40dcf144cb06d0d9d1843c12675727d6c40c2923093cd4afe499b8834e3d63f640be43f0932e7d7562ae4a57126ebd1e79bb6e5f914a7997b301a0c7b981054457718f8b55dd4bdb3091a6d0ca0c1167c8e26a42f0a93a2634d6173f3f81487856f39c3997af22c3cae6e8da8f1e3b05b33bf046cd7d8dfc6b0e8b3e23d2e2a3dd5722c8ab062b72e16cc21f269680628b33d917d8f88b9bdccf4d01e27ad01c9337c72bf2f131c56ad779f4d132cca9ee336a325402eace7bb33f0761e74f009eeb39c479e4aee1a4de910c8d724574dac448ae91dfd39b25103a42b1d969c1df57b806d16121186d4228011c29adf817385dcacd2f17e940f729ccc4ec51ff4ea56802a447d7ea178b949a3d912901e2251f403fc3cbf2fdc31a52d73e71711e32d4e2065a652d0ce4bd6a0797c31171774dc5520b11c50db557823e3c57583a81982556246d30c164f60d10f1080187861512f5a223bf0af89ed78d1173118d13be6a66d0252a72160cd046235b96928cd023304c26b890c29297e8b8b77766b420c2a086a38413d10f37ccb3af1545d69e3e1219b9c2edfed942157b6a5e5333619630c03722bfe9b7dd6f88ed1b8371063109c472900add4198e2f242dfc7d8c06f66c98e74c816a7c8caa24630e49be6b4972276390c19d2e8e60f37a322b9f2457349cc6b50b471ba175d03726dd5d35cc7dbae03922aa64ee8c280e8b6c0297a6b462a2e9467bf9b6e4326fab72767327dda62145f4ae8078eca29e90b6fcc7c0dcb49dc8018baaa6153f81c594ae322cc0c976b5fb68587b9bf6dc522a0de3d679af17c71cdc05e5aefcb15c7be674cdff88a39b85ffd3bbefb712e58dfa67df97b21e5e08d95e6bdd344c02541e61ee8612f591d35443dd76e1d41133fe27545094f451d4aff708d614a86c069603f7a5535fa75c765874f393b046ba9da19fb20109d33ca7e1781f5d3455f46a336dc5b7580365a092ad6d53d6c759b388e4e98857cfffa19728deb72e41130d2f0469e6867c9d1b501b72156a54f5d88f4b6914beac3eb98b78c0d285ca2ddb71ecb874fe030a8ba2825a30ceba4a07c00fc1a7bfbe1845afc321d2019fb8391cbca26605838fe15ac636d787bd3969680ad2d4acdc84f1598857e7e8fe0808df06719c973c08d7cb6ac5a07ef63757cc12957dcf6275dde76f9290bdf7de724b29654a297308a8071d080cb4f32176bb07fd27f5dde3f01fd577efe33ff6bbcfe13f34df3dcb7f6abe7b1dfe63e3bb17f21f9befbe7b1ffe73f3dd7fe03f38dffd8ea7e361e0e578cd4042ba8ae37515375e83f9d2d45e9aa9b3b4521721a4d6ed497bcabe50038ad59cebba948c726a776439aea647c7f1a9c6b79178a9b07daf8b87907b5d5cb5942e8ae58b7dc8ca92d4575287e6fc6604cce715afe6d78f9983d9320727f9d8b655316c7da15834d4afd5a75e7d0de706ab698f1939e8946e76dbf85a9597f29eb49238e8b48983fe1a1a0e7acb4167dab66ddb9e723b360ce86f5130308175243f4ae18852ca51bfe2a03b96cf931c64f224ab7dd84c37040b08e1a0ed7bfb987b28fd8985356433d7e5578fd621da416c89a5efd3ad0153100c8823848bd3a08be27775fcec85e2eac6245901568d12c4815d0ccbb958f67d118621dda2a22ed4e0865d842db1fefdac08d43ef0ca3f0595b5e53a2bb2fa0c1b76d849b7b7ae4adc93ba4e081123ef31c6e873638bdee915e9a6b09dfa4216d72d50fbc2efea57e406141bba511be92c4254d9fbef645906de8c753330cbbcb7f0cadf4fb8ac32ae87e0fd022403520142013e13f7f8df8062c36e19b55c65a4c29c6811815001b0c6227165645ef9419ec7412ff2414ddf82f88f2ca102fd8428d626fb3c91d2ef5637145ef9fb70fda7f5f7da4732588ed163548adf26457bff89dfef1ebfd036549592a469dac764655f9c797db1c7309a13be46d91168d21d326db060c3ac8983763cfadafc429ac4c1edfa21cd093d7c6e7f3a4adce3850d07d052ba0368eddcac892635e96c3892cea898acf5b566ddeefa1cb3ebfa1a1a558adb3ebfae2f2442e4cea74dbf3ff9f6c33cf956926ec84cfe3eaa1471fd3fe07a8436d104ee6e99ba1270250757aeaef424cd9552f675ae621806825bbdd9c7f53283472ebd66fcea93902b29f317d2bfe84569bdbeaf5e44bacb4b349f1b99af8b3946e6ebbaf8e9c7fcb3f247762c1a8dad7577a9158869445ef9759364c9cccccc524a6666174c961ce79c7ff15f524af911d9ee8c61ecb003b7630a1c1b1a0cfb16e706bb85db9539ba1d1982972525b783df3ad9f9792cd94123541669eeeeee7e46635ea2e1e4603b77b7cc8c53023bc55a1c2916c786dfee092c47f7fee6a4d7552b866d52d3b64d65850df9cbe58f6f6da2cac96ad63d7a9d8d124dc3b9c1f2779b757e57c339b657a59473527a551e76641ac77131c6d4d69eaa87bc1d17630d966bdcddddddeb2b58b0dce0764ce0dab0b169ef26bb826bd8d15c4e7a21552466fe4cdb986be0c8ccccfc9a0a2707dbcd31324b1a1b5c8cee693736f7b01471f96df8c68dbe91746dd8a86edcdcb8b9b9b1b151737343636f6e6e543729eee6e666c341eb76374194cc5fe51ef9724e4a2b92bd2e1c2a56ab5b95533a2c1e5df938546c58b30e072707cb2ca5657e1c1baca5a1a9a961a6942953a672caf8d586a135c1c011d3c71bd09ebf2a8032a5cc34b2c71b7f61bd73f2ac6c4646292bfd8eb13f4072f594724a39a45ffed594db71f5d5b2a714e2a29783b28f409f6a4f7d32f69451cef99a9779927b664fbde88c3668ec3a60542e3bafeba5831c10fad857ee07b7a3c6fac9abe99cd24a8482ce57ba3ecebf3e96b1ca0e524e2993b04d239d2dbd68be9caaa5900312e71a767acf6f881521582c2eb5b246e021c2f3849423f06c601a99395f363031146cc7c964bf1186d40c5cfec3f7aacf2f9ff28039b8942aa5fa86a452dce6b18346e4dda236a9acf325fd86ccbe3ceb607df64bc8bfea60fd74b4e71bb58fe5a0fc986cbf830eb6f6d6a656ecafebdb80bcd93704abd8c75c795c5ffc1047e8c3c3636b13d2a54c37e688f9cf5bce94a59da99da99da99da99d2964309dc57f420613044c07e686348ec45aaef4561de2af3ddc5b33d890a3985a5eabb55df70f82ce14e54fdce36fc6f65f5b98e1428c0d5e94a0056494f9b2a3a50a309ec8810b2b90301261490b9aac609112c3165ccc1550008c2523800862cc0b4e4460810517688861891318265305489e6001430d51bc88810561389310263344064370c1823172d0d2851050143dd1820e30c010051532446022420e52a4b182164a539e90d27466062d676208e2c9184c845c200c2c50f8b224863294788200455e6088e224821f4e305ba0e2023389103036d040860e516c90e18b133e704121890a32a4c0528590084871068a304a30a1a4d4c4145da09ac460468731c688d083154a2f8c49c3072f96c47811d2a404d30c288052c60c2234b9000728b4a04594304c9424e96203258617a678210a2e8cb8c0932a2320f3c409972d51ac60723181122c9e6cf141ca152f843c21a20633b0eca0430b5bcc84f922296ac95110600871355628daa2c40a2418c10e663075e0072a4e44f0250d2352b89c8a7052841757a250d1a2060f66c274d1050c462c89c069ec500415262f4a23f061092a0563cc5049c119ad242153a4a841c31a55a86022456be130489e88b1e28216a4e0ca981b2613518b14326840220b175ce14a3eba508a7286082a4ebe10c151d000283d34f9a18b12175264f9c0165dc020e20c2a3c8871012762aef082821413285902c90e4fb0e04419377831d385900a5ce9410a2c6630430b3494e02ec6286901a389268aae1cd183133067c67489ea22091c2f88c1e50b2b74f0c1480409346151c41831495994515a40972434e0e0c4c208c2d051c10e5b90f9a105189498485c1500d1030e24d8a10b1043fc7c817984085eb0c00b28284f484da611b7c1f8e1054ca4c052268831d88a1c9a44e182131944a490a3a888284b4cd9e10a1a5afa7969087e16610bdbcf465457369652caea60b79c73b68c914af959abf221658c314a30524a29a5f4b8a2d12e65bc5310458081e8fbfb44202ae6da5ac5d6ca1c04bed56ab58694fc103307443c5c1f08bef2276ac14aea11116159d78bcafa17278039701aa2b822a2d67d6af5993ea5954fa449a2d7cc483a2ff0daeb92e3415f7e1f3e785c9790d39ddb1e08763b5fb739a7bf3747a98dbdc2e94a4aed16eb175276425adf24880383bd3e0477aef44a9037cb6496654e387ce5ff8130d8eb63900954edbe7d3a5bc0b673b1bfe2d764db6a55767daf5977372fa0a80e6d7f8143db87534778ddeb2bd6f2a377c9ec93b27d605cd5a7b77e927e085e9f645cd715b118bf505e970a4c29bd6c6ac5aa0a55ea57ffaa9f74f0fa1806af3372e4766773cbb2cfbe506884ee0ec9de8783feed8183360e7677370f1f9807df9727a964dfb998f7eeee9f65d9d72c46ec33223f1d20d2f374e2178ed0dd21f1e50e199967326519f248e7e6b723d5aa673c7ef6d2ebfcc8bd3e3441b350d8be3cde92bb9b7943e24b0cc8622f81341e97f57b31567633d11801b67ef416d2be58b92efbaa02ab531fc9ad5fd8de6d56286a7f6f097dd567eeaaf4fda2eff4d301222f7d79915c21f3b3390c444448ec2437fa2a5c71e8ddf84f4cc34234f2bb4c102fadf698cc6ddb1361dd115877486f180ce00c4b84752b11d6e478f457376f04d60d7ddc21dcb36edc42ee0b59b7e76e1fe6b8db33076d37ee96c1dd0470377be3c37ab7e73efe0d8f4600fd353754cf29fbf517723fb5a71edf23ad564bcaa57fbd565f3efd86cccffa8441630458afce8ad409ebc3ea6e68ed94d4be46e98c32c64f07c83b4885edb74284dce4858c3662faa52a57b38ad51ad45fbf90888fa675b17db7cd5a2ee5ddd1ded88c6c0dd3b4c718c86a5f39a8bea66459d68610574cf6e2417bd0faacaefb8430d9eb63df1cec4fb6d135850ab43de917c6266cdff6892b8f9ebd3cacae6e327e381cf430ec9602ad3ad723b9afd2f981e46ab57e36427dec5d8787dfebfbf9f6e05beb571ff336eee91ebab8399797925a37ecc1770891eb47b8be4a6e47fdcb73f0ab46e43dba7dfb325143d1f574c8f5e9fcb87ebad673dbb867be90532f4472f51a97fd270cc10997fe45ffa294839c7a9756eea19f0e6de6e6c723ded0e67648b0a626b8c4a82e58c181ca0a545c705541a5051eae1758d9c28a642a4e5c60549962a53eb1529754b9d4a812259ddeac027595b1529950b9c86021292fec5e544a29a5945f1c41c7cccc19d8986b652f44a2bd96554672b3cd138abd6d245c170b4370b98df3b6ed2be7857db9af4db61fd5e3bb6d5aad9ba6dded2bffb623abff316998e67969aab5d667ce3466e6ecb3cad5ab2f6badb5d697f5aab6db94b46e7777ef19ba75f9b5f6f654fc24f7f8e3f8b1bafe5ae47af8178257464ec9f2ccf654e411717015d7a54ce9fc90518747bdef49230d0292253347ff9268425a7610ee756c6f12ae2b6bd9584f7fb9c928dd8188f131acbb6122b5ea9b537c6271f006fe994430e8ef9886e52711da9c725dd3beecab95fe4cea2392406226f511491c2165c8e674c2867fc58f649650d25cd46cb3b3971a0637376cd4d4a46cdee6fd35d5096c0d92e3715c751eb5f6e7d4346edb5e4b713b549c6ab39e4d66f31b7dcde6351b1bda3f678d56336be80da5b4fb9bf7c7f99ce73e3555efef813f1108346f5fc3b81d76633039dec715fdcea3f1881ca4aff2346deb8be3a93ca1019461b72d878374ce6dde78373c4b439a4541458a1abf7240b228a844516d78860593d1fdfa56a56a3e7c10c7f5c9c10a757059f6853534de95d12cb3fd5986510cf3a82a35a59c483697616c2a158f8c71d670eb56791bffa8bedf7bdbba1dcb30d73828d3529ccdfada14a765aac782b0f7cf38287bffca419cf69e52a5b8acb927feec0340f1c282197fd2289aefe764a7d06b0cddbc923a84f3fdf5c677fd3e39583a843cf09fd4f7fbf8c07f6abe3befe36a7bcde3fec69b43d6c361e35569bfe6b32f949f7acd0bb7abbdc6713b6a7aeb1acd7a342f14316fc35234fdccd1744693a5362fdcf7ab541ff70192abf2fa32c77d53becad33ee56d8fe315d5a19b6f8df3bab89ab3356dce2cd3a60dcf7a20d39fb3e3fdb16d01840cd77f5edcb46d010494eb5f6f0828121aaf57b0e9b5a678aeece4c6a5bc1d5ff56b9e7c4d93de86bd94b3e3713df5bfc02b2711f0e678d90288ebd1e3178d73fcfcf4421d777270dbbbdd61bddb6d6b63df86479acfd3acd7f7ad7dfbf60be7d79bf264d53c9b70fefc2bfeccf1399ba628cb0d97888e1bbf39203445596cb8510641e4f344c4a7889d3224847fc194f982230b1ca96062083f46949169e4c617146d377e1de2500e3e951fb60e94c95ce979c01375e0526feb80ddfebc887b6ad6f022a8334d3e3e70d09558210e3a0b2be2e393e3080a8382baa08e32ece85252e21e7f2177767676067d72f4512bb954eaa3189bae7b2b09718f12cb4a2094068319299831230533b1752b57fe22cb9c780572d60ff98b26199a9a58d65cd154d324435384e28c7cac4dd8e223621813f588491c377a1e1cd5e799a18d4f1f73fa8840456c173253cb8e52fffb08aaa260771ceca334906e6d72960e076bcaa829a38a613b7a23b0534d19580add4b6c37cd9a866d9a651fb73d86611a86611ef79ac77723a12ff69cd751b02ccbbeb3cf8cb22cc8fc2ccbb22dc9c1ecb7afa7a430e5c38e1886613d857bb2d7b26672307bed6b2507b3c7a2ac66c562d9d7ec8940f09ff919f6d8d7a68b7d7486f55edff65d4c9afe7daa076a703bc388eafff31045a023d9c7d8b71de98a84c43dfe3ca9a4f47b4a89fc2b371d89405d03affca35a89a3906cb4ec0e13925f6794daa9d6f06f97a3f321cded9e03fd3f37ef3f49fce7e66b232dc6ee352925b7e3e6b5b9fdec72ba1c9cd764f7d74db81dfdda15bd2f9e392fe45eb77ae47c9dd4f1e59b39d9e9c27f70fee6bbc63038080c0ed279ff86ea2f38a893c2e27cbf2aaa7533c686cd74d35e5fd6a2d7e035a452de752fe4381860d04d7ebbcefd26de30008a735e723b30789cbe395dcef31523eae6bb9c9bc7b9b9e9ee9bee6e70bef35a0c9caf3f749c9b8f51fcc775703e1e7e71fc2902b1121294ff88e140497a881daebfa3e13f2dd9048cff6cd73f3ef11f5e1ae22822e1dcfcf84ffb4fce3b06efdf68f80fceb4e27a4e51130ddb18ca9f9e38282a02adde9f26898318701d0baffc37ee99fb95e745dab5c90c04c093c10078de35f000f002f000f01a789d85c76ba5be9ab67954161b36533379f11ff69f00bc7f7ff19feffd1b8cff74efdf55b493fffcfb7716fee3bd7f3f6901f0fa3f0fe73d0fe7dfebefbcf6f876ad81c73703007c5d04b723832e56b09ec1e7581a76e77b23ddcd7912e21a6bdc12886a5c790bb69b786df4c4eb24dce3afe3b50aadc4eb253c533917a76f83371ffbe6ffc6036f6e769cb0a1277992c7ce933ce9faf874926a4a4dd1cddf78b6c171fb6d3cfb0e7b766ffac36e7821eb666fe3d9f06a3c1a2fb437fb4a905ef829afe17ad8584ae8eb9383a5c308bd19e811c0ebe24a2b63b3ef6b7f32c3097faa311280739084c63c55bc815c97bec7bcf2ef1ddce1215b6fd841dd7862c3ee4e1638df27cc9d38eefc30879c62e08cb1f22507cd8fdc4bffc19e551fe2dc1de7011d87ffc4d77e7a5254af6ddd1a734ffcce3e12c5ba7df4b86f2fdb9edb7c153fe50591f76a5e82bc52ba08a4fa4050c3ce387f7b0cd3b8674ee3b418bf6e3adb46148136240bcceb6f4296eb49ae3781b9e176936c4958bcb8213f61f1724316639b63aedf2b03dec01ff334e01edf21b97edd0ef7340e0e36ceef1ccc2efd1ed0ad60436eaac00c8d44ff074c300281e003aa57d243aa1f62d5a753ad13f1ca1f04392292228d8da7f16a6a6cae8bb9e66d2e1b554d4a7b0a620dd8b81b2995a5a9b1c140fdfc5890f642ed3552e8817b45397a62d4a42889094788b80702bcf21f0de09f0f1804813ccc79ca141e983e200ad2367d75216307c5ce22da758b83aef7cfbb75c6861cd526d096340e78a1f3d2c260c89f85bcd574fdfa520abe5c7fa76ed95cbba85ba2b9fbb7cb2a801bda2c05c17497b0c0706318377e77618400a353e3161464b909b8bc8402a7abf92685be44a14a3fb541818dddc37f7e7b936d93d1d87f3640ef90e6016836eee15f744a5f5a12c34a1b5c5eea62c3adb9bc74e504d4a30a94bcde11ba3bc45fbe52c5923405a98de2914e4ea2242a701a49945429334608255c86e8a78200a87cc9a2852b529454b855dc10147343560a73430643c50d190acc0dbb688a1b369214376c2cd79f5b4e62f0900612380c85afc3d297251a9ab2703214b264b0c2873154184141418b21f9b576204195a42d2e0cc5cf034ea88183115dcc40a3684801604c496245104dcc70c35012269ad08204528c2104d250d849d30b239e58728228c2cc30c6902fe145921609bcac608b208680c840c51951bc600d16ac0c99896a4991c18b0a58b4184a51a10416493750f1c568c8a1700189a3a4322570610443fe4722d00f93175b62203382255e0cf9911888788a6a21054c1c3184a4490b350031a6cc0c2e9031642f284a28a18412140b19847e3a3fe6d3f9792024bf2d0827bb8991232674ab93e8d0e142471c4ce2a09c42464c70b0e5a0cde5bc90bf5ca85aadedba7f10ec58accd16f9dee426a94bffd17708fd2416b68b40d9b0f3c9b1a3cde7e1f1de9318b8e3a0835d13350ba3ed332fe4b9d957edc2b2be526a9162d8b3bc548277ab365e8d7cc3be8b27acc0bd58835bbf29c497201570d089bafbfa79bd875d67737b5a2171d5a18e0e1f440e7a10c9ed08bbfeebf29752be6b117974ee06e41dd23a584650caf93ca794f32f29993f9e3372c741c769d22dcd8c548991ddfd848fdc91559e9b796177436b2dad17eb593b58bec8826707cb175888612db161348a0ac1c9a99fdc909db65869c4a4710eeb533ff37a73ae79d870db4b4d8b1a5ff6566fc8943631c6a635ac529d615c09e52006e4d72f2c7285dc3021082db9d7e696d519c6f63ea1b4f7cc431b438b74b52fb45026d3cdbc30c7f57046d9a4feebc8c6f51381547f5d9432ebfc88f1915c6e304748d79f16a554b0f19de3e19ad1934d791cc55a280cfacbcf1e2d61e3879a110f4fb74ae909db4a9b63642c7db982ba5df3c3661299cc9d5e7f4f05906101ae1bfb89c9ad377efd94c46e0eba00f35ba526e5176250c8b8f9b7d14dbb9e3ef324900cc80764fc6c77a9b599737a3c1fd582f698578fdc887dbd2e304629b969a4b26dae26ebcb5a2b83d8f58d49063120588318563ff94384aa5555c6869dada95bd368aa544d7c8a5938e8aa2f06497d57dd8ed028aebb9c17b3b80ee6fa77eca8b0bb5eabb5dd6ff6cbb232411c7429369eb91d64d8b0a3e22a6aa7a3824420a1f814b378ccc2816c7c0e262af9dc302a81f166051bfad3752f8a1ebf70ebe2865d9405f7708950d913274e9a2af6f16d68e44a2a6b14b6378a1383bdfe62f5eb566b535c7d0c9d11a785ede7b4b0a1bd5e6b6d4263615f087e6d777a35d5840dadb5d747a946697c8ef10366726a8288175e50c94ddb68d6040f1b073dffb99e62b452f9fef66a3632cbb2bf1ecb304c3a78bd1cd362a7b24a4c8620657f09a1d6f9d489bb6595a255ead0c7c7dddd7dfc8ad76593e0953f469508d036c633d62a7190bc1ec6863ed7630f065cd593fcc28af4d13cd9fe352a17ca2f6cf7c24df6173694df7e4dfa4ddf5f5eeae900f1e7d27d9c6ad98ff59a5da1c572fd65e7ef533f369112e3ad699a163d2f46b0e9876baba8cfbad9b74fd45837874b4771ebd74ff7fa429f29e3e72fad0d32df6df6049a7ccdc9293e45a818255bb228b6e294d88a403af04fa482413011687e3129ae64f8ad5871e0b8616cd55a2f6b69d7cd242cc8417efdf23b98eec0bf98d41bc897ef6138287ef107ee91110cf6848def1fc6a4a8286f79911bf9912331313581717a82eaa3d0a72045a029495894f849489123dd62164bc807519010bc216c1893aefc6a3b1eea2b910cc9f7260e6a2c11480ec9f729d2a8fd4252bacd1d08e90ee2d74c7883a0fe9a852c869832f3843bf70b59d7b7af99b074496bce0bae38e8feee515d4486cd9458be6127294d24ad0423b5db5e070259b67d20d8b6d7b80781f6d9a71379a862c082fd397fd66bf69c1d51f6dca3af906f9ad79a938f08e443933ae684cd3e9fb8e217f2966c81465fe30e3650efc584022614345d2d6ecfef1fab37a04f3fe3a0fe82b887be4f04c23ec56d5a56594a2aaf485ca53e565cd1e76addacd5ba2efbafa090b8bae2e3aadfb126aecf9600023c549f39228cc52bfaacb0a358ed20f321ed08ebdfad285a9753698700b8b9fedc1dc40fe58ffc7874818241befdf1e812056fc0b73ba70420d88d1ab8f19deb913509bd866f140b262a08a36f0f8debd17da9fcda2f45639e8a75392d5bc6ca403d8032fece38391e7fbb63142d2a555ae061de185d960759ba6871439e1492173137f4f1f2e5863a588999a6f42e51aebf8dff506fc1f5ffe17d3bbef24e76ec1457d0346756a9986cbf3773d85a18dbefad4a31732ca3318fb77367ab7cfa7f7d31727b4a7e2f394872aae95d7f2a24b7a7ae7f8ca9e79efb2fc7d0c2ae52581e24576befc6d5fa6eded564bc71359fd73def6a33a6a4ce8f2f0cfbe5bb94dd5da5b0d24196b2ef191a4f19db1f73ca30337bd731aec0364717ae58259d32ca4969bca262d9755d15c3ea7545a65dd587cb07e9b4b5135b7c818a47ce4bcabf45729cda63ad15cb32ac3a4559312ca926c9eba38c91ca0a058d3fb2496bb7b18ca54826b8aa7bd795b1b367f7c4d96196b15d5297d425b57bbbb7ca0acb6f6d36ebd815d6bf70cb324ddb2cd329a3a7fae29dbff145d7abcdf974e3699a5f07df1bf87334e2a009df1d90c01bf837c900f7f887dff59745c85c2e7c9743538433c03e1a86cb673cd35f39646c92e5ce4a86eaaf08f734a931d6a5a6179cdcb056d1a5682ed97979a94b0a4c60318a059e6b1d8c12c5050af3c9c1d2a1d4c284ea725de1928592b1372e2f71e9e11ac2cacb4b5c7600a28a15d68b94f284145c79e5fb00a13d2e2b78538d945c806106aff8f2e08202aff8fe8d3fc1f08a9d6a073cb63cf1067c43eb48ad56cb490ddd3a28b6b71421b764915bbcc82d5f18e42b772e493d9512a8bdcb8f87f6d893bf939661529545c5a4524a62c3578a1ebd07db4f79fcc38f52d28f2ca5f4e25f1e0921b8527a15eec58a4e527602edf6d5a063a42dd4dddde449108f2d51b847ba51044a1269e015e750650bced5e06a70e5963db12178b5f7fa5d3f1dff74e4675fb54f477e3ca6bf90aaa8041c44714c1a0738718a1f58b51a03d1e51b607ca694524a1a6764c99591514ab949194a2965947c19a141a7d30e06e0091f4018087bff582b6075ac0b0d6bc3b2965e55d259291635cf475cd56c4aea831b134448dff8cd3b80b8c79fb3c286cc04828f0f8880a40fff618d6e12db38883e77f933cf070ff953cfc70feef1979e03b8c73f7a40b8c7311f4c9b0f1f9763508ed41b8d448e2a276ccbe8374ab69f856eb218a11c639448dd4d86cac6a5545e0fa934ed3bfbce346eca2ba9c949aff4c08ded576edbc6a554d6aa52dc6684142a525e63b4a4ae83b2369dad62a3010ab002d2388c93b25b6eb27b48f39a5713bbf32307aefc815d39342753d7755de72bcf3efb70843ba10d081a39a2a9eb6a94ae4ad2b58693cde1c36c80025cfebf9e8328f6d9eb532df153f2530e10c7f3743b36bef2ebfd2b12e2e0d9c958214bab35b316ebbafa8f846dc992758655afa68964fac5c475824d5d5e6252838ec144c609b65e5e623a736da0e20bd3122b3025f1609ac2d42aaa94ca0c4e9a88706106afb66ef605a721c0a0c1c99421b27002a525ca27074bc792162db02c3cb11b6c7679498b982df6e6f2921626a692c5b9bca4c50ae562e9e5252d53b4202dfd40c495979796a610d3d65a5b6dad4f8a88e10b33433c3101a3e949540e694ce1028a102d349dcc7022a552527bf3e2d6a52d377e67a3ba98381b3071c48d558b20280ec04492eb7f030b1fdec696f9b238f1b7c1ed70170460092f0158e2ccf5afb1a093994a44a951259934df0db7258a96a072e35b6e4724a384962454d60a0624a1c4921245d73f656da8584ae206ce0b253136b8fedb164a442440b35c28156566a2c0c62061c301ae6761fa86db11475c0f83c4d175b74e4b47a4d04176851143802c446689309c4c34e4cbee12eef414a174978c60a108186eacdd96d466134044981bbf6b192298b891d38336bf877ac8c9917b5dc44eb0614fc1110ab9f1d993fc811c0e366c2cb7bf8b70a0cd5758e447a4bbb1aff8d7475f3fe924a4468aabfe1e63e36b5d829432bf9e22fb0c1bf694db2108820fcc57d243f3fbd3f1e721a32e8a4060fb17da6e3eb534aeda3ffa5387c7bcfe94d53a6e6799dc4d958cc63c618d6358b7bdc17efbeeee2c6b0cebce328c8410dccc62d95b30029980e3415080074d30c08360f504f8b0bb7f1987e706f00ae0adbc0d87c74306f078a8001e0fad3c1e42c166ad4d334b5260a06f16aebf9604d4512bcaf5e7d0c5b81e5b4320dd68148f22d2f5dfb279c57525d8bbe7bcd61b3726aac803a556a8c44e9644ad64686c002000000963140020180c0a870402b1583420139555f914800b7e8e427a5a9e8ad32cc88118420819630801048000c8808ccc8c4420005fa000c0054467767bab6fad083a9cdb8f06a1b55e88c7f4edd1f2d5548cce2175c1ac76c0b559f3c57595966048915ca05ef501bc1388c894c42e1d07561062b44cc78c0266437c6918305996435af7b6ce968bea311e1d40700590b836da5a5c271ac4c6989ffa72d7c67a8e6ef40e09de57c589048126f0c66193fd880ebf8c3c604f1b20d489776532fe7f8a706bbeb0f04bbe6497d7be4c4b6c6dd4834c095ac102ec4c5b262526242b1f2ea6528f554f8b37aff272061a7457099544f3498c655b6cb34350b0eca2383518cf53cdda72aa1291bd9e31eb699643959e7d6fe4af9596b81ac592fa97e9dc7bb40b5230117045999307b1f39f95ad0096f6425f0e9974ac12af7d7eb8a69190c0a511e1330a80a8088efcdfee6f42dc7b7d8799d8a0ed0728a2122cf15a4427979dd6127e2ede1a27e424af8e3c8dcf70d4ccedf024c2682d69f60703da5b5a18a1a881cb9e47954046484633d81c9af2b5e85d5f971433ec8d947ccda7c69f207a9952e0115de7d49c7ddcd1d8cc550d5aa666c227da391d15f6bb5690191341a8c96649fa7ac4c32099f126f0df73231086352e105c626ff5603808dba7ef5f12517c65a911b9ef89d998a0530e0c22f2dce3a9c4ac34ea2884699f05390916c482524b849b494c55f8baff8046e2f6f4440d54c91012fc934eccf10ab38806896d79b4241166cc0332a48dd7a048505d3f25ddaff1ed75387074b921115be7d9a479d91ecff6a07255b4265cf8c2b5f1043e12d0685bf69a7d323cb7ebd48e70f97ac553a385e31cee323fc92ae708da2066eb275dee9cf2bed44442252ac593b3fe4faf8d5c74a1e367d78d2ddaab58a5b367519121efc6fc8e340b33f20c304b650e36ff356cd7f25eb381fa747c9dd9cc15d8508f631c0c9a5d5fd80e92bded3111f610297a4f2c21d59d3fb51988e3a9ac858126167567cf269be05144ce819ebb89c53129ab8ea59c804d091fb49af336a3e3d1323b67801dab818a901be5df1d88c6aedccf90854079ef153d91260796e94af12fec676d7f285532ff2011a076b1f66ebcf00ca1243d12d10d93055717f46f9d8f774b245a499da13341aad6b3480d62f1751837fc880c7a6fa0cb058e122ae2e0771f513236f9c98a2e9ed1925edabaab97099af028281c0a358c3d7e3c4fb055a0e45d94ecab781e6777865425fd57212b19e4f55ffa3d172317eb3054d3a48ee1fe4a7c3054f371dcb3e0e1cd7efd076ff8333bfb77da5b3161a8a9b2d368a31cdb18881c5a1d4040301718752334b2e24f598963ab8efe37271b7b90edcb43b63a06718b2ef823ab2c710eb7cc23fc5f5a9f3a80ea2f1d2855b60fb2f4970d39553001f21a70653a264475c16844d9f8642df9c6a3376b765127398cafcbf4cf2c11c7263324f19b682803f3043a433251318c40b228d43d18b9f82694ca29e5a599b9099f91b1ac41bb767e45cc9f12deea30eaf395f2ce7e2ca18a95010e2910453f1a1f47fb3692d596be5ea0e1feff5be3672121f8ef324e5237293463ca44477d7637aa9e1a9958660b1d405e8217cc66bd1b5e7b3653da3c6fb902eab86dc6ff88cd15d2d5108d556ab4e69381cafc37dd123b42ab8be5a926bb5857d181fa5fd310b53162ef741d7d919b31bb2945e04452cdcadd6cec46cf77c9cab60d58f5a317a65cb50ac45cfe8d6cac5eddaa4d70d71f0175d43ad0bcbf4115a56d9ea8cf60881bde24a3abc8129941219fbd7678d42a77050e5f4e9c1ef68cc31a6c029f701a0744386d0d292cd905429af41dcf38d6b4fdec93a336cd0e759afaa1aa7432284499d3af35d5e62b0186b2e451280a3ba340899a9edb1b8f1c5c479fc80dbe7cec237b8eeeb754b876b10a333255bfba9be404c7cf420b5c6668599dac448d8ab64ce83e2c4d1c0beb94f9857b5ac7c41b378d9b9bf91f5c8ffbf0063deab98d802f17cec889c083cda7889ec7d57082d9a1cd3ca81a0d9ee7b8bac9ea07d2927b90d137784944bce3c58233eec0597620c1cb9aab89c14582450bfda2ffe8b85b69e9e0a34e486059938b2319928a67f8b794bf12652eb0d86351bb53c85ea10dfa26d8d6ee9eb088c9ee1803268a99d24e10af3a3c11fb86a5560b57052a6cd416b7eab1a5a2f98bc1af026361d747329494a8df0f3907d8c8b262ebc8fd7c9355573749b3825d8bd34903600a525c5b0afc6e6fe27b39b311f7bdf458331364434cd88e67ad1c5a4aac963018257dbfa2bdaa87d435b2411ef5fd3fb57dcf7c73c4b74b3e89ef56cb6ab1b7ca05b85a31e9bd719eff1014845c895a9273b35a4370bbdfca12685c11be3aa288078957ad6d415ec1ad3dc842b3c321e84bf600d8798c307d46cfe2e95f73b5d878a8c9db4bd262b8c2252b04961c954c8ed66dfea230fae5fd87ee21ea3f55b84a28ca88959e63722d2f6f094c741d29097e52134ca76f229ca912bee63f0f766dfaf7076216842b93d70d5f28de4123e18c1051c04d7fdced50443ce3e92f10cd5f8295481a30c95c4c0685812293dd47e41724a5821430fe66f4ee82b9e9c8855216c61b12fd68b1fce094d1f6da46272342e6e9d174acb2549fd9df3437cbfeb5d275102286c4023e22ae04a927614b356b400139a26bafc9f4f3e3b758543c2688226a54500c74ef85198f9c53423053532710fd449f47580202e35cdbbddb3c2395326ed50e8ed5a8abda7396bebbba94fdc69abe5f36d559a7c5198384742ef5d8ff022d30802fbe5ea7ba4011053146b814aea3d3c49077e04f30246dacc6193b2d7e2e32bf230586bcdd10cf597ec322270daf7a49d835015e725c7c530587e1c9bc99a423103b8b2bc84751bd83add2006af3fef9c5e154e11474f30d41517a7cf2f13d617d9c66006cc5717420c067d954afc9bb652b1e60478c36b6bd113a64f8b0ccb78dd4559e623a9d51d6f972d07455f3ed6769113a2bf92cc282201ea0e2ad7a33add9808f7c533775401985704c1012c62142b0b7f33fb21498328e3e560951e1bd2029014ec94e33cc7625436082211bf6714eb9f9cd62a764d664602b20d81061b6a0e7dfeb7ae765af87ea9230900d19f43aae4793fd4b35f13c9b9f83caa24d23b6cf2f2c671cebe5862f7ccc49b054073a3bb1baf9063a0c1909333d4435f2a703a2522b990341e5bc5b3989ea95d14d749a273bca8b0c9f46aa82b7a7bdd171e016bb366def456050bf3934b32671c0b10eb0682bcda9e9005449bd27f1061c092e9a67270a54c44cee61f5423593915c01847f806141a88a9e33fb7bdf6cf32210dfca6bec226598de3269c11d1400264cc3fcdaf249901b70fd3adb603626e957996a36cf7b3aabb67c6833697e255ca45fd31b2774c535fa054fe3d75fcfc01889aea655554817a3520042d649b6d61a02e6e06ca3ae633396da369d8bd4d757f13b8a0f4f36b763adc6bd22f47e2a829aa19c87926b1e002f5209672ef3a055186137dc81a7e9fe0bde4adeb793ff020744cd705b8758fbbf3f3cf010bcf8082cc762aa313d0a7724d8d2fa03e793d680621381baaaa1463dd6c58447c7382d854e527b576e10ad2e30e524d0255a228cc5a915aa50401d46c68ee47f53be5f7fa3d9cab9217f8c4759027b21ab2245e481ee8aa497d41ea6b8db01f1d8d48efd0138981c75844a8063add72fb7c756429969570bceff23f884a7a6046e7328a367c13d347cf42d94352394804959e1daa178da417b8a136a09e5c3538c903eb8cda63152ca0fc03a5d1690a73b8ab84cc1d26d3f302c36bf423c403ea31af28e1e05c40860c77d1ab9a6cf64ee8525e73ddc31b22b93ab8c95b40f0fc60ce003c6d933bc6a4c55784c88a692efdc35eb68d25bb62598bbbfa12eb178fc844c2fbe7b458a65f87b935cd289c3c5dfc0ad9430ba2d48e3e1abc3632607e1f3b2bd86010f0525dcaafb69d2cd72592bd95bbce3accf0334fe18ef9e54f9c399ca3340a28e43e64ee13e4fee6390deade835ec06a2b32cb79472330e3fd40125edefe9535e1aaac59cf70953b3d8dde4118e96fa43c6c03f53687e8516136a1743c5f249a630870a9dfe62141239aea350732fc8d43b9cea3952574790d6480faa2c9348cdd31f7bf8a80229813e55fe1fdddc9359c07ebd13f53ff0724627193c0db9e5b52060488ca57d37f44759d51d52710782e4111c0d09259c2a9e37c5be9c341851078292b4f2a5731943c6fe2ab8f7544a8e7ca52dcac1b4977f0a097cd28105ee7c32f555f39e18839ee60fab0d150c9ff1d5c880ec5873a7549e4361c09915a8ddf89870d11a9d42242e17af1f618ad137c14113beec61188b31e135d38489617823fe4ac305a8cabf36a6a008d0d8b86d693668329d9e0203038488d901b3ad46b52d019d789cc3176e6bbf0a71ec111814d374efd984ac1cd10e75e60b5350c9d0462242b5036da69a95dd40992b4bf0e0b8e581a994d2e6fd62805692ad63bacb0fc7351b96aff92e7f7df9948038c1d7245aac5952f4c50b07650e9dbe575ed620e15ffe2a8c83a7638af17a021d419798a8ea611672bba7191cac5f35f1c57467823b66793cb059a458bafa94f7a0890959982911247e4972a114b2777c633a944577efcd58987a17c6aee25f985c9c6d8a5fde0dac5c03b88e27e8c8e700b6433adba354063bc7ee74675b3382832997e23edea77d0648644ac1e78b9388fa53d210361c75e11686dd9fc0f8f56f256bd8a7f23a8d34d9c95d6a1c12320dd169f300cf2fc5ff4b94591804d18a924f360dfe1de2198f949f7e07676faedbcfaa963bb2766b5edb8e6ad97a4566599a966e9bb7e1c034a6bc80caa23584c159b1eea3943ff2c6f406ea0a7748e87f98854f5ffcc490b37e898b9876a7e5be494e58be67c39455111dcc6d131d9c7fa617a393c7f9bb6860dacaeb03669ecb4dd9b9e6cd641fb464b6901dfbe556e31627cafdcc9bf9b06e776ab5cad4912cbf0d18df497859b7bf1daf76340203ccbdf0f77802d0d7bdb1a30456acb881178debf398713960629c7f5db8ed9a6dcfd6d24adb699a950262ab07065888be3b6aaebd0f004fddff8173764f8698e6deecb1f1881bc7dd68a88f620170306747218415e77b0436876681e61b9580a8ade84a076f6c337eeeda4cc66c9854a2c308a51105b917116aea18cb4b039e9577342ced2ccc47554fceb657290471a138d93007435c8dcbe9607b88a2bce31095667e6d038e5f01cffa25b7bff6491fbc2d8e958d9796de03967ebe58a7c42274c91527297cf156d0d29cf729c06e2471dc856249feaa46f50c4383837cab8c122ec99bfedb6b28eea07aa3c7041ad4b379b118845f30399305c91064716857fb74d0db06b42f03bf7515611a31f3d4b9192671d3674034674e408f2f9941b6c745d1d32fd83ac9932e2e5f75c19f1a0de51e7aa49871180b166e59392d07713fc77ea55ec415f369555307aa091d9bc0c2388703f793600a8e00546b12e9afa1b6d1b4bee6a44bd823a8173e85caf94d7b8f2a2ec9bca0303174906c53e86d1ceeeeab68f781b3f8a7b407c8e32fb080c7b2c00f12f4f9377d4985019afac0a4520aeb71bb4a57d4c31dc3843ebdf0f03d5654d880de44ba8c1afac9a71cfd9067007adcbd5370030da7e3776b7655782888126e4a09a372d4f004219820a60bea1f630d07dd3649b8fbd5d7cf161760704de844edf7566e5c680f1f57539c59c9101ae4dbf0f100c07b578eb6c15d50088b01b380ef7c9b33a6600c502eeeabc220584623a27ae355aaf1701fd946ed43ac70e4299e9058ee9b9d034d808788f7fd6849737cc72722c0ea390e186e88318fbd636bf53cb945d91e1c7d8a5d1e15d6ba872e63cecd633c3ab8e0a75fdd8192f601510316963f4b9dc674205c57f317e15454440b7c372de0d9977dee63dff36fe8b377ffc375089a21f7af4bc0ee4ee7a7a05e0fa58c4cea80a2416230d1457993e13e40c80c1390e3523891e006740dd29008de1e1233fa8098b82d0a584783fe1b2000ac94a72ec0b1af8fbe756e8acad0f0f14e322b3d98451e1986409f2a6432074ab5cae85ae352b57f61db6135591fc72a7cd317fb20989fdf23b0b6e6b643dc3d5beb4e1829ec792ff7e697c21a1ff751573fdbb60c180408d22549f612b2b72d1add7ee1348ca289119add76b91f817990c08844dbef1b53cb2220df9860f44ba8ff2cc654862d49f3a0f607a3b6cfe2c5f702513e9a67ea6ddd574545ec425b468e72c7d5f3c9708cf6e07fc37792f551ad76713b4d9a0dd4d71d2769398312a1bf3f9f68ef3096536b78048a388ae2b0b578bea969c2fe49af076b8d27c2146b97bc839e038561871b6cc22168d6582f4158f482ee0baded1f948e61c74c96cac43a66456b35bd3b13221f2b699e1c822615c5b371a22d238f098b6166b092aee05a3050c710fc4cf910ad6c7600c2e59b004a52f30fa911bb7b7c119e022550542acf684f61233a23055ea36b2a80621aeac416f1603aa43dc42299d777b33859e4e3d40ef167fd602ea3b42e479b32f704501df1a0d8ab6947984c1d28a0b6be7ed8044ce9e1d77c921f91f5afe6edb7fb67093ef926588f5810ea636f14ba282d58a355b8808a36f9274aa108d857ef107111088a19286e75e488198b1ec1e4630d0427c3629c3657dcc4d49fa0b2b1ea490f70af862baf75b131c0a29cdce9f89527e2d64010cf59d854d7223f36cf2d7f42e2f168506d099e08fd579ee50c822a54c56585850a7b4e86ceb4eae231b8d28dd8f3e8a8144378aaefa3c34f2168d40797d302b0153008e8efb64ea19e9cc2559b97111a93ab9f9da7f70c158a4e835ed2bd78f95359015faf58886b59b7a8743599f4bde9f180a75fd9631bd311238939d336f3c4eb902a29422ad45f828fb57e43c1cddde355858c5b86bffe51d235609544906a87d9b59be0694bed1693c82d2350d2e3e67c15216aa58597eaa926018252c33b870c6822464a492092426e9f7509f7dd454ff67c8ecc2358be9837697041a880f11c49b9b6e2263b6c8ab4d187f414bc7aee78842c7ad9473c0882f193a321ef2e590ddb2839448ff3b131cc88e88a23f29a39589bcf8018a10f86e760883d0f50d197cd68a14e41562503e48ef3c07384b1eb1b6b4a87b426ac76b978eff18cbd046187c4adf138431c5d3148f2d141ce4145968183fbb6716c86b8ee0c120c3ddb3c90c4d0a5b663b24fa7666074dcee2bf3f95e369b26ef327c83b50fb42d44d9358eecdb1f6c0b409af77637838bb4fb5fa7317197fb461b87ea9362b8cf4e258abeeb4c23059d29d818a5e2fc9aad066335db8a6f009b8e724c0d39afe47b8dee020b9bbbadbbf68592c8715459c0134f1d15ee3393e0c9dcf34d05f3e479a1a536a97eeb3b0d64934a02c63035de7ffcb7c6020311f3bf311bc0a19b5941eea00e92272579ae1968292c16c8ede0ab016a8207f4460ca273c96976d3d851d3c7d767a58794dd48da77483609a1bc4360423412a1da52ec6510397fb0f3ae2fd7cdc40976b313e4721676b8d6a4ebc031a413493a16717560938abd4635eb4440a9074a4d0398d168d3dcffeffc9c43952b8f601441418c85632d0a45a294104b2ac0207153a06e64d5b2956970909b6f0317efa0284b09560744b6925b6e4d87d354ccbea4c538336c14a08757274c6c03bd694a313a4d5316ebf532b7eaa8d7164ba1a0df27457b6cc7a7d6be0ec2bbea2744c8f84660f0aee11ff9df858f41510561624a090e94060847f8e90a9a1812c03a63fd67b04ea8c01cfa47a90dea13f0d19065768735840d0421fe95c99c8edae56c3160861e26fd7842363cd15f4d34aca1f8b7f69c7bf3caea247b2c06f4d6bb8375682e31ba41267eeb028e326d274ab74ae3b82afa65fdbd93fabfe93f98581be1cc532de147bad26ef4c3586116e379d256b343f03e57d183a6126d5436a2bdb86e9401321efacd24e801a813ad45c0d705723bad70ce5fdc7a798432144825bed71984bad00990c3ed5bc86b212e2be88aeb524f2e5bc101147b4a89d3730c3a2d0129914196f4a070b75c5b71d8ce081c9b9f1e989bc602e2aae1e44cccfe39dcb64b844ec56d1e1a6d8c082348ee634ad90c28c4eccfb2ceec3c4d2088e704428abd111646ad54fa6e2c03072ec44d099740e7c6c1f3d48742bf874ef91685d1bc56180219b68cefc0323dad540e07f5a7cd200bfe72486112be8bb45112acac586366f45d694ba65de9fb26be98a9cc6689a27c74b3cb5709517dae0e2dbdc961b21e9c2eb1f5bdd7025e0f55cdb11b2cf796c673c1fd61be4cb625b7e01332eeb5e501029d16a8c8b8b4a0f26ee7766ec868281c4372b7d3fbeaf3671c4f94dc58c63607113f37b91b55f65089aa0980222f2b3c08022bc6ac34ee1c6e91c3a7ceaba8819930525ce0ea891b5be06f28de3c80e7f537d80c68a8491aac38d60208c4da5b94cd15b8bb15bc0758341fff15a0a1a084682a03c8956e466817f5b628af61009d2158125986f99ab56af02c4d76d3018dd9b6b8cce6bad1ebf4e749c058ae925558a56e7a73a77078d6882862d6bb345c2635b256c8224858a7be0676bc51e3c00b96c668592274bfdc2be63c97d58dbb47ec6120b89901f7ba17be2e97e090846ca8011fd2ceaa7f6b16484d4fc68fc14d32b76bbe66530b845536c87f21b53154e3c5454022e861c34364c2d380424a2ddc4ed1ce62041213826f1f8f24d28c530dcf0fea2893c24cae90119df17e22d5c100ae145456351ecdd73937eb6377480386e9d571e9b064c29601fe77a038d1429e6d3dc9ec1e1a8bbf93d21151edf031158809408f69f8ce2863b7ae8b761d943aa3c215db923a7bc01bd082769ccf8a63f4672255a282f7a1c4d23230c9a7e6171d8e3b97db2c0c3cde2e0fae89d014fb0074834613d4f2b1988341ac9614e704ae4ced2a07bdc01410d094b3d0d6b70bc8d53fc2aee6371e972ea00f84a1b9d8c8bcf6453abd478d788008c5c2206cf135e0909ed01e5ebf2fee40a0638f0f76d5b5830e7ddd671fa17751a93d268640beeb8e9618fa4de615ca0f0443fbeed84d21e5ec73498fd1215a3f5a1608b830e69085c28f58508c0b3ddeb1b2a28615d6fb1878d5af54978b243b2c239b9bff025e6f010c48e0e34a326b925d175057c03a3f405fa281d8672fb1a13f49380431bd12522b0b8052301f478230b9c093d8b5637c9528500a59efb3f6f55352984a744648a05bd0a0fb83896b0f3729634aaec94c14a33687e7e6bec0431693e72257da8a33b71ac4e270171d63b02d0f2ca88cfa584d402b68308af9438a2212334ae115446cbdd4fac08a962aa0997aeb7c6393b3fc4f800721f6739a0646e87df127ceaa30db6e20425d702d495b52264a90eaec8a15ba35cddc30e790d6350a8de69c3d3fdeb8290716477cd3ee885958f0879ac7c1739e3e51fb9c3d7a1400dc18267bd5aa32d4021661d9ed8280a01ef3f9dd7f0fec7030ad03048f0645485a99688dfe17b406841f28a492eaa011d54a9e1f6631258f107e0d7d65d2425aebf0c30a8d60d1d335659fef4247c2a82cea7766be8d4921999d81ad88391deb55caa14834dbacc6b403bd8c48e7daf6a7dc3c4f915b0af44921fdaa08b41808f2d61c87a98bcca4db40a3ccbb86fdd61dafcd05e6ca8d9db4ea4099958abed8e8924533424d5c83f6b9a5990e27e567374e1e1d7f57702c328696480d7495e2b881b0204d64205cf2b1e03529c99ad262fc32d7acf95565789b0c7549e9225b2cf59b11e5c26ae0a0b382d229ab9bc26d04fa6136b700e5e691e6404753445e87f02f0bdcc7f9851d5043026508290add7c2915b51f12e5caf9f041adaf42d8394b039a2bffbcf8f6e2fd1410aa5be24212d6f0e164de4c990aebb8a3801ca1fba3177f0ee03da2397677ef9c5c68effc57be446a2590e054c2dfbabc9738d799f1d875e77ada3da5f464eae0cab452a635d473e4ac9c53157970971a0f826e48fe18fe6ffc1a06a52a086a84dabaf4f6bcefb8fc8f033aa19c52113af4ebfddc039852280c687c3095a2667e579b6ad372df8169d113b14371ea0970054fc2b151627855f38845c303266bf305f53ee4caf9fe69c309488d6504a8da30a1cf94ebfc5c6f17a172404de309e741dcec284b516d2a04daf5f4c03d55f3e11ca0368c1614dad4583b76fcd81c9efd994527a458979334b42f0a7259027389aa9796014a14b8004c74a8e6531a14ec905501518208f3ea959e5c235e6a2fa5dad6bd32b29452539e033bc15181bfb42ed24cc127429e477b650e82f5f4d6095cd16433bf965c0b237038063567de7cd6fea9698f64e5cc248bc126dc8d7db2ffc269da75be05ae32f4e0a3af0c113c5ae3d8dc838093661300343cb1df5dc0b79771d21419df98cfee03404114b31468d94f9679930ff79aed5bf2fb06ee67ffea1caa397e7b46b3e55ef474d45a55ed0d8a8afce2a78b31884254556a492b1045571550707a26b61d09c2ef61806148125451cf0f330aa98dd5501198911ab1b82d1dc931ce1d8c35ee3d1d748128c4a1480c730234ff6116f1c30668a24c15ed462f1924a52a00c00d353251c6abab185369331ec1d177c4337a4d4957ed697b68b64b45a7345ea55cb771a12fb675de1d31724a0d72cac14befb543fc0547405ae3dfdf350ee7fa552506c1482f9d57557b0a900ea4f3c0b4981178b0b53514c5393c0f475cc5a8693cd58e066ec9e5125cd0c1939c9d7de36467efa674a1e0fd6a27a2dd462a23ac852589cc4cc6832612dd0ef697d17e2c1ba838b445a49eeb035417f60d839cad651dcd7afba5a4c3e4e3f0e3baeee035d5b4bd8f0330213d67671bae285811fd9db86e381db92194961c515ae946a193410db66aabea274f5b1be39ac2eb61c006278dac5f194b888661a0596f0c547bbaae936840f801c82ff2b56e4366b24711ed92548381ee63afac01eafb87ea5490c1599cfaa4c8a962db87a9487cf716145f30bfb7439d8dd19c076d918e6101d310886173cbcda1e1e4115db77a152203928936ae3bdce64defc7b645da7d318a1ae734ad19b275da9d097e9f0679205189b7fe824c406d5367ba9747f4c3161f18db43e78a3ed42e8eba6bd6089bfa50aa642db4fecf26cc443b1f766ea9ba574cd32ab90bd84e219e54c58d540a09fdf30659d7e4323dc1579d81350b85a891fdc4cc1cd774ca8219a583ffc94b60c0e28bc43ba814bac16d21d579f79ad844af6bcf2b78695f59c105677045dfbb5f660b9268a5c1cd7d62d4ce7d2bda367f1a5fd1784ca63d5d8690556e9dae3ba8560ca2419852c80d25c215d45895eddd1b51ddb557c307156413e99005886492406ac7bd8d1c6526b0c3fee9ccd8d524ec7f85e75f04c5898902bdd424b21d45100ff9aa6a8b11d0a04e25a78e6236a23b0973397d2aa2ee4b2e034f0ed975ec5adc7c8e4bf25e3f43dcf08a668862e69ad3c3025b3eedd95e3071f4282727f81ab093c28c38722546926b74f9c1196bbcf4d249df917ae327d850b5bc56e8d43e113a45f85384ff0451229050e53c4c1d68805a2834c6ce7a5131a369759123f4d074200870f6d8629414c2d4f387e8d8cba0f8eb1861a3ee8e1e6f7b1a9aa2ecab05458c6adee65e4df40ddaf8a57e9796590cbc05d615b987b1c6386a500dd2000d6034f8d340d15de9aba4c5ed95fc4e35ffa94b7e878045bd7009bd792dd2de355d97da71ec89cba8e8e0800502fe03d794f4140cafa79502f4d64282ff574a9aba8fe41da1d713947eee9843801251f263ffcda9eb50a3627ec83770917f72379702b2de5bdf330e0b7bec6f8d48cd4dcb98118f8eecb6eb95c07f97e6aba76ebb09d0ec85fdce43bbea7be302794c220ee15ed772a111316a7e23269e94c921f500cffefaa9dc8fa6c3967c935f1064ce6076f96ccfe44f99f1a327dfc303e0dfe576553f788843d8a314054c64fb9716c02fb4339828650a76e77efafdcb1319940a83a63e05134a906f717083b6c1ff95faf33a597c6b71dcbac743d6dec8c20253dac54476e45d026a06ec47aae3f6c17efb1f1689b00963aa448e4b3b0b3a461777507405c7d2986ab62bff8b6a49d207d564c104a9b62370cade38023b4dd76306e312cb0809228983e843c49a942a64ab9529646d04723dd19685956e948f1a19296cabf6c4d6d4fba671d3c5edfcb838e65875f6b1e886e7fbd783f4ab0cbb192644cfaea2c0b13f02bd9630f85caa1185a39a34a80a08fb716eba88f21a02a70a3f929ce2828f053168cc85dfc4cefae83521074a474cfdb7cd7d48f6c428d985b103adfd4f62f48d0aa3d84812dd4684d685bddc06532426129495952180b13843903034330a535ae5b02168d298bfb5ee4f8194c6df038f01a6f0393cc7880b578207291b006b54d48c38a0c610aade3724d81309cb3691b06b4282449cf0df149fc4f584bf2035bb6a0cbbd8e5d1141502656b5501eff6b84612e4057f2ed58f544d2bff4fa20d2898d9ce31fd533e52c995049fc7c59a0582b09f186a69c9ec06b455add5b46411ecab8c9980279f91b7b20a790b87360579aaacefaa36a180216367597aba34214cd640e2251e7231448d55355759a20d7d63186514a56d5b57c140c1e5977e582fd3a604a31aae0a38b2d7af2820189612adb32921e34aeb0096976c205c1de9c69b15aebf9018561e2e3d06c2a46c2cbd346c5011875bd9d145bb02aedf13bff615736b9d6ddb9d677c625b64a82a96cf943b8ac06e2e32ba20da2d39b9fbaa2b6cb7ca6a18471eada77f8c8144ea06d1ec6fda068ea2c4b67a9840c11aa04feb4b29a0d255c62fdc532188fa29cd8635aeca2bcc4f70026d71b0468852f40f95b7bccae8c9dbdd5a84afb747e18b13a4a937d486c6c06f4d4c70b2b74d14d897327060c72d8e956b49c78745476c9e8505aa334a3e8d91262eec5fdebc96815587cef86ebb4945aa9c56556036e33561750965c6eb38fea5ee46831ceb3102e4a967952cf409112a2d2df08f3f5120a04375b1fdb171d4811eef75d436c55c8ca4c574abfeb008c11c2238948635c8faf7c3fe668e4a22f16d596ec184f2aa5fb9b2ea6c13446da4bd485fc52cb4c3d363c3275890bc5ec060e3356e496c2b5b6f4662bdf04238b4b2eea9f67910a61dcdcd2f5217699104c5bf88ecf1035eb9d2d63750458ef880e821069a6c48f0fccc908d03c244086a2dd61b696bd9d69a1c3e1714e0d5c83f21a4856570c6066d5b6e18915f1988f52c081c63488108807ca945674d5d2ceea5ba35b70815d424361051eeb6744563cb429a6de3341a81085f810c306606434b76268c288e46531c8013094069870209e63d3abfd6b0db1ef1a68ee7540388bb4ab47e75faf2c13b30c825c9c30620a6f99db781abe09f8159d7ddf9a1126243c6815638cca3a6b4cc43648d0399625dccaaf6becdf9d4b32aac9bb1ef17cce82cb9afb210d479ad76541dfe05252d268a4c8256817ecfaaf811a4b2e8d365142098812142cdbd8be6c8624029623640a611902b046565efc3ea02419e0162e806228609ad21709441cd119e772589225ba360aa6e25dae953f40212b9c37489e0012fe98922cda65f04eb24e7d1178620abc8922232a44b625a5258a4421762b12599e8a6d15749e60c41b866e53593e814f2cc3de76c2cf5b2f8b2f2ebe112aa9c5708075bea0fc856e86e2cfb4af52fdae97bbf76cd2bfeb1fff7c82d31db67ea797504c6e3e2a033ffac2215c01b3ea22e066028b7a38cc89c127bf297831f0a3d67a2d2bca6585dd1f02e2f63698a0703e81cf176d5bb10cc1d0db712ffc126502d45ec4ec7a2135b888f304196875fab04240a47d04b33020beecaf027f1726e54e510ec57412f7184d072b5b282a7d4710817ac23732f7ff1319d15762ebc70a25cf05d723b6d98668bf478f0f6ac26689b163a3e1d4451a9cc42d513b415c75edfec99afa897e834a65114b4011558bbbb47fb696a5946344de18ec2cc5188586a2476e2d7f368117275451831f868d594bb2b050eae9f6eda2cb5bb27ec8bb6cc549ebf99387a7ff9aab5536349d564a1f4d66d2fbff2b8d4f2f2f5f690264fb85332103ae872d951a6e3d4159447f6c0070701f2046aaedfbba24707aaaf10c5ae01d72779bd9be242414a2c562fa21c80ca0f14e615b2a8d10457ee40a42a51440d3b1f597aa4f1de0260908d045ee202bdc7bfcfaa5038b33251410df084418b6c2abfc83b7c864a1bff7972498328bdbff0cb0ebb5ac83a20ab84fc7ee9520eb3da1401823f64a108d10738f0f6f3565f87cda397efeced02847e8ebf96021412e04aee92112e4b25c2888ce47eb28e1d664b277564b0240d80bb8647f870f68d0f31e77a1b463f7d0b3b47c6695466b19c8068bf44d8ef7b8dd17a38e2d0661ff71f24a70ace484da1e91f6a2e2da5fb6c7814f3757427724789fc370c093baa7d85213cc3644e742ac781f4fbe58318fd276303617898e453f7a3a18bf8514174378db947a6cbb67c2d1865c1bc1808aedee1a6fb98bc6d100e4a10abc7a79122b38607775e684202ce0d25eb0f48e213b47c7ca3d1bb0741e313ee995f9f7bacf9f46bb6d6f6d74f8b5166c6f923f7120a8fa4cb2cff93410e2531db7146ed2882075a1378c5a296a089a73b204e3490a00c2a5e105cd72d3c521d45615168e83471eaa6062bd4ba3bba516b698afbaab088992c01bbc1ecfd2288f77174ae4687ada943172e01c827d39cc8de5b2b7ea1403dcb855792b158c9290369081b56d1a0eb0aaf0c920c819da214a4c976e56141b82b582ddc9b711f4864481d7cf7e8ae4034ce636e691c8eb31120540a8f8c71ee33a54ec9209f3e748b7cf66a011379a541313ba5d26b096b1ebf361908334fd8bd3087b8f7a319d16cc447ad500776484891ec6e8175026ab588f1ab836f66a9cc6804fc01886e1ba27893c93de917d5618363c22267a0cc383770a5a0bf6d987314614c1f742d0e351b6216deba2f08bb694f6d6a635718b8e64fc26e5ca9ded86549383ddf33e6371e821631e694a8f81eedb21cfddea70c2b1add8c26b073c1ce122399470b48e519979f19c400a4d59d74bcba9aab113c3acc93c8578e2f01db7768a93eda4ba352fb70f77768f270449f46e8c25dc24d9faab42d4cfbf768162d15623800421b53ed707785ede1035e13acfbc43194e3b4aade09130b14380364e99073b1374a8704e674edc62db664d13276f379ef4eb75e5eb8449e2dabd8452d86c3940b7eff9f159d71929f697645197efba16e7943818a458e84c50638b63eb3d0903ef61667bb3f85ca32844fa7ba2935cb0772035844885cf6323f91c3ad4eddd909c4e2c311abffc8048857722e3862bbf4c088b2e3c80ce4313ffcedabe9851b053f9b8acac1bcdaeea426d31725a028b8abf860671653fae17fc41349d3e331d6d2ca9695275ffa1fa941d769bf3fd9715a31a72e8c606d27b6f9ba34d5e53f95e49a92facfbbcafa6ea1ca6ccbd4ac83f9ac64a50871a85026595576edba3520ab9832244e201987247bc7bcf0d894474537538bb0ff25c10148e3940a76892f3b64cd92dc71eb8c8fcb195d73f25bb941c92a9896ec9965317b5baeb1766d350f53166f465cc01e0bd5797c0e13d82c58dbd4fe0b143a68885b64fd6ccf8e0dd14169cf039e246504aaea013ebe70438660147b06c3ed8b2bb40786459d810f0079f72219b84128886748a4fec67bafa5953b8cf6fd88e2513b91a4fd82e68bd05564805d40b9d25848668788a3c7fc900c39d902f2eb5b42cb9663bc404b96908625dade55abf3ef16e48d2fc84592c3734fb7b54ebe2d8d3a652261282940ac694357406701dc34ff9960c32172b977b55f75418f64c9edfae67a523eeae2810275e0627bd5a542df6a0ea3958b0aa451c4b06f6633ba43ef529eb1747aaca598cf573d28d46a933da12d60f924ba86c5474a2162394642c4e6c3142bb17156203eb219a29420f7db85e53781f3359f74c2b45442578f28dc859e0b4f6144937c37c616b342aaaf11c3816d31ff37e51e916bc34bfc8206eeb63829662aa80859575eb74b79e9cbbbbf5603ec05dd1c8fb6e920b1b3111f6f3f3f591684d20565a8725c009a89c3388e27a547f69c63e5574d2eaac8ff0e34c90c44b4a1d63852106d14a7cb581c20b311d58fc0b6da832f7eb7e423afda50d8a713d988cc1530d32d17f6609831de57282a7352528e452a803322c47bf6c1391a8c6178f81982943c1cc6da4012d685d213c2d9b4cc799f32f9484fc4003683426085453cc72a2b8c2703fbe11ad3a8c412615a12e511cd8345b3c19b765a7fa79852ad23357238a491a9d6918314cb84050d30e953ab20b0a9cb54d66995f8d49c7cf8149f0a5e358b4f3201e218418ced1fcca24315ce4131b2c3d6d169ec0820a6bfc3cde20e5caa6125a01414d77dba6ece9a2f2062da65c95c94680dda6f8c3b140a09153cf18c4e2a1ec6f9685022aaf574fb89734f339eff4ed9b052e3270e0da33f941b08a11044f132898770fb6fc08898294fbe1ac303b49b5155e674d979141b2e14d7d1659f37377abdf21fb324968c9150da81827b9e08bc50d30bdfe7e602c215f0eae19df95d0ad001c1afcb8f06695da066baea20d99a900227b0a01d23ef3291c54f239843510fe94d0c6012e5bd2a7e17404345bbeb41a1260ae8290d1e31755317cdea17af81a1729083e568bd2bfff326af6799d8b2b09d9075edb8217f08dcaf9afd52ca7e7a761470625ceefcb7ffc5a383601e261423219ebec40be7de8124e3b43e7c84b4fe49cb9d31556caf1fd9e5c7d426bf25df795d9752ba63af7c2fb33cdf711f3ad924c65174ede64907c416570052fd2eceb2b52bdc592883651385905a1bd27359b386420803979d494e3134ccf9089faa782ef7a1d2d33063e8c2c86c2b70f703bdcfbd937450222ecc53cd213f0382ec7eb522d14cf03c430aae52a22915c7437c5cd4ac548241ff4a3314a17deec818477c3864f65b84e20151419590fa0af0f0d553b3a430d927e9a24f28a2fb2582818fe94be87ed6ceb19cb63fd76aa716795839d37629830659b012cebe6007eb24d59c6ab92e6590ea60033e429385ae4eb9014b7441dfef9d59c537b566549dcec59272067490858ecd502523da2f951f27beb4e418a65151ddc9dea0ab244a0814a9b20d8fe1e01e64f4aa12c1e5fd3e561d5a47a2d6f20c5521c21f0ba0aadc6b6923df4badf4840a24831aea0a70e9849a88c8ca61961a29a1f2eeb34c97ad5401f213225f5af3dc446ee4c9336da14f6afdc5de858ac3fbd0e1dff2822199659040fbcc091851c3a6918a8e043b432e1ebd9b22fa78a20e2add11f6b4c74848c8eadce19590de72aa01ce6d5a8aa0fc982dfda833372853002f812a43f184675058ba4c53cbd70bbb1e57447f1ca11aa20c5bfa43ea83f2d2be9286929b016f941906e4d9750b0090f17756efb2528546357744986ae5cb2aa90a7ac895b72962c51592da501fd043ef2edba2947bd0887fcf31527d09ad21a9de02c13d7c212683493921d3f385a8ab7528072297949af9b336a4dfd49eb43c5aa879cda000bc55d386289540cfa8d0997b5915d2248d3ceffd1f0c8602775e935e6d9eee3d8916d7e1617b10048b5d2b7a120e473ff997d12fea7f37cbef93cf58b5fe22804e606733a027a384f31839e30a2bfc533ea5749318974530a2e4fb6bd77bc90bba0eb3da4386de9789e1bee6d9b1365f8004994229cdaebf8946748b60c85e42a0a822cb2446aac8661ac5ab229d1ca1d8ff83ceb2e52765cbbc62d2cd95ccc3c52b53c745f4c86e44830e1979fc3286cddce0d7371bf08949978e25d685c60cd1ae50001e795721f379f8b73bb10a9418b8e5cc5bc076144ad5adce289ab6818546a34479788c629d85bc706b67f0d0bbeb7b01edb345bd2fa51b511601136d2ad1bf65f0a1ae77059b3729e1f450bc0066874d612020af436177c7ae29a6f179b8e5b65a289874e4919e56d61a34a52a84c0df2f10387bb51770ad5714113484ad68cad525b474ed3adc63065d7acb52c44e2c6a654a34cc765b2dc8ad6f27796a07ccf444d7f22b375f33492bbf06e5434d2b1781fc5c8dd828f52b8a10378c00dce833e1f19301062f4a7193725ee25cfdbd62986935e1434ce987ca8534637a3da8d45e8068e927027c5aaa39d8017285d8a1bcc233ac109971041d5cf452bc66c884948bcab80905017330e28b7fb8162b5ede4f31f4a644fb037d2df6e3f2f5905eeae2ecde17afc5b8077ebc27daa037e7aabf6a93f5ba09b2b4a66c6d26788bdd1e8ff3d70015a31f404b63217e3b9be8152417a1eadb68deb2d8a3e4855870c125bffef6f2d9efc4faf29cb0532795c8d43d62dc4ba1c5a05093db80a6745b0298a170302b8b6d43a52f3824f2ff122497445da61215ca93bc1668ba1e5a91a3049034c59d2c5c035971700fd31f716616010bf62e9f24f93c41063a7ddef7e76161f23fac1959f45b004ee6f97062450ed64e37ccd06263bfae5295e0ecf5b187c7d425c8d36aad8ee687432b1ce98cc1b6ebc843a616fd09a9406e8912cae831064798e42fbca160eec68335ad38e3555c4384948a61d2b1647a500bafb595beeb6b674c210033eff6a8e50ca7c7eb16f33af60245ee57ef3bf0ddbf1d30b22f5c7247778dd6eaa6b432a0555b0cfcd5b76b7ea3588bdf2f47e2adfc51c3a8cc2bc1c411bfde5b669e77b7eca568b15db0c7e517659bf19a620aebabea9087af61e267e85a2bc42ef06df3feab0bfecb23d4043b5a6eb0b451e254bcc341daf5f68d019659e8aa7af635db29142be09030905a4b99d738b98ef4297091b3fbf82ac84829d8ede216058d8e66f2838593cfbace6a117149129e80560920d5350e0adfd675f5fe1ceda86c9eaa94d498330266dc38870e2e2bcca32822f1bb79c4df4462d99183e127ace42470b1e155554f8dc8d3464424ff6ef440375a9eed53c1a31e59f9b608b755b2cadfbc87b8af69675c61f5ceed3fd3123cafea4d505ad7f36a53f6afc008083798553111e8d3021f00a13199ca9a40de07e2595d5e224537057e29c7d6a1c744b6be651c43f29c873154d54964e9b1ec895eb5d021b40385276c620f4880eaaaf483c226200ab29423818f0d82034cd76a0589c91c6272524491f6c1200ea0a53adce55e363186bb990eccc94d6f223e028cb8085df7d86235d3ec73c6e0852640f7c45442b91a1ca7d43e11a7e107965267adfee58b48f77e46327895c7848e21b357da7cc12fcfc470a760525981ef7a0fbf481c211fb0ead5642055e3a8ae552748a6bc79c2528ec6cb312f70d6258afad1699b6c928cbc5eb1fa3f917127c518576c6a3071320b522517a8a5dd013ce6dc3343806c304886ec8dccd16a15bc2ce2fec0200e6a9ef8af684d1aba8599aa2596aea8b3a2931dd274e3ecc0e84c6523ee28b6b7f4e3f9e81d3f6f28257b16f37215ded4d69be8f5e2b1d726ce01bda08a4e28898c5cbe840e4a4986264721eed1fe72b079c3b6618e1104d04b51671bca9c2093127f5e83070dfe308e9a1d6c73cfdf3f1b3e80f47dbc7ce0e512974884ee0afd2a1d776a9aab26d210070fcbacad65f401b023eebe0272cef41379782aadce8e3a1a9fb2a56e3ba05d877088181472562657f131f3e46a4c416c2465286114a773294c66c5312c6a546d777d5c8740499bd4c2fba0337c0c7001cb33665596488b1331b12764d36d6818e5f0d2d3c2844de8f5c9044c8c49bcc77021d4159f2f71cd37a4d25ee11e085d995687dd794969317126fefab249010dde01e3eafa8ce99dd35e5ead1a964e3ef070f922c6ed54035ba375fde1b1d5d104a625e493c911b7ed55eceb4db3a97ff52b273830c22a9768d9ecc20b0de088711b7d9829715a944a86739e11cd1a867d2f8cb69e37bd25a2c56316801d6c979f1d7b5b495def861b19c2f3d634a564e94aab694a59fc4be45f7c6825e283715eecdcb8ad74e5596cbd98943c386ff09d90fe2036db382d281720a55005a7373ac3df7f80e7159ecd8cdde31b7412b654d9f85c3fac5b71b103e65aa21b418d8e08c8858e71c6917cb9183f9cf41f5a728e8f1dd7855880fa5ee1593a321da84035ac828ea2d0530c6795df46bbec0a4803e2a85f8d01c8aef6f323b6956371701eb0fa813b8c5739b0a7e7db88291a44220e40adb8b39c239cf8457dd864618a7465734f062cb5e9820f823179c3c213f4a5ff5fbeac5ea564a746c993966bd45978a29959c71a9b87bb84814eac27a0133ad51e9bf236a6e24163a35c01e5d6d12ea0f8ed6866d448605ae9f6316b6ddd1faf2ce9fc688e29eadbe99736a7354c547768b61f8295f67332535042a94d0f5fe3b13b6e1fffda0ccc58b9b06af6899d7a18e9c21106fab6ea452451071af5183b090a9f7e49bcf931fa1caab1119bab9a654c0816c80524efb1ca1dbb002b2168d82ae211a3b9b7d802dee4f0e1a2431c366cf1b9344d8410599072c34e9a14b9417f11149f9fa26edc08348c86aa0c7216f5c521b5d02e2755e26ec612786244d0b8c897dfef3ed7d3a18a24c3b7b848818304f03db642f6402872c5d5938ee087350235a10a55abd32bddd60e6d9a23cd05375392351df9737655f773745634267280a354e1b8440322ee8ceeb5fa0136e3a4c4c0ac644f6419df31dd41ca442f5ed28d1eb9b350c615b634c7d6473a25062779210090fefc4ede2b6b70bb07563492f9e94cce267d308acd990c098556b6833676295630a024f8ea49f0343e2763960a67033e879dcc6a1b1da21d04b4569d5bc82e5dbba7de511bdd5d8e8fe82a2bb3dfbf972881ff115165cd62e470b49c34bd5a1f3c7cf33ba1ef90ab023e1244a3b2c74e340177682649c69f68419b942b80bf8b0d5074ef6756a25bce68212406e0f0e699869ef879e0556d64ea188bb7bedfe806748f2a073c28b4556b63eb2556b5a2459ae09d241a98fdca888beaa7af6f0432d612aa016295154a98c8112987a4b64c1203c080e8e26de388a53dd8530c668ddb210369c1ee51743aec1c47f12b07ef541d0c621a5c3c47ffcde68cfa29d021e4433da51586ddfa47da4b109ff35956aecd9d87fe77b1a7a17b0b76804ac56024317d018b4891935af12999de536312e7cfd850bdb940ef18015a4571d3aca9cc7201013940c4d2b87948e24b25b06e06dad275486e00d64895e1acc50b19cffa64a2e3da94c02c6851df5d931ede69617508f6ca9379caae2004cb735528d56f0f9d74377af76a6496d8e652a42573170e9a476f9b9e1c2803b945db0a9aca8ce5efce6dece3e37419251779c246f8b25a9048126865d78c404c105b8ce116565d8f4c7a96066010094bb7bee6d1569ed5cba53cde66b5a7473d429cf2e92b2d22fbae2a9d2a467976635cc31e19520c371c1307ab07d588880b73004d3456b78a7ac9eb08d36041cd6e74e7f5cd79cb3ee384c6422174331f8e2ca94165cfe299a7b2db5752221bdea5ef8418cbe26bb6a7f765e2201432e082872a4bdaeb424e32be8ecded23df0fbbde3cc19d7323af18b42cebfc2b8b0e49dbd1f2c20687d9710e81064679c5d5825cdaf730a05c38f26a4fcd20eac8d3baf93a323ab6bf80088891e3655d47e85d3b2131dba425e40a22457fe38abe1f88aaa3cb21e8d2dcba79eb28f08304e2825ebd37fcf80214469a3050f73fa284e204422e064a7129d6d21463338ee1992c7124ea4c1f891073fbc703d925c9b03ad6496c5c63ae6e0dffd61ed499a3cc7df43dcee1c09bbcbdec004718398f12c147850c28f544fa3b2d193568226e732b96baf9e454d0471ab7d0b05e2289d3cff82051a625afd48a4481686923fd0aff987e955568f3f427019ca6c56fda77a7dd4a7731614ac0aaf1fcae0f42f810154a425312c42e908f5930b5dc66fd2684bbff142a7d5c712a62d6cbe1fdb3245cc600c92d2a8e06ca2caf66f4d57ebe88335dcd4549343008ae3cc7307c40e832fd5b79920a07c3ccef9c3fd5239222fa278b535d38af0b693e99894753c239ce0f9939dc1815a88efbb9b8a31fb9adcbfb69f6407afa82e41d12107ad4cdb0283a6049e9ebb0377cb6856252d4f8e660a2f34288036807e62aaba648b46522e4fa7c723d090894d2a7bd68103708e84a8227e270c0de94d09b654763ab2d3ecf253e78a03ba6642f2afa9a6350b479a1229eb683ac4c62b356f1912c10e04852a4a9faa1167616e1e5e3ee4f4a5b775f3c281ad2f2561136af51c757e3e1ac54eae85ba01909ef91157c8a90f30fd26105499a6789d0079a5765e8dfa58d1dc3e1622e42886ef805bd4c71ce539b9df265682fa75a800950dbcb474792197f47e47408656118fd61b4475649205a5c9101ee370bb4979b65d9964b9a2cc4d052a15dde243a71c2f4a8b184c60726d755acc06fa31172dbfa8255ce931291957258865107229e0950473781aae98f37b398c9ba278af7a3475f7792dc3b714c48bafa2feefb449baf7a17a0cebd6f7c0122843767c5043b1db3f51b8a3ef374df56c00dc166527d27857b19aa26424cb0e7a3956a7df29d9a3c168290d69271be8bf823d71934d27b021d33ced0d5ba57497f00d664939cf60de89a7292b3450922e7752065b25f6ad2c553a6a33272243124bda3b96be837f4348ab22a2a765ed3315bcdc8218135cdebc30147dd45c9d6e4f1d6d26f5a1479a1640d34f008cbad6791e1d5e1d65bfdeb7ffcc8e93b7c70d0a3a1b0bdcc963117f70229fafed9ee6ea1b62820eb027140048e062aa8457586acc8fd70bc0e3f3753bce5ed050480c210f1b93c1cd11d350620d82787b43d52e49729124428061aa37ddd01b417f0d63fc73c720c45ec91b3f9a02fa5f1b24873e363014794d00c7a481a35b464731587988be45858f5420eed660aafd71df4ab63df7788844331e88ab40bb2258c17d53498c7d3a9a98caeeb214c423c9072a28a11ab9b3869763a1bac410c0e12407a6e4d9a9d3aba8ae94c70be152a1e434f3035a85349b2bf4534afd7ea474c337a1e9202b1b39b2b203c7feef000456566c91c721e67e9232b646fab9fc862891b85086ee7709c41f6a72dcb9e00f8a1137e3dd035654ef81a03c511be2b59c5f0138795ec5c403d05ea39b6065a0b2895508b686a15c1a617f3a1371dc29c2626aef8793590ca6bd166024eda3e5af6065d8fc595054235ad704cb535d0714b96bcdbcc0a60a0ce3d2d014e9e2630821b73172b22e368c0b5215cd5e2ce219d112c656bb6f6941448c150092b9b9a3e7a91c77c252eca9a05e34686984057c1e875dc68d5696daf652d610e062b3b280cfae65c902cd7a78a0b10a2b2e0d80602ba0cae073dd5b7c22f61ca912786ce620e7a6f3152ffa483650c15c99c75de6a965df7a37edf4421720dc639f42c9988bdb694a75e4ce84af15fc031ccb181c47983dafdce74bbf856413b94f827cff2a92d9b962d74922ef0220d8100cfb6070e9a3206ec7e5e5df6f7a1a158200abf5cfcc06d1597f2ac3cc1ba794f0c0d45a9bdbd302d3519366e899ff8023335e3fef840506c956be3d1eecd6de7ce62ea561437d2bdd17c05b484258ee5caf9c9411ee309ebcdff99ab06f024096439024dc57318501fac6ae8ad6a273ffe45a1c0bd2f436fdbc7dc086c2103cd972f15b71e906e9363d8ef577cd23d2739b09193b0aef6c68be8b8b57705464f36b21fb8a0bd5114d3e37bc7ab517a3e30f935473bc2a922d93fc4540780144eec973c79fed14fc2b5d8074a1177afc0db8d7499de718d6c3a32d62ed0d3b7a6efa903994858fa3e30f614d0c8f14d6dbb7c7e62826bfc82190f259816d2607016990ceb4a304ea09845b12f2b5462e710d7fb56f164712ebb4bfd4efa09fbc7a433364a93706d7cd1509a560aa2662356482656a4682e7a91dfb0ff07d3059ee2da609f5598a728dfd50655fc15afb57694f389c4fcd01c8ad376e80fa99c1b0700eddfa1a3c4e6e7fce9bec3e6fcc13e285ed41ee792b53352b2a4062b1589a742263dd00b8ce5ae22f16cfa4a11402c42ae942cce3acdfe1d45a2d8d3d2ab45174e1820d568d61b44b67c7eb676fc61a4b6382565b1af8b8221e82322381608e3da886b2bfac38e5b320c88aae9219cdd49cd45bd363bd3494f24fca77e946f9105fbfa403e5e48526f5c7620123c395840aef11443845c953b58f71c22c713273c91dcc600104ffa6e8f2864f89a1459fa08da404946186df5bd18fd419f5b3ee4236a47c266d93a313d80f269a2622d12240869f5edf1096b15678798a22e9eb6b55168664329a174dfdd0183d88fa461b430e843740acf00558f61c15ab600b9d7487fecc816b30ec7b5cb1a65a2cb4c021c74ebaf628b1fd4ea2679c5609bc23255076338dec1b27d08503889151280826ccc589d58b59f704a475d071ac38a271c3a50339047d2bd1ba740ce2d4ca3f91a010cd1ebc85ae47f031f9bdf03e07f955ca3a8e70f8f6bcbcf41b8603870fd177443d134c66a75bbbb7a25a694fbffb072cf9e4a68ce2c4b6d668068cb4fce60182806097fce86bc5cdadc388cf7ef3328f024113f6f86a9af46ccd8670017f097db14bd4816ce79104597329d883c067cec2572ce9d438f65743cb412050d8c67d245dc1f1c4caa6007db22b3df721dd1abe92d7a945cad384e80c05c1951d8ec629529907a9f778cd7d71d9a5119296c966944ec4f85336718e55e8925d728176db30c2807b6d110f3e9c644378cec968ad23cc345e87461eecaa29c8c80f2550151279d46a3bcbdb66753774c0a597561fed0acaa19523dc4a36ee41ae90d00b2b0cf48a095b23c21d74adac1359357557b78f0de43a87fae2cac902672281b2e3f562a3a1ebceab448bcda54f7aa83cf26faa213cd4437dd2d55badbab897949a12dda19569d95acf7417bb186a0f7e93c2c2e63f8b7c89d905895968be37b2a5985b576d66cb04531683bcd7b2ccb03cd7041ef3fce339396c809199a24877d7697021f0ca355119226109c34e07b8493ffc3bcf1c25bcdaab8c9614cdc64ad2448ecea196ab32426e016dba6cf57ac48c0edddfdabc137177b93b5c2255a31d3a8b76b4afca846da052e035f7d7a5ebcd1bc1ae8bca1927ed0f71091c0e6c6449719ffdc19a17ce623ba3517a45f83a9c03ec1cc07ed25f99171b3d207b8595d147bd0bf7e9e10905a97913789f62db533e1eadbd2ddb5ba743bc9254f8b275565902b69a80195e68cb5b46b18914611df803cf65f9a735b63a4966799ba6b98fbfb2343815b349796e8089413e8093ec569a34ff46628a146733b2a03dc81625585703ec07f9083d6e44f602ee1fa8cc01ab08c4bfa81bf4f6b7a34b2a4ce9c0be0c8b53e0b726c0914436577d5b0899f1489e56a14d1958baa0c86ec5c563969dd29283c1a7350940a73cd4c0e4b0fa75bb8c5767f06fe9ca1fe6fcde545cfb43aa97f1431a394f66bcf25cc3a51784f0ac827d506e22ce4077d71fee4efffe94602ff641b829ed5381a2d71ddbca0d067e70d94b13fe82c2e27282f7e9874e1874cb00264f8162cf6d0a104dda94b4e9f2d40a8e238d8249eb1a0e54af76c734d9d63e83d3629fbc585f142a11d426d41b0c5bee81a58ccb278561d3fd85d1fd72081a0389203df204284ade72fe4bbc1b6d0fe43a31906cab84623ba663b9f1c6acfccfb468b376c43801f39e51a3726cc3caf715303b8033513b611fa5625941d8b21e062ccfd9ad996e7a331fae6dbd5ca609e6e40e1325aabe62cd4c39394c25042ced068e4f563feebce80e3084b69e12f9c50c30899d8509d2dfa2672aae9cdac27fdba67357de48e7e9b19629b63889231a5e78719420c3da52cb248c943dd9201baae5c7cdd0ec6038196ffecec7741c9c544b1fa95387321f5e41065c6d4d5dc67b2ea7d7249b774b5faf4d38e21895d5972276549a133bb7a14dd5b99bbf31fea89351da27034e4e14a5ea317b7bb6487c260a589847682ab75afabc1b8e620f35243c2053b1cc0039e0a8e78d8fb475f20940a63bd3bff4845505e6c25c08ea4efa4a1c954032e7617089ffb47e4c7a50b654a6e2ac76b40a9e8ca6fe883b7d168475fd60e38e939400c159175658f005279c3b410b3ac4efb9d8f3fd0051027ecc9a993100f679526a5b21033de8e69ff609fd445d4f907c3b1e15002fe691c0fd34b9221d3517370c14e6dd7faf6d0cb94c425338a516a191c04ba87733663d123b4ca83b6a6a4b006c0b071db66f241b57ae9be5d52e58104f6278a605b16faea87e972762bb991bf8bccb03253a43117209fa722e9a00a916df85e060713f543bd107a38210650728cccb9d1889002dff02d902da0ab9535191d6f0f2fd0bb2c3585b192ddc6532961ad15fb58615c86df3246a6527c0c8ee2b8f53b8ceaaba62b0dfa808416ffcd3ec47b345ccc5b3ea5b1576075e13a5a2c0e0f745fdece0d98c0956b82b7b3c34b38c4cdd02760c195a6d74e41b01629cba8f131b8dfb7d619b5c9897cd3b0b6bf2219b74fe7b8b5d7cc938c0939710a603d432190894a8f67841e1398ddf1e8eb7cde2e4faa05e2abee3fe262e282fed6d1edc926ab8ca0665851041ef79f27b0a2f64e4b7de8c643ad4cbf3388c1fadf3dfd328f2058a1f7a57771fef3e6d9998bcb20f31e7dba701c620093ba4d54c41346faf02fa6790c3c6800664f228cd459285e26d0419a5f92751eb4487d9aea24f6e2d0acd1d1173e0cdad822ac4f8237e80418d336ee16864985040b1d088b2fbed7c751ed0d1d6a5e5e11235f87149d375e52a6b39a10276535b90bb32cbd152f86358dfc29a161e5a60f017bc2e66729b478dc530a23c3b55e3c78763f0ead81b553df7c3c966053e636363e41fe710f7e680c0467a5284a2bba5012e9dfc12640d0f7ea97f5a47b1c30b80548b146e6761c41357104ca1a607e9f025cd9dfa2bd7a478141c989e93a8c2a958e93b0a1c7f96f6aa41b19454c87279c7b092527c99d0006ea0e0ac0d1b344281d6abea42a0d7687d2eb7e2deb3b365504b1b8fab996b3a358aaa8df2e6a3e476891f113e80fc1b3da0d42f2fdd9d7e32e6a790e5b98edb4064e491fd338227f41be683810fe973707679caea712800540d79833f14073f4c65d61430fdfe2b0a6317efbd73901ce2df632c61cd5d12f21a717bd7be8371be332890175d70b26d62857d784a3b2b73c54057b4cfad9301052a689824ea27fbf6c781a22398c90184f6244c43baa66226da378f6c10806014d890f61bf53711b9b082a7570c5245c411fc1b5c40ec11396d615dc1d24f88349787df641a78ce8be6f25970086500df89c6da58fe200b05c96bbae6bc21e68cd282efffe8a0cd44f74d99a8c3b18265a30dcd7df038ba50210842f5fcc851e14f1535b5200396266cf84a832d44dba0d4ac6c66fd5ac6f3704bdc06c419c8449c6c23aade329f65d0d70da947a9124ae435074d664f59ac234d8928f27de4bd58da23b6177ee54e1c04142a56e2a477b5d4d3675b53a989fd0512b8f84345becc5a6abce643d7143f575254075fd8a99dc489b58b6417b6cecd9895842305751eaa3c1c3b573a1825beb507166a51b6ae3799fc5f0f2f1908e54f1ab12d1b9f57a3b5dedaab46e843e43aa08cf6fe34c8b4bc48eda4cee7a9ea5526fe74b2a618d0e6a31da53b1bfe5f0bf44e1b1e63a8ba212eb652fa4dd2b0a14ad3f077888e9887fd070b8a64eb2cd46872c177b27c3ed5054624f679343b631e0d509eef5b870ae78bd2a59353d4d00501f995724e8c0c8cc407a83b4d55bac3e42068312d20033f16872a22fd699956911120a99984ad4a2dc25aeb36f1c2137517d81d966a1ea5620513b002191a75fa9c8eb9b24ba62a337fa866da2456213ed0ecc6c83533af76705154c29e7b8ba1c40a11f590f2300e4cf119c43aa5511511591c569457c39b6dbb4d41f4800a0e02f882e1d1d191ba1c1d3dd162af352125f534339035a613f2c10ef1a9be2564ad2b049432cbc453b6189025f3f625a4690d75dc283b112ee86829b500dd9370fff1e1582d4fb1e1808290990a8415e2c7916968214b6fb2c59d3f7e31a192e03163324fe2712030d8ae4a1104946fba7354b1aaf52f3fe630bec0588e7c10c4998b08d5b6c892e478b94bf9e218ad8870ebe50688f17f8653592889e4db530767412169da9864d6c765b2260974165b44134863d181b28645ff3873fc3d9802a907510a438718927d00eacbddf1924d4af0a57f50334d4e1a510a45fb8ddf3f517556e73104e2300628e5849ca878a2ce2ebd1a2cc82e672a4d92c7482a8074c8afe625f8ff6aa38a194d535cfdc2599c08dd8954587c434c89b2848fd272ae8721bc037ef2cce651ed14bb336ae2ab905b508a5fa196b227baca18f57140b2e534c810bfd3db5a1a17560c7a1ca6074cd048c078a72ad7606930f3628f454f3e3cb012422b0281a302a4cd9a1d04511be2ca7be8e72e2a20131289690916372b0a7aaa00842666250830c94f9c7fd694a2c09c1777e2b18cd137e0f6f593d871bd84cc2aff001e655938219cd832572828a47e69781ceb9b8090cbe9291989804964f7ec0dfcbfc5e3f9be71c97568491ae148a372f9ca520b9cc6bc82438b9872d75460fdf4ddc3ca0065ecb792ef9b0e210c9bb6e2e7b330e8bdfad81996c14189a4d6289a86e93821025765762f0220fb0ef4dd5e649421ea160625696044de8d22f17a7a77035b64b6a858e7b30292006167d34ec3341b4ec86b6110e755de34f7ec02e5b6a48b18187223bf7c7fc13e5a314275e31b344a100a14e490f2f0cce9aa5503bb711544ec342404f42c45e16cb2efbba70f931cb8d513cbba6bcb5c97798bac79cb679a6736f9fa0f030d78f40d4b0de91e7094f8807a6bcb894ffc1d7fbbf7d7125616e13b2c5dbcb9ff54885c14cfd4ce819f896f38cf0fd341edd0bffc31ec104e408d59ed0bfbdbd9cf9129609e969a044929af7e1492dfdcb5f9cbb0b55b4b89bc317134babe2d0ee98b426400c9ff917bac162f4b892b175848c0280b15039294532f4b6ca855e82046152bde1a2997f202ff298c30e06975cb102a7796e42adfadad632f4009ab8f20a0963a4a8b4a7a653626c9e92616e0118ca38a164dde4bdf59c9c5b38cd575870ad84167712833ca16eadb60606bd709b622ec87c45ebe91439985c5f00b2965e2a66a2f69131023a21cfd286b754aa3ee99230b04a0119023023059961c939e7b1009f16dcfe0307f2570209ed318127e152ca9048d8a038315af60f223cf90f9922c86fe9bb5d6f68d5ca935dc539c6eac4404a51841b3f60431a2c2f417e4d7c2d29899a65b90f616a159a209f7785468777de82c1010781509aa9a9ef2d2ab4da81d3517e626f41f05d53fad25f27309c6a42349573483eb72817202846bd52d2251d29818092634ea99e01976226f88e5cd28eee2cc7e5f56945c5d634ee76052fffe0aaa98814e2a9efde48b6738c05b6f60c44e324d4bacacffe6f22cd5eb0d90f44ba62933b973fdff060c591d2d97c4c348d504aeec748da9d0a22a350ca8ee3babefacd907b4d86883021625d703006e39776e7faa05d707fd7322ed523437f00afd4ea1e24225b2ad9131364150db1c17e5d9ea1b78871190d18ccd2dcfd40f3cc2c714ae310553d2c5168f0af9f045f16b5ec9832e3608edfa52a3ef41ed8002532e57b30745b721c58bf1418ed9c12d54af6d2aa5ffb7627d0c4fba8eb5ca08de1d01b3533cf432e954dc012e2cd8701954a145078393242a17c05976003be6dec6912ce1b47fb4adf1851921cdcf305d0ea608e53c882435036daf1b975dc742c91f9176dba44d4219321aa56eb7ad548b2da430223842894bd8e70503e2b8d3bc18a2846099c6242bfa1c25a0c22fbcf86a426d33ccb2998920c3ce010ac0033919b8de3a15cd6382409e5e0408c4795d27ded8d801b863e564a8bd83f804b15af5907faf23febfecc481a74a331e86ad982f3c47ade07c2e810f8719d22a4b09de5b0ed690e6ef212cfb0e966f0c990b130503b2cfb815ae6d818a4812e72632ddcc6201b25dd18043994e503b1c7161ba16cd47d34e7bc027237a45e78da37304383dcd87beceaeb658c0520873d8508548095c7d732c940d6f8afcc70a93ea3bcada49a8ceb93b839d4a221e3979babd5bdeceb42e18434b65fde67836e181a50c95f5ba3a80fa3ca0b43b7f808bfc8017f7f5e820ace6b01a72cba7d8ef6d0b8332c79bde3dd38875446bc46cc9ee9af57d584f914dce519547e995b32b65c780d7c63337f2713970ec9371c4a40d345ae10541e71d04e84e377719c435e0c5aabe7dd8604a960a74f39fe6e7b1c820ee8d218627ea5453d3c1d332f2170997dec081a8848ccec173043fe990119e84b3743f31be993b69c087cd53a3b35fe4356bf13dba386888add28754089e08d505c051c400f6a11127a7b7ab026f7a6ab2cba6f1e777433e05b0784cad07c5bfbdd4971b20e4659f6b46b268008ae1d3a0f90eb9b9888c0af1ab3fe0550f1b0bf22efa2930cf684241c3c0479dcbc0416896eb211b709747287dbf0dbdaa9a383637330caeae9b92dacbcb046eea166d50053c40e342b5a6e80eec5ed7474e0c2c0e192ea409ea1e264406f65fc4e18ef25ca85098039f081517131aeb603f2c10b33542292bd4a195c70e571741e41dc2eee4d2b9a353112a04f5b3ecb54491312bda0d3144a37019ea7b8d129154c919f363d712f13433a51204cf9ac82b2b34b46865c79386a655ef4000c65e8030cffce26913ba4030e01770ad4fe0ac378fde44b766c4c4490c393c93c0203b0ee8202ba08a4890b0156520dd8a0dd17fa8424f6fa2e3886a9b20e0f3e846922d36d1c09a278d085bd12fddd5089b1b346485372b2bd8f45cb54af03343c0b1d1d7ddc92c61988ff0e035facc50c7160c0b29078e39bba70eacc2e80b162091ff83829ccefccacd21965249aec3b79b17fcfa7238e5eb6a1d532b80744be387c37578a353f3db4c63398d46553a9ef66329b7b4a7da0e7b27d3bc31acfd751d45517cc67616d408eb2e959b51037c6a69565e7f1e72288bd1c644f87d131bbb6a121285defc85d0d199c6e682ddc330bfaa21446dcfcb8acc829fe3b5a38438745e30e88d3e9328f19fa39cd2566e61d9e83bc2666e5796627dc234cd6fed79efa2eccfff6c7fd8d7525fcc5c96ad801c9ce91761b69eb7b1fe1e0a9fc17d9a71718d48c20b4b905992aff0838cb861f75eee65a7a5c38822d083fc770ec0ff1898289287d84016c11ebfb21aca281efd38afab7e50109cb48d4d6bcf36b717e68f8a586f4bf78c38a8bc9de630fc37ba7b407d3b8db7b78384ed2b6e97400f08d9dea0d8599857048f3b8004521d5a9a8c13c83a3bd7d67dc59d67b4100b9898279af4754fa6a7adaa15a8693713062cb8bd4a5fc65c161793384aa960cebd13475324cdda6412340dbf0a15577dbb3939fed6b0a9d827fce4ba1896c2a9c9d6a1d431f67576b4519f0b8ebbef764743f38bc421cec12cb2339b2e07cd6b3c4d73195a3f96daf9183dcb78735168aaf62c97d82d02ec1c8d2c9e8de895b320037be362b041d0e724d94fd83d83c154bed500b0413bf3ca5b561be606327525e1e7613c6cb7a494843efd5ac155628fb63e9f8dab7cdc0507697fa8ae5fd52539efbbf812db00588f0f843ccb6f21d7f79cabfb3e841779b262c928b893d848faa0fd7dc47146a88e9e843eb40f3f2422e7a51f4c1a77410272c038396101dc06583d57e9bfca2e32bd9d7aea0c54ef08b6fbf95104baf7d07e2c51640124bc51118cb80ee5a19df8b23983ef7eead29406b4b7524743e52e9a1d0b707288b2944993456c52704a2b869137fee9052df818fecd8bbfe9efd5c3e0cf3b65a42f89a2785687c11b2491a3ccb30a6c7dd00bf2cc2a192716909e3284defcc59d7392e52d9c62373f8ce3eb1020b63b917d1d656c1ce9df623db0007bccaa7aff39d11dbe6f62503c49256a2da52afeff49d8b9f3de696bf4cdbb65a0e30f9c8db43a929bfb96bd7d9229f457603fd89662f72d40492707c248291f50f69285e37105b0aa22a6a137a86b93e5cc0c1912bdfb07b3a0f1848d39aae756b1d22fcadb987ad92175129d2c0bd70bc48a2fb9c2390ba7d860d85d7bfd3087616cc2e52ebe20b7dd2b93bd63ff0335765cc09ae300242d3bf0c48b599718ecd5aa0f382d5a6ac31af9a9a13d50f18e62d1aab85f64771bf9428edf79b3905953e3ee78fdd7c186a666045fd24f8204de54fa0a4a7dc96209dc467d83883957b2b3e425230721e8c8167d7071df6d6eb173341868aff01f151de97b95a6cbec97e5b2ca2a29705afc69723b334cfe8bb222c6856a1bd24777ba30d95111e15f6bec3bf952f3ea12b365b4b665314cfa11050952478043cc4c01bace3a7d09f0a624c2b0084a797e99ff806a49b859d1eb0c50719107da49548fd270e8e28adedc9eb5c30b2d27308906111cae34fb0229854df00a98392e55e20cd75bb16e2c240b91c8754a3977b9cb8ec728f369416c8606b242acbc21726b3d610c223957d61421c8087bab5032b4b1a5d184619b7a1a9aec7069928c47322d8573d8478223c7dcd6debfc464592ead68f065368f9d84352001ce3dc9c2f1698cad121ff6db4baf54e216e2ea344059e9ecbe362e468056729a506f104eb04b83f94802d4278c851b18442d8aea54ab20f0624439f90d559f89a53bd9a399ee19c4b7bd6f3e3b9a8600ce16191662309047b4f4651a7804bd16f49215654c7f3f142a499a54c3dbfd36a3987fc23842f88c5c129b92a3a9ec8c175a73abfd3d40f979a4ab3b03d80c4e685955955f2dce67df13af5c4f9de74bdd8328a7ae6683949d31c9819f4f6449cf28b596149ce254e11fb5a0ce00e2130f5a0e4b2e63a2475cfb7c2649d147080d5f7b660cc381b67c964d54901594bf2962def2b7431c3e020a7bd511425d7c01e0a3bc1e3ad989ec24a0d41f47c7cd72b0abba3e7ec378c340e34649c4a276c20ab7657a47e4fac5d2f54eb6f32aed71164144bf2db4afc99f62604dc880ec2f5a476ae37c1ca0cc685ad7f26e1fe14d44885c6c9111b5524d9fdde4e41eacee319a1632aba29c2c42ef3f5939a219ab8a54567feafc94f9a752b8f61fd2abc8ad9aa7ea3a0ec602fa6c9b5443b45e1db898857202da641265c0f5a0e945620c6b9e4f208140807e8843105cadcede212a686b58e7e3102cc15ba6fb4c1824fa15eab43077757b72c5f923b0766142c4a2a5cc448fec48b123f8ec7ec5b472d2e29b7a541335980abeed5b77d45f30573c5100ac6af007d3401aa789f51df6a9e16f68c8815e2ef69ac8e48dd110cf0df1e40f73a5f5c9002ecfd1ed89bc4aeb30181f92d9f22b28ac95eaafa460f6f549bec3abfbb749092448a3f1825f182eb6c1d7c2eb0b610a728339d58e450e632ff8b2735a406949000f5906e9e903cda34e4630f032d6e8e469e3fe41356d5f1a16649728bf1fc3ceafb29963df8347da3a522916ef51dab73c9ed33c35bd824c113e346357fc47fd916c6444cf7cf451ddcdefc3481de46484524d28da7822243f0a71ca9d9faa1765393d062fd1dd04f1fb2998a849e0464d024ae56708ab6a51612ecb91be9aacc7aa7093a1654edc710447271811912262936ec58ec2ef769249a93070cb1902e7e572a79af5a11d0afc5d0e53750d21b3a3dd8e773f53204bfcf800ac1309bec996082b8d8da9d90753a945070b364e28ef3400f1cffa75944a870fccc235909840d16d0fb97f1a27840159816e137dbe019100d0aa4b93342f70c3352c6a9954171fc38d1cf322e2887a7aa447b7236ef21b631a6e35309ad1e8b8c70defcb9a3e52bc9dbd7a3e87214ba59addd7437349123218900065fe70d7d3dcd63d739fae9d286ea79c5a4734681907fc08d5ca15cb4f104b217fb78f3a0e149d41197461739c6cb433838948fd8b697c6f4c58cfa692d9283dd592fdcd670692efc3d22b10cc5c8d319cc1751f5c7a7951cf087057b0cbf9027b77e9a3edee6c61c528cc066e2a769e68aac43020e1c2b690ae284425efcc55dbd005daf82b4e97af101806b048900556edbe69a4e5d899a5ac71164b4027a28e66d56205ed5380791bd0b3d86f1ece87c6feeb986c51be6f5c6738a217e4c5ba88d444eef6860a728e8e84120fe1ee1b8072c5e44ebb98c8ee6fb307166676d2c22e901628a451665ffda515f652dd4444448681329539201bc06b806c506dd2e64bfb8a9fdf735496fe943dbcdc5492fed85fceff7683fe97753f3682eca4fd98d03078e8cc3ad9bdb69eb19a2f9e6ee613b208bb0c7b66ddbb659efad6e5bf9f6356badfdd3c4610f0ed792e5f15cc79f4261854d3e3f6bafbd108d3ae9d12c7d24ee89c6f18e131f3db19e3a692c0f81cada13f1d9c9b3ad15349a1532595343d6a0d84292acbd0d1c9d9bc50923f7d3a629fdade5c89ae6a2d6c6c9df7092a63d106aa259ebfa5a3d837bf7debdbf155cb0caf73799ec7fbda0246ff6ec1c5ef7cec9ad0c490fc98d24c24d6a849b74e68517b097278e4c57fe8a09ff44213145e1906317f059b7ef3f32b660af87f8232aaaf458e930373dc3601c25c392b849ed4b8aa654814f3b33a47b252cccb752f8fdd5c649a775e53b93dbc65858cc4932d4f4056a02b507c1f26cebf2b79c2bfce1dfb97bfb82a9b2aac4515c52a45c8997429b6271948af2ece10f355b178c936c9972b35f4eeaa889063445292dbb22ddff5073f8f5487571d20dcb8a7231051ea244674b6361a314d89fe6d3523be3e20a9842a1855766e18aa4eacb9116234550727d71e1246cb3b87992fdc5cdb6299ad231b8eb1ee2df7dd738c9869ae2404ddc7fcff9f99567db9920fcbb3be3a67ff796c68e5cc07626096b699c14f212883225b32f2d270345e957c38503a6c3b882dcfdb588f75951b525776571989362423fea4ca571d209e676b98984ec969ba9997e19a0b1833b5ad37f2fc6e3d8d5ec1a62c0a111d8bc7d08c4bc05b9bf9523e810109dbbefdc95433cc4bdbde5e6d5c5494b6ea4ab5b1d79b6eb3135d1ace3de7befc5a1430af8e628829628a594d2710b38e4c3cd94a6f8dfa76110bf43423fb8f79e2bb3e7f7a46dc8fd7649eebf626b100ce1a6975a8aa620e126e866b7e89e9aec154a70b97b1c6efa56fa0301e2a67f3681b879836fc901276967e727c20812ec2b506e76d7b1e1abd55951b9bfa25a9c14f26a1167f910a8a93a41534821dc7ca9a84eb9f93f8e18a3eeed54a7fac4ab56691835e880ed73ba570e7bd8905c41f84ca08047b8ea531054a5fba62277ffad80d094ae3c82c2c2a761197e476e650956b2872927727f48ba6665562ce8e61cbeaffbdb2975a7347461fa5fb594524a4ffca99fb8d9cee9dfe14d29652277ed82cb4daeffddd784ebb70f9a6be852a0a213aed2700128a8dda8b18a9b446c265329640a45cc48fe462ce0a671d3ff73e18ee5246c1538095b630d77865b7192e556ee4c9dd14a4da3d9cb0616a8a0df91b400bbd99b6fbef9e6db167a9db68538df7f3fc4298130ff0bb7cddbb66edb2e696fe76d9bffb6f9e6dffeddeffeeda1effda9e926e7d66b98016b6fcd0dc50215785f3deebf16e07e1fb5742fc65ce5ba7c6b90016fdf0f5a013ecdfca1f0eb7f1f0a4b20cc5cbf4deeb95b72a6e9719d1337ef9b366e5013f85ca603c8dcdbafe18ffade91ec793f82ce60692354bfff52abd530c30967cbf0d193fa9f29a9fb5a3f77a66125bfffaa403284902a5eb2bf101e0af13df8346cc0f760f915c5858c5da8836183b196b0d205426edca8e54961d73538787a54ba6ddb56378eb3b01ad84cde018b65ff1e7a803fb4c708d8830477ef51020e7f7824e5615f70019f98ab3c7ea8bf715bcd4778b266c926aee7052ef7190de6f34401db3448518314e4fea6a1260acbdd2b9ab55ce966f70aedb1513d43bd888ad25f87190ffd3d63b59354cb7215aa3ec17622b95d95fb35eb4d8622a889079ad2f78851071b75d37dbb9aa66d7f33e78279e5feee46f50a4da2b3db1450937f27e1f9ec9516034dc9d1ffc1b8ab0977d55257368da3f4b792dcdd049f45d492094ad07d0b9a47f7fd4bd452f7fd4620d1dd0ea03cba9e999959e919176dda7c1887821b6ca18819e83007334c01051f8d0441a5821715d82ce1e1da18827069d720c17a40858cca9ca27c9e482145761ad95d8a37649f620a2a84605a23d504b01d94739d58ceaffb65b28429587083832b9a1859c1861fec6085652587203cb169622045a37b4c12186487e20a65b8821523ec48178527dc937d1f9284123637b1128a000fad44813cb4c71585e6d00f31d717731324f09b8f9e9cd6591deb42b3665a8508d4d4651d03d9047cda54cba62c13583665533635f32d86afacba5287f0604705d6deceb6ef592d89bfbdff8092ec0ba8b9fd269675e6cf5b9e367ac6a2c45eb9e16dfbb6289ab23df641c36a29140111bff894078dcdc86a44d2042a10bff3980713e2774a1c0a11cb5b518a10a26e6ebf6dd585a26c7f591b6bdbb66ddbb66ddbb66ddb72ecf021648822c826e06dfb8bc7dfdebc41fbe491d5120a786c2f9228e06151d434536b36eb0a999023dfaeebbaaeebbeeffb6074e0c4b805ee93c267c58661bcd87d0d8a191a0e17ec07ddc87abea8db7251596bf1fc714432b24b042c391927b39cec1209b9939e37918888aa25339489898564cfb8a93d00aa6df112f77a1d79bd648e4592396f574eaaf19cb73ca0278ee6e44feb83cc796b849a663ce7ed8a973815caa6aceac64925137ae25e5ee830a32652c64bdb6c46335332b38939e9b5404f1c8b1531d424a3e5a52d1673c55e31d88b936220819e361b9b24b850d3e8395f59bcb4b55ad548ab85237ace5719d0d31613536790395f8b5053a8c64b9b6ab6a9ea4a452971120c34d0d3f6f2a286cc0125f192369bc5cc6466342f278d54a0a78d6563a9020c35bd6079498bc55c622d8e8be7bcff809e341b9b2064cefbca8978496bb5bc48cb595a4632a736a1272d26e68accf9bea126cf73be67bca45d194735cb9c165ca027ede5c50b316a6281c44b7636839925e1dce77c3b414f1a4bab684bc89cef176aea3ce75be5251b8bf52ac6e2601b1b64ce370de8c9da28e1257b6f88e4983664ce7b809e6ccc73387f04357d323cc379215eb2aa972b64ceffa027fbd22ada53983ad3c1791b5eaa33204ef29ef33ea8697bce9b4e02ffdc21739e53330d7ab22c27d4e4cff9eba51ae37cd353b56915ed399c8711f6e094ae5ce345f3344aeef5e25e27254dd6de678825a7e25e424e15722a0ea7e25eb2f62092f3338e85330b67e18c63c9da8f32c218311b4e8c130b633659fb172350b4b562c2adc5d95a9cad1593b57711fa606caaed25dc54e1a6e26caa0fc81b671b0b6716cec299e745e72266c389716261cc266bdfe273abd68aa95a8bd3d25a5a4cd69e85876ba1a9b4174ea9a95eb2f61d16db9d692cb3d94c63c9dae38e964fae6563d6a6b4b1988d599bacbd68836d3eb9966dc5d8d6b5ad6a5b31e277863659b351d997115bd50b67ab619d59165c6796256bff919e3f10df9ad04137352d94d9e135564b5766d3a74fc98863366428e3a6f622f9d9c86ae99b5514ad4f9f1ba88af6dacb4699ec83034df99464edc1af060ed8fe29ba602aba685ccb496708e35c4e3a41cf1c122791212c8cf1d20eaf7d2813ce84344e72d023b55c5bb514c22a0aac4fb855b4ff6a2a8af6adb0b62a2c6b3555a5cada7376f82f243b8364a51c35b78fe20ebfd0226f7f86b1ac81e10f9f37106c93e6286468e328da7f6458e3a616c2605f4d08fb6ac61a6cc067133b3cf7dc87332779de957a1aa73e6973479ae40ee4483e7962baa9cd84b05af2980fd36a8d357c359f8d97aaaaa2d480af7c7a6ab2f65555eb2a6ba530c651b4d74e702b43989bda87a587208a8fa39dbb0450c003f4b02b3d6e7edfd3e0edb5fffe9df4c39b4eeafefb1327d1fc7defe37b27bfeffb93af1c42617e86584c0d4abca79ee739193ee769cbb8e979b7a8c9dfebca56e2057cdec8de075a254d7fd3c6aa793fa20177ef7fde905d4b7e5fc9245a23cfeb7db74cf69eb3df47dcf464aeca5ebbbcb8029f2dcb2df3301ebdbfe1a41f680e1c3a56dd92bd4e654f95bddf414d45bcf7be514ef29e035eb23c3caa247b36d9bbc95e917cde3f6f14d9c3d97a940bd97b7ae2752da8012a1653a286a66eed0df3c65851ec3d41465094ee87e2062cb95a2b84566fd0147b82cc019a621fc482cf1b481c11a303f42432829a9c7436cad69aedff8946453646b6296d0e1f3d49c2b6c7cd0d879b1ba6299b6fd8b7c61c28c2cded417f76aa53f5b76fd59093ca20514b3b8ee8003d11610435dddfc6dff076e2ef56226ee4deb61660c85bb6218661bbb7420c5c995a2106dbbd9c095f1f949a6aafb437d012ede8e8057cbbd10bf836129c2bf79ff81c198a25fd069f72ca236e5eae51df459cc4291bc9d768c0e08f639f9eb513f56ed194fba30c83cf292fa74bc23679dedc3191067c76abeb9525dcbcff820eb85bd7311ec76fe54bb3b54838693ba90d11b76479dc1772af9f9d5347be49b220d33543f868f67c72d2264de9cf6777a634c5a96da7b561ce8269144fbc3cc1ca348a2754997e9869144fc0e08995dc0ef844ea258d7b8a5237b2a2b47b576afd87bce75a7e7dcb23261623138ba19969f75aabb55a6bdbc671f776dddd3097077e2018861c8e685353040ec75173e0b8c13992e98bacec2d4d9efee2e22b37e9dbcc6c6e663635302f98242f9818988ba555cdd2465ad52c5aa7babb88eef6f6eeeef6eeeeeeeeeeeeeeeeeeeeeeeeeeee6eefeef6fe5c4ee847eef214810a918f7a7fc00d3154922f5021239344c6dae8b0fe57cf2f53a7a10ffdb53cbf0f73f8c8fe4d6d0757f3be4651fabdcf8896f053dab704bffbd8f36858620f79448e4cdf87b6048b1434ca49ae9dddf9731b797b2bc080a6d5ad462244082ad35dab71f76e9cd08f7ccbf30458bb17634debbedf435d0984d93e4e7e1c35edfc84b4d42b15052cf188044fbb04ad52ffe432b97870e588dd52795e5279620394321ce5e9995b29a24f4c68ad5ada6215a54f4bb44afd02a40890ca0348dd5029cf110ba03c714a69a33c3d6f2eb54f4ab4ca863aa24f48b44afd0094330028514ab33c7d87f21c6b9434ca93f2f4fc7dab237dd266be953673842449922449f2699092244fbefc9392249f2449b2243b9324f9f5b519922449f24f6d0c211a7f42e3a4a42cd68cffffff2749f249f2618441c8ff194ffe0cf2ffffff67909dffffffff43274f9e90e569593348cfa5976af1525e2ad51263341a8d46a3d1ffffff081406f9d188fc7ff247a31f8d46a31864e7d168341a8d46a310f94f7e795a22b2954c265bc9108944229148341a8d7e34128d61909148f43ffa1f95676791482412c9203b8b44229148241285fe473f2acf3a2383f41ca37319e95c9dab73b511182010080402814422d18b44a0a7414420d0e8453f1295676710080402c1203b8340201008040281cad0e84523517956249d4ad5a93a55a75285c6711cc7710481400f028d2ec220a071143de845a071fc711cc710d9791cc7711cc731247a90a8f4c17355e5f60ca386a5a6a686c5c5e7f3f97c3e9f711c7f1c3f9f30c8f8f9807e7cd0f8f9fce7f3f974fe7c3e9fcfe7f309817e048de5e936b95fa9d72bf5c2e3f1783c1ecfe7f3f9cfc7e309837c3c9ef13f3f7e3c9ef7783c9ece1e8fc7e3f1783ca1f13fe3a7f4ece2ae8adcd55ddd5511169d4ea7d3e9743c1ecf7b3c9d1661104fa7f379cf7f3c9dce773a9d4ea7d3e9743a9d4ee8f39e8fa73cfdc80c359bcd502d30c61863dce974bed3c12cc2201d8c3ddf794fa73c3be31263dc19638c31c6189721cf773c9df274540bd2330b1811180cd644386118866118628c1fe3b01306c161d879fc1d5c9e9dc392866198c3300cc3300c439dc79dd2076f1a8ed52b1c8b63712c1104411004c1300c431087414210c41f3e0e41f041100441100441100443f8431c969e392b4c702bdcca8dd7755dd7751d08820f829d180601bb2e7cf043b03cbbebbaaeebbaaeeb42e18321589e6d24b6442c169b7df7de7befedbaaebb9c3048772ff8dd83ddbdb7f3bdf7de7b6f08fc0e2c7d68cf1ee9f9db5c94707171b1d9acb5d65a7befb54f835c6badb5d65a6badb5b60c757fbbd2076f542d792d24faf4ad5aa57efd11636f43e5ea9ca6699aa669d65aab816110ab69b7354de3bc9686b53264df963e78be4153da73083c117927483f2070ae17ec5b99dfbdedde64088368e52d69d6dc673c27e4fae31cb0bfd75a9dc0727d7140a8e1067c9a59048e265ee5faad2a0a777360d7f87a98d7d27297bcd6894cc0aa961440516045c8f50ab9abf6c9003b5c00cd5ed670033ebdd6c609d9ac95e709c00c741bb9592287254c983061c2840913264c983059b264c99225b3d96c369bcd66b3d96cb664c992252c2db432cefed5d6fa7f694aa56e3a16023aad1ec57775d0dbcbdbdf52c0fdf53618b2d5e8a9655ac5a47d7797fdf57e9f9e3c76cbf4899c41536ccb64fbf4c737e0fabd622d758de2fa5ba5a6c689b4b7323451b6eef5737538b457ab6ddcedbc0f0c39a22bdbfc7558b4f07c5cbc18413042a2510c1964f536416a6f502186f012caab543af0c332c5515b1545a3a70a8b6915ede97fb515d327d10557198e513509aa6e663634457b8ffc6a680ab81207aa22ba80a925a20bf7e25e5c122f018006872bf76a15ed31ae2a90e59b7d3637fe2a92b52792b53f392b64ed472de0f3cabe9aae64bef69f09a28028e10f941cc670a0476ed9c93016c6e0aa92f1b08aaa5242136681a694216c84c3776557c9ad7192ff2c8485316e8661cd8837500b7f7090c551526ee252258a335e146b88345efc5eafd74b3c79f1937d32b17cf163b1582c51c68b1fcc0723922f7e2894288affe2d7d222c678d193c9c4d08b1f1171f4a2f79abd44d18b1e0d8d08e3458f15c3125fbce8c1c088a0173d94288e2f7a2d2da28b173b994c26133f2f7a444451ec5eaf97d8bd44f0c58e86466cf162c762b158228b173b9856d160c47eb14355140d258a1d4abc2f8a5d8b785fafd78b467c0fd0d3a5e91435d5171f0927755efc23a809bf2863b1c417e2a5cbaa281a8c7861c48bc23a50e2dbf0d245017152f7e2fba026fb628b281365a24c14dfbb2de2f6222713bf172f11d15f7c94b0873883f36a581ae2c94bf69af17a992547c6c782f958323ed6c7da810cff437d2d1f4ac687fa505f4bd6be460c3024fb88c864b28f48d69ec6e813bd685ea1d78b266b7f02c37be1b160582c164cd67e06a81b3d54cbbff03c54e9e27e3a994744d6c9c44ee611c9da937100bb5747f312cb174dd6fe5b6c2c3a560703762c56c7ea60b2f6323a0e58ebbab1437528b16bb9326aade5c959e1acbc68eeebde57bdaf4bd389814511e6b22e8b552f4c5fc8421dea30451dea7b6dffaef63f77f7df02fe566c69898a62ff074ad35255755dc0f5abb5b38a626f5d9955d5bd2a15f8fda1e6d4d48921dbe7d40f6bada58d90f6fe34a479405af21845b13ff343fa576eda2e9de5a67d4a535d2eb0804f3bc4ed4a2de1b71a11270d69bbe224942d424de1dbb732a0268ef81c91538641f4773487fdb5c6ebacda5495911a974a558d780957979bd663b52bbb3a7392d7eaa29d56759ddf50fc7ee324d0491a2e6343acca4dcb6257462a6ccaaa685cc6672a114bc44ddb306edaee4ae3a6ad2e9a6287d457a2b3bf1dd255e5a495935aaa10a809fcf0418d3cc3f2ec4e1844bf776f5396c54dfb76bc02dbb7a958a84dfbe0dbaf3227d5922781a2d877c29d02be42b6ef599071ebd6ad9295935a6cf73677f5ed574f7d350cd92e77b12f033476705df1989bd6651cc5bebd799b5109769a6c7f46d61a65c015c649677d577d829abab7f5d5df39df3088f6ef1e41e71aaa7f86ea67e697dba6564553ec57224ef24a1bf55e8cab6b7499fed4b448b889ddb449d8acca76b42b3882f0afed55fb1dfd75d3fcf1ea8d8c27d7e32df893ce9640e5ed9970d26f7fb2d9c8e2ca48edf7406e2fe3e11cc08992b797126c9150e2d3484b4b1f4aeba6e7eda848282144dbb6af24e995d6ba694f1cd9e2a8ed9af7367bd65a6b2dd7f1b8e93d85225693ad10f7d210378aec57cffbbc75de7f5e577a2cb67f6f20051715b65a6badb5b6d67ac35a6badadd65a5bad1da7c0d65a6badb565b869d2a44993264d9a3469d2a44993264d9a34b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9a91f06b11676838f822bb97ef924f9ff3f1abd48f420d08fe37f3eeff17ca7f3187f183e087ed7fdbd6fed6bdaf749ad1e924d43c5aef1e5cd74b256f9569dac4f5eaa55ea8fa94ef6adbe227c2caf062f2ca55e0d725dd13f412f75837aa93e518e9ae7674554383d57aeaf6d21267b2e279ddfea5bb5782d0f06d4c89a6d6895581ed052bb08f1a71542b645b04e34e04f8b8464424b1d13f2a74d41b62fb03408e24fabe2b4404bbe02e4cffa855ccb50d7f0e3cf6a3342022d39cb477da246d1e3cf0afb92c37af8b3de20571e5421e43feb11d0fb6cc79fee86ec4c6a0a0ef0674589a8404b75a5e34f9722bb16fc0b3cfce9349f527de5f8d387901d09ce0403fce9aeb15471fce92cc805f8d365e03718c09f6ea453b237fe6c31a8fcd9686837e4140fed9896a2a960e3cf7e42f66f2768fcd92e3d841dfeec1f987fb60e427cf2671729ff6c16d026340ed9df032efea4b2d0532efce8c07ff107c87e1a207b4a76f72efbd3b8423cc0012e0d70111224762f1ec71f2b3e7ef081d583d543795e3c8e19b62301083880cf749c4c2b1e56f7e27134c00b47010830805a73434500aa14d5bd188fa30c13e371a4616527a972460c199a16d25c68ad17e5296364519e236e519e9873f1b779ad6ff545e16b7dac17e9b5648c783b6d4b26475eca3b82c7f2569e8a5c7999e7026f6556435699273d8f48cf1fc8158618ecd51275aa25b8b0bad58845447a06919ebf4f4d37830ed5adcc6ac6fb3ac34c0c06f224b93d7f48cf5fe7aea2d07261b13cb39b839bbaa8bbf221e2f1fc85b036c8666233b9595570b55c3a8d04939e436e85f301a7e2521c4a953bb6a4463613529bdc9e3b172a5eae56e7b5684a7d2678b126aa5845a1cade13b9d61fe980fd4f1b36688a467ab6a56da1a576d9c09ff68806feb42f1978bbba518497ef6f5768a96318f8d3a22ef0a74d59e04f7b8383089f21434bbe52813f2b0d05feac4a26f067b5c9fe35c7101ea3454bce92c09fd515813feb0b022e7ad72184cb78ffca424b0e7bc09fd58803feac2d0df8b31ed921c461d4d092cf82fce93306fc595782f077253d00f150125aaa2b20fef49805fce9323ffcf3eec3c79f5e05186a72c1a2a5fafae14f77f1e14f6ff570cf3b901efc7de54468a9d628e04f2fc2e34f67c9de7917b2c359ccd0925525e0cf9621e0cfae3980e377217438125ab2aed39f0d63fab393f0e0e1fb10ad5273f8f70b3571debf55b46465a53f7b45fab3590670f07138114ad09236803fe90d01fe6c22b90037fc3d404f45b44af7fe475093a7f2279509e04f3a93228496b45645a936fc7fd013075aa53eca9f1406007feac801781bb4a4cd54203ea8e933ffdc21ef30d2f0d78c68954a9b9e8e68953a863ddca596bed5064a0d941928cfb1fe8d3ef591226e640c94e7ad17282d80a34fcd2202c74a2d79a90a94e7a5c0ca04ca73ccd1a76e19a23cbd9349801501160474f4a95742e880d552e77a000cd6803eb51121e5e9b95305f119037c1684cf72fd1efad42a20e5e9f5d600b15ac0ea878f3e358b8fd7ab96eeeb87f2bc3ebc7a00e953a77aa835b574570aa8353c6a4d2ecf3117d9519e9eb9594549800a01aa0394e75851ad525f47797a7debea5313e15844f4a9570299fea824ec17cc92a6533403000000531600002810100704e27034c7025970ee0114000f739a4c6662381689a21c07328e10a4182104000300006000000030b40152e97d7a4370e32f9f1d3d6c682fba8500480b849c2ff026a53a8a0a8c2a391e6dd3131b55d4cb3e551991aa6fe5cecd0a4f0ddedc9be4350bfe3de5cca9a118039761228662c07104b15366d4164d94b17526025c70d7dda96120c1a5e9c47c8579db100509692711e56b39589f390d72bd34fe54f04a4e7a23c081e8ab28e644166482b587e5d10a6048164d268732575a1e5d13f686ad6a802bad6f9bbfb2bfe2e3eded775ca4d513debb9c4cc489cba90e022edbb8c1d62e78a738944b916863c0b16c9a81975df606192002fffe8798e5347d1480b05e109569d56ed34ed0685d564146f03793104f334e49696d1558c73019b87e207f81c93ca5b7ac5e0f647d7922cc80ac9c0820009b36f1f0fc9955f7406ea83f673d90f718cad0856d79bf76cc88e6dd4cd837e41fc10b7049dc578659f3eeb550363a02a8f69dc2c0578dca5b70675a074e21123ff7145a58e3c31be67e552821af9fc29de71c6d43986865f147ce2db7d60b2dd571a996359ed78e4dfc10b54d7ed2ed1e7d60cd5e8ac4ed882a589ec7d391f5d2c3d762045ff2b03996eea5001648fac9dfddb064929108d94f5fcdd3625445c0ed1ae8e05a6995fe583e52a039bd75d20d09a2a595832c724453f20c453568707cfb0d7f5987a361e80e8f35868ff43ba9728bea600207f4b78a305c518d31f11495aad962489e06a55ac751151a39575200c8318d8327ba84c2d5b78109100c53b457b394aa0e1e4f7894480a34c4f316d5f811e0f5e2b5eae8ef68b9c3b1d7806e3207ff34b02f36dca945958c507e9b37ed5eaa0518b971ffbb7fa48ee1318ce25ae944555edd7d9ea9c6a68ceab4980ccd16000b1805655431e300bbc88ac146dc7c0d68fe72db25c94fe5f07468b5a15dd723fb3134ecbf50ed8bea84e32af740dc0d5b91c4b7127b059a7d20c217d5372bc93bfb74b3cdd4e2bc1db3a1b9aa44632d4f6c0497c6b62d49004ecb768c9155bd04e2a823ce052422d0a75fcc2b3d78cb91575d6657d6b103d1ff25366f1d9dd4ec15669d145d29d9a5141ff9d668eb33ccf04a5ab727fffeca97dab61bb3731c2d2a44b70e94d40b7a06e3aa7c6c9e4799842d7a2c401a553078a18f4655f13626c6e1c821b3457c64061f29b7643097ed2a87977aba663f99f932a666ad3e6be40eb9748d02d10ae1a9c91220b134aa56bc0b58afc08d6b30d065358dead5eeb76f90d2ce9699071278375d1ca1afd1b0d89095416bab33b5f2aeed3d55fb11816aae18b270f95be194b811a6be138f5af9002e97afac65230f7393b9f6ad9c4f3123098c2901e2ef24b71a7685c62f92169d12270d5da2d949985fdbd38c5f247d147b418fa0bd36a459df0fb5bfc112a2b0470d77e79c0dbd8426dd30fb031ee6665aa27bc26994df7f9ca9ea352aab64794df90f9c2287cc840bf39a87b9828e42c21caf9b6b717cf3cbe4dc984fa4c03ab5a65a5320a278d91713ad42915944cd65992672cd9b2c7dc757331de0bacae0b7f4545b291be08532762be57b740bc49dfaa0890dff6dc1f7b6c4acf489a5034368780d315b23a54ace5fcc409aa135cd3b914156a14884234d73f184cf4041e4f91a5517afbbb449b7f698efaa97d5bbcb46c889827e7b550576b625f8e7de5f560b4e1c5006ba925c67853444568444d1392d529022ced76205b5210f5164f5a92ba45d4d9c62b567908bb3e3d08ae309c359585caf7a4ee3ac314e0a3418d4d948775f313c48c764a807868a076d842d624c5815198a77ce3e09009c3452726f34252b42c9d29be2c40846dd862612b0936fa6a0c79e1ccb3a330b03e91b5aa58d594a4982a59fac6ca9deec59d3e7598d043dd46d9644fe9e9f7234909afc5d3462ada3ca546b35e41c21f698619d1db3187066a0df6b85c85503501525e5148e798e7c8d047d6eb7589b71116ee9edcd08c133ff9e63e34cef18b87021d9976c3d6ef8467abbed95ba3ecd2e3d985e72e98c362a7731db854906568475375b5b9e550cedd61e191be0bca6fdb25d86029f49b9898374ed107d2cd6f5282854364eb99bce32a8f118a56b5103373a288c14ea1dfb7855d7923cc4589959eab70fbe3b9130639d8e5fa6e34d3032819c87334aaa7b291d67e0a204bd2669808ed969820564eb5c05617b904664b32eabc288a20b23b6ae8ee417ae0c41b470725cbd859f854d92727905e617f8e8845c3a5a891cd9652b7fc75c7af8e4efa83f4daf2fde1cea56d53f668490c8c89301990181a17ba69c156f90ee4474eb1a8074901408723737f4017e2e2ea38b3a63d6f5a77356dc67f1b1acb266c059b133a8ea26a7feb1d08a0b41891eefe0fe840e801bbc14406c0c7a806eb1120062e3e800dcc69402c68ea107e0062fa5dfbde3809e31ea4941cf18e84d516f0c7aa7406f8c7aa4a027067a53d40b06bd28d01b46bd28e86d89ea45950179851b5ae3f69c237c6247687b7fa33ce687f160028d1020a6fcf5c9d27f51d43d9d43a3d310c448ba38bf5c5193a2dbb8528a188989ce1130e99861313c86acd540ca0d59d15cf06ffa0e3d5e57d447c7f0690478b99a103545c0107f8b82d13b1084547c4303904e43fe08d6964447a7724bb2832d0a62e54816f72e8f689e869608052e4c3cc4d32a7cef59d2821e09813b073ae1f2c69047f6d6486c5d4cefab39ff1681cd70de79530d254ecc4567bf786a2a7fae55af909fa71e19343f6c4c11e2a0895ae22b773481ab4b98ae4f84e3ded40d824b2e9318bf214824a9e227b20a5d05ba27bdb8c15b25c79a973cc72b456dfba64170c8b1006f6b24cffbb91552aea1e7c275a4b68753bb9fff7d45a66e867534ca299c2e7c30855d494b4f2ce0b52621bafcf7fc29f5efcd0a898d01398d3785c70e715f3fef671c3e053f8de614fec6f5f2469ccef158943eae59730f1095c14e6c877ce9fb7cfcb146a47fae82243e66a200266922a9436d278944423e52e5c3479eb19e3245f856b681478f82923984ecc1bce7050bc93c2a9f5078c3f01f90abb7b290e147990e15305c8f7647a727410bb5a28b677a1351ef227dad7a60aed8dd42861f6583d10a54f3a770cfc602eb5c76f0a3628e021806f180b01ae185ac3cca1ad28a88c916e0a379c1d317927954cea4c5afc9e8bd080844c7e736fa1732fe28cb4aabaa9319410d1abdc217402a58f61d094fe2508569564ca4c4d63389d95f31e31f522c7c4d2335464da3c2bb51b95364512ae9481965ca5533c2f7bfe69da811b4e14d239a46e8be944a928212a27a773ca2228b33dd5a2846f83b4b2db5f87c277ab3f41f9da2fdb99266f003a87ad53190d2d3e45de63abb7cbbba0832cadc0d3ebbf0dd320e302edd8a2e751147296d51f40d87ef4e6c293c6175d66fa2c08c68d051bb2951bc042d8e73ad1bd6cfc01af71995d577519194e5e8d7cfb8c50ad6ba3125b7fa9e3bf525a73ce81b5177de5ece0d7087f85430788a80b2e156c8aa12c71484fdeb15d8b955dc5557bd73fe66f392baffad536e007a7870b22a54b7a8fb7cf56266401dc25bc1a12942ca865b41af36f388cf7b676787c6d19ae6f4d939be8abedcd92583ea181592372cb901ec81c10bab54fda6eef9ab9799037608210b864c152a1bd7896d56e499d67cb41c3b5e5e5a27fd787d22fa8f87ccc1daaf7d3c4d1e7bc7d652dbfc783fe7fd1fef1b777759375a2736b66268b7d2f58beb7e2ec7053bd14047e16ab9cff97567d2782a32d5de236bed2c592deed796292e077bae3572582cc111378065bbcc408b15cc695657f3f5e7b013d13abf59717df0f1e04bb39cfd5169b95af86681ae061ef7bae329ecd20642bd1e3a5d6f7f172433a419166650e3208ca4001422408d0e959cc1ce3c6c140fda5740796ceb0b316d28ef3fafd89f79594318889afcaa192c350fbcff6d26188114e90f3f20a7490ef19e11da9eacdc82ccc1bb8cfca0fe1353c4ffc590cc71def3e29d01da7eef045908deafc31a0523fa22fcf30fc4e93c1fd576ca944661964878c817559d978f20ca033114297fdc2a8cba56c73ccf68e40e45b75bc2dfad446637b6f329481e21fe796f66ea4af906d92490c23952bbc3efe8480091af87fe5cc32e47c77c675efeb313e496cfb4b8a83609a1275fc94b0f7010efed6d1b40554a2ae6b3dfae5b4e3a19aa51330c8b25ca585480d0dc1a186476f59dec7245cd10b2bc98e243d135e875c843e91ab4efae7ae2616e8decffe0ab38147e20da81c146346aaffb022a0e9cee6dad14261527b4918dd87a62351131426a910a082bf2f107b45169d02389d8bea9cf24d66b97152028bebaae4d53409d84bbd817b98ddaa43d9660c10d41e6a1b7398d32849928de6c95cf0208e14ea8946e0027e215296f41994415d16e2209501421b5703e11b77d3b79c30122df1bbd611882a9be17bf1bd4be208ea25df42f3c23dac6088bc97486260c4dae76bc058e45f3306c168e8270621f45d909a7182a398ee826cf0f86f536f71fd364b0c13c51a90c950738d52af2e07af4fc7d2d88c0089904c9248c667f23e113c611a931251aae7cef5878372ae65e55e6f3047aed0503b2b2571e0f677849b37354c1459e0dd29d9a15acf3a2c9c91d9a4df5f7692bfd0930dcc4d1213ed0df282a2767d9430ef004dcca91600adb8ce3f09e5aae2a4fc3948b9290a6aa21d9f49137fe9670516b3141079995e11b7e9caa219fa1abb2972113366b55b18a309e8e32b088c5950cbe0f8ab8c7f670bb1e2cf06198ec9a46b8122e01c19b34750a047c5baf342d9c53b86970d08d35a49a0f5ad71b2e19f6f89c1e28ab4508bcd76b02f0e80e8759180dc60d8a07512ec8bf5ead2b58ad17f58ec1e109835c1f1bdccd7d3a309ab52bb0595de029c78a683f973c4325f9a0ab88c05d307c5491f5f8f51ad5842c6b6c759afc006dfba045cf19d5837c6ae2eec1b395396f797c37c5adfeb0d8b08c270924295565eabd1dca7afa2c7281c4df55c664f1b3fa6482bc6776326788e6fe2049d921709156a0c5e33e8a3a6524a3e452d01c4a6d1544a928b04a9d04c3d2998425550b5bfae82ead91f1fadb2cf1962bb2811811ff4f5f32d07f9164c07eef78d696dae4e44077f9b744bd68640bd23c9ebea162bd882cc655ba0f60c52f6c86682d4cd7eb0ef90a732c1d615278e99fab2e4bba609f5e3f773ce6bd6d2d48a0bf3ccdf6bed77d14fa4d554dc158a618ae228792a05a44fd92e1c76bd288b3ea4ce46f305423e6dd98afe78e5e8df915281aff97a9984fa7d0de93d62c55154c8f0b2dcb04439a0a1a2ab31b52d0a0a38295bda3c20b69145ade6025d6555c25f90fd6452773a8c16d46293e54fc733640bf300181e0915fd73ff8a95e484a78bcf43550deaed17ca7513d81aae33948ba18779fc7801cf536873a5b51ea719ca97e6ca9dfa05080dd77c116c827d8b30245b47e25557ae5fd9750a1bbd36fd7683b3f9e7c1cc6e300df76fb2f8b7f5836f0ecbdac3a4ef321fed9037b88bd4b5014576ff2255f5e7f102e7bf7fd7718b5c3e2c8cffb44cbf01e26aeeaf1e9b9599b95fe44ffd582fc664b7750247205708cd7d47cfb975de2114d2045bceb5003e6f5849a08cc5593a614acf7bf2c9e161c453575c357cf05bac01e01a897152851a721a2000a442ff29d3c6fffb24b3ca209a488771d6a8067a8ff3246aae1301826fd2dd6a4bfe186a2fb38e50ad105f34c0bb6a8882c6fa94a9fbcfc1556746ffadd1965cfef27e30bbfd4377ef4a5bfd52d71654c142e039c38c580e824f0c6bab0eac1bdc5b6252eca8b47aa322fafbfb786dc575f49e7542028c36475a49a3cc0781c6fe135cfc185e3da052df785bc17eb4bd01f947f0a589ca0870a8888fa450ae9c53b174618d6c15091a20968d6117ee649f8518ea18967b040e0d9bf4a6485ffb22e0d2b6b083a063b474838302eb84b81454bc94d92c4cbabd3bc30674e04e043d63d8a42cb23becf922c835b98dca8eb93e36c6578e203ff2701f08bd42e00510d45c4311e1ee36d4ff609c5a138049eec87e2a58ea877ede4a8296319e182a190dcd4efcbe2544979fef260a43d48af586e8b45c42f16197b81c4603f4e77558d77b319e48827d0a06b34b0230004a44bc3064f3a6388f133ef2f2c3b2dc55f895a54a327f0f0597005f0101c7fc8478d1712de70aa5ba125dfa7519b68321007f20e519a183bf99fdf18c15329aa1c45c062149fa63cfaede13dc9581965a1e235d64770f55da00af49db6ee4811c8e51f0d47fe972295898bc82c6c0a3c1fe50b9da2d4cffd757f3e0d3dc6e3b032e01407160625e16fa809ab0efacf45eaaf779533243c882378e00964e9c7947c75468b184b5463d7e8914e3ae41bccd8747c7e0a7470025ff48b9721ee38e1a0d112fc01dab024045e0028eefdbc9cdf41b715c9966257635119921f7d4946de0f06b6a72be1759369a078869c4f8a0765105201a8bc486665fcef9cf84302094beda494f6fe05f21fc4bbce9f0bfd89a6c94c014642a781bd8f17359935d3c02d640b1dcd24074927fde4a0031a8bcb4e40ebfc2074ce5faf3a21a853a640c4d4a0e6e0473fe5a859bdbb953650af5a5a0df5511f6d1417cfb915a472085b02d707e540098057895ba3a06d18c94da449828e1c77240563a1db6a491fa1e648945ba29351e504ced719f213d915ac023ad04f287d8a7981622cb519b3a6f9fc71a8d3ca2a76b15b494976edeeffa0ed94260a751ebcd40049e5f3d944aecc3c3464d639ab46ff49785d48c516927d332ad41c80ab24854d55730a2f847b49634121cca7536114a209df1b90300b99866063fe22d90fc741f344e94c438a6cbf528fb6d33869585a53d9974955077e1b65233b532ecc784eb690f48eafd78715b38f4715da69dae2d4b60391bac8ca0c4c588115d24711e0bd4ca8c0a3c9d743a26f99d5932e1e9990ce23cffe0ed9b02b1c23667574039462fa5bfae6b705d7b38287b3565aaca122785b8eb7a1301e596bf0410d69e981ba51396924d290bf90cb5c20b334e4333d13c81cfaa29a619f751125a2ff0c33fd7ef4a4006190a037c8abec4fa0316db6fb9c971905e13ddb0bbc02300c6f300dcbd36f6a5cb9e0656e6564d6866e31a8ef134557ac22c62b95297473c40decf6521cef2d87f722dd0b568ae05eb842847b811591ee0b5646703fb97200bd3a6e347c83cf8d87dc5093ef3ab82358e9e81e61a50337882b1cdc11ace4e88eb07205f7fc95fbd0e7e3e663b8014f593f5c2bff71dabfbe54d11743adc5507c8733822b0489c87d3aa7fdf374b8c9f231fb8d0fe20d979197264b56d723e225a150f035471d86d1e6c381365887c5b7b7ea4b5234a58c7264d96b0cd0f6667b0db2185011a60501d1e1c0e8281c533719e7a57b42843856210e2b6da2c245859c6902c941a9868df32b7bac809a37bac7b04b73e9c1e19ebfe0c7920800215eac85590c70bfef73934d6e1338dc63bdac0221f4f0994315b8c584c88b0b9eef28bca22599879d6d892d5a8d87ffa3c4ec482eefd7f8a0f3b165e8b8796bc1887fd41a741a882f45ee501767871dd3ef0a627de70d2fbe00902cb64e9353c55a8359c8daea402627ec4c041e6b8f5f6c34efe4ebc0d8043b58030d7d829f2cd702437ba485dccdf339cf5fd7e88eced00d9c1308fe106252e5fed8c5d6377b8e6725058be479960e057b9fa41879005dd5505ffbb5bcae3d59710f1d57a805b6a58662635c4b31069b42798ea541c042aa7a5df5fde226716a0a3f155a291cd153d239331cf8f80fd4b4af885a334f351c19389c7778306b780e1a8bce2c596777561139de196292a0bd097aedc6a7f1a9470fa90f280aca77c80dc8c70210ad3cb3327549c759a34b3825bd56d7ca2ad4576e19a9a4d2202c25402a0d3a73241a3d56b6b1632534385276e3e3cabeb11afdb04063829e9d018976a0742347ca357e5069a37fc44c9963d77820171e2e34d519b5f63e1b4f0fe39bc9458bb9472fca69c2506b5033c85194acf7be8d0109a533b26b156eb0a53f7b0a41380854127adedcae0e980fd8876e40ac0687e24e2f9c98b43e016ae1c423e61371160f27c95422269254d7c3d48945cf2b5118d8598b28720eff10706a85d290f6c8b14fb35b913ea0e90623cba35c82fd3fb0699b1d89a5749937ad0934fb4802c2990aec4fb10ac26f9a9320662487b2fd6e9d87349941582de83103f0908fbcf3b0b5b1da4db9de491a92c27aff7c5588c0ad93ae4d372b8c698b95f1983de80c7850c5e232f8c1fdd0397393d97f2b54df0ca05e5ca981e0589ad2c94705eb06da6517784cd811d5767d9c5ed9b1653296024a5a07c22727030b6bab89e161c42ad6c075e77cf982aebdadc5362a5d2364e471d93d011107a7b76721a051cda5d441f4d8f59906d7b20612ada33efa04ce45dd6cc6055996e49ae1ceb0680b59aa9dd9912ec532f06cb8ef85837a366415cbaa9c7ab3ad002c64580dc76ead90a2f3d03a392382eb31b6b492cd337f6d35c59699e5567ad8026faec9e736eddb5356fba19d4d29835ac8294050a1e1db3323c5429b7167ac25ae07bd34aeeb0c394fb5a927e91cb8c4266868826916b7cc0bdca5c495e254e2f71055d7a1d13b5f52ddcfc5d5d43fdee4cd67c2bd68805bd01fd52f71e0dc8576fe4a7e18870af325d03fbfb9ff529cff7863bef922851f17b9982f51fea17dfb5ff1f89bb519e78b8450cfca63be04fd44cc4ecf1ae30d68d76aec6ba4cdcdd782fcfb6fe9e60b197f4d41058ecd17bdff60a002daacfd500aa9f9b2657ee581b3ff147dbeda4822fee7722902f562ff5c09e67c71d71f9e5773bec22b12ff776f26427357bfb849d47c95ac1f1e599faf05cf9fcf7b3072beee6c63a811613edc0207430a89f3652fffc623a9f355e0bf7ceaff48a53f7869e9af0912c3ceb262be56f98f9feb7f7f416a4a385ff6f837ee48315f05f68b53fb0f2af183372b9aaff53e1726523d5f58c333e27b04e14be7f105bf868e3ccaa0161d46f2f147bca29b267167bde1125b69f02cccd7dd0f07d9288f410d7fc0820c45881c96e8ee7cf12d40b20f0430cc7cfd4339297a83219e4f9eb3f9b22bb693d65843f3a783f3651f8227395f19c87aa3d98bf736095b780b1dc8085c39a369f5896228f83fc9345f6b176c99de1b2c18148be0ffa4d2faba396f69f5ed59696c323441b500a6558c5f2155ecb32b76efd8a412f87ff7764c0548ce5f63105bce30342c5d096be54186931ade5b43b436a7bef7a084fae746a6326d42ac500d4d831e0c84930d9b1b1e8316c303a0010f0f1e9d011b30e0164705ca59083939ad85e6fdcc3980ab3d5eba8bd5308d885a9ae438a524abea4b385ddbde7ec1e56a2c719c648786a453e366a373188382d5b981b4028e4c2ced846d15f480a420ef6232ed562412ac592260765bd7d79f63ae5f0c6db58f08dc60f1b53c3995465e197cf7c7623323bc7c8c4d4ee705ad7f790d803601cb2578adfa511e72f65339043f226724079da3a7e1ab1f760bef17dce9497c1b79d2ce4dac2a2cb0ae53aefb848473b0cdea08079efc9b7e844b9558e794b1e0b62bc75a55e2a3a6e8211abdf331be6552d3cd2dcf0f82af133d1dc11bda9fc2f6b0d42b1ff4c307eeb135f07f68b365917996e672353f2115d21a706e565ceaa157b996e66b1b4dca25a0c202e609f5fb7872d3d33d53f71094c47e5fa93765b667d5dcd388ae0e5ddc06a013648c4a7e74d85f888f49f4c7e5f138ef970ea8dcfed6c3f8bff3ff946625ca0d4690bf1210b98defd64e619c0532269e748ba22689ccfdc109a0492c54c7e5594c2a2abef3cbbde198a4f090bb06c6b0cf666aff2723e57e4b3a7c7874e429cd4a961f82ddeb091d73c5cea13fd2e338b379c20451fe1014db9fd002bb88792d66dae60f3c70153d60bfb28c7d06e0006e610849eac8b049bb8eee3b181792a32922ca4fe4336215c2ae3c57d6706b223dfae76107227ed52829f1d289da5ea988c9b9d849fe03999eed708c50faaae4a50cab59bb2d590441dd2a19d3ef200e2c3834c05a0e71574134a22adb53073078c6c78f4bcdf3c0ac0d49b4c4c7ecd1bb73dcb3fba20b965f0347b7dec35f748bc264c3e7adc8f677a8ae9bee7585b0e877849b207a68c1c777eec0634d8053c6cb5f8211481880d75abd03c3c086bd8f33dee71ce82ef640d5d78943d52e1bd98f2189882d7ade1e0857299716c133b6a15af541d1dd219a6cc57b1938374ecfd6fa60504bfa97690f77c5ed06ef6117d8a95eb26758749676593d1569b45b2a1d6e363dd4da671a4d136b104d9b4cd86446952a1cc6ab711ebe097afdeeed782630b1f97382cdd6b3c37ebfb77bac6630188ba74a3f392bed8e51bbcebc85406a65687ba93a7fb8ebda368b61ab54ab7810b1d9a2b5e6925c02f934cc0f982b0b78e74ca51c24e1f9f859e1eaadbc1377c81edecb4d533a1c6b29e354479c30934303cfdc6296a9dea5200c61ed50263ba192d406202b99493e017ec31a60ec285b7f030d30ffa73aa75b82e3c7064f0cc2d57d3736d8b519f7539ad2efda680a9318008dbe4122db9868549cf68724ae4807f88fcf8b28882e6cdd06b7287e66792206a9f74f0000d3e293269ef89b3fac9d05e0af029771306944f8e42fbb9dd95388a4492c321d5d00ab41abc7dc63b4bc4d92050857f232ca074bfa169a1cd67260554392b7ab584ff482198b45207dc0eb55214f9e1720466cee071b1fa295106fe7a36214243fbb0df9e2fc0f8ce071b78be7ecfbd29d9f202977047dbb01557eacd15e2dea98bc7d5b727a92db76ef15b6a16139c6b0d33116a61b7b94e5edf0d6bc5d83186bba123280d5d5a01b9b0c4736144fd123b7a4ea30ea0225d3d87be6cb254272b4985507b158f4c794a02b6076abd577653b95966733bfdfbe2e5ca66cac53844f79865bee8fc7e6443d57c56c5f5138f3a684bd6fcca800f42cd270a8e171df26d62ab3fe062f6aa7af668fe27f0f99220b3f3044384b00709121e04a79927a0e1b1e4d0c6e1e665c4bb7ad0c2af00fe063c3a25404735ac163f6e5d3bd3e0ea99e20c4f46c3e54574c8117c874bae07d7df3a57905a8e23c5a370a272ca41f644f01fcf9dd9ed6e491fde20b482b40d8b13f0ab9a84c1ab322179d7b31c18bf1f03c76434ebff9e057ddcd8e3ed97614167bf3ab6fe97fcfb3c2d6ac5212ec82d98f857c72b1fae893c61fe9db6a412d3ec1c80afa578ae0c316e937baedcb22e3239bee90160fa4565c539bff2208d186b3ad9a7e4e578684b05b7e037c113e57a3b322508f81e2888f7c53116d34bd3f3b434071e93097dd925dd58e81bb9bf2ccdec7e5ae08cadc4c4bdda40ebd2373b9c6ad3ba6de2254b986e7b0532cf15493bde358366da5ec9d2ae398c681686b6e282d89ac388486f57cf6be4d3d6ad32baa6695a62640fbee9e997acec88752e1499430a2b5f404670c49553f78ad656882606091783cde8694502918d1b5569922383f2f9ea10728b5032b748c391fa71964d2857b5c34e16b7223ba3211dc20ef22152959882e11ee132c981c5f7442e3fb207e9ddf834ff4ce6ca7104f35cfa3a89093e0271b75598751595c8bca52c6ea5dec632aa82e8d0c780581acadd1d2a1b8464e5922929ffcb0f7c4d361aa0dc86546d0aa080eb4427b7b0a5b40ec4e94537eddd209390fb9f3ce8621bdc7d9121ff3abbd1c6ce4c2e57e72e56d1b8d910ba854ca1935e6179a54fa992967c39b9e843c661d09b68112f6678396cb729648cf39e050d2fe229534b65f8115093a3601211a49bc48f54ab43c1cd00ba248b4ed360966200d2bcb95add351189521d73f8a675ff408269ed9329d09d0876883b7b77a9f0d83c1788f0878d7a48774b13bc43bb023640e0e51ea97f4e3e8534261e41116f966b446493c7715555872abc7d7343975dc78d274d7c4029cb6d0115824406ba55028ac32cf2b6530bd117ad1c843a77a22f04725ed448796dc37d9c006f9e854a23e7a23a67df16eb171cbb385bcbd27f0069bde1e7ec391bd4edc8b563ddef87e691a3cb473b5afe9b76502998c03fb32417c2c90275528ecda82edb6a430862f4f9ea319cf57363230e3b096bc4d9cf7899f49d9537599ca05e554d58c6fcce02d0c0977e51d4b760d4a0b893141e83935c45801598c5be1f485c9c69968698ec601eedf77791b14f1d02a19c7889b73abe8f300ba0bb1bec691822e1a5a3471b62fe00b13f0126d6f0a84e37d3bb57f0d27c9ccd5e6f61daace304478483937aef60a3a709a68c93174b70762d390ba01baaf79ffe27796c0f83a118d043c6795d39141f080067f83a25ffda35b92694f471e1e3877ef0378c7ef656c8347c07c046f063f744caaf66b86cf3038ecb48d41f38c958d4a8778d59576579f52660963d73eb83a2a189f938afbbee6fd35cc0ae44322c1cc2af9c79531761bc0c74d55a368a3b56fdd5c8fdc48fa48799a712dad3bc6f335fae15ea276d2aec34155af4d0494741fcfd0a5c0e39cf52d22ac5702097389262962400fd7ab1e8a71003684a209bb619007bc1c0ca898fe260dbd5d2820b86ea15ac71d5d17115ffeaf2263f7a47f2c9858f201c1a699d973730c334956483b2aa91b774dc81c765b51ff3116b1a42221c25ce4d7e33b63c8aac9123c6679046a43d4431681a19c0a5602bb5317ab85cb674f1e1dec8085f14c828ee984a824e5b91f95ddd1aa3ba09f0c35d844ff3025f83c6c41f34bf02d604adc1c7e7f9248106459bbc58718c381ea277626c9f066176d2d7da02d72c73372a639ae42181a5aa345d83d91196fc2ecf3e98b36444aed8d4a000a0906865039a4353b8036b1adba45f5ae76d3b3e96c243fec3cd8dcc52d14d26400281838d3489ddca6e9ca0c540ac59f6bc2b77d0f4cb03702ff1999d4c49902fb93aa9592dfc7297b09b1beaa6e795ba9d1eed1ac8e7ba73a20be71f50f64387beff8a66289095cac70f2d139c81e0910c025143eb45ab5a502f2643c8b2cc1a2be47a4d8bee67544107d5346f2c36ef855f186e7bf893b5349ada19e6a559c46f3ec2a5b29a760e04cc7b9c491bab7009877e5872b77e2bea5925aa90963a5dad39c2d4f1ee03617dbcf67ddd0bc304f5faa4126fbca36b6e194c9c02023f1ff1a28acb9742433374a8345855f96808145982a898f7353e0132840c0c484649f17276e04b52c4fdbdde2e440f5cc54f65b626af0cb33f2dd65018dd57387bd70cc5f353b6be6e8c3ad1043299570a6879368d62a1da62481c539bb50f324402d2455159183e408054bc632760f3d14f09dd742590993bd33aeb99c86fdb784abed55e78dc59b4557f062f5f6529ff4f46fc6a019042a3da85e19283f60132a7b261c56cbe27060e65179bdf1a03ff2992c85196d4b3bee10a4118c7eea8847b67f107436a899b15a4693bfa8215a2e8742d2157080d8e5957eb806aa2e5e8c402ee1d4824baeaaca69df91f07705200a14f1eed17bc3019c109c406b0b399dddebc9d336e3d84227ca257433b7e518f866f7ec8b797c80f6b58cde68c21af0c90086eedb84574cd4a28441828188546f2480373eb7c64c380408a4ff6ce040aa2cd5fa2a27f7e3a0a0d3c9dc1dd7440180385b3a636f5442c38443d0648212741bab107ed402f9077cf55b178128efc3b2ac6b6334c2a0e958f2f4a137324ada072d88ffe0abffb52f7b87bacb2d4b5fb47e4157a4adf378ada533ee08b39d2e13af02af6e71066ee0e320b1309e6768adb36b1ca66a4099ebd5e941c823c3f8ebf18cdb3e99968019225a16a808c9f7982b3112108cf344bd69e8923a767821502a2b394b6496ed63befced3b283c8085d5c150e7ad64aeeaff6b507040fbd639b3b1ffd989339b27d20545dbf27b33dc3a8427095a1f292cac1638b25528819005eae2b0701b16756569622c6180a6d09581160ea38457e44aca0a6cade7863586155d3a871976ab935b55916adaf3ed1f4e6cab46d4684b977f9eb0a51251c32eaefe79728b3a424dbbb8fcc3c92daa889a6de1f28f3c25d419035e8dd2d640f22098aae726ef491585a43284e352a4b5465e790e81f3eaebcbbcff6121458b1c983982547e55f5857535fce6cf0ee884fa56b7a1178a0b0fc67a9eaa97443d21a497c68b4f8f1bdfcb8e3ebe1ebb16d6ea8de23d1a08136b84358c2947d1ea46d1841a9d42d322d3a7e3015a2cbf0a367dd3e0a3ad1fb8f025d542994a4b259a7a4527f9633fe3813705bfc636a8a344772c792a5bd2485f89c68de5709d84c8e5d2f4f1df0e9222236d3cd26c6463999fb93c0063b75a0ea1a988df24483910a2979c707c0975144cc741ec252cb3012f399c614a96422a76a28751b65028e3d8dea5e7b99f2f8c8bcb9e6f66d0ec54fefb1f14d02afb19007b37704730d5b45f23191f0f2d513a7432f9dc4aa06209dd9d6dfbe8142a8158b8355f835dd77032a71ec1487664c74653e18402eeef2944f62b934d3c6150f62fab614010a9052dfa21b5f4de57774c16b0f0e83c5cb5bfb134ba28a66d7e21047e231d44b26c425f321c395d1099a278b6762be303bcbc84c20a3561e0d89406608a1f5e73e4e89bc11bf57ce52595a7385b5054fb2d38ec7876af54f444e8c318e55e73d35dbead79af16543cd47655ddc5b0854bbc81c67051948037b0652ba55b372ca3ab314116c593901f450ee38f135e1e7bdc230ded476d7dbf49a2f09ef6bd2a44263684bcb860f3d2fd54abedb6896854e510dc4c9e3fb9078fd2697eb3a55d6af240991a5ff3eab68f2dfbaf8b13ab4ed3e351a914093e2f2297ffdb6d1926ae37d58206c55c1235a4c450d9332cc3dc889446047577165decb97295732c94c155e93a4afb3842de6ef2fb0c44131b067ffee7e9702b2e23707afb0c9e9b2390b37dc12004ec4782b2434bfe682b1ac7b39a25113ef082ee99637d02e803cebce38ee20002ced5da5418b84416814568964630d114ebd07d0550c97367f3158395bba184cf34a19a63249cf8d645aaa33d09a4ea2561d591e6c623e6c8b03d54838092df5e3dbc903b637e6ea78a14a437b005a44a05a9570cd1d42d32f191238fa07e3f74f72bacacb3a3428ed7612391aeb0a48482cbec5ec204ca8cb067050c499d6eab978cf4bd916cf94f143bed6043c721a4500a971f5074421a3e9c37ab0a54849724718bf3c152805402db35e52c010396e01c59bf1213a04bbd6a81ef3e1d3b51854c0840d0c0d4401f6bbb3ca56792301da65be61dbe6780439a8f444368a582617d643acfb9debb60690e2cdea13d669a0302b81ba300e3f02d945d4f55a8e67b11983138c2ae3aa851eb7b32a4f3a06a49a3a057a38bfa1586aa64f7dc16167ebdf6c7bac890b3b21ebc78936a3dbde9b8fa2865a12f4162c5353ac0200f7c998b48abccbb4fad1ca75fb44ffaafac4a13100d7b68a3b6b2f921837beea09a3c859dee88a86a8349674759aef6fae27a77d6885a0ce4fe20434f89e0706c605f33ef11a10a2623377bb050ac4cb0de812113f35530040b3252287c243d8a8ae9520493cddbfb2b2fd888e926ae3e4e1cc989afa1a7b65131d6dca642d0ea0347e3dce916e8c586942e6b7900a4451ea63279962f2547f456fd3a6b550dd7cd9f0c66ea2f8bd4be161fdff7c44d3221d59ca6d2dd674391d6c70bf53ae12e5e47445b2877945228ef6e4f744c16e4f74fb7fb6bb46e52c9aede2480ce649729e0cbfd64571e550b08d486fd3ffeaa1aac31b68310690583810a4abd8e6ec0eedf39d7275ceac463e8d01c5009b53378399b164271f84ee2d2988d8c16dc71ed4c89f6a391a62dd11c24b2104a92de4a3eb3cb8131f6a2763e2fe4af570693d3d5f48d2713205006d02ef40f4aaa191c29811d85aa899d91bced1be7402f6b1a3813a94620134f707124d89e349a2c1d2cb9b049578eabc1b60dc082b76edaf4cc5e4b6a36ed1c5ed98fee608e37e28b309268652af2fe04a9a932bcf85f86c71ec042fc392e62d4580fbf220668e0afd1478032cf3cd7e422a85aac5d1106c777abc67608c134aa5a52aa6bc33fea229c95a0c6454b1bede8ca6e868de49bc4f6a753dcb39c9d7664cd2f54c45a0743f43ae85782ca2208e96f6b5c4423836a709443b945305098d63244d28cb55c708ab3dd492515002a49faf5ad68d452cae078bb914ff7cbb045ab2bab84bd07f8ddeed02d934a74db490746a4b1039498105a47da4128cdb1b19b8057dce00bbe36eb434a3b4fd7a80288ae0d88922893840e671dcd433b35fadb56c405b4b78025d76739b9680df444b2cb9fee52f159695d6963c40fbd21ab1b816f17d8cb73146c3b946817e0b10e25e75591d7453f1457ae8410791c2126636e224a1c8128ba90039146d99aaa9e91c86b001d355036e148478397dcbbff16a1e9312d86372aa412da4d29da28fa44c60a912c7816119c93756114721e1fdaddf1608deec680b7a3e3a5a40bdb1ae0aa297b077c702db91fe1350642504bad0bacf8ca165c6fc015fe82b39139beb8cc29ace0571e0a2e99b33be389c8409a597e2222577d72ea205790c63e37e39501b501901b2baef665f27c230dcdc5794f6166fa871dd3df9ba2f18c330779649e28e02a3446246ea6bd1f5ffa8cdf55609a8e563d2b65262f81f719b54c4ee8049aeb9d0b7d2192d3c83e6cf6b250d3aab829f4ad52b30290298f5db68ef2b3198a4e6be0024068a55693a38588c0832c7b483f4a4a91bdfdfea7105ab6a5a5ea4b405fa8082899313801e4ab4153a14112dfe19d0a7ce31b4b826633f7748bfa3dc02495b446374ef3a8f64ff84147b1c1f1d993bec10dd81cadb71b55206392551b7539c178362b49e2635d094369b0450cfeab271259fc1a50eb02201dfac343fbb5a844aac38e105221b23a14811c9032aa196b3d797bc41eeef040309a950dd4399026da833f305d4e831d92842806e3e67bcbc9c609c1b35ade8263e9019164634b81080e5b7889897f4b4b55a0e0353722165a65cd9b01f952a903193657ce77b6af2f2fb5769fb03f8c330524db2682423201776861c15e6058c096ab4ef56526473e4e5dc5ef9966f2fb58911001edb09c0027984c133982ace54657609977ee742f6f8189a2fe780d25ea80bb327ea6f0cbedd046e546b916c2dbbe74172df94a43d02b01e41b5cb79fa8010a9ec4672607e0d5ca9596299a508aa3dd8c48dff2fa4b9544b07f7dafe128ac75b40399457f63e6c404eb84d1609e3f06cecb8b9c73083a562c3a47c54a4e6c7f4d7ce5f0251474fd4dcfea1f009f2f6cfe8f820033c01d278cb0f021b29374c85e6180b2ed6c2bcbfef7429b5d548ea5c5a28a6bf2829dc17fa4ed6765f5815d84b7e290f1dfe08a7aa42ff2e277a9320ab09ece7cbcbed125e8256d8e790964242fcd7455c44af845f83c1e5800214e1498b4d9937dde581ea7f4a847c7ac56252d90e69237ec30c5c74f4d592b309eaa5904c15b530e43184b368d8b76944381985983f4acf7550b3004868e140798e452832b353794956fe53ba3ef716fcfdcf2093b99ac90d5dca0b9caca28144a3c92c2c1649f189e3ac6a94ed28369e206c0e86b681b03ae818c581a7942532b444035a7dc4b86697b27c1d4cea705fa670e511b580d8b4a128b7d30a855579242e9b8e6b9d7f8f54d6d8cec0e2aa1b754ab084b5892363ebabe48af2aaa19612b98df65452e614bc516f603b3581d734d932447c65a58b492a4c7500b6c44cc89708bf8379cf3db75174999e80998cde8c90470eef170adab78f34c7018336bf4b59455acd604e61f75c30637f9c386a473ffaab493f716a526e116d2ff1379e8aa71c5f4e2b1b03b2039984026ce41bbc0548304d75298a65839badcf4b9cc86acdcccbfd3b5f6f23e4202efca5086783bade42d75f1fe1e7b4bb2b98852485a484089f7a363ed12e3f83fb2e54688e2db2e8ebdff628053d048e722c913ce04910556aa4b0b962900537507af6a6793471894e2ee1f7912013efea4c4fab5b049d6c8e17919bf07f726125b9762dc554189959cec9c50589d22a0e3aed0c08c87015ae80d2061aa5925c9835d410607708a6c9668a6bbe7ca59f1086f4654176bf2fe3c209f05345fb195328dc75115f4a8960884e90bed9fb186971aee0549ac4520b4d9fa66df4d67a371931e4e68b12e00535bc0eb0acb722d77cd132111b590c1350a4e8ba4d6391d8783729d1a22d7472738dc45ecf89210e4573be393a3eac16c41d0a955327644ed890ebbf677e067badb71b6b0adfe4752dd11cb3b7101beaf910220a871420ed171c6ffb4de2fc44f166cca401fb4c2fbbe44e1cd605793743c26fc7b0a33964d785382d7fe4b449ba9eb82b3b581c09143eea2beaabb3154d3b1ff33e118dda1a7b2c8598e12133bf73cebfab0a7d77331e0d43f3ebc6be19e6f3733143a64dfbccc43ed23dd6d0a7d6886f95d853004dab77f8b79ce7ab7d4b1b6687ae9d4402cfb053fdc0394ac883488056abc548c8f18482d8343df6fa58ba24aca4c84c2c82f4c7f0bb8ba6bf75644b14426af317c0e196a49d787be22cf933231b0f94a76b2fb02ea0c51c4a846c0b679997a46690a05364aec740caf21b10e1b30fca0162703a3b61d2e7efd371812b6c29ae292439774388990f9fb45f8a0a8c27ff408ff24dfb4c614c8855a585f580a04d12523e964194f36079c3c363250073d7b92b4b659abc2db14b32ad0044902bf6de752812c86dff69214819942a675e5b44dbdf03de6492349b5bd7eab93c520d683cf1c43a5b9130e8c22017908e8d47b57213db00ed8c2c915d351ed6a9bc1302cfddcbcc09adca9a2b53a7b78398d9598d940a141dac34f2f414e60687313b8a96ac0b0494fb1ecb4a843b511d1629e41873ce88bae0a7f439faf95365ecb0a765305bb686b726f08ad96a17d20366fa55d7e3cea0e15b013428f9e9443cd218dd760ecab6840a6e377f7c1178f9dc9d75e328cfc3ea4d6c4238b1deab5e9229a3a16d8a9fac1a94f19a498a0709206d142fd80ced6438193c4096c73105fcbd3790858eacca99dfece4298d7b1978114ca1c479b54e41098e4aa405379a75b57285430114fb0ee0ff5ce04790da31b295ede97b8f095cc94e8aaf5023c2d963e4e15d89c185c694aaa4659726c95c27712d78320842efcc753eb8348acd093769fc63a575f006c2603b1a70b4e0d0b4f4f4594acacc9823142b252ff08324152070d980a5a6db95c7a414de309d054d193445986da1539898c041b2d90eb8fb20bf46822b961e0ca1eb95733f6136f4ba02a0d4b88b621d59beb2968893cce2c2acbd9f4252955766cd7cd251dae12e3bdb4a430abed22e5b129c2d38a26077792fdf677badef40def8337166f3b2c3dbec62a4fe165ad24a642a7fe115467a94339253f3a27d46f1f3699630173e86ac434c2b4fc9df6c92346c588df5dbb4c07c76d6b1e18921ba3428f1fabd12bb10445e36ca070bbcfea3f073d62de71a3dc01a62adff5660fd99c18d4c67e19d7336a1f61ba43e84d9f0b719f9fa4fda36e2301a59725743033aaa43d828ba8539709567b78e903dc38d9694d8cfd25b30f3185166ea9ecfe29a86e4c6059489a98927f1a3331120e0be069aa1d541afc68285b216dc6ea6247bfeb452ac00ac95cd4ecca925e33c24084b93a64255564d8efb0ac1621b26a5054c0adada3cac515b325e3c3bec89449c11f93add24ac369ff9d415b0b5cb3aa3ebb20be349e659ff1b6fcdb6153c0617213f24968bc0fa49d878cedfd2b150c662b8757ae920c7ce01b9a757fda905c43993469f871a851ce401b5e4e6721c591d20f7a13a860137a42d1b5e913b5b22415858ab5bada1249c4db950e7509cdbc922b52e8b7ef0c8ecb38f4e7fc1039c60d45cb4532d4bf188fd0a8d78f1631765d54ad23a1e94f3bd379b6c3bce98ad29e2378ffde7ef0ecdabc82fcf476d29b60ac8749e0d44db70bd1b95be294f3024b96f3f3634abce176e94600f89bd7ede2036d974e80adcd4741ca91636306cc5841c2b8ebaa14f34e8e294498660b594865e096eac08d714894accaee71e55d20664e724a0afe26209b27574cd7d5b914830822528981bf479d4d1c4f04c8841a45c61fbc452cb97976b383848e7b27c448d0fed444608beaa3e8d9b8a5daf037ff2e66403f602a9d17de99ca08ae9ba5a62e53429f19926b05ce80a962c86a68e5f4cc821bf5f512f1b6543ceb7ada274814212132260c2d3919ad1446bf4d9702dbd76b43772298666226904cb2ac42db0fa03bcbcf0f9d779643c0146c18b8d860ce5a2490e62620382c2cd4460a9fda6070d518ef81b9a7ef392c77bf8acdea67729eea861e41f827b02ea5ccce749a6d26c93f923cc2013e1145e26f7f779c2a06c4c4ee46684d700626a3ff8c0ee9ab9c362a925e3354b06a6a48f66d531f5f0417f3117bf4a816ac11aa8614f1093204085ed0956889b82fcf94052d5cc5802f14f96365a00218704a40c78fb259ff23d3ef3f441dbab988c6d2109c63a7320f3530a115785c5bc7579199807e76608a6905ed4a67b4144db9042df00eb735004eff60612550cec99e016277fcd9081771ef151a6256f8eb30df5086152f5f1f1c6dae0915bf9d6122f86c87b5398f2d3cf6d17b04bb7cb9269f9c078fca634a6faf007085a5643bbfdb7309adef4d6a3ae6193df844f662f8a512c672539ef9a71abd3a00d7fbb566744a17e258521182e88e26e84aa12d505d98c8cf4f39250e02a4344675d8a4ab472b1d75d7f9a0c0416eaaf2d5c493223a42459e1b2fdbd5d80620124433af0b04b7c4a033f7a34db41c809ec82ef839595922902dc85f14b7051e779dc1304695b04f56da84769d89647d2a7306109f7d8350bb714332e74a47518564e3085831f3f0a5452e5817063bd9f9006029b52b915b36a005ce7f42b1f8ff6fc52f80396c831567941a1c4bc9b89b69193f6fb9883b27f5440041d25b316082b8f235877d50859d86a57fee3e6192fe5015a47bbbfdfdba332a53187502cc944f2d9e122fa84ccd4e81033f36bc048e811bb7eeba9586f7850ca7c7f2d045e382304b40cdd968b460b544b2e0549944e6862d233b678166f84217422ff8dc91951446d5a22f53a3f0b831a81164e202ba3b4a705a309c55a503c93bcfaca7603173881b61a79cc8d3f13b099ebdbf2ee4f330e5cc2b7b1441b34d2c12b0116d080e3813785a80b63088f0aa300082c75834f48e02a10d3b9f1d10dc0ae8fa7851fc206d93bdb7dc5bca2d654a3205b606cf061407a3eceb7fdad7cf6a56b36dcb326bdd4d93b0427944966dd936dae46b367e66e5ad1608bc3afecac7000f65f9c5864da3c57f636521c24506a21fe1cd47e39ba994625cec629cf14df1a515e307219f6cb4f4640dfcdda5fc96dfaa3b5d9eddbda8e5637896179fcbaffc07a3f29fe93f1933dc7f33eee8378dbe15b4c473bf6ad03b8580512e9c2d31b0742f5c7c69458594c26d3fec781e4130a06bbc871f81a6a910d36ca0c3f89787f1b2e3c7cbbbcfe9d3caf0d5621f2d8c9732b3f4359bfd66b51fa558ee4936e5552ce957acca97eccabb70f11ddf17eba3033e601876604e3765f8186ccbb3d818beb32cef62e1f5ec9018c07810fc0def598870f0e192ae813bd83fe19cec08fd7f5dc4fed729b8d3e5a9a597e3da859be6334171f9af3bef33dd17cbb77a5fdf8705aecb679a8da6a3befbd7ce5e477d7979d6bcc7b64f921f18d67398e898f9554221821815b92717fddc39e3f262699b5eae8bc5e1df2d31d866b1ddd906b22e6c97e6afd8166a530fb5c9c9aa4d4f6c43894254d4a61b97443ceb391e33bf5ad7e99da6815020c9c166f4747933461a29d0cc0c24b8af61d0cc9d5fdbc7073d3c4d23a96623bf797a7ca4f46090d7c00957ded2a549633ed38069938d778c531a63dbd7abd6fab46e5b674221701d4fe23f4d33fd49365de84e5f72a703ddf9ae639a9568f33ccfc3bcfe0ffbce3c4ccb817941e0d5b20a8dc823128aaeb17a3a664a24dd53bdcf5a9204f33e180974e5923b6b9078aff731ba57a591461a6bad4febd033e1280dcfa1ad050dad456bd15ab4163364cd349badd6d16f3faa5eada37ec28413f1215dd451b82ce3aecceb9f0b52cda65e1ad619a51c472d4f87c9dfc88d1739d1f54c344b939a8e4a29a5943ee518631d8d481949cb1173a3913645bf304c7bffd6beb16fecfba26fd16ffa0d5fc6fa9aa6fd45c5ceb5e1b2122a865c9d5bbf3de004872cdaed2d1bbfb28d9e313d8454d3bc637b7b7b8d36e5080941218e99d7cb14dfae6e359bf66ab8ef68d335bab6efebdb83a1e5d8bc20f08ebc6ff4dafbbf6836fe57d5a148cb1173a591ee0aae7bdae4d3a689591fb0a68f340a6c9f8065e64bdb46ba092833ffb29d023e1589b165d072400fc3628cee1e638cee98c530ccdddd1d83e1d66f563ed643c3e299312589410587bde683e6c520f1fafb18f137bce99af5d7ee9bae81371e33bfed6d96db0db7d76cdaab712104b276c0f379a4f021c24f10cc0f43e42061028b54497a4564678913c522dabf68382cff71af813eec3b6807de98febb8ee3ba768be67da3996d56be33e9955779cc5e36080dc20d5d2d9a0df67dfd9c9a0eeceb91f240e025bdbf0ee5b891f62bd6bdc0a7f92ad6b76099f9249b623be8b24c29d74dae1fc3b4fe79917d7bebb48e7bd7b48ec7cc5765475678eef489c1c423bc0284c2280c0a8402a1cc6f0cabb4695c341bcd8b39eaf7d083b6697a3db2fe89d108169972dd9b646031c61873c82af4a236cdc95ca44d93d9ab39646ee7b5ac13f916de052f8a3751a74d45a28a8948e4ceafd4ddfd331fd2619273e7e3fc5ca771fde9674c13dffddd69f62a9a8d53af4fbffa80b76af4fd3feb5dcb382d47f5a81775c8e6bd2c5f1623384bb582abd334eea56836f45ba769b8394d07a594c729e5b8aef35a6d07058e884f4430c57c63919c35f3626f35e15a3c345c66cde2e198f9fcd60eb8c6899c69b2f82aba24704490023a748bc34c1ac0359019c0a867614949990f3b48bcfd3e46b71febad07ea51a2151c979ca89af12ccba78727899319a13bff3301dd196314a21d1bc6087700c5a082eb17bad87f91e825134659b7a88bc23d914eee7cfeac2177d219f7934c38269a81a605c5a73d76619a57bfce9061d67a42adb5b29c586bc85a97b57c60fd00ca24b17e82d0eb1e14a08e996f11d11b0e67e64c1d6bc9eda16f08881835ef7c79a469fae7c72246f9ed68471b722a124199f919cc6d09126f7d1fa35b6bd478fe66ee8c36c11a6d829a8a4950f733339f462830dd0b579ae2cee79a3805cb689aec3f32cad26c2e4fdbfe7abe17d4e0f6d47e33787e94ee09e7240a5d9f8d32db3a5a8ecb0b02efe6691f8ff069be66a3c51389602a3ec13233fe74dd84019fe67f3d7b645eab34af8bc030aa6fdfbe7dbbbbbbbbbb5bdad6b1a98fbda5f580bdf69586ccad75abb578acd5e3e331f3bfeb38f9de3a999643b66a06ab874ff3a3b5787038eb653ce2430fecb56fcd1669aba353241a519960dea55be1667c7e82626434683db4d743f33aa84dd56b202f4693196fb7d0cfbc1fd18b809aa68552cf895aa73fa35ce7df3a77be1379cd4cc7f4fb703f5dd09be6571db26a31cb798dc5e331f329e5b8ae33593c98f799e65f51cb01bd20f0cea9025294123ad8efe5ce9f613f7be7e7603f1aeefc00d82fe6cec7c17ea73b7fc6cad84f873b1f00f6dbe1ce3fd94f0077be8cf937d88f8170e7db603f16c29d1f633f2ec29d6fb21f4f71e7d7603f56c29d1fc37e4cc59d4f83fdd88a3b1fc67e8cc59d33f2693ec9d28ce3d30a9f4ef3ad75e1d3fcb7cfa7f92d16864ff359ac894ff35f5819337c9aefc2cea0c1a7f9d4d6e0d37cccdae0d37ccbf2f0039fe67376079fe6972c0f3ecdef6c107c9a1f8365009fe6cb601dc0a7f9306c04f834394e6c62f1a2d9a5e5f0fb75a09f98dab4551c52258dbc3813468d5ebe53c128ede5c32940293471adc03519802b98f222391d021c0ab93075d49f757efc19e3c426165fb08fef126d4bfceddd88031da13d590a5c4995d750ca65dec7fd7d8dce9167a3295cd18619a3525ebe4fc128172fafa75f5d647fa558782d1731e58a1f6360c10589d70784722fe78bcdcff17a4d9b386b7644db29499f2b6510aec45eaa9af868d986e95c3a3a2c3ab00743aecc65254d726ee53229234e08a313a6b71dcb34d226199530bded587ac5314c539f7b0a5c6311c131f26798a1a54545e57ebdaa334c2348b6b83442f33156dcd7ab6fe24c1c46652fa7e7e265069a86d4342adf45e19e48e8719caa0aa06bbaaca40a23b77233c3b87d42e213e18462912bbf98822b6ddec693aa1f9bffd8878707fd176f7e35a9a983e850679b893669c131f27d4c3c0f570e5757a05b74f26f9ee814b9f225a402c7c84f72e57f71a8e79174505c6984a72d2790f82ea2fbc20dd4c29572c592c3c5ff66dcfaf1e757c144932817e6b29226417736b95171f1461ca84d52d5264971449ed8d326f92c48703cd248d3d0974f781298a6be7c47421297095c037f2691212b5cc9240a3f4db831c65add53bc96f8b625fef4236d92360e8523e9fef39e179f03b5fc572a595f02c4712ba516fb837e0c2cdd8b15bfe815638cf167a42d3a4cd7c5bb788ef6240253f0e7caf944aefc9a923df615b3fe632617cf544b07ea409dcc8e2eccec96592db359666b66b1cc5e99a5999d99b5322b331bb3142fb30d03533c19243bc35917f69bb9f0760696e4482d635182e3872b1746c59757fe0bd7401b32c8c9aa464af972fe77ba72fea7c395411e233f4eeb31be73a4f9b065175cc9874fba866f48ed72894ae6af29995439a51cd7755cc3a5ffd550e7371f7372a3f7c1dc59a7e5ae65471716e113f6a36bd98d68bb96d59668d7b2d94e762d5b9d48bd96c5565792eb5a96b68a5ecbce228b89441231bf43f0097b1bfd835f0b864f582973235da38097ef2a074ac07f9e0401ff790f8fff1c898d7ca9f21a0578fe9912e03d02bc8ec7ab6c3c2aff878fd1cdde87052ef79f0b0fb2bea44d52e2a0b485be13361f6d0bfd94bfdec5f5a6fb23da789096bc8f722b5e908ae724effb14cfe36372c42f3657018937bb73b664ff23f6137b0121b757d7038177beb42d997cd6e25f96469b8122b293b4892b4d7266e53857aeae7c88793336f249defe3a433661b7c43bfb678e011a8ec0892d1475b7d7b081ddf22d39c3a13811651424c2296804cb44143c824f10070bdcaa02ed3185fef510875130d371227ec4772e1351765de0be19d588f810074211e34fcba1b086a3c59f71402a862e2ba122e87e8d73e373979550e1735bc321ddb21668539c71af02128787f458a6746d8a222dd039d80838f8f1299defe3a2290f2da594523a3d2e2981bbfc1fe518638cf37a0861c9faebe7dbe820693fec2b11208037484fe251f28828fd0684dc287db5b41e7cc030dcf83e6018eef62da3ffdec479dfa69691a7c374473ff2b84dd9671e8c8febbffa41fc3aa181d2f3a5cfa3e495be0821374a5e112b4faa9e8febe34feb311c51c84ee957523c1c961b25cf88d217d18122843c894422552260186ec4407e49721148a552a9542a954aa552a9148392f74ab88f2b91482412894422ad90482412894422adbcca1741fa0e94bc2252de2b82f4fda6ee388ee92759386a9529442082245cdbb23ada32cdeb081bfd36d2a494f24a2b6fa4aa4df3d2119422c8b6aa8d465addea56b74d04db9669da56b76ddbea36dab6d15fdbb6d5abfe37faaa695f47d7b66919a423b8c96cdba4dce426b31abfda56ddf9718b4fdec8eda23f8a5936920fb7cfb2d736f7cfa845475bdd648cb2a71ba5236e44afd1e8e2bcef7aed29fd9115357738aa998c728b18166374f7186374c72c8661eeeeeed8566d1c59916a96f59e8db6bad551b6d52cfb9a6deeeeeeeeeeeeeeee3fcadcd2b6edaa9aad367ee957ea68341a915452b8abb4a2421aa570d7685437276254e65e54a469628c476cd9b66d5e8f9ad979ebc8b5eda3df25634643c998d1e86f5b46336ddb1855bd2d18b5c922aa188fd836a2aa4911546dcbb4ac6e9ad7512682ac6e5bcd6a966559cdb42cd37e9465353e8cb27c45a110c99d98d3a558be42334b80a227719f1170f0e7739f6ff4fe252fc553add8de41c2037f2090752111f8241f0a81656490735ed33ddd951ea677fca74d328917a30927254dcbe87fc48ba485aeec1d2170867e88877e72767e87359b98eb4f1afe04c529ee4fd3d8943efe40a0a609b2341da58f9ff5c8e27716a397df58706af47245a57786da49d370bfb33334048b6e689b6c749191acbcdc8e6c6faf25d2c768c2c9b7fe7a92d7a3f440e0a53f8070454d43bd16fad9cb874499f7237a12cf7d562c370f51804ff24976022c233fc50ac127f9bd2361ce902b7ff3768a7654dc111f1fe83e2fbc21bf90dbf6f16e9a17013ec9b7f0421f46615e0f9707044e1be34e0f4b54f9de7dec3e1a89ddabeeeeeeeeeeeeeeeeeeeeeeeeee8f80f7d25d03873c463e84e2b462d3b257e6f2305c7ef8bb3c1c924f5fec37c3f2f548dddd2d778bbabb4777187fd97ecbbefc9c99add147076e2e7d17db375ec3371eb3160b230e89c1cb83e06fd8779ceefef32557caf09f2be14a5574892a3f72657cefaa9d85570697d7349beee367ddc7aefbf8361a86ed2350ecbcd8262aea18f99176176cf7cddfc5d21bbbc5760cb6a1b0ddd97e619ba77bdaa793b449be8aed1fdb294b9ac9665ba887da499b560dc5b6b54d64fda65524e0dc89df507b657b8d10dd7537bd3420a0066fa4aa8f48aa6a1a2aa3ca481f6114eba0943a8e9474c70e6a5974d269391497ee7696482289252cb6dadffaf81d17711edd0a8910ca20e7764bf88404f149fefcca32f23b98d9ab995ed1e8950f8e54b575c3a6eae3cd391f8610cc4f64d095f325c0292b0a184516777e041825c3cf7f00a7ac1bdca00977be031ac0292b07ddcf5f00a7ac9e1e26dcf94130aaf4f311309f07a3b89f0fc40e46a5fcfc1cf37fc0319f0746613fffb21477be0d46a57ebe000870e7d76014fdf93ba4ee7c1a3ad4dcf93318e5e2e7e390c355712273e79f38653959dd70e7c770ca5acda78153d693f996531614198c7af1f34d8c62f9f9308c6af9f9cfa88f22c39dffc2292b0ad17c164e59442fee2c6219393f8553f3c68551f6e7778c9ae1e7af306ae5e790d19daf716a0e997f716ae2706a12e114fc6119f9f33346a19ef44fe3e7ac30be359b59677daf1ec355afeaacfe238d5d5c9ed9f39eb9468d671680009e39871fc0009e99000478661b369ef9de67be71e3990b508067e6818767c681a38767eec18767f6e18767fee19973e430808e1d40bcfcd7f700cf6c8067d6f1cc3b9e1988973fc0c3a036e5c266af625dfc0babf22cf6c5db96b7f67f066b7fc5ce40b2a88f96f4346cfc03582076e830408e1f7ce801070f05b8716d10600002a8e1b968232b0ad845117b5a090ede4fe6c81cebc655e7781359ad563a32ca182b2ef2f464d1c94aaa5452155731ae564db86ee4e11d708d0c32657165aca24b0247842b3f480a187b9aa6c5b23c1d5753f73eca75d5fbba155c3592b6c195df385e63c3281891a777fa48ab5a8787c7ba71f9977f79d40dae7ca8e3315af410ae88c22832689a669b6e3c3b8005c2eeb0df1c72e5ebb006b039ec0fd607db83c56179b005b037ecb5362c01ec00ac006c0dfb59493ecbe7b37a3e2b0757dee0ca287ca0b7fe8bfd01637f481aece7545cf931ece7565cf935c837d9cf9f70e5c7d8cfb5b8f26db09f53e1cabfc17e7e852b5f86fd5c0b57fec97ebec5950f00fbf917ae7c19fb450f5cf933f68b455cf938d82f1a71e507c07e11892b3f07fbc525aefc19f68b25b8f275b05f3cc1954f636bec175770e5ef60bfd8822b9fb8f2a7fd620c1af60e1fef0e3f561dc7d1b0f446243065f5c41870caf2f19901148c92e1094e594992c0e008a3626801a7ac9f1f278c30aa5b01a72c20f9515a415046bee44ec0296bc91214e8302aa5049cb298303101138cb296e094252444821c46614870ca1a1a52e2ca94119cb29c3839028751b4084e59ab5508aefc38247a8053d6132823ff0911577ebc61948b2f70ca826245d102a72ca22b70ca2a2a6294cacb172fdfa3308ae5e53b1446b53c94305cc9852b8bb270e53b1538356fb4e0d41cf2044e4d1c2c3835894099278cfa97ef2b46d997ef4e1835c3cbf72146adfc8d15864c0187481637aaace014fc813252885157caba813212f532f29052520565e47b15577e95c1c2f819acf7315819beb3317cc976bf626778ce962c9bf2975d7994bd1eb3d6a72c060343030d3162d45083c9141363830d37dc2043c6e9040000c8c8cccce08043000290c30c1d686a6aacf5feadfd16fb1f6d7d16dbf22f2ccbabd8174f52f9ccba4059d2a72c1562fe94373d30303d3e34d0e09324468c243f35d4f00364320105c5c4042db1c186254c6eb88189900c194243a7d390130000c0c94a4666f56466e609141c70f8afaf1525001f008b2887976f15cdb8d161084dcde76011cdb08a74f8af6986d4fcd77712a1f1f2e1cf0ea8971f79522f5faa684c223bfc87faaf533c35653f79e7a3ec0ef6d3ee7c1ab6c67ea53b9fc6060c08e2e3201e170f2a142559dc5cec5b96344dd72a83241449e00782e411244dc3f323819a86956481a4fad030218b340d6c42662e6347bfc785792bfc36c61634c5bfbf85d2b4502f08306062480fc110dd1ef29a215b8e1392b82e6ebf0c28b77f865e2e55ae456bd489ecdb2455594bd5aa4d7cff93aa0927bd4c97d71f07a54d7cb1a63f55fabcac8b7e400696a6437ef48ff6639f194a67e80cf5a80302327068b9a0762b1e5cd3fdb00f8d1517988c2d0ba463da02f3095ce4c11d733373f04636d1441352a083261091852500c1082683ada5d0b19ab03a09b564741f1b3cfcb0e36a9fee253a89096e2b19e2c2c5c2ec8944e421288d81fef7a2021bf73af891330237c4ca95170f38285af3a2df835ed3f2be19bf78c0d1ef182dda91abf7d1ebd43dbbf093e1c5961cfe43ac5c29e4aa3eca3829ad010b5450051348413483254490b0020b374f1899020911de854e091472f0a698cb4aa09033e4244a8139c6083dbabb3b7374b7722cabd4a324dbe78490debcc0b2a4bc6c14d57d658815d92ea57b1468d6b1d00b42e8ded10931ca187d7a9456a06cbfa8cbd82e479ea3bcd5ca985117955718e201af2025383ae9c32160ee0f9e0d1f7057153e4cb80b0913089c1fc152829397950c0de17e1c644d81ebcb4a867e6e5007f10882010ef84cdfcc500d2e3883a19edbdd65254328b8d96525434a0c2db9acb0b2b0e4115a8cbabb3b0958777737511260b0a4e87a6a1241c8b0a0318340e765c9e891c3620796122c7ca0040b15164b748c21a1b258c1f1f3e49fad080f16f8c7dddddd615fe1eeeef0dde18d5be1eeeef0dd615311b4f252c8dddddd615fe1eeeeeeb0a9707787dd56b8c00861042d7081e96c50c20b96d0b9bbbbc3cc0a7787ef90de7e7a8419ea34dcdddd610b3984b0a1bbbb3b6cd81fbffd0aea4da2c41398e088289028e6fcc008482424736010a23044e608711690496f6e6e804c189826de334042820b5f68753d71228a9550ec264cea143ad06122b4b2d2a4b3a16b52430e51a3896571436432abc2da81aaa027ac1d21130b101490989a44af42ee48f8424343425dd300891248ac8206c988104830048d26b19b4419ee0584603e980f12669a0cc141540ec141420ebd8a28a1bcf023131cb2dbd8022e3e905e01d2f0e3c38755945ea004185cf80e6314b0210ce2eddd363ed422a4358850d20641e55218c53589a21c983641ba84ebda44654b10d606484a44b1ba610521df7acbdb40a5c20d29e4065dc2c536411f8421073c37a4ad54b8c130edb910efd6a92dc3841f70136630849d8329a61ee304ee7750254482d09de919349070330594db7d23fb511fbb3f7c5c40c885cf1a10bfd8430b045ef9f5bf9b0167fdf536faba3e88df2bbbbefec832cfc7bc3db2bfbc1ed5f3312f7c7a2f7a21bc7ee5f282f83df95b1a0e27347875c49b1b3d8d0817c1c0cf2a3ef0e577acf2c5d74a9b2cdf8fc3e447d6d8636d082b1dcbe5cdb812586dec9a966c24976345e058c6a7500bb565a611818b4fafaf7ee66535cbb2ea4d4bc22d662a4a3e867934ce80e0afebbaded2705c2b1d0bf4622ceaf7669ae2579bd8c3ae6f0cebbeb83f16c1656dc2324a443fc1f18f5092eb8f55593b1646c98f31ccffb0af5e8c2d38ec690eec7d87900ff368e493bfb4174cec2d6a7561090a821f1731b42ccbb2567ac049539be42481127ee0812b7f07a3b426b8cfb4a203eeebae7ccb6bc921a97b1633d7987f986db8e1d1347c2bd0347de503c1b910072d903ca404073a97a1dc86968584eac2bef07de80ba306848584ea5adf9a0d7df850b3bedf86dbe81a330b07f5388fc78e364993c96b7e3059f2c5161c2b11c2f98058969573012db1c4124c2cb104134b305182122411218e106d920e877084bdc5e655e4de84e093fc3870f9e3e5d8359cbc62a4b262c36364caf3674dc33900cac867526af4fcb55a587b874f48963099976b8559c9f64e36b238266e0f0d24501429954adcbbffc87fe43ffad2c7d2c7d2c7af2a59e9bb2bb895954a7f7a55faafb5b8f2db490f354d29eba151d574944a25cf7d873a1177d3e1c49c4ce3b2cce2c87e1429ed8ec0a722b00c50907f6c1a17596c9a14984294118dbc87dc278615dc175525ca715d8adfcac2523b26701d5551e535ad22bdca8fc748d37fd7f950ae7b934e09feb0d0118ab0a463e43fe99c08fdf06987e33848848ca8b7206254ca977de939cf38523b91133911b4cca7148fa3f7959eb6c9bf244ba51f79a5928a2c79a39ccc328d9f9f187fdc67c77f18e53e4de3dea5d9f88f7ca8a6c3bdd1a829e5b891e76265a6878686389b5796be81dabb1fa6aa6499c5e1bf33fad1d7952cb3b06bd034e8200b49765eea835f21f8e4b7b6e5bd7d1db8eec51a178cf294776121c78cb894ec5952463e7ab7cca7d1e87d8ceec8e3c7349bf9232fc8ce0f912b1f73a1e588b9f545166d3b41d24241e84d79ce6bbc0835c241d173653bc971228ef3c40130d558b08c0fb9b2b3e02bae24793dc427f92c4a70dc51e5f47a9afea8d46cb86445cb14a2110000008000831500003010080644a2e178244af354560f14800e74a24e684c1acac45992e3200819630c018600000000000800199ab11a00ea59035520a784a3689e404f64d1040249836de5456d9c30a8c91a008258a23c6fe9f80f8641ddce5c3832dd2e06b42287abd526f81175f47139b95d0cd9e964a0d1553f67ffa906d693e86e53fde4e66d959203a65ba40800f5db48765ed253d6c071b4de6807a3ff11067493ea0385ca7eb41c03df69505b27218cc9b6d1fdc93bab74e55993ec525c19209ce46eb48263b22f131cd6b9e411f16b1c85016471fa0b35a9269a02cfbff52482a93466e177868bc9aafc724afa5f48c49809109c21e3542017d488749851d6eee3e3916edc8acd46e28e24010ed9f557001ee8dc37abfe4c02fee7503ea90b5e9f02d8f66048bbcc5dcb97e01622db0a240577d4136c1531bb21c96e89e9e3f7bd6262b24757671b502258ac33767c44e11a089204169533c53bca7a008da588fbf2fec6e75e959ae8953a1a0d455db7eed170a6252d89291d0707410b8adec3d1580e11e110363cfd32aa5b378fd15da56ba72b6202b3315331e87a68e2326ec8bd2aad42efdf9d4bc0816b77f2ad3b9bfdfb98932577a8875b7f9ec7b5d2b99767ea989a712d87dba9f2058e61b518431590840dbfae20420b84f616469bf98044bb71880b3ad53e59a17e31b9d1c3aed0242d580d53678d9491476a3c7dbb98ad89b4f00db9c08b18c4fb197b9f6c9c4de9b17ef40b8da8049f13d33e78841468a960ea3a63a9c8b8cfebe3d607ac43934ac6e2a856f62e9d8e186fd3f1acfd316ab290518ce0c078988f20f99da9dbd7df061144a03ed36628ee83b80a9c897064158f126e359facb00a5434812e0a9def24fb926909c444b39dcf79cebca72b35bf2888de37a6060d6d9be6666672b3a0dc4ceb95a6ecc71072f77f82c1fa9d8a52a682f72609d7da64c0c76a334c3c005e6ae433c340fc532940588488440fd43fb19a8217049b09c6fc6ba6c96a9fe257a94adffed79a6f4cbdd91fc50b24e7bd14d3c49b16171a2d5dc41ba7e415226b21a3df2f9c48185c25272f8d2f91c544d08481b7c5d7c12a379c085f6620e960eaca9e98d65620f0658f25f4b1f5a88d80602730ad8b48df6d1db03ee4d7e1b0923f48691db3072b1b84333f33410ab341aca402cf0fbbb586d170422bfcdad5aacdf9bd76092338151187d69ad1d3e7aa80a827f6e9a11ea1ceb780294d90d7c09d161045c5e0c8b379a57c09991688053dd2a4e3bc0a897a94deeec6384d0db58f435c9dda039f2d49d1a1c385596b5fad9a5899e97c0d7bdeead3dd0b95d7fb59bd29afea60fa9251eb3b3ae78f4f213cfe4df70e60b43b278d92f113c6d479bf4a892be0c6e5821ab814780fedacad4a3551ddad18b93e7fa20516f7db51b4c69ee8448927b9a356991fe14bbcb048ff8c414484986473014ecd3828785b442da5be0c4d3d9651f3921de965697190a97da7cec070162756c703bf4df8e45b0baa7daa57a4f7505001dbbfcf5c0d343d61f425ce2b2151e076297cbb9b8c598359667929813bfd7b84f83e4bdeee5704b47877c7b35118f0ce632c33cbb1483fd362fb55ff7707162a0be490053291c631881f46ba2e7c946a3aecfde5bdd52453bc5913f11969f687184907d981b493b6e6acb15072282a0b223981e8ab90ded393f544d9e16977e0d9f21328b86b84c80594b39488e5190b208ba1e90e6e8404274e6dbdf1d27f60f76e8b5c28fcad5a9428b575288251acc038a05280611949f286e4e46031ef2e46965cad3d8911e09e3e3de045da3713d945293d50658a7f3f9cf0280eeacda4b2774d92d5d990cc7747d92313c44278fbb51a9e16a593f146c586e57b3aa16e609dad3f1137ed65c2d2f152a1bda031ed84b236c6f7339bec41f90957ab94f26afa5d3886fd1496df4f89bb01fc2585dc7b75261db56daff1c70fb03a7679ef2d1f635c857cae24fda97b6b0540ad6577e4fa29608e58fb3dd2df437c5aa847bafa4e14f5605e74666c9d86c76503399b1bb0a370faa1403cc20abe8318d379b7253035af388b3e689b0f2ad7aad06df1042bf7a2444ac009b96f524d03d03d5dbe371781009f7a2ed6524b9b956a13861704222afef5afd9cb64da65e56e8825dbfdafb7dfcfb6a2b8e88079b5b7a2c199b21af486da7cc012f88ded8f73097738a457af1b416b3aff652e5c0b2be4cb0a9a064efe9dd229dc52f17b72f41fa4c4944ec14fe2afb1d015d218c2618617ec2acc5d84a9892f1e2207d855921785809f99300999117aa668f9401c6c913ecd1651a98a05fa6555817162ed9a6aae2928519f52183e2216efa772cc97064915e292c1d297a921505ee8cd8a24b8d7407628fd2ec452b5acba8ec45b53d7d18f0bb5d057aa4a827a5644247d4a9e950b6acddb3d0a30f15a4a979f013ac2543ee71bae7a0fa79449f7932f13c5df41dc2cc723c8dbd5ca208f817ea8f814431e929d09bf3d841008da748de511e1deb238f69a9e9cd2c0764fa092eee23d95a24310dfee30ee6adaf5a1b5ed1186290020ac0105fca117b0d36cebe75096f46ced525d64011639b250d34a27c1e7693ee82536d3c5d7fc6e1a54644f445aa3f6a9bb641702fbc4fe49e3f652e56db5ce8aefc4443783cf9f5118dc8919f61aad71bb7f8ebcfddfe35e0f002d80ee851ed0cacb7098d43929c9507c493e6e6d6273ce8d8b2f1b6f8e4dc3ca13c3f97dbde2d8bb0a7eeda1e4951a9a9d2728743ba27a67890bb205695c173015e84aa6dde16d977def4615ed8029a405404344052ca009a44ff871fcfbb36c892180bcfa80c2818419a037bb9691a5332efa9a1df8639059d7303b84393ec74cd0b4b4e23843653e0dbe2f06b3b28363e9088cc5f34dba03556d3cd54113b44175e4a49b192e96a033f24c4766ae2a8b036bd32f489ceaffcdaee5e07983af6a3c020c6161bd7fe1aa5f4e91513ec3af14e1559eb8ac9b1cda30e0b961d28f60dabac073353229736e8642fac70421df337ac3e3f9d8776564c9d0d52062ef0789e860e872ac740d2f8eda8019e106b30fd36aaf13a47c5beee34cf008f4b492040145184983f4014e9a3998dd3d91424952521a977301254aa428e1fae103efd4d0ce1be50f6721a7e97d9b27fa578229a016ecf02b53e255a5831767b0e0f106191fa48a58bd8227364a22e5177b5f4f7172076fb48cefb4c51f06fb41f30713b627a5492937fd847d7d49929592ac4e978c831ccabcd20bcea01b07434f95ca533abfe4919e9d8e106dbe632de814a6142eb67907b0a8b7238602f664199a868dab208c65d8bd49f19905fb97d003688a66b36745247c24bfac58e0173eb889451cf512287d0c5333f60ef2f8dea8d42ddb044db2a261b942252520898fc0806c66710dbb7292de5753420704bf208778b91c14440840acc3a4310854f20c7e6b8e953731e7937522cb60dce6a91125b7cfd26799a957c606bea30de0d4c2d96c07508d3545a2e81d6a5be470dd292af457ffc064715460fc772445fc26be081a31781cd0c002c139fca58b8ba66d1af26e8d3fe34b56f7c5775111364bbdbd892a64c768cb09e090cb718fdf2996332390522ce357e3d2505dfa91fcd985acd3a4f2392d81b06a516816b6b475bed07e6a9806e01bca2022d07b169ba9c6f340c3510ad368272e4eb09d0c411370ce6092ef446e9ebc69a42500b71d6028890b75d49c40870c1ad92a8254860b120cac978fc428958b390fe89aacb3d696c340254d224d7ba45b3d86a21081c98d6cf90a131aab3c2774fe3185d1f7555b83746bcf813e4fbc907391ee8d127d84dbf8415ef66b5e0178dd332db01b944a5126adf5432a1e3253bdb47e668d0493c0b42dd15847d5ce0ac4f8fdc68518bb1595370632daa6446ab1dcd53f5f0297ebc94884bd599595279727337fdac6bd305d605aed6b7e040b9a7650151eee7753ea327d2335cc292ec8e8837c1c417dee5c8f3a72ee870b11f3b10a7ed87397e037ea6e5d59e327becf662a2f7164baff8ded70ecba8a1c7445d515fe257454b06c420224ff172565991b7094272b0aa7c34b432b2833f786a77ba769193c3004dce6af003d4451e0340eb75dc0a2e41e140313f7ccd2881e711a29c06ceb44b9d0e79b02186b7003b78629964a7ad369688de20023471e517f1c1fbc803e8614cd59aea091f743a69c4313ca2ae71e384f5d30a43c821716d565404b30211684c0a5ca15213c38eecac0d4031785a9821e87d10513ad862e3685787c3fc12c814503747008d1080f2efa14716d63dff84b978780f22008279848b8ee40c5d14a42a7b8e9325295c081613270b7fe8c953c751af899c411ece167cdfc4bd389ebcdd53916c2ca1f1d78ab72cbe623574174d3097f31303ce2bbcb701cc5850a37a080ae61a47eac6d014c996a3ee9163ddd29c18cf7db43b8e885e1baea8b7e6a037a6e14d278d251cdaa573d4c02da7065d8521cea192f64e515173044c97d4e0043f28619739fab92c13a904567e752aace93cfe96b6075917417e949064ad072ad2911c041f6a679718ace64330172a40b2df6ba90543cb5805cfd6a0c6a77eb7989958765838d2cf1b8a2febea623979931817401b8d6415694d97c118273f1ac7ba8bbc5fc21f034e0d43913be271b184ac3e12200dea379ee7a3360ea1c8e73cfb63153b181e016c669acdeff2df836ae5bf8f5cf3371a2755a23ea824fbb9c1d869757f2103286eb88d91e574f2e03d994bbb44c81bf27642af8a5f88f0b7ff9707c8527b940879003a10d08afa00f7c8c15931a4a7bce8775fe0360f834aff4cd0b289012064e8273eebe0341abc3c81042ef9aea960d9498f9850b89a933c272cc6c44d33c98166a88fd441b32245b79086400f1837053488020256f5819dc935b240a03e4d25529c56e2ae3b9b247d6135db0bf1f1f9695ca1b71ca0f7b612e33ccb3aa0acd62d5a99ae9307ca736b5fff06330d3f7c123346c9a6ba1d5b695d15f5aff60d6b88236fa1ee6b4e1e30792b9ca0a8411ca91f87a2b5759b3f2c282f2e84b897100ac8f08d8aa5a1a82d371d1dcd8349953157f84dcae9889001bb13d81ea94470a1860a8388a140727a09317f325f7cc1e9c81e541288f52a3c64465f7c555bf7a133a2cfcb9331dd855e19c75f6ae808e80ef1c0dcf9c1da776e0ef57da380639d261781b675c3cc3b868f65caf8c70e29caedd166b697344e3a47ed00ae7a24e4e192ab50f5d8d8f6da94da727993052e9fddd51a9a6085ef81ac5d3bc04b927947328fa60cc3f908c745dec1f02bea907f42d5acce5261d7165e4eb68f72839fbefb26d47a318e358db60c654cfb440bf1244de8992b9731aac776609dafe01c07a1074c761b83af01c0ba1d5cbe2eb37faf5dfe6d3ba9cb4d573e49d6544e07f5cd4ec19a75697bccf174226ef8a587928865e788042e09abd1e386d0d2fb4633fcc43a854aa98be711501f08a6ba3e54aff0178bdf8f1a3129fdd574fe36b2b3a55d8d3a244d5fafaebe446440e373d8a0178a5202a1fe0359541817688a2524df413a585dc8b964af7d8910bf0e007aa1badde2308401d4c32c82e57d66b3b1aff84b04de32be74fc17cf1a49f4ef4c59efe4ae8394faf72d1ef552f672f2f859e625aaf26d21b59534cf433917ac511a6e368b96505162f2a559dd18e64058548f626569cff91b5cf7952e3b4d81b42319e0eadf5183508688056df7b695ccb825657c3c708b79b4418ff7df5ecbaa8338710c310885bebe5d37ca5a1532eb8bf877e43f355679aad4a3999abef602515039c34092df6194b4a081131589f71cb7a9ac76522d38ba7909e8bedf88bc38c4d87fb3b48717c9027d734aa56358e54151e1df1cc87cfc191225cc5a4c0b93dde507282997e149113f0281a0447d4fe0ed1ae093886e9a629755b30cb4697fe5a918eb78ab84b5ddc82c1c7ac94332e533488ab2a1ad8bcc45f8a05d579d72127b2bb5b349cc536e290c8b49aec1a67f0bfda3d43ae19872ae450c6d6eda6262c5f446544716346fedebfb9e9aca293069947d2bd310cfd6801c95f33b828d762bd60571088eb3bb1069d268a3e1063d64c547e594a2713419c0f39537cab85e1ed7a5e74fea6d278f47b73e90e0773eadfe4cbd32aa89ea45495159ad18ac12f614c4285652bd5c6dec66c25c30b21dc9f72b17b9ef431ffb0a58118566aeace9c2fa6a89f989a40598a683bc71a98faa66ff501dab2c0c8b9abe3d1174dc75de5d901dac0df23865a4a6342169cc7d7cba409eb987d9c908d72fa6b2e126d226134369f01fca4b6856758db7ac09f2a648278bfa5dfdfa3ba4b4922983a091545eadff7a81b9a01c78abae06b03948fbadcfa69081402725bc26f5de980c1491c8bd0c6c9084ce798d1805c8616cd21079a134a70ded854eeb3bc5b11dfca14842c6e8bda69de0bdc63f6d722bd571aac31cc0cea7c8cb737bd797edce947e938d99845187cf9bd600a61aa8095019f9aa61846942bb8d57f64301c87e41e145e6b84bf909f7a5b3822b467ff9c17a87878a188c5f19f628661a48033a42633f24064b90dd62c54071928611c92045b87b40563213e515ba96cf8105b014cce7e0c8a742ac289c63a5701d8a973b16581f0f6fa73af96c06f511e4838c7bc94e4b418e86ecb81505eba5d417989917e926398cd5f8732f363e892f4ab5a99e0cf79af41e929284160f444411d5f7d07b0629dce8d485b4432cd828c09f9fc0b9207525f1981092927bf7de38bab2d7282df96d9be4116a94514b7b93571ef52bbc1862787f869a679a7d944d14540d0c3844c4f134413e678dc373d421629c7eabe24249ef9f66c46b9f9b0399093da95d59a288995acdba9c9d46cb3ce998d3f868ec08e095d0459c811b1c1be26cbda0c907b36aa1864acf48d9778090419bf5a775ce22eebcc63448bffaa8d09504fd672a0530fb8b4635b5e538008b6cec593f44f68f05f5ba110efa462b87456f9cd707771c0ca103cd71cec7c03c1cd5ef6ee7ae16c9453bcacaf5948d003568969640494c8bdaea1b05e5336f9da638d647bc2fc8380845a9a24338c479c9ba433caada29419cb3decf24fae48d34760985f4734a271260811c3898562a621fec72a66caedd9b80824b34dc9ae0227c18a0cffa7e1e88bc50a7780451f329d32f712b1473a116db6de6a45d66703ca6590f523f00c0cdb37b8ecb6f4072dadb0f72bac3ae3631519ff1c2c9ce5a2d3b24faddc89976fcd951011ee5013291da11b6ef4c67ee88677b5d88cb14a5b13d305b9f0d8ba836715e5c3f39b0e602efc6db36c8b08e84e86f94e95648aadf750e926d4bdf98a09c9d0904a2cc6e80f8c00b00a26aca6e1106a98f7125cc18d7feb8f9720feca8167b82a902b8e1b959eb68a174681e1a27fb028e2c558870603de96f864f8f3accbd63ab22afff117ff29bdc49b0dc99aa9b4cfcc844ca8b9f2b6bbf849c57fa68666528c6d3c90eece88f3e93e2bb156d103281861880fa189a251682cdbe3bf5524bb1b0a695cd1133a5e1b77bfac809181f4ddd6e7cee59ad2ecf40b700424087df1279816b5d52cbc042da4ec7f4b66186d75245d30ab56cedac41e9283b61d18d3d6ff49476e402e79e3ac7554adfa9db43f3b820ed25bdf5f0ce53697e1f157d89cdddfc4e8f1a76049c84ef100af2decc226f4c609cd1472e4ff0c65e6f90bf6f1010c8e0a345aee44684796c58690722a3f6d198f5902b3e610e36192def1476ddcb1c27ae0a0d152280b269783624f9f8a2cb21259759da6dbecd69c5420dcf47e545a52cc93dc1ab863d472e85c31d445096a44f4411eb362103dd95e3a0b4a981cbca0087db95926bb6a0e336bd087d5f09e32fc809e5cfd1b901dda5806d7ac3abd39a8a9fba18a1bda50375fdb533ed9994fa72aaf85cf848a9180b89263731dbba980d6b231638f87c030eaa61837a4a24be02b642c4d28c3069684ed6d40306162e8c168945139f1815a30c65b55052fcc9367b4db8d7ac036e93fea8d4436a33964e1aec397178ee7c2883b2b8efc02620fdc00b17021acf1de224642b006b5a0a2a71e3afcc2857f9907d64211cb09cf2babd9ea0387bceeab75ab3d0646928d662ec9d5cc54201e1c18951dc1df0b0c864e72e9fb3d2c3dab30a6a7e95d42eba1e9c2e5a9a77229f2c88a6d98abd131d4a5e88162ed8180d0ec4bef26f3b218b499f81b3f5da46a0c1b1502cd05a26ae6dba6611c926b183520d5f1bc7cc855e5734ce7d32ecf030f4d988afee992839df7f2da7cf50204a8a33d025c6aa5434ed5e8bf6a8470b3f054a7db3a8865d7853b636cb05919e5dd03c1f81decbbe9d9ea2835ab1790c9058353ce4ba36652fea75b4fd9b5ec79b28bd6c1b26396fee7003664e568180607c59a603a2252f67d6d6b3fc2c69fa9554a0e2be76f50644c92322cd42c784a6bbd8f3b46690e906766b1570045f7a3d67cb7fc06396e4de09c961bdbc926bd5b7c5f79affabef0bffaf472a4b471c19831177d675162a06d1f1d237451caefa9db4073fd91cec5ab71f63ef357bc5819e5eb4a6e91fd8d3223129d39f9c04e881520e22aad84cb83aaa1e4cfdd4c16d4ed1a35c9dc24c07501b603809929f691a2ee3a5c541987fcff2859782d040324439804bf3874d19ba852cc61b6be03c8d4fc0738d7f5a3f865791ac6dd38f1e0c71e46f3655f35c20e944ae21fb473b23cb122103f0d90b5665eb4bac11023703b7e56e4d9372a31be165fe396e7938004573d51be261d5f3de182fd6ab3373dd8ef46abfe61bfaa7a65f292f641351848efa954632ca5f8f7c18de448dacf50aed763439666d2938a30a50642b234bf0797e22093b44759c8017f35dd56ed26a061392e7527d02ff9cbdfb3c7f34cdaa3c2f3fc25847eba315d35a430c5d2371ee0eef50eca099609bdb5b085d9965963a97ca9c7a189ee054efb977ecdb27ca381fb613bea0e6d87c2ef12c0f20d65f3af196d4531b41b580277dc1ecd57d3e0bf950628533be547c5e0a5ddc05ed17c1709a2accd7c27465d4e527d9bef514027755114ba90a2cfda7616dc121e5a64b9c400ec1ed1d7547e441044bf0ac1bf732835d542f7303a312e29a62c61d2238a71dc997be13a8a76e5424f3b21b7d211381904e7bc3d7a64cd29bf9c29e7daab3bb8ca49e4ab4621b22e9851fb9deeb6f5b547c6608e8ee9ecba63814e951820ceecf7423c6b2024422dc71c4902d8ecb45e62b9e7a8c0c5bb480c59d9d78a6bfac3f4da2bba808a796558923211a70e8a664f6a70844b28c6b657b8686a3a42c17af7731da762c0e56f2282207ee160b2ad375d6f30595ce9fa26517cf9b4707c2fa0f858d0e4334b77528a58ccc7e1c385fb4cffb6a7fdf5e564e56e228aca8242168f95beb505b1261d084c8b6e788d2bcd55a8bd1495f1da525c29bd7c0e22adcbbc4744e81fff21565806924b429e2b37773f8273eb81da159273b59da052cb344a61c601ebc32efde9a649d3aeffd20378e741969d28c8d553108b5da8432c5b5820f31f7d1b2fdbc759f45ba61f578fdfd6eb71bf3c030ae6535850b8e77801df2c44de3fb7875384f54c46406593a7fc41c0e9ae5c985933956e282513b3d7071d00edc402f242f425851d3137a1003e1eeb81885842283c8bd11c80271aec3b4c87885544a180e275c6fdb7750bd116000fc56618442ca2cc1286eb007d8aa5fd71048f143c62c1d84c55f6a6dc997271aa62ff4c5e728650ceaca61a770ea19f4f17df0d9c529e83e9658c0634ea344426f8ded0dd05d5301a2c156c0f8a2a7c8374efde8c2da2a8d36fb0ad11746bb8a9d3010e39e483160184e87656672938b4b82693bd5655b7b1251c631be9bc1c0d47df5e3a924b1ccade614c55d3d504318ebe3d0e487e9c69dc300e4612958d6d4c6822768ae26302db7520ffcf84d308b173cb0b1e33e91347e20ced8000b1c90f8b174f2bc32185124beb7d34ceccdf0e45677b7bb212a76d0f7591f360f4464febd083aea8094aa808f83960d3c0802038c310a8b956bd0c597cb45f9acf95d2f10bf4c608a03c2ab3e8151911c1d6fb9ae067100118c262e1d0af347bf22c02c6ff046c079b3dd573afd46d6f019f676a3d2e08b7196d842b756d2c3883af84db34c07257f2b8ac6f45ef417ddcf025b3c76141cc90016e8b44ca2462d4a8380eb414d442d7fbab83b06006cc90fee79b792510311a8c116007bba3c4d3909016473786480de8ce674c9304e042558c15c9a504d3f1c4222ff12efe48cd9448b1167f78727354c23326e92d240914913814b08a73e1044a51819098810f4202b887f4734bed141a5d854abcc228dc82e0313107c61f991274f9ef812531752723d08ff2b6478ac92b70f2de91b38b95eea75a45c72a065bdf49c1c0ac8d918e4376e9e0115a99d4e08c3a684abe941d30db322260e0d7196572726c23bc0a53c3e1be899ea7972bc0b1dcaf6aac010bf75d31bd4baed41ee2fb9abff451237e2ba97fa5afc31b87eb6515885068a591a6458bc37f56a2411c873fc9adbc12674fcb08b27053cb09b02ad19da6f16131fb5e253d82325e2366311da64bba8ed3f7bf490af5d97c49f88a923ff169fa7a1710cd613a0cba369d3003818f4a0c972b3a7368f5ccd6e3077a4ffe25a3e28ce673b82695ae2aedff9371744d7e3eda0b020520388ba42f7451b303fe2aa0763c3e644889cad0a179b65782e3a304e07ccaa4ab0d66015653866687f951d594a5c8ed401afec1b44f41ee762311d8390ef57d2f71ac9d16869e851012ff03735472354fb0a58b73fd7fd6796416238374c7bbaa102f802963a9b33d25094fe219991c6874b67924a0c5737b16b9d2b1c633625a7ce3d434089219c32ba26079166f07d321f9217b74cb2ef356e340bdc3d009840e6d0b6607072c264f6f777e6e2c9813df80243d227b73a98a3f6a7b17b99db2de6638c5d8f9edc05d01234f6e08b8813b039f22d857c0c87fd73c733472e5ede1bb3b20b25b93af03ee199f3392d2791bce89cd9287954e1f8bf8ff0e6af88bcb4422866421a8247053f66110b52efd0e2af6df484b9adafb99f3bc0c4145363af583815c6b1c6f2b8920cb8701f71463520849d991c4424e7a7e5b426339a3f730db444e672c3671c6e7c768ec42109e82e8aaa7f28c9988e0acf2cb0149fa3c358cd24f43464334f598e8f7752e34cd1da8222fd114b514ae19f98f414400285b9fe4f432a1c00841d0cefccf6ae30699477ce98ff3536404217e7907832945bffdf46fb6fdf30bd4e38fc6b79c32bd4f92cde4f13db6c8ed7b88a97540309030fe560dddb718cfbafa1d2ffca2970617442a325779774da6c1367688d20d689d40cb4218b8e80f1646bfdcab2538990acd09c0536c8dc2dc8dd09e810c100159026c9bccc02878aaa32b5d40f784b414d279d5404c39026fcfb43717c3308d699508ac2dbef864744eb4a7a3ed36b5b584976fbf61b42237a998cae8ca09033b1b051a54140a2f05880b3780a47c8aba63c3f870ff0f8d3c44f25d5710ffb3964b48fd8218d9994ab053415c8092b39b940c563beb27a50d225d09143de1e3259eb4985b0e4262dcd3544144ce48cb87f5264d5ec0ea0762882952cd2187baa8892af7a849ad8a917991615eec52cb3b3e515b6d742662c8493c63fa016b757049443abf779557a696de3740fcb104a791ee169787c2dcf30d52cf468df4be812faf74588d5cbd65bd303a69316b2f0c5822cec20b83a3098662ad9e9d596f4dccfe3f546b329ce47636aecfdd2eb67d6abd9ea6358c5f52392fd77769d7db16b53e4fe976121a22f64f6e8721b3b1abe700ac403868f462bf58defb3e4566d81b5db14e6d58ee382f8538e0f38a7dcaf65c0d070af65b88e31623e70fe73fdd6a9fc9e8de3d14bf6486aa64e00f8434c3e1e5df7162d68eceb4c4dc2aab777af14eb17bbc627d9da684f007bceff7474927e067b24a0dae20f02c5da9a98b31b70e927430d1f5332bcb5228af1fbffba7c26346e49147211f81e1df46c04bae484d70a20352a08aea3fc25fcb01807bd0020e9c5052f2e1751327742f30d00e984126a85ec303cc3012628c781d081e0087a1f1cd5a6812911d9522b210e28551ea01ed3e3e25429b40292c5d3f009ec9b9b6d642e46744e8ad56300bc52c5e2077f14af7505a1132c6c859de4dd6c8186aea3c508a4c06153dafb4af1c9d0dc3de5d3f94c9dc6d7fcbb6dd4d27261088f805f5a480c8ffc150dd228e9b4264b1bf6303be17c7abec669cb9e9a79745ba28070352796bcde752b040e3e49da6997f388ba8b1cad4268d99010a2f7f7eca6029894d26bd8642bf8ec0805c186c1bdb6937ad4947245d92c11028f49a79bd872df38d4e87b33a8d546cafe302ff48daabea377d6c999a5870c24609cfa9bce7c445e650798de2ab6811a4ccc3521e8c35fe8207c49ded541da6ab5e7831d874f6f4c489701ede7a887c5cdd9af46d03b46e7a63548ae9820a1e42a29ad747407fbfacf6243fad7c173bbe427f7f3ed5b1fc1864e5365f5f97b90858a8c565899aa17a472e5b1b7e6bee8a75c2021e46f61a4bf69b60b70cf0122341853ccb4c7f33a4f5aea4381f381ad6ec092d0259a112028b8ea70ea442f8f135fea2d58122ef56669e7804e7d51b8efa1df442405f018d34c884dca1077271c5d8ac62f6aa6e492dabf01eeef185156eae9af1c04093e93b210273a81b98227d118d4e0ac407e62a6beb13fec0f2a76d5b05372fdd7a47bdc5771b3d9898026817779a7ac826bff79a8120b11bf18aa5602f691d46763b5a5836bcaf473b9f21d113492e000404000e35fb2046fa716c2129d64853cf7e366813bafb576c0406fe3fc5abbe4cd0b8ed5c65028f36b806ebcdea3e3c8d2651134049bc124cb68fba0e3b9ceb98f183dcf6b9cc409d2eb87340cdaef0bd93d7666b58e8b4004ba77d6c3d65b108b8dd23ab4b53d08a2ab59b7b236d12b302bd0e5d10a82ff3169930b39b1af4e01a26200a5768a8d6dee31fc3fb3ed2aad216f3066c9bc5bb5fd4e9b5fc0477d2e381df4f10b95fa0f0ebb89e2694327b7f25b698cbc8ba84e3cd9559a0f862c72bb292b6d250e17131ea5534c2a490f78ac04f640342e1a96e894a24b29d884a6e4e0a438b4633b2de414488d0e245a2355311d1b4085a6694ce56a45110774084ffb846c50a3a485c2a9781815ef6601accc13d5a27578908320c8e16b55fdc26e806ae24b2a41c2c76851c4fdc1ac7206fd0f429f7ddb57500bf9f2b424cf72122286c52c1b9692e16af9ff4dd4beeaa50a18b9591edc648bd10dd5cce12647e3cba97f5f3164509fd699c0303468d88bf2c3fe9a394cb373e43934961f9fae2569cdf2d403415e9b3241955c578914f8a28ed2a95854f070d3b83df2700d10e0eda53c91d9200c1013aad2d61e92881431b135994200c4a41ac6b76e7b3b7a39021cce83e16c4c4a6040c12c95068d91de0642233a860371cea4767de3fc6d6f1ec79eb272f59de114781b66c6f5e3659dd3c304563b9d69a142b68185c3c21e53b733915da26f3e45e0fbffddaaf30b4901240fcbc34175ca0351d1744f9d743e17643b7390f183d5ef599ec445cd3052383d7b18055457ae8c2bd387c7059de7f9583ebaf4b329ad3a23823a38739249867f6074c702e8a07b2e746a9ed81cda545bc5adddce863e7f19376fafe9afe434cff688f481ecdd3800ca51b0ae223652ed3725620b1eb00f0dcc4ca21ffd6d8e409029322eb7bc033d6f31ca9d1305e8b95fcc7c33bd13233556ea3e56d5245a4179aa50a8be0d3837f15d4433919a077a5f7453d1abb63821344af4723f6cbaad97a978d89f083467d5ae01ceb298987bfc9394b8c9834c3559ab21b168dc828be04787c2228d4cae6de8fd29e632a27d450828a5b36330f6c5d2663a7011bd47eeeace7a4ff933feb7ddf70139cfdf53bc3584416732956af341113c4a186b5a60c8461a315c188f93f6080b1885fda709a2f6b44ae973407c08b3525b04b70a731125dfd956b30f2e1127f7df4204b38015778ae988cf989dc1c07e8b44e2bfc641c9519750425f289ec2196b847fb2d93d000d699d6aad56ec0baad63e7048b2db71f9b6cfce1fc6025e9fd0523614453b3a37a02182ac26225d35f28021e4b2880fb35c5bb7e235a0c8c042f84569e03adb29fc8f33c85ccfae6ceb8369278f00ece37c7d7fed9d89cac7145382429580e570de4521c7def1c3162a743bff9083f7feed309414d947a7795266d96c03b97f63d4cf196a984fc804e930ac1d2ecc7dbab5ceded9476334626d084cd5c8d215604b3b8e4f93408bf00fbf9a8cb45181c15191344a6a0871680eb7ee3b76f305902bea52730a83648b0fac302c6a1640f1ad1cb25c642529fc8ee915797d39dabc9de5c92b713a169485a6ed00ceb903bb5bcba5820fcd08c30ee7af22ce8fbc659aaaf8aad3a87d47ac36a0e4db07a7c7f35f64577a00f65c4ce97acc42cb4d8188c1383c8e8362830cec08ea7fa648e853422b85150393464f837d39cf19ee20f9d532fd404f1400ecfc84bdec9eff6e19175de862ecc05ff86265f9a1e322cbed87248ccf86cc51b31dd6b0edb9fe0cef206c812a1169575c5363d67d2d91611fd5cdf9fbe6ebfcf73efe9845c6ae40e4e337982167d261f3a9582a963a7402032779cbb8bcff9664bae3d350523415d8c3e91109eef7838071f144a890a46275f26cda9e865239c8aed6ff6be38afa3648865ef01ef6182dd359ad3613429ee420262098e453d43e1fe45e01c28d700c2b4c1291c8e3dd54ca66fa4068fd240687d66192a06c698d74a198b6ce9ca9dfdd4fa5f0af9ef1d6fa921d3f4188b33db8f19a437549a87b3efa3c3c75558c46db7a66a73ec62ed9ef620fdc2553b9b2f4242e43702dbe1786f1c3132c86abdd76121204d1cd075a6614c0de95bb99682efcb7f39212906f01192ed12072cdf305fc9c4ce8f15e7e38a46f73587017d4909621d3dc2d10f8085e1191007cd08c1abc20428a2e090374309fe98a9b4feda76623cf6b2f4679e3718446d228222524b218d2d5eeba134cdc521ff83baebf50460bd3a5c34349aaf8771e7638ebd3b76524588f5346c23773973faa7f4a4c1cc7aa722a1266351e23918177c02bb61aaac192eb58267cd9b50cd69c1f97796f69a0784eb445de962b89ca9d21125d4fb1750686711ab4a38c3c582026c867f791de057ade995e6e8fe4b3924f63b13ec27201f4ff57054c3f0bc1652c4cf385171a9335555f609da3b19fbe7a585daa0218ba6d7db3495cb5cfacd3e0b34c304854615d37ec40cc8dd7a057dbf80eb7358a556095acd7770b1348d0debf6616c152c7bf5957d2b955f07cd81552e1fc684b6bc7127cbca4f61880fdcd1315d7f765f57c61fce337f8303fbcceef5cc992e25682300f35705ee7636e68c20078eaa4b0670a0d55ac340a79ef002e60748d22345cc3080607684d63ad3b3968961cf766484c1cddbc3b0d89be8edc93814d202d8c6195b71bca06eee594710a4a06347bf15ab43fd83b16bef0b0e094592a57acb5fef37fa7f8cd40f91aa376b9255df405d71152654031215310a95a3054fb1374f6c3cd7b3a8cbad95e85b63287ced792451fe248b257e51ed86eaf611dbc5ac0df47ba24be352fd79e35b549f25c06611c20c610115347048d2fec519681a07803134066352c98e4d566d1212dab05955d4d8f890b66b7b2ba6906c03ea22a50f803a40dc200ec0be0d8faddac142159e3a50254319ab43da1d33c83cee3d8729783aaeb90695a33cbf2a66a98ccfab0c12078448c715847be2c665d22ca9a39f8f517f248f9706e63eb7203b8e7a7ec6493fb18472de98131de4d8005084864eece397a636c7d93113c14cfd77fd814e70c519297e296535ea1823fdaa18c42e2922d026d44db2d3c3873006d17ccbc6505d66ef187e7d41836079124027134a46eabc3bc832737530f7b28a77db9771607af62c0818fe9fd43653b188258114d281c24fe5f361f669632c0fb3f5221e6d2149511a07dce43d15682f6ea3a6bcfd0784ab81cf86241ef68fa4ccc53b76fd6d515719b397592aa35c8b97c09aa4ec1a90961975b97a2170f6396af9bf67f20d6429bdeb6a3261dfb18b77467f1d5aa8e5cde2ba3bdc4758602dc0d9b7cccdeb56bc71fd9a07979e20f51c271963ed95e2d32fa14e13ada5e192f8fd2b7fbe2c1b5f88113c92d8e7925078e101056d007b60d9462efd808ed23d2d82f2e8dd3dd393cd069bd94b652a336592c4ba43bdfda3e8896f1c27fa20f9824c6ee5aed85c3e3bafafbde1598f24b484521f87b8eb2061e32641cd1a1c64af39d7a666fc32ba32122ab366e891e8e770c7bb25613518f50c635d4eb26319ef33cb54fb4dd94a3448c3f8349647e20393e4bb6e34696f41ef1052bb39e2db5447ef42559d23bf794564f843a55820f813b7090960c747fb752043e2a48e3c56dc991b0e9b2db6f6995b908579f505c2d6119074de17cc1e4d23521dc139d674edb91753e816fe4c0f82718bcc91ea8a5898632338e38b00da3ba909d9584bc046083d918fea671fe76710e7f76e08f71d652788a006a2e3dabc1cb5b090688d2951c7f67817518ca659b9a368a5d3f0893efe54a8907bba94917e731f0fe5b2751429f126ba560bb7a5910455d275e1b5e59698f69b9669a6c92fdf322fc8e2d388e8453759893c3f32c5368022d74d924865d9e81e198334b2ddc400894b9cba93f958f91191ad4363b7c16fa47b81ca954f0f6d505c5c7b56137cf619d8b5a16c0d0f4dbc74f6a292f49e3057d2a0e9af3f36f31da086f5bf47443e55e710828cfea7dcbe88be77ab30d8e7c6d38b7d7187aeb3dff7be13272a2ed47f081186137b9813386547b7c95d02407c7b9ff6413036d44e508acdd556c898fffdbd8e40e7424e6a478604f9615bf04a2622bb167294e0483170c30879dcf9b38125b020f28320d264fbfd6efd132eea18442548ebc655f5bcbc89cfca8e1b9dc638a29f20177f62b10eac971841841fc084e219352f1c2690d65902f44e75a2576ca3710e2067aaab1dd98f4727dc4bc386e578ee9c6fa4c06111eb0ddd3e65d11363e3c7dd43ebaef07ede73e46b5dd44db77f87ebd337ae1d008116b7c329bf3ca6a688e79056dd30940090e3aeaa27f80d6ccfd8ac4ac049bc278cb14117734faee983a81072836e0a2a71c6105bcb013d6e053538e6d18962c1414623a9ffdb23dd5ec63edd3abf1100c42bc0efe9d3bdc91fd7e6d2b5de332a89e22a47c00ec65bb7934889638bc1e4c19381e9c98285616541bf0303e1d514e4ab0003bff874a2c1c918e1ecbe1c88aac999526c0acc4b3f667ad2ce91aa72695426942a0c6f09a28a8408d81478c4533b9abc3e939034fdaab034b0719af221fb66205b7528aa73d5008b2b7157ab2b874869eed1204297faec5005de9e37a3cbd511caee4a9282b1ad0111a12c9c7b8f2e1f44dd155372f9084d948fbd0e9c3dc88fc300085729b222c065fd04024b32c621531561d8dad2838cd1a328f29431a4f8244636dee1cbb471f8211a523dcb4ca97cd3debb6875c4b8605ae0758e7edcacd680ff541a5012f1c8ccb69d69d18e5f28b872ec842b34f67c64647cfdbb6d9ed391d1600688305d4285d30b0cef744b2f18846f48d525b426e925ecf95698b71bd0a9ab729933ca03a175221a7ab3cbbd68dc465a92575883be1db76a8b3029d55858d086a5abc19e9a4db338ff96eced1827504510630d1fbaac5eecb4366cc2492fac795b733585d9bd6fb5909ec5aa839228f631bbc53d35b34951c3300771b819be7dcbcc0286a3d93aa7d4d188023ad049019b936256e008b3f8b7ebf86f9bca0ec4d6a3b2687135681c61545554f32bbcb51b54cd985623d055721df38d7e61e46ca3192a3e050678ce1b88eb68dbb856ad7b149b2607475c2696743af51d56c827dfd230fd175a3e770ec8e57b628d5a4e4b3fe8bb892ed2701e32ba640a57dd6671d9fc31e5be0a76cfc959d0a055b7c61fc47b9145bc0fea88d3f8d95b9fac22e12adceda4d7f6497484d8fb70cd4d4bf0f8f41827abb7165c70cb10e0e2728a0caef00dc8fb3a1af83429e858ec387fc746fef255915620de4520875cf1024a579a90bfed00a322ceaab332eff435e85a2ab50f1be07332cea5dc6a32154064aa8510899bf565b96f3fd582fc0b5d7c57dfd5a49ad5b22d2bdaa12336d7d6773c46705f602e2659dddd6c6119d60a3fdfc6e937e0ad11e1f48271f40304542249c42771e8703b290f58b2d318163a1a9fe38091f6d15754a2b50264765551362e6338d620b6c2bc70e038e35785be82a27249d0216ffc2c1a390093c4219735a48f42ed79a9c68f554960ae81cc0ebd4ad4871ce0dee9069bff849b985fdb985a4d178e210c43acbe0d785b1668ccb7946ab0d36e716e8063ce85f969847dd883ac6073dd7e7bdc051d846bae9e2e57969b4b6074e26b0a1d86d8981418c994c6eb0f2339f0a2e48dc084ddc6ca8239f4bfd3545908442801a71716f19c7efdae8a2824d453393f772e8f7006d20ad66445fb63809e34851d34719f913fb475f76a27d28be18f4da6dea7faae43309215d2e22bad7808442eb518647d0d6c6e75fdc5a014c01be181f7720080c1e7d8af60d083f659811604152ed8f341de36b889f0d2bae04226badb1e91d808a8774ec2a88758d3db3640e4bd40645268327d614a3975c6a8a7456d77948dc8cde0a7b9d65b44efbee85699f3bc0ffb59bf648e1e4520970948b5aa7d890871c75991e56e3a06e4f10c9800680ff4deb0ac05ccc0576e1a92f78da06af0f212873ac3820afb2116d17dc5284339b5e081175a78391267f728dbde9bbfcd7750deede396a3c51c179ef44892704674b89635915498d76095ae61fe381bb4a14883290052794e320113801a6928ab40430f0980d76a1d940fb5570db7988ae26823ffe6184bbcea2c7d17ab81eae826c21812746acaac05b5fe1dc6fb65482d8ed2234124f697cc3c9180f468ef5ba42e4d5e1b780e3d96628a2621a91273da30596af22a3c2d9614b12879bc7d2289ccaede1a75cb7f0d3bb94d2ab33a50ca7120ca10272123ea1d98aa826434daebc48070bb816041d2f3493b1e8e3246564c09491e153b30606691d0903221e88fd48c5bd3fc610310236253ee3d52daa8e3ee685758cfa400dab9b2f277ac65da7bdfb3c63aa136d289766461e142d902861ec78e2897b5d1087e18da51dc64e00b363dc3b3f32825235be2d1e29059b90de1106c00ce47242f7ca4b0eef871d291530406a3504a1607ca1d5aaa618c6b61938730e26a1c481538aba07e700d0caba273834a9745af9877106d713655c8387355ce4138c61532fcf716e7884f7f5805ae463439518ab4d15b3849955deb318748698e1c83ee2e1502e39687c85e0a24d5685f1a627334cfee48d9c1449cd034e8e1f6f0f16a458637bc44be0e09f8b4b95e7a29bc1262672efd0c09b9a1cc9d0e4816c45b88be40161a7a16f4c93004ecf01a66177b4308b7924011ed8c61128fef33eccca9c8d8ac7d598c93cb1bb6c67e275188587007b1029fa39cf2be836df2609890adf99c1188e1e6a58f94b81ffab85de9497b3f92cb5916dfacdd321bf338fc0a72d8d70134b2e6b1a7e33b16624861a5c56df406047bc34fd2ca07ae0b45b0819190be4db211f551ba3c2422fb1022ffdb11c72a6c2f532147b150f575b905bda05db642ad4013d8793a84a25e014299840017c7205021895f6427936ec85f45646298cf3abc59f920f041ea187c3c5d09a88b8bb66a26c672bb028f8d4d668a2ac38d5eff76528d439aae2698bd0365bb864da9fa868b14596ddc4206e7636b8e0366f25dbe916b2fbb3602034f088442b52c4b6843694454fcdc4591c2665db282eb9683694da522f91d370281344a02996e937d2c807c3a5acda5f5f19dc4d86f799f9857abcb8e587d9956ad37f7797947d522e3d9d93418dfd49a98d8b26c417b91c7e8fd29c1e2b9581eddfb404fc1e205245780e830b2f75dd643286fc5066fb55939a5f86aa72ab6ddd91c52e32f25abd3f8c6df03d96f5ad39a2ecf475a9ff9812cb2db030576f4988334793b4f4cee9f2f82e2777a20a39c1de9b5176840160f2cc39879cbeebcf3dbace26946a234d9d7053a1fee4edb472b250a14788b3fc62d78cc8d416c0d7e0b7f13c3211d8369fbbce0f00a7721b872c20a72bf9e0471909be0714d426d5043c57b81f0991e35093d58539d577869e6b28c3b438d8cd379bf3ffaae8c8637916d8390ddb477d155c7a05ae58c17c8656d9a7c39f01466f8e699b06f9ed62eac662a6e6fb0c4ac6b7f29c1c71e65e26e17565b3aa41a4b738445eb6cf5ef8b04717fa365cc629950e6760384a297fe2df8677f78bfd7321ee53fafb86e3b85522df486ecaabfd23ce18d7c94c8cbda59d3d49d163deefc740531f4201766056d5f55240408a1158405ad3d31bff67b34f4718d059dcfeeafdb37b2284762ed21a43814dcd34fbfc35f7dbb74f19ec57ea8f851886666fd6a9f018d651712ea2098f593bcc9ff901f13c897805a491122c4e5cd55d7e28d5a5ab954c4900ac23c3730b31b98e35bce2c524c57cec4e947448d85072b18c8c57a7bee9a68c1e4d5a83809ed9ae2a73b47484b45135b61f9ba83b69d910fd1379c4118f8b4a94ce03a4a0990dc757781e4e7e7a9d174cc0e2cc0fafcbc0204999c173b980a37e1490c6c5466360cbe746c51336dd399bd1da03f420d67b0ba83e8dc77c6f540457c5882a305067124914f10d626f83d352161db11105cfc859001a2e596506c9626c8291bb310d924178f405c6018b9f0c8aa8a4f30b1c0bfd11bc0805b7085bf7482f2238443312828418b730aa81d56e4670b42d7b30c93727456c4fca52b4be3721434b5da405151bd02d4f438c70d73b21d9f3a57334713d5ebf8e36d842e8b58c7b7f6a9cc351eeec054f8b686aea5d4f500e929376f58a8d5e6a6eb4ecc1450d7de1327c4b8f1f98471a695975443562567320b11f1624be4a3545bf5123a455b949f608f76a045780b21c2672a5384f17cc63600fd152a804830dc107d7d4b4287cf43bbb860b709619fea566ded8ba68232ed79f1697578657469cc92232633fbff3b36b039db454b98a433db506eeac3f1e0eab79413444ed1b1d19dc1c671b4dbbaecc9e07ad1401aed98bc6e8b5251ca109ac5a4652ec1e695f78654eb1e0987c36e39fb6a89276189c84fe2796c4e3865b92958a58469ab2f0f7a60e7eb8b4cb2e845d396d1989a6a95e4140ad652503d201010d23072c3153d0cd91a62642d07c88d379a3cfb8221b10860298e10987916a725b49af02868ec9a2a1a7f8d43307909daf54b11b526b3d0db2138ba4e3b228d6669a9395d692afee750cd904adcc7ac92df4ae13f84fcee38235606a186cb1347071df0e5b424999771751387036aaf69ae046c6b74972e16c1f48f18b1051404c518617e0061e33d6e5c380ee91b17f12b22f08dc9e4bec98331cc108cfd4ebae242edf30c9c04294c75cf8cb4f2c12d8a73ec3fd2ad3f0c24cd61ed8d13c4694686b86b90552c319d861559063d4c2cb2b08148543f120765f831f7d146151b8819b7c7f4c27996bafe9501c2119b521286dda67ce2c080dbd5a5219463480696ddef537ed0fca711ad7c235f2e575c9c05faedd16bf5f49af324097d9757ccb6fd67ab6563d0b2532f73daac5852fa701b01e900ed956b8e7963af790ac16a2b9d00b7aed30c336b76d0b49e52a203e2b5f50175d871456a46bc2ccbe3b18bdeec52425d60d8224c3c7b139143956c406988c8c1a41d88aa403fed3018a66a28c217e6c94e687ce469894657d5a28a1b6e4b99d529f6c97137b0ca78338c9be8d415ce291520aa75bd20da0e5e6115c8b087900312410442aff757f53651455184a9399a9c304ab7e1535c59424e319e9a3fef76b41a079263925ffd827617e942bbaac3510dd57554dccf3fbce9ac00deddbb63da17237769ee1de465b9071ada7b023187a89829311adc0e6a3c71a588eae45aa0c7140dd8bb40c0f247553a72bbfa9672fd9ceff6a523172d6c1ef11405d4a4876c398b44901c9b7c29852218a111c7386aad40a044c45bbac0fb716c69797d29c81444a2ae427a4fd5895a48fe8741b4572de91211a1458d7fe4337461f5a1538bc7004b855e3f88a33b68a3bf73ede3f7687ddae0a64e72a26cfe2c5fd75c59a53f5f9c8df36caaa78533c595d0067379b27dbf8727da106272e25149f768d6c851449c4cdb9e89768b66c82fab473c2fe96c4ca7220b832ed4f690d4012dd6596c744ff25c77c8ade5d02615b612e673c175875cec57b47a821205941f0319f387dd1239209e7fc5635e0cfc30184c53216f8f738cdb69e1e3adde764439305217c90031ee440ee4435ff8aa2ff1c3ba03f6bcc953872f6bc41f90473d0764017a407cd6b837696c8a251b620da2be2ec823911ff3b80fd4200f8e303b40e039ca267140876e14c4d70019a551889a011720a3001606b69e3bc185b68430eafaf857280c3c16bc7376be559bd842fe4afbc95bad331cf9e19e9ef8133d7241e319dc6f0ef37d3b7786d7ec234e73ac72bfc68c64a7d9227de2d0f4fb6e1d0f7debf6bd9aa3e5f0841fde4315730e3f8d150142ff84aa4f604911f452b84294355f9c11ac7fef8bf74c4981ad877d6a27ab40f2447e2a421c6a11c01cd090f87a5dbe30a93b82b6a8295f9706afdc1d9f45aeb1932abaf07ad5707cab39768cf2017112beff542e8400b3cbf943ef169754ab80d83c8d1ae61e0373dde1e5507747ae4fdd5eb74944e2c4871e8644b2a24141e06f7a3b58e5d9233ae1f90e85b49c9eb2c710c0ac1b49e3daa63fc28e94de796310acaedb218fa3bfd70ce7169e51be6acb7b6474869a6c604eb5da8a8d5bcb0d64918143013d07f7c9af294c78075f49cb902bff812acef6e07684e893237ca18fea7df7705d34eb943609d61b956370ea2553d035442f3ce5d466037a3db6715a30543a4a078956d4fb462bf050a7eea4e6ed0644299a3f4e79c0f9b11395d4e368bbc1e31d5509074cda4a1fd00e477ddbe00008f003bdb67bb08296a36a78b1e601c48b0da09dfff65d63c614563db18decbdb7dc52ca94520ac306c006e606ad1460ec78bd58823801d8ad168492060db5df5dd766666f18de8072dded2144db5e5d9d999e794686ebb4671d9fe13e5a217ced334ab375845ac64d8cdb3813d7dc7eb0c66fbb6c7ab5314d73116ecc23d3b18ce7ec9bbb5e9b8951b268fac1c1bfb87e13101c6cfa4f19fffa8b8b38d9a95793bb5e7251b08f1decd4d63e4e577b18da255f83d96ff19bcbfe72581b7bf70bebed751a9a80c08ead75d61a6f71ebd7e9285a9c9963f133ed753afed6dd2bc7d93a8e2fb9d929ff1a0ae7d629fe189a076b4945db2e1c271b26ec7d2b5063fb65af9b723fb4a5dc8fdd1b3ab1e7b93de2cad7ebedb747b6dac1ed6afe88756600271ccf3fb7c78ce3e4f81e616f2af69f0dcd2955b1c736853d0e0070d8d370d8dfa833ddcc51c59fe162c576ac27dfc479ebc9f75ddde8d5a93de92966ccab37b771743df91a47b39e7c1bdc6953f233eef746fe0d1b14b79ec4d693df9fa3c23a59f963409d7be3338d45a1697cda9bf6f5b43fb5174d3f607bdaa3d6ebd73cca64afd35a87d2fed4a8ed47acf1175d134c2ff39353dacba0aa3602e5349b9aaa69a87fef1c33b166dd9bba1c3547fdfe33cea646bec6f967dcf52b31ee3b7d3aeab55a2a676a7983b6369f0f55feb9cd6bce4ccbb22ccbfa1b4253e7d4ba23dcfae24592175fac6c6526435de3014c88ea1aa9a04b9ddda75285a9dc02b36094fc04fd78935e3518607ad87db824681963ec08cc04dd2c5f84a99f03f16f05b32cd6bae63e9ada5f08550fda2c5f04557e7f7265bfdbac177377bbc90068d0eb677b17905def7a2d5e3bb92968d67dfd7ea43d6ea55e75a775f4c6113aa8d0fd1b56a84f41bbbb3fc04bd585187cea9ca9ad630afd7649c9f49c61147096a963238f9876755f1bfd722518696fc128e2e71618449599df817a75ade9e3a5da99098856b79f1a904dd3baafe3633cbc546147a4bddd27ed39ce0836c52f03bd817f3ace327b50e740ed6535d08f470add211ae6c411c61863444167071afff38ae2b391d931b4290517a21853bf0e4431a6c6ae48e41d81dfc8a9f2438f5f093c7230748dbe50e605afb15b0f76ce5d4d17e57a9fa75e4df8031ef854ae6e162f5ebc74a904a89ba50b2c7548f5af8788ac48e92bb5a8b6112150229d5aa5fea93add83d6d74a2a51afbefd9cad8cf121931f69264b443dd49e085e682be91577516698c20f9ba83d9a23a0fb70e6caad1c30a05f0ff51011c7e92122477aa8bd1e1adaaed5dc75670033e8d401c608218470b1ee6d1eb2565ddfb61590da54ffa4fc6bf2441043e9e731eca2a084c4ff3c0e897c13cc105f76280b5c2faf6e0a8a2563373d153d6fae7b7005dfdeb7a3fa6b523a8e1705e9d5561686f4e0a8bab77ab054bfadfa00a87e8bd4832fd461cf33aa070177bd28d773fc3fee05c644e43e589d8986ad9d4e7332fbc41fef1f618cb121840cb32570621784102b022b024e7787fe506a124209259404d8c075e9a04d50fd1976cccc738822b0241e25942c8523ab14906323253ab441830c924142424242100b1b3468427bd3df6e067597e29a904e0b093104afd90403c1d734f61d5e24c1f40257952aca4b017b637a816b8e951c68f31915dbc4046aec0da6651bef4d4f2b947f0b15b58702506590e368429eea9f93d2a05a44ad885b6690b2a0a769456bde0ccac0cefb47a66d0c4506090509f58aa5348e2645280b0a8520956d089381169989b8dabb4b1a7b0d4719a3ec8ba3a19a739c556929a5063736c91a89c8aa484d4a29a1cf2441848fc4b4a0fc104219ef364688237b56126005d435259dd27cb87fa74c8ff4073bf2b8fcdcd185f372870eb71ab11950feb9db691032840e2184701733428b32d3b04ce36e710759a8fc9099564884ce23cf1c8fe00c3b82f2b76ccd0c41861ee185617fc5c726764d4cfe6840b59dfa4c8861186625765a8b65c5e66585f22fb7bfd6e2426badd5c298f00bd3b4c7320dcb34f6d174a83d8b58f3f1544f27b496a77a3ea1b5daeb3903ca9aa66931f06ebe8c966173fed56a31bd92b0d9191514226d0fb8310968455d9fac88689519b5d7ab8c06ebf567448e931d79caa85759527b34d855d6c57a979b7cecf46c9fd5eb37ae87064e83a57d0d93b5655f927a952d75b1abec0c1b36d5af71acc392d02a4f2df58a5bed9db12ba6613d6de326dc43e5d14e9675c0be9f5bde422c16a74f66e303b4ce568cfebc5e7cc93a2ccef6fd6cc3e2bca7a29caeabe3afa7cf64699d29ca364fc105a0728b3baea2b57a06005d68adda2f99b6b02dda2c0a33a424859722a32e48495fc02c11f5ea933e4356ae4ca2794211cd8e496bde539590a35384659b8e138b8a8a8a36bacd297f826a8fb8b3455d3bc382f4ea6320a8c27e2ad38f58bd1deee9d53735606467050eb440e408925ef908696f4e21581fbbadd2d47e58d3de49be2ce17ad75c3e4a3e87921f6bfc8baf54c652b9a8ee37279fe05842e763ffcd396304cd2216d8056673a3a9709b79f89b89fb78aa29c6f8353e7b9e2ab9eb927ec96c2f3713b79bb38d9736dc860e24e2e9b95e5276f03759a58c50a625176d6de49e2077cb826ea78be3fa55fe36e785927917aef7976196e196e7f39c36b27341e53a90f1649ba89b650ca43a8651dd2c4a43f5cbe9e7fdddef2edb83bb54e75761676ffc659aa01fadec35fb0c17058158e17f3ca6ee776b7bb4fc871863944b655e26d20e25f3cb322d23639299d192e6ccde05f8320f3f7b379932f96bda78ea2667be67de34f33a9ecd742b3129a6979acbc8c83c90ad329dcc5f327fcdfc7c9edadcb719a926eee36afa194e869b9cf66bda78eac60dd9aad66145f008f23f9ea02a9f6cc7545a01c50a142bcd539d636fdbc31c07c9f6dbc96b360fac43f9af694e4ff1eee52f7fc8a9fabb940fb3e7f111b50e0564ab7728ed5df0a755abfe340b1952ea8ea972352d58c21df2466a3a0316231c74a96b748327f59b4637b801c712baaf0309baefd7abda63d6763c7a67b8a0278fa66d7a15bbc8abd3deaa989ca1c595fa4d3194b420438c217e78317aa8a6cafc6fc39f039aaf064c153f280ac349186154ffc6418012ab9f93a04f246169f80073be29aa3d278410422884000421741b301366e1aee30a1d5748f7f6eeeddeddf55d1d5734bbb7776ff7155487153aacd061052a05990a43ed2e470d7e2a7c2d72119b103b4ee21178075568ecba365a7569409d3d6876a154e538d81739fc24aceb96a7e0fb54e8dce442fba1379973ce6f888ec7ce7fda830e74830afdfcc77f5073fbe93f9b829f0463f7d138455bb81b638c7187d07428a0fd9cd389e085ee53a1fc5d8e23badf3e686b43d343f7359a23ba3d5afef51025444e4ff1c5a1845c48a8a7784d35503f160a32a4608f65f2b35f96a9fbd9e48277a8146ed4fe1bb55d881d2a85fe08435717fc170a32a4d46ec8dda8ce2d146324d5e688dc405960cb7eb38064ad6905edaf0e25e4c2fe92dd0a59f6b243099172ebfa54df53ceb1d1ce49870ff4c445a75327d5d8918ab6365fa4adeaec63c423248962263ab13dbce352d9dd86ca427bd39108221b6a57a9fd1c7d64ea0d23e8d74b3e4a4b506929b6e4eb74f4711cc926f8f74727220f8bc3556caa884df08e8f7884feee3295ea000720000150a81b469eda42fd3fa83454bfd3ec42bffda254bbc7b4d7deeaa5f67a0cdafdece3a996cfd1c771784e2367c98ebf786ade807ed1888d96b8d0a3c9445b1e34ab6cd31e774308a1c7d8bde7ffbed59d5db53dbaa2846848b40e25a44322f3dbf3b0669e87558412a2bde94d1d1213ff0cd74428a0269a13d01d37f8c6124ac384176796c62d6b4d3003b215d5ffc40c7250fb4ba7b05e6a63d149dc483a82a0df4a8ea3525229dd18a23ca4c277eea3c9a93d69b820b0cc47365539bddafa8349216143c890bbbbdb8994314228a58c114a192194d26594399d6c5116066a019484c6a7a17df0de80d9a5b8f241b2e4eeee5e55799e9ae1817eab44c4483c4133333377cb70d7be850c30c218af784108219c1113c175c56b1eb9a2248184d7d5c15882211a02cc5d1b1422880b0c777777e72852ceb979153046082184982a46186384f1bd1835299bd78bed504ac9338782bc9d1538400409f71c7d09b3c43e1128c618bd6d420124c4995a9c1df93b8ea39ad02f90aafd2a8f7b75736f44907d47b2ceb6a02a3be6a6f8f748ed1790d46ede64c8923cef84cc2b5a2916699332a35e1df5eacb90aa149991935a843b6f6ffc079a1561f12c09e6006384eeee41746ef080422e26939d70ce092184108586105bcf1482daef10064cdb8c34f4896530389cb39b98086abff33c02316c6d00bb8010426f2321bddad6ebdf54ffa63a27880a3beb75ff4764ab10767003baaeffe205a378df917a713a7b63e38828c3500e964d62a105232d25474d3669977a7c7e80767af5fd0a42428e4ed428c8092a5c9ef736435008c6082184d0e77a8da4f2f7e6622094104229a59411854eb9f4f5f8af114c0006d915911e2bbf2481fc283bdf9be89b6ceff7ed8e5cd71d31d48ecdeb50664e1806c608e7a4f474faf7bccdc9c161048dadcb8aebcbc5c50f1c9b1d420817c632e66cdfe1b599bbbbbb994d2944f67599eea0571bb41fd6e4968571cb82314208216416addc1b9461660b2c70d8b874a5d13046082184f06b743426b4e11241b8c718a3ab361319f45355378d41bf2d5241a7fa6c8f6542e3ef1dd523c7eeeeeeeeee6ece696666de6ef99c4d7b6dda1fe6a187a315d64b13294edddd5f4648e48891110821c47efff7e77227eafe9ef2fd72713951549ef28701d5c090151c2788b382e8a07004c5789ef6001ebef8975ef5152b75eb037e8a7ab5462a007a000f5f588c2cc0cc380578a98168dceddec42f069faa64677bb0970aab16b7355120cb5e93d2f40210ae706fa209c8567b08aaaa61137a4706faed918f904ef962b1844335d0388f23a4537e997cb497cadc4e7b1af89d5e7d5448fb6ba04d3fa03b2cace9c7f563abdba12c8084bf1862d9780ccce7aad33a96a88e545335a5290232a68ddb4c3eb60e05642b3c527b834c9b796acf985ec6f43a6ec2be06374d5a0744abddafe34b15881d91f66ad804ae181345daa9578dc950b18a5d4fdb3bb10c5824dde88176bdb6d77eeb50d82340898504014aacedb5bf9a7575df105eaa5947a1a05b3f3ab9257ffdd5452d9e54af6e962cb06821a56e962cc6d49bba59b2705239697b70b1382a22202a5a7e38824b18085c75ce2c59e850a5ff3aeca2ba048852686282aa095acdc9e19c73ced9736a73ce39e705e79450c639e79c73ce13dc66a84062ec793ecf2e0af67cbd86cdd7ae39af39b139e75fdc9caf458e1daf0ef5237a590e45104208218445d06f919068bfc6d835e7d51042082106bb1b7b38dddfb4edb94e5f5ce8661042d845c96636673651f351b3fb11ebd5cd4e851aa2fdbe0252c6ada78a60e0a7cab5a6797bc0307b7bc0407d7bc020b4a91d02c205d1694ff54404fde66c313d518c9228a64914f324c65853e33653c55740a6888601285d40ad56abbe70a4d5f29e56abbe708d695dd15a2509460db8b46af44d15e9225c7b7a6aa0ac5d121200bf86134b6e02e047ae8891597747806fe454618b20f31da95d427bdb5c24c7998b34e7143f270b346ee68e7f799a8de8a8424f4d4677cfee1f26dba33f86cb7d907b76dce346a85b04807ab326ecec088da39add3724a76e15babfddf4692f763ff6f939c8fe80d0bbe550f0fbe3ef9cde9e76bf59e5737bbc2754f90c23b7c78dcf19c00bdd6f30557e1e64b80dbf182a1b800b1a65593b7ab5c3bd51d7f37fe72d1c020e5b7000aafc5b0e0e3ef5db760b0e3d75b7240953bbee96246324f141962010ed5bd9a264484950adbb4589936e3fe0104ac939044a50e1bbc041d0b65ef9c78f4c39cb1035401bd0b01241d8d0107ac5f815e3a31fe64abd51374b1822b4b81068bf696ff8a7d76fc8563dc2c81b15266a7a05ffb4c1268ab0e944fdf6c8a6047eee8a3003511a03a602d42c6403caffb150ed9dbd691d50fe1ded797b23c2163e85c65bc3f716a24d0b4ec48f388e36b35f808dbb6f47ed8af00c27c331064caf69cf2cadf3d958a6f781ac1e5e12b0df3866b549fb6649b969dd2e09d84b6ea262ef840cea95c23ac11d242747a5f2bc7fcca8893a6750af1434983dd6d7134c3234c44811c2fcf05f10767777c7d883b1fe9dd427abb84804fa7df8cf1ae611fbdc530ff41b4253618792bfa8b9ed9ca09c13c36af0ef1c7d27e73dc2a64008e1ac9a9432db6f91875cb96d73dbe68cf1af4b5edbb63d90ad5b8732bd0bdbbbe93bcb7e4e13b7c92cc3e24bee833a7ef1100503470d14b6177f6e175f729ebc2bd65c325ea1ad8d4d9b3a46f2940d3087187a5a42bf6e75eb27686bf7f8c0260da469b3c154b85a6f9c17b45e577ba98dd458b497d4d3ada48fba74cf052474fd3c09baba7a7150c591848e3985a16072907018c953304cabf2a89ba50c1a541da4d3b7d13927a5a793a7da2a44721c57d2a91694d313ea4db2d9dbf55f2355ac833c05ff48d52da2938a7a731619359421833a84cf48bdfa12b19060947a257ff250d151aff84a678588b1f46a21c7d7d1f1a11bae2faeba59cac052b78befbf575b7c9a66ce0066c0d7a2c907c4b2de562f43cb7a7e7d4f5d550e5afcb5f9fc08ca9a7ce0c0a1675a6925fe252d74c25f98c1ac6328dc0587168afd8c154aad9f304b637ad53dabeebaa7bbd55ed73e9a422fe6a0a42f421ca42a21a7bdaf5b5bbd94ec0dcf79f2e43bc7b5bd57cb40915ba5b3e3ce5f420e0ade7e94f1c54583384ed0d3376ccbda1dc62d7b9869d98ceed1dba11721f406d2fccb689263f2b1b5c73870a05bbfeee955f7139a938d07844309dd1e4de2a081ceffe8eede1c9f2c962e262856a058e125cd065d5a9a4b748997e6a4d4231fb1149ac646a78ebeb6a160f2d11d9fda6021c7b9d1754242b4d3e106327577378e2574e3257e1d48f4986ea1fa9735c5f42a66de98977cb4eee955935ec5e731ad8d9a0e8f69efabcccfa555ca74dde2a536994edd15e1ca2cf93afd257e8a04a48f937e6fe2e3684237d30fd09368da70220ebfa756eb28344e1bfbbd0dae8610d86a459b1adb47e77ae8d7f02cda4d31bdfc7e4dc6e4a34d269366fa5dd91a26e59b5ecef9dbf69aa67133bfa6a9713c75ce745811d86fdcc713f404d332d39a4c269386997c98340d9b53c64895af71df8e81bf26c95325c4be390d7b1dcfb08e87280704da2f3b486b1c674809574608fbe7a776136e9bb2767794a23c359620218f20932e1b109e65002714477f2d4ac2d41a75b528e9a9a8165cf2baa416ec0968448da13ebd941caeb874825c5794b8c4f78ad35f971c025eaf13e4c2e1d93101a25e97962562d4eb622df5ba2e892a4272aaaf39b567d32946b180ea04712d3b2851615d2d3b2061a252a591964529a16c09a596457f4ff16bd9093a28f40afee430c7d8bd04299972473ccc7c73fe7baaeb85e8ac1176d2bba8d56a3561ed975377059bdaca40762862ae80ec60c47a5b63a5d1c1260745840d8a0888c140cb0f4b1ec8c9414d99330725058688a2d2811698cc08ed4008307e90320313f9040c19d07085981a6409020ae9a4033fd4cfab2c9918a141e5055138f1c189a12aa40f4a9411c40f8c78a28a175c58fbdb0273690924b182041054f4c4a00c0490b1831e7ca8a2a40a971f3264640a7894e440850b0f34e0200725195081942992c8d8a28c30b8a82181b46c18519e206389209801851820c081173382985225490653a09328a0f3acc0071d68195a02cb0f8600440d8288010e78b0441328392ce9c1088b690ccee252a840073f4fd8c003294e76be68e2cb194b0021a10493d68c4481962024110612473461810a5ac0b30314265e8c41e30c16ec60c324da9a84c13930e1718b0e347c00f38505412a70010c3c2cf1f1010d4b5ca1e5870acac396187a52008327414d082d31c61341d0208b2837100db183134deea04532a1d301582421a60755e420032e3c57c69881192d17e8e0072e00aad70f10f167ca8750ceeb7705f5fa718149eaa06402266830469219c6e0c18acd892a9410ea62082738144d99c3161e22b08841c150153f2861030f900fc6b822085e005103284c7490914b8cdaa85e5664a9615d1713da5e57bc2e9b8a7201bd9eafb803197400041d9610ba1204eb3b553a06184524e1c2832a6e605d5a72a8b2492539a822280b76145a094f696a6c986f80a11fbd9106fdb885c398b9b5e695130e451abcdcd9dd7d4e8c3ba13d3fa1ba77df90b53105edc788a0ffa3792a1b47344713498eaa905bbcb04cc35ed813beddddbde4560563827e39f03d5887ec51859d2a8712db96e4c9b65d4924f1c591a03998a01fad89e2f32cf8cc00e8edb84168dbe15d5b50e82dcde6c4093e5f18fb654d66cc069bde9f41bf1c052c037ce60276013e5e03e6f7e7e86c8feb7b9dc0b88b32f7921cef0dd76d8ea77abbcfb142bf3fd5402a7c84c4401bc0b4cf2ca1fd1f751d32d0136d2f0daab2c11769b18148fecc1654a73d1b5ce849065a230cfaa94a601d9cf5ee47eb99b2df3439a0507e1da741dadb093afd40bf350aa2c2b748272315680d47b59fa606051582cc16d08d1a4d1a1d1492a010580d4f22757facae6927d67411d009bcacd73c86a0fd9fe7eeeeee5e846bbf08623ccff33cafabc0e86a3941abf6977002fff0fad7b8a956515513df21cea4cacc3c933e6ed1242fb3a749eddf68269c2d244d6e116925ad526d230b7b06ada78f68f3a0fd471c477e1047c2c3faf8a7a2988504c51d8a02f3e30b99313ed621413227b5f1847ad746bde5eabac853fdb26b224ff5b26617fa31d222d180e4cfdb15e1225c799fa7a0b06e07dbd3e9af892a515f39f29643cc16106df568a1010ac3201b54d01a5fd06f0e21d202db7842e10f93bd69e8d3f254abd55df4cdd6527b148030684b4e092730b5bfa6dbe9ae075b09ae9be43e2d6840cb0c566a77b722b3d05e73d1e42810142524a24eeda1503bf22bb02cd9c98f22fb8db009452cb420002794eb083970a2c55a16eb3b2d794a8847d1f93689050eec08f11f1885d6132912fddac60fd497e6843eb57f8ea9fd1b4d059ce5469e1a02853f35989b145bb482956b2109fde67c6e2fe0b546e0531a9eefe540bfa9d37d46a08e11742b5c1dc7814cb763e97828689101a87647a94161756c4e358b46ce8e24b8330654e5b527443f8a03a3401a4a77e3f77678d20bb4b4a0406804856aa7a1c4470da5bf5b0ad0931af7997fb91762a8cc0dd45e0fed4dd3f656a063887e50c8e7862126300542b0b5374ba626e8378fb6d0685d58d45e6f0d6f07af2c24e2d1af230afa41a228f4a31dd43a586868885aa9fd3435ac440bcab47beac7423850c8852a14721c78a5b108b143f1202b908174f8500f41a21a876490148a52e250b412876410dc38543b0808e8c9dcae9f09a4f54c81bf3958409999d753e5807ab7cc3b54259ca0d3ed4907179eca4ba355fb775c41bf59db338141174619e8108c6a3f4dcd7c1abeaeebbadc2f7ef93acef204a7085ee8476f1c41a9d049dd6f58a16be28730d020d7926b9310b47dfcc8911ca73ba7ed035bfdd33e686051fb5b493799827ed4c8126124244642e2a4a42f5f588995dac761eb9bb0676ed0068886507cf83c9a47ff24526ab55a397c7737694d9f2b3c7302fa847edc3a9d069084facf30a6a7956cecd33cfa052004a5ed35fd4e03c04108fa71cb5b533466f5f730efee2ef3c68e5bccfb4e695a6704f5b0774560e5162b7d93c3ccada768ccea174108f43bd55e62a08e68d47e06fa52fbf9492ba9fd39b4a04f18c839065aaadf09c8837a1a07fe78aa1f8a15f4f32013fce982cef9d303faf9cff7531a14ff3e04338560a5f66f34fe353c0bbe6a074deb04691efd365040bf398bc6d09e93926fe23de9d4e61cb995efdf8ca3da6fb34342e6de6b1efd346250efbf9ab1642929cbd2d6c3365924b12a3f985e61351eb00351079fdca814ce16453dead4d000000401b3140000201008860362a16830982772d07c14000e85964080521689b324877118849401c0180208010010002203332454840074b3eaf55f79f8344908f898e6b5f21a4a6b8ba401fd5f0ad869b1782acad19d28347a878a55919c1599991a97ff6a658fb383d92a9307ea50f9981eb7004f5df817501d4150c4f619f03f03287974746a084f5ba810078760124acbee000aa8d15102b321773268298810c17d943636b97b14d71d081e17c49fec24a13603c0a0be1cbecfaefc64e76bd398512dec67f9d8e77fca31acebc0aacd703d5b72658adc458ed78ced0d93607ddf9f0de4f287f9a3553a43b519321512124ce574c03ea2f9b8fa91f85bc3c070db3cfb8db9fed8bae4e8508853f3f78d9ff77d103ae360f93d910add081e00107d552af0a05d4a78adcd80768d4b4cf2b7bda031531eb82808e4e3847eda07083e12e7800ed0db8fd8ff0a6c130812f90e24c361cbd0b26bbf061fcf2adcc0f80340ee02b10b1b0250a6a99871ff0c9f40c80361d432ae09237eb2e546699dd1950f6558d1891915268994fce84d08538ae6584e6adb6f3a0336ff38b687e9b2ff2a98e3124d685637c21953ba306b2b78fe2bd42ab4202ddfe15ee6b1d54d40328d4f9e1a0db3bba1b0c27aad9dba73aae2f74e29575d2002609d8e9d1722047ea5b20924be135bee063746f98d31ad1acac43c246d214960966e0a8bb72509dd976c61a51ac03fbc455001726e8f458405de4c72d9fdaa658ef141582838965f1dd3c9a89c9adadc70d1cc9afc7a62ec2f049036f13044b86b4e3ae3477b706d3decfec2f595fee439d3caaf715b49a739944836f31842e1d78c41a5ef5bb469464fc9e25856d31531d3841f594ec7276905feca06a591451a1743c6db07da70df17eee9c4ac5f1ee17797fe1153776a28b7fcd310f79bd82e0e25e1ec7d070068deaf135cb5960820c1dc0b4e7fa0a725162c9a6b9436c1087f111f3837553ee3e9ba2ee3eb61b0117d7b1ad74cc6210400c4d903eadd58ece0aa9151d03b93dd871e8c3a6f05c3a0a2bc7564e8b4f0032b88a84d2d3859753fe514720b10a04ad753c74f8939082acdc6421d3ecd65e2b9ce1c2c54bf18008d205e305ccb1303e22dffe13ed9d327573d3ce400497a0496b93a1e22b5b2c65bf85dbe72133f4787617d2772ee3713125db719ade04c229f21b9818ffd2c2253f49ed0070359c82c1ee863df3a050df0e98f3d232d7a54c27746f064b57ec8456f82defbcc2478e50c58afcdc3889921f03e6cb172cad9097de0dadd6d8449b40020e4ad67dc2314476777e6b71cb44293da9358fe83f54c5be4de6fce468d7d757b54b74fec894ee5009dde3cfb482433642566f570234285ed4e49c66ff75386d7701a7701723bc8f9cc22406e4bfb14e9835e9371633a3e0fbca9c443b15cb28bf55b8e41b31b26d72a0748c4d5b2b592f9ef427a38310a15ffc25a85b88614b16fb427aba0033dfbac55831d7ad27810393fcbc0252d1f2963ba7843fa52233d7169cae7fc927760bdf9fe74c9472ff29cb9b4157452a748c46683f7189b8ffe4ca3e4c60789af83c5d65f097083d375837a4c955432858c65f53c1759024d5b39603e85e53c523a9a4a6e679db88cdb427b974fe00cd351be78b2e987952ce6ad5df68d5e842db4c37eab9faedd2772044ad44458bb0d3686baa0dca335cae090cef750d1ef19d107c9ee00ad0ee5359f59cc5ab663d3535ee9842659873b9ba7f785d0b0b4c47d73b072ac6ee3f87cc18886a886f6e0bb38aa4862f45aeb8f02a1f661d60302d41949f73d5836fe47118d69b450513189f793636223f3f4d1aa0944af6e9bedf5dbdff7bb3c2d524c866132b66dd7f0e912798aa491014bae77a1ff52ac24b96803f05a850ba0f3187a53399b4a3ed290f1521952ad7c6db0b9d1189961a9bece974afa9280559b23b420d8fea139ca28700c44ed7a1720ddcb5429ca2b0754d1a3397000a66fa98f25990f9553c0ee73abbba4f7c0860398791f2ee53e68cb300ba97946e57b36029f4e1182cc946469fe39ba1999daa0cce99df52e418dd0d8568574aa45fc24611748d1b625e43764e27b311b002baf3ff61401bdf20b59486183cfc834e78878154a91889f029d60a6a3b0f521a041b76ab0a69adbabd03af53ebcb7b1227de71b723805db7a0c5d4ce89e19a31abc5dc6f8e7d4d36d5c8b462cda47a40b0935d5ade54be37969c9a5ea568885f5f43900721afe06c922208d74c86bb00f832f70b851ca06e7ddc9ea5ac27b2fd041c39133442bcf24dcb75bd5f2e7eb8d501604adc50c0970f5ec2fb9b1f78ff71de2c401d32b52013b993f93f2b6ae54aa71a21aee10fb1d2e1afdcef48a070a06c253194f234c7d74c63235a3a2875e1416923def2ede643a57189295af7e481cdfebe99f4d88d7b0b775aa5529e2e6d3246343ed786830426c2d4832e240dddb124e663045285f32c72d35096fe17be2669f3e8bb7ea97dad4521a268d5a0114dcdc07dbee4c7f4addea6bc278e0ddb2db6ed465ac3c53e950a0d4f919e4d041f73234797a4b782ccbe63c514fc5a1982dc61b7a52a8f3117d64f8e8db9f0623077c366ae82ea5610f9febab3351e9ac524f3ef0d9f62b9a2a67ff1be8f72a3b4b8fc7c50060a8df30ca938769813d68d85c4a4a083ed100113a31efddd84c4b4b82b901e39f6878f4a8b6cdb66c0196c91bac6a4096cb1c3ce5c55f2c1d6e8522201faac798057e0a8497847ded842861381b37527b73d04ef5e630c7b976f1e52a5d90c2a884d0648fd2093c2867e9ed7459d83850c7cb6b62ecadae28e6b6d5ed08062fb2a4cf2b5cf1396c0aea886be9ed9d57499088a9935a54b55bfcb4f0d53897f1910cd6fd4926a514d661232e8b243cf5b512b02187d38cddf55c88bb7aaef938d0ebe24f7e1f92e8a63589234163b4e0ffc4e858cf36bc9d0f1028b54f77c6e717b70c00c0ec2f033c194171af41c60c819947720ab1216bcf975555d70e6d88015d0ab5873edfd0683eef34d5880d2b269d6784779aae570927f15b34ed78733532276ff902bbe5564761a1ae815ac2412a95dfac66a8f67eb0e3ac4c3470591969166da561f24666c957e520bd2dcc5a5a9aba6900c7bdd1a6120601cf946244830d42276e19235bcd8d7461695c4f1d23731474033ae7b3c5d966c9cf9991cea09bc198aae7dbb61e6dabf42d50d0ce69af863c14c82b8a4c82b5afdacee1a385b03ab0143114ee24ae4d21a9da253269d75fe98d187ae2f27261c097bd45c0195d152effdf68ac3c0b86b6b776623e5cbaacebae522a81bd945f7f3e8c0c40ae150047c901a31555a5107b1719b1c650fac0c2c807e4a6af2e1d536be919d9c865ddca3cf9ffe206bcee143e7ceca6b349a4d6c610d810f17785d807a58858e6a845453c2964dfd664914932a6be4c8bcab605d9130b46f558964151853cb04847f871a8db0d756476a3630ec9a54ae6b97b0f9a0b690d6ee6e79e8354039a28313dda650f93a88493c83551ce45f63cf13df8b43669df28dfc5b03887536c23c674788d36f8872fcce2656e41dab640385c18f36ebe0c7bd4aa0616905099ce09c059fb2a6df8609438f8daa4c0f2641c56b4219ac61eb822a9030a87685468828947753c4719a9595d1823829e192415deb234c3e802704bcea2150d0af9622a0c4b746b8cb5367ee96b85d31096e82b554b0b48fa6537985489986398d91cf99553e8322ba429f8ac12950613645e167f5d876194122e643fc79e167e2193212eed8a30732eb642e139639262450ecf873dbace826eac1cf4641460d26afca76f9a069513b3e50fe6db2df6c5448256808eaa4501fa0d46991b2704cb120059dc33977f3abddff1175d2892a6523a24308200012579c474ccc80386e428026d08bf033094e9f2e587b0a360fc3028da84a42685f28b352748c8d1949b58064820facd07917a4c36b42f18fbdd5235205462425584e3f809d8550e701a061f909a52a7010aaebdf4f72edda6864f4ed21c8a533d93dfa6c79986a4172003741fd4ac406b07851b64d7e4bec1a73557dfe733e211ee189f440c0b362a14a6140abe6c19cee2e1b0ae77bc3b0996d76543c7ba6938185f052faf49eb5201cb615323e679d82f67f7ed72e5bcecf92545e1ad624475a6b909e82148f38622d3bde0c7c40ad9385820c7575d680313e88e1e5f1b0f343440bbb448b043e26ae84e40d373ca3d7fd854abf354f4dacd2d86e06a624ad0c7bd670e2c6e32fecdfa1a68ec5704e40477a21ff14793ac61550e20561293fc035f4c0b8da77c2f5306bd2c2788d45e95ed2eeadc1cbb01e980a7313f0492256e4cb27d9bbffb68ec05e6e7a92af95fa12139913c050216fa4a64be3c4d6ba916f10844e26b1eaa8be49d69a3114f1f2cabd653b4bd37a9538afb3e477fe693e5b3824b5072105db5873124756e988c90ccab8653e355df82169f0da9770476d497c865fe8ce7de9159e22432dc360c8394ce536d414c8f605813d5441ac75f21967142506d81692e192774b45c34838c8581923706aabae75dc2ba04be24b28ee0252251ed839cf0ae6dcf09b425f137544530f849f14cbf17cfbbeeea098d4715a7413aa6f595f20f6ae8ee4ba555697c16cdbf7dde8a0d79779602b9dc5dfbd2bfb04431bce06c6e36804575eb8382ca95008e74f87f5b19f9559741b603e4c5d7dc2ded58bc1dde7c3b29febe629725846b6ecc94a8ea1645b3d07f45f059974cc847b8512e6f7cb15133deda37528c7a92d33cf16fdb3e7b66d1064dd652ffec930b5d5438f4968b1390c537dafdcfdc1a403f93edba70a65b287297dc52fa6589fb302aef7de4bd187bda1359f640a7bef61086452fae181dcf3edbda7e953dcc08cec6e86505bd3fd939b82adff4c9c5f34299282881b43ea1ab7da69ec327ef30fec41d6019fce8f5e4412a8c950af9c38edcf3326d5cc41dfc669e452a6e3e2a51b92a2eb2dfe7d1f780a3e5a4a618686a79221db27611c919d70617728397fa206bc312a27d4a4603082d1cc05076b6f09a98b861540066323bae4d6e29b04fb0169b099e151a0943982849218c490cf234c63e511fd39195d32a7a21cdcb25054e036d00c38ceb893910e754081885a843c1439c4e5f2d07802121abc2a377a0962e08a75c06becea51ead86d9b7ba76846532772508c66ef85f903f43d21adef2914d82c268fa0902c8329c41935d7a17a2b5a7c99a979d0df8939f1669a99f54600b1fa9501fe73a6610103445d79fcf7cdb0f60d60020723f0b2f17ef1f3994aaf34abb6478e88f814ee973470a3494cdac9eeba0aca1adfafe7f652ca7feefb50c02bddc8362521ff266039007ca25429b21f596ae613c58ff9e7763a523727b9ff2571fb3ca8d4f071738c11822b6ec04b088dd5b19dc64445d18ea46fc20034974c61e9a6da3ee2c12b4ed124bf67d6220486467b5381062cb2c64853949791358014fbbfc29b188dd69afd82f1a09c8ba1a53c09b564fa93977d338e759bfc5146cf0d9b05e3d499b820a7ed1392f3b0323622fcaa8f0b03a405c95117342d8f5cf76d4a8e2d8038fda37f93917541b296a80810cce4db67204e492385b5a57a7175f138c2c75b74b3b3d9a855a5b8d92b059209536819677b6ad0cd8c89ea5b93d3c9cf98dc79f3004b116e76756e9b05789446f13a71e4601a8900302a8eed0c62b14926c37aaf5b216a1315ae278f74a10c838946d1c11f234d2fa7414ee60316c2345a9c8a78ecaafc56afe531d8578994bb8edcfca12abd206be20b495db4f437c811113f13c80f5ac240024e2ba868f3150396c1d44e1f1fd3c3c5dbf144177000cdcd0d4bff08a9774b49169dc9674780f3a62ac7de8197239881116dc04868a4666dd472a2740cbc36f8fbf55115cb8cd0692b1321301b1eee44db76f2b3cc974d386c26cd232171695760bb43264355a8d004eb66fc664678645f8809568f2feb6e5ad5b9fc21d084087fbc60faf48a385c4ad0f27daac811343e39ea97746a053fd5d57a2c3ffcb6a474ef236e3c863f9b8d605d46697ddc62ed75a1cdc83860b283370979d107d361391e3604cab339e7b2df02e5cc9a4358718d43455417770f4a182a54eef1851ab7d59404afeacbfc91e836859b8c23f46d1decee4f754e66ae17276408203005b7d622cd80b74d4613f2e37aeebba49913c6457757be34d1565d37c4070923c64915a0ba57825740d12c1eb6cc5f608bd56ae9213869d129943db076a534809b91c3d45e159aa6accb35f71f3209f4351cd9a54f5be6919c7d22c2d0f4c9d71c20971f953418368d89211a93f7d9816906cdc27038c74f183f98c579898859c8635fe164bef8fc47ac0e3d8885244907cb569b765679bcf7e848bd34e56575e82b128d6110f3b0e5c5bf2692fde75f90b1eedc26a0c2e133721a07b5467235ad0d77653989541a8581460ac49658c235da89a8266b367b67808f3766a331e81769f5691c7ea547b5ac2d2ab3adbe18bebebe72eeeb7d324d3b8937df52b3d3a48ca7730c950c537c560e88f6e18a14aba0d2d80170b78311fda89eccc89c63ac25ccba84171ab487fce53bb54442c470d2665c2455d0073662d5b697f7ba6e51f0a3c50fd23bf890bb6e62d0b3f3809ec22683c7ba0cf4efddd889dfc0ea72378697d4659ad895be54eae678f8850ffbd901090103262b5e0d79a682bc63caa000fd92bab991304e95485ba146051f3a2d4ede463c549aa6af86c6b2ce7c522b73880a09c13fd249cdb8b517ab1a513965c6b3f42afa9262a978f4a803f1635f78785c66657a4665576a853155a7f14a99b63066683218ad7b78ee599dd0c20bcc52f205f141dcba05171ab4e9e0efd20fcf93ed9a2f5b215564c4a9764c649398e58d00baedd8b0226548514d469897dfc51851fba5ac8c9a0ab7491688a36cd811428be3115973edad86916f95d736abb7abf854ddab2663e405c3e1e7e324235c0206bf34ba7e2a4d1ba997a9f4587d96d16f1da9c0d5544f730f01381e820590fb0ea6680673291794a3c5eaa6360b6ba728aeb1bcbe9e27de5245b388ce7dee738702b766469c672bff5d68e5a19f9dec85e219ef46ce0660820d4dba26f41ae47e5b01a98fb57c2eb8d98770dd55460673575b51e7de59cda480386d6c653fc918932ec16fbd6a6cf228607eb058a11fb63da07113952d5c377a82e292b072e7b307e61a2cca58cd28684421f1a79d971d803e812dd79ed2702c582e4212c28361a43f6e5f43f1194204211bf4a1a22264ee082b551d8f55cfd7120e54e7e437ba0730f13246ae724b1117d92348b47fca9e1c0525a5ff664f2e12400ca33824540f3f1c7bcbcc03a0f611d0d48c6dc3f33226139b85dd4599bcbda4d9e6710a8d92abf012c0d0d3fc32bc876bf30f8cdd6e27bcb4b4d4a133ed2077db6c8eee0f750e7948a4473e0f39acdcbe0f6e2ec56badb00f838cd5c22025929bca9858252ed30be34e650530a36d84309bf0bf3c019ac1664173f7aa9040c61d297e20a00bb9c9ba59fb24b2a1e1f26d22cdb046a94d2fd9dde492be25eabc480e41d89d6d18519d340c0f0282e66a30d933d13d20e6bf107ede504ced7ba7b47cb34cfe4922663fa5e4011f70ca8209f9292829941e0cb18c7f710478d6ebc5871f829c47188feb175a1a3c75b34e61e7f913c322b8f38d7cb6966e5d783b90f00711e2ee2c3b67ccc272c83d10d38efc774d4e4b24c7b49a7c2ab092122564576e1156c93b01aa75a4948a7578d53ebccf3ac4833148005485937c174c58dec89f3ffededf39b80186d1a4f099e66b67db7d2d77f0e584c404baac8c4952c7a7bf5f5df53bb2c0941218d306154211fc5c126d1ec538217e38d5735168104099269b538cadc054a0efc2da6f7878a1d6373e29003ae521440153e1d8ea8623b464785131d2d7e9dd221f05977d0449360e0050fb7d6528abd7c5a3d8edc73380cb81e5540871230ae81f292376ce898f4e3105628d5333caa40d4f0097cf48710c212bd233b32eeba2ec7ecd8b67269b345880e2e7993dc21b7eecd5d27131eef7d53451c22d6235f2784b449c4d09c2c63316adc88962122830cea347efe2251110404fe7be913df18b2fd7bb7438b7f1a4bc84d880a27a329a9849344c4dedb476515ceb88bcf3dad349c3e43bc71f18ef1199227ad8a2c90c963fb0568bb034fc6acecc5d3c5b600016825ab1da8786246cc2d755a551efc63b266510465b2eb7573d113a5536808b1bb0ce5c824c7e82450fc5a64fd58632e1230bb78ac1958be27011a4f0d33c84880c44c3890ee50f1a014adcc029ea42a4b4a46269fc92d03f8fa6b6aac9da9a0e6db032b8a024384c4648ecc42064b83508b843b565717dda2250a31a053387f3499f8c83f0eb624bba1622693e7161ff4058e305c524fcf31059481b83a1db195ce52fc85f95ac4679524063015173efed4275190017bbcbbd58b9b33d355aec1a910edcf26b818e2887361af8762bd1720f84dd68f16b296783f0b29d73a73b4e1646837d356f0aaa9e71feab0b5d25adfa5fdf268a8a24c840b775a5a40660c8602acce7a528851eef7e3b843e093c3570df1ed2c71604026b999480cc9ca9a7625e8a2d6829fb6e9bd3f54502607fe68eeadc4626ec9bac49ed721dfbd703c8256193df1a12708e196d71655286fe53b96ca4fac291faa0c5c72c24acb0b7c6d65b2b36a2130ae14d93d35bbfa20b297991196e9b711afb2035978a8e82493cc5970739ed48e84c4a1e2986554fc5479bd8b35f50b106d8d2d01845aa886e79244550a1ccb2a05929a0a47ec148e2c8ad10feb809f92cf222c2fa702c2a2b46fffa6396d8650a4b75c761e128c967805da50481c2805ca53a0861948e18e3aab2d6110bde7abeb4feaeb8c5376148cd4d40420ddac1acfb2263d613c788dd5902e07be28789036b280313d0aeb057a129b9fc1befc8a2d9bf71142c2bf798ff3961cf7eb47eb64c133ed8e62435ac3cf8336cfe9728206df3e6196843747f01b41937c230cdeed584e2b4dbe8de8e100b53276cb42e728c2a210b0fe82ef107ff80bb7777489bd7565689b691b3da862202e4a6e451bb4dd9e1e536fc2b8de09e8c67e5df36da74d88469daedd33ea12c256153bc013107a2c2e5584dc05b50d158536494a4354bf1faa6119d78ea0bb4d0383d5b1084ab9409c42b3e73ce706f4e92611f50c7b9579a3fdc1baa3f463aee158c82b3f8d9587c9fc2e722b078248ef4f194d6197aa7bec2a86aeb51c9f1347f777de88a5b00b5ef550c4cb8d52675db4c0445e939a764d805b3df8860611426e945e5b534ab474909bb14667a17d8559fa2d1ef7f2788491e12f8b03c1f27768ae3069c3ba4c50895e2eeb176e92c6bce8d41e39de883cb4e372a86c7b28279a3a1b75f1f5f3c33fd73167b042e94f88187c5868b65eb0399cac97714e90b7edae8b25b22f013c9e006773f530eb2d655c178eca77961f6da47bdc2be90e2dba79b183fe81ab77f8d66251ec013eefa63acf0b6d981e9955d60f9f7ead688dbe8c630439a971a4179d9b83fe26456be851192a70309b57a9debf8b57f02ff118ab64afd32b40dee99516bb7bb0797d5d2b2f2c478c03d9c56418ecf896f6dd4ff4f33354b97992a0e3f43fd841db2bac05b3577dc00207c6187d404d2ad2bcd213bbb242e8272d30635e71be636334af3c08c8562d9de0fc3fb3b46a8127109bbb02f2f69f04775847e33c0b4e182276b459b9a4daaa112cb19ead2b4b3f97149cee7f90693dd15ca33d2a946ac30ab65f294488486e34d8c375dd0a3782d9157e7ae75dff89dfa7c3d53c98a07d63f5cabe2d8b104058bb3cd8b5b955f61dc9ae34e39618702b360060370db83f93e27b17064dea8a80f2ac6d54d8f413cdcafdace1c19f95fb9cce8c944fc181820c3c7ce8d1b98bfc78241d770705853e1363879d48f2a34c161e98a92d870b0ab2dfc167d8303a176fa4264765a1201593831496abc35415948252286812c3eda520c04749f41d767455282aa74b72d66cb9f419b8456bebf7703c8183b6b5be562c5a9c87bfe2930a8928f14f4a4388fa6530aef03db67b5859db64df61e73948353d022895e92093dff0b011ee2ed1f7091bf062848449b9a7038d7a8e87430d649dd33e92c83f7abfcd07eb171e4a1836aaefbd67adce3c94bf00e190b30309b9cd325dc1fb147096988aa803958710780fac2d3d9598584b310c6e42896ad15d95c3a14d2312c847bd7e85a337ad637c386f9dee5ecbcc774243af02eb6054ad55f0a5acfb7f762a921b916711a26a2bfc88af3b5d9e4e19a1cdde2e6e81dbc72f77d40d646c32e408371b6f4228fb874c013a42ec5fe96a9e9a7a151a7334e309d71004341f2bceb0ced64c9bf08436371dad658c9aa3b9495e28e43ec41a4c05fa548124e9c203a612175b43d16451285122710df3085252ab9d2ac2ced82ca35d3fd24399711be9137b44829cc50498a223d771ebb8e1c925ce6954d29adc935216cf352a33fbe6033581b9909ba8832f34d425e940e30a5a04e2661f550cdf8fa3db5276dcb46b727aeda7884c5fad9a9cda616205910eae4cfa7c2d515ecbb7503d2e55bd7e24773edf146d88c4038ddf6a897b04e8ce2195aa3dba924c2b169986e4ed908153d585058198eb786c8603c65621aba1b2be5984b0bf28d32b830d425571b05902fcbd3f19a583f88c41e2473844107b7a244a3293948268c7ac24b2c11c0f29c9a8ca5840ff6719d136ad6964aab4b569ea6abd4219a9402d99bad5d8773d44d5ed90e220474baa34da94bc43644275ffc900b1c8d69b2f4e9832a67ab97a2152f0ff68ea82c01756d2400d047ebb825636203e0b4d4b6e5c27f36a33d6a1fe20a1792357a1378cf11d51f3b93e67694e996545047199fea5989dc9165b1da8e92db0e71931ef99e90632ce49ff87080720b876a821a77045718165769048e8b0cd7fdd2a0c94fb530430e6ae97c11f3231ba8663f2a36d4d8fa021b019dbf3d5f1c5405b4e29b87f1380fb84020f1180516c4a92882f8334ab7e6dd0390be233ab129f0955d5886965a5d2ac3b0015f91f11ea556f4faf921896bf38c03ac510393ae4182e76ea8b25bc9aab1cdd34b89ff76f83e81ae0a58f706ddb438594fd37c2ef980de78823fa685b024aa2b439a0ecf056f764d2920b8408a9d7a442d41381da43cbccef6ef1b4b76857cf7a31cb9fa4e0f07defd9fb78e7df94282d4a4c0454bd24382b374991c65ec8efdb08e854c499273d1ecf1b02af76d74dfef554b2637a2fda2db62cc315560d43420ba7bbce8be55aa57b19c5eee3dc7271ce83033b25639d29a98e795bdc47f4898759b742a144b15da2cf22ff86d61d21772bf41626f5c06d973fd153dd1d9fa3e70b4cf4e9c78de7e896212afdeca17cdaa4c3dc3fbe0b2c196e80c7835c45c6d696c9158ced4f5f2738873864b619f164ff3dfd07f27cc40203488b866a67f126059089f43c63521b477162809d0a6efeb73ec3ff2bfb4dbc23a3bb3872191509d0fbaf40952a4ff8f2bfeaf629e2f889e7744f4e89125a37e378d581fe4aba2d2728961a7666f216d7c18be1f9b486672ad0417b999f56738d9adbc10b3067e8031a92b5f137bb5da3a5f5f88ca409524b787619ed810217405f5f12e37358c818c19db2e70b995bf0091cc21c3605e69b116b27af9a9f829c4b6644f8234e1ad9905bfae74e24e2ee86910aa7ca32e467337d845655908c092e3bce9cf37966a03c1f1f31bea6d51d3cc117648a2cb96691ac51b2ed3efc367774346e52478a14f1ec414752bdb1d490949d1b06f1a4118085f35d3fbbfde0cc3685791d534fb9660151c71e3ccdf4aa57bd28ce66e4dd2b4e04f922a00b7d3605ec4612007824dc923819400312673764a9f1a43adbf5ad4890417b9924120450a3049906eb3c93704b3aed5294aecedbab86e69172ac1b3d0ebbf976a7e50f80c0b8964214c6596137fc688c217023c18868444221509a95b88ec4c6c9ca12afe6ef513f4b1cb171bac93864936b923cd6d04182dd2b4eff16121996434052c4a671aa21da057c7320c94b3f65b8a786883de2857d9efde26f64f6f9de17ac30a9f6e82916a403650fbb30b8ac5262cd0c97eeb86eb9a251a2ec72a0cc65b5375f3a0fc940199a5ab68cd445660895a6b765c3ce3e4e04b24617c48c98cf0699812188077d698906b91f54e41dd38553ff92a56ebf84890ae648dd0f02169fdfd124814f0873950277d4d2e4e80908f1d0f5c2da1a28789ebb4d1ded092220cf3a736c52e2a78e43041f5f16457a7e016bbd74724befc3b360a620e8025f594742479a197ef99391af30f826fa17a9e30af5c7339f9c1d4e6d159800e20a81555207e59f1829479bc20a96d64dcfc94794778d47e8058a92c8a7736bbaab2bc30860a2618a237b0f9e7e57339e769eca90d1fbed657647c281cefc1a1bdc5f70c071da63b42f387b76343cef6d3ece6e9ac65785edbe526f052023da7f3abf1f3d979ffc1f0bd810a442222dcfd44a696f8bd483a6a3de93e39f2332c08e861cef40caf9a33f937acb2d808f4f43c21b42492d1757f83cef33bdcc7b7752b7b3eb18b20ee77c3b1ffc1729b6848aea757477caa0535674516d62f1afa9472ff367c09a9387b1d65b8be3164b92a8137d0839a450b96c731db0cb59d2b67a9f3859bc85320f41f538c4901d8196dc6ed50565b9f5b4e1e94c8e6502e056b93452f7795b526277b88aff7b46a0d147adfd9d236d2b55beba3449783f2a90e16fc2f9dcd5b93ac5aedd1a292d1c486f5c0ff5c42109478611675f47d3707bc7df2dcc70e37c9a11684df8053e2fbc516309f2b0ee5e806e2f84d3d68098f3b397f51298d3e2dcd63cb6233d413cd25ed7ea7f7b15dd491ef36f6557b3ff0b76235cff0afd7d0285c0cafac5cc5ed0af3b558e364c37fdbae2508c0128faa096a38e45c3b417a61ca70d0a6593bbc67ac130f258174a85a058f6b214701341dae123ebe4fe8cf3f160b67e3a378c843e1945ffd31eb288e4473d344205db4ccfe250e8c0574633fbe612daa9c84d4ea2e7f13bc08fa27b1e80c8c1d38c81a8006c2a162018dd63171ce71031366d5004e9f4902e9d7f7de610312d19239d16b848597eacea80b01d753fa8f544565b40592023513177bb61b8772a6c7b392b2979b73a3f5d502919e6cb60e705e784ec000b76c6ab047b91277dde7d5bcf82ed59e4a9b2ee62f2b2be157ae3a1a72d8489079a07fb51a7999b8b72e44d93dc9e66c1e29804cdd11e746e02aa784c5b1388ba81660cd86c5cb1658311360d62bf2f1d7b7c0cfd49c6823fc837e5d00310c9e032f880e59dc3214117329c8c659887eea59320adf3f9d6056f1d3e75a40ea54d321be4e76f54b8f4925096b683adc42fe02ae296dd498e8029a9a0932a867b1c1f2f59248ebc03e877f85fe460d815a3afde56a2b08f6002393282e87720398ca304d992a47bcc022cd88cc0cd0e78a8f599550a6ef8f5b83f2ad1c65333a53b962782b91cfa82b2da4120eb8a76678683d0c61ba2dc1fb67f6a728cb08b00f69a78cba1e24727566452bcfdabfca87d1b2302b2b2541145fbc982e32804b1c4e238a49bc124b75110151954a0b37f7426661ecaba1926fb746292ebc85641d3af0ec611dfca4c1d0bf8323a11b7c892c041c8d2f2a588c46f2d8352ec55921bd5683f780e6939cd298896515738c0c301f881c4f8381affcd17ebe8e874b9d7c3c468e4141460936ef55a1faa6b073a5be79db6218122f56e92b8598d2847f0bad6a35ccb156f3bd30ef360c331e54089c4729843abcc90b8195cae34b8784d856223a9035d3824ede4be5246071b5b96bf32a9c34055f297eb5badd5425701d28f5b02512e95e130a0d67f6be277018c275616269c12fa97b28f83c0fdf69bc8258fcf7aaf9bebd3d81ec3e0435b14a9eaed83e2e6f583193292d4725ce2c0e78f28f03bf6185b9785999b4b56538faec332e6e133a90487bc08ae2d68fc54a50796e45421342d25f92dac57c7b4dc8b57bd413922ca9b0c0735e5e47d589bf9edc413f7b10452dd21ecd4151f148ee158c4aa7350f1de6eacc50af4d32dc87af7d2af2c9cd07b739600b58c06a21a272b72a19ebc81eec6f31039fc568a9a4d41be05537b7b0babd35ca1a2aabe23c2d86b25e709c33fa0c6e0d960cbf9b514163d5b6e1d2249f7bbc423ef7fdbe2ac6ca4552ee45efdb3cff12ca378ee2cbead45424e33ecca33b85d21a023fcc71996b0567e3d8001a26027c5fc0f19651428c21f4b0589dfe4a83e208d659749f1244265ea2392606fe232af2e111b20c3ebcf16fa2246cbcb9bf9daa728664a88ca1e1c5f7666ee49b9f629821132ced9662e94b072ba826438efb9771e071a82f7dcec487e04f0fb864a14abe1f91ff2e9ec5f983277fac4be5eaa2cffecedf71070887822f5914417f0a28dbe2c713840c6581df0532795cb4a667a6850530b3c014d58d1410ccf3c3c123d203659799d7332691a573baf6dc9a25405a1c7a36b0264395c7cccc63a83f98488427df564eb039e14a2c26e8b9a8fd8e03796230a1dcb37d19dc22f61e82b083f01bd6f743185a279c60d60ad826bb24055bcaba0ed17170d8ec539c820210c90556c8edea707bf4bbc3c8837bf9e89825a57c7180956ae9814a4e34fb5338c54759d96105db087f239efcecc96e8f3f5892ff48acaf36c8a550a2f0a6b5197133480d332d26e4d2a9c139ece2fb26bbfbbc2ac89a2770f5046d869b9154cb6c736ba2a65e0c9e6b9e1a694720fd908e6e691cf2f943ca11fa16938134171e636b71134cb0e3221f7386f3cfa1910155a4fb247375cc448d4db827a3bff0def62904e7d4f3b85e03da2d84091c603f27bb463177c81ccbc201e6eb6dc430846b5ac8fd8dfde375ddcbbdaed82086be501a751b42ab4403c3a8da2a0b6bed797bc88cb34fc9c971721941d1abee0ee687982f03caee2a2919ed3c110704648242ad5b5c91d9bdcbf2851cca1386b9f949340831000128fde7a08f8b8c012fd66776b608f1a10abf2024d91759346a1a94a6077333490c23e831bd5232e2238c9af7fa66eb9051bd1c28f0ad633a1499e541af3dde959c2a4c6464478f1a49a1ea932655487eea2f17ae1ba2b7213f8f328a0f6dbb9435d93908d3499e716a889f44dc888502bf1b9e446facddf6f8c94e89c8afeac995842ae544512877ca417dd59a4aec1a732bbb1974ea69b471408609c2ee2233615120ab7383a7a0043bb9bd89f98b63048d9349cdd28cb45beaabb089e4c6fda1aa346b23a80de758522ccadcba457816d25e642505d351cc2531b0e4ae025723d435edb460f9b034d61cc2946861049ce223205b8ae5c815432cf90bd9b73ca9b55e7ae99eebd8b3f4740d6734002345f1b537927221cf59c3e08aca05c8ec30d531b16c7c3da79437927098124d558b4b54ab12b03ac5554b04c3219ea68ed9d8051101924fb8abbbfba45a23756301c6a0989a2f9cefaac336bafc06fd29e604129187c523bd880c7adef03493681c374a6fce1c5a13cb6c6e5cccd0b65ab8d3d8879375d00732ea360e18e0ba49e09bba735010a85bd626084a8e4de02cbc82304c4c55989d1acd8628e6ab92861a027660cb69b64fa6625501bae930d6a515658a123cae5f425530eeb0cc925908279efb9bfe82e6a201c408497aa328a96ec4dadc6af73ca9d7cafa11d630ca5fb7fca8fc4a9d0702b940b9c8c79e65191849d47d61d949f9712e8ecea15bb34ed8a3902882cbfe10d506e4de0155647a189c07bc2bb9345e26d60949d3b1214b102aec27ad3ff1073b844a1498017236dc4d3f499dbdf3c0a6ad75d6731966420cd972495ad04b382d21ccd5b28b9e252c2af53aa1762d8f6bf643cd6006d0b5831f60edd018cc001416bee8744de2692f0a20744ce9c8f05024dd4bf4aabbcb824e94a3cebb5c27894cd98a87cb0b3c406da0078a61b7a5e0b7e457c3919ab7797ede2e0d5d0a38f415278b94f9605c2f659092fbf514d30a188421fc7ae2f4b17ce10a7e58a77de4f0f469b9351fe7b440d11931ecb676e1559e5f8298b368cad5a85e90a13a30ab0ae39fe04599d58303066e87748d45cfd05cc9017dd8519f3caca42ccb671c7e6081c9fe9b1c770ad342858809277aee826e9b3b0455b3391b71c61d2cbc3d2e05051399ab048c02c7d19c7f05b56367982ef554e78d2f714840e106bed9c2ed25caa327468269bf49350b768e78a423b33420b546a8656626275b09b7df2aebcf0ade3c2ea0c8f3cf58c972afb7f153df06588d84102179c1d58376b03ca4a98b45a159d81c3206dd3379988d54f9a365f0d44b94c016825d224fd3fdec48c699008cf7c1ad3b7e751629ae3b23f43556570e00e449a38c6da088bfb38f4e18b4c51dc8054da80fc0d901c3808cd5241f29f98369b0b06fc17c2a932c04fb6b06df41ddb878129e97e110f0ba625ac93247b56d4ad29fc41366cae0e673b3169bd22b714c3e81819cec6c483666e8b91dfd828ca80847c193d3f7be115817961163e19b829e8c0c9fd674dc88819219fb79a1bb1054b68c072548f126168939a27428ef2655442c904c73fbee83a2d3a3c593d53ac18412661d148be8c6e31f27425e83f33832953130559a73de2c6814532562128a9f721f44d051a568afdd5dea0fb4527664ca4175ae37189987bc3271426a24a6f3c74d263a23f635a070240b1c1cb6f60ac1b3285c657ee0a720b04b7867a3c546604668de710e44c2dd9b6c016e02a4aa6292e344ecf49e4e2c5cef5817f90afc5022dfc3f232f2619f57d0532b525f0e8973f4d178c2c6d99766f29c287b58ffa5bfef65caa916dad11008c487e6489f09675133e66e6ce045a8900e59463a30e7ee6f10bccbdccd3701f69b6dec665e4ee3552e02a092e6ba3f98801129c7206a3ad621a23d2f92c0f67dda15351e5fc3806a4ec6ce0d913bf8cefd3f1d1e36282ba41d58c8920d5c5cf39cfc7e80ed4edfd32a9362f657fc72b265f24b22708f6f80b27bb6c350742e6c858476d292277ff47937439ff21ca2516e62292056cff1f97f031d752765ba4c5712051e1403737cc4a034dff2f4ab2261fde4960b8a8613890afb68ea04f1e2027932e8aad0e81ee8306440e11a073e67cd93e04e08be816157cc157d28dd21afb60cdbfb40f23c399c4245dee9f8a4e07d8b21dc2cf145225cae8ab105f0b39be3cd4a15b5002ee44ac8deb5312462155da038fd385e5a63be955d909e467dc536afed5cfe61f199cfa4f77b48c35dd2123585086841d725722638c4ad895b333b66d00d4f17eda598645ab89627768a2297024d5e50505df14bfad21a23ce7d96f50f9331f4b6de934137918bd7dc616359d3d7dd70c755addfe08c91559e0b68f3c66ab9b05a93cd5a4b80226f15247b7d193814b63e223f42946fb1884c77fe0467d245877677e2f11a5162fa3d02bde27928fd66d17c9860b1596507f32dc66e8f8fabcb9dc9d06f671fc08c754e1debc20dc516afb08dcfff63b874ac8a62908b9869c34b380009b1fd0c35c3b3b2c0d82c583866f164d907b4a7bf5fb58206460eb6e1a90aa45bcfa8807e06f2dd4a8c55426b59e0151d9a21688df8e4c9e1ea4bcad83d80351df39c824f422e17d83aa2d6e97d5b36eea87945ea2b90dd7ab521e5c0f9c1f9c7096e0546c881cd2b3a0bc7843f02b0ca150dcad75cdc2ccbab879ac36871488be1ef7ac08ff05858767cbac8126bf524784b7fe8c8ce6bf2cf644bb82b017d2debbb6912f9254c309aa5abe7f2f380183717d43f981e5024ad6b707dc582e2863c5acad9def688347cbbcf776f9e1222e915b6568dbae5b57fa88e84a9ba19a4eeea6347793afe7279451331b836716feaec1c684b4d5c04cf3238becd0f652fdd4c6b823f7a179a6fce514ba9f3212830dfe859a9c6bd4ca1754fe5e18dc9b2b430108a29f227eda4d29b94f79a0105d409036671eaafa7daba549c60eda4fdaa8133b4bd1d484ec2d805f74f8157ddde4ee63966858a4362adbecee9bd52ab9c22899d76ca927b1b86ba9d3aaed28eda375a513a4f6f19beb61b8e4182612b92538d33c259807314801c26c6a65689e16387602afc3824029c3086d4ee0ced89263187c052f58656118e73916cc989c60a94c3bc6ac908fbc6e9be4962f7dfdd1713a8a846126630c34985ec3c0530849f2ecb263434d1af4a1e825f3f60b0afe3cfb62bcff7d3109f739fb62142d7ecb7187379b3dc0002757d86a915575f23c45cf7ce99befb8346eb08a051404ce92df4655c085e79584f95275d9d79171465c4f4948932cd64b08c8a871890255bb452958fe32d2bff2981c539cde9c07c722559afc3273f337b21838623f27ea99bc80da35f260b087cb75fa7a961b4b2e2c21a68243478dbb685d4c4a02c370290a1d25bdcf6c2d0a34c5179fda44428b66294b30d79f9cd88d64529596220567094240e28ff7344503c619291d0385b7e4e34010802c3130e545b465866917068c0e121d4690cc91441bfa809a420412295d300e109497a0763a7c544b32c62e109891a9947aa9bf5ed0a9c35684b5c23cff773413aac81e633b855bfb2a2c0fe7ffb713f7fed2a8da7c9e138a7e88b6b6f32ea07be0566e9c7573b114b5d297be7b7a0f119a5bbf9b3adc4f256c4fbd7d7e7729e3e094cfed2388670b0bb05b653188f634d09cb686edb174f54d39267e98f62e803aeea317ea76986002e8c59c1fc39dff2f432c2b45c4f717cc4d513bba9661f7ae423df4c10a128bf58c6198fd489e4dd7edd606d99f9268a187bf720fad30c0461ff4afa004fb44c08198dcb70682d5608211a3ed7d2458f3770084c4bb13bd87f8cd0de4eb6c8961da8505a19354831524b2ece1bbf05ccfd282e0a21becc510a172faefe0608f09c8636c772c8aca04377385f8ee7f637ff1be66b89e117f9abc4092e206fab2a073f6d36874d6e8a5265775bdbaa6e734780c80ae29cdcd83d64e63b67c9b3179c9eda3a5f77678832826dd1be10f5c27e4609db4e1dbf4ae7c706f1d35a1a0a5c0e30c9397e746b885abca06880644327b3182bc3dea199052e18080f4734a1942241490e62fb35a091c411c7359d704fc87ad30a3a8ce6a34346869a0c7823bb12d4ca6e1544eada790116f4631441b950f5a62203e92961d8101f0262883e5671295eac2e0633706216515188ef4058c21a088f2cfbee75a03cd6e3bcfa38c111bfafb007e93e70695587ed3b330662e27e46a34dd8f457fdd006ab3baf8b5edf3e8f52384a7fb60074082565a52537d8daf0c622343a405cbaf06c455e193c98e3118f82b8e0b10733cd8ff5c0b86d5e6b1acdd100822b50428c63d0044b0fe4fd76c303406d96b8ccf2fd7c747dde5269545e069336c072de78ec31eb34b9a52d0c753ed8f8c145a4129509d39f89e3e8ba49661e872bf624add907e181c5948c863cb6b917529b7273787ff1030edd25545f81fdae88c03bdcdd83547949546efc39b7dcd5fa041c4fdd13082f50692b7d22f9f4530c66c6c59a0c55f67727a27b87d35aba71f8df7581cfd0214aabf5c982170a513626559cfd961e292ac8d949fa023a0e7b8e22b10853b350da24fd8363a624df157cf4587fe07cfe1fb570d2ee95788748cd0bd5f06bf593a684bd091fa66b8755fe2fa8f6bc0b903f4c71df23c2c4b2987264922f282d6fe502645305031e94981542a41b5b3a7740cc264ae3e371cbcf87ffd83c705b418048ddd5b2da0a7b8b32059b9f5777be2f30d0e50fdcf9de805d2b62fd262390176727ba8a6f4a24ee7f4819e7c84f1702104110c9348c2732b90ad59b14cc6d06779712960ffe8f7ae3aab61b920fea62d973bc14b702e0f3c45a4d6d10d87479f372cd32eab1b41626038002925e1789fada8b54e1a4f1997a660ab68180832d1c203b6119b5d9dbca6d32f7bd08a7e27a5f00169f6be82d57712820248c1adfc5ff418cbfff0b63912cbb520e2e6840d25a1abb4c4d7dde16295767ce10914609cfea6c1425123404c5816e2de007efbf3d366e602ed2548e2526915586d06dea15326428cb8bff221ea17226a3dc1f2b7c2cb46c4909757e937aee59c526dd2804ea2f8630f7041909433d5afa28572d04c83c23ca4fb2171b759c6574188e91f707449b690b133926ea5364872a5629562267dc099a8e34948d437c06559b4a502a6c52974e3f5edc48a4295a2299c025b327a0e0f091ebdbb082befd45a186406e730d5bb9ffa64650d2ddff0680436c71f5d858b10e0b310fca1d5ea207017c2fe8538fc91bcbfa713931f42c59c0c295033a0b0097b7bedbc8532854f311f32288197d95659b08869059efe6baee5b340ec4983423e0e5be39d50b59daa74b0d12086308e309824ba850653c6f1c598205a939540db86eb2ad5c992d5cc2457b8284ec61d88c3496a1d69dc4690f1d8f240e72d535e0e247ff8ad81ae3b9b9f48b51e1b0f47c74eeef6f32bcecbc5d720438b62df15f2b0525ab8680172e308f680d3a9ecb70f48f6e4bfc12a0533d9ae28bf38acc2e41ca9fe726b928b22c5c97e3d78f18ffba059462ac2db4b929e7f1bea8d850a74e25027281efd7eb545695557f3938c072a2b49d71bd8cba4af64bd79c10d80aea44db09ab4c9a439a036321ac41678fdfb5f23be2c2ffc9814328a0c04ce17f027f7e2be3db8534b7691c26e2099c6a4bf79d294b6a6decc8c5b7ff4d20edc1ebe9dbdd948afbf07a8b075fb9706434221c3b649e0095d3d0214544286c8efbb7f3314680dba75d17e54caf7f35f0b013c5c3405e818a8fc85f5800fc041c670b63bd49b77cab970356678d1c2114c641bdbacdb6810038ca3ba2c920e40d0c9134896d365362fe8bffcee1e47b6795fd33bda32a0d12efb61c1920230997c7e8dafa73a6eb0b56ef2f7879ed5a21b30f0a231f7d6168bfe74a25cdce8c0cb0c2e523267145d986125a0ab16ff820d3542ddb3c3c848265e76adb0311f2b41b75e98851d34e869261dfb218e86d8c4f6383e52e98eb92666418b9d37a9f4c4cf12286e3eaa0881fdaf28658f4c9465f32f007b419af99f93287c8a088ce43c5f2ec58c83a586c3747472d5e610ba6f70ead3b5421ebd73370eb9f07b00220d9b3b068792511ce78d0e871305d3f9286b0683b6bb8ef7b7494ea958bce515fe9d2977212fe29dc6387c818b1602dc07c7fdca91d98dbd4db94650432d80b3d9ffca5b995e38bb97463a48d9361962e89c9a429ac01883571a13d4390b331ac88ba9acd9745260c117b9f4b07812c22085395f305c047c5d75ba9f9f756fbe95170ccc5cffe5b8a3fb3d10c264606cdf7616e2bfb5873748abc2999486a006c194a7129549af5b060df5adf6ae5b9f0ad586f47c97f51c0676d4a58be3bdb61752fd10dc0d8a9b8277aa0035bf440022ee7c506aab4b1c38ef44e815027a89f04d9d62075480b1c25d5efa6831c5e1ee0a78593b03b370c2d5dfa82641c44b4c2f87e130349b58c08193ee2448fe558e619801fc117137e2dc00b50e564d39263080402de330aba8dd2d02c409b44822add69dc18a5064841e6408c896e258476331d9db6e6b0e42b77d30bdfa341326ea40c6d149526f409aa7df458ff7906593cbae524643bb2cc18b68fcbae0f41967f6ef22f46e53414423745fd7726f26e1c2640067d11d08630fa88a2de3abaca4eee4888677a949b244b8bb45e4f0af52a4768a8b683a5ba94b881f0679d7d0d72b8a246258864507004b5c121511a718d1d459f7c1e6e5e75625c75fbd9b3e8c097eff96f2ffa5c2b995d36c009779817922e5b640a8f83be5c4d597661506a53fbe4293c6919392907fa10ba1e0190e7243c79ef88f9909abf38331898416750ea5017323159fc75144f06a4f96cacc2b9b9712e6e04fb9c9c0de09e26d35fcb9bf786da4b07c88cc3eebe1a67aa8c47dfe580b6987c07a089f57e6d7609b881103672753d95871c1a34b3b8eba7cd10ba010fe4571bda175c2d416dd701de8bf3703b70ddfbe7a744e64f24b14153566cae25817881476712b412a69895a2db703de1f9e37a99724405fb59eb7204899a40886db10c10a9463b3cd831eb119e027ca67900de93a14a33041ea3a44d1f3616ad35d59d841ded35c435d006bb8dca800abce78e0cc69f7effc377ab905eeb9157677c612890871614dd74aee57f85bf33bc4884883e05e231d29ccf6b2a1316d7a056e832b1f63cf80092b089b6ce45d0d2615e4468bf19af022c51de4dfd407370ce4ea2e2e55510fb175ab454b00e246f2389a0963bfba0f2e53fea1ab4534b46c38eae513eb3cdfbc0b198645a4f85e0e64cb28d09439bd42a00ec888a46754bd50c6e6f47396c468504c518e2da64bb9934b4fd8e11712174f3183880c22ae0da034f0be148177200fe482cd99592a20dfdd1ab62903b226290f65ab6f61d98851398bfc3c0acd3c2835658a9157576704ca3615af9768c58b9be5a54aecf159e68aec09fa7f6d2c6dc861622dcdff0070d21f03887f0d47d9dfa9c8ab384a877f24f0f046a48641855f867be49252c7ef11e15e9f4350e06b81a8cf8dc04a194f42484528ce3d7c69e6f589e50140a10f11288f5e6a5247035a23af3e8e99cb6ee1c0112d0d2d60e22179c4e64a7d5539f1708ef34e2a2925220aa45cb5e1ad7a10dead2d4391dfbbd14dd4725ddbcb83dde05f5c5a140d3e22a3ce2ccec2c3880e9ae8d62a4337a0377448c3606feaa03ff6f5f00e298256a2b2feaa28f624cb219c86b9188e69857119666b108a9bd85ba14a1d1c4d294e06d4a48d11c648ad2a27a029c27cd54ecf5413494216dd99120ca9c34b4dfa96713b5b25162220be0828a1791d876a2b0ce035dc81b1e1a4992f08d89549ce412b16b21096bcfa190457235cd48664f07189265954d38f3a6472894c0b2f4d3108a1544778b691a508d7c33c3bb72b8e0d2349330237acf5b9a1dbfa06b007b9fd69411474986ebd3d2074b6c6e7d2d42efdd19ea90badc22531e4f9d9b63dd3627036955d5a9d68d9013f4399958c42ccd918ebb3aa1b81708fa9223d262e1d953099eb4b4b2f5a12f221ec4c70ac5c4f3877de5b865b58fab4a635d7e8913f6bd98224a6c396f1940b274146fcac613a4be97f61be8de734540d8d9bd7e94b95b335bf84018ce68bf1253fb7b7c140f8f1e63da737963ba9b9c84234998785c7df9ae8bcb1b2ce35502e5f8652b92c9d01b2a3d4ec2243edad631c22115f380931d909fdd003bc5b92916a7ab5979e03a9504303adea0b48f5a8e0953956389d6894f7c631ad0da74e63eaa3e3477897292855bb8aa008c971ac547c5402e335772435faf0a4a24e83a26a0a81c701b7654c2f526516754ed495e9d332d4e0fda5c63012aa58b2f0f2e276178c847489dcc04ac16349abcaab745195bce83ff54ba50c9500238c33c9eaa3ccbaa6f50be4eb7c07dcfe7db39cc40d64a0884a23f02c6d45f28b0687c73639d53016e29b79cd407c5076f29c31f0bca4aab3c5e4a81792b47d56abfa3220eca25a50f4ccc8e56072a023192cad5d902010436ae2621dc322b3ded21d303a2a3a853b4b539a37eb9af1e7feb109d39025ac2a8ac26ca03dc9052e79f30f25e5b617041d14a69e8854d9d1aa5d069fab701fa11d88f7fb8ff183c6155c00a38191134a620b80b2439325187e402f4958520045fe0ad820b248ae7d3eb081d1d37e7d721130bb2ea561dd19c00529540ac826da7d600144745c4511f7060dd7fc699f58d9fa82a55cdf014ed3c08063202008047ebee9e648fbad35be59461a092e8806bfc626f6c0b5e16c1130391979de5c8028ca219a596c76712ac865d201b6ee0e6330f04f35fece4303cfb9d531b6a00589858ab29c7613e940e3f4959713033a98c46dd5dba42400db260beb9572a20b2eba06858380e13347ffb2ed934163b98449315815ad68da296dade29263657058691cde81240e4d165e2718f828bcb731b03fd84da78e5f90ae8616df7248aa8ca01dbf5a29141312a73b3634086997760217e60bfd444858084286b86a5cd7e9e06aa026ed60cd200be8cc2903a0c0730885aa7ca9306a28f8ce3d76a3bf7cb8733b7cc8dfd555959c50a8dfd2a21097258294177ea5a974b8c289744f22203536f88d1fe886a32e13b023d6b250e9689c69e70f1b85e6202cc18306e8a16110c8c2827ef413dcc4cc54047e4c106557966857e7bd33531355ab1606b91a02b2d7f60a1a8a88e22e667a670e50ac194149d45847bc19d9da1b5f203cd2792f225f7c8050a583579d484926c8f68decf495c67e9a059949447b502d5ac14ab3fe7c9a69aa8acf5938475088f97e5709580af96547328431c254851ef6ecc9a21459205549542407fe01ae4aef8131579a4cc20cc7ecbfe23370596ea6ada56d8ef48729b1908e8512614e83fcf6d48a4da598c455431b8a41474b6258a2ec755c29b654dd7e9f46773a0852b8a5ac3316ee4b36f5c5e9d1de36879259a4d306878e7f0f43f18b0c9389cba48ded194bfd13868a6cb99a6f6cbf9af7d280800327f1f75d3d51302511a10a6ab10d8e9a770cce2360cc40148a4296bc8a526938059442299e4bc3f915516c6ca3a8694cf080ffb12ce557282faeb578d247dbaf908e596b46eb48477fae836d568f27a10f722d8a72202a1598f80781687ac5318ee5c1ab418cebfcc519059e1cef4f847ab11c2bb658c745f34bb019c8f77d01362aa6bd424eb62e0088f217c1fb9d59330ebb2fc69099e0966bd8a7b00962e71ef976e4adf583601228d966b5c2bb6bc294d4501c3eb7552534541341c0489aac72bf1c5d87f242f60f694744e348545782ec491cb87d2ea9038e6af6feae8d297568d09c841a084059ea48b36705b9b404f8a5526c1ecec1fac80fc0bdd743ade1aa242270109289710ca002f4a81eb74da01ea7380890f63c631f4f34e3bc1c754e4533626dd14c3f392aab393ef5ab66bcb14166e58221da3ffc3eac318b934026d4e8aa3869c8ca86a8042701d1d78fc609ffe5059135a61ad64cac5aa891433c385a1a5df54a200c85d70f61b5f80bd53332b8512b44208d28706bd48c093cc1e614f4d6c743a6053c05de4a28bad6159d45811802a69628643006a6344009c218b97a50c9bfa771fb07775d29ac7aad87356c19f50a148a3992c41df03812e44a8682143f4c68d05ad0186e84f8faad4b134cd463b201b8e625fb039dbab17f7ab821784c182f456c61a30bf5cd7de49aaacb8a94ea59974ca7e3090db52df01f0d7b328954f10eb0309160e7b33daa60495259d63a22575b21a165e6f905ee8b5d849dd26e78cc719f5c1c2280ec49030d71ba68f1b88b1b718cb5121308504f71366074100be852ccbd4a10decca09179718b45c932ffa0616f6b5a00ab611f962a9282272b61a831bde5669f411c6617fdd8051e3e3315283880fa25388f6e1dbfe1af2d9da3967de325c8ceeabb5bf831e1b3107511770cf510862aca17b5abb3fe88500521817423d65305d5a05a93fdbafb9eab405b6f2ec2d614b39672f61291393e815cbfe2692d4cc4dcbcd4852cb1768517376104f7eb0925e8db41acbad9ded2989557fbd1414e3a67f26f1228b63afe510da89387fbd72909c0eb9239862c956b292651a14f8b06e5fa7275a57c2856f48179cf759c10f1a610e1897ef40d0a58321703c3c7bbfbc005d77c7b301c71f9e940f4e9f993645ea2e32d9a16c82b248006d6328b6194b789ec67449b6f4fe4a71c2de31030cba718cd3e66e77c83a8207d5d05e403bbea29915c8b15b76e58b718022240276df08d07ce55a3863fdb0c791dd5c48be50a4e69b0615c2b9de522deddc929a2136d879c8106891e490c8245a48eb1c06777bee47379a02f414c3d1f42f0b54443e414d1dd25791604222fb89828272a6b286aa6501c8ea4b95b706eae302128af5d5cb65da9e7c712b4a9f40a1948fdd302fe621b86a1e0c6b8dcd86df11be4bf4c15f486855c8c9969994eff6becf25fe4f8ffe4f319d5c2242fd9b0c4a5a450ff194f0d63de42e3a48959247ed91824af9a71f5d7fa393aa4bcfdc652ff8988502930707338d17c34de488aa8d85d9841c342e4b3b50598fee92c9bbf605ac21416ec7b96b3008f18a1c86c6c2d2747b84d76c663bcd25520f177e4fdbb2e697026878f7c7a3e751bce7f6e1abfa6811a73964868da16349899641662332e11d6fc6203ba8b140e530dda21effde41219212acc03817ed0faa3b9f149aad4f0fde5c716835555402cf1a71e931b66bf1e88a6fd132395063deed116407e1da150b350d11a2b0fae8e170143cf32b2182df46ac3ed1de05949485fb8acd0ff38d817fadc6135c092ec788ca686ba52b40bf42c491912a84fd051a6aa2ac39ade3bbf1e8cb81bb06e95f08e9a1c3499f3942dcfd75fb535559bdd40a96f7233ae4f88ab3627c29fc9141d6b451fc6b190100cd29009133a5a0d9f4b725083b94646554879aa560472520402da21c7f45186459c211f37f8a12d0f7ae638c69979666476d4e9d3cfa9c6f38f030014573a2fee03d6f9c849311eb83b9c370e5b78412ff4ee137085845f17a1068bce0004bf251d138667debecba5c58b3db14b0fb49aa75cce7ba1b1143f3fad4ff08e120e2401a71e6256a487c37157e61acb237a31f28679cb5177b75ac5a7f112fa5cedd6980da105e4e2cb5067e91c01140abffd44bf48cee48330a791e1f7079ab2f373cb3f331251da2578fc3e78a555f108b45e6061ec7491a0da5aa6a484c6926678e69528f8371dff1ad95da61d20cedb6c890b3808cc81f80f722da9a4c6e5f55bea10dd50fd81c6564689eb75d6bb78446f3256d549ecb3bb20a3f1855a3a2cc7687cb7312e4100020123ab17bfe64d109dfc5305895481b60da7201ea3b1021dfeb29298c50f5df128fabbd84f137db3a854d510b297a37635293cb31db40baf30040d6e7bc486e8ea1143f4c99149724262d5168daf2025cec01fdce92557dbb87d163e93cfed775495bd74d5f1c03efe3a96af03623c14ac9839c144bec1d195a9f9d4c204388693bedde897422a510f2e0896d59e905e8a53e6da9e6b288aa3751d4bff9403bfcdb264b50f17ad0c41202d4c0aa8a8d0b6c25c513b94c3ccaaf841974ce40961ac486dc3b05072b16122e22495e351b384436a182c79206b0b0696b98af628aadccab36af0e12174d0fe031e0f2fa89edd2d011ed9c4beda137f1fba245a7a55d3c3d2ba8ebd09772c0002bcee5236ec2dba2ee41b8ad93e5509208071b611ed0790e32da9fc58d62a6da9684c15861c0d5fb496c9c5fdb7a7a08411b6be1788b6308a18ec087348d25f85a46365e8c7f1ebbe7a38373b75061e3a017a36aff8c7dd10ba47ba07147551bc6849cf7b03c8bc28d6a371bb2887aaf1297eb2799e80b38913f20a49a0dafe43dcf9c270f93c056c05f9d927f395689200271dbba3100a5c85608f0a4c8625b6b080090dabc90787d07d0ae3cdfcd36f95dc3fa616391c418ff0b403c0371a0c68d22b2f548b0d93525374a055dd9c44211e0047bac1da1a9a4c4644f80c50ff5f0c870608a3a791b270332a1bace3906cec39658137af2afc37e4aacaa21cd0b67d5c905e458a6012674c84e0eb547aca5b52c50a4901587989f098067e66995f826c841aeea187d5987cb6ec20b44d9da4ad2661d05c9579360e183eba85ad155bde702220dc8b462c3307a2a94efc0e6efda6e6f02d8b45f82b71348b0a80fb33744a26e8e58aa872fd965313607657f1e430913a0f6991abb92e083d4a01f6e7d83b7d84c8e58275e0099d8ae2dbc174c086d04b214b84f323d27b38aed43ef8164e74ea326bf745d9ffddf1cdb445675c9d069f0e8610deac28f20f447b69d17c56d714b2c0e2498752b990e02e228ded8218ac4b6e807f84799f93b29710579409eb505eecd0f4ddc19738d623e33487344dcf619786e8a895ab6b7d873396d4c54024d8c8210d8be84de100732fd40b91569ab295a23229a61e5e95c7509232d57b58904459efaca07235d94b2944e688ddf7f9b4485a5f00aa918de50c3f8d375d15011a3490053d2c1bd72e1e2a6f42c01a0d6163a90f0cb6c154e646437bb3a655c6de471ce4020e9a2fca48025c8fe9c029cbe9e34c13da255c4147b284880fd5e808566c109c11c7bdc2dbc9046bd8a7e9d05b65e9bed12b609eb0eea6dcfcbcb9587538282b9d4a250e4d916d1610e17b640f9be3cad913468aedbd2683f9b7aaa9e8b5d4aacbf208bbdb1e2e8f7110734a5f1619b91d02d31ed1f9112a90604fbf0b64b3c673cad455bbd85c1ae1a53d5d06eb2377e60f0c687ed2ba061297337105262d827a6f11df7bd7f95e81bbfafc341a52f5ddaae4482c67d52a9276968d9950025754c655684a98417a89b04d88a29e65ad331a232863563b90521712d7db3818acca29bd7554574df582e9e729a8850cd8d5378b883a8306f15cdfe215314d42502844a5cea1a3a8af11024b5fb3fe3248b65243924be0302ad0a8983e4668184b2c686e8806a414a03ec2d81a7f8147df233d58a16fd91bf94209132c92631f95c688c08d5b90a9c63c4059a779844667cca3590030144b0c55a42498672cb944ec3362de25dc01269b01f99022d3e8f413438c83bfb82b94144f540f23334e17f531861f07c73bf9a095f8cbb10261fd0a6182755a509b7064cace13d683b1cc2a770dc9fa02cbcce8cc280ad04438ba08b2fabce04db09c40268bb4adbf402c572a1d0b488fc89f019dcb4bce43ebc561667ab75cf8b801635e3692c57977307dcaa1796081a379882332553c36bb44549cfdbd535cab642617645421bd0708c458e1c8558a796d1b05fd8e7254c0af88e5df6b30d471036a79ea7d5c3052ba4a07566b4594c4e973b629d9a283d1bb5eb0b58c5e22dd666b044ce44a4fe0d3d4f9baf58e1fde514284485a30b80c0080846118c6cc0c304831c01d98f3b420cf19a64441412eaa5235afe7d1df84ec2de59652ca249394016308370746077e5f5f7f8790be39a5f48159ae866cddaf64a091fb69e88c9093149778fd00e6e543179b2f5630ac82ba66db66b9ed6d77675662d2ca18fbfe3116a76d6cb85365fb90ab5b4af5a4ccae94f2a3268020d06f0ced7c792d5629eda6a8ae2bb832f7c31e0f0a0232df8fc36f28e57cff2bbda534bd84406a4204db94ce5e978c7df60a62cb2b88219ab7f64fff545a0abe4c104159e6b52f464c4641397e0593e367292034328a47ea65702713f31aa3641c10e3b3df6908c87cccd45098f9d4cb701d0b59e6a78642ccf7f65dcd32dbe64395aa9585cc424ec5f8192c802fb614e611e365702bc667b8a5bd8c0f5596c13e543946e74335838bda0ef832df5a8f86bd5927fc3254aa9a9a9a1a99944c8d4c8a8686e62d4713b312c156ff2584d12b883032d9cbfc95e1500f8d9632f6190a774029ae6321a7666cba5931f00c67f3317057533ff336331a0dd7f9506599d71e95a1360d85fa8d9b89e1a276c2cbfda9d4f7a7f0f63332325ce7a79df0b28dccb762be5f26e6fb63709ffea9b6fc3a1d16548f2ac2c6073660d880515db5c977b701b379b37ac6d970154337815f62acb00955557dc4166f2a162b2760ebe052537f4f172cb58df702c2a8ca969ca3bab012da3a38f4716b0821c4b225d2c71b31a87f5f1f8e9b5f391433bef32af9b9959aeffc4aea3b970249e9c88d72bce1452f3343832532354530f1c6fdf79d0bf9b3f51f54c282a40491e20d68740489200b2f28c32c9a7c8b3331e517e133e524e92861e91b5e05b27862b8579db4a45703feea0519be0d0c32fc1931c8f06550c9f0554964f8354a64f829ee9521914cc3b500b3cccbfc0c9792496282e8252935e59997b40495fcb4540cee25c920e86968320d4d96c233f8352b2ac526acca70ae34b918bf64f8405990ad6b9897106054809474940790dfcb07a10c26b3f27b3d59ca3e6cf12129df98c9275e149b74a6d861a63849f6ae8768e7d9d51d865dfda7f7b8c3febd4e87603f5e5ce357998f702853585fc6338de15e87a0e12ecb94fe8655d82af691e5fadb893bfde304f0c586611f59c6be43611f59ce5ec3add9ca70f7a7efb23fe10e0a0161d3b08f2ccf7fdf65d847e63257dceac77660dfb8355fe6a8f190796294be8d114a4a29fc497dbc5931d68718a5eed853ea0333fbfe7a92d44312d9e14f7fab134388ff3a28047107338fa4a1a4ec6de3468f1842cc779eb3184e2786109d1231d243aee5a0aee59073ce21facf8611a880214f7a93c7959cf7dc4c7be089314e672524368c0debb3e83b8777e2bb9c91897d62961e9482c163749fedeeb9391ff8724777dc6d5e6c1ee9a43f7bdc57316c70273e2dc7f672badbf4fcc8b27fb1e9814d0f3a665aa2caa607e2cea6be1a5b52135d5f8d9c050061d9545995ce06becd5ab679af469654803250c615faeb78ea42c00467b4e142f53b0d38c1a64b0f3ee060a3f9bd7ad0c1739af552f9bd7a40410f513a87eb53f280e44489872f240f60c42c5ebc582f3c4891f1808597d9c3911e60f8210739c841173cd0a04a27bc287de141894a252f483e259b4848994e8ca893134c61f37b39d1211629ed80e50629ddc911eac48629bbaa9a304c76773224bb4b29b72cc5151f7439b22209153c9d2a17e9a0088c43286438c28730b630c10d967858b0697b1bb7a53a845711978ce5f72ac292bbed61d8628523daa37b4cc1747777f768248736b2c3a971c082a4c393fc5e3a24c94f8712a030556f64a5b8bbbb67a086212868008430472318ca8a34c98c90ee45846477f78952f2a2be74e892830ff27be9b02577353f1db2e8a0045b6269408fbe51dba37b74777777777787ee45b6c8eeeeee1e6308393405618a227a2872e287305e47c2681cf20f4ab264d1c41518fca08b048c6019720222410c99019051103e7c514ac2e80a2e944ac002243948c1611be2861c9c0c6d587eaf1c887278011325398c80a9d25a690d5bccefd5a448132f4b3d52f0424a094296a5252760e2c4d12c3245a6b590f9bd724072a5caa122273df8708318b038428b1c2e5caad00045922c52d4a123d824e2c4050302620b23be600118215ef48044952b7a2065484912476020e3881611a69423db111e362360cc1ca121c60b2a35037b8359c4095b040baac1541a242830d5af9ab8d02640e4a5d9c5ab3fe8e81401824c91cd06233240d51350f9bd9a10e52dbf5713a12b33dcd7177a395a010ba097560152d20843463ad533f5800bc0ba76d71998ec20776fa907869b8997dcddd5d2291d035c5e4c72d02484dbf86064c9d6a96cd848b0d924d96c886c4b3d5260b282dce530d9b004b1273838573285413843388133040e4ae00c819429c519c26d7e2f1caec85489103310010ce00c4f5ed04c3c5144031b2e8413a32432fcf7aee5807e24458010be4b9561d19d4309d8448082439008744a77a9c47d7dbbc8a694dfbf9860c90fbbba592c59047c427180cd24496422244c869fe1d0e58543960c3fdbe2dab8e0260b94f1825ee5f7bac1877893dfeb06a117119817d116b226bf17d152867f27017ea031f27b110d559c2eaf255bb8fc5e4b56c075916d26bfd7921f960c65efb5c448fe1c6833f27bd950a59e3ebf970d45a096d8c0434c7e2f1b68e8aaea0bd6c614a364e9a5a489fc5e4aa6e4a7a488fc5e4a9ee46e7b2969a2240619992e6668b6a49234f14a9244aec9ef95c404344988b25712a12cbf179230f9bd9074c9ef6f7e2f244cf9bd902cc9c8ef85a488e61ac9f65e489e6499fc5e488ee418f9bd6af822c3cf6a62d0a454a7f835474c07ddc3065f86cd0faa5fe9604aa20620c0f085491820684245103c50a2c10544f0cc1d72777fb360ad15e27438b9737c50831725988e08d5fc5e356c91bb1ca66805a8a804478c00490d2320e2880d4cb1e58a1d3ce1830a80e065d2d003ac43977cb744b154b3a84c355bb272991ace25f9317ab4796243c3f6966c88b6293d365eea9102cdef55c312b9cb618a5860509ad54750ea6bef0e6dadee95c33ef6ec23a3232422ce06c37dd4acb93ab245db27c797f33cc3c17c359b1ddadfd7a1697fefa37e06c5753f7986eb7eb2632f2796cd70d8dffbf7d65b65957e3ba9598dd4ac599ba875507935a6923cbf933c1c2d44c462f068558391b791911194a3a3eee0519e464748cd9a535e8df92b26db6dcfbca93c1c3009696564eb5a08a9524ab1235bd7484253664b9b8eafc3e2888172ffed96dfdf48f69bf3ac458bd9c7da6d1741d95e7e2f1304655f15d9ba16cad1541e0e17cc399270d8d403f86566b6adb59a66b59723e72d0479f06accef5224b4b4c4644b4a72f9e5bef81a17adbf8df1258c17422c6250b3fdceb57cc48c7d0bae2dd7c7f08f981db7e0efda72cb47cc15ffc02d00d9b2e3eaea1cfbc9ab31df724dd44d926c9dc45300d4e2156052793890a016af8ce41e296da3d86da42407c21e434adefc7e52fb3d6a3d9eaf86e16e5283d8607eb95e9a4db7dedd43495e8d87414b5b1e8ef6a56e29e78441733e844d1e2492d4c4fcab35f677a9bb467fcfa2dbae1f3bd894e7d7e8734e89ddd50d5b35a16af235ae7367a8f98ba74ef26accc72de4d580613aa859449a3567c4108cc52dd4423b1ef8b2aa61eb1a890829ded8bc89e20d15a63c1cfe131fb96799a67d8f14b4bf6f841bb3a8f983f3c513065e8d2802d143280f4777d184b9fe750cebe8d1a5cbb9c26193d8bbc5620e4d730ccfd50d1bf6f69dc804ece7d469d6ec7c3bcb322d4737b9ce558fce01f58da900d00ba7cc0e29e1852125bc70b23786b3e941bdcca35ee62f8aeba2261a6243fd4ea33ce6515c0c16f3188a8b397d4c8e985f2dd94e3131b5c6d41a1373a5d65a85f1243513604f59116d1fe3bb466aa45763be369f729dd454f2fc1551ccd3ccf7b896790ff550b746a2505ff3ade5a87189c24b197e7f135b9830e2baa1e430c75018ea1bbb7ab564c31e85fdf4d477b7ba3a9fbaba32da4eff72d744a7ef26cdca5d84aa41a928ea9daba152c5f9c04cd4c91e39bfd50f7d8264ebeead96ab672e6ac68c9cc109fdcffb9e79e7503f433e9e4a67b8eebd0c6ec587f04f284761cfa75317390a8e7ae89f7e5c31776eb970d4528fe2fce9e9777ab564db3e068e799a266ad2456de3f4b5fdf4f79eba0db7e2df8fc1adf83d110381ab27db7b1d1de5ae914e1066fecbaf23a33cbfa7b48da4b6f15e26a0d24d6482137489b2050541f97e7fc9f7e91153b6349a09a8d7b49f4caf434ce69f0cdcc5fce9a966c2fd99bfd9ffc8783f45199cd0f3c8704cc66332f04f9019ff9a049791c998c1456919f7fe0c6ec9873bed5a8f7cf85e769e6330e536ce07fa04717d0ad36c3c2dc084f915e61592ad7b49f35748362618b4a2619bb3fff6927fd6b8a314b7e85ffad97739289cfefeca86ade56fbf73f5ca86ad7ea7758e5bfdf6edaf6cd81aff88f9be7dede757ada761c8d6629bb6e9b7368d399b1d32fe52865fbf555f56143aa05c2dc6bc5b1eb5b016f6f03b208abd6657bf5e9a6918b403993d1c10059b8b7fe56c9fddfde419b9fea7e58018bbdae793cf811c46a26d83597e37c5fcbe8e9f69adf74c8c437b0ad05e01a64f18bb78ab235b3bf7b211d8f124ffc1d9279ff3a7f3ffc93f3ae853fa154a5964eb49bbfde94b49fbdd3b4269d892dfeddd2da77dfb7766b0e5df505629a76c0cbb3e27f6946212ab10526bad9d724a39e59cd64efb6a9cd266c77cfa3a2650965cf7235ffbc975f2b387b1dec7dee2fbe2494a39a594725e30eec369af95ebb62d6755648b5d0e4eb3e2a77a8b1bbc317b6d63eb6e4b35cb4605736cd90f338e2318d9ba9c9cdf817df63a308c9ffe7571dd4f7e3ab0a755cbe12fc27882bfd3307b35fa258da0a577cb11e66a886d65c4da4c4afb735afb34cb6ecdb287c306fecac806532a4cbadfaff79e2807946d76d0afaf83e29f6efee929d7cdd75cfd93311df7f55c1b336f16cc0ef35d15d95ac5cdbf0eebcbafb8ca5f15d9e0c7df52aa8eb2fed364adb2be78f21fe1c62c9b4dff6993465767d830fbbb02ffee0340f93df6f2e77b9fce2df91277411ed775ce32eebd1ada67af71976bf9502030774132f5d67a3ac796124be9fe1176f36d7a6c9e4f8a9c1365dc61b339860de6e4986b063d0a5b67c487d67754209dfb1545093ed47a20ae3f19fe8596ab3fe3c3f8f05b3e9410be2779af2858b267785baece5e81c7a3fd098847c3a7b77e71ac8e75ccaf1fb59e5901209ed3d7087f7ec3f93e3b573fe9dcd5ae9ec07ba183ef50e38694c0731a520284d01f72113696e4be11b6ec3d3fafdf1d51880fa4638d7fed4fc7d7b8eea7ab1f6990248f90dfcb09a69c71966baefb69cd8cebe6539bd9ec7762362b00c483fafb73fa0cb3f66fcdd99fb82125f0a032ed8794c0837a0d855f784d4276aa9f71f4af95afb3c4f5b67f01654a0e40be0f366def05a5896c7f5657af8080b5e58a79beab73272950fff4159b307be65b57f7ca68b3df6fbf75e2a258209b739e66ccaaffd868b95aa3c076fadbefafbdc55dc55d0b3dcfa9f37c5fe8794ecf8013fe794d82fd9fd724f44e6c7facea98b8da9e59e41735ef6ebbc635ffb41c353b5dc3c5c0d65aa50cb323614c105d984d2dc7cc57d48feab17f6151a209268472fd9925c064fb33ef9a0e5b839f77db6b6f3f5eaf791a5f833bc7af92a141a3867b9906f7681e8fd37c6ba667e62bae3c3e009481f8af7fff1d77b6555383bb20b9e6699e137a9e8ff142cff31f03779e1fffa8f042101aff9a847fdbdf9dde9f866bf9449b9e8ee6511fffe51aee394b838b92fa199c4f4fb43270a7c2aece33ef1fe39fd633f3f63dfe59fd7f8a83da0582d87c94d4d3c03f374fe377220dee87c6ffc8f03c6f8107ca337f531c10dc3ff74290d5733868f5ff8f8544a979199e274a5da229c39cfa25b02885c93766290e283ff713a57e949a6fad27fbfa34b8d724d4703170e79806a6b82ec80c475f3e00b8176eb00ccfb3c251b27f3c325ccb27480ce7035bf25f46712dfbda9fb896862d76b500c2d8ec5b8cbe8dab338efe8b18e7b94edc8a5c172407a140357d42e2dafb5eb7f63bf186865ff55c7dcbc9b7d242c9c3e2167cf92e38e63a6802500602f517773fd9de1327b39d56ea92fd1246afa67c69b612e273ec9cd0808c55e3ca49ce2a885510dbc53e34f27d8873d8ef54f794ad9c6cfe53d356b0d8dda2326bbb4fa72c7b1a7cfb28ee8520da9f1ef536487b1416f29a84fec63b11e542eeeabbba831dc5fe6d99b57c5cfcc37eb7fdecaffddbae0dbebbb5d967288b3a3d1667632fb6d971efcd38aeb3d6c6d76e1b05c6bd3a3ab203ca7076733d364329a594f059cd8a37e7e39c73ceee7921a5d3a1fbdffb64163d5f1e10a7306ffdb621256dfaf1af7ad5ca0336f7ff53d5c7186d6efcc366f9f267c069a99df03dfbee6fa869f025d7f2f19c6829f18fe7c4509e3f5d1bfd0ce7fb001d888b5890837fbbbb330a33c86a568439f0ef85f0e7c39f0d21ec09e78577cef9b2e137d733e1ff80eff01f841016400a1804e5ced59425428e90f8f3654416f9e567040fe8c39e7e28a59474e21ffdf0e1ef44e9b959b70756dfb1ff9c74ce39ebb46eb1a9e580feaa7803f5f17bc41b28ac937dac19e7ee9e6537c31a70b7d6ba750d342b5a0c04ea5588375058e549564d3615dac6bd32831a8f895378e7ee67dbfc0cffe8972fb59fdcc6595b77dae79cb703caf3dacfdce74b29a59457ba6b3960c6ecdf19124b6bbf5acff2a1d623b1b514de77b526e1538a7f40f8d9d30cff804f5d5bbe2f5d050d3c0b2788c5079cd08066c567ad8ab64e47d331ff66599651aea39973737a8682c8135f30ce0533a642bce131a980c396fd8d8c7bcd5221def0f93bdda359b1334ea15991fe0f8a027d997d0f9dff63be5779a5a088dc30644833c9e978d96727b14b3ecc40240602736b05ffc73ee0439d66c5784347d3d9691b1a0a25a440e9c10274ef95022a596257cc0d436e081bc6f9837e6b85cc25b36b39686b857e1a39054f7202f27b49614a657301292d47e5917d8b0269c9a7f6775a6707facf0c37eebe85f9ae7b71fdace3f481b2e57c68643a250ab57dcca33e06076d74050c77d57fa763642b1d36fad0032df934f2671a59e21520377f46933fa7ea47ccad15e0bb6476aeb1e540ff4e4312ba28e291b0bd8ca0925f9effa4b55fbf87be0f0c03887fe3d50f0f7db07fd93a5ece31ed0780b2ab1f283be7432377ecfb17ff4491c0e3792148f6dadfd7705086fbef5fee85282bc82079a248ffcf73f3255cb59a33fef397114ff20f657f498192dddd8824529272d47a22fe91314a0e3e9d1f5b1525e5ffc30786d15c96f4e1c0f14884e76ae3633f8fc61dec17bb882644dc30b9968f7eec27867ff4cfd92f1fc33ffa31dc02902d4fecdaf27c0cd73641f670603257ee655a1fe618b7144c51ac924f2950ae95aa5c0dc4f652cdea0e28da8fc2027d774c880d665a5d064c4c6cc9d3723f2af0d88f1a953076b9b1ae82d87c686416e03fd931d71510186291f39f4de879b0ac0a3d0f261980e19ffa41e8bf26217e7c3c90f399cf23e62831fbc40e283b07e0eff4ebe745974a3aebf44c39cf928b1a0ff9d3bf9af0136b06b51c387e0b208c8d6290c21c38125d1b7772fefe978b15cfb7f1efc5260a93877ccff0e9bc9c67cbc17fda94f373d578cc87329b9dab472b618499b599b516dbd8f7585b5e4ceb99ef77c639fdb1ec6b6d69ef6a6d0255ede3f6773efc1fd457dc4d7ceba3b68dfe90127836066c753eea67fccaf13c1eeca4c598e7ac714698adabe5df17b53ed55ab1cad94c9b1df5b1d7513150a66e718fbf7c29f327eee28e7e899d587067d19366cd3f2aa2a94b046a1aff69f7058d524a8b5c1ddf7794365a1f988be843ad87626804a1bc1a33339482911d8487a46c9754d686603c76107df845193e7da8ed383dfc1b1f8df1e43d34dd85dc72c578b59f9b9d3efac0168add432e98e973184f9da4e98bcbbfaedcf291c97b371f196cd2de5fc3f08b8f98e943d811c61384d1f573e58fd0ff5c096b0375e781dc72793ec5fbfddacff88a3bef997f3fa4041e19df3fe331ed53a7b731c69f6978842112ceb49ec6aebe0f4cbcd148619a35936c1d6c12f27040193f1f8291c14130318459657048535aa86d44ec6b13fdcd71df725fb999785745023738e81c81040c33d28ad96ab12198f4a58b7a09ce482b46fb31c1a1a42f5dd4b3cb794c7028e94b8c54520887923c7a64824372c69cc734e5cb00db3b4bdc7d204be93743b67e297fbee7f539062d17b1638cd1e3081c4622ef45e8d2eb7b4d6f886cf03bcf31ce25904b7341849a57dc30c10196dcd52e6070a094d3945fb5c2195a3d8e4d8ffa2f53a08378ee6f35deb06f392132e8f0d417aa3cf762fb95f30002f2d376bc2df2c340d848e0f160fff3927840501f04980705fdcb1480360ff7c070ce913cdc0779b0bdd70d9a72575f5c08d5d8b0a11f754001dd10466fa1b3ab3310150748b9ab2f1c1cbd1b235b57c1e4be41506e0a74100fa671429587be7f9a090fc3f57762fcc9752ee4f914bf39670b48ccef67f2d3760c203f0c0408eaef340c0ae2a9bfd39106f73d1beeb115d0dfbdf75e0b30bff81e0605e587e31009dd064a7088f8041ab6547e2f1bc090776eb0a449144b9a34bda009ca960ed62d870d66aad590ad53a9562f3d621d2f6f5dbe74e1c1972e61c04c499a4225694a134a48d15dca39a99c93d25ab11869ad1866ed8d11b3f6de2cd36e9669dae9843a213b4da192fb5149a8294de4fe18a5dc3d44a4879a4837216a21a62d59306de9d284e5084229e208429982048dc2c0a03050080c0a03894021179ad5ddfeb6e43ee1c2ee18dd25ec18dda59cd4298d0da9cb3929ad15b36e6d6c6869c5306befcdeccd324d3b9d50da0985dab6981899199f99890d67646652291a9a18316a62bad701f3aa5991eb707478e43a1172fb07fa6fb22bddd84093fb25d1c3f18a0cd0839b22548a19bc63ccea0d11e918ac4de374293dc7ee7c53b479ee626e8a3698bb2cdf1cb9297233837b7dff34726e66d8e03f29b1f7629c5c979356ac7ee9a438c4e4472e27ad94c9e5a4330eb99cf24b27b9f42f1edf52078bfa2ddd086df05f8c31c60c54613de2de24d9e0c7bf415237fa1e11bf39523dc618a33bd7a5629cd94d911b251b7c6f9ac5f8374ab697dd107958ee7e72eda24b130ec02c2951a7ab9b1a36cfefd56494bb97d4f444c84d13af157269ea4184a61cbc3861a7595b30d5a08b262622c069aa61861b116c01668b2f4baf1b18c0d00103278b2c4480ca5ef7aa546943276704a9855296b2e684b13da50ee1e55a83a50ee1e5aee65a2997c91a60c952d6408b2ca59452e22d687b3c3ab4488a21bc1ce3e374085acca085909c24657a740f824e157729b8c8eeee3a5596c0c24b4ee5f7c2428b0f9ecca2a3c325d371f728c5d29542cbc4a253a54a2c9e801307078b294a30284395090d148b2344261633c83045dc0b19228e57781962e50a1c58b942cb9037151764516e17b9e288a9c7927429fdb5d1fc5e57ac90ae9861055c76e0022e4cf38a1cae201282714697ee984b1522b0e293cb8b4b1291ee1f49f5a5705f2b914eba8acaefa54398ad89298b2ab9ab4c4b55b6600a113b28bd7628c10e4af27be580650727f9bd7640ca9d2abf1d88d8e187eaba362e603a2ae24386fff0056230971862a565d502a620987c7859987680aa27c35d6dd9fa5fed5775c32d56596696559539c58b4907261b96240c2e542630b595028b8895e37fe0c545e8f95861c1c1b9e28eb392426715054e145164472a53675680b1828b0cdfdbc612a61ed04617805825298a27f24acb9c7345c4d6bd252b8c96c8c060336263c156c486e345051b125b12db9c1309cffc543f1c1687357ff5822d061629d8943625b6233619af29db121b13db2a695b51d9a4c75e6dd97aa4a07ad6cb8a2c59cbef65c50c6a0bac7001b4c268b3424815ab0053450daab092df4bcbfb2a8e50791548a0c4f1e29373820aa44a85d18b0a1ea820826df922c3bf760b16f07e0c603623086dcb90eab425484b98fc5e5ab4d0c29465bc54282d466c5a8a608c96212d43724d7eaf2c5fb23065a9228bd28c0ddb7b65419a7992df2b4b92dca9b2084d1126bfd7145d7257a7e032851256e43e9cef58dd08596701195bbd90d3a3fe4a6e36f76208902702ade1ae66d9ce65ff785ea5ef1cf6373563560d4d91441344812639fed0d10bcb122f2c4f32086720a1162d1bfcca037ff2e03e197ab17ab29abbfb751c16ab34668b3754acba6d745349bfa8f8cdda9e8d8eb0d2a02395318b62e77d62c3c01128605aa20417352811c1050852885831448612c0309344c66c025fe00431980009cb175e54700549ca133834e9620a2caadc20abe2c59d32a852455665e94e2388440c0605bc828446af145d39ca947ec9248d6e25c98a1250407a040ad6a77404891f522004982f5486b8924417466a9001d2152e4e57807080c9c41519c85ca1626b2962a049a1824c8a2856cea0d5287d829825a0981bbd62442608315e7cabe22ead5031ad6031dd0a1659450b7dc1c9ca0e327d626665c58a17546955e5b53a92d56954cd286690e79ccfcaa1dc6ae25fa1a0d62d8a18243515095daa56511081a2494e8aa1a708f98703e224b97b142bc81e038fe3aca4f87c45d143144d62701f8eccd812d3d2178a615cb9167e5e08d2c27b3576e24fceb2fb95f3a1f118e71373c645ae95619b1efff8b23ffee19fbd582b36eba41ef08fffc33f7e0ef7e1d6b6d1f51b6dadd8838adddec27d6d43423a5f4f95727e7d12bbfa075b0aaa11c39573c1afb67ac095e1db07d2ef42ae0fa4df62f6a5d61333f6d162b0d2989da76cc9199356296596c98eb4a99c5c6ea84ddcead77a1a051b1ac584d45acc525b1f06ff3ec6ddae7f1fac96cec0be852b20367881d88fefa21f718bf3a1d182d8d526e00a9f7ee76a960a7be8f053cd720cd76651efb0afb8eb7e72566d465bf3e8b6a1aa396cc5365dbffc29a7575ab18a598949ffd7aabd575a562f269ae8d7963a47df739c81bdb52ee39d50dbe14f31faf4a75bd8778c8735351ec3c688f1d662e7fc6b8c94fda024a8642d9755390a11d1000000001315002020100c078462c1603c0f5345f01d14000b84a2426a4c1809a35110c3200882186410318418620820c81864688e58014c9d86eaea6f4f248053d1fb8245b89d33e15084cf3c1eeaadf05010b383704649e71b90c796dc92efb8254514a8c067455744888bdaa3bd37a322e28ea89f0b741e63ccbdcee349a8e73bde10fd1022cd1b89dcf7f0bce83b03948b8ccb4b547b09d4690003bef8f9e9239fd9f1d2cab1f5f5c3df402617f43d5bc25e5205eafa7b04b3f97de4b9e391345f7e33a862e97faa05e867e511d1e87b5bbcce204ed5a90bbda25c6f7331389af213bc8df10346d1e868ad29c0b325b52f3f6735a899da43454ffe63e4c2938cce1687a8e3e9abd6c3f82bfdd37863fa8878288768f136843beb7ee6e40bab7fb49dba4780e1b923fad5fa644d0fc3b758d33fbcc01b7295ff58731a993780b81ac2edd9c4574ac6de073124fbb1a63995655404245114019d8b07043f14d06dc6f9a5c6d8aad7a877cd9ece57d1d069303ecf0e9e9bcb2e588ecbfdd1cf1da727f9b77c16306adcf5be8c2293b1b13a5b17d390824a8423738c53039b8b19d70cd74394870194c7f28450c9b482f8c495b0d96e808f7bcf1557ad97fb4a8562b37fba535b003b6f7df7c2ff7aabfad6a9462e262047920863acee8cf438896ef091c62e636c09579bcef1861b1132f4b1f9c9dc6cda1f799326763dbdccfa7fc29d07819b16d863b09c1081bc8a4c167619144174de372e8690b2868dfd404ac47b914840aca81d4a17b52c4b4c2d68c5f9286ef5b6ab33ffe03cdae5750ef7491fadde5c93bd36c32d5b79312e95dd76dab25d32f38964e7168d652189dd0761dadec633807e0a0c098dffe766c89539f1a391a35efaa44c609a41875370d1eede322e4bb0e4f987009c0f37dfac18e5002828b9b69a3a6c70d2d4015a351006525266d01e51aed5da2e16b9d3fcfb6f4382dd865065c7e7a039750e0637f9f5756c86fe547b5d8062b03da9155a5630b7bd848a88c9b4f158f43b71e9262759a3bc8eb3d86262dd5ad36f41164526c29681c7089e2852fcc3dc00d2c367cace2ab8c3001b6e9f171db5ca1a06d348e0200d8c524639fa1d58f44e62c82f6abff51141a6d87e66d6d130577d1aa4ea5f10fcf2f5a4108b7319d67bea8bfd68d5b3ae7b48929408196a5ff2b07b00eeb4af9dd274274af82b4278a5601303a8b0de5d3a764ef19b6409f9e00a4dc250e00dd64dc0dc8f3a5634928c7ad12574617da6841bba005b3697c7045aef2fb00c75c87b3fa4fd62a30f2ab029814980ef03b8ddc39992ed1aa871195a9b94667b94acc21a4bef3e8935ca11daa8e0e8a1e64beaf50a366a065de58a66dc0369af786753819c53510258bd190d7e4dee368aa2c6b78e918c278d399797848e3e897838241022c836ff606b26029b659385027778945ac8f30dede62545cbba1e3456a9f7052dff605c48fa963a9feeb3761da1afdef8765313212ee582ab4559f0bf33882b71f929c13c09bb80c4fc07ebab1df3981648195a2399e5e7c7b436936ce5a65e0b634eca332990347445b3d7f3a63508f3c4b1797a809e09654c95976b0437fb590faf4beb477748c20bd83905ab5f7b8d488653f73a47e1705db5e25e695d740e77641a78ebfec83c5b0b283483d4a4453eb4410c7b869ebfa5fb5d696157fa00ffab0ce3be3e063a10c599bf2861605baba4dea8b300b1c776aa51df322cc8252bca94bb759e2f92df918959126899613e1ed1889f7d5c548ffedcd265ed89553cdd3f1fbc42c4521cf52eaf3f60e26b9c730e4c5cf445cd44b4a8e72e16165e59b2122a4256b1aff1455fa19fc234f7a65ef1bcadcd3a1d7e5f0e7803a163eb2efab94ac93905a4029f8046f803fca279b298effe38a1069066b1704b6a4d02eb9bad84b3bb7efb521c6e1a5c1331ea898ba17583a4e6e85806bfd80cbe6dd1dae14663eff3c61eb80c506b0bf5c3a1cee970069ea8010e6cd0b4fc0662cc831b82b736b41ec314a33e12fd6221685a0f85bbf4405232a0905e1ef4d74e51b73c146ae30c6db08a1eae013a56683deacef2b85cda74155aa7a1e91b7cf69e1098be7071ecb2aa858263a5125e97c763a546945e547f147cdedc6c893be4737f3dfda5c45d1b05fc53cefcd26682cf547e5baa6bfb27f07e98ed40c754ee121ca555b12260c26c0515f25964865bcf0c60f32c0ec8c7609ce6658edb0d3064fb2c70a6b7824eddaa8f2bec45c121c95fc4ca6c818485f97024c4832d9a39904c63bda2a87ec4c20889296475ce113ce5a59cedbbcb9b1e8ae7644f8eab3c35091e1b58cc16de2312a5f8da0f697661dc41dd250e8c299997a8512790bb80b966f9aa7da81a9c8dbebaf3c127f127d5afc39d53f6f8ac084c7889971424c0ed68bb46d55b7b12c71e27306e30f17a036dd1cc197dfbb726800963e225861b3fb528008db98e26cbc42b0bd471abf88e0273734da189b79ca33d8a4ff4bdd020503c1c2576fe54c260a694f2488d255fcf2e98e49481ae17f9fab6c83327c1e83898e50ba7a54994dc1bcdccf457b01a24a94f7ddfba20b0e64274774f528d1d206b234576d1f0116c179e8c5bb542bf2e61d2999f8fd2d72f65e87e946a5ede3a553859cee6253f3f8620438a88e9e03e2fb380c06741a0effffa3d0a5bfef10558e0d48dc31b08f85dde63a53fd726e74dc54aa8b334db16c51741a9d2a7db4cfb27c5b63f0411f23dd3e6be961ad5e90c2a6706f8944690507a76497f0fc9c602f4d50458c5c68f86f0e5e5b4603e3059362f5998a051ead92030b88519755181ad37cb7def22825764eaadbe9bbefa23b80b7e1a08ece5034839e11ee03c88dd8c89d167156d10ccf741638cef71daa4656d51269418a68ef1c837f205da4aa4bdeb2b646b78a93e1083a376e8e44264a211a896337277431593c496769ad1bf09940f0e1b14fac64b82ce28f85a4d8a7219881ad0c01079e8028f40be4a24ab280914fe7c812ed98de4063328d10859a3cca2dd82997c6551864f8e2c095852fde6205988d620ec238a61d3d681ca095ecb3a2bc580768a8c62cdf1321458c419f71f4de1ed67416f68347218bd2c93e9d01ea74b36491ac67ac4cf7987feb2114d735121a2978889369386062acea490453893901f29a1c4b8be1b46c383a527f07b5410ddba83fa200a7c512a0602253212fa4eeaa98b61501585d440eb0aabc56b0003df10209818f422debc09bba0909f18c654101cb6c32a21971b225583e2d1a81e4d54d91bde51af5fd7561db14fde5c5d47e27592d24b26aa886d88e17745b62d6c1e9162bb59920c29accca6048b55e4ae21b2bddc988cd8f466d33c4941891cd25904691ed13a12c1064a01bfdd94e1fe987e7995c94d942158ff384e358a6e4f75affc65c034f499e7f7785f86c701b31c77b9a6bb969563b8def6dd2dc1eaea049969438e2e50360492504442a0a866ab2df5df079aca7621f2280ee1947f8f53d627fa902302c7f09c99759d305649c56b97b1fe7d4451bdd687d1c640a451c6e4ac45d66427ac3d814f88a620f181fc46d2320bf75a3bb01c122d8fb86f6273f28539215614d3291ae7d4606eb16f75ac195b18ed6d03313e93f12f4c30b59d6209e7ee2bdae43509a84c5a49a08909abb27bb04f815e05da9d11a00113b0fab4619c0c1ae59af34e9f38ad52d6df7957cb88e4610de616415da3d359639665134e7cc118489709cfc08c6224ac7b2a50a29fee533c46f5ad47250a628f1d118b48f0e101d390098c94be5533137e2e28eaed3aaa2e23aee1ae1029cdbe0b909e07d2ca7bb8095d7688a155b2b18911f0d07eb7f0af35b94a9fe560461ef4ec1e56bcbdb85ce1e78322d70932e804c76cd0752e81c202ecc1fc7f1b2b3a9c634c1d481d38105e6c24f1a68ee3ce5e06e7d53ab65d0d2ff3f6b47fab9a48b4813f4ac82852557927afc2e2d722f648530245de931fabf1c25f24709f3e61a6938f58bed9990809f509c35f7895b53edbc054167c4657f441420d822d1eb68d54f5f9593559acade2d0c7551dfef859a077f8513ed08f2e3b0d806471e99f76e7985bacd28e6aeca128e3e21abff06674ea18d0b54a4a0aa873a2f6e835c5a6d51162e486a5ea1ec955fe959600418e55db02e6857da65fb9fa46cfe84cd1760f3fd5772fbadf1c4826f247c4f73e8d09772f51ba7a276be4451718ca4221d071971e73150ddeb435e7a2c04e3420297157573e56917d0ff8c8b0cf797c809ef7ff1e94c6a1769103e3e8218075c22683a726fc863f387c142ee8ac7015632c1d15da684d86a71edefa6e09afa769f2e24080ddcce57e68dcd1e264baf98b2802032bb52d3e60980a0fbc7c0463eef5508ff82e8c49bf4b5cc82df5f5f28d01d4240c97002e1aa2774c1287c1ae9a89a18cf4895cbf8a214797dda49c0ec46db4d598c713b70ec40fa1ef17be65f068103b164e02176ca15d2d13f9d0d9ea836e32b529c5231ee6c0962b9799025ff8629494312b2cdc105909d005f32022fa9fb521123821f78daba9953930151ee1153bb93546012d21825a8eb103ee6a8683579827346cb73107de74514a8c11383c2b33a085b7918ac849aecb984cfafbf942daf00ed5e2a71a2f26ca61472b52788822bc914ebfbafe0f412d0ec2d6a3fff342c8bb75d01e21ef3cc00cb371f00db33307566f73980063afa0bb5098a1ad606c4bec64537a64cdb345d502b4bcdda54640daa6f636350e47137870caf0a56459a9fbf83e86203e9f6eb104cd81db3e869e61ae12da21684dc4044021f6726aef315c7ab7971e2ecadb3b12a762d60c83a8acf4f4faf284222e90823a8ad42c43a0531312b18c119850fc2792b4847fef39dd89a1277a5a8f1942712afdd76efe5b713ba3768b1415bc2c4a3d68abd1d4367328b1cc0e70646366c7fd5888d8014e8e028d45f0f8589abf0057c3aa038e622cdcb65f02f2f56e87660f916bc376dbeaa2b3e60d5c1bd15613590b102580cd5ce125b7c5c9274bb06db9286f236866e4f1af96461c734312a5d5b95d45301d49145339b7b5c966a1220638198e171b207fa96c67401f8742f41d80bab71511a5c0005b18f2e5380428c135e0e639669af965bbab88dc241de4b666d8360e26ad16f4bc1f12ebb80dd35779fb172c65079aec062a84426bba7039f0899502ab5604b0a880a2f8417852a1462026631127d2e6371e3dc193261707d7ea5756e36236b13dc2e996f65001135a7bec03a1caf17b4e6bbcb156b5f4a1a61351977566b5968a8d235b5f0a24804d1e39a1a3856428db49b284d5481f056dde776ac820a85c3a7ec194cf7348f66d23e6170741dac42813fb337a4d1717dfdeeef8676965c37aa90d4b443c600c2d4d79f101445fb6d772209eb0cecaedef3287cd697d931141454b3bbe9385046c43cc8a8024556327b9ab2be2d7424125e73359ab19bec2df3dcf853f48e0cbd97caaebe99770fcebf9b92687efdc6f067dbf04a03e404820212665422e3bd01044a38666473864b5becd47f6289b4c2b1e8e3e12e79bdb19cb65b78e46cfbb69bca145cf9dc0200f8261d61664255945957fa5a5fbc0497bf025326e0ed3af8913946eae0a7bf5e5a8ef102b76a1d9b341474c43f880317c524abb311768cc03e592beda8a20c147309929e8dfd505af08ca3aa31cb87cb40e19dc7812a07964e0f40be6abbd2284aef8ded74739aae927f89444a3419b812276c850d72fcb04c81828025a33050ab5d9be77dcaf86257aa614a09e46a1a617d2ae407fcb9450e2b565ccd8cccf46b7c4aefd6bb932f371616c856747583e4ca4c612f8bd69ac40fed2cf060b2e92730deb334bad6a7cff42d3bcf5b4b70897624450a5ce26fa57ccd5324a9bda298eb81d8672d6ff453aaddce0fa9c2e2de789cd4d55ae5dc253b7df7b6df3b6d0b95ea00afc15e29a6df6f47dc0ad1a7ce0ffa6f11523e936e8df8db0ec865da2a33a5679d9b131eb40e06fbff4b3bfb68f6d396ee06ec5d016370b1b132eefe228f45ad409a97f13839adebb2c86a68d8d476ccf960e17936369aab90c65be3612dc8ceea6a0cdfa211176ce0fade04665aebed2385b0e28f7b7cc4f0dfe92475f450275de64283b7b9bcdcc4f7626f878ac8b0d034461f3d81c42f6f1706d54cdef23d0a883e8d9b90628b3dabf35cf44129518d524dcd012bcada4f623da293894e5cbc97e9bf59f59271abdca33ad7430359b1432b067e6ca437419c55eb186025916df6ed5506422d0d29d6710e62310ecc139c1e71a56c077284767ccfc8c440295a8233c25ade643d7d41da29fdb7e177e9553d735a7b8b9cf8759c2c970e1afd41f9a9cca7b0849d5f2732262f56eaba244b8c1c6371326567f6b2e32d8602f1f7046f2591413aa94f839ebdefbab6157402fe53ddca2a3ce4bad437e9035300f49abce81c332c241ea90e12b7974405a1d8de5f1b352574afabf21a187093524327c9870523ac68b0d4ab45683c3fd224bd7d2fc8fe4e02dbb300cf01c384bd47371d6d242600b74788046ee3bf1eaf24b159a13887bb1360385fe831e51d5984e0ecc7ebb78e1444abddb7a0bfacbd7b4f22d704dd35afa23d000d6474e8dfdba9605a45d70af04610b42d8ed9fe75608c37448489e8460a4d05d6d2590947cfe7cf0257ae41277f2d245307cd083eaf9b90f9f8c95b992b6907892504a1e57e0a538edf4e9e5a878e284405fe77328e4ae64c1ca7612a9be632ccaf1f9c5233f7cb04db47b7e041173a2efca3dd3f11dfea45eaf2f79c2cf08457f73544f85482511caceb15339c0adfac2c987134909b93385891c4fbbed82eff6ac214bab1967a58e9b965be2918179b0c396e565ee839015eeed1e43cb91939f4185ce06c80e4033405708d41237393ef36fd278f9599950e866192c2ef9f18aabd1aee582ceb3acc68e609c02100eff8b0515fe22eb86270dbad1cce444cde381c22f2c047ee62ee5dbe72e047af55c25938588017432387ea861184bb5814e6f458df29fd402c419ccab9f01d40808525ce8bdad0589270fcc85524951667e04bbd7c5e786596c8e362b1eb106e3068431996507429cfdcb145170fa7da0c4135a0d207c9fee087d897f785455d21424d3e88938e8934201344b2d806b05fe2b4f9b8c837a3874b681bd5f093419fcfbda4eae1729748a96fd60a94864f1322edf9c9ef9c976298da80b1a78fa6108a921864f250e461666d62b49f50a0d56cdc5f01087339530a2f0f582a0b4424b405502b0684e2c15eea47eefd32eed4ed8f0fb01a11cd8fb5d1121338f38e405caab46a145cd2d71733782d4a4369dd1f87dc1d3d1a5fd063ee084867943c4f3de28a779618545fb3b32dc5b9a1b2babad2485079a88ca9098076b59fce45708e7acdebcb452d32566a79b08de27eeaaa64102547c22203384a3a9dd3f452b58f08952194438e2b10331257d7a792d0cf690da619e2f8750d0fce846c10562163d721779c1520daf3484a47d22e693016489f20175c05f73c98d7fca7ed9b144323c092023e1673796aec2925330a149dd976d5340ab79be071888cfc76a7a98cdfc7d907c861e7152753f989310c1c4e36245d63116576f12d59dc678cc08a11a6721c38094617fed583f275711b95769ace45382a867b36e65d083958f3d5863f5a693d33fa92ffd073d85224e44e53c61062e979a0f728a3b63c41b4701a7297838238004569fd7604cd231ebd42b4da53a56aedb7283d3ce53b5eac8e329118552135ab2facb8dd46dbff38b45d922a5125710a6b1263f5c0be589cdefe7e275a5b1eb951d889bff6b28c87f80d39014efd708c027a3d67e00352b844b567155df6c6f7a026b801a52d5a00bf47501d11d8d8b6438af570e97394b7318b45479d5aa1305969517e054ed78b78864744fbeaa3db1ed412ae855e4a42884dbe1d722109c3fa918e02a96d84a4e8e0bcbee40f1e8fe7260ca1237a0f19e074028d86e934b8b7c66474d1739c5c089a07153772273c6a48f2b4156cde934b6daf1f6b2c3f927683c29819182411661dbae31371c6bffe70a8883a8408e859d32a4b15e5eadd69d98364c2f3c07b55017187c51e37927d2e06cdec75597df5df883d585b8911567e3af4865682694478fcc9cce9ff35a5594a1f81e6d0c211231ec10d0e9ebf3823359ed7b0f68915df9a8e691077ec1db3ed92fba761e1fccc6fd68a0dde8bb0f8749876339232626a5531a9fa67d797adc986027fc8d9cfe3ac238330fdd5a87cf16d5eadd99346d0012aaa7a90c38dd08eada1803a343eefa95e6f3123ec01d52c1eea88e6bbb0bd1fae3d3bb17f5b3cb035267c81d4b226326bba37819ea47a06856f29d16415451d1b0034b92aa8c61810aa0d20e1502321244f524a323d960da65662fed51f51188e7cfc0d4151aa1807be02ae81e802e575bc03012b2fc77e8f720ffe144ff6d4161d42ae4a23199ea9c9f80426c627d58ee1e9bc2f1eaf8af4a9d1812e0ba8cd3e90d683c89f21c517d85c9a9d8f766dfdf10b6016e495b765ac799f20e728088216fb089d965175e541d84c874415af66c52646bee439859e36a91957fd4cfc767ef18e3e6a820a80703c453ce2c7f0d14052bf1ace016a2aea63e9ac9b2b0b57dfb40ca78ae4037de35db734d718afa21a46bed07544c50ab2b79dfa98a734f0dee049ee6fad1acbdcc2fdbd42fa68ca2d89139aaedb7a464accd7bc2ba1afc857883fcc01c6a6f321e611ad8eb281fdd2717bb728187effc418c050d621f593ce2a436a7f1e764f9b51e34279486b672990b4f2b14f8c658e88edfecce2e43a727ebef949bb9cc5efb58d83fb95a0a0b2b1503fe4a1c8f461a951f872f650b710135aa04f9925179ce308f55a8e53474d6aae24049e9282422d6ae0a67fb5d840b625b9f502cbbc9b8fd822e22a97da35fd28bb6303f47433f27036feeab497636d91e83e24623258cbe035ac0ff50fc1eb717e3d162e52c0b8c7b754212faad24c60c580e6d0d211911619ab78bef9a916400a12239a1a018cda99120afa18e84b84c3448254e854a76f8e44509f09cb23066c16ad1c5aeaa6ef8cc7f2e598169f2956b4f37cea54e195e152ce7afeb3ac001b69bac20cc72f1efc69f919753fee9da24e764c848396e5b58e548f1c9969b948711b45d654d41c418653a82465384b183b8ba9e53bd737ab71c0aebd58ef16b09758b1d6bfdfcd3b6ce4e2be9b614705a268d13bf19897c3d624e4cbfb304b1c45f24f1112ed0cc1b14c83999f697bc68dc8555472f26061b3b6cfba3d64e4db2c7f86051db15abbf93915f52a65ff457c2cc67c09a2edb39e9089a574eeba60db1c5534b5442bd402130a6e458c34dfcb9ad44b2dbc29459c4e3168ccf27f1316bc54d230f3efe10ffd4f3a4156d6fc10502cc769d02c0e423367d4d93026cef06b9121548025b7037bab94e9a0e36caf297f71ef7e3aa140362a55ca3b4851744b5ec69c1aae0baa0356e271e1e92975020c5b1f0f526cee6b83c46b155be9a84d666f0efafd5e65ec684e4e2f76b1a3e1a6e3032ce349d5c725102192922298f26d798b5a31d2dde6ff0799d5f0b8f1755514ec284f584b24af99803f15595213760dbe9691470ae3015de01ba383bbde01af4b564b27d5e92bb61888bacb689fcce100c40b3b40e062f5d0db64adc37f31025cd5bce903676bf6e3e3664bf034bf2b543db515637bec7894d4fc0324f07f4c5c137f4719bb61b02b98fdd25f00e2ca124a93f433afc9384893baeece8d1a2b6ba069e097234df4647f04c4da5f63a8c96004841fb520ef1cbf4c47bcdcb6afaff993b4dfb24eb84d240c841262a905f0bb0d14d9f9a3c52217dcb4b854ce044eeaa2ec0904b91b21558310557bb0f70b022400b9d3653141ad4377b29d695f11da8c4377fdd11cd573ec35ecaa4a3157a73d8bfd510123c23e87e82417d777402ddb350a00d6a0966ac254903aa7e3dd3d09ce484ae61f8ac7fef633752120741d248dbed6e4b84e107e2749691a972faceb4ed13a9c9821198431b7d03394cbdb1bc0721ec76cd6a2d8a65694a375c49eeaabcdf98a36d333bf463d24c6c96ceaab0b1ea9cdd083579c1ce1c35512b7ed5744c06bf40d3e74b8aa2e2e4db9f21c70875a93bdea571dace915efa7ca78afd930cdafb0664b079a5cf28345cb47d8bdca2bd09014c15a5650ad2a36ddf99a8182e254b1b1d4fea7f93250fd19b20a88c705eb49a56346c7da8bcd1fae4c6b92633e64bdea6eac4023223b623e7d3a15a9df3a36124b286932e09cb0146d4ccfe908449c81ab5e34209966abd1d0040399051014e49bb655a8be37425ec050b81ff2b982568aa4f36bca25420b64439dab7797622f464f6672a5063751cf17b218f32ef62bf9ae309e428a7227d43a160b75fd2659c846e221447da9594d4af01bfa15f4be357bc009ad9e23c585a9fa7186d8c6be0d254b0e9525f8b05d847b12cfe7af27b7f0a9689109c6393041886ee2108a16876cd7e638dcdbb39daefc3525d10145aeb8694a57c0d7bf5f98fd59d059ca110db63dd2ff650a4ea2d226a4940d7d137effd55a35f5a4bd021b90a47caeb64b0257229802d8f10be0ca5d9a0553c15ecb5c0007c6a5776942deeb3e379bd343cc28503a34aa664aa94a30dd6cf813434cc5f640c698a43f7c4fe89b6bd3e016636d60c5f34cce4d4ebeafbe5cba472c3868e1c468eed995938d3b297bd173bc449b4efc770d997bfaf3a1b81e57915f4594ffa2b84eb4efea93aad54965b5048cf3e4bb9fc28c930f3e4e454f59bec6335c002d69797f111f874cfca8aad4a2a8c81445a40e3d234591001a2d34b9f2d8004c6dda368532b73aa91411238b584fb46ef3c16a65e27d684a2caef932e6aaaa8b64bf9c26d3b366856fcec20d9793a8e8a651337bd228074f448ab246bea463b67e18ff2a1235c0bed14180d30938c1047d3d0b4cdf1064b1c188a081a3ba2f3313ea83214169b64662de94fec2e8510f41d597371263ce93d7ca963bc5de520c0df127cb818c127f208b452f7c92de57a9d2e950803a309c99a0679d37d20c9d26d2f6bc65632ed27c5f0c64269c0104b91ef4b1432c927d894a5891e34e98c544afa4fb24ce951bad20c337a133ccc3af8669806beec057f49bbdb01dc088f6efbd9f48d1c75cdc5c8d684f0f7cac3f0ae3e4642286fd34d22b28b602a8ef90d172f9bb1f1814fb172b6701664b4e28cc6894877bc406d0ce786a704cd21bb9a23ea06968c405f075abbc0ca7f8cfe1da7e0c536dd55e5019fe2eca6f3a056d6446406753ccc9235e02cc4392754bb5936fe436c401fbda039d4e824de9e7abb73b6a9e1704557418c10c67812e5168f17a6ba59093de4da633de83f6c0f7bbbc80661489f05756c9028559a7acc0d03340894fa8586a9027b8aeddc99a70f2fbd64a16c7826c5902e0d9c4855ab72f22f6f6703d0bfc81388aafdd86890947d89b80fcc1d74773340ae18188845d9cc7e6936484986d989ed0a2817565280c7714ec17b67786ce1864e67baf8a47af30c84680f1f08c3860280d581385b8f931546ad7dfcfece08ad65d7e304ec162cfd1e41238a2c952fa92ee01a3fa5df724999c7404dbf26c604ed4c4114494badeed0bb590ec0460a12ef5c120093fae21a45f935aaee0672a3d35c5194f70247d90a9422214ea33e7b3bc921dbe860157dc39bedd19695db8fe129026dc21b27f2c07bf64e7a54e9682a8704f9c2963a5cf9825c542c685d3b0a4845118aa098d481166c19a19021b249c3e9fa553c7ae1843d49be208062b34171e1543c17c01c4a719b38eedfa74f7bf9d6eab5d58afba9984fa874ed7429599aae00f64be14bb5bdc9f4ab10c0f5828af649a9d29d00a22970ef4ed6b61a4739ba4f9ef5d01446115b067f2f8c0e42f1a301153842a5c8c51b2010e0ee99af43cbfc8ef086c6c406ce689c37e5a04fd260b316aa147006a9b0dfed22bfd61ea0149c8c51022872951245403649262680cf9ef0ef388e1d5fb9d8c3d8760967e881c03c8f20cb616da0ccc9db642502021ac8200f8ed0f4167e4d1147471cea15ecc85a8bb63abae94985a9b9cde8fda4a7cacfa373403d559f47045920f55bd341d3ab2b4858fe8ba8347afe10085a35de2a37bc21171938f59555254cccf1a52ff2b15040dca6cb8b924046f9a7b6ce015aa37295891ada34812e34265ed209a6631a5926ebfe03242249c5bcd2187e0a43a4e878510324add211582a1262cdec20b7ec47fd27e91892212f912f070884fc4ed4060dbe5c2a9caf3d93ea97bd10d5d986d0c8bc3ad8d2a1350858d79c1482c8949a9f9cebd8311381ff587b6d12dae89ddec17a7173e4a5cd3479280e9fc90072f23c78db2637207d0a17d1ad428e08d43603428b88004699c5741c120936981d7194dc44ab940a49cb9719457687bf123d4f127cec756d7c8184fe0834629cd7d7c646ae38d75d178ee7e854961ef3e39d3453bc00de454c6334fef88259f31e51903fb75430d0d7a8c1610dc9c239dc6d80e7f731b333c17f9104accec3185e845ec3acdc399cffc714de593b9a38010bd4ece5703b4077695258234f67beffd917ae9b3c0a2ebc5d59d6fda2cfb18a6979b8b5dd4407e2f2f52a784375d76d01ad86909bd46fdef81931746b215c6f5f0423c3c4c4c5a66a0186a28ab3ed4a25d89aff0598578d9dfb9fac4b2d2f38bd49b08302b39a651d9f73a6068126dc22ebbbd1f3544e62e78cd4549254ebd0941c76c38e26070ad090b96c2e9edbff3993ad30050829aef8cb05b3f4c89d1b50fa0e7884ea661882b423239a94f121cda7d25ea2073441fed0edb9be9761b185e973c7c95b034298b20299b4583b36fbc4d02c967ac7bbfdad1c7776eddae754b1574b08183b952ac95e078bf079ec30b5ba754e848bb5fb0f49f53253d57380454c4c323129c4adbe2bf8b388298ecc0b2485935b1d8584258e45291415437d91417265ccce5e3579d083d3de27c9ec657e515829092189856c191203b2217598b4e1880f0579121ca8b4e1bec74ba8cc8b65ed8a7534b34d557aeb66ccc1532a28d9b3039d98cf116a6507ded46851a5ca5c7478b1f7766c141319dcc463acde57601956d176c10d2b88e12457dbba54e09e4be40df9d5a2048da666b70fe80502627a67f5a177ac8436be9c72571e0d22300041d7a4acee6547bebfd8a81b4c69b26fd4a660d4b83eaa6040390e25af09f4c283a9c8b46f1da063e6597f1cf3814f68ca1444d446960e7fb136adf1e8e36193d545c0d6870e5ea7425c84928fa6e3373ed59ebab594de6624c88e6815a8af774786de72ab04b8aff19538fb437be742751503b2089fc19a814f87565fce0ea89b4465d609adda4972871feda51f088f8d2793f3f520fbffe8b332f8f021a022b18108ab92b48a1258c7630520e6a3e9b17d60a4eb62c0b4fdcd235a7360f1e20ccdb8340e9b7d4602802639d9ef1acd930aa7a727cbac2c76632a65449b144f9da6259525d5367643c1bffa1aacdab684fa16494c9886036434ccbbfcc6fb933a7deeb0ebd24173b9b2bc2dc1d2a0805bac5aa6a0769fc70c03e66d90e3a60cbe66e51e32963d1a0bf2fe272e63d677855bddf681cea6aa4d2be03c88db26a78e707da5163b1ee0e2ab1fca5459dcc301d0873188f3163d15933a5a4c98aff0b174c7971187ed6559e56ae018c85e50e4ea960c5138bde668968e82a28e2aba778d63496642e46ce1009014c90a27328594a8474d728cd779d7819f53e44e86e8b0228c96fed5010d119789d7c0c58473636e61fb5cc3b3671e49d6dbd86e5eef33e48a521c4fa71cb08b9a86865a793edfd23b1124a6b29a23d151db10b912c0a668b76ae5c6ba6278034c37d11e404029e999929300a9a6339aff24740de8b74166d42eb4ac00b12f1c247ce3d40118a90f3424aba4a39f280a7e7e7b03d01e2e3f4022099a8b2ab90cfb5c823df5c1613df373f3ee529a666a8c185ecf61b8b7c78120aea60a5cc4e923dd89a6304e6c58dd82f5c4b6505940256bb5430406f93a4d54b668341f2ce46feb6ea33675bdb30413daca53c43397064e41b7e967d2bcf56afd892e20a387063e2e86cc2989a009a923d347a85ab2a72235d26c8fe54f8fcc9771f189da1833b73b6d384e56d6cac201f293c4ecf27b7f170bd3bbf5192891955baa51549df4f3cb5a078b3d33ea38094311a7982cb3caebf8e90a77d3259c8e2c52811b40fedceafe5cb5f8546ace2b78d84d215345ce9c96f0f0b326939be42fe366463566b294cd95633f1a24ac5ef2f7a774a03424f74c7ff41937080a9ca1902cb73afeab0e318cc4d755d38fe206a2772caed2b9aaf45765a196af3c8de13313587619adcbd8307a2fa323ac39fe72bb1e458b2d989a24eb156582a27cf95a34686a4114adc9d80cfb0a8b45cd7b19ac2935200b85901119d38b927368ccc00bb7e249cd0aa6382f3e9f4f95578b0cc46b1145be48a1a912dc48beb1196cb64e71f5519cc56d6c79c6212a9bb4c7bc2ec586e97681a1c9711aee96ef7c63b13f08469546f7d7edb6dd2cb5fc6ca153bf5fa467dcc1412abd4adbe5ab43733bcb3ab0acee31d9405c2dc2783ad6798eac6ff53c7c7b3d050be1e66ef3d0b6f1293cb74aa0f7a3ff255188f5913541c5c4beda707f2181994445aef540e50f5cd193b3b8a75188651647c0429b3849d937f42d8b8dc9990f2b61492ac036d23fad3d08be072bb6a5fe2193acf6cfb1efef8b897c81c6ab9b9ea713526ec98e9268c947077c0cc7d43c9fe7c9279e33c3f615fdc1bf6474134909a407f9fedc9a1475203b9a17bcda71d7ad94102901a92b12214d13ed3df2c9efb9546dc5f9d8e90c80494fdaaa43efd76c6e0fa6a9fe135f79ef62a4b429cbbd599f44e26523e699c1506ca3ab66122807f889afc60c7cc58045b2368df02e0f981b5bf17e580c28a77e80724fc500b24738a356e7b04d5a384f7fb24616a4fd61d4b0b51b815183ae82d228d350a0125eeacdf8cd30225eff8dae80ea1dbc624f45879a905258caae84a5980e6ec9a01e0cf940a6b23816e810ab7bb3be2e5811d8fe7f70e1359666953560697888300bd1e53a0ff360486733c9ed7e0f838399533277f9a0ddaa13a22042240d5ea71d4932344fc1ede82b6c0cf0d4bc2166f10a3a425b6df20d32616312fa3ffc437b9516157c49bd06d79fb8654f5b1667dcad3485154def4676fa3f38b464a327b4da7f48775bdb52bb9dcf7547965fc725392b09e0a3ff62f675a6b63c9767f0d039e4ca0c598c1efe53915f16c1444f2cab686117481d99f034ddcf367303bd98d1a957343904eb5b3659462f837e3c1030e0984d6a39138df5545f12288a5f91c9f1acf19ec72c14aeae8f13b689e6e180ab5eef8ee7c98fd5a24ed424ed19745c53349d3c877582c851a5575856d8c79b4077ce3e2be11c4a5281d57197f2b0a52ef87f64657253bbc366c0244862dde55ce4d751a05f5bc22553e9dc47776d453304bcc77d6c84bd229277e3dd1afa4244bcaa13c2583c83761781d9f9bc433d733465d7e5af99cc6bacfdd51acc6d5a8e8e1d8e7c3c77f1708ed9419ce50401537163bd678bdea89fb6caa953bda99bd784948a8f1497900674593f35c48a4b38634f6f210eb33af7895cff75b77b0024279cca825c69a237c96e99c431b52eac99732ebbdd110af30f3515d373e479489a0b043692f07a27a6e41f9a82e38fb04c99b13018ca4bdb0395c40d87235fc0fbf966c1ff74efd3d01c22daa816eac6872822156588bd36cc9914cb472cc4c4394004edc2383b696e2f6c8106426a30cc3ae46f45693708d35e9bb9879afd692aae70e0b08fcb954da3debf74cc392027a5593aecbd93c55ad9b73c4e5540eb47bece2d4fc4ade9086c07a90cd1e8914b011ac6fe7fad1cf6db74f1567a93d83c339484aa129ee7188f733f51a595f489928b26dcdf91cb8c149377c01880e7cb9300785e48a91f798151b21b5429a2f47af3a434af79b59445b9ae0d641c0decb5c634e1bd0495c59912e45d9e7e91c592f5d6b4badae8a244522076fc151a9e2b125d64ec6f52304ac69db4c6cee45fb453ca9066ab5e3bf87207881999a224eda933fd3feb2577ac03eed8ce2c9dd083ea3138129c62ef2add2d33787450973b62811c1f153346207f213d19fce53f02a3cd7788d495183983c9745cac3ea33f5e84f6fc3dd3ea9e23d088965ededd10b6a7079449e378ccfb1c648076c71bcd17e6d7282a99d5a1b4dfb721e20aa1f455a427e296cacf73b3f132148f3ac0437373c7252eee5d4301ea9a4a76043894d9840ee93e493ce207c0e1ed69e19fc9e3b4139f58342b7a9c209393d67a690f46db948754f51b180f8e5a61459e36251774eab6bb7c252b0c25d57e0add84aebc4288f42a40e9b83fd563dca4605879ea841095e7dc5eae72841b693a8785458097d210487d3c8e52467a0e5350ba8e59ace5582925e99e3bd37ffef517e8918a77605fab639fde6812a1608a1a6a3e7855a459ee512dd216879828800fd190a918918357b8f74927c6c76074d886a30261b8872b476305d9f8c284e29670905036aa7ae08f25cb3b1b842cc5ab449fdab083084ab0bf344b53869caee0b0b608470aeb34322b97ffdaed5bc80439eee29bd3786938f079d2b56658e50b72215621462135861cdcffb62ffcca141dd9d9cc708aef81d64ddcaedd516349fff422df640590a15834678982b98fda6e4cb0bc4a4bd2522cd770bc1a042f9d8b66ef02c9f94f9fe3bb8f62aeafc1197b2813b694a942c127663051e41488918abc180add07728c835317db35c3d423fddcc39ccaaf648ddf350a4c5dad0b5d49be481ccbc3deb61c8048144dcd487a12c6b7c5e1239f4e180e9ed850c297628d04c07a5f4680351e21bb7e66351b985d2b6ebe36d65bf9093214441f92a0a029af5d41b506ee25958385e7039b2e46d515a9712ab42c78efd646316d3e5848c2b1c95b02ccb1bc7b09fd80965da5d88021caa496eb16a280ff85753a3407b7ca0dfe7e960332d6688b8b559692c1ccc5d04cb0dc32bbef8c692a1afd678ac15be540e2f2a1cb7c27bc8ae0962ddd84cd8ff32f1b0a5115012e03f69a21ffe04042d79a9d11430451e6a9cefa951f11f8660b81a624c59431bb9cc0775da6602ef9ab7e9cc0b56d5bf60841f3796d3e54fd7a0042d8c467e8a9cd58096434abf21972480a8ce56873dc22e211be59bc7760428510c14c75190331e1ce329131177a854b86ecfda04a1783b2b7d031d0ea5b1266871d37248dc967ae7c00deb0943c88f86ca5deaba619d8265ae3b444af1fa836b39b8eab2e600af4e0602c638ab18b9e9cd9111831a709e19e0947942cd3f44945c8913c6f31817badbeefe39ac8ba1770078833b309a505d35d44f6d9de70beb71e30960cabc11ea6d38eae1f99ec37d40fc44f4bf2a11e46bb1a8f10bf596e53bbbc9f068656bb22d4b29c323e99a57dcc0eb9d6f4db2494ea8f6fb44ffd2c292317f9c402d73cd48274b2d987417d22523ef4d02765f3a960b331def1bfde272414d0f27952b1456446e0ed3f9fdae4da13939d5c00a5fbc6b11f15737b53761b809635a24dd69e8118b1fc9e0a0afcca26def286848448a2c87b65839ae7f76e12eeb8103bd7db7c09d9557acffc315fe04b114b13994fceed3d3618741f15ff5f184ca679d8c65282393008fc6e2cb1e993572c46b0c72cb2bb4222ed6571560f127ed5fbfb8ee16f8fae5733457524cc6aa253511b95be6f3ae58d19916088bc748c2eb0d60471d88d79fa6a0d5377231e54d1ae9b792e57f7bda4b5b556189cd49a30294c9ddc807ef478dd68bb5e138a4a15df0b285c217572738af98bade6fca71f597265e9d3e0a30d804a635d34bfeaf52789945b7da395a0241b729d97d3a6feef7560f249b4eb3ba9823e8716035cd1a59249290731a8942d82269e4dc209efa5569620d96c0d46891a419360222cb545260bfd611943101d2d98d11d0dd8a82f63fdbae6f22e456783af780b50ce5e160513e4eee907ee08549c25559b2da4784668c3e48e0f37cc506b8c33fb64818c78fdc9066840111bf0d64667ee2cb61d6636720e71be1a03807d1e7f101eb0aba600d27a1355fb1b7c4058fa0cc6e8bae1cf41f95f430846f769debc9e8bac076a6acad1bb1ce81897d8fedde832391b08f9a6fddb09e6301240afc32eafa79c8bce1a6363e37e53868041571c6bed1880f483f52468532205b0f8085ec6144e40e337700a59f8e00fd482b5b3937c8b57bb8721adb0fc77ff38dd043d2237f175a6de9cd065be9b8c091eecddf8ba489a4834af80dba0b5b5e9734722ce39aede0910408ae2c2161fab7bf8ba2fa4662f510bc5ad6e3ae2423746fb6434177153da813aa1f47d401999b911f86d0641e6d63fdf883492319760c2d984ce9284e6c87c62ec5c750df807c791170bf05e49e95df9f574a0aa5b6d4169fdb5097e506cc6461ff40ce88ed9e98e38efadf6ce5746f05a2d9a5a9198b6c26f6309ec7e70428dc0a9582f9082f617525f514a76ca038d24be1b734c2b005928abb5f24980acb33fbcb9315177d861ed9e7355bba9e83d11eac444524988d0b562c9cdc48984775ca97abf6f5ccef4c99d0b06fd758544b7399ee93f5e3b031d7144fbee946ae2ece0036054d0c4c31527858e9b8a96d0b3fb127bdb4c789dc6b0c385d7b5c8066705c1ad516ba72b3124295eab1be7a24e3317bcdee2f3364f4fa80572234c488414597194aa56930e7b532bce2de67a8c8434b6c8bd25dbd48056eaf21db3dada31dc0ea2a5eb781430c64e17635be84a3a92ded7681f067f74896e2cdd442427d4b2d0cd9a5002ed80ba31edd1b2752a701d7a4e8700a13b5d06a7606461a660869e49076b9668de654821b22cd01bf07332b16e68c373574a6bb01f8e1fcda1d6232efaad605da28645f9aa55a7ed0fb1a9a71548a6dfce6704ef798a1304da320b8110e853d0b51d07d6e7021eaf47cc4d729cd17c35f2f1eaa53818f0a2575da9c11d427320d9bfa9d3311834df27e57b31011b81dd14e7ef105fdbc5639ef206513759759bcf25ff07d02918d08ac91fe99580c31df5835207cda7515392b91b0e121646f4f913b1438fc2f5ae42c0a20cd1f3c9a4ceaa31e5dc847e29eb8ccabfe3aedc68e2986e3db00e6860baac30c31ae1c84b4e199df3e5986e3ddad80f67c6079736f3ba793e31066f640340736e3452da00bc1fa980f03025efc4ed38c6a90df510aa36ac05775b862ec2b6aab23385c3ca44909c6431ba18d1d4bf03b9492f2727072a9f1bc1591c60fec87e1bdc712a4c7fa3e250d80757f51d050057a165e4acf4a2706d0b156758bda84655bc986c3b55c06f1949ab9c78d7047cfee1c3205b908071f9779d0197abac43c8697978238430de3ea04806b6477a9fbea4f4f376e2fb44898960a34eddec948bd6c39725151cc0d3376c0cf742a9a21b90fe79c4417249b0eb245177ef5da3cf522d2d7018f64f4a032b3d7fd5d1a7bbcff5fad8620838cfe242ae9c23c9c7a952815b9bc13ec1fec0ebb94e2e56599c6dc09f5b2f76b90975db8b8555769fc4d100f601b792e67c1cfbdc46e3d328ceeaabbe483e0687288cce5d37d745eafd12854b20b679da6b0b559041ccc19b79291d082245d01fde489e4a9170b4e8de578ef5d9e5057bbe5e82a7c01d7d1643c8f447a2fe33b6ea77f581658fe761063675166369c6b9a6280e05ca25f9cbe5f1b79db365562c668e1342b1fbbe645efa265741a8afb09fd5c7575dc2b5e806eea8c42101bb6bbdb7f48cd037bea613ce3b5460a55ed75386682ed927788f93f139ebf63883829e92c7962edcd0eeffe31ae5884be8d734aec9b3c21e69e550f7b69f6c90cbc9c00a180989eda7736ed02dcda33b3b8e604548ea149afd548fa2edee8fdaaa421439d211a9f56484fb00956c40903522f41a0633d74b92d90bb43c439b93a8e9ce09922f5784c358b51d5b69a0b95bde388013b4fc0e1669326d05b5db120c2434168e1c0b9a6b6443566b4c6e110c1cbdd10884f27ec922643cd7097e92b1626235fdaedb54d02c5a1a46a58203bd909a7a7303b847f851ae4bedd55646c5762edbedf863272f62187c60600b103b80ab212ec94775ad568cbe36b4e83efb15fda69a2a8c84df56f5e9332511ef2ef9a7557e0d54c34c19f8cdda228d6871356aec5beba2463b0140d1d296b38fd3f3ac9834756c0777c1cb4fd5e4432e76b186d7fd5b57b374e9a7ccc532b78143abc481733307d43ccde3538e5e3ad452e230eaaa2587569d71775a1275409bdf534db91a05a1ea6f25cc4e1e7058bc4cf2d6077c1b275dd04126487904950cf8b453e47075ebed07ce8f0e454322e88b5c2474be50e278494a57fe7120e699896493d76dfee62f0803cee783bf3cadaa7768ea2b3c662e547f2b47b8763ada6f5bab5d248aa9199dfe8fbc6c5f3923dd500d36585b515419c7ce252af24bce390c2bf37c5decf51295357f5e02e0cc55aba78fa788719abc2316cde8ecc6a7c4f20d143fee316912e5c4e77d2733638cc47c8c9958a48dff6a8e7908a27c4b8d9d935a9bfe26f13649d21f45839b2248fa04a8df38d5333796d4fe0bda44aec47e2ec644db3190285e5f816547ca2b1f056dfba575524c91b69388acb53660dd74a3daa511f0d79e62034dd70ef289129e3d34744c7a011112327bf648c81b83081c528505ebcc97c279da11286a037873b1009e69984a427034f809990b25ad20c080e7fe608baa6ccd4484ee883be7d9d087417555be1dc66fee89a972824e4f39f791dfedffbf61c34745ffbb246762ecad8d72042881d5c9a6a4d240bc9b2efb61dddd6d52b426aea923ee6d5308f7687dba6fbb450a76d755314fc6e290f1e2d45bc9b11b8d21d827838b3df9ec2e0a184f8403457a59e09e329ae3f10bcec8417adfa15b4c72601755a39dc97347aca4bc2a33230d4d7399811babbf03917237722d94b9e54465a433bcb38c12ad96b10b390ec72fd314ea507aff3030bcd04cdb3e80638f07ead15afabd0c8b027c9a70eb249b8e09e24a5f9f0128f4beab21f206bdfbf2a854707c5017cec1177bb5513513bbbaa9ce6e280f06bc5aa0653711791860da9fef7a0d958f6d46f306f7819ef0603b84c0c8f4fc11eff05c8907bb6c7d5a996c1ba6f18a0998183472b7e9bce9c2644f36359964e6953ae3ea9418322d9425de55a79bbca6086952dbbce225e4b3b700cefe5ae7ec0aabc722ff7d0afbaba57a1c5d151b3d8a614cae1277864b097e198a2102c3a3886baa6a7ccd0060fdc9e20634120e6ef281deccbbef72f6e84fae7b645e6a4161a94ac8850cb44a719b693fe51eb1b258217808d7afb82f5624014016399c1e1b2806461923b7c985b9b19e1f443c7770121f52f6c0276f04075c2c76d7026288c7396822b649875e73920e7ba917106a9231813c0cd2de562ec5c6372cc0df074b0bb93c1e6d4dec1e4d2edbb2b70cefe3989cd3c28ef5e946671e55194a85fc6f42eb2679fa65e8882ba165a097324891e2828503f7679993482d30e03ac846ea941cb14cb947dea9d26a404216cd8b9062a6f9247dda81c29c65a38429f914dc87d984e193a990f452f98d0d2fad60aca9166d3d318c014ada66942bb1cf0f95fc1332be4e80ffcf5241374abff8d02a7a89a892c66747319495c6a799814666b3b3d67b99aa01df7ddf96c7e52b6f2b5399ec18c92a64f7960070aba767f5f481b877a9c84483dbe0098a618899a8582615ade3625c9d394b5395135fafae106d93ac98d300e34449179badc3030abac441c466f2be41037c4ef19e0e1a45df830b69aa790f04ced10725f14abe6fa532f6a44afa2c3561c614c935b0e936f455139f8c6f7747fc6c2cbbf1e95f741c524d8832896821ec4c1e0a0b42a05e51a349f312144b002bd4a6d6eb2cc0334dad3e2945dd84c3d9193228baa05290dbecce2df089dba7343c304e1cdf5ea458ee6b87b02598382f32a14fa48453b129e148475cd11e0e7ba9421cd351f6366ae5ff83c4214c8cb70140359d1c4cd043be177557992cad3dedf023d7ebf6e3c0a7191b963bc7c9fbc4c9855a0c7ff834046075f5fc9202d52afaa4b8a8f1267de70b9d909bb3b83c60f0f324cac51d5136d89e10cc5f9b14c465cb1bd596e75f59074a352d997abd393cc1095d0fdbfdde2703dc1e1d7a6928a9ee21d507aa377b4c18c8a0ae02e65c2f53a122f92d50b86d01c1768a4a4c22bfd3a302f89e10315d29495fb88c645a369f4c3ad9ba4aa893c49c368bcecef5fd444ee828d5ea13ba140d8b72e09b70eb2a755f95383431ba99b13cb9d1093b07af21801f70563a953e2a98f97295a27b1eeb11a428c27eda7c7f94a01d90100e1ee7cf0befe4e5327cb251cc46eafc75b84ad73c041be940f8b12c0f39f23313099d118c5817ec4cf7802421454bba974cbd0a7dd55128567fb39e0df5ec7e0ac5b10246de73ccd15d9e24d2758a6d247e64359dd437284ddd2ead12203190a2a2f5864b22280eded2612eeae453f086d88288a90d481260b260c919e20b44404436f94bf2b3a249d48982b2a1948e7510ea79b18e4283cfe4cd9dd6199ec320354c30b5221692f9f258755bf2a3b4fc7bc123237455db418c4e13c25114b7ad9e2ca75aa67743c808e4fc3037ada71171394b0fbae5b6f6b09248cf234f791a22c8bdd7161a7a23ba55213879675c526d016e6366ce4bd2374e41f41a75599dfd63378a6447e0ad7309ad43c731b0870ee3c317ec1a9023c0ef6a8c9020639037c8de14a217ae2a76a0417b883906b344bf650ba8019148ebb9c755bf1b25d983897fab8990034c4ec0b707359d1ad32c5265fc641f66a5f9eb6f47c53b48bd3d69d2dc005a14a983dc1d95ed44a6249544a76d3c610d3ca807076b421fc8895bc6a00598240f56f09358b6c05eed2d8ebcd443e13b2ed46d718bbf68ca0650a1c7298bc06c595536b0eaca2f9ba7b625eb226b9c310d22b68279fd74101f42d0d9f3e86ccc8213bcd6ca0bc26d4082991780bf1424653bd3d752dd76f2c6f70e7b03c3370bbe6289870ccce4ed6cba6fecc84a154c884bbeb8bb03d7e07b0fd9ed968b6b63abc4e657a16d50c440248999dee7005a123388eb7b1acd34580e8bce658938044eece108f0e48aca48837db8d57d99ac37c10e3575362c516710b8a72636da0c9be39e04a090a96ecdf969a990208254b2b713c99f05662a568e0f9a6c41d396dcd03816993464b752392ffa2c558f45bb178f9e98e2f330d49c3cc1c9827f74c4afc280d1b7d2a49d0ae68c5ccf75751ae589830dba6b91a194d6d9edec3148333569736cb689b408ae515cc9c73e4f2c84bdb2a9d6797edf30cd53bab028449df7e827ceae72fb7bda5c092a3d9eeb0d9255380c27e29ea448cb46486f4e0a16fd893bb72481b894cd0acf2c70270fb113e7f775977821fcb9cb349c575985f2c732c7a9b2c6b9b37b2a40912cae67a16d54984f489c8cbd6ed9f6ab69d3c3460ab04fc24c17946b4c643de38bbf2da35f0f13905083a42a2ad0891945926fc63b500896b1c9dab4a757022deb4518a6beedadc26f5ac461beee0fdecf0271b1e5bd8628342e01e0e6845025116e62ac63d1442f91d705b5c941dc7ee5cf416e8fc90b2e205a4c84a6248bb985386bf262d85a9c86a16c5346dd5671ab86f18f2934c814158c5e71074d8b0c1dffc0e9f32813d3bff1e2d9b8dff67cba134c17ebe95477374824dcc64879d1c172184fc5019806b6f3a6ddb57c0dda5c16dfcc983e67c31de66232d61a357ca5d40e6ee70f9586f294ca1c7257172b230ae279238ed136a4efb2f6602e35f0fde22e8acc32ad1dc2ad2fa5b87ca26e2380bf0e808da0386fe85600c4a1564b2e8fcfa273d29fd928ca427516ffa325b2bc27092639939d46135b7180b196649ed4c279c238088967143d67bd83aa45efa1d3f2bac30a2acc24834aa1fc922e4b337d573383d56043978594f2a65a983de1bbd2b72264b49a12f78e19ffb9f0b1532684c4f341090fde19dc856b215975d3b9214d9038e994b1c41f938f629907db6de02302fa5bc7dbb5cc6cb946428eaf29e53bdbbabdfc1ee237588d95c51cba4cffc12b47444dd26458c7ce309b3f192a3e6b9a83e2b0e038eae0ab8f5a6b36ff538699436cd71de5c8410f022710d6f048ead6d7cd382b8548a7e8ceadf6adec1ff2e7dd334e9242ffa6ad8edc31ad2393596d392ddf0932f210318ac9f9e951217ccb55017022cd96eacddf9b6fc0360ce0d5ef23ca170d750238886a104c17c2a26565f56ecd8fa138a45638b2fa5ffa2bed4116fce78c71d0111738ef5a36bf7fa4bc8abe05ac1df73c5a1f741caad1265e5a844828e3dc8bf14bedf5a86937e6ebc6f0e6a1b911700dbe506d1579cf83bbc103c0b202294dc9914a245034c5696caf5ee686c953148f28a5769c327d5a6b18b4cf6a1ae6b17361da2327f3971ff4ec22faa8421d4856fb2c749f57dd12af2c8f03b31372cd96dd5485c3b151f2661ec9a7a009480118b90a452210cee68ea5fc8125b2cc9048985872d55dd399df8978ed95390cc39474cc7950bc61e8c7c8e328b688626aa9082b66065b02f23777fd560c214db50bb2235996002310d154b9c2a8c663206646d5362e153b25dd8fb95d0fe45b3dd077353bf0329ec392e0d1158c534247c89f19e62bf1c437a09cdab330fd342288e08f0e81890c9f5a48980248f879eead40f5f2e35a0473041a606b895171c72187309992314fdcc4a8257d146a1dffa882946afde5a090b5ce2bd5749652f8dcf8cc0854822270a064782b6713331dbad3dc7ef53bdb28ace03f39e8419247f97b0bf0dcb8d40b54dad6e9a4e46d832431d7569bdb999bc39c199c430e5aa1122f9e23865962ae0fa4a890f22a8fd0d15a3245e1d6f261b53b5c6c49a41f586d31f84e2dc43b0a68af85076012e8db8f09ffae92eea600c2848b6826a6684af2bbe5f1c1e25e40e712b458a486f0020649ea724d7f6003d168a550b7e3e7e6f55ae446075edb6e2d98d17f1056516ff7562837c9b08349484c78403a21beeea1ad17bf074f19d842bb30bd5df8b7a277b89ffe6d45a4a91a193fa5cc8b377622131e6aaa50d69561cc3fc7c7a81f17883c5bfac211f4581207d11716cf6c3cdd3d0622842cc3c031e5adc833f63f1e76e9786ed3c7bd0e560e540bc22c82a790abfd95d3189fc2795f9daa3c4034f38a72b7c55b256d8faa2d576452f70dd00c4b14d80b8002df42ce1d16afe9c17132cb3eaaf3a4f3c64b9bbce86044c558cc90e6d2900020f3ced4a9aa3c2108b440139e9ceb266886730b6e6e679f84cbe1dcdef43b1839856503434ccbb96b97a3af41ef9de68acc5e74b7d8ab4ce6845343c934278834cc5ed53d93cf70b9cf170d905b2ddfc3d3cb6460afda382eedd0259a53e879a18b84dc3d3145c73c9603dfb0dd3e7a52e084d684c25c6473b63e42e45ae00d2c86c2c35d4b08a5fa9dffc775026e28c4453a906e2cbcc6f6ccc277fc53f14cbdc35573e49aff430b2d94ab8c8c8842ff469b86e1d3c0514f30cc76b8d51776f951b19740217ee783c198fac20c23e860b6ddc2c2d52659396f156b524c10d84cc28f2febcdb0d791223889b7187208a2a9f61393c81fe59851fa1906c433bf6b28e47d265e329bea56afc84fc3655eac0834efe38b95f658de9671b59b89d1537b87b4be74db0606b7e15f6f862b13dfb7b2d4ff07490cc9251c24fc74fd5fdd147aff5763ba18290455f1c53aae545b985ef9224009263b67931a46ad29a19cde6e1d26ca597d90cc404dbe945c3846c3f6140a05690d785a143ad3421a72c696639e7025cbc08b1414d25bede59b3812c89b1ef050869431d04ade46bf2ea84aec990ff3fca7193c8a11f70e19d884df6683e671dabeeb03907395a884df15728136ca3a633b116faeba2da9ff76888aebd740bdde34fb83fcf2417c48a9a672b8070a0e3a69ab580cf79266f9dad01a355d2386f6073ae28dc290710028ea58d7612ee654b72d066a6a373427b75b70984ece15c8c9cff8193d73cc3f0e24926d92ac3bd149ab952a4945922feecc20a8b4caa9b1a161a35debec9f74eae4f5d1baac741b4ab6e9cf79e21217839f20912ef5c68a956c5bf22b30a6f756cae7d690aa9371e299b656a9cb1397e8c0282db227d89b009359a2e73fcc4198139766bdd73472a95f4d0b4885cbda955754a0378e947c8f8fda24956dc18d4f0961674fdcbd6c8bff46683369409c401351789e7008d6a304c374bf2080fcb5f5f3880283950b433fdb2bdd0ebfcbdcb7a117fdef31ddccc9b136d6276292f1c14110edbbff022092aee0f0f97af6e0b0304f688254f12d4b7ed22d0dcdc3aad77c70d0bb6eec952c5cd906cf3eb5ef3cea1f6e49fe163a2fe1f85a848197ba83ba8f1fa8331c139eb8719206f626cd1bea6028f8e33d44b6497aa2baeed1db11add49aece510b8c718b5414835fe3c7d89bade5c36272fb5b3b83211ebb945a292cfb5a08f42b34da4cfb6e8c342d36774d7f615b0f98ccd89efb1d510d8cff6e9f3d6f7f2bbc7d3656c641c39a317dc8709793728474576bf71929b98ccdc5f62cb9989a5109cd30c2e9294bfa3abbca68aea01160150abf395747a599aac4320199afd92645da9a569910761a8e9eac5f45d422b2975735cd59393cf64b18a7566bbd6ee9c075a336d8aa8f1431dfd1d1bccdcfe65a66f3b16d2ac0c96ca92ba369e4eef276a9ea3e5d5feb14010e1ff7d6e0f0f988ad1d44399c0d974c7ab02933c002031b4938891d7a965dd9295d29a1485e361aa5aa086648b0a7972237a10a7e39b8f219a420958e9c35ae0f342bd20f0772867e51503d0203eb08978bae5efae47835df54678359b0fcab5032848c34a09b6047252a2e015a0d32155898c3a513507eb55fe73d640a6602be2dbd8c270859b50db1890f49c1e749303f24440c3229488788a324d5dda9c9cea0b71d549f9f0781e989d3fda7822f1d55d67bb033f59269f12c7307b6caf0f98485d9794cec242974cfcc4c56fb495b7b3659c57a18a7977a2cd8230a9b4e03e76c2fa75741ca3fe871dbbd5f6b0bb65348f4950c5b3645f86b7c3a741347d11717b37be3f24a1e36bfea4d648bdf496ad07b15ee18e895b96d5e865b88fb86c37c72d66d0c8abaf521af5941a7407200a72b800ee6ab3418feeb8bd743544121470d383eff722824dfc83223bb18a885ea642e6393ad87bf25e2aabb16157c114ee3b82ce1e3dd37e49b0942a4110546055835acb414f86b7a2576a6054ac2caea7b8153cea0eaa58a9eb941e1b3af984613f07ba3974993525ff46f60680703f02056e2eaaa713c5c271a5de89bb4a37c4b97382585704679d083c092fe944e66c99200c7e6d73911f7bd78c03733eef9da7a322865235891fc358951dfc13a44961cbdfde8347911907b2c2fd89978b0ef91fbd86b228438d2057c90078899b67e08c81082776078e548c616841dd6af4c8353385131223cb4c426be1e74f17b6cb5238be3f08e566b26ea1888afcb26227f13e37ec74e541e730c82f494e5439ea1f3ad20f5a068884aec7c7d1209839f868f2b1ffaa4ad10f9f1f4523b6ccd676cc8764d4860636a6e743ac78ce4e1346c6af4e4b6e8e615bcd2e96ac16e523f166b9769ad829a5f6bdf532a8660b45bb900a82d88cc294a104162b0517133f3eed20c99603045756677a25902e578a42e583598104d45b57132a88765280a793ac85dd7f05c1155693a2a0349141713befa38a383f5a8841521092968308cc288bef71aba1c2d446796f4b309cde352649b7f03fab2289c2fe357f3542bad1bff407f904911448db88ea32a5c2ab0e136d085c79e02485fe3760c92d58cafb835ef157b1cf79e3bc3cbb31ad56c71bf4d6f4c0db84f858b2ebd1fbbd416c1ce804af8b192af97a76ba50157fc0016316a10e25682b005536e5c8d01911a37be9051e150ae552b069ab1ee0d2341949323c7e4c87468878e5c3504cdc5fb4f604d6701704da31c9d04b8f112af15173b6cd71a8ea7dedeafd9d22d18290d3bf6df3c3115ae4140283fe375206dd92e861ebe5626db2e3377cc236bd6609892f98ff9d4fa1de03f8513181b3a900d32778730b3a4ce8fc76cfa904db485a3b930b3a7bb11682d86797f31bd2449daf6b850b21d15901bca52a2b44e206954156e15eb95821460abee09721a070ce38998bf5fc2d690187c16aeb4ea4b4021103e4eb02aa45ce755b300e334e632f2da7f3d311957bf27df8cd83ace367c667dc187e9fcb53643f4a8289e5b4587db86d068ffd54d0e62d4015aa01a4c200d0c9e8b6849bd884aa7d84b0073e1a01f972138948b32e3d3521216defdd6def2da59452061207e7067b072208729f23087a8a17d4a1e0f7ee3af5e33230d33cddbc361c652a603b898885613dea447b138bf638d4c130bca439a7f9d6bf2fef69f39c17451ee0949efdf40e8a3a6c9620e8e56dbd7fdee7e7fdb3f843b4273d6fc698a617d383ef93473c56fd30e54d35be2a6faac589454f554c9effb6373defcb3ce2f1e7ccdbeb8280acd7cd5b6f6ee1f13e444f2bbebbc9de34cbf8bcd50eff3def8985e6a870f77d9feaa671cfe98580f3f442bc277ee617067b789f3fbc6fdee6cd7af71d04ade9a6ef9b15f379f6fb62bcf3580f147584d4c9c4e258c3735ee1e1cd55eb75745d359ef40c82d250a8a7c6d777f0528b1335a788f2627ad0cd21fe7d7fd6fb37bfcf4b6d1fdb551c1585caafefddb1f7ee333e74cf2134ce1ab081b78f0def1a65bd69e5ed634f7b0ec1639ec724fe8a3f423d3fbd981e4cefe2c3cf20c24f2fc25b5147e87d25ceeb308aa6b21039a2afe56d0524446e1a47cb1b11275a052f3a005ade8492a0e85073af87a22d165d002d6f4341e8db9090a66979236265488906b5e949dade5407befbb6499b7badb5d65af31672c324faf8f40c0fded21c4e6a2e2914757036bc8bb65e87cdc4b8cb4ec51d3513589b356f9ade35cde5dd81de35130882a00f358d3c5fd5ae8360ada67ad33f41d70f199acbd0e44db549967707576279d3b0f814b0dcca1c67da1c8e9e1c8fe9dfb96f1fbab9aed220a02175ee1474ddd68bdef43e54f3d40fd1dcb8ab9eda74706e3b0fda2d7af754bde78f4d5df4e077f8aecbdf7168371f5d5d9d5dd7e1e8cc3dcc5768587c0a3c651b40773837ba3dbf5608df349d1bf7f36be6f4dda71458ece2653d5dd6d51f339e15428f7aee654c0fb8cf0c72737fef4e220fee33f3ccbf6e1af3eee079d356a7c9b21ccbb03c4feb61fbcb9cc8a39e07ccdfc3cafdd6bcbfcce21ec37dc5fdc45de4322c43d1afd166991d48993fd43057a92d83c77c783ad6eb549911bbc6eead1175d61833aec6d318d6306d77a06f4ad7ee531dd068bf0b7ba9abc6d7f7f0527b336f1c9dbae429ddcb323dd8936e9c1a3e925422656e1e3465d07470829fdb6a2e043095caafaf848a63de0e3aa0d1fe7b2f784ec80d6dfa1672437b3f6d1f8a1af128451ea6f37879cf4efc116ad3bc42df7954f3167243833f65cf045ebccd323d489a52e60f94766710fabe866d96d90197afc4e685b8095db90965819da030206a8c0df626a4c27589c1b9a053224c49d4b24a181df40ce344cf6bc1304740fb5a3d4aeb7d36e6c067bbb4054f9d9ea2941c687a3aa7143bb5a439d6fed2d8147889376df763ce69bb7fdca508ded00b69cef4385a49d471bfdb8be0bdcd1b3cf7ce72e07d29fcbadb7be31e79b8537beadd529abd772f733c40cd653bc6a6c0f697cea36beb3581bc8bd2aac05560a9579436e7fccb47cd1ea8656c0a5c6f3f91d094120912bae08e3cc81de92b5fd89107bd52d702e782be1f4879807ac65c80f79026f4fdf4416ff31ee284be9f5753514a0e66ded3f64041eb68fb6bb3941b7c5663589d05b52c680aa49071c314a0bc9c692bb7c034b501b4ddad2e304abe40c9c1e40b098450d243942b428892e50a1766a8f2a48d1d0b068a3746149e97a32f4e8e90bc20690db4ace2e58af68eb9aeebbaaeeb6ea4b6d3659e9d5a2bfd1550d6aad0ce0e3d080c90396a38bf1cd19c189aca57708a1406bc71b6d05f9468aa63401a8a1d111ba20d17b441451b53b4f104cd29dd5bef951dd8811dd88112bc60e69134af2a2d51afe4555aa9ed46fbaf564a6fd6a91648df7bdbde16a4eb79eae5373919b8a4b1aa1971b9a2f2625a5661c348972d5514c58a8a8abe883181370ece0d4b0faba5d814ec79e1f6c287f74a0f4f45ef60388af7c61e3c5d38f2cc83a3d45e771eb0c48153dfdbe588de780d296b409975debc79eaefc11e1ccffde4e16e174eb18693e3329b2561ed2c7654abc594f0bcdaf48e98cdd6283243a2ebba3584745764a7eb90b8b65235d0900149a7da7a44d7753335c8d05ddeae14169648c7050a8f8b1521162e497c5c5450e2d203e6f20113971842959655d4a0d245098d2b5ab4c45420f680c4650724a490828992266158100551145ad6d86265b5a58c1a2e8c2317b8e5c96b0b0ac02ed98a59514b4a4c9e819586500a0b34d210d148c2828b19795ccc08bce5910e086ab0a006d1e90c24a411d24cef991632d3f772ce6aa9963158a05aaaf041cb114e7cd0e243135a78d01c4777a09284a4a549153003354fdaae269d81a44549c781c1e57db3909a7eac07cfab92144652174e10953c5a801bd6863112e0edd583947a94867fef1612629a53eb7dad73e239ebac32f4ac77cfe75a7edb17776dde3497a02d1d37a6b11f30ad1e57a71429d6daedd98c6339d07bef3d01a865153378e082b497df893e3c70b5fde50e76e1f5c2180ed8d29c7b020d219c1dd05b318603a67a87d30379650eeff48e5383a5f0dd5f7910d97ad006e9b9a58fbce9e9796ec6d9ab62cf0c5313100b81141aa413a476d29f117ba203ad8cb1e9e27b6ca68b47a150a8bc59790f61c11529d861b5facf8c5e7ddbf07b6cf8a5afbe635028142a871cbde93d367ad3455114f35ee5ed012088ac21854aa5fab671de51df295114c5ec5d4a6f3299fe23d3a6bc5179772d70d1820c1ae3ffcc68fc6dbbefb171bfc7797717bf4f2693e93e2e6dca56d266cff7d27b6c1fa5ff91e56d6b90d28513bdc34ca94bd32c256d7edbec7bb8db6c3f9de66efb89d1dc7f66b4ede7d3df6d3f2afdfd47a6bfdb7abedb4af7eec35989572e58cd44400bea8d1c58bce9a0e795e851b74e4a2b0d044f63c769f16a3c4ec98169fc3e5312758807c5fb7ddfada69fd5a56a1aa91647db85a7717671328d96d25e806dfd971a25bd295d3ca561b53e3e081326ccfdbee9dd5aefb453e2fb472b18f8fbfc40581678c370ecbfbcf1f77d0ef1851501150147be431140ef0a3f100b374531bf5e0ea83f1d02f4a63f40ee0c15a93f65598bd09b72900647ef25709c41e8cfc772d7a99dfd02a4e03798f74c83e7bebaf4c21ce3026f1a8de6a48efa3dce8dfb5a183e85c7f0aaf1349ac6f053d421666fdce0ebbd304fa92b0503bc798335a976800fc28401a3c1cb1da444d752ae99273c4f77efbb667798774d87ef2ea0a7103dbf4bd073fbe8b951d013a836e2d41b7a718c65c1e0bfcb31a43451ca97b22983fb59e61d8658c3f4f6b9a5716fb0434a13b56a584b73a69d344d739dbb7ad21caf9b595a90fbc62dc475451da55bb5a508082f2feb5d23d596e6c4b260122210a43e1ced384e1ddb62c605be00cd89c008414898d233715f5eb042d77777095dffaa43e87af08528bafb6ae4e9bad78e208d026fc759d08655d4a153411a4eb107d81a5ff6f265a5767d8a3a5e87c50e8aa11dbfd8480476237307ec36089a0e1e748df6ad71e69647fcccc191753be292f8a31ecc9eeaa5f13573d7a596c99682e28ffa0dde76b7f93593770469d45f3e5c57dd75d555ab97c6d4c1f1bbccd81db603748d2810ccdbf47dfad7a33e26efeedfe8d51b952de83b776facf19cb2e9cbf4e00b04413104c587a109a108862068f3c01bd6833ebab1de8e5fa9664b69f7f6de53a2a316fd92b8b9b0c4cd8515689496371748e0c20d23dc5c40a2f72b9745d75b0b5e74ad1547576a699e93de4f9f6a6330c0db7b85a10528b4bc1dd9426f8945df1340cbdb111f340942a617d38225a5cdceeba4cd3626036c47084289a4501e53f8795c110ac4b4e0ce8d3d3aee674f05b47d179f083a6f394e24f48c0e1281e6be4376314e2d69ec0ba65ade8e44d1f33257f9c3e60df4da21add6a153ec416168051584b49434796e940d905aca0decebc8d55f3184799b79e46bee01d2bbfe893dd5938137d61efd68dd41572829c551c28df5c4bc431ce6bd8275b479f11b67c3b4847285521c25d41b2958e079e5ca2ca2740ace500cd2a62ebf7cd477e751df7dcacb29e48696e2bf71dbd214a7954e2a8e20586260b07c12bde761ec72fd34dac6de3b109c791febcff481dff769d318cb82f794328b38515107f88dde4ba3d5dc3b2094068294288c3dd90af8e33283c082058b17576610d30b4ab14c2c72a45ada34c01819d88a4d92b40c69b3d82422e0a4b4cee5953e5bbaa5d3cb3ab513af3cfa8c5a6aed5d5204cb911a5838b285dc18a26f97a4437a2d476b16b9333dcd05d9244a0be27a3acd517b9a932d154bb9b0f40b2d625f68a1e463e09ab6a7cd39712210c3825ff373b6e37ed6f49d6370d3c31c4d7379e3cc5a2a4a13335819110c8b8d81ef19969e9693777d4c8ccf561b47d3db7b4758ec0aadc2ddcd532fb52d75b9cb7bfb54cdc3e3eb426e561fb2cb3bec2bdd024a1a05cb9b912efad60e04c9c154c0d843e69832c7d458b4fdcbc7bdbdea76e4d1e9ac68fb9b919bf6de8939732c1d758efb7d49d4511ab7a8a394ba941c84a16a14ff8df8a5f1746ebce369dce24b4fa1aea843fcf1e57fa28e6e1489cc9d19aa2ef6f02e8a3a6cdc67dde977dce1ef4129eef861df7d3c4a17bb5fd539557ef9b858ba1935778e3cf55527feb0dfe0b7f7cbc3112907b0fa362aef20ed33f1c8237eead3c813de3379614dfc61bd9b658e79c7eeb6d92388bebf39a5ea618574d552ec817b4c21cded904b080999d023938484b44f4a7faf3ea1fe7e94a04de8f94053c981771f4e976e42e93edc8f209aeba1e9f9404fc98177e96111041d2de506bb7b157774bff7de084d5a470fc9811e32835254149a9791b935dd7ece79f162cbce5a63eba56ecac8ccea43dad6b8a2b47b3ab55a2b152b859279cc5da7879d4bc62563674b267b2ded6ab16256aad4098fe24fe1c1d0149aacf8397a26f19c780d8e72de39e9af15e2035684961850d7e4a000e50d8c6f2bddd7ddb5d75ad3f7aa842ffef4d3c1d3884fd8ce8bb34ff521ef4a86e3b4d3527b9a958a9509e18d836375cceb23853061c284b11a8f19a8e99b796ca62b8072c663ef81e0c340e860d2f2d6a485264d6c2841107526a91890b6ab372645dc9634716312061719109d9637265e6e4cb2c8a480202c5a52430d3adc961891852c04b72552b4d5f2b624ca8d86db6d062c829af030051a234072c3162b6e35d470e2b6c8319a1063cb0b98b4018616321b6e3498a1392d6f3484c184cc49a53be0abfbf71290fa9b81884a19883d37017b2e03cd1d2b8a30340fea783980cb1008adf8720097ef8fcc1deef3406e06223277eec33c64e4831dee47e6ce9d3b209df2526b9a3b7614610c526fbcd7d1bb4ebdf1ae43654c8e536fbc6a5f29ae375eb519cc94dececb9dcafdcaa42150ce6405e5e9740422021282203df85206627acd40f3a6871769c6b8de74efaec116fb2f5710a4add6630e44046475fa396f0f64de6620aed70c54efca22d49b6e66209c7ad39df5566665209d1c938168067abde9becaddbf117c9881885e0ef80e1ec877300311950e14fe7b290339dd9481660e335009f506cc34034da13c6706022202324f33d03c101190f0330305a937ddc30389c7382824950483e54eea72675ea7ee9c46b9e375578d404440c0a732d0dc017f3a6a4c21fc773ca6400424cc134a0a5034008339306bb025cc29dc2fa7702f7786e8e70c526638a2261eee75287808cc9d59770aaab4f34126834c896c86995765496676264b9285942da9d5baee268314dd7535190cde2d862dbaa4e54d0914eb64de6470a2a407190d332930502847665dd7dd6068a2bb6e268bc1ca6a10b5bcc1c0843e697983818824496e498eb4a7e52d49113a891426acd0728666bd796812284d86c1b60fb4902641c60246b2849e94e6390e994195521d4039b17c617b9047fade906cd124904069f23ee491be24d01c9a7b3ed0f4bb474753190bf8048ee7ca58c054fc21a7682bfaa097471ac848dd21a27fbd9197330025cdf94a1d77413b76e3ee4e8f3aebdcb857dfdcf14be32eed55de404230a1050d14ea3f331af56d33a12ef35df3d96cb6fa56a9fe23d3aaefef2cd6551e58038c2a6b88a268ba98656e6363a362b158ac0ee430050b324c2693296fd667b3d94ca552514a3950a48d3033a08ff9aeb90d0c06c318a300c60d4347f0ecf57aa1502814468043520d28572a9572cdcccc744c2ca17246cce9748a71b95c2693a98b6254248b291586614c4c0ca5949ba2822ed2a0a9540a637cb748a30629f063df35a7d30985425935bcb0c209d463df3234343461188aa2784bb24108168bc5ca5b7cec9b2593c96c6c6c6279d71d92600305a68fdfb1d3fc9fcd6694526a040e59a0a01fbf692eaba9a9c1186be1c2ca11168bc5ca1b7ffc96fd63de930534acc06412f1e128a9d56a054129828c22abd52a8521be40120586e409241b5e30180c8542011d61021d7440cdbc5e2f5114816600824992e89a999931994c1d3812038a274c312e978b52da812561a6c081a662626230c61ca0e18645118c42a15080e18519804085a7d34914c54e08294b8e106dc2f03f323a34994c9c941ec0b0c204fb9ed9d8d8504aaf0d579ed0405fdf35b3d90c636cc53081165ef0ccb74c4d4d0d4d2a23892250acd56ab36464fe23a3654451a4455b2c2922c67cb7582cd6c4210a1561fc5bf6150c06fb4e7daf5aad16a534a7072a53d0ff3b76d5ebf5fa3e7dab56abffc8e815c63805a210e450e5df3487cdcccc7c87df59a5523d6fa01408354983e63d361ad461dfb2bf5c2ed7b7cdf798f37f647486e5ed8119c8204c15d97b6cb2d33cf6fa8e7d262626e67bf6fd711c5f797b40088914371a1a9a6f5bec3db6d865a799f986dd954aa5be6bbe69fecfe4dd81295590982193fd6746cbbe6db0f7d8609bdef5fd7acce9f49fd3b78c8686c6953707a6b0910315b158ecdbf67a8fed75d86331df334f8561f8cdfa8ec964b298bcbb148021431630d87f6634ecdb36f31edbcc5f87e5edfac9c6c6e6bbf50d8bc562a9bcaf0c92a8628bd7ebf56d73bdc7e6facc5fa7ef9887b3d9ec7bf5fd82c160a7bc6d1554b4e0c3ccccccb72de63db698bb3e137ea76e535353f3adfa9e79bd5e61def48c2856ce70b9fe33a35ddfb6d47b6ca94d6ff37dfa4c4646e63b7fbb6666fe23a3676cf2a6504ea00414313131dfb6d37b6ca7a71e9337feec3b7c0d8bc5fa1ebf635caeffc868d72c6f09c6c8115452a9d4b72d7c8f2dfce9a9bc51aff9b6b94cabd5fafe772a26e63f323aa626ef215fd888c1c8e974fab6d9bcc766f3f0a7d959abd5ea9be6fb944afd4746a7864431a38a2961f89f191d7edb66efb1cd6ef3306fd359df356fe56d3be1d3aadb7e44adfacf49abbe65dfe16d27547dbaedc7a44fff91d1274a29109233bcd86263f39f196df36dab798fade6b3dbe42df3d56ab55ae503b4f2e6c00b9292f0309bcdbe6d32efb1c9bce6b3bc31ebaa9aa44d148ccc2006606a6a6abe6dacf7d85897794ddea8afbe5bcf37246d764bd830628b8c8c0ceb32798ba34aa552651c2926684309168bc5cadbf40d280e545e30a234348cf1ac2109112a60d94cd2660a472168811354ec92364551ec80152d452d105b87c1f029bdc964327140063045a2b4de636b99567fb950c7efb1a18e4f29a5578a2574d0b27a8f6df5d665be559f61b158f77169565e49da148f7a8f4d3cea18639cb74cded5092d86b468b55adf36d57b6caaafdecafb3bebdbd56ab502720b2f460628925b2071618524606280c30e3d5fb6083d81e2e6a58d9b08d2e8847002471118462398225fc084b7da228c196a104952c58a9c257e4001922f3bc08085ee04545a60240331be222985443a6336d1a06822396224bd60830c2369c51846b6a8046d18cd6024c1c0d88875319a29626224dea25591ac224b1146538a12afa22e463945495718bd2886c983d12d429a4a4833e6170014c92450408221a4187ac10a354c1184e14246f18189c5ca14231903171069c188a6e88995249218c5c0280da31c455b103103a32228a204216160344365091fd2900205454958808910585c2086972ba6ec74b71028d19980caec61aa20b96cf1c1282c22436ea1a2091a8c7630ba01c30882996414c19c73ced9cd39e79c73ce2e4b4ac302bc96e3e6bc97e3e6e426772f3d419ee6206e562b7b466d67efadb5d65a6b773b9d6a4b738b0e5852da3c22ad04c9614feb4b28d5ba97c6ed7553a8cbdcb534449234bd21a05adeee80b73cc27fbde67b22edfc5a4f42c531befef5aeef1a4d11fb7aa346f3fa7985e6b0640ed401f0aac1eba950c561f33aeb4e6b771a1295a26b45aa5128426a426abaf4ae3b4d127c42a76990b45e0fb6a890f64e25124d0b784f24249a333f4db037a795b7977d3a3df3fdc6c91a693a11a0b479d558020a4043d3c90c629cb779a58b17006830da8c20d49bf9d91864cce338ff7122c9c6d828ebcd4b66b46225bcb2c202eba22bf7c2144e98319126c7dd3be7cc3ae07bd767be2552042a0e99d707f19139be9350c63c085e1652f34e43046f1c9a94671942230a40734872e084d2b6cc2e5ec6711e9151a2635e8ea394524a2967a71723984e2db7099a9ee2a0187a2af280640e0e4872b0a58c11e8cdfcf5888092e6f090ba3e88cc016ecc5d06911cd49b1aa551241c40e80dc50102bdb9432e9d311881d2e6c45282cc01e4faacc8bcbe693ab4b199c3c65abd99978d475c16478feb469eee262b3bba6f10b2e9a97baa98b7ee3a9d2e1b63a3a437adcc1a5dda5365572a6f98f6f29e690febd4d3b8ed766d531652d39e37c32c026cfc8bf6c98c2518bda1342b789d27b1e8792f77b20c12286db6685704ac894069d68abd574a29a594922381d22c376b094eb89ce9e0f75d878a340cc3300c71b6a73f103ce16a9e7b3b64afc3b0f330fe4733847fc294163e0c292d0cff8a63456fc2d387174370b4360a9e7bee9eacb5b6d69dc67ad03b37ee9ae636a03854e72ead8be688e74ee7b8d368ce0957f39c6ecf71aa51d21b107c105d3a1d8fbb947dae2efd946994369ef24de2e8a2b452e9a9d1a337a5a3c60d74a9a64bd9f3c68a2b968ea34982b73785b4a55d340d0b784f255389177d4db0b20b0a4be42043187d7f7342e7cdf644d2dc3b0ea8841f82d037488abeb7128be6b9de4e29771ab08125169a2398da59ba4ea52f71394f24ca06489b0140001ad80001f30c0b00b4dca5f4561d2aa9a492d660fa1797bd7a53ef9255c1a7625ceede5cb9ca5d2ec0110f11d9fb6c29b5f9b82600f4a477e95d96846970f721a2cb7de64140263b03c7700cdc3135ec8575e7592a6302e3954b7baf3ab4bef010d17da93435d2f5ae37eb32cfc300679080e00895b22b3889524c69322f60a7767216270bb57674c9cc1f1d4769089402b9c7586500655cad999417bb5c33b3f010d1cd98d25cb400ab2ebc242a9e1d481ace0637b2e04da36d8073a31683e9e7dc31f50ad7c7c8c09081312f67376879fb80127d43cbdb132db4147b70ba478575a89c491d3085e558655470289341b59f337b588020c216b511ee169afec67542438ecb306a5d7283aa039ea795bb5428ed3a4aefa59ae6e985a5535b2a0b83d2893d6ce5a45d4014c6f292b3d65a6badb5ae3aa79e9f93d6138fc982490cb1b4e0878788683d02e8c431692ee1390d007a585a90e3acb7d2dc6d955dc1dbf342d0de4e5a3b7c3f4454ed6fc40b3d2f30b702584f693218e06aa7a57672f9e21d535b1458b1b43774d1f27603162d002d6f3714a1a7be5cb8924181b7a7af5d7147a2279779e8ec1ce5ed861fb4146997291e22aaefde4d8d9443b95f2d451ddcbf80139b403c4434b391ef0c11c91bb901ba4ef43167d79d4ec983ea206e521c40293bd402660882d2e4132bf4d43227644dec90d615d173ce1764add500b8f6e4db1328b4d476a441758e00ce32664344538ea1476fa869bdf7665918b5d6322896947ba5b7f4b20acaa3342b83a23e03a0a41d776da5371bcaf0e2adc0cbbc9cf7aab8c068ca0b372c9468c113dd0a1d38c2c3131c60d0461731186184a4b50289e00908d838e2872b443f6630214313528cb84051046714d22944602d48082f33204205a2258c00f364871eec6ce1e6c40a97292b27c5a37bada0b202126c7431722b32c61938f8e0450b2d6a20b1636df0a2eded89908db660b4b59683c2c9510f499c00f1815b932fb4bc49cb9b13287a3ebc395192d2f2d6048c9e4772b3e1e684931dd55bebddc01057905ef0c5892bba0401a1c5112c68818b08a24ce1bee85aeb1193f382e3856b43d35b932f9adebb35d99205288f5c3920756c9c9ec8e206c901a1c3e9090d1ec98888a6585a8fa806dec51aa5264f6ab55b97203447e4035dd7ed299ed0c2c4125480210309788902072a4e50850e5930e9c2e3d6004207040ce589d44196c3922b6090835195142c31430e51a041840a98c08288304caef0383502999ba8f4ec3665410d99aa190800005000f314000020100a86842291502812a9aba03e14000f9ca63a663c9686921c074114c3300840104300200001630c00082003d5dc5100581f9ed1d318a988eec77d471ca0c13d73e1403705bcf014ae2a68ac472811264b38e4648384124ff762609d856dc5e2ab2ab6d5bc20ea68b20286af84fe016af0acff1d779efa77d60320592ae97559a0d7c7a7afaff4058168462b0694f1b0db8e707ad088fa38d395fd0979c4be13dc76048286161d0f8f3cc3f7a111fbc1e181ae3c82474343d38f038f707bd151fc4270a029053c9967d07774e10a419a40f3d1e3dbd9cb93fecebae111441f0dc41f20b8685412006f02edc7675f5a28779f8c33631aa051f4058983becc4fe47131798674a15bf905a629016e6247d037e8e502e14e46bfef0be587ce056c922718dca221e785da454707a0bbe35cbdbfe7cae86291496cc943743190523bdebeb39f56d477b83b8276415ff305e0d9cd838ea7589e5d79d0798ac9b38b071d3bc5f2ecc683ee9d62787665079d27c4e419da857ee517c8930bcfa0f308790c742cbf100234e800741e6e1e46cb33881834a560273547df67b54f482e4d34177fa1b8e84bc0933cee794aaf276462e1ddd0df63039d385099c70d4f3d77869e9e12f801c6c21485272028a59b628c5e81c8f3d69add5c5cb87b8eedd8d74e3d1e77cf23f83df41379a170d04b0013dff1c6b3c7e3f4f10c794767ee20e84df6847ee6c729fe02e22b0466ff56d15f43705a6f48b967d615d0611569c77a4104f8c795e37af97834b0c188f5ad755a96b157acf57dfe40281d0cf8fbb2ade3cc4383a51f891973e628aeefcf5417ec6f918a378d476ccc0b5d091dea2e9f18f803cb1bd766f7a981335bf1043c2659918b513bead6849d43700b19b489a0f7567973f3c437982282a978b45053ba60f7e5ccb70b5ae1f249f90964317bfc99012a0cbb9205ba2c755a6327d9adacb077d9b80fed4a2faa5fe639d8186ab9585009795fa0ae1039cc6c653e9a84a9cc4bf71460bec90fdbafbd9c2d35ebcb1946c40076e19bd468bf5eafab12a8036cedd05360c78785d9990a077707fccb81562013601802bac5e60708a0c7804a351020853fa09b10d3cf8db3559aff07b53ad2069c51ec4c1785d703d41e2b3b387c755c4e19731e8222a839b3362ef083b22f54be542a3868104e67d4016522c6730553b4aa75d5054e601ff08ab8d4a26651cbedab610c412c066c24bc719bc75038e0a0579a1af218a4aef206da40d3452d327dc262bb5c791c7ddeea92dcb8a4a9658850e7c9e9c528c2526150ae1726b2ea5969bd4ce550e6400c86df920aca68661ad1f5d8045859b09ce6f27f39d28c519a3e45243ea60138cd683a40917151931f25447fb2f286569370a6c99468de814a695550db26f9ead72376748d23c75fdfd0af92f7b526890b4182cb6834a711b9f73338613988418d5762578d7e0f71c4fa42dc6cfcebdf7b3243615f773c1371d32c7b745c26eee8d386e96f8850a6a25a6392416e50437c711c7392307c9f2c43b0ee154c667b6e73b241f4498c335c5a7f4e9e24dff81d517891ce45d1edc0279c961443bf355fdd8e5b752d1a12af1f74e15574520322318668c08a101ffe87381d03ffaf579f658ef284954216ea12ada3ce487774eaa47d45ec3cad25b120c6bd1fd8d7dd91585bed1f4f25793e019d24f5675dab32d1ebbb37b5b87747c9a148a95758a9c394c4afb14b807a2c085a3f01f9646af12acbca85c4d30036d82389a8bc25b9409a03fac59b0556454e1db603ce0ceb3b620e51e916ceeb341fc60b8264e92adeb8d87951d9701c3734d6d056bdac45c839b56732690ec4f199e54ad613e9d6a96819e0910011637243efae2d22c0a5234a195a061910368db42a832fa51b872ec0aa97c80ae344d5457c1ba0186b2395e03d0245adf17162092a02c7231e0815133a7a483845fc38b51a3aa0ef18b4ddb7233b2cff8e7afab1858360ea13f9d2ffdbb297f6341f0f4c58c8c762d342b28e17140d892191f22d78642b721dc194a0fe662917efcb8490b1bc28a765c15a3f6922571d2447ab02c67d7fd7403ce15f8ab5032f5943d4b65103f539518144ca40a32ac7a9dbe4679ca6702342d6259eb0eba31d21f97efe2c85faeb429dcd8ba8b178a1be9dc8238505e55b199c5735f74cf1f679c34eb74de77c40f24e0d8b877b4ef170e084ab47ea801bd357f218d05e361eab439f7362d9290f14fea65057f3e8b91e80be91ff31e5e14a9dca8d1eb21abe176c08cd6dd5435054dd88beb08249b9e18d2a42322c62999396f2635da39db28e1687c4e364f91f695e3e42220f6682c7cad809224cafbfffe42b278f41d9da3d080fdd38b5e81a9a8ae5560a39dccdc454d441b7c4ceda73a491d0ee3b00905761209ea1478adc7bccc22e9e9882af81f2f7c9223f74e78ef37e56cb349f6c8754061b3f08b7348a96625e11b08a4725d7042f73b4fc0ec927706d45e0cbeef2acfdaed7647077479ff5355c431ccec557a02dffd00e3429b4e84edcbd2aaf172ba95c46cff39ebf2ce7bd707db5bbef95b3c243ea34fc0b56659e2d8bb9ae1398513979dd5d9b6959ce8994f50df7e2b117cb15ce0666a0a5ca31a285934dc0769c486a193a9fc1a57e8995c069836c0db7dd49f11b5413dbcde20118aa1ff58ab38014c5ed828b84282f58ceee6b19a0d7410111a57ef9098e3b22c3608ca391dc7c419601d424b8d05789ef80dcb82f1837000273e8537acfd69faca723c03a6476dac666ac4436e266e255b3913f22d454481c1988e330a7c8b0e0542174cd7614f9d5dc311c3fc6e8b6e7f30831a25c345689a2b83ea644339c4a7fa25b2166a835762fe15a34b358fd18da09b4a610462405c83e46b6bf0ddffe3a949b8fa1bd6ae882e82e78b8498c1c01678c2e7b011f554082b960fdd6e20db223203c0996a2348b459626e57ab4876a80e6b0c44a9d52c4945fcf7093111fd5cab42931dcd90625be8e86b01c788b0f05812097db9a2bb4f5b52c07ca18d1444f5b51fe1e7baa90f992067ea1501ab46807788fabb6cbd73f47ce2aa0d897af6054b880d18fb74b924aa8b640af18419d444401ecc5e98e92924c4217696701dbbb80b58692895b07631a8da6010125d7996136850c736529930d5822eac9a3f143b631e430eb6409005093b469b0a8622ddcbd2da110b080a38250b6777191f72f186a86170a17dd929a029f8ff69318b5fa354dbbccbb47c27dfab6b6dc1d58719f29aedf40b2508c7fc710fc34799222bea4acea08e0a21af11ddb1adba14241adf9adabfaaaf3d8206310952872a5768dd936c2c7e6f0bbb19540aa149d28ff7fdf7fc741bf1424b274770d84d1fec93173153da3a993f79fc6a48d26ea84ffb273a3208f26a02ebdb39ad230b9534b08b6d19100b4988de5c0ede4ca5ba33fa95badb0896e40358739add399e475f3b28f3e9dce6f726274ccdfea5b32224654885b36f9a26e09a315025f011b1442032627e0031cd9b462f7566da607f0151c7ce7f5ca04ce6f09b01b8e163a8a1e6fa5ed51572fa686f25e08dfff6bc1ec3e3e759fb2909d0b09dfc3c74730b96dbd8a7122acfe2290b32c82b57900f757493b95093668d50bada36def0cd3ec5f1940a95dad3c570c44b74fc0ec2fc3017759d10296fd9103ae6c6f166439c44afcb559b2809621d6969ccd185d8a5cb9dcc07529a4cebf21ec2a7db323c36638b813af15cdd987ba1542c626ac4efacad58b3a6253ea20423b97e553b14caefa36f64ca86334dc817f38319afbbb6a26da916d7f47f9435bb2469000bf361241fe853dbffc70ae846491758d546a31a4df43416445ca1c6c2e49b450d5785609718836172c1e594d0f5733394ad9fab3825fed9cd292d3bcfa539246901f38cc7c7cca929089528f39d499f37321ea7e62956e15ec242c38ba969d3eafc615168c13463fe160e11f5ef80a39b7187b386dce6149909d8e27d7d4d777b32370ca2d8c9372c0113f0b7d3636fa05d43d275f8c93c78bf8ac46e247b4b486c5135f08d4d886e7d5434bd41dd106ebf863aa818446be8968535c412146cf2b338727f2fe6c0cf869540b1a80b4949972b438ab73ff811a25f26c662bc41a188385deed7580d7791985e2d6681d7b576ee6d761b69b9167099772c00595293160c0cbb5ad43c17d6fced6159864b101f35562bb4f28a1982f891ce22a59730586046be2070a40a020d874fbd7811f80e1cc3ef6adcbc28fe52aab7547f5a1836af623d4eb99c9100a73bba36099c8023461483b0816f5f5671f7eda5cc4c27de9b494987b21752b0c11564e1314f4868e0c7b2095173a3535ced806a246f757754c09dffb445acc8b22b3d04c88eeaf469bf264972800f029e6a44eccf5a2e0847e758ea1791951768d10c590483eb8d4fdc9eee0dbf1c6ee54c17a2f9c01f494ddac9db98005df02a79d1ac6ddd459f0ce5dbae07998b497d0c55d4dbfa6c0ff935afa347911f8991db78fb951a573b8f93af18d06e242b946de728a32757eec9b4dc4d790ef074e1913adaaac4df34ce0da68de9a63b3a01d16b74fc9c11618288fc4da195a30bb2198c0994ab837487b35f836a17a7d4b6da647dc93da7ca21aea9e53a51909651bf8495cb50da7145b1b35564fc267dae2c99e2a13226d4a8bd683d2d9959045400e9c24f6d6d0fbd717d0503de99f99582145c8f6ecad089792a8ef2f8979bfcb99f63562ac56b2746b9fd4650a9aeb0f942d59651b48ccdc76e84ad9658f7b0d52b9ac310c4b7f631439c4a8d68704d045432ce90722a2f13a7ab6e5d4512ddb6ef7e8f7b23a3f40d8c4e665ec6845245d192aed7dfd364e639bd548943f7319e9b4d39c3fb341dbb9f596a72e40f86746f9ec03f9272b361183bdd9bb3829a496d92ea473779887924dfd0edbb5907f64a2daf92af6dfc29071946ce502108d833a935b58f714ffb06d258fd6823cf2a63d91412f9d28309ca37c04ea5796b2af25ccaf288018ba215ac60e51d6f81015a853ec1fb10d77948845d174f1635831b424878a7d0b1ae0a2b85d7a3706d14cc2f87d68263ea674b26fe970b26f313c6588d1b90e93c490c4fd2729983d5aa2fbc3a00dc45796cca3a984260b55537f6c48b61a673fd256ecde82b021cd8353acaef9c62343688afb589026d9c3e00835a72b1c214cdea657754397042892f99734b60a1f0d58d17a8838e62414a73cec6044c275a18602a262fb2dbc4f4432c39a5bd18358e0047a685e38fa78addab5759bd3a2032d48ecf860083467f985bae1e11709b58a91c3748da11c72189e1ba27784e84fc827e6f3f597bcd391d24015b872af934c17f93e5fc4a3fed7f11d63e72f000301e068a14ab560943a4a1fbe6accdc00319245fbf369d7cf329d449d85f73811f91f70de6aa1a945ee282f81525f84977a335300f0b7734ad18e0f7a7a0d2100f83ae71099f34b989e75d8c12037d6d5cd050c0dbec6b1a71b35aecc1c741d8d3ae18a113f6537e82cbe111fab0a21077b8f54c6e421e4d1abeeff4e831ac398372cff45f83065592216c292fb41d7a3fd6846435a26c6ac39bb279498555d08e41a77918f0147b28dc0425d627360bf3e8b2da209d97700f8a467a3820509494048ed9b12878b6211fd25b3ca84b58fea2b53bce078a8142b0f7d173824c6ca7e9e0f36bf181e90920b1c7d206fe4ce2fc60d639054b62feee69768b60aba610472375b55c7a9dc7b9cc8109f5462435066986dcac4210cf2e75d6ad08695b0c92a1210f01610ee8c5e469e734fe58770e3fd06f40c84ae0affd3c587269b1e986b852022c63798a13e84234310fe837acea102852e5fde2c39f57f356aa1d00c710a679f1cc9de5d9db530d3c4507bcc9cbbd66798e1c26021b5fef0f56bbd9a80325c2d454f5d0096fab528d72b7f9021ba4fb316bf6aa4aa3ab2e63824009c56612d929539af49f44d3add0582d1c6c47d1bc5ccd7ab88ffbc94a67f3d6124b7b46cb8f255037202c4fd8459fb37e4ce2271eac140a85999bacc90f3d34cb062614ac3f6b1bd61bdb14a9b70a24745506a1113470da1264a97c7af379625bede8898cb2449961ab4edafcb2263be342c90ca40e5861f299e6847f64f06ac50e7891feb95002b4bdc6d54d2d5e0695f1d9343330cd4f864cdcf24d48c21609d0662993d236710b2b802789dd1df45ea6d113cf76980970dc9860d03f67e11a77ea1d66a517a2af2bc4132a946821892e170b6180178c17c39558524539613eb2a4822537963e00c7477f85680000e7e10d3dfe5aaaddc73d9d74b371bb39ce56cf7dc5bcbd9c58fb28e98a2b2d5ea1a6d79b3d13ef9d977286172485d1c0bf8eb80ae94982a6ead0d7c0097124f8280b59dabc4ab83aa1cf869e54625c91a67d1b19d538987fcb9486e6cea7c7132e2a9614adb1002d08c7c72d5f91c861399f8b109f86555b8a9cd2a3e9993e89170d70bfd472ada138322f0d96f048083c6012b4ac5961602c71e694cce9b181d883d8e684581ae3ead99b46237900cd0fadbccca55d82d520088164500a045a2aee2e0d3c2dd561804abe7e4b8aaaef42f90f70a6264f06afd4adda34ffa9d1e10501004df71acdd30781248a089dc6645e5555e89f3ab48a49b7652458a001f3ce30074481c9080b48126b26731a81b0a1845012e8ef6cabaa12e6d5e66806bf9ff269bfb310107f8b839283863fd06c32f691b88e11d80e8aabe9e07c320729795abb7f267bdb7844dd0790f63a114ddf5adee2313cc123044f2d3e424e49d58830b9ccc66091283414c626fa4e18cdb14dadbd9a433b2c97f1d00839bc3b26329b832a757cf15924de72e94cd25139ccb32911ee214f4008a9119c8ab666220ac797064b84e2d9008e7339e7bc9a0339d74be8d0758f4a25537810459ae201a83c6eb8bfccb82c94626af0b9c6bd0faac31249c87ef6ac968fcfaf0d4a5af51fb0df4ed06231eca9147e23df1616d2bc45322fa8a54cfb953c08860b49dc588c4638a8a6a08c910f42b78f8e6f14d8256ab54f81b881f40c7d087fb4f1319e71385dde7e06f1ec3b57dfb908b1275787ec098f4b8b9a68c8655042deb290c0e6ba7110a6fdb0892bbe033efb46a52f550a1ae467badeb403256c6706de74f67ef9aa7536d2b70404a51f19372464e2cd05632d8ad3b50b2162671a3c1a4b487d00cb3dc1d3b6445e96b8da3d8a75e05634c1b87a8ab2f3cab9aa829f7de8c7ff19496f74dca6352d5d2f366339cd501401056111323eb7a64fb481b1119f88062497f31dbef5e818e4211d8d01c8e2acde7a6e6de279670280228d3c3cc0ed36446e3363d79eea7d828bc8c5198396845e4c5500bb86efc7170ede24a8af5a2163ea8903bc4fe3b0a96b21517963406eec6f8b57da71daa85db1c3d52a7652ca90d190fba68ac7f196d9eb727831aa94c3ef7c971f63e61d356bf034cd6225dd089e2b60e93b76c72d2b35340d52974567a75e003db53347c7927044e30d48beb3ee369cc84a395a3dae9eb663d2815f67a088b8838d5aeab89a16b4907781a6064f5d31fd8ab294d4328db3b42b14feabbd8b1526f22ced8974de2256cc2b6c747ec5ff17dc1e66abc8867ba3442fbdec9421b95af90289246153807c321d4cad314dac5b3beb1c2fedd1649acbb7206715c5172f0f8f9a9da48c595baa323d839c9b1b13d91c9e36c7f03aaa5fd4c217f57f9053c2426828c416c55b62662a86f98d9b0e42e0541f9e6f3badb44512b8a095ab65e00f6466fd30940c860597bd3399d783e0e5daeb9f60f4963422096ed1e2abd255701b8bb310d43b2bb6797f319d134b37aafddab5d5fecbd3bb788a96947f0551573942647bbe4c44dfa0121aba3b676a8d4504c08c692c4560bb9f5c04bbc8604be4df84a466ce631be913cfe042689f03bc3abd155685b328341efaacfc3cd1c4efb4581e94ea988a47a833feff995fb986a056cbca8058f2fc18d35d2d2be2917e3e63d44a73944a65c829b014e4b73f54713da6aabc278e9b42acc47786e03527c4d11655f0d71ff93e65d79da00fbdc203e5dfbf5eede51bebcf449245bf0577f57bc8b9b02023d62f98270e13355339b701f26ced253e4b02c0033040a0b06323b7f4a3175c44fd331893f7332ae2b08d9c4e9ea8140a5ee190b51c55b400d77b943b74a26989edac180d32c7db1e0abce649b8f7d2852be67a60d5defc279e417dbb620d3dc8805073a4599f10147e1c053f58145b1dd23f8e57aea3ad3652229132d4fa386f1193e14c0fc6c07e143efb051d35cdc35b771b995d4140624d5ef85637533ccc897cf6503c04fbd55dfb0b9b299b489e0f9b9305599156c053ed59270a1d7674b9cc0ad6685441773035fe70643f383b07ba417300facbf6187d92e90e71b7b0305168a3b4db3ef84635c07c780daac40edce0c6c7969246d5004901263fc7d9b636216ad59e35916fca731324ea11222d948e8548d8ee6b963e3a1b19b1c0e51a341c8f8605aa90603f02327dd137e1b455a051a64b46e4bf065160bc5a6c9add42fb76b5c78f28de9797a64ea3b05abc92c0ea88b54954aabaac01d7dafe4513e65d70df0eb613dacbb43a285c78c117713ebec0e217bf1abed683a5c9b568187934d40c3785c747f2b4d9c29c58658df103bf2abfdeb52a9b63735a99dddc860532b028ff9f4a3e672f669109c41cd9ac9a3dd2fbd319d1168f67fd89c47cd1ac943e614f6064a97b098dd5296f2aad9021183bdabd20186865c3b9c190248bbf6a0bbaba6852312ab7606d9060e9c890d536956d57488a021271289f05255b3878a9031d5e9c0aed94b7b526735acbd3b2390912e099c5d3286728ac672f672cf0fc928918ccd740664c09f3dc18c6a4543abc8d2e5e7541bc52648dc72ad363bc1ea75598a063a323e52055ad8d9237c54a76898772df379908714e7b6d99134a4d9ff663b4f34ea2c915e3eee78574b53316f9d8fba3c1ab86b47c3726df6d29d39e3d6505f2bbbf28c72d6c399865ca365ddb5687873bb56f6360ec0a3a6dc228bd311d2a5b4b203842d839a351250d9b38a0eecc99ee088c2a801e4c419b3253ab64bf66e4f80574d3f253ea725a04b69b263085746357b20e1b167151dd843f6fc3fa504d62bb8409c2f7d31dd4d933d8299325083d14203bdc5e9d89eecd9a3bfdff82853a66a24a63b5ddfad64dfca72f36ade243c902e806ea49bb2436567f369882a1b3f5d335d4aa76427caaab92ccdd7170b1876f341a6ce319722ed1b046cb4ac4b73778f913dd2ab73dd6577737c4844405ed97c446f9e83e4247aceeca0b3af9a466d935fe8c6086606b4beccff1d188736cfe71aa151a3c37f19d305013f4585d81e8e7087110e193a8166296c7319764be39e31c1d087aec7c6a8d588999a4ae5254ff975f01c40db8a7f41b755422ed5e134e3e9d00bbdc78ca9b988991a94e4059da52b8f3709973f2430d28c96e3a9eaa3afc76a1062983d553c3df490ef31296b1a3153a3bebcbc777e38b803f59676ede93f84ef8cbec68436fd5bdbe9f439a98fcc5ec58437f6db9be8f493faf63526acea6148e03271acf14b46e5e55718a5f07fdc2cc6b26fc5c5d2a381bd4edfa592fc570b68b67c8c988073d639ed3a6e66d993a9b695ef7fd9998566a4982e5cc3bd7668fc29fcf904e4faa06987d381f09726b216abe3b04525bdfc401dac69bf6dd771337ba98195d78c73cbbc653784d9b5c5ffbdf8eec523553f3dbaee49d19dd682ece92ec9e443dcec7fd85c4b293394ae84a32fdcbee96077e1657591cc5e97b1bfa37378ba6800c639451f98ca7a8aecf76a83fe45e6f243e096461a8bac3d11f75045d938e1b803f164c292793db7b16fbdf8324aa8b139b41250b3210b7d583709186e4514f8cbbfb0fac39a6ab7f7b4906658e8c16943e35f4991629fd34968558af2e7084f9bac80dced2b956a428593b21b806ee1b47987b8a750990048e2d7654c25b76e469bfcfb1a046826934b4cb2ef961e71eb174461781fc537340b5308b385ce3ee6ad08bc8ba7cab737ebda89d03f5408de0781c933cef46a89c04b32797eb6a2d6a069f064dd8c67ff39017d0e09cf95c1c9018a322a4d9c2576dd4c3a677c10f2fa4eb72034d5cea9086b831a6a576a6e55358446d6c7c9f6b8d8e96f76d3161f194296e54b5389cf0116a73536b3e17988070147c7ce9d6e0bf47edce8e012c06d4071829aa9717c417214c35f14b935bd6c5038da2a4e3527cab6775b8f38863800ed5a394a587476920b2ce506a4a6754bdc72f7419187fe39b387464306ed4e01fcfe5f95bd42868c87e0b79e0f57d8829775073643da268b42be0193f65f4f64e0551d6a103882773b8cf4d10fe30844c3e808dfa19b251e6139174a0dd21efb9002583af7326af86accbd383694fa7a042e5eba999cc9a37f6012b8fc56f3f6b94015a6984a8f7f0ccc6255d3416c98a405759f2c19f7201607f8be09506317d8e6c8d265d29ce1199e7c9f50a78458267a16fbdd6e6ab23857ce3350e2431b5923132fb548de9d8c105d33873ad208a4ce4f426e401f4960f0bba0f2eafd3fee744e0b5ebaf7d914af2a858247b602ead370095f4fa9deb864de8d480d91f6e2cf1914f8b7af325b3cb6797b832e3367a5c7ced907b6fa943c4612eaa90d9a4e53e347cfcde3d5cab1327af113f96d3e7382370d0b715d6c72cf3fafe3accaecb15eb68b7cdb343bc5d3d8e774d0fb1fd388d5ccb4dfc7dc6b225a39d71e4bd36cd65430a84a11022588f5eef3c94e1d4d9e1c0d70420d2bb068bbea55e3f12c5c9e406ab2f4f77cb65020e268cf063e981d80112aa381e199028e6e3c3232bbe3469eb9bd6fdc79e46f40b56f90fa50d5dfe3364e81689a7a02ac5f378be6d7c907fb341bd6d6b2d0da3d692542b4a28736275af452de09520ee40c0d4304062a14c9476f4b62d70953fd7fb4c96346a78b367862051190f0a5354580fdd00156120fe105be8812e16875de26e97c0db2b684786769da83ba4b89b99efa5bb09d3630311a964b6c470b101a768f1363a09d4de7afc2939c4856d83db4eb1e5788ab2271fdb411f93cab144edae962044b011d5495529b3b4d6189b60221e87f5a658b9384598bf10bc7ea7d80dacc92d8ac1150dc321900924b509598b0e4226db3e6cd9209257fd88d4ad3a600e7737c0f2facca24a5bf2fcb80426f6a88ab953830d3ef0620979fac83938c677d06d2618baec27739a779545b0939bbe2947963848b10ff00312032a7f9a84fe1818343dfaa1f66d1c09cf9e4ef2cf8bd9c27199261e52d8aa12a0cb879ae89cc907b2c85ddc6507cc4e9e36c7e8e33cd5133abee00e69d3438e9d0cb170a9d2cb3821a95ace2e3508664ef4d3d3e51c74a281a56f97108996798cd8975f472b86c6feae63d378c58d284f3e0f19c2543c0d116d201713c653a0b32b5841a3907b8d332c4ebd8a82f03e21f0edade3ada5b35fd350af1822b82d468475cbd339a12ec6f2d29d00d0a2a74929b23f76c56bfe7a5182130604e16a226aa8febba193b3a609c7a0644773b67060707f1ed88a426af25969b230c8e4ed78042b04912e99ffc51e02471733c0dc3edc88f8d38f0d1c42486da5de1c1b9e4eba3344917406369976cd5902ea64daad6f403bef0a8dee8d3d3e38968a23f893c288c9f6191b2bd871c0094a0d0fd4b5a1f791037daf1896d37273da38751be5dc7b4c8de159c6537a5a7f495cff20ae4fd0dbe3d1d37b290a4f22cd0a7857417a56c4830468e3c7013eec66e42236e4a36b9f3b7257b5d459d27462f85644b505a872766810fa8df97c2546971acb5d4e5057e6711f5e4806c164599557df7b3913dd19832af95c98ed253f11ca5351ce7c744670065341fdf578605b1f316bada496f712ede58c54bcce9a065ad44471234a10fc034d280dfd9f10f25b2422da3b6e0be98a09617568ace04a55f780a4c76549c4c50c86193f63f346f993f5490971eedaa0952505d16768e2cf25f5b823f4a6034d7ca6b7aac54de41b981707b4b9e601197829a768c0fdd8a0d6b04542f5d6416a7d920fcf551a9eb70a1a97b03385ec063407aeb7441f42009c3b394017b7601981c61848c2ce454b59342461ee29de7e606ef5ed029b4c9d5df26f528b44375b5e7cc5edbd09026dd1a120ca7d051a0db197a743170e71493296426dd015ac718e3ffa511f3c0dbf64cf573e83ea5396dc7a830b2fabbd2308015db59b723753c0e460022e8a83b10eedfd7f2206c97146ab1669fee7f4c2302e4e5549b2cfc692f3585234394b90f92f6cba10f89c1578a083da91ea6edb93e3b6386bad5c287fe2907c5c3dfe29cd2237c39cf9f16e775d5b1292e54d1c4f934749e714d46f9493d4608bfa51d5f637dd585765a83957f263398cafe96b2c821e5b5a116a8bb4071c512dac3def201864b6d58a7f681e9e740108be36613ccdba07868a6088956bcb5dac7281f538daa0241a5d35e1b51c77c77389496af874357c438863b3fdb66b83439a2c41c26155b66a277ff6062db4bc39d6c6ddc4210652e5dc38e7c45b2b3eb75ac8099dd25ace0526c78462200168d73f535a7ab0cfd8642571796a6c2e2b03fc87f42461b16faad612624b8eae741a6b71ff990f994b201b391de9f2faa07b1cb6d05e18dbb67c8019c50de0c45ab111b417515e046c1060f126a1f298d1ededd01dfdedfeaebf35528bab27b4dbd1deb3737a963c2ca0f776a4f6ab38ebe20c4066c1ff6d236d3c8a45bd87d81c857b5d6b1ff22af56333ced494bccc3e4f31e9396daf10939988c27a5883c2257884f2a796513c999e3e658549051f64198299b76d83841d91156465e0f0bed00b087daf6e1a3dea20e28c594fe8f2fc28e9918022930308749ef74717ad88c14893936c539c261279b440d81ea91753368532a28dae57399f785431de9e4b460939a521f24ef9d67eef6a32628baa7e912ca666a6707ed6d707ed5b525a0fb81bdad0d657731518b87961385bccb7984db78973be3d2564dda2729101d6a6488956f8c77425f6bcdff84532b2bc5b6a7995c165b0465d6dcf14e8b85c920b21a54fb4e44e40398017eee18925aad08250bca271be88e86cbd9fdae63ac3cb2026ce0489948fd8e1d59db9b478740b77484e2e71ee24c4cdde3eacb63eeca5ae3eebe54bdc7b5193d76884c6299afafc4bfd4299e42169dbcce3d0bd7428a024cb88ab2aba44e0f685a042afdd9bab49b13f1d8b7b32e7a632172f31ff4f5f8981bbbd7c3828045b9bcab4eea84fc9972c19b52a60d09899335ce7346837b872ac33f786be1192c3f889d3f3c54f5858e37f7af1efff3d3ac1350f247a98e15f23ac038b386c6fffe112239f62c59c198313f9bd6faba3c0afee750df9d6435488dcda351c6c2ac538a7aa38655be1136ce7638d2e23d63f94f4764bec9b415a74843fb75fe7312a3d47fbb081601313d06781bfff3f32f931386e61d9dea9dd0b8ae5829756da6e1653c8fa1822e38195fe539cf09321abfda5274b6feb786ee05fa7f1dff1d3987aeffed199a6e3f5d640b8f2a241e494c0ea6ea3397653f2a3ac62cdd25235f9438679db502b8914452e0fe309a47ae276d0ef2482ab3ee4e62ee0aa03b6c3fd3429d432e940fcae37cb50cc388e3be4122e3e391b73d332dfbf43cfa2b684520c1ed5281baab37b5403604fd279a11908b2b4da11901fb00961d8574f80de45ee70dfc0a17b322d956c51b26c817c6189e2313885c58324c0d9c21a88cd0fa86cc2e735501ed4d6b1987903870bca88dbda36bbc660822468d9ca8121d1d8c96bdebe726dcd688804cb40293e613ed9d47d79807cd80f21e6fb82a7320ff9501de305f1d71a977a12497012de5800e37b72ea5c238ce2d0088061ddcca8599649f6075d9ac829c3ef7c1ecabf3d8fff61b31ad156fa104a66224319dd02141dd3a63c3f45063278dc0294a83dad22282d0a4c99fea37984d7de4d1a63f675dcfbd901542c303a52f252523b00017fc041950926ac9bd6eb1c0dfd92f56aec8c1649bc0dfa59f91bd3bf2f0ef981d32d5993ecec14cf20dc6008d56e6012ad74a80fe49e3c1f43205a74eb9178fd64378adcfeedc84d32650193ad2592150ee163b30a3ab770231ee594fa97556f9fea6e9b2688c8b8c739b2c4b9945314b55228b9a9bd67348fe89117c913ba6d7a3ff1250a69237e365fc138c7e97c7102effee39daef0a61614004b16eb6f9fab56b849b08cbe3f80ae88f55c9ead11fa291a50ef6923a485d8684a99eeb89f927cae21dc5d91e42422c688299acd7af34515709378f62229958e1d2e7b2ceefff24d92693689fe16a9b09854eacb6de8897ccb10d00ed1741a954fa3ff3a99dc0b3793b25a3f388378d0aa8d3f9dcc979e184f83ee82386f404afe40df190951cd940ea9c5d98e23b9bd73e7df851210ff2d7d5bc600c9278ca85417c5be2f5a79c8d2af444c4846e379dae5f7e03828ce3f31f54db35be7645ee609cd0c869b2bb5513c8efc4dc6b366e2afc926c967b19b6b44206a0b618bb17da1218eb4e18b1ec2cfa819426033cbb06e7f7fa51f0f9f9264fcaca0578cfd3f1d4e022854c908ca0e160b5af82c4de51feae0fd9a5acdba80a844e7e99c7dd0c718dae7d6dec5752855ad901c19177f4cfbbcfa978f9c22c79e7e352f5479d7d929d6ff816e34fedd55e842f7700b53e2d8d7ea94e9d8d42a79fcf3e29e25fa2ecc4544a7bca73c76d3837ac51a34f46fd92346ab450c31380ebe41141c2632707142550d26687a27891c8d10b4c63eb776b1cf7203c61605593b2b09790558f177f16447d4c51a51ee737b2ed2168171ca993bcd53123d673177997a382040286913e5d5aee727c387c37ddf5f6bd30fb67739362fe79a8fe98a54f57832810547457b9fc83c93a492e32c644cffe1abb36a920f56d23c3c54f2dab05d8cad6868cb95fa7bf6efcd00266bfd0fbecdb77ad1b0fc1a4fc6019890dbc74ef66a62872c67339b189bf43169ad1d422fd6f4f8b49e65f4e00dc30c02e97759a582f806ad7b0f9b42ed99c4b346a637a67c7b74558d2f3c60b5e4b386049ad6363aa0ded089a58ea844091a12925e1291c6d8d10abd07c777ed5462af07753a17a30068a42a6e194b039cae4c37767c308c4cdf05b5941f55c5e6decc9bdd5bf02f9754e211063d88c6e28d58e551ac0725c3f474b406f129a95f8621b409c85e7904b2d9963ef8cb59c1809f65da3b5a147cee2a2d877f85e4e4ecd509b0502ff5268349c8faad0ffe8fdedf85a83f53f29252634131094a15bd2ba43a4e43ad6eacc966f32fa0a610809b6e16443e1548285f32fe749fee1a3224c4c1bb295c7f94eaaa4870f1b85f87d51a5a0d8f2df7b4b17e74c6373bb087fc1cc1be037eb9e1ddfa8ada4945c517cb732b102f4bbaf1a53dc09570fb755798048bc2ad77a9154db53fe3a1f183351edfc47016820ea1244727351eb397fb97e4da091f59b117f08a9f8da116191d64f768431ab0c2af7091d8846959d6779c789a4af431f6426b3a109faa7a5043da7995efe15072905435fb990d7269c44b82e46821500b7bb68db0e65b6412404f10af9e691a8c41d23c6f5103cd7ce15aed1a75a5ef6e5c34935b5c3bedcbd95ec1ffc9e41cb0f9d6931614499d828f8d7cbba219af62f6983d9d7ab07c4d63ab21d54ed3b6b49fbfa8360526cb62b93f48cce5c0d0677698b3d2f8242820ae84789739709d34a39144b794967105d37cd224fb667ec3a90844fedf9c5a20ef2b8efcc19708f46f1fef6e3af7f254298411b141b9cf11aeb36216c577cb6b9a450e5cfe3ed181a302e8f7f3706d2470b5be068a346640218fe28f16118ccff8468c6dc692d53b9009a13f1b4c85ef67ca5de46666bdb5b3c8b55794faf4498c9a52e37e915da7f3eb178b3c32e8d39094b8f682ab5b5f7ad4c7bccf3ab5837d6065220a2f65465f3b80532f9bc940168d3b9262d33df290a10a9c485ba4899730e9d9e30fac8d996ea633dcbcbf34acbd8c3057b7427e80778d8f5e1293a317f1906a0d0e92d9e2abb2f10b016a93deed5b06e007c761fc50e0b270ee812daa456540f2799d05d7e0a6cb26db43073f597b156b49c02fb5aaf34eef3be47a8eea33414ae3e835438fc6575d88e483d46f5bcd6deb968855cbe05c5d7df542af0cb040e286b343286479e904bbb22b850edb8646b01b4adaf026cba45429f104795daf5392d83c341e8f755be4d6729eec279673533196a4dad03d21e2b6864013b52185489051ef57fb3472fc43b38e7764ad61a6d9b7cdd17322845ae26e49fe2df04b71df0a9fb00ee05fc30f0c4d66c0384d95babac7ce029170a6f27ff1ac6fa45f93a02974ba3238d9799ba869f1560db4a327ecf9f85b988c774a23a022feba5b9f334b3dc3ad1a26bf7460dcbe684277d6600847c605271baa2dbb2c1c738eb179921963a1c55adeed2863b57aa52ccad93bfca4fef0b47b0432ce37aeedcaaf389a3dae7d32ad84dae052055d1ce3e271dadf1de54789bc4a2aa9256f8e26e57e1818374567fccf5f7ffe46ac10eb9571597c6f0939a58c9db6445e98b74c046beb7ca4bf327be598b957a357d6253636b5fe52386db3aaafd22acb112684dc0069fb27428d22f680bbb17e166b97e8e534ad64de15054a8f6b6fd3e2319c18d15e8269970cf2544f72a50489c80216ec18f57b0840a0ad9747e3b7a24c828384801abf7e836e084ce7d836cf32fc8f51faea0998bf2ea7a993dfd65bc12298a87db87c658ec637ebd19e321d55a35c5a6dd87e996dc7c96ff812597071f32402d5ca27e768c32ec92b812941742de049c390fd5a6842c258a6328808908c7a7ed19cb411778bc3331f5656c08c380158975441fcab7e030fbb1e91b9de47a8b872d9f8f8ebc0edd6dc0e1e2c77e61f449748f782ab0f3ebf4c661549c2e77171a81338f43140b58c8c90f4b35e38e2e13095a07f299a4ec34567013df00928650f635ea80facf61a544bd6f1c962854bab9617d9e155664ed466b03d3c6c599cdf99628a21b6a0fbdf82a9756a6aff1a48754ecd9aafe10b8bb65ed8c37a8677ae36220548cb9e2dc18683901bb17a778623a8b554605ce1fedc8651de9768fb685527c85d39e321933cba3eef0c4b59de3d978ff25af3154630c959c46d5e4211306b2bf81a48c7cddca51a022da5f97309bb7f25d15e95d25818caa87b692acea08514d5b4f6f346b3cbac8a7d554b9fa22454ec8c6c4a23ff173e39905a48545a4f29421a5cc2e313032547a21770435f03a98fe34c615f5f6d259bdb3d4209f5edc95db9793f84c1da39a5882ad7481a06341c5b02629c6b3fdcdc44a0fa049a8eb908991c2627e6128e8abbfedb3599b8bb246f961d6887c379ac7b91356b41e776888069e35f71d42fc1c9bff4a0e628463691bf060ecde44a1e8c3ee99f60cca44010c90188ca5e06b5a72b00969ee7b63f60b592503a623011230208e0b54b30311c8c028a0503955ebe73696887ddd17740e340187b682715e0acce9924729458223b7d1ab234e682f14242a12ddb3f74bce7d37e564bfe60c68616b6c965ee70ee56c4c912e0fe1fe21cc3d6c84cb4fc8ee08b2c9568fcf9c0ef0c0862bc48ec900585ec450e5aa7b693821c2830e41cd0713ed90da3c88080af127cc9fadc372d602130e82516d718839e36712da3518b788ada688858f530b9d1c6a03e66d9a750cf66f2fffd121d4a28bbb106fcd85e5edeeb8898f3ed908c30ba848cd7c0e33aefa86ae8850896f85dfb3921cc12e92a93d4b61438f9c11ccd8449c4a158abaabf5d09ce999fb29c67465d24d56df1929ec5f76e739105cd75f1682cd86b60a53989a398e52ded20398e7426d030a6ff961596933e13103e50646a0ce68587f0a941ad321eb4c4767ce1b94cfa664cffc636ca7b32fc8431fa774146e0eee43b45b68347d2211d5c94f8341a1a96a1a023954372c5bf23910833527d98f2e474890a8911709e8eac9718bb7d45496c54129473aa188a951d875baa6f75f45f3e43ae6eb0cb7d0f76c0d5b2342720c627e08adb41ffb93a1d35e85506d6ad30cf9b02caf04125fdf6875465c3036719dc02f4a84c4b1ac211ad441795745040e0a20685ca0b71585c5a2a45c3e82b3bbfae88318b0f72b2c4fd8558e16d12b37188cc04a8d9fe17e8905a70f554533e5b3f9bba337387c43f21404062b876baebb402b877ed9ddd10a7eb97a3d61e018cf7cf15ea444358f8537d59a7770796c56c3ba53f3a530fb1ba0b0ac47e295309196b91c556b7ae3d68cb82b27256c977aa7971a7ed7e2651d0964960a50923e3400516c98d50d958733cb8d48263a5a4296021ae86599c269a666ed75978c65033d4c7f6da87931d8d7417eacce986b483966f022be2d650586a69c606c98b14a79c01d95e80b307540be95c219aba33763c5a1837ed693ac9e60d532886b370fdf4173f462885b14e21808f9849bb59cc1d2a3adcfe5d6a2287f4d2e4096f89655fc1e2be9521bbcaf59557173e1ac8500a5dd27b9470634e56dd065ce6fc7209e5c44f5267179060e0e6ca5714e523667700e145740785441fad25df625c9dd272e7ff460076f60fd49ce6d533d54cda5fe67c848bad84119aa0a636aeb979e2f95c6dbf06d935be5e05f36a8cd065652097a182356d21188de7489ff9e9434538511f874dfbe9e0d97a5836c3aaac12fc2b264c73f1073e0b0265c494ab688421967942b405443ee8c04d78735ee2048ba2318852a61dfcd61398065743bcfde51de5ec4442149818746dd6a67a251b96a3c430430a2f7d4afa8d0cd29907168ea9f3c61d7479d454dfe0e4b53c45bb12eec232aaf4c0404174de7773b66581cb867c94408e578eea07d3c13819d3ff1f278f7c10dc13e8d5e4be210456eafe0a67d2f28f21705cc42c4da8d580e8cac285cc54344f879d87b4d64600642375f2f92ca7a21a761eaf90c4d779fdbb72f9d81e88b46bfc0b87bd6b76df5960c51d71a731f34d8fe1b16d8e15a310b32b7d1c1281508b7e5eba4cc6f01c82405b84d0610e5672ee176d79553739d5213a96911accfc22e9dcf928b7eaf5877a5987da6ff967f9335bb6be1b1e86a59613257d13231d615fa1e51864b1e5ea5cfdceb2fdea2fa195b6e4fbb3374aabd85487e0e55bf4ea12a017ec4b972b27822392340f9cd0ef5ce734454b67ba2fbf2c22920d75894c36cf071351f4912328677989586be3b820a052a04b1d773ce8ce614dc6ae28693b65195f293d65d827bc0e528ebe61e64949183d8c6853f369f36a5f980074f13be94b08a9715ebddab7f2e8029d4cb047fc6882f2057f937e3e9672b29e2a2d87ca8b025c6d526b57b277da7326fe0bdc3a5787c715265ea7c14f6d54bd3972a1aa0f988f0545624efefa1750301983922ee4ee5d7648016bfb4ca5323c87752002ed562b90a7067dbb9cac71317c4cbbc23bc22d0233a480435834fd9ff9c493b6d4c47b285a1eb2ac50e8e182173bedfd83a9cf84abcc22e70a31a44e2550ea0d027f4902e312c35d7adb1814167f882493310d20d1a900a1b3fead6253f2f0d8e6cb7b288eab8d04ba395cf0fa17c2d9e6cfd17574fc08ccdd9480186dfef4c6fdf9bdd269717516650d608397d52d4ca4eb82cf026e86026c107ea6041e59ada330bb5718725664e4cfad714c962c0561900f53150229302f1f7e53b9158387b755e98d35943a17fd2f79a1089e9afe3253095f99b05498aa914938b6a5101b55292ba73799803fb1656afe6afb4b6eaa93c1a886dc4934210305e813e35ef38ac7c3e6522af0088e8d97697c64a5bef23c154402b5c5e4a1e4f23f79422b24821016148a4cdfec69c4a006228511ba53dde4636d0412ba501531863c0213bc80dab36403d30b9282eaeee58a2bc71452761ffc8bcd3002b1d7c54ab932cc48322155b041d168da13a1f160b1d6bb3a33305f0e99483b5d8f59ee4db05b0bfdf46f66b6dffb3a8f540cee15c3509b77a68b0d4315dcc886869e2611a92d30e95e5d933ef01498eb40144561770ff8bec15ac6474806633310b7d54c34cdb2a21dbf53bcd33b04b9da55ff1877423c9d2751a014fa062ad7b9e686bcf8fb3b7b5b3207181b560cc9c6930123fef4251ee5304acd59cd3c9a4137a9afee5a8962c54d3fa1c3ff291109a144e654ee2ac7a3713776c48a7452622d6624a31b74f4de0c394e7df595687f7764eca6d22b362a01e2543d2221107d66bae23afc12f29a7445e044f05d42d2cf0fdc51c8d92b060b2a8475e2cf45c541568accbab9e1a196b4e5d689edcd3a50d6a665c9a871a4b9790ac1f55e445b52179512ca8269712b9bb64023f4d41ac295cb23a62d6435bbdbbf68082dba92d0444037be9280473dd2ed2232376c11eeea9eb2ac18d12cd6c394a4667394dcdbc09336661f5450d2ff145086ecb6e1fbef027c1000d2855e2b824ac602eb0d65acea27e20afc9b7b51cec30f6010d79653ea2aa5753cf9c6c1d4f77b2cf9899884d45613eb424068d5d5e671dde9117b5bc055247586decee2a915eb411ed8beaf4ed101ee06e132ab8823c0d684fafd3e632e476a577db8c95f960867060e7e7f3a6a9671d12e9717b6617025cfe69bdae9b83941ef6e745cfb74373e31f99028cdaa9858da491826873d35ff20213e306345399572f80f7593115387cad1e45285caaae8017b21a5c5a0ca182e7ba26e2b76a167c2965056667c9625cf175d1ea707e8b395dfb647199fd9725cf34b0179b6476f198c1aa768fd798b64fd9d0d7d28c11f688c59c489a7408de74d899b5892e6e7098f281c71f6d86e55f102f537be746439cc34d5ad3019b7c70bd33975f44b333d49a14934dba2c6a5a5c14f98db83d28c3fc5122a37a063d0147f50813f38adf603e072256e008340d97082763ab81d982c3c19a134a0159562aeb41ab2c3ad9ee3cbeb924de79cf6204164ac605fc66b3340555440c03baf27d88a230adb2c120268a62a766dff2ee00e19fd42103a85a8aba0baf30e22ed961ad2c040aec48949b8b59bfcdd494f15b032be6b8f6059769a7673e54a011645e4aa64a7895b49792e1f379f7bd5d492b31a7397944221d02239ec911c6d68752900db5f183bb1bec1acf9006c0bf44bf7125a96142c328257efcf3e2dc1eab6f76cc0cf13efdb93666b89cad0470ae263d75264b8f073a7caee847783eec8d98433d25b77e79074c5c7092465b0fda46c943f397437c362dbe12fe46a1add026c01c57d5c510abe9d488f20ac4753e20f76914351ddce3b12026068dd9b3a42549bdd640e0338290b217155b64700c75776cd07bf3dba8979780b6e672f4b60932d6ee8822a41c81e477c4e8100447f28e4b89a44e3ba9846496fb95c526e8603ae837a636feba7bec7e67c453fe124ff4148fff402582435e64a9148c56eec12e42c82bfae3296aa6ad22b04b3f3ee7e855cf136babf824d254b117e40c7b91dc449fe20016eacb9ecad08fa251af7497b2cfcf3cbdd41e9d95fc411cde10813b80fc8a45ed4544949ec7fa7555d4d255fe8a17480be746515c932bded288c7d8e2636f9c556d85227c8823a31d4c3d44b61cdf3077295ee6424ba0619d34c1b50283247e8a8f126c0ed6bfbec23c6a818b07742d638cb6becc7d8063ce7b929c8a37cc7f56089b79bb9e7ab637c394e2411b54f91a89701dee08ec0408c8bbe537b3e3e8cc0f4205b66c0564601a3beca0f20553e0dd17fd6f4c0cddb3d1b57926251a7c3fc2ba9041d45d4b758b5966a9676bb7673dc256af47add5dc38c69350fc7b72bb885f28f47c94ffcfa0d62e708bb0a5b5f7f9971f8b6499693eb3f394e5b6b4bbbbffa9b70c94f2f7466fe0bbdb67595f625016b9fa20463396d72194b79336c48734df647fee6cd822ba8a4208856e393a0c0658099dc67b66fabd09b67864145aaaa0fd44c5858999a4f1e597ef9898f5ef0a2523492a1ad9654f72d80d7ac02c341695018c3cf6d0e9f1c31202bed8ec5b97e485a8b2a165f29fdf37374f652968de3f175a82020a9e5ea40d99e71c2298e335a610fe858ac6ce6e8f7242c6fbe9d94e8eb02443500c6b6aedd046b679d1de75b8886de18150214b8cdb1e523ab9c3f53371aeed019b89e700440062a5b40f757b3ed63b6e6a4be61f539b036b45e849ab353403d78171d2f07e8da204cf63f07b835791b2434970214b5b4e243e1ac4bfb62c0668729b2a165ed20bd1021c25192faa164e1e3a8e28c749a0a7efd41b01eb527d2eecb5b640eced7bd2282649ec0010c75f878d39ef15c32104ca5509d05e0fce968f4450fa444e66e27a796a663719355e3ed15d0fef4441aef3ea8b8bd64e5d063cc56cb99a2460d6820f4cd760c3922d357d2bca5dccb303204d0e46ee32488ab8039825690f595427215ec79295c88a79f8bd8150d5112dbd06669f137b3f73dd95eecca8c210df9b88b41df00df3e0c59783c3e511fa2121e337cc91c55a3403dacfc78dd5f10e3aa1fba52f0156283b2bf8637c757c164bcc2c70de02f7d6da010617e1c18aec93aa93248e96ff876ca4dc88b7acc2560448d1fb5419a6a7b32764502fe41e61be0c36d70f82e9b15b4e4e300fa2a844020a14b6cc30f944817c979ab064f6b4ec83ea0a4c6c84cc99ce2d3902001c27f82c408a113b5803dfbddcec7e3fa206728c81765774106e0a47d4e63f5acb1a948615601d533cee8255e9e0a02146a7b13f9cb7b9420008a0992c932bcb97287c4c07461b270ba7efa003162025ad2100c21d69e8d1ef2a1efe0b113287867d77e086f536d45fed826f3578f3c8b49581b941cd5259afd0b6618f6c4f424f582004bb7d05f1d129ab0e68a49ca5d50691dff70ec66a43f9d04487753bf9d28cf5d90a5ea328aa1555bc5679d40ae7594e7093a5ba07cb3aad9020369b4661ab7e0bb24b03bece845f0b153538ed824b3be742184f9e9a97e16f52de5d3a54af1574ffc34b27d96de115f72031ba014483029acc6d6159304a71261d1ebc6a75e5aa42bd786946f1e19f7f4820afb2ebc6e25e765757b252c115e5f2a12b8a0de98fccffccef0f0e0eeff4ea70d4e6b9ad22616e0b58d91671d431517f903254fd120b69846c42f6de646fb9b79449caca069805c6052cecd052e1c98b0a303e8bcf9018725862b9ddd70a641e460bcfe6ccfd482197e6c96341e1bf10c94f3c5280ca6125060f2b4c2dc870c691e185dc53570ec897b7a3258a951c15e5c12cc945299263f8c82ca29e0b968c9c15d68a1c123c19c7d8d761930b3b23c685a7c245ca959c8d2939344c9ecfd40b01b245028d1466e4a652689173a3e789ccf916dce450f0cab5099355b09099b0bc53952a554a4ec84d0952828e3f6157230765f7227766861fa1cef708f35223644c6e0d578e05ad07dbbd1a27b91d497251439e4a407365c3ca5553903ebfc50e133859603f55068797dcd7566e0c2b9700e54b5092bb63020c65d8e37d94e10daa2a32726caab0c8f9d0f35774fe4e580c5fb9276572337cc92eb63c922b8f0295aa2d48bec990393c7260377260a8c8f817a8b0781e7ae0d0c9c509cb39f9aa2ab3e48b8b2d2aaedca012a7ea8d20f132a40e8f2773836b8a8c9c95292c724cf4bc159dbc222cd3f84a53e6e97c791d5b72265cd143e58754e5f0205961080c3c72356eb49122c3861416527a6ed0f9166139c7571ea1cc9e2f53b69cb9f22ba87c96aa9c0a48726f86b0e051e686581419df250a8b0fd393d3422749d8bff92a53c6cc975c972db9315780509152f53590fc93214e3c722edcc84d50643c1c282ce6e8c93be8fc95b0dffaca852943c217154a708b9c12bcf2325409e224a7a56404466290caa9d93102283332b649bd5e3c6961dd5aeb64cfb67fdd5a9d6e0c832fa755969ddc1402b85cd8e962260a0b221be01a33e6c79616b2ec2172a768d80ba8b3da0511a9d00d77c371b8e138dc701ca8e6426db5371ca5e2f5f2fda85b0f8fd743cf9248f6ca3ca44f14c9100198a1b40fba4ec4a3d5099bd6af54462289f6afa654994b7947b0ba17995cb48e1a403f0c7de07768cbcd431eb8958ded4cefeefe58564e36ac9a1e4f559f6d0ee12863d826c53a1251cf2a32555cdbfc1815a41774cfdefbb5d65a715e6badda2669120746986606d8c2136e92e56b82f5c26b62e5749b4c39d9263d9c9a3c35617a926d7a3689c148c8640e0d92091b1923133323884cc0608cf191d2a458744a13975f94634fdff7820c0f638c31c6b91e250e634c35e57452d910f2714372d353499a1913933995091b334cc06491561e9193898e8f491313264c3526be10d6c0c8b2a8aeae7852598de3a6271549a5e3a4b2b14751168233cbe3c81d211f5818638c857cdc257da891d14eca880518cac6deb58e7524e25328a3b6899333639b942b9767c98b9b4f284dcaf541054e254ae2885531554df98c3288feb8f10ea29a520cce25629b9e4beeecfad48273491798ac6d8255363d9724d9e6782e21b2648705e894911190352110394640b6422f372b46408014018128f9c12a00012203c88d7d7ff6350202969b9e535d58424253609aa6bab8983a61f814941b5ed57e5cd6bbded3a0fa19a68edc0fb7e93965c3d3787dc075ed3dfaa60ff8dcf8a0025c12f8800bcbc98898970ad5c71e1f23e423f62b868ceb4698e504fe8ff2dbf434b243c1460f1b514d355cc478711a5901e32cc2a7c56f7a1639b36d9b9e45b864c95e1c9f7c3023d1ada318e828066316f290839090e3702546a912ff4702eba4cfb15b2db1ab4d8bede711aa0fe0b6126efb52ad21b888f5ba2055b679c3f987ba8190507db7b24d5005b16e7aa5f6ec3ba7541d0a889d5377eed6f40162fd34a5094a756df3ee320322dd669e53d67af738b64de07dc246d2b0f6898facdbed264f7cbb7e08d56b9ff819d52084cab44d3060d76762da25d3aeef4d44ea598890d0907b857290a1245feee0f87211640c57f141258531318048c15104031603f8494662947b18dd720fa12dbb36bbf26a0e01aeb0763b76418656204ab24b61015a80a860f063776567b741183b25541589618d113963b64ee0a2a5a4c98a858d31152d5a068d8f1d03ae60bce8b3b3b19be1043876a882e008a38207c82ca9e1e5048b963a3fd8e0295a2e7821cfa568c2213b4fc72e01b50390d981600cd7d3140d2167dad8ea204943078e0f34555c602959ce9dcd827c55c1553563802b26bb2c3bea45861d0c76147025c36ece4e0057576b84ae6856bd0a23664f2914b2ec46d879e9ab29bbaa1e3b2ac7851918293bdd95921d941ded0a869de7033fd1e254ce126ee8c10243943555b2b0b88911e3878fa91c38ca5977d44b6b87633700dc95102c1aaeea1bac154640e35430caa5f2d98da9692819a65d98fb9ca97273660a0b2933ae5e304106254188e491d282a16889512d16f4ec64ec8caeea131d2bc0950457580b6c0f6d81cd8e0d88ab9a55e6fb42b75ac0f88913764286d2d96db8128118148b3b7886644591da3105069e1f585444fdc083adb4c65417557633ec62d855d958abc9ae6ed9650080ab0e575a7670dca8a901153b0e04c1300cfda910f626cff32e8e85277f721adee434fc494a1643a6de2b2398120f98364ee304e38594d5a5abcbb9eb0ec130c7292b5218ea5a350ad78560ce68ca1a443f7c1a441f7f107d2f88861794c974f825709b3ead5f5fac1945ce6d4ad9d3ffbec4b84123cfe8a31587e20d92e90ebc2d7b99d6b5a95e61deac51c0b082a9626a578df2dabc40bcd11b8ee348fe38ce7e1cf1f895aabce1bc7f212f920dd90fb4f7be1a25e0287320c2035aae64883ca6bc6f96640e7f1c35e5c01b31ae465e240b8bc2f73e3204f87563310fa53e81799f526fc1697bd49194f46ca1c70e3f7ceff17be9b1bdc7615941845037388264885babf574162b4dfc1fce5e25686561dd38d75d520dc27c77e94521f856f22d7a98bd5dd6681148865f49a4a0508fa84123aeb7eff63dc237098c317e509bf83be11b8780edc5ba52ddf79756eb7766c26eac29c5e0d3d9abbc1e1e33fae7516badfdacbdf6c391d5f4c87522e8db274175927d0e5ce75584ebbc4afa1c88b8d65a4b87b21ab3bc1deeb576fb1cb1e85eeb2068ad4abe37a545de839eb594525a9d0f0a2f024f2ea7b38b180e777689b2e9d965ea64410f7e145ef43daee274288888d457905a41077d318337bac771ac4fe651f4b21fee2aed0b7937591ecb94e6bf90ce8bc0bf7f64c907d04a8aa80fa03bc87ed27f5e7d865ff4eaf5193eb980b9f7dc22b6ef052f8984bdfa4c5665fa82a3f769119d662e799ee7798ff187b59a499eb65fa283d68bfcd26cadf583c24f0a956abe5f7d9bb7380d918e3583ed718363edb34dd43e2f6613b745529c6524ba6711d0b3bf3fd3a6f8f53dd9f7b2ef67da8a1f3e9965bf711bfc96c4ea793438d69dc99e6ef2fbbecffb9ce49ff87ddee79122751a6425495127d59124499cbf267732fdc95d362391684f929f3f9164884a7aa63d4892ee447a8ffd3dce49b51761572fdd9d3c8a2c75d2f835ec342ad057d3f71e9ff677d32658827aac8d5f7bb3c47750fb7450edbd27dfb5598d6c3f4e1fc6a6929bfeb4499244d2f055279995fc9f07b2fdadbef7a47f2289645619496224f567d326189221c4daf5ea4f7b2fd7328bb4ec7958d7ffde2375add49bec87c7332dce7e36d3f5c7b71f8c6c87fb6569e4342a888f7cefc507496d3e36fa7b491adf8f2fbe3789e27f6408516694801274ddfd28d78f9dadacf3d4d2620bcc3ea36825e9dae3067a27a3b610d122a6a58a96282d5cb6b8eb0866694f436f1a4730ecaec7f2abcc9bc611cc8b643784ec3a95e7d2ba36afcc1ffb6399973fd6b21fc99c04f4226ca73feef5871cd55410f8ab9293daf40db46760bbda6622f6eaee5827cd1ee81d7fb5fa7b5375731cdbe6f739c96556e64da35705217b726c9354df65e47fb2116c7f8fb1c6d85a6c2d9e692cb3618cfd4977203daa9179d0edbfc710a8ef4da38c24c7116ce32f4b20d2acd87eed9dca7eb01ade2c6df50dd8652489e49f24d3f847dfe058833e4b3c90378d51bb66a7ca6ee5515c67dfa8acef9fec3189e45ffe97ebcbf2f73f792cb3f8364b13cd1295566b257512f8b2a78d6abeeaf48db377dc1ec959254972a6bdd21e0785ff3d194525a37be81ed39d36a90380a72c5102b7f17f3f6259899936dd697fa31a528f23fede9bc611cca9dccaa94635f6a76ec03346bd40b000fdf081b5da1cf70df302210359561f798170012f2ae14538f7e04577d7049c5a50db6e7a6ad5d87db45e6c53f74a66c85da993db13f626a8f93c63725d5c39325a5ebb0cc3c9c748322457a484ac238b8dffcad2c4a54f4ecc4eee0b9c67020b53e6052fef642bd7072b57056587922e135e4f8fa7ba91338265841c1f2c2a3ec99de792935baca162c60d981c912d24a8f0635266509d6044cc8fdc121c5ab29cfecc621195e7060c0fd52697054dee6a045f6405a8ac9c1495cca3499e71240725e5428e2a5768acb9e2e2a1ecf91c74b0bcc91d4993b3232693d025d7c8b2bbfa315632906419423e9549ea8e0e3a2662c693ed40377dd22135857594e97736d60d39298d44540027e088030f3ef022eff14bc08bf0e3fa596bed9783465cfc47ae8d94f84100c1b2a47604ee5ff728bff72c53cd276012edeb134ab344a1a34a93d6f96a937e529aafa5cafa9e27937d9f07be97bf073dd9e7895325f58a2285eb64b2dc81dd9f5726143a47e9295c67df3d50f789babfba2b89f508dfdea3f0227ffb293c50b7f79e52097a6b44ea6dceb0aeca65d3363dc3aaec7a8625e1b0e919f6648346586d8ab08b6812340965b03601363ddd3c6d917643fddd1d5eff34a8ab1722af714489679bac2d3e79b6b13ad9b41947dce3827ec36b90a0ac5f8ad9e54b709e6ca28839d770cdde47d09e2cdf4682f6e4d7683f7b40d0cf6c99f6948c40dd4126f934b27c329b29361934a33ddd29667a84ad7c9ade298e2c49fb51f24da3cd949cd44a5ed3a6928fe358be588e63ed4b721c6b5f8ee35823c7b21cc77cd3f48eeefeb2d147d98faf7194e17fa967e48ba41ba67dc1a6671b3049e48b4e4993a41da92fb299ba234d9b4a4efb9f4f52f2fdf3e66cf6970c61c269da04516c0e1f62fc06b5daac34c937817f68237c8f20df699524e1ae695f66dbd7b22547d88e6c2dbb677feaba194d2791a6f8b4dc61a6960ce15d2552ca9a1e9e5751d828d75d771bb53dcf7b7bdd0baa6670355dc2d5b1fca3601ef6ddb499ccdd933e770765dfa7e49eecc537c351d44abe93defc6fd4e25005e12fbba607ff3e4d44873e22f0f3efcbe18b62b997528020c5da4963098275a882f8defc7907c51f4ba20a027cff6ca54d9b6efe6850fc51fc464d5433f0ff82c6f75ef67d9fff9006dfc3366defbde9a1ea24d913d9b4926fdbbb7f9f64f34f9b1e0829189889e81533e843df87ded7b2eccb4cfe91fd886495e685443808fcf0e9c6ff75307bf1ee990e02dffbef3df36ed9939f378a44e2cb9e125510d443982f26aa1978fa9695d6e3ab8a1aacaf3d7b68d3f38bcb564395c604307bdce2dbac177a8081815981398165d9285aca7e0c49d2b350b66ea822948d1ac910960c216a2ae60d4f817e6a2f8ab556f1ddcb8a330a5a0ade1eb9f8476ea12a08fc560211fc9988f39a4d1bb5ed0395b599692d0eeb24f0675fdfb4dabcf5b32f5651b452dbfcc4fa226c2b92e2bb36ad0e5b25f00d6fce28296ed049f6675ac9771d41b0f4700535899bd91729d04abe41ab8366ff7df809829ec388faf6dfbe7b79117ef1ed5305615fd4d6a4206ae3df90cdba373c8d4412dfea19490273785bd004afc1fef758a400d71173d0262e488bda54f226d062d6b6f988031155dcf05fb62fe6ef25c8e4e332ed8f6cdde0e53d08a44db0dc9eaee9127cb32c2d54ad1fe8edd99b24498a20293a58f9382c7590ccfb52f43cd274b04d3e0d6c7fb3231c4e5ba80aa2ceb485aa1978916fa1c8bf6408d2461d71e03acf425109ea0638353e29cfa6e9719f701a166966cce055e762fa7968f6a0717a23d36609a34ba78e14176c3ce5a841bbb8bc0173a6c90b9a351b582f34dbdad38bcdb6169fd9d6da27eca5c52bab8f68d60ada0e6eedbbd5d476a89b856e720ed031d84c1e1c6144f69c2c7c31e12e8aa6098e10246eae7cb9f9ace75ee660867d2f9a11f6bd4722ee7df3b33464852b5c4ce43ce551430205aa27ab1a5736a6d0dc2f8ccb7c19b5c65b55cb3a816a15a962e304ced6961cfae51ce3256777c64e923351e1191fa411fbb999a63dcb23aa39d3c20c94c51c70f75e1d4388cc931d23a0e34290a8ad2e3c4b396874e992c587971a7aeca667979d930f1fa3acc7f3e89b3d4f96c009f325430c0a5069b1a58a880962c2c8549191c952c363ce2eaf2e30535ac55966ea2c8395e424d3c3c848af0041a330a07fee493eba56e1d929a9ebfb794f49bfa71a7c2644286f12500dc0bf4f01d500e3fc6913757fcf00ba93be20fcd13ef6ab76ea0cb3e70c3302addec662b6c5569b9f97fb541ce6141325c6090bcbb37f5d0ca4433bd06dd2448c008c76a0fbd374ca0d9b6ed7c0be1b76d520551d18329c0ca74c1ef37a3d7f95f8e9365f48f705e11e2d4e8cf0b0a1044d05626145ccf00012434d8b2b9bb4dafa02cea1e0edbb296c80a972e2664a861a307898b083079c226da45c58c9e37d01e758fc65d780746a71c4ea8b29d91770ce8b1e2319383a081eaf1c4fda70a5c94d44f860e1603205431571c5ca921c5ee4506111011f6ca8a8c0b362cb4a4da14ac54d643823e7c68a2a604b573882c06871856307151663178f69396651c64b4e8a1d2dc89084cd9d2247ae706911c34347578d231be0a756367e31a516a726594db94a93c5c38690df2a22444c1a245a68b8e0318313022c45985618638565cd04baf4b1f2029c2f184a60c42062b12c6b621823a14b921a5056debc007353034f18212d9ab66815ebb416e0e9f26c18c65b2ec66cbcc56267c32cc803678c5a0a82bf0652f58fd22c2f78b15702b93fcafa633044b8ce62ada30cfa7e697f1e870aea866aadd7875ce717e8bcab03c1b2ac4feb673febe9f246695e6befdf5b49fb62adef45ef3adc42a43e566dbce9d68a246014914db756446df36eba22c6f636dd5ad162d707eb0f1575b654b431522166d32d1551549cf735a0e0d310bf590373ae24d28b137837a94f793fe9fe9bc86deb7547096e5aaffbd88fd5b1daa6e718d598d498d3ae6608b67dbacd14fb49bc1f16d2d9f7cddaa6e7981e300cc3f03ba827eac6f9d222a0a2dbdf1f69d10f2db2fdfd1b2daa15955d7bc4c47695c3655bb0e919278abf9c2adba458387c9aadbcfdd468b71f5bada48910c8d168b457c26dda535ad401da4f8d76b395b71f5bada4d5d97bff987480271b818080805e09b781fe967ff2db722d7f99cbb27c25dc535ad481d790222b65dbdf3c290d37db67a082176507ec075abe877b0f57d6ca9c01d779464643433addbffdbe0217de57e0a2fc9beb97da1c3f7c8f840003fcef5b7244fd3bfe4822898f24d273ec6b2b793b49f6e09b27ea16bfe613b5c4edc096103fc844f52dfa3eb25fd66d2f74f7cfc06d7a9d47878cb04e2e8f7a455e5007db5ca26407d7acfb0406f02fedba4f78dbffc7297051ffd6f16aebbf4de4f67dd0ee28ef573705a9907d225277cf8307a221224a3d7859e3e45e16cadd46ddafdea520546982eee1aab561183e52fda5ede02fed4b5433f0b61cd18e589be1a8cd1aae206c8a3106b35018f4eb7d761cc747f21f7f697f389b5fa98c5a28d7e14fd37ba19c719ee24cc52112472a491ca6fd59eb76bb08db9ad76a73a966e0d65e2f724d9442d7b7d66e6b3970b8edcb07ce9818d81669d5f3b087cfdf2343b8761244f57ab62eedbf33c40ff7a946c1ea24e5efb2a4cdc4f746f23be2408407b26c779969792c6f380e2fa49b400b09ebae3914ef08626fcb3a387137a8410d4ea08794200e0b7911feba6999ee197ebb6b264984daf3be1fdf8bc0ba8110397ba29ac1f8b371f42feb234b92dfec7bba4dd183ebbe233b92e4e85d5c0492b22317bd7cffa6d026ecc641443583bb3d0faef34ce08d1fbc561ce237c9feeb22253dc540dc77f348dcffde7aef3a0fecbdd5a7442da494c96cf8f691c2b7bfb4f3f54b94699f07aea3a48b701d254540006b6aa91652dea71e88d0424a4f68532da4a42fd22bb424563b255221d33281a33e529ab88d2ff8beef7bfa5e4b216a6fb3710033ce75f6695ab47948a4771cf2a2212be628ed9bba7d811ea245b77b3f0425255c498bacb5af84e360b3d96cff3daed6ea791f8292126e7b45fffda54520f821282939fd0dbf2d3b11d77994ebf0ec6bb6d956af8217e5e9ba26fb0104db61ca7ec03d1e3589cb5666c350f6a286ecbf21572ed731b9ee8e8dd2ac5cb5376b9b8d311671f8297530f7b250dfc53372c4b5c75fc5d42e2fd2f0f8ab97b821ef6bb3d9acfbcd6fdfe5e03a7c6f052ec298c0b22c6db67b6f3710fc0bbe197b53b56f4040407eaffffcdc40d8b75b061f28976fbbd984082690e8fe799b8d92216e27eaed6b35a007b2d9fe6613846d03ff96cbaf650fd44c6bd95cdab517edf853ebc0df7b24f732fdb2ed97b64d7fcde0d772a939e85c876d088b629b40b1eb36974ce021d7611324bdc9c46d31ebb6e94d5b0782603f8898d1643bd08d3f9770a8644c3867416434350102000316002020100a074422814090c548468d0f14800b78ae425e4695c845722045410cc42008c3300c000c300418020c32c64887f201849bddeceb4c1ca786ce57b6092a91d09edc814e197466778877cc834b701efceb2289c86aca4ca147cc569d2e87f8a01f127c08cee58d816d014558c6f7725e862508de884d1cfa16fa6610ba047b327d62f150c40861b56844fa32f4576e3e95288d970273e46d11a1d450c492b4e8c687c5cc33e1b1d22426e76ffe2ee21adb498b217691b498e0f5079aba6733f8eef5ea7a2276c792dfd5abb2c5ef471c0d1c9528b93afdf8e2bc6ccc96c94e48ab6d410ae24f22bdc576d28bd5edaa957d6879faa851ea9570b25c685761b37f673d1021ab5bf19e7e6307295f0734c70d41ab16e7ad6b940871e597309776ecf368df5daa799cd39fc5d5cf26c249e40ff484dc0d6b98dcc0e7d29ce0c07729bc1513d4206c03a2e99900c4cdc0c3991dcb6a0ffc75a41cdb94e53bc64e8968baca0f79e868c00c8b4abe7d28d47d7f111f187d19e1409eca60059c9bb093bbe8450d0597039a5c61cf1623345069c20a5c429c60514ab1c1a5581b523f6efcf6c028e6f9d15aedd9782d2b2e3c6b5d640f9379068e28e876768a641b989d3ae147cb4c934c911b1de7912de52152ad470b57214d89d76ae548098630532b9228a58ec71873a3ea5cb7aa5929d4d62dc9daf47de7c704036a7314b574ed884c70f75290ade3b1325f35be291f734fc7521fadcd90127f52e7cfdcd5a9c425024db91d346fcd54b7f70ffaa79d682cdf079526673d093c11c728e203d316fb4cbc89c4526a350cf331166722c40e296a2ccc2e1cece706f95a0f4e444633103f9ac0bfdb75c9dd0675b6e666d7b2ca220f3e3620457183f17d2cbace07d932c9c7924841d91c97432144003502fd98a7b6487e064c917f3b012e5ed9242ec5c8784bfe525cdc8a4ec986228c5b221f481e1b17179c29b40ea9bcfed770d6425fce98f9a0d7796c3bdf9208e629bbcf442fd0bca9bc3286c8bb1f4bcf225af19e48bf4fdf62f550ead943d9ea2a77f6521f9576e98213972cb4828b81b6dabd2c0aabb4066c631cde940f8f022feb4a73ef4eb89789aa703d39f049d03655f679cd7e4c2e523dc910d5adbf48359149eeaad9366ca35f2f3eb6b026b13a9694d34b61f246287fe0de97a4ec6fb35b4077828ea53134a9d386c54a54934f523ca303ccc9db26629202462a31f2a7c3bee6e031bee39abdbff1b7e2ace1bc42e1594181233ec54cdf759471a47e010c5460533a2e73c1cdb4565a3b5af6e60a71a9e81a8702229949a0289fe474ca2184f2c32b7645a135a57e68606b1b29cbea925c545c2a4f8a6d94667dffcadc2e742d30fe373ec44dd16a98579c5f2824c1e3cb4d3670e9b72ec7e21e8ad8daa39b0d66350964a1b24f3ec3a14b50af193895d3ce21cc47d17866ce31d8fcda06b72cfa01710d2113ebca854e8e40b33edf96119160f61c878ac46628a77bbb9be40bb6deaf650d7308565f06b975487ad32e9c1d59f7834c8aae5d3d494b95a49482b13260b97989eb5fc4b0755cadbe7e5f271d9de46836a4aca189ee587e8b2c8d25d7c5e8aeb1f33b273de9d7931408d4da2312a7cd244444eda2528c8648eab0c0eaf073e4f3e9842b0671bfa2a40127193b3f7b9726776331970606765f32a796f11b3e0bd571233bbe82f1033785218f3a503be05a5d49a5017ca179f639f682362e245a3049d205b002e77239e4ded65064514459554000bf4028000804b8a89d757921e12e93cae5a383e39e8580ba82a3f176d7847ca05aa65ce9fd20e48234b5cf5b18124097af9d5a393560c80f21282fb990a13f45476c1f78fa0b416840f68b6995c8a23bda9901665941d05bd7e0290b41238fc95f603fb1945897a6bb89ce1bac3762ed8b681b94ab6c690e8b9733f0b772f3e6dd20202e102b36aa1da530f62899c0a40adbcf1b9b78a6ae18b98dc219c8528cca06e54af47702596508661d839426432077c6867b28fa30599027b97416848e127a71c809b0eb826cb428dbf7a227923d6564d45552343bae9d4ed2cdb83e2e158b296587490fb887fb310f5541477e1819678d23569a724808c1d96795c3c56407f201b1cd9c682f7715a70f78f8c6c179856b10fcd6403cacb504ff84d01b43fd21056f6b2dbc3e2e1b048509f8894d1216310e6f590376d785923e3e3a695eac59a055de07073a72f720f09bd8e5dea7cd0d32ee891d8a714d740d79ac157f773e1e2d015877f83100c0ee1ad5d3422d81a6ee0ea7910a2dc516883c8f9ca938a329b3d0d02a7db5c12b1c1590df824acf26be539905efca8d42ece21284d0afaf703539ac0acbff7eb732d5d209f0528da00851af6b502fc54a54baef5121486184bbdba0ea3fb649683c9db5dca8392583831a44d8dc28226a80a47ce89bfe8e9c036996d61c946d123c29af88e47ec488a28230b0594db81dd4f89f11ed3db94c5c6338b58dc092fe6f9b79f8867462c375b7627a7561955830593ff5bdd5040cad8e8461f06d75a18afc04ef043e17126ae4d8984d4c22ce64a1407084450a4eec3496a8310d466e0e9c356368adc2dc1b20803846017f3178e69a2d66d6044c91f683b31f0f347e2a18c9d81ed08af57b92e4824c40ab0d9b5a55c7ff7912754241e52bcb3c292f10b694e7de3543e7b26385c96901f38a8c3a06d94c54e4235c000afa787d840b4d6af8b4f67e3ba6bfda8b236693bb7aa33d7b0d9fd9858cacbbb5058613b5242e32aa40272b19d8a00d9554c44c6f617f453488ca8979ae9e52f89d8088607a9b9892d6cc903199bfaf6deab098b66f03c90df2690bed4dedb183e50f56dfdea909c5cc0fbb186e4fac8fee1561762981702afec803b1a4d0a82c38d0304ba8aea3461c92748e58cee292d48906c8b3b84da5e507fc4f11e5ae24a9899d08e7d3176362b4cfccba4def8b3b3049dccf89a923c3cdb1a1cb4622a6d37661dd4096308169b001be71bbf992770a70e134f4423e75720c5a013d68190b425a892d5b7292054e3fd7bd566d1ae01387d1f9f561552fa6bd71768060fcca116492211186d06ef92563e6974ba706b6ac280b8ee639b9e08dd063ac0e7797623521e0b239e4e815ce5060180b2220be643138994ec5ba19b3277aa2e50b78b1a9eb11c0d70a918021982c513fe2203d5668f67fac3dfd75b5505082fe22c00265368978b5c06d2fa9c92e4f98ae8bddc575d07024efdfd6727f6f835349d907c4cab8fd13c4551c5f97a67a97a3b14d0cdfe46c53b91ec50241def48c835c01ccd60dacc9cd036c3429fb524a0aebf2ceee509ae4bb8d84e791a75548822c38b45b8203468aef0a80ace06a1436cea31ee8a2664e53b07bf4ffa1567b18218207a6c05ac633ea75271cebf6ea4b3575e30c691e259c8575acbf752ee20ffc149c81d29308953df6a3e6f7b5e6c63e47ef0fdba6c2e5061408badabf962abc244c0c9e41876c0515ddc0a0b7086b286635d86375a379cb1771d00d92dd1ea089fc14586768ad8848733adb74956417cbd645f70c5d832d2b93dedc313afd64a311e47b5a06a21b1b097c6febf4dcd3693862f087adf4659782ae8182e83411ee63482d31e50c5c448949177ec8ea92a6a2885dcda6bc635f3d6f2dfd3d583bc9d5945b892acab9ffc97511bf189f6755c8edb44cd4ec14e27335e48e49df8b2d50bfe4e01c9f6da5524a3b74acffa09e5a8a74cfab42cd5ed9bbc4026c934123381e475345e72563ca57100eb2e234990e2aa67f4f951c64ca600c65a882cc0085676caff0d09b973194bc277cc2a159a8b2658767479e439d8c639004ac6560ea42ca8b8bd2103f8f564a863f738e63325f36a18f487429acfc8917a1b3bbe736df8a6007033cfe76d7143c9b73951a4570a0b19cc4060e77c5b465ab34061b285d1313c3c341a7933cd02f360d17550c2e66dfad07ea668c3786a0b43271c2906a1eefc2b906dd50dea0d2ad95ba9b6897def2b8bd0b9a25655199b7fb19ab8939f0fc47bf2f5b22d956fef6c2434ed0503685285c1d8a16b0fb97a68e36f18851a2ea35cb526506779446e3e00eb32eb45ab107251a95bd2084b5899aa5325a6c4bea9079dcb62c20e5c2c36c1394501f1f80d8d4c53411dbcbe33d39e334ef174d6533db345c8a8158e7c8eb11b4dfbcd013fc176a33c35007ea36521b4af699dd5809355296b6beac95bd2b40944b3cfacaddc263ee2bc30ec64eb603a50dad9e2d378d73123b0d9dcb0e983ee32d0bd172010d46868b3894d482b0a30a02402cb4f973292fd5c5ed29675825a01f5a0e8863d8b037a44fb9f54862db277cbb2cbc5ba546944f8a2c3a743ac2b80569c96c413e3ad730f202737d155f6a0609277ff2cdef100bcdbf8e3ebdb597af454da226c37f74c875a5ff3caceea3eab02f4e7c2727f90817f2391398d9f9ec67f2e45c1d67bbcab3e919f147e7998b48422f86cb0f2e7173dca451a703a47a87770295ef8a4b96c651eb85f1163ffe8a985d8f65f4884bfa4ebd4acd1208174cf72c717c7e19142010a534091a3fc8f94902c70a01d647aa068dac9438020584ee89d30cfa186f3d4e6dafbce2b730726017501c200a4f2669b56797ce0894323b9ffa21fc7df6bf8acba6610cf45b0b5692b20ffeff05657c4f9787e398968790bf7ff0e296593f1847de9851f7ad5af8b905d8d42212e062b4f1e5d07bbfdb2d3414093d75fd02193ebba914a611f8c42d27d216e855e8b4a99d68a7134fe6449b75926c93f915871662dd6ffcc9c6cb22c648e8e1c574b54277541014231179a5f1d4309fd9127e7854421e48232c6044492d31a2a6e08d9ccebb4a145500cf62aefe1798965181fe51b4818e0e5342fed1466c4c1db81fd298381750b803e9cb9347d9161d331827c68cbbdac3a7bde9be1a0a41128c980baae9f491ef55cb17990368adae2fefa1db8fd92cf808b6c04ac99364e00a8f3a9e2e24f33011ab1afdbcbefbbdb13ecb699ba36e15a155a113603456757f75c12dfc9824e361b1656644d0eaa68c3cf9ac0dc41b8484a0fe470ebcc8df402853f89d998e0dcbcdaa93108b1ab0e18d9b763dc9bb4715ba39f11181304199203b307e41a80ec8563e9bf92709f3110cbadbbb175e5d5d593a5f7e301a67ab25c97388359e1b8ca91fc2bdb4d44f9dfa7069b36f57bf63279ef74b4f811befae44bb4cf85d0f02e9b9e7b61528be13876c836046098f29c1d0acd35daebf5c103a96f921b213ac56dee8f4703059fa89d3fa0bf97bc4d850cf46bf019aa3be5e634ba48c15aab2adc9e16fd0ef06379595ae93df6cc8c675f61c3e90f4890611094430d3ac093a8a5fbcab994c83c1077ec10e68a948249355953d24bdce9a83a60161a2c6f363258994b44c1087414d605defeeda6bfeeb6bcd60ec5438b74bea143eef317af97e5094ae21b3481fffc6411492fd27a04120cf7fc769fdd0df4e2a67649e21344582076b74181adabde22befd36916165c048f3a760f5a1f6bf56f47d36b30d3388ca00cf656374a1f14bc053379b28508298bab778b81a4a78a8804f745d106390281ee54f8d6bf7f2a2739a12ccc15b45fb99a397a7f3de2dd84ef8567c48be33de171ed300d277811a010bd265dc1991392fb50f8a9834d0112dba34bf6ac8de7ba9d6934fbd20cf6da86fb78cfdba8664c4dd7421f692708360c904708e9cea6092f21fb549f32a0c7e579c7925fa3e761a4257395688ed0f0f20e69b600729fcb2c7a903af85cf8b63676d073d2faf33673e4f0b42794da182fc1b9a8fd585e70256021aec448b004a690d65ce3ab5c48348107e7f6e75d5b18e489fcf9113eb3b884482f88f441406315bfbb343072a80df622166e9dac720fa208b0f862fc44b3a483f4bd9eb2149544ecd2285da62fea563712ead3ac99042addb80a15175c5848f146566a20268bf51bcd1067e80e6d54eb9e3c8a7c6b9ffac4f6711f92d1f5a56a844c4b6014ad991c2e76c2b871c1bc0b1c333b5e62e51529c3663a3c444e69082384ed46f1a09d0b2b0bad6a884bb9e0ec2d7218924e61505c96682bc5ca767389a90459ad1cf4621f11896794d213eb9754ca8a907a4db51b93256db32ab5b10b0e4d4191e7a56a11a897b1d7ce0126452baf6fd03a81bb42fe6d2a858b3ce880cdf4c895ba98e69b62cabb74e4bb1e4e0dab84b01920a5231fda86f3ac13574b4a0993804c3794f097a373ff7b772e05802dd12150af0c8e91e48af3f90495d48a6de110326812e8aac6917bd109d2d866db32a24bd229384060c301a550efa78a75b417f4d6599278a12b2f738d6374492cfe022a2f6481a862d224950a7d3041ef50828a5aafb774098ec58a96ca77174e72497a14384cd21984ebcf316a383923a4820db0c6a3c24631536ec0eeb54b01ae61363ebb45f180aa1f8532ab553586c8bc9e04a5ea2d2fc0bc5e166be42f049d8db0d58202a4af69af3719b29f99446cb42baa297b0a6c2f3e4328665e153761b1565a447566921f4fe566031ebfa78dcf6c65135a9159fa0367186c8986233c503d521223208237911a52744752a620651c75245960c34d801f9978ae27117d5482975d455b86cdf954d7549a97349d0d0d456fce927b24d7bc99668e1838a6cae89d895c4730b931aa176c51a2bb07a6bd3bf693461886a31bb90be7d0a4af91794e29c912bb764cf2981e2acbba572193deea29fbb65f249e003092dc4aa8debd54abb86387e5436d7b1b2662301bea43c48fa51c813008092a6d2027075df5b5154d268a217ccefe2dfdf8ea905d0beece8548e13a7463bc3e98a868bb0906523217757f54ea813b4114c4141af5ff1e82b5c57d304ff107a48be093b5454ee124321896c17a52daa29fac1340ba181ae2ebf04651d7078e7e595bc493c938847a0577f20e4c273b36915e563d31cadc312e6633c8549e1789e2b06d7b76086026426777d0b6acc3d68e2adf5e4d68374d3268d340e6a8928cacc2f04e66f761862dcebb458300f7d1028b9c9615c632e77583a8fe029ec153cc36f7a5d546f90cfbf121b53f38cb18977041d37da05685b893d3b1702211b49a7f4940812e2a036646706dbd0590487ec8dc3ad31a51c66f4cb17dfa4c5088149a15214843009036007c2b21d95a83eeb47be4525af8c551eec3a3943f07058e2fedbdf1b02050f8a9360c40d39fa9208a327526304891692092b2b4fa6729cfb11a0f104998d50298f5893b546880cf5181606b1283791c0fd4254477c9d37015c5364a88fb5c8f39709a1930b9adae81cbb2a435e96908c290a1e6bf95c576402d40ebfff644e707d2ee8888a2753d777a830b1a121aac26386987970f943eac3a5ec7d2ca04a0e88286b56c8150a1e5522667b9fac89ca62b8b1d1c2d6261df214c15b9624116032cce40a3031341fe59f139ce737713a463ad2b7f8047608cfdf9933c6d71838de3e0ef557283ad8191da010e760d3ab780c94116a844fc898b83fdd3164808568e71ecf3973842936c7c02e1a24d545d80adf9156107beb81a14ae0ad74653920a24cea7bbb15af2a88e417182914eb6e6c7eaaf15ef3e6e68ce111f7f2585518022b5403a5fd32f4c90d890758105c35d6760aa47e629d8a5742c8cd24c994281e2d25e846ee18715ed0d7b4cf3b5e9d7199d1d21e883c017dac85e5dfadf402d16666adb76334f9a9260e8a2b37a1276c6d76ff8b6fb073dcf1d5772175af5030c0cf8ba3c5f201c6c1c34f50f70c8db8b91f1af95a26eb09b88df97b1d9fd40dba1c96937ec2e6587e3bba1a0a14c7dee7bba8a2cf9743352eb6906eac68b9910b1138ad2aef46ea8f3c2724482a43e0504cd03f1891f36a0c3f6510327be923fd50ab9ea8ffc1abd3bd5f5c6141b5a11a722b6e3d95c731003d7c6e5718e0563c4d615fba7aa8d8436f665f4f2f87bc2d1402009c2c354244b93668dd85902c0ec6a375c15b2c10f253aa535ab7e75dfa84320a8df14d4501ad4f343171848db0296607f5ea6ca3202ab805903a0e763abf0efa132576dd4d3377eda40329f0ae7ee9e4edf79fb2734c1ed12c0f6138c3e2a70d06ec3869d9a6aa01a8432a06b5a2923dfefd66a59c4df61832b686f0820b71a4d985bc3c9587ab9528ba1e190818b44c61d5958bab6805d8b4063bbc70c7a9eb31ab002a626a7649d6c7d88d75409c8244d89964b8def779a39cbffc40c79407bf8064cfbb3436ad0cf642edb6d904823093b7ac97ad2619b9ccea049d73551d38fabb75714d186f767c23588450795926b7a63e80ef62ce47f5c838b1da8c2bfaf2dac1adb3dbb18d929056bd9eca6c950214a4d85399ca373dc0b6cb0dbec943c4cca4fcb378c4daeb2158f365f8438ec06069b6a16f64b4be0a87a4005a4f296952be4687a03f3888239ae1990ea8be37850c407e6c75244ffb92a9dfa8f538cc86623975554586a3d1ed4c784443663972ab8283a8ffed974a60bb4d93cb25935237fd4f3a78e4d34b1b4abfedd630e95bba5bee0c2b7dea51069e6a48c58dd610d0e4391008d6949c66dcc9bca180af30d32741194a36a186c30e38c6d088a3c499ddc73d4fb6018e659e6d28b835cdd793576dd78d1ee9f48a11ff8665afdde44ea09c227ca265af5a368a0e80ec2235101219a4038caa02516ac44c69980fb1f68481034c1e22941cd98a2d01d7b65124a9c2ee08a0d4a5ab03d917a01670b5a80e6481cc2e72ecac350daa3c8b0bb4efc330e3a909611cd6009e74036553bc8cbbc27c259060b7dd59795fd3c84d3f5c76676ffb4a2456d5a27cc256a2d0d98724c6eb61ceee7301e392ea357ea4dde04b1a92655857ac3176e80697da901b583e73b16d5115fa844d0df6e7c4c099a04da6f43786a1c6327b7d9bc4c38a9c13ffd00c4204fe98838e12a5a4de19a997d10f592162e2c42bc23e8fc08dd71224a25586c601400897d83cb638e4ace87b2fc53c23549894a212c94b8172f91aa5cacc63361c1b156542fd63e21f4005ff69fc0aa391e647c9ba96940a05fdf38d938059fae234d26a0a4ead76b1c9c82800f5edb8fa998c1eb9bed64f0024c37b55fa333d85ea45c721f2f2c228464273b72b6e9cde227582947b08797cddbe46d7540278a9803e99f8d882b3e0c0466194c8cd98cd8e44d828c94081a39947451eb3455ad3789da5751a432d9ead0fa99f3a2c139b6c540a09664e561e4e11cd43fcda07e971d3971de53a4920e44e9fa28f3f6c478b8dbe1fe2dbf430299bcb994af3716a1482f6f0fa62abc29e7105984a8c3728575603a1fe5ecdc123be82b4084bd3bcdc6f4afa9cb80af6acb5d2562e76491ad21529a94de916c386472f310e5e92fcd21bf6a4db8e1b57dcceeb67907fa05c0d963f29e1ae8d03267d3ccfd1c9a3c6a76c06dcf31ba74f12986cb80d66170269e8fba0845595af04c22de274cf51b3a45ae31349c2c5c3ba49eeddc19f8334247b905fd17bae329cb3356e9b0660b1a2c7f7999faa350cd2d25168e1c04259366f10581b23843f5c07201565ba3a00d5801f844e9318f2bc170c62b6bc81f21dce28f1876e1124839b3f02ff372682b0c9fe44a8c6f2e3d42430d7e4f7f7beb6b4cabc4d82d54f1419c6cd90aaf23fa836ab1c21ddb39d5a967de1c491d31823a62b8acb9d64a043af0c6e48a4370209195ee45a2d9ffc11b4079d611050b72b6c595f6f5466278dcae852a038422e14661a814886d11859d23552bd875521b18d07c6d175bed032ccbfb85ec168d0e110dfdad17cf98ad59e226887310d3bad299343fda767e00e808bc816b1a413c6b98267b127097b622405c5cc3f0781615e27afdbe4cda4273c34afd296843568fdd26d2ee8bc3e9b259ef1a41892782127e03b0fcc646129ca860160c110d3d6c3cbd660e9d16409a11b641ac7406448d7bd9a09903db5245253de40c05fdffbf43ad88dc9d67daa2c8b07db26b0e18acb9ebb231d90c6f738e2b63c5c1e4dcaacb4aebf8e8560b18cc1d4ed3f353e41659b9e609cb5a77eb793e84bc27f1c96c1b1abd28375d01f7c7964d0163fe6fef3a6fd33b6e80a9fb7c526031721dcdcd2076cb92b5cc9795a7aa609086e7ce8702ccfad6549bf0e72c37ddd9685f560f166295585bd07d716568bf36ae027b1e2e0025d8cae29797a09007dffd2bb5421c512f6325d0096206e08b760c0a175dfa125024b4d812843fae166d2ad80116d2fa7e85e15445d08fe9b885d0332f0b5e3779e823369a8ed4c336235d853afa09e54157385c647293cd9fbe2ce3900848073110fcaaa04fe1f8277376bbe809920e5dce0957d91221d271819149d986843068b369112ba1a4139a1fa5400a2dbf47c54e11678ce8e52834b8fe3d82089036de9436a051682941543885b6d12e35ccd3323ada7db46e866f0c3656dfb1cca2298520c4a9ace5e1cd8943ff433d50a70b70c3fccd6c01efaf8851798d1e8582a92b1b907bb459bbf2077b6159ee56643a00ae7824f955a93daf703dbdd493f9f6608838c33e4ee0700a94875aa5e0f58d2cd21b455d888f6089a17d8a33469f2cd3f8580fc063841e04705ff3861eb01a33c823828a37f5bddbd27ca07741811596cc06a4c31343e14a42d1aaa92745a6170510a9c912b7decf0b132f7cfaa3ac1c2b1bf6f1254ae390771b1a8bd8eb34fd7534cc074495093dd81cfb7851a8d9c3e8b864567e66716df6277e287cd33d580bcf79ee2dbbf147f01d4261c8d41f1c8997060beddd0c95a2883f6b990baa5a248aeed7b0c50c5c8d9488986553068751e851ccde4cfd1a9840a084e002b49a0378e26babbdab772b1cffbb1ce5063f3fbf3ca6e09d4323cf2d104605374c1d459bc1b6f347a64acf819b745295e7c72050dc40b750adb809665931fe6626201e12237c9d07d75c384049198e89110406ea2dd2d6814582ba5ea39295aefc5dcb8f0bf0ab43100b740a188e2c6701b12702be664d8c7e5b02cf387a9ba7121181f5dee6adfed9ac178aeefde356fce0d094e4cf162266778d743a19f3281af1562e8b373ccf78a5eba25f497c92fdc8aa29004ab5996095bd65581644ff0dbd5992abd10172a6855c9024517591c12bfc2a1d71b03c7460a616902f6d9a860825e4f5cb98f7820f5595ea6de2838b32c89e9bd832f6fd7cf288ba3921e765f44c262f9483b430851cec005a011d53ba3f2ab7c8c16f3fd7df682889fa5c0c260c77bf32bdc475bae32483f756fd8105f9509e97c33056e9bac8df9198a540e7a498794a1608c7eb7e31377ead9a102389b4a1fc7b4f4af1a42de3d5e29b6d642cc6da931d4fe8c51983429b3367c0b0e2372e1e2e2e9611e0e1cf109e958503aff5125ea0e5ee9ffcd1c8bb7ad0c04a3dc325bad663b465ca819e7c6e3305adcf878211f1093f3085ac580a816596b4a954710a2066a74f40e15e2f47c1105c4078aea040c36a5126f3d88605698aea893492a0150081a02a842bd6424ec4ca379962110cb1828d7e216cc09e8ad983817073143a6df53dcd6c89525eb566c76ed4ffb436dcccde429540eac7c3621924580ec8bd409385c4a788228239fcca46f03fc9dcc0eaa0cc18973f778580642252d2219776e589b5508f6847580b8f4cf41607dbb8334772de8b61f7f3c86dc780852ad2c450a85ee60ebad8fa82962c9c95e7d6a36ae805cc26c5e272a1e2a2a9c010c0a8c4ffe8093b098b18a41446ed0ba8add97d22e8ecc4f834130c8a5f8d05f1694d81fbe9583d5b70bbe81e146b4db1218381a14e4c613f32b862373c87b023a505891934f1fea109b84af33b524c2baccd5deff24c2e523b8b8910036d8a4ace740cef7d953d796404ad5807b9e9328bcd7b04320c07ff02e5957a773ffac92d396e33ff33a036e36c0b58bc6fc004f6db05eeeed9ac63ae65cfdca23ed7f6c5f845abc0d415405792a35599151619a1edb9d365e574e1a3b78a398b42cfdfb2a12b16fbbffdc8e3c1e1e1191a386547132cc8059f28838b94f72af733086a673344a005a26b7e62aa60ce4a981a9aa478c574af09041fa1b9849e31567453a4090fc678282ac30e484ba213708359200db7dfd9ee5d154e0969a06663076c94bb43ed61666130fc66f29c8fd53fb3227b53993a0eab6733852dc0a7eb35cf848e11ac4cad488ff12a0f60b24be4187b0d4a5094cd194c7b7f4217be507674e971604ba674c30258a2daa7c23da7ccc286bef0f456e19ebeb109b3f756b897ff591cbdfdfac4816beebdc21d8a1b0ad0a2467247df66d95d39e672cafe4ae4143993c3e9302213b022835e0d57c3d3dbe1c2ca0fe9ae5e04903b2ce6929bc1ee50fe10cb55a4b161697ab76f897b6fe5f9a0d64831911be23cc6c183c5c7c9caed423754a4f2bdea451232fb224b5ebea870c3a2ee8d59e291d46609519b63e0663a8045e8a3071852beb5e43f63156a8de9800185012a88b27f1cbdd937a2615c80f336e9edc591622f8a4d94cd1d602c0f08feb80b819a58ad31d2e13bd808982eb10fa6b0b4ca10e9540f419abe8e9ee64507a7269862bd9b55c619377bb255cc6820a20a65fbd31df08ad0d19ea64682cef843ed213ed8b1ddef95de175c3b83872101d8b8aff96e229da728a30203ce68c50b235bc25bac71beed7135c7e91e7326688febf6414c09050e616ab4d6f02b0762e89a3bb85221726a4cceabd090fd0598a06bbec9745b79db134ce529e79aa1c24037fcd608d502c519a2f79f7cfdc17cc600d8230d591f3485069537a0d6ab59cad7afb9fb0490775119ee617dbc09cc350acfc9458173f1cb2841e57323601b03a3c964c1e06570e542a760062ee2fa41be63a21e07d25ea991ac8643107ee0daaab6e6d1596389ffc41ea933e503ca20875a8ea83d912b6630ee0e6f201975ec28dffe5243c89e86e4bbc9edc6cfff2f5430f2bddb7b3a23cb6386800a507cf4c01d0e310e1d870bdb95dd8a8cd722822847186a507b36e70e46cae9690f3d34441962d91ef805baaf9286b79968069a285991ec7d09da14e6649a4e14d25c7e92251dfbc4ffd607b4c226ddd1c63e71f83c0bfb1909be06202b8e53a9efe7f28801cc318544a2d7836dde6ae3a2253638475a12bcb8e944021d68f6f709f0a26fb1a1929e3edc2bab151833219be6a2d3d049fd72306293a6a25f8a41d34537fbeeb89c4013ea28415c09242f0814a711ebdb6c29aba708647b0148bc71f374708622a32537ad74e940c892c229d401a1e58c4ad824e468a01cfb1b4cb755063250398408344698eabc01e6b68975e50d2d541007e251785a06a5b605bc01a911c2f35b006ad0c52bddcff3ed04a77581407def90683a55d8d56bcc02139b0d7a20cf399de5546ddb63299648b3f540335239c9dfe4a0cb81cc921e41ed70fe1b78508aac24432455d96789a66f70842f03d37f5cbf80ded51e84d83f46ede09a4c6189be0172618811e5dcc55869f60e0f7b843aa73ba6f835930c799163f4e6afe8b8b2f973fed1b24dc26523d5c214dd1c8c8824de3d288d16f67e15af52d2be919295ede94f44b64b4cda0e9d5ee0e2a3ed8acd7ce9b54e109adb9c59d027aeb287c6cbd1781bcd686f0b97aadbd85ccae54dbcd55cf2ce2ab9f36ae09130220dc86a79e1ae81dc41cc182111c14598142a5c9866b82534129b028f61a5b0eec575e7f2348521bc6364c2a8d4f506a4ef50fe8e3c6814cb08351e622138dab705790410bc487b25b50f1dfd3f2a4cf9a61f220f6a6bcf33246e253266d29ec8821d994f6b4e05959cbc87009c1a9ba5a4021649fce6bfaef6d986b5bf4c0a3422bb2ad204a27cee8be102b843618eaa97eb64cd2c297ac1e601b49a7c9d074a1b95b3ae93c9f7e30baa6672a8fb263ee46a939ae02255e5a66665bb82d80bf260efa745cef833d400f2bacc3776f3a7180d7058e3ff0d844e2d26800aa68a8106a90cca99e1a921e8ebf5d0fb6f77ebe667226972cf7144a95f96b4e19be60d458747b8e63ba50644d2fe41294637cf9ecbc89f9746f6df6d5931516a0a2c434f0583558f05e4e3994641cb0504514e7b1dc984ad61b9692b6021cb71ba2ab247c727f41015a3b3d168a01e96546837a9224b73c5cdfacd40e242b615652964634ca27186a92da899b6f4e357423c1991c5fd3a754f368a0fa5f126eb9adffcda34784b8938da5c1055ad0325d444fd0c2eb9a982a2c37504374f1a87508495e2e8dca759b2a7614c0b43649d48b3a463662e217286c8f691c744c1c8ef0260cbc65f3213cd405ac2105b161f2f23c0a7283679a6c0daa23cf04faa0c8405b165c50119b2819c414d5a3ce8fef286c73189798d31f4e8829a90d7d8ea927efd0a5388d83105c235174e86ac4ee1ee891e9d0c59d2d2d998245a7e1f80d32c40b7a7480bcf9b3b9727d120b86cabe84d6b4e50dc377a854c917e18698b411dda16a4d2efc96ac60130bdc7db64a358c32d10c289885ac81ef9a7a6461dc89c7882e0799bc75dc94e3096482d3beaf6c7eba6fc6258d9bdc45124126ca7d3a7c5c98f3ed9961a74db2cdce1fd09dd89efab08b5df624b7ec2f39651385b56ea7e4cfd925b54a4cb4cee71ebc70caba56ef7d8ca6c8d666816b49c1b321d4193b6e38be1e13a7ab4b228b5a35ea1a2f2e2261abb3aa8d7bc3a4729168caf152a60d4e638bde4f4c61504229ade1d3a329cc83f48cfcf352b2137e8e5ef989bf09158c2fb09e76dc90e9444faa2403f1121183d99e20c5afe99d47ec2b74c827ef200b3ba5acb922cc1357167ded5f91dcf9b5da2a710f4f0b2c57ec3998875f5d710a1f0be264561bbf61480919168696044039026033c83fad0b16835090bd61422b78500d6fa325b581409e503bf4f9e3dba5996cd35dcee4d1dfd01b402a5af3d6cb6973c5dbaced8f61ed071ab0461cd208172591b43b75f45b9c468f17a580f48a1fa55e4e6ea11b14adbc5947e765b7d1fcc0295a37b21b59ca52ead15bdcab31253bf72c28025b89ec18f48be4b874c04b462337bd64a8370e5c94f565e6b638d092bd84e53f2f906d8aceda7a0c6fe4c8cbedb371125fa50a08b002c05989d61742f2c7a99fd511aa060febcb7c8e6a386b6ed3a2fa2f74d899431c91f560f49217458aa8a8575598699ca51b171245ad229d1b346cbb7bfeb052379267084dd7688c05935651ecb62ae00de71d950a212ce13ccf719e6b5108213f9c918f3d42bc4fc58a032ae4c9fc636883b716cc6aa9043b297f8a569464772b3a0a6904ebf0499d7b90e88a0f848d3876aae50b89727a962167b8efae304d6138d27b94ee6ed25a7a8241dcc83c4fca52d74653f48bee8573b89caccec1debe8384012a8d56799963b6781a4e8cda28221ee0584c88ffe1072f5368c695795a70c27c11fe556cb02875228f75962b7d2f1c01057b3165d9eea690a6ef3de57005c36f228a808f3b9a0aceff3115837754d2574bb6ef728d8b90fab2f0a5d0aa07cd9d1204ad103abd5f79cc06ca0fc83f416e60242299cff13f7218baf3540ed86458526625619b038e8620513bc51996825baf1a85e82bf2cbf74c5212453cfb37bc24ec426ee29cf6925916a5c01dab27ca3b84835b2e25926e917a694b6595168e4f949d3a7a7d65212673c53fa24f734d375cf1b48d552f4a00e65bddb0c122f313deccc756a876bd75cfba40c657e8b3ca87cfcc1ddbbd9bb94d33cabc114e7aa2e694a63ac82b275e7c16875658e33a71a079509eaf50a25ffe28c0aa966175554c3fcc56fe5d9d76b42afe772c003d6acaad36e1d0c5aa379a1f81b3d963a95f0ec38753f7793c4fdf38b842d78be26747ed5721d2e5ebbd8c144fa2ed63b253fa4b70ca318913a6ae1c14d658b1064bdf289189a1c1679907ef141e8cc55b76b003e1b7b54a7f43ec5bad698b735f462f86148eeaa39fb385b846d8369934f07ac6560b5782370a59145ba6a098fd4e719a89389f4e1bbffe129bb7a97c2a1c4bfbc738ba0cde08d0662f68309d16bafb04bb564526f3b82d56ddb74c4f1ed2897c002365552935e3025f8b444702679c84ebf48a0b3009cbfbb9f76367c3477f267197919ee06c80b891ac5b4481351f3467663dd8fd424de7cd577fed8953d782a05c801a8d93f618123930b77461bc3c4839ed1383e4cb79b8188ad6833ca34dc9d067ea7bf261fb033b2091bcdf132b567ef3873b446e36b75c0abf846c9ba23ecd0ce4d4101de3eb9cd5e0c56101d77ae5a6ec190da7ebf8d7d0f46332f9ff0875da7569ad5ebcfef8a6097dd1ca260fda68cc9df77411a5da62163a396e059bc70be6b59b0c6df3d433ffc0964df331a54586364fba67d15b68599e9d698d72bbecc119f5632aecbf16d797ad11970e433ab2dfb748a8a5b8bc4c583fb85670f2a6a3020a7daa56b672365e7f6ad61e644f044780e559eebfadb7ae3350a31dd39cb46b52689ad896cd78c774a2b0b4f79fbf25def4eb8db2ecc76f2ed8a41be89c8c913eef1eea5064aa73c12e7892c3355fd28d83a7afa614ed6b9826bf9663f2b7b89bd1592c7da66900a7dda1ea10ea50a756b8b5ff9451828c87a7a03b653d10e47585f2483af09f951bee7f963d6c10b4fc094df1bbf824c5751dba1235227122635ed990c28c8b544c020fc683927789e61817b59136bc460ffb46ca4f165e8729f702542b2fb51e1b0c0cfeca27b8e91560f9bc7a29a902bb4ad18712d5cd9323919e578122dd6edd5189e940694532a15e2468e2ff214d7d224920c231b7bcf68b175e9cac4ed34b028eadc5b6e853dabc39acce2777190f23c2cce857cdd24677da19e10560109919bd46294246e7d6f7ffb8fc6543fefcebe4e7b86d0425ae96f180b46ecc29d0c57806e28b7b82ee03f67b5d3074860e2abc5ed9d1ab9098e3bfea5df302b2d58d300fd7c41e70728832e16092f76cebe96a0b583e641a9b204b172ff5ec12656682012099edcccac72725190730067c29e8527f68b156f21246c344b68cde6d4b4589fe9cd7c7b6479825ceccc8c2381be9c31f4e915a92b8fd5ec900da02ca177f161ec47ac76469883cdc8caddd03aa4f4ff13ce1454b7c7fc20502bc22dfcdd5bd59d36fd1858372305c24d3ce12bbc30c964f4116ec960abbad83b66c1f46694c32bc166a233b7f9613d4d4e5d6324156465a58b6414f1160fb106e4f8b580a28702af9ba9ee1fd66e79223cd264bd6bcf4758065c6bc2d03d03c5845d0e27663690702825d7b78a99d43a82cfab67c436215cdbbb350e7bdd8b2862202d652829a08224d53cb6479cf9b45231c904acc25d401b98c2ef11cb3b96f391da6d2235741e24b8ac5e3d17e6a2b485889d290ef69cdbccb80c60fee02097f7cd9247becde3ef3419a6074abbbb7d5dae3912b1f98929ca60212818a8a0f3865f166c744e81b09f26637bb1eebd9a63b91883d45849939d83e21857fb2d361088d3cf8a9756ecf6bff6d38ce250edd8ffa0328e393d88f8773e4c5774604b49a34f9569bb24e9aef973b76866edbe8dfca1af3b47d2cf458dc9ed6e8d1b5be34e9931182bc1334cfdb264f43dcbd6fefc14648ee208748f4e87652fa908c4bef61649051cdde81a929183bbeb8b72b04d946dbe79378379cb55b39f22cdf514347fb833cfab441d1085d201d409b308508aa9f38a33438723c228535563a48c6279abb472c519a8b5bddd74ad95c16dbfc943ff592220887c6da1e83cdc75d373aa13e9f099ce2f9fa62a9ad2466382a3a3699f4c0f048886263691d2a4da61408eea362d292be75f7f434907b2b408cfe62d8513a8cd97c57a2a8161d6b31f7cda127e277623dc50ac90bc46f16cddd5a4a1c186e256f5fd8e6306f7f7286b5170c92687003bed74083ed85c5af4b07020cbe566f0eb4272d6e82551d0395891738c19db83ebc7203f9ddee1bd0ec5c71222ed8b705ec3765e313efeb29d7ba42d5ac703f658daa29e83b7d2410bf15d8318ef1678be1fd9b1879a15031fcb4d0defcea7d35bf54b4c36ecb38b7f1eac3f18e19ff6495c254947b03af87ab2e8407cb687526db9c70da5ec927cea2ae8d438e5d07cf35c5c13b802c3b7523a15e4b11a657a646c893e9fdbcf08546ff4a2d4ce9b7864d66690aab075a77e35eac4a487614ef631fd2ca79353a6a533d5c68965bf5f89a69e007356a0871ee5967ac24c0668dcf351caa926648a14f0e0a964a59680c9788d7d3eca3ed1f79230cd58b57027ad5fa6447411127f70137177b287201dfea11a623c161912733121e0940644850683818c1302c4a0527f9ff17a7a473fc6357448bc8160e59472bfc1780b32283b113261982f7c9f4d472ea36a8df3e39dcd583b0af11efbee6ed9b909f604d1dbaa9ba381aeb828bc3e98fe8fb83cf46eb67b569e6f365355280c7c0ba82de7f643a816119153b9f57e65303c6688a8d5abf8ceed35d1a935c9acf574b9884de1dc557ecd96b99fb3917a189feebeb4850d747e774dfdb0bf6d6e9717cd6b2b4c7b93b2f9b1a257e460797b98cbcc50d2b9c437524c2bf50d1a0b253fe7d38b1547b55ba2da6a5462ff2f3d8bb56683272691621948b4994d54c24c840454722658c637418f4db0d245a712e6b3e0d8e712b3abe905959cc52fb1a7908dc564f1dac1a82f126df51f5b2c7a359471aa6875c70a1055cd3c3f9d163cfc32cf8813f59c64ae43bc00e1be40da9418689c321767aa9486d9bcdf997752d0f81be6318263263935cc8d2c185596a854dd1dc1aa9ca3b3fc09e9fcb8904ec3908eabaef3425c8a7b727752dc1fafe333948fcb230fea39e0b1efe33abb8b187b391a666f1162124ca6f63c31bce603adc2f391578b1ede31bc45151a56a5469e4df47e3d7d51daae27ebb7e90c043a1f8a5ec0c4dab0c83d78644b3c4b843d22b2550c6576f77551a5293a1c45abbca705e4f55c74ca218e780162e9370da33e6230a250b7d9f9333707bddd0974e63614f83bb565bdebec08ea6fe0ae8cae738670255f38961bbddba7e0b4d187e02418b532fe08e7be6d6eec75818ba08d4029c7bf7aff3023aebd375915e45fb6c657ee72675bc4d91b1f811e477361bd277d498cd628e7ccb5144d70ee8316d4f019d9e5208148d5d1c6f285eea25b60efee08978ede9c30148f261910e4b5e856b0bb039d276d67d3f200233a1a2780df19e311bb539c67ecce8ef30872314d9bfd998bdfd847458839eb3efa7281a178683e137e05eac94d8b8a291795dde8b48da9fecd60abe3ebe6a92e1e8174a3a1f50be9a0b7807b1e4de319921e6db890aaf8e832ebfc864bd35dece743a403e4667d2c3cfb3cfa836a1f90b6d7ef65db393d1e41fbe84bfeb37800d28b56fe593b7a77689fe718f6a2ad2cec28f5bbdc095cdf98212f26f292b4e0b37e749f6fbe736f3b42dea1cde70535a17ff1d6b423a43734fc862144f4e706c1c7a3677c0cce23e418da4c58cf435d3bc277411b2b00e30e5db801c8f1e89dfd811658d3f3326b40c261b98e660163b7235c3a3a376fdc0e349ef43cd377d078d0f044e349f43cc280863e1c101e1f3d036cdd8ea05c68688141f6d07120e3f008298696e507e711db21ce63e279848fa143c60b0946cb805fafa166d64d5d70793b34ce8fb1b4d038e0e26e7cf4cd848cd6d0dcd2858eb11b54bf4d8b57286e41f0f6a1e835af141d2d3e5120ea48bbd95be26a8efdd748dea9fed4768a9e67a80e4dc400e0fbd1bf1f9b7784bca17d40423a4ea0b81d41e2e832fe209e319e6379c4d939ce8e383b3a3e3d648637604db299bd80801a3b5cef54171e2675a159bcdd15dadc10571b61300af734e0c30e771e73e3197a83265f1024317a733f2023348efe713b683b949e90f4906b36b28daafd453be8adffbb39cbcc32ee401ccf8da707c9e635108f57c4d1640052df11d40dddc2dbe1e116445d809fe182fbeea4f1f4f4109a51e9b3ec0aa498afd9098d1ba97c2ed91578be69f42418d4f54625f001145c497662ba045224cb7a855f1e0b44599fce340b9c5d107c016920b2cc3da05f148c0e998111aa83d144e6e6161a00b1d813c950b8d3a00f3befce33881b5a7dc09292281194633e2e30b286de96f38891fdc1c63c264062ea4746a9a690f0ce487b18828f26f300cc039058b4ec5fe03b4262689e7d4140d13a1306b21ecd02ee79d2f2a03feeeaa5015c7a02aada85625f400ebada1c124ebab9602720574245eb2c197f0f7619c54a01e371106cd828e44f66ea7dd698c0990777e16341356760dc143774470a0fc877803229c2d6b9b97d0acb503d38313524f8a920c726842fd030335bfa6bb6b67b656f295392328f06c505f80538ddb3c69eae7b2d89e58ba4dcefdfe16bffc50d5442f3cb09af4a406a53c2c8dc56255c2e1a43a61583f537e63164c86bba8ba48e13d3816981de348c50dfb0c60f068ad3df1a3f155aa03a9f0f0a23d43a9fd93730d01dfcfddf0b74c7fbfecf850fc8d702dd397dffc702dd09bfff5b81eea0beff13a23ba9efff54a03be2774ae8047487fbfe2f05baf383eebc7cffe7e33301dd89f9fe0f05baa3fafecfa43babefd4ad0800fa0e5c5faab2eb4bd627eb04b8c89f9f1bdf81eb6fc0e840e6597ebadccb77a07641bd081ec05984f053422921776fef50f657e3b5d19c7ed5789fa039fd31e3ad4273fa61c66b85e6f4bf8c17e9424173a2a039fde278a5a039fda9f126d19c29684e7f385e168cf7ca7895c64bc578b18cb70a9a736b03f4cb8c940ea064b9465a0b50aab8dc1aaf1414a79f35de2a28cead828a49fdd7e631ea35bbd145be368ff19914a71f6351a43321d8b75f7e662783cb74ecfeccda91a8332594fb45a99b72d25099120212f403e627a829b7ff2ce52eca656a28f7a7845a07484ad63af6574c3719088191591f557f519874a7b319c24608d149fb9eb545b2fdd216c9d6493ba339f65f4a62c7b2ff672e0141c0df3df56f970b77ed79feeddf96c434e725447c9f5c439e7ec9b5ec21459a4373684cf75b721c3b84bde0efdec6f7de7b480301fcddcfc0dd47628fc41ec2441995ac34a73e253ba5cae5061b5c180a84700626372037701c0e1676a18c4ada2e8481b90b7b54b9bb61c0f09a7cb1207ad9bf8a3fda1fca7e076a17eed27c2097607d6e7756c0b74fa998cb5a6136dba714c33cdba7d4bab5a50f4c86153ff536503410103ff533443245ce40c1b80ce3b2ad5d6e7f157f576badb576f88055e61e6c0b946b7777b787b0ce9dc889327d7777cf605292b5f686b07b2bfd7b2f17c22c57691ce71d65cae083f874c717100ffc3a0e74e71ef4c72180a9d383324ef5412199f6b8f846e87191b9ef71912f0973c1d5232edb91cb25581fb04f26681fb423089d28d61084191c89ed58bbe4fe14898116e2e7b276d86fdbdddd2bc5a8fefe4ed111605ff6877da9da2597d03ede0dc448c70f1d318e76d65927a48572989e236604c4648192809ec0464047b972015f29e00c1ae988656aa4032805a3a09f4c8d762ce5b2667acad46847923dca2536da1165492e2dde8143ae6f91e4aa04be4ca01a5e90703f1c947a6d5e05ad83a31a356210e2043cd0259ae022c396184e2092a9700e5ac40d146c20b285131f865e38a20610a025a25001900a493ce490c40f45ae48429348c107a61b2cf0218404c681896823b2bc60a1a2c5557ce44cec682224880d2b53443124a446530c40f078c10aa0a0d3161ad87ea5f1946bf32abc2cb054281da7a5d3516ff6364b824587581055b0f8d902145382c3cf134e9e50e11bb4d0e81ac15e45119766c404434ec47000b24409529021b524494ce0a082aa47b4233c006184861e94e072c30f41b4d80045902449aa601b4d6c8c54a172c4b9c4d9c427c713264e24c9e2871e8452f8e7071537acd4781294a3cb064b442ca5c084c5cf960e9bc0a980b3491ce757b84b23d504e457aa0b494c59aa72c414294061012d644347b22041c50f5c8389158fc20a2f0b28bd81207a3861e6044889110cd480c20f36a85c9921878d216e25c2077bb5e8dbc4af28e140dc2c3fd40b6c610e09d2e4ca09a868b04595242668246d190224f452a3f870b1161fbcfb4941876940a9208d13fa81bb6171162072d75a92b0b4892428a832040c43599ac8c854703651c50e39c0f08304c78460a249c9d18c8a8da2262be861d6b8a2040e1c3e5e5dfa2748b705cd46e089134ba830017204878f0a5070b9c28622686c3971ac00834e0b201d5250ea8c58972cb5139829c8d1a5c98f10569e30d9f1c20f92dba0916a22a27f98e89494b89f251d978ab3f8144142058e169e0831bd90812c56aa285d8962cb111dcdcbe2da9b6b4169f0031228a840ab626885981464f801831615f8c023c4c2070c1ba203a8052e437ab4408740da218829558490c0f1c342ee7e5ee8986812b79a92216a3728a1f9e850628b8188099ce0928488fef9c15d616f13bfa284fb152ada924a7458bc608185254a6049c18a1d4c296cd1a1891f2c388ef8a9855ba0608e0248a860021796907c40c15304152f509102275368e9b47c416ca0b8bdd24de6103418988ea098c28815506034c3b964850a40b1539d42b358a078341fba133aa3dc75497008aa2d55a9b14495011059e2872b6e4b82683061e9bba52ba5895f2182c33a6ab300091786fcd0928314409c004b151b88e800050f364cb0328152034e830bf11adc06bfc18774a3a7c055e044c61b303af0591ba0cbacd169588d4e8473222ec46577a971fa9b7ec420d78fdc408658d9c97b7b25114b6ec6cd401b3e01ec077b420e022e973d9d5ca58261d585596b533df6831f1b608ae7744abd987821065a88f62dac69ed1026f68d47bdda6e2fd779a8cb5deb3eb7da25a0d73284ec2f2422f797a9bb3bca944473776fa1249ab596e7de5b56a1cc71254de2e9ba2ee4f1f19583d87d896d4f7d9c6b7dcb838116627d1cc260883d0ec4dd8cd0b33aaa23955a628c31c6b51e564fe7df12f0d5a45695d3701c3dd5da5ebeef96c3f97eadaa1cfa871edc6bed75715ce8008ebbb91c8571a177a1073487e05ce5c6caa51e70619501d8c35ef5388e23bb4a7d0aa66abbbde109bca58f8dd383a80f7f062a3c815dd7751d4df71c89bfabb5d60784997b1a2d298988a8b6e3e0ee6eabb5f612ddcb552020ae2be9d1110ea2ffbdd59de4de23abf71c345d65ee7b56ab55e6c83b3653d7755d2ec1fa787d5a72abb50e2bd3a65ea2b4e9b7948e416b1964ee7d5c658e0cc1ff792c4fadedf6721df63ef014a252e20b4c8c6ac56ab96466782e494373eaada139d5c5052fa8c7b8458dd31f054b11a95ca4abd44db9bfcca06fbdd44c7db3ca4ede9326f6d77acbfdddba2994883653a3a0999238013153a3a022db92f83241a422ee406242ed8309a032350acaf104079cb7848f10142088d040c30d3628e34f14447218e1b0224416bc03d38bcfd712a925903469c1131b53b8a09aa20391293f4c7162c5a500b18e0db2e89436c8029f1c785024e2a31e9a74367ca483d624570c00ed30d211034a656aa483c8db3ea8b524442d443053231d4347cd199920c4a806d18b0ab903f984288558d21ae98458ba4826a2abc318638cbb7775af2962496b1f4cc0280714398e7a505a6b63dc360593616dcb706df5cf5de4afeb3cdc7dd67ab8b38fbbcb6a9bbf5a39cbb9bf9a70c796ec5adfd42320b52959b79b1be872e0a5716b1e1697c647f3d145fe681e03c5e438fd29a0dc5f43ddeefd1275bbb88b8921f7971e164272896242dd1ca76364aca28ed1529a75362d2d2929e532e6563fe6d654722d636ea75cc6dc727fccad75f0f7c72cc530b5ceb5d863f6c4ca0e4473ea17a571faa3f50dad6fbe287dd3359ad38fbae5ee4fc55ae7b684626a9da67a6b7487c25ad9c9aeb16e48fd928934fc6719671afa2cdbf711d4f27d5a1f5727017eef9ef3c7ae24d62f6f117f3adefb99150b482383fe7d182561fef77d6aedf61379ea9beeb1d4bb5a9a0bbadc3def76632c037641f022dff7ea22cdc13684d94b8e34e786308e0602dd733fa37bce86c785b0eb93bb106629c5def77da0e77d3ea37bee41e8c80d36701cefbba0cb05862074e18c8ebb27cfbbf7df65f45ff05a6b6fdb4b624c73ea7b21ac33a5e017f60876f5248aa71084eeb95338a3e3480ca31ed07cdd3e68db5a6b6da518651fd681da25cbb81ff67cdf08b02fdb77d897bb64fb1f7d5818f67821c5e125bf0f644b7609d6c7760a0b104c51cec31e2ef7fbab05b1234bf01bbff7f5c26a17f0fbd21e514a29a51e51eeafb5d65a3b29777f777763a12477b71d2df75b6b2f53eebf5cc9096122bbf1358388df3b752103ba7c7a10a4b8fbd3e37cbadf21ecf4e07f47b97b5cd09c8f2c3fa07b949b2cb9b71cb82d4750cb1ab8b3dc18ac5e10608aa73eb5b35c79ca2a0b796a033acbd3ddedee7df6e9d6f2dccbc3f1bc6a10ebe3570d22f8c1befb6b8f8b6c493beb71919bcc01a436e58b53a7aca83575abb5a66e56a4aed09b4e25b146d7b746197c84de7cb6b6d2d65bd2da179398d43a5748a7aeb418ba9ef53664be858116a9fb29106672a038380d9bc981def4af72d09b7ef0c2c03c751aee5defd3016ba4e1481a08b0bef53358df7aee5b240929e0c819ad779124c87ceb49703deb7d3a98f9be863caf1844f0575f431897fd61c83ffd0cd1e9316460bef4f2e557a3f8e1af805626ea29ea86720514435e22ecf27b04a66768a99b08d515ae80f24c0e33b572c696dbf37b43a537ddf7b7464c033514413204511445aa20736082bbd6554fa6704ffde5c48dbfd31d9abde2f8dbe4f84e00f78ddd2da6f4bbbbefeddbd752184f7a4cc1cad4e8841b60fc2f0daa436facfadb592d1da84da037de75d6e4fe5e1be87efdae91d7c735afcd06d44e75ae0954a7db4163add30101798cce741c7f8c45d1d5404e34e20bd43af7eef8c9f59d06bda99dd9999de97f7d98edd8fddddd3bd3639c6e965ec9d599e6e7ffb2427c7949a552a954ca8459fc82d438fe543092a8e45afa1b4f7567768e7cb1f58dff8b26962933bb2d823ae94ef72e24970e0414a24075283d2175e87fb295e18fec4dbfe4521291fabbb56b5272a5f18a22d6c717645a1d46d4f1b2416b429c2074020b65cd25353ae128d7649b1ff1a68e22d84ad2c8e0727d09e43a8a706d756a81dcdd4307b1b99c2768d10358aba95a268b81395faa29da52a84cff1544d75a27ccf46bad564abd275cdaa86cb7873cd696274a5d2f8ab93abedcbde0b55eeb4575a41d5f5088cd952073b47540da3adfad35e4f1075127b774c7d29c4c3106f1a90b7b6ab63127db3a351071cba50b0a2c2f1e4aaf180b8c48d2124515a424b13335ea51a547133f5ea6463db2182dc0c813376a38717204102366d4705879a1891544c46cccb00d22af3661a386ee824c8d7a9c3d8ed46acbfae6a7ade6a5369f4c23a396363b449b1de2a98e363b445ac9ab04529b92abe5ae6ddd5e7b6fb75b7bafe598ec926db24c3f3766afe5ba6b3dd01b4b4b2b8a422bad945ad761eb7dd78220088ea515b23f805821702ced909581e0c986a86b5349484a494b37f3bc65f1050628a65fe5e6caae56de750596be84a86fda9ef67eef36b02de781610a46c592a119552ff6a6a5602a4a3dd43160384e3f1e2222e925455132fd96d5a58e4181a6fee9205bffa05e51c7a8a619c594625231914ca54cbfd27a9e15c85987ce3ae444b22446c54ab7a61a4481d6e19eca4b7145116951066fe3aae99a061da36530ba069aa7d3e9743ad12fc5ec7ab733f0454ca14207bdd559749ee7a9327f98a669c67030b31992d96c368bc57e62b1582c83f1020a9ce6e9743a9d4e3060d4320c183040b7790eaab0833167d1e927789e3037ee4768ba099a9e894fdf6c8664369bcd663918d8f81893ab699aa679822008822005525f62d17313a750e1c95f70e78967d1799ede99024df387699aa6e93647729bdbdce6b60000000726d7d3f33ccff34c2a1849344d13f7d9852707531cf6ceb3e83ccff334cd1fa6699aa600725e72bddd6e18638cb1b5d65a5bcf7ad6d3cfb3ab372e3c39e8a138fcd9ef3c8bcef33c4f1827019c5e50c4eed432998cb4d65a6beb0de77aab95abb2efcaaeecca427bb337bb04da9b676fd8de3a7be3eced8203380d800004288006a702801a20c9fd0698f58d017474bcab0e46d1c8f06aadb3caf453b96b37ff2e9728f6b70ce32f45eefbaba308b6bc1e599e4e2a1559d6d490a58d2c67902f2a77cd635c2a474836a5a55b53873d61bee4adf54dbf8d0eb1e462314c2323db30d72c002483931e30bc7e7af498e18a1e3ff9ec41a3070e3ca0f070c223cb0a8c101989628a18b024c9e2c3d0132c3081a2c60e30f0900248096a0cfd388204171db67021a1a5c6cf96169c535ee0c184940f6a8d2c4204a168e18447112f1a3c665c7808e14134a529484b1096a0a4a02a414c0449e1c183474c065a7b3a84d10602babb4ad527ea35553f04eaeede5d51c4d873b91c07732ad5e974dffe17cab8d75d14affd102eacdf3e18cae8efa9f4fd61d4bbb00787247c2800b9ae03c10eecc0beb9e1421ef045249638b3f20a44902c7d96bbeff083f8c1c7a4ed063ff8fd89524a2965ca5df7b5d65a6bd3babbdbaf50eedcdd6d52eede5a7bbbeeefe5ba0e245f4bc0f1b50415923438876439420e518f338a2c47c8e0b7873252dc51ee1ed5819c90dc757e94bb92845ad68075f701da98985ac7df719ab5faa69f20183f9cba7c6848a68d4120405adfac46ce098703c5e90781a03adc117ad3b4d6e1667de384ee34e198d09deefbb9259c124e072e078e88c381ee10790cb0c6ad804bc221e18e7046b822dc8ceea840a215375b7dc9ca2537cbfddcd90958d5c05a6e0e655de631b80c3e830f390a3c482a9bf802031313a3c20faa562ccb62795716aeb19cbd35313bc0580133f472c4cb0d9a44eebf20d8b28fb4d65a8fd901c68adccfc10cf5cb112f37c45acb5ac88ce8a4c9cef33ccf33f78b35b126d6c49a58136b5475a22b166d71d9c5c9338d94119415d48fb0871a24f7db2f3c8156a491fbdd73b9fe4c19c9fd8df13be9ee1f269d4459c97d4fe0e7a81f610fa17963ea5803992d74d26a47b6a4d56d755bdd56b7d56d6586666886666886263803660167f8a4542db9dff37a9e9ee47e115b6bad0766c9fd297086dc5f3f29f7939db4da912d292693c96432d927fb649fec93812e994c8457031fe9c79f57d37a44e47e54079298c45f7a3572bf8c47e223b971ac9ab22a64461463d55835568d5563d558351cc3311cc3311ceb8ae4e6aaf8c8fdb5c3ae4c6d5d110e93d65a4b7255b8fa4937a61aab40b1582c169b71336ec6cd5a442d24ad252da2d6ac55d43a5badfc44ee2a5c8dadc65663abb1d5d89e9235b6dc96bc4fcc5a338f01039f22cd55fbd66c369bcd662f4e2c116abe648d497392c4f2d6b8fb8df98df975618680694b16270f4c50b600b18425cb10e24d8ab885681619a160c36cb2b36a6b37bd4f7bc372ab0c0b16b1a4352c58ae00712e7d6c6ef679abb254496b74ac0919a0724133d3a8520417ccc8b81c558ee085abc55a595449825fa962605e2eaa24617c1153a8f0c4a1ca12901daa2c21832f12a3ea5b2695c6d6332e635d372154335f9f7e40776474a7f5f5ebad4fbab3fafa5edf6df86b90ecff81a7109512bb51e64117286b6599206ea3399ca8a526bf1465ff97229af04b181fd97fe56306f5254c95eccf82a92293fa564c1197f8f2e5f530cde34c3343dabf231e5959c6d5628df54673aa9b3407a646733ce68bd19c5715deab40992a06662c3f22b2ff8b986a413f7114befebea0ecd6e80eee1e83b20ccaae2af6d9dcd667c8c56c11b7795fbaf97dd927f865bd9dfed2cad5ca2acbf5c1cfcd5cfd895cbfaca11967eea94719d7d89a1959f962f2c63e1a5edc18dd09ff56b2163ec09eca9a6f7969a06634c76ba9181562a9aa7db1ecafaad19df02f0e47987cc71919cd2957b2d715b19cb9c9b2ff0b19ddb97f47558ceecc5639f5cddcf24c0cd95f55cbfeddf8c556f7c50d08a436a5adf14a63f39e31dd65dcbafcb632754aded88aeeac32ad76c6d51e6791eb7b408ba2d859ed36b0c9408303d0221b0c22b725ab6a6c34c86d4631fd85a53ead0b596e17c8adcab8168c4c5ba654727db133a398b48c8dcca25cbfe3017c25755dd7759dca55e3d96bafbdd623ed0c065860b21b4f5788343b5971102bac225fbef4211ea821d72f7da874202ebef41f177ce9412fbe7a2d835c6164f2cb7f71d037acaf5fe2fc5f8a2fd2894827a22980ea3810f8055962f102b2145d90a52bd790e5d390e5cd387a1214a7ff477782e2f43b11ad75bcd60e04d57123e84d3fc81ab9ec46501dd6f77b1254670ad5613dcd8338e4a1a95da5a9377bcf1aede8d96bacb1b3d7da93a04e24805203505a807295ebc73ca507a07400652bd787e95034cf41b3586397c7a72129cd59bdccb8ca33186021ae5ee65badd5aab52269df7840699446699446f35aa3d7faa67f667429a31f71322491f5dfab9a2633c6e417d2e96546982499f185486614f30ba97eea65c6547e218925951951f98524aebef45aeb716e91e508b9f561768d2db2bc7f55a3c78ca523e57e98b1f4284a7a45cdceaced9af7bcb79293d5f290067c966ff519039a08121364c7f982166fb81f5580164278a66ae2c37c31dff77ddff7899f7a30c545ac591451a718b2bc81a11cf40b89224fe447d67c7dc8576b7dda1ed44d4ce499458fe1a7e3f463145962d1e5fa17b393ce7467adecbd9f58749d7e967e32e592bbe5f6482fa96f7410cb8fbbd11d0fbf47529a733f99a704533fa5566c7567af265b3b5a3337a3ab12b7c0460991036e738258ded33151ebb06e08c344700b682551329a138392e51a26ea1bbf274ad68ab5bbd10c4920786df1d2e2d58507067c56106205175a8004498b8ce849124130c1e58652154228151a3014515a61851a3aaa115d428a0e15cc29508408e123c30c57b00c6de14210132cf3b12197baa8b44db616b184668e00001014017316000028180e0984425112c4611c34db0114000a66884a5a543691c962b1581003410663188e61000088018018600c32482b5b20009e8da77e9082e909188cb96a11f5ccd0f94d372297e583f2b13290439012381bb2688770b66c05d5b614599610d3ff89b6593614bd48f718401b989105105a09e8a76ac199c900841d6c8a4c5b025dca5102c48f899a32306aec2b29217445b0bde6ae8a20250f344e621f2162dca6528e9586bb0631e99430dddbd368e98025bf95c965652b137af6c2c2fb016eed3181ec39cc6608947a82a5b0d9967c7f2e1fc0688d7612cae5f3cff1c03828b3270abf3913ad4f5a4e8f07bf7b8cff4073925631e1ec49dc7aaba54a8391a4c1d960422390b1f7e2e9e736db3103e8a83d9d33fc5304a73d33bc448d1c2f15128b17c42cd8a1ed4241da9fc8af15dd2f79d6c9cdc987c0dd3760d605e96785be68b30735c0dfefe23a9669133f6a3a1e607727428f9e1fbd52cf99f1485241cf5e14f6161857b40676357643ba6835898413f6a101db203ce70c3fe33b3e513c95a7b6acccd6df41bd210108f51ae35779e34256b66408fdad2cdc5b0e4e68fbb7e38897785a5ebb445dddc18a8d3c8549f21389adc7e1836c027306daaf2b45b7716bfad991ab18c3f5d04d562edf3f9607e0aafab90f93810a8154160883084cea90c39835c603ef3652ea1399066b7a0cd0554df31518fbcad2114c11959f478a76e666897cfe817f238f8826b38ea39ab2b1053d62aec15592de6ba7d8c24d383063874010652b492c8eb4e016b8d78962060abe6a46e4ee050bf7079c4f626e634b5a59e0692579f2f26498ef65866b05becd1fa13e2a7ef76cff0a9368f2899c6b2a4cc2f1b4ea2e69d58543fc47293921894a0b519b087708a4510a4f595365fe26c52760eb1b701263080b86978bd890a613b04b9684d68799ef60b85907abeb2f96acaed4ee24345ac4163b0ba428c8e04f7831ea643d8ebbb49a684396b0823d45e80264472054e013e37921b09e85054df3c6b54cf9ab8c9a44fb971590418c2a08b1b308c383d1a606b299ba26f8d89d99e8302fd8b80be5d0057046f42b79c923dbdbea68122e6f2d0b55a495f45a5b4fe96402ed5573d1cfdac620e83f60cde6ff884c2b8be65ceb62457e19c9311b3a9951ad158ee7bd2bba925a67b54e7aaf49fc8495de551535a000de05416dedc8fee1227b38275c2b5746213b9800a4a3fbeef23c15b7a4eb01d2a5f20f06522a8d3b9e6397cb305fa7b6d9c16f6a7675e50efbfe36fdb89ef4a1395df44572d9ab0fe74879789fe3b886df19c2db44b0323f857848d00a561f93f88018fb8e0a9492cbca8e3f06eb7bd2770e8e23253766a525177f20ad0c583ecc1a3e3899168338f5e4ae780f55df1eeca68874822baa47a63ea416d75efa5e68c78f873b30fd8dc36c6aac80395fab17b131835101399a7da49ef83f560cfd64f952b718afcf6a2df75342761e2e3f8f5d4ff38ae8c36f1be62ca290100de17f0fec939f3dee5a468c7603a26adb596e6519791f76135a023e8990aed1db735c6662df8ae97c827021e1b4447823939d56a64e8abc4b40e41f86fe1fa2ececb3dd278475432cadc044670f0bf040876c9f90b3f040e04c7401852b0acc66357598f30b84d421fa09acab9d0ee98c2de46a1914d618b847bab25e02777d7fd2c71dcb4fe69745f9e1bf926ee09b0cf5e1267ef51525d18defd57bd436f218428efbf6289c68f69c614ebc0e15ded75ccc7d66453fa6c78e974b4ca3de3f26528dfe2d2abe32fb37b9d400584b4f2d46ca439be1fcb8ff3b29035e437fc1ddecdf3ff2a5b342231ca701808bfed8ae49e0e3aa819fe24bf52f1396358fb60aec9543a0cdb1a8d5d399087089f0ba910b62f5f877d78d0db40cf96cd978b53a4ded2758b316760d3abcb0f45c15507090fe8ffe21226dacb21f15a744f437d11de0d7738bb581e8a3af835e7af67138b3461b2e01e665cc64d00426b05da30691ea903ba9c267be786709c73aaf2c48bb69a15e2aedea637f7f0887b9c033155595f6eeb76a2eadc71e8c81f5268d3e062d2efbf4ea53cc6b0143ae8f3ada5fd1f482724faa17eb38f1594a49a94a4217904f0e38541b12074f1eac3c28c8eb22279a422b6f4e9efc0caa79c6668eaffc85671380f559a37d11ee116e4dd0581dbd3b6abdd35bd4bd4a1914f457e8dd3f6754ccc7634085c5d8b126a7a03ce6a0fcf717b0535d80de19f504084b16875fa3f7c3565ae7e1cab47a4a077d7ee9a3fecf75ed6de6625d91cc0cd0a19ba5ec0d5affa2a6e500e312ccfd30057e04aca444116a4d92025e0420d2f361fc77c2ba206aec2b621b492d5a14030417958dd48f007151620729dc09d1409082a1cb826fe2d01bac14a46404258370756d089617bf4b30de52a33a9d095efbe04c9941466a95fd832f9805035b7e08bd8c67a1c857e51ad3322c69bb1aa0ce17434db01a8d1b1e068a7e31d49093525c25ab463a5d3bf52624a7249edf92ae302d1a53a791843682c661c0b4531387747ca797dd4194f5839b57cb395f3afa781f559246e76fb4aef395dcb9003fa5520f21c54c8a46622138e839909f8c052ca55ce926ec312eb4493618bc81147dad9147a3151dab69de5d7417924a76089539b44bdb058fe1271a7d5a9193a943ef57af2456c5e60645120dd041be78aa2310d181a5e2441a769900ca7fc048349ac5dc0615f789eb185630d1a3c5db48654033ca19cce679e77c54f781960a2f6a6602b9a12fceb89a2dde86ae1a31821bfc1075f567f2fe5d3871f746436d187f51fa100d9ab380256ad41e029683ee39d4ad37236033118440142291fb5e2a479f318e4167c70c2f42b877ecc683d9a1c093b082387ae7b92d282776e4c4cfb05294df25573c41e4b7285d5667c40d716b0e85c9a0c111eafa070abc15b3e18c0c60ec8f49347548e512cfe0a007a02d4562149ab0430332141262ae75255d2a0771d06311ed4fa3844f022b599f1f4e49076c8aa74449719fff671c4a47b8199790a9b0844e0d3738adb1980f95d1913f48b85862fb0a7f38a335fce8ddb9d0c773c19ddb84dd4862f44ee8205d42a61b1df60ef4d2488b63e413253b1743eda926a91b6e13dda4c6bbc54bd702a7e627e2bfc5cc8eab6514a54e7cb2c84caa4257cf512842a2795cae6d1e99787dd185f196fa58b3d3b6a98c4496adc331947cf8ac0c2910306a601ba50198231b96b3d7380a462562b8a41e6c6c0f7939ba4b4adbfc858d78e9b8158beed140346d340405641b40303766e483e0f153dc40702b4f50eabb350ae0d938cab60d5204b411c4952aea28571596aee881ed4533479c37f9b37eff43a8bb2ef0d6f547c68a605f02964d6a87c097c1b9db8cf716b40d97037de81a3250d3520a519e8e8cea5a3ceeefbdf909e4263bc71cd118a9cd1228fae38b8e36417231617f27f5bfd2fa7a09ca973283a2a90343eec93bfaeb928453c4767254ae9463660e2dbc071956ed90d1b60d066733a5faa0d36b172c9a03c8e176211de701998930b31ef6c88e05504bc44bc49091092a3062b135495e0cfe95f89ae193b187b63629ac538863a5539c091279c80b19355c4f120a0231370b329970fc694c80e9619e90e5650c32439447d42593040ee304fe75f077eadb5a8de91ae1a175061951c7378bd8e2400a5221ba1adec50428919442bc8044e49ee22b2a76de23c62009889c30fced124126319c30c0e40a71d23d1bb6b717499f7f66d80ccc6f096fcd26da169c09c6c1b46d31461351d3c5abe900948f140f84712ee32c709e683fcb6020e4378e8e848568d290f701f27473bf0383e3474314fceb3c206c0f6982f2ab676f33db901ac3606ff61a612543c3d87c9d9d33efa4fef12bb9cfb0fc208d772d3ed0e072bddd31fd8204301915b2c5312959315d317f5d14968d5181311a4d670fb936a52447d88796354cd63e7e757758dc1a550017fb00a981744b61fc661a21ba46190b93804aedd506af7345e4e8817ebfbd9a337c7f4256187e258a5208037a60a2b2405176c0e5f0203f2a9f95201c810869631b39451b58be279408c4fd4e1f81e379deeb63c1833228b914bb8887ff330990786fbdff09fb712380122b2a4bce0be91f55fc3964a8d8b6de6d970ce24620b3d82b5d756255b08cc4ac82fac81cd66154080f83d0e9410c2fdac6cf6b02c05e13489fead554995258f53ed2d4fea0fdd2fd2fd234ef7a396a131fbb32c988f81d3a6b6cb12500aed766e7072018d6faad388d30b7798cc956053cce0398f1e624b202235a3f9b1f8183f72a225d0f8968aa4181f2cacc1a45703f8b6f5e2117c4914c0602524cc36d14cdad651bdfa3fe5fc47c669ebee38e630b9bacea5062487848d36099a366b0730d8e74a9fa3be2c0371aaa2a81ca88ce4e77d45888327402146f53b81a297947142907c543e8337f6b981730ae8413698b09b4debb428d243370c0a9c0b45a679eaaa054317511d547a87d04c8040cad59282923f7708bada62940435b7061d4959dfe5a40c684079fd783b6cb5d111274295158acac08d6859b46df60578fe71d27cd811032f78e061add85d9a44647633b1b05758d5fd9e02b7efb011e80e9aa47616204788842e497913e1c0df97c692d4b09672501b9f6f20d2e400a04d231fdb2f257f219b0ae0ef743556d9fadf07c94480a25e823bf91524e09334472d75f3893ce0e7c7ff7e33e90bb7992dca7dd39ba91afc27eb6d3f89ff06a17bcbf2f7bfc4c0c69366bd8e34b75d3d8c8b61192a95202d4a08dbfd2a4db564fce97ea86b90c24873cdaf346377f1597f2c6cf963ebb35b44b6e980f243c8af0321131a00aa7262d68d1c053fbe586822c754acdba68d72d1a433a10f266df6f4bc4ae250bfe38d7fb85f48ff3d74affca1b2fce8942f78570dc2b6fdc42f49c673ee2ca4bee82c0d7d470211cbac6b2ce2ec14768c6a8458bc0632b6f1c751bedceebb14f01d4311acab08f6cfc0ff012c11df062d57b745fc41454fbe4791f7021e7945f1afaecf045c3d570812b38c02b6b78089a2fd88ad301b14a972e90c2ecbe8082dfeeb752700817c22341f1585e77a5d8cb1649546ac7a9f39581d5f7bdb790e008b521eef69316d4755336ceaf8dd2aeb5ca068619f8aa6dc7aa20660bd9dd47842cc0c4dcd3b85720605887c0f161ee2811b8efd21702038f38be33c276f9439fd74993b9b96781efb22b44032238b86d7faba4a908dfc0b9c164d252aadf1bc5696fdc84df56f5efc7f46b72876229274a7aec240298a0081ca67d842aeed578cbd62ec4a46734a98825e4ba2d3a4bbab193c25f573c72c23ae33d6660dad608c1fc9ed2aeaeddc066466c16f689f9fc4af19805782ea211ec59e029aa7a1c050688169f9e16dfa427d2073005dab6b76c9c0316560913e355aaae262d024e2905d89e40360a5581a171c888e379b4800299a34e1b3edfd368c7c73f252fa00c22b376883ee265e00b87612e812a563e05d762a662785634263a344a3715cd7b9f20ce73befca1a46ed3a5e2e0341e94969276eb139e92927bc2673064d1719454498153e79b26dc5efb478fccf38ef1fb8c5ae851cbfd92898213c7e67ed97fb9e924672071f80edfac772a77c9cafaa20a8955da37df5c391d407a9ed01a178513d2d5ff582cb631a68cd1fe64c97b0456fbbd91def98162865e1d1f8ac9279ea763c4b5e68fe0f3a686a882c990519da4385782969b70a97f73d0035615cedab33db7b0ce23e85bac6c272680dd0015c6afdaa38222a33b873bbdf060ac8f79d145ddbedbf2044588ca92d6692a8452b572f7475f3db3862177596b478d79b834bcb97a42124a9ba6c23b404f1b80186dbbdce5b7a19d84221e5b5267af76057a67751119839a19f472c0154c3d48e849f50ad650ba6d260ef9417bd068f21ee3966f3240caaca8ff43f995d3cb8bca17142e3f20f6fb8081f97664a1c01d5f9007470c381d14abc604366f8a80767bdcd471b50584c3e84ad456a691a73ea004c49b68cbe8376124f2878a78ed680511834380742022c1e443259384ab5f709c418bd225e988244eae47d199bf70b1a513f6b320a2ec1fa9036b848560d4fd10ed3ca5529ce41a737d5ead3d0bfab4a0eda59019eca2fa947f6e4fa2c41bcfdb6baaca025601e80929d54b5bc98930a44fc06fc00cd6b03c89956946aaaeab4006664ff0c5f3a58e25b19515972e6aca92233aee056fe8f69532d65e31c3dfce2708d93ff4f6a4150f7818b82325b7ca3b4bc0bdefd86ffb86e4164e57fe03ebeb01ee131f65e6dc841b0ce091cb81ec560aecbee3bc889ba5634aefcaae086a696eabead2d401552da9199ed11fa78630ba390e75c9101e5adb8323b10a3426a60a2513c71d100c18ae1b582664274d36e5f3bc6b5b1405706189581d415211270d9fcacfdd7554d2d2d0a488d2fc168328767f4008c83710417cf075dfadb4b9ca523bb15404d1a74ee92794bfae36a1244ff460f255f17072b1a083128e6cd7e799a8b0ae096d68553eb051aceaba093e7fa5885955a9acd72905dd20f5c0724b2135e29afe3ca7c6743a45c72c3833aabcca0ac288293d0a644c5ee49a34545e99ee160b2a2705f46332d606d88222ec567973a5e829d4a2c18074074f89fc81db7df0496028160a3f3c6f99422db578ca3965b1f0f1b2c2606227beef9e0894920ce47cd33dbcf90b039a82f64900f34f470abb2e7ec8929e611524980275e80c21cf764bae00993ac16183b6ed8827561b1da250e73234f4d449c0a2e49db271ea2a75c0ab4dc3eb7096071a513eefad1ecab0e962e544451ace1fc9e9acf6462f8324b03fab71e15f7df2f1fe22017b7ab531ec7bb49b59749b1bb387e804d077167a7faa126827e3814946f449d6470d3c2fdc108184a9170daa07f81a52cb95b0239559ec600275d3cc1214377ac11444bf96df9736d830e3672f27762c20343eb59118416c1bd32f4e1b26d2282138473046b87ae4d55384358f685ac15452fcbedc207f02f0f38af2a3cf1e9b2204e3495abe80026d58ba030540938910ac3565ef333939583c2c84e751d0cceb8a83ab0f81ade689dc920fe1b498fa19f7b22a910301ec43f7c31f6cdb4aae4ca2489760553e1d572be36c32900580c7e8a3c9e2124c12d832611c2cdb199b546eba8adb760f4695559764ff3ab0e5823e321eee7a38d4e0065beb6541ecaf125b473e0985904d698414bb35d211dbd818013cc4f0ae175a0406dc0f2605c6716b32d65ce04c395bd95246d108a3865f6d467ab468b6321ce34970230fe93f189468e66090eced8a8346ad178f4ed4f626b195e03beab75a0647005338b9de0e510ec7e3e15c43a95d3a7c1172e2f2263f6143b2c3ae5b61821fa26a2276f1590af846feed572f9382708c325ccf3d398514ba661b267bb8d428a487516dc7ab3c72945835a176d37efa0f4ee6936b6ce502401231022c08e4a98f7945209d9200b2170b309722e968e805d2ac3b54531a807df4d8c05ef34b4ed55974a05291f54f72c6c56a1f7b1cf870c6013619da6918f6b750b61f82b00124e248cf5f7511d612c6c3ad0370fb2558cb9209e03ac67c8f66bb804a981246835b233761b194bd9e9eed034e42ec7f7176e4338481752cdf3fb5de43764d6074c30044e64c24a0cf6780b290e0c657494962b48144eacec94813c46e48a7480b5a7612c52d1c40a336533b43592368107547d94ab8c1a9134b79825b75d6d4718d748c178438cfaa54ab4e4c04b4b1fa5798c55064ccdc109c0e99f142a82b8298cb08f7012e001cfa87bc7275aef85ab1fd10034352f01d8122db76ea5b5a8b4cb59871b3c45594ace90c945bc1f7022b67204a7ddfd787bc67a5d112a289156cfcd0bbf94b79b1374ff8748714d20e4fd1e2cbbc13234b91d284b14802e8182f6c0afbf6045d60e4b854032feb2aa8cfeb0517706720d5fdbbc6d5f33022541012471ae8f358ab948243153cc6d0dec9daba11500ac2490ae67373c13905a9340ccf2f200a0cb6b4fcb3a8c62e6aa0dbfb644c40d10408da9cb678bab3d854a80497d5c2c28af1ceca2090dbb7bd5caa3e8f3bff47e636e5882cdf06a07b65fc21e9236d41d3f8b3d31f5a8c3687e6844ea222eb747fa02be26dc3af3729fd12e87fe29345be7575822b25c4f570167203d17fb530c20580cb95aa9eaaf3a0eb8c943a0a5c8b34caea676e9c49c93490b2ba057805ea58a6b23354c1767f12ee86487459eb832336111133f43436534a43c5417ac37fb6e8eb7e9d7699fed5b36a600bac4b5bfc44ee9e61ed59b49c2ba4f0ba41575f72b821b1139bf7042657096ae7d7c07ba9155445d09f33822728812c0b8be2e75264115ead5fab3c6927c1f4711c6ebf6211da4532294cc4abfcba3af43da95d9381310d4bec7a33ac5e3148672d18239623840ebe5d0e3f16bd118a3866e13f7a2c1750409e856d810156e08683499a791761f4a2660792ba16158d77942e77e0e8513ce20323b89366a8b82ffbf1d2d8fba930088a31e458c336fef451d24d54b7267d40978b02f8ef902c109d5c9700743ee35921043261b66c7660f20216576027a26b38f8b7e37f28970262d5bda934cda7eb54e02a697120e8e5fe5594e18b9a1dc878ff55945caba47bb98d52717cd978f526d1013603434369714952f4a53cc8793d0c186808cf82a408beea4a66dcbf41afdd9c272f7666887d168263876cdd45d1dc12a1c99a717bdbb5ace43df2919b352b19901ec160f634efb5b19230d4071dfc8871693a286d21a1c1925cd88c7d158be96651e2a17da166f04ea751aeab305d2318f75dbff29bcaa3a798908bc9fc8bcaad6249af785339eea71c8ebe6d8730d060a93e9de0aee6c3607fd463ec56b9ce13652663ff3eeb125183cac424345d01a00b3770723415e90d0631e11476ea8476a1d1db3e0f2f03e65e101343a1f3b304a1cd222658fac32753f756af42f6707f3a82718824472962a594f22ca4eadbc90efa8c88ac14924fd505c7c2c1da722e2551efe731c83035a1c72d5434efff97dd22e33ee3c55087fed45e17917e46c1ce3ccfc0183057ea40700eeccead077aef3b16a4da9e780ecbe236caa367cccc03a0424d3a434aaf79a04221653b7f6ebf3c6ab76566fff2549c79291fa41cda9e54e34291450c9077297bf6c2c4865192e28b0380cefb64b7ae78045f42af731b159ec7ed151b737a3da1cb57f5a8134c10d02ae0eaa2ba32956fde897c4768bf1b22e1f250f863adc4323f503752780a7d51408dd025e7f6714ac9003c3b8e33727ffd9152633b941b4393003b0d45040a135fd299c85548f6d3025e82aedc08e3a28ab5b82392cdc62b5873a9b96e34a2ddc4c49b835e711befe1d94373366ff451d9a6e762f833fd869606de50f4416616f21c42cfc9e71062cfc5da6af2e77f6894bbc99cfc1ede4dc9d90ceb96760f90b11119e23b34dcda4b6edef76d5d12b8117c8286c6b759dc906a3bebfc21dc400e52832ef42271a06e1db6901ae83d98f46e6558b01ea90aa408cc2cbe73a7407214ca5d8046832a7c8002ca3b0ddabd22905a8030430899c0ee0a486d12224e61eae91045877dc15b26b076c8027cd7f4d5c638c1b82c85e3613292d7f75b33888cc1233ff0d1e1251f54271644051436538103d42b6c401b14eedb7fa8a75b7a8a96c1898b712b998b307db8be099e124b8e717a618a83d3ab4735093d45265531b15a6307bd0466e93961873e6b91744724990086b43c8907938102d621b6402d64b42b4163f0dfbcb325d4bba92d5aca84c01535e85af375ef2c20028c281e8c5408be4212b107f521d956d4c8c14a9e1eba3814f5b886922ebc84e6522a8b2da70825bdb20474d59a747e7da19583c28eabeaf934383bc2634f06079c1b557de8bc04f604d244a97b61f9e7101f625beb0333e4052216affdd71cce32d1f97f9c6e2b89c309de7e00be96ec1ed6fdbacf8f771bdd722e17c0e6d3474f5c9d511da122e77da8c1c481388ecaf1944d003997a4d4f472c2e771056f062b5ab38407e9e7e268f78921e0dcbc1c93b7bdd13f84c55aabe2bd76d9f752afeef4b9ce96915309cf82fc98d600fc514b9ce680ecb97b482c2d007d9e1dd02055a8981b61c0480bfc60a5cf14c0dc22fa5404a818b3542a5014ffa30dd4200a98e86ba4282157b95a8b266626a9030dc07acb08392b9213cf6e3a52ae62dbc11c20d1e7a0a16ef6cc53bbdcfca11ade127d8dda1090d5b987aab5ba41e49de6893ea4e1cdcbd65e6c5a8d4af76dab2c441fcc872d84dd1f90be59be94ac6e79376c75154f73a965fec5d35952a90ad87bf0ccde86f7ea8ba46f7796e0077ea881d4d03bfb6d4f5ece1a3af4e9b33cc855144244efc290adc7b76b6693281254f6632b1d9457ba750c90545096bdfbcd35e320a886dcc10aa50e0ae6101b772455700fefba5d558aaf398bf86bd8e71dc1e684a7ba259811a53086e6f35c0cbf9f0bdc837adb60330f06ff26dd44d4a28be11ce86555dc67e58b17baaa1cd76455a055d0e1e3d00645ef654192918149aa0c2d8056d181e841cfa65c82e6804a4417bc44b14487479d0131a564212a3d1d6fc1c8f908f1519201998af3cff7811f21f53a1d61d1ce415125bce9515f3eb901692ee3a63739fa004cf35bb34b7ca54ddb08cf8eaa33d98681e959925b1ec4638ae476b449516d08f3c89e5da4350d5f02147be13e94fe7492f87be122f9f5fe52460a9dba50f5049cde9f1dbc4ea4f2abfcd424533033ea9c6242398a6837a14972f4ed11f5a563007479bacc96a3e0b0c2fa3d9acd318f54f9f50f3ce979793279ac3c506cb145844a687572cc5c620f42a3a11ef096148abc39b7d7309bf1b8eaa2ffb18c65751648092a19d4328354290b8dd0c971a0293756e88b03ea4fea6c50c750c5499519ce39eec8d4265176f94e69816e44ac62efd1aa9ebd0fd7a78a37c098c390c5a398e88332b99064fb180a079994089f10f008a96e7d33aa0884cdd8770cf68721e2bec1c78ae4a79a5059e3bc9c484b591be8b353763062663ec026297bfcc77f833b79b8df2b1d2f97b9f82d4c5aefbe396c532c5f66f069fc171030df1406abb2d677357e9323d92ec8742976bb8bfe19f68a601492e84195a1f53c0cbe38e7bf6991265397dae625ebf152ca344af2a8f399b5804f1023e8362b5223860f37ff240e49b5cc7e6247bd1283b63009013a4a6cdfa97295610b4adc5cd3e5d164037a1df29c8ca1ad9f0279d023fcc73572f73df82ff7edf2d1b6c72ed9543168667f71f5fd1ff8c0838c1c4c59c29bc234e9a4e19a4d507d9b51ee7d37086cfc06d3dec929a5b1fe0473594ab8178ce629e5a7c1acfc4b893c5a550d3b4b6241024f201793704726a1b23509446d12fec549a8822922107f3e96442ad20848e5a9a35cc33610ae42ee86835a35d65e23e1cf53e76be4a08155212347f6e1af4b0a112f83596bd8b5f7022c1859348c208d22dd144028ba6e465696f1d9e7fcd504f25230d57dbc2cb4b5724fc82a1aaa78f032da16a4455a8d0e818ec699722b46f26fc513726cc889f96dd4c8018733f3c76ac883c27e1e8e25852822bd7ae8b9f4fba255ae50ce491284902b5666b9e7a818eec92670f981e404ba44ee2edc5e948bb1619d489411e75341461f083ff0457fee26c43e21baadaf53b83e76d32a0d1d0847535a6d171b335485639ad9e776d72c5a455619ab5a9b19854a292c7ad99638155740dade42c9a96946acc7474dd09d4f2eabe19d0383b0b44c87556606dff8b6a18a7ba3c335e5923af326ea0ed174758050518ee059120f83c089755223fb6532731a794b4d381d8f6005ec065352acfd3f3ec6e799d1d0c4149ade7034563d9268994cd1801eb7b742b9248dd2041d9b03e65f9a7001261af7072a81f8d25ec8fb74f4e77233d5e0c8f5e516847e23635e2c4a7f1ab4820fba760332cd8f0ba86214cad760bafd3f0bbf04ec43ef272072779dd4e94e5e96e8aff803503093865d902a131969f98b3e902149d6c74bcaf8ceb9f401c8a58c2e6c1230cc38a2dde1aae945d49bd11bbeefc178563e23a7bc57b1687c1bd358706b2147fa57898b0b6c6344875bbddaa45b4bc42c90824c0280ed54a8d9ecc85b97326809547402656241d837b4192096d7b806d9880f8c9063a1bcc5d518f47eef232da386a6d6f86f421b576631dc5e7239516bfb5d967355b5083c68c334b577e5327bf3eacd0498825cee6ca5c0a2150ced71de1630145ed8957e87a3470127f16d67d3b324b8ab521e94d4b63758a5fe6e98995d5316419157b20fc3e268f4f89e3f1a875c0e5248c32213758228d28da0b01b02d10e0444bb100a764710d18940a0bbc0846c377551b36e819f77fa02fe698ed0d9ffbd792d7579d236c596a166dd31740a49b6c504c254d850f28ba380ea2b0264dbd9a50994bfe7697ba4362b946b3c28923d60d5b5ad809eb4b400dafde389417d96bcc43d6960c5629774a4cc7178431aeebe9968971b6861dfdd47684db4000ee0be621cffca01ec150b759bf145873a15cdadeac177af9232c90d34e3afcd8ab5950316d7b0115593d870ce110068c5d71e920d1ccf34b9ca698d8f62931c845c3ed4eb80329c6cc2bba47a31e4416b9b174d48027d574af9bbcbe2951b27f816095eb5238d19f26e703744b9897cb77e1649a06c61aaf369e442e3c2059dfcb2dcc689bce5c045f312bf308e4a17f7bc7aaac06154d915414d7bdab08765ebc817824326c6fccb14690b7054abc8d6209ceb5b540623874d42b05dad044f1a23280082fbba4b0f9d003c5d288d9ec907263e3d0fc57b950b8cbf940f5cfd0eaf77e14fd0993fc27d0e72c1217508947ff3103b9689414e26a6146f6024e3b0490f6577804759065ad273ee4caafa80a8f32c17b3cc1f04f3408b80076bd06374d8d17ab847776f54d40029e8e84b131a43b435dc27ea050009e1dbf4494d1a59ee9a889656e2c9484ddb1ac575b831deb99d1476bc83c03077407e7c18d297d019f25eb2cb63121b841a688cf9a3956ddb1a131b670c78e48f5640e3ecb2108350c316dd80aacfe4eb482525874a80547761df934c05d070e6ddf3aff8293a5e9609cb61161c1e692fbdb0970ef2dca8c18561d02cf72f333bce16e160384ae18917a7b06dd5aaf3a17547e81ee5452178e037038448d60a220e595fb4e60a43f9388cbbf500c33b7ef6bf8f0c4d03ff0e426ab970f8abed2a9416920ca7c084c7279b10c6c270360dc300d3a1fdd3869eaa89b109a7aa6804123e58015ebeb92cf47293adbdb30e79c4eb51e62f9ed5341c589c5a57b04d006d16b1c9071e6ce30871d892964481775fca71fab4ab6fa2994ad0f6180ad33a16cfad6c005ccf867fcb08e8bb0f510ac1fa300d551f8200c747d0ac5e5b481dab371ae694b456e70ca9e5be3e88a9e2f2396ae8ac3f884c7b457863ada7fa9b05c6ef57da9747a85cc4d570f39607d48cbfb0c9094f20809a48fe39f85bec2164060ff31a99f8632df8505f468e4e3a739925d03d04cd925713dea5f4089a92e70d6816dcf6256632e67ec541a6cd8d7f3369a60fdae97b2deffd9603e385aec6252356309487fa8ed350fa25f5cc97745e036cca904511de7856d1d122d986fc6f56fa96f5dff0aac7373b65f5f2371ecc19d5b60e4360d51f63326a0ed063b3872399bbf893628c16c8083371ca619202f13d2bf55a0beef6425323427cdb572add44d61b994552acdb78420681986e233a3627322cd5d7b37485f9da9c67220158f3b40f5ba599135f010ef35dd7b08ae62775f3732546d46e06fd150d0471fd3bd92c89ccd2c6107b6bc9a0b4c7b5914dfd50f1c7008d91e0c922ab87768d9d5d9e527f00db4d5f5c1e8292d0ceaabb7949516d522d5e3a85bc433bd72412592ca571bfe52a28696d5b5d50b352bd69eec2ca822c70c1a2b86964219975a0a9181b9c08bc136581db12f53ae6110b6cb781e1f2716f792ded61ae1503953b3eca679a3b9463c4724876d733719a998d5d94c17ddf4026c89bc8cc040706f063801834f3cb8cf7bb4bed97f110e581c2d4412ee7b572c19e241ea598b6ca80bfda8eef51caea7b21536da155fa9551ca0411df26209b208149df6a45c84bd86d7e6b2ce2bafd669a3db94e1e3d82055463a745bdc1852b25cb4d048386e3f70613fb82db79d7ea9ccec43d46e7e96549fc84c323610a2e3d7485030ed65874de7ed4d5fd58103ccdf00e717541c2ce9e3bf0f841377efdbe1eeef073d4edb246cd8b01540e9cb1db3f103ae38ea2235146f63451f2a170326dd92655ebccc7e104610684b0bc26a9384b5d7621a865f9630326a8f90d477acfe6750d61bbf306880f2ce2ad9369acf0e3eb28dad0079a0e8ecfb81b52cf9f1752817838f0552b89c42b3815be7050d42d505367701cb3b88fcf481750319d64602fce9295079eb682070db776c6b9ba2fa1e6c407367780e1d5e482916dd7473857bfc41bb2e49d488cde5155376b911fbc5191392f5cfa13722b8453d77ff9b8a11ab48e90a6a69f3d2df833365554a3496782ce16d30185cf35dda491a650de32facdbd6700fd0b7c8c79ca9f8955c29843780b506760d3063e202a639888848b83a634093de7dd3de0d232a440b34b2dd72336b6fad2df9b672a00f9d9237ac91c84db9209d90826a9cbbafc813d22c0a22f3f0c017825b0f07c2fd5748775d6d1c4ec688046d856be1cbb7cee0368bebda49a3f60afa53922463c7a89ea31acc694e916dd6faff9604d9a1710d49b9a834e05ae75bfb936480ba537f946279983049408e557cae02fc2e0de8722392affdb49050cea80485bc508f1578481e3c975dc96af64de3711eb8c9c25e40fa5e7df1187b189c7e4b904024f8462c7e871bd1ec47955cb8504a4016dc1eaf35709d4b038af3799cdce4f4e05f59f3e0b084dfa509ace0b64d6b50fdecd98dff0cd7e18df964c5f2c103b0f05ad01c873615807aa51a394ebc5c64adca5dbe884076eef3733e69eb115c1ae17421b6d966e93c39e37f9379517dfc28be5a41c7b4f99c49ff4d01f9b2270d96b65fb6df3d2a20d7e39b63f42d2bc04268e1d552799c024ac49d31bcb6097058cbfbeee754bcf0a3d434ecedeecfe4c9fda28a0e27c87321a6810207013ff46069166f952f61097f9533c98369ccce0f9c9c41857adffd98081e287ea8e823d6021d98af4acab31179cbcedda1408db57ba0c6532d4e3aeacccef4c34fa65a0fe5d0ca51ea46d809325cb79ca644a2330d4b75b28c4e3c91f6ffd1b29e01399231a1f3010cb7743f59a46cf31fb093cf3b1109642386ec5957c3dd5414ace8f32052a492d9365a6729e1a4b37a26a9bd9c16b990e93c1ef3bc699050d90591f814298eaecc488aa7d34173e49ffa2f948b3dbb4aa142c160b582b122d013e9836d0e74b4949f5a2045ad6764f1d19c2da1bb910183a194cc0d968ff6b9d30333acf95a86c3033f1c0df5e36dd09dec81e0ff781b9f274785cee4d17b2ae1d80863c239cd823a62abd99a6fd9540bf8102568beb60b7a517f6d8046182e3bd4dedfd16422110193c408e9a75dbb66af3e03e59ca7aba569ea24a803798e8620e5dba8f5533ec0194be621ae9a861c2a9620129cbe467c9be4147fdd7abc9226b0493171ae50d5702437635221381e37ba4abe9586ec3ddc16357d46db46983746e2596c3aa8488e40de6d26b94828b684254b23724e4199cdfe4158cfbd9198a87d3e6661e7a93289fdb205108a04c61157235858bbd1c46d414158a19835d197aa4decc70bd12cb59ca7f3b4c3487d26286ea60131035229269b4ae43702963b10b380b218c6280357bb7b072a6be6eecd9ca62e415a749da0300fb32a6e3ed5db7f8c8630ce4aea79d7200013925d5f5a82e0ad0c73ff738f1a07e16d68757bb2a05b7bfe650c15d1ff4ceccd7582742f14fad274a0d99d94686c308271f5eee84dd273213f9183bb09099483fa65e28ec65768061c14c685f516acd38dc1e38dacfc8c97700040a54dca51efde61e51c4aa63cb2a5ed5fd16212a88267aa4c7bf63d4b96587e50f7fd35d4855839cf93f409f6e976526531ba0b3fd1a8ab3b20afa0736a47c83582195582e1847e253ec4a08a9ea9096bcea529ed73c404207734221c5081d6d20992dbd2f37225a21e4ce57b050fa184aac41d77a44537f489735a232578fbc7f1eebd3863ee09f2bdb10b092e656a36941e023085176a3cec5d4837666d3200cccde65395f6c8218bbd617530419f298c17e46adbaccd88d74bd11e8521e11d7bde8595cd18df4282d6a2655e10fac036f37288c38a6dded7fedd5b7f056857c78c0c8e0d16a9fdb412eb0f0d989549bbabc438e3a5575aee1ed2466076f153162bb999e005ab05f0edeec2e1cc02cde745f8578398425832254c2507badf0b71db1e7d09c2127d9f0060cc56c5d7478eaad481855e8d582a9b14b9da603e2d8441cc024c8175e23aa57fc759fb64f2e896a17cd405dfa0c9ff8d247e960a748a0a585d13d821b935d33c2e0840243f99ce9b586b8dc041efcc8e612764e4c9770c10abf9ccb140bd23b704cf07246d0ef3bb327e9178cfa9588d5695a3a808a1bf707472e2725ace298207c4e3b6915551fb2cb46c4617987351e0cda88c9de85460cf22f804c93d65faa4a41e8c4802c5eb8d70d3d454ed0362026cbf6785ac2f8783f74c12391a5dedb2ac988b3cb434c58a08c63eae1c526cb53ee98a0f8a973148122756c0837f966ec0316b84bb2e02125bb0f802c0200b477c1713b4348faebbd8443c4c386faf0e139e8c34a0d305a96f1fa3e06b212679da8aee7931c3c343d931ee5c1d8f7b14a1166139a28844912554ad0e15886ae3c9e471d1db905e3c3f074c9ba46d063719e5e15860742455dab9d32200dbc42ff3c52e4e5c020058f535b2c5517dcd4cbb633c8e257e3ab262bb6bbe1a99e07099c5f27a9e9dbad753f00ef1733aba0da32833d59889e3bac0ac7c2aa958331187fc844282c6b128fff81ea354b66e9c85d22604abf9681094490541d6802353c4cb4492fb80d83056938efce4169030a1ee8fc009370563d9041122527c3f521e1a9ccd98d416ac9afed27b5c001dbc1196551fb8339d4c0bd9df92b356bc04a0c00cf1047c436b8457cd774f3a605fa1f8ccfa7830fefaf9b6c371e979a3f3e65b73ac6fd2771363caf7a057d1ad70ec1f255dce1ea4d9611aec93ca1b311a6c68bd7385071f63ce278e0b884e30fc645a508ddbfa8b1f9e61ea05f27ed747aef99c078b89c7909466e2d7fa9f70c25ba5b82b270d475358088c3c9e8020193f9132f9509821c14da3afbc96d4eaad5f71f65dd81a5f31bae55b7e21f3aa8317faa349ad6b911b84bca424d021f0f102ef5a77f65fab95f50a088a0c5db2a0edec342c56a43d2dbfab844e0c3950ac22c146efeb223395b3f3307ec3ddd385f97cf720c213e92aab7f591773cbf2fc72e98332f29a1532e23b4a30cae0f64071f6dc472eac2d8d9bfb7c80caa580c565407105d6e059d24cfca6f01211f32ef325d3529f43e11900dd5f6e398f9aba9dce8a74232d9f572475c3ac24d277a484b4674c3dc93245b1a35d65727fd62782d150157ab79ace57ed9f33e114aca44a58dbbb0a36a36ff91466f70a7d232d91d14caf11df2bc0c93aea6341fb17e6654d69a2f6f3cc749bdc005de62402de0d00a3193f1ed3e0d888572f1e6441076e32b61bd9c041a3d74369a375787b7fbf32249d6d016fb34005d353c477f57f99d21b035b8d93d5b21a6d7d1f6ce869d70c1c4cab917a1d1ce36e637467f4f69d934b1e8824d31b9bcc6daa9fb5f75a5a6852fc6a1891a40574a16d2c31332f6917d693c4500a4b66278337d0a3445b8c9f3d9d9ebfb0e5aab3f944bbafd2a562fb8015e2a3bef734a00abb9c9f57e1cc4c6c18fcc3b102106d43067677fa70c0f29e84f5fb1d18ee854569a53b5ab12b51fd70b76ff639f4424a457bf54c31e6cb60bba76c91dff5b447959a364bdf8183b87c971c1f61941608639ad9334123440e90060101297c551cc94fd03c9e6ee2705882f73fdc9168213cdc1ca5b028fcbcc554f8a2519702b8b501b696157ba613aa8b5b6c89c7511f01680023e01ce945349421039d1d0d0b5db8fba31f69c61c9cae74efbf84462aabfb49c86ae521d2f3a6c3c519773e164a587bac0b5b0703b124a124c15d5ebe47acc19d5b299f71253b570f2877100e52328b1bd9e8cfcc708139f6ceaff47d0138319df6f073139b3ae92384277f82bc4ebccda083670d786c400032b4b660c8bc9b8928e27bced31c2a72fb2262011608cb757afb1149041f25f59c3f13e470d17a769c2815469ed6022269e2e9cb35d06f9fbec0f41933c20375650d05068fb4f107cea7c40dc6fd9eb3693fd7b451178c4f97714d7c82bc56a2bbb27a15ccc9b3db3a4b800d0cb044504f716cc60981bccd03afd27028843e5b1560afa9f26ecc0c250a1143620fde42f1aeb0a3f52390e0e8b03531ebdafa4983c79bdaf3c29042f325bbf8a35316036c55deafdbb200b432bb8cade008847d36b9678afb302c0424afc63502c67b31a86a106a4440b1c1bce636130264b2cb29fc7832629f4110bc3f81ad66a3aeb68d368e797b4edf32ba25bc4bb20ad26110b1156954788d3f54aa6f5feb00ba507ca811e94928c054414cdaff484aa0e5113ac9f133bc4d86e8f9c7e770bb6e06f78481b77e7fb7763802b84743aa05756a571d8300c57a4e990e7e8565ad85cc645c0d123165d718daf18ae88d76afcc095464cdec06f75566354aa902794a024ae4a897bab2195cccfef9e48b7c89674ccdff141e30d84f04a9b7ea45f7c361e10833889b881fd89775ffcafc15f3f917433756431879992d57cc32664cbd75e6924c6fe52201ad6f0d0af81cb4befa3f4038d7a0e315fc74fb4b82247eb903224fe78e780d5a15538a2607139a0ffa33b5d25d8145c2a9f3fb3198dcaa9553bb29c9aded3e6f07276cde196f0eed92f81951f4b50749523e9fc86986cb3fc184cbabbb44ca9989b4939e2ecff87973cce5d3e06a00b0e508c5abc7dcbb59e63804d7799fe41bfd8292042cb8a5d7ebda8f1a6417734f3eaf97526aad24824debbaea63abc0f0afb516ae3e7f67eb90d2fa14e43290274e3d1b7f8c4c69326b4c006fd8c9216efdea0ba574f016f4f38843e5851846d61ee2249ce1f6ab6a5f40f3a2d5a69c3e813be502e9343dc3d57d3c304c088fc1d4723dc2e43af4bd6129dcf1761625a3641263334916071bf828820560347eef7c80cbe2df71989a04e6efff279a5ba8fe7c3dccd78d2024d89aa97526ed6b300a964e37be553192df94b0eef4b3d4c60ab4305fed7823455482ebfdec53436b1fcffde895b9eaf8660e1e23f8b35b3a44eaf8fc7edf5903dc85dd36f62e769bb475eb74cc3978eb0ac6562465d2a4b10c183e8c7628a9bffb3bb755b7d558c722dfd8af95a31eac54c88b7b8ed96fcd21aa2bd1cbc6a9601b14cfc0a4591841895655d8c5b40992210c73d4023aed8ff2786bb2ee21092ab38efac24107f724e7742cce7ce42583f3fe810f8001ca69b406b18879a2cc45c036a74927e5c4094594d1b157fa9a7b6266343d1961ac2e1216c390df15b5a36276defe322b6ee43766ef2e99a1f94f6536cb91e3c0923fd4741d4b667689f8cb98e65676dbfc7f568f06784b02e6266d492788c26baf7f36d1322298944767777ef6608a607870710164452df509351c2bed764d26ce8f3afd70d419bafda5071ed6cd0e76ba60a33850c710afe551bba94271724ecdd10b4999dced0f619a2d9ce36be53f4cf3ee639f8cb1ef7ece442cee555c74a4e4d4896082e5ab08839c1bcb8ac6831b1a85829ed8edb74d6326ca510a198818a95d2eeb84d672dc3d7564a4f90a1047a0961244451873021a274215c2004500458f0a62fb343104282d021882249a4e8557637e505db1a61083c82b8c12807498438d02e75084bb28412b6098492fc24092287243d488a71ce387b92f0acb2992892f8008293e8f8b8492bab841daa11f5c18f06b02083830e6ec0c376084b0244a6410b7acc44c1c4eade7b6148944082040e486042c4a29919243ff43b67a2b81709907e67a2b8d7670afdce44e19334130545a20449911f581040f121caa7894f10fdc70231f0f1c167f5237708f369a520480f1c708f1d2e137c24a1059b8d02b659a553be66b080246d141b083b138168ebb08db047e0c303770823818f0f3012b06024d0894899081288a05b34a3744174044c46a044d711f84ca0df10db3cfae732c506a2083e8660a040102a830cc80c16eefd319df0c1633a114596f1378e9420a103040d829e68a0c5c451106030248a0b72d08307cfab0ae67522034207da1002d0a59c3206e6814337c514534c81733d1c36a0a3a3a383633f5328650b3721cef5204e8c35310151857e33800e9fa6cfcd06cc8436f2238d51ded474485f76ca3f0c741893080944117a5cbd5adbf60f1f99d2b50e614044e951015d6a1da399f42f26e648ff66fa878108ea1f026e000425f00325d14c0ca200bd98084282bfacb0a49991c1cc0c60475ad1080fa08d4c810aa40daf8e7bee86664c783329a594301c80b26b2b951276a44797a80e61468460044a6dc203186dac017ce2862292700e32455c0530233fe0c10ab6112298911b2cc919c68f7cea54993ca68eac12e7ce5cc58f321445ca50942222a1294853a8204da9927414f40a82f20a8a220434a39493d62969b5f6e24ce24ccb526a596f526ab9719d84cf4581df0d49ef9351a447d4a1f491477ce411d9429a4205694a95a4a357109457501421a0c88a3b911577e291d85a154123188345700a3c8a52ce49698d724e4a6bb5b45a7b2fc6d9c559a669396b2d6bbd6d1cd7c52eeead63696545050b8b69002b6056a1a5f721a0c36781e17d03e8f04d262d75c49791cb24185398b556ce84dd83c81146448aa04482fa07e389fc456f86c786333c367cc977f439e78c2fe79c734a2f760a74890318ead09c53e6074f13439307a8cf39e77c18240d05e694d9d95a09789084cf806748137856370c39eac1901f386188149e1bf418f40f46126698d8b14f0fca0ea9941fed391f98bdd20f41fd5297f4619715763c6b583ae474c78c91d28fb446ecd88b909f6ac42ee1173bbcb25fc8e9c54e32077677993136fd5c8638bac7e3c890098876f3643499b36618949f6a6872f6293bec2feae9ae793cf4fd1c7934b1b8896d92fd075c7af677850a718b1c2ddda013534feb9a0771b2aca716bec3e8e2419b98e1e945684365a841a74fbed54ca7c4d7c68cf340b7d4cbfd97643813638c94461a299d1b4b9998930c65fc1ca590b743588bca34b2b33204daaa41e5a4603527dc23e02240c88a48d16c87b02234c8455e1a5624898da5435811241cac089022376c22544a448c8624a9d02b6031c15a4834ac256445cb878b2685978c0a4c4b87b01f8070fa01a6613f2cc1e2879f16b01f5aa60e613fd8e0227e7e584f117a8a388c480d6460445a30f3d221aca78582f5e8bc80f13801003c4eec8ea3e16e0f79c6c757d950fa1713e3023b840d79d2e34b29b53a7397f50c250d07254d0c84bcb8b8fccbf810d2c382223d4e7c2022e4d4032b318112a21eec0009306841910c1244870829e061c20f5e3034e3458d1cf101002d2984bc6851f1819ac102e5437ba543586b08335a35102253440adc093a7847d87920fc702145871634e8c1e2052426891d4e4886c0140122e4a5c80d3b2e930a90153358802f2246ac1697a71ca92583fec1d70a76589ca840c25a690901526af9d8d952d0a1a3f2830342cf06e3d14b04c93ffcf4a0fd60034f46858722f450f4a3063c30222dd8691181f5e8b49cc0038f139dc28c342102a4c6890de2e3f82e2e905154659e30e79c35c7740141869cb107324f98fc2bfac010259d001dc28450f9d83d98ecdab71ef0a92e1d21ab2c03c50e9224e4a767df82058992c3bd7eeef367cedb3e7b5d7c1dd4f5af60a261418ef4f938a84f1f9ef4f95477fccbd1bddd6ee8e7b8cfdce7d27ef96deb9db7d7cf716fb717f277a7d26bfe9d6cc0de7d1f0d435d3fec340cf5eeb9aff4767fdd5888fde5b5f7e9dff88b28b6e639ea2e7939aaebe3788eeaca2eade24d1c4a1a96173afe45b8bda0c2fb6ae85ac7bdcedd73b9d32b2baf792b5ad7695da77dfdeeb1d669f8351928b20c26c408260428cbb22c8b9fc182ecf44c975e735fe27efe8cc9655916371b1df7dadb3ff56fad6b6cff5597abba3e1a86b6164c7545d441c870e663c1ea324e5a639cd55eba42a74a17d09320201794974b537b71562da4b45e9c6999e63c65cc4f5c2d3e17464ae1cd3429432f7c638c30c21b63a63356abcbcf438e74f9bab529e9f2399f8e76dd94b1636d364cbdbe84a044117a99a498865e364cdb4d3251f94c554c49d184f4b5f0d03121459f1f254c7e7c9eb87ce2d3c8be85a8454a4b510b51cb106d316a8951de32a4b3e2b1b3d219c2d29134f2a39473529a693adb3af320d6d90a3de10e84d0e1cf185c213ffc38840f1dfe6441d144872f5f4d40822b6021483abc0fe1c30057b00f4f4dc03c0113c48b94971c5c945871843bb92df253135dbe9d73c23cd1e5574dd336edb5c673071344979fcdc96d597e91d2e5d3ed2587d8e7bdf7deeba244972fadb5d6da1547882c318cf7767191433eafd7ebf57ab1582c168bd5e5b3c458622c3196184b6c06afc2937fbdedeb8ffaa3c6baae8bdfe5705f438f187ba9d4ce90feddee06cb5581b5e7300ba77d2956bff4bb37c6d8f11cedb98fddbc77bb8fb7fb3875a47fdb5fef9b1d77edb7eff077a94d6a3ce74adcfd9662f5ed1f4bfa524a193b2d4bcdf29147ed3b7e32ddfbf77e6a256fe4e34fed44d57d11c57f2956cadb6c707f79cefdeebbed8696f7b7971b96f2da881bfeaf2382f1ddeecb2df5eabc6bf23a2a49376e37b5d3efdbcd8bfba7d782bb4fad341f2c249281880fdd1e756bbfc495b62f456e7b2e0524469e67a0302fa4d0baff3aadf45bd67d1d916e3fed087730abbefaa2abeb5fe1b57870e20ec7ee77b7ead8d56f0ac8eb4fa580d814abd3bc8e8aa4b14829205185cbe2b3e50a256438f3995e9d965c43fd3b8499008a135687d121cc15eb2678d205d021ac044450e9bad75709758c44504f4087302478f423a4749a0e614730e94718f5dcb5aad571cf9c56b1b41a638c91fbe882b6d9c81ec65a5fd59237b6c6fc2d3c8e4575bdac78d343a8a2a4a5b5d6e644fbdbdafcdabe96756f3734fcb9fe0c527ffe714102259552e2583fbe503fd6eab2fcdb917f2ebdf2eada4095e31d3ab47ed590fa5aa5a4e76c5f43af2c5eeba8c25175c4ea1cfc5b8bb7334817375ab850b554fcc397af3f60fdba822bb4f8caa9f6f06f38bab0597e72f1b8fb7dfa985abfaa58aa2151555fd5d29b8dfa96e7d8df3e4b98fd2e2d31cf925fe6fa5ab659b57e4cfd53fd5dbfab39ca8c371a9a4ac885b7835e35bf6a48af5fb3177fc66ce1719efe18efe441154bd2542df6c23fecc257f06fb77016ae827f2ee87f347a45d56373d4158f965e8bcd189b356be87f2eafc5f2aa1295e4ca97a304371762899ff0d7d7bff917376bc6c01dfff076b1ac2fafbc1751ec1cd5555dda735e5d6d36f4e76c74bba1bb7e9a19a3fe80f007069dfa10f997a3be56f54415542c554f544155ab6fa01db481baaac714135f8690e353578c31de1eebf4484a19371b574e16d0d19c76cad711ad14a94523d6e82334ed0787ec13f75afa9547a8036d286731f97b13ce19a7ab1a551f1b7af782353102c684ca26678452075de6a07fda098c69355d37084c1be39cf2a7b45d0c3264cc6aeba534a8763709814d5619a3076d706643634a7717c3a501014dd2c8526bad758498a39b0dacc1a861adf24c0630c08ae594d55a1b657f7048077bc85a6badb5d65a4e84ddc4fee050d05e6dcc4449ff60c4a2a0d5296b30b4f1a66c593b9cb57fd4ebb67e08ec973aa5d4c6ded20117ddc274ebd2ed8a9605f49c65da4daf1c9b3aa55446eed1f618670f40cf53ca38352e3eef70aba151aca5003a7ed1b1f4663af6643aef30063de739658c314698356e3e0bfadd6a681d6369b34a31943ba8b45358e22a82c792244b862ce161490e3f51f869c2cf9113fbe7c9922292c42543e4121ee6921ca2d0849fa39f218a5f3f40d9cf931e7bb0d7da1f1f3b645576c051d839e8616b60adb5d65a211fd4e55fd65a6badb5d65a6badb5d65a6b5ffb83af24ecd78f919f20fd8341257b30a20cf832aab2875bd41e8ffc3d300e8f26f3e2d79faf17df8b389987bd086dee4f4f429bfbd1eb57e268afc7e334f9009e5ea963ef3da8827cebc94000c01a59f3a91003d6480674f93d0dfa9057ec5168135730f51ab4d70c07e35c9c8c75f62c4ece74f62a4ed674f6284ece3a7b1327ebab35ce7af4626ff78c35debc899335bd791227e70865a61ce558644d941b8d6d4ab8516ef3341c9d37bd71b9cb3b42b929475febd9dbd2cbdd76dacd563a96980893214c78184105a9439811b11cf4f8da8828460875083302a87f9e114718f16344cb889d22aa4c89b688a2c9033d835b853ecc75082b0205521701d421ac889f227c8ae08151c44e8fafa9107946082b4fc49210e490a20e614ba2f48fc296c09634f143b4246849123c2c71f52893e49920fcf820a20a8c88294410a1fb60da1c5022842c112cb844a0600811ae0e6144f8101184886f0f51a57f74082498e12b5990677e10e30b394b29638c415dd6f0ea5256d1e9f5e5131eb2ee441d8cf1c517637c31c6f8628cf1c517637cf195b4da4bedc519ceb48ca754ad66bd694774d51b67abf7451eb53ec7a3d6ef6ac5b54229528a54d2eb4f22794432e975066946da8a6dfdfa337469abad3f41108362940d34403660b21077220c336cdd31a44b5b77b4bab415dbbac3a74bbc83d56594724e4a6b9473525aaba5d5da7b31ce2ece324dcb596b59eb6de3b80e5feb7d3518d9aed376db5249db157c6bf53e18d564d2b5a545d715f663b7f862efabc1a8d75761d4b1c77284bd6f00bddecf268dbd4f01bd7e4b0d485de28f1de3c71d7f35c47a9d405d16a0074af87aed21e80d11eb354b3831129372441094700118b0800712c482d4600418ca104143a40036c49121820ca14495a02694408248081560828fd70e4c204594222742501102e80119cec830617f75a86b3a1b949456496b0d9a8ed712a4810fb71a2c9246f6ee3b1b1bdad0ef78ae1eb4a1ef411bb8c1c023871cb61a196bbbd19091655ace5abb5b0c32660d7da86d36b4de7dce3a6e31742e7a8380dc68d0cf3fb7aedbdfed128636b0e4616883e96643db6c687d7fd63a6e31ecefbf6d10c0b306eecf9c9e1b0dbaf766a3fbddadfc7e9d6d35b286af56fa150e79f7b177fcf340df1c9e5ac01c9741da3933e6e67d598732604cae0d6933c87046889f0e614a98f402f4adea23573c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c2c146beb684cb8baf63e301448297d5e79d39b5c7e454ef7db77decb6b4fc6dfa0f4ed5b9ec050a0e404417dbe667979c96ddfd98853e8eee5772f5fef7ff13eae73f997cee5bbeef5cbd370a45f9eb8dfb6efa3e1a86f0f3b0d475d3ee7f2dbcb7ef9c2735a3e476d752e1c4a1ad36bdef2f057a8d85a4c2b5f7fe31f8b0a152b2b2b2b9dee563a5d2a953e7b2589626dbc6d1f7f83313105c6c46bfb747c0d0543baf6baf3be6dfbaefb72d456fff4f63e10bb7c4de39fde3fb7df9bec1baa87cdfd575b5d6f3c476d75cd77c7ada3968f145d8ddf14cf0fb8e85176173e0e386b1e7ebd95ababbaf26cc897efd9802c501e78e081071e5eb873ce3927a594525a6badd55a6befbd18f380e241ce39b71a7247951a0e281d503f76f418638c319afa8c4929a5bc73ce6929a5b4d65a297c6b276a878dda61c7872d484eab0e9d9f2575d578a190b30c710a2dcf8f74cb8a6db000bbd65e7ba5e7fe6ea7184acf3d8e126fe1d83c5fce7e7dddc256575de3281f5be35ff6f1bea7e5a82b6833bf63f55943bdaffee8f36ba03b34398dd03a42bf524c798a95625d38e9671e9c35947f33ace84f6a2705447b69427dbee6d2a0f4f95703eaf3e7869f14901bb4216b03cbf5d5279d520e02554bd25c554bd2545a69f4ec572f5afec5219d7ea40fb71b3cebacfafc1932f3e8f32b0a07161943478fc751f5c403c49f3a78af5f43b715876d3d9c594c26ad4f80324893623e82b514f6579130d28c51878690869086909eb073b2aff96a444531a3234dc5cab8aa87d757852244b19014b426d1aa3a22c5b61f85669eb06347aa55ac8ee56157f206ab7a90928690a85451f560150bab7abe0eb8b6aac7b2342519ce7c15a9cfaf2d7943eb911eeb6ac6e03e729939d6388a986fe4db63a9b2ab6bd6ccc778bb0ccd4061e7e0aff9b1bcb94f806a507dc91b2842f26607dc72fc2fa6562e50cdab4aaa8fbca14aea8fbc9190d6992860a775488f3f1385affacc9f81c28eb548de64b16a246f8ee48dd682ee5fec5f0a4854817f3b9c1c752279f355571d923710e624d6e75729b5a8cf8f5b0ac89671fa37aa46bf19e27d6ea398db1492f604a51ff18f0b128cad8e7ecc2dc658c552f5a85a916255cf2375fb8429f2e6edef2379f39b8abcc13cc63692bc4932b217e3a26e1fb5c4fe2a50b70ff5e9856f9ca7a3d25b9d1923bb668dfdedb2ad4eb7f6a7db123fb5a08d7d5796fde69f8b8e7f2e5c7659a26e5f7ba6236863fff4b2d99e8eba7d94d0b66f4242b7af7d3bd6ed8948b756e791b81fdbb66da9157d8ee3ecb476db78238ed6da4fad3eb59362d9b9c518b768adb571dbb60e68d6e0efb8ffea928e5db18ea18053401ee38831c63805845e6fbedc5027d8f63fcd55a305fe0e66bd7c0458c73fadadadeda7d7627b1be3bc17526c78843e3ffb34578d3b3d2d5397876babc7c7ef82bee00a2e3aa62f68833f7a9ba7250dee5e1d7ff6bedd69131d7fe6eaf88725a6a596c17cc2ceb15ffbacfbedbd1905b4e7c71823fd5aefbdaf62b1543daad6b743d513e955b1543daa96bcb9d762554f8c2a56bc5cd503352114d056b5543d1a127662e24b6d8a7ed775db0daa31a6b7c6faa53430c6f83ec6297b59bc524a69507c5333a60bd7e71928ec9cfb1568d55f3a842d31a5d7be79c81b9dbd236f2cff52df25c99bd566c99b2af8394a898d52627f95835972d2c1df0424f498ed0ba6b5eda0092f2fedbf2c64d2993551e7ff6096b0fc409b78d2e9f15131d8f1bf2ea9c3f898562c4b4cac97267a3deaf1314ea552a9542a95b2a914b695bb32131b27a5e4aab49cd4a42635c949a93da79f9352729ab37393fa3797aa47d5a3eab127c8f9eed3afa1c77a6be5ea73175f9b69afe9d42ab5933a92931fea4eeb4dffa6c51bf9375b7f6edbf6256ff36f861c5a4f2eebdf36ad498d430a31ffaeed57884d398ee32ad538aee2e7aeaaa56aa97a9e9b254a39ca95bc9ceedecb7de97e89a3b4ebfed2d273eeb3ba9f7f1dd8f1d5ed52f5749eaa27aac0a97a543daa9e2ede1f4b14f50074085bc2b5844f8f3e74085362a7e76efe497ea9ebba2ee7d3dec658bffbcf1fed197e2105e4eeefba52a92b95ba52a92b95ba9f31bbfbb05b29f1aedbddef5be232cc5efa6ae870bbc17df73364e97f73986f86fd78c660f9cda528ed6751f1d9ef6a897f1dd8f159a0defdc6f9eb73dee6511772b47ff3659134d1f2949ea19dbc50cc6dfe4deddced5ebaa556a99dedc8bdd9bd74dbb614eb6edb86b7f930dbcd2ea5fbfee597e39bd6f9761e9c1a9f81f62c0584456f12e39084ce9c79ce24aa740374084b42684ed5103925f664985d72bad5d8d131bd57c5520db92a2539f5a1766f8c543ee5343e7c98045012ac17526c9b658ee45fd4b2965d16ed728285532c8af1c414e3144b4bb130c678c6283d9ca599a3aebaeedb4adb665ea6cd6c7e0a480a480a480a088b93d6071254908842a137df43ea07e810f62428f62ff28f7adf961acfaf3932e8e8f76787f9542df85fe433c8ae6af5274c3a003a843de1d1f3d5b6baba76b98d6f474ec6e7ac3f794efc7b35dc5cc8bdf22f3e9d9c4566912e814bbd522c96d44a67915721acb0ffabfc6e9b8d9cc2a9558a256ff06b1b0d1cb7cf013f7bc82e558f8aa562a95a3d725dc95aa1bdf7e25bdfaea00828a905fad2ab54d2392795a91508e494f3b996d136694579c197a3ba6a55f5a886f4a86245d8a79498d2242758ad2068069ada49ad522c15f8a8a08704c054a0f3225281eb14258ec131965a3bf97421627f9508b584038ce6dd4749f26686a4995b1a4dd948972b40ca182a892d636ca24d249f6c2a6938a594d2ffe4cb79e3fe3743d2cc77c0aa85fc18fb9b68ca781fc65e0c47fd4869246fa42c8ad529f2066adca2c5d78d468b989898180b0383710bfa39c29c69fee5af12e5d7748cb939c71999a7bf75726cebb4b4a20c0efe3232cff9ccf31cab44d4148362735f4bcbaa4f150f23d4d26a39d2e2d3a2a465b5fddc5a56321430f9534ae9a5f46310a5a698bcf964988ce40d354d9137decfee4dab19db44bb48dec4fcfc1d336ac5b8ccf3c721f39cbf0c8e99977919ae43e667baffb68e69056de6feafa515e3eda21ddb4692466a6fde6a8ac1158e660c999f2b1ebafc377bcbc36972f9164e63623e5ffdde7db8c5e018630b09bb6d3163c6780a903453c63bc9e72ff333de2906fe327f921c077f99138e19d4c3bc8802e5e159837fc693f1b847b90c4933379116f3b4a359339f7bd9466317499af9d9b68924cd7c548ffd55a297ff36517f7938636ca259331f26c26c980b0313f39b6846fee17d37116ed1e259b0f8bcd16081f2b1efc7b8882e5afc6df1d4fb36d05bac3c0baff4da4663e5f17f341d9fbcafb6562f9ef6befa6345dd445ff5616969b5b44a1ef5bedd117d447dfee67d26a33d5fbb97ebb963cf19cd6646397b6f0d2a43dec0181067ce938f0b2963431dca1385c4fe34ccc4fabbcf61fb1943044de48b407ff77793db2906fddde3d0df711cdb732c905a8cf1d74d66dcb5814ea9ae4399ac2f589d7e3eea348605eee7fc19f53327a193d04928cea03e6b19922e4c495ccb50f4febdc7f1efa1fe7fe61f06897331e3e9bf8fb5380f6e30f040fdcc67d411f629f02ff314f09ebf089ae09879fe2240f1d37c99ff977911ccf093e531c8fcff69721c32fc4f38de7bceb3cf92a6ae503ef6fc2f4beeb56b035d47a9513350ec963f99923a55f1bf62f913d1a9e8143b1999924c4f4d4916b8c07f96c92c9b59a63fc39f6559863a92371f8ca1a6c89b8c8abc69f11409e5f21fd33f31a527a2579fffbf03474e7e8ac1fb7f1cde3fd7d164be8e7f8fe3e0efbdc77578cff90e9c9cadf8af6508c58236d4e5adf731ef8dfee8a7481ad4110a69c6f09ec57fb3c3bcc43c4ce6e5a95b78f3adb59b9cde8e2e2f206928b700370d411b6a229a48ec95370dc115e4d33711cd18ded36e356be8773e92867ecc437863838034f459cc2d34f9e7b282d026bfe7e15993ff3d1b04b53ef9bdfd2cbc6ff618fed51f9ab7857687f9defbaa8f8bf76d272dde67f23ed4944e9fc5fbf68af7e9fd3a254f87070249b3c40c5324a71f737c2c86e4a19c6c386ba0906cb23ffbc3011bb0cfbf54c6f8185eb6ef795ad6c4f7a0acb9f667d6d09742f6c7fed81ffb73e34a4a69a3fd69d151ac22f6677f9cec7fefbf2d04a3d38f791eea68d6d07fa44e51ac22f68fbc914042f2067f669958977d6283eccb4291379253490339f0c2fc2fe3f9a296d85f7ed58fa18ee00af6a1ee1ec21827220eb430ffcb80a4a13f83a4a17fbfb812eaf44f44fbdbd3c545979ff1cf45ff4e44ddfe748a4242a71f2d471da18cd85f7ec55ef2460ec572e661284251e48d944c246fecd3cf45f246726ca9cd80a479a3a3295492be8abc99fc0294722076cb3f066de8a390ec0f267dacd3b7db7c10cc1854d6c8a79472f6618c94b3b15b0e7506660c8843a9bc60c5c8173cb13e3f4f988fc633792f2cb487a046f41f0d572ad158f94cbd1db75d2a95b25f99316b54c6ac51096bdd6894361bdcefafde363dcbe33071168e430587b346cb99bff4beba42f9d89286a41eb5951957784a7a9af76da0d79f316bf75f75599aac51f9420a8dca18b594f5c9565d92263eaac78e6f1fc77977bf2efd7eff420a97c629ff5e6e35acc45edd523321c319d41250346bebe7ea656f27e454a394d8fb61a098ec6f77ad804e278e7f595c620640fba4772af1e9926b3128f6a7c5fa7fb9b592df09e5563e226f7cb292d5345addacfd8df74950d4629a9176a44dc9bafcecb5155c213e8c11557963a4d3732bd66fbcfc1443cef775689f39c6a13f7fe63af26bbe0327f2d9a2c7ffa1a3ad7c3a51ab12d5224913ab469246a25c5b8b69471a525297f362ec637fd145743f475df5fa3326fcb87db0c438643803852e4a09162890011c1770000c15b000ca04fb72540976849e9611884d639cec6bbf03277bed7164af711d4d6cc671643a32be03a71e412dd92ea825db654e1c179037935307c050010b3c8c1903e0d4c20f423eb5207fbe0df933d33ff10e485e8305afc1421ae962b16a0409c91a4142ba468c18c1466e66041bd18cd0cf9b9431460155045504550455045504e564d7b7303fab2ab3a11fa2eaf3537d7e32ed47fb919c0a419b5965e772010bd20aae603520a9d5d5ac99cf62b55a9f06f423595add5177c4d2ea8efacc62d08605498721428408b9a89df8042ac8def4feacbdfcdbeb32f7ba0371a376b6ee7eebcfde577af8dad39cdedc97748e1ccd513bfbe3b0775f2ae9edb9cc73744f1f6e37b6bcedbd514168a076503ba89dd912b52b88492740873026aa50fbf5330c8d1d06ffc5fca3e1a8dfbf2deb81a1a124a8d40d5676b5746848200000082316000030140c08c422b124c8d238d5e30314800e7a905060469a0a04b22487511004710c528c1840003164406466646614008d08a6c2a56c1f42da7a304f435a7f71c507e29e3c383190c44acdec2a68cef551009d417fca17437877699d05d3d96369f2eb1d1f4266720d7750dba68f462e68cd529690779c447d5f64a938dab711853dda7a2521fec87534df854efa41546b0d48e1d7d31276f59ec4842a67504237525648c651295040ea14953c28fcdfdaae05e72fe3ce4dec206e57857315cbc696a0c073a01d04b28d1aa54d7e4babeef14ef386e99697fc8041ab6eebf2f8d12e680ad6146caf1a26ffacea9e105490390912ac1009ca957342cb4534a2590f75893652615db2e4b6de135a7c1e8871e4daa56cd992c761f9a55f0c6b433d8c661ce555b04238d0f3ac1a4c5f08806ed27d135b3b5ceab8787abc41ef5bb1291b6d658d4125372092df6051f8890ce428436d8e447786329cb605bbf64219a6e00684970965e8e1a00c2cdc02eb7b62ec80956a036558ef26b1d40abfe09200182603812b70236c5b5f1522ff0f8ea372274397eb3bd3a8ef20c90df4b6c13a13106cdef6b942cc7e6d0a5c4bb6cf803848dd5a64707adb9be5f2034c317adb9bdf95085970fc30eda0b71d642dd7d7f5d449d049213fa56479799eb4a4efb0bab4fd8d8d85955bb45afb2e03e88467c4ec24070ba0179e835d0282ae078969869eb50f3630f1298d3721080b1fbcd9508c08f3a3059640aadbfa51978e270eb307f63d9512535d5fdde4f9c9e84b00bf5118960dca144b03d28ca06021e27db8ad42fd1d2f5e00de9055162044a46b8b231a1698f168d6ff5b8b7354b8657bf6edfcec912964754f0ac350cf0009d85153406055b73bda4882f73577252dce5e2a3809c7f6f531d04ca58c73e0d3b6906cc121cccf72b42be62aa41b9c18f43903331a1503a18ed17c6ba00a5ef51a7b69035de9a9ccb3ef9e494de575b7a37db0cfc50c242df45537db0d79ac1110c91dd5e619d1e50193e0deaa3231c3b520ebca6659d886dd20c2176ffb61f6e5264ffc4c01d0f191e78cd749dd156e8be30888c9733ca388822b6ca16f6478aaf19a8107944c8652fbe839bfcac659c39dab571181158357768a3ca29aedfbe33c1185202d15047fdfa08c9b042cb3677ca34733ab112d290c9ca21b60e2386662059dc26db027af2921381e073ae044c1b9ddb6c6df18c9eb185d90a0205cfbe93e2473fa77e5a83ee1ed641b725458dee0d5809d1316af2d3e07c48e6cce88f7431df23d7735b8dadf2fa22772c3ff7c732d2366f279b24f8f67c07341e2589359dafb07c595fd02a051635fecf3d18a6ac9b78c92abe0d3474642f5831d17ca6b48eb1b911a024682d2d86c15dd0ace3e1093669074bd9f81e2003cd1dfc34c5d3f79c3d0f3964f3eb0930205242d5c1ad8fa16277063f8f509de4233b735cfcf51cb07e45cb8a0368780b977c0b0581053ddb12c0a9e87fad2f51ec801c6a068d828a205dd84f7c1a8e76da15eac7b80617efdd48c8914f04d1ef746287dd57982fa7e5e921985007cec3dbc5114b7f940ac438ec4df3dfd9c8c5ee35d881bf84306188bfcaaf100532201083bca647fbe48ad4fc11f95f2e7859c2571418b9873a01aa65c0747c2a3e05609d2ad930aa95b6129a607577b252134433d17b915ecb86287e20cc11238cb6ff8314efd26272646a308150583508e26473f568cc0a7331020f09aefa492ae6ff64b2eae3c966b4cb3a790f45b64c84318d83660e93d74dffa4ae9d6db66d0f0ff0fdac56cc0b3a9caf8b994c1063afe9765fbc8d0f4fcc59db337121b05f88b244e56522e4722d90b7f7c90058e8f5018294ec6b2fabdbca798c325fff23e41e01c27ff380e720cf79e71da807970fc10243f79a951be6d5cbf7e6b306d2a09c51332225b6218fd33f3b947c9f7c3c18d6df2ebbfd881e80b2e16cd7ea3b12f0e2161bb8ad9c5258e900a667037bf5ecead3fefb7c8cd992b2682d65aff262a61e660a05ed5b615f25edb8ac0dc670a2c600520e4ea8a8bbe8f4903caf001e9c9aec48f482f8e2362b99475a128e40086cc1c8e91f0fedd063b9cdd00fe171cf32e3c46b7258bc349c10321e65f93f75e2e67b52f3719ee846ebbdf2c1010ac1a22e845391158cef5c51252538b5a9567e1f8a4f87b658bc8d4ad609a82f384d43652a5f47c090a176e0214990df38763975d84002c536e26d565f242a7a70eca3cb165a5eb98ae631eea1803ca253549509c22de6956dbadd83a8fb4ca69094098adac7a14796f3994c0522559c718d311d7e087c5b1339629d4b21a427f98c10e3bcbfb79d9f9c56d0790ce3077b6425e47ab35171751a6e69498c53a043bfac51c5c87abd9fb2223a58bf750c03787b27695da0d9f510e0bb9ab445b570b573f1c4e6b7941e6af4e4a7a23de3de4a002545f129203bda74f33ba8bfee3198d42be50b556449c648607a7fa0a376b42e1609f2e74bf094ae97611122ddc6d91ff9292eecefe2c1a25e42247b50a98b292f1c122fec715a37716826523beea9c84df016ab05aa7acf1aea6b20385498dec0fbfb749e99325d9587eb6a86f6bec8e107eaebacf66c2d22fae28cec376955582697ab50ca2fa43a88187ee3163ed9f98f597fe73e32430656430b1155f602af0bc8ba71934493f3f1f4a8f78038083a00c8117faae8486e6d5964d9cac33510950a3e9358a21d095fa0aa9a58ba06a8840c2b9b5e6113945add46cb0eb101f1a1caeb48b45711d135fc4c2420824d806221df8f0cde0ba066b16674b3ead316ebc09b3282fd0b571456fb3cd8e7d639d5610841b6966f6b7dafb740cc02f135b2fdc67152fa29a8f7aad36331f60cebd0403b7a9e49c42208066d83da3020546e0ec26d7e7941829434aacf85ac57751ebebcb32dd830bd96151fa03916a086c9352bc530369f4689c7454b0ede0ea5c1520a212c12f69d68911af7eafd1c1ee02b1ac018a75bd1d24b49528cb0a62d2c9b018a43e936121f511128fbdc4bbf495501d97a41d4e8f1e3411429611f28b051b704d1f5b592214d07431f8a390333f50cffd2d2ad75b353cc9967d1fe897daf4b81a5c208d4cd23331e11f9f3d22375753fe007957e2e726efab7d4710b11192197b6d81ddcfba93dd37481abbbb699f19126457b97cd3a895cb4a7b5eea80cc85788cb4adc373fc96a4e89386538e1322b736853e01dea47a40700054918c6ebcc26e4b9f42ff2c9a8420760e13eedf40d44b359f9ad31d975e9de40a77a898456b35021a78e716a0d5c47804c4e67e04725131d5a24902c8cc3671e348c3cd4d577063650064f67a0e2128713b0219906795a5b00f23704f9f4c192e81f4ee84ba98a5aef1a7568f0d026fbc87699a9b6c2f9c970a5a8fe4cbbd3a2162f7d7997ab6d84c38072c2f1ae242492eb18da3500f990660f22dbe75d96ce0039aa8bc3e3a27617b778cfc543c87c93150892fc45dfd804b182e3790274fb74340cc02422e80df8cdfb22f55468877a71906f1661eb441b25531092c8655218bddd84cb870149acf42fe334ddc23d225acb26a344b860d7dd822b1ef85da727955bc98e2525873e122e84a30c1528b5f26f9f604c174038260c5eef7b8aca6fc0c724a887174e4e62df56989dda8716e9bf669edf0438ac719e445d713e1f149782529a5fd74bd9d6257e6602818fce7c0238a3f2caa536f2be3c1adf47aa4b19683e5c0b2d908cccd0575dfb0262a2e836d59881b3f8b920ba2292fcd0e2b6bc30ed3c931b32598681031deac862e57337168b32f1e60baa635aa28bcb9a7267d4fc1b6ceebdb0bb518cc6a9f08391b71c615724003b38e34ac00567dec87989a363a7dcdfd141da90146a6567f4d66aeb734f220f8f436900e06d434c6589cdb8b0492732b3b1a0c54958192dec017bd60c6b0300e73d65da8d6674da08e6a3bc827844d030340cd513949c25019376079604187772bc8b591e2b71e759d0db75feb434120319bb6fa7e0804c681e45fa421782e25d0a5d12cf3da8e5ee18d6ab580e6b54cad90f2a74f41752d846242b45faefc26829e66bef685ed2b4caadc6a70f54e70133ce2de0de02486fcdfd5b8f15b5998f1a16649632f50297605c28c2218352f7fe010852acbadf2c52a9331046380854444a760898a63e2d4cd328ec5c587f05673580e916c6ce9d22ce0b15e4e1150c643cf89372a151881e4d8c114d15accb0402dbb611c54a308c03526cff0480ec1e627d161001603c73d4f45be54afc8a655e5a205eae51c31dc3ef00065a84a6f975618612918f742b89e27b15d4214092f7d18974c6314670ab95fe8461006ed3240e9fe1554113acdaa39620dec7140f44c906484b8c4a052842a3546b3ab7cb687e553693bdac3ad77604a00a00b54e61d85017804845275dc6e185170d235ed9767779808d8c8c96bf276ae4db93af17f9df0802e6ac957eb38201f56505fc068faec8e62563f0eda1f16451daf6c40050724415ffa0eb0703736da5c237199adfcdbb0b7bcf159e952324be486538843df1e0dd053ad1f36ec919f969e1681d70ea9f7bc774f5e6dca71d00a6ea86b0412b96a93d76e276de50d800927f4f9443855f01ac623602ff30ae7f43abb458fcdca82ba51916cbbc72f4b4a9231c860bb5df3ada5abb1f3318bda4b59c34759f994f4d92c98b903389b1635c3ddc56a7825add6fad81e8b4b344952530d40d621cd8ba3e7e12d396952fe6ad4bd85fadb3b9d87ef8f653ae3e18bd8152dac204a904a773a32b7025f42368be69186cf66b503a3d1e8c3d8895613b0b63de0ecdc8e3483970652ef2bb84f3b45ac1a3227838540ab3121731536b6faa2f17b2fc9f1c2f89815ffdc591fc58f4a526362ec9bdf086b4fa83a6106daea53877e100dedaa8082ef31ddb122c1970564d658d84a1e468589aeab5037f09441d0759f86a9e92cbf2eeddfcf8b58c92e3b56a1ba575838eb0cd5a7de3bdd510a73ae7b2118ea701ddd025db0c553c760071cbc51972233b82b74750915d9d2b13c779c163961be99ff2c15ac7e8a320cb82152afe6759757b8d68c9b129336398ce8988bef6fc90267f41cf6bf1f9253d422b0300f211a3d1ba150830d9116ed3adbd5f976116676f1ff5e966a54d2c674868250db97edca84481388da36f0cf1cee8fc8f20b30c12eba19398dee9cb4bbf67412121a0428b55ee9ca3410163a9fba51e427d91d3964e36757d8450dfc52788048b695f808b3fc122307eacb6f1540ffe62f3a274c96413b56f7f0408aba9eb747e0e00f34860f29e0955615a1a28551703b5e104bd30d6a32f064c6c152a9c379c40f1d4e56dedd99088bcf1761db2c957d51533459c7eff3ee710ef01c083ca18b82d3aaaf6c364f74155bf70c440a34203acba2129df76d9919f817bd25d6d336e456dbef751e0053d98216e1071ac7936114aca6617e61eec22ee417f28b255aa848e0455ff15a5c40455e9468e48927e90a7dd80721582e7f7bf44107eedaf05480b0054dc28b068366816b4e36ac586e7df6c1c621bff7314878ffe5537eeae24073573ce6272a0403ecb6edf5196c20e1671f66568a2214c6161795aa9a4bf9a2addf8bf40ac606ae930c24339c7f90d33a7249ca68e2f4d74083f7e7741a03c69f6cc47936376b7a0b8039c4ea077cc68cf8db65242d45dbdec1265a7187561332add0fb6b42abf3ebf487a191edffeefd94d4396cb2a567a2f064692b7dc3e7804667b1f0c4dcd07a199e45a3ed219cbc1caa348d00904e11591bebec5371f62fedff8635d3de2704c87ebd20608d77980b95d4d39a5a72ae237a3be52a9284f65cbc2c8f9259a152925d6f8d2c5a0a5069651b765579169e6dbfb17a3139b3ab6612d4b06ecd9e80f5249fe4e1f9c5bb96e4fac9ec4b5628c01caa042a99952c758b4653ccbc34c36c1fdc8617a4758e76ee886a45acef069199ac747656d6015281f3f28709d28b5730640acb28f29511b7c04f5cf427fb757043b61847b7c494c39a099e516e0f35531d18b8f9e65aa385a55242851fc1f85bdc8df8e71bbcfb2b83d0c195c515d6e77577a7d02adb321164a6b74661244764d22a98b536e964e186f494490ee8932b466115c1061c85162989185d16556b24aae55700aa5bce36c777377f68db0ab361decdc4434f01a15f6902d76f3f6ba9e82a37b800970f04edad77095d508194e2193fe93ebaf6d8f60866bdcd1c84bc6e4b1beb7f39868e395d1d0296f7a9b634e1002fcd048b60966f221a9e26bcaf9a382bd25665cdb2aa341500717b74cda9da8939ccedd12ede2b9d5ca84fa025e5512862aff4008e17840722e32a47a6d30a1672649358861f04c4ea793ad4ebc02bb47cafc84c7485cd41e0d4cdf546cabdac0f7e75331d74797c5326d1f4ba6725bfa71f6f95466cf740feb2c0ce77efde13c7592deedd1313ff1284f1d733ee06a07bf74cd2699c2e960bfcaa3dab4d77bcb97440eb5e4cedc3b2c759d1bb27e591672af9b358f9ee69929d32a7d296f78cbfdfe5e2faad80a35def6d366468b120ef75a9caa4cba99f3954b1a76ff81e5087dc73d20fa815cbd62e7c18eede17d03da5f8f523d43ae460de5c9758a2b7941da45f0e8bae214d7354efe50d857bcf3e18f23dddcfc1bee7229f7b5160e1cc20df5028517548991b2e776263f188132333a41b33ae5a5915d0f1346c052d6ad205766714251ae64a614f889a9bf5be6cf20aa2bd0d04ee321f28f955a2a4efea1c488750acc002e51d20ca3f7e39b59d016fdb59ca706b9b0b47cb5995e06f350f7e2b9cb3c26b394f019c2de62b84af9dbb195628467c1a5c5a999ec43d5928077df4724614d0e5d607e1aa222f0690cac17ffb96dcde5805b6c6cdb78c61db6e48fcd6c7841ab6784e5e47bee2374160ffb7ffe47a7dfc952807c4d90787e524e479226a99a5582d2e49d5e8fd59c0286dfa3390f430d00787066305b177ee83c3123c5eeb99ccd5bee1f19ee8839348c2e9db73d507072ebf8c4fc04ebba907d407c7bce7da93f726277eb1ab7de7894edcd415ac97ad81ae53907e729bfc07203c2a5f739139777f507ef1a993f2327bf9f623ffc495a8aa3688286fbdbda4586ebd2e435de18ddc5ab107f8c5c4d7db8f081d437a3af7e03caeed490b0babfe21d384fd837a018f66b9259becec469fd22f7811abe506721f23d839b8dd7e72fbfa71af212a2b441ce5369df762b002760e99fba08ffb2b508e4e14a6bac1713ff9b12221c713f9fa0ceab2daffbef1171a0cd9e01e9dfc17bc694cef11f12247d6136cb44ae6c6f8b2c651a92d97ce34907d2f039cf9b5216eae014a0af420aa9b631cec923dc8001cd5d77f312ebe8ac5f48f5ef6d7c91fbed60e2c9f868807b07405ec6e5b1898cf6159ccb7d9092bff991587f1b2e6bfcfcea3f8a8be2432dff4c63e69b818d5a366c3d73a4b61831462db3594b2087bf714d4728d2aa39dbc72eae5cc17af9148abb2b1946f2e6dfdf8e23eca744a36cbf9e736c02739b661a9a2cb3457ea5e420141b464cf8ed61ea3a646db1dbd1ef8ef9d92f053fa5b473d2cab0a079a65d1aa8a04f8d8668dbe4ff345e115e15beaa9ef331394aa62bb000d11e40622ba413644d84a2fa3ec0495fa9902daf5753fc1b6537a431e86b1571b9ef26e2fe6e49d3415c71c3947e8c0d536307cabfd94c62ae77370b79a0517bdef84fcf70d0778d02175f21c2d67c19130322e6f37aa45698899eb0cf8bb7f836a250de8aa65fae26a2e4f7a13a15aeb3e0dff68b08965dadc00cb5d9236eb55eac077c805c1c501b5d9b8d734b2191a30733772e54280929a0933a4183d79000a11dff6e72b32a8839ad53bd9dd18fe515a37eb4bff474bd50435eb48c00e8fd973c56004e854bb147beeace32fa481eaa67593ef5ee79dce69356e464061772415df9d5b7edd971a32298eb2ebe3bc6820ac8d0ad16a942b866166464356a3ede008a07aca6e2cb9b29b57836aaea25fd4c1bf14e3013162828bbb3ddb8972624a3000de8a4ecf5d82f6f5cc72d7473324ba06b6627ce50833786e0fa0bc524b90c98f67ab5989219d16caa40c50bb357d94e168182d057cbced82f6cdd796aa02648f2c8a08c384253c1eb1db897d631e96119c228950704e6522a40625f5b45de93f5209942dd80d313e62ca67a9895157a778e681b9fc3c2c6a6ae0ec4741958bd2f9ed84d2842a1bb3f7c3f3d08d09acbb2ce282068ccb38638c33356783de64dd3400fd85176ce0b700fd7ce5eaf2b9ae69cfab369c022cc101a64612c405ca2cb8c1350e83b699901d7d4abb81dd03507bb6a94b92a046c2682148209a752276e6df74bb9f68c5ea033d7166977361bf1155e48ce20664dfcbe882be75bc48d9d2871e6d9331414eeba569a6f53ec1d66095e0cf606b457206971cf87ce75e3a416050532ff8c69c42c47b1ac1c46b835e656b83929a86ef314cf8243ac6ffafeb250416fc85556a22231c28b1216820399d69df923e1f37629041fecc16b46f44491c5f579a67e91d8c29f9bdb0beaaf7f703f5600e95e2b10b664ae44060f3e83c9674ff54024f16c06be9c10fbfa2ca9db07b61b52fdba4b44798915713631bc0465b6f1a0f0497d2fa9709b081e5a9841df20b20f2ac61168f4d4d9df3f6ff628c9abb384d1efd8b6c7f526452e8c3164efce8f5f35a2edcbbea08a58cbdecef53bf63e6e0f49dfd092b8c41e1c824b8b9166dc77cb9de3a0f8f531d2ecb9f6a703a0e25cbe18401df3fa9391bf35f9cab54603fadb2b798caae899aa280855b813f594f6ac8a88420e15672bdfb16028512d284272272b5c1e0a419aa343c8584ff9596533645c279d26d9863ed4256d7468e3811c4770ae6cbeea8d7c36b8b6fa345378e8f774e3e455895870687662758b4654a94e2a8a3d83a19143e29ec060504dac9f98b9561ead7354ac3a217cf3f711b837d72f325b4a962cba875c21bd8cba019b90f68c80543356113d95bc285d8327f97c3db0e36d2aa256cc74fc65355f8dda843f47bad2ea15b1fe16d11563b51d758eda6c7e49d3d4934638543e50b0d5d5429dd97b9cec122f6a52b2f841e57306ca9870d37cdd2d810d596da796c2fa977bb93b4681cb8bf7d4dc1e88b722d01ff2c3e038d8b7a880a8ce8a1a846c06b6ae883e78804eefafc655b9a0ffe4178f02ffd1ecd6f80f5130a953b71524a078e4b6bfb53ac7559dbcf349d3a0d28c99f0d1a88596e2ff47ca8076feb8f0cb45df9660469c2252e7832470ca55b88e35ab5a97c0066de12d40312e762771921b777c08d553165a217414d1c151dab423c3dbda4187df1c6fa5de6b1775a773851a7109e8e3e3bbd8dda938204c464a01c2cf8f8939b2902100ed76012268611d9ce38916ff3f08220872d724ed982e777e9f75207bbb03d0ec6cd207400f0eeec06d0f789b3e22b813c656016a840f3dc363722c31dd4eda21732b4c3ac451ae0bb9a63d3aafe84792862fee6f3e235117ea0ad77e4544983bb1a21121e8345e5efdd82540cde150523eb4ac1d451c6c3945aa25bdc561c52c402fb63db293aea638fb9fa0ddc08e09d5b50582bedfe16dbf5c58747bbf4963071d8152aaaf52852bf3dbab480e05b2833d23ebf611a49d28638403fc97b6211074ae1557645afe37a4a0fac87bd5d0dbe87bd1d6c779116a4fdc9f118a4e188dce2860ea6b160afb4b94da67902be2e709fe3943687f0ec03cc37a5cde788261092894b9b9b759dd30a6906ad14f6a4e9aaf5697206ee554f5c078e24dcb17512c286b36139a5f7a0e042030f62ed5ce357afaaf32661d9fab213e89750391406399d29719891e706cec5364472dfe0be17e496861d5d82f64e87c51ae3638718db2be4b3410025893dfa8951af32ce69b209cb6a44e9da58da3f18d96179d3e911818373823a5878bcde5b2e43a5c23758e116d7922156bd28608bf7aaeb5af04f313628cd5d037b64f8853813d11a47d951eecead874ad069567f74331ca534f6825f2af4795d138d6661a591e9d1383703cd9499b61d5dcc458eda8efdb8d04905a0b5b6dc0323af56215a39fca0789b8e1e5fe524772f58a10aa5474651076b4e8a778fbf604a5f75ec4567c23e432a4a5d8e008e9809818dcc2e3b4f1049829804292b9668075923f5ae05901a0009ee9ccca70673593ca5cdb498bb8404e2271a41405f7970df8f335af224c4738b007f45a39db96e3590772877973935295990dfee2f107abf5edb2dcaf74a264e505f322676ba926d649441e1ae7940a77d8adc2a667cf1cda6e40f12031e242501de98923a5ed799c96cbe916f27561a110d3b9f475fb8242758416c23713c32a9ec467805090b607c94e808b2ecb0b09a6035f017f8a8fd590337dea2787093aad407f320d4f577b9ba90ff7e7daa1f8132b29b9cd66c1767c70c1f6dea1784322ce87c682f79383b1490b0a5a05f042b1b3294b0d3df0149e84bcb81066376149b08423ca609fd193cac0f1b3a428dc9698d99ddf8916369064166b4843b50b2490eb0ecbc58a920b026d0da013db1145856bd82bcbec5deb71ced291d4d1a82b0b605d2810ab72b912658fbca493ccd8d419e5bdc1d3fba0465e5e120a517137a27e0717cc0ae4951a4d2930113a2fc89c266e918c49a80514fea0a133ca15edfd1a50a872d97c88bec8d113d51531b8fa4036058c6243e1569a63b420cefc20ace3d135f47072ce5cd347003c3f64e645efa0bf1f5fe9fee02cf5b1d02109067b436d4ec3403e6536dc972b11d1df358fd7da1a56a22426e02e925874c73ccc9a07e0b08bf2fb4a98b72aa3ccd64747c34ce51d564b557a19e6c5432802da7de9eda11c4da41cc22618f00871a45b8c539116690066511cfca430bde32e17d5fe0761430989e831d262217369146e01927aa70de6b8c1d7ec90082a3869f97ddf2b44f3b14758acd49b8d8343e1c0c209a5ddaa3b68689dc622d1e5b307660d262ca5c9703fd96281dcfd01b93a665bf8435f5818413bb05e5bebe63286ce6257493c72e4414c4d773d80f4b4e14a0e8ca73e7b0e3015fc8e79d1e8658e2bb44d1e5cedbada35a5acfd13f2168fe93e85fcdc4bdf479652c6c508ee8171e613f26b7f6897a27d03dc96792490647aa2e05416bac026361bb452e361c2eb91e1d02729295af66ae0dbabe2023115a2b43c28f42b5baa4ad339f649371ee1d0e3659d7d07bf9a2b32349895a0bc6095aa6b018d3e4dbb7944e0a7d56a200d447a296bd40b457dccd64bd667259f6a13bf95ba988f06b7d25f749e4721c25c59d6684b8f0830879f737a7a127ba05f80a6ff40be32a0c4496274ad002c019b71d3eef65e0f5daa9e5c6551dc4dabb827dceca2611917100f531ff3c61d380bdf4d6f5a507262b984d9bfeb8233c1d9b52e854852026af27908982b29c689c0e707c22334222e51d5cd267536ea05b8b79adca4e4336d8a8b6034f28ccd08d6f9c23f636e507f2247fdfa469029e87def2b4885f2614c40d0e1914553290d39119a95be983f4e10e9191f96df5dae759ec9efe0d08cadee6061c77d28d07005183bd53612a1f6ff6048e29678c872f7b92235664be04dff9932012fece36c713d9a40d4a531ac6303d9f22e59634882d7f92dc9cd61dcb7769a92aa7c77695a8e67407e6c3c24b19d791b538743c049785561d4f7bd72bb2554209cd8c972cd398718e7b98025dcaaccd5b17fa4519740c8969d7e203b1fae13e564d0f8622e150951129a40e4ac311c2a86f7abe212ca14725d3cab61efc60a77290bb45f4e6d9256097d4271c54597f9f5ce94fbb1e166a8bbb84a4dfc774b7221e81e30aac467186a10c3ed237f8256a3a5d0a67b6287e2924a0af97811abca44aa1c02c43016d6646f3d203b856884b4d255245494baf752b12699f9fe350ae7985c79096e8bef7360d5587822b3c4a46994f37c1958078960f1c941fe29a41e0a016208379b067d52601e9eb3f3a19fec562fe23c586f95ac0411df1ce484bc75f678db196ccbf555f5b2278fe926d73cf9412baa69d052d78497aab85150181a747eb25b41d7ec0561cd103a969d5daf1106bc4237b4e233530f0c6c3e695f80ed902e0b59f5fce3b918eb0b0fe280f5d34973ce0a238a45ea5ae5137e8f687cf9648db5f64bf44e7a63a51529048a12db3ab5c4dc465253f5c1169a041fe85f40fdd1882ab5a046260a0a27c48e743807182451d3a87a18cd5c513fbc12f75b3112a6cecad385d150274186771cf821a101a3cd4c09d567f13c46dd14f662d39821495feaebdd1010408022d6d162c00828283182602c73c0778419c91f74c22a0c68486507cd7506721a000ffb4a11438be00403552a442002773b9c050b6abcb7e01781823e64bcaf714fe5df5daaceae250571c1246b99a87e6086e6ed9050057c6660d8edee33f2a31e5128abb35672454cf3f2bffd980b04f2978d05598aece32b69d65eb7023958ddceb6c821b8c99d079fabb3edc645c23b31305be6ca150ffc9332ac174ec8c3057fdcf73a5dd23971244e42c073e6d9cb298aa4b2dbf34d22049a1b29703f32c79d13b50936b31cdda0e052c8ff3a2cf898f07d58129bb32805ceb37a75835c55f8545cc2bdf13a6f88333da5bf7bd6ba0a52e5cc0c32e1c0ce8f11306692d7a59680504934ef3854ce1120025de9c401cc4304c8e5e29606c60a42a20a1f85a5542c690c6ab51e39d21f160bc71ab15462f422a0ec6e262e10313c285c2780b0f54f1c6298da5a776523b6121dfc2b3e0f9626b18859ba5ef95a14013cb2da1947c9db30b051872c62eab7d5b039f8032599d28acb2f213bc23a8334c2a963217d45ee2e8deeb7e959f402bf5ae3a5732aa6b7b570f2fb9f1317abcd0ba37d0a6ff0183875f29363d95d450ac331aa05fd125cf7fc4f084d8775b3c812a8e3160fd3d38abec5f04bb5704e4c11dadd24d5c0a6fcc3bd29007bb47e001c001606c55d68724ac672236bf3613e8803a177cfc944351159fe9914da1448bc18e6fb0621ef9400c1be8aa080868af591787cf5f7c753994f95ff70908ec6224510243c43a830ff6687bf9159833862b14ecb700e6ee203b66beea01bd4d0dbe1750f4534ab38427498c5b42c463484e4db323c6fcf0daec361aa03a49e78ba8a11a95a5d827aedad8c1cfd346f86abb2cf306a156f3a80ca1282aa0d7ba1b3506e0832a051343f320fdc42a9d392846e02ac556502a76cae7a112e57c0bde105fcca20fc3f355ef7ba77e3b68b650a4c293df573ca509cf4a012631a5cc6c0598950fa2085aefb197574119d4e58633e077b31f00058c33819c6b18971d40065dfcf0233b750805679381ca69f2dfa2961499e8f13615f65033508bbf4e910c0660fb5c797e5b4261f5278dffb09590c1dcf3b4d06937195aa3c9e7af5aa0037aa84c098958ec0dcb4c6ef784ecd6d3a9802c96b05fd3fb2a39bc970514b314779818c864b38d4a7ab1fc6384f0cba9fcd9004aec79b8e9f9d8d2b032ca4e10f3c42b062789dff41faf5f2cbae503d8f57cdb12c23f582a61b1843b8a0d10b037fde892e4754b6dd9b9c750d16a62d2b823bf0090030ff58d8e1d3ecf045522e1985daff59234c6201489e46bb1f6011f62adeb11800f9a59a7f657722012b9c937f722445922b0685fccd4cce19fbc5c9fd1c9e5a1dbde1f5aa73d471b64685f0c521ab3e48bf9fb15bfd8a091e36dd278c1eedba2bd97454009de8fc9740371a84a6c6e32f818f6bcb242d87ed0e72d2c9f49fd1910e2937db22f9cf5c3e9303f03a338b348754feb3c301d08ba4d632efd7e8016f5c57d387a2a295ce5025e2b6647c63035767eed954a1950142c849a68cac298f7e49867cc1effed68560ea151d538988bd0582b72ecdeae4ce4e07aa25a5ca2edee3a5ecdc2845d79d85328abdf358db8a65e0bcace526732aa93d9f9939ea885d1340ae356b63327d0e164745eec279bb92c861a7672479333fd9823f083dd885096e1220aafd771c2078b6d83aba4236ad8a533a779d307cb21ff44766ed2a5209616c8c80d9b37a8113074be6f57a9b2f79b9111bc87d32ffbda134f306500c64425f2e10d965fb00b8befb07af39a61b061b0dc64a930d8dc9f101f2829c4eac5c8a5d7f398ef098a1b8ab35f935cb8d2571a09169b38a24384047e7729065bc6563fe15c39315853e0738efa0f5d55261baa63b6f73b02fc172cec3c9dac6862afc2fd4c99063d607ee5c4760b968410692f10eee136205b078782790fa52a583c85cc18f818d136fc26d265bf47add667f94f7c73d246a0c98af98568adfb25d881a4ab2a79e00a5c7fef013730b6860e6c1699cfa318294909167486281b29091688754e95d3f7439dc75fa69620c914132873d311c521c04aafb6eb79ae7b53c6385aa37cabfc2e94a2977400a7c700283e6864d5da75275027a234c304ab81c270307465f12bf08bcf505a8fca2e32b8a8fb2fc11d93cb284ecee18137b55ea0f0a78feed30c1a1d39141bf24541c131287c929de17abc1fd53a022bc955a7342d9e0db00865ac9ff9bf52652c768645445ca768ae3c6e120a2f9c36ccac18f7f369578964539cac3e8b3d2c8626174e93ed05d50fe0f8794938b5079be15c81804e766b83c60cfdd1b0f6549c4a0345f926cd8cca7967689971517f03041c5d004ebcbc279a6e88e1786d04adeb5aafd337d03be4ebc5e1bb35324f5d6622e8db73c4c6533193c0a93bb8c072f5419989fe8581e6aea3108d60b0bbda1eca17d99552e78ec7fbda1d3d9fe2ee9564f15a7b7d35e450ea2f1241c44c0255e74fa2b4d6c1c0bd30a64464c5669ef1cdb96a5a7af3c2f8d887ab61ef0e1e6d06c2e411190149cf7e16acc516ab0b0448beafb7d9b5e31592b4dc049f03cb719ba0bbe3a2f9b0885ccedac18cefbe978879ec83670564bf7c7c9e8a2c3c80e9cc09fc64945663b0154c02c93612a724f449b4142f908bbf8eb0476749267b714e3a0cc304d281cb16507fea1c9112757e5ac1f6e6a55d3185b5004bd453a9c4c2865a38358e5c4a2b838c1d145692891ed9529fb41e63e1517e1ff20b3504beeb0b6a7eeaf58eb77ed2c2c900d860f2dc07fa84111fad3c43f78e315900eab159c5e460096d181e8e08ac05746ee0d3694b26736b14f070721029a0e11de1e9fa7b58a60c08dd72f8841f374c29efd679232fce16baeaf32deea991f8e97927c753ef046b1cbb713e2447aec3a1993a136d8221f7f6a098be46ee11e6dfa4ea7804b3e74ee9c2c000958c60596255c88554db18e56e3a1d855fed4a02bf9ce475eeefd0c5da873b63e5887a5828da2866f62491ef076760a13d2da26fc45a12fae22e076e34465106d609cfc6a22f3a52aeb68ccd8604719361c7158319f820079448b4f434baddd48e6c005e35730d40f45b7a840e206102f346e6715241ee633a3aef67aa9367bd1e25fbdb5508b28554b5c8fb1337cfa4303b9da854092dc3039e0bc5661ec800c79a7e20d6f46a226d48216d277bd2fc11fef0458ccc4d17305ff9573aa753968afe2e4eb637ab045392c5c99ee3523ca9a479346f618606865aa9c7f4103c36b7882a19f0b40930ad4d600560f30c5afb8c658a5651841fc80d74ca1e013e147e1c86f14e3f80eff900d6c2be97a207e1c709bcbdd9325dade7c4b8c34b84ba993243498671b60da82c8a2952a0a8a14d1117a1f97f0aa25c83a8440843239f703e7425f225d22efdac536cf442d146f16d35a0ad98c66039caa117a82d297b63c055dde0a698a2b04ed0d22e55a79a94035fa8d22b6b4ba9993480452271cbe70409b0a124b8798a7082970e39a60988345e83a7ff664c87721984641cf95639a451010b0e210c360424b4a076c77e38b50026efbba3d88de590576d5d89ac40080027fe8e6589042fab6073dad31316a4a6d450a1bdefed5c425b62f4dab7dbadc2ad051c1e5270cca276bcf1612f35f16d611d5bbbf3bd51af3be5025b0aa4410c4e600c813098bdb0a792d56717ac676b4945a1cab672859a5fc0d2a526c74f113089f1ba2b6de56dac3899d110b53f7409dd1428b1b6e644a76ed331bf64f814bbc9de6f1376d8704a25276801c2303fc953ee604e3076c0bcc7531634f70b3f84cf1d02c6030a67a82808c44a7db5a0cb3e5a1941420ad0b53f4d815c0e64c2a0bae0ad17c27751d0a70d94790cb7b0c68ee7040956c26d299b9ede48254e8e20fca62dd0077913e9abdd76934680cb30bb61421b8f302f9ba2ec4ff418df6006b38c2a7a4eee08e463782c13e7c3595124d84e7cb42e825896a59e7574e3052535caad22ab0242aa562dcd209823c0a4ce6a979bd496506cbf5383d9b61807207d5f1ae161361bd7da9a7b0870faa6137d108888ab8a2b7446e63a2304e42866e949798ab23c6d60e0f3cb6d290a45b8f6ad1d7401872087bd4e192ba4f7b75fa834b84758f1c5002114c7e7a1a5786b11c15b1101ae9888b0362f445862eb5a0108311a0f44104243a9c24a883374d1ef7137e1e9b420b19ac0022f85458d340bd738c186c70ae37f0edba954a91b473c5a55c3159807b5966c0dcb5727d5083a987c77d2e6f3b2e80b6a7cfd92ea445ed645a24a4d71370840599dd4829ccbab279177c5bb58d25e271bd7f1b2b02ba9e0c04d867eedc3b67fc6cc93db304003a68c95a40591aae0f59d414674e4beadc212cdc9cd304445b0465bd6d1bf5a8dc8fa54e4e12cdc829601a9db7280edd29207ec4f8e3c5a0edfea84b6457134c1426811731fad6082f5f307637e254b983f11c186b28404071df26791fe9bf878caf26784ce8e945d448eda99b89638e4fb93efe6ffa0531210bf09ae6d4eb97838c8a487b928c70dee02002c0374e8046386abf4ad493443b78f955b09974d9ad24845ea30f0cdf72f94e070330d692feecb1ea926c6cb5f9325e90b725d29846039d2d8c5aa2c7bff864556d82c6dd1fbebd5e0a979baba1ef8f15c90a23d5f91351ce868db0b65bcb9ec5c4c86dd706d6e3c8d2cbf06de60e5de21bd6a6e3ba05f2d6ef8bf3acd1effac3950f9858c97caecf3d068ec80fe57d0e20c59ac80b9d54c502bb46e0e70f27b1a26cbea20683b489a35700dd31ba71dfeee510025b2683c98b03c9da6b6d72b7100b43303fd80bfe698995d3fd60a4c2933110c42c213388a3b4c17b4abe4deabcb702a0c81994530a165bb4e822c35f0b15d28d5d3f21a16a1028df146a90fd09ac671280ed371ad7f74cb2895ccce75e8a52e153e24f5013972c5f92e6bd5aa425a71308fb037107c26b4a06ca4635b9c3fdb3b88ab43a3b18cda0437badc616832a7d4706c7bbe34503a28d47ead4ad40d8f6fdb04fc12bd38fdf1cc35778e2ea65d5491b4ab7d9e1a212a48cd80b9b2ffd842170672e49d0ea26eb517aaa6a6610dbaf24ef2e8cfd425a95d41491913e149ce36a9d2b47d9ad6462cdae58d8c7f288a2165293421f317ba0b005e0bc2a3d141bb289784f2e8219c85d99a39e6798347db1e64999b2e2438e6c5e37673e57663cb5b65bd56bd02c6432cf562671312c45b680488d334b99f6790e57c89973e4543dfda00ed1dfd30624e267c3413c6f9fd618ff877563649c095f8beb7ec4313936743cff8a46a2afa89f9d4c585f61688ab6d4fe23fefc7d9d6c3fc12fdbd8a0cb2f68ea8738b107e85bfe686d455bea12c082dce63a011757460ee82980ea719babf0e133e8cbdb71e0e5dab4f35eb4ba4ece403f6bae46774039c8c66d185fa0798d2fce8a2a3b2ceb883219b7f13991456c3f5b9098b412c055712a7ae7cf460cb8fadaf9c9c763475473d624a885c4ff19cde6eb07ccdaf06944f06e64e23a712b9f10da251dda677359abfd814756aa24c206306311aa049d14ebdc35142b5735f3813421a7c0af865282e2b56f13a999b9994293ca6b3804fcd5ec5b4bc17f16597fa772f5312e2e7ad6e5182a28c29a2e9ad02ad6d016c2e188f105ee73261851b992ff48aef92a479e3f1f368228337eada3b42d697080a68a2ded45f515ccef1273fcd66f8fecd30ac4043402315d6d761e22bf49deb50894d461862914b1202dc8926eb11c607ef2296a7fbb1f788b57a511d829740bfb0e7523fee1205c4a11c646a2f21f06cce5c92da89d0802421c919fb6655d42e44380167a8744c212880b69992e769185f91e48cf274a76a490351f64104ebb07c078f388ead61e8ca46d7cc47d5e72b43c1e2868a8fbd8e7625fd45d832f96dfc0c2aebfdc0552610a684ce626a6b617ec6464af23163c2799f1cd0699c0b65395d6be22d225a1072612a5dbfe75be1b65e03804aef9d4cf0aac01e74bd9c2a80b608edcbb09b4be8acd2967a02dda0af57d885ca3f28228b5ce638ae7d6eabcd7251d69d0b62dc820bc3261eb35526017e4d61603239b3bb68975d00ff49301c3265e6b1cdb20afc6d065780d6aa4d958a144aee0321529e9233c33cbd7a51ba0437b7fecc12ef09d438401c06f7fb79aa670c204a04466187b00b182c0f86986390464ff41a2fae9a41ace972028c73389809dfbd1c3b29b966171513ff35980cc48dc6c19fc7a3cc0582eec09edcefe36d3c7be49311f3204c9206890384b44fc426e36c17791328f6186404b4d049d972887b23234c007e29235b29f379e176785e73271a550b19a5adee039e8a311afa2563b003750aa752dd1a9ae5df7a82ac10100f6dab9bdb08f073638b564281b8ad2308b4ab2d10319d26618a47fd6c5723af328215263119076efcd68d804ca8b1a0f3e4a0820d40622ef99a4b305137cbf5abc498f602114e726f6a560bcb25addd9f082464177b044926c8c56310a133da36047033d7e9ed41dc978c74a8a3860a0e426cc852f97325fe7268c7e6463fc02986da1ea96beb65e0950f0ec535471df9b642e9b6445b4d9269bfee80d036524458b5ef135b2b8e2eb755c0aa7421aee2fbc2320762aabb3ff3707a07eafa2fa663f1420826fbd1b52c479b2d541f3fd2abddc616e68aeee5563aa3365b984f363a097c1fbeaa65cb920d7d7ea9914aedd33635a3b62bcca7363d297a1fbaaab296f447cede6380aa70a3175c2ea2d845aa9db2bdc6a9132e21de3b0f98c24a9633390bd7a77c41b213b147dbd23ac091d4205ca098350a938f49633abc9660dc942fffc0d04d8db11a56a936f643830af6fb1e1954a94f58b93406ec5496f490c2605f35d15a2ea1abbf7d56008054ce9399c4fe74790ab17ad7ba9b0fa2a2de4418a7034e09fef2cd0fbc5ef2ef91431de1ab1170d7269b7c5375c81b8326997909fb43c6ecab3b091d41534c59cfedfc093dfe3d076b6dd99f7e8b1f90790746c79a2a2b1dcbe49d2a742309e90fe323a4ca6421217c04c9123b2ce49ff8b5f70749f87c1f5e4263deee8f5d30019b5ea5549ab2f231db94c326b237e857d804e12d0285c9e1e197c8c0807e412cc001d321fbf7d578de41c6af7e4a7916fb1942c88af6b83e0abf837847c0a2cb58ba1acb1d2916c289d38bdb6ec784eafd7808cd193c73bf9aef4799d497df0df067986b86db22af52b9b88a2b9ba16ba95cf7644010f45c7a2d74108995812bfc6a6842ed61b9830830a7ba3541acb85908b9fd3b482aef2a7f9dd581c5c6fd75592b72441cc170feeb68dbb2e5df8d9e89be7602c707ec8303a30a41c3235c3feccbfd227f329cbdcf83ea9c0c79ce5a6e9c7901858a070136b624082f3d2280a147ba4ee700bae89a646b6eace5f4a02be35747fa768a7c70567c52f58896a9107f8358c1055c95fbfe467de20e8e9840234d657b200fe61bc765c835a8ec432ba000c6b83a26b2c13508f95ed7f075c98594aeb7111c3afbd10d94d5bc42050d833be679a555a0df68e22cc66881fceb5c57a6dd77647a859bdd1b2bfc96af5f8448ad57bfb66cef485fc42864ad20996a645a43943b43e2f2b02ab674c2bfda6dfdaac53f586ab603d281db5638a639c1fb1b959b01427e6513295d8f0824be6b35044bedcc3c76e1aafd48b0da301f1d51f63ac636d5c5fc72a52c401c1dd51d97d72e2f92537e91597455d3b81d38293201e2c714ff4ff286fa6b57744930903ebd9d8458e5b3b29767181cf645f2bf4231b4c1cf1317852edc72f5be0cb4fb1bb838d7c83db4b1aa3479bfd9e04d5c4a3fa8da7e1d594af479dfebb02f65ff5914515fa7165c477edfbb6e95251710bd2fb5d6b8185ec7e0c98f54f7a508eaf1d71afd7c8043eecbdf4b556c281c734a733f992b6e374cdfb72bf8628d6bb8d78a8b854ada90c3938bc01eb1bf25a8cdf68bc889705a70543cb5dc0aeba4395c78db536739f0c28f205115396c36b0f87ebaaaceb429cc060bd69c6a9aef00697c593a75acac4917d0c731d5483708c799d7ac13e86398d674651adc342e773d4ec79b4e9a66f3ea037a42e2015118ca029bce208c354f436710c635534347d0c630d7228f119f409859d3dcee098f2aa124fbc1993379ee930414ac28f2cd17445a7cf1a08530357f21e98f60f9cc1fdb7a378d9c285ff5865961138c7418f430d61b72d24d5030842f342a335f42a5985fb3ae2df1996f1c4e9b7aae1d77ffc333ddb7048249253ce03f02edfa829887855e9f41b96ded7dca9d1cdd434be8a9b2c940231fb369d5e332658bc81d9a67568ed550f45dbb0a7abb78dee19a1ab4955362dfd7237fedc65f88350894199045faf4ea1965b62cfe24cfee9e30fa2de999a024700fb6feb236e610ac6d49f16df069e8328c90c5bd8ef4811ae17b16b0a282756d8367dd87ddb91bb2d4a92bc2f91fc485ceb477814940c4d32e2db381ddf57037b14fcf3314938da9910851c37fde26ead8834559a2f5dcc0652ff457ac926c11ba349246969a879b6ad510bd925d3d81f65ff04d117f5988ab9d9f154ff3892c49db543a5db3c534b165872fdb23118b7f52164fefb5421057520d2ef67366055c1118b4147a41cd9383a4df76d166259f131d4f116bfe3a78b2d846e6bfcdcd30cf1014f149285ff1a3ecb85fe233ca1a17ed84740c0cb66f6ec022203d2779713be59b1dbf4863fac1ebd3b50603d14ef7e43cd06923cf779ba4ffaacf288c413f453f2b292b5f099e8524d66be3b03abd98eb86a4aa11920a2e2126f6ba0de83fd2414708a20e918fc6583b435240f0a32c03f3dbc00217500a9245c4d9e1e0af300b15840f71b97d693d41ec64795245745ff6bced76a949c471a7e1eb40739bf0214e3bbc3be31c0cab03e504b74ec0954efbc91018a1c96abd420069a3e331d942b381573a5c509ecc8fead043bca349234a2a362bd2945dea6096930c6ede8f2b0fbf80c305d63a18b9d6cf72cb6061ae54a7ad6585d9421955eb1825e716241e7ec348dd79b49cddb3606916386472335c209e79c338b9bb654f3ee4d3a0cf3a0e2e9a7eb4a2292b3c79396e702539db6bd5b6a9889a40479be26d2bfdbcee612d13ed879689598c6ec89d8ff130c78ac1a5f5f102deac226d43687d59514f5e8f8c5b57e313b5e8292a9b73c35c8c8ee6d9772571e8ad62222c246bb17fc8702c421e4a0260072b963d200cdd378ab48f3cdda6bb385a4e8952d3b8f4326fb406834d25ee975d5af9d0900d9e852620a505fc7c345b3b70f18aa862c110a8d8f957b23c11dd860ff2fd7ad1b7f7171497fb232e5122070a8eff5eeaf794c7848f2a52827be456c1e1285d0169aa29bd26ffc2550df9a4b5cee3184d9bc2ba30757954a370a16cf68f8915d5c84dbaef27625b8b40bd44896d2572e032b1fd20c120ebf6d64f2d01ecd6e99c056474a3c826c1ea06bfeae3527ed6546c64fb9a19b5e97d974fab6e1ee0dc00988827ad4e869b4dec128965657380f61d8e0b4c5eaa4b9e5d560819652295dd477b5ae381af7175bfc677602377ae61278e6923c84f6a671889541da13b1bd2185311934b2bf3db0558a53542fc0a1696af51b075542a6acb6f2a2b712bc0cb5fbfeb396925eae8a4dd5ab28cf434c9c0903add57461d45607700675466f146ea6822d5a766de397c5863e3ead1ee27cdb722c2163f1d907a475580a1318bd50bbbc828c2264593af8e8cd6d03ea7502b4de718d239de085a9b6b64981b6439d1625fadad6c029df905d06c20f9dc67a283155b772105797c61251d025c3665bb2fe6822179507fe370333ef29090d68f7d4474459f9a3d04de207df8554fb4c9558878924266ff23c3af64bcbfa609ba328f239374ebc5dc62db8b17595a29e2f167419de47275058da800bcacbe0b663eec0b3033987b115cbe847c7d51ea7437bd388339e28f18efee3ba71a2b754c63d4450ade51b97a668011476cecd15b216bd2ee62765ffdcaed6292ff4afb1ba5b0016b66d10e6ef755ae911dd4637365c4c7ccad5d950dc5dad877fb2203b789ff9fd6aa62fb4cfa91c9d1bbfe908c00fb6a61b65d6fb0312a5e333ce4ba9fb797017894fcbb66b9d204f0b8e295105223a272699087194b350c4a2035e1ca0eda232200890a06eaa895e77b74b9596d5fd7fb51450c1bf0f60ea712716b2b4ad95eccd702fe09d1676a3ed894fe72965f6a982a88dbb0f65468399c4268896bf8eca9055d0ded164b0126b2b0ad9fc901c0347e6e9652b07adeadb836aab6e6c55c6cdbdd09e8f84763b80f44bdc88d39a5208eabf195108fc93015f6132ecf3cc4d5ca8514db1704cd799df89e67f1d5816f8b188b26d7b7710a15fd4d5174a3dba1c126989a4305f2bc2e221cb818379235a834f49b4681af0a2a3e8d678eedad1d0be82917f2def4909ddacb13c1001ad893e761beba9a71ef2fe06b8b0243705355aa7bc77013c29888623e8475f5862b0d80cdc4b6ee2adf10000b7f0fc017c8383be2dfc9eb54c02d9c7e7549d5679e2f845e3a0c06b8bbb578e29ba71936ffdeec435a48aa92fdbdb600ef140de6779bcf60b36643ac6e3ab20c94b7acaa940dca54ead8f46b99cda4950e6b99b9f8db7c471ac07543718383f735009dab4f0726900ddebea00f35f1481987d84d9ad012b7900e146395baa71312629605c855963edee4fd69a02ab9c2f3dbc0daf101669c9307cdb6fd808863800b1209f39b97f53c1a436dc6d85d96f40940ebaae4189a4039e6e297324b5357a88280e1c533054c29c15d013ab7e44b34809a848c5698c3451068dc4b624797d6b3d01d5621c03ea9c12e44586b43381cc441db5dc7b0aa84c04a9b43d77b8d8896df1b0ac631a65beef15273fd2a85561bc9792fa16120dd03071b0b4369c4a455227b37f15ea5cf6c6983de3ed9fd2a7d07b242656d473fa9d0df156e08e5aaa4b5bb907b95566cd38ddaf02a4dea528f3d20e7e93c8a70bf283291812ab8a9e92bd6af73a082011203af74cc76d9f6e247aa0e7da07b05f1538aea64be351d1591f694ca4c556550b6ae062f37591d572aa926514e0577bf5f564aeda9278995c7bd97de9321f537111b03ffbc1203c729b1c526836e06a29bf53d56f87b426ff528306dc8fdc0ec84390b6af6fd6cf449eb26f3acdee531a1a8de36e742e23924b2157407bb2a2fa3906685a2f14e15a2d78d5ee72f5d149f992fda326e81035fccc11cd2b3e3adbf86e6bf6cfc599329cc0e57346960a456e3f8367b76d0b0ebc4527304003d823685f7f3166206c37b302c407bb1e8eb08202f7b5e6376a7b838fec3bb15491d687101f8a57d729da0fb208564d88f3395ea0cee1791852e4983e7b3dcef37764e6714b9288bb99c869d2a64331ff9bc3c6e33059279d737b58c5aafafd854df34327ccad102a717e51b6eba97b27720bd18e25e0a1eb0b3d31adbaefb8a795f6ad493346fb60984532e752293670f5004b9a090ec503d2412a56f06637257751cf3ad5aebb3430aa1da5218c56cf23f6abb49d0182b3540ab5339b35af789f4efabc811a252f6d4bebdaa04e7a17975bb90e36bc78722ef9ee4959da1e1d49b8b658c00760a0e2f7fcd043249fb72e7d182d88a49e4a14247a6d6362639ae0c9685a55db827030bcefbe131b3831e5f32972504aba099a943fd7deb36740fe1e2b27d454394b2da7e03e512d1417c517823fc007acdaef74d33be16d2dfe475653503147bca0aad8ee812da308e3c6dbd6e997183debd1e21bde1b8dcc134428fb9e5dad8051b40cdb63a757f7c3ccb19d7c7e776aa435820cdee0a59827210505667757e84a77bc129275a8da42c7a3b294f89d1d82fd798d93b4bbe9c97f39c8b97e10f25bcce0bd69cffcab4b920674c27a24ed0a6f18d6ccfa36607fb09fe89448e2484ff36df248ad222b05ffe589f67484fe25b0678ed57d2e0470a5ef3bae1db9dcd87c696a13d812ef4fa340896dd79b1106b72ccfefb8f49041a53be15b5ae20762816370c95e7a6899fcdda5ebe81241f45d1d39f7a22217834f08b36b3374dff9037fae74c156e90ff876323994b6b9352d25c70c07a5beca0d2570bcac5b79e50e596e8d82b1370e25e404ace2e22fcc48282a8b0223a45c8f8deeddc2286b4dc124a008479d1b898de0ff4ddf3b3100538748f52daf8317a6dde813a8bcd051cfb2bdb0b78b2c1f39b87f9b36504b5ae057ecc6684127450a99763087ff50112794c870250e86a0395f7ba740b8480971216349c50e7c80c9f47dc035862735e453c7cb7e140d555505dd5828191b4b36d6d2358dd82cc613e9308108386215dab5b0d12077800f7554813e06457c8b520e63e409855ef691c70cfdd916ba1738521eb909f7bb6e1f6cbc69011dc3308b0ebfc4fb0339b91e5b38c14499a3b2d820eb852961fdec4c23262477ae3b16026cec02fab813227d6ec581ae2be6c06bc850d8291660d15e97938a78049f7b7277cc0f7181ec6419a21d21d37def65fa939fd292746334ae56677b98fb09fb255cf76368bbafc3686186b039a92dec6dc359bfc897f8de657ad42a5c6287d0cfaf415b3ab75964c8032aa44853ea8d44076253d09e11c68eccaea4ef10ef04c15d6a0f714daeecec1767f85a8a72b9960f48d6fd396040c40b2a9a503dddc9c842b9945f743c99fa44556fa0638b20d77c8056df12fd5c63f60c1befa95baf51a5aa02ffca7d274062fec96fea9cdf0875ca858298190eb572ab5842dbab27f96e02e4d549a560831364920f91428cbb1affd91448ba995bdf6e62862c844e6230ab86cb5adeae5d1e86e7b09cab0ae9d8510ca49a595ad1ac511e9f43f6bfd2bf58d7bc0827cf12735d73764015ff24fc5e90d5fc82dfda7aae10fb880b5fca59409847833b648610ebdf4c7dd917079bbd1c7498fb39bc0b2773e1c94f21977122e6b37f271eae91dcb43167f306182e5e5463d4a7c949d04970f5d7e42d57e0eab2ef2624923e4d4d488273d349709a70692301c8d66624755a515ce0211911aa0e0dec8f975c0f191062879579612aca6c0f35652fba9a411e6f4d84717df24f80873b398f43a384a253457a5f40f6df3ee005272fa8c2018b39836e14f6150b6846e9c4901bc6f3d02d1eb66479aa3e90d85ee771c77534b43296195a56bdf8d6fe899da15d9b01dc3c31a24f923db4aadb69bbaf6e049acc49c4a6a7766c8ecd22a5faa73672c40e71ccaff8793cca8783c75506742f005a97d6e1610056242fbb895c83d4ccea5c167e794cbeb38e19be53e5247809ae694eb5c7030c249296f33aa9e2a83ea94d96725bc688258571826729a0c92858df9aa3a3727cbf6a13eec93f06167202cec933999cab1a9472cd87c189e31e69c466c023a2cfcdda6d171f1491c31f7ef96edc10ad83d91ef820b39b3d6f7601e3de7f23ff4a59c21d8a77c516f5ee2db5cfce11c77d7131095e6f7210b8dc1244aedda9f5951d22b11ee03f623bc6817537f98a99ce3ab26e6e804dc758c2becaeb553dc2964d56d873dd0350a04c63db01128958f0c785114ac805bedc331382e03afe70ee318c10ab8039aeb755c539476cd46e5531562b6f623034ce18d1ff57cf189271a208669534271fa100432a3441ae8b58999ac0eac80e9574e638ce3ec276bd07b674a6239906fb11929f5719b288f4eb965107fba2e271fd6dfd8bbd713864ed4e3fddccece1eba6f327cdbc8900de7bfca036eab72f9611f741fae55b312224dc27efd3df7a1c34e913924923b81601298889ac12d75ad5d9a6f19711570d7ea514a6d741fbd694a90e7205f2212950f6633008df9946970fc6eaffd594fbc4d9464e2c07ea72ca8d6e9ff93117fa1c092fe99f648cf84e968a70c3ea57d4f555e4dd21929e187e50f154e971b197fe7b3cc45650b8dc45b01bc9bae4bd6293a53319a8a9d157fb84df17b4ebde08add664793df9974c8b4faa065ad57b7009465c5cbb7ccb1c5734d0ca47542cde37c155b36813f5b325fe172bccf2917ed9c60ed8ef1b59ab92df45b2268fb742a48025db8b23961a517054ec06db379729f4ba2b9f186199e2bdec3fcd965202d3bbd7d4985fde05b8c3824dd2e5a912bb03f0cc1841788566db26776f7faff43d17111e1e7b25f433f868fc85f3e8462aec2f16c3e3c09a7971b8c7d4eed4e30301bf5049e18466c3648bef161636532647b95d8724d458c2c9bc4176243d144e4268b90f94f66a8f4d054205b32771d36b0405639f9a2f5b3663f1dbeec08eb0ea6e82e2a29db4d92dcf8f3bc12c39297d3c420970e80ed955c8258d6d13d90e172269b74301f9799934fa7e4bd3b5a4ebc693c5bd85754a589b9ee5df8afcf405fe3eae3f5660a004504ffa97fca238a2f28ca2628fa17eaaa50b6344f1b36b5ab743eb9ab9f4fd68e2ff74ba2501f879621712be21b1ab48c878c71a4108415eebe2019da434098c21402e83c38d818eaf52fd097c3deb166aa84719cf70f3a78b220183c3360b6acb1a4900976249a48965ab8b130a89909b561755562450e8b836bab0a1be3d1970d058a7e493b5b81207e8154545690822ae7eb0f83484ad4a66c28aa6a855163bedab61532bb7770873305d1c0de0d02e4cc2979f9fce839541d5567e82dc8c1cb3394bb3ee334304844cbc05b225f102a4022f84f60d85c7487d6b0a14e2cdf51be3e9cf68c75c400d5f618e9a7845d073dd633917ff5972d3f8b7c6fb1b68e286ccc356a0717ea62f5cbb99982a39fd9ed6adf381bc5e06854dff01cd85b89c1050581980d147c6dfff9ee5d18ba83e6390708290a7aa1963b7cdea5dfc61718a3d3c9a74e62459f70d5c45265699461e10e6602d0b1cd5d31c3d4244ace1775cf40e8c2f0105231b98be2470b9aa6ed5437d9223852d49eeba68d69748038aa76819361643812eee458f0a805a6802fc08a3091ff000657a39b786b533fe61c857833ded20d4514e31a2ff311e35dec0f04ba56d3aa5730da0dadc665c88a601dccdd127f02dbc3932c70b6ddbc93ebb0e918cbaaf303fc8f5e30d97b663b0f616d0f7f37819e0bfa7e31fd95f2188385ee9bc0eb5b02c19b3123eaa0dc409550e5c7fffff3cb2db764259152cac20a4e0a3d0a6769d79456882d3e0926c17b5b391fdb0a86f0fbae125bd58b5afc4bbdd44bbdd492e5dbe5a5ae42efd9555661f7eb2aab903b769555b869d7d8aab2fa5adfaf8b7ecf2ce477adb20abfeb240908f1bef69abf4bbb7e27c1a38a16e175152d427d152d42ec2a5a84d953c0107e4f014398b5bcab000937ef2eca5d0548087ebb0a109caa0009bfafc4d604841b10fcb26fdf5656af73bbbca8feb64bbb62d776b6cb97d8aa7284f0b24ed2feca28694becd258332b4dab86a5a51a4a15a3aeb4b455a39dda4943359896ba62cdac34199a49650653666999a1ca4c4596590cc9b26234580babc9ceec84ad4b486f4a5a20599c6489d36ddd4e326b5a27ba9e7ad9bceb373b78ef57dba66ca7785cdba2683f1f10f4bcebd25a6f53ffbab46dda2c25f3f5666d91f9e1d61eb2d27b26817658da1a66d2c1ec64813f55b4755790ba3596669a2c79d4c154d9790f679b17c5be4d4be51c3c6fcb7ba8d2ceee1ef70eaf98b59dd3beb5aeeb9ba997ff7ad66ea65e743e686b6a1e6513e5e226c24564c6518ea94e3ee32757753f2c6de195cc545ecbc45cfeb6cdaca56555af64feb54dd34cbd988ccc91e77f6baa56a8a567b164a77e69554ca551b6a9d119fae68ceb7d4fb67955a2f819275672f125c718b666dcc258599ad7cccad06c0591bc7333f5021a1d61ea759b22d4b6d613bda8dabaaa74c636f5298acf3656e5512ed9264e3f9f8bc742ff3c94d18c61ac9c574ccd3a22072f729b47e66569561ee58fec8efa3cba2bebb6b4f3546174c5292a6ef36a7d3ecf764ef328e787b699510ee79f153b39f79c19fba75b980a6e1e5e649b58d56a1ee5ce486c61968ad7166ea5272ba3d8f9d9e6b57238cff685e65146c97ccf36af133b8745e67fdbd4ad303c7633f5f23d4cc9fccd36b36a9b5ab5cdd40bf8cd731aaeaaa7a958ddba72199a5b1ba69eda6de97aa95b9c6d5eac6d9aa997ec66b62f55b8cdcd36759a47f9dfbe525463d9d5dba2d596ba7d16564535abb61daa6c297b795db7256d6b3ab5a56c7fa0471367183244c8103186a8a184ed4b555943684b7adb3cda97ba99f4b838e506572d232c5954b408edaab20acb8b9268261dc192492c2cb814919ebef255b408f1ed53ca08ef53bc10729b664426990f185284247c1184f3075c2899c4d0450efa3d113d824086163411832d3401073a2885b6490c5d7ceac5f3fb01f4884116a913a8b40086295c5112d9a6d04e11614aa55e3e1fb9f8916df680e9c21652b4b4b042055f943edb24b2533c44fe79e8f73932e9b3cd92cf415062c00ece730564a8ceb32472d32ee1dc8117a5cf5f7a5431040943860c598317529458f28efbbd9c7831640818462064c8909248176082d0c509848045185194426014c1092d214face006482889b7df8efbbdd82d4ce085328001031e48210a25cfedb7e37b2139b33002145b0c9181410904eadc34532f2307bd73964cfafc82b639225e5ce99018da26689ba917cf459e7a01ddf33214d9a6bd679ba09d7af1fcb34d4f679722cb0977196ec0bd55808478972ca4bdb6b9bd6d746b19102cd2fb1ba29964b140c39021438690c10b1e94ae0e5418b2061e7c810643d450ba63380104062ec001115099844417567001e6055a94010da55b81114001c51744f084d30ba5fb1b934917135380a1053394e1e347e9deb2901688194e6479d18b66d21058b0f2c8ce646dcda390268fc4d64587c0a2268f2c897729223b1814c3c5685812b6038003c3f69d620a4c42c2d26c9986dd1356050c7aafb063208145b80581939626e79c493bc2d746c48c13bad80dcb3408e84043d164281a0c4593038049770ad4c55a78b50c0b9b2285b0bcab0f6179d53ba3afb53190da02c10286d4bf59e45b2a3dd23e8f74999156bd38e8979ab42d254360b2cc8012da799e0ec022fd6bfa57bd4030c0e2a06f37b6d706d815fdbcd52352bc08cb7bb2282deb2e5f67ad75b6764584f58f942d8497d59c108b3c0d8b74845a276291c7b9b088080b762544f860a2060abe00848a22d4004545099f891e54c4cce0064fac808a278e50bab7a48dcee29632487bebc1d159dc525e2cd2de8a5c1a90244c41de8705e8f00d3014e127058cdbb2eac6690c388b5d452397ecb2b61d70d92df211a7bb3addd5e9ae4eba0141ae6dc110e4aeec7a5797c5f4750bc48ef0392c5c29dc26dcd557d68a587264ac8bced6c896d43ddf533b390eba9d6e8aef1134ad23d6f5185fd775dd5d83b0bc465e975d59969de9f6bdc28ef0b91171e3a83885e266210589f555ed0afe9561fa97861b1137ce6649dac5c292b4bdef16370b4cc253e0abd620cc2e517f3aae8b588e69ba6b2fb56d631d6c7c851de95de22aece8da25f601de2a8c521912de94b464c6ac0d4d6731ca89c98c129211312402fa24e1091f41b6d8620b1599dd5f31d975ad0473b2adeccab53b98140646db41aa699c0a6b212323232323232323232323232323232323232323232323232323232323232323232323834f28b8984673b795ba097b6130ff5e21d65aa669da77ef4c74e7be7b7ff9bcfdd2edbd69dabea51420a10bc6b46a733139d8be20ef53a4b82fdd0c6b2ac028e10762c058214509b8468d21a14864030b0f5aabf6878cc4af01440a0ac252d3dcd08ab0146133886a4b0d43a5f9ac241a752d051267cf368c654278c92e484145691457b33f2a3706867263e82028140a85e290385fcf5b7f56b4860ad40979ed8fea82b004a130152d4128127faed8a05412558701a48aa40cc1bbdfc56e7675cddd486c97ec84f0ee82758d9689284274535cbad4ab6e0adcc2ed4cb218aff00aaff00ae3165476ddda4bcca3d3564994218437a50561d9a9c218fb43beae69974621f5aaeaae0da2b12bf765b892f8582a826e48485942ca1042ee5600d797b1192c45c8f18045f82954cc84298a4469720a15293b08afba5de0a6108108f7de7b899071ce44c8171172d644c81811c27b21700a11b28c2fd652d0d91a9eb522917704d8920f266c1e95de9900afd13cf2ae43fb10d004095e376147de3b0c5f7b65ad4f7605fcb795b8295584e02e213d2b00ccda9cf3b3de781e8891d9b8d19b5d6a558691d94ff687ef1e4a1992d6db274c5fab32b7792799c9248db3772f5065e7be2cf3348cfd2123ef551a86093318008102810201d375f6c37c38698c31ec6191cddfbdc672c6b06c6dce19e3df4f6f25b25b9d05d6968168c1deb67b17089e03b7fef677ed1d1028102df83ee2fd75cfe94d6be3658510661d89cbb7e0d2f81aef05a20540a040b4c0bb7a48a7aed4ccbb64d97df34b87c75e766d97da17eef2621f0138fd09e1c3c90727323ce31ab3e4e7c30aba31c4f872294108b7ed03aaf9e0c485812b639dd1661199afca0727b00f271f9cc0ac80e916890f048ccda346d915fc141f6189b2a2d81f2c89693032e514deeb8d91ad2d745a4a8813ee5dd75d311a1cf1fd465be2706ee2729b3b6191cee546c5c168ae04a48621b52dd9abe993e3388e3b71254033e9bbde54f607ee3ad4d7a7f6d15ddf92cda4ebdcb3fd384e9ff346ef1b310cbb4ba662a35dc51a232cd7945b2fd705b7de3ba63ab01cab8b190eb512afe0aae07c204ab500b06723370646623565905c2be5c4b5487c310a896bc80c05292008afdb182bd42c5641585dcc7cb1824113466b8c9a3280408168010a839d6b08af4fa10a02b685b05994dc5b1d04b76cac2e66b61096c4c0d03425d8adbdecb66d77b49776d95f6b3f9d63f08327c891b4407c50737d386d1f502cea7126c232c3ba672fcb9ee795dc33ed3a36ea8bbb980b57ca19963348fcf2fa2512abfd0185c4d77522bfd437c508217e2092f1db31825e32f2f87c64c4df0efbf1f0e06f87fd7874f0b76364f45c1c3b1f8f4f0816126fceb84dd036454c91833611799459a2b85dd4cfa7b34d4f18daa63543a16d8a9c84249390ecbbae2424fbb65a242499846463348ff2c8e680483e37b7097ac94e228fb2c7b3cdcf4b76a7b34dcf4b3687b3cdce4b7628b44dce4b760e8542a18744b649b24d918fec23f22863149f77dd5e5ed675552f944336b46dfeb525cc9247780cc5f1a368c429c6dfda591e421ce425bb4d4845ea2e858cb004b5d22030a43e280c0dba38c5e911c212440121a403db023a619a10880456a44b9005b2c5c90217a90284b00451241308f34b1065495a759ea714253d634776d568c2961d5972082d6aec105a0cb1224b9636486b71804eb865e3052b2281cdc20ad20297360588c2a2ce72b26daddda030ec0a0d1605097d08cb6d0b0314b3b5624056e86f6390fa200ba2b97b436357f45d7005c2ab4373028162bc0c5f7b4fcdaad1842d11acd99c6384252806446961266210058ac924f0a0d0b75d8ac740a0d1f3d0d879c719432dd0558bec2e4bea7b6032099ce9d064523633c362ad562588caa185224c06d3a1f1acec0f770838051669f05fe7e13b9d7b88303db44d9c9de2c1e1809e771efe7b97799b0fec34efdbdc05833acd038d9e7f9ebbe050e72e98d361deb7e3e5dbe6363bdbdc989b83db088f679b2307f700c26de6b888b34d318ff4f76d5333bf5310a56d8eec83d7ef74b6796b3a341d9a3b73577735f2effb2eeac68038a0ce364b50a4be0726c3e413689bb8a686d4e770b6398212bfc39026173908246eb3f391872e148ad457e8e241db045d44640666c6f73d14d278489f649b1a95475a6f0fca0cf120d07fdcd2cc8ad4278e89fec916c5ffb825d036c51ad0456236c0d1ff4e26274d14e5c03025253926a3c67fae9579c5986c33dc66068168481d9347103839390ae566ea8504f519f7c4d0d89dd08ad4df1b9ff2e8aeeecca5b935f8945779a4572378cd90fa9f6d5e9d6d7e9b28db0cb779b24d8c0a7336d99d6b929d7a2139e79e6d6233b609c256ab3cd217f7a764e7180c836bf229c364548ed9fcdb174d1e6950eb9919135b6dd3d4e136f30cc9f61c86d41fd9d74c5e5d3017ea8ab956d7cc26fcb7c16d8a33e03bdb14d9270c268ff43f5bd35c345a6badbff00bff6dac268f740da9c1cec63c5bd790fa8b21f5a767488dd190fa0b7776c26abe189a50c370603a2fb31388ba309b997b5a853531341c7dfa5e5a1af025466d5ee699f0e55543ea123b91faa0532675f6d6ca237d1d6aa1186e2e82bb149d888d268ff453ce10da63bbb5b69af3c4a1a4ee3ed022b71434842588c653a37286307b09a22135f6ec8e485c94b4a5a7666b7d3ab0bdd16c6160110d166d61609106d1a8a06109a2361a521f54e1961244034e815b4a243c1283287d51b20455a47ed9a101cb10da97208ad42195867380c29dc9239a99d68c68fd8cc8e2e1368835c219a71c3866067567ee0c0f78846fe211fe17a267a1c5605b464c4c69cbb065c47443d440de994cb2252d143f430ea1457a1c9349436811051ed995487d8ca80a51f934851684d00a518b2cf0c892e587d0220816591297b68cee0bd20297080358b4ed173317c7e0530ca6c134380cdca29181c4c727dc628168c1d2f2860005c2d2b692c0c006704bc6f7b63a34d8512548889fb3f5ec67770eaeb014af1d2d99ed0c2cba76632c5e58f4a96ca1454adeef62c92108f5a73fbdc1e986db709b96bdcb5d87ad180dd6c26ab2333b65680693a5192a53653199d4f9bdf085b177edf676c942da702c59586e5e416f0372faf334a75f62672685324924c3e434a3b22ac76435af322bcfac2b77cd61a7a6656d85f27bf26b72ceefc82fc9aff83be3f718cda492df63984cea8ca0cf28191979a7e4f39901daa692921917676c53c94ef12839c936495e32d221e98c907448ee82457104bd33a353d2e974f67fdc92f81fb734e324334a0e2a21f9876464441bd311f493f1f3ce64f48442e78c9f7b4c40dbf401fdf3d48bc94f4cb6096472d0532f274739d926939de26172916d8adc2404fae79e73443821118e0b4601a18ca0734e3e9ccf5db08987e3e170b68f531025ce7fdc92288feec89247395c0640441edd8ba31179741f1a452c48fb849d7b461fa7204a9d2dfeb825ce26514becd4e9dd6556b1e0dd0ad72cc436f8a0e3fc0b37dfc0eb7b9d2401dd9ada9677550ea7a55df10524e45ce1e6b6b6efcefa2a7a7b7b737b9717cdaa6c9776db7c5d65096126d12a4cb83676663474f8b6460bc360149ff0d9aa69a934eb0c4bbdabab5ef4c65cd545ddf4a2f8887baa69d198b01996dd2cd72e737422ed62687ab64dd8d67249115a6f8bf7b64f4398ae26ccaa7c96397e9e5844a6d4842b2b9344a25f4c539cee12a7f85715cd9cf5bdb4ab4f2d73785dc77120e7a6d4cbe6e0b7196ed36683da5932c904ee8f05db99996eef8d338ce10be7d5c31b18b8cbf2b8fd1bb1eb794cbd6ccf524498b66dca76d67d7a9bfad9beb66bc79e77e86fc7f6bbe06fecf2b7e3e381dd05e7ac6d539bfad73e228ff0b76d6e7e6d22f208bbe411b62cd9369f6fe64dc3ae6d6eae6bda36af4abbe8796e9e73cece4fdb26c6d9b66d6214a36787b36d6e56307bee75a0f64dc6e0879f7a01df6db3bbe7a9d44ece28d6ca23fc6d73fb42bcb76d82de75ff714b2a8c861b04ffe396ba6d82989932352bcbdbc4589ef02c56bdac3cc29c7da9701a3a7782b6e9c1fee396c06dea16e6c1e7e7b341375791ed1bd9da2f9a5e1556f30887b6995153649b206c9b9f6d5e2d6cd59eada52696eed44bf8ecdb3673679bdd79d7cd7136565dd689519c629556a9b86d5e98655e597653a7e6b5ee6ceb36389f194d35f6bc332b8f30b84dece6b5993e4e4194b4ffb8a50c6fbde6113eb7b39a472a7bd7ac9238e79b4b984ad8f3be547984b779a5dbfc714bdabed48c33be5212678de691969d19d6c23096c652bd662ba6622876ea96f652b354ecf9b6a42fcaa36cbf856d318f2ed63bcca37be51dea71f1c5c0eb764b24692669a85025c69c2b549ac2c0a0e877d329933e709bba8d4f25456cf8cc24d3b6313e71a6afa778212c2fcbaaae7645bdcb5f9895434d0c2f8a5ee620f196238ff053ce10965ac7027ef7de79df3681dfc1a75ebc739cb74ddff6fe3df5c27d73f098db266fa77878e7fe719c777c8d3bd6f2087bdef781e0b675ddbb6d9a66ea65f3ee17268b447984d10d086ef3a76f9be23dbda75eba6ff7b6199a1bd8a9976b49e411fe76f153c410e65db2e858a9e4fd9a61cf0425952caf2e5da931aa2ffd505fdafa603ef43b7d67aba6a5d2ac332c75a57a688cca4379a987e61186213d74cd245376f2ce1ad2947ad18e1d6b91e6b64d5ab6bbb5d4d190b8eb6632c93475337984cf62ad56aa8a801e25d3948ac9a46d9bd86da947099bb0dda9f2081f77aaef636d1feb53651d4460541ee1ab5c41c2baaa46b2681885af35155f9746aa2021cc5b250949d8b42eece942676be8aba82a6a68b705c28518d2236fbae633ab30ebdaa505e2854a5e576922d4bbb4409a7092d746c2ae6412582ba58c30efd202d9c20c89040ba9c4b54297f5dd9a9680f05cafdfd5ea175baf7179cdeb5d61e7b6755aeaf4ba322dcd542dc576c9a2f7c9924f9d5e3507dbe67918e79cb1693edf9b297c333441cf33c6d9097adbb3aebbe7c5712dcfcac9d785811ab6852cf77edfe57e65957afdb3b1dbce5d3e37c52787b386e7384c7561e877bc4df37ae77ef2558137034dec79defd70be16c6ddd42cbc4d8cb5453611e1592e2b8f2e68679c6ef7ec687deef4785a9f9b77d5b42476f685ce8bdef4aab09a475764dfeca3b7e9d9e6d5d25f4dadda26b6d9a997cdb16b97e3cefbed0dab2eebc4284e2f956adb66f62e8765773533f05e8c2f7d7cbffc2cc3b8fbedde8b2fbef8e28b35d6c7f7febacfdbbc54d69a2fbe58df7b2f8c626b86ddfb8b2fbe589447f66e6d6fb6376f8bb726f6b818450df95a976fb17fef6e533cb067ff6eb36d7a8e85acb51ecff7755dced77561395b6c132691b5358ff0b5b7313faab64d1116d3ccce772eaa3d70d37ddfdd36da696aad2be34c53cd9387444e3095a7e426d703bf4dc76d5a865d79c63111a515725a7a164b7666aaeb649b1a2df97555255808672be7e6117ac6ce25a1d031124c442467e877595adc9ce7d10d793cc744cc8e87a9eae709c3639dd0d30731679ba1da692a16b7e69686a91976b14c4c839fd76dd19a9f475816f328ab76984757bab53cc27429df1a5b8ca8542ad5ed70c20d1812017d3cf6f33a2ecf2821191143ac102b2402fa78c4f3def3bce279cf10eb9bb1bd92dd916c6e646fe2bee2598ea0774609c9482922afd6c25ac84a93c0c1d2d66b2cadb268ea458475c1aca66a69e933168bc559b61ea1570c63683e31562baf197a595a9f77d558b7a59df8cc5418c5569c62e8d5ca2876e616a66255ab2aaccaf075ead69555e9a5d3eb42edc5b29a5e2f2bd358c5b49a555aabae4ba7973aa384445fb6c30937e05556a1888a4322b7a08f4754a5a1545c6794908c8858cb307d89e7c808fa8da0de08da8da0dcde46d050ea4143bbd44476197a7688e541ed2ec37d4d0b435114cf4c0a695a188ae22f8658f6652815514554115544bdb6a4ed520bb12deaeec9e424c57e03994443f4141aa2db4360e528391c91c3ed1179646fbf1d2b22514a8a89c9c9c95db0799292d8e4194a6e270a4745e29f8c42755b395b8a72548e6a6b6d2b0bc7caa9a19a47f89b6a4b451c164715dd38b7d61ab2589cdaba6173e2e3d89b752587b36c2a1c2b87255ab90d37b6c99d2a3646adf5c6452bc7dae1d9bad9a9ba74dfdca03edcc43c49fc1bf6e63c2293cc1ccf616fac4dc5a187e768919c95f41e4e4a39d406e70c5bdc1a9e29f8e3363bd5dea4243e8d6d6ed07be709be49cb5154125f8795c4f761832987c3e16c1c931cdbe4f4b04db3879b6c4e954798b44d2ecd2187f7c0e1f0f01ccec3c9b5c234443b75b3f1520f05555075c30ec7f11d74d8a6b7a26cf3535b74f80dac8d756a6d6ba86ead9075ae688b4b39670fdbe4a039e47094dda1798479d86677aeac3c875015a62c5f394bb776accdc6537929c802551c385c741c4e127f876d7aad8fa5c30dc7f1ad9cea9e1c9453372bc7dab4b8353cb95688766798e6b0cd50b5b27294ddb5f208b36cb35b6fdcf8ca6663c36fdc064ff55479a4721be00ab244a3d7380edbfcce6fbd01c7455f0bad09555dba393bd506edd44ddab136aa6edda82bdbdcb06edc38caf65879843d16896fc3363d55e5376ef3c8c636c1b5468dabd8788d2f25f147dbfc44dbfc706c33f522fae867abb5619d2fc1147d09aabcf425a87aaa9720cb535f82eb8d6d82ad6d9a28fb537d2a12bfc63647db146dd0c6fe528ff5126c796bcabe27d56b8dac736f5bb3a234caaf456294975f7a5bba054f12a3a86c10fd5427bbb39aec234094c423a8a68599888d5c1e5445497c8f9a49e2299a394b5c9d670e4d0b4351bcc8655756cda16961288a2a55a54578d70b5a2f68bda0f582d6bb7a500f9a4927c728c7f7a49954e3f81e5426a51cdfa3caa4f1f89e984c32397e777272131aff532ffbe3de261aa997f129e336ed31e53552b669dc291ee351b689f2f1649bbcbb283cee826b8c2e3865e4e182c70e7b26e3ef82c7f14f1969bcc6b86f3f1e261b8ab8e611de1f0def1fb7ca2ab42a2dc3ec9a49a052f356d0caaddb2ace882c7125aa628ca8125597c63251a5ada28ac417559924828aa0a24a041555a24a548921d6298aa2288aa2288aa2289e99148ae229a6a8212c43ac9a4c3aa17193e3875a994483c6fec9f1433499b451be9f7af9c7d3387e68cda4f1db84b2533c50f0433399f46d8e4741c10fb1320905013b28a1fc7e2fe3edb7e37e2ffbf6db71bf171ab7df8efbbd9cdc7e3b4cee8243ac4c42d93f6e09e526ff8f5b129d7c9fc6c936f98f5b3ad9e1f6710aa284b2433f6ec964db3cd242ac509a49202d147f855212bf2b412a11b464114133a9a3ca23aca9e14a04154145d07bd0aaa9e14a647d05a9342d0c45f157911f16e8d4b43014c5ffbc2f3f2c123f250d617ef9b916f5a01ed4837ad0ce793b67d7e974d24c7ada81c9a4c374d04cba09ca7f72939b744e9974946d3ad91d935d528489c964773a270b597a5012bf73669267456a33a5b892d8437392f8bef4a0236a09be22d8a2b335ca8b66fabac9c5f1e4a1f1078d33be194dda4bfe194d22e8a6fccf3d23a18bdb741df4ce68da3cf582e987ce194d9bcd3bdb845d7c389a3cdf6c53f6917725e3e623637871e48046cf8c77e3ef8d27ff46936ba369f392e7d1e46d377d9e7f8d266d6b1d93c7a43df5c2e9bc3b369ab4adb78973efd968baae6d53c9a8fd1bb37bfadbb875dea76d33fbb64b8cd0bec91e660fb173b073f43bfa9deb9eeb9efc4ffec7884ce2b62985805b4abd84dedd24f29778ffb6293480ede4e2bde3bb6c2009e8db4fb66924074802fac602faf6cf08f2843a2267649b9b6d8607ed12fc19b129227c76fcbd84457c2f9c63b75f119d974e119e17cfafdbaf88cffd5e3ecfdb34f29489fb4b9e1ad9a6eea11304dcd2c95325dbe45d2479c9533cb6837eb24ddf474cb6097cc92e79d90e9ab14ddb41c75b11e0497ebf17f024b75f11df477ebf97af08efe2eff7e25dbcfd8ae8de3d74fb15c15d84bb08e8f81d043289e4f84964d2c8f1379049e2f16f08f4cf78baa5cf45c6d3f511fac9c5d18778938f8c3e463ee324a30f9297bc3b0ae71d4fe7d3f170b6d9f93637df26ca36370fcff9bcbb9ec7f379a7f31f41943ce7703a0f43ce379bff08a214de7e3a36220f85421745f1232323272139084402ba0b66e9726412787cfc01642332893b3e11ddf63c9e6e299f1b4fd747f7937ba30fef26ff461fdf671c1c7d802f79775df95a5fc7302dfb3594ecd8afb1ff08a2a47f8d3e7e0451cadbd4dbbcb06da65ef4b56dfeda51b6f9ecdac9f8238812f76e3cdd527793f14710a5eede78ba25ef33c61f4194bc7fe3e996be978c3f82287d07c7d32d81df461fa75bdafe2388127897fcf9357aaec7ceb191f36c0cafa9a82164211947c490c838e6701900114674d8b318100313b8700516583c5842291bb92254d1c5ca45164d8ca1848d3d4c40862684210b30a010062f4a7ab45d20230b2c78c1136510815abac61e30b042088b0a282eec2055ca232708146548a10552306107a5fbe1972c430449f3c8a28204091224e79cb32d056191f7273d2e0e4dd1d91a222425236248e421d1f3d9a6d007c42374926d821e12f9dcd3ddcf7e3c0299644b218961dd8383bb13749a0fec3e52f73f44a525ee90f720cb7bfce318beb7079db53fe8cc6117be0ae87ed25d6288b1245d88493e7ca47eb09496b8434aa9ebc74ba90ba5f5817b17706d885b2cf981f6076dc4c0057a9018502223259087f4b8f701262b30c14299608215c028e1334136e1072a08427306335ca17285d4638531c095159e20edad08ca5b190e4bca760176c87e0d6133db846c6381c4a201a85811de97365b8c45677b0859b871eb7e61d2f7cefbbd20082fd99d1bb9e7ece10d7f4264bf7e75b9eb3a0edb462532727bdeb84b6edcf5f1fdc74bdf4e5dbf3a5848ecb70a657c517371f6dbe8fd1ab96363475edffdd5b0d17e19e9919b00b41fb89dffc12d5db7cb9bad15952662d861ec062989575223714b07d3742f6d18df4bdbf27211545b43761e96a186b457901608192cd28620bcf713a2736fdcfe3dc7e4226c7adbb64d8b811da7db95420196d87aa1b04b0a4cb7b4ccd3ae14b760331714b605a3c285c22d18eb92c2b6604fd074cb1b2f46056cc63be725a6e2be634f607ddff6d2d6dc15b75cbbd4f4a8b9e4ebde78af8d971b73103a3d20f1394d683520310d56c98b01db82fdf35811dece00db12420009dbd27d7b12b6e5fbf623ec0f96dcf64aba60dca2a1b0c7b668525812ac61bcebe82ca09f38d915ac5780352ac5e6f81a0ab7582bda461096b6868cf9442cb22d2556844f02ae58645b302d5811fe669799dc5002e832ee3eddc225b6458b61c11a6cc52d59d5826dc9585811fe85428de0bbf1aa76a5bb16a3bb57a87705eef21e6157ba672c6c8bb5dd3103aca87bb72f14e9eddb5d1f025874bba09b287115a5a521cb2b065b615b1be77e4850498dc4aa8691a82c115ef26aa4c52e4a7a3e44f2d3915daa2676a8328479971e89b5d8eef5487c2fbc5d4cc5f5212c4f8a5ea3bc4a90f0c2e01618f209dca28de18b16999fef80fc83dd38eb080352ba109627b3cd346c8b036c58d27f816080ddf68706d895fc1c844822f34b1c8eb82a23a85811961e5984105c49acce2861ad54131313bd7a1adc46c2118ff3dc9d3644fb2836a8a4d03831a971db9d5f7ae3861c58701871acf4d0421ae1c0f1d10ed7e1a9171e4ee2619b76e0e13b3cf5427a0b699b78209d87a75e5ade43cb3691768a07e92bdb5c3909070fdfe13a7c345ac171fbede861e4e12eb865dce12e98d4616f34ea401a7578cbb8c37b1879b8fd788c6ae018577848a1f11d68e8309abccb615c39cb88e31d0ee3880aac29a8c82e7b376e7cc4f1d1532f2bc761659b70a45e74388b0edbb4a2c3579e7ad9e139ecb04d3aec140f1d7ec3366fb80e37568ee3a3df30dee8b437de70179c43a7bd1cc6958f2b77c12cdde58d38ee8271e8b2378efee3966cf88df186957d1ce3fe6844e97c046146300551f0049e368c200a548131a00aae4c469005cee4d14ab26959aaa924e039f2d253b7977a558930b9385a32076e21418265cc718639be9b8c4bec38cfb364f20e9c995161a9ac54d49394abfce4db4cd9261a1b8c2929c274b2c1fc03e5b7d481604a9a02934203a4f194141a2008822797968c9cee4419535a87ea545d4ca776ab8ed5cd9c2714a62319392ec5c26ef54e1eeac178a987f2545e4cb776345dababf1ce3cdad40ff4bcaeab7676a7e719533d75f3a5dea7fcd22fed9e5246b8a9206d15d14e9caf2b63aba1f2088f9a4a8bd1546da5b1b419edd44e1aaac168691e6967f901a1c24a7e69e8394bc13393681c1f3c65d23e3e8866128ee383309974e3f8609a49a3e383285005c66492caf141155c6592e8f8203e38d3d518c78bc6bfb331a2a4fcc4c62fb26143f41aa26db2b1533c6c5c659b2ab79172f22e7b2a2729dde59da8dc05d7b8bc1ae3f813910dec9dd4187fdc92e82a221b1f6da8fc2a29291d8e71fc8df1ef46230a905619e4ed30cfd2a0f1a33cf5327e346ed3f738fa8dd1368da38f4fbddc388e1bdb34da291ea3ef6dee8f688cff516e329a8c343a0c47877938c6f1e35df08df1dbd169cf641c5d3bf0f76292adc68c35666336663463d61a331ae34e47d4b3984ccda32c054f9a30a58c103c559a08b197e0099e07b057905a8776f1f766ef4186b2f164213fa367544b164ed8795fca02b26c6a69c76d97ea9a49437ca1669426afd78cfe82f505ccaa9228558e2155bb72318d96c5afb684d73c3a43e892493a34a19ee7f9821d0829f5a86206e71226a0023d9928d928a85c7005157e94a10c4d28f520410aa410c50c68d630842d6c186780a20728094e334b9438647c410c2a7c21022594005c4b7a946023ce299c35666827c57b370d630d5b8c41adbb5bc35e6bad318db188549808efb14f0776151f612884252fa003042df8cdbae02e5b1d9bce5abbbdebba9b6652e82a67086f6a45a1e6f9fd74748e8d970c8d620e1f8ade22b99799155af28bb50b9164af88dfec13ce0d3e909714b9fd94d0715d00216ee1baee9964811d5919dc600821a9b01b49aab0853090a186b01bb7669de799e282868b161735424e21a70b31410ac93d5c11f1547c602775f2b118380d79094bca681e61326416d851cecf68664156214158e215b9c41d52326fe9c44a7795ae385cb0b898e162ed641825d89195d1bc08450feb06ac144b0846b0234b7e3a3890973f9c6251be8a08c2f0227855c14198038ccdbe5f60d1964198490b848b2cca9b5e1659e6346b81cc190ce4293fc1047694732af23263d1799999f07999b720f3cb2c84cccfea05023f7171c8cf390958d3c2501c71103cca0f8d168f3ccfab6d6531c8bb55617957384888bb20b3092c09936147b895474f5c2030108b432be32fc8d825c602dec2aee01990ab349358ec083f87a6a67615b3224b1692e5045c04c18e2c1745c848eedb8654c14188f1406ab2c480c68eeddff44221450884f4da00bbd2f33c6dc05a824502960a5831006208161416076d77116612f139f47c1271b47814ee324f41deb403f706f9de205741e66796902d0809831032d2f0845004ad296608f52e3dcf05872733c785a33724b4e4c99165146151be1199746b6047f9f99b9d6740e2edb7b02471df28f028b4f113c8acf2232c735a82d00376094bca51e051fe258125e155ae81c5211fcbd8d14a264361d140e677208feb2ea4074286200409e1a7834be14288bd88c2096479d38c2c735a034b1a828b15d8917d4207515c1f90998b294638605b4a8ae84aa177bc88c209248917513881ccdf80fde1e2991484658e017000a081070b805bbc1ff447c8320769bd20f33baf13bd25ecc87241028d0b15841cb21b39239e1755d8421844329e06951bc86ebce1c22d97ccd0f33cab78228a29c2208856d82c4441587e1842a60bbb62c932870f273ff974a0e0305a76b560d4601c06d9c260681c175e4a7c80e43a8e86c478c5221c9ee7799638999393397132282703634596cc3e1ddc0c4cca2a2708cb1ae45571222c4504905c3c37961ac8f93362e421f396482e1e7f4588e249f6492b9d5869647f46e0877e3f23c48d6786a0402695c843860c5903a9812e0c859b735d776ed3a1bde3c68c2360ed4ab7cbed206754c2243bb2d3c23107a1779b31024a58bbb23d07a797c41c883ba3f22877638e22abf228474089951c05b8736a33ce76cefbd65dab21353390562b03a991a16da33606693fbd8d9a18a4fd3acb8da5766eb400770c645207b795c80cc06d09e86be4c68dfadd580a6149253292bb0632b96d25b45d2a71870c195243625b898cc476a9814c5ed73a9feba506b184ecd8069b817d3aae217e62f7f76edca2f32e43ad35867db28a133fc9eb799e3328951e69b110265c2142ae90285c2155b0234b96223aee09e982bbcdbdf7a3e9ae183a51f4425c828b664e3c51c545d7606bb42c6c90025b63859435a4ac01a799094ec800a7bf38631ec68830ddc3f4365d3bc5e3bafea6b719feea74c230a617a8540db47741eb14aa1901040000018315002030200c08476391388c2331b5f614000f80a44a5c4a1c08245a94a328c590420a1110010000010000032020006d30b363fc7b9d2c6c7822050812938d38c5ed9f91dce1a463069a9e112fa6ea46ccd728ff00286e612dc6f7e9219b3015ec9f1410611c5191c53e37642d8e45540cffd83fdac6db80e087feb4a82c92fa96c7a846eb995f14919330267930ac438f368f5a22ebc01a884c5d7595f738921d1d955c01c1b690e2eafc6eafc00c352117297a5e8011faedaa3c6c5dff15d7cc9a94133bb1aad2244141409a6fde1fe013cffee57e2cc55b779617f4ee82a71690ae10b69af41ff686bdd885cb6faf9b138d964eb4fd001893e832a4ae854573e288a26a28420da451579e12a2e5b79a2b2fbd7043f915ecd82497a1c4942bcb78b8ba88ec347b56111506610edc27e607e6f850889a245bca37bc3e8d0a93d92406acc43d05cf2e1237ff269d89dc4c810a062e5db3e653c8df62a817f52a2b9a1876b8f615fb3ae4539bc29fda95a09b7800bf7188e2a8c002bec4dd589550ee4b3b71a2bd81ed725046ebd2c3f06058f707814e2ed0d02ea00c8b9559ee74a1bbb5b6cd0027fd62ec722ea8fa149509c53da33bc8dc3cd2f69723b21f6a1a3e66a767636939fd993e960378912dbabeac71d6559535ae3e4ff721bed5ca2095b342b4343f278b513b5dc9697fcfdb39a82a67d9dadaf06f30a7170adccc46d6ee68751b1d87c48fe378b67073c800d7c0d28f85811a66a02b840a08b832f85aa59c21c398a04de28566a5a00865a23bc8cc0e024f312c97c4e6ba9d5a360c8e21ad92af97fe0bbb74b6f5819297431d35909f7c8508257dffeb01d122b9940512d291798c8bf7c52abd559a93d3dc25c5ce9c3a0abe67e8c5cb486ba08c29f9500bc6143fc34aa6b1c7007471762148867d2ad801cf2a47a32b0178c2234675df411e0c4bdbb86978f6fbb237b1f3dbe2eb3b303e649f2176d62d2e7553bbeffa9a89c05c00d5eaa14b3b3a24e6bcd16db654e5bbc469d28aeb122f4d40f6676b30640730ed003f03070e3fb0ea491f23141636fc59eba2d193ef4963415f1153f0c2f10584dd3d7e01176510f338c39db9b47f7946836a59f39bb96c9d34726308b9736e94569a703b6bb046c99f5f60f33eeaf9b129f985dfad2130a43eba40e3d64bbe60ead7e82168848abfaf823234b4452a0a599a1b76b71cd774a7a51c7e643e100bae55252afe0547b0a2f61a42523c8f68b88262d440a79106d91d18487591e68a3b9146232a1e5d255378d791bc1b9a9f04481803a40bd6efe863bdd2adac281d125908430ba9016a7930f382df7b6f7785ab89e9f0994d7e9e8733c3896bf3e40d49b18a7bed63f416a1ec67b34b588b94262a7ab6fcc4d4753bdda4ef54d95e8bcb381a267536178c2d92c8d865ebdd32b5b2866861d058892f0f96c563b3c22d4d5b5c04f70764f68597045dc6f7c22041527405082cfec12cb3fd6deaa00ea070b0378870d24ccb4d3540dd1e1fae57a004ecc53a33c1208d818dacc9cb2585d37aa95f1a429bc9d7f74331c50bad5f6b0f86b0dae28d7d8889fd61614abcfa9a4cb3146883ad17ec4e0bddec1d9c82e7d84941e580f0ec2f97c8aa35fbe4a93b52b9a72ee217c18700d01e7814a3f0116d822d329125366a716e8a481a7b808d9a0a44fa7cf01041d27211188c6d022e19041cf4100325359903036a5b26276f19f1c9176edb157c9a197935113912a2ec5943822771472fb2e22d67b528925b8ccb33d97dcf0e15a4bda5928421adf7458d1d5dcdc6ae183f8f8ed209e2b4d48245224f56676146609f60d4cc2928aef8e51825e724fa7cb330fdb2e782398ab12fc01a85446c8144c75fc82c8ee366a3d89776689ee41e0e4df8d41c418628726b0ba7df847142454fc75a3486107953b60671e08106b4a34c9221fde171e4fad98b0130ced514c6cd85d29c8dbe1717f954d99d50bff23859609c189815530bee667e182e46586a1caadaf2f8e1e208e6fa49f2bc692de3a451c488397c03db1afc628c9ad37635ca28e9c344f3b12161e73e02ca7f6522928f34060994ea5caa7d714d63edfd0e2245c1d08b4be532bc28f3e0cf953f40e25245fc8ff1264af8fca5fe20fe22f03e739056c03c593e78538752c993a86f30b46fdec10ed2b69e7c8b8df5e8d50be5cef77af93e09d69ff6452c94da4a2eae795068bee37d5c8476b4244a97ff4e2a1dfca5f0c81b89e348b204af476235c6be6b973d9490c8457cb32cfc1bb80b31d39c5fc024f1f102e281c5d3b405d8552b1e8db6b6f8844d1a82402635c06021db334cb9275aa3715173d466e7daa310c785ded5cb5db37e6c1dfe2f3fd8503d247d189ed9a3b5799bce8eebb0db6d4ebfe4b1fca5b754b4102b29504df7a4115edda315fc440c3791675705606f800f8c99782b637377853c8b2aa65bd210ec1e55df39c1dc88ffa3b3d391c9e5aa94deda154e0d188fb1e2194497d636fd628174a70fef9671cd31759bc2183652063e00231ac529455736ce35115e7064a53e325dedde048c188597aba92f784170f19633293e34899bf8a82db9c813d757b977721261bdd2084916777f77ae68c0ddd660b6047fd1fd858efcc1cf0eb7316256ee7c3bccc2e8786a15fd212dc1d7bd4a8cf51d46626ff5cd6bddf3540f4dd22d75b5d8c83e21b9e7cbf5fa0468348723282749844a0444a801e01f7750c669ee28b4223db8030f985ad43c0f78f9992c30a31c9bdd17d8201d8c9c74f0f7fe7b2cbd84938cd596c914c129e536e2923bf3d3529e1c0d774c4659c37a0a9a2c626efbecefe53fa42a724d53803a7e45a44aecaed14d998ad1c2391aef40cb665176049e55e50f6e58dd893b2df3e559242b5c6d932d813191c445435ce99988b67bb699239f4c62934959588129460e91c93c1bf2165566cc7cab7ad286d06b343a23525424b2a2dba8baf8e290e205e9dbc385dcafc4201a27f315372b0840a53d243c0efc3bd35346a80ef119e00e99ef339a93361f8c1cff39182367a5e198e3b709dc580c13f941c44de8d88136c8917afe4a9de1c679ad67aef7d9bb49218a5b64ada5850b125235b384d95aba7dda14624f10689c96bfc4dd173bd92b45c4e84280df0b5adf267b4103322a377537dbfc91d90b0b16bda920c21924f8dc6706c52d412a79f228d22057d544159cd6fa09658b166a2640757fccb9d34ae9332b07215d62ecfa73d28bc41dbb039d586668803db66a61544f599f3e47db0bd2ff6be222f27e0e25e27851136e3cfa22ed7b66f4dae8673d4c6fa557a6fee3ae2be55225a84a58b6695cd715bdd8b78875829e05c819de640b4a1822e76ee2ec5600287aed7151142eeda2a372fc7392ee734e5218af0058f6cb613f38a4acd64c88b54fc4e19af49a0312ca28138b46fde705825628c42e32e38ccdd8c6555c3740b105c74ee8636d0dc72e3500e224bd9e14aa8dc5d1c9a042695a623d6bfe1ca61f3a4ae532ddcf0e27acceb704189b0abdbec40dd387214b69835a69c4576c348b8bab30f4561a47aaabb9120ea8ba162dbbec71142ae5c3d42f303580b6d2000234e6fac7caf806b8faf13f7a883bc4de63a81a24796902117e2ac8cc5023734a9527ef10bc3a53a7fb434639cd73a3d8bfc403dd193b940498f0110131ff99a1c3791766cdd9f2871220131a416b1d52fcfddfb03fb0d74bf0005c2c8af5ff8ba028387227cdfbb1c93dbdf5abf13f2b83ceda5195f46166c64e2b02736da581ceb0a814607a5263632a53263e48fdec869649bfd4009285d239dd744c8cd9ed1bd213c77b97cf3124f6e40484f0869e37a51bd08ff4c7d1b5dbc6f25ca46d631a0e3da8ee6c3948e3f55ed352b9ef6574993eb0e91b028352c9fc922447f48700375ba7eadb228f945bc36bccc7c80d4a21a6ec1864df50653f550bb297b8f53fcadea5702461c5f3390c162de1f9491855d7302807673ec6aee92affc2be69897b73d5565d535c2b65a97ba531330796375c8873c087b83ff664e5e47b6c5c2425b185a5646e5356d3ccd5643f08eb848df33863e6bd3b3ecf60ef203f598d046be67cf0ff65580bfde869bec960f56eaba9ea0698f253a0711342bbd81fef597f294c5d253d124faf2386464f26e166c99c88ebc963cbb59920dcde418fc216fb5a32282a8c8e6b02df39b1ee47dd271d7f29ec14e005e6868201bf8c3cf7984b344713cdd024f0ddab82ff02c804453f28711c0ba877bdad799dfa579ebc960126a3a551da9c42ed41a8cf18b0edcefa50080be6b8ed33dd2860b11d2df14371465a91d0a5f2acc967be87d2c474882ec00df888623d7ccec939e160d23083f8d4e6bb4d436b888cca7c82bb16a2a111a0e34d4e2219893cd1afd15e16b5a02b735a78969a8e522afe42a10b55d942ce0a4c1b618b95e8d3219c0b10a0ac0c0bdf084c1bf630782e89e450262033b7706a0087f17c518cd6eeae2e2c62b8b442bfffd1e9f1e6d836b139898eb11621eeafe5cbfcd86523910ce8ae3bd076fae35ced3e85a0e6077a51bd2caff33e6fa2bca3869a739566b34b02e263c00ff4ede011e12996dfb044688a0143728ce5466aa0e8d8f6338ecdba083332981f38674a93f29677ea74b0bcd29b3b6ca62d70b862a6c95988c1fd7d92bf71cc52a17bb90f152b254b9c2269da72bc5159792f6c10e65e6a9f50c72f93fcf4c0dff652846eb82f95ce6f446d7fb9e636f32b9d96c1f24cc349068dbbdc64fe944abc112565af115c87f91a6e755227e6c7ebc40fb5b55435ad2c5cfd788dc80daaecd71938907914649b316d7b966bb0a8983ed7da77c70243b0a6d403504a9df5def245a9548590a78495253e64dbada71e94a8900a29ccc9e35e39b92bd5680f70647902b02430961ae8242641f36bd2f3a6efe26a4a35f14e833e31cf151c9821432530a28767c7a3a4001e0f80c94dcda4fe12218ca08249e0ffa9411f1405b5223debfd04d8cfa417f8e6e72b7078266bea1f8db1b1d8080eb29c97c1674de8d0f3f14f786222d9367467072400479777f5a7ad3815a5630669a0c8f50822548fac31ded0e22a0e821932f615516be85b04459235796cd7c90f80b401693202a1f85ad31aa0adc64dcb155074a6d53c87b0f01e26a9b61e090b176175c2fc280ec023ad3c08fef25f624288b920585234ef2f48985a45becaafe29a26d43502110e6a27125ee0189eaa60b36313a1d5ecb6a9b7a03747cb96a52774201eae9b957978100805012f5fb1290969747dbd680942dd5893f474ec12efffb18663c20c81fd33c3bfda9efb70201bc3062595fc946351cb79a38424238782418939912790a4aad59b7166c40d22e0a3ce86d6933189648f2ca1f3a15b989fe601f894cbe80556dc99b5b3e52c064d350469da6ac994c949f07c13f5374b044657103b290b8140fb24000f4e297ed7d2a71020a167e74855c83f4b3766aaf7606f33a703607c710a0466343b3a0522d83215d027c6dc3440d4918348f9fe089ad712ee23f8776dc97cc4ca684f016c79b8b4570f739d4ac8d595a37d953b4ac2adcd0a526389993096e275a90776d71453c0d7070fb545a72f9b9bbd02f76a9d733097b227eef9a9a9aebfa2053d550735a7eacd16932516f40463d01e10c78430ae7d6dd49775ca66ac1bbc723204756aae4117f73fbcf3c956b2fd70b5ff249e925cd8f4c6a3c6aee776be0c6782967e1e1f60e99eaf8a8d1cbc2af23d54995e6f08d90c1469a1198dbab5e4813a6094e9b63b73fbccedf6361b0fb8dad3c69d9851f6eaccc61145deff6d8afb54c86bbde03aa2ac7e62df53fe9f6e361150d629716e9f77f2a74d5e90b3bebfe0a2ab4f2f0c44456a963039644e7826b80d00d77af962b3657a2fafc66894708b8ade6da02b6922fd2d836bacbb2726ecc6754e92330b7bd03c6fba9294b3064ac067a179c089f0097759ba272a4df1468641389de17622fb67e5eb3d05280b50d03c32a00d60177d13d9ea0d33aeac2c6cece4109a99d781a8f58c33b29b8a1c32b5e404b158b6df904e0eff24ae420a249721bad26f09f7cb7f454dc3eced2c0c14ef38f4557a9a532c2cc9aaa060ce5c9fcc1a1aaa97d0319bcc6c1c0ea17a7c45d1ef826ad1e5feeed56e9b247e11e1d15b1b0c10d7daf272aa09342c19f99f98cbcbe165e1f33b69ba7961ea7d3bbfd132325741b35a976592068abca65c327ce0d6a363bf4c8ca5f1346762a32b89f63207dcde5e24f35276fb86877c2395d2ec614cec5b2eed573705a47394786c2d262adb95a6ec3541961765492bd9d9cb524635e8ad98b76a8353defe0776127e1f7ee4123418cd5fc1739b7460ca1b6b9ca67b25678810c95830f746277c4496e43259da536347b34cc4543baf536eec2029e7df28be321965fdd683b171ee6c66355611f8d30df7183d3c444d7908fa44a877bea6bc6141e5f67ad82ab7b4bfcdf516a4d158cc7524869bf84cdbaef23ceafdfa6412c69ab81f22f46a521273374ef26850b422e359851557625959b6b2584e13c71b2742f592d82a8072ea8dd2fb8d183066973546297104d14d60b7dc3cd92a27b0423fb31f70921eec61b4f96e5729a119898cf891cc25e70469d7ca906499851c19fa11f3a7846487e33a09a76338305458d6f94d928e015b7a1cf5345b1fa36e17f17785071105ca23fb1b22c8aa304e3cdf807d97eb28e74939e33dc2f6e3982281041724e033fea0f691049c953ec31b95b607c22737081e7d8b622b23496dcdcdc9702dbb8eb03f31b899da9cb738d9185c633eb2b0a19b014422ba83136d608451885e9614b2d2308ba74c0a697354a8e838b2fa39e5f89bd770fc58bdb0f9657ed92711d61cbb5e2f9f9fb14cacbdb8e743f9e147738c969b76f472b09908c34e61154b5c655e9ad92be6ef030034868d5c8d6b81dbc6460be8f91bb7fc8c16d3ca27e14adc486a54090031c96d4ce6b1d09fb6eb98f1787d1d33b2b744ea8bd9c717c2f6f47b82aa7d1163ce6f30528d3d662784800c2ae1a948be310521a6cfc2b8e156c4c9bec45a0cae94ec39d3b05af4660fadc9dbb6406944c3b1c0a080028246e71080332b75337f53482c13e705905f149b8022a520355b4b8480d23f4113c934cdfaa33e95c4c4141cea6b4f3201a31482a5361f63927de25d995f15f1f62679b41773e332d4002155e072756c1b79f8129664b025f945178ff3b9e8abc8e65caac04cfdc19a320e28ab922e0016a90078abc40b7e5f46d4e356934de736a71758a1c1a11a35999996750e341055b6dfc206e4653e076cf153b9a291979925194e9630c5ec93c17a29f28f895c956924d36446aa425c6a8cb1fe229aba5ee86d6bc7bbd26b1fed01f1154bb3a175d27178b7f3cf26c05cabda0abcbfde7c5bb4828b00b2cf513842551f4ae4f4ca671740f6272c6a026e448a9c6d3fda60d2653c7736e0915a5dcebe7462444700c67e4d29e261c1e247c2bb6e987007b336a9499370c1b9c6f200471c837b468566361ae7065c0da5cbfa8d7fbd21098d8b855205ecab946eef911eff7e13ff59efd9bcb19e1ec3f57e28af129910bf606c7646d58819f49312590dbc69ad41fdd7f49b0c5c8fbd654d237411ac5c8dc39350d271918ddd6c763369f5ffd8a47ec0352a12bf881f39843c342b7ce007be57c6c55ca24ab172279296ae0d2d3c56bd69316230882506f0f08613e57fd93fcd84d8d72c567d3a2896bbb71454a336433723e7cb6211f8718bb402d2448b7c6a11cc43e29963070dc6165876652881b5fa84644697c51a635337ab3a0a21a6462a14ed16cf3b3775ed45b6753bceca42429bc399611cc6da8f23201985bca697ef230156484339341a50aa93ab2a92a66c622d9bc40579cfe8e853e40ac6e12635425cb029f4511fb8305102a2233931e82352e669099fda476d1d5a9b76612be1671d5f719f18a7b85883e372706ef6ebc661da43d9b6246a54678207bbb56438566d314f28bedd3745259a0995b5c656126b9d8cc33aafb1f6171d9e1e147762f83204e4741380028ca9ac715810d761a971187c57b7b0b05be7e02566aa364c8298376133cb30f020436c912c3425c6a4af886089a7bebb0675898bc6615fc0491d42af99ed9185e03a8cd35be35bf51ed69f34e8934c528f52c33986035c5c613f0349c6a4c6b4611cc68b7e7a40ecca4350cabfd41aab84f53e488efaf8cb532d7b854000652b295872d6e395060a02478e8f4d882f7d056da51e11048add8b5267371f4bab3c0ae83d1c047b8c04112f53e513a124db4c6cdc9cf787c3f398c52003612a28c7822e9a813048117971d34d3290fd4f030c182059d13dd4ab9aa9f41a52b539331030c9a16ee5913003a9a688c9b418f54f49066a5dd4ae970c84ad18090795e716f2e10c04b670f12267f8b0d99e814edbb03298d6d64c213acbfb773e60eb9492916f47f571172ad9f8436a76fcd02d198815577c768bbfe06b34c22262ad7b01ffb99e66e119e87816cd3deecdd34d60c79d8136293252a4332ec50ec166a0b393d35b1af3c850cd5f4763b11808c63290c692b5ac8749056ad0b433d00827aa0756aae5287c788cf8e9c1286e2efa7830e0183dfd4ab34a2969a3d39dd1be48f4f2feed6805bb7226cb40139da47e4a0fac2093fdbe709e0f9e87a51e4ec0ec1a47e1dbf2fe2131776227bc90d747a819085340fd9f20bf5d68ce449cf89686c53c179331debbc918ef5d937dc273927706222a0fc3ba08daca402770852235056c6e013650e3941974db2d0ffed105f70cf4f642d9d49cd2dc84b16a1a5471309d215dc842b5d15a6665069a3063f73a06fdb4d523037549d9e4a3b32bc7f142cd9d8b8e55e7657a00e1781439518efd5d9a8170f465a08899cd9d93be9281d44751cb38d56796143558b32003352302af6878f43e6c2fd29681d475d7c6b081c9c6c8bb5ce4d6aaf7aa1981d4e9404d486f4eb2bcf45349734b072ab40931bec732ed0971d388aaac4c0f30d981c8a6811fe46d58dcfef80e843acd39a8fe8c0c538914c5c60e34679639f5ec8e03bf4569c90c3f96da8ef40ccb22b9e7c5b7543f6e979d1e3604312f9670fe0fe86dcc32a392f7827fde7f1c40da3421633ba36da100ea8b2b8e137fa981b66c983d66b24fc915c8596f29c696cdfb9e8b15822ce1357815716785c034b8cd192104756360b35886f0a4950bb7bd7e650d3b9a6c8cfc0a6722bfe562b757bd6f4d83cab7049939a7377b2c8fd763e07432a6b47fb496755e1c53bab39ff7af50fafb845328eaab0356096adeab2ced685d167fb5e5888326639013e7ae85d0987e476648ff757c11c0594328b4c8d46354031f7228634ff4f85b0d4bcd23406bac147f9d707f177e94dd85912060e1a62092162c547e25547cd5bcc2cb56740cfe913da1ff794b9d2285a7fea7a60e0eda1e49fd8b7cd07e017e87f8221e95a19e2c44c886345b4ce8cbd749e64911ae8295902ef39e95253dd760e472de9f6b630704bf5adc05a104b71f71773b746852baeb9a5cfcd32b2c5d9a4df999a37fa04912127448bbb8b198bec118b18a5f1e46d283c651313e8d5095ca89e75323208ef4f0701b7a9b3bcd46e8b94926a242c42ee77d294491722287d833444e4e1b6b0ae84900437f3e74e86807660547655984dfa4575b0fd137ed26e3dfb60b21a9742e1fe1873c08993042bf055148e8a0c3342bbc5ae7f7f36e4bbeda4889175e34a316c370ea8bc017196ed9e775a65c7562bceef3f08879d5facc84eb75e314abc7781d07c61e0e2971e9aa186d790cb36853d4b2a26dfe8a136d9b4fbc68233c5fece5f820a7a8aeac140092610eedead0d4b751678a5e770e8fb45787dc28074c27b04a301bce2e629089e28a130e5971da7c8f25ba5ad0214d7a69ecca30707b16aa1c463eed7fa868d3c33d3e25e81c7eb4406e40aaf652559be5f916c7ae0c452709a3ca382aaf43220ebd7b644bf8987017cfcda449f09fe952f46fba14ff992c457f4d4b548e5d1f4e182b01181350dfa1156f3815d54abdc51eaa66e2875fddfcd1b410fd372d45ff4d0bf17fd3521563d78792b7bcd020c1888744cdccc3a971d7281ccb4f97c0f5e1dd13c511e58321d27421fe370f5d5eeb4676a8d6b1eb16e257c90d0c570f257797631ac650ccd50535630b892c5808df848f4556fea12f7402106759c2dd847466745eda18ea2f81ce8198b2e328625a19f99f56c6fe26f51df4917e41ad63d73df914e5073181f82d5782684a1cdcf23f7845046b3278416cd577bc5d46570453d35d1ac38e2662515d3586579ad738443dcbc3af2b0b4d636c4c60c68793a913e83065f0e3c9dad54e844f3e12ce89891f0b4324dd420ab77181866fdfc145f00214b1c7e1cd59acc2fe3edd8d9049cd53c4240aaa78f425c396ed089365512e3f26cd39165f22f76fd8949b84e7fc54c445545e5923113044dc1cbaa7fb05c95298aafa822137ada2d53181b4304ee5d93aaeae236049c84b47b3c993a67b3a22c9f65110149e8cc55031529c26db314034d82f283ee467182ff22b9415f9921bbbd554fe6ace749fba4ee9c4ad92482a83984300da631ab5be9b92a5dc9a6366b47d4e327e5ee212292b97c26d42e511d71c2642677ccda58bcd8506fd9a5270b84dc7a3bc4cfb3ac78fb19a60dd77a630c1337d55f52534c333c1ed6d63f49a47786add01eb0ae6d0ff1a292fd00d881923dc161343d3e503b68d4a169dcb14690e68a9f99a51388ed605ce9fdce875b18fbd3158256e32f5f5e26d9aa869749b9b9e847482358943691626b6ffd24f420c60d3ac8094636ab9a3119e6e8d3905d171300eff30ec032141d2446167922911437b633483e1d78377c9689c7117a243baf223dd2c3ab6c22940bb46a3c0792c68377680add748e9f034c220178f3d41c4ca6b1b91b6e6e9944067462db2d7d3303ef8b1c7ccabd92c05332353d50ba2dc3871311e846683723c7cb8b2f71f5c91b5f80563f235e3fa4a1413c2679ff66cd073d8704adbf9b466624a1aa3c6289f7223a79ec80632bf8b770051e1bb185a22418ba251039a9effc32ee907869d9156fd911ef1d78c309c431314cf6ac42a666aaf0bbf165ddcd01994c7aafc56e1dc79a51db0c791c04141c9b9007f8f06817b10d8252aee8650bda2fbb08da0b25fc292df7ec8fc84c9ca3a42b3e5d46f07a8b41a349b95b8d5a9245c8f60361b9433720a1968bbc4d9c5ebec3e8959dfa7463263e978f68346911f3e448e2bb5ad4b72ddd990126e0e379ba4efa768e27e4f0a1ec182b914b62b1c6d33baee74e48731b3b5c1dca3b1109b51ce02224d7d77bbfe689cd834c8b8f206e5c60c5a4d1c4c44c60cf9cad2e78d4cf0c20cc902faaa3d3311cdb2d2a072c2ed53652b0b1f298f5d62444899b7bc7319704ff6960372f2a4bbbdbf09688cd04f5d86db533d30d8f278fa39380c321c258aa15814d0103748c6462cdbc28585d04d6879886a4b1da49bfc19615e216b5baa36d86781319f8a2d4f3ebef90a9351b7fd77e2667d0670d9a5714e5d0fc289bfc4585ff99d0b1b697d0ffc7aa433c0f72c5424e4c3d2fef3589569421a3eb2a0c9911b09c9a10ce00df20ec4355d44b609db1cb655ca320ea072f144dbe3b5db150dc0c37129df219a4ed82595b396dc400848b54271c69d1e2e2a4bfb4009e1a7701db8502e9abb606db5c970464fd72238a9da71e1af40cb604b3cf90fc5cd7c955e130b9d42ccffba238055945207b31bbe28457f044ce2da42d220d4616e2456bedccfbb8940e9c8b26f5c4a6c04153df172628eedd46513cad8e3e2c668f22fc6564f521a22553e222908657283c8838b81dd653985d0be0c273712295fee274544a64664b9117a1975b202dc87103fab1296e5322169b2be82ff9d68c6216bb3641489e375eafa420c419860119d5c61be1b81110d6bf056a3bba59e4927b256ac7264c2eb4f1a88cf69e7aa9487d79a7878d426265d9656f29713cc32c4364ec624914fda5258ae6e4431f6ba9f59cc259a285ddc1e1e4473018195a368ff1d01fb35c766832c74e39615b2e56389c9e5b0e5b9cd925c91d32908c53b08ea477cf4e460c6cce9f23442f7e71df7ade9ba9962dd91a05462f0a5322dc8c82faa819f2ff93497763069cc46dec556ffb40e107136dfb42f9dd3e0448d47d32bb1c1f80244cdbbcaa90e4cfce146cedfbc158962a0cf8e4c4aac957cdc8cc8180becfbc3524983a02d4efc1a3aa90f2800b5ca3825bd88fa33531f0b794fd38a4cef486ae9e53ffd71e3fdc81b353984d768e66fbdaf6f9ac34cd73169b4a3c345c17b772e2253f252e908a2733413779cb04252e8ef4fffadd13b0498cac104689e1170fc9608baab04e12a4be5e0099b2e22879b2af93be180115f820e627f826c998455a5a693b38cc96a9775c808f30b22efe2b785f1109247707040adb191f5cff2d254fdc2e396ba03d2e802c1a02d0dec0dff8c0aeb8b3683f54312de9240d7d151c6c3757de70c65196e9107689c60bc8fec7e4c44687b7b5c54337982a1de56fc4891bb6d9e54fdd34a35c1832b209d00b88b1d79553304aca183d9a310a4eb8069486c20c857dd44ab0c55a7abcf0abb7e434f5d65733aa8c5e3e897fc9ed2c46271ed5b0b76535889ab7c66786a369740a59d8467c67c07d074e4792f10c852b0f1c8c60be0240599bac95c161c866cbf8fbfe1e7fa6bf603bcbdaaa1fcc64181f0f0817f1862a8861018e8d7c16e01ca857f0be11dec967752545b708d845951d02020f7f0d664e1bdfd0fdcbe0263ffc0d8271f92403c1b3897da389134d449cfb053deb1e8662806f3a9193335f1c8a15346149cabf85c3807e9a91ab05b049804d2609feecadb70cb316489ff88d81fc48736df98e921e043fbbe67b2a4c4452e534b1b380369e348d859ae6a227f00ffa931c4291fdc0aa216a2189d265a82b5a708c34cc5fff112372443ef05b67c7f301bc801deb25bfc0cf1e74329579da7ee827a374194fd4e60a4b1f79b73b1833021d93a2042645207d45d7ae98079536fff3fe02dfaf37b8e47f40dec883daa21fb379a093672ea6185a981ed445a8fc9ba92a4cbfd552b61792e6f981b4f09bd14ab901f89ac015f0ad1235d06aefcce82c0565d5249274433bff428dc5a63f743e6f356e643c5117b4a16e80f185e1415e4e52b02c4f6bb722aacf1147dc356f5cb70caa1714c78f6168935116f4b1715bc582ecbab831618c0e9d2e52a27902f1715a00fadecb7588cbfac7bab35e0e196d5150c3c696401a4ecdf6954995ff096bd1e819da471e1079d90a93ea242b9fd694d0f2c746fa6a3095a822f40d373a96ff2aab149b1ff742b3378618c036fbfa8472a3a98dab547dda831ed61657f08d90ac003397b46618dda3115638a9d676b1f3a37034a7b24d7bbaf143c514b1a5b2cfbcc0f2dc3c598d83eb9f66667016f5686e2adf918a5f3340d29504db12ba0ac9a93215fa2857002c30e9aa32cbd431ce8c7fa6e4f062b380af361977346ba4f72fa603768b977987dd5f1d7df6e9b2cad84fad83976efc659bd523297b1b6b9bcac3cbf11c514c5e977986f13ade87ca3b2b0fbc6adf92d14c9fffd2ffc5d52cd7cb4be46591eb8a544dac7747b49de52d2c02288ebf7184c95e6ee94d707890a4a779ed874eddda695ee8855ef625b56107f9c7bba54e471dda6a79f6a9506a9747bd223c43529504eff27f5f93b642ecf0ac5709abee84e55d6136409df8989d59427b52dbbb77127bb52fa7a84356aa0bc9b60716ca5be315c25d21043e83637b17a2c00c9752cdc9a455b65414ef87571e4904d38f592b9489f2f1bc211be9986a8b2abcb5d160b92f916ca6cbfa4eb3f469ea69f34958dd47a3148ee43ff0dcb5335f2070d046acebdb3ff4bed23a41e4082db2d3edd89bc103e9e22f241f4ebe637d8124ff110d25697656e4023f9336d5734545b9af929b23d23fb92b074862a0ab470fab0bd57e4a84a784a8ffcc32f6f3e1eebddbcf58383aca52f8f89db47d0ec89d06563a978ff8912069b301a0cc5587102cc0d4eb2901cdc2d018dbc00715b70c47017609d5b87bf737d89d10d1f898e83fc974f58b8b11628b5b4287f3278e246ae3d3f8988c6eda2354376446509e3e0d182b6cdd1b875e77b896de8e8e457d422d72d8230fc1129e11a2296047083915e1fcf25f5f2eee607a1210d34c7544ea11051748542690e21d03ae7e334d8a5d71d8e88947965792441d71c110ee475662ca665ae12bdf34fe4754c40916cb22136e97e7462f2a0e229c3a15f7f055eb088d3aa5576e01ff8209a4e559b667cbcc430643170d018ede8053895cf0f9fb76c2c0668d47a0f9eb9302d6e989d7be2eb233747982887af2342eb26d7c601824849b09b45bfa1d4a2a444bb2997904354eb1147beaba7b91f1dc032d3446464d21910a83b6073a27c887cad503914de717ae1e53c8b9f23fcf55d1a0d8a5202f1cb625fb224110903a5c7d4d8397e98ccc8950ad0df5027ac7ad53b7c9c0a33bf1797ec0c1243e24350a0fd2db8e8ba36625eab1fefada0a67a4efab48eb5b0f0b096f3e5aecd33eacf4b29fb5567248435eb1e96342031f223b0929a6f72f0b874d1d105a585041f4cb46b0f7d97ab8231dba9a3791de821b19ebec6b771641bfe9d43789b7e0996f75107c9b9412698db807ff0fd7b2878322f2771836877de458d961630f5d14f5b9831f95c30cc4740d8d8141463fc7064c98e853d3586862ed02288a805ef4ade92342224497724519df868886fb2fe9e5cc59f9e65f38f2b622e9d718077356cd47db294e28a28acb0ad3317d5a21226bd22e1c84f96c5b1a34ef93b6f9cf9a4206e9f4dcdc87955837079e9056239d6ab72023acdc3b1c271ba8450c741bb98f6dbef299c6cfc5267885e75373e355ecc32679c32d0307fe7b8ae641a28e77bb75cb8c2217ff39aa599d94ce138c76057477b9170b49e1e074b2d2dcc9c28c46dfc7c94e536f03b34f3c303b7d281a7a97eba51e601fc0ff18a38c5048d60121e48e40f9174c7bf53421f7100ecadf246a1ca694317d3bfa4e9b8bee532b4622208d037600039e4a4ad7bb3bf795afa42285c01ffaff59d28746b063cd49cc2b49db8a04bf32bfcd2150501fe06a16e560d9dc45ad20fe20ca5b4a5cc64a6a7882f1d7ee126ab221e48e3fa224812900258f6013ea803ae60576dd26ac43dad254060d2991baa0082b4f65e74638428aaa361bceb9f893fde16289d89941aca19e922d0665c543b876ab9967e05860e4e3790b8b6e70e41de8952c8c51d9f21241990125d2cd796e1814c35a1f7422106418da5871ad09a010996b7097b9d72190d2f434484d4e6c836669342ce8ccc0ee926c59abfbcec4bc59dba41c318875468cc8ba2259b32971c8e72aca2c44f85fef8e2667630b2b7322112668f12706fe3213a0e6a6af66b0cabf605d762edc1b847ee7e62349285fbf115e788908d03a6a3a94ce247b8dacf6d822cbc3da831a7da0b61f11cadd77018bad4c3293472ae18eeaa693d225092936a3cefc18261150dcae8e6f6264482ef1a6b286595bfe8376e71acae24a98c316c909e3d625dfb524745d5e3c71817ef42c854d590bd9c3d816aa064a59c2445a200455760b5013d49d231177938f2a4a53989b69451239a42ae71bfa0d0291bd096f344128e7067379117f4fbf318a328e3226970388551c3a2c58527b909ef95b13cbaf35d5b8868643c772be98fa584c99fa38d2ac74152ea72861f6107e2933085ded0b4b03826337b960612cc45684e79219b5a34183204d8fec83d64615f7bd4cbbd00f166ffda771edae18c8f13d4d81550a7ac804091e61e00fdebdc71012f8b522f57e2e52bc1ba69ed381deab6ffeb10913c05b81777304c49ba4dd25c53882dac7090c669740864e8fbf152bd4442fcbc7e391ccdda14f5f45f0848e4eb03129e5a53f2d894b3f2ed68f687beebe23d3f1aa698018a84a341c65fad29f1ee65f56ed8d8cde09f3117b49bee0d17f1846520a8729b14ee20e7e136d885b70674734a962007a8d6df9802f0d6506c34b8d10604b6eed1895d9844c706c78582d4fea35b008c2085fbb133d629edc4343b15fc0d93959960fba33c01df9e405b72c960a8d6bc9580ce1c38342278ad176c3f0cee3b4d80e832a9327ec376b9ecc57e7edc5ab3bcd65b06d83da5b90f8061a3adb47e3d0490b4d1455347be0551736dc992b945ada4f5024553eb5356da85a846d1c82ee29616062885ebe895d17aa294d8832c16a9f1d6ffa10f75e25629577e4dc7d2805e0e14a89626672bdb14a2cbf7521e53d1fcf335c4d1cbe91451ab175f5367ac997a45c3aefb295314a9c07c2ddc8cc955600cf31bd96a1ff7b14db4e4668d02b9c32a7368bdb73bb68949d7d62d9ac87c8e1c19d1454de7bea5ba31a44fae62ff44ee9c9823c9fec57f6ebcfd9e1176b12cbf2d80f229f8dd774035033399791be1a8b4c084869096a10db7a41c2770a5ef2e24fa370f57fbafc45d8e9bfb81715d5206140ce077a3433c0352a3e85fc2d0e15e8be2fbd3c573962415e10c8a3f65c33349d2ffd504f8f028659009c1f5bdec74afb884f6a3eff69b4952d7f7c3cfb05a995ecf47155086b581a292fbf73f3a7612ba1c63152b205b9971899cc80cc71f6464ab7028863499eeb8aaf7a143c17884bc7720428dc11c20dd666b73491051388274971cdb1e2fb540b9d5da4a9ddecbc24418683de22b2cd09e7ef1d36a7996f4026f4351cb0cfc6e466d0af267e1096cc119f241f14f8a609729d1abec6dca67b6b3c237260770bfd941e842e4eec02ec202de718ef497e92c4beca79857f24d495b911924054993d6c364a40f8cdfe200af52280cbe3c314bd72871472029f40de46a383fdb7ee2b999c6667990455fb6112bfed84acc1674114fa991045e0ead7e60338ca1b24729a8375da0128fb891e001f806a2ba979bdacca822a20497bc3781936e58c64603393f9870323a90c5c98327e82ad558530f3ee13a810b1f0dea2d9b1b1c04a1dcf421d6014fa6fb1719fdd55493f05fd1bc029c8b3ee42efa40ec3c00963d9fadd867bc1f04526c280dbf0503d5379d1f9c5f75c395b44382fb79ec9cbe418732dc30b4398b62b52b03ef4f3d7afecc79df2292a11c7557b55b1f580ae5fc4a6d69f13628c73ba65ab6006f5d21e80e616d4115e515fc40f519c2efbd1def2f0d0d3443d9d4b7ea81c9679c396b11e44c9eef710cd44c4225aa90bdcb3e64aea8b6dabc750282470c7c836279a360900ace07b43d30085989fe5267e5ae53e898243ffbce5406248c5a3bc78c9cef742f388a7ccf370298a8edab018b82c0a467af0fe575099918253bc92ed2c9de5e9019ea665c692c4c53227d3f8285b040737f539e98f62f65610539fa94e0c7d1cb6dd54c62263f3cb65122ed428e5896e99c6a0b51c73bb095c100031fc590f322b001b0f695586f096c536b11207881c0e7d4d10d58cdf496a76830c0e1bd57e950c6a1913d1d475cf1f25b8d50bded0bd994bd655eae43477c1062fa85492fd3f7531019412b89b6e089ae3794046d99689192363fcdca43946385f3bab4c8d4d0c3a3f793c0cc3dccd820662b5764a0f886874ddcee6b1a93877f2f4f79a760b5eea5ff384f0577e31bfe43d85b8b504d276499e1e47e3fd3431dfcdd0f453da226a80bf4d3bd08b89ac46f6b8a632fd69695bd334d04a99a1b6dd1b874cb46aa9c96b4bd51a293c6c0d240781e8c76a96cfddd9470c7e5d748e58c6893d8fa25bbecb115d27957484bdac55138e13cc28bd93478961abde7fcdf9981021e041c5721286e3271d4c5b18565b523065df28496b6b353e8a13e9066f6d91cba183afab0172b89da002209885886044923020297d12d96e89898be061c0c6445ffdef189a0090390426b317dc8500a1f02459f72f93dad98b11b495c6b4ecc90a060a263add3a64e8764795d3572e3211d2ddfe7d61320ce3d0a1ef85665b5b88621f45ae68b1a25cf204287163df31bcd7dbf06e6ba02ce25b7c2697e11530982b358aac715a0df15f0562fafbc2a1b631906596057d298c82d3df9114e345d6b3d381706e2fd3875edba2d51a0f06dc6a3b4830f86673b101c4aa1f6a340a9541d8e9ef8f87a08892dba95da959b9d31af677f3add39fb7e36227e745a36ad5c38ac30f23c22bde6f8329114687ff1aa1457dfde5ac1f7dfd8f7d7d8edafde9d4b85182545a83154d3548a3d0a8e4530d5ba48649df1178e677168870d84bb214d1fbfb651ea80093214a5e24b61e54d126ab94e238c963838ddeaabf874a55b8231d66fbbb86596e2c5164b0e4e1169683b86d2e8717dabdeffb7ea321cf1935a0de13f95a64752e4f5c05456c6a9a07b302aecd5eb193a0a543d78a2b77cc97b00302e389bf7bca4708eccbb3548f3c0abce859258fea4ded3320b2b73574755f919117553fcba34a4d38d32e1cf9a777d6dc89338dfae8ec4b3dd7a5c9f734ee96b9314719022613a24f63ccafef38abb9caf1a7e1d3c7305133fef8a4036a069b655b460c5f9dcecd29265ac183cea7ab261027af6bd59c8428fed0a2d7bd5f6c8a1aaf368a531df352382b14f37482ab125dc95ba61726a9144a51917930fd7555e545be0ced49a6ce6c2456485073ad4ba63693489a898686c2f11d025015d6181a3db47f0f9a94e75d5f9386e5c4da95a283f515fa4fdd46795bec65c127f3a8eab5f9fc890336afb65131bced229f3d45f62c79ddab2496dd020d997023e0ffdb5a0395bc92492c23c5a9b845cc7e313d3ae64b51f7a76f5448f68eef50cf58c7a8df542ada3ba86bc1e514fea1dd50b55d4761148a55284eb05e819f50cd5eb495573949c1bd3cd5e14a4f8b2bb510ff55cb35ee809d0cb29e12b2a5b7b2a5245a9a92a5471a8741d7de653c294f3f490c4418bbce0ca40b50110e7fd2304b164f95f4a3951a89faa7e4d26282f70021db2fc12c3c9a17f1dd3c34bf866401fad8038c45e2385ba04d84b87400ca6613d3a2d031880b86c5d3a7b9e446200c45c36cd9f300fc4710d95634f9c5e1851f5754028ec535cf1e9850ec149cf13759590bbeba48fa6ea2103a588a55295c6594347832aa74709ebe5e556c503de97570e56aba317e659049a0630c091bbed66574c049a27606356d8963b3226b97f7853da1f971ef28527ff772922e61b0c84addf6c2eeb4000091644b28506f49ae22a7e6a63771cfa904896c6a06abfd7a016f81edfab4b3d89f955a42a7ebd0a7fc2b95057119840e24ede922f632326fe14c01ee81a3d15049702f4387719497250114a63328fbf119aed943678ba098f13d64a2a6686123e4425317f84f9f51c1071aed29c8d2b144dbdc7204a1403c484262ebef2f1c50691931da42fe196e2141c3f94551d53d09f289b69ff19d2c7e88e14651b65559512c155e67b755c2413653bcb43fce7044d139a783438c8aef2388c031b332c3d93c4e989b1748e131d5af106cd0f6966d85dcf1593737b549fea1ecec6bd288b756d176eb2b1ddd9115303bd9dd2291994804138db2d4c91e1089b6272d70602329e2e70876ceffaa3228080fbe28050c09c91d12b94d3240848fa33ea828566e1bb2e221afc76cdef0f246fd310de48504aeae078a08d80d635034a351f0e65106f777a253370bd816928d78b3d7c90fddb9f0e225d35cdcdd8fd2d81143d6b214063457206b8f194db0cea6db90aa99497888678a857d97c1ad5ce4ae768b48fd3e49c166080523b7c7079d0853909308be071dc09ccc29eb012c01389743bd95b3645dc79c467b4ae8fe8e401921224e0e14dc408d2ce3fb57c264fab1000c6c3b4e6754e06a60f03a3aecd6ed7506738770bfa6bcf53b40d7ac2e15609c55c4dea05be963cc16dfff5849c3e2035ce54182ed3b75fbce44a8344484c4c5f1d852d57710b894725d2125b22bea079368c40ee9ba44f3ca2c69b0274360b088a0d3921d106d817619748683eece7dd1616b650cb6b7ae7570a2b0df23e4b82123f8ac2514ab656743e6261d55d029b330ae0349c7bcb0c4d50bfdad9a982c9a41e6f21734f31e43c59a96730dce0151d11976a75139ef3e88d319e7edfea008621e575c0967bbabb74e63dbc28e3255d4eff61d30d73e934b5d4cd4183f9435c41b4bd8de0d4692b9a58ec9ea23f33ddb66b9b53ddee1cc653225def33121f167bc39b1921a617e7841be504da6efa582aeabd447519eb903a3635f07f8acf51f0d4b3c13a56539e59d74c07b52f7ee9da7828a774aeb9ed8bda3c2b7cf18098b2615f11fc4d0cae9fe23fe8f39403834babf1ac9c9f62e3b243d7e4cfe4099412651db7cc2c378189edac154b07bc38936b1e66ff5edcd6689695e7394b498bec9b67a3b74227f2ef63d706d7e8d079d8f7ac9513e63993541a1a350941f60020e67f3f451897c67494133f3361e0d2fdda3dc15ebe2132341eb4d6c6368ca64c4c96d05ea94a5d4d053a2dfd9b3303512d2e79f9d4cf12f7c21deec44397b9c93c166aaf663b27d5d8986d745e34711f751d2cd6aea2fd7757fc6e0292646b40c369413980cd3a2dfe05655325062eb6c2c4cb0abf2f37e93ad4b60f0439681be53a04894a0451ac44759d8cb335ca7fcece511f5a52ab256952086aa7ce74eda21fe21f576ef1f9202b5681359b870f35407cdcc2fd0eac0fe82a59ab3ee4922545fa203455350b990859abb975124ef501e1eb3a816cbd3dfb94d608879f89776fcbc94f652bc13e708d82feec666850c29d805a2cb93f533bae16bd49b1f09ac107e09cb665a6179cbfc7d2852aa171121afdd2d5f899886d0910c4af9d3d19f0c405d41c8dbc980426abc13368cfbbb516a1b00c6b92c9c10f5e0df6b3339db6e6becea7e6663535206821ff72bfbde177510039211bd3d2c4a763eb93f709970cb8fc929306ecb77fb833892b2f5cc0f9b8c652d24d984847d11674b47b8d387d9d7400d2456f61e8661d8f0562b80cd2878407a0c9df9c8c5ed87ed689dc7ea2a29241b7a818046233292dad92f331f51d54d93a9bd2ad0f1347b0e3f9723c2f42023b66c7832131d75382ce9a5db41314c0d458f2a77fdf59eeb6865f3fb60e01107fdc209a5b3df82559638e5c36b83a92090b5f9cb46c1504c9bd8913b337106277ec865189f1e04562425ebb11232f3b85b561764b709ae0189a757d70c825f628a91da040dce54461bd48174da5bf94493724b0b2d0981624863979cb8021999e8ae6b986c13b836561966664cddbafe3129ca0f49f86a014ddf124cfbf64b71b7eb2903cd0847f56f6168c4133505af250c1d3a1c7eefab67bfa91ad3f15a6e4a626c983720a70eecb8fec2b7729fb4faf6d12ee3a80bc62831fea76094c75d827008ecc0398210d4ede2618c24546c502666b55b68e420a1c157ac3f9a31374a1241e20363feed0b279b631902c1631ba27912c8b4b61b655df00754c821e578a67f9e9f4c46702f4b11ab244942fde2ae86fce37c78d7cc3b3d9622a0be06f0c370d6f883762ed917ac3aedea927ea9d7ac7fa11fa345ebd52cf502f38bdf32a68f0f87e2cd572acec6383e122fa3885c62119544b8e86d353d436a0feb9be3a07197eab0e7725a3190c201d55dd2412787d664094d9c4e4357a55353d8ce30b4741f388871e503e185317468239c6630c22912f0ed3260808df06f7095897c40fe3adb96f9048049fc1c66648515c283c8626c893e380242e24c458b94315aeb9f2b8371dfe295746ee6db6faa9c37e9f456caad6b59afd53ef086589a5173f9b09be8746e4e08f711d87c157df7b12c4622a210bd2d3eabcd7a22cb9f513b8892a661168c046167fadef5c8cb4122ecc73ab090873b14af0ca44070aa395f2c8be499bb4219eacae0d365b516eac37ebf496e5b2d1f0b2719161f0c3d22adb9ae7d539070e3decca7dd08a522a37e98ecd86369505569f086a89e57bdf6a7c5ca707d7148e3282e56333dd930f825c4cbd3fc9d06e22048374013be642275223ab367f56f4b2dfcfd32c54819d5ac9ea1caf5216046bf9962c04f2d1d64d1044ff73d9e1881e840a214b14d70008b9dfce79a4bc9a6896d42cc0b603877b12127051cf8dc0b769ef973770a33b693e05aa71c06e7e22b8466ec6391b842cf2ba350413040847834d472b2068b21b38b4708c4393fde84ff0cd4bc305ae04e32869aaed8e2dda8d8a1cc2236a10abe624168df46209f146dbd6c024959057fb3934e31b3a1c7174775c4870f2a7c74cd4723e71709de817ffa3d658c17c72412eb36a221eb28c02f804b494a4a30d394bce47225646f485d155169a7b373a68c773ea793fd197c5f43140fa853b61920b0ef21b25d59bf9e32a55a444c93d5c3f114c51b81d0b68cd93b89e28e347c4dea65df4f21c4b34b493a2939074c35908737bd2226dec793a3d609984a6a50bffcd2a60662b72f257949463012213f58b5612ad6b284e92945a2dacb2a43ed52d37ac321e5fe9a97c26ceae5695bfc003e2b5338eb19fb7828db357594c4b5c299862a58c668bc9f8c9ad15e6ea25d060a866270640a08a155ec673ddad4574a32db5690712d4c03f2071d232b88aa155513d389cc29ef1e54a7f326be32cdc21d73f1a7790766dfbf7f33b4e08d4cd75f08e9ed3bc544ed20db153c86c8bc3292f1ad14e6f1bcc9021545e3b956d74a5f824daaa65d38684437c6a031633d449e3fd54831cd1061932b7bb578638e5373f44878d2004c3194656600619311b06275946a2abe5350c1e9f1adf7f6867eeb10dfc053d08505d77dd6f620211e8205f1f3622fdf08a57787dc41e9fd27f108204a52f8c0648cddbe6a5b0e1bcd842fd68aa37103bfcedbb5d8ccf83e0136d6f976c2dfc0ed21b1dea1df1f92362c444074823a7e3a4828513302c22d25876321438de2311ae6615e266e860e9de86b6def937811449b1908639225428ed89996b7a202ab6bc717b72348e6b4a4657d911a69c287d4d5decd28534367c790d2221b0f49a3e9127e88a6fb25f8f4619749a8c0b663905d0a80b1a48beee7988d2374a5f4d6998d112150f19d687720c8f0f345d13ba9cd74e29a986e7d19c6a25ac9510240b99f6458aaa1c3908ae5de0bc693c98dd03cde9ffc1bb0c700b44b792876a39a668930a89983f7306e1c210e2d2b26d7de18d735b3f8a3e996eb43d657dbb620361a28965f02dd43bd8ad905e9e2aa7856e12b7e010eef4972a2546ce3a90a28608f6f003276f9c9d5ddf96e3aa215c38a30a26238b4f91cd8ef8e1accbf39ff370eb08fa04a52d0154684c02bda3269904bda0d2e7556db2593bc6ade04ecb41ea9b74376851268baf9764a0c199834c26b14c01c8b2ce6e31ad18110c3c48e30f845146fbd00826b49d629f1c43d18662bf997b37ab342d92dfb277cd2b38fc1b24307bd5e1043de6b1a9fc7b13f39441b4f5cd067f2f42ab8025d8071cb02b5c51cb762ce9dd4e7abe7591c9ae0e505fbcdbbd42c6093554b105ae2d4d20cd0584b6893db30b8b58094ea5ac921d7bf742e913b7e077792f49fb6f861b859e67ac099acd903257d2686910ffc09bec4122aac4a148fceacb44a0b488753e9d3a71c44847a92fc041052a5201ce6942f2a2cfe67eac25b4f989b5a5dfce1eb9e986063e99b40536c1bc06729f2185ed987029c6fdc9dcbd620e51dfc0b830fee6ba92908a8648dec4f2bf41f1d87228591df0b541ff3c487de6545094c85ad1a3cb5c781faafcfefb9412e1cfe96f370bd74928afe9a907a92757b07342d563b5bda4b2911a1084633aafac063c769ec079002b8901cf0e940e5627cf1b639dbe243f4e1a3d354547844fba7272dc20bc39fd71867c8ab1b55394a64894e3a85c8b458600843148c2f26218887bcc519cc268e9f0a18ae322654c1e3ea557845856360bf3cfc6824b8068c27810ae7ddac84d0068e05d0e59af69414b77c3444b7fdf494842b99b13ba6850169dbe7b60bf49ab35ba579d08d2e0a8cc1f1a779c5a361e5592dd46f25200138b7cc0bfdd366bdd4c280c92727e4b0d60fa92043bf61994983456cc8153e1e48e92c67a299182d37e1242d5308818699d2fe583877f437ed0d57a6c0244879ecb20b3929ef3c5d351a5a45878296c869946620d619897c31e07dc5f278c4c6391d9e8ded93516b4f53905f1d0d365f3028834e01d4d9fcb7e2112c241e2f7bf480b81c4934d66877d16f68747490f0e8dab4a73d34b427f25d00835940e9f34c4aba2d0e06fd413e4d74a52fd56cea7c5bb8c7de47015cba92bc0257b1511ba28c021ba23573c0d6b66652a4d8f3774ca67dfe54a31370bf387136a2d3a841e7602811ae9c48c9a61b99406bd9e4d058015a6ba4fd15cfb814d70d57e1f441c5af49b82d6b939ffc1d2fe9da28a4be602a6d00af1f7da6b6c394cdaeba0953f6d03328e500d88f6e45022a15c822dfc5aafcab1507392bc95bffa5cd088bd011d0c87d686768574a652ed72a077d51ef8a9aebb454d07276055f6445263411eb184d5ce0144ed388dd9450730c7c11e4ca20b8ccacb19d39260f75dab045e046b0d912991b9f41f2a0f1dec93b4bc3a929d8bbb84b4822c5b45bcd6c480b7ac572e0c4a9ac28ad16ed93cee186a1c3ebda275d0007dc631aefa21902bc4a6edc9b4c4c5f1529f8d0f0b7edb863ca2971392dd211bf9a369366113251b79f5016aa9f717b9c5372163d058191aff5d52afe3ff1a79ecf5609c200dcc7e36561083633f8e233a84cbab3e70dd602b32c4984f4dd145218fbddcda83c44e6194b8d806280ad7bf2515f2158878368e3d20edfc8430e9a35d40d2316f0a108cd2b4e1e5b7492b158f29cefef9e0ae3f07562ce4db1ff08f1caa9af4211479ac3b7d122dc0a8b0920ac2332e0bf01bd29f2cd78b12ca688596290fe64f199684589623b5f7b4c5a487959b86c6255a3d055ee95423b6197c77683db22752395d24a7d4043691743169009491ba0511e108c64953a6f86c159d9c12e08e4e45c83da3c0f8b4145e62f8e78f49435baa6dae67b79b6bd65a9820b20cd461b878f611daadd8c0f080bbb3030da4c9159815aa8596aa0d5e59e46d9517e5d222e00bb365160940a4a8af28148817287c38f75a8fe307bae812ce19e800c9c38ebe9fcdec5ecd78c1e006d5f5932cacc57459ca26d47d617c48df9b302c2024bac35c9c4b4be2e43e33c78304c2cf8bc96b156cc713f1cf860d3cf790e6e6d1d6f8c1ad3631567962c205206a408a98bc5548c02c57e13fd726b415b7507e7845aa6334644c23808d468072eef0f56abf519c2993469fbe76641ea0be4bb017b821b416f0020f34c00e5c9bc1367d537ce18692a393cc40c916116aca7889e9d7d16de4d4c42b141c50d28564edc3fad6b9e67591e12706e2e1cb847c0419ed1a876f09eccf1c9dacfb7bbe0e503c21faa8b1259e730816068789bc2e80a366c7e84e024ebebd7f809d70f1cb3007e3865b4d5d6630dbd8325c30b88686785534acecd86262fca32c6b899320c6bd4a2cf2aea4552e1774d7805c69459c7963b1367ec291ea7ba6ed2f1982b60a7d9bec63bce7426f2eb262cd872bca0e227fd5102ca2c1136338ac8a213c3051b12d2c235d6c955d071d778f1bb0cb94cc9dcb81d91a866e2d5ef31308b4b90aedc8d940cffaabccd0002074d22edf14bcffbaab6c01567485fa74bb5f080ba38c71bd27666307b9d14fea905c088ad644ce6f7e38c31da2a9c15fbb53b151ccd1a73ebc40c4a55ed57919312550b42d0dfa9db737d258aa206109034ddc1222fe95ddbeaada8593b0f1c08e9004cb3e5b9923339ee0c8b9472e4013603cffc18e841f10d6c09b2cca0c863ee494b24b9562c2c01c02808ea7c0f744befb8518481cfa731268e7721d3000113f778aa1a67ca4308cfab884807713b1c4d94a589fa0bcf112392c3bde0293fb095182182ee879d573913979108fe4509526589c3346a093f76cee084d2bd04002aa6b0f822c6cad06b23a7a6894d7a7445a6fb05434d2e955171fb4db3d03b1dbbd886f5fa3062409f5607537e696a32950ec0976dca63e3830ebccff82f89f6991d0a41d911c710d5c9f9220a92342673963f6af0cd7261b1dbb13e38e3c7ad96f3e615247e47634c21edbdd2c4ea56fb65b553380ac26356b6365bbf053a51056ad072882bbf02824508afc8b5448a202be48147aac61d1a62df02e1abb671f2250c98fe33897fbf3aedb2d4338b830226ba19fc05cdfe153640011b2b3eeb78b78250e9500b4e4026d44557646c44a0418de667895b7c33b9f738e9d24f670e011aacccb88ecb4181ba96abb507bc16ad9a3c62860d9ec5c8984628718e7805a9f98821a840403430e27405c1ee356f4d1ecf0f0b717c7ae71566e65af6ce5adb495aff4955ac9955e395492ce0b7a542f9296137c505f2c9517fca856242527f0a8be482a57f041bdb8745ef01cfaba081a37a4bb86dce542cd9e1360919d86bb1794c16bbd798d3f9d82e89f63bd5061b566169daff39aa09a573d56469fa3b047057904b1f6485d8e0105aa4acc8652187c38a45fdb2ddf41096e245cba71daaf83b6c9945c48d6d693df915bb64418f69a5a4284a21033383614a59aebb355804747d768548769e307f26c6bec75a6cd29d9ef80810e247001021d2c246529a9b4724a545427d1c412495cc2cd12494ce2c46e4c2cd1846e242ae10d09bea9d81d45fa68379e4c0a239a4f9230746ca86d2db6d176e4efa938369c1aa7941552dda04133c7f103c1d1d9945d9162c70ce33bf5be6755d7fb64153b770c9510590c02ce85f8ee6586629248059887adca10a2ef0911e1c2380b320f232ce8db1e02816c849c4d7d42203d4a126bf966df166b95c0303c591aed6b8ba393b592cfbe8be9c595d85b79f065901f2abc801dfcd83afc4d416ac7f16f19a18af7ea971900efcc73427964a62733b78be9f1ad261ad2afb5dfdce3b44efe67d04c7fa1507d76ddffe0d5afcf452355aba0239da99398e9ba510c5773f0a520685bcb749b9921442392193237ff584b4113f4d87c935fd1ff7d1089f646d7e66b7c3b30bd16304bb870edcc17f5518105b44f8a9c8fa5b8dcaf0d2fdb5b6347d37a02dbaa93828e403fc240666f848adffbbccc9b31bf69f4f5809a5affd59f8ab43f547727ec094ed24d60d4e1c0ab4b5df591bb943c7d42d9250c295cc022bac61d7269e656cbe5da52cc8169a3885fde8fb1893a533da41f77b937a0442dd99d44e37b0c8cc410471c57ea7f6b9c0158184c19c358ac873a3a5dd2a4b528603d01066e14c21a3962608d833b0311914399cb615f59144ac09e09b24ba204a266654d599628b94e657ef796d86af2e46ef0faed6c1a77d735c6d00d6719c217ce6640257e33a6a409ff1a1a90dac52adfc2094dd0412b68f7ca1fe25924e3a007223545df666a024ec82970c330a8185abf20704067bac548fb4eb389a85b23950d2f6c2774ed29107b8261a7d83439b320b0cd9b431d3b960385ac684259776266ef202d40387121269e587a205546a1a20a86f9f6a72f797b9d8332e3cfe42166e64e1d8449189e3b89b62e0e3c8746ce695e7f84984287e92eeb8fe2402a9ee688fa518c03749c5b0f1366412f24b55cc49cf287a87b5be58e106638d343e73f9f527ee715a76f8882b87967caa8ea0a4533e8eba7160a8b2273d21bb5bc4171ccea13627973ed35197b6d4c2e9d0c998bf3e43ed9d3685fde2488c43275ede05817f20c4fc2fbab44fedb78b3b161d965d1cab026a6fb7e06e7bd81c015000476d08a1b754ce07f4e320a5a193aadbd4954d346e7133be821869b395d2f8110e84216c5030fab32557bce53b3c790b0c4b821f7705997d83633985bdcc34618d7273a6e397b780257c1d5f998e4cc149673f36d80067a114c0c1fd0c5771299ada09ae78868fb239e098606e19c44d003fab3a220291276e686e5bcb4029834035519e7f70b0583ce30a0b1218fb0f27ef9646439c33fb91bb11369962e48bd30b678d0325e3eff6733d6bfd6a3c186ca41f4eb5f0cf4b3f4207a1e3c327609807c5d268d2825b4b623c98d4687f140f7593c440579cad401cfa8017678fe067ad888ffc753f64257295baf081b33dfe2818efbbfe3593c38196a2b88b8e281f95f1c8c4fb13a8b87ee487fa81b7263c3d8b24c19877888f214e3c19d66f140f7f8e221eea944e97039e9382d1e4e15596e330c46ac36e9b8e2f03177e6afab2d1c363e05553c48cd56443cd00f203cc4ccf444fb679aa3ca4c6e5e6c2f203a502b69870fa74bd92659b84910fd4a02ba2c10d9326f2d77f0a0d69a4c8bbb48cc2a71c04a0bd9f04166b21168196833ea32dc658e7e19f8b729b129268b5e178c1083eea51bf458a7ab56bf0cd967faeb25b21f2a544fe364f3179cf221e4b81cfa9701ab062cb53eeabf0bfd896065acdcd7b2cff8bf0ca09b061b4ac7586ea8c37ac8ff31a992fe32a0ee32c07963553d4433261bfdee763f0761b6d4c65fb5ebf4bf0828d394fe5f0586bd7c5c2dbf86e701989cd640a96bf1bf0c9ac49cdef0c43d767466378a64a598b3b8eccf621eb7a3624fc25408f41832191135d8ba9b5e3a12b1acadb1203839b28291502ae520b211c2d0bcd10a33bf558bebc6babaa525eb3c2e0201ff9715260b4fd80182743a6dd2db727d41e9d023b908600f390a22868608c4389ef472c551a4899064ffdf356e6e0c96147be40f86e6d37c2162bd553c96d054d41c7028b66030168a4e6d391612325a3f857bb7007edb358a09f548e4d56f575761694dcaddd8034241d69d8fe8ae78c301650704286cce64d98f90428646ae89f72b0a25ed88236eecab45b6a6839bdb5b94e2842845a5928b6782a18e89a1806fa2531eb546933ea5d7490990b48dd9fc3399df984a1baa119444b457acbe6b19c6307d33e0c0fe679803c95a80845971d11055d0d11f8476b999190500f0f59308731a8c6a2943ec1abfc10bd266da3adf7d4215e080436c0fd63d243f7a11db8d0dcc92556bb3db322daa75c8ab676f656563da87f4c9a2fdf52b0fc63948eb0ca8ea982d25bd21b31b743910bf9e4f0c600320ba3c8592fb79ee6b5157db8ccd4dbdb47333e6ae9d1e9f8f7a0c45fa6e8b36e4fbe39c89cb2f0267dd2030f2f18a9fe9574787573a0963197d8d08f8c82c2388e73461b901b1141be0207251643cd61996aa745f456f44524e834fdbfec828392e8c403160cbc2ecef976904767cfd40f70a7454cb1319fc16c9952c1b67b32f5974015e59a1485c9b02b334d7dd9fa68dd1a4c1344204429f14557ba5bb9f580eaa4be09f0e9d063f7d5828bf2108562c792e743c6544a3f9160294600879cbc2ec57480bd3dc65c272d20fac5306d3e4c111f2012c4771859c2119fd0a592e66d786849071c11bc03b051613f2d8e232b899747f1a3311e1577dc9ebc5ad4392dd952263d7f1084dfb545b322709e951cc8e98c29646dde42c9fa7082dbd250d8977ba5a0c15928eef26e9b421b01af2b9c951edf959bc0cb739b17f42295c631280c26aa97347198c7a00817be5709ecd0530c11f49225e72fb8ef1774061b2425886a57fcdf3f7091d838ce7484a5e69425c00791a6f7d06f56e1b6adf7810369511b8c20121afedb97d2731bab96069ca9c702cb37a8bd131f34d0dc788066da75340faf532e0c41630345dde48c3663e175d44f9b6b82456e89c874cb15a66d932450b088f9db9032baf8797947fed19745051cfee21b596d4c15b6fe4e9988a1ef7cea19f4db09fca9104445d572cf820fac4213acf6e83320b2df7825be265b97d328406786442284609f4171c208061888d3b3111a86bba08c1f07728e43cdb21a2c7a7aec2e87570f59b6b9cd06d3ab2e47b6e8427c704b6c158f1046c45309a013ce538309cec7f68cbc3b7c85809367c5ed17ea9d989baaae03658d2112fdc0c4e499d00e0aec07f081a7973b965bf43b087a274f946775b2826b116044a649e3871436549a1c581e991169b37e7b0ad260f8159d4134cd179e3146bd315e5950667983444c97bc371a080c8416cbc4c2951c194ffb02eb2a1f5a2a10094ef47e7cc4550866c562ca6e8dfa21226cc56ef221b7c57524d2889e5e42601b10ba5f0146b9d9b47a093d38f1dd440037a52094e193047de6a039d52a279876607ae3189f234df8a0ca84a9acf365d91c73771b371839f3590974337bb8aa0b0af62189be67ff67109d0736155b121399a302e69e7224103a6fced81c4f704021a9959b40fb6b7e624667581a21e6ca130d59fab288cd1cc34676397939eedfd1c17213d7bdbaee4ea5b68528fc47afc7ef141b116805dc12cfd7bc93c86844644b748944bdeb21251f6f6f291b4713aa3bb061af0919e05474aaa2353e20c62d95fec18114711921ecd6d0ad1f14008a5196e659707c830fe3b76bb12c2fb6bca11dd3f8bf984eb2b9807160f9b11abda2d28acafdd1ebedeaefec0f67b7bccd101cccfeaab47335ca2d4f35d11447c2e382d86a76b4f4d3019248d11a6a88394dcc35c57ca08603036e4b755739fcd1f69544256586a476f7597a1f303516eaa4a19fc8de1400172990a6f51ec05c62307b83d516d6df37b5024fad04589114790c01c970c0a9e44749b702716284442a755cf332c968548041456adc00171e296059e6af4864b0c99974f8beaf2416eccb8087a5a97c12b0e57f6664e746083f7dbff010f10040e3af86a1c8452fb08812b0c3555524c16661e3c492a772cc35ee2cd077a07c791d1584de3ef4d9c6775dc920ca2d33eabbd3ad2d8b0806563bfa4a441f754ed6b171d294ecd73ffd604c42bd081969304e8e9f77b058ead28944f460059a6a00f86921e25da1e02f0863aa48fdcc7b08092709fa0e2c9cf9852120accc24e10c86d0be5d9114af3b88e1de09e075aff70fd661baf5fcf83f35c0e8876cded517d8ddbd65a2d45b0e684a33fa330ffd6e7b01123c368907093706ce33f2b5580cc2ee569982e21d20ed66f7708fb8983dc045e028c586a41cc65300fb543f8208f56c0a5b0d9285208ec067e5357277bf02f9cc6dec79ae2bafb9873777b674f9ee6fe3c7ff7b5aaa11bd38dc8b14abfb857c4608e08b9f5afc1d04b47e8d0fc2e170eb7b46081d3fe8f2bf883514480e8e2b217c6fa85303968efe9be32c8c928978a3b5edc7379db7917dcf5f9f07b06745637a8bc0b410396fd8046d5cf1118f241d5651f242bf2e03b76876f4f921394f0b22d38a6b0490c8c5e19b7867abf4193e226ab65d38a4a78827b5448e0491d671310b0b802755c00d08aab2c72e7fc37eae7106eabac07a23c2cd1f5aa41cbf4c1f8d83a0a9e8e3f0f400b052423051e66859612581038ea8a730375100d10c2385e9253301728ef861cd10505e8760db0689c5d559fce17433d1c2c5846e026319a6c17cbb72ddc26a5f109095150b85fe8fa383e7614ee69b2f93920dcdc29f1d453de81f86fffbb89109902e3124a1443113f3623b69e9ddd6ec769c53c3b9f9e9e233e58a7fbf939837c76403b1ab5edcce2d5b4055776a3bcbcf268de8a4d1b8fdae6cdb3d7bca7f0e838f3b9c9044029ba408b6a429925275a58c08c256e033060831520d1101d38c18600587ea83901a2811d9cd08048c60a149ece3ccdc880042e1c4006009cd8c1c1835d019a50c2070798780048c13ed1038f8b85289c9496e842043948100122022021cb081be801870f72a89122e601018ca1a58410c6f8082a450122cb19060820082264ec20877408e13419a3822ee8870806da121422c0c584211e2b5876b872000ed8b8e2a7c18df3849b0d2bce44a122045f7a38d0d8ac408217f266c98b6bd73303093a44bea831fd9cb0830752a02163050aadd1046e8011c2045e3065c500aeb478c48003952d7e514d5861024aa962843b92a10ad305ac3400052bf01805641e42bddc3dac2263842940c85055c852810d167e80590115ba45982f7c4cf8718008ad2c6e4838685103fa834495175c6c504546110baaaaaac61023c40cf1c8dd61a468e416d4fd99778b8b60301d770f9d14733829cadcfd6fefee32778fb93bece52a121ae12c9a02f7e7589cc3e164e8ee4ec3c9d08b7b8aa1f42faf2b30610e32c4e1ee399c0c3770771b4e8645dc9748e55345d7bc5574afb653ff78cc0c95f31bc57935a7afe4b3eecfb4d5bdfd39eefee37e4170f71d4ede27dc7daf36530d6e576e530cf5b97535cabd8a5fd59fcbc101e2354a6d6f37366d779692fb33a72936ddfddd439dbbeb70f202f76a637fbb96bb5f67f9aad735bf8affcceadab27965d5fccb29f0873e8be2957d9e8ab22cfead7af71807bf3ab83b0f279dcadd699c7415b8fb0d275d8a33a728defdce6c50f7bfd3af6a94c51f5cf52ffff66a0b65eedec3499772f799930e83bb47e974b76628fd41163531109b7bb599190a1785571e73dd3ca64e53cc135cf1aa4353775f89a3bb871a74f1c011476cf0ca4105440e1264b22c8d6089a6dd068c68214b8d7068580895a911e5b2bd40862d94d214747809d57210e053a6c68e00d844e06e72a381cfcb0939b83a00fb0226e6545353a58b30185688c584b4204b1612c2179f84fbe367c9d2c5cb13ad9a5ebf7a12b2a0f044ab5a1612580fab797922f15f35125cb5d5b76a24b06aab9af8fe24b0ae1a131206d1a579f13276f132d6500003858bc2d8c54b104f1adc4761ac05f1a44129be535589096101197e2ce6b4fa715573df61126e11ef9398c7629ebaba94c025fc7014ef3d22e65b684ed7d77ad79358bec6711c5fdf728dadd7288aad96f8f7e5aa3989e3388ea2186661bd90583f66810a59f1572b7195c5e5fad0c8d51a694eab9ad3f8ac5ff9c2bf4f258bf694a51c7de17d5f1911f37bef8ae60b5ff4b93ef67a5fd556767c560d2a7cd6af46da938ed289f5b0da5396f2f521ed4947c9aab1e2d12d5d62f9aaf9c6bfb5a72c65abe6c47af19988b5272ffd494d19861270b9102f56b277a2180d2da4a111439a3046a3b171d27a9653abd643307c56ad87a0f81a9437460b695e2e51f8305a172f9708d645f6b1d7ab8b4c0becefaba6258826d8dfd7127b1ad996a929182d263a0dcd0cda250ac3d1e5445e9e28f62e5a972bbbb529b18b0935555555555a48103f569b12bb7879a2fb21cd0734342d24883fa336257ed881b814be68315a975848a319698c6c3a189fe63f105ff62e5a9757952d4434b5fb5a60329a96d85ff24563fd2512f202f2c97b0886df4370fcf12fcd52f897c6a683f1c36f5a1adf6996c4174396025a3d36847afc4bb672b9441b56ebcb97f2f5a2ac26e6b196cc69905ebf62d15c7f691fe2d88891b077b1d6a79a92f5b57ec6b368927c5ba066cc78d56c5838ac98af557bf95a4e5a2d279a97fd8a46b369fd8c8f893216cdb76831a7f1d158ec58b26abe4bf335fffa9a964ff6afda8c9ff138e18c1aeb65350f7162359f4dcbf7eb58fabec7d5e1a5ef8f64367ab86c5eafda139c92f55fc3f1a75bea94813511f390891660289d0916c230b1c2184cb82003132d842144055b28a104104028a104132b30b102132a8cee37764c01062b4a7fa2f4147428c567a5408152fc9b8211a5f8b156eb0635b6688c6cc61acd9dc6ffd25aefb427b7bcfeecb8a5f477f193925a35df96b01c5bb4d5dfd6ea5e9bcb7a712526d124f9b6d88c3a462c1b9bfb2c2736b775754ad74813e5be28feaa8c96bfde45775fad602fd6603b3a2e27318a1730a5f82c2f5fc21087f536ab67f974cad127d65ee4a175b378a2e6fe380529be3bad5ec815e4f8ae5b4d09abbd6aae24d6b3ee0d0388527cf151a0a16cbd0b85154a2f5d9f45f9a374265a1853d6b0b7f4b55a2f9b7b5b57f6faf1e58abd6cf52d59cba6e56ac95a2edf69d57ce24b841acb3bd66c6ecdf5364f70ca96d1f5d93cc1295d4ed777dfc64583e4aa3dc15989e2bbceadd9b47c38e514e4538d3fab053a21904f7c9d1f638bd24b2f45c092c1de378eb746def7b7b91fbbe385d15ef6b736a3a6841a4bd7b363b9eae1d2a12336ae8c66dcd6f1a1c6d27d87a666b3fad7dbc0de6336abd5af5e35a86b037bfdcb25f3c1583fc2c61f698cc6717c9cf2f5af1ace6833fe1dc71f47239adb2ac0093494aee3a5cdb5b9b52738e5adf9c2b7698536ad9ac86239b19cacde35d66ee9d4f2ed2d6568f304a7bc7f9faed30d6b3aae2baa70c20a65ad7426ba6451c66afcb68197e2a58d4f35e593ef758d5a2d1bd6df6ffd8a66c3fa154ee9fa97ab36ea6cb119ff5e9c12a7a4116b3a21ce0dadd0d161a24b144c74a1a1851644d66ab55281cdeafd7df5abda139c72ace1946329eaf88e17e1aedf5286344e9c6cf1bfbf81d76ca0c69a188bb262b96a48ad1fbf5573628d352731ac5d32a4a0a1d181a2cb0aa54b0a51c4f131c4b9ef3838218e0e06c217c3174d80a10c5fd79f5c05d48c2190d37bb84abaa102621e0239893f5ebfad5e7caa117d378b27ac28c72762387ef8a31031910246e850020ce3b74ae852b69ef5e3bbb8aca0743d4b0cc5500cc5505cd920e5945eeaf010a205f9a4538a1fe3b18ee38b301a23160d94d1f52b1a24d6b756a2288ae218fe7d71a4410a6bfe2ea49cf2d69c58effa90e663bdd33c19dff52c1a24d613540875cb95d3aae63ac1d31d6de8701cf15750e37dc0180b999882a174325561ca18a374c9b228bf7426a6c69431f1d5720579bd5e1c6c09128466e3aa4561b19c587f53ae6ace81d5ea5dabd5285220046a8d717db81ac7d6af5aaebfae5a585e31f75f3edc7fb55aefba654896ef25b6584eafbfcfa2794283f4aa8d2bd88bb5a285e56db158bf62b17ea504103e88dfda424ceb5d3e88bf2a2303e5ea59356cd4a84183ec71a9ca56cd272b5befb518ad4b10bed5873117edce4892e689f8acafa1415ad5460f715cbed6b77ca3cb74dcf428396acaa5525dbed01026043222040acb314748230342f67285404eac5ff95c1fbecf8693eb5bac0ffffe934ba60c11d020b9c810c785c4aaad6ae1285e0f57332e950d315fec96ee24e6618c45860caad2c99041041932ba94ab556dbc91e1df522cc36712f2100b77c0c109719ccc0bce94b7d2c9bcc0889289a924ca59e94c4ce950aef7c59a6f4be9b311df3796ceb2116b4f6e5cec58ded8288a2e2150903714fdc59b72f5ad2737a53b0364b11a272c969398c3de86e6d2fc69469a24d978c5da13d63b4e58454c7c810ea5eb634e8468800cbf6673bfe537a5ecc3778d342736b2871a6da0c6d2fd614f3449b11ad4873451c6f71ad4fdd58f3462cdc91d5d3765cb8855c309717c1469211431718c52fc914c185b94a16ff54ead4722cb1d7eec6361eb57cfd28c35a79a532c2c9de68bfdfd300c1194e28f61b0be258615e54ab67aaa29b7fb8e2fe63b62940f57230dea425dd7c79c6683d8cb1f6a2cef3fd5f86e14d6b76a50b78cd57ce28f1fd24459fdad415d2f9dc655ab398dcfde92b5aa3dddf20976c7205a90440b6462f8770c6b5a56ad67b5c69a96b016c40759c60fc3701c572b164b14efbdd7c95a172f1a10dd47e1128935142ed17d2186230669798daf257cd8ff4bf64134bdb4b86a3a9e8cc96a5a5e5a5cb58fd5b4c06a2e31e79505f6222d4b965a9691f5b556cd86ca1344b05a99a92922d64bcc711db4d8a5a1e27291248b55d37169a88c4d636d6545fcb0868323e69010bb6311b1f057ac9a96564bfc505cadc4777d4bac6959d582f860f5a24b14572b16abd50a431c31e73e8e98e33fd6b2bcaba543078b24c755e8aa657189b52c3527a1754726788c97d56ab59ee52ed14c6d158aa28b9f42cc6f7ce2288654ee666abe9e32a412c31f58405523b668b7144201d286032fef0df205e9ae137acd55938538b53048176448c3042630012a2a77ef29fdb631bff922092f642863d761a5182405a4eff532d31245166b6a05469c12424694892c1146253303a392bd4f6666155291e3fb6054669e8728d6ce5caafb85e6f5c40ac238eb572e166be51a3d20c51acb8c38e502434506c982bc6178592a189d3943454583f2c2041a5d845d6041a68b0e8c5d8c5991e92209325dfc00a474325dac208405d215e2ace0c3992fa597b9f169689cdcffd2e5923d19dff74e5853359facbcb1dad10048df7dd62a2b63ac188f2b8a2b887958850c37be9b292a2722b7c78c0f31b099faf13c3c207fbc0f878e8e0f30ae172c267b1f40682f840c497eedc666ca07846686aabc356ee4d0c1e36d5ec81832888f173286fcf1405ec818f29ff642c6903a5f7b2163480cfe064790f7f90023e48530800c710c7916cd97c1b378683befb3b333ae688c769e89b8b3f2f99de7a1852f8abfe2f9d60ecd77cb1d9edfd9a13955e943bb250fcda94a91e66512319ef7f1d9f94be36487a7e7431aeb7d6819bc48e3791bcdcb9d9a6f8b9772e7439a900490e1fbfc1563ac08533acd063eacdfa165f03c829c80f4d1f4d478785834463ca26f4b0f8de72f114fcdb7c528b43dcf8bb4f07768b78cad529c0885209e34282fd1ce87345160220fcd774b9e6722320979685eeebccda9ca1d1a111c54545565ceeb50955eb6be8a311910a9d98cac6722e6d0687086d07c345f082608cd67034c79ff86e6dbf1a5bc8f01cdd753a3f976d068be1d67cafb4ff3dd2f40683e1e602e958ffb3d7050f97a7c36ca94f777d07c6ee6c654793f07cd77bfe0a0f96e10515e2a1b34df0d33e5fd1a3433e57d1a341f8e2fbe1eb266068dcd548f9bf9f2f2f90073cfdc6fe1a02aafeb86e7ceb06287592ccca18a2e3b44081182c4010488cd171b1b9b2f386e80dc7207cc0e1021906f47861d303b3a84403b30ec84d9f9b2f305c8cd979b9b2f40cac4845480f4dd7cb9a12104f201f97203430804e4891b1942a05b03520647ded2c95001a6f40129538a44ce94e207f982f4e1d8e0c009b20519963e1c3884207d34bebcbc884f3346cce182f48d63c6bcbc88ef8221c4116bbe9475bb94d74b794928c7bf2b164e191e41ba20852441fa68809429538a6fa6f4d2e766be1853fa68c694e203291302ad6a365f9e0881c69a8d0c40a20881c6527c212f90be1b9b2fa5f837610819e30b3295c5971d226460c4fc268817a4f79441c6a0e24b9941e964a6b8a2741aa39c28d6c4fbc3cb3563051b5f5cd17878407a4d880b487f91e60a6bad3014c72f67ccf49433686431d8cbd55a05418eb5500c82bc1fd6c2d714794711c8603b6230594c2683c17e47cdd71383c96232194ca6c1efb0f9bb23f65acd90c15cb1d76a860ce662edb82e1749de7befbdf7de5618471b38316af22c598f67fd904f0f8f6d47270322393835da03f9f1b1d6191e3c74e8c89103078e1b373c8aa39acfa3f0321bd47c5e2647cd49cde7638c6a3ea7ba516b52f3091511954c902091c95831301a0cc5c0c86236b516d5929a0b8cab8c922437a120a023468afcd47c382e30383e2e30af83a7e6d361dba9d1a1891179b5705a54438404b9c1a056a33d901fad33327cf132d53ad30aa30c5b54ad33ad16d01121233f3d2d22321fb51d2e93c97aec90c96453b9ffd1232777bf38909a0f9b1a0f99e77017a79ee663fffa5606bf7a24345f8f28140474c42754a4f6537bf9d45e3cb5d78e38f5caa04664a4aad5fe6bffa3f63d6a39359f533d4ecd2734a4e67309a9f9c820359f8e9b9aef31a885b59a0fc78753f3bdef6b3e6a359b9a4b28476d8c97f1286ad468d46635b25653f3098169c1c0845186ac9acf1545193e93704a0c0a5fad952b077943478dd97b8d19d9d44b26fb21f321eb21b391f190e990e190d990d19091b218ec158bfd88f988f588d9c478c474c470c46cc468c448d899f0615438517c191d6376bccfa976bc4fc8a5c3f7385a9066fc0c990c06e300ca27bd96209a9e3c07cfc13b51522d0b949a9627350e6a3654906a2c3365ca1daf17116290ab4c8e5aed46cd47d6a8f974cc5e53644dcd87e351781465bccc181718a7022354bac0c8cc6c1036f9124e952c3360ca1da593314384114bfc9af7d0ddc91088f5364220d6f708816a17881b341bb44bc38521c4613d0ddafd12e2b066cfe359cfe46531d80f14c8fb9745ba0d8d8998c31212eae9c1c1f9d7a1832467cc7819f92ece7e9c40febb66351fe9359f0ed57facf15aea4f7ecc6968fc8c0675cbd93b2df5f7a7417b2296b3276929f9364fb79cfd2dbde69d1696a4d7d06e796b7070c4201eb52316bc303912839c6847ed880530b29a4f47ace62361359feb7a61f9be64f9de4c1625cbcf94ac304ad67b4cc21fe18fd087e8a2df9acb0ac34b3392e218ee08c3f0b2eeb8bbaf9b1b5b8577756704d98448add82bbc3a97ac15de0eaeeb5e31bce33dba3e4224618f3016b6c2d5156f78c37bbb3ec258d81aafb812ef0dbae2c5b9af2bbbf726b93f4298d8baaef186f7deee65e1086ddc31bcabf1220963570c7f5c510cc3311c2f9230bc3c42d87d5dd718de7b6fd747086b8977748df7dea0bf3a88b8948819acae785f77d4715faf5b0c049b1f1783f186212b6ce54bdee8891ddd1bdef0debbe32d07fa075bee190f52e0efc10d5994f18066c516553015424c99c28c37d1a470a61b389099f0065cca0d4ad42085a6a3747083efcc1236c204ca0d3fbe38c23d7a90dca08307099ef143d01434374134d8059ae2920194abb201e8cb705b4e8c1f3bb930a18e191eb4cef710c9008c8e0a80e45e06e473d0703035b0786ef582ff6882bbe8ee4e850325134d997e7a75125c9bdc7d8aeb4668afb811235e80d8dc79974f5387910805011d3152e4c7a7e78afb1269a467af36cce213475323da884b4c8d2c4c8d505844feb658c085e4616ab592b58448c165bcc167cc21d3e1075600a646282f1cb01e3a4c987a79307e20fe88b12c99cb894b88051393e880ad3030e54a62cac6152eaf20b1062f6425985a554dad643c3e3025636b4b4cbd74cc085b49563c2b18bcdc88ed88a1847185a91a5a0d4ddc99f1b874ccaa4a5865e0126a29116b44252e1da6c4241f1e168c05c4941863f11875623ee21d65a30eccc92a8396072c1e2c18cb27b321b3f1d2f1c2d1f24024a304160f9188125663a682f8c6dfe382f8186fb21ad66cbce28e58342ec972b0c25695964d4b697563c665c5c4d528135de20bd663145b535a346a9660b316ab75c5d66864e419af6b8768c3e50564f5c3d40c20b8cc58a38dab490963094aa8e1deabca300108a2e0401932635a1ed30b5f8c88345812a45515a480670859e36a81f04114a5aa269e7002880b3d32c4c0e400258062031780001243f4c063135280967561af2ce8e28c083cf044058ed8424208573eb01245690327407ee4c0a18403b60c2104095a703e7a6815c88c1290e0e9375555460bc0b802420e1c33282e6081084000890514e1420b29e042f0a04a1425a40d3461010d3c4086a31048b1810b5400024bf0a0830a2798d0c519283c00812594484097a97c2219cd68ce5c614509c854c0010d30c2871758c8272e842b205441324a72cb99d16ca0093235ccf042977c86c0019213a3247e85151b6882090b38a0014614e183005ae8828503a425496e41467c7268cc46afbac20a123041c602157040038af041002fb4c042172a7058ae68c0631b430c284f6a668ca0c7971554b052a507071654084a05f1aa12cb08c9588911bec0f5c32826f421ece1fe843e62cfb5c576c40cc4c70124ec716d5a3cc41d57c798e3e278d5a0414324c71ad90c9286876c47ac076cf66ab958ad90c55aadc4ebac2eb02018986abd5aaf71860ba8f5127dc89ab828d55c382c984b6894d5dc51c6c28d51228d381ba330e10053ac1961eb35ead0f81012620a4645768e3a33b884466c5653c6cb522281a925509acc4c0847d80825a70553589440a40c97bbbacd8030e1128e3cc41de0a50dad32f0c0148b0564a9cb8d1aa213c40db800606ac7ab8acc86cc86084e6e005d1b06c69412f6b5414e0064eb25d2ac9cc61456e0e21a67b07cdc9a225c4c80c38bc807bab8c2886146185b3199b5908c4f3208449450638503730213c2ba32126921298101376c5082181770903d5d684c2801a0c054acddcd6002cd4fcda5d2434cd2da01c405d642c282b9668841481b883e604ec09882b59068985c4736464e60af2319d3a984015339461d2018eb568386058b19d542e0610ae63afaa153616a2592abcbea80055b25611119228ab5af21ae2316acb583a6c9eb66547ae5b0d143080f1c445c193c9122840bcc656b15a1291284b4d4928d2d1eab9b08f4928d439e49ebd5828d3cab27a31093bbb211692b1c3d2f972922314aa2c305c3f10ae252a41433e2c2196fc61b9698292233c626ab0c563f5e37638f1a1fd1470953369e2beec49d515c8d500b61004b68750821ca50113184181d02b0458bd476da653317c2950fac5099c2b4048583a39b50111c214130f8d10215a852c11626d06028488dc78e16180d64a0071e3a30d2e0e6e3862cc6a280120d08224c0f3ae47003981aa6e00ce1b1cd708026a6aa3156a0fb40052980004ecf04e002ba1b4a026b8579e14bd00b0451f810e6860428878d18ccd5ea6183c402aee4338a1207484e8c6e467c7268cc66d08c36b4900200689839f280218410b3a50a9520201d36460e94f982cad012a0578b55040a0d48000e0518800a2320c0061b5c88a234946488ec8a21be200c633cf32908b3184d104eb13273a508a1083ff0f280ec899003e1064225c222c42142a7d1776f2e10978f550f9a1d618ef0c6b531abb1a211ce2e39ce08632b58f872b9c2566b158e57bc97e5301ea42d754f759855d115c8934f6da26aab83bb6fe03ef66c3d403e403e478e20e9092a12d463c44810901123423e3e1b31d8fbb78f90c0565dc4c1f769e295d5b5cec40e0dec8e94bb4765f57653db26afa27fc51d28bed3c37da3af73021d2c489d28dc73cf53d1dc8bfa9c5fa2e47196287915e5659d8e181d1c489d2ca4ce53ee77fa731ee73f9fe856d1dfe8b3e82709ae78c53df331fa6c5087c566b381709ace3ab2a2525b1191196ce17bb5a96b66590ce459d9eda318facdc049223e10814145d99dcfcfc1c9e6daea4d048a48ce4f35b3f8374984c9a96622b29c17b8a7269aee5ff56ec5ecfecc53f19a83f3b2f1b2c9edcae69de25dcb0242c6b8bb91ab498ad23d01129f1ffa756d4d224890082109420284e4081223488a20f941e283a40709122121a1202120a1234246848a08fd08f908f5082109120a0a0a020a3a126424a848d04f904f504f10122021a0202020a0234046808a00fd00f900f5002139227424e808d09123478c1c2972e4e788cf919e23488c081909320264e4881123468a18f931e263a4c70892224245828a00153952c4489122457e8af814e92982e447e827e807e8e7c88f919f223f3f3f3e3f3d3f487c847c827c807c8ef818f129e2f3e3e3e3d3e383a447a827a807a8e7488f919e223d3f3d3e3d3d3d523d2889a37fad8a05a603c3c1d1f25a603ab011fe757e840ea228e1e8fc0dc7f5b71c1643a418336408cfe5fe06d3c9e5fec6ee72fa0fcbe5b03d1ebafeb3bb9ce2e4feb6f1ce3d93c5bf25b4254b864c914372eebf51b6d5fd6f34f758774f8232648992d71932e4598caa1838c4060837b64d8ad2ebaa898454e1ee361bfbdbe5559b428270f796934278109273cf79254ade07e733b005759f4f140a3d81bfccb2d8dd5f559c0ce20276d53a9c85bb5f09b2c213ee5263dc7b94706789705fafe8e0ae5e092283bbd782ace05ed3b9fb909271368a496eee8ec4dd85dc81dcfdc8a5e1a647de94216f9ef8db3ed556eb7ea79f6df56b16b744c97b911edb119bcfcece6c5017ee80810480f8f753aa30ad7ab7a36a55f719bc1411fc197c9110a47ec9ff8410fc9dfe2aafa2dff34b68af7b228f7fe7156d9ff2fa53aa30fd99d11d9a027f28d32ec8c99a156ed36b16e1f38a7fd58f7fcd3d16cd6ad417b216c6773bfdac8a6ae0632010ffbe8ddada9dc53df6cfec64e69e99ab6c77d74ed2a668a376139b91ad83b7652894c5269a0251366d7fbfd13f57cccbe9e7dd6233eb3eb727baf3fa8f7fa3ef23c57b225f36197de0bab183a2a58c918604393c96945a4c7500041d6061b232d40a6acab00330eac0cd89acabae90c285aa170000055555552e987265c9bdc9dd7f4e024182c7dd6d3b241023406cee6e731288bb4d9b9a7d9eaad3ac7b56abbfb33ebf67a2bda87fcdaae8ff4d45f14f9fbf59f477fa57cde235ab99b781f8f74135f3f4463ff758773f7277f325c6520a647a7210e3c51549a8400327c40e6a4c2942e70512172f382c010210cec4841c0bf0400052e83dd16e430c208713848a14a003cb26ea0550704007ec862d67c62552c06488105850e540b045836019588490c7105099006d440a260da1c50f054c4146490d65d4600647d40ab0acf07a6106d3162d2cb45b0cb1f3c10e25b859418b214330b01b2ac38684a2b41785184d902138222548b7817ce40293c78dd428c8171b0d4ca500cb141e6aecaa845dfcd8b029aac989b23e5121000540d6131f34725054091981c4a39314812730805c0082bb03491f3ab82e8a6703f14fc76d2cc186e00529a87284e446430398b0616702527851c2931400bc6e40c08b8d0200c482f8608aa12a07145ed5069892aaaaaa2820068b15510d33865e7041ca6c0a981852416ca99142173235c094f1f2a187a1108ed03b708085011b6e82730befd915e1eeb7aa0acca80204351e00841860c02cb03da0456036830374da1c9478a0000721f0d0c2619270f080a2060b30b08202321a808588aa2a3292841570172d00664929c5ac03269e86c67020a8dc6c0a0444c08731ad07a8d828a979a005d0cc85114e70e42688d15e5d55d50ba03e2881fb2c4b082a8e982f4d43c0a0a2e20ccb3b7b4c213300103e90c146a60365bcb0c0191e0b54650851516488114174003ee0061ede1757724431125233e5020a2ecd0aa09410860b163d583e985d984c5459012e8f00dc081ac11822f0562f38850a152b3162c400d142505844062890534565080e68148052e540a48b23200eb490e644071f1c800328a4332ce184b681aa161c4921260c142e9832650a530f56a6a50651ec35b5259c400212e0c4005a400107117018c1070f1d1ea8a10417b480fb0f5290f49be2840a5042644000f674811e2f18c1728d5985e053010f4ca0c6892a6c08220714f8500503186cf1822cc6297470e0a52aa80052c838a2c78a1524d995a1a2005180d1e50163442d81630b939906b03e496821050b1870b463058802581e1af809c102c268411159380104918f89f28baa538d5595a0f552555515c5142d2297255e5b62464c8199143186183f9a902842ec4182ca07102ca049d0a2090e54153c1041127e80a223ff2650ee3e648a6d04a386223440e28314318611c6e40840440a43e71020bdc2163a9808aa8201a11d09707901c218d8470e5543406e555555357e88a1051eeccf54e804aa1a110ce384321e44719ef0c4982dba882872ac602e943f4c60c76bca1411ba54d102133117e8f17481205cac4c0378612a23881060b0d975808b9329547c99a209d158a4030e471cc9410682a02c2b1421a02a052f1c31a58321bc1023460c0f4259d8a8410a5fc1054912249c09151258515941070ad0225241a9f628420c1e187c0483318440e624101bc40801934d9932c583a709c02e5084ec818c0f1e8003a4811ec2157ea8004d025ebab8008064e47372b22301aa0b74004083aa1160e0139303ee8a2953a640792a5363ca8529af3f415901231c408c291304c6c8453c810d69195d96290d10f9828c271c5823a8d2a2c14232b401184f17e86126045d0e9c1b42386618727726eebec4dd814ede2062afb60cb5b6264eb33820fef1f054b4b7710f29c9c91b47ee4ecbbc15a7af35fb1b7db5dd7d6fe3deeff46711d09d7bec6bf6d916c8b6facfbca23b9fbfd3af5116af9f7b4d4ce40d993b904f083fafe21efb6b4e53fcdb3d96a76628aa0d2eee7ed77caa18289579f69a7b1bf7703b4d4c4d768c3e406292624425ca4ed1cf11086634b003880920f86186556f850e6648d9b2431416dc94a85fac0ee0097182ef8505cf3d989f9868aa6b9756ed248b8032f154bd5601b67ae5e95a26d6ccebaa8b5424562b6516f3f2cae395ba69e7b565b1ca439972dcdd85e399b762285a37ad99975915c9dd878880a6185df3ce4ddc5d4810e7a9e8a94dd508e9c444ee7ee3648d2b18b8d768bee6ddaed57d4e752fead314ddbd669fc79d9d00c9162c0ef0c4a68705143d01028a33ea49972f4ea001015e8f1360c020028c9b1f3f5495510453551503caa8aaaaaa12e3f774779c93b310902410411655d19d8a0e807c81fc92f33a3834de082d822768e2851f90a14a8e0b34f921d6415007427c58d7045e6c0841154cc078c075c96a48104678c2153777b18614221a574210022b82281c58d2811dd0520766a4858012a61e60d80d506d6060b2e4081e5475cc4f099e84603ad44ee02e0ba38604bc3e60d4c112134840420b6476120848fdc05344074858028b06dc40b1810b5a88e0049bdac29401ff19c388c3c40c717759d517ee2ec4fd427177d6c9190ef0bddab28aee7698676fbcb27bcd688ec2b1abee6d33ef38add80b0b7a40e08ba4202c742004b41d3ee09480243b4820288114700a90015d1ac1ca08346aa6545559f1a1aa4a04555baaaaaa3c306506ce6b04244d067caf369e8aaeece6c93a233a23671113e8c88f99714f8f5011a01e233dbb5d915d9010c67927e483773d424047784c76b72b8fbbb78078306d3f8853e0469b3c76f71feeeec37db4717722eea20a234c666688fb0593f79aa154f4d51ed3dd3170bf08707728276344b83babf5fa79c5edd37efc190af79eddedef319be626aa46b79a59a98c335116af276e9fc8980e19cb89f570f79cf689fc67f038679a5127ac94d189db0edc738fbbef602ab8bb8e1c7bb5e9d0f4c7d6b3b39d561de5b4629caa713bd525c0dd8db8df2edc1d75f2a50370c7b3d37ceadf1377677277ece4cb9555ad947b55a32c304afea12b90679f694677a44b0677bfaca92622b48073010f02b080165c7c7102054740e0c38929944cd164081064c0489825402c49a125a6aa0a88a6aaaa146c81405555d5097e7077275b2a70b27502275b2570b2f501275b4d38d99a80932d249c6c05e1640b8c93ad0238d942c1c99616275b4f4eb6724eb63a70b2c5c4c9568f932d9cd52b0673d86b3673edb4d19a7b6c9433b3c0765d33ab22e9357f905974adc2b651cefc439bd816a82239993a253ad35657d9ec46a58c8227ce6b5a6620baa46627bd12e5548753144395ac565acfb3cc7b8a8ab49e677966ada42bd9bc77bbcb52548db26656555d746654cd4d548dee8dfed6bc74aaad76c26a2577c771e35451a79f66775be47edd6b9c2afac4dd69cc34d2e6e1d4c999593303a5b23a05b3b8c989a6184a45ca260632f578ad568d58ada49b8038c9ddc91a77a30cb5b6456766570d04b65b9de2ee34dc032759e466515d6a9ee14b9aa24e51cfaaf917c4409e2672062b7fe61f5667c071b7e59c6411725e7733b8bb4db31fa5d30394f1dc3e537d2485937252f9f799eadeeff41f00cc4e5e8144e401ce9cb667ee99fb79505fde6e4d39af3b5c54560d70c600636c58720600e30600e25a031c19404766b189d3e1d8bc65c8a2c7e27ce2703250b90d4b4e8617a0d014e380393be14e24295bd51be7d5c9d4504e34e775a76611f4bac32b2b65b47938dd913164e16efba17abf4e8642d3bc3f989fc440847b303bbd56f212cc4e5262e8d91e88f6760c46bc09d299598c9a7a6ffc5a6907f393dbad69a7f50a8cc1ddbd002070f76076c2a558ef54a4a6252f404f2e9fdabce50ae072effd8eaa5f8d4200336e2b094005c4512501beb8bba76ae9ee444e12a008094318b9dc7f6ed545b9149b4652d9090943129ea6d88421e7bcb5053e4f45b3ee7f7a55b35eb3083b2a09830eb7fdb4f93baa56f3899a68cb348024f2006018c0922dafad9981fb87d5cfbda61dcc4eba760f05f3937c6a7328989f04b393bbfb70520067044065c392db38b6d5f9d4a60064701b96dcce3c0cc40171eee1d8bc05d0c331c95b003a5c0adde515884b53d4e98530dc8625e7b4e25f5e5f80c28625974f2356cd50396fdc030e350135bb7759ca5e318b9bac46b75b13076ce6ad984d71145ecf1e8b46e158d4871bca799d9f222d8e56519c8afef20fabf9d445ba204ed23b1549ef541477aaa8130e49c969d5516c56d7bc6a1ffee172da9efaf733adb02a8e5a35ca66271365cf9c535dab9b642894b7f50fc7629c14fe9d2afa2403832c4e0affb614fefd4c292743aa468b4029f999566eb71fa8330fdd6e4db833d52d10f159d5680a0406b3d39fa94e1264cfdc3359346a63b9b2667db61b842b9f7b4d2d4c5d01e1cadb9cb4669fddadeff5d9ee2f5b60727db64c9f7b2c93ee454969a234c5e65093925d1428515965e18cebb32d63c3920b66271c9b370b44d8b0e43666614a6d71fa6c370b394f1a6221088e5cc18caf2083bbef14d8360de5bcce118e123633ad00e48e74a23815cca06faa5a49052a1574f013452a3a511c4e8526157adcdd08509b4d55abb800a440b77a65773ec30440ca37f0b7519e8aa6a71a0021df4358f4d59687ee50958732a51086bbef53454a618cfb9959746b603ea57c9457cb14a4dcdd3f2aabd828052beeee4b494c4829f4649e9ad314f74c2f59fce714ed45bd6ae4450977f753a36b06ead4f69f1da2160d2545e934c5455e846c5872ac9ad1d434bdb8dc8625b7dbade6ad6a13d702208bf454710f001030716cde433b9ca198e8a9a243bd8d8f7e99454fad84dbe19c366177e6a127db027b1b1ff5361e3a5af389730f4bcf0f969f229f44a805e12c51d0e23be75d94db8ddd99c5e9edf633a5f24f9b6cab792a1ac440a0233f47bd8d7b278a4b536cb25bff4c2b272ab4ea7535fa995656bdae7928c52d6b62b20b165dbeb0fb87aa686963d13ff3aa55dddbedc6bd2eb50c388b4d68c104a112ac705b2ee7754ea4d7c1c96d28dc6bcf24b934c545b9c764095f4ab871c9226d379b7967fb6b7fa814fe7deeb11be562c5ddb96030d5c49494dbd856b3ecaa5fa73d1fb3e8ebac192aab3c15c5ba0fe2292372cb19bc85882d1f6c6991800449c24902e9b6339f39ab3abd027fa38fd53543695ed2dbd2148dfa60aa43f7d6c0cf3d76b77acd3c159f399afd55a32bf0c479e5a9287e157d9cdfe86fb4d7643b832970a3afe58cdb5835ef520b07b47871dbde40cd2e6d1c857fa78db4e0c842650ba669bbdb3b9b66deb897e54b8f25b308b96deff671fb94d72cee6e33710a5c620420caaa196acd6cabe29f89b2f8a7d916abf87bbc56af3df6b3eef71bfddc6b1ac188bb3b10afecabf987e2d4fc692611c0204500e3b6a8acfebaf2d09f66f57afec651f84fdc2b45d022e8705b9a6253ab1bfdbce670d909b33927cc0637da41b00a32e84250c8dda66a608bb34eb33aafb36616a7ff5b45f1cfa68da4c270f713e7f5836b6e9ffecc506836b314291d6c39b99f69e55433532eb33b8d83d71cf53b6727a45407521d90522e77b7e504759fd555eb709e457f37d9d09e995464e5d532aa8c4dd58f7b516ccf54f14e5e8bc8a81e33eed9c4c0fd3bfd4e264e1fff06e2de9a976c2afa39b66c43713eb77ba3faf34fefbc42256123a1beb8db782a7aaa390a54ce6d6b665374f7502e9baaff7ccd7e6fa3511fcc3d36274d31ceab51fa73af896c93b09d99cd2b30a7badce71e9b7b4d2d02da2377b7f154946df5fa3bfd411cf55a0da6a9cec651675ed1963cb399d932670eaaf8f75a37912cd0c9d4a9fe4f5394dd6beeb167cea6d96eb5fddc6b22592157d1df9ad97c7ed6ed219f7b4d2c496e2a5b4e529195dfe95f815299456d3c5bc6f9ac2377cf9dcdf9b76af6443130e7dd9f4d9cdc3a76fb3fbdee808f7fe3deb3e8675345f7a3b8d744ae65dc556cfe994f144aafc0cfe572af69f5e2eeb6536df5cee4a968eeb14ea64e3fb3fa97d72fd79e55ef78685e3394dee8ab267e3543a1bccfbda67564d5bc3feb5eb3ffb9d73494a6b8686807997ee36f5232b459f48742c12991d244eccee7cfb432349483a3e4675a192a87944869a2e08a5113dd6535eb7e8ab4bfcca2b89c7740228053c04d028f4820690677664dc49a77381f2735d0b30e5dcdad6bb7daeaa553cd5beb7077ff995238366df12e9fb86cae7aa79eb161c96d36ef8d71ecaa8139cdacbbeb70526dc2dd862597d9d6cc52f97663739ae2a51c089f33d39a9d4c9d0afdf8fcf0d096c249af9323f4e3d3bece9ad11dcaaaf9442a3af3a9b69a68aff9d76e559b6b56a3f4508e50fb3a422d4e7087b25a65b18af2d055aff96c75fea16caab52e43a1ac89711b1d8282b2699bb46674cded130e977541fc9372662a79ab1ab8f3eaf363828e4165bd119cec95ee7ebb39993abd4690bc1e6e3eabd5fdbf43ff37fab9d7f44972b855e37267ee6d544577bfe633455ba2bdda36103f30e1803bffb05a74bba91add6b86fa29d20ead7969e3d60c94c2bf550aff869448e15fc6e58d75433f455a2552f8a7d5a65f16a3772a92147663282caa22a9a81313dd2abab25246c114082c37ee2dad7929c6833c9348721e79667756717adde1cc0cdc3c66863a7574c861afb61493260e779bcd3684cda90e8bf3ca7b562bbd6d7f8a71da9a2936792a7aaaed2dcd5039c5dd7a1bf77412d09abde5bd66a8bcfe5e6dbd8d7bd704bf3fd7e7ceb82b12b7e07bb5ad9945713f9c57762bf913fd9d196acd3b4bc97975626ef477bb6d353b69f676db2d8b59a66caa38c5447aa722ad9ac5a78a93529c815152fc5bb5d20ee2a5bdda7878d4f676c3e51f5e77b75bfea1acdaaeb959ae0627867bee6fb967f1bf2ed24a3b7bb0837829e71d6e0731706f9c16ed8d53138725040c60c1c122bba2020856fc021f20e16e250c0fa2bcb7f1d1d09012335361badd32bab238e0c62c3e79fbc7621cabe67de2dc3bc2fd1469a1341027857f4ab82a36dc5514f78a501c6671939e8ad444e9e808d4ed8659dce4ccec46f79a97a83cd13b15294d75d19a7b6c141e4aa47593564ad3d6c98984cbab3671f9a799b2aa959a703e3a71ee39adf8b7661647fd1469d5287de2cebc7736f50a1ccaa72ea2a244d5282e2aab43eb518a59558b80aeba37340577ae380ad89e4842ad123353b9ddcc0c75bb9decc6013766f1d9a414c4663e712b0e977fedce2b50afe74f91f64c756fe7b5c7e2364e817c5a1cbb5774976aad63372a95713bc52a0fddac9a51206ae67528c74906fe4c2bafa384597c6236ff70f9b7e653cd5b0a95ef14c56c6ec2b651ce7caa19b85b26a728a6948a948772cecc3bd15557e2283133958d4371676fe3a3dbedc4b987339f7eb8a7df994540f73af43a27125ef192129e8a6a1edb6a1cf0b7f670c01f0ef86b71296635fb53a4e5eddfd20d282cc9dcc92846b83b6d2fbd9a73149b759f37ceaba9a21f4cbf8b0925b88f53eeee0088326e717712a4dcb38c10c5766696457f3f6d5e0e6c4e26d6e9f5b3ee556ce2ade2957d3503f56ba5ab64e4c4b9b714924961b8af59f76b55565212ce0e5177d6e7f3f0aad98d811fc4e9ce0a4e26f5d8ab8dc7c9d4a90e97596c63f34659542abf518f3dd1a853c54467de53328b4fe094a1330381e8caeedb6fe32825ee2ac89d6a060271ee7b2c1af56b46d315e73ff3ab39eb7e0f7103b83b17279f50c0d5fce3e58d3bf3d9a6a68aaaed0ecfe661d5bcf30afcac331ff85b7b7fe615a7663e59f487a659b8144f6c04dddddd0110358209eeaec5490e82f66a3bd5ec64621d2ea32bfbdbd1eafde1ee1ab8bb0b9084a0f2048893725e9b881c07a4223e8310216cdebb9d5e091122e4970cfda9e655d0e880318811003e168d4463391a2137f8c2ae7af3d4707203d6916a10e1c9dd57279d94c1e553ab3b43a12cfa63f1ca6e5dbb7976423e457a80763c4041673e2284441774c40709d06e67ee389d7b87d527764a359067b3ade66151de175e3859d29d195dd915c94809cfa94e87aa51904652ee7f9b80934d80f0a08a5935af79b7434fcdfe3e735675747a66d66c5776abdadca80dff4ef776fbaf6233b5f16359fc98c5e7ef208e4af19f79a33df64f15e35c6e534ce5763bb31a15259f3c15ed99d8cc55dcddc9dd7d2813b2480660eba4b58f2de8c7d6b3a33b7732bab264519493454f4e16a52c56cd5b57e6539bb9c94eeb75e9c4f908af78c9dd77ee46c0df7a946655c5bda2bdf3ca539180bff5e8c4f9c87c4a7afaa12bbbb39413e75ea937c8275e79c029ac4e99527ce22cc5ddf3e9c195558d4e9ca5f44c16ff369ac46a2d8529b39865e2a9280fef9d9358dddb40ac6e5c6631bb71419ce6f576fb657607374e4571cfc42b0616f1545445f12f4bc96d6e3be8992c066eb44910a7590a173bd410810734c0418b2bc884018112106eb0b6b88e80cd8ed8c00bef001142f58b05f438a18a2482d49c00c500b42f343f04a8c1082376bcd051001d4d78c087262438d11480252460b9d31c031dee8e43963d0f66275c8a4dcd833bee8728dc88cdc7e6b393aa7a45a1d41c029633eb3e98ea30dbfe1e8739420f983c8b792814be59292aabab19b4a8577474960c2df91ca913c5617719ca4dca1a85dd6528ffffea95115eea44719977669c93895796e562bd5848c82c51f219e0ce5433f14b865ecd298edde51477a67ae502710422186968f560f7f94b967c0e8e9a539bb96e5dcf0fad742809e3eef9d4ea0759e02fff2d9fdafc9cea70cfc47955db3ff3df52acae9955511a7c7e6797e6fd4b967ccf9679670613ba4026d9e2d4aa074d4845e55259f44a5eea442fd0dd734e26b9b2575b7065d5cce3c4bdd50077ffc0c99b177bb501f18f27e795d5f1ec34d56614566db5898b5a33943ed5bc66566d7551e6e1a533ef168759dc442a9f2a52e6e125b6c7437395dce6f60907fc9d3836a72906e2769bb6bfd64c71aba23829fcc32c6ec24377aa2e72f72b2044c0dd437012099933455b24b69f1dbd53d133ab3c74ef1e8bab90487a3d45f6ce6950c5bff36fc5bbd528545e976e08eede7352e8cc5e6dac89813f6d1eb101d98aec98f9d4e64e4eb1b977822cba62202914c6ddcffcf449364f4583a9aaffc4397dcdfe6651ddb36a464d74b76bf79f99dda1bded5e45e5a129ce22a09b6d751043f13353396ffd5a3efab8e47a0bcc3d57b3dbf29abbc271675cd7cf18e4220ce38f56e872ad46f15ecf11da3cf4dbc1a8c1f5569295d7b0aedf7b572b23612cf47baf181edddbbae1bd224b84f086b715e290722febb63cbc34363164b170607fc7301cbfdcf1de10765910de97afee188af95e5f786fe89a12da8437648de14541782f2bbc1ddc1cf78aa16b022bb135e3e2b8f7de104978c325de1a6e7859e1bda188bbe1087b19096577c8755737e292db0ac3d1478485e1cda901ba238c3e3127e252e8a3873bdc51c41957e1157d45804bc525fdfa1d6f7853705d21917bc355188af716b93cd77565611886aca23b240cc7f0f688b6d80e97904b43834718c35d853bee78c5ebe152f8b03b7a88b2fc739705d16913b8cec402a17859776c5d9a2bca1086ae9b7885c41b86e387b217ce5ddddbea197d80f4dc10c67285ad7b6f370c4320b2ee2b1c45a85b0bdb7bc59dfb8a85d7d5ba619bddb97d5afa60aa4631220329f042053a989962067463ce8e18915d3e3d214f7843e36324bd56cc8a38e17bb5f16ca0cef1591110664574f8ebce5f439b2bd471f72a1b5f517f6a3cccc03d24e21ee6b8873843dc4321ee6190f0c63dc4c03dacd1e01ed28ecc7c70dc7dd643b59466a80c05a89b663d59dc7f7a7572a218a84e89d2695aeed596673c5cfcf7c6c0bdda1a30b30161a3616633e16f3ba3ec9a817a7d9dbf6db4c809379d596dff6cf1ba53b589b36e6571d4ffa45182807177adba871ffe087db8ff037f1b29989dd0a417c29dca1336339d793b51a74841b193a426a6cc5371939a9d7053de6b3ef3a9cd1c3c832766d5cc54ce32c032cb40c73de753cd7b96d382a634c545ab36b31429c11367296b565ab27d5291954fb14ea71bbc2d47edb70555fc3bf475326fc8c66beea7489b6bf2b628ad3a39a1698edae9ffe5a8dfecd6bf34cd51bff1ebecb5d53b4dcbb7eddfad46570cf5515a75da50f60ee935dbe46d39698a59f477e8fbec16674531f06d2b8a819f93db5040477efe273be1f4c9bbbb6dab4ea686c2e624562bed9c9db0511a0a9356ca5258adaab828c8027f4639d5a148ac56caa90eab53ceece45433d3949b1f373af2cee78c86337b28248da5189cc6520b4e638904a7b124f562c96ca02ff80c9df219cafa0cade2337489cfd0209fa118f80c25dd3d87cbc60081936348c0c93114e0e418609c1c630527c79872720ca0936378e0e41847eecec36d76ccb878e1332e1df01997247cc6a5069f71e9e2332ebdf0cea8c941e306170770928b169ce4628b935cb04e7281c5492e969ce462c8492e3270920b1c4e72e162e5981141f11991139f1121f11951cd674438dc5d56c379cc3cc0f199073b7ce601cb6756b4f099950df8cc0a558dd80c1a319f05d9c0674192f82c888fcf82fcf059101a3e0be2f2d9cd183ebb4181cf6e46e0b31b277c7633c667370ef0d98d103ebb01e3b39b197c766382cf6e9e7c7683faec26049fdd308920cc7c28e572553a4df3d166b7eeb179831e4dfecbe70ef0b7d16ca5a66a9445576050f717871954d85cf190b5721bdcc3510c6fe859dcaa32b628e2331e400f69827868e489cf766ce1af13224561bab832dc7f2a1aeef8bfe629c0dd579fe940da19e21e66e1eeadcf72f0b093673946f7edb4e25fcf54b3eed5764dd2f4432f857f35331c533c331ce3942e37a4fca409fa0cd633bb81e3eeb4bcbe0ed678e0331b4af80ecb6736747077d267366070bfe9ce9fd990f2bfed5f4e730666367ab2ae89590d236635aeb8eff629efa12425415d8e8abbe778f1190d1df66acb3d139b978c2c331a2e0f69b3168038f76e2190d90c46650a13f087e2b4480a2dd9ccb41e65a6dda6c0b56592126c89782a7aa6fa8895329272aad9dcb9c98a73aad32216984db35433304a51545602fed6a335efb67ae6a60cf503f533ade0cedb4d9f2d931472368306f7d38819447cfdfd6eb7f5f79bd16c99d1f87e6be0dfd8bc5f77eea52426a4eb72779ecf6459f85e6dc0df467d323e8b18e9e1c138c847772403ed72deed76396807f8dbe8ce18eeeeba62784e75689ae29d8a94792bee15adab915314a326f7abc50b4440b708a8df1766b10eec981bfd991bfdcd6054bb6766f6a74d9baeedb1288f87e7b6812cdb7b16a7c04f72e635f378e8ca662874ab7997cfc73318103318101b479978b7b367341f771f9f2525eeaf2b3357162c70bf2bb82798b9a6662d23ee17eed70b1bf7cc5581fb4d8117c14a67ab31b428335b4d1911325bfdf8d87a767666a315ee6ea26cfef55874c5eeb7a7b7f1d1e5620b77bf7e51b0badf135c2dd8326fac22b9df2c2e0258ada5f8927b94999800f77d4de07eb170bf57b85f2bdc6f15ee970af73b85fb35e37e4be07e49e07e47e07e45e07e43e07ea570bf20b850b8df0fb85f0fb8df27dc6f07dcaf13ee9703eeb78cfbdd80fb6dc2fd6ac0fd66c0fd62c0fd32e17ec9b8df31eef702eed702eeb70277cfc272cd4e265e9b984cb56c41ee52c0fd4ec0fd4ac0fd46c0fd52b95f08b8df25dcef03dcaf12eed701eeb701ee9701ee3709f78b84fb5d80fb3de22ae016e17e8970bf43b85f21dc6f10ee1708f7fb83fb15e37e7d70bf61dc6f0fee9707f7bbc3cde1dee07ec12020e76eda60436fe64a786f6377a8993f591586dbf2daa628de61169faf772a921393aa898aa2b25254569272e6f32ca3b2526882099648f166513343fd99f103f10f174c338bd133ff7e682ef75b3535fb362c5238e976cb81907527dae46d1b43795b8692ca2ddbfe3eeb723b97dbb9b7ed9c33eb0459546d7f3f6de26ce0ebb05a69d54825ab95369a1a4565275a37eda227b613cec0dfbd9587753a2cc600dc1da8817bb5adbfdf115bcf4ee6a978dd5151bc9353a735ff76744ee699cf75cd6218c1281526314226281b88d7a6dec647454b5376077baf598d8a12cc4f80dbc9d429d199d98da1e41fda348555f36fe7532b611637d939d5a13cb585b2539c79786907f313562a2bed3054e0fe5384a969af365c18a32bedb2051970b5dde5b58716d0d002562aebfcebfcadd5dd6e9b55b3aa813f5dc453d13d65af369ead6a74af1a8c1fc0f8e27f3b55d4e975fe7663d70ca56a130c11c0e8c03178167f545657fdcb7be326358ba0572753ce9c4f1535779682744a214a3a736fe394a9caca626096a24e39d9cc6e0d85dd6d933347e9533562010b9490f6ce1654a148e124db0f65775ef5ee54f3d3b29ad367f1b3e8da6a28bc7bfcbb346fdde46f5945d3ff6996d5446f437f6dd9cc0be2df6e7510e96dab66cd0cf54cbac93f7e60fb6a06ea9bd346d7b5d5c167f1ebfdfa5c33548ed2477f669c5eb308b8bcb666de2a9afbace657b1a9a3d72cc21309a629ce2b916f75f077484f8b22c5e883572a8f9a5e8a5153692465833722fa26a629496f5333500373767acd2abded54793a98a9fd9b515314a5b7055355e772bbcd05577ca29ba7ea147f2ef75b67ef763a9f7865334af4b65c4eef14b7da83cfe572afe51fb3b8c9ebbcceedf63a4a7e688f098893987ec9e755b34cff3fc2bf5673b99cd163f03f2c95a150d6c4f97cfd3b6dfbbc59546d9f8b2fb808e3eeb62143fe76a2389dbdcbeb07559ca475d3897313269e8ab2688aeaf0a966a094918a2405ca1648b8afb69d9f29b56676a3600cffdb3eb314ea04054ae07ca2600477dfab4d770206f89a7fadd99e797d157d13e56928526cd3edb66fb75b030d57d5a2c9f76ae32902654af1dc19dcdd750fc0a41405a9a948690a07382b65925413153303999cccdc2bda4953ac772a8a45cf84005c918515445cf1451c5d620d11932cdcd31417ad7a5dab08c3736c0bcc4da9a206cfaad6a68e932a3ca8a2c7dd79e86f9455755614af2d96ca38bcb6f8674ab9eb9d8ab4b44bbcb6252b6584d7b6d467cbe4ee3fd34a91bbbb794df7ab73bff8e6dcfd4cf5d146954a6085bb7b09a0f050872ed15d54318013b8fb950246050751b8a0c40c77173d004af9c10336c63038f8b054c3016382dcfd6a200010a27c49a10625dcfdaa80022039eb4ac90fee1e4231d2e00a1586a8d00277176fb850260531c8a08413eece3a0001c2b84fdcca60e2eea1152c7821cb18315411e4eee30cb996017a18f095c0ddef13ba9f0a5c61e16904ee49b81b8238850458b85f2cee7eef1577c77bb5fd4c291e9ecd9ad9a7d7a59f29b56660b0b7d78cae2bd6dd6e3f538a044ddcbdc9ffd071bf41777337e837c965e237670454dc7b6c423eb69e1d56eb9587ea5eab48493bc996cbbd66598cfb3f55144b6e33e540f8bca7bc4d07cdb178d54536a60fae192a97575d64cb6f33b2a1c0324d71918d6d812a9acbd9896eca7dce3bbdb3e59dcee3df6914582ee5bcc3691687cb79a777aa9a5356aa4581a52df79a6cb9dc4e7fde40ef54a49d8141b6c849285cc0aa185d77d1c9a41a65a6131361a214c54e98a0d0e2eeceaa190a8a2477b7b56afedcaa73afa96927155951d1cfbd2a6fc3cf4a6515ff3e5fefb47c5b2e77aedafc9d0ba679c529caaaed03f12f88d9fd3bf79a5e0aff4cf487d5e099eaad966f63511c95db35d55af74116ddc159ddd87c154d9255fd2cfedfbf75f7b63f52e4c81120ad7e945e79bff3afadf2b65cee734e517238af2a56738f7d9d14b74f3a8a457a9b7e76bf0e4f457fdacc2afa3baa56794ea43503b30ee96df8dfb6d176cd5051aa9c98e8675a393151931359dcdf7c9d3001e21691c423277a241559d9594a2ab2f23c155df2b08a8734bb0566fc4986a8d8144de0eea993160080bb73004b3065d59ca2fb45d07fdb400949494a5945e370b655ef3eea59dc6353b4d5e55ece7c5cf4b635ef56bd6b3a338f879b6c6b3e57fd3988e6dfaa776b06a6298bb512fe8de61ebb733976ab28e6e1df59dda16b865adbfdd909ff5aadc37fbebae29c6a0d7c270da5a2bac7e697629b70ffe975a78126fc83d949fd6df487b240157d56ab28b0c46b5bdaf0dae2efb168d4aaf7ce66561f69a97cdbce288b6375ba37bab39a79c020dbea14bd0daf6dc9ea74b3688acfbca2fb5120fe5c6e3b99b95764d3ec07570cb57736276f63b5faabfe1dfa3babf851e06f236d1458fec6fd069bc521fd0e67282c0ef743f7c6e96f1cc5e24e74ef34c5df33d9ec6467738773ca6abd4a494ac2adbf9f6dbf94a424a634c545b79b894e41816595283614887fcd4eab8ec2ed76dab666a78dd9dd8ee977803f536ac7023deeae5fab7f020d54ec607e62438198e933f828bca229cf66a2a9ae05fe994f23286511939a596caada94f29f13dab16dee4f34ebce9cb7db300af567de1a28953f989d7a2c1a9577304db16633bbc19ff9979b14996a9009f8db481ba9c9cfb402fc9956ce968969d55bef5424e00fb33bffd0a6d76152b26a27698a8b525c74bbc1703b9f38359f27dac3a1408c03eedb6d97e67dbb659ca906f7aad7f5974fe06febb3dd79e78d5bb589dbb82156cd5046b7db101058eaa6dbede6377ebcb6a512bcb698015930608cbb3739917e0f6100149a4d06f41870c4805a3934a4c4cc5492200166f1f9423b3bb64d822a09306ee6f5374862cafdcc5039092bee49e0f88ac499bc43331214e0fd0fca88cd1be82ced1f5e311089251b96dc0f2ae3348b84101c9b7927faf43b7fad8a5b809714085c0013db02dc461e31e6779a8ef8e26e638fe8f933ab2866556d6e7c84ab34420b77efc0492364b0a9edfa9a45a336aad3a9ee813f6dfec63d2398f0d0ddd63d2336aa95144004b05440ce6d6abb7e93027ab45202ca6c16ede915f8c174290102603f9fda4c8090fb4e80cb318b4f361751c6ddfd54731139382eb3ad0e77e6dd0ea518a779b873e808af184844048870b2e5742f2af7419d4ecebb57d11f1065733ab99c77b8dce7bc2b73dee1528cb26bcebb9c773876b7be13e71488b243434a72768294773855a3bb4d4d29fc1b628cdbb0e4706cde438061551497a1704e87003a9bf7104c36c60d2184199c1048d8b0e470e78951dca96addc69d284e8825ed7b2170dcdd2684e735474565a520ccb0651049b8bb07f145eb3e6806910444186e63f3de6fdbc1ec849e1f3cf10341e540c800448fcd412a400041fe60c66dbd1f88c869fa83d4899e2a363f2aef5eeb0ce587a3ddf4c3fb9ad5b48552915e1573464c122ec64bc6fd4c29314b62481fcc1061c392f32178e6dd923e90ee6efb1c268ca4222b61a2f03049ec364ccef661706c3a743f9672f46a19c65d7f5291951a277bc8c26d3d9471774cd443183fd5bcc91e729a5dea81c86d2726ea6ddce3218cdac4831577dba9e28df2407eee35ed4065fb1dbc7894569dd476079e55efd4bcd19d7e1d5860c392c36513855af39eca3b1dacf8d6a1c735bb7570b9e750c69bfc975fb4c389be8aa46ab307c8e7280726265e35ee27cd8104b669aa571c4ab0f37a9e1807fc6d1487c311387471c772e5757068e2b616071d1c6458aefc893e08576c4945565835ff74d2edd614a5d51ba8c87bb79afde97597f3babb41067777f286255bceaa5128dccbba8dfe6e78b7e9c09871304380c96d9d3edbea57792aaad3c4e8a35addd6bda807f3b69c53b30828e3b61c04806155f48780292b41ddb3799708e8b1210c6c4314b6d38629f4ccab49da20e4ee9ac5f9d30677b7e575574318d7e9f2f9582797fba0eecf14b3bac98bedcc51b8c79235e4b03ba3385f830d1a3040831430c8a22d6fc56f3b556de6fdbf7154fe691a8e7a3cb4a7a34283cb53bc59f444db2734fd19a83cc5281ba332a8842f0cf6c11e0699526866602000800063120030482c180f874332b1649794d23c14800479b8667e4e1bc9c324c9610a21630821040000100000801999190500b3a7fc47ad73caaf5c9c190513f30c0e397946f1a71ef93bf0e3e680b82f1d64aa1dbfed325cbff1af2caba14371e6a80977f16f7a0e00d27984d8d2d0e5a1791d98afc38c15abd50fbecfc47e8ceeed87ee497f307f55797cb3afab76402c5c6a343b195ce3011ed949ccfd208b64f35617b10fc4763ced1068f00022944757630abf9fe9f012d63188436e1e9b126439ea20f07aa5ca98d2cfa6dd1a77d297b1e9a8f33f456f3fbea2d121f519bd1b980425407e8dc5ec1bf65505260989c0c3f6309967eadc5a9f1b56c1b7cf46c7aebac00fc50b3a6d5ed5db0746fe8acd167ab3e9b7d446fd34c198e8b7e71cfa157899e718bd44b0d1394c9681df26cd164eaac029f587a09ed540debb37d58233f72bfbc7f66202332c801d63c463d7ba9df259a47d8f0bfe22004910f135d8b3604544a0d309211159db86eb1b74061de89e987fca4fd4c64cbd15729d452f4f6fcf5b4d7f4b6f3fd255829ed27b697b33d64cef3c12b6a7a71b948a041dd5365d66be67f7f07ffe60ee1d4e9d2ad8a7811db63310d27ac05f844e15052536c4453f28ba6c84d3319a71fe564db5ef04882a16ef4ec8de943372b647d64c8874a01b9b30be87d1898d5d733de84dd5510ddf46ad396151fc11164d2a751a44526f0d6ae4a9ab8fc425d67004638196cfcd4582bb343995cc3b9dc203a81a6c9696da1a8ae0133cc0e1a43f785cf851a0bc5eb931e29c10949fd98714f18b86b6a9031cf0200995f9b0b72bd83e7b1987bf95060edd7001c177707c222b85d7378c3ab9fb767e57020d2e3bc8c44240c90450164e07e733808550bae1ff6c374a7c7383fa621647178446ed62c6e2db505f14ceeeb941bbef98f1fcbc0942a88997e069c49b44dabad5aa7cdaaa94efe3f859e419d5398afe4cc2344e9312f692b6e2f87792cce261d4625f2271b822f2cfabe94596b909f7029114a835c5660f4893abdc69a87f4f5b84536684f7a97e399cacbe039226aec19c4ef6649d044fa6cfb0460aa059cf29a52bf563df25cf5dc749d85136264bfe3727a1987f470e730451515802b9fd568e9ad65aa1e06d1f091aa8438545370f4f3b71180069567299092d830f1635406296d43c70520a8b49153d362860fe22eb45d715a89aac474163f56d8161ed9d226a72c4d04584340b69ae10c1fb63805570023a5642084f99361d50b6967dbde60a934b9f460451974fbd66b673ec789214386ecbdffd72c3da208a0e94e8e76d80c2f66cdac444ec23370b425b923b618863f9d5cb462dd3a6b28826ad6df04860e598628b1d1d957dd7389f4212e5b730d5cd48ed35d473c695cbf7848e1c7deb8221c8e48eaa710d8f52ee539f0723ffae164f2b988a2972682b570d04ee87deeff04b2e9904c36822ffd1087fb0963dd294a03f50a5d59c72abe3a9aa9998555992a9ae29b79f99739777e5ee0fb6f36374af217e924d7bc7d88f34a3e25ac8589366fe9043bf453fd397d07441e4b5f44a4cfed0259446a7f90e336d0f0c117578f5db78e479fcf7a119debfe6b670fdecfc8de80f05def8fdcfa8f7e70233279f2a965fbe47f0db33c5ebdb13e6496f7af95e48ac823f650324311dfc53a7c8ebd6f3d083d9f67459ade9936f6dffe75e1e316d386fc1f7655fdfd3cb0fd8df5bdb41a03b6574c6375bef8db4e72512c43f1cb048346191f1388a86f78aacdf8904ec0857c0d7d171c58ef37cb1df8b50703ea89e8faec4eea8818a4e8c38874710035c459fa0f9fddd0a4a6372900991a0869d801a2f49095727557289006c280b1335f362f1cffc906a96ad1afff6598b6c399b8e757f13148df5b3db35a6b969ed09b49bda386e5ffa15ec6c7a1165add8f68ea518c227194ab913787e540b25823ddf89956af5dd1db5ea83123751e80d7d8861248184319758af82eb0718fba6a838322229488d2ea319596c214e79d5a572891df49882e6ed4c80a5a452a4a82082e56aff8a41964c26531b2b84e368c712a40733e467351b6718f9a278c13aa1ae195cb71e1e08d4c88e10ec4e4e96ffb9f01abd4cfe0523b90bf46ea5ec27ee68eca7adc41813db529681220acdc21338951c0bcf484f9cd26e744212b14fc7f77b95edfe1f9382d36fc84fe157962f0313f7926405dbf50be481203cfe878b123f60ee0a4edfbb2507099d09355947bc7f48aff6e3f407a9ceff0fbfd861431246fe54e49eafe12e4154019fc07d2c1f9a328b62f852c15ab8dee0e73f173def30f3351000d4b0e8af003e718af6280cbc34e0d064c20b24b25f14c775353311e093f39c7e32e555ef289bc4fe1bf332b598c88663200edb9043984316b6f6e00beaa74c8d5040aa77c6bde47c93e267fc42e0075ba72685c4e310dcb4e86c5085feafd247879f8c2980607d48e9f445af90bfb1f5016c8cb4773a36005343665216f0a6fb19c9e0dca6b58a6cca5f7cbe1285739bc5dc1b8d8455ed4c1f72bb43313298c1dc7fbd44ee2a3e7f4b05a57a8ef26d4d5773b93f7302cc27fc839d82fa622a7e2b58c469b749adea8958cc877fa72336c1c203b6968eb2dab384f3963a286dee4299a068e3b2d0e53641fcd4c051f789a8661991b70ce49f91af353591b657e8aaf55403bac4ab0b566b8c5a4b18ce2e9ccb47072af566ae47ce0101babbba05a1d8e0c3f71ae8eed943201f0b368fedf8f2417722b501a08ebbfae77503e8046970658e6062b19bbafd526bfdaf941d00db65b07a18307b60ba8559b8fd3c3a5be63eb0d5a500d66705b39e604f31f89af61c7c6264d5fb612d80fba1da07ebed0fd45a83bba9615afae49a925bcce31b7d65c707c7cc437e56b918da909ba29cc1da5aaa2069027551363c970dfec1904d3180bb797c06cecac88a109ac07370315d39a2cc9d999f10cf748121bea30865ff055852a13a4779ef893c6df5888c1662a3106b58a921dfca8f69dfe4446cf0ec4b46e026615534c66b0c7d079578a74db2c43300518a2005b10ae517f382d31da99b20e83ce282fb925e00c200acc0f8d34131a744c48617afbf0b0bd5dd25f0a6b63e90ef922b49fbe2fe42201c987dac5410f28726c318fed7f179d5fa3389371589797567772fc3e977eead1d23fe0e2beaf2e1f36c0c916e14ef59df253e163697b91b2e0c594c75a46ff33deabf3301788e22e5ac73e1ddd7f5714a82195c0af1b7b7442de75050dd3361799fffa07fb4a9fdd982b6b25d411fbfe924b2efdb9f17b672304fbbf7bfa0313b3b3def1a8b7f364d7406f7fb9808e76a1e95c536fc0fef01fbc537ef4557a8eb4cb0fec496d6df77ae530a7b81dba37cfdbf0d6b2749c2ebf65eb3cf8ecc74af6d564e3a47cdb546ef3b4b912b87c5c97536c54c82d27aecf32c83753d5aa8bd94bd9e6dbc230c775abf34339deb4acac3bb40e2ef79c20e01fbb5e559aa6d6a5f424110fc7ceccbf791cf78713e67d06b9f965e81fdcb8f14e41e66a8d57271a7eb08407d5d00e8e2ddc0f5dcb734a51c6de8dce8c00bb0357ecbf10ac8d3475bebe5489ea3f060c6b70d7cd1e01e28973b80ee6fc82c90d191b1e58d59a3ff8698048f46382d207522efd8c25a868459ec9fa9f79d77316a842206a7398e071974a4e393ffbc04d4874476a089ad879eeab5efb75329c68b971d14ff500e7da328a7a69085569d5fd5cc07b153816d82a0620746b7bf042c4ee8b38c4baf1ae083a8bb63d66a9bca00aa7ae07889718aaccc22d9413d7d57c5edf51320d482e3f881df11bf13737fea9b4e7e5695e4871eed0bb6537168bd96cc86830d61f796333141ca4b6a339bbe0ee2845cfb865cc8e5f01e12206627c27235dffb807af2ddc5d4132c09ecceaf5a92a45b5dff58d7887b5ca9022b93b734cda641456143c117a5297a10a76c1682e6183a3a522eb07a56c05ed5d5c39a43c2677a627de6c5da5fcd3b5fa326bf7257ce92591a597d24f0d88ab88dfe2b84d7a0be0e51ca0f2d382f22a48489e6099c36d9986252edb4394e43314a196fc4cb04d15d0b2050618a29997f25e51526c9f027962632a290ce9d11455b42890dc4261149810929c5dbd6b85d787eeca2a1acbd0cdfc465868295b5754fdd124ae7ee6d68c8476084be2919835d4bc0b5a4675ad802f560f606aca724990f5b67b7fb0804761d93fb0d8952a7e1ba9592d8be849562a37d933ac6af633d17de65f5d8b948855063a2f57450757528c958f6f23b8a3973b49295e0734de1722838ee4a3c1eacac9cf95249489010c49bdb1ded3fd6f74b02d56dea7372a681fb2f74532fb7d9a7a3f3f645103e21b33373a79ef3d8b3b719517e4fe78bcb0af7eac14dada578357a165e5a719f2f91afd2632cdbcacca9041ba2749c4f5957b6ced92c5ac5d1da363e1871b5ca2fe7dbddab5c0dc8818870625b8b95fadf0a39763762001edc09b0aed80cd251316eeabbac2c01a5645fe5d1f7ea355aaecc12aee4299fa448cd78fc74361b036e6e081c530641575ad935b63de29584bf7246c3e567b2720511c871558a5248f5d81998d95cf2229bd402fff7a51104b201e79fd6116e478d54f995469442952146e3f8a98704f28580e021b301e800babd3fc436c32656a12a0d1a3800093b79a3501fb5327830b80b7825ab7e144718da2306d0fa7b0c97f44fdff8e2ac25fbb5fe4181221c25d623c18e9a4bee2431d0a25b322bcd1f568bdf642b2312e8c22cebc59bf0c1166aea22292d4f8b1c101c847da3d8bcfef57088e105c5e33c8d32e9373c17e41cf2dbc0994df08eeb35e3ca2237c7bc22b2bec3149f46077c9abd9c754aee84e0f19d56a13470b1a890930b9c7d2c3fdea68aae37797533e43007e6c30cbb2de62140df29e9e92c48568e63ed9b2976b2f1ea65bda6d0e06a5ebcd08881fdbc81efdea375e44b9e24d549d4f986ae308130c272c35ae5cbe8c2383c40d2bbde1916a1683fc7434af0c82b54fcceafdc3d34f9a800d7213a11f0ca00aba8965636fb04efa8b3a745039ba7d91fb43b8856ed34f27cb6c02cb21d5883a61d68c4367b3361b703834439d15e5b53dc0ccd7be6e4dfe1f08a2fa1055c4f7053ba3a209e855985d931a9f07d2c39e21b74e04ec806529ec1a9c92458740ab4196dfe419a748ed15ae44a917d22b3e70d1867e541aceec07877d77ac021ceb9ef059f32ab16b8da929a2f49a5975a2506c547364861204b3060524b302dcc2dc354290374b505e0660184285a4254f649bb6148abc334bc2898fc4714426d3710c5ab6aaa37d692cd9f39e3fc49984039656a99b9f0d949d73cc3f78a698f5ce82dd56f2d653843b06298c8ba822bca0f1d11bf47ac060d09d145b1427ff0fd47387e1a19d303105edf1e3d5f391fb31ab96e0f9ba21db7085717e503b363035c7e95f9141124e23ff2819ef186476c9fc386335d54c4af07db3b26dc047d4c1fbfd14e87c56e07ac35c70711d4cfbf4eebc7c42cc80ff5119de2f33e10e4fb0503773355fa69ab2c63d79d30f4cbbd1ca5c1b96605ade28a8c2f60b9705cf341c36206de63b1c146324ce8bc91850d3c32fbc27555189814ee6b82744215a154759dfbd44727060d47d928e93a9cd046321acff6288e0e2720ce01428da23c0e60c0e184b076d15f238dde4783011ed3ddc764ff77804e9549f73fac910f6bbeca69e9e7f00cb2362067d759fc658f9b9e695c7993f825c88af54c30bc72fb34eabdafb9ee41e30b00ec32d4ffbd9fd8de819cf950de1affc25d8ea68453b7cc1fa6e4d49a4aad2915d3bf3c021638f749883e093288d5d3b1352fc7e0cbada7ff249cd89c762da19720bafe74a50b92feb094a0e9098f19ebe1d9688461fb7be6624a6a44478f911177b1073c47323f856695f789dc8b9ba12988f6516a509d377199dcac8d2b647ec590aca31f14e648332a249a4eb4e44a7b6ca8837cd2cd264bfd28c17f2365545cc94d6fd8fc42c0682ac90746c9336b0026bf991b11a0b2c2cf757fe5a10876ca19d83a7f27ed82faf89992c3266d64ef79b0a0dd9d29685235ffcc9b2d7b1e5778f807817b351ba5535c68db606daafea8a319e2775d73f6af13d11eb17a369f24f8e3787cad1da989a7896dff2b85739932099091ea8c0c1d664bc6a9e0f875508c37e3d6fc2a901fa67fe7d2b7ec7980ea48143e01bffd49f1d4282b713406d3a9555a27b75546c890fd9bbae598aec7a3203bf8cca3144631a8ad85450dd6b47cfd07abfa9dc5787adf2766c66ee008a2f51dceba7197a11f730ec74fa30f4b86f4ec2f61b3545124b10f943661ee47e85080db80dad44736051a70acf034aafa1d87d983a5c18369b4d121781e171729911c2235cc3015d699669a6f61b6168874f919aae9e69719657a9330f35a2bde8b11da74413d3afafda6d4cc595ba604a958b04c82a25a1c34608da61f5b06b004c29a50eedee59df1108820d7a709310f4d1a8d021cbe5188726292695945916aaea9203d709fdcbc5f6741663820357ba5e46b6af56cd6aac5f7ac1d9bf760126ca25457ca0a210cb705495f54e3d405d238b73d79a8c143840f872148ef5c29202b0027b217134b1ae3c9e7457f1011d6ce3439eda008042c8596f340f19e212fbeddc200bcc1892a0158d07fbbc01b22749f12313bd0691505cc2ec75371677bfad78ae099e2d014a35c5d5e35b1e78e0bec1a0b05c7d99cda79374959a96c7ebe64e1d6530eb5b79ecfd4f3ca42b8397c07e0750751437c2e1749ba366061784944790bd29e7ecb7e50119069ca8adb5d6a124b0c46c55c1814a3e3ea29d8c64b5115158a7a6a900e6f55047b1011ea8b3cfa894010654fd45bc0ccd6ed8a7c00bcd141314605c03e538db39ae48bfadf7048aeadb5607253e7a60d221272c61202082a4156720065513ff6ae9ed6f7353de5d44104010d3dbe5bd1715701572d671f1f9f79e0249dc1fd5d0ee1d6887ee80512e169859a04188fbac658cc5eabf8f588b2358b65a4b34f416082824e3d993315ba3364045b4faa0981352d9472824b2b201ce733eb6ea35286e72e20fd0cfe8855a5ea1263014d2e72970b329e3fe053c35bf67884df561ff1b060a37d772cedd6dbf2f8a8e2ef75c8ca9b5c1834117dad75bc5cfca08a05f9af99c33ff5481bc2883364c2c0dcf56cbef24b4fc305797c3089816c84825c862e2f09cd613335a7108b9fc65d4b811dc23199e13ed16baa58460a00b41af24d5d74a8fc36f366337fcb5e932101b2813a2d627cd17295bec7b2a24f6037f9fdf3aaaea376d8ff397d01a02a609833f4f60a0bcc1255e0b6c75d94f602071b717db7f0ce29dff6fc0068f62ad40926b6abcd8fbeae03329fadef63ea3c0c7e0d5c2764efd39e379e7a085c6470d4650af6517a3b2057d7b89a0d9188902ce97276446cbfa7477be2da14895de7cc25f7172fba7688b096de2a9698af3d79fda489f676b8c7a3add6057c7f1a75a69c7e728ad0df2c54df7d8c0355388bc3359b21e0d1398e9d43c88c96ec9665c315df8b017516f5531717877e2a5f22a8994b6d7936237cc5341575b7fc82729599a8ef831edd006269131c90a95e85274a1ea23c06f4a2be28458d5423bcd519626cb530a6e90c856541bcc6df3b95a5dd4187ef929990a2d91416aa88e732b95640bf08ca56c86eee0f5e5ba9002468128896e226e193c136263747854980ee6b8f8b1d164b8e34e075a6cdb5e33a647a317a94857bda442c9dec5f6ee81d6051868289f03336eeb8b0f8fbaedb4f8662916ddf5e08c5d4c9097e97a8ff5f1dc3a109da90f790849c141d36c8ab34f5c1eebcadf116af8c4cfe54a6449712ffff0ad04656288d992be44cea48b2900d4eff2158b35dd01cc551b25694aab787efeb1139780827b0672d9e2f95e9106508a9114d364e89749da1acdc5f9442f0e13b1aafda4125837c2a411ddf981a129154300d902a9608556e1b8a6980d0c38e4a89e2f1fd02becf40124af4314a9d2f1cf272deba00d5d622e892d34ff9b6d7685dd626cb994f6b0df5ffe52823972c4b301a40bb9eee55435d280f498d867cc7e52b52df03626307f53c8ee22e303a7a40e5057ea5fc31e0da594a0393c1910b63032166d4a6a3f5f3420c06f8e88101d471922404fe418c5d11f22241b8051d14fc5a9b21311e4f556a4f48c2d8fec7bfe6100af6ca6394f144b92f95a592ed6f4b40253438733245355d1573095c3ff357b4243bf1b1cf30fb99d12e1f0068d163ccd4282e2577b31595423010affded414714bb51ded7b855ce7a9d7810fef5d9c5d170491e95527560e22ab210a335b6dd1b704e479a929d4a04a81e0b2ca7e916c0ba09324a04b5c12ba148da63d016428a3caa14c877d8cf8c6c6c3104c13ec04850e73fb6b02404ef9cc7cbbda74d761c81044d4710b60dfce904d30bb5279273f3fe07ff16b6cb268db10e3e942ece40e4735b20f39604b92b5a9c7c777d885ca73eade980102d1708fd563fe66aac2826169758054662f1525d7956ad7718af23934e8d635d8000332e454eb76d4d3f131173822001d9d63ab661984e652099df8ae3a77fb03a7a8b6e052e337dba85cde0b3f9b75861a05296ed542b20444997614bcccc6659e8ffee196949dba2219d5b58405cd4056fbd1804f6a064e213fd27867ff8b77c1d771a3d40a5952b9af54ae3131919951b9d864323d624cb997c6e582e9a2455005d092cf54ccc7d84d626a13531a83a488ddd783bdea1a2c585c89d4955090ff54209d8e902dfab22fdfe9e555079be8b6a952262bb89ae1a0a06d6c73031665b87e8560d62d71674b9f88cd2419f898fb08b386b9dcc2e41750ff8f169a95a88d35125ff1cdf177e99a52bf7f911242e0bc080106399870e37511defa5808a9fc656e8898370e479f990c5ba90c54c264008287fdfa0a0de12aa5b878918266d14b6d4edcdc9ac6026148b9e71161950bcfe5266b2d2de8426799ba5d6318b1855e8b72a0f2539fff678de0f58eb7e1cb50280aad645d28b7c097b09a964f2c872b963ecd1cf6b7722001b79368365212ac221481bafbfbc532220d6724c1b613fb0ffd080e180e834882cee08f70093b85618fd7e44d5b718d36ab2339d632401e4476ab34e551f0d3bb074e8b0e35a5eebb93aa2705dff7e29ba81f6c0fabb5224a627074c4f18162e82afa01da71ae826fc10bc4fa68f36587b8950c49d275d0966200b38cf8a4dbd634c6e6a61a504c551242be2e1d8f0ccc7eca98cbc26367bf801c2df9b3c64cb8cbfc32d6533d48307d2a65785b816687d0102582725c8814bd3128b86156074f6bd5061450b9159a9be5b9b82e2b862973b5bd5f45623783e7e643d9eb2bcb9029207d232d862c23cd560846408f703b4493b1d79aef8fd52071e02ecf3ced581f7296e960bbed8ba5a577fce1ae68bddeb08fa70d1868970feb8f0afa53679b79b09ba38e2b67e6c76f822fc93f361e8bb2d1bea2b5046177b65e55884bca4eb73a03f8ddd2ac277979e5ca87eb2ef2a02deed47ee8d3e2b6ed9268d9ab2e5d7dc62a46d403f7c4fccaef76820920b40e418c0c8ccf0548373c933099cf750dd59bb5582a54ec4ed93c8bf54b7022183ed366c3bae36d5103a3996140d5c32ed571a62dedc461fe847908453ef004db99e21e9b8225a58ae6e4d9b13fb542e5ec3f4949f875e5495508f3e4c3d3f1528c95a0546a7bbfa49c457a5133533aeaceeecd389c4f20ee9eb6aefffc89bee033bc8ada6c2224dd7f5bcde67d05036dc07ff6a72ba73806f6d9cbbb7a9859164b5503358c444bf16fdb79cdadf1998e63356649d40c9a9592e3c291bdf905423677117f98427571bccb7fecf0674baadc74541801dca044a16994c4c1b798f686f75b5435d074637c45b7f16377d8da397a9d6268106d53c413e6d7000f1b4c98354e79b60a7a8c0450afa58a3bdba265a0320cc5992f5a0135005ca2d73b96d5a11b4d02c2439c6c12cb7b1ac06017daf690804a919f62e04ed97a00a7e9e5e4b0944be0dcb32f412daa1f27a040a284f69aa80d3658090ba803a3269ff1770e80df4c1bebdc5151668ee83b10b4af7a3e722a2f6b284ef173633c184a235e60d7a9a8d06c5ecd9c22133e2149ceb83e48ad8be3229f52354ed195a7a07120e8dace831e1a5f02a7531421584c7a1c255ac8fe08a83c9c26c8b7e7f73de297c9b7ce005299dcccc4a8088ddf82e50011d3cef51fbc0e7d677e1c6f90653534002a7d4ff8b73bd67fff16fc8ce5cdc5b43b5288c41399f3d271ad6b4b64bf866bb1a625f9148ce5cb0d50d2f414028fe0c5877ab3cade436bc08da62c4df1603e46301532f1acf7ae3a6dd57abefb2ce659647669de1c60d7ce2e1d8d874ec2342d60d2a1213854b6dd58c47e3fe0472540d964a4fef1ee9ea9d79abaa65e13ead8c16ac4a1ac10a2cad316f3ac61d14b41ed6ddb466fde095f9a808bf61731d927e99153c364fec9000c8d2fc24c8fc15d506a3001a22cc9886c002a980c946bbec59c32db53117e2dfff7edc9663db007dfb521710f1036efcbf8751c9c4ad33338d7195a8d152c075269e988f0a34536edb82a4c445393d4c02cec0480be6ec6b1df3705a937d8610ec7bb2bc190a2bcb28df1467d8004ca6215e0fad6b2926b3e2fe43d0bd2662e459cca6602348e6e82e4f251398a175753a4e82b40441cdcd845d53a8eac214de5f2dd20a192716e6aa603efab909e5ceb7750be1f3396b6c9327f30ea637c93a0f662a78b7793234ad5481be500d0b00a96ef481119aa3164dedfef5813b0133da5e577c5a9a752b7529103678d75ff0a3d66c66b820d4c9cc104a64e5019f64ad41d13b3994f85dd9a8e9d76a27fd8b1754c9d9228f672bb1a69735f5408f6f57662a9c7049a66a04114653a1770ad4ad13346dde17863a7e77b84f70dedc29009399e3c217f7d675b654f47596b96075aa0cadd37abd90ea00dbac6a6a5bab70b72445e1e069fada9386db01ee354a5b06ab52de1ca8615d5a2d4d7c2a1751877ed31901cfb26c8747b2c31c4a48626ea7cc1dda37a6744f5797235aaa1340588fe9b9220aef705b465c1970633cb6839f0b90b45b118e0c13deb4e893fbfe6bb07e6f8acd0de5afd6753f8146e2e2083cebd24139757639bf7dcbd461a24a54d924d2ca01664ba86fbf00f7e1a37f730b6e5c97dde91ba81ffd1e7ef07ce8bb8f7f62994af00f106404ce5a2c2ead4edf8d344ba4215d534e4bf8cf39682c0d4ab7c02b2ee0ae0aa4855015ad7b6a99823ac85dacedef09433a4528204daf4b2d7c47b78f6b4e0e219b790d628966b07f01ccf22c16dab86e6a395b879efa448f73f25638443b9ceebdf252fe7a96f00470f14e8599ebd0e7f1b56fb574f65a12cc0a959e9df4d43f32fcce9686b7a5d9850804ecce47f7ad3346b7eefdca3d78ac386b51393cd387e33f67cabd2af7024dcd4914b46541ca3604056f454699bce8c6cb6cec9c34a1bec74b3b977001ba7832f11aec764ebc9be5f3ccb591e0d7510a4b741bba43d58034202ab34b49f81274164edf6c33eb5e6974eb53ef6de4e569c9335fa2b3c653b6582e4cc44328bb797056586f0976cc8f48fb99a21965e82697f3e924acd84c94948a51a6b2517e6942a2215004d754b0371fd9e167317e764f5c54a8a42a98686ce132c4aa67168facc11f10e719082fb6c80021a2647ee51cbc50e3fbcbbfcc34721206f7c4bfd5826bb8eececa4d8b57afadb63420d64316fc86adc398c4931cdaca78558dcffffb88169a60d9455a607fb2c9f675dd9060107141635cee157484ae284f6af9f3987c117ad9b77d7762e45bc58bdc32b9d75f3c08e78eb0e5906de5952ab7a51069a65f2133c56d1dcda320ed89535903a6656dce9664b645c7cbfa44ecd09d84135837ff8bce2df3a7ea644345525a28a833ac680b6f13682be43c03db4a2e6faa23e8ee5a224d00b9bfad52f1b19cd69b5fe481e2027163efbf39511e9f59fb526888718aa9ad5a0be2114cc3d7a2a369a93a404729f317b08d98a8f107e482b5836649343cd18a6876a3ad0a7616d0264c7898fd743ebeec1e45b30e3afde8dae667da4648330162b9f16c80081129000eae6861eafe1f32155e8151ecca59700c4638fce25954ca947a9a2ac5af99f08031484eb4e1bc1d929aa15e97120c6ccbc151f6a309fc9f67e566d5af90bab6f4a4662a9220f28936408e6ae2e463a3ee3b823cdd6195c1a8b08236b0b250c4778bc133e46320eefbfee4f76042317160aa37a925bbe554ea273cf281396a1a2e9e3e14ec6fa7933987de0d707bd8f40915861294999aa424496a461eef6e1ed7591192ed604624249923e6638d2bf932f610701fe2cfce97946bd4f0b0617a7c513ceb19476ecb6115552987e0f11b55d9a1b2bd931d02817db301d34ce69b2cc2fb18154b58dcd8abd6d3a5396c4f30e8894aa484cbc5990e742987ebb5546fb596105a4e94afe2868776f4791730e8eb74dd512b0d42c3dabe7b9d18d6822e5b4740ab67cb104d5cb27ecaac0e69982eef39c84e785510aa2db078b30e523f1930f1b1499493061f1dac1bcae0562138854cd396ba339069b7e2aa96f5d95734981958aa617c9242e3201f22f95d41bb3ef5d97667838fc07e0379036b4616af3a14c770e2504a14b38ea28fe6cad7df3d8846696bc59f890a07654e2899b2bb3f4b11a0ce21102c08c2e4a18848a85931f3f2dab56dcea43dff2fa1e454f2d8e7bd88e234fd77acad2cc6846563b67ea0512236ef00997a42f03cab3af738a3073b0923c10d362fd6145453d462db2abfd006b5f6da86f8a2448cafc92dbb5de3237c126fcf9bc49918caebea42b5e96d39e235aa32e31b92902f3e1be41d2bcbd5a807072f013c31341c7c357e91865b5b05024df6b5011ca1c58aa65d4c35a3b48cc79e8c7a9322c8408e0ccc8c7b659a762ff276c39f5b159dda630d8082bdaedc639f77e1548b7aa96886da229cd534d647022a96ffc99ac44c2f6c9aafe89d244ba3a0f4eba538a1c452c4488c4b2d7e29814e231d8bed367d62ebe23905daa6b490ce73cf86268c7b11c999acc1c2234bc24d090319b955400c8a5ea611580d194023d5e419abd74d23cfaf293384219a7e93377566d155bde9015f269da578be7358acc3f061f0ba76d6dd828f13c57d498dfb88e9aafad94b09dde484b9c5a4999f902879c0274f618a248f276c70714cc81c4650034cd351d4f70f64cf1aafce86a18afce6df7adce4a00507a2478aa84610045ac5d7ba5b30db91cd168e649e41b44ee01962a93166f20a3081fd6689319c683450e270efc316a67e1b21ba5677f4958482a50446c5803968808fdbc593a36331c118ccfef4c5f1047c063df0676e10d9b75e7269b1d8ec6481a5a0b157a954a9e033a60e09bad953d1a0c3e61d4d35cf174e8e6e112944b09bca1302abc10ad01fb7b30da9f1793f91e5487b321bd88ae0397c3136e72f713d4c76c358ee1499710c9989e6c812410585a8bf160f14314c8d06fa568d3ad0fccde9f7a6558793d8515e83875c54702437b1f7c07fd4d3432da96ad700c6c9ac41dbf50bf927cf1269553b36c52d58d3ab0fe1b0ba787902a1ec8a7e3d1a22e042eb0cb02d192cc0d2ed2115a96f0b18c719aa94ac92c7cb3d65b98172536d84d8272e81ad53131d1adf564b7757db290bcd252cc4b6fa5a5b8d2b6a2e929d19d3b6f0e5c303cd7abca0339f54d8c1b0b620d07b7acab4cc7f85cb4b0d74a48d89c7bab4a514dab6e0f39de28f54aad6ddf3dfcf95ab8aaf5b05aad72af1630cf9e2a8fab2c4b751915b76c5c1fac35ee960f838558491da582e0e4414e244d2f04088bd8237988ebb66bafc3870b127b548912b88a96587645477dd48af90f86118e655ebc0c73390783c1d3bb8350b8428a25cd3a4d78f393dfb7cb59a5c860e2c1887586ae9b62fc098ddc788b3f7d5e1a72ab4c67cf6411f788e1a5ddd9faf81342323622fffb3c3176951167a47385ec41c3278857db4fcc642c39b076173c9dc6e8143785449b6b4e4641f26b42fd675a5e8599fc96f26837d947f36fabd6cdcac66c4a26ab335a16ddbe5b081ef5b71cd265984dfe8d54ba8adb1db414fbc4c2e2c6d90e3144d438c09932cfb26c80237e1852ee1fa91d2832860239cbe142dcfed7b26621ad19e1e233b9b9472e6a240cd572b01a41485e5484a892a697f2b543cc8db587037bb22aad4788768f1aa223a6f27d7ed6c2a6f9a2ac6cd3185e012d1c313c1b1f2efb9b606aca49bcc5c40176c940520b9afb838146fe72eca3435ed392656afe7a0dc2a7c3e21fa8f9886c97b207c3650cd1075ebc90b4002c0e52b2947bd61e852afb49d9de0359726b30f2d406d9f6d271b18eb4a9daf24552e5cde56c17012ba7dc708b02566886c5673576a7712113e9fc631d75644973b44c53de6e661e3fe581d6625293ecf6088361b4b84f5358f7c9ad4cc35e2cbd81aec96888308c42afcdbd4853b18875e0e87059346c4b89bf0940f5f6bd77d958b5780a28af5065097c6e555c6ab3348316fb1654e590589100a50874ea304817bd0d75ea5836803f683481ea0deb0c3bb7efada5907bf7be0ed9e3cba255d75c3947fe197e30b64b44e9be4657a18dc75ac33faff9c8306ca68363e41bc2dcd08ed21d6adfda4588b939a2918266bf212c5f8d2457da7c3b51c5d56be696c6e9a3ad24ad580951ff5c516b5dd5c0f71d4562b260d1f2f624ffd795d6c884b1c12ed5aaeee737c9e87011c86272fb2c87e26a824e97f593a0c45a69ef45419f555a2f50f9815d94f86fa632b97021edec1c1ca25b7defb31d5fa11cbf4f1fefde44b62b4ccc2b11584c544ba40e7cf90b874fe45e6607b1c01856c07c1e82abadcae93c04ec6c9b75d810233ba9caa19320ed272cfe184cd45ee88b6c5e2e44e79e18246b0a4129f3f1a174003073d089e5134a70feb084bdc7afef22b4b9d3e01ac6a955f166c3dc592b441997bb05f46ba8f3db8c440db9fc8cfb1fc493e96700cfa9a78c108d1e81010d1cd1fd19fa8457f59ae744205fd083bba88b5215442b3ae1f8f93e00a7afa778d516816b65b18c8233a24946f2916eaf97b27ebf2f5666ca1b6883a7b02b0581a69e42adcf31c14233029d0c71ea92c53d09f277e6252e8d672b2271eda063a3f6ada3d19f9b840236662ee00044323408d4d06ed8a74c0f8b2818308a28f093d7c9834eeee75206cdb7845c66f35234f5e1d576603dfc25acc0236c2bfb2378d87fc209a0b9e3520eb4fc4ee452e26a3d3ec75239321fa5479df226d4f27f58b98d3b72ed6856a923e5b56103f5a2445d34c4f94e1b77ccb2a639dc5b4d90aa96df2213c8d8eff5731ad8e8929fe65ec47c947139291e26068c7606ed098974997f5c0c61fc5d3816f7e2473bdd3da8706cc2cfb7646ab9742eabb5223ced6afbd337a276dcced835db6ec1d799ff08a861545d06ab642dd360a83df25e1276690bb3f4ed3ff83625dc3c5ae5989bf83817a57f2ecda055d8255d11be5cd8eedb37ba90f32172c58bd6d5859a3c7b4d4383164f2e87b962225d7b97045e013a08100378ad5076415fe7830f9a7b998b17e7c5a5aeded2ef0a05c0e8a4c3abcd9dcbc4ae5b2d7bd021be6f061872d1e57affadd92468efa191274c14f5cbe423a8cb4ea45daf1f9fc5ad9eaf375dbf82d71e82e9563acb103d368b6957e6c50bd1e52729e1e0813e9009a58a590f327819810050401c156761007247e7b44adc622fb28dec415750152dbc782ee9122cd7e088e88d4649b7ed09f139062c1fd8dd823adce66cf2cf569fb19d61929eba402bcabb7fe9491a9244d3bc7d55b8cd672a5521039ab278d391994674d91bd5e71abe508ceb1375acfe4482d420223c8baab1a0f460fbbc627ed71b7b341af0032f32c2368f41d9a125495e54de5f19dca20d8cdd1e0037bf64fa1eebe8ad4770d98b23e3937147d0fdeb949811efe44ba6581bc3784f8a333ad5b81a8f5ad0db2ab1e69bfeb1d97c4970ecdcfd834117f4507eb0c7ecd12029b760b9a1eb68a15d1d91d5bbce8738e76f9cb217445adbd06ae57b9e6f8a9301e848839fd09ab362dd74f05388888348a5a24924bda2128e0fce60453cda5817524b5bbeeb024112aa7153f666fcd26e187a5819eb7d495d7ff38032b12827f5cd2bea62fe97264c5ccf1103ab01ebfd974364f272c61981a13c82750998adc842358c3a769bb3f17d3cfedb1e0c3f0fdda253f6c668b362f944d0cb96d56f58d014ec5e3b10a8ec4b52bf6e11ee2d225bd71cc776d5786dfe4cf2e24a31778c719fa21cf3717af558d3e77961c10f9ac1f07e1ecfd9f5b50b469ce7eeca69ce7041a348670fefd35c51ec7861b49902e70407ee6658569a4224267a715d720f5e29621e91c881eee4e088aa36ebbc525ab46e6e5992407696dd5ac2dd9318f9016022dba3a74f201af9a017fa1a4f3abe774e93f7d967d0263c9fc0a398985b156641d164abb5a1749cbbe8acc61d003fe0d00d654755c6bb00ce665677493f85529ee9ae6d844cd06af09cc435d76e0febfe2ebdd3b205b5edd1af18d11ed0edb1dfb7385876ce0fb5ae0fbc1e18891e736d0cc5a9e26530d388f523d56d7a58af8f511d241148ca60e27c1c9432c54fefbf1a558a4d3b8bceccc4ae298356280bf4ca6292bd16fb9dc096339456f894af0b00e3a474dc5887e2695a46ee390ae961777014512e1ac00b8307d3f8a15a76c09eaeee34835115b9f1e6991385e10e0a290027d7f9cf44c067b9a4b0a6bfc73dcdf3da0b9533e95b7fda465fd6e1f902eaf6827927788321d8be23ea8b45cf764db3809b267e380e45a807a891cbc0ed5be72c6e53a900c47667396846ddbe23c21c17f092546114f9ab7365f915b6f4619d85f3621492a8b584819d0a3b2f1ffeea96f65ce76b377adaf2df0d43bcff7f10b778710dd670b1f7d6d276f41ee3fbffff93198f4815198754b8208f30b6c326ee55c9d89dfecf1b2fcee8579392307432eee5c40dedae75f02548383d6b9b6c18a230bbf01c2add5b89036a42dab5fb781d53cf882f8f28b351a059654ff26cbfaa384d3a1dbc091f16b14ea29c81f66fcfda806d91f52bfb226126835637520eb633610b07cf17deae4218c3f6b3b4013d0b924902b5fe0c7c8c40705598321d9edecaed1e684c74f5e1367800cd3b008193f66e696c9f3cb00d96b6a6b451c103f3cd2aa63e0cf79a7fefaf92dc227e790930791af6d1175c39aec61917fc7c1b049c292251883f81aa0bf8b19ac1f97e4f171b9862627849c34371d69d67f62bc7dc813df1cc35554372938f18884bb473c3f318f64e5ba00c98f43d741fbe0347ddc90622f00d383a6ddf8f9326be250f83023feaf9e290f5a3cefe713e516242f75716229ec1be0ab218861d8952f97b2aab3b2d7b4bf1bfcc93e7c07c47b4eb0095f61656e829dd8cd109e4ac3e93df52300e60ef41214a4815b0c6e60ea6511e97f2912c75ce2e9cc746cea70f22876d3b022a247e90061067317f0338625e6048cae4ec8129e193ae9b48bf211ac52fb83af69bba0db25eb6af02345f33faa464c17a1dae535c274f8bf858f158d4d5f03d1fd61287398ec78d3ceb59389d264c8b943ce86228861c4c28628506c5e1ea6a371e7e32318f7afa4de00c7689c72f0f1e4573276fa5eb4c3f3b0b518a3ec3126a5c4a28b6c9eac1ab81e3cdb6aafded1034e361ebfffff24d617722f518f7fee21339ccd81ccc38d0075e8ae28fb2307893b28a5dc405fc2ef72c48faad01f796e88f67a8c27a29fc179c9f644e2b16f2ebc60428a900f9c56d676c875444c5349f98fd5e4d07a7c08fae17eff4360bf06e8ef1ffd301d817424e786e1db52da77da7dfc6e63b30b84df70d2fa815bf8e2fbbfd9a2d14e968cba073af1b7c780ded9cbc933f8da85845dee01a4f86729b9ed65b8ff86b50ba7dbd2e500560f38670ea22f5a3b22331ae804d9716b3ffa6c181ae11fe58217e2407ddb83a35fb734f904ddb5c5646bdc08cb6433f8fd545e4f69284514250d0bfb51a45bf2de887fdbf6cc0f7e08ea142aea2de3776036adbe54cf2632b762a00ed64f0374b02d79ccb991c080ddebdb171ed8a90f4b1b7ac11310c6fa2246ac8daf34418edce7cae56052f5bf7188102b0ad961407f58a5f0d78aef2768fecd0d23fe55d620f861b4deef00fcf7ad227f98746617bc6167afec325beeeb379b67d7e53f0647766773222e856b09b1a9998e1267c34c8ffec0a6f6751c9a0fca2f8dbe4f20636681fdbbf24cdd0999a6da6410e85f6985b20792b7d3cb1f13ef6bd69a3fd13e643ec49be0ad1058dd7a9dbf737fae231e952dc9783d3b6cd1b20c4d2722d6a7e1f55ac0f560cb9b31b2ff6e0f4635ea741e2b3339b2eb91c3aaa03702882dee3836f5cbcd04a4c6b7b7272e9089a7d6690e9c759f0786f22265f91f7956b84fd1556738545c7582b44e79c998293ec8b33c73b953ce44721e5d395809386fb97c7232d87fa78bc1f9ab4fe07301f9c5b57dc3891fee636e78f5bd0dc073ebef1a3ff5022dcfe7f1da463d85f540f697b027b6fde5b550a5b864c1a31c034bfa1a5fd5d84be940b9e3b42fcfd348587a5587c9c6195ae6afec053042230a7b8dbeb0f62599a8d4657f19123ed93198ef2b7c540f3f15f72fb0f46b0272ca8a6fb0f92a6f76441f0eb04ba05e41c17e81981099f9280ff157f00e94061e18210c9a0c7a2625eb19e59d09e4ba4cf71f1819335267e60c93db0804fcfdd7c01e70a755e1331f7ee2992d517004f81981e41323f494a2026f984c5b040e820d0da03f5b14059cf2774e410c5b033415da19fe3d5cdc5ba872794ff8be7e80eb2b9f90abb9672235e4c4e3f8591b59ddd82e30e9ae0623bdb49f53f710dfe654b52c2045819ddb6f9d2dd8292a65ff437e1bfc813f6c15d8fd1d90ba8590970d340aff29f86c2543ceea84ab48f8066ea24fb96e96781e27c2f840f9c489dd1ddeb67beee762ebb2658017ed22f7ed6acf30f34212472b26b2f34e179b31efb1786143de208008eab5597dbb012db7e6de1cf776e2b9b5046bdca1577de4a0413c9712d077e572d695199e92d03be51ffc4ea06490e0797b8f9f0e00bb6976a38d31bebc17ad11f015450086927a6b9b7d9151e211027e8afb19cf300d2e7ffaea5569c81606f0bee5419ca40782db479a55036916772fe0f1b23a55cedd009accdd0bd25b2bb86dbd1bfca4e5845739ee06d044e536fd8b958d68b0aa76257b09ee03fc7d53e3038e5b74da307f7aeb9991b673ea9f0e0ef8d23522e355f01cabb21f90a5e8295461fe8f2dbbb87db328bea6f6bdc29a72bba6b02c2715bf7f6149cee8c4686045f447aa9779f1fe76b77506e15caa71822076c813b438095dad665ccb395f5793f450ae9fe9c72c76c60fe3db8d00b982827a5db948b1f30e651b296ff438dc549a5be3ce73937eca45983e1eee351a6e4b4d3bdf3fceb652706f52dc1219cdc60f4669aea090456c2a119ac1f5a1bd5b2d2293a3913cad8bf18f9fa027f4c9e8fd299ab902e5b014a93b664fbb14c971706ebbff654d613f515babf503022161dff17ac3cadc81997dc072558846a3cb8d62a56176cd35aff54ee58c60be64ab353eb286633b44916ee221f35dab5daa6554a4f7abac9ba21593d095dd7bec417eee9598f2a51e44c38652f97d7de8eb33f23eccd5eb58991c23f14dd71f338a55e367d9eda285d47f779a31feaf31c9542f786fc179c69eb6ed9d2fe189340a32971beda67b140a6c111e9996cb88ba9a2ff963e8c07b4d0e7d1c9bfff87d45e5b3ff4038b59491646e02666a9699b7cc23c0e39b789b68005ed9cb4cfa1f1807e13be3e0cffee72c906e817c834ba12b8177ee6941f164a6d03fefdf0afa2bde6563fb071e5a8c50b40fe5a451f9ea7f8872bcdc3d07e7a5c1f7076b81af5eca71cf1e15b3ed09bdd2ed40198be1988c46f5ef53bf0c807ac5d8b55b07aca46c6f6f102db45ace747d47f08c867eb70bf8e9b303831e582dd8ae34dd674b7ed2e47825a19ebad031ceb18e1a748ed2e947d8a9db8abf735e935787af8da5f7604fc2e2dc2e1e483df107a5a08abbb0344a02204701fc52c8235d59972b12b085d8ddf5d2430d47ee6d5deff345e8ef7e4aad760dfbd9717875891fd9a85aa1e1571104e07d340cdc3adc58befdc10715c194892dbf4c4142cd2deeb159623dc48f22b3bccebbea4966bcced0558185c1b521836171432822731e3e93f6ce53d1c5063b56261191da61cdc95ed55efbcc287770c99a5351de1a0c17c2c171f63dc4d95c32ec501987dec21453efb2170d40197567ba3517eb67775c7588b1fae0d1700ea323776f036943bfc8d3a4b5ac3e15b7e929db485c99f2be22b1aa0c94615fd1e69edc61fd263b52a0eb0cdf73a79f5bdb92cb9eabb665e504df96c834ab62b37b54fd812b3922bceadd42d94dcc29d62ba871f9edeb56162e326c0df20d597bdc4074f7f45fc23cdc929dbc74f99c41e4035ab8ab1a28f010c6874d2ebd8f5f2a446cbcd49989ac1007b3949079b84ea4e9c20789137eb50d306d636b66131be1f2af6db97323b31561d770931a3b1c00bd4416da8e6a322a87bb7b33fd07139dd47c6d638649ab3681198a14f4436671bf4c78fa46ae052cb226303380250870b6e07c5d86a4484f87877b9a21fa8a21dab6e0cdb2aac5aebe3ad59ad38173fe629fd2ce050d66b72a89f72c9e13d94b419ef539accfca9a7c8232da2cc6f989a51240336016fbae2ca4c0a4cc40ca624b3e34d93494343cdc7ac2612436872610ed61a9011f569ea844b629c10d5a19580c94a73de72fc61745f054e8048807095e77d4733dcc509b5ec37fcee6b216ac24b463253263a5bd192b7356c074f11472147e81d142255a6201cae2c6fbaa85d6c23757f269301ae842811d5a800856e160f46d14a96345dadeb3dd442cad5a30a22a053cf8fd340877cadd380a10dc20d741f12ff3eb6ead413bc5bcfe752e856fe529d41b2fbdd17818ee06d9262d5a7849f6513363ab130ee1845b597373833a49b9f710c919f02674f4f053e75445bb028ec16854c38597870f954f07b6798df14a4c95036e94514b524eb837d487aef9d1f1e1024800db8568f48c02aa7eb23fb5a9f09a70bdd196ccbf0849c3c1eef5fb64e2cbd8d3df7a5f875db84d513f2ad06efb9f88467f6a6e41d831172a9963e89d1745816814189a0ae3428a825de2a9264cff669b9d7385d8edff1b512e974f2bcc1333b8fbcede96a2d74c3667c34b04cad8216bfa582641f122cbf4dfd01329667624a48f59145505fae74cf6afda0770a4c53bd29349f29730baea1c1b7096f2853ab4a7c2c9dde922f815023e562d57d53256401a36e84ba2319bec4bfd7185ebb4987754ac9778e57e5d48eff4ad9aa8852f917a6f00dcd0b82684227035b697ba323cdb41b5e4a806541d2aeb71618e22b7ba97a5a143e8e72522c0ef47acc5c5af9f926e0dda4029af18cc5a8cd7217c2b1208a079b1e035c1348e9144897590c88a5ed0e1d69ac8b166ed3f074a2540c0a27616caaf7b71b8353dff38fa1f26dfb78a625d8a45711f3249bd8b74e9ec2e41d4bd1c179ad0b7e67b1e1a363c26e3681ca74693e0727bbe45613de0c4af94cba1369b1965fc3ef43af2a21578480d29e97f375616126771852bf07d03f9aca06644820113149daf11bffa529e1d2c04fc569a0feca62a3b125f2d9755f8e8267a69044154adc817dc7344ea1a78da6eebd21535a22ed03192b5e148d1b6cc41ace82d6f8b83265cce88113c5dede5a86f21b3a953ee075591559982e40e353ee4f86a13193e64bf64f66eff29eb6ac297ac3bfe5f500ecafeb0a594111f63a7137972efb8895dd285b21fd538c9cf8b33dae448050bf05fbb843a69efd4eda61017ced1289c40f0c5b65e8110e89da4985e515f2bd99df81ac4c1e6d9943b3ea3b6420e12d58ff14e3ddd340fc8b78bc8efbf1666d8d6a42bc7531e926237c12b9447228dc149547fda060886df5e16d5b1ba9495d3e757b835064c627ecf0038d1117efa7413d77d08e6791a47c835290afcb8b8c0f72449e226c1aacaba16785c4e09354995d68d41d0c075cd1dc72f11ca3b16a71e8ea101c6c9fd23c5264ebccbba620c7ec59059cdefe353d4689ba2364fb6ef29418713d5f38db10a4b111bc136a3029335eabb0b15a20866fabbdba7138cc4089f5127bd98d5e69d7f9cd4ea42252dbe8e5b3cf32f9c3dcaccdce675f2d0b64051620384476c3e994468070865eb39edf6149b551191f09c2d51f7401c1d2d31141e63bf562fdc62afab216610348cc07f4d117c5275040641b1fe294a61df7178a04ce02dca4d88857aa9902d8cba864e9d756030636a4ed7511baaf72fa806ed38daffaf4f2459e1bf05835cf436e52cdcf6d2004ada372b1cba368579fb44f6ae6bf024e3ee9ad46217ee8d3d5e0d6b06bf2da714e652a9a16b1561a1ed38f0d689ed98cdd9718ebc22ff9a89c872470e28fa44cb3739f381cc854d7ea96e8316b981f3c103bd006689086c75db95a4d6d02818c75b20fdbf6b798be6a1a4a85f67ebca08932f7db40652ecd3d4f99d125a335d1d6babf842f7bd8941cfe5721c714de54edafeae535a73f04b20962ebe6f2f3c852bcf4710dcf3b4bf83c451d42d4aca634cd6fd401b4fd7bd2f2834bba8e2d0fe5857b94cd7e061c30711710bea97231d4371a9193a84d578519408b9982277ed7aad5469e248e4bb67b1e1a8262d9b185d88e96e2c0f5434dc76d0629b4873e20ea380d9c25805fa3cddfcb33ef886d2d22f1012bc6c473bfc3f3c68a8f3d1baf06fa77d2d6c56c86b6a467fbd37cb2a0c234bfc502db95714ef105a4daf9a7f9a9484690487c59133ad42ad0702ea7fe0121dcfe57528990ec03b638b50cf3b6cc7bb41c5a018a81fc0d79d9433bb8624cfc81fa05457b3a7f518cd8717205159183f3710ddf29be4f40c169b3dcb0e02cf88a7eeaee18c5ecdfe616abc209d31e8267313186d07d732b2cb6ecdfa3ef733b26869dfc6e563bc05f1dda2d08773cdb600c81786a6a74afd06161816950fd0a63855a75b9969d969fdfaa49ed500d0d846eb8572ec5136587cbd0d7c8fbc51f793286c636cfafaaf0d55443036290b39d33f36a3e023dbac2da7e00b01be38c17744eff028f5dd6c3746b5112a567b417b3e483f4abef1edd125097e0973dd0f9ed588ea4f7486d61a6360b05d1583ce875c8d31bcdc0d7489345e055bf4e96d235640f60cdc9c433947b981a044512a4453faef36637bd49ed0219a6abb3c9ba4ada572b4410ef5e324b4a653df6d760a1649f7ef707cf28cd9bdcf2c83f98a39c8e5d3cd979d8661388377cdf18a0713bca251f5d3a743e6c9f916d3515816264eb918dd9396f9cb4ad7485fc8e3b6147b6c4e8d541b1240eccd935db540e4fd13b2eb7e6b0e22086bb6cf62bd19d1b27b93dabdb2b205cdc0adf01d5d942a1bbb1a3af015dad17e27e4a83cb6508350db5a19c657005c4fda5f3c1d2a99654b4964bd48150d94bc787d9331b34186206591f6aab7f2a7caee75299b362cfbf486da526482ec57655730f87a46f3b2ea5a1185d9a7720655507b3203dd331396754065a51935b04fd42cf5bc9084363f25b3976a5786463fc76dcd07f49ae7425cb9b3f39f0c466330f475828c240874d6ec0d7abb35a5665e295f7a014f7f79dfc186d02842431b86436cd2e92fdcb0cdc3b4e88ed6b2ae334500176f77bd49d4bd59e7cdc21fb980972b421731248330ab274cdf1fb88313bb0c3625c1631aabb183442b30c46341791837abdce2ea005bc5519574b071a9a25e3a476abffe829bcf44ca75d9dd08cbb950fcbb81040420c6275c161a3d48d760c2bbf1f52fa16078c89374bd3c50d468d4da846bb908fd0fe790d40a9c5a73314ea666ba4e4b97a9b63eb4db48a1007e42c2579a4a8cdb6051c2c254409a3bdabb5a3dd70a117b2e153b2f79a5dc20259dd3401ece130d58eaa960f9334115b81f4c5696854e66ad1e4936ecd137ccc24f980fe4a0da31d97014eaa3cf1532519b298aeccbfe6ad4aa9d147a2858763e3535b66e4269af752d60a24779e20dbdb39bda0d05af98079dbe9cd1ed2c36027b48a7419550568f6e13b7d40f3047b32e2adf0a4edeb64479a4770da594b468250ffd39f609893a164c084d0530618d52f8b634b471fbd6190a22b89d49a42e40af54819de6802e1d16912edb8b445854c5de5c4057fd095d8d39b2d75efcb6d688680183da0af42d390f8b4e68feef41bc545b3c1ce2b1e920a9d1889143318d2c9c01f7f9268ec6955c9444298ab849843b53990339402dea5af27fea00901d1478089620cd4ba0a3e9a8c739d1bb76b038a688e6e8e5f91cad63316e5bfce919856e9ddf0aa912745afb790ec93ccfd13a4e72a2ceb4af0f05f5c4199417ea5d2f9971668c31c221e308a417e297694f7e7944591005accdaba7939dcf1e5170e75154264a50925d77bc9a6fbd14742f3329eb788fa283653e4afc123b7c57c6e094aa17c027ee32962b7e60a053924e3f0344b517c3619a024ef754d0fd859d2e2c874923232c62d93b0b4ce19bb1527f49be26083b0fd5d16e6473fbb845ac0a944627d940a17b985c690a798a53ff69d5a58bf46addd719ffd0683e22c1c2addd7381b82f68484bdf5d54d4245093810a8ec09f6adea6d485a2436f9b6e6bc9606fcac61963ce5811b0983d5707b8a51749f68f28e0653b7baecd053aa5fd5d2af99c4c47a3167bf937aca845f649848903cef14d822548815dd12d89d6b1fad962d74091d3253b26579729d47d0bf27eada67b8773b75088c0451840f57552165d6e679f1c4ab42d7a970de26079f114fd032e61c0c7e71ba9a4462fd8b05a30d91a92c53b5561152be55a03c22d6a197e9fa188625863c62ae60c1420051595e352abf16b75125d47ce77b0f284674d131b4c9d9bf4837942b18a114ec5c2aa8fb884e39e6bfbc74b9730baed65c9c76329cb0e8fc4fa8ea60560486cb441b4b69e5550d65088e7a8b5b8469c722fded466bfb94eba81beaf9de39d1d7c98d0bad55c57666f928e87ba21418bacb4a8f1b8a26a7d405316ad40ccb0b927abe0a7fa948ae904d10b53326089db05cdc4d3086826ee41c5eecba99f5bfec42e6cc413d312a92bccafe378ec2b9c69ae9a9bffd2b8dea19e29775994cf0374da0dace3b63d4b56a931fab869f71f6c94ac7fa34a921f2c59231117cb33b823cece457a47d68e6f9a84088aa08b9bfea81d6fdf85bd193851801c423665992f3936ed869e8d7267b8aa2e0d0fb12fc08ff1a2f59a4819ee9bf2863fc7a8cc09ed69429911231551d330c975dc78c31bc7bd69b184b4c7f535ca8cddefbce683b9d0b221d2b4eb0a34aed877dc201d0365454f4eb2740576f2b5922f75a4212113e2b72d97e68476570967387e0300857c84d96630a992d48d17fd388072fb5899220d42cce7159f188eb0c26c392165bebbdc53f5532228aaa6056d862d75c31feb8bebb836273240c4a7aeeb38e0566c69a1e014fd2ee2251b2df7e24e66031706dc7ee9d0645336a259118ab489b9784e9c12eb0f0589b9aa0d9d95fe6ba2fde8069ecf89458c47ede3ccaa649495f2297a1b6a2d0cccc1bcd1e4f39bf086e376274d89549230d83bc39e5bf43f56976e359ab9583c9ef08032e92442d70980434b1de87a89a6edab1bdbb09991a4db3ac16664f2f56971ed2de150de69773908fbe3db4b03d9b553b7f995435a2824b32c23dbf4651071322f41c8e0cb6ec0fcf711f2f6280cfabc4ff9684ebcd1eb70ed8a8e634eb01772a2838d6e0b7186ef49d01cf4c366f58ff03a6020d7be22a35b18567790e4a9ace643b5c4cca140cc1c970aa1de1252fc938bd29873b9116bb91a57722e567a3c9c76089487a82a87c073b43cf0d8548ee2caaf93fc22d2127ebde6c94012674551067a2a829696b87f7062801ef7a0473cd0fbde1a9514286f188647309e50010c46410f09018b78e42257ad036946bd475e5d70fde931fe2627004d020f524fa2c504694910afcd3361fb8f47888f91816d79291d0612b24332758f6b3f909f8b976e8bc87f79f358cfb69d1ccf610b5f7f20b5b6dd739b57c9ef72df93750d1cbe2e6c47e7e28ab36a2e80964bd93d84cf8d751a6342d0247ab0f2ae2c462a9f4f778291a0650f88374d6b9e71a68b0215ccfe297fd97df63c97c08a69a7118777e64407578e34e2922d554fea4fd3fc3a06d8c418bc2466c7d0e4cce94398557e6584fd5a14907fb6f577b65ef7359b34bdc95cfb196992b9c4f1efe027baddc3fc4b4bb8b7cc9ad816cf7dcf6c38fe6dde15bae55696f640dbfa68767d5890e7f69feb3e5c1b8f771ee665af7889f775fcbcb9f10071bf146cee742895959d0b716c2c0ed01f336860f1971dc70719452ef01232637eecb26d68f1fe29dccafaee8c95f9591bed3eb3ab1a32580b03ad773154fd3224c8e2757e339e2fa930ce7b8e5eda33b631144595f5afcbbebfea2601542f8e4143169d01069dddbac47277c6bcef2ef90ef4b4ccb08ee4318f8c1f80b525b4d041a1d84e496682f9cf6eb42e81ada4ebb4797c7bc5792138d1baab214fe0b3b774d1f75fcd67949ec32dce3bb1f051274df84a1e1cd810a042fd4e23c27846fbcb7db63f7ab69996c55835eecf153ff1cc8dbf5eec69c444826f550b8e98ceb4c0dcdbcf06f6c4d8139dd7b2c8171f5ff063b3b9f14e49f99d71792466bd1355291f09183e23756600747c79e3ecaa2ac26c0bcea071214f053535fe45e28e49bbf0b49a6bb751474c33d83b268187a719be19f0a65612574fb209727c3a0e92d898a195debd4306a1c425ca9a03271328a6fd9853762a19a953e0db6c7e3fda362c70713024721cca3069545cfc98187645cada4858b248724918097fd761f47ad80e4e6cd75d4ef6b7ef0a0268b5fbbacc52aebb217defeb15bb4f37993fa1491ff0ed63f25e21e0bfa009070c5aa558e6fe9a520993f48c011edb5f3e7fb4110ed086060ae86046d686b52b42f85208d7de355409b59bd91420b7ee070293de16266fa038700b75845833afecf154fc936795e4fff9bff834ddaa86591e8e6488e3a2e9725c134ffd9b20232c4fb4e65f1e03eff146e704beeefc39a464116e4dafaae607d24f2f9998f2441b08ff4e421dc44e580adca4fc808797069d27087cec03b4f57086f4251ba74074d790360d27e750890562f832ae35b3b0257da49f0c33d3fee7ebdc083be22fd3bfd10751648a3460b1ec2a3dad7bc633be548b6702305af91f3f8e6b60beefa0ac2507d65c378a24cdf29f0e7a7970b09d6c9955629d6c6d342872b3420918ecaae624672dba8cc8e9623d50e3b93dd9c1561be2f5edb073a7cd9879ac68da7540d11d005140a87926891c54042d8d1d46c4b96ede0456e9bab6765b91cfd2612a3704cdece37c6875d69a04cb68a7ae09ae39ed801be1fe191bbc19c67604550485a1b6f5821b30cd9027b63dd0a14ef4cc13a7657ec109453e0c18b5c5c91ceff43a858fb62e039fd7d28dc0647955650a77e6769a5dbc773ae8a50a2e4f287381a03e14a15d7b66e9a30667d8d98e860d07b280a9a4868b1f1b604c30dcd35eb343a2e8d9294e37cd71b8ef4fea848338572a1781abc6fdf8c2dd5c262e84ed43c4025a0dd0a701611955278b6b28de60c850451776de937e6556277557ae6687c78df454d527637a8486d7864e7e76e18fc64c04cf3db5d13854fbd40f6bce0a5824a1acf57c349015a20773fe0b290d78bc15ee534ec0fc997823ff4f28e3320ccc0dae7d8fccb593c58d135cda5cbd726971935b81a270aeadd58eca4ee65c5b9b5fd915fe47eaa6e58b4a7e0e634a6d540404a3f4144eba4ec4c17225fe405b1f3bd7438c7d54a17312738247db21f480c72d62270cbd12c1889b5367979b33d345c0c0d32a578fbb7483758ae91ea297974a97ececef4656a52342dd4747e4cf07d399db0469fb83b5c816eda8fc83a906b5011d727fe10a449c2e8d8bee45049bf84162a9e964697c19d61f70aaf0049d16abc05b3ed10d3df041e8b69ec5b559d82535ceb4c6b7d75be956103eb385dc20ec3f13d429d5f9238cb46e7226a66b0e2577d6baec9a3261a2cb2b92ad375db02b5932d6c5c1443ea8eacd4920c9c78de043de7eb40179d16a834be7d7764caf85bfaf1bf48299a8a1916d07325ffb78fc7321084a348d67369b02edb3f095a407bef24435e3872f3102b4620d93f649d9db238510ede9c33a622ada6154048885445c3064f0efb7496bee5b7357bd91fa0656250cda7fd27dce58a59adfb5bb45d0b1574647905474d1a86e22430bd049a7d1cab94a104e5f00c0387c3454c4427f63f6db5ab12ced64de3b6c434dd509e04ec74f02f1fd92c4066f959fe5c9236b47c38ed4331e946d86f286776a87e968aad81d3d4d7af41085cc48623b9a11f7294972a86e753f98abd7d91f52436f819482fe724a2bc3aff96d73662250523c1a98d8341911a3c63f106f138f6026da648852b29b07e80850ed78ab2d7ef6010a753a5ae829b3e5e6a5ab8f5f853b15413d3757076cfdb0d1c2019e3a4b6ca68a7294fb7aaf33cdc6c16dedb610ddb52bcfacb232cbd4d9d3d71364bf56ef567aa3a7ae02e9634ceb8e7843414cdcb0141004e51389ecba3c6de4f869ae70e86bcd5d4ea49f738dcfbac31f1a09ed4e667f66bc796e095006efb191d7e2c56d182e1405d6e1136a7041e2f401e3025be8a0e08722abcb1b902e075b86ce5aff915f440adf64698c6a900e5ceb5a72dd6ab524b4882502acefbe1ed6bb20de9c984055060fc0402f148e065435c4a2a9db382fadfa1a762216ae4b3b733147d8955006bb353167c9fbf37f459978062ce13b03a99600762e128e3b9d6cab79ae6a22c33b9cc2eb789bba90d0e75ed971dbbe0cc364b87b968c5c0a819383302df4477910392dbb8d87f42aa55e00f3232901fb2c5fad973e2d630d1490dd232d3259526ea46b807564701cbb28c3671f07bfd3b0e196415689eff36eab0329a455226c21a8c57d3e645a6379ebe66185c8d196ae8f91804abb24e44988098f50828a8c90e9e5aff90e425ab8ea0d06bd77fa600e4a21be7ab3901ae8ee45bcfd0b33e3ab319dbc893758015d4406ca302f39333a671e1e208358098546b49ac6a0cf8d82a723375aba2220e33b74ed3212ef4a8a722db35d564b880a6b448afb030e3e863981fa234d2bc30ef744a60ba0f37430693f81097d5a4891fe63679e201b671359b68891c85a3a9905d376f29f5d3810a05dda80510cfc5a1e2488792c693c351a4a69e3aa456336892f6c635a5af7a389914b594e5e8d9c60872e0723124d6e8c553c1e30d214d7ade9cf84f38b20058e95b31468fdb65d6479e93b2f23a907b028053fb4178301240971b7a41cdea40fe7ad44b3778596efdd99988d00941cf664daa7eb14b887f7f7ff51e39ea969660a5c85f4b7b29f416a253cd494b48439e910b029788fa5d0dd0aa5986abd58704e89e5cef7759732edc15260aba2e16e488e73f69413c804097f98948e4a850789a06f388becc4d900876c1ebdf88d15fb8d3923d21c96d22ad4030d20bf428b2230072db8448fc2d3277cb241a6a37da8b28bf7b5028567035f1e9b1d1d962e3cd8c6925405d7800ef6c69e3b9dd049c69ed5a5460bc93e69d5cd9c1d5528e56002ace0b5d0cbccd19f2b6b5cd72451a3cca0582e54b89deb1860a46b7d8aebfae079dba5e49077f5bc947801f97df3a210f3f5ba76c8eeb517e76bfc42b1e29e1dcea8e93c01354cff614b17ea265511059e56ac68556ebad2856efe83d0fbff09bdbc8bdd184029292295dfb85abe5507aaa04d7583975693454ae4711e6d1470437539de02191878c9002b0dc46d00d6aa4d7f6e6301ce8ce269d92453a071ce314729018b7449533453738f7c73446e96317554713053eb6a30d910059a580b515611878fe8a4e4d44ca6797aaca8734e057f1548b0e09669015e3d004408348ec08109e4a280b80ab86801b41748c600e319287689807b11b41ca13c24e84d42ada1a823a4cb228e1a26d507291ef873a5d128fb8f1b9dbf1dfbcea1f80efe0f2b41b6da8ed1229d056e8b53b24e3d30385a65ea028047eb4bf0ab1aa1ba303e1d399d254e3c7e8ce5b4f4cbc9cf51f8cb5ed461fd977ab15bfc2f47e98e199e28c58e934f615aab8211f3889eca17aa98de7c8e876eaae739251c968104e554e2d945bfba1d43fe5063c9672535862be3274bce291be4c9da6e6ed6cb832bc62101a38738ac6d7f329b448890267134d9859c812c296033f79350773ef6c9a238ea3cc0ace4fac7ee786bb30d13c512ef7c382ae178738cbba8c0e1028fad7395e04fefa1f461891081c2aa996c2216f1ab320b79d2ed2fc6cf4f6449c2b2144688052ed0334b02b13ab09453216032782519a0589e36481febbfa718d949551bce01f6313e88cf687556fc63295e586588acce25f157c908589d801c2cfce8ec2aa137804fa38e551ab46511290aef917ee9b3d2e2d225a53dd6a9ea235e1c461879698c80047c1b16a446d9d97bab5ca559d7bdcd8d98c1bd2693c4a8b0d904639aad494fa7c3e3750ec640d618ad546f433123948d9cd551eaaa8e5adc1960e2a54313291921283bf8f86325c9bd8f9e5a5c783a04833699aa4e459670c80f156168e21d8f4ecb780ceaf3d640473014b222dc2a5b1ea94fe198daf88cc1761d54327b60d73232144bc7b11258e37b65b7866530d05066e8784509b0913dfce09d3ae138428316625218de54e651aace3f24b741d64d358c70506ffdc8fd4e95a0ae44386ba5b848b7172f84807e86998ab0ff4aed35b436b10a7ac4430f6b44bc324dffb17fdf3fca54c0786596eb0efad69e917054ffb0b501321f944e9d0f73ab6b0d669b9649362d87fe88b91fd36a775817827c2f0df45462622529dce8f8a8991bdc51513a4daf1f4a1ce15b82caea5ffb449d1404a0db825a936bbb6fd802f75e1600e5993d6601bbbc41f3fe67c2291efd32e172b9fe2cbe82b7cdc94b1ada65aac208eb3bd612a636a475a0d42bb1881490429b6f4adfdd9d8b9bdaf327c9f442abdf014b49402a06450e46c848b96486797f6739a4c55fe7d0ed695ff2f08fc0149d9ce511603147800b8d00b71801eeb608b0a3408a34a96da2b485a0dd8a64375e9d2c10d816547c3c643a41850298c46a430ce7f2f671c49c837ecf5a3afb503c9cef3e20fcc2f9cb026c4f12c66cc3a3062c0e23ad62608daa990e4c301f4198f2197d9e0c75379c23ba456e08e2094b1742488fe23b31f1902cc3c74c8441089207e634a95248324730474a05c5c99af80eb624005f4244e50cfb323e124c734e8cf653300075a492c4e4b72f68f65bae0028f72a02911d33c3cfbcc0cf417be925634abb2c39225e6fb849cac33bf792f17cbfde2870fe8350e2efb65c888e1a2451ac1802890b1479703e4d86e7f32312f01da3c0f6f477d2c6c1c7731bb3e375b6fa47e9235a91c89d51c2defb49b5ffc3ca2eeb55419fdf522aed95a3fa1139d1cc3d547cdd6104bde5d6005779843dc45fa7a4b9725441ad629fe511c5007f0e646444bdb8f8a534908b72df6e033030f0bb1bc5438e8a3489047011deabfa2d6ebc32d67efe94d93a5e19056863200c61fb060b77ab51f11dec67386ed3b138fcd85a59a85e5b03856606841587ce84b51f2abeccd3bff2fbc52a31d1805892475d304611b8a767455608ccca4d48018bf3a60a87c00ae93775b09d7679c22a41015a8cd654e582d28aa9f215a7d20a1fa18ad13a20c8ec726342408bacb48a937d0494b82651c9b4266dec958b055624e347ad0115b0c82d8794f7f6c85840eab82639fc71c5c542bfbc160b945cc53d65055cccf5c37f8b8cc2dda10d6234eea2467605c4c51c3d3b26f7dec76c80f886dc176c345ae87008fc3a94b16a7d0b3e696f475b2790c678f897014dfed2857891340e58cef9f8e352a0866ea7c2583ededda2717824bfdc588198e3dfa8c87781cc17e1404f8c58bef42e7ed608fece19c70ebe487dd1a634eb3e6544c0712e979e66c52896b414fa17cb873252d44919a9ff4e0013393f8319ad65f1df97a030aaa7a5507d637e4269c8467e8a5314fd6c7b9f6453281e6d4dce8bdc689a25ce4e60ccc68347e0279eea32e506b2d0f97050b458ded30fa98a6c0db845ce783af05d7f15214e5a6998b2d34bcefad7cc67f6b7442127749c2973b858b1201437cce3da627ebfda22130da4e597b2d6c1bce1acec99049e10050e11481020b3a80da197befe467d5dd7d7c1d9b905dffb28cb8fad81f6fb40d844324d48bfda46ca4c26abd7341d3c918f30c6a31910539e24252525c22110e49664e59a42aec3daef96023e1218141a0f347402644400173849d628d0d96c352648a28a14a08860051d2d1ca894a82163f2061f1cf8c1db90d12393213eb022818b0228f386430249c04d0c18257024f938e01012c274f2c31f55eec0f241960c888882851d100e20c8d08d16d91af3013478e04ce0021c4dd868d2c31882dc8e104efa3e4bb514aafc5f8daeef552b918b423cfd5b12777474927474c2ef7a89628be39e05825df581aad6eb53a5e1ad6a5f989c883890c5b3f554fbf23fdf833d9614c28525654e1f54d248257f5452064642160cd33132c24691bb923cc7faefb9f937be57d280ac61071c4ad2e05022712631c3b3be9dff8ff84854c77c3b748cf8872440439aef89248049a200998de23f3a7a895644be0a52fa41923c21c904524aff45319f12f87d19c5cfd3f10fb4f4bbacc497902b4cf7c2df7a05fd7c1f294cf879cb4845660052dea87093025218296fc8dc10a15054464787cc1831273c2922f2ded0095228284c50235f341281c47454af23b1a5c56bb9f995c5dffc7ac0aef4d5450fccff5c16b962c241111da4942018e2eb2902259131fe86c82419e73887f5fc4c418475082452caee903458c6cf0b8ffe27e54d172ed294a3c1059654d20804d8b4314943404e4c134065d2c025502a8932498e2414922d1138a111cfc892226b10c1a24d1c0f28a410398814417c1ae001b2c68f33fa68c3078e02b6e4810601541c00903ba0d4f143870d72bc11872cc08e1b36b051c71a50d230730615654442802d630411461b30342fd670b1c8163e59448185bc825711071555a4580305264ff826f460628b126024917344124640214287215041ccb046fe30830f281e74d8014a0e49e090a3060c1bb6d0a0c70c3e064c6058f3429516e260812f49a528927cc645c435a1e66a0306f16d61458244c50a663c2847757436fc1d2fb937b80d463f2a282a0092021528a039b3c50c0e993663ce10b3a6c823f219927242984282e2008305d09a2f4b26f89f13bc74a15c72b6283e507a1ae041ed984a30694191d04016282344d1c9d1e8dea24fc81e2fdd35168b1a4721544e29587c32efca1a2b67b4110127842d55d050a10290295040f8f9c0863772a4073b1dd85087142898190ea88844005b3608a28d065a066b16c1c0278a28f2021e87055502b006930abc1e146c0163829c24525074c0414199610600a07490004a1211e48001c1163d1ef098ac71a04a1cbc0119c5139f45d630a0b55920884c6422652ca08c1c821060cb152d2491430f7c547969549b1837800cf1614342035962c1a10a12395738d882c11d0130809c80caf1861150c0204e901a9d0205112c76ae20c016121a40048b9b29939cfa182491d3215fac70f2b832c08a1866e31540c2903880409148df6bd56988c14b030d2eca98b9c41229253064c2087f4829a59430e45d4a29331c3f5cc0610129acb43ef610c3cc732ce3e7bd8e3cee8e9f77e471575aafef6693f2c647ca9b1e89d3470e373c52deec6ce143eed1668f2f2426568b2db1c6ced37c9c3d7290126b257d9c3d2cd0e3908e7fa03c00a9f675f36b66ea25018bdc54c932829447622b04c37093f246e746bbd9debb9182524979d2e066fb1f296f749642a0109d12078f3112078f20898307178983c79652a296baff79a4bc9122a5e42efff1b00b6f71f1f545ca1b2c37d9955104575fe43ce15fe240785091f28a192edbc683c7055776847c827686bae0f1445ed901b34324e6cb012cb95214c667e70b5018a14f8cfff11f0c62c1fb8ec0ee7f4420faf9c020296f42f85ceb83093d579acd7be39851fc375534787243e52647ca9b29204879f38194f2c94d8efc2d6fbce9404a39824152de48c1a4947e1bf325d4d11141212d5aac88e33f1a45d0a512f9111fb907bf51fcbec8bf78a8651441a12cfd8fd152c5fff43f4654bda8aada57ebb58dbf8b080a8d2228e47ffc4fc6d58b968cebcd2f9c392a90c30012d3c191438e9c3820c189e38b385c38714c4c078e49a4c474e07003d3f97094812347623a05684455801c0aa0c11b84bcb105a683f3c62825a69305d3790385e9b8c187c474dc706385e9b861454a4ce7124ca70d35dac801d3694308d361a38d94928d2f301d2f71d8f06103024c670d365724ce1aae35b24889e9a8f1078e1aaed305699891062562f8c7df023f0cd3f19ce7e601296f1c90f2a6012c8d7ef344ca1b06a4bc59409abc1b05a649911bd4e974736362e47463ba915fe0a05124b30ec4301d2339eef84390531123f28712c81b248e195b883a71fd5144125d60511957c84cc43e900b8bf3c42f0495be2f82ab8e852fd00c9689df7b49af9187dce5e2d8cb871042c8953e8ff7108431461b32f2c8448e43860d524a8e711c32b614bd312e91ad3f7a20ce1866641ecd1859e44f1a430229331c3182c4d091452d2fc270401871603a9818612021a56423454e0146221f07030c30d6484c071b7995ff5afa48545038386048f0851c526223c7d2fc6f25722fdb18826066ad56890ff99f34df7fdc8b12c62fb639249313279a7491b3c49a2e3e8a2d6fc444cad325529e2c91f254c9e93bf105d6a84066012c9eebd53b88750b4cc98e3e3054c27e0b24c232917b89abfef19067012b5237023851829240152a2ad193729a44ca93049e3010c683a3f83241ca9324529e22394122e5e991365be0680185d20f7a420bff79accffbd0d00293328b3fb2ffd26327852c6e04a43c39d2081606c0428d364f2020e58911294f8b9c12913858a4c1c182754244cad3216d8a70b040cdd1e338e8a4934ed830c2e600524a4c0b86e95c193d2e8c1264d84021fa902b2175ce616c649052624360c004c1031b1f29bfc88932315826facf438fbbc68042e2571a7fc83f0ff392c24f848961b6339acd2be2151c2b70ac7800fb2eb08bd8a842e25f09897f32e3f7551ff87da08fb8f4bf78cfbf88aa18e22ad0bf40ff12b3a58f14b65a2bf1e5123d150a4cc1889c2205de3f6fc4be11e39f2762e2175d9cf531f1fbd8e7b9b00ee4e12bd1fffc7420b6f33f6ffc380b4bdc94a5cf31d1eb40cee3837dbc0a95f07f9f870d8101330406ccf7e0f7a9604360c00c614360c0f8df4496121331a4ce39168bf780609a50404a898961abd56ab55a384c6c2931510485b2a525bec8fed1377e473f3c7cf1255858a28c94fd7b852366658925a2181981e16741f4923c8ff1f07bd387441f32fce13fc343247a384222caaba5001e3847302119a803e7882d4a18118794639a7f85872f6e0413a2c758fc8b181f7aed605f0249232228e28cccc51afa5284045ca6ffd68dc56f4fbbede6ee1ed7bdeb95d6eb4b19a31b420616cee40617ac243220c40b0adf256e0881c856432756883940061670a0c8184d88f0a206caf4401f8108a1650821296590135a3017ef2aa5ff6d724c108648794a83d28c5ee93f89c5e22d510cbf2706b19298ce5098a1a10d932245ca051227080f82e820882732fc2a2b58301dffb1882a2b2a9c356d3012482061cd1b6bb69098ce9a346b5a386bbc48158b1f7ddee7616b72a41457225f1d619fd77a85a3c7853b3a7fe4231017d301e2c90f993c82f3031e52ca2a19a623e503bdcf337afeb77a9a1f1ec0f941fa7009a6a342038c33a49419483512c7070da4ecdf17c5affa59c843b0b3d240030d34e000cfd2187a2f8e81d8ce04383c4092891e0f7d9c0043a641802978c0c122fe23ff1b70e00331fff1a59319529eca90f24400294f6448791a43e2ec2084b383cf298c1318525621a5fc248e0e71c8bfea236fe9807d61cfe0e4b0458987f65302436e25f2550e1a48799ae224451447b03c1e8e23b038387e20c6b55e61c738267e0f7e2a70fc3c150d2634589fd80a39f693402e04797cbe171661fee33a3a5ceb15f650c1f2b8d1f86a19fda4295f69e9862ca09a3b74549ee5adbacee9092925eae4841a18fae8f2fea989d312529e9448424a94202f8f793ce42e895a44ecdf5f6156964657eb732513971a3849c3c1f0559aad8614c07fa4889491b4f0d1a08494344021f23f2118869b387ea00f51ff493a3a2c3e8245208b88032dfd2f4be14f02bbe8f9f8542b062c22e33d9fefc11e71e97ff19fe5f2c222b0c88f9fc87b3e2038e46af532ad5e4604bbef67045944e1fff97ec6343fe8779ce87b2f74b57a199fc43f501766143950087209ff27c6b3449ff00bed88df1bbd346028f4e33f90c862298141e217f94a48e423f729da5ab8c074322da0080358f00cc05952449ed24879524a7a49791ac510cdc925e5093caa42050954895e5645c45ca8a2c2ac8854407001b3225259fa48aa254ea5caac2081042c7c751410aa004a974889ca44898d9412fc507928a92491900f79173d9f1d2e5908b6b0ff89b00e143bb68365ff037db0bf1abd30fc49e087a93ed0a803fbf702bf6fe4ab9792fff8377e31c3b28e89fe2771cf415628f2b005720c14597c14b3a3ec63565c568a38d163712ceb38367e71f456a3c7e2e16ffd952b0cbd16c63fafbf3c18ba7ac628fd2023147e0b2432c2448f257adf0e8695807d3258f6bd70f4df7779980a464628ac3affde9525d6c745b07716cb5361f309d3a3a3232e7d24958ac5fa4a3a3a220a2aa0807d2f5462b13c2b57c0234cfc49ded107822226ee6099288e9fc7fb77893f54fadf3862a2eb7f244c05232410f4227a57388efd90fb1054c2fc1f3feefd27c6ffa3cf5b8d60901fc1a09e5104bd2cf1f82f24865959b2e2431a856152796bac68903a3a3a3a744879fa5a52ca3252ca3b708e56ac1392941268a72828cc999fa1305e3023306282545b971d22a20d8cca6768878c901855991d3046411d9094524a093ce9b3fe106624bec67f647409ce9f44cad2e761369c56f0a49498ffbf0cce9f37918cd9220573c105cc7f8c042d980b2e6055a8885ef67fd8e178d4eb07295542741f29e5108be57d09e28ac0e15a5c14a18d8988b2d17b81e3c8b16ca9838d45b1036c63a2236c0ae793892088ad44ee651443ef15bab051fc9e0552c15cff231d7d1e182c13754410302c5bb9b00ff456463f5c8d60f7751e56850af64516e782654646463f1b459025be5623067e9808f66fec3ca32a56b4146509412e548cfe4afc1d288adec757fdb7b0a3cf13ffd1f7933eebcab83af2589898f9ee8da2d73dd74e9634df7ffc8754308cbb5c1c0c968da2c7c28cb41465316a79fec30ffc2d50ecc02b47e088a50179b0eccae7617c0c28c4123da51fffb5fe8885dc6505cbbc6099911111965511a7b4a86099e8e260ac1cb17895cf5312b1cce7a77b63c85dafce7f30fc673e30a8e70383c6fe031689feffa0dfe241fdbb1428a328494ae9c46ad5e2a2f8f26050992738642439c4103279e09081838c1a64c420e304192670c82c21a50bc4aa7c40050b56e5030cb3f201a6e302312b1f6054304cc70aa6020a386456c0212324a97017188edf57f170fc3c1d95bc81414c6b85cfd3d6524f5c12758ab2efc11fca14030cc8d40290c8d4027fa40210854c052049a602b045a6021045a62ae844a62a1844a62a9843a62a0042a62af864aa023322c82998aee00bef9dc414655f04591d4b33bec68f87fc0803c934e04006769ffb2197061cc844ec8b9ce35f87b13837a3029112c8716c89b3c29f04627f652a626980819412b0224d3dd2b48020766c8048791afa3c96c7bbad4b17695a9d20a5c90a49790a639a529e82c04879023a6df1ffeef485c5c78f2bfd24ef48ca93093f5e3e0b9e7873ead24f5ca43c31d0ea694e4fc4f1b4c9f07b3e529e7a4e27294f3c320251e280b9408a61860364869418d8b1ef85e369e7b405e57f4a90527ab0c77b5ce87f2e1fe87d59414a9989e0e8f246ac033dfe958c44ce61aed68bc396c67f640524c2322397378ae1f7564a235411011be173c142c044c0feef54543012309737aac6cf1aa18acb1ba97416ef2a12b0a5f1b342900b86050b6665145fa191911196bd581c66a58acb1b31f0a3825d5962859ce5b5be577865fcc0d0c8e8e3bff5819cf53a727923c6f9c881ff774649464658e88920d881cb1b432fa903ec8f2f570bec4646580876aed163bd5ad8ff1db6837529a38b175dd680236605d3b182e974594929bb0875c922311d70a58459c1e1c249970c24a633be404c67e42b25cc8a18662eeeb96aa98b62988d69fef7c2712342623aa3a785842cdb07524a9f3cb2a58f240266e47fbe6ba968e97f315afa48dc48f581469fd53171fc47ab1ef22f624b21ff3c4ef430a58f7750c446d747fa492c8e8bded24712a77c51fc2276f479abcf7daf7b2df003fb671561999804062dfd2f1e67271029e50886383b58603abf7fe017665b68c0d9b2b445052925051267cb8f94984e15161fc1174805fb1ed43112b9c7dae99e0bfc44df8360c4ffb98c9f88c5f13f8241481d040a411617c39fe47d6242fe593d5bf8e2b1493a3a764c1d7345d5250b781ac09853ce3101c08491214ac40e1c2d101ccd8d0dc5ee01d3c1d950a4d492482931d1857116d8c1110c5d18e6bf57878d9fd7e223f7e3ef62254a2010081689e3c75d9e502a0dd675184829c5c8949219525ab001149bb02e89c01a26a871446892a51d905e1f2460c807214e8a9803123baac8f20b99418a19b4100dd28409081afec84249046ab429e16488ab9103dc608920b208347e90023a407ae404176a7729a802c516063441e30b0552b810c21b59e841004e5c1a04381c9084670e168e048f3020071b62f022db13dc7108140ed0e18d0c9840630b1b45f248809722f8f820893d8828c30305d2047e5c68248c4a56e002e89005707922490700f842930e021a183204232a6481003053a0d0851d6104522502472892e5007a58d0011073782a21c870a08609187852c4901851f8f852729126844448293454c4d0e32e05809172c9a001a8864c816b5037a8b1414a540d32057a140d334020834c811b15030c2f48897241a64008502da058589a22a51c2353df14f2d5023b65614929f5b972d0a9160a520cb37a84a55291c599a04462c3211aa8cc74e920842e6db49002aa8f37b2cc11891d5da448e0061091782002037e78430a624610379cb1d288263983251fd0d09152810ca8d02227036e9473199d914b83af002b60b964dce4b44400d8d8a13347054a9648c00c41b480010167429891e40c9676f08a7c48fdaf447ff4792018a6f941fe8fa3d7fa963c07594bdc8505800161fa60a10113891e0d3c58f365c8a3900f34a8e1871d376220c26702563099001d02e8a9c1ffd0fb2d90f5811e872d8d5eea432293b007d4d03201a12752ac2081063d94c08534e3c6095b94a922b4e504220431a39a800c031f20a0870e3a064b176cf2031a5b0cd10704897480266552da71e2014d5e8e20e03b8094064cb205922e56c043027924d2a5823045d041c10851bc91c682091c2591d40925298881c39a4d882008804a288c902acc904a0c19222255c6f5ca8e17221d1f306554b50bddb65222201e55971f221511104fea6329bf0447424a944ba6380b057ea8969428964cf12d28a4153c29512b29514752a23a29515f4a949712d5392951dc54a5944f54901265040ec0948452410a49894a010555235da850409d3123a55c33001924aacc1752a2c8c8a36fe4a2f791f8a862f114e248810d4da57041aa0c14500041a60d99212955a93165c66cb2c74b89b2c2e21cc82a1a43ee921245f4043574c2d2ff128194282d424b1fc9f39e9428149094a8a016f8454149107e24ef03c18cde276625f21f823d9f07f6d19b4206ec600787fcf747f19391120506484aa410e482f3454ab9c42325ca841f29515eba4889e2b2494954d47f9294a81e9e9dd45016d41629512568912812b27c7c55f443100ce80243a151e41f68dcf9402edd03c72c852f91ffb0e8033deff35a7ee7fbb0b8d73dff3281257a471f88c5bbf7adfa2fdabc4fccd2b7539374743eef4bf8c557f85d2f222951234829513a284d4aefa9505bf6b47a19ff3f3152a2aaa0342a9bc48cc2a83b464a944569c062bd82fc2f921255a3a0286a4a89c292753c8554d009a92030a9202f126565021c8912210469913a1f89a44455f92c8e7f62c02016ff3e60509acdb35e2099dfe2412cd68b4b4f75b5fc27c6c5c322579a0d048bc4ffb91c7d1e07f35b1e67f580e127660c41d767c123f22cd0cb67711c45052c62b15e5c8a3e31626a928e4ef862097996b7ea59627d6196fe975997fe17d4949bd37cffa1404045f9c08b1a5e9e90e20ab58c94a80ea448c900001810eb186fc788bb8c2e93cbe032b78c2d53cbd0323ffc5ec4ca812c9e6a33dd1b789647464a94067ff43eaffb3ea80c5018f81f86c58ae23f8b75411786c5da61b176a44459204a890ac0f7eaa44455908394280aa4f77e26f8e30ffb07f648894a05d9241d1d9c6aa1640a802dc15de261b823252a02140452faff79ddf79f18b1da241d9d6a9374746a928e8e4dd20276df8fa8e5fb4021f8792d2ddf07fa2d9e8683f92dfef1917b2d2b91affc4f0bfcbe6f819f288a9ee591d192a5ff31dfabf399ee31e4424b1ee41943eef23f4b1ee4d1a2a5da2cd58a19adec57965de96bbf963ed2cd2fafda97969d1fffa5bc61444a39764ec822fe7b8160525bba222f40be20c1887f442b04019a920d0681848a19401f9eade5853b8434150560d47192a311299b1462a60b3a788000060ab8018dc885021d232c8c78401b555e67a40892840b1c5cb0039618849080086540c16010302069c2892927ff001e5278410132433c296203312c1e3031b3412770dcb1c0263a16ec20060d348430c001031c30c8249cd4b143129a0b39703c84c101251830628503475eb8a3863681b8ece8628a084a2831462030c471668a160480c7509b36b4d10c31a410b20040c9c6840b102c59a4052438c9c3034df2431139a4b106262074a14501616cc08746252a318026261cf18038661013c9a3123d4c18814d0f0f5904257103ebe4a5a26cf0ba60e86cb1e00fea871a247e1021d0420a01b21a6e48118696061d3740f2001bad06bca0a186fbc2b80207958c391347076d02c9e38c960408920bd9a185546b8942aeb000e15962023e40018527031c3764284d0c12e0fa200b3f0cc0091c29b84087027a2062c5911551008092cc5bf1b9e4260b261461d3442652ac74d0c38a8f0738b16120f082ce30c40749347c5094c4265aeca8a08415667cc83b4010a290410e004e8042004d0660c553153e56fc1ba864c2c60f2b7460524a4a85d145e700288c318492079c1183904cbca8220dffc7249850701448119068e1c44c03020a5258cc40003a7e7e40094090070ba1242f30406402aa8e497840c4088d157e5845020940155d5861660e2d1c3864657a430062045901e39222f4e8a2014a685815261cc00b20d2783602d0b20292456a78e055061a1a103141177e0462c21306108102148a148961c7842ea668c18a9a053a4a8820c01fe312a023413c919486080298e2c75050047c2ce9224cf7dac40806c64c9e8e8626e8682d400b2c2070c01663b44c9b1144ac8081c98a33ae4a1279e38a6bb98831812576608d4b459ac8c0c01293fca046083ca0d064d4808e023450c425927c3033c9640b1ec2943187255368ab283e8df0e1c3874608254c0ef9034d111624e08c231370cb0a3d1478009a3ca0549088150900b06a027b32c2139244d98391277fd8906a8345253470a1068f12520ca2c29123527020c9165fa8e89068220e32741cfd8429438c3a7ab0e280191d585d30e9423f5981e1c00b5c1e104707423412c91908582102613e0d5d6680a08340b65870c1225924b90285c6f43ff81cf2431a6df43b8c443c9cb06226f941812e422694a1790e20820f9c20811803b28ca1838b3114fc200032147049227cf4f0c80e1a38800c130ce9649be38e009021858b244e72e0814d16134ca1c2197c6cd0002ba09c11c380148f4cd282044b1a14187e081381144bc6151ef02448d1d070c1e0f3c1f9a3864696b8240a4a94acdc27a818010d1cb2267f889902870530d010840c3b94c04242072e5cb69831420165e7d0660c9c1c2e72189b3470461b563217143408ea1c2998e162c6920b7390c18014331c0f454c2b06524690e100293ad417b44400c10194286109339d0c618600b6cc20542501a121554471640510c95779c01906b0c801010b88091f5081c304654821c71fd8086164146d26d983860ab818c34b022251c1a084069d747094c30a6128404a1707a460898046e62201221d1a988388331e59a39036889c60a8640e4bdcc0600f283aacb107124e78e941c90517ccc4700410323880851e6be441c50bd713420c0089147486e4f84292310801420b0565b4dc78628c44a220c022ac9c323700404d064f2442851332b21139a0c0c1141fd250a40894225f8461838a954188aa0610280abcd143181d58e9c4cba1903b5a3081d1b68a16440c65cc6881102f87299a4c208501ce450e3bd8b4b0c6d4c18638de0819b152c20461dc5023881308d1049120780f72a831040a043e70423480704315415cf84754cc800284cd253cc83181173a88d14de1fd082291261c21a22eb4d2c899429de03e6478f043058124c1c1488d8149cfcb102d0e6c4102107fa286211611c4913b3811014b71e30b1b420cf087107770f2038901226014829414174919a12755d858210d2107d88018f5034a154a7d1037aca8a4019d271ac92e5063c768d202e6894cd8182483230b1b073848e408870947504861833eda8022113e96302285287514320464c819b20d10832afcd07088c109139d78d0499531368f1028e032ec903a63103214c8ca464b3af3248b1ebc3024061f76c802024f9c9821cb053484b1011a47d414616903831e3a322182bf31849dc01021c8b8a20178c410c90ce00abe851c4c5e5080e30dd60f784461481a60aa78d0890804782905f16491212ea18212107a38a38f2d5877d0e001eba7e809ce851049d4d460c8068bf061c7ce216118200245687193a480107770b245230a6072011ba928549a246d26b2744148036a1c80861370f4087c8080088bf840c4217ba079e30834a0146244142b524cc1d84001cb246af670638c102b301001220d490c300134233c6962100e09932bf80cd30ea966a526cd060fdc2f12d0c9e9a5041f36e82a57bc50c683c3035c00175e3f7280a0c48ac20923c64784b264170c00006b7c2063006c9eacc008182a3c8047550460c1d113234419da687381153b3cb2085203d2a084921506513d512130c21206a8999c073800c00204026c48a3851c292e38e191299202150eaa3ccac0788c78655143d21821408188053d12e8b0a2a2868d2116a0d83f34f051a0015376206189b08810387aa8e96ea07c1c614021910499e278a4020314f1c62d028b921944fc70a0934e0a39214c03908491a5101c2ea9e2cc23919849b5a1003b84c0c1b4c50c05723eb852e68696d01060bce0039634487a9c818201b21095e025f139a18ea313768f0a26c8a1da24c9db008295188670227008521a1e2801742cf0c1035e66bca0e2b48f6e50402518862a1a100104708882035452152544820040950b7836817060f87983274a18468879b1917c4460101ffe981140d06386951eca98a0260a3f6ec05801660e3fc0c8830f3414e8e2099e0ab21822641327b2a442c051bac8d2c618495a39601fa14f116a2858849101a850c5d7e34a0c3520d122ca22c61928784ca27ae30b509bee084f1559104d516601dd4124382bc81103249b3420031d6854e0615cc1c8012c2cf28000188d64a183106cea382301117448e19237f8d84406e18c69c0174dcc4cc18222c18822900082794082ec59e28806f6f81e4162cac456d08367932a28f9600891e2f87221728894444e3021e511f2871e340a8418d2c6902b3210928514be64ec3ccdc7226209ffe83dc0b554548814bf9f1b4206d922816402a99b878885d3d6e32e63e9f2ee1aa6b9fb7ab58eebae88e11516443165cba46c969307750b6a34f3f8a3172a79fec39b5f3549e512535384d4dc203521a882e97460f7432a589a90affec7889e6b47f45cfce863b95a5e36854b2c521215904832750590d415323265858deca22565a50229820c33b4e042085bf0515c8094f2fbf9be2b0c18940aa1285585cd8e9a2df62b454503556a0a26e5298d3e24085fa44981e02598fa6092d4076348897d1e96c67f50a47256290ffce0e1e7e18631c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a594d259679d75d659679d75d65967d5344dd3344dd3b4bdf7de7befbdb5d65a6badb5ce39e79c73ce19638c31c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a574ce39e79c734eaa699aa6699aa6697befbdf7de7b6badb5d65a6b9d73ce39e79c33c618638c31c6f7de7befbdf75a6badb5d65a5b6badb5d65a2ba594524a299d54db3ae36b2b6dfd4833290c8aa4943cfc58f8d883c5ff0f85642aca275351b84c453941a6a27091a9285b642aca95a92854642a8a063215058a4c4591524a06a02c0081ff2c30c8b3402f5f14779440d6e759627d61463048cc077a52dee8c1e2377948799300298a430800ba8943824143a3d7f280a4bcc1e300f2e60ed48d1d37754829b9e8fb46b48daed7178330cf793cde02c717f7123fa7e4f9ef85fd9073f1ff53e22ef12f7116c6e21c48b92353134891a9142132957a820b0712040bb4542a07994af1c8548a0199c25144a670d890291c24640a6749a6703899c21941a6704e3205e50132c500159962009329062c90524ad0008b7710e86769f4c480419ef380f19c471c125763e8f1f07c161f89b8b058bc6769f480be17f671d6d14fe647c844feb99dbf1a438fc338f679cefdf035beba2a4ae0127761e0887d3f23f8411943ca1b0348794387943773c814941ee490f2260e387e0b24fa6e4879d38694376caca1c276e68748e5b3f984e909da544361868636d50624c6270c1855991d303a3a233fe22e30740125fda30f646939e245421f092cda2f2dad3ffe1c812c2dfbf5f33fefe8db245842ca2a54a494d209169b08a010c36c89538922777de185e49fd7f2c22e7abe2572250cf41201908fc8e223ef5820c82d712410b392858fdc4b7963430d10bc204597e8ffd1e7ddcc20c3e6440c121c5d4b61afd6fbcf45dfbf8ece0f7ff85f68b82bccc5f932e0e69738822c710459440e5c29552e17d7d1d128c635141b58214ceac91662988d1eb72b5589e177b1fe6a579a7aa223e6091429068aa4d4d161000f0f74a9fc4fe3752a97b80386ffcc128f189f9df0d53b78238652b2c270eb208bb51346eaecf060434a8136f3ca15058a6ed080524d0a15088a8a94524a9042b1c4305b8ddc8565697481e210e63d6e357217c662ede8e848e94198ba1143a2f41552a40262a44e97a44e69a04899aca4e4122d96ff76c6ef0b3def620d81e13ff381f8c87d5caca1ef038da207e4620df191fb881ed0e7018b5caca131045744e28f8b352433c0e964b25c2dcfc50a390123e3ea6566ea85132d524a297ff7581b0b01bb9b04225569e60a7da018a990d8818208de75e292460b92ec51010946772c401016962021114ab6b84184138764f9618bd00145da68428e2c9ec4f1480c602060c0236c4840cb90b826101242247ab0a4636e5871d551c3867b14a909688d2244e2c58c0f0372886e2880c60a185f5012456f408209014ac8610513644af8760837535a31ef38effffc149b00d96e9d5f6bfce38ae7f656831cf7c0f4c766b9aa3bc733c69a52aedbdd1f7299fce836bdbe6a6dd3fab3e59352134c0ba09dbf9cd6e9c9ebcd56af34b5b40f6ab9ee9dd65ae759e395f251fbe51df6d96f5fb1f71ed4864c7bd4c9ffd75fcb5dc79cfaac805b39aff5efebf61ed463d37d79e59aeb8beded93c894c786fbc67c538a3bcd6a79ef95a6fe877c256e6d4a80ef7ebdb8529a6bd670adb52260e3dc67ed637b37e614732a6a9a4d78681ad7e69ddcd5b899f5e4600b0c5db416990eb0755acf5553cf751aeef8df95a62e30f494deb1d50e733d57aafda5756b7ea569ff492a70b4d48e8d7efb75add76a6eeecbdd95a6ff03595dd5bd918f62a5d454c7d6b3e6b77fffb58e72d7ef95a69b071b60e370c7f1ec35aedb779e6799a5435f7d353bab96b5baebd539cf7f359e9dafb657aed13f6b1ad31cdbf478f2ddb7bd94e32ed6664c726cd5d66d29a5966b136bbee3d83ed7bdb5dbd28e7e9a2d8563a3bbee8b79dffe6a78da692ac09e75567a35b65a6b1a5b6c35dfdef639b5467daddbbeb1ed3d2fc6d377ba9b9dcd1310931bdbf476f35bafff1dedb84ac59021cab29792a8b721a636b6df75ca77c767b72fdf5c5e699a53d4c5cbb55e98d8d87a563c2dc559e79a33ae959ad6d8fcd69a9b75773d739f7de43ba7a88bd0173ff1fb3ecbfee87daf2c6371af057e4b2cd095654b2cd0a5ab14263536af69954f8c6fd57d6b9ec6f6e7ae1d73bf397db53cb9098d6d62ea77e5b76a6deb0e5fd319188dc98cad7256df9bffacbec339e332b6ce67aebf563bdd7d4e6b1e8fad6e9a0860c9d8b8c97d8eabae1de7d9debfd254e97f3c64b28c53ca323f4c63548dda9aa99df3de4d27d6769dfb7b7a35eff5ee3667fd4ad32596cbc57b727a7238981f6c39a53aa9ee7fc924c696e9d4dae65aab7aeefaf1f8fd1c793c59a683cea88e4c615430362ef7cde9f47476ffe9f52b4d57225fdd9ca22e3d154c4e4f0e97eae58b9c27c3c2f4c5b6abe7b8d6757f4d3feef84ad39c53d425a75566cbb22ca3960a9317fa5a7c6dd57ae269af9d55abb3de6ed67c39afcdce7eb06b21d833be5ce32b4c989c9e9ccf824734bec284647e08f68c197b322fe449aab16799e7e259e6b56a87a98b8dd35babc7977ece75bf3711104f9689262e36affbe668d67bcf9f2df7579a668c75d01955356db1654ff3dc3f7f3ba7a6b97219ca99768b498b6df63cbba9756e7bddffdc2b4d754c596cbf7bfcb55c397a357fa9b7351c5d271f4c586c57ebb6f2ed396abb4db579f3abf5faecd8ead86cb5cb197b7c7fc79a86f9be3fa62b365af3c71fff9ab3f6dcefacd872e5b46d33de98e3ded66baa62d35a673ff7daad9cd530df958a6d766e2fe55d673beb7fe5e8ac5e4c536cb872dc6b9b53da7dcbb756a5d8e8fc59eb492ff7f5d7f468539637beaec8c514c5f669c7d1adb7f6dd766a3f192628b63e39ae5bab3dd67ac71a7f62e3fac4b8f6dfe18cb5bbef1726276a139bc75ddb4ec3b6bb79f3bfb9e41401e5fc50680cd110e90a646262bb54cbd8526e57dbb96e393baf6989ed56cc4d3d37a5dccd7d77644c4a6cf4737dbbc9d9492bce999b92d83aed5abc2bd7baab3fa73513121bd555e3b2a66bd77856bc579a7e2f15b5654c476cb7e69a39ad75ae6fdcab5e695abf4a1cffd8c14ded1793119bfdf773d36b9bfffe699f2b4d59af96ca7f5ce5a9567de49c6adacd5404cd143111b14d8a39cd6a7ddef933a6967b110db1799d6dadf9fe3dbde7f81662eb55e3bacdbbbd996aada92988ed72d472cdf149394e35dea635340c1310345332fdb05d6eff6b359d2ff5579bd4878d9bfed2dfd9feef9e56ff1eb67d73af5e4fcb69d556ed4d3c9876d8e6a4f3638b3bdd7c6fbd4d3a6c9ace6e6a1dd73a3bfdecba95a6df832d95a7951e61ca61f3345b5f3b3a3b8ddbbceb309870d866d6fefc797f4db1e6beaf340dc1b1f30eecd7f5877630ddb069bbe9e46e47f1ec6cf56ab66bb5affade8d2be7ace6569aa619ff51ae2c930d1b37bbd6e5697bb7beb373d7b07538775aed383e3fa51953d1a314d3acfb57c14483a6e16a35ad566bb75a6ab3ddf4248369868dfb9cdf9c4eaa67bdbdbb609864d82c37b95d7dce736fabd9ac420c5bad7e738dd3b5c35ac7a7df38e88c4a05c3a66915db6e69f7bdd68eeb057a61b399d6fcad8635d6b68b0b9c6ebbef9d1ce6b8f598b7b051ae6b7c73acb5d673aea1fffed1676d2058d86cd5f4b79a729da3d9e6d7d256b3efdd767aea3ef1d678a5e917f50f6936beafe5f352cb6fd6aad64a76677ab56635abd1596bae5bebf5fa8d37c7b6eeedf94ad356087e97568d26256dfd763e39bd31be93ebca579a8e211af1f2f0b25f8c9be7dc726d775d6b14fb0e8bdbed72ff98d35b7797ceb9c32d5b5b2bd5f0e7b8a9696d079d51816836aeb9f79ffecd758ddb9b9bd4b5cd59f3fe5af31b677bb529ee2ce0a6e1bbb7b6b8f35eabd7774b1f897bb9b0f66d59fffef1ef785eade28e8b7c6529a6416754ba45abc513eb8da9aef3de8daf46b3e5dddc14f77bb5ecbb8b86b555dfb3fed353adefee534db34ce380b4e9fab3c69d6badb9afdd5f69eac1964af56dfd2d2f149af4b7bcd0858556d8ba49adc6d53d73def9768d77d019550c1ec638ef3aefd672ae19cf9bb9ceb5d338ae9b1bdfcccd3a569b4635d6b8e66ce73c9d7fe6a0332ad5d1a67fd59c7b9ab1d7beb77922d16dfa7faa3397b1e6699e9eb7c0b0cbb2161872c9700d3aa33a29f1edbf76e3da79effba72bfd795f3edff99ab3f5dceeee669eef4af76fc97f9f28cbc28f14f216575fdf92cf2902ca4903f26499ff69400e576a42a35f2de39b2ddd566bd9d2fa3b5bbbee31d7713afbca395ca9cfd37e4de31a5b9f412fcbfc28aa90423e318be33da9cdf65a9bb53bb7b675e79862ca3badeb74ee7faa8cb6fef5bede76d5729a6637b7d2b4e39fca7f5eb7270dffd19fb404d556bde63ac74d8e56edeffa8106b075737a4ab5dad57d71a55585cdfabca9c6b9d7ffb9cef195c8bd64d9d44167543d854dbbb7eaaa394dfb4bb7d5569a28cbc42ceb208bc5f3154261d374e5eedcd8facbbfbdffe8c5fda033aa2e67b6ceb56ef7dcbfd6aecd793ff0532d71aaeaea6c4e511717972cabe14f02ad2a67345cb22cdb1985992deb8e29d7f3b7dcd5defb95a6687eddc9d06c2c9e65ae56cf7294d9f2b49e67caf9ee5be3da934166f3bab3f8e2adf5ad5feb9cfd62cc36b54b6db73dd7a9d62b76bdc0b00bd67410b365cd5bed7396e2ddfdf6937b8993e61475290252f16cfb451122441bf675db7da7ed16db6dbde7b3c8fb9d9fe465d9a64c0c6db6d366b67c5ead615c3b57e5f4b8b8e46bc6095bb79cfacc75fef3a5766795ae158cd0a6ade6b2e73457eef7e5b42afc4aa04be59ab57209b3fdccfdea69a55c53adaa3e5075f4792a51257af39221cab2fe55486196d5a32ccbb29945509d19e7baaf46edd6eac575e74cf5e75afe9afd9a9df957ad13301bb5fad3cef1deddb57fd295a62dd15bb942b005faef574a8faaa80568f37a7edeb756aff6bb8fb595a62ad6ab35bf234f1477582f30599665a20bbc14892f9b9e1cef0ee76e7778cecf579a661f4cd870b7f67ffbbdc6bbab94c347fc6c75ff7fa7cdb8dfa965de579a1efdaf7ad6f7b808e51401e5647b12e165b396b35bd3f35abe37c5dd4a53715475a0aaf512311a2e5986a60bc14e95738a8072b22ccb5c5d966559966996aa542a341da568ba4ab755a990426e69d0199529902e1bdfba63de691ccf2e7b8b579aaa5a60d851fa8160265d0a7f0b3c1a3fb0b5a9abab39779d0a294c81cbe6bd466f97bde7a6ef58e3569a1ebdb8ef714e51971c96abf5c372b5bc7ac7566773d01955d146b3249fed635bbf36adef34dfdb5d9b53d425a7e8dea033aa9f9eed567e6bdf1efb4933cd9a67bb3477bacb9e53cdfa4ba7989d2ddf3db9ed33e3adfdcc33a7a84b4e91ce416754dd968d6bbfdbfc77ce7676b776256cf67697e3ba2ef3df55aced95fea3ffe3f7fdf7a304ae963e5ef7a865f31cb57673f47ffff7e5b7eb40950644c24629d63cc55c676fa5f5ee2c9bc61f53cd7df767ad53eb2b4d5596e64cfb4fb23f236cff778e9b58c318ef9c755d69aaea21afb67f4be2f5d1d96ed76b97ebe52ad5b8a9692b4d5595e6b1d5d9ac6d5cbf5cc7f1dd691ed73de9f7e07731a53bbb05822a960f55aa06f4f6b9af73ed969bd47779f295a663b8b7e46dcffba7c633573dd5ac96579aaaf4c6dbacd7eeda4d4bf1ce1dc7ad34dd54df6dd3caedc67ccedf7dffdc4a5325303c52d16ab79a6dfdbae3bdeba8d7b85f691a8e62dd6ef69addb85fdc699adb74a5e9d8ea343174ab53c357570edf6e29c579a5a9ff38e869d566e696abe59b6a9b771ad568e6569aaec4974b15825ddec9e9c929ca2902cae97f8c121fd2a8c6416754405836aee330c7dbf34be7d4d9ae340df9aaab2699f1d5cae132943371d01915994c5b2de74ab5bcb79eda9d58dbac95c462651bd5bd5fda75afbbe6be79968dad2ecb7e08766d8dae6c176b9f9bdda675f6ca375e69163cf195530494a3fabc5cc3d1657f08f61b744695c5cad6b1ef95cfbda9fdd47ebdd234fc9f085bb555f37f29cd1d573367579a8afe7f5135fe234c738aba883c67d657ce5096b540fff130e339454039aaab059d519972084125be42950a832af4e60da8a4019546014c0121e38a7d50a94a85e5f8afcab8aa9078ef1f29040fecec608a142a18150eac0860ca0621683033a8820116a5830b6ae88105aa0f02505b6057853f09ec3f54a92852c6d5d5bfa82b5852a93ca0200d6f71300155812c15c84ad1160e55fdb05385e0c835804205001d48b0bbf1b5410454f5572a5517bd9606104c79408ce240053168000ce189e73fc48001012c4055ff288a02544451afd502a9dcd40e4e21ffac9e8189aad27c0de4141972e2c8c4f45bfc43e5c8f1059407a41d4310b223512ad4952eb8d8428b2cb06073851555503185145140f184134d30b184124920718411451031841041ac01e2071f7ae061071d6e72b8c1e1861b16244e1f44489c3ebe90e2c68d07a41d6ba0d8919ce0c06681436eb8c108356ad4a851a3a6083536d850020e3c30c490830db381273e357c514396279c6002871d355a460fcc0c54482ebc9063844a90214d0c66c83488901170c4c400d31a26355230e4a1c0f70435c4931f8052305cf0c224a91726e9e14eea051924cf0815fb00fb3eebfb2c9c72410d49422593f024b10688540b840821252a0829516ba444012125ea0729513e4889ea414a140f52a276982de0b05c2d4f954d9acd2babb103ab5041f3552e8f850cd311592038867ff4a4942916bc163882acbed4f7528f94927b759108f49112a5430e01de4eae6b39636f2fa77da5e9f6375e6cf34feddbbe2fedfcfd9eae2c556dcadd74816d77717731ddf7d6cb754ac5b8e1629b9ee3fdbb9f5667db796e8bedcf5df1d7bb73d472bbea1b2db8adb39f9cfb14d78ded026eb2b015dbcf757cfb3bab21375868adb7eebce52afd7c72ab10ecb407dcb0c1b47395d6f9b5e9eb9d2bad5285e0f7c2a2bfeabca7909b2bb6ec3ff79cebba9b5cf5da4b4f4f0e51b562cb59a33c73bff37aabd6bc8acdeaac75bde394ebbd76d55fcdbaa1626f9ab536a946f3b4bb735dbbd973777ac9b29c2220aaa7d872c6bfe35df31cef28a7c14eb5148efb47871b2936cd6b5ae7efd76aeddaa53d8a4dc393eb4e6fda512e637ba1d8a636b7f6b1d5f9765adfd705863e59e60243af69374f609be7b454a396ce8bb59d33533f3bcd738e675e69fa1255dd52fc81ac9e2cfb1fc8ea1a9b1b27368da3b96bdaf5b8eb59f72e0494935304b4c54d135bf59fd368c655ab9c7bca25a7a7fba1f58689ad6f6dd2aa79e67e577bd74edc2cb169d3d7ea7f672bc73bcfed4a5370e42edeba5162c39ca65d9fb7d78efbddb42a5c6f92d834df3fe69a9db973b6d3bfd2aa49c71014ef0d125baf1beb5d29c779af9d732b4d73ff1eecf91e0cd226cd7273c456a9ef3aed62ce739e3fbe2b4dffd106023262ebfcccb47659535cbb3e2b4bd4c29d4a756e8aa8745e4babb557a3786aad33a5d8eaad7579f69b358ef64bc486b5aa55add14eebaeb6f221b67ce7fe1967ed76aab3ff4243393c393d393b394240f77523c4a6e159bbce6d3f27ff58b359bc0962e3b4e779db49fbecbe9edc4ad39e9b351bee9ce6f4537df9eebcbfbb7503c4e6b5bcefbcd75fdaf3b615e9e6870d73d7779fa3b6eb34fff3af34cd14cc8d0f5bbe9973bebbfdbff69d86579afaff29812acf4156968dff28cb728abaa8b22ca727e30a2a157d24cee2c9b22ca363b22ccbb40d47d7cd12373d6c3f6b136b9df5dce3cc75af72bd544820c8d235a7a84bce09393d3940394540391ae775d01955d20d0fdba619f33ab98ea39a7bdbedb0ddaaade61df3dc5bde4acc92e63d73a3c3963b2de36abbf79ae3b0d6ad3465b178bf1f974a6f72d872e6fcfaac697bfdbd9cbdc161c35db33cd7bbafa79cfa4cb4ab2037376c1da72b773b9e5ae6b0a53ecbb2ec521565e446cd563b3df1f67b5b8b2b5551abe6c686add3ce7fe6f8c7db66bb69b8a961c39dbb9daeba6a76efcfe94a53fbc20d0d9bc79fdf4e3bae7fda5595564d35dcccb0691abe5cafd9733febea46868d4e9f3b8d53ba2fed34da3bd934a5babbf37bab356d674e67a536e88ceacc8993add7fa6fcf59d336eeb4f67fb22cfbd326dbae1ce6bef35de39cbf6627b0fdae717cb9be77ebbd33d664a3fb636e27cdb3638ed29ac9b6a9a6d57b6befb4bf7fa72b4d3570fc7e8a5a60c8a57f1791a55c9c30d966ef5beb7a6ab4fb9bbb39cbfa4fca32acb10ca74bb6db69d8ce6df1a5f65aba2dd9b6fe5a6f0eeb6ea9e737af34fd3eabff248de24d2bc999626dea6bb39ddbee4a2747e9ec7a77d7f67b8f4e946cbf5e8cfbf4b9768ed37f92edeeaedf2e73936a8ca797c0a6ed8ee9e45c57ca7dce75926c964f8ee3fe73edce4be78f64e3ac4629fdb6738af5ed20d9eaeef8ce9ffeedefc7db3fb269ee39e7eae6ddaf96eb7a0436daed8efbda9f3affcf3947b69af9f6f5ee0ee7bcf9e51ad9e6bd3373b376ca69ec2b87c0a669dcc65deb30d556c37b33b2cdeaf3e434ae5dff5fed22dbd42ae76ab65d9ebbe3552bb2518ee24efb5bd378761dcd896cfc76d7dbfdb5ee9efb1f91ed5a9c35bbb9aeffdc13df43b6ca59db5d7db549abc655db66b39a9bdf77cf3ba51dde866c98ebf6724c75a61cee2a7dc09633cdfc4ecbdd8d67b71a3da5bbf4bf68baf491344b0bd9389e1d3773b594ebfed34fc8a6759db3b9d65ff5efbaefcd9c06d9f8ed34ce96629af3cf5773c0c62bf775b72dc5954f3eab205b57b53cebe6b57275d34d03d97aaff66a3d6bdae7d6cedf80ed6bb86addbf9ccb2b4d3fca80aa61bca776f1dc5a5faa4dedd25e37b75de6ae9ffa7b4c01d966a713e35ff5e65aeb6eabf593ebdee3f4c74669c771cd5d9d77ae9c6b2b4d9390766ec0c98f6d5bcebbd7f0ad9edfceff4ad324a44bc77cc1158fd302366deb6d3beffec55deb38aab1f81786d99a665325a73e36aab5bf1defddb5dafbdd579aaa40bff9d87ee5335f7df1ee38d66a5d69fa8ffc0f7f0b57f0b4c7d6614abbcd393b6dcebb5b1d79613e2960d397de99a9d64d6ed2fffd4ad3b1fb2c158bff515421b15e5c9a3f6a7a6c974f6e6aad66df7515572d8fed4f8a39beb5fbbaeffd79a5e9d1e789e1abff5045ff91dff807eac91993d393f325a727677c81f91e0ccab22cfb93d61313a7046cbab3ba76dd7387b5d6f9029d10b0693b2fe728d6d7eaac4d7dc263ab9cc376ff8d37e668d5eed492381d60b37572bb6dd73babb1c57aa529d87d7ce43e5b456f0d3aa3321971ba63eb6887b5ebbddf7572ecb9ade564c73635d5f6d34e7dde7a6aad8e8db39cd6fe5a8f354d776b003ca7ae59db344f6d536b6bd52e87efeffabedaa77f6abfd2141cb59ca22e3959d6bfeffe5096655db5347a2177dd59c5898e2d774d63acf9bb79d55aeb3936ee576c3967bdc6396de7be2739b6ad518dcb9d9efaf3db352e8eac697a6b3c27d6365b8e9ee0d86ed775af56eb9aaf55a3b4005b7731e718ebcecd7e6bcea737b6dffde719e72ecf4e6be5949fdca895de6f75c777ad5caf34bd59bb5a9efd39b5b1592d4fbc37a7bf6fff35cdc6a6ebfcbfdfdbf9adbbcfe73dadb1657dfbefb85d39aa39ee7335363a3b3b67f739ada6b179cc77d6bec79c9bdad4301a5bd7acce55cbb8e2baaf3d63b3be5acde33fe7e6b7d6cee51263c6a67198effe79d6689e7f4e2fcdf7127e2510a896b1755473fdb9cdf3d5baa9d92c5be261c88d4e04d8ba7639cfbfee55ebb46d39315906869f850ffcb2acf5fa32191bf5d5d2ceedeecedf719ac6636cda6b9a53ff2db7ade6bc3e89b1d9be35fe27eeb9d26df55c699ae3f52be7872f1164f51c4ebf7254bb9a663885d1fde45ad3b9edac98de96d7eaa80b2730b6bf3fd769ed9dd5dce56c1e73fa62d3bc6a9a677e35d72bbd78a529f712b917d107fa8c35079d51a170f262c3becbdceface5493bcd54a5517cea62d3baade55a7367f1e532d711852311a53a1c45cbc5c6cdeedb5fefe4aee7b8dd8d2f4ddce1285a7bda629b9c7aaedace6d9db566da527d9fa5e9920becd8e3574e510e875f39ffa4c566f1f459d75f6dd613d753161bd614e78ef33cbffffee53961b15d9cb936b9766777bbbeaa4bc3556ffd56d64e6c36ae6b787313d35bb93be7bf62e3775f6af3d699eeab77c757566c96ffca716d6f8e6a5ae36a9753155bc5357b6ab78671ed975a1a8e22155bf7f7664eebb5e36af79586a338c5f6b5d6f47bcff1cb3fc76920c8793949b175df66ae719952ce4d4dc31a079d510de014c5d6fdc4bc5ebd3dd7d456d1634d0ac5e639d761db69bc6a9cef9d263ec1fdfdb7ae956adc6127b6ca757c639b3bb5b5e338bd4d6c9f6ad47357bbbbfedbf9ce32b179cd6b7d6b5eb9ab69d9fabac4a6bfb6f9e65bfbf77d6b196f2725b6a96fe7747b4d7be6b43f579a7e96eb3552aa4f496c765aae692d4febbbedb52922b161add3dcb3d6b256bb79397cc4f66de7eeefb8e53a4c2defd788ed5b8e799eb7663f6bf5d616b17dced74fafc6f9e5facfbb12b1d1dbb9fefa7fffb5bd6757e53ac496e7e626a55a5bce6b3ced4a5321362e5b4d67bc2fefb0de1ebb5caf20b669aff57677bd6afa53ec579aa6f92b968a9ed66cbad2d96da797ab9c4eaab13810dbc65c4f8e729a6679ffbcfb61eb3a3ebfe777fabbbfa5da3df9b075afa9966fc6dce63e7373a5a9e7ae1eb6ee77bb5bcc69fbfebca95e695a79d82abd334fca75da697882fe521c8e623eedb05d5cf7b4b973bbdb5dafcd4e3a6c99739bb3b9dacbfbe45a77ca61eb56fbdbe9cde9cdf9b9e709878d7795d3eceedcfa9e39efa71bb69c396dfba4ffffcee55f4f6a36fdbbebf1c55ddf5cc3ddc9868ddfcc751fdb3e35a7d4d295a62c1e1e7deeaa54dba71ab4b55cd618e7eeadcf7ba5e9aef4a51a5bdde672a261eb9aabfc72d4ea5e37c7d15da9c5add76755a71934f718d7cf677735bdf9a5fa62c85d97b65e9f05739261b3539b777656cfccfd99f3287a405a279bf5ba6f7ab7e536be95eef1ef6de2a4facbf5e5349e2fce4be1f87d1cfeeffbd964a39ce57ad7edfdcfbbcbda9cc0c6e99eb36a1e6b7a5a5aed67b95e455393ed737ef79dbaff7cb53efd4ad34c822993cdfb6c33a5937baffdda3d26a2748fadce62b26d5fb9db75bdcbf5fb8efb1dcf85b24cbc64b35c538e6b9953abb7ffda524b367bebe534ac33d7daf249c51d0d57c251fefbe7fe5bceab768d4c946cfcdbcf6d4cada719d7ca69239826a17cf7ad37d7d56a93ff20930436bc75d570777d372b9f94ae345589926cf8f7fdffb5b4d6fbb3ef569a622ba648367d39cfe58e77f653dab9ef3f4fe97b819f6a5348b6dd2bfddff29e79ce9c6357cbb39ce9916d6a7d6ffb392de33ff7f411d8b4dde9d4b8ccf5fe96abdf914dfb8ffbdd156bedb1ad17ecbe9fee7d5c28cb74235bfd9afdfb7678dee9b9cf479fa74160ebb8d6796f6d35cf9cd51c239be7689e5bc3797bcee7d62ccd8b6cb47bfe71afd5d2cb51ec7155ae18fb9814d9faee9a9e3d7fecb5df7dbbd22ad1ab543325b2695a2fd635676e6a95b3b257f1f008d34a35229ba6b9dd4d8f69debbdb1db7d2744cf355aaefe25dfc99afaaa843beeaf790adab9cde9ecfae7deb3b6ddbdc66b3dcdff8d33b376735df65198bf5fbcc416754664c866c97d336f35d35ce5bacb96ea5a9f754dfb534ea6f27cb963e6e1fb0d5cd55aee3b83f2bf5f7f795a6786c7520980ae13467ffbbfab96c73e7aa9d2f1321dbbe9cd6f99be79ed876f3a6f13f471f1fca321d7446e54d836c58a39efb1d9d9557dc67353960eeac31b599520d63bdb55b71e5bb9bd9627f73dfbcdbaff1a727cbfcf75c60cf3231e3329433a9209bc5db6b8eeb7cbbce33eb95a6bf7fa0f6fb17e6e392b7e80203d9bc466bc5ddfa7ef5af5d5e692a22f1af65954ac521f51e5303e8a677da99ada69b668bb569abf6b97e8a679d9ca6756762c0169efea8b4506aa78916a418328610401065440024e312003040241a0b46a3e19860b653bb001480024e9c74ac44998bc4c1308761140441c418620c3104184200010839545403f84f552812aa0f5e39cf9a854eb1daf04be92df6314eed054ff9363dfe7838088af04f032a247511b5195141f2de53f87cffc1a8b2a135cfe99c3eb4862dd205dd2cd656abd8ff4bb74e5af552630b73f2a7c53c84141e81b807edb7867a68f900ce81c75b46a0840e81f12d2f63ab4408418195128af61404c928badf0479aec6c4828ce51467179ec7f048b492ebf085af15dd89824dc22c03272c016bf4d405a2f7a1efc54c05230600d306ae2baba20ec5233027d830ae84319b3f3a25909ee2ba51ad03e913c8124e16f08c3e5f02657333e8b67f8dc96475c1da7100456fab59cbe3a9e2581c7c1f9580ace5445abc6e2abb171d912a5d1f4f4b81bf154d14f1e7e1e107c92a334474f62c4c2786ce91507d59cebd5447227230ec56836c1d53f8c128e85734096c4b452a68d2e25559fbbc31d9e2f57422191b9d86f4d734b3006f3cfc0b352fc590d515844b16f93e2218f49d6a46c86a52b7342b4693d3926b3ea0532d46d10347c3828233caec6d16f83c2a05cac098d76067e1a5435f6530c0fba2f56b98e4d7ef8248c7525e0244dd61912bc4c37ef7012e01fcd00d87e674d1e180b2779e8653bf24f4436bc7c359efe0e2ea44b1ffeff890cb154175e6b4959d954531fca1c46b8f28c2dfb6703585f0dc0949d9a2cf4af9a4a831f58c41a12474c6d71adb6f80084fe72b287de96d2000f9b67c68e81a6dd731f65a654e15ba37b814f9b4db2cc88fd99c355550ef50a31304d2c4549489f975cdf6c6e5c6c92418e8143ded4efa95ef69c3336157d51f245f1acefba4a52f8233cdc9e6599d84144483ec0a76b6d190921c3bcba320ade816687c62cb7df5270ea019cb7a1f17d0ff3afe69be5b70a6198f582dff2a597b4e7686d5682904a046a7b2cf31900b6b7f3bb04191622e3710a4a2c5192ea0c5896ea8c059dec4f3c277fe1118fd9618f29c526b29d7b43ba7b1ed871633af35f9d854bb0e77edf020b181c5bba13a491a696f97cba8564156a55480daae1fc9da4ff1b74381d129c3c1e52271e026cc8b3ce11868847fd50d15ce54b6e78cbec2a51c49f93e2115f42e591c9e1872612570e176e1c3a540d154693278bb2d9f1990a20551cf29faa5795643464c7c498e7b1182af65577f1beac49ed646386446a39ac439ac4b2b6b3aa79caf247b886514c57c447c8edc4f173d9a7cb2d4667d4be74c128f1cdaa9a6e538f602548e9df73e8b0165b54cfebf8435e55885db57f0afe53aa23520fb62840e1651620fd867c6bd7e0e5bc1d24c2605fe7c68dc8b54cd16d7b8823d909e5b61951864fc1117e3a39535ea9321597a49d4dd062836432a7ea7d1a30be2ccc9b15d3d035af3a379a2f4efde1980185fa923bc2f0071be528bf06e0062fb0a4370bb81c0f52726416c0281e74f3c82d8050297fe043ba5ef6dfe2f2690f3e87f3ef0f0baf8afdad2694e9b701f58a952051d4b7f94a0a1902468b77f2a7f08c87c959536636ddfe815267369e2445d13829cbeb5f9964b685f4969a7c5266e080a535192bb9b4017c8fff62b7ed271ad871e73ad0c80871a2bbb5214431b03ce34797909a036e60ecd3aba1be450dd88922214c42fe4b426c788a2773f7b82c89fee86d455c0eb7e3a1b3e20063db220c58740752fcf11328d0b7bd807dcfb99369771cc95b5903c7d276919dd7692db18dd671122e42c328a8bb1a51e3cdc7c74b8ae4918aa6e2cff9e1c1e1cd95676e353be7ecea809698a6fce80ed3197db0b23c1ee9dcb8901d0c5dc5e31d8682275840c7dfda286afbe76c372ba7152655a46cfd16d4576425f22c3a827d3d90e3662ce07812e9e1f170434c5d8cc588045e6b31dbdf90fa1790758f4343ab0a9acb0cd05d7c1f2dc1ccaf823c506a984c52a6f3fd8ec78edc49ebd33534f887c5fb0824ad075fe02b26a58019a7245d4712b0d09f9db5c82cd3ec35190da707b3c63fd3fc02d0ad09cdb65cd870bddbe04002e6f384bf91c4de734a391ad1bc1b89b0b0ade52d99ec5035d10da734a6f59983e5ed9cfe7d1c5a7d34ea4d27ab4e77bd8f38ad337ca83fc2238b2903e07855feb48a6b277a46290882085f81b728cac6e69febb1b10ecd829979e66ff6b96a52de270d4c7880144755df8a85a665f946754ea54cbbe89ca8217e794a70c8895c1354e7258df5ea050aab4c925dced3d42aeea5f15adb190649b8e069f4c63c5fc30ebb3df0c0e1b42805a20aab4e77b8329fa28f82ac10faeb9deb1fc767e867c2321836eec9141d5280f8e9b9ba133acbc202201ad352aba4700f7d6148b0f67c2feebeac3e9242f4c39494ad1fda41dbfd68d406ee09b83a17ca7b5a21413320822554dfa373ea344ad0c073c03b96ce30e85911682bb66881097714e40570c505490d72df1cbe6a92ac7a4119794aa39aef703c1351af99e8555650f4c7d64e3d08c1b397c5029d34dca05e26074404e2d63c027dc8068326349bc10d32ed5ca2788d320ada37c040ae0bf5ae41930f091bcc501fa4a10a4fccb09064d8d7374301886be3002ef586acb1fa1229146060ca40174077cdce55ef481ed93af48bf7a62628b54648bec7e60e459e4c18d6af402596bbe8057cd19f24c13c6622cf29c5d65212c806a7052551dc9decaa4afaf48eb7596695e619118d72a062768124d4fa6e58b91359b7d640cf1b25ab5f973902304bee8a9af2ca1339462afe681a7ca0659432f61ba44b17d0a004d576436eb7d5ac3f4dd66ca25cfee9f7b34c0d1eedf1a230f1da69740c749f1b278e01c10a1b1d5a51a2c0232744ad6c21f2271285c22312f4eb5d446c7a4f2bfd42be9a5a349cf4eb9ce687a08b4d18f005d27d2a40df2c7f6d8f98a6e2f9d22b27c00cb93f6ba05037fb565faac8b837f497de0cb92609ed22ded175b953ec75a91432924f2a1449d6897b94188483700540551935701cdb80080770b4e393b9843d3682702a198664c42f8154ff7d8592e92709a14b32ee557ec8567b020365d4a36ed36c18342c2ce80660e1328bdd5baf3d4a12731c1fb2a7b7267fd44e72bdb0c14bc30b6a90d1a86d24f789c75fb3dd3a50f1c964dfd27318a8f2d2e9059e7720cec1dbe4260c2605ce20bf6bf921450563215419b2f4efdd5fd94d922afc1b2d0e8a0d964d82de0bc546587003459fa53bee56b203ad8a1013f822ba7590929e8bbffc1443b2e4567b50755861b615d8d97d9f171bbedf59cd0c74eb1c950a297f54a8d143c23709d240e3af638d3450a86366d3f96f7a23d08baf8d3e3b908a0d7749a0b3c2096a6a7c21e1462671a782dd4f4ac2b569e7596a9dcb69aed8193744ca0c0713abf6019f4f68358487d0588afe080699ff98af0b6b4df916a957481fc0730bd7272eaefe33a4f590994546ba3751e083fe694f3ed67018c40e4630408fa767a60f381a67d67856722d36bc859a07528fbfcd8e815161b6634fb9dc34e162becdafeb4f98457a45d18c962197db3750af5bdbbd43cf9039a7a20e57fb76e6e9a84e0e447cfcd2cc2c7dfab264091147f6c52f7d9e6e23b43477fe4d5ff12a99ddc1c6103b9e8a5ef8009746dd103f409bb584808ae95caa64371d080df1e861ae59e86793c94ae68962df266a5de0f90e4051288d2af4d5d66c797765beb6cebd07767ec904af8c2bacd404af717442ccce26276445620b91cc0a335e522eb80dd3c693d346a3614a656786d62a2b010ec39e6ca93a67c1c24b9082da7284112387293cb300109858c84124c8972c99fc6530485baa2c83e1c266f4512fce576517801a1f3f39cc4fa5d01cdd884f6c7e832e86237818da28b98d82cef95156b6e14a6f8a1c92a048c532bd4017540e0f9992666993a328c223d5779970d40d931f9fb7c59081aa1fe9f4a2323a9d013b1af540911baba8e3c20a434372ce3d9d8069f7e6aafc178054f3963eea0516e195fba6f31dfedb6135a56878b1d7424664942efd410e2e3709af42be398b83cd1a22ea8f219c56c6d5831ed5a2b0df949e351a4f47119e0aa80a60cdc7d62d7ad0feb32425ecde7b93d9df731ea034013eba6c6294b005b43b2807c326b26b590e11a207a3822a205dc30b481cfd48fcc02caff8635850aa81301cc210d9d63ed1ca253fb1071869505c5981c5596b58406bdf0d5332f42c7c4086d20ee11e48fecdc3055b76ec2cee9d15ee9af6955b1b6c9d74dffcfa1d6c5025b3743009d872247f90ad681c4b14d1811e4f573a38c43f6ebdebb62a6b1f371fb7993ef791ce0da59fafcdc06d413afe665d05b4617ffef2a666e84056ade19374a2e8cbb6c7e3c453f1c2292978e0e79d12fa8582d4485ff35523424f60372f1712b4900f201125e04c15f2f23d340f39da52d120e3c4427241501ff2cd0b0123424a0a74dd6062a603020c04c027e11db52af4270f0841ebbf904428bb3ef2481949cfa154623ecf27c85f05812e8aba2a312af7b13649aa039cf5da27a9cf96b6f8e68ef87e8be50b82e51b2784ce55471b06694c85c96e1325caa9b3535a82b925f9bf959269e13f3f2b30e308fd0c05c7bd006758b0cda9c504ee4c34b78bdf86bd0d3934d4105c07c6056b4128662d99008c9adba8b93c581484f541f02d43ed9be04aa7d5fa17e8b1d1261c18779ccde0b6046e42a959835ea9c0880c6c22a681c3cb4029aa98e72a7013c77a29f9a91b838119d58b9944aed1d51b3641aa01845337a64e319e364bd0dc1f756f8499b45cb4416e91b94f9e57264a083f29b4a11bd6a45b02e476b0cc51ecbb1de460a53ff6e918fc410a15f205182b8f5926e11c9a39b51f56c66d5ecc9a31f3a0751e70f61b85b45ae6edd220cc36ece4283f47ac7f8d0f92fb435b5a2d9b3aea0e8d775b4651d241569af58a988e73e0e4268e222930ea1a6fbff16bd175de279da7c7bb0e443830808ad8df7ae5c83c0bd32206669a1afc40ad9dd9d5536128d6f47b1b96fa50eeca59c91ca7d38d63026ae4f884d4b94fa22e914e1a13be867c13b0179a3bd867ba566f826a7b83a8bbdbe8cd8294507a6ecb4003e9ff2094d88a20b3998a49726881e81a9bae8f2b10599a3c2504361c0036776438d5b1cd27441ba7af1e6bc6e9c6bef1ff6534d5959e49a158ecff3e83a2ea11d422d377d05257b78e8857f449cc2f61a054206f5d21dd58e763c98d18a724ada551f6797af7205a2239c86433502d93f8102635673d012a8174924975c53167ed0a864b215a44e0f724e735218a47c7bcb9a22051405d5240f987138546ce10aa58457bee5abe072048065d2ed51487fd36833778d700b474e0ef2b04d549615c284553e42dfece11d3e4a0b7f823749eb3714bcf92d845a7bd337a8f47564096c41789f4a4be374656ddfee1fe41f8862e30391231a9d05089cf1b8d8c9270a802ff3033f97c6f414e5cbb43f84586f011213cc84f952f54f7baf69d8c6921b89493f756e0176b5c2cdf552dba4a0c7de5773d5ad87bd4c2c0b30eb1805abb04151ffe877916d7ca5e226c925e211e228a2fcbb14a9776bfbd0477152b2ef1983fc6f1f4a8f8b32dcdf42a093b2658b900ed41389f1203a9207c0ea221669313f11039be016118c308c8b4ee128ef236ce4c949a664ef53ca52a31953cf06013aa40d4448a54fbf721721bd6c3d009ba6174df8cdb9c95b5dc8331e4e321f77c26025e2887c6f0e0f0ebe92d42086b2bfd79d33b0db33296c293d506f1589094d9ff29744e8018b3c6814742c38b1f4c0340f8e93714c3ee9c9a3974e07624bdfe0d37006ec0e7c9ffb3020b03fe28b66ab321b79341c19ef25fc787820a1f02e360b9d451e8bd53a547b9c90fb88e186ddd2d5a632eab586893067405786cca8cca9a3fce8534c52cc43497dd5f185585f47aca7f2fa5abd128b72c526297a22df89a880285c43abd37989f6ef78d429e68eaf6ebccf3f664dae0c69b3432e2094949072594a41f3c64044065e8bc2322417cd8cae1ebc64cce769d8b39e032e598c86b7f2f90253c42b2c0bb072d2c20a487e350ad3e90499d7912ef6454d076dcc45dcb15b24cbab72227ef6e802f472745867c1e0119f11dbfa5a9598dba02ed69c154c88a3999d5a44af4d8a15dc35dc98ab8149662ec68440057269784ea1d209916c42ff667847123ccfea169d28a0673ab29b3770998dcc9a1b7f693d0d6428c9778eb05bf3521b674813a5c9281551200320681223d94b280522f7def3c9a6dbabc0b1802cc9ff580e808de4d543c2bc5b98d135a9d4ce10719ad6112f1184712905c9ae80214740bf12c97e5706de08269b763485b1f09de7086c8e44af1442b09964271d890dad7201f3710c1de3aa899f219604f01d8fed755aa93ed25d8e25e65147d25b50166e321aebdf04aa76232c05b679b81e18ac8689f318dd10af4bb957b5836647a31ee86ef5571ca5c50b261cea414d5704e809b18019030388f5ed990d488b8676f4b6d759b8c8a5bb9f91ce897feefb2be4bcefa1e87caad73a9135488e22818bc9cc998111e4b903074d713742e02faaa3ec5b2ef7e3e8243faa691ce6ed0eb16848ff2e0d2869f777bed1b8dce54b52935814096bf1766ebf5624db83f5240ebba451cfe8ac99b575afdc7a8afdc8513033bca6f1a19d848c401507e9d22fe27a58445cf443037672f7e13cee0fb650d52003aeb1b909f71a387ef271b5a501603cc838d9243e80fc87dac6401658aeb46c00804176c7d2c600d5ba7e1089cd83ce814188ba9ee0858692ed01ce13d5b4992188792019955a27a180f56245c8976a6fc750cb1cf3fb3c42c4356b82e6e5436a80688ff1a3b7c114146a7fe69d2e02122404211ccca9ee3305e4a0bb7dd5db77269b65d5a195bf5f03f6643c7ee9b2120cfdfe517a1f01e48d0cc8549f05046609e5068e74494d0538520ca4a9a774d1d4a3e4900e713c5a291725f13345299447beb13ef3d07ef64ee6673255abb74ba794beb78cc306369c5dd82674ce88b3c64431320c0210df07a954a3030dc3330d1b64b9ae3d87d5ac561e90aeb928cd840638eba5380605eccb89706604d04f47825968409f9c8467a0027e7410cd8692c0fc340cf62105fd520f6a224ad7b19781b703ff9b7922c5fe71c6fcb84527336fbcea7301733eca8c2667ceff68fc24ad3b08f5d652c526384a1a4ff410336986140311fff2819b1c5f445b47df8fea8d89ab6438a7c9f767e3d774bf19f86e680f540fe37f31259aebf414b7a802fcc6199b4e906ff43f95e14a99d4f0088f0d7379db0f1667d0cef595a32403b25fe673cfeaab4d890fd5d675f4a38c22594392d20b3dd77de3a4f12272ff2d7cd13ab5034ef5de8bec46a89a960ff4fa65dada276b43fdb7496ca0f16eb8d037d9b87ef4c0f6c96ade73c34a9ff3e9bfb083be89f5efaa90b5bd631dce01fce11f7508f9a7e4f7e1c1e83defc005777a5a95408ead1330bbcc1bf05d43fd3fc6ee77f293e152f396f42382c1649eacd1f377f1c01d1f12fc179766f46eeeeae00fdbd45cab5adbd334e651c46c1cffbbe8e8d5650cba9ee2c2fba2d32eab5e37c3bdb77febe1f787152d1a54dd39ebec36569e8642bfff2b26a833f3205cc7881851e2d434d8bc4f1b18bec9f34c7718ddd787d5f98584acbf80d5d7a85f6da9addc5c9e371d9f07f673b51ab1b69ac3aa1325a217ae6f7a96e6461a93751e2985e05fa3cb5f5ca2242443e94f4a3d5fb9aecfdb0383c91834e177247cd497d993a60e53e674a431e84ab6215fdfd9600401991c4c9bd7d464ebcf84a8cda84c7b9ac0243ad3607af91a04c4b692b8ac1074ffbc43db507d55482222fa4059ac3608b47642ce67e4fae8b37e25efbfccbc8b585124cb33d6ed43b42c7373526613acaeb757c43407af6b3bcec9e85d61697336ee9b80f507a9695fc0e6645def2112d3cb6fba245aecad09bae39ca531e16ae5a86f62694777279fcf88345e8dbe75c9ab681aef5d62668c8ab512e36e314e3dfd5ccdebfe09077d6968ed677f95d7b2782e12bdaf25002c70902d5927caac8abecc7e7df4b7a3cd23beef00e684a14fad5866f5ec777d97c9b010ff8969cec8de2effcc5fd413aff6a6c6a416d07fb2cb9de7c36f2b713b0be2b0f0b9e8762f42b48cad4df66ff79e6e5a8ac9071b3c6436affac6bba83a46e3ccccf6db6de3d52102c3d0d173af9f954fbb6279b6e03ae8ac0d09e1ee412c691c71356ca6ba948ce6b3bea90f99f7eb8eccb4cdadbe592c65d6eb18a04f662364baaea721c438e745d7d136c322747e7e33629f1db4b28251067981dc2a267004bac92268ebe8bd9371aae57e5e731e9c338acb54359ffdc5130941f6832eb2103f12adb839a58c4bc16985b9d0fc76b48fe04f77e3663baf4ff710d394e05647786ba6fa37c7193f24df57beb38c288e9b3a6021fc7a4ea56d72b8f80b2ec87e8b2285f2c8ffc614b0a632d76cba2faeff6ae86679f2e5c2b8c916e19e9ef28e5938bca3e5b2aaf1ee46e9d49f6b1470eb9fc013ac0194b0cd67bcca7dead9d0890d759686e8e90d75dbca6c4c826fea97d1cb44f4d276801006e7d0221f67cc2aeae45438071bc76279e39dd45fd975ef7a366279fe3170cd242b2daf55d413ca3832fd27a1fae2eac7183f03eabd5ac6024a7196e398c69a5acf55011970e6872c5cc1b84fae469d08eda924477a0a29232e9e516afa904900acf118d5f115d23e6070df992dd4be9e25f40f81ba36ecb5be6d47fded1fb5a74e5a6fdbff69f3fabebb66d9b2a7c1c1df0c9737e55f1d89f1a365d348fef7220b36547e5a912f9b05a212060272a460f300592c450facdff39325f049cd4cabc38984dd525b7540e3c3a484432be1fe3a094b57358a2c6333dc2f277b6f2be32df96debfe86dbffafd2c386ce799ca4380c64263dd76e3a0787434357a1b944a41211fb3e3c0b3fde4e201c133da9ff94499a11f60e9f391b9506bff755d9c713f8d2a021b8f1ec97cb8677a3ced64bbb6885d56154e55956a5ea036a435f85f5abe85ae4bf5127e048ed31145f66e369db17cbe819e9f7bb78aac99b333d18d72e7a1ef0dbd6051a550f4f549135192ce7ec5db5c6276cf2d3473027055bb0abbe416bfc1f90e6688a5d59ca866456c835f3257dbff2df00563dbf3a4c413d2a18c7d4bc0b4b8a04e62a2d94af06948389aa02b9cb45086f8e87ca3cf8faf3bb535223aec58e366e8f0cf525a298623ecde0eb8adbc14bb86f2904511eb62f10dc4b94bbcdc00f582f6fff436197632feb68f93d74d3b83bfe13933136ae908ab6927df8881b0267455e7e7ef55a961c4ca6d0fdae1144c815ae280c6fbd49eb16884146b5cbe5ea618993226a6a5f6c2633d00b0d40d3812fd2a29113eaebbe7d01e964ada8ecacaa22e6e4d3cc4dbfab4bc39ac33e3d0fbaf21100b144423f1089988d099490ec5dd0e909010f2f0451bb2260b029a1975e89bffa2abca5f4b0c3d298e50010b9e365cba9991ac8d1c02c3e261d0898b3e90f6beb72463f62a0c5d92eb6eb2c0b7a647ff058e80ea3e71ed9f4e0c0f7feab391837deceb1ed5da795f7826404b857dbc2cfa03f1f6a0555f3adffe2fe9fc61f1a53f949ba02b4fcd35bb877c723decaca5b97374bf78a7a360064ffd96887c708e5d5b06e821219c7b513c5a9821e582c82e8e4504d44818879827e3c90397fa5a097302ad796596ac7f9a9699a032cae443ff7707a9eda8a8404e915d20e5f8e4246d044382b142a9a3f349eabf6747f143d6c6dc5e64d776145c1f60783b86548694d9ba13c38ff4f192a3f6bc966d95ad074a13395c6d105199d7ec4c3cb99c549598fb4d7ec58c320915346e883601ea1ccf595e334c0c205f6961bd858f6ac4c97129bd4356862c0d5d91807c82a6bf431597f55fa1bceb190801c8df3e0f541d91729f2ad9dd396e6f33f3df0009792ad42c244b0f6b7539ec40877e99600f5b97688a6fec7d2aefb134398357f38e4d369bf6ea0063f8a301c97571edef728e6ac26065fbafd390a301aba7dbe6970ffe3cb22bea5e1c25fd593367ed45c60841feff46f46c899574c7e08c12446cbfd0f8419c2493070aec385478b4e04925f6ddd2464206e7dc217035f288adcf833aa0c9206b1de08ebdefab3a5d7e6d7ecadba3e666800d425356aedbaff3795b4b3fa7566fb66d0fc328b9bd6cf63ca5d72adcc165fba834e7af31383389945370b7af88b4678703bcbd9cd189775281810f098779fe45a2a702c2025caadff5d3ecce22c385c1a1f5e964a53d86bda145e645502d1c6a9aa7e465f90b6dd74f57c01c2dcc50ee376936c7c52fb8abbbc7d59fa4b9cfcf3266e3e3a91966d81aeec08e9b284a541ec8819984e53dbf1c51f147a0d7a4e90e9088b6c910aec981aea1c73935d49c53612137ec2cfe13cf9fa3cea9cba35618c69d5031cc59a119117cc3636e68b59ee908d28df9187a658069d59d5556528827ab1ed3956203ca61c1db538304519c50057495a8cb42876ac08656cebbc38dbaa1af783f781708af66421a9a306afd62d0fd5c3fae44ec1e96bfc684b95d89a9a74c0ec238549c06803e5f8cc207ceb35d497a7f97d31d51bc105ccd028a65be0e3316e7017d977f361c703ea857407b7e05be9d065dadf5ca564d03a5f407139bca640cc518271c70f9f403e082331c53753dbe83c34fe370a91c3e2b23fa24765f1fd46d9d126266de3bd189684f8ab61566798be173970de6f59b2794f72e1c67b692bcdb62433330ba6282399404214a7705b7a1b98eb98794d99d20e0022d656d8fba96aaa51bbbcde5395bf0dc2185ffebc81ed2d68b5f4675b1f448a6b87b9d4d2f1a31cb231255efc2039fa5a70831a2bf4034b9b559dd98031fc1469b62e4b7e87c2912a85e5754eef06560cfca0f63fa41adf686939b71695c141b1ef080a0cb8e60e3a877ddecd4f2ae15bba28df06c6c2e475b744daf99c2960598bd7fe34ecc5c1c7539e41540d251176dfcd24fee778c3c1e61ac0aade24946175729a86408303613b37b3531212f198e1dee091146e1e8ec52aff21e3b3565995d95ecf73df10e46225682d9dfd2d448a4074e885551b8ac93184c39a7bac137fe6d5619aa55c805083a07137f342829e184b4532df4a7a03845e55557bd6eaed39451e4b24f7c69cf692bbd3ef935f48202c76a612d198777a3f34fd402ced0c3c191b66f2f7cfdf57eb7270a0b7abf16bb684c00840bba04c135e6ff31e94a3cfaf1dd0711e412fcfcfdfdb4387dbb523403aa14131f7f2d0014241ea42ec0d2c7f83a694709ca2fba107d300ae3cff3f728fad5cd383e3cef91d9e13ff330c41274c0ef78f64e94d8ace18f3581fb1e5611dbf4e422bb245afb863e88efe633ce786973af7913d5348f76934c6ce145de99311a8e2bf1f81c472e7256d876c041858227e93288fe0c661a65d440df1bba78014b1d9a8067fc3c58fd38a1396b6568848e0a2a3c061ef40f595e37a0d05bfa83d8f0ae8445da3773ff35f6adc860a380ffa86208343affd0ca2fc170fae253d03745fbb76067f027645ee86584ba3a3cf734f5214e1a2970f86746c1de1c57c3684c190a68a8921a0c7920112b8a951281861b69810023aab8450a06f1bbea44a5e003d5b3d2d3b4740419f157134a0a7666006eab0630284ed2a944906786d0664a0c6ba9280988fa2994480575b101372b8231588f128ca46017a3607b022073b526058a7c2cc04d06763000b6dd429098c712dcc4804dd2c979b6d89c1adf6db52d466966d27d8b05eabd55aead5cc515bbc349ea846871c2cfd9ea01e2b6be03d457c689383f709fbb84414e249c053391144a7c00f6585109e809e9509413c0d785a2a08e10ceca99410e213d0c7920248e7408f4a85219c053e2c11417606f4585a006492a764209a07c2e553c29190c64e66c0829f2164474d0255d812933e1d7aaa18a34f3b2b165dfd6a82cdd1d394c1501579d494350698563f6e19be01031dd3af60d4a1e924f2469a5b7286fdb14b9d707ddc202c39a42745328c3f2e9ecac899c9b45903d5fb758ded868a5a2d34cec1058b3258f455f8f5d28e5e2fcf305452eb184a1b16e2704b10e3709b498054dfb4bc44710acbf439ffd11488870143a135669e9a7c4ddd4fc2e6401729ac4e8ba4967c367c6ea7a901f6af68b7a3c44dffbb5d3573dc2f09bd1faff2e661e38ecbd879fcf658c7bedfdf296e56ab9c1b1c3aadb3306ff107e0335108f2b17ec44e9ef81e4a276fed60ee8c39ba1a95708414345db452b1d6ce9aa8ad3ea2f8724989aa05b838da5cb03d720613dfa679905931c257d9f327a81281c1898ae88a262ab11a7e35f34e8ee0e69f1a0692138cd8a58a69502861f5acfdd95e587c577d9eb0be33a53e2bf7dcdabc6656aaec94d907f54d8b2ed711b5798fa26359ec421bafa30bb651a280ca7e771048051e16c79d0008d9ef7a2f826126d6438f4ca120041c1c550a1918b59716d284cc1801e672f4bafadc255bb00e6b620e1c9e96817c022a5a87cf3809369a54b4dfb751bd53affc3277bc71175d7616e668621ee2f0096081a1989d5abb2511173cdbbc51bea9032fe044e8233443ebcebc9c65c5d858b43b4d4d81537c363fa82ebedd074fbfc3c2e2eb6b1768f76138e4153a74720e9a0b0a75510cd0770d3261082900d4021ddaacae993331c7c961db3b8027a4c946af44cc24eeb440ae452fe53edb53b4e5f084242cf6013d6fa2067ce1df8e7d8aa84a632a94b9498707c7ebf2757da1b8bee0bcd61c531376092d9bdfa5be52ec2d9d3e8fcee72439f058c2da106e933c1a044c46a6898db05fc6c2d1175d9a12454f723eadd93c5710c58463e9c813da944d044454e4057c6a6dc539e6ad3c9e295d5cc1ea7e3b188218be9194c4852bc4f14a3f0d46b12304dd6291c116daf6c226ad7504ab5d841fa3edc390b4761bfc9cf31fc86ad0fc45e27b73379f206086fb9a36331a26448ead0f04a3118df726c6d4e75011afbd88c0706c8c57113de19c427f816dabd7512405e0a0d28f6d394e4f55099afd4276cf886ce98d24303fed656eb4f26cd306ad47d046d9b13d1b9166b6101e7f8790fb5eb41d0434fc0134f398e46b1c2d0c6c782d3de05fc26ae87be4343790d5edc9b0f62649f11539740ece8dd8cc4a2411e83fcb926bbff64c40f4142632b71c89fd1c0d5c62246517895897f8f25e99c8834bf95e9825e68299621a7941c1ea7e630e1da0052fa04da6037b049428c7e304f01f1ea0e752a9b0cb9302d53a34250c7f5f96b9ab1cf5ade30eb70ca885cc57307fa13ed2dff821ab7dd2b91a171b97f62c42e8b593aad3eddc08b77c52782cd59ce2639665130aa82fb7e349e459d37b2e76f36d404813b3a37ab2b3039d7de25baf9dc2cc51335e416c53e0ee916f1a664877660b6de14c757e5b2e519ed0dfc1b3eb5c8050877664deaeeb42b776a11b01fb857d71b716ee9567012dce698568d9e3682f98b897233d13d821b0457795400da99947ee1f3543c111eab32a2910b73f605eed85d6c041e5529c3d3f246a679c37cfc240331471956c481c4323e3f7f2bd39ade2ac5f611175738af7ce3808336671c7688409467cab698734ab340ebca3d62f3dd75f6394ce116098cdda5ae6c017c5e13db5b8f906b40ef2ff29f3cb64de1d4d14024d88894c72986233d05419c89a67dcbf459a30676a6d2bcd9e0e4fcefcbde43ac1313e8ff17f1403d7e71e4c9702b1befbf0f0cecedc8d0851b286a84fedc5a2d6959feba5af756a76902618cadaa98c9dec270603bcb05d38d0d5eaf0d2c2dce21b09179fa707f230bd33c51eb5d3b21520cdb0da08665cd662b23cdac4bd3d31f1fdd28725adc72f1570a21c68bddc63a87cbaf9a923b74fb843984600347179a0941dbeb3dc39840b7d09be738bf629286def56d4344ba4d39aec16db4892b5e3b1e200c7fa2a46054fa881870195c2b4694367672964876d9f735566d7b6f34e72c0381e12df8c064ce3c36836b160deccd062039440235a7cc05a719fb1f8c04051fd033aab6c2b8699a0abe96be46dd6bcd4b1cbf99dfb51748f546f1215f0b77007197105fa073526ec63d6a4428e596ee4ef73a4b6fe68be32c9b28e3ad9211210bb1d3489fe51de11ab1d8abf93aac4e47e747f0fa5ee8ebeaac18a8492b2f50a9b66bd75560752652a3f699dc3093e8f40a7aea75414140f840dc89246f1a4349e8b2f236a514e24929f1e32a026e9e02775e7c64a098e366566871c51cea9cb2d51a122ce05d06395af4035db967d73d910e5b46e3de1a844d1e7a518db4d9f430367ba2c75e49b210b70bfce160f67e52cd15396c1848bef1ce55d1dd96355be6dc3dbabccf61b307fb00bd696b9865e3ae978a8fa0fa545b1df3fff9767d54aa1de013fe959b6f08a62b5848c91e04d2550363487910af158f980b2fb2297638a64f89a73f9730dfa9ea3643ae14ddfca04ef726b0aa24cfce08f00e9958740b77868b736476e75eefc22289431ef1eb5c51697aa7cef146f8e71b6f034666e9e782a261dc70c1ed96e018e0fd3454725121030a423e94d5383c939c88a2546d085e9e64a4af2c61bc5c1852428753b1c8f04830c8e71b3ee68b7e85b2e457335c2c2f1ae94ec2973c17cc6a78d5b44a89b0e7e966c40ab5e23fb5ba53cc0ee05a3815ec87dd5365b2c2d6a4d271f9f2c35c085da64e6548be68db689860b0e1297238b890802429ac7ae1fcc01b3801f46813a94bbd67cbfceb435b12de3099663faa0c9b34c76375b067f2b2fdcddab5dd42dcbc544665daaefb393f3cf5b828a3ef5bcdccf326d585ce45e0bc38f6208eaa0f94a498529e7b13a97c3abacd4a9d4a242d81af67f4594fbe0de4a78449c62202a4778d1fe1a0c1d272eda152aba6ecd4ae616e53d82fec4d5612da4afd8621f4ccece0ce630df788c65554783beb98a7acc5811e60f96e72bfef3d7bd33835a43bffddfb7b8a9eeadb195e17028ab07c6415df80994501743064c40a165a464033d633c58d3e544ef10f3fd78a23e1b4cb71f04e1ded47d4e152920077f672e2782b0e213404621ebcb83eb4441eddd162266c7b472901e0530bef474be8a654c2e538d01abf0e12615295e6ea720cb0780bad8fb732f44c8ee600e89b92996b5c0fd11f33ebbd62e1d155dbed0929b870283c1de832d537dd6f8b2987e7a1c9781d67300406ef30818ce562faadd79b266eb68fb5ea22e3839da1582fdca2a7bcb74f530f4fcb5e8c1eb992f450011506d8076910bc98e4d69de4174b36786b3c3ea19166fa2f561f443fb25ee025e3171a4b75cfb4da8238903697fc62e22f307f0d1ee70e6f58237c497e60d0e0486ee3bbbe335d4e19e0594439a0f220138983d2d8550eccc0ab62baafd84ae3f19f8dee9e5babfafb1ba5a8f3c0bff2b19c54ef6d87e38ccb1e71e879e2d617ea7314f8cfb1c859453c2970029f5325e243e0bf86e4d88674b97b424d241c01fb87db253120703fec6769a8855060ab44bbb1356542100903e5cae51d20941053d61f0c4c1221a24dc89ab2b0902491828d72cef485889820078f278a146290e04d5d5c40113871468907726afa824042061bc58bba423910a0ae2c0e983859a48389057d613040c690733e896a48a823078ea886a794f93800ce0ad9f07f9c9ea850ed994323141cf0e0dd5aaec8dd7a7030dc7ca9204d9de5624dde549f0434f878897368e6faccc8b556d2347af950b6035178eb4ed33db7ae87c6fe79b76eceb97d860194edf1c8d74c4d0c587cb88691b24ee2c61407bb2d46b3897478402737c9d3b5374fe27654386bf677c4f454e001debdcaeab0293e8fa9cca02a9b0e3b7b8e09941ee977a7d77871e82ccd354eff06aef3e7cd2e8c3517f3fd836430f82b39f21e663917d3b56d65c77c0b57510563f90e1e49c3940da79c0122546c70a7f64dc3d6ba4554cc297f19b03c4709b93416f9f49843d134cc0b86cc59847de7eccce5a1a83326097469b0d8697f9f4c7b6f7e02b2b0e72dd53efd892e7c3c3d9738f15274fc36fa070b2774a8733f8b7ae7f0f2ff93c3ef16924d345c97e549ffb9e99aaadf1e2d6f31a01d6881c7a961779bc109f39b3dbe5c4c068c9b03fec97afc6bcfbab48f416f4690b9859aa5c791dd58c648443b5bfc9cfd06b81f68e881096d5004ba2c59045368baeb2de8706d4423740fe927af8d54bd793b021ef82613e0f34894ec1bf3e72a1c972b24f1f10c34f9976ef254383178c3c147ba2f1a847d0a5c92692648ea97485a1e22de4b0916d18ddc129d3a9382f42ea61c20f6523b2cbfe6887ea2ea3cdf3759328fc955a65ca960e44936471b58cb541d2c66c8325e4e7241095d172cffff2d0d5629440899f8db6f974afaf3f5f5ec60ce2b66ee4efaf070601731a02a03ce514a8c3a11b1443edd4bc771dacc00a7ea39b08211628a2a5e448da12a5f065030157ed973bad3f2511847b3756689287160c0d942973497cea921311f288c5e3a63478030f268473912f2c402875d43e5d940ca4fcde83f9f55c3ee8845ad4495fef066292dc1d14c73247e9172f0d3aef8fc7f2351ace93d8fdb46152d19d3ae35296d24feced6ba4c7203a6a87406fc78eac8fc5475efe8d15a4c9f90e989cd380c937edb42c5676737992fde2bb0d24ec47f3af51c8a6bf2bc97daa7ace840de05a45ee8fe6912f4397a314e56bd3aafb53785849bc7075a18381cb7096ed739c79968cfa1d60d6335c84342ee7e48f39ad84132fe86c166511daa351cc06d25638f261d7faee99f64554a2c31edb1a31d23d2aa7b33f6f3198300be4dcb6567a5ac2b1ab37c76c5c3ce48bf4d8d674f603a7f55c1d2aab26166c3170806e72354f729a4e2f48a54efa46239995e2193cd98075b27928ca04576ef0505ac44be58929c032d31522fd70679780a4e03d1372e614a90ad35f3f862989af2ec26698116687783078fe0b18fe5ab38a3aa02877c66e50ec4c225edf0897eb48b39880fdb0afade937c1cedc1cd978685937718a4252be1649a716c9522f0c2eb9241a2023554eb7e828523f2488d6d036c637e3048687a9cfb32a9f42b2a02eeff6217504be1977beedf4e7b91dc828b0e11f4827570879a2f9d22465013ce8b7ce6fa667699132d7c0046443707c9af0217bfd504aa1fe3a23a838b7a23410f6ae2e659df94800d540f3ca6472936072497ce0bc66ce6e80f553fbac30a7dfedf25a65c00891b9b6f91333d663613afd0a5b0fb52efb821ea4ecffa60f51c1ad27f40bf1dd224ea0ce0c1b9d8ebe2616daf4813f7299f97c94daf6b8d17d12edace15737c2c5f54fe05f0efd633f471597795f480e3b586e3e1fc6194e1cbe7a0cf63253b9c77f3e6e1af0a889edbe5fdf5fad2f99fe2cc16fd038a0718a77eaed80a2819d6dc5f49fda6c79ee34222dda058d6ef78974fd33195945ad59e725f03c7cc93d530474665a9f77a79f87c927d47e6fbef699ed3eebfe20b0463ffbbcbaf1eeb0fd3299b7f65ef64ff666fef7a796cd85eb7889051e81921da14c59d44ee96e49679e5b77947339e247d29be725e2d1a7af68f9c8251c286b9dd7cfe8d82825872585ddb9d6fa851855d42e17fe0edd197c35329fc29d4f06eaf37cac98afa9b63578c1a669e678fd031b9cedf951b8abfe4328908ab509799730c29fd2ed8dcd5e2b7999fdbf3cde563385fe4a5eaea56924e7cffd699456366c189fa8251e8eb64eec338522f8b52274bc177225d2485d60751f6898dca8cf92c5fd7fbfbc4efe0e70db0f619ddbf8a5ce498947283e4dd640b67b7bebb93b990f60c0d0d497d02d35a9195486fa4a1d3b6c4c30a3deb8fee76d06b020c98a98c07225648fda2717dd33b2ae1b4432e4988cf5a6db228c7fabd2e36d2720d503dc23477d83df0468dac2741f13f1b32b6213ae8e2f0d5bc5e4df1f2f3cea4ccfba03b14e513fe0293da23c3ecdf7b23ab69201c485a2d90c2bf08cc75c66318c937b1d2b40106c47a67590fd011cc68754606c00e1573dea9ca16939281985f698fe6d7477e984a629926c6a56d85f900a00c6de208d0825388fdb3a07c58fda5a3a1b81b9ea3a0423e27195d63e159c4339da7886021e4a2264aa4121fd1a35fc7448d9088b387513040900d4a3d88cad6518ab52d8cbebbb572b6e882a2f1bc4a2519d1507b4929996927281bdfdb000f7b60167fd67ae176ea1b8155a6c9bf6287acb38246d464578ffa371b8bc3f100bbd2adbdbfc6d638ec8c70cbaf579565e6c701fa4fc5cf9c6ba2817b42dc0de062d17a1bbbf61d27d4e794ce457f1440e704dcb76f0f1d39c435dc6b395c294f59d9c195a83f1301b3bec7c7bde72270b9aaa23d9f4c9956f23cc63e2bcc95f6ef126e20440bec25de5fd525651d2bf2e7ae4a11a40d4cf94aacf7f3e0534d3cb8a2943cfe30a9e8f35cfb4a3aab1d7e07b6380105f9d9b681bec6a4231786160901468e6de29d78f93f65d8d044627586d3652e045316f26293c79ec5f2ef8cfcd07b2cb26fe0003320ffcfd4580ec42b7d448535ce41b953d705aaf1b3c998fa20b6cd5f1e088e9349af1c418ecb75e90f1ef4ee14037053ebd8215aed8195abc82e40c0d3609b7b25d4a48903208d209f94c1a91880e64c9824c2a537f410a359dcecb921ae69a1a97c7a8680a8520f9a3c07c6d2bb2d22404df5ad4433e82337b2c5b66536a0320a532783ab1d952d9da2e110c0c18acd1b17bcfe12325585c30ee8758edc6a04328e64f0bac8db6463ff4eab080c081ac39cd8535967fe9fb1bbce6e3b929761ce2dd1290f9ef320d0ce32fcdc87d917e08bb3d90e7689143b87b097a77130ad21b8729e57ae0539893c368f2dd54bc33af43494d0880b106c6188a63a987c6f421382388fad16c74131fc7c4a8934ce241835bcd5de21036648162c95ee63ab1fb6f43056719c2c6734a5d98252cadf52814b650003d3f2319e55803f201f8b46934bee05f4b1848a1bb773316e2a070d2f564f991bd1c6d6513df5814ea25946b603fc94799931ee83d00ea95ba70ac5c0a770fda2714a3229fe7f4a5e99cd0883483c3dba869622aa55ebcdc943819011ece06f272529f44a2bd08949ede06b98587e8e3b92f2b3f0b775e92f12dfe63d7de7bbb3afed85beff0ef635f27d32d4604ce42d23febf093cc059b3b6c571f0b60cba828e2476b13b9a300bb4396c148d9b30d678d41e15960bef85290cfe163b84c9da02713cc95e9daeb0b710d58b6522fb3ec7d687ed58bdce8bcf3c0d727b898a4b17a065c39b727c1274a35d07e5e6b8664165cee3f41bfe3aecc242c176a573cb4ea1165a15cd20bed66418f36a5f70d345ae5810bed526bd44fc3f39b5807caf515315a690b9514e7956a1632d941e33a0c6b603475befcb42330bad693ea053321e8ec5141b09412d1db09f76980aaca30a347ec90b8901ee1351719dfe353f046b8f84e49eee14007b96458bbeb5bf8410232ec7a195e70934c897b5642895c9e52c32507c6306df7a528b99233912689a8f6eacaf29905dc6b6ec154660f0afa2df4a9d217c51d8a3a2a3e0be94570d48263609e484c578f3823996ad3a18acaef79bde829d9c686b00e618d50476124117df67e6f202b5184148e6f313077d338ebd515acc0af79d3c69057e740d0d0478629b456c105c90a44d96e7f73a5439b4e05542338c45e7683d559db680414924b41aaccb4fd1ae25391a21df073ef650891ec23a13f2e48e1406e35931015553bc463a9cc1522c579aeee4dba451649ee72fb7f459baf1d6a1fe21a06462ce4162e99d96a661488d4523ec2c233bcdaa55630f2a7f892f79947888cf08a921db6cd1c232a5d17ba54d1ab800a0a69d9c38b4d491f2d74bdb3884a52219c0d83528a03d81955c45eef0349140627d786638546de53133c8727b785fd37aacd825f857fae0cf29f548ec5218378aeffb841afe6cdcafe887edd762800f20800318e419d9656a0cb85e71a7f9f56c05556cc8d74e97bb79709bfe4cf105858b9d5819a2f01038ea425ef4de0ae4a04021aabc34532ae7f085c91c60e1c270a3dc3408ecc4c6153329e5c4ddda9e833f221e194dcaa8a8ac8a8be5968af2b2c6bd084460070df117f46ca06d4d5f3da95e625d96edb110ddb1e235596e63f0f0c23c00185ee8074b21c3c21a12851434ec74686c07542c44e669f4cb28df080894abbba0a5aa12ef5195046b2bbdb4730813846d982789c9b22f11815be5b94c9911eaa839c00f8023ab7ff29f891822d59a0d47c563206e97bd5a65f8eedc38aa26956f9d397295db5719c7b685b0d3ad215993ee8e000b10eb5355bd43d1316ba37a14503e88e69b53bf7faaeb352b474aac42b701742a5d08e492ce2d298a5f7450f7cdacb90e6ee9e2330d13215c9fdee754d767fc4e3e095c84ff3db70f767e35cf039e2c7961ef26e4b3d1a4629bd0781034f0dd2f9bcec862f8a6b3925e048fb3c13d3bd7c6bc8383fb1bfee7cf8f022276579f859c3bcbec5f6765737ef282eeb269e2cae1f75b708f5a601d5f2698cb1acfe7b2ca356593a3fec6a2084510ad8c9dc5653482f85e1dfe2be8e6103bb44a4e68b9ddf6ec3d9c083174a2b4a6a82734c562c913e3d8a35437b60fa019ad784674698e6d1b2bc0f8a8b32bab82adbfd99cf009f4de54f48e4e879e498b5b43cad41fee012ce7dfa3f76cba6759d4da79ceeb1e61444712fbf4a90affe9be2a5f14ec0621f06d4a9142317eaf3d2f5cf72009563ef9d5346937b796a67153468393a3df70a83549236bc74356e47f6d6a4f16a144833b9f404730ab0252e509c04f7e59ba02d4e3c206925ec8a4f20a6a0f2b5a153768108db5ff09573da469dd4d1fb2f7c630390f331106aaaab834e7178b93a630a9fce166a2caf1db98386507d0714d0b0c1370dede63477463bd421d7b9ababc639b4842d8cebb4ecff81d3edc125b811c9b18d478a5b1a4c2916a724f14e1fd2c7033bfc482bc86f50df95b18f2fc5ee38317ca5447c4e4a2c7ed0c09168ca27b3642f97e69d8d55f3bf4d46f4f700c9911e118abc09eb5b47aee2939926030478170120260e5a2ef65f19ef68916d0f8de10cbb8a7d11d34afcc1ee5d0c2b8de1067bbad9f3b90aa56351ac644ce5cdc796bd6e8c8804d851188090bab02a4530ba8199cb830123c2ea8021efff05a399434405507f164a4024d6846a1f80a4acae01449245b706677324bf41a3c0e258c007a6645e4c9f9765f5d9eaa5ce1ea9b246b2912bc11318b30fcc9c2ecccb141d0213ae497d80edab651bd1a8054bc457ff43566b2cd150ecfa24fd1b787ff47e7450fab0085185827dacf86136bea2ac715134c4dea71aedecf23423f36c57053b819d214ee88391a584cd307cbff5e63db168ab931f40d4dab5096f4d28ecd924a8b6f78aa97df17cd44788ff58920c9b4c92b35d795c20c5fee41956833126f25b68d0f42e68224e195b52f7e35b135ca69aada56e4f7cbbcaffad37e9bfa1cf2cffdacda9875db447c8d5218c137ce695ab187b704fedad9833bb8263f02f9a45bb58fbdbadc69c4ce8b9f94e51177f3fdab8cd20683818c9e02148dc872903a27f22406b0b2cd38345fe9607a3ecbb52dac92e59ba18be084c3635da795e94cf98897ebd6e827121c71b177eace6c30b3470479253a20b4a61d172209856233f17d9ad350f9bbd152e5bf0a7b434231ade6b679bd74668ffaca2509fd4cd3466409218cfebc112eb0c1e52ede466d696b25a80b1ee874bcf7266ecaccdc77bb40f7d283d9827ad23fe92fbf7ad4d9e3767cb6be3fc82f905a5ad3723debcccd537a368e7161dbcc1c8b14f9a2d33c7a03e71fea3d3abf3b01ebd8fe9dd81f8cbd14d5489201f843c6674cac2a465e0881121dc83d288d1f689c2da7a802f8bcd9724c379607ed5a78e37ce5b2101d480e60bd69503661dba1268ae01b1bc2c331c12cbf87c565c31bc0e6393e28770d9afff3ae4a253ce7c0adc97ea2953fb28ee1dd27185bd0d4c79a4565cd9eaf459c431afc48fdc3a1de11a8568ab5bcbac451b5daf7bedfcbe7c2e1d27e65378d48dffa2a881c6a43e4c1a1205ab5ff6f38d6e866ec20fcb642a7526c92df43e9201fa55f9fbddcbb3e2921aadb5e07fa0facb3eabb622355e3039dde93c50c818326664828a413ba57448a132056808793cd067a830cd1069d53da0b96634e22ef4812fc60584f57dc5f2c063f72db2ff404aae0ac816c4ba88754715341864a58af16a6cdbae22fb922584ca9d8026a51c9cb928c77a6b6ae72daa73769ae802859d0224e070ea9dad841fd271f0f2c98ebbd453361f35a009dfb306211e1db66e62e97bcc69a13c8191a8b28cb31635da056f5e09bcc08c131ba720b48da0768288eca29c0a6bccc98c1f42316154059dd77b7ce2a39ad90355d9f1958cc0595f554f2f6c610b41221b4b6db61b7aa64423c8510ac0a0f4a4d905807ffaf42070c9f11278f92fe87f53e92daeffc2358999eb83912eee2875526d045806780e9836b6807f9e3a2d1c1f52cbbfa0b7d1ef9211569014a293af8a4b254df14f3082520d8489bf0cc14870d592d807afd83563333b67a0c96470d7309e334fc5f7f8fe777340cd4f0cf9b3cc91c8561be43a3903814a9373a328fc928215b5fd035d80da4412c3c8e133f59a9672148152b46749d0b312108d3ac4aa381a5ac684aabda8d196b21605d4abbba059753dd8798cab48e3659a7e38eceb724964051ff2c7699838919d9a0fc82fa43eb5da38d84a79f5aca7aee64cff7a9f825d381fce43a7d28af5ecb5c82b7667569c5fda368166a9fb46598def0e87416d83e8676dc15adc6e2f1c9f38fcb8a10c4eab6085333c98e3fc4d83e6e5d865b5e8ff1e440dc4d9f479154a10f2f1aac3a5a1ed1253cdef06ace87f83e0d6facc6bfafff8e9af3dfa9fb4039f9d0da3c04da6f6279aa4f1ae69321cde91e311a6c1436642283d7487dcd8e0271c31d3f0e243b129b187bcc87c3a04a25893909f505f06d5549c83e22b3fb3cc63a7d3b8155920fea9061b000e83a6f62b16cf8c7de0417cf9a97bb5a1879c0285c872a2c298e15a2c3f9c78b594a72c5464e87d9e0aed5e9117553f563b79070c51ba32bf2e5da3807efd3d309f53050eee46a9a7fe76caf00391fe2014fc88e81c7caaf050b1077d2e26b3d71da7421db3b6cef0128cffd23e377ac0bc5d6a844c51936a7e04041afffc41dd09a2bc7041e0db1938ed5b6d20e37871b9fd589168714e4e9624d4a94021904279ceb49daadbd48f613da7314f5de594c6d5da25a751dddc6a651e96b5030d25e39e6b3773d011639735a1ff8218c4c054d7ff51b474327fff1a34b7f2444f6cf0036089fce23534a3b379d9e5981c50b5e04e8c02723c0c8ff1e5c6cc11b1c118c190c5078720f424041430d012a92f2217ad250f4643aa3b3ffe4978b7174f95176894b9daf1ea8f4598271f4e8deb948f8e3663f0c173617f856d70de9c4816d7cfbc3d894e78a95cbf6a64f59d4f96edb4ba8c4b083736de1cff6e52d3dd94eb279fb4128c7625d56d9b536120521677946dd3236cdf62137911b9e13900e38d380e0f9702751b5ebb008c123dc40cd10fb619b1cfbf8963479e981b1fa84c5d966e6db0837f5bcf60f3e7e1cea059ca16a9d260ae05450de74c1821d6b60e30046ed9b9f8924bf449c043be162eccadedfe580d8a2c49e5316f77c081c22055c2dd433458e7abacc4b909127128f4153f2d5ecc3ec6533a864b927ec2360872e884a2dd22d2e3ede21d217f8835ad5528408dcc7240efbaf63547d91682bb45e2e0554557df8d036778d49e020039750d97f2071dd856772290dec405d8bd8ad1306cb0f795fac322988c426b7c5ffc08c22fee378a9503ff83ba5947a8b73ebedeea53feb98e794a8185088995606f50d639f3b944b2ff7b2b3701656fd973c56839e4aef4b99717a2459a3f5484d7dfb02ee6328c5b2fb9344681f26ae848b64c94e08d52b7a0e0a700ea7d391cd96a66e48a7fc65749111c25fd34597f5dbdbbe21faff1b47543ab71364bf648610fd1a987fbabb79532d17bc74d9deb935a61c95ac8b0fe38ce0e8c2b898db68d47180e16d9fb2f992e292dc9bf76582f6a20da48aa4d4d084056994c4dc6765c412f5304653368fcb6057deb89548d6c27220cdff0fa58166548819a5c6e1638425c1eddd239ab1d595a66b85d62594c593e3364cd240e179a73de6c793c9edc5648178646bd0b0d2743502158ddeabaaef9abce1cedc25418703407600d52cfad1e3b0c5758a94fa07bf1ba28e753452ed994b1d570259b54c2323e69223e6d5145d483694b37d42d2af209e71cfe830d4dbcda61498d35bedb9bcb342541c8df9109628d190f3f1e8a55db3fe375d34c19b9edaffd958f9a5270d04766894949797fa958e17a060b5fabfb711df8b301aa9c4f13491c663db4636033d1a9002287211bc45f9ef4e4a6bc7b732890be34a01da706e23701a89a544ea3ca2cf837eb6e0d9b4915d7392d7aa6362743f535042fd161c6de7faef5933ca2acafe37408ff8a222733c26720a6bdea8ce793c6c64c65a8e31fe09fbb551f6fa3a8fdfe22f5c6342ff5d83bf9550d1213b940ef3e36184961a9a38ab7c42ad640ba9799d7ed034909f948252a3eb890a84b3bc1bebb281c7c87fbdc01be25ebb402c94d0b8c0f26340d8c990aba2e241a353de4bf2f9fdbdba0f3d358fc318d5b8fb982f1a1ea4bb05143e191ead9a4781030ccd4d69301bd1b43589943edf2779e7125f9ab64139ef8bc1c0034b2ad04da0da119b9a886b5ae0f7ab932d6a0653ec2ef87405c7948d3ed4f912247c3fb3c6ec834c2b01f4544eca708b101bb5be4f6e19ac180a122f59d9c6c71fd9156563f24807f29ed994da43f9508743c1f5c04b5d76eaafc85ece25cd27ce8323492c56247248c08f44cab03cb7153e8007abe75e28d99cd167d9435b84b9d3959828be0b1d42f21456652dfad0be5d92e0dcd27ee4896e32dfa3cf6c4d7b7c2abadb63245d9816cc46786740003e3f3051c29c933afb8645716ea6b42f7e05e619e5887b57f0fd713dc3e3815ac72a29aaf81df91062a58c91af1c6445bbf10e6f7a7014549c400d27dcaf32e1ff74fe327904f19abce51a752938d14d3db2fbd3f07b57b9440a5c2dbc16dd3b8afbb36e78261768f9d8904b1b3a6363d2b84c5efa85a584d41667830ed51419dd44b13823530bcce916d2cc2c7f4400ffb8a35bb9397c8f7625d5ef8cc36f8838f2c211651d035c2d279deb61a1d9c065f3e4910a07682b2ec68b3cff1543872bb5cd89cd85eb014ba2e0828d793f9fd682b0c1b82b6cf98c4e4cbf95e0c1cbcc2609d2a3123d262c43d132bae9fdfbd311b7565fd468a5e4c21e29ab80f8af2fce824d8a106bb0d70462557ff0ed6d2b09b1cd25759ab52136dfddfb62f64c24112d75707323606f63e1bc15f462847eeb956d5b811ea9d3bec1193475d32dbc220d94e4ea2e443d197dad1a314424af13f359cd48724d35a92e7683d28ecbe31f329cd5110e213c86f63e3c2d3be5898981e377bce85a6a0b96e8ac688f544eb9d4732a695179c0cc3d33a9484ea2055215e24a20bce6f8e2f701b4d481ce0936cf43b2bc30e90e7d52db00ac90d0e1e69379c902f8e7e5298ab16b6c9395f23c5dfb4fa08b238c848fc57a3a2e5595625af343a2333320c5724b719ee2eb517e71871deb0c63b570891a9b3f2f0e521dca9f9d03689a393e2fd19f59367c10600dfff9e4daaeea7129cb23803344eb40d5c788feeaf796957f548aade857a99561047c5d11458d05d2cd9dfa4df874e96314c14ff327494f12b8dcd59d039cbcba55710c198a5308da4014852dfe90bd670c04ab7d8a40a1bc7ea4f5e604f05c1ce2bf04c29760d32b42c43870caf38df1baf013dca3b8fe8e2306aa18cfcb2108d50bab2176b2511d9887ccd26c3ed1d012d13a1752c7dd13964094564388698a520c148d984bd8f54870713eecf011ebfbc9f9de4c60d341e45435204ba80ce4739ee75bd3e11101aae771a93ba35eff9a8223e85050eaf83edd6ff9ef6a7afbcd906752c6e93ef882f81f6a51a28b01682e72b07c6659ff987e9696291cdb85844ddbac273001971fb04383bc616dd6f3e4e2fa87c0b80171d637f597c9732d17a446a2d94e8f324016f28e4f5489829a7467a870890d17e252687accc0d8ac37dc6fd31a0e98f25ca02c04dc5e62236c9c8d7d5d0e302f9fd36d64917f58c9b8b8b05618111e66295a8cf8424f168cf63f69fd1b53e52c726dcd7f9f02a6a90d0d3e123a2f2a9ae1a048db5bc07372d0453c106016504e1ff498c56462b41482e4b6eff4fbdf4dfd6820eaa6e2fba5e7099f373f9428c891f176806b3127633d3565f49ceae6831e462b43978a150782d5b71df266d3b195790eb6b7736ec2a47e43fabf7df68b534d9c35b77f0ab9d238e73a8d2b0008101a58490d5f3397858062a5b6a489f292b3c480feaa6532a0da4475d4d58ef3a404c52ee9bc3e4101a67ecca8639e3f6f347340ff3f590bd3ecf10f020956c8fe69e02cccc0075dbc645565369fae6c98f5eb7ca92bbbb77cef670f87c30044ebfd986d79d621b5bc70d16cc7dfe760fc6b0ba7fde284dce231da503b0ddc1ac7a187ac9902ffd03c737430b3eaee3979fc31379df1c9a101d8030bac07ef44835df829e8f73fe7e0ce6d78e7b24b08f24c6cfe0359430fd2ef1f76d2ba349ac1d6ff937e9895fa81ee4455a7a4371c4ebea3e6027e7eade963f8ce5cda7c0a3c6e23b8264d029f67dcdfac00eafaf043e5dc3b4bd8bfb6cff938fc38b7a869f4f427bd2b53fa7d77a02f202f2a1ca59e400a55bf85bf0c674923cbd2fe68dbd7fff85ee3e917e6cd7ee14dfa7809727073a525832e82ff4db939068a294a24c676f8bb85cf192b4d41c82f7cdcc39ce9d0d1310fb0271de570a9127b06c37c334fbac296abd68715062e3f2f68eaba4e2446fc130dba3dac08839537c9d3d1d61879f8b29f6c13d02097afeb529bd2b7122cb7a8657a87d02cd0937b845dd1c8f93aa9ac80e88ac44f8eaa7f7a56ee928f0a689fb5a5677f3de4a79d5eabbbda829513dae914c2a57e7b962e5cfb3a37efe96908f7524ac9a713581817f2988bed3b95f43c7ee3a27cff17f77c59e65275976e96c69c241323b162932302900f543b851d0a00f46328811f35df91593ccb04572485a39a9e105a56024d23c0c63f2b32852fa72266b47c8729262c15b7aa12251d8ac44c85fcc14ab0f945ceca2301d338f2d2f394d9ffd2d5e412ac5b6c4122e459f5db3bc367d0c7df74bb8e1a3a015eeacb143266fbebddfed9f9e0e2f2e900ef1adbf1cf64701995cc7f42a2b7f139c2d0afc0b3c60ba78fa6a5c96b81e7b6eeea5b7e54824ef2f449a1f7b84547f23737037fad610f4cebbcaf0ae625f84bceacfe49d2644db6470f7c692f6aada926383964648e246793e8ec72dfe92a8994c4f2f3fff4951f8ed50d6b6ab601ce924d3e01537262e8728474051ec5619df9b0203353699edddb6ca3515b78e8eb07c06d475161777154052551d5ea676b552e24b67dc6c02a4eebfac5ef6ca7af2e92a67375165ad352c73ee1d76c4cbebe7f0fa0470d19e0d8dbc1a35117736f40fc135aa99b59f465bb9904119c78371e67c4bafa4f3f75c727b30fa148d1391ac259ef3a5697367283dfc17d272b4d6d5c755a6c6d28e8910a0882956672fed28b04b4727ba65101d59ac67a6479d449c1a0cf7583be216ec8e78b678943313b8a260fa0c895f0b465fb006d9fafb6875bb272ad7059385a653ade132cb10adb86faca25628431b2b1a2e892faf1ac7b677df934be0701e84835e64e846678e2a06f9a9c4b86c793212d3c45f452ed53872251801c7ccb35304db25658f89be05716fd03c04df5961c74684a04cb8333d0964649de06c46174bd72c91a5e2b246270a6c76de6bf1a75cda35a673fc53befb05132c1c5a2c9b70c988513b5afafc2608b49638c7d3c9ff8c40b90afb01a3e3d73ff55155181ca89952951ed65bb24b94656617879c0ca86ba7ef22e4249f008228ce198bd3a9bcc1f24a8cded7c6950278babb4ddda4f5b0c0088fa8bbfafb25ba511a4e2e7e02182332c23466c2f4034bb4544955696ab31216f3acfc31b6cc9eed78f43ff854d20f751ed8dd91e29b079ae941077ef813276e7427754dff4dbf27921896f64d1e14eece36c5470d69f8c3ce31f79bf120888d98d30f82efa74d31adb2c9e05f2c4428f696ebaa8d53e496ebbdc75b585b95d83765e4997bbf907eb7de19b97c83e01b440a5e60613301e0bc187311b1e1a37c1feeace2a00e041ae42a7d513aa8b4aa46af845e8852217c9d868eba8226083cad02befc44b00c3860f26439ff00000000000000000004945b37b6b52a12919294498112a85289cecb534a294999e4c1c1bbb03b7807ef1807ef02088117b00bd20ce00bea0c0288c22149b8cdfb9992a5d4a170125f9fa147c7a5ac3fe1281b4e4ff7a56f061d2e42004e388a9e460a3aebcedc6fc2318a8ce12c8dc88473a94ef814b131578a11c0128e67932d4c33866893120e12f39cc9aa20b21d4ac249fdc786a52bb524120e6ff966df7735557d84d3d56451a141fed7a68d70b80b9d1b751527be16e1f41773bb444db97a36443885be7ec85362c1842680219cd2c44e91b97a2c6d8470f28cb136339234d93608e78c69efadba62df5611080008a7515156432b08712ffde09033358928e9a247cafae014ecc45f5d6ebc10400f4e22a998bf2f9392cd120f4e410531fae2e4a56c9a1d1c553543d260797c56822f001d1cdb3765539afc948f3e0787af9454bef4251c1c848974b9b485104ba4cf057083e3a4dbcb3e1163c50bf2740101d8e06c1bf1c5f2a8b077f91a9cd3d67e4cf69a8409a2c1c12bbb4b4ab76f31a56770509f71f2f7acdd8932389dcc4c2642fef591680c8e9765ff2efb7dc9930060704c8bc9355fed58cce22b8e1ec22fcf6ed76578ae38867c11bf7f32470ab5e2149b216c921c57d119561c5286f28ad91a4ddcb28a53ca7a7b4b48b7922a559c2e4609b118ab909264462a0e5e1636f3f9e76f8e3061062a4e21277ea8b9c8bf163557758a73bea9feb252f512bb298e32a3e737de5a8a936d4a25299710290e31958a7a99bd59551c8531a598442ca536cf8c987ba35588099362847ca238e8e9cfd0ca14b2d7e442711c61717d3bcd36a60d8a93ca9ab17f47ff2b924298f189e3a98d2526a79e45bea030c31387e0a3de2b9dca788e74cce8c429f62a9bb8ed280ba31c3e98c18993ccbf69a44bbc3119d3b19b38f80811a48a67aaeaae234b7758a169e232b78c9995665923c6ac724bc99208a6bd8f3d30c2f8a2a46046268e1a7b497575ea04cd317110424ace28ba620c532f718e1043627d66e9dddf12c7cf16d5139616a4e8332a714e731ff513f79f15cda0c4c14346d83c22a9f28b7e7c648e1a63c6244eff12f2be062b953f97c4298ac8225b847c350f7d7c4c6146240ea3c22531ea73572f2f248e593544375196f3aaf488538acaf4914c88112277c4495ca8aebb6c5fac8d38499651a1fcf3e2a524469c456d5d8a5c418b38a87d9bf0961ba2be4fc00c451c4d68881039ca92dc3711a7499b17736b62b8a4828863e8095effbd9fd962c060312000868fc1586cc130987188536e970aa26e67927220160c688005b08000a21c5886388868361729fe5424a9f1c802028528bb424825b12a4655f3aed27923b25f943f958e2c1268d11586185f742101ab2a306610e2ac412f630c173ff7d43b1ced208e6d29c534adfdc7c7478e423bb4e0c22016411c74ef5b4ce2c9be3505e29ca3e2f86ab80171505a3b2faad4e90bc13f9c4e840ca32e6e336eca0fa7f41149df54d010f19fd187835dfcd2fe19a57b3e1f8c6de1e1a22a215ba4d26e5192d7f6df844cdac379d30919fe4ed522ccd0c3297c53dad018714c7c3b780b1c6b84197938f5fb59886432c9646ac7187c818f8f8f8f1ca57838a89425eb4c657eb99c8e2c535aee7050fd2a5bba7d2d2d3fc30ec724a3399354385dd21483197538f96612daa2da96c44a2b00821c2898418793a4a8922fab37f42937471161c61c4e39e23228716b37b9d691850b9821877325b75417d2a5f90d69e00b31923b20f91762244f33e27036b541c4758d31eedb33e070b09399444f887e97bd8630e30da7bb142ce5bd846594f30733dc700a3a629b5b095d3df2369c4b5e8eca7915f289301bce6357522fa494a3570b63c61ace162a83525fba94c56c861a0ebf13337c5867dccb66a4e1dc337a67a742ab08a1e11483680daaea27865b737cc15c6465402330e30ca7342a2e4f4bcbc4b9be18238b8f8f8f8fcda14c98618643ba9ca5e56f4390997d7ce418230c2365388c8d7a964dd01074cd420a33c870fefe1555f548297bb9c01861e4a831c2b025cc18c3a9846ad658b58be1e013844c3235444d2d0ac3e1eeed2387e990652a301c54d63c9b4dce5da689f9c2219e64b70c994f85d24e0b33bc70168df559ad3235115e17ce5b36ff7b23933a6c2e9c429892531b7553d4f9eeb6709e1cae159254f42e73d585195a38eeffc8cfbbb5513a532bccc8c22188bd8b3d3649857c49146660e12c9a2db4924a8ce4e15de15c2d4ae64dcaa641ba3d98618573f82891ca34fe988adc55e17c959a26c349d6f08b0286171790c10c2a1c7d6ee4a969fd66eb4c7140cbe098318563eeebcf361d1264d2c700238c2e8e91c271b374570e9dd9ac54617881e3e3c3a089c2b9254d74550b9710640ea1704a322a8aec558a8ca3e4858e309c025a7cc1802b1468a1a0cc78c2f1ffc4e26fa6190d1b92010f1c1e04031e17c0c223b7f8820b2d7086138e19fab4269d25b7a199d184539ecb7f272495fc021f1fc50583f1a6b2308309a736a1549e92789d213b630927f912d395b577638f9470cc7f8d131933ed339384838f46ca64b59b415d6906124eb755aa345b3e13761fe130215dec109979f935c2494d16adafa177cdd6229cd6d4a434794426954b09e4e0c20c221c93b2aa52f795958f87805b8648d49032ac5b23866add9d65093d26828470f0cbae8c1bbb2fe27ad1450e475a6cb1a30b9f1184538994326c57d2991ae62820cc00c2a9cf72c6ac3fe922ffea193f382599f3ae5c64e7091531626a1500410e9ee18373888f7c5ed693674a8286193d386d6a4a3199342779a82a0700d8308307673b555182f80842a9af23eb0b2ec0d091a32a2b4b0540906304337670487e428388f3fb93a19a61860e8e193425bb94453428db230b2c3cc4302307c7141d3725fab2b8452b605e988183d3c41e5331d9c4fa8566dce0e47111b45223595874860d4e4ac8bc655a3b5b2b8507336a703e2b5da915e2c80d293438b8c6699a52eb033366705ecd705a6e94503b73860c9290545d44bd2a7ab965ea9d7fcbd56669433f2306a71825310993a1d757d291550a21300306a7122b772a64aabca0bee2d89b46f643a6904b8bae38a5f1acb4a7a29b90702b8eb5f2b591fd4e5b48597148b2f7b9a1b99b6359c5e9bdd484cad590cda8e2dc22938a69a3e22564a9386d2a0d9df6152f64820a837a7ba520dea2292466e54b17df5b6c6d647f8ab3c69837dc59e6eb903f34479523c830c559deb47c826a52ea241e5960e1614c46298e262e6e94b5f0c821234a0a4bba86a79deecd201b8599c3aac3124511ab2b83560829cbb462ccad3357a34e828c501c43e7dc7ca8ac5e9b3bc2e822cb0132407116f318b9d06ba2542ee313072923f385e88f9dc4c8f0c429c9d39727065346278e316da6ec6721ada7bd4d90c189a3ccd9c9244c65ba3375e4f01568b1e3e303b720631327614296ea4b901d7722a589a35b7de8b4969c97d99d828c4c9c4bd457728825cdb46b093230710e19f6bfb4d4f5bcfbf83825c8b8c431cdf7a9c43859e278e222af89511b2124a31207253466918b5c4a8bf603322871d86c8b1fa371c62e963189937649b6592b29a3431992288b66cd160f5353cd95b2fae891613f248b2a2312a794137de4cf851760bcf1c802021188c0c7c70332e0a15500e009199038b557a477f330b9ed3fe218ce4cb5a598e437861d71b6a04677757aa6978c8c469c3389d2dc7a7e3551a2234b01321871ccfecaa36fa63f63361290b18853ccb234135a235cf58a385d16ab0ca5e2d55949c4496cb75f2acd9ca9623ab276a81a6420e2e4675559245abc3f5fc621cea923295ed067a56d168f2c201001356290618873a68acbefcd972c611d59ca931715302aa310879f90436d5c938c98f2228c2386013208d16cf0f810afe14da42371e8d8c111c0418131c0f0820be7c2230b2c3c4e2217640ce22e356f9590b32c85d8616d551b73a5354d8620eaa30b198138c619d16b5a438cff4647627511c3e3e3e3c4a8820c401cdc82f7dce6127b0450ff70484973776c8e8c77ae408baaaaac000872ac0c3f1c539efca55eaf256cd38793c4349b44927a4a862d0b2c3c48d940061f0e7a3e7583fa141f3bede1a05dea42dd58dcb4d80964e8e1d47fb53a5ea6a14254f270bc1fd1d31c9d39568387b3575ace98adf26bd8ef70aa4d0de1f486987e527638a5863fa5276bd5c65c0b136ca9c3215edeec8f9315837c6d0532e870d89afc3f29881f5d77cce19cb77a42e859bd952e8783e52df5bdcce693fb381c24b6cc5b85bc9843a423abc690018773e84cf1e5af367fe575641516c878c3f1ac237d4411a341525420c30d07195bc3c87495f754e6858c369caa5250d92c590855ba0e369c64635aad4d41c498f06c0dc7358d5a72632fbc50c3c142d85cd2943021a46938eac4eca254264bd3211d598b86b3fd9efcb49c61222709ce705ad77c9f62be8eac307c07964086190e17f53c839ae8aff482b1c30b1d596538bacfe78834f94559928e2c0e8393993260c820c3c12e6750b3a5c1e4247d7c1c0a3e3e1cf0f1f1f191a30e05b6d50519633888cd974258559fb3310b1032c470903183527e495fe7f4390821230ca7893967217b49aaf53e3e48f14006180e0a55877c384670710d717217b93d9c347c77ab8d76e4f57012494851f35e3daec9c321e59af7d4ecf170082289906a42fe0ea75db9ac58d2e28fdeed70beb98cfd565264f55e8783b098207b4c252525a7c331f897a6459ee826e4733858cab8d396841c6dbb1c0e7162f655d77138a513295c9c5e549be1709c1459444512da84f60d87bd0f3d312417b5aa1b8e2a496c4812d4f2a56d38c6185478db8961df74361c442cca454c5a2d8bbe8663b9040d97967744d0d57098ef8c2157f469f33c0d670ddbcf3f891a74e368385e9d9ffa7bc6bf8d9fe124ae967fbb429fa8b8190e2b1a2946af4c6e162fc339464ef64a2654da9e0c47b39864ca26154fef6338e99796d8cab51173311c4fbd6e10c9f3322985e19422229f9c8d24eb048673a90fb92a290475a32f1cc2e8d37ad246840bf2c231e4d8b6342162beda8593926215d49998773371e198257ee76f346c24d316ce9ae6fdaab552843069e12031979c24e49c8e98b270fc1775ed6a49f8c9242c1ce4d988b2cd39a964d2158e495a880dafaf309a648593695fd31073aab64a553888b958c959b2d1222a1c44bca84d21e5cb259ac2295a8c16ab4fc64e9014ce59fb7722af39f31d854332ddca4d215ce8160a270b424b742f455e8b9e70f293f226638e8cadc809e7ae4b71e7846c512a6ac241773754da49efa322269c5ef43f834c9bae4eb484938893cdd634620c1329e118e6752164cbd8a550120e4a5f9eb2142f93421012ce26cd23ecf84738d69ccaacae196b77231ce3ef2d836ca43a79110e1ab694ac68cacb13110e2988c9d39ac6c44b433865c5ce1373197684847014dd19fd3d7ff4464138b77686642e13454440388fd709ff51ffe0f0a536474b9a6a4bfbe014d2a4c91bbd4f59dd83433ea54bc491b5de300f8ea6eb2673d9da457607e77bdf90f24d14d1a90ece5bdea18408b9abc91c1c369b0a3f2123268b38388b65dbf7dcdce0a01b49b5e41a75e2b3c1496686a0b43a74c43538685453eb1761971a1a9c4345c8f01541995666704cf2336ad2596a5a0627214f877da53a06c79ac9e7be3f016070b2d013459bc66ef05f711a8f51ab7a62579c47e42f1f1351a14ddc8ab34549e215d166ddc4ac38a4089ae744dc2b45bc8a93a5a0352f47befd10abe23c419549ac3469fbe6542c4269906f6a66549c2c34ce7b828ae8934f71d88e705e67e1329f6c8a53dc589e469313cbe4521c82fe50192f63a3984c8a63d024c247dab9091f8fe26c3ad3e4d36a52e5c2a23869df38dd2cf57d1987e214cb4f244ffcbfcb1814e7dcd3b75fd9f4d4e24f1c84799ec9fa9f4d22ec89c39a478cd519725ac29d380521fb6735227523cc8993c9c92611b1cdf57713a7d011df114dc8d3fc6ae2982af76bf5c4ee9c361327a1ef4e5f50ef1ea2c5c4c92c68b7cbb1b70fed258ea2920599494694b9d71287d90a1a29b7b7bcbc953828d3d7ea5d9bc4859712a791f53b919e3222dc491c2d46ff9ac8b6abdf4ae2dc1b5b62988b359adb489c47c7a995cac98ccd42e2b8a2744df45b7e5ef611077d9a53e405edfb9575c47926bccc468acda26d234ed1249e4adf1a8bb165c449d285520d91b1a5da459c2225d5914dbb2a57ab88a36f5e317d6123dc64137108d9b4a8094b7e326411714ad9ebf2ba86e5f2f51087cf2ceaf4dcf3b457431c840e1574c47dc9e65a889304fbee6e3d53a7a4843888f0e115530a3128930ee2545d952fd56b4a2352411c75f37df2b7ac79480371cceb1a5563bebc1d52409ce452a29d86eb13adfee164992fc5536a42caa9fae15c63b3d92e29532aa97d38786cee9f2062b815950fc73f71257237349ea87b386d9efb9351eec206550fc7df24318fb70611329a87d3e8b1987bc7c242a3783887254d11c3055bbde81d8e16f34eaab7d8e1a06b49b4928a195cabc3692b84ae12caef33458773e6b94e4c26ae4ba27338c9780df17f7f3ecbe1204a64d85d10d93571389f88499a47e56ff57038756a741d194de7e90da71f392d265469bddd704af99ff96e2962d4db703431a9246cb01c9ac486f366cab915f7f6e26b388bd0f03c6542b4ab86d3e8ad37297262d64ec331d204a18486d216848693de1b7df9e76ece339cf623691959974366cd70fe91dc2f275b93b20c0771233dc5af8409950c872423eb565ad3a4ca319c2c3788b8f4aad49f188e9abe534f4d459a260c471323d1fd279c4817184ef2a7a49ac909f1fbc2295dc7fc7ac47409492f1c4d756bc4c85321efc2c93a377b979ec8262e1c436552263129d514dac2499fd0d3a59164ac5a0ba7892592e5c83b69e72c1cb4e889572add289962e124944c31c64247e9f30ae7b9985bedf77e4b2b1cdeae6f364b6b6b5885e3571e5daab45e998e0aa79c22ffb63b7bf2140e12ce7e4e2435a94ae1b43132f7c89bd09689c249848f9c0b214e7a0b140e626d726999587f9f70d0b269bb46ed464ad00947bbf3d851b9f15113ce7ba97266d199bd74261ce3c4eb44093a82382de1b0a72f4cecf4ccb6120c3ea649bea56f004938bf7796cc3a4ac6b83700249c2d68d1117efebada1bc0114ee1e77eb2f5b5c5bc0118e1b05e27cc54122fc1bc0114e1589d79da7e319310de008890085964ea177108877841c9b41314c2218a1c7d49921e310ac2719457aec6538de103c229d478289764ebfb0fce969999236a7235cfa05972e9c1316994e8a682a7e98d07877c9a7a7126c639ede0687ae95c4d4b442c1d9c2f77d7e99219de249783638d0ca252ceedd03e0e8e1db12785bf911bf31b9c6d5683e6f80d1fd9e05ca943553545fb10d5e0d4a6a52f3798c930d3e0a49994568b10e7cd9dc149b5672e53134cd62a8363f049a144c49a78c7e07027247d7a6906008363888a7abe2273579a571c44c52031c278c4d518579cca2e845e92254936d38ab35eaec63c5169426258717453a5c2a90a49ed5fc5f16f4e5d988694f6abe2a4624932d99642487b2ace76ea4b484ff5d8382a4e276f2d56a7539c63255e29318996b229ce9972a9b5b499aa5b8a53ba9021fde424b8498aa35752d24de4d8487114a728bff26abd9a2a8ae21484d871194d11f40bc5496b4ec68a985ca729a03885f8e17eda3e6e2fe513275db252b5a6204f9cc29f249159fe7129a51327d108426be46562a570e23c2ae90ca22bd92c563671369defe956be3f56d1c4794f8ec68e799df23371fe5acd8a21c84b6dc2c421a8fb1041932e71ae98b4ea2de1422559e22072f45c2c66a938aac4d97289aba4e26d34214a9c5ed3ba4dead8b9d0244e9bb4d48d3c9964d492385ad694946f05a152381287512b1a671a76c49038c56897ae33ccc5ec3ee258499de812fbff9575c4e9deb4bec5b6118778ae2d1adcecb265c471542d8eb618d627bb8863f85fcc5caa4e6daa8893de74f11e1ac2344dc4318c6ed01a5143c4b93746df0d3285fb698738e41d93dd9bb92425cd10a714b9c4a62839d495568853693535366a7326a111e29856f6aca2cf4710da204e424b69881934411c47c51192b466796b2c10a770a3a11d59e7336380386e6c4f1396828888b13f1cf5375ad7a678e69bf9e17ca9d33535d29a6ad68783f85bcd242fd137313e1c93589131082d2b33b13d9c367dc4a433f72dc6f570d8a0eff2abd7845ccac321dd6c56f84e3519151e4e5a64d568acc5b8a2bac3f15224913349e6d0a0b2c3d14e7787fc3af55f571d0e2a7294c48ce9afeba2c3b15efdb32ca5a6d53587f3c5d2ee9aa7a55e2e399c75e3493a95539771c5e114f465b9937de9225b7038ce55106a95f53dd57ac3e15433e8cb8d19122d379c34c6ebd31a96369dd586b3faa6932137ac42586c3866b9f0be23228c8cb586930ebd49aea21a4ea9b2f65253ceccd370bc8a49a651576ba341c331b684dbd79469245667386c680b13c3ff2c6e86e3e9b5b7e92cb5f0cb7088e1ec3426fdad3927c32958d85f47b894b2680ca71a9d7017a3293989e1b077d24a7df2e6370cc7482a6bb26b53ac80e1b496f2e88ce06e21fc85a39d6aea0a512e6ee985c3c8ace932c4cfbeb00be70bcf90a4d4feede3c2493566111f6253dacc16ce55c133d79cff0969e1dc6d3f372347285565e1142e58381993db9d8408000b07b1392e3264cbef0811c0154ef19478af1063283f730be68009002104608553d0da7e9a4b82abee5538e6b0f91525fe4d43448553f5ee9a6a10391b9453385d88f2cd65d17b26460a0791f4da8b0af2c25e25479547161028001422a4fc50430f47f32f3d6a4b89dc7b1e0e215ad0a3fb47483e351e8eb12d6e909f75957abbc3d9e4dbba684c4b9a2645a8618753ec2c19555f217f661c1e38ead0c97a28ad28172aa4c3f1c4a9d06255a32d921a7338bd09b5212d6935db500d391c3388477053b61b49b2a3461c0e9bcb8496b7b49a2e92639550030ea794473433eea5fc197ac321e8d0782784db958cbbe11472a578f1e4e85bcad568432927b24a7ee54b2b1b0eaab6b3f4297797f8ab87c161884101ccd70dd458435935b76892942aa4128bd1b2f79fe30be6a2ec0b263b460d359c923c9fddb8a5ca344547560e0fc38b2eaa7078786481050e0fb2c5f1b800161e86fca0461a4e51434352596da3d2588850030d67d7fecdd6a2d16cc267389a98c81aeaff947d6b86736ddaa685344a6ea60c0709329afba414846976831a6438690deac2480cb12be7311ca2dd4f300d633159311c266ca7c6704105a1ac301c9476d59c31472b2e0286e39f4852959a27c94dac0635be70881a5e66c7ec2cecc90bc7d49a2d8a5cc9a52dedc2612e28994276041583900b2793e8e323838fcb7f35b670580fd51cd1926eea57430b875c31e3479e919821a6630747c003879502a4a246164e69538ea83156b84ad9964ed4c0c251429475199542c644c8812d90a78f8f2f76984fd4b8c231e99e04b16c122e449050c30a27257c757e2b28b5b8ab709ecdd4ccb70badaeef126a50e1b4556726f328756415db81a582a35338b9c92a4d7d51eae22e8593758854b247e3cb57f5418d289c248a524926d5e4a00614907a72e977f4a8458eaa04418d279c6a4ce93d4d2a9b9563c70efe628cce61f65d6c4089136a38e1f49a828a9bbc33a26443a8d18453259fb8e9914f82fe76508309c718a4598db80a4b2a4e891a4b30e84b72714a344f574309c7557f134958c80e9c418d249c4be488923fd38ad2b58e3435907012f61a428ceafe358e014618570c4616bb4738c586f4b4d429ba52c6831a46385fa6142469371f21535a50a3088768da75c64b47aba50611ce659b3956567e418d219c544e3d2f7552cf4c4502391c021f1f3584700a7a83b6d2d6a0625c75645d3972d485175d7c7c7cecc03186043e3e2830011b95a8118463b8a04e4f5ab78db77464993a690b31bcd0a1818f1c1e383cc6e8c21d0046165db88e2cc6e8c21df0f1c10552c1186a00e1706915362efab31a3f38c9a48257c49844a5c9560004390c50c3070719844a0c997c64838a8eacd18353e5dc486376571be247a8c183f3f977b9a5d97f89e11d9c5b5f470551a142c9560767139363d4e5ea0d4be6e0a4bb2b5dcca72b5b2e05a6fc8b31b2281ad4c0c1f93f3537669b5f45f906a710a2fd52289b70216b8353aa9c71aeb5a2df6b0d4efa27b3da5cc36953d2e0104be3aca46d3161b9199cf72497c590e2a36f191cec7be47f3df48590522306e70b6bab194995fcba060c4e7a49488c9da437b9c6579c279810d2438520c52db6e0342db8d0028b1c3ac2f8c2930a68b8e224ee16c284c53ab27278f156ca16390a4aa2d18a22efdd4eb8ac31489915883eed5e63b92cc80c035c8c13b8039073a0ccc53881a71cc851b05ad05885bff7a9a5dfae1368a82215a24163549c6b09aa54ac37b3fb92845e5974a4c7052e6d400315ba6b4ccdd61e6a3ec5414694a4bab715adeee3e3e3c350f0f1610846185fd030c549a46cba26a420ced22c85594ebc45d26d6ffff1f1f1f105173bb428e66990e2362d9f3313f3a40ca2310aacd26cb428e1f2be5b6cc138b2c008e30b4bd010c5a55ac62ca4f3cf6e078d501c72a85b4ca664c8eb3fe0e3e3e363c70e68802291549d6f52f14f8fc59c80c6270c429aba5964511714830b0d430c0a74f180c3000d4f9cfc674e25252f839e241d59be45d22247175e6492401716f8f8d861022fbcd0110039d0e8c43194edbf8a7cabba290c1d24d0c23be081230f343871c8adf16b29a9a0697549038d4d9c42a2a994786f96f29f818626ce27a34facd6585da96cb105af166864e2601ac526e8b64d25b2a3001347d5c99142cf7528d3e6282ed0b8c4f1bb720925440a73f37fa48033a00509b400c30255a2a06189c3e756f2da708d0f5f4756250e3fabaae964257187f28006250e4169cc97fecf5fe35e55031a9338a9ef53916cfa592aaf8318185f248286248e5e59fdc2f5c68651d2919523879b80c330333cd088c4c994e61ed38ad1b3e32fd080c46163ca7c694b4849a1ebc802c351a0c5169ef2e3830be481d4856ba0a0e0e3a3683ce2f03631a79a0ce1df81e1341c71085a6a261a4986973017671140a311a78a99bbe22242dd4e1d595611401df8c51660e418c38b2db6e0ad000872248006238efa2a32f45fd2937c91a35805682ce2a02f57eb4a89d292f4628b31f8025cecd0828b8f0f2f748b0c34147112254683cecaa4177b8f2cb0f0f8f8f8f8b0ca518796885330b1d6f30a7fcb1311a755f5d3959942bfa91ce2782534458a7532c4419c5c85c8783dfa529a4d805c070e2c3bd028c4b94428bd26fab217c239c2701264e58006218ed92516cff229dd0a2b81c620ce27a7da43a4e5be4c09e292301356838138ee69d833fd10c2270b88c6dc54699c6a2c938eacf2058d3f9c494d5353c628496216313f21974c9868f8e15c23fb7bdf3ecf4e7a6da0d187d39bbc093da3975973ec81c3c30387070e0f0678648185c7063e3e727c7c202dc640830fc79eb10cd24c776feb7b386a4a9b59234554ee748b2d1887e6a8ed010d3d1c2d54d06dc9a4fe26511e0e229648d172fb7826150fc76c77bb1adb3031e71d0e9656fb55b34529253b9c9432a12e9dfb28ed963a9c45c6a03758281144ba326540830e875861294b689fdf3b8793168d18eace2487d3c414224fc31a87536ec6f5c822fcfe3a389ccd2a95896b85bf685500b30220c801068d371c53237fa80ba936f377c36944ce9c7c064bd94505018d361c84aa864a9737969dca201b8e1bd284fcfbdde8d78a181fa0b18693b78a2cb99f62b7aa181ea0a18693eb25a53e64f7ce851e381a69388a65665d08727b74848693e5def58af9249b489fe12c429ad26e9553fc2c34cc709e5c4a041d6947525534ca70f6b0086937b2c837998e2c0f638c3e06d020c3f13f3677d246331da6319c6ad325bb537afd422c8653c4a43233a55728350ca7945a54f0af724bcb81e120d577f3bdee6653f685c36849166d6564875f5e38890fa1ce7db29ee8bb709c5c278489cba69eb970daf8666d379e1ac92d9c3388b16431a785d3a8067936eaacea928563dcad926fbaf2fbf65870467fa28a38f915feccf99621946e85c3a648ff25a382a6cdab707a178bd11a2e8bf013150eae9bb22f422b463a4de154c1921cddf27f8b26299cb32c5a596f47721745e198a5416d5b8877e1445038851833e7f7620a11ea271c53a9ac8c881b49d9ed8453084205214f528f5edd8473bbc5c4ec3aa9b46a261c33e4b7abbe6b3f915ec241c632fed2c9cf3c62251cc3c92cd2749b10c2e6249cbc4f6b8752a5e96446c2e9624b62a63bdf30f908a7fb4d92fdb4aa648c8d7010b1afaca5722afc5b8453b66b9c0a69e94768897048f9fdea32457ada3b8463690f252fb44238e7bfc5df5b532e5e108e9a26858bede8f60d100e77153a63a52ec66c7f70b4546a1a4a890489fbe0f892e2c58cd46b29eac141bdc8ab90d9a2891c0f0eb194faf1b88c492d3b38c9247ace53531d9cb47f5f32d973709a94ddf444450c1ec2c13156501e63e2d7377d8363cea40c5f9577e436385f24395e19ea7eca1a9c242469956e4c1a3438c9c821b5cec2c80b268d191cbd7a84b8107c6fbda42183c38f92573ddd3b7f491a3138c9249d6059bf2c77a40183b3dced6cc9cc7d3ac7571cc4280931c6ac33dee88a739e7e9f7cf1ba28632b8ec972826b99ce0c0d5971b05152c742dc6ab67015678d7c69c29ab9f67caa38f6a628164ae4efc9978aa34b907315c5552f79a83886ecfd267e4a9e2cef14e71c7d2aec8f965cdd99e2585aef928a77841473a5382849a17123b2d9e40d294e5145ac58bc53cdb619c529d4b6c49069238a839cc82bf964b899cd84e29892f72f49d3f17719501c538d7c7e4ade35a1f289c384d4e59bc4a967b878e2947cc38618a1d233593a71922046f3cbc56d320d274ea951a2c836cda297b289b3b5a87c2592d6255634711809a729b63a72939289e3fbc42029cceb2ba460e27c21445e8db90a1bd9250e32696d510daacc436689636ccc22c39d4d38b555e2a0b2e7c8588d6ab61a25cef6562a838eda5ebf26f126dbabdc172489539828ef2fa54fb61d89d37adba7269572f990388996ea3295226e6e1f7192267ee1e6e205ad3ae2f077192984acf9a56bc4498efcd5ddfad89614230e1a2d88254d2fca941671be8b499e5d16f3b415710a9675549eb9bc114cc4416efda4155921c743c441b47a88c49064f5e510e7d45027bc46867c152180210ef236faa84d1272157900010a710a5742e5d532d5ed3f0108619cc8777aa6419c4218197e2e4ba62c88b3441b5557aea1570fc441c4d33badfa3b1602c429c7b7e23c420a57ffe1a022959a38fad72eeb87639e10fea4c4fe08f6e1a4237a33c47806c7505143c4b6cb847c7d38a7c5faffefd3f1a7ba81f7571611d46c7a0f273dd9f4675b7d15133d1c832a656512620e0bd13c9c8228ad979d7e953313dcc0c3c1bf44ea49109a23c5dce1a81f2a29bdee2999360adcb0c3b12aa244f391af17792b09dca8c3d17b44b00af2375f257438a6760655992d758fe770d8907e1b3486a4772d399c94486b99a9b42f721b8763bc7813e94292ffcddf80c3d154d258bfb2f9aa646ebcc1724b277f61f2f191a3bc08e38b31c0409845b8e186f3b58998a91399fdef3634aa36d4572be9897d09980da4e07523d14648bce8420b2ed6f08cb548cdd47e27a3238bbfd8a16a3045ba3e956a3b36a9236b8c30bee0c26f0437d2b02439973bb7b5cb30d090aa55cd7fef12f4de82adb2c8028b62886738bd4a50c253b28e18d131c331a5afb3d30a35db5705de450e2ec0f0e2c178762cae8471a30c07cf6cb958d3f317a61b64284699f04d3ae46338acadbb5c0935d16614c3b9f29990f91bb54ebf37c270ac4da9b45dceca9d3ee60e30cc0d301c35a9abe44cff9b22a423ab901b5f38e4d820bd476b184dc70ed6615f70b1438b03dcf0c2c1ffe594fabf934b425d38459839297a7357fa9a0ba798b278d508532ac27b5cdcd8c2b9f4ba89d68f25ebcd8111c6175a305590f5eeaea7fa13dcc842a7dabbaf3b6ece12dcc04226e76159b22b465f479699bf7105621a1527dd5fb495dcb0c229af4ed8450aaa5fc455384f52723582489ba499a870b8b835151dfe7339bf3185d3690ccdbfb50a37a4709013f2ab72f88b9074140e335934a9e90d115d85c24969d29922f522fd069f703a990f912b5ede3e74c249e98c6dafabdf68c2f9d54ce2fc6c20b8c184b38bc42c49ddee67f49770083224a57bf442f4540d430c0ae4e086120e41c75967ca2dfdf725e1f8915484b413f3ef55750309c7142cd39b867d68fe47e8ddaeafc4650822be6184833ecf90a4e6326ffc221c82cc74bd659a46cb4d84e34b3021bb7a548e096f0ce168b2effa1ff25e64e8123784708a5cab69a57d5b4ec950e246108e23c94b33ccba59883be206100e5afb73498518d7746e8ab8f183e39d88bc2f93328e706ff8e098c4ffa9c8a53f42927c7c5409e1460f4e3229e79fb69bf6a8e8c81b3c38deef5b0c6ac4563ccb196eece03cb9bc54ea154f0b3774704af9627eb5af11620a0e3bdcc8c1c9ccb32f5af8d2700307276db3a06245510f3d8b196edce0bc416dd756bc37bf0c0d376c70dcfbdd53a22dafc95483e37a060b39692a559469709ae015b66f12cc4658861b3338c5883d67e91ac3fa1b32388649d723694b37a9fc460c8e371b4e927aa92ebd5f086ec0e09437eedcaa6714f5888d579cec3486e09722b2f4a823bb7012ac2bceda73a53dc2a75e34692b4e31132edc99ebc911ebc802c3021c063be0e3030c2eb828678315c718a292e95317d2c948031e383c2890010a508018185c705130606315e7dbff0fbbbe55710c5124e98634f9f868858d54e4a54f4af23bb525c9a2810d5418e29a3aedfaed1a6c9ce22842045d4b391124eea638a4d1762b7fa7a2c6ebac14c7f9eccc0a25e6c3dd2ed820c5498345b0e41775c498cc0b364671f4753b4bef1a13f1a4051ba238e9980d672954a698356769b0118a934a9b1be92b42f8a80d20c5051ba0385b87b8209f977a2946f613871595eb2e7246eef49e389c597aedabeec923efc46127e8ba8d1952f8d93971103731b927739a487d13a75c41ab2949ef6b9f6be22017416c9253cac45163fcc96a4853114d983846aa24a9b27c0899842e710cf9d52f124e260b6a4b1c6753ab9b26a1aa5d57e21884ee2f39966293941207d197db2666bbc4244fe214637e748e882453c99238eba40a9627d7481caf432f987cb9739142e22479625525253ee27859c3a8f8ddf7529e23ce7216a384ac5591c6d28863d48d6946244b1d9a31e29417fe52a4cc6aa359c4c964cae4a3a7336b2953361471ba8b9944b26c1ba2ec89388754d38d97b92ee811719e342a2b2615db76a6431c73093155639de9520bb06188d3c9cd2d6b95102fa58538edad46548bf93f94fce32347246c10e264992a86da4a4a31de208ed92a553c79a733b620ce1fdf966ad9a1392e813826bd97c2850ce2ec36401ce44d261925cdfb87fe54cc7eb1bef5e40fd8f0c3215be4ca5496f2880bb1d18783b27017a9adbb2a592e30051b7c38bd6a66907cf57129dfc3393b838ea02b6f0cb9d6c3d97d72dc7ce82ca9ad8d3c9c9296ec88d3e3e110549ca447ad4a3ad1dfe1105265a588c1dabb94763885ecce538b903b64f7f1b15f70b1430b538763947ce2b75297c9a8d0e120c9bc237c9cb6a462733885bcf8a2d42e4b50971c4e15ae525f3b53683ae1f02017f0c0e1412ce081c38354c0038707a180070e0f8285070e0fd2061b713864d31aefaa3264f154475679840d389c42458aad15338a4cca230b08809105236cbce1e8a777a5b6520ea171371c3ddf6d5e7e547e953cb2c0c2a33661a30da72dd1a1eb254cdd330f1c1fc4061b4e627a4e092d3231a6181d59a62c10c6c787295665630d240436d470daad5049464a1b4729f02e48c05d3005101036d270eadebf11a92d77efa2e1acd154df4893cf4bdf2ec1c6190e334a0931329b9a9594126c98e1a04c5b4fad64c8f0bf0ca7319d9676467bd48299051b6438c80849d464fdd1944169051b633828593771e62d04eda718cea55e73fd8c8c051b61386590b52125b91a646ac170cc7d298a4ecba825ce2f1cab435a6e5223e2df67c30bc7aebf88722a5a791b5d38056d7ab6f7434bf07688917290a00b2db46d70e138319f10a92cdd1146176c396c6ce194b133ca09d1b231c8011b5a38a620255e3ac93d5a2610b09185c326a16c53f55d485a25d8c0c2e9278d85281321bb193024d8b8c229e96dcbb69bf3159d810d2b1ca42495d49b347993b220d8a8c271fe84ba18c1e47dbdda0dd8a0c2792467efc7ff4dc8eec7c7d998c2717ec44b7eb608b7902e2ce081c30387070e8f24010f1c1e1e5960e181061b5238bb4499103d764fa58c17df8516260a27e125aaa57f434d1e5940200b2c3c3e3e6c4021a1238ecc88bf69d2915528e081c3038729b8369e80ea7b2fe15ee6161b4e388ce56ada525a7469b009a7314b3b9752839a6e9870ce0829ac8a10cf90fd259c7ce3424c9717fd102ae110d304556ae35f5a948463ea35734d91eedd234838684b505e59e2b78e7e846336159164495989958c70b0e4995b6523a7945b84b37689ce9df635a12b110e4a8747bcb05d937708a72a2bebaa6d0d614b219cc2881149ce3a2d641704e395d0b1e02a4998beb0c006108e9d2de9faf23b7c648860e307c71492d75dc44a5121ef8353f2f7ca1541678bc8f6e024d4afeb428594142b0f8e93543755678cacdeec80206d7ccf630d820d1d9cce92327da3ca665d4e28d8c8c1c1bc62ec52229d5d9f70704c3294d8ccaed191c5458e31c0088324c1c60db6ca501bcbac31ea6fc3065fe86a6e67c808b9b751036f5c2e6967ce11abdba0819a5d441032462b2bddc60c9041a5982d0831e24464430675e5de4a59b162c8460c3c3f2579bef499ff6c03066db6894f53679ae4fc0abfd56c6e2c766357a4d25bf6a5d0a054b615879aa067e656ba654597533d5fee84beaaab40bf7f672c7de2dda72aae1fc97293b26595525130d526fcae3e44852935a824f69d82bbbeddf0fbba9ac2fa12af15567d624a51309d23634dc8962686148ce5aca8b68cc294cdddfe52feafa844d145b70d0ac52954664bcb9a8ab9078a53f8a4cd5fe216be2c1d599fb0b209691b7c3542fc3d718f58cb269d29a8689d38a8cc5ba2d237b34dc4893c4c868b6da2d473556249f3a97c320894a186268e5f5f23bf7534274b7464a10a98021e383cbab080070e8fd4851613f0c0e1e181c3630c9f80070e0f1c1e1e5960e18143f50be62247f292428d4c9cc4f8e48b17368edc6a47560004394a50031367b994c54e7a5d6f7c7564e5f0526088b1a38b631aa87189932aa9416afc95903d0ba30b2db8b0c4a94246cf2e44ca4f534756176088d1cb811a954844ad9f69eb0c9712ab96c9fcbe4965ab142fd498c4d123c44e7dbffdb0938eac2d5a0b24467230b20b097c7c7c7c58116a48a2d0ad1a3ebce374a8230bab8b0d9072418d489cf25443ebb825656d7e7c6ce5d8e20b2eb42875c5831a9038fd8e9ed409faed5de770146ce1c51848c7401ea8f18883f8511abad5922ef28e38c546df2c4b2a8587d988938c88970df9fb4b4246bc265f2fc184a62ce2ac7311536f7f2971eaaa52a54a066a28e210933ea1191afa4a68be502311e7edcbcd142e69be248488836aa6eddaff56fe10471161732529dee2420d439c63bd54b8c93c32ed99851a8538296fef8f993c218e5bd613bddbf2574c07710e95b349c67882307709a14b2e86f63a10e751e2b593745c9b48c2f01d3bca8e1dac45188602b3420d401ccacc2e69abd10de7f02295fde1a023be8988ee9373998eac1d5e7c7152136af8a14c1f22668467d0c1f8228717e9ca6050a30fe7dcde206272fb9e94443cd4e0c34186deb59079ade345d6861a7b38e9b61aed511dd1523c4769058ce7a8e48ee5c3a1381c0a84c26040000c08a52ec70193140000000c1a8f4662c1884452b5f90114800439402c522e261c241a12161016100a84c1401814080903613018140a05828070404891c7b4ed00921cc40004733e0b0454b73349261f9cc70626f3619bf135f888ad197eb7e924d50e0ccf438768dac33f0cb5100e857045b408afac75a02c0bccf0877d85d039592663da9aae9faa0281e226b057e9d588ea51ee92fe884fef050534663389d24455f41f54d9a8b989a5cacae962e0dbd8cbc2540fc93a397dbd2b31a7c5ee7ff3725f227288a3ee4e39e1ee7cdf26926ad6104e0beaa13d9bdf2362df127c18256f1a448d0583a136c08231086812b21e9fb317c0d6e5984db645a1515bd2afc570b66c1a7542767dcaa9d79d940a533a0fc3f9a39a96a8e8aa5f035be11a3770d2ca4d680c2a9bba0c3f12964b34c3790616f064082e198eb5ab41aca0e20cccc2c1c67ef0c05c1166510494888d107fe389570ece7036761414bd7cee21db2dd667a9ba4b1fea760b2d74b253e968c9b9bf70ad86b044919dbbe7405189ff70c91221a02ec838b2984d33c82bb80218b8fe3ded0d2bb537581b070823f2f1d34027d670a94f22ff26ce3e3da04f383ae5e92cec91a53400ac7cfe01976d8ec8ddadc3bba69be906e961eb158238d0f124e457046f091ae86073da99e7fed7606bd58c3c538e348af7281b9279e5aefd08d1345b13e7de41197ba50fda0003d92392519885a47bcf398f0d4e007438e118a692f9cbe0d80761081849b33049b61298622b3029a2c5cec6f2f6043aa41c2a851af8ba74ba1859fd272c74495b2ced5e8d2e23fa016dd83a8c692ddab4cf3d042722f8ec8b388028916ad698811e343feacb3844a0b9e99a3ba73183e1c0a3f9ac201510867f527258c13b2b3cd36ed0353146b213992148c877295008122317468503734a2340f0bbf934f19b76af05e7e3eb17211fd0abfa0317682de00c380ec9fac77c0fdca2734dfe23ec97d42957d4e8d1c6ec5a9859b9eaeb776778828fd853218a7703e92bf6cce5136b9f59ba3b56e1235657da9a32e7c046376751e334a1ed5f24c3f4051217c50cfaec14a089de8ad68476460d6aeb9e407a496fc590e505d53d79c5afc030b9a40ba4e4d5e3a0a1bacd25cd66d8207a6d9c72810e493a21f16089c57970947d19e406054e901a8a31abe22ffa62d1fb8b0b8ffcd33b14e0fdbaa0296c77c62fee089bf7f3ee8c89bbeaf0e61fe41e76a132495572fa58d3817e9e41ccb0a2b6808465301da2037b35d5564c10033cf4f8fde0a8036f7bc1f61af9848e2e30aaa8e700dd7e66a1978a3cfb1147076d6213443a024144b55a45bd8cbec151cb228d51f99b011903d5c37b7f9f447d7f7540ce8577763e47d624fb508eca9c7ee5377eddce4996684f9f8dcc901a797683ece85a18352fe3dd3bab261dab3fb80c4f6f9514a832eac27fe3564d21758f1260b087961975d69187cd832e05350426989c8de8b846528d517e1c1fb4ccb29e4a256c38154c348980b30a26a18f357acccf5e7b532cd5f0fa8145753fdc578b43a2acbc2e14279d6bb4de229a14ffb34ea27d40005a19eb2ea27b4d773a5d4608ddc9d5cedfa5025df87515e8daa88bdbae76bcbf751549bbe71eceaeba98ee9fd4eda5c13b7f0168dd2edd6175cbb14bcabc75989ab7ae845104e2a704bc98021254bc849ec506d99014343c11b08114c7fc7785411ff06f7c7341682cfe53fa26a33c7f362b0cbfb9f29270aab16ac0e2c23e814da4177e8a2e738bdf2d4cf201f8f37ed56b9c8c7143b6c9c22006d6aea56cab5df53763f66863416e17eff9e3d398803e88fc9cc239dbb322460fdb18a5c68cafb4f830e7b5e12445aec5ab2524a425b111c530431786b8082a8507e0faddddd12d92bbc400f9606a94504618357de9777861f5288272a4003b85f57dd49dce58e138ae048e35d2897aa342b2a9e00aa4b0b4a278440a91c77896493440db32e67829edd9846b8df08c6510e1f1aeeeca9875b89b81ff5158095e3601ff9e25dbb85762868af206d9fb02467ffe6daa0c875142c679ff051b3d1e93e96a51fe111847041b023308209ffc4c21a912d6f57dab1fc7ec1b909c87c9c648db7b4614c8d95e1822ee2cc6615b3e462d6a1e219e69143d9685d587082608e8d79555df0933a00c1283ecef6d344ce36b5248fb8c72a0abcff89e1170e46660d54d1974d3fa2bb3b94f66d8e19eeae2a82aa185df4e02e2a594a2b0436bdfe0e68a37fdee1b347cf463bd5ba26d7aec67ec592fcb2ab466b971ca01a459c7ecd5926bb9c03eee475924a506f650da2f4a29fba88a50c4c89358ca4a8181f0c3a1e0f84a1c3770e62bee8f62ca1b840f241d0356140cbca2b5202b106eac2f3cdf55f8e4066bd2aa4e2d1eb219403d99b3fc8b57e482ef4f9433c3ff449e9976270385d1a3b0929c817febb2aff821bcb403fcc97d60f72fb99122160d02394b0a9798389c621979ba2dc3a1592bd350b6770c1d0c201a127db552b074fc5f489fb8b4706deb0012ac3dc50dd0918d1888612f43e0ecfba8caa8b1d94c30cc64200579785af8799efe23968e0fd9ae53aaaf198677ffebb847f90248903839203581c8ed332a50250cd1cea7e34c8c8c116d29bba5a1f492bb63b812d86ee26acdc590886053b4efcea17d7ac4b007c5cb0eda953e04e5e524ec516cc1fd399c3aa4f7843689b0eb4982c405ac2397ceede1452c7d7d1aa128f539a082b18acae85d9f68f38fb7ac81857fc35eedaaf4491b11341e70f243c74eb97341c48d964155c8391e7005cedcc56e08683cda74a0f91c24a028a0334096afbc340261ad2a9615d60f3f0677bde19317a83caa291cb26b818d7ed8d1937759f6f137f3499c9a3cff5c90f72dcf3eb73bace4075fbed145bfbaa480abe99d077fc712af50f6ad5ddb754dd9f25b540d4fb711e0e931cc500f30f40c035b1f84bb2968f40ab30de80bfa1183890c140c6a7d802b82d9186cd66b9011684c18e69c7b9fdf5b706ea39e130b5a214f767144e630238db862021405680d80d40118a5a45c0b705a182de4f0e8c5e87d5526e0983d0adc84376049690850170a026f427011b17df08d5ad49486f2d85f952073162fb98269829b979ecca2cbb3dd5d70ec3ceac82f0b4bb79cb3b31fea78b1a6fd99929a4cf4e7cd6c746e96114b9b2335e010a0685b63bf7c4beb6f52c28157a257486ef92a3c6869bbbd83037d3fc8fb0ed890dcf237ffc9eba49e8bf60eedd3fea9687945a68e00aa03084c489b4e962a8a22085d77a47e03c1141eef10269102288c78e364fd2b466ee16a6877be45f48d1aa0f8cdaa63d0991b539d4a0cf15065ef1396b5d328ac1f0f85fed6690a12f88069d3c54f7bb210203ba6a7ad63e8fd0b456c7d24db27391410c2f1f76ba13570ca1eccea1019476ef8404a8f7070d02907776092c3de6115725e34a49eb5160a4cf35d9b818b8b4ab4a19585257399997038fb8b0a6f1e820b374f2d17ca69bdb30395b8b8fe27fb69768450023be3d8f9cac662880ef84632d17083fe1f7284de6559fa37875fbab937d0cddf24f4c011aaf8f6c5c0c6ab2661c3b05692fe7978f2c2a7a7680d7bb8bfcb4057322d045d13f541d0fd93a3a17223c804be66b36e636fe31af2d7905d29708fb68a6b95072091388abede100c947ed6d33046a24f5eb962552588bfa76b459ebaffee7f73005ab084390b1b71efd258de8f5a9f714582acd550671e1629b92cafb4bc35ab212b7878bef00c94c786ecf5228da823899e518db8a8d3723d6693cd7316b41501c68073cd55f95e772d1caa06108ca5981c983dadc3362eb0ce2c768a5cf5d1776b3c60ae12b07a76e8a9bad72d7ed73ec539a390e3ba57ea5b660b4b256dbcd2da3bee0cc4d98a4b58e22c3e364e8acfb8ba754f6d0c91b7bae16e887d0869cae09d59a0d0da06eeb1df6dad44483d8363a5e75b5d5168e50ae13352499e6f870ba70001afa8474e7e120e73888321789fa40176c352dc2e8474634ad67b717df5c7a85bcaf9db33a488e9b315940a01132e6020eb30aeffe036411a50618a1add348b8a89e7c9a6437e94cc3eab097387149ebb292f2d82b955da0659ca721bb121d953e2f4144a5c33919326fa1a556f75906b6c4802fbacd0cd563d4646b3016e4323dc40cc04a4b5a707ea607b4b146484468110825b20704237c5e5d78aa9aff8af1249afe15022e1407d6a8642e5a97a65d7fe4d371de715b0c731b16589952b156676b6cf193cbb35999b89d99726296dc6bce0755156f9e548c06c3cdc790d2d81b941970332facd824b9e8bc040ee0125396feed483f2c8910722d3b119f246e6ad28ae14c76d29e79360d106360540a93cdb01ddd7f2f63fa37e4da1a06799869f1629902e6660c8ef270eb0ee97c02d93940f9fd8b43109ac2e7e9f4517268e7fccf16250742defe8025930465d98a0920e74e68837c7e88f1f6e5cf591d7db6e2746cd79ffabcedaae435bf4221cad9ec23aeb61283f7ce9c31a45c45c6f12ac350d0b830e789c5755392903b3e16da9f2eb00692cf26e56f6b595f92db2f7270cfaab832cbebb61c59d339785f1225451cc51820d3b6861ace6b6e79db7695731221567203cd4185404a136ac4c275daa83449efe194438c83a9b87adca4f5a7389bd4171fe93b80e75fce898a186feef4c60f01bcaade036b838971487ee87d9c1daf216d2216ecf954eb93732262fdc3f28b2615fccd4c52df8574d4de85985d8e30ebfd5094d5c111cc085285fe764643ad9b50f71c636fa2faff2d12fd696ee1826afa2dfd366df6522a255a13d72b6764464a0c00cd6d04768340942d000a91d38807717891765e0f37d1e827ff85c4ccf78023fafde35a35544d5b8dc8279e2660740c79d17a04cb25c5e39720ed864175489d16dbd84f82a1313acd2022189c35ee11aa61cc4e467aee58216242f4be432045516af0b24ed7a32a1f7d52ebd49883763f3076607868a6cf2d20280742390a148b532ea782e1953df41355e40abc3bec1585bcdcec4eda76d0050a66cfcb1141721ee6a2154f588c2842d80aa577ab2109ab33891710534ab484034027213d2f90052052abf65750182ac75e49efb6d8bcf260928395c1d3bcb694312c7999c24f363dafa976a9140dd971b40e1a9f21429ac7e92c52debd0e878948e39029cad442b8f98b030047d0f21e0cf57d43b72443ee51cdb618791202623f80ebde5148d4d8a2a98ea4dc4ed0132fc9b3422f21a46e467b7e433d21a4c68ce7eb2f684a6ab01e1ce511d481cd36f7c1c613b34d4098f07b7e3723aec07fd740b904d7a0dc8c211eada603e872bd52be92c5838ec1fa41420309a68198f09d022e35f053e4871c8c3f1dd419c95a12fa0b935b929ca210e26ad3e8efa9318121babd2dcba84c9f0cfc1a169085fad8c8d8e6dd7e6bb4de3028c29d0ff68f3932d778646f8ac74a11e5afd77cb5bfa382f694b32b1f8882312666c60849b96bffd7533a19c45432455857c5a21bad69aeb662022594ae9f04c910e2c386cd8328ca52a82a3b189c7a1d458a1e968b6caa3d8340da4226c4506d18478124a5dcb54ae49603d1f323c3a00770bca0e870d52f1c41c09a3de9d27706c233da129818a4bbc2097ff96dae5727dd6b70e1c0881dc19447e309d49eacd63e1612f05dc217d1be23d1ef7eafa480ea1601f58442c6b16995eca420997c7687793df324860a98527bb3ef862350ae89c739b6df2634cc55a28c326156fb17ccd03430aeededa322ea45397584deaeb3087b0937727894501593284b5e979952bbf273077ea7ebf41f6a18237788886ec72ce6e88053b95f91fa712f39e88df533d773f7dfeb79f744f78c1d70250979d3ef9540880c9b889ef9e0ba58844578ee4929aa555454d716c488ab5215e797da0c4c5cb928a268025ae274845e8ab88ada1ff42789622e93bd0532307b1bd9082d1ba654f5028d1c62c7930eec50d0ad379a61f740276460639d140fcc4719196bf9e900426c816f82b6582fb6ca42a5fef535373d112742303ab9c9dbf6be5e01b46cbef9130248b212a39d60dea502636083ed3a871e5f8259f7f4e21f04721249c67cb9678e9600c5373026b02543b21f0ea84dd146a5ae8d7a3369d6e3dfc5426b94a7f417ba6a13760525fe7e26c8118f6751c727f5554d0229c8db27cd6a7c3773bb183799a78e1ffd80a0af6e398d18413712f5315b5ea590ae4c3e9471038940a39700ae5807bbbf0216fbb89b51c3824f6937ebd21fa8f3541a32a24e719de693f7b428792a709b4f0ff8f5448ab9cc2cc4c6c2c790760c521138d7a1d856227b86ea32fb74cdc7aabb329880b71902227d4a0f0901fbe4124aa6a4090e5bac9cd39a3795b46ded5a4d058a69a3d0a05e429aa97e10c3ed202fd4eae09dac9a9fd17170362641b13ec1e59a133e12703417903d4a03f44d41daf82fc580c6bb8694b4418520e33bd886674a7bebcaa2e7db1dc52d4761feaf716c3876b6d023321c12d55707886d8c49cd5c9b033903489d2e79628fda5e5ca8937c4c9a22ec295ce948d61908a41f84e28af8169287f15fb115eb1d6fc09ed112071bb92ac1987d440fa3b6070f880c3871b946ce782488c9dea354e46c28e92ece03a256ba84989698a032763ce31c358aabc3b335bf6a7eda13e837ff4ab2f6bb10c7233c057b162ff23ac2a6b910b1285f059e2393a00f882eaf884b9cbfb3bd672c24512b341b67b2b0a8e583023693112c68b47ce76699c0fe314693539f8cb93011571e78afac54c5aee705cb2a6ff1e62bbf313639d3497344be52d665f4f19ffe8a0eb86ab09833d09d202bf3ce5f5a9c7728628f1af024985e551066673ad3781cdaa50724e29590083efe7f263834824be5dbe254717c668e104962d4baf4c136bb4e195cdee14c2155ea9126682771e996385eddc514d7f8b436a69dee3aa4054df54aa8fd260e10022c67676d838a49fb4405706c7f61e7d9db3a865fbdac08c8ca098ae9fd8c82473e1af83c581e724bacb434b5dd2d1d6ef759b450404ac01e47439f302a3547c1248526ad36aa29741ab7391e701946e5e434c7500b187a40273b4f340ca9352f212784f34cb68c6d5a2c4a86585361ef830ab42c9c0ad6fa978909e8e52a885b6a9026ed69bba4c4503434529bd3312291f8902267a13b7b3fc497275fcefb748e0186ad8a04836d4e9632982776c9dba74650a56ace1f453b1603622c8943949d96678467603edec1490e0274644704120862252990274529393c1d6c0c052d0a39892131359bcc7cbbbecd89a374e0c52c65adf0df639647383ddd7ec035413eb1b3b4529a595bed10a6330fd047d646a4063a2fe8325034999034537165c48ced09157bdc7fe80debd7405fae4b6a60950517348395c6a8a65431ecb3882b04ebd0536de8fa2116c217bfc2a2a91145ff412e28e55d891848fbc81ab4499c606ffa7205c3b1927340a80caef0b4a2a01f513e2f4d541888876445f1d619106524c6c8d560508d34903422a7bacd2d91970c60a6baa10d0f6b246a8600b8c1780e7c6a944ba3327d3585eef3b2e021e6701898466992b04550e236f65d0240802499b2d9e067b5a8df281df8b9d5969c8655df8d9a4629e1319fb1160825de3b3ab8ae3b1ade063140a706c19882a6c3d3fb3e6b2c4b122b75e788c4005c9f2a232fb148f46c7c0269d3cb27849f12beec45655ea5dfcf2577757ba30197278eab55ade364b20bb3624470eea31107984c3443431174508c43a0a621f71c8892dca62bcc02559e52727e872af5e0d2767edec99d57b65580a7ecd25827049c74e547d4596a3b11bc9cb3841df32df633ae5a0977062d8441241b1858a184613d1310c9d9844428193dc5215b48511744206b9988f5ad8211771b0835a984117f1b1597cebb527948d5a07cdd923cbd9384590f6bb612537b4a854067ebfd315205997566897986bf3e4a40437e7d8d51dccf3b199b4b9dcc697e6cf14b0860062d66aed4ca3723184243af151925caed4d56b8241f14fe85c0ef9b8ad43b2d4279be0a9877c8a544ec775d1cee3751ceab85cd364a88f2c415337b9288ebcc57c91cfdbf51dc6f8d42729ea2143d0d44f7e70ed00fe87e6decd333ae65bbe62c777b6f9ed78ae07b4af877ef32fc0290feb3a9fc7b556b297029743b1c54976fa2cf24965f8b0ec7e48f90053e428a4c6aed583e88af4f4a19e3a0884ac5378f927545d8b0288401dea1391f4e98f44ca122f9968937c2a123ef9d19158ea251269d29f44ca273e32d296bc54224cf2d391f8d44744d2d22f4994497c32d23ef9a84858f2a51331a97d273f66ae0eb1f5d42f6f8988845af2b0c1b05e823f0f9b42976c3668e82fff0d23bb8ee524dfb7f3593c7221174e4ee76b3b78e7f33e86937d8edbe4e1ff745c9471a1074d4bbfeb34c450f191d6d5b54198c1e40c3d40d36ce10012be8f722151686263c967c89f8aa17b60d683a442c37b6e64b092db84eac5b1ac25319fb6fb3b7e6b0ea9d8d4f0f9c1fca44520a45c51477cc4d76c9820670c23da3e974b7cc3ce77e90fc45c3a15167dc39678909704d4445bab73c4c392c9520eb8872b7b4db7ebc02ef16098529e9c9a71668ea8296c3be65b76e18efed67a7aea129789a353a2900cc5e5b42a11fbd05d5ac9978288243e05925e753fb8a1d44dc294221f81284f3e144694b8299854145f151041bd8d6eb2fbd4b05e9224e6eb7676961f3d909459cef3951ddff1b78a746b4aef749898a9c6d26cc1577c324b1f8b07a18583302f40a9c6dbdfa88c51865d8042b3358b6c22ce8b5819919a11b9f0efa80c5f3989395d4fcedc48f4419bc516a0f5fac832fd13e481eb4c929279b969e2a3d695fe962531ea816d2fba8f7e1e1d031e2aff37ff25ffe2a1524f76965969985bad0cede7c4cc9528d3503667e5feda233f0c7de02db4a3ce35259057a4d4d431bbee75020400431489361469117520b6afbf91d5a4c0a7caccea6999f8546605fd55d420e91e891222b513ea74f901a939bb1f0e103e575bd8c3827a77815b31d89b0be9e582db16e8dd85f48ac15b2cd49305d2c3c2c8b756587fe6a668b949b7b157ff7f809cfd13a90b0584859842a0697891bcdc77cddced7c82ee31b2990827986e8e1463f7a8480f05226a1b155a8011a79c2f20633d27601ff38a070b2fd9b2f783dc95973b9719c7499b12c4690b17fea81330d7a8b63498dde3f319e000f0634210a105d6930a07001e74c8e05e5d95281ebe3f08fa27b700e782ae030f746e884927588a7330df8a8c20f93996f48b3339f9cea49569343d692ba0107d2ae5bec22d2f70d2f5898c45e5e31ef3b6681c68cd1fa6c1818bc37e32ea6dfc50718c50f2b67c2e4d026319b75438d50ac73e7ca4e49b2c1e527f9884ab13ce1b44a08670012f7dc26a6814f950b3308100149a4bc63c013ef3f89da82551f9edc86be94ac4d5ce169c5b5b8101bfded5113c56f4a004ad2b23c54ab55e6611aa14baf8d477cd801483aac9797e7fb0953e03b52dccbeb7c66e970cf64d13c55b1d25350ecdfaa3b264c1a0394470379a7a07b6006844c53c014d159717e2b3bb85363778cfd9c1e8d87a2d7df0de1952b3b304a605aa0978bcaa974e0a7cf2a1903204245a4050566163bd6377ec2c6ee46d83e9230d8f3640755e3ee3844ac50b3300e451fe7903796aecf7fc4f5e4fd6d8d8fbb1915c4cde49901243ccdc9913218aa14dbfb5d19bbeb0062ca1ffcf64aaf498a808276a9c7d225a79664ae0b4f70371f68b232cf442a36fbf4000630a35a5a6a289dc47a4c4f4f170809acfec1c321f7fdfdb818d03e1407727ddf762e7f8101fabacd9b0d2ff127572ff6f578c641c4d85b9f6110e514da03c7d56305be94434987528e65394b0acd8dbdec624979283436cb0fb5c8139d6ee8ab0ba3138f6c429a032256fdf5050f826c47d8c16e1814173f71e42d573398a523d697186edd4da1c72308a81f31325116dab4a1ca018284e3bc95c3691405b214683716376b3064324430388716eae863a3ed7655d6c84549d4716dc5504a8ff57d7a857215e5c0ac86c3993724e4af10ecb082fc9199042af8dda614597974d4e7fac89efda913e03428f1793394f2b72a2b60a63227125ff5cf150757127271d8049c9d3d782a56a44d430c74905b67a6197b3001e704d8097452ac829c354eb8e2db22d4bb212147ab0cdcdcba760cffa44107350d85b6d7f48911af76b7b9e9a726c836311ad085a1b550338ad1d835e1b83b413db2109e228518dc1ef61c11788ca863a08cae8ba47e52c665279be1d34b94a71de0941ef31b7e692e188f3e48db3010a9575e6572902aa603a31d57cc1078d3203e63a2d9fc29008d0daba614bc7a3a167b6d2cd40b4532ea14a55934112280e8c2ff91b8db809c4008d00bb09b4f4b4a86da0f3e7ff21b05e96e13ccf9291a7140e6b7d16b65d5540231ba498079b7d9dbf60e6ccb0429492b75d4df02bb7100a4c31259c2ac359a2e9ec4f4e95b3a29ec603ae4bcdf43298d9f95d6a1c303e72a4a341891056386be021ad689c691aac3debd062e243609ba87b982bd433bb1ee5eae2f0ee88626e72ea34c9442f97873f6dad393089b2ac69d37f7563060a19ad8936c2c94be1567336d0d786a30a0ba9a589001172ba0865a57150cadd1b8df1a82fc841d5d5e2608c8f9ef70cb3793f01896a776720bf7e6b8cee60f6046eaefdafba640687d3e5be7349065b447f1bcc13e948d4fed4fa9a68814f4e70409331a99b5638476d3353f2ad0738f151a4e265543d02ff3baaed3878f49858ff9819d7b274d26b5b8e2b06a90dbe6122a45839e841632bcf1e3e216bc1507e87b86e4fae08197a4da3d90839032a13fee3e0f1534680690bf9956fb50ceed10cfba8c5a89edcc319f7a5ad82dd8d9299ab15f204191e7f38820097dd5f727e9f6986021e16e2b7a8bc64056be59bba07dfdf4776074d1ea28ef2e8197b597deb303c8d296f53485c0ec2c81efa6974cf9d2a7334f5cb2514b9da7eac81213138c41657c55235bc4cb8360ce9d81386d4f7afcfef3269803cf7cbe3a0464023d294a3b05781ce8eb5be256220759fe30c78df7943163f3b093a92a56dacf6b8244ca0a512850cca6c5b60b77d503ac69de1d0dc11c7f7d01e65f70e08ea77f6d573c4c62d529b93d2114ba417208f1c8105d5395294bf8dc336f72e3b3d6fa1c0dad58ea333d27a6fca0b9ada43b5fc33e53780cb1dfdfe77b30420d84b6ba0fb18c972a125e67d8f72b18aa2f1a4d6203414b36e6f4dd0b98432717de0b2bbf140e9757140391fb24acc5dd68389f6e2e67fbe6decff4e3a2c30a9cb26e824dd5851df7ceeefcb1bddee3ef7bf43ee2943820c2cd568afc5b9d156151ac714f19803d8ffd3f291c9af2aeafda2cf3758eb534e0407597d420e484649aed01f393634488b9a23c3ae12d59a80929fea0229221b7a3a178f59b733be21db37467b04023bffb2785ce4280bd4754b755a089f2c6a0f9fc876d825185f09887897582606498700b50284391f7918f649506f3d85043bf3b941ad77628c53e49837924ec020198690b90b255cddc31ab0d27377543a01df04fdf1b9298529abc1847bd39d91dbe43ea5ff6bb0d902721c14f34f6f61678cd3a6cb57c51b442ee67b6d92355e3ceb21372397a128494a4d4097ff60a460571d0f770c15dc940c3bfb8a7d3855c9d3a2ee3073a30cc3c1538efbcdaf19a0eb1c391470367d4cd1917f0b11c9504c2b9ac9e7e0d0ecf2e21af4c72ff4a7e28a035a1b162d110484a60ba6fa55022925f5e17810e2e9d33a33d6d97ac533e428104e82e424f7373bc47e08befd1a1c3e830b77071cb90c6b63e5bcc49ecec4856a5fecfc2383aa1386fcc954a1c65b5f9ff7871568b43351cd09c2ecd4955dbe9d6b3afeb19c17caf97db1debeef7aa05d7d9f3cc305815741f558849803cd9db2b1243cc58521f1b38a6ad048e38563fb5925c31c084be372a3c820c82d20a98de8072f3d0064fcfa35e27327056c26e905feb378a6afab4aa9ffb8f2b939587afb20d6abb2f13635b8a2aaebcd8c60936013fedd2a9e0e4b07857f73880e38a15a130c8efd74460fae89c68549f2d3b6f6d2f0869a888121b20ab2b2258cf251d63509409fb27b77ef1f44aa078f495389634651c5130267ad92896325b0c23f1aef4202f1673140af85ce9a2f3ea2eea3790351b0a1241563b68146ba3d31e68867a9078b34d81c113205ff40ca4b6ffd9e51613d35f937812c4ed96742ea451a73ae80223eb6c86544db98c054527ae945d842e276d3ef106c0a363a380558eee74766742424e80125b00ccc4f1f9236112db9878ec927b5520e3dc5836d41d4f5819f301f751fb26a441bebef75aa024a2e0570915c8e3377ac12b3e8d266fba095abd97efad890a65bcbca5d0b4195a39a6d47e5290a2bd14fae4d4dc594c20523ffc2ad9da9aa3a6af183ebd0eac86c5d61a4e28e910de6de889d2c4002de1f44760a3560b25472ff000000000000000000c098550908db91dcb9a4739a8d14f1919b644544b2129fae139fce4c7cca6dd6dad676663aa004590a9d0a2a0a45dd92fc96debb2967436b01171040b102c44803dfe7d9a2af2c99e51f0dfc75b4c871349ad237e50cbc7a6fb28eaeafa3909a81ddf491878cb53b3ac932b07713daccae3cad7b64e03b0e296d8e9d755dad186360da3ec85164e6206a1f6260b72686e598bb95cf1306a6eb62f0949193862ca2e9210618d83c756d7e1795fb02977d69820589d821732fb06f6ba962593d5fb60bec777a9cc3ab5c29a9ed5eb40003748588c105be838eaa91938500528388b1054e3ced35fe5f9aea94060d1a1b1040020420010c0820012608038c148ce01ce07f01e7a91431b4c0ee871369a38536b44cf025780a6cc5c802173fa2dfe370a2aa1790800468d0f8046c0c2c30d13d2c64ff47b1efb80297d3a3b8b15dfc27e7adc07fa61c7d7b557cec53057ee26d6abd9c634eb550818f21d55e88941051c32974661fa4c758a91852e0ebef625689ef2220461498789da27e3a370614d8bc15452d5e8eed53e609dc86fe9821538e93876927b01d9997a3ce568c26f02142b010d1f739cae2430c26b02f15562aaab9eb712422c61298762d9f8e6521714b44c450029be390fd61ce5168d02079881849607d835fb4e7ed6036829186184860d36d33c5d1207561e318621c81b589bd2a3908ed39be0b80c0620c2370aa9931c7ad1fa7ca1f45e034a49c2ce66c51927d1081adfb28b577f71098182a74d8d64154971c2130de59a7d27e4751e209023f759b3af2a02f153a40604d3f4a15696259f2f801bfe691625d5d7689ba0f38edcb0f4477fb52e73de03c2b25bc345d04cd79c0c6e9c89532849c36f31d709b2f32b4a474c087ee38e886494c39f21c30a943e3d548d6f270c0d9476978c8df297b7303ee3f3289d29ebcd96103c63bdee6791c3c90b4067ceeb110621e3b060db89fec4bfde817bf6316ac7f1052aba770cd1e59f01d52f2a4c468eb6162c1db65a81c850a2cf8beec717f472c33c92b98f871681eca337789eb0a366b69368f3a94f4b86d051b24d364b1dab3ee96157c4ae1513234a4f4a15d051ffd810739db9dee5d54c1e5fa0d8f1dd9e3c0cd5470a9eb9aabc3d78e2c890aeee30f5a43f0d368a1bf3c68710a2ecd5a627b10899d4c53701f8ee730737347566629d85ab5b82dc94b744c52f01337589c1073146c8c5c196226cd21ffa2606dd2d444cd39db3fa160335a73cef5bce5610828f8341dbf3aaee4138ce4d9ab74ab8dc7414ff03ff13b37c76027d8aaac4a519e3ae538e4041326162c0739ccdcc34d709ff6355dd29cd4116a82b1158f3c7b7fd676e60b13fcc95a2d32c147d5779dd379904d83093655ea4aa92c871acd5d82ef0d7a9aaa6fbdedb304972d2a244bf534d556828d8ed6eae3d428c125cd4154f6d493e5d924d8cc2acfa1079924184d3daebda5316ec822c19b2653d59c13e56120c16b8e1a7354ddfa633e82bd14216bcc2993ba588e603cea38cac871d5a4a91ac127ebd6cd5a39444f64005a30826f11f7705f624c2d99168b6043d7f486abc771e4a1083eb097fedddc39a593083eeb765c973922f878a3c34355888a9743b096b921446a86b0bef9a08521f8d16c17e2a4e0ba16820dd5d641fe38c88b9410fc74cede71ec282c8883e0cba67273146a4422b2a155c346182258415609fa8a025a0882510b99f73f2ea1a5b6a196821a5e7c71b523d022109ce62b5dd18ef9d385e42ab40004671ff137c6bfc2a0c51fb4f0031fba44e28f78d47b312d28c1094e70800478418b3e30d162de8e8e1b513af482027f020a0820010ea041c30422d0820facd707fada51bd252babbea0c51e989092b87994e398fca25ae8813d0f9d2ba956d5fe4f286891073eaa8c69ec9356672f9ba0051ef8b048bd1ce5f289883b418b3b70297260b9f55ef22294062decc044ede7cb7612af72e4d581c91b7322840ec96306aba0051df8287da8f9de37a4b89a39b035e6b1e396440efc68748fc31d0f3df78b031f76a4c1c3e4da718756e0c0e7574f3988ba79039f63a6fcb4141d112d4bd0c20d6c3c0bb153c658a91d471bf8386374bc2152df000d1aa6822168c106b62c9276dad04a418b35b07ba649f3e78ff52daa81cf1cccd27d98f791652cf0042dd2c0ae59fadc71143142e56860b2434ae9397224592a042dcec0c7e519724ce304f1370b5a98812bc96a5f31a7a5c7bd0c4c86bca21562ee20774d062eb48d479df4abebbc31b0d6b1a5e31c94e5ee1c6288811661603f7e6039f9a1df64ed075a80818939bcd496b2c4cfaaf9027f7da9c752f6beffdc0b8c87ea9ddad6638798d205ce3fac8aa7aa75173e17b8ee204e9eee7d244d6e8149d11af9a176ec216ab4c07828126252cbb38ffc2cb0d1417e0a139398730816980aa91052725c81db601d8e84168fad3b56603da7bce6f133374d55053eba8f53bf1df46c53a8c0b7c798a3b3bcb6623805eed7a24e2de6cadc1d297092a7bf6b4cd42e2b516032871c76d5a5fbe802054e72ec3b457b2b62f4096c8c9b1ee684e77d574ee0cf3fb68e17eb26f0397a0e34e5ea30815bc99c9ccaa124956a09ec67b0bbce1b3af2ae042eb348e565e8df9430099c45dda46e51ba4a2d4860538a1332b2b6233092264597fbd9077d31029f1562fd5d50350d290297aa23740ebe6b5d161b5a56d08208bcdb56483a095155d110b890398779bf52fa8ed242603bd47e90f223d5cca141e0377d90757248028191f6983deff57372cf0ff80d12726caf57be76f980b3cfda9f63e64a96e901bf1eade330cfff75d2e9191a4c3d73a7c50ed88f7a491b43d2a8fbb4d00123a9d2781c049558b9d12207dcc771ebe96aec9398698103a6e3e51cf9a568e6ba5adc80af1cf246498d61a16c2d6cc06fdda6e49fdda68f695103367e98abbb731c64da4d0b1a306a1d3c777db4fa57b3602da775871e96c721c764c1490a2d891762f28fa458f0679dd143708fa397242cb8bacfeea41df1b2545e617db8e215544f5730295fff9757d54bde0aa6d362ee7d2e4b97c30a26fd2f68f63557c1446f048d2976773e8f2af8cad93fdaee2883aa25157c52777df5f8d4ab6f5470d193e3aa5e9ad69e4ec1edeb46493eff403f32059bf242f6dcffd14fb6a560a3d2b3eb458c14ec5dc7cbfe17530c268ec2a41d888751df8b824953ebe1d9d3816e170a5e72cc8b97a437b72f503029438ca9295be6a5f5135c7a8fc89e6d6afd513cc1698c1c427ddc11630a7682e9bffa7fb73cc19286136cfe58924fbc90e961d321009b602aa55b0fa725b3420034c184c85479394c395f2c36b44e996582f568d2412c4f34686005001301b804db59f753ead062caec7f08a8be38bc104115010260093edd4ab875ac64fa814c084025f828488e23ed50734ef60580126cae98a1824d66cd21681585004c82fd891bee21684a9ad00c0190846aa14a82aa5484162b178f30cb71d81e3b8406d923021009f6eb4c3df2acda91c67f100048b025b94b3faeb2dcf708c023b8f5a496e3cf659e1fc507017004df397774919ae287b1423b084023acddcb29bbbecd082e5456f64f13f973b58be053ca314dabb7fb4c115cc8c813911563ce214c041ffeaf6f1acdc9be1f22788fc777f3b4b2586a8760375e12b764b9001882cf10d3a28455b6108042f01b5639d6cca999527501012440000e48810d0cb8089210bcdfb8bb27eb481fb736b4143008fece7d734893f237cd1b4c8346df783002208896d8220a6c4839dbfea4f7bcf70930d08287400d620b281451cf747c2264afc4618b27b0f13c4de448b2cdb28713b8101d64757499d1bd6902d3d146795817bbc3af30811fcb9aa23c884be0fbc3a412a6953e966e0b25b0a22fddd15fd2affad8d012c1d2618b24f0fee156c59c35cdabbe336c81043ea64abf621dc2852d8ec079e8a1e5b018197310a541a3a44027d0811a36b63042d12802b7f6b1ee65e6a03958e35520821b7fca015b10e1b618021fbbe9c60dc193a68710788bdc9c9327a85f17042eeb44777aebbb950e2030b1e26577766df2f07fc048cef4fc1efa3e3f3e60f3667fcbd9593fa44f0f5889ac65d671cc5bc30326fb858454c898d9ee808d14153947a7ea7fba0e380f11d9538687d999c9011f474b159a5a2a6ea671c0addbfbc79f31ddf47b0326fdb788c4f43cf66103de3f63e84f314afee81a703521e4cf6891c3bbb2050d98ec8f9171f5272ac466c1c7efa92547c614f190051f344fee8f3be8d14dc582d72c55cd31784485b0e047d26ef2be4d2c44afe0a34e2f9127de670619ae60ba4aeba63ca57ddcb5824febaa3456417ab3298315dce4f0d7c390e7d1b6ad82b3143f3f672f5dbf55c1e7e8dee1e44b2697a4820db5355a612172d087e1456740062ab8ec4934df9d8648ee00e36f985940c62998ce313d4aa69b82432320c3148ce70d21b5ae71fa6f2404649482b5c841748778b1a145a5ca3248c1f7db666ac49c3c74cb51b011913caae9f09eccdf3081090e093244c1c5d81fa748be9e9597110ad662924ce5df669b1719a0e07b2beb728e23ff7cf14ec627d8cd928ad1a9513cc17d770c396d3a2ddddb8656044c657482ddb8b91273c675fd8b0c4eb0a1d7f43cf2d0be3f7613ac879de9614729f65643134c8e791fa5903c2158d686968c4c30212b54c4090f4cb01a9db284a03d9d630f19976034dd4fe720e844a82c2dd17d943e8c7fcaa8041f644c1a9e319225cd29c145f6fc5af7a15658903109aee34af7975bd779223224c1c773ab8ea27f4a79294f901109cea38e43ce8da300321001aa920109feb7b2ddffb5e686ac0dad47b03925975c8de4d29ddbd092e108ee4e5267d45d26f8e2063540462398aaa47b13bcfa83a6c50a3218c1875aa87597d0d04eb1a104d12063114c76ac67affb396a6a3214c1c4145c7a3c47d9276b222311fcc79147de5551df8f36443099fbd1bf34e738ec900d2d13d4d02ac838041faba7fd943d8f7f60f91b3550f037b0c0288108c2480102641882c9e94f4bc553bf68160c2c13a88069d03085e03a3d7f48f7e883640f427097c3d29ae47114e99b4170afd95f3db43819924510bc9e59f01755c9e9d340701ea7e8217f5c17ad1340f0232d16d378a45b1ef707decc33871d5afcc04da8ce4a1592e79ff581dd0c396bb01c3534a48e20830ffc897d8eaae81c1164ec81cb0bb7143972a3e3e881db1c4dca9da9634d56e781b5f34cbf4c271e38c9891dba488c991eb321c8b80393a93e7a3f62a42fdd0ebc841ecf0d3eddf8301c50429051076e5fdcbd2f3d35d2cb04197460b44248048b106f63670e7c0c19dcfce307b63972e0457210dad712074ed47376cae1a6821a0bc6099220030e4c720f730a3948519ae30d7c941b52aaa54fdab1c30dec4eca4d8f0ca934a50d6cfa8e3abc70ef8c920d8ca49cc12362ef25d1d7c0bad7470e3b2a5d658d1ad8f4b6541de48b7a1e69e043d2982cb38d062663ec5b0faf3d0317a9916eaf927bc762063ea7b40e3f726d115bcbc046571d8de696e3c92619d89063ef88517539a78c81fdc971074b391403d7154f3bc689ac0a590e6484810fb7ae3fc8716c312345061898ead0992e34527e08f90293acab373df454d951bcc047f2a852acd0eb0eb92e70139d1d7ba76e139b8a13647081ab0fbab7222b5dfe7c59828c2df0ffa3ebe1a7dacbf46b81c9c9a68388bb9ada6f828c2c985ebfb3c614752cf01727a5bee0e615d83aade497d2a5eb8cad126458818f3c67c869e2ea7a9555e0266fd09427629210724a10645081cd923a2544bff4513c409031052e744a172765cb1d644801d79469973b74bfa2608e4204d38a7af74e1e644081f7387a43c83b7141c613f00e7763fc74cbd186c041861318f3da548f269e432d29c868021f76f0b11ca41026b0dea134664689983e2f63097c87f8593b322d01194ae0a349f60a91fbbd9387cb480297ad5292dcfe20423255200309dcbbc713a2daf122057804a67386fedeacadfa591b5a5e98dd9061042eb24c520e4f43b0c617a79017328ac07ff897121df32a592483084cc5a0c93b4ec4206308fc74eef0fd345c6faf30c0c01b17061942602d47873479d7b6f36443cb0422e812b8d1a06160901184bc11c609640081bdfdc863d39ca3e7b4c9f841d1380ec8f0016bc932e849e78ed3a5d85094d10352a5c86a39c5552ca5985f629bb2ae78182d327850344a2063075cd0ef69fd40d304bda0000d1a5e7ca1033eea4b239d73dc2dd1c941f952534cd14b9181033ec2d53a961ce49a8071822f6e2880861764dc80ddd51fd714736cc04dd0f48d922260b4e0215003f62e57d2bc4b627eb40c1ab0f639debcfce959b0f719a287a81739a6596461aecc1c9235e463c1899f4b0e3df098757360c146f4493f62e521c4f82b987c91e17edb1dbb4a5cc1a9875153ca881c89e53b10a3157c20b9eec3effc38666a083158c175ea1cf8e5f41539d12a5875cfd48c1c872ac8e9a247693deca4828d93f3e68a1d840a3eecf190beba8a1c9953f0b697de719ce247cb81a6e0527af13049796429644bc1bf47fff529450a266d74cf3f9fb47a3946c1696b4a867774dac81105ff5f9dfda31cc78fef85820f4453fd861845f553a0e0a2e4e63077e81dcdf9049b543ca7bf34f1043741cc83df8a048db94ef071e6ee4bc92dd2db86137c0e11be95838aea286e82bf109afa3977844d474088a109566ad43e87961de7e46782cf51c7ff1e25a3fd02430c4cf01e3385a4b96ef688ee20c625d8ff8dcc4ad25b820931b31edb5c6be30f625482c9ac759d1cb678fa8a12fcc79a83b477a16ecde7418c4970fa39cad5a5e3fff84a82f315dfff0aeeaef6458273f558cddcf368d804124cfefc28cb563e824b8f72f06832a406311cc188496876cff9e3b5182083428c46f0392a7d52f60ea31f821b2f011a343e0437fe86092410831132622c82ad4b298587f79ffa3a5f80e1c5179ef680188a608207f9beeb4be27f9f08f64276685574864a352298c8b1684c9321d2244b0c621c82d58c1b993f8e3283188660534eef61fe39ca5cf10fc1da00a31b50ec046d8293408c42b05eae9ab29b7eb0f65c1083107c0e521a0f2af7f2723106c1f7a40cb1bd92688ee245108c8a487bb81b433d0c0682fdf30e2a47122446ccbb042730c1eb0a6200824935e29bc792f599f4872d77a59c13598211c30f5c12c9a1beabef2307e90327911992f998fde7f8ca400c3ef0d571cee31b2ba742540531f6c0a5201d999dc72195e21761d4d0400c3db0ada9fc3af40f21e3c686d6560b6e84e105df50407a20461e98f459f2dfcaa3f668b3a165541c030fc4f24fea1d98749b5ad151ca5c7163d881499e35a4ba0e543b4763d481adcf641a36a12c7631e8c0a5770eb71ca5dcd6ce818d1d78241db9ffc4e4cb81f33875dcef92ab4ad438f025f1e326effc92128c01072667650b1e65e70dfc66f29e12f7781f8a36b46ab4e0461855e5c68721028ce186a27123461b8e10d37878125ba52c8dc10614418c35b0da7b7635d13ace51f06e847182186ae084d81fdbdb723d687ac0a4474ab5137358370f98ca3455f163f59c68ed801f0f24df5d8abe4eaf033e65cc3928a91b69cde680dd74e7414479e64a8703ee62dfa790f81ee53d6fc08765bd1e5accdb80ff18435946e7ff842e6ac09bc52ccb97d9cd3fd5050dd8b43e29538e37b5c7de2cd8f10b39e5b64e21de4416bc068f334c73782c787109094d412cda86b0e062a7ac7905631f5db2ed567dfc962b9810dcbcffa43e7914b582759fbabebf7ef578b182ab0ce1919e443a225a05aba9a3c91f84942af8287243b03896a3bf2b15fc676ff98777bda95f5430593dfc983ee385f4c929f818e2755489eda9ac630aa63de906dbcf9f15d252f0a93fabe4e873887548c18d69871fa558192d4e46c16ad45cd69fa3cf5514051fa57e3bdf0e0f053f12f2f29a78bc913150301d07921d3bdf4f70b749c763a8f9c7be7a82f77ad38d1da7b5e2d8095637454aab1c524eab73820f338b6da478cc8f439b6027efc6ef730f25a9d30497b26f2a47c9ba827d99e0b4ec27c7a7d6917d342658b55c21e488e657ab5ec21da9d1cfddca12bc079de8fe50bd4cda4ab021f33dcc54a1045795a7ac2a64d393dc24d8ac0b4bcdb210429a48828dda99932ea96ea245820f2443ce1e7273d89f90e0834cc13f94c435dbef117c7fa89397a12398aa8d9b39c6ef94956ec4b6951d5e1f868ce07250215ac83b16374f16c104b51f8f7451e37e14c1afa4d6982a485e0cb14470aa9eb6adbf529cbe11c15d75942cef238474218760b296ff6708a9a31ecd107ce4f04232476a3f5208fe4b52fd7ae8acd19184e03c49d49f7478d49406c1646d7a28f12357a85410dca4f5782cddc4985330104cf2242987ff1140f0123f3386ec51e65bf903ab69c4edb66e3f30b6aada41f3f5866e7de0a3f65c9ac3fdb83a8ef081bf0af531ee9d7b16770fec786d1cbd8ea342533db09acfbb22e4e82c679d072e7f88df399e1c6da58207f6c3fb90423a4df0cddc81c9b17ec7b40ecb999376e073eac07463460d41d275604a4377c50f8f0e669ee413da21cd81cd517d5c1fa5f3c8413930a57ea269ed89039b3f68c72d5d9d3a391c78f5addab28cd48da937f0badd999267b6cdcedcc04da8fee61cc7793c8edac0bd785bd2f43d939ab0811d899d3b7f948f859035b02a9972ffdda2065eed5b227a85a8d85f1ad8b4abfd8fd16f2131686024a7a3b658db19d80e2a786065de1b21c70c4cdaff68b93b5dae6d19981c1642fe90430f92c6b4810b323069dad1bc620ed4477b021763e0420e7423b7e4f4b05531b0be913aeb0335ab1c260c7c6fa48abdc4cf6fcd085c80814fba13a1396f1acff611b8f802ab793d74070f31ff85602811b8f002971a237d501edf05feffb2f452b4bbf85b11b8e0029fe3c9da95742b04cfdf71b1852db6875b8c540baa91c5ca3dc8b4aecbf179f44e0596052eb4c06f84f6a0335bb2c0beb9e438654754da1c0bfc756fc6b4695d4269a7821a755c5c816bf1e87f2b7553e0c20aece7d5a013f43dac7a2eaac06f4f76cb79a316dc08a3580b6e6440095c50814b9ffa1c84ad98042ea6c0e5181ea51845b205ef7002175260cc3473a8f7d8e1568256c1e32fa018180be0220a6c87f7de7a79c392001750603c2a5bc7a1d1bb175f78516ee0e2096c8eab59a2e347971ee5e8c08513f8beb4d0edde91bda64d6062b00eab3dccf1984f26f04922655aba70097c94f3b6e498590729a90cb85002ab97ea17572284da7812d81cdd87d7de3148e0f6f2a62cfb5d4e398ec0d8a84b5d0eff32836d045624076e9da282c664725104fed7aaea7220991f4e99022e88c0ba8f647d9c1f736668089cf4a7b09832aeb7466ffc9fba2b7021045e53defd98c63cb05f04355a606a7c8d138081803058c0346884c18267021741605b35da3425c9714a2b10f4cbf0de8eea9d82e4074c072d77ff304589313c41bf8dc2850f30895426d595d54582058fc35d3aef5855da002e7ac05dca7fb92cbb044b1f3c60a2698e42881d1d224f3b602f9976f2d8f1d7faab033e1ccf57a1b2262d015ce4802d4bc9192745add60e1cf0514788f0384cdbbe19808b1bb0712be784a74eff5fe1c2064c5ef705cda3692c5f8304fd370c2a1735e0fea26d79678446995cd08091b6d2ba107f7ad463167c5df0704f296f264dcb828f3d0edf1a93944e3ab1603fb7698e2e040bae53ca1c2a278d9a35fd0a36e9a6f79da812c9ba2bb87aab4b9a1d589fa55bc1488e83cc519c4c99966205d3797a325247149bec2a58d790aa9f91735cd755c1e7308756eb92f7e3b8a9e03bc86de989d37a3d41051b434eed3a9d5bffcb5370f9c3f1ac147c63e4ce149c5ea61c59f0b092a9a4146c99ed66b6ab49c16ac8f651d4db7c221a1b5a27308157dda0c028784def9148546aea8ba26075b26ace1c39a6975c28589398769346eb902e070a4e33e7f38b2831f74a9f60233b27ba04bd9d14f344227af992c7547782fd8e7d6348ae15a2e6043b95fa9563916c82d3a849b2776b8e0e4b13dc4dc8215fc71d87923ab6c804f713d3377d185539425b608231ff1c4dd5b24a417209763a5d786f5e754c59820f5b7d3563dbc6785a25d84d3fcfd9a9a7049ff9a945731cf9438a4e827dcdf49421b5ffd4280926253ff390d3c31c29468297a4a3aa1d0421c14bc762d32ee26db9f308c692a9a85548f7149d23f8c0da233d48d6e88b36824b27f9253f744b79538c603537472d41832691d422faafaafb9e7cb19e28828f2a7a183cceba0fb648c4ea9e3afe30a6902d44707e1e66ec8f772f54c38b2ff6107c1ca518952544eff28c0dad6ac11686e0b52d3bfe14d19dc7f285175f100bb628049fdae3c8b5c38e2eb74f082e5b8a7f602939fae83308ce83e538834d6ccdd90a82370fa2d363c92092da40f0aec9ba43840e39b20208b6ce3b3b2c85fc81b75fdd907344c714a11fb8ad747617345bacacf5815dcd7142d0cacab12e3e70d9c672c4d71ce70ad903eb351ed36ba7e0d539f4c085c89df6246bc6581f79e0ba92b4c6ffeee09fc70397632f8f237c33feee77607c253d7f59eabd6f07feeb9259c776d1336eeac08e7e94c92bc7d181c9a8217dc89243a48e9d03eb69b53de6ab1cb88a112a75630efd3c481c980ea6a143bd14325f07072e94c5cbb7a7963eb437f0bb1d2276ec29377039ccf210eba22ddac0b7d84905cb528d1b5fd45841eb03b660031bef3695d69494a66943cb8687f14715a954c0166b281a23d8420dfc27cfd21a375d638b347097cbe30c16961dafb4051ab889f57e916dc7472c26f812b009be04df5b9c81d3699d1cb97ad091276660e3c7d08e4ecba1a1c232335b94814f3104cb8be169993d64e0f3bf57523a65e44c8d818d153a721df7f8a5d362e02fbc6fa2e787ff4718b8dd581bf363aeb460010323fdd9ab2e8259affa055e3fc791fe29dda603bdc0a61ce759db445b74818f286697cf22223c2fc0165c60d5f3f55f799424473133026cb185bd7ba703b7d00293bd3e2ab524e93c27177001016c16700b2cd81657d8c20a5c1649214e6b64ecc8ab021f2ade872ae95597ef2da8c0d5844c9a6e6287669d2da6b0782cd18f5207df2e0168d8420adce654e92687ead4f76d6881f1450368d008038c2f6c6c5dab50852da9b5ba2b49e896b252c1f87914214e56dbac162af8f70ea2dded34e314dcc7be769391ee2f6d669882d3bbf530be76ffe537a314fc872b3974e8890d85c10c523019a1513ffa18a5d368432b5f306314bce70f238698f61af60944b0026cc10c517021ea29aa94be82f7e2eb923023148c4ac88d54c9b2a1558c3003146c0e2ffa68e8ecd4ad9308333ec1859c4672a816ab8f4b4fb067c99385e89ca3943f16fcf142049de073a9495876e421677a083338c146dd9ca3b3bcdcdae02658dfad3cafdb1ca51c3220ccd0049b3da8b78fad337f8c3e1899e0fa23f7f5d2f0cfd821f3c10c4cf0917ff4a81e64be50910dadedc18c4bb0318e5f8790f2e9595882128880049660a375e057828fdb3cca0f7d735a3d94e073fc1373f841baf060c624f81feb54d56ba9ca8292e05be2b6a5d0b93d3a1809b65e926dd6c77972ce418277df4b9334ea94ed0806ce7804a3faa157b9a8684e11301cc1c67c7d9f3f4655f0d0f72ec1091ac107f935da6128fd1b0c1194e0c310810d46b095c3e41a72bdd489768be0e39eb8c792da63f78b2298fc7142fa69b714a74a04eb9126d353b2d010238608f63d4c72d7433031edb4f459e4ca21c710fc59cc69f337678f1853083ec51c3f0a92537fcc174230315e84e6cd31cbbc7971230561fc8d1a5e1c354c0046afa0c687d15f847187801983e0d48327c9b721e7385b13e8400d2f660882d3e84107cfa4f9e348c18c40b0a299f45e2d3a5735bc104108de580920030c10c18d3014108ce5384c9729756ee738e30faca760318478ac3955cc0c3f701fd4f95b4927f70be9c08c3e70e91a3fc8d7ce7f9af381cb96db3f4fea778294010accd80317af5affc54d3d70a61792e469280ffcbbc74b97f5b4a1f557801978603a44dacd3a5feec08606fb20cc427aea583b789bd5815964b60e9cd9a7cc41e68d187350d281dbb7f03089edad6b5f80cc81cb91e3690f3b5e68bb37982107cea7e3dbcf0f5316f5c481f5cbd651bf0807def6530e3fcac1f8a2024683196f4863cea3fb1b295ef56eb0eb4f52fcdaa3dd86d3afb667d61c7b1803aa0933d8c0b45b7fca36cd1af85493e5bb96560397e3d843f4a49039424a1a580f37f64df054f11c040733d0c046ca41736e8cf5964c1b5ae3461827a0fa9c71861966b8196570c20c325c61c61898eec8b5693977e0e10bb88000948430430c6c908e4f2da4d60ef5230cfc779fe75ad1da901e60e003738b96e3ce7f8177d790ff3ae7e0717dbcc0a846c964923b2849bd0b8ca4bc9672f24548c87281c9396aef0e1d4f3ce718c18c2d70a192e4b26c8916b832cf1e5723c618449b91057efbf28a4db2cf0c1138ccc042d100c18c2bf02bf935446ad477fc0180871956e02a276e96d75dccb13ba30aacc7251e86f41b665081498bd4d4c9d1c7671d31cc9802df3b218a6dcce176ca1c33a4c07aee58439218d16aa20fcc88c29bd2653febac6d057da3c6ff51e39d64610614d8eed03943e8bf14cfd8d0221dcc7802ff3f91263d67eb47dd2b6638814bcd7ba995d4d46365435100091080003210813258cc68026bd7f17bb9a498c7ab1ae83398c0a73db1a0aafe3a21924be03b5a658a65db3152c28052a36e7c1829f8e246da608612f8ca557de3fed2a7b124306942eadddeb41379cd4002ef61fa4795dd1f818b99fe3d43aa20297723b099d73c8957a6084c164979427a89c0ee7daa2bad1006cc18021fe78f3c9ad05935f82f04763f689fbeb4e4b97910d8dcf3bfdc1ee8e68e01026b269ac4c6434b1e627ec0059514a953a43e60e26feecfd63bcd34f6803f8ffda5b5e101b795233d8fa357cc931d3041b3b6a4c8b1660f2a1d30ade951b874e480dd9c7254ddd81b27453c0307fc4f08b97b3379d2d167dc80514f49976c397b1c4d6cc0a7768dab67550d6e912285193560573c9ad8d2516a8d05c6173666d0e0e891776ac837bb9805932b273fd2d5d2a770025e94ea42165dc4828f3b47c134e4c891478d304460021194805856182cf8eb4017b0e0aad43cc716cb8fd0dad05a0974f10adeaa231189123df50a812e5cc17a5dc7410cf78eaef118d0452b789fdcae8f13c6835105c68b200c115c75c10a3e4573dcc9b3b76df6484217abe05c5a33574e8ce96523a10b55b096ed11378fc6cb9db74217a960f37f867d88b977f9630a5da082099110e2715316e8e2148c7d5cba91d1c25f2d36b46a7c71ce14a756d6944e5f4c5b044217a56073dcb13da285467b9c0dad1a5f1cf24517a468a2f47bcc61d08e94000d1a35c070000d1a3518d1c528b8b71a9d48b7f1730ede95e942149cffb97794640a9e34a1e073becb21de74b851920dadd3052864d0c52750d08527588fd43e49e87daf8c68a18b4e70973f50afeaaaff461818b8f109f812b453a10b4e70f766a9736bcce7d1bd09ce546fc2724479084b1762ecd475496526f8f8335245e75842a96742e80213ec4bd2dcf1fb6d8c235e820d91ba637c0b56b13d4bb07629a78fbfc3c81cbb956032a68e0ee2875282edb07b739cce49b029b5a9fe8d0409950a421792e02a428a34296a9b46ebb68b48f091799cb226ff21c1d757c6a88defd1e491075d3c82d71c7ddd14c33ed0181a344e055d38827df1c9f50c218de03d2a7bfb18274670e5c96f35e2448ebffe41178b60da4b42fa1c7b14df530423b9a6c6ecede3eaa044f0e19f1f07d11d8860c33fc6d7cb4153e5c72198d8bd4957f36b083e6f6770e90e932e4b21b88db911736dc81c844808a62ec5d1e41dc759a50f829316cd9699a305c19bab59cc61fc02c185104997ec23ebc91840f031b22245d27846857f60a543d5095629fdb4e707d6352ba4ceab9162aefac085a7d01c879eeba38b0f4c0a21cd345bb3073633dd251f0fd203df17dc6ffae3e82ba43c30b6eba7dde6b1544978e02f2ba3f8ff55cc90dd81bfce29bd4f58bef7edc078d041ac6fc92ccb611d8812fe1fe7130d1df8b4cc61d99655299e736072caefe9636d21882d07ce4a5b52c7b68a213f71e0a2e59b8e53f6983a0a1c98a459fc3dfcd0d4a1bd81f397b68c53413f2e8d1bd8f30d8f1952c816dd6e03571aa78376774abb0b1bf81c05cb192a56b09a6c0d8c799b447bdd6c8b143570a36ef13a5a9ed4a14e035fd751a3c71f47b1328906a6536458b96ab546f10c9c5a76f1383cadc4ac3103ab59b26732b5ca6db10cbc7e4b5de85879ac4232f0fafa29a6e9d839da3506a6b72bc7142f3da46e3130c13dacd46d6e18b892e0492ccca3cc493030561f4c62fa0f720ef12fb01fa5476f1e97565aed05ae3c48636d1393fad5053ec7119d2e6a4b86d65ce052e8f873f812629a856c81bb94d3cf42d2de8f7d2d30a5913daf7e2cc97d6681ef186eb16e92fdb58805ae734364a607966cdf2b709e2566ef454c2947d50aec74a89f347558d28ea30a7c8ecb2be71016a96a4205d6fa2f6d9afc49f3fd2930917b93875e52bded4b81d3d7f45aa15214d8fcec93a8e93e6ff28002eba939d4e3bd60f1d427f0a9366597d9a7f37c71021f1ee55033f8d54513b8ffb0786d12834eee74c1043ef6d0b62c77524bb42e96c0fbc53829844a51026ffb799e2de593c046d445318f4cf9554402fb9555af133f47e053e5cd39f3a23caa6b043e5cfbcf319e5f044e3be54797a3b1b411420426738a0ebdd3857f7443603574fa36b7dedff185c06a4adb92e5215f4e5a10388bb09c905679d1a340e0fcc2f3a49af903c6d3c5e90fedfa0e351fb0058a6aa814af44c2c14824108683a1602810060ff036f31308001828280bc502a158389b773f1400034c30243c2a2c20202218101412128e44a15020140887818030281408850281905040240ae2f900f09921112a1907e2d88db5550fd942b3f00358670ef788a714eacdf453802140c52755ef9b5e80d933f0483741eb3501054b78169308e71bde28ab85888b86a8f44cfc0e383e0e4975ffa059983076f7060a0bf701a2a139d1798ae805fbf2cccc1aba89efa4a628e609f94bd92de27be23cf516cbf9e21395b22010a842f5accc106161f93f121f6a7ee29d0189ef89c975078db075e4f5d9bfdc7b303a09851e3481ca088e434e97a30e21720e0c2e6b6cf10fc5caff705a110f43a1bd250d21d3ace5f6ba2081bd8c47c8b6fa61e1c3d2c1b227b72a593604e3e41b7c151ade4434ccb951f16afb126581c09e9ebca1b345472cd46d896f5153f6d17d6f0f8b338dc04a28e03ce458a026144d57623fc31b978650200ac60a26b7f2792accc76a0ca3a844475cf8336f0822138966d3105e6e7bb035c979a6f3e808774c410033148d6472826426cfb8be1e0e3d3c039261c080e09599f41f21411375f00b51f103fa5cb995fbe08aea0157e7b9c2648228488c1babfb6c721757df35bb0d71a8b7cfc50592facdbb589fa6fc4c1f9a328fd44ef4e09ddb6f614f8e5645bb8be585c4ea3f21688f0ad04ba6a60807948d313078a0f28be8aa84b224ac95e1438cf053332a33dcc2f3da648ca0e2b797cba308e3a975979fbd7e8af7e6238ea2cefef6109c586a16ce42101a43836d37d1c34ef491775837f97abbea5c850c9b38278881da447cb42093f029cd88d211f1d5a0a3cdf183480d1a45cdf0e2aa7d45c5fd2932346cb0908c21d04b3d8377681c6623d219dec1f8b3e8e2d76c3fc4399023e48cbbec1e93cfabd61c683ef09d488e2d1799a68eff6194f8de099c2db2540bd33c65b3ebe704e4a9831b346f341718a8295070af4d4ded60387e4a2e309cfe5ff0a10ebd9c1e56cc64dd74bb8faafbbaa0a43b22d1225c50e8388f8505b07c5e630015987565ff90cfe26f7b8c74676bb87a29c9dbface45ed2efdb11e6cc5499940bae2bb8bfb346782199c89684768e478c2e67c8efe760dc77bfb39400dabf0aacd3cabee8624e1e1c515a1d1c1232eb2fec6e3eb11b7c9b0da48920f651c7b935033cf547a0e475fdcc7d887005ebf561cf12a90c5813bab26362cb2fa42203471e68114c299cc64a4cb866bc0049841f7ba4b5584a1abe177af86573023205b5a7c125099bef1c88cff3212d74a2f22c0f90f8f657cde78d155cd94edaf079f304410b7b496e48fe147cd21621886a77c922c20045bd0a5c267089ca31935eef90b64394aed5ea5b180712015a97b711183e030bc1b8423db4845a6bc2b9833afbf179ad3d8a07b92bbecbcbc5d0d4582f59037fffd51e8484f1eb9030cf73aa6b7c29bcca95ab30c0814ec94966483ebc0352262aa8a61b81deeb40fc75afaeba0239de60f821c8d6af3e47dbef68020e82271d5482834169cf26534625cd531efe065006393a5af50800340428ce80319eedf3f262d7539a2971daccb3944527b05ef66f7fe40cf11164b15d102d5d9101589fd434ce4f854ff699fd16087694a0878cfa6e34c732039efabb075c6f5fbbfd79169294a4b9b02397242a06d966ec93a81a54da224d586da588b364e75b2536db0dd8d6ec257e41a405bdcdc76770d307b2a05c473e5161d15e6288070114687b1300c0890d75313938150a3447cf29268741d957085da459841187413c3be30ecdee54180442110b146f129c8964f24ac0af70ee942002dac1aae0995a8bb6d202089a538af3b9357fb31f1348f5c643e332c89ab5a9295de1c821f8e2098ec266feb50adfec971478d8bb6277998f3e60583eca2964479ca73762ff8633ad12f822012d0f908b63cbd5cba0d2af77836234889308c3ea3c54057b90dfcd86813553bf8bb287cf4bb587be10c9c1365b5e1bf6b03989853d014348c975e2c585600781be4d23b8dbe07f77e820846fd0db1c3e34a7c62ca97d2ac4e172a267a8ec44cd7686692c2c3d6fc315027f671aec515ba1bac854923b5f560d42f0aeb72701a89d0edf93dc67dd80d38718389b40ad495691dcf3037832a93aed886c2e09bcc244c5c97a8f06b4838533c970cfe441c8f31a0d0ff13101f8ef592fc8648e9f271c09ad2f6ce8c8c7f98d5b24478a56bf9551a743a5d0ee71e2b926d589d04f60fddec8aad47b5cbf1365b488644f431e2a734acf0e784f43d3a089f26441a5082792541a44e25bd0f4279471d8080ddfee3a9a7dde6312d0e64987a6ba407079acee8beea306022bb1c75dcfb6d2911f0638ceb18107bbe9cbb1d8438aa78edf94812594d30bb0455eb62f6414b97d2b260c30aa506a132fad7090100ea569029e5046676b8994defabbedd9632bde2d4d64479e1a74b848dc2cda416f9975d8405acdb15307cc44251de6df7274eb96c862187e794cf240913169c824dc65fc9269bfc79e1227451fbe48f4a2d25a55ec493c4f436551b33288301775642c9cc7da0d950afc1b341e655bec91aacb263c49bdc5cffc54a1ba6b880ec7fc28180c2115f220108f4d7af541ca82585f3f0888b9634094740c7654bddd4a8299e04e3f50faa245501cbd0539f4842fd4361a1821a14d0fb13916c3fa838b476594d092a1d4a08e501cacf28e92d053c9af0ece1878aa3fc4b88ada1a250cf50438afa95ba160ad5931b19a108f50c85800aada89e54165414aa0515835065f3a176a162500d5085501d5079a878a816548a3fea723712097e2f0a1e8b6c3b509145c5a59c4801cb3b50ff5056a805a831d477a81fd43dd412ea438abae77f42950f350e850115e5a0801e2a88c193f513d19e725050f201d5b7ecfab40955b341f5196f58fd01e5ce46c501dfc06312a80ad43c94144a014a7e42a15abd2a9e28a1bc505b42cd52bda106407da0f650cf3e0ad84913c2f27fa82fccf703522b149e0443c915a83e6d3df439d4e346d15616a251f52d67190a06aa0a550095d241a12a47aa6b2494070a1b2a0ad50e55d0a86ea92c546686eac3c82a5f16945d87ea4384118d395011a1501d008a0f650fa5821287f284f28212d5176d064549a2688b794ab0325075502e2877286528604638eacb6e9c8b484c910dcb51c18831376a3d1477a3fe6501cb34a8542830546745f5a4aaa0925021a8782caaef63d51e2042a17e51658b8d743642e5a1a4a15c5082a1bc503a5072280728010e05bcc723336c549fb565de4e3ecabd9472099ab09119282ad413d41bea086a04ea7b41e9305d3ffe11541dd4918ed26154d98c635009a8fb400169ea0557301549bf4902d194055f889bf767ca6661c819c4df18b283c511ba0a64e4bf061e6c197268dad9917a522f965fb89ec2b22e892e3c7edbc9cf773536bf2468179df34f04febbfa8e802037e634b71c1c0098c2b1c95dd1027514f1baeef9e3555e38c897f1550450a0664b2f6bf5bff158ce362025e8a7f00da0e12b0752842d732a8d2127098d6b4f39e1d25cc0dc6cdf1a5d308a8fc8994f4821982322298d3e72335cbc1c53edfb3a1977600d44d119101f39c4f85923d4ff5aa310ddcac82f59a2b94191294c329640376a0a0587bc9db2d5ae6a84c4b6306245e6edc70024f3f4e8c5042e3beb9f0a21cc1fbd9f119284c83a9a473ad477c4e941322793ebca786adb34ffe32f430ca036b1bbdbc532f264764fc923678655cbe859a442a419c9f176cda0d506dd4bd7126c43c7afc8602defd97fe3e215a171dbd84dc5042bb0b06f18993a7b42093ac129f46c65597f49112acc084a69d8577088b7d7afcab126609b5446d86d607f7162044a52de9844cfa08339aeef433b4dbfb1f6b7bc0aae1ea611781afb56919ee399f2967e9d68c8e5b552ab242b515a3599b7a3d000c1aa665f3d7878e1e0e60f7cd65423f19c4e761c9df2a09479083656cef5a97236de2eeaa50b32099250ff4dd7cc8c51d76392d8f2c9e81823eadf1683053ef96f97158a904a5aa065b1c6bcdefa022fb1f22962919228218641d013a94c0d3e4925c5a9650458a06aa31bf86f9f6e0b3fb35c11b79317f23af4dac32c51c8026b086286ad4ed9bb077dbb40049c977bbff5d03ca0247287005b1f64c8480f7457770ab09ad2835485d545b1fc8cc703c0f1bea9c8eabd8e63f2ec134c9cfa4ba7c42514673cbc2b1c554373e21a700bd7c1df7e5fca80f6ce86305594af2de7ac1fd00deb4be9b15213b47a85062504e9288ec30935840d22bb1fe541c6830d31403698ae4d5af0028807e065f948664ea8e01668ef22951a9d830d466022843ff8fa6d61f49e9b0dba1a29e5b1ee2ea5348c0226ddf5973854f7a8021220d16d4987cb4f1695b30e3ab05b2ffae0cb369abdbfa4e0d5138d07c6807d49c7b677f7de9f3ce24d81f1ed2adcd5e43ecb1de08baaf7e5bc40c5b988169c28069f24bcd40517b28a1c4186916f1039ff963779450403728d1cee135e4eca148a08a642074173831dc2391adee3b1c61605c39bb07d80d888b5105f9811ce3c42ae38514521bf904b2bd40829423c3362d651c0740e6bd4cc6cd4339ae58792acc8eeb3d4fcc90d14880463843cdd17a103c2a8e800c78cb443117025cae3188faf358c84b5d9774ccf623e78f044a085c418443747f9fdc39091e2a255ef3c590014ece641fd7f090e1bcc080275e00c1e78cd08d2ae2b4e9c7558861da392e016491217aa34280d94074a1c73d09c66e303d60195c97b778f64a9b3b7e2833c6b3cb0b3e6067945dabf0a0fd22c0ee089fdd257502424d37ce71d0601d67c0a3ee243367e96e2f059a7893de96de69362ff23606961db191048740567ebd2c0d4387caaaeedec01daff1140bdfcd9f76b608414c0940691d922d888b345e8249de76b24691913d95fcf9a50c5b5389dfa3d85635ff3563a4251704996f6089b666e96e9d2c59c72b0e2c45bbd7498183e2e0c041df4e1f38db0c134fd63d856cc4fb21996f62cbf721c5be34ceb4b4528f37a3c759529055e4fe4f0b1223c9193b84086f989144a133698f55182cb57cb0d0191a3bc28882dc262d8b481f0563d509aa7fbb8d4d6a1e8a02c81faa3be785d4e8ca256d550b52d59404b21aa35f13604554ed716685309820db0005af61c1b8adbe83473b470d59f58493a7ee73dc3d13c39e1e3305e0a6f789f0ef39178b2cfa037fd92b7d71bf26881e72fcd23f012a2851fab64671ae9b74575b0869d2571e745c6ee4241dec4677b1176c88871ef6d2aeb25a89a852e4013af41d94f543466a0651cd933c0dcb30d95bcf069da2a5ea6ad667f7134ecbcf6355e4a5bea2f8db23ba7bece4b668beecf1c95fb1916728d22b1bb5c64450668cd5a96534d6cf7a9ef3802efaa4c01384ef54b60d9f0ea6affab05bf76ab957f0698649696f7b1b59bb1454eb5d428b6ad450d0bc9e29be1af290329c3804d1c31662aaa4a903872e69ff5aac0a030442636a1514c23fa459a4b558634ac29896b6079ba147c320747635cafb648ea32c9a80e392eeccfda8d8b1c40ee6c28c48519346e27a16eb3440ca019a6e5cc91c4e60d26dcc964ba7f206670367834867ea3af2640a0f539598050b2fe3a5dc3c6395fc32ba826737de546996a99c45f0aea7404102f0bc3b599ce817388d9b224fea8baada44aca54122ac9f9fdd3042aebb8d60ae61ddf4a461cfb483a4e83b5e75d471e92c2a260960d9dd353b006c04dde2346c6d246cb656b083ee8c661d22d6e845de9caae76dccc242ae191b806882224811937e35a8a86384fb3142b57cc4573f013821c6d33a267a6b221638a6cef70dcc266532cdd5567e15ee19de3857e9782c56607c71d60155a4728234d5016098fdcb78b33c8355b9e6c880158608319a0f6bd8b657750fe75fbabb50dbf6263920bbfacb788f4b7d6f0b83a2f4a4c607d82af507edf41b7658e4d9d5c2c4704cd711156b6207327434d83af567e9e718d3da22f8d5f1b99b1868f9e76633e9ecf423e2b43ce530eb2622b57d40137d7a9c99e81ff5469c3291b12fede5e2252ebf995992f5e25c8151eace4a339db6d83441bce70ecfd29be5e3fb1e0c33bd3404c8b6f5b5bcfec03a373dc08604d03b11d08ce7bc86a5e20cc26b7e8a48ac0805be415fd7ec6460932c691400940165eef0c5af191d24a5d7a3a0d56eb0bc22203810ac961572991bd522758107813fc12ec082e9b82188fc177ff18f820d034ee2342d73a372673a6ba751fffa38e9da00bc5f4635f56f3968b9a384ec45be220941e5d497f724cecf85bf3c4ac3456af6c14ed0d44f301e4825e8736e1c4c794f79a18bf742a579f616099aca65124ad2d861694904b0a1c681e055064081c93e1dac41b1b7b3a9e6194936130fd4a27e8a8d6838e2dfa983d89ce6f29828bc2a85553a7de5db5aa765119a762ba32f25842d245eb28ac560563100bf5412f40e7961e83ca206303770a280326cc6106fc10be31d18aacacca84fcaccc5d0aafc3c90417846ad0f86192e6edcc957ceb2a1f9ea84230d0eea91e32225b351e5e18362c04ceda81a17c38b69ed74259127102b446cfb05a0e7aab72654f15db2d937e78aa4fc1844247132d1644acd0291d2a09115682ae2f39b6a2fd8bcd8fd8be49a7b0d921fe9c127124fda29f90a82c63f815e96031530bd8856aad1c2125436e14cd3ff5a452ea57152fe84a1915d1125d5dbf5ba7c1b9888990e488f158c4dc909e04599c19998dca4d579ce417c81c13d8eb822ee1a05a0753f9e0bd364c769c2f904c56476f905d79c1378ce7e321eb892f676ef839323572fc6b7f5c36001e613e24bf51edebd8a4f5f6adcba6520d47d01224e0e565cfd2aaa159d468505f08f00144d74f7ce72808df2f165851bd0f3c1c270311ab486585be9e39f21978e6d23ae10d9d6d4b2ba6f984ddc38fd8306e5c54263a588dfd4a3af360333dd8a8b3ae78b16f173994096ecb7ccae42163267f3fa2490f0c7e039adeb351b3402faed72d37d17ef0ba47dcaac44960d2eccff02aa8c223ec6780a77e4342db67c9c0642e7f41062e97cefc0aabb756938de76c617d2224ac98dc1706357eddc18352874206fa64f8d64c1c4741e67c20bbc5ea70b4e866bdb124f15956fd2e30b1dae526daac9c3196d8a2c007617cb2fbff3832e39c9554d54d9bd26bfd7d3c966c16eb4edc2776656a8fb3fc825f74a60ba82adad5d8165521218e0b9516a9e88077f1061180300f247a1c29ea48ac4d39fda75c842add935a056335db551de049856c8fdddbb1213551e0c567e23555fec0676b07a2abf022dc26dc44afc1c1128c4887f0496a153f46e5574e59d26e14108996b83f5865b14b647809a3041698c4abf018e72d96f2f33bb80b7a977851d2c3bda3578893cd103d305c9bb9e70f5de0f4b1dfe081c16873a48d3bdae2407a2e9492acae0d3a2bc48327719639385121536cfa9d76622645aefbc637c0fbf9391d5f72e0e336383b19669b143388800d72587697ba2f17c9e6dccc39ca0aefe8c1090ec586ad542b65a15629fea8a8423e9d9513b6414589890de9c4a3d29cd83993976b8980c69719ae97ce944ba2fe95b444a3b85516b2a08216fe5b0ee6f692b91259680722f33c8b11e14d3e84f802653daa840fcca2108c821812ab34be58f5420a2f12d814ba63c70b5822a23b7c19044eb9f311a315bfe4c9c51c1c7334850d2f3157a25af2ae2128c2b7c8d933de988dd630bb60df74509db3089e0887068377b67dfa85298e2cbefbc22c308063edb206d9b210cccac4bb03e51f5bb500093d54e5f2220e1d390a8aea65cfc365b2cd43edf791d7cdebacc3421334d8629a26e30e743604002cb3bede0e6e1cbedce0e4ed5ede97f2b666bf775acb1d43d68725078e33de752095f7eb66604b816693ebc2af7d2de12be0cc9007d524d2d08396cfd4b0096fb80135463dcdaccefd5922f159b16a2364180b3ccddc8d8e2daf5e6fd3284d81ff5270e87b56fbac1d49230ede8849bb9d85de5cc8c1a675ed6fc4e20a95c8c5e5ad325ae1d8cdd5d5db5bb3a3be28d74bed6052b58e7c102bf9cc0c9cc24bb77fc30adcaf7a89e29c99fd8890c959cd20416c38d439466c40c7beed742b6e5f3503eed53e58b6c974dd9d0e7a520f404edf6fe5bb7e87b90aa4f39111d8692e861b94c3625185e16206319124dc19439957853134723304b0853511efa253900045dc204d161b9ac5112f2e1a025ce43e79150dbe3b906f1d6cbdaeb3eb9bfa25b4513c7f2d08d4d2d49a4bbc7afbf23fa1440232b051b79ccbfa0087dd735ee7b564fbe509d8273daf2d785d3de40e290a4ea0e4dc64335f711c581da9fe150309e882134bef4e84b7c385472510b69e143adbddb71088a4a985ead1334dd10a0b617581c740a4fff8b7bf0d206840ea845cd3db41bd11381971bb92cf33a2b6721b830283028be04a256fee9aa1dbb705b455aa0e29cc28a925055fea4ce7a921a27ed9e89b986d8a87ee6667fc986f74a18a7cd4c8bcacf036bb467b69a9b95157de489a39a36aedbcb13f1c47e79fd5727bdb467467620b10d5bfb2a74093045d5003d24c2ac1a386bd7bb696043d4071d861b301a6d5f7d341bd399bbe0c9628006669a5fe8a88c2a8ae125c2760bf2b1bf053757fb3471e9a53b8aacf0a6856b04d83c56ac72a97d505f58cb94f651efa628acde59922817b05eaa5c1ee4e41d05788b8cbc83ef289d4b516b34ea5a4b86035ad636ea9ad3468eb3c9a2aeddeb1fe8429e13ae86dd6b910b052a21e06064647cde5ad21b16a4b910fdf7b24daadebda6a38d844a5d49883e0d4491ce1d3ade78483d5c5fe310f2dfff702a09cf27d9b9edf7f5df788dd718811de72a2139e67d710e2ac5aa4466f81e272e2214735e76fe336be0bf005d43b20a5af348067fd7457e82d2da71b57675c11a8eaea8a95976daa16ea570549fbae8142dc9e78a5b4c5e5706d7a4b9bb8f22941d2524870ad185a54e2d337ba7277de66c9797e0aba0e54159ea58935257551971f33db85bd40a5acd951c16ac01500259041fd28428f2e0576e6019fdfd46db72cae7aca16ee7b1bf42148770a3fb404df9a525dab3cd5f65c2003d3734bf967d8c533cd13039dcdea42ef29c6271e9a7ad6556abf5df22f6c1753f41016e13a15f9e4a6c3af1c6ac348633341d4a83342866c5c43463e9d32d3a8a39e252e36ce282f07ecc7718b005c0f1dfbf733fe46dbaaf339f6e9f7f0b57e123b36e8cac0aeceb36be8905628cb1d048fffa08023772c53fb57f9a1168f2b8c90fa355af82e3ecaf37e526310315d8ad11d38c98cee18946266f121b59f04a296d6cabef75f052891040bb765a2ae09ce4db2c254e34e5cb045d1fd9b78973f4702c9b7fad20e866b2260b9a26694b2d4785ee5c64d66af334b918d477c833450c63b711b02669ec5d0a90bf381d03d1ba6184be992d9115d788c2e4323dcf46843dbc7ff240acedff9b6eeae1ba6a7cb49e05dd4a971e92e12a2377f43ddd441b4444d41e7e81b1e8001af548033fcae65f496133900c46310098a907d20b02606b900361cf44f3c212ee2adabd45b13f8fa28c58331e03c1db4ec3c976978caad29365d3978441d9739fd992aa83cab26b0bc2a8c61c27af0322ac759358e38f371b6a3b153a3341321c0cecc74c10d8bd85314f7bd6717def79ee2803cd4f87a71f404acbc9c55036ff0942e06b8e3759dd6b0d3f5bd6522d4d8da800df0548923ede5b5ec99abcfa151d0435946b7beda9119cd1694253402c95388ce76ac04dc03be34e9b3997aa7ad1c7219420c124048283e54565c3a84105fc10e510e76e89e5a23577cd09ff2bf9f1fccaef671620ed5595a12a5341f02bc74ff81115c3887b42fd7d37aefb85bcf71a7b2c541fa8fa0eec0256e15029e48fe655ef45e472f532f6d2f9b97ecf45a7d34708bac97c40bee95eed5eb95e155c7573358d22b2ffb3a62f0bac4787fe596f64316242fbfbe904dd287c48bb7bc9e48c8024c585ea57f295e583d5960c0bde45ea2fe757d5eb750d978f17a69f0cb06e6f33a4e359758bdae54d7e8ae6fd74ed7bceba1eba0ebefb5eb35f37af5fab191fcd3875b82ed2f5f0f434e605f09053071c4abea85e015f6eaeed5f3cafdaf459010471e7b55d6ebab280fdc93f35a7871f7ea7a6578c5bd62f03a3ea82294201829cd979ff00c36acb5b5a610d47657433dafca5117237afd27d5a1494d0cb0d084d9388c0cf97d142b38d9d7815921e654a40201a32111213361f3ddd6106a582c69643c224152e61f3a16a33cd2504e1376c61e4e6a5c4650d9fb4c790fb9d0d4c1e2a39d77a2fa9d474286bb21e512e80140ae2be932d3a9f29501eaeaeb53b5563b84858d3d13e37c8c23594921214ab71f54a32a9088d328e4b12d58e2f0603dc4944d72d6c2292ced07bfe5e291c6935af035af40414cb358aadb74d94e51470f5382870eb47ee039008850a039b0a221d2224dc5b3c79ede48e4fead480beeca0821fa36da8e0153061e2e39904270e7e32593784043a0cb892218a886ef378e25cbc13285210f0a76bee41e45fbf85728d7477cf96ba8c2ce821eb0c3db28ac3a67f558bf7e2ac241425bf41b8c041c3553ee9a6a17ab085d94db157c3eb3d61c4190fad8f018970b62f7084a8c2ff0becfd60fc712209d58d05fdd95406d1aef1e6ff05d42c5384961a7f0cc6842c8d5d90297459ba564d842f0c1d6b0e7db454d6e3b91bd05114bd44518bd7e3f83c349658b2139de56f3bcc0a03520ed2dda2fa8219c505c8e02fe4ec40f0b3550b4960efcffffffffffffff8fd244686bedff563225a984175624255793924c32a5943aed1600450100009a888848180500002780ee0a7c0bbb0a07952ebefa0c5018c6fc2c7288a47d24e713e6bf101ad224ba4aba6778c2bca1cf436e9f2472e3199d30556c245d22e7e528a5199c30684cb2ec164b9bc082b0feabe81d23a509534e0b1ec92a4c940e65c27449e80829b3f3bd7d3061be5c26928e70a2bdeb4b982bade53b28cb11f96209c3f778e8ec22633f2f95b86eb4e895f38a2c3e254cba25264b126acd72741286243a4d5aca398c562a09e35c760f29ea72108d44c2e45712b7d2886a441312b57ad039f67f8449f7f9bfefe785d11c61f6643a45ff32935761234c419efd45d0ad34f718610a39a8e7ac8724b26e8b28a75ab94a2a4d58127a1598b1206628c2904df4fc5d2e37a1548b989108d39e254faa544e7ad3b7e8a2025ba8610622cc29850959763c5dba7f08832ef9233574c695d78885198630270f5ef313de3eb582021446050a61d212275ea3f38430fbc8b44e42658dea7e10e633bd90eb30ffdcba200c9e4be94fcb17653c0f8451c6d284cba7ce697400619030f9d54ca28810ed1f0c22a7d8b1579496e9143f98f542b4f0d92bc2454a1fcc9611dcad52c9075349fa35ddd2b7a6e11e0c663177eaab54b8d1e9c11462a91c22774e154bcb8321dc86e9909784387f061e4cef7b174c43be9eb466dcc1246b64cfb4cd5c343bd4c1ec4987ce709f17357a3a984b07912ee4bf9130630ee689d7394d882d1ea4450e267df226695b13a9921e0793cf8555ddcf491d321c0c97794af699c829def9064394d39e16424eafe4b9c110b4b958325377f95b1bcc9a122b958e9e22478b0da6ecca6d1d6b1755d31a0cd92c64980ea3553c480da67fb7ef90334d8339ffb35a258f063b72c6878c75786aa8bca77dad4b19aa2fcc3883312d2bab2ec269d86630cf2725f227e14124a595c1ec77a7f51f1f31b00833c8605222e2f15bfd74fc0a02a40933c6606aefce2b7fd93126470ca6d8316b2ee71022231d06d358e88c902e7b059dc160d2c8a1a7cfddc4534ad98c2f18cebf3a25881ed94b99e1059316f5ef41ef235130a30ba64ea955f17b2a9a0ab7a0c70f2ec10c2e1854e6d3fd75cd2c34c68f910211ccd882b984b2f3f3942be94976b045c001ae057329adca12945bd26e65c1a836e1b2a9caa126332bccc082298d5df0b49f3324bf8fc008e302f8841957308ca857a5decb5e26335b7451812d9c30c30a86914f2252c566d5e955985105932c496d13c2c5ed65a9608ad9a59fe46bfa8fa6600e13f11371948db6550aa6b0f5d6637b1db4e8a430230a86c912cb83fc7c3b13858241ca8a7fc9ce130c4256fa9495f6bac3c409a67029eb420aa6f56354c1516146138c9db2994e4e4992c9fe023972f8c891e308339860c8df733e495c4a4a74662cc17cdaec57bba35b495d09a6be4966d516afeee224985f662c9b0a2a2539d00022cc4082b97d5bc75468398f715b30e30826d3fd3699243d5cc8463007917408490573efce1521cf391bbd55393388605022044b2a3e580c15ae4adce5dc5be840982104f3ecc55276a75a2c3d627ce143023d1e6704c1902e9577c408ce0082ad8f193f30c76d8d4574ad90b3b383d50739a3076a089193fe6f153167f0c0244bd769b7e841e488ed2836630796e3ccd08129aca51aeb10c91529099400da302307a64e7221b87b7baedb0f10401a66e0c0f8e797db66935364c34860f0203edec79a99999995f116255ce00542a043005998710383e8549e7a26b7a318e9c20c1b18bf92e5348fa72e6dbea3980e1d0ec618611cc42dcca881f94fb995d84a1d3d767614e32bcca08161df6f3d359450eacdc2f0b991fd57843e1141bf03f9b07b820c59984a07f79494489df4997990110be355d5856c936c6fbd0e3bc88085717f2b7d5a8d14c9f27890f10aa37508a53a52487eea7785b9a28f7ed32d73ead356984e75b9abcca5d3215bab830c561854f5e4e824a4d59a671506e139825095de9429b9b241862a4c77ff2521a86b90910ac3896059e44f19a83029d9dbfe6d3a8597ad09324e619ab0a3152bc92a47bea3d8064286294cb9997161cadf518c05fdc58f87801f649482b59c1e6535e2290521831486a05f1d82451a05280c2f1a308a93210acbb10119a130a90b5f227f56324061169d23bf569788adac8c4f18c4aaad27adddc4ec3c61903f227db4d277c2d4934d522a617a3e869c30afeac7c4393f57372917646cc220e74b3d8a34a5e6a46490a109e3f929938c4c98b3be2809967a21df840993fa90ebc4227f09d36976d0212e76788f5bc2fcb7ed6e314dc4e5bc1226711bd33a1bf193862861ca6e155c26e5204ae84cc2f4a6f7b742afa528ae24cc3329755c622512a6ce56695f49a9244290308a4ee6eab1a1645ff211060fea64a8ceca3a2f3ac2a49d9342c3c4eae9af1126db0de5f5723129ca820783870e30c6e891c60845cff9c92ad1e349b7087389cd8d52418a30443129495ccf62443d11b74cbcc82a22c264b1838e2a914f729e439893d2de1369ee3fbf6d084554f6db499df2bf15c2243a82d0272b494f112684f144149114a2b395c783782eafe43ecce589bb208c9a22d47ef98e16cb06e26c0fe95395c5f29f01619ea836a3b2493995a47f309d7f0e99f171754f2de4c090e10793ce7f4fb9d3aae747faf0966ac907e3c812c1828438da4ab8874cf586eeb6a597d301401864e8c1a8f5f95f31bd3a564a461e0c579efffeea3d6d3b34840c3c7817a2ee471b9128c8b883393d257dad32b28331ddf2c949e1a9f4937530c567ac89a91711d672820c3a983f748d4affff59d7e760ca16792dd17ebe442e820c3998de3d09693babfe6924828c38983cea8965d3a39492131ccc2f61f34ea4e81464bcc194e35b6587a7a120c30de649e146450f11cdc2db0643aa3acbd53c612713196c30883da563b53a4537a58c359844a55ba897d88ae96040861a8c791ffb495c5f13c8488331920e559f6a925e48b240061aea0ca61a95a12e6e266e467614bb53810c33c82883f9e3cb3d66891d990f3298477ef63be4e4c9d60146a3f342c6184c6efe96a52a042d272c3b810c31982c89146daebdb4691f06838767ad501b1f4fc7c160d6aca09e92e4d037d117ccd9245a8c29d911f9f18251a47afa6826b2c876bb80fc8a7e623b234f8805efaf82274290c1056367b5512357d98271f482ee5e09ee214f2d184450bff2a02524d15f164c332744fcdcfb7515f3d791170032c8c082d1b35a2f82beaa282741c8b882b1bf624b82a7ad60522a29d7f10839df2e80905105a3df5fa6471369cf6e2a18f48956580fdf7fab4cc1d879d1a249c873deaa140ce14408fa4d5a3651228f414614cc51e742cb8fbeb02414830c28983da8242e8ef6f8d53fc1106dc77554fb63e5a413cc71f5b2655f7e665b4e46130c666ac95ac47f56d4ee0b3298607a4f6f256644fc45d5838c2518d3a28474ba576fd9db518c142ac850026b9756d91c6424c1d46db3b726f2fdff87e163f840825146deeb888c25611339c83882c17afca48a900ff77847311de5b6e80202a50a328c604e59a4bb575a2720a30886d349268cfcd85da62283080621a4c767c74eb5ff640cc19454467a11625a257238c81082295aaaf0a5724f1201238c4d25c8088239697908c93dba886002c2e155d46bb5a3301364fcc0b87d2392a9ef0373575ffbabe47b60086147e957d3da31f1c02052ce91f9f18452bbefc07c31febd3be9ec6c1da08c1c982d44ddfc1454d02f3b0e38ad23af223cb332c8b8816955ef445c5de8f021193668dc638e2e99146114c8a881c143f4881a39946ecf91410353d4b88fe13e725c7266613908200ba3e8a8619636367dee58687a9617b5a355ac60613a25528a96937d4596f276e5074f35bb17047085a93ee47c7cd1cb296a2bcc37fa6f69e2756889b0c2249452b66fb59dfd5401f888c8d82b1761ac3e9b53f34989b274b0220c22c3f74df78fc9a8b4502311e6b417ec420a9fd4ef05c7a006224ca3fd94278b9d0f427e0883ba2076ed3f88b21919c2103554b08b135408d3cfa7f96ac8e4f59a1086bc9fb7524ee9c5393cc0d0c13ca8310893deae4e2a6f9d14f1e60d6a08c2dc97e4841472a4d4cf0261ae3eeddef7e8d9a3dc518c4715a00620cc622acc83ae8c89ef1016d45e20043ac2a8f10743ba1f6517e2250135fc603c5553e9d9fb7a06a3bd10a3461f8c218432934bddec0fe2834154f04e314412d5d883f1f35b7cebf1caea143d98ef5d24fcfee8ececd6c883612fbaa6c5798050030f264b379ec467a7eb5cba8341279f9d532ab283d942dc3d95b3454eb4eb608a34e1ef7c3627d4a083496edb7eca50e945f33998eb4b3dc7ac4e162a480e46b94949b684202fefe522470e5e428d388851030ee40d667950a33e78e4f88ec205670d37580e28d468031c6ab0c1a4b753ca1b232db5446bacc160972b27f9fbbde425359846286d7b35f3be73d74883f9d47699a67d8e27760d3498b443d9859055d27ce9891a673027ed49e2c9aa7562690230786ca286198c9d8277dd960a1a1372c50c35ca609cbb1029be4bd0913b6430cb7d9a112acebcc5c9188c6379c2849e37e159bcb3167c8fdfe1a32320841a62309c540fef27bcc24cb0cc0735c28035c090cec183fb280315d8c2870f2ec0e0d13bb8c891830ca00e35be7035bc7035ba60f8ceb44979173c24ddfb480bd4e082492f8f5a895235e9845b30c9a874fa7d22d7a87b47311d7a5e430b865341c533d5f272ad4616ae06164cad5721628989e920845e0c0e74c1811a5730c9a57c585887df980435acd004d9166c94bdffc5183ece183e120435aa60922b7f317258ed944f3b8ad5a0c2558d291852965b4e39922a3785f1430a4631cb156163ee28f63daa8b1a5130c92ed1d92d6a362ca806142cc7096a3cc114bec2758c0c6b38c12c5e9147f69bce39773972d46882f9646aebe9af06134cf56ffd29a9c77e516309a90e23b3cc54ac7021881a4a30a71826652e9f04838a699391d995f6c248307e72b1ce32912398f44776bb13353722b92ca86104639d98cda9bc4ef15c1f358a604e53b7470d22d8a0c610744f115591cd3f9511aceb410d2198c32595b0d17e22bffb1a41307dcc3395921001c12cf27a55b3abbff2118617a8fca8f103734cf12f8f23eb572b3f787cf145f7f04c410d1f9873991a214ba9f0dbad460f0c132e4e3059ea1dc5c058418be18380518307e6cdf1f211324ad777ca50506307c68b20aaa2e5ea617ca13a30669d25536d5af2053f07e71d954f2786d04e7b000f06357060f616d513b742f55f3d7ec70a8c0b2bfbf1888b698d1b98fe474cf23d2196b49ff0b6e802026060a00318d8828b2d5a707fa86103f39e88b92182fa9492b6460d4c26e43ad79d58cf95a7a1060d30094a6bfe4cc7d3de2c4cda75f9a38c3aed98cb82ec7b221742b2cd9dc4c218faf54a6e7a65f7b0285e5f5d0e39396aaf30a78dfba06919713cb4a3d8ffe061f65e1c7685f13f9eac74da56184b9c3875de2e2b4cb5191f3a74642b25ab30249147c5b4aed5734d150691b4ca278a892c1105fd238f025b70b18551608b2e2ab0452a94f5cb29ad9e122a2ca1aae475f8f8e914344c411229548594ff769702a97595d529c92f448a3246e4e2e42c5eebb6838b62b405de8b3346180e5013a3bf280b028d5198ab948cca09db4b51270d511833764be7740421725c878f2ef3c18031d00885793be48d24c6a225edbc400314e66b0bbbc9d1b47e2d9f30c5ef9e4be5b227cc61ed762e494c132ee98469ee74b0dca3f75674dde3bff81d6218829c309ef5be9e9c8aed0542a063013436618adbbfa374d71f1260807be1457fc1811c399086260c336123898512d626df514cc70e1f638851c63e3813c6b8b42492aeadd221a28109554c55cc11ff5cc2dc2631c7929e2d61ce9f2bd577a5e4f6d14a9873f88b203a54f29b9c0f461a9430ee888a1e3ea9ed0fe28e62761aa03109532ec9114eaf7848214712e612b36ff731498697ce3c402312c6b7aff092cf000d48184649cf33217e362d7f8f16e37b88c1f60502c19bc2008d4798b2897c0b49aae8d0e68e6261fcb8e38286234c62f5cce3575a2973eb0718af8223d0688439cef6eeb34fa0c10883f87593a4848a9fa4640230782813682cc258f9934eae2145182f8894749df4f81dfd7b64056824c26c57a9d357e8ab35bd75d1c50a7a03762be81e14a08108431031bb9dd4c3c28e6f001a8730984891e294eea4218c122c7465cc5d0843cc0f296715df9209610ef35293a6dc4a94350883777b2eede192f789056136a52a25f159e64904d7023402610afa2c841915574b9d0e307ec70f1d470310663f1d8f263967ab442be81e681ca0f107d228e1da223d44579f861f0c7226c90b6741a705a1153c18671ba0d107e39e1291ba9722bf4568f0c11475fe47785ffc0f1e7c011a7b30c4f5146b54a4761443c1bf8f94000d3d988350e174d2ed546005bd01bd8d008d3c184c5e870ffa5e94f6941dc5ec79f4f881001a7830cb6aa932bd9055b11734ee60ca93c22893763e7192ec0634ec604892548f4af5d4c17826d285b4162ac9f014d2a083c14a489f58722f33a03107e3a57411e344efeb7a3e78a80c68c8c178f7a16de2b2859d380e86916e3d5b3797f4973fde8b770207534a419987509e3798d3ba259adcca8e626e306b9ca9dc0db551c280461b4ca1d4a40a2394ee20ec1dc574f8e0c10673127ea51e84a5f9da5e40630d86933d77d93a440d262dd321b12d7a2a95a5c1acf12d3a7a94a06c1de7021a68308a29e9a3a1f49de5eb0ce6161d32717499c5d86630fc8744310b2a8329a5fa75ca7dade7bfa3981d19cc6bf6124455e4945eae63018d31182cdf45b2dafa90af8086184c265a3452dafa8e14de512c0c86999faf13594e29a98006180c6e962d699f51a595b3a3d8178ca3a425e5397414d7d30b26ddb19f3ea7d30563e9cedb713bce78fa8306174c614d6f48337947b11f626cc198a1bcdcc458af755a305a7819f1902f29ea3c880f1a5930c5291d5f112d95dec2a08105d3dd85eaa85f1e2abc3b8a15d228c4d77ab8d5296dd10504787420478e1c39ca0f30788cd183d0b08271d427c80ed24445f61dc55ac082307a88a1c3070f4d008d2a984d7b4e17c4f3455fa4070f3158f03a7cf028fbe1238c0888f1850f09acd2a08221a86c7c5d4cd29d2c0f2fce1819c89123478e1c39bac718cd31a03105a3218592baca3857934ba6690b6844810614cc73ca83889e633d7e88a1071a4f30c5a7f09e4766a49bd5acc70f319240c309468b23413bdb664544a30957021a4c304ab8244dd7538628a52598d3cf9c3a1d272518c2f775cbc871b93d09e619f56caa3c9e9cc79160f4585a291e42aaeef8118ca19f333f353b98c48d606a0f9f2d5df82e139e2218acb72475ba1313c44304c387bc9098273376e62198c7da434eb2af108c9b113f4bca8b1552120473751c0922878e3e4b02c1b8ee419dc67f4a9ed74180c60f4c97699b319e5d92b45f748fce80bf0bd2746082860f4c425c2cbb7871268998011a3d30ebdb6edc5b38ad7cda3ad0e0817136d44484c47760ceaee0593a553cad711768e8c09421667776cae53a2ed0c881b1e47c7a50290807e6244d8891fda48447710d346e6012295a874b3f1a39f343030d1b9873f2bcb1e76e226355038d1a983ec44389eb8ce5f2e838030d1a98c6d52b68c5c7519985496fa992bd33c9cdfe1dc57490dba20b08e4c8a1c3be18a3c70f1ea747ef0f66c8c2eca95259ec09d7b5be61462c0ceaec82ca51be53c9122c0ca9a359aeecf615263d3ae7641699e10a5338b91e94b4fc39967a6398d10ab387e42f59794d6ebc628861062b4ca22a6f5558578719ab3047ef08693fba88eb4915e6f098d827332c85f8a7c2179deb41626551619c20947d1655eba382a73069c5d3cbfa1d7221878619a630df7de4f5e89da6e62f85b1457830252a9a410a939e98f216743aa57e54c08c5198a4ff55eefcd5191187c00c5118f489146c4525850acdcc08857152a7cb03d87848dd5084c1d4bed39ad5fe5dea003712619022dfeb39f7a29e0e2288f17eb2a3391f220d370c71370a6152b6963df25486557a8310893c4a5dd4db73a87b1077c74cd0492e858b9db31b82387d8ea3a1ce3f271085a0ffbfb4ca8e5c6bfc821b80305d70ef9332d1bbf107c38551f9399ddc6cd737fc601249dc2385931fa5721c6ef4c19cb5845ddbca5ad02ad170830fa6bbabca75f71e2d7258f03a4270630fc6cdeb18f97c4e4f8a0ce2861e0ce26c75c64cab76b8910773092562a9eedc0d3c98c62c5d98a03b98e22e82c4c83999ac2c256ed8c1784125eae7971c16ca91a3053f78dca883d1e6424e3d11743b3ae8601266b2fb37d58205ad0e2fba78e2c61c4c9526aafd9a9646563571430ea68fa415ee9293891b7130ea7e7d5eb9e8ca1fdc514cc70f4579030ee68d1b89f92b890002e37b7cfa0db8173c767091e30de6fbca3942fe5ae4d10d06332daa237a2495376e8339c47e1091b278edbd6c306cbba8ad104ceecf9923479d2f96e7934e23323518440479ea7da2d7cb4b8321a57ca54a10125204a1c1ac5afd611e45ac6ee70c668b54d973054bea153318ad62a9db0b9dfd2096a1dc2083314cb5c76d2da915b4311847e86d89fd313c4d8e188ca6d54c74d2a630182ca9fba0746530987238fbbc2a7a183e868f2f98e33a82f6b2f017a2a9b0e086170c5934db842ab1b513b4821b5d30cb65b3cadd562f3632146e70c1a461daabddf2538a849b70630b0699d7a9ff2fc43a09ddd08249472e5d1bcdb360527295db277d86cc08164c162282d8eb3791f562c28d2b98fb26655ee9586a5fb682a95cfbfc576ff48b6a0837aa60d01b5ba365d486572e841b543066e84ada778486e7730a7a7a1b75123f47f0bef8626570430a26f9701d2db6e48b1b5130fd86de5f0aa5f4b3fc0105a3249324dd84964e49995edc78827145cfa5906f928e2ab913dc7042e16d2529e34da2a570de8d2698921addadf78f6821627ce183078f1b4c30a53a61e2a2e5307a4f17b8b1046348efb990b57430783c20478eee31468fe18612cc9d1ebb91bc6a7c824bb89104d38d4e397584286d3a3ee006124c2525c889113fe7a31471e308c68b6b6ad6bf534e7582c0165c6cf1002eb670001741b86104531e7d9d3dfea90846eba03be92d7141ea89088678b5e63ed17205390dc19083474929b4625a9684607e53fa0f82e12e59d8868c9b1d3d100cc2c4dbf4e7cad0a3ffc02c6ee92c2971595d473e305648a972d015fa83a40766adffa09f638707e6ecd26542dbb885d8d98149e4523fa555ebc0f4eb952de693a689e4c07839f589b8d269722d0e0c3a7ab7c949d13ffd3730caeacedcf89825d7060695843e29314f98ac941a1874e224d39d6d77f31b343064b990d74a3d0b4392bc21de6bf2f249646110a127f9439ab130b66ee996b01216c688934e6a64ebbf13bec290e49ffce5171325e20ae3a91869c47a6475bc5698948ea31d4d975ad05961aa9cdade4f7ed033afc2acda93356425fbe09a2a4c41fd3523c911e36d710e6ca4c2e09f1fa2588e51413b1ba830e934f3419dca6e6b7f0ad38ebe0755e17fe745a6308c0e1e44658ce7b44f290c36417f3b6ff9c94993c25412644d774bb5747814669138e24f3c9f9cbd88c29022c94f9fc6df535c42611e195a5214113d0955018549f6bf25a9d871adf513e6f9960b1f21ffc68478c2ac17e2e385bc13a6781db9f63a934214396114c9a96672acd349b40983feba3e19ad8e319f26cc93ade2d4c7d8337d264ce2b2cb93c92451524c184b75c82792df2925b9843184ead361baaa26344be477e56e41df5a306c54c294dd39edd53f7b3639258c9d636a880f3993308bcda449e9945adc5a126693da41eb6625ffc79130c491682a63e564bd8384e1af5f54fc9ad81fb247182d82a754313752a81d61ae9bf71349f6a24a1a61ea54b9ba3c568c30895e2bdd95d3e5ca44cedf0962b9224c355ed971ff44185e3b9bce0b7ba22e8408a39690d3be765a41720e61d0db7b4f153ae824b321cc25e172bed94b622c5208734ed53d1659dc4c8e8430e5cf49241df753a8920dc294e5f373bcbc204c1a7fe5d14ebadc57e5d8088439dfd2c412def1767340182b5e04ed346bb9e2f307d34af615d121eb07a37fc892e556ea7bea8359f3440e6a79ead353f860c8174ab9c6686cd8ba07b38e6b69cfcff4601ca53ac5f49ff5cf55096ce4c1a424858bb514240953c28349dedc47111ac2ee4f7730e5f1f51ecb67a6eb6407531095424877ca33d6af83415fd0fe511d2727f3e960d2fcd9f0d31af23c3e0773d0f221e647895a429783c9eb538a799ba727ec3818574798c8080ec61e9172e48be5a6f11b4cebdd914bdaa53e0bbbc1aca55df539314ca96d307f12aa63a667d3125c36983d9575b0ccd7caa73598c24daed429531b6a3088f7ec273676726b62230da66d9f1137bafe228460b081066304bd7ccf12225260f0e81fdfa31460e30ca6cc3a117a9f7545ec66307908123d825cf58dda46194c42e78e997cd195da7fc006190c92278c30359d7c373406c30815a6d24b6428f188c1dce7419fb212d7a72d61305a74f138f7e6351eda0083392d56b6483127b8e57cc172241b5e3056b65373661782cebc0b660dbff4147acfa45cb8607acb9b22447a5335cbb6c0c616b4870d2da4c04616dcbd76142b89021b58c034818d2b9c0b6c58c1ef17d15f2e32a236aa605bb968856c51267c2ed1be3cc266dc518c370936a860dc9cf9d891f8567ad9988271dceab7d46d45d0d99082d9e382b234ef946b7e144cbe9bff9e153c7d90a160343df931d67b529a888d2718c56e5d52ee9f6d38c17059e4afa8b4ada304011b4d30bc05919f22e4bfe4ba146c30c1ec95aeedd479de8efc128c22ab6ea699162aab04e3a81111d1266586ca9360f0ec5174320b21c1e4da3d1a39b64730a9ac90338229a51c672ac9b977dff609368a605a917eca44a629370f114ce6316b6355ea97d2219893ee28cf7ba5e4a7f98347186c08c1941255947af4bc2a8bd9186c04c11ce7e2af07194b29e874e0477bf1801c39c0f81f3fda8b6445b0010443b69d1b29f2d92e4e7ef8f80bf4f81f1238828d1f98e279b6cb6ffe2167c9072635cf5f76bb7b31fa1e982d94ee244df23c3068ee5fca323a2129f90e0ce94bca08f5614674ae03b3f6db955af654ab790ecc65f2e3ad2fdd5cfcd8c081b9fa43f47412d674d0b1710383c5b55f1f117490efb16103a3991a73155dea53756cd4c0b4a32be59985296ddb060dcce739f5bbc8b6a092781626afcc4f4152b4d83ecbc2985da225d4568ee5702ccc16b2748d5f0ef50ec3c29072ed7d56bfaf30076d317ebd76ab23eb0a83a7cca56d9589d5a51506b97f613b6e9bff565861f21c954c6a7ffe9cb40ab388b7554949aa30c911172dbaa4bc2e4a85a9d25534691e4585f9fc5d5faed35398fe74f2abf0a5a93b3185297f765bdfc931ea91521877c4f38f7f85e93b4961d027a47765f554e5e2284c61cb2d85a43aea85280a43f69f0f1ef31feb5e288c1aaf62da65e47e3c5098d24f9454eb7fc2dc292bc747daeb90354f98c7c4a8ca703b6192992694dcb1516272c2a083589bff1cb49fd036616cbb1f9d2e084df33461901e523031aaca84b9acee847b9860c22052478e944f41dbe75cc2b8f641e6e59e58c22084eacb2397247f082b6148e7663f67f24a9b588312e6a02babbd4915a1d2aa3109f368891f29c55cd3bb15491894fabb7d33257a44549050a31e9dd2a5841a9030f957d210a34ce7609247185f8210d95d3e47986d845fbf986d9fb68e50a31126b59795c3fcc40d2d31c2b40701688316823089aa7b1889332256301086491e3ea494a262b9260138680108a3a8a535d96ad1e20f66d94f6fbbbc1d4a9b8c02b4f083d95594bebaec6471f25af4c1f4612f1e4f9ef860bed131a9f752418b3d98ce6f84ca3a13c554a40b2df490d021b6c6e7b46ed10504b468910793bccff0ced1a1051e4c1f4a35f63a678c2f74a4167730e9db09db213b2dec60d8b04f5f1b394855b70ea6d263ded94d54e794d7694107457c8aa5175accc1903be8a0a3c42b07f347ffff91a4ea938671300795efa6c289e820d4020ec6ac1acdcb0929879dde60b80941944e22cfb8654fa0851b0c1bdaf4c751bf207f4fad3cb4688341c6cd4a98e441a5f4f81f603c1b4cefda1661375fff3c498b359c745ee7b42e5944490db4508349089315f98d0039d7220de6ad9cecc2cf2fff8b98a0051a0ca944bbc7cc5027771a81166730abaca6fddfd6fd6a5a98418b3298e3ad849f0bed4f4fa9eb8216643048f518d7e172ccd2e351d0238c158c81cb08e39245265b8b65ccba8f584a302da1208c307678e13cee081eb41083494abcb21459fe4a448b3018b52aa84ab2a005188c3b3e562d4ab7ab2725408b2f98e4b34e78cb1035252f182e45e8ceb2f031a62e18a49590b05d4a5c306d67e89a18fa6b73da82e937949ea513a6de4d5a30470d11a393ff5930276f49eb3e23242839168cfda6dfccdfe54bf62b18df563ea8c50aa543dc0ac64892a4c5ad10cd37af82294dd6cc1052a48f8aa860f83437fdba96eee9330543d21e29bc844a3aa58f144c273adcd4561a95f1898229774e22a7730e17b10305738a13847c5a157da9f304a3df6d8c4a39ba5fb89d60507db224be77e9bddd0443fe544163d46682494b53fb45844751f5124c16a3f46e87503b9f5582416b57a8990a17eab24930c5885ab2f328d3c92d120c1be9d77eef239892957b461a696a3782f14fd569891635599522b8de3134ee829408e6b9aba81d4443308da94f49521cd33d89104c9f5c3343361204a30713e1ceb64d478180dbfbc40d19c91f6c5f62a2dd25e803636ac65df8ea2a7d5f0f0c9f3d87586a2699f08e07e62442d7d889a495afdb81d1225975e8be90a2d781414f1c11f2294bc9fe1c18cd3e5e8acc7160485a3ace6385141fb981294976f7749335b26736304a2e29b2cfd7e742a5450d4c16f282e99dd49ee25983163430fe7772f3905cd1fa9f853955dbd409a5a22949cbc268232ead8e8a63622ab138da8c2a532d2a2ccc6917d1f7e6f64c29111770bcc238dab5637496b8d8f183c7077c6cc1c5165c6cc1c5165ffc0f1f2de0c0165d54600b9365c40a385c6192a2f4ef3ed5a7f04f2b4c97c428132ded8e6268ca8aeb4705cb2a8c924ea518629fa1d483431526b5a15b5de48a001ca930854e165f44bd83c87b0c385061b01cf9f46ddafb92740a83783115cfdf620a7346360bd39652984f98c550fb1cb29a22e2208539fe45487137ec028e51982d3d33fe427ad1e1238a3ec505295a210d85f942b894b256e44e290485417d7c122969fb8834a20a4e0b383e61ca0bad1fab3edd75eb09533a0f6f13f284167d274cc226a9b539957e52b50130306049c0c109a3a4154f97dd84c9a96cc2742d5fb26ca2061c9a30293d21b49256f00b5a22061c993079ee25b5ef578d90820313a6f09d91afc6a3e93e895dc2d8aba15933c9765fd5030e4b98f44a18d99ca44a18d6b5da54ace8fe95a784c94a239c2a1dd4856a13ef806312469b13513fa9dda9d861c13b18676dc02109c34849caab94ac2813d5613ddefb7728e251c08023120665228f4ebe761d1e215fe0808439a95cb453c96543fa8f3024b5dc4cd70aa575778461feb6f208612988b8118698befa556a4a3fcb8c309887d82ec230ba258810234598938ee589de4a5a421261aa94dd162985ab5322c230ef55ae924b45b2f410861cd4b359ea4a0a2643982c3d4fbcfb579ca04298923e1371b2460861188f9cd4679983308cc83756e245a524434198279d9e1c727b08f9bd4018fd2e5cf4183adaba038441c77afe60ca5dbaa2648dbf9a7e28ffbd7bc9f20c471f4c2a7caeda469f5a5d38f8602ca99f4ba89af660f658da7f5efa6329bd1e8ca7c498f6fd944795280f660d59167f94ea8ce7e0c124fedd82cc897fe1dfc19427b7554c894f7bb71d0ca66f2ef54f9ee56c1d0c29a927d17fba2a214c07e39acad5797a0e466f17ef58d1958349e55a3dedb98c83497fbea4635eb2f75ae060ee899f6bc376f647fa065395c83f2225ff8e22758339c5a5d7bc2571b9a36dc89408fda14cab6c308dcace71514c58c4c81a4cda744e4aade793e05183494684e8489334fdc23418e72e89d3772aaf233418f5db3f8216a15aea33304289903e2f6a0653d411a7bbce2b83f992a66ce530f23e776430bb67cf71a631183dc91f25cdf308218bc12c398591d1b3ab9e0d8339e82cdf26c782f6cac0607a3925f2e4266cfb5f30dfbcdefbe514ab332f98430e19f9b2f6d5afd20ae0e882d14dcd56a80e9d2a45d31ded52143b2182630b266dfa1284e7912242706821cd4a49455259ea08412b8448775ecaf214c09105e39d7f7c889663091363c190526b84a54a7f80e30ac60be93f9ec75f9990c3f89123878f1e3a5ce00347ca845279cb1ae0a882c992da08f71c52a9e97b8110e888010e2a982fc87ae7f37c9e4d680a869bcb39cbe5fbfff57148c1782a5d144b921405b3acdfa98aae13fff238a0608e35f266637184eaf413be2ecb34d3a2428a80c30906153c5fc576120d19b30047134c41e4d8e96a231c4cc8b66bc4f6d900070001c7124c224cd249869f580a3b03b6e8a2021c98010e25187c821e4bb1a372724a82e965eb5347a4d1d32624182e3f4cd0e9638fe308a68e54a1c5ddccf37c4630e58e7adad16911cc1b3f57beea57390ea3c70f1fefe58860fe52b74e229f48229c2ce09186609ef4313a2c7e3a216621184fce2e56cb780ecfee28467004c1e0a142944859d57ee5047000c1d849c79c7afc3c794d3f30986a096a42543a35221f98bb72c9baec0992e2722fc2d0512c03387a60b2129246b8456c33b50ec6c103f389a8901f3adf959079e8c0008e1d542efae6e72785b00e38f7b290dc51cc71e4c06cb9364c4ef75bbccb0e1c387854a7b41d2cc4f3d71b70b53a31be3ec7120de1b081b1d2848d55be0ec94635306ad898054fc133af36010e1a18ad63e51e71f679339d8529e88f0aab2df69d972c4c2d1b5143c3120bd3d7f8e47c3593e7312c8cef298f0e4b4a9db864dd8d57985c4c7b6b777abe4fc1584118c41526f39435b9238a1831b5c258bad278720b2bcc31226af7f785a8deab30840f3ad5dc9e3aa5bd2accbdeaf77261394f4f5261504a2ce47fcd7e031506b17bf1bfdbd3230c1d0f060f2fc0e091374e6178d50a4a9fea0a95435f84d1239da1294c225df6fde8f0a74b5b0a93d24fd5ed2df1fc4861589f9cd48e598594f928cc9e63a74b3a2b0a636435d36b9d6b62c44e6f84c2f8915b44493c0deb1214c6799399f4e629e4073f61b88c20cbf4c4bf46f784e1828cfd58a71521bc4e1826a75ccb69ae83b4d8f3e8d1c50542a0e3c70d4e1866d724e8a8a49a9ede514c47fff0e29d58ddd884218caa0933494fd09599e086260cb144a777eddb143f755118b8910993f8cff90ee9e2439460e00d4c1845df3fb897cac13b65fb3f78e48d4b184212bf70a245dfa2eae386256e54423119e154de51304771831267b83109839c10a2ba1735748a5fb821098376ca4b93f3c3a83b45c2607e49ac47560f772948984da6280df9a2e643b7479827966509ea624c1aeb86230c52949effe69e89943c4b08dc688439cd84c9fc66c4acfaec0837186188264748aabcb99aefd6224cf9b257dd24378cd3a81470e5f27024100883a16028100401f9760333130000000c1c93862281603424d3d57914800351342242362c1a24241a181c8b04e450281408034361002010088681a04028380c89423d581f08b7b70fc81758c58135b3282705863aa340060ed86a386b2893f4a0c6f92b65a817d65080a584375102743b807b371e39c4f015271f2731f2d9b59ed58c3cc5bc15678ef055c1156e3d4ebebcd52697f13738dd2198ff051d9986e8744b1e6e6bb78a94985a66fc1a2e18bc1003a23717d3fa4c48ea3fa882d3fb5977e0020cb4d71dc36e50e06b548bc8eaa19b007751625adad7be31f11afb075fbf2666a16baa284854fd56487427f631cd0afbda7d8c701a9cde7d4d12d3fb294033d12c9066f18fdcd425b95f14fe22db98b16fa83aa2a01173103128d0fcf766bc3fd7a9b8dc8b0153fb8ac36b06f4b5b260f31d84898940ccde320d8a22ad4dc1d3f59171db89f7f48a8d0bee48f66577e84eb0a87169181c727776c4ba3b31c1e53898b24f355955205f96218335279f5958974d9415cffa4c85772611d15ea6306ef21618e8de811343d1e0fef443684eae803775525c3acffc37b488081ebe3879f3b9fb3e03752c1831ebadbf62839d8bcb460823ac36846c593889cde92bbfe7ced58cd0854d099f124ad25e5fd5f67a0a7292fa1aa4c09c7be3e661dba3788b5b1517f8f0a3914f23951ceb8ede37a0bd6ade023f31f505b357db20c34907c15643a604744b01e41be5ca97484b1ab2c41622b9de9769af40bd846c5882e87ba894d8a4a9910bb4613a4c9b69c3449836d38689306da60d13617ab397937b41cf948b613a4c30138641bd28f2fdd7e47988a96101e79461e052edefbec058cb1843ee67a5ab33a74127eca0c66c8a567d7776676f764ac2540c5543dd46f487dc5ebb7c1d399d3e8062a6800e8b3f113bc0755b4166d1794a90b42ea468711264dd7bf068ef9a68a1950b292e137079c173592eabc8e5e5efc2289352ff1480fddc2e29e7b016eb27bce7afc5ebb43b033c635ed49f84a5b359e0525889d6f1388ab58a65808442bf630662d7c7b394a2c5d800659c87050b7c7f9d10325d2f62be21c313545a063a8fbbfb7227e6e4f6069033223398f99cabc42d79472f79360d2be794fe97c917dbd512948594579fd986189c068e0b3938789d523a754cb05ed41b69d327ef8e7fa28ef995080be86ab72f6187cb313b6b6d66f17553a525deccbad902206b38336e2b46446230a3a10704224153857d96f474e0c3963f95ab5f93bbf54be55f5d69e51672185bd53006be93cc089380410a1cde1a86272eaf2477e1f8d890778f10a578782684c347f4879d5c56bbc561706f3301524bbd3b9fdace8d4e83b168196e688487231379c95068125bb42854776cc5d2815941bfdb116388065ee46acd94d275940e044006d75e639badebda2743871ea934989d5278f464c3a5c810252077291c7a643c21eb6e97a1b699d4924dd38dbf317f829007154d19c93c8c62c5918942a604c180faf39a53e24364655d195f45364e16400910bc59192af5cbc406ea84b6b316417f8a2b8b3a32c6681c538d9aa1cc209b29a07451fc9eed4ae9229db0edfc095ca85808cbd46c95fffdaee69b404cdaabbed06c45b0449044fbcf2b77642e56bab19c917988c227701f9a6e358ab528e783fead300ea6bf7f47c58a23415d79c21c4145f07362671954dacd49068a1b908e7637ba03716f37b06c9c74ba4a1be3e813fc50db3c00d2568199bf5c80f2dbf025e497644032250559c62e251b039f2fc9719205d2799f143bc9eb9472e5483ffb1424522671936649ef53dd094f4d219ea5372eb2b57d54e3fc764a936a78f10aafc942f61593e18ee8a465755fa03e153c5760cfd4063916bb9cb13429fcdf60363d331e5b026e1b41a57c6f75904b0d076f845c08325e81cee82a918c25a1de979e05a9776d537a494c9fff3e3476cdc2da291198f0d46bd362d398e5161ae48156691f7a0a791f66148139e5704ac10f6d2f61396cd3477f071b9b5eb90a24d6cc0d925ac4f8573e4182e471ccb6b05962c21486c15fbf97ead018a0786cf39bd4dfe0d42be2749873d52d7bbc18fc25cb58478a10414a90c3f2ffc1b2b99497dc50adbf76f7c326862655d4138a413b46475ba3f4bf9748c30c966385ff1655374cd5c665cce3ea0569c47cb7876e3656e29ad72fd62fe9a962aa2cd96edb3d10b20b065f91b39c0ccd860decdc0154b9dac7738d10019cfe1178c8d1121f92e4362f04fe4ae7ed92da2b5348973005b53ed8d4585ba33e39c8102e66370d5806dc79efa8e92fb4062e3bf0a0f58b4d9e182a6073138253f3e5f68d2c0927d4bd9dd9a09ad9b53ef2f0ab828d99e4b7b3b42af18b6d94bc1fec18a5a7eec31889907a2b8faf0552e54cbe393cd351a227286a822643cc9b8247df7169be8df7c849fb4e626d384a48aac6cefd51645524048a5a7c11062ce504331bb60284f15c85ed961458c6288da590165e055ba0dbe6481c69e852ad661070d4fa5bcacffe9250d0e65b76573f0f8af7e93cc29e836653f78a379449c98b72c28ae4d0b53d19b645fb9d383f2a65bb8980f521230137712cf37144cd689e51dad02f62ba947be1680fd0dcca8ce32e944cb7b12ea19f8b1167f6beba8f04614b85e61d280b95cdf1bf2d6956b08d0bda8dba2af95dedf074251518253c731b2da956103c402331f118f1ce90e47cfbdc27a9fb087e02798add7f531344624940fee1a3f702aa2569ffc37219ef14d2271834582f788830ed880d62efa19b32550c65aaaec0b0ac84a462d51df536db5d459101a61caf7f1ce0e3669144a911caf8ddaecbdbce119b6641edf13a2ba0fe6147a113d387d3919a5eaf5d8d6bd9c1067bfbcecff58b396a9c04a0e899b19fb0a8f45b6855c640d3bb6a8bf7b596d84e6e9404a1724e9dd1aaec8de0b9ee5ade3239b17b7ec884e3b44e641604116670860a15904a09561d417d201da9b6b203e603f08135e32917cec702ebbcf56a482e26f364e7f988f187657945b5a62d4f7210cc2737f4f627f786858ac6620e2a3172d3dadeb78b91a6f773f824d6a627289660e5318cd3e654cdca59cfd7292e8fd337488fa02e27a92706e0e191d29b0a18cf0fd6b03a65288cec900a99ea757dabd5ff6d500f042e61e24d70d70351c45db456665a2001f2bd5c669fbfca8e7ba51387f640a031be6ffcecd92202000ced42a6a42b54ffbb31a3b0854a38830f98f7732662ce156a22d5ab209e7e4725135ef2a230ccec67549eb39e3d72a0887e4aee706f6604075ec728e763d39b6fd5432bf0c0c341bbc461b80ef11dda0e6aed5080126d840b604d1a53699d316883f270876d1019416a7f0702f649cb02e87a388bd7f6a6a60ba696de953d7bc88aadf8135a6573af0caec0f8c9d403d13cd5258a826809cb89734bb178ea6c99e27892ee3f9b7f4a1e5f41eb6a917b48ffae7c0ade08c42e279ead615996ec60d7911d04f6a6e26a838839ce595b698acf1096f113edb5c68c57976a07abab18ef5ae550081eae2d7a68edd2c062b31396e37d550021bbf97da1e89e855c925fba16fa22fb8795b96adc239c78071414296a7ec353daa5fd3f3a0ab395867a21a01918362915f7c0ec9f2f68963dc6a045767913e2a6d42bcd2218bb9a1c71fc39aa0d9faa530ccb753094bd4bfb8be1659be4f7553b6602cb298494e00e31750810edbb4b12a9a16ab72e6df2a9ce66165496041620c12509c98f9923965b50f475f6d4a4402e882750fc21ac607a5e0465e8f7e8746bd3641d437cf0c703c5753f512284d2d8441729e6aec2838e0bd8f4030b71c8da24b063edee9136563c703dd439b12c04e0c9d436d39a572bbeb1e2ac4e2cf3a4ae2e5635015151aff3fda15081dc6f6b9cb3365c6710ad0014319008b70319ce867484c73b7f222921f08d3f39eb9c2e443b5c146abdd64b614904da6facf6d0c5006b0b733ad0e5f0099af38000f29bd9300648744a2ce12d2a69602067d31162db8005737a96812b4b46406e739a78459a38d7ad7b53a1814554c7c76220e7fbd1b3f7d1d70bc229f708972a4b016d485936fe686e82629b21b0863b869a4ff60bc07dbf2c247b66819667720d12b73d721ece0a566e18e6c4ed560f490d83211afd3db50e1ec33a25fb1c3eef6d792401f2c9971b2d03e90b3b35817a86151a259839b499fc01e120048ee3a108b55a7b730bf3ab2620640a182d7be68d644b9cf92d4ebe57b66c961deb7e87a2691aebd6cfd714e52cf1cd1aa18b7795269e96c08f810e99d10483756088e57801879e9006b96355c23c64dca5b04b31de7024d0b3d8e8784ae1be1fb6415834fab0c06add4836f362f90697e29d181a62c028e2c38892111a8d48b581fb3f4b8c77615df74a2070aafd7fb2e4b04d5c51eda8df036d1f589e436ba38c46b70c0a4b622bae3452693e12550d9d0f75ccd41c5c2b641d638924e88d8f0a7f1cd4db34522a4515f0141457005469a500fe94317634248ea8510fca08ad800cb62e15de32fe001d40a1910ea4827a0ac8ab14150051c6a8dc672e1e4328179d5eb4e4628591bf3cea350d988b4c7065b235a34f26c5851ba7442b9bb83c450fb01850bc573c56cc2e3e15ef29a6164f8ab72cf2e2c2c099866942cd1bd26025b3f336c58d071aea324d3a1f8fa9e6016d7908a22e6838f6203abfaed45826d4f88fd613c42782917923b091f72c4be0e073475d6a47cdb149ae0c2f38d7ebdcb84e0763b2af3b41630224000920388077003bb07d009ac8d0c2b60881527afb585188777c60d8583e04d23fd7006d1e5f73853f2730896fff2a54982c04a09209910a1cca3fe8314dfb004854d3063136786ce4d84062c38b8d1e1b48c1cd088ef9ca908cc9998273e437f3e0c0f4e605e1cceadde33c971a116bc655961b921e278e836abe0a7b79c65889d810cbb09f106ccb73b32af462d18de29dd6491b8c2ea342bea3e45aa64842ffeff3f600532faf6251124bab4cbc5b00f81068c60f5247aa682d932469e4b4cebf8adbdb5d3088b41bf2aaebc92ebe959be7dac77213d342b6c908084604013a648518ab5554ef7d74a8ce350899176ed7a1c677632452232e80171e0b8439b8e4bd15ab9e0eb208b94a72d6a78a41b7b0f3cd2132b6ebc22c12e1c39e2a4c0009db761abe5a577f001f8894695470a1171010f8925c8f22481950bf1bd009b02e06a4c9d5f3c32eb9502d92baa55aa4364013a4b21b32a560ecb20d08df8f084f38abe3c0ff0a3e8ea1bca49f16d624704fa1ee828f4be8a9154c2c1e5214a2da9d96d39d2844ed1ff4b25a2a44662aac56ab6381a71079f0ba7731249796c150daebcc9accf10ce74a38345ecaed764f5c8cf6d2f76cc5c48108707e819eb72cc06dd7272e97ee49aba3fdebbaf8bf6e2522a436194a157aba759052d1097e36f36d3c8c0e5e9271ba44382bf195b1184f4dc83ee1d444a898b5a81db0baf730c61e10c6cbb131ffd1c9068bcd6dffd7a060919718b291164ee3c61758f461ae22c33b7b7d97e0cea2d1c6cbee4ddcd3ee29dec1de21de69ef20de61f714f760f740fcefd2a8c14af6b467cb5b9467c9cff20f1724d6b7c5be43eb0e3c50ccf855fce68da08fb73eb579bbbc0e79b93c86bcbabc87bc77e95bc8c519a488411ffb4d407150330c1a9e0a0ac067448507c300bb9ad6bc833d4cd19056ef3d167eec4d7ce340bcc60e20c65f289dd83b05294c7081b671d1c69bd68046b9871c49286c4c31ed608358e9228d45d30e40ae39b058f57d356a81e7a35dfe1b0b34e66f8cdb4a0934557c5349730f6b4debf8b97de46bd74c44e0efaaf2303752058c4d822ac67d0d88ebfd6510e33f22c1f199ae5e26931f5675fdd2b54fc782aee78b209340e872c9bfd2c73156c6a688bebed46ff4b7516829b18c8ccc88e4ff591d605f6edd0ee4bbf13caa4d515715fe3e62e6cca4145c2a82ee10fef877858f93c6cb003f54d24324233be41d1cce96bfc466e3828704e6180452c1f6e552a2238eb5cf5509ab64e6b4cfdf45a91ad23f8c9a69106ed3a8414cb31a0e8b23fd6a73a7e89df6341ff790d68efa5f39cb1706f3acf14eace5223d53bc12ece844857164653600d2afc9ad929897d2572fbe67a760f12f7aa6c578937e0e94cb82fd4e5abcdea2ddc6dfff741877be1f892b4dcc30245d8ebfe00618f265f5491e201902a466e7da46b1ee0f07db43c931057e0451d3f4cd2686aa5a6e23a4f71df71c0b433b183ca1ab6696ba2f21384fd4509d00cb81a0d0b595926c8e86536687b2eb7761aec4fd2ab688bd94b10c92b66c174b7f54123148b19451c0626d02efbd639e3332644d73ae24fc90917786c4415b4c8112d1cb61105dd2495240f252b31c789dd1c68c64111ba93cb69b5beda22739d3a2087713945b4e443d6759c3cc44edce6f23f6c11922e521259824587bed3cb8732305455684ba885c40e200fca5d07cdf235424bd3e12f52b22669ffacf4973e3d45ef7f4ed069ebeb66ca7af92d16d7f1075e6597b702aede23d48f3a02631b3e9aaa728031dfab78f08eebce9806545143c7dc016e4955d9269043450e6e451d69de0a0e558a80b88268e0c7a574550244005a9e41ae6d45f66a9ac18395daff6ea4d7794b6fcabcebd5a952e149be48f2b455e1b8a61da8482ba26ab512ed9aed530c71c731d83bfe38e50047c7bdcd9070b23f98d91507ae6853e86faf1222dd6877248a7fe802cbd694589f27ea8d841fb7e92f3d79a0785c9e09fd4626c8fc50d7583e1b6ece9d24c1a3f61f1e935f8c41e171680c6ee7f0a6baa15adec4b5b26a112f480ab18419450de31919d715fc50682e053dc1d16a741be38cb1fb24e2291e04cb1801348b76930d8bbd7b05dd548d9104177aedad52682191651bae93e45149600d25d171255a053c5244ea416455277fa9865d93544394ba38dcd7d49ddd748e76a1a3e9bae39f29d8231dd77c03b415cbba98a65c086ab615ada30aaa85de95992e9017f13f9c400f8d96ea5a85eae673269d72eca720cb553757ff6fce4c77e7fd731f68c00f1560598b3f1b1864932ebdf18c5fbd032f19c5e64785ef4181f8fdef15267de47e132038417e7790f860d5e216b4248b254f38bf2a18718f9ea753b823d22c29e0b51ade674e26b8a9ef09d5cd424a31f05753a00d132aa4ec7c56fe93f423f7f08c70ebafe928ed91b687f89352f65c1bcbd3d5d235e427b608ed8892b1ee840f1a8a5e56de32dc4dc83e4ab0c493a3ff579d139ae7b3746c956f31a8416002c5be8ef33426daa828a2d2ae3c14d497237d7c166201d3c7976808f3f98b04703a2a76116380170912179d36d16575cc02064f702289c2bcf46e69fe7def16fdb4efe095f9ef93c7583d20d7f7da41a92aadaaa0bc9e0fc6bbadc30e170d588222a52629af2c4603f5bdbd138cfd761076970217849d44d47a2995011798c04444bd0316f0f7c364d20b164814a73311a63febc58f98eeb2f0c311ea12c1083b50128750547f481119f730f39e668a0b72cd573c781b3fc0f34ce65c9e315a9a155205f6d5d02fce391b912d3568b1a4a54172edde31602b4e93e24c1e5599ff44813e1712ae30222e3d087fe69f3a3d2bc71b312fbc6666cf3f671df62a4bcfbc43e3fc2a5a6881d4f021894d07dadf9d550686ac83b5f4a4f88775f2633f4c5ee06f77192d251f955c25567bbf481fc20295db7c53b81ade196417da6b8b885933afedcf80933dbfefa78d5b227f7771a402e920cb480757aace4c8c316161edf12973a64124f354f385e8372d1a71f582f867c93d1491c7a239f7b155c88877588ce3b4f13f6487e91c154c2aa2224f3ff0e18739d06d71acf66f477acb06326fe7192c296ba3e46b8645043bb680696a482783ec9d1e85cf8e648c3e55cacf7d017f866dcd72dfa2066ff18d0d36f229481905937cc9dbfa2b1d371d1e839c09654e76423956f5baa8f81e7e29c90ae91f6d04d05167fd09c6e2efdff8c3cd550eae82ce2549154c7f987a9f927cc5ef1f854df272403faec9b72445bd2a5221464ac3bf35907c95b5cab2dad0112862459fa285658102b2ceb8ea8cfac5b22060763a5dcfaca3a580514e7a83721917ad377d539ca343727c195c22c318f9fd9b332b77cfb4aad9e5d4be722268bfa244fe053ae57f8a0e78583d842334ed1f928e7fb5525017b721eef739c0ca03ca5ac2de0ad976460f755d1d88289dd890c89cf40a53917ba4922878d5b82a4b7b6ada27b34d0126bd6ba52ed4747aa6a2e6731dd0a97871e19afea234f98c5367e62eb04a7e7b7358c1fc12f66b7935300fce2d6568a3427d419f005f7486a80b4317f6983ae06887940ba86a00f261ec75fc42111e893406879f488e0847f784b09e9f9b3ff53504faaa98c005660f19374086a711f0b931f07899d626bb6f619908e71800239441a6df60bd3aa93599d2a0a682d48c20ec5b5b82f7131ec452e286b0ace3bf714fec76ca5e09c6bcfe28180c69de849bb886add5b533a1717d1b5ad83b55564d4f2383d68d8761e331b004f52b036e1774b864a56eb442e951f304a926c4c602d9620528a5ffbdad8c403c195440794244999377889018a81a50e980ade2180bfcb63a69de986c0d0f10345053effe0505e8ea7fec9353cf919ce4aa72cb021a743809bbe6de7487cb59f5cfa027ec0ea28ebcaf2add389c5b7820c193950edceeff460ef6869f94025bd063f274cc40ad981010998ca1f8e2a5614c0e04b09950a80e3bd5bc0e1d49eba8695ead0b904aba228365c579dea7713b65d034e0dd7af18ecceb7aa850bf2e8bccf63fc3fe77102d5113e8ca4c58749c4b9eb4052b3b3bdc036750091811d4b48af5733af4588a2d6ce1a5bcf43a6f11c00448f862f0b49ce1cb7fa36a0d17e2a6857ef6b20e4d75790b3cdc0ecf3bb59c90ff45f4e5ba5c8c4e86a9c1312f839826ceb204615a0d33fdf6baa20e7e0e6b0b2ce467eaee144d13e643adee29aa68c032d370d17f864c2645af02531b05a2fe40e233e4fcfda68fed9e4f0e56b8632b6b5b098646f2e5f1824ecf0398988daaef1a2a609f5d29b3b4c9cc8a4431c22474fb8ed5a808f2b8a0377686ed3f03901ffbcef9d61861b1859b2ae41654083b0321928a8a509021e36ee9d2bc665dfc5318725bb083c26737b65307e81cba5df0f8224d90af1b1126962ac0cc3e57d23fc2fd34071baf4479006e3cd8a6796bb584da9f42aec227ec9a5fb76034c8e52fcd1574417324075c7d53daa30ed1f8c5f537599bfbd17e0e2b4e7a71ff88d198389878d4f07a1f84d1589419fa640748ab08b7dbbcf2b243e56d7d58d2a5b9d57d7140daf57030fef553d1d63da74bdf166f90abd5576c6a09217d8f44c895a62cce950d56259d89678ec4803a5d6673ad940bacf043335e54680fc855e79e9cb9bf78a3fefb9c63e2ffb3121d9fb2d16bf29c61454d159ec16900eac4effe13b36d8c23f493a96062c2cb2fb8986f0a1ca574caa616a791b2370d1f5d75759a3d5a7cc40969f0f210492f9e2842a5f664bb128d80147148d4f2a767c51519d00bcd34a00062a61faa83795bc43a81c75abc7d7c810a60f0924c3ba74fccf9d46d7868cbc4bc9eaf3da34bbe3f55e044cef8d2dd0a4396e2c87c061c3cc250505dbeb46c3cdcd8bf98e35cd8e4313b137334fdcdb7b6975a2f14b24b023ec79f7665666580d4db9dd1191e3f47bdeba1db60ea75f932de36ae530cdbc2e5bceef6d1e96d1efcea71db488610f193b0f6dd3533b51bd418d03106ced2808c2b4661949c0e9ef70a30e9edf21c23d147d1a0ea8079bdb45ce3adaee4ec50c485e1a758cd071341754413c033012cd6807e30ad1b87d7a059b41651b885cdb6e3b53915c2197fa1db180394176931aa9f6a61e8d528c0c96676c35eb1d13e6cdfbd5044d5246d877e375ebb51a6985133042cc77c6fc8ca43312cfb86f18c08812e312c63d30e262fcf4209011b7a4b686720cf62808168e2131b687d19b088db03f86d494bd8ca796bf1bf34831ec5cb40485198dc660aede12632731743f09b858f28b93fafebca55d49f91c078465af8de934d55c3e9b86285b56372d223057bec6ac6c779f24b2b7ff1ee33ddb86c29c87f99bac4eb7028a8c0c282197c774119cf075ada40460915dff68452160685189ec451c31d06930d36042031e0d761a8c6880913dd8273ad915aa31c0dd00be81f20fe0d59b999b18eba465556c7190b8ab4d437cea05929a35f3b365089b5700c4e3e2834d3d8b40e0a0baa33b070f805625e8e9810070019405b008e01f4c0969e18574d3257d0ac09e0cc0d8439ef1c86a2e82d8da63a333b03852e269c351469b3b69e05eac4a809c5a440d55f80c54e0f217c00272c0e07b0de79fc631f82af20e3cc06b06e32e032077c4288d9c224a7d3ba9cc11fd6d090cd54b55039933e361c74e3f9a0e5b046574b3ee1cdb79c1eda8b7832d69710034e1bd1195f19ccf7986f6653b14c842902d1912b74c406cdc0bd780c1f386d3c373b195f43f079a6901870d489bf45f3d55408fd4401bc7fb07dc82def789625310d177ee5f444291000d7d4be4bb362151c044be8b8dbfa39220684a73282fe515921e9b7615ae6546998a511f8c9eadf8303e39c85842e2c4d7e89c0b516ea299626f20ce1a310c590cbd38b3719f22c9238e69d61b37bd5d2243010dbf6d83833af14e22dc2854df03c7db40a3ab16041df31bb285295802c7acaa43f9c6d43974d228859a529d86482895cca17dc44e29c9a904d1994354b1ef5891d3e8fd05dd257f14c36b891ea2f17102d19ef3c3a228f3d16ef2b2210821101d464515752a78fee5a2cc5e911a50a1b49027dbc1e6d5c010402f1bf51114240d5807cd5c270b3f7e7391d2036d95facab5ffe210a70df47df16059c086829053609639658427bc44c4eec540b93e862329ccdff2fdc15ba86e060a193b38ec60a0a9e25d15f341e9c1f3ef7ea564b72c05ed25b7409b87b772634a60ca1b91a7b4f455e5cc777a80239f5cd00168c22294de18162707d83ab7063c7c09632c71e18852bde58a13b20299d982b2299cf468dcd2f36db7878fb3a70c65b27eabd42dbd484c4a8f7827b8d750a8e76eb70fb7eef467b56d6b3e115bb3dc01f4f3b91bff7eef719805502bc1d460e205470cdeea3e1c0889fbed273205ac5ff36640b46a92f660abf9cbaf3ae6d9d0b74c767d59f838ac2f64e375af382502ea61f4bca301da45a1bf11198ca1670010bd58d12760ba8e35310b674804b999c068ed9f8de5bc088dc96d059243721215feb3aa42d529a6420c25d09c50754993d0964680044981bd7b3bc9c4aa67985ed553cc9ad1137a5107dbc3f306143e4025d931e67053647cae58c799000b756f861f50c84e351ddbc3568e69f452286d4d37303e78a3c5ba9e60d6c5adaea8c8b2d70ac694788041046a77d56592532dd37b15067e9212672391bc4f92619121e89d69d2c88c5f4b6f26109afaa59f39b6d0309f91b9dc14b0a3822159ec2e36724852c8ae8b6292f0f3aa1a4896c4efe8800ec25c1a50f94087a6444a7ddb0e1abb38ac097867b9ceb5e0986099a654480af65b1d76be26e7a3ac00125e451258ca8ba6f6d46cad899563ed8f5a9c8aa110eb18a468e4c69a6175fbc28ede76b2dcf857fd54d9af2997ded07f430a2c5b9d0f653811b39a157fe55e52f2229459751850b1eee4e54799f4c2b85c520b9b1201b3d5852b955a7a297625c5bbfe94fa91294ae78f01d2a417d4f0594c7fb7ced311e53d504c16886dcf1186d10b84d03b9236b9595ebe2eb764381cbeb32a94ff18b0a95ab252c085635f1c261f6c65a1c697f139920da690d2d11c679944ff96a0755e4d28425fbcce679e19b27a614d706c57e55261565b129d586b2a93bf26c8f16193580a82b926c4d16610d47fb6553bdac4821231d9c84edc5e2e83e4716d28e0746a2d0d3a42acf4268801d3fef597dd2e7e10635447790a6e39e7cedc7305cf7a87042b04adb73b1f376bcc665d3b71295fe4ec7419b406e6f3987e167bf3615a06d81fdcbcb07087c0fe430452e2e4116144e15e1667f0825909e7e622cc6f20536c64201d72f81783d970ba88833703e4e65d8ea00e8849d8e7a21edd01c51a8ef6b6bc849823d979ffb7eba7d2c0fecd0c1a7c50ff9b937ad33b369a0ac01f6d76faac550d422ed5a75bf53492db0e052d89f28fa9f4c8a022a30ab5d95e3120d118ca84e4bc1c6387f00de5260f1cef737e428e335cda48405322f4c4b138dfbde13d52ef1ec7087e91c0b8567687466b2a74c01b97881edda7800efaf6ea82c9068776f4c460c59403516707cfdc6fe2a8c0944720d0ffe4b71e9851c05de768ccdfcea4256268752a0c45c60ef3300a8fefdd0b042e63ec2d39ffbb66ccfc2738fdbd7d3b1411eb379fc27dc056de8e69e405fa40139dae67a8d3fb31d667cc96edd3e47ae1d3a0474f99a83f3fb032adf1ba848a340abb12482f42a7f47ca1518893351cd42af4c190ad013baff163403e7ac99f4b46febb6620d311d63b39a77e47039ad26a444a65331129376e1ef824b572815c88db172b48594318f2e1d2d8a80e5368e2a3f5c2c7677befa3bdf7817bef2306ed1b16282455c9362b932047b454d4ac205051d092144dd97c17e8a459d25371df5c3a130ea20f7befe37befe3267bd123ae32f34872bb21699474043af418bc6b682b38582d04163aeb8205c298e85cba53868b14e2007640566a852c2520b49d7128cc7e25cc1ea26c0fa328d55673d954e1f5cef97343633c2853d5d56eb415bf298cc74d8a71e739e85775a6965417307838f484158f216778f6e0519fc716937e6246e5ea1fc83c1e679e4de01cb6d07563234b049a87630acdfa82306fa612fe2e82c91a36464314b17edacabeb1d3f0cb468ca0ec1a3a99fe85483e93e1b9121d4e1e07e9a3a96268a50841b3868fc5ddefba34cbcd795e6f96b48860c06d2ca597e1f54ca88fedcad2ea2583a65f2610b1e44bb7f3316ea3fc618ad5737889f83877acedec327a70f23bd0be1d20e8727b2993d2ea1877447661983e351c78ab160e2e9fbac080751f20531a13ef5a77c26bf6f7728b3145af0be0b3954ab495558cfcf35b788c91de647072ccddf00fb4828cf8938069ab2a58634d6587f67a42acd55e72c8b2fc5f2749493dd41e708360a990bb5d17e30edc094e3d946138978ab7728171c37bf33114c13a3f64e6cc84d6c0291783842e9bf9ebc606e3b24efc0dc7387e773d28ee78fc33176fb1ae6548ba3946d678ce3f9f3bc47285e457b4eb0cf85a15f48b74e60ad7c66b2a72a46ccde6fdfc95bcb61b6b971bb3b5bb093730ffae58c42057a3775b5816e89b38b0c2d29c81f05e9abe742536dc4bb3844e9ec9f2378b9d75a6b7f873822046d3e0a06618ea7b8ad248cbb24e3ba551e04d66b01f1d43a6562dd56c02bc34030ebf85a60e3750b0a6d001f3ffffffffffffffffff47f1ade96f7f0056c924a5c5fd1ffa2b0029a594524ae9762b8e6893df08b10da18be0300a0000040a0fa70e550e18f285c2ba291dccd5e4b879f542f9ecf4c7bcaf5d28da85680dea36215c389b8a67dc13d3ce8e902d781fb6edd5aa7b339ed3f8c90d1e0fd142a964def8a671464148164a826ea58979eee42c1d2c14e7e64b14fbf50aa8ee6e5ca9697d9652cb343a85f76b562808b92e71f1662132e880902a14a4a5baa6c6895d5d53a19849e878b9930b9942a963dc5bd3134d947c43a45072bd90a35cd7843f39240ae51226be4d92a476241f70258440a19873d5b2d96f43ab6703a44302366cdc0c6cd8b827944773a6ea488fa36fe284a2fb86079de6e9f3498734e152f26ede67d9dceb32a19c318a264169ad8edb7ac8125a0db1dab7366fd79b1dd1739b5bf7e5d414a284929f3215e77148120a7b3245b997a431a92904096533cfde203c8edd7c2ae408e5901dd4a726732dd91e3cbe47b281102394424bda0a9bf50b214528f588d868388fa274884884e2c924add793d98de86e081942e95b5e6f2fc47721422897cea1429ed8caeb3f2408e520ccebe52c0d0142a94afe4c92e9eb0615427e504c722a93e4f9ca0725a53cb989a9b3213df03b63899f9418da40080fca1e7359968ca14f12ef5d942abbe42b6b932e8abb6719c5fc04928b82970ca17f456bc346144070516a3d9daa84fbdc373d0f122c14406e51701d3731ec96882d1dc416653ff1592147cc66fd6b51923ac589fa163b4a92a245d1534613ea5b4f8ec921b06103cbc40083470e9b45e9e4909f4d8cb9df4f32304064510e723fc632d5d4396c5f80c4a2b4714f7a8ed5c9ad375c0310589424d9d94f70fd58faa1b601c82b0a9f749a92417bfb08d90413a001882b2cd53cd31a3d6b3579d7bced20329e282b6945f1565de64f724158510cbff1a4dbceaf75ceaea21c62156e73a27dfbaa8a62ca1e799d595af79352519c3b319729cefea38f8ab22969a38ccea473f29ca2a4254d7c94a09a426b539464ae2b49ce26bf34a61425e9fc63a598291dc32545a93609fa325d294136378a629b123ca88928ca99573c6d3e25cd420b453964dc09f5be4151124ddd2441ce05f944e9f485ee8cee9b4f48413c5132cfb25217e2d32771904e94baf44657bdd40a209c28f58be89b58ddca1804b209d73ce7e76f126ba25892c6d5ff7ff234d24c94534db077abde24d98f89721235427d929f49e8c9258ab946a98c089d392b412c5190e11a4bdea464d896412a51fa741b736b9b64924ca24449d47f6478694c53279330e6203f072d9e8148a29cf33ac6738f41880f412251b8ef3cb325b767ff41a2361d359d15d74dd615dee93d8f28e66caef7f8f68b31787c086200e2889269cc7df19eab4ee6401a51f25b13d336616412178411c598948af8f2305f26c4c1f6618cb1438c1b3a7adcc8db0106182806183c72802ca224bdc9ea993c8dce25c90f401451fceb18f4c9319d64250ae249c2adad9b3e8111338020e2400e51d29884785042ac079364889224658af8f638e1fd17a2e466bbff27b8e7204584286c0ad9d1eb49bdd767102551ae6bd654499579238824f97f563587924014db3d68932696085513401434469efa4e3a747fff433936c7947221f4c4c6fd50ecd01244092bababf13e94ba35d5e9959d0fa5eb4bb7906be739d67b28090fba4a122544e8af87a2eea5e7d86775a5da3c9474a7953153ff92753c942d37452d467e7ffa0ee54d82b87a7d7a994ded502a0d32af4990ef92ae43f1bc53644e8c8e33231dca3909ff499293b9ff9f43d97af73af48fa94f727228c7cd2ac2456de5e9c4a1bcc14cff9c87294f1b1c4af531463553ba9adf027943e13c68c9bb2e7143319d55a62e39c7d81d036943b93aa99dfddaf89f1e840d057f2bcf490efd05c917156802c81a8a592c3d6a668eb372a2041035a04dce3c0de590b1bf494c9d2b008286926819d3a67a28ddd0d1e3460cc2c8b103070fb43394be33c4e67ecdc778ef3043694dca283b9556a3fb02078f1b670f002943494d6d637b948ca59440c8508c9f9f353f690c85cf4edab77c34f5e741c4d0cadc9c6d8c9d98a7e6de07b912d39f746908240ca5cc6b3a2641a627256e2060285d8872bdef2056cc7500c8174a4a7b434e5e7b26f104e28592a44d348672afb8f15c178a9ba3688d9d45933afd0e8b000817caa32663ce07690d72a064c70d1d3dccb6504c627fdcf8d1938cf2150640b45012dedb4295242655b537783c8f1c34e891c39c0640b2509ad139569ef425d5a938d87ae478410e74038cdf71630639105a4f00040b850ff6a9f539a2d6c2910387086cd8e891c3f4483970f0d024805ca1a44a480f42a598d4b9e2604b2c006285e26d95245f294adaf08b31c048088054a1a4ad4c9abf49ca7e33068fa60008151aaddab5fbcebc13b939675982124f0246a200640aa5d6cf2fdf5ef2694338d8d23a002285c2fdaba6cc6db6111a3386088e00128572e65162c7cb14f2da0485823ac9b38eea5b2899e500f28492c9da2de2da24493e0d01c4092525e7c4dbb4cfc17d838117e490c0ba01a409a5979fd19fe2395dc82e006142e9f3c870d206b1f1b2b384d2b686b470dff660a21943046673005142494993773c8992994194600e12bc2087046cd8b06103a108748f30728cf1821c86c708a30c240905b1719bc49beb8ba4190041424984f08a2b0f95a2a4e30b043942f1bd3406d313aa5de34918cf3a36b001102394d5b53bda35fa99592d269736993ad2cafc17bc40c706c4d0d1011b365ea0e38b1b2045286c68d21127e6868e01840845519fcc44dbd30390211436276d7b72d472b9138810ccd05b6f9bf37a71b13413dff93baca63001094249c8ddb4e993303752250308108a6a266d7c730f269e9c0901e40705933489f2ee52d65e82f8a0d4a6f4e8786ccca618417a5012a375d8ff9282f0a0dcc13be36e5ee9e9fdb18b925b9e6d65ffa18b9298a9eccbc3780c2a03c60c3e031fb928c6987e46c70ee2e22ebd6ad9181bd7dd52d66edea26cfbab2796b0419f5cb6286f762ea53b46f7f346161fb528ed68ee9a67f3831625b7efbf52425b66db5914535f5dc74e1a5970719bd65eab3657829fd858bb5f365f2cca9ad684ad0e9f3c27592b7cc0a29c693586fb7197d7150c30f015251556927e6e2dadcd7145399cecaeae2353c4957cb4a268b23adf9d374f3de7831525d571cafc3adec8df57e1cd9c788e788596ae5f4d3f934bc3e6545136b5e9b1c7c354146746881633ffdae0a3a2184c389dd504ed76f54e51923663bb9a8acb4d51aacda9d6744c55bab3521434a61293e476f2418ab26cd0294ac3fc07d3f918454986dee898a32f8ab2b98712835bad6ac784a2bc9983ca3ddd31cc536b840f509443a3e6a9f50869e2ff8f4f9464cd248ebaae8c9ec431f061e4c071120e7c78a27cc28f3a615589bb1525892da10c3e386118cb98943e36617aced92e0f3d9eec137c68e22313eabaa7baa5e8bd9b9a6b8589e276c9312e3ea70b21bc4479848e96db57d90d5284014b94af4b274fa25d89a268bd779697b2132aefe08312e50faefdaf1e3489b2a78d10ea9d0487f0218992ac396fca9884ce50e291c034cb877afcea84f00189a2acac9ab07c55cbefe311a50c32fc9309f3b80d5dc093f0e188629dcc303531757ecee94723ca25bcbeeb956e9e9c4a227c30a224b4c71b919e3ae9dac0f858049bbea4d7ca58c2c1c625f85044f1f4e64dd6b81f63b63f12518e613a69fd10228a4927594c07137f5e458728bfcbb896949ab6fe3344f926baf799a3aaab27ca4e89a2b3a45ccd59218ac9ba4b4ddeb365cc0891ea3cbbd751ff4194db94d43949b24deab73b081f8228698f49135f6381289f14a6e4d3082920ca9d4e38616227211bd2c237ec0f859feb4c52f69590b9eee8c1e3868e1eae63041f7e38c69c69c4d998863ffab057d6b779dc76851ef5a6a4dffc8d71c1071fca65caf3e79ff551b2c4b60329b891828f3d944349597222329987a78772bbe9242eabd4b3b323f8c84379941894fa1e9d4c4bac1f782898382ee2dd3cd6295376e0e30e85f3d5f63137b1830a6f470f313eec60363eea50d0edd68e231f5a63121d0a1bc3af9dcea48f39146e54c5095b99ed6ef1061f7228c66d57fdcc9ca34dcc39f88843c1c398f8a62b44c913eae0030ee59113cee424264d9d441b7cbca1d879c73d3ca87ccbe986b9a19c338892d7a89b3d43376c061f6d289fa02fe54a9cac2579dcf9c186d2976dd0f3214dc820e5fe5843a9c45f919f932c39e6440d257fffdc8e9b6a3b7e692897f253a63fa9375d521f6858d5e733f735f771867210328e30a554745eb6810f33acf91ae3e2dda235272b63b247bde893e48bfb518662887c092595b57a4cf24186c288ae92a6459d6cfafb1843e15c3fd697c66d097a3114a407a149d3bdf5aa260cc5370f934c64fa1c4ffc030ca52ed9aafafc2f944bb0bb1c0d93369e245e289a1cdf2676fe93d7f42e942e4ff89313c7ee4c920b05aff5f992cadb4249cc242711791e9f4bfdd0424168e5e7f0ec319e587e64a1a4292e565efa1d4ccfe3030be55d13df4c30a5db4fae50d2f9f1d53d79ecbe93154a523c6fc8ae3959cf5485b2c7664fb25f27494e3e154a9a5d731c2b730a650d7a4c7efa662b49480aa50bf5b318138a42614dfad9ced653531b2894da763ed4b4ca86669e50921ab6c409c51ecf1d5ee27426a9a4090551d2f4e89edb948d33a1b4497aaa2fe92ea1b89eeb7c6cf3e624490945bb8ea1e25a4a8d0c26a1dcd9fadf73fab59322a118aece4d6fffb4537784f2569b7c6e95b37b628472f56c49e2f676fe664528a7bacec963dabca1a428f04184057c0c41e443083c403e82c0e3e3030808f8f8018f3f390ef0e103057cf460e48307df23e9c163868c5d7cc8d0452ebc90818b1e3a74ec28808c5bdc0891618b92510b1d298c1c3768710019b390450264c4c2062330800c58e41023027fc20891f10a2f64b8e2060d19adb891438c089880860c56dc58808c55f050050f03c848c5870c54dc0091710a1e339021c31420324ac18100c8200501648c42003244010019a118800c50dcc8214604921d63448000323e517a514a68519b17a73c1c6ced8992a9a78f888fe260e3c13b90d18992929edf7e554e906914030c1e18e831868e1030278aa1f4c95b1ddb1ac8d8c4fa2547d1bb1d199a28e7573f5332b33157b4410d74ecf8808c4c14f49c9c756e4d8acb590732305110a7524b5d5e21b061e3ec907109b321c3122da312861e9041890cc898045a7ead31a79090218994796e8d9d993c8f83ad022118c148144e494a9d5019838e3941a2d839a86f8db974101d96f1887229496a964bd911c57fb5ce124fd291ee69847632f68695d32774198c287586f4305da98c4514b544b46bcf77f2e88d20b609137436f9ffc34a4994c4c45092b0d965645224ca25322793bd498dfa215190c9aa379de811a5d4a035346f0c1f77845e2f56629e06bd68444928531d53dc33e912f48211256973447eca17511a934d0a0fb522ca1b3cb6778e3111e5bdd499f730eaa24544144ccc15d2c4d337490e0f51b4fcf81d72efce33344441862e9331194a77868528d568d125e9587234434214d3cb65a9124cacbe7010454db2f5a1fbeae14241e0b14d127efd1488924cb28c925eb43d9e0051d2244e8b6913db38fda118fe46e73cebd969f24341f4878ecc9a44d37d1f4a276be79c3aa567233e94647c500d1ef388f4eca1a0847defd7cd6bdad143a93a863c13fb345d270fa59222639d428492763c945ba39fc712afa4d87728a78765127b73f888ed50f6f439f9fc772ed53a14f5548ab40d391b533a143f76dafb74cea1787782de183b8613a71ccae9d2c4cc7eb319731787727c3da94296b41aefe050523b6d52f64bf76ff786620c4a4bdab49d1b8aa2ac94dcfec145ac6b43c1e3a4164b1d6daac48652e8cd9f1feb0c395a43c17bffd34f29990fa9a128672e9fd79386728a075d273408b91e3494d4ed4c924a3693f29ca1209349ddfb2e971e6386e29b245d795c11e17b190ac2733ca184128477dcc8504e4ad041682a3567c23686920932c9257766f7cb2686726e55c3906ac91c4796a481a1a42abbc3e45c642aed0b256562bed6a3950c252f946fd3ae3ec8871353170a9e948a14f1a4de7e2e94f3c86a8809da427137741275d3d2ba168a16e74128a567a19851374c9236164a258927e4f8978c21be42e153f38ef49c413e6c85926675ab509635e9b92a93ac792a14654d92c3c67bcdba4ea1b06b6266769542f17c37de98a857a22814d48fd6cd7442a1541b532817cb8b27942449ff4bda54ef2896174e28cd5ec9bd23736810cb8b261445469b35318f75deca0b261473890669ad2f9bb6f2620945bf8fbd4148cdff9a174a286fccd629d234ec77c78b2494dc4d4e8368da0b2494e39d984689f5276fd2babc3842b9a4d5d7f0496cd638cfd40b2394e4d151923c4a4e5a2dab16a12037f6698ebe33f34cfec10b229434657695eb5afe99428081b419e08e1c37c4e091e6c510caad66592a2fba4deabc1042417cf5e849565a7b4741288973d7b32e21108a2785470d3a8f3f28e7980495173e28e96ddfef1eed2e6f82832d9731e0450fcae6a5d6d1fa04a56f2f785012837dcea042bcdb5d14352775b274c813f424e9a238ea34cca6b6d1594d1c6c65387824383817a550cf23eaa2e260c3cbb1a347e2a298448610fa2fbec811835b144e5c9349f3fe3c6c51ec158f5da72383f65f8bd2b7094f5567fafc4e5a94528449d17ba364167f16e53f39879724476e982a8bd29ffa5895f26935e8635112749c1294683fb2afc3a2a0edca558497ad9bd8bd22f3957d862baaaf1d17ef11ab6de264462bca72d69fa1d44cd78358518c2fbaaeea396ab8fb0b31c0c0800d24c08347f28111d8b06163c62aca6252e3a508e9a6776d559437b96e8e4929cf124c1c98918a92ce78cccb390d15c560a244878c995394cf4c9ef7cfa99aae290aa6c2dfe424c8ce3c11cd2845692dcdb4fae46c4a691f98418aa2e731e529d3346314c5f34eb287c6694ffd88a214fad9c4ca64913d27334251ca11165b1e3b1c6c0e8a82e69c6799d983fe507da224323cc9a23ba9268d278a2deaadce74bb843b75a29884898eea99e7443126a115a3c38bfc516da2602b9fe4684266cf63e160036335514acb7c4d6282527a264a9230dbb227b6c64431acc6a05466fc24c8380ed64b1463ea9cc3b5a491257e70b075ea31c68d1e6158a268258d90ef771a573f3c72809195289d385273839ea79d7d04332851d2681f7c4bdcd1d8231e93289c4cc2653d363cb889248a1eee66b2698a44c9839439312696ce270812c5d07dbaa4de5afd647a445953470fb33021b664ca0c47943aa5dcbfe798c5336e44d1d3e3878658e85329230adf49349a678e07662ca26023f3aee6789265a6aa88d2a690cb91dba446bf386ea41b3d78e8c071238c1d17989188f267ce59ea34c89ed88ae08b2f7268c0860d1d396e88c1431181c2c8418330c608c08c43f4a13e86dca0cce47cf13776e8b82146028699ce30c4692c840942b733c62359ef01469a5188e56e5566d6335db3947c3a95fc27276710a2d8e59fa45e888569508e94120315a800183c121ad8b0f1274937eaa630631025f93a9e12e3c9a64128cd104431c7e0b96df7e4a44c370acc0844d94e36396a93fc9f6d6cc00c4014664529496bfb7f28e9f89b6a6a467e28e9a449fd45c6f251f7a1309ba126427d9492191f8a9f777d55947b2895d2497f784fcfd043493e511d6632cb63461e8a1a9438a1e2263c1433867c9ad0d61dd01131cfd62fdb180ff313d54e7ddecf4962daa1a0e4cf28db8f137b928830a30ea51226ee5d4e9243f43aa34341f67e125fb129e49cec8c39147f358cbe2969dde433c10c39144bf8521b4eee3c62b5ae4008463a4330230e25995d776d4b5607af66c0a12842c838261ef41598f1867257bc0721bd9320a2e51ecc70835a5da287a8b66d28cb983caed7e6fa696743793bf9969ab1d750d0b9d64b86988def693520a71927eab2955932e7cbef5d29531a4a976dff794a5b338f869210c2ced5c284bff69ca11cfab4972841f466e76628c6b916b52fa2363633ca506c0dc29324099f837b3c830c25fb3d2bdfedc750f492e939be5792a84a0cc516f3b439c972e16174c3fcf90733c250ce63f27fcaa4fdf862c2039dd71dcc004339764eca8612cf3c86be5010252713fbcd4ac925ed85825259f24891ab49ac521ecce842f9436f6795fcf653626cd8f81d378c0633b850d01126299d2465324c36630be560df3e5a93707290272d1443dac99d9470de8c2c94b4da9d984dbb858cd50c2c14b4eca851c22f56fbfa4204366c3410665ca130ca6265368c7ffeac19f30c2b94c4ad4ebd1a55153cb94ad7b5cab1caedeeb24ef12e372a9d4185620e0d1b93d0183a767c91ecb8610373c3069882195328ed5e6596d40c7220a480f799a714ca4187d6dc4dd5b17c138572e629b3ee13bac3a6502867e7bcde2899f184c2e78c5d23c3f384ce9d502ee569467c6b424193266730a1fc79748b163dfdd8f38c2594a3e6491f745225651c9550ddaacd6586901df4c40a13f304cd4842a932952e259fde2432b52acc4042793d6cc398b4527fbd0d1b7884f2a79f94bede046618a1b4aed7252615e3e019452809a58490a22536483799418492c63c99e6eaa451b28e83ad7b241d3cd2660ca198b79db399975bb7efcc2a23baeecac60dcc1042f1a40659f7c983836d57cf7346104ae3ee1973d4acca1ae1605b3db3d54b70f0d84b70f0c80ea4e046cd0042398a7a10bd8f0dd294337e50ee24dc95786b6a4de70e1d37c2d8f16c3bbe3b30c307650fd2d77cd5c45c2a2ac28c1e943be377c7bafda9ac67f0a0d86fb28ac6f59783905d14bd764fde3c31a7742c85e8a2749e25de4fafba9caec78e1c3b7af48e1e3c72512a256613ae5c84cc6921b82847934e8eda8cc6f8e27788c11d728b5208ff4eb1a233d65a387a241f481c3d1231d2063d2e436c61955aed95c587d5eb68a6074d6de1e8917cc0860d1c3d12316cd8b0418f0ba94549d0b12b317e52399fb91821b440aef39e24d7e7cc62cfe439b88bc6655152f3df695de3ad8c2916a51139f27e1f930e225bc620041645914975fe07a1e5d48a30087945518397b6f5cf48134a1d3acc1721ae28cee77dd4f06d1f53c4c1c623475b03425a51be5e131af64b3d7a9c1505bd995f13844c6218d92a4a9ffb47f56686b19f5115e513325c26694ea9288b52c2db7d9d92933121a8404ee655b4d5ca6cc7aee41b25357aee2c0163878e0a80b143470a394549a49e6998a046976c88294aa393a444cfa7a564874a510c3a852e4f23ba4970495112740c9d29cd1a4539f8a714ef0c2689d21145b9f36a8d08a1d66fe250f4b9f162f116a6e775abb5bd1d2c93203a09142521acbcc6b4683a994f94fbd38f3a930479a22493b6dfc5d689325e5badebcd3bd9868966f2e7db7be4f8420c11ece8c1238413a5ecd73fd5b5d944c9b3be24c1b277f3074334512e37b525f5c664a2e441e3464ed86808c14441c9a8bb69f2f132c72e51fcf255cd724295142796b8c48faa536a6e2c8454a21c55ae6364100f1a4bc4f8624708259417d1301379edce248b3a13eee821860a422651509aa47a6538b9b2421c6c2689629e6b85d093d453281205f1b9c3c9d5983331be080142a22458da7c6f266993ba53d2712349d390479434099e25b731752911c3063d74f4d0b1430c17e38b102003421c518ca977f44b750e427b70b081a163474a1d290c0c988591438c2f76a8185fecc00ea4e0068634a2cdec6d572a46d4b1a621fb99e12d679220c6b488f298a8417c099927328a70d5d45b3bdbacc653e394203a942aa5bd9a7d22cafad9e4f45c5632699d811044944bae5c951613cdc1d53b44496cd1ab41f4e633fb56af430c51104af947171bd5da056cd8d04148218af199b75244bd7727e54061fc0db5d52341de5023c4eef2dd312f5b66b27552b3687889d69a64075270e3840ca2b01f83ba6f1d4f677588200aaa935e588888cbedab082181286f8a9f5025dc75ef6ec34693210410259d33c388ae5c28a9f387f2873f617573c9ffab1fca329fd24acee13f28933e144b9f3e751deffa2e3e94f44f8552eb98fa9fdd43b1c53e7d2e1d9e4ca97af0da4c5445e7f334bb6c73cfd54407f350eed7a0939dee9ca3e884e0a1a41e365fc921548708dda160f229de36ba103b943349c2698ca55f95b93484d4a1145f5672ebbaa7d66c4d21840e650fca546ebc5967aaaf103287e2783aa53ccf8985103994b64f7c92c96f3dadad33b838943c26f1537c1204877232f5a4b9a4c6494dbfa12094c922f7253c7ccee7047c087143d194a427eb97b24e2616d2869278f11a6d74216c28789ac88f519d19ee453a26d079085943f9e43a519409ffef51e710a28692ff78896947deec73728891340742d2501eebdcda77a1eaa3091aca26c599a026ca903394644796683fe2e4b7cd5014d9b9ad63ac11b1ae0ce85b8cc75a7cd6dcebe70c11a2df2a32944f9263facafb848cc15d11d70aadd592b92e440c853551bab7c46e732d8584a1a4275d9c5677e93c1f140286e2af8fac975c4249567a55430c66230ce52e39cff4cde2ea3630149370d224bf1d3d0ae36f6ca1c6178af9d5a4d2a4f642b1241fa565cc759390fe8e1e877f831cff450c6cd8a8d185d22971295fca3d69eb71b021bb1ecf034d50830b652bb5d91ba6e43cc8a550630bc560291b736e3af96ca5400d2d14370693326c102a63cc0d3e10d4c842493a2144e4ac2849de140eb61b3dfe056d8bb61d48c18d0bd4c0c239c9b9948d568d2b94a32825977f770d2b9862ee46261963154a63a3754dd3f649d951a19cee2e977be229a0253bb7976119fa95617b9a93d85c9f63ac86144a9287fcdd6826aec6bb46148a7157fed38878c934d6804249105b624bd0e7a1733ea2c6130aeb6b71a35df6caf44e2899b827cd3f4735f3b946134a3a8a1899e454956e0c13ce93f5d6d97df91cd3f18411be77b29696509add3449e6d012ae4f6a28a1245cc7bae39a3841a70c057650230925f984fabc27669297d640423189626f427d55e308d568e8862e8fa1420d23143facc8cc9a74112e57eb7abdcc26636cfc9af545846279babeef87507aed93a3b3287da7db1a4228b855ecc8f0a5942497d408424952a226619b93d40042b14cb75486fd4ea37468a06302366cd040c78e1d3a4cffa0c60fcaa19649d03aa56479df07a5dad250a725cb939441418d1e1494e812975efbab21da400d1e944256c8947667eb3aee62bb3d719d990bdd7c5af77b7d458e745152efeaa26ff22d6a3f81462e4a424c6913d46e6c46e843a0818b9298ef564458e9207ebd45e1b3efea867b09f2275b94c27fd4afda4b38394da316053132d263dd6af052f25f981834689178ae98b66a75767687c6650ef149ddff061938d09845f97369b390d339c77e6451fef826867fd8dca8d391c2c000fa22078e0978c5223567fab9d984fd070b45dc2e45e744bbd2cb3d73189525b74ef45f61cad825b4dea9b7345c916d57d69d6caa8e8c6f3a549aec5679b6a2ac61a6aad37b58514c624ed406d553e289345651182509732a553d897fd25085a55eda613263ad972e37a271ebfdfe4a6f9f8a9289faa94284882919a6818a6bd7543eabf276d3c232d774a7522ae518d0384569830ea6b2e34d539493f0b95f44f8344a51904912d74f684c096890a2701bab462919f27c6b1aa3b83e4ca88b29931fd01045496acee36626c7860d1b3680402314c524ffd6fe19734754344051cc247df292f9249aef343e51ea20229368d2ef2a4d70b0d1e0c3b8b143c78d36b40ed0f04449f80719e5f5f7f6eb08a0f5a8c1e3046874a2ec49ecef9e328cffc2ee02343851ca923249535ea2b1892cc4ae5ddfed2cb7d35d4e4cbb9e3749562fbb260a32c6309e995f1c6c3778fc0bd0df48c9179a8246268a5f1db4a6d2a1f3a68d18628c8189924c72126af3a6efc4b0c1084626e068a07189b2cb9f78e998246aff9628a77fd4b87a9146254ae2ba6a7952725ea7438982ce194a36392c1a93289e24dbaa66996892fa72d09044e9d342de9d98d7c504068d4814e3fe8cecf86d26069906d0804439e75172f8589224bd45e311c55c82544fa34c8e28e87492bbe9e91a512cb14194a7d22d619f146830a2184d091e379e67cc6c8d2183b7640c19141a8b2867d588fe6c72e4974611856f0d2e4aa672cdf8f0a0918862559e1e93a31fdf5d081c1165b5f4cc5c2adab07188d2d76a5439195ad6747240c310e52443e7136b932cbf691a8528cd276993f49de45c92922f121a842808194dbd53eec6b07ee280c620ca1e4f90f9548c10750bc26c04a2d4a6d2b46d671393aa38d8ce6900a2a4b6619a3dc367b8265540e30fc5b34d928c12e36d87fc5052b33926f5bc9db0bb0fc53ce2b4e4bd3bec523e9477cec2ed32c6d149b987922ee1e493fd4c8959abb3630c1d22a0a18762dac8e571a0918772ccdad297e7e3a1e41a1ea49ffae6b6b5061a77289f9a65d21ad4090d390eb6307298b543f945ffb599099e4fcce181461d0aea635225d5fa942edd1834e8503a53171dd7f3d3984341c5da53b8c9ac9f840e0d3994636e70134a76938dd00b68c4c1b6f392bd0eaf395711bbabeff817bc40c71734e0500ad91f4a2a193d83fa2540e30da58bd13cead3f54bc434dc504aab2e3b4d2f629bd26843e1838b3e31ed5dec7768b0a1ec25646fedda95183d6b289a2ee13d670223d150434969f61093cbbb63384d60021a6928eeb9baaed6ae29fbf4c8b1c304366c201a68287dd09ddf7162cd981012689ca124425654c932b21f7f339494e8bce7a5d232e89c32949327c9d3789992544b1a642888eebacd60fb18caf919da044ddb7693184a5dadfe260893d4974d230ca5f84df224413349393234c0501ab14186a8922ffa937ca1e0ae9a9468b6bd19935e2889995cc4c93276a12423ad3389731f6a7334b850f6ff5e3b6d72b650922caf8493c1440b0593fbb34c49338d2c70d6717a6d7195a33aa31ac3abcd9fd81dd3c042c16256e4784c3753ba4269445507599b3fa5c90a05dd9264b35deff27c15caf994d8ee87986ca950ccabfb6be614ca1e45989ef8213b87a450b4bd123e959828945e47895a0d140aea614b284f3b21933ca118abcebefcc398a89d50f0f88d29d2e4778e9b50b62b316fda6542c193dc7fd3fe49277509c554f3276c522b6eaa128a396e83d0984c42514bd4202689498c67262494ebef4fee4eaa4fcc7484820cf679e2223342b1c23d4cd6782c95a52294b7574bf4cd295d9488505e4db22f47f38c300da124cda528294b4228bf75ccd87fcbd43f0825d91d94d5773aa9014241a9f6e0fed7a34c921f9484c8ef0913320d1f94844ed2274930e119b34fa307c5d8367aa39e7c21dda7c183725032aae4a833aa94bf8be2db98bc28c932d8f9ba28e7bc51837a3c51d4f7b9288e188de984d8891e7b5c14ffe6657eeebf4549ea3c1a6d741244696f8bf2bfc6b7fa2bb15e7d2d8ab1ac24e93f9454f769514c5533db51a6df836651509214ca7a3c7dfc8f2c8a693b5e7d9969824c2c4a1993fc96c69b560d8ba2be8e8adacccaccaf288edcf9cdec7c4a5be48a829bccec494e5b51d61621abf24d4629112b8aa167527df65c32c9d02a8a192a94389f93652fa48a721635328f189349552815c5367965649273f628a1a2acf9d64197a728a91236ac4ff2763399a254bee124efd469eb2f4541e53db74ab8d5d1214539bc6c93f09c51144c36396651f392442e8a829da99f47f998361d8ab2065da5552a22d406457937d7dac6f6134537cfe9749cbd3ed1e389b25a491a2a62b336783a518c39c624a7121f54698713250fa2fce4b2b38972522635c68c2921aaa389e2e69264df48d3a93f992809fa4c14f99375730613e53fcd691b42c87efc12a5eedf12e5b8cd72a564182f75258a9a32a9797c261d6d4a1444987c728d4ed7263c89720921b73b4ba294bfda952737d3e34894931866f9c1a4657c2151d231879809327e4eed238aba7dab49924789b58e287ec7efa7dfd4fa41d388f2859d8e49daded6ce30a2e01fe447dbc74eaa338b2809b5e1efc4cc28a2e4a6cd437c103f7d22ca1542e6f79079ed4b88285d6b0c7f9bec83e70f51fa91214d8b6e88e28f651247eb4294e452e61d4bf690d712a220ae7cd304a5411437893941eeda9928055192e4469353cb768602516affb0ab1afa840e204a1fbf4d4efaf34e8b7f28c6acd2a93eaf32c3c40fa50c427aa8d25d5fbaa40fa5f0a4a26fe743e94a7cab0b795286d31ecab3a6c53d7af450dcd78d5623938772caf09e7d357828c619d78bbd50a7277728f7b6e6d01394fcb1dba11cb3ffe19dae435163365fbc9aafa6e9509af937e9ee3994767312fb94924ef8580ec56062f298c92fdf1387e27b0765366a6b533894b2e52441597b326fdf50ea6825975ab790d70da5902549c22a6d436934e7a05c83ce86524992a47b476a7fd0f91acaa383c837d550d21bfbc4b498fb501a4adfa7b2a439b193248786d287dac9224728bd9fa198f719948f6f876d6e86d29a289e4aeea94d4a6528a7343ff94a5dc7982743d9c49e743b317a8dd0184a92fca2e25d4266d088a1687f42c6d59230944dc92453530918ca1bd4043d1ade4d5f289a28aaf637f7c22748f3b8b1ee42499b12c4c9b3e9c4d65c28f50959f939e2497e0be5924ffe31312bd5b550d6199d44c427412cce424989a9b664ead5742a164af2875299f2b5be79ce74f41c6b2b144ef2989ebd949bc957a19c537394fa95e8ef53a114a73e964c2568357f0ae5d2d36b1325a78691148a2649d6b9e3319ae944a170233249d19324693b50287627615c43c6fc3a4f28cb7fb80cc2e2ec354e28abe79333ef34982c37a1a03f8dd6bfe68ca26542d13aefd56f846a7d9750da4c262de5fc93adac8492ecfda4f308377b9593508a9fcd179b8f992a23a11cf77f3a4e6af40bf908454fb28b661236c6f0b1118a1bbae46a934edc675c8492184d51523768b513114adfe16dec4d4328eef7c793f75d3d4808e5d41e3ee506654ac907a19c9ad17d7c45a7ea81509273e14992fbfee4fca07062daf9894e268fc80785192b49d3857a5090f9cd04a53909bb1a8007c5d4a724bd739e2c3cbb28ae7e5226c710a69ad6454909bae4291f531d3917a5925f8250e2e2a2dce14fb6ccea1645575169822e37b1ab2d4ae341ed95d61847a9b5289f98c4d1266f8cb89416c56c42be85df9b92e52c4ab79a04ef64f24939caa23c3ac9cd41e71365dfc6a2a4043d2535f869cd6b61515aaf6b5f517453a5841ef31451724539ddfaee4ccf4c9d5a5170cbd50e1db1a2ac77ebc1eee374daaca29c49d2a02594a82a4a327c8c6b9aa7f124d15494836daafd7c6aaad48a8a629a31d998638f785b4f51127f94dc50c2735c5b4d51509949f81b69294a394a0e19aff1b42949512e71849aec5d73334751ccc132e68cdde3218aa2a4169f9379c96b722c14c564e2ab7add89b22905453909f31b19e97f57e9278a9ffe3aaefbed6f92274a7a7292a44c754d893a513c41fd24ffd8f9b5c389a26f971a9ddf5159b28992ea20ccf3c91527a935511272bdf94b0835279d89826626b1b1adf47c1613e5249468223d989728866b0e26274f61329a96289a68aee96aac44e9839217b5f723b651a21c55b34a84d0a6cfca4994bb28ab283531969cedbfef115d94bdeca4d970ca4559ec838c14a5ac379d8ce0a214abe7418577b6a5e71605a99d63d61f6dea451eb1453193f28cfe7bd627ff5a94d76ae4c6d1195d7f97162549369d24362d33cb671665b359f110b21b914551c3fc9de6fa3f6dc2b128bbee85d0d599bfac1b8145c1633c4166d226670f6d478ee4033678ec7803c68361c3869d2d465e9123ae2808f55f9abeaa2683522b4a9904dd4fc2444cde20561494a0429b18b4fde9c657513813369bd2d973a6f154515027a388a92895a4dbb3b7894f0f1d44541464746c9ac96aa3521e394531c91aefecb5842cf970c414e52b5153dd7f52bada044b51bcbf3713d564c708f12129d80423a3c8118c88a23114331801457ec213c5f41d167ac3d689c25c2633931b13f67b4e143d6cd0a0cfc524edf3c82690ddd4f0f8aa6f35ef583f256e43cb114d944ecb96a063bffb7a6a2413a5d0b03ee2bb644e5e61a21c7af31bb944e94ab6d0321acaad114bf09576d9ee65eba66a92d5ec9528c88e9ae62739fbca0f8c50a2e0676a3acbbc2393b8114914f39f12faf15a1ee41d899287d41336268cdd092151b2948fd7be5d72d09b4794b2ccded7dd833ac984c388238aea269e43ff6aee5c8238d28892d84ca132a94cf6261c6144c93be992f7e4e360b341caf1851860e4e0d11d185944d1e7fa2d33bb438c335444e104b73c69f216a69388d28cdaddf14d42444184d22c31870f511a258e90676ba6c1da1143980d5cc148211e93e2a45eabb993445c821142fc26f667d930eab29141ec2e969edd62f5181144415efda74d82692f493e10e5a827e856f7cd0820925f292ae546e90f25492819de33444be3c70f29e195e943e1438951f5c45877970fc5cb9362b36e4acbd9237b28fc58597689ee9dd1eef4b0897c501ecad1c4c9f82763cc9f9ec3433107b1b94bd9497aaa8fdca174aa45f6843c7307237628e82cc97666b3f3c1481dcab2b657bba2c3fc83478792322badec9cb93994e364ef9cc142d449e264c1881c4a997b295a84e5a7130361240ec58d252919a539e792cd08237028cc87d224d9fc3d187903afbdae26c67c8fb8a1a0b3c9f8cc1d3fc99e431869437153ac26613209cee046d8e08e6d581555a23ab286921cdba407d9d89cb51b5143b79af7ed961a9ed6c43192864b96901d1a966fabee12da66335281108cb41b46ce50b24d328350d649db09f6182366182903da1821039fa71ae6ee29f3265def9a3ce2c47d640c6593d36e6f92d6c4a62d8652ecb5981c93acaf170a4341264f1d9336efcaaf236028c9a73ac5d89ce8ea170e16c1c8174a5246ffe05669fa3eef85a27e10bdd6fe98d9ec42b94e7d759f6ed1d90c174aed576b25fef9e2b485d2990e7ea561fa1b265a28d766dcd241aee72877164a9ec5635271b7b12363a17097fa5526bd57289a92a9d39b10ad5012b3fd1aa4dca8b60a052fc94ed034d94f7b2a144efa966bb69b35d3a6502a153eaab97192f8fc30228562973cd93b768eba354561338fff42a1709e157b9208fd9f33234f287aaa9bf1d8d5096cd291e5ff7b134a1d6a6269b76a8409cbddeacbe9eb8a7d8a6b89d73b9fe498ee5e42d13694c86ed59524292d614409c5dcafff31e96492768e24c1a0fe5faa4e8e50184142b1d364e6b449baebc2c8118a1b839273897797a43e3762849236a593d2307b79f7f148114ab23e982925aa36ea384284d2585fc9225f5485cf23432808d59a642849a6fe6c1242f184ce073ff3cdd8cc48100a3ac753a745dda62ed9ad300284c2776afd24bae8ee07851fa16ee207cd49e7ef8382a74d27ea97181aa79484911e144c1abf3a19843bc6080f0aa2eaf7c41cfb5d14f4cd262f5b134a64ae8be26736498ee5359f99cd45a92de7f326b933b22a2e4a62b8df51ed1de5a712b945490942875082b6cd85902d4a1f2cf4cd7f64ee7f6a51d061a3dc84cc7552fb0644685192b328eff469a429a959306fe9257a27f671f949e65326a78af922b228fe89071761e235c6209158143556c678c2a8cba4be082ccaa7d36aac0e4aee15c513334e30316f445c51ce391d26b6a6d912cd3088b4a264aad304a12627fdb2ce0122ac287fd44c9962525b7fdf9123d90544565192ab6376d6a0edf617dc0823078e1e3968a00a2d67752e6b6fcd656436cc4fc7694c1e1a839e53515e7b3d9374d5ff4dba23e971230766400415e5cdee3de14914f17bf048be103945d9f39fa989f224746638d876470f31444c51fc53626ff7e273da064b51aad2eea04d521435dfb8c63c7a829ae4284a69274234d7e507f98ba2a07a6afe836c4361366220028ac289cbc8a711b211f94439ce49d7262e643bd49e287fb66e10724b7c484da41385f5ac103ae82aa15217e144498e16e331ab854e318a6c8299b9d44a11d99ccdb44c4413c554a29953dd9789b2ea68312d9f93369b98288608b126e8348a5ca2b8361fff3b95f6d30bd32062898209626af7e4e7e78d2a51924f5242de2749285152b23d6a4e329dc9b74f22d95443598b972292287ea89059f3735208229128c96a3bf22b7f73494a9028fe088d2566dd3ca2dcf984e83bf9c3dd653a7a8481011a8838a2ac3123fbef4b7b10adfd40a41145ebf2b8d3e531418411e59e171911f616729e0f4416919ca0214a6c47498928a294fb27fa2d94321198dc77855766c55c6bceeb9edc1deccee4560411e5166562921bfb8312371ea29c49f48e51729cbabb0c516e2f75724ff36fca07d520528872e824f96ea9f8159511a29894a6d233ef3167dd340522832828494e42ca7f923e87f14e1025991ec54deeef0251ae0f5ad7b4e8542584778028fe8a12da6392941cb3fdc1ec7f8df1ccbcf343390993932842452757fdae0fa533b532fdd96eecc4777c28cc95ee24c792deeda1a8f15bfa7bece4fd54440fa5cb71d3b177ac83b5792889f268a7273e896a720e0f85ab59adcf29f363d3778792a4c44ccfde2c33b1c3b34341bb5ce63d2fb1c4dc89d4a17c6da1ca3756aa79121e39f43e20428762c8f8f62f42a72a41bc39944629f9b03f2336f3787228accb9658bf265e1c4a929041643e310927bf452270288fc688a7e9d9ea92b4226f2849b2789b657a720b226e28674cad501fd436943aa7e7c9656a8495a412614369d65f76664b759c0f1544d650321ddcacfe53d69d3e89a8a19cc5d7edee3715494339e3cf6bc575ee13ccee400a6e84218286c28e0765e9fa1139434166b37ebf53b9cf9d881950a40c05934ddcdffc25064d6a32144bc96d597ff22d273581d383c8180a9a29e48c12f5c3765a440c85d76c5d3b4a8e2261289668d27b3ce638d81a0322602849ffe757a667a54ef603cc43e40b2593d7e4757c4beb9879f4f1b0168878a124bde7b88de9d99ae51c225d28e9ecb877c2c66abd5c1a07112e94eecb54dda8fb8ed6f0e06128b285d27a8dec924d180c225a28e8c92ea94b123aedf9a501912c94b5b4538a52dea7bc14c142399bd2deaac10425485be40a2549d85392582adce1011b36c400e3b040c40a05257bf4fc23ae840f954815da4c9dd763b21a895081335d9e414d9255640a8eec6e8f14ca79212e645ccf18b5fc42240ac51ca37e3cfe66177d028572b54731255c7acca07e4231772e27943ce80cbb9f24e917db5940a409857dd12d4966f9bfca608830a17ab3947dcdd239cbd72c313cd54aa6129125147dd3e54ff84a28acdfefc8344a693e4942c93599768890dd7c915092a1dbb3a55de9797f89ef2232a5958c505e0f5aa684eb4d7d11cae1d4854ce2834e9f438482fc791ad1e8104a26fa68d026be108a25f37d0c93427e0e3f08e5246d7dcf7b2014cb724cc6c46bdff1076533655d562be2e41cf241614e768db490480f0a226684c8ad9becb0080f4a323ea2fe539fb07917a552be1a424df57cd545612aafd3facc4549a6f739d7d613fec34541bca90efa96994f756e514efda1493ca9b145e13d4753a29c24b5f0648c1e27d3a645395a8a8cc9369d9999cca2a065c6c4cf95d6a04a64513a1d226428db5d1bc5a2243a9689113ab0288d944f9e4208fd12f6571437cf8cd075dd1565f390df31ef924bf556147decfef663b4d6d459511871b25469851032fa2a4a7232cd234c9099c97255944db2cc9ce4f01fb1e9549444c9b32fad4145517d4edc51b5b924f514e551527ec4c64d62dc14c57c9e5b4e92bd42c552144e09b71b45775c3b5294cee3949bfe1ca5598fa294d5e7aff7d93c8c4451eca093fad78f419549280a676a2a66c49a691b14051f99f389a727c9f1e24f94cfeedfe3f224a57f4f945d63f82fd5d94e14a3e6cfa03d09270a6a44eecde9c9f0d1dd44314952adc7986cc7bad544f1d3a9ee6c9248593f13e5cb36a5fb63a234579f35d294c929d64b947fecc7b26489a295652653f259899270672679e7786f9ea4444134335d7adea0ef242751923a7549943bff27f7d327a7b52351decfba9672db510389b29cc7d5867cc7971f51dc92bbabe3e667ae234a4267d18f5362921acc46940411ea3aab456b49624471d683ca86e60f9b5f44399c9be9a03db68b4811e512774c29393d7b73228a23c2bde4641e6737444449c62476795d7aef8728fb8a9fa0f2d349526d88a2492db13a25a8cd72210a222a73a14b559d4b8882bfc6f0d9fd4b65378862e8514b1005d97ea6fdf54014ac476485d201446134cba7388dfda164ca473c67f6762fcd0f2531b6fb3a96fa50ce37a99367eda449c387b265faf43927254cc8f750cc9fad348d5fd9460fe55375adaa2629d1da3c9467cce46c7a25e58887e26ad09f9e335668798792ccb1493a41e7249acc0ee50fab1697b19baf0e2521b5dac7349d5255d5a04339d5377c38d3be1096f1329e1839149428dde49b9f739e588d3814d49b9cd5faac010773418d37144df447bf8ea2e4088f1b8a39463c73d0999385ae0d05554aca3dc2c83146d6a0061bdc1cb76a4d1bd72e9bf9cd24c68fc16434a14f4c75d45843492849497df2c3be8e5f430d45f3fecdd4359b7be71a6928fdc976d2e875dda9c5c8ab81863b43e96e2d737d96209af366289d184bcc24e8e0563aab518662f270535e62ff39e56428de8892f38cb410b163096a8ca1f8977d67aad4b5018f24a8640e6562025924120ac40171281406a0712600831308002040200c86a2d1581cc89af803140003582c1e32302620242418181c24148905e25020100804026120280c0a8582815048489333ed06ae7f9f201f0b1b7d8f3a80d18e62ace9834754ba3e71f98670e307b10ec0b8a9cad87d1160db42b39a5309ef5650d80ba7c0b03042bd52813c323dfb1064de3dc3bc20b785f08ab2209a0db8e758ddd450a1f5e8a847fe29966f6c6b998d8bd4251b2d9e1e8a38a683ba29e4c9529af469e5bed2a639ed7d45d7cd2ca96d9b4d342f5ea061ca2ae024fe5b42e8d726dcc03f697182977fdba83dc9f834ae6a1497e94db70296dd3861e73c44e1f1310ccd2f1357816147f3523130c57d68161c722e6b929d52760fdbae4ded98217cca1b7c309d5482d229e1d53994a9d944c89f59bbfd6539f6fc7d629644f8382710a7a921f21e3219dd75e80fb1aca5e4a95c310fdd181c98c15c6dd10e297a1b23a5b65915c4700bcac49b4960a810b80d521adebd3f99043c72b98b9af09cc16e9c72cc47cef328439b41ab6963e5aad88397817e45881249dae04e0f5c8f30aa1c26c1b69ded1e31db25951f65e3e8add3883f5ef8bc78747065be5437ffd8be32cc6a4d11e84b27c493f7917092b17c3a65cd519584679d1a004e12c0f698cd9e3691a8b07092d1aedc078eb2b272ca83cd6bc26b237bfbee3ec6e10ab69bc553f4f7b3e46bb7ff140671e526355135db51e5dacce09b4a5a2be1f44477b6d2b25c0f7213fb8fdd7388e753ecf7e6e6ded90810eb4e119e2b70e4716ca7ae0a39ef0873108ed874b172ce0db11749d423d925338af5b340f4aaf350149a2124f2ed5311cacfc93a846e2230b45e048ed7b4c9bc4219882c9fa1f830c1141d2b50451af953b068e491ad41d708d772adfe898969fafd68076c6c7d4dd32d59ad797a00090e910fc34fcdff167cfc91cc0b21ea61edd88be722ed2b545e3b26a0950280e882316f2fa14d3988690a2b4d1b5b41f609903e7108deb967421add23f268380c39540eabb110a0869d82468c39dc6b3ee50fd62709c883ffba218f845ee4a94597cfc617ca49c10b0f7a211e037190387c555588d0f7c44bd8112de7a16c5bfb088afeaa08701d7f4c336117eba0852f87f050288020304c08a32ec54c5eb8ce95daa72096e01681058a61ed65fb48f0a2e810b6f5c59f88823b0dfdca044e61884fbc06ba3cd0f71b3bd3cdb716a006e6aafdc16a1d04d133c00263bbd48ad9cd94485eaef17cff9d91991f24093e6a1bc1bdafa227b471206bc7a05cf12559eb9c7dc2d9c910f122a3326cdc8b2e9d5cc6e341098c651e70cbb4935a46f8ed2cfb813a42ee643d367d17c9913012fb02e18a0b0b59921ba08a1d6076fee4a4c52c818e8c1c5d569acf8039687fa8fc41a74686998430a3351c7189d10302fbe08a90dc4136b60f999922eaedb419df9a6f2b1b56a580821dadfeff73a3c8fd07292c7f425a277aa900752c8103113707a61c6b691002a7686a509846ea60ed984d1993de70d8a3ac329a2f6cd5223a15b2228b913362a36bd6472a0424af3f127ba5bc37705cde0446269a85331aa9c3ceb25d15938d1ef3a214e81dd52438ed008bcfcb4d049fa5239c50c5347b5c02a2c6d1cf03e535bfaa70b2a82b6a2307d6e0a619133d4fb142d649e84ddf93609686ce9bfa5478454089e28491248613c7657a89f569a7299e1cabf810ed9a64893a13e031642f7ba12cb152ab752abb22925d1419e0b6a1c1ab8d85893b340b9f4a756879dab7993c2fd1e19f70a79261384b12f6ab98918606034d6c06e6a1c99ab51426f2d2f10dbf151b23cec4aaebae025d7d9544558f1ce903dc2c86a74bb5843b079da05cea739f0d36092c5024c8480aa4e4f875537c847e178097ae844962d4cd6528b89a367cf238c3cb3484d8028d63fb29a84c41cda42373da3dc18445a42b7b0150ece2e387afa2dfb2470774e5ff11a90cd2856449264e5d30c290de0f3ca9d99314a81c9183350223d1ba1d76b128044a151c99a296a4a099504d1b25e8e24240a13bc84ec6244b135b2b80b28f6487260b786477c1f33b908548ad42158ef599c6687c9f009b33d6c1f74990a0752885f8ebea3623ab5d5635264414ad470531212d140660c03c6f76b1580b5747036c97f4e45a13e14105f46ec844f56607eb11c25410a8a193a3a8de3569d12bbd0126b16cf36e22c08ed74203c40512ddabb01035a23f360caf18a62725f58d31483f3a1695fbb57abe17ac4f577e28fc5569c3248143ab3378e124bc99db35522a3ef0d473c816be5431d2861d2e22f116060c3b6384c750df416a30bf7701b9c8ebe2f8253541814f6b881ca025d70e9c0a2288dd192727e46455119306c99bb13702bc46cd93fa3d50cf9bc310cf794f09c4dc0e63aa6a60694edef9210732bb3a45620eb07585cef2e29c3c50281c2f57891d3a3efbf831bbdf9c1a14c0f23ea0afa245c9551335548ead8b991c47a89c04a0cefb8c3610bf4fcf1daa71c576b4418e1a57eb6b48a9eb5c5dfd224869e9020cccf40dd1d25ec0a74fbc5da18eeca22661184cd151731c268f3d9e04e7ca0b111b81f8975e079c50c587e835afb54f509a6c2806d28cb596af4bc505d827346065aef3277c47864fb8b6f28e8e639bdfdd1c0f6661c627d9ff72630ee329e9358f12303fc96eb71439cb2cfdc193ac87c411829aec8248254d937e0786cbf9fc5bb43985ab7c87877e9adad146a8d29db810febfe30901355ebf0b88ded289602708bc6abd8895fa4ce25f63f35b5f0e9bb08c6975f45005a1234bae3019269e893cd62efbfb6c14d060d2aab8c5724fd8e0bb77c6a4bd18bb3e967743e174974782fcb13eb8b530889428b57e06e58bb03343aaf657b96c278954c6e15e7a15d77b4b25842a20d3604c36027934e965448bdf8b6ab208d0a43004360ba51617ebd460eeba8f7c82ca202505267e0b6a74b65455c767180d2bb2bb7d5a9ab6820b4d979a968c6e478d39e3874c758ae64a8f6bbc866b19e4a44c869e1556cf6bc17c044a6afea4fe081898500208073c5bf9795b73e6b4dd2bde6e727077d74b4606d8b625c5d59943f30391c830919e09528db1541eb04a76f545efa12863209a2d0f38c4fd36bc6e6f04493c8fbd502663d2d3b8ef55d250633fb7e353ea9a9df45941a5b5ea8b07c4e5859b132ddebc1ae52d1e0a3fd79216ae75a3d6af9b43c0d1401f830a501874cefbcb56429fab3c4823ba7f93da4c23b8ac4f07af7284801eac8682561f99edf4d6bebbb37aa7a15e367aa757567444d47fcdecf6f7f901232d87c3fa189c443c52008fe3de1884d43209f70f2293a8f9968aa9f30602d39ca27112da151532940b49a965920aba0c161844a8d6217381dbf2bd346ab626b4a4849c259ad7e2ae7ac2d0bd58d75f6c2551eb04be18e82c918f99b7011af0f9e54057bdfa47f8b28a760e03e9c16d7ea528b220e317f8cd6f227c94fa91a9817f7f8c6048e1b3e6f432763f0921c63826b5702ccc09cc0eeec6f98e816b6b9348be316dc43bd619e0f97eb4e7e5a3568f0782fb323c19505d0cd1022c3ba78d553298dbdeb4ce135df4b796cda21d27576639e9ddb79e25ac4f7ff206d1e95928c1b79b5dbeb4781c73512df5f6f430eb4c6d2d54d03f9851f66e2a6245e4c04f45bf894775c3d634eff54c10dddc0c5cb7a2b6e11611abfab9cdfc3aa8277c1b38036db4b6be8beed9624a8231b86306310a44bbfa121312a1a0bdb42af1daddc42bb7b1acbf73c361a3e42f7054cec7993343cc304f53eb9937a750168866a916b9ab9d3659343ac236a1eaf2f694ae1d1e5224d3e0c0395dd1dff3c0b0638bd1a3d8b9b77994d2e810c636bb94b2a043a57e7dac1046f2436b43fc8ad12d72c71a5e81b5366ab50fdbf7aa24a209f2a147044105d1df584fa6d7bb9d4f62f76c0acbfbe2ba4c135e066a8a383839673a7d5c49d18aa7b1d091027a8bf8bd85516c22424ae68482119086cd45c07cf8b4416cab3926526bcf27fb56d3cb6179668a9331d59278395cfef33715242da35fb4f37566728a3cfb7ec4a82d7293923985072353a65e497cf1bf0879a2d956629e1274d0af9b0057723bcc66e6612153c149340ba85cc7c9365e4339a58b0344308d734e0165d8a7b3b96a85cee8348dca37f064d662926dd6f911e3d2cad448085fddc5048209d1a6f95bc9f8b65520fdafc83b8f13462cd5bbede0e6f8be6789fcc4523ae803d0c030651659e7bde1aa6404ac69f18d328f419fda1c3d5351b17f6a94d8c3d0198b91c00071d156bdb1115e0c783c481041761f2afb2ce2ded52716d7399527ed311412b525f8b275748f97ca01c1a4913056ae56c11a495fd31de9a52cafc52ec609832a27b4a4f6241bcad3fac6a8e7867424b7e4c16ed8d003d5d3da95a4431c74d3d48b467252239dfcc125386e6f740124f27eefa7828c7049132c7c79b274befe0365938b218386378c07c8303eba3e7d533587c5a4153c6766f74afacbe05c36fdfd85f32558dd76dc4d94a81cf01fd232415aab7f27155c082a5b7e22bd2b74742ff4c2764c6fdd61ec4ce88627e5354bfe7f687eb11b39293201aa0f666a7afa4dbeb3b5f88d924c6b91a9cacec5db7d17c45fdad413bd4bcf4916c90da46d1d9f823ef260146e8aa1d050827cb55504790a10b530327aad42a76eafa4a44bc9dab146e993954a5ac7e80ba87252e4c041b48a9374a26e55648af2f4269400651a13bf43d9837901c623491d055b0ad1af7e325de0f41a933d43afbfba9998466de5e0fd5cd1ebad5a1ed9ca456c14edadfc90edb53d6e52abb1fbc3ab4726960f4407a48a9c5b978b57d292f03c29e64ed61803fbe6ff214ce4e63b9b03b3f425c06d63619a5f88600010c845b66261036207f89a37392df466a8fa878b9469918a00555052ef91b6ab29fc03cf52c587345b5be5bfd4ba0f4c61bf3838b75cf834418a38bc9e7db36ec1716670ae4b059fe168b254e625669179fadf4cb711d31a847e1a344eb09762ade307a5bbe5f40b4ac0db0b5773fd1834cb17f15b58680e8b00c6993493d2433e8d6a2059b970fea3041a8971d9e688eba757599c69b0973bfa2c3235126f8b1393ebb7f904eb2771d398d618a57bfc14a94009e6e78d41a9eca99098258c93944918e2223e024b443cbe2324c3fb16814fd164305e6c0d4c4be31c731374ceaca0671ae1ee3b62dbd4b923c0b7468e067cf29a1a26816777b6eed991d2719249930c323f8e973f6c74ff85d41504908378bff5161895a4b926181542d8f831a8888e28895a3f569883049c162c3d8b07f0c7926d88818ba6449ede8105613bfc95edc8722eaca428e9d36050720b210be3ff17f293739244b06682e968a25ab9c0a066f2bdc1ca5d2bd96a5585d6677985d8ce683cd0118a482cafafde0aba890c714b13b9d90b7c780b36ae37210c4a8428024d16b09ab1009e4472493f210a38bd1f7e0b5cb12389d78ddd99bbd04799dca63a9c3a23a99d9cb9a77903e14b6eee6e1d1a104d3eeebb7c2ced824a2dfb8ece50c6b96a2c3205dac157ec4f2a5658688165c57498917c1e9381d15a98fe5fc6bd92c7c193dbc5d66ba608dd550add49045214b5bbc122d9bbf2c422722780b526121d12fc7add7c0b2357ca4a3ec0151a60aa47e7970394da561ee58be47805d22a8e05716d7388f43290cd44c783deb6a13866726b155e7136313048f03f9c40846db63e64375d47fca9067e7a0e767efb750c2e43de0f03bb29c802b2a74e04ad9e77c7e9c84fe81cc13c33a8d0f7539f601bcd74d0c50dc4bfb31d32de49da6a5e592dea365f801bb4ece1c250518e7c5286cf09f049a0433ec34004e4209dea36340ca8001c03cd01dc1ebc563aeb7c86b1940b454892f9d8ea085c3646978c5a6e2e009c3c42a538f67d92069c874ae9685cd26d1e551b2ce4a86328def89f84890f498a8f173751a9b39bbf930782d97994b70827971f25df66164b70cc67daaf71a780b51e776b7fd82447238f045a36f027da6048bb3689a6b885318c1a65971bfcdae03f666d77acfd64b5dc613cdb8008eae263f3b57f72d463db364a336e1f5210e8d14f3222dde398e6b6718aa26c39df05460c87dfb6fdefc953e63031e8d72b67d11a051361248c5fa4b37ad007690fc23d140289a9f264f59cf0476d4de5450be07c49fefe92c0bbced171a28975a1b6535ea87e215a7eb3a3feca5579469334828e06e7e67bc8f8000d8c732cbc32376efe9a3ba7e9829aed5c9c5aaedb8f0498d605da6e3cc256c76aedbc0518466bd5bf22bb71248bc7e267385190d330be6bda81f6bf3044eddc9bf54a1c33e60d00814a437ca1618e92a95d29317508573ba31b38e5e78e6c7a8c4a92dd6f380677df594c9d383fe57e4ccf3186430405bbcf7c87ff49db64c86e50d928cab2273d083f6fb12b79002a1cd3038746777a93dd06116905fae7bce04140efa2d96f9cd7300c27d2725f1c17d3e6d77d385d96c65eecff0f726249563a0454e983243a1931450167793561dd9ad72bcc763f47c97d183b65e4e1581140c943538228580c7edfc721e18c23dd59391f97920e7bb9805df6e9b8471a76999f202a352f1aa3f4d118500f809cd5c89987fbb8ffbcb524476daed24c9a22707651cf6427e4f4912e7992d1aa64c0c1566d234df25b515e8cd33c09e7f162583e562b2d0e6b2e3697f447a0b4a82cf37d40fd062df4beddf37332ef032459c2c7a93893f707efe33d22b450982f2952a2919929bdb16a92885a76d2ef6beee348988ffeaabe8d5fd6b372e9613b8eb037d9065ede4be7ba9c88a99124a5943ecc14d97a2376b9fefc3b45ce567655538b8cde82f9fe29b4d1d3407950a7cd76e1414968c0742a15212c2ca71eb334a64865ccda0f9cc28ef27b7b0f6b337e9b34ddf9b3d88bd0fc680667571fae15f166777db285ed440af9d10c2015a932cf889d4562c5afc97868e64890732a5ff4d290798541906a5d3c0f67fe122e536c4c1bf05e8b2484354d9f79a554e511a18d7cd71a07c0a08db237238584f2d199e6295a83a92392056490cf53979dd83373462e8b4b791ea7f3345c8017602a24450a0a435da4a1eccd5aa192049f59a39d9e4faca999896b91bb41ea9ad9c66dc549bbd74d41caffd4eeacedc8a15570713feee419b80f0fc1387416c2d2c2279309e194b5abc6ddf32204670c85dd79ca999d795c9e3f31d211f1113bab7c61c127d674d92eae0b7486264caa54d8ab7356cec799bc202e952ef290acd2838446b5620cba6a73feb643bf14f263be16b9c13a943e5b30157a80bc523274c797c65054d12922139e54956e4794a9c122227b12a6a7444eccb44af657f9f806b6cece1dbeb727f3ce3d3e47e44e9ccb5371392fc3693c0b3033c4689b75f30d8780e800c84ca5a8d089e952f93c116794b60359b5f3393b0df7f1145c78cb9014d923b5c2b1da002bde4dc7cee49f2e0ac3dd3ffb5d8423a38a45967ca98c792fe7de1856077515b9cee431e2b0b779d7d5ad768be74c72bedb458a4998f565b28e0a15446058b3291a44322deba806627e464bec0ccc55953708de3e4157372724b0502823dbb71c21968418d4c515f66602e8f57d946687700ba9ef257415d277da30137ee9261a214feffe36cca5ca64f0a980c12029baa911202c356d72056895580c708fcab1e44140f6c6a69b7e546415acef42721c703c2e0aced9ccde0b7eb844b69955ca4aaca46d66458ad8a569416fe802cd8a909e414cc8e45fe4965c8647db394c9dd2d0e2ff0104502b9c1ea684d9de09364c170e69c248d2cfdb5277e4b40ba5859b6f99c267134ec403311eb0d638864adb11b666c721d98ae7223827262801347364aa254f37c73d1b099f62d018ad1e1b0fcd9c22f84529600fd1813ba853765cbd40de19395453b73ee0cee7f06c410157a2cc80662bcd7c8bb325f9cf4be644d89a24b11d7aa6c46f48d9c03c2af98969571e27307e82245ae67df3d41cd53448673461594f3156884669a9155320098c402a58b478f12ff2f570a4be08b9e99668f20b80ce55f1a84311e08050b4c0031ad8d26f18b866e8be2d9600e3ab59817057a5cf1680cc7450e6dbcdfb65712335631e0ba1f295fbda3d1cc6e7484247ab954bcc12f0ff4de5dedbc955e2e54d78fd17091714f05446565a02b423c801ce933b64cf42129ff431deb3a0db6bf90205d581bf70b5f0d194018d93540e6e70a686fa9de69694187d1c515e87a603d30462f94d2605c118022731e43335aa96db51a405f6134a9a09d2e52d2fa5562b41478a5a1154dd0aceeb172399856b3b0fdf844b455a554b73dbc7a0a993f8d68278b57216f7236047d6313d7c804e4cc894cac0f67c53d29fe38233f19ea436e2bf7ba7a3521d3ddcb067545658a0233e24427ad3d30725d3c40e0191080059e937930a711b48ccd38584ead68a389f03845245f35c4cde54f8719b4e25b1583a5242ebc15955b57df2a52ca3ce2d81abb0fdb6c2635350445f296c679caa93fb815c0b663e9f285707747ef729ef35c9709a09e2f44aab8be0dd0ec205de201a1544d761d3ddd2de46dc2c1c81b02e1907a121dc0c7d4f89ea3864df123e2ee380330e9b75324e8562d65fa3e1e626e2f0b6adf8f7fad188eda23895e9e7b223ce250c14f8eb8e91514e06a76d684e620f762a4d8cfa3910b9a39d989cfcee708ff1b782d5ee463482d831ab6580203653a18ded8d10e288c292b46d9ed37bd26345a3aa1a893221c0862a1ba77a864fb42b6dc0dcda3df8d813dd45ed404ee971babb7383adfc67b0bb5f049e39dcbd080c8d60a4d49fdec6660a6b23b08e10d481b790d261e9a87034879f7c96d08591de1f3e4b9914b353c6ec68554df004872bf238e65bc783000ca7d5b7ba9a9bfd5373c09401ceba3a4ee3130612a89a1027d06ff2cf3242e21a81da96ba0fea0102823efd92c39af11deaed7ae1fa7f9b4dc9165192713305ad87fac349e3472959f49768b41008854fe1bcfdbc66bda374ec83f40063f839a0106d5bd4773086bfa043febeaf4e09391cede8dbd3dab7de3889c6ef4856cb62f34a69df9475ced3a1ed04cb2219e133676c13b5a8bec03d3e660cd932f84b074197831b9d13d52dc1688370b631d228a8c9e7420cf2368002de862d74c446b3f5c2a2b7010c0fde738a24090c544a9a6b2a94ffc7cd6d5d57825300305b048b1ba2cd98b0e0a0d5717e94ebc1c2c6ccdb3467bfa4f83f7d6de2924f13ced9af29681e5ef8cc22d9b8edaa364961a8fd59804110b64c452eef83c1eea26985178231bc17f5a2985d3b9207c7887aecf0106f434b5b59a7c1e88076e20ef0e5053b23e2e13895266940522a0d08b60eb33db5fa1c8d27d59162ddd9ba2000f67f32df18327c120cb436c991634da8f490fa7777ba92950a3ead1e3197ebfa45f7ebedbb3d0c60fc95f1047d1c60453811433934eb89a0d492e7dc5bb8e2be681f3c1d12761f1eefa66f629e2478bc1afb8dfd751392ebe7f65b4b4f2723af66ff0ce8f4db42849ccf06fe213566983186e801a93379a978f56283aa835144bbf309aa9e1a3a6fd940144400b1b6bda7c6529f4bc8a92cbf64e8e8cf4473081033ac6158de928e581a3d6a59a8e0b468508c79353df80e083c59b9ed37cbd3408474bf81b05ec96f92606dd7d9ee62c4d3653180e75e240221dbc07a0a72ab9dfe2f26c69bf48cd19a4f3c8e4a2a09e7d2133f35f71a22ef9600cb7bfda351442b0a486d9aff25f85ae6bdda3a42b8d352e95e7b3d4d9e3f7d2cd1ceb1b6c293625e9bbf31c3bb4ec841005c757367f3c4c12c3ac678c4f90b0f35d9da2442c6d4979518ec4011bfa689642c07ad5cdb0f169c30059e3f4c45cd9feef74b7936944ffceff5f4de15efba5dc3d3a60d216578d47acaea6042139ce6be055db3bfcadc82106c30c9dd6ba414e5217bfd4ba1ca3a57d32a050198b3132f80208a16463ae395c12e2c763522887594d2822521c30cb9eba38648951cb2327816f64943232fbeb6091c33ec22b6434f6ceccb714cd343aea0efcb784dd78667a56721c5aad48dc09237ee72729eb2cf21f30351418498e663077fe259341403c68c162739542e5bd6f5fd492882e8e5ba753a94d8b36e3d313cd3bfcb7125a0dba1fd83b82ec57106bf4bb6e3a7330db99c38836f41aa138d83ec2c678464fa0317bb322cde52da56246598e2b59237a68a60ad64df1c5512b6a6653a596995cc11c5080c13c5bc10a371b7cd214d511c4dd4c7e7af088c2a40d94fcf3168ec692f22073733faa2409944c240cd74be15e4030e8dc35c70831746a08c1fb40dd9da33928e042b3846adbb809d731266672fa301a8db91c9bbfe69b318a5e04873d2dd015c25e5badb8b1ccc81cb25f2b938ed2a0d2bb4801c4240bb7d0f5b590346fef84b259617c4b4cf36231b70312b2412ef409d751d2d69b1bae1c8c3ad0075673ca702e06e9c3bbc65f3188b5f26f4746c7811b48edbaee7adc44d4c87e88e6c4e23c9195b29b90ad5a1a036109f7024f4f69e2368c5877d296f563508af79d96541506b68e2af9eda11396820113918c7ac631540afb8d592db2bb3c8084cb86df26eb72cc0a154dab73cff4e5b32dcb995d45ec36855336757473976e62abf01a8a3123f69c418ec01f8db74b5a15215e424ef48d9dd33861288166a12c432f3b6a1990af698d211189a8f737e3c271bfdf860831795f749337812cc96a4e6a5b532bedf254aef42690f8141ed70731207806469ff79c8add2e6d06e4be8e9f71bb969e325c9b9e56396ad2bc04aa994eb6d6b7af40cf9d2b4b047a8274216e431a98652a8a9add202dff4f8b28fd8b5df616ed54a608b8f84c8a36bbcb7140852bfc261e3f3aaf97e76074d06e3a1941bc90e8d005fbc70b13d6c6a245432704257e3c5aac9da8bd21bae74dbe1c2206ba9f02afc121c68141e4f88b72bcadb55539ad42627fb5d3d8e3606a3cd1ae140728178406b6430052e26c61d39f0860e2b304143a833dcc779b4832f4f141063e238154fcc64aba541037978f63e31f5462cd1a57a1ba58746b9279cb298b1d16461e3e2877db8805080498cc721f93dbf2c7b1ece2634e50557321ff84656be4911f1451740b98785f3bbbcb4071c1d21c67802ad33cef660dbbf4230560de3b818a8c745922cb854c5beaa0fc2637295a316e1f203888773e62a05265d0e9e8ab2a4e5c0dd83fda183e326b03dac1464069d245fccbff010280a156c1320bba07a7531a24dcbaca312eafea2c4048e385a024230492ae1331e06e4478ba92da158721147a33925029e7e3fbc187a7c37fb8a8e8979641ccc2c574954b7b82874e8bcf44952d4fee8944a1a392e751ef724b75d69285dfab99a1f26efe335da207bf83830d547ece70140b69782fdd6304d9610f1309421be6239ba649f5ff7eb5c3bb921bf4bcc3205988b46b623429477ad6687c6f37a095e0fa6c22f2afb90f2aee2fac6552e61993c0babae787f9ac0a60e0e6fb067f144e6f916d2c180d86785962930d473d2f864effebbbc30a403816e2baf7f2f926fb3b98c88b862a6dd76ade2ea064ba38df64f800597d6b0e8ebed8b9e2dea0febdf6024a61190ea0a7cb75143890f09747d3f6ddcd12740b1552c459a5498a7eac4dc2426c68ba459cc2699476047b1408144a05091b04d3182e34f12fd2e38e0319b98cd3d2ebddc35ae563ebf4f30bc0d7da0dabe404620c34b350975cfbad825121bf5eca7ac262508f8afc39b0d840ade5ac27ad79cb3ac15662865262d498d72c5445729ee5784a897b374d52550fab6d826a9bab65919f49954a68d95599027b2e04c502eb64792bb20c5cc5f1d8b90f0b54d17288a27b0acc7300b5e2c2584d7418458ee36a47be10c0e43f92b82a9d07767c4dbf506d6efbbef31a868420c48c77344b649292c890e0d313237295458a2e7608aa6cb5634a106273ddbbc9bea0a1168683164400128492d83d656128350e93eed756490d192059f842741fc6646b657b19c4e83a4eefacd5d56992ef1ff4439162bb444f0bd8046318ebf1190e41059c4bf0205a35f08f04747155cbcff82fdba84a74baf76f8c73aec58f8ef7d33b880e61cfab43eb4c5313c71d6b8fe083fb836fcfc1a925d138ddc792d83b60f2dbf4d0aa785f6849dee05e6285b6d43ac4c84d1c0cd182ad3a04dfff7e52843a09ce7c2dfa7c91f88c149a4db5ad0586a8f3ce5e35430ce9f8f5a29841c49bca0497aab0b705ca9d7ff38439b4ae67f2c6691683399aa91a7e2d08a3834bf7783a2106dd7c444e9b8ee3e685f51eb171a23c72c9137ad5f52659a41d7f7f5741df2c81b7e42a09c93db0d786d779d4cd2e6023ca89fc157232058de5e0afdd546e8edda5eea5e3cc9b722c0dd4b83f7ca58b1516dff25b94d97e3aa628482570b7a4d76d8330fe107acb2db6431db9835a47bd4ba66f758e2dfecfa01ca9d1a23bd8289450021cd51946a6a5604ed1ec0378df8ac5ffe21dd9d1c50db884687170024a829704fd902ed0e97e775ea12b0f06a0b0c30b3fab2e793c44cabe424e7c90c2879d224408e24a27f0466f26cb0026779c9d6c2379075fc844d41855abdb234ef65c612488ac9b3db0924c18dfc2169c1f304adb1936fc8b24f0ca3f9f87f7f658ec6fa6bd9b986fca6a163d064a5b3a0d355715a2ed6af612673f256b9afbc4358933a879fde029800551375fd7388a95179fd1bd16f3852224a5636951e34b216ac422c02996baa58198682dc7e84565dc176c712509ba26fa2ecdb4de95d1df4e8267de169258b313a9d483c1cf1d4180edac1b4e8f32e867148647a28c8512b11e68fcbb6149549b2a711945fdb6b938368066f5940d8f3491c40c802e4f7a95b706db7ada875312f11f0cd5ccc9a76d94d8fa46e2752fda97a681570fe49b98a34b78094dc96b5e89e232273b4e08b0a96819af440b66a93dd47c49263b9c171183e4783f5b6a124133805cf8950c1cfe7cb1df1de208c6008eed0183a2daa51e8a1246ceb2e62d58fca9a368574da9588df92c7b34b16b003cef8403a6361eea740372e20553d12137f1484f14b896ec6b00bfa714ca3a0d6d12a32adb77aeee848053a8f3df71e3e97c442cbd27bf44641aaa1349288a1a68758880898184fb92741a98dc8b1e21973befed054cf755e8fa4fbb90fec75a040d30c315f2164367db1aaebf41f9b7316445ce81f523818a497376a31b234bb68bd10dc1c913c1142f2726af28cc073e3a625dae80f4ab1c4708d4f323f276c4ca7a04709a54f073dd2d8dddc765f2ddd6dbfbbda21f43bd11fa8157db6eebbc10a48808d490e311ea4e710fa23b3e8d772ad28c112efe139d657d079bedb8e5826e6d8b63b2f49155669503c8da54b67c94b7671aa21ad9902fbfba820b116a933b2f15e85fd7449a16d97deb631641fecfa6f1f1fb24ec201be59eb201a5a7986fc571e9b7c1dd6a3ff0ced9740378afc9df25050502b0d77e38e602754442ae4026cb3bd7b5742af405aa31bc54eb7970774b2a592543d73463686646029cb2b6d635ec4367c113d2dac2d41ce8e359cbef6a02dbafd735b5e350b6cc685321322f7af96aecfc548a58ae3ee4c362d39b66c0383e210d89ac972d8842694d1b9367e5d4e318aa88910be21aef380a5a03e0558ddab373d656c170b71660c4cce144bfe8bdb03a4c7c0f2935d8f2c5cca6af01b2e4069bbe3a0fb24c0eb88435809006c26d5dc66f5e001756dde6868ec8c3a29c14ff752b6e7e09daa21c0e7a46b521cf3d1394ffb2b692ff2a6f84d8aabbe6f981435da23a2aa382d7e8ba25972035666b7ce689403812d74fb516695f8ac6e91ed77e820ab186acefbae0a61b7f6a3dcbff622c38030fb982d903407033b284ac8647665a89a697b65e905779dbaacda90d1e852a34b0bbdaffd253ff59e1ee162c8b8cf6625ac46770991f0f6cd6722d721e44057f76cdc6a25e042cd585d45edd39df67677cc7683327bec42289a97fbaab79a2f35cc5643fe0ef25f45447b7e58c1ae49d16cc65d7427a5e452eb8abe0526d8107311f30a322ddbd18c764356e9254096487629d7bdb14c5f0251696af7d0c4e1e6c32888508c42f249b0c3843c0cb65b12220809822a31c5fb79c1cd2f468046ba40b94ecbcd27354c23f9b242b4831c26cecfca4140563768e0a4fa23630169beaa1d4cbaa39faea1d80512df5d13d4e688517882b7c9dfac4e6aee9033b2d388109932842b0eb18fbdc3be1ac24b1590e77393d7e4c7d55c9cbd22fa445c08d0904ca62554f41939aab41520948a1129161cd9900d2ff37394a186fd214204da5d86eac93e4ee2fb9e4276af055546b6539bb316865b7a68ffdaa816b8e8c9174ba38c9a48a4c28b12ba3914e89f9d6fc325a5cced9a12614e91eec2e5af15ec6ffe5232f59fecd7063805f0900348018b071e9e96ca28eacef5ef5db2a2fba8f39170b0712c07d0060c6485e0055eebcbc8a466868ecf27e064e1cf4d54d9998aa8a6ef1d0bbd8fe00829ece539ce33730289aa92ca6aa153d54517973d783e2c6c3e27c9d7f28dd0ca82e8e87e92f5f359343116bd8242abcbb36c3a212a41356ce5e64c319af9c5fb8f72e7c86aa206837adc9f1288d8a2aba812d32bef5832c05cc5c62a4239c9db808c5c16991ea08002198f0f66ba2607cdc42f8dede0e1070fc7e9d5e3dba9673b16cf287227832c87c482118a6045dd7d381928d2e5f7851bb98c77c637a05054bcd86ea863089d52dcea54f98bbb64ceaca63e6ab0c0721e439b5f2c9d7a0c64e2a71462f00dbffddb887acb14ec384defbe31c6d09fe03cfb889cc1ffebc7da0fb1fd093682def2819077e53876070edfc1a109ad6d34d118278d68287843f06ef15cfcde61960ce7edce3bdc617794beb84856bd6f37c8f4d0ffc8eec886fe5245042ba090d43ec28eefc4c901722583094bd67f2b93db17254eb5f4f18afe19c65ee25d76d351e9ad47099b09a488a98d7be10e515c094c8293ff4e877216ee647e39d2412e062ecf988bd277a6642515c2daac2b8c317236465bbb90e901ad664336aca6ee64a2639dc0604bcc78ab8e294a06ec4dca7d30191c08a55102afa83bd6bf9856326387af342b546d6eb49f71a3b6e881172550c0f024d6a1c2db4fe8c0b7f3210d9a7986ae33f0f363bd42ff504357c70f9530a8e1223666a7f6ff5b550d667a9a86fbd6a814505742053e1aec4fe70e746401c3c01fce9253b92678cdb3b7b804886aea8c237d5c1a25db7792e7ac5daaa28a24079dc34af1d4dcc06399458be93dd04b58abbdba99d3f4bc403b152cd3e447c6d171d3aa6b02efdee0b8ea9cbf377cb34e25195aedb3c745f4ff28950915ff3c03567c858d94acc7a3dad639e4270a2748e15486aef19ea7b309f6b377f12818f9019d3636e77e670d65f02fb8bf2d750efdedce9d5f2e33cd2802a82f189f32dbe7202b6b44cbeb9a38a70bc54c4a9d43c0d72d3a2e9f6fd06d3f355397aac56879ba7c1f9f26037c1bdf8faf5c82ebed90cf61247e346d59dbe5fb1e1716172a509b6632a54a739a3d840c00b1a2997ab0e1a72e128d8f99466affcef55ab50ba97a3bf367c5591e7ddaccdd227ccc85b152b64c38e401a27c10e390432dcdb783f0e61292c01000259ccbdbceca085591bb943fa1dd2dab19c192e1b513237d45968f88d9a4e12c081ad34bf5bddf7072137413701215d1c73a30449bf42a4a81faf5dc4f9b9a14d64458d6cc33612b8dfd55c6233779a00cb39860341c85c100ba33b1333c50ee24e9cfcff033fc0c3fc3cff033c07326b2ade9d65a83b566659252528dbd58525ee515642a534a49a69454b4ccedfe6689030c80609490b91fd90b032d0313030a1e1072aa4f26ad3ab516ff1dd069ca17f39b2a46864ab1439f1b4ca3e6e831ec99b7d8ce4aed12b35779943a2056a77c55d375143a5cf6696ce91f7e08287340ca56655bb652b6dd7314395c52e7da33cf3b2db15b0f250e4aad74871dfdf6bc1f50e0b0ca1b1855cfac1f2ea29be3e3661dd45faa5722338a1bd0ca5f73ae2d636d40cd74d9b0ea3b36206dfdbceedfb841a3465903abc11ca3ee2eb2c95c67c634951cbdf5344f88c7fa424903fa94b710d7692a68406c2d7bc616cd19d01b364ca566db6a9b9d182866606fecbbaaf9ab6a19520f4f9e5973e9da26a58761ab750a19d052751619638d93e2fe3120e6f8fffd67a9d7b45a0c8897d24c97b7d276ee8701b5a46d9d4cf4a396d58101ddf996bb942673cbc3be8054e3b3e9b6fa5725748e46c50b689b52873b61afbaa53c51e902ea75b291f939a95c50b880922fbc4ceabc9fb6eb5b4077b9bcf5b56746e9191e82a205b4ed7e9dd6db3a93547da06401a5bb5f8e799ab24e4c295840e7287ea3cec7dce825e50ad7d01353c934753a142ba096562fdfa5eba8797ea50a68573be7a4651cd99975381a3f3a3c54020a159079c54c4ec7d9e95ab3c1fe02ca1434d541e567db8729c3d1985240da277d2a9e32cc88ac01250a68b937e66fa69d93fc2aa04001313eb8dc787ba13c01bd698dc9b865fb5e674d78c219509ce0c6b44ee6ecdcec51a5090a137209ab28015dc347c6dc52e12a76a32461cda1efe939797a124fe65e9ff3d5ce201ac61c8d8f8204028964a2020509c81cc6dd860fdb1150de39efef64a518218b904440893204c4dc68a337d574797216c2afbbb2df522908e8d3327ba81633eb1a77530102537e809ad13d6bdd9c5175f4283e40490da507881d4e47e95aa7151ea0640728794507e8f398a4769951f5a9949203e46ad038f6ed6955ac70e0dcc6fbe426e719d3dc4ed3f1b986aa05ca0dd09ae5fde6bb2d7f73a8d8003153aad4ec3a0af9a8aa01ca746d8d8ad170f3e5bc088506a8f5af9d5725d5b439cd002d63966afc67b399451f1419a033ab0d3fbbdbe4867d0c102a9b5f97541518a0645263ba2ef79835642c9f8d672371b9008106b8d8b3d4009dc6d232f6d334353be7689c181a0b0dd0733a9d6f527f0668d5b33f865129969a593240b9065325f62a47b0c400f9edb359d6cbd2eff2020394a869f01cef647a8629af38aa9ce75af18cf776e7b27abbee543627d915a8b9633fcb74f502a51568d726d37cbd155b3ca8b00225ab40cda8d334b1496f56d11555345ffcebf4d454044a2ad032a34ed9a0366cac534105eae4f8ca926ffc489af1223366a4532037c7cd366d979922dfd13a445e0c554c81525d9e658d756afc2222bf31923c4440dae18f24313a444236368e514a811252a0566f12f7b0528cd3218f4119056a5ecc308e4683b73186c245564481d2ebaff229d396765e8731a3a37d80789118f285443243d5f8c0088d1a60782149911703243712431a6316645042814cc367a5877e9fffad8002e57a569af2db398aad523e81f8785aa9316cd7bc9e7902b9e3b23ccd6dae13c8d9e4f14faac9d138a9d1f1f14a225138b19bcc8db8a9aaebeaebde06b7959daf0e55c30b89a742d90462a6cadd939d56777d3481389de2a69add622abd9209b4f2fc7a6f8ec9b1752a9840abfdb97ad75dad19a3333e3a68748cd490482e817a3d5fe3683448a363a486b1046226a556eab257a9043aaded27b3b9dfb956e6814209f4eb8c1b76f69cc753469904fbd4b6dfdbb92b9240b81c9179155b2dfbcd4502f51ecddcd4abe5aae31010916e0512a89d77a9938dfccadbae0f1580b05de511a84fbad4abfd5af8aa9ba3315371046a8ba74a313c8da834029d64d8dbda75da244436b2fe040ae4f01446a0c68c52b5af58a3ca14288b40cba454e5a82a313e7b38fa1545f49d3de7f4962f99b195f1203ffbf3d437ed381a8f6e22d0bf261a648b59410462ae9499d39c2e91882c8772089498f1b47ece4abb142a36f20197288640ccc88d53f1acbb5a850211902f944220938fcc9cbe844650088172db39a316ae73346c9ba00c0271fa4aeb241501451048d97473d6ec771dc70f046ad58497be4619fb740081d45b5a9f967557b7f53fa0e7fb54c50fda07e543ee01a9b4feb625ea52cf46e9e15c9e2f62b64974819207945aa634ee69d51de39115fcc762e00303223f82819f21f260688afc48470902791cef0862b903c23fdab9b9edba10fd02b1d801a9598937a93c8b98a840686c84ace0fdf0014b1de8907340de7f8c629bb4a13b4e3a40449e04920d8f1a1f72406db5d5c40cb977b0c4c153cf28df2d7040cf30e12d3775db94990e9637205eccf73abd49eb9e19372065f6ceb99933b74db6b401e9356754691d64deaad880566d5e4296ccc15c4b334170903db0ac011d734d4feacbc6cb771735a044124b1ad01d3b5ee7a91734a066123f733193dc79f50c2817af648827f93b31ec132c66406e5e3d6b34ac35e6ec322064ec9ce32fff536b73210342c379de51d999f3751c8d1b61f48c8f0e918e14d21158c680f417cb644aad2676ffabc15d2f81450ce816bfd9f02f1506b44e17b1a9363d8a312d60406e9af9635a32664d63b97c0165af3d5526ad4376b62d5e80c4d205749e4f19b7d556f7a7b98054aa75fcf7a6735be3fc650b089769c6bc97f672716b019d37db4e5e6b5b461b2e5940cb963abe3a7fd8f7060be8cef2f5774deef5c6e50aaed6f1518919331b938e4c2b68aad6ff5dd63ab12a30630919df1bf3dbd65241b5d6da9d3ccbae695aa6d096b3314cb137191e132c52682c31cf6967dac951c0e4dd65c5d3e6e46973b9ca9cb125b38c031728a03bc6b7fdfe2b7b6edabd3c01b5d935ecc76953e72f27a09458d761b5fc8e51d7bb4b1310bb55e62ca3ebf66e1513d0620755f11bba2666b72c01ad36ea7359ee3279f74a407be7b2b16ba9ad7626e29204a48a2964e95ab56d0a170928a9517b2c3b0d7233b31c0135b58ad99f9e693abec50848adf6871d5f9752cbb31401f5613f9734952e4440c7a0f276abfae0b7c410fcc8181b3f328692485830992042611902f2bc4d79ada5d4a37a090161d3fbc5afffdc2c6d10d0716534a7f11720a0d44bcb8df6f901525cabb8c79561c60f171fa0dc3c93383fb1673fb6f40019575cd7dacd99558e97602c3c40674cca4ede18af6bc852587680982ba7c35ed32d30a1418130d2021249ce9048122734542e3a58d7f5f36664c3ae58728058a6f427f11c533a931f1949fa81050768d9326b99b662b9dc00a95ddb4cb5f9c6def44f60b101629ca88ca95101232bf82e3e66744c0ad0804917bf4503b2987cdbd8c2013218a0d9855f179a970a5880024e922dc104ac91490d1101603186eac200324626353c10002c7e46aa31e323d128401613915f35188848881660c8c042860c195838608b074c3a429201b068828cc944490290451a494c22002cd2486220221202c8000002b410f90d14ac3432d9d8d84206160720c0c84f5ea11b05585c11e2d10aed1811e198d0d8d8d062610501965568c7080d8e49dad8d06251c5009654a0a69b6d179ba6702d7654a0a35e9121ebdcd4a7e614e878f2766dbc2d4cc7e33dda14a8f5b24c46adeeb21c236479bc077bbc4729b463448c0e460aed1861201e353636b458468192b7efaae9e41ced8935123758c7fa90c9c1918f0e9d7c748c883c8d8e100f90d5b801126c74601105ba6777f6cc52ad97d2335e84a1012ebef000178ba1b83bee64ccc3c6e0f2f13c8995571ef7f623289062edb8594e7c50d3ff2750e7c2969be7be7802e9fe77e2fad5123b639d400ccd39bddd8d6deb36270ebb2b6a2a1b55d5f58919a7ce7fd64da0d4fa515f9d938733294d206756ecca999315b60e8ec99984806cdc9209b4b4f51837b17f9f1b79c10452acd6c9579cbef2fb12285da6eaf56b837da49084c16209f48ccfe5f9b689a371c3e0c75209748d61b766b34e8b5d520299d546bd4f6abbaebf2789c6e124103adb346b4dfa1095044a7e67dd7df3f81b3c9140cd2c199f3eeadc7196221c90c10209e4a6b7f5f3231a76658f407a9a655f36bfe275ca1128fd25c592b3dcd58c37d308a452a7e48ae8c7e81dc8c288337fdcaeafa9dc8e6511889dc39a523ba810cf5204628a59f363747922107bdd57de5655de928b802c8840c997ba767b67ebd49ae510b788ad8ca6ef1c573e348d09f59ebd9549864029d9f9d834c367b1691c1f894618cf4036e0cf403e8c2e85406757e559469652b93921b633f398cc3b77bc8fe9a8f90b590681162b1f5595988d8c41814510a8f3cdc9d55a2d6c3cb3040229d3d8cb9a7ab35e8cb30002215bccdb1fd3da619a82cff52140d4c2f207a45637a77ebd849311913432d991c50fe85d91e569e76c9da6fb01e2e101b274031289416491a50fa975ba65745bc2357c586cbdd63ba97bf4d4f16c1a5dbcdaf2faee0169ca5d7f738c6558f480702fe5b56fa6973ca0d312d36adb549d5da6173ca0bc564c5db20e8395a8a2631053c618430c310491692b1d42091284213988c2381204516da907124070490c45298e62186314418610420831841042083164448486a63cdeb7533957926c5bbbe93b1f0e4ef73f479d9c8d8e7eb567d68e7d8ef1ba995b55511d117cec206756233d918e900671295c8ea9a04338bde6c4194d7944d2111f0c23c57e0b7d19c80d4d8655b7255eb720730ab0ace890305f5a3138b9233cd04ddbe27e826dad9978b18983a4123ced1b5d1ba40cdfbc817ba6016573d31e9dca81ce1437795752da46b009e44bf7aca4a68efc8434dae4d745072af5dd02584cd4d73da2f953b1e553023fab4df98b994d521bf4af51c94ec628e0203a9b51d7e8410072583264ee4c902b50b6400e0b600a6496881a59e037000e76629313a353dd0c215b2b14d61e90f0544ebfe71c4dccaf45599a3699094e263b8434434e052db75e1ac501958195257aef7e89e0aa83270eba96efb5945a306da409cb58b9c9e54f848d16164f304ca232969b074365739715c265fdf72f67a6639942f81dcd3493796d862ab800077ea1e0d56c9012e268ffee61e58c7dfb65750cad280326659a3c51a6fb70f25a78c83711a127ec991d52ca88018ad6bd4014a0a3873e9c21e277b0e212df4d935f98bc85f3edb26ba3f316f5974c3ac5d3253276037566a8abed9041b6f5ea024280e133a4b3f3b568e974e01b3505c1ed417574920296d7e6d04ab3601072c4b918d823cd3e85ec58e7107f62f551712d78d6e74835975276da04ef190a18a48e04574108ca7a80c14c7ae4c2e0f7fb29d2d4d20a2e112dc8f04a68f980073421ad88642873b1f072caa8334f027d237fc04ba732bfd36a5cf93b59580c9671dc6643cdfbf0f3e81bb07ca54b73cf5623efc7b76a2ec4c74a887c4bdb5acfd4ad71ece7822089bcff2eaada96e0cd92f97b78fa3f6e1c1bf7b4f91ea20bcfaf339a900de501490b19b69ffe2b2fd138ad0105e28e220fd16044b0bfedd5855ecd3ef8c868afdd78a8e0ac9c0e551aea77e630da6e067a3ef68ce0b16c97aac4759861c83e48dea8ddfb4ba62cd50125bd79a51b115ae190ecf235b937c44bd13ec929a1d91f14d22a0b84ae15e7bdd2329dfac948a46a6743e93dae94737cc828a7ebe0352486ed73773ea2281a68aa90b3a60d65e0ffe9ca04b7b2397b17766c96cf61ac2e594eb8da59893a9dcc0930b73ec97ad48c2c16541ae39efad85e3deb71c9ef249ed8bbe3e8aad13f35228c2d33787122d5e4f239d1db557f563f982f2c185f42981e557413ca311e307357882958957ba47c866b0b562ee929a41ce0a90f809da73b77f9636f85ef2e871a41dbdc275199737f020009c1ac2f15a92930efe76f58b3d6375770c3064d0584482130b513b4a4239a88d557473dfc659726a4e1321a2a8d53c43402c838c7d669bae1648cf94ce74a5a95d3d8a4626407525a957609912d36466007e349c85cea5e32f6fa48ab1275d95ef6ec0bf4a99056a56663aceae1f0992616234d1997927f4bc6c586cae31600a1fff8fd003a734b49e111aa716487e09770b53b6c709f0d75cfd93443c7ac3239867137d76f36c0d28d2c6820383acc8b2580bcd9517c908e1d80e0ee68e4b8d265481f4b19928cef2093118340ade328c32a03f737d342048bc8bc0808a0f4ee832d9e73d74db9fef572e45a54fe5f30b3f8fbbed8e969b4ee117b06bf37f5302e1ec018e9ca97c650163bc21d3b823d2ec5173b6f85d07033d1518c55b0c4375ab0e130f4d3736231535e03e23e4a491fbcb13e3a6008ede2f699f70c9e67fb3a43e011f27239015fc71a4720b89ec3fbaa98b9d4b2afca2130fa562dc2fc704adfd3b45838c7acf6c19edaa65056560b27f9655e1fe6fb421145a975383d7e97e98a19149b52e05b89f4f8e6a0b7c722e7f08b746e2bf6a3e4ba27739b0acee8b43f77c4e37d9e61a04480811a1edbf9d2035064864f1a4a760d4af61befaea69d044e46e5d5e588a826726b945608530482715546cfce2ac49fa3a967c53d8df697128a774b738c8a605808d74c222070f0404a7815b2c071101d05fc06737cdfe09a8146a55466856098b476d9c838daf06582768f4945da7a5fd1c15d72173cf35a1a64eb7718a7ab19b286621bc6ed9fd683fe0b7d962550f58b16a9ec23647c38fb003351d610ea0ae50223b0b7c6471df2127cc85241cb6f372be84e992212c9e238b05094716e9a8acdee410b92a89fdee4c7ac27d619077e80dbc28768afa0503eb422bd8631a365fe00077a3c4743f4417dca7b1e968c5786a6d8698b8a1f46d4c47a4ca57fc50f0b405cac837a05d49380c3d37e8bd2b934c90f54c60190dd2a495b61160a4c67f74f2d536c4ef3962fca1e6d44280321a22e5ca845b85ed466fdefa5af12fe5960b9875d4e85f4ff86f8f1e1ce5946e72167fc16dc0ed326e55281f6b52e9ee121ffe3a5f3c5b82ea30d4c0d517a4181410e6b182d303603515f65d648783aad9fca35d33b1b568a281f63f47758841f128eb07f9217e9b59b4daffbc0da5a02019318bdcb9668c3be0d0c94625713c104fbc69bd3aaabe6b385151c9f6a9f9c62a9f225e1547c25b62be8ce887b040a01d246ae20e0399c20f0a2cbab01c5355c60355bd40981851e35672ce913986481a9af60f1aa94b2b40d32c937c9aac35fef0db4bcb29db47bdb4eedb2f85fe2b42c981e1a668936818dab5efd6a6660cbac8c9aad0826fc003bb54caff9988d83a58aebbc340dfd5f82496c0c6d855dca28c1a1a59607f78d18b1d33917670aa5d046e1ef96ea5a21864a50b865641356ae279ff6afd1b86450446e04702c92a065a892716837dccbbb10ebaf0ea3fe4b61316d12ab42f26e6da7156e293864619f0f5fa6481d43d4682e32ed35d8b4630fcf86ccb876eeab1c50aa9e6578944f6174707d44d312dddcfbbaac7fc3930eaa5ca9a68836d7fbecf43e1010e393fc7d6968cbe00a752f3a6149b109f686b0f4fdd77287828c11ca2b569cc0f987870c9d7c171bb419f834f76dd2dbdfb7ecd664e4c49f9a3b4f1a899d469193d4f143fc3a87ad121c359b56d8f68485b7eab8144cb5f9b0304d503de2b4aa3aed67a1b38c69f31a12ffe6b6e336417c04078221cb987710b59b840c2fd3e24190faeebc4b699502d3f30736641c6ba2c4463b0429c8e6c14237ef09be8632ac6e107c8d32ba77d2a36e8dd092e3b6b95def7afb685eb883e64c4ee161b8df94ea401ca2659b9b6e78a405054204387ab9003d4c6eee689696de401a4156b13beb2f40a09fd00dfc00a9a9881cff23c538ed6b35986e640095e1e016a2d5668a160b81c1e0c491a5f3bd965a421de49a01d96ada05431279ee87f50aef51c29c7304cfac46f148cbff7c561fceafb266041ad37a4f05e4922edbbb36b8b6903b2b4bb9fb99981ac7dcb7d439f125f5393d5f95ebe520197d35394515760f881523ae282fa768c7a285d36a17917f434d2516ac1cb214a533561f8e6f8125035dc9b42da37b09237cf6d18e30d752853b8ae08063672835d4ca9d9c54ca2ebbf565b64bae05c10282f29405148d110ac05c57411e436aa3f973eed13b14cb1b141f293815a89964a54cd32bfd7632b7c877fa958dfb117bba281cd868d10a0177a3e6b8e6bbe89940dc09d7d53ed918eedc91b80e70feec91fdbabc845b1a946a8f5c19429a804964da7e2fd4535a3539feb4dd39d73686785fba7a4cb55bc7e08e0676b0a81f916e7e01435dfc6c58a40d3e0f889ed3ceb6ec2fe17233502b3b278a32235c4473210a75b1f3342b2501e499084c9991ac3c5425ed53823f2777cd2ee18d74d8344129f4166beb363e07c535adba78bfe906114f4f10be9464af255a1508a999796c2257899ffa84e5c8c41e6260a4ea5e66cf49508dd75c8102fa24002a216e28c887733a851657126b4bcf05585fccab38bb761fa957830294b94884797a01dafbeb47d00cb1ec78a8a42dae2ae8292d10c24de715c471bc9141ab1d9a9d102c4f59a3085130e1592ecd29f7adc702cf02cbd250d0c893", - "0x3a65787472696e7369635f696e646578": "0x00000000", - "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", - "0x466660cf7b14b9faff091c218b7a25c74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x52bec856e0fcfd14e28957c3151da0574e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x588177cf191e890d83b10e9653cfd663c8a382d5afce8518636dd9565c22a631", - "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0300", - "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00c00e109fbeca353600000000000000", - "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0200", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb325863a892a3ae69770f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c": "0x70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb338e51eb38881e3ae18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43": "0x18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e8eacae64ab191ec7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121": "0x7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121", - "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195010b0aa882841dd68617572618018eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43": "0x18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509b075dcd5e9bcdc0617572618070f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c": "0x70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950fd2b9eba034c493c61757261807071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121": "0x7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121", - "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c4318eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e01217071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", - "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", - "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000" - }, - "childrenDefault": {} - } - } -} \ No newline at end of file diff --git a/parachain/chainspec/gargantuan.json b/parachain/chainspec/gargantuan.json index 3b7dc5a11..fb1b42fef 100644 --- a/parachain/chainspec/gargantuan.json +++ b/parachain/chainspec/gargantuan.json @@ -2,7 +2,8 @@ "name": "Hyperbridge (Gargantuan)", "id": "gargantuan", "chainType": "Live", - "bootNodes": [], + "bootNodes": [ + ], "telemetryEndpoints": null, "protocolId": "gargantuan", "properties": { @@ -14,78 +15,59 @@ "para_id": 4296, "codeSubstitutes": {}, "genesis": { - "runtime": { - "system": { - "code": "0x52bc537646db8e0528b52ffd00580c64047efdc418105420580e710ef5c0df58674a9bdec1c8ff7b0a0466adfa1c7127ca5ff6de3ea7b0457227321a6f306c95e023f338c2ebc838fc05fd4df9cf7dfc028268fc1b8dd3c07ec80d5551d5e2bffddf5eb2b2bbbbb74c49ca8012260f440f229d21493d331034e4fafd34d4803dcf5c3fa66b68c378e6fa60fba0b99f8636d433d78fe9988ee998a624ed17fa34d5d88731d3169bae3d854bfbd2bef569fb28e9574cfb853e7d9b5a4b97c24e57a6dca5abb13e7deab9a451ab00042d3e97990b1c2e68b894e1320397285c705c9e70a1818b0c5ca47071c2050a971eb804c1650a172a5cac7061c225c765072e55b8f8c0450b971fb8c4c0e5e59285cb162e4db8e4c005072e357071b9dcc0c5065507f3a007157a54a1c71a3d9ed0038d1e69f4e8e901851eb21e53e821851e6af488428f580f327a30a187117a70d1a3083d8ed0a3093d603dc6e8f1440f1df4c049cd525a484921f546aa0a292ba46a90ca416a8d141ba936525768a942aa2785462a8d162aa49ed08246cb15525ea49490faa20506a919a47490e249e1a49690d249e120b5839415a9295267a4a248999182428a8a544eaa8a540f526aa48c48dd20d5454a8a940c524c523729225adc48c95ade48bd5ad2683942cb0b5a9668b1a2e58994ab45899628b434d1c28316275249686122f5440a8a140d52364839d182d3d2d352450a06a921a48290522275b674d1024ba9208584d4115264a460a9325a745a9c9002420b1529395270b41821e5825413292e52466849426a891413a956cb152d3d48c5006381b3e81fe01e6028701518073807d807f80abc058e0227818fc04c3011d808b20ba41da417483848299064906690539047207d402281849157904920cf20b920bb20b7207f406a4166418e416241e6802c83e401a943f680b4825c02190629049629587040f2903a205fe4105872c0d20252092c5a609983e50e961e963458d0603983c50c963258c860198325c6d2041626b02c81850d963558a4c01205163958e2e091021e274b139630b08081050e963758dc60f1618102cb13589cc0a206cb1758bcc06207cb0f4b1758ea60e1020b1d2c5b60e16141024b172c4560e182450b961fb064c142041623b01c81050b962a5876c042058b152c3560d101cb0dcb0a56b2b072c78a1d2bb3953356d25869c24a6c458c152f56ba58c162c589159c9527568c582942050e953654b4a0e2a322870a1654aaa042051529a8b8a11205952ba8bca112878a1554c450c9824a182a4b5099820a1b2a6ba8c854662a4e50b181ca0d545ca0d2021516a8ac00e5051523504d505640b58192a1a6800a0355059414506aa0a280f20245068a062817ea0b14135060a060a823a096809a02b5832a020a082828a0c4e8172a08a826a08480e2410d0125056a0b540d504e40290125039419a831503e4025018506ea06a8184a0bd40c5067a06c80e20295062a8b9429a44821250aa215c437c4364a4b987e20d2498989749072460a1a2969ace0a044462906241a27364e6b9c627062e2440594224e65a0a4e06406ca798aa14081e2c4690a28324099018a142834408902a50628ae934cc58e51eb84052a5f381143858c1325786078628c50a0ac3146e18571a5705a8234c6188394304021a8fc98a680fa81bf61a242680d540fea0a141637099531c020745eb81ba8394255f0b8406d418c82890dd310585860da3101a1f446098e921ba5384a3e252c94e43005615442b4423445a949090ca52f986890fa01480b5b460f248448108551c239f9a24483900961134a51a4c84a520820002519ace8a0c5e8304c7590cc30b9412ae36405230c4c4d4864987ec616193b51c1b804a90d938fe90b2630dc159064a6374c5a2041e1e408121a5e132a3c189b50e901a965f2416905a51b10139d1c271e510d6e0b5298309a9d2481c48293304847f88bd402d28d080762152b5a3813d107df16ee0b6c1c1809ab05bb8665e3e466b40382a261232cc22c58a220ad40b6415e21f4414989524b5405a809d10e4a2d486982880a5316610fc2170807f4842827858c93284ea4105d21ea818807a297e8062b44b0b820650c2f09e20c48364c5630b561eac265e22e115e119211c640320871c218844d844e80a2407902050724852905a6133403b1471c432c0364039411ae0e6a0d11cfc90a2e44b843b842b83b778b1b840b841323ba26a4175824bc2390e820cdb1d282eb84c80a9316a524422c4a4c945e5042a2e404c889508b70074584f007e116275e98e43821a2e4822b832bc5a5c19dc1e5b9aefbc5adc16d62058c3b450906273c3706251c94aa28d5a0e483520e4aae5216a59cd20e4a5a94b0280da1042b6d51ea41e9082524948c50fa41e98a92104a5494a628dda0a4532a4269a7c4454907251e9488507a9582500242c90a918fa80cd113443da22b88a420ca82080b2239446e88e010c5217a43a48648264243b486c809222a88cc105541d486280a22364450087d216447c80ba11f511aa22988ce10cd44561893185f3022717570777002c6cd4951e152e13286a823befc0a0fc3c1f02f9cc795e05e7812bc0be7c291e04770981bc18be044f021b8103c080e04dff12dfc07ae85a90aa425c4274837f0ba50e9028a0aa62d6017f4c8812807a9586a8b2f08242e90b640aae39404120e484e905f7c3b1029a1a5092c57a0d8f090e041d161a1d34237871fd1d9c1b245b7856f06b70acf065e0d3c97d771cfb83d378d9314884130cd61a2c37487e90a262c7471746f885a883fb8689045b865a044c114070b1058763a31c41e98c2808b405103c50a2c5298e0f038484a9060c00285b845a989d411a82f9cc8a00383f4e2a48448c5a906a49f9322441adc145832be9d93151d1ca33a4646189f5001c355c1880b27e76501ea095d18dd154cb312938ea7eba2f309d521a201b8634283c5255e7172adec9c74be1f90ec38f1c0c405b106a4113a258cdc1871506fb48cd1b9210241dc6929239585942c4c6984d4486161cac25703d31a274c2c182322785ea0dc3079c1baa04bc26523b5b3f273c91815c17446f78588859845cb19a26b658b963044218c4ca48890caa26444ca072d5e883c18bd31ea42a80b2b5fa452401261a50b2a7780406801e39a912a420b12569aa0ba30f5a0ca4869e1f1784a4091428b0f526e785fb47061b28304c324f3986879a592189d08412135460b162a5e6899a374048b0f4e36f8b6b07258376c15ec142c15c81d2bb359b04eb04fb068581efb85ed59c9817bc1c14082c1def81df608cb02bb02cbc4bfb0c203a7c3b7e07338175c0b2b5b58e981b7b142c7ca1ceee372f81556aef027b815fd05afc2d5f01e4fc3cb702738194e854fe139be03d7f1285694e0307026567ee03e58f1812be12d70167812be027781cb151c0a2bafbea3c1d03f6ec44a18be445da3b2d15ce82f1a8c953a56b8805bd05c3412ba8bbee9239a491bd145d49f1a855a852a85d6a2ca1a8bd6419d42ada372a162a1ca51dfa86af414bd4545a39e51cda84aa8655432ea1835569950c5a8615430ea17d58b1a840a842a84ba45dda947a8b46a4ea5a2eaa0e6a05651add861a6ee0089597fb0c4a6a64e0144088a9060080f3a04119203082200814c03eb01ed451f21060310e00003a859f09ab835d32a62c26b9f5122840440900899e90108905b5d07233340807c31c4803a620890233b18b1a148912310a0193253a4c811080800c5842ae1c108111d8ccc206941194195a8a08aa04a502822669418398244c90b4a0554c9cc10223a182142a364260820669200a199e10100a814d01e80cc1821d2c310213f00a151324323a4480050279df981080d922052294de80c8d101a25409404a1c4c88c12234786b0a484812aa1313223c40c10540a18e80e478e24417207d5e1089124334880ccf06064a64811213d0ce191f2054a73e4489293142f50203b0899290224c90f3f00218224df80e4072536aca4d80193f253a4889021458a08814017a892224484d0cce8006466660700a4d4d1030d0f46668a7081ceccec00e4080d109a1e7820325302550a1df4c80e46880031419299208000420449a63132f3830984fcf8942dd09921468e18012204081124796688912319a03112c4912143f24aca1c3a1c1932a3038f142d50252698f1018812252698b180912344942899e1009932a3252082242799f121c9110f68804cc902053213c44c101b90d16186071e84c80820050b54890f43667a1822075542636426c94c0f46887c4a1c4688ec804a81830e21a2c40891243443cc00314111244884004932a343112241b07c1872a4084b25e50daa2425c50d2a04884a8a0ffd0188101a213a08a199c14028e50a2720524408111a2246889452da0862c8cc1033a2142bd024472000a6b04195cc10a12192e4480f34410819a5ac418d1c2142232449103a00a139524409102248b2902441e4529806f561c89122240032432344072245840821d23a53840442688c04b103109a21334988e860840810202802b842e0c0586296a9cda5b0f3ccd5c65136cf09cf898aa472b663e906d00da0ebbaae63616169ea2eb6b7534ba96bad695ad5a8d6dd3f4aa5ba6d1dbb7ab55e9bf3d6b41ea5aaa6690e00addb354d73b79a6b96b49556aad1764ab55a29ad5a6bd469a594524aa9a659ad9b6a9c534a35cb51add6f6ae605beb6db9da96ba539072de9a53cb518dd2d63a4d73e7ae2d719c085aaa81406d5b7417bb6b8fa3d6bb37dbdb75d736aebda96dcdddb6c5c005a8bb0db9a6ce69ceb589d3a86beded9452d7685b517bb7f5ceba73ee946ad47bd4946a6dadf56ecb51cdd2d66853d76ca5d4bb6d5b4deb6e6a3f6f4dd3b4b09bb61d2d57b52a72d4decab553da54d36a777b3bedee3a6b9db3b641d082a0b57d6fb7b5ee9cbbe55450505050ec2dd95bd234d728c7518ea394f6bd20d84d43dab42fa5b6c74b45ddd2200aa2eeedd652da1e76536fb79abb37d7ee943aa77dc78d16e82cd761da59e7045081d005405bbb2e5a77aeb96eb1dee201f000b86dd7dc35ef16b5e69aa68521a5edae6d947688524a3d1b6853a7b4520a4c20a4946add4d29a59c7b48a9ad6dbdad6d5b29ddb4a61b75eb5e693775da755d67a9a5d6ada59d5bb7b629ed6cb7a55c538e132d78b25c5befda9b735b6a914824eabe9ab7a6b9bb3bd53a6c4aa904dc5aaebb1bc55a77eab5476c4d61d85c736dbb76addb5db3deeeeed76a5a7bd7b97356d3b40ebb3ba4b6bd29e72f206d0bbab5965a6b416aad05dba905bb41afc1452d0294da5a2bfd346b3bb79c75b7dd2e6a4e398ee3bab5118640776b9de5ba6d53eea4396a693bf53bb228c7dddbada2c2699cd6de9aa69d4e5a486953ef766a552677afd629d5c2d014d6a6b49d0b518d768356e88a37281402d515505dd13a34a9426b0a47dd3d6a4d36edb4a64dfdc582deb5d64a29a54ddba916e6510380eb6eafdeddb4d6ee6eb76dad6d8fe3b8cb711cc7596b39aeabb46ad45aaa6994769d46dddd9b52ebee314eb6b737a51af56e4b6d37a595d2b6cd719c77534a69731614397b4bdcb66dee5cb7e6f6daa6546b4b354deb2c673b4a3b4aa97bd3b6ee96524a29775b8576f749a396e3a8a6755377aab96b56b3d6baedbc6ab5d3aabbdbb6ee6dbb2fd79652aa756b3468284d7b80e6008e7377b79ad6719da6f5d8001706a0468b9a610132b8a06cad55ebba51734deb34ed579a8fda79edead8719a06ba7b6beeb5baa635a8eda6b9b5f65ab7d6a9e6dd6da973ed5dd3d6da72ae35a554d334adbb9b76b77bb7b7d65dbb76735abbd7dadd69b46a5ab7d3f6da9aa651aa51cddb6a54ab1aeddadddd5dabb746b54eeb34dadededdbdb5ed346f8dd25adb250d1727f888b609921cf961e6872d013e56863852021a161f0a38c0930e6000200798f14148111200e1c108911e9408e9e115a0c34c92191d921c01e243010700e283902143667c00324324094d00105084880f30090000025e0ef092328014001820003030273a1421d2c3102034458210b2c38c0eaa1f6246072344f0015a0a402324c98c91191e92f4f8e801669428994982e487234380249911e2c8909924408428d161c80c1003f8e043010740326446c9aac70e4492cc08016401065080020ea0c30c902334484c9064488b576446889922408610f2038f110f478a00e1c108111e78385204a5fe40a9016692ec0024c9111a21438e14292244850045840c395204881122427a18b2c30e475e667e2042632488243824c30e3441ac848698e901088d112249900019a284c80f4984d02801c0891214113dc0912349527c18228404458a1c4912801884cc244931c18c0f4068869831c2c2836686071492121a2544869851d9a8018c1c214233448812234292d000a121f2c30f475e52e8018620b2c34c1224389444c84c912128218cccf8093500102347882849322304101d8ccc20412204c80c0f42921ca151028466881915218ccc7800422134b5e982509bfad120d4660b42b5254b96d8d8588e2e59b2c497d8744bc22036d5a6da9c04b1a936a3203615b46489f5f62576c992251ec466894d0759526d962c714b97d8d8d8586d894d5d62e3416cea92254b96d8d4254b6cb420d4e606b1a94bea1784da7841e892251d84dad86c365d109bbaa47a90254b6a109bba64c9121b1b6bc305a1365b9025d5c66db4209406b1a936366d6383501b1b1b0f426d6c3a884db5a941a84d0d9201ed0695525a7f50228406aff9f8310d6e9a6f9a6b8e9f4965791fc9a4b2c2bf21f823787fbbdf6bdf0591db8ba4f6b7825df79d83f6169bf6358cda5e13475cb38dbfbd8e8cf82011d78c582787f1c76e7bed73581c7903edfe3ac87d4c83c8ed6d4c8bda0db7c16e5cc40d0dac7e2695f5e2aa900449195c55ff921f490157d577f287abea7be4075c55bf2339ba75dae86fc9226e6860393eb9aa3e93caea25a0fa61e1d2a7a9f26bf9caebba713c9dde0323f8d457a22f6540f994077d497bc4a7bf8111852c7d0a49fa6b223730f67b60ecdfc0587a2f1c46193ad8c8738abdac5ff4dc4eedb4a79f5dbe54f1413a32276f3ac13524acf3a3e6e447cda7e01a13d6c9c1f4226c8ade84c5d2933ef4a0bf161491570351c8f086ab812864e953c86b491fd32632bce13a0822c31c401fd3158453025043d82461b38449a6eac5eaaaad7a22a97ff227d80c521f05059b3fb069727955d775adfaa68ed4a5742902487979977fe9b3ceeaa7a460b392aa099b354622c1441c270bc94c33d32cc5d4b23699b0d9b1ae1fe25c55c426470255517d92a87e285445f5c517b1b98566e2836a7d91089bd625d60701e051eab7d427e11857d5fa260ce3aa5a3f05bfab6a7d01e0d155b57e00307555ed5eb473752dd097b53b412a6c56176c9a2e0fc2ed7255adff82cd6e61fc2e1cacc7e3ef71f68c6bf9b6c9ec6b7bf148bdcaa7eab760536ba160b3be00d0f23c6635fcb2c26a6cacb2599dc9ce58b7602ed8ec17c68f82bde5aa5abf0736fdc4dc493ecb932edb2c6c73a9fc8ffff553d8d45cddac85c7abc8dad5af867dc7b857cb3857cfb8d6c99d189bee626179146c4f57d5fa6465696db095151b2b3d6a6b9d7cfdc7a6f6ea78e031e62d77f90bc661338fc95c16f3198c059bb6351a3d0ab63357d5fa2bb53eaa843a61531bb1d9a960f3b42d9789c25ea4172ce48a895a56263e65a55494513551b0563ac1e669c41dcc55f551588bd959a99d2dd397f5e5227dd9cd5ea12f3b98a8a2d4940e962b4a096b3357d50a7aca4aa97fbf1b5fa58c1ee547a5d29f7cca8f235c531a8d7ffa933f8db86684757218fdf8a51fb1398ebebe271bbf8e46a5d2c9494a0a0acac734f5150a3675644e8ff2264c5dc57d8f9f824d1acbf451b049c2d455dd9ff0c9ebc8989ef47e42c266085357796fc2a5d0dbd286a9abbe27e1d79efa6a7445d8143175d5fd10d691117fc426088b18c655f4b7a7212e694fa6df2f3382d893f9eae20d97dd87cb31d3d770397ad8e2f2d461c7320ec38e70e598f65ebef264aeaabf7dd995a3fdf2f47ae5fad793f98a72e185264d9a3499e30c1eb0ea7b3ebe3a00149a8481076fd0a0091858f5bd99af383a92f8020a277410043758f5bd1f5f153163086de03c01461d5e60d5ff4e5f1d61c10d6ef0c611659c6eb0ea7f37beb24658c1c61a5db8a30826aceac93c99af628e88b98af66cb8ec641a2e471f8bcbd3cc71f9be8a39e2c755343796e5fa3455b10f5f2bd396252c366123200697fea53d319e4c9f5258a64fa9ab340d973264fa303025cc7df19559f3e22afaffa7d3386ec08655a323d35fbf735cd3b87315fd8a5138d4a72c1b167deaab9a8aa9abe87f0b6479896dad884b5365bbb23ff595e9e977be4a79faa3af504fffe42b94a7ffbe3a3dfd175f9d3cfd6b224d7f42fe484786f428245c7372f2a4d791293daa846b4eb04e0e279f82cdd29f98483f7ad187fed6d0f434bc818639943ea651640a19de701b4c214b8f22497f6b9803e951c80e6fa0610ea38fe9130d73107d4c9f901dde702b88428efe448afe840c3d0d73087d4c5f1a4e0026372ebbdc1f224d113647d82461b3844d13364dd397f00f5779ec74c231a31136615ce54f1261f35de53f0a61737495bfe8049bd455fea1a7ae324f5e4706e54fb0694a49c135e62a4f49c155264b49c175364b49c17dbaca535270b75ce5fe2838444a2191b0597a1446321a6193f4285c4f57b9bf4884cdd1a3706db9cafd43216c8a1e85abcb55ee7ff227d80c3d0ad797ab7c7c98ec277e92824dd3a3e020aef23ff99734d6ad93fbb2ceaaacc64a986c9fb2c62f2bacbeee97d5057e595be197f57cca12bfa4b3ec2798bacae29205f28f711349592592b24824658d48ca129194152229ab499a471286a464478ee4897c57f57fe40b495dd5f1e4fe8da4ae2a694fee7665dab2e4c4a63b945d9a2abd97f6f4af46565647477aa24f6132fdcecbf19539d344084850238bf38a3658356e5d59ccd1c40aa2b0c2183ad881e8385174a1f382d71a703ce1083f6a7464b40f5fecb06983738530aa9881318425d4606db846c33a3968bf7db861737c0da3469f0296102ace93092ebcce9365522f4ed8799e29a0e20cd6e8fb6f0d65bcf3861aca8c4fc31b6af834bca18632e2d3f0861acaf8d3f00619fb95f4727c65b16961b9fd4d1d19cde52b1f33d55cb9dfde189d71301c5ffdc8bd5cf544f222c9aa8c8bb57c05f3610cacad9373bdeaacca60276ed6314e76e3ab71f4260a8329cd62e1f8a2eaaa2d50e82f2c73ae303ceb0c045bb0ef45efbf9675ac7baf8bf95b96f733f76ff635e766dee9ab1136cd951fe11f266cc694527e45d3543ee555ea2bf78bb0595d2119ac5b7dbe32d7ea59eeffb0d9328f3b737f87cd6ef358eed7b0e9302bcbfd8e4dd3d49141fdd6cafd275496fb5970071b5dff828951d84c5bc1a6d64ad1de4c790d5798abbac272bf0a36ebcb64fa14edd464286f7a9476754b8b6d2f3f7bb6b9b8d3651edb669d6c74faf14f3596fb4fb01964c366d7429dfca89bd158488c1bdce18426aa5002152d3a8b9daf96cb05ab2f590a36b5d364fa0df7cb55ddafdc8f82cd7689446fb2332d567ad197bce5a706db5c3e73d9d6da6623e9c127352cf79fb05963ddec64f4a3acc26ca01842136250c869830a1ded94c9ea0c76b65aaf76c54cd8b43391e837ec2e57b5bb727f099bde02815e24d360a1077de8f499abb8bfda6b6b81e18befafdc4fc266c33699abfa47d8d491191fdccedc5ff68b0b5588c11352d471046c16e366b258cfb8f374790b26c2a6958140bf61db7255b7727f089bf6e41e14cbfd179bda4b149f7b5347e6bee8cafd21361dc4e63662534706fc5073b96c9098630c39ba60030d39ce90c1ce984cf67db9cd7ce67db9b5ceee4bed05c2a68db9aa9fc3a6a923b3bd49c41216f71bb62f57f58bd80cb1595a58ee07b176b175b53e2100c10c1913ba5881165894a57d696354e65f76ad1ab35f76b3867dd8c391f9cbc35e6b3b296bfbd2ba3aecdd68ae2a5894b5e125362bb0a349932634d9421330b0386c61afdcef128691b268b2a084368e4085180fbc60519686bdd355fda585e1600384264668d2a44918cea082e59d2c67346902070b9ad0a4c9cd1570b230e3055a7011070e581bf65a6c08818c5913a460220746606dd8c37155bf253bbce1692843c340c28d3ad88082074e48836571873740c1f2d748caba15b4a4e70a714839bb20c20dc268b28427588ebd1c57f5c7b40b1846984c614c6667fdae1bc7d3e973fdf273e99dae2a5f72fdf22597dee9bd326d59a262533b00605c3bcbfdd792540b81fd42bfbeffad608396a4f95e1a76ef24f794ecde9265cdf63bac54184bdac3c48c5c5231328de5fecaeaffd13e4a18990b7818a5bdf7d647fd7e19224067a0b9bf75e81565890193808eecfaa59f23c118b713a059c363bff4fbd3f72030ca864abf343ef54b7f874efd32807ee97fe997fef294fb2bed91a17d942f41da47294303da4798fb3f6045ee0f0165a35f30d697f6e1f8fb85e254bf846cbf7cf999ce326db1e9aa85e2d2be540b8dfd62fb85926e4c68041d353c4d953496fdefa53e802f7e097e7dee3bb2acb9fbdb498cdcb78f120646065fd1d8799e27cb3fc657cff2a7c005e80c35bb8a339122bb256b1e00d83eb47790ce50410f1b89d1660a63b244e6fe5ab0c31a1a8991cb14c6c405dbc7b4875d83f1cbdcd7b086ee69580310b549b6e2bd34c4407be4f797a455fc98fec8b2661197e27f7f1d14c9fe8fecf748f06928819abdbf2279df2369be349c8088cb9ac5bf7f1bf426f0654a6328c5f73ea663fc56d0bb1fd3a22776ce78dffb2ffbd3d0862e5fecc3f7ed91e5f7f5639c8616e8ff2e59730d81a84d7288ef83a4f7311ed397a4f92369f668e6c84d232d09d218288d65da6203560b1b63489bc1590682cb4288425004dd062b59334867e833d707db071dc170691bc1468b91bbbb5b08f7777f7737428aeceeee295752c44ffef6d16fc7f6517eaa0899e7fe12cce537ab04b310b34c614474910590298c089c7c413465db874d2efbeb5bc0878a5116b0b9becdf58738cff3cc6dedd3d08672886cdf62207ca84f73c9e5faa31197a64a985c76aea20918b7afb97661a430226e3285a940961190294c0556e40e6b18c7116c1ffdfdb47d74bf7d7fd7610dddb6fdad60cdb786dbdf8da4a940d460fbc5be0d63debe8614a0797b1ad640fba50473d5706993b5aefb18efc8d2e6d23e8d67ee695883f6317d2f0d1d9766f6efc8f273f71b59d27e1922775fd27e2991e4eecbed69bf943de4ee63dc490e036166ff8da4b9234b956cff56902369de489a69a8019b5d2381f0ec6f49da2f15db7ee1aa8371bb7efad749cdc92d375973254b13e4fa97d66eb14441ae4ed6a7a10d5aaeb9499a6dba02b9b4c221459d8d14a60217e497f651bee4fefe018c77e4cefdb4579555aae47e1a5660092ad018688e6910a6e65caa1ae9d70ac748612978e5b17d50d8f993e977ed83c2529093e983eda3ae28ab24651a73a98fb13e7dea5dda3e2aad99fe05d1f641fbcb2ed3b7eda33efd5b532ab0848cc640f3ad346c1aaa8012ae7ea1d9edfbd7f7b0868a690d5540899c7ea1b9dfc9eb60256beeb09f8616c7340d6b88e91a5ec026774cd72c5c9abbbf76636a53ab0db77e252b1546d829cba5cdfdb67dd0efefea779daf68abca31d72fc74cc95a7b70ff5a6b57ff98aeeedf6fdfddbdbed7eeef72639405faed77b5a17ee3b2dfbb56265c9a2a3ff75faa80f1d2048cb45fca2ef77b0d2530c4799e29c8feb741da3eeab70fda2ffd31eda4ed977e048cf5edf75fd2ade0d83e68bfd0ef27b58f9afb4fedc3bf9f8635b82585e09195d874b97e972b4659a0befdfa16d3d4ec98f64b69337d1ad650715969aab10f9f698b8dd72ac6a599a6ca31f7dfae7d94292e768cf54b30f797feb45f60c6fafef469a881fa3ed87ea11fd3b7929d413a43920acb426ce16d90d219ea97feb45fe877eda3acd99ffe75b2eb17fa0518fde9d37002b65fe83beefa857e4cdf062b6933486748526719082e0b21caf5fb433a43927a662068c84280b97ebf48674852cf0c040d59882dd7ef07d11992d433034143aedf7f1daca4e790ce90a49e19081ab2105baedf0fd2199254591602ccf5fb0364a5b97371b0ced51c1869b7a7d8e04dfbf6f7d5a7f6549e59fd396f5a3839e5fb7e7fb9cb71fc6607c613f39e5837ee9c1e994fff787b5fdcfbe22bffeabf61d48f9a7e6b9bfe68714d639d1cfaed6b8dbb6d5b4ddbb65aabbf56ad3766525915897b5ffa350ec6a23f6eaf61540edafb438145bf73ede9537faab9b66d1cd7ddf6cd1757d1c6e65b6c7ec79d4ea76d1c4d1d997e0d9b9dd9e9c8f4fb536c2ee1aa4582263372afe178fdf1ef1bffc6013193ca22f978e5f1af3d3e30f7eae338ddea9b3efdebcfcc71b2d799abbcfe643fb3df649719fbdb75fbe7be78bd7b3d1008821ff8dd1b8662f7f8eafb2efe3c1cdefef1950836799bc75761e9458c0a4320288a4239843e7c306432994050144ba5b17340b184797c259a70e941e4c9df4b9a46de8f4af7964e406f2a7d7f724ba5ef2b95be6b32914aff954e481339f2c87bfa107962327927a6f04fbcd3e9249e4e2f9edc0645d2741bbc41c63b912179429a48f096c8931f91a6bf2052fcf1150993be7ecf4411e4e32b90e965be1a81beee01954ae4c97fa4af441a91402249249148a0fef946b863ae32895fcb44b17d403dcbdf83e4e56958bf3ac7fa388e23d83cbde3aada3057d57ef7d72a4c75ca8151b31887b1b4b05aebd75b5b177441201886dfe7640953ebcee78939b9ba72ad2d57555c569c5cffd62a0e511f09d8c317c45715fcc03014c57beb8dafbc7a534f5fdd7a0ee12b710824487af015d8439020aeaa7f351cf0fb40e1f7150455d057d0f7dd2b8a9e27aadfade2d7d7706e7dcba4b2445fff5a9f91acf49e07027def81402ffe0732d52a9ac4eff3bc526934123fb1beed017d2d7df5476fc2f5af883cf91069fa0b92a5b1f4f7c7afafb54a180582a6ffd2d7d049f8a6afe089e8241cc39393bf63c87447d3c73458ba63e98e771cc17a120a55532dd55ba9ab2e89041f4486b747a3af6fef573ff07e05d5b73c602561f0eb5f17bd9108feb5208904df1b81465f8f1a1c7974f481a4080bab8db9aade6a3930d65a2b9555efc7f7af325f79d8f4fed61e51f4af315f85c217dfbff25498af44f8f3460d1491df7b63e85ad01b631a1c6b2833561f57f97f6428fadf1a7395bf47d61e577995b9ca653263d7d2beb1bf7dcd02fddacb8ca786375c5ad8f6dae330ee78afef2f468536c757e0bd0fbe75f90a74b14e0ef7c507dfdfe280fe5edbbaa00b02c130fcbe96af2ef69dcf135fa1e8e5647fea59577610b61627fb5f7b23fe7d4f1441f01341e1077e60188ae2bd9ebdf195676fece9ab6bcf1f5f893f335f85331f5f81d87d5ce6ab0fbbcc55fe153861b2f632a36bb884c9388c2e6b97abc2b2d732c586d1e2d2b185d9305a188c6cb4e99a0738ee8baf6abeafe9ff70cd86ffe99fbedf4ea7710395f517d77c9b876b3a0e6fd8d491b17feaf069d43087c7ae73153583a57deceddf1ae240bb6e1cb70d9f4e210ef6b5a7a18cc5da07fcfbc31c1cbfe0255fb560841e18cb3ad39e0363e9dbe33076cfa5801d2e6132874b985c7eb0ebcaf6cb0f06caae0b3c65e099ed7f2469df0323f71e18b7971965f565c60a8ce526cbf671184b181c467f8aa9b79d9e4cd67363de7f1815be8e0cc8f3a3267cfb1bcc5720aef9b0f8f6b79df1bfeff58ddf288a61e8792f5f7df8f678178c85e0e51131081bf1f67295ed70b9ed6c39e07f7f4150f440f143537ccfdb4e4ff4c43004c1efdb727c75b71c97af3e178eaf409c96af42dcc2dbcdc6a4b22ede4e57d9e770595baf8d6ea7630a8ca5e6ba1a8eafc0bf9ee785de381bc331fc3e10bc271efe30f8bd7d0dc757179b7ec44c2aebe4ed5febe3abfbe08364a2bf1875c711f4fe8640f0bd710320e8d550868637c8843c3cfe1d45d8b30ffa11665259a1b77f4fc8d293c8d15f9114e1f8ca24faef4d6f5f6bf9eac3e6f7228cfa51236a37be124bb86684c51f915ef4f6b5d3572411ebe4207ef8a31f5f85d80c5f2c9d94c2cf1496fe5af0842cfd67225d073fd348f49944b74193c9849f4965e164fb25924965913e248dec8fe8bbc9f6995496a8b5b2fd1b22c51fc9f0af47be775ad64fcecce583236b65ebddecc07862d97a3dd92d98d28191faf80a84bfbf0f7afb56e6ab0fa3c2ff3c11d78458e747cdfff7a3877572f01efcf0416c82ef89216cff6ae00581e2df0d0c91e283578b69ef5af082be8fe9db20ef9954964832a92cf141115b58f85d6c7b5c65b195b9aab42ef3535661eccb8cf44b3ffdacad7651590943ed97ede240b5612ca92c5bed4b2a2bebcc050c23cc6bb8acb04cdf62af301a43fd980e61201bae5339f7c5576ebbebdd8ff06fa793c3f6dc874fbf1bfd7672f2d4557434baf7c557d6dbe926ca40311792562826be069eb11bfe096b551909161a9d845e23516d69a2c8c1eef8deebe4eed65992c9f2272dd70c74b9d9669d872bc69d2f59cc25d364b2b3059bb9b6560cc6b5b4d84bd6dace19cc75c6b673a6bd4619ec76afd8279379df82cd342d045d1683bd28a62caf9d0ae3ccf55368c77c55a3fdcfccc7beb5d871d93db9dee6691e57d587c176765e2ffb359fe32b0e9b9de3aafa16a370f876f9aac6560c8c33ec62c7e89a4df7765e9a2a49af52748381b11cc5728b6e286c05ad5c8e623ba21eff5224cbfdf7ebfe728fcd7b710d87efc7619d1c38ee7ef7dd8fbc5ed77d4c73bedd4d3bbdc535c7555d7370706e6e86d8e84f0fd4e703aeeaae3bd51d0a501934195eb697dccf9dbc13a752b79494ee699ee6e99ddee99c1c1c57f5d6b5fe549fea537bb6dac3a1fc66b337fee33f9afb68eec3798fef58cfb19ee3388ed3591e6e27f77f1f36b51c2d47c3f1c9fd9e874ddb736d8fe7db8dcfd6e36d381f4fee2f7538db8dabba27f7b3b486e337de53795cd58dd351dad3519f13fd79a932541cca73c266dd3199de71fbb8aa7d72f7f4cfcd9370ec8df3f80e8fb6637faccf8fcf96b3e18c727287e3aae6b93d3567a771783aa7a7777c9ae7a77b6efa47247ac736c7559d932dce8ef36ca11e1eedc6fe70373f3b39c755e64eee72b81ed0a77dfac77b6edc07c77f72eccd8ec5719e1d10e81d6b3eae6acda7c7550dfac9da8d28723837aed23ee4c9bde3aaee7872b7e32a1f8efbb1397647c3e1d1727ab41d1f8dc7fe683d37dacfb6bd632ec755cde5e06c3b99e76dd7c3e3aafee9341fed67ebb9d97c70b69f1cee6687c3d978366c723ba6e3ce47eb7c3a6c7637dd4fd7c3e5703bb91f8727a767c78787fbc95dcf8d779dcf4fee17ddf8ca7ebfa8e52bedfb4538be1abf5fe4f215e8fb4539beeabe5ff4f2957fff1d49d18348ee6f476ed6be6f1dc77de847cd83704d877572e83a2b7aeeb777cdade6a3682445ef20cebb6d03f94dee3743a2d45bfce5b8aa733e9c0fe7bbf96e7eb89fdc1ffa10363d9fce27f78f2336bd1e5775a96fc7f376b69ddcdf79399c9703f270401e8ef3e40681f07fcfe5b93c7767dbc90dc2e6e56ed761f372dcfd7eb4efe7f3d93e9feeebe9be1e076fbaf0e607fc017d36d027f7731c36c11eabede4fe0dccd17272bfc5268803e2384f6e6d27f7731c36c59c9cdc1b36459fdc6f2d36c39eb08704baf1b1a09e15104ecb6b23cff6a0100ee8c6550dea09795cd5220e78e3aaeec9fd23fe787072ff087b375e4f8fe71e8ef7e35ece77e3dece87e31e4f0a9bdf0ec66ff1f57155fbe4db83efcfbde1f1244e7803f2803b3ce24ef813fafcf8807240382ba19c108eab9ac755fd2d78ebf972fcdbc1f97872be9e1dfbf95c1efbfddc1e7b6fee0f0f1eafe130c7559d93fb496c86383cc21d9047e5597a78c49bf067bcf901ed8072503b3994333ee67c727f797decfd017b34f006f4d1401cf0470373c21b0ddc0971349067e381cd7047e5372cfab8aa451f53ec7155ab883fe20d0ace7803e201ed9c787268c755a38fabfa5770f793fbcb500b71c29c700bb750dc444e059be28fabcc94e7f09833e68c38aeea949d0ce23199423d211e578d3fa752f4d9c49f1ed08d0f07c201fd70a09c1b0eb433e270209e6edc7195d9e1904fc807059ba19fd04da867cce1c61d9c912767f4e5d8b333faf0e4eec69f504fe826b729f413f2e93ef493fb2f891c912232448e2488bc152491a439a0308516aa50820541608d48d21c435841189ac082cb8725226d6cc0c40b2d28810e970b07ac1049635da8c0056b1421c71d56b046320076f0808105134d8471060b44daac80c91c43e0c1e28c1670c1a22185ddc8724cc7b4488a6e442d118ec825ca11bd48d10e2cf76f3cb95f8be57edb93fb5d241be1e4fe0d8c14d682572e617229bac92dba21619bda1a70c2a5758e91c25c1047ae59882d5f503f4d959a2bdb6ff203a306466abfffda99af6a369cd64d8d8e0cf7da6bda996d876b385b71697fb2bdd6c7fab8cabe4cd6d3138b3d0faed9b0e571150e9aadd1b085b9cadac6d67e8ab5d8c58eb15df9efad6093f66f18e9d7f087f64dda77b2e6940e8c37c90d94a561226e28e23c594c7480a0215b5cda64fb95e5efa106e8116ab0966c598a4b9391ca32cda5cbbeaf0ff3e57f79cab51c732d3b192ccc571fb6335f519687638ef879e970cc11b3e770cc113ea70dc71c211b35ec24cd32c09aa4b0cc13635998e6da6c6c7f4bb020960a4267f0efe989c57878b6fbd9a77f41e4f897f4fe7e64f7b7c16d7beb7df761d3f35e4746fbabe19aefbb17d77c582787efdbbceb2047da1bac0541e4f8f67a1fd35f673b7b6d874d0f9b23cb6ff887ab3a86c2bcf65a83401f8b36db621ff7daecbda85fa9aedaba7fb159bdfa611389e76d308f73b9bed37e7df6573d6c5619d771ad9637f366169b9ce761b3655d6fbf6193eb649bedb8e7b0e9b06d3b79b87bb9aabf7ecc03c774101a439d8dad7fc16c310ae3da6a26eaed8a76f229dbab672eeef418f7e64905933424dc88c1152e399020635196959dddd972b93a0e75729272a299fcdc5c2edb66e1c73ae4230a9d38da2002b68305ed94c56667abd57127a391b7461a4c1381204d34bf1422e190a5450b9a1c31051d4644318bc164dc7976dc681c45a3f6d21e249e2633ae508215af33b2d0052b8375af98d771a336044b0361530c41256a6ca1c5167500210b2a5894652f7ee95c5fc9c1bc8ec39dabaa06f3b3042eb61063075ff8b028cb620a3b336d59b262d3fdb27369aa74596e2acb25954d602cfd2c61b2bffcf4d34f3ffdfc52865cfaf97efae9e74bff04c6b25dedea98af5e5efe4fa771ec72fdfaa50c30180f0f8dc5ea97b4a7bfcebebfc25e7a3e76e209bfa4b0f04b19beecc6d3bf800fdefb7d62e86b44efa9af42d8c4352051ec1ec657352286b939738861402c436eaeb73e65fad95d9669cb126b03b657854b7dd57120d1f67f4f5f065fdda71fe3abf0e953c057e0d3fff1015ff9d3bf21393e486edff7bebd864ded378c023d5c33823fbe8e8cf7a1876b40ac9303f8179bf7c16ffccdafe6f7d3fc7e4c8763488e013a32e0f308fd47ee2fe277958f201a13a908a3e259455815cf166c7230927c0f7793b18de5df9e56a6bdfcb5c9fae4e13ff214064558b7c2b15d61ac5f61ab6161ac63618bc42617636179cfbdc56dae150d6667dbcc5fc555fc7938c6378c790c7c9781329f812e7b8232db025d9c0c857acf355343cdb617e7287fa29da1ce3f50d68271d5ebd76a31927ba6e93dccb95ce55c6b8399259f69b2cbcd621426f34ecf61de49f2af3bb997abba98f9e14ee69dadfa72f5ec75f2907fda2c96fde35e2114dccd5c35f2bf22d2f42712e52f880c8d240a0ae8447a101ea17ce8c76b4191e94fd7c111844d6c925caeec1fc226ea47f86bb9ca5bd941d8fc4e57f9777a3390373b61d39389fcc7582cfb9be689ff0d0d2cd4e8c2b2bf4884cdfbba2fd17589aeebb66eeb9ef73cc12336fd4b27936825963d844d1006c2c017e87265ffd363133562d36b8d1ee6a102cbfe26530a0a15c6b23f0a0a36435808c3668812beb2bfe9756452de844dd39f9cc45c2ce3e8bd3e5808a5c7a3c0b2bfcaab60537cb9cac5578864497993e99954564c96fd05cbfe8f53529e49659d32f39b79b0d3f7cafee363f3ce78ccb217f364deec3bbfd68dddd8ca0a2803632da5506686311ed84cc12678baca41a656fe66dfe3976527df6c657785422f580f514a2cfba7f0857dae7bded675ddd785813370d6329a65ff1236439989b14962d37c6c5e19283c411ed8955d05ebc8a8fce943d804bd5ce5cf8243269898828222cbfe2b1894dd18e8025f200c8c8132d28f58144b2f43f99809829dde0c5dd80c4f536c657f152c72657f140ec159188661483a4720192876c2a678824d5874b9cabf84c59928be4498181365e22cfb081a413f62d0cc55b3ec4fc2264826c2a016069dd95dd95fb01848967d1c4fd04c3c431878672dd1f57d399e61cb05c65eb27b7ab06f06b6b2872e11bf7caf10ff9581f8149ed92f1ec5d687591eee40adec23d8d61e68929b64d74983988371fb69aaf4b3c260c00af3b00659a6a1eb952b080333839106ae4c430a9bc12ca7a05df463bac99afdb44b4821ebae21091d17dd51a0b62b4ec56183d67c41f44954b6c9b78214c6572f2fffa7937f9344464b9db60f4abf524afa992b0f9d81d620350b31d2b7d516e1b6d56ec891de879e1772de775eb899da5b6c82dec6b8aa294ce7a6fb0d0d2c119bb6abadf304b13b77ba8abb1d363fd7c98164a28fc07b4550e899d86cd95357bd8ecc7d109b208c6b6d335775b885e3884d8a4d6c96fe62f3e5a3085e2dc63936b7d8e86142b65d27a0d126f605b593c4c93c0f0577a7136bbd55eaec6c936daecda523333e3713dd50c49f6bda3bd6c23034359795759dd6edee22366dabeb6c4c93757b7b7b7bfbe6dbbbd6610b93cdecd9b22eadd5796b2d4eb6c1b657a76ddeeeae0e637bd262956bd5fe2db4cc575e65d15975d59cfaaa3b155679725c2e1c9c56abe6c6572136fbc6558d438d87bbcf73b3b5e3360d89afba6df6765663f9d5610e6b78ec4ee39f5e28ab8635fc90e1c2f8ea82e1f781e0d71fae0171f8fde3078261288a303017c755bef240e2ff4d789adf3f0ef1d1194c731de66004895e087ae4f621a9fd05490b82db879f5fffaee791db7be8a0b59b6647727b10a9fd1549fbb7454d13b9aeb9eeb8e6b6ed410ebe2e4d955496bbbba97396c33a3f6a74646c7bf63bdbdddbc6715de7ae691a365fb4ffc00faf3b9d28c08d31db37d691f187a12618eb9b6094d5d6c0b834b57d47d6583ec914f6c44ebeb4ba3152d81339792553d81337f95aff5088a42c1ac2948ca575c95abe7298bb3cc75fbed32f0e6f5a67efe7717dca5e72bfeef7f2fac5f5ab5f508c659f7d8ef8548e629f619f7dd240f6f2f27f3a6db687cee0719bc35a8d23bac21cf032f17d8d95dd788ac95076212e47f07eb8ce60ac6f7a0fdbe77ed3de7e45e2aafed2d8914ee3e84f22dc4d7dc4e8411179b3c15550a150034707f96a20fd986bec4ee39f7c96fb7f663e32db93fbef06525f859efe0d91954559de637d34e7bcffe130574931f6e0ab9797ffa6699907f38ab5104959158ad1769a6f7fb91ad6f0c361d58933972d6b99affca57cf9f24fe5696c19ac87b17ef735ac41fb8e2432ba866fa02c22fe270be1d9c6da87c56d493fb3eda133f82a468a97143b52c0ac4b0a1e2962d625454f0c132d57519cce35e69c98783d133b0ebb0ed2efb0861f3d4831961d0e7a150303315a455d4a14d12f6adc61061d62c4d1051b4350238b181898d12a9a4b6b45eeef489a65f095cd19bb930c271972cbb001cab2f589b1b4305f85a0c9149a3469d2840a3458fdcee32b10154d9a702109533469c2eaf798af6cb010030a2a8e780215ea5082d5ef3dbeb2b942066eec4cc1041628310556f7f0828e2f5c81841356c0c1eaf0b4c10cc6b042933696c0ea1762ac383dd9710993ab06ea8fc1ed61ecdaaaad20305d378ea7ff0e3d38fe07fa4efccd7e758d417a184b98ec610e6bd8b108d38c7dbde7b4a7f703a71f997ef8ea74fad15792e9f4f5637c4522fdc95712c9f495f415e6861acad450868637d450e6f434bca186324fc31b446f9aa5173d8caf485f9f96b08972fa134f7772fa4c5f8e266c8eb059c214a62b5f5aa1119763ae205c9e721571f9b986b8d2a0b6aec7694e654dc482f8aab6a88ccaa88cca6aabe25457cda9afba535bb5e5ab18274e276e9c68d5961338b5e584abb69cc8a9ad0ae36c440db242072d2970328545318455a6b0288ac817f4cadd49b48fd2ceecac7d94daa99deda35f2ba27df8b0bdb70f0a93a20b3ada478a1523b704607cd0c8d26917e799399c04083a3bcf4c23cadec7340d98bdef9e861a40d2914980a08166cb5d92ee69b6ec3d0d2d0024731fd3942c3bd3f748207864ee3b12081a72a53150206a93cc3d25cb7a019bcc614a074071b9bde6bf611ad31a6967fdd26f494b47bff43b697ffae58ab1d4cedc2b18290c8a38726967b9dfcedcfeb40f4ab3f5f155d8e308de36c52ffcbef0b688c78bad70765b44fa30668e4a1fc2a688cd4f247a94bfd83cb9f7531ef52abff22cdfe6f7a41e85dd3a7b26de1316458f7ff02059c818b2bac83ec9969128a4892c91e3c3844624e2e984147a0b243395eaecec56bbfaf57a9142d0388e236ccfeedbdfdf0e61d30461db72d88b5659c71c66de0fbc67cb753f4c5fdaab6b9318973ed9498c95a23491054a2ec17ce45627c65a9b18eb6bb9c26ab8b5662aab5496579dabdaa7071a438f34d30c932dada54dae94c650d60682d2181c97f5eda3ead3906d72871280c9b59234d3b00620282cd32d089da133fd1a4a80ca60b263d789c11d3737beaa3f9dc3b2ffa5096b1882fa0e8dc13fa6eb6becfaa53eed97fafe4868da877dff21ccf6d1ef5fcff64161d99b7041a6b018c491cbfac3842cd73b7ad53eb45911d9ff6ae4169b428c2be41a7a0c8f18ada2313c66f40bcdb7825fc31afaab8fafac043a6b98f68b7f16a37f9169ae3fbe8ae1e9e19175d96ad861968c6bfb857694c7a5a9175f993a32d67aa3c771affd69bb0ec6378e3beb0cf41b82fd9cdfdb32cef40f1bacaf6e81e0f51fe3c3f4e6e7f7711cf7e2b2eeae8bc9de7c17bb6d9fdd72bd5e61f79decd96ddbdff6874d73b42d87bd689575cc61165bdfb0d9599fd9d3b6ac635b5b24051813b1aa8591c29660220b205398126ee4aeeeee2f8e4b9bdc7d9dc1582d8c85b16f7fa3b5d61a92941aa0ecfefb1ec692faf4e0abeab2d487fa7c64699361aacb57485ce53f82b8ec4ee39f5e3ec3bc380c8caffa64b1c6d3e71226bfe412c607927ee9154dabfcbd5fca2ac50006904aa9a8e492fa90b2b7aa24e50e3740593db8cabf3e3196d595fdabcb57e1fbd71c5fbd7c057affbae3abd00742e40f57f98f24055ce50f22635ce52f9232b8ca3f2483d01968068247b6b82c402e61ec57f223cb2e7f0f92e57df06f8a8b1de3fd0a06b161cbf57db8186543fe48902c3beb83c5281b0a90fb0b00e32bca727777cf95ce00beb5b82c95d864fbdcfbc08124a531545cdad7da87fa15974b727def2d5976fe32f8f63f92d218ec533ac37df0299d81d218405cd6ef9ec6f0e1b26aef913e00c13569d2240c99e6ba246f1c16a276d936b446bad831daee9a6beceb5bc7a54f53ed1bf89b7defc14dc33516ebe4605f7b6f7c2b128da3e78160b7bbb769be9be0268edebbb9534746f4de7ceeded37e9fcb425eb25bc81b615a350c4bf87beb2f586fdbacfb51e431a4eaea33140ac7eebf6f7bd9ad052ae1b0ab695f44f2fa2a89e20c1c3bd3bcdbd5629bb9c54cf8c7f83014566325ec9d246c8ab0a97da725bd292b7d77c2dc9b27676d55577d9970cb2c6193a46173844d3bd35e78c3e606c233fc21f6fa55637d76cb655f2f0f9b0e76e0b74c7b6e6f6a2ed3ca364ec4ee6a58fbe92d97d5beb1c75c5543109bb6e5cf6193c3e68735197698ab5eb4ca3ae630d324a286a57d57173699d498cc595d6b7f6bad17c79db32db6b9ba7a6b93c1b4d7d6da4e3b8b69b057bf8bc32fdab9e19f69f8e42a5a2d1e5d45bd31d73ad9d4e6018646627cc11d995258ab8b4c314d674a63a05f9ff6b87d83cec1583fd4b7aa3cd94c8c1496044ed6328525a144ce1496441279cb1496c44daed8f64bbd63ac5f18290c899f5c82727f3f81041db93e9da1d218680c15e7faf6975e7aab8d25cb2162bc92e05bf2d2b006f02bc97d0246edbb7ce9759086159726131a58a2b5b80c22f670535922131a58a0df7089241721fe4d650d91adf795bc1ddae0f97b4b7a2f9294e54fe90ce197f62d3982d14ec0e2eed6bf770455226a13964e887db0dfb96a7f1ba4e1042c2ebb6cbf7b9134e9d7df70699ffb0e2560bffb90ac4db2485256fd9be2d264d43eaca1e20b720f8635f477b986da7b3801cf1603e1790af1b5f007119525e290a4d90b7f885f4991a4a104bcafef95e43ec6af830e924078b67f4920bc49932677648bcb9a3f0c442d6d72bf47965d7f4c57967fc7912e768cdabd96dc9e86da46d278b6a19ee7795ec0266b98c62d409b3469d2246b24cd96ec96b8cd6d2e0922681fb67dd0dce0bb5f4d7b7f0f6bf07eb1e95ad5b8f44199c290c041ae0f52b2a118290c0926722953980beec8f53bfa4d895cdab76f30d6a74f29caa57f3fe03dfd1f2f560af8aab7af9b288260687883c671b5769de7d57c4df89f677a1fd66cde7f718df860ad191dd76cdf15fb6f4fc29ce8bb179ff4a3af6ac6d08f3fc235a27daabd08734f0a611d19f1bb1f4930dc8b4f9f3efddb91dc7fa489cddbd8ac169b3a32fe22367564c417b95bc30f700fe32a4d7b11bbc36ae5b97f5fc42079ff23bdef48ee37527b4bfa37599f8638781fde8ff470477298921aee486fb2dad425a3f660b77e2561fae4607693f9bb6038185803a3c0a864cbd56745d231a6d4cc88088200d000e315002038300e0a48a4619e68a20f14800c70a2545a401d8844d23c8971100621e69031060900c0000002000001e3aba77b3ca4e695fed0678f06ed936d92841417db54e554821306f293f621f1384221cb64382e468d365ced45b3efa5c5b8fd06d264f5837a126b07a94b59abc24c54b56a803ea8662fc3147a917577bc28606398867cd1338b071b26cb6bef0084ecc34ff5e4c1bbbf76c670beaff3796620fe444b8ea2bb7fad7960864243b3be48b5ad2b6259b706bd8b8903f0eb4f6d288c6c183ad1e16d2b9ef2988389de07e5109b5921d348d137a28e08c025197254b9ac93251266cf9f6068de1b622ed856a13077363647659eef051db80dde93453885bd1c357e81f9384fc4613dbb3e737ddfae1a736bc4c9796a5c9cdd48067fc9b5471c106de1c8f64bb64ceb3f790bb641da568c518f70651672effe8e09de1e76138989be96620d7ff6902a877b767a0eed0c1043d240db4464c1bc8f29ef3e729aba8b870f35226267455f683bd3961a09cb56f6f8e4cd6fa270645a945744a56c508350c92d58e0a1354a4a46f3012a265a399d7c784552f02a6e819fc49ba3033fd0ad11354965ca53f7fc0d7f65b2442222883ced46e2743a51effbdf5269c02a195f84d74ce79d56badf8a6c22d2b63907994e38bb2937793c9edb8342c5d13eb5530a0c646da49d537d2ca8dcf8131d99714f7cfbdee2504d3cba5414e0ef6998a1e0247e03dac637f5cc21ace2a3e28228acc14054016fb8394e6382381bb5a6d96441a7cfa5a45cf3e19fd28b2a3977594813cbb5ce3c861d4e2a1160bba2e80400c636914a89db5a3ef9e354adc4d54ef405248278feed2bd978139089d1b57037d6c4a293eb0ba9698c93b13f67bb1a9b7ed1f9e0c9fb46cc7807046d9958f40284c761c8c0da3398cb9c1c8c134fe450e2146457c3c8531650f1225c5825a9effd60a8766290573bb74bea426cb9f6c3efe2806f30ddc4df8fbeda7c9a65d587b6c051070511940a35614c00c01697febad1f41169bc687fdaa772ad482df2f7c85a0004a4755a403a683922dfcf05dec8d69c2f039c94c901a834f1cdbe07ca23e5c1c6aa57dd49459171a28aeb009a48711eca2d063cbdacfe29810e73b619b2f37b4d13871b40fb9e17dcb370d79a577cbca6b6cfa839b484e47dbe6dcebc5e9c3c166b32bc9826ac4be665f972aa1bf387ef41e044851b808289d68fecb64c0ac71dd9c3e56a7823200fbff289b37ed0b458852b337e01c30e0de0151669804003531035cd5cb6b45dc380fcd5b8c5eae92f7a26023e1996f673beed2a825cbf9ba8802f8d58e1b700c5e84bd3489235cab48006f0764f92ae2e52d452ffa1e1a392837304ede82b90226da06b868c58f1bfe8ce37476947cb64757347143f8ae444a8520a119a3fabf7c566fd05432b3ea70a91923afe79915d971db6878a1ef4ed7a946dc50ca08a01e2888dad523aae8052a0aafd50147cef64776b54cc82f21fdff83a8f7eb3a5528d18940c5db65ebff0782c9d30cb8ff9c1fdd365d3c3474141c4ebab0359f890ca2bb5704f48363bd25bd980cc661a52f218d7e9752128b0fc92391aa3c48de0c14a31f8b25292a3db6b449468b95ea30bcce857fbf024b57419b99278522fce084e24351a721b5285f309d584b30f895aea4f9495491f1cbe25210f63b1e59d57c9653c1f8a08cae10dc927d97b240dcd8351c9931362b5c607b7df92aa37adf0d473639575f0370cfc3a0490d21a57c3beb11eee69248e1b17d1e8c83e5ff9d0c6d968bc52942b8d4b791f1574cc9dad552e8f9c8d76c425e33c79e76309152a91f1d1d8fe890a2c29f3e93afa89d4222e660afa140068bc8d0bc4b0625736c9af69dac322a93383abb86578a731670cc3d36d03b514fb7d25277465a75409ba5a1722e5c9c945d24791a7519885e5b43dcd0eb32e086a8a6c1d7c5658484caa05d83942a413d9ae58b43c5ebd501b1bf37033109c81c38262d0014f4af8286b4765777bb0cb55205f99e8d646cbb1b3aacbf4e5b2d73f212bd3c4040feadb9e2d66465aece21d5eb252c172b1b6125164c42642635e441f021b07129269347ac3313ee4d7ce1fb174829fc931f20c328943def650af4e53506204b9445d1a14dc5b79bc7b735d934732de51dbc05c044103a47cc737548853a87083c6659235dcdf17d877f8d11150f2ef8a8a1bdf1245ef124ac7949c94ee46d1931a25e9f2df4c3e93a4ba544e9b75c317ead997f1302465b9f92ea52b51f12b8fa42d978158add958ae898ab3665f3dcba975644ec44f3305e30ee422250ec8b445aafcfb187e43750c6716a9f28edb516b07e3858179ce75d724aa6a417bb39032d8e4d3c6d24d4625caf51c4c2fb30ea89a3303603d43d91f342524a520f2823514411a83e08197aba806acc57142a493a7431a1d6407ace94e102297caa853658b0ac20a53593f54f0aea870a04120f7294e970ab8de143b4e8515264b2dfed106215a1dd6d6b50adea9285c25046c00c7d4d992884e291968b31a7eb4c2296f6e7a1fb89c362f54108b87a07791358eace564723afe5db7abf941cd34840a201a3fde08de31a89e5e1b850a6506309b36dc1a6ad6aed7ac59a17e2e421ea182933f0c1625a18b4fec6f27ea8379c8d044701c9cb225e583e27cab31e0c46e8e1a77746f55636e66f1fb350eff534606646e7e55cd5c5c07b39b0a0f51e5a2920ceba06ca4a0a91a1931e7031d028d19cebba2bd5fd010c271a88e336b28caf16d34599841400323bf825df9a5785a5376d4b309aa28b7ca22c902ed093b8b9702a90138ce85be4b156b80e3f28253c33a05719b98a53cd9a851f011054d39141bc95f431365f60190d670c3a0fae8a5fede77dc109309e5d5dd9b0003037546273130800f29abbcd7df90156511fc302d8e62a1d8b1e314a1d94333f4a4c0f718e982aa0a16d87ae856ce866d4bab6cade23e32da07f1050eaf9763b36e862e552e1e14703ceb1640d4cedf04c2f31d139ed08c8a18d05117f7abdced8f1930e3b6cb86c0d7eff4f3fe6a4c04f9eb275f6b0bfeecc0a9e2be5762ce6a540b618e0f576c0860bb724a3fe31295e74cb574a21c12581766a17e608142dbadd0406cab039e09f6a925eedf71525f8bad5a82ef1a3f985597c31ed49266bea6862fe6aba6d395f1af0025e5aa67dd578db039ae2fd7f0a8c4e0ab6a6c6a40a993ae2726ed6989b45673005d84de43e5a5516497052dad6b6c70d1026c486ea3ca2964eb01004aa7878fd0826885692cc5f8c8644669ca1e3f41a54a374ccc47e5518c97c9d042fdf63a24224a8e69e48a0e4f44654a1df6c7862727d5a1b55b991c5b2d740bfaa7211210f4a29921ebea8948e800d0cf9402e613f9ad27e1f4928e7110826d811504de88f68928f894638af86360416c096429d10f3dc0dff88c4936df59117d2bf07ff13a87baf21502111640cbaa275b0828193f94827cedf05b84ee5f21133bf4489be11605d9ef316a242910132702976a8f02c3f04f450b0c6ce5c8a6a63dd8d3362f8c14afe806fb4411c4beea4209c2db0942f0c7442fb5af3a9d3c585f75b664894eff4004b262b3d9363ed003ab2620e2248286da3b01ed3b37845ed7e591452920168e845eaf2cce7cf4ce43abbacfd482c857753da70aa5d336e0354bd1b227a14dcf127402653df55b158c27979512a2ddf40bf455db60d9bf9dd87a2a2299a9e3112f759362d410b048b1d49532aa6a5ef699796078d375304e8e094e88da72e286a3716348a2a36f7747a7cccba23c01c8e599023ef4fd875e4f2542761ce3dabc02e581be765a753ab93fe693f674091eeefae248cae7df84dbd30f94b75e1388b518abb23136d78a9e8f3da40a345c1847b5d53fd88f7a9c32f664cf22ec04b73e22f07c100bc649ecefbf67af857fb4b758b5fa3d2d5a65e36fc5f74420be53642ca4df1384d796e2d5303a3c7f28489052f0e0d2da7d53636ef370348f61bfe9e11fe76952342149ee4659be6e556eaeda1756a7a96586a87e6f6eda2efc9b0752087c7204c0e11e7227d6cada591ecd0fcd7daa7141c4e4c55be914ca3f20a40325d5e3a897d7d7de5c736aec7b829df0ea9201e647e3f355f466431ce43b855904078261570f874b5eb584542c43a9198f19316e447b6a7dd2debd2e1118644708d45a45a613ca282680fb3cac118e95dd29611c36184a74e59974969e7de0ab02b701319d140ef90fb96291d0d1c3aa1c0c33df512c39b93398e678fedae784b78381359404a3a898e12f1fcd0b9a4a2f74ce74f0936e90418e18b40061ff940029723ca2e3134f46f36decc330d0445f01b8213d0974eb1372689c72152945766b44318f49046f61553413d16021c936e534f0fa135c51e8da16e760ad85900083a2c710e438dd78fe71e3ffa8c344a8a381dec5e3d0a7ece113fa1d8753d47110bebb3e22108ec1f84f924ba069e87646ea46d45e1d3f19342a348e72551b3ef549545db3e0f645ae79e9fff8045935d401cfa0fdc3e2d309912362efc8294be3c4648f6a3138440ed3ff0e1c725060fe014c1eaf2820a49c88a72ea1bb416250029713ed927960ca22e36856199dd0f446befcd3eddae8210af370c24843f51d991ed899848d476a1803d372af991f94a15c76d7e4722681be234104e1e877e5e987396aec07d291ca6a8a75a9c5bcd470bd3e23fde706921aebec1ee23110e615d100f95f2a3e3e763b3c205ad7dd3833e87e74bdd3968081ebf0cf5bf64b14c07a03de4e011857e184cb0ae51acf8e4a50fc490b486c7d1b8c55c8b9de75514e1d3eb59d5d73b69eccf1da59731cd1b23e1ec0ba8d9d150f8f26093d12b0ff29ca06fbfbb61fbf24558e918018ee7672eea8467a6e6ce347af827e7b867551e7cac596358db30dd7b237909e58db3dc14f9fe0773fe178594c0ad99f882700010c44cce945acec02b075443cab27178e4a30407bb253d855d281b15ef98e26aedc1068e519c4ca39ed9bd744d2464247f9ee3a9912c757ab823c0964f6fb142b563810fe0f01522f82e6691ef3f6b78146d8708c0caff03b60924305e302aedc5443b3a0bd80c31bd95c1ae06c7dca01a9c19d3202cc2c5cb89b75772609a6fff15a9002fa28510fee87402831d0b0a97943a01d2e009407f65e82437f922858e0498ab1dcbb8e36be4646f54bddb7cb47eaf37f64bc55a56b9387a7e24c0a4be3938b77893381dc63cb63100f035fbdc437d391004b70a4ec74fae41412762f2c5b2189c775fa452188a1c29bb940a0064494bf35b89173d6eaef97baed4a9390f115223ca7f34333a97e877815ecda9fc86719d452942fdbff558b8eb96240231da846ff8da2e299f4b1b353462eaf902f5a3b6b903d76b3c5091c5d5de73cf745fc2ebc81e9e32b72e99def3291430574cf7e898e9cb9d426db52297fa8bcb64c5798c33cc2538cbaf060dc48e01e59e2ec98fa5f82ee8ef0b1207070d1f47de5e76befc78e15cd347fc9fdf866326be155dbde9c13261eb23100b69db0eccbabe255d71bf4021908fd79f2301edee4236ecad9cde5a08770437a175cc6a819b4e03ec386c4c7152fcf635cfa255408105b6f2ebc99b017ad517379f616498c8b21495dacf7234e881d13a72eba166d5a7812b9313a05b7428fb13a94659d76abdef45bff25b0da43e34cca802b3bae62e03a1f78a80574dae0a57def7efd7490fdcbfb4ef8498a3fd10fec0b0480e530374b00818805823aee00d5d34a215c1c963270724b80b82b3f02004ace9bf0df542623f4359ea4d17e560924767a8dbdc3c3a215bab891cdc6c338ed8093bac365a77d4068ce7e7f5106584c880c32ec3578e5a7892f74fd0a510d1907c2e31b17ced17bb22dad9b51f5683a70ebe79de2fe512735d83aa70cf02095e5e14f656cb571e974e8960f4be0e81c60809df57989327bc619802f17e74f90a862554a345c54e282b67b0d1941d7c1ddd55349041832b6698a3e85091052acd64ad1a3f004b9c8797758c764dc4656c2ac14e8a0b69005c02f2b322ca0352bc741202a910f1ea275bdd98a46bcb683152326943d831213a4245d2926002cb68b0960404859cbcdd08b95ecfc38d9ad9185ac1263403d81af08ba0769846f456bd3abc619c3e12b63df1c400ccce152c1b5c54eac84f054ccafea7fea9b796b78c499cd33f0f2971a315cd64abb2f54c949aad202cca67ced57b40d79c0aba2d4bb34798e03fee90db6b021a2a059e326c4fdaca5e4d7260a55d981364696080dbd70b07facd73bfa9a7019eecabbf20c3ad030821122c8686b1119a465e13791cedb354518184c3a13b2d30213ba5a45039a099672e37166c2cb049988b7266474d1541ea0a210a7dc304b12d25c75aaac6331a31772a264491309692adc4de312b64e103e94e19ab91f181601fcd32e6871db59425fa6661338348d256cf406abfdcc34d9452613c4f12c60a8b9095083180d9aba4f0354a276a484690e9e3e88c3e1699e3eb8a2532064a461cd710ab88478b8e0cc0a3c9bfc405b6235098e450eb19a285702243d8ee30c1484804b4ab545d6e367b9a1f05da05140f8e9e1df9cfade78e23eee0a102debc321d036475eaf8676284c3b523a1e4d6ec47291e392489e5a1bd4e37ffb555a13216fe8e04680bd1cee989dbdbfc5f9911cf7b0f975d6ecc695d63e9aa1355574c9ad3edf55922494240136d2e32d43e3254b1042857ab24c7300a90b31b4a75092324789f005d12da66e8a4b81840b32d1da3389d430fb388c24f416fde55b5005425192493c94005df547374efcead3d632321fe0b6767eed84b578b690c4b708c358e0810317482c9d74c8317795b5e52c08d404a1b240773ef13dca1516bebd9e44b279b85e5171be73148e3f81cd3103008ddc74e4e056f2ad6bf7d050675a41eb0b4e2d50bec8e181b084c25428721a87e332ab0432b7a7f01a1d427230c0a839df2040c14af646ca48a7abf0f0db112801d681df7e8d280c33c1ddac1e6e48fb02e008b544a7fd1d72d17026d2c3f6d66e6fa3dc577e33ad50d923ec94abaa3f0ea982edb4e2a8410dd727546de04c55009c01e8e3316ce2686ac9dde1ac00c6113da4ec6e3c081e6f900063144209793c263f79dc8469126e4614827774019e25df5a3d01326d00023d3c76e67ac3726eb7e7314c7c6d85bde2d41e131591c5e349f00d4ad86722533260443ca8e1b1d11fb4785d7c3be1314a6501153e35a60ed607fd4dba164e1ba12c773da5ac8cc787be1f5e683499431ebe43a97670bc84bb557d30ced8f8a1d06a42ddf067d4d6b4472095c7ed343b42342d70e3205a21a84d95512b13a37563e9cbfa6ee63cb66f7dafd009c054f0987752ed3b9456d1898bc4022e69eccd2a3b386b5c8090ba1b666c85a0947d3efdea52287a44beeb290ebfd398f5a4e5223cf2d4c6c913c76d3dac45e1a0a9227a3a2dff5d1c5b109a4fffb43b9ad921327a49e0178a3c48b4f335fbf8446ddb270f58b5528f761579c8175b48f0ee4b89613fbc35948db543db9642e0491ab76d7f4a46588edfcee1e380455c73761f8cabdf9b74f52f08b4123ea24f842a7d7e748cb645222222aab178d96077e51f0a8404cbc3b4fd7b00c2b141c662df94473c0cf612d4b8c1d7f42e2bf6c3ae18c794cb733d3cc5870ee47f95906dde3a9467c4fa711bb8f485a890b473f64dea6cddd0b13122ea4d6e828b14f04575f02cbac72f0baf173db6abf2b2cfd7560588d519d77ab0783559831b31b6e553d8a82aed3e33c9f026c2383e202e136ccfa5e784ce903ae3e013eab41b2d7bda096610e07bc3dc9f43f3be8519206f7639936fd5a99f4c0f91f83ef2c58b02cc0a89bb8ac90336933f00a5dbaede38382a0f7e05e8625feec0f17bc583ec102d5cc8e5abf6c42be22bd46c5e7e0e01debdbeac2dd1707959a9db37203e490477478b5d42ed79f91ba27a80e52deb3b62bf5e7ca7882a764adac6b821352ec7854c2fece70207cd59334b1329824cbec61e3ba99c4d040a9c4ca767f5930e64b94c85dee667cca2f7bea9d465035e469ce220bfb0b41bf1d7c7286a6f14a434a1d4c851cc2f325950e27bedd3b99fc05730f0d62dd44dad895433308b2a40675268f798da8d43c0850c4993c46c1e33818823d215d71ac69275e88005b5319651f10dd9e79509fd3ac98b775464ba00cd27675d2d31525d0020ad434eed312150961c8bc8479083c94c1db89319caef552147c8340212070bd6458617f50461cd58f839bb3fc86ca3d8a50f8966c74a7c605d2f3e208bac8bfe9824f4db0455e24ee5ba71345f6bb04fca9e1093cd7db2b24b78e583e8e9a84902cb2b1e37aa0cc41914bf600c05b16826461117ab0e708cd89efbc496e7b15c6413e92d76ea63ea3e7987954cb1a2b736e285d2b2c01f261253644a7d3ce5b4d10a7ce0474e842893126c342a49a566257940c9cbab3f3209978d89be1a7659c8724ccb29ece630dd4e1a0d6a607c375fc481ab9481c145fe95643c486b06d5e722cc3c09d10f579640b340ecc77cc68200be4f6e5d55f6a095d54ee258a9473e67c994e561bf4224cb8a138a7a0e4c2d27ae84abb0e1061666aa9c80f63506ac288dfb23288b7ea7dc7e48e0257b3c054ba31fd7c6517fb9202c34ade3be7544bf52d5072d7e11fe640604dfdf891664ceec840e5607bbbf6cd25fe7c2f395eb15260e6e37dda9a9e55f51e8b9365fe834c70094cc69bd750f982b6ad65a91c2789d9f0d4923e343e4290a8ae44622098975eb25c6f5ea06f22d5b6e15ddd115dc8366b220906ee538d10c7a285368d41c4621c71d838f9b89e1e6c39cf28456c152922b62dfa42dfc922dd9e22fc2c1fb271cadd492c09aa2e01314665fd08d58a1494ed084dc48bfa6f004a6bd0b60933f6fc2eca226d8c2a364c4c286fa26604ab08f441a6bed2c42cd6f84695121f1b3a2a06a6a411f498469127210c8e4ccbe41deac643c030b88476855c6dbd2c1f6b2015fac8ee60e7c92525094aa96e2a8cb9678c7b01f1ea1097e3395b13c84fb128a9d44c9c1970d6168ee157585ef9527ef9c61873a9b4b0884f6b0d213059d21a2b437a2e0b4ba4c42461b0af9a59969c178f9b33f37fd31e195b2102ea6123233800982e9574639e142fcf137682384464cfd7938ec71d04216335b97b56e9b1dacfed1714a76295314c0687800a9702c596e05178cbfddfad2ee4fd2fd06bcfa2f911cfabd968e344c32725a8b7b33e74cc2048f75addf5c78109a0420eb54a983cf38768a9df76c17814a72a6135eb7178d438c6f62165224b947fd6c3e8f992a9ff20bf61e250452482f699e4bce17586904cc6c355d6b35d168bee27ccdcee4d94ce28ddce5a2da49502bb0d94b563602c7c1ea93f74e49423b335f0da2bf3c2fda0e2db3567b4be740944ce4920b853beca51141ddb491142f644b82b7efb412063ef6493da7de64d80354825c94c9bdc9d3d4f4b1633209a934f11ad117d2248118619185265d4dceb64f0af6d1dc65c63c4dce100a7de54ee5649dae97a43c4049533d417ca9f4c39496401649e74c4ea4ded2f74eb91488593d3680aba680e5b1c01aa17ce86f2878a9b0777aec52e39459a06255da663bae84527626309c9b02957547d0dfc5cd1b3b5bb3b57830c17b2f8661b7091ce52096c0b8bcd83306dd3b942c8bcebd3a321a7771f5326d0502364bdf6d149d94f9b83eb62181fcd310c2e40e9d783d01335b1d7211f4948c8133278feb8e6f50798e965b07f29d5229e9c8fca627ed528167e9676e7293545888e61fcac94f9cc872227370295ccb144779024c35d7d7b1c718c6dbac34c8158585b25a9c44b7475131edb7522104a7b896626511b12ca9866b2e54a36eb304662ec97407944e2eaa1ec0bb7026c8ddfcd59523eddeda3cbf30353fac5ec0d6acc7be3e261868d828f697f4cbebad048924aac2cd3b94cf78669d91137c29c6e077dc494c5a22000765ace7e41d35a2df4d69d8e037ace917e2012cd79bd534e70c8d9e16f5a1419a55ed7f1a074cc5d0658e64055d3e3f617f4b67c92c2b88187506ff097b095270c45dba654b0aad3552b8169189fdfbc8ae2b1f1850e8c683cbe7ced2d6123047822c08c2fee6fef13648ee9efe60d1611a25bbe600968b8620cfc74f6ec1e7cd947e389d5b18b8cc54c3b0776fe9c5a7bb61c78a625043444a0240211661d48ad65ef158905862510d1bf13b1881a42ac815b78d4a97ac50cb99ca20752a9f11e0424d03ec9236551fb2a10de4e4b3aecd4ae68c5e923d1ad1d31713e4ca80affcb004544681fbe7fcef6f9286b69a82911dd0c37b69403d14382a61065da189c402e81fb3af38fa6a4ddb028876bd3a66f3ca0d82acaca1d73514503c10342e19d0273544106621729f0b9544210217b4cf7cd7f5c1eb010c97386dd33b31981846830d2539497f8640e903ce707f51d31adc4304dd5810e22d8182698508be516f150718adfcac0f24312d8e0f6d8cffef0a7918596a642991302ec68451ba0d23cc8cbfaf4580263efcfc2e85accb1dd28a39d2d9df2bf8636f820d9d401f4de1219b0a81bbdb7382ec19b3178cb7fca5dd8ae22f6da9de0a149e34951002a5315705e9b5b357b8daa6b2f2dfbdd8593eb7f0678c26c85a9d07bbeda094acdc669af21799807d1ae25fac207e5717a88f6f209a7dd59d19d8d3bdf5eda1117819c0bf0d2666f26441b0f7f51d320b56f78f9b92d10227210a48602463381ed4ee271077395ac02a793218cb1fd01478a59f10b437a8de5879bad5aa0084843828b221d4cde57f903b021a456005e8ef5c83a4e6140caec83fde50ffea8eb10925f29221a791363b6297ef63d1c1ee737ed5dcd9015c0aec806859dcbc501a265ad08f01ecc23918223204021cdf5992e012163a51bf0b73373a1fe1da56265c09c0610504ff68479e783f33e0adb0bc6cd0c0be19075814773147333fd56559ab92d1251e339a244544127df935a578c473e01946a827e0ea37cd9640a50e225a3806524a9d028465d6f139bdffaeb1456b1aabdef3ddec507688b7537097a5c701c6aa352aeb36cab13331b0b764f2bb1f764f84ec9b62244f5b288e47ce1f6a90c89c148afc0b1e24537cb280829c1d85e36c24328fc2ed77ddeeb135ca14d51bcfdf1eb3d64826a69f172a061b35cdf35138e1e6b590839f36338b5a2e32b2cd9dd0e1e2b8fa08845ec0c09a74cf3490c718592dded2e869758bac600ae95a1ac7e1f663735aba147f44770ba267db5fb479ae995b78852c221fbba899e8c1cf84f6c11d701ae2e51c1e910ac545dafdc437cc91176f78c2afe755bc2eb84b5ae467dae1b3a6952291d126cf1b8367d4be9fbbcfb8808fae8c69f3f9671ac3c6f01ee30da5a26210ccdd17a3fbce38122be99ca1aea8f9e8f949cf83c208a6c3369d28fecc81f71591082e49d7b22cd2a12492da3cf6875bfadd4e912230d99562105de2979ee57e25c76433f5e6528ec0498bf7df75b27d27bb5143c73ba2a68584d99d589dfd97e5cbfe7bbe2e980fff70642cb99300fe354fde898e944917bf133dfb35447832d2ad5189abdd1f5b73cc9c28b34f93e1be013594980e7f33625f9fe5afb8279e3593ccade934394888ccc205ae4c235c0d7fb5c65b7b41b61bc7bed3868066f18918da008a540064a1ac6000d0d63007c2dd87079e1aadf1785f402a5c29af996220c96d3e8132eeee6d1a0b49d0e975987f8bd11c2189089dfdf978ed3d5e817776d9117189979ce6c390727ca2609d5b11dad21f887542d50bd9efba07948bbaa7681a5478a0ae392f751fcfa0cf604cd340115ff28de180bdad287d02668e1ef9799c57fdea7cbe366a400747625cd9a0593e142932cad2511424b286de9791b1b5414121e3797e36f784eceb434744e481c0ea00a18cef858cc95b8103ebb9ccca654818e0c67d2ea5f98e87bbae3f74274208e82c5b01c5385ee5c4848914c05e608658d7f422ab18baec8ebde47c1d3ef044f41af8ac83021a9ffa1abfba74ddafcc4d0415b859a24650f2f14b7c161f8065b13e1c6242a04c8307d64ab0c902946bb16fc59fa6789622ce128d6d8452169a1ef5b95bf8f9d61e4cb12e4d5cb9576696153ebba717fec0cf24e8d3f8866533a350e12a390e777e3cc012627540fb23d9706f7c1353fc3472eb023b099ad8bc4995ea20d87426c359a3e29802b8ad0bc4da58d4c674a256dab3cb58cfb5d4c7b6b2d302ee28d0b253c682205177d45d41b73f9a6d652bc4e83f7d90db9c4b0d6eb4775c1d491d354b3b3a77eee0b7c0ccc6fa8b7a485c9f3e927e28cd2ab538414d13ab679b759cbae727e5ca6d4165bca8f39a3ad8ffb40f05e4a63e35c04783da2a403bfba33e11cbd619fe27668a1a6be650f8a952c3fa405c18d07c4154f706c8c9d2bb84702e254f731f420e08379784c742e5629294bd0c6619a65c69a54d44dfb1f11220e784219e81a372b192e6bf58706213ce9151aedf5e103d0885cf3e94b3241cbc11d3f15b711a91081e586d0cdcb86e8cb8b1c2ba1b4ca99a8626d955e325c27160ec2b1d1fffce5d9d4337ed570a1f4dad3097a61a324d32933fba9f2caa504a05a2272e2f7d66e48c97f6772149966cb3494472bddf860e54cc8c61ce347e6ec1859c8999a09c5c3984977658505d674ac35aa9ca8ed83e9e291bb28666c6a5cd1d2fc502b9503a6384fbe345781dab1144d9e7d21255802d4603e01f81d09cf1aaaf5afde646f80d7eedd5f24c15a82f2d7b30edd8089639703ae746dafc3bc074204a26086d3682440ec6f74a5d6d05c4e7141a3bd0795f531a00b3b5f705a9c0d067f6438e107221f80e5d7fbcb72ce48fdfc387b35c48b486ba07ce37a9e36db342f76f0d67a36f948c86c83137986eea784476e677c372ab607ddb03b878030eb2cfd015406b1b675c7c1cc38f079f41c7e1ff3379dc4f99437ccdc3e753e48f55a761279738c311ee4b5287385bc1bd1da51ccf23291190362aa486568f6e5a53eac627d4a3dc8372e938fc4715db18c163b84afce7fcbf5e5c55502de2e4fa97a87068d682c0b5c339e093c9fc020f7070e2978e79b7ffa00770cfb1ac6df9c2686b6ba1de16fba12af019a96b21f9a2e640362ba32dcab1e1182e83afb52209401b0080b5dd03bcf9150090de6e580f141f1fdfd98d687b13dc52b27239166bbccb036ef896b4a300ef45ad05b9613ef0b5477e484343be3e9e54c1d742d88515fc0866ede66ed65d5ac0f247d68224ab3384d2e15ce43f1379dc6907cd1ea76a22a6027be8bdb94821bd81525913653f6be84189d1691eef345a0ed08fbb4128354d3507542e0d4b80f16c05b300e32dfaff309158bc5ec0a54f20f8287a586bc1d7c6fbfa432b3565f8081c51144e945c93366002136021b94345441b8e2099a255cb24980149976c8cb69faa8eecc679062524918cf0b709b7f33e5ce34a86c51157eee4a37c49f86503599efa59043842281e0d9c8ac479b9a9ed03867afba394c808407b47e33793c1e123c9283b88357f58112d067267aec8ccba4c0d5a34ce5a18282d765f3c9b82e796d414281d8be8ff12c510dc320432dc26ea6ed502c7adf5b6097c2ce2bef843a81076908b61f296f247fa4ae1c7df92f4cbe2c5f5ce2e0d25e27ce16819cba791059f9e9aa4540ff2415a7726bd57549bedc13d1c23f4f0d3e339a0e490eb0a3df0582607ef252249144209d5c74a80b62dcbe980bd7f406a4fe253c9777c033787650ccd124a1b377d6db2aec6f623ad2c7b3e53bccc1a64bdc1c302b44b8020c5f5a46a4f2c06a121faa862c8e8fd4b1d2012b46d8c25c33f90fbb1f7f1e6ab937f0aafc82cf24590c7605b2270d76e700efa75e9696419b427c683f693392845a2a1703e1bec067c7b58e554bc4a8e10f676e18c9a46d44bd39e14f6a803e380232c87a4f86ec85c610f709402e741929abc184d7974a2d5ada1d85bf0c626186a2f582ffa42994a26c4e532ca837ae080c777da59e538946ca609282c2a31cd60f939fde1e3dc6721876ed9f22e3ebfb9d94edce993f57912ad640e680828e5105fd222f4e0d9de6625522e83bad10c32a5993c18c35240c487252b4c19f309b0744449b39ae2bce9cb7fc81064168df09a09e40fec0207c23c58254943195dfbbcf4ba35d3fa6c8612c67327e498031c006487666fdb48cc9cd4f4fe613a7b0bb7e39e545d6285103a3fe2dcf21fe62495f52d8bcf1558020d6943aa49b3daafcb0c11d6edc95814a554419c0e05e0274ac5380bc3f8814ddb5bee1a691043d6ef565c1350002f404029d6a7e3fa5f50907426580dd98d9be03564c753a0a9753d9b52760eb197f0a43590401cdabab486e841543f83c208d2e69b22fdc756fd4a588f7289a3e442d8fd0765dbcdabca44d3d71a0d21085e70ed6818c54a758ff4bf25e219387d41a123ed9c0d95b198a6cb131ce16e27b58c3249a446c341b924102f764e9187c7aaae8b9206c04daf59565758e45319b8b7d5d0b78120c494aa0f397313c00408a588dc5a976e16345bd247b3d8180e3f38aa9c5c70f87eb7630f78b376ae4f96bf4e861e795986245ddc729271a5d71c20e041367729c3039a3fcb33fb3a3260be3c431d147023c4e5f737f9ad0c74732407eda23434ab0c38e498604d2e1541654110bcd95ded40ed57cca9d5b814ca68d331f38b2986a72acbead8193366a1e7a1ddce6b92b980ddf94221c5f8921722b05a6053dcfad10a00368d3223ee9704cdb0d3ce4a2ff58d26503d02ac5cf55af33072afd4207d7907dba4cfc1fb52a73bb100d9b7b9654be6f915c4cce58c252bcca7695424bf20d29f5c874230bba45bf30fd053b566234138427a9cb0079039f61424127dcba23b0ad82dc63fe9ba625ad09358665ddc60de9b259e75c3968ea1786191828f2d2ed5163e54fdcd22d08c131b1dd8981da08000f64e9d8bd58a995796ba3c0aa567a19b150207b72cbd56db9ebb5663abbc4452210456efd894f97700bacd8e7275603f02832972da2842399c2dbfe47f27d60b528c8275be88c419db292a19005880bf550cd1e59bad460f3904f500aa27ef405f56309e0d988e580bfa8f14cc1c68e5939b2087851549d390d483a66e7b863bc881e510ec287f9bebd705ad1078809e091df21069235135c12a9af70e24f7d67332c60cd8c006e3ced8606223936409fd3f2038f59eec4c08d50171c99f7aaf187c5465fc974defb86ee193ff550755061683908d52f8f4f598131bc83832f0baac297fdfcc61b1e9b362d2cac30aadaac1027686095c36c9238d52c6f4ab735b9729e919037f78ff89734b12fc06b04119d8313e9c8e053420aad317e6363404c0d5560d0e52407920e98273a13411bbccdad803435a300f2f9d963ed198e1ab74e1ffbebbedb53fd6956c5ede32b68749abf06ff82abece82f35f0c0388df614d015f60cf08c9c698c0241a8be4728d482abc998b06e5c0654be2056b0e258d7012df36d720725a164656cc12837f44583d51847a19358e55b1a03869d8b1bfc30daa8ac94b41a6d702638fbf8a994f7a7efc586caa885dc1ebc853135fca8940d0f7890b644ab53db0d41684a64d0eab41a6f20b5638fa06ed3a44c9b600205e7405ce8ad4fc2b5f59a884a94bf6247a5909aec9985fb487461c90ea2670cc0edcd52388d7ba53463ff11448026f7b48a42ad6aa5e554147f0e3313b822e33772ea800de7a7ac82882943995004141493056b0012ad3eb3f13d6f9abad475652122ab4580f44ae41c994bb2ce81841e68684dd6b3005c5fe65d8daa6380cda32c63ed7863410ce2dc2fdb45eb546052585188214453b3f84460cc2cd8948b1cae56ea480e84a1768ef20dc5712cd7cb53dcf4bbc27baa699ec57123a72536c15878ede8abf4a0c42469b6654321eac8a0b7dc31e5712eff800939fc282145bd855e2e54a02781231c00659cf22df20bd23d2e2a7a03baa629f96a4640fe29c5f51672c8995d189741b1023dd5a79bf64816b9684f2caba55c1a0679475fc23c874b224e8b46fb2cf77fc9c2440f839f22e4b4219b623a2ae859f1562ca46a4078d13693e87ebc743d1956fabc7341c10b914938c2d10704d044c1ec84f28ca8426085a2d12d12e484017352e6a6ae8ff55b30c3e0d9631fedf2688f0a7ef711b8424a9834195702a104313e449e420b830d909904d3809c90c107a340a990c90792c0b190b904cae9ccfebde53239f060b1c619892910f411822347a81d438499221dc15321bf294fed7ae064aa2439e23734ae74390931ea5081184a2c73894f9c13ac65fd149c4ecfc418c1891b7a0be1e997cfd888cc16c978853545f2e1320f91391b33443a3a2b1af085aa322ea321f2a7eb788a2bc52942e994d926ef283b3cf371d998768c2efd3dbec0e0c5c77964690035c463eb605b511802d92671009d3c59135ef3566fc0b8547cb11486e94150ae8478b867f0212a323b91262ca4c78449e3c4ebc161fc9e5c3e9d70324caa4c07889ca358444e0af2fd1a721926a3f030588964fc8ad89a42738fb106b8fdb36e66c88ff16af5b16f9d1474d9d2188ed4d00ef01ce01c87ce51497a3e7a7673b620c0540f11a3a0b31b42a6432432c5c9b31bd65885528835a559c533b58a93db435611b10844edc73e7451ae5bb737dd9c9e0fefd6d6057b2da5be809d5a7175d55676a9a50f96ead733fc13410cb19c1ebec6419762de92696e263eda3e90803a7d41e83883002695a600cf802ddf277e216d7bd565a04a5696d4dce36add6aaf46738538af1b86027781a891f64cadb04b6fa6c06424fa10bff4de4c761a73fdc2818895a850e94bf81a15f569f24852a1b997933bd0a3b4b43fac0fa481f2dcf6253f0e1bea663077f5b45f84df4f00a17dcfa8e8665eb451a3b792f4035d5f6b6ed598e623023a05873052253d70c11f85a96ad991df7ba2251ce453b114a75de9d1a50a9eba524bab5fb56ba449479a8916947178c072852bc88d49fa2cbc08627f6864c95d908e0d60d0dc7d3de45d03b5f2a01f4d5a4fe167e2abbe2ad1ab1e200dff3cea97bbe6f2e34d27d832da954b120d663e7ed1f0ca66206080d0b9e843611c2a987b8cb2fc5e961e36dc75c96606482747626c0e86871e711d871ffd87c764d3e90f0ef202e79ac30aa39e0c25d770710da53d08d6d9b01f58462ba906371bf2e6c46946ff0fe9fb1a46f92d4e49eeab322255815aa8b0d439a63b9abd478e19d26a4a5b69a55a4c5ff56bfd5a304c2d5b4aa131f9298a24cc0a90165c42f3dbddfba6257a0f950df9ceba2c2aeb8756f2c552d12550727980af2a43daae27d2fa25853c272ba05adb8e1e6b0802e3a8bd95e0d3624ee08c858f44e26c465d1a2177142176bfbef745ba571c0b5b9e96ae6be058066d2cbaaf2c24d9b4e48394d709677feabc2fac60762a5abc1559d46326111a440aba7af6b5828d3c51e872288303a0c58214c6ae2da920bfe0c527f5a7967b16c679d5fbf17a4032ebb55cb3f866a3519258d885a633c75ba0821827632e5cc9bb26cbae8a6c82bfad6755e517985073317e9b4b430344e7dc48b34134620b3befecb2f1a5a230efd4dd29a90c6fce4d02f24ee8a87d5166f01fcf1de1b4481065fd5f0575b021465957a554947b8d3099799751137b745bf7ba42b2fa2d92149b438bd0e625c4096538ba05e86176b739482eb17afc09016bae0fabb16001ef173411048f15dedfe3aeaebc8ee6ae3a31ec66094f9d68fdaeab869e8e0e5a16f81e97abd95c210ff45d44860d9ca73d205b44a3993739b6a09a7e53d9328b740602df0dbf9e612d3802f286141ba410e80f5e403e584533e872b3c519ba817412198400abc141828a3e697d9770fa0212e56aebce424c3a411971c71c9d71619e2a1d6f8434fdcb8ad0b2e46220497cd7aad5153696d80e44d917b68e2e8cecf87d05b91072986b099477678c9cd3bdae933746bec826095fa3df409aa1efa2dc744f82a145ac36d6ef2337c0bbd290c9a8e370574d7504895ea9153874dc5083161fe9e3e1f6d8b8e2aa757d22be89af5364354cbdbff84aa7dceb448fd058dd5c64494db58f4878b8aef105c3425f341f2e340a192d033c0065cc0fc42c695b815198eb2dc6ffea77254a0e19c342194f79f25deff3fb7c2f7e5e10400bc90604e8f2f9cb33bc5864bdcc412b0f1f0c0de1ec24ca1234e0155156e5b33ba8465f375fb990d00708b7ff6986a28f0805338927ed8c520711cf935ef653b539cae4d3c792a39fc59ebac508355d2caeaf3129dd447d0998642da8cc94901b016c2dbac8d4f067f12632fab5c9e820e01cc813785da82dc3c441d9036c1d4e3fa01131a5c2d5789fa2f6ce4c90b923fe4d7ac7ce97108caed728b6387527b9e6a5699bb06a1c66eef972e7e97d6080d0c1ef6b5071c733b44e868b628f42d0def7ab34387c173122020ed228c189948a07005dc77d047d55d54abf174c4ea610bbb3916fd1d9a1d3cd72ad88196de7314c58e9b6a69ba02e109fb3f27d010ab13d0edcfb1c51c76c1ab60caffc8be9ad02b08fd80f12f2b5074a0169484ef39022880bca655dfd7359ef68ecb2ee73a46c3483ccaf94579f8575a39868588139a94d3ad30b17f423985958a41aebe8496ef03027bee2f19fa94fd65cda7d2212e408bc0afe1a7095bb850931f0201f9c1a6402d48324cc526aede5247a19f8f4b5ccd8289ccb0e70d1459f68deda3498b1e3c6e6e95343650835c6dafcc1d988187255a3ef8fe69c8434a648426375c7adec2a3d230a7b7e3c5384d7dba7b7299990e46694edf4c1a2d265445829985f16c78e93f27aa321523967021296373c81564ca5f3153b2b29d96e7b3d2a689fffdd58a9419e1de11e8179a037189ed7c8817005a5cd68643eb5f3d3b485c555ad19bf4421e6bafde1017aec14a6958bf597e46b68c51bf7a7fb730391a8c730931a15704f7279d115f5ba137f8a185626ef1134ab07d6c15fa782d1448c643e65b36535e8c4e5e0e455e3e1e2fc8192f11ba59b0169fd1ef30a0d9a71497051b82ae4faf9a2985dde3ab41412fac33b7d3de76745d87a50e762cafb2c1729117dce2143382d88e19f03d9c6324806139effe487556f21ad022013a7920d49ad677d867e383e1d89662bc0d9382ce416e0465f3ef012d653667c9dc27ea52f8fc71a70b57fa078fe28d67f54e0f053a73cc6b6b980912aa0db1511b5666dbc691df377d82efa73169aedfd808b126db21129be1b57f9add48d058a35814a6c4a24ba285f7b4b23b0488cb5bf75764e86f87a9a2e1a0a2543ff5b2dd0962129767a4178418677ea871a107d5680cd1c86b5294d477ba51faf2f0970c74d6b33f40ad7b8252bf63b2734613fa49687d468bc140694c257504a2297d8529234307af22297d875f1d92aae238c898066bfd7e5363b7ac58500ee35625e5d82c392d47f50606f02f58ac5e19d57811436373d5edd9090cacb2cac0a8ed6228eb322a0263b58181e831a510eed380e6c8a192c132d63386385bed725fdc3156ee2338c7b6e8225059effee196617d95d194019661dec72182e062ffa9034ef2ed169f1082d38dc75fdf187175ebf86e78e50f4a697b40f17177e1749c080fbd17f82bc1c4dd26d15d40040010fdb9225824a322969882e9dfc0723c4626f3010e0b63954c8f290c70bf8a6f8a742590492fc640d2491700ea62f91e45ed871330199e7c0f6d8b42891a1d4a796366e216d33d2d857e7e1b5079763c880767f1a9d3052b722bae5440960c57f9b408151a381ecca375843a78502addf63e74438811210ea8b7f23b88bce415ccf0709700247c571d858f5ded328b40593364251adf29a646d4c5ff41e91b71123243917868b9c2d0be55bcd8566058b71f02302036247ebab5c007a480502c66bcb101807e672ae283e282978f82f370f0a62000d91cc30e06c9826aff63deb2a35271141e92e36230ba50b2e78f9035ed765ffb3f6cca235efc9da0ec95270c0e2dfe950eaa72912f32e9c5e0c07b25fa11ca21ba58387cc3d0f2ce8d2d486c9017259093adc1986584763094d6dbe66bb22cb53ed257af0bbc6e3859702b6365cbcf5d2650e00509d8dcd7138276e3414f48a0e013b0828449b32e1cd8985f1254200425507a4879202c400942824afde6033ffae05a1e88b91d815f59dfe0e639d980b41c19f658c2018a4382c20a790b44de48a55dee5448d08aaa52c902db31a418ac460b5a6f8d54035739940466b51b4a548d3abd36093ee91067ba0f3326724047ecfa0c5e641be7b7ffad7e48600ce055f9910453a4eff0ffd62d2ce3815ac67029842c6e12845c9b184584242b7a3f8b3409c4baa1ecd1d659f53e14204a6032db711df90f80a663fd01f26ab59656028c25c232d0066996a044988dc34fcedf1bd0a164d1f68b3f7b79b262dfa07cad5c2c81e49d5d1d8025e0ae7d0c5b39dfd251fa89448f5982d7e911104b30b73319b0606d359c1e3d2e0171d9509a26ab2f819949d760603b624a076043dda531818db5be4cb4a84e263281ac010d01cc889fb7e43b6a37f1de78624d221304ba1845cbaf39abe55c7ed96582cb4941209e86e69f4688043f35c0f38edc4c10994f224e7ef5206eab0384aeea58561e510a45fceb3ce8d9a840fe33812b7dbe0b7e970071ef50736d76a4043fc38e6b6c5cae127c7a1d064b97bd128c55f88152a761090474ce6f08f02e97515488fe2d8ed21d8fb96308feee47b65dad7580ef7e83431c2ff01ec379a3f0a1cb7a67943be8f4ea65de63a74df361b633762089dd01b0e8712e2c4c4a575132260c8a383f82c1c488b2ee48b2f1b8ed3de6b7f50f839156da32c1e85425426a3af6ca62dcff23b4143b1237149460e84e6ac09a568adfd2a811de718fe44071712879ea474ccf30208ee2cf013e9d6d0053de0350803f80cdf3071048337c009cc9c9aa8404a8e8920ed290cf9b2fb91e9a92f501862ab3fa5dffbf094dae6ce2774faf5a288b2156b58af665400ca837c197273049ddbe2659565e3b5fe2b90c1ae9dde7ee86fb7d1f0939ddb0a9e6b8111bae3bc0e3895e647dbb9788bd699a9167d064fe55e8ce31f89feffbbdfb1f38827d9648df4ea2f2de8e1940beec0fe94583964012e3bbe6f6a2c3e3841d4a950ed02c77c71ff8c847808ed8c27de65c082367259630a32cfbfe9dc2bc061b46cd3b58e7d2cd311f8b7b0b58c3e2d4a04693e753bbee7b280c8dc7ea7f300dd5e89f460278d3a811d630a95e23205f3f19a708d7eb6f2b43fc0a4513f54ba920be8299e481ad762fdef0f1126192fc663439981bf2be47f7354b4feca29b05b3e9435516d36989096836e17c311d315a9d3a313457fa3ed4cba2e2b00c462df89f3693f521fcd636aba17334a6ef979682353d81855bb62fe6da8012aef579d29a2c8a2b1097251e55d0936be32e903e51315f8578f5aab746cefdabda3c4794a78c7083489a5d42c1fe4b46225c520ae1b0488d069b25fc955e43cbb8cc5befcde55bfac8055ce486255cd82d0fe202c8d530f739fb17398a43508afcacc33e8b1c122a2b8138b66d8b5cac4f074c98f1526426a267c7899a7cc8f361d062039b13a90a893746ed27f7fe07189f063334b65665f21bc69bb7089aa0d9a83507688895b390a2db15a00f5882c23240e3c3937aa7ec0b117501760a4a46114ae7cbb1c0b060c591ff58ec358602a7b971f9e8fe3cf2ae1d6b7c82a061866c5f16a1348a1ab64c57cdb1e4ffec3a16335a5a495114c65475a7e2985e019c10a4f1d114dd40264b311f711152c49f5ddb790f9d575d0625537851d133a14ae2983646c7432d472d888adad161e9ef1219b45056d44b5a2ab051c09e1dc49d6186bca1dde4c6b9f38d8c0f1b5664f69d57295aa4d38005874ff6a0d8367ce30e4ac6f3af5b05fd9ef065b837e4456bb163c3eff2a695dd77e3530668fa638e1bf41d7d348801204840dcea88cb96ace4080eb4a00fd66be1ea2776dd6d0867afa2b74fef4313cc407e95cab6bd5abd949740dc1e0905d618c4feb169b52d172cf8c8f23be304dc2a4d603957b3e77f9ca8a090fa21085780bda0b86f1b185a899774aa0088fa42d5a5046344ec07b6006ce3b639e16c91ca498bdb3845b288c260c593aef0e043b148ec88a08a6c288cc8a3f8e81e3bc03b5948c3b1ae07555a6a775f3733f0562636a21049666a93474fc3029dd95221e0dcb08f29f003fd563225d1586c061f6dc50fefcac62071c11dd9beb91c08190b4436432e94e1932042314df657d0ac80d0608a14a2cbf0901e6251837555a2b1237ebd7076f62782801d9394b9ebe1eb92468dc3d6914129d62a4ef3f23df90dc69ee06a58ea8d589c66fba6a30af04c9a0551a7d6187ec7613ad51cfc962f382bf8ccdd312c10597812a9547603eef50eeefb29207f5459739b28c70acb9b91b3cd428d0e777a8b30b08da53543e247a60cb6c30990a174bd46d229130dba76e70a4db44658492b3e20a25695f273e76c386deb1c509abadcc46815864603bc3c46cd6d679ca122e66ebb6a799e62a91e8d08dd7638502071b1e32064bd951a474408b212edf231ffc1abcd3c904db9571a44a10a10a0e176dc9b2988cf26f7f9e0de1d62a9921dac7b96fc112ff1d40c903b417712d6e89f708252fa0abb2dce7a38bbdc934c6fc89f4b9fb9b6702e2eb7e55e08b1fcde21a1aebcb6c50fe29a6b5d37fac2ec6c525e9b28f60c8ca78f10b4ef31b38ddf7953df13e9d5365c6734fd37ea595cdfb1dfaaa53a6070b2119a7fb903f3b5ca63f495ff84104ba64f24bbaa2a86082d4033af5eb68690f7698172b5397df33d8d800930ac55ced19472f33fe7471a91cdb17fc913b0aadad2a129c35177c9f6b03d7e84251c5d720392936431f124abe1f6f43f86265db43e78eb1d463d8bbb811aa625ca6f0e8c2220db07cc1dfae3fbcfaa43b3e9426172066a178d0162dc67852833287405a51e6764c65340e5f5915beec8b27fbf5021683d4eb939a6a538f640d05e7554d975ef5839f4aad7bf9b13175ea73018041d8f34e2c9e1c5f4eb4e1eb051cc050521b333eeef7055e60045c073404ed7101dbfa092df823e8c8318e0d60e10cf63ed5b71657243cfdcdbc3375cfc0da01f1518fc62adbd4bffeb72537f169587a8aeedc39172b6a2ee402e1d4bacaafadf5d8bf8ff6b075e94ba0a0b733b5e4b65cfacec4d78128015ebaf5c075fedc111b6fcdd52303dbfcd9929bc615dc2f6a4df7899e83918bfe2ea77a665e5bc2b1c4536508d6cb3fcab1764a8d9687a854f867f7035182bbf590906f3254e3bc160b7c46de98e67ea5b78138951a06b3652293f39e5ac3fc720241acde543db4f004c90a702e9f384e5a7210ce1e9a036f90ef807f103982a1b8905159f6766c5da6224471e02d24e2c66a00ab2e0c631020ead141520b55593c1b3da4dc1ce15ea9dd360feea6ce63e9e1b97a0f351dddf387c7c67538eaa9b32dde174622ca7fabb9ad6d628ea0b08d42b7d2e64fc29ac8c2b1b0e7bc419582bd49b1d993d4e43f558ca3b221423ba7c06ed265d641db47ab29441f98ea66fc913ba9f4910fce417d4ecf2f45aa4e874c482f8b375699fbf699baa5bb2c50415d14095edb00fdf88972baef5643e22e989ea8d7c3c7b5080ac15f221340e68d46ae1875f5eadaa3cbb552ec24470d4f68c83daad9d03cb37ba3239a32ca7496f9ae19c8a88d2fb540015e6e67f967e456efc7fc3b02efe5b46b9a725bfd2a76783d5065aa06c1aa8a0d3de2ed7df55422da419b5e00233d3831f4f06a2122ea4698a7a128023ca5a62353bcd6da46ac7a5020bbf24a4b7e8d5dca25c862b28db2f40995829fb0e0e229b34e936844e72f15ea952a7830d859bde6b591e03feaee3320255d38c52d55c953f80b89d4aade25bf5232dfca1ceb764fba2432f8eccbcb221bf46344b2b96272f565ba0abd702d4e1ea48fd6355c211c21aabcb48ab3fb70700ee9523cfd8c30a4b88010876b366e134e77b94e39e866f6b708d469569083a95a834e29a593a00be99cf1e289b77c24949580fd58e4a0f808e85c7802d7687c483216e8124a6385cff3613b8b9b91f71f650b444f124e943524a6e966c81aa08e08a03be443d21ba36264741f537175f937c6b64eb30ec117db960e03cd0f1f0c7af901870c42d90c45482eb4766023737f7a3c41e8a96285e6c6b61205471e01f6175160349f847daff2b1fae8b12401649857013d7c5daf6f61a181c7036b5b2581ed80063a08c5d8734ff1d371e6b40c6db2062aaf635d793c0e395ba4ccd7d10eec71a0e92960dbc11dd92fce03ef371f1ba358ff3121335351ee20048513e0b342b2b46fc55d9c6b2a41bcd6f4507e66eb0c73ac09de7b86a3a81f1ab2bc1e16eb35eb24857342a10c913b32dcbeba069e204b7e50a902652011c35c21d172578de7af1a6883ec82e7c7c1dcdc3f50ff1e2bfa0309db345274211e1d35a541615d79a938b667dd1a12d4547aff56b08d3b38aa93f8d1ec4661732315ad99dbd6d37b08f3b386e548edc8408c7362bf4bf65517e037df26a6dbb7690af36a321968fe801f49c2e7a19e167edfa45d0748495a0d8833618d10d28c6a614a0d9b35f9ef9642e7fa5176b5b2ae6b04c9a48665baff7c0a4af4ea721234db04693406eb410fee88a2e8c241da043f5c303c2767582d477a8029680d3c3283aa0ba344a38eeff67ab83ff42fffaa7acd89d87970b82fcb6cd06ec4125e48801e8c193e7e42aa9ec792562965a3aa3edcb8841d11d332fdfdfb54ceb2e2088f51806d26bf06c1cf8f96bd87a559af931ba1e57edd480f42da2e9fff4f4cea6ebef3989a3ec115eb68ddd2edf98505dcd2cd9f1e52efd15a528c8758b632d2427718290f25f6a283bbbb9b037d3cdb9a91eaffbab006d15f338a5bb7346d087c965b1532248f34e06fe1d081f877272b5b055b45841ec83d0b3644b26140c732c6b5de6f29a5ca55440b35e465e85fae89b127f01677eb53ec63959d7305e31ed51484256d2e02b31424a5a5b5b55170d13e250a71957afc833d6650093aca634bba530af85a569868147570f97f5027cab6c89c3edd7a88acc1557aad8fdfe808aa7034d6be87fdef5bf5978e00d04cb84c8367c0605a8cba6298946b2d97a6f9ec19dbc2b3c37322fe4b4fcae7d04aaef3101bf14851c38db221a7b6a467c7d200d87eb83cad9ff8fe9772596977b26fc6ee300310ab901d3f62fcb0745e6e069af1c0e5a2f86780fd96f5e57ef64df3f17fa105cfb45de6b11623a387cc610df5b6773a019fd8f3a652e50e61f8242a847e813daff838c37621f9a3e0a2f5372cb1a4545fa3fdbdfcf9560b9de8642e0553d7dfeb44cf79e7f596bff867043267631ed66b6729e920ac103dd4b488ba1777636ecf73af0e3343baeec23bb22d8d3c81b6c77c8f169608eecb4ab55c3d1500f09268ef421e2ef8f6f455bc652f8d5ac97f92703dc22edbf38c5a93c9828c41e526cc456fd3d8136f1e289e26ed92e0009dba145620935f112b6acd29b0eaeaee1bc0b547a7a1d5f7aba686e66446d5ee4d006110e8e285eb081b2098cbad284bd8916787158a07b2a1ab5438e3943b000e9429a5f71564e070280286e7ccab10a9dc3048de7959b7b971ac30c62cd2869522efeffb0457fe2ecb4ffdceddd5e60583a23043b1d1125594492a19d6a4b2a853fa64b1c516ceb95fd0a3e512fbf899e783f41c3851243807e0b70861c5b0909acde695cfd970c889230923e9ebb2ec1de06a78b9eafcb499bd1af107d6b8b4046f9e2f6f0ef7c51a5f6ab16abf98cb906e595ec53009264fa54813458f841ed7010d028f6c488cca278d26aa594b6ee3ace126815a5368fbe86faa7de26b1130439c06d47bd66769e7e0092d601f6094f02a5ffeab5042e461a57cc594258b9000afd30e5981223a32feabdd000b89dab64f2c030959a601b73813237cdd36ca8d163d56d2270a4e050918ef25ad4b75f47379978474dac0247907ee76ca138a6dc6c10f58a34c0714cdfdd98629691b395f9054e7bdc5642c4af6ce313e89d071c3f77b8f5646f3026cea7e8c010e7fdc1b58d7cc8abf4adbac6a68bd0a3b83c4f1d36459abf184f3aa0c94ac30cfa9df676917d81fb4cd079869bad12a4680088a5c789b3bf563930070f62bb3fdead54ef22687a75a585043348997ee555b41b922695c89db2b912accd8c45ec04d397f729a76fb3333d4dc09e876a030f8fc60cf5067ad2133ab219f95ca89bf2ade297504bb16a5c4f52ea0b4e38162a82a16e0a481231894ea6ad1619b3032b32a89c3c73314520e8a58013a09c3b8d210a1e2f2af8580b6ae5fb1903ec39e2e26f749f399d224b5ef17cb883802be4ac37a20008d24ff0471b3a6d17fcd0a490509b1c037fb0d84b1a844325b0e53ced0ef24c9369a97f7141456d58d1c661fc9f74f3be40502fb81249f6f18b7c21d76dd54e8b68fb2307bb4c5424868c818f5cc888f76ff73cebd3f3715049f096ceb1b8c94e230650479888894e47fcc752248b4d329948c9fac1fb3633ffc9f79b2b61791283c24996b1d6f3c4934f473edfdd7caa7c116f0b72617dc3c2590de3569c8771bb834c9048b1c3392884573396bf37e9380a50d699f387cb186fc86ec218378bc24b206b5c620c8fafb160b83901443a43e3e900ed934ebed2bfcad8a8c95721ddd760f25751ceec0d07a7046901acd0cc23e16be08c3cab17fd81d7b92231b3040d631fdb034aa693346e82c1975fe87bfe857cf69660f09f5780d024bfccbbe8fd6ad79f97ae178818fe848ba9660fa909deb15dc0465c588285ab6ac9f1645f40ad62c29b1946764657d20c306422e7fb450ce2b61204e35991a4c4119c01b6661926fc1fff121099aa0a94f98a74221c85b8b39a2609a182fe509da93cdd08cdd8a279bf19b0f11253cadcc1dc96df6fb3f852446f2818a625b0bbb667502ff47eb0e070a7b9aa2022da80d9dbb24c77fc3db418e2ed265065fb84212bab1e297608e16811b76f2301cacf32ae77f322846733101bc15bd1e006b7705b32b2c44f82f3ae508558679321ff9bdeb2387630e8a17383c8872bc368c01e4f3500d2292650d687a229b381e29d93823ca70db1ef4e4c00e6385fc1383403f15787b0e32486f312971a9ed5842271621becad926716a539867a0d06a1c308bc567f09d7d48c35938fb4be68330e78f143b4c42926108f93000972e412f725cfd4a2abb84ba0e1e78f22a501242b9105a7c8ec4b6888ab62a92667e241f8c16c8309f2367c27e520f2e0f726a454b02586c445c036a0587a6bdc232aa74c3da4add889f90dff6c5603aad3868b564062d1a6035935b37a227379d5af73a85f4d87b7a3e9e87e21b8ab996f3aea578bdb6d7029f30129dccc869500016237945594b36e1420213caed9e16c304a8e91490954f1ed6f37111122bbed2df7967b4b9964920193088308ad089d6bbf722ef735fa46cacec6a1c08d4dc6de8dbbd5733fbf5d4ded5cae63b9e77434dbdbef6d93794de6379e0c27f3f94db3e174381baab19273e8a268a2b6a3d476de6b38c31bc7464a87a379fbdc6fdbe6d1fc863377a3b1ece4a0eaa3ee857a154ff1f416861273c0f6d5a6ebafbf7e044749495c81851f4fddd789daceb1f738a6b4180112cc947f0eebb58a42bb75dc5bcdf78a9d271fc554e8e5539d8bd56a86655e7ed2e4230df6422f167b9fe2b0d7b94813c41e9526693ad97694e723e7e3c8c79f2a3e12f9d87df4dccedd6c1ef41d4ed96021365244704a878558a07b0ef6c1f9ce03facf77de0270a3c0c8c8dd6c4020506d6de46e5ee482bebb9f879b053a9c0d055d9f3b634aae36b9ea1558cf4b2b1869864fb186aaa82c94218aa5f311e4f9b8e17cdcf4f9b84af71cef38dd660302ddf0928f38187a3e18e28b62af243735994af1948a89f9ac3e301f5527a5f3e2a34f7ce9743a38a652294a6df7c554fc3c07f4d974626781e8e962e7068b8f3ad11341f163e4e04f87e3d7cd1d79d015f9fab9dd57d7522e822f546561ba958be01421d9dd10e25681859cee45447e04e4f94f6784f3756a1e8ea7f3d5d2389b91e78c3c4a044d8d235265173bd773412357a4bb1c10ee55c4b1dbc21f824fedd7a61cf1dbe42318db84638403b6847abc3c5a1ea73bb51dec0dfb61231061b8bc638ccde1601c8ce3b22c93d168308cc3b88ccb3259c6dda37ff48f2e858defdf52705ce56a966d5b268361b5ab59b59bd532da866d20ae8b279006da4020d006aab7733977d3e168329de7783c1dcf06d3601a4c9369321a0d763d17eb5cce8d3131155de2174f9125a2624bfc5c05021eb051606714dd5b2693c96459ad99cc56b7acd64cab64b20dc3188655ac66321a8e9aa94b64ba557ca4655e975ae52e91b949e4b8385c546bad5acd6addb6dad56a37bb6da04ef3cd6518c771d937863db7e170300ec6715996c96834d806e3b0ac3599b6da06fade3a1dceb7c7f31dbbf5d66db56d6b4debeeda9b7dee6a5f33d92fe5945e3555a3c9c8172c1b053c78f0905ac3f84883355b8665b07f9d9806b3afc13459959aa66aaa4693b117eb4f73d96ebfe056a1a0a56829a6084030b1aaa3b02da82c936d994c269365b566325bddb25a331908ace72a95142d858a070f1e3c340087294e5af4c97abdb2f31b051d85a57f45717d9facf52d850581f52c958ff0b01e56c25e52d896d1681ec3300da6d9b64ca6d62cc3b0ac66999a6d59d56099c582d5df340dd36c5a26b365364dc3b05ab32ccb6acdb0ac6a35dbb29ac95a0a0f6852d3bca6d9be7e66d368aaa666329a86615956bb9aa99cc66dd8a6651a48d369323825c35e6e32cd0bb9b1919c0d4ec9b066c39132e3641a4d268361d66638cb325806c3e4d7ee7e9ebb9eafdbed70be3a0873b40775b44dc31eee3b2fb7f77ce4575941b54eadbb9f07719b6b208d03e28040dae76e9e4ee5546d73b9d7dced6be66af5ab3cad98463a05167e9d9adc246623a5fe865330cd6b99ec378c935fad2cab97b6b9dc5b8da5d94c9d9ac5acb55cb6b9683c5a8a46816d14f098579471820a6578f294e757086778f4ad469b1e603d7a5932d4e22d2e9a8ff25145b1f8a8b29c7c544f292b3eaa194ec1b0afb8683ea52914cf1b7c5e49f662697e4b9e7f618f7e79be853deb92672acf973cbfb1178aa516580fae66953960106db21e35e5f9357e3b61e9f78def2ab034e246f14771ff7cb4fd74c9137e7de197fd657a0e93e73bca0d56be429962c545703e9cc2e42238a75071119c53945c04e70d625c04e7bbc7e459ca53254f539e2b92fa88fefc1c7046fc49e10d38004d5c0e30347f3e842610863ca845f6dca4c54bf6689edfdb103147ec2b18c47c7adde4a2f98d033be79c0f654cac439e36548d37b022c708557f83273e0815caf03cab1c3f0a940d4cb936fea10e1676fe6188ed2fc30cb5feb49506c56d4c9bd5c6c499361ad44a7cb9d9e096b02eb71a9eaffae6f972f4381367d256daca3279c07ad2e505d662e1a4324eca0556a252397e9fc0ae56311d532aa9a8984c7125ae602b1fb98b4b2af5f2a252354cc3b8c9b4b2723ab1b0a050ddd22d3030ab158c8131a552ab7854e5a3f842513e7a7949a55c5c28eae484526bbbee3fb2d8b096894ac0366762a99d928f52aec8725ab13aa61c330ca750ebc29d928b624725c7ca8959f9085bc1f8088351f90853bd705e38294e0a7b9c7f9fc0f67b9c940d6bdece239d02cbfd9b34258aa344cec4497126ee0b7dc799e8c79938537c6db59db495a6adb4d5fcb24bcad368bb740c27e541f9a87efc8eeabdce4beea43a2e9dafd302b3eac4943c2a393e27e5a38a516450ea947c240396982c8313672583150e8c0c4c1c950c54382f1d958ea9b3d23975583c2b1facd20767c00c49e27f7046ccfd654faae4e722179ab22f9dea3fe84253f6a553bdf5d3c272e2504976295d4a97d2a574e9d2dddd5d7e40caafe43d82cfda4c15caf038af39f928e4f25fd7925d83ca1edfdba84c2cd9ada9a24a28140a852a954aa59266cafe1c938f369b50cba33a167bda705628b5b6eb54b9c6edc646b582b131abd2264625964cdb11d96319c6b066c545a82e596f53426d501bd406b5416d50d9bf7254382a2ef2e7947ca4714a313ed262563ed2562e72181f691b1817b9ff46e5994dabd9a6e4a30cb69ba55a67eb7759e84b36ecca459ad4460be740919df344768e13d93940b26fb6c8be9943f6cd1bb27fdda83429a796b7891b958f2089626f0373616fb3b2b0b789c9137b9c92c41e4793d2bcf8487b7f8dca479cf7d7c0f8c8bebf66e5a3fafe9a181f619a26e5a38d4a93cafe1d85dda87c94bdf6147bf4c29eb56f61afab3fb1f772b3f2114a149f47e112452a8a97285451c0d85576f845f165ceedbcbd99aff56e5826636be76dcdb4cbe1dcce63f6d60ddbb08a3da7e3a387cfbc4ba524b00f1cd6621f991ee4b76d25fbd90fb3f771fc22ca2df6114f5bc6b46d32e5bfb94933655299149629adfc335ad5fc350dfb70576daafaa0ec344dfe5a57ccb554d4da4ca7fd35fb212a770dac5792d16a60e37bfd79b719ed9a5fe2a30fee8f528bbdee7f4e29a54f1943fdf2e6b0f16d6846386035b809eb7b78a67fbd7cdf24309f6225ac6c61ef47b63efa75610c79273dac3c8570ea5395bb57999861428532babbbbe5c468675ec328a59dbf3ad8dde964329a26e5fc86d7e45490ae3a1f79ea94846993a22aeba3b7b6d4973e53ab3c1f4d511fa0edb98e73dceacb64328ba2a55a27a59f8bdc478cefb00d944d07466fab0ba5a9b26b45e7e583a2be444e172e7ee26e940b92ee493e5a96cae3ea3a796a64f3ad7251c80457aa92a94d5f9f56d467a9ebfec2b1338140eeaeba4c2911fcb3d4aad48a964aa86852c59349ae3a9dbfb09b3a2a8bb359ebfb8fa322aa5bc5d41755abb83a95526e922a8de62f2c4bfe16caadcabf5455b92abb4cd847571a0bfbf0716169f998277932d11429a4ead4a7e297e1ee7314862d2a3b7551fb253f899ad2871c5428c36bea235a45ee8bef2e8c07dd7fcf75f1dc4da14ff29d9bd27d8a7fe739a0ef708a7ccf6f386b3ee8353785c322bfc129d6776f6f0ae7399c72bdc85792cbbdc8b5df5dcd83eee63d97e3e2ebfdd72e8cdfee8ba737857b92f78b7d4ae7fd25dda4701efb7953a866f3f5ad9b42e9ffc4299ad7feba2912539c627ffb4a72e96ff77aed5a5fef7cec4aac6a1bf61826a1cf3ddc8eb8fe7a6bbd66b391cf91cff1eff8772ae0a32ce5f34ff21b4e017d85318186f1dd6b1f1f05cb7ccac3c0292227239ec74a3c8f7df4803a11ecc3be0793c4ad02dc11f6af8f1bdc8ed0bcf571bba1796bb31dc179f971bbc139a2f3de79c7294226d0242129d93fc90b11c129f54130708a07c60b21c129da772f6c8c3cc90bb100f69e878153b617f9f83946fe054ec93c0926b981bdc7054ec1de83bde7e17644e6333ff2703b62fbed451e6e4768af7d07027db2ffe0a8399258a2557cf4f90852516920a087f1dd05d2fd8b17b94044dec58f5c20234ff2f563687ec3d97484dcd83cf68163601ff635bf790e87f34168903abfd9705ea3d9bcb59ae7b80f4283641f6e35b8cf8340a0efbaee4544447e64e43d9e11cf631fbf9ef828f331c6af808d0a371b4d5c9526f96757a581d487f1da05a2fd8bdf2e90ed5d7ce602c93cc95729fde7946f59f4faa731aeb7defa697d101aa4f9727e101a24c73f1ffba0d70bb971c5c03eae6fa24914c60d4243a549f55fdc203448f5b5abd224ed5ddc2034549ab43dc90d4243a54999c72e109526613b3aef007b94d87979393fefe6adabf9ebdaa777e4de7befb51792b80b49997b82d2001bb702d7b5eebcf2faedf8387c844245ca45f085aa2c4c47458c8b602e29a131c6f8dd1154450e4a5440685261087e37019ad07fe8593802d2440f215d4fbb3722626eec261844fceab91f861a7bde82f99eed948b62134c9448d785a4eb3aa9b708d80bcee88fef2d8026f20b30143f36168d45b7e4c6f2050611ffbabe3285f5da65259a64114de4cb68e2fde80f0631dfb2eebc5ebfbbc432fe98d1bf40c5153840e5ca69be0d017f7a9d657294274dd86b11b3c8e4eac2fce394a7e9c20cebbc5efcb26a0291a4e138af274dd9bf0611d74fac83a47eff7c587111fd137700aab28eeb63fcde86b87e9b3fef10f0ad6febdbfa71cad6f57d3b67d793d8f564f66216d93f5e0f09783ddcfa5162fc5a05492c2aa7585a8931f5ca9d257b0bbdf3da188a596e36967091ff12d646138748d0c6c2caf768d66c97ddbf9fb0be49eccd8743ccbfaceb5d1809a8ea1a00bfd3c94a8929f5a2513b2fcc19177ad085dc486c46645ce8011772d33c53fd038bd9117050fa22ae25117e3e8ec0252e8a2739c6ffef3a6b5d1429253942c288a7172a94e1651a05d6939f08ac5f4f31ec95645a4f5e3d4d4db56df3ebe9873a58eb5eef41d5345dd7f5e579410cafef6ab95017cb75ba562ed3656a41b19c2e13f6ae954bc5525d2a97ca55ba4a568c1563adac950563c1582a4be5a2f92a2f59f3e4013be72966e5a39aa9301c577ffe54f9c866e6cf17fb1c97e22c67b7add69989c953cbacf2ac1a4c9e9a2a4f3c532e9a1fa41b7bf325cfafd325abda969976db3e53719da7fab366321c974d171f65d3e5f391f6b5f8886b41a1587cb4e1c932e769ce39e79cd3a735bd789a3f32e7fc111e3676fccc89c6112862ec93a5739e2e6bcacc64ed4cf9c84bae9aa7799aa779a227d63ccd7992f3344fdf07bf930ff646f33df642797e5826ca47242ddf74f111c905fdc8edbe7aaec857ff5ccef59078b0100b78fe03faee453a1f124b23b9a01fe93c22221ccee7764058f711f9701c05310eabea541fba5ec98ba7dcc53f6f7194a35a4ecb51d7f70a65a15012854a398bb3889c4456444c9c14159cf2c1ae4272440a07536a6dd7c574dd0bb9210212e9de23d279917ff17ca793ea783a1e10a8eb3a9c0fd2a48f08ee308813719b4ed6699ee4e9b4a2bff2f86191526bfdbd92799a273a4f9365a266cb74c9f1ad9226622654ec2b766b5cd6ac2cb0f4743a9d4ea798196592f31eb55ee72847a19a5e31254d1b8de5a48983375883ad3cc5cb9a328337ace18a33dc2cb0fd5e5c81a55fa3c6de4c575f0faaaed82c39362a7ec501b38ae952fc9218638c31f677e2f21e4c752218650a18942954302b1853ca5e9bda24bf4db42c50292d9104972389254a2a38727c5470e4486289d2c702fbb18e5c2939940882945adb75ffbdeae744349ab79d46f3dd8b8c683edf8dbca647467abb01370adce030cbc90e4f0db65a0a0bf35b27c3f16c36990dd71ce7f1743a1cced69e4d7fbff8a8d3dd1ce66c9d0ea773355f37ae7138edd2dfd6d96c9bfe342e96eb1c367a382d85fd78d2907c7f8f7c73df5fede546baae1bf98e64040bb1c0c86b6cc7711ab85180a4468dbb24b7fbda9b05ba8f231cc9ed46b8cdde4cb7f9dc110e58b882145a105edf2b8a2d3cb1c4de3b1ee934f64734426ca47c44349ab71aec43e4394e44c4dacf67e4e146811bdd6b9ec31a6e8abcfc9a61147b0f7a4b73715cee8a60ff600c71f55ccd07296b06d044660186e44b2ce4d3140c42becb0d029de388b456866025768241c84789cd04eb65a52cbfe57b59497e2fc1461912ebd04e316e69e0e66b0fb71adba3c4cca3c4883d7c09a5eccd01be8a21f99d230f2cfc78b045a62f5f6e12904feb5fd8d3fcf5d6edef4d02f5afdfbcd7713a5efd768cbf0e9f430ccd4789b0c3d9e0d8bda51f6baddbeeab04675c3fbfc28d88cffd49c01914661df4bd9ee1efd12168644abb046738e611ff2dcd2da2fed5df5dd0fd5ccf456181b931d7abc4ccd7cbad08f8e5eb33a55d17134db09f8de18ee91514ec6798bdeecd8818597ecf219ac89fdfab68425f6e35a89cb0bbbbdd728bba6bdd4d3ba67367af1fa3177e3c88c9bd610f47eec7bccf1d69a4b163646a3b388a319a7cd4251a4a02ce80793a0ba55da2b8b1f75bddde3b50e8dd41e3eaf0cfdeba9ebfe6d953ed3dbaa38768df377bb815e158077df8e5ec6b3fd6584767daf4e3011d68e4feac9f3ef6955e8fbe07f3e09172631d1ecc439e1026c0c64caf07f3b0804cfda33ba5fed1bffad5419f7ea4feed1f377f8a7534ded14372127006fd4a432e9a2f01f865b77edf1d3432ed414b0f50dcd7eb659f098532eefb7afd1cf799f7fab1adbfdeccd3cb3ddc8ac8b08ec6497c997b94c84427c0620f2841b3fc1e344b2f1437237cc8902cb98739f3bdd5c8706f6d0ff891b9875b0d0e271143fd9db71b9f88a1f9dab56e5c8921fa5e5c89a1f9d11443f3fb0996be174de8cf8f2bd1e4b1b7de0727956802bf1eb4e4e983953cbf814493cef3617422cfffdcb94a8ddecfd1144db09f1f9f8823592a954a27d2a4d45a0847108ae863df5b0dece94338ead59b5058520df3ecb067595e58542c303fb0d867d8a3b69bf4e763f7873a588a77d0c8f4bd1d2899be95bd2524c34a58997ea55747f6f4adeb656f655847f6f0cbd67bd9630fb71a59f628d1ba3d60e08cec67f6de0e949c616f02d9c2de4793fa8475580fbf0c3f9655f6acc73c4db6586266a0f0a00483c85856500473863deb0790ad509fe677a959bcebac2dc1de3bc1baf42ea1143631dcaa3ce9778e9b45dfba9de1467b18d14332dd221b416348beb017234bcf0c0b4318840761603cf73dd44d9f2ad5cb4b2a7572427344d918ca018410622825a59a666da7d3752ea089073ca04497fb95a0b9df033d68dee17928e5fe1e5d6eec02a5fa5fefee0e0388ed1338babe799cd0bfac87d9eb3c310f1a596220419c84fdf5411240c2b010f9f4e22018e98a41ba38d2f5704697f2c49ec4283088fe7ef8c9385f70041a7832fc58a87891e1c762025086df0e5635d8812aa7a6b683af93e1b7832a76000527c36f072ad09ad263c32b136750b198842be21082905ae42bc34f0561c8cd65116b869f0a9a90fb4615d84c869f0a86101bca0c49119ee17b8678470fc91e133106351e2122210eb6630b6c4793ed478906a850468d2e29a6b5299a641e7e7f6f9803dc22c30692613b91e5f5f0714493ed3d212ccd620b2101bc0c179d2c9f88a31c2e8a0f05155d455b91a1d75764f85baf7ced95c75656bc5762bb64726cd8eb2d2af60490bd769191bd7681691717faf97c3e9fcfe703028140201008d4755dd7755d272222222222222232323232323232324242424242424242f2f1e5c9c9c9c9c9bb70e1c2850b172e5cbc78f1e2c58b172f5e9860c0800103060c18305662c4881123468c18314effffffff2cf7de7beffd2027a4fb3114b76061afb7e839f41b72a48dbdd622c76f31e4f8ed8231c61863acd2430f3df4d0430f3df460922143860c193264c858f1c1071f7cf0c1071f7c38fdf0c30f3ffcf0c30f1fe484f403104000010410400001c447298178184d98f880b8108ae4ff702914c9f7e15a28922fe3765024bf87fb50241fdf1014c9bfb7048ae4ff3d8122f9312e0a14c987711b0045f25f5c1b5024dfc5ad0014c927b938a048fec8cd0145f2456e125024bfbb4b40917cd0ed126c152892efb96d8222f99ddb2b50249f73fb0445f237b759a048bee6360a8ae4dbdb2d502471c55a4a29bf5bbe94dddddd4c4065e28b3a7484d87b500c19c22d6408b3c022c38751880cdf00717462a23dfc9268525fcb104208bf62d85fd7cff99e83e3388ee3384eab19462f6b4a6f8fc7e3f1783c9ecfe7f3f97c3e1f100804028140a0aeebbaaeeb3a11111111111111914c2693c96432998f2fd2813e9e0e67a3b15c0360e6c2a1451b04777969f0fb8b3c80dccd11594aa08a26dbc78f56c451f69e0064581115232ac62ba289f6f1a3167144bdf88638871cb77020b9bee74ee4f8dac967274f4fde3a797962596badb5d66a341a8d46a3d16c369bcd66b3d9b870381c0e87c3e1a43a9d4ea7d3e9745e3c1e8fc7e3f17c9093ed26d143c35e43e18532ec09e029f664741696d8eb640fc2cccd468e88fa7c3e9fcfe7f36901814020100804fabaaeebbaaeeb5c444444444444443ec8094964646464646464e4839c9046388ee3388ee33efe884807fa786e0914f977ee0914f9732e0a14f96f6e03a0c8de0a48e7a0eac789c20540127c08150049deccf021140292bccb61195cfbb1450d152a94d19d219c21b3962549eeec241c1880263d03188a0fe1bc272eea950662959879c7e77e2ae2083b7902a0efc9b8de7391bd3ec5ecf5295681124318664014c58ff80df4840e61bd3e599665599665b15cd7755dd775a128a594524a5b300cc3300cc3be2ccbb22ccb3ec80929fb080408109513d27c9510693e10958e345f8592e603011284929ae0624aa58ed4c4b50590202152139e8712697e901352133148f3e3679742913f762d14f9d3db4191ff751f8afcad1b82227fdf76c0cf013f72e30f74b9479777303184227fe818be8c269e8d7a6910cef04f076fc8fe14ced0647f6babe1328e1c552a956a40eafeb8d568ed8322e802e1084507296882a2032fc010cc1e283b49368200d9712abb428ddf31c6166019a6fd67d5baa829fd14ccafc43ee4955918c3eff0d2eb76ef22582313b56510ea35dd2d2ce4729c32b110ebdda960bb27f5d5a636b5476d472d3c9f7e95d36d603da8b272581bf88f2c516289735bcd86905787f5f46b7f5e679829dcda14efd947e2b1f084779d0b7f991b0061067f6a9bc18256dc49f15f462c6903d0deb0b001a40429642841ca54022990410b5209721684f0822c60ea40872690a88be4494639f9727c211810474d72019f303f626d6bac65f85d019525eec450bb0c2a845105ebd1dc33341a353a61650c69e2532764ff2b3891fd3bfe54bbc01650513e8f1aadb01108197e6a18418e5264f8a1e10c3994e187061714d1a38955f67e5c6165ef032d884286958b56587742865f1952b9b1c8f02383135900197e63b0a23722322ba8f20e1c791545dec144267b3dce40864e8aecf56091a29e217b3d527086993d1e5f2042009090ca1e8f96548645d052692b42964a190ec9f03fc387014c9e5ec8fd95dbd17933c23f62cf0d2b597eec47f178619ed7fb4162c4f442963fbd903d1e3fe2dd01870c19127fe4385772c3bce49b6fae518b37469e777a21f746c42be169dc231af1465c1df16513d143f28e1e321d9314d1ef4f44bfe31e35b7e189dc23e689bdf84a74f6ff91efcd7e6f8e17e6be3d6a86ff6376b4436503153983337a469600d8048023ab45fe900d4588094061831590003047ecf57b56f6dcd863222b11f3063cd0b91feb64b66a1add94901938e38389c93b380ece8078c8fe562700c6ec5919c2195d5a83edafbdb51a6c5fa104b1a8b143964a50255ba6182c2d3a010d23860c3f76d6099025315c94526a454abd29a53394e9bf9ac8dca166f85961953b2e2af4d1126b18a2861cb49cc1076920c131ace00a6540c214d290851c90e0c35ba2c9280122aca77183532363087b3d3cf7676fa349fdab5e18863decb3c7aeebfdb56882bd7fbd6ee78a5d1843bd11019b90a5f6d95f3d3c635f3bc2f60cccd895114d429e8bdcefdfd9dcdd34fae9ed31737772b41e5e8f7a144e8ddc20d56831f431e47fbd8b0bc721d6ede1197e5fbad5809a8b6802df5f463481b98b269ffd437086673d73d04d581d5e8a197e59802277eecf8213b94b198e2043ec75298fe0554266f8f21661337cbfde0f4f3e49115996780797e1432fe492ecc121d96382edef0ce301e0f7b6e377d8ecd8450cf597b4505d6529b5024c8eaf65a1caf13b71de4ed7aa0c02ed878fd8aff2de8d0879c4f54c90dce156811f241f41844492755d1f3f482d7b78961343305898e38dfe0bfb608234bf49f23af6daf32cdfba17096e379c44823ea2242b632cc2177af8ece161c8229567865f15e690e1a67aea42513bfb1b6289b5f0e1d72adf6a74273efa18bf010f80333c47cec54bee4c039621e3a8bd0d01c590e156230b6d6e353e6bab61c5a55942c41124b1581346ef21039f833320863008f71558af249f54c1891cbfa40aa61cdf002e444e4d35ac5a628a7d89da58b01eb2154afca3bb46124c9c011858a9208c1fb10e88773091e17bf11b62ef8f0aab0c3f2aa43215a8709b65ad727bc08f4c2d5c7dab31a335ff4a223a5b4f324467eb2d4c73db9883da58d011fbfe70876e480417b19cd6bf930c11dfdfadeff93afce55b6f3d8d26315b3fba7b0a15f68bad5c154af22bd756d8c14496b0b3c419a830bac7eb70f3d740a5daadb8a7b045f66c34f184c8fe2e8400a24901a6f08602844632865a477ceaf12586520363087a5a9e021619bac064488aff12d318d2bc5061ec263c16a00aaac460856e428c489464a7b987e7223c3de0820f72bfbc489464d90d86afdd1f83bce4ce26b5542a955e48f0fbc22f0a56e4fe7e71028ae013140a4b4557c55b015b0a48802298bd01e4a67164a94aa5521148d024734e50c9707bc928b1c6ad461358324a0c818d6640657f4f8b814216d95f9b83b64593c06a307006848931c618638c11539199878c31c618afc74789555e987774de5231e4afc3b2b8fd9096518a594bbbae246e35b01617f94f9796d4f792dd3155f53079311895ec5930dee572b9c04061259b64f861a1c22295e11432fc9eb092a340052a9ac00f0a5bc8fe95259ac02f0a5064ff5a4534692bb2bbb45469a9c0d8f880ca8e1e92250e81f52c156d65a9a020a9b22c61a58b3b4c76e9525960b057e2afad321798e82aae92b9647f97c8f2c5d417d74ad96b4cf60a93bdbed49aaa2e2edfd7525b5ce4a8ca52595ce45f59da455b35e125cb9f5b6a4b654aeebebda898564e1996ec3f02036126f64ab614558275424bae2cd9bfb26450170c15364cfd56653ae726b15f2f7b7fedeac85efb2e9a4c0f09a80a7558d87ecf76701743738652df6538fbd0479acdc3cf8aacbdf555bb3aacd73e2a98e0448edb10316f1f8a261bf63a73b66ddbb68df38d391c0ee6697fbdf528916e4534f6fcadaf9db79f1bec8542b97b4e13122579fbdf6ec809767bef89901143f3371cf2acf59e3f7daf4dda5b97d6d71f6e12f0af8f1243306bb7621a4333c3ae67fdfcebeab0deb3795a17cd872f238e2269425546899daebfb6c3cda37429e503c1f6e88c8485bbf4fe836b8a87cf05feb591af973f4ef9c2de7ceb9560e34becf16b6f35faba73d6b0e64502fe981da750618e10c8416308ae41d225cc217b32531a230680b0e22bb911609da5a5259a78fe4513affd144de0b70431e47ea7229a78eef72ba249007207207b5a54c2ca5d7cd42e1d4d5c62e8b35ebb3461db5d727fc3ef0a96ecb98be90ef194a99732cd958b252294be89f0350cac173dba4a8e3126c795130baae40aebefd912039c44936ed8565bd6b7f56d1901a6afccf47a7e49b2dc6a582fc3b2e67c6b4e6b5a0193a7ccbed598369a5c1fbfd35dd7755d17869f105cb2e5396e35acd3bc44c0dcf2a12ae65596df9fa7b2fc7965869b9732fc80e092e5e7de6a482fe591fe6010fdb42b4b8506b025b973e5fc6348465c818ddf7d3f70114d00e0c20696e692ec1f82a3d0c3d17770d45938b2148ebc2497b0a18a4feb20b66ba7dcf4035315ab5c5d76963f82ca9dbb8a538619e62b9ec8f04bc213190930392bf9e852a92a8d17962f2e929f69d809ce809d729375bab25207c17ad775954ef23d6bb371a95c247f09db57b5bcbacab27e232f15aa3276f2517dcc05fbb0160c85b160aa2c31982cb15596584c96994a7e35c9aa92652d6529a5ac2b5956962c2b2acbda92e56725f9192a63c9524a99a9b47c2ea9ec255b6b05cee897df01a05aa854add635410a959a110000002000c3150000381c0c88c462c1288a247d1f14000c7c92465e48208b44f240c9310821639021c6201010901100100009021e863b44a12458e5a6b4fdaaf8460e73777c1960dd1da6ba16c0beef246cc41521a4c6205ab8f4106195017b60518d9b86d8bca98505e0948675f906182d560bb8530f01b00351b4834790ea0c3e5c16f85550c43dbe272c23a7de1637b90b9e306fa144b32f39c9d0bf4e088b82f839456d6c1b2f23096b0f931fa196e80ee55a339f343b47de7f3356bf0c34cf82718a915b59a302810405cb137610906392e3b8ac11b46347a2df10e4fc427e1fbc028de946ee56b8457c7498d59f6ddcba197a257d8e600729e18d4d18ad2b9c4f76e1bd7335c3c13cb1a12dbf57651e8f893b983d68c9f17ec6e42e927cf47a4f16b34aab2fb6e09c29e73e4e6e762316bb9fbbd1e4ae04f108e2d07f98e0c76efe8ad2816aeefcfd3c8b1611f89f86c195cbc117434d45ceef5b9b9631bba61a56b08261f104b25d33be9854035d3ed8f19ffa41f0053dc7fcb811a740f4e4ab7e008959a05ce3ac0aa11c26699b0a991b86a11719656b2e0e780e00885e4e592e66152c23a39084b4c1c200c8ebfd9e7b459d79a1c670e287f73b0c2980f1b7eea3959e774fe0861246da0942271a6def766b89a3b4cdc38c7ac97bdab89fa084fc2085c1f6a319f3d406766d10e4450c64c38f7f490a74f6518b243b1b60d2232480d9cfbbb7f730bfbec44513943e3c7909d01a9a3aaafd18cc7b78d7dc745cdbc629c15fbb52f54cc2e5ad3e57de9b8dd3af255c6e4ef60ba98f3c2303f09c0786db4877dd214b35adf0cf282d20f6ad8db66305502068cc56a91d5c014e449c14d11a49f92c6e0ebd131e1254102970085d30247f2ed3561482fa314cb7ad324a5fd4828a974aa9ac55604a5bb4184e235473073ab8ac77d7a5db735e70d18bf1402105541271276b2f7e67423044af952d884decd2d919049a1aca7e158a284ad11d8374ae48afee69263c006147c6c418ca67277238233fa880c97e19a6adc249bf5df1b1d212943e616a4c465fa88afac19eb81d46ef7d6926470e1bf13f177dec6c6bb793e75cf0bd63eb094cb8a043a521be0163008a08551883d1eb2e1e9735c47564596569f07e67bd914baa29d3dcd0cf73a4d929dd00683d417a5fd64c9cf1cf898f4cfba12516c00ce18abe1877e58617026c3799204c7d1b955b130df8cd41337e6bf6904c389d7df81dc7ecd7eff3af006f75a2e3970b36dc496f3a1edf7ce66063188c6b5c009c1b235e403a66edf73e45e294cfb7ab1fd97ababc45e04b16ee6513a6c7109905b6010c1a5927e627c837840f0454423eba3fe958bfc199c75a890c71fecdd311e6ec6d4e178b42ce6d9d904391e19f66b5705e1ffd96fdc9496e2c5078772d4784fa909de72f78c31fe41778bdfe3474a80bff468fe89d6efb1ed7a2d909341c276a60b64560c3eb241a51737ca32372a71012965dbb83764fa591e31c77ffcbf7a0dd7c9c119e4e4c624cbb516fe39f0f6784db7682956d313e86bee92d98567da9f6a5a8b2086092ee62a69f99b99c05e85163361a768fbffb08ef62d7b6466f253b5719aecf15a57e6f7c8101723bd34f0cddde6d8075dd603b28531452de201859851d74158888411020145d5e0aff8c80838c80223188965455c9bb1e93bca0ad57683184f11d21463c5f95d2b11e4d21e66815e42ceb854d7826f5a929c7b89a2250367600905b524a53588a1f595af9eb4550ec5249e6d0ce6981cb58f363cea289433b955c26c4a224e178954808c3875f964ef762a725659f9dcdfb097e137b7a771f542a8edc1eca8977b45932ea41f497022eac4c20296b5bfb6e50c91ce2a20f7245b0140609567b508853f6e2bd0291ad49265051dc6425fbc0afb194ddd311cb1d124080b6ed74aaca3c4b443d57134ce7e62e5bf1f25d81a05fcca7b365455ee7bec02dd0721f1cf10f890e6f0c1d2be1c3d80613136669d4fc9711a379e29c5999cde9f82a3e417a222314ba5cd23fb5b1b4e19366e26cd48411afd5e99848fbd340f51ece3d930cf54bcc03f60bdf59a0a6580a357d08a6a53ae9de1a1ad16aa979756eb28f7528e6c8bad2213fc56afc26197bdd96e99980e571949eb013eddf23f7a42cb6bfdafdf208e47675f5c1b275cf69005f9d29323949f31739a811b67313c8921ad5c1550446178859526726d0372f39a6df94c4c1ffb946b22903f177389ead4fd1708f7c7562cc23eeb03edbdf8279ec94e0a7d099e8189f3a615ab4840b58aef4fd3c354a1b0d6d4553a5ea08d90c6d3721312980c0e796246c7b5594659fbe8a47e92a6e6ce9e16f53cd2efb5a43d931647b7278346b63032bc06bb109ad2d298dc7857debf20b58573b26fda4d554dc2b30ad9e9533d5c889d8c7a854856c4cc67531287f8ae11c81a95c2c97886f36570db47499bde998ecc7458d1e4bbd88b651c11d6ec92257782710945582b687e69c7a406554266c7dfa80bd4892fbacea686374a30f29ed08346a6035333e9f07ea0467326351ef8da2b4d70b21c202f1ccf41e92632eabef714982c227d3631783b24d1e47fef819b81298a781f19797c2f828557fa0db7fd0eb4fe8290dac8842db742ad2f510075aebc2fec9ef77f9cb55229318a6e7a68877587cc71847a65fb82f034f8e483212ffcec716dbc96c99e07ded3a1fe79aceaf169ff39af741f276ad84209dea2b2b94272e3c414bcfdd82b1bab5135f1c02d6f52a5b2b68d4390765a9a1c07488d14710a8c5af514782bc105e8fb8a5638785ea56f968ac9222bed17141718a36bdb16a27b6231a54a790747b1f1ed669a985638a78c7e9bbf8c41f7a31ab4f872304ebae335e41e7e46c52dd2aa92b9cf191d2b10204371b7d434ea98ca0828760f71573126b2a8042383d88bdf2ee592c4cda24ddc314d2e3d9ba14da282502a2fa4563795a9efebaeb3b31c0d30a005970ba45cec1628fae98d5d95e47dd00a8a9e898a08ea1fd829a5448efe1a090b0e42b9504b865f35561a5feb8662da90beaefcd3c3d0570e555d079b6b9aec33cda2c78d53d4aae6d21e728cbde51fa03a4d135445ac841bbf903bdab5aaf940e658ff151c9a2126b3c43fa54722a2074d5c5ac0d39366f69cdcde36688f5b85bce31bdd0253b4effeede2146491a3a2c78ee96520bc52925ae90f0ae132bc452354ebd9864138d29cf7201328722d8e6d16b6084b757106da160fdcbf3ae89d3e277b869c3e2f5024428d21f400fca7081a2a525b0046e887ee4f2df05b8b808d2cfbf4467bf21e7b5648f0f05002a1b820617a6a46b55a4b19c49e07c8447ed96fb3c3a37ab79ec8967cc6ebaef1af01e4574d6b2d55573ff3aeb2fe9fe3edf3cdaa186dc2a6d4e8f618e87c335eff7eea697712988a29fa0bd7264df92e6b4d92db3987ee20f41a67432bd0a8c3fec6048eed393f05f49ce608d1db8b3efdb9ca7a2d484186b969854386739dee800b2ed56e6ef36a0d2c1ae0e579f4ce4dea97a0a40e0a0fa2bcc740291f366491b0bdd3c29f27422b5ca8cba47aa5f66f15d9e574824f3aa3c176a3037ec9b49044d969596811b8898f41c47d09782f492942a4fb4c7d77aeecf89093450b189105f48f67404869f4d986ec7b5e9e119f0bb86e24d6a1a2361acc647fb390a851c9281508c0283d2debcf59cb4fd86c4da300545869bbcb4ac335cf2cf5ba1fa832c831b11d6bb430eab5358ffe9524971aa5623b25d87bf203fd38eedc9fb40f510e067b7a9f6a30dddfa44fcf8692198718708251f2b2d8407eed08fe42be305344e08ab3a4eaf1c79c2e0e6c2e691afaa2421ab5b94444f90c87e17d981ab60c949c819ebb67c4670d93678e6e63196cfe9cb9a89af4f26a0ca6ae647a46e4290efd298ec8abd17dca1c176fc4f4f31468f6fd6b3e9295e1c393875a89f3c9e83e156f5a8411e6a78f7e6f32fe09ae12a97857cacbefdad1f8d1b9466ad3d3fa09d9703535e864ee16282da49dd1663a581dca925a6057cfed92f5c976128266d3cf84e874bcb17c935ea66c248494969038ecb7827bc19f763e436bb12e188ab6f08f2575ca6f50ae136e4b5fc7e4cdc8ea71f280475de2300b621136ce5454b866c996177b8fa9dc21a9ebe37b72c92d78bbe0723f86ea0482248c12d1661bc9b47e5c97440c6e63d95b5f2b4494a5ec7a7977784f26f206bf581dc4fd322854941b19303c821e411d229dbe7fad7fb8c7a2ebbaebcdcbe591da3766ae9285ae10acbb4513cc67baf81f3f7792f0067b0c292c5bec419917de388c24c92f271d2fc7d0039d0e721edd14fd84093e055a3e64c436ffc50ecaf21c1ad6f04cae2108b1cf6d0671ba87197238e1ccf2505e780c413a76ef96d112020940512745d486579140659f505a1b72b32710b1da8f55a5935ea32350aa00e88a93831394edf007c1108396722b3896c8a3e7eb1f53209eae6850530a006783d63656cc9cf4299e1c8eba92f0ec354b7bbb4548b7a8cab1c143516a351b0801969241950cc2a4804f732a26903a40f8a8eae0f51fcca07581abcbb91b50fe58a6472aeceb6cc9b6bde7b8ac36857f2994df02fb970c4005c4faae669625ac9d7d8f970c2d63102b9ae4d53b7645d5f4ee8fa071bd93e245bb13a655a1d8251c9cf66dff564702e7759f60da7c4138727d83a3a97eef981d8c90583a7fef7b7972e6a673b8879339d6a281c2c55924d78a9284e60250b9d1e0a1f6e3e2e07fd1b191e457a3996c7143c6e8a120a194dda029c56d15839d6cb6835b2713a75b1ba6837da7a1772e1196cf9a6b89aaecf6e0f4b71c88c4ee8353089039b2d34b67cd146e168f93e82ac8169bc4038a199f2e7157a1ec48503e41a194a634758cae4e86fa8ebba3640e58ef7cdd13d89b36d3048808fd2b8cbc5a8d8d92bf9e3ec6b7dd8ea2b89fdd859977700bfb96450daca0138105cb36e4cd041a88f30a8ba7603b97faae7e047a11b36f91729f8ad3e378a6e7af91ef505cb416526619df4dc28ee90e3a299421707dcd9ac9b1ea229906397a78e2d1faec605eb757a0689008eb23cc49488a75986b8038dec0f595b6ec00e952cb394220aa8781a8120306482dd5331523ef91db5302efae73fbb1f1fad52cbc59be6b6c5b4f0c8658ed725ca6d9efb1bed97950704eaeb9e5970c3fb6612e4967418b99e8d08ac1e4996bc6d3353407f2aa0035c213d228217039bb9edf020a09ff4853573f71a3b81e8590e3e5fbd6ce922025631a4b0b2890ca164d36f664c80b2ab9d79952bc862d8dea8068f88601da9038bc33e0c02680313712ca6bd584b04abd98898488ad6667e5a49df173f37261e81ffb5f12d1e23b1fa78c8788cf66b039af125f508dda4727b620f7f4fa6a306d99b978b1dd9cf0eb2fe1919627aaf39377b86df2ad346e43c05e175531ef610215ed2ac25d281bb2858f003227e3ec8a5c399b66bb8398e852e13b9a96a01dafa5fcc232f8fbbd5e23854aa45715d5ccfb473017356ec504357f5b9698725c702530c044e98c7c156578704e3c85dc363e87dedfbc8e83be47ca3b8e5271257fcbdaccb41c7877809c42f5a79b1138b3d379c7a5fc389678053e5f6443e98c65ed9a096c40e6e45ccbf62fd40db76b49e8677a77bb401c6d31d817efaa97f21547cc59d5dbf830e2c5beb0d72cde0d5e41936e1a63e83d86a2e6a67b055026ea9abedb37ab88eab9e342800b523bc40571790f6ab3976ba1f7c1454e66b5b80caea11a14fc6ac60998030ecbf23e548d909b391eaeaafdc2df4bd82fb56495a5db301913fb02ff5c5bc5bdfe742d618aa408acbac8ac451aaf269092213381289fb9ab3655ee8a7ec1b631b03fa3937db6139a1385ab6349efba873f9fc0d72122247b8de4e91c8955fe940cfdfa92e59b865fc243a15321c11d7ad44e39ebb7e1b83b7c43e5a4d6557ba83d504e7915d2fb324c5c85976afe6c95b7982af2566c9f493548b112e37cd0490b5d570ca21b8f7e65a661b49d0161493bf9157b3327a229333c19b76cf982290d43b19fe7d4064a59f38c8cae12421b772d31c02e7110c87ad9c725572113eb81d86d8781eb62552f7a1ba51e1b588d88b7babc4f9d3b78c4b98a90b2b4f59bb883f2606939c70f26bd98eb397065a3b370b3d10bc9e83b65e48e8cb013846dccf6a23c94a3de518fa571ca03c2a3334f551965796d4787a67ca24512e7afe5e4f35e3e30b2e0fedc947c3f72de6d3e739eba3bda8fb6e6eef1cd485b0820657bcd0e999e4ddeea5928c4dde938da74dec05fc8e7c1473664aabee075985dc101fad2a9d994833571313c6fb754e4d5f496042c1ee8c0ea9b812cd616fdca038a3e5e4e8eecbc07a61ac46208bf81c5349ba67486559e0fea2e5c88f68c0366d70d73e525f3058fc713bc18aea0fd7bfa04029d7f654644c0a5be36ce0c0550e2bdacbeeffccfbf6ed2a6efa3c6fffe05300dac447ce21b6854fdcba53425471805260d445fe0bb608fff0e5b368b23b61974ea0fc8d8c22dddec788fcc9fdfb38c0970abb36aa6302b874030a15c6c6e20d2d8e4d8962e58d7e866c7e3c7b2a1bedeca228eca4175321fad35f0acda16c4a2b9ff38b116156ac65e402613f5cb153f98c2eb71070d06166d712a534970aaaed3bd3f18fc2066b869cfa596ec49ff84dcc9fab844aeb2878fa16cca3628fba685b1da06b3f9eaf85a04b930ca26848bce640d020ac48c439687e4e681a3964963a4e2b16e702df40acac58b6fb5711faceab9905969eaca5d6e34fb18f898588a0d11bd51f04395652a3bdd36f9497c12b3dc6588d161573bb10868bcaae679bf7ed08461e3a7423eb27ffb9b94427aec05ae9624cfa3be5126650d421de20397de4f8f448d4397290d11c226ac36c533571bb11609f7fd8566bc179ffe304733a6361d7e53a2c7703eeee25e714ba963747d3d130e3ae91ad59d1d25a5af20c52e5c66664c09e3690e3c76e8e933387cce58a33834a97c531588f270398c8ea647d8621cd9d2a38d2428bf4b1715e551e317154cc6ca9b78f6269a673339d9ac96ca66633490374af1b4245cc588157639c585866e572b532609d095d67b4e0e05f170171cc1efce87b2d5180024818b894b59319c2e455b1bed2b95058bd086787cbab4e4a9a8e4cec1042a913b368d26326262d2a362c7144c058b4254014904d4b3ec3272508d4d42039dd05db3d5f4c5ffec49eca8337051217eade7293d15902b1f677284a4dcc9a85fed1baf742e3cda254c8ab22c59496bb7f6ff855c8bced709542154e298cc2b1f8c2208bbf17f16e0d38587c6f9cf32e7ea3d88a2c48f576cfb8561f30e796d2a59b004ecb596bf9bba1dfdb6e30a96e77d570ae73c2f3aa052fbcbbed5bbfffd8b68ca0c4c7750e6d3e66d41bf866af1ca3d6537d93819ece497bbbd23c3969de95f7e3cbf76577582d1f3e39b2808474d5d795fe8d76735b361faaf4b1ccc7f25c24428d8760469e0ea37f56d022df674dc1f462cada860ab38bb1da835766597bdcf1277a15d3761fa65b5a9ae0f7dbb0d396b77e27903603f1dfb3466af672aac05dd90c44b77127b5c3568b61b19ce6dfe26e966c11f0967e17faea46b7c1e86b305e0dc66f83e1400309d5bccac2cc0c4dc34befb83c04b78a84ae3a23c06590c343e91ab571c3253ade884ceffaf215940e3373da0b7b8824221095e9e2d243ea54267404809b5da1c9e7eae989b040a219410ca121b0b081c026be0a318248555a7c12b2335381ff932c47a9a5e4aa945011bb9da1af71241c6f8d45b2a379c7a570f0fa78cf1a778b5b7b21be3d1f27d6ebe97f379abb3f58efbaf3e15a3a49393e7dc3121470f6af5ff94aadf7ad7fe988bc28fa37377230242c2083684397b5f40aff1d0bf06291709287cd4bdba6362bea67f5e8e08494948202649b12d5a8889e110904b4385c32069d82c6468069ce4409b4a20c7dd7aafd495187e94de64eeffd26bd6a8e8e0b72b0d1521905426a78a0f36d7d5225561bf7240874c35178e5e9ec5179da5df59539f7541885531e6a1dd7d61209a267f6c73bcce47a8271be4004642c922106b58402b104e187b729f3f1ce0b9f3483480406843400d0ab44cc5e5b59c2563398bc461745458614b1426c68d1094275185ce2fa8a4fb49b365c38a39dcd9df9653c817201eef40077cdc6d381ad9ec4f44f3a7e0d84dbe0e83718f288a506d31cde577402e0de8a2c22ab533334977140586146d0a209ca9ebc263087d095a9c2f273da554aba0941372ee2f4efdae38db446a286337f16fdfe914b0802afbd251f686bd6333bedcddd98f1854dd08ce9f5c3b6f1190364879f5c75f71808a13f1dd154caf0d40121a21ebc80030f7cb9a22776ce2e438a42390e56a462f3f0825e12422374ab33baca6bff16c50b0de00d98d902f249cefa9570ea511561cb51e19da768bac6cb9cb2ec591f44802d803c5a059dbf4296aec7b2e8c8606f757c3166c5665a6942bc1794b0d7540146659955774551d0901c5da84bd5a5779a5f9a721183efdda46939b10b4003b2f510673e1be4751794723df1821a0a6931b645837655948f1864b04820eeee9b18daf0da26c65406383ab6d6dcb29cb0c1f36d529c6be0cd55b22188b50d0d32a193761fa17cdd7f10eefe4cf51a0517d5ede9e19c93c65048011a670a9981756dd4a6827e1e27c3d0b5f0d634e21c4f7338ccaabce33ceb219e4aac7a167c5168422c615358a5bbf00b33eaa69e3daf3aa0021007d282e822cbb9c4a9e5e2795ace6f38eaf183c52aa5aadae1fd8425e0031b2ff8f9cfde81c15005e7aba3f91de7d9da9b4941517b65141e5cd6fd9dd1f9f5f9bcd941eb57534e0836be4de684e0d9093d77584d8c046d52eedd3110e25a283c473510efcd3b9eda1436a4655235b2593fa450f78ce65e70d36b7cb6d560785bde5935af59ce826bf7b6bbd154fb6a16d875012a9da916340bacb3b60b74809866169c84b3dd3d1f5a081cff10ddd39a5f6622399ffa4470be88c58707e21a89628041ac400340d9186d2a884f914404a2185e9a069c416b2ace2b8126e02e850062b1ce8a407cd203f7a5327ddce2c928f719587b0d50c870ec0b5f69859c462a5f5cfcf0a99778754aced1d02396054d17f3de326c63ffb3f4c236133f5ae158efa772c1dca2fc12ba1b382209debc9ddba67ae1fd84651c416becdb1f4179408ff81809a0a38c9f8b4f2d9338418ed0c93c3aeda4e3d2cda3cb745a047644fc08e99579f8f609da3cac17e4bc6c245f0b7350a1e74396d38a7e94a985393720e6648df976994a30c241495a6f466dd626b766131d68c55f3ca0208f1a35df036c33dce8eeb5e40b635f2e34ac1c34ebef337bd50083bd950a26cbfdc6b9b5aadc569f519a39494a1aaa42b219a174320fa42fe453735c5d342e08127e552c1ab7d90c634eda35e6a8b9f6189acc2904c527152f5adca13a6dc76e5766092b2e00ab8543ce1117858e22fd1000d8208f8ebe447560551b7f0da78b382e803e5c93b723fad4a1dd160ecdb31ca46eccac2733c5858cb5f555a7be76faade7ce7b3681762224ebef203bf5f4eb146c722b5eea55157b934b5747dbeba64d4ae20bfdf83c72398e0fe05be3ced947b47de473059ada0a000f409005e7eed0bab8a3f840b65440fb657a41e27b4727166a1f879e13babbc6351112e9437aaf83e683d06974e117a94d53309ce94199fe6b489bae1a3840b77f18eb8fab2533930138393fbdf5b6cbd6ed304b27f77f639ab8c0e53fd611d186b54e9fced50d020cc9d724c6c6b8a09963dea8a358c29319887037721fb0e1fa0149ba69535aeca4d2f1480cddfca5013fae5ce3b121362ade0a351403a26fe91ffae773617ea687f23e4b247d99a24061efd2f8e1e7cf1037be867ff2b677c0824096d4d19a9cf4c9b4e67a35bfd429c198f4330ab4fc5329e6b70988829fb70fc2efe88b3a02463fd0018b901c59776e01c1f0e2a2332bd6eec04d4006ad8140390263eae41a757290a52766d5b62c48b91066db03373d8ad352d64d439ce2f1316a63380da1152ad90782e373873e3a8a7c9bb6d3d3fb6777859f1ca2fed888ceee4166efb16f3eea39b9af4bb4507b8fbad6a62dd46c154f416d057d40ec472090982baf3d00e64b47cb2c4a18258db8d7581c501b1a71f910f3a133996960d7023094f9017d0a7ae76a0aae2594ceed8f2402567e0e1de3a8d6c99a5305c4adc40b967e746b639497ec99cd98320f8207b7feedb6777fab238a00d76894a12a15cb05b80baec3fb8d4801e3f3a0fcdeb3681657b3c4b2d3a06eaf1a8f9a53324f23cd869d8d8257d54e08742563a1149a3317997ee51049fdcc204485a484a1fb1ee3b9d16da692ae478cb3b74c7359de0091a51212c083607d5a782c04ad50112270df1782406c386d8a90b4cd02a3af5dc9ad4478bbf923698deeed7aca82c4c7398d43a453e47b07217d1399d44ecf6d4e7fdbf5965b8c29fd207abeebcf7681d0443ca8df1d8d49ede328bae1326988bab42c126c9f82a203efb04d84d5f237ab990b5d54da1e45701c884824330d05cf4c22f1bd5ec010a1dd092af835af176986c269aba7ba4b117b4f4f25f691b6160807490828de5a3da4518e44465192a5e27d32b521a2b0095324f0a520bc2a02f6216887d287654f0bfac81a0e25a588f5421f4663454ad1f61fbe890174081303c29edf28410ba1fd913a11df47b475516e6f1fb7347d120b96402312ca5f689fd77a2123bf5beaeee9f3d1d6c8a9e273b4e431b74850cb33b33ee8dd9d110122abcf7cc8ffe3cb862d70454d49f9209fe38d31c812f58c144ebaee02a7df28d5a970b24c666977afa4883ef2f9ee0564b424e14d1ff3747b106859ad7032ba7545499ac32c648a2f65690a34c7bb68afca3ec7d0d251168a7c99015b610241b99b48ec4a26140822d835993e710eaecc8c5f23b194917672d573ae22350d1ea8fb73c702137fab21a32d1bef88449f02e2d7a7dd9171e0502167f06fd9c49e05c4c1ef130d127b5f0b7edcc9a2c001894977004c77c2c7a382576440128399060a801b05a8cf6966e69c03f030eda76169271dd97e0f6a1918ca9c4a54b1ac066a4ec69288a5a9551d8512684efbd53e10c98ac4d7f13fa4b0a80c85de6536b3e6d3699a1f696182b36ec7aca9f52950b247d2a31576001876fa18122632e45eabd7a9490a1abb51303aba6f74d22bf11299d45030c411e2bc5660429eef9ed979e17b36487e8b43a0ca8913ae2ae5958e2a77664c201cefdb42072d736ff6f83faedacca4d23c1ab5f4061490e2f105f5642240dee11e89b46f4001e55dcb1d9940ba997ec967721d5e996bc8029a5871dd6979a0d3d76e9042f7c21ef9c14639afa31b4babfe76e6069adcac89b110a94d00bc61ac72d39f09b9527c9f60c7de6f73e72ebbcb6cd2fc9c0bcb55c27bef09176fe396a789b28e13027087126b8d701d61e97c164ac364171819dbc28d4e2d405276f8b642254e157cd330d9ad281cd3daf323a37e57dbe3a3a469165aeaab7774af905f3000d1312e0fe0abf2940948cc524ffba04b8013002ea6b8a3e180e7e8c037e484ba656b021ad00babbea0722f5e16734a77681b23a4cc1f4ec767eb12353def88e8f6877b4e99b719dfe504f30ab3da1ae611bee0a73c1e760ff78fd6d7fe7ead85cca1dc634b6fad847df45fdb402fe494ed5103ae80bc55bfa53a76e20e5f98e97fed22cdf5078ce4d0894187d02eff1c2206af9859beb0a5d24f39a0a4c08b580044d1a7167d7172800302126bc4a4bae309e5439a8e7445d979ae2e9090dc851278b8dbc18828e6ea0aed02180d0742bd0128f5ef190585ae43d3fb17e636b8937d575129fb4c8aa21c3401680ea2492d8674e86cdcd2ac8ba93bf86cdb0f59704e2968c7f7a27d3cb4be3f25300319e62aff0221e2d1aedd897f5e91df5c2a248c52ff43b90b9498f858e22efa84456a1bf814f1bcd62f4ccd9db5fccdc57e8d347e2d7d6da5a66747f8d417f19bac05c04b95f094d59ed06411e87460625eba72ba1c41f412e204e556ef5cf412487a4910b8a5ea7a7f0eaeb2768ba7ab36d92202e07db0325f5cf8faa000026176f8f86eeb119a7730f7ac205009dfa086d14c980c7284075e59841c97ff87a55dc0fda387f09e7befa8e3e4d5474331e960c688cfbad45dddf32b33991cf0af2d1ab402b8f0fa22f06156c0bb41d4343e941222a9ce33572064e86776f77ad5c12b6f20b6d456f6d3af4fd0c3a9b7c4f686413611e09c21c1938ad7bcb410f627c3bc3fab2f4eaaee44f704a4fde75cff543ce6880e64520bdc32c5c75f9762622bcf38c69f1fe6887b871bedb6f2c38578ad15b6ceb1e029441a0b1ab8a06d642299c49a44153534c6cbbc24dcca67907aa04172a541ba022c6eb206e970a825018d1201883943b7d94efa2e1616c1eb3d0ae4e7d9dd421cb46b60c93aef688fcec03d73b7f098cba962dee22f588c944aa1525b48e2b41df3aa594c63d56d40649114bf6630ac6ab60b545822817f5d1ca5fcc1060351e5d0e606fbe87f851a43694dca46b29875390f06a71a6cd74aeb9168889b976a7ec4a236d2ebb75dbed07f937aed7873f9df6237e020ace9ba29b2052d729d73311f0e9733f3dd2106240d14f90c395bbdec71608cdfde6a7782178a7597a46eb1447ce4ec249dd084c53bca6042622dc409e014d1881fb8ca9a3969e72f379a951c51dd1c824e477068edd211e215cee73aa937487361c06bd62afed593d0b67a18de5cc57e7521646eea2c9a49086bf2949b71a15a71409e8fecb32d08b34d59bd96559c1f5ee24f486f75f6e5098eb97facd0f1675f031aead1105d9048c7075fb37107e0c48e5ba71bd1b51cf0515d5f71902888205c4c1d06476a808c3e353d712e320d8baaae0915df1e623f644119f51a923e376cfdcfd137b3922ac2a40c6f29970340490c8b4473d4598be8a99a871fa62534dbc84dd74c41ead493dd7eccaa88f2e44391df85ee3d10b14e55e9c53fedc3666c1a4ffccd68ee455978204fccb7d97629b31bba3ccf3d0f5eb7f2c139e4bbd882a250706d207ac1b1f3284098ec1cc87f6c581d9b7cfb2b1a33360b21e0482f33dbda98f9a9ba08c99ca1408e8660ce8339cc78f29956426c81cac4e006cdf140dd035d76ad75a8b07f758b6770521cdae3fc589c90020c0002d3e0fd58809310c92616aabe858b175203d082a64f2ee3c5bc04d1b95c57fe92a8806319b62dd5cb105e3de351b7d7f3f5d7b38962963d80247a1766ccd167ec694a75ed4b7dd1c43ed03334dab6daf06c8c3d1ddf557f032044ac8534ad37445048655384abb969a823d0438755ffa60d69f4473195780e2daa5dd4a92b39e69a46f4008f6faa3ac7d075b3377367011c0b9e4dbfff58d1d12c2c3aec436da74547f1da439a0e030c3fcd8b8eb92fd4c0176c117462e82d7420e56d6e84f4061373373898445373e6e3d1b9b7cd8cf4b26b09cb9c046d2f74d82a98bce3e600cfc1e0a00b5afb686796f9e4b18655489926cc3e62e528c136712f174df045aab682720725d6807dc0dd63e8f11f1ee56565a670f8a41a897a4294b3f784c7285d05da8cc03cc6100701203c74c9bbeec0b155a0433b8f66171831390d2b412742527764af5ae0138d72f6dad486b149a0c8ee64831110d98c4a2d76cf900a374901920ffa3bbd45ef28b5ef3fe07abb06f87729686aa241d313659a0f0da906626c3bf8138ea07a8e604fc5109ca4550deffe01e9f7d47098c3a21e8d383c9ec46b514766a543a557c8643bcb210b920547f99f5a503e4977e91a4a8f3bbc93c331cb98a1df1e8ad63169c68db2eebe568404080c0c60061473113fa9c657e02bfe4ad18ccc47e96532e19891f0ea58f5680b40d602105516fa0160f1a407f0db82dbd70b31d1ea2f4d9e1e5004d9662d684c11e70e96a108d96eb7e31682cd2778bd346a6a9df34c274cf37cb9bb5cc25de0dc5dedbabb6b1d852e3f7ac1e863b63f5f2f457fb55433638290030f0483074b44b025a68df2a51ee4eb6387fa46d6c1b3d37d690877bd3e98772c1c6a23909436e0af65d35488179d50893c86ddea5f4a61593058f21162b2580fb89c6cb8c2c0ef4fbe67cb85ecbe6a05d5035326c4620099d9e318f9028717bc4098f938967376718030392282c7193118a3e2a06de891b25b1cf52b01308c7cdf6257fe32b49295d65dabff664ba309ee217d331d85c5045a1c625763518201c57b4355ef826d50196cc95de3338a33049b6990a678112ef06617e5d5cb7fe3dd533f4c89b5d4b7693366ba13a3af2daa4600b55977e6ea10c29e78ab85f42b6fdeb38249b00e0ea10f4669f5313d2b46b3cfd959202c59e15dfa09a0f8b30f19a76a8b042ab108a10a0961c42254f92310c42224914740108b08bcefb1c162ab8c7b3d6f1d2593b21ae9a5c2c45df7a6f25cf34b71390744d6aad38b018b73f5c1effa95a8af41afa948d2d30261c991e06800847ee7454cbb128f74289ceb7e5a5c3954a872b9d8ee70a8f2fe43727254f55801d22ff6bc5bd315476c5b80ef9aa338d4123f9852960f49196780073681773d51af849488983a62e6a3e7e9104b8ac34c2d9faf6b38965c383d6aacf59678313ddadf178b8ecc61528c0f2c8fca9728f34bb241ad83a9b5f752f95bf3216d70bbf5baff3690e500fabad1f79fa9856c3ccf11050f2d5adafd4b944a2e9c1a94dc5af788ea73dcec54050802ef9bb8695a006842e2966f532d1a9565105753e9843bf32341bc2b29bc9c619ba3219d55b9045c53edacd0f6a350f0e79a45844416d487248e461c38e565c47a41c2a2e3d66a6d7387d0d5cd360133fa8c1e472f95e2f727b0e9b508fe5712292313920914e5d38153e87f80b01cfcc4e850477e9565acace8c3bbb3c94945521b0f41902aa342119b27bdf9e40c87079f0a5d1fe47188f13dd5e9db0b70e6dc3d55b85baaefef4314f16818055bb0a633d9578bb904f4f1b71d89fcfa2eaaed2dcb7d60246a06bca9013f8a52a6b8acee4704953b2010ab94f6df7b92d18a1c6b556413e358f107f93333121fa22877a9ad060e1aa5e082d4b24b2d370fdc84329aba3e645389d81d220865633e4460d388aae8a185e30dcc7939cdc6b016dd4c1f221a4de3078bdb64690037f413cfbf768e8403a30972e18b455d2ce17fa944c470e7c9c8703988b5abb64676a1972c9b48ff6ece7a6e851d3704343a26eae0047b1ccb2714420a6e280aaf7d08e387e2a5eb2a59981a998b4e361bc7df234a58a04ea8c283cc24b61bad0365d92e40ddce8421eac3c24c6cd27f8e2b134ccc3fa904465ed49ffc1d10050234bf93d59a8a3d8e78a250333a293acd64829f78df1d5c4bd8535acf2c37aa3993b8e04c7730de5efe2b220b9654939416084ceb2a2b81b18df5a12a83d163ce77118d0f5ebb32851ef59f186272ab9bd0b1944e58a1ca5d59a4d435a4e87404eae69ecd0215b2a21d79dc44221c47fe49f0b62479b31cb01529fd1bd14de8968e2e87dfc83cef6865a4f49cc0be2720cce9f6235e3894b2aaa89f052a2f35c5e8859254940409904316daabcc7de48d9a165219d990ecdda855a8337bbb5137a8333d76a3209162b9a690b1c712e40e87555f2c17d9c85f49056258b85e92fb8dc5d422645dbc49aaaccec911e6ace27a67385ae5d0c3a55ad1cb8f797ec06851a11268292d278192cda2dd69168d5f4496d1f982654a146284bfdf1030dccd1aa379d6c22c0961831442ef16e23832c4f20b7c37ec39b3b6940d50127229f4199397aa90cd499abf42e49b44b49d44f7c0d373a18731c9d0abfe1c560d5d5a1b9de429dd6e361d17ea1c858ee5e694c76e87f3c36ffebce164c1bcb221c319be7885fdc2a19c88e1cd2b1c6b57e795398a1bd3ea5828446879a55b3d3ed41829e93bfeec4a3e4e639b7433253dcb19c50eb5a451184a9a0ba496312981b9e65117221e9019e8dad1e18dfeaf2e2853bf48bf14c65def8ba4dd95eecb18f54bce97c591c6fda92f336c5f38b320b37ce8ec8cec88f22339d605597724eba26cc7b0dba2f32d046996fd5505dc23442a7c1510744f40c02a7db607570c22f14bbc28252f3181ac20d6b0f9df053f9756e05a617e7b9905ab06a327f82660ec3e714b87aec9bfc6324ec81142b6eb2e539ae9cf4aba1e75052e98c049cd714576a025631a38a8c37e9816bfdcc2e3df5af85ca27474fb39722501301769c7f286ae2889858fcc270798fc4f680b5663c471d5c8fcc8f59fa69fde6b3af4dad4ffbc6f0e442c10a7506f4ed9a153964d0951ee94111bc7535406a45051868d3ebad2de3ecd937e5a0ea0c2a5a07a6b314a09159195d9c944fbf1311e335942bdafbeaf48acd9f2241124ce6c3c1b7a24ec088fef059036f6ce951ab17c6a98f0a369eeb102126c96a06106f76d3515160d879ecc728dab512b28242ba7376eff05beb32af7566d1b4a6668613900856c6014c606742ea1d2744b0ee4d178c64142d66ad0e9e0c8497865a524d42a941733fab449b900a66bdc9b9aaa47d2600f951f149db0e8ab8c73dc49ca8fe3fcd1f8d602ffc16b0a2264d4222a6ada1a5a6d561057d446aafce88682d0fd58f6543113584cdc69c805d8cba057bc50f59564c8476f729c4947319845d017a1076237f44c00ebf07d48b94aa9ec8782707e974107f90ec4397582728e50bcdf7679fe184bd7708ddfae61b2c94e79ab60cfbf00635fa4ede5190ffc647ca5b0d54c05248e15c049b182e9e2c7189806a08fe856739aab848306e0a01bd69abcfd7c873a0f5a428aee0e29fafaa9a5621dd4015065907c69f41aca6b4dc4819810dc21461a9174ae32cab82fc737f9aa81972a0adf766cf6ff8ef005eba336c3c4d617a553049edb6f8b812bee4c37802cbbedd17c6e3f194e499f9139c796c2feaf60454d1598392d7a6361f89cce61d5de16e4801d3fedc2584de0936fffcbdb1d15225c7892ba852500c0aaee5382ea3647fedb3e0be936e16ac5d5b26dc8d56f5f2f502cb8b2f7b44cfb6470071a0732ab2e3796b61bff69abb93814c00e62d2ff5f3cd42036d3893b1eea7e71eec9353c00ee79bfcb92f30803e0e4b1a05ed62a06452d0530c9e2d90d8ba856f3bc9f8e0fe5096960d914f02211f0f0998094d0a17187aa5d3c006e1318e0826393653ef50373c4509a4aa8b91ee00bc576ed11e6b2ae2e24f7c3091cc2b6309755215a8129560da2de97d06920983291e8a9cb7196bcfc03c7b739b58079a885819f1464c6aa402d008b618124a70869154a4c77a9b51680e7d8869497eed59516a08fd51fe53dcf476133b4ae9f8f06b4c5ee459be80e06864d03dc27a97511c18bc2135d559e6494e763d2f047d9ee2278c6494a3a62b83b0ca5e2c6a99aa1282439a2c586c2de733bf71c36577dc32900fc0276c2f094c4093450f4b62a0d3cd5b43aba8756466c3d0dc2f7c6292bada173068c0e378e26c6688e4bcf6a955f9f83bd2815725b81c3074a4db1e7a442c795a8901e25159552491b145f98e5e1f7298be2f3f323587f3070d27ab05d930126e91ba54411df9528001b78482c9f6cead10b9d4f57e306aee46cc77942783e999a0caf31fdca1b39e52e3ff915433682f161e10705731c04d2ad63fe01b46c0449624b0a8d9026a6888fc9ddffa0465376d0466c29e4bed829aa5f22a60a0ab080786061b320b580e6276b26bf90877e3361f60a4c085cc129f618053649d94ff485d2990be50df32c4c137859dbc2e164dc782d0083adee2cbb2b82d18037c4d03e3692bd35110eadf1fb4ae5341327c5b7c13d68c2baa4acc704dc6676ab48bdfa2fa53234546e9982d2cc9c79de74bf9e394332b208e39fb9af032b15769b2b21d538ab1291b38fef60091695489c388c4453897ccb675ff8a21fa2dc2535dacb995da98ebd3fb288ce32611be6b577c654c102eb192730ebd891498108950444cb8113d617d8f38c5bc31b716276af61fdffe5da5e20e06a4432ba273bb9b53975c0aa4c4fc0b5a999c70bc415aacd3436580999356e4a46e72092e7ebc93c080e1f94b7df7382dcf9275889be2fe584dacef20441870895932844def86e74d43eb6c2ea1f94b9800f0f1d27d4ca01200852b741ee12e69f01cd6819d3b4cf928dcb6a1a70ec31c3061c1fc5b501f88a63b4a4590705258a2b69188007b869ef23740ea13d8dd2e90aefd587123c6e0e00e2c978886f5b1ed36a6ce8d8646cd7573c21a2785a04d4de6812798a06bfbaf471601c8a9ee8fe3d3b3d0d143b436ec600f4e313642fb275b42c00a84019cb953f600861bc878236abcacd609cfbf218f8e87c0b9aea37b68686e9a3a4ecdca96f865d922ca9ba2057e8c032488a61b53431884b460c72e369b0ac7ae5992e6d60ee27203162a90c7a756a17a646fcd4e6b9ba65396a5b1a36d76623488c846f28d41d0fa226e2809802b7f7cae6924155f38f6a522a01e5d3cd86605e62d44691c875c34edc6563adbde84c4b52c15efd7f03fbfc5bb86f7e55b90a54c60ac79a1e564fce526060eee0dd6bf2070de33e88e7d286bc86732497c0d9eb1c26e7d4ce379d37ff768ae2f076409976525c52272cea5069679635a34f1ff42a4951a545d9d38e85e84e4e09205494b3a789cb692296d29e403ba0c5ccad93eeb1d088c6187e39469e8f04aa7d42cdab861c93ebb6ded89ea4a728d48b86f796465b34b006d4a81807b6700c29ce79676fabca389e13a93958146894ee5d9bf9f590d34d6b88690c566964c042df82c047fd567562553f621064f6d378059cd3bea90ef2a71f252534c816080f5bbceac844d29ec87d39098becf5ac91eeacf0ecf9a7a498b4c85efefc503298ecc1feeafb932cbb7d0e80281363629359d756fe0a29c895f140c80291ad58357d07905f7d5ff40e328e15dcc8822f9dc50a6aa94f5e1d214b11aee9007340b6c1eda4a0e3d4bfa2c0ae3e9f41884cabad932832ec8318008148a1f120eaed1300e8c7bebe41ecc2336f2aa3013a5cdf0478becd68bd53622ccebbbe6a70c7c6e71f461805c50a5cd11802b3810ec0ceff6c26532617ec41038e3b948806e49b35d552f1b71f0797068d35135963ab6cbd592b2fefc16c54be88774e6fcb177d436065224bde238a9b21b0e0f5ea1192ba1074a33a734733b4f1ffe85a927f103c3cb77cda6a931622c6d7650e1b6d2033e7fc83c9d5ca7d7b77063543f90364d5a365aa86525ca2b0493c89ab1773860535c1d4d2d0891416d0b7f4e7b7e790f7213437040de2f202f6b2f69df8bc24b424dc632a59cc2eb6978a8916953eac4a5bf3c9ebb4b8ba4654084982e0b216424e5b623e424f4a3409e49b97f190b6f589379e9cd07ebb856a0d40f99146c59f80c5e3428111a382b6fa3b8dd076f20eba25d8fa7738d7777e301953ecfc7bfa0cd48b7d2f2b3d1df46dbe023569e55c22729a2a8aea8f675f933fabc2667459a30cb7ee35731588b7d2e83c2c36dbce2e595a837f7385315863dd1af44843a282faf208229de2abc7d5e61bb45e783421f33bfa0defe5454923866ade8a22fd4287df63872cb39688ead077e6a2a1eec00ce6f8a6c00486a1e46076a2410b5cc318796f913ff5561845a225a3985fd1bb0fea15c22e2912e7f561dc5ecd23e299ba97197d3b0a71c9220565728af3a21c54a926307eecc38c078a4e11fc09db20539b62aa2d4c1cbdc4b8f68964935d0464a6a1d46cd26619be9519f5cec57d63a8ee01008b687e964cd9b2f415b1b75d6b935744d76eb69712d6139fed951484dffa000c5118dabc26594f33dd01f61bcd067be779cc6e43d0ae103e5db4fa8dfaec26c6307d6cdff4e4073b3ed71b463c339224423402e0d13cb1fd0e8dfc1549fbc546ebdb528ede3588a80e9fa0d33b96109f6063892c34a1cdbe386ab42f4ec5db89bff51107df62feb54b89f9b380760ba4e7541fc053a769b50f7bf13f6eca303612662b26f4a3281b3e4d9431101a20a31c368517812ed37da39fac4603a3c77d351ea5beea2295ca03c6a802b049f383a58a8ecbcfcaf532d11887a9ab908f76418c279aee2b4003d475b0c7b9ff9a8a632439306d09d7eec601e0881eaea71f594347cf046838defc7058f9bce0139c035f608e0378742a858c6a5aa33443a748725df52386ea18c2db7e0277808f65bfe849e329e60030a8349359450104ab320ea8b71eda15b861c0338f48d52341f787f69ba6f4b8c60b0d1fcf33e3ba7229e0f7e56fbeddbbff1a0dcdcf9f53183065d3369b6266c67f8637d037770e413eab6917eeca27948abae6f6c15d33d9ea8927d51d827efe2695a4c353f430a587d8ab17fb14fd0bfa770df96deb40954a7cd2e80f7affa9791b9dd4631acd7863fe29b9aea4fd7d525baa9f42fa781611f1ee50d66705b96f109633697e7b9f1bd1ca4a82bc9471dac72fa1aeec276474d9d40be96aa29f329e70a623ce3584f07c4a1b6e94eab5abd98c87a126323f455ddf520e3c10d69bd60dfb5852ac9416eba5cedb798b1d347bcbe11d9bd39abcd6d598e6b4615c35e3d58d891859d9fbf911c4d154bdd64352afa18f822873580fd17db62dc106fca5fba8679ab6cf523b6d177b227e3ddc413bbf6e8b8fdb6e16333eb8920f285d2c59dafb29fc4f08b62d76dba92eb1fcbd0cb2055b90fb21b15999d72966fe945b9f7dd9b29546986ba8f2f99d7671872ba5b4145b292ac542339b68d55a6b49282a94c8817945310d2104dd9cb6fb62941c408a154785994e4c6717eb8769751668c9b65129268cb35135a39bfc1ad5c5c3d8e62fa646e584354d00f9262283bd21836b4625107f4d5d1955346223c849fcf159468b99e1a861c84f3245e9197d7bc730d6e569ade69ba8bdb58f4e71915125c9d02346ad91f194c202af42ef34f3f159d6842a503c57210f897ec670f7713444281a2854711945c426c98c12117e3d46c55b01bad1a91174498b94b24ebe1dff94947b1aa4ef67d791736aa9dc76fca1cc1499f9269adad522754d39f96d568563334b65a1fcb6f739de1c49ea00ad51e2e65cb927086715555e28a0e72b3a400252cabd7c03298b73ce81e642623acdf7d7baf28a14b47b91a60395e4e2f7ffa05b61124534a353eb135ca353858a6a85afda5d87f13523b57c646d69099584ae73e42a505127af25846825a56177783588c7281be8f9789f18d7944b00bf2e7f72c73f954e516db47c4290ab7d542924e520115e26bb752c8104f43bf2e30b8cd9bd7bb07867b80276360afa9bb92fecfd8f18fd49703e697e6085dd4c822ad538b422ac80f69537c80d9dab7133cb836579ecbef6050bed3a34e45df47ceebab039bb7afded091861d4ec462a4d0bce9e18b38940e9ac13b6eb1beb463a3724f02e238f28e6eefa2415775e523b358461c41f4935fef41985455595057f24dc3a711f2c482a9b6f2ec2a398e23cb2c03ea68aed4ae92353be4471b0bf45268bdc26eae420f988dcf2bc64d1d949e761cb1fa9fe8387bcf9c09d063cc9d455f8dae6ea639a12a3c8e7fb7de6335e3248f453d4c73dc293c8b3da39a43515b0a502c0dc992b7bcdac06c6660239f62c05067c0049a532335f564b2516b4548b99a55225413bfc46f2a7e75e907f1c96aa872948df549774f34da29221ab48b5c64457aa54b12ca179e32a10b6cd10edc14a95fd876b1eec3f438682420656954acb41a5d5d321105db35af474d7bd0a1c484fd19f3becf1ba4a0fe2bd0dfcfd36f4c0ad4a55b2969f5178c2032b4e42a9d07ee062bd920a58dbd23d9653a96e0de62bc9532ad86f10f123b7874a256f919a5826c624ee37a5ba7f3fdd9f888d8c4a9650c1a5f7f3891986070e464aa517cb5d6cf9d0bebf3fbadfa354bc74cb445d9e75ab3bcf4343a9d6be77cb5d6f12a4fef354b36e559efb843871e856cba7756e2cb7eac17f88dc8a24e2f8712bcbf499a7885b9113ff76d27f0f42edd2db8a5274158376cc0fa00087f20fb23e47f477685b092be35b7ab5ea7ac20aa15b94f9cdb67a6ca07f92a10485c40c1eb615653841f08d3198d3e23e34dfc698581a436c5a9f9e6646b565d300aee59d6e9f0649526fb6795a56b0150db1adf40787469a1dfbd9ea29d613d5b68548a7977d6dc5dde9e5a1513d7836583c199cba88a95216e53590fe93c7f498be6c1ac87df9bb3bc6ebfeef4686e5e33cad3eb7deb5d5246bab7da26051b5952b7c65b583da8a971b47a147dc67dfc00e3738eef72962a86db74ae780e6ce859064a910e9e0422a5453cd81f2d996ceafc789014de944a08bd2233df808e4b27e02668b093d80ce6cda8ae093d1f844153c4f8ca14e2623b207fbc22159b59a044714a103c7d00b97c33e8619f6d31506c1b0269a6ca7e6d087d30414ec5b3975ba8c015ac9a82aa8d530e66ee252abe2a0a8fc97d2c203a6d2568451cc10c4272eab6903aac6d46f8ed7a8c44bc72eafb6e0f5c555b725cc56e84174491e2cc6a5ad86df1fb3d5319441f636176c6162972c57335382f2940976f3a59c7d90c03886905744b7b457c4b4d2b81f3a2e37e31209d6e2c290635bb261de4a920656855c234bd6fb2566072d5f2110ef028cf156f38fc4f24c93f977a3165e6896b40df9e8717f1dd2fc0eb35b81f7cd299b07a0b8d017fb865eeb86002b8a9561b227c7e5d1fc0f8d0310b2a0b8955cc10ad83561cdd4868f4a16e405a346b6aeb95014757d33e9be5b340f42f6fd9fe7f7cad6c1825bf07b6de499f699c63e71d3e472fa4270171acdb95e01a7ea32de4f511b690297bfd8f923ae3498ac83b8ea22983d5c51cfaa6ee45ec3151f7365b8fa75441a491efc8c0ea57b4ca72b5c2dd9617bd6f495e2dc4dc3fa56299065fd7501efdbdd8ea887c329d763e477d321a7af7a8aa418542daa6a156ffeb247119284088027373866ea703615c9b880f2dca09324d9dd329ea8268128ed7198444ba57a1528347eda07f475fbe0c4f98c718f0844d5e050158f310627f2cddc1cfc6637e8522245ec480b10aacc0861c65bd6d3d249c1ac286eccb036853f07975ef6201661361a904e127ac71e1aee04d5b7b99fff9f065357ea85395d366a5298b4d4d15d08b8b77a315adc9e4533b7f8cef28fd395c3806e2750d470bf50f3a074ef99e0f96b5166fa866bc0c2e3e58567adda5415829ce0abe0713e52a836a7b47de8e450cc8cfca8aed496cf3311e3f289c0b42df1da0c68cd7c242a094b97fb4a7d732462368060ad180a4a73f535d6a2d62bfd43b4fdc5cd39c19fe8e78dfbf5e4b5144e807cfdb2606f52bcde4499dbe28f4590cf0222455a5d04881c7bb76d6a39a677e345f9f33fc6c8b452ec4e3fe163cacbbf0609dc4a21c4ef0a27b58e19293a6dd1901e701b43ba17e3803d74514f64008fb048c98eb05a64f9f8b3c30da596604245f1141da1f250e4e41bb1e89260da186d23637e03d14df05d497f7bf40ab66923d5fcae227914246de4fa5fdb447c7fa625b8e869716ef5cf1d3b8ac32c41c295fb60f01194d3acbef642849e067938c45aa489c1f24e7f454e321c12528ec12c0441de24145e3f15cbc13236d3069aacd184e358c0e1b814a8dfb28eee433e1e51af2d63360d1a208bef182db3bb69885e62a3b8dc8524b8d75d6ecadd50f3bd9f5df9c78c36b66604c16988347619a9640a3d08e280b013f7eed93a6d82d65e475b900f8227e183e049fc78d7eb28861dd00e0928dd26e5605257181958d6e64c8656a4363f46982b4054dda1a66c7eb561a1353f6cb163ce9756f14087e7bfe081490107fa472fe6775dd23343af90569580fcb06f5ba5415a37b186f9e013f0aab144eea0ae4365a328b0dea2314815c91cdb735b10065426e7b75f7ad7086e51e93db2d606f1a1da42b25f6045693a8396bb031039da744edc44dc400d3e9238dd029ee81d4028ed9d5480f53469a3b7af4416e0129bfa8605980e22e3c71f3498004804d712671f20317ee6fd20fb516f83c0def8b02ebfbc1c139f000c1739f5504aab7163f729990c144ffe697da3a32a5299a48dd2526676ac6cf1ea0ec7e461d5d81a79bdf526cccf6d6fb71d687bf6808ae7cce77e141ed79aa2b7400710423e56bad04a2afe51023f92e81232d15f087d3ce2570fe7aae549dba054f9fc710c4d5a4a772bb027b9ea3207bcdfccb4b85304288b9a0c73ec456ad240b9d87f10a5d21f026422aec0254c686903716bda5f92eb7163647af446c680f6f53b50fbd813aecf645f995ee23472348250322dff32567ba171b1d4ae9c86eb55df280f1e910e247f77bd50fb58b94a3d21c9adf9b4e2d3b28e55f011ae71bafe72c88ba76492a414c12716e42438f55a204cfe295aac409ec0406d8bfd7592c3e54b37cda344237e3d9772951216f3640c71c09a090612a4cb724a6ce880d58077a1997a0ee6d4cdaeb14e6a8419a778278583d10ade6f8d66b8568058a1fc1e0f170df740a878539c0fa06a3bc2029b3820d4519b6a8456375923be6c1b7754bef15fe51af72aa7b45b8310addc1558837ca674ea5a5541d9ca2b8a265357129ed454f3fecadd4c04e28191157adfea3da5bfb5e2dd7f2cbe133fa0a1ea8bdc83d033a0cb119783163dfcf1d9c4d8b619b18dd7fdae2566b95439c4e4634052b719db9370c0c5704b38e80c924bc271ce37e447e1f81b2c8ab1dd930cd50bface035cbb85f3b96813a7e298686fa8f62788f2bcfaa5ed25504bd09300860fb7cf4e0858e41444b0235ca12a80c6585308592eae4a654b460a779a216528dd1a373b5f02687533ae4850396f163082b47c33d28aa076e02c340439499db1ce6c3908b12c01b5096383d244fbd8f7beb3e5f001753a4a9d538b15ebe92d36b1826b842fcfb0f966e8aa9b00103cfe9aeedd68036079a7c35e446f9f090ad5391bd649fe433466d5372e7d1d176312f9ee6e77447c2fa8662fcf110a42e8bc44a6e4021116c15e124e2ff1650578a3649c8a2904df0cb51a048f4e18b7858f4c9a9b1de3d48991c041ccc339db71b5033184aa15f228122cc68920f37b720ffe2e1f0fd332921accf9edf56c14a42a0e09faf30340f54ca192ec86043ae4e6084a96e78b0b582ec36f4f6711f3df22aa90fdf06b7367cd7cb16370a430d2b995fa13ddd5f637ff968ccb40b764d65342b90bc55f7e9da220c784b5d464d7f1c677c82557e6cc8b9e030c3a86848fce054ed3ed490c2e1bc93ac232f340183be16708e44c75144957a262fe5879665ae3679c6cc7013154a9b47a45b649c552b9444a9084dece78fe0b217f1886f68fd3ebaca8e0a22dc20c9d17fc080941f4f84eaf4dbdc710af8541d8e0cb2e6d8f3a6691cc63d59d894bc9167af7c330bb0b32c0ec2031ae5619481c43fb216963e81ba0afee753fd0989b42cb218969164feae3c8b1cb49e27591ee613b02613eebc6aa0426c69b35804f2a16c8807af229fc20978505cbfe8a5708cbed60c5455cd67c04b2628053d692643157a9fe9637fd589040166b666996b6d43c8ac0ff956a0d62dba9eb31ee5476bb7510e6aa817fb8335844ee15c3e79107ce4867dc3d1cb49e10d12d3598d8a08389908fafdaf8a23d365a60d25936ad872c035f2e27d668652896d32b5e42311ba3700ed07ca0793d6bfe2f3c03c5781fa1ae86772420310fb55ed6aa935445c02960439f3dafe501708bf72f64619534abeeb3eef86630705b7146434ec10b7f776def7d54f5ea9d7c86079ac3b9b57329901a7ed2b4a0364a8fc1c8de80b3c9385bc687108db00056709b491e7e269f79726d38be6813c7da0400ab17947b7fffa43fa105ee45e14b6442cc365d3ac2037cd0047e9004180e5620581aa5039a0c941ba5ba09822826cad2a324722bb14b46aa3b19225302a634710515e43db9c39fa3a51a83290af5b0b01fe203a0081bec146ca4449b08c43217f1db231f674180cfacb54dc8de9b6c29a59432a5147a0d5a0d280ed2f771d07c668c5408f5903a9944efbcc771d1cf6d37d87db413c5db923c9fe76ec341f379beeb1c1542bd93f7b66e9662053b4b769da3958b7e33215a91f7cff9aedb5d9d7586e262dedd1556a76b981ddddd52abd3cedb1955be62da0adb43e637e90c2c5fb3c6b55a301d971043efed077e8b06b15b4650f2ed03af0ef4a1f9423be62fdfcc07e62693ef05b9ad6d9f634668f2d8b626b7767588ba31e70c88e5985f0fe30c485e2f0b329d9de9ae0ed7a6bc3a5b6dab88dcea101345ee2e576b019d6bb6ca8f2ddf6e030ebd6a55affab5a2a2a2207c782c95ef59a3f4adb2c15ab50ebdb35c79eb21841915563d1e495998f44efbb4bf6d88db7ef4ead900847639ced7e5382d31987e5d40a44be9cffba7c7b5f59c738e06393ff96637385a9dd7f60add06b3fce43dbd55fe5a44e67f10aa2e60b10306e99d0b5517c62c79e735c79829bca34eb6227f9b0f3022c1c487a98d7376b6717402303089c10fc6f8b0ef9b867056f3d7d531c61863f476d23bc618f75b3ed9b2a547796a1968eb9587a757d569e8d5f429430dbdaada8d0dd3a7cbd0abcb6d80586fb9f5d74daffaca367edf57b91df6e9176743981a611f773bec337db3203cef888024c02608cf3bcae31dad4d31bf2276092102ff79633eb314fd0a9fe5e8adb77e3320d663fe36e7561091cc3b95bd1d616c5134e76bbf7684b145531e9e3fe6f27a498052f4ce55500092a4189de37811092610462498bcf39a2f8edefbe2492eee3177d9db78905cdef94cb65bde658ff9b5f5eff3cb9b87ddf2ce659eb74f1f0e9b59907622f0ddf46bdbbf2676350d22e2fce2c1f4ceb58bf2e09dfd38b731b9f5d6b53c908c88304110495aa2f8f0f8d9abe8d3b15e499f73fae24c1ab44b191a5763a1b51b1109263e6d24290049128fd911c616ee37890c36b67c7d4e0408a45f87b294072cb4fe4d22030cff3e94e79209ff1480440b9ba642fc9be4850bfe0813b9e6209091052443c943089d0c2effc26f1219542611235328e952f4cf7d9da911288ff2408d10f9b82c4e505234c4bd12386767f7e7ecaf8b303f06978fee86e4304616ced944771c950e3b4481557ed0053a6fe7909939bafb81e5a043af6a6aabd8d02b7e2886987f0fb74a9687be75b73c8c427af8f6651f8abf1b3f4eaf84788c0feb43314806be3db62f37ddd5c1f487adcc068c331fce49bfe45649f0d18d94a0e791f6d1eed6a2b53d68d21a63cc80385a82eecdf9c51dc405e14883b077aa57d2a1dfc82d6e9b84a5cbc70dbed6ab8d0edd78e38ddf6cccea588f1b8ff9cd8a56c77a4fc7ad8b7e33201dc4b3b7f3626d71551a58abe995c9d753bdba7cfa12bd69cb7c6e44d765babca7b1cd66bae59915fffccaf2cf4d7e2d47a18b7f6ec1e03515da266159f2d12f2c46ff3cf3cbc2e2f6fcba88386e153a66591bbc1883973f76a12e0fb209221dba945990e797cb930c32a53cc9cba75f96c9f72d13f531dd722b08ba452bbfe4b738f36172cb9409796ea241d0eb739809f981e8310fca843c97b446c7a8e57b99b0cd305a8d3ce671436dae89d82d00fb20413911bb0588ce3e3d0edb4c54c88873d4b63e9709436deb63725f89362117c4a241993fcf7cb39f18dcd03277411e90cb23ad995fd4e672e9cf336a73d120d363dc4c1e34e59441a69c409e5bb446bffc0a32d5e8189629fffcaa98db58be7f79dc6c2cea03fae517356d98577eec72cbafcb9d5b5488c758ee17ad268f17ffc6fc32541a26c6337410bf7ee2c1d32be744a6c69e5c9703728dc4e448a038bfac6d93baafc6b414156536bc4c887be717dc56e2a7101eebe5013d3fdd65411af5d22f86a77ae4e847cf4fef0c8887173a020473c1014452fcf441c4e48574e0d3a91e61b2aba34ee7e8ec7262a6f5b92cccb4ad8fd51ea9a91e792274f9e8ad156d453e57632eba739990e7ee83dcca4877755c10bb4d902a82d8b18808571c1190fcb0d42be9ce2fc70f81e0f3869b0fe7cfa7cbe83f107da43f40df56495a1fdc884c244204f1807817643e482b3ff4117d57a7426fffb8c1476d80b847831a725b71316fde46da7ac5767b7fc2bc7c911954b6f163db63bddd674522a6e58b60a7ce84f0fafef8f02400ff7a5727fa73ae990ff675d3f690edd32608bb747649d94dcfd474392cba6c8feba6157251d3ea34177ddceafcee201c522d00929b07571b69ecd7b57d85f6b03cd3876d03866d410dd4cedb4db44d0283cb9bf60c296f7accafcb619c014db7dcb455cb832e37f56693f97bf79b796ffdd9b5ab8372e8bd9d1cb5d5f6aaf9683fd1ca9df968d7bcc7699b4d90f693b79fe86613c4f4eda6ef853252733b1bca4f9910d363cec2f6700e332136abe31ca32caccec94f1b17fd66405ce444c67b5a733b3214153da8dde4b57d6d20d2c264eae9eb653f7abc1b48bbc9834cabe3fc5a1d57a404ed1505f1993bbfac2ba328dfd5b1507ec2dcfde5a7eb645d9e6d3e9c5098f7340af3d395616e5d18ddd57151cb4ddeecdb1b9491546b55a4f421ad6955bb9f5aa6e06a2330acb491067f93c0e8e219861cdddb174dbc736e99e96e73759656207eddb5e11c1a655996617e79966559e68b937510d7755dd7755d972fcec586655996655996e58b63a921a594524aa97197ad1bbe5641e7b93a6d4087edd0dfdc1ed6f6f871beac8626a55f28e7ce39e79c5be7a25f1ce7c661b44d02c38a87fe13e30614a537941c3972a8e038bcc6693c478e1c3972b441434333e33234343434345e5028d4c95128140aa584ebba4c6e5dd7755d635cbbcbce0fb42fc463b2d591d8ea84d1a2948e6d8f76e9d9f6d897de9b14c2b56e795cef85b422fd42ef6f6a16a7390044ba74f9567c964f8fefcd686d45f8df56847fba6c5f9d9d34687af477c3f468d14af4d67cce965f939999997dbef6c9cc3fdbead9b39d1bb5bed9bcde6c9ebfed47cfefea782ca50bf1181f86b01762c0730f40747000d1cfb55c6e757ad073f9ace9ce9a6eb27c1062c0f7ea3c1f8a6fb2361f4cd3f29e9e969bdeb3e8ae8e97f980becdb9716de7ecc7874727259a3c4d00fd82ce2fce1c6e4cc5e7e2daeea4942edd351532b2eeb67dc7be0939962e7423fd9a5da56cd5a432baad3a2fa23d915714c4d7e942524c1063bec8fce9bc552be879fb85cd29bd4e179a488408e27d28129fbd88fc1f885e3a673f32faac43fd268f262ac462fe39b501f2d31e37aed2a3f756a4a7c7aed185a6cb75de82fab9aefcd3d7a503715f7f207a1ffb8f0639774783d67b2d9643d4d4cf9af75ff75dedf0c6c560b43e81a184c1ac71a180448f1edd347d7d583ead4d336d57dc8af0577ecb792bc26f59decbdbefc4d42d1a34fdd27c7b6026fe6bb31f93f3f8a0e997c9375f1d1edfaa9d1f834cbf286aa70ce6af8fd09cd36d155b265a89dee4bc5566b7bcc7b55f97c99a9b8caba31dbed5d1efd9da5caf8ef6ea00222d9e5747bb0388b4f85d1ded9669db08cdb9f66e7fd3f9b8cdad7285ce2e371ba642fc71004f24396a71d003f01552366ad5be51aff69f73607bf8c0e39dac13ed129c7d69a44888d0f1899e84c687fd391bf5aaa61279366ad5731e5f6b1e49f422adb2d13fafab847f6e645d733952586203296c1045cbf3e1ade85591c41442b440073e4888d0f1614f42e3b38e8488f6919e64013eec714bb2001f235070603732c4e53ce7279f4b6a040a0ed1991af1f7760a4500c1091a635051c6107cd6e735d1850ea28842098b0cd4f0599ff5ca03072f2af261a35e05e09ff351f79be26a2156858b07116a5f08be77e49db319ff28acb11e79497988b44627e2b0f0f29046afcff9f96641f60b253e5227f3825e8f7c6577d089102d79c9c37e71f4d0eb083e4acac36e791879500092a48774e9c90e7684b105ad40bc730603c9dffc260d61097f3506372746d68fa0c68c19b3c4c7127263c6bc93a21690708112263ec0c02406558a867ad82defa46847185b141551009224208af68b239ff53a828742ec161f8b16ed169ff56ae421adec6e77d44db212c50ff13b2952e1085898e08ca2229fa546d68ff010050bc408821621292af259af3d6c36811d61288d99804c194778c11b261833c6a7e65dbce262bf789ff035ad226246c0b5ce6eb8f736e8cc12c2e8ce2584f08adb4dc668fc34e4486f5a05e9bed70dc6c53eb707532fa07c4dabbaf05b9d15e2314db755d33167c1737b40c76eba8beea23b9761469703deb42a3afce926f4747316dde6397fbfcd3b233a94f4a655f1f1bfb93adb5daef6b73d7a4b5a5ac2d7a0d8dff660ca84f79bd561ba0db40f5767899af904d73af4b73da2afc3edc11b8f5641dff23a5c9d0c4647caed98b687afce74e8aa5e69ad9a96bb9dd939d3733c74d839d3a16fd2e7d81efcd0e34daba6bb32e2e6d39d2f59f1a9564d1be9eefb6d244d5acaf29bb48484af69d5f428f4dea237ad9a7075d6377babb3fcb05d0c2e7e93dc32e8d7b5f920facd7c485f0f92be8e6d55ba252cff3cf3b1b5b5f1cfe9d64e5aa76f5fce139b5b5f98636eeacc31cb3913020289104114bd500d411b6e8cf91f76e98912237efd07a25fbad8856dfbd95681f8eb868c5620fe726c29e630f3617a37798f5b8c06994cbe54c8c857f7a639e77e9c6f731bd1c58f07707aafa0cb003d15e5a1dff4aa1fa6da3974e858af36a9888bcbd06a305f32f2c1d7bdf25c4ee82f3dca544fafeafcd1abaae9d02be93448cf81cb4bbf40afde4bb758785ef6e0eb76b137576ff209bfe0a5f7a79abcf46b9bd79d8478ccafd62be752faec15bb74ac57389206ad1787c5b5fc45db2430a67c9d9b74869877bedfeeda75633ef32ba3363ff63307dacf9ce8b1adfa63eeb6aa83f68c799b9cf46b237ab70dc13749b7b6da59eafc476b5af5fc23ada96f264e8b26c53d7fde51de19d765e35c3a14f6e8bb3dda04a83163c68cf191deaf739e5f9cfdbce75ace0d5b1de8712362fa20414284fbec1ea0ca3c742278f8ec16a0d23cf46c1100bdd7d06aca75a1d5d4c35e4012f791747d1cc6ed8caeedd13d604ae71786168a68cfd76b70ace26a57d3d7b5e59da1df7b3e0c69bbe379d7accedb9ce732b8a09382e15f0bff6adc8eea72a1baf0d04991f9cf556e6788cb813e5526d543bf86b8399768c5b10464fbf6ab97fbc4b46657e55c6e73dfaa56e588f8d0bebfbf9b0a197192c47d328a43ab86f40b34e7edb3a30baeed22a5228c5463b47616f80d6d95a0d0f215fb6e261a912270086a29021d0b31b4e1dec603cf0739c75cf78ff4e91dfd7925c2f3d32dbf261234e9fb6d6df3a79022fc4c1f0f3c1f69d073489dd6475cdbabad9b44648323a42c514943c9c9127caad63fc4ed3c9f751eb7f32400e5054f98b284832d463eebdb81e756d285db348206a1af2afaf6cac883d98fe48e91da0069488b745b3902a57d216ddf9f47db31235ddbf0b529d219df7b16c72ba92bfc3856b155627cef3907dd73ce3d27c605accb31cce45986694f622125848f2333e4e7c5ebeeac27a6bd4b663f1784cf2dc7b0f7f82f6c3ae79c73ae5fbb6e1e17cb2792ca7bec8bf764941bd0fe94f1f9625dc0e8f341289f8412c2171f7cefc1d7e43df8b6ac94d141f8e283ef3df886f0e206b41f657410c792e3ecb9a5e7e20b1e6c29bdce8a69ed462f5dfadc5a260501e993aa7079b76d1210a8fc4ce2a2e8638cb12946b5563d3af7dd3c8490a584102e417f10d6f770be39d7418e9cfa07df7b0cd9650ea27b10e6d0aae975cad48d673f10f2cf4d6e10ee7bcf41f79c733c22472d9c16a0fdd5697eec96bc3c7efdba1d373337fba061bff7de49e69d73b0fba173ae5fbbee76fd0573bb2e13c332e7ba1d3733374f71d161dc76caf07b2f7a74f1c5f716bae79c7b6e0bf87e7e207427b343abace91eb718c268f09ad94f9c6e675aceb984f3653fbc4bceb907fdb9d9393d39aec117ea395fcff9db7ee06799fdf24db91feb55f6989b566ededda6b506d2fa205cb7dcbcbcbbbbdbcb3e10024babb05e0921a5b118a79479eef53dd16c0c6b156f1623ad5c63652dbf4a415479e7daaf5210527e89b31bfc3dad55356fabefdf934a26f0f2ab244495779efa550a82c95f8e57442bb3ade207b10c412f2a2a1a826238e71cbb73dc2e0b0d8f86eed72dc6f3319af3fde9e6cc0df503914942658a67671a5e26099516bc67197053f6eca737632fa214c4512582bd73b8ff1e1bc5182174479777447f1bf4b8bd7621cf3dc92de55a9c7a4153c480903f8984e4cb23cfcb6e09c981cad0aae7f2a95af57c6e3a5af53c6e3c9e7711b4e937bd63a48722f76123f800eccf53b41aa1b0c709b9ef232f3dfa2c82b6cf31ad5750d2d72ade24e88226bffd26b15134852e50da6fcd6f86417e99f48bee1bc23e5f96339dd815ad6ed2ce9cceab27b96cdf8a16241d7ef5bf1c47a583870c3cd786b27cb3cba239b48adf4bdae3203dc52da5d5a4aede077b080f4276d86068d153bdb2a2f1d7ade1870d4370c8a157277795bfb211bc83288c97ff1186cb3baf61bbd8cc442bf3b3b20da3f5992a4b9fdb50432d199b987031b60b3db290874949dd6691928d5ef623a7f4b897fdc0b8bd82f4499983d13e2117cca155ce7bdcfee664c6519b9324ee33e357365da9e8e857f84d1af3e52f8741266f89436a25fc3944e7a69a79a4958d7cf043cbddfa8c3eb345cc353fb90ca33c5a945d2ead260d28c8f9fb130db2e849be0cbec46890e5273f35e6160d720edf6542e03b2cc2ebf293cbcda2157b29e58c4b89c968979bd968dcdae6942eb3d1f8663e30ca4fb3bd9e9ed964fcb4617e595bc5decab11cdb7c2cb73b65d926b3cdceb1b694a4d508ed713052e7124221f7687d7fc4119dfa34046ac22da846771b51a8eb6a878af2e43d2db730475d17eaba50dee350960ff5a3f61995a150966328c7accb6472944ce69857cd330bf6cc859ac92cdaab93d7ac6568fd81a86a74c32ecba2fb56d21ae1b3d6b18c315b5e00bf4963a48c79f2bcc46fd21b4afe42ed9b36edfbd267a42722233333d1679082aae5d08f7cdc2c9f9979511ef5c3991ba09bea7b199ff2067e8c8de01d11e533a7d3cce934e33d6e26ce386a0bca66b4d3c96766a2a3661c154f54c8fdc97b5adbea910f62cf7cc67bda1de595c6b5ed1d1c5e871c0ba7198fae398e6d3b47a351860ef55bd335df5ec940d75e0d1dea3fd1fa0351a5a143fd33b4f2a2e8299afc79b6b111d8a66541ae37d16a4d2a634dab183e8ab58afd0813c9b4c4c28d24ddd087faea7e03c91be037c98da52cb647e8b1b32d48c88813723e885efa15b32cb66759c7e71b7daf2cf318a33b4785dcf30d2ef3f5cd60e66e24b9819441cf3087310645fafc32399447d7e2890e598ec2e890fb8b56a2b766acc1b29654b421a68d2b7fa1e86b1fea67307f80df24315478b7aa35367390cbefb6a07d970171ff9c25adbc5e990ed1ca463f524c161f80df243146ff239d3319afd1515e7b4773e932db768ecc4e39153a6adb569d6846b14887dc47af308630d1eb506f16970155a22047dba2433ddbd9084983d8a3c30829d6aabdfe35ad393594c19b3c858dfe45a771b9cdf89543afd8314dc627cdea9b590674b9f337dffef3f9bccfce4658f6c3d3afece75dd7dcae49b52732329fe7d0ab24f355ee2de8e4477eae694ecee12ce43ec86404cfe09ceb44f9a495d67e93d1b3cf499d333947834c7ef2e9b56a3f25919123f24d46fa2afdc8f3c37278de958fbe4a375d05d997d7933b5ad9e8a59bdcb177d1a17e8c06713d61352877d4f97413c570f874ce6e38b13f3a3713539b930399ce3e9d3d875e25e119d81fed9ac3a5f30b95013d671ae4d8a5bbf96a0eea38cb80a6073d5a794e76774df98d4fd17934e62440d32f971bfbdba4abb0618e63cbbc46e699e6d02ae933349b8d09e533338ce21924bddc9bbc4aafd24d34c8f2e99857135b34189d337ea2d60cb531b9f4e9d2d93d9f5c3ae62cf0c951986bdbb64a7399edb54aa3d9361d45eb6679e9f1443323a6678e790e6c041bcdcdb4bd5645b7629c948de01d32cef0cc0ec06079ff4d5aa38b3048cfe337090c975f638a147e20e6b1cfbccabf36a83c027e93da306223cbab7e93d8c8828d2e7f3d3bd2bf67f2d0351bcb2fdfc74c8ed587d4b0480b7b6b5a2ad5a72c88e939cb807ac56edf39fb3b42326d2ffb713a381c345075f176f21572e0709a8b69e68edd6472ed2676931bea6c0b42f991e7bd61eebe119275650064b4f6a31c00b4366ff521316d4755a68c726ef8b5a9f61c898c1c61574bf0cffe9a7c8d3ed441ec47beb7ea80b8976e32bd8f9e39cfea5bf235baaffc9206bd4cc84dbff43ad4b43ea48f3ed4df261a93c941071d6c674763a2f1da5ae29c2693f30a5e670eaf1a0eaf29886a77527a539b163cbac979b81d1d5c7016dc539bab60e335aa1907e26c72e74f09cfd0eeb218c26843fd5d87ba3ea437f96943654099370d6a47f92933b90e00f03adfc6b777f8c837eaee2ed03adbb90b2697b9cc311eae3d33d1761700a042cad13a0459b8aaa0b66e61dbc00ead82cea3356fcb3bea5302c47ef2b8b5bb2dfacd86798fe3cb6db6eaa76ddf51ab163a0bb4ce15689d295ab51cb46a2ad09ac2416bea9d86567f95a6c99969cccc92064d37b9e604cf50f95f36b97cd33af42e16626803d58e79b310431bda107c140dca5cba096393c97483f48cce5635c6cc7d8349f38c561e823f5da336964797cec3ed5087ae83dba18b0506af4ebda65ef0aae1380c948f1c67ce17e83a484d1e9d85f73ef393a43184d1ea10fccca3631e3320f6a08c567e93a31c6642b067e7a3cc21d3fa03d19b1ce558d33af4dee4275a5bcb474fa5aaf670691d826ff2e8efa8574ef00cfb98bfec077b489b69d3a155d0ad6d03ad823e371e6db6ea7a499f13bc235266d242df80692f78669a008109223c63e0ec93d17547d1c5076e9c4009a3244cf8d4d43fd1a4a0c6d09145052888a20a33ce70814fa429bf49fd4dcaedf430993f5e0d3b299733e50a3392969220e1e2e3761640460a84a0a404174008c3074ae18887a6875268e2e1f5508c87d079bc293b7b63293654722d4eddae34fcaeaca177a6cffa90de995f9ebccc6f921a52889c14b5612589222e8aa0f0d9217cf4d7abd3c72f603e7ec4f2d1273a8f1a6c30250933c002052794f089b412e179489da6bed07c5886ce61771b8f563d1ff6a52db0a1a57e93be6879d36fd2972a29d9fdc5b51d052c5f677d5fa4ccc9e43fa0c693af530d26efb9ee77aeff837ce0efe19c73ef64fab55d45ab338d34bcb80c872ef2b1fcda2c26993e96b757e7b355bc39b16805e2a1d7e88eb62085c61f9de96c15070df53f5ac4bd739a7f599000bca34e4430bd331c5e515151110e5c5484c326f94cba2ee725a571c4bc8dad4ebbdbd228f212e6cfe8f2f5fd7a91c274def137c90b14a670b591d6bf49608079f63df148b11751befd0687b5b9db6a87f5e6e23b085f31272ed6a2614d30da02bc1780af44f0c83fafd60d6b06dcde061d165e7c262dda2f8e9e57eb5d007ebf38f209c0f3603076769278fe9c33211cc4afdb445f2496cb6db77a04872ef2996e6d44f0f8c822efa1db0061260f61257a9b48870450be7df6267d4ff7740b6d68f3578989265498508282240f37873584b01bbed79bf3b83d6f7f3bebc3ce79f7fce8bdb15125fae8be198ced98aef78a9dfaa302e609f0abb44415e8bd63f269c56d2143ffdc62376daf73a26f2684c8d0d775225825e235b2a0dc13da3efb72179749cc9b1c73a5a32e3f805fa5232641989f8518dad086eafc94a76c3c87abe02ca0501be6d9d6ecdad1d16bbec25191125ede8fde292991e49ddfe0bccb809cbc31cd5119b5c1289019198af2f6cc7b1caacef8ba036c9c06436d32bed903f86d502e436308a3a13cf387c5ec06cc35476d31bb21f39691a13634aed16a2364c6657c7fa8dad05020ed32aff9fed0b7cf78947198fde0b7716dab9a633486309a4689602fe3ed362e43836c9c8606ade01a11ec35efada22811ec6de8b62ae5d90abecec256dfabe02e0bc2ae79674056a0af5529ead0355a73d06d950afe5aa542df80e3061c38b67a2403926d9c05a99a673a3ccafd0db55d738f83d612fce597108da268853ee4feaaaf55590dad71e89d01e9aff16baba1fb7a857906b7ccb3ccad6d88b3a1f79703597eb97b429b6e13845f737ecd9b06614c6d4cafb48494d7f1abb484d107611910d36bbe9910cc4d6eda2e6d43b19b9494d0f27b41acb73af4334ee3db3b373454c667b66d950cadf3b3cc6fbcc765dbc97bab44b08f8eda8af06bfe3220fd9a9bdc442be6daa56d28ce84b4b6f577e643a3955f739477c53e7319baadcafab3ad662e51ae01d9569dbc29d6aa93f7348a0661ce5e334a047b8c9ab49f43bfeeb21fc844b02fc24070fc0a9d21011f7fa694c4956f8f010dadbd57d387e79709732743efcced7db63941b98572ed27c560f4f7fe59d4c6724831985974161142fdf493bba52c5cdc972223a42651a654b9a2a5cb0303915ca80d0994cc89701f282f6641a23f5a29c6d90d3532d3341f80f62d07e27ffd2ffb39d1dd1df5f2f6cc3b0b5282bf32779ed197058954e2143a52f4d22f8bcecbb12c88a3d2aa8c62dbfecc525e40af12b3208f3a2aad2ac232892ddf186737a0a4b3f49e76d6e6acac8ef3ebf1f23e787afe5c6e575a55827f8fbaa5d57142d05c92a3e2aaf46ae111d6d38a2424a2fc2a19251945e9e9cb72475adda41eafe147f4f79ca757267f9cf953692919fed1b09d32e8aa56b167db62d384032dcb3bdb56599656f34cf432da44baf53d2fb9a5ab5e4b7d5cdb691c0009274c3082092db82863c370c2890e9830e20d13b0c0cb13963ca1021578310331d4f0a9feec45454542f868bd5a287c048ad8620b31626c89420b7c2c2025892bb43821086920f9583963096f7061841645f8ccdfcc070ce8c1112b408a421419d0e0093bc611c4d8620c2fbca80005d785b31dc1f1cb0bce06e57294bfebf2cb19e55186e070f4b2f852df93f7047a0fd087e08a79bd823df07b498cde93ee25e915caa13f23a1dd990fe5d7f6aaa4521a4442eb2b4b1e7709c9e56c8fdde1cdb97cad152383e4eaa157a8cb695c887a3fec55fbdc305a100fa9573ec143c84bfe6d32fb110ae27fb9f48b378bfaa0288aa238adbafc7218c68db9d3aece6561d706a6872125b47660902eeabc37fe29a3b50ac9431aa7a10e8c13f390deeb25d84bbdd44b484e8c7a960b3dc498fd6c7e31ea594f9e14a7d2de93f7049502cd7286d8aeb63b4350cecfd951db1b43dbb7dcfd653f9b0fe1194c0e1d02dbe339f408ec4a3ac4f13a7760f0aaf5c0e1bb3b7273d871b8dcdcaa7807bbe5c2b3c3b0ede6e0a09efdf5b35b5bbddc84a271f62bcaf9020c2f784fbfb90599dc077e6b4a46a150285a51ee4126bad4889374937ee272bc24265ae7bfb07552aba0c3b03595ce817ec9b9c4628284f49ebc27bda20efd59d13b380efd25bd2a6fe965f1a8bc2e20f59e7eb35528fef7e43d79563c4a0443b910ea99deb8101a675a847da0547023bd7a8ea4871fbd42d1ea35f48ac6a15f56d0b68afa10bf89c63086d6d3ef89db716028adf32dec59d12af8a23c7c2e8966e32d3800dc795d2f3df4ea597ed42b00387446ead5ccd8a805aface4211294c730860684a241281fe2a7f1a0b7e421b91d076665c2965fd3d02a084f26a75b67d12ae8385b4b7139f0c9a7bcf6c64918c9c645ad82eec040495dd82abf0b26da43ab20bb3b0b6ee33d0c01d12b77e8467ac5535bc17757f09edeb667c58bd22ae82febc17a28eaf43d6915f4161c005edf93877e890008980939397bcc829cbc875e650e7d088899053939fb66428f56cc2fef712e6cfb00d826d62ae8379b131cb7d958d8a0afb0f9f0525b8ead622a6cd0716c3ec89a6d889f86d6d02ae843e00c95a129b78343b7739e437aca5ed560ab541d5d055de800bc11fdba16ebc1ede0f0e8d8b5028e8d37c7798dcc3ba7d97673aaf5ce05c1882d8cf448493e3a2fe9d572f92018713485ca476c7bb54f466b157cecd163e8a249dac3d0c59bd4a80faefcb21feb83e85f0f43bd92f1e85cc6ee682e277aea0c4fe363988fcb2e27fa9c9aa6b15ffca819cd655cc988c94bd7aef722e32ccdc76a6edee1bc8b6e9a6bc2f848520875cc689a8ce632b408ff0cad3e24447ae741da7baf0b84b2651c0f199722458a944bf54e0a26c5edec7292940be3a3a31d7857c711c1791b217ef622efa50ba15e6aae8310e91a2dc2cf7403ad8a98dbeaeb55658f3bb42ac62368553531998da7b42a3a0fb7c3492e27baa752da745290d06490712d7a5bd12b192ae93a77e9d5f428b9f04aa34f4baa89d64b76a257192c476aa38fd1af45a2c2a48b277be5a3639a46b7554e864bcacd62408256dfbba87484155c7ab54a4678f965c2c7553aa249e42e1ffd9a98e32dbd9a9657763eb31f76d7327cae97fd5ddbd259cbf6d88fde487ae5bc07dfdd8cb76d158f5631bd4036374cca9c7c954e8014bdb2169692658a97242abd8a95957cf4e8ac257a54d22b9447ef71359b0ffc958f7a703b53f377d1759017b0b6b70d89be3891067594ac01f0c5e5d76a35b55be5c7f6e8bf9c0810bd727e3990dd49b99ccb2f2b2a31fef2ddcbeb96f197efbc2ede4e90e4b7cbaf5292257f7136b3f730a6af55d22f88c9adcaf02c0374dc70613cd900893ffdfda4f5c8cb565d9894a9514a92e42bcf5f0e69e8d5e53d3fdcab6c0e3b462b3625ad35da4e7a35713d1331fdd1aacbb7a5dbd0ab489f5fbed9cf947e7151fcc1b57d2d47418909900441882fce4a5ae22d9dd33169d29ae22e8e085a0c6134cef295e1b3169702cd579079cedf436687d98f16d7298db570612fef8cd7d22b29c4f2a7654b10cb9f4f97b46eabb05649eabc44b9dc11bf9e5ae179d6d41b6c7776e573d7198423927e837044941fc06f108e40924d8ab5aa479d29c61cb6ea6d8f873b027b15da2a1501e64dee6fb2fc5a5ac1f22a6379b59e9d7787c7e558ce9cfd989cf9f26e667fdf19f338cd62bfb665301e9eedd16ff90576e89573cb13b03bd3e558ae290161ad5c966599361d1b8f5659aa5659ecd0799c855d6e4d8c5a97bb0cc89f4da6cb994d2eb381e84d6cdadee258b009d7fadcc24b9488b8f2970f5a85282ec7af12114f3c685222c65f25224e2067d698a733aca70493ccafd21057f8521a828af5ab34040e18c37e9586b802bb4cd694d9e939b19ac000d5f383e88a1e68a90116272df8c2022017480186081d3891c5116b684d8aa066a43c75333dae00ca11942aaae0c2074a65bc210528928e7014250a94004c71e585244cca012f86a012068a1329de58228021224b193a10828c27a638c2a658225386a5c2afd2104873052efceea43687777adcbe907a1e82ed58a905452ff3abc482a3ef301ab33b4e95b4a755ec4478fe47abb886564d21c4497a8fdc7e4829a5af1a2f5f2ab5e08997524ae7412b119e2c29b8f170d75b0aa773d859c7b3a1f5fc703b497c360901bf4a48ccf8177e9590242dc125f822cb57c50c72c083222dbc3db6077bf63e9affb18d0f7ef67e7e3868b1e76a32f0f0f88165880c327c37501b47b4c184d6c60abc8d29546e494dab1ac974f9f82b7c7e3989be3ecf07670bd02e73be07f0ed302c02daa7a061adda2a364487ecad6a4f7595f6ba5c2ed0abba5436a9a9705939a030536cce423a83ed1c6cce7651abd81f11d7e2ccce7e3493c35cf643c343034a8b15e3c97e68f8d12a77fda676274368fd76b13ace2b4cadb033987cdd667b58be7eb33d26e6eb34382fe99d1f2ec7f90e192a0cbf94a785dd495569f28ea90c8d0a3e64c88f21360c719d04478e4e12a50c0ac551a60a3333ded33fdcce1c32e39776a235dee382579e5a809c7fbe01c02bcfd082d7f86fbe8d57ecdd57d81d4ef9f3d4f660c11fad4468f8e73cadea3982f6bcf23c0d3d5dac748156171e001b0556c7790b5b057887f39bed02ad726eb3e9d02ae7be6da0739e054a64759ca7b61c56c779bfa1a5fc616992da7889557087e3bdb94af9f3dd98fdbcf97ce47670dcad40e9e6ed717394a7b66d550eef71a76d1f670362e388cdd97f6103628389cdd9a71b101b2bd89cfd6bdb362036a6d89cfdde726cb58b168d77aec28663abf36b361eb7a3b91c32b47d49b37117331b2f653f3294abb4ca798c5c6cce82b139fb6c90c18619dac65d7095311a3f1fe78cc4a555cbd3c356b1393b83cdd96723076cf040c7f35f8d35865dfea8966d36419e4b9734e8e46f9fbbb4ca5d272a04f43e88fb489d2471f6c25c2eb0815e11e955dd1c74d8a1875ed5adf2230c0fc27011068c3064bcf9bb7f410cc2b73de4bbdcf8e35b959091ef5d9574ea8edc0b341dffccd89c75c254430bd3c6e6ecee0e101baa5dc99fa7e37f6c68954bb95495c55265b1fc56713bebf39591b0cf58aab82ad2abe4e0c6b8339739b51cb4e690ee3d7d49376dbc6e75a4cfedad8e849b23dddd38c1f1d94b5781e7aca4557cd42ae7437eb43045abcc65bb1c7de9158be955e5e1521d0a927ce5e1e9d57320369cb039abc6b2d10613a0364cb039eb1cf6510b4ddbb89e5fd98f6381566bfef903a2576e6c8f7ee75d44c13860092fbf4a4d7cf9da47ff9cbd3c6729cfdf7b4fd352ee2a95251fd255897ea61007ef6521e607f0ab848517bf59560bcc7e782f22ded2aa7d2f09316ac9210971cb8b714b7c0fc2ed714ac3b4d5699f4a68ed1ea7b8e21839a5bc691516545e5a2fad089f6b2a138537c405db3d19a58c5dc51427449104a525326408b810e2065b2cd1051094e023b148f25262b18297392f198a97524a29a50c83081b84592285155f6a0082247a20c5921c2445e146962868bcb479c94b5e4a29a594329e21e60a2d5c86c08436d260720327d050e2096c349112a50632517e808357705303144ca1066110610506085082124ee0042e59caf8220a2a8a1439580530fc2a41f1028a14227e3a934f5f7fef99707093bbf7de7bd3caf965da36939b7068958976d5640eb40529769d87f48a779e4f7f91367560cfcb1b2e4a1028282df0d94b784da8e127ae4832aec962680f2505568cf9995f252bd08859b055831bd3b25b22614d9c183d3f9cac905446cabb25129a147c8ba1fad14d67770c94be3b065d7c77774be9de10c3096574f9f679e5756069a1c407446630c5922f52e02506432011e9808924cc980113905842ccaede733d437bddddf581bf477b5d1eea557b5d7fbb4e6b5d9f3e9b8bd615c1f3827841bcdab3fd7910d6aa21b49b89c1f71acc43f24c80060fd860620858b814214a0a9ce085173f6032460d6ef06587dde20b337890031c082107437002290b28784102064c30e18d2bbe5c618224216e52fec2125a24810d2e48980115221d5c018520d8a0c88d1810c1c739c74dbc73ee09c14a888ea050c414530c41e9095cf82c608a0a6630e60c235851811539b0628a1533487a275803670d9e171d6ce86278228c2a2e6092048b0ca8508a82125f88e901184d54e102ab8a2b26330166cca90a245260a828c3860a314e0c3041ace0091a544cd1e249156708c1c5142868946184122aaaa0a162072f300ac000008c0f8250c446942c982021091d74b104941f30a9828a221a2a98482d01c6881384d962861b6724c151430c124db008a186133040020a064f98e12330397e95ac586285124fc8852f6e35f4fc68154f7949359c84f31eb7fd70ce3907832bef9460600477aee2418407829950be85c254aa02cbd3fc2a55d103231858f14407d3c9c8e3ad725eb4f7ab5485d2a762153a40f2b6a6beb52aa6cc6e9cde793e3ae2af521535706b1c71841250345184162c2750824889194cb060091a4d30410d9ff5dd41034270021424a31a2c2125891e0cb1040d359660128509900b284b74210412682c01092fae8862892d88b04418644829a54fe9fba4ef0ca349dfe70ea9849b0429a42471c2105ee00618444e8030852fba68512529081fe9cb46916a60b4fdaa4d9fde010fcf96397978c02182e394f01e0f38ed869824929208638c0bf888368c9660828b215a90440b36428187670a169e2481c30918ed49122c5cb4314e3e9071128613a389148b09578d5e9f57e8627cd67b5b43d8adb91c673db7f57439bd051571ef4404d271fc6638bca222ed37c3818bb6d3cbec8f30516c37c414d30d9c113f44a0c00a162f579a78411a45558a48c30d2b6e1086932b2e27377046f098e186587044943158a00412a4198861a3064140a18283379c409139a1e29dc00356a0188214a0d04418242b461851b09841138cb0c60e9c1c65499c1461a9f0c4d46223149e7b4f08ca0cca60c1115eece00844574c610430549081c61626f0423382008a48b97b6a84f9024b124758c2091e407841982f662cb1e4cb14a88431113c6640aa3ddf28fffc29bda006ff9efb7395e3d1abe53163461cbf4a2e40021b7ec9d6606ef802262e88c16ff684e75e7d38eee60927f0793ecf7d7bf07b5d76b8e83d5a89e0bc4bc2832bfc2a3501e3b15fa5263f7829e0217c9e78f18518147899c20d315878c20c26b488c184164cf4a05d193cae0c9c1b29696cc1c9e6d434d50e0f8deed0aaf6ccd76d400cfdbeeb15652642d0b41cb569476bbf71de5a7a87975c4ebb739cc3535eb515bca6de99dcf90adb6e4e6adbcdc9b1ede6ccecc7cd784ff352af4e57b0f42a4baff855f076ae32e32d9dade81d1672789d3b29b79c858dc16cecc5e5748a6ee75c39e8768e0adb0c0d8d77f643e317c430cc81f6315a82288fb9bf83d8569dd7380edf0cc8398a088563c66bb6da4b3f938566f356f50cbd612f6ea7873a358d662737653f2794a3b618dcd0328799c6eef8f77f52adeaccf939cb3cb381e833c6a2c51f360c710e730ec37068550f6955fb8f0dadea1faddaa1575bd54ccc84b3f8a6f237df3f7a052105da773a6ca05790da7001ada6fd66a3f0f238fa244493264aa450c25fa440f2ab14c59787de6d032949ccae080db85c1156f31946081d663fa09ccc82e5fcd3dae474966539d1cfadba0eaf3db4b6e9d19d4bf8913e277887cbf264f6e39cdd99712d4e3531f986d612b654b374b384a3e37d11039140a35fa8e4d75f97764b6ec702638159b2c02c596096962c312625262313926f77d012d32b0bdea4264c4d8793fae8d7a2a552eef0e869e1e618859677ee4530b9e063c1b9405b8ad202e372da4d459a964a39b4c0b89de7dc3d95d2b4b7e589795feaebf2ed3103f28f6f4baf584b4d0c4dd024cd6cde511ffd9bf4d5dd9256fddb6961c1b540abcf02b3a457f5d5a784ca73afb4b4d7b08351942b0c6649526f4962c592fb82f4a66c8f7e2039ef64afdeaf0fc1a157f22d151a744025ad729cd40d13abd35ee5af63bd03917606b8c4ee685f67787445abda3354527383f350ca068f3638a555ed53ae9c5f49c12378d42b777f0d9574432bbac22635700990bfa34aa4731618667e51b2e66e07cea969a994bb4af531c65086e6a443af3013f2dcd904e18fce1f6990f34887f89f4b711b7cbc0d4f26262b645ebe7dc97b59e012a8e4b3382af039e87cda5d527b63aad88e4aabe63fea9288a055d6d2bebb53dbede172a4dc746e556e7787f375e7cccf87ff7b74332f91052cd002706d5bb162c58a152b56ac58b162c58a152b56ac58b162c58a152b56ac58b162c58a152b56ac58b1a22d7d4bc480c172258b28eec81975e152854a93665224260b16295928e1232f4456c74a173f56c78a0a6775ac2cdcc165131d62bb3dde745f68ecd32fcb995c0072c10513673eb417622fef2fc45a7ef20c1dc48599fcca223cc187c2767b5c6eb3dcb43d13ad9c0981fe7c6e0cf8cd8240affd400cf8493536b57ba71ffa8bc49fbe9106d18e10dd264874e8a817dd6a1ef373cc5b323af4b9a15e6efb1008f54492190755c4409a3de9aeaa3e214616beeb2987524feb933e34a304528220d055112349674044ff1cadfdaefba58cd0dd0d90be29d90901e415151515fda3bc2a48826fdf8ab40f3ba00fa5fe9f916f20efdbaf167263c400e9a267ea43f4a1f9bc3ab08f00791f5d91fec7fb4ecbaf578fca200aefbde72ee77977d12aea9f5777ce39c743461965aba1557f29351d382aad2615638c91470bb1842b0d217c8b86f6de7bcff97dd1aaf6ef794dea41087fc031c69f4e43bb711c55744ceb9564961cb14c399fc965741924bdbd4a6ad374ce39e79c734ecbb29c47efa079c02d43abfe1042c751cdd8113a61e905f0ab0445932440a1640997bf7a9bdd89090ab894a0e1bc7dc204ccd0ea7ca7a530009f13b4aa3d849ee276cf4b7069cd45aba86fd4192f5c40fbdc5b34f62d805be7db041c47816b71366b2868afa79fbf2964054f1ee757e90930906118cd06c88334087a7b85d44912f7898e637bd9201c51d1b8b98a6b71aacf566df46b1d12b4555ad2648911b4555a82c52d59bad61d415ba5157079a51568f9f7949e00f3cf6b7a27fb592076ac550b7d715e4371ad6a5550ce54bdc174541cdee1a872386d8e73816cbb711db02d75b3816bab49ed60dab41a22d6864d6ce8dd53415ff888327e485829bf69d52649e942440aba48a2010a7e9882491b63c630210963b48007590b965e9042164e0c2759f6abf48413e213aeafa9162cb98eac03fc2a3d21e5657e959e7002c6a5f40412cf12ad6a3d4ebb7c2ec66a7ad537cd6c315b966f865121f9d6b361df7f4c37883d083e91e479fc2a2161f98861980349c79ce89d9bee32b9cd8a58abd665c45ad5ad7daff3708e6235a9762e68150487705db365e421850683f059a41decdfa65ccef3356d9346f32b62bc450752e3ed4deb8cd738d1f734529597e1572905477f494caa80c35570a2b764f39c524a69796f6eab40bce53dceda30376d974f15b66d95cae435db7bcd6736d369838eda4e7e3186d33bda73ef95a669af693387e6241b172d6975b1bcf4e73db89d973ddf006adb772e529b20d2a303c9deea33f2ecda56e76b51c637e3d346f4bc0df5cbd076ae6d15887738ad7aaee570815e497faec30676e8d5c99fb7eab9353793e654a0551e3d437aa381cac053c30f8ef4ddc4e8444198da00a1759d2846a14d8774b60a6ed569d84486fffb811b49bebd66bfdc7c2f0943e3ce59dfb1e50a9526b278d4af929227bf33ccccccee21d3d739cf3951351ef6e73cb494521360feb9bb9df55182c2cbe3bc28525084daeda2ca9326488ab46499220549c9172f4b5d58f12409922d5a92a62c4102f3e5cad2f3d0d1052a61e79d4b5d58f1e4257948b668499ae2963824305fae2c45b1a28d3a09972d5492788918308b859735a6ae89b6aba3724d6855865f7f19900c39bfae090d3ef7dc73271eabb37e715cd7129e70706dcc989ba2ddbee79e6935c2ceb9d3b2936bfae2549e97bfedf2b9559c9f3e634d6933f3daff7c77860df00e11fc70cc67fca271d7449b286fbfdc14daf48bb9b19ece3d1b20f0b9fb5d9de93dfd339df0f29a6b37432b103fa7e41970788793a135e52ab6693edd8e0a8767a88177b89a72f32bcebb23afd11d56c71169957395f31a7886dd1d26b7fc94396bdb39b6f5bb2bda73dbaaedd5452bf6cfb45c5ab54e04c72688f6d1b58f44321c97de9196e7de32f41ed273e21d3d271ed24362c142081f8c51c61821840b21dc1a42716d39b4eaf2665704c4336740e443bf7663a35609c9e7c7bebeccdc46bbbdf5b7bd34aec55192a48ac4da4f34469fb1bc1d7b8f422b66414cff28cc805a9bd9522eb36936bc74d476f26c931bb655368ace378776a32b0acd44b147e343d646e33221d7f7ab9b65b334841eb31fa8c4df5fa8c69a085aad99ab4a047b1d0f991949aba97729976d15a3e1596e10e9d3270d92ecfdbc37488960ff9a071a3e88bd1d1457afb0d76168d2b5ce61c76a267dcf12923f8f7c943e6caba69c2ee9f20ee96f4bf2efb9343931bd0d7b0784a40853ae6537e59d17b8004c184a90020b13f8d4d4030123096d7c4185142b45f8b41adfdd59fc26c5b4e2d4d0ab7ef61f36f4aae27cf360c28a6f7fa96622a939a8f4ab7505a29c75ca2912490200005314002030180e898583e1804c1747f914000ba9bc4a5e228ac324c8510819630c218610002300200042184d0ca049abf97af5c612c2b01eb764022b6d667e7c3619868b68857fdbd84b25634489a97725ac7d1a6b7ac81a2e2f836c96e4694ccc25cc8d0f7d9a5e417697828b132304267ddce67302e23c95ea3877b91a40c4813949ee1345c9b119a37e9f5a9059b248b3b1ba841b6b478a1a12616e105dac42a20ec9d82249a5b17eb18651fdee2de5099964cfd6c65cb9bb212f87e0f0419249b33687e63c6f5b3ae4c4faa1914926bbc2918a26e39c656217be9be3c030e73f53c810ceee29a86626bb7aab6d04f536c0a13ee05483451518b24f295e13ab4301ea0b01b4b16aa392b8d8b7d19e62306c01031bf4c0e9b12841599e9650a68ac3cafa4498b8c416761deb6f1b7d19df983026bea39c0cb15d22c115953c23e1d87cae5a03517984fcfa2be7967307bf823c6bc3b701502ef159ea2373c8589d40fb0ff0b23906b313ad04a8906ac117903e9e8a01ed8e872bf0e302d8187e78b527db4611d19e51ca89bf7c1d5561d324c03e5776e18f10adc02ffee92109eb9ca4a14ca43d95c1d4e307fd36a390573a6a9e792d8ace868606b3410a506c569588bdd2122175afad077b8343e30961ecf0e70e90628e4e665b425cbbea7088eb3003256e91d556a86c9cebf4a7f0cf2d03514745a122d87d085834a363f8b4e81d03eb7ab86ac6fd02a69f9e42f2c5f3a280262d1a2dadabb9a1d41a858f664a8dee8005cdb83e04a3cf235eb80b8a371af9e3c68be086d48138a0395149a820f3b35b8c4e7aca15ecd2a2cf8bd962bb1f42fe1c41106a2bdf528271dd98404e31c489c2a4105ad26d9764726cdd06bbad1f7e2d9ead1943756042b7f63d8b125a5e6296c35feb5a00ab3d70ed428a11b928f1e3bab49f44394067e1b9e982cf8393081c86c47674de65f9b8280a6b4a8b65b3dd76b6ce7f359296c4ade481cf37d3a2fd7ce9a1aac52aaa1a3091ae5109d2e7894db85219f4eac89be5a175680712b4ee7337fd831815d78385fba90b8a9667dd3a88d159b64ab6ce49b13bdc88dbc148734e49df7cebfd802e3a07f478370d3b96be8ad043f56727d9671635747e23689c2a8af5710f68fa58f4030e275c8798dcaa12d5c40f48668a4b0ded41d6032a8e21a89b4058b901d7496bacbd72d3eddba6e1048bdbde1a256a2f6e8073758df76750349677506f0ae30b5d88833e721bfdf021cd5d9197ccc1f51560121ee16a6f9d4614c2f62f65b92ce1f59e64221bcef8db3e50724d2a98002befd9af863f5693ad0d40d65ddf64c46d8ce9e6e366be1830ef1622ec3a6db1d5b2d876998a48bbf1293dc67db35f48a93b0c3d37313d423a6c15c9f8b861435f72221dbdcf6bbaf53ebd911cd1c26da1837b41cbb055e70d2c374fb293173c97ae1e04f28b32bbe0105cc1bcb6bd98885a7e113001cea814f3964a8496122644315de32622a77c6de4b1e56d6633de030f64ec2ff4a17f029f990e6453b9a9a4944bf566eff8ca43805e17d743f00045e7b0a2d019a87d424345966714c9775f32ae5c136959ae1502489620ef0e684c5d9d9e80e7e52e13d1f2490354d8cf8f22321334f1133d637ed95ef101d44da8e8d6d60b9dc3de477593cb5c79bc8970c2786cd9636e3676156c859a1e7010cab8b8e1d02f6004a6021bf194681f173b868fdd684f4f9f44ac1e2f371ca88558e57ba55e37ca900cae38d1b8fdcdf53b9c47af295061c4e36485fe37c481a8cbf7bfa0bf610027821cd72f393ff46df41afab19b670afb0a41eb139c7d9389b25d88558069655df4fadc72fd5064d3e800a3983f5579bbfeb422b65c94c3ab9bdf0191b450c0c0f64b638d3028ed1a2e115267cf8b888f53f78529dc6419fd7cb8efb4c499b84ef8b6d1dedb44634ee20e1d3c65353837eceeed13c13842c4b511df5f83ed6d8d96c1dcd68e7e854ebf8a2df94f041b03a6afd8433e664c6b0448cb2a62a300ef8eeb4c3b94af4404bf3cc0ab58820bbb0ecd804e3cb71d2e682f0f58c0e6ade5815d03b6331d4b33681a6ca677d21e610856360895085be9851bc1e4ec7a0835de7c53838681a802810954f956fe03d4818c12a5e429ae4d23dad677abf0243811cb64658f01eb51b4195c5199cf78bc030fb8b8e6ba4964d451dc3da1674d31881e6435423b0a04309efb0968a85a79af0fd7d7f8570bb61995ab6bbc4b71fbb0059fb58d0f982a85f4bf72d03aa4bc4c7d260311e2a0b62907bb69492206c356ee2eb2fd44acb1254917a8796209ede893f31aaa48289caf04e094c46f6a64dcb444a56299b016d475a70ac199e6132e773f667c17aff6a9383d214fe348cba0f41edb2cfa2dd665ba3bd7877688e8a4d1a3c7ceefdb9e9bf67713f7e811e5fae5c2bfa57cfbc4619cef8668d771f8631d26f5ad484c3f8cc8124093d6f421e2fed0032139ddd4b5559dd3b691b0d60cbeab700b989cdc012286e583cbe307bd72d09b3470a13ec0f68564756e9c58d498c7302974bcc5743a2072390cacbe8d1fb302ee1e4a4f8f89f54c224158e14590321d3bbed136a8bfd44eb2b75403ead59a63f93e101b57cce4dbdb34f7297666e58e002dd3420387706a8a2e4fe2b81e7634c665c9e06c2fd36632d95622a8d71995f29fdb33d0a24a69ee0638307f82b2493347b3875fee109560a951251231ac4023b3e13b70a341bfca879a7f64f3faa03401ab1763ce2def4300d57b605d32847c021b3860dc97a89b1313dcd8557d4223d497014a44ab8ad6bf9cde8df7f800c3500804c38e1c0f147fb9a12219a95ced02175d14fff953b22a544c519709b5f10bfa2caab6a9989ac9db153b9b43dd03ec682157391d8b2cd6e0d69eedb6c3214a0bde95a72d844bf07bc7402b77920af9e260cf9743d826dece20183176e9cf569565047a7243f8c690de24758be047c538f99410da220e0ea0a5eb484aae68a8f18d8fc2d79846be5ce9757777e08b25ba99bbc0111a050ea6b15359989af14a5d0fad6ba0e6e4d2fc69edc7c2673a96b6ba0eaf8cf92d206e0e580d9d0314f71d9e9e6d3c84a2eddba40834ae77127010bd37d9c0ad063d69b29af902faddcd377aa47f5ac6775136e88602060f37c645481cac1bac1157d0fcf89bc5b5dbd6619f5e6470a4647505154b9fdd542e1f4ce9002413b0aa5a0fd0372887df51c1018f081aa9b2cd2ab89c40bb412bb91833a913adb5ffe828e9f693942442f016ff35bea11bf3cf0d45ba6c93d45576fb6a29471c281ef4415bec9b8b42cb7e1b9635c83fd5abdbcfa0ba51b01ce7d08404687c2d989dec5c84d30874d2d90f4f80f91131cf6d4a00513ad2d88513b140e5674525919a84248d621fdb93a45e893a21071729b78ca1549b99d0997a6c8c9aca26ad8a66aa16022f9a0e83779f27bb88e3a780e49f496ee20230ec777c0442eed287fe5e0e6193f436e883ed409d5b8fbf6e498577ddfd9b8b1dec700d9af3e31f8fb10bc21fdbe0baea00bbc36a68c820dbd9d2e165fc407478044c8b8b27edac50c7ea979c2f3b4f68446b4cab8be4c702017a1ba06f38e2044918964f8ecc9a3ce0afd7a195b5c09fa950f01b94522c7488271d802fda3b7a84f1daa087e28ba7a9a9982f456c49265b3628f52b0b8085fbfb917ec0a619eed67f2d3048d4cfae250101377b47d6ae1093c62398bd162621e84e97cbb4ecf1b09eb64a0ecce391e24adeb86a5156e37b0edb489c3e427bf56acc58dbb5b2cb0f8a95b2d274b4dde7bfc062ef9a1cd735e6ae04cf94eef890ba3404daad0014d61bb786fa06c175be5ec25793e3cddee2d1a288da41287b4fec19eecbd48a69ae02731a1447ed89dda00cc846ed79ec539287ea700c5118c9a8351c2406fcd3e6e58bda4f64e2184e1efc296dc0b7d627a572a5f250affb1913a48710e4753e9892d03b8883dedfcf80c32f27162c43041142d977dc029f871d8f2959ab139cd15fb9ce50c902c3e2f59421deac86fb32b8a9a447d8c8bc636f936f3fb4a21b70f2c48e23a1d381be62a4e8a5a8e047d31d524d6c8265a06fe195ae64781c06822121e980b053130536dbdd1c80344b8f2294dfc3afd778d0450f3da89fd9c5782eaeb5a107fd438aefeb740c598b6539ec256b092962886a75871c892feaebc013e48e29a55a77cc3751999171af61636fe763712293934f26ee4d9a9914dfda3646e3dbfa64ec17755286a96d517e26a3ba845289bc1f9f417b0062919ff2dba1c3d3e17fcc5ea85e341630fe0bab98644a4c8ee00902ba1458268353ad984b946bdc40c833b3d64788b605fabb2509d3689ad78ad9a7e6e199202345df4cc0b5f801d9cc32a565855980a54c55cbd27e3ec0f944f1e58bac1788fc41df4e3e773afcc2321b3d2877e632b3d302c6fb80bcf4c5710dd11ee64f3098809f4ed8405cd2f939b61594374628bffe0895e670097b19321ba31abd2f30b50302b0d09e2d84411ddb98cd3b451826a62d92dc6b70d3d5b0dbed5ed64f10bb295ca79e3b501a6c2c93e997928f8d33f63b7286af5db1421d1a3b497064789774ea34a1752440b4d05b007d03ec3a2bfd0fe8287f683b941d7114586744537e62f01bdd67003df3aafe0d93da6c547dc0c0fb9debf25796002e832dc85fc0b8eeedff0dd1cd5bd0ead01999870f13dde9baacd979e5ee55a5021f0102900a2f5674d43081c17f96e72205ca49d7d284a2c074912e9075ec9f83f3110e783d036192045095560b113e67c12536c5e953e636613f9b81eeebff619a581e64811fcaa372e8e0dde560456f6a1662de8193933552a0a01abaac5fcb15d19dc28f496cf161e6f44867b7fd361998645a1f66da1275cf59796d86e207610d992bfde41a4288c9d2459719e10ed661343002da57a0922a7134abf4b8e26f60adc6b33d724a841288f1c194ed597a2167b69c7edf9cfb8958c1f8d51828e4843866ee164ec4da0505761d1e9e633803d56e9f110a6844241bf45153f8608a64f0dd4a866890e05ad6cd081e07e83d8883715303ed615263b0e1da2cd5082f25ee5d2e0237ddb29129689ec05481287ef587b284c96ce605b9df343c827d804179fc43a1748505ce95634f114e737f25be085277a6a5d6465b458d401bb12f3ec2f1f633f1294e25b3536c1e206c20192e42fd50bba13ddaf50a7c7edea68e55ad2929ee28c03fb85ea99061cb07c857ffce4c8206c0684041d7ae12aa25732cb1f6b6be8a9a7a39cbeebcebe987c8609b7cb69c116d014e37a599d476dfb99ec8a3f65033efbd8c03730edfa83f4df94769ae38a8f0c62b14024f5de1982375571987bb86cee6f1570d57881cbb8124c79a0c7f7ff15e1c1ef92895389211c767f9073dc4c43c8de740b1b838d50f2c8e059117b28c27ebc8626ca5692c71943bf91630ec0e1bf820390224c29e1ed32a81de74acb25ff022805994fc803f595d0f041c905def7f1505213e150bf908f7bbb270dde22093ca1699cc1859d8fb98354f94047e9aac5b556bd226b6e46b0dcd8452d7b058982e29ec43f59f26b5ef39a5835836a1a867d2d8c6fd255facfbbc5227663f6fc98b391f56f262dce7953eb1fba1ed7dff312527f63f5ec989751fb664c5facf2935b1fff14a4e2ca5b42b464b3f74d300f45c21f5b00a867683dd42304c3e92ec0533cb606b993db06f080dcbb317cc2c836d860e3b8557a4a48d93ddfd7708b668080103e5669b0b4bcc1cc8b69aec79cf83bf4b4260422bf462ef4dc36a63aab2cfb59c47067933cd12f98ac4f1c1a856c0d0c81e1e0a0ce4668d3faacb4ccd03a3818d1a03fe6c32acd5b8a11207cd61422e2d06e5127101bf67ab1470ddfb717ef32ef346af8b656a832eae3de02875ca0525a9e5c6c8e3bf5c0c150039267705567deecb7cbd7cbe7ba8fe61028f6c089d05fd0dcf107dcf50d524456799a963c2c755a19690aff9e1399d2c0bc03b54fba21a89a40aebb98230102f03cb722c1d82132754364dd2684b96d832b56d40d105fca7eb878cac62085be8f0c0158327f7a211925d3bdeefbd0810bacd5b46a59d5abfa5587cc2a704739c1aa2bdcbe7c6c9a2ebc13b1987b8dfe39747ed9bf478ab3e0883055bee7d54476ddf70da14a4e2179974cff1983b743b42f99c41c76c4fbe9f5f8b41312756f0a5cc123d826922a0da226118e8e99783fe850647260beb8265038874227cdcb1e83c626f10ab8ebce5424d4649c956364bcdd3d11fbc1220b5cd5cc01348d63a1904a65fe0b259eecb73e89898e131eb2c2baf3ffdd60c4dd88985bcecebdafb949c11be93a50061bbc5c464be4e441eb5ab320da6f4037f8f616fad77f519864b4bfd156c067c738c5d34b11476c24a437afcfe16e15d6dcc6936c0596838ab504e2ab1baf75de36127cc0cdc14dcffbbf57c688d167fcafc8977b6f0e9f3320c81d63b5f0c251718c091af0536e98b31036d680f12c0c44cab61c0c6e557915dc6dbd802276b7476e7b75eb3beb3e96a21b7a9f69051d795d3547485d58a8f655d15021cd913e7b26b8da4f3d03e67144fae62efe05e88d72e94daf248c08b056972ef9f2f8dc69cf4629426f1ff271cb7aa7e0c98d5ab4ca72774f4964a2f5a29eaa9e0174cb7f6a115ccb59cc25a77957d949c05c719d9e324ef72e1e906fe70ef679211c430175865e70896b9db3213cde1fd354fb0d5ba732670f5bb8111231c32bf5d56626f6a06f6b2c6f3fda7cb5f6c489e989909fb2acc3730d16e44fe2202c2356cc795b833c8fdd9c2953dea6f56b9a85f998850d04d9c6996f78c82f0c256cb7da1ba48191a686ac38bdb566c6837a17a4cdd51d011cf9c345075420d73eb5531fd888bdb3baeb63063e2cf4cf01d1779f1de5a947ee33f97d82809cc6a746e2ce613d00173e6a3efebbf4a11120872283aae69f880c0a70bf87544da85901a55fd84d4561e549ca6c874909670721f433d1003ff5af35f98dfe229a75fe8ccf21e81ef3c336073d673567c02c534bf9e8da0483dc23b20fdd961335fc35d902bf4467dd0b1c1c6fd1fa17f9fb177d242dca668dd1acc00e61690766025e4f85f97b85765e2b8ba957daadf88186b14938796c21cc3536b7890d9c134439320b836141969a84305803305bc5e74118091ec116b6934e9728f1286050b3ae59c9ddfa199f9926fdf5e1498162571291911e23214de5e5980460296307408c396b9974382ccdab26ad61dba5ed10bbf0b28c6646b54adc4c2bcc958b61b82e4d30996bb20c7a27de03b0fab018e2bf4a9290602c0758096a24ce47f6dc577332db8db5a04b773e134750266dc68615ee96476f94912e2aca0ff55c5a95c93e9dc3ccdcc5731897d3b67f4c69290e88f307a07d7aa55b4ae7a2f07b9ae19a6e5087f95d5402b5c85701c782af8a7c009c06c5f9674925b14cd85aab6cd1cdd5940907b5e51493f3a435f72430efb28f520debc2b7c66f3a7c770a69d34873e5a60ec0aff7d9cb4d55cf3b833f63016456f5069c74ff9b531d3fa71d7cb1d607293c22ece489e544b3cfc62c4cf69fe61eb6397f954f2307b91856ced0d01c2dd815b6e9e808d5e5181019cadbfe70b750be6c8de5d56d8faf5884019d12bef79dec09e039ee6108a31006daf019944b047866d8b07f9748c5ae5f22d57331f913ace2592be1be2387a1a5d09f4e2ee6d3f990dc17b744550295faee00a6318c47480df538ffec3dd9de6a90d2c03bd207169b5df4a74944824b80d0d2b29ee1963c007538fdb3b081183b41c97e9cfe9dab47f74f48b4bcd86958851c5d8402231fe8849c7e8d3e85c607c4bc8d1b12e35823a6dadda6dfac4cf984ec0c0e67657793bfdc54207cb930136e71fd1d06d885de663ce1d29648b6d8472d4a19f967f898e2e9a03720b79379ac0adef254f4edae805f8caff10ae77db980d5828588f7f8d3aef20d8300537449333a846457610c4294af6de785ea5bccaacaf9333ad6631c709e383d9bb92ae0e348541f77d8c09b0ca1967a331c52ce2c3d156f990724a341bf5cda0f0314f6e4474bdfe5ecde9286c31d3b1558cc69d36ede2a1d39140620cec1fd9e5c5a06d0e70117997483ffa7397403f083d061159437e7b8053b873e1a9f99b1d7d768865b15dd75b491c777f5b3d0c69d8c55bc7d3ecf58eeeceff8916739c62bc8b0795818722cdde89dba5e9397ad105b5332b116878fe44acfa5a3eb1dc0f99d633c1016683cdc4835cea1a900081eeb78b5a49233985609372460379d237470a265b5a9fe7d7a263c6101f479588e4f2c53c3b3fb7da7654f49ec6766368666d6d4b80f0bca77680e82778ae5c6659bca36a9e922b1526ff1a256ae582ab5820ead0c66019475540c315090ab753999bbc14d8a2f095a02c5db41e415bec5eaa34ebb4751fdee49f293e990414446ab529afb4224fb6f586033667f5e5d120a6631e88f958440fff43430e9de9164262e6043d0e18907a2af6a9c57eca95d4d74e72114e02a9df6480e5f6855ba6cac6acecdbb1fc9ac30bf2ab72f981e29b393db8561eb29a1fee6e999d817e4d389afdfed9af9c480fc08a952fecf577251ef155afe50188c75c80c9b97c2d586254515630e64bdd9a23bcfdeab8eedac901d48f5f3d3a8b5fab5cb6bb9d0187d8208e41df7cfb83fac053553f475c02dc11e50e952805cca6022b1774f904d9647b2cbab8390bfb3a416a9f053aebb5dbf075524a60aa0b42dc9136933328f83cb1a24daa68272cfa498a3479114daa68132deaa48b3259d14f56441315ed8445ff44879e6da702016215f30ea600a043815256cd0856e72634ae527881c1475ba139f4359b9316d1fd460b932fd2c48a30ffd8b21317cd84a29fa808e4ff5a38c9a24fa648455ed5c53b5d283f9f4ad75a464c4c9fe1bd952012000cb014f930800c16986be2574130ec03c2e22c37f06f42caa9d1e450d053c95e0dddfade002589b406d3512761526682f89406a21bac17823c90fe381b6bd7429a0058103dd5fda25a360a133b9e172017f2971fc76890d488ba055ec5a03ab8d715aae00b38be14886d2271e54a144bdc1093c32b4b0243c75524897c1ffe1bf893000a8ad05c2baaf5c54b76b27ad702fca27afa71d5c09d9f00ab16b99166c43252c2762fb8fe795ff5204f2260d05a09b9003e3002f3ffb5bda964e1aabcc265c7b2213fda007b3e724ad23fe45cf0119f87a83a3b7188190c3bed20444ae9abfe821d5d4674fc2ff7e7a0e2ec850d5adae0bacd1d83cb7be14e69297774d8e1bdbd45423cafd709ba46dd96cfab74a92d0a0502bf66e16886b28b126dedb510e0a6f4c4cb8bb9c90d3aec1d94fb6774b7a20ae430d55aa0cb2e9744f950b2dc9f5bdc6cd1609f0d7978790f679052dcfffef98b6dc555ebe417965b89b831d429e3a61b514b9dd736bfff80a7fd8a5f1ab475a8b8beaa8b8629f4b5280f6d07c5f85f5645345f0a273abb63d8870f55eb28b5b662e67ba54ae914c699c3eb41dfa88037f208d1e835618027e6ff7f4a47dd1fb1cf5132965fa24a93b55346161c51f79f8b1c15d96368a290e6798c985fb52d5bda431b9dc3470e20603c45cbf0564fdd879d490b789d61a7faf32bcee1603cc1a092a7ac2ab15533038411a6f7f489b4ed4b6f82f74e584b37d0a262334a87f5f7291592dcf9a3638ecd517efb4ae049acf864205acd81453524155428990e58dd0a0a88df76b1ec0ec3513c949536722a9296d4fbaa7bd900f8ee0f9118000fcb5f1ca1f740da7a2b54627e9f7c18f4e4cfdefb51c98cc86aed1905f6e3cdcb5a5b8745c0afbe2196d29bd1781467491d176f4511dbe90278ee36069285cb5b9ff389f84ac4bc83a8f75e4485a582e5cec87acbe2aae655be08774f5fa200d2c1a8fddc3d41b4bd83e8a1c03f04d3cbedc43ce0931e480bee55f6d6328b05eb62dc6653e3376ff5d525c795e6d7ddf1c422a168a0fcc8ee266469d608ceb1f2b57fb33447d6e1a3370ce9bedcb9722be5c7fbc5f088c25140f6e462aea3e67fb2c116b161ce2770a106367dc61ad01556bca58f578958cfd5767e10e0e00ded6b55979dcd63cc3ca82d15a568e7340c9897d4cbb9b0af10d82723d81e771df7d3a7379ad6189f089d443d5f65d1000cc443783a649268ac508aa47f4e4b0a6073c904a19bd66617d7eb6f926b15d092b6fec76f158a467f155f483734a6aaccf03cb72d7525f53631050e3d11e06184a5c36504b24de7ca14f5b7c5332933b04998b90c035ebb2afe22947cfe8f816578ea50e19634bec2181b72a32112a20634cd40d5c94394b94a30f001be2b222376233781d6750fcc5cd1fe4faebb834c3b074c4b7c043376f678200a420b025ce62862cb6020f3e347874596ed9ab0f67bd490ae669eaa16a662c665e1ed4c6ea1d7bb0219462b38d5d7b9b64913f47bf2e675c9080f3c57c9280cd6ec730a3cde0d6c9cc0875ce453a835b9742571138325f4b8b6fc7e7de7e1da6fac43dc2e938c99d396b59d64fb8284c7252c13a48b902787287308a16d58c6fd96059fcfcd35e0c01454bccc1b71678d84e264326dea4e11dd5daf78c162803beefd609ab21862f6633e540dd998e278c164bba0f4e604fe8e2ea0a0c46060eedff70ade18e2a2cb031becac0562a0d0027cf0868e7ce3e69a8b8eb3112d07e14d8b2b08a17f4013d083cfac358da2c27529a8bae9b176cfcc62c861361f8014539119b8747dc377360b7d34150315094c1c8901c675358bedfd0eaf57331cc188594c75db1659f47930dabbc1f8f1fdd0aca3151d3db0cefb1e876c579e6991dd31ec22b7baacebde3caf5ba0aaf1bae0096512ec6c77e48eeec73b8c75218447224d5a8e983028e65e103f2f2a4d9e9e206778fba6be3626fb0ca7a9d09f62ae92dfa1784cf6edc48d0a49e921b9ddff2900d3b4c87efaf4841e4a57089029ddd024b114320215921fc3a58c98d25272d7e6603dfad36ec92b3a5831e18c8440bc8e54788be944ed48244926cc5294f967e129311e1a9ab1a824c00b88d17c56f60e689ba93b2c5ee9918870c62cac167c5209d18182658b01171e29a689f2c86a52681e7504d05e1b33a54f70bdf44a93f0f3b2e07468da4ed79f9ff45ae7d4717b491a93b39694ec27cc466c5e271b3ac4e75cb1aa0ec0d6f700cada03705ff90094ee96799e3c2cd4634cc6551528945b4021300a89ba0f099d7c6346f77b72089e4a494a3e81822e2dc172533f9b72723a2363b41fc21ccb7454d4eaf47640f8dc1f7e1c7846eca4650dfe9c5fce6cb94e8f780a42cd2940811e6e2ed396f63b29a6560fa9d86f40d60402c9992dcf939a6d0c70046e028732e60f8a48f68b484b44c022ea900a34fb8501f80575960f65a3f7282c0af1fdd72da0b77ae7fa6733fffd37e294828afe3584248572fa25ad4834b1a2b0d82aeea0838a5cd2393d11c6bcde8819079b50565dc539b16d2f076eee52be52433e89493f109dadc8b8d897d9f4d78005cfeddfd8f2faa2b6419488adbf87ce5c497775eeac41ee285ec72afe22f3b7149865edf9f8485e4cc330c1d12cfb49cadb4deb91db9065a2a42385b9ed62f67e98a28521afd877f204847aee0a35c89d64bbc51f283124606e0babd73a48959217237adff48c518db2bf5a7c1183f4bde3dbe45dc1895f141dcc7f77a98fd4d4143f1f27ca584ac2f710cf9f124b40fd71809a00b519e358a40d26a19f49a4a9df0a4cac39df2141f121bd4145bbbfe6cd90fb17ab27975cebd4a8a79884a30005594f3737ac13ecf60432a63000d0a5ef8aed8b21824cbcc2e5120b7458fc5a9f005b63f4d662e183267a2e9efeaa64874f09ad089ae81760f8968d6e3e9bfed97a19126795cbea2638375c812dc41ba4dfd458f86e74956160f933ca5a19b28b138197b97c439ec5ba3d1fccd4855e5fef2b2ee1b31334daf2c0f3f87ac527fc80c18e09195041da1edb2e55216dbc0e4c0f83b53c04b144925b5fee945457ca4a5ae9b2a4a14e87c739200d81a514815b872eb170d4ab898813206faaea9846d9f14e8044fdb4d524ede80ae7c04ef2fabde4d988b77b174394e14a2df3f93bdb42ca017cf36c3bb082f4adaf354acf57d7621b655325bc67e1961daba87ca40d0037f758c5b0f7e3660c106308a4ea74a1c845ce2f27c2520773fe9ae4770cab7e5295602a164fd6c235ab52e13dc7cd570e13e1c5992b98921816d6e2737c88ba2024bb317326ae2e21d6c41447b39e64835cb16b8e47a527db2487edd37bccc9bf939cf3a883c782c8141367bd8db0329744a1f249f8e94b16c91dbf6770198319699cd21cea1c2504f55fd5e82ab48cfe285f50acd6479050ca6574a878541434d3d9f2180456f5857941be39a0d72ffa2a00b5e4a59cbd9defd264cf8e0cfd145e5f28db2b4f50190514f2984dd17a29ec23758d77a714cf90b9899c96fed61249980c13dd70096e1e6d8638563c39a32169f5d896620cadd6d033b3b46264b64b73d177dce39c97abda5df677172f3b929d484dddfa4f934b5f4f0135fc4fb3df20b6e9afa7f0ae20bf9c30febed809abe2ad1533f99d3f76b901f494cf16bee5cad7732af8a1ec6cdfde3fef6ee22ee5c2cabe1b74a3fda08f688aba9ff0881d887bc847211cd45894ad948eeb8348a8a11a0d1c9b5e698a4fefa75be9e653306db2d97ed90cccbae00f23c56212029c6e0565ad1108201b25b84cdaca4bae498ff4d1193f10ddfa01d98b009da3679afebfcc41caaca3889e1412ff3d46b5a74d124c7f5ccc45c64ec06f8ed1367c2dabe9797648006e3cc304b0dae3237908f63b13c9b08bb59cf766606c2c2d64180c1b8517dc798622489541bf79c913ff6ea103b45bcae07fa8c8a93d229313e95e8a5f0305fb860b0683fca5b1345adc4cb3787f6943512915f62067086fe374da8b26ca02087e449ef12cdf3cfd6bde9c50696d189d6afb5190f53d5cdf8a25182c3e47ccc67c57953ac175f385f1d71d33d488780f2cea402b24f2010a224fb8bfddbdb56a091ea20c350dc72f894e03a5c00bf9a5ab5705dd062729e1c691d0c8a8b9f9b5a0ec36467bcac9f290dc9b0600660f90504eaee9d7c3df36fb5afd0e9eb60060eee433e94d04d98f586b6f9b8bd0876f282f988c4be98db85a76f526ac4f9df52e5acb3260130a29b00af11b780a05fcda0eeeda21c8ba7435113412fc65eb7ac2746513a0fa634142a2c89c2411de2ea769f93a18054a0203f2922d3b8db7cd207b68a007614f28ed6fa897dc847ea4b3b850454716eb428a69d559887667e6be7ee2cf2bca5379bc13d59fb73778b292bce43bcc7ada4833f7d423965f7858b808c502e754c2c67506c52b4b6bc8a9112338d92c219d9bc35a060e3f7cfe3bb47c9fd64e97a11cc886bfe3529360f998e503e25ba94f74634b7e12f36a614c5a0740f901199751a76139f42ffbbc10180edfd707dfdaea377f1a79b168309af82f21c9e96996e05879ff7af5bd072afac3511541609466903d9df7573576a554a7e041fc4501413bb9dd251d4dd91b4c7f85e181b2306a91f1a5925d04e982bd74d00a64cdfde6c1838f3b80a6678306be06ea0bb76e79926a1b197d6a729ce1f88b8f7f9a41c5159f3650301a1cc23adadd243b028f85a32242aa541fbc17844cdfafa9a9857e6b1a648c315adea07d40232ff1ca4bef67440c7f18ebdc477c96813210334684cb152b387de078b0db246b454337ba9ef7482c4665eadd880e1d3f7dd414408044d34179369e8c657517356ac88640ba44b384fa7a34b690be03d6ec6fe28e1da525f420cbcaeddc1ef269f044871fda9b39af5a92e724dbdf175baf0b66efb473902664e3619e71e819d4e51f92cc07aae08c0b95c7199bf2dfb923c29d238cd19ed0f2cb42b6b487813074fe3ac4123304b14cf60eddfa68d843b03b98a4c03dd6e8c4c160c0d5cbbfe2b02858247cb85f9e09009d891523074016cfd22041bcbfd9874d2a2e8b92316834cf1ba3df2418d8249a55a8c5f4edb0202736f61c60b83a92dbca0d21d88b630c046a766b37918c7d52823b097c03748013fe704325acb90f068a624391229d12d83f51f4473fa59960beb5dc1d8031916a8225cebb742257c70607053c47e822f27d88d87f6af99cfb0c0aa230afed2d382414f5def51408be0e974f84eeb02f829f34015afd99393094507d282ccd9374602ad0ec4ce2e504f9df68863fc069239d61ee104477ea0a42a64d135a62295376a48534b30981c31bcd08a701285984625bb74b3bf53545ed4d54e4bfc06d4e9bfe181062f64c119acdb190f50326e7e6b013d9329790659f0e41bc36fb4618b208329f367da1ca88780b165d14452d845cbcda55dde414f9d48aaf87915800e45ae238728895307747930ffaae621ce968ad9200bf6fd71758e1037d90b1a82e108b3f038b44020caa91a479f227aa710031f7aae7fa8f99c4980c5c3b9fab9f787763aa38804e83c30ea40a2b51b30bccea672a1fc17f7a01ae0ee198e21469b624c950962b1c7e921eff80ae69a816487839c61232d723bbed23d9cab010a1b4f634d5e5dab1974f2cf3451f8027f0c5f884a09b1bd3a2ab3dc8e8c323df4b98577a9d42bc6398acb18f5f7c6bff45cb6dac58040abd19a6b3fda2064fbd492195432750499ddcaa8a01215b60a42f58e3dc5c768fbc0eb62cc2fb51762d0660bfb29f522244fc5c59da7b0088b46c1d7816152bec7c857136cc18ba2dc7b772751f5815758b6c70d63c0d6fcb507c7442ead8a29131a5da971dae7944d1d865c1a6f96edf76d802cab85c64e548347f0dec9a14d63eaf26e7ae3f841cf7618f92f5f95a68d750ff990c36f2c5426893696a7185bb98dd60229105643570d2c85364a1f999da9ebf82acd6ad1969c4e8d63c77f72bb9e083323d68945b0a27f8905d25e39c1bf0325a7265baaa7c0d7826f12fc48e0b5b9bb4ab74d432ae4a500619075a75ec969d50ac1879d5710e1e801986fdb5992b9149a38d3ccddbcf0169882ef9c44805810d9fbd9a7ce074cb0c57177d91483a36d5fd9df08580716c548fb60904cf6b33377c6cc48888687a4773575177f073d3b1ad4135aa4f3ff0284074e29432ecedb07adf79b6adb87d607464a40dce4c1afb3e86f13a325085d620c1c0bd5bc13451e412d476da3009bb68e6b38c0eb5260b458b9a8d2bafeaa33311a49abb709b17089599a12aaafbfa1a54adc99a872a8870fbcee4c4f659af422b8d2a498bdf0a19854669f23538dd46770b861d44459b1b1b8c45a5c4842b8f46b49c7c531fccab212cc6f0740623a61087c3ce1cf5aa6ac9130a4d9fd84fe2bc99bb9ce85b6b4eec866ee743c44935b0b18f9c03ecb8dca1fd99d23b314acd3302d046c7ec5ed78b8e4f248830d36cbeb44d47336de285442a9b03f08e194d394e74052d5bf2e1f2c262244e595a35c9675168936296477c31e3da2837fca8d36d860fe34d38e3b75f868538ada2d6a2513c1ef6f1ecb2ca08707f39ac6c7c4275e94bd3eeb6c2e9bd06f8a2003b1a6f09c18684c84a62c47bd29523d9fa17c14ecae89f73d8d35d28f9b2720eda21701db0956e9d6b4f8091429bd1cd55f0010ff2e8940e133eed643126ecc9815ba4057df40496570c37a124c6b73a6c4a76ead4b8e9e97eae43130623d3f6b937ebdcb446e264bfa5c8053daa94fff599be1d62de95d97897e91cb5105d4ab6c414f49b3adb609e95a97dc25041a102fc03159c84ed90313a83aa9a6190143b76d15afcce118ef8c79a536eacfab3a36e11e5776961af183313068e14b45bb4ed245375c0125019e91a0c7df4057b8a04e4c93e53f342ca1cf14b8269f14ac4fdc99f28320a13373e3a40cde5cb8fcd2151f8aa663c673d0e668e8981403e2c27608c35f111080202be84724279b1fbaf52e6d3b88ed6f32ab73d17a4d188d601655645442e45a642da4437188380a2d6b64986a676cbf92441440a373e310f9a043a6b5cd18dd59780b35e3bd99cd397d71861f449eaba0a17ca4e5135625db7f99d388530a656f5aa2b94901dc6a4f538951afa523eff7851e9ad9e2539302d08b4950e7433ac7d4480109326eb7b169898ac905399f4b12177e6d066cb0bf1c81884bedc3ce4766b34b8dbc8a417996b8ef4f7800d18eb3a89eac88d899db07fe5b76b95909272985cdc3702a0acf3f9013135c05b3da1259eff5b36be2fcc0dc518b925692bf16b376f7c30bc7c284ec161b88730286a28876dcecc009303d3ca1d467e42e6184f30c30e0bc320614ac17d34b259a488c4d4af0d14529ebbfe1a9f9e7a7144d9ff500d5b06e0460555171c3391babd72517bcd1b538c36a2becd6cd7f90e2166f01101341abe0ac3cec95ae9ed57a9bdef89f01efb4087346367712111104a6e4c1fbabc63a13082d054f22c9047fc441547a03bde9897b04a53099a1624a7a9d7562478f0902d3a9238a40a9a04b81322df1b641336c4c240aa5684c839c250ed7b1228ce74730146595f81242ccdb124921429dfdcf1065d05a0452b30aa7deacc4966149c461ccf7986241797c8a064e8be19a8728a18c110d6cf034b8423dda8b482111d79238ed64a74fedf249e09971019abcd59e65292dbc39401225c0bb0e7e440c4ecb4fc66c411f901b89f4355b6812a9b73d60853e1137e5a6ee129edfe5baccb220b2c785b3c72dea82f519bafa5f0e4c7693a73f6f695a6fb90c06e402224ae49fc7db3429d11249b96512176077c13354d062b2c0c664dbdc1f94aba12a24164457d5bcfa8f20397e1c28dbbe513fdaefe120c72bd2372ad1c980cfc11ff563f978623e667b5566123a797f11da84b68be224debc803af5478f775e7e9b62e98b8bb49153b81a7e63ca9bf2cb25240b28572f6cd1e10ee4583ea27fa706befdc642ad1572ca4aed960864af0575e420846727a43735d5ca62890f04086dc6ba97462a576dbdb785190f9d88885224a6bce083b86200ba89b4ab09ed56ad704431f426db07c699407f21b37f368f1051ad5df9baf1a81cec86190a3edcb4ca56d14758afa1108787b8d5a82a07ff6084b5c75ed1e8ac55c4a05e57a5623cc03a8ae48a5412c42aaab3419ad52bfc3b63c70b269658e406688860b6142977b15006e1158a55b42d22164310a70594fe5a4ad96d05623d88fd7c3c6da4b68e187eb7140517ea7a1009879bb19ad89ca5c832d8d6bbc72a65d208b9644c43cbd831617e40531ad2ec9ee0c6f180b639e1a118da3660042a13c623238067351e6930b985b4e28fe079fc733585004b41e043247e6bd4d771eaca10ce1fe80d65cb1bea34929dbdb07d2bbfb0ddea07bc36ca5b8ec4bcd52f61e3db46b85bedb3886afc01bf4861bb90366eb791922b05b18818c0c03a0cc058cceffcf988a99bffc2447a0c083903d81df1beec061de0af6a166dbe492196d94c9a9ffc405f9370b0ee8836aff535cc88cc481ce7ffdf85f3352872f7d80b730c55958b8b74c6af64a190facaa6eaef15ab0067e88ee93e98681f04f4612b4c77386cc470401f0c26dd60484713bca868eb810595fb4e1e8231310daa63c5169354a4d4db379fe97cb223c2f7201b174ca1831a56930bb4fb447844cc624b61e5fba20bf20b33638ebc330ce8695a5a83b68ce2ebf5c7c58a39f432e50d7927b5d011215a7dbf6b18e829ab20449321ec4b7c93a6803ef9d123961c49b851d4d9fb0b6058d282861c11cb7c53b445730c337e88264602646d378b2d70109644a50f83691c52c9fb0576ed7909a8e6a38fa779498aa68c935e3e46de5502082f530eef4dc7c36b3c0108931044c66108258dd7100d81a84dce277cc094fb903ce958e61ce29e1e09266efec7996916089bab11f1b309c5c9ca19b023746fa24f1e175dde44c4f26095056146ac5b9e726a02901df9029904ed2389847ba35841b4dd4b26a0cf426fa7e744d2a216e00b0df33b14036f7607d1e4ebe4318a37073789678e0411389ab456dd2a9c59db67ff2577b9bf81e37cc271849282c2c312708d24671d13141d750463819a9ef790c0d46d08f8ec1538887097474d853a520e3c288a1aee59c57aa6be2d8c57bac3f12a6d7aff4ecafa0615f0d94b67e5546e50ff20a95cbb5bdfb5617c1d76f6b08ff2330aaa8a9a1a12405daa33c86746708ee13037a31f4a4c04f3d6932c9c3c59ecabbf27f4a95e79be5b5315fd5b0035d59d31cb586afe9dece37423e49525534825e9a1f76b47b146d0520d6bf9ac1449a873ce3f1045033db42a1c48aa08d50ca11ccc704e3428a87218d99c1c336d9ca6f7ceb116b3772e15046b55af6bcdc98890ed206b65c4b942ff4681abe51c50388aeedf813b71dda22810212b6ec4f74a69953d4cfa3ed6b4d2c29516a6b021f97d6d83ce4443d873b906bb2a111ac4ff081007472667bb320a82a747480b94590be65977eee48ed057cd9e40ac80327d463cbad96934512bb712cefe3b77c6c628fff5b1adbe142153e68a01c9971efc7efffb107e4a74ab170786f80343c9e07f0d9fc44fca14b4f9e1d5942257b330f71633f8e5433f499cf6857de26e1803c200a49020d2d2d924f9573f6a2becf589ad91b923d02428fb0ec3249615bcd6c7722e74b9d321db4b59459c5493a224d4204b56adbe67ad3b2e99085822fe593069d2fb347b88a9f4a261e60e8003c90e4a60bf1beefa3342bc271d282b6033a1306bd09400bbd328e52454acc4b141d386c3640af0fb123bff1c877aacb6ba4e4aa17d334fd304dbf7a19770f849ff6b3e9c32743ad6793a7602b9693fc49ece3239898faedb586f16cd27a9df2d74b9a300fab5e77aebf6a805c533bb5b4285c2a99ba679590d1d1a47a4d2d961cfe1f968aac2d0528a3e250dfee1fc7023eca403f12f0eea735a8583c2dcfb0198f0e4f694d341bd9db5183fce3548d9b4395572dea86a5ba00e2fe952d195061e8525eb1a6dc6cb2a3e011d86021fdea3acb25e83210418b1947371be19911dd36bc40217f90cf930cb7c6c239eecdc18eea346ef339bfd0042b8a912bdff6b47d1128853eee94cc8138c9be836e08f61a92ae6276acd8b5e3b25159b41c141be93b0be9cdf473c3087cb05622b00257048a7162da81017770cadcd602b721c6cce43e612936cb55404e7c5ee0d76672af95f87145a665cc9fc1a87d1dcce091131acb8fdc219c7398c8cab522966d131020403d742d723363a7c8d5a13274f08d883f1948c7f40626bc5d84335629a6ec3b144838d5cfae508f0bdd879e1334b1bcef5abf34e7249e95f9d1faf700f1d5a692c3a3b6a961043659f0ed32b48bd4d9970c36e897f4a11e6e8a2516bb6ae6c3d9075963cb072d5b8286a288a488dc523038db71e7d389892934503ff36ed7db63e6b9c6faaed2432f8ddf45cf603fcd37e96d938a49ab514e0387c2556f0ac98ba7f4a7f37ed673d2448057cc9b01aa155e2ec12bf51a336343debbaadb8da12d03148cd655c6b3874f4d1e176fe4dc0c08b452ecee6ad76724dde686e86033df23d1e60d036bbc46f61baf4a6531bd13caa63aecfe072242e757b8a2e775eae02c28ec08c39cab26047ce6987151f9ccfe1937c768db014c72b29c76bf92129830e0710b75e7bdfa6ff5686146a30605ff1648ae289bdefe8adbd74ccb997145b98e34deb89ad96145320432c91f8179eb1e7a3be5932fafacdcabe27ae19a426d059bbdad2e9c64dd0bd0b859c26b39980fc6a4ff7d9e936596549d133d3e46b7f0c2dba7c97846a16a5d4b0e46a30945cf71188887fce6230c0ee97c196161a2131143cff7f14728c8fdbaf39da8d61f22c8782c16293230e64add56bd3ab8fde1c3582fe8516349a6d4d007c439f3995091115ee5709736987cebbd05aa814cdaf435296cb6d71df50ad00e1f05b266f98469490f67dd87191a52d4d06ec7f902f8afe9114c397ffba6e898fe5fec63bd84d5d1ecf3d6c0a9292dd3942675ac34222cf0c0d11520f9fd3c0437f5c18ddcf974fec24ac69467433b085deafee72eaafc4aafad0d23aec146b43d0cc0ccf9efd95b63c87bbe2194735a1531d032007aca58747b441271016f514bf1efdc702a373cc59585a3dd3cae057a8d7f681810316a85f27b2019c8efd9c4212855d24b0e9cc3215a90dc11850485aded902685785798648f2a765945dd1906f0919e0579743f10a63554b04bbffe9786e6d6965d0d55c0165ad7fa3bcc3cda7bde2b759cb1a7db13d4d623919fe77b15082f8c8cfc8fb450d1197999e7b6dfbf6147bd2ac0df081d2ae2ddda5d03969b99bed9ceedcef4f51e90669953d14acc8f651ba37c0dde3e4454b9e286fca2c5703aa1d0cb9950a1892be122d9708d518032d0e38e984c9cd3fa6fbc1065347794a49c7c5c1a9d3b595ea2f5a5d868efa550c9bc4e7150acb583f26b3051a7cfa210e341554e667f0460d4e5994b742c0c9fda329bba2ae9f30fec34a2d7155bc298a7293f903677bdecb0e5787f0d34f6b045533e2560341fe32aed9e0f1349dceae120d53c2bbc5b6c1a522c3f66daca3328fb0534b2af07aa49bc249378ec9a078b12965c12209f9eb97111972467326a859b94a932ef48668d25085180241db8c0c40c06f53aad8e87e3969366230cb8da954821e453018dd6d62a5ea95ede89723211ffc06c5c7700636ba7f2f67d0745d69c172fa5aec587c380f921bd18a02b6622a61642b1e4a3c3a3e6e2aaf695686137ba9fb19474fbbaac35edb33f8b88e9eb0455c08597cbb13bce7cc7872d7493d04782e437c5f2bffe5c2f22c40011c278d8ca9f2d1ec2b44989892f08a99487d64bfb8739f3d9d3a8901f689d3f58d366f7f2e873379a35c57d9669f94f8d052b7e1c29d037a8b99f01952b58c2c9bdb794a084cbca5218739cf47ba22645418d1bc45833dd0110d7eef2514c28168dc6adacb6de27eab93369f6d2a904c6f8c8f17058d5e3e243cec629cd7c3df56bfdbd85ad9d7a2f653204eb3367afe5cb0d48bd0f4228f9c13068516a75f0568da3c69f5568d271aff9e4a7e871cf023241782f7ba279d17c53e1eef40c77512212847f7aefe6fe649c615d70a740e825988e376491abc0e1ac7a3a133fc28fae8f4b7ce5d54616d191e46d164ea487fbf8b76ca813b4194375860ef2cbc8768874fec9567ef643eaf14f8584ab4ae3e6260c2cfcbf5d6fc8d1e8392cd38ac590272efd05b89ddcea40a807d2ce8e7be0dfb1288a8ad1e9c716623790c5c431405c572f6804159e8033b06027bafea9f2274bbddfb70edcb4de168fa77390a583025de968f9bef565bc812657e4ee6eff006d8ee3d28e16a2462e98a3749c38fa64e5b5498045d944404cde79b8a7e07ea7b274a3666a121880991989a6a9526949934adc672765f872133f88298757e2c61b81edfba80b99a81fd48d7eceb2ffb3ebcf3d78541cc1cc04ce993815e3de77569b100e546369a632af76c1f77776a954b19886729e651ecfc5ec3eac6e2e38dc453534db8d2042cfe2f45039438d232c0f6075c1aa43a8432ed1789d0f723033a4bb80faea563e3eeca0d57ad1c222e1fc6db529a3641a181a650a5d399855fe33c4434a77f0431bba9816301d0e61bbf1eaf0fc32b514ae5759ba2fba95ba2639ec6a34616b5de33932f41e14e588e2a5a1dccc1ad769682868217da5559ae4bc9e45a7ac3c51482b28f0d197ae55e7a91f364d278880fd8a47c89e075d7cba78228e8be9f0c6a3c39a9d8c1e9b2e967b4f24194e765b86c6b5af83b5f2df05e0835ee5af3c412c1ace4dac7a09b6b56d671c96c95e046c7b291ebf1d34f2849035db082a896f401c53b04e2632c911d68447151e0e293e8f2ceac737065c2cbae950a78b0bd81921f6962f25d2f168386eb1f0f531b5570020770411e46c162beacb210cab259264a4a4db401b80f6f4d121d2698fe2f4d2898278dbce9633cd88ca796175e8257e2179e49b28a72c61ec34841bacee42649a0c599b995942bb7a9966447f7eac18cd654d2d7e9173572e830c665ede48140c183422aacd3eae029d48db24fea1de196424ce10c296bd8fa100028e5addbc50b947ce3478b7556b475bc967f47cc92da7cd126806327341e0c90b0907dae42e5c382bef4ac64a4f1fdf693703dcda2a9b7e163fb0d99647bd9813f6d9da40e3fe1a442caf86f86b1294357b426cbf79414aa33c911d45cb176db9ba1f33f0346ea122d7caa3c146af2d354840f95a1450c92198dc4af6135c18ea3a5a65cd78bd241270f4474393b368d652dc4a5b5e886f2a8954455056514c8111f24c7a0738ed58355bf7c1f3b11529625dae61e0f179419d8df147651e2fd077b77e106a9e2679f84504bc1069905bd48147d6029f23c899c39899e03628d35242b0b9873bef03eb027bbacb4a251394164b112fc174334284311ed6e328a36840ee20f0d1cd5766df50d047d264a502110bc0150eeb6611d2e8c6211671f860d3312d3f3bae9c351abd6b0e16d30e659ffb9044996e36d57925a141728f81be2b127a426b70e840e0632eb6dd7810669b1c9e808d93d99ab80fbe622a2a1297b00b5260b038ede9b67a5ad9cbd949a902bd8282ecc07ea774d295b41f9bd16027884e1495d42a42bc950bd5a71d729a61dd68b66a8cfaf43447cc2d624e94032000791319532e6441be32094f96fbb508bd84e6cfaafaec75a807ea8a267f35100ce3b0869913f0f2762e6c67e33afa6f852f4920dfdc9b09971cab84dd86fb6e2dcb3dbabd0336215c34921606b6d59ffacc891bea85ee0a17105692608df6476ebc094c9ce7253c3d47e4713143e78417282939d3787865d1945e919208d0ca8afbd549b3d5f789ac4bdbf5339b3efb0f5089dd2fef66ff61a19bfb6ce37858851e3603e43b19c38acaae335ed9683986e54eb13aec58507ee1abdd102c8d2def6e367a4ff3bba9ea86535559e1160fbdc424662a16f3bddf280755b4a650567491f56a568638c8959ea5805f6f7c6bd9fe5b26014410922c4410f4bd5d5cca1e5eeb11a3053f075633834523c31dea0c77d0c601c7f93796e022eee11294fe57a1a1376b51ab60f21a2079b600edeac4587d9a71e1bbf91eb186f270dd610297498140c0a83c343f9c69709cc902ea029b8a713875fb8137091d8b6417113d9d880d62d20f530a2fdc14d15a4b587369915e68518cb0fa5f429800304f8f4510550c4d8569104bfecce6de406e1a306183ee37ec4c832cf6ff971524ecba040675a62a1b89fa3e7a771614399bf55623a183e4699dd4102d0c6bfb3cce993ed36a38bca1a5593480301e4540372ce00ec04cf5b490b369412e6e3f291742d426028c9a046b7cedc8e004b0c24b6209791fadf28c342b82ac8a33f2cce06b3f9da4b6a363472d578f715f318ef0810368db5aaf0006a018d0978a8973faad51991afa8beaa3155302c43567ece46f17ec6dea81d331f81fb3d829b881cc3a87e665d784c67f1edbbc77c841303470fd0b97ff74331efcc3f30b9bb5be69ddb3632507eab11a4230044000f16ef0d08ea1e74ee9b84ff7f4d06af415af7599ab0abd24224a6e3460c422a9c535bc1f006f01924473ef4a8b4ba0ae25c4e0916c70822b6a9102a051b7ad08ecc350167c972ef4cffc384f09a38e6300d939e3240a58d362a0fe4682c0a636c736f41338e841bd6d59ca8cb9419643c71818006dac681c951bc0d6b53a88806889f781586c0f28a3916a941fe05479263012107f2f0ecaa82996992b4c178b7558719a1ae0e450381b91039dffe0a99918ab8953c867605200ea57bf6acc6791add0ba6934c74c97aa780d3c668b0d6e0206a00a33153f78f74ced6cec1f849e4614e49373004c8fcbbb5636a341fa5fd402f8671f68c1b80dcdc7449089ee2ce0f30a03f2d1e1ba311d08b9fae5d0481a790c3a271d6e8d246ef40877e1741a8a93bbe61ab5bc2de13593928a6b8e1dcef79f123c09b89db3e545636103c744ec3b83a3285495ea8e7ee902ae8bcc1fd1f45b385bc002d60daec4fa1fc02a1bd40dbd8763b666b7bd37843c260fa050e5ceec0ff635f58e82b891ac1f1268e25569db65faa9b7490ffbc8d87610ae816a6f6a6923e1f4aff005720efc9859b4cd38178dadcacb415b763001bb121e980f98a3e292501d6f2a685a917c502dd2657ef35796c818acd4423876858a7f3608701c4f2ccc4054149089d18178e56d6f34fbfe60f0b9fc16d8d722125ca793a948fe49c9833abfcce543b208f7d5bc92de9ed79cc4fda7818673ad8de745de254218ad29522d8afab028d58d3751317e730ac347200f7183a7ce4ba644351258c0ea5e5ae5f83046d9bc03e889936f7e4e18a0417ac38fc10fe3f30982280c01cb388e983b5ffc6299e522d5cd028232c5b77a0565282f15e0d025207b16aac86d582305552dcdaa85796fb98df00830cca0d210b3ea8b5ef2d0872c603cfcb52e01ce203bade446e286a12d98c15361055f2f0c3d80a73e19d8eb6dc1b6c43bc4e192f5510331413d7d8c17409ffc463036c07edc6267517c9977b4570cb58a1e1661015b1ac5ef03f9536624bc0971f8016471c971148f5c2911bb1a35f4f2b5be67a51038b03f98759b0866ecc3195f5addd3f5df52dfd68a3e0b4ccb64b4feb6856d654f208e7f1418c600c52d260c81497f7ca6c0b443e173e42713f9f9a598faef96a8ce9d4be563585b063d2e18358b57f7639c2da9180a460f519f2fc1f60cde247850a9002592799c21f1b0f5fe1a6b0b95bbbcc24fe2de670e16b01fa7482e7161821ef2071958295828a5fff10e421dd592a8d7b11e876d27366dd93b4bc5638cd7632bdd5165140a7b7e2defc437d69ca22fd37dd2caefc58dee8c5d81852984e2a26aecda700c2f5cc873ba8731f549e2a15d58f73b41d266f248f0ce5925c9a6e4fc731061f3d141bc4986b332177c57287120c456ed10d161d045cf8850dd131a61f2adf449bc5f3c825b6989f04e54927fd65b1be2990722928d941639bfa0c57c8408dfb82ebf4156026a9320083c0db9a44949dba0394228369969aceaf7a33f0a8145be4dcb422b72961ec32a68967b9d394738ed10f633979410e39402008b5174b0a7012aca0ce11eedeb34ccb5a88fe7e5db6d88b5bb54a22120b902c7631acc155bbafea04ddabcf3e5a6d9f45928dc8fed7c945d5c1ecda125414df4f7c0f9a5bff66ba0872745b1e1f3ad3675acf27093157ad3dad911f3d2b1035b7d5d8d97a9d209d3de83e1eee89928588f638c54b99fea20d70773b7f6e89dc3c44b6b5f15ad250eb8f5cbe3fba3b2237b8100e3f5d64f043309f9f2a3adb9864b8981e22bbface5e316f711814dcfcd0e3f2aad4d52771910a355cfe3948a443a9194c0ce0b621d2216c65891cdde63a6d9949ea1d4b6ea2f1606cf15672f271533cf16bc38a36a632b07a3b3720367523a48a09f49e5534aa856e1f790958704303ff60b922a3327d395d42d33eeb0c8a66d090f0a884485b578c53cfc6aba4462cc9240bedb2c161a691726dad9650e41ad07f88f3d413efe5df24a378468f1f54224675fa3e70d56dc590a8f691dc9d83f8f9037a7c79d27415433103f72b8a7f17fc3eb2478613b2c2fdb84c1bbaa558d14e77297aeb8c17a369b027de11482f0d381fc116cc1a86804abe000007367643878bc42dc29f1006ce4e5870eb0454dc7168456547229c0d491254b9a9d983612b54a2edc5f94f082b4781cd4e65d033f57495e21836da2b27370bd7713ebbc8e3bc4e66ee24af105af794319564021c82b80b4fd5b6157ca472a0b1e6afa314a87272f5e10a8355ad681a6b2ce5b687e2d9075514fc58842f52d1b37e2cd0331244256c1ee65be16e5f0bc9a3a483a4d836c1ed3b536c2fde41c50c4c6026e80a1e62444970d1bdcae8a0a82b9cd148f10c1e0aac0d16b633c5779485e5a63f2af49973f8d1f0dabdfedb368668d6cdf0c33f3b3c9725c18461d7aab85c4d9bcc28fe30d8e23778fb95dd8ace24dfaf6b24054c7f30e06938766d7e0452346e21a271c33a29491ada422f13f795f655cef706008d168b4a7629048195a930181099d2d79729d8cee36644ba8935b8d6c8a4de37f204032717541bc8fab2596fddb8c0568642771baee37e82e1e0e9f8433625d4632464a9055c2efe3610fff381dc014d935cd72003b3f4cf7832eb454c30af02b1d6edde3ac133fd82b6ce3e09a3f748a01da420e412606e77d7346679fce9c246b50623d231a458800b43ed9ca71c954e38c66cd8e53412e082bd2ea410f21f8ff64cbc18a7a20db60f0c902568a6703e5f76cffd323aefa03cf62266f41c6a2d0b01490e423172f3b62004480381fcbb63b0c7a470979c1b1e7e839a7d43ef6877a58014ee6d81230c8d1066ddb2fa19a3b6352af2e581f2428b176c7b87fe98bf4a16a7187adba38a1ba5ef4cc45d833fd64aaa6757a945d26894b8b8071023bd8071a28bcadb9b83909af6d09236b4180a6af181d5ecbf12e27b7708931091bf6f6248e2e5d2e60405ed85b1a9bc150ca2ee734f754db4aceec767655e9a09e83695f8080ca56833bc3a071128ed06f99d2ce4919ad745b1863fd26598626457b6c6109556ce1e4889c821390167575ab78917e2da206a94fb6fbf6d31b79e8bd95dd911e73a09aa9e19782c4dc46ef2dd4cddb912ebc38f10c1ad9fa4932e8af28e673d547a0b60b269cf13e98921905909f714d1f53adddc375869037a123e7111afbdbbc5d73cdc2255cdd1af507746494375fe444ead8e46a1a59a359728bb0f6fd637b0cf80f609610b3a07ff414c31c57847a071ef11e244427bbfc1b86efdcc12bcb591abd03bb029ec1d81abc7d55f6f515b8469744070f193970b61097caf01ec2c27639bb68a83e7108b47c0e14c5ea05fb90cf8c7984169566a4bb2d7b187f8bc15e23c2b06eed73c8c8512fc5e9f2b341899ca7842a430d0150979acdbf6277c84b94c4476b9e592571b8c3c37193279a2dd67fe01c21d32cb910f27c6206005d2251ccff108072eb8f1a50a97227f61b685a15c9e9d4c2aa14a1e4629ef40d6965f01d5b249c371d01986eff241f342abb29b97841bd096a6e62867b2992126153f925bf6812ae148ba5884d6f64f1526f919a844cb613a85587cc3166b68723593984d3dd91adbf2e089a83ff5c8839e2f2ff2199b492ec62658e1767f99a88571a8b55573f313a34a1b31eb94e328e2ac5f2b3e4c920f1f0bf6e92d63a8f1474f13e9cd9cf5d2f772a4e16778e3e29a00fe0427098edf9b80f43edef3d64c35cc8279fb35358d7349161d06894f0cecf976148a91c320f00b0d32fd0a27442d60f829977c885f187c2cc6f9422ea6bc6c233573f073af3b2951de2c10661050b3d2832ba4f2d2efde096886ce17928e3c900e5a59c3325d679173b3a3930f5414bcf7871ce345b6c7211ef463262ed3146c42536a5612add55a02320aeb6c405048bc23f5cc7246f4f3b038fceb245129b796f216c9bf58dfe5827ac10b4e5cee49719d28e317e7a0a55e08f70478fe5643c95e01101158460f2b3b7750698b246130e11459232d90a2e0cf182c27830c8e7e30d81b304000b74c80a10f1dcbec402952fe88e3c980701ef15d536b68fe75e63d5bf14b97c0dc342a6ddfd32139a45469c23d57d80a027ebc25601c49a77b3fe95c5b6c80af62630fa90047d74b314235c6ae0a08c02b78f0565de9f195f72304ecc4a3780adc0fe8a945855cf2c0deefc2dcc0b8a050c0aec6b5b5c869ff2848a0bbd48bf2e06b0649772f3c037ae7da0541c5b837134a6446f51ca1f962088832656a02a48e2a04d4c0292042d2b0ba3e3db1180b8cdd9b45ca8a571703e720f188e1eaf08f31f4bcfd810d4df609941bbe18cfc6c353b31704802119cd2b24d697e5f59e863244419fc06d7ea0cd20ac0c5fa78882c4097d1b318a8fad2335345fa9b4dbdb881f839e4a56ef8b64dcd298a590d2e562667b65f63d0615e5b419cc36f84c5823ce134ebbc5d353d493b27c5c5d2fa37529f929dfb5297b8948776586c100529d499e07edff1573525f7264e84fa3b9d7ba4a0cc5f91158e7b056515622218cc396a96e44f92d6c2d394cc14ceef4b6483c0961a9b18a1e5b968078c9f70219c35a15f4eb61845e8a242937fcb92404ba62d62102ba014b026f53911f5764d58bee2a221000e2f00a70db31414db63d2152a5cc710616327315a16e86963b9d94825562386463430ac91cc58243a5e632bf60e0464cc5ec8c0aaa58874affd03963c4e24f8bb5b260fcee848376f26e57c09f5b95700469e24ba9d5b1063f2c694a3a071f9e8e308d09212dd170588188502d0eddf71b334c27da53042669e9bb5198038172e106dcc1e5cb46dea31c1a20e7d2299e31a1706ee24c986efb7c424cca2b2ca19fcd7049cc41cdacec138835c0a56cd520f9542a07598a2c0c350963c023118e5934e6511ba0fb654fc441a3c8481cf4becdc215659fc1f89ddfc663f2338d25cd1332c301350388a0fb758533bb85a39ea460292b07550eb28a9c23c9707cf6268ce80ba2efe4304139c280fd381bcc4881c06c041465d239bc223e64792b5bd89b745125e08b25623e858a90697a52bec680485be43a87f3d0422d412298e45e94d5b42c9934c68b2d2e792930c597e30192426865e4261dc4a05d062e124627fe50f8aea47397c2643710ae699215708574a6985d1d1471750e662a90b0800970922b05e50f94e68a756199eead0039618a697c351bc40b03a38d5e7b75d5fd4cd55cf1d46e639847ac851b91f5271b73e8dd010f1908df30dac6ceeb32def354d70546b46db40e4f721fcd07da17d8cf3c353dab9773763be2d85b9677d0e42aed744ceab756ff70c6aabab5da04cb852657198dff1dab1a4efd4580d207cf5074441bcfa25b630e4abd484d48d64811f560b232cabe37634a109679f72ed9d913e5953ca0cb98ad5283f317899ee1d7703dde0a7db879264b7ad94e86068ede0458d58868303c13a4d710ae198c8e51aa31bdcca0389531126e75c6f57fd81c18e7c46f0cddae9e3c38c8f93cce66e2ed8bf620713ec2ab7da758625b338995898a9d008bbaba7f2f2bc0a3c5c289b561c1e830cf033e87ef63919b6c401d12e6871845881369a8852db802e531631aa4b24c8ff28f3290e0e37af8bfcbe2793d91fdbbe69fa3f915fcce7745c50f7817a5e6da0c878c430d75b449bfda414dfb1518c378ca8ef8c7ac944d0598bec8f66473f564ee282d83d50a9a6e43e64f413dd7d34b5499e551ea6f71053c6e7dc4b4044bb2ed0841ece9af2bb5cfbab3286593d58b6bc96f9daead2541a37478f13d1616abbc689e4cd657ba09bfd44eb65b1319848fdcaa178ab146520e09654f953f8a5024ca6149f2bd7962560a0122174c20b46fee36bf3410eda3dd2b9f8e5ccfebe28225d83d75cb152cc646f22388fe1a3fde5dadf277b38d3a8d9fdfbceea8662dd6a98f6b4e47242973f1b630e9b6d55e8e1a5450d2171fe4f75aa96cd51f5477314df4d52d42bc86bd2c2cb16ed49f6d107a0d7a561e9c2d47dc20917b22dbb4f6f4566428e110203023aa42534047a5df87069c236c55e83176ae3ebeab613c14c6256484bbc16c7908d871b83afd179e528df6fde779d5162c69ea4327a6e536a1fbc6a0cd325d82cc4b7880597843bb10950e0b3b9455905738d13729c62a35e1b27e6cd62646e063e502d90a1b9923af9f688a048e062c8cbacd6b1a1ca1e2cb3a5788de7e9ca4ca4d68f7e69747a3bae59af765220ea2948fd8b87f0c21fcf3acc7a2c9de6c4408281f3804975f7f68d185e980097f4c0d3f199a4e0c64f10b437e33f00ee0a08e8e53d67800e8f6fd28b0fd056931bb9e7c371cf59558bf0696565d2048fea63c657830560b7d2e757aee70eebeb466537ecd8043e6d09b8e29b2ed42430eb3aea81a4decfbc14f4c691436fc91d4fd027a5bc0ffd448fc0ee68c43ee029465d4b060b2ea9044b9dd2bbfc11a232a655a5ef3978cadc96297d0f0561620c17aafd6588a8c43c5cf422bfc5c8d8c3b8c2d2eb34e54379a36b262d1922c10f366a595e36529493145f0f6c6c972c6a4cab77c1296a7c3d145cb3b40b2279cf24bca19e66ea30d3b48614672662583357a58133ec8835e74a9c0e8766446310eb32e59f87a07812ea422635cd54c022385e92d4b89fa29259545d612ce51e11cb053f31a9c51a44243c4d88e0017192ab20202c388b208acbb1d8e33d91ad8948f7ae7a2ba8d91d86c55de637b43ab6120a25faddeb834277c1c21e0081787625fc86586e700d93869d461c5d0931614ef610b1180f4ece5c6523fbb052428be087ef0849a6c24ad0013192152c0ea244c851482f9f06c27cf09a81f44dda0f7da6e9c6f3056c47d2feba78e4e72909fc2fa1f1fc8400ddcbadff3a99103a9200cbcb72119cec35bca33bdc1ed93a36750b20fdaa5ae3cab7d989dec9e0ae3af43d1393f654f035fd0939fef6bbc009d07d208fcba62a6d77094c898dbb386d544616b23645aafbf9e079d9965715d630b7c7266220b641d41c3c2b1310d40e28ef9b208ab4df81f8741be2c998ec2ba16cd090df1bdc5ffd6248edd8014df4006388595f2199d9133b23d3b5ce76ee05ae24ab207044d342d0fc4392a1c79b183b1cead382d9f4f7bd57ded14ecd338d1a2fc1debed20c6b345a20055af6f9846b9f1ed0d692154b7813c4a4cbe42c995ea56bd2b9b863a2f15d20f9a5841c5c812ff738b5816ae380535bba3f7bd1cf245335fd2f27967bffdcbddfdc1c0be7075e05ab5b84082ed78b79675c8b1cf614f408eeaa6116c2979dfba42dde57ee95eee8d005778531819257d898a23abac94d255c48669425a3896d26886ea9b9b73aebf35de1cb1e9d192ba6b23631310e4003f9699b4e99996912b220774f3e1476837a5a32ae82251bba18ef085f42e84249d42c7b54d9000ee848564965bc80cdea1301f78da660e4475986bf22e581135c9206a814651e1ee327223037d1a536ff44b5339b4171ee2489cffcbc06df22c594d780ed6025b91706b650d8843481d3305c5135a659c8aeca6ad047676bb9118f8c53beaf4c3242a4cad64c6014799570a110cc84bf9fbf6b05c30f5ad25febd3df1e7e62adaa9bc9192850d71dbdbb7b810639a47abd7de85fdaafd28668d42608dd8a2e64325566d585a80c1e1ed9b49deae6c83d27e9b0d0133ba7319fb9e8795f0c57db966ccfa1e704d68c00e5b740b133c6e22431ea98a033375dad8e37cb66a9bdc7a3487c47cb7f98b1738f473ef19533711ddf1ea7e5793fc24c4502836599825031d7d2b9da9f934450fce907f081db38f9c3f98a159655bedccf9de829012036e7c992296c14e9de92052f6e28a38c12ca826074c5a3dd7a6a073373902f47f4085b47f019d3e63916374496c09a9d9108376c2a18d87fd88f182b7663f94b7d4ac8a2434cb711012b5c0a5e07b3cfe85e768b08641dd878554fffc43ba0f6f5593c997c9c659732fa00830abb609356872330f84df12fc38e2d4e4ff8482474f9e3013305006419e8916e0163f1e43d30e39a12b6bac0f1cb48d57904a0f898b341eba30fa55d68bf084836e78aff38867f73278a0c0fbabff92154da48fbfbd035460b5cbb4cc7e3da2e93a1a229e92daab1cb18830237016bfb7f7b4f2c1d415af4852da2efc3d8e7adbfc7a3aeffff24a3c144df52fcf9d30e021bb9e8af5cdbfe23835beef09d20db03a909444237d24b130dbba49e1114ceead5ed82a8e8f60961adc40d62e4764563f3db4b18c44face71bae1fb5f5660e7f4f2dd338d9c7118f07ab7d906240eaf2443c00dd411af52629dd056cb5286050b398742942b0e2cdff62ff4415130803e638186239ce22e95a5eb3c604a5b46bdc0789b11dda1c96100565fdfb7d43b5e1e98bc0c19ec7e530169f709b9b4b0af39058a051787c4278ee61004289c1205f8460b2feecfe036419dbcef8ca1b1222b5943c2a76b569cdacfb222941526572ba7733da4fa36c66ab50e2c65878e3733e049185e7e081ff00f9e495d6d3a84b25e8fd426b0282cd832b0f44d6a3a3a3e1795e461221f224991b772366f9b37e6cd1e7100b3da5439fa90cf4e16677e6ab1f9455ccf0dff323e8de6cbdb6c128fee349a6e546c17b03b766bcf217ab1ff84ad3ff08aa6d552f78ef4c9ff0bfa17cb0c6c6fb72b5fd524f707f96c9f1956a05476849d7aad71acb392134941c43f49c2d9154d058aae7b30de3f08f462edc3fe885946c1b885fc0a7b7968fc5e77dd240666027359400d4d148732a325b667cd2c387155bf89bcb369721d3d193df7c8942161966f8c0bc48cefb6db88f19c6df4040f11bbe8c46658a9d09cf88eb4764969984be2880a9fd52ac54b98c855176dba69275f2a9dbf8a1d184f7b9341a31ad8e27f23329af6f32803c300c3a11e1b7c78fcaedfaeb82208a65793c5f78f0ddf743d5555dea428af00e8777efeffe397facbc9ee1f7f0f428c3243f9cd27fea1265fa02ec8f146322b92b993f37a45de4a819a94fc3b639ee1f1846813532ad17af805d1be12cf485e8eb92c0ab68ad55e6ea84e836092946b41134dae9b57468e1fbe53ab00bf553993da04ac4f2810abddcf72eece18b0c61fcbeb68187d7ceafb738e1ae378b9dce96656c52426cbeb63f7e74f486a7c6b2f7fb1685ad327da38d8aa54a8ab0eccdc17d2bb68f95344bf032765fd51d44f4ab24b2dc75e6538a917ad3a7b1ee55828f73e4c3426f6ccbb499c2a79612e2582fa8b5abc8020a67f7531b2582d62bfd0d0032c53a2b282c7e768d64ad7578d61bb65f1eae9b721e13a74c8751c6f4564dc7c13ad833255c182f0f29f11eed0092d1a355d255130762d61ca254689a8a07d032f73c78ca3ca92029623d8f0ba52ba6af3bb4c5507b4a6001b22534e448b89e6c870a547d5b1422663d99c8fa800c62b52ef1f283b3594f7f198fae27db8e3244642527cbfcabdb169450b30048df786e8511362e63d332b3236c4c7011ac5b9de6ce4fca7ec3394e22a949b09d8ac1cc7e3a9ef7870bffe9ea5b1da5efab9e692ed32fc683c2b8f90eb59632131d2b105d43293dad57552566666861e98d338cf401e179163f8d0bc28632da6039c584909358c7bab15221f219bd793ad497f796ac131d962587736a7b3dfe167d099b75931bc7f467c75fcd1211507c713ad77220f8fd9ee20b446b1a8be839a9e9f7412ed2b0a6be00cd6de9a88401f432ce9484166c582c5933ca32b8289dcca23d8233d76e073d9653b6801751ee504842f1274324ca3315d189446b2a897adbc0de9774c999f3498c971364906e239a2a0668b90448da4c50ca7ceab342ca3002a110cf1650c72fe4ee7ca30ffce20d953610477866f126954a5ea65d05055a05ede3ab9f0e22a42060ce4743d8d300856a0e195cc0ad34b64c677a8102ae6d1bd731f302e3bac30ae245f01e455d0fbc73ef85c2ce3d255fc43f3250c336abc26188d4966e4b462c0a501dd995be95e4dff46c2a8093551154a31fae13043aef9903a86654f24eac4a430e0bdd466d1d02a1d9afba96d3667456df4a57b2a7fa7630ffb508b41f2df4c800ba83b9fc1762b8dbd2177eaa74cbef91da38660943e50524bb25c268593720eb6b5fedbaf95a5d1431007809a13cdbf68da181d29427a064c9b80f1bbecf698bd78d7de95efe39f2a60fcbf945e816cdd9bbc558319487c73d4b0cd67bceb0a3beaad656d3df19792b845c66af3196b53aa03c543701de37e3bf50d1498c623ea12315d7639c15a6ee0c3859fe9dba97b64d5b0370365533e48650a3964b9823622df96eff31a37217146a0f17fdc2d35f16f48f36d7753d9519a3bf6363e15e952e101c01169284887c7aed7eaa571f8fe6264bad97b2c2c3db56e57cffe5c24f4010cd92a1ababf58427cb595bd9c393130efdcbc8369e59f040f9c079b987394497d426117c7ec3a04fe9ef8f5337a8d548da74e14700007d12fd817fdfd2dfdf9b7074b853c58335199d8a3e56b654792bda0ca96ae7d47d4286bae4322150e793e336191660c175d2275e01b04b2c1d1197ca08be3ec3f8bd79d699384d34c37f71483f5f06548f830ccc562a109ef843f5a19f5cffe01a525450d67fdfec055ce466989c9daf0318b3365443ea63880d40c25c130daabe897d77ece278665bd8c8d28c7d7f4ac39f387f645693807cb1b8eefc0d4ee0f5ae92bc93c872d3121009b5d145d452a4077824d70e883a9fcefc778bedd1b46e14e2f90666a3ef920cbbfd70f263b887b4aad22a272a66ecfcfccfba0265774d22c80600613e4511ad74d61d52b530d6532450e5f36007cadc78a4ffacd39fe7a782bf7f93c69a8e2171b0c58bca4fd223b771e882b8149010b2deee3e86c2a601dcb55d5228fc21aaa6571486b8a554363e17cc8a39c28d39a785f58e0d8e51705d10e31eb74480cbe1e53603e3d4bff5133d0f8e735bdc7efea3b10b8f0f270e783f1c2c443418bdced5a5769c1fe0841ddd7d6b38e3ae01e05d3cc6829ebe8c685d81ea715b445bc4c4e8800cd06485760b4d8ba61af536bc92b40b6b25e19c164303bd6346a2336bdc6da67dac9ea38eb8febc5f166a60f2774f37d8ce47c581be6e8d58c48759a27db3f69f2b071a442f32dfd5d2fd96a6ea035a9f99f39db277bdafe73d377e4448df04a40297916f7a75fe63c334474dfd0459903eaa0af15e7803d8d1d211a656e0a6226b5aefe6e6bece08819fad20b964b9542815d8225c972051c133292873747fa37627ff003899fbb57cd17073448cf48e3f8140ff0efe07b78cc68d23b6bc47a9ca7a64005098a305424bc55ae1fb7ea3de6ee5a10807128f4caee2c83bf3d406b926460bb79a0971cb8b03c18a6ff17c49d9587d92aa2c959e1b35d52dfaf209f8673ca709f9e58e587fb960120837028cf9cf6bcb31bb2bb2161ae99de5409481eca7fe2e6358ecd632614703cee8bdc09a471cd3ca233ac69a23c23bb738fd5f22a70efea5c1ba1aaf07a35f5b781b2ed5b757930fb2ac7786bf8ac909d391d53a8ab3b93cd7bc21d7aac0d03282be0f2b7b14c75828036ebc09cded0ee73bc7b9d790debbb29a32bf4f387634d602edd17fca75f07ce3b81959c13897042d4b5f133694d8321efa020b9801c45404fa41f39f5c509419a5398593fe6a372566fcc311dc0acec33d1d36afef6d32c2592e42e19e9345185fc33501bc79dea93721eed000b9d6001c1473ac0e457d4fd3cbf3e159d8600d01b4c1316fc0dd281d33da2364adb5ad9b08e926424d48c80ed1097e0a8e09d443785b4122b1dc62b9c562815e412c96153a686459a183582ccb0a8d2c1048c5c22a2ab734150b04b24020ebd441af16c8aab7a292d05f0e55550e4f4e1255c83ee482f74ac9945cdd6278abd94839fb48db7ec8a351663909f46e125e31ade02dcba08b40771d53faca0ade6806e1ada4b97e11e1fbcc7da4c9ab681b0d29877e43a1d02bbcd9871ed2b0771150130a1d7ba689dedae8206d2365d061f6537ff4ac475884419d4121ad7329f413e8fd10aee10b594907d0fb20dcd73352b6b8ca1c8c1f48a4c68e1ca16336208e20fb68ca5b8b0e80a9f3c9e76ca0a2dd01e3618c3ac85ec708a183b3012e368897af527b645e1ad1c64c81c86a10a929bfcd396794a4ac6b4fcbef6cc8f1233a28262094d43729a5d4e196646fb4e449d9a28b91b68f2c5250f65ec07818a53a62f6ba0a6f31c555472d55db48b9622168ecfb089d22466a88b8281c1407c54191c9f1801f361a20b2bb1b22ef0059b5e28d013cdcc8ee260bd7ab0d59b968a4b59822ab2cd259ae72457f9be6fb2191d622d3a6799266ea977916cdf54b7d1706c6a8bc2f630269399c0df399d68036cd631a8e36cd5f1a03da347f35529be6ad36d3a6f9aa1da04df32b1aed97e9608c8d7e99f3cfa2306474444d8561ce39e9dd3cdd6432de64f2d43697956f3bb273771da3f20c6f3f64df723c20bb8f56b403f48b4a486b3145565bda94cd796cfe9a6ffecedbbb8ec9b083338d695bb691ec23d18d045f614c451d75288aa25040390d48b739e71c941405e3050f323254c80eb2779eee1c8f810e4aa64d0ee64584919d111e18f04424b5a4838e981d761bfd362fff3095eb1fae4a3a3c5c932b5cd2a1babdc55bccf6151622661a995cbd26577ae9c59666b52aadbdb44e677b76234a3f29b6d126f7723c409ba43c2571c31c3b96a8ec606eec6aeea32c6023bba882ec9d4626bbcc3d9eea5ca8cf381b4654a4282a6b40a61eb5cd25c7c71464ef91c625531465b2342a91bd53db8b176d72c72419851720c8de2384ce86aec9714b5ce096d8c8f1508b48b217a91262483c3803ec7036bc0d868c06f4cb939912450a153fe8a03a9751c7bc782da6c8689c41b6c164cb552d87371d5a3866772b0bd3d84c298e3a43aa8391811e73c5808e7936f0a5b2d131e49fe3a1aaeaee2191ab6f31383c1c7743f58d003030aa6a5655557527a9adbd6c553d245655352b0bebe8e4e058af5ea6150d22e415794628683e3923900c2708ae0ed222ad2a1d505555a057555555d9abaafaabfe743ac63b086fb402e568533feb634c2e984bcab66a34ce867e4c52555555595555ddb954d5ab4260679c01807036bc3b8cc268c93019144382f722bde81832379521c9209301598b46268a1bdac34c872f577bf932c9c098d0e161e04316853133d90d06e030bda0870e0ab4ea21aca791c9d6e1dda1082a28e10637c8340e4ab5defb84d2803883d8c717db6808f8225f6da69819f8225b44c9ec6fb489012da890b56842e6e66c2d89231da3b174b948c7303224fe863ccdb0c51b89a7c51499fc468a7936f29106d98d8e612fcf001c1d8348c7d848d4adcb243ac6ebf16cb04d965847cc1d5d9c0d7d4ab3b41653641d6f9049297ff8a263dc0c79873799c9bf251cc56283903a3c416a3a8aa2aab6c164776703f519f822bbf716e9e6925d8c97c26f937b51a31186b3c1490d8c600c6c7634d689eadca1731792bdc71823d43a098eb649b30d4fb913f5deeb8e60a8ce456af1496417d9eddca08a1b44b9c192243c7945763b498ee40a2d9544140283344e069271dfb469da87e5ccd65dc788469778bb3ee9af48733d8a14c6880e75c00c7b4882b02ae64939eaf7efd638bdc73cf1c9e2edc56f3aee8d50b9c2a7fefca4eae9bdbe91d241085dda044da43cff62e7f9438e34397ea62518b6eb40cadb0f99d22240ca11c399bec1112449728565da041fdbbab787be699d981bf1bd23f9b58ff8daa67b3c1c6cda47dfd8b48fbee9d1361dc3c7c3e126c6d6a1daa64df106947806caec641476e3d837ede3dd101f2929576738a848bc8691ba34b0e8834c469b5ea4a29473ce4cc6f73e2241e6f096e517713257ddbcb621f21235f2513bf2f42efec00629474cd11819f8a223d36420aad7106430326d99fa43c0180879902ff2d336ba653993594622c551c798af4e3b26768c4a93f4ee4a4a5d621ad20e0038821247c8142eed90295ca25e93df5a8b29b2cd4d31bd0fd1cfdc14c7c44d99c2439bfa07d9bb6b1e644f888c829401e3c5120bc0cc1d40e820e5b7296e32d9e918c26e23191ac743e10a2abca9be38a5bdd70de25927699678a371998156754e732ef23488979ddb62c7f81c75b996363df6abb91ec593e3a6b8294b962411050e2aa25852c40734d0832ab8293c504c197390f52be5d1cff1e41ab890399ebc65794846bfb91eab8a4c0911f3a4329e1d4fa80109c29029da2f1498afa791103bc6181b26472d02323239cab8e457630b327baca948bd701d03336d3099c242c4ac6d3632757869585586695c76d891c40a4d6042a672a5b90c1f01994ce153bc5e1c42a500954ff2f3ae06f66b6d2fdf5f56db64b27db59f6920a0d536984cdb64dd3acd16be14a0f2897a7c8cd3a1775da9ed79b6748ceb3da30832eba68e5161d55b2667af5edfcfb4ac4df69546db648f69b64df6208dd42fd6fed24634a8c4b8b18e393f48003882124820ddca3b79f99f24d64106e129f71fd4d80ebb148e79a3a4ec860f52d4dd85120709111bf4c857871b28082dd5ad515486d06af2c917d41a3c919fdd0e0da0009261b2dba9c14f8e926d7e8f0792636e924dd9ed1c49221f71926bb66ab5efbd37e0e57798eca41859820f24536001141e33dc2b66eba5bf978ea24a5c89d963ad8fe51b14850e7ac4146f0fbb3b58b9e2e7736b607d1c3dce87fa7b8e14f1166b76c46afdf5c4faa4cce8708cef617caa7ecdbf8ac5e36481b7ced5fbc96f733fd6b2e28b31c618e3b3680feda13db4474adee82ded146bad3d0f87fa09855a558c81e93cadeafbc9f43a9a5af1d62dcbb29c13cbb276985a35008b6f33c76f32b77ca3728b3b03c8f0f8e70c70ed57c5dbc3a5194e6ffdb731c69e3ae7cf4fb68e69a05be3bde253bdcbb3db83cdf1cf7a0663bea25eb8062e64f4176ff4546fc57a0bd75fd8de4be302c9daeb0c9a8ba94d2fe288b57b4d13fd5ae83268a0c7a0b1bc8596bd455b794953390b6de4b25b81dff52dc6874ff3d6a3cfe67a72a4b1795a16de68ecc4a7f877eb1bb51e5dac70d470c99efe3d8ba734365bdf686cae8ebd062ed05f1b56b4fcae22f3fb48cbef222da43d67c33b486b67c37ba64567c3c39b0e68dd1ad64f15be5edd1e6cb6fe0a6f98cfc3c18abfee71d04be36a776913c43259ac95525a6b7d0d972baee10bd99f765aa3265bf8f9bc1be8a94f4de2f87e72ccf0be1f1a1e23a5300d5a113bde0c1abe0306f1454c28336af8610c4c129c183051021b14818a19f0356814be4018dc2c8ce0282acf7eec4663ad452dcb7a3e14fb619e8fb599625485c2c018fb7c9e0f0c0c98e7a393b58a14538c9024c364b763049b4c04273c3c41b2cdf41d65e7083f3b4728b2f344153b4f3cb173841e9d936376d666201d9dd3786381787bf7ee60e5776bd44b25391de3d290d3aad57a7dc8baca47af3093353b5a7633c15ca81b931d12b5921076ba22c22b9d93257e23bc6599ea275fe313767a3921b46033c99b751d7d7a306331cb8703fc66cd0ef5ad39f1d639d96ad39cf824ff3e6350735a3bc8378942ac495c12ddfabc8d91ca2f1dde650feff2383a89943227cfecd6a097d8edc1bea932c2f2163c9d96acb1bd0b4f59798a2fb61d456a36441a0f98768389f5994e5203beded2de6720496339a497a7a80c24b5ecef2e809a81811786192c276510931ea6b283eecbde6d25afa2198d369a79ec1b364198961dc33a60c9babc8d56f2709897a3fcbed18c32f6eb1863b935de4f18dedebc3dd8ecdc0b762acbd8b70cc31bcd28cf5f78734e94b4121cad445e1a36c786c6834ca6c4f5849aa5094580428428b962efdd3c9becf04693211157b882cb1564aee0d242fa108ce4c0044de03165c7882b48a1093f96d80013274ee0c4104d0927445039d101cb66b7c3638316947478464a3a749c226b1ca7c8dc6b724d7eb8265708a1901823ce7b144551b86d80e00406e8e5822e54970b28e0812a88000c4610010b7a4011f991e427033e4faa68c284db24cae502dc240857132964143c2d904ed4cd7ddb9a9252c7a2a04c3b980d9a208490045fa256298aca288b82375c8655eceeb6015f1ccf10a6e84c8881220cd9c1902a3c01c7a602458ce044810b56f859d2a406b0091215227394dd0ecf8f5cb3dbe1d1804e472185ec2d05c6ccf07e4f81312e7d53c36978bf9398e04b976e72f17e0b69223006bfdf4660cc2fe5417151e03c21a077e8fd9764f4fe63f27870e48031d72f8bf71f0f18b3a22393ab0b4d869bb4185e9f6da1bd1926adc55b60226ac0d88546c303f0dfcd68a17da65bbcbad046a38f30112d2a2aa2128914d2de0c77690c6179487333449ae923cdc5dd5d80ab2eb48b4b3eb8f8fdc54480ee02977c5809c0574cda854b3e98eee22e3011d94db8e4038b9b6ec244bc9cc5dd0c3703d3de8cfaec9015bf817da65f66f80c0f80b603063ebc296cd0048f1900f8cb771461ba8b03e0051761c2ae09b07591fc4e44112e308f192e77215283f6a6b86825fb920fff5d70a9bee4c355ee824bd64b3e7ce52e7806ad64f10ded251f8e3f032e557c83e5251f4efa0cb864e11b2b5ef2e12c3e03c6ef9a764d062d06ed25812fef2db467447b44e0cb7b497b42e0cbcb5868d74824160d084d457b3c46da0f222d47e8d12c830d65a8a96deda052f78a11c92fe42e8e1df399045d2242bf7733a49d010acd8b6e04a4bdcba7a3c2721a43564032f43b6fefedb5b844fdc2734a7e0fdd8a4621a5078a10f0d7ce871ec244803eef66dce328f910bae8224c847ce86e461c5d04fae8eee290171d079452fce835332ad484e84be0e52084080287f5eb3486540fc2871df6f5d87dd8719fdd027dfe5d1cf2a1df30623e543f1fc225ea2c984889c23756f0bb0ae631d2704a11dfb079d9de0d6c1cf8f28e692d04be081184045e0e228805b819f30b7033420f0247c907ebd8314cc48c791c251fb0cb4b4cc43d763723f41a0e74f90ababb38ee6548a35aa69566dc05b8194658effa606fbd2f8e7bec1047c591e1b629c15f9f691bf0e5dd6ab56f4a10dfa830c934b552e31b30f0e5bd664611a19ad85406d5b58c8c162895c942143a708249a40208110a132da801146c40052000a10a466022454f1576f0440b459c9042e50416560c41c2c7069ee040912858610a31d4841430500513306182c78ce84410728c4e4091a328c716e4186310302085bb0d051821cc828f76c102844d4421c31e33e047ed82056cc309d995dd4e134ac89bd389541343c854763b4d34913718258f052cc86e0a3c921041093eaed0831bbc40a2090ca0000181901754f15c9c110aa420a4702ad229221542286113156428336c92820c6f23158ff49aa841ceb2db6962053c728a7352647707812636412041cf8df7668850e091342ed9069e894f68020579733c3726e669a204110b2e8a26906802484fe4818415c6fa1ee92550f3f0099e521a557d83596699658525dedeabda467d0281481346341326b999e8e4eeeedb03ccdbbba5050173788f9496d1c89355773d5497af68f54e9746b46d7a5de7a531e5d3e1044f9de0add4284a3bc5574ae1bb528a8b0b1162741982109a4990f8801a410808c10d0521e0079336b95cdbc6528cf0158fac050615f9f1fca00acc081329c8db7f5045de5ed02093df9228f22623c2aeb03bf76b74eedacff5d538329189b2dbf901063213aabb2331c1e867e364fab9c426cb532d3e91f51fc992d447f35675f90adb4ce9699ef6d5619ed6e0856ce2d3b46dba099078e2f7293e6e8170489527758c968f769624c9f1d41222790aa4e7b0e19636d95ba38210f0630a26e1937c7f93a7366a87bec4270a5f044b0859a209364b30c18819b2db592267091940f88610041997180419572961ca39e52604074d98c207826c41891be508103ab0822834610a3c9002e5c115620a9ad46420478a1d298450042460b8d1020b829083273c50d844412de13e21324de6bc6b92e7230f8a90e79469426d763b4b00915291deb2440736d02e4e2cc1a3033b5dc840b54305e981d73cc8d94193b80417171e70214f9739a79c73f2400b796260c674894ec4e86407093966b7b35304105478677a8720babdc83d6a530948862f513807265e6024c7cfc018a8638301396880e4be6b23228f9cbcd97cf5f0e0c183c70c18b81d1d27797351e814c979c6e6a6202004503404dce46808b8d17928c87d820c71698797e16bbc0c31024210a54d2ed7e4ecf01097366550fac6a742082929e7bc9b1653f751d2468110cef9be34289c529ed2240529ca658aa2a886f31dc46ccd554fa2a0b6cdeb9b8eb17be4f77e7d13e4b9c718638cdb73ee6da16eef05d916b3f68480d7113375874b3a3847e396e4f8e79eb37235379b490d23256d155c2c8211c9aebed0c64a59b29a368330b31b7d346ff034db3675cc2ec60378a8815b0fb2f79a6c39a15a31d9dd9b5169a7d1af72706d82556357cfbb5cda46ab8b2d2d1b65ab862f64b26f0dd72648a37aacb0835352cb7a4f4dba740cbb65a35c44d75c1dc180399736f59f466a534b471364a603d8380d29c3bfa8591c649b29b749a325c83693a9bb509b9a72ac34d72f73420821ec9c28f1887853e34dc469020eaa48d2932b06e30932cb5937937afb7837384aeb1e99bac36df33cc4ec1b179d36e3a5f6f20e8ae840487e57ced8d6c61544f969409b287caa990fe2a0d6533230f9469ba828de7befbdf79e6b138d1f668e1498f9d47f0f4aa1839b9c9e4c2355d5fb53db7ec893565965fd95b6912e6cce3b4bd6d65bafd60f799e8624b71ba4da7008158f4a72f053e500c9558e113948a2728be5d6f56be52b4ebaa18f1ed2440769fd1eca01911cfac519e5c026674a886478aa444886b723d19b0232646cf4907d0804c242a1872ed2b6ecee2bb7212db4f2990e8d0e127df490b6fd9043973701a06f3487b26f590edd754cc9be272043ceb692c508a8c9192e613fd967b7f6879ce1fc431e3db465a1958fb46d745168e5217c5ab9c5a7fb506bdb08d390f20a065d52e85e2c0f34e0ab4f63b963516ac96a7674cfe10d14d2cfd1f7608bc379a2f0c7c3611e921e7d2bfe56e0ad211011aedf20ec0bc29a1db24221799315d6c75bdf100792be4120cdf2ed8240b23cbd9b95059fe02f4ab3cc4188fbe7c1d71678425cba6658945a20ec81370f07f80a21dc5a5c7e8340f23c8b86e1137cd7195a39fc316b0b3c2b9ca15a7009e34d07c502c325f007bc28843f20bcc9f59de55283afd849da65d16cf750ddf5f007adafbfa7faf97bdd59e9adf0c7acac19f8e3e1502fffb46be607fcf16ee8813f2204327300f863c563a1c28c54e4882dc93a31546e8c910a78a3146f134f3b635c222ee9188fc6a45abc349efda857b46889e17d7380d8c311264fbcb980353bb089f19cea8bc8d18d302c469d392a4df82803d6b6b824cfc7805bf0f62fb4c0f1ce451c4bb3c6085f1fe7c4b466c7ebf917794ef145e41971bc633832a123bcc525b96fd25680aa4cffe8e1f1693e23fe055216322a647ae78425647ae7e4059d8f9f691d108b2c1e2b8e4be052cbbb69f1ea0e5e198b76d27c38cc3f0c8b4b9adeda1a29e91abe9401c7805be0d88276488b07dd5b23dab8445c8234cf329fcd5f55bd342e154f6bd11e908733df9f5127cf4ffbd6e2676049038db4d15968a1b3689976bd1b1ba794fdcd45967345e5efd68020797995cdc5ffa4ca57b452bdbdcbd55379af5259e2e1e084fe64511c150fd3bcc8d7ba758aad4c95632ab8547348172e7e12a947d2cf1719fb46f32257cf46b8062e64f7222df440d84f15c5fe6e60cc732feff20659ae20922548c8c31bcd8b4ce3120f07ecfa8d4bdccb4be32e7937da03d22649715ce2dd305b5e0c531e6c20f387c5eab1533452c11ae610283bc4ef6a16b01b421677245ab363bef9a450e84a10be57a2335cb40429f8091f841042787f1df63cc178fbf5d03b03bdbc2ddf9c3de7bc77ce396997600b6c0163389401fe503bc487507bb8e472f9f99f869ea6b4e9e53c0b16ef7a9086af80354c1802652b98ca4864219cf8f9d440c3815cdaf3d1ee627a0f8b19347bac85ae69a05fcb2e83b6f21834ec2d3495b768a39734d167200badb220611696c7f2d72ccf8752bcd16c149feea317b78b66f964a17ace1595912804cab039fff77a76bccbef42732e146e121251268c9223c5daad73281e604ec6dbbbb08db4928e71a06bf7ee208867cd0ed849e0ad6632a15b01e19b3acf5a81cdb702c2d2e12cc10721bea0854fd7b143ab66471146e475f40946cb7a4666b78eb47085ed5b0bde229df6b3787b4754de1b43cdc2251697b77e1833035fa8b77c650421fcfb33d23918f66b6e34b3dc35cbdd5b361dd1c216f8533ff853cfd14a565a4ad8b61216da016cc0077f5b935fa1d993b4cdde67843acd4418845bc99b6d59f2bab4183e599f98b597f5acf58eb49279391d8a878e71d6133a31da4b473d8712a7231ec9f246e2cc017474a4901e6908fd2a27551158ab7ceffa7b9354244f2c5bde5bc3924d85544a54452e8dce78aa5bb74054a06f0e4054e01dbe4309ab24b6a9908ac8ad8c5c888d2a25ec3d36c2ee67fac9f71e762fe57b97aad9d1555749f25675bc544845e4e1d0879591bc55f3e293bc6ff84a6e3401f2ef5d15c9d51d7505b81bce00a34f2c823413f66c4d83b55fa5c4d3361a5c5279f5d93acd04155211e9aac85615c913bb351a7b33048dacc8579fd52b4b5bc804d4e9c6f29591ae2cd5576aad9c484d45eb273a8aea15912c3f6f8dbef56aa45def7b91b6d91cd284b4ce74316d44b70e8c813a4cb818c884930c64b3ec3d5b2951d14bd9179f5e5d7a93a894c0a813a8039970312ceee5f558202a255c4f7e154be286b3e15e2cce4591c11805381612884a8987c3ab947837c84b93c8920ae9184ad45a15b19aa79f689e4a09b862c9d5329ac808a262e7ede7314922d1375b4a1948c808d2315ebfbebf6761f13cab426bdde269bc65d2e2c9d66bad6a76c0ee9181d33288b40b41d82d5f5542240c89648bae4a46902cdf8de561b7ecee866d5d29af9441246ff73afad40d2d6b5e068e0c2179b34eb2433290783282e4fb383fe79c73d667ad39e78494661685035facf7ebea129746bf9f998dca0a8b9497f2dd2303e741b8c90872c163d7b42ceb160bbc915658244bfefe6c3a28fbebf5c3e42ffc8c086ccb8237d296512a78a3966d22b58c49d624e3699375994c5a329078375c19385a637fb771f7b449e2c098cc06be50a770360a8762969424997a66036332529635a62e0389ea9a3f970c24a8201956f4fd7e2a7ca2ef51109581c4bc9b252553cf88c018122923625141a8205bffe46ba745e1648c860af29325751948c84062ced93d3290901124639235d198642a632203099595d7028a95d6b9ae94d4d54e2dab9494bcb56fda47bd176f7d53f1e96d34a47be96d9beed14d6a1f1da35ffbe89b8b9daecc9a1db76dbac7ed20533b81ee95b381d86b0f6a1f0f879b75ab4cd0556cdfd09c51903de80dba32a2db37f7de1a2b7ff8747bb85719ddfe95b922bc6538d43e6464586d46a67d6058fbb878a32191ee9b58d38dd2ab5f46a2bf7e5def5646e64a12e9caf42693e3956f1feda36dda878c0c1999f6217a222dd4f84ad14b73bd6cbce9184042ddddddd7bb9fbd3b58b96f0d8bfa73f9e1748c4b8392d5a5f15a68f294365f5d0559f9de3b7536a7a4b2f729e57c854f9489a4ad9052ee40d1c05c6dab20fb5890564614ae6a76508ffa8d49502dee8d37d51683dc16f854c5f0d782aa28194ec52003de9a7a2adfb44cadc8148886187eabdb0ae6e1920c1f02c32fcdf0168aeae186faa3a86769f6d6a02a855b3e03de64c051bbec6ff3b26ed594ac2dd44bd433188c4626f18821da142f83b6e3f1686a6afd194883e6f218b4ab8576dfa29534cda24c31c61116e14d061b7a3c82fa46f36808f4be35a81cc40b43be40d93719489466198904430f7a078170a9fa0d18334486371948998df1888d06264f1767c33b756bdc787bb0f97e081853dd39d308b4fc6e336fe211f0d2a843ec8853e2114fc87b8594f93e66dde52aa594a77e11f292725275ebdc14de5e8ffc6aacdd57ad2f77e3beede4a5215bbb77afb5353967dfcdd977735edac9be88ee76d7cd35337cba6e415937c85ef3babdfa92b8047aa8bbffa49cb3d698f65624aab746ff24f1f6acdb83cdfd4f290a553956035d3259cdfdfaf794f6b2b65ea3a5a8592b7ef59a6defc3113bd1cb6f0ffb85e1d27c1ca0b698ebe9b4bf1eaf8b67bdabf5f2d2a852bb4b9b2295144c08092018901f9dd7f37eaccaf3793eef07c88aa672d18ba4f5a49cb0cb82625da1e77361a3d0c87a3f8de5bb2dbb621da34f4adea845a97551abada52b166cba425730fc7cac52e8f4f330a25f0c9b222c4fe1b14929b4505e20520b693064a8f6540e31aaf27cfe636f877f493a468e559fbc1b68eda1afc83b92e1dfad4fde0d6f5a442c4864d25715fe230263306dd333529f4023b503d6554a720678d6c3d4d20ebc9969a39b6c733d397a280d6427eb9415c3060f37188063d64ce9ac0703a3656060c8748c7691ea80cb91a2280f64f44fa6636c8fd240f6723ce0071a20740cd1316afb78373c71f3438e8ee1769eb0c9f00f88d23eaaea6cfb7838bc3a4d3f1b781635786193404d8e0a53ad7dd85779d4f72822310928a4834020f65edad3c39bc35bf1b9f28d6485b01c2bb07ba998877bc1b07b09e18db4b2a239f712bad3409fa1518f0e4ab4e9a0e22d7c338b86f0f662e891c2d6378b2dbcf50f864fa157975b4d089fe89cef0a8542a150844062ae3212c5d04f4aca3436fc417be00f08a47be08f10de5eee8137b407fe8040e08f50a827047f1cc6ace01c21bc91584e62c150843752de9efb057e6455893797ec72d059033be899f6b2abb01c707b324a29fbe6602f9f1da4617f3c6856b3671aa6c5eb85d46060902004027f0819fd20c7f7737b00f20364733d234a299da7724e8a52207452cf474a49f1c8e59a6953ab959a12a980523a4a0fedf96913dddecf4fa67564610f7d7396fbb13ecaa00e483da439f742659f075196c2a80a660cc3a7b655fcf3a1a732c35bcd8567bdf54d13a534406c2db97ea3a1a31cf1a952d63aa60d11895ef136caf4204df4a95d9fa005843e3abc342e4cdb443f03abb499364f3fd343449736af80d0479f6911fdbb1363da1090ed975a7f6da35c3fba34daa67a9166fba5fedd1c9e1d1901030cd386804e351a96d452df03e1ac5f1ea53f3e3f3e3f3ecfe7dd104f3579797725850f62d273a4c9112647961cd139927324c9ddb0539fd72716c99add7a08264f4a3f2db678a379ad206c3369557caaf066f10693e38575f476b18e8e54e353f55af143f26ea070bf4292044924d1963f6abeea67a359449695a440cca77a186d824f82240992244892204982e4c894c9d62279491e14991998f8903c242fc943f292bc230fc94bf2a03c240f098c8e1091068c155f807948421770a18a442230891692630c9263c411479ee2b40824ebe3dd28945d2b3492e6a0061f7650946519e28d94e5b10b637184c9d1bafba46e610a261e71a2df21ba0a6f35149f280a479c23da0461641829cd3212e987312a2cdf9ee737d30cde686056984867d11c7c71f0251e116f6ee21111271e116f22909b7844c4a1f18878134f6d381baa80104208e31152a89a50635f90144f6c0299f492a793e77124c9c9c9117da3312adee83117e3babcc49b8b92fde4ecb5ba670fc9679a732fd6859dabbf309a51a6f617b63e371d30527cb2d88a5d953825394a6ae428c95192a32447498e121c49725c8d51e48d1995e0488243494e141c4a727024c1a124278a6c2538e261e00bfc8dead0ba2ac20ce36cf0024c9691487fce5192a32447498e921c259713ea9c73e2d7a33675e9bc9b71dede9e5655dda8c701ebf5781c8070146d1663104f878db8e560783de0e340dcece1cbe3c0b3791c783d1e0738703064f00e421835ab2f165478237945763b4900922bb4541251080cd23833decd58cd6ac6e7aa5fd5e9651c1014bec0c32cc73c85d372ae3abcb98ab31c99c3341b6643b50dbbf23157f8647175e1e8aaaa92b29aedc38259d4649431de1ce2650448f914f18a30c47703b7673341ce3977a3b22bc2f0785c0d0ac105218496822dedbdf7de7befa9de61ed04bbd65ef06af6c22c76dfb63ea94fea131a5888dd7befbdf75214f5db1262f75eebba17bb1abb7dad29a97743cc5e5752d7b517bb77fdcbfa581feb637da61030e7124fdb7c7c11d4a4a88bfa65e994b0b26e597b416bd96aadc5ec312babf5f65ef6b2ce627b36bfb20c6fa16fd947bfeefafac88a30d636843709d46cd7efb13b78f8aab58f77835551bce56cf600abd931a968a5643152998512ed8ffdb13ed6a76647dbd89e1028c687429f695028b33fdd0383369806caa82c943dcb1eef2088647dac8ff5b13ed6e704a02c4627b146c962cc5e9d64f527d69efa2466598d82c550963d462c1ecb4211db9e1541f291cab0870ed2b0080a81b2faa43ea94fea93fae4f1b8c04e128cd891228a1d2998ec4471c4d23bd01f8661ef1e1805d24e9b75e92c0ca3260582bd9e450d5cb0aa5377864020d0bb07a8839c2e4c04021dc342d84313848380792b22bbdbc375ec339d810ec2df9ecd412211de540ec2dbb3f9263a0b049d056fcf26b4ad845656f0a682439b046a44d94377a1c710de4098c66618c3f5d42fedda0a8f3eebc8c6d1a321a8aa5855d5498d32e79cf371b2b0735e17765d57766cce836c068542197669168baa3e0135fd0294d04a28e79c76ce9a1d765a228258734e6bdeb2d64e6bed2dccfa56fdc2be55d9acd5efaf9b55d7bc339bf3baae83b4cd1ebbe66d8b69ac55b58f91a5554041044209218c22892c80ec76a09002217db96ad894c69a1d6d637b2a2be544dd39abaa623f5a287983b1a2b6aa9234aa43d1034592aaa2af2e35124956186ceb2cb08e36bbd65a0b6abc65a291e8d84778a37d11f61086b70bcf8a71d5b73e238b75b5232477ed896d6975126bed1192ad9bfbcae6b2163f9bcce60c04baac66ad59ad5955cbaa4fea93faa43e11323530a9fda9dad277da36f4b6875694da1ffb43e9dfa52c70e0cb3cad878f876d9c8dd4bd4decb37a09f1c8933ea451fa4ca3b272b6062e6417cfaae28d8604bb4786b73ed6c7fa18b93d7d80842047a022574c4a2877a0c0c903c86e07093fb42d859492cfc51d243cb1838422a1cf793b036342d8a52b08932e8adda569e8187da520cc664af1c5f6dbfd35318dc5b53a59787bf69b755b7db3f021c5a74f0a62ac6706aabb73ce15c082a0d7c41ea05f28e8c347d6c3e95832d9e9157aadd7b27ae5c2dfb656ac43d27b05426bc56d1515eb7d12e99bf5d78f84696c5e4172dda383c4dba36fb0be99ffc49bf5f92d9b58aba84c3aef8c17ed430b931a975864ac0842d86d59cf328b64cdcb8c4658fb28416c61163760fb802f598859acc024ad7dc8b0683266647c642bb05e65a4659acb165f18839eaddfb03fd0c5343667f8b68fc6d8b7cc62cc6618033dc340b854dd3800c523f3eddae08b297a23c3ede3d93c3ce78d8c0c8a3acb235900b6bda93dd08893396b4f6fd17a105af549ede91816ac9786151ba735fb7a21b4ac0b6b271676d5271676a9a8d49e6c852c782d4aaf9a1db246b1dab22c270f07ebb0febc8e81c1da93e5e7e7c4a717a2a1f4b22c5c1259af4e2c086b8f557bf2bcb5f0fa757b98202864f47a45e55ab657a0905619612173da9bfaa4713a480ba94faa936c594b9333b03e44b38bfe5df5c983f67dbdefb5d7de7beb931bb3cdbbb1b49fb82af2fb3d36639ed7a27155d0faa43e7937c8cf6b9fcc597b1a27084ee3d42746b29ba126f4568a501f5c0281e498d9be1552af7ff0f6585efd0181d81bab35a8342db78541e9d08c00000000003315000030100c074483d1681cc9caf60114800f8c9e4e70501889c3284821658c31c41842880c008008c0c86800002320dd315cc7825dd6b40531b785067ee4e6461e40e7f9f49e6bdd874fad613c3d8874c6a19122504b798406790800ac88155b16a2be21cf7000ee421192b0c0763a10c26df02a0b5261c0dc965db4ee032b2dbd20e963fed1665b2a79db3f0b3c492642fc27cf0ea9b01593dda04261cf617969e5d8a51644c14d52ad608154090e8a96906d9cfe371b87e3159923caea6fc1ae1edbca829c7a47cd0807762d3ac9bbb7492c77c451024660de94b6da4b69caca96594c2bcf77af86d6b59adfdb6f06d3f7fcc1c7ae2981cfe0140a51d7602cc8964da2a36370c5aef5bdce695062876f754edf09d8d5b5994400bab6bebab60606815d7af2c65c5c466bf2ab1ee1e56c6443d8ced2fc1ea4213d7d3c2464a93ca64c2bc174e09caf0e32cbb89253ceaeaeb5f2f72af75319d55a5d3b414c8f84e040bfa8fb3c222c1e79cd4a805ed338afe1455dbbf3b2bff2184ce58d0fbc5a090b1ce724a2cdfe8ac7381f21d1ce7bf94507fdd3b5ddff721006c834342fff982ab77c5d7c670359852d0142d2bb49b3355a16a4a86522c4e05e5fd5929ac045005f907d397cb2a0278da888552d74c483a5f700452a081be247a6f513c2caedbb575d47ec76c6c93f4bdd86d72761d45705457004389919739c3da873e24988d802173650353329870fd3f30e9c68c637602b6328cd892c5c2a97262209d81c57ddc9e8d4a95505064b0693266cc330b70a3671f7ab9b2b13438a506f70561b8088e6be711ce94cce90b36693fbb6f5b73d0f21a9348f2034e644518cfde1f0728f334564029e87214404d3be9b9f5622493882a3d769361b740a598ac5aea0b18effb99c614202b0231b3f67aeb0c9c57583d89b600197936f5ca11421d79c6c4e37c860c004a3669d25915972db411b28d17bbe9dca8a14d1636c90f71159ce00deae5f5d240f13f6c455d6ef754e68b03279603300807a3a6849be45c0b9d6a16e0478ac981507f91408a502eb87ab719e8fd4b77ea13e58016fb08ae51e7dd0462a5d1c54dd903bd866bc2411397695e1e1d66f33857d137d438c864363f602f6da3c22ad5086d8515c17e89c981f42ff5f2f2eb738fa1aecab5046ac3757c1b8f074f5daef0466a8a1cce682bc62a98c7eb235ea9c2ea5e78cbbe1b80d8743aa92f530f3d5494b0db6188d349bb3087ca2ca9a0fe54c9a110e1ddc2213ea26ce5d7ebfa802e30c9c2257a47e5b4b30c27080114915771c2dd7d87cddc60f82eced129f1d76b91c6c2f447961531a3aecd37d19c360c3035a1ba21f1f301cbf5c117e440b6856d67a272770f2ec1b540bac3bdf0accf99714f759931267b65747b51c1a3446456bd2b36575f0906ae8f1263ce13ef894e1c9e93a820f4f8a46bb6a68a1eaf9baa23bc2538e34947bfc924a4dc7b61c644d0227ca90752f54a2b755a0eb9a995e44acc531b579ac573456cec071508f5864e4df9b67a82edb0d048a2148d9932a86415dcababb651c5d67b0db3e88da56a57b151d45d1ecf9384ea0480df7781399da66e62dab321ae7e7c1bad3fc9c9fc774469bb78568580c685f9cce0307c20ab64cae57578598372674209acfefc66a58abf1160d5548d13630d80b3041198c91f60f56ee3a01bfe4692f788a358911232d24f479ceed24456ed5ad742e781be5a5b6405cddd5fd00360d2f5bea0c0df45f4360128a6f8b9651d98cfa698f655bce5a0f6524a518820bd1b3c7ba625724df2a71a8c88f55c4d17ed40abe746ac3ed4286e0656bbc5a29815d40b53eaa1086ee712060dc9de61462d03b0fc0f0219ab8a0d47e18bd9e4b470265dc82453126c2eaa75771818ff6eccd86e22a39a1db4dcc9c3743ca03ab330507aa65701037e7bea611ed15cc1d0a1a18de001bcae07ec8c77bff5214347db383ba443601f26eadf1da73fea3abe5afc539e96dfdfc395bcb539ec87aec9ab992f23a72e7f37572e6cf27ea8b90832c0a8d5d83947795ad1535ac3b65b28dec690749c1767f717c00273b916b1756064c0f897921d33ce5f4592e517221d5ec40fe3ef6d69bb1b9d9b9086ccfc0d10db93c60f4caa1e07516ead6f243507375bccc5610a9556f1d121a35015634ec7ae9733e4c32fbba9e639905bcfbffa7d8e6b5594cfff0e8aa62f911922d24f00e3acab46b694fa5bd7b0af51142fac74dc5108229c2c0d157e0592ece932a82af9785c19cb1fe2b32f15e457e1de8fc6e5f31669cbe3108a3685f18e656cb0b6f3049f28ea866b68fee6b528018d130e3f9d3f6ee139acbff735658fc3d9b3e7148419287851e6000a3d6db50da4e283e44d5e60187e3367c1d83184adfd86246ae8fa6a6cee285ac72a330f1ad44e0280774eec0a154d2a7da47ba97baca44f437325bc92958bd90fb35d624f724ef9ee729d86a373bbc3aea1cf357b7036562da1a8274b2772017c5d415091955b70b3ee551ce6522e665f6a3c7183d66fe60a780a4dad698b3277da15b7432328a46c0e8d64f5e3e4e9c27ca081b583fe57baf5a56656342974cda3e2c11cff28ccc1d864fd50323c675e47d8b45d293323dac544c156091aa97621cc20e1a99e64ef2859ccb00eb1adbdfc267a10cdd741b9dc30c2009e73c61b8b575baf2369fb2df13dbf99862d979c44615bd14fd1d54ba2857d75bd55b219bd20271b79d15cc205830af8dec5189eb9ebb5d6908fe316d9cb4b9fa67a081acae3a9a695e692adc3a25418cd7526ce21f3d4d55ec787de90c43be3623e2f510d0add55667088f59bd6f9901aa8345c9947d376a88c9e9c8a1654497b23ab0baa8f0ab86c77b0d47e5cbebc176363602f43c6d10a070fce4a3a214c0d49c333949794e5360c14b4d15fa570f332c34679cd049d4406b0b7003da052c32e5da3d3ecd388c7d6f7c5602ba880bfff7e858bc34034118ab5127563fb6b6dfaac9bdadcbef5eb19cded8f195eac61e9111de28da65f754dcd8841c1a7e66d3da5339f3f37c079754f004e8d8f9e53766f39cd7713c8402af4016163292f0d152b7441f11bb8544d313b988f2c230b2b49189bcedc2674416282dc7834b0bf3d529867d36c30e5b924e7b07774725842aa64c5dd423b2569639e3b15506c72796273f220e2465e84972ad8a8dbc33b862e56ae4cc0c5692073693d11a4d80707c2ca2e7e53ed32a8b78f5f290e5cc3563f7f2352981ffb509d247fcf8aadef8556667e5771aa56434576ccb87df2ce869dc2da89ca79b4a23978785d291df2e2b5f53ae89213623d5c6c7480a0f7643303cc1b71b38ab6a94197ea3a6ac155a9bb7129064050e791c0f1ba844b44973b2533803aaf63b410f31b180801ed6acdbc8ba233cb29cb39463d0cfd4b55b3a4035954aeb64a63e3e575f10210d22f1abd066aea0617c977203467ccfd445d19bccc151719daec5c3f507a76768342a4c608f0c1365320431e60a7b822dd212848391a3d5463799b30f3307dd38b8390dcfd374d7a788ad253d47422e8c80434910993e749e3bba02038cf032f69b85f324507ff9d53515d75209dbca29bb826ddc82e810a61fe0a93d2e863169e3bd168af37c0b9641025132f703b7ffcb4b437bc1c5f334d5081d3dafa8d901e8ef6d305d67e210f3e48742b1ff6a1bc7e1c12a4d769139cde5a9a7b615faa74b3f35db81100142cc93f46f8bcdba13481d7179dd74ec3324137ebd980066a6eda90f04776f7dae54fad0c810c24f43f1db01c34385c7b3db6f58bf0f75e36c0c7125040bbe971beb0cd2147c27daffdfcb1d004600e91dad71eb27f756629fd6bbe1f6473cee5311033ed6e8330cf6d00f14a14a1f679fc8815e7fcd5c84f25614d8fe606ae8d9c2a56c525a8cc7abe7b2b0170697275ef579b3405f5bcebc1ecbd6384452e9833067511880ce262db49ebd120ce8c49d23f0bfea7299cff1fbd48ce2028715690f9551ab6605e74da9e16416590b2d038a3b7deedd9eca8bd7726ae814c8f058f60be414719d47afbd9de82a879dc346eb9bfe3587c36a08849b82207212d1126f5fe8ead3a78c5c362f871720b9273ea235857060a7c72429ce6a96a70e5da6d35967cbe280ca3081f3aea96c66faaa68a1ce8eca34c0a0d7d294121747cbcdcb9ac79f3329dfe110c49f302fc7eeb0e6ea72f3127c01f47179e96f234d3ba4f3bc444d02f0e354d3afda3fca8505f2aa640e33bc039b696c2cc4b27d05e9950a80d7a403d722ad868a7a5bbf9aa1fc5ecacb4fd676a7cc96f74386d2a3e46616fffe6a8e5e910168676aab7cce84c9d45bd428e24909a3a5dee78b7b88803521883146fdd03333a0c53d911530634f8271c3f5d09b8ef887ec3ccb9285310521d8bf309bf73675bf726e86a32dcb9504b8a7ee18bfd359f6e2d8d3a9a54aea8acb4aee32ab52739717940b088a09c60203be16af0620df9d2a9dd9b927347dd28d1e124022e765804304d171588e2f3369b11c6e38af920a535a076063487e7c33f75f870f73a9dc76903c9a621f573f535c5229082d17b84f8e7a4487a0523713bbc72ef2ec150f695cac5e9183a2e851017f22af485a256aff86e64a1c71c2781a8e146d0290f42cf3350185f0dcfca19c299ad26da37dc9fbc16fc4416aa0ffa8e5e143490c7b34005ec8394148623c3cd0bd47421a1eade8b9c01accfced014821ba4707277392d4e723174b84c8bc1c8c1352df372e4a0eadf6a872945353146c0ccf9c1e7d65b51850e2c85284755a718500f5880affc19e9ce89011c999ed4bad624697abf7a96b491fa5b1b1e9a30213814cde5058c6f9f4f191e0601cbf7c774159aeab8abe0022f5cc93eb823e923035b119399fc0dd40e706636d12ccefa0d7f71338db6bb077a94a2b9ee6319e5496ad55f9f8963aedeead768d5c3ef246311f7f1173413df3f17063cdc7e5b7f9f879ff76095b1b0ea95b6ee95258135ff41122e62b86f451ac04813793d2b7833e2bb1b691d888cb39dc6df7a22c16e2af79436e45c36bde9833f7afbcbd849ba0d6fa1fd2a13a16f2f9b0e1e3795458847d4005eee465efb69b897fdb4be5355469f820bb50f4f3070c6573f096c42b5f9f51afda508dca6c47125c56c65d3aae311cc1711dece2fdf8e70d8c5ba2566c3150a9718dd50ccf0ca708996e44401da2ad6cba44e5855ba5c33f40d69e463620431fc60bdd8378e7e5bbace6ca6ad08721f40b335d54a66d6efc82df35bfb25c666c92d31dd38d430fe1da28b319893b4b4ea47a13168d9c20a05c5bf00e5b9e27715ee91038d843ee64e3ba5c6a051fc5ea3157ef7da0d9322af9f98f3430a7d55b864a6af0318c155799b6682296466cb61e3cb2e73cb3d034a71a4d8ef2c9f123b2004fc95a6382258e7f5787d25831860244c9d1f516437c51994599ec137f4807f5c7ba71e011e12a471e58f38632511a89baece6576d538933e068a9e2ed5553ba5f791e507af5d1a1a5007a96cb30fc532b037e05556e18c85df65ec3de82ca6029a3edc014656b856a83092caeea19beb933589295c4ae221a7f0573ce60d9b6b13f9d0abda850023b90281167688a9b2eb07719311c4cd9f364d5ac2cd4f5a757ef6c9c408316b9051429445578688cabeef1355575d7484b28e982b7ea745c644b44b4c79b3d29972dcedf09b00f72c7e5002c6f2dc5db070f4a7d5f02796720023b4b65a73a4c0405f4414caefc08570047e0996b104156160f7d5824d56495e76ca6cde85b9a672802ad9e72f4da22cd0999929e585321c8f12e563e97e9409300b7f2a77183751f295edcc48e0681730707ded21427e29bd79281200fc2289b5c9e41d4b2dad781b7a89ee4a1f9625d5cc8c014b5e3f0593217135f0e0c0ec46b52dfe99dd5322c0781169d32aea060b38a89d8ea3bf2c9013041eed741f7ac7565e31f6fbfe69e2e62e00e58e1a7044a4b4d7a6ebf27dbf3c6715f296bb3936a66e0dd6a7688ff8a200ec46c72640cc03ade5c4765407729901c68f57cdb10e7c863edb7c55f846cd8369862486e302e79417ae8c131ffe5d3cae5aeb1c6801c752efef38566cb6106ec5e5d2ad25b9eac90fa2d1a2980964939e029756280f4cabacee030938b443c6a0ff347f53b39280231a0c5c039002a8af41c047a4860fd8828ddf24471e24102d3f43950e0635c66a2df7065bbca204a68bb7f8ea9842b026414258cba73d4461540263943312957892859739cdbf7c9f6f871dc1752b2fed4ded184a4e105d336bb562341a25259903813c5479b24fbf32b01a5989e49ac6fb935be1e12de25c4dec237f882e140e0142b07a8b70821dd3608213a224e03604fe16945b8fa51ee02d2746f150d9fda38b9b7b67e71a10a9e02bbabd94f668b20d47c072017a0d841f4263b4bf69c9a0de75d3163860d74ec79072c6479316dd28b24f7562f67b5db34ed60abdb04a26610e67ff8ec9976019140410aa80307f7ca459bf1d99c59fc91c625dd9611afdd1acb5d2463790c375eb31e9404f2c4b0055f0132078dedc7d065c44dbd8b126b9deb12e2d0cea0ca6a55598802e1652dbeed91a3cca49380171e4bdde1961356c64ad6e12addd582e8c8d651fc5ea1e23713a4227366399e50960342e411db3f39ee775009323a6e9390afe70fd1a2f00c5169b934a710a15cf3e81d3178e814b2ba2f092b40315bc8066524f88242d3f5d9258f22df18cd470b21c287f369240a43e266f15e7710c661e030776453aec671c2e8a99259ad423461947267112aae0435b2943159db6fa6ef0332a711fc9dc44093d0313390294fd95156513e7075fa5c89d40504c7b97e334ddcd35520a445a0d99e9582a075e9a9785ee5ea5082ab1449b5b2a815bcac2ba224ea3428158f843a8d214b19ac4c2b8d8c735fa85b5619b5c48d252b2a92fef1bc360d0162f8f68b64a66153c4287f4a58b4624263487cf9f44d94cae7a7f25be4d250cfdae2a2beeea96591b04c8f56d964c4193db27072942c0a1f85d7e02f1e1b4f868a97970490563a133350b342f640a94b3216bbfd7a42ae01105edab5efc91dba4862cf053a7fb877aea07858d1f96203f06fe65e5d22e42a9449574fee62708d9b1b43cd737968f6893d6b1f2c8d705bac9aa93c046306463ebd4d70e8f6bcc79f3ba4f81514f382b2ee2738aa4ab568f3b774138e94d903b5fffceb68973fc1f31febf0cfc6f90449944162d308f61af4ca20d79532522b574b2d4e77229ccf0039f9b186b9a711df42babeceebdc1afb90fcbc47614441c1bbdca645cb9addbb9f5e0bb2155c55fe6b4cce3b5af9d6c86b3a636905b5584daf7b650eddf926346695ea281b933379a9d53ac308571fccd1d8ea70d2910fb948fbc86a19ef21d96931fa9041e6f6ee854aa478c2bbdf0d4dad6157b6a514a484833f5b3d9385d3b1a4d622a19e6e9fe27a995d2025e63d9a9a4595c2b4d051ccc3c345d051931d108e0cb87046ac8df402955d244bef540aeb1f404c01c61ab87991814421d8bbfb1a32ae75a37d3da4823ea726f3f5c019ffc0e229eae02a1a2f2c464f1b1e9792803ed7bd9342cd448e65d6bcc30b75bd0fb1c89aa9da1359277588d22be0b8b2d172d478d6c93f742b5a25b85a70adc7c601d2ccd02c155d0b6fde04e61fc0a5054158619e2d234f9baf5e64f7078656c7ad1fc3619092f7d7a5d388a4d46e42655e5bee26a0a04ff66c83b4c3e976d45947bcc65c93fa587faa57ae216209a751ce0953933d97aed36ea0d66aa9459dc92e828d2ad7cec9870986e910ec46b90834d2d79cfa719323f05a12173c5ea7712cffb6f732115fed4b247d832ab1c68ae79e80440436b47d3c44b30e9897b38c89befaf0bd058558113e70837c2c352118683ec084193a23a17af0decb57644b75f1d65a7c4eec4fcfcdcf6c9f480a1a75dc88b6f012fb4fc0191568b73ab23b85e9edae17c995c2cd3eb5c36dda42a3e10aee0ff890a0dab9c0723f464744c98853661e1b929abea45cf5041a8219a1aabf3572991f2ec83443493f9bc5e3f77b13c095db3fb0306b567b23e5436da9965e1fe3bb5b71825395c3b36273666843fb6bfbd831c5621290c054827013accde2d489f3091e33c4a14513d5080ce31086e7ea42d4b1859a38430e33e306094be179a9300455b6cd553854d555db5b47a5bd6670c7c51c2cf9549e55cbd9f990c3feba678637ac53198d5ae1c11b482bbed23c0339cc83993cec24e72358c9604b74c3021660c80880c8872f70903b942bbc2adb621fad65975cfa6a0417547cb48121c9303700b6f7ce9ceafa1e21d15c7cd2b5f91f176e6e2543eb1b276d47936270d51ee4ef35046597314e915abfebab64dfcae1c2c3491e43adff28ddb846c8b27f5d7a494ece162490c1f07d313e610596b48ff0156c38929f6aa349d6674e229212244c956857915d308fd5f0dedf4567090c79bc1de6b4db54013af6063694b465efd749e67473b17a09502ee34bc60f17c2682fa3b2ffac9b3ba6c44e15daf9841b4daaed6a1d1f3d78e4719c0d2889ec59c61e04b9b24e20443e84cc08588c14615f93cc9d25e681cafd3902e90608cdfdd0d2b871d0a9e6f0d2f008892310c0394772f022c26195f5e103665d28da9d420f04bed61facf0b894b95ba56f950dd7d52f967c8125c87f2d9b56f0b7a8a15f60adbf66f4da3d6eb1b7a93c41fa9696d8e92e70147ee5b8bdcd4761f86ecaca14b8281ff5d1f6e94eeb899b88ca65f6a980f4a1e363913aed56d4c38fc11e5e4efe4872f422becc8276b84996c656ef588892506a4d2e6e115ed24c019b4fc7cbc20bbd47247614fc4441f8e1c2c4408fc9186a4e96c1511882924750161e41948d94aa6cb85dc45920057f40c01547a983e0df6f3939b4a2e7624de8157d714007099ed48b37098e9685f7aecdcc41c028ad59f2a2d6f2e4bdab5f355b7596f75c35f75edf88d2eec67b7eafad121c6e0305bde3d5244ea9d7e431322d89087badf3e2d33031310438eb5def06fa3203c9441d2c4a7bd8bc75cbe01514ff6a76e2ad5d1266b7798a87e3b53dbbf0e61cb96625153f56b18d3618e49a6db7c326619689e276154ddf658e3bb6880168602a22cb9e02dafa15109402c274d08eed446666fa00f84304972f8d6125b102fb63221c2e346143581f233f939a437945b2709a20bd285f1b9f834968dfa8587f2fb9658020458f705772ec1370e7ef1b324fc5ca6f3e080bbee9c113b6df3328ad393c8b52b0657a06a40bd9732699ed7ab5ecfcb943722bbfd14b90d1e90579fa3942100ffa819604b9ef966a818bf722e581965e02a5d8aeaec628969c37609f28c60c853d33956e08fe420918772dedc6a7582d69aab940b432b2298a13f608f263b7f2d1e3d19911968fca2b3ca3835e90b73e1737c4dd6261e202b71c59b936b2814b3eb3a790e2f409e81be38fe2a2ad2a6328fa67a3b24fb82079200fa706de8c1432bf781632471ad9617000c15fb8f0e6b2b92382a6161c38ca9fd0f9a56812b3441f0ad151230c9ed6c949d0b8602dd744fd8285c91978d1c773640307fd8eca0f76739af5185cac173e3c1ee089b293b5dcf263a15f50ebd3a1ce6b6bab7d49e651e9e5401e3aaa7ab5fff61aa070ba4a37d959a65541e7def26309058b941eb80356b284d34fe2567f5278a2c491919bea11ab76b9587d9bae1b31dcf7b0373f6a9527dfac41a3bdc4a117e8dbab5b2d5910aab1c30a02dd11404a7677c8e7818ce183780f6e0ebe9b68dfdb6c382e91b90da7277289b06d4edeb8b4419996d72c5c358586cfe2cab0c98f735ef0d19f0a8c6946ff771000904be0d1d18b102e26243476e375dfcc31c335e5e8e39130e815a0b5a5704b2d638b00655b8123b6f7200cbd5c15d0b8bc776319933b94a8570d0ecd5b0e829d00c2a156572b73ab10c225d69abaeab7cf32588f4eb2790344ca9deb5d48528f1693a79c1c89d42c10a0e5b029672e61899103e8a04d6174c1e2e5bfb009a6ffdf43e43ef79c93c4cf84f48bdaef0039276ef62740222096fec01b12e9507ece24f0cdc6b68d65f81cc67abd0e61580fbb776bdfeb66e3bce071f9fe5c7e90dc91c3e5807aec159da634871dab3b6574c2ad68a8d1289ee28593401651d95129fe3489b7fedaca9bc4cc9108a39abbd62cae34356a59396bc7d75d7372f073c17f2b71f2a9a3c7170719521d26e0950768c66dd7790f7bb24e1be2c8c261602aa5f06eb6e06d4f6a6d9b0078dc0b911f8ec5fe09b4ffa32d9fa85b7d776f229e470d9fbebbeeb52df12427e7d629f6a5e6b975ee9a2fc44e86bdd87fd0206d640d8c472d7e5cd77e707120caa8c92e9e2fc24829a3472aae21e718e5931356345762ca97f93345dcca4dcc1ca338632d9843dcdffa8c1a6a8ac92dd8952a5eecbf92f8525956e1d499e021bb89745c7b8a205bc57e611c8c74be00d945bb2f6c6b7021e56df93dedb10510c5ab67c1b1f8bb83e0051bff599284fc0b03df1687f51631967f2f3227c1dc01f05eb882534448fae16eaf18b8600c3187ca2a4a3fbd012b65752d6b28057176c5e0fcc6ea2e1faf42b47af1df3542502c2ec1518b7f5ed9c7c627fc52e739f4fe3d761289170e0bb1f881921cf1abb76eaf176eec3b7884684dd2ffec12a88c13831f963e67e9815b69fc3da9330aae9ea660010753011033d2edf54278ef32dfadc516ef5c4ed40ed46f16f841477ea8355b2a35de395c7eaf9aad4a3eb4d7e79661536f94ca454d62ebaf822921a2348625aba098207b9501b0085e69107ae3c0b5b532e808600e6e7df73f6d7e99059c01f91c114403bd82f5a720078f0e0860b9b254f95876190ef1c97f46c7ff815ebf48cb7716908106973152d8ace20dc969c8851d7fd11202301cb699b055eca58f0146cac163d8fe6545602d46f88bec777c73330d3bbecbba4a8ab1595f9b621ee164df28ca558c52eab0e7a074749e90c7f08e5e0f52da2e763bfa1e77e0b6c1153166aeb548a98686164fcf93fd8cf0743617cfc3d7b3dc3cf5e72ddaf0568a1a12bd7eced57ec475ad03bc02ac7daed0015a535af849763755ca8f88443218b3a2a4e4a00ee59c64302e673d2ce2120ec3f3962a166db67bc47ff0e35e64c910cbd1ca4776babb5af7111dc327ba0612c308bd242db2f82e27a6322dd6b0ecdda27c45a0aaa597a6e2bce80492ba1ad2d44577db42ca9299f3564242de299e25ce5556a46aa3f322dcb6acc45290ea5dbc2bfcb906ce07f0ea27631191cbf580a553b7d3542193df6c926ebb869b0667dc91876f738140164a63ff9f1362d19a51333db50a86910dcb8be1a0247738e9e01635a2747fee811a502b4f790f561f8965b789e58d0aea957920d0ce8a5970c2a754e9a0c0326abac2fbfdf422933655c3ca1b38c3a405a7c1f67aadf2a8a3af44aac7124dd9529bf207fca928ac5ac00158d8cabf9039f26207e443d1815c8d77c7222114a16f3190004b6da26c387746a861df16f40326a34bd2017185598b98e1402953713c2516bb6fa869d702b6e8d5ec8454f2a971e853a6627a9c5e646c62a9183b8e02198e721b69d7d0708d34b06c53e1b19701dfe4e86ccd1e6b4665c0b202945044c8055ed741c824b9bb00641b746c0f41a18545df979608599e7f4b677cd8d5e413dfda58c96ebb6476a8124e93421ad5e2c94ccca6fe05bc96ca06f727d005327df181958f81ed91267f4b52aae659f0af396873bcd2037433da3726b49d86d3daf2bca4549a066c5692cf03f60d83bb120898caf521025222d1a03ce76a3f17d0d362450398e9f09a2c73d3c6b4fa1db3fb09c2f92250bf3f3c01c08de51ce2c071dcc57ffab77fc72e76f24ab330238c0d702c03efac236e4320af088c4f363ef5abedfd4da6d480371003320df4463a64324fca7bac870b67f5e4f3159c189586ee9954a8897287add7f3f1ccbae2217c4d7fecad60348a35eb7fde3e32d2875cb8eae11cc6cef0b054a067ae43c82e8ffdab24f91ab6388be90c2d1498360f54ce535e1bab14cb432a7cf610de52ef3dc8e5aa54b0640171611b04e4faeb68cbea3be3251827aaf2aca39f088d67424e7c8a50269bd0fafd4dbf283fffc5b394d226d3e3d2aa77b040e640b1dd2a9231302f2d866a30a3a42245ca2f9bf5618605f07f8d531c394e7fefac92f122895c5b0c7f518d58589866eb247aabe2990c3516a25da60810777a847234ad026e4d884960ab112fc994b394b47bae962503d647df99fb2fd4dc76fe72420592a9cc7bfd87f09f31e0462db0f6221587aa16c4ca369f21f63957e36bd45eb30dec0773633efd8b6480f82ec012afc06f90786bb9aeb1cd55d9143c2a1029bc2f29ec665ed8a6bd230dc9f99b4f35682103f3c86f04ba95084ed930973a4377b8c84700a05337209ee7a43fcb840bcd3277b120f2bd5b7de440187da1e7771ee0ff08927c404b8dcd50929b4998c4daa6d3bad81086bb6e691e7e9724597c381ace85ce4f9b205002f246bb98259635470a74e0f96179e96a41ae10567dd388fb230ceedc3813517654bc9221d7913672453ac2e550459b1cf148910dceab6b599aacab8159d11f7e28166a87829c615d6a8489950e8d1a1e8b0ac6efaa04a29e3dab713c1b52bd29e4b2dd01c063c3a750511705affa2662f6c3ce03178d23eeb2b2f0c632d10802a76019fb78ca1f2c8f9383ec3582271ac805ebab921c29ca50d4631054fa559e6e0473b072eb636d026c105340fff64f368e43be81e147d1fdd9ed65f76613e7327be5018424d731193e6dabcb092d775d3162f1f5c815d0ed53ccc9e9c410775f7bd07c51f8b52c501206b9e5f1215d79398708b13042ad5bbd62548cfee48c29743ffa6b2db4cee4080785c3c32a5851c8303155dc07aa505a44edeafdc4fd370be9a97a4350cc83a38a8df0a672e9e04e4461d6463ecfbfe0f112d884924c1e317a76514cc5a24762ac3c2b8c306a63c623d51eb00e8e06ff1d6f633b5e28cac64f7ce0342a5d35bc10985038c2889233e94ec1002014d9517bb1254e8e1542e244cb275c216060c8ede28c28634b0817aa8076c23c7f893e3f4b242911db03f8cb6556b8a2e321b6f4d3174c953e498871322d7951719ded639411bece7af91cca75e8145f844b4b7fc26339c449135421b55e9ba294e3da5e1ce0e10a1e6104c81a748f82bd6c7d70c40c6f01e33512ae6a3bf3c533f04364297144fa2911b932c433a2460d4125bf80d65c44f6d94631ff1acace05181161c43cd19cf8b22d0e09318d07c654f8cb7c7e23079347477121966af5ee904bb0ca112468e7217cbba3cfbb98dc8faa7fd8cc3781cc3b9fb26f835932bc8ef14d27d6431ea1b8cdae4050a15059bbf441e45b812c63185dd325212e55651cec2e9f26eff3fb4c5f3e86a10f7ebb8aa55da60fa96b47f9e75150fba85840a857549c6141743f3581cd9fdde5bbb273a23dd4e6420587f20e018817d1ae7292afa3e3754ce340eb08cff01f4329bd19da81048d17d1963642266500f6a7ecd3035e25d392834e7a1c3ce484e90315eeca4506d6a3069c253886309ac5bab835dc9fddf2ba29476db60c845e33ec892a405749af06b476ee559c182fb662e273e5925fc174ab254134abf06920d9a8e1f3dec246ed34b23396293f2c9c6bb6b30a5f5b5e08ea574655bd7756378e40a1778e3304ce124770fb28c23615f5296ebccc8474c272e652b9f0835b8269abcc87691c2efe6995380a113f5a378d7fad954dfdaf5ad802cbda31ddcc3dedd656d9f539df29cec10487aea09a932b49ed6a6139feb5f5db1ffc621896fff7ff4198ea6b829bd8ca7d1777d65c3ddc8e8e868e7a54383fbd72d268789c24fbaf8dce8365ccda204f009ec723cc2c6168a43e1c298be737eaac54f9d9355277f8b9fc8833ef631e034d889ce4071a6466745439e16da59e3375db935f8f4cf3391a6117b3ff310ab4496f5ebb50c8f46309cddbd3538bd1f44cd110443ae25b51290769a2fb1734a347d7dbb0d97258ab224c54292b23df879b3ffefba361a3c235b058b6477e5bc388376ff4339eea21bfd7d26a1096a326b44eb4fc42f621107551648b817e8358c70c20cc501918e6beafec8583acec02152bdcefb1c60526f40694a9bb6a265e01d84ec88c7b28301f153d2845cfcd7ed3cdaa1e5405095233d57ffe7b29bd5f0cc9175d8f00aba499ed023a354be33561532f513f159bf2f4866d6043ea86656ecd2b474f5b5d2bd47fc9081c2a27af3e4f4054a67f2acc7a30f9422c216eaa3f03f123e8cb8284772f01f1250df3faae54c9a3c13ce8d64aa2e168ab15d4b185ae161e0ed9fc7104e2cf63e9b59bccf9c72d0588b6d33bb615fb382a8277638382017d4b67c8f0041e0467f42b744b27322ad90a392897917e085af2937d48e3526b646b60510ad892658486b5b28f7770a02e868c813b5ed2081ff3040619c0a6469432c1c7c45ed9d4a19ca11e8045d4461938fb976eba497d9d62c12151833b790a99df73a9c28f2b7cf6222067b41eee085057b3e97e60fca702f9c022d4cd5e52049d2611ce4eb359c054494119bd86877250b3e9a90889ec924d45c9583ddb2a20704114b1cf791aaa9c3f05c4ee7b2067d5b54158b65095172a660c3511ce3537e8b80f0edaa4083db6f559dc2076072372a813529635b03837b78c74c2468552f8d4f2ffc650ea24663f9f4ada76422527404111b4a5a8084e7d37105b56c4ae02b5b75a6f99ed4aaf702a521e56bb5801183d8510af158e5238b01c38f5b7d0c69316d27594af076167b45641b4996c30a3efbffb260083d7f1788f9d4d1fabd547b43d2c67f94d0c70469d77b9556d20982192a12b152e8b750a614b94bbb04dd0fd4608ebd1329b854eeea46926df6a0dbb59409d10f66938f6a6e200e244a2ee16f4e17e694a107b3b486c2661e44dc21af2b5ff798dc14f953768b3738f02415cf8bae03485e0066f0f80ad10bf90fca6d4e035bdb655c447ac6072d9e8146a1f2c38a82a4ed3f8d704401b7ff41755bbd786f2e7d495495be663cd6828343de29bc985144b735b83cf2ebae40ee30dcb694ad0a3f3e327b6cfeb5a6cd24bf2ab268a1da5845e44efc794887ca3abed3e95b6efb55cd8254f36ff2ca10d6c867eee10ac73a389e91d8efd0d70cb8849b34fead239e208aa018bffd50e64849fa0562c16a87399b27dc2a14eb62f5c5ae33c6d9e3df39a39b1d309133fac3492439a6e03c0ace818c365ba9c74d34adb757440725934ba3100eac9d0dbcb62d79b26dfd0050eb6e6db289ded8833395f2ee664ec01b32a90c5fdf957c1b98386a400389d46059f14c3a9f4fd080a6d5d38d9f4db1e393c50b91ee345e9bf2c4a11c8d1abb06ad8f2dd24b7587f1ee9f744ddd9cba632ef2fc2dbe36380185699e3c81ac5d6faa403c8da910cdb9f4771948b1fba5f35ff39c7fa92dc49d94585287ede6945d96800c305c91f478a9cd49c758cfe8254238e4505c55b14d43be2131212ce9ef08d6c54e0952db38e0027b5667557efe99115494da11f396075ccf203b945592353ec5d9a070f7c8036958e7f978712cf1ba5863e162dcbc4f90a89939fac0f9e8134f7a1dca777c6c88d6da43a14cb3ac842bd5e5763db30ba395ecea09915837d42e983da6c29682ce3aca3056ce324a4bffd58210dca8ed53272d414445d95b1ea25743aea61f3f4a223683ee0e7eab27272157d315f619b3e891ba76ea068dd9b0dc811c9fe255fe6b4a92acf612908fee5c58a83b6e75a869d42287d8d0daaeb2d12f27de156dbeb8ff9222c67adad85391b310cd4aa1e7547791f58150fd698292183e315d821639ebc776b70824d1daa71828a5a40fa21d2129470b202e22d584aa1e2ed9abc82a7e0691b688c8dba4074f022dbbb743be97837f2eb98e6fede58774ad0b7db1c93001ee2acd19ff1e96dbf2ece26ea356984538e59baefc1c87e37cd55774621e8d1089ddf29e8c10ffbaad15aad66436348a1329031dacd4c662d3b24a63ca27fc94fd208c41d5f29a4410f3df7b6a44d73da2a67075221d2053ee020b3c20653baa543044d55837c7cb971b4d876690d299be92ccb69b3bfdb359858b34a691bf3a8d00572bc4ba9ebaaba8f316252f6cb4489dfbd62023165467434c08287453e39d60d904beea4fbe33883a83c505b4acd9f71b5a6285be52092b1dfe1306b7fb68e5e50c072e67b8949b03e0cac3315dbb01c402526550ccbedf293d7b8cf99fa84a560f5d8bb4840f6162c23fe967314f649233baf482624820884eb81ae2a5fd0b12a8144aa76a6aad238301365686cd1c4a33cc481d6f3f745b8a629c04fc2ab0b2b95d6339cb6503b4ee08e34955d933aa04a1097b9be276f4cd42fb8d044893565b4c2b46fb5283e9ecd5e4969069e2f2ed7c0e6952960e81a8d8b5042481e6ea7b2db069c16d90c6ed2fc588c43c3aaae01a555a0d41013f2fa8ed84744ab9441957bf093dc88eaa99af23eb28ecf377a57e810b7df212149bc4892ddb6df2b5bd16d48a003af9aca022ce94ab243080b9c94794302308a2658733914ffbbdac6c15703318f40d7fca77643a827d9077d6b14408613ca08ebeeac6da4644c7bc7124e1a32af1ce752d2e0c0d29196636e1c5137afca80abeabebd8a503bb4cfccb0f7b17b3a7a6b38fd2ce86f7207a822d53ab1efb451e2a8504deb55c126565eab34da8cf88f3d333d2f761f22d064c3d124d471deecebdfdddac1a47054d4c1baaeaa22a06ad247950524f54d8f8348090434cd9326e65764754040f52250afe18ce46807c8730047edab256256cbb175edb215ccd4957d1a62472939abfcd188a4445c556467dfeb92377d6637eac444bb358446dccd7550a76737cf9ca446a608213a31df649676fc2df0c66e2253366b836ef9986161bcaa9df0f20124d37102ea980be7d65c5af32b416bd335c09072d2a91fbb565367ce2ded5a4599e6ec05278835bfd9e226fcb571c7e0581dbb7c22c9c96238f44d21ecb517b8818ab0dad468b28ba7ecb7595f1c4e437c0f8b523a2714064f53aad66d017053ad8f3d60ada422cf297fda4780d865ccc85290eda18bb18ed35a2989a2283c8ec4876d78d3c25f6b8cd690ea1658d3fa5c0724cac8e81787f57144fc98935bd4e5695331555083a01a364722c8ccf70b5ae967af8a9abe63a65738676dd1bd8dd46b5449abde33d19d1b700e21a1cbd5030af14c7bda9f3579cb1d5ef73386a918c66ae9d10309dc61829c40159a9804f08c4080194b76b4643a89ce479ab0139e2836fba2a084cafaaa01bbd92c030ec0d21454f1cd260e033719005e4f6dc8f9c38ce586a7c92e734525d80fa04c5f7d616a000bc77f429d07a7d92e3ba316caa6c21632eded742ddb645976c8db368eef3f3543691d2a33c088e7b536e4f54e3e126372016384ceb271d8d6109a564b243c3056008394467a33e68a22f3cf54c7617a2385f769b2438259fbc6184c346b228f3c17d718b1476ea0e4b0354aca3e97b95b528865e2838b349a9954a34b8579f67b626a59a0004e980b8ee7a77be16102702b3f1266a7f4542c553df322e6e9eb645a4ace7fa9ea3a8bfbc3369b0437fd815c0acdd6241376321211e412c9b6e736ff74f5f4cbde09c76a5631d618526f2d6fe175133c621eea8c8da2282ca4ce6e36a3847214fa1216b25bf3c7b6c209f2e149a2934019c32b6a9c39b24020249ae440cba9424065b4ca3d77b56a82158b8f4bdb55bbbacf132707c44b8282aa90cdc6871d61f18ef0d99a1220ac2a3697028f9a3cff2c36bc5b017269affd6b2d8e5d6d04287a0c592ebad032b251fc5ce928eda9a1bed1cdb18d6a97281b0452dafb65e65374996a7289b2944dd36dbca93627fe61f6983e0a5a9e81393df6a11cc8c3e1153fcb81c09a02addf057cd6da0a82df58ae3b543250e93fad48ddb679aebac14fe86fb608535cea12f6768930cb8c7822e4fbfef334460f160a846885ec527325714dfcaf4cdec251edbc3fc7aea068b71016a84c65ceae81186f47b7946076089aa0bc378d36e64bbb44cfc4a492f0f0adcef5d71ba8024cf1833958666db38686ce921081d11a9fb863c69e47b8e0c39311ccb543388638b4785b65c09162d3e52fdd97bc252a1bb750c932da46a6a6fd47cea1e0426820a101f24b6741d86857826a632a605bd04c4914e4db646a32e4de4a33e672e9c0ef67941c215018606f6c1f1c2c8c8273055404982a10be36e8626a0226ab1aa6400b154080e176936d01f5576542893e74f02b71fe2702300fc4193192c302de6eb9d1c294e00c464d6a694885514af485e929cd2a03a4f1d71a6a3cc191d1c7fdacf9c97f9a772414ea2619a438abcf11f94b612d81171c6b87b53baa2427aca8a6bfb4e395f683d80af3b0d3a4889ed888a4c40f0e54096d93607878d799970f3af4743702b0a67b74dc00698f49272589dc78c2d5db9d1a801beae7918fdd4679f0044e963e02308d7b0c2372e8c063fe4fb4a27b92e97f4da0016bd885f26ca5344d3d38bf57414d132657f68ecefffc8c7f78c3b6f6fdcf1444d3cb0bc15d2d7ece359bee8a57b09da1899880ed5d21be37372d3ca60c317747d12b457a51399f8f4057f149bac2eab292bf5ef10e45eade451995e35d16333a558a2724742351f392dc693a25b327926d2120a97b202792f4b9400975ff6bc420d12441242ae7d3e69bf619f5a77473bc62aa4dbfd31f2143f09429326263c5f1cdeea488ab6ef5f708f4294c3649668fd245608bbec261eda32027f318fc89e1cb4d31d0c90f18a5bbc8256f5f299fde8c6fc04cc21d22eb4255565937154922ade4de6d60e05d157cd6a04f06e13884dc72ae1c77ae442484676cb132b118268bd1180943783cb9be3f9027f7b39dbfdbe9662c25c17f82a623066c7a1d44bb52a671e2b96be2c3437a3cfeeaa91845aa75d15a9f6a26e86dc2448f35ec18ea40f8fa83506b8d519c856526557ad2efe30f494b34c05140373a96987340f82fda514ca102ed714f0bdfc0ae102a11e576c9888bae46e6db6a2f65fae2d321162b4ab33907a4c7a243e0d09da28cbf96e9e639cb035df0af91338e291353f2ee8712e3cdb36c2497fa89b7100204f693dea1933774700de56e811dd74c5bd12b176f0ee6453a0123a447b2ef4072af1c8126d4336dbaa52747a0c01bc9971cd88b6f9198ac777e32024505c98e67a1e2792f8e6e4aea850a266b88f40392ae7ad562ebbe8dfc600ddb4bdb8ec2feb779f50709161c862705c62be179bdea43b29e8e25620b5206d0be5c7980c57d9c2fdd374ccd43a3f1192b7c4c967220ef0bc37d90bbd0a191c1bd8a1203cace86ab4c953e3180f615106f2b8dc2629e4d5e641d990ccc41f9282a665371302500751c89b25f84bf41d0597734b4666a4e200ddb4731c4a1c892e78ce2f6c0c8c74221313d90658cd5e2867943895818192a905a48c111442d6e7d2af49a7658320766da52131ffe8b6588d134a72710aa2387d3fce1b89e52012a2603f4e39438a8e695c896d331e8429bb1db7a1a3f8eacc874f45f30b0d98bd95b70ae6571b55c46da5f6e52e9b58abfb3ce788f92b7740ddef4d499425747a7f3cc7c92e55540eb83a3ac555353de4525a0433694af074950d7e76144c31ee8931d879dd4695ad1bd1322fa56057e6ab3df806ef1209f3bad9ae7c2b001be2062cf72e19dfc4c0bdb480ca578f36227e8000bf30ccc0f73ce488d2be7775855f11cc5fb73a40975abc85d02ee15cb276cd463b5f044e31ad63a4ac8d61e95215964edb819c2be383af298c0f3703756c4227e2e9351f9715d0efddafc72246b870faab63f937da53a0db0152e45a274c9e00d744746e5892a1bf95fa39ba4a92363c63098b9af808358ea6574c1df00206d2daf21721b096d6b81cac4cf51e3194255e2106e9bb67293b8891cc0ca38bc7113955b78e229f693679f3cd76a30626e2f7c7e298139338f985b641748e68115b0007067dc62ff7c059c0917e6d49c67262303ad9a95ba97a9bb1cbd3bfa8473a90b3dcb468cbc4acda24eeabcf9f37236ccaaf243c49888960131e59588d61dee0371ca56329b55a14ba666852045672ee99144393f1835738dcf3b7f3f1db4e5487e70bc301f152c75231b54e4f916ac8aa4fb02a30fcbb75af9d817cd465c306f0e743e20aa608fd70a69a0b268d3631757e61409977c4a6a02424ba11c2726fcdcc29ecc913837684011c69f64c060bc6da1c57814f602376a5bb0e75954d5a45f833867df84765c167bdeb7579c83ead7dddbb087144176738b5468271725d58f9db61a899162e8c6a0a322e33c3837868c820ed2499371bb8dea5c4de396904789ad45ccea020078de7f6a4e5e44ede28d6a874363c278f5a9761843e4f1e570c350aab054a91631733917e7eb72fe515e54a1717e33e5d5614f7df07838c78815b88e55ed2ccfbc0af18de4513b2c98cb3dbc49430e7756b02826b90de59e5b7d853e6e903b0f81b3d2c72c424de38169eba3122f547610d130642462b31b9856dc0236b6a0cc9716f633fd355ff6cad341b6a042bfca0d22336dad8583d4c15b44a8b73106c7755c3ee5756c154224b653324cefde35a1d4eade4da784be219dc806957bead4ec565fb6bdcec302ef9b50b3d9c9bc882bed9cdb2072ecc29c966e96346ea0babce7716916f6f4a1fdef05351a9c9d7a4a98033ffe2d8d20d9edecce428ba225dc815041640c2830212c9cb211279af0cb140206eee2c012eb8e68bcd37a3f99da7b9b041f2166cd8faebfa6e23bcdc5aca147163727e8df882e7e137d4cf428abef21a65a1f7170324f9e3a0d07cb88d22603ecad12d88745adecda90dcf0f467e33c6c61dbd208136ef0d5b84d544175deda20d2a1653ecc5d2d99a281e3dbff756be99a10eb43ed2a33bc739bd1f38e4359581ddcaa0495a7a7ce9b2b7715d399ba06ff840f7592bbac03c7d1799ced9605a14f922d7ec5f04516674ad6513631d9ab78a27d095515a3b34f1ffaca3029e181350a00fe67df9b075d18bb2a3e967be784e03194066999194f41adacd30d837b614644a62cd21a3940b6ec9fc44a3b5517ce52685175d8ac5404f0a22ac4bf08768e32ab09b9264197284c071ea5f2ed4d78e6ddab78f28881039452d12dcc87616023a38c657d2d2639e58b8fda8cc303b941acfe7e64e54154833cc6c3f6388aad82708bae3808ca9aeec8d7c5cb53801993f3240de982f2cd5d1a6989c70a5cf178aeae01f44ca850393e4d87ea7810bc8d18053ecd5e60d2d4f21e63a4aa45ae9ede4acbacdc301ad494dcb1eb6167b59c69d8e9caf4ab52c8e23330356f0250820e436b4346b3b09ef66b614f28192d23a030fa9a9f9e54e3f5767c7f8c7bfc61d94bba8383fbd81eff7cc955a3ded137b0ea2db07a21391631eef6156083a7b08f01a7101e2b217d57ff8b9170b69e40e942fa567a2bfb1caad20a7c72e39d183bfdcdb170099528a8e870c0fa46b14fc87152a6d2af50d297e9c998dd9daea3f2f2a500d0bfd2f60406a8e3a9c41fe566894e9b31c7d27a0a69510a6456fceedc29fe8e8758d20f66f15d68df7735eaf6103cb48c8de67b70b37ceedbb9765a33c47bebc9d0d48d89cb70389ca1bd09d42e6266bf257d6d031273828d9e6c75dcf1f2b76fc9f206a9363dc4c3cd281c04148013edd8f52fd1bb59ec06250d79bc9ae33638332214b6a7cc7eef623a22d23d23274683ce50672063bdfd197c6e0ba2bd8a316049a90a1780aa33502dd23b4dd49d053e7bf54e98ce0c3a0b680375bf04cb27280d31ac41206bda6e8ca9b4b03a623b75fa3b2c00b863bc058bb3b37fc6eb85f060517e7c4f6feed7dab42bcfb85ca7701b8c1dfbf6755374f01611189bd45075f16b816761552499bc1063fb6d5996860efbf1c3ab675d62456899d68f6e5ac0a462b1746c95a84d9fa8c3a5a27883b328cb2d277b2748cc67cb2ad1f87827bc068e94728822892cf021d2c94552970c7751f981b4283cdbbd7c8e6fe4afae02eacea12d0496e58ee7c813e9e4ec18d2f78d521b6f5897eda06f17947d277e05aa421fa86fe5e7599bc75e828e459d3a3ad2ac2b0eca4a7ca91983a414cf38281614fb9569a089f16b564a6566306a67b6ccced07075268e3d0dd97e56c27f62554d676a015a9d61c5d42a72fb8c47b6a46d21fa7aa9d2ffdd0817eec5fea9f67fc88184b6b25867df574a192cfa0a6eeda0cd0d2342c3791928a13b75930dbafd7f82d974478afac5f286e30a2be6da594921e507e94466db93b2098c97dbd8b8e9e3170f2eb410c53fcb75dd4d3ace9dc0cb16e5c8a9411fdaca2eda0d4e7bd7c2c84b8af24513aa2c9a0015ba20a346d7246a742ca4aeeb38ebbdf5bb514c672e4595d9110bcc3e0172ea3879ee1925b8a89ab8d65f1404dc7995292288b4be090498df704efff4ca1f97b0ab4e6ee6ec8269e3d5a3a2ccea84950fbf95946a733389e170a853dd516bd556a1824659c01e2030c26cd830ca09dff344d5a65682dcd8b69e1db4c9608c11766b2677fa80bc50971ae5c0ed0a0f0f0c2e6f2df8931edc01baf9737e5cd847705cfd71f050dcdf0c2a791505fc208e32cfdbefbb093ac4f0a735e40b9f42b015ad245646fec69ffe10386a79f4a91570fdd365d5fa7d7ae187675c998aa30ff4422f824d173690ca1e4a2bb8baf2d0ac27bf2a475676f344163321b95984ef95694f84a4322b705bbf040a38936d8eeb59f214eb85c0be83241f1506d614c1714abac46f18bacce6a6b77ba55e13923f3af462e21e876a62399421b12514a6a26332492219eb7793a2c7177e9429cbbf3491557b5ea7d2fb6f3b10944b81e73a5e6dfbf27461a956a5984ff2b63df694479aa738260237a51fb4edd137dd50bc8cec536303af43c80af51b7bb1b7afab675e2fdc6ed0fd1bc389c5782cc6188734f87da89a9a53d94d27707136d474dee702272c2448159248a7b4b9d90fdefcb2c76b96532b8236f0efc568ace3d68bc4999b0eb69f41417504d7550f9494994bd81fcbdf6fa43144268d69d86194fb07ed1c8a28ad07524721f078011a628b6a5bd62cfcda9bb4d481dd7fff92d2a71b3fe07bad7f4e39658de45f2ed708f7c9752a28f7f7ab7ee312769d58c64145f148dbe93dd05f5fc94fdef645ba784736fafee21c3c6a72bc358abbe5d7a18ff4468936930ae9b685b71bbe6ff3b93cd99e5c5920a5c5da876db32aed369f51ac746df7fa118f3db843802d849dc274c5cc87aeef26d7c1d8a146e714c7e01a08fd144457c56e33ab244ec52dbe293e4eb38e24e8b10a59ac8788248c37bd2351444f7152a43552602b4f45ce15bc28bd0ce67f496e58e19b4178caf562906dcaa7ebabed21f1c17d86c128fc8ab1d26bde812e911bba388c69d9086a54300c3f90c72c80ae73c31e3513ac69afd5a00bb37cbd29dd210eaf07b30795f289dd28fbac920f0f6ea05b837a73aa96a02badc2152651083bc32c009b44b2047dc1b4deac5829f108c016a9587b7f0be19c23d8b4e26495fe12b508e69a747f160f3f883a8402a9def8c7953c7e113346f7c413427b20352fd7487a06a7fe53bf89b6396223915e5819ab82a2767c092ba7633e353f10b518cb85defe5e4d14d3e8f8738ad4d27b2d70a7f39b4178c7ea542c4d31bf3913100ef2c7b95fcf22e43997e34e00a4203cb097a219d9ab5e318dc67e324805fb3877685b343a9060a2735f2addfc1e17e21509fde3023778b6842675cf75f8f09db2623ececfa30a16499cd5bd06d60549089a884bc371cb29122fbccc9b58f4aff9630d86414c4fc21edc8cdbcc8ab5f40a17a0f6b929f53d46213bf6898e638be9fbe18dd1f02fec813b6bbdfe81f5fbed787bdf1d078645031425e0fd8db9016e1daade9758b04f8e853698da1eb66f3696ecdc1f66d6d0b238950a10b9798be5b76139848cb5c5a6361f4a6c08c558efbbb0490a59e1d9e3b2e0604b3ba13fbd758ab0412f2f4a149be167ea9647d13365224166f7f5ce8b71535056ca7347b7c55bcc7501e233d7207920cca32ba539f76ce70a0b1049c2fddc5962331aaf723048a8538e38944307bee78d14e1c6057775ab445b9bc23c05f35af35abe47ec0a30cd032e4e05442c5d9c905f306164c41b628a3954efe22d522aac4287bd530040facc326234b5281a6061619a82d1d3f1bff4e964b1edc04baea513295f829336281b5b60f527171a1bb6b0199417d8c08ec5cacd18d1f023c06c1e2ce4730a6d41130bc2b58e9d563bf9c3023f5895f7893a54e275ec3377299e1e01e3c81089bcaff9eeaffd8e663ab137b86e78aa8a632bd7cb2fd0efac1324a330edbf2bc2248ef0c0986d926bc760409418494f21900a92461aa267231ee0cbbcc4fd95787b16d877c60dab338b3d774b5fb99f133ad09a7ab2d13ceafdb2e1a653bb846ee2b8c8a972a2ecebd63be5c319a87eff6469558d41654baf34c2bd5e3ffc4db69706e7beb1d49bee0e774e7ca3adb794feb620877edd6ceb65d6fb6f2da1694beed4645fdd506081c0ded287833ca1f39bf8de8531546a137ce367fbd2670a68a90eb9f78e7cf85c805fb26f8a860c283fe62d7807aa810ab579cc18192b77c598edb1e984ecc4e42097efa3949cbfc2e3775cceed48e2ef243424bd89fe41d2af98a6d4f11cad68503aafc436b0d4df6f6f4e257eef55eceddbfc2b592376900a2b47241b7e6ed95a64fccb0b3e469b0a7dcfff76ad51a790f2c8c074c620507882492f2be3d67a1a75659dd0f95d0b5ff1e7e96d50bc8f532fdca80e76cfacb8cc82e232e25e496b774527991ce4f069fdf00ba3dd5b0626ee6793d787234cfe22077d0726d11822e17f65f64b2047fec5e9a46d91900effaa7ab56c294c1b411b69c5577023121fb05e35de2a64870d610135fda474cb3d3dd704bc07db8ffcff1c23ed87f11d2474421a5b48b5f903cda5c06abc09a2c4cd27db94ff36d9b47d53de8279cd6afc178e07609190c8eb4e0d39090693c671ea940aaf873af96d08752883aa1449c86608f05a8d5434582d37dff0e110204b4a25680fbd095308f329011a7629d682b08947afc5860e144125031f3edfd88c6c4aa13fd4ff1747fd59e865d608ed0898d300d76f0fef230bfda38e4040194a0e9ea2211e10d06f20cfa603cb945620a0a16d02e5e6b0a80d48980b30b6e4da40a794632ea28537577ba662bf858741391c55a5c0850e0dca4203da6a629b1487a5d4c00afa65ae29636c9af121f67c7691370031b52dcff7c0344388e2dc35b04b5820ed7468c5636d57f4f82df8f98d3cfebf5fada642e0d5e722c3b655442249cc989224c25e8e9a7dc608bbf2fd6cdc69019165d29f6cb731897e26bda6ede995219bd8a2fead9cff3f021d5731e46c8b981a031d610a708a2c31e0ff5081b5900169d8cff44a24f8b04b0f1e317416bac87e86a349d0032eb0832b0c11e6bb2a272939422fe234414264fe685387ac26f81ef0c3da377a02a8ed2be9c9aea092302df8eb7a0c0b4d0e78f4d060c51e7309e4140b95c2e0c15a4b54125708b3c70e72aafcf8a9a6bd15d4f7285f7efc2cbc1fe4b5dbc900a2021f4bf82790325a37325d89f0f1faab52e1662617d919c1e0f226fbab30236426d391594b7c6f10d0b419803d25ec5f91178a4cf88a320e09f86c224455b9a298d13a71dc0cc26b76149e8f186881f2245b4852a8c1ffd1d7f039f435fc173d3dbe435f47f87058581067d0a502306f5fdef805b1dca3d4583655122d20108359b19465cf47220dd431b71b9320c532e0f826955196cd4c3801bf7b593e43ca617972cbe400959ff01e66754fa807947bed1ff2caba0cdd69a7b08d788d81ac6fec0592e8c530a3b8d78730924e3b187a70d17f8c8432440e87debfd9abca7c07907a887fb20e3298232a4d38c28dd439e15316621377cd19e241819cd40e96637c93ae850ca4c160f91b1350a8aef2fee0cf308093b8e1899b3820696086fe10a2e60921b241317bee457f10d33b8762a9190b225667bd29549b8e82b504e081aeab934eedd92adec628fb13a63bc2a183bec75cc31fd7748c7e993b0878364256412a6231e92fd5d0f2b98491a2086f6ecb2a10b12f94d519483a1987ad61b7e013410de4eadd88b2b2ae3162b5f8507a56937a0373c269844d75220a152f0368d189bce832ce893327271096c3839e56e306d05c3678f6a42ca863987f9d8b4657cc54e109d93f424f04a5f7bb331ccd30bcb6388b476b3b12d635a570d8aee1dfd9cb2affb4b081587a7c4a536f688e1716893e6fd448151ca1e6a0814a28751d0cefde9c407bd26b429837b591a4b8f107d56ba1764518d3fedb560135459920229ce67c6b9cd6ac946882fd717e33e94486536d89cdcf265fd489d8421e739132454226a89604980f567ed7261bbf4072bdbe337d653f90360394627d330f91d6d8c85fa661ebc561e9dfeacb87dd880aaa83a719511b1cf693112f324bdcb01eb05b454509dfbd0452d8be7ca7d68911f67eee420ae47f38b413612370c3cd9f34a133086be2af8c3d71c132418045208a4c585aa792ecd1fdc8abca364fae5afe2f59e3cd52cc018fe2a8e9f0c28609534e2aa19fe39a71865d333554188be5c2ea2cf0e1b2c2aadbb2c1fca212a6278feaf410624128b7dc157735cfb1c414eaec523ccd658f57a0f6def4b99f55b37f3d2719de242d94c143c40ba63548361de5ad51ed424bfb2f4386068e9d24462bd3aa1690990cb6732d6430a27b22f4485f22b20f9821c316ed0762dcf1c323eb6ddeb0eae1ece35bc5baa3391494842058b106f85ffa5e6dd96823757f53eacabb462488dcfbd5c46ab7c7dd9b180039677c0076698b04a4f375221d2b48869b80be7d0c06c643900200a793e66fb9a8e6a53c60b1ba8de94ddd4b8aa15ed1b3896eeee2bc930938f9f0f405421937208b4cf08c629deea0aff0fdadf4c85cfd2e154a4b783be7268cd9d9ca1631a9c7eda2b1512772b036eb05f2d9fd99274128ab3d87de7354fb92098109a0ee74a396213361fdcd9515124c0b553e708f16012e69d1a585f951fda80cef7391e0d99e5c9908c962711eea181ced428c4ee0b7cf6d7ce8b10486935bab3e60fb07e1cad0ad82ada22e5c878f83ce5efe1c00e26c31aea221e5a57f914dd3b57ff8dc809f55ca287a01060223ab93452f70e536bdb5671525179f70cbf400dd67154a2b4931abb070bf372dcfff90156cef77376ae2784d8e9f85dd330d1eecf0bbb279982a0bca3e573a80a982ee52e500d9182b962faf4703f37b9664fe00f4a332563fb87cf5d9ae648eff8388e988f3755e6ba46c2620d5e954a0abccf3a552a4d632a811478c3f5d34c0694d93c2289aabedfa4087da074fdabf650ad447eaedb28e3acea0cc97b062031aef0c0085644de6b782940bca47b08a82de970a40acb6e4d5a29eef9c10201409b9f9271b3ccc273ecc7f099a6f01201abd56aebd20f82c8ea9026fe34ec0a038e0b70e9d001a11623700d540cfd2147dfa4841a320ef2c6115f97b78a85c488e3098b398b43f52fa27074ed5c5393fa5ccae4b7df8ee218441c437bd2bea246961e11e9d13a74e0959fd5be658a48efdbf4b1cb5ffe1ce0b6d26d28fa8b5126a8a024c356378a901af2b11868646fad8ff02acc169e89d06cc0ad11d61a45633e38e6c743ca3ca4ec77331c1b82adbf529108020ef93a589cec1b554925ec6f0be2e6f92baaf5fcc46a0b0dbda7f5b1d78e3699296410a8323f09a81c2fc6a23a1c0487d8679f0cc81a9bec089177aeee46ed5a886ceafa651bc05493350bebf22da107c2d0451ed33ef6a570dc84fea69ce74a42225374e2d07ab94808d292173203a218455c2daa2d99be4e4954e01bba1251e0a6aa782c24783613657098e921ff135029fd246fd1b4ee511e5dcce589c30bddeda56f173c0ff5a8e6d259e5602ca578555840c9e68bfd5b17746577552b52c13abf69816345b10a2820e132c7019e9600990e0e8cd7895c5a3c8756e7be3a4b52ebc34d7190faddf388bf3e4eefc5c18804e94f8c342817a5fe4f3948621aa95dd213e809c1f82893d08b024740bdade47fec9a2a02974d1f430d5f96202d33b0b820cac42f492b34daa746e56c22bc847a8e480599487bace9076162bfc3d184f46071773c579071ae49cc943b31463ec90b4803e816194b3c2d345e785f088f2e4ce402970f2dc52fd33e28c9a3538af6ccd69726c7f5c063e39f9661f9d60b361f6ec60f00f59e624871fb3f7a1c3aef6cbc516270d538a4f2b7869cef6047a387e5cf0480579e23534301ff5214ed5002c3e787ec336b966bea08841c16705024aa275463411829f9a315a07e615542635ef8dad4160b5e2885a0e6e5abb9634d98d26cd84a0d9b9d3cbb123e1fb2bec854533a79946818a85aa0a36926e6efc8acdbc2f7d5042e1ba83c42baa6c4cb3aea2fc8991c42971c74a72b0c6f7500cbecea02389d5a55007e35a182af72aa476863be75a00b4c04f8ea110a521e82ca642e9b2d958a8105333eac92d740407a04043e58843aa13bb885c8624d3f665e59836da311e5b960c3b0ce6a67e629c3be4127d9673b0a36b89f222544ab6d50b67da34a4da189bbb98cf5fb22b837e6e7aa400f323a889712121f2b1a38b2f8781c9596fb47070e29c960da8ea999ab98553ddd1a468a6c6de6ffb6417fb3256f423a6bd9d9d43c0ead745207456e545047aca0b4a11c04f58a465f56c43a082e50085c2eda93e4cbeaf6aca99d91502061f5aee7c6199d88eb87704e19b2b4c0316deeee4e565f4261e341b882adfa8f00fff3e11d954036a854014e35e27f6e0471cb22e8076db20253d6c8d839080516858a181384a8cf025435c38ef961f9f444ce42cafde0a92d3fb43cde49f91e797a9fea8b9be7c03333cc1fba0dcc7c53c6ee8bc8c222973061bd0e5fbd5b66604d4bcde8223cf89daeda420a91dc576958ecf85632eb2b09e48b856d63104dcac7d04b0078d2e0352f7d44e4e153740f0fee11f429dbbbf2b043ee7589ecaac672b998eb4ccef975328557a078c57771e4c4bc041a8fdbf50b3fc36fd70ffc309a288f2415270c72e4ae8744734dd92089bb2df03831acd4463a30f9cbd298b3f26b4ada316c42583f5e0945e910304c11c5e5fe25ddc0689a989c30ae7062b438e79af7fb8b3ca1bfc5d93b392596ed2b4608237213400b2718e5c5b81832229248ce16805b9fc0746ad90b89c0e144776884a88600bfa2042a090a58fd7aee32785390eefb88840589bf008faf70db8a535b5e33a4582dbca119ea2281bda116f7c77d205d1e225615bfe0a2c0e1a900fb064befb3a2e37393474f9cd7552c433572ffc3c2727fb6752c2e2ac211a3a6f9895e5f3259f8522fab96c48e2e12c30509b1e684ada48ed40cc12acf374979981c63df9a2c998e0f2c6c12988707770702e716c0e233817fd0e25605778a8a53598660d926b1f70480dddad671000c4815102ed5df0068059f2c8a830d3655a8801ccc22b9690457504b57666ba608d1a6572f1ff1d5d49d8b9d76f94f8f4606d6cef7e8119640053cf322a421e26b4cb693266e93648051e8be52adc79b15c51a374cfca41db8468ba5821d114e915f09f24756144bd91807dc52fdd1c0138ff6464c3110eb937b1e13c60fc4eb4cbdad1f5ccb2544653d15ff4af89921ea0746d75fd3298f3df742e30732788a606b55e7ce276cdea542f030aa8e49b608e93de71ffe864808906787da745da7f247807d2bcef7c673a6980ab5d3d46ad9637d7ecaaae386594ab28e927d419680e242081a880e2170ad92646a6f3b1a06bec030b671fb6c11d1efa331d66198cc033083436187027b8f70a388fd744f217c6665349efc467de9a7e2a8f077febef795c4baf88470fb92f8e243e9fe5186f9931394fcc832aa458368820c730c16d30b4a4ce47aba38e66c2b52bef588c9e0aa28e9c921d1c417661632d9c3bed932a93730e4f5e5cdaf04577026ba5ec964cd03c66e8e2c057c3384adc05da94cc693d8761e60d27bb06d208c7c6b9663a2ffee2516b774fcc7a0f69143818703c12e10745ec16948074c22782fbb3b015f9cf0ef91576179150e500f79d89f69236547667608e4ce111356d02999439da57d171a285ac7c563ee52a3295113d2f93dbe56f33658d71540533a280764dc36487d2ee305bd923e0aeac916910b8b7af093fa6a761d74b55092c9e9a6d42de26f330e963a830c9d43c5ba881e260f1fcd7007e89826cbadb1ef0da2fc0e5efd7854ec2aaca35f91b7acf0538c4b55458f99c6f88f0fa4f317b2c003a18f3b949eec77a230aad8c0229b1804a01a26f017b689a6aca9e813daaa6d1756075b4b510703ac26e2f0c2d93535cdc52f8e72feaba8dde4c538cf7cb56d8ac363a217159d537414463671eaec0c10fee6af2b74a23cb7ad078fa98a1462b0bf8fb136ecee600c36afbe1740a4e35d70ec560adc6acd4ee5bde5c7cfb5ca3928de9b36d3d04bc42687c32b058f9a932248a310ac5a5833801410465e6fd9473338101c62adfede1342c81ae0557570b6c6a45707754104d178cff6ac551f147a907dc2dcf6b58b114692ae120835e134550d02bbbb68d5a5c4427b20b08b2bef55be100a185dea9b03da8d054ae24d217201a11fea3dd368d23fa9c206421919023c2d8fb2c3bf7ff48698f1c8eeb530b067bc65c61bd081d92e1c9afe247bbcd9d2e296207ab90c158c5d35b08f4f82058c0d046159b34c15e1a4178bce128810beb8d9812458a2d3c2ddf86ae7850a067a4fa8e45073f5387267c2c1fd4e39f484810b6001d3963954bdff2499a46b48442ee9fa1c8003aa79974a4741829a42b0273b1b58fe36d7591e89c1222d742755622fac88b8b81ce336486f9c8f717b40ba2718ac54dbd56005626a90cbb4218cbd2c00fe16eec0e31f4721085655fd4cb9f47e9e87863bec6601083cb06ff3ca4806b4b227903ed1a46dd23b5b83d122e7bd8acedf192c6ce28487962033bad442c9cdb51166bd7ea5dc9ebf9f7adc54cd5a30972f908de2a89f60676ff6136a89541ea14ba307cf7058326606f69907a540253aa1a30ff12a71a22546ca0266c1859f685ea77e84a57c0216a8b0eac7b7c68e094b1c80a0f9b87aea5c9ada889e2788c9d507eb9437bccfea8031fc9d5b6be17e78e51ac70665892b95eaff553eb4faa0368f57ad94cba86242891205defc670d937d9824619b3a7e498e62b4996682fbe87b1aaeaa1acb8ba5d665004a34a7e2febbe17dcd51e433a3cfbbb2b2007c4be98b8e57b3394cd379961af51c6c0131e94d4dfc2ac41c21bce225efc727687131923751272b8bb90a1a9890b2dd08591b13de361c9f33d3b4eeb00ad5491f7d3573acf84318874e54ba49fd04ea13fc2d2dd2af284431a97d1b526377bd02c7caddd9cabf54bcbf78688d2ebdcd1ae6d0d0bf1c23edd734dcda223ff335ba9d192624067bb47ed7b179658485b8788bbadc82c45014782e9e0d3d47dd4a1e13cbeaa0168b67d72560617adfca2fd201d6f44534c6dd77c901abd04162cf558fa0f5caa724e90c4bad67d94520347ac6bb0da033fe513ee572a6e9200f33b3f1951e26b241f18b07d441489e14be3a8abf438ff2abeff2d36aaba3c9e628c1261b4431ce02517f946fa6c50500c70aabff92bd2585f0b4f05dabd2f96e2eec7f4a332ae4f6f0e29f5fd3be84e62771609ad1d2bd68c2ff0935c0a56394eeb95505e8fb79655171f36b60b779635c5e9b40340630b94587cc372ae28a6c41f7a7b67c6a46bb0cdda3936ffd3a47991882f89ec9737561f1d54ed6c890a7e7662549fda29c3d5b14a05d9f4a140ed9c4d728a993147ea4ed1ce828e4e3be139d98a34a28f17eda482d59a52913d7c7a33b4d39943503ea51737218136477568f1b602ca6a9dd4873439f75c094c31d08ad3881ab220a000f8b2b622266604ca74fe609b3ea6d9859fd2e1623c6b28de02e7d54975419f622f08ca7724ace9da17c302252c3f65f4a50993eff4492a7fb0ec0cbecb25c9615c1373eb8fa17885ef3f11263a51677abb4d052e809f0d72b5e1fbb9f7e0e5cc21f04e696a78ce21969b330f3b2f9fd580cc9a04ede29dd0f4ab523b5fe7365400088dc8922f09592cd1cd5ae658f61d2fa706d69a81c81b9156072f9823a9ca1b3c5cba19c57b10fb13607e48280d36e933b0e083857f44fb0a4e47edb06cc9526e2a7e9bacdb2db976aa0b1206f9f39ba87457b4ccbfec5856204a1bcaff70979ad2a3aef6f17fa5ce1610cddc9f7036a57d10bad639a16071c00c61b42c0e979f4be057b2ee0f718fb5062f592d14d1b0506a024b318a753f88c99708a2055866cdaeacd025c74d2d58ccceba811b7273e7dc38677fa02e53472114ea95e4dc4226d013551995b7149f883f5760d913d3a837a0056d31e813dea8903ce4e8e1f82d282bcee09c89f46d0210b4bbc20a1dd00a24743b0edd5640f36b915ee03b8f311ae3b3f8078a5723221187e14f8a2c1e659e89f1d7b3369b2eb054ecc18c53e50a4b9fc5ac769936e5096f00bdfbff87d7b50712932070902183e8302146fe639ca84b29628e18812ffec5868725c3474babb789d52603d32d724a074ae6ac4d88ea87f63dec4b704a05d23402ff74603e32d191a73500951041f9197c74d21c862f82b6e8b6cb9b5b0c1c8d1b0faa5607634248d637505f73cda3f4488eae06b8fa8c67c883d6e10e4405cbbcfbd87cc44f8d1d7d061e61eeafdd73bb3344bf6832552b6c038f60c343282686021e3e176cfc93e460a9975de0965a723efab2fa5c641848df36061559d3c55f73cb0c7a45783c9fde4fe94185d867c911ab913a8d67d3812aa32acc6d9578323f949c64a9a361a36eb0559959705f65615c57fb87b18d02a832a981ec400942eaa659a8dfcf3532a85a829107c32d38e38c36c5452fbac0718bc2e46948668422b6adba125cbb8190a233214c4bf0e7f254f8e4e31511dabfdadace19d9c64346a52d6a49eed5032933b0de74021dc9eda2dd370289353b16657245f35aaf93d87ab0f2b46a64ad5ccc02e62015413c6d46b73e07501e5f61ab295f3ad4a5fd612860db7e31eeaef19edc66cdea8a19e8ae369ace3f01e928d4019a63474cd7dc93e1ab2de4cb7dab1178e4ab1b124f3be299477ad63b82d161bc9556ef145135219b5e736d2965e50724d97f3ca20c36f8e44e630115cef1a2e0d7efe05e4e464488697538d04530a4f8a878c70b4a5da497dd6ae8693cc999f457e00030bad4c1fff7b34ca9e7665f3915cbe6513c64f77d822c57281590b502f5eef9b12033a640d16f7d8f282b8b319ee66928f4c6965b534d7c276ce95a191e54621c7ca167aedb855ad2d0d3b262b1820feacb4f79e45cc1262a6a4ccfa067c087df3f90e56a6a0b9bf031beefb9399b0f3ab2cd597dca71860753250a4fe1fc8dde4b1c04cf767d7a2ec2c0e0829f58c3a85b0d3bb8c5212dcd9c0405543d0ca76ceb8d1509168db67ba1946e15f5ef19b498bb8868342b7cf4e26e4bee46a88b2a0cc4a6e349114888047c3815d110fb2454673612c7cf4bb7c98748579e05cb7b5cafa5171611cde1e464dcb74f620f11374f14cfaa31147a0aa2e82b172972ce70227cdbd596448c50ef77ef84ba698d1cd766945a80a252eafae37c7d1c4a51fc80ce8942e4d94f5f807e8c30ad5334e2ccf38380d20ce18870834e37753df601085fc01f5c6b15acde542a2c729cf19063fabfac6f3490bce3234bba5d9393cf0a2b0bee2f2d6d4a023728154d09b62cb8e18daead3eff64514ef048aecb8de361f23110cfd9500e4c7d1cb4ca1976b11c3a7fffe79c984a343d657a6a354006f085f7317d7f2f5ce7f8298409dffe188b9dc0b9509104d96dbd079171517f17269295a19e3b32e4b581c9d1a807b13d711df60f5642b0e496f44c4482730bfbfa8cae3211d7a907ac04b5f27572c1e08b8a05757f6321de5f39a2cd3f3a3aa17d0a03b0d85c69084c471655323e4e475ca1c6b96b601eb3d4e03aea86bd8425b9468ced4ade51c3c2cd8981f11deb4823f15eb74365573c21937a6c7a748c97c063c4cda8cf912b8aa074d8a8629ec004534d68784184872fe747179fbea71823d8a10c6f2f41907c09f6b8f1bed2f90de680a8f12c901b20af473b9c0150ceed18b9211dd9b772f3efaa9a0783d8c4af0a37715352f14fd528d1afef63d940ce84f968c8c4c63f2592273fff1f8aecdbfc8f1cb114b0781276eee4a76cc08059f3373a29e77f7743b1d4f13e66a217ececedf61e16693b26931a2970a0427e9ce1461c2d9c5b298d4a8ea53707891dfa3ab186445ed3e595b8a94e21b49d00480edf750140b076711131834d92d9388278d668699603b46eb5f17c623668e0c61380006e9566f6ec405985f314cfdbdca6e194341f6fc3c483e349242252381d717fe0387bf09847479d667eecde2d50ca2273d5eb59403b925c693c0c7bc80e5c9c394be41629386c61b680db0aaba7a3e8caf4ff25b312303ad3451664d9ba8be3eac89507f56a6fe004cc2cd6d50f61e1ae87e4181200216e6bee658e70a4e1dfa6d4812d43ddc9e6b6173924ac6d030390ba0d8c164f25b0d71f81eb257acbdfd2868a6bc48f2e9b552d523c94aa6fbe30a85e78159c112f579a2bb28fccd8d51ed414b678cf5fd9df9311d6128256eecd0024226a05e1b8fcf84f8e51b278421754ef88f2e7c9990c3d98026147c1d8a5164c1b9b0ae52f62192c3d75253025e9885d1ad2fcf43ff6a268d7926d4da9d3713346003d8c15055c1ac328e7b716bfd8c2cc082dfb400e2819691f6b8348a1646272e4da4aa8dd950899607faeef60f81991747a3aeb8048e61ee30098a96f731e8944841c03896b988b229f70514e3bb44fa23504afc2e79d394c9977d24512cfc07d527c6dc73b4942d6406c201d20b1932cab90fa31e9d8d679ae7b4184928ef0e456b598007a6828434b2cebd7edfa6b76ad355570363e87340fdf97391898d4fbf93f1acd110db8893b515e70bc86a051ec9e38c0ea5902193a4dd92897cedd51edbd34bd623ebe419242637b8eac9221577926b3ad02d3c17ec269991dc558fec89f5b5838eeb5997d7663b1047c28f08e0db999ad0df2483db3226341a0ae23a04d29434f938baa97bd9bdc08b082a17d9911fb431ee574cc921d34866665a51b41a763b4503f1e5546b8766c6aed405d99e26bb2985d0bd17857b824ab30d76f33e6db404302c52def2da7e3f5d0259f35ed9beeb058ef1f9232e7c37eba758f3991f61f7d890ac8b1207e18324e4369875912f5badb1800e4bb238b33e856acad2bb785e80e06d3850862e2c97a797ecae4a8b1208a5a7fceb821a117fbba2315a96e336b6233335919b36474e904251dfd9cb44a2382892e43af3ab0b6aaf90d88a7cc99019a529d4d5440d00cab0ee6a0eeaaba6ceefc6c5e51d58586b5f542ac250a7bbe84ba342c6bd5e551c42f3d6264422d3f00ad7d48253d3394fe0c18733f5950a96fde678085667db7e1db8ede96cd91eb4ed7df769272efbd77a404cd04d5044bc118a0e438a3384e23edd25344d5b47c182d21b5254fae9bb446aa6348e9bd39e79c0fe5f87cf40f1d79ca0a4fba39e779664b6c0d4857ca49cc1b9e547be2519465bc9987841e06b1321740f8f4947ee99996a992b1815b6ae7729e5d546178226c6f478dd719bb1ff96b72586e8ba73a2884672e4c4ca21d0b08168d0d8bfb84ea43584f44426ad0a459235b1a309249cdef99667b6e255578332a5d1830829e9915b7451aaab8bb36313e930775896e429145cbcb23aa8cfb707a616240705c323834f289f4e1a3eecb8b3afaa159191afb806dcd1846834392cd96cdd0923478a9b7b3f20145b2477594117a56edb8e0b0743db331382642233ee49ce6fc9d29facd39e7fc4ae9f3d18fc539e79c73ce39e79cf3990833a1386f64508ccaabaa2e58ca564ae8aeaaa8096864e62d3d994656b2b7116757126fea9a735641e4fbfe34c5ffff635139f7d1586bf4b34ccfba43c7a372287c092d288432092fc7d27a30e1dc3bf47f28cef17eacb8febed9dd79618361d21e5c17c6439363e92bcd29134268cd494583727acf1d9e698a9883cfeeb6fd3680dfbdf7ae116695335e09c4f45fbc6234782c2458212c2b1a1b167787d654efdea148bdbbf72d279916840f97aad19377eff46ccf36adc104d921b1371072405b24501a19eed0299588a843ca3024fd6178722839d56d8b76544696de292b249b2349d24a18b4cbbc418085304ca9604ebaa7053ea40fe943fa8e748e2fe80967e3143a5ef2d9c0df1cac406d03e50e1678ef1abebf96831d381d34a1be00b0bee850db6f7ff00275af31de570420ed7b4590ed7ec17db02c42d374b0bef74cb890b8e8f0de12b5fd063530cfb6048604c6868484dde1bd253032fc6c1648d8ddfe49c0c18b5f0ddb408edb77d1020bf8e50002df1abe900052a8255eba7e8314b67cb181805bae4071e1010eec0a3b0e8280038c57b635dc16ec39d87b6dc1ee17b7053bf8af7d5bb09f03fb820d9c6b090e302e4cd8c099db133670e2a2c3840d3461c2a66da0ecebf0de1336f0a781f79ec020c008e17cff807270031318166cda06d63db881098cdb820db47d768709db277151e17d45906d0a37c74de1a20508b825306698608db171f1c3d8f0d1af081716f3c50d648e0a718f7bdf587eed9f2ddb25c824f86c79a3849b828410e29f8fa6e3130ec8184f4311a7086984b1fdcef48b857599e319a3d6d65aac333ec13a8573c7d050d759adabf5966d27b7f1a0ce5ad4591b215d0bb14af0f0c6786bc081d879e6efed9c678cb7e59e31d7f74cc7f008b1cd2f3ac6060cbcde330c5fcc62f0083346acc4682e6fd9de105200c56005c0b66c0b4f10834798e51b6c70dcbc4618db33edec305597ff9afb318437fd18ef5cf240fef08a70036ec6d56ab3daacfeb01a73e05ad62bac8d39fc36e60073fb0d02ffebac0cffe2e111a93c22956fb564feafb7afd27ca24e559543210cff1375aa5a676bed7fd3596d2e3dd5d91a7bcff1f05dee0db8597dfa9f6eca4d8b3a5bcd01be028087c29b9e4fde1b063e8b70038e95f03156c2c758091f49842ca06d0e8c9531875fc61c7ccd9843bd1473003ae3a7980395e3ed3708f43d8d0114fbc1c50bb826a71873986b72de999fbcf7cc81da1461b7e19c98c306cc4984f011092be9461490b8322dc5d3e2e3ff7fd88d33de8e38e3254183641867bcf786d0c2e5feecff73695f73722421634cd28c3921246ec4bbf7a48fa01e6d08895259b0e928d7c90f2c704d89514cd22ceecc5dd2da5e4b938f5b895645760abcbb4a6c18dec74f52093ba5b161719b6c4945e064beee44c2863460b2ceafd6ff196806a1e9d641620625b97a3ad9a2beacacce8ea476d80eb05a90b56c19c88748d501664c789c82f448408c311591897838396dd56481a03a5276d31477d55121068286d5c343a70a262a86b743a7a3d2ccd919ba3f4c0a648ab2c8c176a0b3ed1f00e3d6a5247d41bb314271a3b45e4747c52b13ef9c73ce79c5cd39e7fc86e36e39e79c1b0597440a34cef37e2053ce51d8e52bcad163cf51f1c95112d967db8dc9bbf7a4b968bbfcfa91503c65b95f53cf235a5171ba56b3b42c191f281ea75c5c0c589536a1c1bee9240075c4af8e9f3121a91101885cb36c96604b4d9dbc0a5fb054f300c1e50505b4b25c5230b3942b8e1ba4da98d26ec7c34ef4a37b7f9b57efbd772daacf477fefdda616b1f48c7520d52c29698dc8ead1366c0745c21a2483034ee1dcc851a196923b6d91b4646d490a8aecc8381576f5de7b9695d8edbaf35b8c2a7de13971c22162c22ad3a95064810164455022bb0fd42fae18c834956fbe288ea4744ed12747af6c8e96e8953b22644e26fa1d7ec2f4ca8aaa9c4f8da76bc3d89abe4b4a68b45923eb3ea660f4e18889a5ccc7e69dbeb0782c91946d35e5bc46d0ba1002514e178a4d1777fb8225956c8cdececa5b57ef832c58dabcbbb8ead894632e6152ae146f0e810893cf1eda905a150bc61aceefbf0ba842a8289d864a452b005a890ab9aef19078e022f7c357f57f7f1ebdb2617055712c453ce70796301d649577ce39e7dc7773ce398f12f1f0aa3c5b4dd691b4675853b7414d154b5095ca5916b692955942723631622c5533625e579948376844f1eeaeaaad1eba5f6e92d255df54c2bf066ec7ac1fa72d11685a9268654acf0525854a057a44a4b1690de6ba00bdec36ccc3960e199c0232bd36470a8c848d40f239dea0dc0c81372d12b088a68fb0ab3430abd49a3db3a5e2f6e6d452051536d6834581b2d844bd570f38446c32321e255f5a253144b0e9b10acaed9f19c3c5789e9f476486b376ea4cc371618ee38f9ecd9e901709d75ba0ce46c4054af58ad1f865f4fe7fc9deffd3fb6d0d21cc22b09c5b70910d1c205ab5999d159d15a81be648d1f37835538785414a7dab3914c29aa8ceacff3950efbd77227604636f451617921f18e6dded79e578f70e65d593e576345ead42f0ce39e79c73ce39471e4f7c66ce39af939df7de7befbdf7deafaadd9e7b674e6458227dee8cd0e258a2d72397b8af956e99871a05f874b29d3bc6efe05090a35d6709284775888049fd69929e646ed17fca44cd2faa137b6bf158d22e8b96c79bdbf58bce0808d38b8c920687de9cf308a0619570974e8a3ade6a92dc8f197647c64b83cf352d14f09373cecba82837e7579e9bdb354cc2cb667c48ee93c1735c182ba40acb534d449ba662d7b503027412a097d0ddff6f81e8331983f5a6f4a1011b34250f761e504fabd7e172e2231862e2e4406cad106354ae536a5a6243acb1331821b9890a73ceb906e79c73b1ed3cc3503856e3b241bacc81407ba560dcb241c924b3a809119069eec172c4d88861b75eceb04c5462b8312114fe8b44676536111a85688b13335afbe1766990d5f559ba9d68b96290de8492b9d1a81b41c8499c737e876d7fd636b322155457b63b8444c6f534aa50054131bfec0901f1879d7727a5c6bc8d401aa54d6f4b96bd96a8dd1d46ec318f39b5dbd77283730360467c302202f57465a0c7adab84653a83974242c71e60376a055ab421742f685ed33722b8e1ce90f583c926ee8869b1989e41526e4619b56186a5993c8d7298c5a3ab2a6dadb097c3144effffed94dcae29bca299ef8766efbd77ce39df4e4f38e79c732eddaba72affff764339e3fd31bb891050a8f342a30f3ea0f335f3ac42a581a06064eb0828872c2e19f598d19bfecf5f87805e181a78b221c03cc4f2628e9cd79e6a728407085bb3c123baca350808c7c72be67d23931efd187111b180a54d80e5c179f7ff3f880b781d88025d4f9cf1de74485af8ec996a7951431982404982534229b902ead2d00b783903386145cbe828ed58a7c23142e32343d366dceeb8dd5d3892f6aca3efffffffff3f642a442be46a412134819be820824bbfffdfa350caa993319835ece3979cb586b23306493e54e987ed13f3b556af944e65d3b1e99cd3819a8c43fda599cc604a1ce0b5aeb6cac8e3f0972009876ac9c3889c470a3ca6a6cbab882397bc1437bb0d0e10e1baee89589911da409273f960a3563de29560e86ad821ebbfe67374e0a821f390a8ba46355913ac1768b7c4a78e2a73b4d7aac8f23093a6a331a42ced8ca3e7e891a723857213479bd0743fc0b4e01493ac825dd976f35ea89282d45688bf1253e1043d972e90a21da327784ef6e286a158807d9ac6a0ce3bd0e5b3f4563613f365c4b93a1699d5e5dc8ce08a90eb6068e29b5e4eea43644a05c9c83157b0ce8626494e714f0e740a95babdf7dead82422319aed96c5abac02d8d2f5870dd3b2bb1a32d5eddd387883218b9b5be28cd714d95673c2541a198fa036777516745353426382ec42321441454d2404a99116bb169199a81c28a256d8965c140f178d6b8521626e426debb8253ffdfd48dffff1d5bdec1e5dce25efaa198a4ccdc8fd2acd039fa8e621e6e521c82079af949bc73ce39e79c73ced99a6d756d7b37e7642fd5f6e6f6cc39fc9239398f6548a816cb0625b7983db538981914b6fa2d76e3ca196f0820223233e3d517b2bf29a98189c100254752dcdab88aef58f14b2669e498656b5b4255700bfafaf0425744d2d5f61611678ae708f40eb4d6aac83eb1bd5ae50bee9e9aac687bdbae19aa1d514101a70f90b493a79a28cc26233b3e3991a49c6f32acf7e29ca3d1d9370b6b730aed2cd649796bdb7be769519b8abbc38248d2a1368984e6cc921aff2ff7ab759cb60b5d285fd29232a11ab3107227d038eb94480bccfaa59938e597aa9c1394292c8ac4582893f0c250eb7a0ad37db1fe605e8baf195882ef5f6e4747c70885f0eebdf7fea1e41f4dfee1fca05a7c54ed0fab860f6c9cf1f2126e09c43efaffffd538987749b33c3427534aaae7a54c85ed0664eb74c24651a9a29f105e781fa28c734166c6a3a32c5ad31bed3e753c7ddcbd375530f200e8a412000a47f5e89a60af23bce66c8cd7302e5cdd9a0767ff518709bbb0f7de9fe49ef284291e68261c2cd4edbdf704c4add81d7598786adcba5d4f0177e7e98a2d41894a6744b34e8565be919373ce3d9c3e0faacfa3ca390f2e85640d1aeadd1c0e5bb1ec2b6e59f353f3e1e6dc4a521249723f66d81d19afe7ffffc8928f2c8d3c092d5052eaff693c66fdb96a65c380a127298b0b6578f5597aa170dd30d93ed03c10c53d1dacfbfb3951eb64028842ac9c2c242b5b6230642f67395237314f2c83e1114b5d93cc4f8f07271fb84c20c3a25b7764841df859d5785a85cfb93de350d9b24e3dcee9f0ab98e46a728282c8c8317910eb70a32da778ce1facc9de7b171b8ee1d5c0ac5f5a09e79ccf117d7345611ae79cd3551fbb287766c0a64aa5a7363d1baa1b242e3325979e988e8998960a71ce4cc734e52505a66aefbd5b5928516a94341f5aac93c40af3ffffe36139de56587f4dafdb2bb7fe5d8881089e1196ce4b69b4a0891a119d123bb4e6ee2a70dee5127ca5566c6c8d9c2e3a7875341b5694ae3c7a44ba5a735719f1cd06eaaf592b75c41c285a2428b565caca14170236b54cd24e75f3f2e46ce6b68e362a77ba3ad1215b2311097b8a73baaae0d444c535f15aefbddf78275a486d06a8aa6712dfb0a22c668b84a42af164855d696ef0b622ac4be89e151d0d5454f9ffff5675f26906faff2772eea333b8cc4f2dce78a61894677ac6097b20c53a923c011aa17af9d24e7141efd88a4561f12811cf505814abafcd43feffffffe14ee05ce06a2c98b353e8075c8e5968aaf23ac939e7db0fe69c50544f669a0e93e9e65c0a4d2e0f99a4f613cb6470f9c00396912724232c1da9298e8d54ca6a044c083ba48cb8286b3b9273527c43318db794f6cbadce53b90eb98b38b920c2498d673a0e0b0ba34f3385055c950612f2971c98e3448964c48810562c42dcac961fa68a89db0cb5cf54598863667aa62b1bf56eef2e1714b65cedb169460bc70360d62f27f2e844bbabd1001363ffffa1f3ff3fe1ebccfd3f9358fb053c998273ceebb8bebbae2bfc71ce834b1be1537d70aaebb73ade1e6271be73de77d418e5ff1feb94335eac84fc1fd3b33618e217e09c972176c9ce2d5c18678c7586d87bef6ac7defbecb4f6ffff27b36a9061855820d98a6cfbffffff758570da7beffd58bcbb3d935c7c927cc585f3e6f7ff8f82cbfeeff4c109342f5611361f163b181421779c9a34584b744119718b14a5eb4b5a101ae9bcdd5fe14b6eea4295d7b94e122f404089d393b5a5e256153455f22ce57062983a42b0b1002770b41a99a2dd37b8978d46cde9415d1615cdfd6c58e9bdf7bed78c335e27b56ac5de7b5bfaffff834bb5b800cea1350b7b57a7e5f8580181b323968440dd48ef27bec317e18cb541eae83ceab1d8803065d80afad11b8144668c8a3cc52ad43804d5a711a22ae49c73ce69e6097cec5610b4f05a1b55455bb60208a556702ad649d8ea846567ce09b52582a48ccd9688286f9bb204df189d31756b4d3d403f373a77bff990213d5a6942facae2c528177c751a0240ad8116794a58a44898b2d680f05142ba898298a5c5275378c39a968b4c84196700eb8e47b3a52936149b28c93413c30c9ea0cdb099ee16119c0b54d3c8d48f925d4d8692a79ae5681776f5d0cb620e2dbf51c51a00ff1363a36a6929027dd08a8b7252316bab9982ea6563126a07325da0db6694204a341346b336e60102631eb645555b3c6b77630618be53c3873f5981810384954cf428a9b3cd9e4089a677e503133542a792211e217670a4fc4ccfdc3aee903fc42d4de4b2d16849a9c019db62912c9461d949138f0c6b8e08aec8a346a48e2eab839014a884ec592d396580520b000003190000022010c4912c1014b9e40114000730b8bc98884c34140c46027128100002006040101000008080400010080402e12090d7da780090f10bc9548a63c5a811a94f5301f5d5049dda0aa0622c6839e4efdae35ce66234959ed5e66df6f878bcd7222337477272c53f68383922e926daae85f4489305dc8d5890e35b6af1ed820aeb02d7f85f6505f03908bb29705d3f9761a8a9dd65be92e3278a1ef166cc6006bcb3becaa9dda2d16d6b6c39a28722290ed794ffeaab35a1a8d25c4f2b5d1a4ac20a500642d0e36e416d42fdc75b2fe1b7451a1fe65f0093fb9376f07bc440f643d6a5420afa04661c36720bd97ff876e4457d19f6bdca068b958a3a5c3b1f8b410dfdc385dfe23e444577e0a5ce7d87d5ce7f61c51d0d78dff69df06f089add823de2b8ea180423867dca9c091e86d747c9493bae587dff74b3bb72a54efe484d30e8f79f3767f44c327b3037553f7ed39f3453de93d0da33043f4033feae8140dc1261d6784b3ff5e567600ed0929f58c3eba994dc06c108748c478066c109eb8f3daf3ccaf14aa2829b0b2a8f806e76e124c6bb5ff2bd52edb25818d1a267795c9b07ea832745ac76c832cb4d5a6c9171199216780dccd65b222bf2b3d6e9488b72af6a1be70b8d2c6b5e0891d1a98dc19bff98a0a8eda848774872e6806bff7ea37900665f6832cbd14c0ecf0a49adfc17391e3dc0b38e900eb01a0d89b135fce8de6f313aa8f6188abf2ae1e872b0a59b2fd1d16a5980f32eda3595bad4df09612ef3f4f0985bf2b46940fc41765d52ac1b0da0e606218efb7d1f68e4683bf0b290883b9992d33695e9d2245b18e1d9690c439b2ecb82f2bb42f65d52cde1d88161ddf48051a21103bab9805582b624b5c250d90e879f3721449aa651426b295cd9936226aded346b2bca62448e310d86c54814696aa76b4f3f4e2b203f66a293d4546161edac68d0388641a5329d0be0f497af127a8f954ae24c96d7cc71f978e3ca2bb5d6dce26ab7dcf26ce6ce3d9138cfe407a15de1b867d5a56a01f05350fdf55b55ab3d0bdd156eba01630cab87a60fff607e4b0820f5135c353cebc3dc3749c1985b4f173a2e55b9de7cc1d40a09a144a4b35552bb5ed1bd4b09937b2c87c8fe8925662318dc2c1d44bb3889a0cb497eed7dfd053407b2fe53e801c03feb66a0c0a203d133faa661fd538407ccd57c15f259de106cbc15aa477e75c19e3b1520a098586cc4c3a072106aa0f637f496c75741bf613d471aca4c988520a45504b8a3277df0f9859716bf8aa05fc51abe00785b73f347e72850f74ccfa36b1a2452dfa643e0a11d3369e6a477999b0036d1e8accb3fc9e3cc282bcebd0a0a8d846d8d3cce8951e0bef747b32482cabfb3bced7f6284c0f3e83328ebfad7cf0f8ab82d02b7b61fff1a13f0c4b7ccbdb15272158c5dc16d30bbdfb33c74dcd0798437e7cb6723ce0e610b34068868e31088c037006044047bdb499e2f9311b3bd79d2039849e7303440c47ecd98b5606929b4db02e89f90d98186168b251996a5b9594841b0bee7a98a9049c8a151329b4fb023beac7c8c59f6543f298d4ddb63a34f02219b4d41eac82dd8920e5f6e8de87236dd8f7d442073ca0500bf19e4407d3c0d79cb8525f91dd05105f71ad1106a8055e13112aa2244c3fcac214588a69f00fba0873e7cf688b4c0c809bc5d34ec719d44a4a5d7f1f936a3901fe018c3a16db634f518c82623cdf3fc17854ba6f55c8a732b6461ee5bfbf8e4ef0dc0945271a7bdb4b5edde7f2cf2d9d68cbf8d3e5fc1d181a39448f1be14c723a038a8c3d7f2f33fbfcb5588fb7c469640ad9704174f5accfebe837607dc7b9efe3d3e55e9f125bbe312fdce079afdec0bc7a2560f7cb2704d91b22cb77b2636a7f1a5d64034fbd4cbe1883f606e64c1deb3a9872e4225f2cb1e71407d66c921f52b1113ceb49ba45c7132e4a3d40f46acad5415933aef75d0e14ea0ce4591c6b4c74f5ec07d4df46b7772203793a9893d4ec69e1da42d2d1ac28eca4b224ab47108182ca2e33a213a4bd0373cdd79049017976bb7ccde789581b460fcda3df4aa4e8c11dda40f552fbde8b5fa0ddd89b0bc54dfa52702cc3d9d6c0ddb454cf60ddfe53092ad6d8e3e2e71b29fbaae86be0d37409832ad3e2e32a491cd4616bdadd44ba1dc2c6a9f24282af4bde4f09b09c3450dfc099c81f822266303aad0f1255ae2f8c4650bb96088b10a78f468349e121a2c226f8d385ab95805c92af3a2ebb63de5db9d7e79ee4746ea9e986d3be539631aee44f00ac1fd8d22fbe4e77d90fd62c2df6550ada09df47b88852960be4dddaddc99496263a878eb69a30dbed0ff34b547ab9073c705937bc24d42809da8dc4f7bd151410915acf9ce360d6bd082c14726801cca4b1aef2455b4bb39b49fd5eafd33b16e6125bede58fb2d2802389231aaa71e947584252548297997899d917d58d18721b89a861b73000ebd5363a1a45c5a177f5548e9ca14d095839b9a4424704b6b4cc185d98dda3a84173266b92ac3ea1b31cf077fce495daa747146ad2e2b80d243a194a502b646fe228603fdba9a04e75dbf2632510844633e30622ed45f1f0da58714119371d37c316760dbcbd6d43c4e2ccfee5b89da86926d4f6c7ba46e77d1c3861ce6ccaa34478dd3213894937b3ff06444a6075be4dd0beafa62ddc27af7925f1a633d3bc9433bf0f56604da6476c659fb209af907412d0f52db07219b2d96d60c98366f217180b70aa0a966d1b87fda148d875ffdc9f2b47d3bbc3d0bfc08eda1e04fbde39c2c093e5a19d4cc5b84342a421035f3e305c83e328ab593f72fad2f73600c2d9df4ef04a72825474ad3d817edfa275c44a81038124e88ffc52c186bd787460414c2309fd5619d55354ebf85bb2ae89a890ff6b77b9eb4c848961f3029dde1a077e541896c41da7c8449a0199cce299aaf7a8868dece31fe2814137d3f2f706db9eab0074d915d4b3fc268cfda95245999e79ae7208ccaf8ac0dbbb77c1a2a407bbdc418a0d2fb86de154495eac9c227538a06a2c8ea5a97f4932478ce9520def8480c0125a356f422cbd69cbd0ee78f24ee4b15a74d997a8b8e2047cdded922fc212724ed0b5d551aed866c46e50ac51a86d00efff15ce1d20c1e660cb30a66eaf9aa4498f2c7d7dfd517932cb1f210c63b23dac34defa3edc4ddbad1245b6f0f9fa01cdf90967b9bf8ec3cda22c8e712553880359df1d9e6f30746da77bdc981637d6d48ba0143b4c0b244e95a2befe9732cc9d7caa40e8612042c04d49cabae3dd8847518011c17394e3ee695981456d06126e0b71711eebb665f3346c6a0732d2bb1990f531cb4fd019ccddf5e50a6c341124aa74bdde3bbda98ee5a3d7f064ae769453030b153fc208387a55aafd1671c304f7bc40b0755ac57695a22e155d3b2bdfba2af1bc41e7ca9e207c00319088021169dda6416456f2e5fcec7472d4d00160108cc6356d9ebb5b0571ed87ca3b36d5480b63badfd6c55e63da8c7b641fd5efac6cc747bbc57ec833172dc7de7789abfd2d67021795b8b907b7f4231450bbc9575f333fb021c8fe9fda3986694a5de4e971c55ffe6f19f6fc3d7b6970f03470264908629c60fa5c8c895ff1f74b647b147cf7a4ae95a3fff77b7209668e268f523906b9c73710a0176b6f4376adedaf12b06f702ff75c50f7b4908f13f4bce5083755d217dc1b839057d0931af9ab10beab6098f1e91f86e74ec57bc9756972def22e35224a445162bf6a13f51283414318a9b98adb12d368f24bb9b5b4628199016859acf7d7302afab1037cbe81f58740c8cfe52a00b0e7a70c587c0b9cb678ae20bf7c9557edd04dcbf3c4e8efddbe3a95228dbd07dbfe4213cb6c710ebe56c45f166accc5bd7c294894a6477bb932f47a71eb673ab530b20827524041cfeaf89667d12eb8e46bd4b9adc966188cbf17b9b0aa0e36006ff7802af67450995e1b5aa58b7d0915b247de6a0f6538c04ff869703c12106c773b42fd769a956cc6efa877ae0a62aaef7382fab0f90d6b8f4ef2bfeb6d32613ef73094834c8c41b010439f229eea2846eabf604d62ac37b1d4b82674b212e764795ac1fbb0519af688eecba66350582d42092b3724140dcfb3a7c33d07efb1d3acdba9ba9b7b005178c370af2f46ec8770dbcf2467f2be47c67452794a3e41983fcd29b5d25f3098d37f640e2bed86087be8de84b93589136bc478e57725ebda633decbbdf97b61163dc4cc1be5831932ca7593c5cb78fe4b8abf206bd1aac441a0cc04c00721a3f066f694b1d50d278e1c5ecfd1434c87b30529fa5a94dee33afdbb911217b3c67168e7a70956db4eb0f4faa23c143196c7a888644fa018cf4473bbb99d08c564140c9721f2f5e065917fc807242862f0156e2386c081e2ced1695fa47634365d293411c32ac8443a5ef6561d978871a4bb48bffaf08ca8eb17ddf01199a12f11e4f55a7c291ee4f61ea74aff0dd2e2e108b33342961893cbbf67be41e59b849a9bf2a03e76179395afe1c87df4d5ac513147d41de0f6dbcca9b79d8a743aa2425afbc9dbe86a2e17b10dedf06f6af9ee239c951c51724032488381d12fccdbf582a799cb8af88793d83aad79385f5f8da2706e25a9cd298933351911de5668bded360ad6058dce22ec84b7b06e686c16d71455503cda12a1678f878b61566c8bd3f56a29026a9dc0b647a4a440b52bf0506f02d1ca5aa0be39e630bc2b691ebe6d75fd9caad38cc15a8fa81987ed188b6caa6948a582cb38ac843ab917701c9c2391695d95df35cdd3ebe9a3b44fe91f52c04adda91dde33a721eab6c9610b77aa707aad2ed4cf4d7bde536191d8e8c64cfc4c4d6b5e0adcf4f57bb0a977e314f4f4ee3b133a85b85abb7a11235e3e6e765841635d550393b69806b8fc8a58572026c7d6896a5be6512608be3ac40793ef9d7a036c603ca4976e2458810ccdb678a416b73701f722f27236bdbb74240b5a4104a2aaa8be561ddfd054c03efffe1e25899328723b16a28072d2f5c1c4aee7fbde42d82372b3c92902c526cf36f9213dbb7ed61498ce0d18d2cb16e66100ac4d0fd5541fc6f9a6124de4157e12466527b0f3fd83b2d30f6c2d05f17cf24cdc05e3560a0fccdd1cdaf04066e2982e631b62ded1ae688df665801ec9fc03a5a03aa115995a6849cc54d0090477d2292a8be6995f1e8514532accb4f382613088964e17ed5c653b8238a41179df22db212fa692ab87b46d7617cd714af09a8c64b3fe87e23b66ffbd74d5838aafbcc2e4fe1881557595e11113969d45e166627c6265be9eea6aed795903301672dcc3e0eb22dc650843fd5a9027e7e341438e0cc79aa8009adecb0b8eb9461656efc0467f2d0c9c97359c56a13bc5e171bb81f73621cd3719869cd5f8d97470c28403982cc3b10190bf720162dc668ea4073c51df582bf9cad8485088864404ed8a873d2110d496b122457800a0fa10ad03d62ebc86ed2a61d35104d8b84451283d254155069e8229519413d5474369b464f91a1d03807ffb14b0a74e0dfa41025575c2b7f8175c77fdc23a45a759a736ee054ae35742e995977d876946568673ceea05d9ba1ca2cbf8a16a42e49a34f03d71bf05f28a65a96366d128231be8ea79794d3a900256808dc76bd10d5d581d8f99050abfa9da8184be7c7341b80f3f6e82a6f5fb454549669505ab44058b21578592a6200521d1d4d9ffbff182619372be5773b2874a3bd9f109a68eaeea70b882b1d73bd2b36dba55a571b6c1b87c4aabced10bd8d4a6d8de53e2885a0d6deb4ce4c0ea13112ae5b1c385fb9a258a6bad03885a5d013ee8deeee6a0b25b385972af92f767f7cb50c39c98bb50b333705b3d8f33797f0530a51a62f7c64e6918383fdcec284c3e033b8ea8a7c6c4e74e5126baf2487345ce5af2ce960922d99371afbffb0d0d9159e9f30fecf4ff21f10844db6d2d4df00244a0c1c57ede463b7587b773d52109b728a13e2f34763b077a65c17ffbcd07c00f40c6bf0e1e87728b7f11cf257fdfbf4bc40c892d9d7cff24511ca22e23539652994b7199114f0757651e3647cb1eefa6caef0b2897d2373540daa920d5c9339a86006a3ccda333cf8b977acf7a1e3c6938e2b718ce20173a2804db1f8e3cd5524d234ea16b96df1172a5fe10b200440ac36054a45d5c68fdabf4bc76db469cb5682eeb22774a4e513f62e4011a103116d2f7e7db75d93138d85bb99b505313772ab83f02e9db0a7cddc8827d70867c79192b470ea03d06621db36c3e7ef7b04ae52593cd9143dcd2ed636f1572e00e6658afe4d25ffba3b4bacec2bbfad8765f63ed40eb81a0041617b29018896bf076cabbfee3bba3980f3d7ebd4cde51b2dd03a0b76a2b29ad8f8d6cde42dc8fba7747d64971a509e45169fd93cfdd956fe83da7333071e7d5779ed0fd86edcd82588ad6a4c0dbc151e27402561c32834edf4b2d721b59b1022e8c4094187ccb0a53058d9fac626b0a5577b4e8c1e4a977826ca29ebbcc0c263c3e38946f3a69ef88a292e9b1cc407e8165181546788194fbd03006da6d388f04b11a4d912660d4a93d201c3204b4fa8996f09393e2249a375088da1bea76c6a24904e06af44531f989b35d8a5897fc8641287adaa6cd4b00016f9b28737688d5d8e9a252656fbdf2de7d4d039b2f1a339c48341b3622e1ab683c211247cb763ccaed08460f0d70ffd2ff977fafa0e7ef8ba9030639b8241f22669cd2ef717cca460abda22b5f6b538c9807cbd2c72980b233c5dd20981f1043d5c1bafbfd40fa646893cd4f80f56ba04ed08017abc62e56bda80c5cc1f100b8b1b6a57b5a6038e1db755f0aa2448805bb378fa6eb33f5ddc543eb446f38e318665811104f0f1900428dd9e6d4afbff8eb84818338c3c0581fd2a639b44454ec6c49127c216c86287458479dbf3a496bba9a7e013b9cd6a4e3fea40a3107c500f9d78947158d64ee4009022ae6fdc907da47bbb6758c1b2f2a3132443ff32e22563e1b0db5798a4b966d13b9dd13ac45549f6406ba2646c8ca7ef1240ace1c67bf7e41dc629e4fae52d1b9edbc18ed07e72213f8c91ec6054597c5a7159078e012e678fc6fc1b8483804b1ca64ae597775b88bdac45e1111006666f4bf9a7b336303299a296565f12652ea27e04ef74671b8f2d4756bde0cab946a7e9a706887865713e1e47b15254c4cfce0c870230e97bff12751b2c444f3829d0356cbf9552f266205572e45c5a7195a7b4af37f92e9ab21921f585c179c6f93c03437d108bde831f41c149362a9f4af225e42f684784c5e248f192545aaf6e459eaf0758383ba16b1ac6f22c750b4a1c74ffaf48c164b579582b5c08110452c88089c65188689224aaff7304ff4695a547a7636acae46a0175234327fd0c479c6c66f4c48916034b77ae4a3645ca2248e8bac870ab6f149a74a795b779d08fcd7b684946eaba230c842e3e9e7e3443f3e3266008df1800b3d2aadd4c89179af2a273832e68aa0e80ad911964406bd58b12298e96e1bbddfcc1ba45f3e918a0683b3fe4b4a00d2e70377369302e6d8aa5104e27553d9811ff33b1bf4b4bfaa730c9c7fa42106098d37afd31e851183a1544ba68590b29cae09d5c67f7b737061cbf6ddc371c11d6f7ca199f759fa6a8fd29b05122132012deec4037d05ace74c9ee672716418383e20676ff500b2fa965d2d27f26b4f2cbe1f165269fc07b006b32b17e530065be4da34d2c52b03382abfa8afa62d5c14edc3c9cfd5559ed33f29c22b32e0e1de1d2933b96511bdc56dcc9c226bcf9186c183e90b1645e2602d796a07cdf159a7e5608d6b072fcce9a2b88481721a4f347a40b9a9ee309be91777e520e69826bb34d6c28cc19b0d1a58257156b6ddd3e4981ca48b7d71452aa039f6482363cec1ef2ebe0b1d7c032bbf030541eb91692f5215492f0d6d625cf467668ecee7cc8fe33b2488a6cd88fcc801060c95f2a5262130a580cd0d2020a6278dc7b8e4e4d71d9f62941610710255c1fde1627f7c388226deafdcb55d3338e378511f65b8341ccc3a129f81650741da03f91c807664be9de86304f33b2208d097b0f5573bab4006ae11fc841439f9b3f1fdb71827e410087057063cf0b9b735e71e00fcd5690ecfbee0308468eef1e6ab04a2306b4de87b936ae23295f3af1fbfe31dc38531beaf442a905e4ab1ffa54c91b487bd7170852a63f685a662880647accaf29d92c780d8a805e0e3626165a27f532280933f8a9a99f71eb2ca81035027c4da8175b595496a7f24c5dd2de205fcab778c47617e6c9dffb9aafbdcdb2f7c84b265938f5fa146ca11df3a0918c0f759295bca13b405d347daa9f433851429b16d08783bf89712a410ee096dd3c7fe6f782257a65fb31f644a2a56fbd673ab091c5da688b007ab25fbad4690bcdc3794332ef3650340cf31629983f68395f4b13cd82fc3132f18d970280dc7bc4a28995de601c138e2f69f24b5e88cddccdb8e6f7627bd5a478d04ffd9069021c8baf8fcd20be7dc3641cb4e9185b8b2753b8d4f5861c0b1705cdee8136e7f75e20531ce756ff8b766f3798cee01ab9926d5904a58a0181038af907dfe91b6d38636aa05598f0ccbc3d015070c4ec25520dc3e217e494beba0b30839baddcf6f5207175bbe9c182133ccca04dbd9e795d9848cd9eb39efceb20c46e7561535615195b4e3e16741459fecf428a774479aabddcc6f88a8a15d1d248854477c0d0e41c96205908ccc79bcaf5a3b4329b925c7aaf4909e3fcee476a8c0ef145048a712be278347e23552384fd678a24c8a0a51d0be829f406a0279859cb547f6eda6e4d13df604683cf488b01144e93ebdfa4e6e00cae13d3484d79e542009c9b9f09348d4bc39619e900d2bfbce9e1a51698068fc5b085ee488411ca2dce1bbd6b7e1b16d8463a317ebc81893adec91385fde9b5ec896642c91417a3c5238c40347ccd38a634fb31fcddbdc94e0b847df3422b530fe008f9371077c5847086c776f52497b3af5ec3fc6662700b46bcb0ce52e17d837ffccac6753c3ae2ad4ba9a52aa5e39fd185bd46019a4959a33157708001c1f141377ba9e4fee79c73dee56575a464cb198622a4c347e42a8d892aee15125bab9dcbc75f14ae43e9db5cfb24708af1d3a23c95648b9b5e2ecd5b414fa2ba2afd44b79cd5926e3e902067922a8bd11120eb2a9d59f2285d82c3c2a0a19acc59d5ee148985ba97fe5ebd23204b08008c5033d2f1656fb39d70a83de7a011bef69287dc4c97d89537f4b3fa11421728560e0846daf7081018e4e9fef1d89960437262d82edc9db24de154bcc5c04eb60186eed24447b2b82652ec04481b4b51a4030388d0470da3f72d8e922875dd0635679ff8b46c3e5fc179a4036b6747cc57be3702ce874fc7097d6381c0d3c819dbd20f554a717f85e07d079b02857ffbb5072ff1ef842138453ad9e3b70c8d70f7c8e11037a8f36cddd5637c9ea3ac0ecfe46846658e50625c7aee153b5c1ff4fd7fb3c6844ed748982ad615359828f552b205a0722ee9b3f55730149d9cbd329e9b8e7f7f9713db0eca5ed458cd51316292dd9e38530faa23b4e7f25ff233dfdf11769de561ad32e9c6ae15e7e607d3cea138bd521882e13d775836ace2732ab41a89bc8e42947a459ab00f94a59a99bbed3314d860d751926de91dd0a623d97264537b21db74b09ceea872857a03db5fc347ccfd0e75e1422c05bc793fe66f51e71fdde0477e317e570f2167bb5a4dec2cb6c3a1da1c824063bc44f6cc02eb7c36fb9ed441d8377c6673bb305f8aa225169750162d7c2ffc8b73b9516eda5f51418c77c0e3b623f5b22754afb17fec3ee3b97b54d04c1450e1c6d63e1ff8e1f47720ccf823a99147dceefda35420ab56b446b340c3f802fc7b86930b447d8f5b2fe7eb377aca90f952a7de9fcdf98ba076c117be4b7f3391b6a2c33952ee06aaeb38b933f3bd15d5fe2612671647175558ca8a32f1cf000234d136126b186ba91e85b54fb34311d65d50adce5a3753e4da1bd0411fca2eb3299e2f20521e77d350d53dfbcbf291ac9bbe7583f109052ad95f2d542935006f23c71aeba18af602fbf2197235e0c6d40f50531ab4bfba6198e8d5479f5abb924beee2e2f4a5175f6cecb9fa06b3ff8b7c45259c5b7b356f28032401d850cfd74fd4e1c36982c9e7f46ed32ebb688c9ef9ca90bd943459db57e1e4da9f22acc9d19ff99a66d35ec3e114645f42dac20976c8aba8001a97b2a38579d60bab2076ff99123d7a7fc9c9f93c7443777929ee048f9442fe909052345fd4daadb11123e400782d973a5c9a05945c9fef367e821cebd533c09a5e9836627e27cd5e6b00205306a1a238093ec3bab185889f45a6d6681174b3df3d2af25d30444fa32a71e0457fb0154970405b8e04747348dd19f5206b4ffc2efef11559310098069454a16a42ba7bd40415eb5c109c336895da73000a8adb87617c3f788a24c2e80d6806cde889698102c1cf746c18828ee3ce541d21f01193176bb21805c3b8aeae689f37ff6ed077e54200d32630707abf786ed5c922bce899818129c67d811cbd3ca94ba3204b14ccc104a86f2f65bc117d1930cd1a55c3bae793c1be34c4a4c652d41379471e995c84f4d26974ed253667cedb5203f170891657036bc59226588011466feb12ec001425ef67e780624bd5d83dfffa4c88d47b1f6994595965140dec8dc1d5854f72e1323733b4b03f4649076170485c740e04ab6f8163398d996588f41da45def07dc76d19a097fcb90b9f55d52eaae8b5d48b8a498a4bec61bd23fe5f8eb792cbb394142a4a3e00eb3a208f3219c5a104044cb1cfc7a3d936e447e9527e464bb33443e8ef5492e127e464ce68a6cf649bff3f036ed094a165f89d2f0fac94da82cf1706846b28648f73bcd5ec5e5a879d6b07b6492c4de0844c6920ae4a2d046594505abf569b68aed43b9614ea7d1e12bce14f54a4a9fa5ba1f94f30084c0e0c838ed64705676f2f21ead568e75b2cf768e770d4644b47033875877e328510b9b233dc8e06eeddd82bbdcc876a6e160f98ebb130f31515ac416634e0ceb0e9b19f3cc70b3f7964d1d84ba761159e8eae464f34f86694c092016e26d16c7c96eb8a5239456325aa09ffa354353a4a31afd56fca2597326526d3bfbfe314ab550ab21e11854cb710f79c5fd97c1ac310c6574b08bca0d38235a49d34032beede9d3c6599bcf48d4c6d12f84d817fce9c8aeb6c32ea305d225e7ababc86ce1f00302e2ec45c70f0e5d6ba1c16673335d807982bffd6a7ca38b93e6a8574f134ef65487506b71c8c883f959954020158607330290d5931f392b10d87fc29edf19a57ebb87328a106379ab84c5db22db10027c3d94ed8173c94457e86dc9f3cf5950c9cd31729ba8bd029b8e9643271d013ba62ddb095e8ea80986ea84b9e3cc0ceb8a2071493038afac698511050ea2a1d04d61de0e6c8eb9be827da665a8d4a1ddaa2ee5bb7dff86b7e2fac4c24f888e00b34d693d657afefb28e9ca0adf4047f71e431ea39f856ca21f294474bc3bcf7ede5277b6fb2a59452ca94648d093e09980946ad774e54de789b774e54cca081d529e65f16cbebe98b2965bee6dd1752d8d0a94227fd01043052938c5a63247750e195b8834a93665031262ff56ab2a2c2774e5f8c89aaf39dd31750734aad319237a89449a96a4df34aa5799253d4eb9d9313d45ff72493aa7ce7e424f550874abdee89080e37ef9cbc08f3d83b273135f8eb03157380511a04c08caa05270a15c8084019d5b99b449412513a825a93a815090ecead4bec9cd5803bad3142c7a23b1783c047e28a201331ef38571ddb6ae963226cc7f90ebbfb9d17f8d9f6f3f737bbd39c2bf280a24b774272738e8788c621f4351f0169fc79b703f1cf91bc3ec29e3b1ad7d3ee34b6d08cca7f699e46020f3a089d7351628c31bef75efb0076379296514668879e9321b7ac5b303aed16acdd823c608570747bd163348e5d8ff67ec17d1164265118c5de63c26c96a8ed906dbaa87ddb9d06c34be29e2e8ac6658fd26feccb46bfecae5b94bd5db7200fa1db75cd622b6147210d0c2073846940b560d7f8a0c584a75884e7b2c20d3a849a9017043a742cc68804be1c4e62b0476f28ad8b7000672ac7a7514013e2ce747b10b6430fb21b5c301b2c5862ea58234295196a0328f452653e9fa459f9f4763d349f73ce19dbf544cf36e87aa263aa2b3a8db1098d324e39678c32be37ae5f72bb475e6299765d9abdb10ad1c0476ffff12e7a515ae98343690b749406218067d3c172618b0a1dd25a6b0b27aaf23848a10b31982207182730e93c515178a60baf78e890c94aa3881426b6e01fd34b23007cb4b0a9e6cc8557fcad4832104211480f5561946ab45c3192ec03bb3cfae57aa8dc8b630193180b139b125e49b2ec4d10e910cecbeea65830d7028d934fc64d3a433bd40f996510cb884270d2c5ee860eba18a38b31c6182384537a11e3a0994604daf350000f517858731c10111cd743023509ba22ab449a3c20560a2938a7a2532b6048ac1e9ad5201b2c33b002833e092ca5c4e68c524a29a58c475e7af4979342b913c4a56512ffd9a8025303369019b51dda019ca9ce6f470812f8c4844142cec9a5cec90fe10b451f65943228a31b46d1d2e68b301c175813a4cc92a0d680cc70c71808e5647668444641847034b1a0a3ae8beea9b304748194735a87f34533233a48bbabd0666b060d4918e078ebb67c7c826a6c6e5496c803c2b1372c968dadb197f5339d2fbf362a69e4430b91a2126a478e31720bf3baa8472afd4d0897a206a4fd551cd60caac8aa9043703142689deb89914a274d20918700e072e687daf99d26c4e65b02dfb395706b7b086bb01f497c961cd6e4544dee6a72b489001f5d134e4e0e609412fa4879a1c308f235d103d8451186e8020a6903e6496a21258f0c4966478836314802d349591815315e50ce19393ee4f7be28e120936241e52bc148262d241709864766856462a5272d9229937a493565f884fc6469625ab6726ca565b2e7cb300c661bbb9e20f1a516c37b9515e252cd222884c6b80623e94c519fbbd0c20a58a61119924e4430f714a37bcaf185a4a19231866430e8e4a562659599a184526e97cef6f2bec0e8364220edae078e8c83f489f87aca29e38c51ca29cfc0cab185c3eaeee830ca68a5cdaaeb111de27c91ac6ef9b051b58f16ba1e08a3732de8ac01990ea775cddc145da06a6a814fddc109e4637a08e1cb1ee8e01335e7610bbe2d0fcb4327541066d03b8e067a4d3d651f6caa10bea6ec3561d78b4e7d7c7eea6f9dcaf1d2bea31bd427e5313de4d2c50a7d4c524a5ef17130c69e31f6b774b82911e2a3a3d11f90db6103e8206d661f2dfd1e77907666de62cae0084d545249afda3fb05b549b5a90482945624514380fbb21f77629b3832be77a92e0fc78d13a9c2f1a1907298e029818a306c48150f2c945f722153e98aa5ed08cea7f7d7c58ef208d2970fc1d21d0a3478b64474874e9dd62cbe3c541a111cd74a2a810467dbf839aa185686d2af590a1a0f3d3036a249543db5e780a8c467ab49158482c2fff845496a19e581c45b3ca5310e98b239e8246b3e3160e0a8aeb90e0a0b270693824489b26a8591d74ab3757db9325b9a4f228546869c992a1fa4af8cc673ec47acc970cd1d7fc5d1a463b6a41e2c3e82a955320ec8d1fdbb1101ddb187e9c9a90166252a6d6bd9c33caf7d0c217d5a0b4d6a1a7a38ae92838053c8fe223948243c0731d6e0151685a9643c0731b2d5834ab43efcc9933672093d7501256773d046ab1b8a0347f5bd2562898b2c480acac32b3f4081b42a60ea1970a21852e9d3dce08dd7d8c10460825ad382cce843833a830830a9839945249dd08263275895c2298a9838dc8c45d80e92232a9a488401f92c42203815754c2b3bb5b08ea7e21da9b844af7d43d6db1f9177f8af9db46ff42fc368241659b7f0e99898bca8e45760d99b5f8e79299fe45f89066520e924e0bea5591a16ab242e5858a0c9fca45d5e47ce01445d5948334663a8d764553357f3c98eaaad231aa63a9527f4fdbb2cd399ea78a7ec1230ffda1cb28e7b4497c84e4506106151fa5087c1e5a9fab6195f7da99a5f7a6448867774ab856164b7ab477087ef41a371616353ed6b514ddd1e893c619639068337b51515bb12d04d23852da21f80f3873e6cc47f8107e0ba09c9bc14347c643a783aff177404d1ecf7318f4d007fa43ed87eb219d951ecf8bf2c239567a4035853a15e394c64a95c5baee137459e99fb4445ccf7395d375647cd4c546962ba21e424928949978b86ebda2bfbc658c97cf5b7b217eb4d5f53cb7f9b284238416baa74c8d2c98a0eee961b7986016d8aa2a3ce4f110e76192d6bf40151414634d1a75098454b684e818a346160fa1dd092282d623a9dd6218c6f98a5457e9428a1750312515a60a1c8081042a566ae025e98c55921a09e82d4c9cd172230a23fca08c2962d4c0053b44a12507680ca1c61a467084d9a96246819424798c1c8161841760dc800830538411050cb8b8224a119ca088f1061532e034a4d2987a6bcc39671b46cde38ebe0d24e88e68c291f2240c2a86d6a5075924bd37e6498c2af28c324a6850a1452990a4144ff41435f0a2075c4ca14284182820c24dad80094d65aa8c81f2e5484a8b11d660411296a04286084544b821e54c10b4306306644c99c1d359c4ac622095e30515a9a7f4f2781c7d5c628cb10653d745678c31469af8656ace396da014e345c59873ce39a70dced0a8e69c2f905fcc3967d3e3715a645f48d51773e280d9e2c916522810a818b105181fb8c08c151ad83895f9d94fb8818413658c31c68cd06c297ece99719164a8d0b06204217c618224369218ea82040e90b4485931c38da3143a0a9e330d2d524a29796074f1e0a8aab834c109397124a59466782a534293128c945443d861c5cdeb62ced9c511549f04262967a7204a3e050cb5204af8ccf428261679f9e9da048a56600d421c60ae8b17a66e2d5bb0748bbd00f6f9f9a1cc7cfaa7fd3d23e7ec4d22b7cbca6a37f4d7fc2ea95bdccd80a9cb513cf4151f6590a5ccc7c8d1b12c835986e1e0f05801a89f34cf2a5bcd4805118ec71d61a58927a86815e0262bf2c439f6d146ef9a8072c417ef9c4603b57d74f478f8e8b28cf4785010c34857d889a9234b75ff6c96cb2ffd2b545e8f997d54ac599ee2299f6a692f3cdb5a7a502f1b3d3df50f990662369a277ac4c9a26350c9c8c8e8892d8ec775a129f852531086e503022918e358707fdd3e3f3d208e7a3cd1a39e807054b7a25334eae5275aa65e8662233eea960e0e6a8c43bacf09378e436e6dd42d8e7a622616a24ffed7076a031c70401089fbc9dc081c50753cd2a31f611fec58901efd8c5bc2333232324a12a2f28e088ec725c141a009381c8fd3803c9dacb522dd8afedccf7357668d2b483f70eb2ddd8aae098ea7e5d19da7a0f3ce691c501be178a24fc1e17a2203ea65a5a7feb9eee9a3c7d84c1f9d530ab0f0961fd243915191970914ad208914c1416e35b06a3714095df082531c47f3445fe9a05e07a5141563c4b2e59906a93a216ad9284ef5195352ec471cd00fc7c31ed48aa2ee870372c70374c4f1f0d4e3891e59530fc32bb3c695a46fdb5ac8d4eba0aecfd47392d2b31f79d17574ce46fbeb005821a19cf2bd77d18cba9b336b18e564a6fe712a9f1a104cb572156e4a84f8cc9df642c64c467cf480aa738db33396fed132d9881e55244ea25155ea39899d68566226eec25e6ab6ea7856fee3017113eb8c4a9d18a5ba72e6a16e7445828f3c2b775a909516334c95498dce0ba7b696c6d2ad6e257a04a482d222a3070521148482500eea4de7a6a6fef1acc462f9115fa500a3fde39cb3f429a5c3fa80daa7add1395a6a7b2b824274f712ce0e283adb2a84e691bcdea8bb1ebebaa8ed197c08fdf98e9c9b686dde0984387ae8cc38d1dba57bed0f493becd6ab943ebf59e6204dc253df59309bc7d826c9de1bf0d8eb1de77a2e0ff9fc7688681f7f87c8f5bcf178b645db12f843f276f7b379a7bdc3dea95652d44bd91e51ed11f5b2986374176fac2e55858c66bc6d358fa932dee292f843317b2fcb9addbc32de545854e798aad657662708b3dd09c2354906b3e7aac7b422d0c8283ea63d4c2be21c9665bc5d18e4c2a2c73a49c6db12f8495830e238fa5cd47d2d614e854ba13bbffd0e5ab60f52a367d5201f4a6b6d5177474794562029bb35d2e37167b1eced240d4874ec4e25cda9466737fa68a7966e3d9f4adbc402a686a165d43b95e6f389a57fb4f48fe3c7ec777adca1c1095deec43509fd40fd87cb6b8af08eb4442eeba5991ecf6c2dfc140715dadb49ece34535d203a2fe1e11546f8d7fcf5f14fb885ef811c1f5885b501bfc9d4a6b4825a50724ab703c2a2dccc2f33763e0c4925cfc9b4a3753e99fc72630fe2d817fe5138d828e51a317f621c9702c3c7f1eb7703d6419aee7f97b627951a512a52c07a5124ba57f3109ae0710be090e2889e391621f3a381694e07809ee273a139e9fbb369efdded0312a3b77f7c55f7e72c4f5ecd042542a5b03730fa8014f9a9c0f1b60d1a4030afa781c8d192e28410547459c34e04993d0b345fc8e7560a997fe0e7b3301875494503fa1210425a11db63c4742ce0c15f74df5d2bf9cc43c3df760729b3c743dec9008226adc18a732a0843033849c57f6d13854aa734729729429008e132afbe3f9eb9c73eeb1a4686539436c51a410a99c53d278c10c05a50adfb36358a6d344ed67c7b24ca789aa65aba6ca8e8252a57f296dd769a2ba67c71cbd00c0ca42c7cc6adcc841332c4ace95a9aeb86942c3ab0beaf0bd77e354d99f73ce51d6dc6b5e317995193cf5fc7dd9ab8e31f5390f7916095b56b7baaf1874f6dd8f5f90ac7fd8d26e9581b1c2d4aecdaee307b5fdf293a6a18558ccf3e3a937f5fd748049bdf77610c77b2c2cf0a903ba5cb5b5dc831e4ffbb0e391d74ba28f7e7d94f2bd27a2175e7811043e47f8ccfe7c6a41e0f3cc22845a10e885733cb0c96b41e843784536dd016596951e4fbb92d2d6669a87c532622821541edd622d55a3de3e6ae0a13f08a158c13c0521538c314230508ac70bd8bb35281f955a4402e72443db56970acba664d9ab2c3434ab0c535d92be2983f95c7bd6b91e2e5a6a03c3685a5881a7a86dfb757775ce654a2cd02953db56b7b173aed3820a75aaa8cebe511b32b99eb62a3a6554a956b7e8bb3518daa91b15e3b713e47d7b5b209e2d753dce332030463927a5d7b5021055cb615896d16898a32ba901a1e941bd8e4cd310f48ae39c06aa3a0ae46a7eab5bf453035271682e9d346adb9ba9b08c46732b9eab5bf4180dd58040d677337ba41a0d2d84b98b5ef39211beab2fd6560fbfed65f26c75a25ed1d3e8fc18628cf826f2e3f1b40e5c8f1cef9a055e8403a2340549204169ad2c160e1c0fe604f920400b2eafc2324dcb3055abe5e3c38202469c2c0ee8ba6c1fb116ccc5a8fee81ca378f3eefab47828657b9bcb5f2a2ccb6a65b1dc5bad151669f30fbaf3ea580e48e5cedd015d6f7b7f3d0ae753558c7073416a41416f8b0fb716a6e6e3031f6e2b682d2e2afbcc8e2e8d0627d4468fa77df5c535d697cb5dabb7f5d3ac26ad691474151616ae4765197cb3920e56723c5038200a446d3f699e1ff6cb357876622e380bae0293425557eae5c4b737966f6fbf2dad8dba55d3c6a5a269f9f64e471616bc0af34bf3b78d30158bd58415a1c23267a4e28265591a4ea908d703879639d763624fb3d02baa266db49ae73f7aa5aa3546ec80dac8f118e1802eefcb52bf8d82cb6f57f956fa71f3455dd168d90a5ba9aed56ab5a2abd594abb882afd9002be77aaab4895ea3018950b277e0c05ed163308333b4d0ca3149c7a8343be27a4467dfa17ffa673aaf9cedd373ce39962ae3a87cfb754078e73a381f2c85596877be2304224102fdb518d7837a2fe15fc24f8b1894225d1ff6d157feb2946fc7565bf765a4bec23e3a0cb3d0ded4af30ed5596ea4eb38185aa752284104d52bb97efcc5e96966fa8f4edaa7e5dc43ca66eb55fdbdbf2b4b89ef60ec33ede148e85f6aec2f5b4d30ef3ed3bc454f6fbb4b0188d0245df424e8301463bfbf2654565cf9a66e127372f087590babb75c8e09a32c207d9e8fbaf0a5cfe065d7f5d182be22f7d519736ae2c712404252e5f08c1d18511ac606982811742d73d7d472d81053ae881540ba0baf41288b88114229ea0050ea1eb5600e6db3d8a6fff31c5b753152cbe9d76f61ef1264cf852136f626f0d4ce0e083259a3e04e13858225a83bd38b80e49350861ef1126f636711f9298f8920f41d83b02d721c99770b057045e43928552832fe1e047ec65e242f852137b7df020887af06a896894da240a519426be44a4c42b09a2e8e04b479a381b1179912fb182021b821e7c499556680ff62671952fb14475882f69c0ed6522ac2c1952a23a2f61912871eda54189217ec2455425aa2ff5c043f89012be34e443f6d2e054890ccca10c5822ca43f8120df60eb14494087b33e04396285b62ef90574b942df1a5ea3c84bd489c8628be4483d360eff5215168f0a5214b9c89f0a5217babf3103d24b1570319f02595bd54e54b74a887215feac13360afca12d510f892ca8a067c290343ce4f7a702ec293f845e58ae5c15e0c5091e1fac15ee9900a0481cb6089aa0c2e83bd18701515197c8987cbde1f9c52f12569efb5daa050e724ec074ae04b1eb07706282b7762af04c25e0885fa92aa043e0394197c05e502be04c45c01618968097c89fee02b4b942d5df6ae9c5aa26c897a09ec9dc18a7fc09766f019ec95c1ca0cbe24c30ff65e5e025f5ad94bfd07f70008ece5c11251207c894a5f415759a26ce54b2a07c25e0c58a20a025fc28063c0de0b58a28a015fba80dbbb7227be0484f3e0714a3c81a531444b94dd606f9c223b60ef0e9e83c710832f7580a7f852b437ce8d684a5f82ce489603f61aa9b2017b895491556615e84bf206af42c4ab18f1a50dcc0a6cc012d11b7c89dab0142195e84bf0067b8970c0978838117b63a042c49762b0c1dee83640bfc137e04bd212cdcf72e44215e88025a23bf8122de215b044d9d2b4b7022e2d51367d49fa0ef6e66089ea77c09772f01cec356289ea67e129164a0ebe64c49f4471a1a522f64e2fe24b15b0577a057c07fff1f80917c144f0106c542444104e8028c10f4d361d27da58ce64d3e13e6c3b3eb4e9780f1bcb976c3abc6e3bbeda745cb5b1fcda7438dd767c6e3a2e3796c74d87c36dc7956c384e822dc747b0e570116c3b3c041b8e27d9721cc996c3efb6c341b0e1380f5b8e7f60cbe11ed8767807361cdf61cb710e6c397c03db0ed761cbc18f6c465c035b0e9e81cd886360cb01007e81cd8875dd4ac12db0a91e878d4675ebf90d9b44028c5410de86ed31318418237c05b65983a31d60f14536a9832168d0c6d7b0b96ea1e0344cf119aa3811a6e243b62c3c658acbb065e12ad6490bf1148f61cbc254ac9327c453ec131ad50b78ce526c141772bd00149a9e02db9d5dfeb990edce36fef904b64bbbfcf320db956cfc73096c776ef9e7306c77b2f1cf5fd82eddf2cf816c579af9e711d820b05dd9c63f17daae9cfae7bf5d19c73fffb1dd89e59f3f60bb53cb3f77c076a7977fde80edce2fffdc85ed4e33ffdcc7767b6c774efd73066c77c6f1cf83b64bb1fc73a0ed522dfffc67bbd4cb3f5fc076e9977fdec27625997fae80edca33fe7902b62bcbfc73046c773ef9e73edb9d47ff9c85ed4e29fffc00db9d57fe79cf7667d23f5f61bbd3897f6e80edce30ffbc00db9d62fe39cf76e7987fdedaee24f3cf09b0dd79c63f1fc07667997f2e80edd227ff5c85edd2a37f1e80ed5229ffdc6e975ef9e7db7669d23ff7ed5227fe790a1b00b64bdbf8e7286c97b2f1cf796cee77362a66c746cf60e9e428f3cf73a0fe39ce664609f5e998cd0604867eea98cd769dcc201eba837e87fe36f9e8f0e37be8377b3f37244a9abce6eee316a992a984cfbcaa66abd9a2d39afac7893983463d2027f4c8fca366fe5d3ae6dfd3bf3254a88ddefd954d609c04c3b59ecb19fcf369837f4ec9f8e79719ff7ca5837f5e251aff569b7b4d36b91e1d4b58f945a7ea1d1870a4e0a16c3262937cb4b109363e22ea9f331252ea1f3f23b1412323213e627334684a03c948884db0b5101f6d4e888dd8e4142521a70121229b1e8f14ab8b4e0c8a66a5986e3d19a65b2fcae684188a954ddd7a414d11aab61d5154d9345971f52e8c21869093a38889e8215f24845b65754b95542fcdbb1ef2877b1babbe76efc27c10d0490963dc92649fc5fe78157e65fdf8e1ee7ab00701732a503315622cffd1d573dc69406a1cf3d64b5695b2e6b84e55c22f574ba8fed745e560ee1e9daccada70d4a8978f5ad1090a2f2f9d0543ab0083daca5142c5f1da3f361e3d49ffd478744c4727c75db7745c6e588e4e8eebe8788ea5ddd2b15787d76e6943a465396b435640a8eeab3bd763878701a4c6716cc0ae5735363540aaebd9e14f0ba2ed702c6672a3711d873aaee3d5f570afa3a3a3e34bd5910e5d8f1b9714d3f19c0d89741dc724cb5f0e2bc7ee08c9f1ea30e4ecb057874d82f32c7ba533e1cff11c5e75b69c1c5bcdb12c9c8d9d7bab1b9b9aadd26c3bf1b56d475a22bd430ae7b077611091f413ab0f080a45a5ea3c3c20773c91c69a77609c79e3a533520ec698c20ce1cb07e05d98a8195cf5b2c9a22279ce7e9364cf2251d12443af7295843dd45e4a915097d431b6d93f66161cb3708158e25ccff377fde5d39fbfcda7631fcfc669b21d1a9afae4211571e2db044a12784037aee39d13145776e81fe77434e6db79a0a919675996b18fe7ec927d5067a73f35f39b7dad0efb67e5b57add2816b57ad36ed178b6539dbafbb7ed54fb0256eb7b31d40a352036d9cecaf9576eb3edacdcbd8d636e65b3e10c516bac4fb71ccec66aa15bad1aa26a7e7d5e90550b41396dd4e7da9095a5cfdd5bd59b2454cdde7ab91ef6b7d1cd69d90bd3a9ca7f3cd576f9cdf6446b9a62c463db556dee69cc549ffee9b9131d3e7efcd379ce9d202ced4eb43e1fd4c214d57d1ccff37996c60dcc35507c02ea2cd19d3f1c1e0c9ba7fd6d3bec910636500bb9a7bd80bd733dbc13e4a14502037f34723d2fa0f00f32b91e2830b91e7621baa89bd768fa66776bdee978e7375a56f8769a28d7c3c7b31c4ffbb3d7b7407b5b5dbe1d4f7704bad541dd6a2728e2f804bc738242ccc3fe617e07a68a26bc03b3e549f4ebe563dcf4ec3a2da8ec42b774aaa84be0dfa0964776e8d68a09d563dc8ef0b043b71a8b0b2a9c396d54e8cf7750bfd19b6c24d88670e027c1d010d7a30161c93ea496c3842237c6770c9059608756320bec3ffa085b1eb0300bad24371e8e78a97287a17fbeddecd92316b72c971f7940979550471e0facb4638aea6f8d9ccd3b309ea0c6b335ea967b21ec39c01b24d466867647da2141ec4111b296cbc2f24475d12e899f6427cbe592e9554d7f59e8eaea0d9f56cb9dc57a8e2d03ea2b930393737242cb3f77e185496a29901b83db127eb8c1a70161c870731a0caa2f13e53bf73110fd64527db0cd72d41252b970e4422a77ee00950f89f14743d686047db459a06a7bc24e5bcce059c341652d7d3c8d4403d2de3f12c935e442b22384e8a71769f1443f3d3e2d488c2ff0c7191df5d0639492d237a0cf6ce2e0541654a534d3c283e551aceb253d74ccbd513ba395ddebb7ba48a02aa36f540ccbe81b15cbb29a699af55546dfa818a55181a6a5028d06038fc76aada9b179ef49f9e493af657353536f6856373737373a9248a66fe7614483d1383e6229fdb4c02d6fd4e8654b649a349499991976ebd26a76f5a652358631166fb8545ddebda7f2d29b8ae96295cbb1ba45afc38149040c3ba3c99479e7ef8c2663a6a1bcb174616a25a9f7de7bef3d18e5a490d2d74ce5a417bd545806b3ec3567adc2326d0557abd7bc6a97692b1a9fcc6d2b2cab2def9c46cb3bc768b0bcf35a33b7ddeef2ce6b986ca08dcd6bb669a56e399f6a238ea936fa491b49890933464c18324f4d57d8094ee22b8c8591da4cb3e1ccb42175a4856ec185165c189fee0cc2aca38452be66c9fd1e8431ca09e77ccd134629e7a4f4524195ea35ab26bd2e950ac3320d6ada6bd654589669da6a455361adafb96a59370d4dd6b5665d5393b5cd0dbcb979cd3736997338396ebb3047e6745890c57acd2cb62eebed06de64bde1e064ed391a0c39596f393418f8ab0ed4d179cd3a74b513e4fa76feb6d7c93f9b43573b41680fcfb69fbae5fc0043c0e81895a505e1bdf79e7d999886efbdf7ded6ef3922fca35bce3515b1fe74d4412ccfffa5cbcb18a4a51e2dec16fc72a55daf7874ab1f8503732a60aefbc1eeee39bb7bce0efa37a7864665b5faa7f91b4785fe71dfdd2eae6e115dc2cb5e8c2dbbe1a6045e21206c0821430861a651e057625c8f73965f60fc0e628f9aa9cf7b01ae090aeb9e2d46842a43536a87dd97bc41df3e93c770ba82899b774e5744a1585c3dbb79748bb3b8d932c3a0746670cc22e820f5ec6e874c4ddfdddd0b00a3740412ba40011c50bc21841e1c818b0bd620e38b34ce685250e82e1418a332298132dde055ef9c98c4b0e40c9e7450e52f6542e5f1ce0909256051e62f7d1c549d774e48806105124c4f2aeb9d13124945549c774e483c995273de391d710449e60834989ae03ba72a5138d11f325efd3aba4e954ab38a13e4d3821542f85845b5316332559a30a411ea15832eaa2a8b38860b97bfae2982a9f39d13172d7f29172529af085fbba61f11900005a69e9c68a2a18e87817c88f4cf7427553714e749d7a9526986d3d64c15a8246f60e5897146a43e63a234ad34ab50d0873671bb4fb782daafcfb7d036688ca9c7e380509f0744a3e8f2dd8ee3ae8753c1bdd018621e8f7beab8a69d3e7db581206085d49caa4d85e6a9990e159f16825ce81f9693156368656def9ca4a8f1f19d9315b40933a21149a83b4248bf3ced1f0643a63ebdf41b2df53213b57179e3b2baf4367a40470f08e9d14cb91e3e5eb682a424ad59e374009e6e05f8a007c4a37924859665b345bbcd6d6569a4d7ad6663a3cd66bbd9260f41d06beb5b2fe801d5b8947eb3d58d323153eb9fbdfcf4d2bac7c3c3e3914c2bcb4c8cb4ed0479ff1c87a7968fba95c383ea849c5074e8d299c9f5c05e3a77e1e101516a250e3934d0caa5634f0322a579a495d22df9b6ab52aaefa85bd2f57c512f331d354525914c2b7f0e5831b1171e5c0f9ea7a23b2afb42bfea72367892619eb84cff38272c5d5ebafb7259cc65323afef2192f5fd44b575d9407795dd4cb2c5e3ae6a8c3009f5ef4b2ccd4a55bd22b967a99e9bd70174a77667da2ad4501f52f8a3b28eefc3d87107309a16bd335c724ec9b24c94fe7797190449f1f5b8b1022d19ca5bce6d06910697dba3565e32ace5436b6e21874a13a272c65debd8f8fca5b3e5354e9b7f52b48eb6385a9384fa18e5120542984c6a97394cd094781b2396128ce541cbbacd3802c71a701a13eb8d38034610a032a9da0d4a767469cdf090b1bbf72238ebd8c6e40563692993285a96c3b45980a13d6893ca2e24542d8acb97cc7c8ca651b48d786d3ad1d23f6857e2397cb31cfa5197786d47329062aaa7f74fc73c946ff38a728b6fc9491d52591fae7caa42bfdd3585cd7e530f05f32e901c5a9a91b2d3dff5c8a79408cf48098097f2ecf78409c8499fe19f2e73947ae878f7f7e0da126e172e83262c49f3f20272cc449d89d953b214ec2579cc4b6630af5219e03cdbb30861863c42fafc131aa01a9e146a340510c457ce53fba868d5f6a42566ec4ee0859b91137629114b1914cb71e83da9019f56ecab65384a73827e19c847582b921d524c453bc4848871bd5ffca242243eca53db80cf6d618ec65e5602f0be71d187164f91eb02c3ec52467269c351b58096724249c9938c21989894d1e3d9ee7183f6676717573f838d52d1a841a0453714e52c26f8c7a1ea31e503ce3c5a87846b76a0bea954ddf3f373efdbbb6f8d4ad179da92851a58a3b214eda766c602bce539ca7d820188a10c6194fa1514aafe35d9828a61ff39aabb07ca91957d9788ab3958da93c8c64ba45e3d3f348463651a3aa427cf21b9f30199d7cabcb7f747cea1f1ffc792463244cb736e944b79efbb0edd06da75a99f42b2b9586a84672945087ec954dd0f550eb561953a19b7be93f7ac97631d6706877c247ce56ac13a6e2ec4ed808e72ad6094ff1296ec427f020988ab7476d0627c4277038433b5371aeb2b1b395ad9da728e981ffa2d8d30c5188aa51af4c4a22f2cb7a26447eeb337122bff499103993a607c4555c8fcb9f634f1819213c0d3e83632fbb56ae66b5b25e045b71a7d9d0426cc53a114288abf88ffee17ac4c431dd8a425cc5beaa8bda7f65936c723db09f36eb96ca31cd55b6d52de9d087a8de08dfb181a33cad081ff98e0ded5cc5b98a0d82a178bb13faf178cae684988ae644dbbd68d82ecf0cdbe5792632e99ff7b0ddfa579e6073c2426c026ff54f8d3f5fc147f3e83c361e0eda1a973eb7a17e1e5ee3ad09b1716849a0f9e67e324df121fe7c4a557119fc3965a2e231f87309d584d212132a09aa84e43bfcb96c0ac29fcb3042f87329a6c89fcb3146fe5cfae0cfa5140efe7cf21048d59fcfa61ffcf97c32ea9f7b833fa748d01f0d4e81195c8813f109b86b07a0e034aa1d00fd79c4f12028701c97c015c771184ee038fe42139ec38124798e47a00687c01117d2c17f09cfe13f50e039fe800cb80334e00d48c273b80b573cc77db8bb80f7c0803300c9737850163e81e7389011ffc9c117c0c45b20720534f10430118e002ec27df889b3b0c40fd083f70cf90a25700300e10570e23c97b7544e80950fc0061740055c85221e001d1eddb25cfae6ae7fdc31a759c0dea2b780fbb0114da28d68d68d68f6b0114dba114dd54634e14634e5463499444952622f0e514860af0e51aa0f4144e9214ad5c1077b8588c2a409133e3837614760ef114b4471f02511d85b03134d8c8aec85ee8325ca7cf02522c7219f69a8c42cf684558c3286664000010000003315002028140c88c442917040cf6248fb0114800b7c9c4c724e1c0964490ee32886619031c600438031041842883134366503436df8d0d8ef2eef85b3fe55d982b8ff599699b823027d486fbbdb25253af70b13bb3d5bc70d5f748e5e2fb66dbd4cab1198204a7c3b74c60d2e76ea553b9c168e3c0cc20d3f7152a1a4384553a7fc0deb6a00ddd548cc32e6cd4f0fc18b17523296086396d5d66fff4395b711da4daeec72b46003c026e0a6d3e15984abc5e863602f57c6b32b349a60876a4c6ed9e96a0d38b84233fe7c0730bb25c0b7b3e0b5c8d80635a2037fda2d02d9b6d9b2b65130a5d0f1b04b74476024ba628c7749f77a9dd580dbb1489ef9d185961db4f99cff3ba4c4ce7d73a4d14f1ebbc9eee5086c7f76d25ffffb69df51746d430405f166abe66d3f9c8589106d72941c751baa8562fc5c2199950f6f290235a3c28dc931d317840760ef7b89cfdda6f91d23052afbadb323cfa2cd83216319dfc259b8b79766d9886c86061eb1fa08002b3e49b415278a68a18d42f4cc2286d94e1b689f6cf35c2b7050b922014e6557df1959313bae5c836a6e9b25092bb253f353590ac9e5bc8bd816c62e13a21c1176abd68c4ae0f1076252984d37c411253e432f1e45bd9d5cb1e3850e86f6281f42b13229c7bbf52428efef8c7e0569097a04492baf1242e5ceaf05027b5d0aaa6784a16d871448b2d64cba6518d91873a0538f55d2fb449cddc363fc6c060c4eb835156713c3b1017796b12ba72df4e01bfb89fb71257040843ea21311fe5c35e67ca797243d132f21a13438d4ebe8688129a8b72f5b45cb59b1ed97fb534e0d39eeae319ff23a29da5617d51813844ef36f985a94bfe81a5a8ac338988d3eb6fc4e6df79ad070bd2fc7699c2a467856542ea8ae91b655761890220fb4f4a060838b4afbd3b60fa13d852312269b44ed0d660d90168ab797cd7f25a653d09105c2c487f14cf4d2f0f19ba0f4d0d89d198c4217d6b2823ecba30ce6cff45bd616c780013133d26d15b5479959b0d126bf9596b9090ba8f572d75cf3e3f4d0f0012d15094a6b43ba3df2a2f7a2eb44d4ad07d01b6a4e4063b228b50af18cce7a70155c3387577f5f88a04bf32ddfc005b1c79553e36416cc9508e6dfa043bd0d2038f3a744580fe28e4d0bfe356ad7b213a830012cbd4b44f2f173107a98c4d0830fab6abd1b1ec513ad62e2d31a69d370a1877dc0d682723bb70bb306d87fd0b622bf2fb65381ed5fb845c9ddbdedd520fb07d6d6e5b7c276ab9173c5b57e31ac102b4b319fb4d0e3606e4de195862c250c27bf32d02207e3ca534f1024d7e60497e056d720dfbd0e41cb479d605497a7679f26683fd00abeed71dbd78c43087702323a7a36a7081dbcbc4a9b18d29169f6cd24ced3ec0b1b55996f2a319299f78808ff829beb1b4f85a94cda10f4a5d6246221f4a7b9f61ae7781cd1e122f00bc15b437ac75b1a8b86d845216015fe11106df7f038fbc34d4680814d081373d0bae024658dce4b0a8f766013e364bbc2898c4b0f817a77a06881af5af8d144f76c592e6338d88adc1102ed27031eea3db6aac1ac9aeb31408d67add752b75f38314b9cb378dca4b0450288bfe21a64d3ae449b3b067c91d7fe8b9e4b5d395e5db09ac5fd1ee725db1ccaeb034e309d87b730e80fad89d4ef978fbca70cc9fe6e0abca2d2967a1b62ead72e8984b0b824875db5f78d4666f2fe25c211b02b3f000e2a9afe95da66c9f80b4da668e7d0c985f40703552468e39717ab7a8d0681280c30b65051c6734fddb74972e6527e0cfc28411c17a102a7ae2c12a5074af7e3649e23df178d0a238a6981a01747ead2a0b2135d7b5136027b9d9bde307aefbd73d8ae959f6810b1ddfa04b7b3e3cd5ea042fd8ca39615cd5830da323cf44788e0cfd2dee023b33ae3647cc393719c1bcb588de3b3d7db53cd362e41c144a52359b5e74aab755fb42a72d3128e5423147d2bb2fe4c97caca18d02320bc2fad7264b7832f20e71345d898299fe6f1c0b05af9eacfc4643301aadd7cdb0d1bb6cfe391fdb78f9e5130667853a6eb23f29f0f447b86080aba8800c6479c0c0832d48c995286c5c5d190dba69d212dd3609b7709bceed00b153c3ea2017cab58139a943f0da25dcf051d11e6e4c4a4060a82350cfbdaf82442cdec876fc61ac27e762c3e378c07feda2bbfb4f0029a83d9d8df67390600611f95dde33f8964cec67c30fdf8716ee0958959d8c8a939701b78e4c99aa04dad2c9f51e9c993b79b5a0fa78026bb3101af7b245b1ddf1e574ca8529ccedd50819a761d3343c34f3bdb0e2057e1dfa0947f918f55391300f6468e3124d34b27b78406a2a5c10bd57d0a7b3ba2a77863fac3264ba18e668b9105c45b179177a29b88c8b10ddf61d72388122884d3eed48696b9eee05a28761c2681e559c99f1a06c4e04f56afef5abce57193ed91ce8ad216e44de5600f710bd3492263ed3a39a680a1cbe69b9b729cfb8925d805d8338ac13439905e0781843382a1063fa59859df292f893d7c7fda9c3ee30b25a564bcb92b9b70c6996282192b5a41249de941e017ee21c524ad4b5fe49c2c25d1645c37c77564f438eee0cfa2f7c8649f8add2787b0158bbe1ca24cfad6f6c9f1c55a1d729ffe226b5dcad4f9fa1d4a1b47b497fdab5898351900ba7800cf685dcd7edfdc64180ee94f43682367d133c50703ab609ca77aa6fdec6ffef72dccf44c21a2b2332a3e2a98dba8d8c7122d55128a7de481b5d4aa0814833d54ce0f32bc32eda071242db4c986102a32132d2772e77cb7e1c2344ab9e3ced9cbad4aaf57756f0469cd216b5d621de6472dd99b09e7ba081c5eccfd3e66be2cb15fb665c7a9907ef106c326b2335017d85c5341138d033a5f2ba5c70b4ccdf75d9582822588869ba682727a2d4f9e5804926fb6b006d3be8f034c7134d44a49af55d4a0aa6144869f892a54d09b6c268cfb60e1a967b79ef079911b357f2d3685420c4fc821f47b36901d1c6a8e8874ca8944d3d8a7e386aa9eb77599953318a4a3c803ac23299ae89c4db8ac5af2a0c39baded8a1eae62a9a18455cafba7c1ef1f9d60b18a978b93aa2c698270d64b65d0b0020603b22bcda3d3ed5c74ccad5eb25f517c19768c7d3684bbe5384a5560709dd16ca516b49aca3669fde3fafde299d1ba0180281033bdfa159a3d86209881c6a4476813947d259a1a2729020f59e48aa710acf0548bc643ec9e60035960ffa65a53b3951aa47e9b9fc935f1f9fad149655ace9b25250c2bfd3c8ab25b451598bee8a56c7e0abfe3f6178a269c0f60cf42d55120b61d41c6274174040ffc23f22b0630636ab40d48e30749842643400020311ec6102b16afff46c7c5d8c4852d22f56301ce4099da9684d29e79af43111fb0b2f90b274f9ee993fc48c262620d6ad5a676d546c720f135f46b59f496d67ffd925c21be8efa317546033dfa033d4e148b0cfa6050e8c6de78077cecd800f5f20cc3b0be41d21665a57929beea1b77d850194b69f40be9dc318d1aaf80ee664a9f3d20f1c6cf4feb6d64c5e33f4f480fe3218dc5c1167e20c3639785085b4a1940b742c31e111ac3ed0bd7f34f99db295276cef3e85a0770326fd8a7c207fec2b1ace0f5f13214e64859b42dac6b4d970b8cc63a933f75cf0a2062537cfc9deeba723c37d34203115a92c20e6512f56177b72f551c596c18bd7cde1542dda2e6e69b184fa8b8f22b0ef53a55592e3dd2760ed3837ae5b76a8b5870523df321681b1c5f8c1e911df11bccb8764ecdb0a4e4f5e678ab3016033bab8f9d8c7fc181e37ad47ad6e3daa8f809935a5a163095f4a68e9e89452bf0f5f6ea6511dcc9a74e4536db2f15cc4cfd356ff2ec8f7e0dc3dc76ffa2afe51ae88bd55519b8f61f4025eb805374efec84675002c0cde04d6993291a9f205541dac26bfc712f91585a6a841c7c25d95409e1d1e7f16067cc084464483fe947304eab8a75b6f18ab9acb6591ba2a3a0f2b779104799400f32c3a650beac7bb849620aa1b3fa304faac600151cd662f0654131570e188ceb661e43ac64640e1dd98f28cd7389a5297069309721544cd2b744ea43fe2d031dd5198b638c19f3e318d6528362279678f2c568491820aa2b283e6749763e8422471700ff04c2731f0929a212ccb438d6a2e699f9c03c5a2b9b03e10adcd2e0f5f48c460a673a742a88cddf1bf4255a965ca01c4fc1af4e6318e66c287a771f4fbe9ee0bd9dae9322a83ec27bbe7a5435a12f0497f45e76d9b4ed7e1d081ddbe63e7177d0492cabb37bf2e8c6a8df040eeceac0e26b2cd7ed3cfdf51a4a30711a1c866cd8d720f31c6672eac0047f2368430e3ec7a919a2cf616783ee305dcf1a55bbdb926b664691466967bde2c6a53e8c0b987bb8ccb0d3829dabd64b2e5d9a42ee77316302834f99345c4890514f05f06e18dc58106ee594ff78c2118be75a4a59e220ea84482a1af9d9546dd820462222bffbe027558b20e282c5852b138c9d23302555a5074132a47c98b839ba4d53c3c8f5d1c17e46336198691632cd403f8cb9f5987006a4791517bd243b87001200b96033f9439a414ee04e98a2608134f429f98f32f03ce1f6a827bbd0ec5229a71d322d8f1e3c132ea214929b50d5a98ef87985ca4dd6332369be701c7914249da703087bb89b6cc0275bce28bb63ad045e6e6f2337d0eb171ffb1ebc18d94b66f54428082db4eba4f50161b0f99d3446af6bdd818582cf8e6816e4e9bcfcf72db137876f26f8fd203d819dcd833f6844a5c2cedddb1fcb78e57970ddcbfb7654d3d16449b4fb47d364b260d7e375200a7b691ad3d77cb962c95ce0f432e7aebcf0ff85017c8d24f5f159cb84efd5d0047570ace5d57b17ddf868dd1069f0260b91f63acc32a8c0e8528d2720dc62336d49c9d8712208d40fb09c50f7e26d984c3c4a4bc737918a80ee1917b35c7c732c149fadf556a59920e025eab5e9f15b822170c863963b83d6b81dae424ced4aa19e84783ad52cd4b17056f5e62a60e7e04fb067e8079fce38e9f29d4172ea7177e4cc22244a5c8b0bb3daffbf2d215845ba953733774a8273a9a02b949dd97615c30c6661cd142fbdf0c3287c83fbb881784e0ba99bec75232084ae64baef986ede2040516779618f6a3d9a4a375e301bbace8d7e4e72d2647412892ab6d7a147894e91187ccc9f779fc70c0ca4828fa5a071304f1c1c52b57f3ff4e9c0f3940e98697ca3f5327d3867d803d6c20792e56650fe81bbee30b71f30d6c92665e87387e2f74b5a19febf8d2c8125de5478935522cff99ce0b554d28ec530e4905e97c3fecb0ed8f2a122956702690501f2b4843dac9498a090115601fb72e0ab4f5d6d511257e8ac395453d7d7e5f44f817ee842e4965053ca3b118d417b552fed7f4f5cb84b6a40d383b70b1fc18537f2f03aa857409c6f2a7d34cc8cf33b7a70ab65c1c6eed12ec5f5dcfd4613e2fc6800077376ceb7124ec9e48816f96e88e3935b12fee08709672db8d943bed97f549697e6130e7838a628e62780b4293eae002c0ed04193d5fd29e89ee2d8661bf8a00bf78805f74422f75e8cdc9d67342428af05f4924e19ff52229f5e896eb3a36b2cc9a540c4696aa21b28b2794f0ef68ccaa569023a1e940584aabc84296a93cd47bae23d1c9916c89a25d06d59a60f0eb7c9d4cb27840c8a318d6a29de2d7b77a78d6d4ac1317aa6d5efcdb136a47375ea87ad48c82d2e4c79873bdf8722e039bf9c9c389fb1dbe8f732db049d8019d7dc573ab5fcb56962c906955f8b887ff34acf95b5eca6f62ebbf8620b6c887f8dab82a46049d46c1370fc44efa4a924fd9bc534175494e6fe4e1c2a2a7fb9b55182df46226c31b0b1afd33584c82329ded7208c58fafa4933c41a2a30fc0779d2cb5f45c95a422b63b7ccb34bdd75c847267c3a2441f8254f5ef9af7725fa8b34247245090f2f86dfba100b7350bc7afc0c0a99df84ff062107bf0ac7672638d3ed7a64035dfb85abe2dd430dea00be5810cd2b46915a72ea1a7146d88e4b14a1a8452f7be2a0fb130431f3a1207bee43b1e1286c085d311afbb065ec6b9faf0361ba1cab81d3545d6cb5398ee3514d2a93dac5ad0f7b7b3cc838657bbec2e30f6ed412cc54a4798e47c1c9590ed6dd0e097e64be46e14af2665b6b852eba20c46199c8f91a6e44fd5f4716fe20a6bb98fa8e76d2281ca1154bf1c5b5ef10c11bcbdf9072cfd059a8a366ef716db32605b23140276f5e370961f00198d47fbe6bca5e155a123f8f67e39849334ab159764d0e1303f0fe550396847fa642d344b43d4d0caf7b0cc3893f457fd853dea38cd573810636f5e1e66f1f30f130e3cb8d1a6ca367201be2cab425514ce3dfca2821e7758cc14195d872bcd174e3d783c4cc84dbdb7910e87f3295308e9a0b82f5b8d82feee225dcb9d58f3679b31a028b48b407a506ca13d090d29bf1c9d1462d32717df12ca324e71fe8e1ccff5ac02346372889a29f8845531c8b036d1a14d96793067b4e72c8fc6f15e04acb48b93ab90d8969483ed6c0f921863ff7f4873e96beb6bc69c37c7e28f3dc2be66d83fcd119b9f5345c733be26c23ed3a86e9265906246509fedbbf45da625eb199f6982d4d022eb75895c8b1650c417d635449b0d18565a6e4a4fc0b7706d88477b0cfa430622b61df55f7d550f0010b77c60b5a041cf4623ccdc4a55e4a1b6eca24259cd65eb3425a2818e287d5cfc3ac97240259c18a506ceaae9b69ebdc044b9a7f5394531755f42c52e441c969bf8ef9edb664166f00391a3568a9650e2d5214339562daca31cdeb71628ede44df36a704fea53ebfefd13197d4824c65fd5b34678488d88add0d1318d2d4585bb36d850c116645703a1fe39b1772a4f50426318f58d153fb3596d8b86760f6a00c9c253564a4026f44b97496dff035dc27b3735a244df866444f0c522c6d246a69288dad724e612e26a2275df2493e0523734bf7895c6e7a31fe74438126d988d73b6ada43049f28c7a5895da4a1dd1c294654f22a9cf45c9109d4d4abaaf795e6083abef838539e70ae72af4441a0339eb4b5bffb5339134f8a5be5541ebf63c75f279013d3b341d3812f903edbccc03f35e1dd87885aa6dc391c0b6e5425efd4e7783bc98f3f05ba82e2584872510751f12635bde10b8e0257e2483fc5450ec6255c1cadd775a1f1f49a4eea180b37485b8f2a83c504e2f6058c7ece07566b34f10308540abc62b266230ab6af8ecfdbc8a3dd88e2f64ce2dc3a08da298ea20c2badb27df2528efe040c52a7b0a64847705555b9d36c830c38d062f7ddcbd0dff392fd1a2afa6d815367054087765cb2ff0a32f9cf8abad383b646177bd899826b2429bf51c83c6ce5229ead81582a2dd19a3614efe9867096d1d467527b321780a056226b781ec753a7db2b01f27bf2b1a00983e15c31fcd5b0e2440deda9a2dd9760b99dd70ee62fe9eb57fc8cd5811fa49f028a3bca1a97b29f68b3f43a6727a3388334fbfcd080c81c7be7b2a8479811a75e145d8b591f0a7e8b8d3534c7e0cf68e1ea45672b597763c4efaa89f049e43864604b28c382348d100f1aaab8f8023b824819ddf036003778ccf451aed8cd29d0b23034497526836b6cfcb2b9338bd2677761210245496e227b42bbe4329b73bf872f791bf1c9b8675c64ffa06c865bb2624f38b63de38dc642c8b0f61fd122fc4faa8114ac8967db1609123f33a086cc379bec302a85feb59117e3760461cbc45b081cd6afdccf8d961fa0b02151ef1d11419f9c8b84dc0314226231d0e7308292d14a99565032546b7913fbab48e3e91900ab2f2b06ea189b111ce074c67b306dd162ba5ae2cb41d56c299187c956a5e7b5da001ecbdbf470c8c708054678bdf19f183b6b0c5e03b68bf85f8909d5d207b797557469c3868ef6e366a4e3b290f4f407bcf8cf25880fec6d6bb538f57a62a7d6f95f64e1694d333e480b718aab76c819249f9f28055789c9003dfd974ae07b0bca2d1fb3dde65a246d84f68a3f2b6f4ae06be0261fd1c48a8de83e5268d78286c62842714a4c65130459ea81fbec17103b7cb7e5be4d74e45255e1e59d7cccb8bccc0235b9c4097e84961318e3e6d18e21a0592244f22d166b9c64e45d0a226ffacbaafc7bc2a7df63db5c326af635b64d7758a146832ce7405f24d64e2c9204e3b955b3db64a0c49d8240b78b8dea200a0b27140f885cb83b9c52d138df84db581110a14b60302d79751080427672cfd4d1fa3bb06fa4dd6b42045e9fc3f3f03896745ead9eae8abc49fa7f31077276a17c02647eb733e02ee2d57092d58fe6a37c24671edbcb8e401e2f4ca94970de8538d4762de1e8fab6a3e5442d0b09b1ba37b155e8e386a7c549604a1c5aad956409bd461f69710f379c98e25cbc6c14065054c7edb623c974449dfaae4bd6ba3a83cba6e06bd470e747446259b89466667005bbb04ca19bc90f2923f5155f7300591cce231534359f19ef57867dc09b411888c8d8c73e3a66219592c7d7a5ea3a15c42cc7362b68541c2b38a7a95b7237e7259d7b6d2ecdc02c928dcebf9033b7f207fa54bba168b6ce82aaead1041b8f9e18cd515b25087e0d38e4191407ecbc34165b53f4a577fc8ced000d73c547d854eb8e102297ba4aa4b2344fee60eee500bcd7966d77381d2c2e1ed18a973df8bcb2afd7ce388cecdda178eb6222e5a589357c882123d1a80dc09558ec9ccdb5abe5597449b421734b73412d71a02a2cf20dd76499b5d0a2570a940745d68e846b4356c2456be625d1e9f560c6c9de8142c3bc3c050da3f8348bb8f82cde1fc72f3d198775b8528774631fb6eb4b30fac55cf10f755106a3811277b5c8e9e4d24716c6a34b08e5d70792864c927519f73d9c8327fcbdd776df2aae6933065b59f44a9d36e6b59b43bc98d41aeb1aae15a555ce807a8be5bb2024d57348adcb9fc428375043655e2d17b93bac4dd1901cbd4b527792864e46b0422b2543a37347f7a7ef32860cb33d1153e46b483cee74411a47c572e2f928076d1362b9f712e8d2e731c4d7051f42ffbad977eff9028353acff6a5e16f6b1be1ed2bac3f08fd45fd9d6c72833d8a90cb18143b710a9c96764eb45f313cb037ba42fbf425d974b0745a693f824424d4e686a95411a9c429f525eba665fa691181d3a6d7517c38578333c3b2caee9d700c3861eb4a284305000fc04a9493fc555954bd1d879aec93dca13893a6cabc6fc9287c122ee973ff09385804b4229c5765558a3a7ada69ae053e625053067932007f1e0d93c99631bdc8358cc2ba321bfe306d47408a8e1d86711950ca5c0fe1f1d29b122f2ea130a6c636f419d3fc71d04a987af8ad4b4422def234c0b4e6de3651becac2b7c6e8ee10eaafd08251a41daaedf86d6fdfcb5179b5d73ce94654f56b134e4a03ba5e0077ddbfed1d8fb9406eae6e248355925b152fc08d82596c95223e02ea7b0a880a9f52c5d20b8ea8ad82b592a4125e691039ecac049ec35aca41aed8249350a2a7efb2abaa87dd33c704b3fde12aed9c45dd62cb715e594cb639599c39e12944c79c3d9d29c5947c7d891b1cef52264ecffaefbc328b8981eb6939442cf1ed6f282a7415939488b046261cc5a2bd80efef74bcf7027adea11871cc58111cf85eb080382735a8f8a440240d135c8357af6d3e4425b05fc7cfff0dc64b35983a90c362136f99b6e4579d4c8d61cb0986c763631507a5029d8d4e4376efbe270118b3892c57b5ac99e22ca398fb864db3fcbff67d36b00d7aa27e84823c7defb3e8f7dc7ceff56e927c2a1d1989d7f6f3b91046afaf6ad90eb5b896a5c084615fca23c9d361afd7b11b89d5498682709ebb180c4bead9a566d6b3283e408f497561a9bed6f8f33a6970c1f5fc30c6b8a301bd1b0e6262f6bbe11e0b62f75f745ba20871413e44598673cc59abe6d015f17031f58e67534a389687cbd01689cc9ced1148bccddea19a050080d39ba09a473f8d6ea8bf9f2938e5cd037df4c3aac7b794325c84206d1567eb5b82ac4b9a054f653d3e94b3fb632cca68224e6720c26aea2cb3e518ff7a4eea5c5c7fcd25462d9a56a5729c9832b21038c02e54c43f5985d13719cd994c352c94f7488b172f61b0889c23be202334c51fa155d5999a17afec9c891e3d3960c8b82f1432db8d028cac46806537fff1df5d0efe0d64d4d2fb4fbd31714e34ca9d138c868200dbdc403189ace399c3f1c3e25af2b5864941cfa51b5d2721daf9664a500ac44f147bb111acca9b23ca84b6843dd296583126e6e83838d84a5bfc0bdc9d907926f4a33eefa0f7d33787c229add3bfef8bbec2a83e5ea8e49931abc23fa7ebafc9730e7e32a4af4dbc2db786a3166b78adb07326f9a10f590654d0d770c05f4594b413e21ba2a81c3653867c501ade0a7756176e549fd9e352f39d93385cffcfe654c635ae351b18ce8b54ffeaa219d988bb216495c89f4e591c0f2d1a002749ea9b4f8e003fde01a1b5c0be556f9d7b80d00b1f9783a81fdd6ca2fd82bb57d38e89ae44f07aba8fe9a9654b27667e514883140b80a2972a83440b5c8cbee275e30cde3a12d80b1b21aad8c0dc647064b5259f433025720926117e9c43e12feab7dfc8be06abb348793cc45f33c9d080d8bf405d75e68652e803ad814bfe7e3880690f85e021572ea747323b84e687fad93db96c1d6c941ba4c23e23ad13d0b1c281743ca783f50cd02c713ba5220d59335db6174560868a00c9f29a0592588b32dd6ba3e69168dce0acab783a5939d230cc2ce72a03ffcc4620f4b40cc240ce1517276a481e1d6ab90e61f5c842804aeff879eb1dae164cb6feae0bb99bdc23a0a0e50365804c67dd5be3970ce122e54cbbe0029f30910844382e95e65432c0db75d2bc04a2b4a297bd17559fdd7276650ac8f8ec98af4351fca83c3adf579214787576c8b8069176e4c4486a52cf1e4e8d34e60189128becc3bce34bd72637e697cf3ed54fe38a31acb0404ba5d9bf0190b329dd339761bc4ce0850d1f8ee83f2a3db136b50dab89c496aa83ca5f895aec8f2ad2a54ea633aceedfb2d46d438f21c27d43a22fa9a5c70ade9181d36079fb0d324bf5ede444b1a10218963ed162849ad8c53628a08afbb158ddceebe1f43facf352a8e0927e19f48e5623d53aaf724ab097e6047646d06faa4295753d628ddd055216e5e3e9678ef026763c97fbc95452a94de42d54d3af21a63a6c404c697c0b13a311bc24ab2bc8c707d9ba65060572f12eee7ccea3a11c4635cefe596aafb389a7edd480216708e6677ae3bf32c11021bb503720f558466cd8f36715c00aedde1a5ac86f8cb2e440ff660bbda85e0164a86f48aa2411951aebd10ea8ad49a2228b3a41107e064c44918d109dc01e1cb9f39a9d80e56c6bd8c1ad5665ff0da3a744ab128b8b82ede036f3b7da62042292642a87d55fd541b78051c103760bdbaf76b5a163ef0c47d94e1f251c704d09cfd7ba42f0a64b7c957f87312b8b1c0e7fb008b39d4d23d0c3c557859f4beb12cfbe52cad091c6acc756418f0f255e9bf105aa1079bcc93faab512839d81b46d8b69f2ac7a22f6e70bd6eac3e55c88b6d9023067fe84a51017db08b6ae358dbf226154f350c77f2b9c57f3dceda10e9b305ad3f5651823d74dcfbf73be147d1410706c0ab0e50483b7eaabe43b9d2e3475ad07aeb96ea82563e2e5b3e5662dfc9622492bccd9359a9aa64c78202ff444212a2dc38534e50b5fac8c92759a34a0bb8f09b524f40efc33b536013259e22301312a0abec5647bebbb6d9741e139af75ea3e6533a4444b450aa771f59a357bca6b4e486ce91c0e7dd6c4c93fa5d65ce2277bbf87c218f1c54ebb7d5ac0885ca156e14a4efa1328128232b8314240f2397ef1b4c52fb22d1c11e8159e72ec9dc610a85f81ffd38af1a86286fc0237864d7c9353ef7ccc7fb6b0cec46bcba8f43e8cc88509f313399387791bf6a00670112fc994e29e06bfc9b4dd0c5ece9c2b37f67715cd7489a5fd1b1011e2992f532a6546451c5a33d540b3c6ad4ad742367621e707c6cd061a3a4ad49cf2d620ab048fd5cea53774de9999b60b82d24bc0b15bba2a11f4f1cf7b1e1a74c157382524fbae2a7649af6bc2388922b6c30793abb49290b333f055c3dab12d3f60af907701418ca005e2e95762f993f2692b0658f6646e87c30544deba760ed23b5a10b0ede33594c00d90a048de6d974a39db8c95994157312b7bd70cc94eb5244f7a7cb14bff425ef2c3f9222c40535c2ddd1bda6f18f1a3c829f1bb069382dabd71464cbef5c3ce54d41ea16c91711a41928f1257cea734e72374aa29b282b1f48e36c2549d458716a5bb0e9ff76b76da600a450424bb4fe0658bf1f52e6f38d39ba661b27f581ee504eb74f3008d3f4a20048401a6aa04a0a98acc66584f494838d9a5685d05d4712b95a2f08c869cb325bd5e61e8ddd4e71312795d6ba9511adc010e689a74db72200910d738e0e4399d013a8736021e1f2ba3f72065ac3f1a029c573de6f69943fdf5b2080cce424fde4c6f2d3e044110e89ec1a198a18009591be233bfa914208db04ab0cff0a85eac5108846d79c1406aeea11688614f10805e42b33540694df75b548bed1a798472088492499b9d2e8393511ed76242c7e2ee3148e0f07d66f8625a264078aff1f3b9623e221b3f1498bc9aa0102ffff47ec08978f1a6d80e84eb0f6c7a6808afb32754fdd58f4053259192e3d88236d6022ef852fe8ff18ed3ff862d5ac80e9cffddf4a319cd83311f212f13a57209d4c3fe019d62708221f48777c530eac498eb7180b734c93c463d05675c5e57a599389f754c05b6f96898188261600f9d53ffddd43e349a6341c6b886626011023acdc3a8115ad8a5d8ca61120d2f945efe4265a0083a1780dc73cab1607b4fa8890922ee11100d3758f6d70f64e577eb4fd16502b2570965d8677f8552c737327194d9c5abb24c043b020cc2ee65e1bd715cd169aecfbfca53bc98a6c0ac1c08fcea8e73908cafa2e6d80ad1a9bd34ad02ac58ffe09e5d0d9ea53c09794926024b1bca0f69c2b4cd25ca54e61aeaa2e2d0660d064a0aa981bc8154abee902cee7b67820a000fb2915c61110d34eae6b562a85fe7792f056ba245c7ee58e9913278317b0240785855553c18df869a0941eef11ba3362f76616b6ea8db3c95b345f5e1fe75e5e4bde5a234610a0884d60e9f49ce42d4e2b397d06b67eaeff6cfe6852c3ec104e63b7a408dfd6a7c4fcd0bebc54970cc1601d6383cec6763789ac52028756db22a56ea97e65b3651aec18cd9b3223af8e4b605313befd28f5e957a098beb09de222bb9edb474f54a82801a69b2fde9ac9e805a6dff44ec711927652aec18c39796d64213b8c3b6fd06f1190d161cd252d09f09869e97b8ca390bd74ef192e4ba3ffe85883fa5ea9840d56469a6f12c4120f79e8e22d2a864e061ce6656c207253a0b2a8f9bc0891bb0ef249c0876ba3818daf7e7f772a92067a3e609ae84775fb66f88343eb2abee46a7f83e95bc8b7f363cacbc2568c84d4810c7806e620712ad0e33bc4a98993500ca5ad10df0b673856aee7b6cea53a27e645a0ea484519ee6a72b10c5e45c94d95335a506cdeabdedbc12aa1ec5c5fa79c5f6b3cf23212d4932d9002525ce48b5aca74e335321b4f2eac9e1319830485af47809789eaf4f84888ea42ac2a49f976723ffa5ee108731c7333df67ae398f5549c05b4201acdc259a76d33549430aa3616b9e2aba6b192f599a8d261d4ec8ad1be6a0080f6196447a425e6eb741ac15e4ba4268df97ec1ef393ca08ba07a64199282c70d427b4da360cf21ac14c263c6f46ff89116a9e8f0f81909408dfa6767942175ea22269fd870a884c1f57f86f5cd9e0b904b1c6e820d2f0951cb2de2b642199d55933fb1dd8ece01d9fca26b81cbec10da27dd4a5282ea9b1c27bb185145cac526c59dfb08974553adbb428b01dab0f40498a10a1e1f84b026073de8bcfbe4595c646cefe869b08cae523d47e597ce7d2663c60696a9d1eb6fc43227fe8bee6514acb0faaf0e56635b7cffe686cf02696cd5c06dd6cf4ef0e97607618cf1a8337169698a065b0937d8516b13b9fb86134b7d463da388472e88e45674003e32107b6d00e3a13a9dc972fb6f1f7839300f727fb01b40bce387c375e96f73a9b1d85519cc7f470001593577a65b3a83d82fe92d184c3b97b307793532959a007414043bfcbe81fcc8ba20ab640e93925ed64ec57cf564bfb80d5fd1808235c4a16fdc2f08ae9da7e70d832d654d502fd41d219940ca2f64f0519dd7bc68ef4a111ac43d44f5dc8d6ae9d6fbf1653ff1d3dba72ca270da5e32d23724809f05d94f3b002e37b006b4373cabc5870941ac4e5a8476fa724b81a027d385cab060cb95fdaa9eb34f24395ae5d6d76ca685ea6972097b7e9fd81cb0603bee168b67209fba35929c234a3e80ade8da15420b6419adedf87bea48211ed9095aa8ee8cb3eca9b12ed6131feec43cf005cd4025ffcd3410f64ea7c7e9067c3b8135628414e27ae96e1a2d9b4b592108ed3f94ee442cd231e737324981f2d1966eb97df41daccedef17f41f4afb01bc7f99293363b4f730f6fab2f83ca4926216a7e08b912df66ad72577a8dfbe471d1ce2cd70b9b6311c67e31e97b1443d4db35d7773e2ee8dd18de5d36f287071254daea2336fbe7bae6435ee45f25e2489bc43960f28eb3949e7abb562ab0f73deef41bfd247f80d9c488a3e555d8c223eaa8910dd16baa7ee545bdce81d04b55aff0d1e033edc6a673a32e74d67015fc59ba59b770bbd80fb7983270eaea12712ae62e8a74fd7620770aba1cc4d60bc91e2a16ee1d8c6e8a883761cf6f214bb8a7d24a9a0518522768b86495a81f871a20cc7633b7bb640924f086db6254e8cb3cc553000338f592e39395bdf87fc42389013bdb474b8077ebd30fe87b16beebc643e5a7067f354299427caf94db49f5330ef9f445b8581dc4301cdfd987673cc8a2cc341208e50fc1d6b39064ad404fd7e5cef54f7e8f06c2f4fda0ea4ef5f4c2252b37e7efa664b4c7d3d75fdafb481cc00202b5be456ba6b9a024ded6d133f6f530829aaa41fab9caa7cd4e529a9a1d112c02b9fffbc1ab785267eeac681a8cc515af6ba976c3cccc92e908894603526836fe3f45e44aae818039c8e6026679d4375b3c32c5d96a4eb44a2c19f71973a547a91a3be11f6c7313ec0192b525cb9fb6167521f02731fd2e0564ac4fbe58a65f740e38783845c6e6883dc25a77d46a6aa8b8ae50f558bc93efea4a572cc5fb6b627a98038e5c42967ac1ab80627c1118c055928ab1c4f8df6b9e47ed8d464d50e99eae0b496373d5c557fe0eeffc81385c8f98c4bc293b92c261f4ace59cba18f25709a255cab90bdb0033152431b4b8dc2e7c1f74217004442efd5e2393e7cff2b5abed6b987bb4d2b30a1208a69a5d4e54e699b36e9a680c9476fe1a6004a905f37858f9b655c50e2ddb6da90c34985499c3c982450ae3291c9f659bd075b561c2237f5c966a986208c2fd3114118e360d3169f42c5012b198eff43c90f90306d9f66f201977a45f36bb78dcc1540d136828676987072794867f59d9b839506bfc17da2bc53b253659ecfd72893f785f56790cfdda4fdf68bfdfdd6d8d5c29de963c3c4781db1cf2099198c63b65e3f5e5e76ec9b11bbc7b1c1e06546f860806e6d60eb0824e57aca787340c0d8529f497a5eb796c86523ed333e52e5331a178ca8dd8fac0bbfa2c6f128ec24e42158b48ec50900574e7cd877ec206fec7f6779874ef95cdade5179ef4e67cf02896e9a9dcb63244c123884e98751f1f15ced5b4001f6ca73254d1f10088482ac7f890cf2258cb4151c303e565bae59e2a066dce97a0c1591f3f77705d2fc3998de879e5cfe011dc2cc967fba5908403cec062436c2d8497fcba45e721b63d58d54f16d9e69a8749992d1d6a21528acde261ad9e71ec0de100e4661b0c4260716ba4300a24fb8a072d115065611cd610355df8903ce3ea1ba2c966e707a017e44365c60a1404503defcef4dbd6062adfbb3036aff1dffd760a34c5282b05cd87eeb00b368453a34dd3c44a8d088b1b69c740d1db232a2e33a73689e4791c7bfd8421f43d8748995cfb8906db57e2b338a4f0ac4cca900a50a4ebb8f9a13de7b5d93e256dd0d2b2c0c28e9f910e331db43fa331aeea69e01dc33cb17dadd5a86e395ea4fd2b76a8d5c472f0cc31e4d11b473cb99270ac20b0538228e5b457fd49140a108b47c2d9f08cdf6e5a5fe6eb233863ed7cc0a3580d0b7e993393df0503a206c9a83b1c5f49a5dec75af06bf9a6316268f85abeac4a7c0e31c54eff08b1f946026d43787babfd6c9a59db068ce63c7a0eb366832e13a151d05046536daed2e33a0c8c0b22ba2a90dc8b55d3039077ea281c4d63fb516f47533431f7f9f84449f7dcb7e2531b3650a52f265f72a5340d296119b4c81d4ae0c61b6acc1e10fe9c768c49e1247d11a289121b6eeadd97e3010b6fef7d29c8b704b012d38b87cb08be1f6416963f598018ca71a0f85d102d9502416171125e81bf74f4350816a9c3139be973450bdc9f49b632829edcbd9cadb43de482a78444d63f71ed084806b1f8d892b2326bafa33b8ffb2532a647fb875113b258003ec7b3a995083e2716230983e0785415c9d3c60a847b03ca8cc7615c5faa1db8935dbaaa742f7ac7434fd86536e542686b82e782e13ab8b972596351cf6da4d4380a6174805d4a1950dd79cbfa896c0799c477c95017b0f1cf592dc005b750df981b47b323ccd575f06fd3e789a83788194b733c3f450c0d565fb2c4f590fa707684b9ddf248f40753aaaa618f5a7fae59a851ff70b6fda34b846c6f46b99459f4daa3d17244a0b67a1223049a6b442b2870e225ba47ba246a11698aac04af94931e0b5b0c1957b0919b935aec10d19f33dfbd937e928f7c9f7986de55699a696807f0ff1ec5b30fd85db3f1b85949484a65a33bb1212e74ae2c7981c89fee0b23c16014ee5fd2335a2951cc68d6a231b0551f75163cde919620684289b26863ad478fc9f211c4d8eb1d133773413e47865a41ca00f19d222dde9c3d15899335e87b2db014041cc37c78e1eb3158a8ca5308bc537bd30ee26340d5a94d3504b2fc151fed2a8d803b53468618761603975df0a2289c1c4ac4d0bc85d41567b2380f092ad01aaadf48e324aabc5029c2dae1862dbe155c41d3094fd5e3b14075f1467358ec066bdb597c6ecb7c8f1b55fc6218fe833db0965761167383ec164d62d0e270110d7e322e1939ed4a71031930c449f5e548e5ffa9f0fa9f00bc993d0d94a859ba359340b58eb2474dd8a86a55922c98c1c57bc9bd48a5c0bef2a89025e1b46f87622001b326751b8fbb7c7ab97881784e5024b38d646d078b1cc2d7eeef2afd101723bed92f4f1edcb38f7628b36fb190be560993f45dd62f015c49ee6afdc748a19b4ead24d01548b2c868beb7190e9483e486788b2c21ecccc073bfea12cc60ebcc8c3e8840e6d9ec8d75ddc4a8f0411940485a406dbf1e919e4a134f87923d5f27cc5835a3789ef84d72b8aa126c204b34aab41ab8c18d24d0dede9f0f459503ea72c6e93e92c7056b9e2b6f8d25869207d36347ba821f32fab0e9a86f6652ccc9adad9158f345a1723a80f9a5f21f65e652aa06825f1d19899adc2a34c0437c090319cd72ac205fa285ae66cc511719c655b7e0f73da90e9281f626008b29addac1337230cf25cd403efdc0e9c870105506e32d727b5305d6aa8388a5bd2743ac643ad3e44a8844e296025fe0f86c7f20715bc0ede91d05a2bbada87ed15492f0aec3ab625f078f4106f77528ce3d0b8bb8b2dd4a10e7d2bf5a2513d5b961ebcfa05d33d4d1c61355914a606ed990bee5483109b13c48f9bf4a2208ac8bc2c73d7920d0c2ac7c58744fbc094f8954c61a34fb6ecfb23501d0087690f9705999a5716854673f1adf3c9f286cb375c2e93d747c61c5fc001ffd7f03cd56366ab31d89cc78c7e4d6181348e84d752ba7203aab1210355ba6af7e828b3eb10e69bcb1d5514b6ce14aefdc8b1e289d863c9ee88c3ae8d6d10a2ee5851a708ed55c28dd9a3d23839f62aa382959a28a6239c075a39692836e6abde6ca4aa7c50cbe7d261362343bb1dadf28a7e56c2bf7ebc3cb743b379c3d59af6c8012a7e0e40efdfc7ed884e4c3f3229a08e094cb9f33a65b97b4948c2393070b9fc28502f782dd0755ba0a9f653e6b1bf9dfb4aa555c57a4ba4b7ef93f555847be90d89eac7d46a2cc91c6e789a26483b3e6c34853263ec3a83257ea8437f58a1a7a35d7706aadf5e340793fc8a3b8adbfce770a7e062054ef5204c9ae7e6a7d08469b116986209ec705b1c51af41f3683cf47e5431ec5aef87334581bef1dc8f593149d26209d214c0d6d16c015c769ce3eb53f3a29f5b15f9d887a6a467c2286b221de38a6c069d3820551fa336f5d5e029fbeda77510bb3c994d4dfbbe7e128437be4db6b809f061a803d0e76e01171927f20a85ef1ac387545df3be6d761510492a18c0b09f779a28f216223af51197bcc2260e247ce17bc155b5a5ce7eaee066916746b9854fc612ecfb02106d02d50ad977914f16ddd16d603a23fa4a2611313e0c26b8a49d77cb0e79dc8c9d7e1ce469cf6be7b70bfa0034207d281e9156dd8169d1455f853a394dafd79d196747d42ad690ff13a110afc6e6304b4e790dd3f5e2c90c68554a5079a55caa9775e2ee402d3d9a2c2c064095519d71749aeec205911b8e567252cadc229dbec09ff91a7ed05e424fa1c1fc24100e6823d4c9e4cf61199f47fc452da2d031d398bbf5a87a7c0484f85bfa3add1bf98778bb52426c5aa83f450988fb453b1ed53a634fab4553cd064cdcf22b59afe09447f89424ebe22a999c6f7e2221c58440d90d48f08ad51ae4118c0a4fa20caeda8a7af557866d0570513e00763d48064c38b818eb05792b978cdea03bb9d40b713f17241ea7f90b22e21c5ff643460e91e3fb2b6a06a4d751f0b388ad2dce971ebbe333912c908b8ce0fa0f309beaa16f01d9baaaa758d8a0e36f43c90359a0a8897d8b42f506016f0a4524d926abf76280a0db792216b4f5312c074a3331b6221ad320e5eac00572ec302886f93dedba3268fd1c64b3b243d449b7bb9ab9074008e6e5dd57ce9417e3ac6ce2cc79407632f2d59739fb14ce8b8df6dc21d68200cd366f8577d0afdd86e761597b04f7e34be6afe784fe71ebda18d4ed426aea21f09e69843b65619da01e6e0444937c5136e9031602e7c349ed72816b81a7834c50e82d90ea1398dc273ae0af574758a8d3e7050a11f78163b113e5c95e6201987680f14f223839c3f15de97da839a48dab24b721d4a9fdd95fc06f927a8382800c392eba47c5609507962ef66d6a242aa80e967a70112b59c93eb7bdc97a02693b0187b9ee22433a871a952b928c3f3bbff06aa19fdebf61533c16a88c984e0500f5b515c81805185d47691619accbc001b633505439c592cf6acf3b95ee9e7a1b96531a926158f17c86638e4f3d601861d8bb0d1f1542d79f734a087c16002d771b1cc30d6829346c786304d036fc29dab3ffe01622255d9d27dda22199b07e2a23918b26e0688b2e24a599b04743603b2d2e1ebc75694eb0915f1f19163e1849aaa68311e90bf21a21b58873ae8fb21003392df8de72296c105a93e99400725556055eb7c629e9a920639818fb7e4995bfde83dc974e554f5054257fd0c91732700bc953cc2e881d35cf305a2fb34af84f7b4eae09adf67d9315f114afb7c9ac8807f3cd32ea6425927faab834d8ecf8024c0b4d569a3e6b44cf659087dfae3b5f21a824b40db6185020f1e51c6f9077c73b85a8afff6688602122d07a5d46a3c24a1d47d86d9227bd33335f00b5a01b251bc5d3d514db63ada79646225f0c682cc253e03f7a1867d3c390ee122e60de2331a59f3aa7d3f0524590b951ac2e0b71bfe66b8527d66379b3ec1c9cbec8714b75aba55b1899500b56e68e97347c0965d79a8e4ed34c26d962af97b571d2f7d10d79f8b43c0d53c222543fb21e2714234e0ca99d164bb136c5e3187085be0ef67fde64fb28bed98372d16f277b6a5f9e45b4f642cb266d3e631c601ed404e332c3e711bb88e7548bb2e52df1f3ea1cd451ca27bd3f75465c0eae83d3b1f1a44d62e06a69fc3c880323295f4d14efe637b450d115698e3f49cfb45f5de49c620e086551b248c380937f7c6fe59c11a7e6260e9b549ae3e37b7ee3e70bf78635cf87e7ea8ce41de759ab35eb7a447bdd8ab43cb0ff01c60109457031a83c4cba817f2eb9ce134ca617d5a20a1a898e3736fcecd9f6409dfd59a38ea2b14303a9ce6316a231c137ccaec1d8977af7683fca4f0af9d6c5eb49011968d692b655515a26a46fb23ce73b1aaac987f9ee332605d07070edc6e2f9e6916e4d33d5b41256d3e831ba9a78b5b1f6da87b540972986e6df56eb2473f8526704aeaf6a13eeaa2b10c6650d5907acb6c0bbabadc1892d9634cd343e181ad4095f3b0ca4a217d3a962e0be8e5475808664c80093d63f431275b7dced6d79607172a98f3aff11264412ec963bbc0856759fdeb238bda0c249ce0c6e0addff7fbeffefffc83c9ea9d0fa35beb894f2373aa04aa7a71a84dc0d98b8d65940425c56044923154c3487644ba2084d2671413527b087a337167fc87ed92ba211997e677444b0c73dd2d4e3989431f4408f098f8d3833fc9839d54583c0481d0a19165ef0daf514c4a8d3bd44e52df6a1d19663c6dd05d2d3444c528fb3374a49ac686abc3064ba1c339c6927d254f11e08e51ac97b2f0795a4eed0486a50db1e9068e15838aa6b77f8a66604dbea434e264a5308bbc98d9b0524add15aa976e6e9c203a766af6a2aad8d321c620d4ab58781e50e5763c080a955130f7972fe41fa85fd4e8f7cb4b64a0a0fb26d1edd99ab7569e77bdfd7ae6dbe54da1e0b67cf679c55f99e560beacd8a674f07bbe5360c9e9ff6525b10da0c5d5a058caf188c2125519de2a13c14ab201c1685abca7434812d1485360a62dbf90f9c2d172284819315531d8842e938e2a83b64240e295280abefe2ed2fce14fbda619804a7feb9b61801c2d7002da7ff60bd85761c99dcea21cdb131a789a8052c203f6b4eae1f5008fa38810261748b15d287a714ad574e14341c3915487085e966712438dcd2747d401536ea944097cc87a3a3bc7ef9a2ca553343f9363ef8da14ca0e203516d61ee6c06433b6f4994cad916c150fa065a6bfcb35f96844f89692c39cadee189ecacfcd1e193185ee224618a1e6ffe0d926d3528b782c145991a05e6348c0d1247bf82a8a94a624ebe4e6086fccb2cc053012470cb7c244a48f8d1ae466fafb9783f42d0a0c730925dd60c8e794e83e8d4a67e555474e700e5b33be912c86626fcb75f34626c8ae5a1e888c2f11be70104bfb2fc9aaccaccc57c8f577c815fed131a24f901bd2f886756c6d4c0cebad7304ff097701396c96312cc73a9bd2acbb9446da7a73364462be210521c14dfb6cf609c3b7bfd86c8f7f8bfa5fd51c14b1e808cb67fd52dd8c1bd7b64cab72db5848ee65649e636b950b5a91b0e9ed8399a6e0ae79f8d1ae390123a20f40ae4e7cbe6058e5107917ddcef696f6e2f28de8f713f155e9fb84d130618132e30bf70e7353992c14423cfb5d51005f98fceb813c086451432fe98bd995f44fbf2b1f184fe7860140e951b69733767518b8942dd1e64ea31d47d8c7c128a6968a749593ae7dbcf5a4cda77c0d7ef86a9df08fc15d198ee22e7e201f114456f08a481034f660a83a6c03a36fa027b2e80e98ad1f1173e5f58e5f931e377a43c419156c56085a2e50d164ed59103b788ff3da2b9d15454ef23635a0fc625bfb2d38629caf3f642388e45d804e28251a7c5672d99265ee4220f06bf8b6d0755066505d7f04fd2cf25233548634d6d0ca0d793572b7ac922f19347be3eaa740daaf474a44791873f78ff06fd4bc16b2efae7a924b3bafb6e5d97e94233f2015dd9fc18743235f989977677b7ce78b32f6f5224eb54c4f0ae39ecfff28efbff10caf80e928d42b58dc88dd31c05abf4d3574ef008afbcaa01ae5ec4f74cfe6bb6716ef99d77ba25704f2ade2366d567f72b220deb8bcfeec24fbfaf567ce6e33a143e924be18fdae94d4deac9aaf5faac9e462660094fedc6f50155207f6af3883a5ca20a36b2656713df7823453bd207672a6a8fc4c25f43660cca412b5baa399feac0dfd55f7037fcbe9448de74a2de1b6d984bffbc154df830a3bac1dfd21bf9f752a569e87a4b99ee6fcaaa3ab001145194347b45578aaf2077471e69aafa45ba9bb53102ea8ef2461d6134c62b63b04e4c0e9f924fd33bdafa5b06cf9318b32c287fbe8139e3c90a44ddb41e8150c33d0b5bd635f6f1c72982cd9d793d385f96271ff2772ac480c6272e1311bac5ab8b872ee943cc384e4a18950c749e25419975a50e0751ad8a719bcfd4051125485f284064e2adcb96a5d52d9963286a0aa9642f16b1806f68b5dbb883448a0f822b24a4cef29914ae54b63be387946054de885d1a70525358e42b3e725fd503d731b34696d2a4710588e12024deb1dad1548047b7c46584a124a889bd4f1ae71e647cd74df5c89c3eceb31b7cf3cc892522fad9e3995866f56d79b19d2add4206eef839b86a70782220a815a994e79ebd14b5f043bb57d24a70639f54815efef00976b62e7e48fa80980e9e833397a0b2a6dfcf2b36d704bea4655a63fae7678d4c8a0c2c90c2725566406cddd885531efc64efa726bd804af61d856847263cf53083637e77e7a72747ecab3e69402f1a3702c16bf9ce311973f29b8a285f1a6668532570a7c966dc7b598e27c2c063eabc9471dda05b4f6861ad22b731233a6f67290ab686fc4d4cc2cce585b32e05f47c43c360d2197b709833773cb06fa41377b38cb32cee46655dbaa4e28f955289d73262bb1468dac7125f02ba3fb4a8a0ebb846a29292571c021d146fa08059af7ab64db0878fabf45023cc892b2ba1f1b750df8544b4c2b07df6ad46a894c9cd9f8874df14c8a68e9c0848923ae851afc1e1d680d9286fc1e93d6091a22cb9e94b84049af8ac97778b2d919c5a657790fe94efc618203819313239745fbc64b50825e653074b287d007fc7fabd76825a6e657861446f49601b86da4a8c58bc0b62db440228060cce5c89d3f82e9b3459d9f392c2a13d47131eca54135e46a959ad403bfc55c0e7c8501d2878be286bf0047033f0a56e49e3cc5f54497cec10d80cee1e3c892395d213c6cffb15d90afd4f0cf18fb4a8bc2fcb47b42f38f76fec1706659c579a415722b72615e18cf295409a129ac9ea0d38e91eb40a0b7cc39e6ee30cf16790ac3d4d0488a74091fda56c79af1eb0b8a46ed1a58bb34cc7508fb11488070381ac8654508b9e1c7cd60cff3da0848c77bae729c944d230d36e06b3d3bf39f8f66f05cd69333faeee234053aee68c7e145a31fd19f078ea9666baf02605a25cca58084ea1b4f8fcd52acddde0bc8addd7d6cc04f1b127bb2de3dab44141aa83c4f82dc21d68b3169f35694b0998c1a4244a5c0ded9f62ad4ba42d3eb9120016c1a877b13d97fd2e7fa950c39671b438f3e86cce6d01e331e32fe87b44fab247fea277b463b27e3923ea950849f0e41a7581a37407fc0aedb78c9c780c3a07311d1e77ed7619fe0c4a73eb4dce3e183b0201a502685b4e4bcd91bb07dc1767b75ca262b537691c16a13b072329fb20446d5b28a5e67227ae3a025634923db9c4263eeef0c616e3cfe6a467f872184d24e747fbc7c01c34862d10bae1896d0e300aa5b58d2d779a1663e495cb8202e8d2aecc079ef537bc4affb16f49efcea6600dcd7154f675ff5c7411489c4224487f8b5fb2bcc916492424a5560347121b3ef30f6033d503e5bd454f7b5e30cc544700a4e08042ff56c16e300aeeb5986caf337e38e4be660efb4182a6bc40f76d6931dc754420e18bbee7a6d785ffd614b17e52b32773b979657e8e07d89bf0cfda881f067441ec3753800fe468079e918e806979b629a67dbb80b6d19c1111be6d20961f33b6b8dc145dc4b40e0ca3d48d600de4a5f01849b11b4d158a4f0c99665e21cc43c40fecd3de3fee8b7c7a5337713d5038da809268e851d211193ccd0132f2cc07aaacea25523859e036c4eefcf97f39205d2c1b80995c15163c25b81fbd8f48c2532e17ae8526c5d997d2dd7a7b1578ea5499b9d8ba5b7dc1e613449b00bb08d54b18d8cc0fd2a0bd79b659b9bb322766650b2cd3c6fb15f0eda11e9e60c710fc068443ea6a3f04b71b9dfb90136957370ce29b3ddee13ee3c51ed2dce23906f51017b5fba93ab84352e87ac8f9fb42e911e0aa9ac842b733cba6f8641ef6267f660aefe98730dab0964d5145857a966e0e0729ddcd8b7c62648906f90f5f1a8bf850b34a4162b07ab8b7f64d8503198325e24806182c6596fbe3d0356aeda7184fee26397672093458dd0de8f29d03eb0bc6232476944d80d8b8347e3c2dac541b6ffe58258fc033bfd55868ecd68c1bd68c98ec3c07caeb28eb3b35687c55810cef5d47f9b448cbda96f17e657c04c4a069022020c8d2f6f342d2673a055d66bbcfc7c211c829a3ae01960edf75909f9c8a90000e10cb61b76bfcbf2ad4f13b8a03d00909893b020bea375e2e10f4e2597c502f0602eb387723e1d180f72a80a592542eec97cd6430680a46dbd116fe1ad2e590487f417d05fab0a81113c3c5208b257dca6ab90a838d3a04c1ecbf46cb956f5c5e601e2b5fe63b85308874bf80f179b1ce75c2a2cb0734e868d970f3b124393a1b3dd850d0de3496ae5bdec142e92dead53c9d8333d125e91c389729ae0cad4991435f66600075a49ae8c1ea79ab161cdd8af666c8c29d9cbb0f59a98caf384391f649db0a501a26da212567b4df3d80c14072820cb2a631d419e206a05aad0f81783c818260982802329eb444d930749b740a619d5875d4cf3846fa681f3f07513fa161a00c94001582ed08e8d359ad9856af649fd9f25089ebc9c88bd0844f2a4eede79474781ce4450c82411486e8a67fdd11744ccf02dde51dc3e9df92171319af25b2524da5202527d33429514887d9c801ce367976525215635c0cb1c874a7448b74b43449e5e33222edaacc4ff50ddc9b0525cb695987b61a36d6519cda53cd94ef40a09b105083419d71c2d657a86f7f31a470a09734dc1933c2dcb818656791f9d2dd3fe977de1d117893c3061e841734b1c8400325ec08b2a3a02331e6323699a1fe83af84d7d9386cccde48404c015720d00a289a50d0995eb88253479929167bfbd67444b2b13e7fa9f13747c377e67e86631687033e83761a46aeaa707c1136151bb8cfadc45cca111cd81d4dfe1017c7f5b5744ada666b8c30ca5c0240e2315f9085c5ca41ebc769d5a698e9a6859787c2c330314d805b99cdf557a6f4b3eb1c344d7c4c1afa3d9dae727cab05d351ddd0fa24c80ab1dcc31f6d2061aad5538adb3ff77bf9e97b2aa3832024c289cff749993c29ff414f112d108b1a3e2e50fb541e91fbe51bbc192d8f37ae9965d3f4c6b66372570bd294ae317da876eb56e8d536560a2a55a57b2aff10b0b74145ab26cff5c4b63c003ff99c44530c833c6d454abcb7d4926b19be89a0ceada16c83587d595a3e143768d81c529c611774be0400308835ae471e1464a30f6634e0ee36d6407c3de8233956d23d39ae554e487ca6d0433a1e6d4f88e4640c24a0b1bc0673caf18fe370c26f0295280c5e4123455bbd91b2cba8839e28615f449036420fa880201741417405a316480a6c4f5b120bd72db7f1c668ff222e3aa256ce39d137d9397007c9038e72b886820857e6f9e944a04181328894385d1b968987469bd76d3ebc1713007adb3ae936b8c9c4e1021a6ff66d6b1ecad30452269402c169280e68a13473aaedf7c2b4c0e39866bf45847e4e9e815d20a589130aa8691b4bee7ec0d0913668fd51ca81e185dc0881ee0200ba3af4b8e3c2c1b3111796f2af286439756d1fc4b827041563ea917f12b1326dc4ba1415670d72621b9ba1184cecf254bb78d209d339921e3296a855fd45d93634b3878cc51bd4a4842947db2ea2fb1812feefacbafc04b68a9107c6ce3af538b87559fd2310671f7cf00036ec9d161e566c974b5d8b71124018368037e18ee2298cd42b7b1fa270b701d7445065e54bf8aa80660e4b3f02be6f5c2eec9311fb01d0952de884e198b5b4dd88fa9996e104efb708db3467cdd9045cd60ad094aa2af56fdb358bae337a56dfaad4f4ab9e830db9a31c94dae4ec268c91644b0466ab3fc0d652375275a5d22003283c9fddaa02f95d53781b4b4145c1a642325548bfbe706e2362826dc37066233f9a0c4079bf399821809a66a29eb45adaea068e0e3a13d68de3fb3f40e8f7d9094dc541608334be9e320db39d823e0ac2d59d97dfc75459d69dfa76665cb126d695fbd1862eb6d87b5c4bba4900ab0972058d9c738f7dce54e5a23af896b6996886056fa8f9dba502da1a2f0516ac17adf8a35f7f73e0ec478808a80f38da300fa01ee66168a2690f618cf44daccb00d3a749d7c237863232e4de1c859dccc2fdc55bfa138392d84d6d17b4d3932dd4496c4dbb2d67ed4b13e37c0989f1384d95287ed8256510ce8a91b9216f3539f1d3a813d35321f9f98cf202074bde8815bbd2f4ce0a2a1612ee0962eb20a25120dbd02699c461af0aa0d93f8d248b99d444ea9eb471cf272e08448e2547f09ae9095eee387ba5db06bb27d0da6f756a35c393b84284a69ff3316298797b5071dacdbb3ec9c52c8bc6154eeb278e3776887bcec0a2ba4c9efe05a94f37ef89931b1f2623b321ef8aad21c5c157afd873833d3e4e7e379dbca470f2323c9a9993314268b6e6fc1d1359c0f0bd0f41c6df2e1750c72d1ebb9cada386d78c528923e70e96e2123cd3d44696c446e5405bd8492edad9cad97a93e26d2ea4859e35d932a799c4dc81474ab6837215dd8e39556509aea78b131938e01dcba1ae1024af2dbf5b708e04c01dfcb6e829325ba02fd26af4929ed30bb63f03cf3433d99f8348d77364684c9bdb52006d87ec08510a10d1bc704bd58371c362e26f4f0763caecefaf97a104023645ddcf17be89318947d94f111d255c1d970a6b0ce1ce803218e15fdc7a84e6cfdb0729cb5048940f658857abb8abf70c631b56ac4867d106572efe443a87446c3e811e786c2cd69ef9bb7ba4f952c1e325db2140cc8420d5e7e227d6387db5f7d05c1775c7a16350c7a55e62fa8b6cc3eace5cb86a9a110cd9e93dc2b825645f11f3013da6c48dcb6246fac2f05a9c3fec5cdfc013d9dc0c1591f37fc3a5415a9df1eb1b13fccab8da7be3020eaf70eac2104aa8737b65620d0b29a0eaa896de0077c00dbcc5d71035e5f58890f7e47cec10bc1a3d806a385c9577a4c1a024f715255ce3c33ef16f9c95255e9cdad1e9e5ab775e93b2e96b6cde10f7ba505636abfb55ed2e4dedde4de52a62465090b4b0c1c0b2cd6c8127a668daca267d6c8427a665121875031e6799565956515e6d957ab714534ae56abd56a5c093dafc615d2f3ea8a1c7265ccf337fb665fcfbe0fca377e4efcf835f1e3f77ddff8093d7fe357f4fc7d43c821438c795675914ad545aaa767576141a41a55a36a54a954aa5125f4ac1a5545cfaa5185f4acea27a97e92f21f5362be71e09be963ea8d6f1c52619e3d8545512a688c1f5333fc984aa566a9a667979c1a5348cf29382e1c37c88fd7a9e979bcf71e753fdef1d6f0e39db31fefbd77bc42cf77bc45cf77bc48cf97061e0d7efae88d79f69c9ebda767f762a3377aa3e7799ed7e48d9ed0b3377a45cfdee8213d7bb22cba2cba1f3f764a9d52a7d4755dbab11bbbb1ebba6eec849ebbb12b7aeec60ee9b99331158ea970ad1fb923ee883be2b8258ee3388ee3f80714ff80f2f1238a0845842242a1ca7846a15028140a85871c82c79867cb316badc562f6eca31ded68476bad1dadd0b31d6dd1b31d2dd2b365430e6163ccf3d6b4356d738bad7edcc68d881fb771dbb66ddc849eb7712b7adec60de9799375a1cd2eb4991fb5256d495bd29e9e5de3e179d482344dd3b4d9b31272c85462cc57a36a548d6aad354b96eae5d92b0cfec71a34d6a35a8d9ac821788c79a6629ebd6f9e9e7ea1609e9d76e19d09856fa64f28d4cbb3d323fb237df2231de948472af44c475af44c478af44c65259043e018f3dc6296347bc7c6263aeaee9e75d373bff1ecb287282087e021c718f3ec238b51d2374e4f8f3306470e993e4e3c9e736693530eab7ee4a06797231379b1223b33318f2cf4cc6cf4ec9259f62c4672f1f2ecb30bef94c037b3fe646f221150e510dea1e238bf807996e314527a1e2711b33379fa0c3008c61a2f3b357c333d29c929871d0576cbce3cdc2a6266cc9702e2e31ace27384851675d18954b54ecf8f6d5857517c974860c5d545f3f0bfed854c8f87629e6db551f77c7f4a2c78f354d94b1650ad19436be7aeac2aa1760072e8eeab023890674c888c186195464b9c1151ea68f7289916620ea544fbea9e550c4e2741aeb1da7433d9d12c56262133d7c3b6dbf17d656c691348752aa52eaddccc1ebdbb90b6b0b64b446142b424568d1032970c490411b4f64e182cb1a534c99415926b0fc906fdfd490b3e9e5478f8c97a38d90af9ec6d732beba9562e5ab7f4fbe7acba8065fddad7cf517ebc8a425eef8ea5ace9312409421c6072218cc40cca1a20589389294b023688b58fd655fa7dd51e89d2e7249af7c751ed699556881c4d2b35305f9a97a98909fbaa8777a86b2e21bbb975867ec2c3f5d52b1520470333ce518107d6bf13d03e6d30193a4041d01f8eac44fad26a6b7b6f85a417c2313fdf4151d5fec03a2c5c72222cb4f670df1714d7c507e3ac7c4f425bdd3333a6b90f8250e9dea25a0505d857076c5277fecd94f303deb2699301f1029be913e90283e192abe91becc956fb4cf32b1a743e63ca2a993369d8c0d5e325f64c054a7355a1f1a32b396619a6d9ecc1994676c6a932763c6a7d45a72a63a43793269709e0c0de8f892a941e779326a7ce34b469d3e2d427999b4bb9b524abbbbf98610eef8661e12fcb142c9ae55ab55ab95524a65efccaf8b007fa0e6777733336b61598437a06ddba62829d257a197e3cd4bb72af44e8542deb0bbb75a150a0148286472a70e852d01fc58a1f896aad2f374ebb5d5a24eb3eef0d4c95a16a953efd4a1a64eecdcd505981494ffb4880a99c4205ad4cd4f2aea939f5e87a6af889e6bd17335ea9bbad5d033513bd8d86c9962b3856e7982864eda8c75f6d3e5174d2fb77881839cbddcf28593ef2159a4651267fa6ca4be71e3f357ef7061f0b0a34b4c9c9ed33bd36905292dc290c1504c9c2c9b44bdd3607809834b4c9c3e8b44c6238b0c3c724c88b514049898389d897869e9a98967b3b9c44ed3a7d13c9a8df4d37da62b91620f75c77a787a678a529c8cc4483d14fb39a367f448d9b66d613841a30739a86802a3a8034b7018518710321c7d09238445181c7e23e137b6f2dbb659590f0a2e084389f61ed6ab63fa9c93e7cc82075f9ec3f1cbd1828a75a87bd2ee66669e73cea0eec2f18daf6f179243c408eb4806b2c637db1bcacba42b825ec8cb242d63bced9d1ff2a6bd4575f8c657f5f69cded1c4eea42baabc4cba228927c0cb242d48fc8d2cc29b46c314a9c89b9f196b5108bffac5aff6975b828a5e7289b9b174d2a64bb9e1752ecfe3fc35e45f0d11d6914959c4fcd6311e7e9cf96d5bb262891fdd8aa01f85fcf672f3b86e765dd779ce71389c77e0880259c7de756ebf43f5300a64cf79d7c92bfbcee7d07717e6b2efd1a5ef9cef09b3167de75d73cfee6e2f35b6e0a5c4e9bc03d9b99612472eb5d14922946bd5088119a99ab6e4ab1643cd5038859c03270e3afc16f6f0f08b4e91fa1c6a0d519f4114e542b9fccf013e3f19508175504e1d042db0ce8b097398a795521ae65895fb135ff51a7e51ab39aaa26a481d067e3047fbb1c4277f7cbd84b04ef5be30cd3b6dbb9badce30f0d7568d771a38bf360cf26a2919be2944c43a46aca34d1c86a250aa859e4dc1f11d7166749b4fff92b080f9cf59deb1c717769def0929964bda52f6ab70a4ff39ab8b9ede5141ce687c6c61e6592e5d8599cfe93d42e35f38b29c66e5dbaaba8ad5e51ba55bf7bcbd8705ce2f61dbbceb407f19d09f03fda584b12c4d38daef0bc7efef95f1a09f710ef3d3cba73c090b974fb92a094b95a771f66de33c568ac655e0a8f2158d6f2dfcc702c799135c98639ec6ddde08ccb07cd607b2c00aafcaeb45325de53420cb7bfa822dccb854a10929671628df7e2d7fe5fcd6811e38b27b2abf1eab8b6ff346e9f04956385a7a91a45ce55dca039fbecd5960a7f1917dc6db59bef90c288fb0d3841fe8bf5aad9c5ed88a05fadf0868ce7216e8358b15c63e16cb43c2aef2bed4bb1baa40d711957fae920947a09fcf9a301affc0912634414a9c957fded32bd0f31950c63fc9afd07584e52967adae5b4ff5b40a4c85a035410b333f27a7a66faa79ab3095cca10c2d73cbdbe2d37ef44711f18db24912cdf08b8675885f2c71b8d690490c221f31e744a307732aa16952e4e41ce1be91a01aa55650d0bee5e85bae6ada6b60fd0d64f9d2beb2e0bbdb3814ca8a9bca7df599826f9f1bc8ff84a6d918a594861b08bf7d4a9c1a7444efd03429bea9c9a8d391bc2c177cd3e5fc2c659d9902219f2f9d1ed51885e2a70ca7b387f468f6499f4e2bedd07e8dd5e1e455d3e5653d814d253be79ce11215d3c7acfa40e2e3576dd58c2f9e3961d37dcc3e2d9c204dc1a03d4d81f6348a39b99b525aab45a12eac88e3b8aef3bc7b2f2c75612a55f5efe9a998d6cab2eeebabb669329e36a3d1d0f4e8317bccccd0f8a0f1e1c3c78c0ccb878fd5e7c3870f958fd4f5e1c3c725b2613eae3ee79cb376adb5d63ab260aed17db3270e3a596b2e4c5e5bb5cd56d048e2709df7dd6b6994a72e2591c4a9201418f5ef9e40bf0e499c0ab6565e0559b5ba0f23beea3d6dbdd595b9deb7d519af823417d6d1781544caf2d47b5c5815f6e163faa829d711ad8663059eba27d3c39bf1e1d1dc164dabd59a9161b55aabafd56aa95aa9db6ab504d00227e3641c1b15c53c39668e67a7c4711cc7711cc709597ace3b97a9eb5dea3d2fa0e49438e75c72dec31c67250ee75ad882b4fc6213b49e85b6592a53ad27f70c4de0fd0ce7cca3ebae7cd3a5d7499b518bfd0401bfe65441e2ccea3dcc4f73693a13d36cd6349da6874a5f7c238779622789d3cdf41d018ef1ab9f7ea4422dd65efa468e7d07b19016eb2c9db659ef9b73935aadb5875f1c525f7a16a473385a203b1b899999970022bd72872c4816ee033a64a17a7b7d000b1d7afec3ca37c3d9431145d82fbe96384dbf289de48ebdfbe6643ea27d8756e23491ee580eada99a56d3f22df3a87201a71257af09afafecde160f9c4c77caacad32a52efce2b9545f7f3d50f6cde633655a64368cc9b40e9cb2a93497fa86697e91c9a6923699a64ce2b04c268bc8cd166166e76e59b1292559e400337e5114c7633b275cb7f39c7bce63ede7751c38679f5bea73d6ba9da7f3349c14de3580b5dfd76ab9bf667db389b9447efe9c499c2abee9e34c79292559446e495def761ccade7bef76af562fedcb778e909a338933c66cb68ab5a6484384d68b860650a6c9a997320a5235cdda78dda86dce4bc93e2fe99bced34ac9946c14df74bff24347a3f222e967a176d31e9d100daba8e3aa7032783016e9d9ef2ed64d4f86ae8f5d4a248e7419c6cb3166ce747d12473af7f75dadfaf65bc87957ea57754aed6982da5f8636397cd281beef11fe9102d49b606a890f1ce7dbefa6c7f665c71e338536415fcbbe93cd1cc74db9a55c4736259ab679fc6192d2d3c3c026a45e8654e2a0784ab03e099eb429ceca3741d79438d2e783cf73d2969a4c964f7a27dbe329ef96dfb4e8a48d521273ce2d9ca6ec223b9652fa09f3a5d7d2d56d64168bc564a03fbd04d691495a88f9afcb4f3eba62b6b1050e3f1af92995587eac33cff82965e842425ffa09f4e5943772b2ec9409fd1cce9e9a16cde88b6f1513431bc325073694e1c5a6f4ad6255cac6447dbaa262f1d91f5b461f855299f82c5105e3e38ade98c126c416c4b78adde063c140cb678fea179f6a6933a24b3e4a94a4f83167b6a5d336df643d286c2f9394a6fc98d332384f6572dbe2517afad6ca7ccedeba357adb7a2a9de8d38fda1cfe29336b9008e8991c325df61a4e5e021151110329b660c3488e178852481334107a7242062836489f45d38815860758987060861148375c88949863075a909881384ed91746981962d0658a229ac4712ebd74d93b38522819b1431833d47012c7c964c68f73c6f492cb906ea56cc91c89825441fa0c9245e84b67b28225d29548ef91fe92fec9a3582ce6830c23b3c1d63ed3721dbe7d7c0979225bbadb5520c2e35bbe41e3a30c1333e3bb470be1d0c3c70985e8db67910e627c1bb58fb34a4f2edf3dbb7cb7baa3a0e1db1b888f33ccf753146c7c07b1100e50be653b171df1cd49f0946faef2cd59be9d653fc4be99cbf7522c16cb411cb9cbb3976f9e1dc1c53783f96e7ae3dbe52387f97e3ac28777b9cb49605764fc2a88af562e5fadbad0e286176c29e30533f041bc63883150a0f1c315316ec0e35709f871e65d22fc58f3aea238dee5721901c4bb8c10e35dbec285b93c4890204182f8082e2c888be0c2563c355074f0351e820babd981e96b1de32be8d573869e7cb5d54bada18f12cc8f328c183f87261400f838a34ca321a739258c1fa7d25cb2a9d5e74cca575575595d4101f091711822e2810e9a1f394a902c21f8c85c58e9abf3520f4e7c652fac53e3d5398c007c643131a11ede70fdd83880f1631719b1ce07535e3e761524d994d68fcda599be7acf1acc8d8ffd463b7df57e4ae3477539d2218f22828f3409a3a31bbc1f6995a51fa9d257a74bac338257a75e84f848bf54a74d5dccfc48dff8e2c71afbea55a877ac58bde2f858a1107df55a04458daf3589af5ec85867005ebd72a9437cac5dbed6a5af54e6a882c41847c880431ae27581173dd400872e74a0e1460d3954b9c11c398c1147290b811b32440963071b8e98a3b214335419a3c505691051dbd0e109283448b1832e62d40d04ed50831769c0c08921b608838731547a108a8138cee95464c44c3c9364204501861051e430c306e2d84317788206279098ee88630a716ca256ea91c6be3a1502ea21091e7020068f1e28d183385224b9032a7470c50a14258c8881385219d05118724859830b29d4c8421ca9d3521c413cd8c11b76e0e08a38d2a7af3970238c39644046102e88a2fed0033ad4f8d284143371ac475f87f81007c1850d19c00006308001f8072e6c00aef3385082781cf7c085e108f1c18e17e21db83021238c30c20823b80a173682085c5e04e7c08589e0eeeebe810bf31b1f7cf81bd7c085ddd823f230c3873bb22c71460f4000a18b07610a182090e89294850e5d8820f8e0e441f00c5c1808af1eb4fccb3170612ffbc107defac0ed071ff8052eec0301f420f402700b5c98006a6a6a6a6a5cc985d524e043e0e1ca87e015b8b01082040912248827b9b0201e80a13c3e004e810b0b80cb5b2eb72e31eff29f0b73b9cd196fe313b8301b000c0df10070095c1800402b5e76f0860e6338c1c3e32e40423c6909c389063be89285b81ae2e1579ec285adc21ddaf8d07d2e2cb4e083533ce811b830100217562b93144b7ffddeeb364258a7c6afe754c1e16f8f2b837b3d03f7837befbd2ebf17847befbd41fc5ebff7de7befbd55d67877f7075c98db2041820409e20eb8b020205439e241702417068275b95c2e9737e0c25c38ff811435f80ffcc8857db05aad56abd56ab55a390a17b60ae36b3c8faff1132eac863ec183af6e02ecc26e14443378cddd868a9a13e189e288d78cc8e095644082594285b682122589d7a208e335194d89755caef9eca27979edcb6b5d5431c30f3898a28e2e5088e3f7151883a8075a4011a58c2822d00f76d45144105958a165898c20bac883083ce4f0421ca52c36915c51747997cbe5f2122eccb5f2d6caed2a0a297ee50cb8b0d5bdf7de7befbd2e5ed8ed5a5b16c50f51f4f0d5bfe7c2b4993f10286cf0407c011706e4c78f1f3f7e78ce8f1f4ec6ff802206afe5bce60ab830ed6b59288af8ea09b8b0ea462eec074fcb0548df72045c58ebc99467b1582cb761790ecb054a3c8bc5056be52c96c662b19c25c293284bdcc1041e36c8c1fad2c41552f030431d5a583b3fce3c510ae1890e5ef32217a6b5be5fadbcb572bb7ab2e5577e800b5b39162a2192c88f44be56b74e78f0d5bf2c5fbde5af1ca1a7af4eac7c759e0bab3c405894e0828d2b3ec4918497abf4370f21ee10a3498d1cd40146bc4ea0f8eb3b17765de7c2583e74a8e27db8012ecc879df1d6cccccc8ce7ccb89df97e4687267e26873b5e23c06b4ee4c2b45a6bad5e800bab4e800b9be1e95164c3f770122eac879d79992d5ec69d753497711b1919cfe19d392463833cac783247173680618628e3b2773a2c45fc00851b7128f184387e2fe3728e97f1193182fc288408183f1ab9f951728982c557af51be0c6185081b8830e3abe710f1f4d57b8ae0e2abe75c5855e21f6d5e93a9f150949658434a93961b3c11a55cd2e235f72baf794f14a4d79460d9c1172ba0d0428a2aa216e5e8351f7261da6c001726f3e569888c781ac7b9301a207c784ab3b84edd46480e0522cbd314e54257940661c45319ca4994e554c6c719c588a887a773ca532c7e9c4a5d28651ad3f082081db64822cb90486de0822cb228a38c2494a8439452098916317964b121cc0c35883518a28a1ad040071959d0a52eb63851258934ae884e7ed072061a72d810451c27521056fccc183fe3422e6cc656973227082a46b8b0fac5cb0011c6cbb808172663991421c41077609932a3228edfb3585b9ee5b677540047163c7061a4a58e2e224b06cff29b0b63f9ca251047fcca5f17b6b29a46b51fa2780d88d80f41bce602b830ed066f3c619aa531e6872571fcfe5ed95fb7bd83c21bb228716061a20739c41f74f8eb2178002e8cce64ff5521c37f6e73619f9dc2caa752de4ab9bf52292a823e2593e22b52ae2403a99a7299a2c2e953ab94969498fa144da5dca652a1103c6043d6c4a4c4450a074e0ce1c6952d339821c58225de888244110e539018810675208da13247186988e38c7dcaa9cb209e52ea1f15b2a70e800ba3ad2d92063fd6dc4cd1069294af5358f1d5c30babfe99f19a5bcdc10bd300f0778a1efeba5fd80d726129a5575dd1e2550ec285a9acd4e03dcf5bacc356bc277afe1292c333e63d239ee719b89ee74baebc17e63d973e8758074aca9b45efc9d1431732a41031c35312a2e7b277209003142a519498b2040cc4f193792e47f9f4de151c3ee5947544f8947f7061a924ccb8438b1b88a484e1248e5f96a73eba78a2d4f404a6072d449a031d4fbde6c2e88fbf6dfc75d7855d357010031de461061e5bc4f1c322812ba06003065e3c216208b1ba0ca2ca5707626305cb6bfee3c2b40bf38c06f09d4b2bdfb98f0bebb82a9e737f719ec3398f104c3c67e4392a5e49ef5891e3b8de914e6ec8e0258c114a349821722f9081164e3838030733c4f17b4e45c30c585cd0841c46e4605af14116c711b320c488a3547ac10e9e4b128206013cb5ded799af2fa0f282275f9de6c26aebc238a78775acc1f0abbd4361e479ebd36edee058c31678befad64dfcdac29e61d1851cd2c59867d6126b8915e6d9595b9c58236b64cda71f592c56bcc43a3e5e3a0ad4bef08d06002cdf883262424de19d8ed2441b1c9fd744543e5e629d564f4e49efc8aad1496f9fdddd526a3274fff6d955869fa46de58ddddaca9774465dd1ddddd429a5a82bbe294fd8be86b4fa4829eac5af2913e59b462f6537734bd4a2388b02c3f518a72d16ce39eb0c9db8977076055fa5bc69261dca231cc771af9695d694389bf36f9443592bdf74dea46cda541a061f534ab7ccc9cd1a8aeb505c0e3ad5137b3b594b2b7de25826a8f7c8276fa833656debbaae7bb5aeb4b4704a1ceaf43b0e75e56ba0ee18134d5437e6be2881a5ac996a9b95b2991b4c4dfc62ea44deec2049e056a23137a40ea54189893ab1e3353ae40e17e386b41e703138b89885028401362774076d0cf5d80b51a1ded19eeaa73df58e14354b63aca339f18bd21da813b9f3b2395ec56938352759444e21fb556928bcd34a7dc3acd3ce94c46c4a5315a72c4f5c688ce669ac773427271ad39c74f88c68a829282e7cc3dec50bd3971998a6379cc23c694e34d6214a56c4375d739238ec9a93e6c419c59e5deb36c22f1418dfc8f3cc4facd3c3a2dc062b723e7e2df4525b7a4643bed847fa9a52dfb08ec66487ec319204764da93526d669270d0da9d374c8576b4a6d8713794383dcd1663d9dfe86e719bfd87d347dd383826c7051ce028c34e8d96dc8a1361860db829063e4b6c3b3f35684cccab89f7a6713ea1bb6587ce316b349edc43a5b6cbbf68acf3bcde3c01e237738676f3be4ce4b4a0bb5a56a28250e779168db73a1bd52e414df74eeed22d1bc6bd51a07de69590b35922c525d09ca5214a6247a36a59baab413cdb753ef6cb12d1633fa729c90f50eca0bdfb07f5102b3f4c6b3a3c23ca3c43cfbd28f5bec699e86a8257eb1cb08f14d1fb798564d9892531acf98c61d139db41999bd98f6d25f124894fefa6e9289f43debb6d45d47aab373371fd9a64b7d44c72f6dc62f8b6f7a0a28096cae3541b96db2390a74fd585408ba7eb42de47003b56ff27cd2bde0c5464d06ca9771a368da2afb8975a6d621955fdfc824165cd2d2476b7d7cfd289d7684d87438d6fc6859a9e6391c679ec39ad148170af9d148121e094c9106d598312e28217af6d792126da86fd89b5c3fd7554c0d2402894992489fdbb66f3cefbc50fea8dc0312ab2ab47da3725528fba676d3d84ccfde299a0a6f088ed5c8e989e83dd0d5d5a27e72fda0dc732f64022476a0eb876342650e2452e750a0ed1b39957a877ebdd4fabcd54a4aadd43b525c629a353df71310213e1f5b63ce038daf3f7ce32c42c2c587a58781bfad4fb7a18b7fd4f1793fc91bf6229659f04089affdc4fdbdd464a93022d8e0535df00ce16592972cdf6111c6c6cc5aa55c020a0ef0b28690974963e4f0dfcba4a528c6906748b7e10ba9bd944e9eb8c1bbe0461e57ee7882450e99f8b14b76cbde6ac1280f222f578801134664671faa68e3790e26ce88411173cc40658cd8012aca98010b2743d431061b930a76d9066563f467369e5dd681652604135c7460c41d225014789cb114c41330f0e248649e37e8a40d9897eef38394a32e5ae8c052244a252fb6b0516486165f441f3a7cd3a5b561362103a22744066dbe10996886318610196c37da106717647891f10e557a30db41638a8c090f972d48620cd4872251b201464f93db8e96219e440665941c266fd5d868f9645217a51f5b5d907eb4ad1ab771f9d898d839c781414256ecba90730ba2e49202bcbc48e40d5e8634167349ab7175a22bd2de3e4a29a56c29a5949dd4c5e847dba5e8d9bd99bfe21b6dd20c84969266c04441212ba2a8942eaf09d2694be9322f650fcf58cc8668c8f66ddb361f79369f5bc8dbb655efe131c2d3ce63a447ab3c467a3acce1d7ab7946c9c4d453023b537777f3f058cb9344c7163c3c4474d2665439bdd63b4da5504b1f5711c9223aaf6992e8b539a4390acc37b7effbe197bd301f353e5904e5089073c898d343ce9bd44b9773c7e78df62c1f2ec52efcf19c498f954b911f056e2f2fcce5c3ead13913ea9e77613b8b03e756c2ca87cbba12d1035d36f441a187bd636fde0dfc9cfcbcae1452a99feb2e1bba52f09ce53f9eb3422654ac33f97a844c54ce7220be301790ccb72e0129f6f00d64272faf4f0fef61aec766391fcf2520452f6c42c57a13ea9e27e92f6ef8304664854dda57dec32cffa1ce0a5d9cb35c8a3fed2e2e74a5e0c3599ec487b3c21ede44c67d78921e4ee3346113d606724ebe6381ae175c40327f73094891c65f8071ee43e32e1f197749408a32ee4ac27296a368409fbed19c8ba239ca27c887f7706d3ee35294015d5be84a41c6693c09cb67c22633ce729ab049131a977129f6f03143c39291a2dbf0a77de51bb8fda6b992bed13e507b8af900f1bd9c5f9aa3dc950275cf7fa87ba1cbba147fda5d36b4e207e486cf7f744dde686eb5fb528b198e5e2e7d10f5769507a4f17da3724e37f56ee7f1d79cbbbb793630678244fce511159e6e0ee610a1e18b5f348d6f944b3e3f4a5887fa53676fa23e279d838c9c0ffa3997be71caa64c16e19fbe42ef7010174c2889d397740b2e7609ae80caf2c9b98294692fb318e3f41285deb152494909cbc71f64c302c4ea4253dcdc670d99006dcee216326182049437626275795d3e938b4bfa1251035d32f459a16fa68b4080f8a612bfa6f3745415dff89ab269ce170cbc23c5e9720649313a9a0d7848038e974ee58e64c30a931359c0008c34964429a55b2ba59d338dc9d9b7988d611299a0a8cc301363904954dc1007932d0d31869926c40e251b4066b1c31046a22463880a9128c1402ae3489c3f5c4942486430a78ad86955d350a9114896c18617422283354fc41878ce26727cfcdea3d5ebba177cf34e05a82d94e205a5b8791e48c11b8627583d757a8f7cae14527e5d49ea7ab6fb822e27765d6f52f40ae07920951e433d908a1562853cbb10b70980666fe77920159ac3f1e64417af5e277aa014eb064ef9aa2da8ff42a7b5cf23a86fb17a5d05bd505bfd09fec8eadb95c275cf955caf43510f37d07e419ecbbee13497a294246c6ea9143b508a1c38b6340ad68ba4006fbd93b36f0a15a680bf9b56d3b46eb69ad69de54dabb4db515cc1c8e9ae9d5f546cf085cdda90c5972b05945b57820a95c810c47c2ec827d9f4402290c81b18b4f90706590f81079f0bf2498a2e4960a7eee2064a1149017eebdf5c7377e67addab0d3eb4369fcaf4f6cef28b8ef1516b9b6ad5c371066924b0f7f3452253696b9aadd6da89804ed13005ac75876d0695ed087dcebbea94e3252813d53bda8552de4c7991a8e02beaa9cf7bc43e0bdd759b7ca0ef340fd5a1dc8539a524e16b3701689322ca5e2432f7013d664c5fd4131f7d2a1ddd9cbd631433739d953c2d4dd52f3ad93b5428edd843815434a7e00b47e894af5ad9677d067fc2296f6af5c9a8672542545ce079165e0f24a2da5d9863506010ca6da0e202cfc3d024681b3bd0f3fc31068a44e637efb8a66c9802fecd8fd0586ca39452a93e9b680926ba4a59a7253dc25e9d851a7ea1a5b2887c0ea293369a77ec017d57bd6a43df2692167c3d429f05cddb3bcd3b4243efa29e609d6d86de453df1f1576bbda2bc0acab7d66a9ab78ff3042a535c98d75e8347682cf6ed1ddafe17e65b5984f603e698a7a1f6c2fc517b764d16a1ceae55af53c0b3998655e2b0cb0babcf5e99983e6ef8d0f8e93edd882c227f7a77f3f8491e5b764eef243aa2b8b70c949b522a2913bfa47603173950e3d9bb10ee6867a425fc9a9399999596f06b6a9aa669cc9aac21bd70f4ab036742ea7d352e81acf1796fd1499b71356533da8306937b01d67e5fabd592d254014d04a09981d604e0fc9cf14be60655f3544544fe334a49d136af8c4f71f108f83bab5ac7b9cba61c39e4ab775d05877cd75e05074029ce2f49408a96ca2aada010ad82236c4b22bc0672e12eec85bf282da98a8a023dbc89cd6c45e7b2486db572523e0620e5a30086bc747fdd489dcea5835201a930086539b7c1451ab63163c688381201ecb60367535a7973964336ef40185a3f254e0bc6fa9b6bf7487d1e798ce1b9db3a945ac7a8efa62095435a30d623f5e9c8630cd784798fb80e9851b02e6d46d5515354dde4e7f304f9766ee07c19a36f25bf14932c42e4f9c6351de85f90adbe6109c62fd77d28518252fcbe237a65f294dc49a9923547eea424cf21626338fd94524a29c1f87cc3cb49a9ec9c74ce5a86a4298f436d9e026ddf80afbe9928295a1028c6cd6885f9b43400cfd4b8b398edb2889c911247fbba85339c6fad3c7bc733d5d99b793a10193e1a4aef1ca813797a3da52e7b897ee92d253ba5ddb576d76e0e5f128bac543addd13e56ec1bbf9916acbae8f730289f3ee9ce45523695524a29e5cc1a45f2d2e06bbd6c88788df49f955d8235778d6f6c79ab465a19a2f0f8a48f9693503f5f4e39795afff8d5b261ab4af985f44fa29e8b7dd27b88ae932830260a8c6fac432f5da2642ffd456bdca6865fb5ce3adde79c944e3aa90d8335fcaaf9295bd4076ee89b546b85a928dffc6652437cd37b58da3a660d7ed8f1bdbe5d4e418104216d886c39dbb66d1ba5b3e9a473ced934698e359a80e4f0b553275202bf78db362943e1fb50e057093d25a0407b4a40812767a602085668a147deb07ffd7d3bf896b25a2402c57a7a478a39763a6ddfb6cdaaf059cf7ed47adf14e217cfd884c23a44338a534a2955a177ac9d71d1944da497ce73671ecd9b76c90ccea53902bdd4b1afbee956dff44cbd8cd137a1903b521ec922f49b88a5620b174f27a5acc32c0c9d3de79c14b4de17f6c2b6d51b6413890beb2c9551e57bca9cf48d91e1c38f53698ca71fa717a69f01d356e9a4946e33cc800a2008261020be4a435415dfb7e4c6a669afb18544a75eeaa85e532a499d2ae5ceb793f2e93972c7f3e93d72875bfaf9f2cea5efe159ea821c8ae97b2dd4be86adbe611a7ea14555618136ee26ea80d2c3c343354a6d0f8f111e1e233d3c467a725ed3f24826233c3d3c467a787a787a8cf0f018e9e961070f4f0ceab8224c140a856e73d639e79cb3565a69a594d2979c51cdc7e934e4e9e199618f117ef14b09927eca1a338799a2cd524a28990d3f9bfa55121d4c14ca101d4d733851a58e21baa3a69452ea4d29a5b4677777cf6eb64f96afe9637b0ebf783a8c7d634e0e111e97f6a8efe19c9c1c995487949c1ca53039bcaccb1a8bc56246a27cedb8d71ebe5966653ef5e936168bc564a2740a768ecb64cf76776fa64194d2ee1f2f93e87082c78da43a6a087383eea8e99c734eef39e79cdddddd19a04e698f974961c000630e2c73abdba64d6d6ab3ce39bf7c616e66ae49df2c9b53cb6161f3deeab6693db5d6ba76f7f422bd24d151c315375032942c073c7938ccd1c19cd385f2d79a7ea1fdc29c1cd80c72eae1e1b16cd9f6f0308f911e233dcd94995700410b4c4a989b999d64d289a51023fc929ae491da9cdad498c7088fec611e233d3933155867e45901042d289993e79c3c4e336a7dbc400796c812c161adb53ee658afd65a6b6bd7aeb5d666cacc44d8a777989b99e915765a570de861c62f69436464d7bc7dd4b41f5d3e764d9b52d3c2b13a4fce9c92d2e94478fc39f434be1c261ed69973bacf9c3c93e6488292c13af878f8c5eec6e73f7eae762017e7f2511405d23a3eb75f48c3b185cac10744c72ae613b8b969a59456add65ab57b82a6699ab6dd13b66fef549d15df488fbeb342f6500fb1ec9d9c669115f63e607b17e61839b969d5b69185ef50ba56834ad6d5a09f5e65903ab6c6c219cdd36edacd15f18b2ba595be60028c76b709dc515889f875276c3a1034be5185f4ec1344357d5c1d3a41e8c73a748214eb5234efa8a7dd9e95d28a42fd5887bebb5b85c42fe68ec24a2471787ac81985ae9f16379f2f650b99c440f48d9cd1f3c8190d1dbdaacaf3a882e2d939a5c6ec6856452e0823956c063a296dcff64e8aa3da92950389d457d03b2afffc47e51f132a5bf8f37d9e72262a955f071235bb799f0a742e4485e1166a75057d43b5bb0405e69329a7a23993feb9cea4863c620f755ac3ad897a61e057efdc95d01215922ca2f3579b2bdf7c49efb8a8f30a7ac74543570a2c4f7992760e9b5c6f4f757b9395b34297e75264b914ad734e3dd05fde855c6843a7edaeee12941a5f75f9d759ee4ae1faca7f6ee84279fbf555e8a23facd0b5b90b15feb4df1ff695b3afdc45431e7e69371acf6beed33b4be48de6d6babcae2d4ca1fdfa05da6fe8da2ec0be0a597e3d09cb6fb8f22629677992957ffe854deee72fc050eef3b9cb2795725792ebd7b525a065f9ca7bf8ba2a05baaaa7fcf324d7556113955fffc226547e547ebdc9e7a91f56116b89121f7e491b6de3ee86ba6d9bad16942fb7cd51edd693b0a3c22661132a4dac7717cabeb93e5fdfb0cfeb135483f5f61ae675f974e8a2de0e24862e1a5267d23f282695855e52272f9d9badb7ff58ef900995cd99843f28e790093bcab539cab5b975ad7e7d537d037d2c0804886f0515f855a914433201422eb68f2ba11cb17d945140fb785f7652d885acf8fdbc27a444f6d94d224e8a750d5c0114b24136f0881dcabe11b2a20d95f40d9531125a82b402a7f143a1c2570f9883025de283817546bb843ab9349716ba52e866666f82f2762952ef2ce87a417b0156ddc76e42966c0cba345f81d8a04b0b7d96f48d68c19c97d38b7e439437273a69d3dea12b85ce374fc2b91636d19cf32d6cd264f3cea5e8f2d9367725d190cc9f2e01295a9f4c7df23461fa589f96faf4096ab71be872a5b039ca93686ec326b609ca3797a21441c02ff00351a04645cdf60d5d41057ed1d94b27665aa59336a81a703e9d08d8e44b97ae1450de9e84ebb009951f76ce9b88e3a7f48dadf75cf356ef74ae75a1f78dd6b2c1d7613bca396cc2cef9f4b1d58ef29f0e5dd53d1afe70bbab863f5ed8ae9fce36b7c60703bf34ef61ce51a0cb863eadbed17c054b5ab1e6c642c5f6947a6773ea4a7a5e9f2d74f95477fd682f65731f59646a4e699d3246df0aed435d49ef58a7f4f57593e5ec94146bc3af6f5e600e58412ac3c4377f58c767b440b824bea972ea309373c9de7992e95cd8c4f3e95dd884ca4f13d9f2e9b94b7ae74062fb0abde392a12b05f6e93f33644245863fd3bb9089e7b37326d339745197223b90282dd8766bd933977418e6bbaaff74f39b50a1de84bdf32654b4f087f3e94d3a97a1ab8629b077fec3de8534fc99ce79132ecce99bd9c79e33b52f6b501ad494cebee95c9f50439aa0d6982cd21ea3471287c6060dd618100df5190a3dbb3c92453e38e1831743f632290c2e4fc2cba430861e48f715499c761a363ee664e0966491e93d9d9341b52471bacaa08a491c0e006e7c334b5d65f8765b978a6cd1b7576e4905c6b76fdcd2f75865a8315944b33fbaebc84feab110eef86aecb9c624ce0ce9114d91165fa5146c218923bdb353d65a6b9589d23189436bad26f4f89e321c992cbd448dd1472f293dc1c7777d01ddb6cda2ac45d9102571d856b1d6365d1368de86dd930c11dc949cf2cd844d4eeef11feb6cdebe69d7848ae226c7cd392d131fbd957eb1f99c73ce39e79c73ce39e93629cd011da2569f53abb4d269ed9cd3fec044b56578534816b13bad2f0e86b943a9f459fb7dadd657df78f628dd7e84df8652e2ccfa06ef68454dac53dd28cc51158314f49a4cc34153d2a0684b5a14262290e635a6de41191915f54e27866fd8838cdec3413bf2a068485e144de625a1297953b4a5ae61f754f44d1f51461247eb1edfb5bda3eda8244c57a5514d2ae566044000001315003028140e88c442b1509886ba5a7d14800f869844644c9b4ac45196c31832c818630c01010100100090c18409027dad87020609f18f77443826a562cc59149cab18c8c59d57454730dace730e2d55618e989bac3ae9efd0c08857d41878c379ef3c28fb8162b1b5a99d276625802265a46054331d5c275bb1eec448f5615148e0bea79bef3bc0c62b3bac51699d0ad8a16c5e45b84a2f5e307da5ff1a7b4f108b4a19a2b2cfb0b85e29fb62a0bbaf1e18152228cb891c31b251abc47d9888c3301bb9a4de6b119b042aba430d274108087b0360a3a69ca83d88a8f49b3c62c59be7b60046c8fad07e96481c5961738695b49d270c014ba39ed12493d9b38ac9a6cf71697c822d251ceaa4e2a9573dc7e4452b96a6bc81373634221f455be591fcba64e99f3d28f5609688822e9d5c27c5459dfbcaf868a3fa935780e1de41467d86f582d7cceae5c18e382e535d6f78846b96c859602384aae4355e0b0bd625727c7b2374909e8ef3c2a59008c0dd97ff4fd33b0ed0c652f9d1279f78e68210a488215d1c2a874b25d266aabdd4686d1ee260978db902a58bf5cfb9492d3307234f908f53f291c81489a1a61ec2e04dbf5d26ac24008c74611e2bf9be353295dc129e480041390ea911b83ae43ff729344f8878d313714e3d19bf30bc39d7349a0e8cbfd5712aece840a689c0035ff8398ebf0150f1e5c60b7510bbd8560e379c7c87ef3d7323d06e106a3fea22d81271c714aa509ed71432f904a24c1ad3beb76c7b21474e3f8101caa67c7c425b5fdc580a9070d37bee848a9595fd957e0a43376caedb0961a1822993f19ac3823fa0c99113c60162c8dfe06bf90dc445e1c1f7880e3d509706626281ae4700196880409803c110b3102d3ef0dd835fa13519038aa1acca6c6afd16636a56ee01c7a245bba77e8b838e283c510f3873aafe950a71d630729a9739e1db6d382d9abaeeafb4c1594735e3e3753361fe6336e88ff47eb816d86665ac57867e09e6642644608d636c4d99b0668dcb94589d63c2c1d203592f119025f436ce4a7cddeba5d96067c06c44d600209c2caeb6e3745e74b686bfa0e823dad7ba72701cb44d5bf6b0b27fc3e1c03906fac866b7d6f7d91aca9243519e695972a60e1a6675a52997eda2e72943e80de0cd6ef2d0edb3453895d9704ac7f07b3fd7acce5f345284870eff870fd0a31bb257d3009825bc17f05df90936feeb91e8cf4ad1963f6dd6027b4da2cf6e276d9c2897ab07eb51602166d57e02fb03f94b3359adf5e78231742027f1fea7350eed40dcc8d0df108c8f1172a03faa8e8ecf830a17f5f33dbc7e1ec9609b8e25752a19a19f77a6b07e47b4695ba6a40ddb7711fc0796c5aa23fb6de476019f1d0f4ced4e863ae2889720df1dfc1ddbe888653e9e576f98c2fd1c9e0f202acc8c088ab4436985225224aa092efa9656abf1b8f4b89b48ee4bd97fe873c434d577b47677f7051005424d14e8bda187b7dde6c914e1cd940381807a1f483016dae046bb147976ad27372d620f2ff84e6f491d3e464c8ef89f849455aec1978a03c02a7b402f95a6b7fa0e556b0da50a8590eb93f07d645248c6235a40c7cf061dc40bf152d1e6c0d8a0db1aa26bfb7ee280dd9bb32fefd9da61dcd86ef475825c4b79a4ef8d4f89669204a29844afa9c5a88b4b1e74cf390e459cdf5ae8d3bff79fcb984b22c4623218100417ce4cbb73f9bfff2b0c19492feb6c52b064fee55a4f7af46c52466f045015b7e56d61c54caf043d887f4553808b9ca516f3734f642eac5d64a157a0c30bff980a28bda517fea417227417468c78ef40d69e1e9c9030af23a412368737622c78cf6b8b6b3a516e664b42b5faeb4ea53c9ebac00135bfbec2a2cecf7ae556500441a45ab15ec8f1c89e0f1f4f491f60b50d72aa49cd8b3ab7d3991f4355e39d7e6a3943ce320fc3bf8c0eef38c4381a4061b915a81e6a40b3df2f6a89c69485aec90b30263f5548ed0e8c7cd033ef15bf2c9822e56dfd34fd101e36ba0083fb4eb211cb41615524e6373e4204d4fb02dd890ab70b752c32a6e39922bfc97b5e72106644681906687e33cdb11cf74792988c1b5062970f6450f81ec850256ce64c16791bf0e993f17b176fc6c13dedb6ce564de09272125ad5581c42009647af792e2683e4ae581ec54c305ef041c7eb637c3c7e9760841578dd233f74f4658916f6a8c56628fcbd4dd9d9c4ec976d9438a8a659123569a777165feb4b2c79b8a6b7bb89147bd6633cd5b8a83fdefbcb56fb229c537a60ebad0c68f93afc545d426010a6b44e9c3c99ed2e451497b7d550490f87207e22f9eadf99b256e3b5d335b988504a14413ff08f264224ef3134cea398db7f134e03e6fb3e74c4a55a5d71e2aa9fb9ad971f5cb5ba1a822fdf2246bd909292f52ff8bb08d7a33139c60e4e13843b67047c007ce5caa8cf7c118adb3be9a24ecf0543cd11143809cb0510d66d2614d55c169076c8762b6e2eed153653ae1ba79c2af6310b1e1a2f2c59331a31cd389b928d08c3d0040308bd73cc0bfc05023436ed16dbc75e3944dfd847fc7f9183274eb1a3055add9a8bdc42eece1898b203f8007ce3d4f029e3f86c3086e34504b068b593df117d531ad96e11980c4d295a2a7dd9ebc93dcdd0c1aec68c0410b5ea6b7372ab733ae4eee06e0b7ee1cb8d25ae61ce61b2d3952aa39480c13d308bb9fdaf8ad2350c112be64de98f111d90d036ff82a5e9a3ce6a851ea8639eecd12c8aa30cdc91182e4cc3bfafe5e701da28325752adeb766a757be41e47dc9d42f110e300dda9c5e48b91f30a521342ef4644daed33d5c5acccb4230287d8ec52ccef9c9c2946d7381e909b9e293bfc1394ebc7c260218b3052dd024c1e44880379c24ccab3d194686e6803bab29492a5329e258f82ad7e12302805a4123b2ce5f95adce0472027d697d1573e8497755152a0b7c39ac4dccb2b35c59bfd625d56ea59363e9bd7422a2c71860bcb30745d88e0488223db42aaa57ec5bba23f11053aa3088d9d2b8ccbd54d17402cd49a2ba8ad197802c61189cba6053452c1e97ace3a0facd5151daa3c30aea50988dddcb7425d7d70da4e7240b7be4213ae936392f8656a9244a56a85954cc1be62093a08dd72354998d675e39e6669ea07c54334d221e997d4239adf0fe8732d4816cc1a316475814253d8cdc5ceccf4963174d2fdee991d839abba8eef55defeeec4a1b4a687f72b024703e93606879d1d17aef0b3c5f72b90610f4cecb1e10027b76a935344e959a7a560b1996651d76f48315d00dd9ec76cb1151e80cc149179ee56e8070d807f6d9902c25d95163d6600d0243d646d74b0820b8c0d482e8f58d4b6adee81dc6ef582c87e1b92374884c37ee396398c9880c921efe5a1c100dc8da584c132fa4d65361b83dd470eff2920b3fedecc4a4e56728bc9006de921bf6863d290e1c1d80456ddd610bebc658df7b90a38d0ef3e471efecb90e3621294571d481babfd365fe99dac3eafbc8f6aa6d66958cdbed17fe71d01a74a463f6a0fca2aa8c0ab64baad27c165e4cfdc74baa24d9d2aaeba2e32c1b9c33d33c5f7dcee4fe4ff3e31e5ee2484631332173c708fd0c45092865f7f6ce2dfa24ef2d63c25dc525190286a8a3d977f9273a42f7cda4e576d233c5d9b4e66b43430325f683ad3a0573492143db47997d6bbcc9b5e2b4b77513afdcaab3af17fb94d04f4480ec3b4c2519317859c598d9ff6949aea57aaf9952d834e33d8609911ccef4ea2cce0a3cd64d19b3ea9e6c51bb456a7cc7e54e4db82c4b9e9bfa1c83663cef26a4c20829cc4979dda333c7840ba897c3a0c79c3c62aa3d53ffd2eeea96ad2ad19e4f6d766fcf98de7ebf0452472ac6d603631db3aef79718627f1e0a9387e994e9924bd0a9c48f99a53e1368cc9b71fcdab4c07c0c78824f9dd849cc9390de68e61631631ce1d5261b52c2237002e802c1b76c3a510a25903e28cf1d9b80ae941397fedab0c60db252ecb6defd8f3e9564eb34aaced606afb1039fc1b7f3c8a1e5c73cc4dab98def026eea22696c57baf148f76b6e2e0e130e6c7eced7482e89b1eabd46b3fe283f60c6ec0c7c6c03878232a4e3b2acbc167543895c84689b46d839781224a48e21dea7a4224471cc05995b74a2ae2a509ffe986a4c160dd082b3132d73d7f40600ecc38056f64534a48fa22775482b7f1e8d0ffb6ee743da09078adf9471ed190316003201de72e6637078f8c74e2afdf05ba71814f7b178c518319d4618813490250c719b0851d5ae693206a9b7cbd3633baf559329647b123f01a6206572ea62031bb453be8764b1e5ac2580c5129fef8922bce0ecb4581502d3fc8a436c61f9ca7702f64d7d6fba4f1da1eb9cff3b4c6610ffd8f139d4e4975251e73c59ff038c54dd9033e8953974d1ec2ec34a453c08da34f5e5ae6142a9fcad4711e548905b7c6de572c1584934a4cf3ff1fb37a0bac22dfd845693f6a905bbc95da10ffc767cc37bb4e7abdd4a8779b6f1adbf2445d5e4a41777a12475b86358e102e983bc5ae10ab95b5ad8a85c17133f87498f7bc2faeefdc79d070a49770a1f22068d62ddd1ae7ab8e3674340cd2407144fbc05682a9ad4720d7c2dfca01de85acf0a6d8bf5fbeed647558c657ffbfb61b69c91941dbd92fcfc8b5ce67b04752ce76848a7b4c198cc7e7ee3a89e886e70529d5d3b0d0a682d3f5138181d9cdba6ac536eb04bf62e4ec09bfbfab75b64b652a04e35898afa2799588e5e5e9401e4f82ada74be70cc1e5c4be1f836ca8132fe63ad08899a0e9ad2275e769dde5692fb349cf18467312679188923ddd889b4dc45d21b21baed551f9cc26a695dedd45de554bd7df6ebfef61ed0991fa08ed453d5332698bb74a7613ee8f579560c7d9fb8fdb7a697f3c73eed90327b1ae39f4ba63f05bd3cc681141869664a54b38f6c7c7ab86cc0db95985618a43a64daa4dff38b73721a657bbc592d999fc8c7802603cefa087f893094f134b8673aa6f37086c9b18ab4c65bd74c262525a41ff9be556b04cd59fbc668eacbcb588cbe3a3abe78f63625cee2f60e76b5f8c412d5a39f15fd7016b73a5a10b18169e0600d8a9e408831b03e2895e8c704a6c5087515872699c6af3e3044f63ddc7138acb8535c0ec4f60452f29854983f67c5ad442cf97e0fbe15bbffdc4376ed69d218ab80b3e4a1a3580597a83bb5b05dc44d7db8bcbe6411c4e587b22d99f8a402d8bf19306e72ab080693b00138b120ac858d4a0c1d67b1a0987ea486b8536d294506ffd942cabf0830a882b5ad733540de5129e9d2feb6cbbfd453113e6aaa0826324ff6801ca1f8a66e58615e77fbcabfa22a9d3c47b924b11087a944e7de847bab27b571857005f5155a6ae6ad955c02655a87ee885d3de2b081aa6efa958c781f6a5fc378c1b27aa11e293c781d0ad02cb6e9d0ecf4cc071660f53a9982d6b9fa6805b927cd155e34d4b58c391582ab8f5b3e53183cc14a0de88061087747dfcbfbc44722340da0ad450207033edffcfa47c3f8d8aadbb2e67e31e5226e6c7eaa9048b3a556a5178019c53aa21df01c7cb51a6f2895c33a3309047c69821c083139d7975482b258a34ae6801ce946c44cd8ec5ff8b056d692261f09423972579f307970beba2cf40158ea9548857ab4529cc5976d14be17c2cbd60ab90c7ce0b651708ca2422ed904afc1abc969f54bf70d0d068462ae1e7bc96fd807630291586f10749e3ce8b05742424e4726aaf3d82d502e00d67ca678d374b42fde185d2dfccd67a500c282fde6c38758ff50ed64541a5dfd4caa95814d7ccf977214abf620f12ba032f394f1b07c85115a4ead47e539b58477343683383990a17cacd178e8b558e396a7ddd7efe45dd743b7d8b9320a9be569d89493161f47cf681347c76ae16436f5122d1f164896942bdd92102b100f1ba0b1f99a3e75212ef97b162632df79c7969cd2fb548956efc336085a50f1145e5f5a8fb3b0493170d6e7b3d8b54a81b587c678972519ad017139695fb4cacfc03db8f8b18edd8fe95a426085ef1e1293edc68e52b1cab47497b776c9069716f6283132d4d5e0a012ea9c6846dd0f72a3a386cfa364a6ad0bb61ac388a6449b9e9a3a0ad1b5312bd728018204e3dbd8ade3c1dc730a58364cb71914ef2eb8f0f3410286e8b34884518f5ad270e0cb119a9c331a31b382d5179bea7afd69f09f016f5d9afba78ea004fb20b0c5ab88a694dc505e5c8868127a78444360854ae607aafc0af72276277d6fd89d7a28c0f2602685998f41c34c8dedce57dd611052ffcfe2c1ba17c7f0b57da9148f82ee49c0362f994f5bbad3c6158be261fc050b01c916e7a16c0a96807cb06e74950f32314acd487e0a95565f71debc2d5dc41f4a8e6d5554197f0d56bc0f80ea6e2e4bf2350f142f68e5d420c56e4851b0cdd2ed24bd63d0d9dd4415ea40f0de60e2da20b46690f504db5101627581bef3786c68d87ff6e57f8e5580c7a6433ebd614d55be5de97f77979a905b39c486980b37f0ae69579100b242d5e8199db27260df4f6548cde89a1909e43d091d32123ebc10d4922d93605add70c44b4ea28ba7bd3e26cd3d5cc0551a2768f16e222021bc1454d1006537a2cc926c894212a524893d703ba3d372ec4f35857c647b921cb3930d084cf47dcf03e737c5bd9e6c7ce4b5ecfb39f1ca3c5db767c64cc2a0f1e51384ca8b9f0f73803a01129780f6cdb380b4ec48e7c7de8fa196f7bf708aa134fd2f5aa842fcb9179d5ea284b2ac38731f101602a3cf41a7c8e17471416ab8c16b45d3d3caa596531634f597bed881cd2db7f4373cf915fe2bd864d790115f44e3c95d19632538195fc625ea693d8670eb7c362dfb23ffbd79cdccfa20837482a4786fbabcebad1235668ae559b2be460e929ad037609cf6a076ee4228dc0719bd9972bdac7ba0805f518f0ed8c6dfa86584fe7ee1f91c74c1f78d36b298cf1e369e445953606bccab3f1fbdf83b538eb0006ffb82b6711d73b271bd162fb58e5c733c7340fc9dcdc32a9ddbf1392537fa193d8053f4da304fa9ea015c3fefbf3d4d222c61e208a06fe0b095f1b8b82a9ea77872d9f14441275a60060098dd39bb61cc7cac7da512dc00f02438a6c04dd5556368ba0100f6663f6ab52e21d55484d2f6d8290400b84f98e62272e399a44edfd368dc3924d088e7803550f576be239461925533cacf7e6369feb0590a19177fb966bd410d9c82b0d8d2459eda9b04079ae6168835f24af3bda1077d52030f843d1f982eb73a0581373d16c8743ab480baad1a87cf75825cedccbd2ca2c1855c495b19d10ddb0e6af5d07e3d95e00e326a5746b0bad6793b9225d8527c73506485c661d3941ae347fe786f20075ac932337b6a852ec3d81e51944e1da49119f7fd9580d7b609af0780f88aafc44e16f1cd3cd2afaebb0cb6ece8087b4849373ceb5d5a343daac0287f198fcbceeacc7c627b0e4f3e7978ba760f07f004c8c3217fcdbf1143f8329c54008e1940eee85f375532038737f2031195f6b15e099ad48ea75690d1a0e373d126abee85b911f9669e50e948deb9df7d2f6dad1cd481779ee850cf878a0077479029a5673555280f06099153c55e0c6f5f54914d3d75940acd7ac80a33a7c79af8770361a972dd9fb4b36fdec90e5eb0b8a2acc079dba2e1165bb53445150be01a432b1e1b8cc4481522dec096607fedfc19febc9a45039d7550c99d659f787503bb51d05b2a39961176552227afa813035967c9c28156bb6ad9d5b39b5d4a73e62b80598ba19e620037d129106279752a4b250f54a1030f7c187b121aca90b3f0dcb3d0cf3823c099b807ac2cdb3d2a962f483a3765d3f5f50a00aa1eddcea1ff9dfe64877419907889e5bc508d849bdd06f12bbf82121c73b7cd5750c431751fbefd5f27581265621dc87426b2264622edfce43ef5ddf86cb15db8ab4e0a5c638be256fdce328b1228c5e74bbe4b4a2924dd1f349cfcf2a1d6851eebef4dec60c15e5c39543397e6f80ac4d01d9414750eddddf821a78de8fa1f09aacc19caf24948c0fa46e3320de3d6824105d61638218770c20b40ce899b9af45e00463f8ebbbed0a59caf732e41005bc9c6351a0817bac881420ed2cfd281bfbb916b0db01fd7e23ac0419f77661957ebd16f8783e7485a2cd39f70b9b3ffdbc52b998f277b59e5b1691a5d425f4f6079e0d53dbf7a0c430d4dec35dbe36409af25888342a4a5daedee8127dacc0a93421f352deda57d33ce449a9979271866651d776ce7d3c5104ad290ecf39a62a766cca15578e38c54fdcab06ed92ba3ec5b7cd15e406a7e505f3a3716d440e8eaffc4d5df27d6d3b0bbe6f7037b62696152d955e932c335b3fbf36ac7473bb8ac087851b6087e77c3d90351cd6ca8fe546e14e22746153d58e6c2eb32db9a1c70b77a23a48806dcf4658610a0bf8b66b7767988743c23ede774928a19bcf09d401e6188af5711bdb4b9700ed0fc27e19fb4e980c24a204e72819a86062274a3ae91caa885baaf708c93d40141a87850a30530df2925e69e4c4165482591392bdf6932789591bde47ea2f843bada4c390c0e83087e34c368963de66323b8c2ee027756e005e6e159b644642ddcdbe02775976eb04590bd1583ff80a892afc9a88acb3524d236883bee786791b9b7e95708a7d0a7668e9792806c0080ca024d17d9e878eb69ffc3dc7163c4f2d83f09a72e0ee6e326eebb7aeb64ad04617b17edfad3f138d0fe9a69d68fe61897ea2cd8a01df69e4450e9490821ec4156c584bb0a864191b7dda4b06b542d02b600ea10127a1fcdbc6a3d247b25c7aec3f1dc0b5d62f84e35142681979d07f1344e2f6cd856d77798f6f15945a4a7d19c9c83520fefe6b5ee271207d4903e6585883cd394884d49bd61236df5c2c996d41f4c2aabe7a3c6d77c2cadaf3e2df6133e058412024ee03775b811446b23070c2a14e467781913f8ad8f85c6a24afd78111745d979d2e41b98875fe7f56b4c27a7203cec2bd8503df2a9e00a8d150065160c8380570d59bfc6a7d1b3616c148b8856e7ca99de2c0eb5c0a95a1c10e30a2bf400197cd8943de970d1bc2c8afec4c5b69660bf1c50673d1312f21792a71eb13dbfd0635bf02420fdb6a2cdc2712f9bbfed51c6319d9805d08de65e2d9f8e5487ac28cb9a00daf843106fe5ebcd8117e2f0aa2cbdff1d6ea4148ec00349788256633cf03b68b0aac85dbdd174dffc05080c534c719d13e3262051320d8cd733ac5d26d2664811edd5c219f1a08e7f8d28e554db3566c16ed60c098f21cb8a7ddda7b3126b3f14db9c872fef9c7c23620bac4e576a944257fc1d17e073b4a16978874929e23b08e08d2a3de3a955c7c399371b3775c90ec371ebd141ae6053cd4387e0e24469f4fc07c200758acfb30ba3b4fbd4d0658613d622d8819dfe6c7125f2733bb0846480a66a4597236ec2682998bea700a6655f99e98e4a6bb9989b40aefd6d8595e3674873c1be2bdfc548972879e7868e347b6a8cd0adf552fb02d95b7a7496d7e42ffb175d59bc5ccc2f91cafec57bb2a21d6cea90ef98b92aa4691298b7afa9d02158a8f947b7887e4dd70c0e8e16f3042116a46ad61e2a78d88c21a04e15ccab768c326bd12f4a10b483d730edd12020d56165c430fb096c1b8d806c594ae1792a3964c0655814c32b5549fcb45bce576803e9a7a671dc837a7ccf30b6eba0ac1ea0a090638e27eb31c0c6b579b2f3d11dd0cea5c58df31dd6d2ca1b67a2138358d5b3453e2e32d8e4683fceb8fb4688362b61a9442842cb84e35169d83f4a32c63214d3b92ea0cd5157e60755f8bca6875db06df40b6c15e101e78984952b31e417d058f936ee47d029614a8f32530130922a6e362df53f1a9b15b501569d1911b1ab86fa3514184aa326fcce03a1db11fd24c86e87de897e2f54cb846dd830aa72bf1f1215cb9639f988428918b9f235ddf3770ae50ebdbf3f74519029291537bfef559efa24846f322c6c1159d956df7551ae72f126b17b71f1b9d35d33730b7088e1162792ea6e61a57eb4920a86de1399dda3cd1c85a23c377f76146a49b0284dde9e1a62849c9d8b3e6e28bfab17b2bea070ecc55f3c055ced0c1f3be0dd06a823fb9f55cc1f46de52e86efc5073f5eabb48f8d38d247d19191d171701405486be0f87fa886848ac2037a5e6eedfcfe784ac07d4c5b9540ca02e637d40e163da76036147bbcb27d3f077a69ed448e1920b4d03794d80a78acaafc4247ba9ea6c89f9fa2362dce0630905c89018ae31e0c15a32dc63967a051430104a424b26a8f5e9e0ad732355528fa5d82c79b4b85f26b965762373fa6ad4970d46961b721ec009be0c038cc606e5d7dfe29be8d988b00ef1c5e87578a797ceff10207df1dfd68c505cbdb765203c73d447e7ac644471d38508ab1da66f95ff4faf27f966af12a7e3a2d6a2abf72e962164caa74ea90b82adf50da9f29a5c5a84b3a93ff59418b4da3ec3b8f19dd6b541ed0ca9531514746c794198c53f96c14116298f11284fe5d12c3d6cf40136407da4397e24b5c8108cba36db96f06f1322ce13ab920bf7d64699a60e7598f84173e486ffa79f0cb6104c7dc75e299bea74e2a5169f651fcfaea6d16c6b9e9061d6e456ca3b8d1ac76dfbdb2f889148a009180fc270135721119c96d16d581f9c9320a9ec2b0131863f13fb65658a9efbb328f780df8dd9c45b1011c925e6e09938228195171684cec348edece8fbdb8ff6314a54e4ddc0e7c8ac44ee635d9f95ae0f83ff758668de3443c24f2f2c553a5dee3dbeed3e2e8e988844cb3acee1803adf27f80773cb85eaf1524ba11b652fe3add2ff26349a82717719a1ad90c000cc59625be32dada6804ba60b50fd12a155f88a098854b8704f7e2133b7945327ece13b119a610824244bea8d115724d5d5015a5888807b689e01dd899c28a1d046b561fdee436b6ac0eb951392a32f669781c9158bbbe3f5103159c7ae67fc26ad50d46c06abbb34b17dc938447cf6c3e9badf144a0ba77025d0e4e4322b87f9b0ed49ed96de1f2f862779be799916fed7eaf05062b7b75e8dd2c5b3dca66b5e5140b1c3e67cf68aa9ab784241a35743d50c8544d7c84219394efaa338ae80f950b3b51af8a775478186302fb9529a739c68408eecc23feb97c331944a984f56651ee69d27e4ed0a8cb4a7da6f0a0400e9e91b2bb91168670c756ef0bfdf20216d00ad2c44ee80badb04e04310c4fd895d535e263681be2606077741629619c21977bb9d93493e7c6dac932baa5ede7edce437e4c174d4a612a3a8f5e837124e8062f03ebd346fe93d2b0876b5ee4844c434ad1f914e23c015411de85d52cd65e6953d2c0cfd670a1be5d53f23421bf99c1102bca3e679caa359cdfcb5cd4b5dfc9c5f84719884acf6b37c5f8a15db0697ee58b774a2f94636ade5e0104c195699de07056896c2a371da5ad18c3fe8813baf8eabd0517c9c3e0bb0dc4c38e7725d274cac43d11b8ad7d99371626cc6700674c267cafb5cfe05dad0b0563f1f2456f091a822783f6e1964e89ce340cb3016e62ce5917ec16f8a8eebbec0cb2c776ac973b07e9d0c0f0806a0a5cc67515e1f905164b861cf23fd83e21c94ed7ee1c8298afd2880055bd7f78d35b0a61ae2e9e262802665d94313fc6e1a15a2f2a867b435a7c4dc9a5fdfc45544f62b209aaaf88856104820ec5b1b699c4fcb6e1c5ef0c2e00aa61ee21e2895ac20adf36eca1611cacef0a571f0f290441656d9e46e166e283cf1df6be65f44e986f3e89e30b6d129bd3a3b5086d83d0175d4d8faae937806fc81d0419a242311e3e62f603de5a3021c33296a9a26629ce0c67d90392d5bc18b3be97ed66a38e1ae4c94a40fae4d3440cf384d64522d392450d7af0d092437bfbf83f5dfa1702452a039e0da85898b5bfc88039c55ad4c78e8b7b6bdf5a53503a990f8a7742a364b5ae5c83c4728002fe27640e9584a8c8bf30234285d551ebb0a51244acc13f2cb5a6d2c6db76c8d5d2a3687f95bb10ad4350a793d6129643762b3366d9e79c9d7e0cc3535c7d5fd7632a1609f8c9a24726c13f900214b26b0b8f8ea467c0675a02a150d12a090d1711d6aa571052f622ba38ef81a1ca286510b943c98a88bd769ed38abf20eba3a09bf9e0cd7aa6abead82a943247c66b46c26c458b785c6ceca502afd7291de5cd18151ac191445b9c4a88d26401851cb7635eff142a9e6e84428158431877b2d869c5a513dbffba2730a4183fe211a399c8a615ac1042efcc0df1b2ba6cd13151f76b3eacc6893d7a24e535cb24669292fad7aaf9e877ad113327c3f673ec4996e02f4a543e1a8c9e26185c112991c84075f83b154dc5abefc6d9772905359d18884c7f363839951a725476bd4eb87bd4e5f4f87dff76cb0069bf94e39cb853ab32695dde24937f7530e44bd18d506eb71deff5705c210e768fe773a0b07cf1c8bdff22e617fafab88a0c47953f98e0194f3991c288fd138d86480cd994539977b4e915618bb242783d59de58008302f91e8a316d2e138bd4d8e0be52ea34c2b4b596ed769709c4f0a792ef2afccf3432363df10c4d27570abdecca3a6330fcce64c4b01ac015a2c1b152134a3c708e4350d0f79a255e6535c9d92e4be4bf4ca558dc8d9cd2c2e094c27a26393aebe658c7e939eb743985c5aebc0803f849ac3038c4a18cc01cb5ecb308f77d6483a6e4ee7d72f21c8521c67092aac6e7317fd27e97a884ea10f5e1c3dfeebdad262c06fb5a2082d2ba8534fc599dfb34d995e06e5f8da6ba1100b54ef4ec9051b5eeb4bb53c543380fd0c50d28cc5cb93d53bf07588f641e91169ba57d5ea19e009044302f57fff6ac0875b24ee1cc1d7583ea920949940f9fd7b08dd2fc0bf762ce664229c7015f66eb9d0d0b59ef44e4a5390088e244a8d4836bf359604b51a8a12671f9350092cc84a40da33582e8a1b9b29beef9a310ac25d7d12e945e492753ed6552c2c6d8990582c268c71626e5955102cbbe8e3224043698dfeec35dbfc88b0e6452ed0418ae8c4d2fcebde79b51fd0ef0b079690aa99df4a37a3a7c3298cfadc56d6cb6ed04d754ae998f097ec86c0b51ddcde95ebea9ddafe14387297b56e56e451663315b392f6231fd841e2a4df80ee68c4b43749d62d0ee1681eab1a354c8f1872030e5575ec7a6739e067d74f9460be05a588c78f7ca4b27a5a87a938603287f4d28f2e0340b4444d10811893553a5b4a94c20e8312417a19895a0759e8103d08a5a6b8718cf56e76c7adf1f3816b3d64c3629439ba65ca5a83d6fe4ba6a5f17ae7dabebed065d68058423a1356863aa6325d07058a56eea304295682e21d2ea8e23b341e50244bdc6d60e120596aa3316d528aa8dd8280811c6527730e01fbf9326045c09b11303c039355daab0694c0b058212c0a8826639e9ecc76915a1d141a6b53844a2fc262598698baa4584c896b39edd6fd2b04787e97bb19696e41b7a7f3bbf477c609543749d8aed08f483473af0816f42298c0b4dc7fcc896b677f8285081eab212bc02c360945b6cfdc61011c73bdbe36e124da8f0e2c74a686e64ef6d83e76ab96764f5b7cb1246a6051e3305489b47a898ce16818d1ad67d81faf2192e933780797a53fe91d7729efdfc1ee9873e0073bc6ce3d1306d743353a65fddcfd36e3c91294c000727f00534d0c44c4fcc1c375d8534c05169401c454a29f4f638d18367c2d1e119194becc0be58920f4b2bfe40e683a5360c5b72dda963d91fb641830d51928e2823ccc10cd562f42daf4bbbd38408e47cf3889ed84bb788403daba9403b6ed9aae9d24b3cd29bac3546baaa9ebfc9a005fecd88bda22f798304dc60f2fa2627e09934bbb91383423bf0158bd2e3297b86d816d150b8d4549c1052406c237751b1bfc9692c0dffa1cd02877dbddf495c129eb46c2c0dd09a633822c3b46a12299afff808a8a48b6126bfe25fbd8c15c1a8824ef1806d4b1be8b1d50e358bf55b2d29bc01cc268fa9faa8b714d568b7251787f0ec47cf228a4e185657c12d39d3017f8ca66739013e0774a2942c937e9cd50f2c93a10cbed94209446f1987189c888265cac7aa0bf57b7926af21eeab389bcd6ce2ea9758abd7dee5e4d5a9651547a7f331ffc0ebbe47b791e47f3ae703e18bdd9cb12f1c45c9ebd0e5b4f872e1da49274352a014ef01f533d90dd84f8b3ca45967ee130c9c8ab4d31aeec0efa01a7351f441507763ba8de959da4c11cb62525f868d083b547038f54ef91dde3e9d732cd7b7c8bce1a5e487b70fb5ce55a8b4fd685f60b51979229d2e955a230a82adb0d29b264816883f0cd04f3724aa1091329a6dc1fcd90e766539b042db6ff4be2c6f48c59a09afc210802cfab9d42469dd3ef3103fe59932c69d85ac2635f79ef18eb10f8536e04a82ef5e4c39dfd5f56845153b05a292a3fc7e35a83e59d343f72abf1ffacb2c2b5e226a0675c14485340c4a569cbe98e263b21549b6bb30c5db273fda5fc2816ec40a14930c28ee9d0d13da1753e8349f58f38725296e2e3eb60d7e605ee7c4a8dbfc94600da033b1d67db0b6071ec28514591deafd11d67d8637abd8ea6e069c04484084144cb4df72b98dd2f60a2d30758f959e1a6b243313379bf04654f86a4b86a90934e6c0bcccc092964bfc4c92e69e2897f12a74049a9502db63230549200860935f6794facf42a991af5b85e8835ac19d3f2a9faa35525aaaaa8530fd0eadb873cd8e417ca4526828bf08b44946c6b3d5a8c104ae1cbd8a9b59a8a9fe4e4043c4b612f885d0ca7c50fdda7ae16e0a00dedbfe3654b6a9952ce922f0f9c9d399e5bc829e7a520227432e2722637c375211f630690ecfd7cb120097aa97905c8e8999426e47eee27ce858236f9a5d9ad83aea8b935de2035e6a8819e6c4f597899c171d08157c8321b3a5169fa49d0dfb3b154620efa2f878a6f1d783e5c3295fb432552ef3967fc0b409bcdfeea3dd2e596d099dd163aa8b35b18d7ff2aa66a49070ac09a05c59a99e84a4aefa08d8e9978e192a1cc96da648d1d66e59cc3be608f9bc885d0f50588e9e10dce7c7ea33c7c6146b14ed506932ef2d6986b4a98c7b4bcb7b00feafa5ceef2eff4f0738d58c79f4277e6e62a95f5224067b3f6a032db28ad8e70e32db47126093b964ce6ab29f45a41517498fa0bbec8c556266796ae1f22847ca1235ea978aa27becfb3611707093bfc23160ef5f42c48d928223896089793d2b39184ee601dc1f81286c0adbb313d2f3578d92f43da73bee66f32ea23f09df01d7fe65caef5ed4bd7b21b93d216ec849bdbb6db8841c1b068f61d202a6566454b8f68c97c670d9daee8bc6ba28848911597930cc29db2609719710f9fd1dc0c8139ebcd5bed9ad367ed79d3e6cf9e647994aed803bbdfbbd59289892c79262760c6eb112864cd6f466a9a4e77bcc2df0cb6e00e1c6885c69f1c0dcb56aff3a57276e2eb0a4371bcab20a997806a3df59793332e50958c6da45a243836dbd09d927bfce3dab858517db12a96674bda34292bce4c7d98ec8ee33833037a9e4f75db10d78ba798b8bdcb38556a4aef7a46ea6d6c33e52cff226100a01c972d94d20da75140fbfb5ee72732676382b41bfa217fdd0a54681ecb95389c21b035220c91e82131012b6b8f7bc9ebec8f0e620d350792017d52e265bc0f43a240b7c7d31b790eaa90d5eb1f53500732d8186441f7deb99a6095cc0f14b05b34f00afaef3700413bec2bc418a56415e7997915b75abcef6b50326146faa5cba5ee2ab3fa98b7ffba5f36616c3396f99fe3cc077e7fd231c323eaa33f9445830b102e500bead2c51297880f1c40eca5251baf379b3d2a1e717a06212521971f9f926a2d532c17c8101c8d8bc15ede5eb9aac1b95b320ded3275295135abb4121253e21d4035458e2819c57f410bcf6c03442526f72afeec94d71cc155b08b35e64199faaabaea87583672c010a28048b1d4c55dff6241f48c3520d1211a460168a644c5d0e0a17d50c17ff271a74a19fa402b0bbec8fc85c56a54ac2f8171be472d77215df68e946639279816cb32c444eafba5a9018d1622b54f1398c3835d6f3add446e26ac6909aa84fd5b80a65eac0ff6c63cd47e5c764a00371c770e82d5bec4b4938cf3b9545258d5d35c5ee19f5b4b6a3bd838eea34423ff2c15346c924340db94a4a1869b6230a84f12609e9d19bec467ad3e52b5117b65d283578f7fcc0f4de4c88a5667f8781f8edd02c12e781552eeb75dd6e9ee345e73dbdc80249a454101870adf286c006e931ec3a54a424a4b1588d2e49be36653c589283ddefb74eaacfa73f48c72723e2a51a0e9000b4a0b20ba4bd75ce8b90b4d9f3cfb55eb6885f97b9c53c8f4b243d43469e421bd423197d4547e2044405e3fac7047dc1e75e9783e718f1933d39e50aea2803938cdc82602716e52efb27ec3c21ba5a9b8f12449c5c7c072b3b884cf8a81cc1dc5c8b435943f96d18b6abbb29e0d09e0acf0ab8712adc8d3c69657fc40f1ff307a369e53b026e4549cdbeb63f63e5895b91301c3cabc8d032c91e6b9017788011cd26871f4e7cfdcbea39801a14d9b6351480abed6101b83c7e7ed9ffbb6009c66862052b4f771a38f275327a49d2b42cba3c10a7db44f5bedf23995e0b4cd381a2cc11dcf19992bd31547d7c9ca52d0c33108f42f646e8acdca346e6cd4e641afe16ad509a84a656e15a9d28b235689aa5168e8b25fbecb5207dd5433a98396d79a341f7a84c629f060ece23c5819363561baa745c901531026c07a88d6c313cd7c53f8a34dcb07e5c6203a9b7cf77f385a88df68de7734bcd3e1793b3a01414bfd53cde47273d497bc90df29fffc01c8c51c512da256f5b49ed3519d2f224abd2650c0dd27f829459fdefc72862fa5492c8df34432449b4cb6b66e170929c0e786ad21088758e755d9f5a385bbdcc244e74496efb4ad8a23420967c3fcff42f8d852eed8d6b720370ccf5180f2526f88719a63787e9d8416aef0f8aa3bbdb2948cab15187585de9d469a3218aca23d1883586e40127ce178abb67cbb3e12db6b2ed2b4e2496e5341067ae6fe0113e2c88fcfad43817253da464d7ce0d476f532e17e43d810abbabc4acf49e7acc4205e179ed0d3a150d5615365c65c2da2571c6bf2349ea0d0a1b515ab6575660d86098bab058f998b15942cda56b6d83d923197cafb7c0e254e6e31e2863595c247a815aaa0444235074a9e338dfa5996d18bdd14e2a6485eec63cd23ab89ba13b583e7d483c0c79407bb1b444f88b449ece507e0c3a36627e92916f07038aa80f9a68ab22c19e4d096093af0967bc2d64f8ca2bfc41363a1464921a47f49b3fe517389465c64f7c6b39cbbf42d7561ae397814b31ec1316356f91b9bb09a8e3a079e3ad8cddf8add56254323ee108bbc627d57a784688c3e10b26c00d285e3785896d24785f92a043e6c75aef3cc3730203d3f954b77b2a207630ad8fe370edc73f61f1174d70d56dce1264d3fedf728cf879d87987eff8208ecb86be775402426fafbfa8788f51e4fdb765d3e2a954fa99944a33af50007ca04fe06404dbf44ef330ab42f782afa1fd3b1117d8da6c176111bf4d630dd4f899f99c8ba317de21b0b9ab61dbb6e2559ebc5179db45a191f4f5ba8e1588a9d3354b090e8fd10b774a8311a741f35670d4ad9415a7ca7f7b0ea04a30319aa54110e08b117a04104e6cb200f2a9d70aa677c214c3a5c534bf1abbe357eae2a3a06fceeea9804ca26819ea9931fb8e6f7bc64100bc405f8284b74230244bff31eb164c9da7b19143840070edf6028c3a240965326cb85d8f64e5860cc85df855a0d16a2052f7d7368033953d70e3c0b2f795e0d7c11b3e9a961513beef661d7162b4617d199a3128e5a872184480eea70f15bb13ac64504f27105fdcabe4144defefd7a71106e5f446c75b1cd58ac6b24d26dcc5c525f565b37a39bf53e0d5472b39f90cdff00651ddaabccf6dd1b05b89665d2a3cf8a74d90d4e053304fd18079a3bb80a3e64fdafaac0d46c8079baa7134e39ca454d6e44f135f5e8000515e03084307594f818b3c5ba603ba6d3b46651864462e5a7e9a85fb0a95bf2d53d9d6d4e4145e1e74049c067cadf8017547ae8cbb9a63da3d598fad485a178ff6ab59905d2798a1f1fb32e0ede2f82ebe063b36f1657753f1553161ae2290622e471c6ffcd9e7870838de162843b6d78246b99f9eb960e5bbd3c179702372275030dcb09fc65333e5451f8978d79e73a43bf4c7a195c5b18c4689401ac7c414ea81c62a57088011c35ade4b5aae3ce96c9d88f49ef3002f7399c7911c5d8d271703e9d3dec1194c4fdbae48a1db1a31d3901f7a912eec2c7821a615b93be7783fc54f5656dc4d80afbe512fa4caaee952ee72c2c5f8cb051c2b95d50db6d1838303b987c5f55e8f3ee2e7cc4f264adff4155a2b288134bb323c4dc407a88d2c19280bce4209e16a113b29ea9880b0565dd35eb377984415a35b4046ece121abb43b3608f0461f8838780bfb6a2c1eef9218372017ad9b43b91b063130f521ffe901b33f31a0b09f326a22088afac0572c801ccf2e8a770efd7ba73f5ce344cd60fce70b5288acfd45929087b67ec56a93c0bd6bd9d41775f360916f2808753d6231c4c4f6633a8b4cd451256fe22e972bdfb41c802cf4647b52b38f6e5e02e2e9983025d1b61e92bac06c7c12d5e26d1390401776b597a9ec37b970ddb807ed07d8f50d79ae7f9a56e17f259093f2515c053f4b839ec5dc3e6265eba11ea5361ad3d84c1886e401d2028d7ee984ec1f6f0f9fe2fde31621ab5858074796208a728359fe1741a93b390a792ed17124289d950a4777518a003a2630e9dd82dc4129db470cca0f029a220feb5c68df1d16fee6887106b9a4027ebe2399ec69deee2721ff4e5120c36a5265a73e61dd5ab5b34e8a0169114fbc3f51482904cc1bc978be53018321bc27a6b6f3ec299b0dc8a330b0a359353ec09e7130b2877c4b55e60a680bbba25ca95cc16d242eaebd1d6b7c854ee6db2b16f8fe8be075996915bea6ff6c10f575c85c20a182e6a7047fcf77311e260dd3bf830e74d60f48c9d402f8bfddfe2b7d2cbc6a0503f33b073b4df29001608a1a7428629ae4632f880335f309213a0cf2689d793b44eed29311b0068cb60b161a91347065fe6a2a25ce0c8928bc8263377e3d50853de7057ecb364af75e9046a8895364eabd6959987e4efcdc242e91181c5eaa57b982b97d4798aa32b5da6de9a33ef49d84f8987d5e76b2c4f86ed6a74719dd7b1e60a5663131372d2961bbc19bc4ce81ead8c58c760fdd9ce1ee7b45ba7c75807f67a1658d0d5f35b45b62742c410a9abcfd19932e8c1062b8f1936500becf52c08c3ad41d1943f9f8259215828f5066b9ba728151068a868cab6496e4576c87e2c6b131085e96fea10054ed45262a16ee757f871efa221a001c1f9d7554b2673bb2cebaa394012f658e5668d7db0c7765b8806d15b79a1a9314b6cf037a60fb308a17ed4951d5f8e3875dcb0e0e9a46aac3f3d953166d544141a44372e66694911d66f02c4400795e1fef02f7f83f3c96665864acb4e52174622be42cb4346afd2741a99ae65cc99aa38fbf82646f6ba3eb076e52c1bb9a60e53aade68dc331b5e1f95e4f02e1cbf31a6fbcd7872ef5db3174e2967ca5ad8367f7cca9b918fd368f6a07efaa79fbeede61bb64292ca80f2f41fb6ef1f689997cadf8e697c00a73c5fbbba3f63319fc31ed302fcba5be8a63219b0d3debb9f162191e9abf0830f87a959914c03f54a63a610d3ae4a5f14432c810809345f027f132c6ded5829e6a252261b4ca1361fd54a2179ea617e97d20093188aeaaef632e3e6c8037375d288952afc17a04d8e7e60850748c5b75e7d8ab0461b2d69536a48fb920e63a19a63f1cfbd6d49bcd0c67dfe95214812866a2b1f691befd707a8a3ee48f88943fec7e2719c0304c22aa755a304f1a54ee12950431f7b47867fbef480e79f7efd279cb9ba765ef1a1cb7dca83e7d57e07dae4e7225764bed808a00aefe2db42064f2df4c68ce02c46b3afe2a341a58f82652660f2e2214dfac884f9a659dcef1b6373fa831a381fad0718c0aa243e3a3db774064912f26f262c75c0a5e986cfc2229a6c24e46df40daa3948e8c7dd0054a48640c6cc21acd6c9fe04b77733a4d6e45c66fd9b9657f5a88d11e705fd7e5949822d999eac0b6f89df039ff1b598b92e6764a3478bf196f6dc79abf89657dc1ebba89bb5d59ed50d1633a84a7bc6cff7b5d392af0654760d1a6375efc031b170f54302998de1d55002cc36497484d37306a217c4dc8f6b754fddfd077c6b2785258b047119016c47430dfa70394e2f32da87a67408ece13ff95fbc4cb2379e1a0be8bd2b62e57219b4eb1ace75706425c44e6a6a9dec132ada8df3283d59880dc600c74d0d8ec180d984aaabb6d9478f7db7e42f32ca9ce678a2d6db6fce1c3ecad46933bbcf74693845d97a40e2a38173cdf273114a9803530f65042243ad1c8c95d823e24ff3fb03cc19ddf4eb924a70e813cc9f4b25d3d5582d67773cb8479763f52f1d33afd3cb0af5e92b2ba8d88a97f62cec2f7c72c22ffade8f873035075518f4d07be640b9e626e4f8a7f8026505a757c2a753135817a04822fea57f1afddfdb9f96e1bfa3858e32922e7baf90cc5571179c922b79e07c130ab8933578197f2b7fc1af475a1eba5d1ac47c3ca25b688b9f0b4c37d923ceb8c5b5ec7163533e6bb6b486fc1d027d1d22b9a32b2283f1240d01a8856bbc08a78b17cb434f3033f209b0cc2ba8cbb9bc8e9bbdc611c42a82f3e06945170d6705af2c1facf7ba415898a8d321ecfbc68371849fb08c307cdceb56566b4b4f5ec6084bb4b77817dbe04a167d0b3965b812991b041bbf059620840080ac80b2bfd0d1afa62fdc07fd120c8eb8277412f45c24ac526a820026322f289b0721be021bd2baa2a7e3ee58b762fee314e50e9ce3be2931d638105de318d7f40e716c8cd195871d1fa37778b83775d924751bac73872e92b0d785de7ab8402644950d1897e0ed618280d97e212416d21ff82be56fe18b26030b7974a5e4700e177b22ba46a3789eebd6a5061f9b194229f238a79107b1dbbec04591c68c337043faa3d68eea2047604f62903598740fed10a0cc5af5596bcefcbda9860de58958350f995471d7dc52c7e15fccb537218b1e7f04864d5b8fac843d2133afa012f36be528e13fbea0304354e33423ff856d72ac571d61c7619a13566ea4b09463a203d051c7d5cf51c9c6984971a9873cf80f091b1770a6799c3b35729f72160d60038153251a37e54585634a3c9c01748ff8fe9ea41d1aebcd70dbbd3f59f2aa81022dbdb65acb363e4e02a755c9096f09df743a485e60c718a2366b039cfb5a40bdea3a728d2349563791d23aad0251bf5199784de5b8435189ab886bae423061ab10aba1a6d40818722aac6542dec5f1a49b3fbef87617754368f539fc6cefa63c153ca23f43ef24f69a7a95d0c4cbc7aa0d09784d48768d5920616523f88cdd421d5cc6c275238248dbfeae9487b736cdb7e27b656aebd64d439f20166da1f39a7008062161f2c3b863183096e01fa44faa6076da69e9e2c33f47375491e70e2bd62cb86a88e3793712aff00069832a626a881805277670e3a7d2c24eec5aa7c1ff143791a1d523071926b1f6d321c28bee168ee97dd6a979f1f22fdc3a97fc3595f72ad14218cc37c0f979d1e18bcbf937483676d838070a1ebdd6489da356cc09f14fbd9b9b647d507ea5dbd4c1296253126737d9b230e216a9a411ffd43bf2e3dc1d863c485c2f28b9388159f85ad8497928834449b6939753d3f80b43ec12d2817666c60074ba8d0a6638bc31c0ba5cf0a97cc909fc90d310ad98c37049a70f319bb105fce7d91d7e8c085750200331a17a16eca4f366fb04c69ab24b7dd82ec67ff0436098b74866eadf7319bd35f7d37471a55beee4eeec4b93ef6933561f8473844e723db4ec94024aa19860283a97248d7e747a78b32e0d808e855856f8c8e90fdfeb5ec8b8ae555c4d1f06eba403bd9b5cf7d585bf1093d2ac81ffd51540ff0c4689a30eaaa6be10fa2442ed057f23f8857f65bc04ac43082d71233615fe8240bb1e102ce47b853e33c4abfb82fef960b0d0601e0e4ba705e752ca16c0e14962d2379dc0ccbfb366844125a83070b31c5b36ea418487373281acaf13a147972c95aed7575e2fa1b25c07155af632080d9c4c24928193b4fdb3d77d9cf9f9597d75405cb60d234e205d5d9eb18f153371afe9442e19d35e6c4a54fe4ebbdc3b9868012c1f1e0c8ac9986e2417d3ac3c08c2c031adbaf60be28415c2cd6223f1231df4410e777ce84ab7eaa27a8b824051b26bdfc5d1c71018b25628e921ab5cf0d4e2a766e8b9776ed4cabed8521632c0ab2a062d326e96848f65a75fe83152209bc87b757a97104f4f0f9c9f08a9d6aa1301209361a354d3509fa9cc4537e207c92be21451d02bb4a23afa31e1a87f49883524873cb85e7e36a031226d367e3fc5a8405309aa941f0c7a7a97aa4e57f75963534b81e203d03037befc524259de9b0d6fbfacde02c88aa320ab902f86911b5f5490810aa05cfe8d3a5f2af98840eca513d6299f7f1322d90493446988a3ead3ec2c3bb82c025ea619c3b92703590067cae28a2ff58bfb4e86bf45d2adb1ea06f133be180c633643602655fe630ecbbf3a61a979b7fca8fe3de66832db51de31ab84568752d492021ea0db2e9c3ef2cde34993dc03ba334ecdbbca49e0401ca455a06d0ca83c49de9f23d30a84c2945a99da4ce94d0dee2dfc8301e3183a3bb618c13c806a8619c842184597388b818e8c1b30ac9e42031fde3f3aeafbd55ac3eff4a53d54fe5f303abc8fc08fd4c745970c66a30680a0a76152478e309e309098092f6f0609f03020522923fe43094315172f6227682b65e3cc7893d2aa05982841aba1f8837ee3ca64da9afc01a9094584ba9d1468bcff80767a9c971de29c2f593be587e86a164703a189fffb31bb0abf264b55fc3172c6e8c239725d58965d024bcd1c38ae670c7f618ca1a0e3eeae24fd40e25be53749a838c75fc039552057f1f121a2582c7e0ffd7375a4983dbd45dc7de35d0994deeb2f662e31e5928f9f13e1306ce11120a832020539ca75a82f244ee58bd683d81c24b94fac911326fdc28c4ef08006aa98416866397f05438a17303e4fab037fee9da97dcdc21b86c1d5880ae607cdc2db750accda6c27a639952921a7e7ec588c4cc136db79dd1429e0320c0f39f5e9908fca0fa82b614105118018cc0a211b0fdd5704d2f6ca459c3a4a085182d41f8ac0c647ff61df2161b097d990f83548e07864256022bc0874dabd2dad863182dc4d205f0a352c87ac44e1d793b9da4e70ac4629b0dd673f7ab3cadb254b557224ac4afdc13ae590440f134a8758c26b9d2a99896af4b99d1a1b05d283731d2afb0184515351fc65cd39a2fa0ac29fc4b7cad253a90d41e69022d1886cf4107d0e1522eaada2197f9f02c2f6796f0b5d989db2f8449bd24d5f8409bc1254e9011d26cea003f35b1713f7ed4763fbe458c2e5940ac364fbb884c8c16fedfd751b6cb5d6fb726138ced692429f0602d685c4aa4d40fdbe3d23bf5f78c4113e4998e21d33e4503eac5a8dba27d8a92c2584a393d462182e1a865d395bee89aa244da6ea664e8dd23301911813d4a03b7ec1ed54a95c2d824344220855be674ca7507420790900e5b3d9af9c2a5d7e7e32a53c9697d08293e57c5fcb91f6f5ad21fcccd7ca055eb51241c74d0ba50011bca42c207c8e97564f1195dfbeed57512c321f51aac1a1993da055d1e8832f6daf8d12ef04808fc9d206fa101fcaa5fd25f8a1b0a21080db7eff2adb90da0bad1c026e125558c253a28076c714c663042edba330de475df25094b248acc2ab39539f90371c72b41239bc737f0e20851aabd48d6d4a55df49d9cbbac2df522921d3518b650b2c981665033e88967aacced2c10e19fd778cc8658eb9dd8392a3bc824db75d61eeb12b72b2655794dc9fa33e2e2345052835f633fc2d58d7a8897793157bdae08f6b889f887f8a9fad0d231798174aa12c5d6db960cdd05e1c7bbf8291b43fd61036671c8b8c8b2d986e92097637c3df03aaa22b2e897fa666ae0a3a680e42f99014a6c051b0a52b77bca07ac438707f0519bc6b123d5d41d9ade25f3ae4cb578eb5423f5447029802ca4898ca09f20ca54f7d903ab6562252d4ea522806620b280335a2449e054c984c4877c65a0e445242c88f54a390e71b61cebd08fa6beca3e53f08d67562784757661977bf55cc12a88cbbc2f08c086310283bec4a947131d59b05772a8631497341a9f1a0c1017f01fe34f7c5b1c64ba3a94bbd3bd60e39cd954685cf3da1c2c4d69c014e855d4c9b4c245959faf2049637fd86be135b9c14c48d9158b431510e597bd49066010df948189180a738656914c7ddc06ca37c1698333748823ae8a29a23994e9ee73cfea33123e764fbf88e2151256784ad6037b8e3a599260b26654414da5f1e042da36b55435e4de98107b21120dce7f3279b6cf3fdf68c24f3d8b47caca93d7ba122a03d5cb4000f41b835f6079442bea719c92a5b8f11c924c079254a30bfda82c82a6c7a780de40bcc9080f24a69f8440de102c82da5cddd79021b6e8d5f62a5834a29b48694fbebdccc5673e99c1776ccad47d3d6dc1f37935f52b6b18926124dcaa53198a3f469df14863abb5b6b027e2f1b359e3f7b780b5a5f740ddf1eab61cabc496ee84bada188e4758208004dbda5dc0dd7f47fafd705a24995af88e2050aeda6f4df7244a0b867154a86432dddfca20e22676676e39879b3deed97861b7eff260719b211e22df739b9e1f7706695c461869ef1472a10d17dcc001b28936a14e8dd583ba1a5ac1c9e3e4a194b086004b127dcef6490c76d7d449ba16b20aa8ee4ebae33ce5c0a98af1a67de8d03721a18897f16e3cc259feefb26209ef84332829ee17d53510b8dad9f81e2b466d6fc3e72a982f722e57595073ba48e47a2ff3e1e200e41780dcc8de0527b10b9e24efae8a4b7112b6ee68aebc7011087e7b872f14a79bcdeba5157aa514c28d1abc5b6269781686dd2dc5410a3ba9ed349409efca050ce206db88fa2f03dfb4633205a5357616afc86d84b83e0ce8160c9fedb9b8ac460b24f491bd1908b214b09e89022eafa462a689b1f126cd017c2f8b5824b1f285ee7b4867d46e06c7762e9ad95b1719caf8d001404a0621667d52518740f8a808055e06f8c713ce09d9fb130451026cb90cdc429de296de597230db40a428c5f89cc1b41160793d982cdc3f22764776237f53fbfcaed2bb519a0a8c6c1be1099275daeed359a72f1cf816f7d71ee1bf4f7660b4248d0878dc96acf685daac70343b1d5025309f5784d4fdee8952d092d9823389f6489c376e2744ea98332eabb5500d55b6df27c115e685e187042d4c4cf488a10fdc256f9983bee00c069fab4b725be23b19a1a8eae08dd363c7e8f2f8edd13d91163d6fb6949d178486f58e822faf500c032ab8763c34410c72ed699068c2183106b84a8b98a851737fd53d11c0d8fb252e0dc1639473f21f33dfda718e26321465edb262293ad01a4a6df65b6870b797748d3108695bddb4bff95fcc125663d7c254826044ffef1366cebfaa19a88bf01551ad768e9b05ef8db74e2961a81eb3a588199aaa74f26bbf790d556843874da46bf7352ff48a4a866b4ef58e39d836b9ef3308d9ca146055ae842030ecda5deb5697e00998e32cd402ac07e4e0c3712c44dbfa58b73fcf4881ab161c1a3f22c3582051e095275b8b614e5c180818116a2008a81b340840eee27ad36ddda09fef8c94fc005ea0e7f06df561fdbe77ccdaddc3dfa969a39f97a970bef2bebe79cf8f2f8e3342e13a54adac52ce377bd89c4b8d50946722a17f64701a93020f9c2121a81ace7fd504731b30a994e11cf7ab905f91de7618c17660dcce0ea16596f3cbdd84fed728afe093ca7cd527a2aa3f41b10b232a4d0b6450d13e8fb6f810231b746f79867b97cfc5d24d6ffc1d375d2391e6a1559ca8522ca7c3a5399229577b3610311843d1d933ad0359d59d10fc0a19d88fa00c4bdf3a078221006753a46d8cabc8872a2d9ac75c232d21953e4ea01768c2c98a68272cb3b4fa6f23f6ba5ea0eb9e27bffba444d2a4be1cddb747b15e728e9604e3fa39f5714811883fcb056679c89fe23e3a446188164b8341c1426a6c4fa8b39868190d820fcfc198802d4cfd4ebeae11474540f2056331ba269c11007838fed9668e1262e9f3d5040cad52b5aa43313c91131169e2d015281be27f6e65d859916a0c7a4958f91dbd8b9af0d6a7837cffa469e73d4bb897c34161e188c661de37055ca7e979723a370d17f9adc69757de5959ace8f15c4ad77e8e259625446331c7af482884dbf14767e9faa069d9bfb1055196b5af8e4e0e6e72c58d82bc9f8053937a7b35b1719cb0c1e3a691a049f6c58eb65c8b9466220ce48b1ee0448e4b78c4945d9c393d873269e5ebc2d136d319d4653614bcd03f0ada3fce9a33f3ed2159161d6cd887a2ba529c2a97a04d05b88631d7a61d83aa60a61d78e2f7cbacb438a592e44a7892e064edb939a06ff724317b5aacf21ea8042bd3fc577c8464ec3e1b327a0184c4ac37511fe2e058ad0621ee51ba5432202137ca46dd8080a13e2c767545306e306b01a0f41efaae0b28d613f202c34a88c047d4a6dffde24885ce460595ca4c1f1bd2d92c38c93c6c6d0f2e256b5da4d28db5b15eccb471f37a187675ca5991db3d6c44cfce9cdad21680dab5508de7735c3fa39a2d50d5f82e3a819081fa3f749e93f270607ab566913a4db8c97b89d43e3cdbb467c0f28ed855498070955413eb8aaad1ad8784227b93bd87b0f694140ef459d4640f2110d81915d743d9176d1a8190d71d9d527a0b753a2365504d39122967b6142f4244c54ed63327500fea1766abeea8397573e4719b20c7d879c97a090930e54cb67169739dfb44f340bdf9913a69387b90d7abc729a71c8c531c9df2d6f9a2cfb59fee49905f292e0aee9207b055aae35e6aa52a64e3c81a8c0102eeabd2f9f4b0bb7e4fa46a3fa0c2060124e6f5bd33937bd56b1985bcb164b2cb0dc08a15c49d753434117b5c09de0559740c320a231d0e981b2a9bcf0ce7f3ffe4aca759f7f5f0deb62b7318aa44fa77376f4886caa2ed1ecf30fddfca58725e98fdeac7d94bfd10888dfbdb8e38f5ddfdb0a8b2ff8a9246011e9e59e40e412dbd629e782b5b048a80ef8853c002512df0a9e8f285730029c5c6155ea684c9de551894384a261a6e3021d1a69123c545fb42b958dbe105451a079d9c09bbaa964c8757c9aadda715cb4c22b19fb436c8ba8640ba6673aad9cb76dd722895fe5374d05083e667ff033e40c9d1f7df12dbec8214b40362fdc78f77c274108bab98b872ff0755a5c26761d4d12666899dc8c4583de16f332227e78160a53b19d9348ef6054a8d939b0cf72d616e91ce94fe2e0bd05bf0c37b9464b5a4cae853a1be91769c45623b8727d9295175c07756e9b4f1b07138e5b718526a483f7d0b85b0b1f5ea3a7a5621094450d1e34a50f2ec0b0ec5aaa53e517cbb14facdf5e9f75e8a8c3a9f54b74756babb225abadd5c7cc2cb5b467ffaf00c358c166e04f9e8f4e980d4c0a6e626213164116944fecee7243a267a3fa8308830c8646afe32871bd8b773d5510cdc066a2bc48d99011cf4aa78d1e27dac354f529dd5c66c4e3dc3773832e82d0d8d7c4b49de9e2e5a81e47bc009456693526572a65c1873c8afcd7931a4552f39477228d5b58615d49d3ad9f799ed1bebec82a8df5b16c8726ea74c6c600c08e9a4a6d8006823a56bfc02e3fdd96142e43257e5cbe842295632ff0337a31e6a364deb49de9ece13aa61d75209ac0e4010193ab108f6679f3be5c015d03a54edc8c18bb6b8e71fb9d6b380f5e17eace1afcffd2c9021d077c53ead4253a7a0dc013745b29e699cb219be9208a0237caf6632853e9a9abc8c5ac5f421e9b92de5f864256f3d3959d565736631c7c11eb1e0f579ec9e7c40977c3f344fdbe220551e2b2f8ac8a12444b1698dbb72ce1b6a3e073628c135ec349e6d8ac03d99d6716dbb40fe9d84cc404667e279cbdaaa3e9af957dac16b2de553de3968c800bf59a745ee237a2d0485adc884a423359a426f6098b675b973a0d7c103ed7370891ad74dbec93c413d4c797a0ee0b94f39510dd7a39420dd7301f2b01b597488ad93c034493f695a2a447cb201f7eefebf0ee6daebb08b3a84534345b94b908c3ed87278e29acefa79bcca89aac41f232892a598318628aab03b742ae5074f936faf5c3e2e09bff9678f4709c29206863323e18fab567c50909f4fa6522290aae3ea02bfa6609ea6c2970b7958ba3240e9c8e6ef700411c4665ecaa913fe17ce71b311b6b6d1c41e0c8aa324efc2a6af15618c480a60acbfeb764e34c0280ca6ee45f47629659b47a16c54d81c535cf5f5302d7cc238e39bceca6ff82201707032cbf57285863e8e689bb3a56eb614ab136bfcdb0b67a5f812ee5ea660015c8e221d2108653633135fa355168454b80172d8b6d482432586bdc25b032806e756c0b7b65fd20f410d3b5b2b5e6a3fddc47ad052781b375fb8be63489f9a1d5eb5bd7cf99febe841db22d2c893719ce5ba28816a60b18a18d5aabd13a66ffc8b3554d3dcbd28a90d1ae0451216bb431f0675ceb9bd4f4f334c2fc6c0ecf4b0d69f7dc76580468b491f80bccf1e7282154d6975ec4487f8ab6778fce07f52ff726dd0abdfd35e51feb43fc6acac3572bbf65b1483c6e35a1d698d015a40b5c5b0d57396e61ecd596c2f98b6cb1d4da687dd16a8594081de6fd3fef25b4cb9fb0338a45d0e3df76f16e6e78aa7f59237ecb305afacb1e436a6ab652884fd94c47566eb1202269942de96bbd00237a24429ce23c242eb4c6b8ce70c688c4299e9f0305ed81e2817fe50590a9c54bbe0eeba309f9e99f11225e03fa2b9ae05c47d53b40529d35b544ab637165c7804e1cbfb9309d3fb727c23622aadd60dbb32643ecaf4d98abcee95b513838011bd9b636107e05e8fcd0e484ba586009b92046764ede7b316335b253c34d814cf2452c8e0c0594b2e1165a4420ee585ea42772199b1b17ead46aad241a559864d154bab50c306f52bc7b7c69ab0158b04e1824eb09ffc79b1fa0d12027997bee517216d17d8e066f97e07e182369bfe60c44933a7f051fc88555f40b50de42021ca18e35834cdcfe8acf6e9c39a29290aca24cc113fe99e78f5586482bcaba248c63cd398f95ba87c8d53b7e3cf3b5711c28b18bd93a485660c0e5f5c5f407d41c472076620b9f6018025b92a854cd3d1702672cda5ec3e356cc0b5301531cf9bd82a1905d56a1a5afd3979a45468554832409050e822a2de2ce02fe99478197f2010c1aeb086fe1e6bd7478fcdd8993200c25fed62ef0450859bd1a013783be2c08af937e3ff56a14091b441bc33220592c2f63dd1f12ef94ccc0ef20171c45b05e2b5b6bbc3e051e35cc81ffd7624ad65d02879bff9854ccee30e350f353792841ebe799cae7dd693c83df8458182086dfa50ec227782b54bf5f4d278d2fe10db009a41cc8fa9d63269457dc6ba60ff2249ca53fb4ff9581496378155b3d39b34fb10ce461063140d8bbce30fc1785be1ae576472acbe33869ac8a489a05f2ba0fd35e7da0c03222e0d23fb1e29db15abea61f94e3607c58a57bcb457a58b6799608300a5c2eabc4ded0d418446f0ebffd333e87a112132006cadae34615bec7560ce5ffbbd7ce01bea23df6b0d1574c572159e204d55c9407bfeb008dd941f13f487e168a8f2d4c27af42a0057ff022612c3e8a27ab1a051dfdb05d1e1addb91d187f0ce40609f8103a8100da1ee108493078a68645c3875a70e10bd24ec7698787ba950eed05e6cf2731ca4efaa8f18ee094b96c5c6b453f11ef6b84e1cd45573850aac29b159f2aa40d054f7db802977f957b2d453713e18f5e5787c5505e1c0c4ab92c0a8b78f4a32e466714c4ba1e0fe5ef3a9a277b5e01031b1cc4f5c043c86c9730363c4e05e0d01fdb29620338ace48a4322e62052f90c1a571558446357f8278a65f233e81c68052af89022a6604b6d000abe5b642ddaeb9812f0f988630a9b884da8f75144d8f230bb302351c4b3ba064cab12180ca43a642c4e5ed39090f1ec6929b34231bd8360fd31d9e5aeb857fa5c4c80c5ed33d935c20cd6ff26db130b34d14d44eb9757bfa472386687b7cbc7281913144543bb4b852c414fb152d5020e6641849f90b5efb4085f32baf57f5d58b64f0ef73cdf10158110c736de2f9d9d3fb483df0aa79cd7b3a04bc4801da2778ecea4b80b5110d1a5fb78e03647a02a5953a8a9257888a4d3f4740a26e4754bfc3574958bdcc6f59564e92ffbaa6a05324016c7e29a177ffc2df86002aa9ca42e4161d872bff04fdf72b598e399018d39553c3eaa184fae2e2dc2ba22dba82f71a23186a6e2d0e65414be5abe26f74cd9cf51970c001be0bbc14d9d6c966ef710f617537e2abb40e2658e53cc0e91e2b98bb5130615cd164da1c96754e6691045fd00d63498b357dd183f4efc982ee03d2f13d1f6a2beb9f4736a17803dc24da4c92d044d721b72a0b6ee3c4d5f62e501daf2f1fa925769e4dee2681d1f2f256965b3128cb90df10db014ebf47638c0825c1548ea356d2a5fb42c02a1d8cf21ae22fbc486da45fb05aa0f8f6ba158c97bebc74c94b5f5f91d794cc1c1c2405d23c0bcbe7ee57bee8bac24e9f0cc8f1c69d2acce58a8abbd2c85a935cabd1047f24360d3de007ae627eedcc7a632c645c0a65de10370a2178629c261347add766806206ac385e911339b713685a0dbcff334ab333ab846a97c70a95156119ebea8785a4f89df8ae888e76b5f5a518c8b0defd0f06445632302721df001a90ac49abba89967d5b6a489a7cd1d12740284036e5ea65bd52f6df5d3f06c19eff3d0ad892ea054656e286245f4247e8b8bdb4dd632afbfeee9c9ac622004efc4c7b086c2c175243d8da7a008a739656a9bf220c546949743afc924cc480d7844a3b6a726ef502dc7a96c80704228f8339f6e964ee132891f19be8ff1ac20092085e4cb03eadbba925b3052b29a0e6a71b4e032c0e89b58075dc1fce72d688bd1ed2f93e8d4371af46ba82ed25a559809d88002b853b1465ba0f573fef9335c37ce0dcbdf748b28caf2a54f352a77d430bddb97171aa9c37534e92664ef2db794322599021d09f408f70841541f1e9c208a6d66eb1354084e6812cbd1607b81a53d52d8ca33c1b07507082b3f50e1616362e9e5275672985368a13ad87a6508bb81b1e5c0be68b14111658c2fa0dcb056f92207449ccce08ba12fbec85921c703926ef84128ddd4b83829344b8b15a4805b62c8d32205446a52b7e587cdc98ccbe24a0f5489a09a4510543095d8d96e6e16433e38f9f9896561c4912578b20802889f5a16b314fc207981b46d1d9dd26f6e52c0c1e105941bfe8b1415085534b961bd11a30a255b0facbdfca40a209654b1c3a4624b65cc262c4c1767d8d3e5273d9072c35aebc113da834d5d7ed28323b88af35e040d69f15308de1418cf4b7537cc8b4bb77577f793bb9b4a5e77772aaebbeb66bb73788b8740dab3bba5b36439e79452764479186395524a30ec7efe4255774ccc890990c58bed299713664a9ff4856e2e9bada81c6fd920ae73a1b9cc413c7499a8f34c5ea954327953bae9d44de9271487b2d5ba6c2e2f3487ea06022e012ed7eea5e6f2835d13b3d14a9f2f30734a8749c9548cc73007e1bc6ab3fdb5ca1c6ff55798973e6566e28c6a5371dfe47cf65fcaa4605e5cec29e76d20c163e60e521a724b432e839312ede84ea1596c837fc371771f22f03be59360becb95574e508231f79b3c4ff2861ee2ed9151e3a652311832fbb309061382cccbffdaa894524aa6e12294524ac91fd906bb7467d1d9db8c2c1c2e07345cf817b238216cb55dceda9974ea77eef956bd8e4ee9754bd48d4e597d0baf7ec94dbe55afa3537add1275a35372be85e9d4752e28e2743aa168dde8945b6c1e7dab9bb5269dfad65d7cab5e47a7f4ba25ea46a7f42d267df1ad7add94d4eb969073ca8e6e51b7295f5e4c3af55f1cc6b7ea75a692a9b484d72d5137ceb7808131e9d487f1946fd5ebe8945eb744dde8949c6f916a01b327eb0fb9dbe97442713c3b3b3b91889624156bd2f1b7d2454eefb8bad18e73e2b36ed424a9b8bc705c8c3c34787931e9cc7f913054d2c9451b78a90d86edebbb985ef040685db0d7878376d2219f2196345460604c3af36152914e08f48d2793c9a453273da552261dff948c91d33bae6eb4e39cf8ac1b2d492a29930efd549591d33b6ea3b5e39cd06da3a54ac5e74665644c3afe3272464eefb89257f29c749c139f9ea4323363d2f19f912a39bde3ea463bce89cfba514f525179c9dbbae6098f6bc87639b7da759dc7f1ecec54af542a99ea904f9cc562ece854e278c4ad2f3a30fa78abe3183e956685d0703a9d5094bb8fe3386e0855429974eaa3e48d796529ba2a51ecebeeee34d659d4496bad8b1cf291a1a847674bd6a433df6e2fbe83892c030915c0e784a942148a28f4f63e413b82724c3a24342474fbbbcd27888261cbec04c3a825e8e5e505a6716aa6ef59890fe70606c6a4231fc6d4425fff947ff5fee66e330e459f8842bfb7ec1246b2d2acfe2bddf2892ac42a8c427fcba296db4fe31733facceb603c621bfd344536c575dc89569bc68a58e7a5bcafa46a06782fdf56cff37a6488cd6845dee77427b7ee3ed68e8d4a18232c13e76a4ad50ce8dedffa8bbe7f95fe32f8acd5dddddddd5f06f7f63c1c56ceb7e393fe3032f0586c9902296b764707186781831b3fc6fff827b4cb8fa0b8910706aaf870a31729922d5611c28d91495047212e3ef1020737c6a496b51763fc1a077152cc2746a42a45b8de4bef3b938ec799743aaecacee5be9a74b81ae57634c5dd9e9a74b63a4d3a54be6e585ac0510f5df4e04e1702ae7cf72b9cc8af7000388e1be2b8feb25dd51a7d71fd5544d0c1f5ff220871fd594508e3fadf7817468c5870fddbee5c2f725b6aacd5c8550e51824516a32a6500b1822c1c06194244c8a00844499e80458974e59529ae94524a19534815e69205bcfd76470fa46041055216242d60518a006205261812a28b30c65001a34c3a529e8470bbdfb4811c643f8c6146104c1001059a14498486aef8c20726ad11ae18a1da191b949e1587a0c4edefef6a9618104a4c998203241821e5b3fc859a32840fb12a589e3c81c9f939a404e3ca20cc9b7b938234e978890b1c18800b2bb77f33e97433f1840f543005133920011b92821d191451041354928004ebe79089dacaed5f21470cddddcdcc373f4d3ab3ab7142dce18629e060a090adb062622d57d2f048841631892b9fc3e8441070b8f2e557ab7ad61321ec70e5e3e4e4d841e5c895cfa3c7932b5f8619a8b8f26908421631a2191522b41faefcb8c343054b4f11141f22aefcf8d32e7eb28511aeac45618613d6429183dbdf4f93524a69ba52beacfc62989d22c4ed669a2d1e5f96de1f68c38c752f9f4707ae7654eeb64a29a5ddd6d599d80fadac3e7d19f7ddefe0b1d5046155dc0d2cacdfbba718317ec9767818fdd9c3aa0aaa1bf955a35b525e5a11b8a4fea9142443cf6dfbee41c3169bcda6a1470fdf70d558118d16b93bce0e3a95d8908b38d0a35df1654c42a19452ee29dd3a4a69f53c4a29ad94524a2955954a94d2f77ad40da6c90769177dfa3330a5325a2ea54f7f866fcca72fc337e44f1d18e06f6dba259fd50ce897ff2a46eae71d0dae76508e7bee87e9e168bf6fb2ebc09bc6f1967ceedbd1c35bf2b71bd65c72330392adb1659147812130f9f13d989e16df38ddf82724bee1baf1eb011470e3832518222cf7f16f1cc773f0f097f7918b3c90270201e1532405eb44030a828bdad64745ed62a0207e6e7c9451bbfa8b1bbdb861e986a8a21b1f15e42fcbc2e9da99e5d58eaefb5505b8afe95860f9596c847473e3132d9262230d6971c3146ee49962e7865c9b32bb210b0d35a382da153e4aa85d5da59de54e7fd6df64f2d93401864994d077b3d54925cb5b113504a28258a8a06e88372786c7b6c6cd3f44dd78637371f2f94dac823f7f2cb1fca3030962b92f88ea72f1b9cffb9a51f0efdcabcb19ab87f34af9526e5f9f7e415497d6707b4af9db2761e80643afca215da44978252a2cf7dd7bddb7e2afafc31098f7dcb7e2e75e8721b0ce7e4ee987c0e2e7e8def46ff2be040aa9ef81ab8dfb0e5c6d4360cff63f7e5825e8aa58ebc0227f82cecd5f328b18a5db94de29e0e090b9fc840a174841a2d6883ceafe1c65d50b70bf8142be1bc2fa8fae0663986d970f7f3682818d90eeed5f61c7c78821a21ddcf08688e78638a4c73eba7d751cb1e261785ca3328c8b617b0a0a91b1580cf439500895f23bfa1b2844f56db2de720930ac1bc2f64bf911c62e48b0728479f4ab5b6cc92ea7b00401dd6ab08bcd8cd1c9be6e36c600be489b47d73f7a2c26057303d2f5977864adb528d4c78016063c7de844210f9d1e7c7139b99cec490027d42900a793cb7ce6449ce6487cc793384fb3da6347b0f1438f1de1ce213f788c731eeff11fef77e9318f3991f498074d97d3e594130095864afb7a8584daedeed4cdee9d3d676667a423eb2d74f92350bf5e9e5dfe058cf5f5f20cf39c7ab6f6a2f8c34874f9e3904dbb36effe6442fdb8a8d44581d267b08254d4ac22d4d09315a42bcde2eda7fbcfd9c523a36659418a5762d10c52df0cdc9ad58ef52c16795191eaa7e73612edcafadcae2a3fdeec80f9aa8d2d4ac39532c25cbec8adf923b8ce5d87d54e7da261c9014ca453d4886589aaaaa2142c2de85c9246c68284b3dd81b5ebacb513844969fd1561f2072c3ab41019a88e9d0f6b41c2183f52c49082040ce72cb45ce9c286499e7841112964544153b18d1e6ca8bade9d8f512c168b0d0d3942464d0758ac406184a3d849b862b389224a1133bc3872c5143029a594aa67dde0e4f81131f210a59bc9952464a6900cc8d811e201071c7a60a1aacb884730a9e0111f9820820d7a6ec0c5e441123bc504a68002832945c85284056f91e58a0f281f56d4a6b060883384b0d0299020f3ec527a16e5830dad9d552a90e40a10367892840c32889034069195488cc042085ba050628a113d060d68789a878b5453b102a32ec698a26527053a8085372c05094f9e8c2a39c03286124eac80c9ea2fd5ac48d850252ff7840d5527bc24aca5743f2232d1449d421115f214d595373786ee5e016741032b9fe5b179c04ff4735fa5e4406e5665e9fecd3282c729d8d48d375567ad31a66e84b93e22d18c439d4a828d6f4a82ed0fe38fa908db2fbbe5675a621bd503db1f5bba0be1ca8579338493ae044309d67070b583bd156be18fd47550485604db1f79766861cf6efc6e241b8bd04f2b27c064fd099595b13aa8bafd95ce183fd9cfeeaa2e584e4e00aa0a1b4d5aacf4c832b6143a6d8ece0a1bd61cff8123e9b64dfe28a574db36ba510a0291778241e68df20a29e50b141b65cf295dca292537cb49f4688db0fdf3638c6cbfb7c7b73fca79c31f2bf7b9d3e7aee40fb9120ce305c011ac4f767195ccaaf136724a295d35e59ca966f1a4d2b9e90f518232c61b7f3e15262db6bfe5ed97f2abd259e83b411bb6b1baf33ab87241be833f4a3e34293871e85723287f7a35c69741289d1394b54a29dfe7fc6dc666713570153318523a5d7a758ffd35c66e261e33335b1ed42884a762f9d766f177c7dea97c63fa26eb8704b84f67f77ceab8ba994c4b94ea57b9aff3d8467bcfb2be2cc1759be101e10712a4de0fc42d9f8ef7737c8e36783c38dd71dbe43a2f47a5b2b63fc76b35efe1adfe1ab3b687ab54563a4a4a97a520d7519fd342a494def48deba49c40825859c3f558e7267d725ba2b3fc15d4fc62b160f0973f7db0dcdbdbfb191505f519d63a7446f732e6f08dfa1eeb4f7fe3249feb19a24825e55fa5eac01ac1cbaf18b35c0edf68eebb8f2618b8ef7e9a5e1841585da89c05b8e5af1b6f751ce208aa2ab77f8bdb5fadaa2bc8836df477516cd3bf8e80a99fbe5043287ddcb08d172776c86bea032f876ff4bbdc78796e425fb64385edc08235d9629e5fd5afd4a39e3fe46ee7b1148ec2502eff8df99e5b2eac90c7b530ee2e3060d8e3da8f6f3fc75f5f3fff7795cac5c5c604aefce8bd80253659300441ea93dc2a05b9a72f9a4a39ed9e9c9ecf29e5acdfc628f89c734a973509afc25c2ec0e50f5f2e7f488099e7af93d68dfb57f94bf5b22616f53b54d88b0bcc7f2096c220b7bb717afed0e5ca3c3fbbea93dcf24f79ea650a0c85dcaebdbe6dc3e57397a813a3e0e281eb5d8326b6d1cf2720d7511634a1c01004339fe45629c8e963b6d16ffa22dbe82f7dcd36babbe7ee9a73971b4521faa4ed74fb98ae47cc60d84e279573ceb942b32828a5946005739a25a3f0b806bd92a8b7db7d3f67b9b7a19315e06ce8e740cb15a836b80dde731f7fd6aceeb32db01eb88a81eb0f5938ab1ddd4b80611dd8fd926ecec711558877fecd8c5ffdaa7f2a6fcd67dd49c3e706478e18ae4c6f4e2a236d67ced28cb519049e4b6bbb5297ffa483941ff772d659ff24b321d77ab46b7bfa75851c3c3030932efda9e5d279a559f446a490ca86c0fcbbdf41550c9e107f0fb46cd953c97ecd2bdda29f324da400c4c0b2e8e7f00d7ae9c7d0238f461efffe6c0b76b46b250186c92692b7e8af563bfc25c030a7b34bbf02fe4b1ca4316fd1bff463882a449e9c1c250461196680a0266e1083312cd620ca113a58e989c5600c63583891eafc55addbe7f8cb269623861e3fc2987ce5524aa9479d7edd2652bb424b63ed0a6bcc2bed6a322efd8905e9d22b37a4b24b6bb46b7e539e8a25e5c337e2f58729e21b32d779080a223cf04099903f453764a2eb42d87016dd227fcd1c70cb5f8aa32a90eec42209c80b5c08aeec24100aced90fdb7020f1caaf6e349ad80e113e4e8881a40888d693c44f2da8a85d613d6a57b819156d7ca38bccc983df06c359e47a9d5d963a60e2b2ece90fbdcb0d02892db0f3a377a5651bf28bdfa4b10d972d6584a111317776da155a9e243b4832154b4835e6f5c70146123e6358581a185b6ec80203e886295cc7e15a7485a8c94402496c88f2f1f117c36689056f7ea273c2f687281f5002466c6863ca07a6e874245343e2391d9d8e84b0218a86a2f96bf678cb5fa5b276a69241d76124141a289f1e9822b6e13dfe3a55e196ff117c4e59ae3b0a87da3d523961c3d3d1f5af73c236fcfd5773f6f86bf24c1f276650bbe6133288aeff4cb95eb483d92343c590acdc70d6907c6e388526d1f5eea75d21caa7d6aeb073d239d1ae1ee3ba18ddcff5f8c50f513ed7bf73c237fafd517ca353261b9c41e4f2132b980c5d7e624613d754eabe3f3564f9460bb9210e31c50647b026dedf1073008415c4604d7c74ef7dc798a97bcef4217d8ffbe2ac59311fd8eeb9ef628cfb9652fe46b9ffe453ee9de3c09cd387a35931e6af08eb0dc479014e1465f470e356e576a62fc69a2553c2e6e8e68f9beb812b9fe0ea5390f734bb5d4b34586aa55f0e4ef7e1a0e1dcc6d96238671871a3e7388ee32fb682e70160c424ad74aa5581832861dca3295da14d98c2cade548d23d060b13811c76ffc16fc469b76c52f055184188a920391213184e08ea835541bceed90757b0b135c3c620b14b7bf6e61b2f9b0453ae79c73ce39e794ee3e7bce189f9b5537e65291223ae50f39f52aa7fb94564a9fb572954cb7de6a8d53fa1051ad9c14390ee0d7b681967960df6e4696898d95cb611b916382ce19294f76777fcaedeef3a9cf39dddddde79c939fe336b4b9bb7becf17e7906ebeeef32fa9ceeb4269bf734c2db289d73ce2f03dfa5574a175ceee459c3bc73db5c5084b4e26d19f89e9b5e2e03df7b89fdb2bb7cc1f48207ee7ca67d257bdd7c174eddf8ab826ff33c5fabd7d5a46c4dd89acb4fb618b969b2c5042cb0a72bd40823189c3174c4194fec9cc144e88c24392be47840d2115e162941d46a6bc94aa1753759a050c14634e3b220f103bda256b3f01c9944455b162248ae40ca122342e493844493f29324450558f4240515c182c80b554709038a4552a96c68398a2d4b9864ad76c8005abed0f2c47555554591050b4953b7842228745cd5720c295ca67777cbf825e105954aaa0c24b248820fb72d106c96306ebf6c6e0b8f99c1ca42ab424bc3baf9b92e979f241171bd58b38a2ce0570d1891587948640bb0f5b9f766b7c586aceb0ae057dc711cbeb171fee4ba471fcac51d16b85b411a128efb5aabd7af773eee4624f8258184ae7f189ba8b7ebb4f4cecee4a92fd9e32dff5a9bd63b487aa759ee39c7a13650d63c88de50eb23be51dfebd736f7232091f3dabf91b4abbe67e99e7b96edb68fd52c1a1958be153ca159ee42f7f539f0c604c329b4a88f794f964e2f5f93c75b5edffb6670beedf6114edca9f1f651dc893b366f576f63b35ad97c74f999f73c5a5700ebe3346b051c49b015c401aed02c1c1288c75fabf715c8dd7a91e00a31d8fc0afc123ec2bc9b1b0ff2ea2ac85aebb3ea77533d5e0ee6430f9240d1e7a527f59e7a0af670a0ac9994d8d5db9c76d5ea269dd2d7124a89a51fca1a959c5cd1d50b6952393094e1d65fbd9b74aacb7b6d8a80bc3e562ed0f79eaa7e45e35732e00bf141d0805b5fc81058096f430960fc8645188db7a16134dee669a8409c66b93cd00788e37239cdf2520e0e0e4fe6bdd5c7defa52e9fb928ffa7c77700491ebfd7fa05e11d0dc5aaf34d50f225f6880bd5e046cbee62f1bae7de27ec5fdf56abb66c0f6519a51d170a90c4b1f4b2ad00519301ce2b1e90236cf7d2530ac979b52e6ab1f47b98ff5a7ccd72ffc5b552a6beb579919a7a04e048378579aa957268cdee774f53ea769a5c1e68bf9626ecc67fa54ad165697ef87ea86d5f49f4db3bc7ed4c2d27ca8aa757a5cdd9e4d11d85634dcea7df1729c8c9ef749c983f5d101f97ff9bb5a036e5d7d5d3df72d21c16ab55a7d0597d46043cddf0063d38e152824c26a4016e86f3f9fca1dccad49c31ff31023c9349885c7c95929a6240edcea990aee76250efc721eb8d5cf71a1cf686ee8b350c67cc6369a61a1cf7c03fc2a8135b077434e429224bb7ef90912ceb81e2d719c62b71ea6d090c445dc75d06beed6c02f29eb164b22397814dce1b516cb83874feffcc85b3b782061bd85501e790786759a886ce8339f4d0f652c82e18fae876f84f14e7047b31a48bc7506246e32d6d7bbc75fa567ef815f2550cada03b95bb12337bc771a8a9a49b3dadda7cffc9a06ef4702fa0cc84a24ed0a7d1693cd2491768541b62b8bc99916d6fb50c62228634aacbceeb1b71c0c65b8fed2e30fd52d49eed3126ffa707daab9bb374b66668eceed31698608dbcc36ba670d0bf84503ba13ddf2d0816210db67d27cab979fb56cc3b5d819c2d44bc82fa7757737cd5f72e7050c516058fde58ebf7ca7fb064354bd13f41f22bb62a1de9787f9150bf5fec0bcff0c7ce3e54b3125af7d6aa65a44c13f15559046315d7b81914632312fdbb66d2ff288e64379d43edef3aaa901bd279a58e0ee0b0c0cb8c4869aa769130f9a9a1a9a1c2fbd7cf5c2cc3ccc0ccdbf3ccde7b44d598bf21e193015035e976756411a4514fc5dde83f9b8ca236ff9a784fca55154210e45145ea451b750852ef7330e7d0a7c01b7ed72edba1fbe118b3a1f6ccdbb13cdf2f71e7fd5bcbf3cf2570dc8dd3abdbb3c6a81bb0d3a4a899defa51e8614b8c4061a5ff3fd3c6a5e48131aefb129061a207beba5e687ead63bbff047834b6cb0f99a186c405efd0e4d3ed0802bff0618576b55df3fdef2573561fb7fdce583a001b73fa7579f9008a379cfabf9fe96d4a0c31058cdcf6f09096a4021112664071b4850f30d800d81d57c8e7bd1c4c21212d47c834b6ab081e66d80b169470d28a46134ef349fea5d3e94fd0112a6aeffa46ee83f40e4d1f58f13e418d3303dcf249052c414504881d464c6670ce9f9a20647f800e5080fa2a852b354cb2b2c250a2d4b52fcb500978fee901f7aa0c8a89ca007275fb4196210d15df9968746450c8a82d8810e3570b0a1ea6f64191ed7089f61dbaccf7d9bd5d9f1a3fb384429bd69163fdd362aa7b452d250fe46299d9482b53a159694d6671695f4e76dc26406b4eb74a4bce1b7f485f9b4eb5ea5b25c4f08743f7f9a5ec8c0ddbedb36c91302f41bfc52b62489889cbb577102083d4d40b9020a285b9011230387db5fb154b933979f60f101e7030f5545ca966ac78ee251676666fefcebf7811b6eadd5c59552a04ab55d5115f136359a7582aa59a619a86e98a0ddcee999cf6953dd4a949656cf269d9a5fad40100469d8d0a0f17ddf67420926702513bef4cffdaf9e69ac5d2f1f9fcada05f3f1e9ac5d311f9f126997ccc7a7b476a53eaa76fc45009e76a18868974b4f013e3e1df2d7003eb63e3e2da2f4a85df6e3532bed3acd7c7c7aa55d4b660f637dfc8dc85f00f8f85b91bf6a7cfc1ae4af007cfc1abf12f9eb848f5f8bda65d4aea376596997e9e357a47aa55da58f5f93dae57dfcaaa55ddd37e13e7ec7d32e133e7e4744bb4af8f85d4fbbfee3774cda45e3e3773eedb2f9f85d12edfa3e7ef7d3aed5c79a8fe0c7ef80da45b3c3cd6edc8e6efca41b9ffaf82b88a888c63ce6ad26c0daedff92b8fd6f420944dc7e1a5fb36a3e279ab573c31a7d87c766be25ac473dc17131a828e8888b9df031b76666663ebeea73249e692bf12d9974a4e7f232ff02f32cc4fbf2392d4d10a87706848991497d4e6b562f29c09b7a0af0b9ac5b28d009f0556eb93cbba03e7eeb636eb914f9cb9d2b72aea85d68b828d64713042aebeb640141d72f4751c6cea54f8a6ed841db26ffe3dcbded6b77ecf8bbbbe3cfda95525a29a594520a863fb6cff10d27a759eccdf9d5fefd5a4bad83826a41b51996156ef1951576d0b2c209726016df70e6f6af8860c30e8a43cd725a5d68de69405634ddc0863f585ada85a3590de2346b86829f1b3691eb2622580a4621202a3aa85d7dd4aed942eda2cfb7e9e710b26a543336cffcbde32f1b70f51f7bc73a9d325e699647a1e6766dafe5278bf51f544b2a7ff5abbee687c0560dce3ccdaf9ee7af40d5f304599897fe0c48c3ba6169eaef5ab33ca8e4c2403a583183b7925a4bbb5edebf87dc5f33eff6e43d7bf7ecdcb3bbd80e9aa9c2863873a80233bfda41f334e08a857929a80287c03cce6bb1582c098cff86b9e0167bc12dbe2ca0400c6ef165010565308b6d6c6e8d7612206cf3502ec9861d4403dbffa33f9c9c990c18fec780a18a0664df692ddfccd741def298b76afe289fb1de0b02831af4246a5a60fdc77d9ae55dccbaeafd7dc75f341f5a7f9a2f87b754e08e99164a6a967f0735cb1fb5a483660e7f56d04d7b2dd5ad14540be39f758b9168478ed7759d2ce6bbd4cbbbbcc97ea934bb2fe693a53e194cf7e0e7d2bd457ddd9f3ed357f2badad508aee348417707e9fb905bfdc21fa528249dbef421fd8b529e3ef43e99e9bdf7a494b2e47d32d47ba7aef42ed6fb642fef3dea64fa642574947e321a5ffa994fb67a9b97f964354ff3319f6ce6559ffa64312ff3309f6c0716ece53d17fba8ef64fa12ac8cc627b3f964abd283ec35a5a729bd6a46e62b7dcc57fad4577a986f86037bb1f559267b19f5a01c1ceedc874edd873f65953ef497a0a4117a5aab572fa5d5a3bf812cfaf33f998c2cf5318ffa6430fff2a74f66dfe54d9fecf428d37b946fabbc4f26f3c9623e59ea93cd7060de83d6fb976f071c98f72edf0e2a580e9b16fbed506139a09262a8cffbd3e7bde9cbc1cf88c1662c1c9807aea0b02b28f8de901ac2f58b149107134aa061b3aaa151cdc8c4a4605e5c2cea642a791d47cdd8e894de6758de01961f1bb13492b47202ab593200f7846631a1448912254a942851a2448912254a942851e2a14c3553cd54f362e0c5204231a376198a1950cc7021304902250b110f8ad1e546eec6fff8e53a18f256c79f40fc8a462ea13b7da8fad090072433450b7cfda9119e37c4c61e145c69fa4c1f9fb5e9337d4c2dcc1b8bc0f01a50bf260f579b3e93c7bd82e1eca9dc267dfa18f56b26d1adfef631b5302f074e1f2836821ed4ac7ef99b8f73d3c7bad7bcf6c34c5c9fe93393983f469ed702cb75d3f9ddddf26737edf61ccd7277777ffe30cee0fabbd10062b11b647ebc135cb9a0baf32f4b7181c438830679b08dd51136ac448d811e3c72b0f3f9c67c070224deb07a4eef0479b00d7fd58ead1fbf5437b007069a05e35124be20b12113f1e841c3d0f51d308174dd4ab7d180e3164ef172b3562ec41a41fa43524d6c2ac5c4364b3e4c8fa5d771aeff07f8c5ca723de2701d878753cadbe6c73b7ffb82ccbb9acf97866f35c11982ac5ef051f302fbee9bbb6f6068659095d360b134ccd02c7fce09db2fbfafd55a4bdb92109932817c108a13f068104941860bfe6b2496a12831aa97a1289972198a12a11bced8e5df2e435112c5355d86a2e4a702366c83e3cb6ac3375c7519647120a572361d126ffd22b3e89c74ca9b211e82c08f2a7a70400608206d2d8f3cae914a25c0860d008dcb31fcc2e918e5e3f4d74a44f43c26137d5766e0db4010c75b29bed15ed8106708dbe83755b1211371604b8f217c834bcd62d69447b8a072e2028cbf823b5d069221b175510100842e80aef0e08415306a3fe861084020a18c2eaae0820715172f0080efd8a8fc953364882c8cc0a4064760c28a99068cd07c504512b4f8e8b08518272e629cab00062a68d0841142b41f2a5664618318f0c00a10847eb8003a71e1039307e12be478800592c56ebd0c944392185e64a02dd88a1e1dff5e3908d2cd59b77b35c38fdbb33766221364c3a65d7e94163dbbec2d4371e1ced39d44b28c14162585dd63129767f42598c4764c30b3b9fc640b940b8443d265201c8e6ec81d3dd942840b94c3d1b5711928879f5bc41110104c8a2842c221ddfe9d66c524d19b73ce19c31f290f8c3bcd8a3ccdaadd26d2ac49bb21b231ac97882399b2194bbafd5782ae1014b6817a810d9dc786eee33d423e7487b0a113214922436285e6f9aa9eefccf39579be314f8451122d53644952cf17e6f9be3c5f1d4576ae7dbe28a26b7abea5bf0264051121232b5a2eaddd6d76b9a23b9faf8e23b4abc348d2f501039137ac47eff960435a85ad47d54abbbc0fe791136917039960e7369d596957e83d1729aac0347f318c7f6a4cfa2561fdb568e321d22efec070eb7930ac484c00c34a540218d62934c0704bc2060cb7da0a0cabac06d46184d62e07e2f2a5f97418a121416264a75d8ee4f2557d3a8cec1849622449ac5d7ee4f29df9741489c9644568ed722397afcca7a308ad089222488edae5452edf984f07edc88a151a52bb9c76f9a63e1d3424da15da95223cedf21f2e5f984f47119e224414218256d42e4fc1e5fbf2e9a015d18c68463bed721f2e5f974f47919d24498accdae5442e5ffbe928322b42a408115a52bbbc87cb17f5e9a025d1b4d0b418216a97a3e0f23d7d3a8c10199962644aac5dcec3e56bfa741c89c9644776dae5b3cbb7f4e938b27324c9912448edf2135cbef4d36104e9ca955abb7c87cb577e3a8cd480808c0cb5cb75b87cb74f879121235618b1e2c8ac5d2ebb7ceba7e3c8ec0891234482dae5395cbedca7c34890905051bbdc04976ff7e93052646464e4a85d8ec3e53b3f1d468e8c583162e508ad5d1ebb476847901c41a2c34852bb7acb35a2c38896cb261d46b448a076f18492e4867416693b3cb4270968083f37dc6473c85b51a4dc49c5a85fdcb406fc9a47cea45ddcaba0581d3051add82b21d5727ba39bdc6af5573d420da954d672746692269870c37ae42fa7825bfd4098729bcebc09b7fa95a84fdc7e8f62e64cbc8705fc0ac0378d01003ea446801f52246e7f3df25700405aa3c86d0008615f701b0807a24b690c8403911293216c58baa1f7d04b63806c0786f147697bf7e9e7cb4059866ea8f2249ece9ae3dee9cc7b7efeb9f7cdef3e16a2903b2ff375f7ef474cbadcac08765258e37b76ddc77b9a456364c3b8d393a442ba219d3d4d17fed32eae47d2859b554b471b916f936db366d1199d790f9366952e4c1836f49eff41fab6d1e3b1058a32e58675ce208a107d12a5b64414239404a376a8e4ec5765390b95a111400000007315002028100c868462914812c7ba2ef514000e719c407458974aa44114a4288a8130641402060040002086080d0dd1080080ec7c535445e532f93884fb0eb83982229da9ebe42df32581b4ec2f3cdf23efa6832929dd809b771201b0cac6e6e7ea8f85872ff038cc3483bcb1f4e2dc1ad0a85cb0c164668849af71b46098dd60fb1abdb43634a28c7e2f2518fbd58ae1136b77ae8a86d91dfcdeecd98b9964f161c22ba06e3cea35a1875af43d9521a34b0fece34e24b679bc287213abcfa2240f8ee0c3325ff95e5a83a8a07343a300082c3ebbc8445a3015f4e4562390396b24379290783bb61153be83f28b969cd2b4b0b1a67fb50f89f3fe51ee713861a9fcb7a4ccf70c1424334afa6dfb0774f90748b5658d4413ac6a00be8862e7615cf83296f9deb7e3ae6fc3477d1216a632437897363eccf444aa60c0beebfbeee071eefd1bdadaecd75a30586708b0febed6e6ff5b27b23bb79fb7df7e51633403af25519bded79d84a5ade66f45070c9f9b5cb095946a8304c644273173c273e62564053ffc691c993c8dbd624bdc9eb9d08e2d4dd0587b9d02d005a4fd3c79627f97e5625cd07275fec3dc82035431fc33cb20d46c91b2adeb1bca3a7e3c75d29d01857f935acdb75f0dd6838e0a7a9290915508f93bc43139415c6b6a6f8c9f8db7b91820d1bd7f3b147aa77c44379542500518dd1362b8a9bd26bc4083495159e04a56ae82d3a457387a9b0ad41c398725caece50c4b5a38de0646b69a40140d90c4ee70e68f2504772f70394c1e5e986a2e612d9f868a72f05e9417978b83b90237a2fef1693aedf6cc9b3d478591a465c01b9049c59f2b9388e18f74419c5b069357d24027412b2cd620ee2b391ce0ee6a9623cfd1290f11913d54d866a2596aa6e68c1d70d3de6394a2081e5d7fb519fd1738253f5cb5233b63236c45bac0fd7f5cf1e9ffac1cc23eb6662f9e86d8eeaf96b6b8d3a0ee9a4754ce578f7a5ae1c635c7c5fad91e993f72ac716a5deb1841b65c2f1494953d85257a1c79114b1ab148884232df06071d9d6d4c002949aea5b53f2b35c6dfd7bda3ceced5e47bdb7c7659a302462d94baa9203198c336896a54d43a491952164730036d7c6f6eae6f58b6cf0d5af4290876e6c7b95d6fbff0ddeb9b3378fe59eda2553e8322060f6832bf76f2a4c6d4d14550580bba5b311078a46dab54d0e8b90b047776b1f91acddfd2b04353f29c14ca5ffcc8b21daf376e8fb2dc971d48eeaf2122d23873849cd7a1d5506e69a3f4bb06ced8a6487e2f2b87d13b3aba4a70f51ada9d91effcadbfe1e5e285baeb857cb4f6a5721c68a11012e1b035a88220e481f4ca57b210a70a2573a2ef2a850b79e6bdca225dfcf686b2f251804a66d86433ee06fee373022f150fca6fab387ac62473434bd1e06b9c0585aa65be5a6fc77f6835b841b263df1d864d681ce10112c34b4ef04274c6ac8d32058208aa8616a2166766e5da85e4b617449cd2830781b1bd67758821be9016c8d9e59434b25c2a25d0e01b3105a5b06d82ce6cd5c71d53ca48a41fbb9429be943daea053391598c07d5b86fce768bc0f9c902d3721454a6b6a52807eb59bd5213843ea54f201340c48489d821887b6b19a634718b310524026082952f0f8983467c01416eebf4bc954404a45d3ae28bf008a68f6e3de1abc481bc4aee9f9120595fa910310d9d1a6f23ca0d7ea7b966e82b33c708fd2ca8edd0b709123ec4f1cac2f52be1255882ca7db0d48886c5089e7302e0e24d79c3ff204e1dc475874895df93ef83dc4e7c2d2acfba2f687969ff43ec734fb701295b075d72b821bafcd68a535620632c4de16c64c755f45b8c7c9fc64d8aa9446d818876577289f7385148ac94ecf7eba978e995a5ea99c0b6ea35dae8cc557e062a99f6f50c6ec9471fafb3d47153539d92760a07769b4dcdf3200dfe884c0b613b0609c64a286e36c8cde97971a45e4d101181a4fa4ecee5cd672f9516825a0d50a2ec440d707942fe1327474d276a4ea827b21c4f72b2cf3eb8408e973e329be52b170fdcfef02c04e0fe2c2257174b1ac6a5c35b2706f7e4b82ce9e67acc02c3a36e1a09470f708cdefd666a8fa488db83c0e414721892eb660071fa64ca7810e6074810560c36b84e36a1095dc445b8cb32e8ffb5e010bea2e06d10071c37de89cc2dad34a8798c36c7c266a6e22d0be81d7c006a8a2eef167f68b102d35853d824274ce909ab59d8f91dcefb90dedcd4db093292d03352e08afc99104481fbde097b6b1db05ef3c867c2fd651a3fefd778e15d73fad1df65e0b9ca534eceda5be05569ddecb8f9c1345a822177fb8760ff9f2faa88bf1455cba78d1176dc9fa70b904c2f72c8a72ee7b735b97e3f2e160c4af054602b0029019d0089163f65e46179c881b81aaa9f14061f218a033316e504d372ee84d19c7101056de626a3cd03415636f05b30a67ec6ccc5735d6e7a50ab0e5f1a6c9318062621a7b11ed2b29659b50502a943384a7067d2fc34b3d64b6c211cc6e9dae907907d3f44fca0b8a92c60234274863811a1354a3203408ae99409a04ae5120cd0ad64080c605692448d69008fcdf05ec0b374f4edfb1b6a8375ca56a8152156a9b4dd7d9feb572d5288046026b109406c135174853813510a079411a09d0b8608d04791a3281c4f4cdd3e8843382b879c224913046fc2795cfcd536eb786c83438a7b5438712d014e5407810c414380e981881d0018871601e2062eac33e807d84ffb46f472d5d9f934682342f5833816a164413813508a699c09a05a6a940cd08d25c20cd04654d1281fe4bc057525f959a5682b86681342b5813813417a429011a0bd69c809a05a24960cd04d32470cd82695220d09008fca6714c4c673cc1c444d61f44e80647f13321aec5f2702c6a56c4b7b204ce8bcc8488d697c2599137fb4244abcb727517e2e4e956114ffb78eda877c15d22d4e9d3b9c7f186b8dbbda2b414dcd801f91b1ab52004e9f9ea6ab009fa79ffe24ab96aa8019ced05a9c5ac9a9b62175f8a80a86ebb37833544931ed5b0dd45ac2f7dceef0805952ab285ff76be2eff6b54b787087943cdb86a1030785c6c6299b0278cab5d36402b3e6be9da866c566811e38a40a2ccd77436eaad57d61f6c2bdd56bd0377e215b15aad7c5d5291f1dab489ac5fd615b89e6b429a3e37597bfee3678f07d55fad56e562e27ac2e1011faafa48dfeab037ee323fa65e91e8088ccdef6df3d48c297699f066735e323bc380bceeca1c9295f02e6a2447d5322fe794c448e9c3f9231a99930ae8113e4e65904487416f95fa52e199ed9298480f564e64cfbf69c5c0e604ce60585e57a5f4535c0b9d59cb30c1de76ea58aad3e4c67900efe1875706dab48a7604f0a93c16b86fd2321c1ccf8c37e58a260b5a98b1ad5911e1b4a61515b94e6f8e9a567f6df7b96a0f6c095c96f2b77f4126f7466da43d154ee77d99fd9457731f4ecaa21fbfd18b4687997c8b44ba384ef76674e96d0b41306f0b3bffa537df2ca90ac0cb95a58712eabfc365068726d471658efd7d63fff1c73cf5f46acf4d59917fb1771a1bdb735b04a9d73b35260828d90d5ffa790a4e6d52310c88fc55e6dc17b9a31eec9c7afb15d81a3f9b62bb198a76d96a3190beafa70e0c515c5b2496771995553e183b32f8dd87b064633764871acaf9007baaa985eeb1005d0221d958c725211b1eeb817d661440bb15072958a232829855743d4f4b68763efdc9be5a203f31198c07bc36b780d0f306f0e0bfcd6290c0d98febfb8f9d970a52a0e44ddba7929443d990c3ef2acf1180c81e3e90326d608e8ae98a369c26c75035ae6e6b6b0eb07b2485ceda3b534055d324cd86415890ab93ece7e8644ffc8184f12fee0869a95e8b338aa2da091ddff6421e8dc5f95083b40aacf555f03d9ac64bf43b5d45aca8fea554631eb324f30d98dbe683ef10d71ec7bfdc0ff18ab747c95f0bd58c9b28a2189848e4a371f862a006f37597a722968ea229f354060336555d621fe7b9a40646c90a36fe2b8c7185b55f705bc2ef9e6d8dd5d9694880ff4f0abbe85df5a29ce3510788e118f119822af680e19ec400aa96ab8841b00e2097bf72bcad47f498317fc58c490df52eda314508f94ef3de25244cbb1b2d07da4985aa9180a03043cf5a39dff0c21daec4d78e685257239c69bedaa945a0e26d52e5b558ee100f885d9b77e221c1a7d0db468f55167f235446a090ffdb397d37c7474a3ff7db872c5dce1131f41f58bc1950f579d0df52eb8e73a126b1367c339b60ae6ea6da66c18ca686cdada50010c4d205c1db2e7ae80a479424db4f3ec1dc85b355aa8d2633564a1671d59555e47abac5667166ff4e6eb3b6cacc8292ad064ce0db22843e2cce2d8ede74ed1c403c25a903454e71e484f45ad51604a3b86866aa1f60cd9ab15f0ee1ad825e5bd14c4f37d25aa3c73266b996fcd818c3ae354c2784419f394b578dade8b78f4a991ca5871ba7a1995ddaaee826cc84cb6844e072cb6bfacb9392b894a5ca30f9cb9cbbb8004f74f1ff332e972395ab903d44ae550407637079006ecfed71ecf8d1180e9a8510604cb17efee6e236685c76d3f4b2fbdb5e8ca9a46a21e0c083082b5c0c93d6d542ddae26c3fa3b648fab07b2db82d3a1a6862078015ed82f05670eb09196b341ea369e6de0087b38c09deae250cb28776ddc25da2b59f54d878d006ab4826baab53cb3b88e363ea8a9c68d1a7f8c3b56a5605d5e0ac830b3c37ec29bcb3c66e3b40c6dda660d47ff618c9b26ddf0dba9065dfc1c5a20ac36d8cbf7ea6781aacc18b448650ddc472490c942ee73fde1d69464d44f61b67ed05a8c565696c894efdf35feca3503a7fe7c65af171aca0d6792212071b640143c1ac4c43777dc1db1a7ccefd3d38b9ac72e0227f49b99eb8b7947111586ec57a0b8af5e872407614cfd5ae67b0e816dfce7711c4deb921e63544554d62e16913b877b6fe85a38fd04ed7eb8ddff991cf93b2747b580f1ae0b95aa887cd33878e7dd7c3a0014c316ec1604a012d03458579b26bf682d090488a86020645413425af66c1bd5f949126b4875a23c8bc30f031bf28c992f62a89dfede0cd2810faad75fd02545bacda18eebb0991f9b74349ab9d16084cd69a140790deef14f3eafc5975a37280c2ff7cc3fa9f93957d186be84d48b6798a99ece454de60d528ce4418b03299ecb24d4a13a5c5f9db7ffa26c1b76862b6bcbb91ec8fb542bdc48833386d5aaa0707c3c2e126d72556ebd12f87be338fa9a7a98619dcf57da42afe514318e87939d84d53cb8997e192a19656a8d99a3af3ff84725fdbf1064770128624e0487bfc97eb87495ad6cddd784885ca3cc9ff59cd8b8d28c4955d28ea33bd9346b068ae9b6623a017ef96b5fa436794367024c9fe0a94d57ea4018b08a960538a8759cc4276b33b95100eea8b98eaacfa32a02acd90bfdc71eb8ce8a3712fd0ce2b1ac7687917a1c0b53b216d09b5cf63b7dccdc331f157278f9129e98cec23dd977ff3e118fb71f8443242ba14aba5349bf02567d472475af399ed4e8e77a28646a472f830e54f071c4280f0707d84b08cef9213a1c8ba15e36144682ce5287629ca7a590f01e4ad964c0a55588fe21b106a0875222645714f7b895a802b39a37b1f213857f923f3b7851d83a9dfd47bf2882bc4f9301c8b940d9630ad2e5509d82bb415df32a768750d097f5d38405896ba32c5671c49269d391d39264574b926343d134a4e61b1a9c687846031f0dc9b417a7bdedd3f6875f2dc281595007d5b8b7230d620f3b92d68256c0c8478f2e6e4478f219c2e64be2ea8e0c3d5af60aa2bb219cc88af458b6e8dac96029b6b1f3b7cc53f3bf0afaf5ca584a341cae660fc2dfb38077dafdff4c7ec10026541258822567acfd17e46dcd7994ac12c8b397e9a8364b42760f8aa3b18defd176457b34c41bc8b791d151f6c57d34da641f658a1764764d3cba5c13c86a99838cdec98f6613c7a331d2433ec118f23662898eea16bb8080e4ed44511106f2c28ba2f39a91c2b9fe7a29dd377688ca761376549e4a4bce38e8e4e5e313ca2ea1211c4f152717029673e75328ea5a15cbc2730ce087805d4dc174b0ffb5929b60f748746c28df86b6a9d0d6ea7de1086733036a47e5d22c1e0d4136a607b6a38230cdf0b0b4f6ed02dc624fcbff4dd5c817d2ada25f6efaf6c51e1bee58a4651666822d9fc51b29fa3492a7d5c27f0b35f072758abca541e00cb274b134ce4cbdbb8185b82a01706826c5f103795b24c66383ea4754bf40ebe132abfd7c8c2a44e2a412d51b2824b7f9e8ac7e90f6522e708bfa7823cc7c085ee8319eea9db770d9fe23cc11afcaa8cc16d8237fc7355f640aa553d0dd0e4ed7ff1858126ea7810c9b2968d92a21446ce4377f89c9ee3b64547203e1451b792adb03bf9f19fec9e40e2d7f1d23a79343f1773442b1077183a15a8ec2f3a2411c9b6b2ff41a65ab4aa2e68201ecd394c67d96abee5a36130109b1f39f18afe8821eef96dc687dc00c0bf5707075538ca4379ce8ca48523022b08b0dcca2a189bd0f4d10716a88be74f8740dbfca1cdd2045866ab2507739ff9c201c49b00eaca7ae7410ca72628205ea91c132e4af79f68780ad9b180a5fb6d66b2d043da4aa340e5ebf9321c65122fa58b832a89291662cfafa5baf975f19b61df77a60629e652cef4f84e5cd966cb2fd0423428596290bb24db5de4a4018c83ab2cefbbfec3044e81264ac8cb48b41524920a628b76b0794e0938ee26fb28c7bab2277733d2501629b167decaf8a56343c3437277dc6c21f1bb836fbca073679202a1d18ee998dc5b3ee266e45d4a2c5bf3177ad7e4b784b1437ec5ba7b4f89550d84721ee57c52607696646178d8f50d9dd147f3d22127ff1e64b27992a9a9e39dbf1780fb983d7fee6e384f072e3205bde0b5135bfbbbb73518444fd5828dc44e4a6005f01b8f4d5777d9d60ac47253aee53d2c5348d19fea8474b42167056e18e4767005408e9c10185001c5c5e21dbb1723e01c8ee9f7a001263a5d1820a1b0b5b9d851eba3c4c971f6cf49b51723ea8028d02e0cfdb390a7f859cd87a06bc2bb54856086ceebce38dca85ca45b1b1a526da3eb0b323d77f83659f0ead440e52f82df4f6beb00ebdf5176e68f2a7d007ee2bec57946f0a3f901a95f5a6d80dfd47856922e49b73c9d1e1041a575046c24a309a70a0072226f182ac4ef769e960e17971b272c712ad2c9bcb6c32129a512553df47c04335795424b660421991d3801f8f40fa12cf5a4c1fc39b62f7462f13189cba2408ea54a97d4fb17b2d81153b30401a2450bb5ea7eefeea4a7e14370e180950a2cd8957f9b161a8809a013cb9315a52420df675324577670f960d451a4cd6e326228bc74c6470075a9e2480d8890ff524b639bc99e71f775240aff488bfb8663a1f2a4e269804acd5f91629aaa20fcec856add00bc8bb0b732b929612879f307b6a0c442e8fa96ecd68d12ce6047416c45617314ef552645588a78605d55aabace2dc06334c5744031d862311e6aac0e11c822fe4aa924e6f3e2af1df66c6569e2f5bcfe92540b0a9d94368be78955f68310d61519e3ccf150f950941c530ca6189d00b3d4debf4230765ba211557d49471e76b79e21ee279804d047106d111fa47508d04ba83bf191cf8748619ba35eaa1a73fd83a7f738c21b66ab79a35dee6c11f9b1d3dcf8ef92128464aefc50f8a9a753db4c2929c0d8a610cbe618acaf8871ab54d03032c9452fd276003b4b56127914a2b25b151cdc6935771349e9f632f235387b46b32a5a914e3b930ae6a6c820056c050e36f16a1c31078b72043521fda6990207d0715fe6c2daeb85ba9763848899b02b2a331a768c41df80d9c3af52818f3aa24eb5997db0d97a7b961990990045156293b93b59a4b18e14051eb8f1c87ce22052d839030e184018da9c89c2322b45c9ca74dae5250583cbea126a0d9feab64f63132d9eb3fe80927cf3e2658dec9b4e0efecff2f25e61d14b6ad803e3471e8e57b12e8d5d59cc06da432cdc1acac974be240be549f50b17efc601427720dcf4df1e8b5703c4a8698dae7afbcd350f8916d4e7fb65d24db08ead29be09247b9836f7d12d0571664db802c88b13e81ae90264a8f1642c373049cc40749f52c325c248513e267db97c1ee16a4d212c5fffc25c7fe12d5d78de01d1af2b43f2cbf078722c0c42b7070f95ba41aaf7846a48321010c3adddd0d151adcf379bd3d3e04f45f6c95ca4ea14383d60c19b5c48cb2f6ade26769d312aa7220f5c3764abfe444593bb61b3c365d49d64af3d8248c492ba92e98f6ab4aad53e2e30e07d71858bfad3a2002bed61b78a578fc13665b6952550f609c562ce3f0b4c9fd98523d4a4b0ad28b0cd028a010137efef678be3723cb1ec182cc63647a22ac42f53eb2d840b08913e888cf63c187dc5a80809871ceb0c09f606b000562b4ddc351d6e4090b8a74e188254a7b09f62233b1cf7290484b0233be94baeacea64fd7928eb4b60f348df17e3e24b0408db4c67d2e248fbeaf0224cb9ed75225e02e395345e84c81778e40a350cb2fc6342acf11b918a84436e45557bc467251d96beff25167210601ab44d22659665ff1c7c37fc8bc1bf4aac016cad86103ae38e570bda2e21916a43f5eb2738e255a09da4095cd176ea4311bb70044d166a2159016cd33b068624cbb60256c957c4256690958f26931790ac9910b2be31852f5db5ed71993cf51256e44bca856a9a691544fd358d875891a0c34c6ac2976f164a209524fd7df515fb4aae00d57036c063935124c9e9309a4b4e144d5dca9e724abcdb177e835e20ba8663cde7a23b14c4ebd807d50bd53098c85b1f1132c99141e5de4a1c83dd050e33c3269462c10cb24b8b8d45e689eb07d72467eabfdc6b65cbcd4344c11b3622edf0e9c07517994d5004fe53fd59229eaab9196a4595bdd6de968a149a00e3ac8572ed4c6d9482a39667b7fc31a1cc006f9613de3b976313fc27bd15457a8a0251a13ab41f4c0cfd2dbae20b6b518e20a92c0d282529b8ade767e5b17f154f00b7655ca8dc08165315c83885697a2426f5a2ad82f5aa36d64f3f50abdf68a8c08c8a919e30938e310632c94a5a20d5503701e8daad811da38ab96e73d859b472bdb9350c090ada364f9c37e351aa4b08a02fb5dc5c0576f778a10e8f78682f4ee9f1838fef174d4e2450651ccca386e8a5ae09e3c34d98613af03769684707e803bc55eae377dec401aaa5ae960f5331c97f8711f26b579e8b9e21d730204a82123c9d306c831628d4f01873d919ec8559c5380839de19623ebca391bdb5052e5f7dd52735c4ff978059da176302cbc19bad255932802510abad83b4240930f50792aa2fdb3d9214a1543281d5311d3af008028a302f4a3e97d971e06c05fb3dc3ee93aaf4682c0f34b0cb4ef579ef48bccbeed5c1fe82cb0c422e095ef3e19bd3b8ddb836dd839624f2b0a407dfca198a7a4427d95f9ecdf3811787da9f83638d3d0a0936cb1e250ae08229580e8531c00800805fa54544b3219540c34fa65bee2ecb3a5b26ff6195a538ffeb3de99d4f5d4ab6b42f52429b9e6bbb6b6ae88d72012f2ea7f63921acab3e02efdeaf5348a41dcc39b1822f74b6d66451000bb841e46eb0d41bf6d4d4e9b3551629a4d33194540578fff5cedb8e66072db8594d611a4048fa2754c519e4587ab70b0422dfd4d1c3fc2a12ef41807a7a117833df949db3ed5bec1da82a2546fb9621299c23bc340f5d111df60c802f42b8ae1d17c83d7010ee4d6f7aa90f479f537913ea015ae4798d25e1929011534b3d3f167af9b06c5679890dad87545d5f87f5dbd1cc77e8cacec93a14833ceb948779086792aacdbc5caa0bed9a918099266c23eac3402fcd4c30d10f05904909cec1d379167e1b3361b7922f3ac073ee60cb95e3abcb5d1ed56b205f437f99a8216f31384cf801e243b3e2827ea0c1e268dbb06d1f660cc69318d00be9613819f5ef4e55a45eec410eaac4da2331e70bf4d17d1387ef199b51fdd96228d25c35f95bed35e08d2b2aa9737c5ec640226fc6d719b0317b19fea6bbb637275590da5c690e5e9ada835cb424f4b1ab221098243985468fdf653a3e9fc09e485a202a166d63f016e5c563e8789993aad0b758176cce5042d0b0f56cd8ae483bee5250e78e6e8becb374f139d3fd4df3c4846f6922796b8963f460c5b734245a911c7f2340201c1a48fa15ec7082ff61fa3e153fd8f1784debd606118ae51f66e91ade0840f6a66ba48b33f4cdb911dbc69dfd54fd31cb2c23ea20bed949c19e4464d19c1d3fa605e425e826e9a291271caee502c342299160712819e67a4346b86559887ceb1449297629727fd5dc7c9c09c054e9a35078112bf8525571202f80a75c6bbf18c909f56ecab8b8ceb2b90c43a60ceefefd3a7b914a83c249e495a7a38554dfc1eabc4b37036f1174c29cb5a2510188b7929a3aa6f955bc7d0873a00d05b35846e876d9c3f77231f0feab991dc160bdbd0c9bcc90c922700d22305605d0a856f49665040866ad617483522bfb385248ca337886ed8d3cdc6029152d896086a6a9b0323857c536297792fb01ebf43861572d55cc99bf9ee5248a9f7f2067768cbcf552341cac6665386d10dca4e960b71e0dd50b4abc4cc42b97074cab42fe7f10374ba2df06367039c371e330c47f743b1f784a4b38a621e53c845e761ddb8edfa408779f3e3e65108c1e4f7246f1a75d6985b46e9a33ad38e984f421482936c15a53effdc0410255e681d9dc82ffa0a00df4ee67114630e40d90d10e23d8a2538bcb198859396516dc6e066baf63f9275aa843c23c9c4cf1da18d8fc09bbfbac76da11ca92915606aa440bfbaf4c5aeb7c2e230a4ebcd6ac4e83e45c6b017e7a6c7fc262a53326fa9b6cc2194e8544782d0d965ba7886ce57811be7b37319d5120e91db153b5494d318879acceb6d6dadfbe6bd4b1adaeb4d2eba56fc81251b3829e0e2609743dcb12699a242371f57daf56f32977cd09c19093c0529cd498df46575128272eae13b6b89846d6635884aeefaf69dc676b8bf63c5214f29f2ff6e6233c439f0bd4f8ab259adc019d4594a0cb76816babf7b37d2a26a94d2e28c9678229b26c0dc50c6ae9c97e87ecfc0f7afd506078e14f7324b9f3a5fc537e2167e1025f2a82caa2ada84a5f34a8fdad0583420b384ef7b10ad31abc75ef0857c5083498a0920e89a861954e48608fd508aab04cd04682e0daa2440391d8b2fb98d5ef58076102673952fbb6274396c87c1c92747eea2d9f762db69251ac4a4be8c7b6b59af3fb39458729b48269f2954ecd0020559b1086b329d5d9ffeec9ba1023d7d7aa1df47fc068dc8e86a44e5b8a9ec4bc1666ed6cb5d46a9404cd403606a61d33b040d9c7ed75872c496cd90dfe789e00a1a63c475390a7b70c10361ad350399bd326c13e17cd475115ec9a906fc935a7c1f9e1a9aee698f45abf89938d14ad7433ed378209ccd2939f5b419854b4eca15283102152016efa3c500448e5f367af48486437339d0c4c522269afc67947d7ce5cd8f25fc30035d073d858a41f5cd7025487ecd349374f8254acc53c4415ae160cf4ccb787d11333241950b405b06b42b12aca04595f45db6ea180ba636f0c0e1ecf0889b6568572eb0abe7ec56d387cfa833f65076df38f636d1f4883c4355940de07f68af7cb632e7162016cea1d2a53d211c74866522b74f8e681615b45ef266ac8eaed1f3504241c74d2399edb27daa71c191075ee05f071245b9773d6b91c1f5046dc9174abeac8b2d9606e95e53ce2f8532188e6c850d396844eb1f590fd5658d8eccb2a571183de918b215a89abf50c86a38eb0134569a0336d5a411a47df326f15fb04d37ba355699377974b3206e1596209809bd7de853233a81d6ad4c4c6ad544abcd463b06ddc783de1143d40f2e8d65730d6f1d21f21d7e7de5db757e19c02f8bb1fc7c6f5b5b56d38b693205e9fdbafaf85dab131024b43842c9440e07d886f31be59929dc25343815648158e708683fd992c2b136db326927c83208af7755d4f73a3e7320c489dd20a64e10328523b1b96821bf01f50a86a99c5162b0a0ee851bd5962b36b7c5cd7419c349c41c57f423a83c14170ed80b021a4a716055763026d6b8777f3aa26ad00ed87378a291b0bf5c76fcdf86c70ff65e92d7c88b52db0c9ca46cf5a505d81be43578bb8480ae6564ccb323f05d27a0b56048ba84528109eaa7766d12ed0b42da253064a1c54faedd59f227199f0399ea5f77a71c2418fd0b9b1682d902a79b7a9665d7dde66e99e1b4bbf247d944b8351169403cfb041049963800da9754a6a3d69e418f49344fa09e77ebcabcf89ca3a7b0f313e1e14bd84ac7215ab7f68380b10110f4e2b743abc505d46650f5a5a2cf70c948fe868821ec9125f89e5b49d475c26685ab77a6be71a9d9ba734ff8269871193bae14d92ec87edeb5c233571c6f2b4b9a45e6f9bd8adbabe6d48e9b4807cb8e0dcc1bbc5c845b70f31481395bbf1bbc1c2ea4dfe1473bd747a90b4dcc2b6943eed0a084a7ab566c612226851cb0093fd4a6a940b0f0c7bd7512bb561be4ec4d808ae1bbac633b4e6f98b29189dc180376055facb31deb183eb3108bdd19cee34b1fc3664fe90880ed48e06cdcfebee01ace667b76b2497b534ccae812a43285524d0549ce21d975e51f9fe79bbcfed5dcb808d275a7c9d3d0bbfd5e9f5fcb4ece3518d7b9d019fd831c3ee7fbabff901065666765e0558dcaadacca96a61acea83a69574de1556d0e51261824bf069e6646ab8d8b42f533e284ee826514c292721e175650c3461efad0fbf80e6cb30e9c2e957dd6a1c0d62a0ec415365453e408d23d12413154364961c083b74a6ac0350d41b5a613c108a4fb257cbb9e1ff70972b7108a3bc5f7a2b2f8d20074334eb7bdbc45df8b38b7a44b7189424e087352e9ad16744a7a4f9e7b497d80a429588eb24c79480f7c99cf18b2e8026913ed46ea48e43eaf1b316a29adba5fe2a917db08aed527aa3c2947b09f203c5f1c0cf55a30bb48b1b764e407b4deb76163c85d7f8213e287576cc9e0db198a489c8b3517ea731af08ab7ee9a1938772715c40bc632b2bb55253c0a8a1f949b64f30c84c6ef8c908ff4136e5564eeb59eafebaad2d51f11c686d2b59efebaaaa2440f04dd6b35643221e876f89327940cb3dd0d1f9dfa575f65d3cdbddfa3a2f8e1a059912dbd592c0328b05a27bc991b15ef5abc0259709c20cadabc03836f32631602feadab9b1dc260c33741094bde9339d508fdc6486677efd7cf8afb058bd5b44a1c26937dfbe51b38444bc1c38914817d6997e48c37244a5709e03d8a9cf1403e66dd590cf045e34a2e8d27e4ba5d332da67db1da71e55f46a2180260a2272b74d4758ed017ec3be8adcb84a741805a60f6e2889366436f1f8ec8a890713e311c3627bc339329b6a6ed89ce3db599349c48d877481fd2faebc7da5c540ca449a2335ddf4effe26a100a223f814a70dbf39e1b975227d7750b5cddaeb045e2777de695c125193e37bbd629be40437ac504b8147691855ddae66af7413d38385899d570999b51f40c745ca3f803abae28214299958406f037574825fdaa72d40d44e0c372dc82d2230657c603462ab0a3e6319184678ba7f6a6ba3e46958ad83115f9a23f64a5ee64ef9c2ac427d5f20138d56e46f3838c7dc8cb096214a71af14bad5a198964fc1b1e26c1373840b35a9150c2b3067e072f134366c3ff47b261a7d9c0b58ea723de8219750c4a2cd7ef5465b6c9ab81a01a9c16b8b2c7219b30d60d16fbd7050220f0909f22dcaff9de6646d91a77854dc54c9e9386091cf514dacf68bb2e0ccafb3512014ccdd1fe201052f4d4dfd707794101b3470137807fd5e20226382e8811dd2cf60bb2e9d4596583b6630cfc774ac317fb8d1d06d446247fe1d810ec4ae37af2276f4f9e43824f82b364d09eb7d799a521ed3196c1b87dbf6f597d49259f884ccf57cffcf092423d695241c7a28dcc25419e528f1b911d7454f69cec6d4563a2930326c73d6c9dcc5ac03098ca1113443cfe1380ddd7078d02d267c84785217c21ebc56f6ebcc58df9592cd542aa6a3feafa99de88d8c43562f817d2a0fa33d2ee615a837d0cdec29044d4c83005f8292f55f71018836e39c267fabc88c0a4027a6c90874cda9d97d0de4b6235a45c310b526cb647c55fa2a720d9d1791d3588539fdd25482d096a6cef135acb87de3d7dd00cde158a4752ecb389ec85d412897dd657b66013280b6ed46593df7089074cf5bf784852f3947b72a3941b62632a6249be9a49ff50202a3655269eb47b687e867cea10e49f7e31c5b5aac2f8270385f46d5e5da7c04f7efe05f2bda08ba2603877858fde91fb285157ea2f08faab0262e49f24536089c900a73c789e91440e470b419dc28d6c18092b0699ac5f63ff7660a8ef2d45e1a46e14e2c72397974ebc38d353e49e6606235d09783314c0ed1eaabc23d1f40d52ae09e0bbcb640dc4775e8a6bf1e8ffb2e82318823da155a6d359ea88f9279a6c5a959ccdc751c9277043b849ee2b5e4d58d0a149c4c3417baf15a9580e359a3938b8987e3e225c98e107e8a237042406c02f4fd07b46e68a46bb4341681abce0d3d01451fe6c1c6453df4db7924808adf9d955ca0c3c11c4539290d830b94ab7c45ee58feae67988e0ba494f60796eb3da83a42c3bc37eb8c3377b3abd5fce12ac9991993c6e5c08b13d9efa7542000fc5332f65c80642485653b8db3bb9e25d7fc59d78ae362176a052cbdb5dc25883864306039b5d3c07e7f404e9d30247b2db58d1316912c2e09c2c76598dd8c910b2473063ad82b277cf5efa88b2d1fdb1bb91787b1fb220e828de6e9565dd3be5332349e13595f2aeab830ec44f27cb7174afa4895108141464782e785fdd5850e628343b0f2a4de4c273fce5dc3e4c69f6c3950a6f9e57a28b501bb4d30e40ec47cdba891252f347fcbfda53cc99ffa99d98a7aeafbc62bbe895edfff2101a6ee13fae7656e2530524d34508a785c2333aeaeb0422092cd6a728f7e77f36c3478315ef605da4857dd94684406aa970550325cf6207a2a6b684a9569c29d9d082a8c4906a97f6600383753d5b45f53ba4899a18e0bf9150d4cd20fc80fea9b0be1940794e365c716cf68d76c3920e30209e66dbdea52aad8638a401feba9f31623419960eadc07de831846130f2912d2622f68c0f070369d700a0310563b14bb3b5f2f8fa1c771d3a70698c4a672bab3a00a21e9d18222543d7de3bc5cd437d83862420e594b94549940b4ef0f1b502e7b5765804b88d4c2529cb916e8c3e2b5d817558c65a1e437fdd02d433b2eb9908512b6113b5d6fe316116f28626cdfd57532ab274421336b089a94d6cfc44dfc3163ed9f4593fdcef82b5a6d086efc9236d7694c4090ff6332ce38204f4fb0fcc5c2dc333e38d4c41d1db62a64db29e5b9424689422cc8a49ce0ecddba4c2464417f69d4533b2ca89d3ac596d792c02d5e3123375d757986d5cb64381c1d679320b878a3967bbda72f48e23dafae87150f532bd4e4afa6f720ad328378df77225258a165e850a426960e96e1248f02b431ca67691ddaf5ee4cb46aecd42650237cc98319c8dd381e22610bc673e9b65acdbd4f01e23889324d0f1094db25440179b3c29be324ec7d5d3a86e2edf7bfc81aa406da74624275f3614e683bdc9d5a6080f65edebde3d5e0a2bb15240c4069946bde08dff15526823bede9a40f55242b0d88ca8d189d96c80da1760ee447f31c20cdbcab769bf05993360d9853711d0daa948dab74621f688bd8c239f3f7c4dfe832f5dd612ae38c3fd49797cfa906d945a82590583ae243076c91430791eff1e7ff28aa2295088eb0f5ceaf65ce5cf154eaa2ddc5cfb1859eb1d19cecf0f33cc555ef54ed23e4e56bc7bffaea7cebbec3916f2299b206fd866e0b7d909314c3c330c2a4db91ac8b90e97c4f7174384d4b434e66dca70565cef321b60d8f32f1c51bbdabdd358a6235ca519a5929eae0bc106a139aa186b391a35ffef071675e86574a86a837934d71f8d9bda423396bf1075be573052ff3e4c6422a7fff01e6ceaaa0aadc9f61f257b5313f6d0896d52df8796397a2255cbd3770c25022e223d4385e3c6eb9740da043c293923cce0bfaee7831f827cbc5e3de43b16c885bba4084181ba3b877758ee69b014865e2d162d6e02c38e5b9ee0bdead289b53f02608a2b1fad39771c570db1eb037a632cc791e16a353f62b1e696623623c71d25a1518560583b0676b68e21d07c288c2d4c7e9af21b73185ffd1e2cb78e64040ab4e6e5ea1cc7daea2f861fd3bbb54cc36345e1a26754748de2d2dd40546c0c2f82a13422ecb39e1e0a9d3038c6f79e5cf4b1d402516c0ce48ad413de5188c34154f67b21d1e9a1a5503408cdcdba8bac77898489607ed6c305d3d8d77dbf21516433cc90b790dea9eb0bd71618cccf4311d1d03d401917885228799bf13e6ef3ffa58adf877536500519e5e017eac9eb81530193c97e394b43169f51f8be3c5d60827e0b16546b25d0bc9643adc4a5a64b71df901f8a259422826824d127aa3b3b6c5dfe4f2dbf9ca58e7222c5a28bbec292ec07a1bdcbf46c8591734354f3838cd3d64aa818ed57c91b97d96acbb1e4ee547db21b544839987d9ae60aef004517b98e53408d289ad050bfcc5caf2548f19a8b1db1344cb8a16d82be9488755127f20583d47b6d1080457dc252235e6f659e220fa012319dd1dac78eb8c3205eb44f24a6391829336dc1624f2004ec43083c193dc613de26b861e4900bfbb97b7993dd6ef273bd7b0838d883c8de25bae630e14e4ab3cd75ba87f4bb9f67537c4f572b03a6dcf6735223441eab2d1aefefaf9a4f64858f57d3ab4b68094664250255f8796072c1729b9b8301676082810eb9b3642357ba18870cfde6e4a84c12a9f4fb1d288fb6942c22c90126b8f02ec5a94305e5efe5cb4c5e97eb7bd70f65d20db02a6104f9df7381eb5c94b52b3e49eae323e91dd51fca6fada70ea1d051cef73822483c1afc7e7efad684085679bcbfb1472a8a00b77e5aab0f45cdc45e5821e1a8b91764c884140d030db7150a7e0a1c88ec968dce979cd8cffcba5251e04dc0e3fb3ce86e06d556e431555cffb3547a082af158a4747f5b21bca8ab62d6fcfbf9c8b160337f0309e3e322efeb28245675e55ed4c4bb4022b197c47007f6dfe4de599e86b64a3e3c469c708f3cdf0ff7d4001ce285030c99b4045bf846318a8d0f2ea40de7e07b7c67d15fcecea44be28af6f3eb3f49e9ecd427651f17f772b702939ad19e88c71eae5e4a3c4132e2cc6f4722415e10de3a0e6e3d0c66e019666cd82fbde6590f9f8bfcf7cb1e33a7e72a29f54136659bad1508c099251fe06db1f26c11d82b0cf9579ea09869daf49c7b1121d3e20d63c46c4a012492ea8b22b9379225626ec8067f1b2989b02f9ec7c39ff43f179d87e0927b450e3476af99efbd072ee66cb328501c890632e9d0bc8a25810560f9b848dc37d9ac7909b3b16486d1c35b77c8272c392d9a272134614e17895501befb839e463385d43b4c2ce3563dba6cfc1d8019a0c9c58410b38f983c750abceea909dc77eb08ee774fac79b68c99d094e95ad86041abac2b3db65a87399f7d92860c77a2a820f9e0bfe71b72ce020e83af529b8b16eb3cb1dd0d602ac341f27c5501874172cfb1ad14f8934d7ff0daf8819ab8efb8fcd72ddffbe3ff6e4ac629aa570a93f87eeaf850b5200e9bcfbd02377eadeb6245fb7795796240e330f288199b21813eb02dcf877362a176ecf630ded65d16e70f04e7adb1ef9295085f0473f921d63d03daf059cefd5dff5d9eb8b4f4617f56420022c11159fbdf098ef3f0e2f990039a0ff123aae1ab14052b406494b543d5bf26a6e9f7d3f8bf0815183e5bddb342f42b273c7e1684c714764f222ed47075ab0526f237b7f10d75496279d6df6191322c51762a9810a3282cbcad16af2ea9dd4ec0185a25d763e4760f629a33ee6fb487116851fd023ed6abf2677957105ed49f427d3cb05a80daef4d5440b72f3280e4396d8c252210f263eccfd60ed772536dc5bf6b8bb81dff8b0de17672281357bceed64476f49c66ee1d956a90f4c5c54bcb63e6242ac2cce61da569302f59a5cc6d3f2cf9717bb2f5208763512c95d77aac787944ba793b544bc0edc99184a667d3563ab0883698c0a7822e72afa05c1d8206298cc7fa124d7cf83a8e59850fd63ab6d3210d95c2c8ef18dacc6d1246c40ffd0eb77ef08d7b36809342c14fab1f94d1a5d25ffd0b5d117affbc0bf5d3ebd6199914a7b00839b5d75611f2bd08758e92df09b48ecc5e06b2784a32ac15b51482c7e338f4068803bb51c485cce282d5d5b6904e58fe077c466b3fb19e5c77b31733ae8d581047c7745b54e02275568726dd7a222bc6cff5657577c98d14100482ebdeb4803dbba99fd38a41d716ff8ea8c8a48dbc9ece2daca1925c37e65bbcf11de843e1d5a5d2698395dc80fcb27c1db4d909af7d567c904028c58260f420577c052ecb560922ea499a211e34112399f4c0111fa5049043fce20332912bd2389d5b22fb4faad18f5a529c68a8e18fe4d77bb2945225ecab574f031e6b9d11ae622d4b770081becbcb58b826a76a53d9cad2e6c5081ea66a41f4b227af299d955ccf3c3fd0021a38283fb480c4c7bc977066c4cf87d67ad675139ee365a9c26b438772bc71d6062be6ccdb57e64e3fc9f73da61799d3d75be94131f366ce60e241161365ac3433fe024c4e269cda7821c238cdb17bdb7adff1da244e5b41dd4e6bb24d92a5aa4347d3bf1a06482e25d25f1e11cee35231a40fe86fd924b1170a1b048cd6bd6c165b01ab11f2e69101c746127a36a1fd588c461cb894f9ca81b5d62dbcc3ed4018ba416b185a20d575f3b0bcef1bbf3fb0f5a76c452045e77daba010aa61e99db2096bb4b86a4e180ddf89ecdd92ceed25e0546b1dbb73a1f3ac63a36661869aac8a5c1a0c7db7d962bc7ce2df770c59d9af7f8c56888f22f122b81d080b7e7f3f5057179cb8a781140ae08769eb75c1405dc3b14cbd7ff3add8e50c1c16d2200830aa4c1e0e1481c6e08e0281efaeb48d6709e975ef5b1b7fab4424ac4b52893b012eac2dbc9018bbad34359f9d5876e6806f281326a19f138352c3fc2b5744fb72ad5f22170e947d295bfd0f5fc01fa99db277f2b65f4af667a70142f25ab72578afaa27c71dea4e6d0010c4833b79da59110be48d79c23df2df35911da18b905601a7cb43f73078591c224e79036bf9bfe4779a18eec492ae40657692870d59a6e3d04501ee2bc12416f56642d70130664d1be012b8204ece050a2735111243e109369158c3ad6b3a069760d4daac5ac4a31fa6340e34506fb8e8265c4a8c6287eebcc405bac80fbb4e19da0d82c00861bf215c5a2292035d8a7022534e7263e2bb5dc06726e70623a40a3f9f3a08cecb554a5aee23317518a42ca27b511245720a641b262108ab3dca49d8613f159558ea29a4d8cf1ef6526907f8888c78f25a6fb2290c6764ecf139b31e6cb256663b873931e469314ab2aaf09019485259b3ce366eed91e281c89d72450392eb4c03fe944918f7ac90d67687d256b26cea2388a76ac1d80c15a145c4b378168c8760a5c855bdcea1db3cc55bfb9aee3ddddd9701acc02de060d8835ea6f38bb79d3b5d63bcb1743092e1c264dced238e8010dbd1aa0580565fd0edba204bedcc1262c7a84fa186120ad9e59bfbb412725b78652cb3ea4bae67dacec2d48c10109a3ce23f2ac6a58c1fdefe95244fe468961f3b6624b598b60b6269ad7ec67d018670541e4284e7b106ab54c3f5d0bbe70ffa482069f946778af30596ec5856dd8e63f980905bc3f969a58b721444268aeb4506a6e7d6c4c86bca9eaa16e7fe7a148b84000f53fd3ed98f49dd30a4b9ab95c72eee62fa3ef26bf61608ecc741749385cb5180b3ef5f6b0cb51c75485f2b58637d3e8fba43f21e4612a47c55c559c3ea1b6d2a555f3bfac0adfa7a1ef55b9222a0baf6361e4f3dbde3f6d724ff4997f3dc846952e3d213c03f95b8a82b9c20dc3f361e23a689f8459678d613f25b4351bf22e6eefec997b1f11173462c09a8a8e97c8015e6600b2a0ece207ff734d36e9b24610d77538ed5c5419681764c312fa2ee7d8666630bcb2ba25cb04731f50e11fea8856d21207a413e21fa38117923e96b055971d1a0c2808091434ea1a8f0a0a60b66e7e8c7c8a137de7bb4f7299fcac6a09ae147852a5c0af7d332c53e4a6da33d02d31427a981ccc117e42fbcd68683aacdc96424dff0035ce0aa28e6f05c840b9e73718d485b8da3974de37c34c185d7fca386938909972302cbe5303cfb60b34978257c7d68ee3f832944cba429018ab04ce44e6ead98e6a99aa8946f6d4dbab47fc03a5acbc7945777a51213df9d5d956cda3dd3021f0c9262a84681ad934fdb297df6c3b5f014982d406661b4fdc2ad2b087f83601fba92b185a04a46b717dec2bb972f096b384660fe044c149d68a90040690e6cc95e79af303362407813319df750d3d3fbd91f720314b1fe7c5123e9d1738d626dec4a36c412d5f092372be7c47ce56d35aae131137bbf12662bcb5e2d77481e5196be7d69fe339672961d41c311d0aaec7fe0dccef89c47f0f25bda018436ecb6bd9066e906536400cb74f4a3f9bb428bb8ff0d0a51511b6a9cfc327a319cba8fc6f0a09f13051f24cc55956ad35349ff86c1189ac13c86a8032f5ac8239afb555ad73eb6c848c8c3f890c98e7f6d1e8bfe805e7308237cf90c23f37946207ed281506109d68090b99ad1a58cc317080c62b22a8fe055cbe8f2d54ee8c2064dfccc28578d51eed781d3dd74921de2bce5395ed2cf32636b7f70d78c287d4c584fadc81c2ce1c6e9c151699b421331977abc547c0b2aebaa56ddd75321bd9fcb19604d3a29ceda264b3ec93d14eb7ded144418459c103604bca45c422b7341a36e5ee14ccabaef1f0320704a5769735742baaf2ce90d1b1945e4789f970c864db7046568adf398aa7ff2a6d0ae3a73b222bf13c7861e6640017d29ddf731f20b04a23523847a3fc6263d28ceefd92971baecb7f2654eb5b7c2ee22b62f61e0944deb8a3d29656ac0b2703f266e5a05a7f5aeb6b88fc100019c83ac1aed58688308706cfe04557368075bb937297d931b0c7abcfc4c921cf3b35ebd263b3257ddf4d37aeb7920b633983d95eed16e541ff65934e700c3d1db8c2c151469d6004353b4253b6435a97db256dcfdf7877d9890a229f6d54555ad0407fe90ecce862c0fc47138c403eaf65467374c6b45bd4e1012a23537f36f436bce40c8789d20a53dc928ec067aebae83c7006b0162bc99fc12a2be49c17a0eb52e4a94c3e93421d496452823d4319526a1a67a4d56b93a766f0c85b0db2e3787d86d1ce6a4e00e4e5a57b32c41c31a65c11a4a81e2302db2a8f7fd2799577525fad2ab15a181019d4a9dd896e37144f23c944dcd3a66c20a511310d0424bb6a98c2ca6371df250dcecdd75343202a154ba5bf264ce2fa09f080b69d7b7051dcfa2934db2d85a002e839d5a94a232a0f2df08a59ba9b61929147bf6ea1eaba90e468b702cd17b204b1583073355622681c167a584dd82254de9fbeb6d4e445142946a804d3add60b94be63471fbac84ca76779ec9fc83189b9f9c590274dedee12a768db41a01809255c4018d22a60ec70d5e0df01bd8357a6b9a6b2660df5d1da6101fa8d2d8f4e3dcf21bae00794bb08878abc96d6fb985faa704aa605fdc2687121427b48bffc946fb506efe432c342b6fd40fc0a94b7c59281d9b4ced291a16bf43170be28289318cd65574316b1a992555ece8913f2307fa4f044ea379a71be270e3c81e987f80a6f701b855dac24bf0b863f4c11a3162ae750498f9e2d73cb79e629f08bcbed2bf4ee92ec37fa893e0f5217adf3314485c7cb82b56192da4b418ece62bef6245c23d3b301c741023ef931e7144c758895c97983738b3a08b47949bb9140807121f6c82630a69beb9f74807ed7a6c4eaf2095eb2c918a9c9a56e16732e6c6097b263f34c501c48187c94a5bc8c2bbbc6f315f5098108e6593c8be4cd66d3288096e1fe4c83762563f632ee1402346cffb5d861f4d5f724508783f040e1f4b88a3aa6989a76e667f0bc70cf15bdc9b04de39a7d73354af6eaf541592b3995ea1e1396471a706540073b33a8998e26d4d0d51e53aa1c6edfc79fb7afc74b7a085eb99709b71bd0229bfaae15ae0ce9450d9a81fd483c3792ac0fbf24240f75db6a8701726eaea65f06dfece16a6fa31b26991b4d39528daa053b4f8f835b0f8a67d082879cb70cace48c67dd399f65d97bb3a497fa458b945b2316e79f3194e4922d469139647093684f69462ef548971d74efaa6e51f93c172f4e1d317a4f528d462041d537c887423737b655ef6cd1783545e3aa73bdfad5659aeeac5a34d92eb424668d3dab9a0067ce6c8a9fa43976e29187804e6182e0716ff5aa12e48d92f71ebb50a743a2ced1d70d53187c41b7ecc37932acf69c08d1076b32b3ec3fac6e45c6658877840008da485102ae7f7056f4368e4c42ccd1d68a00f03e5a532966f28c99d54fc81bb846fdbe12737fe0930f1d4b80fa8e4e3798b291c81eb97f03f77f0a14d0199b0e933eb2197b0085c3b38ea4250c82881208b79f3000ab9ea44cd64989c8a141ddd44744c87ff492aa438dd0fdfae4402ed1cc3872851e535e7c34076929742480f88fedc7dc3b73a889dcefb84728ae071800548275c4bd5b91a418b9165a8855b4b596313ffae820b4b48e6b9175cb4cdcfd9c405d30332d88302f88e9db299af2bf6b30ff1849443639f1c6bb456d31bdc8b463abc07aa9cdf500e739d871425c0aba74349da862c643e067a0ee5af1420756b301f6604a6856af9939cb89c95d88934a2c5f4fde3c5235a86a94a99759997cb4a284a59fbb147e95ba33969d5629124f9fca8380d7f9e1f558bfa5036d95b2510992d79e750db7be25ce2c5a2bd6f5163c5ec6aa17c4f50219e5a7ec032c65e3ad715238a037e66888dfb82944f1e275ecc3e53dccaf7992633454a574d0841bc931f7ef9f94d86b0953b7312270c8af8bc1c55170817d41ea3d640b491bd8d4a6e40e033d00db79751cb8ed88ae4e5a8ba11518bb5b25d47fc749b4d42628e07411fe6239506f9fc23660530488c59dfb0a0cf996540c9d04ba606dce774db9b5051472d0ba19ea9219f67aab8879f1db3eccebe5ac615067171f3d3460a13ac6dd0bc4035eb54e7fa4d762d9687cc16bdb4893a9bfe1ce95ab19ecd20e8b2baafb52659bd92907ddc4646e1c29ce58bbfa5907531d341c127ae88ac9202ec0f66977e9af5cb699047d20a3ac39d06996aa1ce61b43680cee52e4609592fb1622ec2eb50965ce6a2937b0e5bd66f3839c125e89586cf195b59a8f2ffc6dff78644498d9c91b148971cea19f2399a863f53b30a0c42e65fcf0a7920c415939329536bc868ddfceff13f7aa5091e39f58215a7f27c532accfe441b2cf19f7bd33300f1475864a79276fb9343c1669bb0f75a823410600a9c8cf31d2d95ff979e2b3b253fb75242f6881a2092ffdf13e1e7e55e52095c8597f3db91d0dadea52f207ac9aa04c3cd92bebde6af1bf91484f0d0b4a124c9b4c4943cfdd26281a7247fdc0b0305b900ee8bfa4644e503f223b9c6e700e560957d8bd76b6194c3e2c472ae9561e2c5e8c584b7d3aaff828c3c6ebb16819244668dd7de81c94e70cb5e644c33b0014eef8833555b80091e8408e760a1df165eea20d18c80d22a9209ef4c8b09602d9f408023ad57f144a8f5b005db31945c7e000083b2f56f25001f64313c45f881e10a71bd1c633ce894eef23c1e6526816f4a156d35ed50fc7dab8c1973d823f86be41b9993a7535790bf278f0416298a1dc60440d5b93fd6725c692a340280c1f9ded6066637dee2e4f85aadd43710155867903bc19ec0aa8cab3190b76c6d7072b535039f28a578bd38cb46e7776c9f32dbc6997f23edd6b3e6194032105ccd3ac87af82d128e314c438e13870232df81c0ffcf475f79651ce4e81a9d388094978e045005a2ebccff58927509ceb17bbc9f6420b7d8cae087fb8fd9630576b3fa2884b72e91db6f60bd68a639f6d7cd1093425db92a2b94f8495aea502e1c6296a0758a8f70c4b4eee51e73e938143add4b4261d0690db67ad0a8b5e036ba5e225610a6b8f54612dda7efc4d3faa51c6f1f5298ea02947d6b6b77f204471ad5c06da43d94bc7c0c20839c38c83a627f860b2e8d43ca982b9745df34d2ded65abeea4ad16f46ddae0f0a9906f9bb03b4c2a70ef36b7362775cbf72a557b261c982f6f0abec06ca3c3cb2de26834fe2afa3134fc1554ee0ad23701da71f95889887ae185b277067738015a2018deff8dfd5e4a6d9ef340b5af998c380dc1f969122b9fadd4a707a19212c7702176252021cbc20631c3ade18e25adcc18c5dce2318a7006626c3db3722d1f91c9dbee0219d7339045b67acf9915b248042d8808bc31fc7d41f72a293cc157b2ee75d8628b7a89bde3c28d1ac36717afb8d69ee969f49020326f3e4a70a1fd4e45bb07a50cb2a57d3473c289560a97917e2a54caf77660552ace0c24023e66c0732be3423c802661a4909b6496f165afd346dd064c5454630fcdf7ae422d22c30092a8f8a5bd3f1e79c56ce0a262784a5d258e5049f9ca49ea459a2b00d7eed2d69f03c11a8d09e2df4d9a29f4ac6b502a24fd6d49086054936e06e1b30ad800298d777bef02f2dfc74567a0fdc728bf3892e08808e1eac339fae9712401a38ae22cfb04b61ace687f922a7e33a7154bc52211f305d3d6229cfffef79c0e0e3f91ca147d5dd2b4ffa3e24e303d4c35c3f162dbdd86ccb8d477a94a664703aa4847f93407273cda27e789293fb83312a68e489ab9795e3eebed0a69db1d94c14a79f2d3cbfc90d489eab14d1e14a79a2a015d421ccfd374a4b0b9b92ebe819e24c242fcd344050c3b5551cc517d082be72fd840b8588899b0070cf07bf79b859b363341e34d4c419bdc94b6efc4ff5ed452873d3b9751e816de7fe92f0a4a4e3f9f04fde1b5508ba9dbceecdb3e5eda3a7d6c918342a2b81953716a0b9f53e8bec34aa4ae5aebcb47eaa2e300b686d9aaafe36150a171e51a79df988e37fd108bbf0fc6d4c08d6c04e77be1755473399ce1b34644e6ddd98c8759f896ada975f9c467aeb7658db35c03d6743a6a08356384637c9da5898f6d38318b26588e7fe4765636df38c7a1de2168eff4462a8f9a9a352817b091bca2c86b6ac507d05bdf8627ba93271a66b8c3bd901aae84e2440a0c18d4d19c08d3ac6c5bc899bd7cb94693f80269a7727d6a61f5d0425e194db40082708808f8a3cefcf5c58d076707322bbae9e553c89e147bd830dc465a45d44069877380dea69645ae8fc0f51a997134dd2b4534c8c92a5e998e9cd937b9f329b61e9ef0efd3561f960e5f9d3fbcd888bd6d5534ab7d919ae216ea00a70fe102ed4f1586b789bcc7518c8ce216546139b68c166b43050e9acfe47d4de2953eb9afced8cef31ab9c782e4d2f860ec474d90ef8eb80ef523df4fffa73e9f43dee16998b4e9c86767c908e6276c86e36dae56d9597162e482d183b0d7de4213f88599d19be71568fbf5bfde39f526b840542d9834828f8c78fc2218d1cecb8c9a41af6cfcf49333d4a1bbe751ae41eb32027e0a92b10539860651ca6c884b58aa80ee7fc7951ecdb6d2ea5de80dce4831f117c25cbf5b3d3a5f843921098406278fa3edbe96cd6be8e14e4b5fcba7eba2f0e62f8ef60c3d15728e0e78f5a50759ccc881595721476ce2a0f2faa622f8a999261a7ffb5685f47aa1b89134937e455cb69672c2b33428fdab083027b40772be148de0def08e4928fbe6d37d0a219e13a84870b1aac919288660b94d8ff3304cbe7b429289cc57120d56ab01739eb33915c4db90e53b3007561a1f39a1e4621cfb0a1318f83828a0ef12b74d09393e1943b69a4cf1bea231c5e4cdd34c3da7222343dd481ca26937747ad8534773ff61f6a32edd8927aa6c344a379aabdf91cf0fd4c22168e55e4fb779ae0a0b10bf2d87c9e4fee6727eefcad29805dba623e8f2a3e99280ff4bfd982c51a419d126f84c3d1909e107e72c97b0f6046fa539596e7edc662c1200431e79ef53b6ba8ab890f3994fe2ef6feefce11c0d3a534ce2c255205c7023f072ff0acee5f39e92e1016f1137d7cf0e37a12985c081e16b3acf1cd13b5ee28091347ca7e513715ef32b538a39ffc7f7f05ab196ad987302f817c45c173dfe0e3f718cf34f1d2ae6b4dc9fc823e1ac81eab2ec91e4cd18a12632962381a0c5b7f945d0c49af95bd4c001ce123c2a9901dba3267134c6dbf6aca598231cf3dd1431e125a16248c8aae6485af098c2a0e7058d7a2a0bfa83646956f46ba8ada80296f384f4c15e0df263008e6225a3d38a92fe5b698c8ee41083a85d1949fc5cfd889893d974fe9111ffee4fbd14e5045df88775c659d5841c973978583ac6697bd4f9330d3adb01712ebf85b9d577d5d9a808db944728764ff193d67b377bf860ff70715d5aeb3682856b1220b7b1eee88c50e18ec043054a6858871f496bdd639060bcb1118f672d4b746449c55fdc25bc6026459527fd45427c2792f1ca9ee7d9c2fb81dc9149500323c294e2b066736f7d69c97c2a2418c9772eeeafc8fce9289f8a240db3c75cb347f805a655312b47d02ec0e374bc17804900d6bb8c384008b074053e5eae81597f2ac11aed7e5d4e17b94a5ce618a4c54232c7b3352fd069be4edc11eff11704b9de3022cafb5e2c755330a90316ea73cf2ff0cdb51e40895ac82c66135edcbf5c18e8d5a4e56825a5fd573c707692c3c5678e855fda1aa0cc3c67ae15072732610d4ccba8917209779f2b94fcf8e0eb0c16aad4b534ca2e2d0cc66da713892882489c65a7fec9781b740a380ce349529db15f58cc03cdcef4264c2ae9e15fec12ab07ec9fbdc14018c3ffc917f5ebd9243bf0407b37615372cba1cab61f326fb38a7c4cedf731cb557cab3c536516449b9be0398a4f3e0a370f9bd54d760f634354373735eef37d66f4983202359071ae2819c7358fff37748dad66214bb18f8f30a741c07aa0d506c95c809617958297caa986cc916434b5513fe526894f3158e26e425b30d4a5e701a7481fb80e7972c66c0c391672c1544d0ab480e0c3e35b44de5240ba258593178714a901f536e208b5d93100ee095211faf64187239a4c54ca86f7ba1dc35bd35787fe751f9f113f9a494bf28eb69224a30bb64d24f76a07b881fdb6b15e64c641e6caf2dca9ff62ad1286364264c2fd91f325c0b971843e65e201b6f8ec9c2e11d19b21cb68f85013f4d8ce3b7fd5d4b7d4faf78a535d5900d9f4e5cd7a4cbf181a5a4624f2556b4b68b39ea86451199adedeae92088fffa5961305f16ce24a05cbd218469fb0cfa17bcb040f8b2cc936460a55774d045c1a24094e9c6b1288c027ed7383f87082ae599459a247e3d2394f3f38a4795908b06f86c99b1415b5875fb9ce85f516aa412d01025727d692288d8ad6c7d708e17a9f2f8057c2a4775b46673620fd4d441b9ea779d25887bf34b070d92d87d1504f4b8cd8e196c6d9f7b061042fe9b5889a4a4cf2a3ed8b72ee6072158e2a1c53ab946f7ea016b5d500a2bba8f3cec94fc269d9bae1df6d4d0566fa16491cae3c28387750f79c2c7224843b576f89bfdbf1b92446f7db1ef99528a59d2eba0c0d0ca54dcd11a0bbe54e1881b29dd824a4ccec630495495080d7d70582212ca2d07d6c3d1164bc9d1e08278d770a89eebc63f1374ff7253d1ba71b4c97c460266abe012d60c4452c5979ff750ad78a04d551b0523b4108213226e1726d8a87461898bff29302bdf15ac13358e7b6110f67b85328b94f524232b71a4a9a165e67ec100dbc0831850c89aeb5aed9ed7783879624b4779cfadf8c7629483488820668c5cf6cd6100872581997784f06a61fb3c26f8460d72b5303baadd3c638403c68bb35f0e6b9e7c345a547a5fa670e99e1d111ea8560aba2a56d40ae73bc70e2016f5586d35fcf2b09644b242104f4f016761c4c682611d31020ed47904e5d009f67019ba06417c040bf00347f4b665e895a9280f729329038ad213f8a7a15c6a039ea9b59e7c01858b0dfe2a1ae8101d3274945bcfe207a73aece7f43256e9b2b6ecf6ec11712548a196718752c748df6811b9de087cb3f3b68e3057436fc9011ffddeea135832387d153d16d05db4c8e610a33e74cf7e0c793405cddb75b29b34e9e10dd0ed51ee3f37a5e0a538eafd255da260640f9f4f59924018d48440020ff1ccfbd5a96318941f032950a3277519c0bf4e5f96e35d5064b54f176a6641ed8d134659d237d8b559d1e21c8572e3110f02d35eed391d005099b4ab714efbd178dbd08263ce517f9f2cc31614f7baa185c6d9609636923bb00efd637c52c76b75c6f3d1710df2f4387de90a7550a8ed4cf00eccf671c713f3c490e46a05ea23e410538073faa2048aff2ccbc425552170ce5973df59c694c68359cca18fcade6805d230d64549cfb9b8d6c38a43d0723d3e028ed375a6e286e3cc0bb19338ee1e0c3c04e72476ac41529a70d19298139bd273f4e8768c7c51864131009f60f2bf8a0322685808dadc49413ed0129f99ae9a1cd009cb76d92d36979201f27ec95abdf1427bbc218d22c1e08a89909b81ba40f7a693d57ccde08114c5cb03ee359658957b5e4f08542488c008e58ba594b2f0883f92f55e38b11282229221890064bdd3c2d73241965e555bda1fb8272fd988aaf17d3e48cef92528ba6410a0c9d4e2f6ca40606b209960cf6806b0a0d1c09323fdeb6ac8c24a3350ebfd2b8b1b4a99da9873b0ac5eae9cd4ba0d4df9dbc0714b63d38d2076638e6e17599e618780822acd94fb18ebcb10cd1f0d8f0193476326cabaa37f48b404f66124535d583cccb4ecdc02a2bceb3013bbc9a0722333692862fcc691dfd20128e008049213e32293353bc702144e7486ec2792007da11051a47be1219266961d3b0e1dfa4882f30fb745be7036584543d889efb24d998654d8e8b6ddb7a32eefd331555574981401ad4285986dcc16f46af11b223609aab77d5c22687df174be1d314209557948e434b5d07256768369c239d24dfb4bf5aa4c483a2681929ab2c4d79f57ffb1b62989ca05ba7385939ad6d19547c950be4276300b4d842c9b15de1353589c3b0109679614f3e16ed32b4018bd1b49ba2f1256f13d7a4207c45be7b0fcd72a02dc53bdd0eb4316495a16923ed9cbd6fbb6e90d6865981f95eb81aba2372e5e33b6695407792dfbb8f362eb17ab7664e3554bb710fa084c6f0d3fefcc66c759cff83a1f6e3dcea53eefe2a9c161702ce27b71222d14f2649d7fb1422a6dbb8ebeaf35f851228cb6a9345adefde1c7d607d0f410824c1849d661a72890697c8b9faa9d0b06fd9382f2e34303804210d215255520003d1ab8a1204613b6e84dc2373942bf5b709260ceabf8bbd56710f59316f641c3c5370f45ca59a4b2b201de24f74a49821fda237abec93bc3c5e6496f717c7a83618a790cd40af5db24da0e504507ae302fe0cdf5fac8638ba0a90fea8a26ecc5e5cfbaef2ebd573cb945e64ad42b99c487e1823dbf09a443e79af5f2a1c937116b6486bad831f96ba40301288dab40f875ba2983595289eb1db8d64a0aa98321cb82aebd7b2a88238b938bbd657dceac26dc9a08ec48496f20b21fe18e5f6a2b0ae241123aeaeed981e9ed289ee49bce8fa92bccfd952f2769ba3210c8f499cc0aa52044a1bfa3a3ccd983c85bb61553a9674396d840c2e30c1fc231dd1f01615b3e411d3e71191572bebcb813551bc6c0aa88974ca0ea60560c7bb093e1a2d58f505a31b6468ce57c0c0ccf13e611da6fe38ff7cf2bb1975cb4877c22c0af8bdc522c27c25d7ccf5d2e7798e61708b91638042d7c647e2fe5991c7c3cf3e09504de1b91ac581c9ac38e9dda2afb74c0113e9897650ec20d7b87718889916d7424d9b3920a3d4c67408e21253f4ca2f0f5fdb4a292d06fb12f2d9c6dcdcb99982171252e0e665442ef0382edc88a1c1fe0ae1cc490d03bae67c0178b40c43fde071260eb8aea1f8cd1e498432971b5cfad0f7d494fc86d40ac1f9b2799bb3886d8ef53d26434aebfb8d666b3b62a83d26f733971c1b20183278781ab092e1155bbd80eb82be4909343180ba251a8054f36f878dd7494dc0ad50ec04c682f6607434382bd59558912da700d04de733739727f27cc4e897176b9f6e9cda1d338f46c23f443d9becafe7dea86f777c87e02ae8ca6ad096e105bc636e6221ba6940d9b730a9ec33f33728b42784e5be98a1c281c4cc48dbeb1ea17bdc1030181a5fbf8908a00211aecd9bcb48b02dadbed5f5b409655510719e8511fe6f0564bf945f482aa9697f6529b0ef5a582d74add2d97ff843f2c0ee2271ddd8a06be6de3194d8d3709abaecd9cb1583048e1fe418de5dbbd6c7d90b45f23682f97970606f26b4a235b24d97b4b29539232d005db05d305b3fb2da00770c87c414a33d22bfa3d0d13d21eebae041cae5a7ca9b72d2e5b64ea8bd18485aaa4320be0e3c5bbf8ac37c3524a795bcc90597c31aa7ae48af36ea8c9f75331e612704867b4206839a4351a693025995221d3a7b58689b4207e502359ce294390bdf4853297e67d5a537d8c2a9c551fa3eae737ffa55713749f5e717123a17ca4055124875f7b1f55c35037f222d382c587a6d2d352a9572caffa6e4837d230913e6e01869c090bf76998293be6f4390e632aa594d6efb3d4ce3bb20f173ff2390e6393e97fb5622191482fe9bc2db8d07af1a98fdd72f174e6ad17204d5902ffbb37f3fdcb03b43c7db025c2a89e72d984634c9135dff4aacf638c0160c9f35f7cf35d7c31b224cb17b27c8b0c53454b8113d9e4e51bb123bc5aad565506197ec562d5964f8796bf21b38a24e79c263fef4b1d5ac0d8ab9657813864a63449679b4dc3b478fadb8db75c3cfdd3d7e9336b79175fb3be5e7dbd4386af71f024b913643c0a1e86e8e35bc86e85d8e33fde04ff6979daf22dfed3f2343eab5cf52d1ffa4f8b162fd353b67c32fb8ffff811519d177c93fbd19b71579e1aa13f0d338fb47caaaf756cd7d430170c5bfe3efd9235d121f64af538646e015b4c8143ff9169fff196f7a8c090cbd6e4c6172e3e24e5160c18fa445c5118208d225a41c98b2804b9009790e9870e74005a930130c2ac39c7d1f82cfdfebcdf620a1ce219a8c37d1c329bbc0e2d8f43661abca7a5e7e4c64955dfe26b9b5ea13eec58a62fbf1c6cd4e8493d4da940995e51ff6998fa74e62cfaa5960fb95c4a7994a73395a72adf02861cfd073fdc2bfa317ca1cc317cf4682d65092cff472f7a5a2b427da89186e9567cecd6af10f991cf817a45439eacd1e7f00569cd27e83ff443ff91c227c6c806906c20bdac350c7efab448c3f890bc6779198f79c517e2d47763a2bed317de155abbb1a97c3766ca179632cacf8d79f2e190d9042cf5ea3ee99ba32fc6e7354b3e95a16e93b1e80478812a3142ad1a59c20b476c8ec9179418c1e69cfcb0b1c8d5bd5f6050fb033034653a84c27ad51cc7bdca94c2a59e229136030c252185d5e846610d23039c1fc78165eec79d2002a927ba9a88ad5b7ac2891e4bc3a104ea09b9155297a50a46a5d20b9ae2e42607124928a199008925c22489124bc44909ac0f94885e47660f9fe1f0c2600d537fa653f9f31234fb47fe98e285e51dddf2822eac619c5e58a9df0b923f5e9094b3ded92d79a4594d1d943f324b6f822e7a6d332c31ac48a14e2b26951494d28909098f44deb5a18edba24b74e9e8125da24b74691f0e2a61c6ec957c79f11a721cbc4ae9fe34fadddf01329c15df4ea7956eb34423945b7a34b6bc7938c8dc5ebf9d3de79c53464ee4d1a0df14cc3d9a21a38b7c59c3860424e09c15378fc6175de47fd185ebee68c6e53c1aabd0a35192614dde3c1a188f1cb0e5f9768443c8a32167e6dbdbf7de7b65e444d3a3c145975094a190887e0529189279826107b28354764d58ae5b36d3a6be0d8f7c1f56fef16b7228bfdb87af45d81863ba50aeb365046c0c76762bba2210b480934f1677cbc68627ec97efc38b6b0492faf02e9ef93579b2048540ca09d828e363518ce17070a026e3d840cc35397a331b90619061c835525ac0c62f627be4901f74f2cd91f6438f2c9bf6838e48bb59924b5070dacda4ddd032ce9176232437096c6c18cfa1ddd072e4c8d6ef4ffbe3eeddb37f7ea13592fdfb9d86a4d15cbcfe63fa47eaee36d4d5eff6cabfda50c78121e6f2dcb0734f410e5f8e03319da8054cb0265312ec6ca3ce499ca494e3700fa1dbb10dcd76efaeb1721e614facf74b529ebf71ef7d14d178d786bacdbd3e37ab778d2727126c13217bf7c46e22b656feb27a227b0d711551abd5c9a4b232ab3415b09173c8fd610d91dccfd15aa357bda4068ec3d864fa9fb1a9f00087f226a7616e74348c7d9f1f723f1ba68777330308b099651f2008c106e452f915f0b501b9a22b757a95ed51bff2f1f1ca9f50354c6f025114f52a6ffa1a577fa98feb06515fb434e8035026d76b0372bdb37c53319d4e80bf27064f23e61ba6e8d24f8a2ef2b97f7fef7e513ecac67dfb17b42087b261b9fa8571e69c63e7220eedda4633aea69b12f112bd15faa42bf475df0c70cca81a36fdf2495966147d9afba1a72007d2589bd05f70966dbab7a08cb3b60f7df66b5c3250b47b1ba10769782868a37b0bd244676d4f53e3da5c263d308e2ef449d1a5caf6294c3a37ae3b896e1a6ab8e194230d0815d994238d070e7268caa1a9369158ceb6941d65b70c75e7dc8c3f65d74ab7c979d72119ada87f82dddddc102d675987f8b33bd239a5bbbfc89873a680acc3fc0efde98c54084a3b236183d71c91449e48c8adca513480007264d950e38388449cb5f45c43e554726d8ca1183baebbbbbb6b8c74c6c8354c7f777fec6e19638c2251478417df866a27c1d1a87259c618efeca028bab428db6d4624126d367459829e97fb634cac217b6396fe31267a72c6e7442391c8dadc2f3f46d51308fa052124164c5095681b9e2a821bc4294a1084828107403f3ffcd4905dadbb09ec39d278dc60c604ee1c693c64b51a3672908094b51ccac8fd02a4791491fb391a0f9d1cbefcfc60b977020e911c693c70721339d2709cc82a9f1263eaf7a3c498edfd4b3186fbfed923fb471a0f9064077174ee8029873cbc5eaf8cc334caa28c6f4ce806976fd00f759386e7396974a95b74e9982918e6f719945ae2068053a6c0f644cde92f33d8e8ee8e359fe9a6fe544b296577d08e9d3304a80748577de9aae0cd756d7f335d36f57de06c4cd706fa308291ab7ebb645aa6470ce8f79f33e6f7db519ca951c11805103aa64d4468c61da687a40de75143379c2d82c38983d303a76146a12ff4d92080f6e67bfa3b7c1fc625df87d2e67bbbdd0dc4168da9501595516806fccd9bd93ef4ad7af5a97c5f4876e80b5926b0bdf940f0e6c6f610c9d1ec62f8eacdfc5b3981c8b423cd9bc8a22fe98b09aa4f1564a7294c00adfcc8d8b1fc572a954aff8db69b15a9efdb4ea70fb59d9c4e3d8480c3899382f3e19c4e3a603737372b52dbe984da4e50beed7412c267714e9ffa152b52a793106e361d3737a793ca17430cf25956fe0413599e3e8dc96492e96749ad48a14e2b26951494d2c9b6fd643aea58ea6bd4d5fa5ae1e74c130cc184ea2b643d148a398618971a86071a69535f4a78a0a9afaa4334eb0bd3d4d7fcaa4383689239379fb587d86a9f49d32bfaed437f44795002cf1db0af183b6dc9c842d181f261dd8162b93b555f7b60f1b473640dd3cffd8083c894c58795471e7d587532ad3fea6bc2542fd3a31942198de66481325150ba1bfa6eeaabda3481edd70f270cc576942ea265aa5281f3659303079bb0099bb0d114894411c827b672187572c4fad0d3206fb1ecca06d5570e1088c08ad5d58eb5eac2edeb3c79f9f67a22195832b80eb58668ce393db0d624920609d986f8a9be0c4e78229a917cf1c0438b6fad7464431a869b3fa69019cb3427d36f316b8c649eb0099b3b260ed8b57c585f2bddd3b9a346bda1614ec0b9a3c514b867c2bc355fb0d10e8c0383591e9806b09b37380dd34fbfc6aa8e476b4ea6159669dd4103d873f23a541a1ebc355f933e4864e6acf97216200f5d104a4551f994a741d67e0d6af96a6061e9c0fa02bfbeaa4dbda93a5229534e7d8ca9a73b7af5a2dfd5d70884d5970facfe70e3b3be6eeab6e22bf58a7eea0b714889dc515ff515ebabbe6033871376430e2430c418a45f83b77a30c61873786b69d612ac123262d09658e23c96585284a682d98e21d4866083d05c0927a5e22611700843080d889804021623060a96d05080848682d79cb41320f166b24ce7ad31d4bae5bcb5c5d40286abe7529c4e4866129e62ce8ad25b305abc098b87f1c9c89233191a62de9269a14135323428490ba9f2b445f52e7e82219660c8f5a54a9eb6bcfc66cbfc796fafeecbf8aba494561621d22344189367f1219542f5214d42e943ca0418950fe9133295c275f1d9f0ccf7e15d2e5a3ef5f75b2609f78a254eeee29229f52a5a59a84a2a959bbd8adbfd0b76c350696949b57c4a050ce7df2b5f871690d27a25271229a9090b554945b5aa1b2e9952ffe2db66dc88bbcc806adedbf2342bd85902a7ee0811e447909d9dbb13e4d2e00a57b83ad7c79dc195c1cdb931b844dc216eecc260e70a715f707b5c17dc165c1e17e7b2c0e50b16640c5321872c5a12e6381906fb8104c95dc1dd71557053708390c94c2129a56c5a5d958b457a0aa7b4561914ee930869babbfb9cb14969e7689ee9ddeb8322091e28a6bbbbcf299dd25a3d87f34cbf318923143e48d9eeee3ee76cea312826134e6b954d97d305f3652ee8361e1e9e1777fa9f09cb1c944093e33a47f36c7573010f1d82ecfab2d5edeb5679622093c95af2c48d0a9e0b84bbcb2e0adca1e00e857b82abe39ae096e0fe706f6e8eebc325c1b571779e3b822b82dbc37d85010c5f58927a994abce5a71c7f7e9cb330600a79915eb5fbe49612f45a74e957b2242bc9ed437c66440e57e2fbf6c98eee92e5707b754a6aa11c1dcf54314237938d33e5ce67369bcda8d3ae36a340ea64b9df46d9dd15d72a6456190daa319aa45b9d4d9579acca3c56651eab328f5599c7baea744e8fc9281552b6fb8c4f5932166b1899b3e3536b2e845a962e13e2435ce6f54569ca77e43015655d11dad56807eb66dd8e6edd9cae56675dadceba5a9d75b53aabe5beb1ae56f2eaac83dd5ab501a631ce39e774138eb53105aa3814493f5d92c46649aed526f6701c1a14bb9e0db01c7acc946950affa7d625b905d1e1cae72df4d27c6ccef6ae3b295cb562b97ad5632af2f97552973221d2c956dce91c3aec25e397418ece7a405b00ef6816ea672484a652ac6c455b77e46c7e4f97097d15b055dbc3c78a674e430255bddbb4ea535ab81c05727c62ce956b7a359fd468c1c39820449923aabb30a76b02aa40ea932d7f9d1ad3a2bcd3010dccd28175c567d86c618634ccd0903731d487c471224b99774921cbef492f822417fb5a03e07e292bbcb8660a32a8cb3d49c31c6186b2afb636ed571ab2f06cbb501966f72960db0fcd0347311b48cb9d4ac54b6d293207ec92d5baa1d202988417501cb151376960db0e50296afb2a5a4505452a50a2c3ffc925582e5db2d60f9560b587eb459c0f26d122cdf6201cbb7556069af40ba36c8e4e45a2b946673a9dc01b15548b9960a8b04cbb75458b9760aa76ba580ba1628756d14f08a1b634c2ac7b7a915a8d38a492525258ad1e559688e8daa983922dc903b84b216836a45fcfbfdfb91af4e42081fe4464110db0e54a81c6942fcc85e8e342160a0058f14b4f7a391a961666be410aacda0912f67f9cb9b4ebe603833baad05b51c693c7c4239d2780821b78fb71ac759fed884dfb4f2091bb6eac9de8b644cea38cb65ecf6ca8232cd4d0a5c60fb9faeb3d60978071eb480630c7df54dc332aa081c8ad125f432bdb9dc9b09993edf0f9bb010e27b3fef4b5581ef47f39786c67bd1a7928432cba605471fcde85f9ce52ffa68442fe34b6501875ee6f065d4bde88baed0573b707a6c4945757e5ef4fa61a126643d64b26c471555839452e10f355f34e7046de0178134a8f92bfc34a237791ca2eb694c5fe37a19ceaa5fe39ae00d1ca20b3babfe08a4e164842619d4155d261f72134529780387e8aa01d2d450e36a01061c5d22106503bfe83188aa711f85ffe4a54b04f268c0856a100501d193bedf0369781ab42172897ef435ae4efdd44abb02acfac8e1ad22895c452c873227d7ca6df56ba4558193ebfb8faa851cdeeeeee8d9784ffa482b2247aeef4ed0ef457a5c437a943df95e5e0d0f44d9b8effdadefdd97aefb32dddf0224488448c6d1a40f65033fe931e8b1074897f7d2e57db7026c1f25f1a3fc034a4f024140fad2a38020e0b141fa12ca83a0f4184475745da057f551be1d9c855fa15aab247d25f9ea551dbdc8e453be495f7a1bfc2828f9295fe332f94c4c5ebe490a488302ca217ff2d5b82cfe489f0e4aca48a4c72f256803bf0948834179e3acfa3424b0be4c7ae009e2e8329f145de68f409073d67cb9ada9ededc0def8af5f7e1ee0f206b63080e195fb81624c2bd37f6f85be7b8ec3184c1101beeffdf620ca419483343cfe34a1f71781343938ab5ffed7d9da0f155f848a2f8af6e35b1148df822b67d1a789ce0a81b6c63b8bca0de3e5f8dbb6999884de4fead68572076403523fcf1d900d48e70ec80644e60d48059f7b8ee4d9c0fd06863263903b01c1d1ac067748893cd24909252574730ced90226528248d50b991cb33c6442f5bd3cae42e7e959058b1020994949108af5cefc6cc3c853c6483c99978337e22b37745de0c8abad49598563bb0b078338e83875eb5c908131bbade0c94941248eadad82848ca76efc68c8feeebdfec5c3733f74920079f0a488f029eb9ef069e1adbd799fb669021830699598c9717ef0df48ecbb62bcddcbc3cd2c69b99a02814726a39dfe1f5e2ba1cbdd57538b8518642de4c5595629c74935ecad2e7b4d7bb31e3bf99bc668a4b9da574fb62a6d2a6659c5344c17bbd99fadeea27053d37b5f16628284d3a18b58c3e4513e4bc1b1b66462f77c3789d0866842d0857a83f66eede5bb79214a0b61c001981e79122e0508eb421a6c843d44aa0225343d72386004275028f72601a820627280101df17b0608820362130ce9136440fb50b58650a4dc05e122a143126a2c037475aec881e6c73a4c58200c566d0b4980b4cf03126a442027c2660931c69b11fa811f08916b6805792b01981b92328814b48d41cec095102f672a4c1c047119c92230d0641e4d084038c92234d88259942015b61e6f688ed02167810e1072b48928319e88852bca00829b8a04415456081929accf6c41021b0260c310218126a7e50515392049d1a1489814b882aacf02d0821851d31915d092a2cd7302b2695146e43299d9890f048e45d1beab8add2e92de38ae924729647c49f7c72f35bc9a6ac92b98abf948c92398e2382e5f79c73ce39e79c73ce9972e79c7382abceb3c19b5193e96f5bfd21966b98d376caf15bc9a6ac5253324ade3e1a59d3e340ee0570b996f249b6ddf5cf24dbd0f58f94adbdfee16ceff56f94ad4894ade8fae7653bbafedd6cf1f5cf664bbafe85b23db95b97edc9f58fcbb674fddbb245b12a56e59aae7f9eed8a4dada04e22afd3bb5d094284e4b0d3af11b84fd9e94f4a7dce193f4a9f73ce99e75b204f1dd8a80a718cf11463af4c934a8cb5d294ed03f5dea05694ee08e9331fd21d01cb9fa1bbbbcfee08bae91c61b2713644baf64e1acba1bf17fdded94921f2a2bbfbd411559c1ca5c0f2a9e738eedd11f5ddc668d2c51863f41ec41823ad2a1fa857484d319df4883d55b9273d60734ed3ed8eb841b0fcf9dd118e4f2b8d3a49f794bba3622ad5a2c3b7bacd6de66035bfd356a50948e5b6c944946d5a392bc60c271c9cb25f6eb582d1add446858987099487caba072948a18886618981660e95f188c84c65ae15e3e505452fd7302c7426a193ca2ec7733a9d2e366d50b7cf497d525a63b5b9aedace13792292c9490925c5b47242a552a81531b05cce4f5debe9f9699ff6c9fd96a5673da487fc7fa616d2b29d96812d293f5aa77554284868e7c4acaac4a2cb61c1824517eb7a3a27ba9e8bd382eb285017ebbec66918cc652979d4f30496ff7ddecc2d02b6b47833b7884a06932ea624c85941826208ba319ba45b1ca594520ab4a26f529e8f7eb5ac484880805229140b163ef2f34381502716cbc58b17305c4869c4c7e7b47a418144b78b359206924ef4aabf47f650a7441aa8811aa8811aa8e990069245563c591bc55ebcb83517176fe6167177777a7d6e0d00a3188c5b73ae0867e4080e6d46814cdc100b8302b950a0ee933a32c60de180620c916e8d729ad56fc4c8912348909828388ac9212a9e0f29932e00b814e86585dc5decd66c100e0002705f687773b303267bf40aa7573bf645dac89715c01d00cdc0058070ec40d32b1e064000e782281005b25123d62890f27ce48092d18552d400eee5c13128109db786a4cb895d8c0699516d94d32d9b24766bb15b8bdd5aecd662b716cb8db241315423e9a006bab55bbb45bad5e574b11bba580d36c68de1cd4c5a003a2b4784a3714e50200a44815c07e68674399d4eafa89fbc199fe1e4c928c02d8037530b40614231f4bda9fb28d0479350257489378325539006bdac783e562b986ec92861dc92320c703919b1cb6996016224d8c528504cb66e915bbbb51c0d401f2b60274810d3a4d2458344d7137d84205104318214450f7670a2e8c14e1494d2b943297863a59d287a98b13b77a20042101c974174e074dbcece4eca7dbe766690d892ed0b3b380198a3a894acd816b96488660000000823160000201008070443b14012a6a12ee70014800e6c963c685e32124843498ea22006c22008628c01801802903146196768e6280000aafe0b16ca9f0fe7ff24adacc234b6cfbd6c9edd433e03e56ebe0f126bc79cc1865cf081bebb175de0bb4fd18f8aeac66fc0bfcbfb23ea48c52ca888019b2c995bdd2963ef6b3d0e0518b3372fd08ce1d39bec19dd924ca2033e2617e83e364dbe88f2860ba87f7d721851c7f76cc9a0958e8550d9219c98a4e1bf156a7759d01321138866966c6637dc9283c7c281fff4693c4727dabff28336b68d6ed1e1ac3274c3eb8ee791bb294b6263d8aff139c03cd260d2db90a6df7caa0a6002729ce14e6544e4ef27a6191a24626caa9af72b1d00c7ba90dfb2aaf98ba80cd8b1aecd0cd5271d2004cdc58bf7dc283ab69ff3ba7c821df6c56074ca2a4a3e4056b11f25320d774400fa53638d967486f0e332be53fd851120e34d543d0f3eb7e5e8f730f40648e3afcdc5e9b001a2d459b16e29181b8fb9dd064dd40c9381ecfc9cb11bbef0bbe65585d95ac28ef4a5b7add0042a70fd1c5f6da253356f6f67c47d47af9749a49dcf5f29601f9073e8bbfb3f1b6b9fe05ae593bd67fc44cecb66c8e5a179228971bc7bbaf0c63d29cca6d4887603f39f0074a799ca66439ff62bba3dd1df2337a3d727a9520fc0e60bed7748f83edbde3f8bdd4b63dc17ba2e1b36a7173ecea3c043e4d44db0fd34db1680fb19e8e9f9094e0c8a59f7ff210b32e243f9da34764f3a8803a5b68bc7aa7de426d4a24e95811ebd842361a9e3f58783c467db9211c40d7d9e29db13407477a28209d6136dcea46020418e4310fca9b1094b7a1b812dc5d35cb9327e83bd07042f4301403c820d3fc0a8ac14a849d87f14ac4079b8096722bf2d88e87a9b441a8dc009130f8a5de8094a746ffa92a5e8d585bec80da835f631d263adc0dc0085752eb090abfd4435a35eabe8d2ba422612616168a1d328cee15794445e35aa2827162c65ebf75dc07305a9241af3c37bf421219bdab763c6465bddade390eb734a432d68017d88133bc3486cd11ce7f08b679f7731a91b6579b4c0a2c800063734ed18a596892c1b0a0af29b7e95a684d07d9007addc0e349aadc6fffabd49f43207216de01fa587afe38f06850651db99bdd305fc992439319c7a4d8be7c834019421527347fde41d90aa33290a731c77896174cdd4b8dc348d35a90679a5a065d238e524dd2a25c82ec208c084ed3de2d5631daf850ccf43bddd670f7c0c824e748d7159a711b21af5950d83dd349fb8c0147a894712ab77e37fa6274a87cc48754210fb914da6a3f82109bbabb5596cf29999c86d9381fe442d096e91d3fc8d416141a690a5b7b08103248556fa2b8c02039ebb011166ba38cd09eadf98319156f743b72dd198e68031f4064444f7ace13cb7522951b7a765bf34fcbd092abecab831678fbb98dda55dc9aa05fea6a6608f7200b9d10cb24e0ce28b46cbf04c72870f9bc352642dae91a69b2b244db70fe9b8dc44c5c094337029602526ab2cdc34c97f2a0b5a3493c0e4ff6d3ea498bc2f9240846f0bbd2b0cfca6c294df899ca03b514f66c2df98c7bcc8ac0010569139f4f70bed972c8e9e89fae3aa66becede2d24b3a927164686511b0017443ca03c0512b8a8f280dc0c1e43bee78e8aeced82f4d301522ff782a3b56a2f385a23f68a4bd665af38545d768a43d47557e24aa893793d17fcf2f36ec9e9274f9c35267431cfebdd035f5ae24e24f59f96022d1da2b047b751b1d21627a870733bde82a884fe3132ea20dbf372605d692814c79812a0be0f730b2d52585ea6a93ea01f7942d4d12cadf0b0b1ff2aec8877830e101842196998e6468c6949caaabe48abbcaa5376a1a2abba84a50f745da41e6df79a1439c87f172df5be059d4997af159b8fb5cf238be23451608aa91139dc4e5563d9f4525abc39203627fae312bf7028eb5a36a55c30f4a7aeb2b6d3c83c81cdbf3ca4479b6f64315b1966cafd0c99561da39269d4cc9fa0f481a2eb75ab8d4a77425c5fead7173abd98099e36b58ea75291d3d37fb3284653035a68d89b1d7a64df6f2e4943ad83b9c34a70273b49187fe8cce104013ee76cf6e1a1f19ebc7537f4183836a41c4f5c5cc47ac61216a6e047a0fdc43cc2fd634ff5b46fdc239bcbafe012c276237e5165f3037532c2be332d7588293f922e690ef8ff9facb9b945876457c68b01d6884b183a4d2a3dde55d1c44d199563da205dd393aedaa69298c90f9c7997b68bd8655c964d942ee1f916dc3de382e71ccaae71b2da81bb614bb95a049b1263f9b742fe862db6773787144553a806300f2d76d65b9b39cdb09ecd804f3bc029ca2769557e5f76770593436418e8619eab60e2feff131ddecf092139ddaa783dae57463dd1496038e0f55d0bc0dc562465efba5ff7a98969eb4c29dcfdc31e439aa40e7a95ebe4602d6354f3758cd5e39e4c135290e6c8ea2808225f98b393b5f71f49576b8608b8a5e8b4b0e9cbbc8a889796e4542af4ba70232398fb925e18336cbe0c61efe511478cfd933ab2c23f63431a1c2814a8fd8c3d58ee141e69bef1e4e05cb3dd5728ed456458767ef11f4780a341287eccc8c1aba473459f06deb55a3c493f516bbabc6dc286ae1ee0bf9763f4bedf4e80b2b7b682c40d6bacc7a04783b916ba83274e95a759ad581b144199c0316bc2a1a7e0760a4e3b8f90abc07990385744d24a6410872f5aef7571999a5296631988166eda865eb37a1bad5f14cda648dee3222c5d555b579914537a938f5929218a1931cd555f497a72a4d1bdb279768292f15d9c02e525f6a2a92ba8eefd171c14263615e9527bed54af751b20a356a6db5bde96a0bf2e4294e33a13cd5b3f22fac5a215cccf1746b20f4f94e69eb86c0d716e6eafea55c8365ab0b223df2046f8f48d1de6e33bb7c76a65b95fa02269e6cf8780ddc5f05a41ebf078d13d7dd8eaf58f56ee370fc30a3b3eae059aacea5a6757bed88668401d60abcd4e4d6796d9081860730c29f25cabafb7abe176391cbc899661dd49ea9b23a40aa2422fc1d54d78f8fdbb6c2b2158189c87a2162b14058e6c36877aa6c26bac76577300bc29681e845fd54e2635730e31f20cfaa31fa755482308d9cf0853212726070b1326fb01c916852b0a4bb18dab2027b1f4817dd4458bf7ffb09847e0fa48e4d2ecb7a4ce745abaa79f96898e364ab26cd614dfea1fce5c882851117266e4cbf4838fb74e45921911c4ff71781cbf5ff6f6f836cdabb9e5adc8840e540f1069f35eac69599eadeecceb451c7a11c83d80577ea9face3ae6a6ceab350012817a8bc6be7f6d348ac6419b7b24d80b80f05ec1c88c62a7b6058dbdd8719c4c60f350dde97464754d6fc87eb011a1ccd811b35d1b0d57c0be743a52f0f8901b53d907eeac1c2e5be4d8ac1dbc19ad097f4ba9c1d0e3020681d39a7a6d1c6dea7028aa49dcdaf56b212294804e2b66cec2b8aecdb0ad9d03934a6beee3b06350ea5375a210ff65d8e342654196f7574e11f22d4072821d8fefc4a2c8bc08d917a05df36eb30ce045706923df9705eba84c9d2645a6fde58b73f6b5e0f1ab05556f828ae80a5b09bd0ba89e86e5600d60b8d51b1000a47020d4dac55677f0616d978463bf00940a0a8cfeb17f29a2668f45ecb599b9c8ef420810163250366583da6810bbda49b4a61ee4cd145e3af5271984db5b592c6380e4513d3fa3fa01028f8b2a638112e8b3431c9c1db6059e812a79a8302f567239b4b4ee6629b652bd6f2332c32bdd326c5a9bee41f098b03a313edbc44809012dc2017ddbcf1ebae50dfd60fb5c24b0b0b785689209849427928dc3b48a6d5c3e617b0183ad6c7951576aadef88accaba60230c7273699c3623cb0d3216313dd71214d8c86ce3aa446f71712df8bf117c1cc34255eee5dbf05efd77f597203ab54bf62a07da5ca51c29a79a2564132134b137887f9a5e1f9e45c32ad87183d43a363bd4acef460614e51b79a4e6c483680bd00d99d5508578aa344550c82399de944e12a3c418a05a797c1cddb3e851e992fd8a38493a7c223ad9f2d92e8da8f2a55cd5f5bc2a92b951eff927ca66c76d9f322e5996cbeeac5fffa6ffad3efed466db60b7c059a9a0c8e71c67610217ae924ab5321676911df751a659bbc1b9c9163c47fc03ba4018c7582099f37582d6c7c4a28f87adba4d1ef23a1e0ba3e2c0ba981df0de69af2d1c101ac38904fd4a3e0d2cb7e1de5b4bf6dd87176c4e0ccb52df31012416dc193ad9d2f06191705363a1fa86532dcdef89b1ed70ebe12a1b1d96500199d58f3defed4302c9b24c36bc6b57288ee2fbf287e45b9b67de9918dd09cb10338cebf35d3f33b046e10c4998ea6067e194487e15b927bf1f7090b3e98ee94d2797d02c00edf29f42499a63308a24fc610835d7e37cb1733b3a6517fecaefdcb027d82f98d178b1935ef6c7b02ee59a5df03cbb69f58d829295b1f7b2448ffb7f1d9efff0f6e3b7ebb593ff7af7f93f4aeba1694a1e020c99f8d272974099e74b61f10580de1f3641881849e07beeff37d4a684fbc3b2ef6299f5a4370771aff28f001c587eee914247d886b77bd67ddc877be4115bcd1b294c38c83c2fbbe606790ad7a1130675f0eebf620b3847463771feabe07c020926adb7e8b3cfddd65399601f9c3c84c8342aaf277324fb3dc57aeeaeb146ffc4c8591641d465a2319b0b49973163d1cd3094e86290e29d9fa4b22e7838c930a4c4ca7872e894282949a41bfec77bb3b4e4819fb7d6e60c0c31b3c1ef0dd692459ca3f1f8a3c6ca3191b40392f164de5182d7ca163e9ba0de4cdfa9cfb230de610e757e54f5a98a8b17c2092e831e788305df287b5a70137d81749fd34f7802163c6882d4a07910e4c39204ab9232e6c3ff2215d788de2976dd99225a3ac9eb5c89689595af7bebdc56f55b421e55a256f4c6b6a75535b6b23b4700c23fd10e54728c69f407c463c60fd2721709dbd078c508c41bf3e8e35a2c6521a31722a4095e3e600c3b2e9b6f1f5d5a4a67f13d7fe37455112e3a383c3a90a603e9cb86f4811e7c39a8e330d1f0d73936336ca07d8919cccd00cd454108617ff5fde086cf5be005b5ad8e748ac07555e71065424b77dfb6b9046ea7f47fe07dd74071a833a6ed0550c0e02135641a206af835b05c751a4ca67c5b658cde12d69a589dce14557d7de5564b7eabb8aeb320f43b406dde5131d1c3f6397448a0c127dfec4646d955dd8ebb5143a7e3923a8b496715fbacfaf12b6d455838f6cd2a61e9c2ea29617e5d929e80785cb0986282cdb266796a997b2b52c8a67519d686e2c09f1f85ae41b8cc0e40198aac2e8be0eae414fbc1b78c640f896ea41643c0eab68a758519708153d7c313a00305d7076a5bd3447715b62e01f916293fc11ac5373fb2a28ba4dd880203ee1149beb476761d76ca52dc95adc42c791e053139530b13f5cab962cbef26bbd2108a491b8336e6453ac44c048b46d57acd6bc6ee1e394a011dfed8a8c3ba2c1ad694fe38e91396981da7db60bb4ac1b5bae0337fca32f7166f4866fd29adc90dc55a2fa181eb31e3d4e707cc568b7887bd2458d2959f2fa86ff4f73e5195ae1596334c0ddd00f65ac1f674f1e1a9ee27e3b6c1d13eefa2d440b57e67b802c0c2fe51bedd0636fc47533df7de81f5b75fb1aab0ba547241cc18d724c2f8df173de32f79b1e69fe587ff6da166aef253729cd45569516138a7c68c1697ec75ccaa103ecabfa746ccd59d9279124d7cd311334931b6d5eb211f83aa6faf0c66941d3d7fd17a375c1cb8fc179567be6ec660877e3c004ee57082068466f8c29d8e5f908752892c8d07e2a8d776d8ad41503a0f43484a2544412f310cd737c33de5089301e1c88c6b33e41a5b9103e520ec2a369010541ae41838fe36f418f48e8b0b5e98e70c09d03f3d022862cfb252ab2ee52e5345a3dc6b843cb7893f932592a9c2dfcac45fb05a450dc0ea627c9ac8cd8233971165c56cc032a82373575e62ca191580a98526dc3d51998d02366147dbdb843c6b988f865018abfd9542c07120140932963881884249844888bd054670a6c50f86f408894827450795835f7917eb4708de98e50809e4b7684380252888159c3de6ee7caab8d0bd0e66c80fd7feecbb7068dd4d9966fd48eb23003c862425d47a477c781ddf4d8f45c02dfa1a6d46c611f7452ff857ed206a6464ddb26280abc3d473abeb82f48d2830e032e2644b6367c544961a38c7d55178ab56f407e325b2af52e65265b1d57aa667cd01e2ba45194a1d286f5d5abe90ce3d66318c6f788b27db14541ffae72f35f4f96e4a4866a4d16015721c6590d717d8d87f7e33b92f4163e0c30f9304b314d791e81a2ace5bdf77e2dea9a706be81c354550446f3143daf5acd208c7d6da78e45375b66eb1fdb481e2ac4b8d54b13c21974914671165bf43e6cabc84abcdd0be56d937853d84014e2dc39f8cba6b97d9ebb5a964124b4da455a2a713a71389bbbc036446919e0655781ccd7271cb4fe2ab2648b3eaf583518d97be7758bf73a3221096fcffd83609ab22f6cb6482a13823aae33a80d62bde1ebabffac36dbb48aa849df933989915abe1c057a038cc6c088d18c2ae07487ea7005d1d7fedc4a6fe2e425ae4659de378f73606d32fa882e72492b903c75bb94486e7d13e6c8e164441b6a3a33d49db8b5de4fdaecf1534245c61c2d90c148f55202a9a37a722cf327acffd4f9306859f86aae97f5d3e01ff14e4846e580e4a55d033f36c97f33cdcf192e2e5aeb76ac5688acf389742b6215681826020ae6eff03ea609d04898785cb92f8a33cc416bdd9c685a3967bfecb9d8b48c110a92c968be785346ab768fd3d88d057f23cf55c39258629de974d578529ca70769e5a16b6e14786e6efcfc2ee398b4bf57da95176f79ecc2940c8cbdffe040f890df26f82e318c58801c009bdd33a1997091d9f22424bab12484791bc9a58ee7d61e6901b7e69f920f3b6dd86f0f7bfe75085cccd25c42c0f957909e9b1b5bed5eb0df59302277f313c7df8f68e191f67086cbc995d61942485b9090a337603e7fd21a965afffd5e2aae43de91735b63d68ffc5a0a2d2fd3d9a6d0ece5fbca44d006a3b5ae414273dde0b6377a50360c6e7dab432bf2b20e5ec64a19036adf900ae1cc7f8c4840084e40c20249f6b22739e23039c7ccf14f04f6c0b5927d7b8ee5b6c972a6b26df4683343e5cf42f01adba382a7eb16138bf0aeb91b3839a90d44d49a0d254d690dceabe1f59aad34493db8ea9dc0740634862db6fcabd0e0e3d6e70611d11fb455d05a8e909643c0faa15e097ba0da3869e6a3c15ec7fa6f047987d921cc22ca31d75b5eaae999703fdae23b9447215bc02c3733812c51aacfdc34447035114c5c8846d183a81738ec424331d9b352ef6b042609afaddbe115587fbe93c6f12614bf504f6b325ae1938b622cb9f134dc6313751e6ce833d88421038704ca704cfe82a679fe437946185944689d29af0e6b2fec292de6538bfba0fb1a2fdb7b3c3101bf0053930da46ec26b2e4c5d4cdb8d1b17d52bf81017b7fb1452498052da4d4871667d570a776e16e9f410091025977745559651c65c5f5defebcca3d21d29c9ad549facc36cbd9038d4e1a0dd83a2d456bbb07c917cce68c8d4c667e6fef29c07cef4cadfb5d0ecb96c1fb237dc41517303bea93c59c55f18a649770a6b2ca8949ba9cb383bfe74d60369f1322c1f2107a21ce51d0486f658cdf24e7efeb424db616fb5580599960e8c95993a705ef3713465bded3324a493058a7b8898557a9010d4ef4d833032c7e57182e96c7af5b22f7c38ef24bcef26b9bdf86d1e45967936fb30c2003affb514c726721eee379138ff6be264e591d129e7f609e733d867d20fbc2118155e87f3d482d75b38f2453dc8797e64900cb0748394050f1440452a114da40082fc5a18ba0a6193ce1cae6ccf0baae27129ea6dd79288ef9bda9df332df7eed26361a8c992f810e6ac2447dd1464a45f041675c827285fcb40127ecb4d3ca1a70818c33d36f66fc1947eebb64dc71d4eb3206146266af88d6a267567a520fff31215c1ebc3e7f9b6951ad17663bb2df9d7716874b81af6f10f2e2fbc514f7d8babef573908953f3f51269cdc8975aa374081332d1c6c61c0dc7c5d5d49025708aea266696a38b7577a7cf23aaadae6382f3d837dc39f246c4ce95b68ef69f6262382e4ea996f7d2eac681324ba63d7fb9b3b11edbb3997d97462b6d77c90e9817483021dbbaf778a3e572596ee4bbb20f7339191deca0e75d268d7f9d586fb145f0abceea45e97b428c46c49d9160562aad18aaaac47c97991750a82aaea2a77e231a012977c0e2a23c05e5b03b383dc1226c5f4a2beb86736ae6795228f6b9787e7227888a73538a1bd8d9f7b5482df5360882ce548ba50d0638ab300b37d067ad4150b1fd221e44642e4883912b88a3d687790193d6548c4af48bf878fde6dd88fc630c85509a60c8cf80f8cd67c6a8d7689ed209d7134d5c656e7e8a9d5d582fba5322d2c9f17933546369a9d7f5cf5a8df095ce168f921d23d96a70a57a49dfc1425d166d86455917eb1fca63235513ed78222e0d8960d201daf0a4b4febb04fcfc9814d56203e972e7cabb3364edc28ea01dde680229a978c99fca3f979c408ae6ce6cbd8dc09ead1b0a64be41cdf6c2c66a313d10089376fa04a58ad81323d06b700b4a144da354f0cacc140eabfb863650e6c1dc8de7ae5ba0408b209a7881a749658f27c19ef82c1d5aad451a15f46483c834c257d76e70228b72cb7ded6340aa58e09444438930eec62525f46642793027305c3367707d1aba085200d7ab9af984b2883aa67f987a414400469f198c11c488b12ed32a0a47e501d0c3a848a602638e2a2a2148ba3eed498960b35473987aac207b3f972d9115e5e51cb773bb109609ff941f15063f1f4af7d4a11502e01bec960e567b246780b203548861ec109133cb8cef9262bad88b1207b2a1a1916bdd03dc8b9d6b9ddc17e356fd653ec3bcf655d46b7ae09d7288e63600f288ba458da86202f02b53ed0c402403c74308291368f60e197c303271685b4e92e4127b5d0785f5b7bef89bbd0f6af224e856db85f50125dd7d1720c8d3e7bb3736f6dfd005f515602c63f69e8008680e66a28876e7a8036223200846a8772084ee8e46455af891878350caac7a8bf4e77a0da31c8d1e6abdc760a4a6e7cf82a998ab329d138091ca57232143d0230133d091fe24ab327d3592ee3947edd6952d9634c6d00e5197f869e0128dee19c962de9d01aba17f86e161e47f803a22c0ffd82da6b538afc531ec8446a1558512206bff3e4e8a70123efbdb33a72b6d94090ff6ee21abc0aee75f111d7f95d61a3296082ba24a6dc9418529ca094705389475133c2e3429cf2bd02cbd15ee889a798b202de4308302a3fd434af75781efd9cbbec0fb852a3586654f7bf95520584fe1f7e2ce2e123eca643407cc452ec491cbd5026d09742b4c82f59ca01f3d2986174817acb87b95eadd4a9127920c465f913691bd9d91b7da84d2fc2c1f84ad1dc91a237ea8cab37ae77696de535c5d2fccee2b75dc4870e5a9c4bac52695c630be3f238f01ba9438e4a37028345435cbac7424c29d3911f3f0d491ba21bf07720a29e6cd4a267953f286e253578ed07cc60845f35d3d37f29446dc83a87d82a5100ca46ec90d02baafe30f299312ec1d006f94f7e4744b84faed20da75fb60de429ac1b463c89f293a7856e983b3719053134f81ad085c33f07a51d4fcdeab2a0a743c708da99005a5367cf3f6276e90eaddec323091dc5a6e979769e8be2cf8effe2e13cfc2220281fbb343b3f429d395511cf3b77b6c0ef7d7add6b9736898adcdc4eba18c92a02e97d5b59be7cfda54f952fa556b8e249dc238cd1bb1e4bac24b928fbb28c097ab77b9c6edfb2f5b9b6dd31664bca888ac8107b64fcb34a22eb1d4c993070673a3d169bbb57897cc242bead083f8ed4b727767113af599e88ec6656d8b73a0abdcaf5ee6ab3a9c09c9ae3a33bec0b2d2ca4c5b0bb2b4158a4544f949c14750b717cd9ea4b019929ea2e3ae4f2741ad99d555c2d8d7501877bbcc339350acb0101f0900317bcd7c8485111ab0a40c4bd3b2f85b094631867322afd0876e236236f7258aa325df587aaa89d00f86bab3144dc2791e8569646dce1889063b923304588bd9a728d4c5698b49588e662f74cf2eb3d0681c8d4b87176217b9c675172ea8920f9b002eb5ae4a630f2b4a04dd0049d7458c45c133686bc397c851dfda07f51504486eda62a1d6bde32f99b426735c335d66be382841926a3c50f3af4d37b6e1b67a6ba4b3e18bca4b65617580eb6c05526ab6dba152564c421da381b3fd4dc0ed78aa0bec32cb940a31c1fd3670015088c64859f79807a23420874834888b7a7e99c4d703c3e438a193c99bd4e7060f1c08828785b492d77649fae7adf8802f534a237cb7db1552c919285443723966bf22d0d5ea3a78972d4971554213f9b670291fb39e9dc013da2c1f87b664fb4b5a0cbba8d0256316aba86ee081220a9f6127d24c127e254636657ce2b4ea0da0e602c6adc866cd4285ab3e7479ca7519a9b8a7da04528d98fdec90814c9a326e768d70c0a1ac6a517577debced12223c45b4e19238aa0432d76465079dec6f97e089c37de1f5386b521ab3531d06ddbae087a1db558f4f3731702893ca1f404dbd0c69b580204d822db2eb3bc50e55ce12d1686a1296317b3293823f02740905ce37059051b3726c373299abd916c22986e27d38cc18dc3137bbb8f45ba5556d0019e22c0b13da9d9bdcd3b4b3168d817d3042593b0b1ad012d031867e3c9e2425c3290fcb5418e46190c4e151d6bd5f50cdad4e013b12f4d9444e2cd61848af061416c869b3169983514f67897787d7b6c780a40188ac78098867edc7457d54eca998f8946932d30380a964006a7a078e328e3011b5a41c25f60384b254f3b21855087fe0cee486bc3a740203463701e09591feec3bf87dc20a48d9e4d54512959e72fb1465e97bf00e5e9f611c6c160bda6029feabb62bdd871435a2e8b677a6450bd5671c6639b22f40f9633dc1ee969cb557c31fa86cdfee49a6af3f5e1a21fdb1e997864f2b0e9a4dc574863bb91496f1222e4a4bc940e97424690a5820a631e2652be3b047629c0eb074877f4e3cbe93c2758d0fd5f655af467063b82b17cf9acc19a0ee46f1378ba9e9d66c4d7489c5da23abd714175a92b0715f2179f6ef707b283b8fca62ea5ee065b7d5b2f7cdade7520599fe44bec72f954a09444543f25095999a95bbdb79173f0582992e978fe8253414fef7131cadc52f140a351255664646e0505fae1f9b2f04be63960caa378cd7a688dcf71d3e7d7d80a734298fd2c9b1e90139652a9890d3c2e9e467c5f4197babf7327aad91b7df338c5e53299968e36265e4be21a249e12232063d0f94705fa9cebd3e9c372c855925dc2abac51eab8c59f3879ff095111a9cb2f5d176301f2d5296fd545614ab0ef38429de6ae07389d4b1c5caf26bdb162897ca247f576103ea0db009225ccc0b9fed50d118356ed5bfeb201e8c0ee4eab1b9133d9f8149d3accdf0bae4b5d0b7cbdaea0ddc85e07e586fb7fa245b447106a8115f80ac4c80db12ea5cbbacb017218d5ae1c4aa11bfe465f62a09ead24f12d5a97b739aa3a27e6a907a54d0bd5bcd9a13947e32fda153242fa7965e14aa4f0c3cf2505fe97f7c087a5668f2ea73794f781f390c664f88648da84e934dee99fea5652c64e0a8324663df259bf7beb2d1c56e1f91371065acbe60f9d4d183f1405d3d1ab911fe685de5f173d6fce6b3e3dfbaf5dd2e2ac7b34216fe912e5802a7c1d3d46960b891029e3564d69d2f6bf0066d119b79ac2c90e95edd570ec28073e5e383c3c13090e0f19ece1846c754dde0282fcbc5544a6079866b753c04cc3478618a93d6714c476253bf6d915dddc596269418eb446c4eb6f627e612012e360a7346dad5b5eb9b3077c6ef62a7b8a349ec2107d9acac9e6c6e9205633fd93668fd9eed95dfa6b21e83078cfe0f298139a2857f6c7df4c87dbd23b71d24ed517864458f68a5818b56a65aae13fcf8db1ffab9fad0af0154feebd68c7f0b59e33bacf3c8eb6cfdd186ed609a7bb7de892d1c292d80efa36bee8d2ea6a3aaf0f57d25cfa5c7a19271794a83f07c754133af09624a03fea4c4a713c9d9d00630c9989fe24c218fc79428bc4972df0ef00ca83b8c54d69e485058c465706b1155c331c6de12d0f017499c10f72f836e9ecb3c0827f954bf0db0fc72c3144fa5bb91a5d608dd973c225cedc4c50d6538c052602288f0bf93d2010a26310fcbd6bc7146010bc155c01a02896396911b7f0b58b7175652466244aed4b599582c43536257808d52b0c4d7a49989caf4565f32c32d784fe69cd5cdebb2d579a4533c729411c0277fa345c8d52e4323caa2427ddb549da82601affa81e4b673bbacf6aea478a43c66fc8b97b0c21e2e1d077565fccb01895d0b0b2930800b777ec7e04c88c9899d215a07178db455587d2e29b574dd162ddff6814ab9af36a257283b29a1d43cf4dc96d48729e0f00ee16815ede7bde0f0455ac4760300a209fb516addc148518e57a8c53ea7e01712b42c94c9df211c3dcabc9c90d9fff48f681a15627f760897456e6ad7cf1e4f1ea7508bdae2d6516736bf06ea49cf4c9c04b794309d07ce4b9f3dc2bcf8269d3d6b00b4541bb7bd64068307fb7944b8caf14c3815bce272e4812c18b1b95421d8a808c7a975552e1ba4b6c67752d8f139e106c0229cad5e9162bbdd85720da5a8081776d11b9f16e11640a210e88546fe0febaadaa632034833b0b4f7632297e6a93793d23a5504c00a160fb1878998314a8ff476702774cdbf528ee41c116ee1b239d1e727616b7f4683df45b85bf2e46424de02a93933c1d4dd8f5e481e23dcd924a7680f668406dfcdf0e07d769921753f659b9eb846ca1171e0cbe9c60897834482dd22289ebdaa3e1d83ca1140e3d3c5b359562fc711083df5d99df62c0a7714180d5614603a7e14412e76a36399cc55e3e0d3a26b627b8f22806d1847084afbd9df21ac2ae62fc5351eacc0f8bc245516528b14c8fece8a20f81bc5c70857bd6bf22c49d252c8b8c0c4af5fe205f7665825936e0e6ab6c24b31d32607917a132bfec57e4322660bf5f2673b96e103940337496ffca4ff9931c219539ebe27b0865b99f24b0ffd4afb92931cabd0cc1bd1adbb16512750cdbeb240497c8925cbe5637647f7deafc96809bc63ac886e90161a06e60d592bcadae9e649658e68489a2ce87a7ca281841036a0fed65502869be60c6ff38bb40ca98c085fb95456a483bb9ee34c9c423544082f32a1c3bb56bb0f9d58b4439f388fdbdd3c6acc42a9a51e1bded6e1f27a89d8df78ec63b5759d83f10cc3dbb2d6d516e1b74b69e75cf7013d892227c5aa5248ea648f9b6a0e2738bfd33990f803cf9bff4eefc65187d0abb6d160d1884e7ebb953251e7c1f5d2e26404e83ab029ae2cfcfa7213e737f9996b4de0071056a09d50a87b7c17376c6482b61741fa106844d5c88eedfa799ba5a947898fd3fc22c20cc782c5886d650f888705434b587a54a80fe38f7cb52111c050ffd484397340b394150faf0c5c5e0afb13df87d350a0c1db8a9138e5ce9a0efba6d25f8a5ac47eeaa725f4937f32b1b2e7388917691c56c7522659986864de596b353c0b4c0d84ef079bd6b46ac1d011f1f155cb3c12b20881b771a96a4e13436a27fea4226413f6a59f1f3970d1958653ba9837c101de56f64e202ed8cd471d84cb1b0814129287eefb26af04ab083b443486c18c9fd58265178eda76e80fcf5bb8c88bb8e2be2328d1d782429754bb08a14d2a171652df41aa9ae2c77728908f3fdc3bf4529a87977a18193b49c4072f74e2f50ee7f1189c699b7b6f9abc627cc1dd412422ba0ba7306b5ef9af798b24f0e9eee0876a999afbe0780a7ba571ef8edaf44cef2cade5156932b3b2d5b0b1726a8ae3f9d3fe995ee12b1cbc816c1a845408bfc02199d21f8709dc28b61154cbeb6c03321b323cd7c7805ceb7398fb14508eea32c3d482838203370acb804866819bfbdc0746b686ca037520199f3fdc7a568630e71999d138539847133d4083e2c1e8574986bd78811b9f333efd19c80f1c69571d5267adf2571c42d9b6751139a13db8bdc835cc7df950cc31f3056ee27944fc8315d539a9a27f19062f1ea34f200a7208ce16b8152db71d5973c9bc1513f99e142482cb44cc479748d46df376454e11d2abafc02d1e209e64d170b4b04b6dfe9c1abee3cfdd03c523d71ec1370a7a98f3135053becfeaeae7ad573d622c5f4701e15f02b7821a0f4be0c676c408d820b93d81db42822bb8e101b93e6056e6ff0515a040fd48b4242a90d9f0652120a9abc5f81f21171724b78e7290603fe2c910b734bd34857bd290fa0ab871031ba4fc82edecfa48ec90aa3183fac8e9ab907cf766c25c8815f3a199d4a910be25c993685e1fa97a6eaf44279ad8431f33fa08e3577d5b2d78af38446d75df36473bd4eb86e27738fc03ab79be8df7d8331de08e78366aefa8da1bbf89e64427817c9554c8c51345157b5889ceb38e30160766dfcb77be4d3de4aa239544e5498e0ba1ccb791c47c1bea0ecfe365d9b91e59be4d3478d6f1d17c1b8aadc9c1cec1c5de74f3e146c2f9f42c1788dfda6d249e907f344692089009dfa63c8fa0502b36ca4fb6b19f2cce7201edffd5897f63866f4be3c729286a690a39457397a62338e9591f4003b7e08a06eb415701dcc2cf4491e9b6a7fcbdc83f4362ea79147c5bbd154ceedd6629b7d69b0c7c254a986cea6f227f0a71bbf40338b8ae4a4dfc83a0e92ea657a591deae25e482c833fe4befc10a517de83260f3226ac632e46b082585c6ac16afde12552603366cec0c8f5e0ee19d60507e0cd83a1c284a293c596f568e859a0440a129abd438a1481504f492c980ad1bc1aaeebeed695f20f7344bf738c4b275cda64bdc0227b18b70f3eedcc998d98ee95b82c881905bc10c9c116d4b7b095bcb41872554677b6c12011cbd4ecabd88c771e6b8671510e7a8d44c199b0c59703350f038262946a65fddee5eb98376fb11bb3b379882910c2c08e27f665525cd27b1b97a03b7322844877661c99ad4731cbe8022fbbb438c6cfda70560693d9c68cfc7d9397697700ce02a5ad902b4c8b21a22365e89a81620480d17655b9f899919a8ab055c8471a2adb49805ead19ae7f45d6d3d4a128ba8c762e2a2e456edbc4baad5cbeb3eb8d171a309a3491cd9544268c016f5905f5e981b4a0bd145ecf7e735873c134927b78526afae6f754e5e848ddb1326dd1c7a77eea279e2632b7c6b24c25101fe4e9530f3bf96305b31ec060ea6759e756e9166cc6287460c1238c7bbf14f1ecfaa30d1517b11c6c74930c6dc156795ff88c37e073071ca6f33dd06fd14192ae93132bd588beeb0457d68ac6bcae98222095543401c0ff8892de1ce651b15e116e25d30a81bd0899abbd1c2e2ba69ab74104b366bf29adcf6c855f28fea16cc6c60c403a4ecc99e9cb45bf76d6ace05ec824eedb54c68255a13671bbd9e7c93be331c7da18f4b5c95ea2e1f869641f7621599f0ee5086e9f8359085c209628e93955f59f0328a3243dc6cdb0faf6d4080f0f099b615b9b327129a4feedf2f48f4336be21bb942e5229e4a2023e1004f69396f452bcd559fdb5a12b083e7d1293a0707ef9c4329feac452b170674b9fc9d8af80162d2e9be2c8e02c42e65ae8fafb8e45af6cc66c7dff81d0abec3d320bb0b032f8da85339a2c7f5b2026c098fb31f7454479dca25f25f39067d120ad5ae6b2cdb1fded0d94d945f1aa404c0c878e39bf8e4d83feb88f0f23f2ca95a22673de1bda6492c0e513527798e3b90782ec5442e397ce9ce6fc4a334b9572ea09e901443c5a071f74336deedb332fef4013e37f3c99392b83e41d2f053c637c41dc6d8e49412fdb30657c4438cb61efcfb95bd3452f97bdf49b0dd6415cea6405048a9e9a6842a212b468c497d86c23fd76427070781c463e3f4d4e82a8e52388355f3b4832f3918af1a2098a662bad7f1474bb319508d53cff3d2fff3be13fd3fa65151a435bc0358cc355348a5c9f7df60848640ae87ae03401ad936d7f1e0511cecdd479d9285bcce5fc8956bdb672f3cfe961ac956738b9db24d332ecc69ecf2aaffb0ebaa561c69f00d5ba893af08081b369d469fd3aa06b601026bab7c3aa7049344693b8ceca92e6b085a77acb0694bb4ac76595438be3e1485a1604ec699904e2e0d3e9b9321866463e85c216e513159a405f8a6c21b102a862ed84ee0998711638cce617048d502a4c3257c2fb9cd8327cb72bb31c1acf426812392713620fbc91755f0e6f4bc5da761f5ed707439359786385315af35b2ae5afc82ea78242e51223080750b5f463a9cd1569b3a34299e3c9d18c1531eff0cd563f9f7ea59273f457c18cff0895af944f2d24a37474787b9a0f40377d8b442b2b9f7c9e53997b85e7f8dad7e8b4506e37c0dd62abaa31150d1bac02b95111166af9e3384b31d594c78c8243f600204ab4a7ca0392f50e0950e296b555f830a381b7eb2a48d9034f6410cea803281026cbec0b814075acba36955a08c9f6782f018ea30aa9e686777a0887e91dcf87a897716f92913ae9e72d9ce450df8a4cb7e55115f5e47b7f167ce02ba22d6108893a773e08eed119e880e700f3683bd8af6551b1d42c85b2ca44422eedc7a775ad0a137921f12dde0290a14c45ab4ded1559dc0c141e606904e78fd051608e767b7304464de5d8e119c91b408313a994b3cfde8c9aef12a89ab9c28474fc6c94925f02df5ee65f128f8d9ff222ceca4a0e1927b73284226ded6291716990dd3e5afbf5744742bde6165f22245b9f3fec8fb502a39e84c4c15b2cbc9073e78f221279dc4849039535f67103f88ee5f650b0da12c352ae597c45335a8074550e6ba2cb4ed4886ee835f15148fc77d8a810684c30df74b3bcfe3843f1e8d47e2af59ae0ccd84af72d3fa60c633ee08e3a872e2c0ae62f0a33db40e4eb79686e88ec14ea1ae1f38dadc9e307c11ba6f2109d1a85a9558f867022b925100daf3ba4ee482fd87fabccfd0cacac97627a7035f391dabd97416c9485caff551cc1488960622b60742fe8661c75b6e4ae9172e6811050a3760075f7684494f903f64197f628576d3f2e5dec5684f904092919d66ac28643ea37d327c5422fdf97df42487bc49cb420d00f531e2e0eac6cdac8db3261381adcaa9fcffd1aac29e40e7e21d1516f71d6658ab1cec59f1f23115c5c96a516bb242d171e29a3965563a36cdc880a835190da4f20bf2d0259f8cda75e63a69b4db00c26bf8f6ab02f5d5648e476113be3559b478f807f9479dea3e8e3ffb316dbdb6b9265b383c0a21db99673494b68a903fcdc8fd6f3355f75be416e34dc64d3a161ac54d316d4db2940512c1d11c4813c95e20cda019bd678f36e08d08244f4107afc1217a05ff978c19a0c51610a7600897480febac02402dd13cddc3c94ab86fd2f7d912748fb8a5da7834f192f5b6cdd7633fc5775bb06e1953239ad74f1ca8de627a128c0b13308bddcf578b92d92da130098cc2de083c3752fd5ee5d64cf1330d5442e4afa9b654d2999aee07c7db4280cb5cf84d5dbc76a0a2a60e0956c729b4db33aae96243f5f1cf9348cd3e50897591a9e443f5c9b948022423badb7fa15f769fae9e13309a5a2c0a01af928c74b818aec11bcb0d19d3ef29cc574676841538a227f6ee85a0b2eb3fa2b49437e4fcca4a57e927624a18bc2cb926a4c66e8dced625fd9c0cd1cccc556e496d2d3cfa32bf0db064fbb1d450a8b3c384daa79c85a0123b9362e92fe858265500e3a02f2c595a971211381cd7e059f10edb07aa3e5891009068dd513f2829ad8011e87bc0fd5f0103841f69531f0e007cfd107aaaa637a6fc5a92096cdf1980670ce8dbdd42e83ac1be53b07dab9a636541caa012ac4fe463060d64b61b566c122f7c80f13e0e518f10836ac56939c84d76bc242b6a6eca809ef004921925cc8e4f8a555bb480844d9d90ba41d2ec549c07e26fc31c50c4d7b1ebe52ebdf3f5e267e54fc44150e014f50f30fa3ea1feff84501f1c81a3d82bc587ed87bc37d6a237f5d5dc009579dfcd57abeda2faac2165fe714f795a28127415a02e30ea4ea2e57783cb943628f4e643a35b86db00d4a660a88add1a654f1d6e175723fdc0a2400890058de7a340c5426b35b851b0969fd8c1ed9246e95b9e801d061efb6dccb0093b3a8cd3a00a52abf1cf428357282a79dd02288aaa45198cd1c4c09f619b11323692573fa4a18521db360d751b602f5e3b6728bc1d98797b4aca21213b5cdb04a096731dbb18f549aa542eed354515752b415145bd24f97399bb1a1d4e61de7c08fa881316eee3b98f23446d61e2ac74bdda263335c3101eb829ad8f1761d7634786d712d0d19fe49c6a5f94858ef5ee223db067c154810348763841bca801e43bb29f341193e09a1d2a4a8b662cc718a82bb83ab25f49ed32a425f37fe79e030c69413e4f88546de9463b841b433eba3e78d7f75ab7f97fca48b77b955e198d9d365d20fe5ee0803233cba8796676fc939d9a138b7c769f341b06986b59fa229f63fea6d1009af6f1e398a5d55eec1c9c840b6942784510019004f9afb962b7929696000fe4ce82600f1d18cd93ace2e9f4f56f38f4c04cdee868aad75140cac5a82155cbe22726332f49d4031db2168b32fca50ebe0029d8404498e055f9045e7792aba12118e2144431eae59c590135fd275fa0d2a1c4c97a746be34a2d79640e84d1b15b68c4b2345c926a38752288ca3649f02a3a2b24a6b34edcf8719cca736b2003f43e2dff63875acfa91948311509f2cb50c6b2bfc78846e9edad08bf8fa5ca874321999a286990dc2723745251b06283c76a2651fa8224eb157824e16c9ae3f9433d774700069a25429b6be5a0e3150df9fbbaf508f62688053e01be658487ce19e1534b9968af449dbd86f5e3dc101b4b051e4ffe73b3b58c79d26c0112fdb7f038e8de0679e07f3d19c82838f934902fb77c2e7c687de203e53f73070282780e31c829ca1c1027b1a84a05b6ff3e5867544639cb7f808bd01a9ac004f2430b123e235501529bc2a5ba5dc3a24ea82406686b293a09cc3049bfedd343685499ac6307bfbb602d1e83d77c3fca1e114103e7b0c68c45d71b1b5a54d3919eece7ebb15ac916d250263139cd294290ba851712048e03d41048f6c91646186a30bdb0a289bf3d3c894556d396bcfa148a91527afda85a0387e145b4608cc352a1101f2594d5d9f3e5c2371951505bf477ecf3ae213d5b2bb030842c1db81fb1f1006ab9cedd322c742c31973aeed0dee1f181ec02d0b1f3e271f6f76d0e93497b9d41ce1f009d1d1395f82cb7e6589c6925a3f4e132e48a45f1aeeb6bf9f9978b004217b27245d6f2273b3eb931f9eec3c0a87568b311e793274199384673d9fa6586cf7185391869b0c294b716a8ef4b154662a712f4e44dc904b97a9ff3fee3195af66dec07b03390b0c6f7a3b3bc1a250110fce6647ba0f19dbc20881a77e5edcce18e79c8d6d7c468015cfa054dc9e4f06d4397c3f8f482cc757eaa25812b7c1eed8773b0b27414ce1ef4d20d650e6a1abe3006e7ba5e353c9ab38b6ac04234526ebc8621c30dc39b61a80614a9085108fea7b3ea13c9b34ba928390943d6645998ecd46ce56b01e7f4b2979384624b5e6a5aee602c823e36d161e97c94e1dc0c87af77003ec287893db14f8ae9cf4dc7617cf458786507e96c690e580fd4bf29441f1fa01c98e77ae6292e4186db76059de685417b57902d99d03d0e300b314dea270f9acd36fd6033af2713ea431d006420016373f2d943264e4723ddb0a861dc72b0fc2846726e863f96fb30d2ae5c40823cc6d5a4b778b90c87647276c2f44d8a88f4165c2c5946f4ed8caf10d56ea2c6a8b1c19a4e44c1594a0214927e4b1bd899859414a1806272273232341ef8eb4141357ea23600408552511fe30869e8ff493dfd57680300d4985194918fc19a06fe2085c7f01f75643879c5fe5ff7fd4f52c9c80008ce2269d60ebd6614ffb4ffb108d58f1c1fa598b8b86fe958d8e953fdcf20487c1f6af2ed44dd7a84bf6b04509e6bf7e7c782e6a5108e2679bef7db5b9bc624042d6b94da0b0ab184f9982b0bb32621611eaf2b156893b2c40bdfa1c8ad2ac4a79315600d4c18e29c7fc819ab02cb6e67976036e59a3ad89899a7664b6a09ce27a74de80efe119533ee3b82dd37ed508fe52b7f57b73feec7ff761108ed34a1161a6c878db368e2357b724adc3d4d6438c18cd34cc974f83fb4bd756511ed9f71cf205419437a413827ddd855dc2811a03173211b34acf35753e4052e6070d52a649f254df432e48b4cbf8452a90b07ba9be7dc97a2df654329721ef98ec5d3a7be0ca2a8737e9403e29198544496ba928546ec7e26bda21e520571e8abba617673c03f1c6fb9d5b96eeda1fb96bfd633f3e26e5c395c2cbadb06ab74cdb51c39ca3daec804cbdf3e7139db3cf15e66614e19cc0e33dc2f01ac4f045a16c39b5720d8193707b9667e647ee4761b957c5356faaf1e6b871af3b6cfee721653e47ec4fd88f5e141ad56024c3686eae10040afb3fd273ff25134a6a0fe131658ff9effa6a5b393359d9827466fa396365ada24644b99924c01ad09d60996098efa9ee38e60476f7de0dc714e44dfd3c93e6793624752d0ed27f5743f0ac71a0a474bea91a4a03ee1d4ef5d94b932e3c499b9a3b78cb0a3eb78159cfa8144915c085bb8126e3f4b8782ffe856b8540c7099478a2ceec4e1d475fb27149c0a81cde37a524f158563b51fa9e78a7774d284c231513866ea409afa7b36c10609354398ab20044d1fdc0e7d0aab33a577a879d517ae24df4da6fee2267a68818d7fe8370743f25e216739f2ef7b817d7b6002470b3358b90e4ed1457899478a272a0f484c9c964bd9e191a2d31d8f27df93707038255980386580dbff7de8e9651e299adced03e7170a278e111367db68385ba5cb5a94a1841be29c6099478a2554dc7ebe13c7657b48fa4c1c2edc89e3286fb9949fcb3c5340b9355ce6e101950ba5797a0085a7074e80c6c0828c8bc513ab3b4e9c9c25b7b1602171fb27947a5daeb780aceabe302f7ad1b9c9ed65280b61534a9f6a56b1acfbc217ad2bd28fcc9b7d28dbbc2c9439ad2227ced4e99367a0140f25949c293b9d512b6296659c659c655d8f100f7de57b8b53a10f7db7053b7a4f158ee9c73a1efabace09ecdfd175e6685bac8519ce70434e05a764e85e704c8feeba1f61bd2bece8ad3e5127b07062c4edf72f38c54ab8fdda13ecece9935fa190ba802da0b7b2fc48dc71e24c9c9585a1cf57aefc71e254f156d602c2721c1544e8a9cc0c4d9c3ef96ff3a22109337375781183fd9927bdd5a20c3db822161db269a1ee3959530a47f1fbf0270e9bfa59c251387e2eb8a29e38467ab4fd118b48c422128958442216d173376a69e1185108a455382a3e565aece2e74a8b2dfa458b72e24c1c8f55002bda8ea6737be5285ae34fae1dedf2e54b667ad3a24f7ea923bbdfa412a7c6298b535c58ed8e14e70235286d6a0da7454d63d91c0de709c734adb9e9d3a83d61723ddde17aaa3de1149ba6dbd4b4d8b4a6a64f4bfa24bf332dd891d6dcfe1b5a436bb49dac0872fe8d94724a2943b77b3ad857ca90d6200951dc04e1270841266c07d3f964c743885382f5af9dd999dd3925d869e4fa23f562dfb56cfdae65c7ab75fbafb4ac7461e5f60f95ebb3d3228f4b297da745dad398131a3821c66a51b3624550d81163dd5c61b0bc6237dba4c21db33b7ebd859f9bbe60478c85614efa44755aeca782637ac039f45f2095c231fd6156861d3f2f0a3b7e1f56838d6f8c74677b29254bc9d2ffe98da3d8b518038c4bc30986aebfe48e87178505e98dfc66bad3b3b1151781469a73091a09dd2395723c3dfa4a496f9833e6c1821e2aa0c51d40b07caf108797e148adb0c137f009eb193f7b09b2832d25cbe9a1ec392e1c6913577ebf33c627cd9d5cf923a9378dfbaebbd09ca1d09ca14ef43fb44a446f40da84b2e81490ee501efa5339e6bfebba85e9047b8db8c5101356ae6704cbd299c3b0324b1d99ec7818e9c1339bf34a90b768c1ea6ecdc98e87ac527667a407df27dad324515d14a4372dca496dae510f2db86991af3cc3f57012c1b640e5f23944c22ff9650893c9ec45ed3fa382957f0036f563d12a027371fb6738460b3bcecc48144cf6ace2af169c09b1300cfd57b52f195cb38382c5b4ebc71b777e8e8c67f7732422738590cf97068d21e68f455c190ec165dc272d3fcae7fe1b8d46a3d128f442db62136ce826ce8751863e0296e780ccf7e7fefd86e110a39fcfd5a73d97522fd203df913ef71b0d89c85c218620c2ef13d2f3f567018f60791a252a9627812360791b02f15858c02394ac5447d02429e30a2a7c810c6620a30c15cb27a144c51226517a2f0c87982f9f1b854fbc9f21f009cb77e0939a57b1bc832390cb85164c22573b2c9b1a244a6084ea082e43d512d6bca8bc5009ef4b9ef725d14dedf24978218d4683277ad0840bae30024bd5f249b8842c9f444b487af943aab514e284df06ca8b6dfdd7f659c7630ba2200e32ecf5f2c320c021e0bc2e7b473b4a2fecc84045fab4ead30c7bf0b5f922686ae412e6f9018bc6151d9e3bca5c25583e09120be9dd248a32f359be7fc6514a943e09afe491d84d2ca1129f2a099627854a54551249d80a0e194954b9c2843344512511853e4bc823dbe2c572c4b57885735e17eda10557fa04339fbe937b855c7756ab3b7b68c1955ed92641d9f8c628e54b1d331c2273a0610638863e7d1eda679e3d6fdb10f26ef3319e7362cf9cd46184fb23f572cf7f3990ef4639f0077694f9914af0a92ec1bebe5c492464cd104a7fc6514a54f477b80f1f8470ea02e50d817c45d5fdecbca018e0d1dd211c439fc484757697b303e7e538890458448bf43530abe011b0c8469560917e286bbe9d2bfce1be2ca44ddd828bc245f9ae8aba73e76f92abd5daeffb174599f9e117043bf64ddff40ef3a7daef865321d972d37c5bbf20f993a9c0cecf6260fba6fbf9431c55fad93460d3fc4904f18aa7e2dbc19dfde47bb1a3b9851dd98752d2121608c780adaa3a88a8b7d60eb323d82a75f8d7b083640e935335ec3270ccfcd9346014e9673f19fdd83bf8b1a9b8b3afb8b389206d6a07398af4f3b9edb3fa25d03d50e74e52d8416e9a3f6b1d85d6daf0137d28146f173ace6785f5ef97393e5fdab828fe7f5fceb47937c9af3b976d24c99464cd21b1f18d9181ae9481e11fb4dffe872c43c22a9fefbf0f888f8c4c739885350472306a10c6949e293fccac83515848248c2ca4bcdfa74939e4808762ccb069fc6417848f1deb0614c6088eda13b67f870751a016fd87b40884107f1a8df2a14dfe583883fd5869f868d1b9b023056a51207fa735e0b2e82cdeddddddddabe08003002ef78946ef6853a3e6f717e1a8190ef17e663884c302071cbedfd921de1d1c16ff3332220e38cc961eda31657083c4d13630dfb8a3bbbbdba0e105625f6bc868d06a6c3438986e46e845f45997510b338a7ef5eaa6923bc9f348259696918bfd442fa1191d0c4763aba1d190d5501f03af90da9837dc86c6216f606631e45de1c6ad8e92c28a3793a21b033788386cb82c042b9f6d84e0d77069a8418367709f5e2e9fbecb7d6aa99ec72c9747dc2711f729c47dca9ee33ed53e5d7d9a36887d124d37e0b0e1868d10fc1a68a82186308cb2a67466e7322b24f36c91dfc5099b5d17276c6dcec5098bb93861afebe2849d189755ee1b4216aad2f9caaea0f327c74cea79246fda3ae79cf3e23979cef9cd393166e639e7ac2edb27c792fd7734a5945ac1934b580cd183af74ac46814551a3c0a2a8516051605178fba417c65170facda2b824739a9cf4c2a4c4b2cc07953fcb3a1e98b6dda89cf588782d56d3b6297a6e2dd97b7bea7532064a24ba1fe1f567e9fc07567a4b29a3e8b28e0c16b6bdd76b7e7742b9a0f89d502e287d27940bca05a5761807e508a613727a6175dba4dc3629a588d434ecfaeac9d5b66d93629b14467860efdf615fbf48735016f686519d191629c22986122a52a49b62e31b01342ca972fb6d60a153a345830e0d3be04e086463c7464f9fb8efb741a54fa23f526df8f409b3c1138271412cee480316b76b78f152c5ed7f69825123269c78625403284650bce48c9608b9c08ea29f4b43137cf298340d379cdade83018bfd230d4dae17831a3d9ccadedbc2eda771c329ed5f6e3855351434fdd09516f8645455a3aedb093d0b12ece8cf89309088b6c323f45b08f4ae038ff8fdc155c8e20417fba9615ffb09f71cd02772d0c4dd9d7932ff70aa453f1df71bc685dc2e4f58efc26ac331525e70fa348e6c5e9c8c9e6bd23b8c6e5e58231b6ee431e993a8a64518f0499402162d1103d1cf16c6914d131a88bda8469402d112a21f2e6c7647d10f88d5102d4143f473fb3590c638b279b9b9b2b046fb1be97ad4b859387330a74b34148c340a4fb87d842c436543ba72d392db3bb84664146eff45c3262f37b75f6e5e6ec41d16216c2c1d2939db4dc52ac6c4d296fbe61bcdb9724a8e149d9999edc2c971e33fbf1877e53584cf39a713b2766fd8a807267c5a6cc730c73059abe73d0ed782ae93525e977bd7b8bc7161b7db5c5898ec76b7c5eba1a6d3221356d3b99c6a824f544a8b4d4593db1408b3084e9f4ae9133582452ada882b252d0210ee0eeee84da0c06f38e6abe2e3981e8ee9b711063b6a3a5f1977be1179359d2b645f718cfcb027c81b5ab58fb39aa6d12cfb1dad0d216f565d88a36ae55488e25cf9da6754fb38ab695a46a15cf94e5a64ae7dc7eaeebfa381dcd0db3fa40d80bd4bbef2d426fb5132d1317215eeb869adbd773cb45ac5605fca4dd3368ed134a9699dc6711a94166f9ab09ce0b4c85684705b00b5a4fb755d184665180408f7a3cc0074c8100095853c22efb5d6cac8008911258c2b6cae9052b23eb6c7ad75940f37f5d76aedf7fd6f4170aaed730ff7d88f4626d184282b55922c065654e12802e21414dc3cb850da00fd5c186cdf915b3ea4cc710eb00e09858e1c990b128763fa3d29ec289fdcfe2717e687f4a2d8fedaf3a2f4a2946214c3c1b13e5a1266dc351ccb6eaa5877b76bdbb66d1af73fb48a03e94d13d69416bf2b09ee476e939c64ecba2e0ca31b376713172580fbb6fdff604898736476771fc5cb94d5a446486f680b8687d0f5fff0fd0ad9434bd891fa3869b1096b8602512bf0cbff962cc11b1c63039bfa0b10039a6ecbf8734f9fe49398b0d2ffe715516bcb7aae0f1b4e1900cb5898b02f8a542c83192eff60662e4677e41ee62adcceaaa665329332abdb865d4d7c88802e2a6aebc2f10e6d734e0a068b1102b2e3554381a84edda9a13d353e3e4daf392ff739316cea5c39598844d8acd7f6320757be1a68a8f155caa90c460b40ccc7626360cd412f3033c335b77a5dd767e056277671120399270761dc09faa5d8f5d3410d6c8ea19f4d6e825de802459c33fdbe3e8932a11f4eedf085eec3b14fde3f37390d69d01936b0b67e5ea8bda3896177e4aa566b9f25b26ab5f6bf99594f3885037903cd09a73296a35ad552142f129fe63b7dca27faee2f44cb9cd5e2548ae528211573232c0c63b16ac5b04edb382ce3364d0b3310880a58e0025a3844881020ba724fe735f30414b7bff2e0f67b15b71f63bd9041089bfad90abdae8bb2582ccbf5280bb626b491706c10a21c4e9d6ecf7c359becba97df21b12ffa2e84711a24519084fc99fe64014760106ceaaf2e5b3748a20411bd7d245d108e42227afb30fe4142df3d77301efe100ab9c56efac18ac219377194ced9a30b051169318828769cf966420fa4fb50c82136e3933660b8063e551c6effd69247fdee7a345fcfaaf6cd71cd715bd765ac293bad20b6e64e90832325cb51d7f7db3158e63a3518b048ddc77a59b5e6784e155278aa50c2659e2a782e7bc8830c5d00780babe5e60aaa5790a36cbd9ae02b08f3a952dae114b68371c13abaef1f311edcc6a0b8fd23f6c46dcc88db9812f7476c89db23d6731b5b42ad1816acbc2de5155e402ab02d375710a71a6b82d9efbfc6b8c4601d5d4844bdccc0291912f15de6a9e20ab71fc381d25c387e33325a18be0ce50ebe3a1e1264374dcca64f4cc2a8c2ca0d9c70440fb2e0854afeacf50a6253ff75ed5c41e115745d2c47d19f4be75e3832bcc6e8202cd01ff71afed01f5a330ccb300cc3302c14c274aed499d2e2ce157405b5b04256784eb14c8e1bd21d9420d762c8bea3e5e518e61856eb1c63c69d1486c6ae1db2f6ae87bcbc93859a691814a09904ec8080191da4e84764e5b6cc7590862bc51c3101b8fc3964b77f2c80e862b1bfe31c3a295d4a175dd9342a17c3300c7b6dd3b28a6593e562e0c42e90beccba1e7ee56b608e188e09400e321473d4bfa294fc508d27dc936ea6a6aa82845ef4a2d02b51cdf42900b77f87e8fb452b4ee9b8fd223003b2ec3b7a8877060bd6c875c4defad84783533ad4601402d8d49f85335b582e54409b5a077800ce816372dc58998c0b569c999911fd681dda9148e89fb6e2523fd6417bdea1a6aa34048c35f8331997605f01b0a9ff00ddf110ba3294e1987ed219ec0db177c8be5fd65aabd434d69491b97a2cd0cc07589c3287d4cbe5cfc129ff98310039dcff007667a48e50a87d9cd0044e659fd25efa6f94dbc24ad06fb655bfe8bcfe1b78040444543004d7158e1e165144329000f85484c57e664eddfed1c6e50759c70ce7d0cf72092cf60b9fa4cf88c5258c231f1f4e71951b2630aa8894c22de38a108e9140ab8c194e7173f184db7fc92adc7e29bdb8fdeec331fd333eb7480824c34fca4f7ef2a3d77561d7855dd7459344c55d373e3e404d08c1999d99c5f304ca107cd0d333041fecb464f780154be147a53d4c55d5135ce9844b25a76415b5ef0f42130020d5f61a4e0b080fce4f95adbb07b06a0bdf4de294a2a37c8455bcdca345195ab83234d2a36505fb766bdaf4918333e9a461dcce6acdaefa57569bb06eb6efc76aa4d4dcb51beca64fb3c9e574ba7f1f96a38931b2d9da63e991ccc6dccfa99e0112124892815363e054fd54f6fd1248fb7e6e0b2b433aa5c53999b0527a26b1fcb5a252386a199270ec288164189cd2e154eaf64b3174342882635a046cea97354608715336033b4a209b9a054b0109b0bc180cbe227325f872f96f30d96e7f8e1c6e07e076cc6d5935ffac0339b0bb1ef36ee1e89709c7b880635ec03130e098de4279236d98705bd87951f710068614e3f6674adcfe0ac4312dc451d246dab095211851c5752afd33610f59b55ab55a3946ca2a65ad478844f586639ab0a648200934b2e97af4844d86535e0939023c3d98c201d91ecbbbb9eda89654f62e534c19c0651e2982aeb780eccd40791d64d465539b2efbb8803430eff22d20cd8c6f11d26283342eff7d0b48d3f22f63fc30ddfe0da8c50ee3b6bb036541ad2b0da573a24829a54539258c16dbbfa825b01a68d15b2df6f781d6083b4a1b1c920f894425097624f590a8342a872acb916941460e6e86d3af83a383a383a38323a5e5861414e42852cf5849414330c20637347f022d6b6c2e779dafb55e3c469b275ed9834562efd56abdc3eb3fd7baa331ec7aa51908233b1e583e7b1abfa369bc75add20c093be3e52dbdf61f4853e3bf6f9046aafab7ed0ab6e5e56c79ee78b8f8d75a2bb75c9750566901674f8b934a8bf3a7ecb438e794351c336b8dd203bb35942b8e995f04b528c768717e0f4cdc00c9568bf37b15d4e2ac30de54fa3476cd9d3f613ce4a1afdfc9f233bfa1f42947a74f730a933bbf77fad472e7374f9f98270648dcd9aaf95ed3a7b6e9d32883ee3c7111ee742fee94418eea9a1a8d8a1dbba66b7a49a366a8e68cfb73597d621e27aedcf9eda4714267b07306d82aed05f6c6513fca55e8c7bea9359e3b07d47850aa360bd600d94da29fd143f4f6c98cdfd135c097df2457037c79ee84b46a06ade0c73f170bb59fdacff934fa540a4722e1fc0a4e1850d6b84d8b4ce6b380b2498bf3678092e5eca61128a291044bd31f85fe03fa9190e08361d52a1c2b1716e1d6703e8ca66d3b734e7a2714ab3ba74d0ca2981c500cb29850d4cc89d5be992ec69dff37c40a6220f38ae74a5e0c9b73eec0eac06a217779ff7c4998b5b1b0f64ff99347d959d828a594ce3927dda460c7ca1be59abefd5a7b2432eba5dff42b052db5b3e7a4b3f341c985f9417f524a2911ee46773fdafd4ad9891c231f8b0dc4b947faca9772728c7c1beac2b2bc6033cbb64fb4f341c99d23bd17cc0f23afe4b6146a9582ed1fa120ab872abd93caee4763a05fee24c7482b804623778646ccb60be026a5eddddd19ed6984de23f48a6d04a96c5e9f26f7c9660fbb7940e52422034bc4bb729c978877c1c0eae5d5867be36658bdc23ba24811257a371b7070d5ef7603574531e493fae5235ed7db27fdec55ada81756336de3ba90c88e58481e6513fdaed4fd28b5b87c2f336068d4a0a18607431b376cc07183e9063134c3c931f449e01d8fbf7830b5f73e8937dc0dc371b7ca6536dcb8da0358f5d9b8db16de8d03efd6fddd4235dc4d44c3dd6c8dbb8d68dc8d05e66ea41977f35eee566a71b99bcb774411db3d99f2e2dd6d06e96e302c77a331ba5b0d7b371a4477ab2174b7efee0672770bb7bbd9d0ee7623bb9b0df56e38b0bbdd70dd4da47733cdbb0100871c5c06b24a8c1143934ee98dd56c0d7aadf57b300632380eb9dce44a1c6458c6945c068fd04b431d9c903920e9b93d651036e7c9be955c06c73a1691e37a8631061ef17bcd19562555ca2b2eb6383b9c7d79785d53077694c121863ffff4e9026094e42042643885fdfc7f79198d5ce6ba3ffff80b7410d107c7be724722b4c9f65b68b79bcd860c3bda1f4a7f0773012e45d9267de572f672dfe5b8b7e0e5e4e5de3b1e9c0ebfa3b91f051899f24a2edc31b22dce1d54e8e97ba8caa67ecb0598d1c1e6b8b40ab13da64313ae431c64d85772c17104573a8ce3402577035be76a3bbc82b3c58d75cdc0491856b07a341b044d09b262498be49bc96b3ce2435ee691bdf6f24a0d1c8ff4adaf64d3b4efc1577b25370325380eb9f65e1a983df6150be70bba8b6bb928cef29b50c76d5a3823b2dd0cf7bf69337f5ddb425c6773db6bdc3443e99c4280708a514354a155a4bf16d981bdbed22ccb324da3374d5840459c553b5b8274a7b4efc563fec5b2847d82bd5f665872af70b4a1cbe4ca6f7753124c44f572081b0de10760b835ae78698b7368192ca12dfa054e79a10b9ccabebee609486d81535ce01411f5728a3a388531cceb35697dacd649a7502b3826081c7305dda13a54678acb568bfd455f373c76a4a59a56d32ad40afde9935fe9d3a42c1caa934373a4b42877785a94393593d913ef0adbcfd20648c8eeee661658973a67f522b8ec0083fd6f77c7300c53824f8f3ff7fd38c5dd60ab48389653f3f6878064fefb651c753d28de1645d1149bfb749feebf008fd2022ecab8d87337011baea2f82789229fbb05d40d467362af5772af7074795dd2b718d5b0c8a228d66aedf7fd6f45ac86b45c4d51fc112f8bb7d2481eac563faa241c0a8054dbffb46c4eafa4952963e5768eebbae4fd298253ac7d0e196904eb30491b1c38460b45297694adbeb969d2a7515ae91a4771dff26686a338ad388a0b61ae67557513cc15b28a432dcaf0816ce590115b943a3a9c9237b20a46c926d8d4ff7da391bc913a43aefb97fc02067b79b1a758285b12e80977c900184aa3c51731c896942d295bb2f537fee6e6c6e79cdbb66dadd6880bdd711df7f239eebb900689e843cf8d9074cfbdb51a968dac866548b8ef422f0a22fa50f7dcffd071a210d741e9b82ee4ae8a75b737614dd17a23a3f453b252abb5dff77fe97cdffff8398a55d91d67741cd52a01b0404593c400aaeb93d8a0a2aff384203801828a2609f6858dea0aa3249148f0818a865160fcf94a9dffbe4be7d2790f8af5a25c158b2a4a2b4ee970eafa2e2eae22542c2e1d29acfc7b00465d55b0090b2c6e5fd2c34b670737970ec75c50ae9c0be7d2b96a2e25bcb8359ca2d78a753541c15862e693b9742e9d4ba7f4a34dd9f8c658b229d5b45a1fa7ba69cb51dff685c1f6c728ea059bfa5b5f8bb6bc60dae254df994bc110e332e2f673485b1cc3032bc391f6c8ca7951ec58af67ad17859569b5ba25058387735bb3e913937c3805805b23bf3406cbd7cb0183cfedf90518c008801620b0e83fd218d938c829ac65b48d76fb2db43fbebd6c951cb500814f340b165b74fa4477da8aa3684fcff5b7386ef2b73b2dd22cf864b3b03d6e729c5b3febb9360b3e8571fdbd0cd2b4d3270b068bfefeb6c751f6678482eba3125cff0be77acff57781c27a386214ccaebdb14deccdb514b4411ce38f81560c8ef1bfc0d1126bc54dfe9f0cecd8a263ad5cff161d47b5e0b4e45c6fd161550b4e8bde02e54261c7169ceb524ab9d5708cd339e9dc566e726bd3a2bfbbc4d956d75f0372140d7188614736239b161f1193255c86aee4d43c430fee0c99a594fe5314e07657f0368f585e1836a59c94c745ac955cad82b418c2965c1886611708e4ea1f2b96854272ac480c1602119841384529f884a397bb1f8c85eb1bc6554eb536a71686d9f1d8c43e35cf6fb1a3b0fee3df90ab59a55c61b75fd4333bb9aa4584455accc2604706d29f0c9be4d7ba855fc67161b0f4c9f6137cc285353fc32a5eb9cefddcc9d56ac5e29c68268d4dfd208be0f52bb2993e7a442a8a34ec99604f0b200708104778aa4459dd917f7440e59ec091c7b83dbdaf7f8fbefe03f3e24e2f8a6b0c2a5248bdf266d5bdba6f218d1295d6f508dd2a598640b86985282b1546d327f9bde32252e521bd0d3441556d7c824b013f60c149013f78018b2212572fab20b300aec732f7ccab7b75edb9d35ce3989b262c27b2f6d685ee45516764453f378e1a85a21f1b8a5e6e7e382e2c8cbfbc303c5cd7c3d16511fdbcdc38e1862adcfe1b7e3815623f9d1819e8ca8edcce0e47b1cc6c9e524a4eff40ea90dd53ba1c7d927dc2b89f5fe75befd3dc27adbe355825d76ddf7d08bca410daf5d968abe8b9931ba8e44a51d77d05c722b7fb1d1e02bb906b91f9e21639f00617de685114bb6fb4d8bffdfc19d98f1b287e8b93eff8cde3ba535e8def08b6ec7870329b727554582ac04796d58a85cf030719763e5feca2d281b6edf2ae877e1c6268bfd58e87d65757a7e0147729e5f7e02b4398eb8508f9afc7b87bc44a56721d24c2dd4e4a198b5caa712d4a2d1cfdb1907d03e7dcc229ddfdb39f19a8bd509917b89ed5daf9a0e48ee0756f4bc6c69eb5efd9f74aa5d2676dc98eacb5d6b39e673d6badb5d696bcb7de5bfb9ef7b6e495acb5d67bffcd82f53d505eaeb55697b7a5f7feea7e944a2fbb0694defb165a6ab1a0bcd65a6b7fe3acb5d57ab664378e23913c12f83d7756f34a5eb67d090c026e2291b5f54bde5b6b7774add67adedb92f73bba7a5ff2acb5d6daf78e8795e9ce725cc9abed795f72992df37aca712e588b170a6147fba51f69d00ffbde6fb6e4f108bd07765f0243bfc912c82d8ade039feb93e8ad7d2659900773e0ac9b9655eca2d363820e937be3320f1540f7e7f2e54bc170656bf1b1d2a7e50876fcae3b4af2b449be53b9b2050956086eb1b90b1e38bab0558eb6a58abdfa24efd7a7c9bd068e44c44b430dcbb6f7dfb6fa98765d1bcdb4fab9edbf40212eedb10270aabebf013895bdff9681438834ad63c0fcebfa393fab1324425eca9f753cba2f2a998660fae75445e99f204cb30ff6c48dacd5da50e60c769459ad988b4c1a8ef64a49ed3702997f1a45299d379b913944037df2f79f4f04883e5105344af493bb1225573a344ad6b849144a9bf923cda520c65d0f7c3b64c96d604b0cc3be075f2c4492a3e43638fe6d3af6771f0295dc1adabedbb6dfe1dc36a557d17e6c92ddd535f69dbdfff598ccd95a63b281e1ca8531d6d8754dbfe6c48105dbdd2f39555f88f90e0e21a2d47f8b1254b95e7f60434c9008796788a30a56b65a740e6bc98ed1d014ece4f26df5495edf02aa257308651772c751b3257548cea1dffb12e1b6bbbbdce19a05a32687fc016147f2f489bedb16a5d7d90b1cb97b85dca2b6ddfdd8970b2d9139f873e128824ba98e948bfdfc56ddac281c3f9e255db8e40b4d298a55d49f4aca05f6ef50ee00c18eb2255b21e50e19913b5db4285f64d93d3426b9d94e6516c9225964c743ca2ebc507bc9d2bade7b5e9225d74b62733d123844924b7a0f24b1905e92dc07ed820a1fef49de9392a07249cf3265bbb80b75d95cbfe181748c4e0b1b468b4e7affbcd1f5247ba4a75db4e8f40a6de1b88215e288bcb3e6715cc10a41fa23f27a8fc30a76fcb964d6b488c30ab6db6968aec6b9e2c10b3f25d77b2ffc8860b1200ca845af1d0348ef3dd6fd20bde73d89f41e0b8be7fd0d1f3dd6fb9106015d1a447aed49a0bccc281af4ccb58ab48e08b6e5bd9758c7009627fdd5fd60795212502ee9595e760d607952890211216f29f40184f961baa427853e3a6e763de8cffffa14c62522c3b89e3704eb7543b0e35f7fbe1ec8f48b169f8ac131419d16769c2bd9b2cc43c5cee53bfbe4bdb5e37b41bf6d9ce7799ef7ddf1f0de82635ffbf40b52d8a3dfd12e3fc1d173e7fd8ea6634399ab8984680a9d7ba42fe9b7a7419cea2ab7fbb1a10469d1ad17c4aa104b388aa4707c8d84a30b96f42c3f577d6a79af0fd402540a5b8a6049a110de939e251cbd67f991065def49200f2ca49085bbd306b10fed22a341a3778dcc39557e4bd26b62a30f5e904df34d3e02b873ce7f8e994bf8d09f1e96e869916bb57127367e2a57fa60cae50d6c7c63ac39d7e79c5306714afedcc46054b3a4cffcd94bdcd94a1871e79ce1165e356ef2cbe65b3f61fb15ce1d27382d3a65718c5f35d77f8633a7561f3bce9ceb3347a74fb2e6fc8c31c6175f74d105152a524a9652b2949225675184b4f6fb1e0be6918878b7f9cccc5232cff9ecd287d75a6baddd332e3b76641f22407c6494eddbb8996516b071f3e86871e304a01cc336b9238d19d20c96797cb0e4d6262c5fbed11e18d2811b9e1d1c16076ca4ccb09acd8bebb2e7fa3242ac3cd7d1e72e4a29e52e2a1289bc1012cd16c1221c66c572428068b188e74b650884900844c18edc6227442003f9d61b2cd69719bf4a5ff607d1715b588110eb7338ce5b694824081a9f4d486a5a587550f001481020b0bf7efbba6ddb5635add64cc32efa7ddff7f9d0557c8cc102f1d1021fa80f2af8f0390a08077d3d5fcfd7f3f91843cfeab2829458e57cc8b41d0af051bbb6edafdf4686d99179afcfb8b71eb76ddd17388e9340dc6c91878040b40885ad4c3a28d820fa74c9d6009122a164c23692051cd3df3dc146b240b2c0ce1a517c2b62606547e0c1942bac5c11d4d2ddb9ac12e1ac0e42e7ebbafc19e33b29db95a58e4da1be94dd103ce87bb6ae6c145e0da4155bcf68d9ddbfcfda79fd65669ed36bc58270136a0ea149643b193d3636407de29e1554b9fe57ab4fdcc3822faefff505c69a2f67f843f7fdddb7fc504843a3a4c80e90213b7e7ed82788fd5018a4d4f2a557a21a19a8c1fed0ff20a528948f0466f6b36a6445a11795429a28414a25191a1a6ef9d0cf0fbd8b7eb4f267e87fb02d5f7a24a3b7ff430949cb979e55dccd1108334ba0f71624bdcb2b51b1c89c6ff512d248964d0e6b256d5c3c12cb4ada7cac97877925aa19cf2ad28f3247028dbee569a20429fda864df25a4913f3f0ae5289436a4913b3ea39d914f8bb4305482300ee8be1f86863f74f51be687ec617ea200258c31b0481f2508cb73cf71df7d10eebb6e16f7a466d9703ff68ab361b9bcaf5858bc1f924b48e3f25d1096ef421a97b08142ce0b4d2055a41066c6cb377afe198530746ca09667550bcb7baf4445fad1572fff2d342edf852f21cdcb775d48f312b68439a109a4cae56a95c6ce298530f495a878c786303404828412d515e426a7e105f65b3e4a90d277300ee8de7e90ee6d4813a5cb09d27d4b29f4401278b1743fba8dfd8e85c486304144a1ef42981f441f7a514813050648f70f605517fe7005bd800b329041cf6aa5f29d9a56d94fd27de8458f230c96553e3e3b5352d0032420e1ca6a8524c98a0b6168f863ae260c0c901f6bff890dfde7b91fdd070148eca8a6ca6ea10a55584ac8b95a3673250a61b8178530d8abc66973fd71846159e5406ef2ee43df7d2884c1422057cb4dfe17d0e840d7bfe7ea71d4d5baae9eebdd8f970ecc0ca74e8b2eb0e364b166563515ab3aa0284df801106c3016ed0f0a229f7185b226f4053bcad67cff16417f79d55c17f663832365e4fcd92f7aaa39fc79e4e69ddf63de296a546c8e46cd50f98ea795a8d42d1665492c158d040000008314000028100a07442291683c284f84693d14800b809c4a765a9c09d32887819442c610438c2200000000406608940400c098a877360ec60a339d1151f91f721bb2615dfaa0679e6187c4ee3c9c7257315c352a47cd775bc2553a006fe50aff73fbba59e8f90c995251f6d262f78e46ec275928da5c4f8473378a8ab5e9982455fefa73ad310541d872c86c5916e64d26df5e44e96d193f98c6c541650851fdb61f99304d3023e272e040feaa1e03c5af589d0ac4582778eac66f2d45cc30bd83754c9670c7b12895f5c6cead7956867075ae50d24acf671323304684f773311152f00ff0bca23ba155ec57dc7483a214b71ca2f1aa00a1aca239cfa1a949cc27c776004a87566d16d22a83549cee53aefeab583c14b1dde6fa43d2eaf14ad410e7cc3e5bf4844ee2f947ac6400b8adbb41a6f7258322d429093e75a6d4c0362d4b6b4c12f73b6472f62fb5bb6c530710eb826b60b397df60210c35cb5ad6f80c98c629c396ff4c654364a22e304de2a61716d9cfcab4e439585162b1c9e69c1dbb43a0ed8cfff1ad72a07aae2adc7a5ed7295073148e2cddc52c5595353491fea949046ec0bffef91686ee38384c01468ec563e9edfba7b150dbe4762ca7bc3486af3e800175ac9eb2cfd9adfbd7e1e68632dc1e3e981b0a3b49db06c68f0eadd5d067a008f845c71263d6ee678ae6d43ac7f5346d7b4c465b2bf2c0da4d1c44994dbeed595517bf7f197e7e199063adde96ab55ee97e1ddf8ed5310dc92185fdc5aa844d1e824873f25daadaeadb7656ed9835deaebe43d328ff43e2638e5c757878eb2605aa13297b6b0681d30c182ec016272f6a7669e3219b272523f2874004b27d0b31f325f88df87c4f54a47e9960fb78633138100148b2ff447d4c50f05f5fc5a3dc42a3c9d8623690d247ee4585e64ed1a356949dd0516603f7c0c9388c27fca43d48915ab1ef215f52206ed5535ed9e9d12ce6fb82dafc01870f4b1da60ce7b9eb4347e0bfab067fc047940da2f8bfc85dbbc48eee716a2065445f3fb37eb2f9e755669b1d8371b6c0e2989c9afbb04a66fbe680580dd2a9e2d17275cda44fbcbf8806a54e01dd58649d4781f4e18514811ca128554afd7cc28bdec4accebfb90f7400caaa298c0fb1f444f844bdccc11c6d7f57ec0c20403e2c3227e151d6cf277a97ee6e0aeac6ed4da3547e186820f8575606c8944f1bbaae45478482d708b73e7bb0d0b31431d37982893e924bcc9ac51257c41dcbbf5ae04985e0094831551996cfb437dbdb6efd605521296de501051f55d527ef2eae0be5a7aa5ffa516e13df16f28b40bdfa977e854a822bd62b691eb66de8835a84c6e40ee5661e4ca239dcb99b6ae655ac9500d0ac981bc46a1764e2dc608d51f742894d7e9a468bf4be9f364e9aec59c4c7d7b1f9eb025e9f474d041f7ef656cae648929c49025609fa42cf5116dce4a614e33d7455db322784110aa8cd375d4dce92c4be9c77f1c77d2adcee84e3dae653baaba17aa6e7237509c3dc91677321ee67a3d804bd2d574bb4a8c6be051a728ea7c0113d096940afecc673f0357c24a983e64c5d9b0bc4ccc0cdfd8a9f77ad770e959082e2a084fa89919a65d7ad4ab8fa833ef2595c02cc64d3719cddd9e4c3c742c0c5650e9f82f6f4d4f5a12c77bad8098e0b6e073a776b8da44a5ddd9aff688c896a0085505b2813648b1fb737943bb3998ce69e88bc7a11a159e194f05db83510a7cf54deeea4e8966f966302e28f9fba77bc6420c6b0eb0a5f15537251373de466061456ebba0d4b7320694f436ba5ff141e0f195c66ecaa761ff80091dbb64c2128428ca6b9d74ba824f5edbcd2beee308c39a06efe9f40ea325a80be9763b667e74dee42c156d3c93590a132df310e1a4363ac8d6e439ad3eaf916d6e7ff368c07359e9c1d89aa9d6368a77e144bdf0786878f1c404ad132bf18cad66a6cef9ec01fd6d6d4073de3a86d2b8e205311c272fdbc1d23959d39853c3b3cc1adf9e0e9f492bc320c14249fab29d30f6c536930b25172b8eeb03e15268795ee8ddf6d19b45117ba7c8e9ff05b261104203b8d68c2665c4d5e49380498a9cf87033809d4801af77574d52af07f53f5e419d83a5c314905a1da38158c6a4e171063fc9b3dc7f0160983afae5ca48ee23b6a179b351c13a721801b85a7c4144b26a46cf6a4666c32be3cb5daac2b818bb072962bb94dd1d868acca41b53dbeeca296bbdaee36bb14bb501bb2cf1260936a1065ef048aaa5c72a79000c4938a42d3d00536f7029459f12e95e447cda0f4562ca6eeb549f06674573d56bf5b4c2ca5610b3553c01a0b9d20309a1e692995fe596dc73b0332eb7f31f464934cf4531edf1194edcb8d887b46f634aaf35cfff8fab41f181fa8362a50f509eabbb3f656c6eebed68f66550e75152a88fb6f1ded03e4a758fdfbc766bc92e478451d9786a812cfbc580fc9588e2eccd25d0efd2509ce9c777661f87d9f7837c88f263038124ecc2fd2b89e04723636696a4f9cede8dc38c63b8073e9e44a033cf016a98faa9c3a3cf8de3096d153d924406952da13c129f2e62ad52f9d15cac8596b6879b2a4b096f03f65c11bf85c7bb7bf82c4486e3f89be0f719ab14ad15695f6b9aa30d66e9e773815e414e458dab2f3b834568335964fd7081ae077ce9173b09380a5f4782888295b16fea45342accbb84fd773144a10696b35c463a2962df68bf3e23918ac8339b910b8aef7e4e80d9163d33f4694e72785068956aa7c3805db5618d0d98a94b9227bf4ec53c3b491ad07a5d970fb8fe831b1077d8b32ff4e43307da2ed6a177f0799968f887c0ee984d2b4722d16346fa4556d6bc0035f43015cedf87997aeffadea2761ee51a175aa535b40f7034af0219a9c669dffb3b06c42ce32d06e28634e51abd5040e9f9d3f7cdbb789f23faa36487e43ae3d99a5b70424cd198b36025a5c7ea64fc90028ae3326c9b3d925c9c2e6213b7ff2646843b8d86195969014b167d0081030bd3049ad4147700132e0c2ee48dfb06624584bdf6919f05e507e426608a4f3fe2ca61f8f98e1d4c1f7c8fc3ee3b280058784326c66503697a3d03b41203f943da4b993a1ef168c80fc5a6aac55e4e8717a92420a5efd2d0dcb65a258bbca3d598837822af4c3c0a7f9cce0efde7e0996978482f8c37e5e83bfd660b8f71d4f68599a5b6c5057a4d10c34e8852b696c0583521e657ecc91a091458e231321fd162225e8149a24b31aa12f3e773c8304e7de62d429ca0b196e04c2f7a2b68fa768822e55544c12b87e2fab83b9c88a609ad787a3cb7a289d54477b314e707250788bfde7ed950026947feb91c1c695086e104a5e4ef30c92020287ee0623409e16c904b23bba8ac4743a9c3707d9c3e9832e2f8bf1c67447f7ba7d53bdbe992359e9b13f5804310fd4c4518f660d81b7e96d549663ce283b5c799ae6cf36a95fa68160c471836ea3305e9cb20789f803d9d5e7d61b8c534a91a27ec886b843b763ed03752ac9de7e0b965ed6b3b3162c9d448daf20700a54fceb437392e8e86befc50a5dde19cf5c680e182810a1aa27144358baddd09392da2a214b39bd5b31535f7a1056316885a9dff3a5ea8141d66d9e2b8b7cc8359918ac81d94d2e83feb5aa820abfa214459174f43963e4d2ad281cf5e9252777df8f1433960609c45231adc526c94c9691d99c45283b6450e5a8cf36019edecaca335e5296cca6214fe73613d2fae809cec90cc1291e5c0f18b5fd3ca69d29a81cacf8c0934d9dd5a8ebb922fe3f22a874c693125eebb7da3f22daf9ffb4d611aa21c6c41f98cf313c97d3eb7801f2fa2517d4224600855d2e993945fb919891c95588d4f02ea9583f85dd7478c4878e2925d63dbaa726298877d1c7fc446aa5f8472b1e2b2bbb6ae781037f7c6891a41b7e0bcebdeb04125d5b774e673dffbbb98015edd8e60d14123517084b39b2903ff8512cc6656304bb8e9a3b173526505f7ef1b28e4b1073ca2f7e06535f94ab79103540719574c833938cc7cd9cd7a34151e1a231ce1d0d9604b5ba79a979513f9b9d54a9eb04912d65a36b69b7ef669c3e21ce759d2b072cd1cd709bcd1f9747016e47b09c0f2c62751f6b62e3155e286e4ff316b1e985aa8043309876b5a04da432b308100b79bc4da6d8272ac78bcdf8807543c595d23c17089383b34ba2c81e3ff8302d074c812f712ece4bb07377d0e859e08a3b7a3d1f777f1988e0c7ce5d6f2f323cb18c9830e26aa0be70984f5a76e340bce3b8ee307d27363bd5d8e8e3d0f678b42a4d1cefec472c4e7bee41519b28e0585c17775111fca645a7587f5e3c23e841cdf35df528f9aea17fb2e148743c3101c5e588b0bf1c423f4b9d07162e875b969569aa861b5c0d3209866cccabc5cc068d5bd4c0e040ba59f7c2d28aca34a4d4eeae766af19a7459b0c07759cf0f138d9a49000077bcfd0b6beb3c19d809bd4081cac203b17239d5f5596b70e547fdff94323a9c8a4e15362bbe6d4192eb6fa55212db04ec276c57601d8c87804ea163c4809b0591b4cde41d0d639d23f91454fb70a0f81702b894454ad6ab77211ff6a6cb88333b6efbeb2d38cb3434ce31abe297e6c8fddcac14fe4ae252e11a33d2b372c62728e8472abd20dacb7df4503884ca615d1850fba427b43cc58335f9d593fe91e4d57ae25abf94d0d8f69ece7063f7e677d0830a8215ddcfe3d26312b1a2bcb0df36e9f997c81f4a333699fb19b756f6e99d3f80aea1c34cb548bab83b191401497a0a41b996bb158d4c71d9051e4095570b6b89e8187e5ad0fb9257d16674c3fb14d898bc5341956033afeea69863e747150d3913a2ad6371c496b6bc9e824c367c295a6fb19471586822c100d593cd17325a507dd747b9674b93b20b6346cee9b06b6a70876444eb783929051c8d8b2ebbe1297a63f8119dc84c05533659669e081d9df8d613070cce6d17293368a6a73637e4e5d00eafe855ac6928f1c7f02ba466744e7de6af0f6e9f0db30cb6e221d724f29c666df84a7f73ea09c074debbc42bd965b743181c768f90b8b3f4e3c1dd5b030d385b391f8269d4b082ff397e5fc1b5974ab1b5cbddab1c892fd48d371618f6a5babad66d7b7e613aea25486a016c848a14ef53e70316225e596ef47d03ee153726e5e8d2d765ac0bbe8cf6518fe9abb4b0957727499d6aec32b8399f31359cdeb74cddec18db60fec3417de6bedc2af2131408440e314f24f5d87e37cf92bdf4d5bb3dfe91889803a3a83000188230074e465d54808fc906edba3ae0f4170a15a77a0e5b7a04f068f4a639e090f9d498952b39cc8badacfec5534acd618799e33239e885306eac5692cc5908433920dd9bdd11abd492ee63de5750afb70bf013bd9ed729cce1d1fab5e05681608bd27888364828e74a9e37ee9da30a9563f5cdd05e8f3c573531e3b4d68cee0a1e2330a80269367340725cdc507965ae96601eb3c2f3de5678ba7dc793903ac078c5ffc959ee946a93e04be7c5e2c916c3d6adf8934eb8f6903ef292b35dc95282530645f8edd8c74bec0164422151dc9156acf991165a46771810e944832f2c0ae96d5b98b0bc3361232d84c30107e2b493366c012f5e28abde6de74c943bd63b2de4d72e7d84531d6d6e107e09f2b1c3dc79cbb69f400e6c9fe737f94eb42510266e20b700388fcd9eb199bf6c08ee86d123368ee12e0c18d3cff70e56ee7d1c877409a014abd31f905c96c4bc48883e72fe5562e7c471e7b0fe4f14851ae016e1b1b44858ab5b9090d6ce6635f9347178127aaf93fb036a8f75794db365bcf86c423ffd2433597080063682d263e35f1027e8f0faa2dc6c36caa8fb88486112bfa6ef26778565beca66abdc0706894dc694613990e2e7272c3390bd839f0daa0dc2e1015168176f9df11d6add820285197b32a574ae46b2af5099a3637d704fd68fb867f6fcf7657e32f5dd1a38acc7348a63e3f10f881b411f5552ca060ab193c27bb4f411b95e5249b1ed340a7effae51066bd8d499c49ee66cf626b94cf419de1025fe07e4ab2fd85248c4a4da95847ef08991d3b9a4e4813cbf1597a027358f531a5cc3b47d1c3a70a2ba045ea01c03f87f666f10e716c2c53c7ef96e321fece1417a80337b6218f366333d033e9433254ee853e4c31bfc167b5787e55204535f808c4743adf363e0c0be0d2a8fcd0dd98ef37c1241c90d014755ce92caf46d2b080da6ebd384904821d203dc8607926eb15025f6b75f827e591f82a34ae0fcea90a729b0a2901608cf1a63556f72ed1d8f43da20b644e44ca883f490b0b6ef9dc0199dce50948f791f090ea2ae46f85a6d15536dcf8a6bb2ebf465af923501a0817e8ca932c1bfaef0598f0d969da6f7a64091e39d5fd9de192e1cb63d5780c2d6685b713db0772a65d1c533bf1030c1fa3fda702f5e910d93e19e419f34daf27a42108f4f384a3b9db93a7d1037df0bbe4001782264c2836b98c01b40da21ce96a8fe67233938318286e8dbb72ec1f17e224822680b4b4ab0e60c7d8c724ed44c412db8a8816b4237089c946a3101db31604ed8631fce3c7f4c2611d748a32c174e94e9bbfe01891361ddd9323ac229eae06093e1098feb82a6269ba42448ead9d65aaebf36874aa09a90bc6b16fa66a8b2a0242346b6761862c526e35b9cb3bb10240c506b7528f232151dcbdad66b61d9e976279bd942d901da280f34caf69bbba12c90e7eaa466c2d1a3210ea4c0ab558f65405acd88366d48ea860d68a0e90a010b3f62c0df576b191923273ed4d882aefd0c32bcfb4b7b1fc038696f2d10da548921a767b240397f902f41ebbda963fbb0e4a9dea00d72a3bf1b944de7ab0350a45793ac01bd854891c859c26acc1a24a5490fc6bcfd0752ce03aff815f2367ad4299d44dbbd7f4b339bd2bbce36b626f0afbda73680407255b8a1dcfb6903d6a2a2b492ea3f3e06052cc81ecbe821ae14a9008916eebb14cc39f4248133d643cfaa16186426d74f172cd206017e25df335219c6c08866c63733785577857f38d9503f9320a30027b16a1b604a7a8f0d9e1169b4b957f70a7461cd12e0d59764a98c1ac704df24cc4da1c1eab0d09386d03fe1e6db440597dafd367e4182d65a11952c4928b9823cc0c284cabd0c1d091f4283906ac52c73b96cdb96d5175a59dca08b904c1066e58651c76063d2690be934487c2bd7010425d6cd035904889becaa31c0c33f4141986fee8dce2bd8c759e55a470c739b610b996a84c0d2328f77edc1b00b22b4cc3b05c2503621cf252e160035e80d783896d97f4ab13cdefee2a0e2bc4eb2d2a3d0d634371a2a028580b5de7c3caa055b99568531c0ea0bd4da0e0b8e2010e3099ef9e6089cd81c29d016848ed1f04f428a509f9767e54415f067653f3a613c2128c2199fe163f38aaa17f0bc7a08326fce7976ecc358e13c7613afb8c6e8621e7370538b2d82773231d823e3daa530abdd500897228f536ae04d3eb2162532c01823c9bfa782f86be1b04cf97b4f906816b980dc3a288605235cad0bd232ac18fe87cfb98af128a8ba4160b2f9635f483cf00f04bad9c663a9860f88dcaec752b8197b30dd410c4f9179f5eec4c698ca9a6f293aa296c00bd984119b76a56063b2be5bde658b030b94dd7eb8a1873daedafb978eab83787ad6acd68d0074a1a3157c699e8d9862e2a9134746d4db8a113d9d8406bba9fe36a3d6974921336e5cc00e01aa1f96e46f682716fafdd3e1a8d763351d7abfee783ae28f8c4e89ce75701a6370a6e150946eaa0464b1d8a79d836def8110d4e35f3f4e73b2bc9c1d785696db0da9933d4726cecc860ec6fae0382973d6d895fa28acbf34e0bd38f95d773e0342554af930e9e181f72d6a7a1a25265e2e469f5fa15e8c21b318999e23c427ea30b2842c09a0a8f5e37e1ad79bd3d7bd9f74530e6d5a46902cc7e8c722595c56b899b584cefc39a4f404cea7ad60c602ba5d6608dfbd3049afa77a83e481e50e7e97a33c919745dc004ceba068a4f8a2094115c2b375d75ca35a21805e8e773a4f3ce6b4066996a5bf88c5c5189f6dee7eb5556d35c48d41d2ca889fff830ad00296618045d1101ba4acb5f293d62b69e1db474eacf7d3231fc56134898a4f056d33a7558ddd70afca1155902ba65f53a8e63e72c892dcfda2ff793fcde7c971b26e3b12609f12c2fe7e01ac8b7bb455390cde091b8308a3358379a3c522d97232fc5a4c9f0eba54a40135643a6fd04c3cc22d05204515ce7e3d5d541105e12e967b1fd2c251b1def95b7a33f21dc1d0c16d8f320023fed47728e467ba694e49c508f2c655bd7e7d84139ff0e9ad0a4d55729e72263ab9f405bef38ac773d1d7dbb6bd5acbc5ad6c3bb3116466b73362327cea4987ae610b7923eadd7fb523bb4fcb60e97eb700ec5523e9c0b97c896100d9669d01880d8d90c11313f91bc66b6317aad62cab4cc068c7ed79441dbda49b5b3f4e531b2ad03bf2364aa807d7c30fae361e023e495bb9e7a04a45769f2dc2bbfb3fc0a642d3f5da55ca426c1856b21135358e172ef2bbdd37b7f55bb60dbb52d28b6559b07f1ee3e13188d4d01e70a6ddd81f892b39976cbe9402d32e4ad17547baee4fd0a42ccf894a5391f4d34162d122320381133353e93ff57f75b77be15b07419a891836f8e0db958096b7a5c17e76900c277761216255cac72418d0e7cb23ba23044ac8c3664045726f3e9c69d77fcfcc661992e0b147c2a4263ffeff66bd86a97f9adca62d1c9856496143ae505ec03636c8db476d6219eaf822658a219a97378fa4151060644925914078ab37820756c0bbb92e93d9f1111dd9889f025152f668e2a78f8b67cf5faf076d75d13e77866606afbb73784326435bc2a8d62e954b906060c28d47d98026962385c54ca4a2f0434dc059427c82a40cc6b2562500ab0e7b06c40e17191ed2cce5484ed9f916612fc208b7157d231de54a4a3ed8b5c1b955ab9fe65575ba1e28314eb8ceeeaec2513f1f006a283865692229aa5610f5ba40109f1ba36b411a4e26c0dd20ac82eb3816f9a6ce6048e6d4b6ed9c88b83127b884979950f27efe9a607de2be1653f63a6023be17582e648ad15a5fe40be8cbb13689ec6e0326fe1a9c435d8ff9a10cb3c5affe3685bf4d5841d4f6b60b808d3e1f27bfc0d71060c69320bc720709e4a86942c2dd8044dbe08b07c2b1b69fa9746009b2d86c5c06bc1a00e5586c099ee9266155f4d0b0483c31718712d0517e1d0ce13da25985f18a8255d613b830068473c5b611f61cd00c1288f81c130dd92dc8906c6facd86db2a2ecb5e5281ab3986015a97a72d303a2cc8c12b30c037d5c85e63958ede4708cdb49c29664498881aa41289b2a899a86da8d12b13a984920502e49f085598657720e30bff45b1de51003a7767c8e44500eb6757780d36d88f99c92b2df037e80dda7671c34c35642aee1766371333a8d51ffc2daf53333892bb01062bb2dcc3906b6170af8770a1ff54e9e8e214d72acf0ceee2077f6cb2fd5b661fcab1580286ccd40cb0b44ddf52f9a2d8f8faee27285996f21da9af64df7565f216d8400173b898a7102b23471cc2d82b81a501cb00b24294eb767319f1843ea32144e27bbbc3b1f5f6f42510731036680247e79dbc53f04c00dd436fb354ed38434eee417fef45cadbbc09f5ca4d35d9595932293bd8d71287951d400f70b51b1561aa9f9e84faf930ab9402a4e2065d2dc47fd4cfdcdd30842a9870414f663bd468d89b59b2ec83f93419befa4d40d1f54358ae6d74ab17a17f227292981247c29ae706abfc4a18a41a380d52a1c796c361b3bbd375c38b3276c1d94455ef89391f4e6365e05e1c31767fc57fc0941e826706da89cfdf2fac88dc48b4734e7c8499e94374a950d7e11ad6e65fc046689457f8479955fd4e8a8ac56bef5e601a7bc7be09a7d3c802e8eb13ae074b7d2fea657b0be452612dfb9697f512a4012a1a4d411f3b25f922fcdb6e805216be65fd231106bc1c005134c093b0fa5da67169b6188483600ec83d8943b18315083b291728ae28063f0b1dd1edcba7cb0ca395d5a6a81da46dc6889d4980641562dc072639e5d70a2c34999e28a7f9f3ed133e8fb41e9cf77a1000213716880abe7f7fbc61514e88a2c72b954244b8da99164362517d7bcb2cb4fe94d7fe21c4c863318b083d78ae2803125b1d98be246413b2c3fecd8d61fd387ab0703bfe48a3d12e2f654909d47faacf7dd85f88a9ee2b65e1db1ab98e4ad9bbffca0e36930c4984d27f639fe07c37ea0e472e96b247e297b45f78895ee11355f17314e5e4834217f1f5857e4dd04ef6829b3d8c452cd32907f88f64b79f411d8174578b9719c8949c071e7f037d13139b0219f45db54a30faaf4c46e661f4d5b04548979fe3d2279f4fcbb6afa1249d059cade88882518f4519c63d952ff77b519887dbe2825f5d088809859d21064b8fc9a7de479740a911d5c9b00f9972fd98a8d1a61e434026f5b3742e7a1514032d1451f63388d03699429fa33680c60d50c07dc31b2965367863516ed49e3e49e85f3b3c25c0c25160c89b27127160d85a2104061d70199a7498fbe1462c4126eb0d345d080ba83dd0fd8d95078551bc52cf4b976cafe8aed624968dd092c424f19655635362d46fa1219db4d24311e8ccbf32b4b5f854035ad1ba8236ecc0f2117df45034b24061469a54970a783fd5c935e24d108c57377a0a6f1716c0c3644c02956631549daf82d013edf4d542f47397ffa2f9a46c0e4e5d79a23c780038e405f041cf2a567d6d34f1b3061145e39b94fa5983b08deb12903450c21b8a641e30da76e4c6fb3e66215200a3ebc8a483029681124e253f435d7169133c48faea8f6c2f99ec309ce2a77a986b87e5642ba3e89a7cb290360975a74f0257c87130c4eea8037b6e6187be052990c43ef1c456687877618c1811629451282eef80937af44da7da24ba72e8ba13a4751600d6b74f0bfb54152aa9b180d24f7d8390b59b041749a1205593e1b1bca2fcc9766e611e62a532fee017ffb039e4f0c2bdfa0621a1eac35ffe49f02b93904cd99ca1eee79a9c050a74a00e6109a571ef8c19f08127ceae7a88b0077d310ea253cc7d36dafd25dbe76daffa0af6f44b0f09ce28d4a6a82d5b327fd5e7fdcfe35498e4894824fe430c858c8fedf8e99143e63c91609c81809a665f1e9b1f258d6916f99198386ff88b43b41fc7f2311ad0bf39f8691cfadb8df8549d09a49857d687dfe68c4a180b8b3ac698280b560a8a2b60eb2e838b65d97a3dc5806265a061d899e89166c65a3419ae33d220609e1dc61b4d545292c951dfcbae3ec1f6c7d06994b7385661f10e9c04da979e55831ae7b358ac6b8633a086e6ee33d9d92dfed2f3a7a20d55cb1fb2ab03ba086573880edb0e7578318f53615a3c47ded9e782aa0507a8fa5d5d3040d6b8779acd5bdbf1c43398c5797479201012570dc7bde21ce9b2e7376dcca3dc3ebfe194e9d5a63452d460e137fdee622425d7c338563221bd79ef43398a5362a10b2c60590a2cb716b63a793bef4ea132b95469cb2bd383f7a6c065c4d3a71e10b049825f168fbb5a4cc1cfb42e4d850c14eb455eb63778c12fe4ddf5df57629592b6ca3ce5b9f4b514356e019f2bec66622d09c1b060417f1789989b046bd9b2e30af0cce5f8aea3293df6706df53de21f0caaa381d9147e838a3eb37e4e8f81171ebcd7dde823ce093dbaf58b11ca96b8f88b0da72b387d4ca6de144e1d5863c4b992c724360cba4f8a699c1a8ba71aac8edf1b55cd7b3fede2ae87374373b63d440609f8e11f367ebc2635f48c93356d4a0948f019ab574687afb216445497133acee77a90b9638095eabbe598efc205a99b33992a9ac64c0f796dd5c448ba13a8a0309048e0eed5c9d08cad887ec9c9ec55b645bed594cc2701f16609fb730deee617c4598de2642147d6fd713b305ad5b81e44225db40848ab547559ebd689caf505f893ae2a582761a20a5d2cc6000022bc39a8a90758a9880c6419e5a2ad25cb3c3e73acccbf887ba4ca3134e63f66f0fd0757f19da65c98982483ddc1471133bd06a86b8a3453b99893698ef5a6a3fad5ebe1abb5145906bfe74b555cd90d7427fa1a501e2e56ec6849351dc9535d497a62457168e1e0b345540436a3bda15865d9a75f9e1639725bb736796fcda15df3246884a5ba6bebf444e5919734dfc366bacaaf390606f283ff7a2b0e13b11f1afb32fe111523bd71e9c673727b6a2ea734d75c834f4d7f7c34c93c605767220e235790611eacee4d0cc7ddb66c3bac4834ce69f0189234d1e15dcfb5360ef61a953e53943ac8d38d5d49ce231cc15e2f9906dd5f2ccbe5f523fbfb848599440f2ad0b4a6776f86722a9bf1b1db1cf890c0eaf5b279360e1204b1053e7c5468ee43a47b36eb1e6b461e908f967f093eba4b701936a1b76cc04e825c5127d57f5a0d1b013ea050b40248c67fcd2293b430c7ad36b42f448fcb27b1124f59cb67b792728eb0d3126de49b62f33b18f1a127052332bae2245b2607b716759a429877997b843709ca9b94706663d896e893924482dc3372325c6a9ab3ae8b5f1153dd9e79b86bea0407b99586b82fc64468877c6525ffca33288457de43033879d2a76c3c8d906067e118216cebe16d52b393d8a28539806b4218b09c4c05af3766e5451c745aa08d4767eef089b75a93736e0cc7c6ed2773265ffd4b5f3f898a580c48f40439001106c357e8f10f77935ce47f248fc1ddd07481886d1814dc9176a01204811f46da3ad4050aa0b1b090f691b36bcac3512a49b441a82c30b43ba0cef2f5cc99f75ee16fee4692fa2eb756d14c647d98bd2c3239270e8b7599855dbb890252002ca74309c2a67c3d8b9fd124f23b38033c5f7cb253fdf378e1bc52e95c7a63cae90926566b766d43d048ffe7cd38b6e5f67163248114d4f742e0dce374030794862fb02319ea6f61563cbf4748e6e2c389ed4a4f88c77dda3cf93f9ff2b047d2a6c36fc68e5dd73b339bc04a16d6e93911030ecf3e79162cf2873f6801fddb1970fc9fb05ff369a5fb992111450c10d079c69d8e7582b0fa08c9c9532062fbd988b1eb796e3b98fe229e00ee65386d6922c90eabeab4f2eacfdc1833eb4ee5adb84bb460de533906265d715ca546c7285979a5efe643f58a5522c0ee002d5fe9b1b0c64818883102af2f1d09c72c71d90bbdccc81c9b81614431f3b0b142c5de1d64848fe8d31308e27f822d076ae35cdb62d108e0daf538a4f7d8c4c21b87a1bfe386089332d2ef82b42bada6a1840d872a40c9cb3427c0a2e33c3daed134cfd74d88b616dcf55a335a8dda4c134d137066105c99856e84f547e07af2ab823f491e95c3c9010d5c87ba23a176696d66910b054148d6d4391e9db4479b35ae90c8c733dcda18071318d20732cfb00cc06226601f5ef66b1000de8d0c76a0441c60e9139763fb1473623983144c382c5062f15e23188af5c643e54f16eaccebad26afca7e3ec1f33c5776f9ddaf510c7a29c21e901e8a620e27443ba2e83b3a4d3965f14b117563e39822883e9dd3eb3d1b07a81e243d5f9d1084da6900a2dad800a74be8eafbbf4c57f70f837050c1233abb64cea87da3a8abaa6ecaa6d32c99edbb59c84359c563dcca6245859b796de59e4072988b97a9dfe8003454e04954d8a03cf5943d7a058ef426a25bebcbd9d10e11754ead34ef970c53b05eb80823dae0f100b4126d4f824e5dc7d61be843a1b9a463f79d8abbe06aac1da6fbb20b632095164184f4cb0aa3070d18611f9dfb1b69f35c4c9db07709eba576e59d55dee8a8fc5e4c2ea4ba93e04b1cef1c071598f3623d578471edc968b9a681d9ee3c76f1d62a1ab09741113588c4dd57d0aa41cfb108f6ed0d3854b957fa23f6d3871dec0d58295456a39bca756fe610b5edab9d52df0f0277e12e72d09f6f73cdce4cc6283a9ea220b589554a2d081ddd357aba7a312bcf49c651bb5dabb25976f9caf3969764ce1c2e2f2c9d541efcc9cf7af6e163496b3c17f5f642ada12801b1d28c55c57379c96cc8d27e372e5a93f05ef895b162813863fdd30aefffd12de30eac45ed3b9f4057f2258b3b8bac98611681f16241a1878b3474884a5f11839e5104cf349576ca3affce13a25032219a8437dec7f18a44e34b22108c20af25c55f3c1cb751d77b28260b8f889c2bc6888383754d9e8e95bf413c9fdc5d38003375b11053496b06aa834f027f0d93463d9c283f590299a6cacdbcd862c73b18cb76012bf908aec0848b734359ce779a253127ed5c0b03defd412c4906e55adce5832cb78686d21db3a6b182b508707073996c16b9970387bd2d8a74c92c7dde788343f758c742e0bc9a6feb4d6d4c17d7976f3d6ce0db35ca2aa323eb0789be68628fba14e6bb7232f4f38706817984c53813680d3ae6638aac61977d53df2d521a273ff21a2dd2efa1273dc23b0ef108e28c492511bc0a75bc643868985c813f6e303feea81fdbed0712c2fae08fc1e596c2f7818252ed12780b5d8701bb84d0842d5c13b5e5cc39cb461234f5eccf90d49d98fdbb41a4ee05030b068192b57a20af30f2c195b2364a36466126c719324847d84dbd630f5ae442e6c6fbdf304fc4ad6acda7f0e7771ab1f8adec76c979b0cd0f8e6bfc6d12e6c83e1693a376f774f127e74b171086095090744da9e9d767862c3cfcba78e02ebc9984eae9b13d328a49affbe1fbc0d9238aea83ef28fd82938a08a3387df9559d1a742fc222d160529472c3c8baf4f20422e43a0566adc9b0741afcfe1fb59e3e6c07a89d0de0a70ed5e544e93dc7a7f6e93a2f424bebc307744636186177d612a2e72bbe8311c6a89a9fcf85c7f2306605a60ea731c6dd45ba96189bd4ccb36a2f1a81aea75c9dc46e7c2c5958f403e85709d2912428319c8259c117e462f1282bab85149af89115b0b4fd070a245b56c7cfd05858fc488630973a5c5e91fdc84e368fc16ae7069e15d60c1e62b13ee15032610919412b0ae4f620a7fd614711b252cbf393dc563d6b35784ac6c1e0762c14ff09851d909c8d312b5f754feed1adc5e396d275ab380a6e7282a49ce5a69ac9dfa92e11ee9dc45b78e3e8ef4ee1ff6319f46d71aa2e4e315a250e2dca7eee493891b3ed1a86c9f0705932f3af38b4bf105cea8ab7dea7bd02493c681267a0fbacb5b5fdcbeef61d95944cfc4c100be647467df6b1945e4782e593de431040b46f0cd807153c4b79138b92ca990a841af2f6ccae8af08ca7fa7440c43700d2dcf38ec07cfa03b7e2c4d26fa7302b2ca998ef6df30eaddab06bead9cc0efc1b6c8c57c28618e3fccdb1c8c035061fae70b276bbd501113b3ec83a36495ea3d5bf6f6ac096aadd7a31059c7d6c8f3654fbd6f1cc7f3b15743be6a0a1db1ac3e2b286931649b6a413589f65bf20c6c471ca86411e11633a40cb790b7df3d60ae23095c73259a1c6b3927faf27f12331970117f356aeb61860fb655a644744de70db96dfd930924d25441f1b1143190dd7c2e649f71237cce4151d599425ed84c33973c7171f0bd3110d7f8515d77650795279b32817cb23ab3514d874726241afef6b55582bd205985762bf0756528ac90d379688b4c5c21fe48836f2ea4bfac51d3f4f72e2b3d3f38b53ab1c57df60c6cca6cb0f5d33f21d78f88af1581b4d2b336bbec5abff1029c62d210ac100de593ced9ce8a9e0e560a9a21213d090b749c532220c3dd8255c2f05b4f5cac63d1d11f5ddb73572fe859b143e7fe81d48b71464317686443c91fea2467419bd4b860cd749240164c522580b6e205b93a2557e0337b06197aac0997d707ce65ec13453993e8792a4247207d9fe8b95834d4bba043df8ed086803e601ade89b16b0479e881483a405cc21c04efa982539a457d5cc582384454b7b9fe311ee7cf7c6ccf62826e2c27b3edbce4b05838a94145014a8f5bfe04b214c88ca76d325c653042f808e520ab71aa56df2305b79eac202704c3bd8f48da054ff240d08fe4373383d7f4e364ec2ffc864bd822472d22a9bd058191a48ae3cf5811479ba321eff9ac974ec36d02959c7e36d10a913c97c71ae59c274322b39dd799f38dd50a145a85be83fff04b0a93f19059d883c09d3d59cccafbad0549015d71e0ea98eb89071ed7ea376e72b38442e139990705255136d67f2e15b6b2ec8eb80f2c847a2bf4c29f61712d4b40c70632c4019e10badf07495bf6ecce1d08bd030754580775d44340aa2b6a35ff9fb7c6c8c96078e3e8b29461de08ddf778892939cbb589cba7d989c98c12f3ce53af9999c644366dbb9ec730e36934e94821f97420f112a9e336f36a350a673264e653f05a6205b9c50ff70081accaa737034f57f7549b96868e61b114ceb9dbe096d9c53636c5c4d600b79d75c30910bcda72fa4cb24626be473767be6bafceb186de9767509528ac8d48931802689cd92d2cc610466f23fa136793096518593c962869c38f9b82911680ab0d2d4628217ea4984f4975a90efbf9e4480b5d74fdb2611a0de28604e3a1632448c039a296e1ef70bb2bb8ad5d835a8148dcc8b71cedeac2107004d3d4ab9db0b30a004ffda01cdaa6563a48388fa40b358eec44875d8b9ad6ac5d2e8d86b4467d3bc7d4278ad77a284f250936e1e0fa7f0f86797c55c00cbca138127411a203f7ca121c48b46ad6455b99396d3c5ec12a49bf6dd7958ff072e00bf01141657af830c46d742b6d73e51d00a35a2f7ec33b8723268d85476e22db4c75ef3922ea74394b7937162246d092f5d9c45e97a83615325e7d3283d6fdc78f2fd44dc0242ac7ac59140eb7cad87aaf82f5f1e7cadd8fe070a1a63a0c8eeb48b3b55d9e38c83823020a23383a41c7a2c28d71772439c3114e7423c302df9c9c0190e1731ebf2e3a69e0eef43599576cd9a5560d4cb606831033b4d3726dbf6fa5eaba82fee0f5adf84d858f5e2202bb4a0215fc0f429aca9e112b1dab3011b7e7537d3b89b5de333ce8c7fc466311c88511aa70dbe3f181ed081c58e42cc019e924dff181fef80af6db421150d8897066d8d02b57cefa025d16f1826eca02ddfb8cf847f543c6b355fe3c980416c8d164b4972905b2844f4d4b328d9c8b7ff17b44df05e214e0c1df515c260ac3898e4e7ed4f21341d089d90ecadd4ff3acc151761a8641d99d13913178782fbbe2919913bc781a500a2f3ebce517e5836b17439e60111bf089c289cf9bc4281746ec9c516eb631f717f1b34fbdf55f18e86412a5cdcfad955f1f136436c34142a187f8440129283d67f2be18bc0f9ca80cf306fb1ac2be6644887bcd7b8fcd23f34dc9414e32851ba570439eec187d9e011dfe098cbbf6a3d6a1289b33699f82b88893f151b26b18046d6857b153c5c41ffdc61052b40210b118db8e970cf32c974f99a98ca48605aca7cd1edc8d78360c0eade6c536d458e145ce0cc14b04732890bc6eccf117b1930f7a2f090e5cfa40e1ca146b5207d13b89b8593337f8d10ebbb9e1542f9e358246d3454d008a92d7b17d998c710ca6580a234f9f352dcba144ebebc9933b44898e9f8342ef648afe2293cf9a52bc46764c0466d5c7618d547db3a5a3a8fb12add96a485de3bb490a142c1335f705fc75703058781939ce8cded80a47399360510f2e2fbd54892ea819263dd400e85fc0bcf1d8780187f00be6f29fe5f1c2f4992945b50a6f176fb8a821a4acb886b9d5b7eaf8eea552751bec2000c8e3a1b1a09882e75358dc0c8e62203c52d5549f72ba69754e6facb4bf1dd6987102fba2f4a1e7604dd04eee3b9d5eda969b024108b049550c69e608649c86e0d7bb3a0dee899d9af5867944695928a809350be3295fbddcc56c19b1b78e1013f3b0d021bd6074417a98c7dd513c639a79c75f349e641d24af0ff09c3e53f6d0bc751544054222aadece8d2dadb24127e5e3941b16646b50f144b500332a38eb7479c824ce8d26b3835aa00c9fb652eadd4f2b1108a40987fcf0ad5b52593282658c624cf457589b029cee78f17194af1490c23c2e772e32ca2c9e812a132a1b471ab0fd22aea39fc39f575aded353923e9388335ad6330d04f71a2d468b1ff9e9c68632d97e4e3637c58291fd10358665977b06dc6579c8e2bc4401a7850d33afbcd551825feea79dbb2a321986b9c58e7bfec629db6a4a58a3c43c4a71bfd4f2987fbde72f4a27f673eaf05c2f419d2bf2d118edc55b67e34841ab09c77aebee9ff3509d9acf9a8eff72eadee1e6cf0e68f18a0ff3d17082e2d120786e75511b137a4d496e663502924602d766d16c3c37d34e8566613e34ff83549103d0e36399b9804ba12d5d2011684ce37af3d4a209871565495cf20a73689615e465d02e23a12b5a917cb587c935b91263a431806f921d8787e13da159cfea74d06b8945a2f9619e6dc0722e08d9d596a17f62b5cfa1524843c33989b1073497c90f89197fbc874778228fa819612c4bab50b9ffb6c2fd7fd81110941cca2dbea75872925ab807524d31d91c9ce4b7b11cc13cdf52297a9a347b7b380c5ef86ae23df9cd5dea37a2c57d7b1fa557673a159de58e36962e1e9696d6e730410489461b19000ba53b07a65e34b4ddceb70e4936c3e555760cc3149f205023cea3a965e7e398ceccbc61bdd0791c5cf23a6a54e20d19bd8ee02268ec7a7aa97174e2f265188ee0598a083239bf483c4e769bef83e631e3db75fddb65109159da094e90d349394c32d75aec2885297db049624164a504cc7076e48ce74ed349e346c6e5e1546aba1aac02aa8f2c7b675e850a829dc6c25175ca633d77a62dbb2ca7558570fe062b7f27169cc7df0a897bfb4f599630c81778c013863b4477b52c3aa9413bb7ef7bf02dea5281c01f191a635b2517b385ff15a379a9c820ccb219e924196ff64073aaad6fa11b9cfa379868775c54566bc40d8b9a5c28cc3c82a658da89d2684607b276b368d3bf72b5e14032f0ee1e1947af21117219311703379a7c0fa5c95be5eb350d56605ce90415365156699c425cb2f2afca9ac98940f656e394b2799418b7adf7a29904b2da26bfa50f7d111e553c8c5a3a510744c1c032d58614021da5a1a26fe837c67617037a0bfef0480e671a70b91cfd9396fc04c92397a84459acb1e671e287c8a6736d3fc53c00501fa2256b6a487d4697b88a11f93824b8974f1e7910ad59fbf88064dd9518be1eb572957a294a4c310909866b42069f8003f79b03024660ebe02342a137facd32d52a0e81c924f6fd2fa5a3c7336dbc2c19feb3e38a382a90f9e2a366610c76864db64ef838138fa59bc6191708b83487c2ca15fab07ee79329d9a5c3a7acd49b2981197fb5c38cc0486a49762b5ff098e5746681b3890f46e9544c29342c246f0d20716159b5c366f1267af8b18560a95fe61aed15218b55a9b3126a918cdae46dd116c645699b89a7cf6995102a72d57b12ccee103d616572dcde604679662444e3082640edfd5c292367c6e61c1037f32b88b0c51240a51f132df15698c3ec26c3ed1b819b4a998b78b54d3e6f75eb5c9911e2aa1f3da0c996cc31c770625b58c45fba9c2940f48667be7af9a72ac595bcc732a0c25c8c9b3fea741dee592b0c3895161baf3bce29dda8b4b7c1af5f981456625ec17f5e4a974e3111e5fff235fdfe8d44055d908ef0bffdbb97f7f514e9ff9251bd88c9bc4f08e0e213e6c035b2c4a9301bbdfa9abfcf044ecbadc5c7292a148144478605feec7cb6329ee25414c85426018e5d79803de39aa3fe7315d2c04f9aebe6c3c32f69ddf406c2c8fd1acf56568762bc0949973bf2c41c4eeb35465b34dd5b09d375d1a6b05793a1a147f28204bb1d28213d545fb33acb3c34c4667d8a81cf9e4e360941d396681ce8c5210314c616b94a0a6035014a00e1ce52c69af5bb82ae2020dd880b24e690a3a7c2ce08ad6565db958a4608a95a21109a54c2985786c202affd1e140b3bb7d6dbd24b6cb3912b8d643f11fcc53572135d09036502a414c51298174e1817268a006ccd74a02d81d118b44013cd98e7af2cf1fcaea9b0e1ef31f989ee05358f5e9b5998d2939524cdfbc63ab9ced220d9b6d2301659d35184e825cedae38139c402408e4673ef66c668004afa14eb33369ede0ccc18f153888ebd8b167fbaf990534be49033142bf1be03db4799a40886acf406fe682fb6e047e3d948f3c89b8fc48d6164aa1108e13b345f9510c8b68b96295135c0dba11c18976ba6e764c881d48c8149d8bb1e51fb0a3c1656b9cd720196ccde00c4857f9ee378b82215688d85c019475f7863b50a3febbcc7f10667dcc7f593b905120090c1b76fad50f9813237b9f38882b51824449cc8d2e2bc0b4aa4f805d31dc62b420e64121ee5f8990c678bbff24f3160f4b64154894eafa214e2878e6468e167e1a816a953a71c2a9eeb0b53281760885f1826ec500c3a5b84a453615cbd9b225ee7b9a3b51be4bfc43fc17d47fa044f58b7e90a9e26e9c945a280ac972ce00dabfb718f2d50d4034d0b87df03d665ec2d9a3ec54d00ca7632e8e6ea88d6212bcd07ed5457211974fd042010ae700348d953289941014e3a046ac3031ff75bb851254f5c44cda346982ae981c75da8176c5c2afe825fa33757429ebec1abe851d3a8ae8313f678019afe831854dd45525121480118bc06fa5e76b29289cdcb57bf6ad6eb09ce2a916579a12593dc9d964c8c1a30f14ffa27be15b4fdc686cb6923c59237fe32271cdbbf1acae07b317678bd369982afd812fc0226cb059a591b0d0e24925c4029870d233630a81f9b1a2fe5b0e105fca1fdda8cf4b137a297c92c4262acbde5323c82332129898f981b7d7580083b9e8bca39c22c56ef56537f9675da5255c4c24061083205c1ba0b6041c8c02227b43164a3235d1ed83d23aa2067c07600c94d66c858d2524356d2ee101670772ac7bdbe40f32bd27e5be0260de9b2e0d549fb0273c31bfa364cfe80456dd33541b223d65a37c69749dec46bcf157b6615c4b7e1318821949d2784a3733eae44e3932ca937487f3c976f0ed89f1456e25870c00313bf1d18d518ee99f6144d8a4aecb18d2199653c42d11d10fa611b0797d773dd7c1155ba9a6808e67093c1721b3b2e23b880100f5d96844b3be8de9270019d096394ec3bc40d4a7312006cfd1e77800888a7f6def898125a4e9cb8a098634a847b0a3038b3523df73f5a9f29e28ecc052ea6159f8aaa41b72a9a35394406262ce10b094b08020186097660a77616e3ed618a3ef7a0c9438b7caa43139284c99201c9650b213635a2f39c0620d45f0f173c8e016c1031f1cf41bc0439af6f12bf23e1b5c6c2ad7be73a570a5c735d727caf37d58a3d478a13388bc06441e99cfc16b37666392edd5ccc911df55c5f15dd5deb75c4ca5de157251304ce00055b24055d05e710eaa9bd61c476ccb9eec149fe7045539ac5f79e3f47ab59ab281d47de19efd78410c2e19a4cb55d22da7604d1b81e125499b0c53d82263b80f91f28bff8346bd897ad9f144de3bed3b58a635d3a242cfb2cfc13dfc958d47a63e67ef0d60d8d5d261a390648ac13674ff2a860d7c86869c80cd3641a5748705dc1169eb01a6a18b8a1960e28cdb9360ff4017b6e02eb27de06aab63ac14e15eaf4c3875bf6060bee5f812c6edb6b08228a37274e1cfd11862dda991e8f20ab91eb786c9700ef4b4ce462683058713017dcb48869dae428fcebc434c1a03a44ddc9e86126a2596a9dbced4bce9dd8c3f775f08c9690ab6a0be60ac964325c5a13294bf9c1349ccb60c05e44fce93aa3b64c617b7b0cd227ed4b613d956522b23694ea7e66604f502bc2ef05b87eff9640070eeb2bc203ea254cba5d6c55e0425e7822ea8d8cf002114b48262b3893a21078d96afd37091e34b1cc7fa1677a20c1233e1520140fa5cc3bb4d6cf82a0b96277a642691575c34ef1c13e25076bd2faf8b210e7d4b2b7cdb455306327214626e6155a2d7a7abc37f0629442db112f8bd779af10427cde6b5c74680a4cda0cf7c92cdce7d08a884efd5432254cd688b2d05474f1168970825c841728d7cf2c33cdc6c7ea10572630e0246396548f3d959ea88b320ae2161afe744a2567309113e7a3a16aae0c118008d158c61bdabe5cc023ffbdb1c267b6c34f0eb13a2fe65c452e91f739b73768b999fe900047ced5bbd2b2127269e851944f1de6d052a108292aa450bff0da3b0e102c7ef792f259aee0202bbbbbec930c91dc826ba6bf4938d02942c47a63ab3a705e67983a4bdc1878b6a10404217cf30a36151f3a9a75d06f55d5fe101c0cdb305823dea48a259501704d6ad60c37e9b409a4d5ac816e08655efb4daf4b185418ff3ec3e2fcad69920574a2de15937f3b35d85c2065a8e50814c53b750976f577e5d41774fb9a0f8bd19fa1e631b90c16a4fc8c3889910c40a149133635623f84579dcac91e72cce2d56196b6d7bd777da7405cb2f6f569121f05226bb823a6ef4ae47c7451dc77c540b85dca09654b8209b7e5db194ad4518d9af62b571a26b3eb4d1f559d9111d8fbe8e51538e011da3771b021d94932edcb679fc7fce2b4fdaaf40fd96bb5f5fcb431b44b53bd822e72cb6be983f51c9f03730d5c92b20dd7a3714033d703f875322435c0a9c039ca9ff8127f2f5199abee8b0569e5833ebfad26347db1f1ce837b58bcd723507e063b18bcbf2f00ff4e811adaa5b35abacd9c2075aba9820d5a03f9582cf06ca729569bd55e2c48f67ca95b657d20c58045e791d1f3ebb4ab41d93b890d140e0530ba65cea4d29cbe8752c432ab734da0f85346694454dbdbf7e10c56695e39a3837e50dd0b3440371c3eef9fbe33d2ab4031f1b2d8149a8973fed5884454be9b4d1136ea1ec83b45b438fbd93ffad7d7c8ed61f649c4acd40954db24794c31cbe9cb6234bfaed428a0155b2fb2c362517be867d5e399a8ad3ce8feda09e8d5768c0e9e09589124afeeca5722fb4266639465b81ce80af28b4b58033432f1fa3c2ffb482460fa61690109cefce2687275a7cfccf3d3b449f1009bef80fd4464e57990d825bab7ec8855a5d278e4567756dc3ea1c0e73b66948ed77c13cf887253575f9963285137cffc75f544cbf21cb8ea633fb6b4cda6cff999ec4a5d12129a1e5c6b7f917c9cdb5dbbabf7481bcd4f6e05aeb0824a80c379c6ebc039487e45657d651d2d59c41f2dcdc160c10910fff9851b34a63fb6f55fd9922d1c9aeeb98fefbd5d144d96d9646f62493d879f25f53a48a89d28f13abf0f4daab1e3386b41ce93f0da146185caf3c60b9723de2627258e58a34841a1b621bcc0e80dad4ada7a25b0f515c10ac3c109398af9f179acd8ab9b2a946bf49be5a782a1766f56f81670a7310d016432df99e45acafa09026f108749274b9ffe8eb173b76cbeefcac50899e494245f9a07caa561c3b6c4d9656e7e8554b945f9863d5892d7597ae8f5ba4a4fdbb5c97d9ba6d681bd49014eb1c935d703977959e95d71dca470e9b2a85e218b1a2ed86815191a2f1595a2b3762f90f95fa941edeb88621225677c1aaf153b8302309ff239055c6974984afc6de1fd93fd799e1cf3a96c2b837fdbb02b29066d643969696487ac0ed178ea08a81e2b3eb620290342e94052458dc79e9152564cd205dc101893aca719fe7103d43e61e6b6039e2ba72c9ac6f450ef834782d94e4f6ab2fe6dc251c655bdb5dcffa3e0b624cea834489975e7e15b9dbe0f5bad450a120c430969421fa3086501c90340e30bec0dab7222bffd3b2da633ce276737a360489c1cd77bd0091350005876eaba9b6e0d79079358a711a3eed86851cebf1c6cd9107effeb2d4e2d9ede96613298b5e1aa1c4ba01fab933db48a23d0747e828c63a0e12985ee0c58ed50795cedb045dea7aa7f15638172ed5a452f930007b74bad33a6305be0bd4be380790fe3ee9ef89fa550592e47d1d5fd27fe43b75f57dd12f8f2157c961defc1fe3e0c002a450ec2c005133bf9cd0cbb49d956de7ecb702556d308b9173bbce216ccfce180d174c5a58ae95aa9c984a11360d90bb8d744206942f986f7a92c053a1004445eeb936bc401ba97153270c140119944e7bc604d0c33867320142398f2d8257f5cba7a35963412cc60b7439e0786dd1535ced3fcadbede4131eabe34535d3e811a6ab726a67e38765c8caadf35e30625375067d0fc0d4ee43d8fe045cca161639ffaa29b8de8130f4ed4e64b6ecd62065a3242f17cac45aa1c250255a20e0588959fae4ee6ba48fbf7c1a1b8c9f398059c217ccaa711643a810ac857219477a8a9aa1e38c0e86cf0a419f68ff0ee4d324906d658e31303c456d3e334792bd0f31be40eeebe8aae58de6ece783a4584b5ddbda73c55a79ef9e1a638f22def020f287b2a63c4dc99ac367079029cba1d25834f59b183085b51a12533189140f6062aa1baf291ba9e3fe711a75d71f2cb61d5c06b6893403e09ca558a9773802115633a903e43b937a62532766617d1f78c8be23680e3c27a66224c639f57118bceeba7c26c1b8d9e53e50af3933f23e5a134a4d938869ea5313496a1b87333fcf9098c01707ec3d17d80fb38ef72f30a72096f5815c3d7df439e272426ef95b3cbd2d2decb798017d072a15bf1892c6627056d84a4f26803165ee1f5200a16765eac1d124cb3350c96ee774af212dfdbcf02c5e08cdc5a92f5a65535938b3edf545c283f2d8125a5bdcf93f3b145629a5afe1fd1c1e590b66653f86681a901169ce1aa1ea55849b7b368ce9d09dd6d6dc1539259f7a4bf7cfd530aa9061907aac35420896a76ec637c1f179c67b49c94609f8365f4855994c97df1c5f5fbf09e582722a5d854b846cd14b2a8616e27ac935a225bb60372844e5cbba6bc759fc83f62711064291eaec90223b442e1fc4622c95e26cefe464cb57ee50d9e27a19353081bdd1e85eaafd9302c1af5f5e56db81d69bfa8d51488bad37b20bafbf48beca2a6adc8c0b59d2dc4cbc683101fd150e20f121ef9b81083a29ca3f6bce7e03b8be5919861e9f01cced75708d546c3af552d8a6eba0d35353ceebb45af7413c5a64f44973bac1037cb908a41452ec8052b66d73ae67190b182ff368733dc7e322874fcfda2f062024c626731ca669c69baadfd028e23fb57c868321677be59499cfc2c1bdf7f3290e60b55c2ec63908b4686cee8f4b38e62ae65c9a9b8c0be15ae95a86241d52cc64ba671df36427446086dc17af12305d0377acfd1da793f9041484ee04c4f84f9754b32edd4a80649c9bc131cabd53cb17437920ed2226593786baabc0af4286466b3ee0311bf0cdcf8ba6e2d0d98726110ae81657156e91d3dd14d274fc8a0e8a69eea26cbdc2d7034625ca615e4ec2c81b4845a347b5aad87c1e21bd1af7071ee05dd9849b002aa50024417e04cd10dcedb430eab0dcb6c23aab2058eb88ccfc60037515ec56036996b84b62825878a78b4e0f2dfec4756cb514516d09b8b7aa3e777fdb2738e095bd21179661b992cdc99e0d7ade0245a39649d2da4de4b7f48ba18fe54222ee67acca9c150e532c7d94434f581d92927cd783cd8fcef8287103820b280e0123bbaaf0257af85086718eb6ebbe628f06815a0a1389814b424b91d22206fc87b01ccd1f824c75f47c0aa115392f42ea8383d0c6943e5a1d84356aebbf71caa3c9af4e6017e89735c71b491a7937307422da745e7660ec09007b628790d372ecfae0d6651c16e3647eec9c8c59275dfa8192ab13456ebc9a098ea28699569d038bca852f25e0657021a808d3f38806551c7d178527dc2bcfba59314a6e4f71caee12c169bff636a4b0fef307939e545a127d758e6297b6b02e9a78dfa9d679974eb0f24c332feab9e0e48b64416a092525dcbc55055e55c795dd19b0916ad78c7deb9adf8ba370113fa4ca839024d7e4dcebae8055e6053e3618b10e96bb9d07f1541b36c3b722eb9a5c604671831a48333da4cb5a6afc0e96f30cf5dff04f4fe82148408ee77b99efb7745059b819d49de61f90a2fe264376626c18c7b5b2eae61c234575d87fc8c996b87069e99a6ecbd6ec5bd693dedcd69eabe04da76f4c88d698a2e34287d2f742b94b920b4b192aca0792ab7ca36d8bfb84bb7d4360fd2de21b83fb35b7c0e908b2af9158631f5da2e0799079e0eb68c6f06cef702057301e09eb75320463de4253a62e163f2749e931ad6b8215ca5052c67c57dce6d91eb2d0e4fd7563c4cac58eb773e7c3073c83161045a93550b0f9364edc958d425005410b2eb81682cf31d00cb2fd39dc13e1b1b304685d53ee66153eb34505effd9cf3413d134d39c1ce68f8d057f77120df7be2f8cea61902c7b53819247941a162401b750967ca9d01bab8561d1b70234c39224fa7991a1bc48c1b39a077fd3644e6b13691a34119461698722c9148eeeb387f2f4ffecdc2f039475e56e42f5731145eda830355ab998f0bc698af82fe3df03390690dfdd77dca45efe481cfffbc4155fd8450d8a7378908631ae6cce4aac8828675655bc9459d4dbab533b30d55ff2d8444cdf4bfc79b62c9b444a49123884ae1f4e800ae0adf2e087c8c77e095a8156247203299fde0ff0186cb793d21df2494f3b4d5763eb78dc53ab2da1ed626c3f229813d7d867e9de09eb0bde1616df1c45e2beeb42bc3e2284d2e520631f16d4dfe325eabc0de7daf4ede2ea928d72a85015ed17e4973c72667476beb03b391c50009c434ff9c5f14a351c306af26cee6c1ed227a74c9a4c5b814c9d72041727fb1b01592d7db2ceaa8fae62633eeb17979edf860ce7956790de2a56ee828f8a61cc858338526ebe4c2d3d4baf1d42feac37c12df00c7b51b2b9b6ce2ea669ac5c4a14f3a3370a699cea937c8ec342b95dcb34e0e4a796737a9b266f4941efa455c5d29e0139292d2f4fee9d00158f7a9bda123a1f2ec646d0cf95500ece29f2043c4fb36fda2f14a7d8e1d16d057f8cc7ff5468ca331c458963a8b0bd443815df07afe684d93c873df63f16529ed204b12735946395701139e48310b5e75945b4178346f7ad1525fac62180f1937c067959f6e604494a89032925b13ba47ba16b4d0a7cb57ad07692338b7f594c666a09e5d4e5f5abe4f7faf7e427e93eaab110ffab9270cc36343e6f21987ac8d02c33ec16842f585ef211d086bb49bdff3b13aab3503a09422b08585130ce02ef883f5d21818109f73959d9d43eabd319c92e01e73889beb32321869c3b3ca16962378d27b7b845b72a0870be22666e6f7d02fee0ba03cc70bee45824cf004b778d10120c318c11a90d5f8ae9f4c8902bdb6ae428b57847f21e6957c6c79f0fed966e8519f782cfa3d6ecfec6ddd771e8a716cb63b0f5b2db5ef417aff39d327ddbd2cbded5fc7f40973999efce6e31d9463b23903c204ae86f56564a0eb6475f651287cd71dde4d8af5fdc830a2948a249b35ec8587d174e9f630b36beb3458574b6a0e8ffb12c3cdebe329d0ddb5df31a339082def9134ac9c3f102676737ecc759bc90df35a213125dfc7c156e5d3c3be2c45c196c55838e6602d5429ab119335b02ed15198b56d1acd8149a15b829b331f4764d7175b50547a86f11746f59afc13b8aa2a0abfe0e51fe2a15fe5c69abcd45b5ca7dd1f6f21c30e199fe7489cf087b7d7bfd8a4f6cb89e6ca0a2c191c48cc074c493838be0d69416743ec3173ab92ee9be624221ebd098dcd3fc9307545cd7467d9eb0827e39ce427f79f493e7eb881202ebbfb5640b7a7761556e74f069c5127a213d039136d8242dcfafc427b402a4120d3b3098ebd8c991523a4961b0f91bd2691d13d8159accdaa4623a21e2ff184ace783e200e4c916e627f768eab0d204580e3e1ed662090a99f81f4dcca06a7dcc83461533e46406e015d5ba8cdd94aea4cc10538b3ae5e5181dcd4b7af4cd7705a72bd644ebe3150f4df1631804d07819bb5df3146f9365d4079a9b78d9718dad7cb7695cc1a80110a0e9ce4f087607873b5bd2d40b308f5c99ad9dbdad7c4dfd91a3aa6e906236c24c2481376f08b3d97f4abdab5f00b7db802b2338699b0de8bb3b217a731745c9038fa4f810f0517096c58368d7814093cdd6e54191fc09d4d0c4ecd726e692d643df474f3baefee869f145d93ba19820ce7d65e7be53b1187f8d3e9db991515369e14abf91beac0aec23cfce0c89eab2906891989cab6823064637251544c008f41a28c53567d361c4f39774fed8e631aacb5aaa7543a380370e675681fc01eb88ce66824d122c45369c8ab4064154e846c2651c311a6c07d5cfb3e177b6b2712b6b4638f1d1d4ac894c652253a23bfe8db3f2aa50db09848413dcfc06bdcfd4c9ca688f6706bd2cf7f034374d6e7cf5d68dad450ef9d3e801766f2296dff7df40d7889553dcd6d195afc06fe720924b01e39a6e768e9f95303cfdd39291ff386824e3cba0cb8dc5ca77642f7258a99cea563139f1640cb42897fa9c7ca1f9e578f64b1862b117aeb552c2e0a3bb1cf7b3e49a72f66d75a27c132488d1a0caf4e70f26abdfcc3a2034cd2a209867fa317b3ecc065b7cb0a9bcfba42c6d613338a06949df697171a92ceaf572314289cb14f92d2db49a8590d4d2760444c15d01b1d78571a30ffb65466400947514d24a07cc5c01479ed7c3b9f234de2dd707b04c6176d2de0dad20234ea8a1731fb5b53e31ec4e9627cdedbad6da0dacc62c8374785f11b64ef420acc62441b99280d971810ba0e1fb11ec697aa2fb7251175ff83d53714b7529c8760e688f853391f9998a4ece3e16709d897a45c675b0101d127ef2c9b036c723b524f4fad50d767a279f019a54285fa648ede6a2814670ad29e482b56af3632fdaa88d08609aa8a5491ce4f07d80429d24084c35478c538ebcd759e22ea50b214671007a0d03c52cbecf183c13824b1f42889c3fc01216fa2493fed234b2e883156eb1d3ab4b0f9d33a57ddc4e7f8e50796e3fa60e295183da3cc2e7377494fea9b8c40fe0b938b4ee1810d861f8de481e58ef5bbce7e637c1e2d5700c9f9bf438238ba9a476290cc51f53beea11a4ebab5b8d3bb4dc0c10433dc90b5a1067d634e8646a68992804522a915b4b2bc41bca4bf4e8956a2129e9b88c8ed1b77964255d02e45cf040421b655a0127064f8a9dac384c220ed4a1e13d1cb284f1d8a22ac0461c32080eda69dbc0328e4c0160bdbe9f485a299e18e3aaf8db2b2d5b64a094e9f62f6c671c321b1b510c8e8ac2c4f52c5d39cb1268ac20590f0e236e4295360b4063dd6e43983d9c18311778ac99b95f7597b0085ca831b57c0a4488076dbc47f8e06037ddd6efcda412cb0aa25625299914495f86649faf7f61260479fb565cf8c9585656886dfc6c8b0a92098320e8265c42d8a4ca9ed8d38a5267883f07808e30106bda6eba82885559018e21bd99ed90307a0edc65e45f0ec8c9b113dd0cfa2c32816a1294add8b448b12aa50c879ed4be94740f8cccfdc5c5938e6fd0717cc23a4e742726ffe908499679720da89e37c6623c4123b68045b586bd2be2bf8bce758303ca4f71348c45cbd9c2b5078db867d3a6f2900ad80a89272d38c5d40459e69c9c01b9c203237af3f2b36053cdf67b482116051d1ed31607407bd0eb79ed6b733272d35e05183b8835722af6754867a193f7b23daa04b02acf4119d8175048507a7ba725a830c9eb4b566a863711a11b6c80b42190f40f81adad56ab767f2c5be39c79c7df555481572430ba708c622cee7ff94713ac5d33b1f898be15df5b7705a1416fb4d7a19d298372f79e0df22146fef6f285cc1ea68becf6748f08bddea789170cdb4e052037e4d585bdd85fddcb2ccbf1c6a63f6b04368d016fbad9f4728ffa3f1d3ff9ef0e0ef8e1ebf1cc130513a57d0854c18042bce9c8f3a1b53b27bf493cad8ed4258ed194a93a30ba67c5687f06bd39c52937a340260b90cf6b258d97ef402c1883839ef30ce83a2922aac1f118485577a78c7e4f66e75e9f2e8b6b525adbcb5bbd3658cb702737a2101d5250c5fb99621d3cb8d2ac8a826690f81bb453d340416755a39261531ae374d6b1e6b693bf3ae4c3aa161586166184ea50422e23c188bebdc3b30942a021ee9764529add7912839732668ae1b700d79994aa20660534716707c9bccbca9e510525dae1d3207f0c4a84bf971f5265e4bba1781b9acb8f12df03fba6683afe245e5bc7a33f7b46fcfd085a6e9a09a3c9bd60dd5d881c095eaac14479ee981e7c1aeb0b7567a9f61f8d2a24145e1d34d4794edbf39117bdf429e0e07d4386b577a8379f8a330ec16913eebc809738536dce35a780acf2318e14ed845142d2bc67d2b9e1ac403f53c7775ad7c0d15213cbca5989df9906537dd78a4e7a599fcb22e1c4300032ccc72be66b31949096cc470ea90ab2abf414f142b1097714e47eb742c129806be3d6eb05d9072ac37595c833f84db02ae2766fd8451e5a250f386e1b51326c50b4611cb68999020282fc5b3896757c125ee5810fc3d889fc2882beeabdebc7a78ebaa92e1909d079bdd105ae11ee6e9c9ab3ded24e56543e246cfc647bd081c7edd7600b77fb62b5e304fa09319334e0ed8db70205c1d0872efd22c5494dcc9fe74a9aa1848cce786110a9381464b5ddf20e84c3a7851613298a5b2c4b8e997380cf7e9f8a40923e61ec70d26764dc62c2e0833ca9be0c1d9fcfeabb8c96161c828c6644ed458e7924a02bbcb00e61f87dc839aaeff0a84ab094651a0274389d129db13a4c62c663a7678b8bc7ea17ae07d691b8e43e6d82c70d49b1dadfecb665c818ab769107f91886628064ee75cc28e86a0def5b66aca7b623954d6c5145f72da091b3b5fc9b61a1fc1944fa850b230cc7010f5b31e12848444f2bf988ee071a17beed9f3b000f43f354e5ba2c0b79f7d96768914ec189ffa1ffcc3453a32e2984361261032acc306b897aa18a2e9e19662516a9a80c73bb1915b62d8c9d25e299e9405c8ceddfa8c3f5675040d3d3b6034f512934edfc14fd7b495d5636676bf14023768461ebc99fb7e4f3e0da381169520b8d5bca460afd18ecefe5a621522a52dcb687dbcc40edb90a2b4e09a9ade98d60f8894e06b9d01bf8cc9f9467bf05471790339c181d7896eabba13df40e4aceca1d124da1c4e6d95efe12fa32611d0dc15c280b18c4450763cfa490a221f93522f35d8ba43619870c64c36ca0435bf68f04282d686bcaf953f5d9a93b287b3b7a625ed8bcee81492fd412a187dd6c3702404ba8166bcc6d54aff670d201aa46a2df27c191632ed0eae96c633175a1820e41f26c8a9f1f18d9e6a91a86a6be6ce42f93f1480e5e4617858c11abfcdd7038e33d62bcd5df068413bf56879cf4c366f47caae116892d45cbf0805e7e92e110e36b241ee83c98b084bc6be9bdde1cd5173b6e8db2e54e00b6830203bb20d771e133a87c546f4c657eb7a0d278df54985850ff0de9fc148f3c2a5643ff68407eb017cbfc9a3632083ea7f14be7c967cc042114bf2e46b628adb0222444693e5568f893ee6f5dbf7011c671af7210e90cba1d7004bfa99d30f67f6a058e179a9393faa15f50952963fb8dc62199d6dc8d5c2e079e76ecd514d65244e4db244203ae984c05a9725f088cab364be0ce45e7f5b37b87af8457a03f3050dd95617d59f8d176db18f20c2894ce5f51ebcbd7770bd68970184a11e00113b34d40c0181c4f1eb8420d859f0c620340ade5ce6ebda57319cfe813f13827467cc8a4ac527b81ef5b4313b2ee8364d92228318a3693aa4b0d38147b957e6f25d4527a14ed12264bc6385a239e33d1d768e20badebe75d9896e63c9d4123e66b6220b45b6012cd2dc49a8c63dfabd4df22b25f58149b61743552648838c78a911e2ae752d4e8d9d88d6007a98869e2363227800547868d070e09c404879cbdbf55df50ff823ee5615aea98bcf2ecbbf770b996a55f0a37933b5ad5c6df28a5d83b7a87e91d3a369a1edfc96aaa3f8e914ba1beaf2625740082cfec46d052135a477f2522544172da1623f1b0ebab5d2e33c4425535dc43300e2258d1cf6f28b1c5971a44d7a1aa825dfebc33815c75a20198fc59dc45f9c16d97bb4473373be6f2eed6a04ff6d4ed2d238ab5d6a16bcd29dae486c2de864bee0c3faf42c4a81a4147b9ce1db21dfa4a98ab9877b81de620ffaac73ec1877e75dca2f52421266f8dd500cb02bdbc6c88b15ca3ef78831cd04a8ec8ccab0e63c6e7c30633e9a5aa1c9f1a3af1281d05abd1f8d286cd594dfa7782dcd6fbba134275dc56cc4e35c36a3db454a26733c69614949db812863fe5ed019e0dd829895b3c5184b71159a35ccbd7527b50b5a64e757be956e900eeb6e17ed5b12cb21e4e4c23ce32390d27d17b40f822173f0e032292c37d8540fae6401170d1a97e734e9599124366719f08a35e5194ca0fc01d2e7ad5efe7ed769179e9d5a03f9a3a79361b35add8738387364f2bf78291c2819ccd1fcd769c02ccc30c5e2a578081c2af18c18e0ecf65150a0bfaf853a2ac3d13b9441fee994d616c6b203c267088c337f3c54d909eebd7a1a88b4d9963ea165822044a8a168b21340629b28494314fb85e27f55c7130b0d671888cb6b0e94c597731feae0be76a476bf05698ac530f32e9e870cdef94bc9ef5943fe97c6b3b945708683384d5b0a619d1bb1f55df3a0cde26276649e06a9ba3be91d3e18b98994cfced4f8aa65373dcf63310c2a9a758c19af66c921092ba0fd0b9fc2bbce37e9cc4ad98cc29a406c3f897813e88056670651b391289d5d363a1aaef48e5589174c83803e967040e8db930f21246083404ad15793a18a1a531042ad9a8df7d062ce466219fc7b11c5e5af96536bc5a19fd384997140d2a353a2df214684542c6403dda68d3df1d2fba0ae17b63403dd06fff7a0b70b5fcb55a0a3264560ddedd051d4aa42024e5f2dfa000ab140240e242a49031f17c36f2a40b3c9162800bff29907f8501b652ecfecc94c0c089c769a7af6606e98bb877e71076ccc5b9920a9898399511eb553a608d791bfaa903a3ab993715147d8dc8d1b30c3b184a5524f79d2504928621897966861477cc6591868814924e1eaa2eaffeafb12322161178e24d20d355f1e0cfa2e07c00a0ad92d1092d91e573defdee1f3aa2ae7653fed245db5f6b2e3fc2fa45618813f155fcd13937a1997e2800d2e6f2882fa9903c720f51ce1413dc4eedc4db119d406899dbc6f5904972254ee9ac813a4563c353e832a193530e0e3f047bb271a2157494d0fe6ebcb344e78eb85e8ae750c919041e55cb09fd42fd6f4e6946301d9cd7563d79cd60df6f716405351dd75874c85d646facfcdbde084327f883b36a82ae80ebdef8a4ac812d3ec70e287acf340e87d584acc809ea60a9552132c67fddef9bd398b9d7fe8b739bed1546c372476e781b17c220fb05fd578be04f886b16294730c410e5c9110294c4d6e08ce4444f59130c6d5050bb1f660ab1986455049c8b330570f812014b4b8387c34e98857a4c8da8a0acc3ba44b0cef081232b93fa9c900bd5237b16c79f7bada4418b69bfdf9f0358a1ab2a9c726206ab5712a755556ad0e99f92be4393f1f42068a78d93b55d556a0e489c1755f83d4a789840862a46e79bdd1d29b9e4712583415d30db571dd3b9e3e9c019f64aa131d26435d794001e04a4679e62b14aa57f8ae3d041914a803d9793500e6c6a29f852c51ecbc4ac8f1e65001869e15476bb547e3c9f414b2fb96444d6fc830048429c5d509ceae3433062b08f8f9f82df7b66940e38af7a0e27717e2f2eb7e514dd280492ee8e65cfb3064f67cfdc00ae06ad23e00ce54282d29d1f13270de99dcfdfc0202bf0a56aa2aeb650aa6f64958d5e6d22551ba8ea03bdca40afb6e8ab37caea85a47ad0546f04a8bed62c6c80ff4a526d47085340e2c02715c04af6c6eacdc802e30a7e4ce369d493dd7a32bb98ccaa243b85647645b25d4eb68b93cdb264a3967c54271fe5c9b638b95d4c7615929942b2bb90fcae93db7594ecfe095d428dd7ea2242e37ccd1c56f1921ff754142c3266bf6f59d7b067bf5244b978b28f391b41cc601c48d8cf8bafc6d21462e7407c5c4a2184b7bce0d5cdb69e7fd27918cae6545ee69795670a89a44c492e38ca3c17f7ca915b56b8a86fa5b5a825faa7bc600856df80a00b620f94d7d456df655a26c55e362ec3cbdd368731ea7c30558e1155a7c3fcba86ab3d487a9f011e6af08fed1ff297a9e6b75d8b14bc465480d85908722534c84fde1d2147c921e5ea3f40cc1063717a4548fae758c15723c5eb2fa03226a9c34110279cc056a2c3da69fdc75d0bd382a1b2015ba3d4df3028641c9da4cab8eff65dfe73394d3c751f20da4d49aef189dd5ce0dfb779a9c1ce6868ed9a65f5fdc62b24930126adf0ad70f471b2280eb156f67bb347c0aa945761cc57a701fbfb14c43463c430104a6e3c16c45c84754c4e1dff2968dfd5669ccca98d5d63bbe3e1726ad79857c1e75483e1e2964cabe2ff0f8bc3e4769f957a567ee5fcf3fb99d7a18c5189fa6e0b28f904b8b10d7b54c73e7de9aad54e9ee72c19d1ae1bfb630015b9e82313fd750c26756d41db8bd8691872151026936c112167dad0487a7af3cc81f517214e901686f6bd3e301b0eedeb547a6a2da8f21919a01f654689dfd0229a23ce1190958090dd0f71ced0324cf597b9bfad087702a5f0a4ea942ddab1b4139a264fcf697bc880739eef43f318d8f2885a05bdaa362e120cc3047599fbb458f36d78b5c0b95629ff1cdd736e84c453682ddb312a58a19959e5254c500495a1e656ef8f7b5fc61138d42fcdc5be450d075ea82e078d393b2e21908443a2011eca66ac060dd7a2f6a7a95022759b8cbba16f97eae15992d69bfed3ab9828536b8c5984f920c8d5ae6e83ca5c33063c88d1403bee74531ad8ec75a6833b77e40c8ca9b629f4cd5cb226ee019d41fea7d244efba3d296728a2991881e2c1a5462e9fdcea98f192b5694375c6017f29e45f5bc6ae8443761f21f440b9eb7d6c752f59c6928bd4ab4b94a3438cdf890b64f3ed517f4e507f8180bd3bd67875a92cb15c8e93cb7475b18160efd31847ee79a6cba1072cc91c977a0c83139f88b7a92e45c01d3215ea19ddb5b210bd4bf3687501f8493989c8d193c7c62de95b87b20c6d39a5278a347c6876d91bb999b48d1bb8b9c5dd824ce2b64c734be5e1fed16b09e24c6d98db290dd217d0b36b8bdc2c148ee0dcc964a79b3497030572f2e8caca5432eccae7907c497ab13b92a1acc6f66849a8e9bd78a65528569f3dea4e5a85e36971945f2c36526c69e2e0cd39a77f19619e3dfca56a13e03eac961b9e1e8625aaffe737abe722ec603b37d790bfd494f42b06a65d88050c1b7996617ac0b792b1baa11f1e31ab8cacbe89287324da078b7b1dad55eacc31f40741bbfe634e832e24c83a8122a60ec24e1ab4432104d9e5e049b41ff5e9e5aadbc456a204cac07e80b880306fbb30ec252eb0dcf97484307d0caf3e3e6594bb2b4f6db6c7912ecfe83cfae957a50039f431ad201ca2e748906c215a7f769c14c545b2ff03766d5df98c2326dda4be41f06403112080ff0e3d9a358b299850834ae6fbe0566615174a202cf3eaba7a038539ad653636dbfbd1d1a5d253c844cc82b96214aedb622e5445f38e297a0bf8637190fa111425925b74ae7f99c67ec29657178ceab2184c1403aa19226d5d4014564d07e9fde9fbb9bd1f05f29cd6ce78fbe5a72ffc9a3aa129f2e708562f7a96a85f4385da38cffb33c861b0b3f7c1601c30ef7cc0c28b7c06d5faf1320179092fcc1c347fa009b637b3cc309683458cb2dbde7b6f29a594294919940675061e07f7b520b2993604871b1090b7648e4da801f5553e7a0da08f41188c7c75dfa65550301c2dc1a1496833017ee9a1decd4b4f9cb9bf92030e6566323399a2e8eafebe8c162f632433c33827125ed1d5f93e0c526038c23cb8e7845fc94e290698a507869de8fa7c9f00a41f38c43c4cc8f2e54837602933858c93ef3f949b94882a5350290a8b1226a03873fd1b2c19c599fb2816a038b3e5008391c4d134e4fb7406a522871b10e74326bd3037e3002691090bf27de9e9c12b5f8e897fe822e366e4ee4c42f95e8f7403a63871064964c9ccde4528bab697b122aaee872e497228038c7c5fc65174cd7fe93921c9f74f42d1f5fd8622fd87da5e66d646e4fbf765ac30926f15241acb910910f97ee822cb318a28f97ef8d293ef7f280618c599fee6b74dae3b21dfd70090ef871828dfc73cf7628c79f2f54c429784211c8f4463486628cedc5945be1fcafcc8f7655ed1f5fd3522df0fc129f27df0becc5074f9df90c650beefa00378e2ccfd9410f0bc1f38cbf73ec963f9beb7cab7074bc027ba68e40bc551be0f8391b77aeb40195c90f75d402173a010f84f278c2f0c525c4a8061386ad5fd0d84c1a855f73550f302f787110aa3fc04f6e2050e1c3870e0c08103078e1baf1b39375e3784dc80c5907c48299bda80c30f16fb86348bd6fa3386a24b95a750c65dabe8d0ec2f27cfcf69d64b6c95cc538617d8822f8036da14a7f3b72770f8c1668871a95b1f18de1cc2e4f9291e1c7eb03c9f8419e0f0837db066b5cece10176226d2acaf48b3eccfff62cd7a911f3f23cdaa1f7eb01f20176250fe84be24797eb23c3bfc966419626256086944facc1cf2e83aa76141cc1cf28853382751a84319cb210ffa9a376037726ebc6e08b901bb01b3810bb09c54d29026fbf81c31e223a4b4c50c6ded0e6b6d9782b8cd39c1189de4f8f7892218f2286d6088f3bd2b3378aed65a1b39fb3586ed93a38d315a5bedac94c64a69143d8367fb9488e8c96cc71322ba84bbbbbb67c3e68181e5dbd73490b5c8c1344edb60dbf6178cb286f614d539f7de7b2f4a8699bb3bcac3944f828b21c86c70aa0116c508aa19d0155266d475a4943e826e87f5cc47e03aae2304d79991a025e521e33af30838fc1f8a71de1cfef4a3ddc6df51dea82e53ba514a2b7d8dd264eae373c4483c227d7c3669a3f8ecd858818519e6118923661f283188f049ec209a11113d99ed782289884c4004834b3fe0120c2e3d219a43c0e1bfd0bcd1070eff93000214f992393eb060d3421786d0e925eb9f46a27d1a89fee99f1f1e542dbc90001d303b7e8a8ac084f48ab2c0e1a3d0ff45971442be1ad6b016d2421ad644c3667487165e48808e530b8145c1a14a2582d917f2a0b1ecdf92524a298e9ddaf4250a874e963f270e58ab248e1e640d4e6a00c74eab6cbc7bba4cc18604cbbf7f35d03e6775e8dc1112b3286d5e4a414ae9bdf75e4d7b3a357b5f4cbcb42f72a4d704cf177e4e10e6149b758ae1cd708cf91ee30b14ed6e4a6b9d3dc81af51de6df71d63a77c21eb976bff42888019b9b3eb9463dd044570d79d064ff19041cc6a23ee9700a8147bb1f10f1804726933932877e4b203247e6444dd612c81c59b36074702893fb25955d86e98a805f7a8143954a750435634b621949f077edd9ddef0e62d0b1c4ee2edb9d085c9443c03168063939061d29924bc83108899ecc7df5e547806886f9b8148122ea1e03c843ca192dc05c4e0c7a61476748ee3701c1a1847994337e945a705126e62a701887401b3878fb300aa970fa277e7c411ae9966ada347e9b7ea159dff727a059a4f73a1afccd6af9e8cd2b1fbf5f79cea377b372801ed9419be67029ec915b50a58f9e4aadf55d88b9822fa6b7407dfa158ca6394ba830b27cf4bc477616fc13ff0a2abc40c66fe32a1faa7482e96c7c43859daacb9124bb73d2af3f5132fbf409525429cf4ec6ae0fdac6755e84399d3a8ed4258cb96d5269abdb22aacfba69b3a70815eb4384399d6a9730aed637ed6e8eba16d53e3d8b16dc40233bc29c4eb54b187bbe5d6c59d7b428995ef5acd4dddfd3228dd33bc638b3e00e1023e83d81e54715b817f9a18efc22411dfd1fb3fd0dc440fcb07e063c0bc181fe986f0dfd1ee89123d4c0f0367d0a7abf8f1e391af4319f822006e2574d47b6f3252347833ffabdfb3dbabf0f8c7d15df870f1f600782d93347c6d953477b0790e08bf49ec0343dc4a594dca749a95139a5d4bc8f700788394a19a3acd1658e1e93fe89eeeeee34d25db674d92ea5e4640eff3871e964376e4a6badb5afd9d7de825a8f2c51fd94ca6e8f7bc93d45851ee0341963fbe6beb977bb3b6dedb9abb96bafe5b66edf03bd9a3f3f9e0dfc7c25bfd8276bd5fc96b46abafb37f47db24f26cb53fe37f396ac01ce578483738ae4f9f1e8e4c92384647a30257472f81de529f493436c037ee5896179e29d3cbf52c9d3acf083c13e59b3e62bdf90c34eba30b3dc74901f6eb37cc37c1edd3284b0cc94e26441ba01c7202676f20c9c3c411a38db0fb0d8799d176ddaebb48de3388edbb627f0269b7a92e6d0d78be6dc1cece7d0752f077073e83877fa8aaef81ea3af19bbb01f13a594529ff26d9a52225a44675e6059f4cae996fdfe524f74a590fb7b458bf0d0190f9d650a44797a663c72894d9f66d80b1c9eae0987062cf7e31c93b9f6d953c6e9d392e6dbf49739efb61cf0f67448e290af6ffb8cbea3cfc8a859534ab38e764c43bac54517cdcf946e5122bdeaefd26744bb9b0a7d4642cdc2be79eb45079763035f34ff8c8ceece4e9763134548922718629b3f29dfd16764841bd3b045968865fb34804c38f99ee01df704b655d6aad49256d9970d8529157c4c5460955f49cdbe1fb2fdd667e4ad94cc573635cbf65776c0b155322553ada8c8b6b79f5a625d76f2e632644b0d215b195b29a3b8b2bffd06247134117953c99bc906d32b6fdb4b08b1954a85b1d100fabeeffb4824cfdb9064c9f2d2f4b2f4f237f92129ca8ff2b5e8c22b2fdf4d38d984baf85eef2615d42afb31c699a3ccc1a588284d15b5cabe4d9b50353ba51a9e6c6b601b0f0e53b3d42c45647d8849f2a68324cbc8b2cc922c6584b21c024696a9a32c4310490685b20c6920c9f26900459797250d21179f2c5d80b29411cbf643532c4b534fcac857f643134f0e4d42b294998064f91b50746d59b2e40f4d3fb2acf1a1061657569a60599a76b2fc9a1d624d09b24d019d80935346deaa81f92a06a855f6694a96ed7335306fc9e0f1956d568ccfcd618c4fb61fcefcbd4db380d15735c503945924eeedcbbc302a1525cea48e7c653fc6a759a92e649438525fc81af6c3989f02d8fc81292313608a93c3124f4d8fb762807c656b74882eecad95b026962dfe3eacf1c9f657505705157dc600d1b732aa681823946bc6aa4c41faf2b9df019350f287f033ca5606959fe16621569e7b02df1c3307daf40aaa3bd20bf773eeb93023df18b76ddb36da2a0ec54599a34c49e9954d19b52a65d4aa949456c9e04951816b789a15cae0a919e252a707a002abb12f83c7ca4da77d18b26d91c38e04d93ed7a138778f01f250dc775393dcd397e6135ddcdbdf7e44566ab6bda24bbefd1859292ba20df4150344c37d4597f63248fb9c4475afa1e4a766cda2344455bffd940db9df3650d75222db0f4b3cd9b274c8f6c318a06c5f43b16071c6cae0a981c519fbcda58480b59741e6306594eda78ca26b7bcbf56b32db1a58b61fc6a16c2fd8028a33f6e58735b0256239a640407b6c3d83072edac0d323784fad8a57a301f60fbbec9ea74651b3d2596705bd8c5b8badc674d923e5d8accd67cb19a725dd4f3549307c439a5c9fc65bfeb5d55fff05961e4c87a893271d45578d5c5132bdbaacedebafc042b7ba5ed5af9526d7fa214c0e69be7e67031cd2e4fa3436acbffd93b764d13523dba73dcdf26c51c840f9da6ceb0a34adaa21cd27a3262eb07ccf0ae0e4479930c66b55a95593a728ba6aa20e9a6b448fc28ab343c790fe6f48b3be513a76b48a24730cbd4347abeee5f18ae4d94d29a5f4eb69d5fc62797e445a356538fc7888c49f58603a5356604f8744c5ac23b504df8fe7e3f1166e9027cf6fcf85f0ebc953524c714addfa184b80381d70dcc7915e38bca4178679eeeeeeeddfd9c1d001d2dddddd65cbe850384e76bb5fc145191b51ca76c73e492f5c79508023184ece3dc65377bbb777935ed841fffc0581bf20dabd414aa9b5f6deab695a8c1ce779f192eaf5b8707732f4b81414df6bd28d728f495cb650c6254da3023b1876dbc6715d4f1deeeef9d57d3678ca625eeb3300397e37a54f991d75a2333a40a1ebf209397e8c2538ecb2ec6eb35639befff47929df91efc877e43bf21d7902cb972b404c26bd482f1cd2e49b83b5e7a247d993b471270ea6bd9bfb7e63cf2927e780a366914e307d81499f39e7e6ece6dc70911a8be5fb46f26d5ca455b7ad9596925e0c501d90714fabeecf8e840386010c195982ed587a502f12a35e2428047dfc556ad1a5bd7c9250746d2897215fdd6f94cbcc573662a96c2366c387d3ba46c51c18157370241cf07c1210ccd28355fefe0270bc45fffe025edeaa7f7f010b58c00fd1a53d98afcb500be0be448524c050b9cef2fd7629caf7498091b0d31e0c9d83822e325f05b30a50a088067bebc357155dd5e5608e8f51271c49f1051b05e8903d60470a9d165d1295e34b948c17092c5b85ff7b313e9d6ae46ad4aa292e57d3d15774551ad3e7d6225fddcafd608a9d1ce4764eb6d6298baef9d2a9ad3159467b720ce71339be29e62d171f5ff587a658688af95c179febe2f2837201f26e28e8e223c50e3d30cd498c162d31455560f9a18b8f8b4f8b8b8f8b4f744501ccb817d7a25af44500c8b528089f62906a51517445201b3278971bf56f1d275f4d77e7dc638c119c53669a698e602a0abe61b70c74fae4f13243f95f8cf27b649941207b78773beeeebe494d258b7bdddd567777a7d54484259d1813c1f2a75ff74904968f3b9c022c1f178123d729e9e964ce3967fd3ed38cf9320335419912563300d6caa46aa4b025e0051045e363bc9b79820a50962c8961c7eb024533a3d705882aaa8556f5c7783633605c5e54a8a9993666103c657837d38600280bad921474912180e8b4c65affc54340078060c9ea628c73b6de08956a05073c6b56f0128ed1e2ad04297937d4e4ad046139b931a9bc33d07e60f92bde4a10ecad04c173de98d33573307def86067191bc2094524ae99c73ce49e99c73ced9d3d33c1697934b4caa841597659ce9133a140adbd3888199f142c20c9d57a8a60764d0991b801a3eb0b778375e838603d6d2a440e9ac1fc38a18decdfc16efa6bac7e8d15d053fe288166fe515801a3795565854529873ce396dd03941ed85e5636fc507a637f4f36c084008a6de0dc5de8a0fec2f0540e389f67ce1f91c6dabadc480e9228d8ecb32baa494527a2ec1adc5e4d2029a5e525d110a321d1d43b7eef70ad1c9ab60f1aae90116cf0033afc4b0a4565297049b4b88998412dcfda509934e89039ad014e7098e33943a8b7733250f559d70d78aa157aa0e03c122b372951256562a2674d28be104cb133ae9547ede0d05351cb05c4def669ef09dd0ad8800cf972b3660bff1977300e8e2a67154362326fb64531c512bf035b226c0a72124704b8e41532c6179a628c2ee0093720c9a82871e610a1c2a0cb08c2e6a12b8e483306ef063e8055ba80006433b580cd140b9c038ca1178858ada0366496204939f2130311204f6720c62224414263c0081890e30ace518c4e4c50467c86a7171cb0d7cb06821649a41ce8a50c908202a59f8b09c60077c05ce0a0e46f019e1a8e0e005840465084fc9f18030f45d1a245be8d05d7c3b8cc58cd40321dc0f80bc4b83048aede2dbe1a29ea25887858068190a0e7769661024564490fc031e21990ba112f8c8443d41486ad8c958c0a2e4a2222d5e19082f1fe01004a22040c1424a70e8e955cc5bc47a157378716e410a198d8d1558e88b698e4152087151465c238c730c92c2871c5e1a2152e0d42f308d2aec0f7e252c0e8ec931288a20681458468e415100a922c02a60d426b0ede2054b7890c36b715892e48625422d2c01fa22484917465a8f1f22dc7084922749bec0222261811127b8f01104249af48006ad07406c4c1c6a4b18dd5409274bb484018234a83d089a40362dbc90802e2a59b2a2040b971c83942001032e25c3e1cd9d659412f4cc352aecfcd2df03958a823b85047794597e8f2cc154142c5348b094ddc36507715cc442f811edfe7de81e6762fd8e3352ba808bc0d22669e1a44f4f4b52654b72bfbd57de18354d03a2ded27c0c20739c6ae015dc249a2c6b495c809140de2209f9ca471bca7d22693e34324966bb762f4c85340d725316a1fe95a3e89a4020c9fd2a43ba6592b98a90ca4eb3dc741464f45364645624b7694885087d712cb2dc47508a86e804674c9a64de6a274344332845598a91a93ad8b26aa50501e985e973a4178e5e71589f324f70bb019615ac2b47252056c060423f263be815c519fab8087daec6985c272d019129ad4f29fd5ed22d16fa8e135dfef45bc67234c5484a119459a6946528534a12ca9e1640e4ee64f0ea7cba23224543d32bb2c2fe8ccc22f35697d3ab7e9da121332245b9885136a2f94cb07bf9e47e47759ca14fabacd149618f2ce3cc740dc724dd5dc656b91066271db2eeae01a639062981412d02fe720c52b2821c46590cb0cd3148090aa4129d951c83940841e6720c52f223cf2be875975c771c285aa101d70aca40b30479d09d3cbf534002dc7fe94f14f822a78c1dbb174f1a4efe30020c4c9f77e3335038c9fd24efa687081a22e2430ea838c28b26c278ed2071920220945c9f4098e8e28b6f27080a227985dc2a4678a2880bfaa107079c1f998e133d46a0b38323042133c24310395e903f7fe449f2018718472572f76fb7748574129676a0c514b2259e38c13cb5143149d041d1dd41509e7f718b0b827420449e3fe7dbdb1273620b16f2fc3ac5942824b983a1dc414e0c4912941b439913260735609ea01c1421cff7df8ab02c05219bf2bc4d927411074972abe8e0e4f97d3c3026190ebfd927a55b9fcc574653aa88ac0d482cc1a2ab7f7e69f6313dd1a5429e5b10a23c75d021cf59a9c84b4679fe8c28c692baf3f1786bfb84bea00fa8559f91ad16b2f8915904c92c70d092654c7654ebb46afe4dc35a355f0afc739672bf7c51e002cd8ad9bb24f3493e68a3120aec73ddde93a2d7ca3d06b63ae7dce6fc39e78c3cbca74f69adf4b78dfb95efd694f5cafe50695664b9228e5968c592f6aca05a72b70487f767d0586599bd962cf3d791620481350d8567b8364bfbcb42abec974abdb21faf0c0707c7043e4042d80953ae6f67781d89090ea7916cbf48ab6cacc90f6a932e6a1322b4c0031ae2aa56231c1c1c14d4a1b88ab35b848d4e571cbdab052c70e24ccc779557502b1c6750a4914ea1564d9a665597716a31951cc542ab649c629c4eff212d755e110eddc84724db2139766c6bdfa6db8656d5bac55a6baddbcf5a351c1ce2e9376ee3182431c145684fac06fb1c1704d42a8b5bdea621600121404093a305b78b1efd12aa86d29bbe963e66f7408f1ca51258fa8a9a1fc0c95e2aa17c80b7d4b52a16bb3d1515d29e6c8b8034d62a6b573a9fb88a49d015949755ba8a5ae92a2a9429c9526992ba89b88a4e44152c9490b2ca77de8d4a4bbeb1f6495260faf8432a64a9908ccea0342b06616183942e093ec2d1a9af29cdaa395527ac74c849b354de3e9d1135abead457cdb19d77e345c061ddc9f6655755402a5341d12420158a02d3167076388fb2c52090cf4f500f8dcd236f4d23b3e7680aa126106a06b52ac6b77c784daf57b631c0967b4d18af405a82c329a3471446759a1ed99f326ff9cc57f6f13de129cbf64950e0f00291de7626548cabfa2554ed6810b848b6dfd32aeb3dd9feecf116c6f649fe52045c3fbcb221af4b6f4d6fbf71bac7df1ab560b39950d85716a657f6ad0d1b28a0202303634be0f595f5a16cdf879aa5bd75fbf61d855d45a655fee1ad3cb587d734576837e010672958ca9f5f0eb8d65a6b7577f7ea35c729460b96dcdddd055468719665a5c2c635fad7b9d949cf051829875ad5efa89885845ad5fdd3aa86bd83218feb837dde8635ac617d1476ec74c17076f87d2fc6d2c857fd5216814309a32f61d1e5357b9f9a2583cc30f24aef052428b0b3c0fd48233f3f48202163fdf3c3d1d70c13bc21e4c62b165d31375e2d6858c360205f5326e703ce498c7706278a4e88889ecc763c21ba3b885ec0f1a0452e91dc6943683d1088aebe3c2ee0a6e45347d43de966fdea97774434e03aa262af7c7c8e1839d2eddffeeeed9336fd66b3a2cfdb74773d8226873cbab054fd4f218f530be957bfc0a0ed39d6ba75ebb3eb10548e32e246359483210f9a39e79c73ce89834881c84e7fa03f62eb3a7d81258481e92bfbd357cdf5475cf9eb90f10fd927187e8f09554a6dceab5dc751a9d339a93b759f3866b45034f8ad12026ad27389f08833ad88d67379b421b422f7462cf682171c71448eebadf946c81c9a1113a05364164418119b0c4189a493215c20c320c212d141541d2412438ca08710617d8cb080061da94eb8806a21a918e2884c470c3b203044ed410bb149c85c25e6dd6ca5f3c482190d1d2b34cbb2a0e5d0b7fbd629829a00a1812713b4244e7fa95953e62a31ef662b9d0eca74c4b003023834f1e134a33dfdb1ffeadcd3c39c60891da6f8d8c006407270600591239814551c8942889c4172201bf2440f4954d0050f3942554e610754dc30050b8e5043124d78e0a2490fb983d8e0faf46a6d8f2270a0a2861930b185500f076012032082e0c44e9126664eeeee7b65641335f1416980459fb1a7d54a948c0d38be047b6c12e43a89ea7133e7913a894a45c19d4485539ba86e6d36aa31a594b290bac8650e8732f7a0522563030e6f0178d058ae2409b6446912c56303cab4271505879f234b27513241707c2e964ab3a4927bcb20903d66773e3011d8dd3dd2588e1f61262f4d161a5b71f718ddedb793e3ab587c71115fc7c4b5946787b4d157f4380ec864704163ace9f129e5e9bed3282c52aa5dd056ba93e3bb7bdc48946a5d8cd27d835d7ca355713fade20c55512dcebcdf3813e374c74260f9dbbb7b6bbb31befab63c0d1c04963fb758dd7a32e5c9b26b220649257b0a5dc8f649abbddac6751ee9c32a2b2c25534b8c930c9717981931346a66c04caf3f632a8046f4d5ad5ae43366a050600a33606040304503f3f2924a793754a6488d4dbb9b76a97d717191d97672fc0d165df5254cceaa0e4c7ebcda2bc08c2c43c2482861e69e4e2574a4173601851c93710b4c9842d1b5135dd55b1d00845c64b5e8aa28ce94b189aa45ad9ab528468c957763ff04af2fe9ea69111aa3469a755fce5c7cbc559fab506a51d134994e00802a9700062609c5226fc9592d3adac1b0ee954d313c049631193353cc575d32c54c179b90230a3325984c331b6c92a02991e38c6322b91d1ae24ccfc81267a5066c2a9502504306031730da80068e6ad0a42410819d8ca8a4eeb515bd643433120010007315002028100e090563a14812e5a9a2e90314000b6584427252409607438120c6611004511043410c31c820a09021ca3844451d050c803b9df8a5c48849680d890d3a6f12ed117667a20793e62231adb02abd74538834903badaf290f57adee6fcefc7063ab255e2783e12ec4e47f51be3857ed122b7d84ddc36399be272992f7218d01a27b1c7fcb6eafe9ca1cc591c6abb0b0d665f731fb8bf0b469aebd04f4152a111ca80e6dcf9d57a9568fd5c149d766e24126e9c5e33188d006da729885f0df8125327acb7f174e5dce99afcdbe3f2800069946a4d1c96b10dc9ac8c97e96c00f424760b0e3a250e839d48b8c6f6ec1a9958279f684b49d2e268cd20ac8b37699506214c4934dcdcacfe8c02e6d1010cb6766f3683025000360b82b48ef28884bd19869649a3c8d7a3156403f420521071a6fb8d54a3ee8b040a88725d03c249aefbcb207861a7fb1f06239ca3ecd29460bbac0efe3e1fd1672298c837a5aa05fe7064907b1666f57db73c856bf0751e26fe94dcf19789a1b01a2be72dea412c1b2fa676fdff0bff1dd9f318d09d3dec0932cdce51f12d39bac0036eb3485567f65318059d150fa30e367f24b428b24e9f7f77a3bdf1e4ec5c70eda481a13f18d161a8dabbbfc43e5478a6b683adfff8becfda785e30ddbb11517c4f724c94102e4303f5065d68a48819a6266489c7944789be77b22333dddcd3a148d046834a4da7802bc5cace50ec8e25e7201a3f21a7e39db9962d272895bb7ddff560e93afba2d2d818834a7422753cc5ccb3f5585180ac910276e41256868db609643c3d923d5d27bd2ffc1996602de1e43dbeacbbb5c33f712ec390794e3a7e2935518a65fddd3de47e7755464154df6becb3f4eefffc5e61bbf43e6bec16b76c3fac3106bb1aaba643ddcbad7da1497fff24c0b636c45eac77f1a7c67ceaa953b79fc4aa86c4250271ebd9763cf0eef2e084413f06803d536567a2bc19ee5daf6db7bfdd9bda0b78e1d40211dc746f422a05dc504dc872e1b751edf383d18bfe99c65ffd04eddb515260679f9510df6196e448dd24cc88b1007db519ed3ec50eeb6967b3cde760d0192c699c9d352bc1b58af32ae1081d3ece4b3006009e19f32a3201bf626a0900bd1e6677eaa262fd4bb8a7f58d3b47e41ab4bf289ab0adf26797f2291352181d8e7be2abd43ac9db2043a9bfc474eced425314e05a339201bc57fe964b469969f9ef1f8ac679b3b4c9b7b769d76747def91eee2a8c475172f1c258de039f5bb545afa25e112dcbcb4bf90b0ec23b8c5545c12f1020a767f26a9d0a9dc955a61bee0ca7e2ab70bfc28ed0c404b6ae589800e7ae8217bd89651a1312bf751fe2bd77948dba0f46db75009f6c815faf06ddcc289f93ea480d7ec59a8969ed64b43b8caf14e6789ce0e8baaf89122f62917d9fe311ed41ae9e001c04f9aa433b3203ba02bc67aa100fa58bbeb5c3057e08b9c45035bdf0f14136b9e4a28e20928163aa3a77c0ab2ec9c22811edd117372dd52bb2dcd0d527d25ffe16e9bae5334b892f01689c312225c399b630b48ab129b1973e2cd33bc7b144d0f763d62b818975e2d23bb3c617adcbcf3938a78e60a758b4d6d688921d6b513c586a37caebad80ae621223ee867f12abed8a6f596931619efa2d9b729f23e33cc8d5499ea802f573c7b7ed4dab76e95cb6539d3fb9d5ced3c60dd418f08ec9f29a444fc319473bfbf8c508cc80847aa1731e8bcf867404c4ee8be929446e3ca4e638755a87ba22934fa87fb84803dc7326415e6082a252f91e0bed7a5884b9a8806525cfcf34b6cff6759d725524ebea865a309a0f926e5f40e57193ae39b6b6b8cb5291262a76a5168e2438c8127c108cf446235b38edf812aa0ef94b4744485ff369e37da566d7ccdcdeebd07f46a9376a5887cd4c5c47b8d26a4283e65c5dcd6646ef5d88be97b8a304676951191e081b874f6fab9f6e3d6de1d762ee89cb7ec7462764a84f4269b91db42e65ac4cd229a30ca7d9b7110a7267fb9136b3135871625b189b6d653adda26ad1f60d2fda83890ef9ba882ca45ef5e4fb72d56af7e79d23bc1f9596325a030f58da4b60c21d0a1049b173435e6fa8cbbbf32dbf4d27e927cc45d2c6ae187060092d46863ef60db6dc97ce4371166c30898f99e5ae3b15a2fee15b85f4572eee02e1ead6b464bc02f5553723f8abb732dd96aeaf3a5336e58d86296e121937d85905346c3e2f31fdac61c4485c92eb7258571d72d8088e73ed4497b4fca00190224c18177ffe95eb24fc698617186478540bf2de2912c69e4bb77c5e2ed14878f380e182b3380f8c8c3866b288204fe88461d1f7b55c6e0804aaf6069fc2980a5022763a11786758c7d1644e10200eef2110272eb041c515191377e52a249bc276e59d3d9a2ff56d61f168f0ffc720f7d7fa5174fcbea3f5713509cbf49aaf4c05e0c1e5ea5293ff9d95a9aacda1470b35e28fd2fb0a1a09e22f102a1f7ff400702936c431d199e628046e35bff77c13caa9ad09447e8f93797052490b833b1e2cbc6662cd0bbad3696d2430a59726b5398a33db17a21ba8054307805155ab9642603a0f7b9e1b2427aab54520284ec6a36c5589a566a31e50b909360d48cd18c9463a9c16f15927890ebcfd86647ce537c2c2acf48788d42786f2d28737e4c4df3159937d96ffee8162d130867c2067073d697603e94f79afe34769a15031cfec4ed5e69d4fc86288651b71b88f001a91fb5e48be6d952abdde1362ae5b73515f20cddf8665b2eca0fdcd2115d44afa843c4f4032267cfcf30025c1fc64601036d224601a21a13e517fc119de434924d337c4acb8c33d312734d41f78022d13496caaf9bb320454c546646da91b9779ed7b4cb6cd3e37ef6d9a421589348523c6399e976f3073cb146a5bbb7363acdbb5a0b886a7a174ed0c40535cae72fe9f3b0c256535dbb4237d5947205f5331fb61a78ca149ed9d185923b2cb010a0872a6b53d2ddba9b22a931b4d4d40fca7cb53dea74d756087f938167afc537445158302ea0f233b9230cd72ee534c2e87794840f1a0863b52aed054f5c9485490c2dad420085d3c67c19b92a0b9771e047c75562b78fbc8dfd393a4f4ef81180461b89bbfce380b34551364d2bb7a6ed14846750194130dd92bec74c086a76bc036e5b4c7526b6a9aff6eb4fb92110c990e2cbafecb52d91d476cd9b59e344267f115ba161c6fc3774e0913144a5f718c90de96c3777589e6ffb9e8b1b56654bf6ec80c54e972fb9d96ee355970392933be55be9975b90c99daef840bbafbc2f07257945ebd8343fed13b444b3e62772441e98f880e7d132310ff0ce91618031da83ff6965da780314808674835c056e6611317e88149efa5b5beb8664fd9962e54526e917d8cdf8ad854f7fe5d3763292ef3d417559a47564445c516cdf2fc6e3935aed2a4e5c14240fe50e312f12cc8a2402610b960cda313e30d2e217be79f24ab7548a6b38db2dddfaefe1122f46a6d2f002e8c3efc4606fadd7f68d1abd705edbe397bacf16f46abd744db2f64bb4f14f4dfc28662c977f6762bd646e602ead2fafd5f77c58ef6ac9b33b4cf8b49eaeb4c7a79e47672ecb46e8e246d748402bedcadb16777b68dcfddd4a2e6274f602edc13f788ad87e84ddf38a3bcdb611eabeb55a4ddbbbeedbf09e6d580aa98a00cdc1aa109bb95b412a18dc45795056f4b8fd91f4444fab8acdfe9f662aac222952f8a251aa09080035dce035e0947b6d97308949640d86222896b9674e76e63c366b73da4c488138c403cf4981b6d1f027ff4d220cb1ea9c023c3b6f1084cb51f91cb17d7392936b36d9601066768123321ec305998fed433c5680d6b7d1fc621724210c79436125901ce411050534d4e950b01e9535c2c2d3548610e72b131d40db0804ca9fd2000a452ec835ca90fefe846966f894fcb4818835abad86c9b1816ccbded7f8d933c5b89a72e9d454da4d71f0ad0141d3560f5d70a2f536dc80b060ca971a65adf698a1b547e891356b4cd872cb9e9b0a481291b540f558586d38eaa41ac3b44fea9d8f586f25ba958b73a81e427f3751029045a5f5eebbfcde2926012fd67a34e87aa4916d9341ce0df6ef0b80eaa406c8631012070484f4e488e4e2ade0cb83ef208df52e0b111248d78921a4e86d9ef5ef3e6cf09085e9336f1695fc8291eb752d112802c83780af5011091f159579453d918a7a425f811a242f89e13b2937111a70a0bd6c3416050c87ec3a9379a0ecfa58a0f0024bf5517b32ff74497b76f770db75738ae85be1af80687757e2961e4d3cdcd7bb15c50a5740d8cc68187c53cce8e22fa39d585188c22a2bb601062477ad6af197b9bab1a35044f7f0ba3db0eeefbaeec1b2f728bcaf8e4857aa9856fa607e124fa6ddd8a20885f55a80f5e0bc57a1c3574fb79f1b75dd545c5c75e2eae362902b7a6c98bedca873535191f0c6acb409ad6497038c45ef1536c4c44a4d9b57a857332880b0e80d85afad98f8d2cf6db1e6d1cf81df4fbbdf720be0a5ee17690072d113b0632fda46996962dccbc81f57ebdc4a5184e8d50f30af8ca51f9f8d857b93aba07f66adba1e2656dfd26f24e716bc744e98ebfa339ff7ebf16fa06f25be9b758dc3476b98eb2b319eca05ee0c295aacb7890bcee3491ce5c199de12fb342a9e6daccdea6aa3d02b874adb2278e13e22264cc33c1f5c60772b2b72bdd0a8d58e2da167d30e1398010c0562281eb99d25be080ac694cad6854e0a7da90b6980cc392282c2f4bed31f0f25bf6880eb1add9035800fcdd10142e5542db8108783a9b2fe8b36564f975864e0f3a28377aa7c083b5188c3b853a5788cfe1e3cddd6125bea57976c893435bcc5f16e6117aa898807026564f95780e5780589e8b100ad3623c6686a71a2baafa10311d71f0278a6ca8545c5e679907f8c3ad14f222ae839d93661594eb3ba089eb784e8767b176df71561608ac82288085b88ce831ad71804a7dec0110f812243de10374000228ac83109e1e588cc6d36ca3901fd893b0e3999fe6145e32d6268897364200bb34f076e8046717a9d05c29b0aa56f354fbe071879171f6e05e87eaf3409e8cd3683945d52c93398fe439053f043692522a881bf8f833d1b0ebc8aae5ef200eca222717db9b02316ada6e566ba065ea3ba8faea3fc13a848d7172674f14f81d355b6a90715e5b27d73a0155d5e3471ab1baf0ae2e562bdc663e1677f999af0cec02382b0a54b3cfbb146205479f6c1ec65c7931909b53ca488c05ef7202252ee64438f79fc018cd4ec7731aa957cb200b28957f39dfa35e9edd4854849b4aab0adf6e64f8456dbbe92333830f77f2654f25cd0bae1c5ddd36caae038c9b77c6f418e2b3a56f05ed629c562cb3f4b060700f1242a4195cadef6591b115014ec581508a56708e2caeec020a9c152deb659518b93d124e58c56871188378ec620e44d5caf2c16dbf1856c2f553eb04d07cb43064124d095cd91070591cb94a9646284b69ed24e1e9be4005704dc4b2d8d9cbacd9f582fe148627d82168cccc0b06d8ea8797ba55919352d438796cf0e62680e71ec81c039a4fe0a6f862807e446a7eda272670985cc5dd26d924f9f54c9b7bd5747f9325ca61dafd567b08fa9cad7769906792712a5838c6c95b8e1534989df73bb9f4ad4f0c2972ce1be26c8bf28d1999b8dcf58d8e24547a9654ca5ab26603a0089a0990a3a586846012892af280ed0de8bb2eb088f3f545a50e9e4964c24747c3e3662d85aa2d19d47d5cce214580cd6faa4782984fb88c7a462e78e2756605727e7bde174b285116f8e66f3d2d04851003651fe5000642569fd5b8fdfd49c99f0140ff6d75e4709ff6ae84e0abfb47121fc945519992356e0bc6905581a6e007b84dbe24f52a1205268d6f8dab92fcb5c453bedb520d475c29058857817f7abd117bdcfd1f278601ce87ebe838d1c41186b3d526ddf23c68d4ac23a2218648100b34eb5035971e1ed73593e72f30efcb15650a27e2e891f82f985b19d62d582dabcfb7308b93d74f85837460392fb502ba2677851c05e85a1b457a13bec9c5e6107f75db0c3909fa5aa84e157d2178083434ffbe9b3e49127148717c899f795f844b5627f841670cbcb6e4ca402a0ecb4b321dd00a3776eb15f301b3fbaf79a82f0be3bcca35a1ad400dd81631606ed403dbb993211821b0345b7bd45297728ef13b4ba85aa5caf627804a390547bbb6f5a344b9146bce8554d62824fd1071c8e61b302b2a82cfe7b064d735cfd3a634c598242c71595884460e2a66bc87f9dacb784742c6ecec5a2535985d25f0954d4a3aae97f4f14f7e78806604f5fdf192c2f07898336d7c667fed51fa51096ea5632a867379a12506109dc885f7a5b26bdc72d2efd3d937537f81f6094d48c80160cb1270c2de3bb9ca61c0341c7bebef7f7e9f77df9debfbfe7f93ffa167f5edfa291e31625b64cdbe9a195799eed5655ab5fb7e8ce8175d4d0496a8320db9ab6d9ad8dfaa266577b14045673cd7c481de68bd353c64135546553a7ad7ef4d532959e646efa7c1b3508771c39433efe7437e57c19200c7acba8b497654451736f229c8703df53aa359ebbc7443e0e3589acce086dab250a48f11b3cdc5df38c09c9a1f47435e75d85496fb3e05fd1bd76604c0cee61b123bba45543bd9e361b05f88e405a95757cdd9847ca98fb4a0c28a30dbf8cde2e05513ae53c4c3f3e0e886d50f789101d4f82770e31b5124488667e4615edc93b8621caa6bca9732ebc8910d9ab76594367053adb53642b790f94d3ff1e6c4fea5fbca87c1203c63167d048c6afd6a57cc715718869df4114898de0cb50c8572a307dfa73ed172e8411fc11f24828a90906f7d125a60fb62b84d47dab95169a020458665c5c5bf1c23a1deb9eb4c1d2e228f583a49948f7b33414b2b6edc28541cff1b957036f83764393afc1405cce9146834bd377b44ee39a601a673a08e0ee24e4bf8060b028221735b97c2134571b129603d53abe89710a63c986a276578fcdf68eb681eaada0348eae765578ba87960f4a2bfd9bde464a13355de0983486c4391d4016ad72ef794513bcf7e91a7483b0d164eee273140f50759c7b0b1a17ce0b465a4b03349df6184274688067287067d08ad0edd3047d3f0602fb05cc54cd64d7ae15ddb4592f6a41e2c60758da3c8b4fa0f44904535e79ea491144e2b76f7a7b62b9e9f723539c719a2ecc5364191d41b819cd9a4fd8108bce85d01a2d11ed986b4f7b59e353180ad9c22d4abb9ed378251560012e2449667366723b005c9c1c39f824c770ce370e7d61a43fa3bb35ac3aaf0f31681704584b30876cadb705eeaadff54b8d36777a8fd2fffc247180f235dace2ec876cd7916088efebf663632f9b083ac7e4a721031b9352b639c1e48f2254a188133291976e4a5cd016ddb059932ae53b91d88d96371fd66f66bbaf576680f1438cc469f1c49793495f22c0085acea56d8c8555a73c5777dc705c92fd75127a4508120043b14233e1c4b5bca037a819fa3f1f41d21eb1cb4e707d97c3661d5d91c0d2396837b42bf5d4126849c5b94bb799b192f4268ceb9749ffba9a7b3b526349c904b0b4d854411a76d08f5566e0a6f5447430bb78256ee81c5f7cfd6c63955fb46a6a063a088e857a729fce5b53a469a03829386eaac64a41be350c1b4ca956ef5b7b7123bcc1cba7bcbc08c5058f46838c78329ded70a8ca377f5be6f1e11558bc29f6a2d203d2545f0807fa043872669d1c073b0e33e77938fd989d4e506f207a08073605c517eb3e46fa8724695efba30fc54e27fb2b8b0e11678db33307dd9732e3faf193fff094b284704328d62df1c0ebffe62ab3a145410743ea79b3423803a59c752087fba8ff3f29219465ff5f0356adea7ff7c8c000196ca5ef116ede232c1f6f3f58ced565cb05cff13d79a47adaa79548f3163685f7a349f1ee01a2088098d0f8c70aa6b7927580ec084e854859bbd18908d9393289f32390b60a10f56c0f44524b3edfd6d243becabd9a910eda0c10224ad5d9f5bbd2132a77bdadf9401ace783035639e47b06e027017f45edb641932d8ac9fe4b568c6e79bf8b5a47e15b4b33c5e73db967a4a64d5a1789bf2233a27a34a93213420484fd31618d22458e14f6949e65949fb8843063876128b384c3f03e7235071a225724e7907d3c8072986c439781d37ae574f5f25658ef9667b50b504c5f06d4b2ec5cd1c2189ca55690c5f76496b7c1a850bd4ce7e5265202e1d9c8031d217eaa05f2e33dd464d975290cc35ee3f55ad6565aa51b1a30892b7b56c27c606b92bf533234f2795618a2dd1ab272db33832e6884370c3be9f4cd2ecf805c31df2c56403aba0ec742fe70efeffbfba73e1cfe05872cdd27d260dbe51dab9b26c9df177ddda263715d9c5d4053f7bb2cc6798c40b2bd9d1083fe11224677d1ab8ea30f9ba4990fb9d50149a79e59a84490336b631cdb81e943bb84206bb8ff460a8b9d672c89566058e2b8bff73459055fb281a213321dd811d1692901611bc2eee6662238d95ec4cc22400f7dac4158db4dafa2587c68e14dad4623c7b8424d70ce2cacd5e781f13ff172f0c8bbc5e638e535c3e2ebc023e0804052bc3fbeeb2f9ba77c2c478d7cceeb6bcc048adfdcaeed50a2a2e89312a77805c7cc50ec94a69f7831640ed3e61450c317e1f05ad44e6ccf77edf56b05322920062cfecb1ad5bfb6c32d84c209224568dad75c52d7b34652c62998037ea6d496be76bcc041b292a05882a43b3b3052404ef8106995ae6ede317368ec20cad8d33895d0af48901dd61954767aad7f403b3ba3324cdb18155c6d1e022e6ce267d7fb9f8f5f109026b0b5d7f9968a5ecad52ea044cc57ea207cda936034e5f30b69b59c6f199cb59064aeea5e4206c3c0a3032ab403aa416fd6c961bddca53d5be7d4f8e97510069c5e08d6fb6b808f518c10c1549a7034c290c572e7df2ff7a6210f97bdcfd88c01863de2d3bee125274b927c32f62fdf6cad8b3fe4a4cea00bbd7d3e8612acb65c6c77fe99cc32fa7b9fb0cd8796e20a10df1902b2583afc5cf486f8b66f82ede86717dbb1c303290e31ffb016e8f4b1a9a6e13ef3d269bd9a289ee84e9f661b9105623cac992628d38309cf3baa46a6b342b9323855cd1125e89b626271d64fc1fb681b5eb53dfab0a7bf739a10c9e8a1f46379fc9d77d07d6a4d8f56885117a5d91b7847224727c2e4cbdc75305a9c9f927f2b0659b0a71b364a565c55d80592ab648a32ffd882b04bc0bb8e5955b73875a044783555b658de92425df7ff5d003af0232ada08c4128f971c971e326f4eb3e79b06d258749a0a41a4505537ac3d683474d77a98ca9984e0166f02b322cd00fb2aefa2e822c2dc3cd5cd5dd6489f43ab8961c2f80ad7c86580aaf3130bb87113973a820d35625612f7773d3bef2268b5b9d37427e0deccd73207bf85b4c347324a8df91208ed6ee1b22ff31e74264bfef89a9652db55b9f35ba9979a5c72c7dd28321b9441e1e8a72f727c0bb68e8acecb5e729a6750271ee7535c50a8d21ca1a301f55ac755f7447802cc28befcd76440134058d9a66506b2184f4445114debb21b5608daa9ce068df200048b86514fd03c956002ece60fe85efc3c38e3570164ee171f48926afc35f454c5b62709f3f81e92edcb5a898168221e31b8b14808aea8a2479f5ab8d1e32d6e3ccee9f544e303466349faa07fc777b5448401fec4d81e4d211e061607836bf72d35b5bec887030a168ca54cd781118eeafa5eb7e9da2924316f46362fa197e6db49e843a20e46b36738248010a5807d1d68421f4150b0b6dec93a14c2c807c4a9979d3bce98c1f71936123051ecf6dd3137deb48816ae2efe68fdfa18b4b2ccedd5d998d57213cab25727a9917f095ddd3eceb7a149c77611cd66c16ac215bdf6349aad3e63a09af7e1197784193d85ad9c91e6e3d165f41e9fac85c91ffbecf3cb376caeb18899a7e92407d5ad0dddc4e5213bbc80d6c3474c7946e18a2c31b6ad299d4782d20830b6452f1c33f7f7cd4f9fd8ae9b564863aacd32fc8e837496e80524746e0438f04ec68a19aa5708759e9541cb4a9ac01d258b9f21cdce9ae1ac49b8292c259323f759e36992d7334e4e61ef7aafb04c36b2e8417545b2e8932cdcb2da87eaa5033499091517afd550706d921e1d60359f626b44312c376753db58538502b6b9957d1de05b6a8f937a5d53f86c91697ed65ac8023b7eee277746dac7f18f9bb70d2ab8bf22db7e44187a7ae51fab162192ad78e01c19be3b00c3f06b2a48d34c5bfcc3fee6ed2a1532b85fa01e2c7f12bedab43175c286ad486e5055c188ed0c0af1ea76d8f52b4e6f2f653ea14fa6971332d55191d317b284547d08f38bb6018d590c3460ecb56706b82c24dc22e200f3f51a427722a727fc8f88e7e7a24eff776b82e60b7d2a0209d9d026ca2ef6fc41133d531a5d579b2783067eaa2bff9132615ec835112e4c93999cf079b8e683e3460528835bcc4488ff89edb056891328b68c1edd04b8d862875b03474533b6411a13b17ebd9be280bdcbe2f6457434907aee90ac33b56a0fe65152cbd4a893f9490905fba75502ca195d5edc0aa4c8baff7e172dbe5c05e651baefe9426fc48237b3144a4a05fb71f9a4b40e7cd1d5ab78937e477340dc668e295960bfbdd2ac09b9ce49390326e408bac4f2b104d8ab1bd439941c61d1fb785709be9205344e43357ea1a05c8d009edf4f3904d31af8cda27b67e2a70a3dc1e098b705571fea38802da056b4b10cb94c9f48690e9016f0bccfc70664395b39f799e1b26ab75eec93666363f8817b56565bf6c32e891ed8a8b83158c7f25442cd7a104d9bfd6197accc677f66c86a8890fbc9347413c46086d79f035d57b2a2beb6dd906a88730e4da9c6bc94639691ca4aa9e88056cb54f0e48c977aa39bbd898e51ed9fce80ed8581f31cd410b8fe34142955cb5086ff204543edc7eebd28a90f670e3c6adc7c0de995d4f2e13219435053dc70fe41fa06dfbfe978112886c133a5e243b094a6161f500c297291e5cb436fb7004e8a7c4059c1d2b5c285fffca122db6ae2ede9055979ffd0dc0614cec56146dd8413938948c73eac299daea95f920b2d3a9e2cb083ccb61962144037e8f1626b1aeb43ba1b540735b2f40d5320e01cb27db793236800dad04bdc8aaf2d7c490a37a6ecb12e168f5bd471c93b1a1195983803429d582853b9aa4e8a488489ecf475c98426852e79dad307d2450f40cba5635126dbe09bfbc0c1694615b734be17223bdd438e471cc83569d98f4f505193164f6612aceffd0eb0aaacf39b3186385f3dc65b62cfe2908280e255b0e1261043edd5956b0be299590d8308329df33901b6fea44a0aca593eb164179ce6bf704133c222a312897f786cbbd16418f833960f1188eb26353ab67aecc765880c74c85dc1189502f41fe7bf4dc7f5618edfdb3c502a48688081198805838fd207b128833b0d6938caa19f078e27d8164b20b7aabe63ef3f3c3669c37beabce0a3e62638241ce72d72edc2e09e7592f6278521b7ed1a06650b561f9618817116f9f7540932facf1ba62544e1bd9e6f80246d260ea61ffdb25fc0ecba1407ecd413a0a81f456724a86e1e284e10adfd6f84146ad040039f0b4f738c340f7512a8c54bf1ac621c1735c768b646f61c20584a329027d32adfc9e9454f6dcd7b0643f8e084b43682dd8be106956b0405983486704a07980480de663d32b435ad8591a56b2518c26233731a311ca18322032b2077b0b72f10d26e7003b179d7eb2ca74430cba3fb848f5281a72e10af4258f03f05880be688216d0f5596b92e0a7a12b4f7306545eb16bbd86286010a455d87cf85a67a4d0c64bff5f5889b5c2588e6154000986f6031d69b7cda673810d87a5f12906743b5c8d3810bafc5661201df1c0d89520169efa16de28acc112c6a28c5716a4454bda9d4f50643edaa94cb7d95a1baf091727456814e77f2b270099fe9d10fa49ce7e870747761660ec0ccb27ddd1f1854c6eaaf5c95c6d9d983fcaba857f23a02f53e9cec13eebd89bfda189688e1b922ac9f172b94dd5d6a3519d5482ffd6aefbfc2196ee81c1da25e9eea082e67e43ef2137fcb389817041e791f4328870d8421d1b885225820a48ea8745cb8e82fac700743928ca9e7d9b461ccf7b2c43b0b60c46d3d3a61df0a7683283b6dc9de4cbff49c5aad0dd06135fa4067a5226d7fead970adf43b8593580d0c2a37450e35f757b9a30f488028b78694d936fbc5ab5e37d0107878b20a2f6756204b14c00a9b0635a0e1241970e4a27802c8a216715b5e3e95da6a6100f9ed0f730c166f2b9cf4e6bae2df36e9ce6938086fbf786ae78a9c5d6e06c3bd4f4b73c8532cd1394a386090c2f167afc85b07aab4d2eb388e6b56d84af9028b69c5a139745b6c5f1aff225ebd4c1d8ed74185eee5af42735965e3b41d3a61a0cc8cc7bdafe173e655e417af33ac64e29d97e2fa79512be3d34a3d8de26e86fcac12e20e441a08f475a613919fa8496f1f0b88ec4f6f4591ed8f87bc67c85a7322a22d1a5c4ad50f7ca75077eea2ee10368a7996b7375b813675f8cab25d37b38c1708325138581c2a4bbbfed032696505e1522872469cfe517587c501c608bf855c2a2f6c38279eb3f35e02391c7e562577b663f7b0c2955b46c1b85a45f86315a13819c2527e398bb9aa65cc1cf1a5b0374c113088526acd8c1522d98daa4abc474436af132fd276adfad9e26cdaf141bfaeb6c9dbb160db7c96fcd1aba7c0f6e422bc22f7884040a106b81af6ec57a4a088076b896668d1ab03ddfa1ab2b3d5d1aaa9cb9015dd476b1bc7019b941b753da04ad604225d457bf0c850be48952b3ea5847b081b772c33636f8d0badd7b050414acbbd65a5a9738dd216060842e2a8488739ee176086c2f76d1da585ef9587cd2d010dbdd83f313e7858af173e30354a5cdafaaa51973b052be0c30d90b83bd87cf8bb2eff1b38f2439703ea35a8044ea6cfbe52e5ff6d07674ad25ffebcf58714d2ecc813e55903b07af153e7b7ae79f451640f960359f1141c3845f9ccd4cb22e993290acb16d186eb4a13dab848633b4975e3bc71d03264941248a47f69b43e66de57221f5b3f3bf77fd32055ca628f087998b21daf730b6367638b97acf1fcad607a26db09581c515af84af56eb9795ba4b264e7cd5d894a6f144644dbf1a6c043f94d0897f4938021b08f49b75487a7b4079bcb9c775f964264443962015773caa5c229f43ab5f9daed829126ce7d097cd73b85960891f8eecda0e08b11818e76254a23acdd98794bd5f40e308cab7b64afcef6a862ec26706b30b267118c6d8c6dee0cb8ea76c786b4194aaf19e7a9de070d1f4fb9a2d0dc18e5affada516d41e40be9f81bca15cdc7d5d84fcaa2831ac8ef93aa0627e915a4ebe0133988c486aaee5d91e49f1abc857eed1304024815836bf5eb7f030ea7774acc14e888ac233468cbd07a7429cf25f22bd1dad113dccfe9703a361a0b2013afaf923dff97d09c65d675e76e0fea1c276272f102b009c100ec2a97caea6d2e98f60a85db9822d10998b79705426370ba59f732a60a5f2d168745dcd338a7a2172fadafe248c2de7b15130847ccfbe143f958047a0400083fc4dad2699383f3e6c5fa7796d007ab382664f8801d2a94115cf4cb25a374a46fe3fd418ec7d05803499d886d7f1c815852ffa1a6b352f40125e2a0229bd1448a6606c859c51a4a0cd272d0d975850e65674dfd5ab1ed4c79ea3384e13805b3010593c1b56ed06b31f074901c2ba897afc5730a8dbcd907f4614ce9f52c883b40289345c0058f0a055c93076d8a2e0f6b2e5375d74fe60f358e9e0de4f094b0f3570abe03f4335ff67353872157acd099c687897378f3541113a6d2a262bab5faf6c03028cdc27f7b54f5fc37cabd6a8a379690ee8b905733734e3c78884b7818d4d2dba36b6f4ae465e5c237d196b10db31e20b9a1e9eae66e91262b96b3fcdf2d7754852522d81b5b6310acb53e3d006048bf23ca11a3e85de291fa1170cbfb2d8542087c132e2a48e6c610574c26d2af72d230fe638fe0c273114715c91a0ccc13bd884d7ed052a9b229f9dba582a80a93f160029c62976083519ca7ae4360124bbda9830d620b81a128f78edb4e780c0027c42864f826b09a51dad0b227a6edbff257da1c187847c5660282c3aab235cddf68cde409aa0fe8bb25d6175b992e104509bdc8c5445f2ffc777464521e81ca5c8aaebbd138cc4ed2c78fb1ca47cd39ea4626a2311885efd59f0bc7a2f180977061e0d8c6dd6dd392e5ea5630a0b2a9300c48b8f7005c489f710f520a57fe6497e5ad826e5162476712521d9104501d841c24c9c8a0eb5c00ea381fc1e3eab973727547a0e86c23cbbecccf62813b62b894e4f9176eba4962b85e8adaae8b64d2b821bab8fb15da7ab68df55e53093f04b8d52483ad28df2b9fe65980011f3732dbc860ac48da736937f8c9bc518c72c4a424a1d04d92ad9994dfd8c587ee9844b47cecea28804f150c5c2c37d0b2ea9d1528ec81a7a10233eb4d5432747d0c33ec4e3826503b6ac944390245d2c86e26a6cc7a9cf82c1f1211c3de0fca7c4bd7b6d9dee443ae1654b5011d2ab712f57735fb28f979e36dc0caef1ccbfb37f247ec5000c6a179b34f8a4192d5226d5c2929eb885160cb1dd462b7c2598673893c2458be282802b69a5334f3de64c2e521eeb8f11ce491670618434af65135e90f03af2d776711628ba3af05cc17582fe35ca6d54020fe60236a096e6cb1ffeebe1d30d404485447fc1d1046b0ba7a5e3bbf50568683f88e88fcc1707a7ad70ee6f4ae7d743a705995f815d48e891771fa647ed8241c3b64ddf008e6c32c41d67283a33c786d925e4d8a30fd0e51852f7d66990e19a702ddd120442655268405d6e1d24ccbb0a55d75b7d7e6b8717c517238c136880de0a24d49b0c44878e6502fa6b7d51363a38236aa8c8bda740f03699dde1408ddf6b013a56cbba9b7a9d054540971962093c0f8f194727a0bf8c4702b70e755bd1727260cadfec22a9b72a284efb31142e44886eb471ea1640c5d1e65af5b7ee343a09b2b68d1cdf92ecbbd4f4ca2895807d18d689bf1e7b0d5143b3a92a0931d98507e00bd127d5180d4796e6bd36c0249c5584816e38309fd3bb64d868f2eb2e9936706c9f8e8e20857ab53c57cf083b9833e8a94126308298b68d6c5aa9812fe7b9c7e3219804c4017fa7ae66649222b296164494d168397a47686743ae0627c8d8a978e2cb6c3fafef5f40126a38617e37a7590b0c68665048fb6abf862e277e9629fc64aad131af848ade0b93475b94c190a6dc3f0c29fcbb2581109b841432f3f3d59beced20c8f808c931c5205e5c17c3990eff2d4c9b7974b0c496714cc79e9611618772f9b22a4d9838e9131efdbe19716dceb1ff34f650f4c1a081e096ea0f50dba876ec0c0d6d2ccc64f16263ea8c4deb008280240470b27e3663fc810a3a7cbe3ed4b1b9c0cb589acc366ef963d3c388b747647bb59bbae85771dab6bb2e1f655b9cfc57246771b35d91edb76d7c4ac218565ab2cb2585a044b9b52b14bae8a7eec57de3916ac125f8bef1f6c138562394b24a677f493d280c4ca03124aca64d0198842a65aec0e07b229aa1163a289700a1060539986241d356f391f52500d3d2e9fb27dafb9a41b11c09a84d62abdf719bb642c5f3a82a3ab85c32da3e466afb994b30e68affc10cc91cb5175893f1972447030b1bb25046d218f0b62b538c9470dea1cb6f0a9b3f90426561e4732eebd0099470ae10674d4ccbe051e47c801d36e1a47cbceabfe0997530c9c015d206239cca84f24354612e29c3509471b36c5ed10775f24d0a83ca8062c23d1557ae3e958e774e91bb9617cb66086e7215dc25c9da4a18757b75892a43dfd10854523a12b8af2ae76cbbd1cd646198075902d769d6d21b2a846bf05832e43f13153cc9d80f40c8af3bb4e290a6791db9411767e4f22bcc098c747b6bca2dbc16bfb0a86509ae084db896439ebc88958b11d527ec97c3255dbbea4428000dc94515b332ad9e22a923c9e24d09496a2fb83c376ede3cc04c974175f0aae49a7aea695040dc20e5ac08175d4937922be4785368af019caeb6e791a69a1b85fac083245955a92799e103dad5472f60c8f39548edc5d31322f3fd5631b4fdf862af2d48e1b22b0608f8488f0b640af1364cfc13c2ef9bfc3c558e053c83c7064747b3ad76a47997865939cb7ebe2db5326c5b5c3b313e0f9e6ec8530a72d903e5455327f69af50a47f25aabbe62c7650c1c397dd53faba1d4f3e69c57b7acf14a0352e14b2d2d2bbdd8a5a848cc4fff6df3f53dde8f30168e4bbe47d0e1e6ad13b53731ac8c3e1b6b1f3a5da19ed270e7948200f57c3a6435eab5b6a605057147c9534ac6e6d5615e609dca0bcd6edf103d585ce63d2c6f622c4454d0dbbb184260fdd2b6e4d77d872fe66ce7ca5abdb345bdcf7bebd695908c818bd7953663a6e7babb74c1b4a19ac3f0a655048bb441c55345d9984cd6dc31f9d538e464008c14bd5583caef3b68e2ae5acd58348a385356779a703db3c776c54b0df14679003e285fd6ac682e52fcdd301d809758c763ceb10d37ac8ae4dd4cffcc11746f55dd7fbe8872542d0e6d0c76324f24640c0f5bad431ed40071424f90c5398b3821172846b4f780a796b331497474cf2eb2393f13b1ce5c07fe7ba9ae25eecd6629f2987e3a3ddbea2b5be3a31c9fa605a3b0e6f2b711d93290a12c82daf0ffd359579d97ff4a5ce4fe008a4a8332c2f547e05b225b1498ab5ce48a70d48e39e173b9f4f021005527e3f3e70353028bf9aaf2c2347e2c462c3cc29381f83898717413a4b47de00a4982a1ebbc222b265a5ab1ee7f830a2849d7b9603ff7e73be7af7ede2a4838d9ad489aeeac9db7b308b73f98838d3560cf11c34456aabacd8faa7b97df706c4e3fe2ab609b91804b214ea376355488a3b5c2c628144ded441542585d5d66d4efe4fd76a403469b9ec5057e0791769204756a65442f4f4f17d2e09b90fac02a1d6ccf78b9f8b373b7bb196a0717abb352b671fe5f7511cbfff797036ea89f458dacaaf8c3d76e34dac0dff671db28850c3b20e5e5d19d3d3d4e7bf8765b7253a278aedba404ab63397639d1988700ffce6d884343f040bba438ddef97c365b3dabedb4f1b624f64448a2e686f09d0d8a519952a4daec431ae111b618a93bd50cc0b69e4271376e74363dd28a2f5f63d0f624b0cf8cc2620ff5385120b5f87a73f1f31adf80871e9d05f71439ebf669b47f02bdd8dae18f82992c893d0c23cb2e9394df6f395f90c8d010b0845cee9761610b70f82e9bbdd6032694b353e257ab3744622305d41c4da8c0c99afea014eade6b02300b2120a22c01af57d209c1c0ca3614e15509ae6eb376c486d2f79f94efac85a9cb6f659f17aa3b9927603800168442bebbef0a7ffad1590abb4b97643b1fa1aba078fe7394652ed84d122006c493acfe465ea357798876d451f22eb4334139874cde26d9b878184018b34db22a95c9250f5404b6c063d149b6bb5ad43044e71715a0a9136ae3b5a81bf0e5df54dc83dc850d5aee2f35d509f9c734a85693b410754611bdc950ef66a25e210366abf5f71cd64ab4e15c7c4f4aa2671730cb554a9192a1e9a43f647862e424be5740ad1f769c1a1b92517a20475b398a1d48a6916f68629a9b061213cf638bec49cf6be34860e0e61f730f83ff3320d5be8b9d30e5214c730630cea0a5323e81216859256ae60c8bc7baa32b0bdb1559890a5360e64a7dba69643ca1ae41bbdb2de0952d1d365aede69e8c4881b19d99306010c21d72633d8e83f63c4516330aa3c0a8cc93f979b62f9c00250a0cf7d485217f9ebb31d1505f951a60cb428c0a141835afde075c359454d0426d741bac119fc09083980a5404d5d0fee3cd0c894a3a2443710389d099264097b79d6455ff7769360f3781b7131a833c2c97ee45617baaa6652bba6f68dd682a2bdd8f61f00b318e24d6b2655f6f010ab28d69dc5b11fcfc06624d4fe109d085e4231808ce7e36de0b9128873ee6bfda09fd32d10536fa944a05188a58bf4009748ce36472f84dfc73875b978ba8dafd9ce1c3f7abb4b189a35d507ad856dfa3236ae6d14918d3171b68d11d0f8b89136084be758a05d34f96b0ed12720968c54e8143a02e7dc761cad6305453e1f164b37741598bbcb935a50dad314d80f1e592c0321dff7cafa248b2de0326a4eda68485e4b773d4b3fad370c41955ca72536597070c3b04e845bdd5d16a48bd4e1a69a3aad50e6251156000f1d0472c7f4c891eec40dc34e78c4ab19f91dcd8279a3e69bc8a64bc48ad0985a9c275bc199f4c0be16d3b7c508a82bca3ca5dc4c818a772fd90b0d82b8177a1f91ddb9bc3f3646d75bd87da10323f68f74a56dbdcce1d087b3dc60f81b215d4d68947314e2e5f8fecc00db5f6325ad2eb1a8bd4bc91cc71e1dc5c622be6faeddb6000feab8bebd78a0da70311be0c9fda9737e0fb685c3f874d631e57bbf9764e3f3dfb7ca30edf56558682edf6d062e17099313d9e63558dde311aef416366459d821231dfce41b443ded581f7b0ed296978da8af55b84b9fada5035e7a9acb916ead322091255685f90bd54573d604115fee2977fc54849881bed73605980108b6302c831990ce8a1e7c919ac20170031f0457a061bd70a0270a6493f3907e356fdf97ebc28d2460893a3a3843ff46e8970687df34ffc3c00b290a59930a01a85eebbf21d821b348c793a1ad4bac01f025b6d1fab1e310fc86a99e1b1988a946cc898b96db98855bf1c835614b2877e7cb5ad932eba4785b67c9c5b446f127f91faf4a24eb15ca477f572831ec5f671dbe502d28bd4ca64f40ef8614c5d180605647fce468239bd32b094a7768e4cb85e95cdfd2fb2a384e51417b0b6285e42d595a62481213fa1a1410b411a99fdada415d213b12030702b7f4d98ad8657048ce5eeab092bf1186221c09cd9f71246b1480e83c444ca0ff66226558fdb24087ca939f779a459048d96b104129df4e47d838636afa0fa85265f7a96d59aefbb4a6d93f889ae5f9f542432cd9850c5cf9a59ca07ac19740edb682cea72a8e879e841ba51041099d6fb029f8bf5f362dc04a3083a296c0d88a7cafd29880a30461fed6b952577f91ab53964feeca5b7b9503f824e35d5800a920ae51d731a02ef38a948147cb1e98a4469cdb202ad437c16a5f1a5dce13d8d946b37f1a034607d761b605f1320c90e665af441628f46f334a335c247ffeccb33b8ca8be536910c2e154bb3dca0f56727f1acd471fbe9c08459cad7f8b5a2f6b5160419e79925e21559a060b643eb60344ec748f7c72ce7a2c30e1606e34dc1eaef7976c874491a32dabe3cbbfafba3a755e7ed10158d13f9905890f33677f1ccb5eaaab0370063e9f2524b7a528828d2a1608e0e1f004e57879845c0db205cf1de762ed65a24d8567a4a899cfe761e2ac5f3d75d993d3fee0e06461af1055be87da6bd62556e90e2278926d05b4027093fc5784866eeccaa3c43d5de37cc860273f9ce1e75044626e3a1352da3c65b53fde4521cbf8d75fa81da3b14ba0ef65bd2492b545d4e2289ba118e0742cea84380ee30fe18e4a5e756eda14b58b2397e6035cc6ea2b70c89fac17d1f521a934627d60e3f94bdd939aecc5668d2215993a6834a2a755e14b023ba0614b367a227d06a5bb92ec5837590a0beb73b08cde923479fea6c23f70fd60d6ba5a639cc175ca5b5f9b1368aa6ec31eca3d508a5e618d0192b90431c1ad16e41ad0c9a37196c7a5b05c6e756488ad2e71e519dc362b03ed0efd50fa613eea23eddcd90280622c1441ea541d64791625c3e41435fbb39832aa540664affb34acb1f489532b767543c14a85594bda9987851bb5b1ab31af8960e6e630283daf57e362a3cff8a8d004b73264f0c8ab6d0117f57a7093c5f6c2424d3435c06967158d4d9ef7519db66ae3e0b2f2fc3a87e6ca58ab2bcfe7d8bd9b538ec24dc61387cb53a88cd951796ac4f05ca8b8fb892faf428c8470850f2915779a574ecf99756e4f0b80a5b636fe4e152bb7fbdb06871a5e7cbb82711923b79768e93cc93016ecf90f71894a3a180f856e6970a239c9e337524e528c5c36ae403fc6e6befa171abd163a17eb3dd6726058a8856bcc568655658b04c7be2f2e3c2a76197cddd8127318a86e48f56cf6eb1d6150c5e2e560d30b7109bb5b633bb34a9b048729f120443781c08a2d87f47a4a11684bb2fb3be4391eb19c35251146638d7844ede1715309d00357a562707746a189f41b5f998effb29b320a91614da1d81cda3b3fcb7b20468a1888638a7690d0477cd067c50acd3076b382fb000dd3a65fa78cd3c78654d966a2c50189b8f4a9136b2accb150460c3ce1587e1f24a20533b5724418d1c8fb3c58a541a1eb3442fdba380e9f7ac5d868b865fec5eaea2044f2f489d58cf48672d9269c2f190e4a46c1f7de86ea226c9d267839d9ad854457c679cfa93e77402302f0b691bc4b3635277c314b707bf4164eea09338683aad35dad7bfa6bb716912731910ed8d66e0a81444d0b5674a01151496d4f5ff64b767a6ee3ad9a4fc5beb4db7e62d8f7c1ccf4f45ae3f4036e2ecc4d3756b823d92574841f20ac5c48bcf2fc40803116978818dbfe42042802aad30970b0034f14e6dc6836c37959de804f80cb40f7fabe871fa2cc1486d2c9f4dff806d33473051c7496ffcf816150b7b92327f772041028da2c281a23505425c5153f0b91873636e346eb40d77a93df8d74ecfc04c20015e7f72fee6fcb1b14ab0a9f76da10354d3a41fa67d2af427ac9fcedf4293d64fc9d76751eb07e4b07b74d145a7d6af71de5ec996ae56a5bdbc005c321c1bd3087c03c365965318cfb564628a62a5cb28efe8ae72d5c5949b51f4fae2ee67720cd30d6f3eeb17e18cf1c4f664035cad33bdec8fae493511f448b8a6cb1ec82e2fd3a6406442fd502b3e9201131ae83ed12e5f17e9df9641f3566c75e0504628ed8827a7526e06a219ac8a3055d37936973eee7fbd618c478a44e5ad9a731ff60d55186ff07fb5500a043d7e2daa124a40340a5729fbbe44fe6310d2e9d8df306db152099e0e8e55678adea9a7dabd98c0ac5f09b24cc5558b8019a41ffb44fc5305e6cfa5db0cecc0ee0674febe34de4277bd49974b6fb50bebcc21f1bf55e74d90e70179fdf62e91b29c321d3fae2bfc8d4684a657d41119a9aee5b964e587afedaa751d9caabb44c1a2d02178343454e76d47b3140cd355b8344ab95bef33973a7caf7b170e80eedb33700d84f40a84a4d8862d5dd52698c1eeb1080626789bc7bb959f65073f4416eeaae6e88d37b1b1e186f85858fa541a22b80b9f0dc2d958d0768953ae4d5beca55f9a36dc5ce122e04b03ab07a5334cbd63bb7608eaeed360e627cabbe8718f537aefee6304712e874c098e594b0a7f31ca5cb1f552a91ecf2995569ca5f149e8cb68ade5ddb33d9f8e4e514b8a17b9e4b4060c199c7e6ac2870ff43b9629f422caedd55a959c6e698afcd1d125ba61f600148beb5c890b304517131a991aeb2162b7f3561cb750441cc01a016afe0821af723bc64e3995fe25603c0b34234b9614e481fef7be471bc9cd3862b2a4111cd173a648a4fdec6decb7c30fbce46d788645ec305e765fec04ccd8d040d0dc7c5d72b64f3fa42bf7718f08ae1d4e84fdfb93cdc3308540fc52c25bff21929a22300c0ad3790443796143f13b1066a385db5c7ccd2d5657a2daac5e02ba4c6592f0a354947875675ed95cbaaf0ff956480ac42bdae2f20efd884762c52a48ff3c81dda0637d93abec67d4965cbda7e6d63cadde194d510de0533d20e4c4c6d1a59480469da1903ef7b1e3900a2244c4200d49ee13ce865489f617b25b9b17babe5a8901a020f3bc7be9813c96fe8596cdd8bc7bc305ef8e11ff09269931b4864ec24da535c83dcecd5ae8de37aeeef13e823d2a4dfd9ea697542bef54efbd7b5c34c84db372e1c42f5d30f776b6100fd571fb2e1fe0c7415c0a27ece4add67df38a70350d30121d341a6f740b070c5b229188dae0ce7d2ab4c1baf8904ef10d9e6908176893f527c11414ca28e22c90c10723da6434908d2c7302849a6b2b48c4a34505890c815020985664431e59244e4dff4c001d96225a8366656e2a6d10ae134cbe2bca6f501b42437c1d57ee696925530977db1c7b90f0675ab943d0b3b28d3cc2c842f2545d8b6d7850491fd43b307674fffb43e4d0f07a490848bea2e10d698d38de320b43c8661641790756096ce967d6410a181711437cc89c8075add3e0d94b3764a8ca02463a0bf6d9e83a5e9e65de1b4390328bb7d51fc8517fa061fa1992ccc329152a1725cbbfd96846a05fda044793d0fff96b6816d1307f8928393058b3a530cad199db78cc17b73db21b3a4013f4397cc0b0716b31812010490c92ac28ec79918360d60b49615036497cc9a2921320529e10b51ba8884d8b7888396efa3b658cb6eb814427ed311362747213e11f36180891a7e16e089a0f392d67cd05990b3ef6948c106e91ebc996199cd468cf267328ad7147b6ce1647c513293785d6cd22dcc66f1a4ac20a28f255a4e3c225436dd11e405df15407dac00d6ab791c9f637813c726db3b2fbeed8b282318ae02a0847480b4263383bb6d44b6f6ee2f6cabdbc4ccbaac776aa4db887150b4a82a85067059994277f3db6f72cc1bad78f0197ceaf58e03b324d6fa5c17d46eff68c02137c7806207bf4f2d66dc175a405555bcc477b044edd1d9d6b70f8500e410c7614923c9244e68d366c83dba6ab481b6ba20405851adb1100929303c88aa7fea2ddec6047cd293822a9dea9b8a0290265e3a30fe8fee38956f3c0a1aa0ca970d3d711ff780f352015b3a4b98401dd9cca38622dbfa43584c50885ad8dd4d0854338f80cd19a8f27badc75f45da1a27b0e072b7c7b6e538892b59286c8308d6baa5b56c4ed823dad05b7a425e6d59fa1308d30a0c306d550b8b2c0a4a04590813272b18fecd3a4b8dae0bcc9eccc12338e12d9da96026046addab6f7443f5c451a45401392f0dbd042ed3c0fece32ee249070f808a2495cabafc68d80dda42cf632f77d65741894df2fc4339e41764546d18223f2dd363100abc492a3b7db457112c631df2fe9c64f891cd8578b6ed5bd25732216e082d84e16469e60367d4e75869b93ce16e4c0d036dc22073ed880fa0f491bd84d9ec2591160148fd144a2c1da09dd220337463931a4f5a4cf2994e2c1e2e1c59eb5f3c259a918d7604643254d886235b537a28c6ddf0ef3eed3b1176db2ab63a5a9360c4e551952b9f5371640bb1a35702d22e29e12a7c62d9f94e71644d5e64a55ce99a0ac0d8c3071fba4b64e3c882e62915364711362a0c5f19bf82f4bfc185f489924363b9d5e8d47ad6c0d423d855cf3a9b30ed7e183e5fe891e26dfadbfd5ef516cd9da7e4e6b15cf9a2b56c473101ebb051acbd6ed1bb5428d68fd32c3faf845604ecd999cccd80401f4107112d7956ab5a85d6f8ce67bba2703d5cd41dc934b778d40045f71a8b1d5fd2cfe92c4534a7c97837a0dca60bad148d42012efac25c870f3258f04c874fb194dd34f181fd0dfdb2ed207e5cb029dba7cebd7c0841b35b5b5d12019ba999800198035c9af184e83f24665759a06456dbd7d162fcd7d5f297fa18e79c2aeef1650f07fbf6189f4ac9d63e05f6a16d23306d38450abb1e05da1cf78f0a64f65929c39e5c2a0d9d80605ddfb6f29732a9d767fa63e2989f28ad8cb5e93c67abe52a5950acef5c66e2da58507c12861fa1cdd26f1c4b80355d87d50d115650de17a36b54b8d7eb724d0679d28c09d418d90ab3ae2b9a161f8f633fbdc8641af6a4d8211b7860817e112f2e2c778098a1a5d42f4e8ed3b412b71f959922c93ae200462966582be7e80b329470747d27764f0fdcf57736649b56257969f78462ac24c3ebf6e1a962501f9f1072ebc9426425b00e038c667c84fd70cf9245b21b674d78b5f8dff999debf37172efd767dc98204df0de805759567ad8c3242017c5faf85d86bd27ffe3a17b35938c38a70dd02afc3bb0d8e75b15be4c98613ac9bb9ef4cc41c437586cbc680d852e5b72f816813d803b63b41bbb5b0e5c6b2c81509be80b0a70f3a19620ec8435c686ce56c6c43d937caf6ca33f0429ab1ace94fbf55a5a6998cd849835c7cf2002c2e5a0fb6fbbe9d46de5baa8de0ff0389d5f428d0bc1dd272b2d89f9b794680028d32f4257f75f13b54a1bba2dc498375400a5e12f5d340cb92053f6c490bc8077cb10d0e72648fdd6e54c85b467846443275062d63574a5cd913b2fa1704dfcc04ac669bd49c1764db40a76abeb464105e79bbae8fe1328409683e5d8afa719a499abc0f60ede3104f5da2d39082b4c20d9b9c7f89978cb1906ae95a28b6f8934aceae9fc0b1ad479170f7fa090e6bf9744876bf52711aa7731b878cfdec4a7afa87f28e306ec9ec4cd27d8b487c2479adb66cc24459e953e4c8c0e60adf721452b2d9bb80e81889b2aaafddfa80d31922fedef8f7976856055e3f1d5e8aba56e0e5d961dfc76ac433d2b76b28667053e4f0e647144d8d288fbaf54d8ecd353b55db88a6eaa6ba44066013aca9a3dfb2e3fd2f18c4dc71d86a18626164178644200ae2484b856370d679b38d965acc8428eeae5fef92680da0f1dffd128d85536a1d35e2328544657e569f91fa90e137fabae56715dc0eb03d8882bbe2be881efc6d4702ed5e17c09a51d7e5cb00cb159d2797f151bf13243df42f6cf322b4527944acc7f1f1bd3ee9854877b23b73b5c8312440aedde935016015a730a03a71cf8f2afe894be2d7dea691740731519fcf2884dcadc406f610ad4bd88721dc4e1f0bf0637b5990cbe8951e91b4c3c978f43715bd768f67d476a1dba1b00030bc82005bb751300421554815aa56e90d1afd101959ee864652b081debbcffbc8453f6f94807894a477e93b8cb78bbcd493e0b29955d7d42216960e380484976754824ceb7c36c4a9f98dd0dcd73fbf5ac2cb74bf87e79580c20fe1b391778035888542dd48b3f97f7ef0c304c7105a3cc86b52261d710bc885488535edff2d400a082a8153bb73408b62beecaa154ab8c7136b5082b4ae76e749f08b5db09dc31a38ad1f8a26c1febc8eea4e1356342816981bbe6f92862cb0105937cb507dec670002a3c0666d96ef9dab070e0796a2cd66d9ccb8466549fce3a58879d61a4e45f88af30d998b5ac236c931f7ac9382032c73972c04ff433cd8b77640928e7a476388178f3b4711ff9d10c91101321d7362f845878f3aff532bf66512ddbdcefd1ed2519f6deaa85ffbe13b48f13fda133bfa55eec26acbb0dbb9b62c445d84f0ed556a67f4fe94334968909d47fe21590445d4026d951f4cb506e48b9051c223bb0075909e8e2ac2c6274fe4d734a5f5ad0ddb364f166a4178821e0259eee31d3e3a45a7b117879f36539778de86fdd3d3937d984d12bb60eeb0fd43479edcc9f56afe7e16839b0a01f664447d9071c9b0c5985700cd714b92b51fe9c01340eb7f47d7fd1e1fecc004ae28c40e0d50107855a5e9a06959ba79610ccf35c4354770d3b093031dab61c7b8923270345f2ab43f2f697727e918ed1baf36d181f0402fdd309116b76678828d42755b9c577facb5b5d6da7613111111217bcbbd030f0d560d0f0d5d7b00787f36aa87d8a6e52b19ab700503739ae64133cce76703739aee62043103c6f719330e86e146e4751f76e1a17966efdb8eb868d637ba4c975657c233a78a609ce6ae76b392ac1a9fa12143c6e4596304420142012a552da79f0dcb5b5a8000020820fee27f7979193d6f2c799ee78d075d5c662e611e34bb9ca6b130f2b038f296e8784b76bc2554b464cfb3443a3a3a42f060a43c100e17c4e2ded416f3da6923ea7e3a214da29af44a9cf12122a224493a9e4aa2884a0a74c21139e1849c704160bc3ce5723bdab8e07402f092f3b123e325f7238b5c8b03c209c0b378080f6122dc5349d5c9a121dcd3827b5ec438ebea05f710315edcc3018d3e9c13f010878411071bd1588510a9222052452fa9229754514baa2855c4f9607c41168f713d1bd1490e6e890224f664f195441dd595f8139d2c9eec609d66a52e1efb6816cac5e39d66a55c3cfed1ac938bc7ad66a12e1e036996cac5639e4a1671d1f664f1e50ba32cfe4551b34e17ffa249b34c17cbeb87288b17879a752f5e142f1669723352c59e4a1271f1a24fb3ba8b1789348bbb7851bc9254922c22d98c1cc922111f7f680db0e8962a415709aa24ebe2cb1815219556255180ea4abcea2999a031666654d1182f33ceba5a698db3ae867879555a413c54c8e25582b298e28417882ac8a237c458de95b14c798f19cb0b31962aefa5b18cc943982bf1a5ca1eb2f81e537c268c78164e15d59598352cee73e9e2e35dfc6cef054816fff2a3552f1710ee4482055136cae2bda39a1c356bbc788e8b66e18bef749ae572f19d8f66fde2bb9d66bdb8f8ee47b35e2ebe6b35abe5e23b20cd62b9f88ea759405c7c17a4592b3c2da926ad129f2a02df70cb8dc8a5952aaae48b91ba12b58c02446bcc2c96f47ba9ca5e79215ea672cccb9b3dcfbb5649f2de58ea984339250a77be18a9a45623be18a12c4c8bea4abc8b4fcc85184b2dc7cc98548e71f169b9f09cb42a89e25357e249ab95c50b00972742b278188c42d42af1214681815190b44a7c0c8c92a455e283c02842ad121f008ca2a455e2571865a855e265609425ad120f008cc2a455e25518a5a855e2517c2a89d2a457220a1759fc87e29351b6c8e23d940bcb34cfc321b05c6dcacd0b2c73b24b4b3c8df1a759a58b4fcaa859e5f853a459f60645597caa89f85251464152a200cd6fc688020463e7961b51890294c56f441b9366c9b8f8ada859df8db62d3c23951cc0c573ad4abeec6c4795b4f194cf4624bee47eb2f8720e117148b2388bb8244242bce494c8bcf47c68bcf47ec47b40cd82715149164b2f894be921c9a237b4248b1e11eb6567847cd90165f1253794c50f0057a3ba120f822f954a9e782ae4c5a75562f65ebef864f1f4bb7921f22ec94b5050abc483fff0cb91668934cde305c984f95e809a3573abc489852b82e40ff3981306fc3703a67410bf1c6995f80fbf004d18f11e9e79062e6f2e1d06975a2e8d21b0b97c31f262a455e267462a1d8479f962248b8fc1654c57c2a211345b1be4f9cd286171c66881645b83dc51f0256c6950c2b68a3c3faff41080ef51b30d38f2c8720ef0333e3f25c031c7772dcf384dcbf8fee2d3acf0e25f88bcfc84637933338f2c970e45cda5b1fc461e5996f119231435cb186990568994e37c64f1c3251818eff3bcb1a43c3630fda92bf121a6b906165f121fc3a3a010bf84f825c4e76723c4287ee6cf47160f000c630ff77bf7799fb7f256abcfcf6635ea64916610d33c83432cfe65a7923488f268b7a42f3b59bc4b1150161fe4e253855c7cea108d22f12e419574f1116f44e627c647c5936addba93459f2cbe838137a2562537262ead2c6e4db2f8d285278b57011f4410ef82f882c0a5cfef2ba96e3fd5adea1df53ed528b6b2f8ad8978733896db16593cf83d3c087edf8f2cd21c04a639c43eb2789432dcb21e894ba0202eb182b8840be21231889fb3c601966031678830ea51abc47b78e6d148fc5010915051165fce21128b6761a32c7e14e2113ffe54b25b75253e48c627c688aaa43ca99e3b2b298e3fb9bc279f4ab22efef45349f2e24f469ae55dfc89a89232174b2f27900ce678ea4abc8737a3568927f15674a4c58b447c2f63802f5f66bf5cc9e5c9278bcf3e05740d4b4c1565517caaa85933d5e4ba7820f0a9a755e25ff00948abc4bbe0d34eabc48ff8a4d32af1189f7cb44afcf1e947abc4bfc0a756abc4b7e0134fabc4b3e053107c12d22a712c15d59d2cbb0a0e537018d4aaec291ca2e0f004874942a156652f02874375951dfce9014d1824d2aa6c2797c672025d3f393b0973415a95fd628ea755d9168ee58a068ee5cbfdc632c656b435c19b91026a0d11e36674cbd34694b39f7c9aa58db355449aa5b50a7b11cad325055c0e4c23e008211648b0488285122cb0689a0e89e439615a081d84d0210991e7eb746a26bf99f925460ccd115850f223971389662f564ea6425410654b69466df54a2d7be991ba6f17f89da6af0fed186e1f60dac3612a246cf0da2ac97a2fb16bd8bd4b4fa7d3e974c25e3336f2b899794e9853674b77e60c3d928dd4223fada269224768cb7a16e5f6b339bdb39e874fb806d524556804aa4230fc70b39f8edd7e37194a926c6f91b4ca9e2aaa0bb7a4ad9924f3d020b4e787da3991b0412897b44816b3dffc342bab468a342b3b92ed6b56eb4fed7925d2ac2ac4d6aa53c953afec577662f09c300aa5354e2f290c32e625e10d5d2d27a94a7a0a3ac3e2217b63b760c2d89fdce0d656105a83d25c759ab553b2c187b6ae531dea83eecc99eff6f405b76782753aca4d27a5dbe36816e9f7b6bb45dd622fe751b6d8ef59af5c418d144528d39b8632fd095e92e94bb8883ee4a1ffba275322991a4d2c24d36f3fc84934696b680d9a6d4d8c97956c2dcd70fb20d3d3209406a944283d8059d021739803902b1011c2913c910401482e2750107cdc69c12c6aae2badb2585b65fb00d0c83665ae6c0a24bad526c164d63cc18623b99c4cb2c53e33f64b4f546b96fd029077d26250196b2d900cb6193df80212b8c0032eb8c00a3bcc20c80678f84218c460861b5c4136dbf2c151b1cd33cc200848003b894c96052ddbb0cbd624b00882c5820a2c5e1087263296b276bcc7e6755dd7659fe3ba72e8e68d10211323466573eabeb50f1d5886db75d3eeeeeeee49cfd0cd5a6bd7d6d9b2b17ca7d5d1bbb74ba5b85f07285b1cad64da0db3603bb771db316cdb7adb1ad32d7a45b968af2db5d68273c2d4b18fd0b8650365fbb2878e040d2d69969d01901b7e9024c9f67da459748b56d96b63134fece81ff3c8477dd93a44576f442016373b7886ecf3b3555b6fdd4d9649d3d8a548bcbefacab29fb8e63ad5aaac0c17a5a4e79cfd3aeb50ab32a136603bcd9aaf46adcadec4a859d867ebcc111b6bd1c6825bd6a39cc534eb622f2e23fe0becc565c47fb169b58be8e6cdefc6fab0748b16dcd3895219174d5a26bdcac45984ca629ad5d7d55ddef5d9f4afec7572c1f292faf890ffe4c569fad812d555263211512e7e05973979e5d867b3f2ebba7e33f3358a29cf319ff293e76494a76014dca2828273545ac463b8b4593c4d5dc1b589578b9ad524bb8ad5c32daf169115d2acd92c6cbca6b8592dcad9b393932f9e4d71ac45578ba5d52da4a3a36383338ce10c6838c31ae66aa23c205e5c46fc0e42544920c6174d73b9774ca5f0ffb1c812358f196e6985ae1e1efb9352117f61b5366be5d9ad922669935e655f1969518f434e8efaa9242d12c732a5627f52c69f7cf5f05c3da667a6a7c652331d652cafe9d7d883f2ec3688ed11d22863b86515dac9cf739230ec944f4e7e613b55276fac4dd5c98fbdb93a19f2637c61f02838fcc903726a5e7cc8c79dd7bc500006c4821d2f3e9b13ab83c2a577c2265c6a36f5a87261759a852dd68a8caa502569d1bdd9e91d9b279c4bbe7a5c69938f1665afde8b67d72af967bf323626500be91e7a4c8d9aa45c10112306cd8a14d0352fb2735adcd212e5cc125926d96951ab32a30b28d5308670cdf9eeba2eedd7a5514ac575fd98545c576b528d627af081840e45f244b2061e246bd8e9ba40ab90122419e74905253df6b35abb7d5afa053161b418adb28fb157cf2d830862ce4c7bab751df534ed599689e073c0f0c31571bed77faf07df5ed903c41f2eb5bc795d4eeeb00e2f736353416768df709d423d35dbe7300bb87773729f997b37c77de23a525a396ed44e954847478707356d8f549729c464d593797ec624bdf798a737d2a2bac2c65ec184b13db60e14662b61aeec39dcadbaea29ccd50ac65c764b87d6e81f3a03c85a1ed29ec62d3ba89b34cb96dd44a85965f74fb6efa037f8c8f69dc4b6926cadb576c864619fcf26eb9a2ff2a41b5dc39cd9c63961b4b189bab183eacade66d7dae7e81fadea160d54f517cea6aa1ec375aac2d944d40ed9b27303799eadd2722a29c51457f090d9c6924714198917b96233dbf66920ad2a6ed93b5def54ebd597b4761d8770b71f1224c8951b7d701cc7711cc7515abffaeb436207cba1fdc2d1c6ebc23cfadda0df0d3ab6aa25ff001244488ac85c4d4ab5636f9c434382ea01472667e390eef673808f6eec565d59ed39ba653f1c8dea21d7fa9c0f871ac9759c13a6b9d3234d72b7a7cd936d6b54d3de5064ae6630a73e9802a743b08c5321e0099c0601cf38158225700a041c73fa031c81531fe0009cf660089cf6000be094079889d31de097d320f801a73ac0439ce6003be014079888531edc80d31be0224eadc04b9cda00b34e8160069cd60093a734c00b38ad02cb9cb6b0024e6780699c5281719cca00cf9cfec0374e63806b9cc20027e0f405d8c6e90e56e2d4057800a753e0244e5b800970ea032371ca025c80d31560049caa00d39cea609ce7e0fc14e0238e025c7329f0cd4f800d70136023aec2077810d8e61c0e330a16227343001982678856ab54340b388a532c60129c5e0143718a048fe0d40ab8c76915b0084ea98043700a84799c4e01ef38950206c16914f0074e8f600f9c4201eb387d02ce71ea045c9e1ac11d386d027ee294099803a74bc01b382d823570aa04ecc481c0304e938033708a040c80d31f8c81d32360214e8d802f707a058e714a0437715a040c734a046c81d3216019a73eb802a743f08c5321600a9c0601c79c0ac1133805020ec0e90fb0044e7d800570da832370da03fc72ca030c81d31de0214e8360264e75808938cd017ec0290e7011a73cd801a737c0ac532b70034e6d80c953207889d31a6099531a60069c5681699cb6f0024e6780674ea9c00a389501ae71fa03e3388d01b6710a037ce3f4057800a73b3801a72ec004389d022b71da025c80531f38895316609ad31560244e5580f3a90e46c07370cd5380718e026c804b818ff809f0016e027c7315b67910d888a760d453f8e41c3e1d05aff26c97a1d145a855dd47d5e196ac56e92294fb430c31444f1dc2a75935ab8e4a8065b800d5fe103aa4bb009186d0c96d74c232844eb00c25282c6348a587d5aa6414efb3782a4982f7593d9584e27d964f2547f03eeba7923dde6719a9a408de6701553204efb3822ac9e37d96502577bccf1aaa2408de671155f203efb38a2ae981f7594695d4f13eeba89239de27752a59be4fee54b203ef93ad4a3ef13ec953490ebc4ff6547203ef933e95d4c0fbe44f259d789f3452c90cbc4f02551203ef934195bcc0fba450259b789f1caaa405de27892a5981f7c9a24a52e07dd2a89213789f3caaa404de97d1a96404de97d9a92404de9769559289f765782af980f7657a2ae980f7657c2ad980f7657e2ab9c4fb32462ac980f765802ab980f765822aa980f765842a89e37d99a14ade785f86a89209785fa6a8924abc2f6354c924de9739aa2412efd3d0a92402dea7b153499cf769b42a79c4fb34782a79f33e8d9e4a1af13e0d9f4adabc4fe3a79207789f86914a1ae07d1a4095ac799f465025f3fb34842a49f33e8da14a16e07d1a449524c0fb348a2a3980f7691855d2c6fb348e2a59e3fd199d4acebc3fb353491aefcfb42a29f3fe0c4f25c9f7677a2ac97a7fc6a79245bc3ff3534922de9f3152c9212af972fa145acdeafd99a04a0ae0fd19a14a06e0fd99a14ac6bc3f4354c919efcf145552c6fb33469584797fe6a89231deafa1534921deafb1534900bc5fa3a5d2a32284bba63a17c4398ee39e7d361cfd6ec09d5baa30f72830abc5e261f5b07c583f2c232c2056104b8835c4226215b18c5847a40eb943b6481eb287f4217f4823241019440a9143241159441a9147323a323b322d191e991e191f991f192332403241324232433244324532463247347468ecd068d1e0a1d143c387c60f0d2334806804d110a23144838846110d231a47333a333b33ad199e999e199f999f1923334133423343334433453346334735746aecd468e1212f5fe11dade6659c01aaab1366c95d7b5160951f12601523506015a0116095a01e5845480458652804588588075629da81558c4080558e3e80451d0f60714707165b39b0c85362b1a70358f479028b3f1cc0a2910d60114803580c72028b4219c0e21006b04874012c16358145230b60f1a802784587027865670278a52501bcc21301bcd20301bce2c3045ef979005e31e200bc02d400bc12b4045e1162005e195a005e2152005e29c281578c6ee095a30460161d2530cb4e1298a5850466e1410066e9c1c12c3e4760969f1bcc62c408cc02648359820e8059840c8059866a300b51c62c3498a500988500b86500b8c5066ea9815b66700b0ddc22835b48dcc2c22d4075d52f02b710815b86c02d02c02d01c02d31b8e5a8aefa33f00b19f8050c7e1103bf1002bfe8a9ab3e00f00b18d8c7cb5990029d1a185fe1d2a788a9993300a6b764318be8d378875bba18d168cd00b818e566a1d19a33a7f76780e60c4beebb78e1962a2cc4d18469189f09d357619520b00a1058a555572aaa23132e5bde50b6b421f749328472ffb25a658d23658da072223143ee7bb89461945b728a4f1c6e2943e748864e2565e8b082c8d0610541f1e1563c338a0fb7e652c6f1aca15ff70ef38e3bce3b9e51862ae87855eb8c0beee8bebc0c8db8a2175c83a7aeb8a20e0206102f5c50108b0b0ae2e24e2f081840346aa7555d102f35ee25081840bcd08cf271d4aa1317adea088cff282ef1bffc6594e2cfa9e9507ab8dccb0ba85b885cc2706b2e657e6af0b4aa5934f578d6f4b70457e332033461528aeee9a89230dea556ded3118c91d5aa408cb3ae5844fe70672e592d4b6705b9bbe01f1dae46c6a7ae582d2f73b3e6059e352a34b8cfa5cacf4f25592dd6f8a3e5aaf2c36afd604105477265b558ad2bfc0c2187a5214045372b396f966d5ce76536f2cc70ea62d52f131373040d1375d538549787d19aab6b0c2ccd72d79e6dd6f52141829c0dd94e37bb6ddbb66ddbb66ddba55d9bf64bbbd5a6b6a5526eed33cd5eb68380e40e4a8081d1134860e023db3b619b647bab69f7da399ba8e4ed8fb844ec1b1395e44629aeb8dea3f86be481a38e52d09a6d14af73ef8e8d4b6cd74629ea3705cc9a6c9482d6644bf4bd518a2be85b5579e0881997d0be8d524c416bfa9aa6699aa6699f42a6461b65eaca8eafaba35425a790a9b9e2529dcd0754d3bcfaa35d3d6cd5346f07b597529baf5f3dada2d86f67d726b93d1bcb5a6fae8dbb4cd3def7b4acb499f31171ae0bab5849cad778d9cbd26f4113e6da021804dcc1a59f130608a7076d48a597615dddd7f55a63ac56e53074d137a5da785bd5d6562cbb5766cb2ddbd1ab503851828dac651d364eb30a25f6d2be5e572a63187655cde68a5bd3344d1b675d5525689a57a9d5b07b3b8f342dbb75d5a79daf2cd677192442d76a310cc3b8abe28a75b3c11e0eb7fb74598e5a7b599ad95f17b5f6d602a1d9af6398c56e2e5afbe5edaff465ed65eb088453934fb341ed6870d9157bd99c5ed24dc7f6baccc9a9bac26c3d21855a5d81797e386a137b6159dba6a51bdc3ea5252b6e9f963797a470fbdd6dfbbab26c6e712f3db44629d6e6b58dda9859cc5ed65abb021b6beb6b113a29adf6aa576ad27c9b3ab9c8e2f6359b75786ae8e6ac3169538cd119353a5f57e7eb74a433eccc75691386656ded2c7a787cf03c2ddb7a2dc3e6b5399f8d76ceb2c79d30f3f35a8047b416f5f56e62184531a15dc75c6dec5dea70af929e5651e735ce7a8a4333c9658f3e35abec71d5cf59a6206397f66de346ed16973957a581662f4cab40304dd3b06759a6651866ebadfd92ed2805233e363a2fcbde7dc904354ff3d4eb4231aacba4626e555dd749ebdab7908e948a955dd3eebd4cab2e6a93e1fa105f9a66d1b4aa7732ea03529fec3219a669d5f51429dceca58c8deb1c76f232158263491ab5aad3aa2bcc32a1aa532f1fed23c32da711c6c3cd5ef64ebe0e62ea3361aeef888951a9feee652a5fddcb3b8d4863a9795e0dae7d3983a6922392c0b104131368c28927ba13c66ec2d841dbaaec21d65a956570cc48156f44401208d6fb0cbc619865ef2c9c390b33bc8507ff7d59883dd0620ffb943449498b7819be0ccdb4d7295fda58a6b2976bcc514df466c1d3869d1ce68984894e9ea5da632d8661d895eb965b02b3b29868555327544c8d4c18244860b2689149ab80e52c869a90bbaf5c72c2a53fb98eb40485ae743648274c1db3e84a37b8f49396acb8f4938625293496e784e91e74b76fbb354cd3bceec32d53da53cdea34ed1cf6288f9baf6c2c3b67df6c145248c1712f75ecc8ad23db973d726fda6db44c47282c1226180311c670847c8d39e81bc85ad1693f6ae8b980d4208650480d5da801c864b5fbaee9e86744cd77c2d4ccefc674d574ad5dcb1acd97ced8caa2070b408a64ee5c25b5cd1b65be25ba310a1c3b6e4c373aead5cf886de4ae61ce04468cd8ce9da6660d9777276fef284edfe8fae1583a53c03961366e1471aeacfdcada338d288ba2fa0d370ac07fd7bc83a34c5d65dae7175357d99fc8be438a2b67a74082b22a84063ccca29c19a581066828ca25fd415b99ad29694fcece6dda11cd2a5f7034ab5431d1ac32a6896695134912cdb23b38983c19dbbfa862c4c81cc076a53f2112184bd0902d694336870c0f591fb22c8341dbd075adba7376731e359944d50b43435160d2e39c4c86eca46ab651dbc32db552b5dd5e653d5a27d0e4ecf6d19f42abd92896bd3bbbf014a99a952ec27e7ea92b08b76ff1106d9cbd02ea17a19c7db6b4fcfbff81a2e87d04bd7f17b128969d3d71fce7b9b8d8f0087f3373cb81c8349fa04e4e545454545228a9544a4aca69da543a2d79423a3a3a415862c5121e2c11c235424a092cd93b05e22b7d87f9cb8fe83d7cd779a82659448598e6efa549945611a4cc399dcbe0eda8aeb622f05684d2aa24ebac91f3d12bf131c6128527a3f464f11d915689016349b70f092e6bdf0d865db3c1342c7531acb63eafbb76e88f7a24779b92cb8d654ea73585c14561d08d4591310a033a63fb6cae90662b38b1ccce5a16ee3f47d4553d750255c124b57a017a84e2a08a7ce1309bf8ce5d199a232ac97d5e776a92cb744aad87b665444418868d445c6092dae4b1a389ae6e916b10517ea259417235dac9b5d2fa091c0293f5a615bc461dacb91f3fb9be0b6770fb097335aba06de186217586af4145663a3a3a3a466aa676596bb35fb6c3a1393381727dcac7ed24324f44b133b4a347b3ae79f5eb75482193657e7cd43a6776cd39e7b47376d381091d9ad0618b4b072e522a606920ac8f06bac2fe54332f042368c184a93a795abcc06a5185d50207560b1e58cb42792a59c9edf521cf9c994059013375eb1ecc90ab9d70c8f5f4c3e1860bbeabdeeb65682ad91d9c4141341f96c1317385dd6bb27b9d424e34f9595718b6c2541328f3d8f1cca389fe018e6823ae3c4323acb73a843de7fc8b6cc4aeb1bbbbbb31da5974739298a6655a5da580e6d8a661e86a7777dd42676da55ae8ea290d43add75fd01ee79cbd825badb5d6d689a4d65ae93542bdcd3283ad39e7a2ed02ea69ad9a359c307d16d8b062cfecafd70f0912945bbe9e037b4dd77493769661bf3605ddd40dd7e754b16a4c554a265553007040b6cd650c0a2edb6daeb0d7c84e9a2baccb6363ad5d6bad954e0ebc46ca75b976a143049ae99b88b5b55e612d45c2ed1a5a63bbc16cc2744d461b8a6e56b2e697ed9a9c9a02da94081468d6cc22bd42927fe8cf192e7d97d408ad017242a761594a4e65147b79169bab0ae61a82eb943bccb3d8944ba49b3bcef3bcccf33ecf62afcb5de891bc8ce459ace5aee4599c6197cddd098a97ea4415d08ed784a9c77235416e3a8bb0974ab3446b83eb288f2738b8f4dd35b5d6603ad405978efd9933b4fb74b49765e99f39d352bbb10f89faebd7916ebee4a654e47be78c15dd7c292fa3ad1e554b67dbcf469be49cf4e8199b7409256a1693f959be7a7450825dde9ca4ecd43baa4d9a3533f6595399c55eb7b054ccee97e90cd555e522632ffb47c6eed5a14a56a3baeaa1fa3bc96935ef12b3db777d13ae1b6b110e01cdd84fb4a083bd4cc918296323f6ce6a586b15f654ea5e9d8c5db3a4ed2fb4c676ac1e7b95c36471c75eb59864782c44c9652d7a91cb5a94b18738c375aa666807b1f60f6b156baff579c355fbb6d5daaa4be8fbc2494458cde26c5759c6864dc9949e0e486610fd6ca6f699ef78b1db5f31c8c1c7026c5b6bade583d5b4c672726b3755d65963ce19deb154aeb69322733d9487b6e8ce1117464593680e4d2519c3b0989cbd8c91310cd3308cc33ca4c85add308f9ab5b1ccb1a50ab3b1182e1ba8798c8268cfd1142a5519e31145ce5ea6804796b3b1cce9eb164b49e28622b9df9a92a9f9d01edb456bd6794dc89cdda359fda3fa3ebd4d4f7ab273679d535420adea7151cc04c6c1821b02ab01d18ca5964f534c68406bd0496bbd2663bb5144027bff06b3a51479f63673f2c434b5cef266f43a424fd93b5ab557768d1d51ab6ca6b53adc3f7678704bad5e6b6dd77575776f6f4c53b3f1eaf1a80d52da3ce97bc76372559d5cb690dcd3aafa0c7790560169550f6eadf7f1e307b76d8581457279f7c6bb825b6af9a77c5d9ee7799f8077cf3b13b60ab406cddeb5e2715f67af69df58de9b4a7163f9ac4dea619887395c4691b91820fe7069b3f7edf3e3bc9ccc6dd8350c84874ec5fb90f013c7f66df4bab8a59763625eed3ddd360c630fd76ed7843439abd6b31181d6a0f9b2dea59f1f860fd02bfbd0bba5a9ab4bd3b42ccb0e1ed384e4eb3435b37d517b599109651e1f6ee9cd67f759f62dfbf1ef0dbe337c805ef50130bdae77e3752fc31b2e7be40dcf8dc33979c33abcac8d140674467d86696ab67845a70dc3e74b4e7171cb944aa69253b572394a7b878aaa791ea679d27715db9011a251222b68c7d1871d9a766f2a65d4444f251be568473dfafd2d6b85e5c1b1041395e462346b356bacd02369c94db51ead6a6c7291b7eacd3b4f5345bfb2426b6c73468c2ed3972d59673ba0b9b726a7a3be6e93eb13b8cec4750101a89b2fa5ed3962ced48a4da9ca4ccc4ed3ee4da5fe36aaadd1ba8448abaa4ff5f9b9d427759f525d2553b2c22d67517ec228d72e9a45b3a8b3607849a5b149463ed7a78d7c7ac827cbb49b532f554b5a551a499a76ef0d532923eddaeb11aacfac4598e4d74af8c6ea03855bb6cf916615a927e2f3d3ac1e99781b6924cda2b9d227c9f5b6c72379adda8e040e4932bdf6a10f4ca19cc3de7bd945727d13e1de79580a27723f91fb03532897393f39c7750c48ceca9c9f8c6157566263fdb163f5a944c070772af91d7c7dfb54121cbbf452f55b1aa78a34de27e4469f3a5457b512559fea33a4ddd45f7d54421688e5b1ad56554ee7d6a34a86af5a0dc718709cb57ec395481daa3e43651d1aca52f729d5551d53bac22d6fbedddd6d5e090bf7b3d69e133974f3a59b3ef2e4549306d59e23ab833c70719d6ed131f2cbcb9c4725eda13a514851df552079722c15e4e95d0a0d5f9c7dd6703a63141af66bcb70ea071ae7d16583c36c9499ce4ef952a3a8ab0bd32d82a2a8b5090f93d61225b9569f6a73902b1662c0936badef344f6b419e36c81cbec2bee33cb00d17e85242ccbe00eede69da7e4b70f746d511eeccdebf77ff469daca8a61b45999a1e999e4a52a09e1e0ad433621c75558f6855cd1c15b7a44040cdb21fc21dab4c78b8099f9ed9825b52a00a1481ee9cf70eefa8ab7a15196c7fb8b4d38705e1622305dae25a2b6428525480e25e215fefef6656815261aa2ad084a9e77edc721e45d12c9d9b832640d9e6691a335c9e7e79ef1fb446f7eef71af6eff6df58a24e428da56e2c69f721c165cb7d37f6e1bbd9547c365eeade316cb31bb661de67efbdd33e1bafda994b240d48a6adac85fd43d3b4dbcf460b8570c1b15badfaba91bbe5c6f23bb6dd6ee3f6aca479bbd1e5ed7aa7b27d36daa54581353b7291fb5be43e4d6dd95cfd8d677b769ab19757e96596cb8ff432cc53bb93fc480f444d163759d9646193d5cd99c4646d3f9908502200e9f35d00ae2cd3d359043da9fc322dc322ca2d5f19cb1ce088ba6acc61d8e221a5316608694c52c76b046d1078ed1563f984afd828f8d95cdb97444ec557ceb08c0d4c7380234e18071e523a77bac5434ea57726cc4417d10f8570e96d0d0e144619c32d6564bc8c6218ab78c83dc543c2a3b08f2bb24ff19a2bec4fd8c715daeba7788d69bc82be8477b41dad267ce77d36553bee3e9b0ed35c354c839dfb6cb8cdd21cda839f5a773f1b4ceb0cc3347f1e4e556a8dc828d75f17c2300cc3300cc3300cc3300cc3300cc3300cc3300cc3300cc30fe803029f00c280b6b52f383dea8891ed582992564d8ae4b2b60af6baac0562c256c15a241609bdc54d87ee0e855c1b41300cb311e4b291031b3c36726023888d202eb87cd0a5f2d4a62aecc1ade9ba0cb320bb8661d967cede9d65d97565a7e9ac7603e96677ddcca19446f9a9852022225e8e892288688b4c3f41390891801c80c4618b4cef358bda4cb55645a09b2fe5bcc24f7782c2cdb6ecbb398da573dc3b941f5cee945deb5ef0d4430edaa0531acb9c5ce25459e58706b764b56adc61aafaa1d13da98e68b4e64cf853884d0f8d5a05e11db873dc4bd5d1492773a6fbd2c5c83e849901e24e261d235af269e4aa0a70596f6902a35c66d771b57269c88bbfd4e8421280c23ddc527656ab5998fb0bcc62b5a8b8dfae4d2ce8e421d79314ae0ca1669dde97a144c650b34aefcb58d22c0edb7c8594d425ad6aa256554ac19ddb7de08b49860704c4e13fdcf2ce5eb54b61366121b2f8d25f308c09aff2ca65e89cbc7f3a3a21914a2de1103a43e80ce1e3050610f5b3e1ae5a2bf9f218a777ed7181d2391db1b4b8a5eae874040308964fb36ab3c29b9a4b87399a33a6b7156a4ef99c2c17a0d4617ce60c771942b8868b15509a35a70ae57d161942a89f8eb831c56788960966881637727731c2308c542f8cbc78c12f4af885d08b9438dcd245c845a892ac56a9ddeb22c4877b7fc28282c2a54502561d95970a72a93a2a513f725f54f1aa77c273083e8787bca07672efbc069f6ede0977962143a7553e4eef463be4ceccc90092b18c1ff393a64d3eb764b558ad6671409a95e269564a0968162816e81528126a055a054a050a44a740a540a3408f5028d02750275023b40994097409b40855024d02fda146a0442811a80f150215427f407b280f68109a03ca43ada040280d688b52417f5018d01d3a05f5415740755220850982509d00053954059405b405d405f405340654067406b40a5a036a037a038a03aa03ba03da03ea030a041a043a840e8116e1fd1a3c95a457bc5fa3a792f408efd7f0a92445c2fb357e6a18199a3328efaf95c1b087d079d7711c07e6eeaa57d36b5e19783a4d670193e02580e2f40a7804a748708f532b60119c560187e0940a98c72910de713a050c825329e00f9c46017be0f408d6710a059ce3f409b83c7502eec0a911fcc4691330074e99803770ba04ac81d322d8895325e00c9c260163e0140918c6e90fbec0e91130004e8d809b38bd020b714a045be0b40838c6291170054e878003ddf72ee52721ca49b851dea87b93e9245c9e328954de7c7a1f85290f9a4d46944cf87494d33d914ea787e5cd27d34fef4cb8df99f01d454033ea33df37468d3c7066ab4c9fad32a27494c9841aef4da77b027f1a7380478d39421da77c7ae312248d27a750e534962927279f12dfd7754894a7db9dbeb14441ee7e75a7db75b6c3a9eeeb3a0ff7a036a4ce7bb7c5dd2e22914ddfb987b71fce77eefb4c1f6a2c7332ea1797a77cf3e9f366d20a0acac94f2f7da5049e980e1e65fa34e198ce8d37fad967bf33e16b3abd84511f4ee986f4d38743fae9766e5c82d7ee4db873573f9c12291c4973ccf19d5ec7297f23f7797387a76ad2743824559ef2762fe4b87b397bcf79bdcafee12cec9e48e1d091b25fd7258476d0344d0bed104514da65521d16e9deb8bbc51d69bbb66cdbb6d6b47b53a97fe39e2d00e527a7df1228e3c9e70782e0078e22ea208eccf564ee1d0ae368d7c63961b6d1f48e5e1486e245de0ee29b99b79c2164ee98bd3c14c6d96eb61c2299fb69db727c323796e1a7fdc59a778227cd7503c3bb55d2957dde0ded22d3508b1b6a71b7b1c491b56b63a9e3656ad31163ea4ca81397c07e7d01b96ba55d88f55dbbae7476adef4aa7bd6e6c9dddad8591f80de59bca554efe3db5d23d3c783a883d534c7f37ba87372108822058228d60aaaec2b38ca9baf2c297aaec61af54c2397c89b3179ee47961e879e196bd52297cb965ef226913c67b18862727855d77f0f648bd366d4d39953872784fd36e2af597aadc8da578d3dd338207ed0247f61e2ed172962fa0c52b9fc8dd73a288a254e2c1a354e2c183078f52a9478f52a9478f1d3b16d07296d174338f68227b2f9bc8dd2fa65de4f9dd5124755e479b84a1789167268d10c8de4b08e4ce5e1e09e3a04efac53728efa409e4eef5da9046d358e2c8dd696a189ebcc365f7d378f270cc7172d2982345474c0e1f934f6256706991a0292a9f37a27c3b8a77f01d88121eec4094b0f3405114bf6111044110048dc27b61f82ecc89c9e03d5ce2c81eca4f4e2cbfd9f37e721a4b606a4419cb0b9ac6f2e04963a9ca153c0ba6a9a58e981b8621781386231863c4c591bd7b770cb5b8658e91ecbd7c227b21f810779e38d4e27ee1186a71bf97315947cc3cc21193a1a02a2d16d963bf2edb8dfd0d671bfd6eeca0a91bbede592ebb87eda665d865e7c071dde307865e48fac0df0e3daf41efe3388ee338ee86dffbe3b0299cc36d9630fcc2ae22dbc8af355c5eafc5ee39b673a36da2b51013cae1844a3d051ee527295fca0b8f9ba1107378a3e48de20d6dab4abf636982f730af543ae9d6f4af74ec5382f4d6b46f47fe3ef0a611c61eee37822f8d39c07b638e948b7bd85c7aa74e4af9bcca494a0a85727282429d3b0a45fa48a88f443a9d4ee7f0894422914824395ced3b728bbf78f68a04beb17fdffd6cab4eb787cddeb8ddf9926a900bd73b904b650e6b7f97bdc3a51359d3b697292e4c004e3e61df8dd9aa32897c7ae9bb31274c89934f47ddf46d2c8fc8a79bee443e8d65f789af9bf09c30f625d2b50969da72df3a6e2c35562aa77e823bfadda013863b9d30d5ebacd95669375d37ce09c38da51359fb36ce09a3659fcfc60e01d5b192db96fa1a82c3ddb67b29e73850e5df67f8772146f9c538276fd30dd24311d06cbaf75e53cd263c338e0d4f71582ae1f0f33302f5d269da844ba59fe0d2f47904a48e2aa54ed3a5dfdb52e92794931ee2d2891c1efb94b8ef52a9f432951fbe2be17e473f1b936944404e1ec18b5c1a1bf512268da578c3f412e98d4b1135e298fa250d64d2677622b789844b37a1c6d2583a91492f4dc74aa99370191e6584b1875b7adf48bd34e6481d35e638bd7413d6b1128e3a5672ea8ea58e957cfabcf89d3c95eff394f0ce9da63def86d70bef4d4949398753eebdf7de100ea4d34c227d7e37e8bd6f1d0987db762fc4e54a0e5fc2a513b9745328d44a7f0575574e2f1df51554f812c67ec2a58e954cfa47226ddfc6d20b6f34e6d9a675da9e24d258d28bcb947c71ee8733274c89827c53f27d376b5b3a2fb0749aeed24bddf9e2f27bf6f0a4236686e1870be2d8568107df59effa70e884015932d86f9c95ec8ddbf1cd1c9e4eaca434ca08a6ac9d346b787446bda669d79ae55dbb77e70c1786e1bbbee93a9ceff737dfcdc9dd28e274bfbfe97e9f9331bc75df67633a463a8d38d8bd635eb97d1e1b31ec9443ace578b446f7eae18d346760df4c1933957087b39dbbb977142f8934ce09133e27df51c4b927fde69ea491f0c561debcf7e78d38ddb777da36de09a39d1bef84c1b6679e8e8cfdfa1cc15c4b14f3f91bb2ec5ceadaca8b8b576179cbe9a69f70786ac22724507eba498993a34ed3a5928e5c7a1762ef28ecfd8449228e481a6fdc879f594736dd87d87414f672dccf8cfa099bc652472e4dd771ca77148df80e1ec7081eb48bccfd1bc191a63b3b614e2261d27b984ca7e987d71bc36c3afd8ea039fc09a384e15934ef7435afebe887533aaa3beaf3bb61fa490434773f95a752cb272727ef428c7a8769eee87783f47b7a47228d22e9220e78ee37e0c88d3999348a38a473bf219de3481f683f94879e1762ef441aef84391d85efe8b5ea74d497c48b937ec261f813ef84517e7d09a0f9c54d28a8170fc71c2f4e1a73b03c2c759c72f879142eefa8e3945f8cb3552b3f61f97c0b2e6d5679fd90a00fdf1f0ecb685bb532fe525149b9f6f9299192a2a5681aeaf744d350bf299aa6a14eb47b53da984a5dc329ed5eeddefb4dbb9af686d2692e3d1cbd538e7b56e29ee1ae84bf2c3bc1285ceac8a887bf3fe179f3bd9f1de293c0a366af4e63d9fde434f504cfa3caeea60fa7f46c2cb9973ed4c3cbab1f0acacdecfcbdf4ce7e38a8ebf493df9c48179fa048b8b4b9fbfc4ca6109bba11e7e4e14fc6b1d491effc6e50e39d30a58e532e73c63b612e387aadba63a9e3944bf71e62f034b5843bf78753f3f712eefbd008da05ea9d88ba8863fac96f4ce39c30a69fe464d428e2a07ef21bd4c5a5768267abc2a3f06cd5fd0997368737e1d2e6fb913eb1f71269c4b90f6fee78278cf7f0257ba769301cbd56790747ef1b7370a72fbb51c7297363b6e15bead071ca50d0963e3903932c234f2479289237ab5de3c6926a18db71aedbba1d3dec8e1eb67f7cda7079dac303123c1841b24513d33714959f1c959203fc3d78ef1d88bf7b38a4bf4bf27d2ac91647f2efb024d73b28c9f52b2fe2c3310441f1033dd2455c963e8f00950ef3404cfafc1240b30a78d208630f1754e2fbdda184378addef588a374a277de61da5d88d38a571b68a34decfbc235750e5eb6133e9e10d9593c61c2aefc61ca9df1e36df87b804c71e36ab8cb35528f7529f4f41a546db2a94f1d7ef58a26e5b851acb1e957e4a84dbe9db690befbdf34edbe681db16fede3b6ddb16829bf7d0336da3c9f40d6fdebdb1f4ee9dc33d68f6b6365c11099aef69be0fc7ef343c97bb3c00c931f2448287a2dcb5a7bdbbdabb97ddefbb8b417a0dc91644b2f62ec4e58eccf2d267932b0f3f7b2cb51c9e74163cdbf3be8267afbcef212e7bd8ac1d3ba86963d98120f81093eee1f2662ff43c5c7ae0785bf581638eeeb4f4c61e3677a3c6cd3c752c8fac8b2eba30ca3d2a0e2aa7f4a793615956eb7551fae3baa86845a1b318bbd6d32a8b6159a65df3c656612759b8a597b18766c832ed54c3b20ccb66ee8464cd276b716062dbe28dc8a66d1a76ecf3d34ed3f5f616d32e0cc3ac76d5d32f1b7964b962f6eabe6cedcc0bf4e7489123479e6084fe3ce1089233a0e1d302f4715dd7d584dec17ed894c7a477bc35784c9a7c7348d9eb695713f46a623396d598cc46a7565417cd2e8f2271e59d2bcb3aa22e0d1dd1335bb16ba39c10d882399bfab8ae223f3b49e8326d1b4beda6c700b92eb044ba21d7dd66f6d8f5dd74ffde5ddd95fa6c15d625d20db9cd06c9f4a0d5beb780d8a0fc6164b89ee5d16e74b96b5cb5cc0a3ae33afd6e582bb86eebb4aeebc652474cee7e9da6dadcb88cc91ccd4695b5e2fa6c2e21ad0bcdf3b6efbbb0cc03c3f0bbde4622910f998459e4b08ab0f565f11165ea835b92e92707c42161615c92ca097143995a10a553e2f96cacbd78ae9c1a0de7d470479733d2ac22adea5aad112be852d27d15c9dd433dfdf212ba862a57843b423157a45908a841930a24d352a58a4cef4d963da5b4a644f928513f32a5da4b952099aab44a54c82940a92a326d654a4f9e86c525a418b50eb569373cd1b21044f1b6ecc3340ccbb00cfc529e86736a3aecbb6abaebbb61c9768876154c55290b202cd76686ac0d210b9f09b30dd51e7db621faf2f2f93e1b4bf18abaa1d5407ad55d5f60c274bfec960f918a5dc0bb2e1f1736587b33be2929b5535252522c9d295d4c11e891e6bc30bce79d748b4b3b9bb5436b64c77ab4763b8259a802a8b4cc7496554d32846646000000080800b314003028140a078462c158349a47d2623b14000f8ba64e684e9a88c32487510819438831861802000003020382d90600503c869f77da58b2d8e5bd9898327e8c918d794c7a6954ec9e76207a2f51458ccb1d2b8dd94ae68a468d93feb6d571b5dadabdf5859cac9e564ba554909e68464009e1b6f13b95e963b6734ba22adce917986d926e0129f1ec9e0a8edf7204c6295aae04d4219baea36fd080a5106eba8e6582a4d753d2fc0e197b40803a8321eb6000be3de69b80086a357f3d800ba983a3916f0cdd976e3e2bef696330bf1ef7f47e61abd500d57d43fe1ae466b71860827033a81b36d27a5c64ec8f28c75eb3d8f94c3d7694b4fb16f3f335e533241aa22770c8674fc301bb0f210eed5c859b12dbe8f04bca102ff35981e6a92d568c6db84cac238e0ea179ee0ccc5da27d99eedbb9a27d30f471b403d34e9d93991436f8b671b60f5e847ff04c7935fc98e76368ad68a5e359d8529934f6eef0635c608720664ede95b5d65319e76d8502575059b84684727c0dbded33bdd229e94c3711f9952a5cacdb40e11f8029f61429f16aea6e2c009dc7ce32e5d37958806ac5a3f21de4fd0b942b20cad2c8c10009bde03e0f284440275290a9ee3c82620a57b60d1a278f6af13ba66abfb71ce38835c0fffdd00e35f2bece0096f7034b2a947828aa901843bad0703084c4f197504b49337988c5eab193a9b0bf88ff26ce2dc38836c269259aa9b1f963b649c6bdbdf55be5320cfad262dae86f15439c188ed75ef37a7cc8c6c10e1662cb3a46112712dcdd24109689847b49ce9e2e92b1227dc340f1e6d36bea793abded844b0c6e084949a310e1abfdf8b382b726288ff00f87d5ca0af0292c008a29014785287c00227e1dee833ab4070afdd93045e137732fea2266437b87f4f235ce89ad542ac3b054317666fc2405c455278d63cc5fc1452a04481245da84ab013efb77c48111c3b711389cb6d88e28ce4560cb715ad227b60ef03db7af5d18dfcfae6028b0f3d5c4f5c5c7968c3ee92ca043669dd5e3b2ab1f10f8790f55af17a5e6f8b88edf7d6e7c931041e4ac892282ce089b4ab2330912ef7e8f9076d914c858a5e12b908d5bfe9342d23460792d86b0ac159395f0dd319a2d453d9e90448113d702df86ee4b2f5008cd6d1ec97ba27dffcd7e4b7355c985029eb12868015ccf0f648d008409385c2fb2e6c049c80d6c7418155ef1b3443415d666e10293d2d8412a341031a5c2926648e0e2368fcee2d5d892dbe36b0c870c06d26e6d0bf27ac9791c400adae3df62b1678d897d08b14ac132f6ba9126c0f62fdf9c4c7718b51df5f2ea6b61a836fdcc24bd8245f79ae6fd1c267ecf1659cec161be71661942265c988b08eca1c481a5821951c761a2680ffa1cc348e2d099063720255fe03e13625d81ad0d77cd534d1afacb65b554a225a033e31e9d070d1e4a1211b83e0196414a10009e2cacd3b60b00d079be1d45163c178e5666101242fa1be6ddb5a4decd62e4d0606575cc48baf8213bd033ebc5d98e319ca32555ee090332da639efddb3613c09bec3a935e9c61cedecc312e0474b0278436cf609f7f80643279dbb844e3c3c0754c30f00d49bfbe20a467fbe1de1838f98ced1739d5a60be30705d3ffd6f3f27c429617110bdb311b7017f7305d1113e029c8e4d0c9019d057dae8fd6609e7f559ecac4aa9135832034a0f40728403f24645a3b44309746e64a6bfa48f1646db3bdbcb43d6075306fbf09846d0db0bdfedf64eab083627cade099169bbbc2e41e2247e8c300ba30d206c127b825914fd1d3f2ff65408dfea2fcc5755fc9732afa3b6b6f5b85982c0f6d777fd9a0e22f4cac17b9c85964023610d0f09db6720e8dbf758520b8fcfd4674d295097eadfd7cb07b6cff1b7ecf275280d630dec4dda0756318b21343a92ace23f903b1c25e1060c2cb039ecf9bcf2f9a98081b0e8175da0aaefe338642636913685f43d485ac8703afb54244e6ff78274f0801b138beb43e9273a18b789652f45792ff9d04a3e8fee3fe05aa0d087a0bfbf6f36361aaa488b03815348a02cd50b36a244878878b9c2a8f8d76659f304385b101035a26ff7a23f74ed5ea71e798c3e083e4b44dd590a0c9622a24c5c55b0f28d602371fcab80af2a1427c2fccfbdacff753c9f19950806df127c509fc49ae72dd2b8a4970b866090e5df03af6d7fad53dc0b7d2c875f6239b02f8cadcbd717a2a9b02c7d749ec63956cb40442c6cc04bf90d6d2e285b25b3728d72e0db8e6978a7ef443bd0b80f975b516d028d40184b2ea7c4e2bfd6a09bd0ca0d02cf0aa9757864eb4a860d34dc2ee2c1c32097838941c4215566f2a27c2b1fc8182c1eabf7a87972f1ffb6482cfc2edcee0bdc3ae681345b1d17e4e0b8254ac6e1c8de98bb0be1925e250a8982df60e6f2d1defcb80ed3e6503589f94f631b6692c52bf9c0816e4c05985e0193dd97a123d3f631b8e0dc48616e18b6d7f4580355670162a855463cb8fcb58635f412cc13cc2c5ac3a5a90a1f045a1854a21303978668d751d3549e02980f9562fbf8bcd631779cf06936f6965257cd5420b890d5a5221077b4043f2b2ecf0a641c342a56c66f9713d010b843ad2d698cebc2023ccb25680f14ea5f9d30bd2843face046dafff8d3982437c7c659a814fb6ab6be027d0b95022d9e9c8aa4157945170d8ba00c5491b1664832a55871cd9874d2927bcc17fb749b08105982e8b31971631ca3d82599571bf41c5a97281a973413ad1f65268b908c8ab2c61e7342485e49848a709b1554129378625b61cee948df0bd15e68d7b27ebd71b21de33203f24472e4b6348a8fc9acb3484a922ab67afc132343f09f46b2f3b8591d146c660b130bbda6c6a9126b24c459ae0c138b884cb08cd63e006c69367b7b4b994bd1f462ef2434167a398ea923c80f89f7381037eea1a1979d5ad30b4723a317270ddd70b51ba5ce5bd26ae9d079df440b508af106f3478762c1c4473ce96b997685355a1b89106e1c8a05fdf4bfc5cd00523a0b73f8eedce929319df7da59db9e5c0590be424395859dc756ff6abb2659162ef33673652d167d6104e3555d612d58d248e554d9ff6ab8bac5a02146d5ffe283275715c15407bd7f0092e1c413ea7b2c7e37f50e34da485278c1a7f8190ad65032e7c35741a46dff6111212ee6db91aa52175e6cc1007b1d84cf1cc86211bbec65b1d0e3702c61e21ca3c41dfee381bb3282784cdfd8853b0b6ee21d8bd7fc09324e7429e365b340a43c4d9742e892c4091346299ff8c5d0569325610586da2ceb98bf9a679ac84481cb29d2e3e00ebd2dcf038bdd355fcf4d7d8a91060e15375dca18c8f8bebc8f04d8d382d9abbf83a5d28fd02853ce4224c22dba3e37a97c9e03f6f26789bd1c817868a90ed091480285aab2c776306916d04ea7171ca46160b711ecba3feeab12339c5f19762269a220ae22ff4e84ee5c9d9ceb43f051d7832d9a79c15b9812887c46f901cd04581c9ac5dacb6a15d391318d0b794a5a302f98dba4c149161df4bb18f2cec91a8ddd455051c291abb2ab819a298b99e3747ebd7c2de38433ef6dee61773b07003a8744e615ca25516e303c6ec04f964435b7a06b900bc02b2ba1f84974c21a1318ddc04551f8261b37bdadd51051ddb606100e034ff54a24e05725e7c97767df0b901dd1110fb48518e50dacb9102d8e668dfdc8e28b82e24bc776e610f7b5b5c1140594ee61d27dd717a7812ed8e1188eeab83e0312d010024547c2b2c061c1eee05756e2d82fe20f36d12c85acacf1c220959b11d035be21f0f3865182fa94437272a63f6a4a51d07c0ad126a5f399237403236c86b3a09ebb2ff20a580c37f2d234de1f5ef8c35537f800b1f1f49f46ab6825852908220fe615a81d1a060335427f18a279311709364bb452a62e0f98877f862e5a8596a5084520406d008f5ee48ab7cd8be4089290abd720f21a55e07e5d051c1da782def99060326dcf220594e9de1150d0f66fc2e4a8ae959123cd2a3a61d0551cb4706d27748a420fcbfe638891acaebdb852d4d302d9e4a47bd127d75f732a61769759951c730f873604485d86ba61903ec7486554fb7f304cfc6e13210cad78265146ef329d9e0cd5b92ddd5b322c62a3473fdafa46580a672e762d866657d10f2846724fe5eae4f93c23f38b50f9565f8117f262a82720e48e5026ab2226d28123626dcade9cb507d936dd310f2b63ee6a9845361d002fa9e1ea6ce31b7012bde57ee21dc5c94a1e637679dc91633236b10ee74b7427f27757356666236d840e033bf28b97fccff1f14cf34c63909f4b29ff01be249b94fd648737f5559188707aa4016681d95bb63dd06750d4fc2e3311de1fb43ab5965896ea752dcb3f35d667767e80bfa7c114676833932e2d787ad12d7777479b821641075dbaf6e18f182525e00e7afdad4def0570b02c26cab47834c208e8c05e0ae5144e39bf2c3980bbaf8e4c922d5ee3f62cd845d9b394a0a4968af38b4d75cd411692529752659d46ef5dbe976332bad87b9c331c54349faeda4711a0df9f94aca290d0b01951818e3ae01d8b6b260781e85f235c5d6bc15a81f04f9658d4812d724275156c5d152353593289a4733e12e5bbdd7e81ecbcca75d92d40236932c8e789d435cd9b8e48de25c56c22c8a972bfbfe1423666186e323960ab75f172a66a55eba17b91536c64dbc0a117fc6b464fecb01c4143ca9f9d6e66390602dc26e4271884c9d9fd2989efc1beffbd6c9b83818f20a89628141831a387317a74483ac9109a7a1cadacd902113b2a09936f82837b08f15c04134f429be0a53bee3e6e8491805dd18f48a289524ad1f7d69c649ab80f5878b2dee5cd81946ba69c565a38ef0d0663ccd8bdf42111062f1291c4983ccafed54a65e3213be1ef360300c19a9df86a3963ec0af59ee8b4065b859aebe0cfbd84292cb7cb357d03a88d4b942a7c0145376564b25bba59dbae8ed1a6967c4b08233482ca9043010647af23741f10f90fd214ed45052a3ff8b463c6419c07528773cd348a884df8d411f26753f79a16b9285850b98949c7b0d003521722119d3999dabafb6d7c2f68f72ed2e94af2e3e0090a8ed724d3f2ad1d66c48f032e804ccb564abd8ebee5c78cb63ecab1c5f42499e843870377bc25902a4111ffe630a2b595475b066499f0c3bd74db7fb6656e4627f1f40c9aedcf0a8680d7161ea27b5f66c49304f2458a6e478c8f65fd27aafcde38183a3a8add8f0f5e4ee186bdbf7268a27316dce7995bf2b0ab9e967e71f4bd5210ebc504159ac38d2053e504c562ca095d1782d5c9ef9c8e3d81066ad6bc587542bfe078fad9710c83a2f92ee07660f8e989f327380ac87f6e11c0ed2b01a297a79d474382d160df360effb31ca04332dff64c07bfb53527979e9dc7f3554fb8065caeea19f22067e37c87022d6b2f217540a24788d522337210f9021c7b9234908039ac5b5847918b82f522d8bfd2a03884cc2edcf5c5d9ddfe49a5747dda9479784f663945f62c30349af484640782e77de935b6c6ae85f1886252d3354df4911bb88d0f181ff89070477362d1c7ae54d429647a23da81060b70fd70983d716a61fc0aeb21016d888c79fd3a88c7b8f6b5c679e7144d1a754896ee89f358c473dbefe387bb89dad04413326178dd13f3855d88e032194fe5b16e951f3f3d4d1a33bf51a3169a47d1840ecb679fbdca6421d37374ebf5e6c3ec5b45db719e45b8fbb52488bb25019a28c55eac319a4ebbee89ab0a48d3d5289a68026975bbf030956d9ac9bb22bc021e9d95410b9f081e181c3420643a218c73e4b903c3a0976714c343601d4d09b99f7f5e9fa8775d0eb945a71838574d509cfd15e49b54097649bed0dffe3f9d377c8ca249b8cffb67fd9fdc4edf359961400846d1a443b9ca32133e11afba888d4f1112e1f2b5628d66c4812271d741988ccce107b8196112260d7f144dee33477e004d610e273b0a72c666f72f1b2f6be992b15070841a26afca91419f0f5f1435691002cb40f7480fad62fc6d3646d0df0bb5381700bdae2e91dc4ef96f05d466c246286001db5cd73a364d46aede108618f7c0b1b28d865e90ca31a560c222b38abbb7050b836597be318ab0ffe14ab635cd4e48d4370d1245b57584243be7b282c95083803b8b8a261475b88320c5d988bb23010c08f7af612d9453adfc45255d1cad362f283fd07d597e4c6bb6171db8b8a6cef5695c9bfa0b77a49bdf3a8e83321ec70ff9268c0b0da143a003144d20a544deefb586fa193cdf97911c451a8181bdf9246897e20633ff504a023ae33117fd8d169ca7da365c7d1dafde8d2f47aace41176c1ac763c6796d696e87375e89f6e67a370d174dd139480b391b4b4cb80dfaefd6b879c5965b8d8cd0b2c20fdedd6379fdc0020fc35d39639808dbbf7869d770bc6559b130fc7d234b155221b8e2efd0599f0dbb3085a0724a953d2aff484fdbb65a9419d244dbd0482ce3762d9053e74689a582e7611608d128567225268ded4fa7bd5b78b05398abe9fbf53b4cd20d883fd075b3ef31631eadad72f3b30f5ca971d5b17d29c4ebfe0d46e3359e48f949a47094b636db0a6d71c82fa2c45cee392bcf7ab9c8f7fee715a4b3a2702480fe446466af0c810911a43e6266bd76502a7da4cc982aa0ad8bb55567e932c76133923e982485ee90e60ac00406cadfafd4c3426c4ab35ac319ba656a7111b21fe64699e8fca54f76c5e6d0ff2b4595426e732121bd5c4ae3effe2fa52554a57347506f03a3d79cf0d5dd1926f7063caf4401ce36ba0eca6a0cc4064415401281384baf017f1aaea7ed23bbbfe2ba84fd70b66e94a0644d6bc0fbeddc65db5ecef9c38db62e59c7be2c9dbea5af19d0ecb70f8c2f738a71e6ad9c78e78b78596ed2df8805f124e69fe0ee320795fc78ccffe6eeb774554af559de3f1197d82259f376d07b7ee39fc4a997cce34acb8d6f53e050627ab5f35a3c40b2c295b154125c877cb338de2af5e7a0ae436d469b199e396487df56f41a8d056af50205e10a31faade7052e65f5121678abdfe969dad92f0f1be4829eb488d765bae7958996dc9caa560e60d786580d9bb48e99bc841cc2055043096163970a059c2c390af04f4979bfa950dfcebe2538ad3ab606fe861814e40a7ae2d3a757a8e4f1f3fa257fbb13283aa51ededd3f9ce83420faafc0d8710d9bbc0c23184047a9056eaad4b23b8fb1e05ef40e2b1d070b3c0fab29b456521f03e200fb12b0744145e67ed116006f16a1e2bf2a6c1a1210aa44d1f8423fecfb7739fb9d3ebcd832bbd975278a02b88d4e667c91127cf740169ef4a51780f7e5eb451abad562d257bd24decb0c0589ee6787286d059679df62dc4f4a5c0670008939b0a5ef9619dc240204056e9868a94c774bf9f04249f2c61d66e8d73915c3b879e779f1c33701425f34197d166c67f6407de55e47de7995302323a78214251569b7d2b18b1396c7d21032a3aa8e1c0932623c5231e7f0bc68c572eaac09d482e686e0424062b4540294710a7f421581a604508c01f776698daf06cc9ef7193ca3a504729b3c1623162a54caa31d636878e9f048c0cf4860d147a608daf27cda82b06b4b53445740ce03c9732754670523cfaf481a1c8b99275307ba7a3fc00d38a11467850d0ec2e0c45d57a6349487592d7ba1a786b21ec9a977311d7b5b1d7813da70df3cc0873c4d4f51989f296dee39cf788977624bbc78aa007235eae82c5dd24b36f0ba24ebfed7c9a2282425e87d6984095172efa5368a68c117d3308802be44e07b1d848458cd9d3478101487083c6a15d427eea6143e8444d9c569206a4bd66f8814e70e4adbcdadaed688ad31dc8383bbe14725c2f5d76a403162789b0936299a41faa9ede72c24173381b85678f0baeac4c2d28dac71983d9126722c272cab6f8fffc2cd2b79cb2bb7ccbfca8d40953e61e38b94a5b7c6c032b52247c549d259ca36a040ed0583e41fc4f730b3e5078976e7e74b73b7f302d728df4127c129860537dab0b2cb1a7ac8cbc918221b500b422050f96faa0d2b9e7931aa7559231b2ae62f7abc7f18af6b0a9835944e581830db03b8d0150875e56a7011cc1a8a9e725fe6a51ebfee1c48e19ad630020be22c69db37ec872f877daec32780fbbc1fd4c06c93dc8c5626fe9095d8c56995f99c12567791c2d8d17aae4aba932d2999beafcdfe4498d6d9d285a5469340fe6c27ba0186850079b7487d9d0ff05845664f8f1bacd00a5ae63d26945dd27aa0db92c01ffe1238301a2f2026c1281bc080fa345c5ced1f857d742a68912bfb4561dfec17c0e29069f0be89bfa6a1ef0abba38abeb0f48e22f629e179bf6c368034c06595ae9639425b6c06714f09976977dc62b5f174f19eb4163e54d4c149c470f2ac3688cce458d4ace294152184f518075252c8b32caf5438b58f6520fc0d31c31bb19856aa169bbd9783c110db3d8fca7a5f1e5f491a43359f85cd8194907bc1c3eab3765000df5592f42144c38fe0fac85f0e283b8838f8bcac4465d0ede10345999dd610566ad84158c99e5c9f10f95144137a215718276df399ad004e8ef57942fbfb2813d8d1c779939feee54bd600d77adcd858fcef0552a46d63daad1446f4be9fac396c284001ab7a88d694f82ceea0d90c88de30e5cf90376bcbd2a80aec85875043f0610251fc1c6627d137efb9865a034e252481fd17fd06499e0c6893bddd9c4357e98ac5714c0b4c65e575dbb467b2ce5b498dd256ea3ab47e71f0c7dd11ab117786f40403eec5f1b6ba3f36392ff34c1fe7b081ffbb28c1bcd0ccf6a342fd663476875d41ba4238b1041e08141ff889cf16a22ec63e0bd85ae5f897579b2f23ca6a35f2767b35a48be436babd94b0c0c8e0fe38d410ea4950e03ecea673368d7bc9f81061bd0c982e68d812e5b9971a1c5be4cce09b7dc921f7929dc2583c35104c0d245d523d7d9a8269c4b498ee1266b21dea0ca9910d20837f99e97466e6a61169b73b63fb8c9621f44c4e5c914793e82bb2f57e71c9eb7a32e5d52b8c35633ce847ad78924f0c37829f48153d89c6745efafbea83cd189ddcf7f22608fbcc8622422aa106711561c24944ff02bd1b19cf1d303e2323d478e2f37a2cf0bb5facb9ceff0ce6845a9c3e827e64a5945d06bc7433e6266b3795d3090369c78f613d2f301362930b3a997a07ebe6801a2fa4a05f86e8a6cb1c05bf62503cd11361cacac01a1e93c2b05e0262e60b938c8aa75f1ab2b0b8b01ab6990bd09b5b57d0413434eff75f5ebf0a079d0df84dc859344fd608890d508233c9f947557439b18a51c942d176ef187978e44841657f95e80f1b73a6b840611e490f6b0fca9a9a99265ba449317dbe51b5aef3da006e81111b414ada2dbb42837250b5ee01c1bf469b211efcd7ae89d8f52676664c37e82e7a04e088feaa9de4a2a5a9208a9243528c48c32154c5ddae6a70a944a15a00b0f6c2fc6d8074e1a2d44fe9b3da21f442d818b28e9682a54830c600411be15074aa046c0dfe3d3f6138e3b4445c14f85cfe00ab2c4ac677bc8f840b6b5f6d9a5955a5b8e8228e6f2ab9b1da815168c44210b3a0dcb84b4d0b5137bfec3af6d3d014b7272412f2ce6b584ee8af19d2dec7a0396ac0c3cb8841d69be5ec9ce0873090bc48ff826423fc447232fcc51b8cd88510b550d3c39250819fec044ec461b4e324f30a3e41b4d38ea6f54a18e72ca2138a2d7e38216fc76ed28e4e28e7ffe6a58b61306882e31cb9fca34b0929e38b18119dbcaeb9b8e843d68ae36dcc2c356ea842982a1158ed35853a511944552c1d26399a359b4174ab25759a157863b4863798383b90a1167c0192ef989e2119239ea2e08ebeafc29f12bce1b4ab179a6ea722d2d9368d2ea165837420144f58f36c09cf8be6eaea3918440f86d79f213bb3b2a1a5f4a0b5eb88fb501c6393dad595b26a084df50ff760e7c6b67e4a8111607c57321446f0a5f82883ef2bbd8874662be760ce7ff8b6694f2105d46083aa4441f44cdd97bf287d2ff32dce52ad519f8e4ec6530e2360cd779c4ca1ef34273c5edfa9ec2d22188736455696d525297f76745ec230b301198960c512d6726fdfdfa983d114672c63c0d2f76d0c3dbafc02d126af38f8773f1e008e64bb0b243791fa3e1db32ca4ac931df1c9efd585595b2266bdc06068c726185a4a327ed04ea2c0d32e5fbeb599045d4f349445109d40c97a230f7e9f337596b0041e84f65ce291da09235a0546c4573c7e6eae29e01fc209ee0cce23202a50029c43f508d83fb9b5d07c0f9a8358ecf8fa631951d49908f824fc306c486b9b0ae2397eed7897c739101bcd46badeff6100871f5631b7602e1afe9841a0d8b7f6406cde3f344ef5badf5404bfe1865da29a46ffa5f9f92c3eb4a93441c003c47db07854afb77a0318d0b15eb5ef7c9083dab38d5d8fb38076511cf61638c87e6f90e593067ef8c9b43c7ba2a052c894e744a47af6547e8a6520c56b161982e36cfd30169d86273662a7e8b6d2dc36494eb9e829c0b018bd31645e40d566388d0367936e227076993710b8392217260d893cc6655064f9ee91e625eee27ab33d07e9f2297aca3ac4845b9b169d8c1e92b9ac7a8ced5e5789e699ae0ceb4356dfb1261f066713638246f8b38a61d37a5ee280d28776b34c96b877d8201952be01afa2be5366593b902dba8a24d566ed8a6312368ad4b778ca970e3e71de45b71dd571ebb3e2c8067a555743a768704a7e844d6abe6224241307a8594f63805ac13b4744925737a0ae4757c67c7dcd9e68e4e10da76855234a116ecbb3d9385cf6e4f4b9dbf0299496eb73c855ff6d446ca25119698d660c04b8001aa9ebaa6f751ccbb948c6ead330c0addac6575776131b95079892002b90d1b30a99184e0a2a1d04e525f1ecb7a10446f6b0b4f955917a14f64469e171f62a81d8260f1a8a0f61a997ac9fbba071871d90dd1bfbfdc4b1ed64156269616855f3ca7a177e4fed6c423052d93581279116a8a55da5b0fc326357d00cc9cf4bb82edce793e2625b34dbb954b5c9cc6669647b4ff21bfe10209c301ed90e419d56b412947aac0639cb9abbdf88f67a0311768e2991d03f6214ecd017dea757419153d77d2a00a647eb2bab3c72965c99f41b30ecab8c151a0d14c2e38f76e591b357115aad682c0ab7d449851a5437049698895bc009a319112911a234781008c58800214d242dcbf8cb49f34a3087fb399be2f17a0459801a94c9b613fbcdf9f75404d62a6555068566dd63f7549d45f37926da2ad51e529edb554d3592bb4221e946a96ca3b29413c5a86cd57e661a8bc8fe79a4dac539e1582f15a88551854107cbd72488c925e45155cec24ab9ae91f09b9d97c13981f1a77cbf89e30ae6d846172d8be274660ccaa5963fe5c8ddad2e67f7eb72c918944b5fa5b8bb1c6ec96b3e2857df8c4dbcae978bf7057607460f2e3962ece79c307c8dfc344f12de98bfe70a7f1b3565d1c966843eb6ab0a649c5e220f340b54630031c684a3598126aea7312270d430d851df0c3377d592000cd2551eeff4723483e8e5831b58d4443dd3cde8a6001dbeb788b9559adec2dc1d026062a40a8d7632e3f3ff83c1c00412e237549a6375753466395acc71a7a5d3f4ee482e371b6bdfd21653505062ab66929da68a341b65a438a80670d68dee058a617fd3e60ec75639114a6f9ff5accdd8fa9f72a8a4907e6693e054521d48d61414322976e05e1149a27e2edafc641563428278e6e4c13f09903f3a8be9e94685660912139932515b0ad83e5e22260af11d644f12e64c9e4afc59c397e5a4bb31db36569cabb207a8e4e5718ae3732149a72a13013eea6bdf0cf49898fb5675d6cf3fa679bfb8e62b23ff623ea0e21e1e62866ded18c95fbe5a0671a25d05a0f208fe12be3a827add00e656dfaa82fc69c4625214baba9e3ed79cb159a1f2aeec4e90e48b757835c62cde1e1a8f4263f7ae903bff7092f6ea8f3965bbbeee9478ec085c81a68b99e7e200641af962ec86fd4f892cb8b270d9091b61d8a703e3a08bec4538c54a6b5e0efd696a2dd8947b15315357e8f849046dbb6912a6a920fa6183a17fb056145914a68452e26fd9412da2809f7797c4e4506a5778280fd193e81831b8ac0db822db0a4696ffb78d026fc02d41c8ae4471e3255a91d58382be993c75f5582f5d9e218c8c6a6681ec5fd9c186708acc018432c54fe3dc313d9f94c81b3c0ac2cb2f0c832c69a00f9edc5bbc9842937257640e38414e7f7964404940d35ce339019209e9ed7a731f3d5afec533c659cb87aba231bd35c884d89c1f9c279bd2157e381e251f38c393dae8db12131f9060eddd8d388c0efef8d4d214a4545372241e22a57c6c3f22841141e77a10cc8c7e466455737ec880860ba76590951b123c2392019bcabc97ce11fd39924d1b9627ed08e3891b0305961ad838f53b3ab9492300cf56cf1a39c2c40b28e22edac948785ddcba17fb5aeea035277bdc04bee0cced59a3b187d9ed21b5322cd21cb3c0ac1e3fd4112c0ee8c32fb849eb4936ac585ee93c3f1d919e17ff9f1415ae8dfe13ce79145d08d1a536513cb5ebbf0afa6d298393ddcb00bbc4c1ea82858190f06cfd77dbf0948b6edec6bb3691e89e0327844892b8d21deeafd3d7f217de134f75174d5b21e5a79cef26428f0c33ff2a3471ca64360e2e460bf431c25ec960df44a4d12fc7534612ba28c8139c58928cb8e3aed8d2bbc69ee04c1fdddb2c708b94cefa45205f8dd8920c28935fb2f4214a7b7a747e7ec7efbd8acbf7988fb433986577cdc0647fccc7f4d14406016dc5c97e08e400ff575e00b506bc438162aae4c70200ab5d4148f69c692f332b76e8a18ddbd9e0c0d147ed34c5774b62216d72e2f18793ac6fc24b6b8d2160f346c85c2351d861388baf557535af314643a5a5c74d1253062d2b4b567864e684df7a5a909a2d363a1b2bff08cf56cdb78d508aba87008bde16de93985f404e03bd71b4dd1a20714e818c6bcdd5401a9fb076e6bc256650197a8379aca07c4980c5a1a88480caf6d44e8694cb39855079088f0cfb3479386971f68486f10eb0d1f621e192c40389bc5766266c69b04056ea01d41119d63a5b40ecadbba368bdc3a018fa4e0af0d6e6896ca1bd3c71ede8d3e47b9db46f413b24b99ae2463c02ab6682130e1f8cdf1e8f7a08fac79b3e1c962ba2f8a241570dbe00f60bdf3ce8bdba78d1bd55c73e20e2de09b02fa2fd17b6c5eaa7796fa583c6e487ebac780e35663705b39692104535ddbb0e3f98c96c0368ebd7dfe15a6b809c810485d53ffc8210edf99b07dace4dceb4e6f3ff88dce90cfea8af024c1a3ba068b9f4cd9533de29512eff71932e86fe1d8e43a5404b7816b42f324585c35e019ca91b2f7be7f597a8cbd4d549cef7034df772419bb69f56081968635341102aec73e16cd7f51f2c2091d3818005272d63297f37ca151be529064ef27cf19fd45045112e6330a4b0c0153fdccb68d60a9c1370216d6d893297c77be0be208f805c662cebe6115ee1c0a0750891c31932979d4c638ceda3d6e03642ecfedb477e7fd7fde153923c21cc387049f6cb20e2196dc85eea686dcf040f734495ad8527eb54112efdaeb80d26d9537ee891a22f32c238f9f1c2ff7951bd243aa72a1cab32a4fe79fb32164de068e6218eda6d8d1091e8eba47e13d7b04857002e1b566671434ab1031c3701da3c160bde7be539936105ab11a55b82f00ddcff64bbca437226d5a0720cf786e716abdea35ce9164d4b1d7e1fa0d925d6576f3ba0b372e064e2576bfb0b874a155557ab4b1cbcba717a972bb6a0050f043e444bba7d211128990e0a0af069716d498058f165e138d3ba5c72fe1027abf152fdf0c34bb0082a90b8ea8510ddfd8b10e4be015aa0242a41c042328ba9395d10708ac63690f9d8b027d834c3b2dd81bd43006245e33f0c2dd177de33ddfb06990897ef110f464a73b305c1a92c9c3f21eda57f00ff2a78dd00f622fc1f7ac4b7ceb01031a0fdf844efa99cd0f229adce6165d21a45a9e915e040738f3a464d71ca189461ceff1808b6ef8708e0c09f171515e99596e1f0760bd21f265c9e9340c6239fa627b0449a1cb33040e408164500ba837c8172f62393dbf4b095e80a0410fea3f0eb554ab9c45d6194735bd1ffe65b00725aa8530d245a1c40216fcb555a44d1f97e8f56a8958b8234b2bd8023e144526ad487b7ada52808955bb3f4c28f8e1d9b940d27f1749ef724727b93fa2b771ef52d2ff4ac4e1bd117c7eb1422743ece130f3059acc6f58128b3fc6ea0f5a72ff0d283e8bfc0e66061b61a7d3637adae2b1d4c69ec3555e77aaa5216f5f4918f99de0ff451979640cb7bff61c1820ec8fe2e8f8960eaa153fe6e7a4c085a83ae1820583e2cb776d6b874cf8dbdb26af1ef9c7c408f96c834c8d20f73e23d6043627ee5de15854dc95e630d34e0aabe5112a0665d5d8f43ba9be17b14701928dc9988abb0fdc501adf6999bac1cdc9570704374c94f123bb8bf9920f10a8e3e31c0e3baf56907430084104876a6eb215cc7214417aba8e81cb4b6eaf2e66604ff5c614b33facf368837e9dffe9ce043e04d58cee1c547fb2361fc1fe01a329f47b4c86b9cc64145e3e42676447ddce524cbe1308d0fbcee8ee08d02a27f980458aa5eaf2a20c5739fb6025d89d1411a23c3d2be1bad2c3e585e96949cf2c80c9874c427e52fc41f2fb9947a4aa076f9298e335f392d7e22be8fe0e17ffead0bf8956d01573d3bbe67f8adbd72259050ce8157607eb2f3dc3411f4c148b3fb2d8053c4abfe2fd703a839d16ac2674ad6eb70f83579149bdbd0ac30a0d12006e0abf4e7a1e67db5eb9f2da26c24e72ea09092c1c2b6c283e41c4c409c2983111cd16bc8fe0cc63afc56414dee82c6a2a09186007c028abb890d8c8412ffb99885b040aa6c118a3c6801cf9c024602876e427c59ec843400811c1498c118c66cc2b3710bb5f1e1c626e2081882bc08bfdcf5f2234e1de8842db46de41d1cc13671d147b05faa758121274982478a3f5308e846ccd84650122d9336b6c26b52fe69ff6867a517c8e485830a9a5e770ad3913bb4cc5b5b1650fbff11bdb076b306a3843655f011f36c48de4a7383f8772d293b64cc5dd499a462ae87503fc2d53aa902acc0743eeeff56be4df9a272cba74930522a041233bd4acad95ce90685a3c6f6cb12be5cdab2e3e49046c17d8ce33b0f4305d0900988bec18f14e145c83c950cfe4651fbfc5dfc50e89812b49fb2c03d45fcf1a9c2c2d2c75dfff661c7ace431a8aa2b436b02a0dbb60187bba9006ae2039a33391e90506c8ea3e253c2b3e7b38637594b8d900b3907951116141a67fcc18a60c88a1dc0ddbbdefe9fd9249fae8c87bb3e0be4304005f225acf5468a74fcaec2d4cae4104a70569196becdd17269c3f993eb17aefc7e5f05133c07eff92d946b0526b47895656eb08523b71d2a073e65aacc9ce30e58ab294d780b2b57cab0e1b2fad97d67426e607303811080022edf89e239f8eef41af06f2998b10c80e1e344583b2827d098060c64db74751a38718084dc95d82b21d1dcfd08c05897a092681182b4cc8eafc8fc9c6b68f9d90b8a579b31c09c90abc6a6e3a6cf21c417e46974980c4fc7b95ffd0f0a0522ee2d22fb4d7612e4736208f958c3715b06d84aeb59bb2aad002ab531ec25f92f832976bb59da50a34bf64e5cff4b520b09c574a3299bddb2bfe0466583b5c036aa5dee88792b66d09f39f5f62aed3461550d74bd40fd2567e481826321af297debfb4afc5b4498ccf73f632687608cc7b4ee9165ffc910582418b60d261d372fba1f3c3e36873869902258d10a9a1898f603d515cf8f812b3ee8c16329ba6baa802b12194cc53bb1d4039e00ea9a5dc2a3bc2e1c325a942bdb5ac9211f0be277487008885387b2395c0b1183309febcc7f5298d79d630b971229e116c736c22988838e1251800a1ff8d7e972d1de19cf58b73178e714ecfeafeb2e98dbfd877d7cf77bdceffb1cd024cc5a0bb191002446e144ffe5a01ceb5f8b1cb1038171a31d29082433a28f533b30b4be1f0f711a288303bacb15e616a68e5d74b1b7229b62b1d260b3841d42c098056705851452de2e602d19e12168621a92600b6ebbf86e90a82db87f446c443cc3d4c39643440acb322265d2cc75405c34ced70710881f2de6e9891a71272db1807c0d38ad0c68b0b53ef2b957d2333446e3d82f9f784164bcc53e7dadb19956d36c2557ad885afc970a818b63f562a8bc38a86f7bc3b167cbb09413571db9f1bb172a7e57a7c179dc5fc225082fc3d9e04c47ca886053feb23679b74fd0e50ddbe41ab6283280254bdcb17c8f475a08a0bbc3f23b1ed102d722fc3cc03773a454418e07393e0bf1370985b8fc53e25271907ee287650a7f4aff077c4ed0ade2d4e63345a7bf66dd24460380092346b479bbd358e9fd98d962506dff4769a8c97101ada24788a890d58f9a6146d7d2d0758d88d904941e2a423444dc5b53a482ff699ad749523cbc31060204123c3cc56a400e47d17e0400a483f8d1fddd4682a24394ad23d8f8e1db677e6c33b0213713eab2f2f4543ade9e798bdc26659e145152303aaabdfa045db490f6d81ec7b421db7f0863499c001fda6359db8570db50fa5c32cfb6e99e2a1e09b5cb1aea8a83cf227d1fc1d544ae7fcb7d1d63ea4fa1f7d5be7fb14dd911fe3aebfc3550b9ec71f61808466923a0fb80de7c8bbf0907ed49bdc2a84becc6e07e434a18c59fbe3d3efd0f020cd98d2a2d9b60a7d8821267ad150b42606dc818c7e1a3877686c9224499b033d4d35a44ce67b0b7f1398f77559d58a94039675dd14515d9c38cf78bbaf0758f9c25233c28e737c6acb68cb9d9c49a6535ef6cb0c3e8dbb897a47286621631c499b0ba10a0b835611fd06c4e15f40d2efde10b070b90d1316d7509a67efe6ecbb358afa0d51753be54df8ad48c055e7062d968b558323f1779040bb5e099eb061955bbc42c2f9a467aa9670a51e68edddda3eff0e145fa15f5b28560136e2a5eb8d5493a73607f3b67879f36218c602b5641f689fe99bc70519b041cd8b37753295bde91cdd61bdc0c3a33e23930b32101f081dca6a179443f047f497a5d9f9b8877b28ab8f368592e8bde9b98ed83a62925f52eb8c8d01f086d5040b4d9b023d1ce3e867eb2e90533289318bdd6e59892903169ddb4e00e1dcb5c3ee3416364ba89d10c9fcf28c3c62b01f0a8d258bab266626c1c3d8dbf31be3cd78c1ee78b17e3b02fab80cb6498ab55c5eb8285ef65092baa413157adad822791a0227ea36d694edba8cefc7d3e4d9d84b32e873d717f057c113b011993dea66e94602c1f21329c1e65120e5485827acc11195c14e9c28cc9c491d8a62b27145da3a604fb4a41f5d2b05f5d8e807b29e1ee7301fc82eb75b9c51d3ae9d2180ed63fcbc945fe331566dca4650f748421ec7be71ce5bc273f7a5748cfe0926ca58b29aa8c5c61991c782b4eec4562d198098b7be15e1af9d0c85b4c4553348764de2d060419950fbac66092a01201b9014dfdad446e920d0793518b0a13017d7ae3c3bf27a304865f5b52e9ca0b780e3eb24fb8c7a35808765ab3c252cf6fdbecc83dc80432e771e8891b768fb82340338acc95dfd0407115f6784eb6df07ec49ba9ac5a9855e1617d3ca5929f817794d401c29599e9adeb4d18aed09feb8d62216679876ccbeac216bd3b0a8d64aa87ecaf3a072ac056e99418ec124745467276e030c19ae6e4fc086462d8c6a19da06c49e8866cb73f4d42fc77a9d83e61ad18d2ceea03c54c19a1c53350573556c03483f9073e36811d8d8e7a6c9fa3eb5bbd828db275dd62d220e83327f1b78811d8742e1aeeaef45845d36ccd552fbdf1315604aeac41c17e096779c2a87d147bb1945b835413e546d10bd9c229666e31ae60cca007be45567b2f99bde54a8eca43ab3eb33b8e846565f6353c0e72fe0ede3d4c48c8bb63f16f5d3458f6718867dc34b0c2239047507e9aec43b528f41237b306f1f4c64280ca1eb5f49f0231bb9e064bd22334a1c3766fb8fab255d9ab3c90fb3790762dcfd163dcc029fe5443c3083c001ba4666883ef1a139340b85c20b6f9624e588da5f3dba4359d103bd5cc33b8d15a1de6a6468491ee0103be3f7e2d6808467332c1b7846ad6f9fce0e991b3207bfc74b4fef234a01f40749cdae8c59ecba3803fbcb0856f7d199e3e328d1fcbf138512babe71c5811583b654c9a50e1f0929f33d2b6c294ad08d0907bd411876ad2404c6620d0a240929875d8b4d6241863c2a97e1f3a87ffaa9b00943f61370793a3e1880ce6c37628f4a2e6049142a22e3b2e2b2c4d422bcbd0bbc9a6b5ec06e02828e5f8987f337e6f42e71f4911986cc55ae1d2cb6687b6808c49824eed1ace01e4bbab13f8f5635eed4554e2be39e4255443049923ca4daed90295468e6e46e483f185603473841aa9700722ac13c7248dd3e337bdf4118dc93dd4c758cf7a81479a7f6e51cea34ae1e066a9e70d41182e8f7625dea21d7364d61194f61f400c19c8cbe4bf7e1ccb247a7f9405b688ba9187a18f8886dc58e9648d4150ed297dd5e8abd428be2004877b4c85d9eebafdd3f867b0264d4a92266ed31c0597f021ec35957dca420aabf503ae0ab950ec5c86fb4ebf457370f4e971dd007f2c1c717eb050572fb2a3e41e00dc7bf2e8dc36dd89f792ceb2110e0fd5966b2624b0a6f227f52ec40fd01611c2cfce990e21780d4524dbfc5dc626b542476939ae106d2d81876336cd69ee311e2b5b223a237e2000d713031ddf208a349c072e6cf1655a8c1f0bedf21adae1d1e513956225ca293b1470e326101888662d6dc488a4fbdf1e283fc881740b0206829c8ebf32fa4bb27c4e6a09f6e7d5316f21e16e762df33d04910fabf2250aae2be41d3708118701affcb9aeba59a3d0b7c8aadf1d5b19d00f72ef6887d6257cc891db146ac1133314f8c11bb628f98893d62375755c751ee3bb147cc8a396227e6887d62578c89356247ac113b314e8c1123311fa529b192282ea397ac7d879e61542d5511190b346097b0c23b3d442a96fd9b8dde3963a6ff5d6d74cba3c0df9f9e9977b1f13e29da3ab80cc1084ad331b6544f0b31c74265a78825479feabfe00e04f75347b495f1980911f9869fba0f1b3a99231a674675df6079b669b3baf3a6b495c37674d86e5aac16f9a8190e26701f3a11430efc8bd14b3d2d95f2f9889fe111f4f9a7fef1700b5ff96a3ebcdf879bca926fe53266ade1c3438f77a917225dd691cb6d72b86fdb3264a7935dac62d6bd14905c8223c39ca0925858079bcb526744bffbff87b6441c99f286eef50a815b11447a9511ed9d20f053e55cd0e4ab49e088186511d395eace57aafdd88f333833ab5e0b28f5d89921b51fbcfe1b71886463d07a8d2c1ffbcedf43f4efe3361ed2a283e3f193f79320ca8c585c08dbc9f955535048bd9b1f218b52d286254cf34047016db0a9287eabbc95c833b837872aa7e162cdafd519ee34fea4d271575550d0f24a994bb212b6d46c8d89629df40bf4e307136f6af13218f73230831955c29025b58524a95bd63e20ea9f4674a469b1caa5c04a69d4e88a9a5c7a821b091a952bd298044bd6e9fe531e6a75b2c60733af7a59fc4fc4ccb1e89a82f06e35b11419e216833fe710f2f6deef13372bf94c524de3d6f351ec3497cb546a34bbc3ceded7f7773399d469e2c76ea2f3e84aaecafc30d99b17497b997702830214b89db396c3fca89bbb6e683fdafdfd2cfab957506eb4c99d8668182097bceebed42c0b5a2f5c0ab5e5096e96c52ecc8581fb90063f0f84feb51978364c80629af2b6e5def4c05d14edda5e0de66ceadf9cb0412f39ffabf37d27fb15474deff6808c6aa750e3cf7bdcc317a71240b581dd0469681128b53b87138e6f300e5d2ce705c55f9d1f5793b4c9403b25ee50e98d3cde04c363ab5abc6cf51a722b0d3e0b350f926f257b9e25e4d7ead09b46506214947ad4df38c69fa657a276edadae72b1679ebfc371b9796991245ad044cea148fad3e045e58cb1dfbce6894fb2e7131f09a022b287f94d61bffe43c1719d009636842782dd3367146f3e6eede187b88949e9e3c02ed79ee4380866add45f76b3b6b1c205852da9aa3110adc79f204e531843dfdedd057c2dab43dab73b7d4dba786a5177c00821c28f23d6136df58d45a21e05d101869f0142856129024a14d775e70a90f8b103261c79f412b613c859d992b90d157a2fc4409191c87a14eef20eb02d52991026e56e3051f44ac734200ebafcf4b744233a5981270d2ec6f473af1fd2b92fb4f451647f9503831cc327c16a59ff9974c4d8dfe2f195880f663d4fd72b396478fbd016e4060010d970b82b0fa792e21a590e60d4c4da0412b9f41db1f104d9a4375aa7ba45f3571ceb5e003b39b83cb3821dbacb05d1af859217c5ce664d30f666546fd0a79741f3fe61ab04930472002340df0d8df36fd4961c78c7bd0cb0576fc7fdbf24dc65b20f80ae36edc249ddf5c124a45c16d6ebdcbea000d8d4b1b7057f1bd846936a00c63ffb9dc0a2e27a4648741526c20a2c2450c4302bc3ae6c64ce342c9441792169410b54db59a2927b9b585218d569f43148ce9e0002c7e8bff95a46bdb7c8d609d2dc76715115612868d7b39cee78fab83102b405377baec777571c3008b94d49491ce0a41b3d07e4c97a7370685a77173aa47fe61a3c33e8ce3819ce28b11963d64ce064860a3083e8cbac8d6500a23223a68ce34c460d9231e8c8840c6428f1186476cce23406081833f4629c8d6214911823464c700e430d8641e8c2ac0c61003198310bc6e10446058031f45fc2c62f14d11724e6cb72f6020c7a19745e9c0c5e94b88b31db2570ea42057441f45cd6462e00119762942d8e44b16126854b67bd252c6ea1f8b62061b62cb3166057cb20a7c549d0a2fc598cb1590253162a8f0b22c3652d6f0178b78c705b1cc316b5afc520ab252469a1fc27b4201f8582009f6da1551e74b2e98b4e363ef9c8177447125bbd025916544a62e33aac9506289c1896e126b68e5f1c4f44c6b3dd9c02549aee7ce9ed313b9410d5b788358fec117ce47dc8accf33299436e285f36d4719212054cbe0af94838f472c5044880687cc02ad93f1ee04689089d15b46783a782f2e26026847435cb6e37027d8fd380c7c44637e803b1166dbb4e501d1230442aa04a8146452df422d8e892b8ca289864c2b9e48501680d01ee10829016b18dc8e1580536880765503cc22bae8e5134e6decb9c69627e0696328fc7c567a973a9517ab9c733e3e2b5001928b8099532f9881070cc127a3e82353f88b657cc2387ce510bc64101c72469d1983ce59896736c137d3842b63c08161f8c4387ac41e7a648f1c33871e38032f188247cecaa7fbcf717fc55fa01937a509faac4248f8a102c9ffb8e244fd46b9247d4281c47fae72a2bea358923ea142f23f5738519f51964a9f5022f19f2b9caaef284ba44f2890fccf154ed6679425d22f9448fee3122256cbd218641292fdbb767c1193086144c04cf092f28d29046bb6838057d16ca89f6ca51da9c60fde9130a25e6c1d40fcf572840ea7b7406140b81c4d49828f866a7bcc34fa51385569a9cc5fe7d682b737cb897bba50c01b39a645f9213d069de1295de99f06524abe03ece326e430be35bc0ea7bf177dfde51ce0fe909e77c5d0fcd29e22be3433e79885899489dfd664b832ee8549b77678acd27a9459e02bb78c3db81a67336b9e6db79f5c956e4ab2fb702fe53a37401f66be56e3dbdb11732a7ac7955e3c7a7fa948bcd6b450c43d05533a4b05fa9e835c437da7a19d90a11d4eac04aa40ac33eac604141806a34c1b0b490ed983d6b90085ec1526d884148758ff581a8fe9ad198e82083f54d5a348b756e0801af7f5e00d353828cd5a4cfa47f025c3dd24db9f5997ee2f1468680c44e136b84db3afaf8af83a16de7064fef1854aef6104768976e42cce58bf899ba11bc2c178f21f226fdb15faa2efac89a9b56256c9f1c362731e27db9d9286da7a2dc1f3cd8681d622c6fb01ee9f4a201f5451cdbc2923eba30f346a2757518b7d0b6d581c605d609e54141999e91eff6ae0a36a90afc1d7710da5ab36b9debc417304d454ff20888ac8bc967f74d2404e3f0b87f13415aff258ac5c1d07f9f22db664fe6ff019fdf47dffcc15e3a1bcb17c8a73e59dc36db35d4c09adfefaeee69da374bf6fdde38fe484569fff1d9db3cd7d60513297e589c8f034563ce205ea35b491a46612b919eeb5bae442914a3391e73761702937a53f934ba52a88c0517c8b9fbb69c96100f8e6fc8fed407cceb61f2fb442cb62ce8262c5b6683f2cd9284771b74c59208a357b0a097034b8bd84e2eaa370ead5bdab2ad86d38613677607edde6e596667fe434eafbfae91528da217bc6602fff7b71aa810d1e33793500969293eee2172629b7472c0309f81bbcb84ce761325d73479cb023764896908a8a3aa7ba9b15ed0d8989384f5a51d3dff23f844ab920481633dc0ef04bb8d051066113d39b1cb4e41cd03697f87aa3afd6b0af63d2c40b14e92362d2409ce47c5b9fac174098a399dd24421f95b393e7c08c0521f0279a88c423940e585b6476a0e5b1e254e17d6ba4a16dd34b69e6113eed70d2e55f206b5344d2921ca1be4d29c5403edfb9f288cd2dcec60ec13b6960014dd4b003cdbf2d84a24d9c5be341af19253ecf27bd145167680e93f32530cca73e9e0e550c5dd1ccc4cfe4f5cd864dc901f68d705fa7beff79344637c21dfc130c4b25dec7571d0fee6bc49579e29d8b684a1dd3e9a15ad3bae0275bd7ab2985f0402902cde3e43bca2c4cb11a7e068dfc86457a2452a92932cc0709f1b17c76aec7a8e7bd7bb2b7ae5fbac3a876758ae77a41190c6c9b9915c518c0981f24d251b94649f0ae5bda72a3135956ab45645d94b9e6d664a21e614dfbaf272e91e17d5ed9e4d894dfc69442aec726129ce37cbdc942c935c0861d5d5138a60fdfb1199c5f321c47fc6457ae848651062258f7b1e6998028aca9f6f278a5e79adecfdcba4e13e38744f71671cdc4f67043f4be3456e7604dfb624948b195644a323e0962be480117deae4fc506d7413948fedfd4ca6d14bbc0a4c089fbde2a2692db21723b23f5e4371355d1a39c91bf6fec99b259084455e9e561e41e0d599c5b6fe2c46eda0e89069ad66c72dba801dc3dab7b88a4026060d34760ca1f4d4a373246a5b4c10fb00f7ff1ee7ff58aaf8956b951f0d204c698ef233635b97c35708b4ce7de8e4e021afeb59551be4d95b23e0058ca0b73c7a9ec16267fe6daf6a227e68c1ecaf895bd39e996e76bc805005362c61df0a6c1154c069af28088cdc627da17bb63310c3c6a30bcc11f33101715010bacb93c8dec180e107dd0bfda4ae3826180d100c78d40b0d7729cef2f56bc8ac62fece7a36402355e117d7c5876111455d6b9919f1c3fa5be5dada3a7fc2fa4741745e197ba6c723595353501247fb334a759bf1a149fc60f0985b66224b1d58fa7a42f003204a99a959d04d7fb13b4130a4a468ec0e97f304ae103c4639699d38042f3d3c4c90a256b93f115cb8071f6af7ca25437829129cd05e208d47ed3dff7bdc04edbc807f85ddd39204c0c889b931d18a9d17f48403c52ac000c136d544dfd3e3dd5ce44dac5da4587609abfaa1d0f84727b249122d0d2442c3f8b843b892b3a08befccc4ba79478b40f4fdca92ed707011143d8ba6aed23c266d1720530feb3da2c0e8e5b5454502790c9faf1b1cc0734d69e6b28a81d957fbda8741f4aaa5c6ce30b3b6985d3c233c8adb6a110f786f8aa92f163e685056a5c669e91a16f22315f3b7a980ba3e26ac8bdb99a91b893053658834445f77381349596adc3be6d3fbe299d2f2b9ba2512e48406c9455ff49a1ed072c0389beed4748e9c359a3f3b82590a4381955ef23f7611b66170b85c3d247818a7a6c5c913d56c056ce50e76050a3c56805086a8aabb4a66e664771004ece756867099f3b5d6c04890f2414450cdf9a5c76370e9129847abc7c9a1533ad40622abd6d5f16485545217aae465f0c5c19695798b53050aaab4bda04eea70b5414fc41bb57aeb5bfce7b817a821254c8bfdf3b50ab633f81ea6865fa154624a384e1a9498836de631e92355e2d4c6620084389838b70ba8e28ee2b02a33d3f57cec00ef858175fc6ee907631ba2ff1c4c3f90b73fc6922b7e89922735d07f2798de066a49a54908ab431e2f05a43ac551c0d55c170115b552b12d720a19b699384721aca5ecedab21ad9940f6810a4da8ea1414f04f93445f5bd1461e6dd1bf637ca17fa44a5e8089b920d07a8609668322b475914c718a76c4b56a83ec1a6791d3547548fd3f28bcd10a4df0ba96e84c071320c5a8d3163011fdb73a8753f8225ea895a5bf5fbe3b98901cdda12273d233c49a8d8b69b64ee98b8244aedd8d4a2298e032e362e03ca1c8c656e4aebcfd04e618af27ba17af3e28ea29763ddc957fd8f8158ab857c2109a479f827255a0f50cf5d3160ed461f58bb000fbe9ddd5c469d36720184d8a35ea0736e0244988781bd35b429cd3b868abe759f08810bac9f604ebd337151187715aa9c604d35984c98a5578d93db5202084c36680490e2f8b785c1189c8b2c2680ee4fae5ad09ecaf85cda296735f22fce092f5d3910263f03a034778dbea3abb90067506363d220be04cfe208200dd2e3dace8ad7a7cc7edc7d203dcda02fa07a8291728523ad7cd88fc7ee726618be68a958485a35b34dfa76127b9fd75039a5e1d7a6bb981a4da394f65b5724281663f7e1f5d314fa3f9422b260cd147d775b6cd2b12a7aa0b53a6d680e7071c87093ae67b6f5f40110568d3d3955085ec1f9747c4bdd9481b4c86a5bb43011bd4e354be58033309eaa6b4f8e09ae510b0423963b73968854b432e54faac47ecfcd6a0f5c8ac32e0b96e3a7ad3f6a84d608e90d1feae13b8539cb2e38186e9032373a83c53ca0d5e1467b0d0cbea8503b4056355449812fb1a9ba7eb09912b859d05612ecc3426772db50fff5a2c34f0ead69c4968306cf74aaebd143b4ed66b369b7a09fa06d84ff4996559a850a27d46201f6a38063514ec85564a731a80f2a1fe8462610969f550b65b1f528d341492696247360d3d0ba9d377fc4d49417141f6a52b8ab2492bfe018fa3c94c876bd7aa46b08df6a26e9107bfe700cc89b44e77a6b1c46848a947a5b8b7437f5881765e0367e712e363f955ab5542a2708fe8f9cfaedffeac07d71b60a9ce2d4b148487ed84e4cf41dd13b9596448b29a34c1ca4eb77ec2e4766f77dc9e62274a801da426857b97b38a07e287163ff6fa1414ed0af553cd8f27627fc0a02a8e34cc24390d2e99406498fdfc761de21b1f8db9101578744f5c5dfd852915120798e435ed866557a0cf783c687c72816eec21dfaccc6b60a5e4a9ed89f80933111bd020a36d9459074c8ed7a54020689403b9bbab01b6ab7a6a5fdeff4af82a93ae82be7727aba4b3c688c58e2b4a2aefc26caf2706be483acf435c9f817b1b101bf11aa89fb8722a080969c9c0ab19f7f5400e9af7ab17e8b2239a7a4f3465903c54145fe33c386fa1466848df19869c861d9a2cf7174a89f951ef67f4185ce954d00fce025b93d327cd98dacbb04f8cf1d0df402ff2e89cdd8e7ae4307eac0424f21da53cb6346341862b5026775d81fda2e290583d0b4ad142907d42cb0839c738283da118dbf78c97c277db6f12bea9108d45c43d4cf9c66dbe3d0552ac827f510be2efe5fb8fffb73fe80d89613e3c181b27b35916673630dd3b24c7b9cf514623af862896648e4ac214d14d7a43b26b0762508cf5c3d1d4d239907f036c46c8e20c22a563837a715319a23485f2f0f50b3e80f95e70f8f9947331572d9f063bec220db55125450ffc18e7d2e5c1b5171faf5b2b77b3cce45a24aa98d3be1085ccc4b78cb6190276838b77fe2501f751d94864b7fc4b23d2abc2de649cd2a29e836e27ae78f9314191d993a2a80a02db89e264a130c8aee1717b280cd9021ae2d76231596461bb9b8791a79988996134fd7a694553c474872fa5051925e9e0551d448bc91e8b6d5a55231ff8fe2abed6f569057c17f0bd760e20287bd44ca030db537e49642bfa294b2b08e6c5f904cdb6374507aba4860bc7093157d5eb125647ddaa9b7468ff296e9230dac2d69052c9fe98c782fd2887aa20912f13c74a782d7601e7a63218f695fba596a3ee1b6f5ee43344c3764b8108516b673c15cd216197e0b96df894d664a795f05468b164faf66ef74990c8477cbcf0aba8ca88d7436cea62ace074f452d06628cf1ec32c9533ff5a40de32169301e788190f47db783586c0dd94683815c9e4f7c1a296ab79e0521241b40d38f77516975367d2695200212de03df3cf1b7d283d39a6b3577e56768ea7dd96ac2015fed0c07245f30d453c0c141dae6b8b9623c09ac0d96c35c2b8e88a72241715b05e721a3806cf60185d8bf2481543a5222cad45236ed4079cd675536135a216a67e0575f20f0d20682f8e14a17e54ad33a777ff280286374984b92687b3206564023398dd81e0285b5d662c0790dff7a528ad8735737af40ea8146b831d9329cbf72e9e48e7c4cd2ddb2082039565350c04de9694e90489c21229a5a4bf135c7aa73886b8331ee4a8689bd9e1b9c400b621ce56051c16d8e1dbdad7119a2c54e52ce3007e2368a92bab7f5fed193a342a3f13645a1498b12ec34de8a81a278a9320b18285d3046328e25d84312052880c056018d125a0af95bc04a5cdaac1e29f9819892425a81088e79373a54ca5ef13bcc6073bd3a4aa5bbf46c667f4833d108e1b881d44b1bbf01379ca57207c26f7490e9e1c5cb73b8004a6e4870e895aa10144be52a05dbb6b6b514ed6902f2842b42bb3e45d220c3068da298f7473e3f3f6e931490f0163d679c980cdb74755112aa44ec3ccce0c5630ca918dc981751c35ade119c176ec04a1181a0925500e60f278c9a040b1aa1e017fd3a2093d752236d9bf8fd7f3657b6f89e301ac7f9cbcb7dc7f46e3dac5895e4c7cb146e47d67bafa0ac6b464bdc45c5ca70c04805485c7f18b31fb4e3411ae8dc517418dcca8192fafcb10801357f1601c8d50e7c191ae4c6274019344032a2c9b96ef8971c659bc17c3b89150459188fd65869d59ca998d15bf0d1ecd1f4829cd5b92c562b23b99cedfb9e42d4e7b732a35ce89d98f3936c3c1f84b5b30a7438b362154d6cd0e36d943622baa8302b26b48396e71daf2ea39ff9fe53107ae85a2547ab868fa11c31b8a24551a73769fae96c8c9fa71073c3e5029fb1fb2512b164390d22ff400f8ba69fa50176f5c8daa9271a5b792fc55cdde38102613b08ffa9bb1b83d7d7210f912f6efb8958f44e09a19278c34a7c82470aa252a76ef3ea66af7244298a4279075aa2a37e193b42745847955d35bd5a13af93c4704c4e9797958161b2701a881afae04152a73167ee2d211157921556d389f8442075d680bf35c53e24c2e818ee4db34011a7149bd459998afd709cc1b7f240745f20b6a8ada50a5d04387458a90fea517d425feeded29798d1ae7e935609444e45783914f2305152bf3b7e2079b799e73bc21eea52444452e2a8f478c7a302765c2cb309d1e3e54a0deacb385e694505c04605dfd12191c466155b8047323f1ac940feeb08abb4671be027a0bbc32aa78145937847934b6fbe2a82119314b691223bb23016e183ddf92f68e660a530790b15a976f1146b9f460775f4b1dca620baedb0faa086f37f535c8fd7b87a5a8b717ba71ef364a674d9ee093f777f0204d79bacf899a2ad4f39ec13619b6658b3d68eec197320729b4f3cfb8a1dfbc66b403f1a15ab8bfc92b9684374128cb850f1234174a5255e5eadc0dfff331ec709ff1799f6bfe28d3eb084c82cfc8d5262178e5f60b9e54908c34627ce6674e40104fc1cd459325ea6c051fa4726a178ada3d22c580a57a82c4e2b757707389f2cdd7269f67d592347a21281c1657e920e328bc783ad3a62c4696ef45aa4bf1d074f57f722c2832a956df52ebd38b8db0276c0f2fd9091d40199ca1744e1e182deb62013a8f396c9fb7ddf63954c14720ad11aebd01682b6a6a0c001fa879bd0d4fc84d4ed357bb6a4a9ff957898f40615040e1a4cff918ffe02d72d602fa8bd04ca46b5c7111c1cdc22d3a7e33db46c8dcdb2d433e3fac6c197f5691618676958095632bd9241b80a95e34e6ab435568091821ec1e574aa69683a56845401b031eb76c3c27aa817b7533057736dd61330c82303afb906b0e1c2c26eebbe66b6bd7396248340a2f0ebfdc0f2d4202a8c87cb585de10d41c2a98367441729577ddfd29d9067d83e73e79ef1320a3e1f21c4a66665d6bee09f4cb3e8a80dfeaa6234468ffa0f79427d630758da4dd92f81a89c7e0e354079793bbf2f6a6448542231b8cc4fd2c1ec4732d4b71038bbd85e437f5a0999fdf0a88448978724d437c110b828c40eb215a730676747661de9c7e019399f5856a73d2cc7e07f17b2bd6537cc4dbe6b91a94b630c5445231d0d1a0be8774c8bedee7a94dafd1ba5b4d44cd1ac175f8bf079e8e4393a690c6bda7200619714cca773004ab56466d9b9d5cc05bbe6e9e00ef12365d1c102e6e508f7177eaac873f933d7f5a2d22a21e01524bff57d89243e03b78f00021549b8d116f3d87d35b551bb161b509e93dd10fc05b5c4854836ee17196413fc6f5f57e69ed561b644de5e30693bd187ba1ddf9572c4484cf53b2ce5a3a4d9bccb9f723b282fc85ad659bdc3bd8a02bc8f0846530463e149a84fb9e6ab20d550252f12b0449b19c3cc2230c7d4096dc7e81898be0617a0f33bc2da874b7e5823ee646c59e1626b350fdf9e6fdf406e973a45a9b867ebb6e8bb76a656793937191b6418124337ca6b998019ea8bcd9ea307a3b619d9f27a21b3b8e5a34c2ac0bff36b33a77c284bbe90177c1877f3015beec951ee1a8061d028ea6af6d7b3ac18c7f1c4df258be357f58c747b0fca5a87871f642f17846c71ee5a244e094cd12635e70e3ed760d912f33017c365139849dc09a10e9f709a231f0ba97a15989bb5721cc32fa93f29d764778c34a1cd9e058706520d31c0b32e82dff82caf58fc9e2851881d19f9e971fd12d0601d56d47deedd7d33b83b52b211d09c63c22b05f5e452cf6775f07acc8ad6d06460f82de88a8cf19dabc1c64e0b6763af51f674e3ce48202c14f40502d8487159a0e7032016cb4f62b0bfe685300220b85e960b01f53d340e77f522d644da47030a769e4a5ff3aa55aec8622094b9c52d0feeb7bb818d54bb5e84f52db8bd08ac7f7b6dfc84c570a6f327592be16c22a05e843015771d1516c29396ceeaa05bf9170b1e103daa38ee9f30afb56672e2bcff6dc5de681be562cb709b71db77bf9d997f5bbab88d003eabb5c12299078d279233caef5a72656bce30e274c5e6eadaef3ba1636e2406855abb67da4ff4503c736fec911f9bc80b96d3b9fa9716437cd10e3e695a47ce6edda41d3cbc1459f9f70f3ed245092dc742667c2f35924439dfd01df022525342b2fe45533397ccfa2f4285e78823d0da46b12757b0ce468df8c376070634a8972a403d82015e9f6641c60119819a22c7be771b5c4315d45699feed4f1ed56f10176e59e0370b01895742fb55395c3f3b24221ca8d449569f26b376c9fe72f0c01ac02589b4b1924191dcbd7a69aec4bb387576e31a4f60f3553981adaf0256f16c40c7e7e76b2c057ba18f79b226402176e745f9f8987a94aaa4f43da0ccc13d285bfe974babc327c9631b11e5f128e62e4ba9304df238e21784c821114dcd6c5fe77af5a29673cc061540677a217bfc2bb8ba7ceb73d7e1e579a6146be3970cab403d7f1872739cec923cb1b1d9483a2cc26057e4937265a1e4cf8ffdd3d1505ba28130973fda3cad7d1f1627d45fd2f30e6053096e6c8f2d0a65c8d82a816c2a55999dd88020e92e1cdbb14847e13583186725bd94c78c174d85cbbd73767eac8edaf0f55356cf9a5d97c951248b941dbbce809a9b74a051262bd98dec1d2e81554f082f80581b3bc3e40b4b221d55505c8009817a0d101c8f8d3bf837bb105d8007b80c9ad0f35090d6c81c417ea02dcaa2d92459f7c87b40c67cde83884022eba239fe1d1c2b6215ca8170379ed7f8cee553cac02edeba8874d7abb2c8c6534b2f1012a083e0cd8778e0244ed831300c9b55ae9c6c1e57bd592a280fdfeed8acaea0f2e53e8395cbd0e64a6ca4a81b3c848302f6fb7d49bce25fdca5ae6f53f3a1089f6e934d36931c0f3077a6d8c421910bab202a3ae18c574e248b51926f2cc380c3219c7685035ef89468fc4e831b1df4d25ea9f4e441d7604827158e00cd5af1f27ef2cb89c07e394833509f3ef8ca365c06591a3d4eb79c0120221e65a252a9b8824a02a158c81790ea9fdc466f045609d35c9d77f22ccdffc9d0a9ef9259c12e732df5147086a34ed75dab47d1e21a9875c9669956f5fcac352c66ac4291c11c09c9b37ab96f9abd30ea23f9745517f777ff85fbc4465e90fb47026db61d16ecae812535a0d1208862778beb0f92004384101f7cef859756933980dd8cd2c7ca2b8d1dbe01b42b0510af84c37931e329180ccd1f6caead9bc676a4b0f6cd468f5b36bda6e6f1129410a95baa462d7c1017e1d263fe5c0b0531d6741c6527fb51183b9b9ad59ad8ac0e645848f5c4c17e5aa5499b61dad037236a35dd914421ee74f0d595ca929027bed379d95ae7918f4df433f7d0c19f515f745c8be9ba820a2980636ccfca81d9fb09ab9e6426c4ccb2639bb4735305ce5b2df1ac977b9080609fda08b8e173287b805ae9c84f6d37da39265b7b06d58db9ac47e430dd04a8e2e82f24bec37bf6af2bc20ca7bb7e45d54b91c3542888fc0964ecafe033318e6aabe76f2b410bf05b7dc5461c9656ac0ea8b3d3a1c66bb153a4d7e0ad0544945eb3064d18706c6add2f00be8710990895c1216d17d7967757f6f245941b584af8509273edb30299702b5517a6d60353cb0ab17bf547c5b1f4550c2be1a26a8924d120d22bcbae5c6c5d0712e5e3cb45c0cf5092bf154128cca02d005075a16ff1c898dd8bd8270991b8ee01acf2437ad90527a3e4e994c56e7c63748e237e2758e0bc704405469496673323973cc737efefdb9a92e78a73756aa87a1223dcedf69680667e669d8d4a311ec05c28e1e2b726ad828aaf1f23d1ce73a48e9f6357a35b6f8b5f2ac3a3126d94890b2a32f19b175d0a2973e28f945cb57ef4dbba85b019bdf9364e2beda7b949cc1b33618cb3fe57962dc61b943f6fb755a8fd1e6fc590c611906d931878c243366382a5538bfb1c4cf0968ecf6ec034cf0984e2fac6ac855a6f891b9a0a660ab60eeb79ec425903bf511be63da0490c9477ee231d254eca1d552964145c8915528d8ca778cc94788d640b6023bc88fb7fc5c9d93e26ba9b023e920c40d39f1b462702618807b53417093bbe91b1707bc2880b197f275b04efc2822b66b31af36697c1baca240ef9010518cce5105cae44c7439ca78924cbd63ef35d4fda8eccf1cbfaa43e0984ec8e26cdeb2300c88a7d014beec70acf84c3e522989dae9411c939de137299e1de3956ff04fa727a8ce7e071e2f22ceb581f5d22b16fca079c99c7285decaea6880f1016a2aba2ff1c75a8d0f88ad1e52bbf385c8b0322f774b554269eea3e6f2ee7ef7170538feabf0bbf8eaed6f45b19b04411d2cc67f46d1d12a8c2af54d08cc4f8aa9ceeb953954372c734d65f70e21b08940c825d46748b7318ec01fc82a981e3c60547186b8cac13ecd80c30da8670404cccc37f99b99d33a220159c083b1b513cf529303abf3580a9cddf1ee92410700bc3e4eb144b4be4a694f206c50fb6ea04fa2c13ae1048e1b3989a3cba59c78d262df668e3f5d80a978a0dbd073c9b16603cb8ccc410ac34d69630edfe6fb1699e83e17708376178edac060bec4d60cc00d2d0d7a0fd60a86dc00c0a5893b79df401aa833989dfb90865f021a8692839739f2b40b785ce454d193bad764d806a2f7b6583d8713bc932cf039287dec3315c78fe422d98f74b93ee81e51e7f656283156dcaf596769ddf17f34519ef9440fda41104920bc8dd6438ceb71650571f96620742f65e14bbad0e9e26c0cd283ef444982c0d1f94f6e4184875ba04b03c1eeb4dc17fba1fd332a6d0f154df730f36fa452d04bd096e0b53e54102084b2421f37fdeea89d78d8c2948e4c849de08acf937c69610ee0f8a07308ea17a9729d58e3d8a40452608c374af01607a6afe8396b270269c92b133b612b972b546c772ad9e777ea0ca2fff6b38b88242d410b56318a24c302f75bd829b60c3c908e5060b99c66ced7e4d447cc4c40459807045247e755c941376417ee9b69c34b4d55154c87c8535757f8fcc343b87b450bc15b251145b830ce2da6bc9690ff4c8050e8386da57250e77ba25ae7d0164f9d862f68f6acf8accc2a21a9ff8b32e71c2bf7d9012dfa172dbe4e00d601ff3d076c2f44d516e5878c2c5c8ce83fa6a734215f60ff9285968e7a1bf2812fda5fadff471ecb01c62545ddd67c6d342da23ad9e8b37561f00e2351f17f6aaf1e1dfb4073eb110d735c87ca52f4f4c0ea61878a1ad01a3c827452df906e7ae9f1bfb8c47374f0e74baa75639dee76f81094586d1a59808e6c044426cabf364928f37c8f23908b008e27d5da056c49dad3abb627cf84d76190c765d240be504d8c2a2aea2e4a48feef830ea94cb16c7c21375692fd481c50177b3952ac72a9ad50dc6a3755c6283c833348d7eb0a32c807cdb1277c20bb9da7faf0710bfcf0d64d1101a64bee0207237e115c3f2a938edd184e74fcb9b18f8d5393ff524b299a6de076de72682d45444cfe7a7edea3341dc7802b547b52233c255fff7178a87bbddc9d6ae11b27dd08d8c8bd699516eafdd6e936d181e847a17aaeb2c8d7161999f7ee07f7586b3792a21ece5d6fe2599961cb21429190ac520e1f305ec4589ea1015a2754f7366d0225958c4dfdc048e81b6cd125a24e212b9e9349a3c832aece32f56fa67b2f023bf135986e9490481f544e32b84123af6f879a52017a64a281162d3ab860d82020217ff304bebc098af7046f7daa3b7ce5c2ee18d91768bd9024fe422251559cf35ad2596594c23d6a22cdef25c3a05e165b9b8915a1ff1b6ee3bdb4f1a630493c898d7fec6c18321f881b685157409e0242bd7e313d76694b2bef2b00ad28351b999b2f44b7f2fd16614a109afd39aede51fdac0872316072bc0ce50c7823de13cea94669feda7b89948023802f710086be76398bc164efac9a8646a052f56f8e3f307a76159203ab50385378b3f0218f536ee253de61824e591ddada9b425f38688a47c229574a9fa84efa5ab3b62dbf578bbf64c6b9d32ea27217801b05ce00432701818491c1c59a8b42fbd4593c3f2ed6df6b457c442a015e968575d09010378582b5d6be91ebfc662d077b24f277edd21364e546130532e0ed2ed5ce8b1fa3f18bbf5925a1bd903870bb9e0c12c488cee50d696a9dbd812dce977670988579877ff9498a737aaed83cdf1d4c16e1da05872b84bdf1e1dbc31704b9513e0bd94ee3b1586ca39687a5519b8b30363dd0fc3e0c08b6c00a223ffce5bd3b09b2f7974e55139986a86e1d9cf43a43dda3db328fb8e14ece3740668452fe4a1e1dced5b299e7166019b0062c9e14237fb54f4352697e53e1d6ece73f7bb3189f05374f608159b2ff1024541ee14782527736a469bfb9e139c92efcb810fea3dc2864ade6cc3ac38512055667763e38f32d27db3a1f91a3624625d464e9843d4fd400a1b957880c9acdbf5dc7c2c3ac97f4482b6aa2cf9aba17d92625d88e49d166255d66f9bb0aa408d52b6408e9e8d922fe933038c8273bba6865a0bd468f33a809f4c855e4b5527b1cb333ef978aa4cc12efc4e60b0ec5a5c2e5000ee5b1836a8c99a916ee3ff51c12e6becae458d8caad82b88bcd2a5d2ee85c08628c29395c59c18759d13c84c2688a1c447e4c1415ccd0555f1c2733ee8372233d679fb8db4fd61e285e4d76aade80f07b2b9e0e8cb71b585974d19346ca949332e9436639d10896ed8dfaa92c04c85f1c83caf2005ebcb31fdd98204fd08b5c09df38913088e602fe7833fe8017bd8a9f836b745d2e5ade00207553c27ae32a26ea8a3d0217e68e9c38a24d14a763282ec071b71c9ae0f42d9e3038dc80aaae2ccc1f11ba59e6092a246e9bb15442bcf3c4309750e933ccff43a6cc928d914817187262d2855ea716a434e2a9d9c3ef512f9c5e16f39c1af9c4abe9304ff3819529b5ebeec93d47257754eca49498a904eb58a2007a8e4f33f3a66d455ff5127816dd2f9f00e4bc86ec838293c25c2fe7db0309e7fbd53bf93443879894d775232316a9f33ed8217e7b8574e169cca4bf8b2f477419d0e89d037f0592bb260ee4680ec3e07fb8f095fbcfe06bb615ce8b1f9c98accce3e8b01b20ff628acebf997177fe33fb6f6d5c1619db6800827d8122f3d80a3dc34e6cb817999f21b16dee67ebfdbb7002c6a44ac52020180c8705fc0e7ff8e35add46ddfc2f558061478cd1640252ec4cdd30f2b0588f498f286c9959debc497e93689528610a3a1bb062825a904415614a2200cecb04dd4b414b8b45a53c769dfab6ae6cb6b5d9b1a78f8c5af50b3b9b7e0507993b25bcab0fe9bd5182c76da582263381590c3f31ae1038dc839abd7bd8dce37cc7ff8c90aadf6b73b7dc437c3643050d174d250173b366a2145d24c78a3f88910c792d52914a3b505f28da9bd201726e7fc91f602b38a58952217e5e9b6f491e9f28f621e2938cd06394b34a44148a22d5018d00904d0e464188b6fe9ed084e60d4f9b1baa51967b0691a05da696fd75099468d13cc3bedd8660708196a95b18b66bc7b61a0a1f256384ed0686a1b2a1f4e12f742c850a59e4a3cacc253eb27aab4b04a6c287f35bc1604f12f39a58c86b183a6aac029950a1345810d69ff39a8c6671a73695b575fd1c165dc48f689f037dc35547511826807e14b02cb0ab843485c869520c8b530c8c1def9325e21b864adcd9c47370c6e883945b14292bee2a17d60023440a21e8053b055ebea69dd48b66010a507d85ceed7eccb4d833ab47fdc4d53131a14d644ab52e578cf8fb20435657b7557659efe383bccab3126ed64b78c312afef40c27be7c89854f434e5f1b555e8659f45640aa1eaccf31633cf91a812b4d67a2f87003b981f5bfa0f7bd1f69b8b1a4ad7a122884d940b90c77f85268fe678d2354e9880d2fd4188a98039ee7e005e631ad05d40bb926aa16e94982925e6dcd554b17d91d12ff7d694859c3bb19d0a2b011656184cb3b92d4bc0925fbc44428a629360475eb8b96c2f722f2fc330b4a9cfac1324349fbb2c44480f7dd53f2781f4c3703d73e4f6072c5b09a0ea23dc7045f46a3a30e4c0abf12b4a7aff917e029a82f2875701bf1b9e5f9cff407b73bdb58c39c5473cd8c7f2c4a457fd4b7b1e9aca03c61d647731340c7ebef4e420a9390c1a80135fb46371339db3ead528d0f8050213bffa502060d5fa62af9f539022680cd041a542bdae9d25eb46ecc6d474a7da8c3305c33d951dc48aa90c3b2bf98feca3eacad426ac9cd07608c1ef10e2db99da40ea5302cff7786f7f6e8920656a0e25e3792b6a5d0d155341b4a0d43666e124383affcdec5a4fb062b91709eef2dff80c2008b51122ef2df512d4b320de313230acc60eb06526bb48194b792ea4d84de837f28c1021ca4a055fc5a9f43de35a98601aa690b60ae5ab14c66e40b4311b08b750a5f20bef0928956e7e8bb4803c934dc396e00534d34e9021a310e735f4010b2da1c5afb5d76ef31d4e5f8096b657cb4da1ce889b812425532bea59eeb44a137a379da8ca8d646245b0a22f72893b82740dd7cc0f6c8ad5f80f9719dfb41b49aa8623056fe5afb4be122cac76aebf5b7b525a1252bbbbbb794524a199506c5067007bffdb8d2650f1c220ef0bfdffec471f5ad0472fb86630be95badae2e5657a737a75f4941ffca5b506b0aee48a570fa95f2c4e92b2b9c3b3bfda7d77f9232242548afdb57c11ca75ecb9b8dbe3eeb6ac535056d545f9e2ef4af600e507351b449e510cbb386fc7421f51bfd5416bdfe89eb13b743f5f4ba8025a75d737ec11cdc87af395e2db84355860fee1976dd427efac0d1a5bf21fdf62a6ef5f79a6f5cf5a2de4a2590daebf9e39eb5831dbc017ec857db8bdb56daa852220fc53f490fbee62b91b422bdf8fa43be0a9ff4a5dff86afc133781a0e6a63f716dd2f4417e8620f86069a3223ddd7c257e897f2b9e32f1aff4d1ef718ea5cdea57e2ab3ef5a758daa8e8872b9efa50c553bf71f137df7ec5554f411b5549af0b0dcfdac3144ad85e731eb76f2510dbeb9661d7a71b03bb7e0bdb6bdfd5b77137eb32d9140f1ad58a4e1c9eaf308ecb9316750aeed0ca4aa455ddeb8d7bed713b7078faf61a87c7baf4f617ffc66957a9b45a79ed38aee332c6bfe113f073277dcedbb0905dc3de364d7b1b1607dd81b156fb09557b8febcffca44536deeb6a7be6678579a50af87a24d09a629e392ef9b94f187a255da86fcb7d5d68dd714b8b7363b0b63ab1bdf65ae31ab8630377705b79eb129def0f8a74da2dc6b56e38c45ab74dd36cb52777bbc5b1e14a8dd7ae5d6007d9f71360f145a13e8efa7687ad3d9ff0f5f173bcc3dc86fbaeacf7f273775a7236474caf5d07b4a7b4d65c7bd47e1fffb6658e6951d7722e4f1e56d3b4b2056e2b7d685ae943d332bef61ec1433e20ea95d25a71bdaffd0fb06bfc729f9fa39fcb9b158efab96c612b813ce0c4d14a1ff97de40732f65cfaa8ff03ec15972d00a957bc7ed13b10dc7e85de3d87bb6e23c3155a566861c176915e7cd2dbd724bef2968f266d5cf99194e364f9d962cbd54a89e35496ec7ed426b75df915fa7e0d4c80fd91b43abdb8c2f8a41556e8e3ef93b4c915f27779437ea9bc31b5f4fd634bcbdee3388ee3388ee3b85d5dd7755dd775ddd972aed0c571b42f561ef5a872e5348ee3388af4c596528eb1a58ff6492328dab285470b255aa1a503a93c60edb78d2bcfdab9b79fb7af5ef7f4f373d9cc1df7a3e7e7f19d10f87074b5445c6c0d7e9b7cd1520212a387df939ef4233fbf510cc52d82afc1ffbeefc77e4a122710a38bbfb528963ffafe9ef61ffd133fd20df8babc493d89c7d749bf411edfdefbdbfbdb5b731c17bbeb74d76d7efee8bbb437f89b6bffe9507481146efe7de2dfecff5efc4417be70ef127c6a4df0fbf0046215adb5d65a6badb5d6dca4b5eeb4d6553ccff33ccffbba2df1f83ceffbbed2a39bfcf1c18038bcb210adbcf9a5e2f20dd6d4b41c5c795660d6b5dfcafc9a80513fe9f3c82f1b8416b5d89a547a6fe54cfde94d1f57a19ea0f853fd06c5ffbe0ff787e3abf8a9bfd4835feabf1f4bd58bff7d2b3087fe51044b1c9bf60accfafeefd4a91755630a2c6f502ff2d861897afaa409b5523a994ca552a9f4855fe90bb74822799cb4396f7b9e165dd7755dd7759df88d3c84293bbffaf797477ef5aeebf2ab7fa1900ebee8e5e8b2db7de3a7c85381597ee557a720e090f593e239eebae06de3eaf6fc347dde6f6557e2cd65e34c2a98a41ca1c0d4a7c82fbd6afcad477e7a91dbd00289d14fe38ba2c8d91f5f34fdcaf7a7919bb649dc1a9800f1ad9d408c7df2e87b8ffc247da7ef4de5cdf7a7f226fccdc334963c4c5d0c9fbeaa14a640b2540241101447111c45118542bdc751a2288aa27864fa713c897f12c59578fa15f8246bf9f8f734756bb91efe6932997898c0a2df36ab697f3bb07b4aa9a64bafd39ed3b42ddbc87f7dfe07fa3bf2bbd5af50ff7d4af5a530e45ba5edf26ce13b10553ffd7e4dfc909fdfc3300c4ffbc50f57f869d2504fd2506fc38a1f3e3efdc8c2e6389389962b52ee1388d14dbf229507c0e9a6f26481853ebe6842fdc8cff14f650853f658a25e2c6f504f2a6f542ffec87d2c7fe48e2aa93557b9ab9e7e29a55a915f2ab5c90490df919b1c37b9c7adb5fe8eebfd83bd4d7ce4270b7d0cc3f28cd1c3df7fdae51903060c1696172f562b952a952249146a65e57432994a2512691cf716c53004c1efd3daf3ba1e767da350e5e9f4e7d652fdeacfdc37b92a4fad879f7a55aa3cb5be7f65ebfb2959ae3c8a53ca5a294fdcc33fe51e3ea532a70fc3f22cfdf8db34bd8d6be2f447ae7d899f3f3269f3f1effeb13cbf6fdbb701683fbf67168cfa0f9caaa6a1adbfae7f5b19efb5d6afcfdaf19347efb4d6af2d0a1f8ffefdc64f15fa579edd5390d78e3b1d6a5d350ae0eeba3c79f4ed3dcbade9079dc5cee1b99e10b53dde8345d4d65e18cab230198a2d88918f51950cf493cab41b6cdbe910151bfa496558a6c346851586b4e0426b11458b1c18f9e0a7f4d62fab3032313b7d8b2dbdf7de7b33367db56b1c10fa8504d25612c6d4c3bd65a5ac6a2fcb042df6fd7a4bd3be38678acf1730d7c5d2a15bcef8e14584fe82fc0069020c37c4be5f4dfd962e9d5a99974dd3862082081e228eec27764e7d5d17fa72aa823e2581b6589d52192388a0514ae91572850bf7da28a5f40ab9c2e45edc1501f03a13915d3f87905fdb2404005a28fd4000bf6e89255e5873fb9628c3ca6366ed66be3f9394f143fdb069afd89a2fa660d7bf31c60ba39f94524a4b24165309bc419fbeb877452956e5154a51dfa594528ac97b494a55a995d3bda6301663c9a92b58be541659db3412c71303c6f831364e65b14b7eefbdf815ee9c531594527a535980f7664dfbee3d550180100260d2d746f844385591711ee13b559131c618cbf8c427767d4cee9d5d5fab9a04967d21a1c2bcd4171258324610c1c50c400830218d2128f5eebdb7de7befb53033da7f5c49c93e5f7cb7abd54c0b161b538bed0c8fedb5a823b66fc90fa383965ebf59d75e73dc3ff7551a9aab5b1cdb734f632e97ab484df74f65b6afb5d61bb5a5dfe83af75be980950eeeba68b4ab7ad8102859b0a6f6496cfb2735c21bd03de8eb550af4b7b7fd769a7fdbde0465f35853889539bfde909f746108c7ed4d92dd6ff9d5737e6e084984db1cb99f247bc671b9684d266f5600ad31c075c95f852acc9ab94b62d7dc44d396adf4f8aa6a2e8adf87250ce17f30e8077f01deef1206f1bd074b187e6cc4f71e86f1757db0865f3f0cff2b5ff85e2c57557c1bed3d58be20bef72f8c3835143cd5ffeb726bd9c3e6fbcd9121dd3835237fe1c7663c77c45f40ed810852e0aa812186d225be58c600838deb72c7fb7d5872db0e803b61381dd7250b2ae338351fc7a91979f89b8b5f790a1fafae1724e0725db82ef77b2880d6b4a0005a337e7f56d7873ffcf14f3ac32ffe7e5a037ed5b92ef7a593a3f3e2e17f5cfcee75e55d09a4b6606b0cec61237e58bef042f8615961d7e5be02684d902a248a4f6b421bd7d61a0ad6d244f6edaef0d75cae70894b1664dc90a62d9ddebe1ba5efb84e879fb84ba4f14f13fa0c2b73ff9e3817d3ae476bed577b5f5350013840c06488d2030a7e6050831a6a5dbcb71c2c2dbf7cc7f4a4b2c5bae01f79ed19bcb185d81f2782e0c731064ddcbfa7e0a7b9d955dc62be6b93785b8d089cf136c3da0f50e12a13dda665acc40253746f4807321b9bb5f6372d631bb35100032040c90c329801e854c90c16e85fa743538460804d5f49aed3d751846b686ce740654045c5c13e55c18f0e4f5800072b7050639f34b77dcab5c049b74f39fba4d9826e9f72f6491c683cd217a1765ad63e6b9aa6695bf9a376d5c55a0eed2fc6da6fdba651a1dacd8ff18e7c1fdbe09c31d6f8cdf8deab84aec1f42593c67354beed0c80e0ef0c6cfcfaed6fdb06e4ae706c5f7f03752d7d046851bf5979fddca903716c4f5f3565035f8361dfaad5da07ceae9b7ea5bf9fea18706c0fd2edfbca936e31742aebfad35a87347cca3f1f3839f6f7e0d33eae4e085f87b48a62597bc4e0ea3e70faa67de0f4ad0c3f6b7a03fe87c3b369fdfa2b3f6daeadb53c6dea59bdd7fc8441a7871e2dcfead592d2a2ef13ad51d7fff16bcfef2bcf1de4a7edff38287ea0b712df13ebd3174b1cdf8311a0455b89a3c28afa56deec0f576a75f590df94def42b65c9644295de549e381c2dbdfed2f7a1fedb5e453eea374e491e21a5281e220dbf5da2be8f8740657faf5f1791dda924555df7df47c3f7815a6f1548edf50b53e4a342be81e0f621ffe817fe2986e24a0cc3d7fff1d56f7c45faf13710d45a872208aec0fdf44dabf0c7075f9b78e9e3a537f1cf64fa9e6edea3924a9bd4afc2271ff56758da908fe2418bc24f71d48b2447fdc6c31ff9f6294e3e056df6a37609bdbe056eb8309255b98a88528ee8b948cf7f87f4fa39db6b6dce91ff5a8b69cff4354e2f8eada4d7e55e3047ada70dbd2ed7a53c6dbfb7fc51fb75925fef20bb67e54f43529674fd2b55740e27e0f42baff11c5cffe55bd7fdb661e3df4e1c0ec85de1d0b52bcfd303b9fd06f5db736fc376afc317450a8227dc881bdfc014177fe3357c31fca7e7ee600c38f47ffaeb83e0156fc0a75dbf2ec350ace0cdf7a00f9cfe7dba82ff3dede3091faf95b2c8d71f3fb7df3efc522d95e7f6f4ce9cb5ebf2dcce1e1270f51fb7fbc0e97a8ba1935ed78fb7503b69f381d3357dd293aafeed757913bef85b79f33de88278bad0f5b370392b8ae569235657dfaff9c942b8c5f2ac2e6e2bbde6e79377db1eb56f0f96e70ed583f9b717fa383ec8bf32ff76fb0896f9bf9bb10452fbf69baf48235f910218df272690190111040840403c403c4040f762ced35dd6b411cf4867a49be55826d2b4cc03e444d6340c94cb8d31d0b5cf536bdd382d73db0c1f60cc7ead09449dac012f27a783d1c524b2a8c304880990112346c895930a36d6364ee3b5867b92cc18df274a5c8c374ebb50acb5b633825d23e3f6bc7bafa639cd3d1eef3178c3eb3a264050ae14186dd3b04c33da360dca927b2fde625bd39ee3b64d8382e47e1ba7618e52da0476b1b53b30020202020202c2196b1bcfb6cd7fce3e81b58c392ccb465dd165028471c8187346f932017a92639ba7bbd80cec84b2923052672040878db108c63462c6b2bd5630016202c404e8052e80a15c6c635c022c6b010b56f0840a52e0045113285032c4c4098c248999a0044b20512209248448300211001d1174c4082c04403ff79a2000242b5801ce3d414195c868c4d833167171d6b658f8d2c21765d9f015be7a9871b3ba84082ea6154130c6312b8244d1c4071d10807c7c72f0c9c1c72767ac1d397224e79c878872f0c9e13bd8811be6cedc5bc2fc3803226aad59c3b97c982fc90c3b00fb159c31c3cad019946567cc00e2e2db049ccd688b8233960ecec099067c7c8e1c3942fa004511c5c5b92663d2c8c8c80aebc2e2c8912318f6837163e103c76df94559f6f5a22dea15f178a6f860e144144d724ecec1c287582c16c3a2d4c5a7d14c9bc0f0056ae2e3e3e3e3e39383cf913ff236623c642de3289cc8d99a744646b445bb255d91cf0b72c65a93a72c5b7d80ba0fd41eb76d4d320c465936d3163df21dd19e0cba23b986293e537ca6f8ec8c20a0b74bfdc3944bec9188b1c838c4f881518851670c62f4c0d88191c808c4f8c3e8c3983372e0c8d8c3c8c33864dc61dcc0a8c3f81a3530e630e230de306660c480cf68c32864ac61a461bcc018649c61b4c0f7df84d036aef362f7e27c2fe775b7e606d55a2b91082e260f3cd90b99c97d0ad41c2263006bcafe6b5003994cc775b16f53a0322050d33e495692bc34bb74cc807e2d05fabd1c75c10f42def4f35bac15f100085a6e02c348f4f4f4f4f4685b5050d02c96897a7cb8f8f6dc1e29b5568c61cc9b43ad55db342a2c4bef404f4f505010090514386b30da93240f32102528280826c3b8a314e938cee3e17aa25c25504429128b723110141444051254f4f4f4f4f4f4e0ac693d390807dd6b3f70f1962fd6a0702267a3a22bc2b787c9c5176b19df9e4fdc21a83d8f0addd5cab1f46e07894121f805651e7a7a465717324680e95c6c51858a16538ea4fcc0075944c102caacd61a74450fac78c2831de8c0490e7070832aa8a8b5f6d8a089510d68c0640a2936d8ca49c900ba25696b08e5e956a7ea602b840c8a62f07aaa7367b4082f4625348119256912be60b1f0a5ab57c99ad2eb224142bfa010dc0cc7401d6e565720828b5992dd2e00b6e3b0a8c3cd70ac9be1188e9564438c3c22fa26a2d7e572a850a7ef11dd190350226f0a6f0a2b737a52785264a223922449eb7503fc40e419611900406350614615c682a9032050db7d57fb165b39f578c76d2f482dc3b4fccb30ad69da27def38bc9c745995219d83fa96942c9b1faea66b18d67a23c946304b0a6fd193362d703f56fb6b556d3fdeaeab7c4b8fc5a2546a75446db3e6f25a9951908d2acdb87e91be78ddde2241d49882495a403ce3a65696f593a69663fc0f2826618bed5561ec89553dfdd4767eedb92bc66a9897d12800056e6a441630bdd99bc6512f4d325c36cb027497c61dd1af560d7dfb1178907969a7949f40ba6c132cc4ba25fb14bc3b8b1882b57bf2ac33de5f20d23a5916fa82f96c79ad62b624dfbbad6ba554f27bfbc571ef2d29e089733e4864a23ebb0430eaf2822a57546e4d1bc22b6a7ded12e424427f70044a79f7da034f290fa6273449e3b9373724ece119120b132a7a884a844e715d1ad77b4951d514e2e9287541ad9870c44ee21e7e0224a2e1005dcef0b09c124e8670d6558a8d3cf43ac0ccee94ab043a79f75a0343a24f5c5825062b26ec1596cd372907e764f087544dd10924aa34bc20465a566bbfed9c54a2e0dd4afcf685bbe4d601a2c7f13182ec10b46f7ae769a5fdeab8b51d6c9065984f9f9f981f9f9f95a03cc6d216d5cf0860dcdfc14f0b576c7a85b2ba32d0d1fd14f93139d931b9d5113fbec649dccca9c5d0bba16843a74fb2776c286afaea85bd2c93a2359b755d899ae0aa3cee8fe70a1910d328cc532b1176d5dec8224b2d75ed32ebef7b38e4d7223637c236b7893e2459263df532ca32dedd25d09bd33b241a52cefa9f75b0c9dfb8d5296c7d23d6ccd6d73726f8d6724433c138127bcf45b66a1186cfb202ce3689b7fb015e6de6f792a0f030398bd64d767c9e894d25b4b9c47d0b5b650bbb05d6bdf8662d7df2fd8f5b70b76fd2ddbf5770b76fd360b76dd2bd8f5f713e2b755b0bf9d825d7f3bb1eb6fa25d7f37b1eb6f149cb612d4bd7b6833b14ff06227d9f5772cc6b74db0ebef12f06f2fb1eb6f24bbfe56a2e5db49ecfa1b8910b690792f07d3bb9f4d0211be3d8211be2d825d7f1fb1ebef2012be7de4e5db4660be0ddb257c94b6604a80d98eacb5d6da1291ebaad65a0dc0b0b25210f6a1e85b61dcfad755df2eb1535451abf45a667bed25e2561a6bf9dfa66c6caa5b896dab0e022726eaf471122a9399c0483a7d8c0495099ff4f4b1106d8d4f3351f7347ccd0f17e4c77b1abe26f5f4696b7b4d6bb516638c7b0e676116e1eca4ad984b380ebcb1a37aaec46984a2c08a10c287ae830d244dd579f6cb21b7bf18a5eef19d189f394d1165d13ca171426344338535ed1344b18eef04513d0d5c0d0c29ba7d1a19d0171b83fca701a626f5309c184aa282a31965d94fc120da6a541ed8e7cceb0a55d8ccabdb279b4482c183c000e16980a9299fc710a92ff56b8c4ecc105e0c4f4c0e628c62663151ee16abc631474efa69f6b3150381b7945001d82f79ab095a827dce5b49e80088ac69745d2c8cb215a32580603c8a211233448c8e17432486272607314631516266378627862786278647c7f0e0189e98a37d5dc85c2f0f8944ba7c64f1b08c60c15857b09cb08e5854502ba7ade34870b18c60f1b060ac2b584e5854584728166c85053bb1601b0bd6b1601c0b46824b974c26ef020000b052a9f4b167e289780ddcc55f7c0712b572da3a9e439f898f3d11af81bbf80efc453e11ea89569ee8f444db13754fc4734ca7130a5c12faf2848905863f1d16587fb10c6179757b5ae14dec29fe93bfe33b2c1eef81c5ff5888b0e8b00cc18d47a19470c6c2c362c48bf9e391293387ac8b8d593388dd8ad55e89866294c542a4d2a049c2320412161d2b43830413dd3ec742a45bd76c3c0aa5b018c1c2e38d47773c02c7a33d1e8d47e391c85b4c505692b21503c13e55aed79d6179bd54a7ef04dec87f0251c0ddb4729ae2c8831db3a63984326287dde33bff1ddfe1cff11d1e6486ad8b2ebaa8f9b99fe27866654eafd30ea79c530ca84bfd7a929d8c4e5450b37e45d5957aaa5bed6a3d39e9b596459c87595817aee7b7e00d5d86336bda3763668c46680724fdf4b2323448849cc6e5e4947367686438c5e0e824bb3335a09ca8787232b232357400ebf651e5f9d2cf53ce4a799afd3c9d4e39db29a73be5b4624f2a6b84b3a3f1289c15f5b37544d462a29564e6c4688a226b5ad9164d65cb948d606465ee0cb779c6a39570d62d4af5281005dcb5eff8cebfc77738f79fb266acbed423fe3d7eec8e5f8fe38cb246593f69b85a2ea02ff663f0d6d1ecba38a982b78c5a45ad252d1965b5885a43d66c41d1ed9bb14aa395a4c58419ebb615dbd9e77874129213ceee4ba350df57d2085d178b9ff4001fe9f8a0830d3298b25d49cdf221a71142594794a562a1b181ba7a8d29b406149a4517da31a253841047ad986ac55bb2eb32d2cc4e9a12a75d05ded8617955c777589e637991efacb890af493d7e52697455eacb0c47a132de0cf74fdb4ffca45b4fc875b1335e13d4c57a314f66331354c683829af6c13f5fcc93dcd52b4a8d475b279cd108d11c4153e2343c552a0d1a23f465061a9f6edfd2144113d4edd955a961c46b1409b93335a034419d74fbb64613ea02458d296456a6860ca879bef8d0ad0e6f43b7343278426a4c1192e315f5b0ac311b8f4690519635652c7cac625d78edf5c537f26093a02ec723964fc5d2afc777540f83b762ad27784bc65b45d7c5a6b890af51bd093ce1a57f65ab0568efeb42561de3058f5182ba34652dabae01b0ba5cd53bbea37a4f95e23b8454c9c2f2626bd9fc542c2c2f5e7c2fc01be612b3c894ad5027d04630752da3637d2aae6b8e49f8549cd6d4efe55ba9b0cbd52fd77ac77756cfad5e65ca76d08ab9f0dc191a4efad97281d1592b567fc5b995153366c6cc5838bb33dbbad40f67afe5d3783456318137c2d97814ce70c61fce4e53cba51289441a4799b904bc311ed5609fad98acdb6fc9accc391e751b1b6a11b59eb03f1ed101a0549cd6a438adb9dceb2798691c946d802864bf668b92d5374db0818057af7449100c8bf5f31395c84085121960fda443bdd68c2d75d2eb7fa65e3dd0cf17bd5e9315db5e2b0ad11e45ce5a86691996731581c638e3a08c83f085717766b335376c0d0551a83608e2419bc14887d671f75e6b2d06b6d8a9125712598a102871118101d60694b83ab06960eb4e95b836b0f5a0c475819e1d763ad485949d2746764785d006a338a28b26ba38628716265de820e9628876451736146d930f72b0f50e9eec8f061a145b433165772928b910d2a182cc46052d082e78645c14e92795e51b70f1834c07172f1d2ac86c5490828b0bc840b0c50ffa49653a645be86089fd31d176b04199165bc43424b62882680b2221d8c20620aa6cd14f2adb8854c142e653e506fda4321db22a33a842d44f2aab22822a44b6229beb74a8ca900e5471b9a21081831da8c83212fbd4e9101525fa49653f1b04c25ac657a6c3468515f41046a3cecfcfcfcfcf8f8f8f4f0e3e39f8f8000101f100f10001dd5bb7493f7df205ed76f4773d5a0a305d2b5f5873c7fbaec704eb92024c4fcff5a1e597b0b86224707c701832640917078a40829381293bb8401313e04027a84a0f1888620327ba6802060fa044512444055448d1a1b2828f5009025379f5dbe910151c6a0ed7de8a6f78cbbd6330a60c93a3c1fc381fcec44f6ebcef8c0c134802bc699ad4a42daa89609a32ec8c0c738a89a94959f64d9a71c098076ce221db5a37186c8c81b00cafc3305ac7d50a838134cd0371e4bfdf65bbcd60c8907b39237eb08c9a61eebd303f1a14b2934c96b1b671dc36ff5f10050c9688af3ba36d3a5cc813eab633ba1b0be16beb14115c4c8c739c3aa8248c998c6aea14cd000000007316000018140a074442c1348dc33ceff10114000e6d984460503014c6035192a2400882208a8118638c218630630c32c850911100ba255741a962dd396d142aad22cdc62a4aecc8fef5495d80907ac7765614fb9b1fe42104ef2d2624ec8cd91f9fe6fa803cc00f765782640460471cefa6bf4a7ef47b0dff8e3100ec06d08aca9416f1ca3f945ee1e04c4ac16625b503cec88dcf58a7bec2d76050be03a17dfd9dca3419b15e2433009d00b953f02e05935ebcf2bbafef4263be6692e3a68f47012100106e8857ce8c08b5c75bb567dcf9fa8a9d77d6761df10de046b6a7a885f1d64d7fe853b4a032ab1ced4b18a7e66da6a76d8859f99ae818a91a2ed4703ff30d841103e3a54a551dd818b0de4a5a3acb1433658b2bb832817c58b9da6e637cd2e222e51bba2ae88f6e6a87f0fb349a2e33b755b06eaf4f4d7aa9db331a14f0555759c6f4c58e23dfd939d70109ba147ab4db76fc5a26a7afe6e2392152b40fe340e85d9a25c2e7ee3a32a078ebc8a81e21ec5eb59234bb5b58416d67fd227d16238fcb0469fd2d71ca8a471d978cf573c0078a98bf6684e60dc035ee5ce58f56308a4932b10a9eab8e94f4d6af54e44e243b814ed47e5f162ec7012deae362db91bad29c7d6e0eb509e8cf356abae7315cd1f58058247eecc8b111a34ee0528e01ddd1ae4c825521680b561857c455f51595d5ba9c16e2c4fb6502f044093239063e359ade3df92e620bf02a916d9bc4a28c777bc53baa80bddf5ebc7f799d239606411c811f011c097f4cfe031156b269cfcaf164c669363479342516fda0e51e069c97fdc2d4e63bfa07dbaf42d233ef07aa9b00ed6fe9c73d4fa5524f38f1a4d14e9474f2504e967084b8d2091e9d788b596e83ef184f2e04799e0823a97ad64a502e27a97306576211a19911b48798ba1af26bcc938561850dfb4e50122af0073ae26a13d59f5b759f139ec54fb9d0a4f5040e4f9c2a96a1f01d6d5e6215576e6ba39607dad0b32883019ba772e021118b4107b32a3c4e47adf78cd06ebb088dc323559329dfa080d1dd4fd0622a30f79a4cc732508d367a1e0072f62314388dc545f3186312a968dac22ae69a1cd7844963bb6125d91631cb563c57022f135a9745a806de3e13c2ce9d8e72123fd989d1e3907641de120bacba236a2d7cfc0f8a5d0b10e5887373466e3ee8946afe6fc676ff01bab0a87152dd61c0938ac5cdf05055575541d57b13a3ed60ab1e1899c1e85864c0170111f799be25a3ef0f601abaf3431e30da931278c0a82f62ed71a40477a39729bf2e6084ca703553fb1ea3f6f42b63ff8f811500f7fa0a0629f92a435bda2f3d032ed691612c36d7563d49339c4498b14463838e1a94fb497d0b9fe216b7f32577fa06188c59c1ea8e8949651eaa3931ca05c5444e0787eb5e791f350f0db8334426ad655201c1dda33170934696e09610157874e07e3ce5fe8d2324caa9a883c988ef5634b95001d369f0c330a27fd671013071f7f614db9793a2ec898d05eb7e44bcd89657d8801bd35f67143e4d891f36fc43a3864623cb47137f7fda91a15673128c514125cd377e375a847b83aac638386323ed8cf1a0f4b37ac9bd12292e3ac0cbebe92f0452c79d372dff4c5f33073c6d4893b4f4a2cfd19a5ff2b3b127e012fb87b56c4c3365ba72c14c23da8efbd614f0200096ff7be4d154dbbe95411198b2c6ff5ef22a221a094b8f400710e4003bd870284892d1b80364f3b1c7c0f11ef6f0aaf95ccc1425d9e5e3a416fefac6800188e6448f89777d6bf14e891be90b0b68ecbc6347be53e1d5f10586888ba7544e34420f994a5666c8c55397022a70c837cebd21d1a0c1d18f925d06238aa38cc46dac23722129984a5b3946cffbcf14711026a378c9869308253fb4f33eb02e91c4ae1d1c409d57caa5e24b2f9c5a9b82720d74e6bc3f961fdcdfec7d46bc2f2330778abfd0a7bbc18c10f89aad995c3db46f86f17ba39835ef81a7dd7c6f197937b30e56587edf4bdfc49fe0d68e7482138a75a373f4a32b2e9cdd6a90e2416f159aacdc2afeff430168562990408164ff6ffdf8431fa061d2f8efc8d13bb669e3e6b8102ce4183d1bba65f81e948a1b7472988c3e2d44242d029a55149f21e503137df2644fb33b06c6d48219fdfb214693a8f5c882869dfcd4f8e797737e972bae40afec95a245beedadb7292e5918969f07f550677645d5ba4c3f5f98541ee1af2da46809cb84f26a954afbf8614505b4640ebeeadfc54e20a1a0466b7f12c1724cf1d000cb6f80218b77d567f3ba31602a43a0fafa46cea5334eb6034d3704776f2d0fb5c5db892b744906f156305f76303fa9ac9273c7057e2ca24f23f5d5d323503e0f615ebd86b3d25c4503363cf2cc9d11c722f5415e63592106f55eb7135abf7e79084da62743b678981400f69dc33448192754e1e2179938586751be791a747cd84325a8aa40c4583c2e0e682be8d35c97e9fc4fa06973a6522061dd6babf1ac9b7894b5869b13a4b811fcbf4d982f271281e55bd675182c89e9c6007e6fed249d07f719febee935e548c7f932d57e29acccdc909267f62f2e2a9e18f52691700b2b6315621cd38bdadd78b432a77cd32f903f274e22c89ffcf9815eb30e24e29e14796661cd5a7fc7e62eb43e1b4b5e1498ffd0f2730bcbb7db3350058b92ca805d0dfed9eb29d633725feb1d60a81adf3c3431a97a5bf6a90b9b0fc17e446d23ce680012a2ee89fe292d3731f4103091bcb87c8ecf243a816cd094f37120c10b28815c35817dd6d0af57d04771192347e42f1ab312c55935276c7b66abfd8241ef4e03da06d04ab3d4ab699cb0ea9252562a940fac586bc816b1488e938082970bd781d07fae8f49afef311bd0d672ee5b4613fbc5ab559bc230ba2b2e895b0ada08b1b7251d98b9e03bad664085af63b49056bdf2d584b6fa3be25ae692d0ee5d9bf8736c3836a0cc58a74950e0baea5b6f2cd5118be603e3b8ff1eed9240b292ef62753f1cdde21f1425d740932d4a9f5088270887d27dba432f2484aed0dccad1fdb4d0998d363a48b89965a2f6bb3c31914b64aabdb87744564993a2d4c52d9d09eabf703be46a4d100d9dff340f5dd4fc046301e77f8ab7e56957394ec263c3fd547614f5d60e8810d163350e9d02bfedc7408f400f9a5ff771283fd5fe31129c79a9c1b4d2070d7f443d09f8afb1146ecce7fce937220768426f405497259f2b977d3819cf08038015fdcbd6fdedbb34b1fd3710237ce1bf2f527c07269fbd34eae7b468351972202afd926115cd6b214be48734ab95636a0f5349f995010bcc1970d1b7489735d7136f00eafd0866c5d4dd01d5a25331ec33b7c952aa39e5738595122489034b6d90210603a3f05fc6cfb0c06e10dde48bf6c40e032e0fd43841b30f82bc542cea7050168be9d6f6e455b8d094bf54fada9ea970825e0dec399480790c91b34fb5b71e34fb51f66221310368f63f933e639012161f72f58b9d8d60cca4627fe55d0be0061a0f2c940cd4812b79d842f81b9dadef341dd5e7ada622b179f9fdd9a242925fdcf9aa3d4e856172e49c3d82997d8291e828df3e1b708835fb0590d25b8e7d36ea080bf614019e22a280ede6bc4059df2e08e33ca8f3835055a6560328f579f7e8ea34453764912467b85b03691ce3469e9058a69f2148c2e914439a584a338cb0781e68a255dd83301e9d5917b4a5f152bf35c851dc391f61e2c91a418b9e6468066398d729c3ba485bdec46d3000ce29702e3b8642d8c37e053741c4f0b147f044bb632c4433b7c013d0100275b328f5c0ab7b135ad5345c437f79a58b817419be0f11e3cdab34765e1d163d6ff16d3e4e3bb61acdbc1f3c744c0a23fa190ee70a6cbd8c44be276ea6f7ff279489a3ce0c2a6e33832b7fa2d977eb70598e00e943c99fd0decda8d96828153ad44879112e45493b93e49f8a3da32ea344218cce771e03359f83b18384d92c7398a80476d60fb2aeb7d8a19195b4f882e22358904bfa15dffd2e44a41537b841bfad2c0b16feb02c443a37cef8bbedb60d2e77058683ee9c35a20ae41b54b4c013bac0cb358a4914ddcfe3773eb05e9e102cce4eb4aa9b60e1bcfb4e7e0b38d1c44f1796abe6713633c0703468789bafc2364dbf651d15833cf70683afe9e1ac15f9531a890c6eecfe961ef8a64e18f48126b985fe8303e8b4186add0ee465c72c1dc805054dde914f37347be0625f598c836eb6695b3e9c415d90e737cab0bd08b308ff3ebca6c255b42116c4d6d27fbb4e985da04efa3f41834fcbe6702bac7e52304bc15266cf208a71146c4c4b5741c1086a6f07b6058b539fab89813de70ef45bc49d9a7ea7c05acef373c34e2e2de040fd62ecf5e94d864f1c7fd1b6602c0df7106ef71fb09a65eb67b5c529555a9ac3b62d15a16b94e5804704460a141b539e96ea984f5846bf16a541a351a271765f17abe7e0bd0faa7d665bd9cb5b4566dfe5c427f8d605abec8c4a66157764903c4e76ab61d037544682c1a393c7831521b96c48ebefa33971bd1177e8b578ca0f788625d70687977e3181157184441f7f90e610d2f057986d7a256677a3b9bc7b49fa3d0b9e95ca79d63834984d6c420733b4e80bff2e51f54c932375b66392524cc83dc81c223738fd40a39057e63dddf4f49e2f91d8d34fbd28fa0c9cedece2ce9d2b53a3ad1c90fef2ff657fbfeb8bd18b72b49aa9180edca29529e82c88ed7f1b548076a337df4fc5267d7186220a2dd09ab408d427c54d90b7458b7e36e6192d6d206dc623c3633f0a5383e619dc1d45b3dcbc327498ef8ad49d924f6ae0dd7378c1a79974da2dcdea3e113e0a5a51c0b4e1bf8641e95302d4e50202a1257302941aa926081a808e719477aef80a7dc945446274761f365434dd202c5546bddc1ef69f09ce5c55b4eed02fc5790670927614555e3a028768a128e3345d837c3d7939ea3b08165f98deb6e94bbe2074322c683aa0e11b4fb930255119dcd197d2c0a862f9faf5b3683ea1414cfdee4a68b99123d9d26b363f60ddc7d29e3043847cf034100b170b97b33bae64f198656618c7f9ab4071701509f3f0c9fd1bae0775299af96db73ae453cdd959162f023cedc5d81925347242b19603f0c56da051a89c52b4a250f46483084b226191ec3df51c853a2f2b8d9d859e8b3fb0a57805d4033db6c18b31a5d82c45abd5c4f8b5adceea5571033dc069c616cc88cc4f392b31fe241cc68c4ef39d4f8f8aa01ed02f781b76fe011fd19c9eae7f448d71a9d907dd5743cc3b80bd12d06803f64d7dff076a6572ad618e8233010bd4c7796a6fdb3e0885ac6652dd01e039656cda462232ae725d4c65c6810b1d8c6ca03ddf490271398b1d7a59ad107697710337e95f907050dcb267105e7908025b756838218d815281608d28aa9200f209a30e9fe8717f6cc3f2ef4051ce5bf31b831e78f1d7fc4c22b341938dd6745132181e8c277a96387ff452b4f302d034a76141b47525a38e27d301b8173b280eb074a067aabb722474a0ef759902852657001d90ed8b0f6727ce414d0352588cbaa7fa57c0ad5f3ed4f8ec71c4f20666c81d8cef173784b6619f689f2bc86357fa264fe9a63e33bc2123705d52ccbe017e20928b69783f7ed8c2f05df56ea4510e44dc41824358f07f7e8b69709ea0b3e3d70018f8a04b2ab6c0ffcc2020e2da40c1d34d530023ad0fd24c042cc77a24f254ef38a72f5df46a29104f248fcd6698460865228c848983ddd055bd667ee92216e52aaea3acaa7e4793f0ec8b08319cfac1a6a50f1e79dac5272826048cc27ec0717ca934295f411ee54fa22483f4109becf8177470f63062c9c5b465a02aad8ec39a70f29b7f29476530b7c2e09315cb38577da7fa1c6d62d5a5e2a0b5d17dce1a0447a7aa5a95a4ba5797a3b3fffe520a36db78788446e3c79d2eaa5b3d9fb932ffff8242fe0da09bd3e7b804d70bdaf4fa572911eb0af15e40bf9ee00e2fd810dc5f37ffa95a5841a46d30eeb3f991cc9b6817f7ca462a8969be2dc65e5bfdb73c05b6a8bfb49673e3c79589c8d2e8c6c2799c988f9840bd02ec4e482096cb812c100d65bd10c0d6667fed17198a2917d003b7881265d3079e133b4f3c840ece6ad666173dfbe9caa4291ea502aca1f8ef27f87dad7c3375cc9384f6877b2282cc341bafc0643be1d1c2849ef4651dc0e6a17ba0d5231b47894bcd14749a40b95a1c18746e5131b589d0832a366a36fe59e5704f244bb111e919d8989170d2c8b366c31a46cd9f4d13fee939330a40b70429f5e72d1893cc5b883452d7a02643344c83a0b0f9678f53aecb3c17648f34b62c3b9dadfb2c2e63c01505b38873caae59f136aaaf4b71e8bf93a82f393ccebf94d54d5b4348877d2256e68f923e02def83a6b2ea17d9974b18ce22b3e93c1511d1e9a70c30d5ca22b8f28cdaa24c56d36271a51bde6c0e879fefeb080205a3e8228dd3c021860f1b8834185a3cd646d7406b7e6306a7d608232587a36992f6948b6238b05acf11840abf44706bbc242ed6b996ce3caf540cfe2894de9bd8556e9ccd06c331795289548308fc121b9e4600e871c8e5850a744bd2d35ab3fc0cb83b800514049becdf126b140832686f7c13f8c0db01e5de598d8b15777274666318e10283ad54317c41616f0010094ce7119928ca5364380affc55438df737f909c88540d001641240d051afb5b4a60b64d18a6e491dd11a45e328c28dba2296cc0b25956c6ce8474be988f3630e3c952b0371721f745491e6239c8c91a42e386b1e4c136427ed869facfeb3e23e357d12877e8e7f074a353f5bd057b4f210c882873fe50d16a69f794c9516f2b1451f912da30d9354716686dfa242cad1aeaf98ec17922c3fb485c8f20e1718109a1c5895d899d2324c4c9672f88bba394b9d5f0741a67778e00b5f2939c2f6406ca45abfbe7e36cafa8951e3a38b4286a4c34ea017816e19d76cd10736989b098ee1a36b24e2acf2353b410a2bc3a0e2692e6068ee8f9436001e2bdcf62e32050c028fba41e75bb802104ba8ed9d8dc442e3503a939aab45e03584966032a2cee7bb787e92a054e3876483f2364962908edc1e8195067911ca1e25217db1e7e967d9b717aa2f01885ce983a930d6737b571461b312803d3ada025144820155387ae803c816de9a9da7ae07fec86963afaa8707a8c4b4413dec0143f0fcc4117fd8d1cf77086f7b8065c538c06906fcfce81f36dcdb10f52d29d25db0f30d5ce9d169cbfcbd04ea47a6ecee1cf79c8db3b2385a542c088656cbbe58874320203b6b4fb8d1172e8f1dc1cfb00d6ca310e185804b07175f3c59245659f4ecae46068299c3d6878896d59f90cbcad528226168b9f2d6c05bcb161ad377b281fd91950159eb70874f317d4ebbc359ef21a5abecbaa43d392ab86830926235c21d21e0d07a6da9aaa8c579ae2e7136ad095f716d8de75cf0012e60749b25bb1e8189607ebca59a1afa6f3c21c1b55fab28270c2c901b7789e2a0864bb035804f3e1d9424c61f63e08bb67ab409b9f25aebc829960f5a85219cacca6d3a8d859005f0f621e6872a8be7f0b988c2423afe2dc88011413accd46f0fd2d6c5df8312072c814a9ceb25442e38c32980475f9a0d28c7c7d31548effc3e9aeb7db97074eda2c5caeaea10e6c58cc02d5931994628c109d6d1d252cbf82f84f53160fa89c302d7b9341ddc47bc197904b547337f706f32db249424e697c8b7467fab8ba082e142448a856959576d792d6411c19024c5b83fa740dc4a648cb880adb4c25ff68f30f5c9ffc6b83ee945cc1143c06a92f25fa703d8bf3c4cc6f2b9dc9823c90e697bbcc4b903a44f5adcc607b81174e9edaa3485db3464aad1c6dd9787d81ef582f45ed3b6b2fcda689622d8de6888cd17e6d93687cf88de1c88ca1e13e4823f7e956d609a5e727bb02013718ab73bac4bc1df6b7205d2e1a7bc5ca9f3284be01b7521a78e210e65f9acd03bb84f0134373a1fe2185852d4d49748526f42085d17f78864536b257597c5eebbacf9000e466f62588776611203b1accd34f59cddc37dac9413fec962a241c8c6567523cf688a272f943612c789ed8ac3ae9f4514ec2c4ead5623e401252b8cb9e405d248025f05bead46e2eeee12a662ed69e23bf2b4344e50d8715119f1d0f1efb07a0697555c98ddc55d48b7c0b6a258f5d34718ece08d1fb7bb52508fe74632774ff0ed899017f878372ca7abcc30fe5271f942d7380669cafee45b2d80ddbba40febf65e2c6a44ed048071af10ec24d384d269fc267aa81cc9e16d51fd4024c550601a91af9203529a37990b4aa634a1d60ce577405e540c78878d66d08c503cde17a6e17982bbba5e08e90aed08a0380fb9681320e291818a4a97e3c4135a3e147aab7db9129e7c71bce4b6010d171cfc6bd23f22eefaf1fd3b801173edbc0911673f73cd59d254ec0fd874bc3433976c5ea5482baa3027bae5107c6e6693c004210c0704596eb15058d37292b3d9b885420ae91401085124c028e231ce5327bb5012087ebab84adf11158f7e8e1a1b6430815f56ef5456aed8d0e217ac6feb7909d1fb746939382836285ce89186484b268e2e5b01da5bdbf67f07cef7cce7a97ea98093f7f53b3018eb1579fed213ebc03a3eb92a5ee9000fce136e0fa93b0c15be2254a0e33054767287127a24d03fb5d032b4496fc9534d0b2825ab847da516663b9a13a52bb29c01b5ea612e2424c21be8ff60955b421fdc0973368459a85b92f38fabe84f8bb2ec7a47000b36051307f5c17c892c3e482f06650a3b89cd72b345aa77d280fbe2797d6fc07343b79e97b98ae34b656a6317e1a1d1934438f679013913e009fe5eec7a8f15b575ba495ecf58a31e769737f17e2a4d88e71cb0421e3fbe61fb4ecacbdf76c342e8c7aa5e3339222a173a1fd5604b2202cfd070604680f21bbee7392b4cad3816c7aac61d037db4b8190ad1053c12a7339fa72dccab5c0df3936091713b9ad9362732117bb9de977b621fdd9835ec661edabab8f1f3fd985b48b09ab1ed4d42645b1fd0c8714b2950abc8587656d8b8cd7482e7e5f6aef59fbe01e8cae62e1b92fdeeb43e5c32ed112546e8563491bc328b624391948e799d65a6fe8fb225dc2e7e2a7ef45ea4dafe55f08e3d09f535149a1a0f9559f83a0eae96420dc1203343b1ab7c9d9bf8aeeeda22f8a224971a5b6fa171779d36b809ea42fce1d691758e4348113034c7f1deb9929de3ac869713c6109aec82c0ffd271441d06c7ad0f004f8bde96d52f22dc6051f6f7ca4d28d586db4e81b2932bd295a21cb4b717bcde59d4a44153fef6e7b5e26560990f3323d55969ba88c58b704edaa52016981bec304494ed7763b8fddab23643195e3665fd601469595b280e96ac9eb68e381c7ae52b836d2462a2d4f6997c126fb4030a9261c0bf6af6cd16b8f9ff2f21e50d365be14ab948580dea07e3470dd7e015e2f9ebcdec7334da3faac1a5a8cd085d0559b25ce76f52c31473ab2151571b621cac42c441e07473204f3050e6e857ee95441958638321e61ae0aa468b13fcaa33b2f0ee33bf05d90f3306e018335690e32a8799d5b4c854017d86cbadbfbe41a3ea69bc1fb05fd4806b19697532d9d68da0e63172b7ce95c4bf5fed865a5c0ea17c064c61c1a12a830d1ec2e75286872ba2628bbb8d16b57f85185c1a307cad2e20a73b4466724ee757b01f665e538b9ec788b7b40fec9316f6397bf29cadf88b079b80b8949fe59033538e4ff64d2c963467b07ef304522f43f0bd13584ae68afba23557725256731afc64b27631ac460396786b2a105b47e6e05ce0c64d86f0e84aa96751441f18b0a6d2c904c6c88782d758eb9406d53556c92ef42e2ec953a059ec3102755d80d428d9dce6f203cafcde921f01cfdd152682c3f9b3b1424c6ade902f3db57eed96955b7a395059a40410e3a32396a8c11d8ca22aa9cda24d0a1e3b35fd2cf0b8f3a8e5ff23e80dd60df39015b77a20e6ce0b9ad881d3f6cf975c3d05dbf029506636e924a8677e82f06cab34a96741cc587b440515a68a01d0df0557167737224fb5df9208cbd3e770985cb6fedfafe77607cc2a6886c9af6b783ad59ca20ede9bb8c6186113c3e771b1064e51a6e4ac68516fc216d57803be9080735a2238b89edd5427e134da0e461fbd0591320c3e364fa3690db7add5cbd8d8af5c3c309771fde56984913d17e8bc381745922c736fc15c4d29ac1cb8d39b6b62f95855db756bc6a9458d5180a12a151c6b61358da12aa3ea0add176b988860423bf30696bbf7e699640f0849ca164aa6a6878d10bebc0c90dd907e9fbf880d1f6fe6fa8291f708641e131fb488290ffb21580ef428e540a288ff18985144ccc27c5ce9c5b6b09d8b4a4c826ccde64e267efd792bda9a974d7f214c8d46e1f020096e08f8d72cb73dc0c87673e73d3ba1c935040bad751cfc4067099ea7ae75dac229eac93eb8a60ff7f21a1091aa78d5b263a0b9d42db9d4dea0d46469df0e2658f077a79e9d725f1cd783900162c60dc3b4f571747404eba101ade32957cd3a384406d030cd72dcad3fa6864891e03a60a44051ba44df9cb34a54611363cbc29fec42b283b511dbfd36da49bad11f8667de91fff9649cf88960dae8fc38f56d8670c36569cab1d936ff8aecbbe74e956d749d9932b6627a59cd25c8ddae170ed27c5393e4959f23dd8762070e9b75863537767dfbdc8dee6574824f097d6afd79a2495ee1cd4c64ba0003c72bb636b585510bbea85dccbb88206ad5434d10bc6dd7329bbed0a1a7d0203d1fced722b356859507aa42e3533b74d8153cd4d6f3cc388c58e7a832d9735823a526b04e614be919653ca3b735f821c99a9748afd0a658b559d16c418cf11ffbbbda5f918a6e29181d7b66cb940ce22e1c25caf7f5d9c4f92303d0c0cfc8bc8772e1ac402ecbf896d78e795a9056e64e93a985f83e6c7a6634f46e38a777a6e3451531246114f31bea107d9a0681cb7c9b579df2c959e4395ef82f1e696b8813345b795a535b1905050f7b7570788165f51aea0e9e4d2cd99769646f46236b02e28de378e96c9e93306c2e6ca9b60c2d7332cf62f1897b8cf116790b2bd6632f7fda3e763869c0f4100c9d7f94599c6d443f7508484ab5b5a21ce81638361605cd2db62e1734522471f6cf7d4eb9f9c453704518e6b76b568f2607a54b6d46932af7f516beb03486f4ecb199781180e7dfc86b9edeeaca039ab354dd50f32f782a3ec2958d3d768eb4cf490b35abec0bbfb394672deec9a255bc12ba5c8ca113dbbc9b80bd9f633f90412fb8b4affe315f0f0a67b28ad23d563897f7bc2adff692e1b9414698b477d7aa80d3e8a4fe820ac9454d48d7db55924a1d87ab0a26d7ee17c7f1e36993b2ac5ae6c97f6b34442275f7178e9ad4ec7d82aef6505031af943725d05807d69c52cf3f9916a8cfdb22ac82483d58a9786277abf15093c1e95edce3a85e6ad0de89e471ecb9ae02b362007246562e49744914dbb580d3adf7ff7447e04437929fe81e84a2db479338d0df3bf31531138542d687769a53f7ee4f2faa09f0b80103edd03f79b5e0413c08a5ccd07536a7becf123f0dd653ce19bf0d8e42c7ebed0152079b6691a267454e6abdec6a3f2ba5460ae3b466ccbf55d4b15bef4715bc06b760635123b8ab3f005ff8a715062ec03921f58a3b3e45747d3b24a7e0c4adef73380c547077b2bb0caacde49669a8dd980372aed328f75cc67e7053dc9a606097f9df72d4a604bbd08a76ae0e22461cae7b348804ec6507d217f2a261d5608849ea421f613b75eb804b318c8699c6601bd4c756b02aaa7c4f12d04ec85f3da17200b395f576c2910b14597cac50bb975e9f102c3cebc72dc2555c9aa0260426c3d5111eec55c3adb26d88728a5f5029af3569f8fccce56ad4299e06c8266043c5f35d0505ae7cb841e4f233d4a5839ef701400fb84c2e11e6e77e46ac555c89dfec3e98430cea64b0f7bf5d012a53f9956db252f59f4df854c8cb4c526f41cbf1717c540a4101c969dfce1fe43660a95fc1674a243a41e92e8523f49f69a169c242d689049e7ced5e2c355a3f8ce3931981f5787f9f4337fdf41a65fa18d7439a26bfff5a012915794af02cf5b1bd72bd2da94d4a57e25e0ad744f3809b658c666e65995e476b78aece5b8c924819c1ef4737cfe0a395768203c69d789d2479e4b05f8a06eecfc979a72d8bf6dd66b83f88ab5a20dffeaa292030152327e7f79020c57303903b857be8ad0c842b822a962a2d6426484573cdb804d2781cd37b71cbb838c261488ecd0bd44e7907b6f41e5d7be1105e46b6944189654fb4285a933f9e7ceb79308216ab9ab7dbdf445e095ed0a727a03e7270c974cc91a710e8465eec0e4fd48ed14879241ed7e0da219c1ba8691cbf6664d6a29487582bd79c2278d2007331ada38e6ce5349fe9116386608732a62c665e96ab6244c0afafb30a522d12aa5723945eace3bfdee88bad18901c2f6d66b889bc1cb61202644db54cce408eda155c92c2e5a7332973c0b398ced405e405cbecd1fea6a04def31bc1b8088a5be331d5cf0d837db4047409dcabc1cbcba61189d719f806148f31e3da11f3a7954baf5d68cc4a0fdb511f2293356980bad37559ff070404e2724dc01c1771bffaf05ea0dd929f32c9555b927aa66271bce2430dfe754091dbb7af8c679259b07dbbeb1b69f98f6574fbcfa1fb65242d65df7ad3657dcfc6451f7446f873dabad3fe018902e62eee4d88fd3af484c41fd43f2b26429ba53be23239f0722345d2defc2dffee416e2dc968c2b1feff61fece4ca95b6abf732b7060461919f28b187dd4753212ea43fd8d4c074affa76d49d1c183a54d2182710d80411a201096f73a0928aa7ed8c94409016a4deec41413a1e764315c668be3f9a99cb2f94f00e2ebfe6eb2fce5866dfcebc97d9cefee5bb1412cb3fa9ac8bd159ac8e136c05c196cec863432fc44620d256ccf2d3ff2a109469c829e8750e05aa38ad15da475459ec13d550d886858632a124eb060a709118984b18b65de7ef997a203de7cff4ffcbc9c0d8bd8a5f9bbaaaee368bdd0cfd6068f390d21d43403869af13f4c2dbefeb8353554ce175499303b93280cb681c5d283bf0f49541b43fdf8d98524c9eadc6fdb469daafca1199c5accca4002a40daf2abd5d427ff0f9010a05c4058b302a8e6290a209abeb7d27009506c76dfc882042be7c30185c2e650179335480295cce6f6c25b31b5e7d47beec69f2da0393b709edd1b84e4916e1e09d8f9541317d7d2e3781f84aec8396abd9f214f9806641965ba8e96f1cdc61bab02e10d59c14707d1ebfa05791d9a6e9fd2b7b658597baaa06268cc96e326a7eb0f678d0e4448dc3c572fa387232d39dde07b35bd08bfc383d8d5a4889744c6a1f2dfe80e39ac5e4edc318fa19cd3aa48b25177003e8b112dee2fe0d8ce418ed5f7c9f76b036a85af9745e6ec2fb6a4591fcc77190f60c378b4351ae9d76a9fba0075f253e2c026ac79fe0465e0d172ee639dd1000540bf0bf4910498b31b7ca5bda387380f2f56ce3ab67cb4cfe2d55a4116826b4b5b234b3f4c5d955137e592214fd24d89b264769154458c3d92d2b9e4f07ec9183dfbc7dec626ed5909200670076aed0e485bd1cc0e12d64c9e18665bcb3a0c59e411e334b145e9ed85c41b549326fd308f65481fadc6973d60440bb4b5d0d1515c873eda4ccc5a411d95781dd20ff1fc3cbb2404323eda44ea44e19dad904818d4f8a3a024f788fe8f038970c5935a8be02a4687a33e969240fdb127e57dd820c93ee7d805df7ada475ca13944878eb22796924087ac33342a12e197f993614a7d901ed10ed104b94650968513037c06807018176fb84b850fd5df6f745bbbca1f4b2aa37c1c2463610b35b167c3b42ade6407b9bb7df3c6c8bdeb79158b2ca2cca54f7ecfc7d926f2b1887f92b3c3d180842515e8bb7aff85f57395adefa86c169d8e8667d390b0513d5c6f2ad9981315f193036e82397b8bbcdfad234c9d75bec21c477f4f76b0eaad8e3c38de0e6719e18c4212bf0051522b16f6b3175abc21474332c2bf210cb02c3eb2fe21f39d569f5ef39bdce89d2bcd4182dd36166c6dca0687a08472c4404bc5f36e0b9ed0120287ac2f84a34cdabfc4e6f5afb88eb757963f2d546cee91dc4b4008bdcd9333cccf9566768f36caab089153e99adf2cfe2f4902fbb946c8f7c7f3bbe91681f1eb3baf3f6e9100474a5009b7f4add909b0d074c0b8e69c2a62712d86d2eba8110c028f57601fb6695ebe082436b817b7c5c60e8147d1999865d76d93a6d1334ccf0ba27c8f266924c291d87f9915db4310b5aa6b4c903ecfe9fa1dad2f0e06d08f8b993f53860935d878f5fd0a521e9b0fd30242cf63359a7ce3e76efd61bd2d2fc7b8416ec8d592b20589cd9ca7a85b059438bbd9283de53f1a5e5b2048dbd00bb2a057bb58d9510095cfd7f3e5e7a7870defce5a86acb6a8476889ca643642c4c3dc8bdc7cee6a1805dba400783ecd93cbd1b1cba7a51a576399f20af7edcd7cd0c3217ddfecbe555964001df9dc0dec26b24c7fe6c3f81c8d135415aa7dfae00ad21a5b1a09fa25df204c96bf01a6db2c9f6feaef78e9a5c938667090b2e47e66df704062412d272df4495c29a9bc098313dae2fbfd6160c44349d31f4922f6c42ae072f703c52d25f0158329c9734fbfaa28dface0be232d909d94f391866ff136f76cd3948852e807ea7e054e7c8d0f2e216cd23ecfd077f874ea174a6f5fead5adf161bc5eec43904693026c29f34dd5d2117d1f188204e753f635b6d0df1a39c4a88fc4b000f98c7305b616d200c49c37a3818674c03d6fa6202bb58ede6ed4a279e6a67fa44f142842b4c3b00a26b4d4a1012e90c0e9442b60200cf6b14c14c1a063030efd92bcc215369bbd6c03651799b608aaa6491a6ca2d11c802df2e762d323a16b8a174f46832cca3ebf576a56874aabbf08a0e8022397c6f4dec636053cd06cdcfbcee0d5c50a67acfe5ff67867709f411853ab059d05f2d2487726846d708251e6b37d26865bec23271e20b0d5614ed2bf2c158c36dd034f185eadddb1d7ac379209199a28738ecae6d9cabc6d5dc3f0a49463f89720cff043f376c36d427dd5b0d9f9f372790d4182361c9ed304b6a9dbe73b1c729ac2dd38e40f0c9d0eda7a5a8659c8f9e4e0767a03c6df42b5d431b154a1729f2b421a46dd9dff89fb237b8ad9d530ac1004c83041e6ee7431c5a5ace665142d1a5ffa4abcad8be4e4cc5e2bf6946e256757aa1dd4243b3ea169c51de54844f87d3e5edb8ad542d98267162190fdbe596a728e16a9345d853e68bd505786640df76c835383fe0cc5c2da3392d0a8c8a183c6725d6ee5169eb3dddc21ef6b065a37e5ccefb3c58ea57ef67e2fa69fc5f4921286c179414a27e55b238571a107fe1aaff1f230a09ea2a49746d3d3c04a1f9413c99ec9b99b11de8ac18f791e9b0a8f8ca4dc24061770d6553b1ac1c0d0715fb1c162c9fe697e9a89f13510b1e3199b77dd197030bc2308d0cf06196e8b10dbd0421c22c2408a2f6f287ca4a1785eb30ebc03e7d9dfc8064f0c284f5b9825ee9498d146a7e9bbb418934088855a41a2bd2b8d8b858277561add8e600dbafd38a5dcdc12cc0e68f242c399decd0252f825c505e478e33229dc6470332a74e7049e9b8ec0f1ed5dfaaafb0ab00655c769209fed3e4254163348be081359a05d4ae73e9083bddb726b6640f8a8053da3d9ce70cb984d17261ab5e3d2f821b2055e6dfdc7352fd6858b6783d68562ad59884770dd6395faf056a392e726b04be8cc3c60fc56e0e128d4152d50c0ab56ae7ac3d83207e758a4835d7a52a52868a0868bb3e80aeb705cc7583a1cc1e6469118630abe6aa548191cdc4af96d6ed31173eebfacf4b50726ba465241989a6da99d7aacde5d4c94ea5de86d22ace714d6e64685ce07b3cd52b5a08734ec535dbc2834659f61f7b287a7ba30347879cfc8abae34ffe047fe35a47912b131f4985c907a09bc3898800f79469b8de3fc149eb1a0b9f82d77e7b0b20100715828e6006507f6e2db51552b5925a55c275556ef3d5d254167f729fc37448feaa000a395bdec2bbee79b2985fb3b046d9d6d9d43f8eeddd04b42e8839a7369769eca46f6eeafc319261ae73c758bf67591fc86a801a2ac5f381e0e45df56ddea27469b26e41ca729977ad7b7bda7b326856863d02e5466c949803006a7bba3ea398eb5d0b4193d16e14219193e012a140c4c4726719f2ab0640f01a3f54335b179bfa4c5bc144e5d74f9ecefddfdee2c57a20b824c756613fbe0cdcb8a86d6dd5b4b5c6264504cbd9e1b5573d98017a834149fee719447c8b8c4eadd79dadf056166d1b1232dba06de3301771aae55c0afd599869beaadae3218e38a33c402836e1463b3a3a5aab74c51ce75a025108a260956e76330348140fb54178c1b22b116c86aafcc527306746fdcfbacc81be9bbcee96d2e25e39595b7a5952bf9161d98d4b2da35d814b8280ab15007e10638c0d1ccb507dd353ea14f602a37522043266ff2be64669c8550595a56ea355b1da67621cdc0206bb62f1aeebdd3031e4c9a41c4ef40390437007ff314f3cda5445a405c71f36c114374412e22ace41f361e99217f3dc685acd07b026adda3149f447e0521cb73978a3d73c4aba2d54ec20f32892d98a12536eac7e7784ce4e310a9bb19ba2ceb4db7e0db5141b60eae3a51a0fbf07631f863e635ef76c2e15a9d0c3533ca531821a3b94b21a197b729a669ddb7603524fe64bd27a5caade65920cd019fd95d5e74d377df15f9558f850ce7ae9a0fdc82f2743e90303da21852132710b7312f430068d76c3c0dc5aaa8fe42a2b93af50c699e536ac56eb7bc305d32b9080782324d03c0c964b90456e137a02db84c74f0cf76b5ae44f2737efe3cc64bd267be7c4d72ce673e0d221256e7682d7cf26b3b4282965200362570d1517817d0354115e5893a2f024dc380b1d76bc010ca137349e027c1ae99f1233e11116af88da39a6b5c762d4bbd710c04d5af18f5152890e63d18a24f319f65895d2292a48a595eea096e2831b29d39008e04c75b7079609f7303bfa524d846af9ea82ed7319767db73a1b5e0b4e50cea95ce815086a2854716a9d424b2de6466c4e688bae5c13fb3a9956444ead0cad28d5ab226cca7149ee50d398cc1cc36e6e1ace2a09fca06376636123a832973b4cbff9d702665e704f6040ad75dee347a36d7dfb7fa44d9325d440403ea433fc33af482c5c3a756d0a5a00b57eb2a9326bd6ea99e15aac7db40e0e749d684fee43bee9b3a6fedec455f5ddaa67cba306bfb1799e4d39d60e6b7731695e0e777b93b817b657ab10044cd89818026b9e5b47ac7ac1728f4eff30b4609df84db2d521671a5e99cad48da6313b7239ec3ea8458bf02aefb8e4909ec47e701bc5e685555e5282b86eaad32c5c503de97842342cfdd5de295d1ff6d3304119ee95212271f63cbc91136f09752d29134ecac9915d4d69b89e1991650a594b188666e3ead27b990d4326704f26ff8841025fac615dbb8172d366440ab5754666271470844dd7283e6750a4de43dc634fd050709b1a1840d58b94462e64558333845cdc4ca201811175e0dacd7c838dc15b8844e3e779fdbc21e2184480f7c4f2665a5a4b449b3e6f5939848ced3bac6fc3a766b10463ef39e9b42f939230445c0230fb09d396dd0580925ad17c0f1c614ab1acbe3864a8855aa703d22447154eaa42da21094c9a4653e3fb7f7ccad57356ff1f33a42bdf148a0fcafc8c2ae80aa33bb668a616bf4c13e2e2aa0b153a0c0a56a8c1204150bf2f73c25be484c5ff5aa78917dd70b4a4832bd8f130078aa355aa0127becf8072a232ed199faab11d0b85ecf802c1d2d59aeb652b9dd9c25c19d2b3241872f40087c2b58602bc56684d27a9a49013905f2fa64e8e64e0718c7afb9b85375cd7fac5f1c5004c880dd66a129dbbf512cca34211d080c21938240323197dd743c611d0b412c10740ab642a2499451d7955f1b66598094de941450bb7c49a09807518b7fd883e94f7e65e70e8d03bb6ff3b17fb18323802d037f0cac0ebac0d2cf80bb77aace764aab56174abfba603141ddb7da2b21f5fb9169acbe1b3c0433a59f76e7bf466857ca9848aa4a9d9fb26ed2dcabd826042ec62c2265c31aa7b1f2ada7051d5acbdac7c72177298fc3edd8034bba491673947b257d72034f38d1e37f2104ce27e0ce45368da049b39888e0c9e8f59b7164151f9f302c1ca02506f81f62416721fe0a3ba40f44334f7c910d6739d0b51cd1c160ddc8f30e53a3308dd98939ce392f9ddb8df02206fcc61295435e2611712850a5e5ad66280c67714faad0039e51870b63d43411467acc46572ac25108bf6c723d2115c2241dbae818888f9963c5cec7387c0b832bebe158fed933c744d8fc02618cc5a7ca8c19023a4cdfdf73076c51083bf83a4f3e978d47c853fefc51f280375fd37670fed8bc82266e1af8ffe4d1ca2a2c8a19f4dad717c9917d544e16d26f448a04842ac153a901cbcf7704f55d212af35a905044240824b85d80342e9b6d99bc40fd76578759258b384481ad841e8a165da19bae4e9988a9b13c977b6a309412d05f2fde5631909e315fbbd8e9b9f1aa6772f4f466274b981da0daf1845aaee11d82d755a4214334cdacbfa59d4592b72afe09d9d70acd83f4bb72e934b2fad225e0686c702662348597e2d4049577fccf39ec68c0780f96989f36f2890f3dfab988d04ac2ae3e94b9dc5435a53027a2464f54b3f0931150c93c1eccefd22bfa108c0912e864d296ea7e20ef687a5a0b6beda81537a725e1d00f06ae7e9665193a0301bfcb6ea86fcdcc00ccf630a0a340196d5c774fc7b8e9af2b56e166d3d25de61cd5d4b1e61f216bcdbfbafa920ac216a34af4c1bdf4916c3afc60432ec83b059d80b3e761c8fb8f290aa4ea1fb56907244ae66ec24b692ac309b432d20f679789732ac09cc8dc67384c4826f6840cb778c927896797c37883766f6c81908db4c624750098a17f041e80ec8440115bada1c4c3a1538955d8c8cb0b2ab0dad60cdbfc032de4705260fc628bd03a4a48523a2f9922ebc6b0d3cf782b941240d482b6383aa04c722d702e0515024affe9aeefe5d45454cfb181e0d8d31c7ad0825a32418fcb8c72618ffec8b3f3d91d828b4f5694c4482bcabfe35ca2ff783664ddee5de503de698650afa576e494f76e988e91f1a06b094137dd48bc294a454f7461db799384fa0a6bd410526f33d0d45a22ab47bcc0b8add1226ca5bc0af6799529323826db0fbc525456407a374f8ba068810eae8fbb59567a570370bb2b2c47d134ea71e368c23b46e8f0272c727851eeffe18945b965baf85c47aa467440c5ef002bead627ffc431b70fe1e7ff22fe44b787afb03fbdc5da6627fb727b6f2157b5aa8f1d9808e8b3d459ed4238e9a80dc35ddc6a1494854cefc459281688dcf51c2ab33957c4ed03a08d836ef157acaa3cf3088c04927240cfb9d617de4bc2a62cb86e02d5daadd96e078230e25fbe9b37727727c54cba63187127f590f903c55fc31f7a1e099eba5f3f0844d693b8e7bae64c854c9ef260060c954ec345850156a17424011aa5d4ed281cbbca439fa583d9881c9be6b768aca362136f2827573e10ea4821e885a36ba0f3437f40b8e3f6011081215c4d5df44ba2a652d0dfb91db2eb22355ffd611aa613e368262fd80b6e195ad6ad979ba7597360a6e028084e8fea6991cc4996b1d9298c000b180d230c6428cedef96cd9a8687218e4b5f3b5fb9cbb1fbe8668f454c3916c02b48cf96fa9138c7c2801d1b64477f94898e5607685e197e959caed8c7c4457a9ce9108caaa21258291d53a51a3b3d3142e16ad52e3cfdde88adc2fe878bd15be5c41f8abebc2d513760d189f461ac523fd655270001a21bffb13d291c72d373164fc5b1612450dca3bc4d5ea88b963b867c5b3e6439a50d40478212a16bc6162afcf7d30e467576b9ed8e879eee4ba92547c96b167e5aecf46c44b102bfe74cc1c9e5984069225eefd53849100ff0eed01ff56620d7473d3e193429d605d862722d7680c42f94411a7bb2bd6955f215ea051f49b3aa71ce20f631fb315eafafcf7fde677d837a9b0a1ce2a10a79fffece62bd3761f3a36924a38686b4670aa02d66478c6c3bfa500d82fbb991143fb253a529e2716554db09c259a2c89904aa2cb7b9fa47d58f5e9ce4386da6afeb738aff28e5e94d04b82164cce2cb2209ad0f2145488a2927cf45069bb2f3591d10d50f1a7abfb7e19216685e7d8b9c4465c000f156690ecd8402dc3f04a446f28680d01a1461628fb21fb5e4e9178c8b1e5565a141c91e37a9d92129c231cd4ea0f48f64b638887105c02db7f3b152e831ab75752554b2f14ea0ec2c3dbf028047fb8209eff18b85cc4beeb040e214272a79d06abe2545002132d515f339966aae4a6efd87753476730d1aeaeca85d35cbc4ac4d41906430c837e009e2a0874d1eb7828bc5493b5921b82b7b3ef0d527a52899c1f80c8b58043d0487c9e0ebe20dd061c055aed59337027d36dfa97fd3cc244b4205289e91f03cdf53704f20c50aba4bc3a95f45a72217266791eba5e5e193f7da4c244303ba39e2a37f692edaf3041952a8539c2f89a08e2d70084c0489cd741ca066905956925e19d4b58e6186bec93ef5324218d29c83703c0034e6460d9259e2d6122fd1c8cc60863c02c9d523db308dfa5bd4f78236b9f23f9411604412490dd797c32ca0fa9b9f9f9f140e0946a3c8fcf44276266a745c2bf3133f15f7f49e16dedf497f3ae714233dccb529484ff9d1ec4f44aeb9e7636760c4923f2ba8782194799deeb12ab513043c35f872a496dbdb72233eedc02e718f30e0601861c089c2f10f495129e44705f75207efb6c608e602369856c9f0360d00a662e1792e1919845308c3e635cf1bdc3bede1e31feef5ed3a06c7f21984de76636610bb5154cb24a0dba6359785b13bce7e980f250f02c740025a1a1d05ec2bfff6998cd495e35978330430940ba610b0f0ce1db555691b1979878435374f681921e83d91be2c1ec98d2a930a2914887d1c33032f1863e87e310ca9458b4dcbf4f6b181adcc21fdc43fd6c010bb3c5d39a0775aacd8e6bb6a5d8c723725ac92b3005bace5b6183f3967db4b6d150af76d4886fb1b6dc5d5e7c83c99be81cbf848c0a2ce2987c00d082bfadec8edf3c342dbe1216d092a0343f23c63e0823246447cd3693f53a2aea0f6efbd01271aa2013178591dda14804eaa84c44a32576ee6cb5d0017406ea8181918b551d130de587a82f944e35b309d7d119472a7571b347ebc673e2165d50b792bf6894cd7b21a95522dc9db404ed1b151926764d40624e9593ff513ab8298074e697ec00b72ee8b1036e90afc18e2913171555223960c6f253b1b587414773e596350aba3b7bf0f49d432ebe56471a6ac2b72320052318e4ff43a88f052c1dafadb49941cfa736a0813b8b88ead84d05d620376399dba728fb2381103d96394d42a7757cc63d48645067983c8cd8bfaeb92e1aa13fce8d3e24e95e41a38c5bc80158b8be53ad5dfe81505d13b046406eba817212dd76bc855e908f17d5c10c6afa7835869ceef96ef2bf15a495fd91013ec1f8b8a4a1ac80b4a06da60d2df882c9081081d966ac85e5a8089f8d5f4d034e01d4860a7c9052a94e1d3255ba4696cde346f32b642323136e30f77381234a3f953b4279c52997cb6eb4e06029f7c9e707178ddf3db0fbb1a6345a96275dd1b5583554fec14ebc08549008d9e32153f4bf77da371135748a06da8898d95d00fa8489ec34740dd2e40e1daa235a3555a85941798203162bacbd836d57a951addc89629e00d9cc39073116aae33c3ad9e4ba361caa8b536fd33227166e558d35883612efba88820706bda88bdcadb96d0886786eb4ba86384dce17abd6ed3f0feedf7d235257cdb7a494cc1c021a42fdaad2c04166611474fdf14add9580223f6a62c91259f654bf1fed63c75cca284cf8531bafa5ce4d4bf093fb4db56e33c414653e587cdacd145caf07504fc32931610a092635cbe013cf3536f2168fd088156d7410a7fc6f7d608b434f7c0486019ed48d96b9a6d48eae20d0fce6e8c4d2b6b04d83ae8a710e285260f0d7e08d6f21f07450b490e923ce98fa2b43d2e844002d14c4f907ff50747c21a3cd99f946f8c53f3e5fc84914a1cea259560c745d768a8dcbcc600bfccb9416f8109339cd92ea9ce60d8452666f279c5116722d295b026e3a4a390697791b708bc4a95f53863e8474fa5d5a4e0347eac4c9a78f227b8a1157b3f4c18a664ada8df8268c0920d7d5695d76c2506957d6d2ae32e2282abb8dd19d08092cff3f4e1a44f78d9bddbefa165444c5c485f2bda5eb3c66f7bd71997851e312c0c57ea7e6e525f8b88b36e42379bc53c03e82523f617495e44717c8067c5b4f5e2166026c64dc4039267efa5ec0058c6ec155889802e4821095542a3fcf1e61c2f39a8e15c6c7e7c2ad25346d72592c4a305316a50d4740f73ee802e240f02f73b3373f2c91d19f8b5f178c3f556b9d9f30a18506844e10c2a19632d1839f1b39a112ecefc479048032e1722720243915de62f7cbdc573d8064465f34b6c920bf5f5c255069a1f85a59303f791871f763df038b02a11b205a4ed0224330443f5ad9558460c817467906ca287798c2490823dc61ea33a10cb884aace8133e20ea93c07c390238cf20492417e2885d3851bfc82085c1afb8def8951af9612463da228944d87ecc85cf8f7516b766f4c8c3c92c6143950cc6e1fd4c0ea91789eb635806c78970d2e03447730b0c4004e570259d1a8012d287ed1d2eec1a10bbc740e6859d77071512a8b80b63ae4330dd0ae0e29f62bb730cd04fa8fbdc4b6d62007ef0ff2bfef22112ed17ea4909f9a1d2e7494bba800e97b113910067339b15c28d731361e02dba0853d132fceaa547b6939e387988790d628b4624dd366f9720996372b13cf050c5dcd020799ab543bb2638c59998a3e979f2623600528178e66a5d13fc712942fa0301edac7d0f375e1790a8c7cb43734cd742972862ccd44167fe5714a5baf8d5d5e6f8a8f8561032c0d047acfce8e5b30161aa703d5fa793ba811768a60977e2f327768e651a82eda723f2d5e825c80d12811a7398b270484bb901f74a73f1cdd12f39d5f9d09c2d1ee2140077097f26f1d07315f8cd29ebdc4d04031c67ed242dade7b6fb9b79429c9141009f508b408628aeec8de35aa65a0537ea1457e5a6d2ed3b428adafecfc0177378dd6597bcddab3a73273cef973ce6e98ec474abb524ab3a735d09f7649cdb2945d626b0c13db62c62a194619778641bceba136845aad0ced9f5aeede4dbfd2acbb555575e91d6728ada0531a49ab02fe72a048a5aa6739a0db3e846da3bf7d4741ff8c9d0beb16f25fbefe73ce9fef383ebd8f8a9c5a447c12a18b502d495cceb62da6262a4c657394737a388bd8e9a7dd1add7826a34e4b72ec2ebddca277f9933e7fd277d0bb9d7bee4d2567d9f7cdc23102a11da20a0d0a4ee974a2ac6ad2270ab4ac67e61d86500d22cb7490696034f148bae9eec4ddb5253891a2a37432414fc08453ca3ba0bc849d590b77769967fa8cba74accefccddfcc728b6e3ebf672faded6cb3f3958e99999999999959c8a96bfbe11f698b79d250137266a6469dff8fbf743158f4771c98258f7c5ede3dde3cdeae6e09358bdffbc705d5d56250139262549ddbdf1507f6b9d2e32a4226636eb7bc46abe2b2d23cda95eed1b05c2d4bfbd056ff70a8b5186119ec23ccea9fd425c30af596242ebb6558e1e7392790ccbe724d7f16faca57dd846e526f779792f60cb9bbbb89690beb355ccd922f3aeda747bc076669aeee2a5da3f1b88b750cfdc0a206fbc9e283a5b51e0da8b3013d729be8e321fae143725dcdfa8e302c8b4f0f49f60864ccd578bcc6b3e25dd17a3c2cdacbcba2f97c3adacfb7a3c1b4197a441c4fbb74a63c590f1c882520832f74c0831f04210539e08ca93b5de07241122cb828820267a4e1319d90c1ce0e86f839f204132845c07c80438023472a78051185161138dc4f8bcd3db93d52701bdac28ecf3c5363c78e768875d4706192e0faf7bd699781090adc775decaeae11077487ecf6ec0d8ef54a2f943ea15ce221a122245d48e44b36914fe40e37398e0d60a7275b99b7a9b6cdfbea356c050ff716665eee73c0c6172ed74251ee88d22101fd70c50f4077bc71391a6ef3bac5cd185cae23dd70bb7b9981b7135656da545ae4845ea2583ab690275bd563defe1df3f6e7bd090ab6ebbaaeeb703462c30ebe320c9ee13e067a3be45e7bf9edd02ac8571eb10e0985b99fb4c8fdb458fa9be636af96be7e39b49b5e053b0ad814eca6d7dc774ce872dbd31d5be97b4a8b5c4769b1815ae45ec60eecc73d4db32ceae8729ffa179ba5ead7e59efb1b1ce48395cb7dd781aaeef98efdb3836ff7fdd3ac2e1c2bec49b33a2813757f6abf9ad5df4b9ac5413e085d6e7b4ec25ae47e933f2d72dff25abb258f78867be9b3848272498bdc43b9dccb3058c7f6dccb274817ea73ef8143664b30240eaee5173cc3bd0b11dc73f3b96f9d66d1e7be5d9d44bac097eb1dae7bbaffbedaa277f3f0e7cf853534d1298b5b4ae504a263999719d8f9fdfe047d8981752fdd2ce49815625cfe6428dd28537af8f999628716103d01440b7c524044134443b4a0150bf2e1f585e52e47393ac21d19e8054743c8f1a1a776b5a9305270a792ea02495f524ae953bed265da7b971ba66b6e2acac2c8dcd1cec04437219d99cba6938da6a8e294765ebe95392b413d94e0ba572158518bebfedc97e3b59ffb4777770d6e078931c5edd3edeeee1b8dbafe3cc2c8dc0f43e8fa5b1e7eae7f6a8cebff2e1e5ce7e1cb71916ff08409095ec00316604431b6a0882ac2400284114970e693eb4da6cd1dbd1d7ae8c10eb207137c3953ae7870fb2b110984b05ebdc58377fd79346b3e8bef9c3574b0dcaed5a7a7dce512aeff70fd616816778bae7b14d63de535e284c2be70e5bf206f74cdc4d1f9818ddbd76b2a4ee988cf04764a0e0556be11d92a9283156b78b0d0d8c2652c82808067422057fd2d8f60e1c3d06d99435784677a8642686871f3c27677bf6cd0f96702ebf737776739e5cc323ae7cc40b16760cd9fe1901cf933cc49e4a3c8339e011a5a747f2e07ebffe37dbab879d1caa8e858865439005cb385417a3512e0faa8e3ba092cbdcc8395fdd9202f0778c6c4851d39c6c36be598bdaaece93fcc070dbef2972162bf712a65328df50239948ebda6bd4127392a192ea536c83186aebc418ec05be6d07a65cff4c423c4c894c672bba71c533c7a92c0405e1d537787a4494d196cebd21f6f5c1aaae80bac8386b6c5a6b675b3b033cd708531ac6d5dbef3b9757b421b5f2281a718938b0c318c1e8b6ae111eb8f00b830cfaf02bbb0559138dadb6a7cf7c5c4c46c3131a8a1a198a1989850ac5265800980bd6df961f2ea49b97e0004d4d050ccd016862512789a12031262a21303c284afa500f320aa052616f3364050075c418597d1383f6d0438aad95941051952e96d84e0508c8b0c0dd5c2f9c9b4d5ecd4ecd8a1144c2e333f33309881cd3861c68885160b3ab0e02240cf90cc360383999f19d88c13668c58d08185d60c6c0636039b81cdc066602cb808d043809e1b9b128b4d89995c5043a613482abd8dd005168bc14c2eda731f7b1dbc101804e8027b4023542f3452333cf0a5c2933a24b3c562327410b32d4241b88ce85109bd08d1104a091db1de0b812f159ea8f0e4f6c7a6c46253622617d490663a81a4d2db085d60b118ac8607033aa15f4126ffa737bd963069d2028df39218f384244ee9558bb1588c8b8c179325afdb6f512d2d68a2715e3a99629e909e5c934b6c4acce4129b124be12bc844e63590490ca8e3c4052685171aa9191e0f8d2c12873624b3c562326ccb0b8dd40c2f95424c880a8d2cd2051d3f00c2edd76864b9a8a1d0c69748e0c9e43254148b0db9c45490490a5a0a0f233314b3289914b616195b68e34b24f014e3128bb9c830c5688f6a29caa2ca15af228ef48043b5435f23c6a21ee65df0fa8fe97846c710b020c26dd97efbaa9940263560aae6a7911bdaaa481748576495db0f13e322a3a25a343f99483c578663056a310acff493ae48177404e18074c50e0dfd36b4c5c4dc1a60e96d68351e268614530306b4117e205faf06d8c9a4f06da138d462cc0a5e0cc838352ac824e6b918981a00f0622ac8a4c66b356ad488198aa951e3cb11ab24413414f364a84a124443304f866e7831335bcc06ce98adc662b1a18cb6b49c4e6215d3d09058a53454b58c9e6240c68179690bbc3a366d61f97bb2508b5d98b6306df12717184d26cc2b12c7f482c5a63d306b1ab5d85d6303a86b60605d13e3d5787eba8459b646a7f4fc1409b3502dcf4f93a04a5026af309e0b9e24d0d6a4826be691affa9de0557feede937f6067ad5c7453b2383b8c6e4a66b96471b6ecbe32943c33678966d35b5292efd07dd7710378e45ebe46ba69120706f12e73931ad0cf610dfd3c7a2f7f76231d6948a4ef7c0f0ce2813564cf779bde73b3bc5abff74059fa7be31b58830d466ee9f96ae058c320446ebffcd207f1db35f47377197d19ad04553248ba437bae9b610d63378029a5e158fbb7d7b857f2d24f86beb25a90be46c1b17f6b00bffde6dbd00bbb905bbce9094ab0895c0e12d6c0cfddf466f661102f88776be06f4077553394e15377e4ff5727838edba4848672840e95227ce4cb40ba2c05099d2b8fc072c77ac495dbda6529471c815b74495172e5b2142555ee682f2ba172598a9228f7b214254f5cd365294a7c302045c99194d7912bb7cb525eae2b3fac4027f38227d8feaee555e9251ed452c17c4d91f305452704cd9cc8e80b484ed0b22657ea6bc94bc936abbc90d8978e922c9d922bd51be3c957aae251516194ec80c6e47f30500253d2c42a4932515f9894ec28e1140522b454819a324e577e0b18339eccf032234b83e37999aad7d18481421260902042820a0c892840f2af7c30a842352c908d268f44122490c8243c48ec58703eb13d613d220b8fcc1157aa0a4754a92b744158c1db621e15a71800bc2d26df83e2d9cb414734a9471c8144feab280a2997a29a64a89b222b92004071d87cf20258722f072519228cec7250129eebc50d2f494b3bfa22ae8a9d12e5f68b0cc37290114fee7839c88826b70a613f24b0e0228483c6635066b1fd2f520ccb414624e92e42c2f4a03b31b1de092c4b7915ddcb525e4497a5bc846e94cb525e4f2e7f7659cacbc9e52e4b792971e57b1aa8fd29317be8d6388b0a030f05481b978388b801114cdc4c8916d05ddd4d9f86b0dc9f7efbef4bdfe263859460aca305c93a4c5f356e0756aba18d0d0dcdb7fc98ba327eace14863a05f4f9f81dc53ff725e9332d8562b2492f5288d31c61897a33981628d0c6ccded9185d86824423f4e874abe54c9e74b44254321b7c80e309fe29eb063dff936448df33fe91169b185b850880e2e6de07e475f8e7bce0755cbe81438fc304122d9dd4022d9cdfe9b377bad75656be848c8285644045463f2459a9b3b7fb4b9a175e77746eef6f3dbfc6e2aaf89a8bc97d70b85c8fbb2848da40aec776527c46b980745248f4ca283d16db9e4b624d271ddd1d3f9789c80e54e1d8eee2891f40b3569701b2524a49359688a160f57869f10920ba705641c1e53bbe8d0f8d478be0d2db22eece97210113d77a4218227141ae20846f8dc7927efe04a09c4955f5d7ad7610e7047992aee7823c70bd7fd8e4cc5ca15df01c21d251299e4fa8f72491043d7adb8fe95a789eb6f533c4f5cffdfb91e7444caf5a74972fd6d8410428cebefe387eb0f648626ae3f0d2690e2fa0be1e1fa1781b99040e1e1b9feb2a7591c74022dae070d11c575e9a3834abac44a61c40909af2cb21051459423b8b8c98288264b841084129ee0cc400c2392bbbb33c00a1550a1850a84f0410d70469ec255523a50f283128e644184209c27a08002d3f5e7513371bc364e73b59ef1dc6a834353fb2b2532fd0318c714b2af4ccfc41452a3facdc357d97735a5cafe061c8ff92a0b7de838cc57f5bb5904272d1cc67122df3e105fd537e2c488931ffda537e2c48893fae3abff7d29fc422336beaa1d1af1ea936c60e2334efdea17aab6908616eb0bf155fdf1e507fbd561bcaa5f53190aae1446dcd161b2e5abfa1e58df1e61553fbceff7be7f74586c555f0581ef997ca17cf9aafeadef53a40bb3a759f2d6779803557a82fa3235ac21e3995d59d0b18c4909566acf3a1ed03832567dd0bf01a7ab200555fddbdf8063a4439503e8d7ff41bf1a71d2e18ffa5b0ddf57fe46d85734f41c1f295f39cff86b21fb2ac538e0042dcf50b1f2895c8a6e5649e34e959c3fc3f6dcffd8b82e34c2852aeedec816aaf819677bee6fc0696f03c2abf95ad6137c271a6d6c5887045ae09a09c8d5fc69a304295c843b7f641b00e199f939b88d868780784dea67862045a44e0986e40e0fb11167912de9923c343964bf04d268d0b14cb3ea1dbdcb76484e3ae4ab498dacdc99c49dcfcd56b3b49f4fb1d0a3faf3b9a309d4f154c159ee14e2c2b8b36bdde9bab3ffeb37f2bdf783f4de7ba111ef4943cdaa32bd4fca9d34a33dd42ca279e54e233a44ab601615028bf327d09d34f33e2118d1a1e9a437281dda2b51574e4f52afc1962b334f36a8ca34d0bbf274a58a525483dc95f41bd4be63affb9844a25878663efd826fcc6ff0bb92824777fe175223efd96bbe2f3d0448ef8522f09ef4a550044e7e784f0a552d02d27fa1aa1967aba0f5d57cf9e135a911afe6cf2a292551a316e71256debede7f3f8a57641d948b15d7d02ee46afecc7ab833cbe1ce1f5bb8933ee1ce1f0770a7914c8bd36b295bf60b0f3dca74329e2c49ebce315b72e79c73663e773e6d295fd267ceef5e968beee752eddb5a243073f98a7e0b3cb782342ef54824609612dc9e332dd39e6659cb41ee25282fdf4e226ba6d32bfacdaa9c454d1966ad130a2c7dcf74bc70f4c016ea5484a5204b69d2e47699a7d1a216afd0a317157eec98b5b0fcc8b9e0d2e77e58473fa5afd1faf43bdaf95c2ae4ab971178e752a2ae874bbb9e4b497f038e7c26fd177ea191ef4b21cbf0b5227aa56b2ce9255f71a9241ae50fd0952f7fb6972fa1b4e06aa104d205295be0a951d42c9ae5d2ace52ba266d1a2a6466016dd8245faf48a2b7fe42b5e970b479b2b3a587390c02d529245f9347349d05271bbea491075a1b8dde6fddc8ef324d872c4ed3a2fb6022c97634c287125a8ca427f1c97753b8fc90f14b904d1e520262e0fb85c22f6fa77a4267ef0c6382d21e6e48aabbd0455da9f3c09723db85d8b9781da4bb07ec7282691fabf9b811c737283ab814cbeaf5fe6ba547e3bb21da4db6198e9b45aa4363e74be27d9b4906a1b9b4b5b29281695621d190e3c439f3e0daec972902bfa94c61db3168ddbe1980921575938663e5c9abab4c533f45350ac6db16a5fafa66599067a2dd2ecb396d7c0f0642e24cd7a7150104063f6e4d29d22dcd1330282873b663d979660873b664b64dc3b663f978e19ecd2af5af6d32c4a47947cd2ac09f3ae8b66a07ccfa29ae5dd4fcb0cbe232bf94f4358f9534a771f2df29f6ae0eeee9d02d1dd9d7207375a9c2dcef79f30e836540a1cb71055dd94c56a9fdded41171d2c7f6fff2203bb7dddb694d875dba3649ad57916a6dbfea6c5ad57808275af610d342423ab9a41c61544280f4a3a4e98fa7368e32b5f5de1b343958ff96c836bba37dd8417111144536787c7c95e7b034803c81b601c8369569de190793d0cc2b1db2fa980b175f137af0130c56cc53a0270b5f7f6da796771743fd21eeeabd671a4cfe475e011fbc5905d2da43c151c29ac455d303a6cd456cbd81ab027aefc8e71f13adaeacdb35bc30aa698851795c7cc346e5cc93a98c757f27b7a5e2f0aaba2860a42363e85ec3797a1af7d37e3cbd1aa8c2fa7ca3bbde6ca0acaf019ee29782d1d7f79861576ac3154548051d9171aa90008c0e5868c19164e309f1534f9594113d84f9315c07e9af87c60e5361c261a3b80d29b50292a2894e933942580f5e92922c98c2aec5644129f5792227c5e34090d694f26bb0230ed695991c8ab1db9f2d593fd4d339290f2780eaf7a0d7706f09875749729ab669b5547fd4e4a991b5f8e164a1c32ac60aa73c1a3947ead95b6b4d6e6372a0d7d1a5aef0bf332b507f0842eb7749c68dffdd02777a44034caeda8b8f2fbbfef401d84501ff72850140fa42d5fb9acb02385397f5938c3033bd22737ae06521fcfbf1de29d60006e06d216d5e955d3279a96ad2e87804e8037bb86b67a5e1c2ba82093020b33370410809508001a52ed1c2b98b55923a535d0f1c6ed6f7e9637eedf7e19d629d977ddcd3fdf059e773e4b10242287c857b1ec19582c675f3d6b621ddc672f83756c368c20fbcb3c236d9037b4a2b84146ec6459100a786e96912ca9df06a8da9e6f0d55b5853d1070fb87dcdc1aaa6440c0ed47c0edbfe1ba4603eba4ecc1e99af965a5742576c8948d6a6b93b0e3bf8cd841742110a23f949dd908b3ff5c7f8f9999dd06ed01a09103ea09d2819e52106c575454545454345b4e265b9a2d618b54d42a6161399a1c5ecafbac34c1c6d2a5a2a2a222a36ef534a2757cc76b9c9c80a6595b929247d32c97401938dee03376b6414fd2a217555b545404644b2f2526e44f1b1db5dce53c5cf6b2dec0d62fbda9d4452622e7e119ffda547a286ca11685ea8400fbaa27609f49494bbd96aadc675f1884876e7b35f49e13d8329eb0a4f71f6c0d1be8ef226e7418074aa3168b7ce5df8112cb518b2e8b483ca8df9f7d0d77f0a53224aba83a94ae16775a847d5158d1579b8c17d825bdf2225e53fad297c22e4240e9c8f6783d48b0a304bab38b322acdaabf85def3b2f3e5571cbe1e2aeee601d686f45b58badbd66d6d1476514b2bc23185aa6dc352027df8aa5b5ed345ecddeaa25658d44533f8ca81b458023b76d1eda2ad04d2206473828eabd5a2f79536eaa26e3533771258f42fea56d1f5b06b1827742cf372e5ede4473f87cb1410455114c58c022dba288aa24833f29522428870964bfdd56ab55090d3938557ac536b728ad8e115dfd1ba90530412c6c12f579b379e662f9d607df502841579e52f1ab9785b488b3b8028c1ca2bc46b3c94911311d222a5524ab962d145230f5332be39e9af3595b2f168901185b52dba0c29ac7c4ea645af390569d16d7c6573ddc6266573d382bffb3469d63442ee20b9fe397a5e903e2592189e91e44e8beed2c76b54f619a7a515e1b4bc3f09942650f2b46863e335a7175bc0f7954b974d6692ae8d3b6249efd9db78cd77c56d2a6edfbaa5d85ff41a0e6d5fd268e36ae1c85904e12250863341ad2f5d5e73b2b9018323c272ed62606f6c5af46d0c3b840dcff84bd7751aa25a9ccf5e6a0f089e416469d879dd55fb5eceaea3de979379c3784503ae01d2525cff8e73bc6f7f52033b4a98a4d22c189428343645a8a812b903f11a7751a116a95cc9bd6b8752aafdd6514a358d526d04b746b99bf3f41577b52865d8454ed8b1c70a6b564f92972f711f6fe23f45545e19d6746a918112c5967fda791ef7e5f09516bda1b4e82f5f6a4f29a9549f524a27e94b2450d334edbbd382b05057650b2442c8cf015668fc4b7faee65f8ec6bd0525cc575a17ca222e944450ec288d6496fa1a96666d46f2a85b1226611226611246d4a25b69d16f98a184a54492ca272dd9922dd98a41fe784f101156ae7ca176cdf0811d3267c0ac07d1b6f7a7c16bb53695faef1d7cbdd0c66bec9bde14ca96affc65fc288e7f12bf254c8516ec225fb911ac36acd66a535b38a6607f61e26d229ef197405a286130ef09bde742611a76518bfe0d835907027f5cab93faa49956ebf4af95522dcb3277772b19137456cd9ff0a9d549678b093a6990031e8d54e947631b57ec74665a2dcd40c4463c06adb3fe945dcee7c50f1fa46e062e32ddd1b2d1952dc903cc67373f27a5aa6593ce50b4a2e1311c4a26eb1733f8aa6bc7d16c33d170813c2b226ebf44bec12cd72add381e5ce3bd10d932b98260f1fbe01aebab7e3ae7a4efdd7704db354a468672498bfdf225a5922fec685f28259ad762fde143013c60b859b906c06343c40c68c69681fc68f1a6459b164dbcea07a52b886b353ab927c8ecca9edb3d3e78a6ef690656caacb33e4191b91321f1b19b16334a39078d9865762ed1b18c4f4aabf6e5544b2a655a37e9d6160a17fe8ca21737f92da03ef7dad7e303777b0eb433f5dca4ffff344fbae997537aae661ab77d243bbd52cd723c2ba5906dc9d36276de0206f5d7bee551de577a7ef1060dc99e4e60ca92482530fcbc56edbcc5d45b11bae772e8675ac64d1b46502f572b934a367bd69eddc4158a95cffdd6c96f01db775fbf1e1fe0421b569b1c48b0322d768e16c51b2db67fe23ba53d3e3069684e4ed0709d25e9987e4f9d8aa6e767130d1a976db36a5c15d2027a11ffea03134cd4ca93a79c1c3884c8f4dcab077660338ef95c36e7acb49b9352f749e9578d2e607a0e7e52ff7248ee7ed352faf799e7699ee835a59a69943bb5a058a4e787a159396c28bb505dc89e1f5592a62f59930d4b255b4ad92880a816eda9de6ddb36afcbbcdfb6cdebb68ddbc2ae3f19e815b3779f4388580a12495d4a6ff6cd383219e89ddf02fcd230d36c735e74cf2bd9eb32afeb8ee31eddc33fb3dbd7a3ceefd19fce4981e8a4772b0df36ffaa63f15589edc44d7c912b3943497672983d16856513bf06b2814d1ad9e0f0c4c91f75c961e6a072bbf6baf3d6e71eca1cb63ad2d4cb05262413d6a075b2a7d3b60aca9c4dcedeeeedcacb5b4b4b4649406493ecc95a1cb162b3aee6ab6c3e37874f3bc66a134b0fe37ff936100777bce7efb7232adf332505ee979fdf5a0b9f35f560843deec5f3622acdd39007ae78e4d1372e7b4d2d59ae5c8364a27d5b43949255a6d577b785ae4f9f155131f964472844567524ba06a7bd2976e60a054b5551b7289f20c0d55a1dc97545c68e9adf629c8c4964814d5e2943cf225a744b9db46e9466d3a4a6bf55c1e58b76df3b8dfbed3be9c2d07f7b5524a376ef6943c3c487ab64d6e5a388a5d97bc68c141b2662681240eafde89ba13c86bea9c19902e5c8071ccc700cfcc1fb33bdb0649834be4eba15d0966c0a751110a6af7025cb3f16afeb6994c34644c028561ebcfc03333c802b4908908b6af0fa4be1127db465f86230ded14ac41523706218ae5148553149ee86afd5ab3acd65a6b142638ca6854d3268979e6cba9415f087552f834d0759a4929e9cf8cd2eefe8ef0fe427e00a140068230c855eafc101212120232332a97b4e84637b2c05c79bb2c333d7f5e279de01a5328ad8da294c201e97bf08940082516a18f16b7caced3582dd3bec43273263d6221a36e9ede696196c4c2058bce8466dfbde7e3c717aa320a866c3dd94a35ecbaf4bea980169d26259412127de5ef819fe2bc9822bda022bda8c2caf1c20a8b7c1d4635df53cd2faccf81f46eaf814cbe50e5008aa3831c03c70b59f268d159c80710d6a1bdbfbfba1f07b00269947e346d3fbeb2a1e48257fea5ad924cf6a391f2c01f2591165a288d8c7eb4e85f65cb07919546cdaab26a99b603cb5150174fa4d1c842d3dd478bfe2a9f1d4028287eb2f440e8a78504b69f080c2d02f1c8226b36259ba8d7949eb9cbbacf76ae67912d224734b4e8576e78c6935f8ebc13ac3ad851be543fb6259084d2a2bf074a9818cc924b64122c3a93d397def4249004438b73ceaf77bebce6640a6b687dd52dd8b11e59cf72bd8b26d7dff31a927d01089033500ab4a52f85bf811ec8a41492c2cb26cb37af256c912c7220220d337e6047cef258bafae5649dd9b4686f78f80032030d4184102962a459f3bdcb3ef3ea7ffebef2b1421836fb2ecb62f25a30755337b0d266183cf91a399359d6524a95fc5154c950bc36a712b089af8d4fd13049f617fddd63a0f1e1ab399f7b0aeed06e57ebdc7eb471e7edba1008283ec18e4c348ab7fb49b970c81079bdb0e240d3581e45f5aaa8094d7bd77cd04227337bcea6cf6c32d3ec25a54f6b0cac94609cc0387d71fa22fbec0486cdb22ccbb24cd3b22cabe131ffd4f2a72f6c4b188425cab52ddbc8cdbe7a17455f6e561766aa1ef66a6faf16d616399c99155d0dcac77a2d6e4d7c0d984650a1e220bf5461c7da43247f6ded19aca1d68ac147d0ba446a903a7996109ad31bf4405435829dedeed91e62b774398a9516d0123ff2aba11748baa872a7e862cae529ba806273798a261c5df9e5b8fc60f9d2a74b58ede5779efc6d7b77ce9d7beef9eb421bb4cba11c0c828f7f27360bdc649216535e2371a4ec69b13f971dcb37fb1baf3ab787d0b18c12464bbc2e072dc17347e9ba1cb4a4753bfe725e806c2b4174394809a13bcaa0cb414ab8ae7f392f30db5b290bcf6654ebc051e6ba25fd7c6b3d9f5fb27fd3260f05a6605ee6064d8b36e00b372dcabf819577f6a0d7dfc30e258f16c47439e79c2f403578a18165ce659326f451b75f2e6951368162f93b6c72a59437e8988485e4859d5dc73263f3dc9d5ed16769ed734080b4ec58ee47e9fae9cdaed1d9e17166bccfc964c6773f3f48ea92545dd883deeebbb0488bf3678b3364e27a225bedb6edcb69f9edcb4175dd9c5d57e4a15ab4d94dfb9b0d5573fef42fa7050b7d0d94595aa44f3f697402dbbdfcfe6fbe7dff20a9cbfd66ed5bf719aae6cf700528d8d75ee6a0a6dc913c4826e8dc37e78fe0bae5eccbefac6db6cf63bd0d447d055bba0e747907657c2797b84c363d7db9a445ba3db78bfb2c289148977c590b4a243d21fdd2cee9a551ef902850efb824a2efa02caad22cbe555aa4bf0d9940974619a58f953bbeeb89149941bfebbe03c7794bdf7d4fea883a929cf66710ef03e7db6e7e17b2c4d175dffd0c5087db0063bd12d5227d1b033b32d0a447a5ffbc6f161bf18d16e990f9527e31d8803764401aee900b3997917419d1ea02324aa34bdffb667107c2b4483fd5c5c03ef7445c2f4f6ca0dc69917e45225d2dd27739816d9e53cf5841f762033bff3487a68b10d6ffc5899719586dd3344dd37e035fa2b0fd55c3a16319af7b996a71dbb6ad3ef72f50c86c2488655ad7a6dde198c9aad1c86a69bc90c657f32b0d69b630957a1f2f50ac7b9414c028a5ee3de79c4e29a594524a29a5944e4a9f524a29a5f45d84b0f3e9a44e744ce2c2ce1625517d0d3c3d129f528cca93cb592ec77cd0a4ebbe1c9274d9433af9775d27bf1cade51b94db97d312aab486a1451e0af86e8a60fdb7afddcf7d909416aa3aec416f734f3f1e7c6bf0cfbefdb3ed494ba4e7bde46e7afec9e09b76b7bf697faf9cbf07763504fdab74ae65f0525fce101a9efc7698de7b69dfc1d273d504ca78353aa8dfe17150a193d20a61d8d294a52ec9d2bc3939c16e3f32d0182475733c54c9b007bdf26558a4c5ef678bdf9d250b96408bf27ebe247544ee242939d0bbdc4bd7e5e473dc3b289feb9692e3c09c21ddfb8e1dddac35e277620e0f1c821d1988eb5740b35afeb468675604262af5d08159d87e91c27e0a5cfca1c5c2f64b70870e1dfdce68523094d2fe6cb6f441c77e838ebb66e21c492adde508fd72a6949e0427101dcb8cfd925acd5a488bce46359431fea6102f621433ea84645d630c2479dc6d8c8d62228d7b385a4f75617f64231b1e30c82497e6851bd963df8369561652213e5fb65c37fdaa437c50124597839218a2b91c9484cf9d36cd6af71a8a53569b9bae3b729bdb02afe6dbc04a996ec333f3495858176718a617a8a4ed366541eb14cd08000000d314002020140c074442b1482c1ea8c1b67714800b81a0506c501a8ac324c7611884102386114200c000080000cccc3620007ac903a33bb4bdcd5b4ec13477137d9d6ae94d9b50ff85e17abdd964cd1268069f320e9340e9b67c99eeb99731cb35c8ea7fac9016d4b3db0d6cc0e45ab3e111506efd3d3f86957524ec59734349e6ff6a3ade76ac2907f43e91c5872d8871e8b840a558ccf8320abd9139ca3c0b433a5fe720f3ae9ee4550b47a932b7cbeb3ff767aa553404c27ed323fd7d71f8d65f5767ea375b4d8921173195a7b7e70e967f346d244bfb0037e8b553d76ee1eed71a6aaef8a09bbd56751cc50fa1939f568e488dfc354928f469d84b89b9ce1932a2dc7b2697ffdc422272722f8e13d17ea5a0b2d4cb64afa2dc1dc5ee20192aefd658588cede5647ebe03f65f01d0f9853fef3c7211ae4cef4f3f3ef3cf5b93cf92142f9d1a1e27269bba86aa02170ac32f62772f427730d55524e4b5863a6864db69012f678045bc81b29082612ed36eaf0e94af236d0fac24e64af55413fd7036f573edfcba88c6b991fbf840138b3aeaa819ed86758ea9f3597f0d87a24100e4e39d3c6df0cf43bac0ee65bc19ecfcf9f0ffee0c48e259344f649d6b1c58464391019cc2bbf56c3f4e192b7969816311c830e4be00b9042cd83e4538273e97d97c163ef389dab6fe045e72c81ad121d9807b43c3e350b9a3e34d1f0fa0bcaabe108aeef0c35d807f3461f94a36822bc1429ff3986a6d1024683ecfc425a85cf6887b440688833984e8d7d2cf72aa8fb0915cea0785ba0698d7064551c7ee67fdde5e3c65d3b7a25d7c81a5ae4a042e8fbcd982c825d3d90cf175544ac1c6e0baf41eb9f0636721cbfbeb3d6c7c0f89ea9c2571e585595f087230a8f933312b40c6f8151988b823b96f808612ab3430d143cdb186cb2989785b74f1dd63f7a8296d186d6a4534f3747cd7f9c3bb919c3ee671906a8f3e224148104b580ff5e43b1c17ea5346d6848400efed7431d4523d1db647e698badff670c05149b64d06637469edca26d43516afb29da37f29e94926f1c12474bd73ba2e643fbfac26ddf6842f580d61261cac6258499380e9e0ad209d622c13574c5692a2d882998c7889973be61797441201b939ee1aad07a50acff297d2e68ad46d4dd977628f24812c7bfc1b19a0a1249e16ee2dcf75593524decd3f070b6eead110d8aa29ece2ce66a2347b90e890ef1433408ee08bd34eccb60fb1fbd0e964453eb544e2500dd1fc53225ded950cf70c30605dfd8879a2dbdd51015060519a2b7eea3cd2e2431c414df1f7ac2111c1642843e56b2d78a6fa6a5fccd1b6e44a4a48a873d94745c5bd58eea8a86a66c4bd0e616328d8a748e1fa00d874d491c3214ae73cf0727ab1219efa2383611c9be8c369728c281885bb685fe043280aed17c415822ba321285e63f3946c95035b4b8fdb75377c43c0eec7730ffa11bd2f164ccfe8413978ec5de02d1976b0545077102455c9b5301058928b56e4597ad21507452275ce7558c2807d12b3323af39ec7d3fc330d36fd912bf73615827b579e05162189c1038c28d4004aacf0ab136e469ad61e5ebe2db6cddbe231064129ff4028c52624cbe4fa0eeae5858c90790833e3c88805ee3d36d8c0ab4dc2e39384224f0b138abe4b3b9a862f697ca7e76f1adfe9fb4bd3471a7e69f795a65ffafda7f39b063f69864f9a264c16c0b47923f54169faa4ef733a9f69f848cf7f5a3ed3f09db65f1afda4e193b67fe9faa49d3eb2b4b6fb23719a6f406fbaf8d64dea83f39e10f4a48928730f52acdf1dae8b7f981b725e0c03f34f99c1fa4fecd98100baa4d2ac979cf99b79503188d384917053c0fc45f51e31d0a81734326e293ba76f5d52caaa4f207609d9ea2b7faaaadc96bb1af7d28701584a789f691e283c37e26054c3f2705236670f16898ccdbebbb1be9ac8955345447509d6943a385a2c6f174a92a9763c680396d21dd941434cf4592298d44285f05f7030fac7c6a51c6b9a801b38228ecfe22cd436cdcc78dccb727960909d20e0fbf97dcdb77a687aa60c185905028ced29d7bb894fcfc0d5369f08c5aa7128bbd8ce9f12b8f95969392d030a2d97afdc3ff4b34a2230f7076f347dad4adf272069f0ec024fe9570826712d7bef2e4c6097e9236d4ff46b3c250a93cb849f18e816eb06d962dd444cc5804b2f75e96654dd59f52b72aebd86bbe40037d9cb85543a599a71d166f1c5e26a0dec5f6f8cd776efb7c028afae5851aa538096365be9f3ab0c92629c578474acc4fa0d9a7bc640788489f710810a58349e874257efe70268550dc7ec436042f2a71e48a98004d4d3cab47a2e5e652c14d01d8705fc48f069e24e0d1a757b0fbde66cec5f2e149013c608515feee9697aa93ef1ab87ed8ba22d7ded7ca0feda6015c4c5e38a31b8ef79f5ad86cc9db373299e0b2c73ad9267cd517ecb14292b1b23f0ee79b8d17b7cdd41fb4a4368b9a9e79d2decf3945ad1b8779a0dd3b2b91b1195082265d90f15cf8b154ca45dca1a425d9ec0da57560425c1f7094ac2c0c2459fb51654c407928d7ecf30d02457f14180e5c01161277681ed76c6b6256292b1ed0639634efa47c9180c96e668ecb7c6bdbd44ab4ab39b6fff558371f1789df64b90b0ed469c2c46d09e529327a1d23102a07bbbe295adfa08b9f30b3a57495b80d01cd31ffe427e9053c5060e5ce99bb86a4a9c31654434b4cebc1ee4f63c33eee46fa6ad6940f6fa826380831e782cc3b0b3a10245cf6a5bf3cfeccef451bc868ea6a3b4ca80b53c87a160bb9cc57606e2e8b812195eb25c55ff4a7d6663946b05ccca2f638c84aa2e3bfd6ebc08d559e0adb245596827e94226d6ac33053c98829d1ab1368dfc0b7d80c8ee1586eb94201dfd1133c630d64df9b33b0a136c10824b26fe8d2164996aa802075ef7a06e0ee1409d01abed0233a6d1e7d3daf51f7acd602f1e9e99f90934f0b792aac76a8875e5f44543d3f2edfe804dcfe77f1261447cae6038c2affece318ca3154053743f2c370d1605905942386339c936be2a8c80f01b9e921ce9179826d32bf0d1f9ebd1cb4c7869883cba1e9415c62e4b1011a5bd8bae0e0aacfbb3b70d476284a34059997c1753aecb5e6a8b4888b11c30ae48034937a6304000fa9387b141968b6e0e97ca8389b2d70a157270bd17ea4b58347405ed32262f2f4ba7e2700485e56eb8adfe8f659caaa81646aaf6c241719ab8eee980c5a18dc506a0ffcc1892e7a6ca1e6c54eca966075519bc3fc0ff96bbdc25f964929a50a4388f5f8abb4500c69ac9b69679be8877520e023195524f52d1e6d5a00d40e311a1836cddcefdadd3ae47872cbe80899784c65d142ebd117bfcfd46f697332887f4f0c6ab1e48a498e2db3619c6ddd3050f2e7c013f0d52cd36bf1fc1cfa3c7b44a7666d0e1ea3da5c9e5b3e01590075de02a2bb89e88695bde828d0dfc59aec4b993551939e0de820297d05d6d3eeea7b8eb51cc6114e11207bd9922e2ae1beb0f844ca2d19812ebf6322480776ff5428a3720dd27271fb77ace8296301a575dcf89502ba349bad047ad17450f7104dfbf58270e1371281775aadbc09714bd651be040e331558efc9e24711f36a029812b96b2f4980896ef342c2449160b4f2d3f6f191a4251c6ac958cb8dc13e46ee88e9265a5f6d16505a27d6bb9a8d24d19be73bf2682d2f162666c8052e0e6c6bb2506e22dd4a38ee72a621fb1c674c64ad4c1b46e98ef722921310affcbfe993bdcd1c5821ed52e1754ed8283e71b5f34a7c323693ef02b96c0c324dfb3edc61450e188703aca6107ba66ed8d95a94c4caca6a570ca323364f0a4c86b929cbe1fb5069f1daf9d81ee3a96a0f8991e603390b3ac6804110103a97ca7b9122388ff81f4df2fb31aa868c42272150334519d039f32355007b154e32f0712ce3e1d3dc70d8d0037d3724cf351ec87c03d0683df1da88504b67e45a72093d17efefd09054f883a3703943f2efbc7301a38e003740dc0db014f43e74d60e08aeb584bbab315835aab449fd03776da264d7816828c54bd5cab206ec8fd2579226e427b640d8bc1d7932e505ca795b1e17574b94fdde8ca570327da271a854e4130e72b3dd632b6164c65415ceed463689512935e77827add16f2a80e613d120a7f6b363b5136e45735bb609205f779fbda89068b17a1a1491c497098882f3189645c0fdd3c0680e09f2de240709cdeb5a7eaa888356e18845b6a56c42466576de55dd4fb88fa2f51672fbda936a58698b4662d33206968bd21a791c498b3cffc7e26ee33bdb7c97ecdec61b267537b9aedd9fc1e937b98b3cfb4efc94c682c8213b4f48d4ccce41e26f633bf97c93d4cef6db647337b98ec6ddebe267b9bbcc7e43e26c8bedc04ed5e260eae2a31d4904df93ebe196e18f16b405c9b6ddf6ace5bfbb98cbc958bf877b57927e93ac6b9fd4d097d830323cb72286fd97d8c137a5ff66633d1ed6f2805e56ae9789a7946b1e68b8d6fb08ccba883b7e09d8d1ce19880a7adf77b740fe3326a6790a2c9cd65e6312526cd322ea3681e5920c9f9ad027b90cf4e822c31ae538b4254f8cd2d76550719cde83d3202de78c00b06c24d33f392aee2e70fadb431e215261062333486d331047d2f8f66f088b1f25a045d81780b9f15c6d0371916ccc6791547628315a3745961c728bd48c8b00219a343805fdc01a4c0612cc24ce090cc14b570f6c14e5dc4a875cce4baa0f3b6fbb1f89f1b04f32faa4adde10efe1b0733cb32a14408b5306f5bc62fb20a8544e03119f13cb8299c9f24d782c76a66b2ba3fbcae61b95a1bf749743791436f952172acd68042834e235de1f2940a508ae0264ccf1231f75510ea15917112becd2020197c1ccbeb79b8ededad1227d51f0cfe49e6112192b334b5c7033b9c8abf47c4dce9919ab256d1850e67502d60d138d3dea981d0ffee6e41cd6266e1a603cda9a885039e8d0e2b5c4e656d764acdbd1bfaeb25071fd200f128bd9a633b50bfadb2ca4f97c337057d7330c7f88cad95aa68dbe08025a6c5a2c8dd4bcf0146303af0797344a0258a081df64d77ed58aa888a116b828400d2e48771df47ab1cba71498514487d757bc43c18ab0894cdbaa3e68d09e555e1771ab73c8825054cc1d3c1258ac229b1d75f238efd601b7ae984bee47c0bd871190c1b11d5f15becfef669ff5c2b6130b006bfc095dec2302858afad4542dc487638b23316edbef1fccd873cf8cbe7f757cc0069c4e6aa10500f004e979b5ae5f52766f4f9d84e4cd376451609039d67e47c1491e4df27de8c234001b70ddae8e972029319b732a6e488c5e592ef50aeb0607634e76a4def19a3202c5f674e36518d26dd30b0a5d61c278a2a7677ef744009f42a195119fd8b44562cbb0af852e20fe90f71942fef6b5d7d5dffe79587211401e73cd969aa456cd241287fffc61086696d7ef92f98d6b92949ed04eb06e56a236b758a22168ec5a6ffa2819580990498940a02a85c87511ed1f1cd2e1ee0da31937da01c9a5115e0e4dd2fe488348f4001cf06cadddf1361ca20ce8ba710d9b4b66e80198b65706a8e12727e81a5042f9edff82d978a2cf581422175c2bc4017fa32d6cd9a86e14e5028d850be2aef5e11f4283147ad13ad9f6d01d10291c8786888c69a1b5239ae9a6b0ff354ca099fc065ab8d81cdd0f83d7db285beff16b3c236c8d05b4014f81665b136e9e9e15f39224682d11a34d685516b8de44c432feafce8300f9993a2bbbf648b2501969f54c92813518071960e145e74d4050d34837d9dc0cbcbe52da3800dee1144f3120fd344d0e7e324e3a115c3cba06900254e40e8fe7634d887b16c3fb9cfe6024301ad46e49998f6704ee75c4064df5fb03bdb48b7476b97d29572e320eb90211748888b7c3016562c4b29d7d1213e703b99fc843ad89c30a7c3aab7b32f5772561230a0f290fef840975349581db7ef4b786b10485b79c19df99ad27f00117020eac26239ed6443cfbc0e36a61233b93e06b025d9d9992981d9f7a5acafc19bcb44bc0526942e74697759619228e7487ad61c1b70955822d932c00ec8d37835107aea313c8d761f612094175877f716fc7e05194b69d5ac80fb1368e597043d3b623fdf666246b4af8b280e7bb737439cc797a594210e360205d18a469744804fe9387d8645baf9855e3f9aff7b74c5626a08e6911d76725643680e517b44b79218be40d0d5504af38500f88fba6b054a5aedf857b2bd7f908e1d449477551ff776a88e4b8265a43a5c109c431f63bebd492a38877239244c1e1019ff5304356b692e8f5a4d78346dd07bf780149baa507a6fa847a1f9f26e2e6c36281f1917640faac4470b198bcc612777412f5dc8229592323c766b88e595152834f9da4141e9947df69db7db79b610c424b7ac031a7f89b304325b2190fb5d5f7a324e4de55cbd7b6c9783401d91b1da12e501cc70a326a08d7106f488766d81ac853643ef40b451d067a2f3e74f7271206f31f10d0122a8a24fab74adbe1c2d0c0bfa3950f1e86c0baaed0e0ce740177f73fc533c6e291328a5fd235cfc617d558d5f33cfb42100010476d41fbde8b1967313f1736bc4526c5de4e6296c3b4ae65a9bd579ea743491b9ce25f890fb0b75411eda65b2ff52a14c7468bba34c28ab574ee19a512002b35e505915ae04d670e3ce65131439c72c0edec46900df79ed05534bbafc085253b23a4bbbe731e3a005525aff422095f45a35bc91359981943ee5dfdc4b65f6e2a5c134110f7713d64ab57b76cc3e929193d95078766e7f59ad2223ba9574e256c361be651d96b4a4c567739e26913dcefefa799bfdc1216e1b28521c27b0cdc3a79e96cff588f3c78aa8f5d93f69743c37f15e0ed846db37ebdc026ec4582ec4785d3e5f580eaf4b75af2c61ad555f89eaec78d7dfba2711e49734427287eff015c299cb76187d2a7980e1d552c313796d28e1def11bafc13dbc638f710c900d2e04a433828f5574c9fa8a95cf353b260da4dd8eecd2ea04617a960d10762daec82edbf87ea1021ac7ef2598d2cf93a27e7c29d10d4b94d74b78266f295a1b27797752850b2354274c72dd4484cb993f40514d48d8e6dcd5408d2f24e235b3a3591a7ecfa409880a449b3f2df78bcd5907d92d8b750aa391418dfd79c27045de0728183c91b9019aa1113012ccdac31c40793ada58a21099835910ef8da2e40aa356d1c0e52b8754f1d2da00516230d401c073cadc33e43865f7e69114dcc8bc2f11715e6a0362d654a9248779e9e6a1b95643572c5a4e449c695c0e9845db63b211e1b00e3b6658c0ad0e03038e2bd1bf62e72fc941ba2518e1c8bdad62ad1cab57f5495f68e555edb462e61fd4281614e1f18873d2ffa32a26a0da48549e3fa7de4e7e469f7b87d8d2de0ae81a0e7fd1292025bb52963040b8e16c119685899205c4b44e2ae7d1528314b9999e062b0ae51569ac86285be417116307fa8ad72f5b27bdcb53bd2569d50f18d4c889ac70d63e72deb368cf235d71c2b910e3fa32d79e8a14bf3b8f73e5ebd162699be5fb4fe21e4107cf38b28af9ec69dce392375c289f6def5a783aba1961100e0b407403ebfc52b07091203645627b3ccf2fbc97b46a7dce0b16cbc46e57e32afcd898143c79799718350e02cf06d1d2e1b377408234b186e5bad7612e2c5948e2a4463843b3c3718afd4671dc1962c817644c595d0f5584233fcc13c32ec9bce9ff806045dc679966e4662174fba557baba570bbcfa7d4cd1a8e30fe01ea7809c4c247018561723fead9587951a382939e4cbf8db9e3de7ec86196eecac0662846cf336db02bf3cccf1b4219cf2a33b104ea1f9723cc76ca546f7674f88ccb3c849cbb0bdd4f5ba4d256193ebb169f50ff2166ad48023681597b369faaa008023ba9fa838738d5157be50fe5e238f6c0258d3a446d7b553a80faf5bdc5e4bae01ae1e39407961a20ffa5abef690aa6cde7c212ec7e813884f17a10435fbf098e1afef6082fb3a3890c6f1efd5d697df36b15618cc1660b64a8a000219d223ada0307a3a85443fb25d1bdc9b081199a5470e993c1633519bf2f2dbeee0f0e3a4291398cb623616e4eec80ee063a1e1f7941a9e9f20c6c7b980c6d7006dd61bf7f8b506fc8b3ad856305c00122e4b0ef5fdfa0de8261691e9f3400b71abadccfea98b48a6468f56baea04ddb7f9bdb2e7c572c0d602ed3048352848e639fb7eaf03cd36f6ef1574812587c8528d8ce4888a9c910901e90d67938aaa0c3bcc1f974942d55e6cbe8d3ffa9dcd21a7e888c15d10ba1a6d99507ca1bf60132e1a627a1f88086b3b172c3b4a4fd13a1aa6e1fa969de59d877d61852f101bdcabd6fb7f8cce20a29e51a1d4e723c5ff7f158886190999066c57c43ea13f1472792078aa5784cc408e025ee7712bfb20cd8ccd48e8b36ad61f060a87b99102389555eefd3e0acde42e8dc9b51cca8d30a0ac2af0d697ed67075610cac490da6559fee0f43200bd5f68ee3a207ba48df57042ea40676b75077c9b2bfc9de9a1bc053a9f51c344161593280c2252e1b2e438903373fdbe2537b5730f270e30c1c226144a52d7cda6370bf3a54688ea3c0a0886c28b871c6eed9a87002ef8addac57a2f1b44c7bbef9caef81608a0bb08407c1fcdae1c2882ac6ff65e0a599965e700d79d6e95877917b8835645b0ee364654c03ca6d79f1bb03b71578e7df9cd7eabc98ac3eb5bd9ae5f91c3d4d7091c4d5afce60493f479f75ec6a290502483d33bc7c70631c8d88e648a1a936e6ffeda43370f317080c106904499d4e5136d99226ec2288a32ef8f187351e85b0ba0f9dde50af463ecca6c785457a58ef1eb5927a3a9d168850b4ac8bf2d1c3903a1f67a65edc476737a42b7489d9ce42d82eea4753049c9631657b6118b42f0dbbcde081726ddb77050770bde5dfc109a6a8ebc590fe0ff12350a4c4c9d68d85155b3859b5264207a6202184389e7f638601ec2e2a9c95e9b93e2063acdadc6d04a0a62220df4e049134b9df25fdf6183a37a9c6de2ad03278a82d996bc3d30695fbdab679d711e2f088c269a858ea50eea313eda379157824c0309261a34e331683d3a03b756239f4a569b8ff53e91e9f0f937b6383b568b74654b8f9b765960bedd7fabfc28a73036356642bf5326b5501af886296283737cab309d79f123d5639e8017510d423512c55f5eb00e9d9c7af085b65c9c8825a27ec6008f5084f71d4a5613ca11eea82544142172c3acaf9287f22da96aaf7de016365149e8b8427dc28d56762080246882fc81d54743d9a79f38883c52a4b025cad2c630f1afa24487703a279624077f47b878144cfbd9d8c19866fa2188a8b4b2010f0d651383be867f730aacd932557a1fb37b66c6891e850ee781b42064b79d8282008221c670781e507ce02433afd25fddf3a2d35b4f818f472bee33d4626c6599b7b4c54bfb3684c40ad077e1960318f3b8b39c1db5e3b9e88df96d531812cba1bfe793377eda59adc1ddf58f076eb4d26b250a7c19b0b35c22313c4301fffaa20e18eabb76adc006bc07ef1312c02c70f3496380bdd9e45bc5737509562470d7744b10eba141ddfb34b32ace994e099f3ac47fb5ae225f3c50e8e925720bdcac918ac81278843a6142923806d920cfe6ddc94205164577cf032d0c3296db6db0d20dc6df603f11a8691f5efe19a10de44efc3da113ee84b6ff8d7caeba80c179ae2749f6bdaad16f1c1ce19845f3e80e0283af22eb251edb93dca004ce41b91330d7134e5d4ab24ab3e3f09e4bc4ff53c97c1471ef76f0dc4582d8611e4604bad0130c05a5c87736b81319a1c8ec71b44fb909f424f6a1a13a76c5118c163dd02ef5b2dd66c055dee34215bcf13c25eb128a2f288633821d0539d86e23f9009832db98073119542067e5279fa915de900af41cf10c98d648ddb8abaecc8bcb398f759bd8bdb6e3fda847c0366ab8f399494534642b6c1fef83044bfa45d993e3fe1124e08a8f7be4eb3066b88f87ac87815644898a60a828e823c4231252107cf18f04a1739472784ceff2e1ee6cab2216d5d6a2ea6c6481c7f59033fb318fd752168848958df4066c2589a87e784cc4840caa8e25815908bb8090e9a85017939f2af9b55d4f6f5f2d901113a428e817e8dd4ef4cd7c7005ad093d421c18e0586b0deb24eae50a4ce0bada4b681fb48639a0ffb80deecc6eac1f0a2c54bbe117776fa72b52f0df14280d83d49a08b9a725260b2882c023427ba6de1b0d39c93e018703095410f05408c2c04aeabaf361e9b64d3f2cc117acd315b4837f6aa40c387a2b713c7b7786745e29d5a77825b47a8082ca48bb11969cd6c44b463df9e0c6a994b27a5d965a748864f846f5d63895acc204372338440588d77e3bb0a56c77d76ebea52a7eb764c5055e441a959c815c44fca583bbbde92abe08ef60611e0c8e34e893946abe6d0fc549a4c301a6821773bff13bf895a185b529179326d28a3270926cf77c57ef29e0c27aed6f60e8dae3ac13484f002696ba1a1382f791275763f5d5e73ec5fef509fc748d7a82e1e255ad0d296025fb5bc5aa7be8bb887afdfbd65cc213a11558c0a8b543b782e554a82bbd1d9014cb72a920ca6e9322625ba62a6502bfe97927115f4f004f21dc168141a51f020e75aa93a87a60a203234beb1b61a1cdaf5806470465a5190c989d6fb5c023533da88e4a1d66028c0d40dcf094ab74be83e0e14dcd4de96290697d7c9f632d98cef6cd32e3b819a93854f4214a9a7a05628d73696fc439d9b649b8e385fa1881e6f4c3935c1428e62fa29a35acc625e8a369cb5cc8974b35c8ed0503025e91baf6784b46df18e493478d54b3eccf6ff0c585058897b44b4c8d68f3c41cbbff8f2db8880bed43f161b3dcb1cd77807a8925a4e35132ebea4184ede8ea0f45764ad9a10a27f744fd23d186792b907d31c247a270cc40c889218396fe5b2489f75c890a53087c1cea18fe36f7f940a412145d554ddc6ee8db052c0162f187bc4afd20561a4f0981a79537a12886866bc87ed06a3985ccfb87ec7408c65e961c23bf44a29988c53a8233783f622e186e783f1a243be95441869d499d1c81ea83bd4661bc501e0533773a55687fc6f990c85bfb98a902c79deb7fdc6c7690aa11c16e096265f6c310a204e5d346f64bca7328f0d238ed8295a248a749aa836797d6105098d600c1d8ce8c61aee4c3b800f1d63b45e9b9cac96839463bf76e5d530ed5e4af459e47b2b271aca1457a712336c342b67c54b969f4f32194469629893ef05d90dfe535c68e9256b042f778c5bc973813dd5951caeb0cbbdc99482510f1156f2b6f68fae7367dcd8f00bee85a477ad019eaa450e216086bd4186692c74c3c31951bb77388746b9df04fac34d1f2f4d0b0f831660b345e5dd96b7b01f1936bcb3a115ec82b8a4f814c6d8ab5b5694f2a3e97adac0b9ba7dad44205819f23cbff4dbda1ee73b4b48989eefe3d99fc34bea6a2e5216c9d81f9cd133364b3166c6163a8fae682e468c5a6e008dff8b6fd048093eb73bac666c66e36a6758340e18be44c6b8d27e8bef60d3f1a98969eaf53ed495fbbeb30b4730d01d08547250b03ec98c35dfd2a1b12b480ca4e2a1fd978636c816b0ece3680fccd032d2108057fefeff547db559fd02b85a81b696ffe514c69f4b1d51c93be5a60adb4d00c3efba66a42092416aff6eae54c440b8c387845c4efc5c573f3cacbef13ec92a9949431b61f4e970b3a60caf1903b4e21a79be11b5e0381d2f6837957d5207bc006afab7f7746a5c2e09370aec354bd99d9feed39257ae2c95731066d97a492b35f00e71d5467a4517547c0a8713fab5ba0168b7494ea428f410553157bb42c1747ca391f21543adaec92f6abd8ead56975618af5884cb26f59f70d4d3e86083e2a7df400d6e2df4e4c0a604bf05d2b68f01ab5bbfd85a7d94d1aecfc737bb08bf623d3c948fc41f817998428170b70b333108dd70b62889e5a7525e844a769f7152668a922c9811d23e9c49d781d07d9f4e0d52cde232818bcba91682e825a23d0e355b7b9e25b752d850340b0ee93e849795fa507a6cdd0c51121c571ab9e3e7a01c8c9bc595375294081e8c850196b79e483e2c111497f56d95396fe98a3f1b94ded4deef3550c7f52f85bd0100064934163659e6455c2cf9881abad939a17d8eec9420d43a0cd3fbac44de5a0dbfab92d1782df44ba628b62d7efb7fc3a8ff4cdb12e3d94568bbcacc89ab2eb5c9185b7b323d18bfbb2c141eb6cb7dc2de9e48046f5888c514a0d2d024439bc2484f6e0088467e8f1ab8626b131874191deb032725005a4809b423e79667521eccedbb84a878de7db902043310b82899273b77bd3a679d5e4875003fd1a10079e03557da748385fc71394b1a6b75b49c24af653ce4e0b792ce2ebd1f6233ac2f9c9464a5e1ed58349db26fad721d049440851ae1dcf54b881b2061cce5a7c019817e130133b8dc3239917e33f3e11f210ec93fb1343f9e762de57ce96828f46fbe616b13618953f4185bcc2cf58b8c4e23f9d108c6eed0762072f7f44459743f00abb41f6afd72b89f2d3a505603c49ee1eef60f75a7242c75a25dd45bc9756d600c06f6c40c18d60dc8da109222798f76c42ac22a50033d70018015102aa2cd24897755d855ccd5dff8519a94377d807166c12877769b365cc811df100eaccb26efbe4c85d43a55d69855af6bfc05cdd95a7e3e768182b80321116c03237118dfbe9e62c6202a9588127a6122f0e0149ca584ccacbf08fdcd479c06c23cc7ab1d58979e19b225ebaa2fa0a48b595653dc3019d7f50f79cb2d1929727bcddccccb8ddf6764772376749a5aa6fbf67f03ef080404988f19c1e98d0df98e9caca369bbf83451a36413ce2e1baa42de128453c226b8aa206afe45c8ed86df5c76c77157e074030294e7ed01ee2ac5a8b56db866be11b3fdb909948d781aa2023b63f11db8a665aee08a42520a2b42fd301c46d19fc3cdf16eaa3b772a7306c50953ff698d13befba835eb203d017f0bb142beb756c157c4d6c3ff4d7284629bd2421a5cf73429021e9e9d49ed2dd622638542ed247c2f860978f027e24e9fc48376ae8ac4b3c70426a9f261e41c66b022bf4d77b792ea1a54cada6637fde0a5f271a2c54c7594b1325ab48b5e29247099a5280d4d2e1fc8f2a8997e09a1adabddcfdcb7bb74afd4b256f226ad910b217aa24be99ff8092c11c260e41d97930d95ef00902f44cfb3ce2f562b2b63af4d446be66229154399a5b5620a0aa00aa3736354d24b8528cb94d231c50a0f1e523e18269076ce4c9505a6b8216e64a0451a6fa017d308437fd9df903b140ae04afc623e46a416537cf4281ce15d019681a30e0d9a8896f6602a9e684f991a4dedee040231d339f9c5982f648664bb7a6d3c9d4d8110eb64688a797604463985d3eb0ccff3df020ff724baf38db8b4017bccac9c46e6d071dd2d94a98753dd51adcfce7f0f7b298a6a74cb5a312ae1c68aa12e1cdc5f238ddaaa17f02026730745416e6b1a2266630229d96888ffd8ba20d72703ff4d54cbf2f0ac043e5ab8bf80c2dba12d161883705f70a85d6dc56884db5d6b2e3c843b4316d050d203fb4f5bb77534d2bebb9a4a22a4fd134e2aa0bdf74c18f053fd70ab15fa46688658de0d149eaacad53b11fc41dc0f3294bff957b35fdf0083c359603cafaea218f68f32869f4706334b30a84baa934487b2ea8873af756127dc84dcb73d517b9fee42cfbf02a2e50c0ffd9c918f7cf535f358e024f30b9492882ebc9917651cf0781bed72613a12dfc6f341a1b92bd13aa1810f201d6cea7a12b77466065344b2222e05f299e4fe3403dd056058c1044042c8983ea2f5bc71a9683e87bb4843efdd211be672de0b0e3f9f6543d9d3500a495204a19481c1360ed5d18c9a3939e817766bd8bd82eb49e45991b94fddcab4b64d8345b240ce676646b901ce3f9d3634328651e7923e3390102fb4623e0b9cfcebbb168845ba486d7ba20907bcea61aaf33b687804043d6f19228680da41a870ca073d932003a7855f0e47d7300a07cb3d8116dd6d9f527240e25370a9d293e1c0c35c5b5b1984e74d52a6b978a52ff26be76779c95deeda7b7d85a32e8a002f1f2930fb1440f09db2a9a6c682750c82877816d7fb816b424e4e1dd85c682a102cc9dc081546be0a85d8a45084df47ae83bbd7c18a7801b3c850fe3597cf1ec009e07fd9a855005d5ba836afee06360e7dc5540e35decdc95800b14de880c2f43afe7d7bc2417146784386af23aa88082e1e0ec4bd7b77512dbd7aaaa8ba1dcb12f1f22704e10884d06947488809c5aa2763e28fa22884ffe0867113ce400a6867eda6c66a44c67924aca564fcc69bda50cb24bde662e530147b1b909a665739ee115d6ff5ccd33c95060267cc2294d281c8f250a19c89d183635e5c88e858fef1848853859a11d6f8af53decb689271d588c0152f55599376ebcd26ecf361cc6a92fdea2f8f61f703279b7219ff4138e043ef40329fc952a81c1bd9636f31045bb508ec2233a3aebe67bdaffc993313911cf350847d9f60d4b4e0e3e3b4926c5c2642528b4d8bd27718dd6430b13f8a4fc7df46829fd2653208d2a734b3f438d27c56a434c7e103f0cda010622a0b8d9dc606b8692a2fd013c1ef2ed9c1e9362a302e2157db2c450fe7b7e94291328ef40d6fca16f084c51e5a6304f4d2dfbcaf365758a22923da612b473465dd0a6831d414a248ee50da15a1aa374b343d0d2e2d826567cae6049e7d203f843773e772e1beef8b1d9a74291703f27735fb2b886e0926b3c6f3c8103a71fe81b7c89b844f7a6a03eaf4635b24d4191dbac3e1c81c5d4157763c84b0dfc0526dc69f5c862eae2cd8def4b3eaa47dc247a7f81a628cdc4715552c82e3c6d97d2b5c02e2b82b754bb39f4939c9cf42e913234c5ea32b668a94d29020f8c8c743dec802b2a432f846ad5e255c845f5c27d9891d92d99baffb75b45bb48ac579954266a28cea89631e14332f40cefde55efffafbc6b141addac954e83c332e52162d5eb1b24f2f92119fae1ce04db3a0e64b01b67ecca1b0b708fbddeb7d9a50e33dbd53fdeead5bcf404465ad2a5894bc636fa137e2413d43f3a190eb4086f9ad79657268bcfdcd75b22959f975e6067af8cf5acf927dd5fa7ac98b0217afd9fe05b9011e3b35c51c4a29348f693a4800408d8aac0f00b0932b9d835449865d7cafbf14877b28cc4f8295e93e17a945584c99a631b1032623db4fd84a09f9b2594c952f75a885824903ee6f370a2885e903ab9746854b27f79383056026820bac01e56cd3d15a4dac96f495833909021910ae2a48020d5cda588d62dd184b9f8928672315abf80ef7db1db747326d3518aa9408ee60eab5bbd39869157a35c109219bb8baf6385484e8e62aa02f0563cea51fd26f1b34b217badefcadf3073c31c1dea92982a79784239a49fd7e83ad25c851fc47d41a8e78fdb1b0affa4c3d6e85a82635a34e598ee8715cc17490a6c9bfed49513384f98833fd279e7d125715cf0f3c97fc8e1c2ccafda58a0474f00b878a0f8bd2e126613e094e1431506c0f1ab10f4ac9f8ff58a736304377fb61c2ed8f9a304004e2f047ca3e157b1b59ad0301b13a21fe464ac2c12c1939c003806c377a221d128c4fa5dd5d3ef19440377135bb0dce4498c483ecdd7d4d94e763151fb704062d46e4a8e19012203f5f8aa7f56149d27323a77c9f954f01c1fdaeef741805dda5c028433a72d2f0dd00458995cf1defeab0f52aad50ba1592bc2ca336c87812a9ddaec49c9db062a34188d5027e8d06a0f6e8a9ead38c1a34b643d6308c07d2355edb902925841bf7e5e03ed6be2db8636a609bfbc93690246d73ccb0eb2d6a4bb7d96afeaf5b343085c50ff7a21cdd0d0da20d08bac4f8cb8d5088ba8c361f60e6715886194a14f2b9ad79fc11357b4b928fefc71dc51f519bac500ee9e2cda1bf26d3d55af2f83764da0d01784eb82a5ded18a2646aae48f7ce184dcee9eaf0d058077b4b6f4492fde2219ee7e251555595232ff704e8bc65a15e6f5b1c4c6d9eb0527a21127ecf5007493aae7fe80c21f3c3e28a75db884e3a876001e0a9f897aafa3d70ef21b4014f55489c133e9a9b0f461624b5c3cad607e1d5906f0dace61989b86f5ede4b3ceb44fef6e7a3ca4c3ee4b4944f74201ca7246b53a6dea3741c2ab90195f114fd3128b9d26b710744400c97ac8d1a960928023056a78a2b0926bf225918967d1dc24fe77c44b5bc4119c1ba2fae008dd7cc233cfbc52d097f64dc4403da5921ada70d4e36863fd059be641290848b64dd2cd0244b330a2d52cb18905228a1ec4a4aec48b987b4bb3ec3c17ec4654496b36492011849eefde0daf40ca5c5e1db438ee547ef7687dc6c205b28965ad72cacd77286eb482ab86a3029ce3afd554603520d47215043baf9ec076fd53e29d1540079549fbb17a880635cb76dfa4b23f9256b88ef875c236cdfbe3d23633720b7f2238f064b78cd98c7e9364e112c8a40c7c5afe2eeccd9633bdde3acba94073839c8dffb463ced20fd4cf2015e91b3d541d6672e7bc09946fce06b22be5f8ee143d7373b2ea6243e14564b1e78ae2962a552a3332e54e69309f131bac5a26c95dce6ac01a3f84d3b412b69a87ea813b6d3fc55b1743b1b91120aa070765440b23bf7e453a6a4067670e2bd0a628ceccf2f1340c55c8dc0959a25b4301065df9b96be0befd84501b020679b6455c774474a71ffe8bb9dda481037e5f90354e09ecade1774eadf171a2e5895debe206da3069d7291ddde45587d172407ab893931fb19b8d18ed7f4af9242ad9bebfa30b7fcbc57d03091ff405dcacee28a8f8825fb60dda58fba19042a2020b12f67f45ef00636488a1fa3b3bb53fa562a0596838d6f7ca4204bfae4a773b88619ac94f1e59b2f0b14caf54959392a0e8931c5f8fbccb33d49d3764a65e982ead2a79e41961d220e621e6564931cbb43181a3a85a96c31652643b27a2823628d37b1fc0ef8f190c0c737d478139e549ecb98d0a6f8d488432fe7e779502dba46fc2e6fa5be6abf5ca0be099b508aac3f5bf8f0cca1cb33e11d446fb6fa3242e1d8c794efd8a09f0a8aa47bb005babdc472f7a6b3fe8e2c93fe33b14ab1a687b989b2ccdf8368e25c1a2826ebd6a4cc4b69357e6874a1463559d46d3c2c039bd598fd872f06d0e2da6337808dd3117a7df3c052a3a3c8b86683a2d1602d6cb33fe97005d63cf506b4ecfdb9aeae9848272f98fc3e8f3a9116028369dcf921e2b126f9390a2674d448a7da881a01ae08cb404609f6e228637cc6ff7fd9cfd5ab34b505060e110ba1be6e327d8b8c20efb34604abfd629e17d62838290b28f920516549549d716b529709b21f8ca5b618e22780621fad0b20efd71091232170f4c77918f558b381a0ec335675a524705a769b236eb535be04d8b268393a8b81787ecf2a10ca9a5d41f6519dc043296a1cf3f6f5d6030738eca0268911bce42bb86718cabf2999579ea9cfa0fb08d208d69888b226911b346b51da7709ab2bb5122bdce2508e720c8c0a952ab14c0b7e94c7462eb982306c6c69a4c990efb459b3700d399e09577749644c318e5b71c7bbcc7322086fe747210d49a67f695adc13cb1f881047da5bb03aa15f49f83eb8e83bc14d506a58b1703f268be028b6fa6bbeffdeab2a1f5cbcbba2e0d1a80033957db69ab370b38e70f5bbab1347d7bf2a5a44a3546aac590fd0432fe4d35727a02d114dec9ace0dbca7e15a042e118fd33957de2612ac80967c1094dc261f65f1c645fd9177cc324fd3896eaf4e92147bd502d6c55bd2375e66b775295b5661eb856652d6b054b246dc0a89d4bacd519405ae8dd1b62642f2c711571112f202be04841e72b5ae130304c91cd4813cd7ac5fdf608bbbcbe6762bdd63e4de1861b269e3d8097af085b538e3382eab95782f5a088c60538b6cb7e0746293b055992d09ac159514a6917879a510d9c5b70bfc477b5093d3ebe1c739995a8e8ce4e2efe257321e6035f8eb5863db0ad571c39f048f977733f83a23389b6ca94f1f9e74bd47a593c5f1fc5d27418659dea9046af87b1735c8a3a04c2d6908446af3cb551447f30e2a9de29b063a781f0f1020ffe66637e6704d56f87f501091cc244b230e2778a09244ce0228d4c409a038ce39b15b0d0d2177cd7902accfdb24dda3c0871bd4da87a48ec3ba48a9b19777cc3d7db4cf136b3a84c999a131fcb0a4cce3142ccffc6a71c81ec0826bdc04b44d21aa707e4105a1f2444c5c884482a8f77f264b6dd5ee20136e0a1e98769fef1f9c70bd92ff1c0dcdae0181e1a169d37ec0e02482f6bca60bfba193622cdd1839c19322f0eb843a8ddde102a071adff04e5ad8b843b3cacb1aaace723f4e483ca283c411fe85b60ac1e72ba8662b17d9b312108675fc3d2ce29c8a3648b6e990bb4e08f0b52b33bf0287e6f04dad9e55490e2df712aa7110107f14cdacc2798dd5c9d17a1971817f63b977d792c9b09cd486fb0cd1d190ee880b540c062232301c6bfdc720ff3e61c24ea8f5db51eadc5cdb5bacce8b6f4296615df03a9f298d94d89828d224640257954042fd10f7c5c1b8ffe1802a13f4cae88b8951689677312934b40b65406d8469ca0b74438b619db0271b43efbb7ea0d90dd42c8f53bb37216fd08fc043863ae5392ade1e96feade194a75d0330d0e9be91916ad7599b0515cd165061d7b4d1f3a7e5b4dab5bfea125786f4e98cd6e0826e0e17b5978a43408060387d8829bb99835ffb015fc32c57f92d14cee5e3148d5b0ee2acbc8b2d4e31af1b40d042dd6d8b0b5b43a74cd319befc65db60e33d0bdd3369de9a558b06d76496a6d0c12cc7704626a0557430089b98fcff721f23c326f68b988083af6f390f72c13bd02b12f9a1cfc210a92761a6f7525aa80976a5b7d4b569056e40b38d844172c29eb54f5fec6eb594e0661345b59d0aa5e401ce74d480d7fee2327d529c9eb7275fa8515a038408d624e3b7aba150c449200c3d2aa19ddaf31a52312515c8ff2c08f2fcbd717543d393c580348c44d8df39b94e9253cce69fe08c28a4b0d273dffc2604e89458ca8920efe03d542e00b3ea3f8cf51dae30c6a4107b876a2791bdde5be58febd9ad6d9a880f5cdd742327effb16126a5742b0a200a2c7d340dd34601780070a26a3bc148c9ad1bd4c763c8d64ec07bc9c3c10401245bfec1ffcf585d7ed0788e4c021c15152845d8e7d754b6fe6be07ef6b6fb0c1ba634bb2fd445e612f28174ebf7f3a1c0e9324fb95eb8daef004149cd0318e7658736f2f060d5d9b3e34369d496af6769401cce11d4f211c8d37d3ac4640a6b400945d774b985a5d7515661a0ba26d28e16d0a5cd3cf38ddf5146968cd264a09027c233bb5f8984f8154d7283e3e0e6207b9ad05fd0fe0d2d2a8162a70171d8170ac69e313c0f55358556cef5c2c902f4d764b28773824fffb8c397821f3c73a590475a1d2a915246d63db90c7e7d70df5f291af46c43d3ed22ca99c8aef268e31524b8a19b098c10f9b5a798cb77292546c3fc703fc1101eac74efcf81fe4cbb3ff4311ea62043102076da305ef7a0be91afd8af8f420e0110dec8bbe1642aa78f93933dc3c3e77752fcb823c80dbc2c6aaadfb2398fabd8b0f24caa16f1cf5bff46b7519ccffb3488b5f91c95b56f50ce04c213feb3eb65dfa35aeafaa1e5a96c5b5ad2847bbba24ba8c7383637d86773333066d08863ef3112cb62e99f93a99691f740ec2287dbe90f88fa0dfcfc3fb6bd238c84d13179a4a39e90e7a809307d02786867125095972ac1939a3de1b17f3c6284f7b6019d5cf4c2febc8d260a5c3e8ed210205812c982fa2dc5ccaaafa67f06ce6e6085cf9bb1c9dccb52c941157d10ea0263c6d8e77a8e70015084ec6ebf3f614ccbe359e4c1524b48e19762916b6244e564554c1d7cec947e0bfc88fdb41f27102989c6783819f9e1e1697ee4ec422eaf727f8c77554e19b615b7cbb71f2cffa8ba16fb91d993030bc3e6e948133faad48788369a9858663ace6a334e8ac970904be025529312874a6582d7617ef55ad6dff120f0e6bbe90c34b84964009e146df0716fd64a21d524c72c918c7d71df5a6c7e405b24eb61869a3b8ff4fd289c9185bba0cdc62196249ee90fb95d2cea863a34968cd59dbd3289588824ecf7364bf19d400e50410f1b6f326d4403f1eb72da43744384f412c8e5e2ec9009fbad8109e0494e73fa03e1d611f11e54bdd0e7043ca9318166c7b1b5c4f6e3509baf9bc40f10297599e4fe53b98dc8acf2f49d2d2ffdd6510963a29600a4e22c329240cc130c952c0a92fb769130d64f403bad4b90a65a72a2f87fa247c969cd45b42cec86424cc5f3d1584b030f0415cbceba8e513bbbed3d9e07af6475671dd348273f4582306a18feee53db3cc3b019c232c0b66edf4bc7d02be2777577e2f9e11028ac24c76b5c3249589f68b80084ec363195c0420e34009feb088594f6d20a9bce2722380c84f55ca7b6e02222736a8a8134298e7cdc15c91bc2d641fdd7b209b0c2217bf593dafa39196c87b5d0e8a8918cf26fb50b9837e7912f350e427f739b7df9470f693f83367c837375898312d2efb80b4b04623db9429671e835f3ff9b691c569d80620ba0392c536ea075932effe58daa647b809804e358e582d8f82700b667781eca6780a421d9d21480ef6d3b2f9ddc5935bf64da404babddf985afbb5fa59362855cc9f8e35e4b5a6a8ff4bc3335ed2ea76b7149258513ad6dccc5b7cf11a6e0367e595bef8e24b1dfb5aa75816da4b7df29472f08479c02ab19e066b31935ce121152ac9d729b9d585ea05d2ce8b159c54c05d922bf4c5bcd82b491d103bc3cdaabdd42ab98bfe29543b0b13140b43094f6b7a6ba8d558c9e474f18650379e9e79535186e5d0643abcd7075f5340b996d98f573ca22866edff3cb95960eefa6ad06924082133c1f1d80b0ec7a7f03130c5f29db2583f99ff1deb84d912047d59a3acb532a609c05c226ba3d13f9d463087be0a6183b198fa713e76c217e3a3b30b6a3f95dfdefd05d7b0f029e3abd618012015b7da247896a413856dc95ac12e1d68c14b28a9d1db040fa19e69ed3ab5c3a57e062575d1e7810c9c4751573601363682344ad2d9f99ff302e89af92f5b579ccc5829f882d2debe6d7cfd031204c7599928a01aa5fdf484ee69237f3d78768cbbc449eb1aec8bf698858c53e9245a8f6a410ba2f2ea5fb74a3b2d1ad8eb05d913fbb3a7c7c0398e392f705d79048eca7e68bce27d4092276776cf7fa0e757adc5f46b89d82f6e70a8f71c9e1cc57e76d1a92d4d8ab4b2067f638093c1478a10ef8c1ffb2ec75d49744ffd6ece20a802027e4050da8ca0450f0d4ba26fc7805e0e9fe78403592f0245e6e9e912ae983ef937945ad9e3f707358cbba86f4894353c5ccdb244f83f0c185af88b76cbc298136b7e4b32042ee278aea4e543c79fabf244fe100105b1c8b39fd60cdfd02b16f3a47475ba8be39fff9ec1708ca57fd81989550783c46fd370ffb5681893c68470964beeb8cefe81d80771ff4db252634856d6ea945905923e8f50fc900f70f6786b215f8293b84d95207ba02bfccccec13cd1c7bc184c99c54079aa9ef445e70dcea6d160fd41f2642ac6a92adfe9c40a01468f92189a47a53d0fc2085a106315495a34b09e92aabb650a48bab7b6883873197a4b5e1aedc0a966296b048a1b74312367d29eccfb156b2764b777fcd40f010615a0aee394b65e0bb4fd85b6b49e659788346bee599579e686e9c58e53393210ba610f66a9056aa430ad3384aa5d593b3bf3598d912f8fa6dd5f844903958f5add4261e6753074650b5a14170a87a838d5f966ca27c29c7f4b55fe7466744dd1724daf9219507d088ad31a0dd31adbbea6d4912fa1085dabdff1fe0244880413b8426e474b1e219cce5e5600de4013ee54d1497c7d5acd018c129739093bf11344b13e9db72ca4425d1d3779efd8a094c4c1a310a6f3020b3a6d119dd7d8a110067d30d0b33248f7b6f2291c14e9faa8065170422b32838090886a913c8f5d30f76aef6c4b4071c612d158aab5ba16181fb5c41fbce29fa679def7794de1e26b3201b36c73e80c56317dd8b6d150f628642ddf8c0e1fa21a8cf95b893ae86c4283a11ac4d9d94d90a36de22e97ccbe32ababe04f7b2ae1adfab42815b5c33b2c1a4ffd9d159fdc7323c298e53076048d52ef22395d890e375083045276e433169c3674664b4cb2b86c3bb0414397f23e6f8817ef84c773109ff4cdd3534d16a6b1cdc576a4a1929c4ab7cb5a391395ca60c69498eee965c54caf5787c65981db86891d178dba6ca184682292c043d4fb44ab21259c68812f52ec514a39fd7bc933ceb9a4bb7067c05861a58b68c9781de603d4653fd065bffb0ff2ec96b053f725ff5d4f7bea2bfe4bf124c34a43b9214ae589cc7ca88c5195e7c51eac7ac76d24388966a702994bcc6bcc1b0b052521444e0e455f4fadf2742945529c79cc78b724d5cec788ddd15e11f7179a260a3ac0abee3324446ce6e2e4854d413a25b295af81406ec4e6a458bf19bfe74ecbf971ce1f9dcbf70acc9b803bc1ea30251eb05efa9c38df30851d767e679bcb7c64e185fb79196eb9dd4151114fec59df37a0b6bce9a6b6ce9a0aa37a19e8ed2167efc4df6440e3e5681a8797090d250ce2792294be4693e3b8163334857e7cf0d4c72014cc6c4ad16ee40a32969dc0ea8a317f21145abf1c4383fc1bdb293b62d816fd0b163d90664ca8303ce14e1dfc31452527f00cad8ee1e662eb37ffc57252fc619ae1b0aff566ea49e05bb82416fc4dcf4a4e6e0cfaa1d712aeeb8521ef730871db57f770cdc4a2d7ebade0440aacf4e2844c3f5cd2893d4820b0f24e3c50d2766943bec8e3170f028c7c637c323d1b52e365a2e32ee563eb4f2e20b01d1a865da4c875ca8bc0c4cd4cea84abd365fa67934218bd1d44f40e681d5f55b903d1ab8e2e40c6e9104a409acbf2e02f28ab60bf391154c612278d0c7cde133b0bae0a9174659993243dc382325a30f7e4c0ae6ff3dbefbf27a9075adfa9dbc09610dd851bd6d5958ad70e148058b7bd76443f948234b685d75f97a9be2ea11ab6f01b819e2507a0ee9862a85a694bdc2af95be8436ec62cef72ad3bafb341f85e6585648421f99fde9018771dc13cc9f71c2531ec0525098317b00658b54d725c1a43c6b2e8684b31892ce566091d0fdcccc50f0993fba2c00fc90c5af2696eca80fd32f05e6ef2e7c4ccb0a86880aca9098d19026668d16a7421fe444bade76339739105c74b71539394d02566d538206872c33e9064725b8969a212487ca963ec029a7b1058c5847089ccccc1e1c50c1a68c9c18973405391062d4eb713e26c56777528debe8500986149b243383b4c2c1f0b6128a1169bb0375e71f7de51b637a9213c670312befee9f4c51584ff0c29903b673fdc00baa9d877374e3546ad001c43a111643345efe404f4a3d690d833da1ed564cfc3dba3ae6e2be00ebf36a4401012b447236ca4771a9296daa368547c3d0a1649e3a3b64382b1d19cf2d4885071a63eaad4c15896a189f516bde265fab55a2b763f0a5a6394715058c11c9a603ef6b9d7e8f9b3ecf928e77e147812f051803133d1fe93af97e033fd5ef3a0eee45901acfa64c8883741481cbfb7ace30b8c70f9f38b4ffe28a7f9e3f1717d518432bda2524c422ff76ec885bdde848566bdc54c669c5f6c78851e5959ab3f31affb854da78ea320770a04d8368af42fa2e3bfd5154596cd0157c76af2eaa0fd9356fec3091f665c087f42b0df9fec75a959a8f8a7053cd8d342115d373a4e63f071321784680a1e6bb09b220a8fbd3a4d6d9d87251bcb32deb9961ff1742ac640521c1af81d7d8822e3415e2088a24dfa67e0f2fb92b10ac47d6973c7fccb592d41770b05a3b410aa632450e6be561fb5834fd7902694565ac4961febc8f57e221b6b6fb5539dd91ea3cb011d695365994380f2e539dc771f56cc17ab5cffb6f92a6abfc410feab997175010e3d2addf4a89e13e801827ccb4e3f42475ec4ac4995a19ebd6ab016c8c6169a331a2ae3090ca2d211a16fdc8ad3ff8feb8491ecb952111064a04b992aa356e7c83dc8766aeed7a9aa87443b838e3086f012867bdc862976b752220382ccf9d3ff777e33be0c359fd1b39d9336f7d08e339994c4349657ab303686e34dc082c70fd2fbeebe99b10f6cc4f689c10b8e13219dc144fe4dd14a4ce23baf3d19f0c13548e987e105653bb1ee779730816af83039c5af49b296767e5e97a7a06824e3ea6f76fef834b2103fac8b7f6853b48d1bd7f8f6d73a073e0ebd224d9fafa2b698f41cc4b018ef2c806500399ce1b5a4fca281aa7be940a819b19cfa6c21c266fcf2e44ba9f651cb76e7e83300b90a3d360613533a8f525752b78503201695d0c285973337694a5bafd748ca0eca225d7ffdaed0a4ee3df1fae90234f5505244bf368c34508d9723a4b8fa96ba8bf1f1ea5c69a5f15ff75d0f5ae24dcdb0bb0b7d0fb024570041f822e01065cd5a7c15420b83b37f021fb3017919e2677fcd566767889ab0d01b8a24778e9911e9a24040d5a602181a37dfa23152a7e3799aa34f8e2906ddfc15e60f7ec707e3040d91f3a0d450afe8773bc59ed61e4e91d25242c711279c67ecec7d9a645233826e40d7d5ba8dbb43d0909544fac6770adea8008fd0acdcea6e54d0ede2f964361dd8f7f5a9ef4eead1c1a5160c891595a4f0d3565151ccc0a43be51f4440a313859b75d72c285e0d4b3212ccf52ef0c5b974efc1b6acf3910d742e34440dde5077fba0f901825766ef5e75108bb6209c10c22f02aac08daf68d4856aae4f7aea24c6a7ffa9a18e1e3fada5a369ba64be505b075f9dec3ef0fe0fd4cd1d6e6c4dde5a99aaa01d23633162caa75da35f17b00904f1bdb78930692819a6ab183bc7509f442c5d070eacf9ea4056c43efb33896344dd9358e28d89383d39499735c606b9ee5e6a7a5d475ea9d9bf2047e8589757705c9a1c640f22b01aa3da8deef77733a1cb6c3c233ee1b59d6bd3b0a8f9ef8e0393b480ed5d4207735c4e3d7f781b0daac1816306a916ac9feb500264053a5e099960d575cc82c64c9c89bc8653cc7b5af9146b9ce3af58f6af33ce220860fd0a6246ca3cd56120d84fdcfa8b60bfead927f1bfdf5df00f1a8d7aa2863d89bbd9d631ae0352d1a31acd01d18d0291f6d740ac455857e850f9b15f416cea59e527bd7a28ac8313347e53ba3f620b1ccab77c9bc04547f6960ce6b43c1c3160e7203d627f99810a371ca00806f95250f63d378191e190abfdd0f912f41ce8fd59b30f828f9c924477be2ab5555e5e0799ccfd3b4d74cbf4e0a68114994fae4d0190052ae81f6913382c1bda6bfe6cee105426e8c465dbc5119e078aee86c22090ca04401a9b81a717f683a9e63eee92527fc4db675522360465c5a40cd1443ff3d7fc0ed07a2b56f220ee51b6ec962d5caff238e47605caa71cffc3efea7d275e70d613ceb4b304002b8e6b4c137718b284c8c4ac9bbb1c9d3f43917f22d08a3dab50213c14d718497359b286d0deab066c4d373c4b8cafa3f0e28bb80551bfcb021dd1323a6b9d6c71cde601a10f9ee9afa64091c9aefae8a961c0e79a9f5bab2b95be5a075bae926ba33244ec887658d09470d0ae4b795784a497b78d477fc20394e35ccc0ba04251787c683d08fe7423d8505653dfb1d66098f5d844fc3fdc4c8a3965741c13b8409bec829c610880d2de21bf3a00149217cd9b0154d7b8b57c68817a56bb73b8e4708387083be92a9af46ddc8a66aa733e61fbc4d1946d575b83cb8c038721189d2946673653006c0c6d28298ce92ddf23ad4a0a300ea052adad7f02843e17281c6ee8e112a56b4fe0cb6ad39f83f1a6c36bd060689c05d80cb40daac9fff90c504d19b2b8ed6000c647e21717dc4ae765d697e6470666d1a4546299e43874554adfd3efe89a00c4d4bcc76aade84d590a8e5aa626c0e5c21fefddc8bd1f8e556e9c76376716286362ae2e4e4634e012f6a2787d0d435fcc9cd56d7fd3f117fdc046d007b7c3ee6fbe5b7474a4ab797f34c7739f01a0571e66fef3891c77bdf1ee67adb6639a3c5bc8840ee83186dfa04b82673c4bda1ae69917f17d3a98183078080b9ce221b408726df0b5a0000af55301f897ec072a11a057263ac35c741051ccc108488ac6baea7d3d9d132f5b4a018a7f7bb71e7edc23859590e256388b0cbac28919981aa00a0a55bb1db3d4e02f3a921153a3d0db7338cc3fbff94ed85f9f04a6a926df267893a968c2207940831f7cfea054bd26ed64e64da88c07ef455b68381d9dfcefc59f1ddfe8bb7f952288c48e68e5d4811af6a8ba88ca3c83f476bccbccd0aee4510a9aab13de8aa5524006dfbb7f6a067efbd927a6ec566a3f7659fed19f33ca26526c94e5b88aee4bf9eb18fadeab51c01f82657d7908a97d8022916feec8c394e5ee9cb103da773f218bcaeee0bc704aac385b45530961b6557bcfc27bc0260675f73e15b96051c05233abd97c2f97a84eba0f117b404b6ee3bb5a333a233c0550e2ada9094d99fb00cdd740aaa543f94c5a85057842d2f22f0f3395b117abcb96e4182720e02a247c199e6a2c2094617613d378a473c567a84b0ef3f555bfca2620a821f06e243f447a1e205db764ce29b18a6102f3890c064ed3480042c4ea0bcfee8046fdef0a1219586657584b59d87db9feeb8a9ba39aecd0ec8aaf1abfba466272620f648c6eeb147d2311fda6f8a4e18270249369f270a4799000b1005d8958ab7fb4722d874105d0a3d797094d1f57a81a80624514db3012d1eb157a56764217a3ccbf2b91217b6c2181b0826f4b0775080fb50bd64091d8daeeed24ec284b48842b67883d3c8872334ca6d5a650986e1253ad6f2f70214db39fb14c55ebd7f67781cb58c388dd3300bd87f0bf4eafa70b9fc97d6de033f577d31a4710d2cd997af7cfa33cdacdf9e2a9c532003fdded5a790709ac2144d462161a1c7a03cc7c4576e16157cd864063570eb68f47938440d1b68053ffc689c890a7402d87616715648251aab294a69515898125e84ac3bb841a8ed0fec23b94304b7e3e2aba3dedbe7a1bc5054ba9be3dc0520dbe3f71caedeae023e2bc5446cce18a21fb42cbd322199deb982c726e4e99bc720ab414b3388a489b060207239975df86d1b7f43cf9a376a56703c75d6b11ec3c217e0695ce97ffe5259d31621fb6d490b69767029f5e695268d175a2a32e4025d2296758fd6ed4360b72ded173a4ce9861a3ecdde86f85ecd7b04a0baea9a841f85d144297cf0919f132473efa1b4372ab6ea61aac046b70e7bfb0142e33b4817f5a55cf63da55762f0822dfbac5eb090abf5b246441096ecfe21297dcceed6c23f86f8ce698b846854f28448cd0a26b6ba066e504985f447b1d2239d4619834239536d4c514b9d2799a8602d52aa0f09cb554c688c73df83bc5b53c904eee18fb4ad45c64171ca6ae2148930e742808d2cc22f108d4480bccb972bfcdcbb8203d026c00e46bf12ea9dc82298f36810c84ef7c130aa8749d84e857dca096413651ab60095525f32f957e8eef9f3301327cd0bef2c20ec7557116790870564adb48653c5d8883191947648a5308f1240b0376980ae390d7b5fac2097018231ac0699104b371f9b4545daacada7e457aacc57cc35b263bb38e1905fc6952fcfdc9b35c3a67bc04b12995709b34d061f161f380927a3bc66583e4b5e9650a1c286d23a95d9f0394d244715d613d1a7888c1050ea0cc06d780b2fd1f922aa3951512a93b26a811542257c228216cd6546ddb988f8dcf9e9ed5e49e6dd49227e6b0c8aad28fb4babfb28e5a68be17ab7b31dce24e8f898f32b22732832b1d0db6a22da6a24b0ab36597681a3c7aa038695b4b2505f100a44fba77c8aaec84344cf10c1ac1352352aaa21f99c2a5536b4b200c6a5ee0ece5738e4292b884741ac8c9301d32d1b1af4b4594294a624e5b659906b09d9ac279e9047f866ddcbcb98268677e9d19aefeb2a865bca9f915ae9c231b8a65af5ec78ea78f95b94d2eca456c033bd82a49f49beb1f61c6e5adf541867dd374758c65377e0050c760a67b561dd2c6cde541878c8250b453d40108439c7c4007776b0bcf4296596c821ed44fa750d9947509b8b849ebddb900c5d9ac1621745ad959933f891523bfdd4a03e3c2facd8f7ee2a2a173ebef761429d3802497e4faae632c870ac6c9669da139b6c8cae71656f073da5411e1b2a76c8da261ac7c1d99d14201d5c872b5a2e18362dbb2603001eb305949e24a2d05347a83cf986df90eb8cfe0b7c372016f6de3b452bef950ec609aba810807497f4b6db517458e1678dc85d93dbec7203c1a918a36d1c861042577f3c10491dd488d7e8bb112b0d4880c08ab2d04b47e8000d13259d48092bffe8817148bed613e6e58190dc1742f0f11eeabc2d2dc9262c5e8b66992d1045755a009f80c03aa5d466c5b83594ed5cb04a03d82fa98557c9c3d36f7c98e08a727ee48731bbe43b3863f860f8046647f6db88c0b0be971df18eb6ad08bf6a103ea03ed0c3b2ecd62d2c0508a680c8e783c13dd499d768725c7816e35e0f8d06e962958cad8041f67d88a1c4a3fa13725aad14781bacffd2ccff5b3a4cd7927a11ef9647a0ea727e2a62f69d341adc9d34ef3f110124a80b9c0d198019a9a48518a018d12c1c309b40c209841b7263d067799faf9608c3784d0d1c970cc15186311213ac40e4c84c6e182eb51569f3cc7170e9d2aa3542374962d27bc740849b7d75eec52a7e1f3208d4396ecff45f11db378cca4139d36d2162c089e33c832bf86d76421b244bcb18968305c52a0dd8877d765cf95f95b094400eb6086389b3bf964d94292f09bc64c2b3c8fbd2e2ad97c4e9bc0ff08c3e78257009fac80103de544e55f36a9441bd27d735ac73eb4ef895321ce8661d561c9cbd1640b0380b1606404b72530c809ea89363489cb4ffb930bf2291e7a084ee7066f6b832a157ec56f18c26f822aaf38a1a286c433af8f88ca5c09e9b83bf0183b708692d55d34bc5a8865e251eab1b04ae4859e788ea61d36d7e232bceda03293e7e47e7659180c5b443a8e053cd698735b461d96bc91d9ba7eea37f7838b715137e144ddcbf23070ccdacef8289260d5e47a82b22a32974b0133381048c3efa249a3d19d49d2c5e56d421afacfb2688222601a8fb84456ed93ac07028d893c6f628afdcda9ccaca7746b81c04bcadb5fd58158f53e6f130f80f6cf094547305318f6c1f8b8475f2f7af773229698771c3b01794cc5198340b3130c5e41deaee2a3a3d4d5e8553e265828669cd869c10082a9832f00424631875700fe8b3bf830f04453bde1f6e49dbb1b665b19fb62ccb327a21f05975caa01e09e63b7b937007499d126a888d4008e700bebd751f2615ed574e8fddf849b9aba1d02cab812f9106546e3ec950af3bb5bb8f21aa5d72f7c232dac4df0cbd1acdb99147390e19b8a148bded0c51e52050e853bbd5ff07c53aefdeef8278fda0670d722ee29d137c600046be5686aced5035c32a1c4626383c8ba5ea85af50741ac6a1e91e9d875097f83ec971403215a2b6d2b1f73d07973273af966f6b508dfc12b47d36fcf644940a40635402e0bd257bec83e8579ac95290bf536dc6477ae163a865340daed0d7b5e9cc1efc65e9fd0af1a18ae27a2e6eb8eeec4506fe0e6f56ceee403557e5e0a9e56aae004eac9d23aba65f67f9d3e2be5afaff634b2e195fc62d7e4e5e4003a1ab16bf075024097bbd8aff99d0aa3f78fa1f58c38b1b4fae1b982d20b04a509fdb191330ca02bac4da9c381e452b71edeea61d8953849fefb937b727b850a4d52cf5145f6f9c33ff107cc4f9ca775ed2cf02a56981919ecd8c48b5f2d8af3b04ad38878e9962ef0c0e226bf241d3fcc2e2d36c487ff6d29a8263819ae7066ed29aab8b2219d1c09965d5654036d59e1d14d5e95d691983bf6f5e825382cdbbe23689b98ac6bb02e36e88c6273b18e1b5db91561b5c6f43684bea00c620ceef8c4f059267dd62eff236d43effa562f1cf9dfe6ed70aba918e9dc9f6e6459817ed2fa36d4048857ebff596ae339410427bd5c18e376942375d413127f43f74afb1669166e7350b65139e0d0ee3c9a5d91189400e193787490dd257cb9c8285e01a4145bd6df026f4f7e8faa6d0920de89da199a6858af7091be71345dfcb6e3067831560f6e9d6f210448637a046d4aaa7092a09afcc440580fb66803990b511211088bf26584ad6354e0ace8b60835eb58882dc78fc1c5d6753feaa23fb2ebb2cb53c34d042abbeebe748898fb3365d836e1c5195d5a32a8f7668e8c110232fe081fbce6e343bfd114a406b380c904c1ec05866d738dbea29a959b1b6adc9cf580b17100fe4d5f7fab7bbdb37eb95506f026d806c19c9e03b4c852126547e009132809a1917063568ecbacd750d8ddc488c75843b773c48497c22274c290260a50de1328e889458af15e521058c8f4f4c456ba64ee0f9e50ff910cbedba0df7858af009bed4a5d25b1627cec6ff4bf0e20d1b58fb711536d23670174d725ca0143936378cccaa516b58c59fa8311ff9a0d83694df1110f66c6949d5da0e57d000c7caa740e94dac7a04259d837c67c91cdf3c067119276c6d2a559184567f61f8e2d633bab659549d982d08d4e1aeda91f38fe610c61c0f89b3c6ef7b02d03bfbaa89b23f475fc5739f4a3ac3da3a876d731debef1b5b1f1b06f6c7d36a547b97854a65e78c616abe672519d34e2013219a3f52c96d609de3e424a66964fbd8c7702e12a38c19080a05d971dec52bad74ce51d3f8180104c9a353f66c2cb59d50a5c9f91f36900d8a834bd4f6a55ea60f738d5583239c676e57ce51d24de97ae4fdce94351b2c04a9daa4cd434706bbb10d75bd0acca207c8fed18adfd29cc845756d2305ffd91289a25a5e9b58b0497387d207fa71800092cd73c2c5ffb57c53efa7d8bb0407abd132300f44d445caea5cd328af5cfb9044143ae0d66ef9f9e36168fdc4f83c7fdfe00a338c080b043f492284c459f9ec35d8d8617d9af5e90173eb68f4c0daa48cdcde2522464063dac72d403adcc0bd3bf1150b382aa7c7cd3320e5d09ca00805774be3a281cfe87e757255f0f7addcfcac08098f1a0ab87d409a7f1529b30acd28526475d426c788826542b26c844ecdd80bed260111af3637a035ca5b1a6af3c3f37976f347ed6d862b18d7317058360358ffb8ac0afee326a9d2b83bb0a3a648b95832fabaaba5ff3a3951bfc28dde8e06fece95891612656e9ab3e63ca9b4e6740802bb8448f3d6a4c2d30e3e4d9d98e238957f3103eca5b25bcc072d778f3dbf3865bb90de040f59a47097aaa53a77798a42c41f1df93228b5c9a874ec9e495b08249463712e6e2d55e6a205d75efdca59a2adacb03c39583474fcb92ab9a86ab23ecd38b48e58442a8f22a56d1e1fe3dba046faf01b7325262a116b053bef056cdd93dec93ccc90a437785a04b07e241b6ba2cbd99a0e44e85e6d5554a7a98ae5bbac1d68eb075283a164755296d4d3a394336481c1849684daeaa63f3dd7449e0b9e994629a3644e86bd5319d38deb5744d049ad14b4b05802a616ba685b36d448b0a06df1e40c62e266f6eae709092f419a30fd29e5580e55e0a187c0c97980bc0a00159a9d3fe0be2df0527ed10b10b00d7822d5dc959b096632da5f3c33c23ce8da2e9e9379e1635546616e5beb12525facaec7428263e3f6aac6415c3657b2a22e435d6c8cfc82d556f00711424602a6e8744c0eb6b1eb6c496d0a2b1702b2f009910c382c75f8905cd8514a078fd019250be4511815aefdcf3f00a3b6f2989e3976456dd278760013b3094870c6029bdc5c80abde209950b30ee9f68bb903e24cf4faf4f9935be8e1e243922be9c19bf9eb01f96314ff1d3ce634fee6402ddb78fc305d179801008e55f40f84edc761815b83e17803ea6414f5126c39156e1ca879c0423c7cada0e2b9053fbc22e44e1e1e8ce479b862a8bc7d386c8cb0268201f0172fa18988c8bb584bda9b7dd376d27835e4e08a3cee1ec6e05c76733816a0883f839126f6da0c0efc8ceb8712f503cb94de26161ebb26909ebe53640ca12c2b00a27957949f83b1765d3e5e55c42fd47e1e833e6a0f75d08dce7c4269ccf492a44149543088f230bdd42184accb41e54a99c2a7d8fa78c5fe60af7321ac91c42cc04aebe88ccff7f776069bdd6e3528916b6ccf84ac11167388525894e703938e808f7ef1a07efc0a498b8306303232656453b10b2c644afdd08ee1e0650b4fdc3a5e2633effdb1321ff7a2ec1d87d39ba5568f83486ee56dd8685947f94ce141e51c946d07f57d3a0dda7a17779d4c79935ab4117d9c0e9c82cd48b538c95586f0d0ff8b9f49582c7037d6097210976a444db5b371679a565042f68899d0ebb155b635f0de70b47391385ff06ce0792d12dfe8faa8315d25bb529687969bae88fcf859bb0482745203eff954aa3e8da58099d0f221735ec4cdb40b69a3b169499f40832849b13c6bbb09326b4383144683876dff5d4c732a9d15f0c82a4ac4121950e63ca2db25e2630c8eba40f3948caad2ec72eacc1ff387379a107ff9080ffb94875acf4d2bd3ae6ddc2ee0ab0a6f3baab926cba32b6e96958d5da607f37be3356ae2452476728210fd236048ae56c49ca1b1b0c196eee51cbc44780ab4390421a8aba0841c5f6586a8a8f62c833d0000b74e60cb750cd2c34d49ecab63788d8c76e924e5699bf19385247a90b697a56d28a9cc1a6692429a5d05f0f005bf94f16bfbafd5660f13e64f16f42233124d4a91cf76ff5f3140f898002b1bd0a8dce07fd4a6685b8e92175115b47c821666d48da24a4fec1d85e38b812f439e14d920501ad01b888fb578db6660767c4b608cb3325f0adc6a70d7c9bf916246f469c1f7e04933a8be81dd404acf128116641a6c89ba66ac996c09c48bff8e03d496433bb7afcfee7732274df423f7617add25c66df2eabfa363946a2278be363a23911d30cd90ddcadf9f4a00b48449eccf297c210e1582f1b16a2648305be7035237006be9fd04ce67317a28436203994cf21d09033dd436ee3ca12fa9d138e265b4d9c6ca58cc826dca12c13ffe82ad2effd4792e8f61ebf7f2c5b3dd436411ddbc1437a6f52be39cb14dfa00a1fe4b26354101eb1f63d8ac535bc0618023c596b32c090d8161338706870d001f4775f1e981030043b00ccc7f7e2054434ab139f27d403e5c9b3f0ad7e47ed4d3c260776aac26af09a76bce402a13e98f9a607f5a4c96615d8c2ce4d210423cacdf16b380cb10d873266b1911f1c5e23c5942dbcbd430b8454eb4db53f09edc94048552834d1d00321c1199da2c0de88d57d58ee9888e8cab1f281a425f1b0c662eec65d434207c33d07be19d57f6d5f5291188e76afd388341b14114a20117df276c18f8e4744417ad82ac29dc8166390598786bbd8a7f813f7f80b4504c143291be28c5a965fa01ddc389127e7f4943382f9001ca8d0a95ba48ce18ec01ab7ca3f02a953ab4da683706143a76a37e96fcffab537f37fe91276e28ef22ebf7d9ad36d2344d03165b2a0c93c005642e0693b107589b7e1c97537b6aec2dfa785924f99874c1312a91012b974ca9ac63d50b1dbe1b0c703eac964973ece42ee07efc9ed67fd004be60801d549ce1dabb081f1aa5176ee00e748753cb253170c37bdd4b22d705cb7022a06ae695b301612710e1923f4a663b3c8eaa737b373b371921373c9d69e7b54e0ae379bbac8f030643eb67c5b0068e32278681b0314633b6f79f81a38cc2fce9b2c0cc5e26115c96b39605d112b9140b5d55fe0bfa8acfdee6ea14d649252069b0663065d06e326daadfba25761b2163896f91bcb1cc6b6562bb6c562a15d1db4eb4d1909413776614a9cf9d90e400104a5c0d660e975b95c9b0b0673c15c34970d3430178c862324f11086a2835380a5f88b2ae31b88054b392a40d6ac46dbf6b99bbbaa772644162486b32f0764775bd06eee64b1ccb9b823b65a5872ae24775b71bfcd68b59e0f78fa3575c13304ddf5895d7990d8627f2230b42be84b900ad5357df9ed1252e9f4f5692a8cf8f573541894afafa3c29cac5ef2872b7be0b207ae9c2e397bb129c5940282fcd8866d31de267cc3435084273b38100211e5c51e789f9ef4f9fdf3246bed87f41bf7a7df7efcac4b0b81eeed9372dd23a83df0f6938e53e8abd761f2edfd9e46d358e6f08ea5b8f3287bd92c7b0bd1d66587581c6d49da02d116b800067406aa8b4b08b484eb62735c753b5055f76f0badf6860a326056f062dff7404984e57f9f056d92b7d9599779dfb07990d0625bab7bf8e646bb042b00ffcddda658910e6c306b36d87b7b663b5a2edae5b0bb6f34368c188dcc19e02ede009970a3c23098c5f612ba5cae168425e7e27238cee572752edbe25cd6e50ab3cb9a2b084bcef5a55d076eda5a6b6da72f59722ece456efab32b4802360b7b3b6fca76dc86732ce50846084bcef47fc9dccb2dc786f925ccd955202d2c3933e54c9990d990adc8ab0775cc9935ad01071e6f24212c39f3e34ef081619cae2da541c10961b9d1cac7ff59dc5ea4994e500873664186b95aa84b074d61e5d368341a8d46a3d168341a080561b9d1ae906dbffc58a89782ef2535a540488479971889b0db25f62f41ab1154957fb9edd0c6e3e806cec36eef295332aaeae2ef7fedaa05abf2779389c9f6afd6f3b6a7aa8bcf204c79c5e3b51e85bffbc3fbe63a737375b13ff2212c73bdd7e25069b32f0ed88a7daf48828a1548a1882454b10539811f5821c515c478028602f2be9804844395b1a01cae683ae5bc6531c2722b51a6ff5fbf0206638b71a01bdc8139876129c65cf592f2150edf62617d5759d10547bb30ee41b6ad95413d7ca4f08557bb307873ed6ddb1623c2bea90d0b61b9c53c9aafb657955b902f54da9c273db37dff1bc34b7b03831ffbdb971708f0ff560875662ff4c294f58b22df911d773623adc65fbe501f6b31c71ec047d34988bf640693842597f3c5907cb20b83bfac5c0e675eeeccc9590c5336e69b91f16da70f3a6711bed8133e6cf1840f694dd12b18e13baf308659f2c1c10fbe1a0e8480e3cdc0404d21f08011051e30749ac8c9deb2411660cc063d4db44c5a6992161339a45da01f0a04864f27803821756205297002482cfd88d01c10c9413a0577d6748a5cd144e72921a40970f68412563ca1c4907c854b6c06dd27a56209b304c74e2d7848b5a0410b3c1be64d2b76240c2aecf2250c2043989862a096143968be3573160391134128e244105829e824c535225908b58ef02226129948dc7c220b2aa04f6021e4892cb0b0f63f7c3176d379245d028f0b0c1ed71427d861ec932ecce562db7befad6ddb42127c5737f97f3c994722dbbf3ba8e37edb2e374260dbdc73e30d37b5b97b0d09246e8ae403244652d711244ca0be7495158d1a36528ffadaa5b47e1b7ac6d7d02b4f43b7bc8a4ef90b8aa1dba547e912e4d2f8cdd0e3afe819225074f216f49de636ca3bca97be9db4f2e3ebf77f5fb5fc8cf7924824fabc4522d1b7a271d0e86899a16b7015fe9206796cbb47dea707c11311283a01c1ee4b5fab0c68a613e991498f1b6a4cd083df5b1e9e112c9fa28d58097b649648ae54d08c09cbaf6814d432ca28a30c964721bdd6a97715c6b8ebbacfbbb3c10061b63943f0db20175b3dae3c7e1b56b4e82afca3d6d955312494aeebfef340339d773d6ea8e94aff7d4033a5aef35dd276d76eb4e1a6f08dee4b249188135d9108d589ffd9ae2bbd682a3de9bbae54ea3af14b263d026b8188dade60515bf3a6f0cd4d618c312ec9202cb38b83171483f8b67a0ca647d1767f780b95def42ffe57500cffa5b1561752e9f3a9fbb4a4451dd555279aff3f75237e71a7765949145d77298588b034da538cf086af56c64cb28c2bffe38564f96ff9198f5f87afc6c7bfc357fa31fe109c3cfe1e17a6f4f86fa821dd5816298441d0963ce93f07cd9044273ffa52e9c4ce9c8c2f35d57d49db4dd276b7e85c55ddcfd06155753f6ab1aabad7faabaafb914e55176aaafb120d434d751fea989aea5ea4115053dd87f48d9aea1ea4195053dd831a478d40ad05e89ea5fb15dde96e7b6329762618a4390bfb7979473a84659da11b7ffe782218fe7b9fb125c483b44e5c0304aec51702dbdec6d0f69acb62f3b81aa82eb8d35c6982cd8d3baa4b789ce1de1b22c48fc7cae3d48d34a8af3b0779fa9347f99bd2abbc505dfcadbe3100abf2fff4cd8f27ab254019dae54b014a99ed5f5faa8b3f0827ec6440bdf83a4e6ffa8e26e55137e5a617eb4d8927edc3f4dfa3b40f229ff791f2e0db2d3ecae7101f65ac2192bf26e5c5cf630e9457196b4c8f82a2c557d1288fd2e09ff4498694073f47caf81973987c639df727ead0a368d09fe8f04b5af423edbd892e794ecf50c99b9bf0437f237ad06ffa7b92aea4a82b89a22b79a22b59d29524694b6ebaeed18d0e3625e18b420fba01ff23610889cc12edefe97095ff8d1bf342b2e9aec88aafb6784184fe12937a498562b6b7e4013ec0f246b3715b8a0bae18c21148b79eb2a3ca0120bad845f8d285e8ff29e960291500c44d07d68d17c51f618c63d164600325cc0b52c177084b8e0552c1b6b1582c562cc68ab16aac1b74c45831568d63d164c1b91d1c4b0395093cd0d05cb08cb07561ca9c6b979af47cb5a5dcaab050b8b95632c2b2c6685908588b0d8bd43187a4db369a92512542dd6a7599659a9aa289c5625509b40b8323164ae1a68f86f03c1381a2129311a9844b25bfb654da66285b92ad8b7d5fdc68a6ed8b7d3f65ab9df0e9e4d79eb6590e4e909c5348eb841d9902967248b858caf5705f7063703d5cedc6e46a2d979021ae5611b4d5c93a241d169dac8b7997a49bddd4ed622ae075bcd94f4dddb78fadb5f625664375605b4c96a6699aa6e7799ee7b9cd7291bbcd66b3573e124e2122d9170bd0c663286e78e3ba344dd3349fe7799e9f6db6cdb619cb078240a190481496e09212bfb684b4379375e6b4b22c9c89b4afa57ab8f474d5799ee7a9b3a5b0d826034121519aa6691a9ee7799ee7beff262312a9543a394119e11896cd68b59e56abd56ab5b85aba99699aa6e9c6dace2d676b6dae11a9d56ab55aa593344dd35434a5a49c4e2894ca0a5e59f16b574c366eb930aa93c2b05a8fd9b5b81a8ed56ab51a96d16a3da69456abd56a9db81a57e36a5c8dab71b57d7f54d12535efc12666e173e39c2ed6c2ad56abd5c22e8c621d9c62d80a4babd56ab5340dd37499e933d3695dac964afd8b627776a7a3a1b9b7c84273b2698e5d2c77b1ae8b715d6ceb62b88bedfb2d99956f3290ccca66c6c96716ed94d357d9f434f562e8b0ed3197f98cb64488fa2a7f63193e97cd2da3d9dcf7696434a33bb46ff8ca730e43d1cf1a7efaf909b98a9f55a6fae9a79f7906da928c929b2321dcad796d3ebc715dfee48f3dd3da6dc572776f9dd933addd1657beec3ab3675adbb88db3678acb973ab327763cbbf7de34b47fefbd379683b8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf5eafd7ebf57abd5eafd7ebf57abd5eafd7cbfdf57abd5eeeafd7ebe55ee385fd85fde55ec3f10bfb8b468d570d1b5260014110fc6860df71eced38feec38de71bce378c7f18ee39dd7a3d16834b2b1e3d8db71bce378c7f18ee31dc73ba3288aa20dec3b8e3f3b8e771cef38de71bc3343454545658585e55feb71069e31c3af9d91da71bce378c7f18ee39d16adf53863464b0b0d1a356c601b36fc5a1b29ec3b8e771cef38dea191da71bce378a7460d1ba9944ad5420b007001bbe0825feb420afb8ee31d1ba99d940afc3c2084bdd77589ed6667f0d516eb78e37ce3bafcc19f8f5ffbe1f2c7fb400c827e2d58b31e080a89be10180a4b4c46244c22f9b5a41476640a588a2496d67abe18a3a7b6dd6c660d1c9937272c149c7d5f3c4dd864f26b4da64e91970e7a24459de649bc0ba7f9ccbff09acfce1c9c2039a790d6798f5c3cc5beef18e616c9bebf8162b01bc76eeae2f4a66e1061896f0ea9789ef7b9adebbae8d5b9692d85c5643347cd1b334dd334af6c46abf574288aa2286a9aa6699a2b2c25252525a4ad657bcccb3a7352584c364bd3344dd3f33ccff3f4197a5b288aa2ae8b5e9d9b5e58f6344dd3f43ccff344511445d11f0511d218a516269b9d699aa6e9799ee779faac36ebb9e665ddf3e674699aa6697a9ee7799e3ef399cf7ce6339fe9d309a5a2b2b2c2c2f21a6bedd76a137be69c96755974479aa6699a9eae3acff33c2d2c6665335a9aa6699a9ee7799e270bcb6b3d8e3366b4d0c03468f8b5344a76549a896bb55aeaf64cd3544cc36c5bd66551ab93a6699ae274c638a3a585068d1a356ca4702ae5d7a6c22dc784a908fb56cd55a9d4bf28d6322dac896fe650c4355cc3b5ad8671cd71ada505b45bcbdeaf21a4c6907ddf86eba6ace68153a018acd52ab405dc420b7e6d0b397472c0c706dbb3222c6b12456c7f061c91847dc3f3de7b5fdda82aff9d3a1e33111f783ff022a0c84dd52368e82b181a7da8fca7827db03c389ef05712fc4afe76edfd4014e53634a39d456332140b0de9a4f21f8a0eccb6d034062a02d241515011900e8a9e5650df4a252f28e5c1d7c1f4dfbfb6e4afe4133d3357032d26068370c20d55d1d5abab3ee25b57c639671e382c52657450571f98c24ecf91ed359c6440bdcaeb38fdca5b100dcb7fec0dcb8a4a6a59d6acd58506fc461ad4e88348f73e521e1c7d80dfe1223647074563dd3551027f2ccb6561513941a14e291f8a0928964c26d39b581ef5292cff8120f82c608ab6e4f54124c7e94d0fd23e4e6f620159b68d0359f0e733ce70cfc242029bed3f43256fc447f93c9ebaa761f4294f034a9f36167ddac6dfb6ff7ee56dc8efb37d3e59ac2181b6f18bb59f0c538f502167632eb66dfb18ecde4e3280cff239c06759196b54c6d3f6a34f79f0f398e37bd458731a4fdd28c3e853feb48d39469f72ca417ad393def49f0ebd49835ed4e1a368d197fc64979c3cb1e16eb8040ffc3c7ae43d025bc6defec793df932731db44f4524027a451a85402722007722017d2a60785f417124d27d924de10831bc771dcd6656ed315b002b0c0557901ca9a64db3a1ea73cd298b88d75b7ab0e3a5457e193bf714bf4e7473afc8e33e9387b7323fa1bd07f7f13fa1bf0bddf6ccc1deea8890d7741555cf1e545bc2fe2897a1bf274ef458d3529f664b9ab6b0e535fd9d39edb88efe8202ca67242a590bc128aa98596368d5d9c9c3bdaf3bcfef7c4e12bd48ffeb3e048e5514f52794fa350ffbd0aea49da92275f52413d96d1c000ec6a03b397d90b7772dc9923047d85e891d3554427e5acce8509d1b0488886a9af6ee809d39e30bda167479da1aa4c7eff17eacab6e07c39cf333c7253bef2d8f62f11b0f1e681f78c0aeab1f8277f83f2a5ff5434cba3f4ca9ff4e85334e97471a78c84ead8f47b96cff13dcb5843847b6ecce1fdca584352d1dfa3b4f7275dc9944a96684b964e342413fd79930e5fd4a247d1a13fd1a091893e92d372a13a292c76c2d2339d7177c4c52c5b51bc31e1487c31e5dd23bd97fc67752ccc02b12c2c66e073c0b2fc39f1b3e0d5a6173fc5f49e16bf4ffcef4d9ff829272fdc184002f0c5d88ead223d2fcbb22d3b04d5b137d4f4ecf0150ed80c47757110575fc4c77687ea926379b02cebc3f6b779b42c16f799fb3cce6cf9f3486332894f021baef4287f43fa93177df599f4f7a2f61e84133aabca844e329c3ee56938a58c27ee53be9234545775294fc3f729a30f2239c4f7de878906ef4d6f1a7d885c2541920ebd89fe7c890e7fd3335412eb93aea425b1aedba492674ecb85eaa4309665cd4636dc1545812a11a6612912e988641726ff5798680a25c2fa1574ca9be56439b71da38c7d4f0df0cd3df777776f5f04d3f55336054868c730c87891acaa44325b45b23b96a148c682ee3ac326dbcd08e97659cca6312979ef41ebf6bd20a14552fa4a96de4b9ac80c95bc21bd09e93d6df21634f37da4cfe44fb4253f0b92e14443229994bc31c6c33f597fcbfabed05853f29ef735a3ffbeb721731886315506cb7c40c6ae4a935d565a195369658ca522b43f43254b7f6bb8a013f6aa0cf7fe06a82b8b430c375a737beeb931e6c2acbe5f79ff7da10bdd9b07de33271ad2d79dc341273cd27c9f674daf32dd769812b72eb434dedc841e14620f7059dbff7bd2e7f89e34d610d9be66347ebf8d39bc37196b4af0e743d187400f0ab9297fcb02c34a56f263cdd015b66ecac3d64e4336dc151d813fd6c464744d62c2d1674dde83c4c7253884513009638c87f857108d48074b96b48eaf4483efe9cfe44716c4a37b9168f4a237118ddea42d59a24dded3a3bf333cbaafa40d2fc67e72c7d831c618638c31c6db07f3e09e04763b05aa0bfeab4df08f30fe8c3aaa0bce5ec96e869307952c88e6c67b51973c8a0eff448bbea42de99c3ed198986c18638c31c618631cda701714830fffcd8f4c8624f837ac1712fc1f21f97d196e1e295abb2661579e289cd82814e99efe5de54be0bb65c93228c300d23a72e8552eea4f2fea4aa2fed327196c48f067b021c1b1c686ac2192e3f4e0a33e470d0d70bac08104c71af2f4a2b6e4d575dfa0bcca57d0a9439dc2fb794284f7abcdb6b56fb83037ee2814bb045d907ae76eef0b0eff08f885428559fbe22b51ccb5ee10debf633d759f2704056aa84095d5256c7faba3baf87b29082d686604f971fedb3ed71372bbf218013b327393b4f1ae3c4770320e5e7ff015af2e2b8f1566d5a5fe7731c618ffc761e7a8a8b5565cf58b62ded8bd74ecee165b8cb1b56f31c618cbdc5a5518a7f67784d0fe6637f131fe8a31c61873b5bad43762eda7a466caaa7dd6d7ff15dbd36b22c69cbf3d6d8e6d5917777eceaafaa1ae6c48a96c48837d87b8aad4f5f185971b31343874b8ea7e03425b4775b9ff1921e4aac98ef6761c67cf473f4c48dd108cdd3aab6fc7b82bf1aaecdfccfcf06108b21a12206a4096f7be2ef89787bdcb1b64dfbf393cec1b23c56c6553d3348190b726b1e1887deb17fb5a96c5b936884b8c7cb598028995d160df243b37dfabb29fe280594cc18228b8081204079e2d48182b5450c448ac071a5841566bd610841fa9ec0816319802f9054e0fce0f446881853b051fa628821131c8a14708594e60053844821431c4064390a5ad99643a78c105921d802003f20e80f6c412b6e019c38718785e9d6297a8ededb2a58923b6f79ef7611348b6f7e20cb6f7ef4414b6f71e68c64b1da0850dfe0734035e544b1339eceff185d1fbfb0c9af93c04dc94f55a6e4a34016201e9175755d331aeaaee9b1490179c9820575599107255759715c684cefefc67f5b5ae3e2bd452323f079ac96167c54c9761dedc1c6e216dedadc2dcd43453a669e250bba0190c827775d30bee321b99c17045588a291037b6413258e203fbac8c48151c1128acec23f21cc9b128bbf24431248a20f5b34478bdfce5e7bf9cf3e7f3795759fb7ddff77ddff77ddff77d8f3fbb438c84d056932b48a18717183c48f8a1364144cf8e174ac8610724701278c22023544fa39125b67f4ac6114dd6e40a234396b062fb8ba019bf444a16a58a2572d8fe270438028c287ed061410f2eb20c770d70e0a2871e7001042a58e4c558ecdb84499529592b76886c7f52f6586c073a80811023501041962fdb617a50228822aa48c1ec0664cb344d1edcdddd1d09dbdddddd7588a8117e134be30ec100b508bb44c0bef5884e4f143d6062df0fc51e3861df7f1ff860df4ffd4005fbfe4be5f9c1919710fbf2bcbcd8f74b626394e1599810eb48b1fd45a42cec5f120cbb44ed5b2b1076e982027609c3be3b80416adfcfe10e78f67d31b62b0f0fd2caa3a3827d3f14b38512f6bda42b74c083daf7c1326a9c85f970133a40c10d535bb6ef44157400e118ad7d0be81051b6c8f6c7fe1d173c4558b0efccdd6214116e395862fbe3185ef0821b307c9041153c364000a9b1c0c88d49440fe4ddf8085940c237e241588ad6a63908b2c7edf9de7bef95b936003888c216619957210e8ac8e4f0a9a004ba2b8f122dfb2eecca837eb1a531abecca18effb622565593803ebeaca5c65ed573be4a65cd9e59c5b175af2801f5879c84d65d4f5232c3350864b028fc3c6ff436b97d9b599e169fb3183262ba0794876edf0c91db7b93e7d5ee6ab6c83aab2af44eec1b64c6cfb577665d9555dac37c4478721f6625ecc57368996274b32c685a93c28906d2d0c5ecca36ddbb24b2fb6add7b3ed979fb92d48961f6b5b7701414f1d9c34089810b1216441aed8f66db9dd6cfbb6dc8c6cfbb6ecba30621b6187bc8eb420816197d695a40bfb85117669c7d856b6cb7bb3453125a63eb54f4f76e5d8b6dfc585e96a5dadab75b5aed6d5ba5aceb12eebb2aecd480c8e1818e8c19243b77d1b4e674b5ddbcd85294396af32eacef255257328b236f48a2a73df26d946847ee7b826191a75b8ca869ea6b249c84ad2400356104d683cddda4dd9b7213f4084e595a1750c72615af6c3eb1243eb7a596bdb96af2ae94275d2f2c2b67d21b2ebde21aa8cfbe7339e6ea849b273c76d58882a93b316c14def4d4d73dff164cd1b33882af3d12208414db2bd9a696e6f3c5916909bb24976d635c966e19c38278f1845ec7be604b17f815c98936d5921d6c56de9b6a5d9b5a5d985f1d7fa6e0d239fbbcddd8643646cffc09baa6506b2ddb7ff16fb8e0bb38d270c8306ea2a5555f72d0efb8611def1090af8aaaa5fa980625f021c607bfd934bd1634ba2b62fcebe36bd410fbb049de706e6bd77c3b56281afd8171761a776e4faf505b02f57c243b4df7dfe3cd65457bd0d6943dafffcdeb0bad8ff00119675d6e5aefb3c76e3c947192ec98dd543135f8ff5b9c9beeeeef7bafb758cddddddddadbd957581b863bc6da019d28f9117a157efed3c9d5dd19baadf87bfcfef67bf7a122a6df6fd5a5deebdee8e4111d8770cc1aa8b8744adb5be9d42127b6dfd5ac3975224630adb8e65d8524710d4d8572b084578bf4211faae5b579bb0b9bf35c83072c53e7d1714c3b73fa894c61e24f4d761010d8ea9f320dd18b07dadf74758da6c0f8b310428c81339f8e08df847bbf224b1139b1ba8d48c99eb59e810cd0000800000e3160020280c0a068482a12cc9a14053eb0314800d5b823e605846180863c1481004390c83280c31c60060102000188490d09c0d00fe6654fbcafe8a32b8318da0a780744aadeeef32000d4c868daaee5804d031121b2b101d952bf36c09916756e6ac185eccf01e32bf7c77cc18c6b88825e554185b8c477e3fac52b64337c0322740bf61f7926db237d67d5ce5426cf5beed8842e62aabcd4bd7908d76e44e26cefef79817800a4a3af3bb6b4f4e99965f44428da5b6d583246ebd807256d0da01bfe5f74fb3cb003d5456c53b11e0f61645b8ae922ad9da39dde9b314829536264e4a0d9ff80d3003c72d9b574d5af077384d4cba9fa1c1322f1c759597d5951a6c95fa4b894890d8c8b2eae7becc248b3c3898914b41835dc5cbe428cd9534cd5c9a80b438ea8acdc80fccff77e181c4ab93a50dbd44b51c4641ee799eb7f3e71e83ab25644dd4b5d425476e21e4ae568a0faf92232f16972add148208ffcded5f1cfb42ded053f4416b737a3cfc11a3ea898eb5f052b9e959ee87449b82392847689a40744da0590332edae50e14e67d0c8989b431fff71388844c222ca4d1f90514d3035179299e71817f3ca64be2fc558f8131fc2a5b941c01126fb604d9f7b5ff2f0a86dfb8cc850b8247d5ce43b4197236d58dc5a6e67817d47c4446191aa80cf85d88a8da5b2891aa79ad79ecbcad87379edef99f00ce80b0073540cc9ba318e9508ab6ca996c1c88875b1039d8a8984f20276986b540b18da32527f83d1f1593ede8630a8b6a202fddf4a859f6eb2a73cf897a5f498e4e5f274cce5b6ebfb435d1f2abe4b9b724d4fd978f244dcf92e69d978e3d4f6fe2120fae8d137e9cbd221de0329f14b992fd02e9f143be553d88eab88794c22759d759a946320035b1529e1107c5b7bc2de3ba43337f44ffc331f30f10526e650a002e96011142d037314834211af170a418c6a02b9775f39e2cb4047d52586a8c14903447f79d75ea1f393f6ba9eea6025e57ea1cf24618158f015eddabc683219169cebcbe01950be310023cb64fa71b8e089b4fbf99a69ac7bd6049051a76f77477ef18bb4a65a0b5d1007745b8623f12cff29de9343ed81ae4fe43b3b035e712fa1abc268ebb733242dbc0c3e0a0ed5a5eb8186def88f0d295e591f174ae4cf32391e6fc3122347caed9b6ade595e01ea6922280650c4ec5e867282256768cecf084d03fc03c10f1ed8bb350d0a658ab37941c701c6b34e89a8186888f2c709744922e34a6630f96242e785dfc51318d373f1b37a20f5dc7cf5cedae0326b2e2c29f446739694cf4622329bcf1294941dc661a4896ee6e0c93bffcb2416d67ff3debfb33f53ed961d16a72b8316fad42858e7f2def1c2a6a441386e756984d72e969ef2bd002ef6af009102469c77f37a4914eb1c1619f7f226c0b85cf2999f1eff080d968bcc27a609f2f638e5e5f618f02b38bc56ee521929ef1796ce69d03fe8945920d1d224b774e2c230193d6f7223474aa7fe0bebd803478caf9351a44ee9c6846711f12591b4b47e4029b432ecc79b3c4aeeaa5ce417111284f9f552787ea029aa0bdf807fead5a6695c8a9bce4c102d992b32f911b64f4804e590de5fad51cd9bd77694c5894e82bdf0b7e403ce6f64bf6bca03778e00dd3d5daba136418058cb50ff0c4b8742c7318d762c5a0d8d1ab63c67df31b20866207e476f01630604aac1052145c9342c8bde20c36b2f61816ecfef6d5d9356597fcdcf01a3021a33ccad4f344b7aa275e87f6a50fca6bd8a027873dbcff79073416b38c7411ae1b8282f99b49bda4caa1bfff4457947b464700726d84d78e8bcf6997a052f336da2ca4a9c37716b559a7d1d29258840634db98bf3951faa9838c328e6f99b95b110b589443a021926e2f62b3ea4c49f388b7b803b04a1f244982a4a5d66fce52601dfecc29b5c3a58636df3b8b2f87460cf9cc3a6041ae01d8213f8b5d9ee04e5b8f0eecfc57d320c3c41c8ffc95c8a121670e612bd6e88b1e711fe14e978ece3b94217485b92635dd05f4162c042695c8685b61043daee6004d3e62f54122268b05694f3f14250f75b8c06f71b0b8a062ecaa061a7735bc9560c3fd02603c20e00dd55a065b875c0bf638ab2d93f5c6e0301f7f062b6c5eb049c02ed93a040ed3d61eff25014640162043c2a9e02b4dd269aff3fbe10697833e62fe54be51a84008b4dd66d9e9131c030f5f341d55d6e3d64b5c44765526bf43f4d650c96b9ef293e82106d978e2dad3d7dbe2466d1280e17007af4ca2068e0057dc54ce7828be0c1209e8e404fd8e8e50f960d0d9ce37e4fb33a065c6ca41b76887ac0c8d610648910f63406e8f4c1ddd75df022cc10ec457ef3a5e23304a6f58076ec30ec9a83e4a75f67dc5cc891dca83fbf7f0a9ecdc70fe20a33ec1621689a4d9f16fb37b4e4625c74b257c9c54a401f6d6b17a48fd44f2dbaec80a0c6e2ae3938dd160200c644960c5e72795df705f00bc14d7f9841a22fd84bb4b3f46cea235c5751ca3137b2b0798642db4e9b2eac526e2078051606e13ae11d15a2ce08d149033792164c182f6108b1d8f0506871d1bf680877ef0d97b92ddb72d789d8f807a75aee64ae01e04801a9160c96752d80c42b96ad9a0a607fc1e9850b9a0813e995bc7ac76bf3a97d12f623affca875bdbccd2686868783ac05893808f6eb0a7c61cc4f1b6a81d5f10c5fe62d6ebe68591b6d38e5afcba33aaeaf3151b81bf03e10868ff3d79d3f1c8af99df3e99872892f516951d4473414bc1b8a1af7886fb71f85b40fb6e7365c8c6b930996cb087e25b696d6cb754bedab00f235b9f5c705d57973405f73c0329b83b6c07fcca5eb3ccd1954b84249c7692ce454f4889087d9abc7b0bf25e679f8537c391a3f6ad89b31c84e0b8ca35cb615ce1afb8c7d5da7c346686920606b3696304845a4f7c732447203ecd33d6915b706588c4c2d2ba5ce906ca8752034a6cd87a1e4a04703ec7e60855239412089bd843b471061040041728353f9af8a228e2b4b9a0869f763afbf3c06f62d19651b5673fe8f5d94f69e61e992dddbbdd7e9e1bf631361cb8cf75d6277fb0367b43f85a4118d11c3da0b0aa63f83b692439c5fc8c1cfeda9616d86aeaa614b513732b032cd9ba81c2ff7430f81953fb4e9faea47118985a3b95cd26c93b69591576c75ec3d24eb14420e2741ae55e6ad7faa1dea7f0a096d35fdc20994ca1dad5b7ee8d13a8aae1ad7a255c42815777ac6fa8a0f947787a9a2ec69f9ad5dafbc98682f7e2bdf630cd83e0b6a925b88e349910ff2604a32ad28069542e125408e87f73adbbe678ccd5364789d42d36a919171bf4a42e48f7d58b2af6204fcb0e22d208b1da6f553154692007ff1cd47c38bd07e7311cc0ffdec252a2cb9ef4a73f95f642c141ffdb8940214593e0a6b7d05b786e1b180b7ddb6da6509897c25022dd0015f6bd717bc9a53edd313021f4472977250b9f5fb6d40dcd29eb1ad2c56328fa43922c7a9840e52a3f6d88dd6cb646eb4ec0c0480c592ed72875a2c0c15cab82a4679f1914d867964497dcbd5ec1e425fa404595b96b46082d88dde4803796a0c43d04d0aaa705692e9970ae6dfc3de3fc10bd94337c1e4e2f3584cd990d8f61328d0d1c140e8081a13a5b0557d548046799c3b0ec6471b4720f4f40c42482e86cb34b203ae07cec719914d224051d53503f521bd4c408fdcec3c560923025c2ef8537caad5102d51d146e54343140fb9b8090ee254363d022e15166812ab2b51644336bd24a1695e6c0a6cd2a40f4198fc2ac67e5e8d5508950e964f0fc67b6cdba2cd188d54d2f5af46077fc10a2054b6fea12bcd9578cd77b06fac79b97c5f18114d8050c359b68597c7d5008e422458c2613a30578590606e408fa60edf5b5cf5a70bcfca5578a522947b811adf2b15190bc774e4cf116b68cc4cdd1583562437fd7cf0163894c00e2fe6d00183ae0cd5efdd23fa53a11f068a89a698f10c5cc785e97a3cae4e3238946203a0f445d6d95fe4d830c5d36c8c88ee87ea28939dfbd0459a5488335d3c21830c90094d9eb333b654495fda6f84e19b972f8809940191749a19ea4333eff7f40c1290139b3f0d0c8b8be4ffccca0d179c57500ff598988bec87e4420403c8b79a72b143d65ac48210176bdd0d40a1da444c54d2cd7a90969daedc7c488071d51ba9db1c647f4a019ce47200a482456e8038c38b63aed044a6bb184f7d2740d1a72c97434e305716ded1657e61d0b4ecc8998ad193a36be4363b1bc9ce2275a43eec2cc544bb8069b16a26572f28f3addc2f8b3591140544c192b54293c0b7953b9bfdc0cedc9d2028f0a5350fde4ad0aaafac2474289f86bd0235d8d27d7ddbc41253ba9935063f0a2f2d78d7143201db0b8990724ea39460d1cf59308fb9271e34fb95c5c17b8164e3d01aefb5592675892a1defd9cad510b49f4c4116b62768393072c233e124b78ac6d9d3ce173d7ba9fa62849bfc42f909a8dec016e9d7ff894cf83ccd2cdce98c0fd767e594ad00ca13262213350f08499c985232e597934ccdbd94e4b93054593127f69533cae5958842a09629777afcbde371b0c0cc0ce4c4a7a5732fc9ed4ee92c258d7b9224e4124159cc3dd5ada5f2477e30828149aa1092d96855525ff41603e584912a7fe8ab392fe8dfec955ccfdf57566d48b3fd4ac2fd416ffa04f15addd2dd6aba7facf7446d25c6a49cd390a8a6ff6c82e1986fe0f37d283a484474105b86dc3af1bc8109b77ae7e3515e75ce5cb2de96a8093f683ca462b66f61ad3927cb27f4d205f6e23910e2739ad16fd00485c988fcbc35e49621c9b0ce2ac02f7474a7542f7e392745497b7eaf498262536430493c212abe6821e439dad33e99b6e04ec3f350f9a63eb79c5445c4bc9d9b2ad724161e898d405b051b154b66909d98a5e68800e248c43c3a144f82ba2623fcbd01045d871dad2a0d912e046cb64c53b04d61a88a770e988b7099db890ae504e92c862056d1828e58f66fc1530f860d29650f75c6d1319817100eeca694c2cbb0429613a09068a8a6d847ef8414cabb2e8f7ec82a54ff833d8e42b3e9c34121733ecbc43078ca5495543ad5a45253060567dd9c4bf9b14f66a1489f05302efddc218fd6609955b0ca4f03017fdd1414c365cd3049f3852d52480a5dc6d1090e6112710bb416315b8308ffd8218fb0a5afc131a34f496b13a41b8cfb7774143eb8745a56b28720e1d957cec7560a3cb266049756733706671ce7e55496c66e5599cfca7c99c1e9a826d7529e7956ce43f9492139fa40e291b18e87a042f13fafc9567ad6b2a0bea5b51252c36a75104c07c7de0ca6f6d077b6b9fb9700ca6e0cc1e1b692783633eef810ca2d5a10d2245a6d698261f8c5c207d562d6ae3033dc0adba0a183388e9741bfbe43751067e6afff94e040d50433c7adc73505248ec7ab4ab347fffbc7710370a36d623ecd7dff71ff0123e4a2226db836ca2206186c0c13689197c2cbec8bbeb0f8c0fb43ae5919920c44c647e1bec65416c827937056f3f028e82a2c5a5e874889542d5551216ee61f73541d87a0b065be7706558147bd101d7355003d82af7907fe1c4125d60a86cd54c70c966325db24b42c41542707897136b0505385e1ab9de3ff4089f77c9d83243e3da294b2675e33f4e9d545b25c78e68c964b631afa236a5527016032996448364afe43aee34229b8a011c53225aa06f815165189c1860e7ba87957ee4eb4a6202b96bf19fb7a6cc4ec7e080a6f6980ce9d5ca161b7961c98aef8607e1b0fb864cfe04721cd96c8b7d314242f3dcae042e0964b6daac80757fab51fae962b6b4e63a8773513efe057351c15c5f979e283ac4d7c82e3c4bc533100838d83ec148897c36e899c1da2f68a4a063c7531354244b9faa2fcb0215ae00de7cd144bbac885b97a457709eb93dffbefb31b4bb931e39d9325d23686c53766ca9bee6f9a234f93381ba1f0930e34daa949c1f0aaa9cc584dcd683e7ba7442c008a074acff5c03c8d58b35e72d45d1c8b1a811ba696a316c8fececed93950a4f422cd2a21060a624a255d55b6398371c5aaf24822f4a1e7c02535399884cd8e66bd02dd423958c1e470a1b2d7e69365f7f96558cd339fe1a435510920a41c2e9a96bed4c5006a1587b4baca8463a9db4d21ea0f90273f593f1f998408c48e1d6af8ca8151497ff5a6f1f3d347c5a72f2d4e86a5d47333cc75269037ac06d0f45fa5950a1e944428c58d4701e9eaf7f6c49b980956a69199dbb419711c7fdb2c0182eb645e49e6530f77d406c2d4f3262820a63ace567f320553363014e4f642bef944a3104f2ac39c55ded5cac922e044d7d8f0d86f7523e9727571b373a601ae17bd12f61973c60b8ce3843a8744b3776ed181fcd0a580a0297fce8d3d462ef60c1ecc07b4d1562a9a8f7c91ce96e10367a9449d156dceb0e774fe2923105d69c4b207b0a0022244d2f938ff8449725b9043d9acdff7d1089791b055a7e9066c727832b187d7588e541afda2450b9ab71e1802a6151606f45e3a3a6008198263d2e6632075efddcdc583255090294599b966eb2a9b694437e0b30d0d983dfa3f8f4eb17216011a71f68fe2d7a1cfcb6a570681121024b3548d97f283b8cb1ac4fe015fcb1fb5643db9d41ddd9b64e7a3da8734411b6b4b2bf2173567dbd51511e50a5a5a12b57d359889414b42a04ab1485a6a2ebab9c90d0f7e368001ded7046cf446f7ce18661790262358ed75bd7872fd9eff53fe5b137b47fa8c77b1289f1128413dabc96478a2ffaba673f46d3a6130a86304986253959c99e989db04ad4799bfd7954db0e44f1570b2e99bb3423484d70e87b9721ce8df33ba581192e424941f185cff49513134f94ddbcdb323128ad81022615108ae3b6008fca4518d8d21623e8c6118223fbc88169099ac8304c4befac28587048d4593902416c3471865e4998e5ca6dbcb4ea8923547eb1758354ea769e2a91ff86b44ab57045336a7af36d8232e8b858f60f67a4c4d974de35350a0c146f36dd000726a8247aa633fb0e147571d64ded1b1960d0f40e66d6e365705b01e20308420ab86247d422a2a5443e4c37e9c5cb56bbe3ab7e9dde8d4c51f816285581bf683838b97a23b1ba38d1d43d5f86763d33462efca6f52c86a40e373fac891f9f17e7c2031258c364313e0456ddb502c8ab02c2ba85003b78f922d4e604a0b55866209fccc6b5529aaeec5483c156a3f0f543d2296281f3614594c6cf791035aa4bb1a7c752eae4ba34fff015a9eb3013a39d9053a506179b165bb94434b2d68316c279c01e1673fc41da6f1423d321b40de20db2bef4ebe13bf35a4aa77771a6ef039a5d0406fa77c78ac186a112655df2996da9fe60c6dc424e2b1bba80c679fcb09a98cc844e5f880f3f9e0170ae4aef161a8c61e4df622028531c3af58b9b4320edc27c24ca11dc18c8aba93e2eace91d15b62b540dc798083e04d3f3b908a6604074650b5132c90ce78a8c3225dc13f1b34285790eeb2155686424c5f44701713954426e100500af14802652ea145a49b587189323339a34bd2d2ff7530e53db036d7de168a0d08c28b076334c61f7280ad8111e890c56e492319a39346b2d2c334b5f708d87c2245de626e233ecc5ff19af5ba834e1b02318ae19ebe5508c472b1bbe3ca8f7fd86eaef86ef32f58ee4950b787fe56db27a12460e83ef94e269f4cc9067925686bd17f60e6bd85edba8fa8c307b9d4ba2a618f21a8b710792ae0563df5e88405c2fe1ae2799f3705dec0b56d609aaf5e0103d9583a33895ba7e62c87c2b99534cde2d233110e1f11843775fe098f7b762abe927f6f76243d377a97a1a3658c24c63e88d8a2bc86182542fe182d35c624d533406584e31a809924337b5dc193ddb0f6ed9b157c0521d3c53a04fa03ca5a9a16893661ab00894c5ad0f0a221840d02518f84b8de9a18b4ff2fbbc97d003e22818010ffdc200e31292f484c94521826853150681dcbbe802c203149c932187f54ce973bc3bfa7aa701a711ed6c55bb2532172b6566fa9b141af098c11cd311855a7e417ba7a4a51d220c7c5cf8a80224a830cc59b5ba2d36f6488086ae1a53eff1154d6106d599235a5996491c97075815e2a561ae78a4b82e89b6efbb3d117434a636666f30f6ddb410bb9aaa20a9549d40f65a376924f45c88f1e2dfd7ccbd6dacd18c38cbc394cb50016b1f5710083d2cf8948dcf718dd0112dce7ea2ab4175f27a077fc5c75432fac123028ef184748cbd1da5a4f7b2a95b59bd75ded420afa15464879753fe9bf8c67f597c6a7aba569083af41a85d1a75e586718ed9595cad3afe53a1f48c60da2dc0be4eeb3adae99146fd234643be6338118115df1fa8ca2b333a7c515bc5ef5813461dac9b529840aecd938b1339cc2d6fd733e04351c7d760c9dcb4246bf1197786f740367b3a190e1d580d8d402b184540d51045ba49f3d55b8bca9700312808a973e1562f31a00984a83fb3ae9b34069dff72d62c2addaf4c10682a47d66ed855a808046c1f93991b92962830b9c4d5a3449e5b66a57af67765ab43d3bdacec016e6ee35d9e817e89e0e5a5a28fea8533dae20fe041d87371e316444feef84821919ba3d87481f2a41049d68b3fbe09dde514f5630654d17a3bfc550d99aa7b718e91c75f4467021573da641b8950ce29ecf8f7e951080e43e573ca76ee1889462dab7302b0136550e4c4620e03ebe9d296ad3b922e17bcbf5f98b22d3deb273a14192c7ccc6c0f18d3101fc65c4ccf448e03a7e5825065ee0678f1d8fe6b07d07612709646ba62a9a82291c9c3442670208d7da518440aa27faa5401185b5cb8eae04682c2c6a4aba5bf364ffd72d0934fc589e15b38ff65faacdc9a02092b31fa230ae32c71a4091843873a112f01ad3bc8d242d13438b8b9b709f90973ead73f3ea2206f59259e89604c6ea65eecc4fb6b5d8b02058beeae529fa01d354f27f4ada90ac6bbe8b537163c9389ab8cd48808e35d986e751c9d9120fcb779ccdec4517cf9f248c49a543a5af8e2ab2eae578712b4980ca1307bc5950ae562fc08467d979b5b308c2cea24a9bf4b73968d2055294dbcb5f1998086853d510ca35155f9dd9b49efa7ac1ad9bbc9dd87727b2047815500edd6a65ce22202f7494970e1740ce11985d8d3012dbda89e112a9c9ad205b41304e604dfc1067f88535c14e035e6402136230928a14a07a38a76be4f6de03bb0152081a0ca5f236967404d552db122f119458df0a43a4246d0fa86849b5153827450fb02b8023c5b39d489c4f8edf34aac6beed92fcece886890b95e04ae355eb9fac6d0c6b30681b9876ec2a358b87cd1eb333453f0fa02bda1ffd976679b7b6cab22df5b9d6cbf336a98936084435f7fa73d6f672aa3544c7de19402a651dd48266e9e1db89f4b4ae20558c055511abbd667d4677f139d60fdbc1705fe389de7b15f5c8fb7b1421a38a1a8291a642336c9959accb3df1cdaa7673ac713347a79454fe4c2044eacd61b6401ab0a86d83d46b977f1230c165482a49557ba44e305721f9fcc4fa35ab81e4ebc7ba453cf0d173601c3520f5d2be0b665c5cbe578aa54e2195d5464b3b6c3006f16496ad7ebb55f06ed9ea8958386674e7b970d2ebcbf60b82839167013f9c725194da0fdb11b2d1ff5687b8d88915969410ad6b54a9b3878bcd945b9c567b3693c5e7b5c8248c761d716beb32c35fb9ab8c07c038711dac0b8b1a028e501f4cd6e5b52b00fca01d7b79c44f7318ea78c176018d5d343eb6d5c26d31205c93c3028d4757e0722a04387dba32d1897d71dfdac0b340ba6e4f80ff98846f8f9c3f14d6b7b135a33b800ffb0dbce8118a809b855859beae4124e25e05cac3b6074ed4680f05fd8ffcb8e43736e51c8cbb8e51164ac375e42ca03d4756604f51fc2d3d12e0675ffe2ef8c4266f8d8733593a327957db5404551b67cdc50f3dff4a114f903122018af45ca713ae60ce07bce889964cadc68873798b631668c2601cc4650491403e108a0f14ec1bb41ef6edf3c501bef1c57c3ad506055f9afcf9add35982f9d4e94e963eeae3795f446f5e55188e8ea043587f39bdeb445ce3a2be9398fabfae5713012d4cec9d64dd470921987d8d3071ac2890b4af53ca15bf7a949c42e81a360faaa605ef6932d2526b5d39a842f74a4120634f2a814dd950c5823dbb613cf58c6f23f918fb2a01cda49c46b31751c54a113144c8f5dc3d3d150256182266d9ff37faeda072aedc0b75f6180f79a1377a31fe40238844650bbc12ad887b98bb7d40415696b85fb4b7104720f8acf75fc321991c243defb4a0a9bccef0366144aae3eec48930b159cce51122b9947a9b49d0253b51ee5634877b96aabd48d540ed3eb81b6bd7bf72ad689d1fdbd12f6de92019b1962a47fd731987432b252cbaaeccb167d559e320fd2eb0a29d45cb17481a59458e7daf60499ce341261e6469c21cec3a29666997d6ced9df2c43f00b6a4330d9a9d211c53f67e0aa2ea6d384eb8db3a8c7918855d8a09b7b860d5a26863d0bdbb77501f932a1763122cbf5c861cf8d0ca21993c9faea924e78f106060d55394144448e093c563f35f8132b24873b14992c607e30fc00e3462067728aac201c218d1277dc879e90881d5dee81936443e2c44c44a94dea00d8cbbe1188a01adeb9470cd43c232960088963dd57e8dcc5a0dc623e3a2f240b6c2f8fa71814c1f2a9343af421e0fbb71937dd0418b4e139aa3e8e4705198fd19e7f2dd17789c99b583054232e9f4c86630419b4a98a927b4e144bd6d6457eb6d2394dc88cfd670b53ce52f6d07031187e483423a4b0d71d151d06ad9271568b44dfc17bd4ba9ce8662fcf1f6502a7218d95157061089a3c59ec244fd20dc4b013db5c930345ee9ef47532094c28bedd55c1ed6cb7266d218673bc4059527f4d7b2228152f3072f9008ebd928aa00defc0a36b41200fbb560a5afd9bbb75421a716a50cf73bbf931298b9ce11fffb8b66d340357fbdaf052e3d7d780707466c34ca7244a2176aeb3af7ac399e4c01f78a3a2bb08b03f372d1facef0724630ee5d02a4c4c9a472e9111c41fd15bf7cd4e53fbd6b46ef9081384fc9e26878f64eea046850ea6542641afbd9f6e5f099805f07d653805569e9d255d8082cf9b17ed7ae09324e2fee676a229a31cfaec2cd25076964b8159f8000daeff0fd5b3d05b46bd16a9d442ab4d10bba1ef9b3d09cbd7076acabe8b14ab7b104a2f5c3d605c555229c4cf0ee0059d88ccb70ff64aeead66c123383a1963c564feeb173a2bc5a8f0269204d9f3e800e81a26e4770d86a3cf0a01696cab4eafc63c5319fac728cee1de8a6aadc7318769f446a1c3a615d56895c0c083b9e4cec9d9a031eda7a2a6323483a191a00cee20634568a8196fc31c4a9202670b849c29eeb77d4151953488093c08307d65bb803a178032356e31b126dc44d5114d1abb88ff8a7b269bfc44cec857dd367d0a34834303dc3b267f92c24a5bbce128eb2aad36f083c5bf9f57086b8b188d9a2e1f3629f69cc015f46a7ce6b695751057f662b66f2a39149adf8eaf49f1cc26e1f077d777049c11b04973dd0b6874ae0e16fc4c9257132df8e94497050e5d0e3eacf7102ada960c1f60e21ec88f602368f8460618481171765be32d006f0368d410f8af37de3dcec3874db970dfdbafbcfdf9dea6aac3715fe65de2ffbc6d4948ac046d117c4ffaa52959b0f6ce7dfc9d886c57066109a70cd915650ffcf72cdc8a3688205c1bb20b21f671c09fb370abda008a726c58b32fedd53af0f774648b720023969b17a261dce36e9d91804e48edd92dd78489301a040b3c6ed9f7bd0c164cc8168b76f3dfeebcc4149282287cf1a9f045284adf5d2424744f5e0b45a99b9b84d4b6e84b19e2f590a67056ad0388e1a1fca3cf45226921a16ae77a2d0677442ba868a8da5d47ac1d97e1d64a06bbf432e0bf21192196dc857570e960989f649f0b847e1b29a8254335fb07435fe24c56de20537132b9c295d79ced8ee178f3378fa57f5922a86bde1de787fd66bd3b5899924320e717add0580cb8470bb87e4d1ee031923bbf90d281c53d0170dd388b356dc5abb73763c33e735bf1e5ed98db100fe76b60b192dd1a5468e354c945412f2e5cb0bf5703b259e8457cfa4efb02589eaf906368746905fc7a46eb50423dc11ed40308db9ed0419884e00b564c1d260d26cff42199eda448e399c1032192bd99a5018d2b599d304a3f9fae12827d40dad045f7ffbc428845ce5f1058f9a036bacd7f9a15a58cc7de3c30cf578779f885174716fea7115c100a0be667273000756c534b0c0f5d3930b7b8cd34de5402484ac27a25fdea40369aba21deba86a8b92236f3a8bc7cdb49eb56382e87acf016916cf121b810c1dca811310710ac196751d362b176f1670e9817f39aafe0c752e89fc4743f083eda8199c6b786b65bdab24903659e7618e0dd4dbcdb780f923a17130a188c156c919502bdcdcd3dd8a47b374eed8af56d118980510496da1e1f7b35d7d97ccc4e092974227e40c755faa256d0bc8af05000b4f1fc0065ec1b702694fbed9d5bc24e66a77c55c1ee1aaa346289c2f6d74297a45f9a95690104c7bfb5171c387f2e011bbd830ddef552c26427aa4c0026577a4b19b5ef9bb60a28f3b037c71435cabd2118d915116d85f7cf1807164387f70542ba94f8ecd69ca9ea8e143e0d891478103fa0e3dbaab2bd14a33a96d3969353c09d3c388eba7517504155331889a5270b990828a6a0141f4ca2fe3369c610ceb0c1780bd64aa88bce158dc8161d6a18eb1e36242664e9fc2975afa7b705ae4e8713fd9bba8447d888cb85e56d2c3b6a5a0edc0691f01bd91bfc6ea5a2c446ba37a5a5ac9036f49971d505fdeb744e7b3a3a25048f12551041bc1b0281bf5bdd0fb304536aa57301d93d52e32dcfd35529e2cda1173f41e9afbf64da329b2191c5060e54da2a706eacef46da7740d9e4cd5bd5d9e9794f6d396644eca0cfff3831eac420c56f201a7581a067053ff808bee963b8fce781f55539cde603dbda54be8fec6baaa5a1a8945095b95a8aacf39f714afebdca4ca94f730bfaea55da094b3d6a62cddcc1e4b8c70b5c151ab23160006b842db4db43869cfe23f091af6f25d3d94d2205e345124dce37a45f5dd94d9573645825eb0b4905d842b1588cf1fc66460cb1ca0082fe812749a30685f13a752684d0fe31f261dd69319b1934868b3ac8bb9549f5d46857400ec90d3278e8b80a01c29f739e08f161c7246e63868975b228eaf0b4b48f1cd7b1b68a50f2714285e81687212c90bb24df5fb3b03c4b1d71256ada02b18e62a4b40dbd0a51c1abf89e0509c0a0290570a91105008dd46e828958e065df346437418b08602eccc5a8a8706e9c391fc367aab9414b41fbbdaca8a36c6f7db75127984e238df3c6afa989c8c133661319ee09ebe147da64031b26a6d72843472b14ddd1787173b2deda6b76911626f0115c7d0f90364e5ba954011b7721e67d3db42dcdb629a5f397d4a6928ac968e1310b7aaae734c4eacbd7198a4c29b22a2c0c1cfb05def9bceade972f96dee57983f90e4f7359591fcd83633638f7c8729177ab9b629d82a2d8988f2f55b9cd77f61d61526b2358c3e23fc8158b088a2903fa3e2112260a7ffc6775196d592a6dbace5ef9d554273bb2c081a834c502f10567f7b69a96bd7becb8d24799022d20b3fa49b246059b5dd03dc650fe488f5d3f49fb7ddf00b10a03a0459f0ebe3316b5003c91df43bb81970a4e68b3a993c7efb4936a88ca0563f8a924dd4f20fd912f36556f0ab1fd5355daba33bd672c1ec8d4fda8840f5cc50e76d8c92366dfb6de505d9576d2b645089f68b54a04d4e7157ffc93025434e9d6438042756240aff5600cd34d5899f589d85ab7c544dd44ecd62c3152b38a4dfd44ecd46db1b26e894d8d25b635ac98a95b6253b7c5ccba2536f59698d6b162a56e89499d2d366b9618a959625b6702cc57a14fe844e835ba489a0a5d849ed3f4cea522ef15b7c86dc52d7aa335f5abd02a7411ba1a5d24ed432f75dea157971079abb845ee29eee2e6551f8bbb1217714995585a53d7e97ec9a96735464632c49c0dd06bc24af3ab2904bf894d674c80c031de7bdfcd86ca3ec43ca386b8713290fc08e1d5ccdc147878863165f25f5f736f3c909bb334273290be44843eeba8b7a152280e23634e762f05b3d426ba39755d4ae6d58d6872eaf8e7070b84332d374affd5145b8e1b5f70e8de0acd521be872eeba2a995337d0e6b4e39d2f36a61c345106fa1323f4ac4bfd864ac5a951826f8d6b53245243fcacb0409a6e5792d045f8813e3faabc69e142ff6a88906c143313e8d5185192216e16382819b6c2c38375419f0743c04c385661f63800e1b2d0722b35a36aa0c9b9d3d10bab5be897468e9edd64449221ee2c40580ad95a2793bdc9dfa0ca4432f4a740089e31fdf9ad2954f49cce0f85a95a67bc9b1041d0e3c9795687366a0335aa228c0ddaf3a873de05f89f4ce59739e6d5c0a16ca01badbceb4a6039052944add5e5620965b3c23b1b3bb12dc4ac1af48fb0ebdeb4d9f407c489545fdbe61a87d8767d6a59789731a7b7d6425554e2de8d3a8d9146644a5de8a8160bd35407d2001b1fc9d0970bf406800ca8c46a3e2372353b809ec0f5512865b8d4ce35e8757128356c3537ca6bae487f6fe9ef4aee86b33593c7ad3f91f1a69ea4d320f980f6d11df228859522c4ef016de1ed5a0fd83c75c77ff44c4490162eb7b7aeb9d09e67ba2d5e1e10e8b9333dee2f8827a3a599f5c8f92d2616d18c70c8792dc47e8f9e57c37997c3bb18faeb9b094bcdb2223f7cc928d50d7903eed5ed075db4fbf7da038d2e35bacaee360e32530e6bf7148023782b8d6c2a57677e0b7b7331290f195a1c553ac6258077f302802ad719b061e51704909413ee991b023e803f4ac73d463b70752e01abe0f47fd40abc1066b47a193e96e19a84166099ec8db72757ac0b70db750eaf60d1f4e0b14e6e6059f7d603111b222323446e590afcecccc5d912b68199cbd2e3626df3795b0aa3d51592429a3864720e0ce282dae72ef46bb6457a62114aba1d87f68333fbb528d0c5c4968b8a2e967b513d1584c02f9db3054013d6ab21876e0b2ed850f6bfb65446ef258fc5f9a2742c26a79385a8e013ce7ebcf0bca9d4b23eb9102a3a863705258ad421e65ace59f25094421a05c7d78ef1551db34c33860d41dce86cc3b29478d8cfb1b32437b5ec83b564c8fbab262f57f1dc35494cc05d173fca3e7d66b4c44306d745d3e493c6cbbb376741af9dbf79edea6bdab8e0f66b4ae4a0c2cd6f29b35cac8a8a1b10a578d45cae45465d7e0cd4cc6ba4bb633be6bc58c2349be8b44ab0c04c610c8360eee658a387b86bb9cdc26f0f62b646448888dc22ae5f4eee2427afa7e02a41ec52bb9b102506bd5a5004890a0b92013359d4bc787f172e65f6e90b9966ea3ca141c553537f855ca03aff035c15f5fca98db19b3bced55eba99a64d5740a753c647199cefad8af3391d75d3e04085de2ded4991928a7a11bb96cb6ce5ef50ecccc33344c0b997f876422e19345549854a245566188e46f6212eb703924afe3a9f24d6235d185c2d4b5646f2b631b722a6471533dd4099bd7be764e63584efec65ea484059de0d8cdae4c8a03f6888662a2cde9f7235669b4a6d54305042988c217b52b457a6dc513c576d5fb8c75d948f70edbeedfa7e6b42ec5ace66841f55544cbe5626d23ff39eb34e867625bc2a916da3683153071c5a9dd0b8fc40ea1d2fb085220c619b2352c9e7dd7a6050d69c29bd0aa4468f65d3cad12a9be8a603207ba370bc94dc61da06d45b251416ce0c40e8e64a751646cb880cbc98f8d352084c08ded7ecbe4d348ab6a4bca70ec3d8d4524ea6750c93f8df84d7723389bd07e1f002822e2c8f0385e29081a034e686bd7812741d422e41ea80c07a48e8ebf893f919dba523de4f0cac17dccf0a9e8f0e2a68702c7d0b24c953b28c09e948c76da2173022f685d952f02d3166d3218ade69f9f3bf12822175575016f2cda1516510316f748de4883025b685e6cfa9707f969ec0d5100584eb5b9892741d7986c6cd376ca6476a2394078babf3812f4ab54bbce4249f8feade6890920451a433e8792f4b9d3d0fe2638737e6864f2f6c41330aa5348ac098081a79ef3e5427beed1af3697aebb94d1232050c5908897e83c50228cf2fb64e4226d96716b69e74c3e624870fca14826145c9932d8c35691b06ef841cd63ff64e60164385c595cc387a97d61e6d37eb66d655976211618115bd582221bee00e457ac6c51f1af2e318a8d3db74c04220e655425f7d5e79ad87b1522d8ec2687ef7262089f29a03e213a986c5a24dcf1795788a44931beef620c7659a92e05d29e28969989b0a51448d7fea1e3e71d3c9f19614e6dec02d751837cb4622b1365fe6af4e26fddb5ea527b91c911de62bc14a1be8264c3a9b1ad5f9eeb8ef4111d93bcddf21bb19976c40ac267e1d3ad036e8d2789827b454d19e0044e6ead73380fca78521072b3f1150f11593560de3221535989ff4fbd945210a165e7b3b64dc10b81eff070991fee0638b922e850edd1beda89cb89a235d815776eb4e97bdc29580e06f9a4c8eea1d38dfaa57c00ba66d9261c95dfad769fc1d093d384fb409da460a661d46bbcbb8528a15e0431987b0e127fdf3ed5fbcbf8c8f7f247f498dafc241e1bc15a2a1c3100f583b6c3cce3b1a01c411351d67c115e5c9684631d62f3cce1a3b3a715fb98c47e16a27231a26525b169058074f8266e8c13f0066448258119704a3ce38ff69275266c476e813ed9e8e3ac15280445e9ca5ac79ac9e8697f3511adad38445996637a0a9d991c210e0e6635da14ed27e2341765f9acd31be276460e5889258b8b0e944a14a1183f9545e8a269e65418a1bcd797e68006336e8f9b3878a5cb1c249be645db5ae082b96da1a733fc80893211e26879096c7875191e1c0525ad47553e3fdfd95669f615cb633714446f277209305e944150e51b25c4241c4fa5a78dc36a43ef59311fa037c2c7ba36b7e496d79b8f367a9719bc7d3dcaf6b4ab91241777a96633d3848459e67c97f6221b6d930da93c077ac00673c02050be1c0e377d3221ceca73ba604032b79a11ed52f060337b93fed98880aa59b62deba0a5b643c28a3c2bb828e4c7013896b428e77de38a8be6b7bb1a6dcc5bf5123fbc5131719be4fb7b3472c0b5c7926012f66815426c5237780304f0f7cfd97a5b3e6876954ab7364979a684fce05238cef14278fef58151b2b15b5f174efc8191f0f62af8a39127d03e202d151ef9dfdbd8ca88e78e6a4a21d7d0518455a808094a242da9880c5aea13daf2a738156671645633f2f97bf3c989ef67d268208288105615e815bfd2c3cda7becf26987372987ac78477d41d8c72411f0398372f62e76334786bd69f513618e5574069e9907780bb7d3fc30dff107a634554901da38ad7e55c59e4154aec379ab9bf44a37698a4803dac685de8fbb3836148fcf7508ccfaaaff4965a17e0d3ec3e5daf83e21182e220283132958438f5d3ca8d8e13f4dd58aa8497c77361315a7e3f32002ba45edd71dfe908219df54a224404c0050f039452e666dc8f7525863d500660dc310638c05c64ebb6bef42a987ea4aa8282096201303faf3198094d0bb39f4f6d3fb96e3c58005902cbcc16183307121e874c40be7c1ed49a4c8c9d1ea93a9d41715c3d49b982ff2548a7a48294eebf0d0965f05aebaddcb9e5e7c87a800cc784eb93ae714b1fdc371e40be72ef83935014be7c2394bf1f3a3b9e8439b198fe18278a192de50598e897c820dc1af316aee480fe3882b29c798770df08142bc576b3d1c06c8e64ba35732c9a665441a78072f17c06a3b5c1d90c16032cd11922e52fd58ee4e5d1d0fedeba112c8c2e6bfbe8fddd5e18d45d1504a78ec7bf01afffc2f1e2972b76cdfe31cac7feba73ac5d6f67e364e28286035b596f9e76257752e60820136d13ce2d1f1a0200a4e7c8b7c49e1886bd08b6fe299509d12874f6f27571293275c8f8c0df05d2546b0df69de7613c570dcb44bf939993bb6661c94466cfab4b3229b5a24d745862949e11a01927c8487a81f7b942105fe39119c19d04fd431c7c54830be4cc7e221b9a59ae01559099f518435f0e51d5e0afa885bcd9ab37a51483c7f1c35950853656bfedcb44c8617300abd99b4a8434c46d4bb048216699c050b0467a540aa709abc2cd28e027f4c4844d9e4d13a82f2221472e5d0dbd9aef8af873e78f31b0bb3c8312961af5d64a5b508950507f232488aa07efdf02af175a054a8a3c8443831aca351b99623203e4d3dcdabaca5c22396f5f87549a8ec40bf58dcd877f62fb119da903ae2e71bdedb01a6fb8f199069896726380b88f630f1167f9ff29288086a2ab2afc2798ecbc2a57829249db882508595b70678706b80b75cc9a66507442c5664dd1c4238a852a8db5a74f5d1d9df259180a32417a7202ab1ee297e2cb6495069d31dfe1a86a540ba952db40b11f4d2f490af9338608fd523bc03a7cf0d8c104b383d333d3c5a5ebf67058274ac029e3ee3782040100ab81c9af3b6788dbee636d45fd691ea47a038da93c463ddb2d1e1a10fcda4dbecabaa55d211e45f049c73684fb4028c6830b1a27df6ff1f50e3bd5c2cd1a9277bac1a482324dca204381651b19ce072e7e2943c0fcfb2a48bab5061383413e4c5e9bea6dbdbadc515c37b946d91a89e2de55a0b410e4b2d5921c2369d018989463e207d31ba878fe1bceb11c834073d721fbc811a0e693cad916aafdf754f660623eafd885b1486fc17b9700b83c67f09634764c8c1d5d2f71b6dcea5af1cbcb5c5716547c9663ea590cb04b030b7d0c265a1836b3e7e532d931fd3daced5bb9a71cca5dd349d865eb76f5d0bcf8a3f9362cf903a21bb371df78f910e71ff308d47123e8c0022688a6779110752990aad3505327d1be1d2c5065eb5d227c30835f3c3df99eb04c495c1e0fdc788a062931650d0ce2eb8f8f0e331c9608636723d693f65838d7423a9fea7cdba58549649b70c6c88d315afd410446d2a3f8fd314aeb4924a64994f43234894ee83ccb833ee726c456e54e789f31da0e5ea8e42e5efdbadf820fe436e63a745295d99d256107c5f4f309b269d15962d6552bc05f1d6d7ca96687a34b538962deddb71bc0b8828009f2bee16c1bb13d69003f8f8d5d293543a3db074890456ffaf462c3671d31d814e9818704810266a0077cfe8e80f2e601049100110820a768df4a8ad59b86348c8d9340289d1b783b72261862acb04590f1cb942e7880bf9d15fb82a1bff0a297398be56104559ef9dc02923429d44c7d48dabe16a2bf9c2d9fb6948af4b07eeb1796bb3a2ea3bf03042f8fe08e8a5cba98a3c2bdc1aa8daa966b4ce33f2091be29702d01ff03793bbb28a90ae6174683eb42546a662bca6d45b3a0c2c2754947b0b2cc71d0ce55de3db4fa06c44f5fbb7e86c7866f7b0f69525d0032943aae4f8c6da68513b1854fc7d33f39f1eb793e391bcec960ed8fa13e72a4708cd422cd8667435409c23c086316d6e95f9cbcd24484e0200c51f1345f19e7ce399158a101d2b996024a9e618a633bf60e69ef6662b698bae64ca1169e618e4a131c1473c3aebb7bb51c5b56cc45974c56543e4e429043eb184b33585b6f65b46c5a7a24609c0294e42b61f033c4883d6f4bd45ff29866c1cbd16d9e2b9792c058150f08ded1555344293a6c60aad9cee94754b5d1df7edf6c27d9d2bcb68ee031c1d0fe2f9a3e09325081949f595cdc6328db56478d05d3d9993583d30dfc5abaf6955d62b9c88af3ade3a616fe43d6c96581d94ded6777bd93142fc7c5beadd9de98e9164b5db95f901e04956bbe3697c2588dbd3641262c62614d76c71aec8551ad2c0ffe446ca543dbc9fcaba7393cad052dde5a40981ba3797876f3c494523481a2ec864994311ef17e399ed0950688361b92b3add8fe1f6594f4266f7308a201fc996e70ff0e3c49fd222b25888e41ea59931fc5c8f500698c7e7ecbaffd4d1d515531db8e36daa17ff1f8f20b3523b123141cb6d0d01f88548302b9b0e91d695e9295c4531f06f232dde5b8e478938fae1f343c10f56bc1fa5671ea762422d141e3252dd1bbe47fa8587e7c86849128987700bd7be02e7ea9dfce78b4a29340ce32e46ab2af33350eb33545b19f9a755d8151c2c0f1105f55efbf77242939a5422e311824fd6a2216786d2e021caa4859343ffef76c92da05a6284a00e851db55a6e0174eb6dc6ca93bd78040d22b741bafdbde02048cfc9230db634352e0aa6ccf64a79fe8f70429d03c4f5b877c0718152a8a1f983d493b0f511857623817b413fdf4a45849f3dd54661280a1abd82b0b5bea0f5120a3d882cad98dd27576a9496ade2ade57ed3ca8d906d387426e25251e49cc6f234974cc01bf65833faa73fd2ba24b24a0d7e778e7f3c50c776208b311deeef1d38d17bd2abbb81992fbf9c7417789aaefd83533548d51e8cd9638a41b837c5154c59d3695546c6ee9547a9b709057dad62f2835c1a9b5c8f39a06780190a6ff1bff3866d9abb75c1f992dec621172dd6d832b9ab4dcb12cd563b3ef61acb9f1af8cf0a54bacededa0c057faae705153bc41a1cbccab13022456710f8b6b4bb6b553aa69c6e1e8a321d794ead798037d1ab123001c72c5473b584bf1d6b93f7dd808e53ead9e2ffcd3ede3645a8eba1ed7a46c4f4765f03ceb87e320e00c017ee7c85a8f30618c78e9d4c2649a0917fe973484f6530d90130de0a7f654e95e0b580cc8b88fa4ea3369cb61da05cbd20bb200775028583e4851203e59e47852f474733a9eb5179fe8c6a9a7d1744df22ae769fda20f3e30d18e587c3073d34491f720f55182a197b01268e9b02edad66a27bd66e4adaf2a7ad956dc130177a5d8c4227053e5de363245e81ce08fab9221998b240710603c4c86a86d79374bb309d4512e812bae47ca6bc8b25cd0d6cdacc01d9727966e583d578a74403700d705b738812e0bccb420a82b6748e5295fd24490dc83fa927b04117d0c9706a5bf6097eb62d58cca6049aeaddedb2ef1445b709db420d023e9cd847fa0ea99760562fad678c74c4dd83989245ce4b84c598052466726e3b3bef80f625234defda029f7e19dd6b6cd121f5fc18a4e8e76b042276eac58ab749115d048274832bfff6a8b97cd3be4b2623bca3969e9d03ee0ce84e29f67772a7e9f627cbad04cc6cd8755fb05e4bc3c08e29af6fbad1b27fa5c66f6085d40950935fb29764383bf533cebd11a92723873eb76d89704e6c636fa07fcc8297d66cf05b8319362c75a1a9f437c6ec6e3917edc73d70814a9f820e52e40b0e9ee4391fc36e0e29790522c16749e1fe1dbbba8250b578052b7f2c47e5006487eba13664beec85dbb9416a3b577b9b5817044955317a49dc8febb45a71e44b9255858dcc0d1f08bf2b325beba7ef415587676ef18ac132e8c2c7600e0ed81f5ad4850ba7004dd3dd7dcd972107ce5decce04530a23b89db9aea6c2b5b9d81d8fda65db27d4fad00898b57e3389df915fc6ceab1a969089c58f17ac435ff2ffe7efe682c914f251ca201fe7bc942acc96eb24ddfb055651a4079abf77a171d3e7845f016312241e63428d5e877128b064905a7c5a9c53ce0c88aaf83d6d0c0ab58ddefb34858cc5547f657c2203daffa9685c30ce3a4e078073228bafdf81b1b00014ec4af400aec164b05dc7f1cb4fa7eb5df4f3296fe219494623e3803409198098c5327ace08148090ca9c89f92236482eebd448e7951815ac6ef9beb520b2819fb02fddc8eeb6e59652a624657e069d066b06d66edb867d45e9f595f49ea3156c2f94ce022cb2e490c7cb822c2f2cc902c392642d57a9f70e0f7a8020732c346bb74abd59387474c878a8c28d3a4f86e74b770f4717dd7dfe6740b3cc1d8e12c894688a58a7fb680079c810793464b673e003f05236a4a4377aa8c9a186c9efd9832483e4370b7585c61531c370af76bb232bfbdd6c058504f1d57ccda4a323241fda910fede8e8082929c9c260a825c1504b4a4a8259ebbe1a826febdcf0f3551778944716d62d9ba43f8420e0f5d511d210f78a34441e5d6cdbb46436a3d13034a36168369bd188888a740022d201888888a8c85ad7b1d6da7babad3ab3039be34a81475695453a3aa51f6aa84108db5325a94a92edb13122a2a22640444d808888888a6638063ce219fef087b1bc427444decd0ef200e9c46cc70e17f058935e5e5d27697650c3047450cb1037b05f2e7a5fe0b18a99a8265527bd9a36dab57d65d2ab69ea882cac26d524d4113bec80f232f27443445e4d3f62424a8a845a443f62432ddef080eb4b2cb1a12c4d3c7ec4a85cd38eff88fd8811c59cc49cc49cc4866254624331a21665368a51f99850bbbe58502c28e624e62406f4c5624e6a1b1149425ebcf09099765eb75a05c479b2773f0f95b3bb9b52e972f694534a29a56c392b125e44e1c2858931b2942d61f44496b225602de4342559ca684994c852a60409f20cea56891b4ab994d41da7f2f8a69ff9a016857ae5342f69c21baa6838835ac439017f45a5d1c2f52eae28a03e852aaaa24f55b44756061738d5a2a3610483e52e54d997790ea9ec53950d679050d0dca4c814e704dc44ee9a41214e034ba6e18893f0cd173c7ed91b5349679d4e83b2d8c498d244134d54c952d604953c5e19cd49beee6a25bdeaa7f9ca184d38c963c7a4ac0926b967330983c768992add0a3fe8dc45cda45d4936241a1254dc6513ebc980dc52b665e6aef9aab06e754791fbe991bb28edd2230e4c22b3c491d1290b1e69104dcaead543caa4ca2a83fd63321a122dddddbdc56ae232cf98b3a9a5d659a525c99d42ee47e5804d61b00375cb29508bdd4aba259d745033a9f4a7c576226fc88093c46078a441aafbf20b175c83e850b724fe3a41dab18e551a963145ae61c7362979c0d5bdd4cf7729a9ac3db27f5259f6aaa594524a59633362ae0d519658a2889bc3142d5a4a52071b2dceeff6496bb576dbee05e2b8a052a9eb3c2ff551d3a5a7f9a7130d18d81f65da91f2c6fc70adb8bb323b3461f058836868523538344f6089c5a74aaeb1039e2dfad79befe6e6c6a646cdcd4d8ae6e6e666e64606dfdcdc707783936d4029d39147cc151c67a6404adb069956ab7d123adbb05b63fee133eb399b69e1624469ad5c5a94f987cf142dcaecde0f1fa34b6b97d4a26536abd2a2fce1a3a54599b118636986055b4b29a558706996fb9d5a6b2dc5b36ef1504aa9879452a7ee4ec3120d8c2866b31560eaffc3874abec2857a3c962754f918936d388a30c7dcb06fc3fbc387ca0f1f2a57bec869b2e1b83d099d6b671bfef0a1d2a2cc3f7cdaff878f50fee133448b32bbfff0216a51e61f3ed4fbe133049576e15ef550fee123d42d9e2c739b7c82f00f9f210f7ff808b528f37d52058c922cab60f173a50a4f77c5497b35ac26cf0af65a9c1f6ef1e3016fcf6393b5e26f9e91a5cc0a954cade8906760a6a9014f6b6a1ba9ac5696840b59caae50c9546685859acaf34ed922901a16619a405b6c23da801c688b7960e01a03ba338c0724d4ad2d66823c7f02017d6d648b89b0826d86a310429037b008228ac562dbfc4ed67ab698bb86d4b6586d8b4dd8160ba10b46cae3044ad2ae08349876e9e8554fe142ae3246eeb749d616d999a5e521ac5195ac25cf1fedd1043262922535c1bc33baad25a9534aa9a5f6474a29957588cdcfb7235f3aa55e7bbb77dba4eca2d0f7bf2f370bf6688005d201ea8174ba6d3185d62553aa0d9a62b7a02b399033dd2e3785bbd6fab83eee590aadb44e0f5a6c2969a020458afb3d72a5dc4d89d4a1a3d3dda21ee060d16a3910abad4970ceff46777c2d46d9aea7f2a5cf652a298cbb944a9a199be5bc28a5c3aa465839611b01366529938dd123f322cbac68a12fbe1e169a2d91d1869015c988b6ad642bf51d1ef4004116a3d9a0ef0655a46d490cf4c5c3900542051faf97d5050e7df1f57094a45e4958af647ef5d0d32b9947fc31614350cf2b2826fba12806282f28d75df43577643d2c1c3a3aea919744db61abecf278b72d58264b59100ed64ad00c5b16ec65290b52a1528162461e6f0dca1725f866298332451eafd5019fb2944159e206210c757a9ab720cde3d016f0cca37ec0f6c7f9a39279d4cf4c4195ccd3c7dfe16e6df6539f8dfffaf56da4c036eda4be9b3ebee4d273749ce3f65dcdfb06ca5c13fa0cb300dd14b25f577f061ce9083e30fd1d94e951de1749cd20f9e3ae54d79f09545398d5bfb5ce59ae5b58512bc02336fd9109d4d9d162fd0f861e428614c911b80008412810c2f65b78fa001022a5ae14aa80949e7b1ea52e1cb72f61e6124fbe3b7c9a76b6d30730fe5aff86f5535f8ed82dfb154946cc501e511985d42d49fdbf5b439ac91be21356eb7f58c3af3e77f108b0aa7bef250d067ba9ba30c8e94d3feaf4e8baee640285e028b921cd0a8c0a9a203c4a1acd8e80cf1f8a7bc5f7a1905048ee0a2257fe9286424221653f0305b9f481a31acae40055ffbc824413d86ba2107ba2e895ba82871099f55358f04c143d8c9f3cce9e30624af21058ca9080907eb24f977cf90d001add90b90d915b18f6f498dd7b45774d19ee92ec3f75c82eba286ffc1029bf19ce9e540fd81fca0d6f1e6187863c649e3dfe812ce34b9a4f2f9fa25e7592efd1dcbd67fa9c6ed1bc2bf5a3587a99b99af0dd756fbeecf85af0a057fd3427508c3a0579510f0f5ac4e9f1127839d0dbb1b56f46987ac0dc9f68dc2df93541e96eb6a284aee79a873c8482db69460db9ebfaf54bbb3fa092c0f35d4a29e574fb19d8efba7b75779752ca3ab77b65db6cd6c161cdb474bddb4aa77c79b8c3efbff835100d04ad75b33b6692268dec36efd692d76e2799439e8052777707e50df97576bb098deb83e90a3cbfd4023cdf3f536a6bbe6ddbbd41532e57ba5b98e9bbddb6ed96ba7b83a6749e90e7a3be5341739409ca1b94facf5a6b057bdbe68c32a572e9640ecd9e949ec3ddb17f1fc66e931b4b1ae9aadf3534e2ab5621c9298511291288090563893c5e1b032388cd070c1cc600c3853062dcd05f24f94aeedc5bc33759cac2188529fa0953b46d255ba9eff0a007085cee2dba2527647061d362c65d96133019ecc5d7c346beba5bc63822dae205872e625b88b0818ba02d42a8d08517b07b595b9a28995b04f94ade2d311f2e927c25efb6956ca5cec2a1a303658526852df2288b4e20e58923f097a54c0a112c609a2c6552a24849438a8be9f7cfd2a157fc5848a8ee4ea385eeeeee141820b0803923fb3f066828628b100c7df18404a92e431c35a14594275624e5c20c99131a3247834992fd33a0e2e68b295d30c05288343ac30b0e0a04c8a3e7050f5fc0b2ff05a61dc7663c015b36a80e28ba08d38592ecafc2473547b3ec4ffd714c3b7e8574f390bb6d86fc650747380ce0e6336a2ec6e8feef0332d9885c68c9fe354c3bfea161d4ddddaaa11d7021c316567c41f43c81644498254280021e3b8491a4061fc22051f1ea973db7684177b74905a80f40f16df1427677f7a7f9d4145eca86150ad08249eeffb42023f7cf3c15113cc9c24a085810449754143d44fcc068c8f2450b347a909132a61d3ab1e4fe8bbf17e5171a723fee0008295e78d2857682130ec0f0d0840b492990408c97bbfb97ec35b2bbbbcf156652367238c0914594dc4fbfa090fbf1f7226ba775b248d91839b802c8005734818510166358ee3f672c394d2f6523c795fb2ffebed645eef7aca4e0a48a1db4b8028d987b0b59b660c1a5cb962d7c7839cd22bb1946d9dddddd3b2395145f1e71505105942ac6586183152ca8518125f797ca08e286d22b54a090fdef18586ace29b2647f6bdaf16b44b138818653834066496597c2082956907d8a22b27f2ed3c0e2ee6ee38516063672a0d8c24b1444442145ee7f29a8e47e2afa4b21f3045b6a8450a3f9458e86508e8929460eba7841f222c6680685132f36e46661cc0324c58c274060c941c919af9fa12c8248e28408885ed2a896a71442af4823b72ab7115ee00f86671e534f5889a20b50f6c75d5890fde747050d1579cce1c0091c726f71220c2e57b844f184103a5e38aa96fbbbd9013d41ca4b13845b85dcdfdf73fbb13467e688931b556467286a318f2821f0a38adc25af4ccc51dec21153ea40ee8c823f8f2854915cf55f1410aaa8075411aa08558ba1ca16efed16103ab30eef2ccf00ca4a5cc8ad8492095342498f93c938b76ebad18d52ba81f41da46fab8d7b34f3d9d1cc8f72ba3ad179168813f177138f3926cfafcf637e2da223df70f42fe1793c9c6ab17164c09f6b35b2b47242934cc34ee529a59473db40992738278fda038c20e916ac5d1cae2f93e69759a3f768d676a84d30f5a8c51a174da34ca45448a9f0291646ff5a697d8f869845c3e3bd53acc76da51fb7bf2de2dfb6108b4dfb3dbca9726ab661112f53e7c946747fbdb4fd589f721c27e652a9e4953a1b3c3935e79c1c374b9d6732795d89bba1a6860dbd76ab4eb72953b62953b62976cac5c19ac2ba1a9037fa87c81b549c525a634acd748dc0dd57c3144d8dcd8db7bdad352913100bb88fedfd67b61b21705079f60735b953d9d88a58ab99a4c35d90320121924ad1ccd8b8c02b477fb89b754c654af964fae0020cb6b982d85ef31a0bbfdf8bf1f76d30b71b7029a34e18645063f26237b5a139a727bee9b1299c4eb426edc834413102b33857af9bad9bfb74c2d26cfd0a84349cd62bbd9ba59556eab508072b0a6b4e580928c89687455a8c22c5faf68694525a27373f5f9580ba4f1fa0e4206b9031a48aa4ee6669d57117f7e24a06c682cd1564299c3ee4e6a0dccdd2ea43b09eee219d345ac368301a8cf6419727ed085a9769b7a8acc95c145cd3355d748c8aab07bf1b2c7344f2d4dc550a3d1c27941c6a30cd154ef79a30f6beaf9bb3366b6a4ab0c934adbbd5c80eeacb7c7d7abe146893100f76b80e1d293a34048f7c0764cb43125a6c232daf7e7bca2e9db650d66de6d67037325386ca1b25d104e57d15446976b3f3663aac1578c3a58aa4b8bbd9ead578ab2c4d25f0bb9badd424672cd16eb2e6aba63f67cd5d73d6e6accd59db20cfa1138858c8fd405e3e28654fd980e7fc5357933929185513b38171f34ab9d94abda5bcd7451f67f9534aee96ba372f3f7c2f31ca048403dc9d73ce10753201b100f827132b0c76f7a14fc47fb1af684996326532afdf52e680c0b65aba719e3f7edc18e3ae54ea24c6df8ba5ae7462f57432477e0f77a9e4bddcdfbbc957d1c577cec95d07019d1f850bc563a53e7cf813a929742648f177001d83fda50fff241dd69c916145d6c96eb5e80bf3bff9374bd3645941c18c238ac4c061280805af118bc9f4e50117c600e3890a5048317ac99719e8a4cc9143e6574741240ec692a397099d0f9d8489a197093a76b86186177d07914cd8cb0469e5454329aaf93c7986e374169af879995080316292a0496a3f2f1358204082f2a2605f89e10b31885e48e609518c5e26b80af3ca7b0a33cb02e9e560a7c4abc1994de8a2211f0f789448464838e2ae9a9e997719ec427d0d98a45f2afc4b8acae4a542d5e9e52bf527f0a640ac53fa6c7c5d89bb5b0df0ce80acdc5f7209c97cd50855decb578d07c5df64496072b3708c2c2348dd35a0bcbec29faf5eec557f0559d96b6e8c5acdf83c4c0306ad94d2f9b5bda52a0221125388c4840e9400a2e825e57ef7def3bc4ecff3c2cff39a799e970ccf4b3ece0bfea3a47d281a440c7e96d7f9b0ccec80276702fc807a132448b76ea82ac92898db2ab5dc0c771d7863a8210da4d76645d110482f6e4707d9425a08c5fc2f092c3f65023c5ff3468ccc0c1b72b720371248c8f03fa99fa6116015f72aee7d85d302feafa7a7e74fc9e427faddedbd3d4de2bb87664686fe97bae071fed4bcca42e6e68fcb84f65f14e74f95094b4be0b18b28aecd235fcdbf5e38dea2b0049a4f322d43c8b450b142468f152da8e469850cd87492b37cc95296854b98a52c8b0f79fe748bc904ea96936ed1a00f6cf670329960ba5593d4fca4f34d5e601a8e3369fef894f963f7501a4e30d37d84aafa32dbf750855f5e1c2c16e73a3a746a920b7f98c81c44c81cf7a751f774cbfb8e47e74e726f1299a3f3fc89348fe60fce0a3dee60f02882917c0a896cdfbffdf61b47254d4d2e1c8d6422fef403ffd148a6a8efb8d9cefd3681b670fe4c265760958ff6a73b5af4007c021ae90142153c4a30fda9247017b9ab6bb756542baaad80618e031e8be8c853a8c5d93369e06422430771d764d2abf94eae10a692e79c43e439abccaefdf474ad6b5dcbb367739eb9249d9774cffc49c13afc1e77cd9f39bb7b382ee68fc37286f8be2959b848b020f7cfd41b6032488cc9fd325509b521f7631a83c1603f3ff749ee47cd0054aa4244ee3f81600e754ce47e530d06832d29e29a30c9fd9ec76030580b5a681528727fe7b4adca97dc5f6a24180cd6c2115e681533723fd735180c06c3a642eebf4b1c26a324f76f30184c06194a5272bf1d82c16036dc305344eeaf310c5d188a4c5ce47ecf0106a3a16464e4fe8ed53825b97ff6d39b255cde2b3712569ce4be36b210839625972c65486720e52aff76a6fba612f71b388a79bb77db4cd7c32615e0131993e8f1cba02691d78a0426afb2942115e5ae6b6f9a78e8c8d3ebcc7cd4f4709d41d1cc99bb4c0dea073ca7b5d6a65276ce223a72ffb4a9015e2cb06a7be92bd41606c1beaafff9aa9edef4f57194cc84e3bdae1a9a70fc6402244042a27f235d228cdf27739c70b7a9ea9f7af278eac9f72f6bbb17e3efa36211567629d87b9a806c5c645a82826988a34589d449d35ffad5fe0e671531e2fdb85bd2482321c538328a5ab4f901e79eb3670d9e220166e1d099261e276f0598e694cc71ca2e84c5f27bb952d07c1f836422f447565741fb4fdfd222acb63a385a741ac3cfbab9ba7b95b57a82d410eeebe3067838fbfff3b7bfa500a0819f847abf6ee0f662ceb435fc162595e172d062081df6d750648959648d2208dd9adfa35b93356b3709357b383fd1b3984378641571acdbffc91ca76dfbaee6ed3bd901f6f727610b9eedb5fc3e70d0e60fbc3639407ec99d3401a144e8e63ce68f2454ba7da5140af7d75d5c18c24b06c684d5a58c16a6fdce823e280c56bd7f763842eda88f3a4d3c64a5750333ab7cd40ebd347a908139d1c99c51760dcadcdfe108f64728c07c12be7c0079637e3f10f7d000529cef0dcbe108f649b8d900f2c6f4ba846e9f929e44f06c4396bc315f55330d6f9519c7ec09d4c99739ed94be04e703ea1678ac3f79c9d7597fc8b3f290a70cc7aa4318795ce5d95d6958632d4a1450f66d88c48497f4557520b8ef55dbdce6b3dc15e4d292702ac9812a59fafb52dee790f0bca40902f3f5f94a7220045e4856a828dbef5ed25dfdf3f15b59a4c312b952874389fa744e986559beb2d6daaf41f9ba4ebcb73f874a6fe71fcd997f0dc349ce42ba346ce45f0d2c3d074eef39b07bcfe12824d77f51af038d804772f29016790820d79a2ab0b4af20116817f7f6bb3900215aa5e420db471dc91c0bc857085311873071e054d22b3b63ddf6292d1501d9580cf6c71943b66f7f0a5527bd000e2868c62073c87b017088af6c18428b335830be18db47b2d6beccb12111ec5d2a412773f0cb7c77b77fd9473dc7a166d9fb9295148c366f33ed98baaef4a7fb5dd7954a7d53f974812121b45829d02d2225213f6da6e0cba92004d49f88a0def3817a6f47064422f31844821ffbaae29d20a920dc7df7f5551368ac9ac0fc999f093d20739fa2c0d37320095405a44444843926df2f65994b5ef7b4f45c17ca7c294e0f78481802a7026c7f875f50ca1b8eb4653165609902e0e5c66d5cfdaf4f65ab65777457c371a135ea9cb5ce0a4ea316c1496b917eb1f101cfa3c9a900d7841cb43869ee62d1a7cfc1b6d9fa5bf8f7dda5c1d3e75e049a4bb8206f802773b7d3d16991fedbb7dfd5803a3740e09a1f5999470d947f69b234412092f9d2a000431a706034f859fb7f02642a3efd1e72d7eae93751f87fefccccf7e3a7fa1afa1b0a45f3a86775eb82a736701e830c70de0607070794d90614c1846cf316031b9933b0b1c12064b5484d3b17d8b041b38d117c6c4799b6487f47b62864830f5f48001e7c5504725e002f801049cef3bc8254f4e60bc7ef0c2819c77a7c4513847190ccb1802303642af33add9238ba65f3f4a9acf91bd567fbf6a5ad0165c6b1e098ca37aa5067e726647d1ccd36cf99803060cc9831d9e6a5a901dc9768b60155404a44b82fbdcc1df572402439df0309003e886927c88e71e6d4af42d5041c00ef2f5f62a89a403ff860e8811c205cca86ac07e7975ed1af014e302dd277d3cefc0ad6e89439056600ce80a30c3802c5200a1c693e81a368da0054d9ffa730d82b00e183af01900f33d8ab4d1fdcafaf81074afa38ba557a4a3729b6483f0704008889d49b5350f0984108ceda3779fa94d287807ccd9aaf28133c67ad6605c625672073481958c763a3554a50afeed59ba0a0490a2c8d3569a236e932dbe88a164ac59457885961b691989e9931ca4081511750e890e58cad887a75afab273bb0e0c5125788c464e0436556050c445f8cb852b59468195d2cd1f5a129b6c9448e981c72cbc4b020c67d42db21e906000f8250afee2a5010740de6fbcfc6e7ae1d435d9843e582aebbfbcd15ecee9286ec4f8f686243f6f7fcdfdd2bc8f2f1eb1a8c1cc5243172ff2df2b29425d5929e58a156ce6e97e3386eb35e8746545096cf3d91eeee4ea974a74aa4984829256d77226e7344e0f9a3572f10783ecda3cca57493d2414a29e58c90524a4929a594529a4ad1e838a5b47329a594524a1d4adf47568a09fe52e6884085f13ccf7442618c3a99bc25d56e4e5d48484848484888ba10129d3579a3d6c8d84a7b62473c8fca911d9d5489f3f0bdf3c8e4646e40393bf247ee662b5d7244de68ee84aa39f247640e19ab91a4a3789e0fa8742e3715ad746648000080022315000020100a088522a14024cb1455563e14000c62884076663c1548634192e43008a220a48c218410028021c420a486860c086f66b9fac23c2701129a5229b11d3ba68969f3f0383200b2b35f5dee2961c9ab2316986b001ac094c1ba0542d172347c963f6e220d80cbe75ccc1ddf5d66437beb964fb07f26a55c56df2c728199e2043c96618c75f1d42d510472d8069f0530ed14f9c3e2128d74e5234e96c87b1386cdc2c6610185149911211b0e3893a6154d0e26ac6044be4f41a7a3d53bb462fbb7c60297d380a9fde85831ce4205c1986a77262768609779f6c605716e989c1e5e52201e03dfa68e6b3a67cb18dc9d315fee6b766b66493c1457002f007a29796dc5f17d6703e7aeaef5be19e5a5246a4089358d09e2cd87a2397b955a3bc8b29c09a10e5fe97835724cb3a5e4c6692350acaa45da1d0a0dabe7d3b7cb3aa04c618fa4491c990e35b9dba641fd91f5e29c5178b6c7acdebca8126f5cd511be74f4ddec324969cde0aebe341e566f2bbc65f693909bae2e5ddec5a485929b02fd4d6afebc6246c7dea9e3734d313067a6a53fa82b6146094fcfb04ae166a0edbfa8016ad48c2bee52a4277dfa614f0b6dbe4c89691b34dc58d1b03a3c2296826fbbf0667cc6e98b0d8abe3985d98a09c452ab58c254d1d009ba62fea2d3625a52980f2faa83e509b668a9ba7dfd86417f512030732fca48b74882168dc343411db62963b075948bfdddb21ee6f286854dae0ae2a214a85db69fba457df78606fdfb70c19790c3c147704eedff0617703b3c36e4c8cc674d00dfd45c56088d57f27fb8a4fdcd9b6f7b54f905f628910839b33f87c8d6b8c78c021da57366959d1f8310f45015a73e3d658799dd2339546f1e103480908f476d29a08d25b04e15002bd56f08009d2ad0bb48334f232a8ba9d012b960a9138e8eac38b0c314a4d20885e03a113e10260515c8126ffc919cc6ee3fa90854807f93c3968aeb7fa828f476b491115f5b91d6fae710d305555c5b6314ee790309558b8501fe9f2ea4c5e32b35b895608114e0cec61e63ff427951d282368053c90c8cc51b24d26fd900b255201ad50edc1c9405ac8c338dd88c8ce24c6c27a2543848a7d4aaa48895a776cd4a4d734f944c9234ce58fce0be69d8cc1f1748ee506ac511a9674cf69a531b69eaa104e078f1320924568a7e59b8f6b7d042d35e61bb09de3034aacbd0981481fe008d354625620387de9e144b076b42d628ee98dcc9551d018728fad62804474c5c8e6606bee35a54fb1dd5dc92c1c9fe3ec80e0de2fd4ad074cbd97f320c024e8ed51cb94e50364111054d44628a84fb1479d2a44df47bebd6325cb8b24447233fa783968ccef680b18ba4c648264e8c85a9c80705cda256059cf7242b717657859debbcae2047585cc11626e3a7fc87ed2167857fb1c9ea01608d82a940dfbb19c27fe381397131f375b1aca3ffbacad074ecb17a58a50970600b612aa86d31467b4fc2f33309aa92e6da66940939239f188838d3b39749e0eda46a9394d9ed97c99a4d389ef79d770e096fa33894904c741839c4f32934fafa0bc9f19fd8e10b9c1e1af406a31fb73a859bb7c5e14173556f500eb127d26af1ee0c42bb727ae11125812b0e8ca4460ad8a1ff524949fe229c85ef82481ed70191e9e6dca87d1040728d526c939255ae64aa978868096f4b96e72d27f01d6f8f4552ab1014d769325b02eb83739e19edfa0371b4039e39139d6433ea5e6471b2ef817f4352dfa2c0041e3fef14185623d9282da0f38ae188f750fd94fe1edd5b64b071308cf5d85a398aaf967f242b1ed0d62c5869014e3b9334859aee46c6ffe45e5a14c555413780a93996a779e0e067745d5f47cf3176be45c0ec8f0cf2ca5cf0bd63920a633715101c6e28a5121f3d19b5c0336b82a465de65562d78a5cb9c6b27e6d4f56a9310cd47d14c0b5e8b8f9cf8c807444e4043be35c9d13e3fc1bbf74aca3081cd51050fe484398adb62721aa85fcd3633b4ff26bda5242b624f7214bb0772beef0598ff3b8c2938159e21bcefbd909a979c05361a466cb303ebaa9bbf1de3f9433a192080120847e9e6badd6cc072330af6403b49be887d5eed94883f3a6fddb062a5d4bc17c59c29b363fbe4342589d4c4dc678a4fb613f965b7fd978a50d13383533be49c03e1102e1c4871d0e67d1a9674bc5e06cdad1cbb1160dbf9a79c2a2c4722508d74c74dacf8c9b052cc9d134d1e23046341ae144ca331568408eee1d21aacba33df03db60d816d96494b6b55bcd914ebeec90ef65c72ac1ef4724ce3fc6ae08e96990025c723854399741d79e8a72e7a77a1e5f7d11d578ff2b0100f8fcdbf2712d41c8dfc3a1bf32afb8967b7df81b92b8d5a59183d83067589c366250cf699861b69546e465d9c6ec23cdb0c514043725a6aa7cf25e739ec2eeb0959ae964087bc30e84351990aae9839c3d35a4e01ed6746e07532e46990c71f86d9bd7ae3c0f08548c0c2b607be97c7e7a0ebd302f11f6bf61e9cb32ad5c0bdf505e11fadd7f6b38656567d76c4c68221e07b31749ca1d423701f5aeada3233ff830964450ba7f80620f93afdf39d21112c90a2301113d8733ca4dc6c9a4e188a5d40e59070cecffb8348a4eb5a1b597fb3c4222e6a20405aea8085adfbff39424898331dd5ec6c941dff5e6b430ff2d958208ee2d970c444714241a6db3a69e4494ee19de4eee45b6add1ea1c51f0fbf4e0fb5049badc9492faa667e1040c9cd2a1f02862ae7c5fa2dbd4cda92f75f2d537d142c75a520a35ad6a028ae2bd893bcb73e72f067a6820c9d86175a0f9416c0007533e3dd2a77bebbfa6ac8c1f768cc95ac145f7053f80320a7a7ab36a1a5d4d5d30942cf13bd86ab36d4ffe670a18e00a8515c92382b76d43a67946ecf34be5af09336468c4ea9695816f087e61e11d04b738ebffc2a5e89a30c0591de379232dcca48bbe1f9b415fb787923f86bc12e47a85f16772cf38ff993b88bc5cb2d27367923210129b0642117ddb93455e1435b747e7f1d288275cf8465ef52df29d153076743d1411264b72af8118e294666f9ed1da7d0cf085671d92c462ede49edd7f11599e1c0a7ab8268b09dfc90bed8dc6d988a2c78cfe5f625dfb3cdccf9caf220c0f14834cfbc66afbaa0318e4077f247eb1c7901cb3e0dd2529aa1e3d76e5a596d00def6896f8a55b4a9703e1bc46e01832b2639986f92786d74c8dbfd86852c4b9d92bdc9a854f4cae30353fd2211d7895ca86d9f577c66c16ea4a493ba01670729f1b3be2cd40466503131797280459c44cfd020a09bb7b6dae7053780431724995ea916f8c19120e49f91a8c1c2588d8c5b6db5a3a7667416f4c32179572b0978a1cd0dddff604712db5bf6c99c57ae1da7d1dea931cf3cf264392c3d76f153e4b506af1279a2e1409f4c1c8e4866cc20d15aeaf19cb039d2f25344f3f4d0fba26b0f5bc71949b14ba63a16b8bd1521607c04a3d84df1e53564a05145ff5dbbd6f2ff0157de6227f97e610c3c3412cc771c77d9e478e255dc494067103f28d1ad7cdea5de5cfbed76df8137c1e1f3ebd36abe77408e10b69d2a931ec356ae87e871bba66b0d058bd81bdfed24adcfe5f0b3a2f0b54c25fd4e0fb0f107711874371ebd15c75a0b03cebf934a96b08e2cd902de7927bbee825ee59a15e864ae60acee7736e7740c27113d2cd295fea23082a42506675df14bf8c8466e23843aed60b8e284dde9c85110831f602533edb820937e7a6a517e4b4015bc358a57598668b4900e0079f265f472f031a820af7d44229c844400293608a86e28994588f88fbd5334bcbe977e30580d7d651ea38138302f6dca2c160f6c84ec83323a23089eb2203f526c3bed3ea12256b7d2feb5795e6d41049a913d993a30bc4bfec32861efb417f23d7c66f648a38fe141842310315357209eee6722e200473c9cb664f9404a9760e15c058726cb8c498b47a9a939f63388b801bf14009164060ea26f7a2361cb2cabb9586bd59237a3a17d863cc720db4a81700209209c4e343ee796ee5051b2c8afe90435ac678c195cf09f42f2a8a030f2db87e79c8f117879621408e5c7f27f299de42061cae227c1ce4207a482559d67f2df2d90c0596078764affee4f9a013a81243a3d044289ac50e630d135ff69272baa8140fc7451d99b1b44d960ac612a1a432441f90128fa7b029665a0031d0ba8a40babc7de7214f08a1c8c7d84f2e57a6c9e71c4d816f73f4ede30f2db7f21fe09f1d428aac347cfcdb114c12034d9893e3a7bb0b6e8cd4ba6925cfddc9861f20ecb97e53b5ebc81841f5e822ae30096bc5044e2f67c04018596e83159d83c447747915f11d6b68bd8a9afef73b048ab693abd22771380ee6e82cc09e922926a3e15c1f0fcd604c273839d47ccad8e387126f8acd82ab17c9416148732777fa8ef18941451867af59a6840d80781422d7b9d424378b0f50fbfe593c48479ad52b1de7bb7ffb1264095e24d460dfd1131f7232347166e363c87a33e4bbb0b5f71cbf8df8e181eb0326e1f89df5b8ca70a3144b113aaf2ac253b9f8e4907949fd176bea2e06b4af12dd3b53177a617231a91eedd49809f3e6a682eca6d4d17b134bb0fa0d56d0609c72ad7f24649b3af616c12243eefceaa4c4c9ac0ba44efc09fadc5faf8c8fa15f224505606038512ef44f175f0d822020043ff48fe88aac0264105e0611f5fd13dd0138c309cc47002499c30fe5750c0b61627413c30de3b48263611912fb0201af8219fafe3e6e043ef091a4a0da37059ea04da8e2bc276447eb4af9eb3cb1deb2ad86f3a74e3b9f1feeadc1d8ca539b0970038cceb7351f4f68d71ce0b39fd9fcbbe06438c78f87c56e492dc9a517dc0c7454dba1c4b939efa13a299a900f0e9c82bc7f9cac559f4883ba67a07101adca7cca6314407d94458dba75be2c3cdeaf84aaf601a80c604970334d814010e5d2f4fa86604f9c4a86a64d12ac12a21e8e628206f654d69bfb2ff65878889e3c9845d6b3790bdbfd0566d1bb922254db07ec3134ba702b34342c48702c21a2afbdcdd4d628868ba34812f9e05aad52152937f4ee491b16aef82882af2e9f119a2b77b9f6b5eac91749316ca1bc54e0ed4b192dfcd70e38aa9bc4f3e3d00614e55632dcab0b6ea8c5449461e4c6fc40c16c0097a55922feff7782758b4edf85bb89e2437a93f4394404003d35e025a9bb82320d70690292f2d7da2d479195213e4880ec0231c15bfb9b88228b3dc0da3d548579a509257f2883e2038771797469204470d0974db671e748537abeb4bbe34b3b25cebfebc7df6bee258b357d626440d916823bc328b440bdb72efa58a6c7454ec789cd3dabd86f85d0efa140d013c6538ed7fa1b6c59ca7350d56d95493039dffa7f4328de02531a8af4845d4e7f5cd32f0befad5246e8420a6535f1a5e92771f828122bee08335f926fda81ed190d316051bf3a4f27ca852ff521cb169ab56f5ef68fa8b74a3f123ba5d6864399b6e3ebe5dd44af2d825900527692c310a5318468e38447867c2783ea51edd383f78ed3afdeba80253ed322b1b56bfac38433adb759422e33ef992369b666acf15bddbdf9856529b3a319a07634e9a195167080c2c99506e221fc5c3f236c9c18e9028952cd6b453561a5b88c24455a21fa22aabf9d662a013391a8b516137d7002045ff07641882c56dca245482ad1052b48fbb53af6a95b4eeb521dba93d9eb9278f123bfbce642c64fe461e87f74a95df65f263826490552389ff9c1f8fc3a8586c21268fcb3a5da1046a4a20e35dc342383a93b493df5c193c41afbd6f6031024b7e7c6bbbcc641aa639b625840b8488accdd7df82fe89d68915885dded95744e9001cb133915c750b467072fdd4bb150bd8f5932f73b173503bcff8f0f960133a50041909e723ee75cf7ec8784fdd55920a92021f6193258df7005bd2de5ae9011f9b54fc6d426bd68ff874232ec2b7020eef28c8467b8b6a6530eeee41c3ad034e4a8bd61c0232ed9035107188580020848a0e6323d57060c1a0b794e516184ca5ceffc3e9281b98188bf2893d118e8c2e8032fdf433b3c14074464153ffa8d27cd8f005c3b493333072075ed5eff26f626e7bd245c11dffb687c4514c9e6b13e7ad7a199d73c3adc18b56895e64ec5d00cca50271453b40845ae29ba9d2de3958011639d1b098f773179b8b574fe31b4e05241e6da4826f2f784c1c35fba79e2c46c61726800afecf79feb97c8a44938c75c0abe45d4cb273b9d39c782a5b638abaa641c7aa6eec0b12a7bb095a3c555c7733bebd926da700a518d26b50030bfd26564521575aa14c21fc51ef84786fe573418dcde2524a9bd09094c8319b95a92933bd225914c0becc2525536dc8c32322ebed56b24752dd799be07b1781fc9ce556d0d6018613eb52300fccbc2cf8550a0f337cd1b96675b9b4c0e2900a21750d014708e21004196e8c08d13f0857b55df8b00fe169fb4eba860ce502d8e1f46a4dbf650a2437fa34872c081b2e6e237466e7f5bd38b4230815039addd4ad0f95b0677ba5c6b8965ad5de19911a6a1df7487aaae4a629e1f67b1c23cc0ab5b0bb307aa19c6fcdbec7f6c05a1aea1e0f06d4eabe40f8a8e613ea696ec5b8be794f161ea5ace5d716c306b722bcbd707c39fce89c052d46ade41ac412fcfb08e3cdec95da00f3d87935c499fa5de23b965bd01016aa8894920e09b8321aabb66da6f62d14f756c432e730937d575ab40713fc20986a360a3818864fcbc1e1342038e17ae522a3f9c5ecf83f1cbafc151a9d920747ed562da71ba401638d8bca560ea56731d1320b49f13bdd4d87c16ba6c808d185818acf81bb97e782558f1083b79cdee1ce43bd26e0d4425bd400c8dcdcfeb0b8945f7272f0c0c8267fd43af2a619a32bd9478f659d62539c61bdb188b534d5d0af03646ee464a058d881537be4e2095b8ae945e7d09501a4ea6dad6810039cb62672da98adb5b902a37e143af71b157a06f85e0154dfabff1738bfde1656c3a1ed390629b78040f58352fd02aa4f97b8484313d5dde1312c229be24952c9a022a679145e081083afd3a6aaa68a9a35abf83f368c81ca6891837fc21b3142373ed53c460cb0013def72aa84d2e8acca6f4af542cb49bbae9c605d86cc4fd4fac75f9dbc9230f9168709a045a90cd44ec9a9848ecbf102650062af390c250ec00e69a8a5e4082044381554d079ea551745d02f6ab1f2b5964a6db67340aa8cfaf67d5e328a851d1a593178a36d7cad12d401ca2dc45f54f821703f5f999d2a1df2885ac5ef9c04e2f2958e6d088c885bab4d75de135ca95897b94ddc5e658514516f72224faee5e2a270d4c0517bb88614ecceddefa710246f4621e956e896970041f28e40a0303f7423aef2522f0c682aa2422d4690ac4d621aeb6e34c43ae5c22bb063cc70183b99c4b312ee23b62f5dda24eaa35e20c1305378d79956690b66ee9c08d83d2062cbb45910edbb5e585a2c0324db20116936b4b612d0dfa378a825397e7fd691478184bb56d1649baabed8879a1fa0f337863a1e522bb6529113d6e0ffbf5a2cc4865ecced69c1a715f6b76b9aa4b304df8ddf0b6d038237c7f81cea7da1eb7894d67974244c787235574b0dcca98172bce3a924600b31cd4c0b6dfd6362c8f507bed45234b21b98129decbae23c9a8b53bf16224ac4a685cd39f6cd90f17348a4cf96bf8245664ca700523f8854ec1e5c8c9a90cd10fed4e6985225e2ffe8d06c35f58b5cc865695e0c834cdbe4eb3b927fd2b572db36e7c71089ea1219c8173afd7e73f1a843f35ec0f5dc3a3fcc7ecdedfa5cc6937b41ac9176a6f7338d8b0d63b631000c130f3540bdc3a42eb93019cb6d5bdb38c1e484e0fc20ad4a99ab065c69908d1def21f0f2497d54078811f0082a70c8551d53148be6708492a0d4d7043b2380df3fc64e16006a6130f967fb4aef00015faa8bdd20b9363da43cd750f1fc539b399a283cba5a64000440ea68e935926ecf8e82b420db34af0820745a0d9c7a34139a6e1b93185f0d65e9f2a52a4011e263f875be94bd201cf888e295e9ce25a004a1f910d747a9007948b08210251d30500d56915cc619c9a020122f0279385000f20caed7b169c1ba0a0cc534717b820c1581018c1260894bfb7ce8c9ec1ce5e177f6de681d65a6c2abc063a351917afb1ff71e315ac2de2290abfa1d915ffe67df21736e40181b489b7e0331d2431ecefcf5c23df1534792e7a9084bd35d6acffbe68485bbce5d0190ef81c184d7d8a24553fbd26f968a5557304ede20aa62907068c6bbb2957122b49d56341f0de148db1543253a608d811c2d53907a5ccce1ab5506831712c069e1782a1706a23f7b7b5aed0b578ab3639641194cf035e0779904c354762af4baa07c7795830fc59b9d158759293b005aef08ba8183527ab0827c2268663587bc2377d951917018a8a914e7f69e23625e976898fcdd415fd34652db624aa33cbf40eccd19c9530111fdab13bfbfae38a7c370b83bab76fd80c12f667d32651a0d1d644d83e728727c2703c1f8f0371a403f78cfe92e163820897e99ba99da4641befa6dea30094e656b790434f8845f0bef3cb5787ba6abb1b06deea73a371e218b1ba2f2afad4d2190ca836b0610c03a49caf62800821c797ed667a376166d7912dabccd4bc90aa5522b6fa79358cdc619126fa95ab96b529fa5272a81b3a078967006bacb2072f78f46b81abbbeb03886c194c6032fd934d345d5c0932b62b086f06c152433214ffe58b3c255bc5ce1ac00da142e14832aae76ab3500f33f1205201e46818d2146fc1c4894fb3b1ab0bc85dc2e5497f2e6e305b56f23f907bc7bfec476a92e8bf9d0d9e59b5f9b1578fa628aac7d579f2f4694ba538cd91667f28b640008d9ebd942ace3a5a386e5274fe54037315c35eed5f2eabff20895fe615189725526b9868e77190c05a2a9529112c4ddb49a5d0dd15109bb6574d0590dbd0bc803f82ce96b928d04312dc2c29a3c63965dc046b9b0a6b99b94ae6663798067b03f560fef3c699a839140c5de794bb931577006764cf36a3d90832d8ccfb7dd8af2af1d3c9d5200496fbb05515301f6402d7715e6ab55da57138503afd3d8882d5d2078e86eb9ca0f1687fcf75692fae5e496e0cf04fd2ef1e49efeb732ec67e2c098c9311707aa359d659403987afdbb4304d6f1fd863409dac4887fd948d5345c74e13d81db3722dd5517835d42fc66382d3f0c6cbb386a0de0c020686b31a2ca9dde146961bb2327e7960cf4a79ec17b1a51838af7581b306a3d3c3349a272c2014db0805a0a569e3782df580cd96bcd9422996fe50968271cb194957065b8a0ef582f00fd5e260ce7f4fe461f91c883a4e3aad50c0435adc3a4563ad6512778b097f986d77c490ef84f4ca3fc2795e2ca3982134e3642e3945a1a5dc2e14cf85120a6ec8cb802d4fe284bf393ad894edb2df7a85bc5edef27f877c963fe211f599bce4ad47e35b4efaa0289ff4f1579e43c3e1671564919fe084343f7f1538fb26417b9a1ce24559196428428ccc4176632fd4ec13061ae3a3c489621890f20f421bb39c39387565c0a060d89c25227a37658711ee71921bca121ba1c1cbe426612a427f73b9ad6bc4121fc4ce269ea48471ff360b27e4e01a41ff92253641cb14fc12784d5a47ed531e9c58f76d4c75725932eeae1196bb6379f048a1f41530c3d6a2fb068afcbba5fa31dc77911aedff099960e1f3b50110be57339fc3f425d4604772dc57e63c09718ff35602f6732fb5dbd4dfedbb3111b947fb3545370537e0ecb5cf3f82b35e80245a5267c9f833968ff3b9e4ce579f91bf121b811fdd92878a551985ea18d54c06370cc991de602f125914cac5495e49d19e4de03e38eef2b2255bce0165d008aa0f11744bc83b0fd4239c6b043083d988151fdd750ce2db63537491072fac22616f8f36611521333f0109613b186dba3de70ca3763c7b34615b52b6150946d984694f0da5c143c972a2e994138ab5fc7a8ec0a29971fd304a8535a36f35fd33a838ed9ea6631eca9e2fd88128e91b2641704da23c84f666d69045dcf1689e7026b24400dafec7c3e0daa67268529953fcec88d9da6d0977d4c3405616ddfe4781747653b303198d7d463c236e0fa083d69351dd8a5413c8cde39c6477bc32c8f2cd5963184c0ff4a8b463bdd6c7850f1136107f2f0fea22d4e935c414ddf781d6b9c94ebae65cb31f7d110ce8421b132966566c088629e192a66df8caa4770ad01c1b0f8d6554dc96310f485bea07359da3375690e5fc467750aea265ac8a0604761c15e8de2e6b0bda4d9ab2072b69e244452d734a7480f291bca8006577e9a2a18833ee35550ea22fb3ee28885fa5eb19ec55d69e3abb798c181526f63c4956d1a0e954875aa74a3dfc3240549b9ab50f6c600637630dbeae0432927f400132eb9701d5e4acfcef9e1608a709d76681a59f3ed827118669afb2bf76a904215386b7a3ae978a95d43b7e291dbd9926829ffd3b8c796626056409c969b9fe172eaaf90d744772062b54894a09056e965b993a81c2066102bdc359cb0a1c761e47952111e67fbda85eb99fb64a82a52ce7801de599206f78420bc7573000ee58175a6a57ea5842a9583e655775014f5673f23d9850412e41f438bfc75b2928f0d7306b38f1a5a00b99ed558827c50221299b420037bc69d91452e34484e422408aca03b3b4039e0f66d87133008bd9f18e598c533dbf501f5fc91e74c24e81a8ba873b526c4558aee3e64a9f12ca807977b5160604b1cda6320e4629887ad937bf5d7c7065169cef2ce33001a0ca305a71a6b66883d0e46d32db504645089bdd0437cfb4752cd72c45141e83ef7bbc9440ce9ca121931c260f9b3d01fa8c1b051a929e7c41c92cb90cef6cbe45c8d9738a3947849aabb44bbb1df092713ccba268f6e9d4e322dacb9a7c574a275222cb251638be9e9433a4be8c0558d764b2289738b4e42b139c73427a8824ce91f126956fb714240cc3f81e24aa8740067eded4a91013015f592bba7186440556d59fd4e90624c00a230fea569e7288b899ec0c8306d0782a3059b1b28993f44dc6f7e79b7f536f3f81643a26348511abacacd8e791b6b90c328b8f966bd9a58fe3d2b4786528c9454987cf7dec73a7ab972f0ed7e0e0c8e309c1c7b95bf178029334bb2c74ef60bf364c52a4a756340763ea65bc7b0be1f658afa87e771265784138d15acb71c818ecef831a7ccb62cc70715312122150d8ff85315aaaa8b24559eeceb15180ad08c63fc4cc8a236a0898714774ddd6f9284008c4e8c68af668eb22cf1640c3faacee70615077a5be564e9278457663e6195044f7b98f97715826047968c084d24e17b49ad50b4cda30ef3bbade51fddf0b2fbd1c5b6e55dfa8208f781e17164cadc276c7e90d9ab331e7b1f67771f83bdf327b4cfc914b8ecc8cd11b855874d90b897743b1f9574418f1556db11280729181d711db429baf8c4ea8ba735c0580025147ece4dc3276dad1445fcc77948d8cc9164959762119d337e30910adb87571bcb9cc125a530414eed023282c92d2131fee6184871b8d0d1441afaeaad694066d2b0deea1b953e870bf0ff1f22f20c2a5ede4648ba7eca0c02d34a0926657d58a9da139ce242ebf302ffea353e7a1adb83e9959e15d8550d96c8bd64899d51a2b99013d232c7ca0edfeeae3cff4cfeb6881e87cef8143313d05a8d6a015c4468ba5d4ca0fe8b325b8361f5c5df789dfac5c58e2fcfb8bd6e966922f7cf8c0a2b8572b59d271118b6932c680ea66a19e80f18783d43a01dae55a7cc065709f35f8c99a82d1a2f6fdc09302a6625051609afecb31bec0d0eac864651813e72eb68b9fb9e029d83808321a6cf39dd1338123f8a71faa1585ca07da20ccc2c61fc0f59d2d7baf75643a7a0e88a952abf7c1eab199e9e2752912c1c7317c1d0f52eace48c2dc307a19127b5db13838267a5a8164aab9512da55c7f86360980b0b10f60a9de67bc1279895b48783cff6a9d3cb58c1b2c6eb6e78430edaee3cdc44e239b3befd49381958fc145ff776ff4195aa64594580abf894826300aa2dead7d219e244c7c26ef8ef5edeea5f3f2035d3391c84e27b6e35374d20b9713b280e43fd144c54b97c8a832f96572bb01e557b35e8b2c2d8ec1de12c1cf8b55b2c25a67703bb9bbf02655e4a2979c5a4ad5e528481ec195e974a0805d8663c5539932077ead0865fc501d033706d96d226dd36de28238f6165c5a0861544d8c68a5c5ba114dad5b16eae535a25dab60358de07899da64a10c3c40946125a2abac2590fddd5073d301c190a11e0551293ab86967e524fe1a573cbe3b07511ed4ef7445cf00177f39c05162d099f24c0347ddb80d6c44b63b510ca63eb04c3b2ca8b5d07b416fdaa7fd42283555bac2e995e29371e77d1b57caa7e87787ba4f64d5b87a696e0f7e09ebf62770f460d423f44720e11d347848ebd3c8c26a9147637f21c078c686e999bfd2fa637d71002875019d493525641fc7f14ba538286b38c7a7f2bfe4d3363dcfdc3e7c91ea6088c8232e3db4ba56f8920de31be8e4895cc8bdc181f3ae294aafa3c108bad00a470c97c649fdf100f16d7649be583bc4bc4855dc2aa2c44c40d3128efcd4d90c59ff6e0dcc4e538a9df772004d296a85312d4833da1e7a96d9fcc8af86dd80d16a1440e795eb910699163f2dd93fd49b55437534c3b19cc4e5c558b676358bb0efae247b57c68563c666c63f917b319c2e40f17ee46ae5ba034f23b75ec984e5bb386653b12ae9def854fe118ad01927ed35b8807fdf02971d689050e0bdfb3cdefd0cc09fd09c0d2009dbcf53366918ebd0c6fad9e49efb620319e27e6bb2b324a38bc549f830fdd08a33d03b64d73e4459c3c963dfd967c5fff2b4a22be69f9bfd6afa23d95cf452d94d8d67d7c1e24479db57fe4a0840b5bab760e9ce54dc224e40dcefe3722b25199032a0a50877083612adf09d9f8406b0713448401ffacb720018dfa1c59697d8536001dfb05f1e7ba9909f96018c9bd1d8cc5ccdc1d0d06b63b897421e6a54c723ac9899a4214def7518fea3278a703ed110f723d0b055961cbe62834c7af25f17821dd325c80df36b3a90d6614e8aaa48870fea5f258108c39725fca16c4eb0a92f6fe7b704b1a9a92096c1ef30eef2559da342aac76812075db40933cfc7bd74c2e2736a735d44b0b95b1c02284073f752820802cf03a5e94e9df7ad9f2a8e16c0834901e9bb5b2f7a38ea3cad1747eff53ad5f8d9fcf7c6586f12b88ae33fab3e002e3cc9a90c5bb77f725a22dc534c8adec096832d919b0efec1be03b07880f27d8a305cbefd26da8fff9956494419dd2d6590cd708e1c4c813db1fa51355450f0d286f00e074753b27ccbe384fe69d146889a292defb651b05b4b4a66c6cacf208af365e86f98453fc1bcd5c402d9e88294d7bc3ece6477adf0c35f8742cd51f7cb053b71a950a84e60ebc8c9b52bd7fc2afc2d2f5905f2414aafa9864c9a5a28b709853849fbb6e55c487f3ae0a379d30a071bc616f5a5433b74c737ee8226834b7e54b474d7989c4004f5a22468590096839c91ca51acc13c3b92cc07ae50d4229101b2b748e52a6956608286d3eaf2465c0bd272474466b9262d97bfece39a0909950aace9b971519ba4a5f78cec9e2de01170080073e5567372d479a04a20c54f438bb9a05cb44c55114e894c7b5e5f45d0346f50417cb440e265cb96e5209c50bef9776a3b943e524c751edc1082fdc2ca89e1e504238af46e12003f0c2a329dfb73c349dfac14ea8223f5c23dce95375c4ce5dfad33ccfaf0406f7337773df37c92fbf80cf36fcac244449321eb718276b01c50f623d39b8ef02af18f416996fc44e62ffda892380e310e3bc8449f5589b997762dd43e17454f0a7f8f97821207a62df7b56a91f438611e774bb342c2f7b0e83e3e7452033091e48beb52fa33bedf6b9acc4d835a70f849bfd2898efc7f7bdaa67039818ca535a72b5c90f6035421c38e82da01d18cc990d6940c652f983960f161bf7e63c35eb0cdfcf2273cbbe219c37e6270aba11ba922761a53a1793edd416b990dc95e43d00b4b27c95735fff08a1377788b2cd059171605c460b43ea1b7e5172b61b645f8f0faf4b213ab629092ae847207db38ff09b731183064656a0e392ace1443ef872eac05aa63925a146ff0e7c554c192456d9e18e365bfbf8b5da4552ea8939deb837067962124469899b0eaf8391e7ae1487fe1b7dae9eab83c0e5229898e156ba7ddaa0c37eb48b8d953dfd811236c9d1e31784936c5468d3a0a61267814bff909cfefa3b2c671d4d2a09a11fb33c479057760b0520f247e1ffb5f6f33cf7b59fc0fb8c64d2148478642b840bcceba5982a6b9f81c58c00baabff79be29ba454e3b8bbfb23f1358ba4413014246e167247aa5aeaadf2efbc751b9dfcdb3bf6935aa1e0fe64a2507bb8004b2d7af01c6757f88c85751dbb50e7a729c6bc429dcd1c51d38666cfe5cd5de6036cb57d78b8ae2ea2571f4e39c2f9928aa497b24d9cd3ce2b67a3a9ae63713540ad9c8ac226c3e997c892973726cfbc70f3f15eca229b2b12f3f26e798cd6802a87a80c671e81aa013b68c980c8719c2765b6e91d629f59f294bae62f60b84e8838247a0e10843794676e1790ee08f264adf0ba480667fb2cca82175b4564089a856cf4aee5f961912a547f7d3cbe3742124814cbfb24926594353da5cefa6d574a2f59ee2635a0f274d458fbb4fa81cc28f4620819b92167498bd846ead8d4d73fa53f53dd9ea54038d783993f9dee83b7af66d768c60c5c96fd58352dadef09cbafc0e5553492b6485670210e715daf566da12e8ceb23777e7ca53179c0061b9297d3c53b294313e61e6f20601102949507b2599a9e449784744bf0fe65b2ace59b70943b08337cf3fc35b863ee7f442d4df3cadc3c67674931914dc11a9b315aa09af599c5747b88ef9740c4ad094878db7513f168d0021ebb8135436354ad35fdeca1740bc4996e35d32658f5adf4d341440afa34935d6797420cd1b2d72f31809619b84b0287a56cd1cfa3924deb53958efba71701b11d18809148fc58322e2b59c7bbffe89789406af6b68198d61c0476b679bb1d170492d0cdc50587e136d077d84ad61d64b5c92ec9146141ec3392398e36c5f74d765ec5c2dbc2a1976e4e8cbd07de502d8bb53044ae35a24084bf537a339ad6a279cca8506e6894aa8f81cba2033ecea40c921f4047880f530ad89ad1b134a5ad5a209f4891ed919d0fc89d0a97dc1622fbaf9914102841c9f149012e506511035014efaf8898701443604f033c05bb07d4177bb471f93d26ac5ec95e254584fa00a5bad4d5fe8c89410765b59cc3636148f5706d033a8582c85009da586ba4998de74efb912c2b14bbedaa1e123e9e00c0370709cc3ea03a2dbdd7df395f0110627137bf1855d767a36d33831d31ef3b9adabc507fd466fa09fe1703072bc6edf24c9b56193278b33c62af2af440a81b1345ec024d073c8369f098d5f1e92e38f78e9e7466441604d5c4251aee0a52ca0f4f90c7140ca4f251f2ec136c024c0c4201c7fb8fb18609594dd22d40d242e4dc5d91295ec76e62b438ce4338cc8599415f72a1515b021ab02f5a99532e2325ebae36a0f16b6b90a61d791d2a96c431b6abe37e670208c0de5bd0e60496075c4085e5dae124a66151fbca6f3a1a4eac1b0ea309d8a34ac05005bb975dd1e0e5c4bb3164f98852f7420aebf375dea49de089684bee3016f4fd3d64aacd9998a03139b3b0e0a68071ba523b5b8b8376bc1ab8cebdc9a798115b8bd9262783bf4212f2207b51cc55559221a68968b935e9f91cd095eaead3e8fa3a6ee8a8896f8ebcabec9d16db63e0bbdf360d376da189af5921cd3059a1f7d0e04ff42145b32dbbd8fd0466a9e0bc3a116cdc7e557f94b5c93a360865cd4728fe99fe4cac6baea82be1b86d4699aa1c5cca2bce6fe92670c976a1e2d1aa0ab6577c5528ea120eda83f9f979c1077c1943536f9a91f8c04f13a4ab83ae88e4ef2afe90d8f3682f0c05d399c09726ac02caeefb52e399ed9614d32d326c691e3e655063881eb3b85a98e0d07a832977a84637c96e70779e8194de99ca038c94a37639703c16333b602f8f380e2e722eb8b17b1c1b5c9370581fdfd7f58b8ab5158a6e740e382d41b63eae2ad69f5dee17f5c6597cc8171471e63479ef185da791cc11351507eedbec9ea9b88127982bcf033581f023a72011b385c45f223cd1fab1e86d2031dd3a434ce06ea835087cd1146b26b752970845c054d66327a918ebb0cbe30fc39d322c839686e831112b7e66fbf4bdb288edffb9fb55506de8770ac010ae429f2bf00660e17103ad95e1132bf028e4513243c401dc0d4bd866a4958ca1bcdf0b4d0b6a5b26748de746ae9079807b79492620eee34c1020d2400fc8cbf45e749a2060f89abe2b471de0ee4b078a7e6bf231ccec563b3caa8ecd51dc171f6a08ca3f94527bdb44dbbcb72f1762a2c1c68ba6d78a9bd7963ba0db397245bcd190ba050c981bc1761475ef8ab5e2bd7bc78d378fd66dec4d9636bb69036e8b45a0c898dfa4ff4487663a9fe900f64d1a89e304fcddb0879224f5d2f87e266a958fcf0f0cd69f5f788407f8b446e264514ff811f1e40508a583408b6eacec91230642c731ee5d14daff591f21d4dd6728d1576ebc9eebfd1f01823cc863e7157a82b74745a81132bb7730e48ee63697e49d68245d85748220ca71318eea26fbc7da1f48678008dcf7808a065d32e3c4d3261c0232d28a93c26a3f0ae7f3a2117e5205749951690b4e188a381d260fcc631f1c2fdbfcfbec90360fec824712f85059a56f8d8aa0cdd2a2924b38302df8b027d6eb9bde320b80c8dc513d5b569e9735a00450b16609e50019142ce0412f1ea0701014464f601f4423c15f1abedfdcc4bdcd00e413ab955ef47809c0bf9cbcd43360f1a8b3963138b770a9c481b419d1482a5a6db6cc64e378c00fe133d149505729b722b0517391d3fd67ae5dd0535612322b50c81e3110f618472c51638bf0ef907423b948643848f7313f43c180def9c24d0498ec8c60eeeb1d6e14eb27f5e2c56dc0091adcd9fc4970ca13f593216708b893b87c9455cdf599dd0d880c7d69ce39bd4dea88a5a21372ba3580ac27cdfa248887db923460427b46870c4dfc211cd2bbfbed83f7ac2e0062579c7f92b44481fec6397d16ee4371db1349d6adcd122c56fff37190be182c699d5ada6ff2e86543d4b82ef7c51121bc9c8f0998a28f9721b3e0316d47beb17507192baff49fbcb4a957cc04632756c3ac5384c9e8174377d2126579c27bc0b7411e23770802d75824a4d8ae234faf05bb03f89f636b181bbe4b2e421c8d03f0ce619f8df2b6cf19715da2ad120825b02ea5d0894f80fb2d6a84bef9f5adfdc267d7a7e35b2b713c88bf2300912f8c02488055ed55d28cc63b1fcfbabbb9d844f6234adfff4104bf6028f45729412cf779a2010684007d6fd09924bf1be1c3990bf821cbe8fa8dd24c39c92528f3384ae2c7ea6300ff284f55f9b9deaf75de5944b6beafd93901910808d55cee99b3f0931cbe08857abff5b67b6a4ea507fc6d14fbc7becbfbf7756600f747e57f8a3501d022ddc6f0e31a28227fad930cc95d2b19b48dd571523b9374d1e8ae21f46ffd25adf97e8eaf85ff10ff2abb5a30b8790d1c7ddf9f1e6fe68069bc8ef753cb6fd58fdaa62eec2051d2dabc9ab3caf68e220285217c9261aa4d0a03f5024844af5a23db4c1b264b07700c4389a4e1561539408ee754b0390e373e40dbe89a198c218afa650d607bed039fc94cd8c81cea2615d5dba1695e52c5305b70907bab877a9f1a06cc2a4bbd1ccd9671e9effd90438bff7c0a5a71dee15ed3443350f79ceb1b2daf3a133352ef21d20c5992eaefd7b1b2cf79f9f3ca2669f83a5b6ad0b2fc28f68278736fcfa7636a7413946ff83cd66e5cd9046a7e44a6d794bbf8d574b20b1c720ae69e93bd11dc64f7159625ef0d1eeef3e3d301e00813451eda8f678fe1f93b9b61689dbf4fdd400ff8d6354dbcb5332fe63cf35e6829be2b3084f6859916bdadff2a8b1fd527c94db95f61fc9c279d863f7c0c2bb10b969e4221f92aac4e15060a8cf1182deb4e0df12db9971a563f0a074ce8a82654329d8426ca370e8e5779153390e3dfe47ecda3a06b8c3010a5128cbff9afc2318bf38a45c708fb5340e2ec6e27cb845d4e80c4534722bbc5958567109485710d61d681c0363e2a817dfa71697a168f356b5806b36594a49612add15d03eb495af82eba91aaa54dc5e3965fcea79a727fc055b06b16e2a102d0199b0388661bcdc6a624d67216d59162548123301210726ca0c2ae5d1e49f4817bd2a8b6c641e446bbd5d2ae02e6984cef1924e44b1a8358e29668100ea1adc5ec9e00dda993273c810fbc63ff6f26bfe57e3b61fe8784a19315b5e4990bd2c5285c46f88c6ea0dbb9a6c236ca16f3ef5a5e447e019deb9d358b5aedf731692f4054a51d89c0c377c1ff9633fa1c23c87d4d85fe7d6add8c02d020c0fc3edc047d7561c25e7a82a1518b1ef8357bf060e88345fc482d3a75f2f9e3acf54b2334b12b52ea136fe9d0a7478dd7758be10aa9b1d2eb1f877956f11fcb603910e5d63660e40419a511fb99c994226351d38b1944935b0b77f03d6b7b6d410c3599fb1a4835301a546ca45e9260f94f6681f67f34d2979b23922978777d78692fe2486e9790876d7dfdb6a32d122dc802624f4817f2404062e60744f0c9b12af97cee77652cbc3de99914ae4fbb85986e29f8555e839b2c0375cbb246ee1f09d742cca1bd96c0c46626873f87f5fbe1afe35dced52d279c4b51e362f7652660bde0855b8921ed8fdd19bdad21c1b81001845d99ad29c9078ee9a2d73b3b8410d7f94c6cdd02cff475a4322a1fe12a22cbbc019dfbd416f34be17efbccf100c986f2cd13c60e8ea9c91c8468600c191e3ac590b3391eb329e9d57ac3a55ad17264c18504c2bd134989eaac933a939d56592fdfb79fdfb03c16f174fe5a5d3913f1a9f20878829b0b834154c929bd3a0c98494276579e207eb299e5168306d13d452ea1fd36f2208d9aedc38720c0ddad9c6a8b9144870d29b232c2eb29cbbef7403948bb14539ef476facbd826d3b16bcdcf76b47217de23833da874e9163091eba817a53e26adeefb7cc45eb040aa2a1f0bfc177bfed005ceae8d93eaf9639d365b8d2750aa4256b8408ef2043853d262852830c8468832e542d53b6e7724823b4d4f6a7699b52978483eec2c81a48c51599f4cbc4c70cb29b8673315da23c4b9d0809773084062c7f28a3bf73cea51c17129952bd0e249ebee5b7ba64cce94d107f1ba134c200538222861c88ec4ad9411bc3044801d63f56208bc1fa6ec048c37c01d2d10cad53d623f90d1a14f34715c0afbeb1d607d55377e6ec12d271ee2e7ba37b1d9c3e3600e360d8dc3f08e0c47ead496fc173f0022ccb72e52b90010458de161a5acf3eb554bae304347954a41077e8242d7c9774014642f6534a4079ff223148f8d43b5ac1da1a406fcb46ca840fd15edc149191081350d431292d4b83fc31e78c4e401efc0172276c6b64c4e47678b35264fd0bad540f4f9150b6585462881b2def3684633c255908e1eba658474e153f15849aae9ddc974fb4a7aacaaa66593b9c8f90a7da3cbef0ac442d0df0e9e82aab923c332f43ac4409181d6883c55299db80aa4db603562afa99fa1696f8d40a4be2171c7d87e5f8df7197a15bac6b0b9352be37eed5dfd1ee8a2490e58667ad207ec3e0339859570e829fccffb6b9ec7e3c972db5f194f0a025dc89605d763aaa97c106587e5565ac710a9088a3cb61ef3a0d7fdc68d6118e0a7e31556ae7fb81e6341b06bd22fe6bc1ecfa8b824990bbfecaa802b3a513655408dead489e9bc965770cb23731a5570a1434f2d46a225198a22898f48783e6ba1e11b99ee14dbd6cbe859f7265b8d8964f5f6f050c1bd09bdf0f0cd015015e0bc5b95f1801bb2dd63ef76c1fd632fe7aea94d139e27d954dc2e9971fbd371c318e5867d48ce6dbfe3c1becb71bd4c704b77a540ce30d8bb91cd617fc440e7435083c1e0411ca0fa77cec291b74f99a5ebb84108da04417e49801fa8615d827a364590ce40c874cfa06bbd850d4989c9316e32dcfb0949137629810546a065d7a4fa50f1c656ccd999d93fbe5fa3dcbb0c1872cb8be0e9a789ef97da90bc71a59ad0a0e51a0dc65aaa28488a154217cb2862bd136f2c0af3929356aa1648c40d478f4d5471bd85a39fd38de012c2d58c72d808ad2d2e19f8ff5d63d56ea6792048ff46adc76d214db7d876d4d9041a4b03e3920f9b4c04e44082f4d5547d3b98316fb9617be939f0cc51742cd3ed972a6dbceb637031d7606b599fe8ca6a0a9ba6e0a8ca77f8521ef7cfc6da88dfd40d5ac4aefc8bd7834730d187bf312a4d564ff10af5f6cfa4eb3152f36c570f88c9544419a9feddfafc965ada98b58d1edb12e2217d0c643b0566d855a6eae963705a6d34745ac6b4c3dd3710aa5aaab97de1c860d2524581c4f8109a588a12eb3de92736983f95a6094483ea8a12b185d1c62e5a6cafc9bc59a31f4a67fa4fed7fb675c1e0b5fd718385210da6d66626b7cd1225a4ddb6b61b06ce3a2ce8d66a86617e2c2510db2ec202e164cbc3c34a262c038a430698c4753819966af5345bae7a83aec709fc428d83563914166aed3b5bdb2a1782faa7a7a986fae3bb411a798c09b5de976ead0768ebcbf1c7452b456dc297c4b8d329d073f08bd5f15e1af19e23f0049a05c8ff04136ff1880473210f4aee4d1206f077cf48f795307a4afcededcc29354f601a545594882f84361651627b4ce64df9b9d2f2c1e46ec9778ab8b06720f4569636f8781dbed9811a5308c361d20e6405c6db6e48f8c58fffdaf6ceff313a72a313ae6b079795b5ba526f683943150d40431c120e1f8485c637b20ae0179f83b7ff0d00f71576c6c59223467cf86c8e10c46594e66d115ab6780e16e857fc3480a42bdd8c301aeadaf3285d301e5fa6bcfb201ce92f0a43771d43d56249d0f31c89601f107a0c83f188484f9525803102585d8b81c12911bffc20ce7c0565265df355ca475ff99a74d0da9486fe5753d71d0fe268e8a0ea13435dd0e121fd5e1b1e030b8f029af008358307b0ebb82a02d24ae18a0ef58a49789edf9f739e1d38a26ea1fcb216847ef4f100d09c80d31a1c10595891c957c5420e22ba53765f8894e5816e9a2f076456a4d0aafe007c48f7d50710e8139bf7cfb75ea1a761222447e7ba4a307b8f94d019a83491de99d480bf55a35b6f57776ef0a60cd7518a08f773c1c458e3a313310e75f8ca0d19f818f2b9491fdd615c7ecb65e7b7a59d76ddd42095750c9466ce0eb281c05913529b63f642cdfc26f7a0880b0866fdf2d92f5ebc88c85f57fe43832ecb3f106de9d8cea211c45a0bf404bb76fe015f7545567dd89adfd8f522638bb28781b9d7bcb10c7e5f586b85b9e43de27d57bf1567a9aa0bde83450ca1fbdf3cf4a820e1eb01ad379b3a8373930ae3201dc48ae77a8481af53316552c93abcc8009e3023678c49dd0b90e43168e360c173c24fe55f075f422104ae59c9eb4e869d2fbd48e471edd4300a3245f281a32ee6f1800163d69a74aacdd66d986f42f76e8102b2cf95e63fb9e6fb0a15946dd32b2de7053523016b856d5da8c4da9affd875119b2f587afb9fcd928ff7db722991f53942f45118d1d29e24cf49035171854e057d9aa4df14ef1b2632c95f7d118c728e073a1878b4fb3594534cb9756b0725aa977724b90fa05c4225cdc65e8c8d394784536241c95e47ae1d7e76db9a2dd22f41f6c8598ff733745bcd836fbaca327597eabc39e113e8e08604846fc95ac64a77060007252acc9e7366784f6ac97c5ac2693f2e2aa8b37cca8c8b05b362defcc4c17341b1a88c373ae00e912aa3713898fcb8690f1cd82087d9f8128a200fbf1ca9f97581d0170fe4cc716f877f7b6683bc381d9b8854c07f2a4961fc7ded15d34a77f771e543a90e191154d89f0ce22516c5504db48641e2370d4f103b92a84bde845f39fa1e5a8ec71c99096916a72a3db2a26f19690d16e1379bcd0ff2fd63a22e9d1f10df4b8407dfb1b36660066f6bde4abc8b37dd928caaba65900f5b4349d0ec42f4d076cba0516918e765a8f965631ac842cd5968cfeccbe0d95a4e7e41dde4d4430b94ecea01aa311970a2929eacba67a00ba2e879c09f95d051a1025d7b3cc04349ed874a0a79051501cae02d1e1bcb7e73e6e6bc661c8a52ed7c3863a8529a11d59282acdf4aacadf11bdd2e58ec51ca60f37ebf7b23b791cefe5ede6b8b7b4589e3858317fe93d91ca11bdd5038e5a48b8aabb9bf33e0a736d30e6799f83f67a7d124cb7ab86ad55c334aed17b56b54745ad34b302584bb1e6d9e1885b0ed433ec07eb31cc3540f844aab23c950665e7f16d7a238e47bb5b97e400ab6e9949d8060f6383fffcb1e80b2d68d7790ed2fa7e1579cfbce4e8741dc6371f29e277bbcb8c319e3274b7634d75abef9aee53e60f7661f9db67b3fb0c48010aaf9515fc68c8c75f8daa30f1eade3a8fe36a7827bc4264b4ad936bf437675c6bf13ca937f04929ef9f03123f6060f64f9c9b63255ae2cde91b02c167e00c29be001ddc2b0a9664caca5ca980da3a683565b19a4aef40a0cdcb9d1088f1b87b28beb94654fd17aad7477ed8a8f0374580bdb386348eb51612c6fc38658c78c92da5d319617fcf483ce0e33456738cf777b437dacd01dd4cb437d9fb0a14f8bfe61f96c37cb817b6f5262c425e333e726ac1df63d2ac379eac319a2cf2d85813c9b5ea9096024bc4854a823cc318eccfc7963ad51d78386e4c7f2f7700dc654adba7b478fb8c5fe991f4c359643b309923503a7285b06071b8199c3c540037fffbfb26c8f163f50e2f3b741ce70e81a9bf299677be8b5648779244235823fea3ee7c8047ce2396cc7409acba0cb89f3f7239354cf8a02258a372a15ee9a39a9b77d84dbefe7c4986becdc7056a9eba4cb6986e75482734820c10d7235d4127f92e922cc57189fb6134ba7965698a289333cd4f20c914c9befe7348aadcbad0395bc9f6aceaaf50100018ace8f69f3f5e5f69466557e803a9d531bb5d89adb0d95f606f3a8ec58519ad54a3039a790f74d2c0bb7c9a47dc69c643bcf7d1e1621ed8b37a9accd18c754107bed1bb6eb636fc08e12458124f352106d60422ca18599316fca082bfcb84ca210195802d2fa5975cda6f0d109efc473fde4ba8cb6b98b0b61fdc828b678b20b7a7afb70ebfc46d745c636c926882bf822666bfc8fae4b8f4d9566a919b76b7989d660f2bafd4f576aced07de194298dd5609ff203e7d23fe1b6852cd895814b6a87cec00abcfd120518f4f4b6250f6f90f8fa51408bbb552d3b9629e7940c98e856070f0c1751cab7aef17b6f710a153bd1a233a70a023870d4772f036f890fbe6bb0e1b5b3642ddcc607f57f910c59c06715a937cdb16440d3716c3e557ce1ef867c019eeb683f4eb2467d83346054b4879448fbcc91b7480b09fa6b775d50f67491cae2301371004492d674709e43c67f0802ab0c298210b03f246473f9175815e416f5db37600d5ff4ce2733955c167a47409358b1fada52165f9df473d22f56b005b2efe4a93cb67627d1b305ab10a6dfccd6b9cdfbbd6b4ae705f6df6f4a07ac82e7b2c58595dbe4adf06dff99c8bb3123eda931320b91d5c85d6cd25ffd5f65a88ed32675fbf2cbe89c56cb4393bf4a67211b665be6091c98a2f47800e30dd74f355e6f9c4137ddd1cd23bc0acb95f4da7577caea8a765ccb2478539608d0a955c1dd85e37491510604f00295244d0ede8abde553b7b46952c67833dc73b707692fab530c6c55f3f08e9d0758d7ea2f47d6fb2dee6eb3e3c7ae0c94fd8811b1460ddf7bdd990cecd529858cb6d313c051eac1fd32527ea1911781d78ebd57f413b0fc56148c1749142f331c42a3130911214ea5d8b50276ca3e4ac2d140b4dbf161149b40ded54b99a8661c849c2ea330e5b9a5696c3fe7684a5679cb935c36a5a20aa77545a1db3777a67482db5a9bba07c4f5e24becc24432944f22269a35d110df54121ac0ed28912efd9361149527180bc1bb3d0d5c783e974e6f14759d6de560adcae13954ab44a19c91e5110e2a823edbb8d51a19a7f844ec829168d978cb765d9ccb6c933178ba67601e4dc4453781f03bd1d765ec84674d3f15ef1191dc68072448d31156058d7da4471fc776ec90dab18afb204622c15ed9f13479a3721d15491daec7fe05e91218ff910d6e2a50c3682c01d83e1ebc7f1edd609191ec5fd4b25295a843ee393639711e8f3082179ae7735d5ba2bdaa4f6e5610163989846ef31a094c32e54ae670b6721131d5ce9304feb654fef1bc2d5ccfeccd15e56e8f3cd14264b72b85df9589de0fdda3adca541627bc69141affa990691dd5d37df0c834c212d34f0a0dd4cd5cbdcbbada8b319afa4b6a5d0e9215191a028de6466afaa2d3352b483662c27ee9f3b6a2474285a6ec05036a669be84d3ccc5b6e17dcdfd4e36d34687cf3459fc7d6008f6714ff53a4370ff488a2f39ab366823c559c9cd9e0f75b98087f7ff6b5e94c1850de7f0d89a3ce6ec96fc2ad95a75cba7f99b3c76b8c5588051d32d5bc9d4044d784cfc0248fa53858df5c6d414cbfc44f93d54878da618175aee924e8bb6c50c37ab0a6e2911e01f583f0dc499545b5da3e15ae462d0c883ceb5607ac85245f2b757364e25d8d9af6e5b20f174ed15c9c6878d9e969f0d3be6ad8ef1fc6440ad33b240ee35be38bfc21e6a0a34e9591e1eccc43b04a39a347e4f97812e3cc33a7ad9fc4b6333df14087669e45732816ab12e0073c371c69cfdca3f629db27b0e87b21bf0e2048250b3e02bb5e600b39b5146e708782e3247a98ec991f80b1900054cf8955a956620e8865401c0061c2673d0c2e858a0924e93a4cfbe9cf382c00f89cddf16ddcd4c0ba4bd1c0e90690d7cd38d887aa61a69451043dbdfe1584489afaf2f2ce2fee34cb41e416bc9077c7ec4f8b07307ef4c4a6084dc14e06381039c1746e1d6e53155b01703f1754c7b9ef2d63720d1ba638ebc6a67813254e719576f769d6cadf6a7825a0a104408bc9f7ea7982c9e94d2f7a5770d5a2755570f605b2d8850b7d5f3ce9eddda394718dfa1716d380c715773ed9a4059c8c71844e802dfa42d021f07ae38130759c32624c085b6aa3079d539e1f09cb85e32ca0972214306a4fa8fa7ee3f73a866d9edd31d026fac9c4cad5a6da4ce9ad154c63e8ebf8486819800aaef8f5062896db657352b28a2b82c15bfb2d04149ed6046b0f688003266db4e2d872b3504f8f821cf429b7e071c72fcb44c83047272c6271596041088b04f5f7c4adb1e0b229a6b4997931b48d379550d38d6a4ad250de45e2d4ee96ef0a0cb600230c859b0ebe0dc16754b448ad4af546d171ec01abcdd23514286b3387015e3428534ab1b807f3f02706bdcab12c3db3b80c83ef1319a48006539ad9eee8d9ff3e712316f9f7707b3cc10b56dd6124a6e76a1d0d232d71710ec399876fd83683bd683e8f47445c92d52ce8a33cd8a228e57009a2663888e94105ab548149693b8adf404cfa52a762850c1b63c78afcfd41d8817d59aba05947f2e50cdc5b51b5f97d9e03e77eb74fd9b33b4b53c30cf41798743f1a5480d4773c1160e918d2624a50a742fe6d1b356077c47afffb1a5d7b9147764816bae9e6901c038959eb904d8b0c32c39035691e129e8965d28e48770e19f957cd9679fa42addba57d1c7be4e84101c877116eb1d6e837ab2353e2f32e16f6fbf7a6586d7feba4fcc1e0e8ab02a709a203c099b632f60aaf3ee410142a7300dfe346a39337c64606a48300cb5e87dc938ed3b53add121a69aa5e346a22ab3cbe35aa1ebb24d801561ff4f6a958156c89dfa763ac779f00406e2659a5837d6ad805ce251a5369683ab40b101406e45125c9f6aaf329367623ef920b8d34002e7b5f96bc689529cf65300e3a65ca05a5261d6315d72684cf58595505792f7313a708ee38c5aaf5850e4680d1e26828125622e59726657db165c370c39ba2ace47736056dec0326b9070bf811e8d96c69baea48a4d87f463338d9a7a9343374804216e434010124d42204b557144dcaa611fbf15be68fbe28fb9b7c065eee2521b2c2aa5c8cb9e524b9457eada49c24cd36f549f4061c5241e4acea1a3e09f1aa4e4d97f6e740161dc3698487221216899f34a97e686abb7a8aa809d2b4177cf93ba92690ba1396361617e0784c0949315ad18e3bec9c5ebce9046889a7227d1aa6361e3b69ce0e06ac26e4abcf8375e5e774e0b60f28f31a50ffc6971aca6d53e8d47c3754b178c1eb0788fedcb642f53265e38f291ca3e82d098f676ce72c412ae3ce4eef423287163290b7d30464e6fbd9e8d4fa5ffb9153fbdc61f161511649ad56e4360ad2f258a285cf3cd2fb643d8e69376d1484373f872f6898e5d931f294754b454c74e55d6c0f8e6821b0525380a4fde4471dca0f01b3bac9500a4a6dbd9ab23a2c48266d423034f19b920e354473021252c363ec1f44873f843a6214b0036aa192875c3052b29a0eaae9682e3b428694162176a589566bb5380b2033e181f5ab864a7531cc6f45735624a910eb78a7cccc700c1611f5b88e618e415875a49f9d0360ef8c9d471717beb88b857be92c3e3611111112d2f6967b4b29a59449ca160934080a08b8e3a1b35269f85d2e3a75f62a4735a9ad54866013e3982a9f096176583f18ccaeb9e1201176a84517c3a65001c51a73ba092a16869f4c580dec460d9526180684c4b1c69c766266318681c7d68755afa67e08bb51bf1a2ab1196361b21eed9a04a7beaa5e1c2ab63eae3d43d8eb8e443d39ecd095a76013cef34e5badad50887a7aa220e2f1c266a1533b12f118e5e0b142e99d4e7724e2f929aaf22d14c95244871990c080441637602183a203221840e1821c6c240184961c4431e5065018d939e20a12a01e9a48e901d20d443120a9810f2cb072452808a1228cf84110a6b002071008c12305862331d810748226560004911810b1840cb8a8d4526ab728a1210536388125083b3d62dc4087a22c52700425ea0d58f8e8e0892931d0c10f13b2ed065b0421893205143d2841a5072168a82284113f5f24f153441158a468c1c1061726c0f09141083a6009a306113cc962891fa2c85cb042ca8f1f8038029220308192a58693201f78a0c40a2f6a28d2012a508adc80c89222b39f0b041d81e4861b5060842c92c871eeb889343058a4d131764f28fe797f6a299fab724e77f726534f59659caea7add55d506bf8e11a3fce38e3a71d9cf3ce39ef0c9e95c31a578f93914e269986946f6ae2342621293ad42621279ac3314ad933927cd0f14d42d4249427fd4c42719b846824991ad3a961a83f6dfc289d5bb4c0e53edde78cee24bba28d73bafb984d00770f11a0837e2e970deca98a60ccd1f989b493c48a23238cc8c288e80434bc70020b032d3630c2891f183901921744a596529b64c42729082d2ed08ad080892e908c11c5c80c31466a80c4e8871910662002068f93150451031fa218c28a164942206a4113488e541fa258118c38c1119e00a248173050e00462c8f0e4ca0d474821c396229c52a872854f922c4d8851439448b62c619a1d810220d8100430963cb9a24370cf0c331f26c3c15218da15b118230967408807258c00b4829f246218630bd44e13141421c2072d3bf080e8051b60b121071ef0c87e767c48e248500c536068a244ede9810b487a66d0021918b1a2f2c41d2059428b2d43fc64992d416154a5821f9608b500071e92c01586318250c580832176d002868c89185440082f7030c30d2f88a286178660a1e881912b726045910e2c8840962b5a8a30c2173fb8a0ba200691139622285baee850041617a0d0225aa094fc082f62288a9105083df041a202a9871adb216717848045125f0c7591c30d47495a72882206407021bf08e30743a19144f4440c0c2122665b7aa08a3ca84106556e10a28959102cd09bee26feb518af40b2e9eb53d3b40f743d5d934995514a6b34a5f49f524a698d8f6c01d10b3df81d895e68626f3b12bd2003257cc95196179e6062862470423d271d63cf398fc8c0980113404d70e1029c885d2e4d06ee3e3dca10b113d1898691a1c82379a4c41af9184ec5b03bb12b4f28e1c20a27a29072845331542085082ab4c0e0899e2240c043d81e8af011824881124a7890d2242a11050fb020e2420f482e205d23502d48582d9070420727ae10696209096c7164c90e8698c2083330722c251ac6117cd85b7cb0b0e383859ddbc456eab43371f97625d3142a262fe2943cc7e93c5b64f6846b5464e908b4564c1fcb04c81d9f84d1638c3132b9300afb60f5085ba9536e964cfaba0db0e643537c06d9b6d9918808155bfb74c785c68f3d26e9300efda8a9af7351f099fbd63b1cab8da34ca8aa7f336b36a537218e9386e2a4e08fc80d7050ca136a98d01fb5b0e0337286fdeb4cef33269ad5965fcaff40109fe9dea5ddcf9df7e2d1792f1e9c0e7630e270ffb0c765558e7f61a8daaa47793aeea97714ea7bf140bd7e1da82f3aa8fac21f27ecd417fa0b911bf5dbebd27b9c274fe5a0d463e8b066fbabe02ef99da05fbffc6a5008f2811d2bef048fc1ae685423a39435352ad78cf01af937537a53e87f8fa14c58c20e67acb67c55b49135fb4e1acdc69fb068af8b11bef94dbae74b0af4630c8b325ec991c3818e537aa6ecf09340e289884cd951ca8e444462b07790c747c69e89c9f4c5ec458f5f3f47af7efcb8d0f7b3ed746c9fdf76397ce79749c84022dae44f469491db278b88322dd0a18c4923d1a6f4920335b63bfe2b559366c5014d66c7c725ecf8dd8c1d9ffb6aec30e660c31f35db9fca586c28c686b412fd49836c9df83f38fc510e209403b8f150fac7ffc16d4aff8738c616ff710ad5cf0764322499c481a5805b48416b01bff6459cf7bce6ad7f84cd188739ccb5dd65f51a04cd465701fabe7d1fa45dafbd6fa594b2e5245621edd70ec7b665cebbef71d7e3be7ab83eaeb55ef9dbf679dbbc7b398ea33af66ffdcdc39f6bf682f8ce893600c20641eceeadecde769f69f7ded9bf5d8efdfb2d215c52feed78dcb71f646f6f734efe5e39328cbfb34b2fbb1ced7b69bfb3b59b1906d93fb87d734a8f43ab3e7a682ef9375419ae77b9ea17ea9d4e9672b0953aed4a339bf4ed4a267d3ddfda642b9d1e692047f07dc044445f598b2a92cc2f63a906ffa63b0f4fd93cad6e934763319c92179f18e2812c86e338d9b3456678c6488c918eecf8859a52e1554c23afa28ba3bff7ca7befbdf7de48eb0973626dcb558aaa505154a50a2b91eb4a26ce2b39310979a147d9f14f4fbcc98eef312dcee3312dfee33e494555a828aa5285152a7296442e913399443a9147bc8b2c48b10b2d4973283c61c73056d9f1ab8933e9933c9d4e5a9f4ea7130a954aa95417634ddbb6ac6d39735cd7954cd4649a2e4d5c572a994c5a9f4cfa7442a15229158a9baab99aacd66ab6a66bbab870f3e5858b5ef4421d67d0f0cf34b858c3e5fbf442eed5e990fbc54d4f46a743eee9cde874cce0a647a3d321b7ab06ad5163baac814baf9c3dbf90857d3f3c037f2c56abe572b9b896153dbf90eb70bc70d15f2ff9aa2e2f2f2ff4e565fa8b7c399d4e28542aa54aa53c954ac9542a954ac0124d5452f47c988a0749e9123dbf10bfea967b670fc1b9c8b3e455c937fc468d7f34b06fb2207638e2471df41d1dbcb1430ced8e0eeaf02d778cb0185fd2229c4e0ae3ffe0fc6a1c94afe3300eca10f6d9e77ccf89437b3a0c461453623b7521feccbd04489bbefba35ed0b5fea8b22c3464af5f87d55aeb9cd3febcd3de1d975082bad05dd160cd0e136027dc4cea529a7085a633eed29d892d4b6c07c119b43907bf1822bfe4c3602f3af84239c2642971e2fb3f510c03b48478e32f2e2e72ab38eeedc6435afb9b1737abcbe1de7eec56ef512b772ad5e5f0bdfaa283dbcac6157af5a5a73eb3fa5e42e85e3d10d35efdb6fd6af5f34fdd03e8af3e2172a71ed5e548bdfe1c004e461b1b3ad02195c9aee99aaee9e6fba97bdff4c468dffba115cad607d1068844da4672df4f750fa06f1fd5f130f29999853d42753ce85b9510b9555fa87ad75b2a7c26dcac159fd1d96c91cfe88e077ecef4c48f938d9fe34c5dcee6e25e68debf56480b5d0a59df7a1dd6afa28dcbdfa74aa2cd8b07c4b47deb592e9edc5de780ed57abdfb67ff1e25efd8dbb7871b3364f88dc2c299305c0459980b8e36f2fd3e5c8319cb49d8ed417ba3ef5fa6fa7c3a5d4d9b842653aac0f22f7ea5d9f8d2b3aa4b27d3ff418eb6b7d21ebb5bfab4f87f5abbfdfad9eeb72582b027032dae48857a60d231d7a6cdf0fe9cc676c687aedefd3253ea363fa8e07ce317d8e659cb5841db46f1fdb0fc7b0846d676c1baeb635eab868c3bdfdc21f305048dba72f17e3c77f33555edc292f6e9477f242acbd20d212d9f74bf8c6e75f6a89c498fb37ecfb214ec1bedf5dabe43ee7599f2873bf250e563aab5f1df41a71b090cbfe7dd98f07b86f6cf937b6bef762fcdd10efb72df93648a418a5348a094874090d4da26f4ca31d7a94ed8fb49d278afca2e989963b4a299ab243179211bbcc65a0dff8b4522d061cac9a763a39911853da1663978b0a913843bf9e16a05f1e691e73e4311ee3435ee74454c167e4a61f2d067c26d4c1810f031470b0627c33b7af2037f759a1eed07d82d4eced70b07ec95301034e232b3d921fbbfbeb88eed910433f97bd0d7ff67636fd280a3857aec19452f72f626e15a429a594fa94bbda41e7971c9d60fd79a4627d84829bca582591287fba184a23bb7638b4d055dae15e0f07ebbb00a11d85af878343a6eff3239fe6b9cc93e8f8ed3307eb910e872771b0beacb5e9c72ea7b5dd48fc59816ece0bb5fb34a13bfa0dad88746b47221a88d8398cf9c6ce1926e7e79cc673fe7cb9182fd68f9fdd67d238951953613010bc5ff85f574ce8c88111d030db3b7ad81043d7a7a9af0b407d0873fa50c69ee2607d70d2a430ea5da7af82430e7653747df9a1d31973f97cb17b8443bf179e3077022603045ddfa3ab91c714ff08a3d6faee614f3382e6a1b51ef90e96fab055851e2e17d27421a90238c054ac312cca0419c107a2951d672c084c3ebe57ea61c81a7aba093885b115d4459a9ae48e38cb06f2a64354cdd78aa03d94b34e9f1fbe0ca245681c0267421f7503139fc7a13fa58cf12839fa17c28a38e033613cfad1c347ac82495551e1b4bade415cb16b180e39c26c3f3ae4b81264cce498018c2863e328e32f39f8ef9f40d08f59ecf97a7a3784040d2f54624c2cca31839504e498c1280e206e302491234c11a820407321688256c9d8ee2e97f897b7d5a04319d8ff7964f9302f2ad06068d803727497b645fbfff8746ac39cf1632a6b0fd3be17fe88a3bd148169580b2a9d73ea806027b6e327646e9df92ec336993e7aa553b4299d3a54b4e9505c2ada70a96813b36a156db695c68a361aab65eddf6813a38c67a34db4b12ef525dad49728e3f4156de294e133a28dcf8832fe34a24d7c7f196d6844991af184fb1e65fcdd15e511f68ced304ab963ec74786ccf2076cf38b14f579422cf899d827856ea8558eee8e0f482cc7d4770bb1cdac728136a1ef682cc7c3909e4eefb8fabfc6294911f411bdc695de2de0eef260ec5a138148750f013b60f0b3c3f3aec570ab605fcf67bd1c761a2e11d37274cd78c9934e8300e590b031d9ca0a452d26f68a6c52a8889bbdca1521a9971d2e09f0820c08e5b18938ee6cf1889c38618ffd91d63e66b52ca89f19cd5290bb2932718c87e440ca41070b002b303239e0cc15ce150a2bc80688699cb000977e146f61e9c873a631723f049d24410a8ec245bfafc3401842ab647a47a64875cb6dc3c61077738e88f8283fed86502cc8ee98ae0ed78d4bf737e9df5efdc1163fd21a9e8f93caaa44f75e87ffdb48372460f770b0ac3575fd0a70a3d288d451189a77aa1553b12c9c0c5121a65e54ad128a21b8536195185b46652850c3948a1bb1d8964f091217663c0019dd118a42002450c4dc8008550a562da6a6da582288622982882143ce4509fb80d6bee4e832a4f38b1b3536aa48246bd7e74624a54a27e3ba2949de7075344474abddf43e40e75ea8339b536a736a786b1cb45298d484877908a9538a7c4d8d648a5d60a524aab9cd3e239e72be7ced290943e69b5171760efdf326ce7e8d28f9febb42d0f206f3e58c35d891b8271c9f8f82af4d83169b6974202d035b248ca3c1f170adfc3c71c123e5236637cb4beb600cf01a498147b97918ff4299a3ed3c7c7c7c7c7c7a44d781056a30b702ac04d41c1944e4dc4bd51a3d3a5c471a04d8291c465cfaf5510c5404387635aa00822560491c50d80a2200205b12d72f882083440c1d1810a8d6a0f63636c829617c4889203212250a61157847e20f3811638a88233a59e15057bbed0c39eda9b98341bceb6e583b2e7687b0e72183f946fedbd2f1f3286835feeb046f7a676570c718c47e8cca34d664cf47fe5d46c7f1d21736fc0738c268432467da82c4988cbb0c321326cff4226248833260c3237086610bbfd8fb6378dfc68c7f8d20ba7951d7fc8dca94ea750c519638c739a68ad54bac7a2cadddd2d0ab2d76969a5b3565ae74b9f95d63a71f867cddd5a5be7cb9526a5a53f632e581f4f6d85203a886bd3f992d5da32ad955b8518af74567b7ebd126bc92729afcb77f71f35769d31699ea1dd94524b7f06716d5b82ed2fedf6fb7994a1e16cc1876c7f39e76965728f1541bf9c6cc501b8f68b06b6bfe46ac7636eff58ad0e6cbb8a32f45961b8da5467fbca6425eec9738bb3fefc6d7ad402d123805700cf0051a662d847020c06f3191260b6fc37600ee698e2e10e028eb37da624562a180c46b5529560d126a69648cd949729a594d2d2ffe8b167a8fd8f1e2a956af53dacef697dcf64cd2f9dbcb0472a552a71dafba71e6f2a3b4fa530a77a80f6a92fe43eed63a2fad80d81e108512a614e552af540f917f698a999eae12a95cae5552e2eaf52b9ac5ee5d231e6f4abef393debf4adef893186f517e5853d7a6c13ccd5f2589ecbebbcb0874aa552a95437ccb6c69ccae4a92756abc779e22f7da6ee5e8efbaacf25d56b547b201cfd84b8bca6711af7f4b5cc7995edeeeeaa8fb1db11555ffa1b4765495738ead87fe5e0cdfd8fbb85ccad7aeed33c1b5bb4f672cb0efdb9ef72ee962d5b8eb057bffa42ed63c2fad80d89453842a8fe9543b73fde74abbc506e0fdcedbfb3bdcb296dff540f70f7efc5437b7f6d04b158cc858db3e94ba57737b9fe2cb9d3873b421b9bb66cfd996e6d6e3addb7ffeae35ee5c528c37da1fc42ed4b3b425a7e20f7c3116cee13e2b2b9a714c87d10e8686f7a1dedfdb37bb14321f40fc8fd903eeb7bd00fc87d4f637d3126d43e6b1f10ee634284f53d437a76bed56ab55c3326460284318a8d7ad6c7ae87eb6f9ce5f50c9149428b1f9410020d78b082d3fa1d263878018c209000e1420d70582c16cbf5a11009807ad6a7674c7d1691ae9f7a3d6988b4707a5820d2c2e919c26ab55aad56abd5727dabd56ab55aad8fa106a7f5f598c009247480820508404238ae2f461d6350df33a4470447529c0c0188c88722e0b47e08ebebf97a5a5f8c3b4b78a4e0e1a18a162c8a70aa0e484c51039623724024f5838509232452b6988253ff66fe46e9984e61fe1f3b42db14dba6cf3fff8d873b423be3cf21feecdc90adbdf3c431b6e0b01e0689e373717a767058f40bdd814cfc9af3109283f7fd1f776b9f90b9f117ca13c8767d2c7bb2eb53ea5fe86f9f841764f9d483817e8d917edcf4a5a494bac72f72432028c31b5bd2a21823f1f5a1940a79cd955b6127b62df5c2fab43ede81925313a3094d664cb489c36a3f9df9698fb76bd2bc3f8ef6943ed53e7a64c583fc150f3ac4f2b5c7b6fdd8961d52e97fc40e29a54fe4116b6d92ab524636a5945a4a29b5d65a6bed5f2bd5ea39bf90fefdb8dff59bed903e0e6bb6a4f6665bfbf45fae7dfa3a54e30febdfc72e5574eed38d3d9dfb09997b03f64db05d3eb3c20a425cdbdaafd996c33129a59f6a0c2dbfbe0c2eb4162dfb29e2c9307424b2418ba740c3f80372b49ecac1a90a42df384b4eb3fdc4326ee6e7e0fc1b3730186ca4fd259ddcb6ebf69bc7237184c04f3b1cf565a731d928fd96126d6f340f3a8c43370dd851bf15eafeb10d6dfb810fe354485a5a37158a32928b701bdaf15b4a34554955ac34d762fc591ee8d0b59df318f92ee58c529cccb0a757777639a3d3524e94f128250822ece9695b7638525e48a74e8f64df89edf93b49ecfa2bdcecf93b5126be6adc3af363f47c67f7e2a4f134b63f0d06d05863e798e3c718eeeceac92ddbbd70880bdb63b5c3417fcb7ccd38b4e347b7ec4cdd71cf8acafccbc35a21542bc69a96ddb55b6bc57fe355abb5625c7ddf6f3253a26ddaad72b6c467c2fa76166d94441b2df426404cb573007d4c5fc30dd8e160d5ba3c6516d27814a0e5fb88d9314e8cb576b9feb57dadb2ad2b3ad4dbeaccfff1f4c3d2a5898ebb7e2a07ad3fcd3cb4bf9f4339b9eb8541e4d6a99f6b07edcf20d2e5a07deda0854094b173ce5a3f1cd23565d4f09733bffefcdff1e1a095b1bf22f2e1a055a1060603c17f97ab07137907a42a7d768c6247a2224e76112d5beffab7fe357daddf39b91dfd6afadae256f7e9adf5627cb78b73ce9fea72d07dbf78e91782cf4dfce1b81fb681850ee5c6a2b1d03a41e4d679b9b4582b552a15a983f8a5eb6aada79757e8faf66d60d1f627b7c38e8769d79d67442cefbdf9c59900432383bbf7de7b8f501d0e377223cfc2678ebcfbf9a7ce01f7ebeb2ee77e7d00ad79ddb7cf39783f1ee0be7fbf1b4e85be1d0ff9b5d4e5745f6f77f5ec7eb5bb4e46f7b4fb6ad20fc4b4a9dca5ce01f776de7d7d4d9d10b93b9deebbd7cfe52ea7c315d73aab8d2b74e4b4d61c1765bab7a1850ee350037c263c3dea754e4f7ffecd8e362a15ea4ff553f529fd7af2eaab6aea543d21729f3eeae9d0af8ff2c28e87e9eb6f5d0efdea25aa2169c71fec4854c3cfce247051a6e3680e65c7a39463dab405df610bbe5f39738743362e759cc9544b35735e3a7d610b9b7e986af8ce5238646307ef675be99ca53877e7612f8bee4b5e8877f77d5d61f3e4d1fdc21d5b1d2567cc20cbf730d6dae57a70cf1f717eac3466524a292c7a8cd8a30c9def4314092a6f6cfa31cef93948a38df1e3ea09d980a19a4dbf1046291da294092d635976ae949e758d07a123519124fbfe6f2a895025cad6ea735d4eccae7170e65c6d893771707e6e3242872ee442d106e3d0d5e9f87d7fe36ebe17e3a771468b3334ced0fc1886c37638b4cfd903f201ed35ede96b40e4ae2ffba1aedf091e7dfaa998707fb9cff7e7fb136dfbf979bbdaa65d97b9118fb1a1850e5dc8be26146d868836f9a7fdfa99bef6d67300fd5a9fd206783b7a5cea0991db7e2838883fe771309ba634392d853bfb863e35c1c1f9186bed72adb6d0e16b7bfcf7b57d3cc08d41ef466e0585f6974a728409edbd3976b21963dfe293fdce7e42288e6b59fb9c7deef34a26c063443f4e36fdd5675794443042dffe809f8bb2b9df80e07e5bfdb65a3dabcac62f9ff8b1a18b1d69b023910d4776f63869e30a6ddaa5ff38196d22f8795ee8fa1a3468bc5a1fc2b4bef42e2faf97cb8b27f7ebf5d26ab55aad9fefd2ca315cf4f3f5ea535ae58e2ffa395e14d3d9e5f097de95127b2f0cc6177e103fb8e98e2ffc810ec6b75f08d26fd503ad232710791a459bd597563bf68836a9972f67d106156d4e2fdf87a28d7e59ad0f7bfe955fecf9d8a7ecf91a0e7bfe36444e79aa4779a9cf270ff5597ba7cf264f7f5e953cbaf24ad55eac6d992b791488dc22e37e187150f25e3ebc681fca2f4a9ecbf6a14f2979aefce1c481e3529eea4b5e8bfb7062d9258fb5bb8c578ff7eabde8f58832f2519e9c4519f9274f22f95094916ff266c99b465146ca48832d956c39049712ce1bf6bc6255634c645aedc51ad6f09db269448568b5175f3c43f2a12933b2d7bacf907c68ca68b5b56886e443b4d219924a8996efeeee2a25eeb148a54428ca30090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a9a2e573c4040404040404040404040404040404040404040404040404040404040404040404040402b223e69b5176b5be6ba92499f5029d58ad572b9bcbc64cc98bd662f3397996bd69ab166abd90c1a3566acd96aa69aa566a8d96956e367a7999e9966a559377b6fd6cdb8599e6d33ef9b6d336d86671f09333cbb33126066770653c26a464ce545963e69b5176b5be6ba92499f5029d58ad572b9bcbc6818cd623364bc5e5c5c35de933165b35a64e46ab156aaea7dafd994a550276dfa487891328be443a652c79100e32243e22e4c092ef9f15f3ed9f6f74d56828dd636da9a90d16a6b422ad08400c0a9540080151159dc4dc3f2e28ba58fecc8bdf7de7befbd17df8b2a9258beeb44a59e8628bd17df7bebb5f7627cf1c51ecaefa38cc8db3d96f7defbd2435999a6dded6e72bbbfdd7cb187a2e2e0fdd3d0d0fd1d9c7bf1758cefbdf7de7befbd8eefdf7bef957705baddd7eebdf7de7befbdf7de7befbdf7de7bef939695ab5d7ce5bdf75efcb9b67bf2de8b3fdf5bffde1973ff7ef6b0f79ad4347cefbdf7de7befbd58caeead4d56fb3ebef765f80cf7f77f52ed63781c21b6e7ec76af9417dffb19df0ddf7f6d9b76efbdf7de7befbdf7370ddfbf57d2406a6d413fcbe8e08fecc7488c67c6c57cb6ec89b17a3949cc47ecdb6c649778ecdb2c7350e69f2db338aa59ae1763cc36e70d36c5caf2664315ad40b80b5c53fb42abf2197dedcbc7f4c1029e3114068d3fac3966ecfbf8ab46f7c36a2480593daa59eaa69148c90fad499b5a9e4dadcfa6daadf119fa37f2ab7fb1e90f3be4e2944d574822cb3eaf14fce7cfaf853c347138ef95c2cc54660ccec6830eab516e7224b4e5e72725a8408735fb04919b0b655b7e921dc22a2ccad0df8674f75c11f7bd5268c164b232695e3e3816baef9542f7faf5d702273b15b8144c5f2efaf2504765672b5be612cf1038a3d6e39fa51237e2e03c12e78c651e07372db55a7b2fc69a36e396e0d04db659ce1cd7e59f2ccb3e5166963a1c39b669e171d08883f348944599f9e3a096753ae8d63e9a6339d1dbeb0e47ce48591c9c3ef9c7c169ad129d7d1c9c5916cb28548723c7509d16727082bb56224a9e76a285b892a78588d04e1c64b124abce57ad565a1ccc3107672b02a0b4254599f9decb8729f5a6dfc1d1a78f85d36b1fa83fa13e763e4e9ffa583825b1623366aeb6241dae505ef67129bda222ceaac86776ecaf8a1c9c9a96a39f6ac75a520350b4d2e26344cb119fe1b2ecf9ac19ab4987ebea68d585cfb80bf62c9a34ab27aba2d593d51370a79cd0e1eac99ebf2a3a7ae233e1aae8855e564798fb2d29dad49f1246bf0fdcdc96b425e55511b84df8b10dbda5bf429a343664ab241e966c3e2b090f51f6dc92f6fc90b5644f96cf9e443c54b14396913d5947f60c57487bb29aecf9b6d22de96514f3993087b968cb7c26cc465bfefc4d8bcfd4267b16614bfad9331bd9738873160da48d7fdb98986c4859427e310afaa95476a432712fac111f766893a06693a662d48c7361d7af76a04daf3f72b31dda188e4ed3b80db3e3bf0bffe713959d978ffaf73b0dcd186cbdd3d18cc1d73b19a17834edd33cd40f4ae6207e941107b9ee35af84f2f199888a3988bff3503c0ee2770fe5e3207e2bdb3654916b675d349b31f8b10c358acad093a1139549f3f2c1026ae83b519931f87770306ae6203e75291e9f09eb91e6a91c7c755f0edf9d8c2a8eb224f9cc4b50948d3f15f319b9f1a77cbaa9a166b50b9fa92b5855a143d40c3f7685f8664a5451688d8427a3f08484e3917e1c95e464848a4d1a8c83ff343469224e929123948f955d4465e36c05dc4d367e1b3be233a7d9a489384d849eb4ace03ff9f84c6c196ddc3adab88524dbf89464632eb6f16f5a8d790191bb0e45192c250215a71ec9b4f6dcd7a38dbf0a0d451beb5d4fbbbcdf7cdb349758928d634352a08a84889260214a42c4cb9c9be6a08fcf842e99cf84d8f2f88c7bb1f1126c8de0d793c64ab4616dfc15778fff8be86bd3f308b1751ed53cebe3d91f0771f628f603786cc6e024ade14715e1cf9a57851cc49f4354d1c6ffca797b54d1a4a14856b24021f94cddf8515a7c262e61e5c8c6618a676354151b6354968d51491ba7621ba77c36febc69f868637ffc11067f3a34905a59b29c33461b63a434ce1923a5b386525a77a043bc5da02777f4549c8c8fb8624d732d853b39a6cb9d86c6181953db1fc72a6b691ae5892f2a36e79c1d163af19cb3ea39e79c4e815232463fcd395d9ace49e934d592ed2e9d73d298b78d4e0dfb0ce66e674b1c2a868a55d78e26aae79cae39e79426284c5268f9d5ea49f18c9933aa5c46a44f5aedc5da96b9ae6452edd40651a9d309eb94de9903b107d33018acd4a956a615ab04f3cf6a750990e16ab938970bcd76583440a4c18fbf9b19d8ae41bbbcbcb4970c2c63c65da966d0b0aa140d550d0c9b3155f360a81af4df9bd1e6b55d01a205a894ed775439cb10d5000000002315002020100c084422a150280c6441563e14000b79924478561f4b83498ee32808820c01c0180408210618c00809cd1015024d20986ec6730df53dcc7ceb8301fedcb8f6e2c22d1f688c9f98d85af88b36b4094de2fd6d30f8d12931a5ad4172038285ce6b9e89e0a7275b32d3444c1fce82a2a5a29422110750b264d5f0e1db7557f635a8247285e968e83cbeb619a08bd6c488a03185ce218848d6c3ecee5260100650b0c597fb5e2b1ef1e769f1076215aeeeea45c462453524ef0baeffc017e2f70499490a9941c599f1779e04db08e767efc74c0c1a22d4db00adb53e362a26acc955c486273b8b4c993ccbc04f399dfc47146a08f32e069efeefdde6c1d91c11a9ed008c7ae57b3a44383cb9794a5a91b19084842029ca1ea6f801add44edaa654540fea2bb02070d740111a74cb6f20923ef7a68c1d24cc44c94a1212423ecdc6c1abf35f453a37b77fcc8b3dcd02f03ee2c8209d29b34501582d91efd4dd1a7bcc27ffa8e9080db3720529e8dd873c7cd14347e2cb8d2c4b23d3d92d0760e1b034511a1e3ea06bb5d9ec154097e9e1ae24ce268bd1cdef046f422c30c417b2cae288e8c47eb7092b4c4840b76116b0908d41421ddb01d1b6317877e4923223da94620516853140fa67402177959024fd292f7f9b44778994cb30ec87d7eac0d3da93b83c98b3e3d2e796adf5bc23a556378c2c5bd2809b00dcd8f34e69d4e8b8f9441b31e72d4471e0a38b668c396ea65d45c50f7ce3e67ff2e99bcc7b5fef3db2b65fdd5ef7e248a67bd7e57d323fe856efdb64bbef3a06b7c34d76fb5a9fadecfe35796772f56e32c392d5454e245179f56341ede941eba7077963c52e5faf32df4fcdde93d9ded6f7dc1667d9d95fad7793f948af5adf9799fdd563b80d6765db6fbd7c357b57ef1d93ab6fc914965e3778a2a3ee04bbaa5c90057a379ef86594b7659fc0c7a401da5d7a24c45369f1cff12839569bb02c06f9f6f82abcd54d608ac0fadceeda44cd55253d37675f3b7773eecdd96e67ba3c73f32e61ed73d0edf69ccbb36fcebb39efeab98b67815b227ad7cebc9df57e8eabb3eeae15ae56b763007439ebe6b9ab6785cb91bb58005e9d7bbfa871094abb78e6f25ae17265429b9a0d274ab591dee189d7f24489f03bc4b945a9223dfadf732825140fa14f6807c7d3a4e57c518f9eb7ca58db046511d4dfc555e32d37825904eee5f09e38f594fa30a8be02f0d3d5ba2ca6a8aa9fba81fdf6cc8ef3aaa7c2872c6e24a32cf1ea7f974c6cef715cb7aea36ba5db290f69065b90f44da00c5fbcbeab2ae28433c7e14e76df9753ae6b0eb1becbb6c516d1113947d0b894cf29bc951b917438fd4370ae32c1d857b6ebad34ca1316ed0db635a3e9271b69f98e921d715f9fa4f7c88998c4377fea147b8d71057caf38d84b8ba5c01d0d1e564cc46e33553fed7bf0bd359aff1673b02944fe034077f783804203e5c6b85409935da5dd9f1815ade42dd0d707ca3d1b495d1ce208e2c720a7ba728588c80afca3f39337cd993b8e80658ba414d2157d92c06274618eaf3ab9536ae1111a75ee4ecdbcc36bc51f0d670971b102be781f8ce0202508a022dd7f01a1d30a3cd9cd20083c893fe7efb1f2eac2a7a3f51b97ed9aff4127eddd5ebe011af89c06f0105896b33aed15127171a4e08b24b27ebf6442eae8803904faf147f386ebc543b6af71eb0b4ffdc6d29ac63e9436acd46f14f04382f912b54f22480e8a4bee259196202b5dd6c843d26f76c60f8a93e56be0c5b5a7ec6482d35d9c81c52fd6efaed6efc7ef0e18f27e1b91084f6359be056f8b4fbb3df658bfc98e488a69ad1488df1801fed4effa80d6a42eb92ca731e69b777e3e9d7e400ea955ebf78ee41351cd475da098dca40601ac7d76279d4066fd16546214aa1e8cf785e783495a18b4c98572c03820c53dc7b8ccae2ee928c49cb62fc9e86ffe34c6261690a4a8e45f120a8b7e5377a415628da6e5b7a2c737783d57cf8a75df104ae150fdd871b294f8e977757efde6515707ad353f9201119aa1a8ba45a3857e3b2d7b0fa864e0cc9c82b0cf63cb948a3fa4b3a6c3c3db3e3b7c2b4999eae79462d6197e08a67e23869617fcf87e6c13c5f97e934eeb1351df732e2007c80e0e3261e73357fffdcd2dbae8844e6137030ab97ed7bfe7fc252f1900b07e7b443f23c06d2a11c0291c128dd8d860bd0f3a8255f8312e22ce08ce6d7d7107f29d34f7505e5dbcb13c82fb2c7b123fb31927ccf8a8798cb08cbe982093097a02423bab63bffa37b1365fb98ed82e721f0a8f88e065712f34b722f1da2c58592cdfa93708c1e8521c02223716a8f8010125ef97d41068c194f351bcb130a21811b47a3f9111a981a1deeda9d713b75b73189083559f026d48ab5d0762fe22c624008183b35fb9e0492954d2db13e5d336adf221c3fd42b2f88645ef5d8a2d3f4cfb3bbf33906b9f257a27929e6187bb91746c622c8e2a2a5a60131083d1472f2b6793934d0b79c77f66cf04c9f8077371d13dedf4d29fe9e4ee5d87085fb662573a595f87614533fc2c9d1e4480fcd8111f036395532cade9340b86d23496c1aa839a9696720d532fce091c646eb816f0e500246c001e0f521a6ded015b4bb6ff2ea3c944e225461a9a7ef4e9f877218f296f6234e7b231ee9d4b3bf1d47d3b53f538bbe55e95e1b2683e805b37d40be58593f764d10d83fbad6f486d501837dc81e580796c35dfc37333e4415f043c444f107d69ba1d384ce3641f25c1d23c5af169a4f958e2dd87dd8e86d87d2f2438b31c63a2c17efdc2250acc0d5c135642b4568c366b00d10cde3c7f684a2cae739b95a43dfe53f53eab09a80b28b2315e0e97d5b083048a6334de954ac948eaf8044a57639f0bdcecc287a638cb041d38a39a81f2d79be49da6246ab5df9259ce45dfbe5202f7833bd27df9fbb75afb775fd0fec92f9c0f277a6625f96aeb81df45348108d29e5792ab2ad179906dbe97c274e9f5a904b1b7bca16557b06cde4d60577f41f107e5422bcb3eb845b1ecb064a3bf5504b758e7e3c004e21d1d986173cc7f54b36ad1cb453f7cdf76a58f60b3c3e07328a2f386bc11022a0787c3b57dcd3bb2f574bc81a993340ec7cae7f1a4c4e93031c1ea35fd4ef8a9180f8b5aa54a059e481e405c078db09c4155b3608568d58edf02173339f93cefc1244d51c250d8050706ca5831f19501fed41394fa824b3af6179e321373b29325295295969f50095967b0af39ced70119448e8289c21a87cd62e46dab5070294a32bae07eb0b2c70d09e8522b77d77c87135c2516c6a4a953a176fad79cca238f585178e13950abe86e3d27413a878b2e13cca354634cba90e139bc18a3cffb1c26391683b004695058e8ae71b0748bafe18d1dcfb64f1faea3412cb212dbee02b12d09a334c24a8726a0ffab7d6416f9e27d5c4d234c92caa3bafa40089ad91b621880f3b676e1fb9bbf77978e932d467753174024ce90795b0565e7560638b10f6e1b0fdb6443d09d1276d3819d84bfd3cb440143dcc9ef347583cbe428c58890908241646930806009aeac0971d6df98fd1e07ec09748314ea22808e2a659076c5948eab87aba78a509a4b0b4fe260149f0085bf956000e12483b6500afef9680535195a564f9c2d4ea12ae034aae4577ab0efc65dfa5504d0b83854c96138d2aaa3591709805636dc06cb90c425f2596863e4ba41fe7b2c5452fd6bcd8454a3d3798fbe1fdc0f5d3304fcf83bc604a7697abacdd10d12b41f631212e0449f5f97859fa16798a900a8a9a9c1a26b58e451d0f434dd5625d6c62aae08adad09eba25282f84092e45de8bbc2e06156eae4758048fc03a1b086325be881d4124c4839bc6461d1120df999da8023534f2692f81c510d28d0f8649d50aa18b8dc519901dfdbe361d00988a1f5f8fe9260d8566e892eb0421b68c199cca150905812f101dac7156d7d0a285ae10b0a06b47760b484be317f90035ab80103490046a49021222aead5e539ee2cc6d39d9deb102a9161e6de106ef441cd5f85e79bb44fdc98dfc2c5228c2a3b916ae47e521dc11b02510c8e9515ef928e2553e080be8c4f3314ba31b44411b4698ee6a88ddd37183178b8d7f8b1618004445c50d67c12bc71cf1ea45eee07193e08938db43d41ec8756e3316f4079d77b15f8a8eaf9f67e1656801c1714aa2c59716c4a8e540e60bb27024323d587c001fe4bf4a5a4a20533991748817aaadc4e9c2d4a58b16dcbf05b13555368810099849094a36ae977623407119f74cab499551dfa4019ef67645d3b5079c210cf5dba14dccc1b7c5cae41402d9cd9f785cdbd140b63109a28b3fbde752a57d7b73e84fbecaed08639e429985bb8e5346b5506bd6b2109e153be0a45059f8f9947fb2e0e336890eaf5884c6c89fec23e450355e457f9f65af1b3100d9f98815c2f88945c73b09ddac436b044f552cf07e56b5e7ecdd3fab2c3521cb33b0ac7ddf28238131c93b50df84f88fa7a277793f033bd9da2ec261e7210c94fe6a196ac59f16805031a7690fc06701e8eeae9eb1c3f8ab4703e15b215369b20006d3d843239b9d7811f469e58f85d6de584c5f06b6558132240959df88d0a77ba84a44590da093747e4837b29feccb9b104b3ddb6446498cf2ec8101e488297cd35f763edf14e133f1b6f9fdae07815c4b48a3251825dade46fe1b2d46843c87e87f6d3b50942ff5fd564aabb4e389706ead0bf97045347ad04df090445fed493147d83a29473721e985cdf8b8c098ae7c4fc2dfe2c319abaa48733594b74bb9fa20c671979b32ceb71e2bf802c5a0361e2f5b0672ab392035b072ac4a8d82eb73d783b9f3556a1c39f3b6348849ef20ae1c1780c1ccc879d7d3b5b48fb74e9b10514f06a6161950c337fcf82accc50b2dad750f7ec8c9fb484cfcbc4f8a81edb00cdb9d61bfe15f4a0500e62dd1df2e9988cd3620fbf86c440b06a601e589e6c01f3bd1cf43db0e628e658258b6fc4ee361f545446a75ced175385bdead4863954b68549cf585ccbea7e9b0f4cf627ca94b01a30f599426eddd490da013b1aa624be82d11826fbc029764b3bc752d95a3e6122ca6928ad618c6085954c9df4e394f4412f47618b1922a81990549f4215c1aa11abcd77bd36825ae065f9b366dd709f248597213004c168340c58f78e31c3435e84d4db2e0325d0f2a757c04577db7d2c1e2bb95ce5742540d87b04316c489b57156b0653b156f38899af785dae0f9dd0a2a8098b1bc9657f9ef0cb2f8f138760caba6e4d58e3d72682f7d268c61d6e29e8a427f2431dc5241d8ab856e16aa83270874aa3a94c4acd37da07029a4f06d7fc9c5959867115f145a1ac01f89b00acb9e6d3db288cf3e6a76f6fa0215ce9c84a0d8ead14da2a5a4052c00469aa3cb201395ba09dfb52326044411768ec0a2abef490186472d6f155f354deef92a3523c89704a3159def04db7470b8b63859617dac0a2dff230fdf8252cfd9a5729904febe0b49503ec667a4cf3d836352691a33cee271760df27195a8748a144a7cb1f2a90565c4b0f510b3925a7aa1d1f5f2d0d82e1788ebc02014daab1bd7db224a53ce74e8a008ed7ea6eb6efdd4f44e1a70fabd5bb1ad2af04103017a1b845b06cba30f7dddb7418895e80b7b26feba7d7d1ce9ad1bd46e9d16dfcb5057b2b366bcaf2f7d865365b743d9f039991d314d099d0ce70057a87b0e92f3961be396572d3f7ad23ec0efb557c34bfc8256c6cb97441c8fb53ff927d14fbb3f791509d6fa77daf67bd951bc31d1f3aaa1af126423a5ab68075cd399dccbf430f3f987c8cb6b7b57f79349a6115e3bb3dce18fdffb2c85ceaf36a21f439267d5e68d7384735aad8c1863d7c8ad981984fd363d08c9d79a59d1cef07c17bc7d0508a8b5143971abead550c9d17622248b637cb771ed1beff61e885a94859697f4a18842d84d81a282dca0f6a6e2ca7a771907afbbfcb9d9cf09abe4569bd7ef163383d1d9f552e755dd85f0806a322c3242b94a4c2014cd61f17edc789b24defaa45aad7e8cfe0ab97b3bc355e93c386f9440a7ef871457421ec0e5219f42a9db45eff6bc3b89ad6a69c221fbfe1608d62aba0934847bf4b65c8724117669dff1aa253f299bf1d16198c720492f29fb979e386d5264094f983616bcc8bf3671d2173c81968aa1191557a89c8c12d320eb04a9d4953fd2ac8cb626360e316db49e9d98063cf1dd285c5d69fb24ca1c137a8caf4b7a25405b529563ed65baddb3c92c1026781300ebefb00356546f85487cc6334b26b06ef9876755bbcc9f4adc3aac8ed0ecae4d5618f5f4fa5038a5a394dfc7242451cc8e7c6af3887535a26d326d324bba343552ab90c8185e55de13943a6584013f1bc196d81a004fcca584eb84917edc806f36ab9a16f78853e13cc10badd4b9b306d52b101f9c29f0a269c9e735664667910168b47de4f8a569de2dd72417b0af1fa1c8bab808f05c193d15cb8dd2ecd273c20b54ac782a73307fa5bbeb547dd454e363c9914c41deb5668d04b46cfe08212fd3a27de01e313a11de6df3151e2c8347cc9421b3df7c4b3205b04574556271a21efb5f274f5d54b4847f9100ef6c82ea91325eb867fa483cc80de196e8e929d1a70162eed40c716b50542818ddacec3569a9be2d603a684a5bd1f64dae4466a3f84ac14c2d396340a021f817206a43631336c426f1afb83fa57a6f095267fa6f589301fbc48ad4ad5ab1a8062bd22a5a837668e153ce0704e7cac0e102abee6ac5219cee14dc8a1f123b51e33e73c39caba8157f99b97b851025e370318f94138100b19de411ad31e5cb68a32af253799c930d9805a97fcabf225e5f20b1e7d5c3126c5ca7eec8df46e6077217f70dea813375cb409e4c2a06c614f5b08fd3a33638a28190f44a50bacff0a1cecee77310eb8526d8a0e9dca518ef22cf0b50a11115c0ab663c738a1b748feea6f6830a321dcfe43aa78b36f1f63fe63443fc698a5c24186f4ba0712dc3007724f2c48d93fe23607aee9e880318aad97f083babd89896ec0e3ae5168bd2c2b7e8e0e83f0470109275304f06614da4d1bbb06cf82fcf95984607089523dd853b29381713327b1b0b515dce3809c4d7b4cf7577d4312ab800cfaf0bfd85fe2aeaa1db1de97c0a48644226b4717c7193e537606633be62059cdb3e3cbd31ec1917a64b527d1bacfceeb9dab5343e9a42d270f78f7e9a30e380e8ff6c47fac81e290237a2904e594ca5198e57f67f935feac22542a9fb0711058a55cd6c895fbeee531606ee382eb6da09698397a8a3333ff6c99a12a8dbe24184586dbf0b09c2a16fe231755c979e1f025698b182ff9edef1067db8099b640602050a2b84e58a6985792a3b508bd121032c40ae3bc465125f00eb28bbe2850e035302981368723280846212056dfe8470734f6633b3133a370a31eaa3c91226abf82fa795802224f2c7e56c4da85ee54279b47f3c1c62eb2a38e1b324db6112c7bf41e545400c0a86c8499fc86ecd63d6787f322d5c2f6a8f14f6dd1527ae600f1251d4b1e8a98ed3de2a132fcead4d80365acf8019e7c6dc4c718f096c373c6664c41c4926a63de3ede7ef4a2ba7f0379ba279bad822e70d95a299c8d2d0ed1ba976d683d35ac385d9bf88abb17b745b4c22fc649846cdc849368207e3f0a68833f550397a5d0d2872d3facfbbf7ab7fa07478febf0780295f41ec6c486deff1230343ec214ce406b175b4cb5dfeeaac7c7285d2f6f37d78f007a0e264676a33df2ff117177324ef33add877534850002ae7f553bc76bef8d2d9c0d2ed7d426c068570b4e9bb443e19ed8bdfe9049a8026d3a73ccad71b4b90047a99ad9a504b8c021a7f73bef215d5ce87fe87e21ac6eb2cbcb7c91c7d31b4cd2802e16335907b4c6ac86d1bf085fa43d54c0805af15383b14ab4ac8853e6393f1ed6aaffcac17d3332bc00def75e212ab0a823054438e43607361e15d8549225bf5dd7ee848f31ddb9d7d66f1d352e394c07450774453f838f1fc2204688cc4d4c2b8e34e21bb4b720e9d80af7f8d6fef6c3da55fedbc8580f56441c26abcdfc6cbe627eeb027285b631eedbae6c37616d34ff9957d4b763087ffe919b4217d1276948d159b8178ed67681c804147cfe814d63a3bb9f0e0a320eb7a2b00e69c1fe8d38c0fe41e544fe14124280cc927521feb3b0d93e1009205e3f8658bb7d6651d8d63b254fea185e2edd1b9ed339685f15437c3288e95d27d41bc0e80222f516b7a30b61d7f5668d6d6cd217283b721c419e5e7a388b4358a9bba77794ba1a10b6f410d42d8b1e345baa5b413d6d7af83d7e6087afab79e71d8a3aa946b99cc85537655c6740ecb0f2ec9c446565426450a381823ae1109b67acad25da80158094790ba012738544ea458f9cc60f1f0d1757cb188cc49946537215e1af41ac2095bd6d929bc44a11a7ba53d6333830c36acbfc8ab0fe2a8aedf5739bea84619d6f9ee532bc8b5a896dc6e1ce32f42c9f3e72421aeb5bcf7b6ca396f97a2bc943f87bb135412e0a02bb90d4d9f48d256e82b98c58f3d5c206f3bce3aadba66946d112f9ef6933e638025fd6dcc3f641b88d000f48eb2aa3d4c14d5fb12fc4927a372ed61dd7913c821e1e9da1ab6090d0e036b85f4ef1f9081b698191f23b4d555c5c9b5830e84362f031a50ae653dc6e980419149ddc4d17957db1cbd89af5cac8fac2973d0ec268d31b5bad94be2e5d4df81d39ce4b63152be458ab53740e3a20132d380809caf790a9bfcfc196c7f2a44a11fcce0a10dd73f9de58751d30dbb6bf1edc22d1fe59caeb9e4bd9075539800c5047e6c18867c20fef3536c3a8fed978e065b9281a4de5a37e91d389659e0f8aba588f2ba99d27d7de35a6363fef59153b156583228634816e98d6adcd60b0a75b48de2fdaf4129287761432a2a5d64bce3ba73cfacf8bec6d46e0b2e1277ea872a056c0f0b12f41e83116728ebab450f5ac1a019b6c69cd4eef9773c0acd27bba93ced7ce96f5d1d2f611e807096b5d800ba38132f7ff4c1217187e9fbe5424d267df789766331102de3c4767712cfb8b3eeaddb1a1a28d76c43979a26c461d3add8e6d05d0653bfc8d541ac761f4ee84290041dc4259dec2288af195e482dc2ee196a69aa04b1d301aecf106972cb4972090e7a393ac7a5b469b0dbc1605f3296a01171722899ec7a57b24c9b12db7358549101dc6797e8394dc9e6d58bc2494095869c27eae88320f2a4b9debeb6451a8eaa591504b0d996fde1b7654ba4492ac4e8f1111d874c32732b12749e63031fbca31b9c1f84140979a99f801d836859c38e440804c4941b54387d4235e4be0e691ae60e13e87bed558238ca686922b38bc1ee983475d24e22380d7d865872a45d64b48aa8834470c43369ceb946b16a4c5ebcc77f183ea82589642fabdb39adaa5a15794c6202cc02ae95b0b47a4b5bf8b36cfbcab869ad12105e61b44347c617e9ecb21c0a8db108365de164ccf8fa4da7521c801ad37d7c9552cb46f3f32777cc50ee7829c99eda3cb91eb045f8f52bc58eb6d4288018b13a0b72f0a0e443153b27e06ff059cb43ee382893bd1d697f812c71b47c7aef3a8b90a2b8c023eacb61d993369cfc6dc78c243be2e2f019fec96da4a8547cefaa37a2922e3bdf987c161bd0c629a250bb99e89e23482c01eca431c5a9ebe7fb35b77b08a24771979d36e67d315f420900eb91ec16c578339dc81efea720fbcbcf79b0643ff08f65eeaa3c1a716d0e7e09d1136320173401ab9dc29a98a01d4b50654407200c4881b66bcdfe2ec0b8a47906244609204bcd844ea2bb50d4a3a44d2ce7a6a58cc608bc9127e898b7796070221d5f6f508036a42db20eec0dc796884d6eb2024ad929d9e91196b6cd3b35b61d9c96516f0c735391f60b8885356207cfa462fc66fc9bac7e413c00467e9a9048a5b2ae5a1975dd2100e6536cd111e0b9ed78d628f4cbee5559826b539efc4f156991d06a6410cf2fc64bbed62fd126e5b7e830b6ada452574c0d1395a8dc263352d258c1502670e7f5d3c5d9a7d85c6c50d06c6f581432ce9defdeb83263323a87900e18b1e329a84e507325a090f6a537b046f6eb48c341092e70535669ccddbe3ce19b999ee1e44159b273bede2a4856ab59a9475fb237589b4f3f1170f464504a5bc597f4f4f3306ace757cda8b802b9e5ddbad487acd36fd03c4853739ee7c7c675cdc558e0f138d3a0fd3b5cd05cb85bfe600fbd4100e22df540e9c45722498c8be33fad29f03a1c28df4a15a2bebab7dc6aace170b540fa756b65c02ebd7345008a385d331ebc0674630aa3297343f88dbea023beae04c325520ab0e0b4df3b32606522f2620d59ab950579a6322be7f50d4126aca148e8d9727315bba3321c5cdca9744b00d216a223eea7f922cc9897f770dbaf75592cf3049a762831c01505102a348a393513007ca3569778d54a0e4949bff69290b8096881229a1d8fdb55c85afaa9fe0130b4fbb461ea210b9d5c2f479777b6f647ab5b7f10c148a080f407aa01139f630bbb1e68cd81f4e131bdfa6631f9ebbb6e5ced9e033c2518b126ddf770a5a3f4b5f17cd456d3aff47ca54229c74f172c621903f249e61f4cc0330810dc35b0ca4dcd28893702809791edee965a4ca07f52554030e1358a2d86dad8b72f027ddf3a8eace365819be14f4081b187a8b58a2d3e64d9828582fa85ca5d426615c4c2a293815234968e977ee364c7dea2b26e2b6a1d782e31c0341f20e8cb97944088f77a3e994e601568be3ffa06be509b8e28563d8a5a7193be685637d6e30a295910f8ed89e5f6e5c144e1e086ecd925a3a4217df72166634e712730c02aabaee7dbcba646bf07db51e9b795bcf67687397039ff51b7d4964707768b456f8703d9283c46578c4e30b502514191638e51f9a45e984b78c660f8cbb901b3c34c1b0ab4ad99d82ccb6687d2b5965518adef2812303f8f0e76076b070f45029219876378f5917cab64dafeb1f5838c3ba0f6a708130897508f08998b6105331ac610dc43e6a7a4953355b10b78b9c0f2970d291fc134d72a5fecd119eca363489bfd377ab725c2b88262529512c0640815b29257c28698c46ad4dd19f7a9126eb73c19e726de12f5422090f976cbd3f8b90259ec36c60fc0c564fa3106857c83f97c3d6c0ac3896a4b7be16b82c243b306e186f72538d9b8a39b52caba80101f0411e1d571c027515eb4a28241015690219ee9f7386eeb5962cd9aadd2a2928a2b614da8d9043fe11655f6e6a2bf864eee334c67af14edf68d06598da92961598d11bd0e292042b95869977e4457c37a56ccd5da56bcb2b3850237be137a2e0d442ca534e113a14c16039ec6a5dbb2cc6233a6de94e5d3edffcd96fb5baf73dd430e5e70ebde1eccf654f464d202f27b3824d22589b517137cb16cab839b9ae42f24b14dc23055d697f2467c463e57fdc20c88f2ffc2ea70581e67d3bb001066ebd90a36d8609f98aee6a15df946bb612357adb3d1c74fd0883acf623505e45dfb5cf1eeba25c3c1424148feac7fadaa5aacad8737542706e29329183e9ab8e45e0a960c662e56ad23453dbb58eb50f92156d913d9364cba1b711db54a286a08f912c2b5229f761fe44894960bb0dea23a1b62522ec6f7b8671703f5bac8f765414940fe3eddf7f1903dd8f469187a3dae97da4e59418cbfda4271e03bfef20f17cc36004f19e94bf97b1c5fa8aef5e2c52e384e93be1049ae5ac407a62fa955de02201e7a50ab4c06db54b4dfaa1db8be3d0dd73918363b92d1e403c59cb40a46db256be86a803d98ba343339d98cd59ed4dc34a12493cdfe893f243e71f9911d2a3035bcd96c355d0a0473f5bb2bc4e6e07513222f5acfd7868b26f6a77c45700128751c2327bcdd0685943c5bbb6346721914c67e3f2ceeb0f13d57451b734134e1570cdcbdf4e78080a834a5dda4aeea11f64f5d0a48a4cfd3096c9445c2bf689711565b39b7b3ede0bce7562a18c3285511be051306ea1344a2c2e8cff6f05904dbc39e8e5c886f59aa37076e57e07facbcc0d681fc96ecfe1d4eab5ce0387c50b1d30b57719e9aa6e6373b648e245618680fb23647220b9650637ceee4ab40c7c2dfce1f2a41f5985bc063a27d3369e5fb9338991cfa902e3ef6cb2810e1393339264a4bf9aa5e57ec88ea97b25da51986044dc251c0a41e164df6e03ed6313c0159189b69cf80c12cf361828985d2fff0474f342e236abb36e3a46b2910d10f0395b00ee97afe4d26385418ba2eeb620c9b0ff695dad81e0a5a255234294887b67cba3bd03c665329e730656be0fa18b4a09417b44bb46a1accf7e2b273649ecea68bba6674073193e4e7c3c5e95acc88de4871119a765fe0cd10a7efd1baff33c49994f8aa55a7e0e5d1e5df5243e79281bb7192b3e11d16ea4205b6d6b324d46b0328499f3cd410a8bca7bbb9d780eb8bee12370cb7c628a21a95e9d24b6f1a625485e85818dcc4a5ec56e47b1a6c6aeb4c4f27f3c2150d5efdfc977de9fd307161076a57aed0604089e35113f4bdaad2d377955ce28cf6e034fd2330e2214a3c1afa83050198d8bc9f16538028a9bb07718880227955e47bac11c6b150221a4237ba32cb8d16c8fd85e8ede0937a9c1680a1596dc2323bd744fcaee3d0c542b0011f18db78f53d102e238c17b45e27fb6edc0b537952397edbb3d7e2a76c0b25f2b6763a6cae2dfbd0a13bdf39c41d24323d2bbb64bf2988cdb8c240db97a186e4463af091aa55b5154c481e87b1406157ecfb4921ef625d69654866c39316de09aa2d9c71f325118398a1b072047318c36b148b4f5ae8270b6382f539e01d89fa9bb3e8c64720e6f89154f52e4de6f1812d797f29c40319e82c4c857581fe56b20cd61f7ba7f0ff66649fb035726517e05f2187fd265896c02f5447202a69ddb63f1ef79f1cf618761f2313307e4c0a7aa29023a10012a04757a5f065c1511ec9cd43cff395418b4085120148de9cc74a9960bc1d515ceca014ff998fa69eaf7a51f0dc32060f2251f68fa144986f6e7c53035f3f7aa3dd8032196d9a7218b0eef4d684003a4eda4fa2085c18b7dc11ff88b77cf5778dc24f8471290a6167258f4970238b90b40dd977cda03432ca504133e968c32d344c6468bf4c089cb899f03aaebfd0faa74a776d6631b6cc3aaf2c37d8920d508a04cf8b5354dd6b424a9f4264f4a402b19f8599120064ea1e8815cc3a087e001d624f7260feac4a8fa024c0c7d82f62354cd81462662b407690d48f67ce09e85c4e7919677902fb5277d940ca291594974aae8d3255a8c8cd3ee9fb8a38227d5b0288fdde3ee20043e60a938154e55488c2be69aa8e074b5f6a95450b3b681f880e8c0aee3629270bf17d7d478edda1e694533f5d36d22143779659b033bc31360a45231812a7269c8682b168ab2c1ea95f0a56081e0f0d394d889a0ad63cdc3836073d14c18a6354e4ccc3c106783e82b69740f5c2b3240192e8290d52bfd4bc519bb0d6ae6d2a5ecc86953114b0bda16e21bf8734e98290ebe92b101d81c1446ce6ca4b2eab4b1cfd7bf70c1d86c483ab512dd862a8c55658b4ead35d542805b6433082b8386038e70c45919be4b3a12911b2fd181deed46a54a2e0d554951c2c2a1fd2fcf0c6dce7f7b4688a8f4681b9a4deb11ffd7a1c004c8fb20d2171f9c372435871b0df8228af21d9feb2f40c3392980688330314a3d2d3b2ebc4ccc317f12e4b987a3a165a69d8106fe3d23ab69b78189415084315d5041a991c889102a245a4dfa23a513b763852dc7cdaa7ce184809f14faa92fb2aaecaacb48326c3d406097f83006a37724ce2759d49742a18c75a36e44f9f9af78a8a3a4df6f31803351d519700942e35299f6383acf19fa0f183e077adf4d74e6e49cd032fe2fa095cf219b290e82025ba09e015c8d1004732bfa176cd4d5f50129f6c17b5f7e89308d230cc32c8980737fe7bc07230fa0f0c3280b0dcfe05c6a1b6de3d8f6fbb9480c36e8cc18e39d8ad286a4329bb0e980e3258c311a14c85bad875267d890f4c24832b7021530f0616015b47b06672e139a44230aa7b91075a5219038d7a8fa2488c2cefece19a970c479ca4581b7a35866d299d8a449f63b8759e50583a779a8dc48361cb541d7dc4a6286d0c75aa2eababfddaa0fa816927b712937a3a4d9e56e15bb99b918ebf9c7972d78ad71d1f1a58beaf0a07f469cce6b4024a92747e6f1bad2c8bf6f7a43a8b6e823b2e12fe018ae1f07cbafc68a8e33fc327e4c6b7141fd600a2ec8118d095280fecb75c604563483070e892880857ee5947933dd391fce463efe2675737458074e00774e28a71d15f762fe4c6c5d8d7aa6c2859fa0a012fe035ad51ab8dac4d43d90280b5d818cdca62ceec08b16239007f1c94f07d0dee4f26519376d7898af585604929615609818d28a836adf17da53aec38c1891d0d6c318664e99608fc995bf56824c1845f263ad758ef4b27aafec5ef6cf469baa0c83831331b0e1554c86048b2dee7ee09635a794459c5b48c37ca692d0ded7a7f5b52d7682ba0fa3c096a64086c2ec35db6a7e45f0b0cf29bfbe7793c3410fa6eea172fb7c23cf5c4c38a0a0d4095bc64a3a59b46a06099a8daaee30c421af62990fe19e31a30b1463115fd62c140a127dfc70ae309aa2341c777b7cd41f167ca7fa868073f1af1102e1f6e0aed6c1838f903ebcb4d20678906f6ac9e7420c8fd6019319b5bb3d3799330f50d94e9c529ab6b9dee89247c6949673e5a30ddb5c9137213099d1b5fcd34b0a9dafde881328cb36d71b58187a9f7208c9a594b5ec5df2bc1a61d789bbdb309486e15e35addd887e2ca2aadaa858cc1d88646d79a2f4e8211a4902ab7722c97a560755d1fa474bf52f4b7018841c9ea2234468686eda8e415217256b2928d0a7af87cb0067573d0923a09830994006630e02b8b61a635b8480e26a35492c38ed022178a702005e21dd0dfebf9d47cf167ce6e0d8797811ff6cf946d788fec53dd05c8b27f2bca0167f21c61d748cbbd1542ed4a82f523c637fcb324a0855ab77eb30a0dc1caff66a5f0c2dbee2eb1dbd2e84b7ca488d2f3d72569e1328118dc543c485895eec457521069aaf51854bfc3e97190393021cc555267650fac02f1880d1cef7023698b7d7744c60b512989c1917d3da17b304c9fb92aa2c6ba86bd977c2bfaa4829e84c2c29aee9faef4eeb278214838edec6693ad4bd94064c7558be5b990d1f14ed5b992803ec83e6219b5ad4df97087617e7c18a91fedaabb7d659f4bd74bc02087872d7a8b56af3322aac7741288528aa41ddc940d95517403ac01d4c03a1caab90f68029e88a96283d542cd55f0c52b16d7069d5a7f243037f6d00c556046fbd2e17fa9db82464891efe62313fab307682f17c78aa449014d79d25825d4926f398e558e035b292a01077388b492f33da7076d91876591f280663ebf1cd0c660a388ed329641c77d156c49b7f1e2e4b27b944ee6366889af4bf3ce58ac458903cf08b3e4e645830525450d7dbc535e7ac16105001b9d5619758832ce2256a94b71058f4ea0bd0d29bf889e65204d9a58053c366f753576f13bc75c035f5b3212601419aedafb66b2785ceda6f2078d2765919d62c65833279351dce8e1e716da48145414fc8faeb4287ac1394d725fe697652c1f79051b7c693a8d049c47c3756e09512821aa981f6fd2c93b029181c660d0b3015bd554703669d5b01b849dce2ce5d88265c2cbc60e145857d1ae35a9d5c032574c12043d634dd06d6afd8a789e774666ecc85291565cd58e3910598ab2264685c745576cd48846bf454434b402a8790e9cb0572913ab72e1af542d9329d9507c0357146069c465f1b89253c158b9afe39387b01501577c1c965b11c3fc4d7135fd2bccdc395ab79fea386c9664afa380c42072f0ae07d45ade71b0914ac35656c51399c265ae513a8d9bbac7a44f32711206d4abf650317d4acd40608509bd6c7d190cc85f769747df984bf8295ba5c3ecf1e48da1121359a5c312f9e8196812207ce9160d796908ff1ca4a879ea6f9b742c0b8286572f8c41efc3e6b5f610d800021a25a1cd31a8830e1e7f70a455c9586143c570b01c3524fb1f4746efc3f6b69aa525d2e2e1c03e993b76b601acffd6a9202aa5f35032c0143cd091a7233db2cd9947e832112e5663b20bf600771f480f1b1de9d224703103d23132065d2ab1cd448cfd609663c8f98dc9309cfd45893b9591acd03d843222e4c9615afbacb8e8ca3b4b98d5cdfde823a756a35df8b96e3e52086e4995a4b9d81fe3db44e6b1bfa42661c91c73a972955c0358f50e52c63a907e8d338b21efa0afe42508bd180fa14dddd058ed90e72bdc8c6ac7b4d1051c288b48e5a7708310842e5636253f9c8dd6e0b2ed27e20e9ef079b4d3ed8664765f0f102abdd4d71ab13170cb9e101920d1d8419eb607f34d9fc327e867366e233b062283d3e7cbd0b58c57f269bdd31b2ced8b715e0d9f8d26608cc265b7fa2f9820aba9b5f4109cbdc116f2b8d3c096f258e083b6439fa99a7696dd5555fbacde12a494e2316f17f0254a9fb0108dba7cc4c3b6f09b196d2e1c327aa613da28ad7044d45d6598d13f041da4988a4ebbbe999565c3b1ad6dd86a79c6557570c24109cbbb222f0da88cccbd6564d11098e25d6e964e6c83a85ff6ca07d38a45e51e80b962407e54d1c190c4a7e112749150049cd19185ade9560753974483c1d392fae8322cc1095f7910f2c750b241c3fc8a8abbe5d303744b4266ccc6ebbf74487b4b18e786e4622e181702fa4f3f42c8ea939e4a6a26952ddd475cd7cca4a8977b2079652972b95bb62babf4c73c30905d546a9d4994d73d570b0addc5bbb2a7473e3a2702d10d7620fb07d654460c21d7694dd83ab5de908152aef75a287fde22bbf473dd84c5c21aff06857b2c809ce7ca8603d39d3880acac5c407971429d615c035bcc58203544b39fe6070c8cc0de6fc384e1bd23c64be3b80f9d3eb662cc1ab588025c8f78805202f818ac2c9650c965b9fe518d5122cf124e6bd78b77240cee5570d855e4be407cd1c0bf0c5f04b7376b1603f6b0575440a7e79b8e5f3ca50fadd38e91fa08fbef2beb35e5d09830226a69c8b4b6676dfd6afff05a236f7a972aa861fb295ecf7b03047c6cbf0d47ce814d5708e4c4c8bd218454d81211a5107290d27e37cad2efb5bfe5f9b64e81ef904d9370048796df9de58bdbe9464ae56b748dc2a85b5f6f991a2e9f187cad9f217982674ec4c4e63a500b24b89dad69da905d45b0dc7165cdc1db5a4a87a3c9a26ad784be5af3d01c0cde83e95b583a2efa7e07bca5d8d136b34e5fa9542ac7170776bb737d5977d786306c61011c4194e254ac55219e541d396447e3a742d540f8ecdb2feeedddaf40423eb5042805239b12bc5f78438571b3b4d21ad8d6217f8266b876ba0683a421a28b3d8cb9c2e89df6e6bb8aa86f6d1cacc2a1f9b00197891bbf80d7ec035e29b04be0671f26828f90fb0464cc5f1dc3c9ce1ad1f0cd5ca02775a7f42477bfc92059eb2f8e36a28d323b873172138bce71491aa15ab677f5729ea30cabf3e63074357d7f32287d675b4c43b4adcd37e29946956153f6c72fe30c2a36bb3a8ca5a6d8ca5e6d596434b25f828c1cdc3bd0f75289386d5cfa0ab05a9887503deceac797714cbf7893d3117c13e486adab5f4f17aff163beaf2f8ad9ca975018b1616578ac5cf1e50b26819a63084265e89ba11ad963acdd23e240eacc4a6dc86a495798d35fdafbb66ad507dd39d647cec88f2ed903c0e7ba551b870f90a3806648d3c9afca54f9ec2fc30490b440047b763c09aa11a48d4f0e8282652c10990812b82d7dc1eeafd37218c9364a9ab853eec397780a4a6d8a172365344b3ac82f860c528cded83ad3581fa022b4d7b1e4535f0b938e9f81d0d361d985d33b5355f1f787875f941ef59c51ba1b9e83df4b38506fd810c09f50fb0397cd14fe90a909cbe571299a22ea91f2102f4e7c220dcda3d44ada13e970c874720da5963a1a0f762a8c1390dffc82cb68b52bf5261a9f6172ed11871fb1a3026a39caa6a96cd3a3ad7d1e40efe8d2a4e9618e7efe2af157adbc57192ad0a0c3a5c2c42a4a7bbb3959bde3de24ba141ae0da18519a6ec2dac8fc156a14a2ac9071a50b4e906e29a8ad0439dfb328243eabe9c723e1f7ee2cc59ec3a327520df6b64f630e25687c39d7aac6f247bae1a9f155da5bbd93e30b629b50c3cc1edee9189d93309034e82326c24fe6d32a962b2fd24b8fe5044cb8e0b37de7bb6305f461c8d7a6944f2a897c82e308f2015a2d8816624c45fc0b38573a8748b3cb0871c3bcefdf803ec2798d84f4978487d638bdcd33431630f46629220cb0a953c81794fd875a92e9928211386d0131593d21610325958d77a3213e70e0b2c1200c18e837f951c4ba11315e6b603abffe678a3b9008a7305872fa7e922f007103ca3c41576407e89a57022179da9ecdd98dfa8c17303dd4190c79065bc61335b2b5107636bd9b0c9003ceeaff3004fa92b4abf1553596074544262d989f7aabaf253c10aae3c5624f1d0e421475882a45d58c6679f3d8784eabeb32e61b7858721c6a3159fcb55af774c6fe2462ce176ef960b45c1d4f9d29360c40136c3be9d32148e7e2398d081710dd6b88ce3e5dee7748c10a6848ca645a97649b22c0dba219287c4c4dda9bfceafabdc7ac744e8663803e98a1810e197b186b9b2fbbc3d7b2f11081d1788e833692f521c34cd555924d8024ff767ccbe4dfc4e871851638c5f80f8a01c62d3ec091061153ece0597c124842a509292b4e1803026c1b1ca700859cc24cd5b761f5284f1ac1132a4e2efaa4afa16dce83721d1c4e27b7d76022e484b3d694edf9ee14f2b069d18fa1c5ef03ebf23ee5f215f797f3d5d894bd191a9dd5cf7e14bda998c03785f3892dc0d55ae6b3352373e6d8b32a2bd9d34da011a317e69b1f7a9b34c49d83c440351b1abb705f00385520edeed130fc3c0c3a58757cf5b8f90d68e74eb958b20bcdedb011d16a6a0f02649eb86dc5252d221532121de940fdc793970b4bc303b401e162f8fc802786554b77dc73b13b0d2cacdaba98c077d51f89ea95ae8ac7159dd9a7c0f534796892842fa8193cfbaf6a368e322d68d871d21e3f5b486d1ea2d5852777017b87dc754e87e4d6863416028a31609a0a01b387cc3c74486b26aebfe2f09d0579b87fc772970d167501056646b0792aa26e720fed91a377fd1982c2c8efcc3f000d870d0cf1c7d34f481f0cb72ac406879db5e216f8b34d1ca01f91f5a5c23a2bbf2a2f848d33a7c98bc6bee24e28c9325e66d010583c9c0d942749382e3ba0ed5dad3780df5a661273a02aa6db8082adc129978d34dc43ae980be1032e977bb3ec26a5211846150ccba7d6eede110da40daf72c886d872a00b9281823351ada6341d3c227a665e3d9491b496e16a3122ea758bcbadfae76e5adeab34b100e764a808c80dc22c6c43cab62d35ac09d3bb09c32be822aa4a5f953fbd20d6a3d0d88ada741cfd7cd75a9068f7a59d8c245700edbeb946ea30c19768bdd889edfdf8e72d733c42552acffcfbfda91adde53e66c4c3d7412774f6aa7eacf9527c521a4125bcc7f03fd4d4bdef14bb994217f80820a2598be1b7800ba021010df1234178f2d5dc53e1a571cc30f41187823bf6047f0d4b32e6e3bed8692a64a7e80585e273ae4c920b8f5dd475c2e1ee5ae3f1776bc4039889aca5db42b9d80af1a77273714765ce6a771aa2ee3271496cfaba7f5618da25bfc83ab6e0e5d5008a3997fbe48e797c7f8321f44409859424797c977341ea9da875f6bd973ff74ea5c5dced18c6f9e5b0ccf3c85550e8b92c901fee68d815371de7b49467333ef771c38300cb7ca9f2578788a2cef5f5c67591f0d395cd54f24d7f7dcc8ac8a05cf3d906eea02370b7af21598564e973c70d44c3bfdc0a797808ae0a2f7e692d65de9a006e10b7e362634f40591c0f70f3fde8c8bcb350d72322a2abbdb000e0175c690f84707820e4b435b5fca2e73da9340f3761ce860295572b81981e1daaa15464f84161f4c1211a4a2d8066d0c7568ca55840c8d7a834eed7a4d1f541dc5e223f7aefe3b06ad8e681dc0d5dde225738b4e7c3a0d9e808569d923cf8a19241be1ecd6f24eb4d78d93e4025e01c38714aa32e2ad0e262e34178b5d773b617e1609370a05b986547111f36d25f030bffafeb3d8a27c44d7f70ba01e5090dcbdaa1e0148bcfcd1f24497fe5d541becf5daae4aa6048c714c9a78f4c8a6f7e762a651799a79bdfe8a0303846c22a8c71fcba068599f79ba359086eca8b8a1888b05f6dffda2f296b50b019818538446a4b006a8450dfbd2a05ac7f495b323795491289e885e6dc663363b4f1cc7f6a7bc19143ac72c61fb4d46a2ade7d63802c517c66ccbe6444221b5705798b856e12b4c5df0c25f8e4d40a576769f1f2c352a0a9eb220dadf3e95431322ce393bca827efb5e0aaf4f0999f246576887dd3290ba8d3c0127c77ed816a4df7d019f24c0fdfb57dcc499f8525df47d122f059e175ee3da060477784761e95565056891c909150501f0252ce427f78e5e123ea29d70c5abd013b2b19946e3f7e3863c155a1ba5a438fb492cf777391c26b8b70bddba74b1fb29e40df659d17bdf19bcb409cf1e25ae24c0562df6b88b61e356af26a5899a28600096cb9feb26b47da751805bbe2f27ce6ff755287c94337e042309c3e4b1e0237d3215d920e5f7e805c62afd4d362685879562eb159a06c5c2fc986101397d86177714183900d7aa4aabb2571f7d20c4d4f583dbda9c8d8f645ffac911850024b125fa7c304d8f5bdf39cfb358b16b795ab39268b57ed1aceb5c8e1e749d5879beff9b6c03a8a709450635796254bf0a46f53417606e512e59b0663a927c7136cc80846f481486cb4fc3bdec2ca89de44464ac16f8d1d25452501e9ed4f1004fc123d5b6cb91375875bd6f5d272fd15387d33bf0fc430bfd3da81ebe2a7da04fd8841721954f2b1fa9b985c7cc081adfdf7c539b9b143039e9f6e8447fb7ff32753269610eaecfe337ee45c74a4039b9c5bb6a5fc384756f47b9f108c5332f56b6f213a3566441354310d4ad6b83c2ef212a1c29c023f43a3567b0a0dd26e2c42b04b7af4cdfcf15aeed45f5a26bb5f1d24f062181bbad71e0d13857636dd0f38fba24c417b166db8d36c981499b14dd18fc6024fb0b59224f32c0aea35e1438c142dcfd6bd121b2d580385503704a41e8715c6528d6084aa84279e8ab076bd6619a435cd9f7aea48dc29f035e5e58cc17ef93bc969f0f0d0d069fd41ef8f22eace75469510ab733f2529731a60104c83532988daa2d252c777b29c5cebbeb81d07b5084511510a042bae960ce4b100760714312797e5e4b8399b55eaca6cc840adae89d55f50f880257f8f729c552284253a0941bb3e2b95a80348c559b9582a4cab1e712dbf577733c54cf77b192f009a2b155b8866cfdbdf94a835e558fce429222e81d08359affa1a9d01abf395ab3440f7aaed80fbe41c4ebe6efd1795a7117d1faccf65e6d5f2434c5f088b7a04077673391daca91aad650947a2c4ead37635c410c1bfb52b5ad152e3325a7311525d4e7e762d4243c1c954e4df67c016f8b267c1928615301cd36c660a5c3155901c10752499f64c5ab040b29243deabb717dc7f52d57f78637a5afe7aa89a8727e924ec8702009a0074d6366a3f655a798617a8cfb6466d718f13731a35a662d2943fcce3ec8614186b6166c6303a538f18262f9a54e13b59a9f2d1d95aa205e65dc246fd8dabf48e92d24880c164e977e57f7e6160716af42ba4d5349706960cafb9c232d0b6d5579249bd27e353f2c233f958c76502099a8e931a0fd00fe00268e6d839ab718943213b3f93fae6e0866b7b617bdefb1f52bdcc38f5d9e444e83771681780409e3353f702e8969ad8fa59ae59d8d10d1ab059be96b448badad337ea5dd5e45879007864742daf71f63584a85baa12494431e4d8fceacb3396fcf10e8fa3978155be16bf3513e18046dfb156fe4c8fdab05a90a2649f9ae652c6b7a6290db687a1032b6cb308af694b6e83a818d0a5f0d66a62c7ba79868aef37dd27c0263f835d3ed46dbd526cc021d058150f40f06f6a9debeea90bc96fbc71542ee9769a02da59fc015bb09f86cd1fd41a849427e705a63b05fd5a8fc1df2fc4128d57f020c73c52eb5a1d1135aa4323cd4aef5d690ae59a946ea32c2c774d40a196ebae3e961036e576a5ae732763d93615ad59482d95217bc1c1abd3b101f964a3a662ec57fc5c54dea4928f132692e9270ed1a06eefffb46c9f314e899cd21e08e535ea074ff0fbccb9a1b8462263d49c1b9a74ebbd15169fee6a1e69a586faed43df8ce8ae6ebdb7222fbe33dbc632d87529dcc77eec9265cd0ef7b43056a94e2e8fd2a81b6aa87e4cedf7488feac5875ca51158caabc58334fd5b387147d7d75be10237ae2111257d9abef1e06ebdbf422ebecf0f57c25464631eb6205a20fcf4303f24bd3ee1a255762fb2849bc0213804dcc6a462c181cefe4a04d5625a61673e131746d3d148fe8815ecc2b16875cff18896145a1df3eb308d1c4828bd182ac89a82e8822290b427ba99bc3ca68d29b473bd7a7887e6c99d42ca47db15647c5366672f9be1dcf7b065259b090439bb84c2b34542eaca4b8dca8810e2e6793b02ae1b677c2a108956621c8343007c6cea904277df9cf724dc022a26dea276b08b022735059b8ef6dfabfe0321571c813b26066182379f0f7d14e4710ffe8026992262181e8c357e8e7ee7a17b112b927e652880649634ca09d2cc2ebbf8954eaf47a6ed290c037b54c3c5316416d8b9c09a8ee8492adbeb7a083017709ce3d9d2a88999daf63e6429e120a57ba2ee375a07609e512caf5961d5e7203948795cafa90cc5ca889e8cc275e4007498c11c858ceb91e505ef35913b32646a9ec6a01c1736ec8680289a20eec69e130ab0db41ea41e225843ba397e8e961801f10afd8fe17f2186b80ab75c21d6fdf85fc948bc803fcb2f28059501a51c0600d019eb999a0eb696f167afae676bdfbea51e9910f445ab7b928d501e0f439d084993f831c68af6fe889551c70d3abf52ea7240e18c07d793f7ba69622911bb4768ed8d0e75525eff46ede59d4c9e86993122016f3d66a87fc666ce32eff0184bc19ace9d30fa2f495111f3bd52cb67d402ee0073c81d99be64afa804c21e92ca6aa2068753e4bae2efd068c28cc16caaea97733244f02c0124aae319a962493dd637a889b836947b654da59527583d672c46c250671ad84d030bd1889b181e6efe14b1b458bf44e8f58c45e667bf8228687ac6cfa2bc5012c5d4dccc39faba252c932a21ebe7ca9675abc3cf1bbfa37053e9f84418e476e0d8d829baace55280fe0ee338319806bf21b1a30878b1aa7e6d33aa74e0898aeed644aa4998e222e8a6a69314bba0727e7513ec3bbc031e931b4ffb1007b1a6f1b01475fd6882ee93421cb5426bc158309f518def010cb32e68660cfc22c3ed4266b2ea39dd1d0defa3f1cc1e20f620f0539ac772a2918541c751b23b80b8139e5f31a71b2292d8883c5bace1a0c724932cfe303bad78b79b6102aac5abe1ae7d4f90f356fae4238c2babebe10578005ce90688c695a81e84d3d68df1256c65557b506358023c5d6e354a63c58d097eba4e73f4ec13c99bd086c5bc0dcb129a2025e25767448b609cd8bd83babec13d61a1baabf942f9bced173b4a6e8eb21cf14d5ffc9e33098b074d5c2fa534fc9b4dfe1304d27cf85c540bd4c2f7a5f3d4e05980990dbd62db7c56d720bfd2109e9a34aa6dc8910c0d747fe2dbaf2e91bb2527dc69a9e0b4503e8bb912c824bff4091744af11aa14c7a10558173d1d91e11b9f135ab22b26b01484a6cbc5ef0577888959e893b9ab80274f2d9d60029e52c76feef37205dec3e76a1af6e1f1a6447386cfb2441d7381f17ca0835758ad22818a14b1acebe74a2581b485e46593c02925e636f859dbbf3c455a6d447d8bd73983b90ff0c262676694e66ca737fa1c4b274d277966b6979814475c3258a709d86f0a120604a4ec50035aff0444b72d1f88d5cc0de1b57831bf53a40a6692aee85a17a0bc732c86c9ecabe04557a5f5b415a2115f38296aa0d470447559eeba42d36cf293c0f17f93980a91636518b3b2b3611867668cd27f2738d54706285160f1f68a58c01d278fbbb50687b6ee5d84be22eb4f08812d849804d7afaf331831efbb0952c0418baac33a8186fa196bbfcc7fcbfc1bbe7af78b04fa2a7518f54bf5265517fa9c8e873adad217f64f95d34d6c5da9ab34a7112ab86f5d516bf7de312f980feb1f6f2d5f586fa6d41668081ba1cf61b88eff17a99c3a2b00fdf79db32d065bfb5060d1f5eae44a47da800e377b1af922dcdee7f0738a21977d17cc28ea7d7a7f54b852f50cce1e55ca925e10a81d2276e724439be16f6107df6e8e520ce008f3e28b315bd0651aa7cb43db3db9354934d07d51cd6e0400ce1fc4d90ba15ff0b01a9f5248de0fe23e988f25243f4977ab98655db78f41a0f1f27380efa89309546088e8d1e04bdd463461357b4fb5ad03309cb6971e63a4bffc8d4d8bf5b7f9908659f6c3fc23481ca5dc0d53f369d8df108637dd8e313197621e0da7018e11654d3c3dc9d4e7e43d36108f32b778da35804f41e15668e32503b8f17d94ef772bb2fbd2937bc8553cecc8e39223026ea54494e5bd7ad58ce56c6c46a0d7200a0b5541a378a0820a1cbbc02e9c5a3037822db7fd41c3fbfc4f0de5ef29a919a439443fd8ed5c0e9132dc9a110707d7c6296a8d45810bb7621308360daab13f2ff412e1100a7317f57562ae0f0adf6418706162db17f19284578e64320696c0f3640261808f1f48a6e5ddb468c0693309937c3b5bc59ef5f2971094e5dec6072743b6f21bf8acf79622753971bc703f36153e694e86ec83e05944e1673500cc5a82eb9c2863df4489d9f04e1392b85578c1e2076c4090d16f5a23044675c1080f4ca4830e6c45781266bc582d38dbf83703106e72c8491c0e6cf4364b97dd8170291ebb088ac5a14bf2bc074dab48e123aae41cf8030b439f5f8c955dc6bf3317dbf7d1302beab4160e2e6b7d826d96c33514f67c3ed68108b2ab6128210d27984f9af5f253408f6af2cc8edca209834cdf2f801e17e18ac6ab0694eefbd10a7af2a52550f4a8624f03fc0bed8111c555db7625cab62b3c1aeef5f1bcba51ee05420419f1bbcdc8e57c4ad3d3182ebaf8123a4e88c67ba0c18225c637337390dfa02470fa25e5abdc91b929ef0825379c4481de7349e28d59a61668045aa396f36cb917d4b9acf0b2e6e4405d0d0d1d8edade7c96dc681e5329ce636d9568f6686a790b92ec145f5fc9284f80b8aa506bcd5257b661b5afe044495225e5eead1ce021069c6c1e486ec1ec237daa98ce60c579500348624f163078c784f120f94a0545d0be1966c8270f05a24b85de0b5cd1c5c1ca7aa818a1616f9ebd190abd61940ac498dd33f93d70806de8cd2b536186a7f19565b95de441c0bc70efe06a572d028c52a7246913f31c6f1803eee116a13d3cf9fc9879aab42710566170e8f474c7b1cc2134149a21e244253e30b209f297b6401aa5e666e26589fda60e9968409210f0ab6577b93ea4f0ad393f67fa01d1a4290a248e299dcca3b7b26e31a40d8b2daaf6ebeecb43a8e6e8512616d1fedbb9276722c0044bce8945ebf44b7d7a1228681d9bc8db3a87c00be76d200afd5242165ae4770002ab40e00fd0afd451db85574614e0d7250c15267b2ec7f510c9f139b2e9e253704e50c73254d4ac2e274928a72eadd0788e53a2b7d4348970a1498f415d538190e97e9d3bb9c5cbd09e5249dce8f9efb78e4bb73844ed145ee8f0f3e38bddecfbc936282973c104453c8be1b8290888fa3d103d4f800713c8c9abf4985069e93855af0e917bb20d1a432acf1b56a7da12cdc0266f13689d05b58a48b6da9d118ef7a1d1c3d06280d15d25872749d1d30c05c1ce8abb08779603e12643a48f03e636a15909f525aaf94611c37ddb9838d7a6921ca8f035b23c1280bab27c57827ed30ffa0143babfaf80d72fa53b21900870d62c638babb764a3ef88962163a7cf4be2277a28f1098d5c60a044f6884bd59a6d47e91cb3ded8c005db4cbe8cf201849f6c88e577016464500a92ba9d025f3db42f8ae2fc5ae12d14968488afe7e3fd04f7dec2f3ab60b445bf6389822fefb211589f3f77ec215ee4cf187da0a02a85f6f572bbca407c2f533ea13f288a309b2335c43b7ebec76f79821ccc26ed3d764567af635307c504eca9e52ca9d1ce1c1f3ae92f47f5cf8bd0dc1283c99ad7cb548d8cb169c4cc7cf4195b03328b4b491aa921e4ad682904ea51308d7cf4a0b7d240c159537837baa487b56b464eb268c31e5b3a01d105d14643f4873b3621d406792d7fd18c0e3a86eb7b5915f01c086f33c7a3791fdf2b5db020084e73f8d0de17cfb4ddb9d95854f1bc88a56d71960fa607d97b2b46e7d84a2f350bf45b3c0b64c4d0f2bf7acbee6afde321912b7e2255b36fff142e4b124dec030141392dacdf289efa1083c98a06037627dad5302746cb45704160b3bf12d9fb874eb6ef6a8c8709ca00c39ab58c1254c243eba63f5e30818977b60f35aa0f06892351d8eeccf4651987dfd295b6d233989f96e821b949a0dc3463f3f24232a32b89292d57c0ff94cfb07a97d1ed8fd68e8fc0b062d7b8a722024f6a0d623ed7a57476ecd8bdbf4473b1a9f7986b6085592681cc14e13924c02fee5fdcfbcc6a9ca9a13866f7f92592463cd47004decd82068cfa18a3535e1e1b5d1888a3f461b35fe7698164d2871a8a9a69315a5e148c7b18b5927403d1e319850031464ce76338dc78dff48f82fd0d4c833aecd3c14fe677931594956c52af349482b079062ee9b398b63b938240d5a16a0c825125a07f0afc393134ec641d637307629fc641d8915441fc22da584a34e19d98b9679ddb9967810fe9f9ed767a8b669d3b3f8e54cf8559e3102eba0992107f22ce4cff7d8c4db749510a4b05157f19178cc0782de3d56b908004cb568d90077cbd6882fd003c9188fd76cf6b8f4fefa534cd81b37372577412701fc833720f4fa8474538ef974e388685bc256e38c900d8072fb45bcc6bb608f3587ce2c4306e7e4ff8c748836236214fa77f6ad1e3266f3352ace5763489122bca6ebf7bae3b79540a24055f39f1699df84bde8bf474cc9587cb2e16f18966800f8e09a0da7afe43569bf3e80620ba762ffe9072d29f731413a09ba57dae9185fcceff1bcc709c9a48137e0ab6a8a5a0c5f477279fa225d59a60a486e8f326b9b373c0939e13ed1e251814caff2c609138e5b8f455333cd39b51662682c349ae726ab55109dacacfaa2a50db967c6b72ed7caf503dece1bb0710d8eeb1344b5d654b527380955e1fa411e99c3df979155982d354e6876a75641b582868fcf8c49c3a3dc0e3d444d4a690f046c6ae98d2685d9c5a29404797124e0e70b799f1fb5e257e0cfed2fcc8b61f71a1539e3035a41f178084172086444d11a897689354ba3847c75f0b92152b50b148fef7dee888833bfefd7896329a05e3f49eb15de48434296828b8408dc46ab82df37964015a2896fcc2b29541f269d154a7410880067b32281e9c4978338eea1624ff2a06a2b274cb0aca62d462872fb12e4633bc7788abf84b7decaced7da5d2df03f1a03a79b583b6338452759a6620249db2cffba1f3fe3f0a57a393211b3f02052c213adf6e970d405ab88ae89937aad99a64162f0ad88e1e49b124975581f2dbd41e2ae8492833691aa56b03fdd95a146cd353ee45efdb150d0b9c4997e4e80121265e63bf544117a724cba0f2b9d16531fd43660051115bb2bf432a8ee82a5e3e7ed7d3bca69e35a8ec9d2f487e082ac961cd89881a1bf0d717fb8482f0d8752e4dd74c74d43f2d00584d177e8aa0b7ed631b2b0d60b88e50dc622c95b4fb2899185486f728161dafc39b9c5cb30d2dc04b190714698d3e6382e7d64bf80307c64d6a233b0993997fc2e0934b338c42653c44a9d8c787493bf40d3d465378c2ed4b54677486e20777d8249c0e29dfdd6da26e9a297841a245ddf40b2a2f78ad03b8cbb996ef914e48e094903b0de164dcd1540c3162b6edcc5864698d8988d9846956af8c9623eb8dbce9e0bf457b3825f3895e35e18a01b23f5ec7ef7d9874eced8200094586bae6129a6f745a694b30549dc78d564f6a1c8d6c76bc62ad18d825a90cc56229377057be7a516b6f5691abc61e3f446cef72ce7774cad26451a444b86324e48da916eca7db53ed4e5928194e0c1476df0d5f8ace0252deb1748b65d1114a0a694e760fbeb257caf507b821af1c0772a5967a4fbeb859ff921bed2fa0120a3a198adca81f3872fa0d787dcc095068fbe72db8d7c4e0d7baf797ee583e9f88e4682ac9c5bedceac54afc0ab8022a458b57653236a0d9025dd1116a502ae6120888d59a6e3cb826e8bd72a82e6af0d0c88ab16f2fdd6b1f93ec14869721ffafacca450c7f337c0e949593f2fd41e917809020ed49a37d913f71a208b60915a2b3d7c56cee45abbbff4c5c2285a59db35425b6bcdee6641d7df6c9125c29e3a5f0949eddcc179b5a0b1beacb8019eaa1d8d68cef09351644fb596d0fee520d9cfc30c4482f4be94b6f3f4ca5f8112d3b40e56a66a0b7f0935de6eb933766d43c8c1127f888a9cb6359bf558d6c0ca059ad2600ee08f3312514dc00fc51b2571f4cd6948e2aa5276f17476cdd3108eee19ce51abf6bf22fd9a611bd1709ae06f4192e91b18cd25a8e99afb4a02c3f00d370c3bc85cf6e48d10a2fa83ab814c39f4198f58e60eeeef71128e535adc21ed4452a34e89f5273e2198665013920e0838207899a9dca78da6c133f7753d25c64c32996e4b8bb1920314e6b4608ddbe55dc3c048f88552b8f4de720eb007370bf9c03071de7298ed74fe047070196f0d4eac5203fb7641eed5fabb964c90961b62cf5a5b6514a488396a349d9e19e4f4ef5a6abbec6b4cb416f945716dc590fad2087d5b0a73fcb586d7b971981e56655450c650ea2170b4b5f0dea6df04d242c4c20d045a602698848bac8eda0ae4202b161228a77cd2492d45d1d02dedf37cc59b1fb9a9ccaac4aa4b7b59a3a23028b0af6aa9c87cde4d91537ad4f11400ce618a6b342c9509e2580f7a6ee41ef71e0c90d6473c6760667f25c89744ac38bafac5950657066df4df9172292e2d462f48181df0966656be413839e4f4f5dea142ee418cd671fcbcdf38d45361c90e5d5f153642317e142384c5a501556fabe45301f25b4af4ad019e7d888249596830ea1ddb431b9f013678237d4758b136686166d247ab02ff5163e95280b561d9f486fa0f5543881d254f1e469efd189d34209e020147a1acc83c711681134f8c0489433fa04d17b41da780064fd9d45539a8a53aa633449c29f3f99496024d27e162e16ab3cec39bdcdbf0ce07e9e9b2c57771e18ac85ea3f44f0bfd0a43480362ddb2d1011e20b80aa4a4bd65e48c89ab2a161c9c7823d40295b26262e4d1e32ac365d5467fe72d59d7cd0f7f28ddda16d4f621f844af8735019aca16e20080d9e0fb79b41e4a040dbb48033399db1c03d3a60b42b88eb8568705307c4a2a7c647c37ac281229aec276904878bebf4495665d66f0f7c75c1b121bbc5039468045321ebd44a554c3b92d9affb1ae983e330e44e58f18ecef8e6cc6308ce3c476b684bd6c3bc2b2c388cf68e9ce1c4a7e681f917954a927243d367900e8abb350f5920cacf16a423b7029de7e037cf0d4d49cabe5a419f560144c78f47397bea7dff12f382da44b19e587250a36bf202640e1bab6834209fea4e726443fa00f29ff181527de7853325e9d6e303dee1a92a4e95b85385f2e1e5635b4b953b5039fd6f5bb64be8d4c77993fedd5c71726555488f2de6a1ee2a931589ded05f5c41c93750074a0874cb21ddf7a2f8232a2f7f8aee69402b26c8605425acacedf51c4a685eb676c1e6c997f740b1d197d6e239a2b700427996528a831c015ef49c3d8890f4b9684055efcd67d0094f8718cc7a44b65a4c9485fc616130e330321fac14440101e5afd4746d38610265076ad168abd653d9a76943d120d814f58d41fdc8c208a260d28c0f4cf2bdc4a19ed579e705fee33e6484c8d4de9ce63e7263fd5d402ac64de052403040ac2f68b59309a19a7036cc1df3b100bb5ac11c02c034c79f7d9c1c55e8df1d20113212d36641402f48dc034f392b32b6a63e4aed206b5af55419026ddb9194b8ae27f350665ba1ba6338ace3e4add9335e29316d2f6de7bcb2da59449a67708bc08030930b5650851bee0c9752ade8bf18bd2c7e145e9ac397c4a25777777f973888b54c260f443102b818ffc11d23054f0e9eca323c76ba624ece5a00e38601d8e57c2bab0032d43403fa42fa461e877252a5801778f115124ec5e8c4ba57f189de3055f70d0cf54afd73588748216228cc0c9feaf2342a235759f312d446029a59432c42146ff4667ffee31224a9e11225f06c5889c3f8388e402f61fdf1e11b9e843ff3524baf7d3b4bfa1e7624cc28ae0fa7247d3b0466f75577d7a6be86be8b9ef42603fb769212a128944a2d744afbd28c4ef8d41f28741d1f744f2e75dea71d665d8d2c74ad1d1dff9a6804285c30e1d4a789ac06e5e3a394558475a405c1c2244c70e1f3d9ee4d8c163c37c3e6ced94767e3432fc1e5387428547a2c7dfab5c340a4dd6cb211289382e07cb087ba26f47794458f90b4d32fcde86448ce4ef3f9f1c7772dcd78fa67bfc3c8430c4804d39d8976fc37efc9e1724d3ffbe9e1488974361f784229953e168d54cc5843150f8887cf4828efe0b8c649984dc3d2e252e8a82c7ab6387b43f7cb828a1481faab3b3c48fb8705e3ec4452392e5a2fb0f19a5071378ec183599e4dbd62c92270d4737926738276c677a3a76f8477ffbbbfd1804bc02cbb76fc32bc369c31e41f208a4943b9a2e34c9b00747fd2425f726f9f3a51017454be0fadc03f1f2e4c0366d211017fd7df49801fe6862e60c674c0f766f5aebdd500b6df5324b6e06e8e062bf6955814442c0c9dee3bac9fede9c92ca6f8a5a654f21b9e9b72cd5550d8bd0da51d7e7569faeb4d734cb0009c4f3fa9de65124ac47c28520f06692dcaf9f8dfbda5d85d76fae9409910e2ed2c8d130cf22bac4bcc8c880cdf01a5ed75948b7754f93a669c34e70f4e808c4cbf74df3ebbbb8189ae6d76c675872b1a5049886a6559d3f964a35f692d40ba279be7cf2d35066cc8caee12517b1e7a4ee6ec36a9ae1aaca77297c80321d8394f27dfb0e20f5e703f723fcdd08eccc0969b0a5c8e851a114e9ed7724b008e985f8195298e4fe0cf843a119baedb978f3f8637b91ca935892bb278727c7e4eec96165d37d99ef9340d30d67b8685f262614057c47cf6da64e888bdbf631d9e2173d102f873e668a409c4734b46d2ef247fa14c1a30bd7242677ec774cb6bf5d2fdfa71d90ccb9efe2a87efbfff2a2e8bbef44607f07de97df7d993bf7fca3b9df018930c67fa3330e4da3e7e13ec67f7ff4dc18649418fcbebf10911cfaba8ebaa8c96bef9512c8f32d0b4014a44fb414d9b4ed2f0e1d726cdbf6dbf6dc6be00d4badad0fa494b7774d7bcd1b716e3ce307bf055d1c556bc8a2cdce340898a803adc0d6daa02f3f88676bc3f147b774b19bc57edb70dc6cf5384b73373f6b1fc617ec776f9fa3d9ecb7b2b6946b491343d6be858430be80b51048a919a0752112efed0782d24c12edb5cf8666b3a669da0d53f826ad3f3469af7dcf32cd26ad7f0bfb86a52cc4c5fec7e9ecf7b400a867052bc81d66c95cd852e4bef6f6bb0b6a4066910ebebfbdf740bcdc7d0f47715fdfc70f0fc4a61bae6abe7f438f036ff4d51e48e96a9afc3c7c423e1772d807fac48b3238b29c511f87971153e329183a1fdc26f773df11b9df75a18efb5a3864fe35794434f0ce13f71be89925092c339ffb9899f940981c60ffbea94d3aeae44c1deab07fdffe64eebb3b3ddcfd959929ede74340fb39a7f63df39fc7fde45ee37a703f0ac78b391157e2e697437b395dce90a93f88cca6ef7568bf7de865062289f7406caf3d104942a10efaf781d07efb099a3a347da18eedb5d0d43fe3853aeed3078286a60e635ce45e669e380e096cea5efb9e3175da43609b7f1f08fadbcfbf634ce6b45739776f5ecad9a0ccd36b9a4ec1f8827df6148294d2a35f0ac6972cbb0a6de2286f91d65a6badb56a4f48026bddfb3cac14eca05651825c59fe536bceabe5526097490a8aa855b43c2017ebd75a6badb5d65aabadb5a9b5535c1e9557ae3945768cf0b4601e50abe9c52ee20c9697112353438a5a5a1474f04dd5021e6572adfed53553b7453c4f55868b17fc0fc9e6041ec5ee5395350d9ea6a17dfdfa04ad822d6207b9fe34e2290dc6922901d2f9c1c9b5c2e6112da80b65e1895c3f26b6884f82329d9f5828a861eab38882a6c166ea3be2a7fa37311c1f9d9f5c95e4fadf1259aecf6941b9be7f2b2e3c72e0a861246986a3c60b64ff6a3180dd34270ddae143962b1441f5e50345501e3b4aaeb5c94fadd4a5148922d8f5c212debb4cf93346f99aa6c5c8689a4ccc8ccea14796efd3c6f867198a50dc7dfa74af4d74576a170758b32f2f28f30c30be30f6fbf61d96b6ef1db6e7a19fc5c5d9ab76b1b797dfcaf31662fc2db5f0b2a0e25bb8374da58cef1a97863872e8d0fd5ad8ddddddddb2310c0bb2457f869438d030870e9f0ebbbba594524a295b4ad9b2314c56e19476e19d275b69d8e3870f2019faf8f1a307d08fffff1f3d7c00f9e8f1a3878f1f3854455a3e2da023ae1bc99236402e19a887a346d9f2d102226d642b4fd903e81fe8815c34134c61ec29544022804587bee888a131a04ccc0c17279d523e95f74ad9522666460c8d91a13466860cdd213e489f20236546cb6a4891f2543667f7d0c027d3a905165ac07eefbd9a4629ddeebd97e3b8ed6a2c2c2652460d1c523e0e09e690af838b5386239d41faf5ebd7afe7d0212671e4903874c82155322dec3fcac470e4d0c1513aa64c0112bb3776f14d2c5697b8ad38a446ed7df4cf01ee681f1d3b28a53b1c4569e9b7ef2eb82307a8630580ec3976780e47c9ec61882346f504ea3f437761023c7af409d80635270bb74f0f0d58998638f82352be73ca825d9c9409df133c9fd2eea6361f11f09c53015dbf7c4e30fdf9f5fdabd719fa9d023c7ea6ffb2bf21e011e7a761cca43d484cc9dd83449418b97b90d8c9b0fcb97b6af0caddd44e1eadf26b40e76a1bd036fc355f892b7ffb9403161889649577dec19f071b9aecd7af2fe61a8af465ce9e1ab4f228770f125572e79ef6e3fd312e8a42076dae20ed0430803b00af67cacef47f8cd78ebac16ba1011e1b26695c33dc70c347a3cd50004f7e37b8f7707c7144f7fdedd0dd8482a0b47e4f6dc8865e255389545354412a2a8eaa6fe328fcf3731147d55015e4e254510955535433505505cc362ef6679b7952cd5441f32423b9006f3dab6911e0b3a165ca44a6e0ec5840ea4e8c4fc1caf3b78f7e054d34f496de795f688a30ad23d20a0b8b961625196630b9b8a001000f5a2dace1e5058c6b83780ac01643068c000610d3fe32956ca610a00a725495ccca47647952c1e426cf8651c9530641796c2646f26c26ad3c99b82693579e6f2bad69d51c7154b5419e5280422e9138a10367c8a1206e49ac2cd4733144591c65edbd2ec5fc220678bcd3633cc8512e19e555c906d4345a58b2d729af4ff2595c70f2e8c9ee450aec375ce40d88d61d8e27dc6a988279b8252159d368e178389e99f23c8e87e3f1381ecf13c172d740644aca0a813c2de45c6ec3f6936be45cad2f4fbab331e15c7214ddb8f24a2c04459e4fc116339d15d1cd4accc5f92f526077c99d0b9b8699ff52043caec4425613b4893c830b9b1aa660ffd1c5cf8f8b282d2564358d9659f2f19929173f2e7e4a3eb2e4e3e2a7e4235dfcb8f85989e5e92287403df36b78e1b1b64a27c0636dc919dee82c236686bdd1594e197a2e4a391dec172fccd252b297e75358d368c913e316c575e7072f589e5fa34963a025a80cdfea072f53ff76d09e7a7dad678912685ad57620a283fc83fd76d0acb5b56ed6dab05b460dfb3ba2eb97d183e5da41a05152805f2f878b75de1d1b1b9b200421774f0c8c90676c0681fbe6771d8d61617171c9fd22a34af153fd683a4a416b84a7e309792ee7742b68010c2f11bc4cf04ac1eb05af19f4003a8f322f1b1b1b1e0c41231fa14f3dc585029e43d078a9ab46dad5325944439dabcbd5b93a57cb0d1e3b96ac697837c553de8d7bdde61de953df5c1cac5352f24bc4267dea2386d0a7ce232a6b403636363f98e95740153ae5b9c07379aeb19379aeb19379ae86a9df62041ebd56ae1dcbc5238eea5ade42092ca7d3b2ea4f8d52813cd7d8c9ee0592c9a65ceffab3fa74898b1577aabbd0c13a9ee7e996d42f75eace80f5679e6437c5c52a338de5efbb1f3b195d499152a3bf5fea1476b0937553bab09bb2c2c2c2c2c2c2c2c2f2a495959595959595951f9148241289447a21646648cf82050b162c58b07821646658d052e7612773710440a66117e462bddecd0d47a3d168341abd103233a377bf8e418c31c618bf103233f8bd57bdb77567f3e4ee9dd7cdd32ee8bd3c9777e362bddf85decdebcbf76b0ca2a8b98b41933e75eec2f186decbfb72e7d173792f5917e428b0f2d459c3c8675921e19fb0f42f8a313195a75bd2c1c28ec7c5fa2c908083f2288a40592a61dcd595eb3b385299956fdf5405579e799a91be3ee79aa9d197bee3f9b936ac5c37d84c4dd96ba6666c039a9b6ca6445f7f0b9aa9d0d7e75e36b35950908fcfcfcf587f7072e57272dd5eb39914a86aac5cedec562b8b599f599fd3f1faf655dfd6259c925cab65e5ea55ef474b02e54cc73353b74ad991c09ea1b03ec99726c628940be668be1294e55399e222ad52da1b8886e7d28bd648fd21dd3bc218974aa2ff5014f301f0d8f1386aec6419e6a8d17379378ef226ba25b56b922b06298bb6a86b9ee45b90c65c94a5129e9605058f1d8fe7fac9f53dd74cd5297e925fa964c9533b9e3c7f8aa3641e6cf319ada0ecd227f536daf4bbcaac69b44aeeaf5ff04a25f7d729dd3765d1d64c8d5c33457ae2e3a85b77ea4fe5e95657a0ed5b7bd6cbd5348855c2b3dcfd5272d328b99f42a1af9b333ba6bf7dee4edd69118465ddc0aac445af3b41587e0bb0071264dad2b4566d6581884c5f6e2fc31c0d53251399facc54ddb9f8451f9fecdf02e2e8532dc9e05038c6c46231d93cbdcf0e0b50d5a7da3c9c6c964a18779c8c72ffc5b48d0b29b5f972b6d64c7dac790a9a294f364ffe3939a59cede5ca77b6d9783e72b2209f99e262b19f18504cc6e5e4f0e4c072b8ebda82e6c93f68f39927ff8b6db69d79f2ff7c643d77c4ba83bf98cff4037c8625fc623ff83ed54ab5ff72be9c2fe7cbc9ae7d3e2efaf7e48b5921968bf4a91af900951138abd0221fa8024e2ab64a918f552a61fcb1b24f2ab90e31e2c93c9927f36413ca101d4e3695e43ae4499e2fe973312e26953cc9a194cba15c0ee572e411104ce1581c8b63712c2a4682e816b4054121a2a3fde633e7d630559e3ba512c65d7b7cc85db0343be95b6b434e0b78bcd96a9aa669219e5f7d5bc2de8ff6af8bd8daeffbbeef35cffbf9dff785a307624db35c38e22d2cf5a9dfceab69a129076b43530e23b521901ab9522b85f6201b681aa1ef977194f655f4ddfda4eb9632a586a7648ece12ea89c022a217e267446112fb33c8afd56e60f620c7905d1ac6ad8b86d1c14ff3bf70bc51462c25c767cc67ec188eae8aa4e262108b28308b27d8be943d2e9c1c7bdd88b7fc25dd65b2ef38262de846d09d492a8eba3faff6520b721490a3e4cb2952e64b59e7f54e168eb981a6e112e8937c3c854a96e39764544082883395c39c53b4218e2130f5d0abbbbbdb033e3ede92d2a36b6181d5a4944ee379609124f285f89976038c324aae2fa1c89f2aa1481f2260194a28d247fe287ff4b27d3181e5dff8dfd75e2e176bcb7cbfbebc097d34f7937646b6325eec7b1f8d8581128a2b4614af4bfab8585fb2aacc4010212d332256fddbc31e12f0fad377e0d0e1a8ab83927c4551944660c2ade2ca2a5ce9d3303e2d518f15415604b90dcdfcda4f9a946a6019b9ffa5bee499a7fa77e3995db57171888bdd5d9273ce39e74fe9e7e7fec84086859e0d115279ead3212ed69fa02c226d7e7e64f875252c5c31cbf5e58f953fd75ff2dc8b71e91a79a1431c659f9aacdd31efaddf32440acba8b5942d1118cc32cb5c9f04458541d1c1c55a2550c5ad42aeb5bf906b047a4708a8de68e97294fd7ad382f2caa354e23872e8000587155b9c2959444d22949e650b669dd00983b85865b85865f8a98aa52cb8065e218e1a4b41ead4ecbbcd809643e53966ead4a7fab5fe560f90eb5fcd866207524aaf73ef2ec9ebeef2bbe1b7f4a0a5085d170a028f2fb908ee3eb5e0b4e0b4e0b4e0b4e084360897524a29a5045c9c52ce294306879a72b9b2d65a2b955eab54d14c991f99214c29e97c2a6a478912e6c205ae2c235b6326ae0d0a26796e5090660d3343b19b50291473c19a259bd24f298aa368e882d5345a42b1ebadf8cc54e9a7f4b3e253fa59f129fde4f92e64d0f5cb28a395e9cb8ec8a4d29bcedddaa2ee5d1abc9c43b9dd8656a63f7a3618116da3910d25e813b581e533c110e7e5a20bfa2e8ab860b930e2a9f088cb2c34323d1ab2f7e5509e2fcafddd98f986616b82a10d181609592ed27799b954717761e3327399b9cc5caad49cba33a7fd973ce75ffcf37faa1c831b7724d3a73f6e3ec8f469c83604cbf44959a03b08c16a6ba656627ea24fa9d36a47521632a5da8f2db800e6340c75993dc9f42fe8327391fe8b14b8c2d86918fa2f45c02eb3183c14c64e7d1b58f46d30e228d3a51f83a769b464faa14d68335336b0fa44ffded026531b5899be064a30a769684f7f86d16596a98b3c65b44246d0ef28d8a20496dfd4450f365ce419d29dd006912973e8fa656c2940797ea739c65d475fb680bd4ef9996808d329bfe9b52481a910b8cacf230fc79a535fc84c559e1178e7e9bb9a9c9ad73cb9e6c96a1f0d87f1878270dfd9f9dea9888ae46ba7e688eba606c7515da3e32819d6846ccdab06a726c7c5e9fdd7c72090ba93a9ffe67156dacc34ad93b213d2ddae9bde539a1a70be70f4804ffee8d79dedbd564a29a5d946257361ce3e900abe98c1b0c1b264a29cf55a92c0869820b062f25f7916bff23d332299203002c2347a961f7dcf90543355904ae60698df9dd7a283bbd5ccc5d94eaae4161ddc043cb6945726d97a73903679256db267cf2450880cb0dffb2f8ab4e3d17203cc67e1caf359c0295e79f2700165d28f42551004467fa1e01547cdf2ace6c65342fc345f64245b95733dc095a769f8d7d4b4fc343f0a0b8a67f364d62468894c09509edd4fee766c38368c4a50138d5255a1c5d928d59416e728b7ac93029dedb2d3ae375220aef3e836056d59a2e6c8ca55d3aaaaa0ce896e47d4b204ee9c582d59b9562e274beacae528bf79ad721ce58e32d190ab8993274e7c9c4071f2e3244a9e4f473b77b4131289eeeac643d18b30e017b3900b49d630cc4a251cea96886c4cbb5895886ab7a489ad15c9e8472db03c9feb680becc511b8fe4cc74fd3f09faf036865448bf3c799256ae57271067d87434160d193409a9511dff75cb9b087344141c0a2ef42456ed0904352c8c6da9a1987634391853c5fd43dac3c3fe483b4507e2815853ce4306f527a5273e4c68614b17b5b52c472b7e322c0a1ec7662b8bee7b1db097da7e445976b78e1b1a968499114e1ef5bf288eb9392c7111e372290871117c1172fecfff2225848050ef69086aac927cfe7443fd87b9113300cd68b999f78e0185d817d4489b178491fb1263e3e9ef880d2c44794175b7811e4c3c5f92c4d458e833c585760ff517c11e426bc7ae22816174d38b4e119af628ef296d50b67fe4a67b5335345422820018fa3ea1221f2fc156c05c5103a79fe6a3e468d2d250a0c1b478d2f82f29c0fa388a3288ff411c551b257c954414e5a1cf9b0998443a9a69583c776e27a317b316b8102fc6296b98ee3aacce7604d83cb7e85bfec944ad6aa2b74aaa6b585ec30c063cf30f6e1c447cc53e9ec2899824734c59229605334c9b383c5ba262e3ec91d8f8bd33dec94743b373496042bab61bc55d37a6da18dbc08725409b2ffccf9a2803f1e3a1ef61803907f081c6716853ec8ec4da1d34c6c1ce5465a475c8ef29b170f1c1e3948483f9f878ea3ec8e1247f1f058e228cb03c6a3098f98a3648864f4f3793c7194c9c36ec9d8ed744a768e90c29d07e2bad903e529ce6063ec5e2d9f516882000b102bcff23db3321a420a4d10208d9ef43d331a3d698585341a918064b3888d0d0b4846f2a4b481113453a497dfcd666af452e619c992522a649e26ac0b9a27f92b61075b79966719bb5996bf121299f170ec6a5af334f3ce54ab4ad655ea3c9cb878187154cd74e5a242e40a4b880596a58f067fb5316634f435c62ccf17d95058e3026b8ea00057af2c162c5a8488c076e529fdd1da5c6aaab645293404aed5a661bc46e183a0dc512c092a40ee28962cc932848270972f7aa7964a29ad08eca4a4e295e787c0b1a9c0c9f345e0d854e4642dd7b4641e7b26a469d0ecc9f0d1d41c71318bb9ff850c3015ae194c4ad0ecf420694cf273800d318bb9fe031e307a3a2bfd6ed4774badb4d6da76098d3f648ae406bd715d74ece23245b67c0ff9b7b8943ed0ec1f0c3160faaa29df0d4a2423193d95cdd428a441793e0d34742d49e09a16f862f6d1bca8e2e2fc1c3ca27666a81d94927a77304ff6d20d491821bb7c7f02d864f78711c026cf2830c9f3c71b9270d19065480158c3c400c3f8e8bdbc524d71ef95670b12bc62791b50c83ccd3ac59a237e9a6fc19a9b9a170d6b5a2d8050657c31cbf39dfe08a494ebbb98835432d514f1c56ca6280c1a68904196660d1f0dfe6ed5831c43be377a9863e07e7b0e8618bcb7eed9dcd406a6a0554d918d0083d8a5f34a2fa26002c8d3575d0b2560f9d24314ccf35a816ad8d5d0bd3aa2db09926e274ce4f9dd8bd773ce956ba6564295ec7a8eaa01460090b7bc704dce4c753bf3341bf5220c2d16d1a817b39ab943c54e9e54f0846167a6fec5cc7fce6e274f8a002019bab20dc79a535b0de3ae9777d334b62a4655a859c8950959c2906f830d1f8df6272f001e0f223ab512d6b8e6693e09b7608a236a5a9e9099c2e2ec0620000188e1cd8f11e3bb41b3908699dfddfd620c9d5a0957af799a4fc224c83374028388bc72b520d1edf0382a00797eb7c4532a196aec9a944e80dd433f3f1b71147ef1080e6f74a64c5ecd0487490e0d573879a5e3a2ce2a67f55ab956372ece1f40642a99281cafec050e1e7be6c49567cbbfbe62c8f0bc66690f55321960208f0194670c997bcd114fad84aaa0d01b854c6c3820e01831d28fdd2b466ca654533c86cf6c5838b2c4887556c95ecc00770d48e011868b5479e6c961b8f2fc4e068c27801b2d38531c65ca4010958cc6c583355342fa347f0a96f70cc68371942877b594caa076018f3d05aba6e528930d1799d61c30d760c58cc6d534443f7f5301564d9935aae4f950b8c0c5c42633c0f802eeafb78e40beefd5c03b4fef4229f5df2ea5f4de074057973dad7322d0aea5a14f1446a63c5afa74699840cb58b57ba218c8b3deed873c658bb26ef56a1b68803e511f1390e9026e68f11a6095a776ca3764ba439e2fed77db28885faa95b20061b11a6d29d469e7a42207769fe6977a52ba81154f5c9a5a1828d5a64846d3d0be24131333c2348456f4e5f0d92032b668ef7d2b1e58b0b4cb5f01dfbba4c5cf9b1d575b66aac40de3928a5b725d5cbc3dd0de729b7643c372d07ebea635d016f37941925610b096bb27490976a0030611e8c8808450c5d31c147185ac47276788a0d72b66864c8d1e1d967d32441254c07097bb270928950abeb97b92786243c786872ab934b6318982e409162479777777b7e2086d32c1dd5d9352b83b0a669b64c2563a4b4be06c4b2419e5ee498112722877cf122cc81b94cee557596ba55ef6424dabb5d651862735e481d212c52ed25ac3928bb4d65a2b286dad95524d033bcf92a364bf50bb8422484104aef3b7d6deeeef8aebbaefe67d110c22b37c8ee3fc391e386bad8c0cfd760073c95afce3e76ddbe4672b41d9dc5fce723bf0c079e5aeb5d65afb356ed5386badb5b6b3a173dcdc36fbd6dab7b7e4a22d65ebe1bb68efe5b6cb5d9b378e6bceab9ce5eebdefd97779bbddab352c4485ae3d09eda52ccbe766f9f3fd964d83fbbbd2564064ee6538bef84083c83cdf8273bc5d25d7fb60be9f63de8b33b65f01d06236eabee31f3d3b7e7f4d645d1cf1b95e1b337b586a98faa52758fb208e1a3b87900e340d9489f4a1bf5f49e14acca42785325cfc7e547f045e17bfffee76b7bb6dee83ccb8541a69fe424f0b29962e250ff23d89dfdd73b1d6920c5f03694b8931f7e3cd5c885dacefdb36efedae3d6f870c4fb0d411ee70b1bafb0eb23bdfa677a9e7efc168b6d43466be1bcddd7af3aa8760dba4d730f2594881c7f6294904c85cdf25878e7a350ec8ec61488ebaf093e95f4ae9f70bed41e2ea41d24a40773bca3467b8124517bf5fc60a23091c8ba3b60e097e4e2ac9de8f1268c6605a9124b43eadd5eae4f95cb660f72831cb151ce5ce9590e5ce9d3b6a9f68a067edbba17d34f7ed7703ac4bea4ecda92f9cbbb7a0f6171cbd7c7ffb0a561d7bbba7239536b9070984dc0df3adb617fdf6e5a0bd281c6b7d559cedbf1febab2e7171d68ac3fd386113067bf223256856611d79e9f034f18922a3541ce59cd464e76d6fff7e376c7de599c23a8eb23baff9c4c5267e9ab287cf74bcaf85e3f6dd73a0a9079c6928f3fc682a78df82170a1eab8df6ad72b0cf85637dab19014fd88c62538bd4397d260c8333369bb8a885138604dc595adb38348220abbccb349c3017e76c12cbf35d1c61fe38615936f819861892b022cbcf3e3f0fd1a0eb97514a57a243c293fd3bed83e531e6da0edcb4176f7cae4ff38b8b5ae8650ef420caf2bd981a3874f40022a448eb264709ecc98f94021649d2f9b70ab3fd8f63e9f2f6614ce3c30d9941cf3d80d1bef4efdebf1510dd73df85a367fbad389b93fdde29a5e5e80dc7d7e4cbe842bfd21278fcf0c5450ed81e24b1ec4f9b46e76680ec41e62dec418a743a0522d35a043cfebf0475be25114591d7ba4b49fbbd9aa7bb9c4287672a1c3390b96f707b6b41a794524a29ddc21b76e198012d744a29a594d239e79c734e1a0ab9417078a6e8cf39a7b5f6859099b11fc490faa57992411099b16110f6277d0f7b9e64b701faed90ee02f45b2279ece7381b76dfd02f9fcef4b79299338143e6537777777777774ae90b213343c3206698841333f71c76105cf782506bd7d9b4fbee9eb5d6ae697777775dd73d471f77b7ebef3ace56ae6fd3ef9f739a72e8d09443fd0e81d4c8d7454aa70deaa89489115dbc4d4cf9026a0150edbf734ef12708a47d72ad3ebdeb28973967adb5d6d973629cafd28edb34cff3bce955ea795658c1bb8115b6daebd149a715bc1b5841e79c56f06e4027142aad4a50cf0bc26ddab787035a2f124a7895d2aa84129e376d94e02ae72b2da094ab7306ca90fad144f40f637071d426696903a912d57fa6384b3d8e7a54f83e19479df8a81092ed216eeb3e1b5f087375d23aa98c73820a75525aa98c7362b688826c0f51519d947ea268039108568c131304e262d5b8805ffe31f53e06dc5a353b6d406d30e2b8ed5b7d2053cafdb001a95a7b359626a4a74225196630e5fe512be0fbfeaf062b66e60b5977e6878ba3a8aca80cd01963f11c07ad5ecd5376859d1508a7beef7f991b62c04e89f3e473878b12e8e1a2bf945d133b68ae98de04a2818b3f660bd90f8d814e4924ddac16796c5996f2819285a408db35315b544147f193856c88e4b3fdd90ffc314f366c255409f8e5ff6ab682405ca49e0c27c1fdbdfd76a040fcf443566d5d89d2f58b8b1b029edd2da329a5945a4a9f053ba594d2f0fe044b93089c129d12dcc66285e5863e4f441a618d854501fe9aa5fbedb1a36cb7792f8eb2dfea07adfbfa5823812350368cf618f486d1c251fb97aefbc13ef7f245dd77dd7ba0374c17765f0edcea039707edeb4b91f35e346b736c50c3a05d16c9deb02916853eb75fbf869d44d6499f2f2edef78894b7a681bf5b5ae495f0258db6044cebd77dba7704de9b81fc22f0fe777f5e21defdeedeab7de7faf2c5c56b8395b822a74f74ed2d3a3b5d9547648b4a9edf6ed355e84f149994096be2f364eaf0ccd91d3a6bb66837add4be89be265b261aae6a769619fc0583cec5e70923513c512477144c7247c144963b0a263f3977144c8c90bfdc51306992e57b35c83ae4b4806f4f7ac3eb62ffbdf76af76a9a9d75d2f9f5882953c3ff3ea61a0cb0e72fe28e972f12c85f2b2a044c80b913efa1c10d76f727de242850b0820a54b0a4892b6688ec400c2c30028e0ca7073633c664fa3aee4eae4c3c0e8e62c94d901ce104227878002509222178a2c7c809ba70e352c20ca53434063c93dd085dd7a283bbeb9824b087ee58d4ed4929dda5cba58ca8c48dd6453dea10cd00000001058314002028100c078442d168349c07aba23b14000c8798447c5e1acbb32486610c19638c014200040040000064a46940003675463f8a85534a1376020bbac5f64302230b8959d35335064ebdd0d425d63472d8348a07918d6ca5dffc4ac3bb4054e55c6b8156a8d031db0c43dc418007d6709d956e846a960722ba704608be2534757cb50bcfacfc928c377858558a40145459d0a6ea1206a03fe2e6e01cc204029470940944e3865d0268c3d35e851faa13806c5e2b3eaaaca9cfcdbc2abbfffa959477b58df61d91f457d0bebf587e19db226b4d04f261a922c302d9d5e24efd3c66511b29a5d103d527661379f45c007ffba73129a171a19672b77f824a2dca7df29c50d379030f00701967910cdb0fe201067730473d4575e79beb514e358691828e42dacb39c30096c7cf7f2231fe4225c588608e7f4d04f8397970e47380f246a200c1190729cdea0eb56d7b59659e9baf15076244a243ef74ad2cf0192f3eb258ffafa3e6a3df5b1b030c5e670200b9c26b5f9b950c2c0e448ac13af2bcbe99cdf269ebea212bff4aa45ce7223e1e60f91223cd918d330870fe89f13699e3ce30bf621e28e34f6d0ddda0a5012336436f751852666f7f919e32d10f67288017fec028c25105fac173e2950b2cbb009b1ba21e7cc0a4e4c22ba6c22c56d2ceee5aeee997a276201a98dd1d4700807c6b5a01869d3a8189b68f31b6a12ec70ff4b9db551df26937237403cc81dd19eaeac43faf4e2a2d28ed398e33d62301c231e7720398f89c35b322c6c33c5f3ae6114e1cc191d817f9eccc32f78dc9497210114a7b6f8ddff50d4eedac19c7ed3cd555c90fb68adda62acb142c3dba83b157533f1c32328047c7f6ba889ae65c9e524e98a727837f779d7e33356879c0895371993018f3310c9c412ecfc0d14eac67dd9a06217e21cee2589b9cde796ae8f1e254fb3c6144ab0ab40704423b01f92e8e34f19cc3e1da8b1353a0bfcb95c2025aa655c66dc29da44022be16f88d8a433868d6828033588275ba5a713a4de8632288bd8055ee2d9a50c291d80e70d4c9a25ee905e83863dba47767b471d99e3c2bc46aa44a2d45151f1d80cfb083e2d6865e528f6681a50cc994f0b1c9506f8fb828fd55018514b6ef0c884a9014f7c92b4da5a7cf69a91224e1943c80890989862a18d678ef9ac1030db2232635ed748161402c0064f200cde507ed89b18939fb9ab0e9bbf0b23d883baae833e90ba93cab02d884ccfb5b869f67792b8538cdecf01b1af880cda6b1f9541c68e59cff6c559491a0c336c01e0c32b60dfc304fefa30ef46e66ff101985998d829579cd9942526ae67a43674b2214a217a8d613baf9650bdf036eb2a6db29518a20281b732acf47637023571c459ab3bed92fefa252494e9391657b30d131965ee9b5a2d0c4193cb1767022353873d3f2edff9c678b35ece6f45ae1cc78c16c66bcd9116cdc9f7e633b21d6cb076e59220b8bfaae12f8b9f69e88927a0ec1cae7f84a8089ad99a5678dab46b16c1a159cc3a71ec054f1fbf4f7ec873b10ee3ea2e1850b10f407a7193dfb91c56a840bc490aa3da8c75a464a8414810130ab6400d768ba55f70068bcf2e66455e84f8dfe7757c740c17d69ac3233402e67df75add01b04f9f0e398e9eb17a4f3179fe1237baea32f5d254f880e8ef1105eab27efd097f20c052f055567b008e88a404cc32dde9bc004d1c16b926921dc482582533d3893d4c0779befe17fcaa5af52e9a4a362ee69c1e15de594b16e691535a2cf5c23e518043571c4f30735764d02677699069d9015db8bc8e515287a05bb5c021547ae4b9946a1c50eaedf2915c28521b7ea95201e74f79d8383e9448795941c5452b337d45ceade7725aae41efcf74119c153cd83a403186fa294d8da844dad3f84ecea0652f7ad924fc8af8dae1e93c454cc13dc5ad8e674615d9e27f0fe729060d3268456f2691980a2f345ca0ee48b62f39ad2a8546efcc7f7e39b5cb97ca3c4fca1924192f9fb8fed2994486518ca60b69dc03a897f051185d6c71df0885ee9e912fb7924f2749d2ef7f0900cf3d7a4a1e26398330f90fa7b2ee505ec46f89441216d9b9b7de1fa48420644f3dee79b009b1d2f8916b048538f033cd0b7997ed798b600fe2e44741ada1689e6bf03468304d0048004545af595d933a48d302930557586e6af1b3892f1fed99875a565cc5fb282741e1193c6434d32f1688fd9e037b31298889e709d00e4ebb030fb15e38fa7b7e7daecf1c08c276892cb20606e784f341cba83f0dafcae88ac20b2006427a939dd1f9b3c68079c0b49eeb30c5572ae8875eb332b1388ddd44798863d168a7b7ac0afe5fbb6d0c4740f51ea1d14eb2b61cd0d8207131d0c123e125c81167495b7a0617bf1b9d59ee9de7a1fcd39353c6350d3dea0d9b4994c7da0e01764d3df2794205b1578f6d90808defe60e0c4fd6cc36b1fb2c19c3538fb1e34597058f2108b45f01e2b4d82b80ef30e01203fa1df8b14ff4b164d41e4b46a782879710752820e38059fc1db2bd11435800c57d886c4116cf204cc35d2e9c8cd1d90330bcaa7b36409fb3851d35dc918242e9287e0c0dfe97b44db837582095b29589c2fbde774c057c4ed7688758f77cb534328c7b4c01d0db35521c7c8cc9abc533b9e423d7da7e82592a215a784ad3e880d772752e303973fe94f5f9e4c358edc24acb39a96dab3db664322d9440289ecc78f397d0ad843da7d46462f4c81ccb54be022b6bb95643aa0badfdb305401d70928d11b3d874e7cd63f210d61379eb510db89313be4a573624a8cb5f8b0cda3a2f3e6dd74805e33ee1f8a4d79642839347dd84509f346ea1d41eb5bd7d09300afde12427ef97905e66dabf41aa46706f8d92f89656945ddf5abf8d94b5cc788829d8ed7d5de0978b006f6882ebe1e5b17b41fcd06d19325db6d44cfd94733acd86eee3532ee037fb84794254662c0629a3d7d8cd298a22a63498a0d7243a9ef9fc4465c926145ace6dd6663cad91a2a7b137486bd2b7fbbf3bd044b2012b09e962eb47fb1b548207ae7084ab542b6444cf07f5c6c16ab5ff9c40610bb4509541ae2190f0bfc9086da3b4e60ffa6dc41cc22669070f9abf8286edd3aada10a6ece2c1fce798296b822e460cccfdaa5f57d6c57f7e6049cc582ff13a17a5474d57cb76ec836cb35f963d496433ef6537b87297f56a94bd30318a54b7242bfb1bb54d430fc04ce563e447bac528454bba73a8980d5a62a85e909248404a9d9c4dadf0c0b93ff023b5169614cc6436d8f48f0fadb520998c68787b282a56f56c25c29bc34e6fa7675f31c5449bd028050021a5dc23813344b0f9bc6b3637d9139ef6a66f5d5db4945182fe50ebb76d48c189538b5083cab66b3611e83fe81aa37755c09b3ef021cf1d10843c88a761645a6d1ce24c9157ac58b49a340a4f61f5aeeee5641e2b3a972c91ba1cb50bcf27c6d8aba6e1413b5303db4a8ec30aef6e2135c704c35a1e17f406f85b782bace92ad5b663053380c7ecb269c3b5b9e4f2d7c3ef99203844a5f40a5531485d1f116d88ae6010548400864c7cfd89a5e6912deb1cd5041674012f7121ad63922dc7f00b6b762eb465c3856febf2a35c783bce389dc27554a0ca1548b207f7060f4fbb6f4cba666f2c1d68957f3efc95ec71785640bbbcebdfa4d8030ea0418c59cbe0e8843ef14072eb7d5307d54468626cc312873234456b0fa53ef85f3371a32c9cc732d497dbaa37e0d45cea3979bd1629ff1fde2a715451a1260f8122a62c80a7d0ee33a781dacaefb6804f84ec35293140abd055473ebfe7fc8afde1b2dff05547f080ac3430eee28608d0cfa6b475f360426734d06cdd24e6ec678fef198d978e0461fca209bd3b318db116346ccc472bbf15be81501b6c077b063b426e93ac0b7b52ed0d48516716e652d984879b51e6b7cc426703bcb2d029155daa7fb1abe32ca8a86cc02511294ff1d7aed718392d8a9e5cc40e081d6a71e8088882ef3012d504e6e1505ff8b4aea8e23247fafc35607ec6b7eed6c387387131fb0365cd920369f5a8f59f9281534549794e938977e9aae8c177950ced45c84154913c0869f9229d7b5e559aaf1106cc2696fefc39bd826544d8cfa440976591226bd48d574f3e70981a64b1870e0ae153889b9cb96b650fa3bb7a7a4c737b156f8ed8798d7a7d4003b701aa90773e4aa6ac50f832aa407ad43b3032513a6ace5c53dc899e071a79bfa1214e2bb896d06c6ab5a4c078ec55e137cc0a3193864cde6f5092b73f511a65c06183d20368b9e682867a041e116436d1ec81364d622d7e3b637553bb7e06c799360af4ae2e027d891dbf185300c1ed1253ffe39b9405af750a5659b10e8ea0eafe6cff4cf63aac0d9c540711e42c59e3ddf6a630b28392c1b720cd5827fdb8ec24be15393222c3d68289ad8b620fdc1032a0e6976d55b7ac35aa5243bc87b263e1f6154e045373f3191b140f022372953e30fabc9c6c10628a6159bdd7445530e365de938b36f358cd26df9b33ee651862927015333d91405ec4449470450b8032f91c16ad132772a2b607b7215efca1bc1128d57374ded1ea037b4b69c84a4c7223da5a6454a080961c47c7c3e324c71d4ee3aefd35e114bfb82d70b8770f4f4493e64f4a9901d9d6cc66e45d4ffed9f091e176b5e0c779cecafaa541e2f9ea725d98ffe401e50f6595cb4bd6b6795f99b1996d7ec4a8fec6856730885b4268efeae34b91c4b3fa6a6889d0c1d5e2e94821c5c8c0c93a77af93c3af969fb52f668c9ebe7cc238ec9bb5f8a0ac23a0cf9f4b9b4d1d09783cf790bc69eb08040a2f21a04dba6cf3b52e08aeeeb7d7ea0351df441b9f9b71eb0c27da18ae15117c7b116501e7764c1ecd133dde82e33944cf9e637768a528d4a44fbf2a135a9a650c43555ff91b10636eb6909ea3853f9b6d8a299bc2c2c8130581c8d149647324f5e9f6a9a83816db3f9b63f0a0e506c1123a293568b885a9e997f0d56a63f8d9211922e1a9342ce5340b19d0d2bdec37e694771c8f411d18cbbeb4e9ec100645af50c631df49f3c09286a7bdaecc9ee62daedaa89b77e119d926734e94e339a05495ccec4e05179a8956fac93d0d2b7bb29c978a77b0e0f6718c0e1c97498c7c17358b4c650bdb0da66761624e7dea9472da65998efa82982e8a73a915ae42ffb3dc8b18e89c255c9158b699f1e30e95c6728d798ee4fe13ff8fa389c3c6a8ba3e5711e5c6e90d1ef9bc67d0ab41d32de1775d4af4729581a9808dd02460d09bca72faead2112a864d803539fcf5759adafe3ef7c3520f94fd3960c42bcce5bc1c63208140c5cc2d24080b69d838f40826b4720c046e94b1f24cab42225938f194cb0a0835b045f896f0382864783dd1b745a6b339a8e759c7398ce16ad235b53a51d7bc1ca7100e132451da89e80847070703fe575ee04daf37a59221145a0d803a3d52af30a665bae86bcb7401092d9a89eff2d68fe408a2d5f1d72c8b1d942f9c348813ca14c091cbef87504912cb09c2f54788cf2fdeae4026713bf7bfcf6dfe2a17b1ec03685612d1186ee7802f3cc69747aac8b71c2de6f5d3596dceb779d9b06fa5fcfed0cc425eb3600a9ad7c7dfbc6b5d8d0717621b7b9237401bd4ae933344eb9e6a6f15f655e67c8a53e6759561eecee8a3e3e3f89a1ca9d5ec98f2214a4a460fd4803a6f2e0a79949155761d19cfee75e96cfb6e5fd49ac8f06e6a7b1ea85a90ca6fb5ecbcb0f3dbf4d6607040213007be9c868d081ed6b62ee0c54d60a2d7bd7e48cb1189a15132aa89431dcc0288cb5ea3db1024741dd8e8d728d5f3f0a3828240149fa27bd494acaa170ebbe0dae786e43b14f6eacc45e271f9f892868e736e1f9a47e9fb82e11a5cdbb754c2d4afaa05653008c87fc4983c30002807e0019011b89666841278a377666b1bc528625545b9dd010845e13d366623fa7933846ffad53a271c167e75a6e122c0f4b02d1703d1ab78ce41c0d067661b5952076a050c96d66033e408569dfd0f61e87f61176f43f865ccd8dd00fe890f418bfc10d516ee79661e8d0cc5d807c2b620743b7f0e70ee9e1afd590fbfce16c770b432f3aaa819174aefd36d4c6a8b8f4ceda85485a9604de7db82a9d1b4d67cf84726adfe04e560a869ae715a91bc8047d060229d9cef73f3acfb67fc5b73ec7d03eef9499dc7a92275f022f826e0146f4cdf782671fd8a0c48070851f68c5a1d1281a4874c12537158b90d27a2c60281ff7ddcfe5ad7f8980387f920add43c2251e829d3f0996f8ecc24c42da7c4b1fb1f0889997125befc33c147e7bc2b4b970add2190ac789d1a29e2cb279011e6a908574583e0a5b9350782c30095f1e48e39fef885012d4f57c0c88f63f6b7dea95a8e5af99b5fa244d190b4c58966b1e3ebb54273bc7bacc6b8b138fdf4947aacea48de9f1d4fbee23d3530aecc358ab93c74b9a4b30d39d58c719b9e2fcba07c5cc99adc8e619535a1274bbe1fc985636aa34bfa83d4df480fd57ba8e731f0bc820bbddb61bc0b627b3ce5fe9e6a441b7b7fa2ff0f740839a73a9104f33b6bea04040cd8fa016c4784a1e95e1f1ba887ccaab09adbc77356e857b3891845ef27e7b056e5b2100a2807b9b146bbae0c101b8cd9a1fbbbed9c85a847a8c7fcf500351f0caab74d7a13c1c4bb419d14753069b389dac23a26723573c49c8826da3b46821147edd0cc03f5c68d471936736e0b6ea382cf700584c63b3c0b54b66c03442c7fcd0a91d93fe9e003dc84d33625b8d81564b36ddcebe65eb805a0379baa5e467bff9107750b860d26397f7a858575e1d500bfa0df25d60667e9105ecb36825a6f1f0613973757c16f3f3e1ecf8d124a067360c8d1a1f03f5ec84291b96f0d277e6b7bd89cb6689ced78f4c3b55ac7a958ae28b2a300a2a4e4cdfbdd9fe2ce90d99ab38f0a00186576fb5c2374812e40c1ee6e252c31624e4c1b22fd5595eb6ef9c7322eb3ab6fe56a2783ec4d06c7f591c24517dc2af791b1c433daef5841c485fd8c76abd55fa599f573b96a2906b13ce3f3750746bef12c4f493b97481d59d4fe66081254af4a06c939d5d63937392b94c2f0b3d43a3cd1897f48914ec432c40ee22dd6e2fc248a7f6bb9f1c40da145d2d1fc303fdfd27e8120942bd6151964b97f8ed88944a176d574b900f3bd79e3dfb0fac16f1670512c8b24c05be3258bc2de86193d7b674a981c0994ed487069b18c0467d33f146316f5b9366dbf1443866bfb89c1a64542eda48fa0244157b390be064d1d0c9f3d99a7836d21a620e9e27563c410b883258aa1e8673419789e787dfe84fff295fc248b51c37750b4bb73b02486ce4cb21e1d0e1d12b295de82e0a58ca0d5bcff667624562c64fd8fcaea72cf9ab994431a5686486ccf30f699e43e0b25c32f1b0de857835eff39ed34c082902bdb5efaecd68d0c4d07e904653c5e589378ad10207d8160695ea35f352c130c0335a2108a901a279c09c99e8d46607543cc0f3e497318d2f1848f2506432107a4f0da0aa80a386b00f1e99fa278057840040c45febfeeeb4f594f4f715b6f227e6db8b5d154639937816a0300e0047173fcaeb124976feb8fbfb6a51c29970a199c0ef4651c73e59ed80fabe735aefeb2d0c909d02f29a5c01d153f5003a103e82d377226bd489daca4201a406fa115752d19ba8454b0deb4740b6387be2d343305d3ae83f5e255c64a2a60d5259d78fe6fd10239a93ec4f9294265c89072b6613d368dc4ebb22ad60eb4d5aacaf580d65d742f6b90f62c818b5ec32444b4234dd4f0ee7f9fc342ca8d70ae05600a76e3037bf04244d027c0422bd249091b70e1d9cb2dd5271a3ad23b27d8d3b6b44488395ebdaca8f96ea79bf08cfd6772cd8768b77333733760edd3c6c7a5acecdf6d6cf97b2b8c60e9c34b5fe207ca9676af9fad38307dc04ea49476423d966556a8a11690e107abf446b8d0cf7e5bda7274e36b10d8030706969ef8751f6c77d97b52fbdaaa61d9c1ae8f01f6b91cf419a2b2dba291282788e10753b339080f3c0efa2bbcf904a3c2914b1d1b80ced23baa96bc47552e9151f7fd345e1b3244736f3f11e4b833ff142bf630f0a58d946d74742fe01e6dcbadd14ddd541d5c35214c88753f4417e47f15fa4cf1a5aa31e550fd96a13813b630cc06505360061b315550b05dc08d3f30e8e28970665fd443faf11b0581767a13d73b5327bcb77b7cd3ec9888a7bc09be81f434bcd1613b93608fe9722a5a7b41b7ca74786447d7ee5090d6d2b7370decd7659582cb0e8debf804cd38f6e7329de7b3f085044336c83a8db6811b1ed497b79b6f5d12a2321ef19f4b2fe4f944047873a0b509869137ecb0cd9f392d38257119ec66fb7be393c2e304f0a5d1fc5a2a3d128b97b7574c6f7be5c90b864560a041e02a93e911d0992b7cb6895cbb72de5f57ed23c104627a0fbd268c1d764ab08953c033dec4501f3d4c909b4a04fe16d15380315b99c596114da5d13d20982f6828f0b95fd5859a8a3f160cdc58d3349b6c03d07d632521ccc9a8108916a460bd3797e2823b47f0b1f1938be093be260a2a2a6cb83026e54655f205a268be44acb5c43eff8230aba4dc46f0e3208327d8759d7706430d84f8630519c15cf627073448e2d54bac4ee16c0cb091e80b8c439809bec0d0e3ef9e818cdc81acdb22ea51fc35297496ff41d94cd2e6c4a8ad226a691deba108b1d23e15c92b932862c3bde66b2ad3e348e17829e00a15f523a7c7a7ee1948222b0615756f666dd33c48c2cc9130e18a3b9bf8f0bc333deb514d165a53f6272a7f305eaf33da4f0cc8bf8df33d87e4196d20e77673f99b5b24f015f5af94b54f8d26ba32b63372c84e3f183b68af4857900fb99c3e69659331e725aef35a343a253097cfb15959759af61116061c98fb77be3723f858f90c4038abaf2aaa0fc1a0eb5862a4db1a0c01442308e63f9c472e3ae0556a70b61c9f2c8c8ba33592dda60e52085337a1393ce32d87927bddbc5040d671e0daaaf897faec7c60c87f26205609db12767684763d382bd909cb9abd769d0bf17bc6e198ea323580a999b580775a3d90b8501d0bf42c907471a01dc2b1080408f0f81673c7a04705f7038cc5ee1c9204e71c5c84df0ac99c399e01dbf9582239423be39e33951f1ca7259a543ae36d6b374fa6d154e4bc6f9f00f27e7db51b76a09a51801f387f89d2fa3ca4970b9f4475c83a1b0d8f56bca23b5d25a25ff83aba024e0d1125621732536fb55d113ed022eaa60c7ea985e2eb5150216fd6ed3389bf89ce65e0700c7581062a608c69e2c991667313972baf701c40d294125217359573c7924bed5204aa70090e2fa5a77e32c80912977d6a2731c905edc25da89ceff76d95d26e8acbdc97585a8a1bd17dcbfa6fc203795ebefc64b74c3ed2053ed632e2f50496482d85ccdb103cde64052bdf939f0d119c15bfbb860f7807def5fb506df14fdf9cf2d66727440129939c7e13253fd047d9d97028f6666ac0896de83bd487ff235413ddaa3036c1aac3ce52c6616a39b2a3566f76eea205445a8137ddc14c99d0e1b2727dafe9ff0d90e178693d041ea712ef4828cb2a4c37de6aa45eddfb152ee33cda5847bdf77e2040758d4b69f0bf5a9fde2ed5c82b3854d9e39dc0fc94b96e239d6dd993b2efaad18d3d1a5daca502b913b47c73b112d72dee0accca7c5677b4e68a9eb96d3dee3679b5007a9efcc690f5ea689a69f0396473960eb400eb5eea6418311928db5ad5ce29f8bc852d6d173b0418f3162d5882c8b4f042497f11edcf3742d3745c9351a4143620e40a3d3c6ce4414f76a881f19c5ef5dbf0f2350a73ea5bbb85163518669734828c0ce85750d0d98d34e43a607e5db7f384be6f244d031ea91887d2a285b161270a63f0470ba117187436cec5901a28bd3d18821b4a0f24dfcaff6cf4eaac9c898c6493e27ba2ee6f3ad6581dea7a2dc10622c455d2554c87710e076107fb064e47ab1ec002a42004e150b7f04a0a9605d087b914e13fecc567a4e68108516536c238753be5583b6a659b8d24c8b524883345fa06e12d97f144acbf07b9fc7a970603cf9b5d589a8212040d1d0191137c324834335da8f0314f109482fca4478b74bacbf97781407c7b02fd9a8e27c3c68437238c162256c4b8dce6abff53dcf36585a4905716cbb03fb61839382a0cf0794e921a883561273c1a689ddb1102793978efd4b7e70d4e68ff43bace91094ab809bc376fe29a711fecfb9b38effa6ed45f6acdf61e820fcf67ca4a54d17db74d05b04251f32f628fbebae3c61c0e5b386dbcc4fe5e07ca1c837207ca5e29af9c5fdb2c97d4a0cc5470fb8b9667ed43c954ca8657ebd7d3d6ea9fc10e9268ece38d42b955f11820719bc2e451ed8623bf1839dea63c7242d50eb058290a62b95dfc01ab0c6f7a724b4db8ebb21da5134bd6367a8841e11739f97436cbfd32f634580e055202a9c8e2e63514097d31c7b12e84d1011f58f157f139e0474e8512512255e3eb779ddacf86e167976cea4c7fa4945beccd92facf7fc6f4db97479356e4a89ef23afb79a827dd4dd13f8e20fa42f87b7581865c6fe639d6edc5ca3be77688d7400125a77bb20623e4acd666a9f25d936c2cce33b00b4b170c570e2faf7939337363ba39925acf53efd23fd94744850532ad1f0fd5f5d2c3135527e2e8c5c2be441465445a30edf00eadda8dfa6d32192b69b04160391426be38648e4e2b703daf71614557a8ccc3eb8abe4a33ee693349e06587b701163fd71cce5c27cfcee3c32e1d905962440a28062eb7044da9fe9a28fa05d39ab1b82c9e7b38b98fc80816537250dbf0adcb4e1bcba4ae263a8d44e16077a7f3d55536cf1fd956dc81fc2e7e81026acc048a4f661a69b8756c3795de268895a00403bdd21ac4ffdc66964702f62bea92a336d637adf20979cdb5cb8bc2fc2d64f4522098fe4306905e8b80053bce4eb9d0c368a33812541cc0c13f89063b4c7299e48f903817f5df27e2f4fca22f919973189d9b5963714e00a328b5eb16e29cbef6fbf6f61da8e9127b723c8fc0053b8e700f8040809deba0c4e15f527de5eee398fc0e6f9ee5e423ecfc924b7d5f54f6bf793c68e79e86d4047fbf5bec3fd869ddc438b404f3596709fac138b64170c3781e34f1c3d649c417d66abe94efb9490e00a23fad9faecb6e29c4d46b022d05d57622a2d8a14374536167b095714c9c4df010e4b94af79d5b42894d006ef75d80209042b960eb66182814581f8e612342b2e196fdadfe07131fcadbe21910285c2ee58100f19811655e066ccae50e3b05db69675bfcd36d3163a8a086de6d726b3c85836d3d3e5a87a73b003459d1be5ca2c22b2f07d912b2c2793bf069707a1972f24ff49e5b4cd53ba34a7b3f99d80de59b2a35f05b0a2f5a94798f630cdb26caba57477cf0e9f224c726dae79a0cac329579f77a8e8f5974bf1c953c0bdb3cf821dc2fae0a430be1288d632d2214def5dba4a4720f096d3b847a74900efa4550832e8457de05b43094c8adaf324f464666f7f8e27f1ac9fe817d6017ffa24398c5b0d2fb83fb4409d0e1810f1bbdd7cd5a018a5065c3960320ea3ad98ac7fc6958e1a8687cfa0fc04c82b24728749d038d7b2a33cf124ab66e3674d446306849628d8c616ab927bacb0041a5129a3dda8799812741b3e30b47b6a8ef69219de0964abb86c9895437f325ba7c0d4cae23c673adb70ba4e3de2c2f06e74732b3522084371721a71a808f938f033c2801cffb05eb1f9d788414837b44c541f3c712e77a16af0004fcae18646bc047b887e0f9c0c5244e715273cab7316c9d2385ecc8b915d72546262142324c517464cbb3254ee762602a58225cf38d8daa96ad4b7b5439cf9a5c307404c00fbd4616320f31f5ee5242b1a8598932a689e5c92231a6f28d5088e112e77e3861bee69a5d4567c3f0500a47d207317131d3b121ba92c523b30533e8d9d8a4b684bc8b5b0a6c3d448216cd1cdcd93b13e85bf4c6cf4cebe04464764e71e7a7fbbdce2280f1d103d1d9691142fa42436ca7281326ea5182eb168e78c83918187e1807d4261cad0b75d6ad189836508349d592c7dfba2c5c5b8c2d465a4011576d563c9f587e150ddf2be635249502cc9bb0c714e14ac608769583ca1e368bbf6d30c01b88dd1f0444161e3bd16b1d63774ad8a2543a206adcce035cbe45c0de063780dbee931d9d59482e30d0b077fdf1b20c36b718ff669bde807cdc6a3d0d0634c45452ac4bf9b5e43390ca945e16e7c1ef837a97a8de0c531c5da538d80065b58feb4fa889b0efd0af013300d28e4d60aca7e0db857c95d156b79cf3c03880738232f61daa6f206d97aae23d25b429eab8b3fd6571dca8193f0a7c18851834d51b5f83ef1e8cba89304d2bc724f751dae3c22aafa4474a3eb17a7811e6e5435a789d943f12ecfe5f29011ab8587a6ef07879ee973f5e7cf0c41638b1b2bc3218ddbc6fe877e2bd5a5195940e0519a916593598669c09716027c8ede89e96f9a45253091c5e57879b4fdf5870129352f9cafdabac7c8b8bc11c704afe51a9ef902777c565c684cd8ef19a75cc2f1902b77d0a36bbf1f8bdd0005c839f4be527cd5dfa2b1b2ccd2fea728f5ec98699aa1c9c77196b87d9bff8497ddf4440839ecdfda391738e0e611c03652f038a5c959401601e4c221c4bd56201ec1fddcb2342d71077d5c43d84f8bdcfda7b69940d86415c7d17e4ca2af9aec6b0b3562aec09ec1482cdd1bdec74b7b8cfbec7d86ab95de731c96c799bfea842cad706f3458d185688777bc04510bb72ccf3ff703a1e3fe055e63c27542a2d9608ec344d5eed1d33e473e4c4e51d1402a90644aa5dafe0dbb10dfb4047f518a3144a01d67f2720cf037bd9d7f24d57638635f7b9aa7815af6d5dec89a6f54ad4eccb2884916263b87cad94ed6d7e934313b178e0a1b7b5af36b5268b4072be4de66b46c99c610b7b881023c73e115ad7a5a9f03edf4bf25eb9a49de58e824f6d11cfc5fcfdca567964e0364a5a328da65d4e8e6c1ceebeebb7df44a1cbed0c46a72d60e04f558626286c995210e1159b74f29e103b1c42225eee2a1e0b756f4d210c4b9c920bc07a0746e09b37320253e075754d8aa59b9bca0020c56c1631f6d9fec53256a63a14409d06ffe15bd567be012d657d6a9fca8d2f184474f512a38c9b81ebebbebdf27b3980d5ca66901865ae24ddcf7aca14358da06dce2f377f11a0ed9a550b546f479ed980b3967c3eb33cd02593dfd8deb814818dcab27b52a2273075cd8a06134259fbac0f75e8522e1dd16a63fd10c67170190016d6f2f012073b88abaffe3aaa2b22cb5963895a4e71c8d70ba00c50a753d8047759ced33965951785d6854f8c2dca5c7ec96e0e9b18e0cb0b41742cc6bd2db651a0218d25339d13d0457822a78fdc3b94aca5bac2ba296eac7c697f2c52688489b7352f69d0f4250fcf43c72dc55e5bdc5805183b13e4c9981fdb2ce0c92feffc00124f9e31a4e316fc3bce35331a961fc3d6892851e3d0ffc40de26cb809bef5e8d95ae48c005452a55d20e7ee94425eb246f203c98a6e4702ee413f19ce53d3f7b95d703d6dda7659ee8352d33b8e0190371cab5d026dbbd4093ddd28723371a3d6ea4ad4fb91c8b784060832a1e651c3517603d230c487b10a8726391fac79a232c8d72787477e3cc73ccef33fae5639bd99fb66800918ddf3e64804a0eaae11ece10c82381f37ca3c1bbbd0f00e8c494005a11691a4e9a288da618c5f2df303318800c2d65d3882c2e6acf642907ef9e7b44133968a097695fbf52effdba76db8e01bc90181a2fa5efdc2f737be6cc2550b90bc487d99572a75c7921622bb00f188c3524d4411d65f426971247d58444f141d7b76e81c4c666ad0c7fc762f047857844c0b232dd9df6969b44620d491452be1136e463659d0cae9c208cb0ae9ced4b1801c6cabc2c948b0362621120e1fea3edcbfdbd71f458fdb08cf614bec142108311b7d5709714f3cc3012dc2992ffd609cccbbb07464f31acf9400b66c549b02e8fa9315f8f0fdb42c9f2760b4837da24b7b3b6d64c50b21853b13c677704ae4d84ab985b28b776b16663c8ab8c75e6007eff6cc47a843f9744a95bb56f8bec636c15e64aa50924fa0ea3373ecea89c182dbd8c2596506c0d71e5b7d35beb68c910e298ca47ae3d57b17ebd1dda4971e9ed0005c77d370b21cc42bff06124e54475dc4b6b452a7b3c969f382d982d5e4db948eb302cb461a293ad7e3bcf78cd14fe93c5c95eb07d5cb60a949cd2e3375ffd01895febe127e8165b8b63f7ae5f1a5fda2313cff8645ca68f729fa434f74a80aabb327e05f4995c4acc10f1ba3873f64110ab713a4977a2dbea3579b9904115f2aba42e70362bbb7ac575fabf01ccdb6e40431ee1358bc7bfc31d5d226e7666ae423190a86cf464b39996ed626bdfb2af82a3b52ac5bd05e11c994dc7b8ef84103afe75ea24c181a7392e65f3feb6aaf315ee59a32ff5e6a91ab3ba11d061632f372efe9362148d9bfba7de702a2e73fe645dbd0c6bc161355c1a634ad888c0b2239620f81ff02310c0e76e6e449547ee8f0bcfb952b63d2e3cea8b83013206dc7cebacd9bf42a739104514765bc9e093d7fff538cd7b168f4cafbb88936c5d8fb3d063f3303ef01ca2378706af5c917e2dd59f80358c63e97b3eeface233ac5852348ef74e9834be11c398f367a80789117a7e3e0c7c5f57c4e14fb8964e5bee19d05e5423d96ee2f8152957d1fd706911e4919c80fab5cea3ca85b4f14f2a53d7c9f5612d9ed8027363d63660a4c15fe12e571af4e135be5fd8254ebc62924fb970aaf13505161df839730744b0cca4a770874054a728e0462f5a23c990db3e44877831e750a41fca4f85770c05dd4c23cfdeb04be181c78ddb1f0fa9c534eb74e0b122be0976d2ca8f14f10db163a1263eb9fb0ca0327f41bb723dc400be5d27c4329f4a28d95e47fff07aede851ff90946d69db907bf01a80b9e1cc153cf7919316b81657162608cdf2858f002486e201a73b0605b2731a0032fef1c2941d180749bb7b35112e9b38c2a7c013dc61918367647120b9d7d27799fdad56f78f0423df5d66be53a9b2aec1243e1e5422842ec17addaf50648f1fa1d04ab331fa5166249b8656cd104db715a0bb116bccb9d96225e8d8698c4eb70b37ffb5d796f3a7b1f246754cc0f93bf8e90b3ae37706157569487af20ab829a62634ee89de5e655dea78f303bb555fb700478e775e32e8df7792ad95222b1c23c3f3d0c30b9192dc5f1761f88e58b31858a0b6b5bfcef9577208a0c1a1b8ef89240b51b390e3dcd525148fc2023e508aba2d6ceef1906558d0d7d005b0d6c3fff262d1474b2f87b20543177dc145573ecaec7bfd5ecde4a6468a1c67598f77ef4e8ab16a1cc77ccebefe0546c5c8fee8c472342dec338acc67640f04995666cbba8f92ea4150d37a7b4eab1092f547482969d07857b2098e3df91e2c439aba056248c301760a765ec99ce8a3d1b2c02423cbd257f61331cbd087bb2e2e5636285d040a84b3ec28aa1683d15aa7c958d9d476e87c848d9980cc15095474a8ec0aca073314407a75675d4dce116b259d3776a4ea5a366d60238ad4712adba1361e7bb0c42f09f508548e295296d899d3c096d2f5f0a0bcd4213fa158e1f5bc3b8175b2b94398435803a00ba30024b9dc96d6ce420151bc7e54e8526e819a467d678717503e125197c1f267d907649277b990eafc8a7634a94e3c66d1935ed0df9c794a7e9ec31956266997ac259b36c361b4d44e915878dd8bc549f3e76fb18a9e521816ffda9423ee93bda87dc20cc2070e00a13ecf124026000ae15204f2c204b02d5a657c134913734cf91814ecce6410e60e853e4bf25560a351be439b090d5b0fe54ca5da6cf2d79a4d601b53e0f5361df3ae663805a9bb923e393bde61c122b23218da3c3b35c644305b5d1554499ac34fc55e21c909740f524ad7bfd8aa1562e76e250a90745e25983622a88c6081c4aac3041c823a7a40354b601ba246bb73044522d8e41f59cf0019653fd70452afb77e9795d3a8ef7b07c8b87caf6f4ce04cca95803ceb2b77b36df5de08283305c92c818abdfce8724d4759af11688bab8d596356068e4990830112ddbc71f351dde95268734b59e1781fe75237a6a0862dd5b0eae72309e2e573556ae80bb86f431ccbb798866072a9ce8e6bf860b7cbee809f614957d5a13d05d481cfb23d5dc5149ab7d7fac3c61ba288882dd487b4066b97c0a672103bd223811388309ecb49258e94278b7c404cbee3ca01bc41d021171f4d691c58d2070377067a131da06bc383f9085268efa26f9005771bf51bdb9018b238b4b95943abd29ca0f9f6b3feccdc5f035bb48566cc02c7d89dae5bf2c7a164a0e1f0dfd9a070fdc4219c9c2434a1a4be119afd705bd0acd474bde080d192b75d5b6349a1c9d5df005bbccd77b529d3928bc9ff8bb932b7c8248544a037471cc5873296921e4a5d20ceb430b2c662beb1d254db1274105ce20ca83498f24c6d1989c2f82bcb36e4c510c6b09abefdc36b0a9162e251aa0e1b5c4962ceb8c91bbfd5f0d42f3b42cf72ad89efed35fba5ff9901aba2b0f6f046a5f973d73c8482964ed4baf10a87eb5a1a086dc056fa455de531276aa3a55796f7599ac38187533e97e8e085ae495fd66ee332d7f7b5c48e45509d767ed4f015abb9c9ec3d76d0da9057222617ee0891720b01550e2e7f8c1d24c4785fcc95f4a3f1dd1132f6cb2441c2135b085d390d8b73148ce8c8ac94a058bed74f9c27355f615e63d6eecdae7b810f031be427d567b78535cbf00dfeb9f188b3b51a8a95a53111bcf2b5497816dd85639bbc395c44eaa118efb62fba3ec12bdf41fbc24b0f32ecde08e9bebf8bc1c30fec1802380768d76e91a2f4783862d652f8d7df3ef040fd69a525b9f5d06c184db09e35885c5fac6adae758da667e73d0293a2855e24c229c9b78448e5eb81ea84e0155c3ea065fa9c464309093b2233041cab9f3f1602eeac3d27744ba0c05fbe493b6b3d8590183e5cfe477b4497480e35b757c8263ec317f48bebfd273cabba69651f08ed47f38adf2d09faf20623753a60136d1b5e031be56594206ee818a87f78a13ef45e61b54fc958f0504e47cbdbebbc8b6db46f16209f08abf41be8d61f07f05f37280a23a3a8728253b8c3daac840354c8b7bd0fca22e05506882130dd83e6753d19a48356b42a728dd3291ceefc8a2220503cd8883a31ecb0a8684e490b15b2ab2513c451cc672a636968ade4a605136176227774a1f8b9f89279b666d8b1435891c28db28c2d48bd6147358184338cacbdfb2a1e2d675402f5e632abf501ce9dbb59e5d9006cdd31709065f5c253e155713ae23f03604a75f06f18fd41c0b57d357484cde8411bad657becbe737126d60d80c1cb41f76803ecd20657607f43017907c005f39f19dba1086b26a05119d2ecbc98d79de29ccf707b45b51fcbee27542c0faa72dad4b8ebd9453c21c509806ba2c20edab7f345b6fd045ae4e5fa5c945fee8e661857cbbf6d21568afc0bb071ca785605ff55db68fe061770f5f6a2a90da105c9f4d4c05ed6dcfa6066636f9d111eeecae7e6db2ed73be5906ca541d286fc7eb41bc47b1b997d9bec8abde86cbb1c9c2d33fae4569811f0df273af5ba54c2fce46b24a5f2293ed9de3a83961150c900b5171e40355a3b9289c10dfddcd7484fa2f9d821604b1aafbe658e0cd9cb7099a701cd3ba7469feb72834c102e73c2856a87105f540d2205fbb0755268fdaa1cd73abc9dcf10fab1761da22414a139fe00d822dc0c086f84f758ecc5a0d95deee54b2e7bb38d2de66cd45463c6942c487174cbb884bce2e371970e463684325296eae6fc584ba68e33d1a80a1bbbdb0b2d47d10719ca5650f9bef314146c8f70c0732309bb3af3ed3d579dd03f910055bd75d58901fd12e2e0b016cb1730b4fad1f192a99b0d4b518ae0b7cca1d3e5bef8c394a71ee1de5a11f579302296922d03b3c16a90435600130d59102f2505222c574a6ad9359ffbfbb6402a1c8baa30b87becb11c45da1a5533556a6a2cb0bd894debce724fa06a5b57a32e3d97ec6a7b3240511c0cb781397b274266dc1464cbb2619d5feea9d93161e12cb64e6983d9d0e5dcb3b467af42e65213d056bf9c979ba2f8569e75351658cba915e30f6967ae7537798bdddedd65e0c0dc5cbf7781b6babf0bec79f508c8cc4a70d86b975e77635d0670ef7b320bb361339a1a5f5ad33d2aefec89e6ed3c7864eb94cdc364cb5e09520008f9ead5809e797b0fd65298165849c9efd8e11564672e73b0e351642f6133669cf8dd090a639aab600e9e74b3169a6ac2128219f6e97c89f47af520616b971be997695b736949087d689b0d79b8cad709c7c62ee0cd9de85ad4fd9b14719ef769dd82d9282e34c27f42647b639e1c1276cf815f672d3ce5b00e1ce2762bf93ab2c036e18ad84ea553b0cc2574133566a847617a1fb528391385c541eba4f77abf4772206241762bfff40185c5a8ae9fd9915e15a35c5a5414168ceb852a28f605874ddd11a5c389527639a385b12594be26b61b0df7c29f95286832905209e35ece62b5abf6f5d7cc233c0e0118fe6ce5eeb5bffdbb118e0def1ad102d3cd3a1fa58ae65421f50e50342a5f9c4deb487f9d3f9be68dc346ad24af41394400e54f7a5ce656535e5632ce9e5cdc209a69bc505dd13dbcb238a91133be43ac206d4a3d65d42a1d87ca6e9ba6ac037d695cdc31fbc9de5fb166c8ca205d681e8c3c088b494d4227fb446288c001475770187f95532fafe3f051829d120fc278746fc0da993927047e232ea023ed657ac3f2fbd52756c79960011b188204607027b6c63b88d51ca3734b22868f82dd5c6cf60f6bcb8dd204e172a6f9fe1473b2e08a62ad8e20bd6e492a9598dcaae8d6a940fd6ca3ef39aea4422ddcdec10ce0a2015015deef1c6d68df89b2f3bf281c412ef9679f0f6aef7dacc2d1b146059885004a5aef64241db8322ddad09964411555728760d0af2f6e9aa326f2654922c2c5a12453b25092c0a5ff01118b68511e3085ecda43b924b104d3aee4e1534b0aee0e519b69bfa13b4fbd48f6fda38365c8337fd608604d7a77b41b1d24506bbc417f1f8fe34936d201eaf1c672ca0725c594a61fbae7d4e8c0f538cbc934c986abd2954b1070f8272700f1127c94293d899d6a18232b3726e170aa8e4e51ab529f5377c9907f34ef4845d89e16b0f6e84d618c0e1e045dbf6f8a2b2b10f185e60df9495f1976b3aa9ab8262964fc07638b1800dd6d0f277ec1ecf40453167afa9612cff2526090e7a47775016730c90ddbb3d7c0886b9b6c9da415e57ce5c583a48ee45b641fc6f717e43b20b03f02dd65a5f6a724001cda004b43335011ee3998ccf93525d454df192636ecfe9ece0380b06cfc0173806e671c1e05804e8dc18dfdc6f6f42e16bc87e7b47d632ab0fb1f445c5e009dac9a1d8a7dc99b2e0e8bdfec70a6946f806402879bfd39ddbfca385516d69579ffc736a68e36df2b5339ed8d4c68bb4b60c96455cdb686f2b8fa0b166ef8ca665eeb0411d07dbc4607b3c5bb0b603f27bf2c7c8fd7d700b54102c94c7ee568be7f7ab37c86adab26b625cabf2b001c866e5701ae7c042bb76de9ec1adf26479b0227bbf5d140b5879cf88198a3e28b336dba6b359c4dac2e1287a8b3de86a96dea0d0a1a44bea3af89e84c58c6e2dd79e175138f7bd1898894deec67a355018f910bb7c8761a34808cc0dacb7d8d4428e0f325326eabc640efd1637c8fab004e3a1e85588ced3415532b4896b943c25e449ec9af5972cc8a073195edb342ec89819bd1ac3e2788f6bd2e1ca2df66892e2c6f840e3d3fa47329dc2945cafc707339e926ddf0a22f689ecd08179c45300da0b7761e8dc3353b4c01c10f1c88338f7a39b57809de5de4e03e1fcd7861fa940aacb579bb7119d6911d2613815f9428a703b870ff8b91535683a42505d8c76c5736bf26ce32d90f20b4b2e7a3ff1be51fe6bfcff633cf515022b192202aaac8afcd206f1433253ac3771315ccb90d411d2d36a9c0053c82afbd0b20ef5a9615baecc082ab4310c8614f0d8f893d6d3afd2c60db3a9b0edeacd0124799b4298036aca5c20c12ee76ba9e8c5880f9106261ab662d33d0ec19edf11fa11c868892670d5fd6a5f2a18a209d0cc74a7542cd1d7e54ff1e490f3eed2fd4ebc62a39a48a040348bd5e45172b973dad000246a8f612bcf85388af203541f82c26d57b54fb1d6400e4c91b04ad51a6deb88123764c6f0e8e685249631940f33ac719564229bbd65e7ec872ef3bb2c2a299a9677cd7406a57ccb9297bd4a4e19430d94b447e30ac2d6dcf863ac3b438945f4973d4773dc1c5a8889e06be07fad8732ab8bf770ef9c5d6f0a8508026e3456da3aad56da262d0eade6d98edfe81e698db35e00dc777a2d896757b4f5fb5754dd3e34a0dbe5d66ef249a83bffd28ed4cb1788014e9ac3ca8bfdc0f86939a0c542c20581a8cf7302f9f0893ad7507f3e70677bfd747c2c2ec593ca4d0c281aad68ff5008646d447d0d87b6d58dac5194ef94793c51e2cdc12d607ee42d926022a45dc746a4b806ce02d851078bb2b7fd7a66a7437d8e990f609a81eacb324541ed194a9f9f699816e659a61a86bfc9f401811c5a5e0e8a4f9423c8e386ed765487756f797774cb4db66c344dbe9d1a19fc7e95bd19a99843311052b3d14d6c6b83a06eae3d01c19432024895a9b4248cf10db54f6c8f3e169a605418c4c47b4ad842b7f0666454c961a3727068b10a22f4c76f9d022cd076e5e3367a5728d1bbc66bbc27e1b66a8721620a19fe7a0e62463f2ed21d39bf849748df862187e16b20d9a36057ebfc0bcd7c84524476e41134b691eada6f6c03191b1ca97a14f5ab6013c8432354a7de8d076c989a0f196e561e97a99ceec3831ea8ad8b2093faf229b17a20475bf21cff67572f6f3a15d38062ec59ce3e4fd35a5655fce087a39163167c26c7a22c3d8d76af1fa2638c81b003d4259aa9b01f3303b38363a9ee58eb96db9e355fe533e2a9018b840dc257777b66568fe6001bfdf6cc1f12cdb735b2dd0c40c18ce4e821b95f9b508aa59694c3347e162ad0392eecb5094410b2ab81777eacd92bbb00a1b96c245da7bddd867fbeed1fa2889c371884fbf512ceaaa9ec0b6a14734f6e7bb6d87a0403a6167793c4127a590fc9bc942955ebfa46c2c69cf5471835e6f821b0c90e90f3086fc01f4dc5e305f173620de8800c6153fd43e75c9fd8246621342d4f48672202426f9e8c6b95b153ee9d4c7e43872e6fbf5262faf8224d0cad8a3d946a65bb59819c526cac0eadd7b7687d80a4c07f3e0a44594c8102e37f3f02dc7158897da04d0cea41e6428e4b0c7f7706ef9aa81b1ac948eb40e83be3678d3c40de8e03114b61e46ae31eb2ef587456e2981b720a7cae9a5e4fa815e8e9dbfb94962ef158af18204183a46aa37ccab9ddedcd0d6d65dc247d2b7406a9a0492e9bff57b7aeb09bed41273521ad28b0beb9fd16662b089a3c0d388f4df4762c39a839ce3d17105d9f12681a0a85454310bead6d2ab120fcd2c84a8a79dc16a1f6541b832810bc71cb42eeaff17803c1b07390261cfe64b32e0fd463a3fb84994a6485ccbd924d7da5c688658272883913dc8d7bc0e9e734602325c66fce0e7e6ecda3235e7fabb1dbf796779a1c63b548ad6697896015697e60155c93f24e4797f04270dea21c13e2eb3ae8d564497edcf439f8c362195d2f5799364743df024d09efd333b2b789a60796b20a1ad4f57fbfeb0dab0db86deac2cb5e03d981d8c595cc8dc30a1a8a6478c0b604f6f8dbb58be0f0d93504f72ab248fe5ea7a3bbae5082fd2997c3c11704ddc099844a5a651b0e060b99a929bd87b8fb037c8e9d4a31634f2d00742bbcb1c0a329d20a48644c8210f5721c01fbc1a9c5d89200a02acc4b3c129eb1bc297500558bf0100bc66dc843e2664586ee83ac1f114f17d4f84763db3f78b2324a13a78331ed973c7faa53088d95ec59f2a8c2d74040512cbb2eb2cd975d672e8c741144cc44105f202234591297998a1d1d38d2094d899e368ea86802a4531a726f0def7a0e6db7fe89d499ca2a8762245d31deb8c059e4df2884a28d1e3896b67884ec40a10b351957a6e1cd43546af51f3cd3316d3ca1c17d120ad6367066d4ce80b248c0c89193b33414659074834b4b4476454628e20c8041c9bb3c7093e00d3e5606c977153dc485757ca8c2b0730b6ebc2e4c135e64846043520bc6f78b9b4f6c1b37055ed63181485806217a979a1cd0e452ffb3449626d58bd68da3bb1eaa45ba9efbe074a1e71de4d860044853f15c15e02326ed40ca7ac64621b875ec7c30c02ee633fa2a0d796dd00d3206871e2abec6f1616a06ea49838eb816dd7acf4a311927ddf7335b849f31288032be4902708df9da42354f28e0ebef7e460dc3f5154b5b69b8c2a5fdc8d90e8e20593118d701b8c121bb0e7bec491c6d05b92af57d92e91507d546baff499962de52ee60ababba775bdfa0fa81927314901ee83be7c5b173ee1a53c038330a34a1e9c80f71bf27c3b637a1ec95a43524c919350bfaea82c8fdcb9d3dcbbbbcd49a434de6dedd78ec51401e8c01efb3683d4e3df29c5238048234dff205a0749f0841f02c54a07a5d4dd159527f334a02f3274e7a8d37b6122a31f5a5bd0c91678831f4558657ad5283ac5e6de1b395a641a9bae7a320711720345249574a420d7a16f0e0002d08389d7906df078a375f98d0612f41c968189be08210b84078e5f6d55485e841aefa48d58c4ca0f891449e66c2e1d6994d6631df856ad2d1d2382ecc0cc2049f52772b6d11bbe7b1c9518fa0f1812dfd8db0d55c783b285a0fb97016a58ced64c99c6d29cbcdf5a8af46d784a461130d11c83a4200e6d170fb39ff59c05216217104121a199feb3bb1b3d4e182a33a5b4acc2039e68521d25cc27bea0962c78b00bd56e6ae02d60243471dbf6397168402352f28e5af6d50430573db630e829e5e06b68798d8d0d00c35ce0ab93f82dc2237fd20a4cead15ecc920ffbef3cd167f268595a8c72f1f4533e00ec440b20711a53562ff93de758a9f7ddf20222da1c846b6ac4991fe7124931308712ca548511bac64d382efc0dc3796b2e844ebc394b071063c6c198f0f5dac53e92ccae9a9ede514f6f1e26dab8dcb21cc0c0c3204df5c305d93325352bb9b98af1cb648712c9e2f6d01bb7f6f0297723f97fd576213669283dc70ed27e96949bf8deb7593d0fc040ce718ae0b0a8c49e22178c28ae06d95f6641c9056958bcda4c23da0f8c38b15a5cc65dbf9aa1ca15a5c7e308dc78a8e7033356a561a029e8bbba73eb3afd3ae3ba076e08d18dfd6d7f0cf9c5ef19e26e5636d48ca2cda3e270acef74fdd73009d92be90da9024d17819396c223237eb459c683857ff023cc689483aaf0e342cf41a5bacd3d0742c0ab261317e9abda56a10267a569655666814fc4e50968506936092c17b0e5c514d0a8cf8a7c237abd4c607d9dc2a2bffa9152ac0c6d15f350b7fcf66c3793710f3fab10ee90a6a780d8d6c65c38528113fb8f18a22db93f224281df8ff9ae7aaded1d3bb35bd65288559b744aacf36bec9de086b8b9e3f593a7f1b10bb35cae92bc59b73040a0ff91d5b1cf7c6138d1024da726182b8ea8c41edc369dd1966d1a7727a6c43f4a8118e86ec5fae99170a6694b87bc9432ebc6a6186a3ea985ba3a88d80a655a8705efff175a6c9ead042aa09fa40830cfa68cd5cbc22ff4be95ce52d677770ae2416f36b95637f04be9fbd5cd4729e34c1813b2e35dcf41531d9feb4e8d951b1ff6965b745b9bfdc4f685caa532bf016822bbf24f162e6deadada15c68a881739754bf80a8ace5d162426508aeac4f521b5b84a081a4da086a05f7cc48b11161c3ca96ab9f28d66fdb3cf2889453063f148968734ce384685f3abd2dd1aa6b15538d337bf683f03a8236dfefb93b23539878bc57ebdbd721b107cbd5a59b70696b1d92a2dba6f76cec40a9d082e47a105b6c1d708bdb84b5dde4dc3efb6fd3adbce7997d3c95d2019581178e689510dd7cbd352bd3b681c2f3eac456d8b3f1dda2db1ddd5e2e4091e7fa4397c383325e722c663fd619e5c82b00ada84652affa9adfe522643034d8b51f75824adbc9804f9dd9b77bcd30494901ad1937af3db8727746123a699194d9de069fa94e65296a09f1e43619e38ba96f8c79d3244f7bcffa61d14de7a0fbfe485bb8af00d9cdc1e1cb23dfbed27467d87642b6437927b302ad44119b2aec2b7d2560b7c6c8138f35ed3b37c007dfcacc0eba45cf9349017a5f5a3318e039dba54c88120c12db3858d8335004c148d6c43fea9ad54e6d2ee86e9e46fab4626bc4c655637a5242a6c4e62018f63226259ce0b60cb8c0c988fab82a2e6bd5dc757bdc15296b652ec751704f86b1955f233c615343f071938c450e41b1b8fce4f3dac3b064cc287358468a5f1272bb8e4a3c358393c624ad04c4e957d4f20b121ea0dbe5e09a1a911b481360d8e6a3aa8fadd40e46acdefe2d0100a93bd7275cd0643d0ecef6a201b53bf9d5e3455ebcda4e2ed0a791329765ad743aa3dc6f04143d121069c8dfd766ef4a29edcadcf522f692848551b3b456f431bcd7d959bc38b08f20b3d132944615858118e22c80b29ee7e17fc4b013fa3d8b4bdcd3a9a6826b046fda374cd9d25f3ba7ba86217967bebec5b1f289913bc8ffb84bb204f1f1dc1872161ff53aca2d911f2f0222b782414e6406d144d311a03af324ee099c6608870a8823e558c809af7ff26f0bf6bdf84f7e40a98efd3b8448eca2f3e6fd2b0e2c4f0c0c53817705df83a2cbc0be509ec8dc0606512d468ddd3aac39ec411c54ff1572db8245fcdc141a5124ec3c2ab4dc70b656a7df7d68d5e3d76c07c898661549dec06bb392cf0a9ef8543c298e8535526c4dc006c93a2063cb2c6c497bdca79a08170452f0fcb474670d5666900f8506d540d7f97417fc0987b4bf1217605eb93f9232eb862f17cbfcb3c2893760d03cba5b3fd6887a41c9e36eebbcb0c0a4e3e8303243d9d79e5bc5b86b0b5389118860d8cba53137c720e066fa8bbf6cf2fc28b487b579ad8ff7c722ae71ba9a80183f48c2041ae35593ed8a90e0f2ec33d875a84556e84e50816e96effbc2e9597798eaf057df3776e3e7a2037271bbd6fc5278649d8219bcaee213376d07c4267c4f694262bf008462320982d3dc801ffec6d417fc6b38458ff1ca8ac177b0e377129d327bff245640f891c1f3a1025bbcd811470e3584f0f420b8d30ff19194ea0ffca71f53aba1b43585e6f3f9f334e14f1cb579f593b8a38b098201ba283e83c3d41ddd15202a5564d4536b0314ccf08dadef7f07c79146675e010e5d7c071793426b1f00d39ee9d9ababe0cdbfa94e1100b352ab3645c0bc2124ea7e28e208b866324858cc11187377621d66cf5366532e64bef20ba764508daa47f273ebc86f231fb3ce85f8047349b02a6a606396a8c2ed18e2214ae31e8b4455c8b2330cdc0478821501cb586c85437605cc9c414a1d4f07ac5508abdd717e9be7c8162b3b23de7885c54c3de6a315230c2cff9a7421315b07858703fd4e52bc3850cb9099e32d33f072c49585c8409487209f3c4a5860f044cb8ee20a17819a11bf5cc82fb8c8f3d8378c716fc65ab894a3b36f1ecbdd9976740bdd9986be2ad89b92246350a0661f6272d2d18f39e94aabde616c88f0f2a0f714a1e1737ab2cb3637d35ddc437cfd39f10e996568619cdf0c1aa1f1190f22bc04bb7e90795a32deab25ce570c64fd414b8996f25afc0c21b8aa55648b6ee50915d6c3f2ef784871e5ab90809eeff5b6a9197e276b70ff09bdf764c525b085d598c6f95ecc40d112b38e23de1e30cc2d8b88f39caecadaa2af859ab0aa2aa1a2e4622716c7a32dc858451aeecc09e837985d67ef55822463910f963e6738a6747e176669bfd343faba57038e7fd074394fe42035b6833a0746cb028a5c106220d74dc1950b9e3cbdde4c1106b059ea6803fda4b00ab1da85ed9b388e159d11c0e89bce281390ef097c96d78320e8e68f15a5ae1177f731fa095252eec6ef8690630f0af9bf45f63cc26321fd79b45c0c88f0da1d5e206c87951b503a46d91bd6fd009793647e344ee5054ae73ca302f6152d03ba69f5403891ba0e9d0acba90afed64c87bb8d84c9c9782cf7cd377ac1dd807f4e867d086ceabeadbc44014361eba67da6840510fd2cd88c28dc250337a40816072515053e6f90f0df9275b78e75286146c199c2908be8d57093502d8bb2bc5f88183a037dfcacd1dd5426cdeeb7ade2ee4630d91731078bf0d816e757d688bc4201fa2a541ccff77a84836a56e0048528140a79e09732854ee966f3659716e77a151dcf36a16b436b2ba8108f9b95a552ae9b16601aee63c48b63d2354df4b7be0b2e5d501e1ff08658b0d46ddc1992d3e932aa5f8b034a237e91a8a1b8cf57b7cc5354263dabcb2d99dbbaa6b892f4ceeab8ee2c06da373b2d6d56b450af98ac7fc574238f0c9eb517d6555f2ecb1a6cbe5e07e3f15ce88eeacc33f247d27cac7c8ee415024e36c935da6567925798e6107af48bed45e67d2efc0c78db5156606711abde288360dc336fe7be904696e8d534befa0cb00cd1d6bdd947732830f7343c5aefc2060bcc02f4aebbc21cfa542913b6230f2400e55fe00b1cf059745898eaf856fd20d0931e72180990c94220e1f2ea4c130a80e5c90aae38cf060aef1b4f95557c1f429eb8c083e42e3a89298e094a9b7913aeeb9f814f5f412c0733cec7b27c4121bec35471e1e8d76d70825e38ec8b43dca8cd09ae9ab263445f6575f706e3faedf3bdb5be87a7d1e99d3cdb9e25cf3b5e725592851739e620fc3d504a97b66245c96163d993a9b357b1a1b70da97d850586e6cbe45d372e2bc0a3f508a50c44fb848497ecfdfc39892f049140079dc6b77ec4ef17578b7fc3ff3f69ae3df6cbdf1241b4e8552bcd34c209b86e9f6b1a18bd36a980ab290ac9304a00f2da43e9884589308d21c1b41efc3b9f5164d3e41167691c24614d51de6baab1e49fc78edac4aa138413bed093b739bee16051f25021bd3e231ed1afcc9356913baba85015b492a3920bc75fe09d0711b5b4f31ba1e66e0751e73dea320018ec168e545baac57513ec722c237bb1abec47432dd97f37a74f7c563ef098ec99fd6890e005877fb9dbb13abb6a84a830c47f2376aae8d09aba7b0730b7246a909e213f423a49dd8d7501bef4dd7808cbdb19d0de9820f3aa1b86ab1c7d6f96a4e76540d4501e10d1ea631daa675d5007f2963c769705f90ed5fad2b215cf0b06b7a62de7dc37395ef0ab241feb39b91f1ac1c3968c89c05d711ac92ccc57479fac1a528e02131f5d550201c07b24a119b3a201c0aa203517f4367a1f22a84c387e09d99df6538454b7d8fec6d18595515cc5316eaee12d3b6e175ecf54d804a11789c1dd3051b1138c0b1b0aab443d6410f05c45d93ada95fdd3345f8da9fbdb1e1ec4340dd40f55ea88d1b1c7d12ccf28f28b92b8e92ecaf7550ad55f995a72af5930e1cc4290a227358946fb03ba5712656db82cbf68325d958fe787dd3f9583bde1f6af8f552e3c4a8d8e427e000dddd36f3f1d5800657ae7b4dda6808c771f2f56b3de954d3d51efd9f7c983581d9fc0a54bbfec5c200eccea840bfb961a7b7f69b76c13fea8a4460acd9c5ba2d16e013cf65bea750a6ba2184cc41a12bc2e3f0fa32ddac57e622b1fc467acc88b09b51a9df944195d0c873103de489ac84a043e5d2ab5bb668000f3e5a6ac56c6640ab0661c6a7e707487b316fab93d17e9e639cd87911bff06e820f75674db1613873878781169dac2dd761e682b31e25cf0f8c2488130112cc9b464155fd168a71b43e3549ff57a6da470f2874838b8f013c9160dd205ada3cad3f9b4373efed709e11dd2b80384a608b8634a2aaa53f86cab28926256b0dc559e835d785447411bfa29760867f9ad4e6756e046229ad3062c1ba0132f6046ac9c23da2825cf9a2f10d1dcbfd514874cd54c54d24b3ad149d4778d93dfc51c97c8ad148d64b9cee9a7e74da115e620805345c552c9cc2e8318070c8d969127d178fa3e0959ed5fde416d1e0bac0735e88aaa7258085167e728e039694370eb8ba17e93472253242ea80e7978b165a5d80673bc095b93b7fa1272344816e3856127d55dbd206846443ade1e823814aca06c0d79a684f1dc8220e98a0d54167954694d41e43a7c6333b97711e7f758e6b373ff9b4d20929599f3dcd6ebf58d04b43a17726d9af5e343b9af3d1e7a86a8aa739a20f6fec104846cf529cd7fa0a2162d8647d510f2f806624211df4519c639078d8f5f287c20ad0fe4f84344fdba40a287596bbe9ec4dbcd2e65c05a771bac75b759aa0bdcf82e8cd88ba2a5e929ffff5e2910c9845b63ed78a2ee13f6b91ea89876c5bd2138ba63c775619ca7531a05096ec9019d6c932615aea390e90d6ef7fb23b6078ad5452908a851ad134607300d65c228c14ee944d88b61a0955b7c481edbb38cc063975b6b22726342edfac9974a641e38e482df9925b5334b147c4821dfa425b0d779b5099599cb9a1db808b9aea92e1e55776a8e41e99e212a1e2200d0e15599642d0ba65448852838bb1120eef8365dafda85a64fb128fd1adbb46cadf1e9a693702441e94246a693627b6fd4861f0aa4fee14e7427f52bf679df175bcb530633f66def5c0a57b33f928d3454299f2ba86bc4f216e495a2f2856e3aafe24f4e16759f39766115109806d9094a718e9ba4e54897b110af3b62aacc50e98a11a6764316ccb4e5fa9d830deca7fc84db54677e1022f4dddb32fb40e753ec46a1240930c06d777fa03e159aa7faf2bf45ee1a15327a989e0f683d790f7404fc5c1ee30cf16004899a48701d48925edfc279a97002cb86b6ec90920bce0f091079b09f3e7774b3da904b70532e31cebc244c015f1e5c7fd95d0763b683ddb53914bdc4e15cb16f1f6302765a570492c3470d475e012b5b561fc3e0c4356a6969d33b3fb3a2e0129cd57580a557d5f2d4c5f4f87d62d75cef83d60b67082501df82182212b0019511f0542cbf1f7c30fc2118a837efb120ff3c86888273c3d005b0ea1df1120309b953b86da3b94bab41c404d4102361e4b3c92763b9b814983373d5d4bf1152052da1fe41a765f2aad6cf342e86b7a80714264ac81682b88148d3a1e7c18c6e7d20e227ab248bd30a5ccc3af1410acc83488a3c4b89e017f18a7de94d515765a7604b9d7b038b0e321b41740e74f25411d7a16fb806bb2aa1d90bab52c989023f5af5a3cd6bd4e5fbefd62f03443cdea16d3768ff7c63624b7f4c4d45e5d188c2d4623336538d7670c536acd607cec713e855c501de1d6e0785f52876af4b3f910408634df12e4b3f865f26675d7e392554db03efc7335c460aead5452e6a595ea6ae20dac3507fd0fcae0a145d5d9b3a7ffd466a298fa9e34733942764a7eaba2c9185c2892d0d9f061105b21fbe4e55bbabcf1ac5b7e3b777032fbbbf8cf09c3bb3b99c7c98a2905777a1d8e72c598f0f281997c9c206cc547794047f32718ff84e672b8c9851f61090eae7970390a7faa2af683db8c8eafe6e96cfb8a33a0002e2495dd7da87b91fef0f5725c2657edd0754750a2296799b89579c113947feba38d46372ec210a4efe4ab7b65d47198ad9adbd5c1acc1589d20a25ed5301be6c4d72412983888d60c10485584a25f7349450cd3e62a9e34fed3e8c3213f7bd756214d0582036bdd23f68c9ffaa637bbbc411e05c5620ac34962e7321b1c6c38b785c0c9e458e8505cb5e7f03b2fc2b0ded8f795d844447eaf00bae397da02c83fac833ee6ad79c1923dd2d02bcd56e8e16d793ece69ecf4c261eeb63eb1261fb480451d97753703bba35fd1d394515de88f50a5648c097eaba2a5491b4d80f48aa7db34020753b5ca7d5629f33aa0834b83d68ff4d8ade791b9c88887ecda3ef8255166145c8b86a96b363b1964035cee6d53eeed4086efe3ffb7ee26f2a84a319614af5df95a532dfc8b4a09a83b673b09523ccc6cdc8a30af30dd978de48cd3734d58debeef1ab5d286764b223840f6be8cc333850cd51f221b861a04e7d7fcf3553fb26d9d44b963494fa7e6546cb2fa366f3fd1af735e9d726e2b94a98e7ac8402ee204c7bb225fd5422621642e34b5b8c5c5b4e313d5945dbb802b55fbd7cfc820a4ae800dd6fb836145f2f105295bc7d54268d11c43b29625773ba64f633357a9f34fca8c0258f532cc3e4b252e1a0f0905173b23dc4f695f006f9aad9c8104f7b2480cd1724dc9edd7ef304bc3a17fdabb95c6dc004f401a38186378c134aff379239defe76a3e28def7fe0581ae5f03ee1b5d9035e51fe2e702e53df568fa65afe5c9a4fa3ab7a362550abf9c80f7a019bac313c7b6441c5d31287977c39567c0b998639fc6363c1655bb1ebaf57d1b865398b2fe334933e7f55acf4ae2726231a3b9d4291e374d9739722010fc6dbb13ec10e0fb287da1e73b6cb49f8c23b831eea9fbebbda8531cdb63eaf6c63138fa84ba02b81eb12758920bf16d064639889e310d8b1b2c7c4fbd886203ff37099464fae85ec4b2f5351e610a312f27785096ce96522d07ea22ff0bd0629cdae4039e69dcae4be3661a2a5ef35279343e5526efbae8bab05ca2cf9a10ce91b2aa9c47c20f73b2672e23d114a24f5803f85adb7f96072389ee175e2d4127ec95ed05eae72cd4066348382780aaf5a69706533dbd0decaa85e136a49693ec974d0feec67609354f9b31d520e3f691b89131143b85357b67d3069dca42b60a4b3c5475327d6d43fc57a81ee4426ba948faf064ca48b7d5c90a511e4f64aeaf8b6c120525f14bebd3a6bb6aaf6aa9b2489016d619de7843779af3f60195a2160f090a1d1c6c8c4947aae57da135f66e154cd1ece0939f0dc45ae87024edce10693f72d1902b62cc78eb4aa2f29d18a878f1f20ce8bfac9c3dc984b14189f1f06683b999018312e7979abf9d6a790e0add24042c79d0c478258252d7e147f6953245f8ccff4941059ca4f64ff94ce9877727450112c907df52f67aba93c90507744f7d674e2764a039ace7cf01472ec1cb4f662e21bb1fa9d66af57163ca263548a3c0ce6dd5a89bf5e2e8c3c1e36f39078a3f2b49643e56cb697b90aa1817f89bcb740284fd29ac65d03217965d3f3f9338c1ac173e0255cc5b3207bd65f864f826fa71a7ad4c4e5e38f2a6c5a2a9c7780c85845d147800403f9dbaa5e70c76acceffce2aa8317bdd01b4ef024edc661ec96b484692b5605df5e9d869a2e31e0ef3d4a0bcdacae97f990a21a3d119905f82dbc3ab01b5266b9f4dd665668153cb58f3ad5bc0605f0023f12514a29f04652a28a1015044aac004b9da608abbb7397160670b6626e25ffc2f5857327801887ac8cfc20ae7b4e55f1e834ad81a14517cfa8dbfc689cdc34d43f40529b271659b770c3f95a1e44977fb1267f9ec3bbef8f794a787807484f29b68b518cdb9b2842f8bad337dccd18fa8d89f74a2c1389d1bada41f32f2fa19cab09d2f865479649e3c80d2b1078a23f5f2fea7072be13b3133cf25ab6bb7df6f2a8c8e4d18265fa4835527114316ed11bdcf0bfadde7b5ca5af4dfd15bb103f433b710af76c968dd9e24de5d520958f2e19e95fad5b8f705b30909437ca113fb8fdd7f1bf4a3707e41472163cf662aa0f6eecc396ef33b6f27f73b6df3a63af7811cd28c86f94290130f14b135a857660ae55650f52573c7aaeb1871aea41dc63183dcf73f622d6e04ee75bc53200752df1791b7dd2f9c943bac70c2edac918b32be6a029866185641a4322662c9ce70e0cc017ffd3614dd8e795ce180bd7d26fea5005625c37558af10c80c7969d1f55388bb5d50fa304d5c8efed30c2c335017811b2ba5b8a0b9857845934631363384c404840d20c552ad56fc8bb6d037e1cdcfa32af8ba2420ccd0cfd47d2e0ada4b41b7a85cf0e08d0722848b19e5b13be7b8ae90cd3934f861c2550ee9f530ddd5fe44c90b4d52176ab980f6d42c9b9001c9df5708a58d26ab82f2a20a625c3d382c5869b0889a284ffbbd335c3166bff9b9fc2082efffd43684cb3d37d787a4f83e43afda4e778fa83499b9e9ba7f7122ddc9bfe6386cc372d32b39ecca27e69d6e740d05ac3bed5315eca03505100b59b53810a4d7a1ee50082b71006857d13edbc31fdffec94cceebda5da4231a634a912547f021e19283ea0f80664dbdee012f70cf43c70e8b36834772387153163e54b33940a24d75c810494c1a6627f092fffcb58d467bf60b0926402d208d65fc61896371eacea2428f098a74ed17de6daba12abdbdec5a0d7d6f12f47a51762dc67c7a01f8ce513ed41844726cb203624be0b24e169e5311775dc2a133d4f5117e00a669a7db2781eea17ac600f8aafd0b7eb7062cc82a530ea66dd40c15ec72b8582e40dcf144cb06d0d9d1843c12675727d6c40c2923093cd4afe499b8834e3d63f640be43f0932e7d7562ae4a57126ebd1e79bb6e5f914a7997b301a0c7b981054457718f8b55dd4bdb3091a6d0ca0c1167c8e26a42f0a93a2634d6173f3f81487856f39c3997af22c3cae6e8da8f1e3b05b33bf046cd7d8dfc6b0e8b3e23d2e2a3dd5722c8ab062b72e16cc21f269680628b33d917d8f88b9bdccf4d01e27ad01c9337c72bf2f131c56ad779f4d132cca9ee336a325402eace7bb33f0761e74f009eeb39c479e4aee1a4de910c8d724574dac448ae91dfd39b25103a42b1d969c1df57b806d16121186d4228011c29adf817385dcacd2f17e940f729ccc4ec51ff4ea56802a447d7ea178b949a3d912901e2251f403fc3cbf2fdc31a52d73e71711e32d4e2065a652d0ce4bd6a0797c31171774dc5520b11c50db557823e3c57583a81982556246d30c164f60d10f1080187861512f5a223bf0af89ed78d1173118d13be6a66d0252a72160cd046235b96928cd023304c26b890c29297e8b8b77766b420c2a086a38413d10f37ccb3af1545d69e3e1219b9c2edfed942157b6a5e5333619630c03722bfe9b7dd6f88ed1b8371063109c472900add4198e2f242dfc7d8c06f66c98e74c816a7c8caa24630e49be6b4972276390c19d2e8e60f37a322b9f2457349cc6b50b471ba175d03726dd5d35cc7dbae03922aa64ee8c280e8b6c0297a6b462a2e9467bf9b6e4326fab72767327dda62145f4ae8078eca29e90b6fcc7c0dcb49dc8018baaa6153f81c594ae322cc0c976b5fb68587b9bf6dc522a0de3d679af17c71cdc05e5aefcb15c7be674cdff88a39b85ffd3bbefb712e58dfa67df97b21e5e08d95e6bdd344c02541e61ee8612f591d35443dd76e1d41133fe27545094f451d4aff708d614a86c069603f7a5535fa75c765874f393b046ba9da19fb20109d33ca7e1781f5d3455f46a336dc5b7580365a092ad6d53d6c759b388e4e98857cfffa19728deb72e41130d2f0469e6867c9d1b501b72156a54f5d88f4b6914beac3eb98b78c0d285ca2ddb71ecb874fe030a8ba2825a30ceba4a07c00fc1a7bfbe1845afc321d2019fb8391cbca26605838fe15ac636d787bd3969680ad2d4acdc84f1598857e7e8fe0808df06719c973c08d7cb6ac5a07ef63757cc12957dcf6275dde76f9290bdf7de724b29654a297308a8071d080cb4f32176bb07fd27f5dde3f01fd577efe33ff6bbcfe13f34df3dcb7f6abe7b1dfe63e3bb17f21f9befbe7b1ffe73f3dd7fe03f38dffd8ea7e361e0e578cd4042ba8ae37515375e83f9d2d45e9aa9b3b4521721a4d6ed497bcabe50038ad59cebba948c726a776439aea647c7f1a9c6b79178a9b07daf8b87907b5d5cb5942e8ae58b7dc8ca92d4575287e6fc6604cce715afe6d78f9983d9320727f9d8b655316c7da15834d4afd5a75e7d0de706ab698f1939e8946e76dbf85a9597f29eb49238e8b48983fe1a1a0e7acb4167dab66ddb9e723b360ce86f5130308175243f4ae18852ca51bfe2a03b96cf931c64f224ab7dd84c37040b08e1a0ed7bfb987b28fd8985356433d7e5578fd621da416c89a5efd3ad0153100c8823848bd3a08be27775fcec85e2eac6245901568d12c4815d0ccbb958f67d118621dda2a22ed4e0865d842db1fefdac08d43ef0ca3f0595b5e53a2bb2fa0c1b76d849b7b7ae4adc93ba4e081123ef31c6e873638bdee915e9a6b09dfa4216d72d50fbc2efea57e406141bba511be92c4254d9fbef645906de8c753330cbbcb7f0cadf4fb8ac32ae87e0fd022403520142013e13f7f8df8062c36e19b55c65a4c29c6811815001b0c6227165645ef9419ec7412ff2414ddf82f88f2ca102fd8428d626fb3c91d2ef5637145ef9fb70fda7f5f7da4732588ed163548adf26457bff89dfef1ebfd036549592a469dac764655f9c797db1c7309a13be46d91168d21d326db060c3ac8983763cfadafc429ac4c1edfa21cd093d7c6e7f3a4adce3850d07d052ba0368eddcac892635e96c3892cea898acf5b566ddeefa1cb3ebfa1a1a558adb3ebfae2f2442e4cea74dbf3ff9f6c33cf956926ec84cfe3eaa1471fd3fe07a8436d104ee6e99ba1270250757aeaef424cd9552f675ae621806825bbdd9c7f53283472ebd66fcea93902b29f317d2bfe84569bdbeaf5e44bacb4b349f1b99af8b3946e6ebbaf8e9c7fcb3f247762c1a8dad7577a9158869445ef9759364c9cccccc524a6666174c961ce79c7ff15f524af911d9ee8c61ecb003b7630a1c1b1a0cfb16e706bb85db9539ba1d1982972525b783df3ad9f9792cd94123541669eeeeee7e46635ea2e1e4603b77b7cc8c53023bc55a1c2916c786dfee092c47f7fee6a4d7552b866d52d3b64d65850df9cbe58f6f6da2cac96ad63d7a9d8d124dc3b9c1f2779b757e57c339b657a59473527a551e76641ac77131c6d4d69eaa87bc1d17630d966bdcddddddeb2b58b0dce0764ce0dab0b169ef26bb826bd8d15c4e7a21552466fe4cdb986be0c8ccccfc9a0a2707dbcd31324b1a1b5c8cee693736f7b01471f96df8c68dbe91746dd8a86edcdcb8b9b9b1b151737343636f6e6e543729eee6e666c341eb76374194cc5fe51ef9724e4a2b92bd2e1c2a56ab5b95533a2c1e5df938546c58b30e072707cb2ca5657e1c1baca5a1a9a961a6942953a672caf8d586a135c1c011d3c71bd09ebf2a8032a5cc34b2c71b7f61bd73f2ac6c4646292bfd8eb13f4072f594724a39a45ffed594db71f5d5b2a714e2a29783b28f409f6a4f7d32f69451cef99a9779927b664fbde88c3668ec3a60542e3bafeba5831c10fad857ee07b7a3c6fac9abe99cd24a8482ce57ba3ecebf3e96b1ca0e524e2993b04d239d2dbd68be9caaa5900312e71a767acf6f881521582c2eb5b246e021c2f3849423f06c601a99395f363031146cc7c964bf1186d40c5cfec3f7aacf2f9ff28039b8942aa5fa86a452dce6b18346e4dda236a9acf325fd86ccbe3ceb607df64bc8bfea60fd74b4e71bb58fe5a0fc986cbf830eb6f6d6a656ecafebdb80bcd93704abd8c75c795c5ffc1047e8c3c3636b13d2a54c37e688f9cf5bce94a59da99da99da99da99d2964309dc57f420613044c07e686348ec45aaef4561de2af3ddc5b33d890a3985a5eabb55df70f82ce14e54fdce36fc6f65f5b98e1428c0d5e94a0056494f9b2a3a50a309ec8810b2b90301261490b9aac609112c3165ccc1550008c2523800862cc0b4e4460810517688861891318265305489e6001430d51bc88810561389310263344064370c1823172d0d2851050143dd1820e30c010051532446022420e52a4b182164a539e90d27466062d676208e2c9184c845c200c2c50f8b224863294788200455e6088e224821f4e305ba0e2023389103036d040860e516c90e18b133e704121890a32a4c0528590084871068a304a30a1a4d4c4145da09ac460468731c688d083154a2f8c49c3072f96c47811d2a404d30c288052c60c2234b9000728b4a04594304c9424e96203258617a678210a2e8cb8c0932a2320f3c409972d51ac60723181122c9e6cf141ca152f843c21a20633b0eca0430b5bcc84f922296ac95110600871355628daa2c40a2418c10e663075e0072a4e44f0250d2352b89c8a7052841757a250d1a2060f66c274d1050c462c89c069ec500415262f4a23f061092a0563cc5049c119ad242153a4a841c31a55a86022456be130489e88b1e28216a4e0ca981b2613518b14326840220b175ce14a3eba508a7286082a4ebe10c151d000283d34f9a18b12175264f9c0165dc020e20c2a3c8871012762aef082821413285902c90e4fb0e04419377831d385900a5ce9410a2c6630430b3494e02ec6286901a389268aae1cd183133067c67489ea22091c2f88c1e50b2b74f0c1480409346151c41831495994515a40972434e0e0c4c208c2d051c10e5b90f9a105189498485c1500d1030e24d8a10b1043fc7c817984085eb0c00b28284f484da611b7c1f8e1054ca4c052268831d88a1c9a44e182131944a490a3a888284b4cd9e10a1a5afa7969087e16610bdbcf465457369652caea60b79c73b68c914af959abf221658c314a30524a29a5f4b8a2d12e65bc5310458081e8fbfb44202ae6da5ac5d6ca1c04bed56ab58694fc103307443c5c1f08bef2276ac14aea11116159d78bcafa17278039701aa2b822a2d67d6af5993ea5954fa449a2d7cc483a2ff0daeb92e3415f7e1f3e785c9790d39ddb1e08763b5fb739a7bf3747a98dbdc2e94a4aed16eb175276425adf24880383bd3e0477aef44a9037cb6496654e387ce5ff8130d8eb63900954edbe7d3a5bc0b673b1bfe2d764db6a55767daf5977372fa0a80e6d7f8143db87534778ddeb2bd6f2a377c9ec93b27d605cd5a7b77e927e085e9f645cd715b118bf505e970a4c29bd6c6ac5aa0a55ea57ffaa9f74f0fa1806af3372e4766773cbb2cfbe506884ee0ec9de8783feed8183360e7677370f1f9807df9727a964dfb998f7eeee9f65d9d72c46ec33223f1d20d2f374e2178ed0dd21f1e50e199967326519f248e7e6b723d5aa673c7ef6d2ebfcc8bd3e3441b350d8be3cde92bb9b7943e24b0cc8622f81341e97f57b31567633d11801b67ef416d2be58b92efbaa02ab531fc9ad5fd8de6d56286a7f6f097dd567eeaaf4fda2eff4d301222f7d79915c21f3b3390c444448ec2437fa2a5c71e8ddf84f4cc34234f2bb4c102fadf698cc6ddb1361dd115877486f180ce00c4b84752b11d6e478f457376f04d60d7ddc21dcb36edc42ee0b59b7e76e1fe6b8db33076d37ee96c1dd0470377be3c37ab7e73efe0d8f4600fd353754cf29fbf517723fb5a71edf23ad564bcaa57fbd565f3efd86cccffa8441630458afce8ad409ebc3ea6e68ed94d4be46e98c32c64f07c83b4885edb74284dce4858c3662faa52a57b38ad51ad45fbf90888fa675b17db7cd5a2ee5ddd1ded88c6c0dd3b4c718c86a5f39a8bea66459d68610574cf6e2417bd0faacaefb8430d9eb63df1cec4fb6d135850ab43de917c6266cdff6892b8f9ebd3cacae6e327e381cf430ec9602ad3ad723b9afd2f981e46ab57e36427dec5d8787dfebfbf9f6e05beb571ff336eee91ebab8399797925a37ecc1770891eb47b8be4a6e47fdcb73f0ab46e43dba7dfb325143d1f574c8f5e9fcb87ebad673dbb867be90532f4472f51a97fd270cc10997fe45ffa294839c7a9756eea19f0e6de6e6c723ded0e67648b0a626b8c4a82e58c181ca0a545c705541a5051eae1758d9c28a642a4e5c60549962a53eb1529754b9d4a812259ddeac027595b1529950b9c86021292fec5e544a29a5945f1c41c7cccc19d8986b652f44a2bd96554672b3cd138abd6d245c170b4370b98df3b6ed2be7857db9af4db61fd5e3bb6d5aad9ba6dded2bffb623abff316998e67969aab5d667ce3466e6ecb3cad5ab2f6badb5d697f5aab6db94b46e7777ef19ba75f9b5f6f654fc24f7f8e3f8b1bafe5ae47af8178257464ec9f2ccf654e411717015d7a54ce9fc90518747bdef49230d0292253347ff9268425a7610ee756c6f12ae2b6bd9584f7fb9c928dd8188f131acbb6122b5ea9b537c6271f006fe994430e8ef9886e52711da9c725dd3beecab95fe4cea2392406226f511491c2165c8e674c2867fc58f649650d25cd46cb3b3971a0637376cd4d4a46cdee6fd35d5096c0d92e3715c751eb5f6e7d4346edb5e4b713b549c6ab39e4d66f31b7dcde6351b1bda3f678d56336be80da5b4fb9bf7c7f99ce73e3555efef813f1108346f5fc3b81d76633039dec715fdcea3f1881ca4aff2346deb8be3a93ca1019461b72d878374ce6dde78373c4b439a4541458a1abf7240b228a844516d78860593d1fdfa56a56a3e7c10c7f5c9c10a757059f6853534de95d12cb3fd5986510cf3a82a35a59c483697616c2a158f8c71d670eb56791bffa8bedf7bdbba1dcb30d73828d3529ccdfada14a765aac782b0f7cf38287bffca419cf69e52a5b8acb927feec0340f1c282197fd2289aefe764a7d06b0cddbc923a84f3fdf5c677fd3e39583a843cf09fd4f7fbf8c07f6abe3befe36a7bcde3fec69b43d6c361e35569bfe6b32f949f7acd0bb7abbdc6713b6a7aeb1acd7a342f14316fc35234fdccd1744693a5362fdcf7ab541ff70192abf2fa32c77d53becad33ee56d8fe315d5a19b6f8df3bab89ab3356dce2cd3a60dcf7a20d39fb3e3fdb16d01840cd77f5edcb46d010494eb5f6f0828121aaf57b0e9b5a678aeece4c6a5bc1d5ff56b9e7c4d93de86bd94b3e3713df5bfc02b2711f0e678d90288ebd1e3178d73fcfcf4421d777270dbbbdd61bddb6d6b63df86479acfd3acd7f7ad7dfbf60be7d79bf264d53c9b70fefc2bfeccf1399ba628cb0d97888e1bbf39203445596cb8510641e4f344c4a7889d3224847fc194f982230b1ca96062083f46949169e4c617146d377e1de2500e3e951fb60e94c95ce979c01375e0526feb80ddfebc887b6ad6f022a8334d3e3e70d09558210e3a0b2be2e393e3080a8382baa08e32ece85252e21e7f2177767676067d72f4512bb954eaa3189bae7b2b09718f12cb4a2094068319299831230533b1752b57fe22cb9c780572d60ff98b26199a9a58d65cd154d324435384e28c7cac4dd8e223621813f588491c377a1e1cd5e799a18d4f1f73fa8840456c173253cb8e52fffb08aaa260771ceca334906e6d72960e076bcaa829a38a613b7a23b0534d19580add4b6c37cd9a866d9a651fb73d86611a86611ef79ac77723a12ff69cd751b02ccbbeb3cf8cb22cc8fc2ccbb22dc9c1ecb7afa7a430e5c38e1886613d857bb2d7b26672307bed6b2507b3c7a2ac66c562d9d7ec8940f09ff919f6d8d7a68b7d7486f55edff65d4c9afe7daa076a703bc388eafff31045a023d9c7d8b71de98a84c43dfe3ca9a4f47b4a89fc2b371d89405d03affca35a89a3906cb4ec0e13925f6794daa9d6f06f97a3f321cded9e03fd3f37ef3f49fce7e66b232dc6ee352925b7e3e6b5b9fdec72ba1c9cd764f7d74db81dfdda15bd2f9e392fe45eb77ae47c9dd4f1e59b39d9e9c27f70fee6bbc63038080c0ed279ff86ea2f38a893c2e27cbf2aaa7533c686cd74d35e5fd6a2d7e035a452de752fe4381860d04d7ebbcefd26de30008a735e723b30789cbe395dcef31523eae6bb9c9bc7b9b9e9ee9bee6e70bef35a0c9caf3f749c9b8f51fcc775703e1e7e71fc2902b1121294ff88e140497a881daebfa3e13f2dd9048cff6cd73f3ef11f5e1ae22822e1dcfcf84ffb4fce3b06efdf68f80fceb4e27a4e51130ddb18ca9f9e38282a02adde9f26898318701d0baffc37ee99fb95e745dab5c90c04c093c10078de35f000f002f000f01a789d85c76ba5be9ab67954161b36533379f11ff69f00bc7f7ff19feffd1b8cff74efdf55b493fffcfb7716fee3bd7f3f6901f0fa3f0fe73d0fe7dfebefbcf6f876ad81c73703007c5d04b723832e56b09ec1e7581a76e77b23ddcd7912e21a6bdc12886a5c790bb69b786df4c4eb24dce3afe3b50aadc4eb253c533917a76f83371ffbe6ffc6036f6e769cb0a1277992c7ce933ce9faf874926a4a4dd1cddf78b6c171fb6d3cfb0e7b766ffac36e7821eb666fe3d9f06a3c1a2fb437fb4a905ef829afe17ad8584ae8eb9383a5c308bd19e811c0ebe24a2b63b3ef6b7f32c3097faa311280739084c63c55bc815c97bec7bcf2ef1ddce1215b6fd841dd7862c3ee4e1638df27cc9d38eefc30879c62e08cb1f22507cd8fdc4bffc19e551fe2dc1de7011d87ffc4d77e7a5254af6ddd1a734ffcce3e12c5ba7df4b86f2fdb9edb7c153fe50591f76a5e82bc52ba08a4fa4050c3ce387f7b0cd3b8674ee3b418bf6e3adb46148136240bcceb6f4296eb49ae3781b9e176936c4958bcb8213f61f1724316639b63aedf2b03dec01ff334e01edf21b97edd0ef7340e0e36ceef1ccc2efd1ed0ad60436eaac00c8d44ff074c300281e003aa57d243aa1f62d5a753ad13f1ca1f04392292228d8da7f16a6a6cae8bb9e66d2e1b554d4a7b0a620dd8b81b2995a5a9b1c140fdfc5890f642ed3552e8817b45397a62d4a42889094788b80702bcf21f0de09f0f1804813ccc79ca141e983e200ad2367d75216307c5ce22da758b83aef7cfbb75c6861cd526d096340e78a1f3d2c260c89f85bcd574fdfa520abe5c7fa76ed95cbba85ba2b9fbb7cb2a801bda2c05c17497b0c0706318377e77618400a353e3161464b909b8bc8402a7abf92685be44a14a3fb541818dddc37f7e7b936d93d1d87f3640ef90e6016836eee15f744a5f5a12c34a1b5c5eea62c3adb9bc74e504d4a30a94bcde11ba3bc45fbe52c5923405a98de2914e4ea2242a701a49945429334608255c86e8a78200a87cc9a2852b529454b855dc10147343560a73430643c50d190acc0dbb688a1b369214376c2cd79f5b4e62f0900612380c85afc3d297251a9ab2703214b264b0c2873154184141418b21f9b576204195a42d2e0cc5cf034ea88183115dcc40a3684801604c496245104dcc70c35012269ad08204528c2104d250d849d30b239e58728228c2cc30c6902fe145921609bcac608b208680c840c51951bc600d16ac0c99896a4991c18b0a58b4184a51a10416493750f1c568c8a1700189a3a4322570610443fe4722d00f93175b62203382255e0cf9911888788a6a21054c1c3184a4490b350031a6cc0c2e9031642f284a28a18412140b19847e3a3fe6d3f9792024bf2d0827bb8991232674ab93e8d0e142471c4ce2a09c42464c70b0e5a0cde5bc90bf5ca85aadedba7f10ec58accd16f9dee426a94bffd17708fd2416b68b40d9b0f3c9b1a3cde7e1f1de9318b8e3a0835d13350ba3ed332fe4b9d957edc2b2be526a9162d8b3bc548277ab365e8d7cc3be8b27acc0bd58835bbf29c497201570d089bafbfa79bd875d67737b5a2171d5a18e0e1f440e7a10c9ed08bbfeebf29752be6b117974ee06e41dd23a584650caf93ca794f32f29993f9e3372c741c769d22dcd8c548991ddfd848fdc91559e9b796177436b2dad17eb593b58bec8826707cb175888612db161348a0ac1c9a99fdc909db65869c4a4710eeb533ff37a73ae79d870db4b4d8b1a5ff6566fc8943631c6a635ac529d615c09e52006e4d72f2c7285dc3021082db9d7e696d519c6f63ea1b4f7cc431b438b74b52fb45026d3cdbc30c7f57046d9a4feebc8c6f51381547f5d9432ebfc88f1915c6e304748d79f16a554b0f19de3e19ad1934d791cc55a280cfacbcf1e2d61e3879a110f4fb74ae909db4a9b63642c7db982ba5df3c3661299cc9d5e7f4f05906101ae1bfb89c9ad377efd94c46e0eba00f35ba526e5176250c8b8f9b7d14dbb9e3ef324900cc80764fc6c77a9b599737a3c1fd582f698578fdc887dbd2e304629b969a4b26dae26ebcb5a2b83d8f58d49063120588318563ff94384aa5555c6869dada95bd368aa544d7c8a5938e8aa2f06497d57dd8ed028aebb9c17b3b80ee6fa77eca8b0bb5eabb5dd6ff6cbb232411c7429369eb91d64d8b0a3e22a6aa7a3824420a1f814b378ccc2816c7c0e262af9dc302a81f166051bfad3752f8a1ebf70ebe2865d9405f7708950d913274e9a2af6f16d68e44a2a6b14b6378a1383bdfe62f5eb566b535c7d0c9d11a785ede7b4b0a1bd5e6b6d4263615f087e6d777a35d5840dadb5d747a946697c8ef10366726a8288175e50c94ddb68d6040f1b073dffb99e62b452f9fef66a3632cbb2bf1ecb304c3a78bd1cd362a7b24a4c8620657f09a1d6f9d489bb6595a255ead0c7c7dddd7dfc8ad76593e0953f469508d036c633d62a7190bc1ec6863ed7630f065cd593fcc28af4d13cd9fe352a17ca2f6cf7c24df6173694df7e4dfa4ddf5f5eeae900f1e7d27d9c6ad98ff59a5da1c572fd65e7ef533f369112e3ad699a163d2f46b0e9876baba8cfbad9b74fd45837874b4771ebd74ff7fa429f29e3e72fad0d32df6df6049a7ccdc9293e45a818255bb228b6e294d88a403af04fa482413011687e3129ae64f8ad5871e0b8616cd55a2f6b69d7cd242cc8417efdf23b98eec0bf98d41bc897ef6138287ef107ee91110cf6848def1fc6a4a8286f79911bf9912331313581717a82eaa3d0a72045a029495894f849489123dd62164bc807519010bc216c1893aefc6a3b1eea2b910cc9f7260e6a2c11480ec9f729d2a8fd4252bacd1d08e90ee2d74c7883a0fe9a852c869832f3843bf70b59d7b7af99b074496bce0bae38e8feee515d4486cd9458be6127294d24ad0423b5db5e070259b67d20d8b6d7b80781f6d9a71379a862c082fd397fd66bf69c1d51f6dca3af906f9ad79a938f08e443933ae684cd3e9fb8e217f2966c81465fe30e3650efc584022614345d2d6ecfef1fab37a04f3fe3a0fe82b887be4f04c23ec56d5a56594a2aaf485ca53e565cd1e76addacd5ba2efbafa090b8bae2e3aadfb126aecf9600023c549f39228cc52bfaacb0a358ed20f321ed08ebdfad285a9753698700b8b9fedc1dc40fe58ffc7874818241befdf1e812056fc0b73ba70420d88d1ab8f19deb913509bd866f140b262a08a36f0f8debd17da9fcda2f45639e8a75392d5bc6ca403d8032fece38391e7fbb63142d2a555ae061de185d960759ba6871439e1492173137f4f1f2e5863a588999a6f42e51aebf8dff506fc1f5ffe17d3bbef24e76ec1457d0346756a9986cbf3773d85a18dbefad4a31732ca3318fb77367ab7cfa7f7d31727b4a7e2f394872aae95d7f2a24b7a7ae7f8ca9e79efb2fc7d0c2ae52581e24576befc6d5fa6eded564bc71359fd73def6a33a6a4ce8f2f0cfbe5bb94dd5da5b0d24196b2ef191a4f19db1f73ca30337bd731aec0364717ae58259d32ca4969bca262d9755d15c3ea7545a65dd587cb07e9b4b5135b7c818a47ce4bcabf45729cda63ad15cb32ac3a4559312ca926c9eba38c91ca0a058d3fb2496bb7b18ca54826b8aa7bd795b1b367f7c4d96196b15d5297d425b57bbbb7ca0acb6f6d36ebd815d6bf70cb324ddb2cd329a3a7fae29dbff145d7abcdf974e3699a5f07df1bf87334e2a009df1d90c01bf837c900f7f887dff59745c85c2e7c9743538433c03e1a86cb673cd35f39646c92e5ce4a86eaaf08f734a931d6a5a6179cdcb056d1a5682ed97979a94b0a4c60318a059e6b1d8c12c5050af3c9c1d2a1d4c284ea725de1928592b1372e2f71e9e11ac2cacb4b5c7600a28a15d68b94f284145c79e5fb00a13d2e2b78538d945c806106aff8f2e08202aff8fe8d3fc1f08a9d6a073cb63cf1067c43eb48ad56cb490ddd3a28b6b71421b764915bbcc82d5f18e42b772e493d9512a8bdcb8f87f6d893bf939661529545c5a4524a62c3578a1ebd07db4f79fcc38f52d28f2ca5f4e25f1e0921b8527a15eec58a4e527602edf6d5a063a42dd4dddde449108f2d51b847ba51044a1269e015e750650bced5e06a70e5963db12178b5f7fa5d3f1dff74e4675fb54f477e3ca6bf90aaa8041c44714c1a0738718a1f58b51a03d1e51b607ca694524a1a6764c99591514ab949194a2965947c19a141a7d30e06e0091f4018087bff582b6075ac0b0d6bc3b2965e55d259291635cf475cd56c4aea831b134448dff8cd3b80b8c79fb3c286cc04828f0f8880a40fff618d6e12db38883e77f933cf070ff953cfc70feef1979e03b8c73f7a40b8c7311f4c9b0f1f9763508ed41b8d448e2a276ccbe8374ab69f856eb218a11c639448dd4d86cac6a5545e0fa934ed3bfbce346eca2ba9c949aff4c08ded576edbc6a554d6aa52dc6684142a525e63b4a4ae83b2369dad62a3010ab002d2388c93b25b6eb27b48f39a5713bbf32307aefc815d39342753d7755de72bcf3efb70843ba10d081a39a2a9eb6a94ae4ad2b58693cde1c36c80025cfebf9e8328f6d9eb532df153f2530e10c7f3743b36bef2ebfd2b12e2e0d9c958214bab35b316ebbafa8f846dc992758655afa68964fac5c475824d5d5e6252838ec144c609b65e5e623a736da0e20bd3122b3025f1609ac2d42aaa94ca0c4e9a88706106afb66ef605a721c0a0c1c99421b27002a525ca27074bc792162db02c3cb11b6c7679498b982df6e6f2921626a692c5b9bca4c50ae562e9e5252d53b4202dfd40c495979796a610d3d65a5b6dad4f8a88e10b33433c3101a3e949540e694ce1028a102d349dcc7022a552527bf3e2d6a52d377e67a3ba98381b3071c48d558b20280ec04492eb7f030b1fdec696f9b238f1b7c1ed70170460092f0158e2ccf5afb1a093994a44a951259934df0db7258a96a072e35b6e4724a384962454d60a0624a1c4921245d73f656da8584ae206ce0b253136b8fedb164a442440b35c28156566a2c0c62061c301ae6761fa86db11475c0f83c4d175b74e4b47a4d04176851143802c446689309c4c34e4cbee12eef414a174978c60a108186eacdd96d466134044981bbf6b192298b891d38336bf877ac8c9917b5dc44eb0614fc1110ab9f1d993fc811c0e366c2cb7bf8b70a0cd5758e447a4bbb1aff8d7475f3fe924a4468aabfe1e63e36b5d829432bf9e22fb0c1bf694db2108820fcc57d243f3fbd3f1e721a32e8a4060fb17da6e3eb534aeda3ffa5387c7bcfe94d53a6e6799dc4d958cc63c618d6358b7bdc17efbeeee2c6b0cebce328c8410dccc62d95b30029980e3415080074d30c08360f504f8b0bb7f1987e706f00ae0adbc0d87c74306f078a8001e0fad3c1e42c166ad4d334b5260a06f16aebf9604d4512bcaf5e7d0c5b81e5b4320dd68148f22d2f5dfb279c57525d8bbe7bcd61b3726aac803a556a8c44e9644ad64686c002000000963140020180c0a870402b1583420139555f914800b7e8e427a5a9e8ad32cc88118420819630801048000c8808ccc8c4420005fa000c0054467767bab6fad083a9cdb8f06a1b55e88c7f4edd1f2d5548cce2175c1ac76c0b559f3c57595966048915ca05ef501bc1388c894c42e1d07561062b44cc78c0266437c6918305996435af7b6ce968bea311e1d40700590b836da5a5c271ac4c6989ffa72d7c67a8e6ef40e09de57c589048126f0c66193fd880ebf8c3c604f1b20d489776532fe7f8a706bbeb0f04bbe6497d7be4c4b6c6dd4834c095ac102ec4c5b262526242b1f2ea6528f554f8b37aff272061a7457099544f3498c655b6cb34350b0eca2383518cf53cdda72aa1291bd9e31eb699643959e7d6fe4af9596b81ac592fa97e9dc7bb40b5230117045999307b1f39f95ad0096f6425f0e9974ac12af7d7eb8a69190c0a511e1330a80a8088efcdfee6f42dc7b7d8799d8a0ed0728a2122cf15a4427979dd6127e2ede1a27e424af8e3c8dcf70d4ccedf024c2682d69f60703da5b5a18a1a881cb9e47954046484633d81c9af2b5e85d5f971433ec8d947ccda7c69f207a9952e0115de7d49c7ddcd1d8cc550d5aa666c227da391d15f6bb5690191341a8c96649fa7ac4c32099f126f0df73231086352e105c626ff5603808dba7ef5f12517c65a911b9ef89d998a0530e0c22f2dce3a9c4ac34ea2884699f05390916c482524b849b494c55f8baff8046e2f6f4440d54c91012fc934eccf10ab38806896d79b4241166cc0332a48dd7a048505d3f25ddaff1ed75387074b921115be7d9a479d91ecff6a07255b4265cf8c2b5f1043e12d0685bf69a7d323cb7ebd48e70f97ac553a385e31cee323fc92ae708da2066eb275dee9cf2bed44442252ac593b3fe4faf8d5c74a1e367d78d2ddaab58a5b367519121efc6fc8e340b33f20c304b650e36ff356cd7f25eb381fa747c9dd9cc15d8508f631c0c9a5d5fd80e92bded3111f610297a4f2c21d59d3fb51988e3a9ac858126167567cf269be05144ce819ebb89c53129ab8ea59c804d091fb49af336a3e3d1323b67801dab818a901be5df1d88c6aedccf90854079ef153d91260796e94af12fec676d7f285532ff2011a076b1f66ebcf00ca1243d12d10d93055717f46f9d8f774b245a499da13341aad6b3480d62f1751837fc880c7a6fa0cb058e122ae2e0771f513236f9c98a2e9ed1925edabaab97099af028281c0a358c3d7e3c4fb055a0e45d94ecab781e6777865425fd57212b19e4f55ffa3d172317eb3054d3a48ee1fe4a7c3054f371dcb3e0e1cd7efd076ff8333bfb77da5b3161a8a9b2d368a31cdb18881c5a1d4040301718752334b2e24f598963ab8efe37271b7b90edcb43b63a06718b2ef823ab2c710eb7cc23fc5f5a9f3a80ea2f1d2855b60fb2f4970d39553001f21a70653a264475c16844d9f8642df9c6a3376b765127398cafcbf4cf2c11c7263324f19b682803f3043a433251318c40b228d43d18b9f82694ca29e5a599b9099f91b1ac41bb767e45cc9f12deea30eaf395f2ce7e2ca18a95010e2910453f1a1f47fb3692d596be5ea0e1feff5be3672121f8ef324e5237293463ca44477d7637aa9e1a9958660b1d405e8217cc66bd1b5e7b3653da3c6fb902eab86dc6ff88cd15d2d5108d556ab4e69381cafc37dd123b42ab8be5a926bb5857d181fa5fd310b53162ef741d7d919b31bb2945e04452cdcadd6cec46cf77c9cab60d58f5a317a65cb50ac45cfe8d6cac5eddaa4d70d71f0175d43ad0bcbf4115a56d9ea8cf60881bde24a3abc8129941219fbd7678d42a77050e5f4e9c1ef68cc31a6c029f701a0744386d0d292cd905429af41dcf38d6b4fdec93a336cd0e759afaa1aa7432284499d3af35d5e62b0186b2e451280a3ba340899a9edb1b8f1c5c479fc80dbe7cec237b8eeeb754b876b10a333255bfba9be404c7cf420b5c6668599dac448d8ab64ce83e2c4d1c0beb94f9857b5ac7c41b378d9b9bf91f5c8ffbf0063deab98d802f17cec889c083cda7889ec7d57082d9a1cd3ca81a0d9ee7b8bac9ea07d2927b90d137784944bce3c58233eec0597620c1cb9aab89c14582450bfda2ffe8b85b69e9e0a34e486059938b2319928a67f8b794bf12652eb0d86351bb53c85ea10dfa26d8d6ee9eb088c9ee1803268a99d24e10af3a3c11fb86a5560b57052a6cd416b7eab1a5a2f98bc1af026361d747329494a8df0f3907d8c8b262ebc8fd7c9355573749b3825d8bd34903600a525c5b0afc6e6fe27b39b311f7bdf458331364434cd88e67ad1c5a4aac963018257dbfa2bdaa87d435b2411ef5fd3fb57dcf7c73c4b74b3e89ef56cb6ab1b7ca05b85a31e9bd719eff1014845c895a9273b35a4370bbdfca12685c11be3aa288078957ad6d415ec1ad3dc842b3c321e84bf600d8798c307d46cfe2e95f73b5d878a8c9db4bd262b8c2252b04961c954c8ed66dfea230fae5fd87ee21ea3f55b84a28ca88959e63722d2f6f094c741d29097e52134ca76f229ca912bee63f0f766dfaf7076216842b93d70d5f28de4123e18c1051c04d7fdced50443ce3e92f10cd5f8295481a30c95c4c0685812293dd47e41724a5821430fe66f4ee82b9e9c8855216c61b12fd68b1fce094d1f6da46272342e6e9d174acb2549fd9df3437cbfeb5d275102286c4023e22ae04a927614b356b400139a26bafc9f4f3e3b758543c2688226a54500c74ef85198f9c53423053532710fd449f47580202e35cdbbddb3c2395326ed50e8ed5a8abda7396bebbba94fdc69abe5f36d559a7c5198384742ef5d8ff022d30802fbe5ea7ba4011053146b814aea3d3c49077e04f30246dacc6193b2d7e2e32bf230586bcdd10cf597ec322270daf7a49d835015e725c7c530587e1c9bc99a423103b8b2bc84751bd83add2006af3fef9c5e154e11474f30d41517a7cf2f13d617d9c66006cc5717420c067d954afc9bb652b1e60478c36b6bd113a64f8b0ccb78dd4559e623a9d51d6f972d07455f3ed6769113a2bf92cc282201ea0e2ad7a33add9808f7c533775401985704c1012c62142b0b7f33fb21498328e3e560951e1bd2029014ec94e33cc7625436082211bf6714eb9f9cd62a764d664602b20d81061b6a0e7dfeb7ae765af87ea9230900d19f43aae4793fd4b35f13c9b9f83caa24d23b6cf2f2c671cebe5862f7ccc49b054073a3bb1baf9063a0c1909333d4435f2a703a2522b990341e5bc5b3989ea95d14d749a273bca8b0c9f46aa82b7a7bdd171e016bb366def456050bf3934b32671c0b10eb0682bcda9e9005449bd27f1061c092e9a67270a54c44cee61f5423593915c01847f806141a88a9e33fb7bdf6cf32210dfca6bec226598de3269c11d1400264cc3fcdaf249901b70fd3adb603626e957996a36cf7b3aabb67c6833697e255ca45fd31b2774c535fa054fe3d75fcfc01889aea655554817a3520042d649b6d61a02e6e06ca3ae633396da369d8bd4d757f13b8a0f4f36b763adc6bd22f47e2a829aa19c87926b1e002f5209672ef3a055186137dc81a7e9fe0bde4adeb793ff020744cd705b8758fbbf3f3cf010bcf8082cc762aa313d0a7724d8d2fa03e793d680621381baaaa1463dd6c58447c7382d854e527b576e10ad2e30e524d0255a228cc5a915aa50401d46c68ee47f53be5f7fa3d9cab9217f8c4759027b21ab2245e481ee8aa497d41ea6b8db01f1d8d48efd0138981c75844a8063add72fb7c756429969570bceff23f884a7a6046e7328a367c13d347cf42d94352394804959e1daa178da417b8a136a09e5c3538c903eb8cda63152ca0fc03a5d1690a73b8ab84cc1d26d3f302c36bf423c403ea31af28e1e05c40860c77d1ab9a6cf64ee8525e73ddc31b22b93ab8c95b40f0fc60ce003c6d933bc6a4c55784c88a692efdc35eb68d25bb62598bbbfa12eb178fc844c2fbe7b458a65f87b935cd289c3c5dfc0ad9430ba2d48e3e1abc3632607e1f3b2bd86010f0525dcaafb69d2cd72592bd95bbce3accf0334fe18ef9e54f9c399ca3340a28e43e64ee13e4fee6390deade835ec06a2b32cb79472330e3fd40125edefe9535e1aaac59cf70953b3d8dde4118e96fa43c6c03f53687e8516136a1743c5f249a630870a9dfe62141239aea350732fc8d43b9cea3952574790d6480faa2c9348cdd31f7bf8a80229813e55fe1fdddc9359c07ebd13f53ff0724627193c0db9e5b52060488ca57d37f44759d51d52710782e4111c0d09259c2a9e37c5be9c341851078292b4f2a5731943c6fe2ab8f7544a8e7ca52dcac1b4977f0a097cd28105ee7c32f555f39e18839ee60fab0d150c9ff1d5c880ec5873a7549e4361c09915a8ddf89870d11a9d42242e17af1f618ad137c14113beec61188b31e135d38489617823fe4ac305a8cabf36a6a008d0d8b86d693668329d9e0203038488d901b3ad46b52d019d789cc3176e6bbf0a71ec111814d374efd984ac1cd10e75e60b5350c9d0462242b5036da69a95dd40992b4bf0e0b8e581a994d2e6fd62805692ad63bacb0fc7351b96aff92e7f7df9948038c1d7245aac5952f4c50b07650e9dbe575ed620e15ffe2a8c83a7638af17a021d419798a8ea611672bba7191cac5f35f1c57467823b66793cb059a458bafa94f7a0890959982911247e4972a114b2777c633a944577efcd58987a17c6aee25f985c9c6d8a5fde0dac5c03b88e27e8c8e700b6433adba354063bc7ee74675b3382832997e23edea77d0648644ac1e78b9388fa53d210361c75e11686dd9fc0f8f56f256bd8a7f23a8d34d9c95d6a1c12320dd169f300cf2fc5ff4b94591804d18a924f360dfe1de2198f949f7e07676faedbcfaa963bb2766b5edb8e6ad97a4566599a966e9bb7e1c034a6bc80caa23584c159b1eea3943ff2c6f406ea0a7748e87f98854f5ffcc490b37e898b9876a7e5be494e58be67c39455111dcc6d131d9c7fa617a393c7f9bb6860dacaeb03669ecb4dd9b9e6cd641fb464b6901dfbe556e31627cafdcc9bf9b06e776ab5cad4912cbf0d18df497859b7bf1daf76340203ccbdf0f77802d0d7bdb1a30456acb881178debf398713960629c7f5db8ed9a6dcfd6d24adb699a950262ab07065888be3b6aaebd0f004fddff8173764f8698e6deecb1f1881bc7dd68a88f620170306747218415e77b0436876681e61b9580a8ade84a076f6c337eeeda4cc66c9854a2c308a51105b917116aea18cb4b039e9577342ced2ccc47554fceb657290471a138d93007435c8dcbe9607b88a2bce31095667e6d038e5f01cffa25b7bff6491fbc2d8e958d9796de03967ebe58a7c42274c91527297cf156d0d29cf729c06e2471dc856249feaa46f50c4383837cab8c122ec99bfedb6b28eea07aa3c7041ad4b379b118845f30399305c91064716857fb74d0db06b42f03bf7515611a31f3d4b9192671d3674034674e408f2f9941b6c745d1d32fd83ac9932e2e5f75c19f1a0de51e7aa49871180b166e59392d07713fc77ea55ec415f369555307aa091d9bc0c2388703f793600a8e00546b12e9afa1b6d1b4bee6a44bd823a8173e85caf94d7b8f2a2ec9bca0303174906c53e86d1ceeeeab68f781b3f8a7b407c8e32fb080c7b2c00f12f4f9377d4985019afac0a4520aeb71bb4a57d4c31dc3843ebdf0f03d5654d880de44ba8c1afac9a71cfd9067007adcbd5370030da7e3776b7655782888126e4a09a372d4f004219820a60bea1f630d07dd3649b8fbd5d7cf161760704de844edf7566e5c680f1f57539c59c9101ae4dbf0f100c07b578eb6c15d50088b01b380ef7c9b33a6600c502eeeabc220584623a27ae355aaf1701fd946ed43ac70e4299e9058ee9b9d034d808788f7fd6849737cc72722c0ea390e186e88318fbd636bf53cb945d91e1c7d8a5d1e15d6ba872e63cecd633c3ab8e0a75fdd8192f601510316963f4b9dc674205c57f317e15454440b7c372de0d9977dee63dff36fe8b377ffc375089a21f7af4bc0ee4ee7a7a05e0fa58c4cea80a2416230d1457993e13e40c80c1390e3523891e006740dd29008de1e1233fa8098b82d0a584783fe1b2000ac94a72ec0b1af8fbe756e8acad0f0f14e322b3d98451e1986409f2a6432074ab5cae85ae352b57f61db6135591fc72a7cd317fb20989fdf23b0b6e6b643dc3d5beb4e1829ec792ff7e697c21a1ff751573fdbb60c180408d22549f612b2b72d1add7ee1348ca289119add76b91f817990c08844dbef1b53cb2220df9860f44ba8ff2cc654862d49f3a0f607a3b6cfe2c5f702513e9a67ea6ddd574545ec425b468e72c7d5f3c9708cf6e07fc37792f551ad76713b4d9a0dd4d71d2769398312a1bf3f9f68ef3096536b78048a388ae2b0b578bea969c2fe49af076b8d27c2146b97bc839e038561871b6cc22168d6582f4158f482ee0baded1f948e61c74c96cac43a66456b35bd3b13221f2b699e1c822615c5b371a22d238f098b6166b092aee05a3050c710fc4cf910ad6c7600c2e59b004a52f30fa911bb7b7c119e022550542acf684f61233a23055ea36b2a80621aeac416f1603aa43dc42299d777b33859e4e3d40ef167fd602ea3b42e479b32f704501df1a0d8ab6947984c1d28a0b6be7ed8044ce9e1d77c921f91f5afe6edb7fb67093ef926588f5810ea636f14ba282d58a355b8808a36f9274aa108d857ef107111088a19286e75e488198b1ec1e4630d0427c3629c3657dcc4d49fa0b2b1ea490f70af862baf75b131c0a29cdce9f89527e2d64010cf59d854d7223f36cf2d7f42e2f168506d099e08fd579ee50c822a54c56585850a7b4e86ceb4eae231b8d28dd8f3e8a8144378aaefa3c34f2168d40797d302b0153008e8efb64ea19e9cc2559b97111a93ab9f9da7f70c158a4e835ed2bd78f95359015faf58886b59b7a8743599f4bde9f180a75fd9631bd311238939d336f3c4eb902a29422ad45f828fb57e43c1cddde355858c5b86bffe51d235609544906a87d9b59be0694bed1693c82d2350d2e3e67c15216aa58597eaa926018252c33b870c6822464a492092426e9f7509f7dd454ff67c8ecc2358be9837697041a880f11c49b9b6e2263b6c8ab4d187f414bc7aee78842c7ad9473c0882f193a321ef2e590ddb2839448ff3b131cc88e88a23f29a39589bcf8018a10f86e760883d0f50d197cd68a14e41562503e48ef3c07384b1eb1b6b4a87b426ac76b978eff18cbd046187c4adf138431c5d3148f2d141ce4145968183fbb6716c86b8ee0c120c3ddb3c90c4d0a5b663b24fa7666074dcee2bf3f95e369b26ef327c83b50fb42d44d9358eecdb1f6c0b409af77637838bb4fb5fa7317197fb461b87ea9362b8cf4e258abeeb4c23059d29d818a5e2fc9aad066335db8a6f009b8e724c0d39afe47b8dee020b9bbbadbbf68592c8715459c0134f1d15ee3393e0c9dcf34d05f3e479a1a536a97eeb3b0d64934a02c63035de7ffcb7c6020311f3bf311bc0a19b5941eea00e92272579ae1968292c16c8ede0ab016a8207f4460ca273c96976d3d851d3c7d767a58794dd48da77483609a1bc4360423412a1da52ec6510397fb0f3ae2fd7cdc40976b313e4721676b8d6a4ebc031a413493a16717560938abd4635eb4440a9074a4d0398d168d3dcffeffc9c43952b8f601441418c85632d0a45a294104b2ac0207153a06e64d5b2956970909b6f0317efa0284b09560744b6925b6e4d87d354ccbea4c538336c14a08757274c6c03bd694a313a4d5316ebf532b7eaa8d7164ba1a0df27457b6cc7a7d6be0ec2bbea2744c8f84660f0aee11ff9df858f41510561624a090e94060847f8e90a9a1812c03a63fd67b04ea8c01cfa47a90dea13f0d19065768735840d0421fe95c99c8edae56c3160861e26fd7842363cd15f4d34aca1f8b7f69c7bf3caea247b2c06f4d6bb8375682e31ba41267eeb028e326d274ab74ae3b82afa65fdbd93fabfe93f98581be1cc532de147bad26ef4c3586116e379d256b343f03e57d183a6126d5436a2bdb86e9401321efacd24e801a813ad45c0d705723bad70ce5fdc7a798432144825bed71984bad00990c3ed5bc86b212e2be88aeb524f2e5bc101147b4a89d3730c3a2d0129914196f4a070b75c5b71d8ce081c9b9f1e989bc602e2aae1e44cccfe39dcb64b844ec56d1e1a6d8c082348ee634ad90c28c4eccfb2ceec3c4d2088e704428abd111646ad54fa6e2c03072ec44d099740e7c6c1f3d48742bf874ef91685d1bc56180219b68cefc0323dad540e07f5a7cd200bfe72486112be8bb45112acac586366f45d694ba65de9fb26be98a9cc6689a27c74b3cb5709517dae0e2dbdc961b21e9c2eb1f5bdd7025e0f55cdb11b2cf796c673c1fd61be4cb625b7e01332eeb5e501029d16a8c8b8b4a0f26ee7766ec868281c4372b7d3fbeaf3671c4f94dc58c63607113f37b91b55f65089aa0980222f2b3c08022bc6ac34ee1c6e91c3a7ceaba8819930525ce0ea891b5be06f28de3c80e7f537d80c68a8491aac38d60208c4da5b94cd15b8bb15bc0758341fff15a0a1a084682a03c8956e466817f5b628af61009d2158125986f99ab56af02c4d76d3018dd9b6b8cce6bad1ebf4e749c058ae925558a56e7a73a77078d6882862d6bb345c2635b256c8224858a7be0676bc51e3c00b96c668592274bfdc2be63c97d58dbb47ec6120b89901f7ba17be2e97e090846ca8011fd2ceaa7f6b16484d4fc68fc14d32b76bbe66530b845536c87f21b53154e3c5454022e861c34364c2d380424a2ddc4ed1ce62041213826f1f8f24d28c530dcf0fea2893c24cae90119df17e22d5c100ae145456351ecdd73937eb6377480386e9d571e9b064c29601fe77a038d1429e6d3dc9ec1e1a8bbf93d21151edf031158809408f69f8ce2863b7ae8b761d943aa3c215db923a7bc01bd082769ccf8a63f4672255a282f7a1c4d23230c9a7e6171d8e3b97db2c0c3cde2e0fae89d014fb0074834613d4f2b1988341ac9614e704ae4ced2a07bdc01410d094b3d0d6b70bc8d53fc2aee6371e972ea00f84a1b9d8c8bcf6453abd478d788008c5c2206cf135e0909ed01e5ebf2fee40a0638f0f76d5b5830e7ddd671fa17751a93d268640beeb8e9618fa4de615ca0f0443fbeed84d21e5ec73498fd1215a3f5a1608b830e69085c28f58508c0b3ddeb1b2a28615d6fb1878d5af54978b243b2c239b9bff025e6f010c48e0e34a326b925d175057c03a3f405fa281d8672fb1a13f49380431bd12522b0b8052301f478230b9c093d8b5637c9528500a59efb3f6f55352984a744648a05bd0a0fb83896b0f3729634aaec94c14a33687e7e6bec0431693e72257da8a33b71ac4e270171d63b02d0f2ca88cfa584d402b68308af9438a2212334ae115446cbdd4fac08a962aa0997aeb7c6393b3fc4f800721f6739a0646e87df127ceaa30db6e20425d702d495b52264a90eaec8a15ba35cddc30e790d6350a8de69c3d3fdeb8290716477cd3ee885958f0879ac7c1739e3e51fb9c3d7a1400dc18267bd5aa32d4021661d9ed8280a01ef3f9dd7f0fec7030ad03048f0645485a99688dfe17b406841f28a492eaa011d54a9e1f6631258f107e0d7d65d2425aebf0c30a8d60d1d335659fef4247c2a82cea7766be8d4921999d81ad88391deb55caa14834dbacc6b403bd8c48e7daf6a7dc3c4f915b0af44921fdaa08b41808f2d61c87a98bcca4db40a3ccbb86fdd61dafcd05e6ca8d9db4ea4099958abed8e8924533424d5c83f6b9a5990e27e567374e1e1d7f57702c328696480d7495e2b881b0204d64205cf2b1e03529c99ad262fc32d7acf95565789b0c7549e9225b2cf59b11e5c26ae0a0b382d229ab9bc26d04fa6136b700e5e691e6404753445e87f02f0bdcc7f9851d5043026508290add7c2915b51f12e5caf9f041adaf42d8394b039a2bffbcf8f6e2fd1410aa5be24212d6f0e164de4c990aebb8a3801ca1fba3177f0ee03da2397677ef9c5c68effc57be446a2590e054c2dfbabc9738d799f1d875e77ada3da5f464eae0cab452a635d473e4ac9c53157970971a0f826e48fe18fe6ffc1a06a52a086a84dabaf4f6bcefb8fc8f033aa19c52113af4ebfddc039852280c687c3095a2667e579b6ad372df8169d113b14371ea0970054fc2b151627855f38845c303266bf305f53ee4caf9fe69c309488d6504a8da30a1cf94ebfc5c6f17a172404de309e741dcec284b516d2a04daf5f4c03d55f3e11ca0368c1614dad4583b76fcd81c9efd994527a458979334b42f0a7259027389aa9796014a14b8004c74a8e6531a14ec905501518208f3ea959e5c235e6a2fa5dad6bd32b29452539e033bc15181bfb42ed24cc127429e477b650e82f5f4d6095cd16433bf965c0b237038063567de7cd6fea9698f64e5cc248bc126dc8d7db2ffc269da75be05ae32f4e0a3af0c113c5ae3d8dc838093661300343cb1df5dc0b79771d21419df98cfee03404114b31468d94f9679930ff79aed5bf2fb06ee67ffea1caa397e7b46b3e55ef474d45a55ed0d8a8afce2a78b31884254556a492b1045571550707a26b61d09c2ef61806148125451cf0f330aa98dd5501198911ab1b82d1dc931ce1d8c35ee3d1d748128c4a1480c730234ff6116f1c30668a24c15ed462f1924a52a00c00d353251c6abab185369331ec1d177c4337a4d4957ed697b68b64b45a7345ea55cb771a12fb675de1d31724a0d72cac14befb543fc0547405ae3dfdf350ee7fa552506c1482f9d57557b0a900ea4f3c0b4981178b0b53514c5393c0f475cc5a8693cd58e066ec9e5125cd0c1939c9d7de36467efa674a1e0fd6a27a2dd462a23ac852589cc4cc6832612dd0ef697d17e2c1ba838b445a49eeb035417f60d839cad651dcd7afba5a4c3e4e3f0e3baeee035d5b4bd8f0330213d67671bae285811fd9db86e381db92194961c515ae946a193410db66aabea274f5b1be39ac2eb61c006278dac5f194b888661a0596f0c547bbaae936840f801c82ff2b56e4366b24711ed92548381ee63afac01eafb87ea5490c1599cfaa4c8a962db87a9487cf716145f30bfb7439d8dd19c076d918e6101d310886173cbcda1e1e4115db77a152203928936ae3bdce64defc7b645da7d318a1ae734ad19b275da9d097e9f0679205189b7fe824c406d5367ba9747f4c3161f18db43e78a3ed42e8eba6bd6089bfa50aa642db4fecf26cc443b1f766ea9ba574cd32ab90bd84e219e54c58d540a09fdf30659d7e4323dc1579d81350b85a891fdc4cc1cd774ca8219a583ffc94b60c0e28bc43ba814bac16d21d579f79ad844af6bcf2b78695f59c105677045dfbb5f660b9268a5c1cd7d62d4ce7d2bda367f1a5fd1784ca63d5d8690556e9dae3ba8560ca2419852c80d25c215d45895eddd1b51ddb557c307156413e99005886492406ac7bd8d1c6526b0c3fee9ccd8d524ec7f85e75f04c5898902bdd424b21d45100ff9aa6a8b11d0a04e25a78e6236a23b0973397d2aa2ee4b2e034f0ed975ec5adc7c8e4bf25e3f43dcf08a668862e69ad3c3025b3eedd95e3071f4282727f81ab093c28c38722546926b74f9c1196bbcf4d249df917ae327d850b5bc56e8d43e113a45f85384ff0451229050e53c4c1d68805a2834c6ce7a5131a369759123f4d074200870f6d8629414c2d4f387e8d8cba0f8eb1861a3ee8e1e6f7b1a9aa2ecab05458c6adee65e4df40ddaf8a57e9796590cbc05d615b987b1c6386a500dd2000d6034f8d340d15de9aba4c5ed95fc4e35ffa94b7e878045bd7009bd792dd2de355d97da71ec89cba8e8e0800502fe03d794f4140cafa79502f4d64282ff574a9aba8fe41da1d713947eee9843801251f263ffcda9eb50a3627ec83770917f72379702b2de5bdf330e0b7bec6f8d48cd4dcb98118f8eecb6eb95c07f97e6aba76ebb09d0ec85fdce43bbea7be302794c220ee15ed772a111316a7e23269e94c921f500cffefaa9dc8fa6c3967c935f1064ce6076f96ccfe44f99f1a327dfc303e0dfe576553f788843d8a314054c64fb9716c02fb4339828650a76e77efafdcb1319940a83a63e05134a906f717083b6c1ff95faf33a597c6b71dcbac743d6dec8c20253dac54476e45d026a06ec47aae3f6c17efb1f1689b00963aa448e4b3b0b3a461777507405c7d2986ab62bff8b6a49d207d564c104a9b62370cade38023b4dd76306e312cb0809228983e843c49a942a64ab9529646d04723dd19685956e948f1a19296cabf6c4d6d4fba671d3c5edfcb838e65875f6b1e886e7fbd783f4ab0cbb192644cfaea2c0b13f02bd9630f85caa1185a39a34a80a08fb716eba88f21a02a70a3f929ce2828f053168cc85dfc4cefae83521074a474cfdb7cd7d48f6c428d985b103adfd4f62f48d0aa3d84812dd4684d685bddc06532426129495952180b13843903034330a535ae5b02168d298bfb5ee4f8194c6df038f01a6f0393cc7880b578207291b006b54d48c38a0c610aade3724d81309cb3691b06b4282449cf0df149fc4f584bf2035bb6a0cbbd8e5d1141502656b5501eff6b84612e4057f2ed58f544d2bff4fa20d2898d9ce31fd533e52c995049fc7c59a0582b09f186a69c9ec06b455add5b46411ecab8c9980279f91b7b20a790b87360579aaacefaa36a180216367597aba34214cd640e2251e7231448d55355759a20d7d63186514a56d5b57c140c1e5977e582fd3a604a31aae0a38b2d7af2820189612adb32921e34aeb0096976c205c1de9c69b15aebf9018561e2e3d06c2a46c2cbd346c5011875bd9d145bb02aedf13bff615736b9d6ddb9d677c625b64a82a96cf943b8ac06e2e32ba20da2d39b9fbaa2b6cb7ca6a18471eada77f8c8144ea06d1ec6fda068ea2c4b67a9840c11aa04feb4b29a0d255c62fdc532188fa29cd8635aeca2bcc4f70026d71b0468852f40f95b7bccae8c9dbdd5a84afb747e18b13a4a937d486c6c06f4d4c70b2b74d14d897327060c72d8e956b49c78745476c9e8505aa334a3e8d91262eec5fdebc96815587cef86ebb4945aa9c56556036e33561750965c6eb38fea5ee46831ceb3102e4a967952cf409112a2d2df08f3f5120a04375b1fdb171d4811eef75d436c55c8ca4c574abfeb008c11c2238948635c8faf7c3fe668e4a22f16d596ec184f2aa5fb9b2ea6c13446da4bd485fc52cb4c3d363c3275890bc5ec060e3356e496c2b5b6f4662bdf04238b4b2eea9f67910a61dcdcd2f5217699104c5bf88ecf1035eb9d2d63750458ef880e821069a6c48f0fccc908d03c244086a2dd61b696bd9d69a1c3e1714e0d5c83f21a4856570c6066d5b6e18915f1988f52c081c63488108807ca945674d5d2ceea5ba35b70815d424361051eeb6744563cb429a6de3341a81085f810c306606434b76268c288e46531c8013094069870209e63d3abfd6b0db1ef1a68ee7540388bb4ab47e75faf2c13b30c825c9c30620a6f99db781abe09f8159d7ddf9a1126243c6815638cca3a6b4cc43648d0399625dccaaf6becdf9d4b32aac9bb1ef17cce82cb9afb210d479ad76541dfe05252d268a4c8256817ecfaaf811a4b2e8d365142098812142cdbd8be6c8624029623640a611902b046565efc3ea02419e0162e806228609ad21709441cd119e772589225ba360aa6e25dae953f40212b9c37489e0012fe98922cda65f04eb24e7d1178620abc8922232a44b625a5258a4421762b12599e8a6d15749e60c41b866e53593e814f2cc3de76c2cf5b2f8b2f2ebe112aa9c5708075bea0fc856e86e2cfb4af52fdae97bbf76cd2bfeb1fff7c82d31db67ea797504c6e3e2a033ffac2215c01b3ea22e066028b7a38cc89c127bf297831f0a3d67a2d2bca6585dd1f02e2f63698a0703e81cf176d5bb10cc1d0db712ffc126502d45ec4ec7a2135b888f304196875fab04240a47d04b33020beecaf027f1726e54e510ec57412f7184d072b5b282a7d4710817ac23732f7ff1319d15762ebc70a25cf05d723b6d98668bf478f0f6ac26689b163a3e1d4451a9cc42d513b415c75edfec99afa897e834a65114b4011558bbbb47fb696a5946344de18ec2cc5188586a2476e2d7f368117275451831f868d594bb2b050eae9f6eda2cb5bb27ec8bb6cc549ebf99387a7ff9aab5536349d564a1f4d66d2fbff2b8d4f2f2f5f690264fb85332103ae872d951a6e3d4159447f6c0070701f2046aaedfbba24707aaaf10c5ae01d72779bd9be242414a2c562fa21c80ca0f14e615b2a8d10457ee40a42a51440d3b1f597aa4f1de0260908d045ee202bdc7bfcfaa5038b33251410df084418b6c2abfc83b7c864a1bff7972498328bdbff0cb0ebb5ac83a20ab84fc7ee9520eb3da1401823f64a108d10738f0f6f3565f87cda397efeced02847e8ebf96021412e04aee92112e4b25c2888ce47eb28e1d664b277564b0240d80bb8647f870f68d0f31e77a1b463f7d0b3b47c6695466b19c8068bf44d8ef7b8dd17a38e2d0661ff71f24a70ace484da1e91f6a2e2da5fb6c7814f3757427724789fc370c093baa7d85213cc3644e742ac781f4fbe58318fd276303617898e453f7a3a18bf8514174378db947a6cbb67c2d1865c1bc1808aedee1a6fb98bc6d100e4a10abc7a79122b38607775e684202ce0d25eb0f48e213b47c7ca3d1bb0741e313ee995f9f7bacf9f46bb6d6f6d74f8b5166c6f923f7120a8fa4cb2cff93410e2531db7146ed2882075a1378c5a296a089a73b204e3490a00c2a5e105cd72d3c521d45615168e83471eaa6062bd4ba3bba516b698afbaab088992c01bbc1ecfd2288f77174ae4687ada943172e01c827d39cc8de5b2b7ea1403dcb855792b158c9290369081b56d1a0eb0aaf0c920c819da214a4c976e56141b82b582ddc9b711f4864481d7cf7e8ae4034ce636e691c8eb31120540a8f8c71ee33a54ec9209f3e748b7cf66a011379a541313ba5d26b096b1ebf361908334fd8bd3087b8f7a319d16cc447ad500776484891ec6e8175026ab588f1ab836f66a9cc6804fc01886e1ba27893c93de917d5618363c22267a0cc383770a5a0bf6d987314614c1f742d0e351b6216deba2f08bb694f6d6a635718b8e64fc26e5ca9ded86549383ddf33e6371e821631e694a8f81eedb21cfddea70c2b1add8c26b073c1ce122399470b48e519979f19c400a4d59d74bcba9aab113c3acc93c8578e2f01db7768a93eda4ba352fb70f77768f270449f46e8c25dc24d9faab42d4cfbf768162d15623800421b53ed707785ede1035e13acfbc43194e3b4aade09130b14380364e99073b1374a8704e674edc62db664d13276f379ef4eb75e5eb8449e2dabd8452d86c3940b7eff9f159d71929f697645197efba16e7943818a458e84c50638b63eb3d0903ef61667bb3f85ca32844fa7ba2935cb0772035844885cf6323f91c3ad4eddd909c4e2c311abffc8048857722e3862bbf4c088b2e3c80ce4313ffcedabe9851b053f9b8acac1bcdaeea426d31725a028b8abf860671653fae17fc41349d3e331d6d2ca9695275ffa1fa941d769bf3fd9715a31a72e8c606d27b6f9ba34d5e53f95e49a92facfbbcafa6ea1ca6ccbd4ac83f9ac64a50871a85026595576edba3520ab9832244e201987247bc7bcf0d894474537538bb0ff25c10148e3940a76892f3b64cd92dc71eb8c8fcb195d73f25bb941c92a9896ec9965317b5baeb1766d350f53166f465cc01e0bd5797c0e13d82c58dbd4fe0b143a68885b64fd6ccf8e0dd14169cf039e246504aaea013ebe70438660147b06c3ed8b2bb40786459d810f0079f72219b84128886748a4fec67bafa5953b8cf6fd88e2513b91a4fd82e68bd05564805d40b9d25848668788a3c7fc900c39d902f2eb5b42cb9663bc404b96908625dade55abf3ef16e48d2fc84592c3734fb7b54ebe2d8d3a652261282940ac694357406701dc34ff9960c32172b977b55f75418f64c9edfae67a523eeae2810275e0627bd5a542df6a0ea3958b0aa451c4b06f6633ba43ef529eb1747aaca598cf573d28d46a933da12d60f924ba86c5474a2162394642c4e6c3142bb17156203eb219a29420f7db85e53781f3359f74c2b45442578f28dc859e0b4f6144937c37c616b342aaaf11c3816d31ff37e51e916bc34bfc8206eeb63829662aa80859575eb74b79e9cbbbbf5603ec05dd1c8fb6e920b1b3111f6f3f3f591684d20565a8725c009a89c3388e27a547f69c63e5574d2eaac8ff0e34c90c44b4a1d63852106d14a7cb581c20b311d58fc0b6da832f7eb7e423afda50d8a713d988cc1530d32d17f6609831de57282a7352528e452a803322c47bf6c1391a8c6178f81982943c1cc6da4012d685d213c2d9b4cc799f32f9484fc4003683426085453cc72a2b8c2703fbe11ad3a8c412615a12e511cd8345b3c19b765a7fa79852ad23357238a491a9d6918314cb84050d30e953ab20b0a9cb54d66995f8d49c7cf8149f0a5e358b4f3201e218418ced1fcca24315ce4131b2c3d6d169ec0820a6bfc3cde20e5caa6125a01414d77dba6ece9a2f2062da65c95c94680dda6f8c3b140a09153cf18c4e2a1ec6f9685022aaf574fb89734f339eff4ed9b052e3270e0da33f941b08a11044f132898770fb6fc08898294fbe1ac303b49b5155e674d979141b2e14d7d1659f37377abdf21fb324968c9150da81827b9e08bc50d30bdfe7e602c215f0eae19df95d0ad001c1afcb8f06695da066baea20d99a900227b0a01d23ef3291c54f239843510fe94d0c6012e5bd2a7e17404345bbeb41a1260ae8290d1e31755317cdea17af81a1729083e568bd2bfff326af6799d8b2b09d9075edb8217f08dcaf9afd52ca7e7a761470625ceefcb7ffc5a383601e261423219ebec40be7de8124e3b43e7c84b4fe49cb9d31556caf1fd9e5c7d426bf25df795d9752ba63af7c2fb33cdf711f3ad924c65174ede64907c416570052fd2eceb2b52bdc592883651385905a1bd27359b386420803979d494e3134ccf9089faa782ef7a1d2d33063e8c2c86c2b70f703bdcfbd937450222ecc53cd213f0382ec7eb522d14cf03c430aae52a22915c7437c5cd4ac548241ff4a3314a17deec818477c3864f65b84e20151419590fa0af0f0d553b3a430d927e9a24f28a2fb2582818fe94be87ed6ceb19cb63fd76aa716795839d37629830659b012cebe6007eb24d59c6ab92e6590ea60033e429385ae4eb9014b7441dfef9d59c537b566549dcec59272067490858ecd502523da2f951f27beb4e418a65151ddc9dea0ab244a0814a9b20d8fe1e01e64f4aa12c1e5fd3e561d5a47a2d6f20c5521c21f0ba0aadc6b6923df4badf4840a24831aea0a70e9849a88c8ca61961a29a1f2eeb34c97ad5401f213225f5af3dc446ee4c9336da14f6afdc5de858ac3fbd0e1dff2822199659040fbcc091851c3a6918a8e043b432e1ebd9b22fa78a20e2add11f6b4c74848c8eadce19590de72aa01ce6d5a8aa0fc982dfda833372853002f812a43f184675058ba4c53cbd70bbb1e57447f1ca11aa20c5bfa43ea83f2d2be9286929b016f941906e4d9750b0090f17756efb2528546357744986ae5cb2aa90a7ac895b72962c51592da501fd043ef2edba2947bd0887fcf31527d09ad21a9de02c13d7c212683493921d3f385a8ab7528072297949af9b336a4dfd49eb43c5aa879cda000bc55d386289540cfa8d0997b5915d2248d3ceffd1f0c8602775e935e6d9eee3d8916d7e1617b10048b5d2b7a120e473ff997d12fea7f37cbef93cf58b5fe22804e606733a027a384f31839e30a2bfc533ea5749318974530a2e4fb6bd77bc90bba0eb3da4386de9789e1bee6d9b1365f8004994229cdaebf8946748b60c85e42a0a822cb2446aac8661ac5ab229d1ca1d8ff83ceb2e52765cbbc62d2cd95ccc3c52b53c745f4c86e44830e1979fc3286cddce0d7371bf08949978e25d685c60cd1ae50001e795721f379f8b73bb10a9418b8e5cc5bc076144ad5adce289ab6818546a34479788c629d85bc706b67f0d0bbeb7b01edb345bd2fa51b511601136d2ad1bf65f0a1ae77059b3729e1f450bc0066874d612020af436177c7ae29a6f179b8e5b65a289874e4919e56d61a34a52a84c0df2f10387bb51770ad5714113484ad68cad525b474ed3adc63065d7acb52c44e2c6a654a34cc765b2dc8ad6f27796a07ccf444d7f22b375f33492bbf06e5434d2b1781fc5c8dd828f52b8a10378c00dce833e1f19301062f4a7193725ee25cfdbd62986935e1434ce987ca8534637a3da8d45e8068e927027c5aaa39d8017285d8a1bcc233ac109971041d5cf452bc66c884948bcab80905017330e28b7fb8162b5ede4f31f4a644fb037d2df6e3f2f5905eeae2ecde17afc5b8077ebc27daa037e7aabf6a93f5ba09b2b4a66c6d26788bdd1e8ff3d70015a31f404b63217e3b9be8152417a1eadb68deb2d8a3e4855870c125bffef6f2d9efc4faf29cb0532795c8d43d62dc4ba1c5a05093db80a6745b0298a170302b8b6d43a52f3824f2ff122497445da61215ca93bc1668ba1e5a91a3049034c59d2c5c035971700fd31f716616010bf62e9f24f93c41063a7ddef7e76161f23fac1959f45b004ee6f97062450ed64e37ccd06263bfae5295e0ecf5b187c7d425c8d36aad8ee687432b1ce98cc1b6ebc843a616fd09a9406e8912cae831064798e42fbca160eec68335ad38e3555c4384948a61d2b1647a500bafb595beeb6b674c210033eff6a8e50ca7c7eb16f33af60245ee57ef3bf0ddbf1d30b22f5c7247778dd6eaa6b432a0555b0cfcd5b76b7ea3588bdf2f47e2adfc51c3a8cc2bc1c411bfde5b669e77b7eca568b15db0c7e517659bf19a620aebabea9087af61e267e85a2bc42ef06df3feab0bfecb23d4043b5a6eb0b451e254bcc341daf5f68d019659e8aa7af635db29142be09030905a4b99d738b98ef4297091b3fbf82ac84829d8ede216058d8e66f2838593cfbace6a117149129e80560920d5350e0adfd675f5fe1ceda86c9eaa94d498330266dc38870e2e2bcca32822f1bb79c4df4462d99183e127ace42470b1e155554f8dc8d3464424ff6ef440375a9eed53c1a31e59f9b608b755b2cadfbc87b8af69675c61f5ceed3fd3123cafea4d505ad7f36a53f6afc008083798553111e8d3021f00a13199ca9a40de07e2595d5e224537057e29c7d6a1c744b6be651c43f29c873154d54964e9b1ec895eb5d021b40385276c620f4880eaaaf483c226200ab29423818f0d82034cd76a0589c91c6272524491f6c1200ea0a53adce55e363186bb990eccc94d6f223e028cb8085df7d86235d3ec73c6e0852640f7c45442b91a1ca7d43e11a7e107965267adfee58b48f77e46327895c7848e21b357da7cc12fcfc470a760525981ef7a0fbf481c211fb0ead5642055e3a8ae552748a6bc79c2528ec6cb312f70d6258afad1699b6c928cbc5eb1fa3f917127c518576c6a3071320b522517a8a5dd013ce6dc3343806c304886ec8dccd16a15bc2ce2fec0200e6a9ef8af684d1aba8599aa2596aea8b3a2931dd274e3ecc0e84c6523ee28b6b7f4e3f9e81d3f6f28257b16f37215ded4d69be8f5e2b1d726ce01bda08a4e28898c5cbe840e4a4986264721eed1fe72b079c3b6618e1104d04b51671bca9c2093127f5e83070dfe308e9a1d6c73cfdf3f1b3e80f47dbc7ce0e512974884ee0afd2a1d776a9aab26d210070fcbacad65f401b023eebe0272cef41379782aadce8e3a1a9fb2a56e3ba05d877088181472562657f131f3e46a4c416c2465286114a773294c66c5312c6a546d777d5c8740499bd4c2fba0337c0c7001cb33665596488b1331b12764d36d6818e5f0d2d3c2844de8f5c9044c8c49bcc77021d4159f2f71cd37a4d25ee11e085d995687dd794969317126fefab249010dde01e3eafa8ce99dd35e5ead1a964e3ef070f922c6ed54035ba375fde1b1d5d104a625e493c911b7ed55eceb4db3a97ff52b273830c22a9768d9ecc20b0de088711b7d9829715a944a86739e11cd1a867d2f8cb69e37bd25a2c56316801d6c979f1d7b5b495def861b19c2f3d634a564e94aab694a59fc4be45f7c6825e283715eecdcb8ad74e5596cbd98943c386ff09d90fe2036db382d281720a55005a7373ac3df7f80e7159ecd8cdde31b7412b654d9f85c3fac5b71b103e65aa21b418d8e08c8858e71c6917cb9183f9cf41f5a728e8f1dd7855880fa5ee1593a321da84035ac828ea2d0530c6795df46bbec0a4803e2a85f8d01c8aef6f323b6956371701eb0fa813b8c5739b0a7e7db88291a44220e40adb8b39c239cf8457dd864618a7465734f062cb5e9820f823179c3c213f4a5ff5fbeac5ea564a746c993966bd45978a29959c71a9b87bb84814eac27a0133ad51e9bf236a6e24163a35c01e5d6d12ea0f8ed6866d448605ae9f6316b6ddd1faf2ce9fc688e29eadbe99736a7354c547768b61f8295f67332535042a94d0f5fe3b13b6e1fffda0ccc58b9b06af6899d7a18e9c21106fab6ea452451071af5183b090a9f7e49bcf931fa1caab1119bab9a654c0816c80524efb1ca1dbb002b2168d82ae211a3b9b7d802dee4f0e1a2431c366cf1b9344d8410599072c34e9a14b9417f11149f9fa26edc08348c86aa0c7216f5c521b5d02e2755e26ec612786244d0b8c897dfef3ed7d3a18a24c3b7b848818304f03db642f6402872c5d5938ee087350235a10a55abd32bddd60e6d9a23cd05375392351df9737655f773745634267280a354e1b8440322ee8ceeb5fa0136e3a4c4c0ac644f6419df31dd41ca442f5ed28d1eb9b350c615b634c7d6473a25062779210090fefc4ede2b6b70bb07563492f9e94cce267d308acd990c098556b6833676295630a024f8ea49f0343e2763960a67033e879dcc6a1b1da21d04b4569d5bc82e5dbba7de511bdd5d8e8fe82a2bb3dfbf972881ff115165cd62e470b49c34bd5a1f3c7cf33ba1ef90ab023e1244a3b2c74e340177682649c69f68419b942b80bf8b0d5074ef6756a25bce68212406e0f0e699869ef879e0556d64ea188bb7bedfe806748f2a073c28b4556b63eb2556b5a2459ae09d241a98fdca888beaa7af6f0432d612aa016295154a98c8112987a4b64c1203c080e8e26de388a53dd8530c668ddb210369c1ee51743aec1c47f12b07ef541d0c621a5c3c47ffcde68cfa29d021e4433da51586ddfa47da4b109ff35956aecd9d87fe77b1a7a17b0b76804ac56024317d018b4891935af12999de536312e7cfd850bdb940ef18015a4571d3aca9cc7201013940c4d2b87948e24b25b06e06dad275486e00d64895e1acc50b19cffa64a2e3da94c02c6851df5d931ede69617508f6ca9379caae2004cb735528d56f0f9d74377af76a6496d8e652a42573170e9a476f9b9e1c2803b945db0a9aca8ce5efce6dece3e37419251779c246f8b25a9048126865d78c404c105b8ce116565d8f4c7a96066010094bb7bee6d1569ed5cba53cde66b5a7473d429cf2e92b2d22fbae2a9d2a467976635cc31e19520c371c1307ab07d588880b73004d3456b78a7ac9eb08d36041cd6e74e7f5cd79cb3ee384c6422174331f8e2ca94165cfe299a7b2db5752221bdea5ef8418cbe26bb6a7f765e2201432e082872a4bdaeb424e32be8ecded23df0fbbde3cc19d7323af18b42cebfc2b8b0e49dbd1f2c20687d9710e81064679c5d5825cdaf730a05c38f26a4fcd20eac8d3baf93a323ab6bf80088891e3655d47e85d3b2131dba425e40a22457fe38abe1f88aaa3cb21e8d2dcba79eb28f08304e2825ebd37fcf80214469a3050f73fa284e204422e064a7129d6d21463338ee1992c7124ea4c1f891073fbc703d925c9b03ad6496c5c63ae6e0dffd61ed499a3cc7df43dcee1c09bbcbdec004718398f12c147850c28f544fa3b2d193568226e732b96baf9e454d0471ab7d0b05e2289d3cff82051a625afd48a4481686923fd0aff987e955568f3f427019ca6c56fda77a7dd4a7731614ac0aaf1fcae0f42f810154a425312c42e908f5930b5dc66fd2684bbff142a7d5c712a62d6cbe1fdb3245cc600c92d2a8e06ca2caf66f4d57ebe88335dcd4549343008ae3cc7307c40e832fd5b79920a07c3ccef9c3fd5239222fa278b535d38af0b693e99894753c239ce0f9939dc1815a88efbb9b8a31fb9adcbfb69f6407afa82e41d12107ad4cdb0283a6049e9ebb0377cb6856252d4f8e660a2f34288036807e62aaba648b46522e4fa7c723d090894d2a7bd68103708e84a8227e270c0de94d09b654763ab2d3ecf253e78a03ba6642f2afa9a6350b479a1229eb683ac4c62b356f1912c10e04852a4a9faa1167616e1e5e3ee4f4a5b775f3c281ad2f2561136af51c757e3e1ac54eae85ba01909ef91157c8a90f30fd26105499a6789d0079a5765e8dfa58d1dc3e1622e42886ef805bd4c71ce539b9df265682fa75a800950dbcb474792197f47e47408656118fd61b4475649205a5c9101ee370bb4979b65d9964b9a2cc4d052a15dde243a71c2f4a8b184c60726d755acc06fa31172dbfa8255ce931291957258865107229e0950473781aae98f37b398c9ba278af7a3475f7792dc3b714c48bafa2feefb449baf7a17a0cebd6f7c0122843767c5043b1db3f51b8a3ef374df56c00dc166527d27857b19aa26424cb0e7a3956a7df29d9a3c168290d69271be8bf823d71934d27b021d33ced0d5ba57497f00d664939cf60de89a7292b3450922e7752065b25f6ad2c553a6a33272243124bda3b96be837f4348ab22a2a765ed3315bcdc8218135cdebc30147dd45c9d6e4f1d6d26f5a1479a1640d34f008cbad6791e1d5e1d65bfdeb7ffcc8e93b7c70d0a3a1b0bdcc963117f70229fafed9ee6ea1b62820eb027140048e062aa8457586acc8fd70bc0e3f3753bce5ed050480c210f1b93c1cd11d350620d82787b43d52e49729124428061aa37ddd01b417f0d63fc73c720c45ec91b3f9a02fa5f1b24873e363014794d00c7a481a35b464731587988be45858f5420eed660aafd71df4ab63df7788844331e88ab40bb2258c17d53498c7d3a9a98caeeb214c423c9072a28a11ab9b3869763a1bac410c0e12407a6e4d9a9d3aba8ae94c70be152a1e434f3035a85349b2bf4534afd7ea474c337a1e9202b1b39b2b203c7feef000456566c91c721e67e9232b646fab9fc862891b85086ee7709c41f6a72dcb9e00f8a1137e3dd035654ef81a03c511be2b59c5f0138795ec5c403d05ea39b6065a0b2895508b686a15c1a617f3a1371dc29c2626aef8793590ca6bd166024eda3e5af6065d8fc595054235ad704cb535d0714b96bcdbcc0a60a0ce3d2d014e9e2630821b73172b22e368c0b5215cd5e2ce219d112c656bb6f6941448c150092b9b9a3e7a91c77c252eca9a05e34686984057c1e875dc68d5696daf652d610e062b3b280cfae65c902cd7a78a0b10a2b2e0d80602ba0cae073dd5b7c22f61ca912786ce620e7a6f3152ffa483650c15c99c75de6a965df7a37edf4421720dc639f42c9988bdb694a75e4ce84af15fc031ccb181c47983dafdce74bbf856413b94f827cff2a92d9b962d74922ef0220d8100cfb6070e9a3206ec7e5e5df6f7a1a158200abf5cfcc06d1597f2ac3cc1ba794f0c0d45a9bdbd302d3519366e899ff8023335e3fef840506c956be3d1eecd6de7ce62ea561437d2bdd17c05b484258ee5caf9c9411ee309ebcdff99ab06f024096439024dc57318501fac6ae8ad6a273ffe45a1c0bd2f436fdbc7dc086c2103cd972f15b71e906e9363d8ef577cd23d2739b09193b0aef6c68be8b8b57705464f36b21fb8a0bd5114d3e37bc7ab517a3e30f935473bc2a922d93fc4540780144eec973c79fed14fc2b5d8074a1177afc0db8d7499de718d6c3a32d62ed0d3b7a6efa903994858fa3e30f614d0c8f14d6dbb7c7e62826bfc82190f259816d2607016990ceb4a304ea09845b12f2b5462e710d7fb56f164712ebb4bfd4efa09fbc7a433364a93706d7cd1509a560aa2662356482656a4682e7a91dfb0ff07d3059ee2da609f5598a728dfd50655fc15afb57694f389c4fcd01c8ad376e80fa99c1b0700eddfa1a3c4e6e7fce9bec3e6fcc13e285ed41ee792b53352b2a4062b1589a742263dd00b8ce5ae22f16cfa4a11402c42ae942cce3acdfe1d45a2d8d3d2ab45174e1820d568d61b44b67c7eb676fc61a4b6382565b1af8b8221e82322381608e3da886b2bfac38e5b320c88aae9219cdd49cd45bd363bd3494f24fca77e946f9105fbfa403e5e48526f5c7620123c395840aef11443845c953b58f71c22c713273c91dcc600104ffa6e8f2864f89a1459fa08da404946186df5bd18fd419f5b3ee4236a47c266d93a313d80f269a2622d12240869f5edf1096b15678798a22e9eb6b55168664329a174dfdd0183d88fa461b430e843740acf00558f61c15ab600b9d7487fecc816b30ec7b5cb1a65a2cb4c021c74ebaf628b1fd4ea2679c5609bc23255076338dec1b27d08503889151280826ccc589d58b59f704a475d071ac38a271c3a50339047d2bd1ba740ce2d4ca3f91a010cd1ebc85ae47f031f9bdf03e07f955ca3a8e70f8f6bcbcf41b8603870fd177443d134c66a75bbbb7a25a694fbffb072cf9e4a68ce2c4b6d668068cb4fce60182806097fce86bc5cdadc388cf7ef3328f024113f6f86a9af46ccd8670017f097db14bd4816ce79104597329d883c067cec2572ce9d438f65743cb412050d8c67d245dc1f1c4caa6007db22b3df721dd1abe92d7a945cad384e80c05c1951d8ec629529907a9f778cd7d71d9a5119296c966944ec4f85336718e55e8925d728176db30c2807b6d110f3e9c644378cec968ad23cc345e87461eecaa29c8c80f2550151279d46a3bcbdb66753774c0a597561fed0acaa19523dc4a36ee41ae90d00b2b0cf48a095b23c21d74adac1359357557b78f0de43a87fae2cac902672281b2e3f562a3a1ebceab448bcda54f7aa83cf26faa213cd4437dd2d55badbab897949a12dda19569d95acf7417bb186a0f7e93c2c2e63f8b7c89d905895968be37b2a5985b576d66cb04531683bcd7b2ccb03cd7041ef3fce339396c809199a24877d7697021f0ca355119226109c34e07b8493ffc3bcf1c25bcdaab8c9614cdc64ad2448ecea196ab32426e016dba6cf57ac48c0edddfdabc137177b93b5c2255a31d3a8b76b4afca846da052e035f7d7a5ebcd1bc1ae8bca1927ed0f71091c0e6c6449719ffdc19a17ce623ba3517a45f83a9c03ec1cc07ed25f99171b3d207b8595d147bd0bf7e9e10905a97913789f62db533e1eadbd2ddb5ba743bc9254f8b275565902b69a80195e68cb5b46b18914611df803cf65f9a735b63a4966799ba6b98fbfb2343815b349796e8089413e8093ec569a34ff46628a146733b2a03dc81625585703ec07f9083d6e44f602ee1fa8cc01ab08c4bfa81bf4f6b7a34b2a4ce9c0be0c8b53e0b726c0914436577d5b0899f1489e56a14d1958baa0c86ec5c563969dd29283c1a7350940a73cd4c0e4b0fa75bb8c5767f06fe9ca1fe6fcde545cfb43aa97f1431a394f66bcf25cc3a51784f0ac827d506e22ce4077d71fee4efffe94602ff641b829ed5381a2d71ddbca0d067e70d94b13fe82c2e27282f7e9874e1874cb00264f8162cf6d0a104dda94b4e9f2d40a8e238d8249eb1a0e54af76c734d9d63e83d3629fbc585f142a11d426d41b0c5bee81a58ccb278561d3fd85d1fd72081a0389203df204284ade72fe4bbc1b6d0fe43a31906cab84623ba663b9f1c6acfccfb468b376c43801f39e51a3726cc3caf715303b8033513b611fa5625941d8b21e062ccfd9ad996e7a331fae6dbd5ca609e6e40e1325aabe62cd4c39394c25042ced068e4f563feebce80e3084b69e12f9c50c30899d8509d2dfa2672aae9cdac27fdba67357de48e7e9b19629b63889231a5e78719420c3da52cb248c943dd9201baae5c7cdd0ec6038196ffecec7741c9c544b1fa95387321f5e41065c6d4d5dc67b2ea7d7249b774b5faf4d38e21895d5972276549a133bb7a14dd5b99bbf31fea89351da27034e4e14a5ea317b7bb6487c260a589847682ab75afabc1b8e620f35243c2053b1cc0039e0a8e78d8fb475f20940a63bd3bff4845505e6c25c08ea4efa4a1c954032e7617089ffb47e4c7a50b654a6e2ac76b40a9e8ca6fe883b7d168475fd60e38e939400c159175658f005279c3b410b3ac4efb9d8f3fd0051027ecc9a993100f679526a5b21033de8e69ff609fd445d4f907c3b1e15002fe691c0fd34b9221d3517370c14e6dd7faf6d0cb94c425338a516a191c04ba87733663d123b4ca83b6a6a4b006c0b071db66f241b57ae9be5d52e58104f6278a605b16faea87e972762bb991bf8bccb03253a43117209fa722e9a00a916df85e060713f543bd107a38210650728cccb9d1889002dff02d902da0ab9535191d6f0f2fd0bb2c3585b192ddc6532961ad15fb58615c86df3246a6527c0c8ee2b8f53b8ceaaba62b0dfa808416ffcd3ec47b345ccc5b3ea5b1576075e13a5a2c0e0f745fdece0d98c0956b82b7b3c34b38c4cdd02760c195a6d74e41b01629cba8f131b8dfb7d619b5c9897cd3b0b6bf2219b74fe7b8b5d7cc938c0939710a603d432190894a8f67841e1398ddf1e8eb7cde2e4faa05e2abee3fe262e282fed6d1edc926ab8ca0665851041ef79f27b0a2f64e4b7de8c643ad4cbf3388c1fadf3dfd328f2058a1f7a57771fef3e6d9998bcb20f31e7dba701c620093ba4d54c41346faf02fa6790c3c6800664f228cd459285e26d0419a5f92751eb4487d9aea24f6e2d0acd1d1173e0cdad822ac4f8237e80418d336ee16864985040b1d088b2fbed7c751ed0d1d6a5e5e11235f87149d375e52a6b39a10276535b90bb32cbd152f86358dfc29a161e5a60f017bc2e66729b478dc530a23c3b55e3c78763f0ead81b553df7c3c966053e636363e41fe710f7e680c0467a5284a2bba5012e9dfc12640d0f7ea97f5a47b1c30b80548b146e6761c41357104ca1a607e9f025cd9dfa2bd7a478141c989e93a8c2a958e93b0a1c7f96f6aa41b19454c87279c7b092527c99d0006ea0e0ac0d1b344281d6abea42a0d7687d2eb7e2deb3b365504b1b8fab996b3a358aaa8df2e6a3e476891f113e80fc1b3da0d42f2fdd9d7e32e6a790e5b98edb4064e491fd338227f41be683810fe973707679caea712800540d79833f14073f4c65d61430fdfe2b0a6317efbd73901ce2df632c61cd5d12f21a717bd7be8371be332890175d70b26d62857d784a3b2b73c54057b4cfad9301052a689824ea27fbf6c781a22398c90184f6244c43baa66226da378f6c10806014d890f61bf53711b9b082a7570c5245c411fc1b5c40ec11396d615dc1d24f88349787df641a78ce8be6f25970086500df89c6da58fe200b05c96bbae6bc21e68cd282efffe8a0cd44f74d99a8c3b18265a30dcd7df038ba50210842f5fcc851e14f1535b5200396266cf84a832d44dba0d4ac6c66fd5ac6f3704bdc06c419c8449c6c23aade329f65d0d70da947a9124ae435074d664f59ac234d8928f27de4bd58da23b6177ee54e1c04142a56e2a477b5d4d3675b53a989fd0512b8f84345becc5a6abce643d7143f575254075fd8a99dc489b58b6417b6cecd9895842305751eaa3c1c3b573a1825beb507166a51b6ae3799fc5f0f2f1908e54f1ab12d1b9f57a3b5dedaab46e843e43aa08cf6fe34c8b4bc48eda4cee7a9ea5526fe74b2a618d0e6a31da53b1bfe5f0bf44e1b1e63a8ba212eb652fa4dd2b0a14ad3f077888e9887fd070b8a64eb2cd46872c177b27c3ed5054624f679343b631e0d509eef5b870ae78bd2a59353d4d00501f995724e8c0c8cc407a83b4d55bac3e42068312d20033f16872a22fd699956911120a99984ad4a2dc25aeb36f1c2137517d81d966a1ea5620513b002191a75fa9c8eb9b24ba62a337fa866da2456213ed0ecc6c83533af76705154c29e7b8ba1c40a11f590f2300e4cf119c43aa5511511591c569457c39b6dbb4d41f4800a0e02f882e1d1d191ba1c1d3dd162af352125f534339035a613f2c10ef1a9be2564ad2b049432cbc453b6189025f3f625a4690d75dc283b112ee86829b500dd9370fff1e1582d4fb1e1808290990a8415e2c7916968214b6fb2c59d3f7e31a192e03163324fe2712030d8ae4a1104946fba7354b1aaf52f3fe630bec0588e7c10c4998b08d5b6c892e478b94bf9e218ad8870ebe50688f17f8653592889e4db530767412169da9864d6c765b2260974165b44134863d181b28645ff3873fc3d9802a907510a438718927d00eacbddf1924d4af0a57f50334d4e1a510a45fb8ddf3f517556e73104e2300628e5849ca878a2ce2ebd1a2cc82e672a4d92c7482a8074c8afe625f8ff6aa38a194d535cfdc2599c08dd8954587c434c89b2848fd272ae8721bc037ef2cce651ed14bb336ae2ab905b508a5fa196b227baca18f57140b2e534c810bfd3db5a1a17560c7a1ca6074cd048c078a72ad7606930f3628f454f3e3cb012422b0281a302a4cd9a1d04511be2ca7be8e72e2a20131289690916372b0a7aaa00842666250830c94f9c7fd694a2c09c1777e2b18cd137e0f6f593d871bd84cc2aff001e655938219cd832572828a47e69781ceb9b8090cbe9291989804964f7ec0dfcbfc5e3f9be71c97568491ae148a372f9ca520b9cc6bc82438b9872d75460fdf4ddc3ca0065ecb792ef9b0e210c9bb6e2e7b330e8bdfad81996c14189a4d6289a86e93821025765762f0220fb0ef4dd5e649421ea160625696044de8d22f17a7a77035b64b6a858e7b30292006167d34ec3341b4ec86b6110e755de34f7ec02e5b6a48b18187223bf7c7fc13e5a314275e31b344a100a14e490f2f0cce9aa5503bb711544ec342404f42c45e16cb2efbba70f931cb8d513cbba6bcb5c97798bac79cb679a6736f9fa0f030d78f40d4b0de91e7094f8807a6bcb894ffc1d7fbbf7d7125616e13b2c5dbcb9ff54885c14cfd4ce819f896f38cf0fd341edd0bffc31ec104e408d59ed0bfbdbd9cf9129609e969a044929af7e1492dfdcb5f9cbb0b55b4b89bc317134babe2d0ee98b426400c9ff917bac162f4b892b175848c0280b15039294532f4b6ca855e82046152bde1a2997f202ff298c30e06975cb102a7796e42adfadad632f4009ab8f20a0963a4a8b4a7a653626c9e92616e0118ca38a164dde4bdf59c9c5b38cd575870ad84167712833ca16eadb60606bd709b622ec87c45ebe91439985c5f00b2965e2a66a2f69131023a21cfd286b754aa3ee99230b04a0119023023059961c939e7b1009f16dcfe0307f2570209ed318127e152ca9048d8a038315af60f223cf90f9922c86fe9bb5d6f68d5ca935dc539c6eac4404a51841b3f60431a2c2f417e4d7c2d29899a65b90f616a159a209f7785468777de82c1010781509aa9a9ef2d2ab4da81d3517e626f41f05d53fad25f27309c6a42349573483eb72817202846bd52d2251d29818092634ea99e01976226f88e5cd28eee2cc7e5f56945c5d634ee76052fffe0aaa98814e2a9efde48b6738c05b6f60c44e324d4bacacffe6f22cd5eb0d90f44ba62933b973fdff060c591d2d97c4c348d504aeec748da9d0a22a350ca8ee3babefacd907b4d86883021625d703006e39776e7faa05d707fd7322ed523437f00afd4ea1e24225b2ad9131364150db1c17e5d9ea1b78871190d18ccd2dcfd40f3cc2c714ae310553d2c5168f0af9f045f16b5ec9832e3608edfa52a3ef41ed8002532e57b30745b721c58bf1418ed9c12d54af6d2aa5ffb7627d0c4fba8eb5ca08de1d01b3533cf432e954dc012e2cd8701954a145078393242a17c05976003be6dec6912ce1b47fb4adf1851921cdcf305d0ea608e53c882435036daf1b975dc742c91f9176dba44d4219321aa56eb7ad548b2da430223842894bd8e70503e2b8d3bc18a2846099c6242bfa1c25a0c22fbcf86a426d33ccb2998920c3ce010ac0033919b8de3a15cd6382409e5e0408c4795d27ded8d801b863e564a8bd83f804b15af5907faf23febfecc481a74a331e86ad982f3c47ade07c2e810f8719d22a4b09de5b0ed690e6ef212cfb0e966f0c990b130503b2cfb815ae6d818a4812e72632ddcc6201b25dd18043994e503b1c7161ba16cd47d34e7bc027237a45e78da37304383dcd87beceaeb658c0520873d8508548095c7d732c940d6f8afcc70a93ea3bcada49a8ceb93b839d4a221e3979babd5bdeceb42e18434b65fde67836e181a50c95f5ba3a80fa3ca0b43b7f808bfc8017f7f5e820ace6b01a72cba7d8ef6d0b8332c79bde3dd38875446bc46cc9ee9af57d584f914dce519547e995b32b65c780d7c63337f2713970ec9371c4a40d345ae10541e71d04e84e377719c435e0c5aabe7dd8604a960a74f39fe6e7b1c820ee8d218627ea5453d3c1d332f2170997dec081a8848ccec173043fe990119e84b3743f31be993b69c087cd53a3b35fe4356bf13dba386888add28754089e08d505c051c400f6a11127a7b7ab026f7a6ab2cba6f1e777433e05b0784cad07c5bfbdd4971b20e4659f6b46b268008ae1d3a0f90eb9b9888c0af1ab3fe0550f1b0bf22efa2930cf684241c3c0479dcbc0416896eb211b709747287dbf0dbdaa9a383637330caeae9b92dacbcb046eea166d50053c40e342b5a6e80eec5ed7474e0c2c0e192ea409ea1e264406f65fc4e18ef25ca85098039f081517131aeb603f2c10b33542292bd4a195c70e571741e41dc2eee4d2b9a353112a04f5b3ecb54491312bda0d3144a37019ea7b8d129154c919f363d712f13433a51204cf9ac82b2b34b46865c79386a655ef4000c65e8030cffce26913ba4030e01770ad4fe0ac378fde44b766c4c4490c393c93c0203b0ee8202ba08a4890b0156520dd8a0dd17fa8424f6fa2e3886a9b20e0f3e846922d36d1c09a278d085bd12fddd5089b1b346485372b2bd8f45cb54af03343c0b1d1d7ddc92c61988ff0e035facc50c7160c0b29078e39bba70eacc2e80b162091ff83829ccefccacd21965249aec3b79b17fcfa7238e5eb6a1d532b80744be387c37578a353f3db4c63398d46553a9ef66329b7b4a7da0e7b27d3bc31acfd751d45517cc67616d408eb2e959b51037c6a69565e7f1e72288bd1c644f87d131bbb6a121285defc85d0d199c6e682ddc330bfaa21446dcfcb8acc829fe3b5a38438745e30e88d3e9328f19fa39cd2566e61d9e83bc2666e5796627dc234cd6fed79efa2eccfff6c7fd8d7525fcc5c96ad801c9ce91761b69eb7b1fe1e0a9fc17d9a71718d48c20b4b905992aff0838cb861f75eee65a7a5c38822d083fc770ec0ff1898289287d84016c11ebfb21aca281efd38afab7e50109cb48d4d6bcf36b717e68f8a586f4bf78c38a8bc9de630fc37ba7b407d3b8db7b78384ed2b6e97400f08d9dea0d8599857048f3b8004521d5a9a8c13c83a3bd7d67dc59d67b4100b9898279af4754fa6a7adaa15a8693713062cb8bd4a5fc65c161793384aa960cebd13475324cdda6412340dbf0a15577dbb3939fed6b0a9d827fce4ba1896c2a9c9d6a1d431f67576b4519f0b8ebbef764743f38bc421cec12cb2339b2e07cd6b3c4d73195a3f96daf9183dcb78735168aaf62c97d82d02ec1c8d2c9e8de895b320037be362b041d0e724d94fd83d83c154bed500b0413bf3ca5b561be606327525e1e7613c6cb7a494843efd5ac155628fb63e9f8dab7cdc0507697fa8ae5fd52539efbbf812db00588f0f843ccb6f21d7f79cabfb3e841779b262c928b893d848faa0fd7dc47146a88e9e843eb40f3f2422e7a51f4c1a77410272c038396101dc06583d57e9bfca2e32bd9d7aea0c54ef08b6fbf95104baf7d07e2c51640124bc51118cb80ee5a19df8b23983ef7eead29406b4b7524743e52e9a1d0b707288b2944993456c52704a2b869137fee9052df818fecd8bbfe9efd5c3e0cf3b65a42f89a2785687c11b2491a3ccb30a6c7dd00bf2cc2a192716909e3284defcc59d7392e52d9c62373f8ce3eb1020b63b917d1d656c1ce9df623db0007bccaa7aff39d11dbe6f62503c49256a2da52afeff49d8b9f3de696bf4cdbb65a0e30f9c8db43a929bfb96bd7d9229f457603fd89662f72d40492707c248291f50f69285e37105b0aa22a6a137a86b93e5cc0c1912bdfb07b3a0f1848d39aae756b1d22fcadb987ad92175129d2c0bd70bc48a2fb9c2390ba7d860d85d7bfd3087616cc2e52ebe20b7dd2b93bd63ff0335765cc09ae300242d3bf0c48b599718ecd5aa0f382d5a6ac31af9a9a13d50f18e62d1aab85f64771bf9428edf79b3905953e3ee78fdd7c186a666045fd24f8204de54fa0a4a7dc96209dc467d83883957b2b3e425230721e8c8167d7071df6d6eb173341868aff01f151de97b95a6cbec97e5b2ca2a29705afc69723b334cfe8bb222c6856a1bd24777ba30d95111e15f6bec3bf952f3ea12b365b4b665314cfa11050952478043cc4c01bace3a7d09f0a624c2b0084a797e99ff806a49b859d1eb0c50719107da49548fd270e8e28adedc9eb5c30b2d27308906111cae34fb0229854df00a98392e55e20cd75bb16e2c240b91c8754a3977b9cb8ec728f369416c8606b242acbc21726b3d610c223957d61421c8087bab5032b4b1a5d184619b7a1a9aec7069928c47322d8573d8478223c7dcd6debfc464592ead68f065368f9d84352001ce3dc9c2f1698cad121ff6db4baf54e216e2ea344059e9ecbe362e468056729a506f104eb04b83f94802d4278c851b18442d8aea54ab20f0624439f90d559f89a53bd9a399ee19c4b7bd6f3e3b9a8600ce16191662309047b4f4651a7804bd16f49215654c7f3f142a499a54c3dbfd36a3987fc23842f88c5c129b92a3a9ec8c175a73abfd3d40f979a4ab3b03d80c4e685955955f2dce67df13af5c4f9de74bdd8328a7ae6683949d31c9819f4f6449cf28b596149ce254e11fb5a0ce00e2130f5a0e4b2e63a2475cfb7c2649d147080d5f7b660cc381b67c964d54901594bf2962def2b7431c3e020a7bd511425d7c01e0a3bc1e3ad989ec24a0d41f47c7cd72b0abba3e7ec378c340e34649c4a276c20ab7657a47e4fac5d2f54eb6f32aed71164144bf2db4afc99f62604dc880ec2f5a476ae37c1ca0cc685ad7f26e1fe14d44885c6c9111b5524d9fdde4e41eacee319a1632aba29c2c42ef3f5939a219ab8a54567feafc94f9a752b8f61fd2abc8ad9aa7ea3a0ec602fa6c9b5443b45e1db898857202da641265c0f5a0e945620c6b9e4f208140807e8843105cadcede212a686b58e7e3102cc15ba6fb4c1824fa15eab43077757b72c5f923b0766142c4a2a5cc448fec48b123f8ec7ec5b472d2e29b7a541335980abeed5b77d45f30573c5100ac6af007d3401aa789f51df6a9e16f68c8815e2ef69ac8e48dd110cf0df1e40f73a5f5c9002ecfd1ed89bc4aeb30181f92d9f22b28ac95eaafa460f6f549bec3abfbb749092448a3f1825f182eb6c1d7c2eb0b610a728339d58e450e632ff8b2735a406949000f5906e9e903cda34e4630f032d6e8e469e3fe41356d5f1a16649728bf1fc3ceafb29963df8347da3a522916ef51dab73c9ed33c35bd824c113e346357fc47fd916c6444cf7cf451ddcdefc3481de46484524d28da7822243f0a71ca9d9faa1765393d062fd1dd04f1fb2998a849e0464d024ae56708ab6a51612ecb91be9aacc7aa7093a1654edc710447271811912262936ec58ec2ef769249a93070cb1902e7e572a79af5a11d0afc5d0e53750d21b3a3dd8e773f53204bfcf800ac1309bec996082b8d8da9d90753a945070b364e28ef3400f1cffa75944a870fccc235909840d16d0fb97f1a27840159816e137dbe019100d0aa4b93342f70c3352c6a9954171fc38d1cf322e2887a7aa447b7236ef21b631a6e35309ad1e8b8c70defcb9a3e52bc9dbd7a3e87214ba59addd7437349123218900065fe70d7d3dcd63d739fae9d286ea79c5a4734681907fc08d5ca15cb4f104b217fb78f3a0e149d41197461739c6cb433838948fd8b697c6f4c58cfa692d9283dd592fdcd670692efc3d22b10cc5c8d319cc1751f5c7a7951cf087057b0cbf9027b77e9a3edee6c61c528cc066e2a769e68aac43020e1c2b690ae284425efcc55dbd005daf82b4e97af101806b048900556edbe69a4e5d899a5ac71164b4027a28e66d56205ed5380791bd0b3d86f1ece87c6feeb986c51be6f5c6738a217e4c5ba88d444eef6860a728e8e84120fe1ee1b8072c5e44ebb98c8ee6fb307166676d2c22e901628a451665ffda515f652dd4444448681329539201bc06b806c506dd2e64bfb8a9fdf735496fe943dbcdc5492fed85fceff7683fe97753f3682eca4fd98d03078e8cc3ad9bdb69eb19a2f9e6ee613b208bb0c7b66ddbb659efad6e5bf9f6356badfdd3c4610f0ed792e5f15cc79f4261854d3e3f6bafbd108d3ae9d12c7d24ee89c6f18e131f3db19e3a692c0f81cada13f1d9c9b3ad15349a1532595343d6a0d84292acbd0d1c9d9bc50923f7d3a629fdade5c89ae6a2d6c6c9df7092a63d106aa259ebfa5a3d837bf7debdbf155cb0caf73799ec7fbda0246ff6ec1c5ef7cec9ad0c490fc98d24c24d6a849b74e68517b097278e4c57fe8a09ff44213145e1906317f059b7ef3f32b660af87f8232aaaf458e930373dc3601c25c392b849ed4b8aa654814f3b33a47b252cccb752f8fdd5c649a775e53b93dbc65858cc4932d4f4056a02b507c1f26cebf2b79c2bfce1dfb97bfb82a9b2aac4515c52a45c8997429b6271948af2ece10f355b178c936c9972b35f4eeaa889063445292dbb22ddff5073f8f5487571d20dcb8a7231051ea244674b6361a314d89fe6d3523be3e20a9842a1855766e18aa4eacb9116234550727d71e1246cb3b87992fdc5cdb6299ad231b8eb1ee2df7dd738c9869ae2404ddc7fcff9f99567db9920fcbb3be3a67ff796c68e5cc07626096b699c14f212883225b32f2d270345e957c38503a6c3b882dcfdb588f75951b525776571989362423fea4ca571d209e676b98984ec969ba9997e19a0b1833b5ad37f2fc6e3d8d5ec1a62c0a111d8bc7d08c4bc05b9bf9523e810109dbbefdc95433cc4bdbde5e6d5c5494b6ea4ab5b1d79b6eb3135d1ace3de7befc5a1430af8e628829628a594d2710b38e4c3cd94a6f8dfa76110bf43423fb8f79e2bb3e7f7a46dc8fd7649eebf626b100ce1a6975a8aa620e126e866b7e89e9aec154a70b97b1c6efa56fa0301e2a67f3681b879836fc901276967e727c20812ec2b506e76d7b1e1abd55951b9bfa25a9c14f26a1167f910a8a93a41534821dc7ca9a84eb9f93f8e18a3eeed54a7fac4ab56691835e880ed73ba570e7bd8905c41f84ca08047b8ea531054a5fba62277ffad80d094ae3c82c2c2a761197e476e650956b2872927727f48ba6665562ce8e61cbeaffbdb2975a7347461fa5fb594524a4ffca99fb8d9cee9dfe14d29652277ed82cb4daeffddd784ebb70f9a6be852a0a213aed2700128a8dda8b18a9b446c265329640a45cc48fe462ce0a671d3ff73e18ee5246c1538095b630d77865b7192e556ee4c9dd14a4da3d9cb0616a8a0df91b400bbd99b6fbef9e6db167a9db68538df7f3fc4298130ff0bb7cddbb66edb2e696fe76d9bffb6f9e6dffeddeffeeda1effda9e926e7d66b98016b6fcd0dc50215785f3deebf16e07e1fb5742fc65ce5ba7c6b90016fdf0f5a013ecdfca1f0eb7f1f0a4b20cc5cbf4deeb95b72a6e9719d1337ef9b366e5013f85ca603c8dcdbafe18ffade91ec793f82ce60692354bfff52abd530c30967cbf0d193fa9f29a9fb5a3f77a66125bfffaa403284902a5eb2bf101e0af13df8346cc0f760f915c5858c5da8836183b196b0d205426edca8e54961d73538787a54ba6ddb56378eb3b01ad84cde018b65ff1e7a803fb4c708d8830477ef51020e7f7824e5615f70019f98ab3c7ea8bf715bcd4778b266c926aee7052ef7190de6f34401db3448518314e4fea6a1260acbdd2b9ab55ce966f70aedb1513d43bd888ad25f87190ffd3d63b59354cb7215aa3ec17622b95d95fb35eb4d8622a889079ad2f78851071b75d37dbb9aa66d7f33e78279e5feee46f50a4da2b3db1450937f27e1f9ec9516034dc9d1ffc1b8ab0977d55257368da3f4b792dcdd049f45d492094ad07d0b9a47f7fd4bd452f7fd4620d1dd0ea03cba9e999959e919176dda7c1887821b6ca18819e83007334c01051f8d0441a5821715d82ce1e1da18827069d720c17a40858cca9ca27c9e482145761ad95d8a37649f620a2a84605a23d504b01d94739d58ceaffb65b28429587083832b9a1859c1861fec6085652587203cb169622045a37b4c12186487e20a65b8821523ec48178527dc937d1f9284123637b1128a000fad44813cb4c71585e6d00f31d717731324f09b8f9e9cd6591deb42b3665a8508d4d4651d03d9047cda54cba62c13583665533635f32d86afacba5287f0604705d6deceb6ef592d89bfbdff8092ec0ba8b9fd269675e6cf5b9e367ac6a2c45eb9e16dfbb6289ab23df641c36a29140111bff894078dcdc86a44d2042a10bff3980713e2774a1c0a11cb5b518a10a26e6ebf6dd585a26c7f591b6bdbb66ddbb66ddbb66ddb72ecf021648822c826e06dfb8bc7dfdebc41fbe491d5120a786c2f9228e06151d434536b36eb0a999023dfaeebbaaeebbeeffb6074e0c4b805ee93c267c58661bcd87d0d8a191a0e17ec07ddc87abea8db7251596bf1fc714432b24b042c391927b39cec1209b9939e37918888aa25339489898564cfb8a93d00aa6df112f77a1d79bd648e4592396f574eaaf19cb73ca0278ee6e44feb83cc796b849a663ce7ed8a973815caa6aceac64925137ae25e5ee830a32652c64bdb6c46335332b38939e9b5404f1c8b1531d424a3e5a52d1673c55e31d88b936220819e361b9b24b850d3e8395f59bcb4b55ad548ab85237ace5719d0d31613536790395f8b5053a8c64b9b6ab6a9ea4a452971120c34d0d3f6f2a286cc0125f192369bc5cc6466342f278d54a0a78d6563a9020c35bd6079498bc55c622d8e8be7bcff809e341b9b2064cefbca8978496bb5bc48cb595a4632a736a1272d26e68accf9bea126cf73be67bca45d194735cb9c165ca027ede5c50b316a6281c44b7636839925e1dce77c3b414f1a4bab684bc89cef176aea3ce75be5251b8bf52ac6e2601b1b64ce370de8c9da28e1257b6f88e4983664ce7b809e6ccc73387f04357d323cc379215eb2aa972b64ceffa027fbd22ada53983ad3c1791b5eaa33204ef29ef33ea8697bce9b4e02ffdc21739e53330d7ab22c27d4e4cff9eba51ae37cd353b56915ed399c8711f6e094ae5ce345f3344aeef5e25e27254dd6de678825a7e25e424e15722a0ea7e25eb2f62092f3338e85330b67e18c63c9da8f32c218311b4e8c130b633659fb172350b4b562c2adc5d95a9cad1593b57711fa606caaed25dc54e1a6e26caa0fc81b671b0b6716cec299e745e72266c389716261cc266bdfe273abd68aa95a8bd3d25a5a4cd69e85876ba1a9b4174ea9a95eb2f61d16db9d692cb3d94c63c9dae38e964fae6563d6a6b4b1988d599bacbd68836d3eb9966dc5d8d6b5ad6a5b31e277863659b351d997115bd50b67ab619d59165c6796256bff919e3f10df9ad04137352d94d9e135564b5766d3a74fc98863366428e3a6f622f9d9c86ae99b5514ad4f9f1ba88af6dacb4699ec83034df99464edc1af060ed8fe29ba602aba685ccb496708e35c4e3a41cf1c122791212c8cf1d20eaf7d2813ce84344e72d023b55c5bb514c22a0aac4fb855b4ff6a2a8af6adb0b62a2c6b3555a5cada7376f82f243b8364a51c35b78fe20ebfd0226f7f86b1ac81e10f9f37106c93e6286468e328da7f6458e3a616c2605f4d08fb6ac61a6cc067133b3cf7dc87332779de957a1aa73e6973479ae40ee4483e7962baa9cd84b05af2980fd36a8d357c359f8d97aaaaa2d480af7c7a6ab2f65555eb2a6ba530c651b4d74e702b43989bda87a587208a8fa39dbb0450c003f4b02b3d6e7edfd3e0edb5fffe9df4c39b4eeafefb1327d1fc7defe37b27bfeffb93af1c42617e86584c0d4abca79ee739193ee769cbb8e979b7a8c9dfebca56e2057cdec8de075a254d7fd3c6aa793fa20177ef7fde905d4b7e5fc9245a23cfeb7db74cf69eb3df47dcf464aeca5ebbbcb8029f2dcb2df3301ebdbfe1a41f680e1c3a56dd92bd4e654f95bddf414d45bcf7be514ef29e035eb23c3caa247b36d9bbc95e917cde3f6f14d9c3d97a940bd97b7ae2752da8012a1653a286a66eed0df3c65851ec3d41465094ee87e2062cb95a2b84566fd0147b82cc019a621fc482cf1b481c11a303f42432829a9c7436cad69aedff8946453646b6296d0e1f3d49c2b6c7cd0d879b1ba6299b6fd8b7c61c28c2cded417f76aa53f5b76fd59093ca20514b3b8ee8003d11610435dddfc6dff076e2ef56226ee4deb61660c85bb6218661bbb7420c5c995a2106dbbd9c095f1f949a6aafb437d012ede8e8057cbbd10bf836129c2bf79ff81c198a25fd069f72ca236e5eae51df459cc4291bc9d768c0e08f639f9eb513f56ed194fba30c83cf292fa74bc23679dedc3191067c76abeb9525dcbcff820eb85bd7311ec76fe54bb3b54838693ba90d11b76479dc1772af9f9d5347be49b220d33543f868f67c72d2264de9cf6777a634c5a96da7b561ce8269144fbc3cc1ca348a2754997e9869144fc0e08995dc0ef844ea258d7b8a5237b2a2b47b576afd87bce75a7e7dcb23261623138ba19969f75aabb55a6bdbc671f776dddd3097077e2018861c8e685353040ec75173e0b8c13992e98bacec2d4d9efee2e22b37e9dbcc6c6e663635302f98242f9818988ba555cdd2465ad52c5aa7babb88eef6f6eeeef6eeeeeeeeeeeeeeeeeeeeeeeeeeee6eefeef6fe5c4ee847eef214810a918f7a7fc00d3154922f5021239344c6dae8b0fe57cf2f53a7a10ffdb53cbf0f73f8c8fe4d6d0757f3be4651fabdcf8896f053dab704bffbd8f36858620f79448e4cdf87b6048b1434ca49ae9dddf9731b797b2bc080a6d5ad462244082ad35dab71f76e9cd08f7ccbf30458bb17634debbedf435d0984d93e4e7e1c35edfc84b4d42b15052cf188044fbb04ad52ffe432b97870e588dd52795e5279620394321ce5e9995b29a24f4c68ad5ada6215a54f4bb44afd02a40890ca0348dd5029cf110ba03c714a69a33c3d6f2eb54f4ab4ca863aa24f48b44afd0094330028514ab33c7d87f21c6b9434ca93f2f4fc7dab237dd266be953673842449922449f2699092244fbefc9392249f2449b2243b9324f9f5b519922449f24f6d0c211a7f42e3a4a42cd68cffffff2749f249f2618441c8ff194ffe0cf2ffffff67909dffffffff43274f9e90e569593348cfa5976af1525e2ad51263341a8d46a3d1ffffff081406f9d188fc7ff247a31f8d46a31864e7d168341a8d46a310f94f7e795a22b2954c265bc9108944229148341a8d7e34128d61909148f43ffa1f95676791482412c9203b8b44229148241285fe473f2acf3a2383f41ca37319e95c9dab73b511182010080402814422d18b44a0a7414420d0e8453f1295676710080402c1203b8340201008040281cad0e84523517956249d4ad5a93a55a75285c6711cc7710481400f028d2ec220a071143de845a071fc711cc710d9791cc7711cc731247a90a8f4c17355e5f60ca386a5a6a686c5c5e7f3f97c3e9f711c7f1c3f9f30c8f8f9807e7cd0f8f9fce7f3f974fe7c3e9fcfe7f309817e048de5e936b95fa9d72bf5c2e3f1783c1ecfe7f3f9cfc7e309837c3c9ef13f3f7e3c9ef7783c9ece1e8fc7e3f1783ca1f13fe3a7f4ece2ae8adcd55ddd5511169d4ea7d3e9743c1ecf7b3c9d1661104fa7f379cf7f3c9dce773a9d4ea7d3e9743a9d4ee8f39e8fa73cfdc80c359bcd502d30c61863dce974bed3c12cc2201d8c3ddf794fa73c3be31263dc19638c31c6189721cf773c9df274540bd2330b1811180cd644386118866118628c1fe3b01306c161d879fc1d5c9e9dc392866198c3300cc3300c439dc79dd2076f1a8ed52b1c8b63712c1104411004c1300c431087414210c41f3e0e41f041100441100441100443f8431c969e392b4c702bdcca8dd7755dd7751d08820f829d180601bb2e7cf043b03cbbebbaaeebbaaeeb42e18321589e6d24b6442c169b7df7de7befedbaaebb9c3048772ff8dd83ddbdb7f3bdf7de7b6f08fc0e2c7d68cf1ee9f9db5c94707171b1d9acb5d65a7befb54f835c6badb5d65a6badb5b60c757fbbd2076f542d792d24faf4ad5aa57efd11636f43e5ea9ca6699aa669d65aab816110ab69b7354de3bc9686b53264df963e78be4153da73083c117927483f2070ae17ec5b99dfbdedde64088368e52d69d6dc673c27e4fae31cb0bfd75a9dc0727d7140a8e1067c9a59048e265ee5faad2a0a777360d7f87a98d7d27297bcd6894cc0aa961440516045c8f50ab9abf6c9003b5c00cd5ed670033ebdd6c609d9ac95e709c00c741bb9592287254c983061c2840913264c983059b264c99225b3d96c369bcd66b3d96cb664c992252c2db432cefed5d6fa7f694aa56e3a16023aad1ec57775d0dbcbdbdf52c0fdf53618b2d5e8a9655ac5a47d7797fdf57e9f9e3c76cbf4899c41536ccb64fbf4c737e0fabd622d758de2fa5ba5a6c689b4b7323451b6eef5737538b457ab6ddcedbc0f0c39a22bdbfc7558b4f07c5cbc18413042a2510c1964f536416a6f502186f012caab543af0c332c5515b1545a3a70a8b6915ede97fb515d327d10557198e513509aa6e663634457b8ffc6a680ab81207aa22ba80a925a20bf7e25e5c122f018006872bf76a15ed31ae2a90e59b7d3637fe2a92b52792b53f392b64ed472de0f3cabe9aae64bef69f09a28028e10f941cc670a0476ed9c93016c6e0aa92f1b08aaa5242136681a694216c84c3776557c9ad7192ff2c8485316e8661cd8837500b7f7090c551526ee252258a335e146b88345efc5eafd74b3c79f1937d32b17cf163b1582c51c68b1fcc0723922f7e2894288affe2d7d222c678d193c9c4d08b1f1171f4a2f79abd44d18b1e0d8d08e3458f15c3125fbce8c1c088a0173d94288e2f7a2d2da28b173b994c26133f2f7a444451ec5eaf97d8bd44f0c58e86466cf162c762b158228b173b9856d160c47eb14355140d258a1d4abc2f8a5d8b785fafd78b467c0fd0d3a5e91435d5171f0927755efc23a809bf2863b1c417e2a5cbaa281a8c7861c48bc23a50e2dbf0d245017152f7e2fba026fb628b281365a24c14dfbb2de2f6222713bf172f11d15f7c94b0873883f36a581ae2c94bf69af17a992547c6c782f958323ed6c7da810cff437d2d1f4ac687fa505f4bd6be460c3024fb88c864b28f48d69ec6e813bd685ea1d78b266b7f02c37be1b160582c164cd67e06a81b3d54cbbff03c54e9e27e3a994744d6c9c44ee611c9da937100bb5747f312cb174dd6fe5b6c2c3a560703762c56c7ea60b2f6323a0e58ebbab1437528b16bb9326aade5c959e1acbc68eeebde57bdaf4bd389814511e6b22e8b552f4c5fc8421dea30451dea7b6dffaef63f77f7df02fe566c69898a62ff074ad35255755dc0f5abb5b38a626f5d9955d5bd2a15f8fda1e6d4d48921dbe7d40f6bada58d90f6fe34a479405af21845b13ff343fa576eda2e9de5a67d4a535d2eb0804f3bc4ed4a2de1b71a11270d69bbe224942d424de1dbb732a0268ef81c91538641f4773487fdb5c6ebacda5495911a974a558d780957979bd663b52bbb3a7392d7eaa29d56759ddf50fc7ee324d0491a2e6343acca4dcb6257462a6ccaaa685cc6672a114bc44ddb306edaee4ae3a6ad2e9a6287d457a2b3bf1dd255e5a495935aaa10a809fcf0418d3cc3f2ec4e1844bf776f5396c54dfb76bc02dbb7a958a84dfbe0dbaf3227d5922781a2d877c29d02be42b6ef599071ebd6ad9295935a6cf73677f5ed574f7d350cd92e77b12f033476705df1989bd6651cc5bebd799b5109769a6c7f46d61a65c015c649677d577d829abab7f5d5df39df3088f6ef1e41e71aaa7f86ea67e697dba6564553ec57224ef24a1bf55e8cab6b7499fed4b448b889ddb449d8acca76b42b3882f0afed55fb1dfd75d3fcf1ea8d8c27d7e32df893ce9640e5ed9970d26f7fb2d9c8e2ca48edf7406e2fe3e11cc08992b797126c9150e2d3484b4b1f4aeba6e7eda848282144dbb6af24e995d6ba694f1cd9e2a8ed9af7367bd65a6b2dd7f1b8e93d85225693ad10f7d210378aec57cffbbc75de7f5e577a2cb67f6f20051715b65a6badb5b6d67ac35a6badadd65a5bad1da7c0d65a6badb565b869d2a44993264d9a3469d2a44993264d9a34b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9a91f06b11676838f822bb97ef924f9ff3f1abd48f420d08fe37f3eeff17ca7f3187f183e087ed7fdbd6fed6bdaf749ad1e924d43c5aef1e5cd74b256f9569dac4f5eaa55ea8fa94ef6adbe227c2caf062f2ca55e0d725dd13f412f75837aa93e518e9ae7674554383d57aeaf6d21267b2e279ddfea5bb5782d0f06d4c89a6d6895581ed052bb08f1a71542b645b04e34e04f8b8464424b1d13f2a74d41b62fb03408e24fabe2b4404bbe02e4cffa855ccb50d7f0e3cf6a3342022d39cb477da246d1e3cf0afb92c37af8b3de20571e5421e43feb11d0fb6cc79fee86ec4c6a0a0ef0674589a8404b75a5e34f9722bb16fc0b3cfce9349f527de5f8d387901d09ce0403fce9aeb15471fce92cc805f8d365e03718c09f6ea453b237fe6c31a8fcd9686837e4140fed9896a2a960e3cf7e42f66f2768fcd92e3d841dfeec1f987fb60e427cf2671729ff6c16d026340ed9df032efea4b2d0532efce8c07ff107c87e1a207b4a76f72efbd3b8423cc0012e0d70111224762f1ec71f2b3e7ef081d583d543795e3c8e19b62301083880cf749c4c2b1e56f7e27134c00b47010830805a73434500aa14d5bd188fa30c13e371a4616527a972460c199a16d25c68ad17e5296364519e236e519e9873f1b779ad6ff545e16b7dac17e9b5648c783b6d4b26475eca3b82c7f2569e8a5c7999e7026f6556435699273d8f48cf1fc8158618ecd51275aa25b8b0bad58845447a06919ebf4f4d37830ed5adcc6ac6fb3ac34c0c06f224b93d7f48cf5fe7aea2d07261b13cb39b839bbaa8bbf221e2f1fc85b036c8666233b9595570b55c3a8d04939e436e85f301a7e2521c4a953bb6a4463613529bdc9e3b172a5eae56e7b5684a7d2678b126aa5845a1cade13b9d61fe980fd4f1b36688a467ab6a56da1a576d9c09ff68806feb42f1978bbba518497ef6f5768a96318f8d3a22ef0a74d59e04f7b8383089f21434bbe52813f2b0d05feac4a26f067b5c9fe35c7101ea3454bce92c09fd515813feb0b022e7ad72184cb78ffca424b0e7bc09fd58803feac2d0df8b31ed921c461d4d092cf82fce93306fc595782f077253d00f150125aaa2b20fef49805fce9323ffcf3eec3c79f5e05186a72c1a2a5fafae14f77f1e14f6ff570cf3b901efc7de54468a9d628e04f2fc2e34f67c9de7917b2c359ccd0925525e0cf9621e0cfae3980e377217438125ab2aed39f0d63fab393f0e0e1fb10ad5273f8f70b3571debf55b46465a53f7b45fab3590670f07138114ad09236803fe90d01fe6c22b90037fc3d404f45b44af7fe475093a7f2279509e04f3a93228496b45645a936fc7fd013075aa53eca9f1406007feac801781bb4a4cd54203ea8e933ffdc21ef30d2f0d78c68954a9b9e8e68953a863ddca596bed5064a0d941928cfb1fe8d3ef591226e640c94e7ad17282d80a34fcd2202c74a2d79a90a94e7a5c0ca04ca73ccd1a76e19a23cbd9349801501160474f4a95742e880d552e77a000cd6803eb51121e5e9b95305f119037c1684cf72fd1efad42a20e5e9f5d600b15ac0ea878f3e358b8fd7ab96eeeb87f2bc3ebc7a00e953a77aa835b574570aa8353c6a4d2ecf3117d9519e9eb9594549800a01aa0394e75851ad525f47797a7debea5313e15844f4a9570299fea824ec17cc92a6533403000000531600002810100704e27034c7025970ee0114000f739a4c6662381689a21c07328e10a4182104000300006000000030b40152e97d7a4370e32f9f1d3d6c682fba8500480b849c2ff026a53a8a0a8c2a391e6dd3131b55d4cb3e551991aa6fe5cecd0a4f0ddedc9be4350bfe3de5cca9a118039761228662c07104b15366d4164d94b17526025c70d7dda96120c1a5e9c47c8579db100509692711e56b39589f390d72bd34fe54f04a4e7a23c081e8ab28e644166482b587e5d10a6048164d268732575a1e5d13f686ad6a802bad6f9bbfb2bfe2e3eded775ca4d513debb9c4cc489cba90e022edbb8c1d62e78a738944b916863c0b16c9a81975df606192002fffe8798e5347d1480b05e109569d56ed34ed0685d564146f03793104f334e49696d1558c73019b87e207f81c93ca5b7ac5e0f647d7922cc80ac9c0820009b36f1f0fc9955f7406ea83f673d90f718cad0856d79bf76cc88e6dd4cd837e41fc10b7049dc578659f3eeb550363a02a8f69dc2c0578dca5b70675a074e21123ff7145a58e3c31be67e552821af9fc29de71c6d43986865f147ce2db7d60b2dd571a996359ed78e4dfc10b54d7ed2ed1e7d60cd5e8ac4ed882a589ec7d391f5d2c3d762045ff2b03996eea5001648fac9dfddb064929108d94f5fcdd3625445c0ed1ae8e05a6995fe583e52a039bd75d20d09a2a595832c724453f20c453568707cfb0d7f5987a361e80e8f35868ff43ba9728bea600207f4b78a305c518d31f11495aad962489e06a55ac751151a39575200c8318d8327ba84c2d5b78109100c53b457b394aa0e1e4f7894480a34c4f316d5f811e0f5e2b5eae8ef68b9c3b1d7806e3207ff34b02f36dca945958c507e9b37ed5eaa0518b971ffbb7fa48ee1318ce25ae944555edd7d9ea9c6a68ceab4980ccd16000b1805655431e300bbc88ac146dc7c0d68fe72db25c94fe5f07468b5a15dd723fb3134ecbf50ed8bea84e32af740dc0d5b91c4b7127b059a7d20c217d5372bc93bfb74b3cdd4e2bc1db3a1b9aa44632d4f6c0497c6b62d49004ecb768c9155bd04e2a823ce052422d0a75fcc2b3d78cb91575d6657d6b103d1ff25366f1d9dd4ec15669d145d29d9a5141ff9d668eb33ccf04a5ab727fffeca97dab61bb3731c2d2a44b70e94d40b7a06e3aa7c6c9e4799842d7a2c401a553078a18f4655f13626c6e1c821b3457c64061f29b7643097ed2a87977aba663f99f932a666ad3e6be40eb9748d02d10ae1a9c91220b134aa56bc0b58afc08d6b30d065358dead5eeb76f90d2ce9699071278375d1ca1afd1b0d89095416bab33b5f2aeed3d55fb11816aae18b270f95be194b811a6be138f5af9002e97afac65230f7393b9f6ad9c4f3123098c2901e2ef24b71a7685c62f92169d12270d5da2d949985fdbd38c5f247d147b418fa0bd36a459df0fb5bfc112a2b0470d77e79c0dbd8426dd30fb031ee6665aa27bc26994df7f9ca9ea352aab64794df90f9c2287cc840bf39a87b9828e42c21caf9b6b717cf3cbe4dc984fa4c03ab5a65a5320a278d91713ad42915944cd65992672cd9b2c7dc757331de0bacae0b7f4545b291be08532762be57b740bc49dfaa0890dff6dc1f7b6c4acf489a5034368780d315b23a54ace5fcc409aa135cd3b914156a14884234d73f184cf4041e4f91a5517afbbb449b7f698efaa97d5bbcb46c889827e7b550576b625f8e7de5f560b4e1c5006ba925c67853444568444d1392d529022ced76205b5210f5164f5a92ba45d4d9c62b567908bb3e3d08ae309c359585caf7a4ee3ac314e0a3418d4d948775f313c48c764a807868a076d842d624c5815198a77ce3e09009c3452726f34252b42c9d29be2c40846dd862612b0936fa6a0c79e1ccb3a330b03e91b5aa58d594a4982a59fac6ca9deec59d3e7598d043dd46d9644fe9e9f7234909afc5d3462ada3ca546b35e41c21f698619d1db3187066a0df6b85c85503501525e5148e798e7c8d047d6eb7589b71116ee9edcd08c133ff9e63e34cef18b87021d9976c3d6ef8467abbed95ba3ecd2e3d985e72e98c362a7731db854906568475375b5b9e550cedd61e191be0bca6fdb25d86029f49b9898374ed107d2cd6f5282854364eb99bce32a8f118a56b5103373a288c14ea1dfb7855d7923cc4589959eab70fbe3b9130639d8e5fa6e34d3032819c87334aaa7b291d67e0a204bd2669808ed969820564eb5c05617b904664b32eabc288a20b23b6ae8ee417ae0c41b470725cbd859f854d92727905e617f8e8845c3a5a891cd9652b7fc75c7af8e4efa83f4daf2fde1cea56d53f668490c8c89301990181a17ba69c156f90ee4474eb1a8074901408723737f4017e2e2ea38b3a63d6f5a77356dc67f1b1acb266c059b133a8ea26a7feb1d08a0b41891eefe0fe840e801bbc14406c0c7a806eb1120062e3e800dcc69402c68ea107e0062fa5dfbde3809e31ea4941cf18e84d516f0c7aa7406f8c7aa4a027067a53d40b06bd28d01b46bd28e86d89ea45950179851b5ae3f69c237c6247687b7fa33ce687f160028d1020a6fcf5c9d27f51d43d9d43a3d310c448ba38bf5c5193a2dbb8528a188989ce1130e99861313c86acd540ca0d59d15cf06ffa0e3d5e57d447c7f0690478b99a103545c0107f8b82d13b1084547c4303904e43fe08d6964447a7724bb2832d0a62e54816f72e8f689e869608052e4c3cc4d32a7cef59d2821e09813b073ae1f2c69047f6d6486c5d4cefab39ff1681cd70de79530d254ecc4567bf786a2a7fae55af909fa71e19343f6c4c11e2a0895ae22b773481ab4b98ae4f84e3ded40d824b2e9318bf214824a9e227b20a5d05ba27bdb8c15b25c79a973cc72b456dfba64170c8b1006f6b24cffbb91552aea1e7c275a4b68753bb9fff7d45a66e867534ca299c2e7c30855d494b4f2ce0b52621bafcf7fc29f5efcd0a898d01398d3785c70e715f3fef671c3e053f8de614fec6f5f2469ccef158943eae59730f1095c14e6c877ce9fb7cfcb146a47fae82243e66a200266922a9436d278944423e52e5c3479eb19e3245f856b681478f82923984ecc1bce7050bc93c2a9f5078c3f01f90abb7b290e147990e15305c8f7647a727410bb5a28b677a1351ef227dad7a60aed8dd42861f6583d10a54f3a770cfc602eb5c76f0a3628e021806f180b01ae185ac3cca1ad28a88c916e0a379c1d317927954cea4c5afc9e8bd080844c7e736fa1732fe28cb4aabaa9319410d1abdc217402a58f61d094fe2508569564ca4c4d63389d95f31e31f522c7c4d2335464da3c2bb51b95364512ae9481965ca5533c2f7bfe69da811b4e14d239a46e8be944a928212a27a773ca2228b33dd5a2846f83b4b2db5f87c277ab3f41f9da2fdb99266f003a87ad53190d2d3e45de63abb7cbbba0832cadc0d3ebbf0dd320e302edd8a2e751147296d51f40d87ef4e6c293c6175d66fa2c08c68d051bb2951bc042d8e73ad1bd6cfc01af71995d577519194e5e8d7cfb8c50ad6ba3125b7fa9e3bf525a73ce81b5177de5ece0d7087f85430788a80b2e156c8aa12c71484fdeb15d8b955dc5557bd73fe66f392baffad536e007a7870b22a54b7a8fb7cf56266401dc25bc1a12942ca865b41af36f388cf7b676787c6d19ae6f4d939be8abedcd92583ea181592372cb901ec81c10bab54fda6eef9ab9799037608210b864c152a1bd7896d56e499d67cb41c3b5e5e5a27fd787d22fa8f87ccc1daaf7d3c4d1e7bc7d652dbfc783fe7fd1fef1b777759375a2736b66268b7d2f58beb7e2ec7053bd14047e16ab9cff97567d2782a32d5de236bed2c592deed796292e077bae3572582cc111378065bbcc408b15cc695657f3f5e7b013d13abf59717df0f1e04bb39cfd5169b95af86681ae061ef7bae329ecd20642bd1e3a5d6f7f172433a419166650e3208ca4001422408d0e959cc1ce3c6c140fda5740796ceb0b316d28ef3fafd89f79594318889afcaa192c350fbcff6d26188114e90f3f20a7490ef19e11da9eacdc82ccc1bb8cfca0fe1353c4ffc590cc71def3e29d01da7eef045908deafc31a0523fa22fcf30fc4e93c1fd576ca944661964878c817559d978f20ca033114297fdc2a8cba56c73ccf68e40e45b75bc2dfad446637b6f329481e21fe796f66ea4af906d92490c23952bbc3efe8480091af87fe5cc32e47c77c675efeb313e496cfb4b8a83609a1275fc94b0f7010efed6d1b40554a2ae6b3dfae5b4e3a19aa51330c8b25ca585480d0dc1a186476f59dec7245cd10b2bc98e243d135e875c843e91ab4efae7ae2616e8decffe0ab38147e20da81c146346aaffb022a0e9cee6dad14261527b4918dd87a62351131426a910a082bf2f107b45169d02389d8bea9cf24d66b97152028bebaae4d53409d84bbd817b98ddaa43d9660c10d41e6a1b7398d32849928de6c95cf0208e14ea8946e0027e215296f41994415d16e2209501421b5703e11b77d3b79c30122df1bbd611882a9be17bf1bd4be208ea25df42f3c23dac6088bc97486260c4dae76bc058e45f3306c168e8270621f45d909a7182a398ee826cf0f86f536f71fd364b0c13c51a90c950738d52af2e07af4fc7d2d88c0089904c9248c667f23e113c611a931251aae7cef5878372ae65e55e6f3047aed0503b2b2571e0f677849b37354c1459e0dd29d9a15acf3a2c9c91d9a4df5f7692bfd0930dcc4d1213ed0df282a2767d9430ef004dcca91600adb8ce3f09e5aae2a4fc3948b9290a6aa21d9f49137fe9670516b3141079995e11b7e9caa219fa1abb2972113366b55b18a309e8e32b088c5950cbe0f8ab8c7f670bb1e2cf06198ec9a46b8122e01c19b34750a047c5baf342d9c53b86970d08d35a49a0f5ad71b2e19f6f89c1e28ab4508bcd76b02f0e80e8759180dc60d8a07512ec8bf5ead2b58ad17f58ec1e109835c1f1bdccd7d3a309ab52bb0595de029c78a683f973c4325f9a0ab88c05d307c5491f5f8f51ad5842c6b6c759afc006dfba045cf19d5837c6ae2eec1b395396f797c37c5adfeb0d8b08c270924295565eabd1dca7afa2c7281c4df55c664f1b3fa6482bc6776326788e6fe2049d921709156a0c5e33e8a3a6524a3e452d01c4a6d1544a928b04a9d04c3d2998425550b5bfae82ead91f1fadb2cf1962bb2811811ff4f5f32d07f9164c07eef78d696dae4e44077f9b744bd68640bd23c9ebea162bd882cc655ba0f60c52f6c86682d4cd7eb0ef90a732c1d615278e99fab2e4bba609f5e3f773ce6bd6d2d48a0bf3ccdf6bed77d14fa4d554dc158a618ae228792a05a44fd92e1c76bd288b3ea4ce46f305423e6dd98afe78e5e8df915281aff97a9984fa7d0de93d62c55154c8f0b2dcb04439a0a1a2ab31b52d0a0a38295bda3c20b69145ade6025d6555c25f90fd6452773a8c16d46293e54fc733640bf300181e0915fd73ff8a95e484a78bcf43550deaed17ca7513d81aae33948ba18779fc7801cf536873a5b51ea719ca97e6ca9dfa05080dd77c116c827d8b30245b47e25557ae5fd9750a1bbd36fd7683b3f9e7c1cc6e300df76fb2f8b7f5836f0ecbdac3a4ef321fed9037b88bd4b5014576ff2255f5e7f102e7bf7fd7718b5c3e2c8cffb44cbf01e26aeeaf1e9b9599b95fe44ffd582fc664b7750247205708cd7d47cfb975de2114d2045bceb5003e6f5849a08cc5593a614acf7bf2c9e161c453575c357cf05bac01e01a897152851a721a2000a442ff29d3c6fffb24b3ca209a488771d6a8067a8ff3246aae1301826fd2dd6a4bfe186a2fb38e50ad105f34c0bb6a8882c6fa94a9fbcfc1556746ffadd1965cfef27e30bbfd4377ef4a5bfd52d71654c142e039c38c580e824f0c6bab0eac1bdc5b6252eca8b47aa322fafbfb786dc575f49e7542028c36475a49a3cc0781c6fe135cfc185e3da052df785bc17eb4bd01f947f0a589ca0870a8888fa450ae9c53b174618d6c15091a20968d6117ee649f8518ea18967b040e0d9bf4a6485ffb22e0d2b6b083a063b474838302eb84b81454bc94d92c4cbabd3bc30674e04e043d63d8a42cb23becf922c835b98dca8eb93e36c6578e203ff2701f08bd42e00510d45c4311e1ee36d4ff609c5a138049eec87e2a58ea877ede4a8296319e182a190dcd4efcbe2544979fef260a43d48af586e8b45c42f16197b81c4603f4e77558d77b319e48827d0a06b34b0230004a44bc3064f3a6388f133ef2f2c3b2dc55f895a54a327f0f0597005f0101c7fc8478d1712de70aa5ba125dfa7519b68321007f20e519a183bf99fdf18c15329aa1c45c062149fa63cfaede13dc9581965a1e235d64770f55da00af49db6ee4811c8e51f0d47fe972295898bc82c6c0a3c1fe50b9da2d4cffd757f3e0d3dc6e3b032e01407160625e16fa809ab0efacf45eaaf779533243c882378e00964e9c7947c75468b184b5463d7e8914e3ae41bccd8747c7e0a7470025ff48b9721ee38e1a0d112fc01dab024045e0028eefdbc9cdf41b715c9966257635119921f7d4946de0f06b6a72be1759369a078869c4f8a0765105201a8bc486665fcef9cf84302094beda494f6fe05f21fc4bbce9f0bfd89a6c94c014642a781bd8f17359935d3c02d640b1dcd24074927fde4a0031a8bcb4e40ebfc2074ce5faf3a21a853a640c4d4a0e6e0473fe5a859bdbb953650af5a5a0df5511f6d1417cfb915a472085b02d707e540098057895ba3a06d18c94da449828e1c77240563a1db6a491fa1e648945ba29351e504ced719f213d915ac023ad04f287d8a7981622cb519b3a6f9fc71a8d3ca2a76b15b494976edeeffa0ed94260a751ebcd40049e5f3d944aecc3c3464d639ab46ff49785d48c516927d332ad41c80ab24854d55730a2f847b49634121cca7536114a209df1b90300b99866063fe22d90fc741f344e94c438a6cbf528fb6d33869585a53d9974955077e1b65233b532ecc784eb690f48eafd78715b38f4715da69dae2d4b60391bac8ca0c4c588115d24711e0bd4ca8c0a3c9d743a26f99d5932e1e9990ce23cffe0ed9b02b1c23667574039462fa5bfae6b705d7b38287b3565aaca122785b8eb7a1301e596bf0410d69e981ba51396924d290bf90cb5c20b334e4333d13c81cfaa29a619f751125a2ff0c33fd7ef4a4006190a037c8abec4fa0316db6fb9c971905e13ddb0bbc02300c6f300dcbd36f6a5cb9e0656e6564d6866e31a8ef134557ac22c62b95297473c40decf6521cef2d87f722dd0b568ae05eb842847b811591ee0b5646703fb97200bd3a6e347c83cf8d87dc5093ef3ab82358e9e81e61a50337882b1cdc11ace4e88eb07205f7fc95fbd0e7e3e663b8014f593f5c2bff71dabfbe54d11743adc5507c8733822b0489c87d3aa7fdf374b8c9f231fb8d0fe20d979197264b56d723e225a150f035471d86d1e6c381365887c5b7b7ea4b5234a58c7264d96b0cd0f6667b0db2185011a60501d1e1c0e8281c533719e7a57b42843856210e2b6da2c245859c6902c941a9868df32b7bac809a37bac7b04b73e9c1e19ebfe0c7920800215eac85590c70bfef73934d6e1338dc63bdac0221f4f0994315b8c584c88b0b9eef28bca22599879d6d892d5a8d87ffa3c4ec482eefd7f8a0f3b165e8b8796bc1887fd41a741a882f45ee501767871dd3ef0a627de70d2fbe00902cb64e9353c55a8359c8daea402627ec4c041e6b8f5f6c34efe4ebc0d8043b58030d7d829f2cd702437ba485dccdf339cf5fd7e88eced00d9c1308fe106252e5fed8c5d6377b8e6725058be479960e057b9fa41879005dd5505ffbb5bcae3d59710f1d57a805b6a58662635c4b31069b42798ea541c042aa7a5df5fde226716a0a3f155a291cd153d239331cf8f80fd4b4af885a334f351c19389c7778306b780e1a8bce2c596777561139de196292a0bd097aedc6a7f1a9470fa90f280aca77c80dc8c70210ad3cb3327549c759a34b3825bd56d7ca2ad4576e19a9a4d2202c25402a0d3a73241a3d56b6b1632534385276e3e3cabeb11afdb04063829e9d018976a0742347ca357e5069a37fc44c9963d77820171e2e34d519b5f63e1b4f0fe39bc9458bb9472fca69c2506b5033c85194acf7be8d0109a533b26b156eb0a53f7b0a41380854127adedcae0e980fd8876e40ac0687e24e2f9c98b43e016ae1c423e61371160f27c95422269254d7c3d48945cf2b5118d8598b28720eff10706a85d290f6c8b14fb35b913ea0e90623cba35c82fd3fb0699b1d89a5749937ad0934fb4802c2990aec4fb10ac26f9a9320662487b2fd6e9d87349941582de83103f0908fbcf3b0b5b1da4db9de491a92c27aff7c5588c0ad93ae4d372b8c698b95f1983de80c7850c5e232f8c1fdd0397393d97f2b54df0ca05e5ca981e0589ad2c94705eb06da6517784cd811d5767d9c5ed9b1653296024a5a07c22727030b6bab89e161c42ad6c075e77cf982aebdadc5362a5d2364e471d93d011107a7b76721a051cda5d441f4d8f59906d7b20612ada33efa04ce45dd6cc6055996e49ae1ceb0680b59aa9dd9912ec532f06cb8ef85837a366415cbaa9c7ab3ad002c64580dc76ead90a2f3d03a392382eb31b6b492cd337f6d35c59699e5567ad8026faec9e736eddb5356fba19d4d29835ac8294050a1e1db3323c5429b7167ac25ae07bd34aeeb0c394fb5a927e91cb8c4266868826916b7cc0bdca5c495e254e2f71055d7a1d13b5f52ddcfc5d5d43fdee4cd67c2bd68805bd01fd52f71e0dc8576fe4a7e18870af325d03fbfb9ff529cff7863bef922851f17b9982f51fea17dfb5ff1f89bb519e78b8450cfca63be04fd44cc4ecf1ae30d68d76aec6ba4cdcdd782fcfb6fe9e60b197f4d41058ecd17bdff60a002daacfd500aa9f9b2657ee581b3ff147dbeda4822fee7722902f562ff5c09e67c71d71f9e5773bec22b12ff776f26427357bfb849d47c95ac1f1e599faf05cf9fcf7b3072beee6c63a811613edc0207430a89f3652fffc623a9f355e0bf7ceaff48a53f7869e9af0912c3ceb262be56f98f9feb7f7f416a4a385ff6f837ee48315f05f68b53fb0f2af183372b9aaff53e1726523d5f58c333e27b04e14be7f105bf868e3ccaa0161d46f2f147bca29b267167bde1125b69f02cccd7dd0f07d9288f410d7fc0820c45881c96e8ee7cf12d40b20f0430cc7cfd4339297a83219e4f9eb3f9b22bb693d65843f3a783f3651f8227395f19c87aa3d98bf736095b780b1dc8085c39a369f5896228f83fc9345f6b176c99de1b2c18148be0ffa4d2faba396f69f5ed59696c323441b500a6558c5f2155ecb32b76efd8a412f87ff7764c0548ce5f63105bce30342c5d096be54186931ade5b43b436a7bef7a084fae746a6326d42ac500d4d831e0c84930d9b1b1e8316c303a0010f0f1e9d011b30e0164705ca59083939ad85e6fdcc3980ab3d5eba8bd5308d885a9ae438a524abea4b385ddbde7ec1e56a2c719c648786a453e366a373188382d5b981b4028e4c2ced846d15f480a420ef6232ed562412ac592260765bd7d79f63ae5f0c6db58f08dc60f1b53c3995465e197cf7c7623323bc7c8c4d4ee705ad7f790d803601cb2578adfa511e72f65339043f226724079da3a7e1ab1f760bef17dce9497c1b79d2ce4dac2a2cb0ae53aefb848473b0cdea08079efc9b7e844b9558e794b1e0b62bc75a55e2a3a6e8211abdf331be6552d3cd2dcf0f82af133d1dc11bda9fc2f6b0d42b1ff4c307eeb135f07f68b365917996e672353f2115d21a706e565ceaa157b996e66b1b4dca25a0c202e609f5fb7872d3d33d53f71094c47e5fa93765b667d5dcd388ae0e5ddc06a013648c4a7e74d85f888f49f4c7e5f138ef970ea8dcfed6c3f8bff3ff946625ca0d4690bf1210b98defd64e619c0532269e748ba22689ccfdc109a0492c54c7e5594c2a2abef3cbbde198a4f090bb06c6b0cf666aff2723e57e4b3a7c7874e429cd4a961f82ddeb091d73c5cea13fd2e338b379c20451fe1014db9fd002bb88792d66dae60f3c70153d60bfb28c7d06e0006e610849eac8b049bb8eee3b181792a32922ca4fe4336215c2ae3c57d6706b223dfae76107227ed52829f1d289da5ea988c9b9d849fe03999eed708c50faaae4a50cab59bb2d590441dd2a19d3ef200e2c3834c05a0e71574134a22adb53073078c6c78f4bcdf3c0ac0d49b4c4c7ecd1bb73dcb3fba20b965f0347b7dec35f748bc264c3e7adc8f677a8ae9bee7585b0e877849b207a68c1c777eec0634d8053c6cb5f8211481880d75abd03c3c086bd8f33dee71ce82ef640d5d78943d52e1bd98f2189882d7ade1e0857299716c133b6a15af541d1dd219a6cc57b1938374ecfd6fa60504bfa97690f77c5ed06ef6117d8a95eb26758749676593d1569b45b2a1d6e363dd4da671a4d136b104d9b4cd86446952a1cc6ab711ebe097afdeeed782630b1f97382cdd6b3c37ebfb77bac6630188ba74a3f392bed8e51bbcebc85406a65687ba93a7fb8ebda368b61ab54ab7810b1d9a2b5e6925c02f934cc0f982b0b78e74ca51c24e1f9f859e1eaadbc1377c81edecb4d533a1c6b29e354479c30934303cfdc6296a9dea5200c61ed50263ba192d406202b99493e017ec31a60ec285b7f030d30ffa73aa75b82e3c7064f0cc2d57d3736d8b519f7539ad2efda680a9318008dbe4122db9868549cf68724ae4807f88fcf8b28882e6cdd06b7287e66792206a9f74f0000d3e293269ef89b3fac9d05e0af029771306944f8e42fbb9dd95388a4492c321d5d00ab41abc7dc63b4bc4d92050857f232ca074bfa169a1cd67260554392b7ab584ff482198b45207dc0eb55214f9e1720466cee071b1fa295106fe7a36214243fbb0df9e2fc0f8ce071b78be7ecfbd29d9f202977047dbb01557eacd15e2dea98bc7d5b727a92db76ef15b6a16139c6b0d33116a61b7b94e5edf0d6bc5d83186bba123280d5d5a01b9b0c4736144fd123b7a4ea30ea0225d3d87be6cb254272b4985507b158f4c794a02b6076abd577653b95966733bfdfbe2e5ca66cac53844f79865bee8fc7e6443d57c56c5f5138f3a684bd6fcca800f42cd270a8e171df26d62ab3fe062f6aa7af668fe27f0f99220b3f3044384b00709121e04a79927a0e1b1e4d0c6e1e665c4bb7ad0c2af00fe063c3a25404735ac163f6e5d3bd3e0ea99e20c4f46c3e54574c8117c874bae07d7df3a57905a8e23c5a370a272ca41f644f01fcf9dd9ed6e491fde20b482b40d8b13f0ab9a84c1ab322179d7b31c18bf1f03c76434ebff9e057ddcd8e3ed97614167bf3ab6fe97fcfb3c2d6ac5212ec82d98f857c72b1fae893c61fe9db6a412d3ec1c80afa578ae0c316e937baedcb22e3239bee90160fa4565c539bff2208d186b3ad9a7e4e578684b05b7e037c113e57a3b322508f81e2888f7c53116d34bd3f3b434071e93097dd925dd58e81bb9bf2ccdec7e5ae08cadc4c4bdda40ebd2373b9c6ad3ba6de2254b986e7b0532cf15493bde358366da5ec9d2ae398c681686b6e282d89ac388486f57cf6be4d3d6ad32baa6695a62640fbee9e997acec88752e1499430a2b5f404670c49553f78ad656882606091783cde8694502918d1b5569922383f2f9ea10728b5032b748c391fa71964d2857b5c34e16b7223ba3211dc20ef22152959882e11ee132c981c5f7442e3fb207e9ddf834ff4ce6ca7104f35cfa3a89093e0271b75598751595c8bca52c6ea5dec632aa82e8d0c780581acadd1d2a1b8464e5922929ffcb0f7c4d361aa0dc86546d0aa080eb4427b7b0a5b40ec4e94537eddd209390fb9f3ce8621bdc7d9121ff3abbd1c6ce4c2e57e72e56d1b8d910ba854ca1935e6179a54fa992967c39b9e843c661d09b68112f6678396cb729648cf39e050d2fe229534b65f8115093a3601211a49bc48f54ab43c1cd00ba248b4ed360966200d2bcb95add351189521d73f8a675ff408269ed9329d09d0876883b7b77a9f0d83c1788f0878d7a48774b13bc43bb023640e0e51ea97f4e3e8534261e41116f966b446493c7715555872abc7d7343975dc78d274d7c4029cb6d0115824406ba55028ac32cf2b6530bd117ad1c843a77a22f04725ed448796dc37d9c006f9e854a23e7a23a67df16eb171cbb385bcbd27f0069bde1e7ec391bd4edc8b563ddef87e691a3cb473b5afe9b76502998c03fb32417c2c90275528ecda82edb6a430862f4f9ea319cf57363230e3b096bc4d9cf7899f49d9537599ca05e554d58c6fcce02d0c0977e51d4b760d4a0b893141e83935c45801598c5be1f485c9c69968698ec601eedf77791b14f1d02a19c7889b73abe8f300ba0bb1bec691822e1a5a3471b62fe00b13f0126d6f0a84e37d3bb57f0d27c9ccd5e6f61daace304478483937aef60a3a709a68c93174b70762d390ba01baaf79ffe27796c0f83a118d043c6795d39141f080067f83a25ffda35b92694f471e1e3877ef0378c7ef656c8347c07c046f063f744caaf66b86cf3038ecb48d41f38c958d4a8778d59576579f52660963d73eb83a2a189f938afbbee6fd35cc0ae44322c1cc2af9c79531761bc0c74d55a368a3b56fdd5c8fdc48fa48799a712dad3bc6f335fae15ea276d2aec34155af4d0494741fcfd0a5c0e39cf52d22ac5702097389262962400fd7ab1e8a71003684a209bb619007bc1c0ca898fe260dbd5d2820b86ea15ac71d5d17115ffeaf2263f7a47f2c9858f201c1a699d973730c334956483b2aa91b774dc81c765b51ff3116b1a42221c25ce4d7e33b63c8aac9123c6679046a43d4431681a19c0a5602bb5317ab85cb674f1e1dec8085f14c828ee984a824e5b91f95ddd1aa3ba09f0c35d844ff3025f83c6c41f34bf02d604adc1c7e7f9248106459bbc58718c381ea277626c9f066176d2d7da02d72c73372a639ae42181a5aa345d83d91196fc2ecf3e98b36444aed8d4a000a0906865039a4353b8036b1adba45f5ae76d3b3e96c243fec3cd8dcc52d14d26400281838d3489ddca6e9ca0c540ac59f6bc2b77d0f4cb03702ff1999d4c49902fb93aa9592dfc7297b09b1beaa6e795ba9d1eed1ac8e7ba73a20be71f50f64387beff8a66289095cac70f2d139c81e0910c025143eb45ab5a502f2643c8b2cc1a2be47a4d8bee67544107d5346f2c36ef855f186e7bf893b5349ada19e6a559c46f3ec2a5b29a760e04cc7b9c491bab7009877e5872b77e2bea5925aa90963a5dad39c2d4f1ee03617dbcf67ddd0bc304f5faa4126fbca36b6e194c9c02023f1ff1a28acb9742433374a8345855f96808145982a898f7353e0132840c0c484649f17276e04b52c4fdbdde2e440f5cc54f65b626af0cb33f2dd65018dd57387bd70cc5f353b6be6e8c3ad1043299570a6879368d62a1da62481c539bb50f324402d2455159183e408054bc632760f3d14f09dd742590993bd33aeb99c86fdb784abed55e78dc59b4557f062f5f6529ff4f46fc6a019042a3da85e19283f60132a7b261c56cbe27060e65179bdf1a03ff2992c85196d4b3bee10a4118c7eea8847b67f107436a899b15a4693bfa8215a2e8742d2157080d8e5957eb806aa2e5e8c402ee1d4824baeaaca69df91f07705200a14f1eed17bc3019c109c406b0b399dddebc9d336e3d84227ca257433b7e518f866f7ec8b797c80f6b58cde68c21af0c90086eedb84574cd4a28441828188546f2480373eb7c64c380408a4ff6ce040aa2cd5fa2a27f7e3a0a0d3c9dc1dd7440180385b3a636f5442c38443d0648212741bab107ed402f9077cf55b178128efc3b2ac6b6334c2a0e958f2f4a137324ada072d88ffe0abffb52f7b87bacb2d4b5fb47e4157a4adf378ada533ee08b39d2e13af02af6e71066ee0e320b1309e6768adb36b1ca66a4099ebd5e941c823c3f8ebf18cdb3e99968019225a16a808c9f7982b3112108cf344bd69e8923a767821502a2b394b6496ed63befced3b283c8085d5c150e7ad64aeeaff6b507040fbd639b3b1ffd989339b27d20545dbf27b33dc3a8427095a1f292cac1638b25528819005eae2b0701b16756569622c6180a6d09581160ea38457e44aca0a6cade7863586155d3a871976ab935b55916adaf3ed1f4e6cab46d4684b977f9eb0a51251c32eaefe79728b3a424dbbb8fcc3c92daa889a6de1f28f3c25d419035e8dd2d640f22098aae726ef491585a43284e352a4b5465e790e81f3eaebcbbcff6121458b1c983982547e55f5857535fce6cf0ee884fa56b7a1178a0b0fc67a9eaa97443d21a497c68b4f8f1bdfcb8e3ebe1ebb16d6ea8de23d1a08136b84358c2947d1ea46d1841a9d42d322d3a7e3015a2cbf0a367dd3e0a3ad1fb8f025d542994a4b259a7a4527f9633fe3813705bfc636a8a344772c792a5bd2485f89c68de5709d84c8e5d2f4f1df0e9222236d3cd26c6463999fb93c0063b75a0ea1a988df24483910a2979c707c0975144cc741ec252cb3012f399c614a96422a76a28751b65028e3d8dea5e7b99f2f8c8bcb9e6f66d0ec54fefb1f14d02afb19007b37704730d5b45f23191f0f2d513a7432f9dc4aa06209dd9d6dfbe8142a8158b8355f835dd77032a71ec1487664c74653e18402eeef2944f62b934d3c6150f62fab614010a9052dfa21b5f4de57774c16b0f0e83c5cb5bfb134ba28a66d7e21047e231d44b26c425f321c395d1099a278b6762be303bcbc84c20a3561e0d89406608a1f5e73e4e89bc11bf57ce52595a7385b5054fb2d38ec7876af54f444e8c318e55e73d35dbead79af16543cd47655ddc5b0854bbc81c67051948037b0652ba55b372ca3ab314116c593901f450ee38f135e1e7bdc230ded476d7dbf49a2f09ef6bd2a44263684bcb860f3d2fd54abedb6896854e510dc4c9e3fb9078fd2697eb3a55d6af240991a5ff3eab68f2dfbaf8b13ab4ed3e351a914093e2f2297ffdb6d1926ae37d58206c55c1235a4c450d9332cc3dc889446047577165decb97295732c94c155e93a4afb3842de6ef2fb0c44131b067ffee7e9702b2e23707afb0c9e9b2390b37dc12004ec4782b2434bfe682b1ac7b39a25113ef082ee99637d02e803cebce38ee20002ced5da5418b84416814568964630d114ebd07d0550c97367f3158395bba184cf34a19a63249cf8d645aaa33d09a4ea2561d591e6c623e6c8b03d54838092df5e3dbc903b637e6ea78a14a437b005a44a05a9570cd1d42d32f191238fa07e3f74f72bacacb3a3428ed7612391aeb0a48482cbec5ec204ca8cb067050c499d6eab978cf4bd916cf94f143bed6043c721a4500a971f5074421a3e9c37ab0a54849724718bf3c152805402db35e52c010396e01c59bf1213a04bbd6a81ef3e1d3b51854c0840d0c0d4401f6bbb3ca56792301da65be61dbe6780439a8f444368a582617d643acfb9debb60690e2cdea13d669a0302b81ba300e3f02d945d4f55a8e67b11983138c2ae3aa851eb7b32a4f3a06a49a3a057a38bfa1586aa64f7dc16167ebdf6c7bac890b3b21ebc78936a3dbde9b8fa2865a12f4162c5353ac0200f7c998b48abccbb4fad1ca75fb44ffaafac4a13100d7b68a3b6b2f921837beea09a3c859dee88a86a8349674759aef6fae27a77d6885a0ce4fe20434f89e0706c605f33ef11a10a2623377bb050ac4cb0de812113f35530040b3252287c243d8a8ae9520493cddbfb2b2fd888e926ae3e4e1cc989afa1a7b65131d6dca642d0ea0347e3dce916e8c586942e6b7900a4451ea63279962f2547f456fd3a6b550dd7cd9f0c66ea2f8bd4be161fdff7c44d3221d59ca6d2dd674391d6c70bf53ae12e5e47445b2877945228ef6e4f744c16e4f74fb7fb6bb46e52c9aede2480ce649729e0cbfd64571e550b08d486fd3ffeaa1aac31b68310690583810a4abd8e6ec0eedf39d7275ceac463e8d01c5009b53378399b164271f84ee2d2988d8c16dc71ed4c89f6a391a62dd11c24b2104a92de4a3eb3cb8131f6a2763e2fe4af570693d3d5f48d2713205006d02ef40f4aaa191c29811d85aa899d91bced1be7402f6b1a3813a94620134f707124d89e349a2c1d2cb9b049578eabc1b60dc082b76edaf4cc5e4b6a36ed1c5ed98fee608e37e28b309268652af2fe04a9a932bcf85f86c71ec042fc392e62d4580fbf220668e0afd1478032cf3cd7e422a85aac5d1106c777abc67608c134aa5a52aa6bc33fea229c95a0c6454b1bede8ca6e868de49bc4f6a753dcb39c9d7664cd2f54c45a0743f43ae85782ca2208e96f6b5c4423836a709443b945305098d63244d28cb55c708ab3dd492515002a49faf5ad68d452cae078bb914ff7cbb045ab2bab84bd07f8ddeed02d934a74db490746a4b1039498105a47da4128cdb1b19b8057dce00bbe36eb434a3b4fd7a80288ae0d88922893840e671dcd433b35fadb56c405b4b78025d76739b9680df444b2cb9fee52f159695d6963c40fbd21ab1b816f17d8cb73146c3b946817e0b10e25e75591d7453f1457ae8410791c2126636e224a1c8128ba90039146d99aaa9e91c86b001d355036e148478397dcbbff16a1e9312d86372aa412da4d29da28fa44c60a912c7816119c93756114721e1fdaddf1608deec680b7a3e3a5a40bdb1ae0aa297b077c702db91fe1350642504bad0bacf8ca165c6fc015fe82b39139beb8cc29ace0571e0a2e99b33be389c8409a597e2222577d72ea205790c63e37e39501b501901b2baef665f27c230dcdc5794f6166fa871dd3df9ba2f18c330779649e28e02a3446246ea6bd1f5ffa8cdf55609a8e563d2b65262f81f719b54c4ee8049aeb9d0b7d2192d3c83e6cf6b250d3aab829f4ad52b30290298f5db68ef2b3198a4e6be0024068a55693a38588c0832c7b483f4a4a91bdfdfea7105ab6a5a5ea4b405fa8082899313801e4ab4153a14112dfe19d0a7ce31b4b826633f7748bfa3dc02495b446374ef3a8f64ff84147b1c1f1d993bec10dd81cadb71b55206392551b7539c178362b49e2635d094369b0450cfeab271259fc1a50eb02201dfac343fbb5a844aac38e105221b23a14811c9032aa196b3d797bc41eeef040309a950dd4399026da833f305d4e831d92842806e3e67bcbc9c609c1b35ade8263e9019164634b81080e5b7889897f4b4b55a0e0353722165a65cd9b01f952a903193657ce77b6af2f2fb5769fb03f8c330524db2682423201776861c15e6058c096ab4ef56526473e4e5dc5ef9966f2fb58911001edb09c0027984c133982ace54657609977ee742f6f8189a2fe780d25ea80bb327ea6f0cbedd046e546b916c2dbbe74172df94a43d02b01e41b5cb79fa8010a9ec4672607e0d5ca9596299a508aa3dd8c48dff2fa4b9544b07f7dafe128ac75b40399457f63e6c404eb84d1609e3f06cecb8b9c73083a562c3a47c54a4e6c7f4d7ce5f0251474fd4dcfea1f009f2f6cfe8f820033c01d278cb0f021b29374c85e6180b2ed6c2bcbfef7429b5d548ea5c5a28a6bf2829dc17fa4ed6765f5815d84b7e290f1dfe08a7aa42ff2e277a9320ab09ece7cbcbed125e8256d8e790964242fcd7455c44af845f83c1e5800214e1498b4d9937dde581ea7f4a847c7ac56252d90e69237ec30c5c74f4d592b309eaa5904c15b530e43184b368d8b76944381985983f4acf7550b3004868e140798e452832b353794956fe53ba3ef716fcfdcf2093b99ac90d5dca0b9caca28144a3c92c2c1649f189e3ac6a94ed28369e206c0e86b681b03ae818c581a7942532b444035a7dc4b86697b27c1d4cea705fa670e511b580d8b4a128b7d30a855579242e9b8e6b9d7f8f54d6d8cec0e2aa1b754ab084b5892363ebabe48af2aaa19612b98df65452e614bc516f603b3581d734d932447c65a58b492a4c7500b6c44cc89708bf8379cf3db75174999e80998cde8c90470eef170adab78f34c7018336bf4b59455acd604e61f75c30637f9c386a473ffaab493f716a526e116d2ff1379e8aa71c5f4e2b1b03b2039984026ce41bbc0548304d75298a65839badcf4b9cc86acdcccbfd3b5f6f23e4202efca5086783bade42d75f1fe1e7b4bb2b98852485a484089f7a363ed12e3f83fb2e54688e2db2e8ebdff628053d048e722c913ce04910556aa4b0b962900537507af6a6793471894e2ee1f7912013efea4c4fab5b049d6c8e17919bf07f726125b9762dc554189959cec9c50589d22a0e3aed0c08c87015ae80d2061aa5925c9835d410607708a6c9668a6bbe7ca59f1086f4654176bf2fe3c209f05345fb195328dc75115f4a8960884e90bed9fb186971aee0549ac4520b4d9fa66df4d67a371931e4e68b12e00535bc0eb0acb722d77cd132111b590c1350a4e8ba4d6391d8783729d1a22d7472738dc45ecf89210e4573be393a3eac16c41d0a955327644ed890ebbf677e067badb71b6b0adfe4752dd11cb3b7101beaf910220a871420ed171c6ffb4de2fc44f166cca401fb4c2fbbe44e1cd605793743c26fc7b0a33964d785382d7fe4b449ba9eb82b3b581c09143eea2beaabb3154d3b1ff33e118dda1a7b2c8598e12133bf73cebfab0a7d77331e0d43f3ebc6be19e6f3733143a64dfbccc43ed23dd6d0a7d6886f95d853004dab77f8b79ce7ab7d4b1b6687ae9d4402cfb053fdc0394ac883488056abc548c8f18482d8343df6fa58ba24aca4c84c2c82f4c7f0bb8ba6bf75644b14426af317c0e196a49d787be22cf933231b0f94a76b2fb02ea0c51c4a846c0b679997a46690a05364aec740caf21b10e1b30fca0162703a3b61d2e7efd371812b6c29ae292439774388990f9fb45f8a0a8c27ff408ff24dfb4c614c8855a585f580a04d12523e964194f36079c3c363250073d7b92b4b659abc2db14b32ad0044902bf6de752812c86dff69214819942a675e5b44dbdf03de6492349b5bd7eab93c520d683cf1c43a5b9130e8c22017908e8d47b57213db00ed8c2c915d351ed6a9bc1302cfddcbcc09adca9a2b53a7b78398d9598d940a141dac34f2f414e60687313b8a96ac0b0494fb1ecb4a843b511d1629e41873ce88bae0a7f439faf95365ecb0a765305bb686b726f08ad96a17d20366fa55d7e3cea0e15b013428f9e9443cd218dd760ecab6840a6e377f7c1178f9dc9d75e328cfc3ea4d6c4238b1deab5e9229a3a16d8a9fac1a94f19a498a0709206d142fd80ced6438193c4096c73105fcbd3790858eacca99dfece4298d7b1978114ca1c479b54e41098e4aa405379a75b57285430114fb0ee0ff5ce04790da31b295ede97b8f095cc94e8aaf5023c2d963e4e15d89c185c694aaa4659726c95c27712d78320842efcc753eb8348acd093769fc63a575f006c2603b1a70b4e0d0b4f4f4594acacc9823142b252ff08324152070d980a5a6db95c7a414de309d054d193445986da1539898c041b2d90eb8fb20bf46822b961e0ca1eb95733f6136f4ba02a0d4b88b621d59beb2968893cce2c2acbd9f4252955766cd7cd251dae12e3bdb4a430abed22e5b129c2d38a26077792fdf677badef40def8337166f3b2c3dbec62a4fe165ad24a642a7fe115467a94339253f3a27d46f1f3699630173e86ac434c2b4fc9df6c92346c588df5dbb4c07c76d6b1e18921ba3428f1fabd12bb10445e36ca070bbcfea3f073d62de71a3dc01a62adff5660fd99c18d4c67e19d7336a1f61ba43e84d9f0b719f9fa4fda36e2301a59725743033aaa43d828ba8539709567b78e903dc38d9694d8cfd25b30f3185166ea9ecfe29a86e4c6059489a98927f1a3331120e0be069aa1d541afc68285b216dc6ea6247bfeb452ac00ac95cd4ecca925e33c24084b93a64255564d8efb0ac1621b26a5054c0adada3cac515b325e3c3bec89449c11f93add24ac369ff9d415b0b5cb3aa3ebb20be349e659ff1b6fcdb6153c0617213f24968bc0fa49d878cedfd2b150c662b8757ae920c7ce01b9a757fda905c43993469f871a851ce401b5e4e6721c591d20f7a13a860137a42d1b5e913b5b22415858ab5bada1249c4db950e7509cdbc922b52e8b7ef0c8ecb38f4e7fc1039c60d45cb4532d4bf188fd0a8d78f1631765d54ad23a1e94f3bd379b6c3bce98ad29e2378ffde7ef0ecdabc82fcf476d29b60ac8749e0d44db70bd1b95be294f3024b96f3f3634abce176e94600f89bd7ede2036d974e80adcd4741ca91636306cc5841c2b8ebaa14f34e8e294498660b594865e096eac08d714894accaee71e55d20664e724a0afe26209b27574cd7d5b914830822528981bf479d4d1c4f04c8841a45c61fbc452cb97976b383848e7b27c448d0fed444608beaa3e8d9b8a5daf037ff2e66403f602a9d17de99ca08ae9ba5a62e53429f19926b05ce80a962c86a68e5f4cc821bf5f512f1b6543ceb7ada274814212132260c2d3919ad1446bf4d9702dbd76b43772298666226904cb2ac42db0fa03bcbcf0f9d779643c0146c18b8d860ce5a2490e62620382c2cd4460a9fda6070d518ef81b9a7ef392c77bf8acdea67729eea861e41f827b02ea5ccce749a6d26c93f923cc2013e1145e26f7f779c2a06c4c4ee46684d700626a3ff8c0ee9ab9c362a925e3354b06a6a48f66d531f5f0417f3117bf4a816ac11aa8614f1093204085ed0956889b82fcf94052d5cc5802f14f96365a00218704a40c78fb259ff23d3ef3f441dbab988c6d2109c63a7320f3530a115785c5bc7579199807e76608a6905ed4a67b4144db9042df00eb735004eff60612550cec99e016277fcd9081771ef151a6256f8eb30df5086152f5f1f1c6dae0915bf9d6122f86c87b5398f2d3cf6d17b04bb7cb9269f9c078fca634a6faf007085a5643bbfdb7309adef4d6a3ae6193df844f662f8a512c672539ef9a71abd3a00d7fbb566744a17e258521182e88e26e84aa12d505d98c8cf4f39250e02a4344675d8a4ab472b1d75d7f9a0c0416eaaf2d5c493223a42459e1b2fdbd5d80620124433af0b04b7c4a033f7a34db41c809ec82ef839595922902dc85f14b7051e779dc1304695b04f56da84769d89647d2a7306109f7d8350bb714332e74a47518564e3085831f3f0a5452e5817063bd9f9006029b52b915b36a005ce7f42b1f8ff6fc52f80396c831567941a1c4bc9b89b69193f6fb9883b27f5440041d25b316082b8f235877d50859d86a57fee3e6192fe5015a47bbbfdfdba332a53187502cc944f2d9e122fa84ccd4e81033f36bc048e811bb7eeba9586f7850ca7c7f2d045e382304b40cdd968b460b544b2e0549944e6862d233b678166f84217422ff8dc91951446d5a22f53a3f0b831a81164e202ba3b4a705a309c55a503c93bcfaca7603173881b61a79cc8d3f13b099ebdbf2ee4f330e5cc2b7b1441b34d2c12b0116d080e3813785a80b63088f0aa300082c75834f48e02a10d3b9f1d10dc0ae8fa7851fc206d93bdb7dc5bca2d654a3205b606cf061407a3eceb7fdad7cf6a56b36dcb326bdd4d93b0427944966dd936dae46b367e66e5ad1608bc3afecac7000f65f9c5864da3c57f636521c24506a21fe1cd47e39ba994625cec629cf14df1a515e307219f6cb4f4640dfcdda5fc96dfaa3b5d9eddbda8e5637896179fcbaffc07a3f29fe93f1933dc7f33eee8378dbe15b4c473bf6ad03b8580512e9c2d31b0742f5c7c69458594c26d3fec781e4130a06bbc871f81a6a910d36ca0c3f89787f1b2e3c7cbbbcfe9d3caf0d5621f2d8c9732b3f4359bfd66b51fa558ee4936e5552ce957acca97eccabb70f11ddf17eba3033e601876604e3765f8186ccbb3d818beb32cef62e1f5ec9018c07810fc0def598870f0e192ae813bd83fe19cec08fd7f5dc4fed729b8d3e5a9a597e3da859be6334171f9af3bef33dd17cbb77a5fdf8705aecb679a8da6a3befbd7ce5e477d7979d6bcc7b64f921f18d67398e898f9554221821815b92717fddc39e3f262699b5eae8bc5e1df2d31d866b1ddd906b22e6c97e6afd8166a530fb5c9c9aa4d4f6c43894254d4a61b97443ceb391e33bf5ad7e99da6815020c9c166f4747933461a29d0cc0c24b8af61d0cc9d5fdbc7073d3c4d23a96623bf797a7ca4f46090d7c00957ded2a549633ed38069938d778c531a63dbd7abd6fab46e5b674221701d4fe23f4d33fd49365de84e5f72a703ddf9ae639a9568f33ccfc3bcfe0ffbce3c4ccb817941e0d5b20a8dc823128aaeb17a3a664a24dd53bdcf5a9204f33e180974e5923b6b9078aff731ba57a591461a6bad4febd033e1280dcfa1ad050dad456bd15ab4163364cd349badd6d16f3faa5eada37ec28413f1215dd451b82ce3aecceb9f0b52cda65e1ad619a51c472d4f87c9dfc88d1739d1f54c344b939a8e4a29a5943ee518631d8d481949cb1173a3913645bf304c7bffd6beb16fecfba26fd16ffa0d5fc6fa9aa6fd45c5ceb5e1b2122a865c9d5bbf3de004872cdaed2d1bbfb28d9e313d8454d3bc637b7b7b8d36e5080941218e99d7cb14dfae6e359bf66ab8ef68d335bab6efebdb83a1e5d8bc20f08ebc6ff4dafbbf6836fe57d5a148cb1173a591ee0aae7bdae4d3a689591fb0a68f340a6c9f8065e64bdb46ba092833ffb29d023e1589b165d072400fc3628cee1e638cee98c530ccdddd1d83e1d66f563ed643c3e299312589410587bde683e6c520f1fafb18f137bce99af5d7ee9bae81371e33bfed6d96db0db7d76cdaab712104b276c0f379a4f021c24f10cc0f43e42061028b54497a4564678913c522dabf68382cff71af813eec3b6807de98febb8ee3ba768be67da3996d56be33e9955779cc5e36080dc20d5d2d9a0df67dfd9c9a0eeceb91f240e025bdbf0ee5b891f62bd6bdc0a7f92ad6b76099f9249b623be8b24c29d74dae1fc3b4fe79917d7bebb48e7bd7b48ec7cc5765475678eef489c1c423bc0284c2280c0a8402a1cc6f0cabb4695c341bcd8b39eaf7d083b6697a3db2fe89d108169972dd9b646031c61873c82af4a236cdc95ca44d93d9ab39646ee7b5ac13f916de052f8a3751a74d45a28a8948e4ceafd4ddfd331fd2619273e7e3fc5ca771fde9674c13dffddd69f62a9a8d53af4fbffa80b76af4fd3feb5dcb382d47f5a81775c8e6bd2c5f1623384bb582abd334eea56836f45ba769b8394d07a594c729e5b8aef35a6d07058e884f4430c57c63919c35f3626f35e15a3c345c66cde2e198f9fcd60eb8c6899c69b2f82aba24704490023a748bc34c1ac0359019c0a867614949990f3b48bcfd3e46b71febad07ea51a2151c979ca89af12ccba78727899319a13bff3301dd196314a21d1bc6087700c5a082eb17bad87f91e825134659b7a88bc23d914eee7cfeac2177d219f7934c38269a81a605c5a73d76619a57bfce9061d67a42adb5b29c586bc85a97b57c60fd00ca24b17e82d0eb1e14a08e996f11d11b0e67e64c1d6bc9eda16f08881835ef7c79a469fae7c72246f9ed68471b722a124199f919cc6d09126f7d1fa35b6bd478fe66ee8c36c11a6d829a8a4950f733339f462830dd0b579ae2cee79a3805cb689aec3f32cad26c2e4fdbfe7abe17d4e0f6d47e33787e94ee09e7240a5d9f8d32db3a5a8ecb0b02efe6691f8ff069be66a3c51389602a3ec13233fe74dd84019fe67f3d7b645eab34af8bc030aa6fdfbe7dbbbbbbbbbb5bdad6b1a98fbda5f580bdf69586ccad75abb578acd5e3e331f3bfeb38f9de3a999643b66a06ab874ff3a3b5787038eb653ce2430fecb56fcd1669aba353241a519960dea55be1667c7e82626434683db4d743f33aa84dd56b202f4693196fb7d0cfbc1fd18b809aa68552cf895aa73fa35ce7df3a77be1379cd4cc7f4fb703f5dd09be6571db26a31cb798dc5e331f329e5b8ae33593c98f799e65f51cb01bd20f0cea9025294123ad8efe5ce9f613f7be7e7603f1aeefc00d82fe6cec7c17ea73b7fc6cad84f873b1f00f6dbe1ce3fd94f0077be8cf937d88f8170e7db603f16c29d1f633f2ec29d6fb21f4f71e7d7603f56c29d1fc37e4cc59d4f83fdd88a3b1fc67e8cc59d33f2693ec9d28ce3d30a9f4ef3ad75e1d3fcb7cfa7f92d16864ff359ac894ff35f5819337c9aefc2cea0c1a7f9d4d6e0d37cccdae0d37ccbf2f0039fe67376079fe6972c0f3ecdef6c107c9a1f8365009fe6cb601dc0a7f9306c04f834394e6c62f1a2d9a5e5f0fb75a09f98dab4551c52258dbc3813468d5ebe53c128ede5c32940293471adc03519802b98f222391d021c0ab93075d49f757efc19e3c426165fb08fef126d4bfceddd88031da13d590a5c4995d750ca65dec7fd7d8dce9167a3295cd18619a3525ebe4fc128172fafa75f5d647fa558782d1731e58a1f6360c10589d70784722fe78bcdcff17a4d9b386b7644db29499f2b6510aec45eaa9af868d986e95c3a3a2c3ab00743aecc65254d726ee53229234e08a313a6b71dcb34d226199530bded587ac5314c539f7b0a5c6311c131f26798a1a54545e57ebdaa334c2348b6b83442f33156dcd7ab6fe24c1c46652fa7e7e265069a86d4342adf45e19e48e8719caa0aa06bbaaca40a23b77233c3b87d42e213e18462912bbf98822b6ddec693aa1f9bffd8878707fd176f7e35a9a983e850679b893669c131f27d4c3c0f570e5757a05b74f26f9ee814b9f225a402c7c84f72e57f71a8e79174505c6984a72d2790f82ea2fbc20dd4c29572c592c3c5ff66dcfaf1e757c144932817e6b29226417736b95171f1461ca84d52d5264971449ed8d326f92c48703cd248d3d0974f781298a6be7c47421297095c037f2691212b5cc9240a3f4db831c65add53bc96f8b625fef4236d92360e8523e9fef39e179f03b5fc572a595f02c4712ba516fb837e0c2cdd8b15bfe815638cf167a42d3a4cd7c5bb788ef6240253f0e7caf944aefc9a923df615b3fe632617cf544b07ea409dcc8e2eccec96592db359666b66b1cc5e99a5999d99b5322b331bb3142fb30d03533c19243bc35917f69bb9f0760696e4482d635182e3872b1746c59757fe0bd7401b32c8c9aa464af972fe77ba72fea7c395411e233f4eeb31be73a4f9b065175cc9874fba866f48ed72894ae6af29995439a51cd7755cc3a5ffd550e7371f7372a3f7c1dc59a7e5ae65471716e113f6a36bd98d68bb96d59668d7b2d94e762d5b9d48bd96c5565792eb5a96b68a5ecbce228b89441231bf43f0097b1bfd835f0b864f582973235da38097ef2a074ac07f9e0401ff790f8fff1c898d7ca9f21a0578fe9912e03d02bc8ec7ab6c3c2aff878fd1cdde87052ef79f0b0fb2bea44d52e2a0b485be13361f6d0bfd94bfdec5f5a6fb23da789096bc8f722b5e908ae724effb14cfe36372c42f3657018937bb73b664ff23f6137b0121b757d7038177beb42d997cd6e25f96469b8122b293b4892b4d7266e53857aeae7c88793336f249defe3a433661b7c43bfb678e011a8ec0892d1475b7d7b081ddf22d39c3a13811651424c2296804cb44143c824f10070bdcaa02ed3185fef510875130d371227ec4772e1351765de0be19d588f810074211e34fcba1b086a3c59f71402a862e2ba122e87e8d73e373979550e1735bc321ddb21668539c71af02128787f458a6746d8a222dd039d80838f8f1299defe3a2290f2da594523a3d2e2981bbfc1fe518638cf37a0861c9faebe7dbe820693fec2b11208037484fe251f28828fd0684dc287db5b41e7cc030dcf83e6018eef62da3ffdec479dfa69691a7c374473ff2b84dd9671e8c8febbffa41fc3aa181d2f3a5cfa3e495be0821374a5e112b4faa9e8febe34feb311c51c84ee957523c1c961b25cf88d217d18122843c894422552260186ec4407e49721148a552a9542a954aa552a9148392f74ab88f2b91482412894422ad90482412894422adbcca1741fa0e94bc2252de2b82f4fda6ee388ee92759386a9529442082245cdbb23ada32cdeb081bfd36d2a494f24a2b6fa4aa4df3d2119422c8b6aa8d465addea56b74d04db9669da56b76ddbea36dab6d15fdbb6d5abfe37faaa695f47d7b66919a423b8c96cdba4dce426b31abfda56ddf9718b4fdec8eda23f8a5936920fb7cfb2d736f7cfa845475bdd648cb2a71ba5236e44afd1e8e2bcef7aed29fd9115357738aa998c728b18166374f7186374c72c8661eeeeeed8566d1c59916a96f59e8db6bad551b6d52cfb9a6deeeeeeeeeeeeeeee3fcadcd2b6edaa9aad367ee957ea68341a915452b8abb4a2421aa570d7685437276254e65e54a469628c476cd9b66d5e8f9ad979ebc8b5eda3df25634643c998d1e86f5b46336ddb1855bd2d18b5c922aa188fd836a2aa4911546dcbb4ac6e9ad7512682ac6e5bcd6a966559cdb42cd37e9465353e8cb27c45a110c99d98d3a558be42334b80a227719f1170f0e7739f6ff4fe252fc553add8de41c2037f2090752111f8241f0a81656490735ed33ddd951ea677fca74d328917a30927254dcbe87fc48ba485aeec1d2170867e88877e72767e87359b98eb4f1afe04c529ee4fd3d8943efe40a0a609b2341da58f9ff5c8e27716a397df58706af47245a57786da49d370bfb33334048b6e689b6c749191acbcdc8e6c6faf25d2c768c2c9b7fe7a92d7a3f440e0a53f8070454d43bd16fad9cb874499f7237a12cf7d562c370f51804ff24976022c233fc50ac127f9bd2361ce902b7ff3768a7654dc111f1fe83e2fbc21bf90dbf6f16e9a17013ec9b7f0421f46615e0f9707044e1be34e0f4b54f9de7dec3e1a89ddabeeeeeeeeeeeeeeeeeeeeeeeeee8f80f7d25d03873c463e84e2b462d3b257e6f2305c7ef8bb3c1c924f5fec37c3f2f548dddd2d778bbabb4777187fd97ecbbefc9c99add147076e2e7d17db375ec3371eb3160b230e89c1cb83e06fd8779ceefef32557caf09f2be14a5574892a3f72657cefaa9d85570697d7349beee367ddc7aefbf8361a86ed2350ecbcd8262aea18f99176176cf7cddfc5d21bbbc5760cb6a1b0ddd97e619ba77bdaa793b449be8aed1fdb294b9ac9665ba887da499b560dc5b6b54d64fda65524e0dc89df507b657b8d10dd7537bd3420a0066fa4aa8f48aa6a1a2aa3ca481f6114eba0943a8e9474c70e6a5974d269391497ee7696482289252cb6dadffaf81d17711edd0a8910ca20e7764bf88404f149fefcca32f23b98d9ab995ed1e8950f8e54b575c3a6eae3cd391f8610cc4f64d095f325c0292b0a184516777e041825c3cf7f00a7ac1bdca00977be031ac0292b07ddcf5f00a7ac9e1e26dcf94130aaf4f311309f07a3b89f0fc40e46a5fcfc1cf37fc0319f0746613fffb21477be0d46a57ebe000870e7d76014fdf93ba4ee7c1a3ad4dcf93318e5e2e7e390c355712273e79f38653959dd70e7c770ca5acda78153d693f996531614198c7af1f34d8c62f9f9308c6af9f9cfa88f22c39dffc2292b0ad17c164e59442fee2c6219393f8553f3c68551f6e7778c9ae1e7af306ae5e790d19daf716a0e997f716ae2706a12e114fc6119f9f33346a19ef44fe3e7ac30be359b59677daf1ec355afeaacfe238d5d5c9ed9f39eb9468d671680009e39871fc0009e99000478661b369ef9de67be71e3990b508067e6818767c681a38767eec18767f6e18767fee19973e430808e1d40bcfcd7f700cf6c8067d6f1cc3b9e1988973fc0c3a036e5c266af625dfc0babf22cf6c5db96b7f67f066b7fc5ce40b2a88f96f4346cfc03582076e830408e1f7ce801070f05b8716d10600002a8e1b968232b0ad845117b5a090ede4fe6c81cebc655e7781359ad563a32ca182b2ef2f464d1c94aaa5452155731ae564db86ee4e11d708d0c32657165aca24b0247842b3f480a187b9aa6c5b23c1d5753f73eca75d5fbba155c3592b6c195df385e63c3281891a777fa48ab5a8787c7ba71f9977f79d40dae7ca8e3315af410ae88c22832689a669b6e3c3b8005c2eeb0df1c72e5ebb006b039ec0fd607db83c56179b005b037ecb5362c01ec00ac006c0dfb59493ecbe7b37a3e2b0757dee0ca287ca0b7fe8bfd01637f481aece7545cf931ece7565cf935c837d9cf9f70e5c7d8cfb5b8f26db09f53e1cabfc17e7e852b5f86fd5c0b57fec97ebec5950f00fbf917ae7c19fb450f5cf933f68b455cf938d82f1a71e507c07e11892b3f07fbc525aefc19f68b25b8f275b05f3cc1954f636bec175770e5ef60bfd8822b9fb8f2a7fd620c1af60e1fef0e3f561dc7d1b0f446243065f5c41870caf2f19901148c92e1094e594992c0e008a3626801a7ac9f1f278c30aa5b01a72c20f9515a415046bee44ec0296bc91214e8302aa5049cb298303101138cb296e094252444821c46614870ca1a1a52e2ca94119cb29c3839028751b4084e59ab5508aefc38247a8053d6132823ff0911577ebc61948b2f70ca826245d102a72ca22b70ca2a2a6294cacb172fdfa3308ae5e53b1446b53c94305cc9852b8bb270e53b1538356fb4e0d41cf2044e4d1c2c3835894099278cfa97ef2b46d997ef4e1835c3cbf72146adfc8d15864c0187481637aaace014fc813252885157caba813212f532f29052520565e47b15577e95c1c2f819acf7315819beb3317cc976bf626778ce962c9bf2975d7994bd1eb3d6a72c060343030d3162d45083c9141363830d37dc2043c6e9040000c8c8cccce08043000290c30c1d686a6aacf5feadfd16fb1f6d7d16dbf22f2ccbabd8174f52f9ccba4059d2a72c1562fe94373d30303d3e34d0e09324468c243f35d4f00364320105c5c4042db1c186254c6eb88189900c194243a7d390130000c0c94a4666f56466e609141c70f8afaf1525001f008b2887976f15cdb8d161084dcde76011cdb08a74f8af6986d4fcd77712a1f1f2e1cf0ea8971f79522f5faa684c223bfc87faaf533c35653f79e7a3ec0ef6d3ee7c1ab6c67ea53b9fc6060c08e2e3201e170f2a142559dc5cec5b96344dd72a83241449e00782e411244dc3f323819a86956481a4fad030218b340d6c42662e6347bfc785792bfc36c61634c5bfbf85d2b4502f08306062480fc110dd1ef29a215b8e1392b82e6ebf0c28b77f865e2e55ae456bd489ecdb2455594bd5aa4d7cff93aa0927bd4c97d71f07a54d7cb1a63f55fabcac8b7e400696a6437ef48ff6639f194a67e80cf5a80302327068b9a0762b1e5cd3fdb00f8d1517988c2d0ba463da02f3095ce4c11d733373f04636d1441352a083261091852500c1082683ada5d0b19ab03a09b564741f1b3cfcb0e36a9fee253a89096e2b19e2c2c5c2ec8944e421288d81fef7a2021bf73af891330237c4ca95170f38285af3a2df835ed3f2be19bf78c0d1ef182dda91abf7d1ebd43dbbf093e1c5961cfe43ac5c29e4aa3eca3829ad010b5450051348413483254490b0020b374f1899020911de854e091472f0a698cb4aa09033e4244a8139c6083dbabb3b7374b7722cabd4a324dbe78490debcc0b2a4bc6c14d57d658815d92ea57b1468d6b1d00b42e8ded10931ca187d7a9456a06cbfa8cbd82e479ea3bcd5ca985117955718e201af2025383ae9c32160ee0f9e0d1f7057153e4cb80b0913089c1fc152829397950c0de17e1c644d81ebcb4a867e6e5007f10882010ef84cdfcc500d2e3883a19edbdd65254328b8d96525434a0c2db9acb0b2b0e4115a8cbabb3b0958777737511260b0a4e87a6a1241c8b0a0318340e765c9e891c3620796122c7ca0040b15164b748c21a1b258c1f1f3e49fad080f16f8c7dddddd615fe1eeeef0dde18d5be1eeeef0dd615311b4f252c8dddddd615fe1eeeeeeb0a9707787dd56b8c00861042d7081e96c50c20b96d0b9bbbbc3cc0a7787ef90de7e7a8419ea34dcdddd610b3984b0a1bbbb3b6cd81fbffd0aea4da2c41398e088289028e6fcc008482424736010a23044e608711690496f6e6e804c189826de334042820b5f68753d71228a9550ec264cea143ad06122b4b2d2a4b3a16b52430e51a3896571436432abc2da81aaa027ac1d21130b101490989a44af42ee48f8424343425dd300891248ac8206c988104830048d26b19b4419ee0584603e980f12669a0cc141540ec141420ebd8a28a1bcf023131cb2dbd8022e3e905e01d2f0e3c38755945ea004185cf80e6314b0210ce2eddd363ed422a4358850d20641e55218c53589a21c983641ba84ebda44654b10d606484a44b1ba610521df7acbdb40a5c20d29e4065dc2c536411f8421073c37a4ad54b8c130edb910efd6a92dc3841f70136630849d8329a61ee304ee7750254482d09de919349070330594db7d23fb511fbb3f7c5c40c885cf1a10bfd8430b045ef9f5bf9b0167fdf536faba3e88df2bbbbefec832cfc7bc3db2bfbc1ed5f3312f7c7a2f7a21bc7ee5f282f83df95b1a0e27347875c49b1b3d8d0817c1c0cf2a3ef0e577acf2c5d74a9b2cdf8fc3e447d6d8636d082b1dcbe5cdb812586dec9a966c24976345e058c6a7500bb565a611818b4fafaf7ee66535cbb2ea4d4bc22d662a4a3e867934ce80e0afebbaded2705c2b1d0bf4622ceaf7669ae2579bd8c3ae6f0cebbeb83f16c1656dc2324a443fc1f18f5092eb8f55593b1646c98f31ccffb0af5e8c2d38ec690eec7d87900ff368e493bfb4174cec2d6a7561090a821f1731b42ccbb2567ac049539be42481127ee0812b7f07a3b426b8cfb4a203eeebae7ccb6bc921a97b1633d7987f986db8e1d1347c2bd0347de503c1b910072d903ca404073a97a1dc86968584eac2bef07de80ba306848584ea5adf9a0d7df850b3bedf86dbe81a330b07f5388fc78e364993c96b7e3059f2c5161c2b11c2f98058969573012db1c4124c2cb104134b305182122411218e106d920e877084bdc5e655e4de84e093fc3870f9e3e5d8359cbc62a4b262c36364caf3674dc33900cac867526af4fcb55a587b874f48963099976b8559c9f64e36b238266e0f0d24501429954adcbbffc87fe43ffad2c7d2c7d2c7af2a59e9bb2bb895954a7f7a55faafb5b8f2db490f354d29eba151d574944a25cf7d873a1177d3e1c49c4ce3b2cce2c87e1429ed8ec0a722b00c50907f6c1a17596c9a14984294118dbc87dc278615dc175525ca715d8adfcac2523b26701d5551e535ad22bdca8fc748d37fd7f950ae7b934e09feb0d0118ab0a463e43fe99c08fdf06987e33848848ca8b7206254ca977de939cf38523b91133911b4cca7148fa3f7959eb6c9bf244ba51f79a5928a2c79a39ccc328d9f9f187fdc67c77f18e53e4de3dea5d9f88f7ca8a6c3bdd1a829e5b891e76265a6878686389b5796be81dabb1fa6aa6499c5e1bf33fad1d7952cb3b06bd034e8200b49765eea835f21f8e4b7b6e5bd7d1db8eec51a178cf294776121c78cb894ec5952463e7ab7cca7d1e87d8ceec8e3c7349bf9232fc8ce0f912b1f73a1e588b9f545166d3b41d24241e84d79ce6bbc0835c241d173653bc971228ef3c40130d558b08c0fb9b2b3e02bae24793dc427f92c4a70dc51e5f47a9afea8d46cb86445cb14a2110000008000831500003010080644a2e178244af354560f14800e74a24e684c1acac45992e3200819630c018600000000000800199ab11a00ea59035520a784a3689e404f64d1040249836de5456d9c30a8c91a008258a23c6fe9f80f8641ddce5c3832dd2e06b42287abd526f81175f47139b95d0cd9e964a0d1553f67ffa906d693e86e53fde4e66d959203a65ba40800f5db48765ed253d6c071b4de6807a3ff11067493ea0385ca7eb41c03df69505b27218cc9b6d1fdc93bab74e55993ec525c19209ce46eb48263b22f131cd6b9e411f16b1c85016471fa0b35a9269a02cfbff52482a93466e177868bc9aafc724afa5f48c49809109c21e3542017d488749851d6eee3e3916edc8acd46e28e24010ed9f557001ee8dc37abfe4c02fee7503ea90b5e9f02d8f66048bbcc5dcb97e01622db0a240577d4136c1531bb21c96e89e9e3f7bd6262b24757671b502258ac33767c44e11a089204169533c53bca7a008da588fbf2fec6e75e959ae8953a1a0d455db7eed170a6252d89291d0707410b8adec3d1580e11e110363cfd32aa5b378fd15da56ba72b6202b3315331e87a68e2326ec8bd2aad42efdf9d4bc0816b77f2ad3b9bfdfb98932577a8875b7f9ec7b5d2b99767ea989a712d87dba9f2058e61b518431590840dbfae20420b84f616469bf98044bb71880b3ad53e59a17e31b9d1c3aed0242d580d53678d9491476a3c7dbb98ad89b4f00db9c08b18c4fb197b9f6c9c4de9b17ef40b8da8049f13d33e78841468a960ea3a63a9c8b8cfebe3d607ac43934ac6e2a856f62e9d8e186fd3f1acfd316ab290518ce0c078988f20f99da9dbd7df061144a03ed36628ee83b80a9c897064158f126e359facb00a5434812e0a9def24fb926909c444b39dcf79cebca72b35bf2888de37a6060d6d9be6666672b3a0dc4ceb95a6ecc71072f77f82c1fa9d8a52a682f72609d7da64c0c76a334c3c005e6ae433c340fc532940588488440fd43fb19a8217049b09c6fc6ba6c96a9fe257a94adffed79a6f4cbdd91fc50b24e7bd14d3c49b16171a2d5dc41ba7e415226b21a3df2f9c48185c25272f8d2f91c544d08481b7c5d7c12a379c085f6620e960eaca9e98d65620f0658f25f4b1f5a88d80602730ad8b48df6d1db03ee4d7e1b0923f48691db3072b1b84333f33410ab341aca402cf0fbbb586d170422bfcdad5aacdf9bd76092338151187d69ad1d3e7aa80a827f6e9a11ea1ceb780294d90d7c09d161045c5e0c8b379a57c09991688053dd2a4e3bc0a897a94deeec6384d0db58f435c9dda039f2d49d1a1c385596b5fad9a5899e97c0d7bdeead3dd0b95d7fb59bd29afea60fa9251eb3b3ae78f4f213cfe4df70e60b43b278d92f113c6d479bf4a892be0c6e5821ab814780fedacad4a3551ddad18b93e7fa20516f7db51b4c69ee8448927b9a356991fe14bbcb048ff8c414484986473014ecd3828785b442da5be0c4d3d9651f3921de965697190a97da7cec070162756c703bf4df8e45b0baa7daa57a4f7505001dbbfcf5c0d343d61f425ce2b2151e076297cbb9b8c598359667929813bfd7b84f83e4bdeee5704b47877c7b35118f0ce632c33cbb1483fd362fb55ff7707162a0be490053291c631881f46ba2e7c946a3aecfde5bdd52453bc5913f11969f687184907d981b493b6e6acb15072282a0b223981e8ab90ded393f544d9e16977e0d9f21328b86b84c80594b39488e5190b208ba1e90e6e8404274e6dbdf1d27f60f76e8b5c28fcad5a9428b575288251acc038a05280611949f286e4e46031ef2e46965cad3d8911e09e3e3de045da3713d945293d50658a7f3f9cf0280eeacda4b2774d92d5d990cc7747d92313c44278fbb51a9e16a593f146c586e57b3aa16e609dad3f1137ed65c2d2f152a1bda031ed84b236c6f7339bec41f90957ab94f26afa5d3886fd1496df4f89bb01fc2585dc7b75261db56daff1c70fb03a7679ef2d1f635c857cae24fda97b6b0540ad6577e4fa29608e58fb3dd2df437c5aa847bafa4e14f5605e74666c9d86c76503399b1bb0a370faa1403cc20abe8318d379b7253035af388b3e689b0f2ad7aad06df1042bf7a2444ac009b96f524d03d03d5dbe371781009f7a2ed6524b9b956a13861704222afef5afd9cb64da65e56e8825dbfdafb7dfcfb6a2b8e88079b5b7a2c199b21af486da7cc012f88ded8f73097738a457af1b416b3aff652e5c0b2be4cb0a9a064efe9dd229dc52f17b72f41fa4c4944ec14fe2afb1d015d218c2618617ec2acc5d84a9892f1e2207d855921785809f99300999117aa668f9401c6c913ecd1651a98a05fa6555817162ed9a6aae2928519f52183e2216efa772cc97064915e292c1d297a921505ee8cd8a24b8d7407628fd2ec452b5acba8ec45b53d7d18f0bb5d057aa4a827a5644247d4a9e950b6acddb3d0a30f15a4a979f013ac2543ee71bae7a0fa79449f7932f13c5df41dc2cc723c8dbd5ca208f817ea8f814431e929d09bf3d841008da748de511e1deb238f69a9e9cd2c0764fa092eee23d95a24310dfee30ee6adaf5a1b5ed1186290020ac0105fca117b0d36cebe75096f46ced525d64011639b250d34a27c1e7693ee82536d3c5d7fc6e1a54644f445aa3f6a9bb641702fbc4fe49e3f652e56db5ce8aefc4443783cf9f5118dc8919f61aad71bb7f8ebcfddfe35e0f002d80ee851ed0cacb7098d43929c9507c493e6e6d6273ce8d8b2f1b6f8e4dc3ca13c3f97dbde2d8bb0a7eeda1e4951a9a9d2728743ba27a67890bb205695c173015e84aa6dde16d977def4615ed8029a405404344052ca009a44ff871fcfbb36c892180bcfa80c2818419a037bb9691a5332efa9a1df8639059d7303b84393ec74cd0b4b4e23843653e0dbe2f06b3b28363e9088cc5f34dba03556d3cd54113b44175e4a49b192e96a033f24c4766ae2a8b036bd32f489ceaffcdaee5e07983af6a3c020c6161bd7fe1aa5f4e91513ec3af14e1559eb8ac9b1cda30e0b961d28f60dabac073353229736e8642fac70421df337ac3e3f9d8776564c9d0d52062ef0789e860e872ac740d2f8eda8019e106b30fd36aaf13a47c5beee34cf008f4b492040145184983f4014e9a3998dd3d91424952521a977301254aa428e1fae103efd4d0ce1be50f6721a7e97d9b27fa578229a016ecf02b53e255a5831767b0e0f106191fa48a58bd8227364a22e5177b5f4f7172076fb48cefb4c51f06fb41f30713b627a5492937fd847d7d49929592ac4e978c831ccabcd20bcea01b07434f95ca533abfe4919e9d8e106dbe632de814a6142eb67907b0a8b7238602f664199a868dab208c65d8bd49f19905fb97d003688a66b36745247c24bfac58e0173eb889451cf512287d0c5333f60ef2f8dea8d42ddb044db2a261b942252520898fc0806c66710dbb7292de5753420704bf208778b91c14440840acc3a4310854f20c7e6b8e953731e7937522cb60dce6a91125b7cfd26799a957c606bea30de0d4c2d96c07508d3545a2e81d6a5be470dd292af457ffc064715460fc772445fc26be081a31781cd0c002c139fca58b8ba66d1af26e8d3fe34b56f7c5775111364bbdbd892a64c768cb09e090cb718fdf2996332390522ce357e3d2505dfa91fcd985acd3a4f2392d81b06a516816b6b475bed07e6a9806e01bca2022d07b169ba9c6f340c3510ad368272e4eb09d0c411370ce6092ef446e9ebc69a42500b71d6028890b75d49c40870c1ad92a8254860b120cac978fc428958b390fe89aacb3d696c340254d224d7ba45b3d86a21081c98d6cf90a131aab3c2774fe3185d1f7555b83746bcf813e4fbc907391ee8d127d84dbf8415ef66b5e0178dd332db01b944a5126adf5432a1e3253bdb47e668d0493c0b42dd15847d5ce0ac4f8fdc68518bb1595370632daa6446ab1dcd53f5f0297ebc94884bd599595279727337fdac6bd305d605aed6b7e040b9a7650151eee7753ea327d2335cc292ec8e8837c1c417dee5c8f3a72ee870b11f3b10a7ed87397e037ea6e5d59e327becf662a2f7164baff8ded70ecba8a1c7445d515fe257454b06c420224ff172565991b7094272b0aa7c34b432b2833f786a77ba769193c3004dce6af003d4451e0340eb75dc0a2e41e140313f7ccd2881e711a29c06ceb44b9d0e79b02186b7003b78629964a7ad369688de20023471e517f1c1fbc803e8614cd59aea091f743a69c4313ca2ae71e384f5d30a43c821716d565404b30211684c0a5ca15213c38eecac0d4031785a9821e87d10513ad862e3685787c3fc12c814503747008d1080f2efa14716d63dff84b978780f22008279848b8ee40c5d14a42a7b8e9325295c081613270b7fe8c953c751af899c411ece167cdfc4bd389ebcdd53916c2ca1f1d78ab72cbe623574174d3097f31303ce2bbcb701cc5850a37a080ae61a47eac6d014c996a3ee9163ddd29c18cf7db43b8e885e1baea8b7e6a037a6e14d278d251cdaa573d4c02da7065d8521cea192f64e515173044c97d4e0043f28619739fab92c13a904567e752aace93cfe96b6075917417e949064ad072ad2911c041f6a679718ace64330172a40b2df6ba90543cb5805cfd6a0c6a77eb7989958765838d2cf1b8a2febea623979931817401b8d6415694d97c118273f1ac7ba8bbc5fc21f034e0d43913be271b184ac3e12200dea379ee7a3360ea1c8e73cfb63153b181e016c669acdeff2df836ae5bf8f5cf3371a2755a23ea824fbb9c1d869757f2103286eb88d91e574f2e03d994bbb44c81bf27642af8a5f88f0b7ff9707c8527b940879003a10d08afa00f7c8c15931a4a7bce8775fe0360f834aff4cd0b289012064e8273eebe0341abc3c81042ef9aea960d9498f9850b89a933c272cc6c44d33c98166a88fd441b32245b79086400f1837053488020256f5819dc935b240a03e4d25529c56e2ae3b9b247d6135db0bf1f1f9695ca1b71ca0f7b612e33ccb3aa0acd62d5a99ae9307ca736b5fff06330d3f7c123346c9a6ba1d5b695d15f5aff60d6b88236fa1ee6b4e1e30792b9ca0a8411ca91f87a2b5759b3f2c282f2e84b897100ac8f08d8aa5a1a82d371d1dcd8349953157f84dcae9889001bb13d81ea94470a1860a8388a140727a09317f325f7cc1e9c81e541288f52a3c64465f7c555bf7a133a2cfcb9331dd855e19c75f6ae808e80ef1c0dcf9c1da776e0ef57da380639d261781b675c3cc3b868f65caf8c70e29caedd166b697344e3a47ed00ae7a24e4e192ab50f5d8d8f6da94da727993052e9fddd51a9a6085ef81ac5d3bc04b927947328fa60cc3f908c745dec1f02bea907f42d5acce5261d7165e4eb68f72839fbefb26d47a318e358db60c654cfb440bf1244de8992b9731aac776609dafe01c07a1074c761b83af01c0ba1d5cbe2eb37faf5dfe6d3ba9cb4d573e49d6544e07f5cd4ec19a75697bccf174226ef8a587928865e788042e09abd1e386d0d2fb4633fcc43a854aa98be711501f08a6ba3e54aff0178bdf8f1a3129fdd574fe36b2b3a55d8d3a244d5fafaebe446440e373d8a0178a5202a1fe0359541817688a2524df413a585dc8b964af7d8910bf0e007aa1badde2308401d4c32c82e57d66b3b1aff84b04de32be74fc17cf1a49f4ef4c59efe4ae8394faf72d1ef552f672f2f859e625aaf26d21b59534cf433917ac511a6e368b96505162f2a559dd18e64058548f626569cff91b5cf7952e3b4d81b42319e0eadf5183508688056df7b695ccb825657c3c708b79b4418ff7df5ecbaa8338710c310885bebe5d37ca5a1532eb8bf877e43f355679aad4a3999abef602515039c34092df6194b4a081131589f71cb7a9ac76522d38ba7909e8bedf88bc38c4d87fb3b48717c9027d734aa56358e54151e1df1cc87cfc191225cc5a4c0b93dde507282997e149113f0281a0447d4fe0ed1ae093886e9a629755b30cb4697fe5a918eb78ab84b5ddc82c1c7ac94332e533488ab2a1ad8bcc45f8a05d579d72127b2bb5b349cc536e290c8b49aec1a67f0bfda3d43ae19872ae450c6d6eda6262c5f446544716346fedebfb9e9aca293069947d2bd310cfd6801c95f33b828d762bd60571088eb3bb1069d268a3e1063d64c547e594a2713419c0f39537cab85e1ed7a5e74fea6d278f47b73e90e0773eadfe4cbd32aa89ea45495159ad18ac12f614c4285652bd5c6dec66c25c30b21dc9f72b17b9ef431ffb0a58118566aeace9c2fa6a89f989a40598a683bc71a98faa66ff501dab2c0c8b9abe3d1174dc75de5d901dac0df23865a4a6342169cc7d7cba409eb987d9c908d72fa6b2e126d226134369f01fca4b6856758db7ac09f2a648278bfa5dfdfa3ba4b4922983a091545eadff7a81b9a01c78abae06b03948fbadcfa69081402725bc26f5de980c1491c8bd0c6c9084ce798d1805c8616cd21079a134a70ded854eeb3bc5b11dfca14842c6e8bda69de0bdc63f6d722bd571aac31cc0cea7c8cb737bd797edce947e938d99845187cf9bd600a61aa8095019f9aa61846942bb8d57f64301c87e41e145e6b84bf909f7a5b3822b467ff9c17a87878a188c5f19f628661a48033a42633f24064b90dd62c54071928611c92045b87b40563213e515ba96cf8105b014cce7e0c8a742ac289c63a5701d8a973b16581f0f6fa73af96c06f511e4838c7bc94e4b418e86ecb81505eba5d417989917e926398cd5f8732f363e892f4ab5a99e0cf79af41e929284160f444411d5f7d07b0629dce8d485b4432cd828c09f9fc0b9207525f1981092927bf7de38bab2d7282df96d9be4116a94514b7b93571ef52bbc1862787f869a679a7d944d14540d0c3844c4f134413e678dc373d421629c7eabe24249ef9f66c46b9f9b0399093da95d59a288995acdba9c9d46cb3ce998d3f868ec08e095d0459c811b1c1be26cbda0c907b36aa1864acf48d9778090419bf5a775ce22eebcc63448bffaa8d09504fd672a0530fb8b4635b5e538008b6cec593f44f68f05f5ba110efa462b87456f9cd707771c0ca103cd71cec7c03c1cd5ef6ee7ae16c9453bcacaf5948d003568969640494c8bdaea1b05e5336f9da638d647bc2fc8380845a9a24338c479c9ba433caada29419cb3decf24fae48d34760985f4734a271260811c3898562a621fec72a66caedd9b80824b34dc9ae0227c18a0cffa7e1e88bc50a7780451f329d32f712b1473a116db6de6a45d66703ca6590f523f00c0cdb37b8ecb6f4072dadb0f72bac3ae3631519ff1c2c9ce5a2d3b24faddc89976fcd951011ee5013291da11b6ef4c67ee88677b5d88cb14a5b13d305b9f0d8ba836715e5c3f39b0e602efc6db36c8b08e84e86f94e95648aadf750e926d4bdf98a09c9d0904a2cc6e80f8c00b00a26aca6e1106a98f7125cc18d7feb8f9720feca8167b82a902b8e1b959eb68a174681e1a27fb028e2c558870603de96f864f8f3accbd63ab22afff117ff29bdc49b0dc99aa9b4cfcc844ca8b9f2b6bbf849c57fa68666528c6d3c90eece88f3e93e2bb156d103281861880fa189a251682cdbe3bf5524bb1b0a695cd1133a5e1b77bfac809181f4ddd6e7cee59ad2ecf40b700424087df1279816b5d52cbc042da4ec7f4b66186d75245d30ab56cedac41e9283b61d18d3d6ff49476e402e79e3ac7554adfa9db43f3b820ed25bdf5f0ce53697e1f157d89cdddfc4e8f1a76049c84ef100af2decc226f4c609cd1472e4ff0c65e6f90bf6f1010c8e0a345aee44684796c58690722a3f6d198f5902b3e610e36192def1476ddcb1c27ae0a0d152280b269783624f9f8a2cb21259759da6dbecd69c5420dcf47e545a52cc93dc1ab863d472e85c31d445096a44f4411eb362103dd95e3a0b4a981cbca0087db95926bb6a0e336bd087d5f09e32fc809e5cfd1b901dda5806d7ac3abd39a8a9fba18a1bda50375fdb533ed9994fa72aaf85cf848a9180b89263731dbba980d6b231638f87c030eaa61837a4a24be02b642c4d28c3069684ed6d40306162e8c168945139f1815a30c65b55052fcc9367b4db8d7ac036e93fea8d4436a33964e1aec397178ee7c2883b2b8efc02620fdc00b17021acf1de224642b006b5a0a2a71e3afcc2857f9907d64211cb09cf2babd9ea0387bceeab75ab3d0646928d662ec9d5cc54201e1c18951dc1df0b0c864e72e9fb3d2c3dab30a6a7e95d42eba1e9c2e5a9a77229f2c88a6d98abd131d4a5e88162ed8180d0ec4bef26f3b218b499f81b3f5da46a0c1b1502cd05a26ae6dba6611c926b183520d5f1bc7cc855e5734ce7d32ecf030f4d988afee992839df7f2da7cf50204a8a33d025c6aa5434ed5e8bf6a8470b3f054a7db3a8865d7853b636cb05919e5dd03c1f81decbbe9d9ea2835ab1790c9058353ce4ba36652fea75b4fd9b5ec79b28bd6c1b26396fee7003664e568180607c59a603a2252f67d6d6b3fc2c69fa9554a0e2be76f50644c92322cd42c784a6bbd8f3b46690e906766b1570045f7a3d67cb7fc06396e4de09c961bdbc926bd5b7c5f79affabef0bffaf472a4b471c19831177d675162a06d1f1d237451caefa9db4073fd91cec5ab71f63ef357bc5819e5eb4a6e91fd8d3223129d39f9c04e881520e22aad84cb83aaa1e4cfdd4c16d4ed1a35c9dc24c07501b603809929f691a2ee3a5c541987fcff2859782d040324439804bf3874d19ba852cc61b6be03c8d4fc0738d7f5a3f865791ac6dd38f1e0c71e46f3655f35c20e944ae21fb473b23cb122103f0d90b5665eb4bac11023703b7e56e4d9372a31be165fe396e7938004573d51be261d5f3de182fd6ab3373dd8ef46abfe61bfaa7a65f292f641351848efa954632ca5f8f7c18de448dacf50aed763439666d2938a30a50642b234bf0797e22093b44759c8017f35dd56ed26a061392e7527d02ff9cbdfb3c7f34cdaa3c2f3fc25847eba315d35a430c5d2371ee0eef50eca099609bdb5b085d9965963a97ca9c7a189ee054efb977ecdb27ca381fb613bea0e6d87c2ef12c0f20d65f3af196d4531b41b580277dc1ecd57d3e0bf950628533be547c5e0a5ddc05ed17c1709a2accd7c27465d4e527d9bef514027755114ba90a2cfda7616dc121e5a64b9c400ec1ed1d7547e441044bf0ac1bf732835d542f7303a312e29a62c61d2238a71dc997be13a8a76e5424f3b21b7d211381904e7bc3d7a64cd29bf9c29e7daab3bb8ca49e4ab4621b22e9851fb9deeb6f5b547c6608e8ee9ecba63814e951820ceecf7423c6b2024422dc71c4902d8ecb45e62b9e7a8c0c5bb480c59d9d78a6bfac3f4da2bba808a796558923211a70e8a664f6a70844b28c6b657b8686a3a42c17af7731da762c0e56f2282207ee160b2ad375d6f30595ce9fa26517cf9b4707c2fa0f858d0e4334b77528a58ccc7e1c385fb4cffb6a7fdf5e564e56e228aca8242168f95beb505b1261d084c8b6e788d2bcd55a8bd1495f1da525c29bd7c0e22adcbbc4744e81fff21565806924b429e2b37773f8273eb81da159273b59da052cb344a61c601ebc32efde9a649d3aeffd20378e741969d28c8d553108b5da8432c5b5820f31f7d1b2fdbc759f45ba61f578fdfd6eb71bf3c030ae6535850b8e77801df2c44de3fb7875384f54c46406593a7fc41c0e9ae5c985933956e282513b3d7071d00edc402f242f425851d3137a1003e1eeb81885842283c8bd11c80271aec3b4c87885544a180e275c6fdb7750bd116000fc56618442ca2cc1286eb007d8aa5fd71048f143c62c1d84c55f6a6dc997271aa62ff4c5e728650ceaca61a770ea19f4f17df0d9c529e83e9658c0634ea344426f8ded0dd05d5301a2c156c0f8a2a7c8374efde8c2da2a8d36fb0ad11746bb8a9d3010e39e483160184e87656672938b4b82693bd5655b7b1251c631be9bc1c0d47df5e3a924b1ccade614c55d3d504318ebe3d0e487e9c69dc300e4612958d6d4c6822768ae26302db7520ffcf84d308b173cb0b1e33e91347e20ced8000b1c90f8b174f2bc32185124beb7d34ceccdf0e45677b7bb212a76d0f7591f360f4464febd083aea8094aa808f83960d3c0802038c310a8b956bd0c597cb45f9acf95d2f10bf4c608a03c2ab3e8151911c1d6fb9ae067100118c262e1d0af347bf22c02c6ff046c079b3dd573afd46d6f019f676a3d2e08b7196d842b756d2c3883af84db34c07257f2b8ac6f45ef417ddcf025b3c76141cc90016e8b44ca2462d4a8380eb414d442d7fbab83b06006cc90fee79b792510311a8c116007bba3c4d3909016473786480de8ce674c9304e042558c15c9a504d3f1c4222ff12efe48cd9448b1167f78727354c23326e92d240914913814b08a73e1044a51819098810f4202b887f4734bed141a5d854abcc228dc82e0313107c61f991274f9ef812531752723d08ff2b6478ac92b70f2de91b38b95eea75a45c72a065bdf49c1c0ac8d918e4376e9e0115a99d4e08c3a684abe941d30db322260e0d7196572726c23bc0a53c3e1be899ea7972bc0b1dcaf6aac010bf75d31bd4baed41ee2fb9abff451237e2ba97fa5afc31b87eb6515885068a591a6458bc37f56a2411c873fc9adbc12674fcb08b27053cb09b02ad19da6f16131fb5e253d82325e2366311da64bba8ed3f7bf490af5d97c49f88a923ff169fa7a1710cd613a0cba369d3003818f4a0c972b3a7368f5ccd6e3077a4ffe25a3e28ce673b82695ae2aedff9371744d7e3eda0b020520388ba42f7451b303fe2aa0763c3e644889cad0a179b65782e3a304e07ccaa4ab0d66015653866687f951d594a5c8ed401afec1b44f41ee762311d8390ef57d2f71ac9d16869e851012ff03735472354fb0a58b73fd7fd6796416238374c7bbaa102f802963a9b33d25094fe219991c6874b67924a0c5737b16b9d2b1c633625a7ce3d434089219c32ba26079166f07d321f9217b74cb2ef356e340bdc3d009840e6d0b6607072c264f6f777e6e2c9813df80243d227b73a98a3f6a7b17b99db2de6638c5d8f9edc05d01234f6e08b8813b039f22d857c0c87fd73c733472e5ede1bb3b20b25b93af03ee199f3392d2791bce89cd9287954e1f8bf8ff0e6af88bcb4422866421a8247053f66110b52efd0e2af6df484b9adafb99f3bc0c4145363af583815c6b1c6f2b8920cb8701f71463520849d991c4424e7a7e5b426339a3f730db444e672c3671c6e7c768ec42109e82e8aaa7f28c9988e0acf2cb0149fa3c358cd24f43464334f598e8f7752e34cd1da8222fd114b514ae19f98f414400285b9fe4f432a1c00841d0cefccf6ae30699477ce98ff3536404217e7907832945bffdf46fb6fdf30bd4e38fc6b79c32bd4f92cde4f13db6c8ed7b88a97540309030fe560dddb718cfbafa1d2ffca2970617442a325779774da6c1367688d20d689d40cb4218b8e80f1646bfdcab2538990acd09c0536c8dc2dc8dd09e810c100159026c9bccc02878aaa32b5d40f784b414d279d5404c39026fcfb43717c3308d699508ac2dbef864744eb4a7a3ed36b5b584976fbf61b42237a998cae8ca09033b1b051a54140a2f05880b3780a47c8aba63c3f870ff0f8d3c44f25d5710ffb3964b48fd8218d9994ab053415c8092b39b940c563beb27a50d225d09143de1e3259eb4985b0e4262dcd3544144ce48cb87f5264d5ec0ea0762882952cd2187baa8892af7a849ad8a917991615eec52cb3b3e515b6d742662c8493c63fa016b757049443abf779557a696de3740fcb104a791ee169787c2dcf30d52cf468df4be812faf74588d5cbd65bd303a69316b2f0c5822cec20b83a3098662ad9e9d596f4dccfe3f546b329ce47636aecfdd2eb67d6abd9ea6358c5f52392fd77769d7db16b53e4fe976121a22f64f6e8721b3b1abe700ac403868f462bf58defb3e4566d81b5db14e6d58ee382f8538e0f38a7dcaf65c0d070af65b88e31623e70fe73fdd6a9fc9e8de3d14bf6486aa64e00f8434c3e1e5df7162d68eceb4c4dc2aab777af14eb17bbc627d9da684f007bceff7474927e067b24a0dae20f02c5da9a98b31b70e927430d1f5332bcb5228af1fbffba7c26346e49147211f81e1df46c04bae484d70a20352a08aea3fc25fcb01807bd0020e9c5052f2e1751327742f30d00e984126a85ec303cc3012628c781d081e0087a1f1cd5a6812911d9522b210e28551ea01ed3e3e25429b40292c5d3f009ec9b9b6d642e46744e8ad56300bc52c5e2077f14af7505a1132c6c859de4dd6c8186aea3c508a4c06153dafb4af1c9d0dc3de5d3f94c9dc6d7fcbb6dd4d27261088f805f5a480c8ffc150dd228e9b4264b1bf6303be17c7abec669cb9e9a79745ba28070352796bcde752b040e3e49da6997f388ba8b1cad4268d99010a2f7f7eca6029894d26bd8642bf8ec0805c186c1bdb6937ad4947245d92c11028f49a79bd872df38d4e87b33a8d546cafe302ff48daabea377d6c999a5870c24609cfa9bce7c445e650798de2ab6811a4ccc3521e8c35fe8207c49ded541da6ab5e7831d874f6f4c489701ede7a887c5cdd9af46d03b46e7a63548ae9820a1e42a29ad747407fbfacf6243fad7c173bbe427f7f3ed5b1fc1864e5365f5f97b90858a8c565899aa17a472e5b1b7e6bee8a75c2021e46f61a4bf69b60b70cf0122341853ccb4c7f33a4f5aea4381f381ad6ec092d0259a112028b8ea70ea442f8f135fea2d58122ef56669e7804e7d51b8efa1df442405f018d34c884dca1077271c5d8ac62f6aa6e492dabf01eeef185156eae9af1c04093e93b210273a81b98227d118d4e0ac407e62a6beb13fec0f2a76d5b05372fdd7a47bdc5771b3d9898026817779a7ac826bff79a8120b11bf18aa5602f691d46763b5a5836bcaf473b9f21d113492e000404000e35fb2046fa716c2129d64853cf7e366813bafb576c0406fe3fc5abbe4cd0b8ed5c65028f36b806ebcdea3e3c8d2651134049bc124cb68fba0e3b9ceb98f183dcf6b9cc409d2eb87340cdaef0bd93d7666b58e8b4004ba77d6c3d65b108b8dd23ab4b53d08a2ab59b7b236d12b302bd0e5d10a82ff3169930b39b1af4e01a26200a5768a8d6dee31fc3fb3ed2aad216f3066c9bc5bb5fd4e9b5fc0477d2e381df4f10b95fa0f0ebb89e2694327b7f25b698cbc8ba84e3cd9559a0f862c72bb292b6d250e17131ea5534c2a490f78ac04f640342e1a96e894a24b29d884a6e4e0a438b4633b2de414488d0e245a2355311d1b4085a6694ce56a45110774084ffb846c50a3a485c2a9781815ef6601accc13d5a27578908320c8e16b55fdc26e806ae24b2a41c2c76851c4fdc1ac7206fd0f429f7ddb57500bf9f2b424cf72122286c52c1b9692e16af9ff4dd4beeaa50a18b9591edc648bd10dd5cce12647e3cba97f5f3164509fd699c0303468d88bf2c3fe9a394cb373e43934961f9fae2569cdf2d403415e9b3241955c578914f8a28ed2a95854f070d3b83df2700d10e0eda53c91d9200c1013aad2d61e92881431b135994200c4a41ac6b76e7b3b7a39021cce83e16c4c4a6040c12c95068d91de0642233a860371cea4767de3fc6d6f1ec79eb272f59de114781b66c6f5e3659dd3c304563b9d69a142b68185c3c21e53b733915da26f3e45e0fbffddaaf30b4901240fcbc34175ca0351d1744f9d743e17643b7390f183d5ef599ec445cd3052383d7b18055457ae8c2bd387c7059de7f9583ebaf4b329ad3a23823a38739249867f6074c702e8a07b2e746a9ed81cda545bc5adddce863e7f19376fafe9afe434cff688f481ecdd3800ca51b0ae223652ed3725620b1eb00f0dcc4ca21ffd6d8e409029322eb7bc033d6f31ca9d1305e8b95fcc7c33bd13233556ea3e56d5245a4179aa50a8be0d3837f15d4433919a077a5f7453d1abb63821344af4723f6cbaad97a978d89f083467d5ae01ceb298987bfc9394b8c9834c3559ab21b168dc828be04787c2228d4cae6de8fd29e632a27d450828a5b36330f6c5d2663a7011bd47eeeace7a4ff933feb7ddf70139cfdf53bc3584416732956af341113c4a186b5a60c8461a315c188f93f6080b1885fda709a2f6b44ae973407c08b3525b04b70a731125dfd956b30f2e1127f7df4204b38015778ae988cf989dc1c07e8b44e2bfc641c9519750425f289ec2196b847fb2d93d000d699d6aad56ec0baad63e7048b2db71f9b6cfce1fc6025e9fd0523614453b3a37a02182ac26225d35f28021e4b2880fb35c5bb7e235a0c8c042f84569e03adb29fc8f33c85ccfae6ceb8369278f00ece37c7d7fed9d89cac7145382429580e570de4521c7def1c3162a743bff9083f7feed309414d947a7795266d96c03b97f63d4cf196a984fc804e930ac1d2ecc7dbab5ceded9476334626d084cd5c8d215604b3b8e4f93408bf00fbf9a8cb45181c15191344a6a0871680eb7ee3b76f305902bea52730a83648b0fac302c6a1640f1ad1cb25c642529fc8ee915797d39dabc9de5c92b713a169485a6ed00ceb903bb5bcba5820fcd08c30ee7af22ce8fbc659aaaf8aad3a87d47ac36a0e4db07a7c7f35f64577a00f65c4ce97acc42cb4d8188c1383c8e8362830cec08ea7fa648e853422b85150393464f837d39cf19ee20f9d532fd404f1400ecfc84bdec9eff6e19175de862ecc05ff86265f9a1e322cbed87248ccf86cc51b31dd6b0edb9fe0cef206c812a1169575c5363d67d2d91611fd5cdf9fbe6ebfcf73efe9845c6ae40e4e337982167d261f3a9582a963a7402032779cbb8bcff9664bae3d350523415d8c3e91109eef7838071f144a890a46275f26cda9e865239c8aed6ff6be38afa3648865ef01ef6182dd359ad3613429ee420262098e453d43e1fe45e01c28d700c2b4c1291c8e3dd54ca66fa4068fd240687d66192a06c698d74a198b6ce9ca9dfdd4fa5f0af9ef1d6fa921d3f4188b33db8f19a437549a87b3efa3c3c75558c46db7a66a73ec62ed9ef620fdc2553b9b2f4242e43702dbe1786f1c3132c86abdd76121204d1cd075a6614c0de95bb99682efcb7f39212906f01192ed12072cdf305fc9c4ce8f15e7e38a46f73587017d4909621d3dc2d10f8085e1191007cd08c1abc20428a2e090374309fe98a9b4feda76623cf6b2f4679e3718446d228222524b218d2d5eeba134cdc521ff83baebf50460bd3a5c34349aaf8771e7638ebd3b76524588f5346c23773973faa7f4a4c1cc7aa722a1266351e23918177c02bb61aaac192eb58267cd9b50cd69c1f97796f69a0784eb445de962b89ca9d21125d4fb1750686711ab4a38c3c582026c867f791de057ade995e6e8fe4b3924f63b13ec27201f4ff57054c3f0bc1652c4cf385171a9335555f609da3b19fbe7a585daa0218ba6d7db3495cb5cfacd3e0b34c304854615d37ec40cc8dd7a057dbf80eb7358a556095acd7770b1348d0debf6616c152c7bf5957d2b955f07cd81552e1fc684b6bc7127cbca4f61880fdcd1315d7f765f57c61fce337f8303fbcceef5cc992e25682300f35705ee7636e68c20078eaa4b0670a0d55ac340a79ef002e60748d22345cc3080607684d63ad3b3968961cf766484c1cddbc3b0d89be8edc93814d202d8c6195b71bca06eee594710a4a06347bf15ab43fd83b16bef0b0e094592a57acb5fef37fa7f8cd40f91aa376b9255df405d71152654031215310a95a3054fb1374f6c3cd7b3a8cbad95e85b63287ced792451fe248b257e51ed86eaf611dbc5ac0df47ba24be352fd79e35b549f25c06611c20c610115347048d2fec519681a07803134066352c98e4d566d1212dab05955d4d8f890b66b7b2ba6906c03ea22a50f803a40dc200ec0be0d8faddac142159e3a50254319ab43da1d33c83cee3d8729783aaeb90695a33cbf2a66a98ccfab0c12078448c715847be2c665d22ca9a39f8f517f248f9706e63eb7203b8e7a7ec6493fb18472de98131de4d8005084864eece397a636c7d93113c14cfd77fd814e70c519297e296535ea1823fdaa18c42e2922d026d44db2d3c3873006d17ccbc6505d66ef187e7d41836079124027134a46eabc3bc832737530f7b28a77db9771607af62c0818fe9fd43653b188258114d281c24fe5f361f669632c0fb3f5221e6d2149511a07dce43d15682f6ea3a6bcfd0784ab81cf86241ef68fa4ccc53b76fd6d515719b397592aa35c8b97c09aa4ec1a90961975b97a2170f6396af9bf67f20d6429bdeb6a3261dfb18b77467f1d5aa8e5cde2ba3bdc4758602dc0d9b7cccdeb56bc71fd9a07979e20f51c271963ed95e2d32fa14e13ada5e192f8fd2b7fbe2c1b5f88113c92d8e7925078e101056d007b60d9462efd808ed23d2d82f2e8dd3dd393cd069bd94b652a336592c4ba43bdfda3e8896f1c27fa20f9824c6ee5aed85c3e3bafafbde1598f24b484521f87b8eb2061e32641cd1a1c64af39d7a666fc32ba32122ab366e891e8e770c7bb25613518f50c635d4eb26319ef33cb54fb4dd94a3448c3f8349647e20393e4bb6e34696f41ef1052bb39e2db5447ef42559d23bf794564f843a55820f813b7090960c747fb752043e2a48e3c56dc991b0e9b2db6f6995b908579f505c2d6119074de17cc1e4d23521dc139d674edb91753e816fe4c0f82718bcc91ea8a5898632338e38b00da3ba909d9584bc046083d918fea671fe76710e7f76e08f71d652788a006a2e3dabc1cb5b090688d2951c7f67817518ca659b9a368a5d3f0893efe54a8907bba94917e731f0fe5b2751429f126ba560bb7a5910455d275e1b5e59698f69b9669a6c92fdf322fc8e2d388e8453759893c3f32c5368022d74d924865d9e81e198334b2ddc400894b9cba93f958f91191ad4363b7c16fa47b81ca954f0f6d505c5c7b56137cf619d8b5a16c0d0f4dbc74f6a292f49e3057d2a0e9af3f36f31da086f5bf47443e55e710828cfea7dcbe88be77ab30d8e7c6d38b7d7187aeb3dff7be13272a2ed47f081186137b9813386547b7c95d02407c7b9ff6413036d44e508acdd556c898fffdbd8e40e7424e6a478604f9615bf04a2622bb167294e0483170c30879dcf9b38125b020f28320d264fbfd6efd132eea18442548ebc655f5bcbc89cfca8e1b9dc638a29f20177f62b10eac971841841fc084e219352f1c2690d65902f44e75a2576ca3710e2067aaab1dd98f4727dc4bc386e578ee9c6fa4c06111eb0ddd3e65d11363e3c7dd43ebaef07ede73e46b5dd44db77f87ebd337ae1d008116b7c329bf3ca6a688e79056dd30940090e3aeaa27f80d6ccfd8ac4ac049bc278cb14117734faee983a81072836e0a2a71c6105bcb013d6e053538e6d18962c1414623a9ffdb23dd5ec63edd3abf1100c42bc0efe9d3bdc91fd7e6d2b5de332a89e22a47c00ec65bb7934889638bc1e4c19381e9c98285616541bf0303e1d514e4ab0003bff874a2c1c918e1ecbe1c88aac999526c0acc4b3f667ad2ce91aa72695426942a0c6f09a28a8408d81478c4533b9abc3e939034fdaab034b0719af221fb66205b7528aa73d5008b2b7157ab2b874869eed1204297faec5005de9e37a3cbd511caee4a9282b1ad0111a12c9c7b8f2e1f44dd155372f9084d948fbd0e9c3dc88fc300085729b222c065fd04024b32c621531561d8dad2838cd1a328f29431a4f8244636dee1cbb471f8211a523dcb4ca97cd3debb6875c4b8605ae0758e7edcacd680ff541a5012f1c8ccb69d69d18e5f28b872ec842b34f67c64647cfdbb6d9ed391d1600688305d4285d30b0cef744b2f18846f48d525b426e925ecf95698b71bd0a9ab729933ca03a175221a7ab3cbbd68dc465a92575883be1db76a8b3029d55858d086a5abc19e9a4db338ff96eced1827504510630d1fbaac5eecb4366cc2492fac795b733585d9bd6fb5909ec5aa839228f631bbc53d35b34951c3300771b819be7dcbcc0286a3d93aa7d4d188023ad049019b936256e008b3f8b7ebf86f9bca0ec4d6a3b2687135681c61545554f32bbcb51b54cd985623d055721df38d7e61e46ca3192a3e050678ce1b88eb68dbb856ad7b149b2607475c2696743af51d56c827dfd230fd175a3e770ec8e57b628d5a4e4b3fe8bb892ed2701e32ba640a57dd6671d9fc31e5be0a76cfc959d0a055b7c61fc47b9145bc0fea88d3f8d95b9fac22e12adceda4d7f6497484d8fb70cd4d4bf0f8f41827abb7165c70cb10e0e2728a0caef00dc8fb3a1af83429e858ec387fc746fef255915620de4520875cf1024a579a90bfed00a322ceaab332eff435e85a2ab50f1be07332cea5dc6a32154064aa8510899bf565b96f3fd582fc0b5d7c57dfd5a49ad5b22d2bdaa12336d7d6773c46705f602e2659dddd6c6119d60a3fdfc6e937e0ad11e1f48271f40304542249c42771e8703b290f58b2d318163a1a9fe38091f6d15754a2b50264765551362e6338d620b6c2bc70e038e35785be82a27249d0216ffc2c1a390093c4219735a48f42ed79a9c68f554960ae81cc0ebd4ad4871ce0dee9069bff849b985fdb985a4d178e210c43acbe0d785b1668ccb7946ab0d36e716e8063ce85f969847dd883ac6073dd7e7bdc051d846bae9e2e57969b4b6074e26b0a1d86d8981418c994c6eb0f2339f0a2e48dc084ddc6ca8239f4bfd3545908442801a71716f19c7efdae8a2824d453393f772e8f7006d20ad66445fb63809e34851d34719f913fb475f76a27d28be18f4da6dea7faae43309215d2e22bad7808442eb518647d0d6c6e75fdc5a014c01be181f7720080c1e7d8af60d083f659811604152ed8f341de36b889f0d2bae04226badb1e91d808a8774ec2a88758d3db3640e4bd40645268327d614a3975c6a8a7456d77948dc8cde0a7b9d65b44efbee85699f3bc0ffb59bf648e1e4520970948b5aa7d890871c75991e56e3a06e4f10c9800680ff4deb0ac05ccc0576e1a92f78da06af0f212873ac3820afb2116d17dc5284339b5e081175a78391267f728dbde9bbfcd7750deede396a3c51c179ef44892704674b89635915498d76095ae61fe381bb4a14883290052794e320113801a6928ab40430f0980d76a1d940fb5570db7988ae26823ffe6184bbcea2c7d17ab81eae826c21812746acaac05b5fe1dc6fb65482d8ed2234124f697cc3c9180f468ef5ba42e4d5e1b780e3d96628a2621a91273da30596af22a3c2d9614b12879bc7d2289ccaede1a75cb7f0d3bb94d2ab33a50ca7120ca10272123ea1d98aa826434daebc48070bb816041d2f3493b1e8e3246564c09491e153b30606691d0903221e88fd48c5bd3fc610310236253ee3d52daa8e3ee685758cfa400dab9b2f277ac65da7bdfb3c63aa136d289766461e142d902861ec78e2897b5d1087e18da51dc64e00b363dc3b3f32825235be2d1e29059b90de1106c00ce47242f7ca4b0eef871d291530406a3504a1607ca1d5aaa618c6b61938730e26a1c481538aba07e700d0caba273834a9745af9877106d713655c8387355ce4138c61532fcf716e7884f7f5805ae463439518ab4d15b3849955deb318748698e1c83ee2e1502e39687c85e0a24d5685f1a627334cfee48d9c1449cd034e8e1f6f0f16a458637bc44be0e09f8b4b95e7a29bc1262672efd0c09b9a1cc9d0e4816c45b88be40161a7a16f4c93004ecf01a66177b4308b7924011ed8c61128fef33eccca9c8d8ac7d598c93cb1bb6c67e275188587007b1029fa39cf2be836df2609890adf99c1188e1e6a58f94b81ffab85de9497b3f92cb5916dfacdd321bf338fc0a72d8d70134b2e6b1a7e33b16624861a5c56df406047bc34fd2ca07ae0b45b0819190be4db211f551ba3c2422fb1022ffdb11c72a6c2f532147b150f575b905bda05db642ad4013d8793a84a25e014299840017c7205021895f6427936ec85f45646298cf3abc59f920f041ea187c3c5d09a88b8bb66a26c672bb028f8d4d668a2ac38d5eff76528d439aae2698bd0365bb864da9fa868b14596ddc4206e7636b8e0366f25dbe916b2fbb3602034f088442b52c4b6843694454fcdc4591c2665db282eb9683694da522f91d370281344a02996e937d2c807c3a5acda5f5f19dc4d86f799f9857abcb8e587d9956ad37f7797947d522e3d9d93418dfd49a98d8b26c417b91c7e8fd29c1e2b9581eddfb404fc1e205245780e830b2f75dd643286fc5066fb55939a5f86aa72ab6ddd91c52e32f25abd3f8c6df03d96f5ad39a2ecf475a9ff9812cb2db030576f4988334793b4f4cee9f2f82e2777a20a39c1de9b5176840160f2cc39879cbeebcf3dbace26946a234d9d7053a1fee4edb472b250a14788b3fc62d78cc8d416c0d7e0b7f13c3211d8369fbbce0f00a7721b872c20a72bf9e0471909be0714d426d5043c57b81f0991e35093d58539d577869e6b28c3b438d8cd379bf3ffaae8c8637916d8390ddb477d155c7a05ae58c17c8656d9a7c39f01466f8e699b06f9ed62eac662a6e6fb0c4ac6b7f29c1c71e65e26e17565b3aa41a4b738445eb6cf5ef8b04717fa365cc629950e6760384a297fe2df8677f78bfd7321ee53fafb86e3b85522df486ecaabfd23ce18d7c94c8cbda59d3d49d163deefc740531f4201766056d5f55240408a1158405ad3d31bff67b34f4718d059dcfeeafdb37b2284762ed21a43814dcd34fbfc35f7dbb74f19ec57ea8f851886666fd6a9f018d651712ea2098f593bcc9ff901f13c897805a491122c4e5cd55d7e28d5a5ab954c4900ac23c3730b31b98e35bce2c524c57cec4e947448d85072b18c8c57a7bee9a68c1e4d5a83809ed9ae2a73b47484b45135b61f9ba83b69d910fd1379c4118f8b4a94ce03a4a0990dc757781e4e7e7a9d174cc0e2cc0fafcbc0204999c173b980a37e1490c6c5466360cbe746c51336dd399bd1da03f420d67b0ba83e8dc77c6f540457c5882a305067124914f10d626f83d352161db11105cfc859001a2e596506c9626c8291bb310d924178f405c6018b9f0c8aa8a4f30b1c0bfd11bc0805b7085bf7482f2238443312828418b730aa81d56e4670b42d7b30c93727456c4fca52b4be3721434b5da405151bd02d4f438c70d73b21d9f3a57334713d5ebf8e36d842e8b58c7b7f6a9cc351eeec054f8b686aea5d4f500e929376f58a8d5e6a6eb4ecc1450d7de1327c4b8f1f98471a695975443562567320b11f1624be4a3545bf5123a455b949f608f76a045780b21c2672a5384f17cc63600fd152a804830dc107d7d4b4287cf43bbb860b709619fea566ded8ba68232ed79f1697578657469cc92232633fbff3b36b039db454b98a433db506eeac3f1e0eab79413444ed1b1d19dc1c671b4dbbaecc9e07ad1401aed98bc6e8b5251ca109ac5a4652ec1e695f78654eb1e0987c36e39fb6a89276189c84fe2796c4e3865b92958a58469ab2f0f7a60e7eb8b4cb2e845d396d1989a6a95e4140ad652503d201010d23072c3153d0cd91a62642d07c88d379a3cfb8221b10860298e10987916a725b49af02868ec9a2a1a7f8d43307909daf54b11b526b3d0db2138ba4e3b228d6669a9395d692afee750cd904adcc7ac92df4ae13f84fcee38235606a186cb1347071df0e5b424999771751387036aaf69ae046c6b74972e16c1f48f18b1051404c518617e0061e33d6e5c380ee91b17f12b22f08dc9e4bec98331cc108cfd4ebae242edf30c9c04294c75cf8cb4f2c12d8a73ec3fd2ad3f0c24cd61ed8d13c4694686b86b90552c319d861559063d4c2cb2b08148543f120765f831f7d146151b8819b7c7f4c27996bafe9501c2119b521286dda67ce2c080dbd5a5219463480696ddef537ed0fca711ad7c235f2e575c9c05faedd16bf5f49af324097d9757ccb6fd67ab6563d0b2532f73daac5852fa701b01e900ed956b8e7963af790ac16a2b9d00b7aed30c336b76d0b49e52a203e2b5f50175d871456a46bc2ccbe3b18bdeec52425d60d8224c3c7b139143956c406988c8c1a41d88aa403fed3018a66a28c217e6c94e687ce469894657d5a28a1b6e4b99d529f6c97137b0ca78338c9be8d415ce291520aa75bd20da0e5e6115c8b087900312410442aff757f53651455184a9399a9c304ab7e1535c59424e319e9a3fef76b41a079263925ffd827617e942bbaac3510dd57554dccf3fbce9ac00deddbb63da17237769ee1de465b9071ada7b023187a89829311adc0e6a3c71a588eae45aa0c7140dd8bb40c0f247553a72bbfa9672fd9ceff6a523172d6c1ef11405d4a4876c398b44901c9b7c29852218a111c7386aad40a044c45bbac0fb716c69797d29c81444a2ae427a4fd5895a48fe8741b4572de91211a1458d7fe4337461f5a1538bc7004b855e3f88a33b68a3bf73ede3f7687ddae0a64e72a26cfe2c5fd75c59a53f5f9c8df36caaa78533c595d0067379b27dbf8727da106272e25149f768d6c851449c4cdb9e89768b66c82fab473c2fe96c4ca7220b832ed4f690d4012dd6596c744ff25c77c8ade5d02615b612e673c175875cec57b47a821205941f0319f387dd1239209e7fc5635e0cfc30184c53216f8f738cdb69e1e3adde764439305217c90031ee440ee4435ff8aa2ff1c3ba03f6bcc953872f6bc41f90473d0764017a407cd6b837696c8a251b620da2be2ec823911ff3b80fd4200f8e303b40e039ca267140876e14c4d70019a551889a011720a3001606b69e3bc185b68430eafaf857280c3c16bc7376be559bd842fe4afbc95bad331cf9e19e9ef8133d7241e319dc6f0ef37d3b7786d7ec234e73ac72bfc68c64a7d9227de2d0f4fb6e1d0f7debf6bd9aa3e5f0841fde4315730e3f8d150142ff84aa4f604911f452b84294355f9c11ac7fef8bf74c4981ad877d6a27ab40f2447e2a421c6a11c01cd090f87a5dbe30a93b82b6a8295f9706afdc1d9f45aeb1932abaf07ad5707cab39768cf2017112beff542e8400b3cbf943ef169754ab80d83c8d1ae61e0373dde1e5507747ae4fdd5eb74944e2c4871e8644b2a24141e06f7a3b58e5d9233ae1f90e85b49c9eb2c710c0ac1b49e3daa63fc28e94de796310acaedb218fa3bfd70ce7169e51be6acb7b6474869a6c604eb5da8a8d5bcb0d64918143013d07f7c9af294c78075f49cb902bff812acef6e07684e893237ca18fea7df7705d34eb943609d61b956370ea2553d035442f3ce5d466037a3db6715a30543a4a078956d4fb462bf050a7eea4e6ed0644299a3f4e79c0f9b11395d4e368bbc1e31d5509074cda4a1fd00e477ddbe00008f003bdb67bb08296a36a78b1e601c48b0da09dfff65d63c614563db18decbdb7dc52ca94520ac306c006e606ad1460ec78bd58823801d8ad168492060db5df5dd766666f18de8072dded2144db5e5d9d999e794686ebb4671d9fe13e5a217ced334ab375845ac64d8cdb3813d7dc7eb0c66fbb6c7ab5314d73116ecc23d3b18ce7ec9bbb5e9b8951b268fac1c1bfb87e13101c6cfa4f19fffa8b8b38d9a95793bb5e7251b08f1decd4d63e4e577b18da255f83d96ff19bcbfe72581b7bf70bebed751a9a80c08ead75d61a6f71ebd7e9285a9c9963f133ed753afed6dd2bc7d93a8e2fb9d929ff1a0ae7d629fe189a076b4945db2e1c271b26ec7d2b5063fb65af9b723fb4a5dc8fdd1b3ab1e7b93de2cad7ebedb747b6dac1ed6afe88756600271ccf3fb7c78ce3e4f81e616f2af69f0dcd2955b1c736853d0e0070d8d370d8dfa833ddcc51c59fe162c576ac27dfc479ebc9f75ddde8d5a93de92966ccab37b771743df91a47b39e7c1bdc6953f233eef746fe0d1b14b79ec4d693df9fa3c23a59f963409d7be3338d45a1697cda9bf6f5b43fb5174d3f607bdaa3d6ebd73cca64afd35a87d2fed4a8ed47acf1175d134c2ff39353dacba0aa3602e5349b9aaa69a87fef1c33b166dd9bba1c3547fdfe33cea646bec6f967dcf52b31ee3b7d3aeab55a2a676a7983b6369f0f55feb9cd6bce4ccbb22ccbfa1b4253e7d4ba23dcfae24592175fac6c6526435de3014c88ea1aa9a04b9ddda75285a9dc02b36094fc04fd78935e3518607ad87db824681963ec08cc04dd2c5f84a99f03f16f05b32cd6bae63e9ada5f08550fda2c5f04557e7f7265bfdbac177377bbc90068d0eb677b17905def7a2d5e3bb92968d67dfd7ea43d6ea55e75a775f4c6113aa8d0fd1b56a84f41bbbb3fc04bd585187cea9ca9ad630afd7649c9f49c61147096a963238f9876755f1bfd722518696fc128e2e71618449599df817a75ade9e3a5da99098856b79f1a904dd3baafe3633cbc546147a4bddd27ed39ce0836c52f03bd817f3ace327b50e740ed6535d08f470add211ae6c411c61863444167071afff38ae2b391d931b4290517a21853bf0e4431a6c6ae48e41d81dfc8a9f2438f5f093c7230748dbe50e605afb15b0f76ce5d4d17e57a9fa75e4df8031ef854ae6e162f5ebc74a904a89ba50b2c7548f5af8788ac48e92bb5a8b6112150229d5aa5fea93add83d6d74a2a51afbefd9cad8cf121931f69264b443dd49e085e682be91577516698c20f9ba83d9a23a0fb70e6caad1c30a05f0ff51011c7e92122477aa8bd1e1adaaed5dc75670033e8d401c608218470b1ee6d1eb2565ddfb61590da54ffa4fc6bf2441043e9e731eca2a084c4ff3c0e897c13cc105f76280b5c2faf6e0a8a2563373d153d6fae7b7005dfdeb7a3fa6b523a8e1705e9d5561686f4e0a8bab77ab054bfadfa00a87e8bd4832fd461cf33aa070177bd28d773fc3fee05c644e43e589d8986ad9d4e7332fbc41fef1f618cb121840cb32570621784102b022b024e7787fe506a124209259404d8c075e9a04d50fd1976cccc738822b0241e25942c8523ab14906323253ab441830c924142424242100b1b3468427bd3df6e067597e29a904e0b093104afd90403c1d734f61d5e24c1f40257952aca4b017b637a816b8e951c68f31915dbc4046aec0da6651bef4d4f2b947f0b15b58702506590e368429eea9f93d2a05a44ad885b6690b2a0a769456bde0ccac0cefb47a66d0c4506090509f58aa5348e2645280b0a8520956d089381169989b8dabb4b1a7b0d4719a3ec8ba3a19a739c556929a5063736c91a89c8aa484d4a29a1cf2441848fc4b4a0fc104219ef364688237b56126005d435259dd27cb87fa74c8ff4073bf2b8fcdcd185f372870eb71ab11950feb9db691032840e2184701733428b32d3b04ce36e710759a8fc9099564884ce23cf1c8fe00c3b82f2b76ccd0c41861ee185617fc5c726764d4cfe6840b59dfa4c8861186625765a8b65c5e66585f22fb7bfd6e2426badd5c298f00bd3b4c7320dcb34f6d174a83d8b58f3f1544f27b496a77a3ea1b5daeb3903ca9aa66931f06ebe8c966173fed56a31bd92b0d9191514226d0fb8310968455d9fac88689519b5d7ab8c06ebf567448e931d79caa85759527b34d855d6c57a979b7cecf46c9fd5eb37ae87064e83a57d0d93b5655f927a952d75b1abec0c1b36d5af71acc392d02a4f2df58a5bed9db12ba6613d6de326dc43e5d14e9675c0be9f5bde422c16a74f66e303b4ce568cfebc5e7cc93a2ccef6fd6cc3e2bca7a29caeabe3afa7cf64699d29ca364fc105a0728b3baea2b57a06005d68adda2f99b6b02dda2c0a33a424859722a32e48495fc02c11f5ea933e4356ae4ca2794211cd8e496bde539590a35384659b8e138b8a8a8a36bacd297f826a8fb8b3455d3bc382f4ea6320a8c27e2ad38f58bd1deee9d53735606467050eb440e408925ef908696f4e21581fbbadd2d47e58d3de49be2ce17ad75c3e4a3e87921f6bfc8baf54c652b9a8ee37279fe05842e763ffcd396304cd2216d8056673a3a9709b79f89b89fb78aa29c6f8353e7b9e2ab9eb927ec96c2f3713b79bb38d9736dc860e24e2e9b95e5276f03759a58c50a625176d6de49e2077cb826ea78be3fa55fe36e785927917aef7976196e196e7f39c36b27341e53a90f1649ba89b650ca43a8651dd2c4a43f5cbe9e7fdddef2edb83bb54e75761676ffc659aa01fadec35fb0c17058158e17f3ca6ee776b7bb4fc871863944b655e26d20e25f3cb322d23639299d192e6ccde05f8320f3f7b379932f96bda78ea2667be67de34f33a9ecd742b3129a6979acbc8c83c90ad329dcc5f327fcdfc7c9edadcb719a926eee36afa194e869b9cf66bda78eac60dd9aad66145f008f23f9ea02a9f6cc7545a01c50a142bcd539d636fdbc31c07c9f6dbc96b360fac43f9af694e4ff1eee52f7fc8a9fabb940fb3e7f111b50e0564ab7728ed5df0a755abfe340b1952ea8ea972352d58c21df2466a3a0316231c74a96b748327f59b4637b801c712baaf0309baefd7abda63d6763c7a67b8a0278fa66d7a15bbc8abd3deaa989ca1c595fa4d3194b420438c217e78317aa8a6cafc6fc39f039aaf064c153f280ac349186154ffc6418012ab9f93a04f246169f80073be29aa3d278410422884000421741b301366e1aee30a1d5748f7f6eeeddeddf55d1d5734bbb7776ff7155487153aacd061052a05990a43ed2e470d7e2a7c2d72119b103b4ee21178075568ecba365a7569409d3d6876a154e538d81739fc24aceb96a7e0fb54e8dce442fba1379973ce6f888ec7ce7fda830e74830afdfcc77f5073fbe93f9b829f0463f7d138455bb81b638c7187d07428a0fd9cd389e085ee53a1fc5d8e23badf3e686b43d343f7359a23ba3d5afef51025444e4ff1c5a1845c48a8a7784d35503f160a32a4608f65f2b35f96a9fbd9e48277a8146ed4fe1bb55d881d2a85fe08435717fc170a32a4d46ec8dda8ce2d146324d5e688dc405960cb7eb38064ad6905edaf0e25e4c2fe92dd0a59f6b243099172ebfa54df53ceb1d1ce49870ff4c445a75327d5d8918ab6365fa4adeaec63c423248962263ab13dbce352d9dd86ca427bd39108221b6a57a9fd1c7d64ea0d23e8d74b3e4a4b506929b6e4eb74f4711cc926f8f74727220f8bc3556caa884df08e8f7884feee3295ea000720000150a81b469eda42fd3fa83454bfd3ec42bffda254bbc7b4d7deeaa5f67a0cdafdece3a996cfd1c771784e2367c98ebf786ade807ed1888d96b8d0a3c9445b1e34ab6cd31e774308a1c7d8bde7ffbed59d5db53dbaa2846848b40e25a44322f3dbf3b0669e87558412a2bde94d1d1213ff0cd74428a0269a13d01d37f8c6124ac384176796c62d6b4d3003b215d5ffc40c7250fb4ba7b05e6a63d149dc483a82a0df4a8ea3525229dd18a23ca4c277eea3c9a93d69b820b0cc47365539bddafa8349216143c890bbbbdb8994314228a58c114a192194d26594399d6c5116066a019484c6a7a17df0de80d9a5b8f241b2e4eeee5e55799e9ae1817eab44c4483c4133333377cb70d7be850c30c218af784108219c1113c175c56b1eb9a2248184d7d5c15882211a02cc5d1b1422880b0c777777e72852ceb979153046082184982a46186384f1bd1835299bd78bed504ac9338782bc9d1538400409f71c7d09b3c43e1128c618bd6d420124c4995a9c1df93b8ea39ad02f90aafd2a8f7b75736f44907d47b2ceb6a02a3be6a6f8f748ed1790d46ede64c8923cef84cc2b5a2916699332a35e1df5eacb90aa149991935a843b6f6ffc079a1561f12c09e6006384eeee41746ef080422e26939d70ce092184108586105bcf1482daef10064cdb8c34f4896530389cb39b98086abff33c02316c6d00bb8010426f2321bddad6ebdf54ffa63a27880a3beb75ff4764ab10767003baaeffe205a378df917a713a7b63e38828c3500e964d62a105232d25474d3669977a7c7e80767af5fd0a42428e4ed428c8092a5c9ef736435008c6082184d0e77a8da4f2f7e6622094104229a59411854eb9f4f5f8af114c0006d915911e2bbf2481fc283bdf9be89b6ceff7ed8e5cd71d31d48ecdeb50664e1806c608e7a4f474faf7bccdc9c161048dadcb8aebcbc5c50f1c9b1d420817c632e66cdfe1b599bbbbbb994d2944f67599eea0571bb41fd6e4968571cb82314208216416addc1b9461660b2c70d8b874a5d13046082184f06b743426b4e11241b8c718a3ab361319f45355378d41bf2d5241a7fa6c8f6542e3ef1dd523c7eeeeeeeeee6ece696666de6ef99c4d7b6dda1fe6a187a315d64b13294edddd5f4648e48891110821c47efff7e77227eafe9ef2fd72713951549ef28701d5c090151c2788b382e8a07004c5789ef6001ebef8975ef5152b75eb037e8a7ab5462a007a000f5f588c2cc0cc380578a98168dceddec42f069faa64677bb0970aab16b7355120cb5e93d2f40210ae706fa209c8567b08aaaa61137a4706faed918f904ef962b1844335d0388f23a4537e997cb497cadc4e7b1af89d5e7d5448fb6ba04d3fa03b2cace9c7f563abdba12c8084bf1862d9780ccce7aad33a96a88e545335a5290232a68ddb4c3eb60e05642b3c527b834c9b796acf985ec6f43a6ec2be06374d5a0744abddafe34b15881d91f66ad804ae181345daa9578dc950b18a5d4fdb3bb10c5824dde88176bdb6d77eeb50d82340898504014aacedb5bf9a7575df105eaa5947a1a05b3f3ab9257ffdd5452d9e54af6e962cb06821a56e962cc6d49bba59b2705239697b70b1382a22202a5a7e38824b18085c75ce2c59e850a5ff3aeca2ba048852686282aa095acdc9e19c73ced9736a73ce39e705e79450c639e79c73ce13dc66a84062ec793ecf2e0af67cbd86cdd7ae39af39b139e75fdc9caf458e1daf0ef5237a590e45104208218445d06f919068bfc6d835e7d51042082106bb1b7b38dddfb4edb94e5f5ce8661042d845c96636673651f351b3fb11ebd5cd4e851aa2fdbe0252c6ada78a60e0a7cab5a6797bc0307b7bc0407d7bc020b4a91d02c205d1694ff54404fde66c313d518c9228a64914f324c65853e33653c55740a6888601285d40ad56abbe70a4d5f29e56abbe708d695dd15a2509460db8b46af44d15e9225c7b7a6aa0ac5d121200bf86134b6e02e047ae8891597747806fe454618b20f31da95d427bdb5c24c7998b34e7143f270b346ee68e7f799a8de8a8424f4d4677cfee1f26dba33f86cb7d907b76dce346a85b04807ab326ecec088da39add3724a76e15babfddf4692f763ff6f939c8fe80d0bbe550f0fbe3ef9cde9e76bf59e5737bbc2754f90c23b7c78dcf19c00bdd6f30557e1e64b80dbf182a1b800b1a65593b7ab5c3bd51d7f37fe72d1c020e5b7000aafc5b0e0e3ef5db760b0e3d75b7240953bbee96246324f141962010ed5bd9a264484950adbb4589936e3fe0104ac939044a50e1bbc041d0b65ef9c78f4c39cb1035401bd0b01241d8d0107ac5f815e3a31fe64abd51374b1822b4b81068bf696ff8a7d76fc8563dc2c81b15266a7a05ffb4c1268ab0e944fdf6c8a6047eee8a3003511a03a602d42c6403caffb150ed9dbd691d50fe1ded797b23c2163e85c65bc3f716a24d0b4ec48f388e36b35f808dbb6f47ed8af00c27c331064caf69cf2cadf3d958a6f781ac1e5e12b0df3866b549fb6649b969dd2e09d84b6ea262ef840cea95c23ac11d242747a5f2bc7fcca8893a6750af1434983dd6d7134c3234c44811c2fcf05f10767777c7d883b1fe9dd427abb84804fa7df8cf1ae611fbdc530ff41b4253618792bfa8b9ed9ca09c13c36af0ef1c7d27e73dc2a64008e1ac9a9432db6f91875cb96d73dbe68cf1af4b5edbb63d90ad5b8732bd0bdbbbe93bcb7e4e13b7c92cc3e24bee833a7ef1100503470d14b6177f6e175f729ebc2bd65c325ea1ad8d4d9b3a46f2940d3087187a5a42bf6e75eb27686bf7f8c0260da469b3c154b85a6f9c17b45e577ba98dd458b497d4d3ada48fba74cf052474fd3c09baba7a7150c591848e3985a16072907018c953304cabf2a89ba50c1a541da4d3b7d13927a5a793a7da2a44721c57d2a91694d313ea4db2d9dbf55f2355ac833c05ff48d52da2938a7a731619359421833a84cf48bdfa12b19060947a257ff250d151aff84a678588b1f46a21c7d7d1f1a11bae2faeba59cac052b78befbf575b7c9a66ce0066c0d7a2c907c4b2de562f43cb7a7e7d4f5d550e5afcb5f9fc08ca9a7ce0c0a1675a6925fe252d74c25f98c1ac6328dc0587168afd8c154aad9f304b637ad53dabeebaa7bbd55ed73e9a422fe6a0a42f421ca42a21a7bdaf5b5bbd94ec0dcf79f2e43bc7b5bd57cb40915ba5b3e3ce5f420e0ade7e94f1c54583384ed0d3376ccbda1dc62d7b9869d98ceed1dba11721f406d2fccb689263f2b1b5c73870a05bbfeee955f7139a938d07844309dd1e4de2a081ceffe8eede1c9f2c962e262856a058e125cd065d5a9a4b748997e6a4d4231fb1149ac646a78ebeb6a160f2d11d9fda6021c7b9d1754242b4d3e106327577378e2574e3257e1d48f4986ea1fa9735c5f42a66de98977cb4eee955935ec5e731ad8d9a0e8f69efabcccfa555ca74dde2a536994edd15e1ca2cf93afd257e8a04a48f937e6fe2e3684237d30fd09368da70220ebfa756eb28344e1bfbbd0dae8610d86a459b1adb47e77ae8d7f02cda4d31bdfc7e4dc6e4a34d269366fa5dd91a26e59b5ecef9dbf69aa67133bfa6a9713c75ce745811d86fdcc713f404d332d39a4c269386997c98340d9b53c64895af71df8e81bf26c95325c4be390d7b1dcfb08e87280704da2f3b486b1c674809574608fbe7a776136e9bb2767794a23c359620218f20932e1b109e65002714477f2d4ac2d41a75b528e9a9a8165cf2baa416ec0968448da13ebd941caeb874825c5794b8c4f78ad35f971c025eaf13e4c2e1d93101a25e97962562d4eb622df5ba2e892a4272aaaf39b567d32946b180ea04712d3b2851615d2d3b2061a252a591964529a16c09a596457f4ff16bd9093a28f40afee430c7d8bd04299972473ccc7c73fe7baaeb85e8ac1176d2bba8d56a3561ed975377059bdaca40762862ae80ec60c47a5b63a5d1c1260745840d8a0888c140cb0f4b1ec8c9414d99330725058688a2d2811698cc08ed4008307e90320313f9040c19d07085981a6409020ae9a4033fd4cfab2c9918a141e5055138f1c189a12aa40f4a9411c40f8c78a28a175c58fbdb0273690924b182041054f4c4a00c0490b1831e7ca8a2a40a971f3264640a7894e440850b0f34e0200725195081942992c8d8a28c30b8a82181b46c18519e206389209801851820c081173382985225490653a09328a0f3acc0071d68195a02cb0f8600440d8288010e78b0441328392ce9c1088b690ccee252a840073f4fd8c003294e76be68e2cb194b0021a10493d68c4481962024110612473461810a5ac0b30314265e8c41e30c16ec60c324da9a84c13930e1718b0e347c00f38505412a70010c3c2cf1f1010d4b5ca1e5870acac396187a52008327414d082d31c61341d0208b2837100db183134deea04532a1d301582421a60755e420032e3c57c69881192d17e8e0072e00aad70f10f167ca8750ceeb7705f5fa718149eaa06402266830469219c6e0c18acd892a9410ea62082738144d99c3161e22b08841c150153f2861030f900fc6b822085e005103284c7490914b8cdaa85e5664a9615d1713da5e57bc2e9b8a7201bd9eafb803197400041d9610ba1204eb3b553a06184524e1c2832a6e605d5a72a8b2492539a822280b76145a094f696a6c986f80a11fbd9106fdb885c398b9b5e695130e451abcdcd9dd7d4e8c3ba13d3fa1ba77df90b53105edc788a0ffa3792a1b47344713498eaa905bbcb04cc35ed813beddddbde4560563827e39f03d5887ec51859d2a8712db96e4c9b65d4924f1c591a03998a01fad89e2f32cf8cc00e8edb84168dbe15d5b50e82dcde6c4093e5f18fb654d66cc069bde9f41bf1c052c037ce60276013e5e03e6f7e7e86c8feb7b9dc0b88b32f7921cef0dd76d8ea77abbcfb142bf3fd5402a7c84c4401bc0b4cf2ca1fd1f751d32d0136d2f0daab2c11769b18148fecc1654a73d1b5ce849065a230cfaa94a601d9cf5ee47eb99b2df3439a0507e1da741dadb093afd40bf350aa2c2b748272315680d47b59fa606051582cc16d08d1a4d1a1d1492a010580d4f22757facae6927d67411d009bcacd73c86a0fd9fe7eeeeee5e846bbf08623ccff33cafabc0e86a3941abf6977002fff0fad7b8a956515513df21cea4cacc3c933e6ed1242fb3a749eddf68269c2d244d6e116925ad526d230b7b06ada78f68f3a0fd471c477e1047c2c3faf8a7a2988504c51d8a02f3e30b99313ed621413227b5f1847ad746bde5eabac853fdb26b224ff5b26617fa31d222d180e4cfdb15e1225c799fa7a0b06e07dbd3e9af892a515f39f29643cc16106df568a1010ac3201b54d01a5fd06f0e21d202db7842e10f93bd69e8d3f254abd55df4cdd6527b148030684b4e092730b5bfa6dbe9ae075b09ae9be43e2d6840cb0c566a77b722b3d05e73d1e42810142524a24eeda1503bf22bb02cd9c98f22fb8db009452cb420002794eb083970a2c55a16eb3b2d794a8847d1f93689050eec08f11f1885d6132912fddac60fd497e6843eb57f8ea9fd1b4d059ce5469e1a02853f35989b145bb482956b2109fde67c6e2fe0b546e0531a9eefe540bfa9d37d46a08e11742b5c1dc7814cb763e97828689101a87647a94161756c4e358b46ce8e24b8330654e5b527443f8a03a3401a4a77e3f77678d20bb4b4a0406804856aa7a1c4470da5bf5b0ad0931af7997fb91762a8cc0dd45e0fed4dd3f656a063887e50c8e7862126300542b0b5374ba626e8378fb6d0685d58d45e6f0d6f07af2c24e2d1af230afa41a228f4a31dd43a586868885aa9fd3435ac440bcab47beac7423850c8852a14721c78a5b108b143f1202b908174f8500f41a21a876490148a52e250b412876410dc38543b0808e8c9dcae9f09a4f54c81bf3958409999d753e5807ab7cc3b54259ca0d3ed4907179eca4ba355fb775c41bf59db338141174619e8108c6a3f4dcd7c1abeaeebbadc2f7ef93acef204a7085ee8476f1c41a9d049dd6f58a16be28730d020d7926b9310b47dfcc8911ca73ba7ed035bfdd33e686051fb5b493799827ed4c8126124244642e2a4a42f5f588995dac761eb9bb0676ed0068886507cf83c9a47ff24526ab55a397c7737694d9f2b3c7302fa847edc3a9d069084facf30a6a7956cecd33cfa052004a5ed35fd4e03c04108fa71cb5b533466f5f730efee2ef3c68e5bccfb4e695a6704f5b0774560e5162b7d93c3ccada768ccea174108f43bd55e62a08e68d47e06fa52fbf9492ba9fd39b4a04f18c839065aaadf09c8837a1a07fe78aa1f8a15f4f32013fce982cef9d303faf9cff7531a14ff3e04338560a5f66f34fe353c0bbe6a074deb04691efd365040bf398bc6d09e93926fe23de9d4e61cb995efdf8ca3da6fb34342e6de6b1efd346250efbf9ab1642929cbd2d6c3365924b12a3f985e61351eb00351079fdca814ce16453dead4d000000401b3140000201008860362a16830982772d07c14000e85964080521689b324877118849401c0180208010010002203332454840074b3eaf55f79f8344908f898e6b5f21a4a6b8ba401fd5f0ad869b1782acad19d28347a878a55919c1599991a97ff6a658fb383d92a9307ea50f9981eb7004f5df817501d4150c4f619f03f03287974746a084f5ba810078760124acbee000aa8d15102b321773268298810c17d943636b97b14d71d081e17c49fec24a13603c0a0be1cbecfaefc64e76bd398512dec67f9d8e77fca31acebc0aacd703d5b72658adc458ed78ced0d93607ddf9f0de4f287f9a3553a43b519321512124ce574c03ea2f9b8fa91f85bc3c070db3cfb8db9fed8bae4e8508853f3f78d9ff77d103ae360f93d910add081e00107d552af0a05d4a78adcd80768d4b4cf2b7bda031531eb82808e4e3847eda07083e12e7800ed0db8fd8ff0a6c130812f90e24c361cbd0b26bbf061fcf2adcc0f80340ee02b10b1b0250a6a99871ff0c9f40c80361d432ae09237eb2e546699dd1950f6558d1891915268994fce84d08538ae6584e6adb6f3a0336ff38b687e9b2ff2a98e3124d685637c21953ba306b2b78fe2bd42ab4202ddfe15ee6b1d54d40328d4f9e1a0db3bba1b0c27aad9dba73aae2f74e29575d2002609d8e9d1722047ea5b20924be135bee063746f98d31ad1acac43c246d214960966e0a8bb72509dd976c61a51ac03fbc455001726e8f458405de4c72d9fdaa658ef141582838965f1dd3c9a89c9adadc70d1cc9afc7a62ec2f049036f13044b86b4e3ae3477b706d3decfec2f595fee439d3caaf715b49a739944836f31842e1d78c41a5ef5bb469464fc9e25856d31531d3841f594ec7276905feca06a591451a1743c6db07da70df17eee9c4ac5f1ee17797fe1153776a28b7fcd310f79bd82e0e25e1ec7d070068deaf135cb5960820c1dc0b4e7fa0a725162c9a6b9436c1087f111f3837553ee3e9ba2ee3eb61b0117d7b1ad74cc6210400c4d903eadd58ece0aa9151d03b93dd871e8c3a6f05c3a0a2bc7564e8b4f0032b88a84d2d3859753fe514720b10a04ad753c74f8939082acdc6421d3ecd65e2b9ce1c2c54bf18008d205e305ccb1303e22dffe13ed9d327573d3ce400497a0496b93a1e22b5b2c65bf85dbe72133f4787617d2772ee3713125db719ade04c229f21b9818ffd2c2253f49ed0070359c82c1ee863df3a050df0e98f3d232d7a54c27746f064b57ec8456f82defbcc2478e50c58afcdc3889921f03e6cb172cad9097de0dadd6d8449b40020e4ad67dc2314476777e6b71cb44293da9358fe83f54c5be4de6fce468d7d757b54b74fec894ee5009dde3cfb482433642566f570234285ed4e49c66ff75386d7701a7701723bc8f9cc22406e4bfb14e9835e9371633a3e0fbca9c443b15cb28bf55b8e41b31b26d72a0748c4d5b2b592f9ef427a38310a15ffc25a85b88614b16fb427aba0033dfbac55831d7ad27810393fcbc0252d1f2963ba7843fa52233d7169cae7fc927760bdf9fe74c9472ff29cb9b4157452a748c46683f7189b8ffe4ca3e4c60789af83c5d65f097083d375837a4c955432858c65f53c1759024d5b39603e85e53c523a9a4a6e679db88cdb427b974fe00cd351be78b2e987952ce6ad5df68d5e842db4c37eab9faedd2772044ad44458bb0d3686baa0dca335cae090cef750d1ef19d107c9ee00ad0ee5359f59cc5ab663d3535ee9842659873b9ba7f785d0b0b4c47d73b072ac6ee3f87cc18886a886f6e0bb38aa4862f45aeb8f02a1f661d60302d41949f73d5836fe47118d69b450513189f793636223f3f4d1aa0944af6e9bedf5dbdff7bb3c2d524c866132b66dd7f0e912798aa491014bae77a1ff52ac24b96803f05a850ba0f3187a53399b4a3ed290f1521952ad7c6db0b9d1189961a9bece974afa9280559b23b420d8fea139ca28700c44ed7a1720ddcb5429ca2b0754d1a3397000a66fa98f25990f9553c0ee73abbba4f7c0860398791f2ee53e68cb300ba97946e57b36029f4e1182cc946469fe39ba1999daa0cce99df52e418dd0d8568574aa45fc24611748d1b625e43764e27b311b002baf3ff61401bdf20b59486183cfc834e78878154a91889f029d60a6a3b0f521a041b76ab0a69adbabd03af53ebcb7b1227de71b723805db7a0c5d4ce89e19a31abc5dc6f8e7d4d36d5c8b462cda47a40b0935d5ade54be37969c9a5ea568885f5f43900721afe06c922208d74c86bb00f832f70b851ca06e7ddc9ea5ac27b2fd041c39133442bcf24dcb75bd5f2e7eb8d501604adc50c0970f5ec2fb9b1f78ff71de2c401d32b52013b993f93f2b6ae54aa71a21aee10fb1d2e1afdcef48a070a06c253194f234c7d74c63235a3a2875e1416923def2ede643a57189295af7e481cdfebe99f4d88d7b0b775aa5529e2e6d3246343ed786830426c2d4832e240dddb124e663045285f32c72d35096fe17be2669f3e8bb7ea97dad4521a268d5a0114dcdc07dbee4c7f4addea6bc278e0ddb2db6ed465ac3c53e950a0d4f919e4d041f73234797a4b782ccbe63c514fc5a1982dc61b7a52a8f3117d64f8e8db9f0623077c366ae82ea5610f9febab3351e9ac524f3ef0d9f62b9a2a67ff1be8f72a3b4b8fc7c50060a8df30ca938769813d68d85c4a4a083ed100113a31efddd84c4b4b82b901e39f6878f4a8b6cdb66c0196c91bac6a4096cb1c3ce5c55f2c1d6e8522201faac798057e0a8497847ded842861381b37527b73d04ef5e630c7b976f1e52a5d90c2a884d0648fd2093c2867e9ed7459d83850c7cb6b62ecadae28e6b6d5ed08062fb2a4cf2b5cf1396c0aea886be9ed9d57499088a9935a54b55bfcb4f0d53897f1910cd6fd4926a514d661232e8b243cf5b512b02187d38cddf55c88bb7aaef938d0ebe24f7e1f92e8a63589234163b4e0ffc4e858cf36bc9d0f1028b54f77c6e717b70c00c0ec2f033c194171af41c60c819947720ab1216bcf975555d70e6d88015d0ab5873edfd0683eef34d5880d2b269d6784779aae570927f15b34ed78733532276ff902bbe5564761a1ae815ac2412a95dfac66a8f67eb0e3ac4c3470591969166da561f24666c957e520bd2dcc5a5a9aba6900c7bdd1a6120601cf946244830d42276e19235bcd8d7461695c4f1d23731474033ae7b3c5d966c9cf9991cea09bc198aae7dbb61e6dabf42d50d0ce69af863c14c82b8a4c82b5afdacee1a385b03ab0143114ee24ae4d21a9da253269d75fe98d187ae2f27261c097bd45c0195d152effdf68ac3c0b86b6b776623e5cbaacebae522a81bd945f7f3e8c0c40ae150047c901a31555a5107b1719b1c650fac0c2c807e4a6af2e1d536be919d9c865ddca3cf9ffe206bcee143e7ceca6b349a4d6c610d810f17785d807a58858e6a845453c2964dfd664914932a6be4c8bcab605d9130b46f558964151853cb04847f871a8db0d756476a3630ec9a54ae6b97b0f9a0b690d6ee6e79e8354039a28313dda650f93a88493c83551ce45f63cf13df8b43669df28dfc5b03887536c23c674788d36f8872fcce2656e41dab640385c18f36ebe0c7bd4aa0616905099ce09c059fb2a6df8609438f8daa4c0f2641c56b4219ac61eb822a9030a87685468828947753c4719a9595d1823829e192415deb234c3e802704bcea2150d0af9622a0c4b746b8cb5367ee96b85d31096e82b554b0b48fa6537985489986398d91cf99553e8322ba429f8ac12950613645e167f5d876194122e643fc79e167e2193212eed8a30732eb642e139639262450ecf873dbace826eac1cf4641460d26afca76f9a069513b3e50fe6db2df6c5448256808eaa4501fa0d46991b2704cb120059dc33977f3abddff1175d2892a6523a24308200012579c474ccc80386e428026d08bf033094e9f2e587b0a360fc3028da84a42685f28b352748c8d1949b58064820facd07917a4c36b42f18fbdd5235205462425584e3f809d8550e701a061f909a52a7010aaebdf4f72edda6864f4ed21c8a533d93dfa6c79986a4172003741fd4ac406b07851b64d7e4bec1a73557dfe733e211ee189f440c0b362a14a6140abe6c19cee2e1b0ae77bc3b0996d76543c7ba6938185f052faf49eb5201cb615323e679d82f67f7ed72e5bcecf92545e1ad624475a6b909e82148f38622d3bde0c7c40ad9385820c7575d680313e88e1e5f1b0f343440bbb448b043e26ae84e40d373ca3d7fd854abf354f4dacd2d86e06a624ad0c7bd670e2c6e32fecdfa1a68ec5704e40477a21ff14793ac61550e20561293fc035f4c0b8da77c2f5306bd2c2788d45e95ed2eeadc1cbb01e980a7313f0492256e4cb27d9bbffb68ec05e6e7a92af95fa12139913c050216fa4a64be3c4d6ba916f10844e26b1eaa8be49d69a3114f1f2cabd653b4bd37a9538afb3e477fe693e5b3824b5072105db5873124756e988c90ccab8653e355df82169f0da9770476d497c865fe8ce7de9159e22432dc360c8394ce536d414c8f605813d5441ac75f21967142506d81692e192774b45c34838c8581923706aabae75dc2ba04be24b28ee0252251ed839cf0ae6dcf09b425f137544530f849f14cbf17cfbbeeea098d4715a7413aa6f595f20f6ae8ee4ba555697c16cdbf7dde8a0d79779602b9dc5dfbd2bfb04431bce06c6e36804575eb8382ca95008e74f87f5b19f9559741b603e4c5d7dc2ded58bc1dde7c3b29febe629725846b6ecc94a8ea1645b3d07f45f059974cc847b8512e6f7cb15133deda37528c7a92d33cf16fdb3e7b66d1064dd652ffec930b5d5438f4968b1390c537dafdcfdc1a403f93edba70a65b287297dc52fa6589fb302aef7de4bd187bda1359f640a7bef61086452fae181dcf3edbda7e953dcc08cec6e86505bd3fd939b82adff4c9c5f34299282881b43ea1ab7da69ec327ef30fec41d6019fce8f5e4412a8c950af9c38edcf3326d5cc41dfc669e452a6e3e2a51b92a2eb2dfe7d1f780a3e5a4a618686a79221db27611c919d70617728397fa206bc312a27d4a4603082d1cc05076b6f09a98b861540066323bae4d6e29b04fb0169b099e151a0943982849218c490cf234c63e511fd39195d32a7a21cdcb25054e036d00c38ceb893910e754081885a843c1439c4e5f2d07802121abc2a377a0962e08a75c06becea51ead86d9b7ba76846532772508c66ef85f903f43d21adef2914d82c268fa0902c8329c41935d7a17a2b5a7c99a979d0df8939f1669a99f54600b1fa9501fe73a6610103445d79fcf7cdb0f60d60020723f0b2f17ef1f3994aaf34abb6478e88f814ee973470a3494cdac9eeba0aca1adfafe7f652ca7feefb50c02bddc8362521ff266039007ca25429b21f596ae613c58ff9e7763a523727b9ff2571fb3ca8d4f071738c11822b6ec04b088dd5b19dc64445d18ea46fc20034974c61e9a6da3ee2c12b4ed124bf67d6220486467b5381062cb2c64853949791358014fbbfc29b188dd69afd82f1a09c8ba1a53c09b564fa93977d338e759bfc5146cf0d9b05e3d499b820a7ed1392f3b0323622fcaa8f0b03a405c95117342d8f5cf76d4a8e2d8038fda37f93917541b296a80810cce4db67204e492385b5a57a7175f138c2c75b74b3b3d9a855a5b8d92b059209536819677b6ad0cd8c89ea5b93d3c9cf98dc79f3004b116e76756e9b05789446f13a71e4601a8900302a8eed0c62b14926c37aaf5b216a1315ae278f74a10c838946d1c11f234d2fa7414ee60316c2345a9c8a78ecaafc56afe531d8578994bb8edcfca12abd206be20b495db4f437c811113f13c80f5ac240024e2ba868f3150396c1d44e1f1fd3c3c5dbf144177000cdcd0d4bff08a9774b49169dc9674780f3a62ac7de8197239881116dc04868a4666dd472a2740cbc36f8fbf55115cb8cd0692b1321301b1eee44db76f2b3cc974d386c26cd232171695760bb43264355a8d004eb66fc664678645f8809568f2feb6e5ad5b9fc21d084087fbc60faf48a385c4ad0f27daac811343e39ea97746a053fd5d57a2c3ffcb6a474ef236e3c863f9b8d605d46697ddc62ed75a1cdc83860b283370979d107d361391e3604cab339e7b2df02e5cc9a4358718d43455417770f4a182a54eef1851ab7d59404afeacbfc91e836859b8c23f46d1decee4f754e66ae17276408203005b7d622cd80b74d4613f2e37aeebba49913c6457757be34d1565d37c4070923c64915a0ba57825740d12c1eb6cc5f608bd56ae9213869d129943db076a534809b91c3d45e159aa6accb35f71f3209f4351cd9a54f5be6919c7d22c2d0f4c9d71c20971f953418368d89211a93f7d9816906cdc27038c74f183f98c579898859c8635fe164bef8fc47ac0e3d8885244907cb569b765679bcf7e848bd34e56575e82b128d6110f3b0e5c5bf2692fde75f90b1eedc26a0c2e133721a07b5467235ad0d77653989541a8581460ac49658c235da89a8266b367b67808f3766a331e81769f5691c7ea547b5ac2d2ab3adbe18bebebe72eeeb7d324d3b8937df52b3d3a48ca7730c950c537c560e88f6e18a14aba0d2d80170b78311fda89eccc89c63ac25ccba84171ab487fce53bb54442c470d2665c2455d0073662d5b697f7ba6e51f0a3c50fd23bf890bb6e62d0b3f3809ec22683c7ba0cf4efddd889dfc0ea72378697d4659ad895be54eae678f8850ffbd901090103262b5e0d79a682bc63caa000fd92bab991304e95485ba146051f3a2d4ede463c549aa6af86c6b2ce7c522b73880a09c13fd249cdb8b517ab1a513965c6b3f42afa9262a978f4a803f1635f78785c66657a4665576a853155a7f14a99b63066683218ad7b78ee599dd0c20bcc52f205f141dcba05171ab4e9e0efd20fcf93ed9a2f5b215564c4a9764c649398e58d00baedd8b0226548514d469897dfc51851fba5ac8c9a0ab7491688a36cd811428be3115973edad86916f95d736abb7abf854ddab2663e405c3e1e7e324235c0206bf34ba7e2a4d1ba997a9f4587d96d16f1da9c0d5544f730f01381e820590fb0ea6680673291794a3c5eaa6360b6ba728aeb1bcbe9e27de5245b388ce7dee738702b766469c672bff5d68e5a19f9dec85e219ef46ce0660820d4dba26f41ae47e5b01a98fb57c2eb8d98770dd55460673575b51e7de59cda480386d6c653fc918932ec16fbd6a6cf228607eb058a11fb63da07113952d5c377a82e292b072e7b307e61a2cca58cd28684421f1a79d971d803e812dd79ed2702c582e4212c28361a43f6e5f43f1194204211bf4a1a22264ee082b551d8f55cfd7120e54e7e437ba0730f13246ae724b1117d92348b47fca9e1c0525a5ff664f2e12400ca33824540f3f1c7bcbcc03a0f611d0d48c6dc3f33226139b85dd4599bcbda4d9e6710a8d92abf012c0d0d3fc32bc876bf30f8cdd6e27bcb4b4d4a133ed2077db6c8eee0f750e7948a4473e0f39acdcbe0f6e2ec56badb00f838cd5c22025929bca9858252ed30be34e650530a36d84309bf0bf3c019ac1664173f7aa9040c61d297e20a00bb9c9ba59fb24b2a1e1f26d22cdb046a94d2fd9dde492be25eabc480e41d89d6d18519d340c0f0282e66a30d933d13d20e6bf107ede504ced7ba7b47cb34cfe4922663fa5e4011f70ca8209f9292829941e0cb18c7f710478d6ebc5871f829c47188feb175a1a3c75b34e61e7f913c322b8f38d7cb6966e5d783b90f00711e2ee2c3b67ccc272c83d10d38efc774d4e4b24c7b49a7c2ab092122564576e1156c93b01aa75a4948a7578d53ebccf3ac4833148005485937c174c58dec89f3ffededf39b80186d1a4f099e66b67db7d2d77f0e584c404baac8c4952c7a7bf5f5df53bb2c0941218d306154211fc5c126d1ec538217e38d5735168104099269b538cadc054a0efc2da6f7878a1d6373e29003ae521440153e1d8ea8623b464785131d2d7e9dd221f05977d0449360e0050fb7d6528abd7c5a3d8edc73380cb81e5540871230ae81f292376ce898f4e3105628d5333caa40d4f0097cf48710c212bd233b32eeba2ec7ecd8b67269b345880e2e7993dc21b7eecd5d27131eef7d53451c22d6235f2784b449c4d09c2c63316adc88962122830cea347efe2251110404fe7be913df18b2fd7bb7438b7f1a4bc84d880a27a329a9849344c4dedb476515ceb88bcf3dad349c3e43bc71f18ef1199227ad8a2c90c963fb0568bb034fc6acecc5d3c5b600016825ab1da8786246cc2d755a551efc63b266510465b2eb7573d113a5536808b1bb0ce5c824c7e82450fc5a64fd58632e1230bb78ac1958be27011a4f0d33c84880c44c3890ee50f1a014adcc029ea42a4b4a46269fc92d03f8fa6b6aac9da9a0e6db032b8a024384c4648ecc42064b83508b843b565717dda2250a31a053387f3499f8c83f0eb624bba1622693e7161ff4058e305c524fcf31059481b83a1db195ce52fc85f95ac4679524063015173efed4275190017bbcbbd58b9b33d355aec1a910edcf26b818e2887361af8762bd1720f84dd68f16b296783f0b29d73a73b4e1646837d356f0aaa9e71feab0b5d25adfa5fdf268a8a24c840b775a5a40660c8602acce7a528851eef7e3b843e093c3570df1ed2c71604026b999480cc9ca9a7625e8a2d6829fb6e9bd3f54502607fe68eeadc4626ec9bac49ed721dfbd703c8256193df1a12708e196d71655286fe53b96ca4fac291faa0c5c72c24acb0b7c6d65b2b36a2130ae14d93d35bbfa20b297991196e9b711afb2035978a8e82493cc5970739ed48e84c4a1e2986554fc5479bd8b35f50b106d8d2d01845aa886e79244550a1ccb2a05929a0a47ec148e2c8ad10feb809f92cf222c2fa702c2a2b46fffa6396d8650a4b75c761e128c967805da50481c2805ca53a0861948e18e3aab2d6110bde7abeb4feaeb8c5376148cd4d40420ddac1acfb2263d613c788dd5902e07be28789036b280313d0aeb057a129b9fc1befc8a2d9bf71142c2bf798ff3961cf7eb47eb64c133ed8e62435ac3cf8336cfe9728206df3e6196843747f01b41937c230cdeed584e2b4dbe8de8e100b53276cb42e728c2a210b0fe82ef107ff80bb7777489bd7565689b691b3da862202e4a6e451bb4dd9e1e536fc2b8de09e8c67e5df36da74d88469daedd33ea12c256153bc013107a2c2e5584dc05b50d158536494a4354bf1faa6119d78ea0bb4d0383d5b1084ab9409c42b3e73ce706f4e92611f50c7b9579a3fdc1baa3f463aee158c82b3f8d9587c9fc2e722b078248ef4f194d6197aa7bec2a86aeb51c9f1347f777de88a5b00b5ef550c4cb8d52675db4c0445e939a764d805b3df8860611426e945e5b534ab474909bb14667a17d8559fa2d1ef7f2788491e12f8b03c1f27768ae3069c3ba4c50895e2eeb176e92c6bce8d41e39de883cb4e372a86c7b28279a3a1b75f1f5f3c33fd73167b042e94f88187c5868b65eb0399cac97714e90b7edae8b25b22f013c9e006773f530eb2d655c178eca77961f6da47bdc2be90e2dba79b183fe81ab77f8d66251ec013eefa63acf0b6d981e9955d60f9f7ead688dbe8c630439a971a4179d9b83fe26456be851192a70309b57a9debf8b57f02ff118ab64afd32b40dee99516bb7bb0797d5d2b2f2c478c03d9c56418ecf896f6dd4ff4f33354b97992a0e3f43fd841db2bac05b3577dc00207c6187d404d2ad2bcd213bbb242e8272d30635e71be636334af3c08c8562d9de0fc3fb3b46a8127109bbb02f2f69f04775847e33c0b4e182276b459b9a4daaa112cb19ead2b4b3f97149cee7f90693dd15ca33d2a946ac30ab65f294488486e34d8c375dd0a3782d9157e7ae75dff89dfa7c3d53c98a07d63f5cabe2d8b104058bb3cd8b5b955f61dc9ae34e39618702b360060370db83f93e27b17064dea8a80f2ac6d54d8f413cdcafdace1c19f95fb9cce8c944fc181820c3c7ce8d1b98bfc78241d770705853e1363879d48f2a34c161e98a92d870b0ab2dfc167d8303a176fa4264765a1201593831496abc35415948252286812c3eda520c04749f41d767455282aa74b72d66cb9f419b8456bebf7703c8183b6b5be562c5a9c87bfe2930a8928f14f4a4388fa6530aef03db67b5859db64df61e73948353d022895e92093dff0b011ee2ed1f7091bf062848449b9a7038d7a8e87430d649dd33e92c83f7abfcd07eb171e4a1836aaefbd67adce3c94bf00e190b30309b9cd325dc1fb147096988aa803958710780fac2d3d9598584b310c6e42896ad15d95c3a14d2312c847bd7e85a337ad637c386f9dee5ecbcc774243af02eb6054ad55f0a5acfb7f762a921b916711a26a2bfc88af3b5d9e4e19a1cdde2e6e81dbc72f77d40d646c32e408371b6f4228fb874c013a42ec5fe96a9e9a7a151a7334e309d71004341f2bceb0ced64c9bf08436371dad658c9aa3b9495e28e43ec41a4c05fa548124e9c203a612175b43d16451285122710df3085252ab9d2ac2ced82ca35d3fd24399711be9137b44829cc50498a223d771ebb8e1c925ce6954d29adc935216cf352a33fbe6033581b9909ba8832f34d425e940e30a5a04e2661f550cdf8fa3db5276dcb46b727aeda7884c5fad9a9cda616205910eae4cfa7c2d515ecbb7503d2e55bd7e24773edf146d88c4038ddf6a897b04e8ce2195aa3dba924c2b169986e4ed908153d585058198eb786c8603c65621aba1b2be5984b0bf28d32b830d425571b05902fcbd3f19a583f88c41e2473844107b7a244a3293948268c7ac24b2c11c0f29c9a8ca5840ff6719d136ad6964aab4b569ea6abd4219a9402d99bad5d8773d44d5ed90e220474baa34da94bc43644275ffc900b1c8d69b2f4e9832a67ab97a2152f0ff68ea82c01756d2400d047ebb825636203e0b4d4b6e5c27f36a33d6a1fe20a1792357a1378cf11d51f3b93e67694e996545047199fea5989dc9165b1da8e92db0e71931ef99e90632ce49ff87080720b876a821a77045718165769048e8b0cd7fdd2a0c94fb530430e6ae97c11f3231ba8663f2a36d4d8fa021b019dbf3d5f1c5405b4e29b87f1380fb84020f1180516c4a92882f8334ab7e6dd0390be233ab129f0955d5886965a5d2ac3b0015f91f11ea556f4faf921896bf38c03ac510393ae4182e76ea8b25bc9aab1cdd34b89ff76f83e81ae0a58f706ddb438594fd37c2ef980de78823fa685b024aa2b439a0ecf056f764d2920b8408a9d7a442d41381da43cbccef6ef1b4b76857cf7a31cb9fa4e0f07defd9fb78e7df94282d4a4c0454bd24382b374991c65ec8efdb08e854c499273d1ecf1b02af76d74dfef554b2637a2fda2db62cc315560d43420ba7bbce8be55aa57b19c5eee3dc7271ce83033b25639d29a98e795bdc47f4898759b742a144b15da2cf22ff86d61d21772bf41626f5c06d973fd153dd1d9fa3e70b4cf4e9c78de7e896212afdeca17cdaa4c3dc3fbe0b2c196e80c7835c45c6d696c9158ced4f5f2738873864b619f164ff3dfd07f27cc40203488b866a67f126059089f43c63521b477162809d0a6efeb73ec3ff2bfb4dbc23a3bb3872191509d0fbaf40952a4ff8f2bfeaf629e2f889e7744f4e89125a37e378d581fe4aba2d2728961a7666f216d7c18be1f9b486672ad0417b999f56738d9adbc10b3067e8031a92b5f137bb5da3a5f5f88ca409524b787619ed810217405f5f12e37358c818c19db2e70b995bf0091cc21c3605e69b116b27af9a9f829c4b6644f8234e1ad9905bfae74e24e2ee86910aa7ca32e467337d845655908c092e3bce9cf37966a03c1f1f31bea6d51d3cc117648a2cb96691ac51b2ed3efc367774346e52478a14f1ec414752bdb1d490949d1b06f1a4118085f35d3fbbfde0cc3685791d534fb9660151c71e3ccdf4aa57bd28ce66e4dd2b4e04f922a00b7d3605ec4612007824dc923819400312673764a9f1a43adbf5ad4890417b9924120450a3049906eb3c93704b3aed5294aecedbab86e69172ac1b3d0ebbf976a7e50f80c0b8964214c6596137fc688c217023c18868444221509a95b88ec4c6c9ca12afe6ef513f4b1cb171bac93864936b923cd6d04182dd2b4eff16121996434052c4a671aa21da057c7320c94b3f65b8a786883de2857d9efde26f64f6f9de17ac30a9f6e82916a403650fbb30b8ac5262cd0c97eeb86eb9a251a2ec72a0cc65b5375f3a0fc940199a5ab68cd445660895a6b765c3ce3e4e04b24617c48c98cf0699812188077d698906b91f54e41dd38553ff92a56ebf84890ae648dd0f02169fdfd124814f0873950277d4d2e4e80908f1d0f5c2da1a28789ebb4d1ded092220cf3a736c52e2a78e43041f5f16457a7e016bbd74724befc3b360a620e8025f594742479a197ef99391af30f826fa17a9e30af5c7339f9c1d4e6d159800e20a81555207e59f1829479bc20a96d64dcfc94794778d47e8058a92c8a7736bbaab2bc30860a2618a237b0f9e7e57339e769eca90d1fbed657647c281cefc1a1bdc5f70c071da63b42f387b76343cef6d3ece6e9ac65785edbe526f052023da7f3abf1f3d979ffc1f0bd810a442222dcfd44a696f8bd483a6a3de93e39f2332c08e861cef40caf9a33f937acb2d808f4f43c21b42492d1757f83cef33bdcc7b7752b7b3eb18b20ee77c3b1ffc1729b6848aea757477caa0535674516d62f1afa9472ff367c09a9387b1d65b8be3164b92a8137d0839a450b96c731db0cb59d2b67a9f3859bc85320f41f538c4901d8196dc6ed50565b9f5b4e1e94c8e6502e056b93452f7795b526277b88aff7b46a0d147adfd9d236d2b55beba3449783f2a90e16fc2f9dcd5b93ac5aedd1a292d1c486f5c0ff5c42109478611675f47d3707bc7df2dcc70e37c9a11684df8053e2fbc516309f2b0ee5e806e2f84d3d68098f3b397f51298d3e2dcd63cb6233d413cd25ed7ea7f7b15dd491ef36f6557b3ff0b76235cff0afd7d0285c0cafac5cc5ed0af3b558e364c37fdbae2508c0128faa096a38e45c3b417a61ca70d0a6593bbc67ac130f258174a85a058f6b214701341dae123ebe4fe8cf3f160b67e3a378c843e1945ffd31eb288e4473d344205db4ccfe250e8c0574633fbe612daa9c84d4ea2e7f13bc08fa27b1e80c8c1d38c81a8006c2a162018dd63171ce71031366d5004e9f4902e9d7f7de610312d19239d16b848597eacea80b01d753fa8f544565b40592023513177bb61b8772a6c7b392b2979b73a3f5d502919e6cb60e705e784ec000b76c6ab047b91277dde7d5bcf82ed59e4a9b2ee62f2b2be157ae3a1a72d8489079a07fb51a7999b8b72e44d93dc9e66c1e29804cdd11e746e02aa784c5b1388ba81660cd86c5cb1658311360d62bf2f1d7b7c0cfd49c6823fc837e5d00310c9e032f880e59dc3214117329c8c659887eea59320adf3f9d6056f1d3e75a40ea54d321be4e76f54b8f4925096b683adc42fe02ae296dd498e8029a9a0932a867b1c1f2f59248ebc03e877f85fe460d815a3afde56a2b08f6002393282e87720398ca304d992a47bcc022cd88cc0cd0e78a8f599550a6ef8f5b83f2ad1c65333a53b962782b91cfa82b2da4120eb8a76678683d0c61ba2dc1fb67f6a728cb08b00f69a78cba1e24727566452bcfdabfca87d1b2302b2b2541145fbc982e32804b1c4e238a49bc124b75110151954a0b37f7426661ecaba1926fb746292ebc85641d3af0ec611dfca4c1d0bf8323a11b7c892c041c8d2f2a588c46f2d8352ec55921bd5683f780e6939cd298896515738c0c301f881c4f8381affcd17ebe8e874b9d7c3c468e4141460936ef55a1faa6b073a5be79db6218122f56e92b8598d2847f0bad6a35ccb156f3bd30ef360c331e54089c4729843abcc90b8195cae34b8784d856223a9035d3824ede4be5246071b5b96bf32a9c34055f297eb5badd5425701d28f5b02512e95e130a0d67f6be277018c275616269c12fa97b28f83c0fdf69bc8258fcf7aaf9bebd3d81ec3e0435b14a9eaed83e2e6f583193292d4725ce2c0e78f28f03bf6185b9785999b4b56538faec332e6e133a90487bc08ae2d68fc54a50796e45421342d25f92dac57c7b4dc8b57bd413922ca9b0c0735e5e47d589bf9edc413f7b10452dd21ecd4151f148ee158c4aa7350f1de6eacc50af4d32dc87af7d2af2c9cd07b739600b58c06a21a272b72a19ebc81eec6f31039fc568a9a4d41be05537b7b0babd35ca1a2aabe23c2d86b25e709c33fa0c6e0d960cbf9b514163d5b6e1d2249f7bbc423ef7fdbe2ac6ca4552ee45efdb3cff12ca378ee2cbead45424e33ecca33b85d21a023fcc71996b0567e3d8001a26027c5fc0f19651428c21f4b0589dfe4a83e208d659749f1244265ea2392606fe232af2e111b20c3ebcf16fa2246cbcb9bf9daa728664a88ca1e1c5f7666ee49b9f629821132ced9662e94b072ba826438efb9771e071a82f7dcec487e04f0fb864a14abe1f91ff2e9ec5f983277fac4be5eaa2cffecedf71070887822f5914417f0a28dbe2c713840c6581df0532795cb4a667a6850530b3c014d58d1410ccf3c3c123d203659799d7332691a573baf6dc9a25405a1c7a36b0264395c7cccc63a83f98488427df564eb039e14a2c26e8b9a8fd8e03796230a1dcb37d19dc22f61e82b083f01bd6f743185a279c60d60ad826bb24055bcaba0ed17170d8ec539c820210c90556c8edea707bf4bbc3c8837bf9e89825a57c7180956ae9814a4e34fb5338c54759d96105db087f239efcecc96e8f3f5892ff48acaf36c8a550a2f0a6b5197133480d332d26e4d2a9c139ece2fb26bbfbbc2ac89a2770f5046d869b9154cb6c736ba2a65e0c9e6b9e1a694720fd908e6e691cf2f943ca11fa16938134171e636b71134cb0e3221f7386f3cfa1910155a4fb247375cc448d4db827a3bff0def62904e7d4f3b85e03da2d84091c603f27bb463177c81ccbc201e6eb6dc430846b5ac8fd8dfde375ddcbbdaed82086be501a751b42ab4403c3a8da2a0b6bed797bc88cb34fc9c971721941d1abee0ee687982f03caee2a2919ed3c110704648242ad5b5c91d9bdcbf2851cca1386b9f949340831000128fde7a08f8b8c012fd66776b608f1a10abf2024d91759346a1a94a6077333490c23e831bd5232e2238c9af7fa66eb9051bd1c28f0ad633a1499e541af3dde959c2a4c6464478f1a49a1ea932655487eea2f17ae1ba2b7213f8f328a0f6dbb9435d93908d3499e716a889f44dc888502bf1b9e446facddf6f8c94e89c8afeac995842ae544512877ca417dd59a4aec1a732bbb1974ea69b471408609c2ee2233615120ab7383a7a0043bb9bd89f98b63048d9349cdd28cb45beaabb089e4c6fda1aa346b23a80de758522ccadcba457816d25e642505d351cc2531b0e4ae025723d435edb460f9b034d61cc2946861049ce223205b8ae5c815432cf90bd9b73ca9b55e7ae99eebd8b3f4740d6734002345f1b537927221cf59c3e08aca05c8ec30d531b16c7c3da79437927098124d558b4b54ab12b03ac5554b04c3219ea68ed9d8051101924fb8abbbfba45a23756301c6a0989a2f9cefaac336bafc06fd29e604129187c523bd880c7adef03493681c374a6fce1c5a13cb6c6e5cccd0b65ab8d3d8879375d00732ea360e18e0ba49e09bba735010a85bd626084a8e4de02cbc82304c4c55989d1acd8628e6ab92861a027660cb69b64fa6625501bae930d6a515658a123cae5f425530eeb0cc925908279efb9bfe82e6a201c408497aa328a96ec4dadc6af73ca9d7cafa11d630ca5fb7fca8fc4a9d0702b940b9c8c79e65191849d47d61d949f9712e8ecea15bb34ed8a3902882cbfe10d506e4de0155647a189c07bc2bb9345e26d60949d3b1214b102aec27ad3ff1073b844a1498017236dc4d3f499dbdf3c0a6ad75d6731966420cd972495ad04b382d21ccd5b28b9e252c2af53aa1762d8f6bf643cd6006d0b5831f60edd018cc001416bee8744de2692f0a20744ce9c8f05024dd4bf4aabbcb824e94a3cebb5c27894cd98a87cb0b3c406da0078a61b7a5e0b7e457c3919ab7797ede2e0d5d0a38f415278b94f9605c2f659092fbf514d30a188421fc7ae2f4b17ce10a7e58a77de4f0f469b9351fe7b440d11931ecb676e1559e5f8298b368cad5a85e90a13a30ab0ae39fe04599d58303066e87748d45cfd05cc9017dd8519f3caca42ccb671c7e6081c9fe9b1c770ad342858809277aee826e9b3b0455b3391b71c61d2cbc3d2e05051399ab048c02c7d19c7f05b56367982ef554e78d2f714840e106bed9c2ed25caa327468269bf49350b768e78a423b33420b546a8656626275b09b7df2aebcf0ade3c2ea0c8f3cf58c972afb7f153df06588d84102179c1d58376b03ca4a98b45a159d81c3206dd3379988d54f9a365f0d44b94c016825d224fd3fdec48c699008cf7c1ad3b7e751629ae3b23f43556570e00e449a38c6da088bfb38f4e18b4c51dc8054da80fc0d901c3808cd5241f29f98369b0b06fc17c2a932c04fb6b06df41ddb878129e97e110f0ba625ac93247b56d4ad29fc41366cae0e673b3169bd22b714c3e81819cec6c483666e8b91dfd828ca80847c193d3f7be115817961163e19b829e8c0c9fd674dc88819219fb79a1bb1054b68c072548f126168939a27428ef2655442c904c73fbee83a2d3a3c593d53ac18412661d148be8c6e31f27425e83f33832953130559a73de2c6814532562128a9f721f44d051a568afdd5dea0fb4527664ca4175ae37189987bc3271426a24a6f3c74d263a23f635a070240b1c1cb6f60ac1b3285c657ee0a720b04b7867a3c546604668de710e44c2dd9b6c016e02a4aa6292e344ecf49e4e2c5cef5817f90afc5022dfc3f232f2619f57d0532b525f0e8973f4d178c2c6d99766f29c287b58ffa5bfef65caa916dad11008c487e6489f09675133e66e6ce045a8900e59463a30e7ee6f10bccbdccd3701f69b6dec665e4ee3552e02a092e6ba3f98801129c7206a3ad621a23d2f92c0f67dda15351e5fc3806a4ec6ce0d913bf8cefd3f1d1e36282ba41d58c8920d5c5cf39cfc7e80ed4edfd32a9362f657fc72b265f24b22708f6f80b27bb6c350742e6c858476d292277ff47937439ff21ca2516e62292056cff1f97f031d752765ba4c5712051e1403737cc4a034dff2f4ab2261fde4960b8a8613890afb68ea04f1e2027932e8aad0e81ee8306440e11a073e67cd93e04e08be816157cc157d28dd21afb60cdbfb40f23c399c4245dee9f8a4e07d8b21dc2cf145225cae8ab105f0b39be3cd4a15b5002ee44ac8deb5312462155da038fd385e5a63be955d909e467dc536afed5cfe61f199cfa4f77b48c35dd2123585086841d725722638c4ad895b333b66d00d4f17eda598645ab89627768a2297024d5e50505df14bfad21a23ce7d96f50f9331f4b6de934137918bd7dc616359d3d7dd70c755addfe08c91559e0b68f3c66ab9b05a93cd5a4b80226f15247b7d193814b63e223f42946fb1884c77fe0467d245877677e2f11a5162fa3d02bde27928fd66d17c9860b1596507f32dc66e8f8fabcb9dc9d06f671fc08c754e1debc20dc516afb08dcfff63b874ac8a62908b9869c34b380009b1fd0c35c3b3b2c0d82c583866f164d907b4a7bf5fb58206460eb6e1a90aa45bcfa8807e06f2dd4a8c55426b59e0151d9a21688df8e4c9e1ea4bcad83d80351df39c824f422e17d83aa2d6e97d5b36eea87945ea2b90dd7ab521e5c0f9c1f9c7096e0546c881cd2b3a0bc7843f02b0ca150dcad75cdc2ccbab879ac36871488be1ef7ac08ff05858767cbac8126bf524784b7fe8c8ce6bf2cf644bb82b017d2debbb6912f9254c309aa5abe7f2f380183717d43f981e5024ad6b707dc582e2863c5acad9def688347cbbcf776f9e1222e915b6568dbae5b57fa88e84a9ba19a4eeea6347793afe7279451331b836716feaec1c684b4d5c04cf3238becd0f652fdd4c6b823f7a179a6fce514ba9f3212830dfe859a9c6bd4ca1754fe5e18dc9b2b430108a29f227eda4d29b94f79a0105d409036671eaafa7daba549c60eda4fdaa8133b4bd1d484ec2d805f74f8157ddde4ee63966858a4362adbecee9bd52ab9c22899d76ca927b1b86ba9d3aaed28eda375a513a4f6f19beb61b8e4182612b92538d33c259807314801c26c6a65689e16387602afc3824029c3086d4ee0ced89263187c052f58656118e73916cc989c60a94c3bc6ac908fbc6e9be4962f7dfdd1713a8a846126630c34985ec3c0530849f2ecb263434d1af4a1e825f3f60b0afe3cfb62bcff7d3109f739fb62142d7ecb7187379b3dc0002757d86a915575f23c45cf7ce99befb8346eb08a051404ce92df4655c085e79584f95275d9d79171465c4f4948932cd64b08c8a871890255bb452958fe32d2bff2981c539cde9c07c722559afc3273f337b21838623f27ea99bc80da35f260b087cb75fa7a961b4b2e2c21a68243478dbb685d4c4a02c370290a1d25bdcf6c2d0a34c5179fda44428b66294b30d79f9cd88d64529596220567094240e28ff7344503c619291d0385b7e4e34010802c3130e545b465866917068c0e121d4690cc91441bfa809a420412295d300e109497a0763a7c544b32c62e109891a9947aa9bf5ed0a9c35684b5c23cff773413aac81e633b855bfb2a2c0fe7ffb713f7fed2a8da7c9e138a7e88b6b6f32ea07be0566e9c7573b114b5d297be7b7a0f119a5bbf9b3adc4f256c4fbd7d7e7729e3e094cfed2388670b0bb05b653188f634d09cb686edb174f54d39267e98f62e803aeea317ea76986002e8c59c1fc39dff2f432c2b45c4f717cc4d513bba9661f7ae423df4c10a128bf58c6198fd489e4dd7edd606d99f9268a187bf720fad30c0461ff4afa004fb44c08198dcb70682d5608211a3ed7d2458f3770084c4bb13bd87f8cd0de4eb6c8961da8505a19354831524b2ece1bbf05ccfd282e0a21becc510a172faefe0608f09c8636c772c8aca04377385f8ee7f637ff1be66b89e117f9abc4092e206fab2a073f6d36874d6e8a5265775bdbaa6e734780c80ae29cdcd83d64e63b67c9b3179c9eda3a5f77678832826dd1be10f5c27e4609db4e1dbf4ae7c706f1d35a1a0a5c0e30c9397e746b885abca06880644327b3182bc3dea199052e18080f4734a1942241490e62fb35a091c411c7359d704fc87ad30a3a8ce6a34346869a0c7823bb12d4ca6e1544eada790116f4631441b950f5a62203e92961d8101f0262883e5671295eac2e0633706216515188ef4058c21a088f2cfbee75a03cd6e3bcfa38c111bfafb007e93e70695587ed3b330662e27e46a34dd8f457fdd006ab3baf8b5edf3e8f52384a7fb60074082565a52537d8daf0c622343a405cbaf06c455e193c98e3118f82b8e0b10733cd8ff5c0b86d5e6b1acdd100822b50428c63d0044b0fe4fd76c303406d96b8ccf2fd7c747dde5269545e069336c072de78ec31eb34b9a52d0c753ed8f8c145a4129509d39f89e3e8ba49661e872bf624add907e181c5948c863cb6b917529b7273787ff1030edd25545f81fdae88c03bdcdd83547949546efc39b7dcd5fa041c4fdd13082f50692b7d22f9f4530c66c6c59a0c55f67727a27b87d35aba71f8df7581cfd0214aabf5c982170a513626559cfd961e292ac8d949fa023a0e7b8e22b10853b350da24fd8363a624df157cf4587fe07cfe1fb570d2ee95788748cd0bd5f06bf593a684bd091fa66b8755fe2fa8f6bc0b903f4c71df23c2c4b2987264922f282d6fe502645305031e94981542a41b5b3a7740cc264ae3e371cbcf87ffd83c705b418048ddd5b2da0a7b8b32059b9f5777be2f30d0e50fdcf9de805d2b62fd262390176727ba8a6f4a24ee7f4819e7c84f1702104110c9348c2732b90ad59b14cc6d06779712960ffe8f7ae3aab61b920fea62d973bc14b702e0f3c45a4d6d10d87479f372cd32eab1b41626038002925e1789fada8b54e1a4f1997a660ab68180832d1c203b6119b5d9dbca6d32f7bd08a7e27a5f00169f6be82d57712820248c1adfc5ff418cbfff0b63912cbb520e2e6840d25a1abb4c4d7dde16295767ce10914609cfea6c1425123404c5816e2de007efbf3d366e602ed2548e2526915586d06dea15326428cb8bff221ea17226a3dc1f2b7c2cb46c4909757e937aee59c526dd2804ea2f8630f7041909433d5afa28572d04c83c23ca4fb2171b759c6574188e91f707449b690b133926ea5364872a5629562267dc099a8e34948d437c06559b4a502a6c52974e3f5edc48a4295a2299c025b327a0e0f091ebdbb082befd45a186406e730d5bb9ffa64650d2ddff0680436c71f5d858b10e0b310fca1d5ea207017c2fe8538fc91bcbfa713931f42c59c0c295033a0b0097b7bedbc8532854f311f32288197d95659b08869059efe6baee5b340ec4983423e0e5be39d50b59daa74b0d12086308e309824ba850653c6f1c598205a939540db86eb2ad5c992d5cc2457b8284ec61d88c3496a1d69dc4690f1d8f240e72d535e0e247ff8ad81ae3b9b9f48b51e1b0f47c74eeef6f32bcecbc5d720438b62df15f2b0525ab8680172e308f680d3a9ecb70f48f6e4bfc12a0533d9ae28bf38acc2e41ca9fe726b928b22c5c97e3d78f18ffba059462ac2db4b929e7f1bea8d850a74e25027281efd7eb545695557f3938c072a2b49d71bd8cba4af64bd79c10d80aea44db09ab4c9a439a036321ac41678fdfb5f23be2c2ffc9814328a0c04ce17f027f7e2be3db8534b7691c26e2099c6a4bf79d294b6a6decc8c5b7ff4d20edc1ebe9dbdd948afbf07a8b075fb9706434221c3b649e0095d3d0214544286c8efbb7f3314680dba75d17e54caf7f35f0b013c5c3405e818a8fc85f5800fc041c670b63bd49b77cab970356678d1c2114c641bdbacdb6810038ca3ba2c920e40d0c9134896d365362fe8bffcee1e47b6795fd33bda32a0d12efb61c1920230997c7e8dafa73a6eb0b56ef2f7879ed5a21b30f0a231f7d6168bfe74a25cdce8c0cb0c2e523267145d986125a0ab16ff820d3542ddb3c3c848265e76adb0311f2b41b75e98851d34e869261dfb218e86d8c4f6383e52e98eb92666418b9d37a9f4c4cf12286e3eaa0881fdaf28658f4c9465f32f007b419af99f93287c8a088ce43c5f2ec58c83a586c3747472d5e610ba6f70ead3b5421ebd73370eb9f07b00220d9b3b068792511ce78d0e871305d3f9286b0683b6bb8ef7b7494ea958bce515fe9d2977212fe29dc6387c818b1602dc07c7fdca91d98dbd4db94650432d80b3d9ffca5b995e38bb97463a48d9361962e89c9a429ac01883571a13d4390b331ac88ba9acd9745260c117b9f4b07812c22085395f305c047c5d75ba9f9f756fbe95170ccc5cffe5b8a3fb3d10c264606cdf7616e2bfb5873748abc2999486a006c194a7129549af5b060df5adf6ae5b9f0ad586f47c97f51c0676d4a58be3bdb61752fd10dc0d8a9b8277aa0035bf440022ee7c506aab4b1c38ef44e815027a89f04d9d62075480b1c25d5efa6831c5e1ee0a78593b03b370c2d5dfa82641c44b4c2f87e130349b58c08193ee2448fe558e619801fc117137e2dc00b50e564d39263080402de330aba8dd2d02c409b44822add69dc18a5064841e6408c896e258476331d9db6e6b0e42b77d30bdfa341326ea40c6d149526f409aa7df458ff7906593cbae524643bb2cc18b68fcbae0f41967f6ef22f46e53414423745fd7726f26e1c2640067d11d08630fa88a2de3abaca4eee4888677a949b244b8bb45e4f0af52a4768a8b683a5ba94b881f0679d7d0d72b8a246258864507004b5c121511a718d1d459f7c1e6e5e75625c75fbd9b3e8c097eff96f2ffa5c2b995d36c009779817922e5b640a8f83be5c4d597661506a53fbe4293c6919392907fa10ba1e0190e7243c79ef88f9909abf38331898416750ea5017323159fc75144f06a4f96cacc2b9b9712e6e04fb9c9c0de09e26d35fcb9bf786da4b07c88cc3eebe1a67aa8c47dfe580b6987c07a089f57e6d7609b881103672753d95871c1a34b3b8eba7cd10ba010fe4571bda175c2d416dd701de8bf3703b70ddfbe7a744e64f24b14153566cae25817881476712b412a69895a2db703de1f9e37a99724405fb59eb7204899a40886db10c10a9463b3cd831eb119e027ca67900de93a14a33041ea3a44d1f3616ad35d59d841ded35c435d006bb8dca800abce78e0cc69f7effc377ab905eeb9157677c612890871614dd74aee57f85bf33bc4884883e05e231d29ccf6b2a1316d7a056e832b1f63cf80092b089b6ce45d0d2615e4468bf19af022c51de4dfd407370ce4ea2e2e55510fb175ab454b00e246f2389a0963bfba0f2e53fea1ab4534b46c38eae513eb3cdfbc0b198645a4f85e0e64cb28d09439bd42a00ec888a46754bd50c6e6f47396c468504c518e2da64bb9934b4fd8e11712174f3183880c22ae0da034f0be148177200fe482cd99592a20dfdd1ab62903b226290f65ab6f61d98851398bfc3c0acd3c2835658a9157576704ca3615af9768c58b9be5a54aecf159e68aec09fa7f6d2c6dc861622dcdff0070d21f03887f0d47d9dfa9c8ab384a877f24f0f046a48641855f867be49252c7ef11e15e9f4350e06b81a8cf8dc04a194f42484528ce3d7c69e6f589e50140a10f11288f5e6a5247035a23af3e8e99cb6ee1c0112d0d2d60e22179c4e64a7d5539f1708ef34e2a2925220aa45cb5e1ad7a10dead2d4391dfbbd14dd4725ddbcb83dde05f5c5a140d3e22a3ce2ccec2c3880e9ae8d62a4337a0377448c3606feaa03ff6f5f00e298256a2b2feaa28f624cb219c86b9188e69857119666b108a9bd85ba14a1d1c4d294e06d4a48d11c648ad2a27a029c27cd54ecf5413494216dd99120ca9c34b4dfa96713b5b25162220be0828a1791d876a2b0ce035dc81b1e1a4992f08d89549ce412b16b21096bcfa190457235cd48664f07189265954d38f3a6472894c0b2f4d3108a1544778b691a508d7c33c3bb72b8e0d2349330237acf5b9a1dbfa06b007b9fd69411474986ebd3d2074b6c6e7d2d42efdd19ea90badc22531e4f9d9b63dd3627036955d5a9d68d9013f4399958c42ccd918ebb3aa1b81708fa9223d262e1d953099eb4b4b2f5a12f221ec4c70ac5c4f3877de5b865b58fab4a635d7e8913f6bd98224a6c396f1940b274146fcac613a4be97f61be8de734540d8d9bd7e94b95b335bf84018ce68bf1253fb7b7c140f8f1e63da737963ba9b9c84234998785c7df9ae8bcb1b2ce35502e5f8652b92c9d01b2a3d4ec2243edad631c22115f380931d909fdd003bc5b92916a7ab5979e03a9504303adea0b48f5a8e0953956389d6894f7c631ad0da74e63eaa3e3477897292855bb8aa008c971ac547c5402e335772435faf0a4a24e83a26a0a81c701b7654c2f526516754ed495e9d332d4e0fda5c63012aa58b2f0f2e276178c847489dcc04ac16349abcaab745195bce83ff54ba50c9500238c33c9eaa3ccbaa6f50be4eb7c07dcfe7db39cc40d64a0884a23f02c6d45f28b0687c73639d53016e29b79cd407c5076f29c31f0bca4aab3c5e4a81792b47d56abfa3220eca25a50f4ccc8e56072a023192cad5d902010436ae2621dc322b3ded21d303a2a3a853b4b539a37eb9af1e7feb109d39025ac2a8ac26ca03dc9052e79f30f25e5b617041d14a69e8854d9d1aa5d069fab701fa11d88f7fb8ff183c6155c00a38191134a620b80b2439325187e402f4958520045fe0ad820b248ae7d3eb081d1d37e7d721130bb2ea561dd19c00529540ac826da7d600144745c4511f7060dd7fc699f58d9fa82a55cdf014ed3c08063202008047ebee9e648fbad35be59461a092e8806bfc626f6c0b5e16c1130391979de5c8028ca219a596c76712ac865d201b6ee0e6330f04f35fece4303cfb9d531b6a00589858ab29c7613e940e3f4959713033a98c46dd5dba42400db260beb9572a20b2eba06858380e13347ffb2ed934163b98449315815ad68da296dade29263657058691cde81240e4d165e2718f828bcb731b03fd84da78e5f90ae8616df7248aa8ca01dbf5a29141312a73b3634086997760217e60bfd444858084286b86a5cd7e9e06aa026ed60cd200be8cc2903a0c0730885aa7ca9306a28f8ce3d76a3bf7cb8733b7cc8dfd555959c50a8dfd2a21097258294177ea5a974b8c289744f22203536f88d1fe886a32e13b023d6b250e9689c69e70f1b85e6202cc18306e8a16110c8c2827ef413dcc4cc54047e4c106557966857e7bd33531355ab1606b91a02b2d7f60a1a8a88e22e667a670e50ac194149d45847bc19d9da1b5f203cd2792f225f7c8050a583579d484926c8f68decf495c67e9a059949447b502d5ac14ab3fe7c9a69aa8acf5938475088f97e5709580af96547328431c254851ef6ecc9a21459205549542407fe01ae4aef8131579a4cc20cc7ecbfe23370596ea6ada56d8ef48729b1908e8512614e83fcf6d48a4da598c455431b8a41474b6258a2ec755c29b654dd7e9f46773a0852b8a5ac3316ee4b36f5c5e9d1de36879259a4d306878e7f0f43f18b0c9389cba48ded194bfd13868a6cb99a6f6cbf9af7d280800327f1f75d3d51302511a10a6ab10d8e9a770cce2360cc40148a4296bc8a526938059442299e4bc3f915516c6ca3a8694cf080ffb12ce557282faeb578d247dbaf908e596b46eb48477fae836d568f27a10f722d8a72202a1598f80781687ac5318ee5c1ab418cebfcc519059e1cef4f847ab11c2bb658c745f34bb019c8f77d01362aa6bd424eb62e0088f217c1fb9d59330ebb2fc69099e0966bd8a7b00962e71ef976e4adf583601228d966b5c2bb6bc294d4501c3eb7552534541341c0489aac72bf1c5d87f242f60f694744e348545782ec491cb87d2ea9038e6af6feae8d297568d09c841a084059ea48b36705b9b404f8a5526c1ecec1fac80fc0bdd743ade1aa242270109289710ca002f4a81eb74da01ea7380890f63c631f4f34e3bc1c754e4533626dd14c3f392aab393ef5ab66bcb14166e58221da3ffc3eac318b934026d4e8aa3869c8ca86a8042701d1d78fc609ffe5059135a61ad64cac5aa891433c385a1a5df54a200c85d70f61b5f80bd53332b8512b44208d28706bd48c093cc1e614f4d6c743a6053c05de4a28bad6159d45811802a69628643006a6344009c218b97a50c9bfa771fb07775d29ac7aad87356c19f50a148a3992c41df03812e44a8682143f4c68d05ad0186e84f8faad4b134cd463b201b8e625fb039dbab17f7ab821784c182f456c61a30bf5cd7de49aaacb8a94ea59974ca7e3090db52df01f0d7b328954f10eb0309160e7b33daa60495259d63a22575b21a165e6f905ee8b5d849dd26e78cc719f5c1c2280ec49030d71ba68f1b88b1b718cb5121308504f71366074100be852ccbd4a10decca09179718b45c932ffa0616f6b5a00ab611f962a9282272b61a831bde5669f411c6617fdd8051e3e3315283880fa25388f6e1dbfe1af2d9da3967de325c8ceeabb5bf831e1b3107511770cf510862aca17b5abb3fe88500521817423d65305d5a05a93fdbafb9eab405b6f2ec2d614b39672f61291393e815cbfe2692d4cc4dcbcd4852cb1768517376104f7eb0925e8db41acbad9ded2989557fbd1414e3a67f26f1228b63afe510da89387fbd72909c0eb9239862c956b292651a14f8b06e5fa7275a57c2856f48179cf759c10f1a610e1897ef40d0a58321703c3c7bbfbc005d77c7b301c71f9e940f4e9f993645ea2e32d9a16c82b248006d6328b6194b789ec67449b6f4fe4a71c2de31030cba718cd3e66e77c83a8207d5d05e403bbea29915c8b15b76e58b718022240276df08d07ce55a3863fdb0c791dd5c48be50a4e69b0615c2b9de522deddc929a2136d879c8106891e490c8245a48eb1c06777bee47379a02f414c3d1f42f0b54443e414d1dd25791604222fb89828272a6b286aa6501c8ea4b95b706eae302128af5d5cb65da9e7c712b4a9f40a1948fdd302fe621b86a1e0c6b8dcd86df11be4bf4c15f486855c8c9969994eff6becf25fe4f8ffe4f319d5c2242fd9b0c4a5a450ff194f0d63de42e3a48959247ed91824af9a71f5d7fa393aa4bcfdc652ff8988502930707338d17c34de488aa8d85d9841c342e4b3b50598fee92c9bbf605ac21416ec7b96b3008f18a1c86c6c2d2747b84d76c663bcd25520f177e4fdbb2e697026878f7c7a3e751bce7f6e1abfa6811a73964868da16349899641662332e11d6fc6203ba8b140e530dda21effde41219212acc03817ed0faa3b9f149aad4f0fde5c716835555402cf1a71e931b66bf1e88a6fd132395063deed116407e1da150b350d11a2b0fae8e170143cf32b2182df46ac3ed1de05949485fb8acd0ff38d817fadc6135c092ec788ca686ba52b40bf42c491912a84fd051a6aa2ac39ade3bbf1e8cb81bb06e95f08e9a1c3499f3942dcfd75fb535559bdd40a96f7233ae4f88ab3627c29fc9141d6b451fc6b190100cd29009133a5a0d9f4b725083b94646554879aa560472520402da21c7f45186459c211f37f8a12d0f7ae638c69979666476d4e9d3cfa9c6f38f030014573a2fee03d6f9c849311eb83b9c370e5b78412ff4ee137085845f17a1068bce0004bf251d138667debecba5c58b3db14b0fb49aa75cce7ba1b1143f3fad4ff08e120e2401a71e6256a487c37157e61acb237a31f28679cb5177b75ac5a7f112fa5cedd6980da105e4e2cb5067e91c01140abffd44bf48cee48330a791e1f7079ab2f373cb3f331251da2578fc3e78a555f108b45e6061ec7491a0da5aa6a484c6926678e69528f8371dff1ad95da61d20cedb6c890b3808cc81f80f722da9a4c6e5f55bea10dd50fd81c6564689eb75d6bb78446f3256d549ecb3bb20a3f1855a3a2cc7687cb7312e4100020123ab17bfe64d109dfc5305895481b60da7201ea3b1021dfeb29298c50f5df128fabbd84f137db3a854d510b297a37635293cb31db40baf30040d6e7bc486e8ea1143f4c99149724262d5168daf2025cec01fdce92557dbb87d163e93cfed775495bd74d5f1c03efe3a96af03623c14ac9839c144bec1d195a9f9d4c204388693bedde897422a510f2e0896d59e905e8a53e6da9e6b288aa3751d4bff9403bfcdb264b50f17ad0c41202d4c0aa8a8d0b6c25c513b94c3ccaaf841974ce40961ac486dc3b05072b16122e22495e351b384436a182c79206b0b0696b98af628aadccab36af0e12174d0fe031e0f2fa89edd2d011ed9c4beda137f1fba245a7a55d3c3d2ba8ebd09772c0002bcee5236ec2dba2ee41b8ad93e5509208071b611ed0790e32da9fc58d62a6da9684c15861c0d5fb496c9c5fdb7a7a08411b6be1788b6308a18ec087348d25f85a46365e8c7f1ebbe7a38373b75061e3a017a36aff8c7dd10ba47ba07147551bc6849cf7b03c8bc28d6a371bb2887aaf1297eb2799e80b38913f20a49a0dafe43dcf9c270f93c056c05f9d927f395689200271dbba3100a5c85608f0a4c8625b6b080090dabc90787d07d0ae3cdfcd36f95dc3fa616391c418ff0b403c0371a0c68d22b2f548b0d93525374a055dd9c44211e0047bac1da1a9a4c4644f80c50ff5f0c870608a3a791b270332a1bace3906cec39658137af2afc37e4aacaa21cd0b67d5c905e458a6012674c84e0eb547aca5b52c50a4901587989f098067e66995f826c841aeea187d5987cb6ec20b44d9da4ad2661d05c9579360e183eba85ad155bde702220dc8b462c3307a2a94efc0e6efda6e6f02d8b45f82b71348b0a80fb33744a26e8e58aa872fd965313607657f1e430913a0f6991abb92e083d4a01f6e7d83b7d84c8e58275e0099d8ae2dbc174c086d04b214b84f323d27b38aed43ef8164e74ea326bf745d9ffddf1cdb445675c9d069f0e8610deac28f20f447b69d17c56d714b2c0e2498752b990e02e228ded8218ac4b6e807f84799f93b29710579409eb505eecd0f4ddc19738d623e33487344dcf619786e8a895ab6b7d873396d4c54024d8c8210d8be84de100732fd40b91569ab295a23229a61e5e95c7509232d57b58904459efaca07235d94b2944e688ddf7f9b4485a5f00aa918de50c3f8d375d15011a3490053d2c1bd72e1e2a6f42c01a0d6163a90f0cb6c154e646437bb3a655c6de471ce4020e9a2fca48025c8fe9c029cbe9e34c13da255c4147b284880fd5e808566c109c11c7bdc2dbc9046bd8a7e9d05b65e9bed12b609eb0eea6dcfcbcb9587538282b9d4a250e4d916d1610e17b640f9be3cad913468aedbd2683f9b7aaa9e8b5d4aacbf208bbdb1e2e8f7110734a5f1619b91d02d31ed1f9112a90604fbf0b64b3c673cad455bbd85c1ae1a53d5d06eb2377e60f0c687ed2ba061297337105262d827a6f11df7bd7f95e81bbfafc341a52f5ddaae4482c67d52a9276968d9950025754c655684a98417a89b04d88a29e65ad331a232863563b90521712d7db3818acca29bd7554574df582e9e729a8850cd8d5378b883a8306f15cdfe215314d42502844a5cea1a3a8af11024b5fb3fe3248b65243924be0302ad0a8983e4668184b2c686e8806a414a03ec2d81a7f8147df233d58a16fd91bf94209132c92631f95c688c08d5b90a9c63c4059a779844667cca3590030144b0c55a42498672cb944ec3362de25dc01269b01f99022d3e8f413438c83bfb82b94144f540f23334e17f531861f07c73bf9a095f8cbb10261fd0a6182755a509b7064cace13d683b1cc2a770dc9fa02cbcce8cc280ad04438ba08b2fabce04db09c40268bb4adbf402c572a1d0b488fc89f019dcb4bce43ebc561667ab75cf8b801635e3692c57977307dcaa1796081a379882332553c36bb44549cfdbd535cab642617645421bd0708c458e1c8558a796d1b05fd8e7254c0af88e5df6b30d471036a79ea7d5c3052ba4a07566b4594c4e973b629d9a283d1bb5eb0b58c5e22dd666b044ce44a4fe0d3d4f9baf58e1fde514284485a30b80c0080846118c6cc0c304831c01d98f3b420cf19a64441412eaa5235afe7d1df84ec2de59652ca249394016308370746077e5f5f7f8790be39a5f48159ae866cddaf64a091fb69e88c9093149778fd00e6e543179b2f5630ac82ba66db66b9ed6d77675662d2ca18fbfe3116a76d6cb85365fb90ab5b4af5a4ccae94f2a3268020d06f0ced7c792d5629eda6a8ae2bb832f7c31e0f0a0232df8fc36f28e57cff2bbda534bd84406a4204db94ce5e978c7df60a62cb2b88219ab7f64fff545a0abe4c104159e6b52f464c4641397e0593e367292034328a47ea65702713f31aa3641c10e3b3df6908c87cccd45098f9d4cb701d0b59e6a78642ccf7f65dcd32dbe64395aa9585cc424ec5f8192c802fb614e611e365702bc667b8a5bd8c0f5596c13e543946e74335838bda0ef832df5a8f86bd5927fc3254aa9a9a9a1a99944c8d4c8a8686e62d4713b312c156ff2584d12b883032d9cbfc95e1500f8d9632f6190a774029ae6321a7666cba5931f00c67f3317057533ff336331a0dd7f9506599d71e95a1360d85fa8d9b89e1a276c2cbfda9d4f7a7f0f63332325ce7a79df0b28dccb762be5f26e6fb63709ffea9b6fc3a1d16548f2ac2c6073660d880515db5c977b701b379b37ac6d970154337815f62acb00955557dc4166f2a162b2760ebe052537f4f172cb58df702c2a8ca969ca3bab012da3a38f4716b0821c4b225d2c71b31a87f5f1f8e9b5f391433bef32af9b9959aeffc4aea3b970249e9c88d72bce1452f3343832532354530f1c6fdf79d0bf9b3f51f54c282a40491e20d68740489200b2f28c32c9a7c8b3331e517e133e524e92861e91b5e05b27862b8579db4a45703feea0519be0d0c32fc1931c8f06550c9f0554964f8354a64f829ee9521914cc3b500b3cccbfc0c9792496282e8252935e59997b40495fcb4540cee25c920e86968320d4d96c233f8352b2ac526acca70ae34b918bf64f8405990ad6b9897106054809474940790dfcb07a10c26b3f27b3d59ca3e6cf12129df98c9275e149b74a6d861a63849f6ae8768e7d9d51d865dfda7f7b8c3febd4e87603f5e5ce357998f702853585fc6338de15e87a0e12ecb94fe8655d82af691e5fadb893bfde304f0c586611f59c6be43611f59ce5ec3add9ca70f7a7efb23fe10e0a0161d3b08f2ccf7fdf65d847e63257dceac77660dfb8355fe6a8f190796294be8d114a4a29fc497dbc5931d68718a5eed853ea0333fbfe7a92d44312d9e14f7fab134388ff3a28047107338fa4a1a4ec6de3468f1842cc779eb3184e2786109d1231d243aee5a0aee59073ce21facf8611a880214f7a93c7959cf7dc4c7be089314e672524368c0debb3e83b8777e2bb9c91897d62961e9482c163749fedeeb9391ff8724777dc6d5e6c1ee9a43f7bdc57316c70273e2dc7f672badbf4fcc8b27fb1e9814d0f3a665aa2caa607e2cea6be1a5b52135d5f8d9c050061d9545995ce06becd5ab679af469654803250c615faeb78ea42c00467b4e142f53b0d38c1a64b0f3ee060a3f9bd7ad0c1739af552f9bd7a40410f513a87eb53f280e44489872f240f60c42c5ebc582f3c4891f1808597d9c3911e60f8210739c841173cd0a04a27bc287de141894a252f483e259b4848994e8ca893134c61f37b39d1211629ed80e50629ddc911eac48629bbaa9a304c76773224bb4b29b72cc5151f7439b22209153c9d2a17e9a0088c43286438c28730b630c10d967858b0697b1bb7a53a845711978ce5f72ac292bbed61d8628523daa37b4cc1747777f768248736b2c3a971c082a4c393fc5e3a24c94f8712a030556f64a5b8bbbb67a086212868008430472318ca8a34c98c90ee45846477f78952f2a2be74e892830ff27be9b02577353f1db2e8a0045b6269408fbe51dba37b74777777777787ee45b6c8eeeeee1e6308393405618a227a2872e287305e47c2681cf20f4ab264d1c41518fca08b048c6019720222410c99019051103e7c514ac2e80a2e944ac002243948c1611be2861c9c0c6d587eaf1c887278011325398c80a9d25a690d5bccefd5a448132f4b3d52f0424a094296a5252760e2c4d12c3245a6b590f9bd724072a5caa122273df8708318b038428b1c2e5caad00045922c52d4a123d824e2c4050302620b23be600118215ef48044952b7a2065484912476020e3881611a69423db111e362360cc1ca121c60b2a35037b8359c4095b040baac1541a242830d5af9ab8d02640e4a5d9c5ab3fe8e81401824c91cd06233240d51350f9bd9a10e52dbf5713a12b33dcd7177a395a010ba097560152d20843463ad533f5800bc0ba76d71998ec20776fa907869b8997dcddd5d2291d035c5e4c72d02484dbf86064c9d6a96cd848b0d924d96c886c4b3d5260b282dce530d9b004b1273838573285413843388133040e4ae00c819429c519c26d7e2f1caec85489103310010ce00c4f5ed04c3c5144031b2e8413a32432fcf7aee5807e24458010be4b9561d19d4309d8448082439008744a77a9c47d7dbbc8a694dfbf9860c90fbbba592c59047c427180cd24496422244c869fe1d0e58543960c3fdbe2dab8e0260b94f1825ee5f7bac1877893dfeb06a117119817d116b226bf17d152867f27017ea031f27b110d559c2eaf255bb8fc5e4b56c075916d26bfd7921f960c65efb5c448fe1c6833f27bd950a59e3ebf970d45a096d8c0434c7e2f1b68e8aaea0bd6c614a364e9a5a489fc5e4aa6e4a7a488fc5e4a9ee46e7b2969a2240619992e6668b6a49234f14a9244aec9ef95c404344988b25712a12cbf179230f9bd9074c9ef6f7e2f244cf9bd902cc9c8ef85a488e61ac9f65e489e6499fc5e488ee418f9bd6af822c3cf6a62d0a454a7f835474c07ddc3065f86cd0faa5fe9604aa20620c0f085491820684245103c50a2c10544f0cc1d72777fb360ad15e27438b9737c50831725988e08d5fc5e356c91bb1ca66805a8a804478c00490d2320e2880d4cb1e58a1d3ce1830a80e065d2d003ac43977cb744b154b3a84c355bb272991ace25f9317ab4796243c3f6966c88b6293d365eea9102cdef55c312b9cb618a5860509ad54750ea6bef0e6dadee95c33ef6ec23a3232422ce06c37dd4acb93ab245db27c797f33cc3c17c359b1ddadfd7a1697fefa37e06c5753f7986eb7eb2632f2796cd70d8dffbf7d65b65957e3ba9598dd4ac599ba875507935a6923cbf933c1c2d44c462f068558391b791911194a3a3eee0519e464748cd9a535e8df92b26db6dcfbca93c1c3009696564eb5a08a9524ab1235bd7484253664b9b8eafc3e2888172ffed96dfdf48f69bf3ac458bd9c7da6d1741d95e7e2f1304655f15d9ba16cad1541e0e17cc399270d8d403f86566b6adb59a66b59723e72d0479f06accef5224b4b4c4644b4a72f9e5bef81a17adbf8df1258c17422c6250b3fdceb57cc48c7d0bae2dd7c7f08f981db7e0efda72cb47cc15ffc02d00d9b2e3eaea1cfbc9ab31df724dd44d926c9dc45300d4e2156052793890a016af8ce41e296da3d86da42407c21e434adefc7e52fb3d6a3d9eaf86e16e5283d8607eb95e9a4db7dedd43495e8d87414b5b1e8ef6a56e29e78441733e844d1e2492d4c4fcab35f677a9bb467fcfa2dbae1f3bd894e7d7e8734e89ddd50d5b35a16af235ae7367a8f98ba74ef26accc72de4d580613aa859449a3567c4108cc52dd4423b1ef8b2aa61eb1a890829ded8bc89e20d15a63c1cfe131fb96799a67d8f14b4bf6f841bb3a8f983f3c513065e8d2802d143280f4777d184b9fe750cebe8d1a5cbb9c26193d8bbc5620e4d730ccfd50d1bf6f69dc804ece7d469d6ec7c3bcb322d4737b9ce558fce01f58da900d00ba7cc0e29e1852125bc70b23786b3e941bdcca35ee62f8aeba2261a6243fd4ea33ce6515c0c16f3188a8b397d4c8e985f2dd94e3131b5c6d41a1373a5d65a85f1243513604f59116d1fe3bb466aa45763be369f729dd454f2fc1551ccd3ccf7b896790ff550b746a2505ff3ade5a87189c24b197e7f135b9830e2baa1e430c75018ea1bbb7ab564c31e85fdf4d477b7ba3a9fbaba32da4eff72d744a7ef26cdca5d84aa41a928ea9daba152c5f9c04cd4c91e39bfd50f7d8264ebeead96ab672e6ac68c9cc109fdcffb9e79e7503f433e9e4a67b8eebd0c6ec587f04f284761cfa75317390a8e7ae89f7e5c31776eb970d4528fe2fce9e9777ab564db3e068e799a266ad2456de3f4b5fdf4f79eba0db7e2df8fc1adf83d110381ab27db7b1d1de5ae914e1066fecbaf23a33cbfa7b48da4b6f15e26a0d24d6482137489b2050541f97e7fc9f7e91153b6349a09a8d7b49f4caf434ce69f0cdcc5fce9a966c2fd99bfd9ffc8783f45199cd0f3c8704cc66332f04f9019ff9a049791c998c1456919f7fe0c6ec9873bed5a8f7cf85e769e6330e536ce07fa04717d0ad36c3c2dc084f915e61592ad7b49f35748362618b4a2619bb3fff6927fd6b8a314b7e85ffad97739289cfefeca86ade56fbf73f5ca86ad7ea7758e5bfdf6edaf6cd81aff88f9be7dede757ada761c8d6629bb6e9b7368d399b1d32fe52865fbf555f56143aa05c2dc6bc5b1eb5b016f6f03b208abd6657bf5e9a6918b403993d1c10059b8b7fe56c9fddfde419b9fea7e58018bbdae793cf811c46a26d83597e37c5fcbe8e9f69adf74c8c437b0ad05e01a64f18bb78ab235b3bf7b211d8f124ffc1d9279ff3a7f3ffc93f3ae853fa154a5964eb49bbfde94b49fbdd3b4269d892dfeddd2da77dfb7766b0e5df505629a76c0cbb3e27f6946212ab10526bad9d724a39e59cd64efb6a9cd266c77cfa3a2650965cf7235ffbc975f2b387b1dec7dee2fbe2494a39a594725e30eec369af95ebb62d6755648b5d0e4eb3e2a77a8b1bbc317b6d63eb6e4b35cb4605736cd90f338e2318d9ba9c9cdf817df63a308c9ffe7571dd4f7e3ab0a755cbe12fc27882bfd3307b35fa258da0a577cb11e66a886d65c4da4c4afb735afb34cb6ecdb287c306fecac806532a4cbadfaff79e2807946d76d0afaf83e29f6efee929d7cdd75cfd93311df7f55c1b336f16cc0ef35d15d95ac5cdbf0eebcbafb8ca5f15d9e0c7df52aa8eb2fed364adb2be78f21fe1c62c9b4dff6993465767d830fbbb02ffee0340f93df6f2e77b9fce2df91277411ed775ce32eebd1ada67af71976bf9502030774132f5d67a3ac796124be9fe1176f36d7a6c9e4f8a9c1365dc61b339860de6e4986b063d0a5b67c487d67754209dfb1545093ed47a20ae3f19fe8596ab3fe3c3f8f05b3e9410be2779af2858b267785baece5e81c7a3fd098847c3a7b77e71ac8e75ccaf1fb59e5901209ed3d7087f7ec3f93e3b573fe9dcd5ae9ec07ba183ef50e38694c0731a520284d01f72113696e4be11b6ec3d3fafdf1d51880fa4638d7fed4fc7d7b8eea7ab1f6990248f90dfcb09a69c71966baefb69cd8cebe6539bd9ec7762362b00c483fafb73fa0cb3f66fcdd99fb82125f0a032ed8794c0837a0d855f784d4276aa9f71f4af95afb3c4f5b67f01654a0e40be0f366def05a5896c7f5657af8080b5e58a79beab73272950fff4159b307be65b57f7ca68b3df6fbf75e2a258209b739e66ccaaffd868b95aa3c076fadbefafbdc55dc55d0b3dcfa9f37c5fe8794ecf8013fe794d82fd9fd724f44e6c7facea98b8da9e59e41735ef6ebbc635ffb41c353b5dc3c5c0d65aa50cb323614c105d984d2dc7cc57d48feab17f6151a209268472fd9925c064fb33ef9a0e5b839f77db6b6f3f5eaf791a5f833bc7af92a141a3867b9906f7681e8fd37c6ba667e62bae3c3e009481f8af7fff1d77b6555383bb20b9e6699e137a9e8ff142cff31f03779e1fffa8f042101aff9a847fdbdf9dde9f866bf9449b9e8ee6511fffe51aee394b838b92fa199c4f4fb43270a7c2aece33ef1fe39fd633f3f63dfe59fd7f8a83da0582d87c94d4d3c03f374fe377220dee87c6ffc8f03c6f8107ca337f531c10dc3ff74290d5733868f5ff8f8544a979199e274a5da229c39cfa25b02885c93766290e283ff713a57e949a6fad27fbfa34b8d724d4703170e79806a6b82ec80c475f3e00b8176eb00ccfb3c251b27f3c325ccb27480ce7035bf25f46712dfbda9fb896862d76b500c2d8ec5b8cbe8dab338efe8b18e7b94edc8a5c172407a140357d42e2dafb5eb7f63bf186865ff55c7dcbc9b7d242c9c3e2167cf92e38e63a6802500602f517773fd9de1327b39d56ea92fd1246afa67c69b612e273ec9cd0808c55e3ca49ce2a885510dbc53e34f27d8873d8ef54f794ad9c6cfe53d356b0d8dda2326bbb4fa72c7b1a7cfb28ee8520da9f1ef536487b1416f29a84fec63b11e542eeeabbba831dc5fe6d99b57c5cfcc37eb7fdecaffddbae0dbebbb5d967288b3a3d1667632fb6d971efcd38aeb3d6c6d76e1b05c6bd3a3ab203ca7076733d364329a594f059cd8a37e7e39c73ceee7921a5d3a1fbdffb64163d5f1e10a7306ffdb621256dfaf1af7ad5ca0336f7ff53d5c7186d6efcc366f9f267c069a99df03dfbee6fa869f025d7f2f19c6829f18fe7c4509e3f5d1bfd0ce7fb001d888b5890837fbbbb330a33c86a568439f0ef85f0e7c39f0d21ec09e78577cef9b2e137d733e1ff80eff01f841016400a1804e5ced59425428e90f8f3654416f9e567040fe8c39e7e28a59474e21ffdf0e1ef44e9b959b70756dfb1ff9c74ce39ebb46eb1a9e580feaa7803f5f17bc41b28ac937dac19e7ee9e6537c31a70b7d6ba750d342b5a0c04ea5588375058e549564d3615dac6bd32831a8f895378e7ee67dbfc0cffe8972fb59fdcc6595b77dae79cb703caf3dacfdce74b29a59457ba6b3960c6ecdf19124b6bbf5acff2a1d623b1b514de77b526e1538a7f40f8d9d30cff804f5d5bbe2f5d050d3c0b2788c5079cd08066c567ad8ab64e47d331ff66599651aea39973737a8682c8135f30ce0533a642bce131a980c396fd8d8c7bcd5221def0f93bdda359b1334ea15991fe0f8a027d997d0f9dff63be5779a5a088dc30644833c9e978d96727b14b3ecc40240602736b05ffc73ee0439d66c5784347d3d9691b1a0a25a440e9c10274ef95022a596257cc0d436e081bc6f9837e6b85cc25b36b39686b857e1a39054f7202f27b49614a657301292d47e5917d8b0269c9a7f6775a6707facf0c37eebe85f9ae7b71fdace3f481b2e57c68643a250ab57dcca33e06076d74050c77d57fa763642b1d36fad0032df934f2671a59e21520377f46933fa7ea47ccad15e0bb6476aeb1e540ff4e4312ba28e291b0bd8ca0925f9effa4b55fbf87be0f0c03887fe3d50f0f7db07fd93a5ece31ed0780b2ab1f283be7432377ecfb17ff4491c0e3792148f6dadfd7705086fbef5fee85282bc82079a248ffcf73f3255cb59a33fef397114ff20f657f498192dddd8824529272d47a22fe91314a0e3e9d1f5b1525e5ffc30786d15c96f4e1c0f14884e76ae3633f8fc61dec17bb882644dc30b9968f7eec27867ff4cfd92f1fc33ffa31dc02902d4fecdaf27c0cd73641f670603257ee655a1fe618b7144c51ac924f2950ae95aa5c0dc4f652cdea0e28da8fc2027d774c880d665a5d064c4c6cc9d3723f2af0d88f1a953076b9b1ae82d87c686416e03fd931d71510186291f39f4de879b0ac0a3d0f261980e19ffa41e8bf26217e7c3c90f399cf23e62831fbc40e283b07e0eff4ebe745974a3aebf44c39cf928b1a0ff9d3bf9af0136b06b51c387e0b208c8d6290c21c38125d1b7772fefe978b15cfb7f1efc5260a93877ccff0e9bc9c67cbc17fda94f373d578cc87329b9dab472b618499b599b516dbd8f7585b5e4ceb99ef77c639fdb1ec6b6d69ef6a6d0255ede3f6773efc1fd457dc4d7ceba3b68dfe90127836066c753eea67fccaf13c1eeca4c598e7ac714698adabe5df17b53ed55ab1cad94c9b1df5b1d7513150a66e718fbf7c29f327eee28e7e899d587067d19366cd3f2aa2a94b046a1aff69f7058d524a8b5c1ddf7794365a1f988be843ad87626804a1bc1a33339482911d8487a46c9754d686603c76107df845193e7da8ed383dfc1b1f8df1e43d34dd85dc72c578b59f9b9d3efac0168add432e98e973184f9da4e98bcbbfaedcf291c97b371f196cd2de5fc3f08b8f98e943d811c61384d1f573e58fd0ff5c096b0375e781dc72793ec5fbfddacff88a3bef997f3fa4041e19df3fe331ed53a7b731c69f6978842112ceb49ec6aebe0f4cbcd148619a35936c1d6c12f27040193f1f8291c14130318459657048535aa86d44ec6b13fdcd71df725fb999785745023738e81c81040c33d28ad96ab12198f4a58b7a09ce482b46fb31c1a1a42f5dd4b3cb794c7028e94b8c54520887923c7a64824372c69cc734e5cb00db3b4bdc7d204be93743b67e297fbee7f539062d17b1638cd1e3081c4622ef45e8d2eb7b4d6f886cf03bcf31ce25904b7341849a57dc30c10196dcd52e6070a094d3945fb5c2195a3d8e4d8ffa2f53a08378ee6f35deb06f392132e8f0d417aa3cf762fb95f30002f2d376bc2df2c340d848e0f160fff3927840501f04980705fdcb1480360ff7c070ce913cdc0779b0bdd70d9a72575f5c08d5d8b0a11f754001dd10466fa1b3ab3310150748b9ab2f1c1cbd1b235b57c1e4be41506e0a74100fa671429587be7f9a090fc3f57762fcc9752ee4f914bf39670b48ccef67f2d3760c203f0c0408eaef340c0ae2a9bfd39106f73d1beeb115d0dfbdf75e0b30bff81e0605e587e31009dd064a7088f8041ab6547e2f1bc090776eb0a449144b9a34bda009ca960ed62d870d66aad590ad53a9562f3d621d2f6f5dbe74e1c1972e61c04c499a4225694a134a48d15dca39a99c93d25ab11869ad1866ed8d11b3f6de2cd36e9669dae9843a213b4da192fb5149a8294de4fe18a5dc3d44a4879a4837216a21a62d59306de9d284e5084229e208429982048dc2c0a03050080c0a03894021179ad5ddfeb6e43ee1c2ee18dd25ec18dda59cd4298d0da9cb3929ad15b36e6d6c6869c5306befcdeccd324d3b9d50da0985dab6981899199f99890d67646652291a9a18316a62bad701f3aa5991eb707478e43a1172fb07fa6fb22bddd84093fb25d1c3f18a0cd0839b22548a19bc63ccea0d11e918ac4de374293dc7ee7c53b479ee626e8a3698bb2cdf1cb9297233837b7dff34726e66d8e03f29b1f7629c5c979356ac7ee9a438c4e4472e27ad94c9e5a4330eb99cf24b27b9f42f1edf52078bfa2ddd086df05f8c31c60c54613de2de24d9e0c7bf415237fa1e11bf39523dc618a33bd7a5629cd94d911b251b7c6f9ac5f8374ab697dd107958ee7e72eda24b130ec02c2951a7ab9b1a36cfefd56494bb97d4f444c84d13af157269ea4184a61cbc3861a7595b30d5a08b262622c069aa61861b116c01668b2f4baf1b18c0d00103278b2c4480ca5ef7aa546943276704a9855296b2e684b13da50ee1e55a83a50ee1e5aee65a2997c91a60c952d6408b2ca59452e22d687b3c3ab4488a21bc1ce3e374085acca085909c24657a740f824e157729b8c8eeee3a5596c0c24b4ee5f7c2428b0f9ecca2a3c325d371f728c5d29542cbc4a253a54a2c9e801307078b294a30284395090d148b2344261633c83045dc0b19228e57781962e50a1c58b942cb9037151764516e17b9e288a9c7927429fdb5d1fc5e57ac90ae9861055c76e0022e4cf38a1cae201282714697ee984b1522b0e293cb8b4b1291ee1f49f5a5705f2b914eba8acaefa54398ad89298b2ab9ab4c4b55b6600a113b28bd7628c10e4af27be580650727f9bd7640ca9d2abf1d88d8e187eaba362e603a2ae24386fff0056230971862a565d502a620987c7859987680aa27c35d6dd9fa5fed5775c32d56596696559539c58b4907261b96240c2e542630b595028b8895e37fe0c545e8f95861c1c1b9e28eb392426715054e145164472a53675680b1828b0cdfdbc612a61ed04617805825298a27f24acb9c7345c4d6bd252b8c96c8c060336263c156c486e345051b125b12db9c1309cffc543f1c1687357ff5822d061629d8943625b6233619af29db121b13db2a695b51d9a4c75e6dd97aa4a07ad6cb8a2c59cbef65c50c6a0bac7001b4c268b3424815ab0053450daab092df4bcbfb2a8e50791548a0c4f1e29373820aa44a85d18b0a1ea820826df922c3bf760b16f07e0c603623086dcb90eab425484b98fc5e5ab4d0c29465bc54282d466c5a8a608c96212d43724d7eaf2c5fb23065a9228bd28c0ddb7b65419a7992df2b4b92dca9b2084d1126bfd7145d7257a7e032851256e43e9cef58dd08596701195bbd90d3a3fe4a6e36f76208902702ade1ae66d9ce65ff785ea5ef1cf6373563560d4d91441344812639fed0d10bcb122f2c4f32086720a1162d1bfcca037ff2e03e197ab17ab29abbfb751c16ab34668b3754acba6d745349bfa8f8cdda9e8d8eb0d2a02395318b62e77d62c3c01128605aa20417352811c1050852885831448612c0309344c66c025fe00431980009cb175e54700549ca133834e9620a2caadc20abe2c59d32a852455665e94e2388440c0605bc828446af145d39ca947ec9248d6e25c98a1250407a040ad6a77404891f522004982f5486b8924417466a9001d2152e4e57807080c9c41519c85ca1626b2962a049a1824c8a2856cea0d5287d829825a0981bbd62442608315e7cabe22ead5031ad6031dd0a1659450b7dc1c9ca0e327d626665c58a17546955e5b53a92d56954cd286690e79ccfcaa1dc6ae25fa1a0d62d8a18243515095daa56511081a2494e8aa1a708f98703e224b97b142bc81e038fe3aca4f87c45d143144d62701f8eccd812d3d2178a615cb9167e5e08d2c27b3576e24fceb2fb95f3a1f118e71373c645ae95619b1efff8b23ffee19fbd582b36eba41ef08fffc33f7e0ef7e1d6b6d1f51b6dadd8838adddec27d6d43423a5f4f95727e7d12bbfa075b0aaa11c39573c1afb67ac095e1db07d2ef42ae0fa4df62f6a5d61333f6d162b0d2989da76cc9199356296596c98eb4a99c5c6ea84ddcead77a1a051b1ac584d45acc525b1f06ff3ec6ddae7f1fac96cec0be852b20367881d88fefa21f718bf3a1d182d8d526e00a9f7ee76a960a7be8f053cd720cd76651efb0afb8eb7e72566d465bf3e8b6a1aa396cc5365dbffc29a7575ab18a598949ffd7aabd575a562f269ae8d7963a47df739c81bdb52ee39d50dbe14f31faf4a75bd8778c8735351ec3c688f1d662e7fc6b8c94fda024a8642d9755390a11d1000000001315002020100c078462c1603c0f5345f01d14000b84a2426a4c1809a35110c3200882186410318418620820c81864688e58014c9d86eaea6f4f248053d1fb8245b89d33e15084cf3c1eeaadf05010b383704649e71b90c796dc92efb8254514a8c067455744888bdaa3bd37a322e28ea89f0b741e63ccbdcee349a8e73bde10fd1022cd1b89dcf7f0bce83b03948b8ccb4b547b09d4690003bef8f9e9239fd9f1d2cab1f5f5c3df402617f43d5bc25e5205eafa7b04b3f97de4b9e391345f7e33a862e97faa05e867e511d1e87b5bbcce204ed5a90bbda25c6f7331389af213bc8df10346d1e868ad29c0b325b52f3f6735a899da43454ffe63e4c2938cce1687a8e3e9abd6c3f82bfdd37863fa8878288768f136843beb7ee6e40bab7fb49dba4780e1b923fad5fa644d0fc3b758d33fbcc01b7295ff58731a993780b81ac2edd9c4574ac6de073124fbb1a63995655404245114019d8b07043f14d06dc6f9a5c6d8aad7a877cd9ece57d1d069303ecf0e9e9bcb2e588ecbfdd1cf1da727f9b77c16306adcf5be8c2293b1b13a5b17d390824a8423738c53039b8b19d70cd74394870194c7f28450c9b482f8c495b0d96e808f7bcf1557ad97fb4a8562b37fba535b003b6f7df7c2ff7aabfad6a9462e262047920863acee8cf438896ef091c62e636c09579bcef1861b1132f4b1f9c9dc6cda1f799326763dbdccfa7fc29d07819b16d863b09c1081bc8a4c167619144174de372e8690b2868dfd404ac47b914840aca81d4a17b52c4b4c2d68c5f9286ef5b6ab33ffe03cdae5750ef7491fadde5c93bd36c32d5b79312e95dd76dab25d32f38964e7168d652189dd0761dadec633807e0a0c098dffe766c89539f1a391a35efaa44c609a41875370d1eede322e4bb0e4f987009c0f37dfac18e5002828b9b69a3a6c70d2d4015a351006525266d01e51aed5da2e16b9d3fcfb6f4382dd865065c7e7a039750e0637f9f5756c86fe547b5d8062b03da9155a5630b7bd848a88c9b4f158f43b71e9262759a3bc8eb3d86262dd5ad36f41164526c29681c7089e2852fcc3dc00d2c367cace2ab8c3001b6e9f171db5ca1a06d348e0200d8c524639fa1d58f44e62c82f6abff51141a6d87e66d6d130577d1aa4ea5f10fcf2f5a4108b7319d67bea8bfd68d5b3ae7b48929408196a5ff2b07b00eeb4af9dd274274af82b4278a5601303a8b0de5d3a764ef19b6409f9e00a4dc250e00dd64dc0dc8f3a5634928c7ad12574617da6841bba005b3697c7045aef2fb00c75c87b3fa4fd62a30f2ab029814980ef03b8ddc39992ed1aa871195a9b94667b94acc21a4bef3e8935ca11daa8e0e8a1e64beaf50a366a065de58a66dc0369af786753819c53510258bd190d7e4dee368aa2c6b78e918c278d399797848e3e897838241022c836ff606b26029b659385027778945ac8f30dede62545cbba1e3456a9f7052dff605c48fa963a9feeb3761da1afdef8765313212ee582ab4559f0bf33882b71f929c13c09bb80c4fc07ebab1df3981648195a2399e5e7c7b436936ce5a65e0b634eca332990347445b3d7f3a63508f3c4b1797a809e09654c95976b0437fb590faf4beb477748c20bd83905ab5f7b8d488653f73a47e1705db5e25e695d740e77641a78ebfec83c5b0b283483d4a4453eb4410c7b869ebfa5fb5d696157fa00ffab0ce3be3e063a10c599bf2861605baba4dea8b300b1c776aa51df322cc8252bca94bb759e2f92df918959126899613e1ed1889f7d5c548ffedcd265ed89553cdd3f1fbc42c4521cf52eaf3f60e26b9c730e4c5cf445cd44b4a8e72e16165e59b2122a4256b1aff1455fa19fc234f7a65ef1bcadcd3a1d7e5f0e7803a163eb2efab94ac93905a4029f8046f803fca279b298effe38a1069066b1704b6a4d02eb9bad84b3bb7efb521c6e1a5c1331ea898ba17583a4e6e85806bfd80cbe6dd1dae14663eff3c61eb80c506b0bf5c3a1cee970069ea8010e6cd0b4fc0662cc831b82b736b41ec314a33e12fd6221685a0f85bbf4405232a0905e1ef4d74e51b73c146ae30c6db08a1eae013a56683deacef2b85cda74155aa7a1e91b7cf69e1098be7071ecb2aa858263a5125e97c763a546945e547f147cdedc6c893be4737f3dfda5c45d1b05fc53cefcd26682cf547e5baa6bfb27f07e98ed40c754ee121ca555b12260c26c0515f25964865bcf0c60f32c0ec8c7609ce6658edb0d3064fb2c70a6b7824eddaa8f2bec45c121c95fc4ca6c818485f97024c4832d9a39904c63bda2a87ec4c20889296475ce113ce5a59cedbbcb9b1e8ae7644f8eab3c35091e1b58cc16de2312a5f8da0f697661dc41dd250e8c299997a8512790bb80b966f9aa7da81a9c8dbebaf3c127f127d5afc39d53f6f8ac084c7889971424c0ed68bb46d55b7b12c71e27306e30f17a036dd1cc197dfbb726800963e225861b3fb528008db98e26cbc42b0bd471abf88e0273734da189b79ca33d8a4ff4bdd020503c1c2576fe54c260a694f2488d255fcf2e98e49481ae17f9fab6c83327c1e83898e50ba7a54994dc1bcdccf457b01a24a94f7ddfba20b0e64274774f528d1d206b234576d1f0116c179e8c5bb542bf2e61d2999f8fd2d72f65e87e946a5ede3a553859cee6253f3f8620438a88e9e03e2fb380c06741a0effffa3d0a5bfef10558e0d48dc31b08f85dde63a53fd726e74dc54aa8b334db16c51741a9d2a7db4cfb27c5b63f0411f23dd3e6be961ad5e90c2a6706f8944690507a76497f0fc9c602f4d50458c5c68f86f0e5e5b4603e3059362f5998a051ead92030b88519755181ad37cb7def22825764eaadbe9bbefa23b80b7e1a08ece5034839e11ee03c88dd8c89d167156d10ccf741638cef71daa4656d51269418a68ef1c837f205da4aa4bdeb2b646b78a93e1083a376e8e44264a211a896337277431593c496769ad1bf09940f0e1b14fac64b82ce28f85a4d8a7219881ad0c01079e8028f40be4a24ab280914fe7c812ed98de4063328d10859a3cca2dd82997c6551864f8e2c095852fde6205988d620ec238a61d3d681ca095ecb3a2bc580768a8c62cdf1321458c419f71f4de1ed67416f68347218bd2c93e9d01ea74b36491ac67ac4cf7987feb2114d735121a2978889369386062acea490453893901f29a1c4b8be1b46c383a527f07b5410ddba83fa200a7c512a0602253212fa4eeaa98b61501585d440eb0aabc56b0003df10209818f422debc09bba0909f18c654101cb6c32a21971b225583e2d1a81e4d54d91bde51af5fd7561db14fde5c5d47e27592d24b26aa886d88e17745b62d6c1e9162bb59920c29accca6048b55e4ae21b2bddc988cd8f466d33c4941891cd25904691ed13a12c1064a01bfdd94e1fe987e7995c94d942158ff384e358a6e4f75affc65c034f499e7f7785f86c701b31c77b9a6bb969563b8def6dd2dc1eaea049969438e2e50360492504442a0a866ab2df5df079aca7621f2280ee1947f8f53d627fa902302c7f09c99759d305649c56b97b1fe7d4451bdd687d1c640a451c6e4ac45d66427ac3d814f88a620f181fc46d2320bf75a3bb01c122d8fb86f6273f28539215614d3291ae7d4606eb16f75ac195b18ed6d03313e93f12f4c30b59d6209e7ee2bdae43509a84c5a49a08909abb27bb04f815e05da9d11a00113b0fab4619c0c1ae59af34e9f38ad52d6df7957cb88e4610de616415da3d359639665134e7cc118489709cfc08c6224ac7b2a50a29fee533c46f5ad47250a628f1d118b48f0e101d390098c94be5533137e2e28eaed3aaa2e23aee1ae1029cdbe0b909e07d2ca7bb8095d7688a155b2b18911f0d07eb7f0af35b94a9fe560461ef4ec1e56bcbdb85ce1e78322d70932e804c76cd0752e81c202ecc1fc7f1b2b3a9c634c1d481d38105e6c24f1a68ee3ce5e06e7d53ab65d0d2ff3f6b47fab9a48b4813f4ac82852557927afc2e2d722f648530245de931fabf1c25f24709f3e61a6938f58bed9990809f509c35f7895b53edbc054167c4657f441420d822d1eb68d54f5f9593559acade2d0c7551dfef859a077f8513ed08f2e3b0d806471e99f76e7985bacd28e6aeca128e3e21abff06674ea18d0b54a4a0aa873a2f6e835c5a6d51162e486a5ea1ec955fe959600418e55db02e6857da65fb9fa46cfe84cd1760f3fd5772fbadf1c4826f247c4f73e8d09772f51ba7a276be4451718ca4221d071971e73150ddeb435e7a2c04e3420297157573e56917d0ff8c8b0cf797c809ef7ff1e94c6a1769103e3e8218075c22683a726fc863f387c142ee8ac7015632c1d15da684d86a71edefa6e09afa769f2e24080ddcce57e68dcd1e264baf98b2802032bb52d3e60980a0fbc7c0463eef5508ff82e8c49bf4b5cc82df5f5f28d01d4240c97002e1aa2774c1287c1ae9a89a18cf4895cbf8a214797dda49c0ec46db4d598c713b70ec40fa1ef17be65f068103b164e02176ca15d2d13f9d0d9ea836e32b529c5231ee6c0962b9799025ff8629494312b2cdc105909d005f32022fa9fb521123821f78daba9953930151ee1153bb93546012d21825a8eb103ee6a8683579827346cb73107de74514a8c11383c2b33a085b7918ac849aecb984cfafbf942daf00ed5e2a71a2f26ca61472b52788822bc914ebfbafe0f412d0ec2d6a3fff342c8bb75d01e21ef3cc00cb371f00db33307566f73980063afa0bb5098a1ad606c4bec64537a64cdb345d502b4bcdda54640daa6f636350e47137870caf0a56459a9fbf83e86203e9f6eb104cd81db3e869e61ae12da21684dc4044021f6726aef315c7ab7971e2ecadb3b12a762d60c83a8acf4f4faf284222e90823a8ad42c43a0531312b18c119850fc2792b4847fef39dd89a1277a5a8f1942712afdd76efe5b713ba3768b1415bc2c4a3d68abd1d4367328b1cc0e70646366c7fd5888d8014e8e028d45f0f8589abf0057c3aa038e622cdcb65f02f2f56e87660f916bc376dbeaa2b3e60d5c1bd15613590b102580cd5ce125b7c5c9274bb06db9286f236866e4f1af96461c734312a5d5b95d45301d49145339b7b5c966a1220638198e171b207fa96c67401f8742f41d80bab71511a5c0005b18f2e5380428c135e0e639669af965bbab88dc241de4b666d8360e26ad16f4bc1f12ebb80dd35779fb172c65079aec062a84426bba7039f0899502ab5604b0a880a2f8417852a1462026631127d2e6371e3dc193261707d7ea5756e36236b13dc2e996f65001135a7bec03a1caf17b4e6bbcb156b5f4a1a61351977566b5968a8d235b5f0a24804d1e39a1a3856428db49b284d5481f056dde776ac820a85c3a7ec194cf7348f66d23e6170741dac42813fb337a4d1717dfdeeef8676965c37aa90d4b443c600c2d4d79f101445fb6d772209eb0cecaedef3287cd697d931141454b3bbe9385046c43cc8a8024556327b9ab2be2d7424125e73359ab19bec2df3dcf853f48e0cbd97caaebe99770fcebf9b92687efdc6f067dbf04a03e404820212665422e3bd01044a38666473864b5becd47f6289b4c2b1e8e3e12e79bdb19cb65b78e46cfbb69bca145cf9dc0200f8261d61664255945957fa5a5fbc0497bf025326e0ed3af8913946eae0a7bf5e5a8ef102b76a1d9b341474c43f880317c524abb311768cc03e592beda8a20c147309929e8dfd505af08ca3aa31cb87cb40e19dc7812a07964e0f40be6abbd2284aef8ded74739aae927f89444a3419b812276c850d72fcb04c81828025a33050ab5d9be77dcaf86257aa614a09e46a1a617d2ae407fcb9450e2b565ccd8cccf46b7c4aefd6bb932f371616c856747583e4ca4c612f8bd69ac40fed2cf060b2e92730deb334bad6a7cff42d3bcf5b4b70897624450a5ce26fa57ccd5324a9bda298eb81d8672d6ff453aaddce0fa9c2e2de789cd4d55ae5dc253b7df7b6df3b6d0b95ea00afc15e29a6df6f47dc0ad1a7ce0ffa6f11523e936e8df8db0ec865da2a33a5679d9b131eb40e06fbff4b3bfb68f6d396ee06ec5d016370b1b132eefe228f45ad409a97f13839adebb2c86a68d8d476ccf960e17936369aab90c65be3612dc8ceea6a0cdfa211176ce0fade04665aebed2385b0e28f7b7cc4f0dfe92475f450275de64283b7b9bcdcc4f7626f878ac8b0d034461f3d81c42f6f1706d54cdef23d0a883e8d9b90628b3dabf35cf44129518d524dcd012bcada4f623da293894e5cbc97e9bf59f59271abdca33ad7430359b1432b067e6ca437419c55eb186025916df6ed5506422d0d29d6710e62310ecc139c1e71a56c077284767ccfc8c440295a8233c25ade643d7d41da29fdb7e177e9553d735a7b8b9cf8759c2c970e1afd41f9a9cca7b0849d5f2732262f56eaba244b8c1c6371326567f6b2e32d8602f1f7046f2591413aa94f839ebdefbab6157402fe53ddca2a3ce4bad437e9035300f49abce81c332c241ea90e12b7974405a1d8de5f1b352574afabf21a187093524327c9870523ac68b0d4ab45683c3fd224bd7d2fc8fe4e02dbb300cf01c384bd47371d6d242600b74788046ee3bf1eaf24b159a13887bb1360385fe831e51d5984e0ecc7ebb78e1444abddb7a0bfacbd7b4f22d704dd35afa23d000d6474e8dfdba9605a45d70af04610b42d8ed9fe75608c37448489e8460a4d05d6d2590947cfe7cf0257ae41277f2d245307cd083eaf9b90f9f8c95b992b6907892504a1e57e0a538edf4e9e5a878e284405fe77328e4ae64c1ca7612a9be632ccaf1f9c5233f7cb04db47b7e041173a2efca3dd3f11dfea45eaf2f79c2cf08457f73544f85482511caceb15339c0adfac2c987134909b93385891c4fbbed82eff6ac214bab1967a58e9b965be2918179b0c396e565ee839015eeed1e43cb91939f4185ce06c80e4033405708d41237393ef36fd278f9599950e866192c2ef9f18aabd1aee582ceb3acc68e609c02100eff8b0515fe22eb86270dbad1cce444cde381c22f2c047ee62ee5dbe72e047af55c25938588017432387ea861184bb5814e6f458df29fd402c419ccab9f01d40808525ce8bdad0589270fcc85524951667e04bbd7c5e786596c8e362b1eb106e3068431996507429cfdcb145170fa7da0c4135a0d207c9fee087d897f785455d21424d3e88938e8934201344b2d806b05fe2b4f9b8c837a3874b681bd5f093419fcfbda4eae1729748a96fd60a94864f1322edf9c9ef9c976298da80b1a78fa6108a921864f250e461666d62b49f50a0d56cdc5f01087339530a2f0f582a0b4424b405502b0684e2c15eea47eefd32eed4ed8f0fb01a11cd8fb5d1121338f38e405caab46a145cd2d71733782d4a4369dd1f87dc1d3d1a5fd063ee084867943c4f3de28a779618545fb3b32dc5b9a1b2babad2485079a88ca9098076b59fce45708e7acdebcb452d32566a79b08de27eeaaa64102547c22203384a3a9dd3f452b58f08952194438e2b10331257d7a792d0cf690da619e2f8750d0fce846c10562163d721779c1520daf3484a47d22e693016489f20175c05f73c98d7fca7ed9b144323c092023e1673796aec2925330a149dd976d5340ab79be071888cfc76a7a98cdfc7d907c861e7152753f989310c1c4e36245d63116576f12d59dc678cc08a11a6721c38094617fed583f275711b95769ace45382a867b36e65d083958f3d5863f5a693d33fa92ffd073d85224e44e53c61062e979a0f728a3b63c41b4701a7297838238004569fd7604cd231ebd42b4da53a56aedb7283d3ce53b5eac8e329118552135ab2facb8dd46dbff38b45d922a5125710a6b1263f5c0be589cdefe7e275a5b1eb951d889bff6b28c87f80d39014efd708c027a3d67e00352b844b567155df6c6f7a026b801a52d5a00bf47501d11d8d8b6438af570e97394b7318b45479d5aa1305969517e054ed78b78864744fbeaa3db1ed412ae855e4a42884dbe1d722109c3fa918e02a96d84a4e8e0bcbee40f1e8fe7260ca1237a0f19e074028d86e934b8b7c66474d1739c5c089a07153772273c6a48f2b4156cde934b6daf1f6b2c3f927683c29819182411661dbae31371c6bffe70a8883a8408e859d32a4b15e5eadd69d98364c2f3c07b55017187c51e37927d2e06cdec75597df5df883d585b8911567e3af4865682694478fcc9cce9ff35a5594a1f81e6d0c211231ec10d0e9ebf3823359ed7b0f68915df9a8e691077ec1db3ed92fba761e1fccc6fd68a0dde8bb0f8749876339232626a5531a9fa67d797adc986027fc8d9cfe3ac238330fdd5a87cf16d5eadd99346d0012aaa7a90c38dd08eada1803a343eefa95e6f3123ec01d52c1eea88e6bbb0bd1fae3d3bb17f5b3cb035267c81d4b226326bba37819ea47a06856f29d16415451d1b0034b92aa8c61810aa0d20e1502321244f524a323d960da65662fed51f51188e7cfc0d4151aa1807be02ae81e802e575bc03012b2fc77e8f720ffe144ff6d4161d42ae4a23199ea9c9f80426c627d58ee1e9bc2f1eaf8af4a9d1812e0ba8cd3e90d683c89f21c517d85c9a9d8f766dfdf10b6016e495b765ac799f20e728088216fb089d965175e541d84c874415af66c52646bee439859e36a91957fd4cfc767ef18e3e6a820a80703c453ce2c7f0d14052bf1ace016a2aea63e9ac9b2b0b57dfb40ca78ae4037de35db734d718afa21a46bed07544c50ab2b79dfa98a734f0dee049ee6fad1acbdcc2fdbd42fa68ca2d89139aaedb7a464accd7bc2ba1afc857883fcc01c6a6f321e611ad8eb281fdd2717bb728187effc418c050d621f593ce2a436a7f1e764f9b51e34279486b672990b4f2b14f8c658e88edfecce2e43a727ebef949bb9cc5efb58d83fb95a0a0b2b1503fe4a1c8f461a951f872f650b710135aa04f9925179ce308f55a8e53474d6aae24049e9282422d6ae0a67fb5d840b625b9f502cbbc9b8fd822e22a97da35fd28bb6303f47433f27036feeab497636d91e83e24623258cbe035ac0ff50fc1eb717e3d162e52c0b8c7b754212faad24c60c580e6d0d211911619ab78bef9a916400a12239a1a018cda99120afa18e84b84c3448254e854a76f8e44509f09cb23066c16ad1c5aeaa6ef8cc7f2e598169f2956b4f37cea54e195e152ce7afeb3ac001b69bac20cc72f1efc69f919753fee9da24e764c848396e5b58e548f1c9969b948711b45d654d41c418653a82465384b183b8ba9e53bd737ab71c0aebd58ef16b09758b1d6bfdfcd3b6ce4e2be9b614705a268d13bf19897c3d624e4cbfb304b1c45f24f1112ed0cc1b14c83999f697bc68dc8555472f26061b3b6cfba3d64e4db2c7f86051db15abbf93915f52a65ff457c2cc67c09a2edb39e9089a574eeba60db1c5534b5442bd402130a6e458c34dfcb9ad44b2dbc29459c4e3168ccf27f1316bc54d230f3efe10ffd4f3a4156d6fc10502cc769d02c0e423367d4d93026cef06b9121548025b7037bab94e9a0e36caf297f71ef7e3aa140362a55ca3b4851744b5ec69c1aae0baa0356e271e1e92975020c5b1f0f526cee6b83c46b155be9a84d666f0efafd5e65ec684e4e2f76b1a3e1a6e3032ce349d5c725102192922298f26d798b5a31d2dde6ff0799d5f0b8f1755514ec284f584b24af99803f15595213760dbe9691470ae3015de01ba383bbde01af4b564b27d5e92bb61888bacb689fcce100c40b3b40e062f5d0db64adc37f31025cd5bce903676bf6e3e3664bf034bf2b543db515637bec7894d4fc0324f07f4c5c137f4719bb61b02b98fdd25f00e2ca124a93f433afc9384893baeece8d1a2b6ba069e097234df4647f04c4da5f63a8c96004841fb520ef1cbf4c47bcdcb6afaff993b4dfb24eb84d240c841262a905f0bb0d14d9f9a3c52217dcb4b854ce044eeaa2ec0904b91b21558310557bb0f70b022400b9d3653141ad4377b29d695f11da8c4377fdd11cd573ec35ecaa4a3157a73d8bfd510123c23e87e82417d777402ddb350a00d6a0966ac254903aa7e3dd3d09ce484ae61f8ac7fef633752120741d248dbed6e4b84e107e2749691a972faceb4ed13a9c9821198431b7d03394cbdb1bc0721ec76cd6a2d8a65694a375c49eeaabcdf98a36d333bf463d24c6c96ceaab0b1ea9cdd083579c1ce1c35512b7ed5744c06bf40d3e74b8aa2e2e4db9f21c70875a93bdea571dace915efa7ca78afd930cdafb0664b079a5cf28345cb47d8bdca2bd09014c15a5650ad2a36ddf99a8182e254b1b1d4fea7f93250fd19b20a88c705eb49a56346c7da8bcd1fae4c6b92633e64bdea6eac4023223b623e7d3a15a9df3a36124b286932e09cb0146d4ccfe908449c81ab5e34209966abd1d0040399051014e49bb655a8be37425ec050b81ff2b982568aa4f36bca25420b64439dab7797622f464f6672a5063751cf17b218f32ef62bf9ae309e428a7227d43a160b75fd2659c846e221447da9594d4af01bfa15f4be357bc009ad9e23c585a9fa7186d8c6be0d254b0e9525f8b05d847b12cfe7af27b7f0a9689109c6393041886ee2108a16876cd7e638dcdbb39daefc3525d10145aeb8694a57c0d7bf5f98fd59d059ca110db63dd2ff650a4ea2d226a4940d7d137effd55a35f5a4bd021b90a47caeb64b0257229802d8f10be0ca5d9a0553c15ecb5c0007c6a5776942deeb3e379bd343cc28503a34aa664aa94a30dd6cf813434cc5f640c698a43f7c4fe89b6bd3e016636d60c5f34cce4d4ebeafbe5cba472c3868e1c468eed995938d3b297bd173bc449b4efc770d997bfaf3a1b81e57915f4594ffa2b84eb4efea93aad54965b5048cf3e4bb9fc28c930f3e4e454f59bec6335c002d69797f111f874cfca8aad4a2a8c81445a40e3d234591001a2d34b9f2d8004c6dda368532b73aa91411238b584fb46ef3c16a65e27d684a2caef932e6aaaa8b64bf9c26d3b366856fcec20d9793a8e8a651337bd228074f448ab246bea463b67e18ff2a1235c0bed14180d30938c1047d3d0b4cdf1064b1c188a081a3ba2f3313ea83214169b64662de94fec2e8510f41d597371263ce93d7ca963bc5de520c0df127cb818c127f208b452f7c92de57a9d2e950803a309c99a0679d37d20c9d26d2f6bc65632ed27c5f0c64269c0104b91ef4b1432c927d894a5891e34e98c544afa4fb24ce951bad20c337a133ccc3af8669806beec057f49bbdb01dc088f6efbd9f48d1c75cdc5c8d684f0f7cac3f0ae3e4642286fd34d22b28b602a8ef90d172f9bb1f1814fb172b6701664b4e28cc6894877bc406d0ce786a704cd21bb9a23ea06968c405f075abbc0ca7f8cfe1da7e0c536dd55e5019fe2eca6f3a056d6446406753ccc9235e02cc4392754bb5936fe436c401fbda039d4e824de9e7abb73b6a9e1704557418c10c67812e5168f17a6ba59093de4da633de83f6c0f7bbbc80661489f05756c9028559a7acc0d03340894fa8586a9027b8aeddc99a70f2fbd64a16c7826c5902e0d9c4855ab72f22f6f6703d0bfc81388aafdd86890947d89b80fcc1d74773340ae18188845d9cc7e6936484986d989ed0a2817565280c7714ec17b67786ce1864e67baf8a47af30c84680f1f08c3860280d581385b8f931546ad7dfcfece08ad65d7e304ec162cfd1e41238a2c952fa92ee01a3fa5df724999c7404dbf26c604ed4c4114494badeed0bb590ec0460a12ef5c120093fae21a45f935aaee0672a3d35c5194f70247d90a9422214ea33e7b3bc921dbe860157dc39bedd19695db8fe129026dc21b27f2c07bf64e7a54e9682a8704f9c2963a5cf9825c542c685d3b0a4845118aa098d481166c19a19021b249c3e9fa553c7ae1843d49be208062b34171e1543c17c01c4a719b38eedfa74f7bf9d6eab5d58afba9984fa874ed7429599aae00f64be14bb5bdc9f4ab10c0f5828af649a9d29d00a22970ef4ed6b61a4739ba4f9ef5d01446115b067f2f8c0e42f1a301153842a5c8c51b2010e0ee99af43cbfc8ef086c6c406ce689c37e5a04fd260b316aa147006a9b0dfed22bfd61ea0149c8c51022872951245403649262680cf9ef0ef388e1d5fb9d8c3d8760967e881c03c8f20cb616da0ccc9db642502021ac8200f8ed0f4167e4d1147471cea15ecc85a8bb63abae94985a9b9cde8fda4a7cacfa373403d559f47045920f55bd341d3ab2b4858fe8ba8347afe10085a35de2a37bc21171938f59555254cccf1a52ff2b15040dca6cb8b924046f9a7b6ce015aa37295891ada34812e34265ed209a6631a5926ebfe03242249c5bcd2187e0a43a4e878510324add211582a1262cdec20b7ec47fd27e91892212f912f070884fc4ed4060dbe5c2a9caf3d93ea97bd10d5d986d0c8bc3ad8d2a1350858d79c1482c8949a9f9cebd8311381ff587b6d12dae89ddec17a7173e4a5cd3479280e9fc90072f23c78db2637207d0a17d1ad428e08d43603428b88004699c5741c120936981d7194dc44ab940a49cb9719457687bf123d4f127cec756d7c8184fe0834629cd7d7c646ae38d75d178ee7e854961ef3e39d3453bc00de454c6334fef88259f31e51903fb75430d0d7a8c1610dc9c239dc6d80e7f731b333c17f9104accec3185e845ec3acdc399cffc714de593b9a38010bd4ece5703b4077695258234f67beffd917ae9b3c0a2ebc5d59d6fda2cfb18a6979b8b5dd4407e2f2f52a784375d76d01ad86909bd46fdef81931746b215c6f5f0423c3c4c4c5a66a0186a28ab3ed4a25d89aff0598578d9dfb9fac4b2d2f38bd49b08302b39a651d9f73a6068126dc22ebbbd1f3544e62e78cd4549254ebd0941c76c38e26070ad090b96c2e9edbff3993ad30050829aef8cb05b3f4c89d1b50fa0e7884ea661882b423239a94f121cda7d25ea2073441fed0edb9be9761b185e973c7c95b034298b20299b4583b36fbc4d02c967ac7bbfdad1c7776eddae754b1574b08183b952ac95e078bf079ec30b5ba754e848bb5fb0f49f53253d57380454c4c323129c4adbe2bf8b388298ecc0b2485935b1d8584258e45291415437d91417265ccce5e3579d083d3de27c9ec657e515829092189856c191203b2217598b4e1880f0579121ca8b4e1bec74ba8cc8b65ed8a7534b34d557aeb66ccc1532a28d9b3039d98cf116a6507ded46851a5ca5c7478b1f7766c141319dcc463acde57601956d176c10d2b88e12457dbba54e09e4be40df9d5a2048da666b70fe80502627a67f5a177ac8436be9c72571e0d22300041d7a4acee6547bebfd8a81b4c69b26fd4a660d4b83eaa6040390e25af09f4c283a9c8b46f1da063e6597f1cf3814f68ca1444d446960e7fb136adf1e8e36193d545c0d6870e5ea7425c84928fa6e3373ed59ebab594de6624c88e6815a8af774786de72ab04b8aff19538fb437be742751503b2089fc19a814f87565fce0ea89b4465d609adda4972871feda51f088f8d2793f3f520fbffe8b332f8f021a022b18108ab92b48a1258c7630520e6a3e9b17d60a4eb62c0b4fdcd235a7360f1e20ccdb8340e9b7d4602802639d9ef1acd930aa7a727cbac2c76632a65449b144f9da6259525d5367643c1bffa1aacdab684fa16494c9886036434ccbbfcc6fb933a7deeb0ebd24173b9b2bc2dc1d2a0805bac5aa6a0769fc70c03e66d90e3a60cbe66e51e32963d1a0bf2fe272e63d677855bddf681cea6aa4d2be03c88db26a78e707da5163b1ee0e2ab1fca5459dcc301d0873188f3163d15933a5a4c98aff0b174c7971187ed6559e56ae018c85e50e4ea960c5138bde668968e82a28e2aba778d63496642e46ce1009014c90a27328594a8474d728cd779d7819f53e44e86e8b0228c96fed5010d119789d7c0c58473636e61fb5cc3b3671e49d6dbd86e5eef33e48a521c4fa71cb08b9a86865a793edfd23b1124a6b29a23d151db10b912c0a668b76ae5c6ba6278034c37d11e404029e999929300a9a6339aff24740de8b74166d42eb4ac00b12f1c247ce3d40118a90f3424aba4a39f280a7e7e7b03d01e2e3f4022099a8b2ab90cfb5c823df5c1613df373f3ee529a666a8c185ecf61b8b7c78120aea60a5cc4e923dd89a6304e6c58dd82f5c4b6505940256bb5430406f93a4d54b668341f2ce46feb6ea33675bdb30413daca53c43397064e41b7e967d2bcf56afd892e20a387063e2e86cc2989a009a923d347a85ab2a72235d26c8fe54f8fcc9771f189da1833b73b6d384e56d6cac201f293c4ecf27b7f170bd3bbf5192891955baa51549df4f3cb5a078b3d33ea38094311a7982cb3caebf8e90a77d3259c8e2c52811b40fedceafe5cb5f8546ace2b78d84d215345ce9c96f0f0b326939be42fe366463566b294cd95633f1a24ac5ef2f7a774a03424f74c7ff41937080a9ca1902cb73afeab0e318cc4d755d38fe206a2772caed2b9aaf45765a196af3c8de13313587619adcbd8307a2fa323ac39fe72bb1e458b2d989a24eb156582a27cf95a34686a4114adc9d80cfb0a8b45cd7b19ac2935200b85901119d38b927368ccc00bb7e249cd0aa6382f3e9f4f95578b0cc46b1145be48a1a912dc48beb1196cb64e71f5519cc56d6c79c6212a9bb4c7bc2ec586e97681a1c9711aee96ef7c63b13f08469546f7d7edb6dd2cb5fc6ca153bf5fa467dcc1412abd4adbe5ab43733bcb3ab0acee31d9405c2dc2783ad6798eac6ff53c7c7b3d050be1e66ef3d0b6f1293cb74aa0f7a3ff255188f5913541c5c4beda707f2181994445aef540e50f5cd193b3b8a75188651647c0429b3849d937f42d8b8dc9990f2b61492ac036d23fad3d08be072bb6a5fe2193acf6cfb1efef8b897c81c6ab9b9ea713526ec98e9268c947077c0cc7d43c9fe7c9279e33c3f615fdc1bf6474134909a407f9fedc9a1475203b9a17bcda71d7ad94102901a92b12214d13ed3df2c9efb9546dc5f9d8e90c80494fdaaa43efd76c6e0fa6a9fe135f79ef62a4b429cbbd599f44e26523e699c1506ca3ab66122807f889afc60c7cc58045b2368df02e0f981b5bf17e580c28a77e80724fc500b24738a356e7b04d5a384f7fb24616a4fd61d4b0b51b815183ae82d228d350a0125eeacdf8cd30225eff8dae80ea1dbc624f45879a905258caae84a5980e6ec9a01e0cf940a6b23816e810ab7bb3be2e5811d8fe7f70e1359666953560697888300bd1e53a0ff360486733c9ed7e0f838399533277f9a0ddaa13a22042240d5ea71d4932344fc1ede82b6c0cf0d4bc2166f10a3a425b6df20d32616312fa3ffc437b9516157c49bd06d79fb8654f5b1667dcad3485154def4676fa3f38b464a327b4da7f48775bdb52bb9dcf7547965fc725392b09e0a3ff62f675a6b63c9767f0d039e4ca0c598c1efe53915f16c1444f2cab686117481d99f034ddcf367303bd98d1a957343904eb5b3659462f837e3c1030e0984d6a39138df5545f12288a5f91c9f1acf19ec72c14aeae8f13b689e6e180ab5eef8ee7c98fd5a24ed424ed19745c53349d3c877582c851a5575856d8c79b4077ce3e2be11c4a5281d57197f2b0a52ef87f64657253bbc366c0244862dde55ce4d751a05f5bc22553e9dc47776d453304bcc77d6c84bd229277e3dd1afa4244bcaa13c2583c83761781d9f9bc433d733465d7e5af99cc6bacfdd51acc6d5a8e8e1d8e7c3c77f1708ed9419ce50401537163bd678bdea89fb6caa953bda99bd784948a8f1497900674593f35c48a4b38634f6f210eb33af7895cff75b77b0024279cca825c69a237c96e99c431b52eac99732ebbdd110af30f3515d373e479489a0b043692f07a27a6e41f9a82e38fb04c99b13018ca4bdb0395c40d87235fc0fbf966c1ff74efd3d01c22daa816eac6872822156588bd36cc9914cb472cc4c4394004edc2383b696e2f6c8106426a30cc3ae46f45693708d35e9bb9879afd692aae70e0b08fcb954da3debf74cc392027a5593aecbd93c55ad9b73c4e5540eb47bece2d4fc4ade9086c07a90cd1e8914b011ac6fe7fad1cf6db74f1567a93d83c339484aa129ee7188f733f51a595f489928b26dcdf91cb8c149377c01880e7cb9300785e48a91f798151b21b5429a2f47af3a434af79b59445b9ae0d641c0decb5c634e1bd0495c59912e45d9e7e91c592f5d6b4badae8a244522076fc151a9e2b125d64ec6f52304ac69db4c6cee45fb453ca9066ab5e3bf87207881999a224eda933fd3feb2577ac03eed8ce2c9dd083ea3138129c62ef2add2d33787450973b62811c1f153346207f213d19fce53f02a3cd7788d495183983c9745cac3ea33f5e84f6fc3dd3ea9e23d088965ededd10b6a7079449e378ccfb1c648076c71bcd17e6d7282a99d5a1b4dfb721e20aa1f455a427e296cacf73b3f132148f3ac0437373c7252eee5d4301ea9a4a76043894d9840ee93e493ce207c0e1ed69e19fc9e3b4139f58342b7a9c209393d67a690f46db948754f51b180f8e5a61459e36251774eab6bb7c252b0c25d57e0add84aebc4288f42a40e9b83fd563dca4605879ea841095e7dc5eae72841b693a8785458097d210487d3c8e52467a0e5350ba8e59ace5582925e99e3bd37ffef517e8918a77605fab639fde6812a1608a1a6a3e7855a459ee512dd216879828800fd190a918918357b8f74927c6c76074d886a30261b8872b476305d9f8c284e29670905036aa7ae08f25cb3b1b842cc5ab449fdab083084ab0bf344b53869caee0b0b608470aeb34322b97ffdaed5bc80439eee29bd3786938f079d2b56658e50b72215621462135861cdcffb62ffcca141dd9d9cc708aef81d64ddcaedd516349fff422df640590a15834678982b98fda6e4cb0bc4a4bd2522cd770bc1a042f9d8b66ef02c9f94f9fe3bb8f62aeafc1197b2813b694a942c127663051e41488918abc180add07728c835317db35c3d423fddcc39ccaaf648ddf350a4c5dad0b5d49be481ccbc3deb61c8048144dcd487a12c6b7c5e1239f4e180e9ed850c297628d04c07a5f4680351e21bb7e66351b985d2b6ebe36d65bf9093214441f92a0a029af5d41b506ee25958385e7039b2e46d515a9712ab42c78efd646316d3e5848c2b1c95b02ccb1bc7b09fd80965da5d88021caa496eb16a280ff85753a3407b7ca0dfe7e960332d6688b8b559692c1ccc5d04cb0dc32bbef8c692a1afd678ac15be540e2f2a1cb7c27bc8ae0962ddd84cd8ff32f1b0a5115012e03f69a21ffe04042d79a9d11430451e6a9cefa951f11f8660b81a624c59431bb9cc0775da6602ef9ab7e9cc0b56d5bf60841f3796d3e54fd7a0042d8c467e8a9cd58096434abf21972480a8ce56873dc22e211be59bc7760428510c14c75190331e1ce329131177a854b86ecfda04a1783b2b7d031d0ea5b1266871d37248dc967ae7c00deb0943c88f86ca5deaba619d8265ae3b444af1fa836b39b8eab2e600af4e0602c638ab18b9e9cd9111831a709e19e0947942cd3f44945c8913c6f31817badbeefe39ac8ba1770078833b309a505d35d44f6d9de70beb71e30960cabc11ea6d38eae1f99ec37d40fc44f4bf2a11e46bb1a8f10bf596e53bbbc9f068656bb22d4b29c323e99a57dcc0eb9d6f4db2494ea8f6fb44ffd2c292317f9c402d73cd48274b2d987417d22523ef4d02765f3a960b331def1bfde272414d0f27952b1456446e0ed3f9fdae4da13939d5c00a5fbc6b11f15737b53761b809635a24dd69e8118b1fc9e0a0afcca26def286848448a2c87b65839ae7f76e12eeb8103bd7db7c09d9557acffc315fe04b114b13994fceed3d3618741f15ff5f184ca679d8c65282393008fc6e2cb1e993572c46b0c72cb2bb4222ed6571560f127ed5fbfb8ee16f8fae5733457524cc6aa253511b95be6f3ae58d19916088bc748c2eb0d60471d88d79fa6a0d5377231e54d1ae9b792e57f7bda4b5b556189cd49a30294c9ddc807ef478dd68bb5e138a4a15df0b285c217572738af98bade6fca71f597265e9d3e0a30d804a635d34bfeaf52789945b7da395a0241b729d97d3a6feef7560f249b4eb3ba9823e8716035cd1a59249290731a8942d82269e4dc209efa5569620d96c0d46891a419360222cb545260bfd611943101d2d98d11d0dd8a82f63fdbae6f22e456783af780b50ce5e160513e4eee907ee08549c25559b2da4784668c3e48e0f37cc506b8c33fb64818c78fdc9066840111bf0d64667ee2cb61d6636720e71be1a03807d1e7f101eb0aba600d27a1355fb1b7c4058fa0cc6e8bae1cf41f95f430846f769debc9e8bac076a6acad1bb1ce81897d8fedde832391b08f9a6fddb09e6301240afc32eafa79c8bce1a6363e37e53868041571c6bed1880f483f52468532205b0f8085ec6144e40e337700a59f8e00fd482b5b3937c8b57bb8721adb0fc77ff38dd043d2237f175a6de9cd065be9b8c091eecddf8ba489a4834af80dba0b5b5e9734722ce39aede0910408ae2c2161fab7bf8ba2fa4662f510bc5ad6e3ae2423746fb6434177153da813aa1f47d401999b911f86d0641e6d63fdf883492319760c2d984ce9284e6c87c62ec5c750df807c791170bf05e49e95df9f574a0aa5b6d4169fdb5097e506cc6461ff40ce88ed9e98e38efadf6ce5746f05a2d9a5a9198b6c26f6309ec7e70428dc0a9582f9082f617525f514a76ca038d24be1b734c2b005928abb5f24980acb33fbcb9315177d861ed9e7355bba9e83d11eac444524988d0b562c9cdc48984775ca97abf6f5ccef4c99d0b06fd758544b7399ee93f5e3b031d7144fbee946ae2ece0036054d0c4c31527858e9b8a96d0b3fb127bdb4c789dc6b0c385d7b5c8066705c1ad516ba72b3124295eab1be7a24e3317bcdee2f3364f4fa80572234c488414597194aa56930e7b532bce2de67a8c8434b6c8bd25dbd48056eaf21db3dada31dc0ea2a5eb781430c64e17635be84a3a92ded7681f067f74896e2cdd442427d4b2d0cd9a5002ed80ba31edd1b2752a701d7a4e8700a13b5d06a7606461a660869e49076b9668de654821b22cd01bf07332b16e68c373574a6bb01f8e1fcda1d6232efaad605da28645f9aa55a7ed0fb1a9a71548a6dfce6704ef798a1304da320b8110e853d0b51d07d6e7021eaf47cc4d729cd17c35f2f1eaa53818f0a2575da9c11d427320d9bfa9d3311834df27e57b31011b81dd14e7ef105fdbc5639ef206513759759bcf25ff07d02918d08ac91fe99580c31df5835207cda7515392b91b0e121646f4f913b1438fc2f5ae42c0a20cd1f3c9a4ceaa31e5dc847e29eb8ccabfe3aedc68e2986e3db00e6860baac30c31ae1c84b4e199df3e5986e3ddad80f67c6079736f3ba793e31066f640340736e3452da00bc1fa980f03025efc4ed38c6a90df510aa36ac05775b862ec2b6aab23385c3ca44909c6431ba18d1d4bf03b9492f2727072a9f1bc1591c60fec87e1bdc712a4c7fa3e250d80757f51d050057a165e4acf4a2706d0b156758bda84655bc986c3b55c06f1949ab9c78d7047cfee1c3205b908071f9779d0197abac43c8697978238430de3ea04806b6477a9fbea4f4f376e2fb44898960a34eddec948bd6c39725151cc0d3376c0cf742a9a21b90fe79c4417249b0eb245177ef5da3cf522d2d7018f64f4a032b3d7fd5d1a7bbcff5fad8620838cfe242ae9c23c9c7a952815b9bc13ec1fec0ebb94e2e56599c6dc09f5b2f76b90975db8b8555769fc4d100f601b792e67c1cfbdc46e3d328ceeaabbe483e0687288cce5d37d745eafd12854b20b679da6b0b559041ccc19b79291d082245d01fde489e4a9170b4e8de578ef5d9e5057bbe5e82a7c01d7d1643c8f447a2fe33b6ea77f581658fe761063675166369c6b9a6280e05ca25f9cbe5f1b79db365562c668e1342b1fbbe645efa265741a8afb09fd5c7575dc2b5e806eea8c42101bb6bbdb7f48cd037bea613ce3b5460a55ed75386682ed927788f93f139ebf63883829e92c7962edcd0eeffe31ae5884be8d734aec9b3c21e69e550f7b69f6c90cbc9c00a180989eda7736ed02dcda33b3b8e604548ea149afd548fa2edee8fdaaa421439d211a9f56484fb00956c40903522f41a0633d74b92d90bb43c439b93a8e9ce09922f5784c358b51d5b69a0b95bde388013b4fc0e1669326d05b5db120c2434168e1c0b9a6b6443566b4c6e110c1cbdd10884f27ec922643cd7097e92b1626235fdaedb54d02c5a1a46a58203bd909a7a7303b847f851ae4bedd55646c5762edbedf863272f62187c60600b103b80ab212ec94775ad568cbe36b4e83efb15fda69a2a8c84df56f5e9332511ef2ef9a7557e0d54c34c19f8cdda228d6871356aec5beba2463b0140d1d296b38fd3f3ac9834756c0777c1cb4fd5e4432e76b186d7fd5b57b374e9a7ccc532b78143abc481733307d43ccde3538e5e3ad452e230eaaa2587569d71775a1275409bdf534db91a05a1ea6f25cc4e1e7058bc4cf2d6077c1b275dd04126487904950cf8b453e47075ebed07ce8f0e454322e88b5c2474be50e278494a57fe7120e699896493d76dfee62f0803cee783bf3cadaa7768ea2b3c662e547f2b47b8763ada6f5bab5d248aa9199dfe8fbc6c5f3923dd500d36585b515419c7ce252af24bce390c2bf37c5decf51295357f5e02e0cc55aba78fa788719abc2316cde8ecc6a7c4f20d143fee316912e5c4e77d2733638cc47c8c9958a48dff6a8e7908a27c4b8d9d935a9bfe26f13649d21f45839b2248fa04a8df38d5333796d4fe0bda44aec47e2ec644db3190285e5f816547ca2b1f056dfba575524c91b69388acb53660dd74a3daa511f0d79e62034dd70ef289129e3d34744c7a011112327bf648c81b83081c528505ebcc97c279da11286a037873b1009e69984a427034f809990b25ad20c080e7fe608baa6ccd4484ee883be7d9d087417555be1dc66fee89a972824e4f39f791dfedffbf61c34745ffbb246762ecad8d72042881d5c9a6a4d240bc9b2efb61dddd6d52b426aea923ee6d5308f7687dba6fbb450a76d755314fc6e290f1e2d45bc9b11b8d21d827838b3df9ec2e0a184f8403457a59e09e329ae3f10bcec8417adfa15b4c72601755a39dc97347aca4bc2a33230d4d7399811babbf03917237722d94b9e54465a433bcb38c12ad96b10b390ec72fd314ea507aff3030bcd04cdb3e80638f07ead15afabd0c8b027c9a70eb249b8e09e24a5f9f0128f4beab21f206bdfbf2a854707c5017cec1177bb5513513bbbaa9ce6e280f06bc5aa0653711791860da9fef7a0d958f6d46f306f7819ef0603b84c0c8f4fc11eff05c8907bb6c7d5a996c1ba6f18a0998183472b7e9bce9c2644f36359964e6953ae3ea9418322d9425de55a79bbca6086952dbbce225e4b3b700cefe5ae7ec0aabc722ff7d0afbaba57a1c5d151b3d8a614cae1277864b097e198a2102c3a3886baa6a7ccd0060fdc9e20634120e6ef281deccbbef72f6e84fae7b645e6a4161a94ac8850cb44a719b693fe51eb1b258217808d7afb82f5624014016399c1e1b2806461923b7c985b9b19e1f443c7770121f52f6c0276f04075c2c76d7026288c7396822b649875e73920e7ba917106a9231813c0cd2de562ec5c6372cc0df074b0bb93c1e6d4dec1e4d2edbb2b70cefe3989cd3c28ef5e946671e55194a85fc6f42eb2679fa65e8882ba165a097324891e2828503f7679993482d30e03ac846ea941cb14cb947dea9d26a404216cd8b9062a6f9247dda81c29c65a38429f914dc87d984e193a990f452f98d0d2fad60aca9166d3d318c014ada66942bb1cf0f95fc1332be4e80ffcf5241374abff8d02a7a89a892c66747319495c6a799814666b3b3d67b99aa01df7ddf96c7e52b6f2b5399ec18c92a64f7960070aba767f5f481b877a9c84483dbe0098a618899a8582615ade3625c9d394b5395135fafae106d93ac98d300e34449179badc3030abac441c466f2be41037c4ef19e0e1a45df830b69aa790f04ced10725f14abe6fa532f6a44afa2c3561c614c935b0e936f455139f8c6f7747fc6c2cbbf1e95f741c524d8832896821ec4c1e0a0b42a05e51a349f312144b002bd4a6d6eb2cc0334dad3e2945dd84c3d9193228baa05290dbecce2df089dba7343c304e1cdf5ea458ee6b87b02598382f32a14fa48453b129e148475cd11e0e7ba9421cd351f6366ae5ff83c4214c8cb70140359d1c4cd043be177557992cad3dedf023d7ebf6e3c0a7191b963bc7c9fbc4c9855a0c7ff834046075f5fc9202d52afaa4b8a8f1267de70b9d909bb3b83c60f0f324cac51d5136d89e10cc5f9b14c465cb1bd596e75f59074a352d997abd393cc1095d0fdbfdde2703dc1e1d7a6928a9ee21d507aa377b4c18c8a0ae02e65c2f53a122f92d50b86d01c1768a4a4c22bfd3a302f89e10315d29495fb88c645a369f4c3ad9ba4aa893c49c368bcecef5fd444ee828d5ea13ba140d8b72e09b70eb2a755f95383431ba99b13cb9d1093b07af21801f70563a953e2a98f97295a27b1eeb11a428c27eda7c7f94a01d90100e1ee7cf0befe4e5327cb251cc46eafc75b84ad73c041be940f8b12c0f39f23313099d118c5817ec4cf7802421454bba974cbd0a7dd55128567fb39e0df5ec7e0ac5b10246de73ccd15d9e24d2758a6d247e64359dd437284ddd2ead12203190a2a2f5864b22280eded2612eeae453f086d88288a90d481260b260c919e20b44404436f94bf2b3a249d48982b2a1948e7510ea79b18e4283cfe4cd9dd6199ec320354c30b5221692f9f258755bf2a3b4fc7bc123237455db418c4e13c25114b7ad9e2ca75aa67743c808e4fc3037ada71171394b0fbae5b6f6b09248cf234f791a22c8bdd7161a7a23ba55213879675c526d016e6366ce4bd2374e41f41a75599dfd63378a6447e0ad7309ad43c731b0870ee3c317ec1a9023c0ef6a8c9020639037c8de14a217ae2a76a0417b883906b344bf650ba8019148ebb9c755bf1b25d983897fab8990034c4ec0b707359d1ad32c5265fc641f66a5f9eb6f47c53b48bd3d69d2dc005a14a983dc1d95ed44a6249544a76d3c610d3ca807076b421fc8895bc6a00598240f56f09358b6c05eed2d8ebcd443e13b2ed46d718bbf68ca0650a1c7298bc06c595536b0eaca2f9ba7b625eb226b9c310d22b68279fd74101f42d0d9f3e86ccc8213bcd6ca0bc26d4082991780bf1424653bd3d752dd76f2c6f70e7b03c3370bbe6289870ccce4ed6cba6fecc84a154c884bbeb8bb03d7e07b0fd9ed968b6b63abc4e657a16d50c440248999dee7005a123388eb7b1acd34580e8bce658938044eece108f0e48aca48837db8d57d99ac37c10e3575362c516710b8a72636da0c9be39e04a090a96ecdf969a990208254b2b713c99f05662a568e0f9a6c41d396dcd03816993464b752392ffa2c558f45bb178f9e98e2f330d49c3cc1c9827f74c4afc280d1b7d2a49d0ae68c5ccf75751ae589830dba6b91a194d6d9edec3148333569736cb689b408ae515cc9c73e4f2c84bdb2a9d6797edf30cd53bab028449df7e827ceae72fb7bda5c092a3d9eeb0d9255380c27e29ea448cb46486f4e0a16fd893bb72481b894cd0acf2c70270fb113e7f775977821fcb9cb349c575985f2c732c7a9b2c6b9b37b2a40912cae67a16d54984f489c8cbd6ed9f6ab69d3c3460ab04fc24c17946b4c643de38bbf2da35f0f13905083a42a2ad0891945926fc63b500896b1c9dab4a757022deb4518a6beedadc26f5ac461beee0fdecf0271b1e5bd8628342e01e0e6845025116e62ac63d1442f91d705b5c941dc7ee5cf416e8fc90b2e205a4c84a6248bb985386bf262d85a9c86a16c5346dd5671ab86f18f2934c814158c5e71074d8b0c1dffc0e9f32813d3bff1e2d9b8dff67cba134c17ebe95477374824dcc64879d1c172184fc5019806b6f3a6ddb57c0dda5c16dfcc983e67c31de66232d61a357ca5d40e6ee70f9586f294ca1c7257172b230ae279238ed136a4efb2f6602e35f0fde22e8acc32ad1dc2ad2fa5b87ca26e2380bf0e808da0386fe85600c4a1564b2e8fcfa273d29fd928ca427516ffa325b2bc27092639939d46135b7180b196649ed4c279c238088967143d67bd83aa45efa1d3f2bac30a2acc24834aa1fc922e4b337d573383d56043978594f2a65a983de1bbd2b72264b49a12f78e19ffb9f0b1532684c4f341090fde19dc856b215975d3b9214d9038e994b1c41f938f629907db6de02302fa5bc7dbb5cc6cb946428eaf29e53bdbbabdfc1ee237588d95c51cba4cffc12b47444dd26458c7ce309b3f192a3e6b9a83e2b0e038eae0ab8f5a6b36ff538699436cd71de5c8410f022710d6f048ead6d7cd382b8548a7e8ceadf6adec1ff2e7dd334e9242ffa6ad8edc31ad2393596d392ddf0932f210318ac9f9e951217ccb55017022cd96eacddf9b6fc0360ce0d5ef23ca170d750238886a104c17c2a26565f56ecd8fa138a45638b2fa5ffa2bed4116fce78c71d0111738ef5a36bf7fa4bc8abe05ac1df73c5a1f741caad1265e5a844828e3dc8bf14bedf5a86937e6ebc6f0e6a1b911700dbe506d1579cf83bbc103c0b202294dc9914a245034c5696caf5ee686c953148f28a5769c327d5a6b18b4cf6a1ae6b17361da2327f3971ff4ec22faa8421d4856fb2c749f57dd12af2c8f03b31372cd96dd5485c3b151f2661ec9a7a009480118b90a452210cee68ea5fc8125b2cc9048985872d55dd399df8978ed95390cc39474cc7950bc61e8c7c8e328b688626aa9082b66065b02f23777fd560c214db50bb2235996002310d154b9c2a8c663206646d5362e153b25dd8fb95d0fe45b3dd077353bf0329ec392e0d1158c534247c89f19e62bf1c437a09cdab330fd342288e08f0e81890c9f5a48980248f879eead40f5f2e35a0473041a606b895171c72187309992314fdcc4a8257d146a1dffa882946afde5a090b5ce2bd5749652f8dcf8cc0854822270a064782b6713331dbad3dc7ef53bdb28ace03f39e8419247f97b0bf0dcb8d40b54dad6e9a4e46d832431d7569bdb999bc39c199c430e5aa1122f9e23865962ae0fa4a890f22a8fd0d15a3245e1d6f261b53b5c6c49a41f586d31f84e2dc43b0a68af85076012e8db8f09ffae92eea600c2848b6826a6684af2bbe5f1c1e25e40e712b458a486f0020649ea724d7f6003d168a550b7e3e7e6f55ae446075edb6e2d98d17f1056516ff7562837c9b08349484c78403a21beeea1ad17bf074f19d842bb30bd5df8b7a277b89ffe6d45a4a91a193fa5cc8b377622131e6aaa50d69561cc3fc7c7a81f17883c5bfac211f4581207d11716cf6c3cdd3d0622842cc3c031e5adc833f63f1e76e9786ed3c7bd0e560e540bc22c82a790abfd95d3189fc2795f9daa3c4034f38a72b7c55b256d8faa2d576452f70dd00c4b14d80b8002df42ce1d16afe9c17132cb3eaaf3a4f3c64b9bbce86044c558cc90e6d2900020f3ced4a9aa3c2108b440139e9ceb266886730b6e6e679f84cbe1dcdef43b1839856503434ccbb96b97a3af41ef9de68acc5e74b7d8ab4ce6845343c934278834cc5ed53d93cf70b9cf170d905b2ddfc3d3cb6460afda382eedd0259a53e879a18b84dc3d3145c73c9603dfb0dd3e7a52e084d684c25c6473b63e42e45ae00d2c86c2c35d4b08a5fa9dffc775026e28c4453a906e2cbcc6f6ccc277fc53f14cbdc35573e49aff430b2d94ab8c8c8842ff469b86e1d3c0514f30cc76b8d51776f951b19740217ee783c198fac20c23e860b6ddc2c2d52659396f156b524c10d84cc28f2febcdb0d791223889b7187208a2a9f61393c81fe59851fa1906c433bf6b28e47d265e329bea56afc84fc3655eac0834efe38b95f658de9671b59b89d1537b87b4be74db0606b7e15f6f862b13dfb7b2d4ff07490cc9251c24fc74fd5fdd147aff5763ba18290455f1c53aae545b985ef9224009263b67931a46ad29a19cde6e1d26ca597d90cc404dbe945c3846c3f6140a05690d785a143ad3421a72c696639e7025cbc08b1414d25bede59b3812c89b1ef050869431d04ade46bf2ea84aec990ff3fca7193c8a11f70e19d884df6683e671dabeeb03907395a884df15728136ca3a633b116faeba2da9ff76888aebd740bdde34fb83fcf2417c48a9a672b8070a0e3a69ab580cf79266f9dad01a355d2386f6073ae28dc290710028ea58d7612ee654b72d066a6a373427b75b70984ece15c8c9cff8193d73cc3f0e24926d92ac3bd149ab952a4945922feecc20a8b4caa9b1a161a35debec9f74eae4f5d1baac741b4ab6e9cf79e21217839f20912ef5c68a956c5bf22b30a6f756cae7d690aa9371e299b656a9cb1397e8c0282db227d89b009359a2e73fcc4198139766bdd73472a95f4d0b4885cbda955754a0378e947c8f8fda24956dc18d4f0961674fdcbd6c8bff46683369409c401351789e7008d6a304c374bf2080fcb5f5f3880283950b433fdb2bdd0ebfcbdcb7a117fdef31ddccc9b136d6276292f1c14110edbbff022092aee0f0f97af6e0b0304f688254f12d4b7ed22d0dcdc3aad77c70d0bb6eec952c5cd906cf3eb5ef3cea1f6e49fe163a2fe1f85a848197ba83ba8f1fa8331c139eb8719206f626cd1bea6028f8e33d44b6497aa2baeed1db11add49aece510b8c718b5414835fe3c7d89bade5c36272fb5b3b83211ebb945a292cfb5a08f42b34da4cfb6e8c342d36774d7f615b0f98ccd89efb1d510d8cff6e9f3d6f7f2bbc7d3656c641c39a317dc8709793728474576bf71929b98ccdc5f62cb9989a5109cd30c2e9294bfa3abbca68aea01160150abf395747a599aac4320199afd92645da9a569910761a8e9eac5f45d422b2975735cd59393cf64b18a7566bbd6ee9c075a336d8aa8f1431dfd1d1bccdcfe65a66f3b16d2ac0c96ca92ba369e4eef276a9ea3e5d5feb14010e1ff7d6e0f0f988ad1d44399c0d974c7ab02933c002031b4938891d7a965dd9295d29a1485e361aa5aa086648b0a7972237a10a7e39b8f219a420958e9c35ae0f342bd20f0772867e51503d0203eb08978bae5efae47835df54678359b0fcab5032848c34a09b6047252a2e015a0d32155898c3a513507eb55fe73d640a6602be2dbd8c270859b50db1890f49c1e749303f24440c3229488788a324d5dda9c9cea0b71d549f9f0781e989d3fda7822f1d55d67bb033f59269f12c7307b6caf0f98485d9794cec242974cfcc4c56fb495b7b3659c57a18a7977a2cd8230a9b4e03e76c2fa75741ca3fe871dbbd5f6b0bb65348f4950c5b3645f86b7c3a741347d11717b37be3f24a1e36bfea4d648bdf496ad07b15ee18e895b96d5e865b88fb86c37c72d66d0c8abaf521af5941a7407200a72b800ee6ab3418feeb8bd743544121470d383eff722824dfc83223bb18a885ea642e6393ad87bf25e2aabb16157c114ee3b82ce1e3dd37e49b0942a4110546055835acb414f86b7a2576a6054ac2caea7b8153cea0eaa58a9eb941e1b3af984613f07ba3974993525ff46f60680703f02056e2eaaa713c5c271a5de89bb4a37c4b97382585704679d083c092fe944e66c99200c7e6d73911f7bd78c03733eef9da7a322865235891fc358951dfc13a44961cbdfde8347911907b2c2fd89978b0ef91fbd86b228438d2057c90078899b67e08c81082776078e548c616841dd6af4c8353385131223cb4c426be1e74f17b6cb5238be3f08e566b26ea1888afcb26227f13e37ec74e541e730c82f494e5439ea1f3ad20f5a068884aec7c7d1209839f868f2b1ffaa4ad10f9f1f4523b6ccd676cc8764d4860636a6e743ac78ce4e1346c6af4e4b6e8e615bcd2e96ac16e523f166b9769ad829a5f6bdf532a8660b45bb900a82d88cc294a104162b0517133f3eed20c99603045756677a25902e578a42e583598104d45b57132a88765280a793ac85dd7f05c1155693a2a0349141713befa38a383f5a8841521092968308cc288bef71aba1c2d446796f4b309cde352649b7f03fab2289c2fe357f3542bad1bff407f904911448db88ea32a5c2ab0e136d085c79e02485fe3760c92d58cafb835ef157b1cf79e3bc3cbb31ad56c71bf4d6f4c0db84f858b2ebd1fbbd416c1ce804af8b192af97a76ba50157fc0016316a10e25682b005536e5c8d01911a37be9051e150ae552b069ab1ee0d2341949323c7e4c87468878e5c3504cdc5fb4f604d6701704da31c9d04b8f112af15173b6cd71a8ea7dedeafd9d22d18290d3bf6df3c3115ae4140283fe375206dd92e861ebe5626db2e3377cc236bd6609892f98ff9d4fa1de03f8513181b3a900d32778730b3a4ce8fc76cfa904db485a3b930b3a7bb11682d86797f31bd2449daf6b850b21d15901bca52a2b44e206954156e15eb95821460abee09721a070ce38998bf5fc2d690187c16aeb4ea4b4021103e4eb02aa45ce755b300e334e632f2da7f3d311957bf27df8cd83ace367c667dc187e9fcb53643f4a8289e5b4587db86d068ffd54d0e62d4015aa01a4c200d0c9e8b6849bd884aa7d84b0073e1a01f972138948b32e3d3521216defdd6def2da59452061207e7067b072208729f23087a8a17d4a1e0f7ee3af5e33230d33cddbc361c652a603b898885613dea447b138bf638d4c130bca439a7f9d6bf2fef69f39c17451ee0949efdf40e8a3a6c9620e8e56dbd7fdee7e7fdb3f843b4273d6fc698a617d383ef93473c56fd30e54d35be2a6faac589454f554c9effb6373defcb3ce2f1e7ccdbeb8280acd7cd5b6f6ee1f13e444f2bbebbc9de34cbf8bcd50eff3def8985e6a870f77d9feaa671cfe98580f3f442bc277ee617067b789f3fbc6fdee6cd7af71d04ade9a6ef9b15f379f6fb62bcf3580f147584d4c9c4e258c3735ee1e1cd55eb75745d359ef40c82d250a8a7c6d777f0528b1335a788f2627ad0cd21fe7d7fd6fb37bfcf4b6d1fdb551c1585caafefddb1f7ee333e74cf2134ce1ab081b78f0def1a65bd69e5ed634f7b0ec1639ec724fe8a3f423d3fbd981e4cefe2c3cf20c24f2fc25b5147e87d25ceeb308aa6b21039a2afe56d0524446e1a47cb1b11275a052f3a005ade8492a0e85073af87a22d165d002d6f4341e8db9090a66979236265488906b5e949dade5407befbb6499b7badb5d65af31672c324faf8f40c0fded21c4e6a2e2914757036bc8bb65e87cdc4b8cb4ec51d3513589b356f9ade35cde5dd81de35130882a00f358d3c5fd5ae8360ada67ad33f41d70f199acbd0e44db549967707576279d3b0f814b0dcca1c67da1c8e9e1c8fe9dfb96f1fbab9aed220a02175ee1474ddd68bdef43e54f3d40fd1dcb8ab9eda74706e3b0fda2d7af754bde78f4d5df4e077f8aecbdf7168371f5d5d9d5dd7e1e8cc3dcc5768587c0a3c651b40773837ba3dbf5608df349d1bf7f36be6f4dda71458ece2653d5dd6d51f339e15428f7aee654c0fb8cf0c72737fef4e220fee33f3ccbf6e1af3eee079d356a7c9b21ccbb03c4feb61fbcb9cc8a39e07ccdfc3cafdd6bcbfcce21ec37dc5fdc45de4322c43d1afd166991d48993fd43057a92d83c77c783ad6eb549911bbc6eead1175d61833aec6d318d6306d77a06f4ad7ee531dd068bf0b7ba9abc6d7f7f0527b336f1c9dbae429ddcb323dd8936e9c1a3e925422656e1e3465d07470829fdb6a2e043095caafaf848a63de0e3aa0d1fe7b2f784ec80d6dfa1672437b3f6d1f8a1af128451ea6f37879cf4efc116ad3bc42df7954f3167243833f65cf045ebccd323d489a52e60f94766710fabe866d96d90197afc4e685b8095db90965819da030206a8c0df626a4c27589c1b9a053224c49d4b24a181df40ce344cf6bc1304740fb5a3d4aeb7d36e6c067bbb4054f9d9ea2941c687a3aa7143bb5a439d6fed2d8147889376df763ce69bb7fdca508ded00b69cef4385a49d471bfdb8be0bdcd1b3cf7ce72e07d29fcbadb7be31e79b8537beadd529abd772f733c40cd653bc6a6c0f697cea36beb3581bc8bd2aac05560a9579436e7fccb47cd1ea8656c0a5c6f3f91d094120912bae08e3cc81de92b5fd89107bd52d702e782be1f4879807ac65c80f79026f4fdf4416ff31ee284be9f5753514a0e66ded3f64041eb68fb6bb3941b7c5663589d05b52c680aa49071c314a0bc9c692bb7c034b501b4ddad2e304abe40c9c1e40b098450d243942b428892e50a1766a8f2a48d1d0b068a3746149e97a32f4e8e90bc20690db4ace2e58af68eb9aeebbaaeeb6ea4b6d3659e9d5a2bfd1550d6aad0ce0e3d080c90396a38bf1cd19c189aca57708a1406bc71b6d05f9468aa63401a8a1d111ba20d17b441451b53b4f104cd29dd5bef951dd8811dd88112bc60e69134af2a2d51afe4555aa9ed46fbaf564a6fd6a91648df7bdbde16a4eb79eae5373919b8a4b1aa1971b9a2f2625a5661c348972d5514c58a8a8abe883181370ece0d4b0faba5d814ec79e1f6c287f74a0f4f45ef60388af7c61e3c5d38f2cc83a3d45e771eb0c48153dfdbe588de780d296b409975debc79eaefc11e1ccffde4e16e174eb18693e3329b2561ed2c7654abc594f0bcdaf48e98cdd6283243a2ebba3584745764a7eb90b8b65235d0900149a7da7a44d7753335c8d05ddeae14169648c7050a8f8b1521162e497c5c5450e2d203e6f20113971842959655d4a0d245098d2b5ab4c45420f680c4650724a490828992266158100551145ad6d86265b5a58c1a2e8c2317b8e5c96b0b0ac02ed98a59514b4a4c9e819586500a0b34d210d148c2828b19795ccc08bce5910e086ab0a006d1e90c24a411d24cef991632d3f772ce6aa9963158a05aaaf041cb114e7cd0e243135a78d01c4777a09284a4a549153003354fdaae269d81a44549c781c1e57db3909a7eac07cfab92144652174e10953c5a801bd6863112e0edd583947a94867fef1612629a53eb7dad73e239ebac32f4ac77cfe75a7edb17776dde3497a02d1d37a6b11f30ad1e57a71429d6daedd98c6339d07bef3d01a865153378e082b497df893e3c70b5fde50e76e1f5c2180ed8d29c7b020d219c1dd05b318603a67a87d30379650eeff48e5383a5f0dd5f7910d97ad006e9b9a58fbce9e9796ec6d9ab62cf0c5313100b81141aa413a476d29f117ba203ad8cb1e9e27b6ca68b47a150a8bc59790f61c11529d861b5facf8c5e7ddbf07b6cf8a5afbe635028142a871cbde93d367ad3455114f35ee5ed012088ac21854aa5fab671de51df295114c5ec5d4a6f3299fe23d3a6bc5179772d70d1820c1ae3ffcc68fc6dbbefb171bfc7797717bf4f2693e93e2e6dca56d266cff7d27b6c1fa5ff91e56d6b90d28513bdc34ca94bd32c256d7edbec7bb8db6c3f9de66efb89d1dc7f66b4ede7d3df6d3f2afdfd47a6bfdb7abedb4af7eec35989572e58cd44400bea8d1c58bce9a0e795e851b74e4a2b0d044f63c769f16a3c4ec98169fc3e5312758807c5fb7ddfada69fd5a56a1aa91647db85a7717671328d96d25e806dfd971a25bd295d3ca561b53e3e081326ccfdbee9dd5aefb453e2fb472b18f8fbfc40581678c370ecbfbcf1f77d0ef1851501150147be431140ef0a3f100b374531bf5e0ea83f1d02f4a63f40ee0c15a93f65598bd09b72900647ef25709c41e8cfc772d7a99dfd02a4e03798f74c83e7bebaf4c21ce3026f1a8de6a48efa3dce8dfb5a183e85c7f0aaf1349ac6f053d421666fdce0ebbd304fa92b0503bc798335a976800fc28401a3c1cb1da444d752ae99273c4f77efbb667798774d87ef2ea0a7103dbf4bd073fbe8b951d013a836e2d41b7a718c65c1e0bfcb31a43451ca97b22983fb59e61d8658c3f4f6b9a5716fb0434a13b56a584b73a69d344d739dbb7ad21caf9b595a90fbc62dc475451da55bb5a508082f2feb5d23d596e6c4b260122210a43e1ced384e1ddb62c605be00cd89c008414898d233715f5eb042d77777095dffaa43e87af08528bafb6ae4e9bad78e208d026fc759d08655d4a153411a4eb107d81a5ff6f265a5767d8a3a5e87c50e8aa11dbfd8480476237307ec36089a0e1e748df6ad71e69647fcccc191753be292f8a31ecc9eeaa5f13573d7a596c99682e28ffa0dde76b7f93593770469d45f3e5c57dd75d555ab97c6d4c1f1bbccd81db603748d2810ccdbf47dfad7a33e26efeedfe8d51b952de83b776facf19cb2e9cbf4e00b04413104c587a109a108862068f3c01bd6833ebab1de8e5fa9664b69f7f6de53a2a316fd92b8b9b0c4cd8515689496371748e0c20d23dc5c40a2f72b9745d75b0b5e74ad1547576a699e93de4f9f6a6330c0db7b85a10528b4bc1dd9426f8945df1340cbdb111f340942a617d38225a5cdceeba4cd3626036c47084289a4501e53f8795c110ac4b4e0ce8d3d3aee674f05b47d179f083a6f394e24f48c0e1281e6be4376314e2d69ec0ba65ade8e44d1f33257f9c3e60df4da21add6a153ec416168051584b49434796e940d905aca0decebc8d55f3184799b79e46bee01d2bbfe893dd5938137d61efd68dd41572829c551c28df5c4bc431ce6bd8275b479f11b67c3b4847285521c25d41b2958e079e5ca2ca2740ace500cd2a62ebf7cd477e751df7dcacb29e48696e2bf71dbd214a7954e2a8e20586260b07c12bde761ec72fd34dac6de3b109c791febcff481dff769d318cb82f794328b38515107f88dde4ba3d5dc3b2094068294288c3dd90af8e33283c082058b17576610d30b4ab14c2c72a45ada34c01819d88a4d92b40c69b3d82422e0a4b4cee5953e5bbaa5d3cb3ab513af3cfa8c5a6aed5d5204cb911a5838b285dc18a26f97a4437a2d476b16b9333dcd05d9244a0be27a3acd517b9a932d154bb9b0f40b2d625f68a1e463e09ab6a7cd39712210c3825ff373b6e37ed6f49d6370d3c31c4d7379e3cc5a2a4a13335819110c8b8d81ef19969e9693777d4c8ccf561b47d3db7b4758ec0aadc2ddcd532fb52d75b9cb7bfb54cdc3e3eb426e561fb2cb3bec2bdd024a1a05cb9b912efad60e04c9c154c0d843e69832c7d458b4fdcbc7bdbdea76e4d1e9ac68fb9b919bf6de8939732c1d758efb7d49d4511ab7a8a394ba941c84a16a14ff8df8a5f1746ebce369dce24b4fa1aea843fcf1e57fa28e6e1489cc9d19aa2ef6f02e8a3a6cdc67dde977dce1ef4129eef861df7d3c4a17bb5fd539557ef9b858ba1935778e3cf55527feb0dfe0b7f7cbc3112907b0fa362aef20ed33f1c8237eead3c813de3379614dfc61bd9b658e79c7eeb6d92388bebf39a5ea618574d552ec817b4c21cded904b080999d023938484b44f4a7faf3ea1fe7e94a04de8f94053c981771f4e976e42e93edc8f209aeba1e9f9404fc98177e96111041d2de506bb7b157774bff7de084d5a470fc9811e32835254149a9791b935dd7ece79f162cbce5a63eba56ecac8ccea43dad6b8a2b47b3ab55a2b152b859279cc5da7879d4bc62563674b267b2ded6ab16256aad4098fe24fe1c1d0149aacf8397a26f19c780d8e72de39e9af15e2035684961850d7e4a000e50d8c6f2bddd7ddb5d75ad3f7aa842ffef4d3c1d3884fd8ce8bb34ff521ef4a86e3b4d3527b9a958a9509e18d836375cceb23853061c284b11a8f19a8e99b796ca62b8072c663ef81e0c340e860d2f2d6a485264d6c2841107526a91890b6ab372645dc9634716312061719109d9637265e6e4cb2c8a480202c5a52430d3adc961891852c04b72552b4d5f2b624ca8d86db6d062c829af030051a234072c3162b6e35d470e2b6c8319a1063cb0b98b4018616321b6e3498a1392d6f3484c184cc49a53be0abfbf71290fa9b81884a19883d37017b2e03cd1d2b8a30340fea783980cb1008adf8720097ef8fcc1deef3406e06223277eec33c64e4831dee47e6ce9d3b209df2526b9a3b7614610c526fbcd7d1bb4ebdf1ae43654c8e536fbc6a5f29ae375eb519cc94dececb9dcafdcaa42150ce6405e5e9740422021282203df85206627acd40f3a6871769c6b8de74efaec116fb2f5710a4add6630e44046475fa396f0f64de6620aed70c54efca22d49b6e66209c7ad39df5566665209d1c938168067abde9becaddbf117c9881885e0ef80e1ec877300311950e14fe7b290339dd9481660e335009f506cc34034da13c6706022202324f33d03c101190f0330305a937ddc30389c7382824950483e54eea72675ea7ee9c46b9e375578d404440c0a732d0dc017f3a6a4c21fc773ca6400424cc134a0a5034008339306bb025cc29dc2fa7702f7786e8e70c526638a2261eee75287808cc9d59770aaab4f34126834c896c86995765496676264b9285942da9d5baee268314dd7535190cde2d862dbaa4e54d0914eb64de6470a2a407190d332930502847665dd7dd6068a2bb6e268bc1ca6a10b5bcc1c0843e697983818824496e498eb4a7e52d49113a891426acd0728666bd796812284d86c1b60fb4902641c60246b2849e94e6390e994195521d4039b17c617b9047fade906cd124904069f23ee491be24d01c9a7b3ed0f4bb474753190bf8048ee7ca58c054fc21a7682bfaa097471ac848dd21a27fbd9197330025cdf94a1d77413b76e3ee4e8f3aebdcb857dfdcf14be32eed55de404230a1050d14ea3f331af56d33a12ef35df3d96cb6fa56a9fe23d3aaefef2cd6551e58038c2a6b88a268ba98656e6363a362b158ac0ee430050b324c2693296fd667b3d94ca552514a3950a48d3033a08ff9aeb90d0c06c318a300c60d4347f0ecf57aa1502814468043520d28572a9572cdcccc744c2ca17246cce9748a71b95c2693a98b6254248b291586614c4c0ca5949ba2822ed2a0a9540a637cb748a30629f063df35a7d30985425935bcb0c209d463df3234343461188aa2784bb24108168bc5ca5b7cec9b2593c96c6c6c6279d71d92600305a68fdfb1d3fc9fcd6694526a040e59a0a01fbf692eaba9a9c1186be1c2ca11168bc5ca1b7ffc96fd63de930534acc06412f1e128a9d56a054129828c22abd52a8521be40120586e409241b5e30180c8542011d61021d7440cdbc5e2f5114816600824992e89a999931994c1d3812038a274c312e978b52da812561a6c081a662626230c61ca0e18645118c42a15080e18519804085a7d34914c54e08294b8e106dc2f03f323a34994c9c941ec0b0c204fb9ed9d8d8504aaf0d579ed0405fdf35b3d90c636cc53081165ef0ccb74c4d4d0d4d2a23892250acd56ab36464fe23a3654451a4455b2c2922c67cb7582cd6c4210a1561fc5bf6150c06fb4e7daf5aad16a534a7072a53d0ff3b76d5ebf5fa3e7dab56abffc8e815c63805a210e450e5df3487cdcccc7c87df59a5523d6fa01408354983e63d361ad461dfb2bf5c2ed7b7cdf798f37f647486e5ed8119c8204c15d97b6cb2d33cf6fa8e7d262626e67bf6fd711c5f797b40088914371a1a9a6f5bec3db6d865a799f986dd954aa5be6bbe69fecfe4dd81295590982193fd6746cbbe6db0f7d8609bdef5fd7acce9f49fd3b78c8686c6953707a6b0910315b158ecdbf67a8fed75d86331df334f8561f8cdfa8ec964b298bcbb148021431630d87f6634ecdb36f31edbcc5f87e5edfac9c6c6e6bbf50d8bc562a9bcaf0c92a8628bd7ebf56d73bdc7e6facc5fa7ef9887b3d9ec7bf5fd82c160a7bc6d1554b4e0c3ccccccb72de63db698bb3e137ea76e535353f3adfa9e79bd5e61def48c2856ce70b9fe33a35ddfb6d47b6ca94d6ff37dfa4c4646e63b7fbb6666fe23a3676cf2a6504ea00414313131dfb6d37b6ca7a71e9337feec3b7c0d8bc5fa1ebf635caeffc868d72c6f09c6c8115452a9d4b72d7c8f2dfce9a9bc51aff9b6b94cabd5fafe772a26e63f323aa626ef215fd888c1c8e974fab6d9bcc766f3f0a7d959abd5ea9be6fb944afd4746a7864431a38a2961f89f191d7edb66efb1cd6ef3306fd359df356fe56d3be1d3aadb7e44adfacf49abbe65dfe16d27547dbaedc7a44fff91d1274a29109233bcd86263f39f196df36dab798fade6b3dbe42df3d56ab55ae503b4f2e6c00b9292f0309bcdbe6d32efb1c9bce6b3bc31ebaa9aa44d148ccc2006606a6a6abe6dacf7d85897794ddea8afbe5bcf37246d764bd830628b8c8c0ceb32798ba34aa552651c2926684309168bc5cadbf40d280e545e30a234348cf1ac2109112a60d94cd2660a472168811354ec92364551ec80152d452d105b87c1f029bdc964327140063045a2b4de636b99567fb950c7efb1a18e4f29a5578a2574d0b27a8f6df5d665be559f61b158f77169565e49da148f7a8f4d3cea18639cb74cded5092d86b468b55adf36d57b6caaafdecafb3bebdbd56ab502720b2f460628925b2071618524606280c30e3d5fb6083d81e2e6a58d9b08d2e8847002471118462398225fc084b7da228c196a104952c58a9c257e4001922f3bc08085ee04545a60240331be222985443a6336d1a06822396224bd60830c2369c51846b6a8046d18cd6024c1c0d88875319a29626224dea25591ac224b1146538a12afa22e463945495718bd2886c983d12d429a4a4833e6170014c92450408221a4187ac10a354c1184e14246f18189c5ca14231903171069c188a6e88995249218c5c0280da31c455b103103a32228a204216160344365091fd2900205454958808910585c2086972ba6ec74b71028d19980caec61aa20b96cf1c1282c22436ea1a2091a8c7630ba01c30882996414c19c73ced9cd39e79c73ce2e4b4ac302bc96e3e6bc97e3e6e426772f3d419ee6206e562b7b466d67efadb5d65a6b773b9d6a4b738b0e5852da3c22ad04c9614feb4b28d5ba97c6ed7553a8cbdcb534449234bd21a05adeee80b73cc27fbde67b22edfc5a4f42c531befef5aeef1a4d11fb7aa346f3fa7985e6b0640ed401f0aac1eba950c561f33aeb4e6b771a1295a26b45aa5128426a426abaf4ae3b4d127c42a76990b45e0fb6a890f64e25124d0b784f24249a333f4db037a795b7977d3a3df3fdc6c91a693a11a0b479d558020a4043d3c90c629cb779a58b17006830da8c20d49bf9d91864cce338ff7122c9c6d828ebcd4b66b46225bcb2c202eba22bf7c2144e98319126c7dd3be7cc3ae07bd767be2552042a0e99d707f19139be9350c63c085e1652f34e43046f1c9a94671942230a40734872e084d2b6cc2e5ec6711e9151a2635e8ea394524a2967a71723984e2db7099a9ee2a0187a2af280640e0e4872b0a58c11e8cdfcf5888092e6f090ba3e88cc016ecc5d06911cd49b1aa551241c40e80dc50102bdb9432e9d311881d2e6c45282cc01e4faacc8bcbe693ab4b199c3c65abd99978d475c16478feb469eee262b3bba6f10b2e9a97baa98b7ee3a9d2e1b63a3a437adcc1a5dda5365572a6f98f6f29e690febd4d3b8ed766d531652d39e37c32c026cfc8bf6c98c2518bda1342b789d27b1e8792f77b20c12286db6685704ac894069d68abd574a29a594922381d22c376b094eb89ce9e0f75d878a340cc3300c71b6a73f103ce16a9e7b3b64afc3b0f330fe4733847fc294163e0c292d0cff8a63456fc2d387174370b4360a9e7bee9eacb5b6d69dc67ad03b37ee9ae636a03854e72ead8be688e74ee7b8d368ce0957f39c6ecf71aa51d21b107c105d3a1d8fbb947dae2efd946994369ef24de2e8a2b452e9a9d1a337a5a3c60d74a9a64bd9f3c68a2b968ea34982b73785b4a55d340d0b784f255389177d4db0b20b0a4be42043187d7f7342e7cdf644d2dc3b0ea8841f82d037488abeb7128be6b9de4e29771ab08125169a2398da59ba4ea52f71394f24ca06489b0140001ad80001f30c0b00b4dca5f4561d2aa9a492d660fa1797bd7a53ef9255c1a7625ceede5cb9ca5d2ec0110f11d9fb6c29b5f9b82600f4a477e95d96846970f721a2cb7de64140263b03c7700cdc3135ec8575e7592a6302e3954b7baf3ab4bef010d17da93435d2f5ae37eb32cfc300679080e00895b22b3889524c69322f60a7767216270bb57674c9cc1f1d4769089402b9c7586500655cad999417bb5c33b3f010d1cd98d25cb400ab2ebc242a9e1d481ace0637b2e04da36d8073a31683e9e7dc31f50ad7c7c8c09081312f67376879fb80127d43cbdb132db4147b70ba478575a89c491d3085e558655470289341b59f337b588020c216b511ee169afec67542438ecb306a5d7283aa039ea795bb5428ed3a4aefa59ae6e985a5535b2a0b83d2893d6ce5a45d4014c6f292b3d65a6badb5ae3aa79e9f93d6138fc982490cb1b4e0878788683d02e8c431692ee1390d007a585a90e3acb7d2dc6d955dc1dbf342d0de4e5a3b7c3f4454ed6fc40b3d2f30b702584f693218e06aa7a57672f9e21d535b1458b1b43774d1f27603162d002d6f3714a1a7be5cb8924181b7a7af5d7147a2279779e8ec1ce5ed861fb4146997291e22aaefde4d8d9443b95f2d451ddcbf80139b403c4434b391ef0c11c91bb901ba4ef43167d79d4ec983ea206e521c40293bd402660882d2e4132bf4d43227644dec90d615d173ce1764add500b8f6e4db1328b4d476a441758e00ce32664344538ea1476fa869bdf7665918b5d6322896947ba5b7f4b20acaa3342b83a23e03a0a41d776da5371bcaf0e2adc0cbbc9cf7aab8c068ca0b372c9468c113dd0a1d38c2c3131c60d0461731186184a4b50289e00908d838e2872b443f6630214313528cb84051046714d22944602d48082f33204205a2258c00f364871eec6ce1e6c40a97292b27c5a37bada0b202126c7431722b32c61938f8e0450b2d6a20b1636df0a2eded89908db660b4b59683c2c9510f499c00f1815b932fb4bc49cb9b13287a3ebc395192d2f2d6048c9e4772b3e1e684931dd55bebddc01057905ef0c5892bba0401a1c5112c68818b08a24ce1bee85aeb1193f382e3856b43d35b932f9adebb35d99205288f5c3920756c9c9ec8e206c901a1c3e9090d1ec98888a6585a8fa806dec51aa5264f6ab55b97203447e4035dd7ed299ed0c2c4125480210309788902072a4e50850e5930e9c2e3d6004207040ce589d44196c3922b6090835195142c31430e51a041840a98c08288304caef0383502999ba8f4ec3665410d99aa190800005000f314000020100a86842291502812a9aba03e14000f9ca63a663c9686921c074114c3300840104300200001630c00082003d5dc5100581f9ed1d318a988eec77d471ca0c13d73e1403705bcf014ae2a68ac472811264b38e4648384124ff762609d856dc5e2ab2ab6d5bc20ea68b20286af84fe016af0acff1d779efa77d60320592ae97559a0d7c7a7afaff4058168462b0694f1b0db8e707ad088fa38d395fd0979c4be13dc76048286161d0f8f3cc3f7a111fbc1e181ae3c82474343d38f038f707bd151fc4270a029053c9967d07774e10a419a40f3d1e3dbd9cb93fecebae111441f0dc41f20b8685412006f02edc7675f5a28779f8c33631aa051f4058983becc4fe47131798674a15bf905a629016e6247d037e8e502e14e46bfef0be587ce056c922718dca221e785da454707a0bbe35cbdbfe7cae86291496cc943743190523bdebeb39f56d477b83b8276415ff305e0d9cd838ea7589e5d79d0798ac9b38b071d3bc5f2ecc683ee9d62787665079d27c4e419da857ee517c8930bcfa0f308790c742cbf100234e800741e6e1e46cb33881834a560273547df67b54f482e4d34177fa1b8e84bc0933cee794aaf276462e1ddd0df63039d385099c70d4f3d77869e9e12f801c6c21485272028a59b628c5e81c8f3d69add5c5cb87b8eedd8d74e3d1e77cf23f83df41379a170d04b0013dff1c6b3c7e3f4f10c794767ee20e84df6847ee6c729fe02e22b0466ff56d15f43705a6f48b967d615d0611569c77a4104f8c795e37af97834b0c188f5ad755a96b157acf57dfe40281d0cf8fbb2ade3cc4383a51f891973e628aeefcf5417ec6f918a378d476ccc0b5d091dea2e9f18f803cb1bd766f7a981335bf1043c2659918b513bead6849d43700b19b489a0f7567973f3c437982282a978b45053ba60f7e5ccb70b5ae1f249f90964317bfc99012a0cbb9205ba2c755a6327d9adacb077d9b80fed4a2faa5fe639d8186ab9585009795fa0ae1039cc6c653e9a84a9cc4bf71460bec90fdbafbd9c2d35ebcb1946c40076e19bd468bf5eafab12a8036cedd05360c78785d9990a077707fccb81562013601802bac5e60708a0c7804a351020853fa09b10d3cf8db3559aff07b53ad2069c51ec4c1785d703d41e2b3b387c755c4e19731e8222a839b3362ef083b22f54be542a3868104e67d4016522c6730553b4aa75d5054e601ff08ab8d4a26651cbedab610c412c066c24bc719bc75038e0a0579a1af218a4aef206da40d3452d327dc262bb5c791c7ddeea92dcb8a4a9658850e7c9e9c528c2526150ae1726b2ea5969bd4ce550e6400c86df920aca68661ad1f5d8045859b09ce6f27f39d28c519a3e45243ea60138cd683a40917151931f25447fb2f286569370a6c99468de814a695550db26f9ead72376748d23c75fdfd0af92f7b526890b4182cb6834a711b9f73338613988418d5762578d7e0f71c4fa42dc6cfcebdf7b3243615f773c1371d32c7b745c26eee8d386e96f8850a6a25a6392416e50437c711c7392307c9f2c43b0ee154c667b6e73b241f4498c335c5a7f4e9e24dff81d517891ce45d1edc0279c961443bf355fdd8e5b752d1a12af1f74e15574520322318668c08a101ffe87381d03ffaf579f658ef284954216ea12ada3ce487774eaa47d45ec3cad25b120c6bd1fd8d7dd91585bed1f4f25793e019d24f5675dab32d1ebbb37b5b87747c9a148a95758a9c394c4afb14b807a2c085a3f01f9646af12acbca85c4d30036d82389a8bc25b9409a03fac59b0556454e1db603ce0ceb3b620e51e916ceeb341fc60b8264e92adeb8d87951d9701c3734d6d056bdac45c839b56732690ec4f199e54ad613e9d6a96819e0910011637243efae2d22c0a5234a195a061910368db42a832fa51b872ec0aa97c80ae344d5457c1ba0186b2395e03d0245adf17162092a02c7231e0815133a7a483845fc38b51a3aa0ef18b4ddb7233b2cff8e7afab1858360ea13f9d2ffdbb297f6341f0f4c58c8c762d342b28e17140d892191f22d78642b721dc194a0fe662917efcb8490b1bc28a765c15a3f6922571d2447ab02c67d7fd7403ce15f8ab5032f5943d4b65103f539518144ca40a32ac7a9dbe4679ca6702342d6259eb0eba31d21f97efe2c85faeb429dcd8ba8b178a1be9dc8238505e55b199c5735f74cf1f679c34eb74de77c40f24e0d8b877b4ef170e084ab47ea801bd357f218d05e361eab439f7362d9290f14fea65057f3e8b91e80be91ff31e5e14a9dca8d1eb21abe176c08cd6dd5435054dd88beb08249b9e18d2a42322c62999396f2635da39db28e1687c4e364f91f695e3e42220f6682c7cad809224cafbfffe42b278f41d9da3d080fdd38b5e81a9a8ae5560a39dccdc454d441b7c4ceda73a491d0ee3b00905761209ea1478adc7bccc22e9e9882af81f2f7c9223f74e78ef37e56cb349f6c8754061b3f08b7348a96625e11b08a4725d7042f73b4fc0ec927706d45e0cbeef2acfdaed7647077479ff5355c431ccec557a02dffd00e3429b4e84edcbd2aaf172ba95c46cff39ebf2ce7bd707db5bbef95b3c243ea34fc0b56659e2d8bb9ae1398513979dd5d9b6959ce8994f50df7e2b117cb15ce0666a0a5ca31a285934dc0769c486a193a9fc1a57e8995c069836c0db7dd49f11b5413dbcde20118aa1ff58ab38014c5ed828b84282f58ceee6b19a0d7410111a57ef9098e3b22c3608ca391dc7c419601d424b8d05789ef80dcb82f1837000273e8537acfd69faca723c03a6476dac666ac4436e266e255b3913f22d454481c1988e330a7c8b0e0542174cd7614f9d5dc311c3fc6e8b6e7f30831a25c345689a2b83ea644339c4a7fa25b2166a835762fe15a34b358fd18da09b4a610462405c83e46b6bf0ddffe3a949b8fa1bd6ae882e82e78b8498c1c01678c2e7b011f554082b960fdd6e20db223203c0996a2348b459626e57ab4876a80e6b0c44a9d52c4945fcf7093111fd5cab42931dcd90625be8e86b01c788b0f05812097db9a2bb4f5b52c07ca18d1444f5b51fe1e7baa90f992067ea1501ab46807788fabb6cbd73f47ce2aa0d897af6054b880d18fb74b924aa8b640af18419d444401ecc5e98e92924c4217696701dbbb80b58692895b07631a8da6010125d7996136850c736529930d5822eac9a3f143b631e430eb6409005093b469b0a8622ddcbd2da110b080a38250b6777191f72f186a86170a17dd929a029f8ff69318b5fa354dbbccbb47c27dfab6b6dc1d58719f29aedf40b2508c7fc710fc34799222bea4acea08e0a21af11ddb1adba14241adf9adabfaaaf3d8206310952872a5768dd936c2c7e6f0bbb19540aa149d28ff7fdf7fc741bf1424b274770d84d1fec93173153da3a993f79fc6a48d26ea84ffb273a3208f26a02ebdb39ad230b9534b08b6d19100b4988de5c0ede4ca5ba33fa95badb0896e40358739add399e475f3b28f3e9dce6f726274ccdfea5b32224654885b36f9a26e09a315025f011b1442032627e0031cd9b462f7566da607f0151c7ce7f5ca04ce6f09b01b8e163a8a1e6fa5ed51572fa686f25e08dfff6bc1ec3e3e759fb2909d0b09dfc3c74730b96dbd8a7122acfe2290b32c82b57900f757493b95093668d50bada36def0cd3ec5f1940a95dad3c570c44b74fc0ec2fc3017759d10296fd9103ae6c6f166439c44afcb559b2809621d6969ccd185d8a5cb9dcc07529a4cebf21ec2a7db323c36638b813af15cdd987ba1542c626ac4efacad58b3a6253ea20423b97e553b14caefa36f64ca86334dc817f38319afbbb6a26da916d7f47f9435bb2469000bf361241fe853dbffc70ae846491758d546a31a4df43416445ca1c6c2e49b450d5785609718836172c1e594d0f5733394ad9fab3825fed9cd292d3bcfa539246901f38cc7c7cca929089528f39d499f37321ea7e62956e15ec242c38ba969d3eafc615168c13463fe160e11f5ef80a39b7187b386dce6149909d8e27d7d4d777b32370ca2d8c9372c0113f0b7d3636fa05d43d275f8c93c78bf8ac46e247b4b486c5135f08d4d886e7d5434bd41dd106ebf863aa818446be8968535c412146cf2b338727f2fe6c0cf869540b1a80b4949972b438ab73ff811a25f26c662bc41a188385deed7580d7791985e2d6681d7b576ee6d761b69b9167099772c00595293160c0cbb5ad43c17d6fced6159864b101f35562bb4f28a1982f891ce22a59730586046be2070a40a020d874fbd7811f80e1cc3ef6adcbc28fe52aab7547f5a1836af623d4eb99c9100a73bba36099c8023461483b0816f5f5671f7eda5cc4c27de9b494987b21752b0c11564e1314f4868e0c7b2095173a3535ced806a246f757754c09dffb445acc8b22b3d04c88eeaf469bf264972800f029e6a44eccf5a2e0847e758ea1791951768d10c590483eb8d4fdc9eee0dbf1c6ee54c17a2f9c01f494ddac9db98005df02a79d1ac6ddd459f0ce5dbae07998b497d0c55d4dbfa6c0ff935afa347911f8991db78fb951a573b8f93af18d06e242b946de728a32757eec9b4dc4d790ef074e1913adaaac4df34ce0da68de9a63b3a01d16b74fc9c11618288fc4da195a30bb2198c0994ab837487b35f836a17a7d4b6da647dc93da7ca21aea9e53a51909651bf8495cb50da7145b1b35564fc267dae2c99e2a13226d4a8bd683d2d9959045400e9c24f6d6d0fbd717d0503de99f99582145c8f6ecad089792a8ef2f8979bfcb99f63562ac56b2746b9fd4650a9aeb0f942d59651b48ccdc76e84ad9658f7b0d52b9ac310c4b7f631439c4a8d68704d045432ce90722a2f13a7ab6e5d4512ddb6ef7e8f7b23a3f40d8c4e665ec6845245d192aed7dfd364e639bd548943f7319e9b4d39c3fb341dbb9f596a72e40f86746f9ec03f9272b361183bdd9bb3829a496d92ea473779887924dfd0edbb5907f64a2daf92af6dfc29071946ce502108d833a935b58f714ffb06d258fd6823cf2a63d91412f9d28309ca37c04ea5796b2af25ccaf288018ba215ac60e51d6f81015a853ec1fb10d77948845d174f1635831b424878a7d0b1ae0a2b85d7a3706d14cc2f87d68263ea674b26fe970b26f313c6588d1b90e93c490c4fd2729983d5aa2fbc3a00dc45796cca3a984260b55537f6c48b61a673fd256ecde82b021cd8353acaef9c62343688afb589026d9c3e00835a72b1c214cdea657754397042892f99734b60a1f0d58d17a8838e62414a73cec6044c275a18602a262fb2dbc4f4432c39a5bd18358e0047a685e38fa78addab5759bd3a2032d48ecf860083467f985bae1e11709b58a91c3748da11c72189e1ba27784e84fc827e6f3f597bcd391d24015b872af934c17f93e5fc4a3fed7f11d63e72f000301e068a14ab560943a4a1fbe6accdc00319245fbf369d7cf329d449d85f73811f91f70de6aa1a945ee282f81525f84977a335300f0b7734ad18e0f7a7a0d2100f83ae71099f34b989e75d8c12037d6d5cd050c0dbec6b1a71b35aecc1c741d8d3ae18a113f6537e82cbe111fab0a21077b8f54c6e421e4d1abeeff4e831ac398372cff45f83065592216c292fb41d7a3fd6846435a26c6ac39bb279498555d08e41a77918f0147b28dc0425d627360bf3e8b2da209d97700f8a467a3820509494048ed9b12878b6211fd25b3ca84b58fea2b53bce078a8142b0f7d173824c6ca7e9e0f36bf181e90920b1c7d206fe4ce2fc60d639054b62feee69768b60aba610472375b55c7a9dc7b9cc8109f5462435066986dcac4210cf2e75d6ad08695b0c92a1210f01610ee8c5e469e734fe58770e3fd06f40c84ae0affd3c587269b1e986b852022c63798a13e84234310fe837acea102852e5fde2c39f57f356aa1d00c710a679f1cc9de5d9db530d3c4507bcc9cbbd66798e1c26021b5fef0f56bbd9a80325c2d454f5d0096fab528d72b7f9021ba4fb316bf6aa4aa3ab2e63824009c56612d929539af49f44d3add0582d1c6c47d1bc5ccd7ab88ffbc94a67f3d6124b7b46cb8f255037202c4fd8459fb37e4ce2271eac140a85999bacc90f3d34cb062614ac3f6b1bd61bdb14a9b70a24745506a1113470da1264a97c7af379625bede8898cb2449961ab4edafcb2263be342c90ca40e5861f299e6847f64f06ac50e7891feb95002b4bdc6d54d2d5e0695f1d9343330cd4f864cdcf24d48c21609d0662993d236710b2b802789dd1df45ea6d113cf76980970dc9860d03f67e11a77ea1d66a517a2af2bc4132a946821892e170b6180178c17c39558524539613eb2a4822537963e00c7477f85680000e7e10d3dfe5aaaddc73d9d74b371bb39ce56cf7dc5bcbd9c58fb28e98a2b2d5ea1a6d79b3d13ef9d977286172485d1c0bf8eb80ae94982a6ead0d7c0097124f8280b59dabc4ab83aa1cf869e54625c91a67d1b19d538987fcb9486e6cea7c7132e2a9614adb1002d08c7c72d5f91c861399f8b109f86555b8a9cd2a3e9993e89170d70bfd472ada138322f0d96f048083c6012b4ac5961602c71e694cce9b181d883d8e684581ae3ead99b46237900cd0fadbccca55d82d520088164500a045a2aee2e0d3c2dd561804abe7e4b8aaaef42f90f70a6264f06afd4adda34ffa9d1e10501004df71acdd30781248a089dc6645e5555e89f3ab48a49b7652458a001f3ce30074481c9080b48126b26731a81b0a1845012e8ef6cabaa12e6d5e66806bf9ff269bfb310107f8b839283863fd06c32f691b88e11d80e8aabe9e07c320729795abb7f267bdb7844dd0790f63a114ddf5adee2313cc123044f2d3e424e49d58830b9ccc66091283414c626fa4e18cdb14dadbd9a433b2c97f1d00839bc3b26329b832a757cf15924de72e94cd25139ccb32911ee214f4008a9119c8ab666220ac797064b84e2d9008e7339e7bc9a0339d74be8d0758f4a25537810459ae201a83c6eb8bfccb82c94626af0b9c6bd0faac31249c87ef6ac968fcfaf0d4a5af51fb0df4ed06231eca9147e23df1616d2bc45322fa8a54cfb953c08860b49dc588c4638a8a6a08c910f42b78f8e6f14d8256ab54f81b881f40c7d087fb4f1319e71385dde7e06f1ec3b57dfb908b1275787ec098f4b8b9a68c8655042deb290c0e6ba7110a6fdb0892bbe033efb46a52f550a1ae467badeb403256c6706de74f67ef9aa7536d2b70404a51f19372464e2cd05632d8ad3b50b2162671a3c1a4b487d00cb3dc1d3b6445e96b8da3d8a75e05634c1b87a8ab2f3cab9aa829f7de8c7ff19496f74dca6352d5d2f366339cd501401056111323eb7a64fb481b1119f88062497f31dbef5e818e4211d8d01c8e2acde7a6e6de279670280228d3c3cc0ed36446e3363d79eea7d828bc8c5198396845e4c5500bb86efc7170ede24a8af5a2163ea8903bc4fe3b0a96b21517963406eec6f8b57da71daa85db1c3d52a7652ca90d190fba68ac7f196d9eb727831aa94c3ef7c971f63e61d356bf034cd6225dd089e2b60e93b76c72d2b35340d52974567a75e003db53347c7927044e30d48beb3ee369cc84a395a3dae9eb663d2815f67a088b8838d5aeab89a16b4907781a6064f5d31fd8ab294d4328db3b42b14feabbd8b1526f22ced8974de2256cc2b6c747ec5ff17dc1e66abc8867ba3442fbdec9421b95af90289246153807c321d4cad314dac5b3beb1c2fedd1649acbb7206715c5172f0f8f9a9da48c595baa323d839c9b1b13d91c9e36c7f03aaa5fd4c217f57f9053c2426828c416c55b62662a86f98d9b0e42e0541f9e6f3badb44512b8a095ab65e00f6466fd30940c860597bd3399d783e0e5daeb9f60f4963422096ed1e2abd255701b8bb310d43b2bb6797f319d134b37aafddab5d5fecbd3bb788a96947f0551573942647bbe4c44dfa0121aba3b676a8d4504c08c692c4560bb9f5c04bbc8604be4df84a466ce631be913cfe042689f03bc3abd155685b328341efaacfc3cd1c4efb4581e94ea988a47a833feff995fb986a056cbca8058f2fc18d35d2d2be2917e3e63d44a73944a65c829b014e4b73f54713da6aabc278e9b42acc47786e03527c4d11655f0d71ff93e65d79da00fbdc203e5dfbf5eede51bebcf449245bf0577f57bc8b9b02023d62f98270e13355339b701f26ced253e4b02c0033040a0b06323b7f4a3175c44fd331893f7332ae2b08d9c4e9ea8140a5ee190b51c55b400d77b943b74a26989edac180d32c7db1e0abce649b8f7d2852be67a60d5defc279e417dbb620d3dc8805073a4599f10147e1c053f58145b1dd23f8e57aea3ad3652229132d4fa386f1193e14c0fc6c07e143efb051d35cdc35b771b995d4140624d5ef85637533ccc897cf6503c04fbd55dfb0b9b299b489e0f9b9305599156c053ed59270a1d7674b9cc0ad6685441773035fe70643f383b07ba417300facbf6187d92e90e71b7b0305168a3b4db3ef84635c07c780daac40edce0c6c7969246d5004901263fc7d9b636216ad59e35916fca731324ea11222d948e8548d8ee6b963e3a1b19b1c0e51a341c8f8605aa90603f02327dd137e1b455a051a64b46e4bf065160bc5a6c9add42fb76b5c78f28de9797a64ea3b05abc92c0ea88b54954aabaac01d7dafe4513e65d70df0eb613dacbb43a285c78c117713ebec0e217bf1abed683a5c9b568187934d40c3785c747f2b4d9c29c58658df103bf2abfdeb52a9b63735a99dddc860532b028ff9f4a3e672f669109c41cd9ac9a3dd2fbd319d1168f67fd89c47cd1ac943e614f6064a97b098dd5296f2aad9021183bdabd20186865c3b9c190248bbf6a0bbaba6852312ab7606d9060e9c890d536956d57488a021271289f05255b3878a9031d5e9c0aed94b7b526735acbd3b2390912e099c5d3286728ac672f672cf0fc928918ccd740664c09f3dc18c6a4543abc8d2e5e7541bc52648dc72ad363bc1ea75598a063a323e52055ad8d9237c54a76898772df379908714e7b6d99134a4d9ff663b4f34ea2c915e3eee78574b53316f9d8fba3c1ab86b47c3726df6d29d39e3d6505f2bbbf28c72d6c399865ca365ddb5687873bb56f6360ec0a3a6dc228bd311d2a5b4b203842d839a351250d9b38a0eecc99ee088c2a801e4c419b3253ab64bf66e4f80574d3f253ea725a04b69b263085746357b20e1b167151dd843f6fc3fa504d62bb8409c2f7d31dd4d933d8299325083d14203bdc5e9d89eecd9a3bfdff82853a66a24a63b5ddfad64dfca72f36ade243c902e806ea49bb2436567f369882a1b3f5d335d4aa76427caaab92ccdd7170b1876f341a6ce319722ed1b046cb4ac4b73778f913dd2ab73dd6577737c4844405ed97c446f9e83e4247aceeca0b3af9a466d935fe8c6086606b4beccff1d188736cfe71aa151a3c37f19d305013f4585d81e8e7087110e193a8166296c7319764be39e31c1d087aec7c6a8d588999a4ae5254ff975f01c40db8a7f41b755422ed5e134e3e9d00bbdc78ca9b988991a94e4059da52b8f3709973f2430d28c96e3a9eaa3afc76a1062983d553c3df490ef31296b1a3153a3bebcbc777e38b803f59676ede93f84ef8cbec68436fd5bdbe9f439a98fcc5ec58437f6db9be8f493faf63526acea6148e03271acf14b46e5e55718a5f07fdc2cc6b26fc5c5d2a381bd4edfa592fc570b68b67c8c988073d639ed3a6e66d993a9b695ef7fd9998566a4982e5cc3bd7668fc29fcf904e4faa06987d381f09726b216abe3b04525bdfc401dac69bf6dd771337ba98195d78c73cbbc653784d9b5c5ffbdf8eec523553f3dbaee49d19dd682ece92ec9e443dcec7fd85c4b293394ae84a32fdcbee96077e1657591cc5e97b1bfa37378ba6800c639451f98ca7a8aecf76a83fe45e6f243e096461a8bac3d11f75045d938e1b803f164c292793db7b16fbdf8324aa8b139b41250b3210b7d583709186e4514f8cbbfb0fac39a6ab7f7b4906658e8c16943e35f4991629fd34968558af2e7084f9bac80dced2b956a428593b21b806ee1b47987b8a750990048e2d7654c25b76e469bfcfb1a046826934b4cb2ef961e71eb174461781fc537340b5308b385ce3ee6ad08bc8ba7cab737ebda89d03f5408de0781c933cef46a89c04b32797eb6a2d6a069f064dd8c67ff39017d0e09cf95c1c9018a322a4d9c2576dd4c3a677c10f2fa4eb72034d5cea9086b831a6a576a6e55358446d6c7c9f6b8d8e96f76d3161f194296e54b5389cf0116a73536b3e17988070147c7ce9d6e0bf47edce8e012c06d4071829aa9717c417214c35f14b935bd6c5038da2a4e3527cab6775b8f38863800ed5a394a587476920b2ce506a4a6754bdc72f7419187fe39b387464306ed4e01fcfe5f95bd42868c87e0b79e0f57d8829775073643da268b42be0193f65f4f64e0551d6a103882773b8cf4d10fe30844c3e808dfa19b251e6139174a0dd21efb9002583af7326af86accbd383694fa7a042e5eba999cc9a37f6012b8fc56f3f6b94015a6984a8f7f0ccc6255d3416c98a405759f2c19f7201607f8be09506317d8e6c8d265d29ce1199e7c9f50a78458267a16fbdd6e6ab23857ce3350e2431b5923132fb548de9d8c105d33873ad208a4ce4f426e401f4960f0bba0f2eafd3fee744e0b5ebaf7d914af2a858247b602ead370095f4fa9deb864de8d480d91f6e2cf1914f8b7af325b3cb6797b832e3367a5c7ced907b6fa943c4612eaa90d9a4e53e347cfcde3d5cab1327af113f96d3e7382370d0b715d6c72cf3fafe3accaecb15eb68b7cdb343bc5d3d8e774d0fb1fd388d5ccb4dfc7dc6b225a39d71e4bd36cd65430a84a11022588f5eef3c94e1d4d9e1c0d70420d2bb068bbea55e3f12c5c9e406ab2f4f77cb65020e268cf063e981d80112aa381e199028e6e3c3232bbe3469eb9bd6fdc79e46f40b56f90fa50d5dfe3364e81689a7a02ac5f378be6d7c907fb341bd6d6b2d0da3d692542b4a28736275af452de09520ee40c0d4304062a14c9476f4b62d70953fd7fb4c96346a78b367862051190f0a5354580fdd00156120fe105be8812e16875de26e97c0db2b684786769da83ba4b89b99efa5bb09d3630311a964b6c470b101a768f1363a09d4de7afc2939c4856d83db4eb1e5788ab2271fdb411f93cab144edae962044b011d5495529b3b4d6189b60221e87f5a658b9384598bf10bc7ea7d80dacc92d8ac1150dc321900924b509598b0e4226db3e6cd9209257fd88d4ad3a600e7737c0f2facca24a5bf2fcb80426f6a88ab953830d3ef0620979fac83938c677d06d2618baec27739a779545b0939bbe2947963848b10ff00312032a7f9a84fe1818343dfaa1f66d1c09cf9e4ef2cf8bd9c27199261e52d8aa12a0cb879ae89cc907b2c85ddc6507cc4e9e36c7e8e33cd5133abee00e69d3438e9d0cb170a9d2cb3821a95ace2e3508664ef4d3d3e51c74a281a56f97108996798cd8975f472b86c6feae63d378c58d284f3e0f19c2543c0d116d201713c653a0b32b5841a3907b8d332c4ebd8a82f03e21f0edade3ada5b35fd350af1822b82d468475cbd339a12ec6f2d29d00d0a2a74929b23f76c56bfe7a5182130604e16a226aa8febba193b3a609c7a0644773b67060707f1ed88a426af25969b230c8e4ed78042b04912e99ffc51e02471733c0dc3edc88f8d38f0d1c42486da5de1c1b9e4eba3344917406369976cd5902ea64daad6f403bef0a8dee8d3d3e38968a23f893c288c9f6191b2bd871c0094a0d0fd4b5a1f791037daf1896d37273da38751be5dc7b4c8de159c6537a5a7f495cff20ae4fd0dbe3d1d37b290a4f22cd0a7857417a56c4830468e3c7013eec66e42236e4a36b9f3b7257b5d459d27462f85644b505a872766810fa8df97c2546971acb5d4e5057e6711f5e4806c164599557df7b3913dd19832af95c98ed253f11ca5351ce7c744670065341fdf578605b1f316bada496f712ede58c54bcce9a065ad44471234a10fc034d280dfd9f10f25b2422da3b6e0be98a09617568ace04a55f780a4c76549c4c50c86193f63f346f993f5490971eedaa0952505d16768e2cf25f5b823f4a6034d7ca6b7aac54de41b981707b4b9e601197829a768c0fdd8a0d6b04542f5d6416a7d920fcf551a9eb70a1a97b03385ec063407aeb7441f42009c3b394017b7601981c61848c2ce454b59342461ee29de7e606ef5ed029b4c9d5df26f528b44375b5e7cc5edbd09026dd1a120ca7d051a0db197a743170e71493296426dd015ac718e3ffa511f3c0dbf64cf573e83ea5396dc7a830b2fabbd2308015db59b723753c0e460022e8a83b10eedfd7f2206c97146ab1669fee7f4c2302e4e5549b2cfc692f3585234394b90f92f6cba10f89c1578a083da91ea6edb93e3b6386bad5c287fe2907c5c3dfe29cd2237c39cf9f16e775d5b1292e54d1c4f934749e714d46f9493d4608bfa51d5f637dd585765a83957f263398cafe96b2c821e5b5a116a8bb4071c512dac3def201864b6d58a7f681e9e740108be36613ccdba07868a6088956bcb5dac7281f538daa0241a5d35e1b51c77c77389496af874357c438863b3fdb66b83439a2c41c26155b66a277ff6062db4bc39d6c6ddc4210652e5dc38e7c45b2b3eb75ac8099dd25ace0526c78462200168d73f535a7ab0cfd8642571796a6c2e2b03fc87f42461b16faad612624b8eae741a6b71ff990f994b201b391de9f2faa07b1cb6d05e18dbb67c8019c50de0c45ab111b417515e046c1060f126a1f298d1ededd01dfdedfeaebf35528bab27b4dbd1deb3737a963c2ca0f776a4f6ab38ebe20c4066c1ff6d236d3c8a45bd87d81c857b5d6b1ff22af56333ced494bccc3e4f31e9396daf10939988c27a5883c2257884f2a796513c999e3e658549051f64198299b76d83841d91156465e0f0bed00b087daf6e1a3dea20e28c594fe8f2fc28e9918022930308749ef74717ad88c14893936c539c261279b440d81ea91753368532a28dae57399f785431de9e4b460939a521f24ef9d67eef6a32628baa7e912ca666a6707ed6d707ed5b525a0fb81bdad0d657731518b87961385bccb7984db78973be3d2564dda2729101d6a6488956f8c77425f6bcdff84532b2bc5b6a7995c165b0465d6dcf14e8b85c920b21a54fb4e44e40398017eee18925aad08250bca271be88e86cbd9fdae63ac3cb2026ce0489948fd8e1d59db9b478740b77484e2e71ee24c4cdde3eacb63eeca5ae3eebe54bdc7b5193d76884c6299afafc4bfd4299e42169dbcce3d0bd7428a024cb88ab2aba44e0f685a042afdd9bab49b13f1d8b7b32e7a632172f31ff4f5f8981bbbd7c3828045b9bcab4eea84fc9972c19b52a60d09899335ce7346837b872ac33f786be1192c3f889d3f3c54f5858e37f7af1efff3d3ac1350f247a98e15f23ac038b386c6fffe112239f62c59c198313f9bd6faba3c0afee750df9d6435488dcda351c6c2ac538a7aa38655be1136ce7638d2e23d63f94f4764bec9b415a74843fb75fe7312a3d47fbb081601313d06781bfff3f32f931386e61d9dea9dd0b8ae5829756da6e1653c8fa1822e38195fe539cf09321abfda5274b6feb786ee05fa7f1dff1d3987aeffed199a6e3f5d640b8f2a241e494c0ea6ea3397653f2a3ac62cdd25235f9438679db502b8914452e0fe309a47ae276d0ef2482ab3ee4e62ee0aa03b6c3fd3429d432e940fcae37cb50cc388e3be4122e3e391b73d332dfbf43cfa2b684520c1ed5281baab37b5403604fd279a11908b2b4da11901fb00961d8574f80de45ee70dfc0a17b322d956c51b26c817c6189e2313885c58324c0d9c21a88cd0fa86cc2e735501ed4d6b1987903870bca88dbda36bbc660822468d9ca8121d1d8c96bdebe726dcd688804cb40293e613ed9d47d79807cd80f21e6fb82a7320ff9501de305f1d71a977a12497012de5800e37b72ea5c238ce2d0088061ddcca8599649f6075d9ac829c3ef7c1ecabf3d8fff61b31ad156fa104a66224319dd02141dd3a63c3f45063278dc0294a83dad22282d0a4c99fea37984d7de4d1a63f675dcfbd901542c303a52f252523b00017fc041950926ac9bd6eb1c0dfd92f56aec8c1649bc0dfa59f91bd3bf2f0ef981d32d5993ecec14cf20dc6008d56e6012ad74a80fe49e3c1f43205a74eb9178fd64378adcfeedc84d32650193ad2592150ee163b30a3ab770231ee594fa97556f9fea6e9b2688c8b8c739b2c4b9945314b55228b9a9bd67348fe89117c913ba6d7a3ff1250a69237e365fc138c7e97c7102effee39daef0a61614004b16eb6f9fab56b849b08cbe3f80ae88f55c9ead11fa291a50ef6923a485d8684a99eeb89f927cae21dc5d91e42422c688299acd7af34515709378f62229958e1d2e7b2ceefff24d92693689fe16a9b09854eacb6de8897ccb10d00ed1741a954fa3ff3a99dc0b3793b25a3f388378d0aa8d3f9dcc979e184f83ee82386f404afe40df190951cd940ea9c5d98e23b9bd73e7df851210ff2d7d5bc600c9278ca85417c5be2f5a79c8d2af444c4846e379dae5f7e03828ce3f31f54db35be7645ee609cd0c869b2bb5513c8efc4dc6b366e2afc926c967b19b6b44206a0b618bb17da1218eb4e18b1ec2cfa819426033cbb06e7f7fa51f0f9f9264fcaca0578cfd3f1d4e022854c908ca0e160b5af82c4de51feae0fd9a5acdba80a844e7e99c7dd0c718dae7d6dec5752855ad901c19177f4cfbbcfa978f9c22c79e7e352f5479d7d929d6ff816e34fedd55e842f7700b53e2d8d7ea94e9d8d42a79fcf3e29e25fa2ecc4544a7bca73c76d3837ac51a34f46fd92346ab450c31380ebe41141c2632707142550d26687a27891c8d10b4c63eb776b1cf7203c61605593b2b09790558f177f16447d4c51a51ee737b2ed2168171ca993bcd53123d673177997a382040286913e5d5aee727c387c37ddf5f6bd30fb67739362fe79a8fe98a54f57832810547457b9fc83c93a492e32c644cffe1abb36a920f56d23c3c54f2dab05d8cad6868cb95fa7bf6efcd00266bfd0fbecdb77ad1b0fc1a4fc6019890dbc74ef66a62872c67339b189bf43169ad1d422fd6f4f8b49e65f4e00dc30c02e97759a582f806ad7b0f9b42ed99c4b346a637a67c7b74558d2f3c60b5e4b386049ad6363aa0ded089a58ea844091a12925e1291c6d8d10abd07c777ed5462af07753a17a30068a42a6e194b039cae4c37767c308c4cdf05b5941f55c5e6decc9bdd5bf02f9754e211063d88c6e28d58e551ac0725c3f474b406f129a95f8621b409c85e7904b2d9963ef8cb59c1809f65da3b5a147cee2a2d877f85e4e4ecd509b0502ff5268349c8faad0ffe8fdedf85a83f53f29252634131094a15bd2ba43a4e43ad6eacc966f32fa0a610809b6e16443e1548285f32fe749fee1a3224c4c1bb295c7f94eaaa4870f1b85f87d51a5a0d8f2df7b4b17e74c6373bb087fc1cc1be037eb9e1ddfa8ada4945c517cb732b102f4bbaf1a53dc09570fb755798048bc2ad77a9154db53fe3a1f183351edfc47016820ea1244727351eb397fb97e4da091f59b117f08a9f8da116191d64f768431ab0c2af7091d8846959d6779c789a4af431f6426b3a109faa7a5043da7995efe15072905435fb990d7269c44b82e46821500b7bb68db0e65b6412404f10af9e691a8c41d23c6f5103cd7ce15aed1a75a5ef6e5c34935b5c3bedcbd95ec1ffc9e41cb0f9d6931614499d828f8d7cbba219af62f6983d9d7ab07c4d63ab21d54ed3b6b49fbfa8360526cb62b93f48cce5c0d0677698b3d2f8242820ae84789739709d34a39144b794967105d37cd224fb667ec3a90844fedf9c5a20ef2b8efcc19708f46f1fef6e3af7f254298411b141b9cf11aeb36216c577cb6b9a450e5cfe3ed181a302e8f7f3706d2470b5be068a346640218fe28f16118ccff8468c6dc692d53b9009a13f1b4c85ef67ca5de46666bdb5b3c8b55794faf4498c9a52e37e915da7f3eb178b3c32e8d39094b8f682ab5b5f7ad4c7bccf3ab5837d6065220a2f65465f3b80532f9bc940168d3b9262d33df290a10a9c485ba4899730e9d9e30fac8d996ea633dcbcbf34acbd8c3057b7427e80778d8f5e1293a317f1906a0d0e92d9e2abb2f10b016a93deed5b06e007c761fc50e0b270ee812daa456540f2799d05d7e0a6cb26db43073f597b156b49c02fb5aaf34eef3be47a8eea33414ae3e835438fc6575d88e483d46f5bcd6deb968855cbe05c5d7df542af0cb040e286b343286479e904bbb22b850edb8646b01b4adaf026cba45429f104795daf5392d83c341e8f755be4d6729eec279673533196a4dad03d21e2b6864013b52185489051ef57fb3472fc43b38e7764ad61a6d9b7cdd17322845ae26e49fe2df04b71df0a9fb00ee05fc30f0c4d66c0384d95babac7ce029170a6f27ff1ac6fa45f93a02974ba3238d9799ba869f1560db4a327ecf9f85b988c774a23a022feba5b9f334b3dc3ad1a26bf7460dcbe684277d6600847c605271baa2dbb2c1c738eb179921963a1c55adeed2863b57aa52ccad93bfca4fef0b47b0432ce37aeedcaaf389a3dae7d32ad84dae052055d1ce3e271dadf1de54789bc4a2aa9256f8e26e57e1818374567fccf5f7ffe46ac10eb9571597c6f0939a58c9db6445e98b74c046beb7ca4bf327be598b957a357d6253636b5fe52386db3aaafd22acb112684dc0069fb27428d22f680bbb17e166b97e8e534ad64de15054a8f6b6fd3e2319c18d15e8269970cf2544f72a50489c80216ec18f57b0840a0ad9747e3b7a24c828384801abf7e836e084ce7d836cf32fc8f51faea0998bf2ea7a993dfd65bc12298a87db87c658ec637ebd19e321d55a35c5a6dd87e996dc7c96ff812597071f32402d5ca27e768c32ec92b812941742de049c390fd5a6842c258a6328808908c7a7ed19cb411778bc3331f5656c08c380158975441fcab7e030fbb1e91b9de47a8b872d9f8f8ebc0edd6dc0e1e2c77e61f449748f782ab0f3ebf4c661549c2e77171a81338f43140b58c8c90f4b35e38e2e13095a07f299a4ec34567013df00928650f635ea80facf61a544bd6f1c962854bab9617d9e155664ed466b03d3c6c599cdf99628a21b6a0fbdf82a9756a6aff1a48754ecd9aafe10b8bb65ed8c37a8677ae36220548cb9e2dc18683901bb17a778623a8b554605ce1fedc8651de9768fb685527c85d39e321933cba3eef0c4b59de3d978ff25af3154630c959c46d5e4211306b2bf81a48c7cddca51a022da5f97309bb7f25d15e95d25818caa87b692acea08514d5b4f6f346b3cbac8a7d554b9fa22454ec8c6c4a23ff173e39905a48545a4f29421a5cc2e313032547a21770435f03a98fe34c615f5f6d259bdb3d4209f5edc95db9793f84c1da39a5882ad7481a06341c5b02629c6b3fdcdc44a0fa049a8eb908991c2627e6128e8abbfedb3599b8bb246f961d6887c379ac7b91356b41e776888069e35f71d42fc1c9bff4a0e628463691bf060ecde44a1e8c3ee99f60cca44010c90188ca5e06b5a72b00969ee7b63f60b592503a623011230208e0b54b30311c8c028a0503955ebe73696887ddd17740e340187b682715e0acce9924729458223b7d1ab234e682f14242a12ddb3f74bce7d37e564bfe60c68616b6c965ee70ee56c4c912e0fe1fe21cc3d6c84cb4fc8ee08b2c9568fcf9c0ef0c0862bc48ec900585ec450e5aa7b693821c2830e41cd0713ed90da3c88080af127cc9fadc372d602130e82516d718839e36712da3518b788ada688858f530b9d1c6a03e66d9a750cf66f2fffd121d4a28bbb106fcd85e5edeeb8898f3ed908c30ba848cd7c0e33aefa86ae8850896f85dfb3921cc12e92a93d4b61438f9c11ccd8449c4a158abaabf5d09ce999fb29c67465d24d56df1929ec5f76e739105cd75f1682cd86b60a53989a398e52ded20398e7426d030a6ff961596933e13103e50646a0ce68587f0a941ad321eb4c4767ce1b94cfa664cffc636ca7b32fc8431fa774146e0eee43b45b68347d2211d5c94f8341a1a96a1a023954372c5bf23910833527d98f2e474890a8911709e8eac9718bb7d45496c54129473aa188a951d875baa6f75f45f3e43ae6eb0cb7d0f76c0d5b2342720c627e08adb41ffb93a1d35e85506d6ad30cf9b02caf04125fdf6875465c3036719dc02f4a84c4b1ac211ad441795745040e0a20685ca0b71585c5a2a45c3e82b3bbfae88318b0f72b2c4fd8558e16d12b37188cc04a8d9fe17e8905a70f554533e5b3f9bba337387c43f21404062b876baebb402b877ed9ddd10a7eb97a3d61e018cf7cf15ea444358f8537d59a7770796c56c3ba53f3a530fb1ba0b0ac47e295309196b91c556b7ae3d68cb82b27256c977aa7971a7ed7e2651d0964960a50923e3400516c98d50d958733cb8d48263a5a4296021ae86599c269a666ed75978c65033d4c7f6da87931d8d7417eacce986b483966f022be2d650586a69c606c98b14a79c01d95e80b307540be95c219aba33763c5a1837ed693ac9e60d532886b370fdf4173f462885b14e21808f9849bb59cc1d2a3adcfe5d6a2287f4d2e4096f89655fc1e2be9521bbcaf59557173e1ac8500a5dd27b9470634e56dd065ce6fc7209e5c44f5267179060e0e6ca5714e523667700e145740785441fad25df625c9dd272e7ff460076f60fd49ce6d533d54cda5fe67c848bad84119aa0a636aeb979e2f95c6dbf06d935be5e05f36a8cd065652097a182356d21188de7489ff9e9434538511f874dfbe9e0d97a5836c3aaac12fc2b264c73f1073e0b0265c494ab688421967942b405443ee8c04d78735ee2048ba2318852a61dfcd61398065743bcfde51de5ec4442149818746dd6a67a251b96a3c430430a2f7d4afa8d0cd29907168ea9f3c61d7479d454dfe0e4b53c45bb12eec232aaf4c0404174de7773b66581cb867c94408e578eea07d3c13819d3ff1f278f7c10dc13e8d5e4be210456eafe0a67d2f28f21705cc42c4da8d580e8cac285cc54344f879d87b4d64600642375f2f92ca7a21a761eaf90c4d779fdbb72f9d81e88b46bfc0b87bd6b76df5960c51d71a731f34d8fe1b16d8e15a310b32b7d1c1281508b7e5eba4cc6f01c82405b84d0610e5672ee176d79553739d5213a96911accfc22e9dcf928b7eaf5877a5987da6ff967f9335bb6be1b1e86a59613257d13231d615fa1e51864b1e5ea5cfdceb2fdea2fa195b6e4fbb3374aabd85487e0e55bf4ea12a017ec4b972b27822392340f9cd0ef5ce734454b67ba2fbf2c22920d75894c36cf071351f4912328677989586be3b820a052a04b1d773ce8ce614dc6ae28693b65195f293d65d827bc0e528ebe61e64949183d8c6853f369f36a5f980074f13be94b08a9715ebddab7f2e8029d4cb047fc6882f2057f937e3e9672b29e2a2d87ca8b025c6d526b57b277da7326fe0bdc3a5787c715265ea7c14f6d54bd3972a1aa0f988f0545624efefa1750301983922ee4ee5d7648016bfb4ca5323c87752002ed562b90a7067dbb9cac71317c4cbbc23bc22d0233a480435834fd9ff9c493b6d4c47b285a1eb2ac50e8e182173bedfd83a9cf84abcc22e70a31a44e2550ea0d027f4902e312c35d7adb1814167f882493310d20d1a900a1b3fead6253f2f0d8e6cb7b288eab8d04ba395cf0fa17c2d9e6cfd17574fc08ccdd9480186dfef4c6fdf9bdd269717516650d608397d52d4ca4eb82cf026e86026c107ea6041e59ada330bb5718725664e4cfad714c962c0561900f53150229302f1f7e53b9158387b755e98d35943a17fd2f79a1089e9afe3253095f99b05498aa914938b6a5101b55292ba73799803fb1656afe6afb4b6eaa93c1a886dc4934210305e813e35ef38ac7c3e6522af0088e8d97697c64a5bef23c154402b5c5e4a1e4f23f79422b24821016148a4cdfec69c4a006228511ba53dde4636d0412ba501531863c0213bc80dab36403d30b9282eaeee58a2bc71452761ffc8bcd3002b1d7c54ab932cc48322155b041d168da13a1f160b1d6bb3a33305f0e99483b5d8f59ee4db05b0bfdf46f66b6dffb3a8f540cee15c3509b77a68b0d4315dcc886869e2611a92d30e95e5d933ef01498eb40144561770ff8bec15ac6474806633310b7d54c34cdb2a21dbf53bcd33b04b9da55ff1877423c9d2751a014fa062ad7b9e686bcf8fb3b7b5b3207181b560cc9c6930123fef4251ee5304acd59cd3c9a4137a9afee5a8962c54d3fa1c3ff291109a144e654ee2ac7a3713776c48a7452622d6624a31b74f4de0c394e7df595687f7764eca6d22b362a01e2543d2221107d66bae23afc12f29a7445e044f05d42d2cf0fdc51c8d92b060b2a8475e2cf45c541568accbab9e1a196b4e5d689edcd3a50d6a665c9a871a4b9790ac1f55e445b52179512ca8269712b9bb64023f4d41ac295cb23a62d6435bbdbbf68082dba92d0444037be9280473dd2ed2232376c11eeea9eb2ac18d12cd6c394a4667394dcdbc09336661f5450d2ff145086ecb6e1fbef027c1000d2855e2b824ac602eb0d65acea27e20afc9b7b51cec30f6010d79653ea2aa5753cf9c6c1d4f77b2cf9899884d45613eb424068d5d5e671dde9117b5bc055247586decee2a915eb411ed8beaf4ed101ee06e132ab8823c0d684fafd3e632e476a577db8c95f960867060e7e7f3a6a9671d12e9717b6617025cfe69bdae9b83941ef6e745cfb74373e31f99028cdaa9858da491826873d35ff20213e306345399572f80f7593115387cad1e45285caaae8017b21a5c5a0ca182e7ba26e2b76a167c2965056667c9625cf175d1ea707e8b395dfb647199fd9725cf34b0179b6476f198c1aa768fd798b64fd9d0d7d28c11f688c59c489a7408de74d899b5892e6e7098f281c71f6d86e55f102f537be746439cc34d5ad3019b7c70bd33975f44b333d49a14934dba2c6a5a5c14f98db83d28c3fc5122a37a063d0147f50813f38adf603e072256e008340d97082763ab81d982c3c19a134a0159562aeb41ab2c3ad9ee3cbeb924de79cf6204164ac605fc66b3340555440c03baf27d88a230adb2c120268a62a766dff2ee00e19fd42103a85a8aba0baf30e22ed961ad2c040aec48949b8b59bfcdd494f15b032be6b8f6059769a7673e54a011645e4aa64a7895b49792e1f379f7bd5d492b31a7397944221d02239ec911c6d68752900db5f183bb1bec1acf9006c0bf44bf7125a96142c328257efcf3e2dc1eab6f76cc0cf13efdb93666b89cad0470ae263d75264b8f073a7caee847783eec8d98433d25b77e79074c5c7092465b0fda46c943f397437c362dbe12fe46a1add026c01c57d5c510abe9d488f20ac4753e20f76914351ddce3b12026068dd9b3a42549bdd640e0338290b217155b64700c75776cd07bf3dba8979780b6e672f4b60932d6ee8822a41c81e477c4e8100447f28e4b89a44e3ba9846496fb95c526e8603ae837a636feba7bec7e67c453fe124ff4148fff402582435e64a9148c56eec12e42c82bfae3296aa6ad22b04b3f3ee7e855cf136babf824d254b117e40c7b91dc449fe20016eacb9ecad08fa251af7497b2cfcf3cbdd41e9d95fc411cde10813b80fc8a45ed4544949ec7fa7555d4d255fe8a17480be746515c932bded288c7d8e2636f9c556d85227c8823a31d4c3d44b61cdf3077295ee6424ba0619d34c1b50283247e8a8f126c0ed6bfbec23c6a818b07742d638cb6becc7d8063ce7b929c8a37cc7f56089b79bb9e7ab637c394e2411b54f91a89701dee08ec0408c8bbe537b3e3e8cc0f4205b66c0564601a3beca0f20553e0dd17fd6f4c0cddb3d1b57926251a7c3fc2ba9041d45d4b758b5966a9676bb7673dc256af47add5dc38c69350fc7b72bb885f28f47c94ffcfa0d62e708bb0a5b5f7f9971f8b6499693eb3f394e5b6b4bbbbffa9b70c94f2f7466fe0bbdb67595f625016b9fa20463396d72194b79336c48734df647fee6cd822ba8a4208856e393a0c0658099dc67b66fabd09b67864145aaaa0fd44c5858999a4f1e597ef9898f5ef0a2523492a1ad9654f72d80d7ac02c341695018c3cf6d0e9f1c31202bed8ec5b97e485a8b2a165f29fdf37374f652968de3f175a82020a9e5ea40d99e71c2298e335a610fe858ac6ce6e8f7242c6fbe9d94e8eb02443500c6b6aedd046b679d1de75b8886de18150214b8cdb1e523ab9c3f53371aeed019b89e700440062a5b40f757b3ed63b6e6a4be61f539b036b45e849ab353403d78171d2f07e8da204cf63f07b835791b2434970214b5b4e243e1ac4bfb62c0668729b2a165ed20bd1021c25192faa164e1e3a8e28c749a0a7efd41b01eb527d2eecb5b640eced7bd2282649ec0010c75f878d39ef15c32104ca5509d05e0fce968f4450fa444e66e27a796a663719355e3ed15d0fef4441aef3ea8b8bd64e5d063cc56cb99a2460d6820f4cd760c3922d357d2bca5dccb303204d0e46ee32488ab8039825690f595427215ec79295c88a79f8bd8150d5112dbd06669f137b3f73dd95eecca8c210df9b88b41df00df3e0c59783c3e511fa2121e337cc91c55a3403dacfc78dd5f10e3aa1fba52f0156283b2bf8637c757c164bcc2c70de02f7d6da010617e1c18aec93aa93248e96ff876ca4dc88b7acc2560448d1fb5419a6a7b32764502fe41e61be0c36d70f82e9b15b4e4e300fa2a844020a14b6cc30f944817c979ab064f6b4ec83ea0a4c6c84cc99ce2d3902001c27f82c408a113b5803dfbddcec7e3fa206728c81765774106e0a47d4e63f5acb1a948615601d533cee8255e9e0a02146a7b13f9cb7b9420008a0992c932bcb97287c4c07461b270ba7efa003162025ad2100c21d69e8d1ef2a1efe0b113287867d77e086f536d45fed826f3578f3c8b49581b941cd5259afd0b6618f6c4f424f582004bb7d05f1d129ab0e68a49ca5d50691dff70ec66a43f9d04487753bf9d28cf5d90a5ea328aa1555bc5679d40ae7594e7093a5ba07cb3aad9020369b4661ab7e0bb24b03bece845f0b153538ed824b3be742184f9e9a97e16f52de5d3a54af1574ffc34b27d96de115f72031ba014483029acc6d6159304a71261d1ebc6a75e5aa42bd786946f1e19f7f4820afb2ebc6e25e765757b252c115e5f2a12b8a0de98fccffccef0f0e0eeff4ea70d4e6b9ad22616e0b58d91671d431517f903254fd120b69846c42f6de646fb9b79449caca069805c6052cecd052e1c98b0a303e8bcf9018725862b9ddd70a641e460bcfe6ccfd482197e6c96341e1bf10c94f3c5280ca6125060f2b4c2dc870c691e185dc53570ec897b7a3258a951c15e5c12cc945299263f8c82ca29e0b968c9c15d68a1c123c19c7d8d761930b3b23c685a7c245ca959c8d2939344c9ecfd40b01b245028d1466e4a652689173a3e789ccf916dce450f0cab5099355b09099b0bc53952a554a4ec84d0952828e3f6157230765f7227766861fa1cef708f35223644c6e0d578e05ad07dbbd1a27b91d497251439e4a407365c3ca5553903ebfc50e133859603f55068797dcd7566e0c2b9700e54b5092bb63020c65d8e37d94e10daa2a32726caab0c8f9d0f35774fe4e580c5fb9276572337cc92eb63c922b8f0295aa2d48bec990393c7260377260a8c8f817a8b0781e7ae0d0c9c509cb39f9aa2ab3e48b8b2d2aaedca012a7ea8d20f132a40e8f2773836b8a8c9c95292c724cf4bc159dbc222cd3f84a53e6e97c791d5b72265cd143e58754e5f0205961080c3c72356eb49122c3861416527a6ed0f9166139c7571ea1cc9e2f53b69cb9f22ba87c96aa9c0a48726f86b0e051e686581419df250a8b0fd393d3422749d8bff92a53c6cc975c972db9315780509152f53590fc93214e3c722edcc84d50643c1c282ce6e8c93be8fc95b0dffaca852943c217154a708b9c12bcf2325409e224a7a56404466290caa9d93102283332b649bd5e3c6961dd5aeb64cfb67fdd5a9d6e0c832fa755969ddc1402b85cd8e962260a0b221be01a33e6c79616b2ec2172a768d80ba8b3da0511a9d00d77c371b8e138dc701ca8e6426db5371ca5e2f5f2fda85b0f8fd743cf9248f6ca3ca44f14c9100198a1b40fba4ec4a3d5099bd6af54462289f6afa654994b7947b0ba17995cb48e1a403f0c7de07768cbcd431eb8958ded4cefeefe58564e36ac9a1e4f559f6d0ee12863d826c53a1251cf2a32555cdbfc1815a41774cfdefbb5d65a715e6badda2669120746986606d8c2136e92e56b82f5c26b62e5749b4c39d9263d9c9a3c35617a926d7a3689c148c8640e0d92091b1923133323884cc0608cf191d2a458744a13975f94634fdff7820c0f638c31c6b91e250e634c35e57452d910f2714372d353499a1913933995091b334cc06491561e9193898e8f491313264c3526be10d6c0c8b2a8aeae7852598de3a6271549a5e3a4b2b14751168233cbe3c81d211f5818638c857cdc257da891d14eca880518cac6deb58e7524e25328a3b6899333639b942b9767c98b9b4f284dcaf541054e254ae2885531554df98c3288feb8f10ea29a520cce25629b9e4beeecfad48273491798ac6d8255363d9724d9e6782e21b2648705e894911190352110394640b6422f372b46408014018128f9c12a00012203c88d7d7ff6350202969b9e535d58424253609aa6bab8983a61f814941b5ed57e5cd6bbded3a0fa19a68edc0fb7e93965c3d3787dc075ed3dfaa60ff8dcf8a0025c12f8800bcbc98898970ad5c71e1f23e423f62b868ceb4698e504fe8ff2dbf434b243c1460f1b514d355cc478711a5901e32cc2a7c56f7a1639b36d9b9e45b864c95e1c9f7c3023d1ada318e828066316f290839090e3702546a912ff4702eba4cfb15b2db1ab4d8bede711aa0fe0b6126efb52ad21b888f5ba2055b679c3f987ba8190507db7b24d5005b16e7aa5f6ec3ba7541d0a889d5377eed6f40162fd34a5094a756df3ee320322dd669e53d67af738b64de07dc246d2b0f6898facdbed264f7cbb7e08d56b9ff819d52084cab44d3060d76762da25d3aeef4d44ea598890d0907b857290a1245feee0f87211640c57f141258531318048c15104031603f8494662947b18dd720fa12dbb36bbf26a0e01aeb0763b76418656204ab24b61015a80a860f063776567b741183b25541589618d113963b64ee0a2a5a4c98a858d31152d5a068d8f1d03ae60bce8b3b3b19be1043876a882e008a38207c82ca9e1e5048b963a3fd8e0295a2e7821cfa568c2213b4fc72e01b50390d981600cd7d3140d2167dad8ea204943078e0f34555c602959ce9dcd827c55c1553563802b26bb2c3bea45861d0c76147025c36ece4e0057576b84ae6856bd0a23664f2914b2ec46d879e9ab29bbaa1e3b2ac7851918293bdd95921d941ded0a869de7033fd1e254ce126ee8c10243943555b2b0b88911e3878fa91c38ca5977d44b6b87633700dc95102c1aaeea1bac154640e35430caa5f2d98da9692819a65d98fb9ca97273660a0b2933ae5e304106254188e491d282a16889512d16f4ec64ec8caeea131d2bc0950457580b6c0f6d81cd8e0d88ab9a55e6fb42b75ac0f88913764286d2d96db8128118148b3b7886644591da3105069e1f585444fdc083adb4c65417557633ec62d855d958abc9ae6ed9650080ab0e575a7670dca8a901153b0e04c1300cfda910f626cff32e8e85277f721adee434fc494a1643a6de2b2398120f98364ee304e38594d5a5abcbb9eb0ec130c7292b5218ea5a350ad78560ce68ca1a443f7c1a441f7f107d2f88861794c974f825709b3ead5f5fac1945ce6d4ad9d3ffbec4b84123cfe8a31587e20d92e90ebc2d7b99d6b5a95e61deac51c0b082a9626a578df2dabc40bcd11b8ee348fe38ce7e1cf1f895aabce1bc7f212f920dd90fb4f7be1a25e0287320c2035aae64883ca6bc6f96640e7f1c35e5c01b31ae465e240b8bc2f73e3204f87563310fa53e81799f526fc1697bd49194f46ca1c70e3f7ceff17be9b1bdc7615941845037388264885babf574162b4dfc1fce5e25686561dd38d75d520dc27c77e94521f856f22d7a98bd5dd6681148865f49a4a0508fa84123aeb7eff63dc237098c317e509bf83be11b8780edc5ba52ddf79756eb7766c26eac29c5e0d3d9abbc1e1e33fae7516badfdacbdf6c391d5f4c87522e8db274175927d0e5ce75584ebbc4afa1c88b8d65a4b87b21ab3bc1deeb576fb1cb1e85eeb2068ad4abe37a545de839eb594525a9d0f0a2f024f2ea7b38b180e777689b2e9d965ea64410f7e145ef43daee274288888d457905a41077d318337bac771ac4fe651f4b21fee2aed0b7937591ecb94e6bf90ce8bc0bf7f64c907d04a8aa80fa03bc87ed27f5e7d865ff4eaf5193eb980b9f7dc22b6ef052f8984bdfa4c5665fa82a3f769119d662e799ee7798ff187b59a499eb65fa283d68bfcd26cadf583c24f0a956abe5f7d9bb7380d918e3583ed718363edb34dd43e2f6613b745529c6524ba6711d0b3bf3fd3a6f8f53dd9f7b2ef67da8a1f3e9965bf711bfc96c4ea793438d69dc99e6ef2fbbecffb9ce49ff87ddee79122751a6425495127d59124499cbf267732fdc95d362391684f929f3f9164884a7aa63d4892ee447a8ffd3dce49b51761572fdd9d3c8a2c75d2f835ec342ad057d3f71e9ff677d32658827aac8d5f7bb3c47750fb7450edbd27dfb5598d6c3f4e1fc6a6929bfeb4499244d2f055279995fc9f07b2fdadbef7a47f2289645619496224f567d326189221c4daf5ea4f7b2fd7328bb4ec7958d7ffde2375add49bec87c7332dce7e36d3f5c7b71f8c6c87fb6569e4342a888f7cefc507496d3e36fa7b491adf8f2fbe3789e27f6408516694801274ddfd28d78f9dadacf3d4d2620bcc3ea36825e9dae3067a27a3b610d122a6a58a96282d5cb6b8eb0866694f436f1a4730ecaec7f2abcc9bc611cc8b643784ec3a95e7d2ba36afcc1ffb6399973fd6b21fc99c04f4226ca73feef5871cd55410f8ab9293daf40db46760bbda6622f6eaee5827cd1ee81d7fb5fa7b5375731cdbe6f739c96556e64da35705217b726c9354df65e47fb2116c7f8fb1c6d85a6c2d9e692cb3618cfd4977203daa9179d0edbfc710a8ef4da38c24c7116ce32f4b20d2acd87eed9dca7eb01ade2c6df50dd8652489e49f24d3f847dfe058833e4b3c90378d51bb66a7ca6ee5515c67dfa8acef9fec3189e45ffe97ebcbf2f73f792cb3f8364b13cd1295566b257512f8b2a78d6abeeaf48db377dc1ec959254972a6bdd21e0785ff3d194525a37be81ed39d36a90380a72c5102b7f17f3f6259899936dd697fa31a528f23fede9bc611cca9dccaa94635f6a76ec03346bd40b000fdf081b5da1cf70df302210359561f798170012f2ae14538f7e04577d7049c5a50db6e7a6ad5d87db45e6c53f74a66c85da993db13f626a8f93c63725d5c39325a5ebb0cc3c9c748322457a484ac238b8dffcad2c4a54f4ecc4eee0b9c67020b53e6052fef642bd7072b57056587922e135e4f8fa7ba91338265841c1f2c2a3ec99de792935baca162c60d981c912d24a8f0635266509d6044cc8fdc121c5ab29cfecc621195e7060c0fd52697054dee6a045f6405a8ac9c1495cca3499e71240725e5428e2a5768acb9e2e2a1ecf91c74b0bcc91d4993b3232693d025d7c8b2bbfa315632906419423e9549ea8e0e3a2662c693ed40377dd22135857594e97736d60d39298d44540027e088030f3ef022eff14bc08bf0e3fa596bed9783465cfc47ae8d94f84100c1b2a47604ee5ff728bff72c53cd276012edeb134ab344a1a34a93d6f96a937e529aafa5cafa9e27937d9f07be97bf073dd9e7895325f58a2285eb64b2dc81dd9f5726143a47e9295c67df3d50f789babfba2b89f508dfdea3f0227ffb293c50b7f79e52097a6b44ea6dceb0aeca65d3363dc3aaec7a8625e1b0e919f6648346586d8ab08b6812340965b03601363ddd3c6d917643fddd1d5eff34a8ab1722af714489679bac2d3e79b6b13ad9b41947dce3827ec36b90a0ac5f8ad9e54b709e6ca28839d770cdde47d09e2cdf4682f6e4d7683f7b40d0cf6c99f6948c40dd4126f934b27c329b29361934a33ddd29667a84ad7c9ade298e2c49fb51f24da3cd949cd44a5ed3a6928fe358be588e63ed4b721c6b5f8ee35823c7b21cc77cd3f48eeefeb2d147d98faf7194e17fa967e48ba41ba67dc1a6671b3049e48b4e4993a41da92fb299ba234d9b4a4efb9f4f52f2fdf3e66cf6970c61c269da04516c0e1f62fc06b5daac34c937817f68237c8f20df699524e1ae695f66dbd7b22547d88e6c2dbb677feaba194d2791a6f8b4dc61a6960ce15d2552ca9a1e9e5751d828d75d771bb53dcf7b7bdd0baa6670355dc2d5b1fca3601ef6ddb499ccdd933e770765dfa7e49eecc537c351d44abe93defc6fd4e25005e12fbba607ff3e4d44873e22f0f3efcbe18b62b997528020c5da4963098275a882f8defc7907c51f4ba20a027cff6ca54d9b6efe6850fc51fc464d5433f0ff82c6f75ef67d9fff9006dfc3366defbde9a1ea24d913d9b4926fdbbb7f9f64f34f9b1e0829189889e81533e843df87ded7b2eccb4cfe91fd886495e685443808fcf0e9c6ff75307bf1ee990e02dffbef3df36ed9939f378a44e2cb9e125510d443982f26aa1978fa9695d6e3ab8a1aacaf3d7b68d3f38bcb564395c604307bdce2dbac177a8081815981398165d9285aca7e0c49d2b350b66ea822948d1ac910960c216a2ae60d4f817e6a2f8ab556f1ddcb8a330a5a0ade1eb9f8476ea12a08fc560211fc9988f39a4d1bb5ed0395b599692d0eeb24f0675fdfb4dabcf5b32f5651b452dbfcc4fa226c2b92e2bb36ad0e5b25f00d6fce28296ed049f6675ac9771d41b0f4700535899bd91729d04abe41ab8366ff7df809829ec388faf6dfbe7b79117ef1ed5305615fd4d6a4206ae3df90cdba373c8d4412dfea19490273785bd004afc1fef758a400d71173d0262e488bda54f226d062d6b6f988031155dcf05fb62fe6ef25c8e4e332ed8f6cdde0e53d08a44db0dc9eaee9127cb32c2d54ad1fe8edd99b24498a20293a58f9382c7590ccfb52f43cd274b04d3e0d6c7fb3231c4e5ba80aa2ceb485aa1978916fa1c8bf6408d2461d71e03acf425109ea0638353e29cfa6e9719f701a166966cce055e762fa7968f6a0717a23d36609a34ba78e14176c3ce5a841bbb8bc0173a6c90b9a351b582f34dbdad38bcdb6169fd9d6da27eca5c52bab8f68d60ada0e6eedbbd5d476a89b856e720ed031d84c1e1c6144f69c2c7c31e12e8aa6098e10246eae7cb9f9ace75ee660867d2f9a11f6bd4722ee7df3b33464852b5c4ce43ce551430205aa27ab1a5736a6d0dc2f8ccb7c19b5c65b55cb3a816a15a962e304ced6961cfae51ce3256777c64e923351e1191fa411fbb999a63dcb23aa39d3c20c94c51c70f75e1d4388cc931d23a0e34290a8ad2e3c4b396874e992c587971a7aeca667979d930f1fa3acc7f3e89b3d4f96c009f325430c0a5069b1a58a880962c2c8549191c952c363ce2eaf2e30535ac55966ea2c8395e424d3c3c848af0041a330a07fee493eba56e1d929a9ebfb794f49bfa71a7c2644286f12500dc0bf4f01d500e3fc6913757fcf00ba93be20fcd13ef6ab76ea0cb3e70c3302addec662b6c5569b9f97fb541ce6141325c6090bcbb37f5d0ca4433bd06dd2448c008c76a0fbd374ca0d9b6ed7c0be1b76d520551d18329c0ca74c1ef37a3d7f95f8e9365f48f705e11e2d4e8cf0b0a1044d05626145ccf00012434d8b2b9bb4dafa02cea1e0edbb296c80a972e2664a861a307898b083079c226da45c58c9e37d01e758fc65d780746a71c4ea8b29d91770ce8b1e2319383a081eaf1c4fda70a5c94d44f860e1603205431571c5ca921c5ee4506111011f6ca8a8c0b362cb4a4da14ac54d643823e7c68a2a604b573882c06871856307151663178f69396651c64b4e8a1d2dc89084cd9d2247ae706911c34347578d231be0a756367e31a516a726594db94a93c5c38690df2a22444c1a245a68b8e0318313022c45985618638565cd04baf4b1f2029c2f184a60c42062b12c6b621823a14b921a5056debc007353034f18212d9ab66815ebb416e0e9f26c18c65b2ec66cbcc56267c32cc803678c5a0a82bf0652f58fd22c2f78b15702b93fcafa633044b8ce62ada30cfa7e697f1e870aea866aadd7875ce717e8bcab03c1b2ac4feb673febe9f246695e6befdf5b49fb62adef45ef3adc42a43e566dbce9d68a246014914db756446df36eba22c6f636dd5ad162d707eb0f1575b654b431522166d32d1551549cf735a0e0d310bf590373ae24d28b137837a94f793fe9fe9bc86deb7547096e5aaffbd88fd5b1daa6e718d598d498d3ae6608b67dbacd14fb49bc1f16d2d9f7cddaa6e7981e300cc3f03ba827eac6f9d222a0a2dbdf1f69d10f2db2fdfd1b2daa15955d7bc4c47695c3655bb0e919278abf9c2adba458387c9aadbcfdd468b71f5bada48910c8d168b457c26dda535ad401da4f8d76b395b71f5bada4d5d97bff987480271b818080805e09b781fe967ff2db722d7f99cbb27c25dc535ad481d790222b65dbdf3c290d37db67a082176507ec075abe877b0f57d6ca9c01d779464643433addbffdbe0217de57e0a2fc9beb97da1c3f7c8f840003fcef5b7244fd3bfe4822898f24d273ec6b2b793b49f6e09b27ea16bfe613b5c4edc096103fc844f52dfa3eb25fd66d2f74f7cfc06d7a9d47878cb04e2e8f7a455e5007db5ca26407d7acfb0406f02fedba4f78dbffc7297051ffd6f16aebbf4de4f67dd0ee28ef573705a9907d225277cf8307a221224a3d7859e3e45e16cadd46ddafdea520546982eee1aab561183e52fda5ede02fed4b5433f0b61cd18e589be1a8cd1aae206c8a3106b35018f4eb7d761cc747f21f7f697f389b5fa98c5a28d7e14fd37ba19c719ee24cc52112472a491ca6fd59eb76bb08db9ad76a73a966e0d65e2f724d9442d7b7d66e6b3970b8edcb07ce9818d81669d5f3b087cfdf2343b8761244f57ab62eedbf33c40ff7a946c1ea24e5efb2a4cdc4f746f23be2408407b26c779969792c6f380e2fa49b400b09ebae3914ef08626fcb3a387137a8410d4ea08794200e0b7911feba6999ee197ebb6b264984daf3be1fdf8bc0ba8110397ba29ac1f8b371f42feb234b92dfec7bba4dd183ebbe233b92e4e85d5c0492b22317bd7cffa6d026ecc641443583bb3d0faef34ce08d1fbc561ce237c9feeb22253dc540dc77f348dcffde7aef3a0fecbdd5a7442da494c96cf8f691c2b7bfb4f3f54b94699f07aea3a48b701d254540006b6aa91652dea71e88d0424a4f68532da4a42fd22bb424563b255221d33281a33e529ab88d2ff8beef7bfa5e4b216a6fb3710033ce75f6695ab47948a4771cf2a2212be628ed9bba7d811ea245b77b3f0425255c498bacb5af84e360b3d96cff3daed6ea791f8292126e7b45fffda54520f821282939fd0dbf2d3b11d77994ebf0ec6bb6d956af8217e5e9ba26fb0104db61ca7ec03d1e3589cb5666c350f6a286ecbf21572ed731b9ee8e8dd2ac5cb5376b9b8d311671f8297530f7b250dfc53372c4b5c75fc5d42e2fd2f0f8ab97b821ef6bb3d9acfbcd6fdfe5e03a7c6f052ec298c0b22c6db67b6f3710fc0bbe197b53b56f4040407eaffffcdc40d8b75b061f28976fbbd984082690e8fe799b8d92216e27eaed6b35a007b2d9fe6613846d03ff96cbaf650fd44c6bd95cdab517edf853ebc0df7b24f732fdb2ed97b64d7fcde0d772a939e85c876d088b629b40b1eb36974ce021d7611324bdc9c46d31ebb6e94d5b0782603f8898d1643bd08d3f9770a8644c3867416434350102000316002020100a074422814090c548468d0f14800b78ae425e4695c845722045410cc42008c3300c000c300418020c32c64887f201849bddeceb4c1ca786ce57b6092a91d09edc814e197466778877cc834b701efceb2289c86aca4ca147cc569d2e87f8a01f127c08cee58d816d014558c6f7725e862508de884d1cfa16fa6610ba047b327d62f150c40861b56844fa32f4576e3e95288d970273e46d11a1d450c492b4e8c687c5cc33e1b1d22426e76ffe2ee21adb498b217691b498e0f5079aba6733f8eef5ea7a2276c792dfd5abb2c5ef471c0d1c9528b93afdf8e2bc6ccc96c94e48ab6d410ae24f22bdc576d28bd5edaa957d6879faa851ea9570b25c685761b37f673d1021ab5bf19e7e6307295f0734c70d41ab16e7ad6b940871e597309776ecf368df5daa799cd39fc5d5cf26c249e40ff484dc0d6b98dcc0e7d29ce0c07729bc1513d4206c03a2e99900c4cdc0c3991dcb6a0ffc75a41cdb94e53bc64e8968baca0f79e868c00c8b4abe7d28d47d7f111f187d19e1409eca60059c9bb093bbe8450d0597039a5c61cf1623345069c20a5c429c60514ab1c1a5581b523f6efcf6c028e6f9d15aedd9782d2b2e3c6b5d640f9379068e28e876768a641b989d3ae147cb4c934c911b1de7912de52152ad470b57214d89d76ae548098630532b9228a58ec71873a3ea5cb7aa5929d4d62dc9daf47de7c704036a7314b574ed884c70f75290ade3b1325f35be291f734fc7521fadcd90127f52e7cfdcd5a9c425024db91d346fcd54b7f70ffaa79d682cdf079526673d093c11c728e203d316fb4cbc89c4526a350cf331166722c40e296a2ccc2e1cece706f95a0f4e444633103f9ac0bfdb75c9dd0675b6e666d7b2ca220f3e3620457183f17d2cbace07d932c9c7924841d91c97432144003502fd98a7b6487e064c917f3b012e5ed9242ec5c8784bfe525cdc8a4ec986228c5b221f481e1b17179c29b40ea9bcfed770d6425fce98f9a0d7796c3bdf9208e629bbcf442fd0bca9bc3286c8bb1f4bcf225af19e48bf4fdf62f550ead943d9ea2a77f6521f9576e98213972cb4828b81b6dabd2c0aabb4066c631cde940f8f022feb4a73ef4eb89789aa703d39f049d03655f679cd7e4c2e523dc910d5adbf48359149eeaad9366ca35f2f3eb6b026b13a9694d34b61f246287fe0de97a4ec6fb35b4077828ea53134a9d386c54a54934f523ca303ccc9db26629202462a31f2a7c3bee6e031bee39abdbff1b7e2ace1bc42e1594181233ec54cdf759471a47e010c5460533a2e73c1cdb4565a3b5af6e60a71a9e81a8702229949a0289fe474ca2184f2c32b7645a135a57e68606b1b29cbea925c545c2a4f8a6d94667dffcadc2e742d30fe373ec44dd16a98579c5f2824c1e3cb4d3670e9b72ec7e21e8ad8daa39b0d66350964a1b24f3ec3a14b50af193895d3ce21cc47d17866ce31d8fcda06b72cfa01710d2113ebca854e8e40b33edf96119160f61c878ac46628a77bbb9be40bb6deaf650d7308565f06b975487ad32e9c1d59f7834c8aae5d3d494b95a49482b13260b97989eb5fc4b0755cadbe7e5f271d9de46836a4aca189ee587e8b2c8d25d7c5e8aeb1f33b273de9d7931408d4da2312a7cd244444eda2528c8648eab0c0eaf073e4f3e9842b0671bfa2a40127193b3f7b9726776331970606765f32a796f11b3e0bd571233bbe82f1033785218f3a503be05a5d49a5017ca179f639f682362e245a3049d205b002e77239e4ded65064514459554000bf4028000804b8a89d757921e12e93cae5a383e39e8580ba82a3f176d7847ca05aa65ce9fd20e48234b5cf5b18124097af9d5a393560c80f21282fb990a13f45476c1f78fa0b416840f68b6995c8a23bda9901665941d05bd7e0290b41238fc95f603fb1945897a6bb89ce1bac3762ed8b681b94ab6c690e8b9733f0b772f3e6dd20202e102b36aa1da530f62899c0a40adbcf1b9b78a6ae18b98dc219c8528cca06e54af47702596508661d839426432077c6867b28fa30599027b97416848e127a71c809b0eb826cb428dbf7a227923d6564d45552343bae9d4ed2cdb83e2e158b296587490fb887fb310f5541477e1819678d23569a724808c1d96795c3c56407f201b1cd9c682f7715a70f78f8c6c179856b10fcd6403cacb504ff84d01b43fd21056f6b2dbc3e2e1b048509f8894d1216310e6f590376d785923e3e3a695eac59a055de07073a72f720f09bd8e5dea7cd0d32ee891d8a714d740d79ac157f773e1e2d015877f83100c0ee1ad5d3422d81a6ee0ea7910a2dc516883c8f9ca938a329b3d0d02a7db5c12b1c1590df824acf26be539905efca8d42ece21284d0afaf703539ac0acbff7eb732d5d209f0528da00851af6b502fc54a54baef5121486184bbdba0ea3fb649683c9db5dca8392583831a44d8dc28226a80a47ce89bfe8e9c036996d61c946d123c29af88e47ec488a28230b0594db81dd4f89f11ed3db94c5c6338b58dc092fe6f9b79f8867462c375b7627a7561955830593ff5bdd5040cad8e8461f06d75a18afc04ef043e17126ae4d8984d4c22ce64a1407084450a4eec3496a8310d466e0e9c356368adc2dc1b20803846017f3178e69a2d66d6044c91f683b31f0f347e2a18c9d81ed08af57b92e4824c40ab0d9b5a55c7ff7912754241e52bcb3c292f10b694e7de3543e7b26385c96901f38a8c3a06d94c54e4235c000afa787d840b4d6af8b4f67e3ba6bfda8b236693bb7aa33d7b0d9fd9858cacbbb5058613b5242e32aa40272b19d8a00d9554c44c6f617f453488ca8979ae9e52f89d8088607a9b9892d6cc903199bfaf6deab098b66f03c90df2690bed4dedb183e50f56dfdea909c5cc0fbb186e4fac8fee1561762981702afec803b1a4d0a82c38d0304ba8aea3461c92748e58cee292d48906c8b3b84da5e507fc4f11e5ae24a9899d08e7d3176362b4cfccba4def8b3b3049dccf89a923c3cdb1a1cb4622a6d37661dd4096308169b001be71bbf992770a70e134f4423e75720c5a013d68190b425a892d5b7292054e3fd7bd566d1ae01387d1f9f561552fa6bd71768060fcca116492211186d06ef92563e6974ba706b6ac280b8ee639b9e08dd063ac0e7797623521e0b239e4e815ce5060180b2220be643138994ec5ba19b3277aa2e50b78b1a9eb11c0d70a918021982c513fe2203d5668f67fac3dfd75b5505082fe22c00265368978b5c06d2fa9c92e4f98ae8bddc575d07024efdfd6727f6f835349d907c4cab8fd13c4551c5f97a67a97a3b14d0cdfe46c53b91ec50241def48c835c01ccd60dacc9cd036c3429fb524a0aebf2ceee509ae4bb8d84e791a75548822c38b45b8203468aef0a80ace06a1436cea31ee8a2664e53b07bf4ffa1567b18218207a6c05ac633ea75271cebf6ea4b3575e30c691e259c8575acbf752ee20ffc149c81d29308953df6a3e6f7b5e6c63e47ef0fdba6c2e5061408badabf962abc244c0c9e41876c0515ddc0a0b7086b286635d86375a379cb1771d00d92dd1ea089fc14586768ad8848733adb74956417cbd645f70c5d832d2b93dedc313afd64a311e47b5a06a21b1b097c6febf4dcd3693862f087adf4659782ae8182e83411ee63482d31e50c5c448949177ec8ea92a6a2885dcda6bc635f3d6f2dfd3d583bc9d5945b892acab9ffc97511bf189f6755c8edb44cd4ec14e27335e48e49df8b2d50bfe4e01c9f6da5524a3b74acffa09e5a8a74cfab42cd5ed9bbc4026c934123381e475345e72563ca57100eb2e234990e2aa67f4f951c64ca600c65a882cc0085676caff0d09b973194bc277cc2a159a8b2658767479e439d8c639004ac6560ea42ca8b8bd2103f8f564a863f738e63325f36a18f487429acfc8917a1b3bbe736df8a6007033cfe76d7143c9b73951a4570a0b19cc4060e77c5b465ab34061b285d1313c3c341a7933cd02f360d17550c2e66dfad07ea668c3786a0b43271c2906a1eefc2b906dd50dea0d2ad95ba9b6897def2b8bd0b9a25655199b7fb19ab8939f0fc47bf2f5b22d956fef6c2434ed0503685285c1d8a16b0fb97a68e36f18851a2ea35cb526506779446e3e00eb32eb45ab107251a95bd2084b5899aa5325a6c4bea9079dcb62c20e5c2c36c1394501f1f80d8d4c53411dbcbe33d39e334ef174d6533db345c8a8158e7c8eb11b4dfbcd013fc176a33c35007ea36521b4af699dd5809355296b6beac95bd2b40944b3cfacaddc263ee2bc30ec64eb603a50dad9e2d378d73123b0d9dcb0e983ee32d0bd172010d46868b3894d482b0a30a02402cb4f973292fd5c5ed29675825a01f5a0e8863d8b037a44fb9f54862db277cbb2cbc5ba546944f8a2c3a743ac2b80569c96c413e3ad730f202737d155f6a0609277ff2cdef100bcdbf8e3ebdb597af454da226c37f74c875a5ff3caceea3eab02f4e7c2727f90817f2391398d9f9ec67f2e45c1d67bbcab3e919f147e7998b48422f86cb0f2e7173dca451a703a47a87770295ef8a4b96c651eb85f1163ffe8a985d8f65f4884bfa4ebd4acd1208174cf72c717c7e19142010a534091a3fc8f94902c70a01d647aa068dac9438020584ee89d30cfa186f3d4e6dafbce2b730726017501c200a4f2669b56797ce0894323b9ffa21fc7df6bf8acba6610cf45b0b5692b20ffeff05657c4f9787e398968790bf7ff0e296593f1847de9851f7ad5af8b905d8d42212e062b4f1e5d07bbfdb2d3414093d75fd02193ebba914a611f8c42d27d216e855e8b4a99d68a7134fe6449b75926c93f915871662dd6ffcc9c6cb22c648e8e1c574b54277541014231179a5f1d4309fd9127e7854421e48232c6044492d31a2a6e08d9ccebb4a145500cf62aefe1798965181fe51b4818e0e5342fed1466c4c1db81fd298381750b803e9cb9347d9161d331827c68cbbdac3a7bde9be1a0a41128c980baae9f491ef55cb17990368adae2fefa1db8fd92cf808b6c04ac99364e00a8f3a9e2e24f33011ab1afdbcbefbbdb13ecb699ba36e15a155a113603456757f75c12dfc9824e361b1656644d0eaa68c3cf9ac0dc41b8484a0fe470ebcc8df402853f89d998e0dcbcdaa93108b1ab0e18d9b763dc9bb4715ba39f11181304199203b307e41a80ec8563e9bf92709f3110cbadbbb175e5d5d593a5f7e301a67ab25c97388359e1b8ca91fc2bdb4d44f9dfa7069b36f57bf63279ef74b4f811befae44bb4cf85d0f02e9b9e7b61528be13876c836046098f29c1d0acd35daebf5c103a96f921b213ac56dee8f4703059fa89d3fa0bf97bc4d850cf46bf019aa3be5e634ba48c15aab2adc9e16fd0ef06379595ae93df6cc8c675f61c3e90f4890611094430d3ac093a8a5fbcab994c83c1077ec10e68a948249355953d24bdce9a83a60161a2c6f363258994b44c1087414d605defeeda6bfeeb6bcd60ec5438b74bea143eef317af97e5094ae21b3481fffc6411492fd27a04120cf7fc769fdd0df4e2a67649e21344582076b74181adabde22befd36916165c048f3a760f5a1f6bf56f47d36b30d3388ca00cf656374a1f14bc053379b28508298bab778b81a4a78a8804f745d106390281ee54f8d6bf7f2a2739a12ccc15b45fb99a397a7f3de2dd84ef8567c48be33de171ed300d277811a010bd265dc1991392fb50f8a9834d0112dba34bf6ac8de7ba9d6934fbd20cf6da86fb78cfdba8664c4dd7421f692708360c904708e9cea6092f21fb549f32a0c7e579c7925fa3e761a4257395688ed0f0f20e69b600729fcb2c7a903af85cf8b63676d073d2faf33673e4f0b42794da182fc1b9a8fd585e70256021aec448b004a690d65ce3ab5c48348107e7f6e75d5b18e489fcf9113eb3b884482f88f441406315bfbb343072a80df622166e9dac720fa208b0f862fc44b3a483f4bd9eb2149544ecd2285da62fea563712ead3ac99042addb80a15175c5848f146566a20268bf51bcd1067e80e6d54eb9e3c8a7c6b9ffac4f6711f92d1f5a56a844c4b6014ad991c2e76c2b871c1bc0b1c333b5e62e51529c3663a3c444e69082384ed46f1a09d0b2b0bad6a884bb9e0ec2d7218924e61505c96682bc5ca767389a90459ad1cf4621f11896794d213eb9754ca8a907a4db51b93256db32ab5b10b0e4d4191e7a56a11a897b1d7ce0126452baf6fd03a81bb42fe6d2a858b3ce880cdf4c895ba98e69b62cabb74e4bb1e4e0dab84b01920a5231fda86f3ac13574b4a0993804c3794f097a373ff7b772e05802dd12150af0c8e91e48af3f90495d48a6de110326812e8aac6917bd109d2d866db32a24bd229384060c301a550efa78a75b417f4d6599278a12b2f738d6374492cfe022a2f6481a862d224950a7d3041ef50828a5aafb774098ec58a96ca77174e72497a14384cd21984ebcf316a383923a4820db0c6a3c24631536ec0eeb54b01ae61363ebb45f180aa1f8532ab553586c8bc9e04a5ea2d2fc0bc5e166be42f049d8db0d58202a4af69af3719b29f99446cb42baa297b0a6c2f3e4328665e153761b1565a447566921f4fe566031ebfa78dcf6c65135a9159fa0367186c8986233c503d521223208237911a52744752a620651c75245960c34d801f9978ae27117d5482975d455b86cdf954d7549a97349d0d0d456fce927b24d7bc99668e1838a6cae89d895c4730b931aa176c51a2bb07a6bd3bf693461886a31bb90be7d0a4af91794e29c912bb764cf2981e2acbba572193deea29fbb65f249e003092dc4aa8debd54abb86387e5436d7b1b2662301bea43c48fa51c813008092a6d2027075df5b5154d268a217ccefe2dfdf8ea905d0beece8548e13a7463bc3e98a868bb0906523217757f54ea813b4114c4141af5ff1e82b5c57d304ff107a48be093b5454ee124321896c17a52daa29fac1340ba181ae2ebf04651d7078e7e595bc493c938847a0577f20e4c273b36915e563d31cadc312e6633c8549e1789e2b06d7b76086026426777d0b6acc3d68e2adf5e4d68374d3268d340e6a8928cacc2f04e66f761862dcebb458300f7d1028b9c9615c632e77583a8fe029ec153cc36f7a5d546f90cfbf121b53f38cb18977041d37da05685b893d3b1702211b49a7f4940812e2a036646706dbd0590487ec8dc3ad31a51c66f4cb17dfa4c5088149a15214843009036007c2b21d95a83eeb47be4525af8c551eec3a3943f07058e2fedbdf1b02050f8a9360c40d39fa9208a327526304891692092b2b4fa6729cfb11a0f104998d50298f5893b546880cf5181606b1283791c0fd4254477c9d37015c5364a88fb5c8f39709a1930b9adae81cbb2a435e96908c290a1e6bf95c576402d40ebfff644e707d2ee8888a2753d777a830b1a121aac26386987970f943eac3a5ec7d2ca04a0e88286b56c8150a1e5522667b9fac89ca62b8b1d1c2d6261df214c15b9624116032cce40a3031341fe59f139ce737713a463ad2b7f8047608cfdf9933c6d71838de3e0ef557283ad8191da010e760d3ab780c94116a844fc898b83fdd3164808568e71ecf3973842936c7c02e1a24d545d80adf9156107beb81a14ae0ad74653920a24cea7bbb15af2a88e417182914eb6e6c7eaaf15ef3e6e68ce111f7f2585518022b5403a5fd32f4c90d890758105c35d6760aa47e629d8a5742c8cd24c994281e2d25e846ee18715ed0d7b4cf3b5e9d7199d1d21e883c017dac85e5dfadf402d16666adb76334f9a9260e8a2b37a1276c6d76ff8b6fb073dcf1d5772175af5030c0cf8ba3c5f201c6c1c34f50f70c8db8b91f1af95a26eb09b88df97b1d9fd40dba1c96937ec2e6587e3bba1a0a14c7dee7bba8a2cf9743352eb6906eac68b9910b1138ad2aef46ea8f3c2724482a43e0504cd03f1891f36a0c3f6510327be923fd50ab9ea8ffc1abd3bd5f5c6141b5a11a722b6e3d95c731003d7c6e5718e0563c4d615fba7aa8d8436f665f4f2f87bc2d1402009c2c354244b93668dd85902c0ec6a375c15b2c10f253aa535ab7e75dfa84320a8df14d4501ad4f343171848db0296607f5ea6ca3202ab805903a0e763abf0efa132576dd4d3377eda40329f0ae7ee9e4edf79fb2734c1ed12c0f6138c3e2a70d06ec3869d9a6aa01a8432a06b5a2923dfefd66a59c4df61832b686f0820b71a4d985bc3c9587ab9528ba1e190818b44c61d5958bab6805d8b4063bbc70c7a9eb31ab002a626a7649d6c7d88d75409c8244d89964b8def779a39cbffc40c79407bf8064cfbb3436ad0cf642edb6d904823093b7ac97ad2619b9ccea049d73551d38fabb75714d186f767c23588450795926b7a63e80ef62ce47f5c838b1da8c2bfaf2dac1adb3dbb18d929056bd9eca6c950214a4d85399ca373dc0b6cb0dbec943c4cca4fcb378c4daeb2158f365f8438ec06069b6a16f64b4be0a87a4005a4f296952be4687a03f3888239ae1990ea8be37850c407e6c75244ffb92a9dfa8f538cc86623975554586a3d1ed4c784443663972ab8283a8ffed974a60bb4d93cb25935237fd4f3a78e4d34b1b4abfedd630e95bba5bee0c2b7dea51069e6a48c58dd610d0e4391008d6949c66dcc9bca180af30d32741194a36a186c30e38c6d088a3c499ddc73d4fb6018e659e6d28b835cdd793576dd78d1ee9f48a11ff8665afdde44ea09c227ca265af5a368a0e80ec2235101219a4038caa02516ac44c69980fb1f68481034c1e22941cd98a2d01d7b65124a9c2ee08a0d4a5ab03d917a01670b5a80e6481cc2e72ecac350daa3c8b0bb4efc330e3a909611cd6009e74036553bc8cbbc27c259060b7dd59795fd3c84d3f5c76676ffb4a2456d5a27cc256a2d0d98724c6eb61ceee7301e392ea357ea4dde04b1a92655857ac3176e80697da901b583e73b16d5115fa844d0df6e7c4c099a04da6f43786a1c6327b7d9bc4c38a9c13ffd00c4204fe98838e12a5a4de19a997d10f592162e2c42bc23e8fc08dd71224a25586c601400897d83cb638e4ace87b2fc53c23549894a212c94b8172f91aa5cacc63361c1b156542fd63e21f4005ff69fc0aa391e647c9ba96940a05fdf38d938059fae234d26a0a4ead76b1c9c82800f5edb8fa998c1eb9bed64f0024c37b55fa333d85ea45c721f2f2c228464273b72b6e9cde227582947b08797cddbe46d7540278a9803e99f8d882b3e0c0466194c8cd98cd8e44d828c94081a39947451eb3455ad3789da5751a432d9ead0fa99f3a2c139b6c540a09664e561e4e11cd43fcda07e971d3971de53a4920e44e9fa28f3f6c478b8dbe1fe2dbf430299bcb994af3716a1482f6f0fa62abc29e7105984a8c3728575603a1fe5ecdc123be82b4084bd3bcdc6f4afa9cb80af6acb5d2562e76491ad21529a94de916c386472f310e5e92fcd21bf6a4db8e1b57dcceeb67907fa05c0d963f29e1ae8d03267d3ccfd1c9a3c6a76c06dcf31ba74f12986cb80d66170269e8fba0845595af04c22de274cf51b3a45ae31349c2c5c3ba49eeddc19f8334247b905fd17bae329cb3356e9b0660b1a2c7f7999faa350cd2d25168e1c04259366f10581b23843f5c07201565ba3a00d5801f844e9318f2bc170c62b6bc81f21dce28f1876e1124839b3f02ff372682b0c9fe44a8c6f2e3d42430d7e4f7f7beb6b4cabc4d82d54f1419c6cd90aaf23fa836ab1c21ddb39d5a967de1c491d31823a62b8acb9d64a043af0c6e48a4370209195ee45a2d9ffc11b4079d611050b72b6c595f6f5466278dcae852a038422e14661a814886d11859d23552bd875521b18d07c6d175bed032ccbfb85ec168d0e110dfdad17cf98ad59e226887310d3bad299343fda767e00e808bc816b1a413c6b98267b127097b622405c5cc3f0781615e27afdbe4cda4273c34afd296843568fdd26d2ee8bc3e9b259ef1a41892782127e03b0fcc646129ca860160c110d3d6c3cbd660e9d16409a11b641ac7406448d7bd9a09903db5245253de40c05fdffbf43ad88dc9d67daa2c8b07db26b0e18acb9ebb231d90c6f738e2b63c5c1e4dcaacb4aebf8e8560b18cc1d4ed3f353e41659b9e609cb5a77eb793e84bc27f1c96c1b1abd28375d01f7c7964d0163fe6fef3a6fd33b6e80a9fb7c526031721dcdcd2076cb92b5cc9795a7aa609086e7ce8702ccfad6549bf0e72c37ddd9685f560f166295585bd07d716568bf36ae027b1e2e0025d8cae29797a09007dffd2bb5421c512f6325d0096206e08b760c0a175dfa125024b4d812843fae166d2ad80116d2fa7e85e15445d08fe9b885d0332f0b5e3779e823369a8ed4c336235d853afa09e54157385c647293cd9fbe2ce3900848073110fcaaa04fe1f8277376bbe809920e5dce0957d91221d271819149d986843068b369112ba1a4139a1fa5400a2dbf47c54e11678ce8e52834b8fe3d82089036de9436a051682941543885b6d12e35ccd3323ada7db46e866f0c3656dfb1cca2298520c4a9ace5e1cd8943ff433d50a70b70c3fccd6c01efaf8851798d1e8582a92b1b907bb459bbf2077b6159ee56643a00ae7824f955a93daf703dbdd493f9f6608838c33e4ee0700a94875aa5e0f58d2cd21b455d888f6089a17d8a33469f2cd3f8580fc063841e04705ff3861eb01a33c823828a37f5bddbd27ca07741811596cc06a4c31343e14a42d1aaa92745a6170510a9c912b7decf0b132f7cfaa3ac1c2b1bf6f1254ae390771b1a8bd8eb34fd7534cc074495093dd81cfb7851a8d9c3e8b864567e66716df6277e287cd33d580bcf79ee2dbbf147f01d4261c8d41f1c8997060beddd0c95a2883f6b990baa5a248aeed7b0c50c5c8d9488986553068751e851ccde4cfd1a9840a084e002b49a0378e26babbdab772b1cffbb1ce5063f3fbf3ca6e09d4323cf2d104605374c1d459bc1b6f347a64acf819b745295e7c72050dc40b750adb809665931fe6626201e12237c9d07d75c384049198e89110406ea2dd2d6814582ba5ea39295aefc5dcb8f0bf0ab43100b740a188e2c6701b12702be664d8c7e5b02cf387a9ba7121181f5dee6adfed9ac178aeefde356fce0d094e4cf162266778d743a19f3281af1562e8b373ccf78a5eba25f497c92fdc8aa29004ab5996095bd65581644ff0dbd5992abd10172a6855c9024517591c12bfc2a1d71b03c7460a616902f6d9a860825e4f5cb98f7820f5595ea6de2838b32c89e9bd832f6fd7cf288ba3921e765f44c262f9483b430851cec005a011d53ba3f2ab7c8c16f3fd7df682889fa5c0c260c77bf32bdc475bae32483f756fd8105f9509e97c33056e9bac8df9198a540e7a498794a1608c7eb7e31377ead9a102389b4a1fc7b4f4af1a42de3d5e29b6d642cc6da931d4fe8c51983429b3367c0b0e2372e1e2e2e9611e0e1cf109e958503aff5125ea0e5ee9ffcd1c8bb7ad0c04a3dc325bad663b465ca819e7c6e3305adcf878211f1093f3085ac580a816596b4a954710a2066a74f40e15e2f47c1105c4078aea040c36a5126f3d88605698aea893492a0150081a02a842bd6424ec4ca379962110cb1828d7e216cc09e8ad983817073143a6df53dcd6c89525eb566c76ed4ffb436dcccde429540eac7c3621924580ec8bd409385c4a788228239fcca46f03fc9dcc0eaa0cc18973f778580642252d2219776e589b5508f6847580b8f4cf41607dbb8334772de8b61f7f3c86dc780852ad2c450a85ee60ebad8fa82962c9c95e7d6a36ae805cc26c5e272a1e2a2a9c010c0a8c4ffe8093b098b18a41446ed0ba8add97d22e8ecc4f834130c8a5f8d05f1694d81fbe9583d5b70bbe81e146b4db1218381a14e4c613f32b862373c87b023a505891934f1fea109b84af33b524c2baccd5deff24c2e523b8b8910036d8a4ace740cef7d953d796404ad5807b9e9328bcd7b04320c07ff02e5957a773ffac92d396e33ff33a036e36c0b58bc6fc004f6db05eeeed9ac63ae65cfdca23ed7f6c5f845abc0d415405792a35599151619a1edb9d365e574e1a3b78a398b42cfdfb2a12b16fbbffdc8e3c1e1e1191a386547132cc8059f28838b94f72af733086a673344a005a26b7e62aa60ce4a981a9aa478c574af09041fa1b9849e31567453a4090fc678282ac30e484ba213708359200db7dfd9ee5d154e0969a06663076c94bb43ed61666130fc66f29c8fd53fb3227b53993a0eab6733852dc0a7eb35cf848e11ac4cad488ff12a0f60b24be4187b0d4a5094cd194c7b7f4217be507674e971604ba674c30258a2daa7c23da7ccc286bef0f456e19ebeb109b3f756b897ff591cbdfdfac4816beebdc21d8a1b0ad0a2467247df66d95d39e672cafe4ae4143993c3e9302213b022835e0d57c3d3dbe1c2ca0fe9ae5e04903b2ce6929bc1ee50fe10cb55a4b161697ab76f897b6fe5f9a0d64831911be23cc6c183c5c7c9caed423754a4f2bdea451232fb224b5ebea870c3a2ee8d59e291d46609519b63e0663a8045e8a3071852beb5e43f63156a8de9800185012a88b27f1cbdd937a2615c80f336e9edc591622f8a4d94cd1d602c0f08feb80b819a58ad31d2e13bd808982eb10fa6b0b4ca10e9540f419abe8e9ee64507a7269862bd9b55c619377bb255cc6820a20a65fbd31df08ad0d19ea64682cef843ed213ed8b1ddef95de175c3b83872101d8b8aff96e229da728a30203ce68c50b235bc25bac71beed7135c7e91e7326688febf6414c09050e616ab4d6f02b0762e89a3bb85221726a4cceabd090fd0598a06bbec9745b79db134ce529e79aa1c24037fcd608d502c519a2f79f7cfdc17cc600d8230d591f3485069537a0d6ab59cad7afb9fb0490775119ee617dbc09cc350acfc9458173f1cb2841e57323601b03a3c964c1e06570e542a760062ee2fa41be63a21e07d25ea991ac8643107ee0daaab6e6d1596389ffc41ea933e503ca20875a8ea83d912b6630ee0e6f201975ec28dffe5243c89e86e4bbc9edc6cfff2f5430f2bddb7b3a23cb6386800a507cf4c01d0e310e1d870bdb95dd8a8cd722822847186a507b36e70e46cae9690f3d34441962d91ef805baaf9286b79968069a285991ec7d09da14e6649a4e14d25c7e92251dfbc4ffd607b4c226ddd1c63e71f83c0bfb1909be06202b8e53a9efe7f28801cc318544a2d7836dde6ae3a2253638475a12bcb8e944021d68f6f709f0a26fb1a1929e3edc2bab151833219be6a2d3d049fd72306293a6a25f8a41d34537fbeeb89c4013ea28415c09242f0814a711ebdb6c29aba708647b0148bc71f374708622a32537ad74e940c892c229d401a1e58c4ad824e468a01cfb1b4cb755063250398408344698eabc01e6b68975e50d2d541007e251785a06a5b605bc01a911c2f35b006ad0c52bddcff3ed04a77581407def90683a55d8d56bcc02139b0d7a20cf399de5546ddb63299648b3f540335239c9dfe4a0cb81cc921e41ed70fe1b78508aac24432455d96789a66f70842f03d37f5cbf80ded51e84d83f46ede09a4c6189be0172618811e5dcc55869f60e0f7b843aa73ba6f835930c799163f4e6afe8b8b2f973fed1b24dc26523d5c214dd1c8c8824de3d288d16f67e15af52d2be919295ede94f44b64b4cda0e9d5ee0e2a3ed8acd7ce9b54e109adb9c59d027aeb287c6cbd1781bcd686f0b97aadbd85ccae54dbcd55cf2ce2ab9f36ae09130220dc86a79e1ae81dc41cc182111c14598142a5c9866b82534129b028f61a5b0eec575e7f2348521bc6364c2a8d4f506a4ef50fe8e3c6814cb08351e622138dab705790410bc487b25b50f1dfd3f2a4cf9a61f220f6a6bcf33246e253266d29ec8821d994f6b4e05959cbc87009c1a9ba5a4021649fce6bfaef6d986b5bf4c0a3422bb2ad204a27cee8be102b843618eaa97eb64cd2c297ac1e601b49a7c9d074a1b95b3ae93c9f7e30baa6672a8fb263ee46a939ae02255e5a66665bb82d80bf260efa745cef833d400f2bacc3776f3a7180d7058e3ff0d844e2d26800aa68a8106a90cca99e1a921e8ebf5d0fb6f77ebe667226972cf7144a95f96b4e19be60d458747b8e63ba50644d2fe41294637cf9ecbc89f9746f6df6d5931516a0a2c434f0583558f05e4e3994641cb0504514e7b1dc984ad61b9692b6021cb71ba2ab247c727f41015a3b3d168a01e96546837a9224b73c5cdfacd40e242b615652964634ca27186a92da899b6f4e357423c1991c5fd3a754f368a0fa5f126eb9adffcda34784b8938da5c1055ad0325d444fd0c2eb9a982a2c37504374f1a87508495e2e8dca759b2a7614c0b43649d48b3a463662e217286c8f691c744c1c8ef0260cbc65f3213cd405ac2105b161f2f23c0a7283679a6c0daa23cf04faa0c8405b165c50119b2819c414d5a3ce8fef286c73189798d31f4e8829a90d7d8ea927efd0a5388d83105c235174e86ac4ee1ee891e9d0c59d2d2d998245a7e1f80d32c40b7a7480bcf9b3b9727d120b86cabe84d6b4e50dc377a854c917e18698b411dda16a4d2efc96ac60130bdc7db64a358c32d10c289885ac81ef9a7a6461dc89c7882e0799bc75dc94e3096482d3beaf6c7eba6fc6258d9bdc45124126ca7d3a7c5c98f3ed9961a74db2cdce1fd09dd89efab08b5df624b7ec2f39651385b56ea7e4cfd925b54a4cb4cee71ebc70caba56ef7d8ca6c8d666816b49c1b321d4193b6e38be1e13a7ab4b228b5a35ea1a2f2e2261abb3aa8d7bc3a4729168caf152a60d4e638bde4f4c61504229ade1d3a329cc83f48cfcf352b2137e8e5ef989bf09158c2fb09e76dc90e9444faa2403f1121183d99e20c5afe99d47ec2b74c827ef200b3ba5acb922cc1357167ded5f91dcf9b5da2a710f4f0b2c57ec3998875f5d710a1f0be264561bbf61480919168696044039026033c83fad0b16835090bd61422b78500d6fa325b581409e503bf4f9e3dba5996cd35dcee4d1dfd01b402a5af3d6cb6973c5dbaced8f61ed071ab0461cd208172591b43b75f45b9c468f17a580f48a1fa55e4e6ea11b14adbc5947e765b7d1fcc0295a37b21b59ca52ead15bdcab31253bf72c28025b89ec18f48be4b874c04b462337bd64a8370e5c94f565e6b638d092bd84e53f2f906d8aceda7a0c6fe4c8cbedb371125fa50a08b002c05989d61742f2c7a99fd511aa060febcb7c8e6a386b6ed3a2fa2f74d899431c91f560f49217458aa8a8575598699ca51b171245ad229d1b346cbb7bfeb052379267084dd7688c05935651ecb62ae00de71d950a212ce13ccf719e6b5108213f9c918f3d42bc4fc58a032ae4c9fc636883b716cc6aa9043b297f8a569464772b3a0a6904ebf0499d7b90e88a0f848d3876aae50b89727a962167b8efae304d6138d27b94ee6ed25a7a8241dcc83c4fca52d74653f48bee8573b89caccec1debe8384012a8d56799963b6781a4e8cda28221ee0584c88ffe1072f5368c695795a70c27c11fe556cb02875228f75962b7d2f1c01057b3165d9eea690a6ef3de57005c36f228a808f3b9a0aceff3115837754d2574bb6ef728d8b90fab2f0a5d0aa07cd9d1204ad103abd5f79cc06ca0fc83f416e60242299cff13f7218baf3540ed86458526625619b038e8620513bc51996825baf1a85e82bf2cbf74c5212453cfb37bc24ec426ee29cf6925916a5c01dab27ca3b84835b2e25926e917a694b6595168e4f949d3a7a7d65212673c53fa24f734d375cf1b48d552f4a00e65bddb0c122f313deccc756a876bd75cfba40c657e8b3ca87cfcc1ddbbd9bb94d33cabc114e7aa2e694a63ac82b275e7c16875658e33a71a079509eaf50a25ffe28c0aa966175554c3fcc56fe5d9d76b42afe772c003d6acaad36e1d0c5aa379a1f81b3d963a95f0ec38753f7793c4fdf38b842d78be26747ed5721d2e5ebbd8c144fa2ed63b253fa4b70ca318913a6ae1c14d658b1064bdf289189a1c1679907ef141e8cc55b76b003e1b7b54a7f43ec5bad698b735f462f86148eeaa39fb385b846d8369934f07ac6560b5782370a59145ba6a098fd4e719a89389f4e1bbffe129bb7a97c2a1c4bfbc738ba0cde08d0662f68309d16bafb04bb564526f3b82d56ddb74c4f1ed2897c002365552935e3025f8b444702679c84ebf48a0b3009cbfbb9f76367c3477f267197919ee06c80b891ac5b4481351f3467663dd8fd424de7cd577fed8953d782a05c801a8d93f618123930b77461bc3c4839ed1383e4cb79b8188ad6833ca34dc9d067ea7bf261fb033b2091bcdf132b567ef3873b446e36b75c0abf846c9ba23ecd0ce4d4101de3eb9cd5e0c56101d77ae5a6ec190da7ebf8d7d0f46332f9ff0875da7569ad5ebcfef8a6097dd1ca260fda68cc9df77411a5da62163a396e059bc70be6b59b0c6df3d433ffc0964df331a54586364fba67d15b68599e9d698d72bbecc119f5632aecbf16d797ad11970e433ab2dfb748a8a5b8bc4c583fb85670f2a6a3020a7daa56b672365e7f6ad61e644f044780e559eebfadb7ae3350a31dd39cb46b52689ad896cd78c774a2b0b4f79fbf25def4eb8db2ecc76f2ed8a41be89c8c913eef1eea5064aa73c12e7892c3355fd28d83a7afa614ed6b9826bf9663f2b7b89bd1592c7da66900a7dda1ea10ea50a756b8b5ff9451828c87a7a03b653d10e47585f2483af09f951bee7f963d6c10b4fc094df1bbf824c5751dba1235227122635ed990c28c8b544c020fc683927789e61817b59136bc460ffb46ca4f165e8729f702542b2fb51e1b0c0cfeca27b8e91560f9bc7a29a902bb4ad18712d5cd9323919e578122dd6edd5189e940694532a15e2468e2ff214d7d224920c231b7bcf68b175e9cac4ed34b028eadc5b6e853dabc39acce2777190f23c2cce857cdd24677da19e10560109919bd46294246e7d6f7ffb8fc6543fefcebe4e7b86d0425ae96f180b46ecc29d0c57806e28b7b82ee03f67b5d3074860e2abc5ed9d1ab9098e3bfea5df302b2d58d300fd7c41e70728832e16092f76cebe96a0b583e641a9b204b172ff5ec12656682012099edcccac72725190730067c29e8527f68b156f21246c344b68cde6d4b4589fe9cd7c7b6479825ceccc8c2381be9c31f4e915a92b8fd5ec900da02ca177f161ec47ac76469883cdc8caddd03aa4f4ff13ce1454b7c7fc20502bc22dfcdd5bd59d36fd1858372305c24d3ce12bbc30c964f4116ec960abbad83b66c1f46694c32bc166a233b7f9613d4d4e5d6324156465a58b6414f1160fb106e4f8b580a28702af9ba9ee1fd66e79223cd264bd6bcf4758065c6bc2d03d03c5845d0e27663690702825d7b78a99d43a82cfab67c436215cdbbb350e7bdd8b2862202d652829a08224d53cb6479cf9b45231c904acc25d401b98c2ef11cb3b96f391da6d2235741e24b8ac5e3d17e6a2b485889d290ef69cdbccb80c60fee02097f7cd9247becde3ef3419a6074abbbb7d5dae3912b1f98929ca60212818a8a0f3865f166c744e81b09f26637bb1eebd9a63b91883d45849939d83e21857fb2d361088d3cf8a9756ecf6bff6d38ce250edd8ffa0328e393d88f8773e4c5774604b49a34f9569bb24e9aef973b76866edbe8dfca1af3b47d2cf458dc9ed6e8d1b5be34e9931182bc1334cfdb264f43dcbd6fefc14648ee208748f4e87652fa908c4bef61649051cdde81a929183bbeb8b72b04d946dbe79378379cb55b39f22cdf514347fb833cfab441d1085d201d409b308508aa9f38a33438723c228535563a48c6279abb472c519a8b5bddd74ad95c16dbfc943ff592220887c6da1e83cdc75d373aa13e9f099ce2f9fa62a9ad2466382a3a3699f4c0f048886263691d2a4da61408eea362d292be75f7f434907b2b408cfe62d8513a8cd97c57a2a8161d6b31f7cda127e277623dc50ac90bc46f16cddd5a4a1c186e256f5fd8e6306f7f7286b5170c92687003bed74083ed85c5af4b07020cbe566f0eb4272d6e82551d0395891738c19db83ebc7203f9ddee1bd0ec5c71222ed8b705ec3765e313efeb29d7ba42d5ac703f658daa29e83b7d2410bf15d8318ef1678be1fd9b1879a15031fcb4d0defcea7d35bf54b4c36ecb38b7f1eac3f18e19ff6495c254947b03af87ab2e8407cb687526db9c70da5ec927cea2ae8d438e5d07cf35c5c13b802c3b7523a15e4b11a657a646c893e9fdbcf08546ff4a2d4ce9b7864d66690aab075a77e35eac4a487614ef631fd2ca79353a6a533d5c68965bf5f89a69e007356a0871ee5967ac24c0668dcf351caa926648a14f0e0a964a59680c9788d7d3eca3ed1f79230cd58b57027ad5fa6447411127f70137177b287201dfea11a623c161912733121e0940644850683818c1302c4a0527f9ff17a7a473fc6357448bc8160e59472bfc1780b32283b113261982f7c9f4d472ea36a8df3e39dcd583b0af11efbee6ed9b909f604d1dbaa9ba381aeb828bc3e98fe8fb83cf46eb67b569e6f365355280c7c0ba82de7f643a816119153b9f57e65303c6688a8d5abf8ceed35d1a935c9acf574b9884de1dc557ecd96b99fb3917a189feebeb4850d747e774dfdb0bf6d6e9717cd6b2b4c7b93b2f9b1a257e460797b98cbcc50d2b9c437524c2bf50d1a0b253fe7d38b1547b55ba2da6a5462ff2f3d8bb56683272691621948b4994d54c24c840454722658c637418f4db0d245a712e6b3e0d8e712b3abe905959cc52fb1a7908dc564f1dac1a82f126df51f5b2c7a359471aa6875c70a1055cd3c3f9d163cfc32cf8813f59c64ae43bc00e1be40da9418689c321767aa9486d9bcdf997752d0f81be6318263263935cc8d2c185596a854dd1dc1aa9ca3b3fc09e9fcb8904ec3908eabaef3425c8a7b727752dc1fafe333948fcb230fea39e0b1efe33abb8b187b391a666f1162124ca6f63c31bce603adc2f391578b1ede31bc45151a56a5469e4df47e3d7d51daae27ebb7e90c043a1f8a5ec0c4dab0c83d78644b3c4b843d22b2550c6576f77551a5293a1c45abbca705e4f55c74ca218e780162e9370da33e6230a250b7d9f9333707bddd0974e63614f83bb565bdebec08ea6fe0ae8cae738670255f38961bbddba7e0b4d187e02418b532fe08e7be6d6eec75818ba08d4029c7bf7aff3023aebd375915e45fb6c657ee72675bc4d91b1f811e477361bd277d498cd628e7ccb5144d70ee8316d4f019d9e5208148d5d1c6f285eea25b60efee08978ede9c30148f261910e4b5e856b0bb039d276d67d3f200233a1a2780df19e311bb539c67ecce8ef30872314d9bfd998bdfd847458839eb3efa7281a178683e137e05eac94d8b8a291795dde8b48da9fecd60abe3ebe6a92e1e8174a3a1f50be9a0b7807b1e4de319921e6db890aaf8e832ebfc864bd35dece743a403e4667d2c3cfb3cfa836a1f90b6d7ef65db393d1e41fbe84bfeb37800d28b56fe593b7a77689fe718f6a2ad2cec28f5bbdc095cdf98212f26f292b4e0b37e749f6fbe736f3b42dea1cde70535a17ff1d6b423a43734fc862144f4e706c1c7a3677c0cce23e418da4c58cf435d3bc277411b2b00e30e5db801c8f1e89dfd811658d3f3326b40c261b98e660163b7235c3a3a376fdc0e349ef43cd377d078d0f044e349f43cc280863e1c101e1f3d036cdd8ea05c68688141f6d07120e3f008298696e507e711db21ce63e279848fa143c60b0946cb805fafa166d64d5d70793b34ce8fb1b4d038e0e26e7cf4cd848cd6d0dcd2858eb11b54bf4d8b57286e41f0f6a1e835af141d2d3e5120ea48bbd95be26a8efdd748dea9fed4768a9e67a80e4dc400e0fbd1bf1f9b7784bca17d40423a4ea0b81d41e2e832fe209e319e6379c4d939ce8e383b3a3e3d648637604db299bd80801a3b5cef54171e2675a159bcdd15dadc10571b61300af734e0c30e771e73e3197a83265f1024317a733f2023348efe713b683b949e90f4906b36b28daafd453be8adffbb39cbcc32ee401ccf8da707c9e635108f57c4d1640052df11d40dddc2dbe1e116445d809fe182fbeea4f1f4f4109a51e9b3ec0aa498afd9098d1ba97c2ed91578be69f42418d4f54625f001145c497662ba045224cb7a855f1e0b44599fce340b9c5d107c016920b2cc3da05f148c0e998111aa83d144e6e6161a00b1d813c950b8d3a00f3befce33881b5a7dc09292281194633e2e30b286de96f38891fdc1c63c264062ea4746a9a690f0ce487b18828f26f300cc039058b4ec5fe03b4262689e7d4140d13a1306b21ecd02ee79d2f2a03feeeaa5015c7a02aada85625f400ebada1c124ebab9602720574245eb2c197f0f7619c54a01e371106cd828e44f66ea7dd698c0990777e16341356760dc143774470a0fc877803229c2d6b9b97d0acb503d38313524f8a920c726842fd030335bfa6bb6b67b656f295392328f06c505f80538ddb3c69eae7b2d89e58ba4dcefdfe16bffc50d5442f3cb09af4a406a53c2c8dc56255c2e1a43a61583f537e63164c86bba8ba48e13d3816981de348c50dfb0c60f068ad3df1a3f155aa03a9f0f0a23d43a9fd93730d01dfcfddf0b74c7fbfecf850fc8d702dd397dffc702dd09bfff5b81eea0beff13a23ba9efff54a03be2774ae8047487fbfe2f05baf383eebc7cffe7e33301dd89f9fe0f05baa3fafecfa43babefd4ad0800fa0e5c5faab2eb4bd627eb04b8c89f9f1bdf81eb6fc0e840e6597ebadccb77a07641bd081ec05984f053422921776fef50f657e3b5d19c7ed5789fa039fd31e3ad4273fa61c66b85e6f4bf8c17e9424173a2a039fde278a5a039fda9f126d19c29684e7f385e168cf7ca7895c64bc578b18cb70a9a736b03f4cb8c940ea064b9465a0b50aab8dc1aaf1414a79f35de2a28cead828a49fdd7e631ea35bbd145be368ff19914a71f6351a43321d8b75f7e662783cb74ecfeccda91a8332594fb45a99b72d25099120212f403e627a829b7ff2ce52eca656a28f7a7845a07484ad63af6574c3719088191591f557f519874a7b319c24608d149fb9eb545b2fdd216c9d6493ba339f65f4a62c7b2ff672e0141c0df3df56f970b77ed79feeddf96c434e725447c9f5c439e7ec9b5ec21459a4373684cf75b721c3b84bde0efdec6f7de7b480301fcddcfc0dd47628fc41ec2441995ac34a73e253ba5cae5061b5c180a84700626372037701c0e1676a18c4ada2e8481b90b7b54b9bb61c0f09a7cb1207ad9bf8a3fda1fca7e076a17eed27c2097607d6e7756c0b74fa998cb5a6136dba714c33cdba7d4bab5a50f4c86153ff536503410103ff533443245ce40c1b80ce3b2ad5d6e7f157f576badb576f88055e61e6c0b946b7777b787b0ce9dc889327d7777cf605292b5f686b07b2bfd7b2f17c22c57691ce71d65cae083f874c717100ffc3a0e74e71ef4c72180a9d383324ef5412199f6b8f846e87191b9ef71912f0973c1d5232edb91cb25581fb04f26681fb423089d28d61084191c89ed58bbe4fe14898116e2e7b276d86fdbdddd2bc5a8fefe4ed111605ff6877da9da2597d03ede0dc448c70f1d318e76d65927a48572989e236604c4648192809ec0464047b972015f29e00c1ae988656aa4032805a3a09f4c8d762ce5b2667acad46847923dca2536da1165492e2dde8143ae6f91e4aa04be4ca01a5e90703f1c947a6d5e05ad83a31a356210e2043cd0259ae022c396184e2092a9700e5ac40d146c20b285131f865e38a20610a025a25001900a493ce490c40f45ae48429348c107a61b2cf0218404c681896823b2bc60a1a2c5557ce44cec682224880d2b53443124a446530c40f078c10aa0a0d3161ad87ea5f1946bf32abc2cb054281da7a5d3516ff6364b824587581055b0f8d902145382c3cf134e9e50e11bb4d0e81ac15e45119766c404434ec47000b24409529021b524494ce0a082aa47b4233c006184861e94e072c30f41b4d80045902449aa601b4d6c8c54a172c4b9c4d9c427c713264e24c9e2871e8452f8e7071537acd4781294a3cb064b442ca5c084c5cf960e9bc0a980b3491ce757b84b23d504e457aa0b494c59aa72c414294061012d644347b22041c50f5c8389158fc20a2f0b28bd81207a3861e6044889110cd480c20f36a85c9921878d216e25c2077bb5e8dbc4af28e140dc2c3fd40b6c610e09d2e4ca09a868b04595242668246d190224f452a3f870b1161fbcfb4941876940a9208d13fa81bb6171162072d75a92b0b4892428a832040c43599ac8c854703651c50e39c0f08304c78460a249c9d18c8a8da2262be861d6b8a2040e1c3e5e5dfa2748b705cd46e089134ba830017204878f0a5070b9c28622686c3971ac00834e0b201d5250ea8c58972cb5139829c8d1a5c98f10569e30d9f1c20f92dba0916a22a27f98e89494b89f251d978ab3f8144142058e169e0831bd90812c56aa285d8962cb111dcdcbe2da9b6b4169f0031228a840ab626885981464f801831615f8c023c4c2070c1ba203a8052e437ab4408740da218829558490c0f1c342ee7e5ee8986812b79a92216a3728a1f9e850628b8188099ce0928488fef9c15d616f13bfa284fb152ada924a7458bc608185254a6049c18a1d4c296cd1a1891f2c388ef8a9855ba0608e0248a860021796907c40c15304152f509102275368e9b47c416ca0b8bdd24de6103418988ea098c28815506034c3b964850a40b1539d42b358a078341fba133aa3dc75497008aa2d55a9b14495011059e2872b6e4b82683061e9bba52ba5895f2182c33a6ab300091786fcd0928314409c004b151b88e800050f364cb0328152034e830bf11adc06bfc18774a3a7c055e044c61b303af0591ba0cbacd169588d4e8473222ec46577a971fa9b7ec420d78fdc408658d9c97b7b25114b6ec6cd401b3e01ec077b420e022e973d9d5ca58261d585596b533df6831f1b608ae7744abd987821065a88f62dac69ed1026f68d47bdda6e2fd779a8cb5deb3eb7da25a0d73284ec2f2422f797a9bb3bca944473776fa1249ab596e7de5b56a1cc71254de2e9ba2ee4f1f19583d87d896d4f7d9c6b7dcb838116627d1cc260883d0ec4dd8cd0b33aaa23955a628c31c6b51e564fe7df12f0d5a45695d3701c3dd5da5ebeef96c3f97eadaa1cfa871edc6bed75715ce8008ebbb91c8571a177a1073487e05ce5c6caa51e70619501d8c35ef5388e23bb4a7d0aa66abbbde109bca58f8dd383a80f7f062a3c815dd7751d4df71c89bfabb5d60784997b1a2d298988a8b6e3e0ee6eabb5f612ddcb552020ae2be9d1110ea2ffbdd59de4de23abf71c345d65ee7b56ab55e6c83b3653d7755d2ec1fa787d5a72abb50e2bd3a65ea2b4e9b7948e416b1964ee7d5c658e0cc1ff792c4fadedf6721df63ef014a252e20b4c8c6ac56ab96466782e494373eaada139d5c5052fa8c7b8458dd31f054b11a95ca4abd44db9bfcca06fbdd44c7db3ca4ede9326f6d77acbfdddba2994883653a3a0999238013153a3a022db92f83241a422ee406242ed8309a032350acaf104079cb7848f10142088d040c30d3628e34f14447218e1b0224416bc03d38bcfd712a925903469c1131b53b8a09aa20391293f4c7162c5a500b18e0db2e89436c8029f1c785024e2a31e9a74367ca483d624570c00ed30d211034a656aa483c8db3ea8b524442d443053231d4347cd199920c4a806d18b0ab903f984288558d21ae98458ba4826a2abc318638cbb7775af2962496b1f4cc0280714398e7a505a6b63dc360593616dcb706df5cf5de4afeb3cdc7dd67ab8b38fbbcb6a9bbf5a39cbb9bf9a70c796ec5adfd42320b52959b79b1be872e0a5716b1e1697c647f3d145fe681e03c5e438fd29a0dc5f43ddeefd1275bbb88b8921f7971e164272896242dd1ca76364aca28ed1529a75362d2d2929e532e6563fe6d654722d636ea75cc6dc727fccad75f0f7c72cc530b5ceb5d863f6c4ca0e4473ea17a571faa3f50dad6fbe287dd3359ad38fbae5ee4fc55ae7b684626a9da67a6b7487c25ad9c9aeb16e48fd928934fc6719671afa2cdbf711d4f27d5a1f5727017eef9ef3c7ae24d62f6f117f3adefb99150b482383fe7d182561fef77d6aedf61379ea9beeb1d4bb5a9a0bbadc3def76632c037641f022dff7ea22cdc13684d94b8e34e786308e0602dd733fa37bce86c785b0eb93bb106629c5def77da0e77d3ea37bee41e8c80d36701cefbba0cb05862074e18c8ebb27cfbbf7df65f45ff05a6b6fdb4b624c73ea7b21ac33a5e017f60876f5248aa71084eeb95338a3e3480ca31ed07cdd3e68db5a6b6da518651fd681da25cbb81ff67cdf08b02fdb77d897bb64fb1f7d5818f67821c5e125bf0f644b7609d6c7760a0b104c51cec31e2ef7fbab05b1234bf01bbff7f5c26a17f0fbd21e514a29a51e51eeafb5d65a3b29777f777763a12477b71d2df75b6b2f53eebf5cc9096122bbf1358388df3b752103ba7c7a10a4b8fbd3e37cbadf21ecf4e07f47b97b5cd09c8f2c3fa07b949b2cb9b71cb82d4750cb1ab8b3dc18ac5e10608aa73eb5b35c79ca2a0b796a033acbd3ddedee7df6e9d6f2dccbc3f1bc6a10ebe3570d22f8c1befb6b8f8b6c493beb71919bcc01a436e58b53a7aca83575abb5a66e56a4aed09b4e25b146d7b746197c84de7cb6b6d2d65bd2da179398d43a5748a7aeb418ba9ef53664be858116a9fb29106672a038380d9bc981def4af72d09b7ef0c2c03c751aee5defd3016ba4e1481a08b0bef53358df7aee5b240929e0c819ad779124c87ceb49703deb7d3a98f9be863caf1844f0575f431897fd61c83ffd0cd1e9316460bef4f2e557a3f8e1af805626ea29ea86720514435e22ecf27b04a66768a99b08d515ae80f24c0e33b572c696dbf37b43a537ddf7b7464c033514413204511445aa20736082bbd6554fa6704ffde5c48dbfd31d9abde2f8dbe4f84e00f78ddd2da6f4bbbbefeddbd752184f7a4cc1cad4e8841b60fc2f0daa436facfadb592d1da84da037de75d6e4fe5e1be87efdae91d7c735afcd06d44e75ae0954a7db4163add30101798cce741c7f8c45d1d5404e34e20bd43af7eef8c9f59d06bda99dd9999de97f7d98edd8fddddd3bd3639c6e965ec9d599e6e7ffb2427c7949a552a954ca8459fc82d438fe543092a8e45afa1b4f7567768e7cb1f58dff8b26962933bb2d823ae94ef72e24970e0414a24075283d2175e87fb295e18fec4dbfe4521291fabbb56b5272a5f18a22d6c717645a1d46d4f1b2416b429c2074020b65cd25353ae128d7649b1ff1a68e22d84ad2c8e0727d09e43a8a706d756a81dcdd4307b1b99c2768d10358aba95a268b81395faa29da52a84cff1544d75a27ccf46bad564abd275cdaa86cb7873cd696274a5d2f8ab93abedcbde0b55eeb4575a41d5f5088cd952073b47540da3adfad35e4f1075127b774c7d29c4c3106f1a90b7b6ab63127db3a351071cba50b0a2c2f1e4aaf180b8c48d2124515a424b13335ea51a547133f5ea6463db2182dc0c813376a38717204102366d4705879a1891544c46cccb00d22af3661a386ee824c8d7a9c3d8ed46acbfae6a7ade6a5369f4c23a396363b449b1de2a98e363b445ac9ab04529b92abe5ae6ddd5e7b6fb75b7bafe598ec926db24c3f3766afe5ba6b3dd01b4b4b2b8a422bad945ad761eb7dd78220088ea515b23f805821702ced909581e0c986a86b5349484a494b37f3bc65f1050628a65fe5e6caae56de750596be84a86fda9ef67eef36b02de781610a46c592a119552ff6a6a5602a4a3dd43160384e3f1e2222e925455132fd96d5a58e4181a6fee9205bffa05e51c7a8a619c594625231914ca54cbfd27a9e15c85987ce3ae444b22446c54ab7a61a4481d6e19eca4b7145116951066fe3aae99a061da36530ba069aa7d3e9743ad12fc5ec7ab733f0454ca14207bdd559749ee7a9327f98a669c67030b31992d96c368bc57e62b1582c83f1020a9ce6e9743a9d4e3060d4320c183040b7790eaab0833167d1e927789e3037ee4768ba099a9e894fdf6c8664369bcd663918d8f81893ab699aa679822008822005525f62d17313a750e1c95f70e78967d1799ede99024df387699aa6e93647729bdbdce6b60000000726d7d3f33ccff34c2a1849344d13f7d9852707531cf6ceb3e83ccff334cd1fa6699aa600725e72bddd6e18638cb1b5d65a5bcf7ad6d3cfb3ab372e3c39e8a138fcd9ef3c8bcef33c4f1827019c5e50c4eed432998cb4d65a6beb0de77aab95abb2efcaaeecca427bb337bb04da9b676fd8de3a7be3eced8203380d800004288006a702801a20c9fd0698f58d017474bcab0e46d1c8f06aadb3caf453b96b37ff2e9728f6b70ce32f45eefbaba308b6bc1e599e4e2a1559d6d490a58d2c67902f2a77cd635c2a474836a5a55b53873d61bee4adf54dbf8d0eb1e462314c2323db30d72c002483931e30bc7e7af498e18a1e3ff9ec41a3070e3ca0f070c223cb0a8c101989628a18b024c9e2c3d0132c3081a2c60e30f0900248096a0cfd388204171db67021a1a5c6cf96169c535ee0c184940f6a8d2c4204a168e18447112f1a3c665c7808e14134a529484b1096a0a4a02a414c0449e1c183474c065a7b3a84d10602babb4ad527ea35553f04eaeede5d51c4d873b91c07732ad5e974dffe17cab8d75d14affd102eacdf3e18cae8efa9f4fd61d4bbb00787247c2800b9ae03c10eecc0beb9e1421ef045249638b3f20a44902c7d96bbeff083f8c1c7a4ed063ff8fd89524a2965ca5df7b5d65a6bd3babbdbaf50eedcdd6d52eede5a7bbbeeefe5ba0e245f4bc0f1b50415923438876439420e518f338a2c47c8e0b7873252dc51ee1ed5819c90dc757e94bb92845ad68075f701da98985ac7df719ab5faa69f20183f9cba7c6848a68d4120405adfac46ce098703c5e90781a03adc117ad3b4d6e1667de384ee34e198d09deefbb9259c124e072e078e88c381ee10790cb0c6ad804bc221e18e7046b822dc8ceea840a215375b7dc9ca2537cbfddcd90958d5c05a6e0e655de631b80c3e830f390a3c482a9bf802031313a3c20faa562ccb62795716aeb19cbd35313bc0580133f472c4cb0d9a44eebf20d8b28fb4d65a8fd901c68adccfc10cf5cb112f37c45acb5ac88ce8a4c9cef33ccf33f78b35b126d6c49a58136b5475a22b166d71d9c5c9338d94119415d48fb0871a24f7db2f3c8156a491fbdd73b9fe4c19c9fd8df13be9ee1f269d4459c97d4fe0e7a81f610fa17963ea5803992d74d26a47b6a4d56d755bdd56b7d56d6586666886666886263803660167f8a4542db9dff37a9e9ee47e115b6bad0766c9fd297086dc5f3f29f7939db4da912d292693c96432d927fb649fec93812e994c8457031fe9c79f57d37a44e47e54079298c45f7a3572bf8c47e223b971ac9ab22a64461463d55835568d5563d558351cc3311cc3311ceb8ae4e6aaf8c8fdb5c3ae4c6d5d110e93d65a4b7255b8fa4937a61aab40b1582c169b71336ec6cd5a442d24ad252da2d6ac55d43a5badfc44ee2a5c8dadc65663abb1d5d89e9235b6dc96bc4fcc5a338f01039f22cd55fbd66c369bcd662f4e2c116abe648d497392c4f2d6b8fb8df98df975618680694b16270f4c50b600b18425cb10e24d8ab885681619a160c36cb2b36a6b37bd4f7bc372ab0c0b16b1a4352c58ae00712e7d6c6ef679abb254496b74ac0919a0724133d3a8520417ccc8b81c558ee085abc55a595449825fa962605e2eaa24617c1153a8f0c4a1ca12901daa2c21832f12a3ea5b2695c6d6332e635d372154335f9f7e40776474a7f5f5ebad4fbab3fafa5edf6df86b90ecff81a7109512bb51e64117286b6599206ea3399ca8a526bf1465ff97229af04b181fd97fe56306f5254c95eccf82a92293fa564c1197f8f2e5f530cde34c3343dabf231e5959c6d5628df54673aa9b3407a646733ce68bd19c5715deab40992a06662c3f22b2ff8b986a413f7114befebea0ecd6e80eee1e83b20ccaae2af6d9dcd667c8c56c11b7795fbaf97dd927f865bd9dfed2cad5ca2acbf5c1cfcd5cfd895cbfaca11967eea94719d7d89a1959f962f2c63e1a5edc18dd09ff56b2163ec09eca9a6f7969a06634c76ba9181562a9aa7db1ecafaad19df02f0e47987cc71919cd2957b2d715b19cb9c9b2ff0b19ddb97f47558ceecc5639f5cddcf24c0cd95f55cbfeddf8c556f7c50d08a436a5adf14a63f39e31dd65dcbafcb632754aded88aeeac32ad76c6d51e6791eb7b408ba2d859ed36b0c9408303d0221b0c22b725ab6a6c34c86d4631fd85a53ead0b596e17c8adcab8168c4c5ba654727db133a398b48c8dcca25cbfe3017c25755dd7759dca55e3d96bafbdd623ed0c065860b21b4f5788343b5971102bac225fbef4211ea821d72f7da874202ebef41f177ce9412fbe7a2d835c6164f2cb7f71d037acaf5fe2fc5f8a2fd2894827a22980ea3810f8055962f102b2145d90a52bd790e5d390e5cd387a1214a7ff477782e2f43b11ad75bcd60e04d57123e84d3fc81ab9ec46501dd6f77b1254670ad5613dcd8338e4a1a95da5a9377bcf1aede8d96bacb1b3d7da93a04e24805203505a807295ebc73ca507a07400652bd787e95034cf41b3586397c7a72129cd59bdccb8ca33186021ae5ee65badd5aab52269df7840699446699446f35aa3d7faa67f667429a31f71322491f5dfab9a2633c6e417d2e96546982499f185486614f30ba97eea65c6547e218925951951f98524aebef45aeb716e91e508b9f561768d2db2bc7f55a3c78ca523e57e98b1f4284a7a45cdceaced9af7bcb79293d5f290067c966ff519039a08121364c7f982166fb81f5580164278a66ae2c37c31dff77ddff7899f7a30c545ac591451a718b2bc81a11cf40b89224fe447d67c7dc8576b7dda1ed44d4ce499458fe1a7e3f463145962d1e5fa17b393ce7467adecbd9f58749d7e967e32e592bbe5f6482fa96f7410cb8fbbd11d0fbf47529a733f99a704533fa5566c7567af265b3b5a3337a3ab12b7c0460991036e738258ded33151ebb06e08c344700b682551329a138392e51a26ea1bbf274ad68ab5bbd10c4920786df1d2e2d58507067c56106205175a8004498b8ce849124130c1e58652154228151a3014515a61851a3aaa115d428a0e15cc29508408e123c30c57b00c6de14210132cf3b12197baa8b44db616b184668e00001014017316000028180e0984425112c4611c34db0114000a66884a5a543691c962b1581003410663188e61000088018018600c32482b5b20009e8da77e9082e909188cb96a11f5ccd0f94d372297e583f2b13290439012381bb2688770b66c05d5b614599610d3ff89b6593614bd48f718401b989105105a09e8a76ac199c900841d6c8a4c5b025dca5102c48f899a32306aec2b29217445b0bde6ae8a20250f344e621f2162dca6528e9586bb0631e99430dddbd368e98025bf95c965652b137af6c2c2fb016eed3181ec39cc6608947a82a5b0d9967c7f2e1fc0688d7612cae5f3cff1c03828b3270abf3913ad4f5a4e8f07bf7b8cff4073925631e1ec49dc7aaba54a8391a4c1d960422390b1f7e2e9e736db3103e8a83d9d33fc5304a73d33bc448d1c2f15128b17c42cd8a1ed4241da9fc8af15dd2f79d6c9cdc987c0dd3760d605e96785be68b30735c0dfefe23a9669133f6a3a1e607727428f9e1fbd52cf99f1485241cf5e14f6161857b40676357643ba6835898413f6a101db203ce70c3fe33b3e513c95a7b6acccd6df41bd210108f51ae35779e34256b66408fdad2cdc5b0e4e68fbb7e38897785a5ebb445dddc18a8d3c8549f21389adc7e1836c027306daaf2b45b7716bfad991ab18c3f5d04d562edf3f9607e0aafab90f93810a8154160883084cea90c39835c603ef3652ea1399066b7a0cd0554df31518fbcad2114c11959f478a76e666897cfe817f238f8826b38ea39ab2b1053d62aec15592de6ba7d8c24d383063874010652b492c8eb4e016b8d78962060abe6a46e4ee050bf7079c4f626e634b5a59e0692579f2f26498ef65866b05becd1fa13e2a7ef76cff0a9368f2899c6b2a4cc2f1b4ea2e69d58543fc47293921894a0b519b087708a4510a4f595365fe26c52760eb1b701263080b86978bd890a613b04b9684d68799ef60b85907abeb2f96acaed4ee24345ac4163b0ba428c8e04f7831ea643d8ebbb49a684396b0823d45e80264472054e013e37921b09e85054df3c6b54cf9ab8c9a44fb971590418c2a08b1b308c383d1a606b299ba26f8d89d99e8302fd8b80be5d0057046f42b79c923dbdbea68122e6f2d0b55a495f45a5b4fe96402ed5573d1cfdac620e83f60cde6ff884c2b8be65ceb62457e19c9311b3a9951ad158ee7bd2bba925a67b54e7aaf49fc8495de551535a000de05416dedc8fee1227b38275c2b5746213b9800a4a3fbeef23c15b7a4eb01d2a5f20f06522a8d3b9e6397cb305fa7b6d9c16f6a7675e50efbfe36fdb89ef4a1395df44572d9ab0fe74879789fe3b886df19c2db44b0323f857848d00a561f93f88018fb8e0a9492cbca8e3f06eb7bd2770e8e23253766a525177f20ad0c583ecc1a3e3899168338f5e4ae780f55df1eeca68874822baa47a63ea416d75efa5e68c78f873b30fd8dc36c6aac80395fab17b131835101399a7da49ef83f560cfd64f952b718afcf6a2df75342761e2e3f8f5d4ff38ae8c36f1be62ca290100de17f0fec939f3dee5a468c7603a26adb596e6519791f76135a023e8990aed1db735c6662df8ae97c827021e1b4447823939d56a64e8abc4b40e41f86fe1fa2ececb3dd278475432cadc044670f0bf040876c9f90b3f040e04c7401852b0acc66357598f30b84d421fa09acab9d0ee98c2de46a1914d618b847bab25e02777d7fd2c71dcb4fe69745f9e1bf926ee09b0cf5e1267ef51525d18defd57bd436f218428efbf6289c68f69c614ebc0e15ded75ccc7d66453fa6c78e974b4ca3de3f26528dfe2d2abe32fb37b9d400584b4f2d46ca439be1fcb8ff3b29035e437fc1ddecdf3ff2a5b342231ca701808bfed8ae49e0e3aa819fe24bf52f1396358fb60aec9543a0cdb1a8d5d399087089f0ba910b62f5f877d78d0db40cf96cd978b53a4ded2758b316760d3abcb0f45c15507090fe8ffe21226dacb21f15a744f437d11de0d7738bb581e8a3af835e7af67138b3461b2e01e665cc64d00426b05da30691ea903ba9c267be786709c73aaf2c48bb69a15e2aedea637f7f0887b9c033155595f6eeb76a2eadc71e8c81f5268d3e062d2efbf4ea53cc6b0143ae8f3ada5fd1f482724faa17eb38f1594a49a94a4217904f0e38541b12074f1eac3c28c8eb22279a422b6f4e9efc0caa79c6668eaffc85671380f559a37d11ee116e4dd0581dbd3b6abdd35bd4bd4a1914f457e8dd3f6754ccc7634085c5d8b126a7a03ce6a0fcf717b0535d80de19f504084b16875fa3f7c3565ae7e1cab47a4a077d7ee9a3fecf75ed6de6625d91cc0cd0a19ba5ec0d5affa2a6e500e312ccfd30057e04aca444116a4d92025e0420d2f361fc77c2ba206aec2b621b492d5a14030417958dd48f007151620729dc09d1409082a1cb826fe2d01bac14a46404258370756d089617bf4b30de52a33a9d095efbe04c9941466a95fd832f9805035b7e08bd8c67a1c857e51ad3322c69bb1aa0ce17434db01a8d1b1e068a7e31d49093525c25ab463a5d3bf52624a7249edf92ae302d1a53a791843682c661c0b4531387747ca797dd4194f5839b57cb395f3afa781f559246e76fb4aef395dcb9003fa5520f21c54c8a46622138e839909f8c052ca55ce926ec312eb4493618bc81147dad9147a3151dab69de5d7417924a76089539b44bdb058fe1271a7d5a9193a943ef57af2456c5e60645120dd041be78aa2310d181a5e2441a769900ca7fc048349ac5dc0615f789eb185630d1a3c5db48654033ca19cce679e77c54f781960a2f6a6602b9a12fceb89a2dde86ae1a31821bfc1075f567f2fe5d3871f746436d187f51fa100d9ab380256ad41e029683ee39d4ad37236033118440142291fb5e2a479f318e4167c70c2f42b877ecc683d9a1c093b082387ae7b92d282776e4c4cfb05294df25573c41e4b7285d5667c40d716b0e85c9a0c111eafa070abc15b3e18c0c60ec8f49347548e512cfe0a007a02d4562149ab0430332141262ae75255d2a0771d06311ed4fa3844f022b599f1f4e49076c8aa74449719fff671c4a47b8199790a9b0844e0d3738adb1980f95d1913f48b85862fb0a7f38a335fce8ddb9d0c773c19ddb84dd4862f44ee8205d42a61b1df60ef4d2488b63e413253b1743eda926a91b6e13dda4c6bbc54bd702a7e627e2bfc5cc8eab6514a54e7cb2c84caa4257cf512842a2795cae6d1e99787dd185f196fa58b3d3b6a98c4496adc331947cf8ac0c2910306a601ba50198231b96b3d7380a462562b8a41e6c6c0f7939ba4b4adbfc858d78e9b8158beed140346d340405641b40303766e483e0f153dc40702b4f50eabb350ae0d938cab60d5204b411c4952aea28571596aee881ed4533479c37f9b37eff43a8bb2ef0d6f547c68a605f02964d6a87c097c1b9db8cf716b40d97037de81a3250d3520a519e8e8cea5a3ceeefbdf909e4263bc71cd118a9cd1228fae38b8e36417231617f27f5bfd2fa7a09ca973283a2a90343eec93bfaeb928453c4767254ae9463660e2dbc071956ed90d1b60d066733a5faa0d36b172c9a03c8e176211de701998930b31ef6c88e05504bc44bc49091092a3062b135495e0cfe95f89ae193b187b63629ac538863a5539c091279c80b19355c4f120a0231370b329970fc694c80e9619e90e5650c32439447d42593040ee304fe75f077eadb5a8de91ae1a175061951c7378bd8e2400a5221ba1adec50428919442bc8044e49ee22b2a76de23c62009889c30fced124126319c30c0e40a71d23d1bb6b717499f7f66d80ccc6f096fcd26da169c09c6c1b46d31461351d3c5abe900948f140f84712ee32c709e683fcb6020e4378e8e848568d290f701f27473bf0383e3474314fceb3c206c0f6982f2ab676f33db901ac3606ff61a612543c3d87c9d9d33efa4fef12bb9cfb0fc208d772d3ed0e072bddd31fd8204301915b2c5312959315d317f5d14968d5181311a4d670fb936a52447d88796354cd63e7e757758dc1a550017fb00a981744b61fc661a21ba46190b93804aedd506af7345e4e8817ebfbd9a337c7f4256187e258a5208037a60a2b2405176c0e5f0203f2a9f95201c810869631b39451b58be279408c4fd4e1f81e379deeb63c1833228b914bb8887ff330990786fbdff09fb712380122b2a4bce0be91f55fc3964a8d8b6de6d970ce24620b3d82b5d756255b08cc4ac82fac81cd66154080f83d0e9410c2fdac6cf6b02c05e13489fead554995258f53ed2d4fea0fdd2fd2fd234ef7a396a131fbb32c988f81d3a6b6cb12500aed766e7072018d6faad388d30b7798cc956053cce0398f1e624b202235a3f9b1f8183f72a225d0f8968aa4181f2cacc1a45703f8b6f5e2117c4914c0602524cc36d14cdad651bdfa3fe5fc47c669ebee38e630b9bacea5062487848d36099a366b0730d8e74a9fa3be2c0371aaa2a81ca88ce4e77d45888327402146f53b81a297947142907c543e8337f6b981730ae8413698b09b4debb428d243370c0a9c0b45a679eaaa054317511d547a87d04c8040cad59282923f7708bada62940435b7061d4959dfe5a40c684079fd783b6cb5d111274295158acac08d6859b46df60578fe71d27cd811032f78e061add85d9a44647633b1b05758d5fd9e02b7efb011e80e9aa47616204788842e497913e1c0df97c692d4b09672501b9f6f20d2e400a04d231fdb2f257f219b0ae0ef743556d9fadf07c94480a25e823bf91524e09334472d75f3893ce0e7c7ff7e33e90bb7992dca7dd39ba91afc27eb6d3f89ff06a17bcbf2f7bfc4c0c69366bd8e34b75d3d8c8b61192a95202d4a08dbfd2a4db564fce97ea86b90c24873cdaf346377f1597f2c6cf963ebb35b44b6e980f243c8af0321131a00aa7262d68d1c053fbe586822c754acdba68d72d1a433a10f266df6f4bc4ae250bfe38d7fb85f48ff3d74affca1b2fce8942f78570dc2b6fdc42f49c673ee2ca4bee82c0d7d470211cbac6b2ce2ec14768c6a8458bc0632b6f1c751bedceebb14f01d4311acab08f6cfc0ff012c11df062d57b745fc41454fbe4791f7021e7945f1afaecf045c3d570812b38c02b6b78089a2fd88ad301b14a972e90c2ecbe8082dfeeb752700817c22341f1585e77a5d8cb1649546ac7a9f39581d5f7bdb790e008b521eef69316d4755336ceaf8dd2aeb5ca068619f8aa6dc7aa20660bd9dd47842cc0c4dcd3b85720605887c0f161ee2811b8efd21702038f38be33c276f9439fd74993b9b96781efb22b44032238b86d7faba4a908dfc0b9c164d252aadf1bc5696fdc84df56f5efc7f46b72876229274a7aec240298a0081ca67d842aeed578cbd62ec4a46734a98825e4ba2d3a4bbab193c25f573c72c23ae33d6660dad608c1fc9ed2aeaeddc066466c16f689f9fc4af19805782ea211ec59e029aa7a1c050688169f9e16dfa427d2073005dab6b76c9c0316560913e355aaae262d024e2905d89e40360a5581a171c888e379b4800299a34e1b3edfd368c7c73f252fa00c22b376883ee265e00b87612e812a563e05d762a662785634263a344a3715cd7b9f20ce73befca1a46ed3a5e2e0341e94969276eb139e92927bc2673064d1719454498153e79b26dc5efb478fccf38ef1fb8c5ae851cbfd92898213c7e67ed97fb9e924672071f80edfac772a77c9cafaa20a8955da37df5c391d407a9ed01a178513d2d5ff582cb631a68cd1fe64c97b0456fbbd91def98162865e1d1f8ac9279ea763c4b5e68fe0f3a686a882c990519da4385782969b70a97f73d0035615cedab33db7b0ce23e85bac6c272680dd0015c6afdaa38222a33b873bbdf060ac8f79d145ddbedbf2044588ca92d6692a8452b572f7475f3db3862177596b478d79b834bcb97a42124a9ba6c23b404f1b80186dbbdce5b7a19d84221e5b5267af76057a67751119839a19f472c0154c3d48e849f50ad650ba6d260ef9417bd068f21ee3966f3240caaca8ff43f995d3cb8bca17142e3f20f6fb8081f97664a1c01d5f9007470c381d14abc604366f8a80767bdcd471b50584c3e84ad456a691a73ea004c49b68cbe8376124f2878a78ed680511834380742022c1e443259384ab5f709c418bd225e988244eae47d199bf70b1a513f6b320a2ec1fa9036b848560d4fd10ed3ca5529ce41a737d5ead3d0bfab4a0eda59019eca2fa947f6e4fa2c41bcfdb6baaca025601e80929d54b5bc98930a44fc06fc00cd6b03c89956946aaaeab4006664ff0c5f3a58e25b19515972e6aca92233aee056fe8f69532d65e31c3dfce2708d93ff4f6a4150f7818b82325b7ca3b4bc0bdefd86ffb86e4164e57fe03ebeb01ee131f65e6dc841b0ce091cb81ec560aecbee3bc889ba5634aefcaae086a696eabead2d401552da9199ed11fa78630ba390e75c9101e5adb8323b10a3426a60a2513c71d100c18ae1b582664274d36e5f3bc6b5b1405706189581d415211270d9fcacfdd7554d2d2d0a488d2fc168328767f4008c83710417cf075dfadb4b9ca523bb15404d1a74ee92794bfae36a1244ff460f255f17072b1a083128e6cd7e799a8b0ae096d68553eb051aceaba093e7fa5885955a9acd72905dd20f5c0724b2135e29afe3ca7c6743a45c72c3833aabcca0ac288293d0a644c5ee49a34545e99ee160b2a2705f46332d606d88222ec567973a5e829d4a2c18074074f89fc81db7df0496028160a3f3c6f99422db578ca3965b1f0f1b2c2606227beef9e0894920ce47cd33dbcf90b039a82f64900f34f470abb2e7ec8929e611524980275e80c21cf764bae00993ac16183b6ed8827561b1da250e73234f4d449c0a2e49db271ea2a75c0ab4dc3eb7096071a513eefad1ecab0e962e544451ace1fc9e9acf6462f8324b03fab71e15f7df2f1fe22017b7ab531ec7bb49b59749b1bb387e804d077167a7faa126827e3814946f449d6470d3c2fdc108184a9170daa07f81a52cb95b0239559ec600275d3cc1214377ac11444bf96df9736d830e3672f27762c20343eb59118416c1bd32f4e1b26d2282138473046b87ae4d55384358f685ac15452fcbedc207f02f0f38af2a3cf1e9b2204e3495abe80026d58ba030540938910ac3565ef333939583c2c84e751d0cceb8a83ab0f81ade689dc920fe1b498fa19f7b22a910301ec43f7c31f6cdb4aae4ca2489760553e1d572be36c32900580c7e8a3c9e2124c12d832611c2cdb199b546eba8adb760f4695559764ff3ab0e5823e321eee7a38d4e0065beb6541ecaf125b473e0985904d698414bb35d211dbd818013cc4f0ae175a0406dc0f2605c6716b32d65ce04c395bd95246d108a3865f6d467ab468b6321ce34970230fe93f189468e66090eced8a8346ad178f4ed4f626b195e03beab75a0647005338b9de0e510ec7e3e15c43a95d3a7c1172e2f2263f6143b2c3ae5b61821fa26a2276f1590af846feed572f9382708c325ccf3d398514ba661b267bb8d428a487516dc7ab3c72945835a176d37efa0f4ee6936b6ce502401231022c08e4a98f7945209d9200b2170b309722e968e805d2ac3b54531a807df4d8c05ef34b4ed55974a05291f54f72c6c56a1f7b1cf870c6013619da6918f6b750b61f82b00124e248cf5f7511d612c6c3ad0370fb2558cb9209e03ac67c8f66bb804a981246835b233761b194bd9e9eed034e42ec7f7176e4338481752cdf3fb5de43764d6074c30044e64c24a0cf6780b290e0c657494962b48144eacec94813c46e48a7480b5a7612c52d1c40a336533b43592368107547d94ab8c1a9134b79825b75d6d4718d748c178438cfaa54ab4e4c04b4b1fa5798c55064ccdc109c0e99f142a82b8298cb08f7012e001cfa87bc7275aef85ab1fd10034352f01d8122db76ea5b5a8b4cb59871b3c45594ace90c945bc1f7022b67204a7ddfd787bc67a5d112a289156cfcd0bbf94b79b1374ff8748714d20e4fd1e2cbbc13234b91d284b14802e8182f6c0afbf6045d60e4b854032feb2aa8cfeb0517706720d5fdbbc6d5f33022541012471ae8f358ab948243153cc6d0dec9daba11500ac2490ae67373c13905a9340ccf2f200a0cb6b4fcb3a8c62e6aa0dbfb644c40d10408da9cb678bab3d854a80497d5c2c28af1ceca2090dbb7bd5caa3e8f3bff47e636e5882cdf06a07b65fc21e9236d41d3f8b3d31f5a8c3687e6844ea222eb747fa02be26dc3af3729fd12e87fe29345be7575822b25c4f570167203d17fb530c20580cb95aa9eaaf3a0eb8c943a0a5c8b34caea676e9c49c93490b2ba057805ea58a6b23354c1767f12ee86487459eb832336111133f43436534a43c5417ac37fb6e8eb7e9d7699fed5b36a600bac4b5bfc44ee9e61ed59b49c2ba4f0ba41575f72b821b1139bf7042657096ae7d7c07ba9155445d09f33822728812c0b8be2e75264115ead5fab3c6927c1f4711c6ebf6211da4532294cc4abfcba3af43da95d9381310d4bec7a33ac5e3148672d18239623840ebe5d0e3f16bd118a3866e13f7a2c1750409e856d810156e08683499a791761f4a2660792ba16158d77942e77e0e8513ce20323b89366a8b82ffbf1d2d8fba930088a31e458c336fef451d24d54b7267d40978b02f8ef902c109d5c9700743ee35921043261b66c7660f20216576027a26b38f8b7e37f28970262d5bda934cda7eb54e02a697120e8e5fe5594e18b9a1dc878ff55945caba47bb98d52717cd978f526d1013603434369714952f4a53cc8793d0c186808cf82a408beea4a66dcbf41afdd9c272f7666887d168263876cdd45d1dc12a1c99a717bdbb5ace43df2919b352b19901ec160f634efb5b19230d4071dfc8871693a286d21a1c1925cd88c7d158be96651e2a17da166f04ea751aeab305d2318f75dbff29bcaa3a798908bc9fc8bcaad6249af785339eea71c8ebe6d8730d060a93e9de0aee6c3607fd463ec56b9ce13652663ff3eeb125183cac424345d01a00b3770723415e90d0631e11476ea8476a1d1db3e0f2f03e65e101343a1f3b304a1cd222658fac32753f756af42f6707f3a82718824472962a594f22ca4eadbc90efa8c88ac14924fd505c7c2c1da722e2551efe731c83035a1c72d5434efff97dd22e33ee3c55087fed45e17917e46c1ce3ccfc0183057ea40700eeccead077aef3b16a4da9e780ecbe236caa367cccc03a0424d3a434aaf79a04221653b7f6ebf3c6ab76566fff2549c79291fa41cda9e54e34291450c9077297bf6c2c4865192e28b0380cefb64b7ae78045f42af731b159ec7ed151b737a3da1cb57f5a8134c10d02ae0eaa2ba32956fde897c4768bf1b22e1f250f863adc4323f503752780a7d51408dd025e7f6714ac9003c3b8e33727ffd9152633b941b4393003b0d45040a135fd299c85548f6d3025e82aedc08e3a28ab5b82392cdc62b5873a9b96e34a2ddc4c49b835e711befe1d94373366ff451d9a6e762f833fd869606de50f4416616f21c42cfc9e71062cfc5da6af2e77f6894bbc99cfc1ede4dc9d90ceb96760f90b11119e23b34dcda4b6edef76d5d12b8117c8286c6b759dc906a3bebfc21dc400e52832ef42271a06e1db6901ae83d98f46e6558b01ea90aa408cc2cbe73a7407214ca5d8046832a7c8002ca3b0ddabd22905a8030430899c0ee0a486d12224e61eae91045877dc15b26b076c8027cd7f4d5c638c1b82c85e3613292d7f75b33888cc1233ff0d1e1251f54271644051436538103d42b6c401b14eedb7fa8a75b7a8a96c1898b712b998b307db8be099e124b8e717a618a83d3ab4735093d45265531b15a6307bd0466e93961873e6b91744724990086b43c8907938102d621b6402d64b42b4163f0dfbcb325d4bba92d5aca84c01535e85af375ef2c20028c281e8c5408be4212b107f521d956d4c8c14a9e1eba3814f5b886922ebc84e6522a8b2da70825bdb20474d59a747e7da19583c28eabeaf934383bc2634f06079c1b557de8bc04f604d244a97b61f9e7101f625beb0333e4052216affdd71cce32d1f97f9c6e2b89c309de7e00be96ec1ed6fdbacf8f771bdd722e17c0e6d3474f5c9d511da122e77da8c1c481388ecaf1944d003997a4d4f472c2e771056f062b5ab38407e9e7e268f78921e0dcbc1c93b7bdd13f84c55aabe2bd76d9f752afeef4b9ce96915309cf82fc98d600fc514b9ce680ecb97b482c2d007d9e1dd02055a8981b61c0480bfc60a5cf14c0dc22fa5404a818b3542a5014ffa30dd4200a98e86ba4282157b95a8b266626a9030dc07acb08392b9213cf6e3a52ae62dbc11c20d1e7a0a16ef6cc53bbdcfca11ade127d8dda1090d5b987aab5ba41e49de6893ea4e1cdcbd65e6c5a8d4af76dab2c441fcc872d84dd1f90be59be94ac6e79376c75154f73a965fec5d35952a90ad87bf0ccde86f7ea8ba46f7796e0077ea881d4d03bfb6d4f5ece1a3af4e9b33cc855144244efc290adc7b76b6693281254f6632b1d9457ba750c90545096bdfbcd35e320a886dcc10aa50e0ae6101b772455700fefba5d558aaf398bf86bd8e71dc1e684a7ba259811a53086e6f35c0cbf9f0bdc837adb60330f06ff26dd44d4a28be11ce86555dc67e58b17baaa1cd76455a055d0e1e3d00645ef654192918149aa0c2d8056d181e841cfa65c82e6804a4417bc44b14487479d0131a564212a3d1d6fc1c8f908f1519201998af3cff7811f21f53a1d61d1ce415125bce9515f3eb901692ee3a63739fa004cf35bb34b7ca54ddb08cf8eaa33d98681e959925b1ec4638ae476b449516d08f3c89e5da4350d5f02147be13e94fe7492f87be122f9f5fe52460a9dba50f5049cde9f1dbc4ea4f2abfcd424533033ea9c6242398a6837a14972f4ed11f5a563007479bacc96a3e0b0c2fa3d9acd318f54f9f50f3ce979793279ac3c506cb145844a687572cc5c620f42a3a11ef096148abc39b7d7309bf1b8eaa2ffb18c65751648092a19d4328354290b8dd0c971a0293756e88b03ea4fea6c50c750c5499519ce39eec8d4265176f94e69816e44ac62efd1aa9ebd0fd7a78a37c098c390c5a398e88332b99064fb180a079994089f10f008a96e7d33aa0884cdd8770cf68721e2bec1c78ae4a79a5059e3bc9c484b591be8b353763062663ec026297bfcc77f833b79b8df2b1d2f97b9f82d4c5aefbe396c532c5f66f069fc171030df1406abb2d677357e9323d92ec8742976bb8bfe19f68a601492e84195a1f53c0cbe38e7bf6991265397dae625ebf152ca344af2a8f399b5804f1023e8362b5223860f37ff240e49b5cc7e6247bd1283b63009013a4a6cdfa97295610b4adc5cd3e5d164037a1df29c8ca1ad9f0279d023fcc73572f73df82ff7edf2d1b6c72ed9543168667f71f5fd1ff8c0838c1c4c59c29bc234e9a4e19a4d507d9b51ee7d37086cfc06d3dec929a5b1fe0473594ab8178ce629e5a7c1acfc4b893c5a550d3b4b6241024f201793704726a1b23509446d12fec549a8822922107f3e96442ad20848e5a9a35cc33610ae42ee86835a35d65e23e1cf53e76be4a08155212347f6e1af4b0a112f83596bd8b5f7022c1859348c208d22dd144028ba6e465696f1d9e7fcd504f25230d57dbc2cb4b5724fc82a1aaa78f032da16a4455a8d0e818ec699722b46f26fc513726cc889f96dd4c8018733f3c76ac883c27e1e8e25852822bd7ae8b9f4fba255ae50ce491284902b5666b9e7a818eec92670f981e404ba44ee2edc5e948bb1619d489411e75341461f083ff0457fee26c43e21baadaf53b83e76d32a0d1d0847535a6d171b335485639ad9e776d72c5a455619ab5a9b19854a292c7ad99638155740dade42c9a96946acc7474dd09d4f2eabe19d0383b0b44c87556606dff8b6a18a7ba3c335e5923af326ea0ed174758050518ee059120f83c089755223fb6532731a794b4d381d8f6005ec065352acfd3f3ec6e799d1d0c4149ade7034563d9268994cd1801eb7b742b9248dd2041d9b03e65f9a7001261af7072a81f8d25ec8fb74f4e77233d5e0c8f5e516847e23635e2c4a7f1ab4820fba760332cd8f0ba86214cad760bafd3f0bbf04ec43ef272072779dd4e94e5e96e8aff803503093865d902a131969f98b3e902149d6c74bcaf8ceb9f401c8a58c2e6c1230cc38a2dde1aae945d49bd11bbeefc178563e23a7bc57b1687c1bd358706b2147fa57898b0b6c6344875bbddaa45b4bc42c90824c0280ed54a8d9ecc85b97326809547402656241d837b4192096d7b806d9880f8c9063a1bcc5d518f47eef232da386a6d6f86f421b576631dc5e7239516bfb5d967355b5083c68c334b577e5327bf3eacd0498825cee6ca5c0a2150ced71de1630145ed8957e87a3470127f16d67d3b324b8ab521e94d4b63758a5fe6e98995d5316419157b20fc3e268f4f89e3f1a875c0e5248c32213758228d28da0b01b02d10e0444bb100a764710d18940a0bbc0846c377551b36e819f77fa02fe698ed0d9ffbd792d7579d236c596a166dd31740a49b6c504c254d850f28ba380ea2b0264dbd9a50994bfe7697ba4362b946b3c28923d60d5b5ad809eb4b400dafde389417d96bcc43d6960c5629774a4cc7178431aeebe9968971b6861dfdd47684db4000ee0be621cffca01ec150b759bf145873a15cdadeac177af9232c90d34e3afcd8ab5950316d7b0115593d870ce110068c5d71e920d1ccf34b9ca698d8f62931c845c3ed4eb80329c6cc2bba47a31e4416b9b174d48027d574af9bbcbe2951b27f816095eb5238d19f26e703744b9897cb77e1649a06c61aaf369e442e3c2059dfcb2dcc689bce5c045f312bf308e4a17f7bc7aaac06154d915414d7bdab08765ebc817824326c6fccb14690b7054abc8d6209ceb5b540623874d42b05dad044f1a23280082fbba4b0f9d003c5d288d9ec907263e3d0fc57b950b8cbf940f5cfd0eaf77e14fd0993fc27d0e72c1217508947ff3103b9689414e26a6146f6024e3b0490f6577804759065ad273ee4caafa80a8f32c17b3cc1f04f3408b80076bd06374d8d17ab847776f54d40029e8e84b131a43b435dc27ea050009e1dbf4494d1a59ee9a889656e2c9484ddb1ac575b831deb99d1476bc83c03077407e7c18d297d019f25eb2cb63121b841a688cf9a3956ddb1a131b670c78e48f5640e3ecb2108350c316dd80aacfe4eb482525874a80547761df934c05d070e6ddf3aff8293a5e9609cb61161c1e692fbdb0970ef2dca8c18561d02cf72f333bce16e160384ae18917a7b06dd5aaf3a17547e81ee5452178e037038448d60a220e595fb4e60a43f9388cbbf500c33b7ef6bf8f0c4d03ff0e426ab970f8abed2a9416920ca7c084c7279b10c6c270360dc300d3a1fdd3869eaa89b109a7aa6804123e58015ebeb92cf47293adbdb30e79c4eb51e62f9ed5341c589c5a57b04d006d16b1c9071e6ce30871d892964481775fca71fab4ab6fa2994ad0f6180ad33a16cfad6c005ccf867fcb08e8bb0f510ac1fa300d551f8200c747d0ac5e5b481dab371ae694b456e70ca9e5be3e88a9e2f2396ae8ac3f884c7b457863ada7fa9b05c6ef57da9747a85cc4d570f39607d48cbfb0c9094f20809a48fe39f85bec2164060ff31a99f8632df8505f468e4e3a739925d03d04cd925713dea5f4089a92e70d6816dcf6256632e67ec541a6cd8d7f3369a60fdae97b2deffd9603e385aec6252356309487fa8ed350fa25f5cc97745e036cca904511de7856d1d122d986fc6f56fa96f5dff0aac7373b65f5f2371ecc19d5b60e4360d51f63326a0ed063b3872399bbf893628c16c8083371ca619202f13d2bf55a0beef6425323427cdb572add44d61b994552acdb78420681986e233a3627322cd5d7b37485f9da9c67220158f3b40f5ba599135f010ef35dd7b08ae62775f3732546d46e06fd150d0471fd3bd92c89ccd2c6107b6bc9a0b4c7b5914dfd50f1c7008d91e0c922ab87768d9d5d9e527f00db4d5f5c1e8292d0ceaabb7949516d522d5e3a85bc433bd72412592ca571bfe52a28696d5b5d50b352bd69eec2ca822c70c1a2b86964219975a0a9181b9c08bc136581db12f53ae6110b6cb781e1f2716f792ded61ae1503953b3eca679a3b9463c4724876d733719a998d5d94c17ddf4026c89bc8cc040706f063801834f3cb8cf7bb4bed97f110e581c2d4412ee7b572c19e241ea598b6ca80bfda8eef51caea7b21536da155fa9551ca0411df26209b208149df6a45c84bd86d7e6b2ce2bafd669a3db94e1e3d82055463a745bdc1852b25cb4d048386e3f70613fb82db79d7ea9ccec43d46e7e96549fc84c323610a2e3d7485030ed65874de7ed4d5fd58103ccdf00e717541c2ce9e3bf0f841377efdbe1eeef073d4edb246cd8b01540e9cb1db3f103ae38ea2235146f63451f2a170326dd92655ebccc7e104610684b0bc26a9384b5d7621a865f9630326a8f90d477acfe6750d61bbf306880f2ce2ad9369acf0e3eb28dad0079a0e8ecfb81b52cf9f1752817838f0552b89c42b3815be7050d42d505367701cb3b88fcf481750319d64602fce9295079eb682070db776c6b9ba2fa1e6c407367780e1d5e482916dd7473857bfc41bb2e49d488cde5155376b911fbc5191392f5cfa13722b8453d77ff9b8a11ab48e90a6a69f3d2df833365554a3496782ce16d30185cf35dda491a650de32facdbd6700fd0b7c8c79ca9f8955c29843780b506760d3063e202a639888848b83a634093de7dd3de0d232a440b34b2dd72336b6fad2df9b672a00f9d9237ac91c84db9209d90826a9cbbafc813d22c0a22f3f0c017825b0f07c2fd5748775d6d1c4ec688046d856be1cbb7cee0368bebda49a3f60afa53922463c7a89ea31acc694e916dd6faff9604d9a1710d49b9a834e05ae75bfb936480ba537f946279983049408e557cae02fc2e0de8722392affdb49050cea80485bc508f1578481e3c975dc96af64de3711eb8c9c25e40fa5e7df1187b189c7e4b904024f8462c7e871bd1ec47955cb8504a4016dc1eaf35709d4b038af3799cdce4f4e05f59f3e0b084dfa509ace0b64d6b50fdecd98dff0cd7e18df964c5f2c103b0f05ad01c873615807aa51a394ebc5c64adca5dbe884076eef3733e69eb115c1ae17421b6d966e93c39e37f9379517dfc28be5a41c7b4f99c49ff4d01f9b2270d96b65fb6df3d2a20d7e39b63f42d2bc04268e1d552799c024ac49d31bcb6097058cbfbeee754bcf0a3d434ecedeecfe4c9fda28a0e27c87321a6810207013ff46069166f952f61097f9533c98369ccce0f9c9c41857adffd98081e287ea8e823d6021d98af4acab31179cbcedda1408db57ba0c6532d4e3aeacccef4c34fa65a0fe5d0ca51ea46d809325cb79ca644a2330d4b75b28c4e3c91f6ffd1b29e01399231a1f3010cb7743f59a46cf31fb093cf3b1109642386ec5957c3dd5414ace8f32052a492d9365a6729e1a4b37a26a9bd9c16b990e93c1ef3bc699050d90591f814298eaecc488aa7d34173e49ffa2f948b3dbb4aa142c160b582b122d013e9836d0e74b4949f5a2045ad6764f1d19c2da1bb910183a194cc0d968ff6b9d30333acf95a86c3033f1c0df5e36dd09dec81e0ff781b9f274785cee4d17b2ae1d80863c239cd823a62abd99a6fd9540bf8102568beb60b7a517f6d8046182e3bd4dedfd16422110193c408e9a75dbb66af3e03e59ca7aba569ea24a803798e8620e5dba8f5533ec0194be621ae9a861c2a9620129cbe467c9be4147fdd7abc9226b0493171ae50d5702437635221381e37ba4abe9586ec3ddc16357d46db46983746e2596c3aa8488e40de6d26b94828b684254b23724e4199cdfe4158cfbd9198a87d3e6661e7a93289fdb205108a04c61157235858bbd1c46d414158a19835d197aa4decc70bd12cb59ca7f3b4c3487d26286ea60131035229269b4ae43702963b10b380b218c6280357bb7b072a6be6eecd9ca62e415a749da0300fb32a6e3ed5db7f8c8630ce4aea79d7200013925d5f5a82e0ad0c73ff738f1a07e16d68757bb2a05b7bfe650c15d1ff4ceccd7582742f14fad274a0d99d94686c308271f5eee84dd273213f9183bb09099483fa65e28ec65768061c14c685f516acd38dc1e38dacfc8c97700040a54dca51efde61e51c4aa63cb2a5ed5fd16212a88267aa4c7bf63d4b96587e50f7fd35d4855839cf93f409f6e976526531ba0b3fd1a8ab3b20afa0736a47c83582195582e1847e253ec4a08a9ea9096bcea529ed73c404207734221c5081d6d20992dbd2f37225a21e4ce57b050fa184aac41d77a44537f489735a232578fbc7f1eebd3863ee09f2bdb10b092e656a36941e023085176a3cec5d4837666d3200cccde65395f6c8218bbd617530419f298c17e46adbaccd88d74bd11e8521e11d7bde8595cd18df4282d6a2655e10fac036f37288c38a6dded7fedd5b7f056857c78c0c8e0d16a9fdb412eb0f0d989549bbabc438e3a5575aee1ed2466076f153162bb999e005ab05f0edeec2e1cc02cde745f8578398425832254c2507badf0b71db1e7d09c2127d9f0060cc56c5d7478eaad481855e8d582a9b14b9da603e2d8441cc024c8175e23aa57fc759fb64f2e896a17cd405dfa0c9ff8d247e960a748a0a585d13d821b935d33c2e0840243f99ce9b586b8dc041efcc8e612764e4c9770c10abf9ccb140bd23b704cf07246d0ef3bb327e9178cfa9588d5695a3a808a1bf707472e2725ace298207c4e3b6915551fb2cb46c4617987351e0cda88c9de85460cf22f804c93d65faa4a41e8c4802c5eb8d70d3d454ed0362026cbf6785ac2f8783f74c12391a5dedb2ac988b3cb434c58a08c63eae1c526cb53ee98a0f8a973148122756c0837f966ec0316b84bb2e02125bb0f802c0200b477c1713b4348faebbd8443c4c386faf0e139e8c34a0d305a96f1fa3e06b212679da8aee7931c3c343d931ee5c1d8f7b14a1166139a28844912554ad0e15886ae3c9e471d1db905e3c3f074c9ba46d063719e5e15860742455dab9d32200dbc42ff3c52e4e5c020058f535b2c5517dcd4cbb633c8e257e3ab262bb6bbe1a99e07099c5f27a9e9dbad753f00ef1733aba0da32833d59889e3bac0ac7c2aa958331187fc844282c6b128fff81ea354b66e9c85d22604abf9681094490541d6802353c4cb4492fb80d83056938efce4169030a1ee8fc009370563d9041122527c3f521e1a9ccd98d416ac9afed27b5c001dbc1196551fb8339d4c0bd9df92b356bc04a0c00cf1047c436b8457cd774f3a605fa1f8ccfa7830fefaf9b6c371e979a3f3e65b73ac6fd2771363caf7a057d1ad70ec1f255dce1ea4d9611aec93ca1b311a6c68bd7385071f63ce278e0b884e30fc645a508ddbfa8b1f9e61ea05f27ed747aef99c078b89c7909466e2d7fa9f70c25ba5b82b270d475358088c3c9e8020193f9132f9509821c14da3afbc96d4eaad5f71f65dd81a5f31bae55b7e21f3aa8317faa349ad6b911b84bca424d021f0f102ef5a77f65fab95f50a088a0c5db2a0edec342c56a43d2dbfab844e0c3950ac22c146efeb223395b3f3307ec3ddd385f97cf720c213e92aab7f591773cbf2fc72e98332f29a1532e23b4a30cae0f64071f6dc472eac2d8d9bfb7c80caa580c565407105d6e059d24cfca6f01211f32ef325d3529f43e11900dd5f6e398f9aba9dce8a74232d9f572475c3ac24d277a484b4674c3dc93245b1a35d65727fd62782d150157ab79ace57ed9f33e114aca44a58dbbb0a36a36ff91466f70a7d232d91d14caf11df2bc0c93aea6341fb17e6654d69a2f6f3cc749bdc005de62402de0d00a3193f1ed3e0d888572f1e6441076e32b61bd9c041a3d74369a375787b7fbf32249d6d016fb34005d353c477f57f99d21b035b8d93d5b21a6d7d1f6ce869d70c1c4cab917a1d1ce36e637467f4f69d934b1e8824d31b9bcc6daa9fb5f75a5a6852fc6a1891a40574a16d2c31332f6917d693c4500a4b66278337d0a3445b8c9f3d9d9ebfb0e5aab3f944bbafd2a562fb8015e2a3bef734a00abb9c9f57e1cc4c6c18fcc3b102106d43067677fa70c0f29e84f5fb1d18ee854569a53b5ab12b51fd70b76ff639f4424a457bf54c31e6cb60bba76c91dff5b447959a364bdf8183b87c971c1f61941608639ad9334123440e90060101297c551cc94fd03c9e6ee2705882f73fdc9168213cdc1ca5b028fcbcc554f8a2519702b8b501b696157ba613aa8b5b6c89c7511f01680023e01ce945349421039d1d0d0b5db8fba31f69c61c9cae74efbf84462aabfb49c86ae521d2f3a6c3c519773e164a587bac0b5b0703b124a124c15d5ebe47acc19d5b299f71253b570f2877100e52328b1bd9e8cfcc708139f6ceaff47d0138319df6f073139b3ae92384277f82bc4ebccda083670d786c400032b4b660c8bc9b8928e27bced31c2a72fb2262011608cb757afb1149041f25f59c3f13e470d17a769c2815469ed6022269e2e9cb35d06f9fbec0f41933c20375650d05068fb4f107cea7c40dc6fd9eb3693fd7b451178c4f97714d7c82bc56a2bbb27a15ccc9b3db3a4b800d0cb044504f716cc60981bccd03afd27028843e5b1560afa9f26ecc0c250a1143620fde42f1aeb0a3f52390e0e8b03531ebdafa4983c79bdaf3c29042f325bbf8a35316036c55deafdbb200b432bb8cade008847d36b9678afb302c0424afc63502c67b31a86a106a4440b1c1bce636130264b2cb29fc7832629f4110bc3f81ad66a3aeb68d368e797b4edf32ba25bc4bb20ad26110b1156954788d3f54aa6f5feb00ba507ca811e94928c054414cdaff484aa0e5113ac9f133bc4d86e8f9c7e770bb6e06f78481b77e7fb7763802b84743aa05756a571d8300c57a4e990e7e8565ad85cc645c0d123165d718daf18ae88d76afcc095464cdec06f75566354aa902794a024ae4a897bab2195cccfef9e48b7c89674ccdff141e30d84f04a9b7ea45f7c361e10833889b881fd89775ffcafc15f3f917433756431879992d57cc32664cbd75e6924c6fe52201ad6f0d0af81cb4befa3f4038d7a0e315fc74fb4b82247eb903224fe78e780d5a15538a2607139a0ffa33b5d25d8145c2a9f3fb3198dcaa9553bb29c9aded3e6f07276cde196f0eed92f81951f4b50749523e9fc86986cb3fc184cbabbb44ca9989b4939e2ecff87973cce5d3e06a00b0e508c5abc7dcbb59e63804d7799fe41bfd8292042cb8a5d7ebda8f1a6417734f3eaf97526aad24824debbaea63abc0f0afb516ae3e7f67eb90d2fa14e43290274e3d1b7f8c4c69326b4c006fd8c9216efdea0ba574f016f4f38843e5851846d61ee2249ce1f6ab6a5f40f3a2d5a69c3e813be502e9343dc3d57d3c304c088fc1d4723dc2e43af4bd6129dcf1761625a3641263334916071bf828820560347eef7c80cbe2df71989a04e6efff279a5ba8fe7c3dccd78d2024d89aa97526ed6b300a964e37be553192df94b0eef4b3d4c60ab4305fed7823455482ebfdec53436b1fcffde895b9eaf8660e1e23f8b35b3a44eaf8fc7edf5903dc85dd36f62e769bb475eb74cc3978eb0ac6562465d2a4b10c183e8c7628a9bffb3bb755b7d558c722dfd8af95a31eac54c88b7b8ed96fcd21aa2bd1cbc6a9601b14cfc0a4591841895655d8c5b40992210c73d4023aed8ff2786bb2ee21092ab38efac24107f724e7742cce7ce42583f3fe810f8001ca69b406b18879a2cc45c036a74927e5c4094594d1b157fa9a7b6266343d1961ac2e1216c390df15b5a36276defe322b6ee43766ef2e99a1f94f6536cb91e3c0923fd4741d4b667689f8cb98e65676dbfc7f568f06784b02e6266d492788c26baf7f36d1322298944767777ef6608a607870710164452df509351c2bed764d26ce8f3afd70d419bafda5071ed6cd0e76ba60a33850c710afe551bba94271724ecdd10b4999dced0f619a2d9ce36be53f4cf3ee639f8cb1ef7ece442cee555c74a4e4d4896082e5ab08839c1bcb8ac6831b1a85829ed8edb74d6326ca510a198818a95d2eeb84d672dc3d7564a4f90a1047a0961244451873021a274215c2004500458f0a62fb343104282d021882249a4e8557637e505db1a61083c82b8c12807498438d02e75084bb28412b6098492fc24092287243d488a71ce387b92f0acb2992892f8008293e8f8b8492bab841daa11f5c18f06b02083830e6ec0c376084b0244a6410b7acc44c1c4eade7b6148944082040e486042c4a29919243ff43b67a2b81709907e67a2b8d7670afdce44e19334130545a20449911f581040f121caa7894f10fdc70231f0f1c167f5237708f369a520480f1c708f1d2e137c24a1059b8d02b659a553be66b080246d141b083b138168ebb08db047e0c303770823818f0f3012b06024d0894899081288a05b34a3744174044c46a044d711f84ca0df10db3cfae732c506a2083e8660a040102a830cc80c16eefd319df0c1633a114596f1378e9420a103040d829e68a0c5c451106030248a0b72d08307cfab0ae67522034207da1002d0a59c3206e6814337c514534c81733d1c36a0a3a3a383633f5328650b3721cef5204e8c35310151857e33800e9fa6cfcd06cc8436f2238d51ded474485f76ca3f0c741893080944117a5cbd5adbf60f1f99d2b50e614044e951015d6a1da399f42f26e648ff66fa878108ea1f026e000425f00325d14c0ca200bd98084282bfacb0a49991c1cc0c60475ad1080fa08d4c810aa40daf8e7bee86664c783329a594301c80b26b2b951276a44797a80e61468460044a6dc203186dac017ce2862292700e32455c0530233fe0c10ab6112298911b2cc919c68f7cea54993ca68eac12e7ce5cc58f321445ca50942222a1294853a8204da9927414f40a82f20a8a220434a39493d62969b5f6e24ce24ccb526a596f526ab9719d84cf4581df0d49ef9351a447d4a1f491477ce411d9429a4205694a95a4a357109457501421a0c88a3b911577e291d85a154123188345700a3c8a52ce49698d724e4a6bb5b45a7b2fc6d9c559a669396b2d6bbd6d1cd7c52eeead63696545050b8b69002b6056a1a5f721a0c36781e17d03e8f04d262d75c49791cb24185398b556ce84dd83c81146448aa04482fa07e389fc456f86c786333c367cc977f439e78c2fe79c734a2f760a74890318ead09c53e6074f13439307a8cf39e77c18240d05e694d9d95a09789084cf806748137856370c39eac1901f386188149e1bf418f40f46126698d8b14f0fca0ea9941fed391f98bdd20f41fd5297f4619715763c6b583ae474c78c91d28fb446ecd88b909f6ac42ee1173bbcb25fc8e9c54e32077677993136fd5c8638bac7e3c890098876f3643499b36618949f6a6872f6293bec2feae9ae793cf4fd1c7934b1b8896d92fd075c7af677850a718b1c2ddda013534feb9a0771b2aca716bec3e8e2419b98e1e945684365a841a74fbed54ca7c4d7c68cf340b7d4cbfd97643813638c94461a299d1b4b9998930c65fc1ca590b743588bca34b2b33204daaa41e5a4603527dc23e02240c88a48d16c87b02234c8455e1a5624898da5435811241cac089022376c22544a448c8624a9d02b6031c15a4834ac256445cb878b2685978c0a4c4b87b01f8070fa01a6613f2cc1e2879f16b01f5aa60e613fd8e0227e7e584f117a8a388c480d6460445a30f3d221aca78582f5e8bc80f13801003c4eec8ea3e16e0f79c6c757d950fa1713e3023b840d79d2e34b29b53a7397f50c250d07254d0c84bcb8b8fccbf810d2c382223d4e7c2022e4d4032b318112a21eec0009306841910c1244870829e061c20f5e3034e3458d1cf101002d2984bc6851f1819ac102e5437ba543586b08335a35102253440adc093a7847d87920fc702145871634e8c1e2052426891d4e4886c0140122e4a5c80d3b2e930a90153358802f2246ac1697a71ca92583fec1d70a76589ca840c25a690901526af9d8d952d0a1a3f2830342cf06e3d14b04c93ffcf4a0fd60034f46858722f450f4a3063c30222dd8691181f5e8b49cc0038f139dc28c342102a4c6890de2e3f82e2e905154659e30e79c35c7740141869cb107324f98fc2bfac010259d001dc28450f9d83d98ecdab71ef0a92e1d21ab2c03c50e9224e4a767df82058992c3bd7eeef367cedb3e7b5d7c1dd4f5af60a261418ef4f938a84f1f9ef4f95477fccbd1bddd6ee8e7b8cfdce7d27ef96deb9db7d7cf716fb717f277a7d26bfe9d6cc0de7d1f0d435d3fec340cf5eeb9aff4767fdd5888fde5b5f7e9dff88b28b6e639ea2e7939aaebe3788eeaca2eade24d1c4a1a96173afe45b8bda0c2fb6ae85ac7bdcedd73b9d32b2baf792b5ad7695da77dfdeeb1d669f8351928b20c26c408260428cbb22c8b9fc182ecf44c975e735fe27efe8cc9655916371b1df7dadb3ff56fad6b6cff5597abba3e1a86b6164c7545d441c870e663c1ea324e5a639cd55eba42a74a17d09320201794974b537b71562da4b45e9c6999e63c65cc4f5c2d3e17464ae1cd3429432f7c638c30c21b63a63356abcbcf438e74f9bab529e9f2399f8e76dd94b1636d364cbdbe84a044117a99a498865e364cdb4d3251f94c554c49d184f4b5f0d03121459f1f254c7e7c9eb87ce2d3c8be85a8454a4b510b51cb106d316a8951de32a4b3e2b1b3d219c2d29134f2a39473529a693adb3af320d6d90a3de10e84d0e1cf185c213ffc38840f1dfe6441d144872f5f4d40822b6021483abc0fe1c30057b00f4f4dc03c0113c48b94971c5c945871843bb92df253135dbe9d73c23cd1e5574dd336edb5c673071344979fcdc96d597e91d2e5d3ed2587d8e7bdf7deeba244972fadb5d6da1547882c318cf7767191433eafd7ebf57ab1582c168bd5e5b3c458622c3196184b6c06afc2937fbdedeb8ffaa3c6baae8bdfe5705f438f187ba9d4ce90feddee06cb5581b5e7300ba77d2956bff4bb37c6d8f11cedb98fddbc77bb8fb7fb3875a47fdb5fef9b1d77edb7eff077a94d6a3ce74adcfd9662f5ed1f4bfa524a193b2d4bcdf29147ed3b7e32ddfbf77e6a256fe4e34fed44d57d11c57f2956cadb6c707f79cefdeebbed8696f7b7971b96f2da881bfeaf2382f1ddeecb2df5eabc6bf23a2a49376e37b5d3efdbcd8bfba7d782bb4fad341f2c249281880fdd1e756bbfc495b62f456e7b2e0524469e67a0302fa4d0baff3aadf45bd67d1d916e3fed087730abbefaa2abeb5fe1b57870e20ec7ee77b7ead8d56f0ac8eb4fa580d814abd3bc8e8aa4b14829205185cbe2b3e50a256438f3995e9d965c43fd3b8499008a135687d121cc15eb2678d205d021ac044450e9bad75709758c44504f4087302478f423a4749a0e614730e94718f5dcb5aad571cf9c56b1b41a638c91fbe882b6d9c81ec65a5fd59237b6c6fc2d3c8e4575bdac78d343a8a2a4a5b5d6e644fbdbdafcdabe96756f3734fcb9fe0c527ffe714102259552e2583fbe503fd6eab2fcdb917f2ebdf2eada4095e31d3ab47ed590fa5aa5a4e76c5f43af2c5eeba8c25175c4ea1cfc5b8bb7334817375ab850b554fcc397af3f60fdba822bb4f8caa9f6f06f38bab0597e72f1b8fb7dfa985abfaa58aa2151555fd5d29b8dfa96e7d8df3e4b98fd2e2d31cf925fe6fa5ab659b57e4cfd53fd5dbfab39ca8c371a9a4ac885b7835e35bf6a48af5fb3177fc66ce1719efe18efe441154bd2542df6c23fecc257f06fb77016ae827f2ee87f347a45d56373d4158f965e8bcd189b356be87f2eafc5f2aa1295e4ca97a304371762899ff0d7d7bff917376bc6c01dfff076b1ac2fafbc1751ec1cd5555dda735e5d6d36f4e76c74bba1bb7e9a19a3fe80f007069dfa10f997a3be56f54415542c554f544155ab6fa01db481baaac714135f8690e353578c31de1eebf4484a19371b574e16d0d19c76cad711ad14a94523d6e82334ed0787ec13f75afa9547a8036d286731f97b13ce19a7ab1a551f1b7af782353102c684ca26678452075de6a07fda098c69355d37084c1be39cf2a7b45d0c3264cc6aeba534a8763709814d5619a3076d706643634a7717c3a501014dd2c8526bad758498a39b0dacc1a861adf24c0630c08ae594d55a1b657f7048077bc85a6badb5d65a4e84ddc4fee050d05e6dcc4449ff60c4a2a0d5296b30b4f1a66c593b9cb57fd4ebb67e08ec973aa5d4c6ded20117ddc274ebd2ed8a9605f49c65da4daf1c9b3aa55446eed1f618670f40cf53ca38352e3eef70aba151aca5003a7ed1b1f4663af6643aef30063de739658c314698356e3e0bfadd6a681d6369b34a31943ba8b45358e22a82c792244b862ce161490e3f51f869c2cf9113fbe7c9922292c42543e4121ee6921ca2d0849fa39f218a5f3f40d9cf931e7bb0d7da1f1f3b645576c051d839e8616b60adb5d65a211fd4e55fd65a6badb5d65a6badb5d65a6b5ffb83af24ecd78f919f20fd8341257b30a20cf832aab2875bd41e8ffc3d300e8f26f3e2d79faf17df8b389987bd086dee4f4f429bfbd1eb57e268afc7e334f9009e5ea963ef3da8827cebc94000c01a59f3a91003d6480674f93d0dfa9057ec5168135730f51ab4d70c07e35c9c8c75f62c4ece74f62a4ed674f6284ece3a7b1327ebab35ce7af4626ff78c35debc899335bd791227e70865a61ce558644d941b8d6d4ab8516ef3341c9d37bd71b9cb3b42b929475febd9dbd2cbdd76dacd563a96980893214c78184105a9439811b11cf4f8da8828460875083302a87f9e114718f16344cb889d22aa4c89b688a2c9033d835b853ecc75082b0205521701d421ac889f227c8ae08151c44e8fafa9107946082b4fc49210e490a20e614ba2f48fc296c09634f143b4246849123c2c71f52893e49920fcf820a20a8c88294410a1fb60da1c5022842c112cb844a0600811ae0e6144f8101184886f0f51a57f74082498e12b5990677e10e30b394b29638c415dd6f0ea5256d1e9f5e5131eb2ee441d8cf1c517637c31c6f8628cf1c517637cf195b4da4bedc519ceb48ca754ad66bd694774d51b67abf7451eb53ec7a3d6ef6ac5b54229528a54d2eb4f22794432e975066946da8a6dfdfa337469abad3f41108362940d34403660b21077220c336cdd31a44b5b77b4bab415dbbac3a74bbc83d56594724e4a6b9473525aaba5d5da7b31ce2ece324dcb596b59eb6de3b80e5feb7d3518d9aed376db5249db157c6bf53e18d564d2b5a545d715f663b7f862efabc1a8d75761d4b1c77284bd6f00bddecf268dbd4f01bd7e4b0d485de28f1de3c71d7f35c47a9d405d16a0074af87aed21e80d11eb354b3831129372441094700118b0800712c482d4600418ca104143a40036c49121820ca14495a02694408248081560828fd70e4c204594222742501102e80119cec830617f75a86b3a1b949456496b0d9a8ed712a4810fb71a2c9246f6ee3b1b1bdad0ef78ae1eb4a1ef411bb8c1c023871cb61a196bbbd19091655ace5abb5b0c32660d7da86d36b4de7dce3a6e31742e7a8380dc68d0cf3fb7aedbdfed128636b0e4616883e96643db6c687d7fd63a6e31ecefbf6d10c0b306eecf9c9e1b0dbaf766a3fbddadfc7e9d6d35b286af56fa150e79f7b177fcf340df1c9e5ac01c9741da3933e6e67d598732604cae0d6933c87046889f0e614a98f402f4adea23573c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c2c146beb684cb8baf63e301448297d5e79d39b5c7e454ef7db77decb6b4fc6dfa0f4ed5b9ec050a0e404417dbe667979c96ddfd98853e8eee5772f5fef7ff13eae73f997cee5bbeef5cbd370a45f9eb8dfb6efa3e1a86f0f3b0d475d3ee7f2dbcb7ef9c2735a3e476d752e1c4a1ad36bdef2f057a8d85a4c2b5f7fe31f8b0a152b2b2b2b9dee563a5d2a953e7b2589626dbc6d1f7f83313105c6c46bfb747c0d0543baf6baf3be6dfbaefb72d456fff4f63e10bb7c4de39fde3fb7df9bec1baa87cdfd575b5d6f3c476d75cd77c7ada3968f145d8ddf14cf0fb8e85176173e0e386b1e7ebd95ababbaf26cc897efd9802c501e78e081071e5eb873ce3927a594525a6badd55a6befbd18f380e241ce39b71a7247951a0e281d503f76f418638c319afa8c4929a5bc73ce6929a5b4d65a297c6b276a878dda61c7872d484eab0e9d9f2575d578a190b30c710a2dcf8f74cb8a6db000bbd65e7ba5e7fe6ea7184acf3d8e126fe1d83c5fce7e7dddc256575de3281f5be35ff6f1bea7e5a82b6833bf63f55943bdaffee8f36ba03b34398dd03a42bf524c798a95625d38e9671e9c35947f33ace84f6a2705447b69427dbee6d2a0f4f95703eaf3e7869f14901bb4216b03cbf5d5279d520e02554bd25c554bd2545a69f4ec572f5afec5219d7ea40fb71b3cebacfafc1932f3e8f32b0a07161943478fc751f5c403c49f3a78af5f43b715876d3d9c594c26ad4f80324893623e82b514f6579130d28c51878690869086909eb073b2aff96a444531a3234dc5cab8aa87d757852244b19014b426d1aa3a22c5b61f85669eb06347aa55ac8ee56157f206ab7a90928690a85451f560150bab7abe0eb8b6aac7b2342519ce7c15a9cfaf2d7943eb911eeb6ac6e03e729939d6388a986fe4db63a9b2ab6bd6ccc778bb0ccd4061e7e0aff9b1bcb94f806a507dc91b2842f26607dc72fc2fa6562e50cdab4aaa8fbca14aea8fbc9190d6992860a775488f3f1385affacc9f81c28eb548de64b16a246f8ee48dd682ee5fec5f0a4854817f3b9c1c752279f355571d923710e624d6e75729b5a8cf8f5b0ac89671fa37aa46bf19e27d6ea398db1492f604a51ff18f0b128cad8e7ecc2dc658c552f5a85a916255cf2375fb8429f2e6edef2379f39b8abcc13cc63692bc4932b217e3a26e1fb5c4fe2a50b70ff5e9856f9ca7a3d25b9d1923bb668dfdedb2ad4eb7f6a7db123fb5a08d7d5796fde69f8b8e7f2e5c7659a26e5f7ba6236863fff4b2d99e8eba7d94d0b66f4242b7af7d3bd6ed8948b756e791b81fdbb66da9157d8ee3ecb476db78238ed6da4fad3eb59362d9b9c518b768adb571dbb60e68d6e0efb8ffea928e5db18ea18053401ee38831c63805845e6fbedc5027d8f63fcd55a305fe0e66bd7c0458c73fadadadeda7d7627b1be3bc17526c78843e3ffb34578d3b3d2d5397876babc7c7ef82bee00a2e3aa62f68833f7a9ba7250dee5e1d7ff6bedd69131d7fe6eaf88725a6a596c17cc2ceb15ffbacfbedbd1905b4e7c71823fd5aefbdaf62b1543daad6b743d513e955b1543daa96bcb9d762554f8c2a56bc5cd503352114d056b5543d1a127662e24b6d8a7ed775db0daa31a6b7c6faa53430c6f83ec6297b59bc524a69507c5333a60bd7e71928ec9cfb1568d55f3a842d31a5d7be79c81b9dbd236f2cff52df25c99bd566c99b2af8394a898d52627f95835972d2c1df0424f498ed0ba6b5eda0092f2fedbf2c64d2993551e7ff6096b0fc409b78d2e9f15131d8f1bf2ea9c3f898562c4b4cac97267a3deaf1314ea552a9542a95b2a914b695bb32131b27a5e4aab49cd4a42635c949a93da79f9352729ab37393fa3797aa47d5a3eab127c8f9eed3afa1c77a6be5ea73175f9b69afe9d42ab5933a92931fea4eeb4dffa6c51bf9375b7f6edbf6256ff36f861c5a4f2eebdf36ad498d430a31ffaeed57884d398ee32ad538aee2e7aeaaa56aa97a9e9b254a39ca95bc9ceedecb7de97e89a3b4ebfed2d273eeb3ba9f7f1dd8f1d5ed52f5749eaa27aac0a97a543daa9e2ede1f4b14f50074085bc2b5844f8f3e74085362a7e76efe497ea9ebba2ee7d3dec658bffbcf1fed197e2105e4eeefba52a92b95ba52a92b95ba9f31bbfbb05b29f1aedbddef5be232cc5efa6ae870bbc17df73364e97f73986f86fd78c660f9cda528ed6751f1d9ef6a897f1dd8f159a0defdc6f9eb73dee6511772b47ff3659134d1f2949ea19dbc50cc6dfe4deddced5ebaa556a99dedc8bdd9bd74dbb614eb6edb86b7f930dbcd2ea5fbfee597e39bd6f9761e9c1a9f81f62c0584456f12e39084ce9c79ce24aa740374084b42684ed5103925f664985d72bad5d8d131bd57c5520db92a2539f5a1766f8c543ee5343e7c98045012ac17526c9b658ee45fd4b2965d16ed728285532c8af1c414e3144b4bb130c678c6283d9ca599a3aebaeedb4adb665ea6cd6c7e0a480a480a480a088b93d6071254908842a137df43ea07e810f62428f62ff28f7adf961acfaf3932e8e8f76787f9542df85fe433c8ae6af5274c3a003a843de1d1f3d5b6baba76b98d6f474ec6e7ac3f794efc7b35dc5cc8bdf22f3e9d9c4566912e814bbd522c96d44a67915721acb0ffabfc6e9b8d9cc2a9558a256ff06b1b0d1cb7cf013f7bc82e558f8aa562a95a3d725dc95aa1bdf7e25bdfaea00828a905fad2ab54d2392795a91508e494f3b996d136694579c197a3ba6a55f5a886f4a86245d8a79498d2242758ad2068069ada49ad522c15f8a8a08704c054a0f3225281eb14258ec131965a3bf97421627f9508b584038ce6dd4749f26686a4995b1a4dd948972b40ca182a892d636ca24d249f6c2a6938a594d2ffe4cb79e3fe3743d2cc77c0aa85fc18fb9b68ca781fc65e0c47fd4869246fa42c8ad529f2066adca2c5d78d468b989898180b0383710bfa39c29c69fee5af12e5d7748cb939c71999a7bf75726cebb4b4a20c0efe3232cff9ccf31cab44d4148362735f4bcbaa4f150f23d4d26a39d2e2d3a2a465b5fddc5a56321430f9534ae9a5f46310a5a698bcf964988ce40d354d9137decfee4dab19db44bb48dec4fcfc1d336ac5b8ccf3c721f39cbf0c8e99977919ae43e667baffb68e69056de6feafa515e3eda21ddb4692466a6fde6a8ac1158e660c999f2b1ebafc377bcbc36972f9164e63623e5ffdde7db8c5e018630b09bb6d3163c6780a903453c63bc9e72ff333de2906fe327f921c077f99138e19d4c3bc8802e5e159837fc693f1b847b90c4933379116f3b4a359339f7bd9466317499af9d9b68924cd7c548ffd55a297ff36517f7938636ca259331f26c26c980b0313f39b6846fee17d37116ed1e259b0f8bcd16081f2b1efc7b8882e5afc6df1d4fb36d05bac3c0baff4da4663e5f17f341d9fbcafb6562f9ef6befa6345dd445ff5616969b5b44a1ef5bedd117d447dfee67d26a33d5fbb97ebb963cf19cd6646397b6f0d2a43dec0181067ce938f0b2963431dca1385c4fe34ccc4fabbcf61fb1943044de48b407ff77793db2906fddde3d0df711cdb732c905a8cf1d74d66dcb5814ea9ae4399ac2f589d7e3eea348605eee7fc19f53327a193d04928cea03e6b19922e4c495ccb50f4febdc7f1efa1fe7fe61f06897331e3e9bf8fb5380f6e30f040fdcc67d411f629f02ff314f09ebf089ae09879fe2240f1d37c99ff977911ccf093e531c8fcff69721c32fc4f38de7bceb3cf92a6ae503ef6fc2f4beeb56b035d47a9513350ec963f99923a55f1bf62f913d1a9e8143b1999924c4f4d4916b8c07f96c92c9b59a63fc39f6559863a92371f8ca1a6c89b8c8abc69f11409e5f21fd33f31a527a2579fffbf03474e7e8ac1fb7f1cde3fd7d164be8e7f8fe3e0efbdc77578cff90e9c9cadf8af6508c58236d4e5adf731ef8dfee8a7481ad4110a69c6f09ec57fb3c3bcc43c4ce6e5a95b78f3adb59b9cde8e2e2f206928b700370d411b6a229a48ec95370dc115e4d33711cd18ded36e356be8773e92867ecc437863838034f459cc2d34f9e7b282d026bfe7e15993ff3d1b04b53ef9bdfd2cbc6ff618fed51f9ab7857687f9defbaa8f8bf76d272dde67f23ed4944e9fc5fbf68af7e9fd3a254f87070249b3c40c5324a71f737c2c86e4a19c6c386ba0906cb23ffbc3011bb0cfbf54c6f8185eb6ef795ad6c4f7a0acb9f667d6d09742f6c7fed81ffb73e34a4a69a3fd69d151ac22f6677f9cec7fefbf2d04a3d38f791eea68d6d07fa44e51ac22f68fbc914042f2067f669958977d6283eccb4291379253490339f0c2fc2fe3f9a296d85f7ed58fa18ee00af6a1ee1ec21827220eb430ffcb80a4a13f83a4a17fbfb812eaf44f44fbdbd3c545979ff1cf45ff4e44ddfe748a4242a71f2d471da18cd85f7ec55ef2460ec572e661284251e48d944c246fecd3cf45f246726ca9cd80a479a3a3295492be8abc99fc0294722076cb3f066de8a390ec0f267dacd3b7db7c10cc1854d6c8a79472f6618c94b3b15b0e7506660c8843a9bc60c5c8173cb13e3f4f988fc633792f2cb487a046f41f0d572ad158f94cbd1db75d2a95b25f99316b54c6ac51096bdd6894361bdcefafde363dcbe33071168e430587b346cb99bff4beba42f9d89286a41eb5951957784a7a9af76da0d79f316bf75f75599aac51f9420a8dca18b594f5c9565d92263eaac78e6f1fc77977bf2efd7eff420a97c629ff5e6e35acc45edd523321c319d41250346bebe7ea656f27e454a394d8fb61a098ec6f77ad804e278e7f595c620640fba4772af1e9926b3128f6a7c5fa7fb9b592df09e5563e226f7cb292d5345addacfd8df74950d4629a9176a44dc9bafcecb5155c213e8c11557963a4d3732bd66fbcfc1443cef775689f39c6a13f7fe63af26bbe0327f2d9a2c7ffa1a3ad7c3a51ab12d5224913ab469246a25c5b8b69471a525297f362ec637fd145743f475df5fa3326fcb87db0c438643803852e4a09162890011c1770000c15b000ca04fb72540976849e9611884d639cec6bbf03277bed7164af711d4d6cc671643a32be03a71e412dd92ea825db654e1c179037935307c050010b3c8c1903e0d4c20f423eb5207fbe0df933d33ff10e485e8305afc1421ae962b16a0409c91a4142ba468c18c1466e66041bd18cd0cf9b9431460155045504550455045504e564d7b7303fab2ab3a11fa2eaf3537d7e32ed47fb919c0a419b5965e772010bd20aae603520a9d5d5ac99cf62b55a9f06f423595add5177c4d2ea8efacc62d08605498721428408b9a89df8042ac8def4feacbdfcdbeb32f7ba0371a376b6ee7eebcfde577af8dad39cdedc97748e1ccd513bfbe3b0775f2ae9edb9cc73744f1f6e37b6bcedbd514168a076503ba89dd912b52b88492740873026aa50fbf5330c8d1d06ffc5fca3e1a8dfbf2deb81a1a124a8d40d5676b5746848200000082316000030140c08c422b124c8d238d5e30314800e7a905060469a0a04b22487511004710c528c1840003164406466646614008d08a6c2a56c1f42da7a304f435a7f71c507e29e3c383190c44acdec2a68cef551009d417fca17437877699d05d3d96369f2eb1d1f4266720d7750dba68f462e68cd529690779c447d5f64a938dab711853dda7a2521fec87534df854efa41546b0d48e1d7d31276f59ec4842a67504237525648c651295040ea14953c28fcdfdaae05e72fe3ce4dec206e57857315cbc696a0c073a01d04b28d1aa54d7e4babeef14ef386e99697fc8041ab6eebf2f8d12e680ad6146caf1a26ffacea9e105490390912ac1009ca957342cb4534a2590f75893652615db2e4b6de135a7c1e8871e4daa56cd992c761f9a55f0c6b433d8c661ce555b04238d0f3ac1a4c5f08806ed27d135b3b5ceab8787abc41ef5bb1291b6d658d4125372092df6051f8890ce428436d8e447786329cb605bbf64219a6e00684970965e8e1a00c2cdc02eb7b62ec80956a036558ef26b1d40abfe09200182603812b70236c5b5f1522ff0f8ea372274397eb3bd3a8ef20c90df4b6c13a13106cdef6b942cc7e6d0a5c4bb6cf803848dd5a64707adb9be5f2034c317adb9bdf95085970fc30eda0b71d642dd7d7f5d449d049213fa56479799eb4a4efb0bab4fd8d8d85955bb45afb2e03e88467c4ec24070ba0179e835d0282ae078969869eb50f3630f1298d3721080b1fbcd9508c08f3a3059640aadbfa51978e270eb307f63d9512535d5fdde4f9c9e84b00bf5118960dca144b03d28ca06021e27db8ad42fd1d2f5e00de9055162044a46b8b231a1698f168d6ff5b8b7354b8657bf6edfcec912964754f0ac350cf0009d85153406055b73bda4882f73577252dce5e2a3809c7f6f531d04ca58c73e0d3b6906cc121cccf72b42be62aa41b9c18f43903331a1503a18ed17c6ba00a5ef51a7b69035de9a9ccb3ef9e494de575b7a37db0cfc50c242df45537db0d79ac1110c91dd5e619d1e50193e0deaa3231c3b520ebca6659d886dd20c2176ffb61f6e5264ffc4c01d0f191e78cd749dd156e8be30888c9733ca388822b6ca16f6478aaf19a8107944c8652fbe839bfcac659c39dab571181158357768a3ca29aedfbe33c1185202d15047fdfa08c9b042cb3677ca34733ab112d290c9ca21b60e2386662059dc26db027af2921381e073ae044c1b9ddb6c6df18c9eb185d90a0205cfbe93e2473fa77e5a83ee1ed641b725458dee0d5809d1316af2d3e07c48e6cce88f7431df23d7735b8dadf2fa22772c3ff7c732d2366f279b24f8f67c07341e2589359dafb07c595fd02a051635fecf3d18a6ac9b78c92abe0d3474642f5831d17ca6b48eb1b911a024682d2d86c15dd0ace3e1093669074bd9f81e2003cd1dfc34c5d3f79c3d0f3964f3eb0930205242d5c1ad8fa16277063f8f509de4233b735cfcf51cb07e45cb8a0368780b977c0b0581053ddb12c0a9e87fad2f51ec801c6a068d828a205dd84f7c1a8e76da15eac7b80617efdd48c8914f04d1ef746287dd57982fa7e5e921985007cec3dbc5114b7f940ac438ec4df3dfd9c8c5ee35d881bf84306188bfcaaf100532201083bca647fbe48ad4fc11f95f2e7859c2571418b9873a01aa65c0747c2a3e05609d2ad930aa95b6129a607577b252134433d17b915ecb86287e20cc11238cb6ff8314efd26272646a308150583508e26473f568cc0a7331020f09aefa492ae6ff64b2eae3c966b4cb3a790f45b64c84318d83660e93d74dffa4ae9d6db66d0f0ff0fdac56cc0b3a9caf8b994c1063afe9765fbc8d0f4fcc59db337121b05f88b244e56522e4722d90b7f7c90058e8f5018294ec6b2fabdbca798c325fff23e41e01c27ff380e720cf79e71da807970fc10243f79a951be6d5cbf7e6b306d2a09c51332225b6218fd33f3b947c9f7c3c18d6df2ebbfd881e80b2e16cd7ea3b12f0e2161bb8ad9c5258e900a667037bf5ecead3fefb7c8cd992b2682d65aff262a61e660a05ed5b615f25edb8ac0dc670a2c600520e4ea8a8bbe8f4903caf001e9c9aec48f482f8e2362b99475a128e40086cc1c8e91f0fedd063b9cdd00fe171cf32e3c46b7258bc349c10321e65f93f75e2e67b52f3719ee846ebbdf2c1010ac1a22e845391158cef5c51252538b5a9567e1f8a4f87b658bc8d4ad609a82f384d43652a5f47c090a176e0214990df38763975d84002c536e26d565f242a7a70eca3cb165a5eb98ae631eea1803ca253549509c22de6956dbadd83a8fb4ca69094098adac7a14796f3994c0522559c718d311d7e087c5b1339629d4b21a427f98c10e3bcbfb79d9f9c56d0790ce3077b6425e47ab35171751a6e69498c53a043bfac51c5c87abd9fb2223a58bf750c03787b27695da0d9f510e0bb9ab445b570b573f1c4e6b7941e6af4e4a7a23de3de4a002545f129203bda74f33ba8bfee3198d42be50b556449c648607a7fa0a376b42e1609f2e74bf094ae97611122ddc6d91ff9292eecefe2c1a25e42247b50a98b292f1c122fec715a37716826523beea9c84df016ab05aa7acf1aea6b20385498dec0fbfb749e99325d9587eb6a86f6bec8e107eaebacf66c2d22fae28cec376955582697ab50ca2fa43a88187ee3163ed9f98f597fe73e32430656430b1155f602af0bc8ba71934493f3f1f4a8f78038083a00c8117faae8486e6d5964d9cac33510950a3e9358a21d095fa0aa9a58ba06a8840c2b9b5e6113945add46cb0eb101f1a1caeb48b45711d135fc4c2420824d806221df8f0cde0ba066b16674b3ead316ebc09b3282fd0b571456fb3cd8e7d639d5610841b6966f6b7dafb740cc02f135b2fdc67152fa29a8f7aad36331f60cebd0403b7a9e49c42208066d83da3020546e0ec26d7e7941829434aacf85ac57751ebebcb32dd830bd96151fa03916a086c9352bc530369f4689c7454b0ede0ea5c1520a212c12f69d68911af7eafd1c1ee02b1ac018a75bd1d24b49528cb0a62d2c9b018a43e936121f511128fbdc4bbf495501d97a41d4e8f1e3411429611f28b051b704d1f5b592214d07431f8a390333f50cffd2d2ad75b353cc9967d1fe897daf4b81a5c208d4cd23331e11f9f3d22375753fe007957e2e726efab7d4710b11192197b6d81ddcfba93dd37481abbbb699f19126457b97cd3a895cb4a7b5eea80cc85788cb4adc373fc96a4e89386538e1322b736853e01dea47a40700054918c6ebcc26e4b9f42ff2c9a8420760e13eedf40d44b359f9ad31d975e9de40a77a898456b35021a78e716a0d5c47804c4e67e04725131d5a24902c8cc3671e348c3cd4d577063650064f67a0e2128713b0219906795a5b00f23704f9f4c192e81f4ee84ba98a5aef1a7568f0d026fbc87699a9b6c2f9c970a5a8fe4cbbd3a2162f7d7997ab6d84c38072c2f1ae242492eb18da3500f990660f22dbe75d96ce0039aa8bc3e3a27617b778cfc543c87c93150892fc45dfd804b182e3790274fb74340cc02422e80df8cdfb22f55468877a71906f1661eb441b25531092c8655218bddd84cb870149acf42fe334ddc23d225acb26a344b860d7dd822b1ef85da727955bc98e2525873e122e84a30c1528b5f26f9f604c174038260c5eef7b8aca6fc0c724a887174e4e62df56989dda8716e9bf669edf0438ac719e445d713e1f149782529a5fd74bd9d6257e6602818fce7c0238a3f2caa536f2be3c1adf47aa4b19683e5c0b2d908cccd0575dfb0262a2e836d59881b3f8b920ba2292fcd0e2b6bc30ed3c931b32598681031deac862e57337168b32f1e60baa635aa28bcb9a7267d4fc1b6ceebdb0bb518cc6a9f08391b71c615724003b38e34ac00567dec87989a363a7dcdfd141da90146a6567f4d66aeb734f220f8f436900e06d434c6589cdb8b0492732b3b1a0c54958192dec017bd60c6b0300e73d65da8d6674da08e6a3bc827844d030340cd513949c25019376079604187772bc8b591e2b71e759d0db75feb434120319bb6fa7e0804c681e45fa421782e25d0a5d12cf3da8e5ee18d6ab580e6b54cad90f2a74f41752d846242b45faefc26829e66bef685ed2b4caadc6a70f54e70133ce2de0de02486fcdfd5b8f15b5998f1a16649632f50297605c28c2218352f7fe010852acbadf2c52a9331046380854444a760898a63e2d4cd328ec5c587f05673580e916c6ce9d22ce0b15e4e1150c643cf89372a151881e4d8c114d15accb0402dbb611c54a308c03526cff0480ec1e627d161001603c73d4f45be54afc8a655e5a205eae51c31dc3ef00065a84a6f975618612918f742b89e27b15d4214092f7d18974c6314670ab95fe8461006ed3240e9fe1554113acdaa39620dec7140f44c906484b8c4a052842a3546b3ab7cb687e553693bdac3ad77604a00a00b54e61d85017804845275dc6e185170d235ed9767779808d8c8c96bf276ae4db93af17f9df0802e6ac957eb38201f56505fc068faec8e62563f0eda1f16451daf6c40050724415ffa0eb0703736da5c237199adfcdbb0b7bcf159e952324be486538843df1e0dd053ad1f36ec919f969e1681d70ea9f7bc774f5e6dca71d00a6ea86b0412b96a93d76e276de50d800927f4f9443855f01ac623602ff30ae7f43abb458fcdca82ba51916cbbc72f4b4a9231c860bb5df3ada5abb1f3318bda4b59c34759f994f4d92c98b903389b1635c3ddc56a7825add6fad81e8b4b344952530d40d621cd8ba3e7e12d396952fe6ad4bd85fadb3b9d87ef8f653ae3e18bd8152dac204a904a773a32b7025f42368be69186cf66b503a3d1e8c3d8895613b0b63de0ecdc8e3483970652ef2bb84f3b45ac1a3227838540ab3121731536b6faa2f17b2fc9f1c2f89815ffdc591fc58f4a526362ec9bdf086b4fa83a6106daea53877e100dedaa8082ef31ddb122c1970564d658d84a1e468589aeab5037f09441d0759f86a9e92cbf2eeddfcf8b58c92e3b56a1ba575838eb0cd5a7de3bdd510a73ae7b2118ea701ddd025db0c553c760071cbc51972233b82b74750915d9d2b13c779c163961be99ff2c15ac7e8a320cb82152afe6759757b8d68c9b129336398ce8988bef6fc90267f41cf6bf1f9253d422b0300f211a3d1ba150830d9116ed3adbd5f976116676f1ff5e966a54d2c674868250db97edca84481388da36f0cf1cee8fc8f20b30c12eba19398dee9cb4bbf67412121a0428b55ee9ca3410163a9fba51e427d91d3964e36757d8450dfc52788048b695f808b3fc122307eacb6f1540ffe62f3a274c96413b56f7f0408aba9eb747e0e00f34860f29e0955615a1a28551703b5e104bd30d6a32f064c6c152a9c379c40f1d4e56dedd99088bcf1761db2c957d51533459c7eff3ee710ef01c083ca18b82d3aaaf6c364f74155bf70c440a34203acba2129df76d9919f817bd25d6d336e456dbef751e0053d98216e1071ac7936114aca6617e61eec22ee417f28b255aa848e0455ff15a5c40455e9468e48927e90a7dd80721582e7f7bf44107eedaf05480b0054dc28b068366816b4e36ac586e7df6c1c621bff7314878ffe5537eeae24073573ce6272a0403ecb6edf5196c20e1671f66568a2214c6161795aa9a4bf9a2addf8bf40ac606ae930c24339c7f90d33a7249ca68e2f4d74083f7e7741a03c69f6cc47936376b7a0b8039c4ea077cc68cf8db65242d45dbdec1265a7187561332add0fb6b42abf3ebf487a191edffeefd94d4396cb2a567a2f064692b7dc3e7804667b1f0c4dcd07a199e45a3ed219cbc1caa348d00904e11591bebec5371f62fedff8635d3de2704c87ebd20608d77980b95d4d39a5a72ae237a3be52a9284f65cbc2c8f9259a152925d6f8d2c5a0a5069651b765579169e6dbfb17a3139b3ab6612d4b06ecd9e80f5249fe4e1f9c5bb96e4fac9ec4b5628c01caa042a99952c758b4653ccbc34c36c1fdc8617a4758e76ee886a45acef069199ac747656d6015281f3f28709d28b5730640acb28f29511b7c04f5cf427fb757043b61847b7c494c39a099e516e0f35531d18b8f9e65aa385a55242851fc1f85bdc8df8e71bbcfb2b83d0c195c515d6e77577a7d02adb321164a6b74661244764d22a98b536e964e186f494490ee8932b466115c1061c85162989185d16556b24aae55700aa5bce36c777377f68db0ab361decdc4434f01a15f6902d76f3f6ba9e82a37b800970f04edad77095d508194e2193fe93ebaf6d8f60866bdcd1c84bc6e4b1beb7f39868e395d1d0296f7a9b634e1002fcd048b60966f221a9e26bcaf9a382bd25665cdb2aa341500717b74cda9da8939ccedd12ede2b9d5ca84fa025e5512862aff4008e17840722e32a47a6d30a1672649358861f04c4ea793ad4ebc02bb47cafc84c7485cd41e0d4cdf546cabdac0f7e75331d74797c5326d1f4ba6725bfa71f6f95466cf740feb2c0ce77efde13c7592deedd1313ff1284f1d733ee06a07bf74cd2699c2e960bfcaa3dab4d77bcb97440eb5e4cedc3b2c759d1bb27e591672af9b358f9ee69929d32a7d296f78cbfdfe5e2faad80a35def6d366468b120ef75a9caa4cba99f3954b1a76ff81e5087dc73d20fa815cbd62e7c18eede17d03da5f8f523d43ae460de5c9758a2b7941da45f0e8bae214d7354efe50d857bcf3e18f23dddcfc1bee7229f7b5160e1cc20df5028517548991b2e776263f188132333a41b33ae5a5915d0f1346c052d6ad205766714251ae64a614f889a9bf5be6cf20aa2bd0d04ee321f28f955a2a4efea1c488750acc002e51d20ca3f7e39b59d016fdb59ca706b9b0b47cb5995e06f350f7e2b9cb3c26b394f019c2de62b84af9dbb195628467c1a5c5a999ec43d5928077df4724614d0e5d607e1aa222f0690cac17ffb96dcde5805b6c6cdb78c61db6e48fcd6c7841ab6784e5e47bee2374160ffb7ffe47a7dfc952807c4d90787e524e479226a99a5582d2e49d5e8fd59c0286dfa3390f430d00787066305b177ee83c3123c5eeb99ccd5bee1f19ee8839348c2e9db73d507072ebf8c4fc04ebba907d407c7bce7da93f726277eb1ab7de7894edcd415ac97ad81ae53907e729bfc07203c2a5f739139777f507ef1a993f2327bf9f623ffc495a8aa3688286fbdbda4586ebd2e435de18ddc5ab107f8c5c4d7db8f081d437a3af7e03caeed490b0babfe21d384fd837a018f66b9259becec469fd22f7811abe506721f23d839b8dd7e72fbfa71af212a2b441ce5369df762b002760e99fba08ffb2b508e4e14a6bac1713ff9b12221c713f9fa0ceab2daffbef1171a0cd9e01e9dfc17bc694cef11f12247d6136cb44ae6c6f8b2c651a92d97ce34907d2f039cf9b5216eae014a0af420aa9b631cec923dc8001cd5d77f312ebe8ac5f48f5ef6d7c91fbed60e2c9f868807b07405ec6e5b1898cf6159ccb7d9092bff991587f1b2e6bfcfcea3f8a8be2432dff4c63e69b818d5a366c3d73a4b61831462db3594b2087bf714d4728d2aa39dbc72eae5cc17af9148abb2b1946f2e6dfdf8e23eca744a36cbf9e736c02739b661a9a2cb3457ea5e420141b464cf8ed61ea3a646db1dbd1ef8ef9d92f053fa5b473d2cab0a079a65d1aa8a04f8d8668dbe4ff345e115e15beaa9ef331394aa62bb000d11e40622ba413644d84a2fa3ec0495fa9902daf5753fc1b6537a431e86b1571b9ef26e2fe6e49d3415c71c3947e8c0d536307cabfd94c62ae77370b79a0517bdef84fcf70d0778d02175f21c2d67c19130322e6f37aa45698899eb0cf8bb7f836a250de8aa65fae26a2e4f7a13a15aeb3e0dff68b08965dadc00cb5d9236eb55eac077c805c1c501b5d9b8d734b2191a30733772e54280929a0933a4183d79000a11dff6e72b32a8839ad53bd9dd18fe515a37eb4bff474bd50435eb48c00e8fd973c56004e854bb147beeace32fa481eaa67593ef5ee79dce69356e464061772415df9d5b7edd971a32298eb2ebe3bc6820ac8d0ad16a942b866166464356a3ede008a07aca6e2cb9b29b57836aaea25fd4c1bf14e3013162828bbb3ddb8972624a3000de8a4ecf5d82f6f5cc72d7473324ba06b6627ce50833786e0fa0bc524b90c98f67ab5989219d16caa40c50bb357d94e168182d057cbced82f6cdd796aa02648f2c8a08c384253c1eb1db897d631e96119c228950704e6522a40625f5b45de93f5209942dd80d313e62ca67a9895157a778e681b9fc3c2c6a6ae0ec4741958bd2f9ed84d2842a1bb3f7c3f3d08d09acbb2ce282068ccb38638c33356783de64dd3400fd85176ce0b700fd7ce5eaf2b9ae69cfab369c022cc101a64612c405ca2cb8c1350e83b699901d7d4abb81dd03507bb6a94b92a046c2682148209a752276e6df74bb9f68c5ea033d7166977361bf1155e48ce20664dfcbe882be75bc48d9d2871e6d9331414eeba569a6f53ec1d66095e0cf606b457206971cf87ce75e3a416050532ff8c69c42c47b1ac1c46b835e656b83929a86ef314cf8243ac6ffafeb250416fc85556a22231c28b1216820399d69df923e1f37629041fecc16b46f44491c5f579a67e91d8c29f9bdb0beaaf7f703f5600e95e2b10b664ae44060f3e83c9674ff54024f16c06be9c10fbfa2ca9db07b61b52fdba4b44798915713631bc0465b6f1a0f0497d2fa9709b081e5a9841df20b20f2ac61168f4d4d9df3f6ff628c9abb384d1efd8b6c7f526452e8c3164efce8f5f35a2edcbbea08a58cbdecef53bf63e6e0f49dfd092b8c41e1c824b8b9166dc77cb9de3a0f8f531d2ecb9f6a703a0e25cbe18401df3fa9391bf35f9cab54603fadb2b798caae899aa280855b813f594f6ac8a88420e15672bdfb16028512d284272272b5c1e0a419aa343c8584ff9596533645c279d26d9863ed4256d7468e3811c4770ae6cbeea8d7c36b8b6fa345378e8f774e3e455895870687662758b4654a94e2a8a3d83a19143e29ec060504dac9f98b9561ead7354ac3a217cf3f711b837d72f325b4a962cba875c21bd8cba019b90f68c80543356113d95bc285d8327f97c3db0e36d2aa256cc74fc65355f8dda843f47bad2ea15b1fe16d11563b51d758eda6c7e49d3d4934638543e50b0d5d5429dd97b9cec122f6a52b2f841e57306ca9870d37cdd2d810d596da796c2fa977bb93b4681cb8bf7d4dc1e88b722d01ff2c3e038d8b7a880a8ce8a1a846c06b6ae883e78804eefafc655b9a0ffe4178f02ffd1ecd6f80f5130a953b71524a078e4b6bfb53ac7559dbcf349d3a0d28c99f0d1a88596e2ff47ca8076feb8f0cb45df9660469c2252e7832470ca55b88e35ab5a97c0066de12d40312e762771921b777c08d553165a217414d1c151dab423c3dbda4187df1c6fa5de6b1775a773851a7109e8e3e3bbd8dda938204c464a01c2cf8f8939b2902100ed76012268611d9ce38916ff3f08220872d724ed982e777e9f75207bbb03d0ec6cd207400f0eeec06d0f789b3e22b813c656016a840f3dc363722c31dd4eda21732b4c3ac451ae0bb9a63d3aafe84792862fee6f3e235117ea0ad77e4544983bb1a21121e8345e5efdd82540cde150523eb4ac1d451c6c3945aa25bdc561c52c402fb63db293aea638fb9fa0ddc08e09d5b50582bedfe16dbf5c58747bbf4963071d8152aaaf52852bf3dbab480e05b2833d23ebf611a49d28638403fc97b6211074ae1557645afe37a4a0fac87bd5d0dbe87bd1d6c779116a4fdc9f118a4e188dce2860ea6b160afb4b94da67902be2e709fe3943687f0ec03cc37a5cde788261092894b9b9b759dd30a6906ad14f6a4e9aaf5697206ee554f5c078e24dcb17512c286b36139a5f7a0e042030f62ed5ce357afaaf32661d9fab213e89750391406399d29719891e706cec5364472dfe0be17e496861d5d82f64e87c51ae3638718db2be4b3410025893dfa8951af32ce69b209cb6a44e9da58da3f18d96179d3e911818373823a5878bcde5b2e43a5c23758e116d7922156bd28608bf7aaeb5af04f313628cd5d037b64f8853813d11a47d951eecead874ad069567f74331ca534f6825f2af4795d138d6661a591e9d1383703cd9499b61d5dcc458eda8efdb8d04905a0b5b6dc0323af56215a39fca0789b8e1e5fe524772f58a10aa5474651076b4e8a778fbf604a5f75ec4567c23e432a4a5d8e008e9809818dcc2e3b4f1049829804292b9668075923f5ae05901a0009ee9ccca70673593ca5cdb498bb8404e2271a41405f7970df8f335af224c4738b007f45a39db96e3590772877973935295990dfee2f107abf5edb2dcaf74a264e505f322676ba926d649441e1ae7940a77d8adc2a667cf1cda6e40f12031e242501de98923a5ed799c96cbe916f27561a110d3b9f475fb8242758416c23713c32a9ec467805090b607c94e808b2ecb0b09a6035f017f8a8fd590337dea2787093aad407f320d4f577b9ba90ff7e7daa1f8132b29b9cd66c1767c70c1f6dea1784322ce87c682f79383b1490b0a5a05f042b1b3294b0d3df0149e84bcb81066376149b08423ca609fd193cac0f1b3a428dc9698d99ddf8916369064166b4843b50b2490eb0ecbc58a920b026d0da013db1145856bd82bcbec5deb71ced291d4d1a82b0b605d2810ab72b912658fbca493ccd8d419e5bdc1d3fba0465e5e120a517137a27e0717cc0ae4951a4d2930113a2fc89c266e918c49a80514fea0a133ca15edfd1a50a872d97c88bec8d113d51531b8fa4036058c6243e1569a63b420cefc20ace3d135f47072ce5cd347003c3f64e645efa0bf1f5fe9fee02cf5b1d02109067b436d4ec3403e6536dc972b11d1df358fd7da1a56a22426e02e925874c73ccc9a07e0b08bf2fb4a98b72aa3ccd64747c34ce51d564b557a19e6c5432802da7de9eda11c4da41cc22618f00871a45b8c539116690066511cfca430bde32e17d5fe0761430989e831d262217369146e01927aa70de6b8c1d7ec90082a3869f97ddf2b44f3b14758acd49b8d8343e1c0c209a5ddaa3b68689dc622d1e5b307660d262ca5c9703fd96281dcfd01b93a665bf8435f5818413bb05e5bebe63286ce6257493c72e4414c4d773d80f4b4e14a0e8ca73e7b0e3015fc8e79d1e8658e2bb44d1e5cedbada35a5acfd13f2168fe93e85fcdc4bdf479652c6c508ee8171e613f26b7f6897a27d03dc96792490647aa2e05416bac026361bb452e361c2eb91e1d02729295af66ae0dbabe2023115a2b43c28f42b5baa4ad339f649371ee1d0e3659d7d07bf9a2b32349895a0bc6095aa6b018d3e4dbb7944e0a7d56a200d447a296bd40b457dccd64bd667259f6a13bf95ba988f06b7d25f749e4721c25c59d6684b8f0830879f737a7a127ba05f80a6ff40be32a0c4496274ad002c019b71d3eef65e0f5daa9e5c6551dc4dabb827dceca2611917100f531ff3c61d380bdf4d6f5a507262b984d9bfeb8233c1d9b52e854852026af27908982b29c689c0e707c22334222e51d5cd267536ea05b8b79adca4e4336d8a8b6034f28ccd08d6f9c23f636e507f2247fdfa469029e87def2b4885f2614c40d0e1914553290d39119a95be983f4e10e9191f96df5dae759ec9efe0d08cadee6061c77d28d07005183bd53612a1f6ff6048e29678c872f7b92235664be04dff9932012fece36c713d9a40d4a531ac6303d9f22e59634882d7f92dc9cd61dcb7769a92aa7c77695a8e67407e6c3c24b19d791b538743c049785561d4f7bd72bb2554209cd8c972cd398718e7b98025dcaaccd5b17fa4519740c8969d7e203b1fae13e564d0f8622e150951129a40e4ac311c2a86f7abe212ca14725d3cab61efc60a77290bb45f4e6d9256097d4271c54597f9f5ce94fbb1e166a8bbb84a4dfc774b7221e81e30aac467186a10c3ed237f8256a3a5d0a67b6287e2924a0af97811abca44aa1c02c43016d6646f3d203b856884b4d255245494baf752b12699f9fe350ae7985c79096e8bef7360d5587822b3c4a46994f37c1958078960f1c941fe29a41e0a016208379b067d52601e9eb3f3a19fec562fe23c586f95ac0411df1ce484bc75f678db196ccbf555f5b2278fe926d73cf9412baa69d052d78497aab85150181a747eb25b41d7ec0561cd103a969d5daf1106bc4237b4e233530f0c6c3e695f80ed902e0b59f5fce3b918eb0b0fe280f5d34973ce0a238a45ea5ae5137e8f687cf9648db5f64bf44e7a63a51529048a12db3ab5c4dc465253f5c1169a041fe85f40fdd1882ab5a046260a0a27c48e743807182451d3a87a18cd5c513fbc12f75b3112a6cecad385d150274186771cf821a101a3cd4c09d567f13c46dd14f662d39821495feaebdd1010408022d6d162c00828283182602c73c0778419c91f74c22a0c68486507cd7506721a000ffb4a11438be00403552a442002773b9c050b6abcb7e01781823e64bcaf714fe5df5daaceae250571c1246b99a87e6086e6ed9050057c6660d8edee33f2a31e5128abb35672454cf3f2bffd980b04f2978d05598aece32b69d65eb7023958ddceb6c821b8c99d079fabb3edc645c23b31305be6ca150ffc9332ac174ec8c3057fdcf73a5dd23971244e42c073e6d9cb298aa4b2dbf34d22049a1b29703f32c79d13b50936b31cdda0e052c8ff3a2cf898f07d58129bb32805ceb37a75835c55f8545cc2bdf13a6f88333da5bf7bd6ba0a52e5cc0c32e1c0ce8f11306692d7a59680504934ef3854ce1120025de9c401cc4304c8e5e29606c60a42a20a1f85a5542c690c6ab51e39d21f160bc71ab15462f422a0ec6e262e10313c285c2780b0f54f1c6298da5a776523b6121dfc2b3e0f9626b18859ba5ef95a14013cb2da1947c9db30b051872c62eab7d5b039f8032599d28acb2f213bc23a8334c2a963217d45ee2e8deeb7e959f402bf5ae3a5732aa6b7b570f2fb9f1317abcd0ba37d0a6ff0183875f29363d95d450ac331aa05fd125cf7fc4f084d8775b3c812a8e3160fd3d38abec5f04bb5704e4c11dadd24d5c0a6fcc3bd29007bb47e001c001606c55d68724ac672236bf3613e8803a177cfc944351159fe9914da1448bc18e6fb0621ef9400c1be8aa080868af591787cf5f7c753994f95ff70908ec6224510243c43a830ff6687bf9159833862b14ecb700e6ee203b66beea01bd4d0dbe1750f4534ab38427498c5b42c463484e4db323c6fcf0daec361aa03a49e78ba8a11a95a5d827aedad8c1cfd346f86abb2cf306a156f3a80ca1282aa0d7ba1b3506e0832a051343f320fdc42a9d392846e02ac556502a76cae7a112e57c0bde105fcca20fc3f355ef7ba77e3b68b650a4c293df573ca509cf4a012631a5cc6c0598950fa2085aefb197574119d4e58633e077b31f00058c33819c6b18971d40065dfcf0233b750805679381ca69f2dfa2961499e8f13615f65033508bbf4e910c0660fb5c797e5b4261f5278dffb09590c1dcf3b4d06937195aa3c9e7af5aa0037aa84c098958ec0dcb4c6ef784ecd6d3a9802c96b05fd3fb2a39bc970514b314779818c864b38d4a7ab1fc6384f0cba9fcd9004aec79b8e9f9d8d2b032ca4e10f3c42b062789dff41faf5f2cbae503d8f57cdb12c23f582a61b1843b8a0d10b037fde892e4754b6dd9b9c750d16a62d2b823bf0090030ff58d8e1d3ecf045522e1985daff59234c6201489e46bb1f6011f62adeb11800f9a59a7f657722012b9c937f722445922b0685fccd4cce19fbc5c9fd1c9e5a1dbde1f5aa73d471b64685f0c521ab3e48bf9fb15bfd8a091e36dd278c1eedba2bd97454009de8fc9740371a84a6c6e32f818f6bcb242d87ed0e72d2c9f49fd1910e2937db22f9cf5c3e9303f03a338b348754feb3c301d08ba4d632efd7e8016f5c57d387a2a295ce5025e2b6647c63035767eed954a1950142c849a68cac298f7e49867cc1effed68560ea151d538988bd0582b72ecdeae4ce4e07aa25a5ca2edee3a5ecdc2845d79d85328abdf358db8a65e0bcace526732aa93d9f9939ea885d1340ae356b63327d0e164745eec279bb92c861a7672479333fd9823f083dd885096e1220aafd771c2078b6d83aba4236ad8a533a779d307cb21ff44766ed2a5209616c8c80d9b37a8113074be6f57a9b2f79b9111bc87d32ffbda134f306500c64425f2e10d965fb00b8befb07af39a61b061b0dc64a930d8dc9f101f2829c4eac5c8a5d7f398ef098a1b8ab35f935cb8d2571a09169b38a24384047e7729065bc6563fe15c39315853e0738efa0f5d55261baa63b6f73b02fc172cec3c9dac6862afc2fd4c99063d607ee5c4760b968410692f10eee136205b078782790fa52a583c85cc18f818d136fc26d265bf47add667f94f7c73d246a0c98af98568adfb25d881a4ab2a79e00a5c7fef013730b6860e6c1699cfa318294909167486281b29091688754e95d3f7439dc75fa69620c914132873d311c521c04aafb6eb79ae7b53c6385aa37cabfc2e94a2977400a7c700283e6864d5da75275027a234c304ab81c270307465f12bf08bcf505a8fca2e32b8a8fb2fc11d93cb284ecee18137b55ea0f0a78feed30c1a1d39141bf24541c131287c929de17abc1fd53a022bc955a7342d9e0db00865ac9ff9bf52652c768645445ca768ae3c6e120a2f9c36ccac18f7f369578964539cac3e8b3d2c8626174e93ed05d50fe0f8794938b5079be15c81804e766b83c60cfdd1b0f6549c4a0345f926cd8cca7967689971517f03041c5d004ebcbc279a6e88e1786d04adeb5aafd337d03be4ebc5e1bb35324f5d6622e8db73c4c6533193c0a93bb8c072f5419989fe8581e6aea3108d60b0bbda1eca17d99552e78ec7fbda1d3d9fe2ee9564f15a7b7d35e450ea2f1241c44c0255e74fa2b4d6c1c0bd30a64464c5669ef1cdb96a5a7af3c2f8d887ab61ef0e1e6d06c2e411190149cf7e16acc516ab0b0448beafb7d9b5e31592b4dc049f03cb719ba0bbe3a2f9b0885ccedac18cefbe978879ec83670564bf7c7c9e8a2c3c80e9cc09fc64945663b0154c02c93612a724f449b4142f908bbf8eb0476749267b714e3a0cc304d281cb16507fea1c9112757e5ac1f6e6a55d3185b5004bd453a9c4c2865a38358e5c4a2b838c1d145692891ed9529fb41e63e1517e1ff20b3504beeb0b6a7eeaf58eb77ed2c2c900d860f2dc07fa84111fad3c43f78e315900eab159c5e460096d181e8e08ac05746ee0d3694b26736b14f070721029a0e11de1e9fa7b58a60c08dd72f8841f374c29efd679232fce16baeaf32deea991f8e97927c753ef046b1cbb713e2447aec3a1993a136d8221f7f6a098be46ee11e6dfa4ea7804b3e74ee9c2c000958c60596255c88554db18e56e3a1d855fed4a02bf9ce475eeefd0c5da873b63e5887a5828da2866f62491ef076760a13d2da26fc45a12fae22e076e34465106d609cfc6a22f3a52aeb68ccd8604719361c7158319f820079448b4f434baddd48e6c005e35730d40f45b7a840e206102f346e6715241ee633a3aef67aa9367bd1e25fbdb5508b28554b5c8fb1337cfa4303b9da854092dc3039e0bc5661ec800c79a7e20d6f46a226d48216d277bd2fc11fef0458ccc4d17305ff9573aa753968afe2e4eb637ab045392c5c99ee3523ca9a479346f618606865aa9c7f4103c36b7882a19f0b40930ad4d600560f30c5afb8c658a5651841fc80d74ca1e013e147e1c86f14e3f80eff900d6c2be97a207e1c709bcbdd9325dade7c4b8c34b84ba993243498671b60da82c8a2952a0a8a14d1117a1f97f0aa25c83a8440843239f703e7425f225d22efdac536cf442d146f16d35a0ad98c66039caa117a82d297b63c055dde0a698a2b04ed0d22e55a79a94035fa8d22b6b4ba9993480452271cbe70409b0a124b8798a7082970e39a60988345e83a7ff664c87721984641cf95639a451010b0e210c360424b4a076c77e38b50026efbba3d88de590576d5d89ac40080027fe8e6589042fab6073dad31316a4a6d450a1bdefed5c425b62f4dab7dbadc2ad051c1e5270cca276bcf1612f35f16d611d5bbbf3bd51af3be5025b0aa4410c4e600c813098bdb0a792d56717ac676b4945a1cab672859a5fc0d2a526c74f113089f1ba2b6de56dac3899d110b53f7409dd1428b1b6e644a76ed331bf64f814bbc9de6f1376d8704a25276801c2303fc953ee604e3076c0bcc7531634f70b3f84cf1d02c6030a67a82808c44a7db5a0cb3e5a1941420ad0b53f4d815c0e64c2a0bae0ad17c27751d0a70d94790cb7b0c68ee7040956c26d299b9ede48254e8e20fca62dd0077913e9abdd76934680cb30bb61421b8f302f9ba2ec4ff418df6006b38c2a7a4eee08e463782c13e7c3595124d84e7cb42e825896a59e7574e3052535caad22ab0242aa562dcd209823c0a4ce6a979bd496506cbf5383d9b61807207d5f1ae161361bd7da9a7b0870faa6137d108888ab8a2b7446e63a2304e42866e949798ab23c6d60e0f3cb6d290a45b8f6ad1d7401872087bd4e192ba4f7b75fa834b84758f1c5002114c7e7a1a5786b11c15b1101ae9888b0362f445862eb5a0108311a0f44104243a9c24a883374d1ef7137e1e9b420b19ac0022f85458d340bd738c186c70ae37f0edba954a91b473c5a55c3159807b5966c0dcb5727d5083a987c77d2e6f3b2e80b6a7cfd92ea445ed645a24a4d71370840599dd4829ccbab279177c5bb58d25e271bd7f1b2b02ba9e0c04d867eedc3b67fc6cc93db304003a68c95a40591aae0f59d414674e4beadc212cdc9cd304445b0465bd6d1bf5a8dc8fa54e4e12cdc829601a9db7280edd29207ec4f8e3c5a0edfea84b6457134c1426811731fad6082f5f307637e254b983f11c186b28404071df26791fe9bf878caf26784ce8e945d448eda99b89638e4fb93efe6ffa0531210bf09ae6d4eb97838c8a487b928c70dee02002c0374e8046386abf4ad493443b78f955b09974d9ad24845ea30f0cdf72f94e070330d692feecb1ea926c6cb5f9325e90b725d29846039d2d8c5aa2c7bff864556d82c6dd1fbebd5e0a979baba1ef8f15c90a23d5f91351ce868db0b65bcb9ec5c4c86dd706d6e3c8d2cbf06de60e5de21bd6a6e3ba05f2d6ef8bf3acd1effac3950f9858c97caecf3d068ec80fe57d0e20c59ac80b9d54c502bb46e0e70f27b1a26cbea20683b489a35700dd31ba71dfeee510025b2683c98b03c9da6b6d72b7100b43303fd80bfe698995d3fd60a4c2933110c42c213388a3b4c17b4abe4deabcb702a0c81994530a165bb4e822c35f0b15d28d5d3f21a16a1028df146a90fd09ac671280ed371ad7f74cb2895ccce75e8a52e153e24f5013972c5f92e6bd5aa425a71308fb037107c26b4a06ca4635b9c3fdb3b88ab43a3b18cda0437badc616832a7d4706c7bbe34503a28d47ead4ad40d8f6fdb04fc12bd38fdf1cc35778e2ea65d5491b4ab7d9e1a212a48cd80b9b2ffd842170672e49d0ea26eb517aaa6a6610dbaf24ef2e8cfd425a95d41491913e149ce36a9d2b47d9ad6462cdae58d8c7f288a2165293421f317ba0b005e0bc2a3d141bb289784f2e8219c85d99a39e6798347db1e64999b2e2438e6c5e37673e57663cb5b65bd56bd02c6432cf562671312c45b680488d334b99f6790e57c89973e4543dfda00ed1dfd30624e267c3413c6f9fd618ff877563649c095f8beb7ec4313936743cff8a46a2afa89f9d4c585f61688ab6d4fe23fefc7d9d6c3fc12fdbd8a0cb2f68ea8738b107e85bfe686d455bea12c082dce63a011757460ee82980ea719babf0e133e8cbdb71e0e5dab4f35eb4ba4ece403f6bae46774039c8c66d185fa0798d2fce8a2a3b2ceb883219b7f13991456c3f5b9098b412c055712a7ae7cf460cb8fadaf9c9c763475473d624a885c4ff19cde6eb07ccdaf06944f06e64e23a712b9f10da251dda677359abfd814756aa24c206306311aa049d14ebdc35142b5735f3813421a7c0af865282e2b56f13a999b9994293ca6b3804fcd5ec5b4bc17f16597fa772f5312e2e7ad6e5182a28c29a2e9ad02ad6d016c2e188f105ee73261851b992ff48aef92a479e3f1f368228337eada3b42d697080a68a2ded45f515ccef1273fcd66f8fecd30ac4043402315d6d761e22bf49deb50894d461862914b1202dc8926eb11c607ef2296a7fbb1f788b57a511d829740bfb0e7523fee1205c4a11c646a2f21f06cce5c92da89d0802421c919fb6655d42e44380167a8744c212880b69992e769185f91e48cf274a76a490351f64104ebb07c078f388ead61e8ca46d7cc47d5e72b43c1e2868a8fbd8e7625fd45d832f96dfc0c2aebfdc0552610a684ce626a6b617ec6464af23163c2799f1cd0699c0b65395d6be22d225a1072612a5dbfe75be1b65e03804aef9d4cf0aac01e74bd9c2a80b608edcbb09b4be8acd2967a02dda0af57d885ca3f28228b5ce638ae7d6eabcd7251d69d0b62dc820bc3261eb35526017e4d61603239b3bb68975d00ff49301c3265e6b1cdb20afc6d065780d6aa4d958a144aee0321529e9233c33cbd7a51ba0437b7fecc12ef09d438401c06f7fb79aa670c204a04466187b00b182c0f86986390464ff41a2fae9a41ace972028c73389809dfbd1c3b29b966171513ff35980cc48dc6c19fc7a3cc0582eec09edcefe36d3c7be49311f3204c9206890384b44fc426e36c17791328f6186404b4d049d972887b23234c007e29235b29f379e176785e73271a550b19a5adee039e8a311afa2563b003750aa752dd1a9ae5df7a82ac10100f6dab9bdb08f073638b564281b8ad2308b4ab2d10319d26618a47fd6c5723af328215263119076efcd68d804ca8b1a0f3e4a0820d40622ef99a4b305137cbf5abc498f602114e726f6a560bcb25addd9f082464177b044926c8c56310a133da36047033d7e9ed41dc978c74a8a3860a0e426cc852f97325fe7268c7e6463fc02986da1ea96beb65e0950f0ec535471df9b642e9b6445b4d9269bfee80d036524458b5ef135b2b8e2eb755c0aa7421aee2fbc2320762aabb3ff3707a07eafa2fa663f1420826fbd1b52c479b2d541f3fd2abddc616e68aeee5563aa3365b984f363a097c1fbeaa65cb920d7d7ea9914aedd33635a3b62bcca7363d297a1fbaaab296f447cede6380aa70a3175c2ea2d845aa9db2bdc6a9132e21de3b0f98c24a9633390bd7a77c41b213b147dbd23ac091d4205ca098350a938f49633abc9660dc942fffc0d04d8db11a56a936f643830af6fb1e1954a94f58b93406ec5496f490c2605f35d15a2ea1abbf7d56008054ce9399c4fe74790ab17ad7ba9b0fa2a2de4418a7034e09fef2cd0fbc5ef2ef91431de1ab1170d7269b7c5375c81b8326997909fb43c6ecab3b091d41534c59cfedfc093dfe3d076b6dd99f7e8b1f90790746c79a2a2b1dcbe49d2a742309e90fe323a4ca6421217c04c9123b2ce49ff8b5f70749f87c1f5e4263deee8f5d30019b5ea5549ab2f231db94c326b237e857d804e12d0285c9e1e197c8c0807e412cc001d321fbf7d578de41c6af7e4a7916fb1942c88af6b83e0abf837847c0a2cb58ba1acb1d2916c289d38bdb6ec784eafd7808cd193c73bf9aef4799d497df0df067986b86db22af52b9b88a2b9ba16ba95cf7644010f45c7a2d74108995812bfc6a6842ed61b9830830a7ba3541acb85908b9fd3b482aef2a7f9dd581c5c6fd75592b72441cc170feeb68dbb2e5df8d9e89be7602c707ec8303a30a41c3235c3feccbfd227f329cbdcf83ea9c0c79ce5a6e9c7901858a070136b624082f3d2280a147ba4ee700bae89a646b6eace5f4a02be35747fa768a7c70567c52f58896a9107f8358c1055c95fbfe467de20e8e9840234d657b200fe61bc765c835a8ec432ba000c6b83a26b2c13508f95ed7f075c98594aeb7111c3afbd10d94d5bc42050d833be679a555a0df68e22cc66881fceb5c57a6dd77647a859bdd1b2bfc96af5f8448ad57bfb66cef485fc42864ad20996a645a43943b43e2f2b02ab674c2bfda6dfdaac53f586ab603d281db5638a639c1fb1b959b01427e6513295d8f0824be6b35044bedcc3c76e1aafd48b0da301f1d51f63ac636d5c5fc72a52c401c1dd51d97d72e2f92537e91597455d3b81d38293201e2c714ff4ff286fa6b57744930903ebd9d8458e5b3b29767181cf645f2bf4231b4c1cf1317852edc72f5be0cb4fb1bb838d7c83db4b1aa3479bfd9e04d5c4a3fa8da7e1d594af479dfebb02f65ff5914515fa7165c477edfbb6e95251710bd2fb5d6b8185ec7e0c98f54f7a508eaf1d71afd7c8043eecbdf4b556c281c734a733f992b6e374cdfb72bf8628d6bb8d78a8b854ada90c3938bc01eb1bf25a8cdf68bc889705a70543cb5dc0aeba4395c78db536739f0c28f205115396c36b0f87ebaaaceb429cc060bd69c6a9aef00697c593a75acac4917d0c731d5483708c799d7ac13e86398d674651adc342e773d4ec79b4e9a66f3ea037a42e2015118ca029bce208c354f436710c635534347d0c630d7228f119f409859d3dcee098f2aa124fbc1993379ee930414ac28f2cd17445a7cf1a08530357f21e98f60f9cc1fdb7a378d9c285ff5865961138c7418f430d61b72d24d5030842f342a335f42a5985fb3ae2df1996f1c4e9b7aae1d77ffc333ddb7048249253ce03f02edfa829887855e9f41b96ded7dca9d1cdd434be8a9b2c940231fb369d5e332658bc81d9a67568ed550f45dbb0a7abb78dee19a1ab4955362dfd7237fedc65f88350894199045faf4ea1965b62cfe24cfee9e30fa2de999a024700fb6feb236e610ac6d49f16df069e8328c90c5bd8ef4811ae17b16b0a282756d8367dd87ddb91bb2d4a92bc2f91fc485ceb477814940c4d32e2db381ddf57037b14fcf3314938da9910851c37fde26ead8834559a2f5dcc0652ff457ac926c11ba349246969a879b6ad510bd925d3d81f65ff04d117f5988ab9d9f154ff3892c49db543a5db3c534b165872fdb23118b7f52164fefb5421057520d2ef67366055c1118b4147a41cd9383a4df76d166259f131d4f116bfe3a78b2d846e6bfcdcd30cf1014f149285ff1a3ecb85fe233ca1a17ed84740c0cb66f6ec022203d2779713be59b1dbf4863fac1ebd3b50603d14ef7e43cd06923cf779ba4ffaacf288c413f453f2b292b5f099e8524d66be3b03abd98eb86a4aa11920a2e2126f6ba0de83fd2414708a20e918fc6583b435240f0a32c03f3dbc00217500a9245c4d9e1e0af300b15840f71b97d693d41ec64795245745ff6bced76a949c471a7e1eb40739bf0214e3bbc3be31c0cab03e504b74ec0954efbc91018a1c96abd420069a3e331d942b381573a5c509ecc8fead043bca349234a2a362bd2945dea6096930c6ede8f2b0fbf80c305d63a18b9d6cf72cb6061ae54a7ad6585d9421955eb1825e716241e7ec348dd79b49cddb3606916386472335c209e79c338b9bb654f3ee4d3a0cf3a0e2e9a7eb4a2292b3c79396e702539db6bd5b6a9889a40479be26d2bfdbcee612d13ed879689598c6ec89d8ff130c78ac1a5f5f102deac226d43687d59514f5e8f8c5b57e313b5e8292a9b73c35c8c8ee6d9772571e8ad62222c246bb17fc8702c421e4a0260072b963d200cdd378ab48f3cdda6bb385a4e8952d3b8f4326fb406834d25ee975d5af9d0900d9e852620a505fc7c345b3b70f18aa862c110a8d8f957b23c11dd860ff2fd7ad1b7f7171497fb232e5122070a8eff5eeaf794c7848f2a52827be456c1e1285d0169aa29bd26ffc2550df9a4b5cee3184d9bc2ba30757954a370a16cf68f8915d5c84dbaef27625b8b40bd44896d2572e032b1fd20c120ebf6d64f2d01ecd6e99c056474a3c826c1ea06bfeae3527ed6546c64fb9a19b5e97d974fab6e1ee0dc00988827ad4e869b4dec128965657380f61d8e0b4c5eaa4b9e5d560819652295dd477b5ae381af7175bfc677602377ae61278e6923c84f6a671889541da13b1bd2185311934b2bf3db0558a53542fc0a1696af51b075542a6acb6f2a2b712bc0cb5fbfeb396925eae8a4dd5ab28cf434c9c0903add57461d45607700675466f146ea6822d5a766de397c5863e3ead1ee27cdb722c2163f1d907a475580a1318bd50bbbc828c2264593af8e8cd6d03ea7502b4de718d239de085a9b6b64981b6439d1625fadad6c029df905d06c20f9dc67a283155b772105797c61251d025c3665bb2fe6822179507fe370333ef29090d68f7d4474459f9a3d04de207df8554fb4c9558878924266ff23c3af64bcbfa609ba328f239374ebc5dc62db8b17595a29e2f167419de47275058da800bcacbe0b663eec0b3033987b115cbe847c7d51ea7437bd388339e28f18efee3ba71a2b754c63d4450ade51b97a668011476cecd15b216bd2ee62765ffdcaed6292ff4afb1ba5b0016b66d10e6ef755ae911dd4637365c4c7ccad5d950dc5dad877fb2203b789ff9fd6aa62fb4cfa91c9d1bbfe908c00fb6a61b65d6fb0312a5e333ce4ba9fb797017894fcbb66b9d204f0b8e295105223a272699087194b350c4a2035e1ca0eda232200890a06eaa895e77b74b9596d5fd7fb51450c1bf0f60ea712716b2b4ad95eccd702fe09d1676a3ed894fe72965f6a982a88dbb0f65468399c4268896bf8eca9055d0ded164b0126b2b0ad9fc901c0347e6e9652b07adeadb836aab6e6c55c6cdbdd09e8f84763b80f44bdc88d39a5208eabf195108fc93015f6132ecf3cc4d5ca8514db1704cd799df89e67f1d5816f8b188b26d7b7710a15fd4d5174a3dba1c126989a4305f2bc2e221cb818379235a834f49b4681af0a2a3e8d678eedad1d0be82917f2def4909ddacb13c1001ad893e761beba9a71ef2fe06b8b0243705355aa7bc77013c29888623e8475f5862b0d80cdc4b6ee2adf10000b7f0fc017c8383be2dfc9eb54c02d9c7e7549d5679e2f845e3a0c06b8bbb578e29ba71936ffdeec435a48aa92fdbdb600ef140de6779bcf60b36643ac6e3ab20c94b7acaa940dca54ead8f46b99cda4950e6b99b9f8db7c471ac07543718383f735009dab4f0726900ddebea00f35f1481987d84d9ad012b7900e146395baa71312629605c855963edee4fd69a02ab9c2f3dbc0daf101669c9307cdb6fd808863800b1209f39b97f53c1a436dc6d85d96f40940ebaae4189a4039e6e297324b5357a88280e1c533054c29c15d013ab7e44b34809a848c5698c3451068dc4b624797d6b3d01d5621c03ea9c12e44586b43381cc441db5dc7b0aa84c04a9b43d77b8d8896df1b0ac631a65beef15273fd2a85561bc9792fa16120dd03071b0b4369c4a455227b37f15ea5cf6c6983de3ed9fd2a7d07b242656d473fa9d0df156e08e5aaa4b5bb907b95566cd38ddaf02a4dea528f3d20e7e93c8a70bf283291812ab8a9e92bd6af73a082011203af74cc76d9f6e247aa0e7da07b05f1538aea64be351d1591f694ca4c556550b6ae062f37591d572aa926514e0577bf5f564aeda9278995c7bd97de9321f537111b03ffbc1203c729b1c526836e06a29bf53d56f87b426ff528306dc8fdc0ec84390b6af6fd6cf449eb26f3acdee531a1a8de36e742e23924b2157407bb2a2fa3906685a2f14e15a2d78d5ee72f5d149f992fda326e81035fccc11cd2b3e3adbf86e6bf6cfc599329cc0e57346960a456e3f8367b76d0b0ebc4527304003d823685f7f3166206c37b302c407bb1e8eb08202f7b5e6376a7b838fec3bb15491d687101f8a57d729da0fb208564d88f3395ea0cee1791852e4983e7b3dcef37764e6714b9288bb99c869d2a64331ff9bc3c6e33059279d737b58c5aafafd854df34327ccad102a717e51b6eba97b27720bd18e25e0a1eb0b3d31adbaefb8a795f6ad493346fb60984532e752293670f5004b9a090ec503d2412a56f06637257751cf3ad5aebb3430aa1da5218c56cf23f6abb49d0182b3540ab5339b35af789f4efabc811a252f6d4bebdaa04e7a17975bb90e36bc78722ef9ee4959da1e1d49b8b658c00760a0e2f7fcd043249fb72e7d182d88a49e4a14247a6d6362639ae0c9685a55db827030bcefbe131b3831e5f32972504aba099a943fd7deb36740fe1e2b27d454394b2da7e03e512d1417c517823fc007acdaef74d33be16d2dfe475653503147bca0aad8ee812da308e3c6dbd6e997183debd1e21bde1b8dcc134428fb9e5dad8051b40cdb63a757f7c3ccb19d7c7e776aa435820cdee0a59827210505667757e84a77bc129275a8da42c7a3b294f89d1d82fd798d93b4bbe9c97f39c8b97e10f25bcce0bd69cffcab4b920674c27a24ed0a6f18d6ccfa36607fb09fe89448e2484ff36df248ad222b05ffe589f67484fe25b0678ed57d2e0470a5ef3bae1db9dcd87c696a13d812ef4fa340896dd79b1106b72ccfefb8f49041a53be15b5ae20762816370c95e7a6899fcdda5ebe81241f45d1d39f7a22217834f08b36b3374dff9037fae74c156e90ff876323994b6b9352d25c70c07a5beca0d2570bcac5b79e50e596e8d82b1370e25e404ace2e22fcc48282a8b0223a45c8f8deeddc2286b4dc124a008479d1b898de0ff4ddf3b3100538748f52daf8317a6dde813a8bcd051cfb2bdb0b78b2c1f39b87f9b36504b5ae057ecc6684127450a99763087ff50112794c870250e86a0395f7ba740b8480971216349c50e7c80c9f47dc035862735e453c7cb7e140d555505dd5828191b4b36d6d2358dd82cc613e9308108386215dab5b0d12077800f7554813e06457c8b520e63e409855ef691c70cfdd916ba1738521eb909f7bb6e1f6cbc69011dc3308b0ebfc4fb0339b91e5b38c14499a3b2d820eb852961fdec4c23262477ae3b16026cec02fab813227d6ec581ae2be6c06bc850d8291660d15e97938a78049f7b7277cc0f7181ec6419a21d21d37def65fa939fd292746334ae56677b98fb09fb255cf76368bbafc3686186b039a92dec6dc359bfc897f8de657ad42a5c6287d0cfaf415b3ab75964c8032aa44853ea8d44076253d09e11c68eccaea4ef10ef04c15d6a0f714daeecec1767f85a8a72b9960f48d6fd396040c40b2a9a503dddc9c842b9945f743c99fa44556fa0638b20d77c8056df12fd5c63f60c1befa95baf51a5aa02ffca7d274062fec96fea9cdf0875ca858298190eb572ab5842dbab27f96e02e4d549a560831364920f91428cbb1affd91448ba995bdf6e62862c844e6230ab86cb5adeae5d1e86e7b09cab0ae9d8510ca49a595ad1ac511e9f43f6bfd2bf58d7bc0827cf12735d73764015ff24fc5e90d5fc82dfda7aae10fb880b5fca59409847833b648610ebdf4c7dd917079bbd1c7498fb39bc0b2773e1c94f21977122e6b37f271eae91dcb43167f306182e5e5463d4a7c949d04970f5d7e42d57e0eab2ef2624923e4d4d488273d349709a70692301c8d66624755a515ce0211911aa0e0dec8f975c0f191062879579612aca6c0f35652fba9a411e6f4d84717df24f80873b398f43a384a253457a5f40f6df3ee005272fa8c2018b39836e14f6150b6846e9c4901bc6f3d02d1eb66479aa3e90d85ee771c77534b43296195a56bdf8d6fe899da15d9b01dc3c31a24f923db4aadb69bbaf6e049acc49c4a6a7766c8ecd22a5faa73672c40e71ccaff8793cca8783c75506742f005a97d6e1610056242fbb895c83d4ccea5c167e794cbeb38e19be53e5247809ae694eb5c7030c249296f33aa9e2a83ea94d96725bc688258571826729a0c92858df9aa3a3727cbf6a13eec93f06167202cec933999cab1a9472cd87c189e31e69c466c023a2cfcdda6d171f1491c31f7ef96edc10ad83d91ef820b39b3d6f7601e3de7f23ff4a59c21d8a77c516f5ee2db5cfce11c77d7131095e6f7210b8dc1244aedda9f5951d22b11ee03f623bc6817537f98a99ce3ab26e6e804dc758c2becaeb553dc2964d56d873dd0350a04c63db01128958f0c785114ac805bedc331382e03afe70ee318c10ab8039aeb755c539476cd46e5531562b6f623034ce18d1ff57cf189271a208669534271fa100432a3441ae8b58999ac0eac80e9574e638ce3ec276bd07b674a6239906fb11929f5719b288f4eb965107fba2e271fd6dfd8bbd713864ed4e3fddccece1eba6f327cdbc8900de7bfca036eab72f9611f741fae55b312224dc27efd3df7a1c34e913924923b81601298889ac12d75ad5d9a6f19711570d7ea514a6d741fbd694a90e7205f2212950f6633008df9946970fc6eaffd594fbc4d9464e2c07ea72ca8d6e9ff93117fa1c092fe99f648cf84e968a70c3ea57d4f555e4dd21929e187e50f154e971b197fe7b3cc45650b8dc45b01bc9bae4bd6293a53319a8a9d157fb84df17b4ebde08add664793df9974c8b4faa065ad57b7009465c5cbb7ccb1c5734d0ca47542cde37c155b36813f5b325fe172bccf2917ed9c60ed8ef1b59ab92df45b2268fb742a48025db8b23961a517054ec06db379729f4ba2b9f186199e2bdec3fcd965202d3bbd7d4985fde05b8c3824dd2e5a912bb03f0cc1841788566db26776f7faff43d17111e1e7b25f433f868fc85f3e8462aec2f16c3e3c09a7971b8c7d4eed4e30301bf5049e18466c3648bef161636532647b95d8724d458c2c9bc4176243d144e4268b90f94f66a8f4d054205b32771d36b0405639f9a2f5b3663f1dbeec08eb0ea6e82e2a29db4d92dcf8f3bc12c39297d3c420970e80ed955c8258d6d13d90e172269b74301f9799934fa7e4bd3b5a4ebc693c5bd85754a589b9ee5df8afcf405fe3eae3f5660a004504ffa97fca238a2f28ca2628fa17eaaa50b6344f1b36b5ab743eb9ab9f4fd68e2ff74ba2501f879621712be21b1ab48c878c71a4108415eebe2019da434098c21402e83c38d818eaf52fd097c3deb166aa84719cf70f3a78b220183c3360b6acb1a4900976249a48965ab8b130a89909b561755562450e8b836bab0a1be3d1970d058a7e493b5b81207e8154545690822ae7eb0f83484ad4a66c28aa6a855163bedab61532bb7770873305d1c0de0d02e4cc2979f9fce839541d5567e82dc8c1cb3394bb3ee334304844cbc05b225f102a4022f84f60d85c7487d6b0a14e2cdf51be3e9cf68c75c400d5f618e9a7845d073dd633917ff5972d3f8b7c6fb1b68e286ccc356a0717ea62f5cbb99982a39fd9ed6adf381bc5e06854dff01cd85b89c1050581980d147c6dfff9ee5d18ba83e6390708290a7aa1963b7cdea5dfc61718a3d3c9a74e62459f70d5c45265699461e10e6602d0b1cd5d31c3d4244ace1775cf40e8c2f0105231b98be2470b9aa6ed5437d9223852d49eeba68d69748038aa76819361643812eee458f0a805a6802fc08a3091ff000657a39b786b533fe61c857833ded20d4514e31a2ff311e35dec0f04ba56d3aa5730da0dadc665c88a601dccdd127f02dbc3932c70b6ddbc93ebb0e918cbaaf303fc8f5e30d97b663b0f616d0f7f37819e0bfa7e31fd95f2188385ee9bc0eb5b02c19b3123eaa0dc409550e5c7fffff3cb2db764259152cac20a4e0a3d0a6769d79456882d3e0926c17b5b391fdb0a86f0fbae125bd58b5afc4bbdd44bbdd492e5dbe5a5ae42efd9555661f7eb2aab903b769555b869d7d8aab2fa5adfaf8b7ecf2ce477adb20abfeb240908f1bef69abf4bbb7e27c1a38a16e175152d427d152d42ec2a5a84d953c0107e4f014398b5bcab000937ef2eca5d0548087ebb0a109caa0009bfafc4d604841b10fcb26fdf5656af73bbbca8feb64bbb62d776b6cb97d8aa7284f0b24ed2feca28694becd258332b4dab86a5a51a4a15a3aeb4b455a39dda4943359896ba62cdac34199a49650653666999a1ca4c4596590cc9b26234580babc9ceec84ad4b486f4a5a20599c6489d36ddd4e326b5a27ba9e7ad9bceb373b78ef57dba66ca7785cdba2683f1f10f4bcebd25a6f53ffbab46dda2c25f3f5666d91f9e1d61eb2d27b26817658da1a66d2c1ec64813f55b4755790ba3596669a2c79d4c154d9790f679b17c5be4d4be51c3c6fcb7ba8d2ceee1ef70eaf98b59dd3beb5aeeb9ba997ff7ad66ea65e743e686b6a1e6513e5e226c24564c6518ea94e3ee32757753f2c6de195cc545ecbc45cfeb6cdaca56555af64feb54dd34cbd988ccc91e77f6baa56a8a567b164a77e69554ca551b6a9d119fae68ceb7d4fb67955a2f819275672f125c718b666dcc258599ad7cccad06c0591bc7333f5021a1d61ea759b22d4b6d613bda8dabaaa74c636f5298acf3656e5512ed9264e3f9f8bc742ff3c94d18c61ac9c574ccd3a22072f729b47e66569561ee58fec8efa3cba2bebb6b4f3546174c5292a6ef36a7d3ecf764ef328e787b699510ee79f153b39f79c19fba75b980a6e1e5e649b58d56a1ee5ce486c61968ad7166ea5272ba3d8f9d9e6b57238cff685e65146c97ccf36af133b8745e67fdbd4ad303c7633f5f23d4cc9fccd36b36a9b5ab5cdd40bf8cd731aaeaaa7a958ddba72199a5b1ba69eda6de97aa95b9c6d5eac6d9aa997ec66b62f55b8cdcd36759a47f9dfbe525463d9d5dba2d596ba7d16564535abb61daa6c297b795db7256d6b3ab5a56c7fa0471367183244c8103186a8a184ed4b555943684b7adb3cda97ba99f4b838e506572d232c5954b408edaab20acb8b9268261dc192492c2cb814919ebef255b408f1ed53ca08ef53bc10729b664426990f185284247c1184f3075c2899c4d0450efa3d113d824086163411832d3401073a2885b6490c5d7ceac5f3fb01f4884116a913a8b40086295c5112d9a6d04e11614aa55e3e1fb9f8916df680e9c21652b4b4b042055f943edb24b2533c44fe79e8f73932e9b3cd92cf415062c00ece730564a8ceb32472d32ee1dc8117a5cf5f7a5431040943860c598317529458f28efbbd9c7831640818462064c8909248176082d0c509848045185194426014c1092d214face006482889b7df8efbbdd82d4ce085328001031e48210a25cfedb7e37b2139b33002145b0c9181410904eadc34532f2307bd73964cfafc82b639225e5ce99018da26689ba917cf459e7a01ddf33214d9a6bd679ba09d7af1fcb34d4f679722cb0977196ec0bd55808478972ca4bdb6b9bd6d746b19102cd2fb1ba29964b140c39021438690c10b1e94ae0e5418b2061e7c810643d450ba63380104062ec001115099844417567001e6055a94010da55b81114001c51744f084d30ba5fb1b934917135380a1053394e1e347e9deb2901688194e6479d18b66d21058b0f2c8ce646dcda390268fc4d64587c0a2268f2c897729223b1814c3c5685812b6038003c3f69d620a4c42c2d26c9986dd1356050c7aafb063208145b80581939626e79c493bc2d746c48c13bad80dcb3408e84043d164281a0c4593038049770ad4c55a78b50c0b9b2285b0bcab0f6179d53ba3afb53190da02c10286d4bf59e45b2a3dd23e8f74999156bd38e8979ab42d254360b2cc8012da799e0ec022fd6bfa57bd4030c0e2a06f37b6d706d815fdbcd52352bc08cb7bb2282deb2e5f67ad75b6764584f58f942d8497d59c108b3c0d8b74845a276291c7b9b088080b762544f860a2060abe00848a22d4004545099f891e54c4cce0064fac808a278e50bab7a48dcee29632487bebc1d159dc525e2cd2de8a5c1a90244c41de8705e8f00d3014e127058cdbb2eac6690c388b5d452397ecb2b61d70d92df211a7bb3addd5e9ae4eba0141ae6dc110e4aeec7a5797c5f4750bc48ef0392c5c29dc26dcd557d68a587264ac8bced6c896d43ddf533b390eba9d6e8aef1134ad23d6f5185fd775dd5d83b0bc465e975d59969de9f6bdc28ef0b91171e3a83885e266210589f555ed0afe9561fa97861b1137ce6649dac5c292b4bdef16370b4cc253e0abd620cc2e517f3aae8b588e69ba6b2fb56d631d6c7c851de95de22aece8da25f601de2a8c521912de94b464c6ac0d4d6731ca89c98c129211312402fa24e1091f41b6d8620b1599dd5f31d975ad0473b2adeccab53b98140646db41aa699c0a6b212323232323232323232323232323232323232323232323232323232323232323232323834f28b8984673b795ba097b6130ff5e21d65aa669da77ef4c74e7be7b7ff9bcfdd2edbd69dabea51420a10bc6b46a733139d8be20ef53a4b82fdd0c6b2ac028e10762c058214509b8468d21a14864030b0f5aabf6878cc4af01440a0ac252d3dcd08ab0146133886a4b0d43a5f9ac241a752d051267cf368c654278c92e484145691457b33f2a3706867263e82028140a85e290385fcf5b7f56b4860ad40979ed8fea82b004a130152d4128127faed8a05412558701a48aa40cc1bbdfc56e7675cddd486c97ec84f0ee82758d9689284274535cbad4ab6e0adcc2ed4cb218aff00aaff00ae3165476ddda4bcca3d3564994218437a50561d9a9c218fb43beae69974621f5aaeaae0da2b12bf765b892f8582a826e48485942ca1042ee5600d797b1192c45c8f18045f82954cc84298a4469720a15293b08afba5de0a6108108f7de7b899071ce44c8171172d644c81811c27b21700a11b28c2fd652d0d91a9eb522917704d8920f266c1e95de9900afd13cf2ae43fb10d004095e376147de3b0c5f7b65ad4f7605fcb795b8295584e02e213d2b00ccda9cf3b3de781e8891d9b8d19b5d6a558691d94ff687ef1e4a1992d6db274c5fab32b7792799c9248db3772f5065e7be2cf3348cfd2123ef551a86093318008102810201d375f6c37c38698c31ec6191cddfbdc672c6b06c6dce19e3df4f6f25b25b9d05d6968168c1deb67b17089e03b7fef677ed1d1028102df83ee2fd75cfe94d6be3658510661d89cbb7e0d2f81aef05a20540a040b4c0bb7a48a7aed4ccbb64d97df34b87c75e766d97da17eef2621f0138fd09e1c3c90727323ce31ab3e4e7c30aba31c4f872294108b7ed03aaf9e0c485812b639dd1661199afca0727b00f271f9cc0ac80e916890f048ccda346d915fc141f6189b2a2d81f2c89693032e514deeb8d91ad2d745a4a8813ee5dd75d311a1cf1fd465be2706ee2729b3b6191cee546c5c168ae04a48621b52dd9abe993e3388e3b71254033e9bbde54f607ee3ad4d7a7f6d15ddf92cda4ebdcb3fd384e9ff346ef1b310cbb4ba662a35dc51a232cd7945b2fd705b7de3ba63ab01cab8b190eb512afe0aae07c204ab500b06723370646623565905c2be5c4b5487c310a896bc80c05292008afdb182bd42c5641585dcc7cb1824113466b8c9a3280408168010a839d6b08af4fa10a02b685b05994dc5b1d04b76cac2e66b61096c4c0d03425d8adbdecb66d77b49776d95f6b3f9d63f08327c891b4407c50737d386d1f502cea7126c232c3ba672fcb9ee795dc33ed3a36ea8bbb980b57ca19963348fcf2fa2512abfd0185c4d77522bfd437c508217e2092f1db31825e32f2f87c64c4df0efbf1f0e06f87fd7874f0b76364f45c1c3b1f8f4f0816126fceb84dd036454c91833611799459a2b85dd4cfa7b34d4f18daa63543a16d8a9c84249390ecbbae2424fbb65a242499846463348ff2c8e680483e37b7097ac94e228fb2c7b3cdcf4b76a7b34dcf4b3687b3cdce4b7628b44dce4b760e8542a18744b649b24d918fec23f22863149f77dd5e5ed675552f944336b46dfeb525cc9247780cc5f1a368c429c6dfda591e421ce425bb4d4845ea2e858cb004b5d22030a43e280c0dba38c5e911c212440121a403db023a619a10880456a44b9005b2c5c90217a90284b00451241308f34b1065495a759ea714253d634776d568c2961d5972082d6aec105a0cb1224b9636486b71804eb865e3052b2281cdc20ad20297360588c2a2ce72b26daddda030ec0a0d1605097d08cb6d0b0314b3b5624056e86f6390fa200ba2b97b436357f45d7005c2ab4373028162bc0c5f7b4fcdaad1842d11acd99c6384252806446961266210058ac924f0a0d0b75d8ac740a0d1f3d0d879c719432dd0558bec2e4bea7b6032099ce9d064523633c362ad562588caa185224c06d3a1f1acec0f770838051669f05fe7e13b9d7b88303db44d9c9de2c1e1809e771efe7b97799b0fec34efdbdc05833acd038d9e7f9ebbe050e72e98d361deb7e3e5dbe6363bdbdc989b83db088f679b2307f700c26de6b888b34d318ff4f76d5333bf5310a56d8eec83d7ef74b6796b3a341d9a3b73577735f2effb2eeac68038a0ce364b50a4be0726c3e413689bb8a686d4e770b6398212bfc39026173908246eb3f391872e148ad457e8e241db045d44640666c6f73d14d278489f649b1a95475a6f0fca0cf120d07fdcd2cc8ad4278e89fec916c5ffb825d036c51ad0456236c0d1ff4e26274d14e5c03025253926a3c67fae9579c5986c33dc66068168481d9347103839390ae566ea8504f519f7c4d0d89dd08ad4df1b9ff2e8aeeecca5b935f8945779a4572378cd90fa9f6d5e9d6d7e9b28db0cb779b24d8c0a7336d99d6b929d7a2139e79e6d6233b609c256ab3cd217f7a764e7180c836bf229c364548ed9fcdb174d1e6950eb9919135b6dd3d4e136f30cc9f61c86d41fd9d74c5e5d3017ea8ab956d7cc26fcb7c16d8a33e03bdb14d9270c268ff43f5bd35c345a6badbff00bff6dac268f740da9c1cec63c5bd790fa8b21f5a767488dd190fa0b7776c26abe189a50c370603a2fb31388ba309b997b5a853531341c7dfa5e5a1af025466d5ee699f0e55543ea123b91faa0532675f6d6ca237d1d6aa1186e2e82bb149d888d268ff453ce10da63bbb5b69af3c4a1a4ee3ed022b71434842588c653a37286307b09a22135f6ec8e485c94b4a5a7666b7d3ab0bdd16c6160110d166d61609106d1a8a06109a2361a521f54e1961244034e815b4a243c1283287d51b20455a47ed9a101cb10da97208ad42195867380c29dc9239a99d68c68fd8cc8e2e1368835c219a71c3866067567ee0c0f78846fe211fe17a267a1c5605b464c4c69cbb065c47443d440de994cb2252d143f430ea1457a1c9349436811051ed995487d8ca80a51f934851684d00a518b2cf0c892e587d0220816591297b68cee0bd20297080358b4ed173317c7e0530ca6c134380cdca29181c4c727dc628168c1d2f2860005c2d2b692c0c006704bc6f7b63a34d8512548889fb3f5ec67770eaeb014af1d2d99ed0c2cba76632c5e58f4a96ca1454adeef62c92108f5a73fbdc1e986db709b96bdcb5d87ad180dd6c26ab2333b65680693a5192a53653199d4f9bdf085b177edf676c942da702c59586e5e416f0372faf334a75f62672685324924c3e434a3b22ac76435af322bcfac2b77cd61a7a6656d85f27bf26b72ceefc82fc9aff83be3f718cda492df63984cea8ca0cf28191979a7e4f39901daa692921917676c53c94ef12839c936495e32d221e98c907448ee82457104bd33a353d2e974f67fdc92f81fb734e324334a0e2a21f9876464441bd311f493f1f3ce64f48442e78c9f7b4c40dbf401fdf3d48bc94f4cb6096472d0532f274739d926939de26172916d8adc2404fae79e73443821118e0b4601a18ca0734e3e9ccf5db08987e3e170b68f531025ce7fdc92288feec89247395c0640441edd8ba31179741f1a452c48fb849d7b461fa7204a9d2dfeb825ce26514becd4e9dd6556b1e0dd0ad72cc436f8a0e3fc0b37dfc0eb7b9d2401dd9ada9677550ea7a55df10524e45ce1e6b6b6efcefa2a7a7b7b737b9717cdaa6c9776db7c5d65096126d12a4cb83676663474f8b6460bc360149ff0d9aa69a934eb0c4bbdabab5ef4c65cd545ddf4a2f8887baa69d198b01996dd2cd72e737422ed62687ab64dd8d67249115a6f8bf7b64f4398ae26ccaa7c96397e9e5844a6d4842b2b9344a25f4c539cee12a7f85715cd9cf5bdb4ab4f2d73785dc77120e7a6d4cbe6e0b7196ed36683da5932c904ee8f05db99996eef8d338ce10be7d5c31b18b8cbf2b8fd1bb1eb794cbd6ccf524498b66dca76d67d7a9bfad9beb66bc79e77e86fc7f6bbe06fecf2b7e3e381dd05e7ac6d539bfad73e228ff0b76d6e7e6d22f208bbe411b62cd9369f6fe64dc3ae6d6eae6bda36af4abbe8796e9e73cece4fdb26c6d9b66d6214a36787b36d6e56307bee75a0f64dc6e0879f7a01df6db3bbe7a9d44ece28d6ca23fc6d73fb42bcb76d82de75ff714b2a8c861b04ffe396ba6d82989932352bcbdbc4589ef02c56bdac3cc29c7da9701a3a7782b6e9c1fee396c06dea16e6c1e7e7b341375791ed1bd9da2f9a5e1556f30887b6995153649b206c9b9f6d5e2d6cd59eada52696eed44bf8ecdb3673679bdd79d7cd7136565dd689519c629556a9b86d5e98655e597653a7e6b5ee6ceb36389f194d35f6bc332b8f30b84dece6b5993e4e4194b4ffb8a50c6fbde6113eb7b39a472a7bd7ac9238e79b4b984ad8f3be547984b779a5dbfc714bdabed48c33be5212678de691969d19d6c23096c652bd662ba6622876ea96f652b354ecf9b6a42fcaa36cbf856d318f2ed63bcca37be51dea71f1c5c0eb764b24692669a85025c69c2b549ac2c0a0e877d329933e709bba8d4f25456cf8cc24d3b6313e71a6afa778212c2fcbaaae7645bdcb5f9895434d0c2f8a5ee620f196238ff053ce10965ac7027ef7de79df3681dfc1a75ebc739cb74ddff6fe3df5c27d73f098db266fa77878e7fe719c777c8d3bd6f2087bdef781e0b675ddbb6d9a66ea65f3ee17268b447984d10d086ef3a76f9be23dbda75eba6ff7b6199a1bd8a9976b49e411fe76f153c410e65db2e858a9e4fd9a61cf0425952caf2e5da931aa2ffd505fdafa603ef43b7d67aba6a5d2ac332c75a57a688cca4379a987e61186213d74cd245376f2ce1ad2947ad18e1d6b91e6b64d5ab6bbb5d4d190b8eb6632c93475337984cf62ad56aa8a801e25d3948ac9a46d9bd86da947099bb0dda9f2081f77aaef636d1feb53651d4460541ee1ab5c41c2baaa46b2681885af35155f9746aa2021cc5b250949d8b42eece942676be8aba82a6a68b705c28518d2236fbae633ab30ebdaa505e2854a5e576922d4bbb4409a7092d746c2ae6412582ba58c30efd202d9c20c89040ba9c4b54297f5dd9a9680f05cafdfd5ea175baf7179cdeb5d61e7b6755aeaf4ba322dcd542dc576c9a2f7c9924f9d5e3507dbe67918e79cb1693edf9b297c333441cf33c6d9097adbb3aebbe7c5712dcfcac9d785811ab6852cf77edfe57e65957afdb3b1dbce5d3e37c52787b386e7384c7561e877bc4df37ae77ef2558137034dec79defd70be16c6ddd42cbc4d8cb5453611e1592e2b8f2e68679c6ef7ec687deef4785a9f9b77d5b42476f685ce8bdef4aab09a475764dfeca3b7e9d9e6d5d25f4dadda26b6d9a997cdb16b97e3cefbed0dab2eebc4284e2f956adb66f62e8765773533f05e8c2f7d7cbffc2cc3b8fbedde8b2fbef8e28b35d6c7f7febacfdbbc54d69a2fbe58df7b2f8c626b86ddfb8b2fbe589447f66e6d6fb6376f8bb726f6b818450df95a976fb17fef6e533cb067ff6eb36d7a8e85acb51ecff7755dced77561395b6c132691b5358ff0b5b7313faab64d1116d3ccce772eaa3d70d37ddfdd36da696aad2be34c53cd9387444e3095a7e426d703bf4dc76d5a865d79c63111a515725a7a164b7666aaeb649b1a2df97555255808672be7e6117ac6ce25a1d031124c442467e877595adc9ce7d10d793cc744cc8e87a9eae709c3639dd0d30731679ba1da692a16b7e69686a91976b14c4c839fd76dd19a9f475816f328ab76984757bab53cc27429df1a5b8ca8542ad5ed70c20d1812017d3cf6f33a2ecf2821191143ac102b2402fa78c4f3def3bce279cf10eb9bb1bd92dd916c6e646fe2bee2598ea0774609c9482922afd6c25ac84a93c0c1d2d66b2cadb268ea458475c1aca66a69e933168bc559b61ea1570c63683e31562baf197a595a9f77d558b7a59df8cc5418c5569c62e8d5ca2876e616a66255ab2aaccaf075ead69555e9a5d3eb42edc5b29a5e2f2bd358c5b49a555aabae4ba7973aa384445fb6c30937e05556a1888a4322b7a08f4754a5a1545c6794908c8858cb307d89e7c808fa8da0de08da8da0dcde46d050ea4143bbd44476197a7688e541ed2ec37d4d0b435114cf4c0a695a188ae22f8658f6652815514554115544bdb6a4ed520bb12deaeec9e424c57e03994443f4141aa2db4360e528391c91c3ed1179646fbf1d2b22514a8a89c9c9c95db0799292d8e4194a6e270a4745e29f8c42755b395b8a72548e6a6b6d2b0bc7caa9a19a47f89b6a4b451c164715dd38b7d61ab2589cdaba6173e2e3d89b752587b36c2a1c2b87255ab90d37b6c99d2a3646adf5c6452bc7dae1d9bad9a9ba74dfdca03edcc43c49fc1bf6e63c2293cc1ccf616fac4dc5a187e768919c95f41e4e4a39d406e70c5bdc1a9e29f8e3363bd5dea4243e8d6d6ed07be709be49cb5154125f8795c4f761832987c3e16c1c931cdbe4f4b04db3879b6c4e954798b44d2ecd2187f7c0e1f0f01ccec3c9b5c234443b75b3f1520f05555075c30ec7f11d74d8a6b7a26cf3535b74f80dac8d756a6d6ba86ead9075ae688b4b39670fdbe4a039e47094dda1798479d86677aeac3c875015a62c5f394bb776accdc6537929c802551c385c741c4e127f876d7aad8fa5c30dc7f1ad9cea9e1c9453372bc7dab4b8353cb95688766798e6b0cd50b5b27294ddb5f208b36cb35b6fdcf8ca6663c36fdc064ff55479a4721be00ab244a3d7380edbfcce6fbd01c7455f0bad09555dba393bd506edd44ddab136aa6edda82bdbdcb06edc38caf65879843d16896fc3363d55e5376ef3c8c636c1b5468dabd8788d2f25f147dbfc44dbfc706c33f522fae867abb5619d2fc1147d09aabcf425a87aaa9720cb535f82eb8d6d82ad6d9a28fb537d2a12bfc63647db146dd0c6fe528ff5126c796bcabe27d56b8dac736f5bb3a234caaf456294975f7a5bba054f12a3a86c10fd5427bbb39aec234094c423a8a68599888d5c1e5445497c8f9a49e2299a394b5c9d670e4d0b4351bcc8655756cda16961288a2a55a54578d70b5a2f68bda0f582d6bb7a500f9a4927c728c7f7a49954e3f81e5426a51cdfa3caa4f1f89e984c32397e777272131aff532ffbe3de261aa997f129e336ed31e53552b669dc291ee351b689f2f1649bbcbb283cee826b8c2e3865e4e182c70e7b26e3ef82c7f14f1969bcc6b86f3f1e261b8ab8e611de1f0def1fb7ca2ab42a2dc3ec9a49a052f356d0caaddb2ace882c7125aa628ca8125597c63251a5ada28ac417559924828aa0a24a041555a24a548921d6298aa2288aa2288aa2289e99148ae229a6a8212c43ac9a4c3aa17193e3875a994483c6fec9f1433499b451be9f7af9c7d3387e68cda4f1db84b2533c50f0433399f46d8e4741c10fb1320905013b28a1fc7e2fe3edb7e37e2ffbf6db71bf171ab7df8efbbd9cdc7e3b4cee8243ac4c42d93f6e09e526ff8f5b129d7c9fc6c936f98f5b3ad9e1f6710aa284b2433f6ec964db3cd242ac509a49202d147f855212bf2b412a11b464114133a9a3ca23aca9e14a04154145d07bd0aaa9e14a647d05a9342d0c45f157911f16e8d4b43014c5ffbc2f3f2c123f250d617ef9b916f5a01ed4837ad0ce793b67d7e974d24c7ada81c9a4c374d04cba09ca7f72939b744e9974946d3ad91d935d528489c964773a270b597a5012bf73669267456a33a5b892d8437392f8bef4a0236a09be22d8a2b335ca8b66fabac9c5f1e4a1f1078d33be194dda4bfe194d22e8a6fccf3d23a18bdb741df4ce68da3cf582e987ce194d9bcd3bdb845d7c389a3cdf6c53f6917725e3e623637871e48046cf8c77e3ef8d27ff46936ba369f392e7d1e46d377d9e7f8d266d6b1d93c7a43df5c2e9bc3b369ab4adb78973efd968baae6d53c9a8fd1bb37bfadbb875dea76d33fbb64b8cd0bec91e660fb173b073f43bfa9deb9eeb9efc4ffec7884ce2b62985805b4abd84dedd24f29778ffb6293480ede4e2bde3bb6c2009e8db4fb66924074802fac602faf6cf08f2843a2267649b9b6d8607ed12fc19b129227c76fcbd84457c2f9c63b75f119d974e119e17cfafdbaf88cffd5e3ecfdb34f29489fb4b9e1ad9a6eea11304dcd2c95325dbe45d2479c9533cb6837eb24ddf474cb6097cc92e79d90e9ab14ddb41c75b11e0497ebf17f024b75f11df477ebf97af08efe2eff7e25dbcfd8ae8de3d74fb15c15d84bb08e8f81d043289e4f84964d2c8f1379049e2f16f08f4cf78baa5cf45c6d3f511fac9c5d18778938f8c3e463ee324a30f9297bc3b0ae71d4fe7d3f170b6d9f93637df26ca36370fcff9bcbb9ec7f379a7f31f41943ce7703a0f43ce379bff08a214de7e3a36220f85421745f1232323272139084402ba0b66e9726412787cfc01642332893b3e11ddf63c9e6e299f1b4fd747f7937ba30fef26ff461fdf671c1c7d802f79775df95a5fc7302dfb3594ecd8afb1ff08a2a47f8d3e7e0451cadbd4dbbcb06da65ef4b56dfeda51b6f9ecdac9f8238812f76e3cdd527793f14710a5eede78ba25ef33c61f4194bc7fe3e996be978c3f82287d07c7d32d81df461fa75bdafe2388127897fcf9357aaec7ceb191f36c0cafa9a82164211947c490c838e6701900114674d8b318100313b8700516583c5842291bb92254d1c5ca45164d8ca1848d3d4c40862684210b30a010062f4a7ab45d20230b2c78c1136510815abac61e30b042088b0a282eec2055ca232708146548a10552306107a5fbe1972c430449f3c8a28204091224e79cb32d056191f7273d2e0e4dd1d91a222425236248e421d1f3d9a6d007c42374926d821e12f9dcd3ddcf7e3c0299644b218961dd8383bb13749a0fec3e52f73f44a525ee90f720cb7bfce318beb7079db53fe8cc6117be0ae87ed25d6288b1245d88493e7ca47eb09496b8434aa9ebc74ba90ba5f5817b17706d885b2cf981f6076dc4c0057a9018502223259087f4b8f701262b30c14299608215c028e1334136e1072a08427306335ca17285d4638531c095159e20edad08ca5b190e4bca760176c87e0d6133db846c6381c4a201a85811de97365b8c45677b0859b871eb7e61d2f7cefbbd20082fd99d1bb9e7ece10d7f4264bf7e75b9eb3a0edb462532727bdeb84b6edcf5f1fdc74bdf4e5dbf3a5848ecb70a657c517371f6dbe8fd1ab96363475edffdd5b0d17e19e9919b00b41fb89dffc12d5db7cb9bad15952662d861ec062989575223714b07d3742f6d18df4bdbf27211545b43761e96a186b457901608192cd28620bcf713a2736fdcfe3dc7e4226c7adbb64d8b811da7db95420196d87aa1b04b0a4cb7b4ccd3ae14b760331714b605a3c285c22d18eb92c2b6604fd074cb1b2f46056cc63be725a6e2be634f607ddff6d2d6dc15b75cbbd4f4a8b9e4ebde78af8d971b73103a3d20f1394d683520310d56c98b01db82fdf35811dece00db12420009dbd27d7b12b6e5fbf623ec0f96dcf64aba60dca2a1b0c7b668525812ac61bcebe82ca09f38d915ac5780352ac5e6f81a0ab7582bda461096b6868cf9442cb22d2556844f02ae58645b302d5811fe669799dc5002e832ee3eddc225b6458b61c11a6cc52d59d5826dc9585811fe85428de0bbf1aa76a5bb16a3bb57a87705eef21e6157ba672c6c8bb5dd3103aca87bb72f14e9eddb5d1f025874bba09b287115a5a521cb2b065b615b1be77e4850498dc4aa8691a82c115ef26aa4c52e4a7a3e44f2d3915daa2676a8328479971e89b5d8eef5487c2fbc5d4cc5f5212c4f8a5ea3bc4a90f0c2e01618f209dca28de18b16999fef80fc83dd38eb080352ba109627b3cd346c8b036c58d27f816080ddf68706d895fc1c844822f34b1c8eb82a23a85811961e5984105c49acce2861ad54131313bd7a1adc46c2118ff3dc9d3644fb2836a8a4d03831a971db9d5f7ae3861c58701871acf4d0421ae1c0f1d10ed7e1a9171e4ee2619b76e0e13b3cf5427a0b699b78209d87a75e5ade43cb3691768a07e92bdb5c3909070fdfe13a7c345ac171fbede861e4e12eb865dce12e98d4616f34ea401a7578cbb8c37b1879b8fd788c6ae018577848a1f11d68e8309abccb615c39cb88e31d0ee3880aac29a8c82e7b376e7cc4f1d1532f2bc761659b70a45e74388b0edbb4a2c3579e7ad9e139ecb04d3aec140f1d7ec3366fb80e37568ee3a3df30dee8b437de70179c43a7bd1cc6958f2b77c12cdde58d38ee8271e8b2378efee3966cf88df186957d1ce3fe6844e97c046146300551f0049e368c200a548131a00aae4c469005cee4d14ab26959aaa924e039f2d253b7977a558930b9385a32076e21418265cc718639be9b8c4bec38cfb364f20e9c995161a9ac54d49394abfce4db4cd9261a1b8c2929c274b2c1fc03e5b7d481604a9a02934203a4f194141a2008822797968c9cee4419535a87ea545d4ca776ab8ed5cd9c2714a62319392ec5c26ef54e1eeac178a987f2545e4cb776345dababf1ce3cdad40ff4bcaeab7676a7e719533d75f3a5dea7fcd22fed9e5246b8a9206d15d14e9caf2b63aba1f2088f9a4a8bd1546da5b1b419edd44e1aaac168691e6967f901a1c24a7e69e8394bc13393681c1f3c65d23e3e8866128ee383309974e3f8609a49a3e383285005c66492caf141155c6592e8f8203e38d3d518c78bc6bfb331a2a4fcc4c62fb26143f41aa26db2b1533c6c5c659b2ab79172f22e7b2a2729dde59da8dc05d7b8bc1ae3f813910dec9dd4187fdc92e82a221b1f6da8fc2a29291d8e71fc8df1ef46230a905619e4ed30cfd2a0f1a33cf5327e346ed3f738fa8dd1368da38f4fbddc388e1bdb34da291ea3ef6dee8f688cff516e329a8c343a0c47877938c6f1e35df08df1dbd169cf641c5d3bf0f76292adc68c35666336663463d61a331ae34e47d4b3984ccda32c054f9a30a58c103c559a08b197e0099e07b057905a8776f1f766ef4186b2f164213fa367544b164ed8795fca02b26c6a69c76d97ea9a49437ca1669426afd78cfe82f505ccaa9228558e2155bb72318d96c5afb684d73c3a43e892493a34a19ee7f9821d0829f5a86206e71226a0023d9928d928a85c7005157e94a10c4d28f520410aa410c50c68d630842d6c186780a20728094e334b9438647c410c2a7c21022594005c4b7a946023ce299c35666827c57b370d630d5b8c41adbb5bc35e6bad318db188549808efb14f0776151f612884252fa003042df8cdbae02e5b1d9bce5abbbdebba9b6652e82a67086f6a45a1e6f9fd74748e8d970c8d620e1f8ade22b99799155af28bb50b9164af88dfec13ce0d3e909714b9fd94d0715d00216ee1baee9964811d5919dc600821a9b01b49aab0853090a186b01bb7669de799e282868b161735424e21a70b31410ac93d5c11f1547c602775f2b118380d79094bca681e61326416d851cecf68664156214158e215b9c41d52326fe9c44a7795ae385cb0b898e162ed641825d89195d1bc08450feb06ac144b0846b0234b7e3a3890973f9c6251be8a08c2f0227855c14198038ccdbe5f60d1964198490b848b2cca9b5e1659e6346b81cc190ce4293fc1047694732af23263d1799999f07999b720f3cb2c84cccfea05023f7171c8cf390958d3c2501c71103cca0f8d168f3ccfab6d6531c8bb55617957384888bb20b3092c09936147b895474f5c2030108b432be32fc8d825c602dec2aee01990ab349358ec083f87a6a67615b3224b1692e5045c04c18e2c1745c848eedb8654c14188f1406ab2c480c68eeddff44221450884f4da00bbd2f33c6dc05a824502960a5831006208161416076d77116612f139f47c1271b47814ee324f41deb403f706f9de205741e66796902d0809831032d2f0845004ad296608f52e3dcf05872733c785a33724b4e4c99165146151be1199746b6047f9f99b9d6740e2edb7b02471df28f028b4f113c8acf2232c735a82d00376094bca51e051fe258125e155ae81c5211fcbd8d14a264361d140e677208feb2ea4074286200409e1a7834be14288bd88c2096479d38c2c735a034b1a828b15d8917d4207515c1f90998b294638605b4a8ae84aa177bc88c209248917513881ccdf80fde1e2991484658e017000a081070b805bbc1ff447c8320769bd20f33baf13bd25ecc87241028d0b15841cb21b39239e1755d8421844329e06951bc86ebce1c22d97ccd0f33cab78228a29c2208856d82c4441587e1842a60bbb62c932870f273ff974a0e0305a76b560d4601c06d9c260681c175e4a7c80e43a8e86c478c5221c9ee7799638999393397132282703634596cc3e1ddc0c4cca2a2708cb1ae45571222c4504905c3c37961ac8f93362e421f396482e1e7f4588e249f6492b9d5869647f46e0877e3f23c48d6786a0402695c843860c5903a9812e0c859b735d776ed3a1bde3c68c2360ed4ab7cbed206754c2243bb2d3c23107a1779b31024a58bbb23d07a797c41c883ba3f22877638e22abf228474089951c05b8736a33ce76cefbd65dab21353390562b03a991a16da33606693fbd8d9a18a4fd3acb8da5766eb400770c645207b795c80cc06d09e86be4c68dfadd580a6149253292bb0632b96d25b45d2a71870c195243625b898cc476a9814c5ed73a9feba506b184ecd8069b817d3aae217e62f7f76edca2f32e43ad35867db28a133fc9eb799e3328951e69b110265c2142ae90285c2155b0234b96223aee09e982bbcdbdf7a3e9ae183a51f4425c828b664e3c51c545d7606bb42c6c90025b63859435a4ac01a799094ec800a7bf38631ec68830ddc3f4365d3bc5e3bafea6b719feea74c230a617a8540db47741eb14aa1901040000018315002030200c08476391388c2331b5f614000f80a44a5c4a1c08245a94a328c590420a1110010000010000032020006d30b363fc7b9d2c6c7822050812938d38c5ed9f91dce1a463069a9e112fa6ea46ccd728ff00286e612dc6f7e9219b3015ec9f1410611c5191c53e37642d8e45540cffd83fdac6db80e087feb4a82c92fa96c7a846eb995f14919330267930ac438f368f5a22ebc01a884c5d7595f738921d1d955c01c1b690e2eafc6eafc00c352117297a5e8011faedaa3c6c5dff15d7cc9a94133bb1aad2244141409a6fde1fe013cffee57e2cc55b779617f4ee82a71690ae10b69af41ff686bdd885cb6faf9b138d964eb4fd001893e832a4ae854573e288a26a28420da451579e12a2e5b79a2b2fbd7043f915ecd82497a1c4942bcb78b8ba88ec347b56111506610edc27e607e6f850889a245bca37bc3e8d0a93d92406acc43d05cf2e1237ff269d89dc4c810a062e5db3e653c8df62a817f52a2b9a1876b8f615fb3ae4539bc29fda95a09b7800bf7188e2a8c002bec4dd589550ee4b3b71a2bd81ed725046ebd2c3f06058f707814e2ed0d02ea00c8b9559ee74a1bbb5b6cd0027fd62ec722ea8fa149509c53da33bc8dc3cd2f69723b21f6a1a3e66a767636939fd993e960378912dbabeac71d6559535ae3e4ff721bed5ca2095b342b4343f278b513b5dc9697fcfdb39a82a67d9dadaf06f30a7170adccc46d6ee68751b1d87c48fe378b67073c800d7c0d28f85811a66a02b840a08b832f85aa59c21c398a04de28566a5a00865a23bc8cc0e024f312c97c4e6ba9d5a360c8e21ad92af97fe0bbb74b6f5819297431d35909f7c8508257dffeb01d122b9940512d291798c8bf7c52abd559a93d3dc25c5ce9c3a0abe67e8c5cb486ba08c29f9500bc6143fc34aa6b1c7007471762148867d2ad801cf2a47a32b0178c2234675df411e0c4bdbb86978f6fbb237b1f3dbe2eb3b303e649f2176d62d2e7553bbeffa9a89c05c00d5eaa14b3b3a24e6bcd16db654e5bbc469d28aeb122f4d40f6676b30640730ed003f03070e3fb0ea491f23141636fc59eba2d193ef4963415f1153f0c2f10584dd3d7e01176510f338c39db9b47f7946836a59f39bb96c9d34726308b9736e94569a703b6bb046c99f5f60f33eeaf9b129f985dfad2130a43eba40e3d64bbe60ead7e82168848abfaf823234b4452a0a599a1b76b71cd774a7a51c7e643e100bae55252afe0547b0a2f61a42523c8f68b88262d440a79106d91d18487591e68a3b9146232a1e5d255378d791bc1b9a9f04481803a40bd6efe863bdd2adac281d125908430ba9016a7930f382df7b6f7785ab89e9f0994d7e9e8733c3896bf3e40d49b18a7bed63f416a1ec67b34b588b94262a7ab6fcc4d4753bdda4ef54d95e8bcb381a267536178c2d92c8d865ebdd32b5b2866861d058892f0f96c563b3c22d4d5b5c04f70764f68597045dc6f7c22041527405082cfec12cb3fd6deaa00ea070b0378870d24ccb4d3540dd1e1fae57a004ecc53a33c1208d818dacc9cb2585d37aa95f1a429bc9d7f74331c50bad5f6b0f86b0dae28d7d8889fd61614abcfa9a4cb3146883ad17ec4e0bddec1d9c82e7d84941e580f0ec2f97c8aa35fbe4a93b52b9a72ee217c18700d01e7814a3f0116d822d329125366a716e8a481a7b808d9a0a44fa7cf01041d27211188c6d022e19041cf4100325359903036a5b26276f19f1c9176edb157c9a197935113912a2ec5943822771472fb2e22d67b528925b8ccb33d97dcf0e15a4bda5928421adf7458d1d5dcdc6ae183f8f8ed209e2b4d48245224f56676146609f60d4cc2928aef8e51825e724fa7cb330fdb2e782398ab12fc01a85446c8144c75fc82c8ee366a3d89776689ee41e0e4df8d41c418628726b0ba7df847142454fc75a3486107953b60671e08106b4a34c9221fde171e4fad98b0130ced514c6cd85d29c8dbe1717f954d99d50bff23859609c189815530bee667e182e46586a1caadaf2f8e1e208e6fa49f2bc692de3a451c488397c03db1afc628c9ad37635ca28e9c344f3b12161e73e02ca7f6522928f34060994ea5caa7d714d63edfd0e2245c1d08b4be532bc28f3e0cf953f40e25245fc8ff1264af8fca5fe20fe22f03e739056c03c593e78538752c993a86f30b46fdec10ed2b69e7c8b8df5e8d50be5cef77af93e09d69ff6452c94da4a2eae795068bee37d5c8476b4244a97ff4e2a1dfca5f0c81b89e348b204af476235c6be6b973d9490c8457cb32cfc1bb80b31d39c5fc024f1f102e281c5d3b405d8552b1e8db6b6f8844d1a82402635c06021db334cb9275aa3715173d466e7daa310c785ded5cb5db37e6c1dfe2f3fd8503d247d189ed9a3b5799bce8eebb0db6d4ebfe4b1fca5b754b4102b29504df7a4115edda315fc440c3791675705606f800f8c99782b637377853c8b2aa65bd210ec1e55df39c1dc88ffa3b3d391c9e5aa94deda154e0d188fb1e2194497d636fd628174a70fef9671cd31759bc2183652063e00231ac529455736ce35115e7064a53e325dedde048c188597aba92f784170f19633293e34899bf8a82db9c813d757b977721261bdd2084916777f77ae68c0ddd660b6047fd1fd858efcc1cf0eb7316256ee7c3bccc2e8786a15fd212dc1d7bd4a8cf51d46626ff5cd6bddf3540f4dd22d75b5d8c83e21b9e7cbf5fa0468348723282749844a0444a801e01f7750c669ee28b4223db8030f985ad43c0f78f9992c30a31c9bdd17d8201d8c9c74f0f7fe7b2cbd84938cd596c914c129e536e2923bf3d3529e1c0d774c4659c37a0a9a2c626efbecefe53fa42a724d53803a7e45a44aecaed14d998ad1c2391aef40cb665176049e55e50f6e58dd893b2df3e559242b5c6d932d813191c445435ce99988b67bb699239f4c62934959588129460e91c93c1bf2165566cc7cab7ad286d06b343a23525424b2a2dba8baf8e290e205e9dbc385dcafc4201a27f315372b0840a53d243c0efc3bd35346a80ef119e00e99ef339a93361f8c1cff39182367a5e198e3b709dc580c13f941c44de8d88136c8917afe4a9de1c679ad67aef7d9bb49218a5b64ada5850b125235b384d95aba7dda14624f10689c96bfc4dd173bd92b45c4e84280df0b5adf267b4103322a377537dbfc91d90b0b16bda920c21924f8dc6706c52d412a79f228d22057d544159cd6fa09658b166a2640757fccb9d34ae9332b07215d62ecfa73d28bc41dbb039d586668803db66a61544f599f3e47db0bd2ff6be222f27e0e25e27851136e3cfa22ed7b66f4dae8673d4c6fa557a6fee3ae2be55225a84a58b6695cd715bdd8b78875829e05c819de640b4a1822e76ee2ec5600287aed7151142eeda2a372fc7392ee734e5218af0058f6cb613f38a4acd64c88b54fc4e19af49a0312ca28138b46fde705825628c42e32e38ccdd8c6555c3740b105c74ee8636d0dc72e3500e224bd9e14aa8dc5d1c9a042695a623d6bfe1ca61f3a4ae532ddcf0e27acceb704189b0abdbec40dd387214b69835a69c4576c348b8bab30f4561a47aaabb9120ea8ba162dbbec71142ae5c3d42f303580b6d2000234e6fac7caf806b8faf13f7a883bc4de63a81a24796902117e2ac8cc5023734a9527ef10bc3a53a7fb434639cd73a3d8bfc403dd193b940498f0110131ff99a1c3791766cdd9f2871220131a416b1d52fcfddfb03fb0d74bf0005c2c8af5ff8ba028387227cdfbb1c93dbdf5abf13f2b83ceda5195f46166c64e2b02736da581ceb0a814607a5263632a53263e48fdec869649bfd4009285d239dd744c8cd9ed1bd213c77b97cf3124f6e40484f0869e37a51bd08ff4c7d1b5dbc6f25ca46d631a0e3da8ee6c3948e3f55ed352b9ef6574993eb0e91b028352c9fc922447f48700375ba7eadb228f945bc36bccc7c80d4a21a6ec1864df50653f550bb297b8f53fcadea5702461c5f3390c162de1f9491855d7302807673ec6aee92affc2be69897b73d5565d535c2b65a97ba531330796375c8873c087b83ff664e5e47b6c5c2425b185a5646e5356d3ccd5643f08eb848df33863e6bd3b3ecf60ef203f598d046be67cf0ff65580bfde869bec960f56eaba9ea0698f253a0711342bbd81fef597f294c5d253d124faf2386464f26e166c99c88ebc963cbb59920dcde418fc216fb5a32282a8c8e6b02df39b1ee47dd271d7f29ec14e005e6868201bf8c3cf7984b344713cdd024f0ddab82ff02c804453f28711c0ba877bdad799dfa579ebc960126a3a551da9c42ed41a8cf18b0edcefa50080be6b8ed33dd2860b11d2df14371465a91d0a5f2acc967be87d2c474882ec00df888623d7ccec939e160d23083f8d4e6bb4d436b888cca7c82bb16a2a111a0e34d4e2219893cd1afd15e16b5a02b735a78969a8e522afe42a10b55d942ce0a4c1b618b95e8d3219c0b10a0ac0c0bdf084c1bf630782e89e450262033b7706a0087f17c518cd6eeae2e2c62b8b442bfffd1e9f1e6d836b139898eb11621eeafe5cbfcd86523910ce8ae3bd076fae35ced3e85a0e6077a51bd2caff33e6fa2bca3869a739566b34b02e263c00ff4ede011e12996dfb044688a0143728ce5466aa0e8d8f6338ecdba083332981f38674a93f29677ea74b0bcd29b3b6ca62d70b862a6c95988c1fd7d92bf71cc52a17bb90f152b254b9c2269da72bc5159792f6c10e65e6a9f50c72f93fcf4c0dff652846eb82f95ce6f446d7fb9e636f32b9d96c1f24cc349068dbbdc64fe944abc112565af115c87f91a6e755227e6c7ebc40fb5b55435ad2c5cfd788dc80daaecd71938907914649b316d7b966bb0a8983ed7da77c70243b0a6d403504a9df5def245a9548590a78495253e64dbada71e94a8900a29ccc9e35e39b92bd5680f70647902b02430961ae8242641f36bd2f3a6efe26a4a35f14e833e31cf151c9821432530a28767c7a3a4001e0f80c94dcda4fe12218ca08249e0ffa9411f1405b5223debfd04d8cfa417f8e6e72b7078266bea1f8db1b1d8080eb29c97c1674de8d0f3f14f786222d9367467072400479777f5a7ad3815a5630669a0c8f50822548fac31ded0e22a0e821932f615516be85b04459235796cd7c90f80b401693202a1f85ad31aa0adc64dcb155074a6d53c87b0f01e26a9b61e090b176175c2fc280ec023ad3c08fef25f624288b920585234ef2f48985a45becaafe29a26d43502110e6a27125ee0189eaa60b36313a1d5ecb6a9b7a03747cb96a52774201eae9b957978100805012f5fb1290969747dbd680942dd5893f474ec12efffb18663c20c81fd33c3bfda9efb70201bc3062595fc946351cb79a38424238782418939912790a4aad59b7166c40d22e0a3ce86d6933189648f2ca1f3a15b989fe601f894cbe80556dc99b5b3e52c064d350469da6ac994c949f07c13f5374b044657103b290b8140fb24000f4e297ed7d2a71020a167e74855c83f4b3766aaf7606f33a703607c710a0466343b3a0522d83215d027c6dc3440d4918348f9fe089ad712ee23f8776dc97cc4ca684f016c79b8b4570f739d4ac8d595a37d953b4ac2adcd0a526389993096e275a90776d71453c0d7070fb545a72f9b9bbd02f76a9d733097b227eef9a9a9aebfa2053d550735a7eacd16932516f40463d01e10c78430ae7d6dd49775ca66ac1bbc723204756aae4117f73fbcf3c956b2fd70b5ff249e925cd8f4c6a3c6aee776be0c6782967e1e1f60e99eaf8a8d1cbc2af23d54995e6f08d90c1469a1198dbab5e4813a6094e9b63b73fbccedf6361b0fb8dad3c69d9851f6eaccc61145deff6d8afb54c86bbde03aa2ac7e62df53fe9f6e361150d629716e9f77f2a74d5e90b3bebfe0a2ab4f2f0c44456a963039644e7826b80d00d77af962b3657a2fafc66894708b8ade6da02b6922fd2d836bacbb2726ecc6754e92330b7bd03c6fba9294b3064ac067a179c089f0097759ba272a4df1468641389de17622fb67e5eb3d05280b50d03c32a00d60177d13d9ea0d33aeac2c6cece4109a99d781a8f58c33b29b8a1c32b5e404b158b6df904e0eff24ae420a249721bad26f09f7cb7f454dc3eced2c0c14ef38f4557a9a532c2cc9aaa060ce5c9fcc1a1aaa97d0319bcc6c1c0ea17a7c45d1ef826ad1e5feeed56e9b247e11e1d15b1b0c10d7daf272aa09342c19f99f98cbcbe165e1f33b69ba7961ea7d3bbfd132325741b35a976592068abca65c327ce0d6a363bf4c8ca5f1346762a32b89f63207dcde5e24f35276fb86877c2395d2ec614cec5b2eed573705a47394786c2d262adb95a6ec3541961765492bd9d9cb524635e8ad98b76a8353defe0776127e1f7ee4123418cd5fc1739b7460ca1b6b9ca67b25678810c95830f746277c4496e43259da536347b34cc4543baf536eec2029e7df28be321965fdd683b171ee6c66355611f8d30df7183d3c444d7908fa44a877bea6bc6141e5f67ad82ab7b4bfcdf516a4d158cc7524869bf84cdbaef23ceafdfa6412c69ab81f22f46a521273374ef26850b422e359851557625959b6b2584e13c71b2742f592d82a8072ea8dd2fb8d183066973546297104d14d60b7dc3cd92a27b0423fb31f70921eec61b4f96e5729a119898cf891cc25e70469d7ca906499851c19fa11f3a7846487e33a09a76338305458d6f94d928e015b7a1cf5345b1fa36e17f17785071105ca23fb1b22c8aa304e3cdf807d97eb28e74939e33dc2f6e3982281041724e033fea0f691049c953ec31b95b607c22737081e7d8b622b23496dcdcdc9702dbb8eb03f31b899da9cb738d9185c633eb2b0a19b014422ba83136d608451885e9614b2d2308ba74c0a697354a8e838b2fa39e5f89bd770fc58bdb0f9657ed92711d61cbb5e2f9f9fb14cacbdb8e743f9e147738c969b76f472b09908c34e61154b5c655e9ad92be6ef030034868d5c8d6b81dbc6460be8f91bb7fc8c16d3ca27e14adc486a54090031c96d4ce6b1d09fb6eb98f1787d1d33b2b744ea8bd9c717c2f6f47b82aa7d1163ce6f30528d3d662784800c2ae1a948be310521a6cfc2b8e156c4c9bec45a0cae94ec39d3b05af4660fadc9dbb6406944c3b1c0a080028246e71080332b75337f53482c13e705905f149b8022a520355b4b8480d23f4113c934cdfaa33e95c4c4141cea6b4f3201a31482a5361f63927de25d995f15f1f62679b41773e332d4002155e072756c1b79f8129664b025f945178ff3b9e8abc8e65caac04cfdc19a320e28ab922e0016a90078abc40b7e5f46d4e356934de736a71758a1c1a11a35999996750e341055b6dfc206e4653e076cf153b9a291979925194e9630c5ec93c17a29f28f895c956924d36446aa425c6a8cb1fe229aba5ee86d6bc7bbd26b1fed01f1154bb3a175d27178b7f3cf26c05cabda0abcbfde7c5bb4828b00b2cf513842551f4ae4f4ca671740f6272c6a026e448a9c6d3fda60d2653c7736e0915a5dcebe7462444700c67e4d29e261c1e247c2bb6e987007b336a9499370c1b9c6f200471c837b468566361ae7065c0da5cbfa8d7fbd21098d8b855205ecab946eef911eff7e13ff59efd9bcb19e1ec3f57e28af129910bf606c7646d58819f49312590dbc69ad41fdd7f49b0c5c8fbd654d237411ac5c8dc39350d271918ddd6c763369f5ffd8a47ec0352a12bf881f39843c342b7ce007be57c6c55ca24ab172279296ae0d2d3c56bd69316230882506f0f08613e57fd93fcd84d8d72c567d3a2896bbb71454a336433723e7cb6211f8718bb402d2448b7c6a11cc43e29963070dc6165876652881b5fa84644697c51a635337ab3a0a21a6462a14ed16cf3b3775ed45b6753bceca42429bc399611cc6da8f23201985bca697ef230156484339341a50aa93ab2a92a66c622d9bc40579cfe8e853e40ac6e12635425cb029f4511fb8305102a2233931e82352e669099fda476d1d5a9b76612be1671d5f719f18a7b85883e372706ef6ebc661da43d9b6246a54678207bbb56438566d314f28bedd3745259a0995b5c656126b9d8cc33aafb1f6171d9e1e147762f83204e4741380028ca9ac715810d761a971187c57b7b0b05be7e02566aa364c8298376133cb30f020436c912c3425c6a4af886089a7bebb0675898bc6615fc0491d42af99ed9185e03a8cd35be35bf51ed69f34e8934c528f52c33986035c5c613f0349c6a4c6b4611cc68b7e7a40ecca4350cabfd41aab84f53e488efaf8cb532d7b854000652b295872d6e395060a02478e8f4d882f7d056da51e11048add8b5267371f4bab3c0ae83d1c047b8c04112f53e513a124db4c6cdc9cf787c3f398c52003612a28c7822e9a813048117971d34d3290fd4f030c182059d13dd4ab9aa9f41a52b539331030c9a16ee5913003a9a688c9b418f54f49066a5dd4ae970c84ad18090795e716f2e10c04b670f12267f8b0d99e814edbb03298d6d64c213acbfb773e60eb9492916f47f571172ad9f8436a76fcd02d198815577c768bbfe06b34c22262ad7b01ffb99e66e119e87816cd3deecdd34d60c79d8136293252a4332ec50ec166a0b393d35b1af3c850cd5f4763b11808c63290c692b5ac8749056ad0b433d00827aa0756aae5287c788cf8e9c1286e2efa7830e0183dfd4ab34a2969a3d39dd1be48f4f2feed6805bb7226cb40139da47e4a0fac2093fdbe709e0f9e87a51e4ec0ec1a47e1dbf2fe2131776227bc90d747a819085340fd9f20bf5d68ce449cf89686c53c179331debbc918ef5d937dc273927706222a0fc3ba08daca402770852235056c6e013650e3941974db2d0ffed105f70cf4f642d9d49cd2dc84b16a1a5471309d215dc842b5d15a6665069a3063f73a06fdb4d523037549d9e4a3b32bc7f142cd9d8b8e55e7657a00e1781439518efd5d9a8170f465a08899cd9d93be9281d44751cb38d56796143558b32003352302af6878f43e6c2fd29681d475d7c6b081c9c6c8bb5ce4d6aaf7aa1981d4e9404d486f4eb2bcf45349734b072ab40931bec732ed0971d388aaac4c0f30d981c8a6811fe46d58dcfef80e843acd39a8fe8c0c538914c5c60e34679639f5ec8e03bf4569c90c3f96da8ef40ccb22b9e7c5b7543f6e979d1e3604312f9670fe0fe86dcc32a392f7827fde7f1c40da3421633ba36da100ea8b2b8e137fa981b66c983d66b24fc915c8596f29c696cdfb9e8b15822ce1357815716785c034b8cd192104756360b35886f0a4950bb7bd7e650d3b9a6c8cfc0a6722bfe562b757bd6f4d83cab7049939a7377b2c8fd763e07432a6b47fb496755e1c53bab39ff7af50fafb845328eaab0356096adeab2ced685d167fb5e5888326639013e7ae85d0987e476648ff757c11c0594328b4c8d46354031f7228634ff4f85b0d4bcd23406bac147f9d707f177e94dd85912060e1a62092162c547e25547cd5bcc2cb56740cfe913da1ff794b9d2285a7fea7a60e0eda1e49fd8b7cd07e017e87f8221e95a19e2c44c886345b4ce8cbd749e64911ae8295902ef39e95253dd760e472de9f6b630704bf5adc05a104b71f71773b746852baeb9a5cfcd32b2c5d9a4df999a37fa04912127448bbb8b198bec118b18a5f1e46d283c651313e8d5095ca89e75323208ef4f0701b7a9b3bcd46e8b94926a242c42ee77d294491722287d833444e4e1b6b0ae84900437f3e74e86807660547655984dfa4575b0fd137ed26e3dfb60b21a9742e1fe1873c08993042bf055148e8a0c3342bbc5ae7f7f36e4bbeda4889175e34a316c370ea8bc017196ed9e775a65c7562bceef3f08879d5facc84eb75e314abc7781d07c61e0e2971e9aa186d790cb36853d4b2a26dfe8a136d9b4fbc68233c5fece5f820a7a8aeac140092610eedead0d4b751678a5e770e8fb45787dc28074c27b04a301bce2e629089e28a130e5971da7c8f25ba5ad0214d7a69ecca30707b16aa1c463eed7fa868d3c33d3e25e81c7eb4406e40aaf652559be5f916c7ae0c452709a3ca382aaf43220ebd7b644bf8987017cfcda449f09fe952f46fba14ff992c457f4d4b548e5d1f4e182b01181350dfa1156f3815d54abdc51eaa66e2875fddfcd1b410fd372d45ff4d0bf17fd3521563d78792b7bcd020c1888744cdccc3a971d7281ccb4f97c0f5e1dd13c511e58321d27421fe370f5d5eeb4676a8d6b1eb16e257c90d0c570f257797631ac650ccd50535630b892c5808df848f4556fea12f7402106759c2dd847466745eda18ea2f81ce8198b2e328625a19f99f56c6fe26f51df4917e41ad63d73df914e5073181f82d5782684a1cdcf23f7845046b3278416cd577bc5d46570453d35d1ac38e2662515d3586579ad738443dcbc3af2b0b4d636c4c60c68793a913e83065f0e3c9dad54e844f3e12ce89891f0b4324dd420ab77181866fdfc145f00214b1c7e1cd59acc2fe3edd8d9049cd53c4240aaa78f425c396ed089365512e3f26cd39165f22f76fd8949b84e7fc54c445545e5923113044dc1cbaa7fb05c95298aafa822137ada2d53181b4304ee5d93aaeae236049c84b47b3c993a67b3a22c9f65110149e8cc55031529c26db314034d82f283ee467182ff22b9415f9921bbbd554fe6ace749fba4ee9c4ad92482a83984300da631ab5be9b92a5dc9a6366b47d4e327e5ee212292b97c26d42e511d71c2642677ccda58bcd8506fd9a5270b84dc7a3bc4cfb3ac78fb19a60dd77a630c1337d55f52534c333c1ed6d63f49a47786add01eb0ae6d0ff1a292fd00d881923dc161343d3e503b68d4a169dcb14690e68a9f99a51388ed605ce9fdce875b18fbd3158256e32f5f5e26d9aa869749b9b9e847482358943691626b6ffd24f420c60d3ac8094636ab9a3119e6e8d3905d171300eff30ec032141d2446167922911437b633483e1d78377c9689c7117a243baf223dd2c3ab6c22940bb46a3c0792c68377680add748e9f034c220178f3d41c4ca6b1b91b6e6e9944067462db2d7d3303ef8b1c7ccabd92c05332353d50ba2dc3871311e846683723c7cb8b2f71f5c91b5f80563f235e3fa4a1413c2679ff66cd073d8704adbf9b466624a1aa3c6289f7223a79ec80632bf8b770051e1bb185a22418ba251039a9effc32ee907869d9156fd911ef1d78c309c431314cf6ac42a666aaf0bbf165ddcd01994c7aafc56e1dc79a51db0c791c04141c9b9007f8f06817b10d8252aee8650bda2fbb08da0b25fc292df7ec8fc84c9ca3a42b3e5d46f07a8b41a349b95b8d5a9245c8f60361b9433720a1968bbc4d9c5ebec3e8959dfa7463263e978f68346911f3e448e2bb5ad4b72ddd990126e0e379ba4efa768e27e4f0a1ec182b914b62b1c6d33baee74e48731b3b5c1dca3b1109b51ce02224d7d77bbfe689cd834c8b8f206e5c60c5a4d1c4c44c60cf9cad2e78d4cf0c20cc902faaa3d3311cdb2d2a072c2ed53652b0b1f298f5d62444899b7bc7319704ff6960372f2a4bbbdbf09688cd04f5d86db533d30d8f278fa39380c321c258aa15814d0103748c6462cdbc28585d04d6879886a4b1da49bfc19615e216b5baa36d86781319f8a2d4f3ebef90a9351b7fd77e2667d0670d9a5714e5d0fc289bfc4585ff99d0b1b697d0ffc7aa433c0f72c5424e4c3d2fef3589569421a3eb2a0c9911b09c9a10ce00df20ec4355d44b609db1cb655ca320ea072f144dbe3b5db150dc0c37129df219a4ed82595b396dc400848b54271c69d1e2e2a4bfb4009e1a7701db8502e9abb606db5c970464fd72238a9da71e1af40cb604b3cf90fc5cd7c955e130b9d42ccffba238055945207b31bbe28457f044ce2da42d220d4616e2456bedccfbb8940e9c8b26f5c4a6c04153df172628eedd46513cad8e3e2c668f22fc6564f521a22553e222908657283c8838b81dd653985d0be0c273712295fee274544a64664b9117a1975b202dc87103fab1296e5322169b2be82ff9d68c6216bb3641489e375eafa420c419860119d5c61be1b81110d6bf056a3bba59e4927b256ac7264c2eb4f1a88cf69e7aa9487d79a7878d426265d9656f29713cc32c4364ec624914fda5258ae6e4431f6ba9f59cc259a285ddc1e1e4473018195a368ff1d01fb35c766832c74e39615b2e56389c9e5b0e5b9cd925c91d32908c53b08ea477cf4e460c6cce9f23442f7e71df7ade9ba9962dd91a05462f0a5322dc8c82faa819f2ff93497763069cc46dec556ffb40e107136dfb42f9dd3e0448d47d32bb1c1f80244cdbbcaa90e4cfce146cedfbc158962a0cf8e4c4aac957cdc8cc8180becfbc3524983a02d4efc1a3aa90f2800b5ca3825bd88fa33531f0b794fd38a4cef486ae9e53ffd71e3fdc81b353984d768e66fbdaf6f9ac34cd73169b4a3c345c17b772e2253f252e908a2733413779cb04252e8ef4fffadd13b0498cac104689e1170fc9608baab04e12a4be5e0099b2e22879b2af93be180115f820e627f826c998455a5a693b38cc96a9775c808f30b22efe2b785f1109247707040adb191f5cff2d254fdc2e396ba03d2e802c1a02d0dec0dff8c0aeb8b3683f54312de9240d7d151c6c3757de70c65196e9107689c60bc8fec7e4c44687b7b5c54337982a1de56fc4891bb6d9e54fdd34a35c1832b209d00b88b1d79553304aca183d9a310a4eb8069486c20c857dd44ab0c55a7abcf0abb7e434f5d65733aa8c5e3e897fc9ed2c46271ed5b0b76535889ab7c66786a369740a59d8467c67c07d074e4792f10c852b0f1c8c60be0240599bac95c161c866cbf8fbfe1e7fa6bf603bcbdaaa1fcc64181f0f0817f1862a8861018e8d7c16e01ca857f0be11dec967752545b708d845951d02020f7f0d664e1bdfd0fdcbe0263ffc0d8271f92403c1b3897da389134d449cfb053deb1e8662806f3a9193335f1c8a15346149cabf85c3807e9a91ab05b049804d2609feecadb70cb316489ff88d81fc48736df98e921e043fbbe67b2a4c4452e534b1b380369e348d859ae6a227f00ffa931c4291fdc0aa216a2189d265a82b5a708c34cc5fff112372443ef05b67c7f301bc801deb25bfc0cf1e74329579da7ee827a374194fd4e60a4b1f79b73b1833021d93a2042645207d45d7ae98079536fff3fe02dfaf37b8e47f40dec883daa21fb379a093672ea6185a981ed445a8fc9ba92a4cbfd552b61792e6f981b4f09bd14ab901f89ac015f0ad1235d06aefcce82c0565d5249274433bff428dc5a63f743e6f356e643c5117b4a16e80f185e1415e4e52b02c4f6bb722aacf1147dc356f5cb70caa1714c78f6168935116f4b1715bc582ecbab831618c0e9d2e52a27902f1715a00fadecb7588cbfac7bab35e0e196d5150c3c696401a4ecdf6954995ff096bd1e819da471e1079d90a93ea242b9fd694d0f2c746fa6a3095a822f40d373a96ff2aab149b1ff742b3378618c036fbfa8472a3a98dab547dda831ed61657f08d90ac003397b46618dda3115638a9d676b1f3a37034a7b24d7bbaf143c514b1a5b2cfbcc0f2dc3c598d83eb9f66667016f5686e2adf918a5f3340d29504db12ba0ac9a93215fa2857002c30e9aa32cbd431ce8c7fa6e4f062b380af361977346ba4f72fa603768b977987dd5f1d7df6e9b2cad84fad83976efc659bd523297b1b6b9bcac3cbf11c514c5e977986f13ade87ca3b2b0fbc6adf92d14c9fffd2ffc5d52cd7cb4be46591eb8a544dac7747b49de52d2c02288ebf7184c95e6ee94d707890a4a779ed874eddda695ee8855ef625b56107f9c7bba54e471dda6a79f6a9506a9747bd223c43529504eff27f5f93b642ecf0ac5709abee84e55d6136409df8989d59427b52dbbb77127bb52fa7a84356aa0bc9b60716ca5be315c25d21043e83637b17a2c00c9752cdc9a455b65414ef87571e4904d38f592b9489f2f1bc211be9986a8b2abcb5d160b92f916ca6cbfa4eb3f469ea69f34958dd47a3148ee43ff0dcb5335f2070d046acebdb3ff4bed23a41e4082db2d3edd89bc103e9e22f241f4ebe637d8124ff110d25697656e4023f9336d5734545b9af929b23d23fb92b074862a0ab470fab0bd57e4a84a784a8ffcc32f6f3e1eebddbcf58383aca52f8f89db47d0ec89d06563a978ff8912069b301a0cc5587102cc0d4eb2901cdc2d018dbc00715b70c47017609d5b87bf737d89d10d1f898e83fc974f58b8b11628b5b4287f3278e246ae3d3f8988c6eda2354376446509e3e0d182b6cdd1b875e77b896de8e8e457d422d72d8230fc1129e11a2296047083915e1fcf25f5f2eee607a1210d34c7544ea11051748542690e21d03ae7e334d8a5d71d8e88947965792441d71c110ee475662ca665ae12bdf34fe4754c40916cb22136e97e7462f2a0e229c3a15f7f055eb088d3aa5576e01ff8209a4e559b667cbcc430643170d018ede8053895cf0f9fb76c2c0668d47a0f9eb9302d6e989d7be2eb233747982887af2342eb26d7c601824849b09b45bfa1d4a2a444bb2997904354eb1147beaba7b91f1dc032d3446464d21910a83b6073a27c887cad503914de717ae1e53c8b9f23fcf55d1a0d8a5202f1cb625fb224110903a5c7d4d8397e98ccc8950ad0df5027ac7ad53b7c9c0a33bf1797ec0c1243e24350a0fd2db8e8ba36625eab1fefada0a67a4efab48eb5b0f0b096f3e5aecd33eacf4b29fb5567248435eb1e96342031f223b0929a6f72f0b874d1d105a585041f4cb46b0f7d97ab8231dba9a3791de821b19ebec6b771641bfe9d43789b7e0996f75107c9b9412698db807ff0fd7b2878322f2771836877de458d961630f5d14f5b9831f95c30cc4740d8d8141463fc7064c98e853d3586862ed02288a805ef4ade92342224497724519df868886fb2fe9e5cc59f9e65f38f2b622e9d718077356cd47db294e28a28acb0ad3317d5a21226bd22e1c84f96c5b1a34ef93b6f9cf9a4206e9f4dcdc87955837079e9056239d6ab72023acdc3b1c271ba8450c741bb98f6dbef299c6cfc5267885e75373e355ecc32679c32d0307fe7b8ae641a28e77bb75cb8c2217ff39aa599d94ce138c76057477b9170b49e1e074b2d2dcc9c28c46dfc7c94e536f03b34f3c303b7d281a7a97eba51e601fc0ff18a38c5048d60121e48e40f9174c7bf53421f7100ecadf246a1ca694317d3bfa4e9b8bee532b4622208d037600039e4a4ad7bb3bf795afa42285c01ffaff59d28746b063cd49cc2b49db8a04bf32bfcd2150501fe06a16e560d9dc45ad20fe20ca5b4a5cc64a6a7882f1d7ee126ab221e48e3fa224812900258f6013ea803ae60576dd26ac43dad254060d2991baa0082b4f65e74638428aaa361bceb9f893fde16289d89941aca19e922d0665c543b876ab9967e05860e4e3790b8b6e70e41de8952c8c51d9f21241990125d2cd796e1814c35a1f7422106418da5871ad09a010996b7097b9d72190d2f434484d4e6c836669342ce8ccc0ee926c59abfbcec4bc59dba41c318875468cc8ba2259b32971c8e72aca2c44f85fef8e2667630b2b7322112668f12706fe3213a0e6a6af66b0cabf605d762edc1b847ee7e62349285fbf115e788908d03a6a3a94ce247b8dacf6d822cbc3da831a7da0b61f11cadd77018bad4c3293472ae18eeaa693d225092936a3cefc18261150dcae8e6f6264482ef1a6b286595bfe8376e71acae24a98c316c909e3d625dfb524745d5e3c71817ef42c854d590bd9c3d816aa064a59c2445a200455760b5013d49d231177938f2a4a53989b69451239a42ae71bfa0d0291bd096f344128e7067379117f4fbf318a328e3226970388551c3a2c58527b909ef95b13cbaf35d5b8868643c772be98fa584c99fa38d2ac74152ea72861f6107e2933085ded0b4b03826337b960612cc45684e79219b5a34183204d8fec83d64615f7bd4cbbd00f166ffda771edae18c8f13d4d81550a7ac804091e61e00fdebdc71012f8b522f57e2e52bc1ba69ed381deab6ffeb10913c05b81777304c49ba4dd25c53882dac7090c669740864e8fbf152bd4442fcbc7e391ccdda14f5f45f0848e4eb03129e5a53f2d894b3f2ed68f687beebe23d3f1aa698018a84a341c65fad29f1ee65f56ed8d8cde09f3117b49bee0d17f1846520a8729b14ee20e7e136d885b70674734a962007a8d6df9802f0d6506c34b8d10604b6eed1895d9844c706c78582d4fea35b008c2085fbb133d629edc4343b15fc0d93959960fba33c01df9e405b72c960a8d6bc9580ce1c38342278ad176c3f0cee3b4d80e832a9327ec376b9ecc57e7edc5ab3bcd65b06d83da5b90f8061a3adb47e3d0490b4d1455347be0551736dc992b945ada4f5024553eb5356da85a846d1c82ee29616062885ebe895d17aa294d8832c16a9f1d6ffa10f75e25629577e4dc7d2805e0e14a89626672bdb14a2cbf7521e53d1fcf335c4d1cbe91451ab175f5367ac997a45c3aefb295314a9c07c2ddc8cc955600cf31bd96a1ff7b14db4e4668d02b9c32a7368bdb73bb68949d7d62d9ac87c8e1c19d1454de7bea5ba31a44fae62ff44ee9c9823c9fec57f6ebcfd9e1176b12cbf2d80f229f8dd774035033399791be1a8b4c084869096a10db7a41c2770a5ef2e24fa370f57fbafc45d8e9bfb81715d5206140ce077a3433c0352a3e85fc2d0e15e8be2fbd3c573962415e10c8a3f65c33349d2ffd504f8f028659009c1f5bdec74afb884f6a3eff69b4952d7f7c3cfb05a995ecf47155086b581a292fbf73f3a7612ba1c63152b205b9971899cc80cc71f6464ab7028863499eeb8aaf7a143c17884bc7720428dc11c20dd666b73491051388274971cdb1e2fb540b9d5da4a9ddecbc24418683de22b2cd09e7ef1d36a7996f4026f4351cb0cfc6e466d0af267e1096cc119f241f14f8a609729d1abec6dca67b6b3c237260770bfd941e842e4eec02ec202de718ef497e92c4beca79857f24d495b911924054993d6c364a40f8cdfe200af52280cbe3c314bd72871472029f40de46a383fdb7ee2b999c6667990455fb6112bfed84acc1674114fa991045e0ead7e60338ca1b24729a8375da0128fb891e001f806a2ba979bdacca822a20497bc3781936e58c64603393f9870323a90c5c98327e82ad558530f3ee13a810b1f0dea2d9b1b1c04a1dcf421d6014fa6fb1719fdd55493f05fd1bc029c8b3ee42efa40ec3c00963d9fadd867bc1f04526c280dbf0503d5379d1f9c5f75c395b44382fb79ec9cbe418732dc30b4398b62b52b03ef4f3d7afecc79df2292a11c7557b55b1f580ae5fc4a6d69f13628c73ba65ab6006f5d21e80e616d4115e515fc40f519c2efbd1def2f0d0d3443d9d4b7ea81c9679c396b11e44c9eef710cd44c4225aa90bdcb3e64aea8b6dabc750282470c7c836279a360900ace07b43d30085989fe5267e5ae53e898243ffbce5406248c5a3bc78c9cef742f388a7ccf370298a8edab018b82c0a467af0fe575099918253bc92ed2c9de5e9019ea665c692c4c53227d3f8285b040737f539e98f62f65610539fa94e0c7d1cb6dd54c62263f3cb65122ed428e5896e99c6a0b51c73bb095c100031fc590f322b001b0f695586f096c536b11207881c0e7d4d10d58cdf496a76830c0e1bd57e950c6a1913d1d475cf1f25b8d50bded0bd994bd655eae43477c1062fa85492fd3f7531019412b89b6e089ae3794046d99689192363fcdca43946385f3bab4c8d4d0c3a3f793c0cc3dccd820662b5764a0f886874ddcee6b1a93877f2f4f79a760b5eea5ff384f0577e31bfe43d85b8b504d276499e1e47e3fd3431dfcdd0f453da226a80bf4d3bd08b89ac46f6b8a632fd69695bd334d04a99a1b6dd1b874cb46aa9c96b4bd51a293c6c0d240781e8c76a96cfddd9470c7e5d748e58c6893d8fa25bbecb115d27957484bdac55138e13cc28bd93478961abde7fcdf9981021e041c5721286e3271d4c5b18565b523065df28496b6b353e8a13e9066f6d91cba183afab0172b89da002209885886044923020297d12d96e89898be061c0c6445ffdef189a0090390426b317dc8500a1f02459f72f93dad98b11b495c6b4ecc90a060a263add3a64e8764795d3572e3211d2ddfe7d61320ce3d0a1ef85665b5b88621f45ae68b1a25cf204287163df31bcd7dbf06e6ba02ce25b7c2697e11530982b358aac715a0df15f0562fafbc2a1b631906596057d298c82d3df9114e345d6b3d381706e2fd3875edba2d51a0f06dc6a3b4830f86673b101c4aa1f6a340a9541d8e9ef8f87a08892dba95da959b9d31af677f3add39fb7e36227e745a36ad5c38ac30f23c22bde6f8329114687ff1aa1457dfde5ac1f7dfd8f7d7d8edafde9d4b85182545a83154d3548a3d0a8e4530d5ba48649df1178e677168870d84bb214d1fbfb651ea80093214a5e24b61e54d126ab94e238c963838ddeaabf874a55b8231d66fbbb86596e2c5164b0e4e1169683b86d2e8717dabdeffb7ea321cf1935a0de13f95a64752e4f5c05456c6a9a07b302aecd5eb193a0a543d78a2b77cc97b00302e389bf7bca4708eccbb3548f3c0abce859258fea4ded3320b2b73574755f919117553fcba34a4d38d32e1cf9a777d6dc89338dfae8ec4b3dd7a5c9f734ee96b9314719022613a24f63ccafef38abb9caf1a7e1d3c7305133fef8a4036a069b655b460c5f9dcecd29265ac183cea7ab261027af6bd59c8428fed0a2d7bd5f6c8a1aaf368a531df352382b14f37482ab125dc95ba61726a9144a51917930fd7555e545be0ced49a6ce6c2456485073ad4ba63693489a898686c2f11d025015d6181a3db47f0f9a94e75d5f9386e5c4da95a283f515fa4fdd46795bec65c127f3a8eab5f9fc890336afb65131bced229f3d45f62c79ddab2496dd020d997023e0ffdb5a0395bc92492c23c5a9b845cc7e313d3ae64b51f7a76f5448f68eef50cf58c7a8df542ada3ba86bc1e514fea1dd50b55d4761148a55284eb05e819f50cd5eb495573949c1bd3cd5e14a4f8b2bb510ff55cb35ee809d0cb29e12b2a5b7b2a5245a9a92a5471a8741d7de653c294f3f490c4418bbce0ca40b50110e7fd2304b164f95f4a3951a89faa7e4d26282f70021db2fc12c3c9a17f1dd3c34bf866401fad8038c45e2385ba04d84b87400ca6613d3a2d031880b86c5d3a7b9e446200c45c36cd9f300fc4710d95634f9c5e1851f5754028ec535cf1e9850ec149cf13759590bbeba48fa6ea2103a588a55295c6594347832aa74709ebe5e556c503de97570e56aba317e659049a0630c091bbed66574c049a27606356d8963b3226b97f7853da1f971ef28527ff772922e61b0c84addf6c2eeb4000091644b28506f49ae22a7e6a63771cfa904896c6a06abfd7a016f81edfab4b3d89f955a42a7ebd0a7fc2b95057119840e24ede922f632326fe14c01ee81a3d15049702f4387719497250114a63328fbf119aed943678ba098f13d64a2a6686123e4425317f84f9f51c1071aed29c8d2b144dbdc7204a1403c484262ebef2f1c50691931da42fe196e2141c3f94551d53d09f289b69ff19d2c7e88e14651b65559512c155e67b755c2413653bcb43fce7044d139a783438c8aef2388c031b332c3d93c4e989b1748e131d5af106cd0f6966d85dcf1593737b549fea1ecec6bd288b756d176eb2b1ddd9115303bd9dd2291994804138db2d4c91e1089b6272d70602329e2e70876ceffaa3228080fbe28050c09c91d12b94d3240848fa33ea828566e1bb2e221afc76cdef0f246fd310de48504aeae078a08d80d635034a351f0e65106f777a253370bd816928d78b3d7c90fddb9f0e225d35cdcdd8fd2d81143d6b214063457206b8f194db0cea6db90aa99497888678a857d97c1ad5ce4ae768b48fd3e49c166080523b7c7079d0853909308be071dc09ccc29eb012c01389743bd95b3645dc79c467b4ae8fe8e401921224e0e14dc408d2ce3fb57c264fab1000c6c3b4e6754e06a60f03a3aecd6ed7506738770bfa6bcf53b40d7ac2e15609c55c4dea05be963cc16dfff5849c3e2035ce54182ed3b75fbce44a8344484c4c5f1d852d57710b894725d2125b22bea079368c40ee9ba44f3ca2c69b0274360b088a0d3921d106d817619748683eece7dd1616b650cb6b7ae7570a2b0df23e4b82123f8ac2514ab656743e6261d55d029b330ae0349c7bcb0c4d50bfdad9a982c9a41e6f21734f31e43c59a96730dce0151d11976a75139ef3e88d319e7edfea008621e575c0967bbabb74e63dbc28e3255d4eff61d30d73e934b5d4cd4183f9435c41b4bd8de0d4692b9a58ec9ea23f33ddb66b9b53ddee1cc653225def33121f167bc39b1921a617e7841be504da6efa582aeabd447519eb903a3635f07f8acf51f0d4b3c13a56539e59d74c07b52f7ee9da7828a774aeb9ed8bda3c2b7cf18098b2615f11fc4d0cae9fe23fe8f39403834babf1ac9c9f62e3b243d7e4cfe4099412651db7cc2c378189edac154b07bc38936b1e66ff5edcd6689695e7394b498bec9b67a3b74227f2ef63d706d7e8d079d8f7ac9513e63993541a1a350941f60020e67f3f451897c67494133f3361e0d2fdda3dc15ebe2132341eb4d6c6368ca64c4c96d05ea94a5d4d053a2dfd9b3303512d2e79f9d4cf12f7c21deec44397b9c93c166aaf663b27d5d8986d745e34711f751d2cd6aea2fd7757fc6e0292646b40c369413980cd3a2dfe05655325062eb6c2c4cb0abf2f37e93ad4b60f0439681be53a04894a0451ac44759d8cb335ca7fcece511f5a52ab256952086aa7ce74eda21fe21f576ef1f9202b5681359b870f35407cdcc2fd0eac0fe82a59ab3ee4922545fa203455350b990859abb975124ef501e1eb3a816cbd3dfb94d608879f89776fcbc94f652bc13e708d82feec666850c29d805a2cb93f533bae16bd49b1f09ac107e09cb665a6179cbfc7d2852aa171121afdd2d5f899886d0910c4af9d3d19f0c405d41c8dbc980426abc13368cfbbb516a1b00c6b92c9c10f5e0df6b3339db6e6becea7e6663535206821ff72bfbde177510039211bd3d2c4a763eb93f709970cb8fc929306ecb77fb833892b2f5cc0f9b8c652d24d984847d11674b47b8d387d9d7400d2456f61e8661d8f0562b80cd2878407a0c9df9c8c5ed87ed689dc7ea2a29241b7a818046233292dad92f331f51d54d93a9bd2ad0f1347b0e3f9723c2f42023b66c7832131d75382ce9a5db41314c0d458f2a77fdf59eeb6865f3fb60e01107fdc209a5b3df82559638e5c36b83a92090b5f9cb46c1504c9bd8913b337106277ec865189f1e04562425ebb11232f3b85b561764b709ae0189a757d70c825f628a91da040dce54461bd48174da5bf94493724b0b2d0981624863979cb8021999e8ae6b986c13b836561966664cddbafe3129ca0f49f86a014ddf124cfbf64b71b7eb2903cd0847f56f6168c4133505af250c1d3a1c7eefab67bfa91ad3f15a6e4a626c983720a70eecb8fec2b7729fb4faf6d12ee3a80bc62831fea76094c75d827008ecc0398210d4ede2618c24546c502666b55b68e420a1c157ac3f9a31374a1241e20363feed0b279b631902c1631ba27912c8b4b61b655df00754c821e578a67f9e9f4c46702f4b11ab244942fde2ae86fce37c78d7cc3b3d9622a0be06f0c370d6f883762ed917ac3aedea927ea9d7ac7fa11fa345ebd52cf502f38bdf32a68f0f87e2cd572acec6383e122fa3885c62119544b8e86d353d436a0feb9be3a07197eab0e7725a3190c201d55dd2412787d664094d9c4e4357a55353d8ce30b4741f388871e503e185317468239c6630c22912f0ed3260808df06f7095897c40fe3adb96f9048049fc1c66648515c283c8626c893e380242e24c458b94315aeb9f2b8371dfe295746ee6db6faa9c37e9f456caad6b59afd53ef086589a5173f9b09be8746e4e08f711d87c157df7b12c4622a210bd2d3eabcd7a22cb9f513b8892a661168c046167fadef5c8cb4122ecc73ab090873b14af0ca44070aa395f2c8be499bb4219eacae0d365b516eac37ebf496e5b2d1f0b2719161f0c3d22adb9ae7d539070e3decca7dd08a522a37e98ecd86369505569f086a89e57bdf6a7c5ca707d7148e3282e56333dd930f825c4cbd3fc9d06e22048374013be642275223ab367f56f4b2dfcfd32c54819d5ac9ea1caf5216046bf9962c04f2d1d64d1044ff73d9e1881e840a214b14d70008b9dfce79a4bc9a6896d42cc0b603877b12127051cf8dc0b769ef973770a33b693e05aa71c06e7e22b8466ec6391b842cf2ba350413040847834d472b2068b21b38b4708c4393fde84ff0cd4bc305ae04e32869aaed8e2dda8d8a1cc2236a10abe624168df46209f146dbd6c024959057fb3934e31b3a1c7174775c4870f2a7c74cd4723e71709de817ffa3d658c17c72412eb36a221eb28c02f804b494a4a30d394bce47225646f485d155169a7b373a68c773ea793fd197c5f43140fa853b61920b0ef21b25d59bf9e32a55a444c93d5c3f114c51b81d0b68cd93b89e28e347c4dea65df4f21c4b34b493a2939074c35908737bd2226dec793a3d609984a6a50bffcd2a60662b72f257949463012213f58b5612ad6b284e92945a2dacb2a43ed52d37ac321e5fe9a97c26ceae5695bfc003e2b5338eb19fb7828db357594c4b5c299862a58c668bc9f8c9ad15e6ea25d060a866270640a08a155ec673ddad4574a32db5690712d4c03f2071d232b88aa155513d389cc29ef1e54a7f326be32cdc21d73f1a7790766dfbf7f33b4e08d4cd75f08e9ed3bc544ed20db153c86c8bc3292f1ad14e6f1bcc9021545e3b956d74a5f824daaa65d38684437c6a031633d449e3fd54831cd1061932b7bb578638e5373f44878d2004c3194656600619311b06275946a2abe5350c1e9f1adf7f6867eeb10dfc053d08505d77dd6f620211e8205f1f3622fdf08a57787dc41e9fd27f108204a52f8c0648cddbe6a5b0e1bcd842fd68aa37103bfcedbb5d8ccf83e0136d6f976c2dfc0ed21b1dea1df1f92362c444074823a7e3a4828513302c22d25876321438de2311ae6615e266e860e9de86b6def937811449b1908639225428ed89996b7a202ab6bc717b72348e6b4a4657d911a69c287d4d5decd28534367c790d2221b0f49a3e9127e88a6fb25f8f4619749a8c0b663905d0a80b1a48beee7988d2374a5f4d6998d112150f19d687720c8f0f345d13ba9cd74e29a986e7d19c6a25ac9510240b99f6458aaa1c3908ae5de0bc693c98dd03cde9ffc1bb0c700b44b792876a39a668930a89983f7306e1c210e2d2b26d7de18d735b3f8a3e996eb43d657dbb620361a28965f02dd43bd8ad905e9e2aa7856e12b7e010eef4972a2546ce3a90a28608f6f003276f9c9d5ddf96e3aa215c38a30a26238b4f91cd8ef8e1accbf39ff370eb08fa04a52d0154684c02bda3269904bda0d2e7556db2593bc6ade04ecb41ea9b74376851268baf9764a0c199834c26b14c01c8b2ce6e31ad18110c3c48e30f845146fbd00826b49d629f1c43d18662bf997b37ab342d92dfb277cd2b38fc1b24307bd5e1043de6b1a9fc7b13f39441b4f5cd067f2f42ab8025d8071cb02b5c51cb762ce9dd4e7abe7591c9ae0e505fbcdbbd42c6093554b105ae2d4d20cd0584b6893db30b8b58094ea5ac921d7bf742e913b7e077792f49fb6f861b859e67ac099acd903257d2686910ffc09bec4122aac4a148fceacb44a0b488753e9d3a71c44847a92fc041052a5201ce6942f2a2cfe67eac25b4f989b5a5dfce1eb9e986063e99b40536c1bc06729f2185ed987029c6fdc9dcbd620e51dfc0b830fee6ba92908a8648dec4f2bf41f1d87228591df0b541ff3c487de6545094c85ad1a3cb5c781faafcfefb9412e1cfe96f370bd74928afe9a907a92757b07342d563b5bda4b2911a1084633aafac063c769ec079002b8901cf0e940e5627cf1b639dbe243f4e1a3d354547844fba7272dc20bc39fd71867c8ab1b55394a64894e3a85c8b458600843148c2f26218887bcc519cc268e9f0a18ae322654c1e3ea557845856360bf3cfc6824b8068c27810ae7ddac84d0068e05d0e59af69414b77c3444b7fdf494842b99b13ba6850169dbe7b60bf49ab35ba579d08d2e0a8cc1f1a779c5a361e5592dd46f25200138b7cc0bfdd366bdd4c280c92727e4b0d60fa92043bf61994983456cc8153e1e48e92c67a299182d37e1242d5308818699d2fe583877f437ed0d57a6c0244879ecb20b3929ef3c5d351a5a45878296c869946620d619897c31e07dc5f278c4c6391d9e8ded93516b4f53905f1d0d365f3028834e01d4d9fcb7e2112c241e2f7bf480b81c4934d66877d16f68747490f0e8dab4a73d34b427f25d00835940e9f34c4aba2d0e06fd413e4d74a52fd56cea7c5bb8c7de47015cba92bc0257b1511ba28c021ba23573c0d6b66652a4d8f3774ca67dfe54a31370bf387136a2d3a841e7602811ae9c48c9a61b99406bd9e4d058015a6ba4fd15cfb814d70d57e1f441c5af49b82d6b939ffc1d2fe9da28a4be602a6d00af1f7da6b6c394cdaeba0953f6d03328e500d88f6e45022a15c822dfc5aafcab1507392bc95bffa5cd088bd011d0c87d686768574a652ed72a077d51ef8a9aebb454d07276055f6445263411eb184d5ce0144ed388dd9450730c7c11e4ca20b8ccacb19d39260f75dab045e046b0d912991b9f41f2a0f1dec93b4bc3a929d8bbb84b4822c5b45bcd6c480b7ac572e0c4a9ac28ad16ed93cee186a1c3ebda275d0007dc631aefa21902bc4a6edc9b4c4c5f1529f8d0f0b7edb863ca2971392dd211bf9a369366113251b79f5016aa9f717b9c5372163d058191aff5d52afe3ff1a79ecf5609c200dcc7e36561083633f8e233a84cbab3e70dd602b32c4984f4dd145218fbddcda83c44e6194b8d806280ad7bf2515f2158878368e3d20edfc8430e9a35d40d2316f0a108cd2b4e1e5b7492b158f29cefef9e0ae3f07562ce4db1ff08f1caa9af4211479ac3b7d122dc0a8b0920ac2332e0bf01bd29f2cd78b12ca688596290fe64f199684589623b5f7b4c5a487959b86c6255a3d055ee95423b6197c77683db22752395d24a7d4043691743169009491ba0511e108c64953a6f86c159d9c12e08e4e45c83da3c0f8b4145e62f8e78f49435baa6dae67b79b6bd65a9820b20cd461b878f611daadd8c0f080bbb3030da4c9159815aa8596aa0d5e59e46d9517e5d222e00bb365160940a4a8af28148817287c38f75a8fe307bae812ce19e800c9c38ebe9fcdec5ecd78c1e006d5f5932cacc57459ca26d47d617c48df9b302c2024bac35c9c4b4be2e43e33c78304c2cf8bc96b156cc713f1cf860d3cf790e6e6d1d6f8c1ad3631567962c205206a408a98bc5548c02c57e13fd726b415b7507e7845aa6334644c23808d468072eef0f56abf519c2993469fbe76641ea0be4bb017b821b416f0020f34c00e5c9bc1367d537ce18692a393cc40c916116aca7889e9d7d16de4d4c42b141c50d28564edc3fad6b9e67591e12706e2e1cb847c0419ed1a876f09eccf1c9dacfb7bbe0e503c21faa8b1259e730816068789bc2e80a366c7e84e024ebebd7f809d70f1cb3007e3865b4d5d6630dbd8325c30b88686785534acecd86262fca32c6b899320c6bd4a2cf2aea4552e1774d7805c69459c7963b1367ec291ea7ba6ed2f1982b60a7d9bec63bce7426f2eb262cd872bca0e227fd5102ca2c1136338ac8a213c3051b12d2c235d6c955d071d778f1bb0cb94cc9dcb81d91a866e2d5ef31308b4b90aedc8d940cffaabccd0002074d22edf14bcffbaab6c01567485fa74bb5f080ba38c71bd27666307b9d14fea905c088ad644ce6f7e38c31da2a9c15fbb53b151ccd1a73ebc40c4a55ed57919312550b42d0dfa9db737d258aa206109034ddc1222fe95ddbeaada8593b0f1c08e9004cb3e5b9923339ee0c8b9472e4013603cffc18e841f10d6c09b2cca0c863ee494b24b9562c2c01c02808ea7c0f744befb8518481cfa731268e7721d3000113f778aa1a67ca4308cfab884807713b1c4d94a589fa0bcf112392c3bde0293fb095182182ee879d573913979108fe4509526589c3346a093f76cee084d2bd04002aa6b0f822c6cad06b23a7a6894d7a7445a6fb05434d2e955171fb4db3d03b1dbbd886f5fa3062409f5607537e696a32950ec0976dca63e3830ebccff82f89f6991d0a41d911c710d5c9f9220a92342673963f6af0cd7261b1dbb13e38e3c7ad96f3e615247e47634c21edbdd2c4ea56fb65b553380ac26356b6365bbf053a51056ad072882bbf02824508afc8b5448a202be48147aac61d1a62df02e1abb671f2250c98fe33897fbf3aedb2d4338b830226ba19fc05cdfe153640011b2b3eeb78b78250e9500b4e4026d44557646c44a0418de667895b7c33b9f738e9d24f670e011aacccb88ecb4181ba96abb507bc16ad9a3c62860d9ec5c8984628718e7805a9f98821a840403430e27405c1ee356f4d1ecf0f0b717c7ae71566e65af6ce5adb495aff4955ac9955e395492ce0b7a542f9296137c505f2c9517fca856242527f0a8be482a57f041bdb8745ef01cfaba081a37a4bb86dce542cd9e1360919d86bb1794c16bbd798d3f9d82e89f63bd5061b566169daff39aa09a573d56469fa3b047057904b1f6485d8e0105aa4acc8652187c38a45fdb2ddf41096e245cba71daaf83b6c9945c48d6d693df915bb64418f69a5a4284a21033383614a59aebb355804747d768548769e307f26c6bec75a6cd29d9ef80810e247001021d2c246529a9b4724a545427d1c412495cc2cd12494ce2c46e4c2cd1846e242ae10d09bea9d81d45fa68379e4c0a239a4f9230746ca86d2db6d176e4efa938369c1aa7941552dda04133c7f103c1d1d9945d9162c70ce33bf5be6755d7fb64153b770c9510590c02ce85f8ee6586629248059887adca10a2ef0911e1c2380b320f232ce8db1e02816c849c4d7d42203d4a126bf966df166b95c0303c591aed6b8ba393b592cfbe8be9c595d85b79f065901f2abc801dfcd83afc4d416ac7f16f19a18af7ea971900efcc73427964a62733b78be9f1ad261ad2afb5dfdce3b44efe67d04c7fa1507d76ddffe0d5afcf452355aba0239da99398e9ba510c5773f0a520685bcb749b9921442392193237ff584b4113f4d87c935fd1ff7d1089f646d7e66b7c3b30bd16304bb870edcc17f5518105b44f8a9c8fa5b8dcaf0d2fdb5b6347d37a02dbaa93828e403fc240666f848adffbbccc9b31bf69f4f5809a5affd59f8ab43f547727ec094ed24d60d4e1c0ab4b5df591bb943c7d42d9250c295cc022bac61d7269e656cbe5da52cc8169a3885fde8fb1893a533da41f77b937a0442dd99d44e37b0c8cc410471c57ea7f6b9c0158184c19c358ac873a3a5dd2a4b528603d01066e14c21a3962608d833b0311914399cb615f59144ac09e09b24ba204a266654d599628b94e657ef796d86af2e46ef0faed6c1a77d735c6d00d6719c217ce6640257e33a6a409ff1a1a90dac52adfc2094dd0412b68f7ca1fe25924e3a007223545df666a024ec82970c330a8185abf20704067bac548fb4eb389a85b23950d2f6c2774ed29107b8261a7d83439b320b0cd9b431d3b960385ac684259776266ef202d40387121269e587a205546a1a20a86f9f6a72f797b9d8332e3cfe42166e64e1d8449189e3b89b62e0e3c8746ce695e7f84984287e92eeb8fe2402a9ee688fa518c03749c5b0f1366412f24b55cc49cf287a87b5be58e106638d343e73f9f527ee715a76f8882b87967caa8ea0a4533e8eba7160a8b2273d21bb5bc4171ccea13627973ed35197b6d4c2e9d0c998bf3e43ed9d3685fde2488c43275ede05817f20c4fc2fbab44fedb78b3b161d965d1cab026a6fb7e06e7bd81c015000476d08a1b754ce07f4e320a5a193aadbd4954d346e7133be821869b395d2f8110e84216c5030fab32557bce53b3c790b0c4b821f7705997d83633985bdcc34618d7273a6e397b780257c1d5f998e4cc149673f36d80067a114c0c1fd0c5771299ada09ae78868fb239e098606e19c44d003fab3a220291276e686e5bcb4029834035519e7f70b0583ce30a0b1218fb0f27ef9646439c33fb91bb11369962e48bd30b678d0325e3eff6733d6bfd6a3c186ca41f4eb5f0cf4b3f4207a1e3c327609807c5d268d2825b4b623c98d4687f140f7593c440579cad401cfa8017678fe067ad888ffc753f64257295baf081b33dfe2818efbbfe3593c38196a2b88b8e281f95f1c8c4fb13a8b87ee487fa81b7263c3d8b24c19877888f214e3c19d66f140f7f8e221eea944e97039e9382d1e4e15596e330c46ac36e9b8e2f03177e6afab2d1c363e05553c48cd56443cd00f203cc4ccf444fb679aa3ca4c6e5e6c2f203a502b69870fa74bd92659b84910fd4a02ba2c10d9326f2d77f0a0d69a4c8bbb48cc2a71c04a0bd9f04166b21168196833ea32dc658e7e19f8b729b129268b5e178c1083eea51bf458a7ab56bf0cd967faeb25b21f2a544fe364f3179cf221e4b81cfa9701ab062cb53eeabf0bfd896065acdcd7b2cff8bf0ca09b061b4ac7586ea8c37ac8ff31a992fe32a0ee32c07963553d4433261bfdee763f0761b6d4c65fb5ebf4bf0828d394fe5f0586bd7c5c2dbf86e701989cd640a96bf1bf0c9ac49cdef0c43d767466378a64a598b3b8eccf621eb7a3624fc25408f41832191135d8ba9b5e3a12b1acadb1203839b28291502ae520b211c2d0bcd10a33bf558bebc6babaa525eb3c2e0201ff9715260b4fd80182743a6dd2db727d41e9d023b908600f390a22868608c4389ef472c551a4899064ffdf356e6e0c96147be40f86e6d37c2162bd553c96d054d41c7028b66030168a4e6d391612325a3f857bb7007edb358a09f548e4d56f575761694dcaddd8034241d69d8fe8ae78c301650704286cce64d98f90428646ae89f72b0a25ed88236eecab45b6a6839bdb5b94e2842845a5928b6782a18e89a1806fa2531eb546933ea5d7490990b48dd9fc3399df984a1baa119444b457acbe6b19c6307d33e0c0fe679803c95a80845971d11055d0d11f8476b999190500f0f59308731a8c6a2943ec1abfc10bd266da3adf7d4215e080436c0fd63d243f7a11db8d0dcc92556bb3db322daa75c8ab676f656563da87f4c9a2fdf52b0fc63948eb0ca8ea982d25bd21b31b743910bf9e4f0c600320ba3c8592fb79ee6b5157db8ccd4dbdb47333e6ae9d1e9f8f7a0c45fa6e8b36e4fbe39c89cb2f0267dd2030f2f18a9fe9574787573a0963197d8d08f8c82c2388e73461b901b1141be0207251643cd61996aa745f456f44524e834fdbfec828392e8c403160cbc2ecef976904767cfd40f70a7454cb1319fc16c9952c1b67b32f5974015e59a1485c9b02b334d7dd9fa68dd1a4c1344204429f14557ba5bb9f580eaa4be09f0e9d063f7d5828bf2108562c792e743c6544a3f9160294600879cbc2ec57480bd3dc65c272d20fac5306d3e4c111f2012c4771859c2119fd0a592e66d786849071c11bc03b051613f2d8e232b899747f1a3311e1577dc9ebc5ad4392dd952263d7f1084dfb545b322709e951cc8e98c29646dde42c9fa7082dbd250d8977ba5a0c15928eef26e9b421b01af2b9c951edf959bc0cb739b17f42295c631280c26aa97347198c7a00817be5709ecd0530c11f49225e72fb8ef1774061b2425886a57fcdf3f7091d838ce7484a5e69425c00791a6f7d06f56e1b6adf7810369511b8c20121afedb97d2731bab96069ca9c702cb37a8bd131f34d0dc788066da75340faf532e0c41630345dde48c3663e175d44f9b6b82456e89c874cb15a66d932450b088f9db9032baf8797947fed19745051cfee21b596d4c15b6fe4e9988a1ef7cea19f4db09fca9104445d572cf820fac4213acf6e83320b2df7825be265b97d328406786442284609f4171c208061888d3b3111a86bba08c1f07728e43cdb21a2c7a7aec2e87570f59b6b9cd06d3ab2e47b6e8427c704b6c158f1046c45309a013ce538309cec7f68cbc3b7c85809367c5ed17ea9d989baaae03658d2112fdc0c4e499d00e0aec07f081a7973b965bf43b087a274f946775b2826b116044a649e3871436549a1c581e991169b37e7b0ad260f8159d4134cd179e3146bd315e5950667983444c97bc371a080c8416cbc4c2951c194ffb02eb2a1f5a2a10094ef47e7cc4550866c562ca6e8dfa21226cc56ef221b7c57524d2889e5e42601b10ba5f0146b9d9b47a093d38f1dd440037a52094e193047de6a039d52a279876607ae3189f234df8a0ca84a9acf365d91c73771b371839f3590974337bb8aa0b0af62189be67ff67109d0736155b121399a302e69e7224103a6fced81c4f704021a9959b40fb6b7e624667581a21e6ca130d59fab288cd1cc34676397939eedfd1c17213d7bdbaee4ea5b68528fc47afc7ef141b116805dc12cfd7bc93c86844644b748944bdeb21251f6f6f291b4713aa3bb061af0919e05474aaa2353e20c62d95fec18114711921ecd6d0ad1f14008a5196e659707c830fe3b76bb12c2fb6bca11dd3f8bf984eb2b9807160f9b11abda2d28acafdd1ebedeaefec0f67b7bccd101cccfeaab47335ca2d4f35d11447c2e382d86a76b4f4d3019248d11a6a88394dcc35c57ca08603036e4b755739fcd1f69544256586a476f7597a1f303516eaa4a19fc8de1400172990a6f51ec05c62307b83d516d6df37b5024fad04589114790c01c970c0a9e44749b702716284442a755cf332c968548041456adc00171e296059e6af4864b0c99974f8beaf2416eccb8087a5a97c12b0e57f6664e746083f7dbff010f10040e3af86a1c8452fb08812b0c3555524c16661e3c492a772cc35ee2cd077a07c791d1584de3ef4d9c6775dc920ca2d33eabbd3ad2d8b0806563bfa4a441f754ed6b171d294ecd73ffd604c42bd081969304e8e9f77b058ead28944f460059a6a00f86921e25da1e02f0863aa48fdcc7b08092709fa0e2c9cf9852120accc24e10c86d0be5d9114af3b88e1de09e075aff70fd661baf5fcf83f35c0e8876cded517d8ddbd65a2d45b0e684a33fa330ffd6e7b01123c368907093706ce33f2b5580cc2ee569982e21d20ed66f7708fb8983dc045e028c586a41cc65300fb543f8208f56c0a5b0d9285208ec067e5357277bf02f9cc6dec79ae2bafb9873777b674f9ee6fe3c7ff7b5aaa11bd38dc8b14abfb857c4608e08b9f5afc1d04b47e8d0fc2e170eb7b46081d3fe8f2bf883514480e8e2b217c6fa85303968efe9be32c8c928978a3b5edc7379db7917dcf5f9f07b06745637a8bc0b410396fd8046d5cf1118f241d5651f242bf2e03b76876f4f921394f0b22d38a6b0490c8c5e19b7867abf4193e226ab65d38a4a78827b5448e0491d671310b0b802755c00d08aab2c72e7fc37eae7106eabac07a23c2cd1f5aa41cbf4c1f8d83a0a9e8e3f0f400b052423051e66859612581038ea8a730375100d10c2385e9253301728ef861cd10505e8760db0689c5d559fce17433d1c2c5846e026319a6c17cbb72ddc26a5f109095150b85fe8fa383e7614ee69b2f93920dcdc29f1d453de81f86fffbb89109902e3124a1443113f3623b69e9ddd6ec769c53c3b9f9e9e233e58a7fbf939837c76403b1ab5edcce2d5b4055776a3bcbcf268de8a4d1b8fdae6cdb3d7bca7f0e838f3b9c9044029ba408b6a429925275a58c08c256e033060831520d1101d38c18600587ea83901a2811d9cd08048c60a149ece3ccdc880042e1c4006009cd8c1c1835d019a50c2070798780048c13ed1038f8b85289c9496e842043948100122022021cb081be801870f72a89122e601018ca1a58410c6f8082a450122cb19060820082264ec20877408e13419a3822ee8870806da121422c0c584211e2b5876b872000ed8b8e2a7c18df3849b0d2bce44a122045f7a38d0d8ac408217f266c98b6bd73303093a44bea831fd9cb0830752a02163050aadd1046e8011c2045e3065c500aeb478c48003952d7e514d5861024aa962843b92a10ad305ac3400052bf01805641e42bddc3dac2263842940c85055c852810d167e80590115ba45982f7c4cf8718008ad2c6e4838685103fa834495175c6c504546110baaaaaac61023c40cf1c8dd61a468e416d4fd99778b8b60301d770f9d14733829cadcfd6fefee32778fb93bece52a121ae12c9a02f7e7589cc3e164e8ee4ec3c9d08b7b8aa1f42faf2b30610e32c4e1ee399c0c3770771b4e8645dc9748e55345d7bc5574afb653ff78cc0c95f31bc57935a7afe4b3eecfb4d5bdfd39eefee37e4170f71d4ede27dc7daf36530d6e576e530cf5b97535cabd8a5fd59fcbc101e2354a6d6f37366d779692fb33a72936ddfddd439dbbeb70f202f76a637fbb96bb5f67f9aad735bf8affcceadab27965d5fccb29f0873e8be2957d9e8ab22cfead7af71807bf3ab83b0f279dcadd699c7415b8fb0d275d8a33a728defdce6c50f7bfd3af6a94c51f5cf52ffff66a0b65eedec3499772f799930e83bb47e974b76628fd41163531109b7bb599190a1785571e73dd3ca64e53cc135cf1aa4353775f89a3bb871a74f1c011476cf0ca4105440e1264b22c8d6089a6dd068c68214b8d7068580895a911e5b2bd40862d94d214747809d57210e053a6c68e00d844e06e72a381cfcb0939b83a00fb0226e6545353a58b30185688c584b4204b1612c2179f84fbe367c9d2c5cb13ad9a5ebf7a12b2a0f044ab5a1612580fab797922f15f35125cb5d5b76a24b06aab9af8fe24b0ae1a131206d1a579f13276f132d6500003858bc2d8c54b104f1adc4761ac05f1a44129be535589096101197e2ce6b4fa715573df61126e11ef9398c7629ebaba94c025fc7014ef3d22e65b684ed7d77ad79358bec6711c5fdf728dadd7288aad96f8f7e5aa3989e3388ea2186661bd90583f66810a59f1572b7195c5e5fad0c8d51a694eab9ad3f8ac5ff9c2bf4f258bf694a51c7de17d5f1911f37bef8ae60b5ff4b93ef67a5fd556767c560d2a7cd6af46da938ed289f5b0da5396f2f521ed4947c9aab1e2d12d5d62f9aaf9c6bfb5a72c65abe6c47af19988b5272ffd494d19861270b9102f56b277a2180d2da4a111439a3046a3b171d27a9653abd643307c56ad87a0f81a9437460b695e2e51f8305a172f9708d645f6b1d7ab8b4c0becefaba6258826d8dfd7127b1ad996a929182d263a0dcd0cda250ac3d1e5445e9e28f62e5a972bbbb529b18b0935555555555a48103f569b12bb7879a2fb21cd0734342d24883fa336257ed881b814be68315a975848a319698c6c3a189fe63f105ff62e5a9757952d4434b5fb5a60329a96d85ff24563fd2512f202f2c97b0886df4370fcf12fcd52f897c6a683f1c36f5a1adf6996c4174396025a3d36847afc4bb672b9441b56ebcb97f2f5a2ac26e6b196cc69905ebf62d15c7f691fe2d88891b077b1d6a79a92f5b57ec6b368927c5ba066cc78d56c5838ac98af557bf95a4e5a2d279a97fd8a46b369fd8c8f893216cdb76831a7f1d158ec58b26abe4bf335fffa9a964ff6afda8c9ff138e18c1aeb65350f7162359f4dcbf7eb58fabec7d5e1a5ef8f64367ab86c5eafda139c92f55fc3f1a75bea94813511f390891660289d0916c230b1c2184cb82003132d842144055b28a104104028a104132b30b102132a8cee37764c01062b4a7fa2f4147428c567a5408152fc9b8211a5f8b156eb0635b6688c6cc61acd9dc6ffd25aefb427b7bcfeecb8a5f477f193925a35df96b01c5bb4d5dfd6ea5e9bcb7a712526d124f9b6d88c3a462c1b9bfb2c2736b775754ad74813e5be28feaa8c96bfde45775fad602fd6603b3a2e27318a1730a5f82c2f5fc21087f536ab67f974cad127d65ee4a175b378a2e6fe380529be3bad5ec815e4f8ae5b4d09abbd6aae24d6b3ee0d0388527cf151a0a16cbd0b85154a2f5d9f45f9a374265a1853d6b0b7f4b55a2f9b7b5b57f6faf1e58abd6cf52d59cba6e56ac95a2edf69d57ce24b841acb3bd66c6ecdf5364f70ca96d1f5d93cc1295d4ed777dfc64583e4aa3dc15989e2bbceadd9b47c38e514e4538d3fab053a21904f7c9d1f638bd24b2f45c092c1de378eb746def7b7b91fbbe385d15ef6b736a3a6841a4bd7b363b9eae1d2a12336ae8c66dcd6f1a1c6d27d87a666b3fad7dbc0de6336abd5af5e35a86b037bfdcb25f3c1583fc2c61f698cc6717c9cf2f5af1ace6833fe1dc71f47239adb2ac0093494aee3a5cdb5b9b52738e5adf9c2b7698536ad9ac86239b19cacde35d66ee9d4f2ed2d6568f304a7bc7f9faed30d6b3aae2baa70c20a65ad7426ba6451c66afcb68197e2a58d4f35e593ef758d5a2d1bd6df6ffd8a66c3fa154ee9fa97ab36ea6cb119ff5e9c12a7a4116b3a21ce0dadd0d161a24b144c74a1a1851644d66ab55281cdeafd7df5abda139c72ace1946329eaf88e17e1aedf5286344e9c6cf1bfbf81d76ca0c69a188bb262b96a48ad1fbf5573628d352731ac5d32a4a0a1d181a2cb0aa54b0a51c4f131c4b9ef3838218e0e06c217c3174d80a10c5fd79f5c05d48c2190d37bb84abaa102621e0239893f5ebfad5e7caa117d378b27ac28c72762387ef8a31031910246e850020ce3b74ae852b69ef5e3bbb8aca0743d4b0cc5500cc5505cd920e5945eeaf010a205f9a4538a1fe3b18ee38b301a23160d94d1f52b1a24d6b756a2288ae218fe7d71a4410a6bfe2ea49cf2d69c58effa90e663bdd33c19dff52c1a24d613540875cb95d3aae63ac1d31d6de8701cf15750e37dc0180b999882a174325561ca18a374c9b228bf7426a6c69431f1d5720579bd5e1c6c09128466e3aa4561b19c587f53ae6ace81d5ea5dabd5285220046a8d717db81ac7d6af5aaebfae5a585e31f75f3edc7fb55aefba654896ef25b6584eafbfcfa2794283f4aa8d2bd88bb5a285e56db158bf62b17ea504103e88dfda424ceb5d3e88bf2a2303e5ea59356cd4a84183ec71a9ca56cd272b5befb518ad4b10bed5873117edce4892e689f8acafa1415ad5460f715cbed6b77ca3cb74dcf428396acaa5525dbed01026043222040acb314748230342f67285404eac5ff95c1fbecf8693eb5bac0ffffe934ba60c11d020b9c810c785c4aaad6ae1285e0f57332e950d315fec96ee24e6618c45860caad2c99041041932ba94ab556dbc91e1df522cc36712f2100b77c0c109719ccc0bce94b7d2c9bcc0889289a924ca59e94c4ce950aef7c59a6f4be9b311df3796ceb2116b4f6e5cec58ded8288a2e2150903714fdc59b72f5ad2737a53b0364b11a272c969398c3de86e6d2fc69469a24d978c5da13d63b4e58454c7c810ea5eb634e8468800cbf6673bfe537a5ecc3778d342736b2871a6da0c6d2fd614f3449b11ad4873451c6f71ad4fdd58f3462cdc91d5d3765cb8855c309717c1469211431718c52fc914c185b94a16ff54ead4722cb1d7eec6361eb57cfd28c35a79a532c2c9de68bfdfd300c1194e28f61b0be258615e54ab67aaa29b7fb8e2fe63b62940f57230dea425dd7c79c6683d8cb1f6a2cef3fd5f86e14d6b76a50b78cd57ce28f1fd24459fdad415d2f9dc655ab398dcfde92b5aa3dddf20976c7205a90440b6462f8770c6b5a56ad67b5c69a96b016c40759c60fc3701c572b164b14efbdd7c95a172f1a10dd47e1128935142ed17d2186230669798daf257cd8ff4bf64134bdb4b86a3a9e8cc96a5a5e5a5cb58fd5b4c06a2e31e79505f6222d4b965a9691f5b556cd86ca1344b05a99a92922d64bcc711db4d8a5a1e27291248b55d37169a88c4d636d6545fcb0868323e69010bb6311b1f057ac9a96564bfc505cadc4777d4bac6959d582f860f5a24b14572b16abd50a431c31e73e8e98e33fd6b2bcaba543078b24c755e8aa657189b52c3527a1754726788c97d56ab59ee52ed14c6d158aa28b9f42cc6f7ce2288654ee666abe9e32a412c31f58405523b668b7144201d286032fef0df205e9ae137acd55938538b53048176448c3042630012a2a77ef29fdb631bff922092f642863d761a5182405a4eff532d31245166b6a05469c12424694892c1146253303a392bd4f6666155291e3fb6054669e8728d6ce5caafb85e6f5c40ac238eb572e166be51a3d20c51acb8c38e502434506c982bc6178592a189d3943454583f2c2041a5d845d6041a68b0e8c5d8c5991e92209325dfc00a474325dac208405d215e2ace0c3992fa597b9f169689cdcffd2e5923d19dff74e5853359facbcb1dad10048df7dd62a2b63ac188f2b8a2b887958850c37be9b292a2722b7c78c0f31b099faf13c3c207fbc0f878e8e0f30ae172c267b1f40682f840c497eedc666ca07846686aabc356ee4d0c1e36d5ec81832888f173286fcf1405ec818f29ff642c6903a5f7b2163480cfe064790f7f90023e48530800c710c7916cd97c1b378683befb3b333ae688c769e89b8b3f2f99de7a1852f8abfe2f9d60ecd77cb1d9edfd9a13955e943bb250fcda94a91e66512319ef7f1d9f94be36487a7e7431aeb7d6819bc48e3791bcdcb9d9a6f8b9772e7439a900490e1fbfc1563ac08533acd063eacdfa165f03c829c80f4d1f4d478785834463ca26f4b0f8de72f114fcdb7c528b43dcf8bb4f07768b78cad529c0885209e34282fd1ce87345160220fcd774b9e6722320979685eeebccda9ca1d1a111c54545565ceeb50955eb6be8a311910a9d98cac6722e6d0687086d07c345f082608cd67034c79ff86e6dbf1a5bc8f01cdd753a3f976d068be1d67cafb4ff3dd2f40683e1e602e958ffb3d7050f97a7c36ca94f777d07c6ee6c654793f07cd77bfe0a0f96e10515e2a1b34df0d33e5fd1a3433e57d1a341f8e2fbe1eb266068dcd548f9bf9f2f2f90073cfdc6fe1a02aafeb86e7ceb06287592ccca18a2e3b44081182c4010488cd171b1b9b2f386e80dc7207cc0e1021906f47861d303b3a84403b30ec84d9f9b2f305c8cd979b9b2f40cac4845480f4dd7cb9a12104f201f97203430804e4891b1942a05b03520647ded2c95001a6f40129538a44ce94e207f982f4e1d8e0c009b20519963e1c3884207d34bebcbc884f3346cce182f48d63c6bcbc88ef8221c4116bbe9475bb94d74b794928c7bf2b164e191e41ba20852441fa68809429538a6fa6f4d2e766be1853fa68c694e203291302ad6a365f9e0881c69a8d0c40a20881c6527c212f90be1b9b2fa5f837610819e30b3295c5971d226460c4fc268817a4f79441c6a0e24b9941e964a6b8a2741aa39c28d6c4fbc3cb3563051b5f5cd17878407a4d880b487f91e60a6bad3014c72f67ccf49433686431d8cbd55a05418eb5500c82bc1fd6c2d714794711c8603b6230594c2683c17e47cdd71383c96232194ca6c1efb0f9bb23f65acd90c15cb1d76a860ce662edb82e1749de7befbdf7de5618471b38316af22c598f67fd904f0f8f6d47270322393835da03f9f1b1d6191e3c74e8c89103078e1b373c8aa39acfa3f0321bd47c5e2647cd49cde7638c6a3ea7ba516b52f3091511954c902091c95831301a0cc5c0c86236b516d5929a0b8cab8c922437a120a023468afcd47c382e30383e2e30af83a7e6d361dba9d1a1891179b5705a54438404b9c1a056a33d901fad33327cf132d53ad30aa30c5b54ad33ad16d01121233f3d2d22321fb51d2e93c97aec90c96453b9ffd1232777bf38909a0f9b1a0f99e77017a79ee663fffa5606bf7a24345f8f28140474c42754a4f6537bf9d45e3cb5d78e38f5caa04664a4aad5fe6bffa3f63d6a39359f533d4ecd2734a4e67309a9f9c820359f8e9b9aef31a885b59a0fc78753f3bdef6b3e6a359b9a4b28476d8c97f1286ad468d46635b25653f3098169c1c0845186ac9acf1545193e93704a0c0a5fad952b077943478dd97b8d19d9d44b26fb21f321eb21b391f190e990e190d990d19091b218ec158bfd88f988f588d9c478c474c470c46cc468c448d899f0615438517c191d6376bccfa976bc4fc8a5c3f7385a9066fc0c990c06e300ca27bd96209a9e3c07cfc13b51522d0b949a9627350e6a3654906a2c3365ca1daf17116290ab4c8e5aed46cd47d6a8f974cc5e53644dcd87e351781465bccc181718a7022354bac0c8cc6c1036f9124e952c3360ca1da593314384114bfc9af7d0ddc91088f5364220d6f708816a17881b341bb44bc38521c4613d0ddafd12e2b066cfe359cfe46531d80f14c8fb9745ba0d8d8998c31212eae9c1c1f9d7a1832467cc7819f92ece7e9c40febb66351fe9359f0ed57facf15aea4f7ecc6968fc8c0675cbd93b2df5f7a7417b2296b3276929f9364fb79cfd2dbde69d1696a4d7d06e796b7070c4201eb52316bc303912839c6847ed880530b29a4f47ace62361359feb7a61f9be64f9de4c1625cbcf94ac304ad67b4cc21fe18fd087e8a2df9acb0ac34b3392e218ee08c3f0b2eeb8bbaf9b1b5b8577756704d98448add82bbc3a97ac15de0eaeeb5e31bce33dba3e4224618f3016b6c2d5156f78c37bbb3ec258d81aafb812ef0dbae2c5b9af2bbbf726b93f4298d8baaef186f7deee65e1086ddc31bcabf1220963570c7f5c510cc3311c2f9230bc3c42d87d5dd718de7b6fd747086b8977748df7dea0bf3a88b8948819acae785f77d4715faf5b0c049b1f1783f186212b6ce54bdee8891ddd1bdef0debbe32d07fa075bee190f52e0efc10d5994f18066c516553015424c99c28c37d1a470a61b389099f0065cca0d4ad42085a6a3747083efcc1236c204ca0d3fbe38c23d7a90dca08307099ef143d01434374134d8059ae2920194abb201e8cb705b4e8c1f3bb930a18e191eb4cef710c9008c8e0a80e45e06e473d0703035b0786ef582ff6882bbe8ee4e850325134d997e7a75125c9bdc7d8aeb4668afb811235e80d8dc79974f5387910805011d3152e4c7a7e78afb1269a467af36cce213475323da884b4c8d2c4c8d505844feb658c085e4616ab592b58448c165bcc167cc21d3e1075600a646282f1cb01e3a4c987a79307e20fe88b12c99cb894b88051393e880ad3030e54a62cac6152eaf20b1062f6425985a554dad643c3e3025636b4b4cbd74cc085b49563c2b18bcdc88ed88a1847185a91a5a0d4ddc99f1b874ccaa4a5865e0126a29116b44252e1da6c4241f1e168c05c4941863f11875623ee21d65a30eccc92a8396072c1e2c18cb27b321b3f1d2f1c2d1f24024a304160f9188125663a682f8c6dfe382f8186fb21ad66cbce28e58342ec972b0c25695964d4b697563c665c5c4d528135de20bd663145b535a346a9660b316ab75c5d66864e419af6b8768c3e50564f5c3d40c20b8cc58a38dab490963094aa8e1deabca300108a2e0401932635a1ed30b5f8c88345812a45515a480670859e36a81f04114a5aa269e7002880b3d32c4c0e400258062031780001243f4c063135280967561af2ce8e28c083cf044058ed8424208573eb01245690327407ee4c0a18403b60c2104095a703e7a6815c88c1290e0e9375555460bc0b802420e1c33282e6081084000890514e1420b29e042f0a04a1425a40d3461010d3c4086a31048b1810b5400024bf0a0830a2798d0c519283c00812594484097a97c2219cd68ce5c614509c854c0010d30c2871758c8272e842b205441324a72cb99d16ca0093235ccf042977c86c0019213a3247e85151b6882090b38a0014614e183005ae8828503a425496e41467c7268cc46afbac20a123041c602157040038af041002fb4c042172a7058ae68c0631b430c284f6a668ca0c7971554b052a507071654084a05f1aa12cb08c9588911bec0f5c32826f421ece1fe843e62cfb5c576c40cc4c70124ec716d5a3cc41d57c798e3e278d5a0414324c71ad90c9286876c47ac076cf66ab958ad90c55aadc4ebac2eb02018986abd5aaf71860ba8f5127dc89ab828d55c382c984b6894d5dc51c6c28d51228d381ba330e10053ac1961eb35ead0f81012620a4645768e3a33b884466c5653c6cb522281a925509acc4c0847d80825a70553589440a40c97bbbacd8030e1128e3cc41de0a50dad32f0c0148b0564a9cb8d1aa213c40db800606ac7ab8acc86cc86084e6e005d1b06c69412f6b5414e0064eb25d2ac9cc61456e0e21a67b07cdc9a225c4c80c38bc807bab8c2886146185b3199b5908c4f3208449450638503730213c2ba32126921298101376c5082181770903d5d684c2801a0c054acddcd6002cd4fcda5d2434cd2da01c405d642c282b9668841481b883e604ec09882b59068985c4736464e60af2319d3a984015339461d2018eb568386058b19d542e0610ae63afaa153616a2592abcbea80055b25611119228ab5af21ae2316acb583a6c9eb66547ae5b0d143080f1c445c193c9122840bcc656b15a1291284b4d4928d2d1eab9b08f4928d439e49ebd5828d3cab27a31093bbb211692b1c3d2f972922314aa2c305c3f10ae252a41433e2c2196fc61b9698292233c626ab0c563f5e37638f1a1fd1470953369e2beec49d515c8d500b61004b68750821ca50113184181d02b0458bd476da653317c2950fac5099c2b4048583a39b50111c214130f8d10215a852c11626d06028488dc78e16180d64a0071e3a30d2e0e6e3862cc6a280120d08224c0f3ae47003981aa6e00ce1b1cd708026a6aa3156a0fb40052980004ecf04e002ba1b4a026b8579e14bd00b0451f810e6860428878d18ccd5ea6183c402aee4338a1207484e8c6e467c7268cc66d08c36b4900200689839f280218410b3a50a9520201d36460e94f982cad012a0578b55040a0d48000e0518800a2320c0061b5c88a234946488ec8a21be200c633cf32908b3184d104eb13273a508a1083ff0f280ec899003e1064225c222c42142a7d1776f2e10978f550f9a1d618ef0c6b531abb1a211ce2e39ce08632b58f872b9c2566b158e57bc97e5301ea42d754f759855d115c8934f6da26aab83bb6fe03ef66c3d403e403e478e20e9092a12d463c44810901123423e3e1b31d8fbb78f90c0565dc4c1f769e295d5b5cec40e0dec8e94bb4765f57653db26afa27fc51d28bed3c37da3af73021d2c489d28dc73cf53d1dc8bfa9c5fa2e47196287915e5659d8e181d1c489d2ca4ce53ee77fa731ee73f9fe856d1dfe8b3e82709ae78c53df331fa6c5087c566b381709ace3ab2a2525b1191196ce17bb5a96b66590ce459d9eda318facdc049223e10814145d99dcfcfc1c9e6daea4d048a48ce4f35b3f8374984c9a96622b29c17b8a7269aee5ff56ec5ecfecc53f19a83f3b2f1b2c9edcae69de25dcb0242c6b8bb91ab498ad23d01129f1ffa756d4d224890082109420284e4081223488a20f941e283a40709122121a1202120a1234246848a08fd08f908f5082109120a0a0a020a3a126424a848d04f904f504f10122021a0202020a0234046808a00fd00f900f5002139227424e808d09123478c1c2972e4e788cf919e23488c081909320264e4881123468a18f931e263a4c70892224245828a00153952c4489122457e8af814e92982e447e827e807e8e7c88f919f223f3f3f3e3f3d3f487c847c827c807c8ef818f129e2f3e3e3e3d3e383a447a827a807a8e7488f919e223d3f3d3e3d3d3d523d2889a37fad8a05a603c3c1d1f25a603ab011fe757e840ea228e1e8fc0dc7f5b71c1643a418336408cfe5fe06d3c9e5fec6ee72fa0fcbe5b03d1ebafeb3bb9ce2e4feb6f1ce3d93c5bf25b4254b864c914372eebf51b6d5fd6f34f758774f8232648992d71932e4598caa1838c4060837b64d8ad2ebaa898454e1ee361bfbdbe5559b428270f796934278109273cf79254ade07e733b005759f4f140a3d81bfccb2d8dd5f559c0ce20276d53a9c85bb5f09b2c213ee5263dc7b94706789705fafe8e0ae5e092283bbd782ace05ed3b9fb909271368a496eee8ec4dd85dc81dcfdc8a5e1a647de94216f9ef8db3ed556eb7ea79f6df56b16b744c97b911edb119bcfcece6c5017ee80810480f8f753aa30ad7ab7a36a55f719bc1411fc197c9110a47ec9ff8410fc9dfe2aafa2dff34b68af7b228f7fe7156d9ff2fa53aa30fd99d11d9a027f28d32ec8c99a156ed36b16e1f38a7fd58f7fcd3d16cd6ad417b216c6773bfdac8a6ae0632010ffbe8ddada9dc53df6cfec64e69e99ab6c77d74ed2a668a376139b91ad83b7652894c5269a0251366d7fbfd13f57cccbe9e7dd6233eb3eb727baf3fa8f7fa3ef23c57b225f36197de0bab183a2a58c918604393c96945a4c7500041d6061b232d40a6acab00330eac0cd89acabae90c285aa170000055555552e987265c9bdc9dd7f4e024182c7dd6d3b241023406cee6e731288bb4d9b9a7d9eaad3ac7b56abbfb33ebf67a2bda87fcdaae8ff4d45f14f9fbf59f477fa57cde235ab99b781f8f74135f3f4463ff758773f7277f325c6520a647a7210e3c51549a8400327c40e6a4c2942e70512172f382c010210cec4841c0bf0400052e83dd16e430c208713848a14a003cb26ea0550704007ec862d67c62552c06488105850e540b045836019588490c7105099006d440a260da1c50f054c4146490d65d4600647d40ab0acf07a6106d3162d2cb45b0cb1f3c10e25b859418b214330b01b2ac38684a2b41785184d902138222548b7817ce40293c78dd428c8171b0d4ca500cb141e6aecaa845dfcd8b029aac989b23e5121000540d6131f34725054091981c4a39314812730805c0082bb03491f3ab82e8a6703f14fc76d2cc186e00529a87284e446430398b0616702527851c2931400bc6e40c08b8d0200c482f8608aa12a07145ed5069892aaaaaa2820068b15510d33865e7041ca6c0a981852416ca99142173235c094f1f2a187a1108ed03b708085011b6e82730befd915e1eeb7aa0acca80204351e00841860c02cb03da0456036830374da1c9478a0000721f0d0c2619270f080a2060b30b08202321a808588aa2a3292841570172d00664929c5ac03269e86c67020a8dc6c0a0444c08731ad07a8d828a979a005d0cc85114e70e42688d15e5d55d50ba03e2881fb2c4b082a8e982f4d43c0a0a2e20ccb3b7b4c213300103e90c146a60365bcb0c0191e0b54650851516488114174003ee0061ede1757724431125233e5020a2ecd0aa09410860b163d583e985d984c5459012e8f00dc081ac11822f0562f38850a152b3162c400d142505844062890534565080e68148052e540a48b23200eb490e644071f1c800328a4332ce184b681aa161c4921260c142e9832650a530f56a6a50651ec35b5259c400212e0c4005a400107117018c1070f1d1ea8a10417b480fb0f5290f49be2840a5042644000f674811e2f18c1728d5985e053010f4ca0c6892a6c08220714f8500503186cf1822cc6297470e0a52aa80052c838a2c78a1524d995a1a2005180d1e50163442d81630b939906b03e496821050b1870b463058802581e1af809c102c268411159380104918f89f28baa538d5595a0f552555515c5142d2297255e5b62464c8199143186183f9a902842ec4182ca07102ca049d0a2090e54153c1041127e80a223ff2650ee3e648a6d04a386223440e28314318611c6e40840440a43e71020bdc2163a9808aa8201a11d09707901c218d8470e5543406e555555357e88a1051eeccf54e804aa1a110ce384321e44719ef0c4982dba882872ac602e943f4c60c76bca1411ba54d102133117e8f17481205cac4c0378612a23881060b0d975808b9329547c99a209d158a4030e471cc9410682a02c2b1421a02a052f1c31a58321bc1023460c0f4259d8a8410a5fc1054912249c09151258515941070ad0225241a9f628420c1e187c0483318440e624101bc40801934d9932c583a709c02e5084ec818c0f1e8003a4811ec2157ea8004d025ebab8008064e47372b22301aa0b74004083aa1160e0139303ee8a2953a640792a5363ca8529af3f415901231c408c291304c6c8453c810d69195d96290d10f9828c271c5823a8d2a2c14232b401184f17e86126045d0e9c1b42386618727726eebec4dd814ede2062afb60cb5b6264eb33820fef1f054b4b7710f29c9c91b47ee4ecbbc15a7af35fb1b7db5dd7d6fe3deeff46711d09d7bec6bf6d916c8b6facfbca23b9fbfd3af5116af9f7b4d4ce40d993b904f083fafe21efb6b4e53fcdb3d96a76628aa0d2eee7ed77caa18289579f69a7b1bf7703b4d4c4d768c3e406292624425ca4ed1cf11086634b003880920f86186556f850e6648d9b2431416dc94a85fac0ee0097182ef8505cf3d989f9868aa6b9756ed248b8032f154bd5601b67ae5e95a26d6ccebaa8b5424562b6516f3f2cae395ba69e7b565b1ca439972dcdd85e399b762285a37ad99975915c9dd878880a6185df3ce4ddc5d4810e7a9e8a94dd508e9c444ee7ee3648d2b18b8d768bee6ddaed57d4e752fead314ddbd669fc79d9d00c9162c0ef0c4a68705143d01028a33ea49972f4ea001015e8f1360c020028c9b1f3f5495510453551503caa8aaaaaa12e3f774779c93b310902410411655d19d8a0e807c81fc92f33a3834de082d822768e2851f90a14a8e0b34f921d6415007427c58d7045e6c0841154cc078c075c96a48104678c2153777b18614221a574210022b82281c58d2811dd0520766a4858012a61e60d80d506d6060b2e4081e5475cc4f099e84603ad44ee02e0ba38604bc3e60d4c112134840420b6476120848fdc05344074858028b06dc40b1810b5a88e0049bdac29401ff19c388c3c40c717759d517ee2ec4fd427177d6c9190ef0bddab28aee7698676fbcb27bcd688ec2b1abee6d33ef38add80b0b7a40e08ba4202c742004b41d3ee09480243b4820288114700a90015d1ac1ca08346aa6545559f1a1aa4a04555baaaaaa3c306506ce6b04244d067caf369e8aaeece6c93a233a23671113e8c88f99714f8f5011a01e233dbb5d915d9010c67927e483773d424047784c76b72b8fbbb78078306d3f8853e0469b3c76f71feeeec37db4717722eea20a234c666688fb0593f79aa154f4d51ed3dd3170bf08707728276344b83babf5fa79c5edd37efc190af79eddedef319be626aa46b79a59a98c335116af276e9fc8980e19cb89f570f79cf689fc67f038679a5127ac94d189db0edc738fbbef602ab8bb8e1c7bb5e9d0f4c7d6b3b39d561de5b4629caa713bd525c0dd8db8df2edc1d75f2a50370c7b3d37ceadf1377677277ece4cb9555ad947b55a32c304afea12b90679f694677a44b0677bfaca92622b48073010f02b080165c7c7102054740e0c38929944cd164081064c0489825402c49a125a6aa0a88a6aaaa146c81405555d5097e7077275b2a70b27502275b2570b2f501275b4d38d99a80932d249c6c05e1640b8c93ad0238d942c1c99616275b4f4eb6724eb63a70b2c5c4c9568f932d9cd52b0673d86b3673edb4d19a7b6c9433b3c0765d33ab22e9357f905974adc2b651cefc439bd816a82239993a253ad35657d9ec46a58c8227ce6b5a6620baa46627bd12e5548753144395ac565acfb3cc7b8a8ab49e677966ada42bd9bc77bbcb52548db26656555d746654cd4d548dee8dfed6bc74aaad76c26a2577c771e35451a79f66775be47edd6b9c2afac4dd69cc34d2e6e1d4c999593303a5b23a05b3b8c989a6184a45ca260632f578ad568d58ada49b8038c9ddc91a77a30cb5b6456766570d04b65b9de2ee34dc032759e466515d6a9ee14b9aa24e51cfaaf917c4409e2672062b7fe61f5667c071b7e59c6411725e7733b8bb4db31fa5d30394f1dc3e537d2485937252f9f799eadeeff41f00cc4e5e8144e401ce9cb667ee99fb79505fde6e4d39af3b5c54560d70c600636c58720600e30600e25a031c19404766b189d3e1d8bc65c8a2c7e27ce2703250b90d4b4e8617a0d014e380393be14e24295bd51be7d5c9d4504e34e775a76611f4bac32b2b65b47938dd913164e16efba17abf4e8642d3bc3f989fc440847b303bbd56f212cc4e5262e8d91e88f6760c46bc09d299598c9a7a6ffc5a6907f393dbad69a7f50a8cc1ddbd002070f76076c2a558ef54a4a6252f404f2e9fdabce50ae072effd8eaa5f8d4200336e2b094005c4512501beb8bba76ae9ee444e12a008094318b9dc7f6ed545b9149b4652d9090943129ea6d88421e7bcb5053e4f45b3ee7f7a55b35eb3083b2a09830eb7fdb4f93baa56f3899a68cb348024f2006018c0922dafad9981fb87d5cfbda61dcc4eba760f05f3937c6a7328989f04b393bbfb70520067044065c392db38b6d5f9d4a60064701b96dcce3c0cc40171eee1d8bc05d0c331c95b003a5c0adde515884b53d4e98530dc8625e7b4e25f5e5f80c28625974f2356cd50396fdc030e350135bb7759ca5e318b9bac46b75b13076ce6ad984d71145ecf1e8b46e158d4871bca799d9f222d8e56519c8afef20fabf9d445ba204ed23b1549ef541477aaa8130e49c969d5516c56d7bc6a1ffee172da9efaf733adb02a8e5a35ca66271365cf9c535dab9b642894b7f50fc7629c14fe9d2afa2403832c4e0affb614fefd4c292743aa468b4029f999566eb71fa8330fdd6e4db833d52d10f159d5680a0406b3d39fa94e1264cfdc3359346a63b9b2667db61b842b9f7b4d2d4c5d01e1cadb9cb4669fddadeff5d9ee2f5b60727db64c9f7b2c93ee454969a234c5e65093925d1428515965e18cebb32d63c3920b66271c9b370b44d8b0e43666614a6d71fa6c370b394f1a6221088e5cc18caf2083bbef14d8360de5bcce118e123633ad00e48e74a23815cca06faa5a49052a1574f013452a3a511c4e8526157adcdd08509b4d55abb800a440b77a65773ec30440ca37f0b7519e8aa6a71a0021df4358f4d59687ee50958732a51086bbef53454a618cfb9959746b603ea57c9457cb14a4dcdd3f2aabd828052beeee4b494c4829f4649e9ad314f74c2f59fce714ed45bd6ae4450977f753a36b06ead4f69f1da2160d2545e934c5455e846c5872ac9ad1d434bdb8dc8625b7dbade6ad6a13d702208bf454710f001030716cde433b9ca198e8a9a243bd8d8f7e99454fad84dbe19c366177e6a127db027b1b1ff5361e3a5af389730f4bcf0f969f229f44a805e12c51d0e23be75d94db8ddd99c5e9edf633a5f24f9b6cab792a1ac440a0233f47bd8d7b278a4b536cb25bff4c2b272ab4ea7535fa995656bdae7928c52d6b62b20b165dbeb0fb87aa686963d13ff3aa55dddbedc6bd2eb50c388b4d68c104a112ac705b2ee7754ea4d7c1c96d28dc6bcf24b934c545b9c764095f4ab871c9226d379b7967fb6b7fa814fe7deeb11be562c5ddb96030d5c49494dbd856b3ecaa5fa73d1fb3e8ebac192aab3c15c5ba0fe2292372cb19bc85882d1f6c6991800449c24902e9b6339f39ab3abd027fa38fd53543695ed2dbd2148dfa60aa43f7d6c0cf3d76b77acd3c159f399afd55a32bf0c479e5a9287e157d9cdfe86fb4d7643b832970a3afe58cdb5835ef520b07b47871dbde40cd2e6d1c857fa78db4e0c842650ba669bbdb3b9b66deb897e54b8f25b308b96deff671fb94d72cee6e33710a5c620420caaa196acd6cabe29f89b2f8a7d916abf87bbc56af3df6b3eef71bfddc6b1ac188bb3b10afecabf987e2d4fc692611c0204500e3b6a8acfebaf2d09f66f57afec651f84fdc2b45d022e8705b9a6253ab1bfdbce670d909b33927cc0637da41b00a32e84250c8dda66a608bb34eb33aafb36616a7ff5b45f1cfa68da4c270f713e7f5836b6e9ffecc506836b314291d6c39b99f69e55433532eb33b8d83d71cf53b6727a45407521d90522e77b7e504759fd555eb709e457f37d9d09e995464e5d532aa8c4dd58f7b516ccf54f14e5e8bc8a81e33eed9c4c0fd3bfd4e264e1fff06e2de9a976c2afa39b66c43713eb77ba3faf34fefbc42256123a1beb8db782a7aaa390a54ce6d6b665374f7502e9baaff7ccd7e6fa3511fcc3d36274d31ceab51fa73af896c93b09d99cd2b30a7badce71e9b7b4d2d02da2377b7f154946df5fa3bfd411cf55a0da6a9cec651675ed1963cb399d932670eaaf8f75a37912cd0c9d4a9fe4f5394dd6beeb167cea6d96eb5fddc6b22592157d1df9ad97c7ed6ed219f7b4d2c496e2a5b4e529195dfe95f815299456d3c5bc6f9ac2377cf9dcdf9b76af6443130e7dd9f4d9cdc3a76fb3fbdee808f7fe3deb3e8675345f7a3b8d744ae65dc556cfe994f144aafc0cfe572af69f5e2eeb6536df5cee4a968eeb14ea64e3fb3fa97d72fd79e55ef78685e3394dee8ab267e3543a1bccfbda67564d5bc3feb5eb3ffb9d73494a6b8686807997ee36f5232b459f48742c12991d244eccee7cfb432349483a3e4675a192a87944869a2e08a5113dd6535eb7e8ab4bfcca2b89c7740228053c04d028f4820690677664dc49a77381f2735d0b30e5dcdad6bb7daeaa553cd5beb7077ff995238366df12e9fb86cae7aa79eb161c96d36ef8d71ecaa8139cdacbbeb70526dc2dd862597d9d6cc52f97663739ae2a51c089f33d39a9d4c9d0afdf8fcf0d096c249af9323f4e3d3bece9ad11dcaaaf9442a3af3a9b69a68aff9d76e559b6b56a3f4508e50fb3a422d4e7087b25a65b18af2d055aff96c75fea16caab52e43a1ac89711b1d8282b2699bb46674cded130e977541fc9372662a79ab1ab8f3eaf363828e4165bd119cec95ee7ebb39993abd4690bc1e6e3eabd5fdbf43ff37fab9d7f44972b855e37267ee6d544577bfe633455ba2bdda36103f30e1803bffb05a74bba91add6b86fa29d20ead7969e3d60c94c2bf550aff869448e15fc6e58d75433f455a2552f8a7d5a65f16a3772a92147663282caa22a9a81313dd2abab25246c114082c37ee2dad7929c6833c9348721e79667756717adde1cc0cdc3c66863a7574c861afb61493260e779bcd3684cda90e8bf3ca7b562bbd6d7f8a71da9a2936792a7aaaed2dcd5039c5dd7a1bf77412d09abde5bd66a8bcfe5e6dbd8d7bd704bf3fd7e7ceb82b12b7e07bb5ad9945713f9c57762bf913fd9d196acd3b4bc97975626ef477bb6d353b69f676db2d8b59a66caa38c5447aa722ad9ac5a78a93529c815152fc5bb5d20ee2a5bdda7878d4f676c3e51f5e77b75bfea1acdaaeb959ae0627867bee6fb967f1bf2ed24a3b7bb0837829e71d6e0731706f9c16ed8d53138725040c60c1c122bba2020856fc021f20e16e250c0fa2bcb7f1d1d09012335361badd32bab238e0c62c3e79fbc7621cabe67de2dc3bc2fd1469a1341027857f4ab82a36dc5514f78a501c6671939e8ad444e9e808d4ed8659dce4ccec46f79a97a83cd13b15294d75d19a7b6c141e4aa47593564ad3d6c98984cbab3671f9a799b2aa959a703e3a71ee39adf8b7661647fd1469d5287de2cebc7736f50a1ccaa72ea2a244d5282e2aab43eb518a59558b80aeba37340577ae380ad89e4842ad123353b9ddcc0c75bb9decc6013766f1d9a414c4663e712b0e977fedce2b50afe74f91f64c756fe7b5c7e2364e817c5a1cbb5774976aad63372a95713bc52a0fddac9a51206ae67528c74906fe4c2bafa384597c6236ff70f9b7e653cd5b0a95ef14c56c6ec2b651ce7caa19b85b26a728a6948a948772cecc3bd15557e2283133958d4371676fe3a3dbedc4b987339f7eb8a7df994540f73af43a27125ef192129e8a6a1edb6a1cf0b7f670c01f0ef86b71296635fb53a4e5eddfd20d282cc9dcc92846b83b6d2fbd9a73149b759f37ceaba9a21f4cbf8b0925b88f53eeee0088326e717712a4dcb38c10c5766696457f3f6d5e0e6c4e26d6e9f5b3ee556ce2ade2957d3503f56ba5ab64e4c4b9b714924961b8af59f76b55565212ce0e5177d6e7f3f0aad98d811fc4e9ce0a4e26f5d8ab8dc7c9d4a90e97596c63f34659542abf518f3dd1a853c54467de53328b4fe094a1330381e8caeedb6fe32825ee2ac89d6a060271ee7b2c1af56b46d315e73ff3ab39eb7e0f7103b83b17279f50c0d5fce3e58d3bf3d9a6a68aaaed0ecfe661d5bcf30afcac331ff85b7b7fe615a7663e59f487a659b8144f6c04dddddd0110358209eeaec5490e82f66a3bd5ec64621d2ea32bfbdbd1eafde1ee1ab8bb0b9084a0f2048893725e9b881c07a4223e8310216cdebb9d5e091122e4970cfda9e655d0e880318811003e168d4463391a2137f8c2ae7af3d4707203d6916a10e1c9dd57279d94c1e553ab3b43a12cfa63f1ca6e5dbb7976423e457a80763c4041673e2284441774c40709d06e67ee389d7b87d527764a359067b3ade66151de175e3859d29d195dd915c94809cfa94e87aa51904652ee7f9b80934d80f0a08a5935af79b7434fcdfe3e735675747a66d66c5776abdadca80dff4ef776fbaf6233b5f16359fc98c5e7ef208e4af19f79a33df64f15e35c6e534ce5763bb31a15259f3c15ed99d8cc55dcddc9dd7d2813b2480660eba4b58f2de8c7d6b3a33b7732bab264519493454f4e16a52c56cd5b57e6539bb9c94eeb75e9c4f908af78c9dd77ee46c0df7a946655c5bda2bdf3ca539180bff5e8c4f9c87c4a7afaa12bbbb39413e75ea937c8275e79c029ac4e99527ce22cc5ddf3e9c195558d4e9ca5f44c16ff369ac46a2d8529b39865e2a9280fef9d9358dddb40ac6e5c6631bb71419ce6f576fb657607374e4571cfc42b0616f1545445f12f4bc96d6e3be8992c066eb44910a7590a173bd410810734c0418b2bc884018112106eb0b6b88e80cd8ed8c00bef001142f58b05f438a18a2482d49c00c500b42f343f04a8c1082376bcd051001d4d78c087262438d11480252460b9d31c031dee8e43963d0f66275c8a4dcd833bee8728dc88cdc7e6b393aa7a45a1d41c029633eb3e98ea30dbfe1e8739420f983c8b792814be59292aabab19b4a8577474960c2df91ca913c5617719ca4dca1a85dd6528ffffea95115eea44719977669c93895796e562bd5848c82c51f219e0ce5433f14b865ecd298edde51477a67ae502710422186968f560f7f94b967c0e8e9a539bb96e5dcf0fad742809e3eef9d4ea0759e02fff2d9fdafc9cea70cfc47955db3ff3df52acae9955511a7c7e6797e6fd4b967ccf9679670613ba4026d9e2d4aa074d4845e55259f44a5eea442fd0dd734e26b9b2575b7065d5cce3c4bdd50077ffc0c99b177bb501f18f27e795d5f1ec34d56614566db5898b5a33943ed5bc66566d7551e6e1a533ef168759dc442a9f2a52e6e125b6c7437395dce6f60907fc9d3836a72906e2769bb6bfd64c71aba23829fcc32c6ec24377aa2e72f72b2044c0dd437012099933455b24b69f1dbd53d133ab3c74ef1e8bab90487a3d45f6ce6950c5bff36fc5bbd528545e976e08eede7352e8cc5e6dac89813f6d1eb101d98aec98f9d4e64e4eb1b977822cba62202914c6ddcffcf449364f4583a9aaffc4397dcdfe6651ddb36a464d74b76bf79f99dda1bded5e45e5a129ce22a09b6d751043f13353396ffd5a3efab8e47a0bcc3d57b3dbf29abbc271675cd7cf18e4220ce38f56e872ad46f15ecf11da3cf4dbc1a8c1f5569295d7b0aedf7b572b23612cf47baf181edddbbae1bd224b84f086b715e290722febb63cbc34363164b170607fc7301cbfdcf1de10765910de97afee188af95e5f786fe89a12da8437648de14541782f2bbc1ddc1cf78aa16b022bb135e3e2b8f7de104978c325de1a6e7859e1bda188bbe1087b19096577c8755737e292db0ac3d1478485e1cda901ba238c3e3127e252e8a3873bdc51c41957e1157d45804bc525fdfa1d6f7853705d21917bc355188af716b93cd77565611886aca23b240cc7f0f688b6d80e97904b43834718c35d853bee78c5ebe152f8b03b7a88b2fc739705d16913b8cec402a17859776c5d9a2bca1086ae9b7885c41b86e387b217ce5ddddbea197d80f4dc10c67285ad7b6f370c4320b2ee2b1c45a85b0bdb7bc59dfb8a85d7d5ba619bddb97d5afa60aa4631220329f042053a989962067463ce8e18915d3e3d214f7843e36324bd56cc8a38e17bb5f16ca0cef1591110664574f8ebce5f439b2bd471f72a1b5f517f6a3cccc03d24e21ee6b8873843dc4321ee6190f0c63dc4c03dacd1e01ed28ecc7c70dc7dd643b59466a80c05a89b663d59dc7f7a7572a218a84e89d2695aeed596673c5cfcf7c6c0bdda1a30b30161a3616633e16f3ba3ec9a817a7d9dbf6db4c809379d596dff6cf1ba53b589b36e6571d4ffa45182807177adba871ffe087db8ff037f1b29989dd0a417c29dca1336339d793b51a74841b193a426a6cc5371939a9d7053de6b3ef3a9cd1c3c832766d5cc54ce32c032cb40c73de753cd7b96d382a634c545ab36b31429c11367296b565ab27d5291954fb14ea71bbc2d47edb70555fc3bf475326fc8c66beea7489b6bf2b628ad3a39a1698edae9ffe5a8dfecd6bf34cd51bff1ebecb5d53b4dcbb7eddfad46570cf5515a75da50f60ee935dbe46d39698a59f477e8fbec16674531f06d2b8a819f93db5040477efe273be1f4c9bbbb6dab4ea686c2e624562bed9c9db0511a0a9356ca5258adaab828c8027f4639d5a148ac56caa90eab53ceece45433d3949b1f373af2cee78c86337b28248da5189cc6520b4e638904a7b124f562c96ca02ff80c9df219cafa0cade2337489cfd0209fa118f80c25dd3d87cbc60081936348c0c93114e0e418609c1c630527c79872720ca0936378e0e41847eecec36d76ccb878e1332e1df01997247cc6a5069f71e9e2332ebdf0cea8c941e306170770928b169ce4628b935cb04e7281c5492e969ce462c8492e3270920b1c4e72e162e5981141f11991139f1121f11951cd674438dc5d56c379cc3cc0f199073b7ce601cb6756b4f099950df8cc0a558dd80c1a319f05d9c0674192f82c888fcf82fcf059101a3e0be2f2d9cd183ebb4181cf6e46e0b31b277c7633c667370ef0d98d103ebb01e3b39b197c766382cf6e9e7c7683faec26049fdd308920cc7c28e572553a4df3d166b7eeb179831e4dfecbe70ef0b7d16ca5a66a9445576050f717871954d85cf190b5721bdcc3510c6fe859dcaa32b628e2331e400f69827868e489cf766ce1af13224561bab832dc7f2a1aeef8bfe629c0dd579fe940da19e21e66e1eeadcf72f0b093673946f7edb4e25fcf54b3eed5764dd2f4432f857f35331c533c331ce3942e37a4fca409fa0cd633bb81e3eeb4bcbe0ed678e0331b4af80ecb6736747077d267366070bfe9ce9fd990f2bfed5f4e730666367ab2ae89590d236635aeb8eff629efa12425415d8e8abbe778f1190d1df66acb3d139b978c2c331a2e0f69b3168038f76e2190d90c46650a13f087e2b4480a2dd9ccb41e65a6dda6c0b56592126c89782a7aa6fa8895329272aad9dcb9c98a73aad32216984db35433304a51545602fed6a335efb67ae6a60cf503f533ade0cedb4d9f2d931472368306f7d38819447cfdfd6eb7f5f79bd16c99d1f87e6be0dfd8bc5f77eea52426a4eb72779ecf6459f85e6dc0df467d323e8b18e9e1c138c847772403ed72deed76396807f8dbe8ce18eeeeba62784e75689ae29d8a94792bee15adab915314a326f7abc50b4440b708a8df1766b10eec981bfd991bfdcd6054bb6766f6a74d9baeedb1288f87e7b6812cdb7b16a7c04f72e635f378e8ca662874ab7997cfc73318103318101b479978b7b367341f771f9f2525eeaf2b3357162c70bf2bb82798b9a6662d23ee17eed70b1bf7cc5581fb4d8117c14a67ab31b428335b4d1911325bfdf8d87a767666a315ee6ea26cfef55874c5eeb7a7b7f1d1e5620b77bf7e51b0badf135c2dd8326fac22b9df2c2e0258ada5f8927b94999800f77d4de07eb170bf57b85f2bdc6f15ee970af73b85fb35e37e4be07e49e07e47e07e45e07e43e07ea570bf20b850b8df0fb85f0fb8df27dc6f07dcaf13ee9703eeb78cfbdd80fb6dc2fd6ac0fd66c0fd62c0fd32e17ec9b8df31eef702eed702eeb70277cfc272cd4e265e9b984cb56c41ee52c0fd4ec0fd4ac0fd46c0fd52b95f08b8df25dcef03dcaf12eed701eeb701ee9701ee3709f78b84fb5d80fb3de22ae016e17e8970bf43b85f21dc6f10ee1708f7fb83fb15e37e7d70bf61dc6f0fee9707f7bbc3cde1dee07ec12020e76eda60436fe64a786f6377a8993f591586dbf2daa628de61169faf772a921393aa898aa2b25254569272e6f32ca3b2526882099648f166513343fd99f103f10f174c338bd133ff7e682ef75b3535fb362c5238e976cb81907527dae46d1b43795b8692ca2ddbfe3eeb723b97dbb9b7ed9c33eb0459546d7f3f6de26ce0ebb05a69d54825ab95369a1a4565275a37eda227b613cec0dfbd9587753a2cc600dc1da8817bb5adbfdf115bcf4ee6a978dd5151bc9353a735ff76744ee699cf75cd6218c1281526314226281b88d7a6dec647454b5376077baf598d8a12cc4f80dbc9d429d199d98da1e41fda348555f36fe7532b611637d939d5a13cb585b2539c79786907f313562a2bed3054e0fe5384a969af365c18a32bedb2051970b5dde5b58716d0d002562aebfcebfcadd5dd6e9b55b3aa813f5dc453d13d65af369ead6a74af1a8c1fc0f8e27f3b55d4e975fe7663d70ca56a130c11c0e8c03178167f545657fdcb7be326358ba0572753ce9c4f1535779682744a214a3a736fe394a9caca626096a24e39d9cc6e0d85dd6d933347e9533562010b9490f6ce1654a148e124db0f65775ef5ee54f3d3b29ad367f1b3e8da6a28bc7bfcbb346fdde46f5945d3ff6996d5446f437f6dd9cc0be2df6e7510e96dab66cd0cf54cbac93f7e60fb6a06ea9bd346d7b5d5c167f1ebfdfa5c33548ed2477f669c5eb308b8bcb666de2a9afbace657b1a9a3d72cc21309a629ce2b916f75f077484f8b22c5e883572a8f9a5e8a5153692465833722fa26a629496f5333500373767acd2abded54793a98a9fd9b515314a5b7055355e772bbcd05577ca29ba7ea147f2ef75b67ef763a9f7865334af4b65c4eef14b7da83cfe572afe51fb3b8c9ebbcceedf63a4a7e688f098893987ec9e755b34cff3fc2bf5673b99cd163f03f2c95a150d6c4f97cfd3b6dfbbc59546d9f8b2fb808e3eeb62143fe76a2389dbdcbeb07559ca475d3897313269e8ab2688aeaf0a966a094918a2405ca1648b8afb69d9f29b56676a3600cffdb3eb314ea04054ae07ca2600477dfab4d770206f89a7fadd99e797d157d13e56928526cd3edb66fb75b030d57d5a2c9f76ae32902654af1dc19dcdd750fc0a41405a9a948690a07382b65925413153303999cccdc2bda4953ac772a8a45cf84005c918515445cf1451c5d620d11932cdcd31417ad7a5dab08c3736c0bcc4da9a206cfaad6a68e932a3ca8a2c7dd79e86f9455755614af2d96ca38bcb6f8674ab9eb9d8ab4b44bbcb6252b6584d7b6d467cbe4ee3fd34a91bbbb794df7ab73bff8e6dcfd4cf5d146954a6085bb7b09a0f050872ed15d54318013b8fb950246050751b8a0c40c77173d004af9c10336c63038f8b054c3016382dcfd6a200010a27c49a10625dcfdaa80022039eb4ac90fee1e4231d2e00a1586a8d00277176fb850260531c8a08413eece3a0001c2b84fdcca60e2eea1152c7821cb18315411e4eee30cb996017a18f095c0ddef13ba9f0a5c61e16904ee49b81b8238850458b85f2cee7eef1577c77bb5fd4c291e9ecd9ad9a7d7a59f29b56660b0b7d78cae2bd6dd6e3f538a044ddcbdc9ffd071bf41777337e837c965e237670454dc7b6c423eb69e1d56eb9587ea5eab48493bc996cbbd66598cfb3f55144b6e33e540f8bca7bc4d07cdb178d54536a60fae192a97575d64cb6f33b2a1c0324d71918d6d812a9acbd9896eca7dce3bbdb3e59dcee3df6914582ee5bcc3691687cb79a777aa9a5356aa4581a52df79a6cb9dc4e7fde40ef54a49d8141b6c849285cc0aa185d77d1c9a41a65a6131361a214c54e98a0d0e2eeceaa190a8a2477b7b56afedcaa73afa96927155951d1cfbd2a6fc3cf4a6515ff3e5fefb47c5b2e77aedafc9d0ba679c529caaaed03f12f88d9fd3bf79a5e0aff4cf487d5e099eaad966f63511c95db35d55af74116ddc159ddd87c154d9255fd2cfedfbf75f7b63f52e4c81120ad7e945e79bff3afadf2b65cee734e517238af2a56738f7d9d14b74f3a8a457a9b7e76bf0e4f457fdacc2afa3baa56794ea43503b30ee96df8dfb6d176cd5051aa9c98e8675a393151931359dcdf7c9d3001e21691c423277a241559d9594a2ab2f23c155df2b08a8734bb0566fc4986a8d8144de0eea993160080bb73004b3065d59ca2fb45d07fdb400949494a5945e370b655ef3eea59dc6353b4d5e55ece7c5cf4b635ef56bd6b3a338f879b6c6b3e57fd3988e6dfaa776b06a6298bb512fe8de61ebb733976ab28e6e1df59dda16b865adbfdd909ff5aadc37fbebae29c6a0d7c270da5a2bac7e697629b70ffe975a78126fc83d949fd6df487b240157d56ab28b0c46b5bdaf0dae2efb168d4aaf7ce66561f69a97cdbce288b6375ba37bab39a79c020dbea14bd0daf6dc9ea74b3688acfbca2fb5120fe5c6e3b99b95764d3ec07570cb57736276f63b5faabfe1dfa3babf851e06f236d1458fec6fd069bc521fd0e67282c0ef743f7c6e96f1cc5e24e74ef34c5df33d9ec6467738773ca6abd4a494ac2adbf9f6dbf94a424a634c545b79b894e41816595283614887fcd4eab8ec2ed76dab666a78dd9dd8ee977803f536ac7023deeae5fab7f020d54ec607e62438198e933f828bca229cf66a2a9ae05fe994f23286511939a596caada94f29f13dab16dee4f34ebce9cb7db300af567de1a28953f989d7a2c1a9577304db16633bbc19ff9979b14996a9009f8db481ba9c9cfb402fc9956ce968969d55bef5424e00fb33bffd0a6d76152b26a27698a8b525c74bbc1703b9f38359f27dac3a1408c03eedb6d97e67dbb659ca906f7aad7f5974fe06febb3dd79e78d5bb589dbb82156cd5046b7db101058eaa6dbede6377ebcb6a512bcb698015930608cbb3739917e0f6100149a4d06f41870c4805a3934a4c4cc5492200166f1f9423b3bb64d822a09306ee6f5374862cafdcc5039092bee49e0f88ac499bc43331214e0fd0fca88cd1be82ced1f5e311089251b96dc0f2ae3348b84101c9b7927faf43b7fad8a5b809714085c0013db02dc461e31e6779a8ef8e26e638fe8f933ab2866556d6e7c84ab34420b77efc0492364b0a9edfa9a45a336aad3a9ee813f6dfec63d2398f0d0ddd63d2336aa95144004b05440ce6d6abb7e93027ab45202ca6c16ede915f8c174290102603f9fda4c8090fb4e80cb318b4f361751c6ddfd54731139382eb3ad0e77e6dd0ea518a779b873e808af184844048870b2e5742f2af7419d4ecebb57d11f1065733ab99c77b8dce7bc2b73dee1528cb26bcebb9c773876b7be13e71488b243434a72768294773855a3bb4d4d29fc1b628cdbb0e4706cde438061551497a1704e87003a9bf7104c36c60d2184199c1048d8b0e470e78951dca96addc69d284e8825ed7b2170dcdd2684e735474565a520ccb0651049b8bb07f145eb3e6806910444186e63f3de6fdbc1ec849e1f3cf10341e540c800448fcd412a400041fe60c66dbd1f88c869fa83d4899e2a363f2aef5eeb0ce587a3ddf4c3fb9ad5b48552915e1573464c122ec64bc6fd4c29314b62481fcc1061c392f32178e6dd923e90ee6efb1c268ca4222b61a2f03049ec364ccef661706c3a743f9672f46a19c65d7f5291951a277bc8c26d3d9471774cd443183fd5bcc91e729a5dea81c86d2726ea6ddce3218cdac4831577dba9e28df2407eee35ed4065fb1dbc7894569dd476079e55efd4bcd19d7e1d5860c392c36513855af39eca3b1dacf8d6a1c735bb7570b9e750c69bfc975fb4c389be8aa46ab307c8e7280726265e35ee27cd8104b669aa571c4ab0f37a9e1807fc6d1487c311387471c772e5757068e2b616071d1c6458aefc893e08576c4945565835ff74d2edd614a5d51ba8c87bb79afde97597f3babb41067777f286255bceaa5128dccbba8dfe6e78b7e9c09871304380c96d9d3edbea57792aaad3c4e8a35addd6bda807f3b69c53b30828e3b61c04806155f48780292b41ddb3799708e8b1210c6c4314b6d38629f4ccab49da20e4ee9ac5f9d30677b7e575574318d7e9f2f9582797fba0eecf14b3bac98bedcc51b8c79235e4b03ba3385f830d1a3040831430c8a22d6fc56f3b556de6fdbf7154fe691a8e7a3cb4a7a34283cb53bc59f444db2734fd19a83cc5281ba332a8842f0cf6c11e0699526866602000800063120030482c180f874332b1649794d23c14800479b8667e4e1bc9c324c9610a21630821040000100000801999190500b3a7fc47ad73caaf5c9c190513f30c0e397946f1a71ef93bf0e3e680b82f1d64aa1dbfed325cbff1af2caba14371e6a80977f16f7a0e00d27984d8d2d0e5a1791d98afc38c15abd50fbecfc47e8ceeed87ee497f307f55797cb3afab76402c5c6a343b195ce3011ed949ccfd208b64f35617b10fc4763ced1068f00022944757630abf9fe9f012d63188436e1e9b126439ea20f07aa5ca98d2cfa6dd1a77d297b1e9a8f33f456f3fbea2d121f519bd1b980425407e8dc5ec1bf65505260989c0c3f6309967eadc5a9f1b56c1b7cf46c7aebac00fc50b3a6d5ed5db0746fe8acd167ab3e9b7d446fd34c198e8b7e71cfa157899e718bd44b0d1394c9681df26cd164eaac029f587a09ed540debb37d58233f72bfbc7f66202332c801d63c463d7ba9df259a47d8f0bfe22004910f135d8b3604544a0d309211159db86eb1b74061de89e987fca4fd4c64cbd15729d452f4f6fcf5b4d7f4b6f3fd255829ed27b697b33d64cef3c12b6a7a71b948a041dd5365d66be67f7f07ffe60ee1d4e9d2ad8a7811db63310d27ac05f844e15052536c4453f28ba6c84d3319a71fe564db5ef04882a16ef4ec8de943372b647d64c8874a01b9b30be87d1898d5d733de84dd5510ddf46ad396151fc11164d2a751a44526f0d6ae4a9ab8fc425d67004638196cfcd4582bb343995cc3b9dc203a81a6c9696da1a8ae0133cc0e1a43f785cf851a0bc5eb931e29c10949fd98714f18b86b6a9031cf0200995f9b0b72bd83e7b1987bf95060edd7001c177707c222b85d7378c3ab9fb767e57020d2e3bc8c44240c90450164e07e733808550bae1ff6c374a7c7383fa621647178446ed62c6e2db505f14ceeeb941bbef98f1fcbc0942a88997e069c49b44dabad5aa7cdaaa94efe3f859e419d5398afe4cc2344e9312f692b6e2f87792cce261d4625f2271b822f2cfabe94596b909f7029114a835c5660f4893abdc69a87f4f5b84536684f7a97e399cacbe039226aec19c4ef6649d044fa6cfb0460aa059cf29a52bf563df25cf5dc749d85136264bfe3727a1987f470e730451515802b9fd568e9ad65aa1e06d1f091aa8438545370f4f3b71180069567299092d830f1635406296d43c70520a8b49153d362860fe22eb45d715a89aac474163f56d8161ed9d226a72c4d04584340b69ae10c1fb63805570023a5642084f99361d50b6967dbde60a934b9f460451974fbd66b673ec789214386ecbdffd72c3da208a0e94e8e76d80c2f66cdac444ec23370b425b923b618863f9d5cb462dd3a6b28826ad6df04860e598628b1d1d957dd7389f4212e5b730d5cd48ed35d473c695cbf7848e1c7deb8221c8e48eaa710d8f52ee539f0723ffae164f2b988a2972682b570d04ee87deeff04b2e9904c36822ffd1087fb0963dd294a03f50a5d59c72abe3a9aa9998555992a9ae29b79f99739777e5ee0fb6f36374af217e924d7bc7d88f34a3e25ac8589366fe9043bf453fd397d07441e4b5f44a4cfed0259446a7f90e336d0f0c117578f5db78e479fcf7a119debfe6b670fdecfc8de80f05def8fdcfa8f7e70233279f2a965fbe47f0db33c5ebdb13e6496f7af95e48ac823f650324311dfc53a7c8ebd6f3d083d9f67459ade9936f6dffe75e1e316d386fc1f7655fdfd3cb0fd8df5bdb41a03b6574c6375bef8db4e72512c43f1cb048346191f1388a86f78aacdf8904ec0857c0d7d171c58ef37cb1df8b50703ea89e8faec4eea8818a4e8c38874710035c459fa0f9fddd0a4a6372900991a0869d801a2f49095727557289006c280b1335f362f1cffc906a96ad1afff6598b6c399b8e757f13148df5b3db35a6b969ed09b49bda386e5ffa15ec6c7a1165add8f68ea518c227194ab913787e540b25823ddf89956af5dd1db5ea83123751e80d7d8861248184319758af82eb0718fba6a838322229488d2ea319596c214e79d5a572891df49882e6ed4c80a5a452a4a82082e56aff8a41964c26531b2b84e368c712a40733e467351b6718f9a278c13aa1ae195cb71e1e08d4c88e10ec4e4e96ffb9f01abd4cfe0523b90bf46ea5ec27ee68eca7adc41813db529681220acdc21338951c0bcf484f9cd26e744212b14fc7f77b95edfe1f9382d36fc84fe157962f0313f7926405dbf50be481203cfe878b123f60ee0a4edfbb2507099d09355947bc7f48aff6e3f407a9ceff0fbfd861431246fe54e49eafe12e4154019fc07d2c1f9a328b62f852c15ab8dee0e73f173def30f3351000d4b0e8af003e718af6280cbc34e0d064c20b24b25f14c775353311e093f39c7e32e555ef289bc4fe1bf332b598c88663200edb9043984316b6f6e00beaa74c8d5040aa77c6bde47c93e267fc42e0075ba72685c4e310dcb4e86c5085feafd247879f8c2980607d48e9f445af90bfb1f5016c8cb4773a36005343665216f0a6fb19c9e0dca6b58a6cca5f7cbe1285739bc5dc1b8d8455ed4c1f72bb43313298c1dc7fbd44ee2a3e7f4b05a57a8ef26d4d5773b93f7302cc27fc839d82fa622a7e2b58c469b749adea8958cc877fa72336c1c203b6968eb2dab384f3963a286dee4299a068e3b2d0e53641fcd4c051f789a8661991b70ce49f91af353591b657e8aaf55403bac4ab0b566b8c5a4b18ce2e9ccb47072af566ae47ce0101babbba05a1d8e0c3f71ae8eed943201f0b368fedf8f2417722b501a08ebbfae77503e8046970658e6062b19bbafd526bfdaf941d00db65b07a18307b60ba8559b8fd3c3a5be63eb0d5a500d66705b39e604f31f89af61c7c6264d5fb612d80fba1da07ebed0fd45a83bba9615afae49a925bcce31b7d65c707c7cc437e56b918da909ba29cc1da5aaa2069027551363c970dfec1904d3180bb797c06cecac88a109ac07370315d39a2cc9d999f10cf748121bea30865ff055852a13a4779ef893c6df5888c1662a3106b58a921dfca8f69dfe4446cf0ec4b46e026615534c66b0c7d079578a74db2c43300518a2005b10ae517f382d31da99b20e83ce282fb925e00c200acc0f8d34131a744c48617afbf0b0bd5dd25f0a6b63e90ef922b49fbe2fe42201c987dac5410f28726c318fed7f179d5fa3389371589797567772fc3e977eead1d23fe0e2beaf2e1f36c0c916e14ef59df253e163697b91b2e0c594c75a46ff33deabf3301788e22e5ac73e1ddd7f5714a82195c0af1b7b7442de75050dd3361799fffa07fb4a9fdd982b6b25d411fbfe924b2efdb9f17b672304fbbf7bfa0313b3b3def1a8b7f364d7406f7fb9808e76a1e95c536fc0fef01fbc537ef4557a8eb4cb0fec496d6df77ae530a7b81dba37cfdbf0d6b2749c2ebf65eb3cf8ecc74af6d564e3a47cdb546ef3b4b912b87c5c97536c54c82d27aecf32c83753d5aa8bd94bd9e6dbc230c775abf34339deb4acac3bb40e2ef79c20e01fbb5e559aa6d6a5f424110fc7ceccbf791cf78713e67d06b9f965e81fdcb8f14e41e66a8d57271a7eb08407d5d00e8e2ddc0f5dcb734a51c6de8dce8c00bb0357ecbf10ac8d3475bebe5489ea3f060c6b70d7cd1e01e28973b80ee6fc82c90d191b1e58d59a3ff8698048f46382d207522efd8c25a868459ec9fa9f79d77316a842206a7398e071974a4e393ffbc04d4874476a089ad879eeab5efb75329c68b971d14ff500e7da328a7a69085569d5fd5cc07b153816d82a0620746b7bf042c4ee8b38c4baf1ae083a8bb63d66a9bca00aa7ae07889718aaccc22d9413d7d57c5edf51320d482e3f881df11bf13737fea9b4e7e5695e4871eed0bb6537168bd96cc86830d61f796333141ca4b6a339bbe0ee2845cfb865cc8e5f01e12206627c27235dffb807af2ddc5d4132c09ecceaf5a92a45b5dff58d7887b5ca9022b93b734cda641456143c117a5297a10a76c1682e6183a3a522eb07a56c05ed5d5c39a43c2677a627de6c5da5fcd3b5fa326bf7257ce92591a597d24f0d88ab88dfe2b84d7a0be0e51ca0f2d382f22a48489e6099c36d9986252edb4394e43314a196fc4cb04d15d0b2050618a29997f25e51526c9f027962632a290ce9d11455b42890dc4261149810929c5dbd6b85d787eeca2a1acbd0cdfc465868295b5754fdd124ae7ee6d68c8476084be2919835d4bc0b5a4675ad802f560f606aca724990f5b67b7fb0804761d93fb0d8952a7e1ba9592d8be849562a37d933ac6af633d17de65f5d8b948855063a2f57450757528c958f6f23b8a3973b49295e0734de1722838ee4a3c1eacac9cf95249489010c49bdb1ded3fd6f74b02d56dea7372a681fb2f74532fb7d9a7a3f3f645103e21b33373a79ef3d8b3b719517e4fe78bcb0af7eac14dada578357a165e5a719f2f91afd2632cdbcacca9041ba2749c4f5957b6ced92c5ac5d1da363e1871b5ca2fe7dbddab5c0dc8818870625b8b95fadf0a39763762001edc09b0aed80cd251316eeabbac2c01a5645fe5d1f7ea355aaecc12aee4299fa448cd78fc74361b036e6e081c530641575ad935b63de29584bf7246c3e567b2720511c871558a5248f5d81998d95cf2229bd402fff7a51104b201e79fd6116e478d54f995469442952146e3f8a98704f28580e021b301e800babd3fc436c32656a12a0d1a3800093b79a3501fb5327830b80b7825ab7e144718da2306d0fa7b0c97f44fdff8e2ac25fbb5fe4181221c25d623c18e9a4bee2431d0a25b322bcd1f568bdf642b2312e8c22cebc59bf0c1166aea22292d4f8b1c101c847da3d8bcfef57088e105c5e33c8d32e9373c17e41cf2dbc0994df08eeb35e3ca2237c7bc22b2bec3149f46077c9abd9c754aee84e0f19d56a13470b1a890930b9c7d2c3fdea68aae37797533e43007e6c30cbb2de62140df29e9e92c48568e63ed9b2976b2f1ea65bda6d0e06a5ebcd08881fdbc81efdea375e44b9e24d549d4f986ae308130c272c35ae5cbe8c2383c40d2bbde1916a1683fc7434af0c82b54fcceafdc3d34f9a800d7213a11f0ca00aba8965636fb04efa8b3a745039ba7d91fb43b8856ed34f27cb6c02cb21d5883a61d68c4367b3361b703834439d15e5b53dc0ccd7be6e4dfe1f08a2fa1055c4f7053ba3a209e855985d931a9f07d2c39e21b74e04ec806529ec1a9c92458740ab4196dfe419a748ed15ae44a917d22b3e70d1867e541aceec07877d77ac021ceb9ef059f32ab16b8da929a2f49a5975a2506c547364861204b3060524b302dcc2dc354290374b505e0660184285a4254f649bb6148abc334bc2898fc4714426d3710c5ab6aaa37d692cd9f39e3fc49984039656a99b9f0d949d73cc3f78a698f5ce82dd56f2d653843b06298c8ba822bca0f1d11bf47ac060d09d145b1427ff0fd47387e1a19d303105edf1e3d5f391fb31ab96e0f9ba21db7085717e503b363035c7e95f9141124e23ff2819ef186476c9fc386335d54c4af07db3b26dc047d4c1fbfd14e87c56e07ac35c70711d4cfbf4eebc7c42cc80ff5119de2f33e10e4fb0503773355fa69ab2c63d79d30f4cbbd1ca5c1b96605ade28a8c2f60b9705cf341c36206de63b1c146324ce8bc91850d3c32fbc27555189814ee6b82744215a154759dfbd44727060d47d928e93a9cd046321acff6288e0e2720ce01428da23c0e60c0e184b076d15f238dde4783011ed3ddc764ff77804e9549f73fac910f6bbeca69e9e7f00cb2362067d759fc658f9b9e695c7993f825c88af54c30bc72fb34eabdafb9ee41e30b00ec32d4ffbd9fd8de819cf950de1affc25d8ea68453b7cc1fa6e4d49a4aad2915d3bf3c021638f749883e093288d5d3b1352fc7e0cbada7ff249cd89c762da19720bafe74a50b92feb094a0e9098f19ebe1d9688461fb7be6624a6a44478f911177b1073c47323f856695f789dc8b9ba12988f6516a509d377199dcac8d2b647ec590aca31f14e648332a249a4eb4e44a7b6ca8837cd2cd264bfd28c17f2365545cc94d6fd8fc42c0682ac90746c9336b0026bf991b11a0b2c2cf757fe5a10876ca19d83a7f27ed82faf89992c3266d64ef79b0a0dd9d29685235ffcc9b2d7b1e5778f807817b351ba5535c68db606daafea8a319e2775d73f6af13d11eb17a369f24f8e3787cad1da989a7896dff2b85739932099091ea8c0c1d664bc6a9e0f875508c37e3d6fc2a901fa67fe7d2b7ec7980ea48143e01bffd49f1d4282b713406d3a9555a27b75546c890fd9bbae598aec7a3203bf8cca3144631a8ad85450dd6b47cfd07abfa9dc5787adf2766c66ee008a2f51dceba7197a11f730ec74fa30f4b86f4ec2f61b3545124b10f943661ee47e85080db80dad44736051a70acf034aafa1d87d983a5c18369b4d121781e171729911c2235cc3015d699669a6f61b6168874f919aae9e69719657a9330f35a2bde8b11da74413d3afafda6d4cc595ba604a958b04c82a25a1c34608da61f5b06b004c29a50eedee59df1108820d7a709310f4d1a8d021cbe5188726292695945916aaea9203d709fdcbc5f6741663820357ba5e46b6af56cd6aac5f7ac1d9bf760126ca25457ca0a210cb705495f54e3d405d238b73d79a8c143840f872148ef5c29202b0027b217134b1ae3c9e7457f1011d6ce3439eda008042c8596f340f19e212fbeddc200bcc1892a0158d07fbbc01b22749f12313bd0691505cc2ec75371677bfad78ae099e2d014a35c5d5e35b1e78e0bec1a0b05c7d99cda79374959a96c7ebe64e1d6530eb5b79ecfd4f3ca42b8397c07e0750751437c2e1749ba366061784944790bd29e7ecb7e50119069ca8adb5d6a124b0c46c55c1814a3e3ea29d8c64b5115158a7a6a900e6f55047b1011ea8b3cfa894010654fd45bc0ccd6ed8a7c00bcd141314605c03e538db39ae48bfadf7048aeadb5607253e7a60d221272c61202082a4156720065513ff6ae9ed6f7353de5d44104010d3dbe5bd1715701572d671f1f9f79e0249dc1fd5d0ee1d6887ee80512e169859a04188fbac658cc5eabf8f588b2358b65a4b34f416082824e3d993315ba3364045b4faa0981352d9472824b2b201ce733eb6ea35286e72e20fd0cfe8855a5ea1263014d2e72970b329e3fe053c35bf67884df561ff1b060a37d772cedd6dbf2f8a8e2ef75c8ca9b5c1834117dad75bc5cfca08a05f9af99c33ff5481bc2883364c2c0dcf56cbef24b4fc305797c3089816c84825c862e2f09cd613335a7108b9fc65d4b811dc23199e13ed16baa58460a00b41af24d5d74a8fc36f366337fcb5e932101b2813a2d627cd17295bec7b2a24f6037f9fdf3aaaea376d8ff397d01a02a609833f4f60a0bcc1255e0b6c75d94f602071b717db7f0ce29dff6fc0068f62ad40926b6abcd8fbeae03329fadef63ea3c0c7e0d5c2764efd39e379e7a085c6470d4650af6517a3b2057d7b89a0d9188902ce97276446cbfa7477be2da14895de7cc25f7172fba7688b096de2a9698af3d79fda489f676b8c7a3add6057c7f1a75a69c7e728ad0df2c54df7d8c0355388bc3359b21e0d1398e9d43c88c96ec9665c315df8b017516f5531717877e2a5f22a8994b6d7936237cc5341575b7fc82729599a8ef831edd006269131c90a95e85274a1ea23c06f4a2be28458d5423bcd519626cb530a6e90c856541bcc6df3b95a5dd4187ef929990a2d91416aa88e732b95640bf08ca56c86eee0f5e5ba9002468128896e226e193c136263747854980ee6b8f8b1d164b8e34e075a6cdb5e33a647a317a94857bda442c9dec5f6ee81d6051868289f03336eeb8b0f8fbaedb4f8662916ddf5e08c5d4c9097e97a8ff5f1dc3a109da90f790849c141d36c8ab34f5c1eebcadf116af8c4cfe54a6449712ffff0ad04656288d992be44cea48b2900d4eff2158b35dd01cc551b25694aab787efeb1139780827b0672d9e2f95e9106508a9114d364e89749da1acdc5f9442f0e13b1aafda4125837c2a411ddf981a129154300d902a9608556e1b8a6980d0c38e4a89e2f1fd02becf40124af4314a9d2f1cf272deba00d5d622e892d34ff9b6d7685dd626cb994f6b0df5ffe52823972c4b301a40bb9eee55435d280f498d867cc7e52b52df03626307f53c8ee22e303a7a40e5057ea5fc31e0da594a0393c1910b63032166d4a6a3f5f3420c06f8e88101d471922404fe418c5d11f22241b8051d14fc5a9b21311e4f556a4f48c2d8fec7bfe6100af6ca6394f144b92f95a592ed6f4b40253438733245355d1573095c3ff357b4243bf1b1cf30fb99d12e1f0068d163ccd4282e2577b31595423010affded414714bb51ded7b855ce7a9d7810fef5d9c5d170491e95527560e22ab210a335b6dd1b704e479a929d4a04a81e0b2ca7e916c0ba09324a04b5c12ba148da63d016428a3caa14c877d8cf8c6c6c3104c13ec04850e73fb6b02404ef9cc7cbbda74d761c81044d4710b60dfce904d30bb5279273f3fe07ff16b6cb268db10e3e942ece40e4735b20f39604b92b5a9c7c777d885ca73eade980102d1708fd563fe66aac2826169758054662f1525d7956ad7718af23934e8d635d8000332e454eb76d4d3f131173822001d9d63ab661984e652099df8ae3a77fb03a7a8b6e052e337dba85cde0b3f9b75861a05296ed542b20444997614bcccc6659e8ffee196949dba2219d5b58405cd4056fbd1804f6a064e213fd27867ff8b77c1d771a3d40a5952b9af54ae3131919951b9d864323d624cb997c6e582e9a2455005d092cf54ccc7d84d626a13531a83a488ddd783bdea1a2c585c89d4955090ff54209d8e902dfab22fdfe9e555079be8b6a952262bb89ae1a0a06d6c73031665b87e8560d62d71674b9f88cd2419f898fb08b386b9dcc2e41750ff8f169a95a88d35125ff1cdf177e99a52bf7f911242e0bc080106399870e37511defa5808a9fc656e8898370e479f990c5ba90c54c264008287fdfa0a0de12aa5b878918266d14b6d4edcdc9ac6026148b9e71161950bcfe5266b2d2de8426799ba5d6318b1855e8b72a0f2539fff678de0f58eb7e1cb50280aad645d28b7c097b09a964f2c872b963ecd1cf6b7722001b79368365212ac221481bafbfbc532220d6724c1b613fb0ffd080e180e834882cee08f70093b85618fd7e44d5b718d36ab2339d632401e4476ab34e551f0d3bb074e8b0e35a5eebb93aa2705dff7e29ba81f6c0fabb5224a627074c4f18162e82afa01da71ae826fc10bc4fa68f36587b8950c49d275d0966200b38cf8a4dbd634c6e6a61a504c551242be2e1d8f0ccc7eca98cbc26367bf801c2df9b3c64cb8cbfc32d6533d48307d2a65785b816687d0102582725c8814bd3128b86156074f6bd5061450b9159a9be5b9b82e2b862973b5bd5f45623783e7e643d9eb2bcb9029207d232d862c23cd560846408f703b4493b1d79aef8fd52071e02ecf3ced581f7296e960bbed8ba5a577fce1ae68bddeb08fa70d1868970feb8f0afa53679b79b09ba38e2b67e6c76f822fc93f361e8bb2d1bea2b5046177b65e55884bca4eb73a03f8ddd2ac277979e5ca87eb2ef2a02deed47ee8d3e2b6ed9268d9ab2e5d7dc62a46d403f7c4fccaef76820920b40e418c0c8ccf0548373c933099cf750dd59bb5582a54ec4ed93c8bf54b7022183ed366c3bae36d5103a3996140d5c32ed571a62dedc461fe847908453ef004db99e21e9b8225a58ae6e4d9b13fb542e5ec3f4949f875e5495508f3e4c3d3f1528c95a0546a7bbfa49c457a5133533aeaceeecd389c4f20ee9eb6aefffc89bee033bc8ada6c2224dd7f5bcde67d05036dc07ff6a72ba73806f6d9cbbb7a9859164b5503358c444bf16fdb79cdadf1998e63356649d40c9a9592e3c291bdf905423677117f98427571bccb7fecf0674baadc74541801dca044a16994c4c1b798f686f75b5435d074637c45b7f16377d8da397a9d6268106d53c413e6d7000f1b4c98354e79b60a7a8c0450afa58a3bdba265a0320cc5992f5a0135005ca2d73b96d5a11b4d02c2439c6c12cb7b1ac06017daf690804a919f62e04ed97a00a7e9e5e4b0944be0dcb32f412daa1f27a040a284f69aa80d3658090ba803a3269ff1770e80df4c1bebdc5151668ee83b10b4af7a3e722a2f6b284ef173633c184a235e60d7a9a8d06c5ecd9c22133e2149ceb83e48ad8be3229f52354ed195a7a07120e8dace831e1a5f02a7531421584c7a1c255ac8fe08a83c9c26c8b7e7f73de297c9b7ce005299dcccc4a8088ddf82e50011d3cef51fbc0e7d677e1c6f90653534002a7d4ff8b73bd67fff16fc8ce5cdc5b43b5288c41399f3d271ad6b4b64bf866bb1a625f9148ce5cb0d50d2f414028fe0c5877ab3cade436bc08da62c4df1603e46301532f1acf7ae3a6dd57abefb2ce659647669de1c60d7ce2e1d8d874ec2342d60d2a1213854b6dd58c47e3fe0472540d964a4fef1ee9ea9d79abaa65e13ead8c16ac4a1ac10a2cad316f3ac61d14b41ed6ddb466fde095f9a808bf61731d927e99153c364fec9000c8d2fc24c8fc15d506a3001a22cc9886c002a980c946bbec59c32db53117e2dfff7edc9663db007dfb521710f1036efcbf8751c9c4ad33338d7195a8d152c075269e988f0a34536edb82a4c445393d4c02cec0480be6ec6b1df3705a937d8610ec7bb2bc190a2bcb28df1467d8004ca6215e0fad6b2926b3e2fe43d0bd2662e459cca6602348e6e82e4f251398a175753a4e82b40441cdcd845d53a8eac214de5f2dd20a192716e6aa603efab909e5ceb7750be1f3396b6c9327f30ea637c93a0f662a78b7793234ad5481be500d0b00a96ef481119aa3164dedfef5813b0133da5e577c5a9a752b7529103678d75ff0a3d66c66b820d4c9cc104a64e5019f64ad41d13b3994f85dd9a8e9d76a27fd8b1754c9d9228f672bb1a69735f5408f6f57662a9c7049a66a04114653a1770ad4ad13346dde17863a7e77b84f70dedc29009399e3c217f7d675b654f47596b96075aa0cadd37abd90ea00dbac6a6a5bab70b72445e1e069fada9386db01ee354a5b06ab52de1ca8615d5a2d4d7c2a1751877ed31901cfb26c8747b2c31c4a48626ea7cc1dda37a6744f5797235aaa1340588fe9b9220aef705b465c1970633cb6839f0b90b45b118e0c13deb4e893fbfe6bb07e6f8acd0de5afd6753f8146e2e2083cebd24139757639bf7dcbd461a24a54d924d2ca01664ba86fbf00f7e1a37f730b6e5c97dde91ba81ffd1e7ef07ce8bb8f7f62994af00f106404ce5a2c2ead4edf8d344ba4215d534e4bf8cf39682c0d4ab7c02b2ee0ae0aa4855015ad7b6a99823ac85dacedef09433a4528204daf4b2d7c47b78f6b4e0e219b790d628966b07f01ccf22c16dab86e6a395b879efa448f73f25638443b9ceebdf252fe7a96f00470f14e8599ebd0e7f1b56fb574f65a12cc0a959e9df4d43f32fcce9686b7a5d9850804ecce47f7ad3346b7eefdca3d78ac386b51393cd387e33f67cabd2af7024dcd4914b46541ca3604056f454699bce8c6cb6cec9c34a1bec74b3b977001ba7832f11aec764ebc9be5f3ccb591e0d7510a4b741bba43d58034202ab34b49f81274164edf6c33eb5e6974eb53ef6de4e569c9335fa2b3c653b6582e4cc44328bb797056586f0976cc8f48fb99a21965e82697f3e924acd84c94948a51a6b2517e6942a2215004d754b0371fd9e167317e764f5c54a8a42a98686ce132c4aa67168facc11f10e719082fb6c80021a2647ee51cbc50e3fbcbbfcc34721206f7c4bfd5826bb8eececa4d8b57afadb63420d64316fc86adc398c4931cdaca78558dcffffb88169a60d9455a607fb2c9f675dd9060107141635cee157484ae284f6af9f3987c117ad9b77d7762e45bc58bdc32b9d75f3c08e78eb0e5906de5952ab7a51069a65f2133c56d1dcda320ed89535903a6656dce9664b645c7cbfa44ecd09d84135837ff8bce2df3a7ea644345525a28a833ac680b6f13682be43c03db4a2e6faa23e8ee5a224d00b9bfad52f1b19cd69b5fe481e2027163efbf39511e9f59fb526888718aa9ad5a0be2114cc3d7a2a369a93a404729f317b08d98a8f107e482b5836649343cd18a6876a3ad0a7616d0264c7898fd743ebeec1e45b30e3afde8dae667da4648330162b9f16c80081129000eae6861eafe1f32155e8151ecca59700c4638fce25954ca947a9a2ac5af99f08031484eb4e1bc1d929aa15e97120c6ccbc151f6a309fc9f67e566d5af90bab6f4a4662a9220f28936408e6ae2e463a3ee3b823cdd6195c1a8b08236b0b250c4778bc133e46320eefbfee4f76042317160aa37a925bbe554ea273cf281396a1a2e9e3e14ec6fa7933987de0d707bd8f40915861294999aa424496a461eef6e1ed7591192ed604624249923e6638d2bf932f610701fe2cfce97946bd4f0b0617a7c513ceb19476ecb6115552987e0f11b55d9a1b2bd931d02817db301d34ce69b2cc2fb18154b58dcd8abd6d3a5396c4f30e8894aa484cbc5990e742987ebb5546fb596105a4e94afe2868776f4791730e8eb74dd512b0d42c3dabe7b9d18d6822e5b4740ab67cb104d5cb27ecaac0e69982eef39c84e785510aa2db078b30e523f1930f1b1499493061f1dac1bcae0562138854cd396ba339069b7e2aa96f5d95734981958aa617c9242e3201f22f95d41bb3ef5d97667838fc07e0379036b4616af3a14c770e2504a14b38ea28fe6cad7df3d8846696bc59f890a07654e2899b2bb3f4b11a0ce21102c08c2e4a18848a85931f3f2dab56dcea43dff2fa1e454f2d8e7bd88e234fd77acad2cc6846563b67ea0512236ef00997a42f03cab3af738a3073b0923c10d362fd6145453d462db2abfd006b5f6da86f8a2448cafc92dbb5de3237c126fcf9bc49918caebea42b5e96d39e235aa32e31b92902f3e1be41d2bcbd5a807072f013c31341c7c357e91865b5b05024df6b5011ca1c58aa65d4c35a3b48cc79e8c7a9322c8408e0ccc8c7b659a762ff276c39f5b159dda630d8082bdaedc639f77e1548b7aa96886da229cd534d647022a96ffc99ac44c2f6c9aafe89d244ba3a0f4eba538a1c452c4488c4b2d7e29814e231d8bed367d62ebe23905daa6b490ce73cf86268c7b11c999acc1c2234bc24d090319b955400c8a5ea611580d194023d5e419abd74d23cfaf293384219a7e93377566d155bde9015f269da578be7358acc3f061f0ba76d6dd828f13c57d498dfb88e9aafad94b09dde484b9c5a4999f902879c0274f618a248f276c70714cc81c4650034cd351d4f70f64cf1aafce86a18afce6df7adce4a00507a2478aa84610045ac5d7ba5b30db91cd168e649e41b44ee01962a93166f20a3081fd6689319c683450e270efc316a67e1b21ba5677f4958482a50446c5803968808fdbc593a36331c118ccfef4c5f1047c063df0676e10d9b75e7269b1d8ec6481a5a0b157a954a9e033a60e09bad953d1a0c3e61d4d35cf174e8e6e112944b09bca1302abc10ad01fb7b30da9f1793f91e5487b321bd88ae0397c3136e72f713d4c76c358ee1499710c9989e6c812410585a8bf160f14314c8d06fa568d3ad0fccde9f7a6558793d8515e83875c54702437b1f7c07fd4d3432da96ad700c6c9ac41dbf50bf927cf1269553b36c52d58d3ab0fe1b0ba787902a1ec8a7e3d1a22e042eb0cb02d192cc0d2ed2115a96f0b18c719aa94ac92c7cb3d65b98172536d84d8272e81ad53131d1adf564b7757db290bcd252cc4b6fa5a5b8d2b6a2e929d19d3b6f0e5c303cd7abca0339f54d8c1b0b620d07b7acab4cc7f85cb4b0d74a48d89c7bab4a514dab6e0f39de28f54aad6ddf3dfcf95ab8aaf5b05aad72af1630cf9e2a8fab2c4b751915b76c5c1fac35ee960f838558491da582e0e4414e244d2f04088bd8237988ebb66bafc3870b127b548912b88a96587645477dd48af90f86118e655ebc0c73390783c1d3bb8350b8428a25cd3a4d78f393dfb7cb59a5c860e2c1887586ae9b62fc098ddc788b3f7d5e1a72ab4c67cf6411f788e1a5ddd9faf81342323622fffb3c3176951167a47385ec41c3278857db4fcc642c39b076173c9dc6e8143785449b6b4e4641f26b42fd675a5e8599fc96f26837d947f36fabd6cdcac66c4a26ab335a16ddbe5b081ef5b71cd265984dfe8d54ba8adb1db414fbc4c2e2c6d90e3144d438c09932cfb26c80237e1852ee1fa91d2832860239cbe142dcfed7b26621ad19e1e233b9b9472e6a240cd572b01a41485e5484a892a697f2b543cc8db587037bb22aad4788768f1aa223a6f27d7ed6c2a6f9a2ac6cd3185e012d1c313c1b1f2efb9b606aca49bcc5c40176c940520b9afb838146fe72eca3435ed392656afe7a0dc2a7c3e21fa8f9886c97b207c3650cd1075ebc90b4002c0e52b2947bd61e852afb49d9de0359726b30f2d406d9f6d271b18eb4a9daf24552e5cde56c17012ba7dc708b02566886c5673576a7712113e9fc631d75644973b44c53de6e661e3fe581d6625293ecf6088361b4b84f5358f7c9ad4cc35e2cbd81aec96888308c42afcdbd4853b18875e0e87059346c4b89bf0940f5f6bd77d958b5780a28af5065097c6e555c6ab3348316fb1654e590589100a50874ea304817bd0d75ea5836803f683481ea0deb0c3bb7efada5907bf7be0ed9e3cba255d75c3947fe197e30b64b44e9be4657a18dc75ac33faff9c8306ca68363e41bc2dcd08ed21d6adfda4588b939a2918266bf212c5f8d2457da7c3b51c5d56be696c6e9a3ad24ad580951ff5c516b5dd5c0f71d4562b260d1f2f624ffd795d6c884b1c12ed5aaeee737c9e87011c86272fb2c87e26a824e97f593a0c45a69ef45419f555a2f50f9815d94f86fa632b97021edec1c1ca25b7defb31d5fa11cbf4f1fefde44b62b4ccc2b11584c544ba40e7cf90b874fe45e6607b1c01856c07c1e82abadcae93c04ec6c9b75d810233ba9caa19320ed272cfe184cd45ee88b6c5e2e44e79e18246b0a4129f3f1a174003073d089e5134a70feb084bdc7afef22b4b9d3e01ac6a955f166c3dc592b441997bb05f46ba8f3db8c440db9fc8cfb1fc493e96700cfa9a78c108d1e81010d1cd1fd19fa8457f59ae744205fd083bba88b5215442b3ae1f8f93e00a7afa778d516816b65b18c8233a24946f2916eaf97b27ebf2f5666ca1b6883a7b02b0581a69e42adcf31c14233029d0c71ea92c53d09f277e6252e8d672b2271eda063a3f6ada3d19f9b840236662ee00044323408d4d06ed8a74c0f8b2818308a28f093d7c9834eeee75206cdb7845c66f35234f5e1d576603dfc25acc0236c2bfb2378d87fc209a0b9e3520eb4fc4ee452e26a3d3ec75239321fa5479df226d4f27f58b98d3b72ed6856a923e5b56103f5a2445d34c4f94e1b77ccb2a639dc5b4d90aa96df2213c8d8eff5731ad8e8929fe65ec47c947139291e26068c7606ed098974997f5c0c61fc5d3816f7e2473bdd3da8706cc2cfb7646ab9742eabb5223ced6afbd337a276dcced835db6ec1d799ff08a861545d06ab642dd360a83df25e1276690bb3f4ed3ff83625dc3c5ae5989bf83817a57f2ecda055d8255d11be5cd8eedb37ba90f32172c58bd6d5859a3c7b4d4383164f2e87b962225d7b97045e013a08100378ad5076415fe7830f9a7b998b17e7c5a5aeded2ef0a05c0e8a4c3abcd9dcbc4ae5b2d7bd021be6f061872d1e57affadd92468efa191274c14f5cbe423a8cb4ea45daf1f9fc5ad9eaf375dbf82d71e82e9563acb103d368b6957e6c50bd1e52729e1e0813e9009a58a590f327819810050401c156761007247e7b44adc622fb28dec415750152dbc782ee9122cd7e088e88d4649b7ed09f139062c1fd8dd823adce66cf2cf569fb19d61929eba402bcabb7fe9491a9244d3bc7d55b8cd672a5521039ab278d391994674d91bd5e71abe508ceb1375acfe4482d420223c8baab1a0f460fbbc627ed71b7b341af0032f32c2368f41d9a125495e54de5f19dca20d8cdd1e0037bf64fa1eebe8ad4770d98b23e3937147d0fdeb949811efe44ba6581bc3784f8a333ad5b81a8f5ad0db2ab1e69bfeb1d97c4970ecdcfd834117f4507eb0c7ecd12029b760b9a1eb68a15d1d91d5bbce8738e76f9cb217445adbd06ae57b9e6f8a9301e848839fd09ab362dd74f05388888348a5a24924bda2128e0fce60453cda5817524b5bbeeb024112aa7153f666fcd26e187a5819eb7d495d7ff38032b12827f5cd2bea62fe97264c5ccf1103ab01ebfd974364f272c61981a13c82750998adc842358c3a769bb3f17d3cfedb1e0c3f0fdda253f6c668b362f944d0cb96d56f58d014ec5e3b10a8ec4b52bf6e11ee2d225bd71cc776d5786dfe4cf2e24a31778c719fa21cf3717af558d3e77961c10f9ac1f07e1ecfd9f5b50b469ce7eeca69ce7041a348670fefd35c51ec7861b49902e70407ee6658569a4224267a715d720f5e29621e91c881eee4e088aa36ebbc525ab46e6e5992407696dd5ac2dd9318f9016022dba3a74f201af9a017fa1a4f3abe774e93f7d967d0263c9fc0a398985b156641d164abb5a1749cbbe8acc61d003fe0d00d654755c6bb00ce665677493f85529ee9ae6d844cd06af09cc435d76e0febfe2ebdd3b205b5edd1af18d11ed0edb1dfb7385876ce0fb5ae0fbc1e18891e736d0cc5a9e26530d388f523d56d7a58af8f511d241148ca60e27c1c9432c54fefbf1a558a4d3b8bceccc4ae298356280bf4ca6292bd16fb9dc096339456f894af0b00e3a474dc5887e2695a46ee390ae961777014512e1ac00b8307d3f8a15a76c09eaeee34835115b9f1e6991385e10e0a290027d7f9cf44c067b9a4b0a6bfc73dcdf3da0b9533e95b7fda465fd6e1f902eaf6827927788321d8be23ea8b45cf764db3809b267e380e45a807a891cbc0ed5be72c6e53a900c47667396846ddbe23c21c17f092546114f9ab7365f915b6f4619d85f3621492a8b584819d0a3b2f1ffeea96f65ce76b377adaf2df0d43bcff7f10b778710dd670b1f7d6d276f41ee3fbffff93198f4815198754b8208f30b6c326ee55c9d89dfecf1b2fcee8579392307432eee5c40dedae75f02548383d6b9b6c18a230bbf01c2add5b89036a42dab5fb781d53cf882f8f28b351a059654ff26cbfaa384d3a1dbc091f16b14ea29c81f66fcfda806d91f52bfb226126835637520eb633610b07cf17deae4218c3f6b3b4013d0b924902b5fe0c7c8c40705598321d9edecaed1e684c74f5e1367800cd3b008193f66e696c9f3cb00d96b6a6b451c103f3cd2aa63e0cf79a7fefaf92dc227e790930791af6d1175c39aec61917fc7c1b049c292251883f81aa0bf8b19ac1f97e4f171b9862627849c34371d69d67f62bc7dc813df1cc35554372938f18884bb473c3f318f64e5ba00c98f43d741fbe0347ddc90622f00d383a6ddf8f9326be250f83023feaf9e290f5a3cefe713e516242f75716229ec1be0ab218861d8952f97b2aab3b2d7b4bf1bfcc93e7c07c47b4eb0095f61656e829dd8cd109e4ac3e93df52300e60ef41214a4815b0c6e60ea6511e97f2912c75ce2e9cc746cea70f22876d3b022a247e90061067317f0338625e6048cae4ec8129e193ae9b48bf211ac52fb83af69bba0db25eb6af02345f33faa464c17a1dae535c274f8bf858f158d4d5f03d1fd61287398ec78d3ceb59389d264c8b943ce86228861c4c28628506c5e1ea6a371e7e32318f7afa4de00c7689c72f0f1e4573276fa5eb4c3f3b0b518a3ec3126a5c4a28b6c9eac1ab81e3cdb6aafded1034e361ebfffff24d617722f518f7fee21339ccd81ccc38d0075e8ae28fb2307893b28a5dc405fc2ef72c48faad01f796e88f67a8c27a29fc179c9f644e2b16f2ebc60428a900f9c56d676c875444c5349f98fd5e4d07a7c08fae17eff4360bf06e8ef1ffd301d817424e786e1db52da77da7dfc6e63b30b84df70d2fa815bf8e2fbbfd9a2d14e968cba073af1b7c780ded9cbc933f8da85845dee01a4f86729b9ed65b8ff86b50ba7dbd2e500560f38670ea22f5a3b22331ae804d9716b3ffa6c181ae11fe58217e2407ddb83a35fb734f904ddb5c5646bdc08cb6433f8fd545e4f69284514250d0bfb51a45bf2de887fdbf6cc0f7e08ea142aea2de3776036adbe54cf2632b762a00ed64f0374b02d79ccb991c080ddebdb171ed8a90f4b1b7ac11310c6fa2246ac8daf34418edce7cae56052f5bf7188102b0ad961407f58a5f0d78aef2768fecd0d23fe55d620f861b4deef00fcf7ad227f98746617bc6167afec325beeeb379b67d7e53f0647766773222e856b09b1a9998e1267c34c8ffec0a6f6751c9a0fca2f8dbe4f20636681fdbbf24cdd0999a6da6410e85f6985b20792b7d3cb1f13ef6bd69a3fd13e643ec49be0ad1058dd7a9dbf737fae231e952dc9783d3b6cd1b20c4d2722d6a7e1f55ac0f560cb9b31b2ff6e0f4635ea741e2b3339b2eb91c3aaa03702882dee3836f5cbcd04a4c6b7b7272e9089a7d6690e9c759f0786f22265f91f7956b84fd1556738545c7582b44e79c998293ec8b33c73b953ce44721e5d395809386fb97c7232d87fa78bc1f9ab4fe07301f9c5b57dc3891fee636e78f5bd0dc073ebef1a3ff5022dcfe7f1da463d85f540f697b027b6fde5b550a5b864c1a31c034bfa1a5fd5d84be940b9e3b42fcfd348587a5587c9c6195ae6afec053042230a7b8dbeb0f62599a8d4657f19123ed93198ef2b7c540f3f15f72fb0f46b0272ca8a6fb0f92a6f76441f0eb04ba05e41c17e81981099f9280ff157f00e94061e18210c9a0c7a2625eb19e59d09e4ba4cf71f1819335267e60c93db0804fcfdd7c01e70a755e1331f7ee2992d517004f81981e41323f494a2026f984c5b040e820d0da03f5b14059cf2774e410c5b033415da19fe3d5cdc5ba872794ff8be7e80eb2b9f90abb9672235e4c4e3f8591b59ddd82e30e9ae0623bdb49f53f710dfe654b52c2045819ddb6f9d2dd8292a65ff437e1bfc813f6c15d8fd1d90ba8590970d340aff29f86c2543ceea84ab48f8066ea24fb96e96781e27c2f840f9c489dd1ddeb67beee762ebb2658017ed22f7ed6acf30f34212472b26b2f34e179b31efb1786143de208008eab5597dbb012db7e6de1cf776e2b9b5046bdca1577de4a0413c9712d077e572d695199e92d03be51ffc4ea06490e0797b8f9f0e00bb6976a38d31bebc17ad11f015450086927a6b9b7d9151e211027e8afb19cf300d2e7ffaea5569c81606f0bee5419ca40782db479a55036916772fe0f1b23a55cedd009accdd0bd25b2bb86dbd1bfca4e5845739ee06d044e536fd8b958d68b0aa76257b09ee03fc7d53e3038e5b74da307f7aeb9991b673ea9f0e0ef8d23522e355f01cabb21f90a5e8295461fe8f2dbbb87db328bea6f6bdc29a72bba6b02c2715bf7f6149cee8c4686045f447aa9779f1fe76b77506e15caa71822076c813b438095dad665ccb395f5793f450ae9fe9c72c76c60fe3db8d00b982827a5db948b1f30e651b296ff438dc549a5be3ce73937eca45983e1eee351a6e4b4d3bdf3fceb652706f52dc1219cdc60f4669aea090456c2a119ac1f5a1bd5b2d2293a3913cad8bf18f9fa027f4c9e8fd299ab902e5b014a93b664fbb14c971706ebbff654d613f515babf503022161dff17ac3cadc81997dc072558846a3cb8d62a56176cd35aff54ee58c60be64ab353eb286633b44916ee221f35dab5daa6554a4f7abac9ba21593d095dd7bec417eee9598f2a51e44c38652f97d7de8eb33f23eccd5eb58991c23f14dd71f338a55e367d9eda285d47f779a31feaf31c9542f786fc179c69eb6ed9d2fe189340a32971beda67b140a6c111e9996cb88ba9a2ff963e8c07b4d0e7d1c9bfff87d45e5b3ff4038b59491646e02666a9699b7cc23c0e39b789b68005ed9cb4cfa1f1807e13be3e0cffee72c906e817c834ba12b8177ee6941f164a6d03fefdf0afa2bde6563fb071e5a8c50b40fe5a451f9ea7f8872bcdc3d07e7a5c1f7076b81af5eca71cf1e15b3ed09bdd2ed40198be1988c46f5ef53bf0c807ac5d8b55b07aca46c6f6f102db45ace747d47f08c867eb70bf8e9b303831e582dd8ae34dd674b7ed2e47825a19ebad031ceb18e1a748ed2e947d8a9db8abf735e935787af8da5f7604fc2e2dc2e1e483df107a5a08abbb0344a02204701fc52c8235d59972b12b085d8ddf5d2430d47ee6d5deff345e8ef7e4aad760dfbd9717875891fd9a85aa1e1571104e07d340cdc3adc58befdc10715c194892dbf4c4142cd2deeb159623dc48f22b3bccebbea4966bcced0558185c1b521836171432822731e3e93f6ce53d1c5063b56261191da61cdc95ed55efbcc287770c99a5351de1a0c17c2c171f63dc4d95c32ec501987dec21453efb2170d40197567ba3517eb67775c7588b1fae0d1700ea323776f036943bfc8d3a4b5ac3e15b7e929db485c99f2be22b1aa0c94615fd1e69edc61fd263b52a0eb0cdf73a79f5bdb92cb9eabb665e504df96c834ab62b37b54fd812b3922bceadd42d94dcc29d62ba871f9edeb56162e326c0df20d597bdc4074f7f45fc23cdc929dbc74f99c41e4035ab8ab1a28f010c6874d2ebd8f5f2a446cbcd49989ac1007b3949079b84ea4e9c20789137eb50d306d636b66131be1f2af6db97323b31561d770931a3b1c00bd4416da8e6a322a87bb7b33fd07139dd47c6d638649ab3681198a14f4436671bf4c78fa46ae052cb226303380250870b6e07c5d86a4484f87877b9a21fa8a21dab6e0cdb2aac5aebe3ad59ad38173fe629fd2ce050d66b72a89f72c9e13d94b419ef539accfca9a7c8232da2cc6f989a51240336016fbae2ca4c0a4cc40ca624b3e34d93494343cdc7ac2612436872610ed61a9011f569ea844b629c10d5a19580c94a73de72fc61745f054e8048807095e77d4733dcc509b5ec37fcee6b216ac24b463253263a5bd192b7356c074f11472147e81d142255a6201cae2c6fbaa85d6c23757f269301ae842811d5a800856e160f46d14a96345dadeb3dd442cad5a30a22a053cf8fd340877cadd380a10dc20d741f12ff3eb6ead413bc5bcfe752e856fe529d41b2fbdd17818ee06d9262d5a7849f6513363ab130ee1845b597373833a49b9f710c919f02674f4f053e75445bb028ec16854c38597870f954f07b6798df14a4c95036e94514b524eb837d487aef9d1f1e1024800db8568f48c02aa7eb23fb5a9f09a70bdd196ccbf0849c3c1eef5fb64e2cbd8d3df7a5f875db84d513f2ad06efb9f88467f6a6e41d831172a9963e89d1745816814189a0ae3428a825de2a9264cff669b9d7385d8edff1b512e974f2bcc1333b8fbcede96a2d74c3667c34b04cad8216bfa582641f122cbf4dfd01329667624a48f59145505fae74cf6afda0770a4c53bd29349f29730baea1c1b7096f2853ab4a7c2c9dde922f815023e562d57d53256401a36e84ba2319bec4bfd7185ebb4987754ac9778e57e5d48eff4ad9aa8852f917a6f00dcd0b82684227035b697ba323cdb41b5e4a806541d2aeb71618e22b7ba97a5a143e8e72522c0ef47acc5c5af9f926e0dda4029af18cc5a8cd7217c2b1208a079b1e035c1348e9144897590c88a5ed0e1d69ac8b166ed3f074a2540c0a27616caaf7b71b8353dff38fa1f26dfb78a625d8a45711f3249bd8b74e9ec2e41d4bd1c179ad0b7e67b1e1a363c26e3681ca74693e0727bbe45613de0c4af94cba1369b1965fc3ef43af2a21578480d29e97f375616126771852bf07d03f9aca06644820113149daf11bffa529e1d2c04fc569a0feca62a3b125f2d9755f8e8267a69044154adc817dc7344ea1a78da6eebd21535a22ed03192b5e148d1b6cc41ace82d6f8b83265cce88113c5dede5a86f21b3a953ee075591559982e40e353ee4f86a13193e64bf64f66eff29eb6ac297ac3bfe5f500ecafeb0a594111f63a7137972efb8895dd285b21fd538c9cf8b33dae448050bf05fbb843a69efd4eda61017ced1289c40f0c5b65e8110e89da4985e515f2bd99df81ac4c1e6d9943b3ea3b6420e12d58ff14e3ddd340fc8b78bc8efbf1666d8d6a42bc7531e926237c12b9447228dc149547fda060886df5e16d5b1ba9495d3e757b835064c627ecf0038d1117efa7413d77d08e6791a47c835290afcb8b8c0f72449e226c1aacaba16785c4e09354995d68d41d0c075cd1dc72f11ca3b16a71e8ea101c6c9fd23c5264ebccbba620c7ec59059cdefe353d4689ba2364fb6ef29418713d5f38db10a4b111bc136a3029335eabb0b15a20866fabbdba7138cc4089f5127bd98d5e69d7f9cd4ea42252dbe8e5b3cf32f9c3dcaccdce675f2d0b64051620384476c3e994468070865eb39edf6149b551191f09c2d51f7401c1d2d31141e63bf562fdc62afab216610348cc07f4d117c5275040641b1fe294a61df7178a04ce02dca4d88857aa9902d8cba864e9d756030636a4ed7511baaf72fa806ed38daffaf4f2459e1bf05835cf436e52cdcf6d2004ada372b1cba368579fb44f6ae6bf024e3ee9ad46217ee8d3d5e0d6b06bf2da714e652a9a16b1561a1ed38f0d689ed98cdd9718ebc22ff9a89c872470e28fa44cb3739f381cc854d7ea96e8316b981f3c103bd006689086c75db95a4d6d02818c75b20fdbf6b798be6a1a4a85f67ebca08932f7db40652ecd3d4f99d125a335d1d6babf842f7bd8941cfe5721c714de54edafeae535a73f04b20962ebe6f2f3c852bcf4710dcf3b4bf83c451d42d4aca634cd6fd401b4fd7bd2f2834bba8e2d0fe5857b94cd7e061c30711710bea97231d4371a9193a84d578519408b9982277ed7aad5469e248e4bb67b1e1a8262d9b185d88e96e2c0f5434dc76d0629b4873e20ea380d9c25805fa3cddfcb33ef886d2d22f1012bc6c473bfc3f3c68a8f3d1baf06fa77d2d6c56c86b6a467fbd37cb2a0c234bfc502db95714ef105a4daf9a7f9a9484690487c59133ad42ad0702ea7fe0121dcfe57528990ec03b638b50cf3b6cc7bb41c5a018a81fc0d79d9433bb8624cfc81fa05457b3a7f518cd8717205159183f3710ddf29be4f40c169b3dcb0e02cf88a7eeaee18c5ecdfe616abc209d31e8267313186d07d732b2cb6ecdfa3ef733b26869dfc6e563bc05f1dda2d08773cdb600c81786a6a74afd06161816950fd0a63855a75b9969d969fdfaa49ed500d0d846eb8572ec5136587cbd0d7c8fbc51f793286c636cfafaaf0d55443036290b39d33f36a3e023dbac2da7e00b01be38c17744eff028f5dd6c3746b5112a567b417b3e483f4abef1edd125097e0973dd0f9ed588ea4f7486d61a6360b05d1583ce875c8d31bcdc0d7489345e055bf4e96d235640f60cdc9c433947b981a044512a4453faef36637bd49ed0219a6abb3c9ba4ada572b4410ef5e324b4a653df6d760a1649f7ef707cf28cd9bdcf2c83f98a39c8e5d3cd979d8661388377cdf18a0713bca251f5d3a743e6c9f916d3515816264eb918dd9396f9cb4ad7485fc8e3b6147b6c4e8d541b1240eccd935db540e4fd13b2eb7e6b0e22086bb6cf62bd19d1b27b93dabdb2b205cdc0adf01d5d942a1bbb1a3af015dad17e27e4a83cb6508350db5a19c657005c4fda5f3c1d2a99654b4964bd48150d94bc787d9331b34186206591f6aab7f2a7caee75299b362cfbf486da526482ec57655730f87a46f3b2ea5a1185d9a7720655507b3203dd331396754065a51935b04fd42cf5bc9084363f25b3976a5786463fc76dcd07f49ae7425cb9b3f39f0c466330f475828c240874d6ec0d7abb35a5665e295f7a014f7f79dfc186d02842431b86436cd2e92fdcb0cdc3b4e88ed6b2ae334500176f77bd49d4bd59e7cdc21fb980972b421731248330ab274cdf1fb88313bb0c3625c1631aabb183442b30c46341791837abdce2ea005bc5519574b071a9a25e3a476abffe829bcf44ca75d9dd08cbb950fcbb81040420c6275c161a3d48d760c2bbf1f52fa16078c89374bd3c50d468d4da846bb908fd0fe790d40a9c5a73314ea666ba4e4b97a9b63eb4db48a1007e42c2579a4a8cdb6051c2c254409a3bdabb5a3dd70a117b2e153b2f79a5dc20259dd3401ece130d58eaa960f9334115b81f4c5696854e66ad1e4936ecd137ccc24f980fe4a0da31d97014eaa3cf1532519b298aeccbfe6ad4aa9d147a2858763e3535b66e4269af752d60a24779e20dbdb39bda0d05af98079dbe9cd1ed2c36027b48a7419550568f6e13b7d40f3047b32e2adf0a4edeb64479a4770da594b468250ffd39f609893a164c084d0530618d52f8b634b471fbd6190a22b89d49a42e40af54819de6802e1d16912edb8b445854c5de5c4057fd095d8d39b2d75efcb6d688680183da0af42d390f8b4e68feef41bc545b3c1ce2b1e920a9d1889143318d2c9c01f7f9268ec6955c9444298ab849843b53990339402dea5af27fea00901d1478089620cd4ba0a3e9a8c739d1bb76b038a688e6e8e5f91cad63316e5bfce919856e9ddf0aa912745afb790ec93ccfd13a4e72a2ceb4af0f05f5c4199417ea5d2f9971668c31c221e308a417e297694f7e7944591005accdaba7939dcf1e5170e75154264a50925d77bc9a6fbd14742f3329eb788fa283653e4afc123b7c57c6e094aa17c027ee32962b7e60a053924e3f0344b517c3619a024ef754d0fd859d2e2c874923232c62d93b0b4ce19bb1527f49be26083b0fd5d16e6473fbb845ac0a944627d940a17b985c690a798a53ff69d5a58bf46addd719ffd0683e22c1c2addd7381b82f68484bdf5d54d4245093810a8ec09f6adea6d485a2436f9b6e6bc9606fcac61963ce5811b0983d5707b8a51749f68f28e0653b7baecd053aa5fd5d2af99c4c47a3167bf937aca845f649848903cef14d822548815dd12d89d6b1fad962d74091d3253b26579729d47d0bf27eada67b8773b75088c0451840f57552165d6e679f1c4ab42d7a970de26079f114fd032e61c0c7e71ba9a4462fd8b05a30d91a92c53b5561152be55a03c22d6a197e9fa188625863c62ae60c1420051595e352abf16b75125d47ce77b0f284674d131b4c9d9bf4837942b18a114ec5c2aa8fb884e39e6bfbc74b9730baed65c9c76329cb0e8fc4fa8ea60560486cb441b4b69e5550d65088e7a8b5b8469c722fded466bfb94eba81beaf9de39d1d7c98d0bad55c57666f928e87ba21418bacb4a8f1b8a26a7d405316ad40ccb0b927abe0a7fa948ae904d10b53326089db05cdc4d3086826ee41c5eecba99f5bfec42e6cc413d312a92bccafe378ec2b9c69ae9a9bffd2b8dea19e29775994cf0374da0dace3b63d4b56a931fab869f71f6c94ac7fa34a921f2c59231117cb33b823cece457a47d68e6f9a84088aa08b9bfea81d6fdf85bd193851801c423665992f3936ed869e8d7267b8aa2e0d0fb12fc08ff1a2f59a4819ee9bf2863fc7a8cc09ed69429911231551d330c975dc78c31bc7bd69b184b4c7f535ca8cddefbce683b9d0b221d2b4eb0a34aed877dc201d0365454f4eb2740576f2b5922f75a4212113e2b72d97e68476570967387e0300857c84d96630a992d48d17fd388072fb5899220d42cce7159f188eb0c26c392165bebbdc53f5532228aaa6056d862d75c31feb8bebb836273240c4a7aeeb38e0566c69a1e014fd2ee2251b2df7e24e66031706dc7ee9d0645336a259118ab489b9784e9c12eb0f0589b9aa0d9d95fe6ba2fde8069ecf89458c47ede3ccaa649495f2297a1b6a2d0cccc1bcd1e4f39bf086e376274d89549230d83bc39e5bf43f56976e359ab9583c9ef08032e92442d70980434b1de87a89a6edab1bdbb09991a4db3ac16664f2f56971ed2de150de69773908fbe3db4b03d9b553b7f995435a2824b32c23dbf4651071322f41c8e0cb6ec0fcf711f2f6280cfabc4ff9684ebcd1eb70ed8a8e634eb01772a2838d6e0b7186ef49d01cf4c366f58ff03a6020d7be22a35b18567790e4a9ace643b5c4cca140cc1c970aa1de1252fc938bd29873b9116bb91a57722e567a3c9c76089487a82a87c073b43cf0d8548ee2caaf93fc22d2127ebde6c94012674551067a2a829696b87f7062801ef7a0473cd0fbde1a9514286f188647309e50010c46410f09018b78e42257ad036946bd475e5d70fde931fe2627004d020f524fa2c504694910afcd3361fb8f47888f91816d79291d0612b24332758f6b3f909f8b976e8bc87f79f358cfb69d1ccf610b5f7f20b5b6dd739b57c9ef72df93750d1cbe2e6c47e7e28ab36a2e80964bd93d84cf8d751a6342d0247ab0f2ae2c462a9f4f778291a0650f88374d6b9e71a68b0215ccfe297fd97df63c97c08a69a7118777e64407578e34e2922d554fea4fd3fc3a06d8c418bc2466c7d0e4cce94398557e6584fd5a14907fb6f577b65ef7359b34bdc95cfb196992b9c4f1efe027baddc3fc4b4bb8b7cc9ad816cf7dcf6c38fe6dde15bae55696f640dbfa68767d5890e7f69feb3e5c1b8f771ee665af7889f775fcbcb9f10071bf146cee742895959d0b716c2c0ed01f336860f1971dc70719452ef01232637eecb26d68f1fe29dccafaee8c95f9591bed3eb3ab1a32580b03ad773154fd3224c8e2757e339e2fa930ce7b8e5eda33b631144595f5afcbbebfea2601542f8e4143169d01069dddbac47277c6bcef2ef90ef4b4ccb08ee4318f8c1f80b525b4d041a1d84e496682f9cf6eb42e81ada4ebb4797c7bc5792138d1baab214fe0b3b774d1f75fcd67949ec32dce3bb1f051274df84a1e1cd810a042fd4e23c27846fbcb7db63f7ab69996c55835eecf153ff1cc8dbf5eec69c444826f550b8e98ceb4c0dcdbcf06f6c4d8139dd7b2c8171f5ff063b3b9f14e49f99d71792466bd1355291f09183e23756600747c79e3ecaa2ac26c0bcea071214f053535fe45e28e49bbf0b49a6bb751474c33d83b268187a719be19f0a65612574fb209727c3a0e92d898a195debd4306a1c425ca9a03271328a6fd9853762a19a953e0db6c7e3fda362c70713024721cca3069545cfc98187645cada4858b248724918097fd761f47ad80e4e6cd75d4ef6b7ef0a0268b5fbbacc52aebb217defeb15bb4f37993fa1491ff0ed63f25e21e0bfa009070c5aa558e6fe9a520993f48c011edb5f3e7fb4110ed086060ae86046d686b52b42f85208d7de355409b59bd91420b7ee070293de16266fa038700b75845833afecf154fc936795e4fff9bff834ddaa86591e8e6488e3a2e9725c134ffd9b20232c4fb4e65f1e03eff146e704beeefc39a464116e4dafaae607d24f2f9998f2441b08ff4e421dc44e580adca4fc808797069d27087cec03b4f57086f4251ba74074d790360d27e750890562f832ae35b3b0257da49f0c33d3fee7ebdc083be22fd3bfd10751648a3460b1ec2a3dad7bc633be548b6702305af91f3f8e6b60beefa0ac2507d65c378a24cdf29f0e7a7970b09d6c9955629d6c6d342872b3420918ecaae624672dba8cc8e9623d50e3b93dd9c1561be2f5edb073a7cd9879ac68da7540d11d005140a87926891c54042d8d1d46c4b96ede0456e9bab6765b91cfd2612a3704cdece37c6875d69a04cb68a7ae09ae39ed801be1fe191bbc19c67604550485a1b6f5821b30cd9027b63dd0a14ef4cc13a7657ec109453e0c18b5c5c91ceff43a858fb62e039fd7d28dc0647955650a77e6769a5dbc773ae8a50a2e4f287381a03e14a15d7b66e9a30667d8d98e860d07b280a9a4868b1f1b604c30dcd35eb343a2e8d9294e37cd71b8ef4fea848338572a1781abc6fdf8c2dd5c262e84ed43c4025a0dd0a701611955278b6b28de60c850451776de937e6556277557ae6687c78df454d527637a8486d7864e7e76e18fc64c04cf3db5d13854fbd40f6bce0a5824a1acf57c349015a20773fe0b290d78bc15ee534ec0fc997823ff4f28e3320ccc0dae7d8fccb593c58d135cda5cbd726971935b81a270aeadd58eca4ee65c5b9b5fd915fe47eaa6e58b4a7e0e634a6d540404a3f4144eba4ec4c17225fe405b1f3bd7438c7d54a17312738247db21f480c72d62270cbd12c1889b5367979b33d345c0c0d32a578fbb7483758ae91ea297974a97ececef4656a52342dd4747e4cf07d399db0469fb83b5c816eda8fc83a906b5011d727fe10a449c2e8d8bee45049bf84162a9e964697c19d61f70aaf0049d16abc05b3ed10d3df041e8b69ec5b559d82535ceb4c6b7d75be956103eb385dc20ec3f13d429d5f9238cb46e7226a66b0e2577d6baec9a3261a2cb2b92ad375db02b5932d6c5c1443ea8eacd4920c9c78de043de7eb40179d16a834be7d7764caf85bfaf1bf48299a8a1916d07325ffb78fc7321084a348d67369b02edb3f095a407bef24435e3872f3102b4620d93f649d9db238510ede9c33a622ada6154048885445c3064f0efb7496bee5b7357bd91fa0656250cda7fd27dce58a59adfb5bb45d0b1574647905474d1a86e22430bd049a7d1cab94a104e5f00c0387c3454c4427f63f6db5ab12ced64de3b6c434dd509e04ec74f02f1fd92c4066f959fe5c9236b47c38ed4331e946d86f286776a87e968aad81d3d4d7af41085cc48623b9a11f7294972a86e753f98abd7d91f52436f819482fe724a2bc3aff96d73662250523c1a98d8341911a3c63f106f138f6026da648852b29b07e80850ed78ab2d7ef6010a753a5ae829b3e5e6a5ab8f5f853b15413d3757076cfdb0d1c2019e3a4b6ca68a7294fb7aaf33cdc6c16dedb610ddb52bcfacb232cbd4d9d3d71364bf56ef567aa3a7ae02e9634ceb8e7843414cdcb0141004e51389ecba3c6de4f869ae70e86bcd5d4ea49f738dcfbac31f1a09ed4e667f66bc796e095006efb191d7e2c56d182e1405d6e1136a7041e2f401e3025be8a0e08722abcb1b902e075b86ce5aff915f440adf64698c6a900e5ceb5a72dd6ab524b4882502acefbe1ed6bb20de9c984055060fc0402f148e065435c4a2a9db382fadfa1a762216ae4b3b733147d8955006bb353167c9fbf37f459978062ce13b03a99600762e128e3b9d6cab79ae6a22c33b9cc2eb789bba90d0e75ed971dbbe0cc364b87b968c5c0a819383302df4477910392dbb8d87f42aa55e00f3232901fb2c5fad973e2d630d1490dd232d3259526ea46b807564701cbb28c3671f07bfd3b0e196415689eff36eab0329a455226c21a8c57d3e645a6379ebe66185c8d196ae8f91804abb24e44988098f50828a8c90e9e5aff90e425ab8ea0d06bd77fa600e4a21be7ab3901ae8ee45bcfd0b33e3ab319dbc893758015d4406ca302f39333a671e1e208358098546b49ac6a0cf8d82a723375aba2220e33b74ed3212ef4a8a722db35d564b880a6b448afb030e3e863981fa234d2bc30ef744a60ba0f37430693f81097d5a4891fe63679e201b671359b68891c85a3a9905d376f29f5d3810a05dda80510cfc5a1e2488792c693c351a4a69e3aa456336892f6c635a5af7a389914b594e5e8d9c60872e0723124d6e8c553c1e30d214d7ade9cf84f38b20058e95b31468fdb65d6479e93b2f23a907b028053fb4178301240971b7a41cdea40fe7ad44b3778596efdd99988d00941cf664daa7eb14b887f7f7ff51e39ea969660a5c85f4b7b29f416a253cd494b48439e910b029788fa5d0dd0aa5986abd58704e89e5cef7759732edc15260aba2e16e488e73f69413c804097f98948e4a850789a06f388becc4d900876c1ebdf88d15fb8d3923d21c96d22ad4030d20bf428b2230072db8448fc2d3277cb241a6a37da8b28bf7b5028567035f1e9b1d1d962e3cd8c6925405d7800ef6c69e3b9dd049c69ed5a5460bc93e69d5cd9c1d5528e56002ace0b5d0cbccd19f2b6b5cd72451a3cca0582e54b89deb1860a46b7d8aebfae079dba5e49077f5bc947801f97df3a210f3f5ba76c8eeb517e76bfc42b1e29e1dcea8e93c01354cff614b17ea265511059e56ac68556ebad2856efe83d0fbff09bdbc8bdd184029292295dfb85abe5507aaa04d7583975693454ae4711e6d1470437539de02191878c9002b0dc46d00d6aa4d7f6e6301ce8ce269d92453a071ce314729018b7449533453738f7c73446e96317554713053eb6a30d910059a580b515611878fe8a4e4d44ca6797aaca8734e057f1548b0e09669015e3d004408348ec08109e4a280b80ab86801b41748c600e319287689807b11b41ca13c24e84d42ada1a823a4cb228e1a26d507291ef873a5d128fb8f1b9dbf1dfbcea1f80efe0f2b41b6da8ed1229d056e8b53b24e3d30385a65ea028047eb4bf0ab1aa1ba303e1d399d254e3c7e8ce5b4f4cbc9cf51f8cb5ed461fd977ab15bfc2f47e98e199e28c58e934f615aab8211f3889eca17aa98de7c8e876eaae739251c968104e554e2d945bfba1d43fe5063c9672535862be3274bce291be4c9da6e6ed6cb832bc62101a38738ac6d7f329b448890267134d9859c812c296033f79350773ef6c9a238ea3cc0ace4fac7ee786bb30d13c512ef7c382ae178738cbba8c0e1028fad7395e04fefa1f461891081c2aa996c2216f1ab320b79d2ed2fc6cf4f6449c2b2144688052ed0334b02b13ab09453216032782519a0589e36481febbfa718d949551bce01f6313e88cf687556fc63295e586588acce25f157c908589d801c2cfce8ec2aa137804fa38e551ab46511290aef917ee9b3d2e2d225a53dd6a9ea235e1c461879698c80047c1b16a446d9d97bab5ca559d7bdcd8d98c1bd2693c4a8b0d904639aad494fa7c3e3750ec640d618ad546f433123948d9cd551eaaa8e5adc1960e2a54313291921283bf8f86325c9bd8f9e5a5c783a04833699aa4e459670c80f156168e21d8f4ecb780ceaf3d640473014b222dc2a5b1ea94fe198daf88cc1761d54327b60d73232144bc7b11258e37b65b7866530d05066e8784509b0913dfce09d3ae138428316625218de54e651aace3f24b741d64d358c70506ffdc8fd4e95a0ae44386ba5b848b7172f84807e86998ab0ff4aed35b436b10a7ac4430f6b44bc324dffb17fdf3fca54c0786596eb0efad69e917054ffb0b501321f944e9d0f73ab6b0d669b9649362d87fe88b91fd36a775817827c2f0df45462622529dce8f8a8991bdc51513a4daf1f4a1ce15b82caea5ffb449d1404a0db825a936bbb6fd802f75e1600e5993d6601bbbc41f3fe67c2291efd32e172b9fe2cbe82b7cdc94b1ada65aac208eb3bd612a636a475a0d42bb1881490429b6f4adfdd9d8b9bdaf327c9f442abdf014b49402a06450e46c848b96486797f6739a4c55fe7d0ed695ff2f08fc0149d9ce511603147800b8d00b71801eeb608b0a3408a34a96da2b485a0dd8a64375e9d2c10d816547c3c643a41850298c46a430ce7f2f671c49c837ecf5a3afb503c9cef3e20fcc2f9cb026c4f12c66cc3a3062c0e23ad62608daa990e4c301f4198f2197d9e0c75379c23ba456e08e2094b1742488fe23b31f1902cc3c74c8441089207e634a95248324730474a05c5c99af80eb624005f4244e50cfb323e124c734e8cf653300075a492c4e4b72f68f65bae0028f72a02911d33c3cfbcc0cf417be925634abb2c39225e6fb849cac33bf792f17cbfde2870fe8350e2efb65c888e1a2451ac1802890b1479703e4d86e7f32312f01da3c0f6f477d2c6c1c7731bb3e375b6fa47e9235a91c89d51c2defb49b5ffc3ca2eeb55419fdf522aed95a3fa1139d1cc3d547cdd6104bde5d6005779843dc45fa7a4b9725441ad629fe511c5007f0e646444bdb8f8a534908b72df6e033030f0bb1bc5438e8a3489047011deabfa2d6ebc32d67efe94d93a5e19056863200c61fb060b77ab51f11dec67386ed3b138fcd85a59a85e5b03856606841587ce84b51f2abeccd3bff2fbc52a31d1805892475d304611b8a767455608ccca4d48018bf3a60a87c00ae93775b09d7679c22a41015a8cd654e582d28aa9f215a7d20a1fa18ad13a20c8ec726342408bacb48a937d0494b82651c9b4266dec958b055624e347ad0115b0c82d8794f7f6c85840eab82639fc71c5c542bfbc160b945cc53d65055cccf5c37f8b8cc2dda10d6234eea2467605c4c51c3d3b26f7dec76c80f886dc176c345ae87008fc3a94b16a7d0b3e696f475b2790c678f897014dfed2857891340e58cef9f8e352a0866ea7c2583ededda2717824bfdc588198e3dfa8c87781cc17e1404f8c58bef42e7ed608fece19c70ebe487dd1a634eb3e6544c0712e979e66c52896b414fa17cb873252d44919a9ff4e0013393f8319ad65f1df97a030aaa7a5507d637e4269c8467e8a5314fd6c7b9f6453281e6d4dce8bdc689a25ce4e60ccc68347e0279eea32e506b2d0f97050b458ded30fa98a6c0db845ce783af05d7f15214e5a6998b2d34bcefad7cc67f6b7442127749c2973b858b1201437cce3da627ebfda22130da4e597b2d6c1bce1acec99049e10050e11481020b3a80da197befe467d5dd7d7c1d9b905dffb28cb8fad81f6fb40d844324d48bfda46ca4c26abd7341d3c918f30c6a31910539e24252525c22110e49664e59a42aec3daef96023e1218141a0f347402644400173849d628d0d96c352648a28a14a08860051d2d1ca894a82163f2061f1cf8c1db90d12393213eb022818b0228f386430249c04d0c18257024f938e01012c274f2c31f55eec0f241960c888882851d100e20c8d08d16d91af3013478e04ce0021c4dd868d2c31882dc8e104efa3e4bb514aafc5f8daeef552b918b423cfd5b12777474927474c2ef7a89628be39e05825df581aad6eb53a5e1ad6a5f989c883890c5b3f554fbf23fdf833d9614c28525654e1f54d248257f5452064642160cd33132c24691bb923cc7faefb9f937be57d280ac61071c4ad2e05022712631c3b3be9dff8ff84854c77c3b748cf8872440439aef89248049a200998de23f3a7a895644be0a52fa41923c21c904524aff45319f12f87d19c5cfd3f10fb4f4bbacc497902b4cf7c2df7a05fd7c1f294cf879cb4845660052dea87093025218296fc8dc10a15054464787cc1831273c2922f2ded0095228284c50235f341281c47454af23b1a5c56bb9f995c5dffc7ac0aef4d5450fccff5c16b962c241111da4942018e2eb2902259131fe86c82419e73887f5fc4c418475082452caee903458c6cf0b8ffe27e54d172ed294a3c1059654d20804d8b4314943404e4c134065d2c025502a8932498e2414922d1138a111cfc892226b10c1a24d1c0f28a410398814417c1ae001b2c68f33fa68c3078e02b6e4810601541c00903ba0d4f143870d72bc11872cc08e1b36b051c71a50d230730615654442802d630411461b30342fd670b1c8163e59448185bc825711071555a4580305264ff826f460628b126024917344124640214287215041ccb046fe30830f281e74d8014a0e49e090a3060c1bb6d0a0c70c3e064c6058f3429516e260812f49a528927cc645c435a1e66a0306f16d61458244c50a663c2847757436fc1d2fb937b80d463f2a282a0092021528a039b3c50c0e993663ce10b3a6c823f219927242984282e2008305d09a2f4b26f89f13bc74a15c72b6283e507a1ae041ed984a30694191d04016282344d1c9d1e8dea24fc81e2fdd35168b1a4721544e29587c32efca1a2b67b4110127842d55d050a10290295040f8f9c0863772a4073b1dd85087142898190ea88844005b3608a28d065a066b16c1c0278a28f2021e87055502b006930abc1e146c0163829c24525074c0414199610600a07490004a1211e48001c1163d1ef098ac71a04a1cbc0119c5139f45d630a0b55920884c6422652ca08c1c821060cb152d2491430f7c547969549b1837800cf1614342035962c1a10a12395738d882c11d0130809c80caf1861150c0204e901a9d0205112c76ae20c016121a40048b9b29939cfa182491d3215fac70f2b832c08a1866e31540c2903880409148df6bd56988c14b030d2eca98b9c41229253064c2087f4829a59430e45d4a29331c3f5cc0610129acb43ef610c3cc732ce3e7bd8e3cee8e9f77e471575aafef6693f2c647ca9b1e89d3470e373c52deec6ce143eed1668f2f2426568b2db1c6ced37c9c3d7290126b257d9c3d2cd0e3908e7fa03c00a9f675f36b66ea25018bdc54c932829447622b04c37093f246e746bbd9debb9182524979d2e066fb1f296f749642a0109d12078f3112078f20898307178983c79652a296baff79a4bc9122a5e42efff1b00b6f71f1f545ca1b2c37d9955104575fe43ce15fe240785091f28a192edbc683c7055776847c827686bae0f1445ed901b34324e6cb012cb95214c667e70b5018a14f8cfff11f0c62c1fb8ec0ee7f4420faf9c020296f42f85ceb83093d579acd7be39851fc375534787243e52647ca9b29204879f38194f2c94d8efc2d6fbce9404a39824152de48c1a4947e1bf325d4d11141212d5aac88e33f1a45d0a512f9111fb907bf51fcbec8bf78a8651441a12cfd8fd152c5fff43f4654bda8aada57ebb58dbf8b080a8d2228e47ffc4fc6d58b968cebcd2f9c392a90c30012d3c191438e9c3820c189e38b385c38714c4c078e49a4c474e07003d3f97094812347623a05684455801c0aa0c11b84bcb105a683f3c62825a69305d3790385e9b8c187c474dc706385e9b861454a4ce7124ca70d35dac801d3694308d361a38d94928d2f301d2f71d8f06103024c670d365724ce1aae35b24889e9a8f1078e1aaed305699891062562f8c7df023f0cd3f19ce7e601296f1c90f2a6012c8d7ef344ca1b06a4bc59409abc1b05a649911bd4e974736362e47463ba915fe0a05124b30ec4301d2339eef84390531123f28712c81b248e195b883a71fd5144125d60511957c84cc43e900b8bf3c42f0495be2f82ab8e852fd00c9689df7b49af9187dce5e2d8cb871042c8953e8ff7108431461b32f2c8448e43860d524a8e711c32b614bd312e91ad3f7a20ce1866641ecd1859e44f1a430229331c3182c4d091452d2fc270401871603a9818612021a56423454e0146221f07030c30d6484c071b7995ff5afa48545038386048f0851c526223c7d2fc6f25722fdb18826066ad56890ff99f34df7fdc8b12c62fb639249313279a7491b3c49a2e3e8a2d6fc444cad325529e2c91f254c9e93bf105d6a84066012c9eebd53b88750b4cc98e3e3054c27e0b24c232917b89abfef19067012b5237023851829240152a2ad193729a44ca93049e3010c683a3f83241ca9324529e22394122e5e991365be0680185d20f7a420bff79accffbd0d00293328b3fb2ffd26327852c6e04a43c39d2081606c0428d364f2020e58911294f8b9c12913858a4c1c182754244cad3216d8a70b040cdd1e338e8a4934ed830c2e600524a4c0b86e95c193d2e8c1264d84021fa902b2175ce616c649052624360c004c1031b1f29bfc88932315826facf438fbbc68042e2571a7fc83f0ff392c24f848961b6339acd2be2151c2b70ac7800fb2eb08bd8a842e25f09897f32e3f7551ff87da08fb8f4bf78cfbf88aa18e22ad0bf40ff12b3a58f14b65a2bf1e5123d150a4cc1889c2205de3f6fc4be11e39f2762e2175d9cf531f1fbd8e7b9b00ee4e12bd1fffc7420b6f33f6ffc380b4bdc94a5cf31d1eb40cee3837dbc0a95f07f9f870d8101330406ccf7e0f7a9604360c00c614360c0f8df4496121331a4ce39168bf780609a50404a898961abd56ab55a384c6c2931510485b2a525bec8fed1377e473f3c7cf1255858a28c94fd7b852366658925a2181981e16741f4923c8ff1f07bd387441f32fce13fc343247a384222caaba5001e3847302119a803e7882d4a18118794639a7f85872f6e0413a2c758fc8b181f7aed605f0249232228e28cccc51afa5284045ca6ffd68dc56f4fbbede6ee1ed7bdeb95d6eb4b19a31b420616cee40617ac243220c40b0adf256e0881c856432756883940061670a0c8184d88f0a206caf4401f8108a1650821296590135a3017ef2aa5ff6d724c108648794a83d28c5ee93f89c5e22d510cbf2706b19298ce5098a1a10d932245ca051227080f82e820882732fc2a2b58301dffb1882a2b2a9c356d3012482061cd1b6bb69098ce9a346b5a386bbc48158b1f7ddee7616b72a41457225f1d619fd77a85a3c7853b3a7fe4231017d301e2c90f993c82f3031e52ca2a19a623e503bdcf337afeb77a9a1f1ec0f941fa7009a6a342038c33a49419483512c7070da4ecdf17c5affa59c843b0b3d240030d34e000cfd2187a2f8e81d8ce04383c4092891e0f7d9c0043a641802978c0c122fe23ff1b70e00331fff1a59319529eca90f24400294f6448791a43e2ec2084b383cf298c1318525621a5fc248e0e71c8bfea236fe9807d61cfe0e4b0458987f65302436e25f2550e1a48799ae224451447b03c1e8e23b038387e20c6b55e61c738267e0f7e2a70fc3c150d2634589fd80a39f693402e04797cbe171661fee33a3a5ceb15f650c1f2b8d1f86a19fda4295f69e9862ca09a3b74549ee5adbacee9092925eae4841a18fae8f2fea989d312529e9448424a94202f8f793ce42e895a44ecdf5f6156964657eb732513971a3849c3c1f0559aad8614c07fa4889491b4f0d1a08494344021f23f2118869b387ea00f51ff493a3a2c3e8245208b88032dfd2f4be14f02bbe8f9f8542b062c22e33d9fefc11e71e97ff19fe5f2c222b0c88f9fc87b3e2038e46af532ad5e4604bbef67045944e1fff97ec6343fe8779ce87b2f74b57a199fc43f501766143950087209ff27c6b3449ff00bed88df1bbd346028f4e33f90c862298141e217f94a48e423f729da5ab8c074322da0080358f00cc05952449ed24879524a7a49791ac510cdc925e5093caa42050954895e5645c45ca8a2c2ac8854407001b3225259fa48aa254ea5caac2081042c7c751410aa004a974889ca44898d9412fc507928a92491900f79173d9f1d2e5908b6b0ff89b00e143bb68365ff037db0bf1abd30fc49e087a93ed0a803fbf702bf6fe4ab9792fff8377e31c3b28e89fe2771cf415628f2b005720c14597c14b3a3ec63565c568a38d163712ceb38367e71f456a3c7e2e16ffd952b0cbd16c63fafbf3c18ba7ac628fd2023147e0b2432c2448f257adf0e8695807d3258f6bd70f4df7779980a464628ac3affde9525d6c745b07716cb5361f309d3a3a3232e7d24958ac5fa4a3a3a220a2aa0807d2f5462b13c2b57c0234cfc49ded107822226ee6099288e9fc7fb77893f54fadf3862a2eb7f244c05232410f4227a57388efd90fb1054c2fc1f3feefd27c6ffa3cf5b8d60901fc1a09e5104bd2cf1f82f24865959b2e2431a856152796bac68903a3a3a3a744879fa5a52ca3252ca3b708e56ac1392941268a72828cc999fa1305e3023306282545b971d22a20d8cca6768878c901855991d3046411d9094524a093ce9b3fe106624bec67f647409ce9f44cad2e761369c56f0a49498ffbf0cce9f37918cd9220573c105cc7f8c042d980b2e6055a8885ef67fd8e178d4eb07295542741f29e5108be57d09e28ac0e15a5c14a18d8988b2d17b81e3c8b16ca9838d45b1036c63a2236c0ae793892088ad44ee651443ef15bab051fc9e0552c15cff231d7d1e182c13754410302c5bb9b00ff456463f5c8d60f7751e56850af64516e782654646463f1b459025be5623067e9808f66fec3ca32a56b4146509412e548cfe4afc1d288adec757fdb7b0a3cf13ffd1f7933eebcab83af2589898f9ee8da2d73dd74e9634df7ffc8754308cbb5c1c0c968da2c7c28cb41465316a79fec30ffc2d50ecc02b47e088a50179b0eccae7617c0c28c4123da51fffb5fe8885dc6505cbbc6099911111965511a7b4a86099e8e260ac1cb17895cf5312b1cce7a77b63c85dafce7f30fc673e30a8e70383c6fe031689feffa0dfe241fdbb1428a328494ae9c46ad5e2a2f8f26050992738642439c4103279e09081838c1a64c420e304192670c82c21a50bc4aa7c40050b56e5030cb3f201a6e302312b1f6054304cc70aa6020a386456c0212324a97017188edf57f170fc3c1d95bc81414c6b85cfd3d6524f5c12758ab2efc11fca14030cc8d40290c8d4027fa40210854c052049a602b045a6021045a62ae844a62a1844a62a9843a62a0042a62af864aa023322c82998aee00bef9dc414655f04591d4b33bec68f87fc0803c934e04006769ffb2197061cc844ec8b9ce35f87b13837a3029112c8716c89b3c29f04627f652a626980819412b0224d3dd2b48020766c8048791afa3c96c7bbad4b17695a9d20a5c90a49790a639a529e82c04879023a6df1ffeef485c5c78f2bfd24ef48ca93093f5e3e0b9e7873ead24f5ca43c31d0ea694e4fc4f1b4c9f07b3e529e7a4e27294f3c320251e280b9408a61860364869418d8b1ef85e369e7b405e57f4a90527ab0c77b5ce87f2e1fe87d59414a9989e0e8f246ac033dfe958c44ce61aed68bc396c67f640524c2322397378ae1f7564a235411011be173c142c044c0feef54543012309737aac6cf1aa18acb1ba97416ef2a12b0a5f1b342900b86050b6665145fa191911196bd581c66a58acb1b31f0a3825d5962859ce5b5be577865fcc0d0c8e8e3bff5819cf53a727923c6f9c881ff774649464658e88920d881cb1b432fa903ec8f2f570bec4646580876aed163bd5ad8ff1db6837529a38b175dd680236605d3b182e974594929bb0875c922311d70a58459c1e1c249970c24a633be404c67e42b25cc8a18662eeeb96aa98b62988d69fef7c2712342623aa3a785842cdb07524a9f3cb2a58f240266e47fbe6ba968e97f315afa48dc48f581469fd53171fc47ab1ef22f624b21ff3c4ef430a58f7750c446d747fa492c8e8bded24712a77c51fc2276f479abcf7daf7b2df003fb671561999804062dfd2f1e67271029e50886383b58603abf7fe017665b68c0d9b2b445052925051267cb8f94984e15161fc1174805fb1ed43112b9c7dae99e0bfc44df8360c4ffb98c9f88c5f13f8241481d040a411617c39fe47d6242fe593d5bf8e2b1493a3a764c1d7345d5250b781ac09853ce3101c08491214ac40e1c2d101ccd8d0dc5ee01d3c1d950a4d492482931d1857116d8c1110c5d18e6bf57878d9fd7e223f7e3ef62254a2010081689e3c75d9e502a0dd675184829c5c8949219525ab001149bb02e89c01a26a871446892a51d905e1f2460c807214e8a9803123baac8f20b99418a19b4100dd28409081afec84249046ab429e16488ab9103dc608920b208347e90023a407ae404176a7729a802c516063441e30b0552b810c21b59e841004e5c1a04381c9084670e168e048f3020071b62f022db13dc7108140ed0e18d0c9840630b1b45f248809722f8f820893d8828c30305d2047e5c68248c4a56e002e89005707922490700f842930e021a183204232a6481003053a0d0851d6104522502472892e5007a58d0011073782a21c870a08609187852c4901851f8f852729126844448293454c4d0e32e05809172c9a001a8864c816b5037a8b1414a540d32057a140d334020834c811b15030c2f48897241a64008502da058589a22a51c2353df14f2d5023b65614929f5b972d0a9160a520cb37a84a55291c599a04462c3211aa8cc74e920842e6db49002aa8f37b2cc11891d5da448e0061091782002037e78430a624610379cb1d288263983251fd0d09152810ca8d02227036e9473199d914b83af002b60b964dce4b44400d8d8a13347054a9648c00c41b480010167429891e40c9676f08a7c48fdaf447ff4792018a6f941fe8fa3d7fa963c07594bdc8505800161fa60a10113891e0d3c58f365c8a3900f34a8e1871d376220c26702563099001d02e8a9c1ffd0fb2d90f5811e872d8d5eea432293b007d4d03201a12752ac2081063d94c08534e3c6095b94a922b4e504220431a39a800c031f20a0870e3a064b176cf2031a5b0cd10704897480266552da71e2014d5e8e20e03b8094064cb205922e56c043027924d2a5823045d041c10851bc91c682091c2591d40925298881c39a4d882008804a288c902acc904a0c19222255c6f5ca8e17221d1f306554b50bddb65222201e55971f221511104fea6329bf0447424a944ba6380b057ea8969428964cf12d28a4153c29512b29514752a23a29515f4a949712d5392951dc54a5944f54901265040ec0948452410a49894a010555235da850409d3123a55c33001924aacc1752a2c8c8a36fe4a2f791f8a862f114e248810d4da57041aa0c14500041a60d99212955a93165c66cb2c74b89b2c2e21cc82a1a43ee921245f4043574c2d2ff128194282d424b1fc9f39e9428149094a8a016f8454149107e24ef03c18cde276625f21f823d9f07f6d19b4206ec600787fcf747f19391120506484aa410e482f3454ab9c42325ca841f29515eba4889e2b2494954d47f9294a81e9e9dd45016d41629512568912812b27c7c55f443100ce80243a151e41f68dcf9402edd03c72c852f91ffb0e8033deff35a7ee7fbb0b8d73dff3281257a471f88c5bbf7adfa2fdabc4fccd2b7539374743eef4bf8c557f85d2f222951234829513a284d4aefa9505bf6b47a19ff3f3152a2aaa0342a9bc48cc2a83b464a944569c062bd82fc2f921255a3a0286a4a89c292753c8554d009a92030a9202f126565021c8912210469913a1f89a44455f92c8e7f62c02016ff3e60509acdb35e2099dfe2412cd68b4b4f75b5fc27c6c5c322579a0d048bc4ffb91c7d1e07f35b1e67f580e127660c41d767c123f22cd0cb67711c45052c62b15e5c8a3e31626a928e4ef862097996b7ea59627d6196fe975997fe17d4949bd37cffa1404045f9c08b1a5e9e90e20ab58c94a80ea448c900001810eb186fc788bb8c2e93cbe032b78c2d53cbd0323ffc5ec4ca812c9e6a33dd1b789647464a94067ff43eaffb3ea80c5018f81f86c58ae23f8b75411786c5da61b176a44459204a890ac0f7eaa44455908394280aa4f77e26f8e30ffb07f648894a05d9241d1d9c6aa1640a802dc15de261b823252a02140452faff79ddf79f18b1da241d9d6a9374746a928e8e4dd20276df8fa8e5fb4021f8792d2ddf07fa2d9e8683f92dfef1917b2d2b91affc4f0bfcbe6f819f288a9ee591d192a5ff31dfabf399ee31e4424b1ee41943eef23f4b1ee4d1a2a5da2cd58a19adec57965de96bbf963ed2cd2fafda97969d1fffa5bc61444a39764ec822fe7b8160525bba222f40be20c1887f442b04019a920d0681848a19401f9eade5853b8434150560d47192a311299b1462a60b3a788000060ab8018dc885021d232c8c78401b555e67a40892840b1c5cb0039618849080086540c16010302069c2892927ff001e5278410132433c296203312c1e3031b3412770dcb1c0263a16ec20060d348430c001031c30c8249cd4b143129a0b39703c84c101251830628503475eb8a3863681b8ece8628a084a2831462030c471668a160480c7509b36b4d10c31a410b20040c9c6840b102c59a4052438c9c3034df2431139a4b106262074a14501616cc08746252a318026261cf18038661013c9a3123d4c18814d0f0f5904257103ebe4a5a26cf0ba60e86cb1e00fea871a247e1021d0420a01b21a6e48118696061d3740f2001bad06bca0a186fbc2b80207958c391347076d02c9e38c960408920bd9a185546b8942aeb000e15962023e40018527031c3764284d0c12e0fa200b3f0cc0091c29b84087027a2062c5911551008092cc5bf1b9e4260b261461d3442652ac74d0c38a8f0738b16120f082ce30c40749347c5094c4265aeca8a08415667cc83b4010a290410e004e8042004d0660c553153e56fc1ba864c2c60f2b7460524a4a85d145e700288c318492079c1183904cbca8220dffc7249850701448119068e1c44c03020a5258cc40003a7e7e40094090070ba1242f30406402aa8e497840c4088d157e5845020940155d5861660e2d1c3864657a430062045901e39222f4e8a2014a685815261cc00b20d2783602d0b20292456a78e055061a1a103141177e0462c21306108102148a148961c7842ea668c18a9a053a4a8820c01fe312a023413c919486080298e2c75050047c2ce9224cf7dac40806c64c9e8e8626e8682d400b2c2070c01663b44c9b1144ac8081c98a33ae4a1279e38a6bb98831812576608d4b459ac8c0c01293fca046083ca0d064d4808e023450c425927c3033c9640b1ec2943187255368ab283e8df0e1c3874608254c0ef9034d111624e08c231370cb0a3d1478009a3ca0549088150900b06a027b32c2139244d98391277fd8906a8345253470a1068f12520ca2c29123527020c9165fa8e89068220e32741cfd8429438c3a7ab0e280191d585d30e9423f5981e1c00b5c1e104707423412c91908582102613e0d5d6680a08340b65870c1225924b90285c6f43ff81cf2431a6df43b8c443c9cb06226f941812e422694a1790e20820f9c20811803b28ca1838b3114fc200032147049227cf4f0c80e1a38800c130ce9649be38e009021858b244e72e0814d16134ca1c2197c6cd0002ba09c11c380148f4cd282044b1a14187e081381144bc6151ef02448d1d070c1e0f3c1f9a3864696b8240a4a94acdc27a818010d1cb2267f889902870530d010840c3b94c04242072e5cb69831420165e7d0660c9c1c2e72189b3470461b563217143408ea1c2998e162c6920b7390c18014331c0f454c2b06524690e100293ad417b44400c10194286109339d0c618600b6cc20542501a121554471640510c95779c01906b0c801010b88091f5081c304654821c71fd8086164146d26d983860ab818c34b022251c1a084069d747094c30a6128404a1707a460898046e62201221d1a988388331e59a39036889c60a8640e4bdcc0600f283aacb107124e78e941c90517ccc4700410323880851e6be441c50bd713420c0089147486e4f84292310801420b0565b4dc78628c44a220c022ac9c323700404d064f2442851332b21139a0c0c1141fd250a40894225f8461838a954188aa0610280abcd143181d58e9c4cba1903b5a3081d1b68a16440c65cc6881102f87299a4c208501ce450e3bd8b4b0c6d4c18638de0819b152c20461dc5023881308d1049120780f72a831040a043e70423480704315415cf84754cc800284cd253cc83181173a88d14de1fd082291261c21a22eb4d2c899429de03e6478f043058124c1c1488d8149cfcb102d0e6c4102107fa286211611c4913b3811014b71e30b1b420cf087107770f2038901226014829414174919a12755d858210d2107d88018f5034a154a7d1037aca8a4019d271ac92e5063c768d202e6894cd8182483230b1b073848e408870947504861833eda8022113e96302285287514320464c819b20d10832afcd07088c109139d78d0499531368f1028e032ec903a63103214c8ca464b3af3248b1ebc3024061f76c802024f9c9821cb053484b1011a47d414616903831e3a322182bf31849dc01021c8b8a20178c410c90ce00abe851c4c5e5080e30dd60f784461481a60aa78d0890804782905f16491212ea18212107a38a38f2d5877d0e001eba7e809ce851049d4d460c8068bf061c7ce216118200245687193a480107770b245230a6072011ba928549a246d26b2744148036a1c80861370f4087c8080088bf840c4217ba079e30834a0146244142b524cc1d84001cb246af670638c102b301001220d490c300134233c6962100e09932bf80cd30ea966a526cd060fdc2f12d0c9e9a5041f36e82a57bc50c683c3035c00175e3f7280a0c48ac20923c64784b264170c00006b7c2063006c9eacc008182a3c8047550460c1d113234419da687381153b3cb2085203d2a084921506513d512130c21206a8999c073800c00204026c48a3851c292e38e191299202150eaa3ccac0788c78655143d21821408188053d12e8b0a2a2868d2116a0d83f34f051a0015376206189b08810387aa8e96ea07c1c614021910499e278a4020314f1c62d028b921944fc70a0934e0a39214c03908491a5101c2ea9e2cc23919849b5a1003b84c0c1b4c50c05723eb852e68696d01060bce0039634487a9c818201b21095e025f139a18ea313768f0a26c8a1da24c9db008295188670227008521a1e2801742cf0c1035e66bca0e2b48f6e50402518862a1a100104708882035452152544820040950b7836817060f87983274a18468879b1917c4460101ffe981140d06386951eca98a0260a3f6ec05801660e3fc0c8830f3414e8e2099e0ab21822641327b2a442c051bac8d2c618495a39601fa14f116a2858849101a850c5d7e34a0c3520d122ca22c61928784ca27ae30b509bee084f1559104d516601dd4124382bc81103249b3420031d6854e0615cc1c8012c2cf28000188d64a183106cea382301117448e19237f8d84406e18c69c0174dcc4cc18222c18822900082794082ec59e28806f6f81e4162cac456d08367932a28f9600891e2f87221728894444e3021e511f2871e340a8418d2c6902b3210928514be64ec3ccdc7226209ffe83dc0b554548814bf9f1b4206d922816402a99b878885d3d6e32e63e9f2ee1aa6b9fb7ab58eebae88e11516443165cba46c969307750b6a34f3f8a3172a79fec39b5f3549e512535384d4dc203521a882e97460f7432a589a90affec7889e6b47f45cfce863b95a5e36854b2c521215904832750590d415323265858deca22565a50229820c33b4e042085bf0515c8094f2fbf9be2b0c18940aa1285585cd8e9a2df62b454503556a0a26e5298d3e24085fa44981e02598fa6092d4076348897d1e96c67f50a47256290ffce0e1e7e18631c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a594d259679d75d659679d75d65967d5344dd3344dd3b4bdf7de7befbdb5d65a6badb5ce39e79c73ce19638c31c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a574ce39e79c734eaa699aa6699aa6697befbdf7de7b6badb5d65a6b9d73ce39e79c33c618638c31c6f7de7befbdf75a6badb5d65a5b6badb5d65a2ba594524a299d54db3ae36b2b6dfd4833290c8aa4943cfc58f8d883c5ff0f85642aca275351b84c453941a6a27091a9285b642aca95a92854642a8a063215058a4c4591524a06a02c0081ff2c30c8b3402f5f14779440d6e759627d61463048cc077a52dee8c1e2377948799300298a430800ba8943824143a3d7f280a4bcc1e300f2e60ed48d1d37754829b9e8fb46b48daed7178330cf793cde02c717f7123fa7e4f9ef85fd9073f1ff53e22ef12f7116c6e21c48b92353134891a9142132957a820b0712040bb4542a07994af1c8548a0199c25144a670d890291c24640a6749a6703899c21941a6704e3205e50132c500159962009329062c90524ad0008b7710e86769f4c480419ef380f19c471c125763e8f1f07c161f89b8b058bc6769f480be17f671d6d14fe647c844feb99dbf1a438fc338f679cefdf035beba2a4ae0127761e0887d3f23f8411943ca1b0348794387943773c814941ee490f2260e387e0b24fa6e4879d38694376caca1c276e68748e5b3f984e909da544361868636d50624c6270c1855991d303a3a233fe22e30740125fda30f646939e245421f092cda2f2dad3ffe1c812c2dfbf5f33fefe8db245842ca2a54a494d209169b08a010c36c89538922777de185e49fd7f2c22e7abe2572250cf41201908fc8e223ef5820c82d712410b392858fdc4b7963430d10bc204597e8ffd1e7ddcc20c3e6440c121c5d4b61afd6fbcf45dfbf8ece0f7ff85f68b82bccc5f932e0e69738822c710459440e5c29552e17d7d1d128c635141b58214ceac91662988d1eb72b5589e177b1fe6a579a7aa223e6091429068aa4d4d161000f0f74a9fc4fe3752a97b80386ffcc128f189f9df0d53b78238652b2c270eb208bb51346eaecf060434a8136f3ca15058a6ed080524d0a15088a8a94524a9042b1c4305b8ddc8565697481e210e63d6e357217c662ede8e848e94198ba1143a2f41552a40262a44e97a44e69a04899aca4e4122d96ff76c6ef0b3def620d81e13ff381f8c87d5caca1ef038da207e4620df191fb881ed0e7018b5caca131045744e28f8b352433c0e964b25c2dcfc50a390123e3ea6566ea85132d524a297ff7581b0b01bb9b04225569e60a7da018a990d8818208de75e292460b92ec51010946772c401016962021114ab6b84184138764f9618bd00145da68428e2c9ec4f1480c602060c0236c4840cb90b826101242247ab0a4636e5871d551c3867b14a909688d2244e2c58c0f0372886e2880c60a185f5012456f408209014ac8610513644af8760837535a31ef38effffc149b00d96e9d5f6bfce38ae7f656831cf7c0f4c766b9aa3bc733c69a52aedbdd1f7299fce836bdbe6a6dd3fab3e59352134c0ba09dbf9cd6e9c9ebcd56af34b5b40f6ab9ee9dd65ae759e395f251fbe51df6d96f5fb1f71ed4864c7bd4c9ffd75fcb5dc79cfaac805b39aff5efebf61ed463d37d79e59aeb8beded93c894c786fbc67c538a3bcd6a79ef95a6fe877c256e6d4a80ef7ebdb8529a6bd670adb52260e3dc67ed637b37e614732a6a9a4d78681ad7e69ddcd5b899f5e4600b0c5db416990eb0755acf5553cf751aeef8df95a62e30f494deb1d50e733d57aafda5756b7ea569ff492a70b4d48e8d7efb75add76a6eeecbdd95a6ff03595dd5bd918f62a5d454c7d6b3e6b77fffb58e72d7ef95a69b071b60e370c7f1ec35aedb779e6799a5435f7d353bab96b5baebd539cf7f359e9dafb657aed13f6b1ad31cdbf478f2ddb7bd94e32ed6664c726cd5d66d29a5966b136bbee3d83ed7bdb5dbd28e7e9a2d8563a3bbee8b79dffe6a78da692ac09e75567a35b65a6b1a5b6c35dfdef639b5467daddbbeb1ed3d2fc6d377ba9b9dcd1310931bdbf476f35bafff1dedb84ac59021cab29792a8b721a636b6df75ca77c767b72fdf5c5e699a53d4c5cbb55e98d8d87a563c2dc559e79a33ae959ad6d8fcd69a9b75773d739f7de43ba7a88bd0173ff1fb3ecbfee87daf2c6371af057e4b2cd095654b2cd0a5ab14263536af69954f8c6fd57d6b9ec6f6e7ae1d73bf397db53cb9098d6d62ea77e5b76a6deb0e5fd319188dc98cad7256df9bffacbec339e332b6ce67aebf563bdd7d4e6b1e8fad6e9a0860c9d8b8c97d8eabae1de7d9debfd254e97f3c64b28c53ca323f4c63548dda9aa99df3de4d27d6769dfb7b7a35eff5ee3667fd4ad32596cbc57b727a7238981f6c39a53aa9ee7fc924c696e9d4dae65aab7aeefaf1f8fd1c793c59a683cea88e4c615430362ef7cde9f47476ffe9f52b4d57225fdd9ca22e3d154c4e4f0e97eae58b9c27c3c2f4c5b6abe7b8d6757f4d3feef84ad39c53d425a75566cbb22ca3960a9317fa5a7c6dd57ae269af9d55abb3de6ed67c39afcdce7eb06b21d833be5ce32b4c989c9e9ccf824734bec284647e08f68c197b322fe449aab16799e7e259e6b56a87a98b8dd35babc7977ece75bf3711104f9689262e36affbe668d67bcf9f2df7579a668c75d01955356db1654ff3dc3f7f3ba7a6b97219ca99768b498b6df63cbba9756e7bddffdc2b4d754c596cbf7bfcb55c397a357fa9b7351c5d271f4c586c57ebb6f2ed396abb4db579f3abf5faecd8ead86cb5cb197b7c7fc79a86f9be3fa62b365af3c71fff9ab3f6dcefacd872e5b46d33de98e3ded66baa62d35a673ff7daad9cd530df958a6d766e2fe55d673beb7fe5e8ac5e4c536cb872dc6b9b53da7dcbb756a5d8e8fc59eb492ff7f5d7f468539637beaec8c514c5f669c7d1adb7f6dd766a3f192628b63e39ae5bab3dd67ac71a7f62e3fac4b8f6dfe18cb5bbef1726276a139bc75ddb4ec3b6bb79f3bfb9e41401e5fc50680cd110e90a646262bb54cbd8526e57dbb96e393baf6989ed56cc4d3d37a5dccd7d77644c4a6cf4737dbbc9d9492bce999b92d83aed5abc2bd7baab3fa73513121bd555e3b2a66bd77856bc579a7e2f15b5654c476cb7e69a39ad75ae6fdcab5e695abf4a1cffd8c14ded1793119bfdf773d36b9bfffe699f2b4d59af96ca7f5ce5a9567de49c6adacd5404cd143111b14d8a39cd6a7ddef933a6967b110db1799d6dadf9fe3dbde7f81662eb55e3bacdbbbd996aada92988ed72d472cdf149394e35dea635340c1310345332fdb05d6eff6b359d2ff5579bd4878d9bfed2dfd9feef9e56ff1eb67d73af5e4fcb69d556ed4d3c9876d8e6a4f3638b3bdd7c6fbd4d3a6c9ace6e6a1dd73a3bfdecba95a6df832d95a7951e61ca61f3345b5f3b3a3b8ddbbceb309870d866d6fefc797f4db1e6beaf340dc1b1f30eecd7f5877630ddb069bbe9e46e47f1ec6cf56ab66bb5affade8d2be7ace6569aa619ff51ae2c930d1b37bbd6e5697bb7beb373d7b07538775aed383e3fa51953d1a314d3acfb57c14483a6e16a35ad566bb75a6ab3ddf4248369868dfb9cdf9c4eaa67bdbdbb609864d82c37b95d7dce736fabd9ac420c5bad7e738dd3b5c35ac7a7df38e88c4a05c3a66915db6e69f7bdd68eeb057a61b399d6fcad8635d6b68b0b9c6ebbef9d1ce6b8f598b7b051ae6b7c73acb5d673aea1fffed1676d2058d86cd5f4b79a729da3d9e6d7d256b3efdd767aea3ef1d678a5e917f50f6936beafe5f352cb6fd6aad64a76677ab56635abd1596bae5bebf5fa8d37c7b6eeedf94ad356087e97568d26256dfd763e39bd31be93ebca579a8e211af1f2f0b25f8c9be7dc726d775d6b14fb0e8bdbed72ff98d35b7797ceb9c32d5b5b2bd5f0e7b8a9696d079d51816836aeb9f79ffecd758ddb9b9bd4b5cd59f3fe5af31b677bb529ee2ce0a6e1bbb7b6b8f35eabd7774b1f897bb9b0f66d59fffef1ef785eade28e8b7c6529a6416754ba45abc513eb8da9aef3de8daf46b3e5dddc14f77bb5ecbb8b86b555dfb3fed353adefee534db34ce380b4e9fab3c69d6badb9afdd5f69eac1964af56dfd2d2f149af4b7bcd0858556d8ba49adc6d53d73def9768d77d019550c1ec638ef3aefd672ae19cf9bb9ceb5d338ae9b1bdfcccd3a569b4635d6b8e66ce73c9d7fe6a0332ad5d1a67fd59c7b9ab1d7beb77922d16dfa7faa3397b1e6699e9eb7c0b0cbb2161872c9700d3aa33a29f1edbf76e3da79effba72bfd795f3edff99ab3f5dceeee669eef4af76fc97f9f28cbc28f14f216575fdf92cf2902ca4903f26499ff69400e576a42a35f2de39b2ddd566bd9d2fa3b5bbbee31d7713afbca395ca9cfd37e4de31a5b9f412fcbfc28aa90423e318be33da9cdf65a9bb53bb7b675e79862ca3badeb74ee7faa8cb6fef5bede76d5729a6637b7d2b4e39fca7f5eb7270dffd19fb404d556bde63ac74d8e56edeffa8106b075737a4ab5dad57d71a55585cdfabca9c6b9d7ffb9cef195c8bd64d9d44167543d854dbbb7eaaa394dfb4bb7d5569a28cbc42ceb208bc5f3154261d374e5eedcd8facbbfbdffe8c5fda033aa2e67b6ceb56ef7dcbfd6aecd793ff0532d71aaeaea6c4e511717972cabe14f02ad2a67345cb22cdb1985992deb8e29d7f3b7dcd5defb95a6687eddc9d06c2c9e65ae56cf7294d9f2b49e67caf9ee5be3da934166f3bab3f8e2adf5ad5feb9cfd62cc36b54b6db73dd7a9d62b76bdc0b00bd67410b365cd5bed7396e2ddfdf6937b8993e61475290252f16cfb451122441bf675db7da7ed16db6dbde7b3c8fb9d9fe465d9a64c0c6db6d366b67c5ead615c3b57e5f4b8b8e46bc6095bb79cfacc75fef3a5766795ae158cd0a6ade6b2e73457eef7e5b42afc4aa04be59ab57209b3fdccfdea69a55c53adaa3e5075f4792a51257af39221cab2fe55486196d5a32ccbb29945509d19e7baaf46edd6eac575e74cf5e75afe9afd9a9df957ad13301bb5fad3cef1deddb57fd295a62dd15bb942b005faef574a8faaa80568f37a7edeb756aff6bb8fb595a62ad6ab35bf234f1477582f30599665a20bbc14892f9b9e1cef0ee76e7778cecf579a661f4cd870b7f67ffbbdc6bbab94c347fc6c75ff7fa7cdb8dfa965de579a1efdaf7ad6f7b808e51401e5647b12e165b396b35bd3f35abe37c5dd4a53715475a0aaf512311a2e5986a60bc14e95738a8072b22ccb5c5d966559966996aa542a341da568ba4ab755a990426e69d0199529902e1bdfba63de691ccf2e7b8b579aaa5a60d851fa8160265d0a7f0b3c1a3fb0b5a9abab39779d0a294c81cbe6bd466f97bde7a6ef58e3569a1ebdb8ef714e51971c96abf5c372b5bc7ac7566773d01955d146b3249fed635bbf36adef34dfdb5d9b53d425a7e8dea033aa9f9eed567e6bdf1efb4933cd9a67bb3477bacb9e53cdfa4ba7989d2ddf3db9ed33e3adfdcc33a7a84b4e91ce416754dd968d6bbfdbfc77ce7676b776256cf67697e3ba2ef3df55aced95fea3ffe3f7fdf7a304ae963e5ef7a865f31cb57673f47ffff7e5b7eb40950644c24629d63cc55c676fa5f5ee2c9bc61f53cd7df767ad53eb2b4d5596e64cfb4fb23f236cff778e9b58c318ef9c755d69aaea21afb67f4be2f5d1d96ed76b97ebe52ad5b8a9692b4d5595e6b1d5d9ac6d5cbf5cc7f1dd691ed73de9f7e07731a53bbb05822a960f55aa06f4f6b9af73ed969bd47779f295a663b8b7e46dcffba7c633573dd5ac96579aaaf4c6dbacd7eeda4d4bf1ce1dc7ad34dd54df6dd3caedc67ccedf7dffdc4a5325303c52d16ab79a6dfdbae3bdeba8d7b85f691a8e62dd6ef69addb85fdc699adb74a5e9d8ea343174ab53c357570edf6e29c579a5a9ff38e869d566e696abe59b6a9b771ad568e6569aaec4974b15825ddec9e9c929ca2902cae97f8c121fd2a8c6416754405836aee330c7dbf34be7d4d9ae340df9aaab2699f1d5cae132943371d01915994c5b2de74ab5bcb79eda9d58dbac95c462651bd5bd5fda75afbbe6be79968dad2ecb7e08766d8dae6c176b9f9bdda675f6ca375e69163cf195530494a3fabc5cc3d1657f08f61b744695c5cad6b1ef95cfbda9fdd47ebdd234fc9f085bb555f37f29cd1d573367579a8afe7f5135fe234c738aba883c67d657ce5096b540fff130e339454039aaab059d519972084125be42950a832af4e60da8a4019546014c0121e38a7d50a94a85e5f8afcab8aa9078ef1f29040fecec608a142a18150eac0860ca0621683033a8820116a5830b6ae88105aa0f02505b6057853f09ec3f54a92852c6d5d5bfa82b5852a93ca0200d6f71300155812c15c84ad1160e55fdb05385e0c835804205001d48b0bbf1b5410454f5572a5517bd9606104c79408ce240053168000ce189e73fc48001012c4055ff288a02544451afd502a9dcd40e4e21ffac9e8189aad27c0de4141972e2c8c4f45bfc43e5c8f1059407a41d4310b223512ad4952eb8d8428b2cb06073851555503185145140f184134d30b184124920718411451031841041ac01e2071f7ae061071d6e72b8c1e1861b16244e1f44489c3ebe90e2c68d07a41d6ba0d8919ce0c06681436eb8c108356ad4a851a3a6083536d850020e3c30c490830db381273e357c514396279c6002871d355a460fcc0c54482ebc9063844a90214d0c66c83488901170c4c400d31a26355230e4a1c0f70435c4931f8052305cf0c224a91726e9e14eea051924cf0815fb00fb3eebfb2c9c72410d49422593f024b10688540b840821252a0829516ba444012125ea0729513e4889ea414a140f52a276982de0b05c2d4f954d9acd2babb103ab5041f3552e8f850cd311592038867ff4a4942916bc163882acbed4f7528f94927b759108f49112a5430e01de4eae6b39636f2fa77da5e9f6375e6cf34feddbbe2fedfcfd9eae2c556dcadd74816d77717731ddf7d6cb754ac5b8e1629b9ee3fdbb9f5667db796e8bedcf5df1d7bb73d472bbea1b2db8adb39f9cfb14d78ded026eb2b015dbcf757cfb3bab21375868adb7eebce52afd7c72ab10ecb407dcb0c1b47395d6f9b5e9eb9d2bad5285e0f7c2a2bfeabca7909b2bb6ec3ff79cebba9b5cf5da4b4f4f0e51b562cb59a33c73bff37aabd6bc8acdeaac75bde394ebbd76d55fcdbaa1626f9ab536a946f3b4bb735dbbd973777ac9b29c2220aaa7d872c6bfe35df31cef28a7c14eb5148efb47871b2936cd6b5ae7efd76aeddaa53d8a4dc393eb4e6fda512e637ba1d8a636b7f6b1d5f9765adfd705863e59e60243af69374f609be7b454a396ce8bb59d33533f3bcd738e675e69fa1255dd52fc81ac9e2cfb1fc8ea1a9b1b27368da3b96bdaf5b8eb59f72e0494935304b4c54d135bf59fd368c655ab9c7bca25a7a7fba1f58689ad6f6dd2aa79e67e577bd74edc2cb169d3d7ea7f672bc73bcfed4a5370e42edeba5162c39ca65d9fb7d78efbddb42a5c6f92d834df3fe69a9db973b6d3bfd2aa49c71014ef0d125baf1beb5d29c779af9d732b4d73ff1eecf91e0cd226cd7273c456a9ef3aed62ce739e3fbe2b4dffd106023262ebfcccb47659535cbb3e2b4bd4c29d4a756e8aa8745e4babb557a3786aad33a5d8eaad7579f69b358ef64bc486b5aa55add14eebaeb6f221b67ce7fe1967ed76aab3ff4243393c393d393b394240f77523c4a6e159bbce6d3f27ff58b359bc0962e3b4e779db49fbecbe9edc4ad39e9b351bee9ce6f4537df9eebcbfbb7503c4e6b5bcefbcd75fdaf3b615e9e6870d73d7779fa3b6eb34fff3af34cd14cc8d0f5bbe9973bebbfdbff69d86579afaff29812acf4156968dff28cb728abaa8b22ca727e30a2a157d24cee2c9b22ca363b22ccbb40d47d7cd12373d6c3f6b136b9df5dce3cc75af72bd544820c8d235a7a84bce09393d3940394540391ae775d01955d20d0fdba619f33ab98ea39a7bdbedb0ddaaade61df3dc5bde4acc92e63d73a3c3963b2de36abbf79ae3b0d6ad3465b178bf1f974a6f72d872e6fcfaac697bfdbd9cbdc161c35db33cd7bbafa79cfa4cb4ab2037376c1da72b773b9e5ae6b0a53ecbb2ec521565e446cd563b3df1f67b5b8b2b5551abe6c686add3ce7fe6f8c7db66bb69b8a961c39dbb9daeba6a76efcfe94a53fbc20d0d9bc79fdf4e3bae7fda5595564d35dcccb0691abe5cafd9733febea46868d4e9f3b8d53ba2fed34da3bd934a5babbf37bab356d674e67a536e88ceacc8993add7fa6fcf59d336eeb4f67fb22cfbd326dbae1ce6bef35de39cbf6627b0fdae717cb9be77ebbd33d664a3fb636e27cdb3638ed29ac9b6a9a6d57b6befb4bf7fa72b4d3570fc7e8a5a60c8a57f1791a55c9c30d966ef5beb7a6ab4fb9bbb39cbfa4fca32acb10ca74bb6db69d8ce6df1a5f65aba2dd9b6fe5a6f0eeb6ea9e737af34fd3eabff248de24d2bc999626dea6bb39ddbee4a2747e9ec7a77d7f67b8f4e946cbf5e8cfbf4b9768ed37f92edeeaedf2e73936a8ca797c0a6ed8ee9e45c57ca7dce75926c964f8ee3fe73edce4be78f64e3ac4629fdb6738af5ed20d9eaeef8ce9ffeedefc7db3fb269ee39e7eae6ddaf96eb7a0436daed8efbda9f3affcf3947b69af9f6f5ee0ee7bcf9e51ad9e6bd3373b376ca69ec2b87c0a669dcc65deb30d556c37b33b2cdeaf3e434ae5dff5fed22dbd42ae76ab65d9ebbe3552bb2518ee24efb5bd378761dcd896cfc76d7dbfdb5ee9efb1f91ed5a9c35bbb9aeffdc13df43b6ca59db5d7db549abc655db66b39a9bdf77cf3ba51dde866c98ebf6724c75a61cee2a7dc09633cdfc4ecbdd8d67b71a3da5bbf4bf68baf491344b0bd9389e1d3773b594ebfed34fc8a6759db3b9d65ff5efbaefcd9c06d9f8ed34ce96629af3cf5773c0c62bf775b72dc5954f3eab205b57b53cebe6b57275d34d03d97aaff66a3d6bdae7d6cedf80ed6bb86addbf9ccb2b4d3fca80aa61bca776f1dc5a5faa4dedd25e37b75de6ae9ffa7b4c01d966a713e35ff5e65aeb6eabf593ebdee3f4c74669c771cd5d9d77ae9c6b2b4d9390766ec0c98f6d5bcebbd7f0ad9edfceff4ad324a44bc77cc1158fd302366deb6d3beffec55deb38aab1f81786d99a665325a73e36aab5bf1defddb5dafbdd579aaa40bff9d87ee5335f7df1ee38d66a5d69fa8ffc0f7f0b57f0b4c7d6614abbcd393b6dcebb5b1d79613e2960d397de99a9d64d6ed2fffd4ad3b1fb2c158bff515421b15e5c9a3f6a7a6c974f6e6aad66df7515572d8fed4f8a39beb5fbbaeffd79a5e9d1e789e1abff5045ff91dff807eac91993d393f325a727677c81f91e0ccab22cfb93d61313a7046cbab3ba76dd7387b5d6f9029d10b0693b2fe728d6d7eaac4d7dc263ab9cc376ff8d37e668d5eed492381d60b37572bb6dd73babb1c57aa529d87d7ce43e5b456f0d3aa3321971ba63eb6887b5ebbddf7572ecb9ade564c73635d5f6d34e7dde7a6aad8e8db39cd6fe5a8f354d776b003ca7ae59db344f6d536b6bd52e87efeffabedaa77f6abfd2141cb59ca22e3959d6bfeffe5096655db5347a2177dd59c5898e2d774d63acf9bb79d55aeb3936ee576c3967bdc6396de7be2739b6ad518dcb9d9efaf3db352e8eac697a6b3c27d6365b8e9ee0d86ed775af56eb9aaf55a3b4005b7731e718ebcecd7e6bcea737b6dffde719e72ecf4e6be5949fdca895de6f75c777ad5caf34bd59bb5a9efd39b5b1592d4fbc37a7bf6fff35cdc6a6ebfcbfdfdbf9adbbcfe73dadb1657dfbefb85d39aa39ee7335363a3b3b67f739ada6b179cc77d6bec79c9bdad4301a5bd7acce55cbb8e2baaf3d63b3be5acde33fe7e6b7d6cee51263c6a67198effe79d6689e7f4e2fcdf7127e2510a896b1755473fdb9cdf3d5baa9d92c5be261c88d4e04d8ba7639cfbfee55ebb46d39315906869f850ffcb2acf5fa32191bf5d5d2ceedeecedf719ac6636cda6b9a53ff2db7ade6bc3e89b1d9be35fe27eeb9d26df55c699ae3f52be7872f1164f51c4ebf7254bb9a663885d1fde45ad3b9edac98de96d7eaa80b2730b6bf3fd769ed9dd5dce56c1e73fa62d3bc6a9a677e35d72bbd78a529f712b917d107fa8c35079d51a170f262c3becbdceface5493bcd54a5517cea62d3baade55a7367f1e532d711852311a53a1c45cbc5c6cdeedb5fefe4aee7b8dd8d2f4ddce1285a7bda629b9c7aaedace6d9db566da527d9fa5e9920becd8e3574e510e875f39ffa4c566f1f459d75f6dd613d753161bd614e78ef33cbffffee53961b15d9cb936b9766777bbbeaa4bc3556ffd56d64e6c36ae6b787313d35bb93be7bf62e3775f6af3d699eeab77c757566c96ffca716d6f8e6a5ae36a9753155bc5357b6ab78671ed975a1a8e22155bf7f7664eebb5e36af79586a338c5f6b5d6f47bcff1cb3fc76920c8793949b175df66ae719952ce4d4dc31a079d510de014c5d6fdc4bc5ebd3dd7d456d1634d0ac5e639d761db69bc6a9cef9d263ec1fdfdb7ae956adc6127b6ca757c639b3bb5b5e338bd4d6c9f6ad47357bbbbfedbf9ce32b179cd6b7d6b5eb9ab69d9fabac4a6bfb6f9e65bfbf77d6b196f2725b6a96fe7747b4d7be6b43f579a7e96eb3552aa4f496c765aae692d4febbbedb52922b161add3dcb3d6b256bb79397cc4f66de7eeefb8e53a4c2defd788ed5b8e799eb7663f6bf5d616b17dced74fafc6f9e5facfbb12b1d1dbb9fefa7fffb5bd6757e53ac496e7e626a55a5bce6b3ced4a5321362e5b4d67bc2fefb0de1ebb5caf20b669aff57677bd6afa53ec579aa6f92b968a9ed66cbad2d96da797ab9c4eaab13810dbc65c4f8e729a6679ffbcfb61eb3a3ebfe777fabbbfa5da3df9b075afa9966fc6dce63e7373a5a9e7ae1eb6ee77bb5bcc69fbfebca95e695a79d82abd334fca75da697882fe521c8e623eedb05d5cf7b4b973bbdb5dafcd4e3a6c99739bb3b9dacbfbe45a77ca61eb56fbdbe9cde9cdf9b9e709878d7795d3eceedcfa9e39efa71bb69c396dfba4ffffcee55f4f6a36fdbbebf1c55ddf5cc3ddc9868ddfcc751fdb3e35a7d4d295a62c1e1e7deeaa54dba71ab4b55cd618e7eeadcf7ba5e9aef4a51a5bdde672a261eb9aabfc72d4ea5e37c7d15da9c5add76755a71934f718d7cf677735bdf9a5fa62c85d97b65e9f05739261b3539b777656cfccfd99f3287a405a279bf5ba6f7ab7e536be95eef1ef6de2a4facbf5e5349e2fce4be1f87d1cfeeffbd964a39ce57ad7edfdcfbbcbda9cc0c6e99eb36a1e6b7a5a5aed67b95e455393ed737ef79dbaff7cb53efd4ad34c822993cdfb6c33a5937baffdda3d26a2748fadce62b26d5fb9db75bdcbf5fb8efb1dcf85b24cbc64b35c538e6b9953abb7ffda524b367bebe534ac33d7daf249c51d0d57c251fefbe7fe5bceab768d4c946cfcdbcf6d4cada719d7ca69239826a17cf7ad37d7d56a93ff20930436bc75d570777d372b9f94ae345589926cf8f7fdffb5b4d6fbb3ef569a622ba648367d39cfe58e77f653dab9ef3f4fe97b819f6a5348b6dd2bfddff29e79ce9c6357cbb39ce9916d6a7d6ffb392de33ff7f411d8b4dde9d4b8ccf5fe96abdf914dfb8ffbdd156bedb1ad17ecbe9fee7d5c28cb74235bfd9afdfb7678dee9b9cf479fa74160ebb8d6796f6d35cf9cd51c239be7689e5bc3797bcee7d62ccd8b6cb47bfe71afd5d2cb51ec7155ae18fb9814d9faee9a9e3d7fecb5df7dbbd22ad1ab543325b2695a2fd635676e6a95b3b257f1f008d34a35229ba6b9dd4d8f69debbdb1db7d2744cf355aaefe25dfc99afaaa843beeaf790adab9cde9ecfae7deb3b6ddbdc66b3dcdff8d33b376735df65198bf5fbcc416754664c866c97d336f35d35ce5bacb96ea5a9f754dfb534ea6f27cb963e6e1fb0d5cd55aee3b83f2bf5f7f795a6786c7520980ae13467ffbbfab96c73e7aa9d2f1321dbbe9cd6f99be79ed876f3a6f13f471f1fca321d7446e54d836c58a39efb1d9d9557dc67353960eeac31b599520d63bdb55b71e5bb9bd9627f73dfbcdbaff1a727cbfcf75c60cf3231e3329433a9209bc5db6b8eeb7cbbce33eb95a6bf7fa0f6fb17e6e392b7e80203d9bc466bc5ddfa7ef5af5d5e692a22f1af65954ac521f51e5303e8a677da99ada69b668bb569abf6b97e8a679d9ca6756762c0169efea8b4506aa78916a418328610401065440024e312003040241a0b46a3e19860b653bb001480024e9c74ac44998bc4c1308761140441c418620c3104184200010839545403f84f552812aa0f5e39cf9a854eb1daf04be92df6314eed054ff9363dfe7838088af04f032a247511b5195141f2de53f87cffc1a8b2a135cfe99c3eb4862dd205dd2cd656abd8ff4bb74e5af552630b73f2a7c53c84141e81b807edb7867a68f900ce81c75b46a0840e81f12d2f63ab4408418195128af61404c928badf0479aec6c4828ce51467179ec7f048b492ebf085af15dd89824dc22c03272c016bf4d405a2f7a1efc54c05230600d306ae2baba20ec5233027d830ae84319b3f3a25909ee2ba51ad03e913c8124e16f08c3e5f02657333e8b67f8dc96475c1da7100456fab59cbe3a9e2581c7c1f9580ace5445abc6e2abb171d912a5d1f4f4b81bf154d14f1e7e1e107c92a334474f62c4c2786ce91507d59cebd5447227230ec56836c1d53f8c128e85734096c4b452a68d2e25559fbbc31d9e2f57422191b9d86f4d734b3006f3cfc0b352fc590d515844b16f93e2218f49d6a46c86a52b7342b4693d3926b3ea0532d46d10347c3828233caec6d16f83c2a05cac098d76067e1a5435f6530c0fba2f56b98e4d7ef8248c7525e0244dd61912bc4c37ef7012e01fcd00d87e674d1e180b2779e8653bf24f4436bc7c359efe0e2ea44b1ffeff890cb154175e6b4959d954531fca1c46b8f28c2dfb6703585f0dc0949d9a2cf4af9a4a831f58c41a12474c6d71adb6f80084fe72b287de96d2000f9b67c68e81a6dd731f65a654e15ba37b814f9b4db2cc88fd99c355550ef50a31304d2c4549489f975cdf6c6e5c6c92418e8143ded4efa95ef69c3336157d51f245f1acefba4a52f8233cdc9e6599d84144483ec0a76b6d190921c3bcba320ade816687c62cb7df5270ea019cb7a1f17d0ff3afe69be5b70a6198f582dff2a597b4e7686d5682904a046a7b2cf31900b6b7f3bb04191622e3710a4a2c5192ea0c5896ea8c059dec4f3c277fe1118fd9618f29c526b29d7b43ba7b1ed871633af35f9d854bb0e77edf020b181c5bba13a491a696f97cba8564156a55480daae1fc9da4ff1b74381d129c3c1e52271e026cc8b3ce11868847fd50d15ce54b6e78cbec2a51c49f93e2115f42e591c9e1872612570e176e1c3a540d154693278bb2d9f1990a20551cf29faa5795643464c7c498e7b1182af65577f1beac49ed646386446a39ac439ac4b2b6b3aa79caf247b886514c57c447c8edc4f173d9a7cb2d4667d4be74c128f1cdaa9a6e538f602548e9df73e8b0165b54cfebf8435e55885db57f0afe53aa23520fb62840e1651620fd867c6bd7e0e5bc1d24c2605fe7c68dc8b54cd16d7b8823d909e5b61951864fc1117e3a39535ea9321597a49d4dd062836432a7ea7d1a30be2ccc9b15d3d035af3a379a2f4efde1980185fa923bc2f0071be528bf06e0062fb0a4370bb81c0f52726416c0281e74f3c82d8050297fe043ba5ef6dfe2f2690f3e87f3ef0f0baf8afdad2694e9b701f58a952051d4b7f94a0a1902468b77f2a7f08c87c959536636ddfe815267369e2445d13829cbeb5f9964b685f4969a7c5266e080a535192bb9b4017c8fff62b7ed271ad871e73ad0c80871a2bbb5214431b03ce34797909a036e60ecd3aba1be450dd88922214c42fe4b426c788a2773f7b82c89fee86d455c0eb7e3a1b3e20063db220c58740752fcf11328d0b7bd807dcfb99369771cc95b5903c7d276919dd7692db18dd671122e42c328a8bb1a51e3cdc7c74b8ae4918aa6e2cff9e1c1e1cd95676e353be7ecea809698a6fce80ed3197db0b23c1ee9dcb8901d0c5dc5e31d8682275840c7dfda286afbe76c372ba7152655a46cfd16d4576425f22c3a827d3d90e3662ce07812e9e1f170434c5d8cc588045e6b31dbdf90fa1790758f4343ab0a9acb0cd05d7c1f2dc1ccaf823c506a984c52a6f3fd8ec78edc49ebd33534f887c5fb0824ad075fe02b26a58019a7245d4712b0d09f9db5c82cd3ec35190da707b3c63fd3fc02d0ad09cdb65cd870bddbe04002e6f384bf91c4de734a391ad1bc1b89b0b0ade52d99ec5035d10da734a6f59983e5ed9cfe7d1c5a7d34ea4d27ab4e77bd8f38ad337ca83fc2238b2903e07855feb48a6b277a46290882085f81b728cac6e69febb1b10ecd829979e66ff6b96a52de270d4c7880144755df8a85a665f946754ea54cbbe89ca8217e794a70c8895c1354e7258df5ea050aab4c925dced3d42aeea5f15adb190649b8e069f4c63c5fc30ebb3df0c0e1b42805a20aab4e77b8329fa28f82ac10faeb9deb1fc767e867c2321836eec9141d5280f8e9b9ba133acbc202201ad352aba4700f7d6148b0f67c2feebeac3e9242f4c39494ad1fda41dbfd68d406ee09b83a17ca7b5a21413320822554dfa373ea344ad0c073c03b96ce30e85911682bb66881097714e40570c505490d72df1cbe6a92ac7a4119794aa39aef703c1351af99e8555650f4c7d64e3d08c1b397c5029d34dca05e26074404e2d63c027dc8068326349bc10d32ed5ca2788d320ada37c040ae0bf5ae41930f091bcc501fa4a10a4fccb09064d8d7374301886be3002ef586acb1fa1229146060ca40174077cdce55ef481ed93af48bf7a62628b54648bec7e60e459e4c18d6af402596bbe8057cd19f24c13c6622cf29c5d65212c806a7052551dc9decaa4afaf48eb7596695e619118d72a062768124d4fa6e58b91359b7d640cf1b25ab5f973902304bee8a9af2ca1339462afe681a7ca0659432f61ba44b17d0a004d576436eb7d5ac3f4dd66ca25cfee9f7b34c0d1eedf1a230f1da69740c749f1b278e01c10a1b1d5a51a2c0232744ad6c21f2271285c22312f4eb5d446c7a4f2bfd42be9a5a349cf4eb9ce687a08b4d18f005d27d2a40df2c7f6d8f98a6e2f9d22b27c00cb93f6ba05037fb565faac8b837f497de0cb92609ed22ded175b953ec75a91432924f2a1449d6897b94188483700540551935701cdb80080770b4e393b9843d3682702a198664c42f8154ff7d8592e92709a14b32ee557ec8567b020365d4a36ed36c18342c2ce80660e1328bdd5baf3d4a12731c1fb2a7b7267fd44e72bdb0c14bc30b6a90d1a86d24f789c75fb3dd3a50f1c964dfd27318a8f2d2e9059e7720cec1dbe4260c2605ce20bf6bf921450563215419b2f4efdd5fd94d922afc1b2d0e8a0d964d82de0bc546587003459fa53bee56b203ad8a1013f822ba7590929e8bbffc1443b2e4567b50755861b615d8d97d9f171bbedf59cd0c74eb1c950a297f54a8d143c23709d240e3af638d3450a86366d3f96f7a23d08baf8d3e3b908a0d7749a0b3c2096a6a7c21e1462671a782dd4f4ac2b569e7596a9dcb69aed8193744ca0c0713abf6019f4f68358487d0588afe080699ff98af0b6b4df916a957481fc0730bd7272eaefe33a4f590994546ba3751e083fe694f3ed67018c40e4630408fa767a60f381a67d67856722d36bc859a07528fbfcd8e815161b6634fb9dc34e162becdafeb4f98457a45d18c962197db3750af5bdbbd43cf9039a7a20e57fb76e6e9a84e0e447cfcd2cc2c7dfab264091147f6c52f7d9e6e23b43477fe4d5ff12a99ddc1c6103b9e8a5ef8009746dd103f409bb584808ae95caa64371d080df1e861ae59e86793c94ae68962df266a5de0f90e4051288d2af4d5d66c797765beb6cebd07767ec904af8c2bacd404af717442ccce26276445620b91cc0a335e522eb80dd3c693d346a3614a656786d62a2b010ec39e6ca93a67c1c24b9082da7284112387293cb300109858c84124c8972c99fc6530485baa2c83e1c266f4512fce576517801a1f3f39cc4fa5d01cdd884f6c7e832e86237818da28b98d82cef95156b6e14a6f8a1c92a048c532bd4017540e0f9992666993a328c223d5779970d40d931f9fb7c59081aa1fe9f4a2323a9d013b1af540911baba8e3c20a434372ce3d9d8069f7e6aafc178054f3963eea0516e195fba6f31dfedb6135a56878b1d7424664942efd410e2e3709af42be398b83cd1a22ea8f219c56c6d5831ed5a2b0df949e351a4f47119e0aa80a60cdc7d62d7ad0feb32425ecde7b93d9df731ea034013eba6c6294b005b43b2807c326b26b590e11a207a3822a205dc30b481cfd48fcc02caff8635850aa81301cc210d9d63ed1ca253fb1071869505c5981c5596b58406bdf0d5332f42c7c4086d20ee11e48fecdc3055b76ec2cee9d15ee9af6955b1b6c9d74dffcfa1d6c5025b3743009d872247f90ad681c4b14d1811e4f573a38c43f6ebdebb62a6b1f371fb7993ef791ce0da59fafcdc06d413afe665d05b4617ffef2a666e84056ade19374a2e8cbb6c7e3c453f1c2292978e0e79d12fa8582d4485ff35523424f60372f1712b4900f201125e04c15f2f23d340f39da52d120e3c4427241501ff2cd0b0123424a0a74dd6062a603020c04c027e11db52af4270f0841ebbf904428bb3ef2481949cfa154623ecf27c85f05812e8aba2a312af7b13649aa039cf5da27a9cf96b6f8e68ef87e8be50b82e51b2784ce55471b06694c85c96e1325caa9b3535a82b925f9bf959269e13f3f2b30e308fd0c05c7bd006758b0cda9c504ee4c34b78bdf86bd0d3934d4105c07c6056b4128662d99008c9adba8b93c581484f541f02d43ed9be04aa7d5fa17e8b1d1261c18779ccde0b6046e42a959835ea9c0880c6c22a681c3cb4029aa98e72a7013c77a29f9a91b838119d58b9944aed1d51b3641aa01845337a64e319e364bd0dc1f756f8499b45cb4416e91b94f9e57264a083f29b4a11bd6a45b02e476b0cc51ecbb1de460a53ff6e918fc410a15f205182b8f5926e11c9a39b51f56c66d5ecc9a31f3a0751e70f61b85b45ae6edd220cc36ece4283f47ac7f8d0f92fb435b5a2d9b3aea0e8d775b4651d241569af58a988e73e0e4268e222930ea1a6fbff16bd175de279da7c7bb0e443830808ad8df7ae5c83c0bd32206669a1afc40ad9dd9d5536128d6f47b1b96fa50eeca59c91ca7d38d63026ae4f884d4b94fa22e914e1a13be867c13b0179a3bd867ba566f826a7b83a8bbdbe8cd8294507a6ecb4003e9ff2094d88a20b3998a49726881e81a9bae8f2b10599a3c2504361c0036776438d5b1cd27441ba7af1e6bc6e9c6bef1ff6534d5959e49a158ecff3e83a2ea11d422d377d05257b78e8857f449cc2f61a054206f5d21dd58e763c98d18a724ada551f6797af7205a2239c86433502d93f8102635673d012a8174924975c53167ed0a864b215a44e0f724e735218a47c7bcb9a22051405d5240f987138546ce10aa58457bee5abe072048065d2ed51487fd36833778d700b474e0ef2b04d549615c284553e42dfece11d3e4a0b7f823749eb3714bcf92d845a7bd337a8f47564096c41789f4a4be374656ddfee1fe41f8862e30391231a9d05089cf1b8d8c9270a802ff3033f97c6f414e5cbb43f84586f011213cc84f952f54f7baf69d8c6921b89493f756e0176b5c2cdf552dba4a0c7de5773d5ad87bd4c2c0b30eb1805abb04151ffe877916d7ca5e226c925e211e228a2fcbb14a9776bfbd0477152b2ef1983fc6f1f4a8f8b32dcdf42a093b2658b900ed41389f1203a9207c0ea221669313f11039be016118c308c8b4ee128ef236ce4c949a664ef53ca52a31953cf06013aa40d4448a54fbf721721bd6c3d009ba6174df8cdb9c95b5dc8331e4e321f77c26025e2887c6f0e0f0ebe92d42086b2bfd79d33b0db33296c293d506f1589094d9ff29744e8018b3c6814742c38b1f4c0340f8e93714c3ee9c9a3974e07624bdfe0d37006ec0e7c9ffb3020b03fe28b66ab321b79341c19ef25fc787820a1f02e360b9d451e8bd53a547b9c90fb88e186ddd2d5a632eab586893067405786cca8cca9a3fce8534c52cc43497dd5f185585f47aca7f2fa5abd128b72c526297a22df89a880285c43abd37989f6ef78d429e68eaf6ebccf3f664dae0c69b3432e2094949072594a41f3c64044065e8bc2322417cd8cae1ebc64cce769d8b39e032e598c86b7f2f90253c42b2c0bb072d2c20a487e350ad3e90499d7912ef6454d076dcc45dcb15b24cbab72227ef6e802f472745867c1e0119f11dbfa5a9598dba02ed69c154c88a3999d5a44af4d8a15dc35dc98ab8149662ec68440057269784ea1d209916c42ff667847123ccfea169d28a0673ab29b3770998dcc9a1b7f693d0d6428c9778eb05bf3521b674813a5c9281551200320681223d94b280522f7def3c9a6dbabc0b1802cc9ff580e808de4d543c2bc5b98d135a9d4ce10719ad6112f1184712905c9ae80214740bf12c97e5706de08269b763485b1f09de7086c8e44af1442b09964271d890dad7201f3710c1de3aa899f219604f01d8fed755aa93ed25d8e25e65147d25b50166e321aebdf04aa76232c05b679b81e18ac8689f318dd10af4bb957b5836647a31ee86ef5571ca5c50b261cea414d5704e809b18019030388f5ed990d488b8676f4b6d759b8c8a5bb9f91ce897feefb2be4bcefa1e87caad73a9135488e22818bc9cc998111e4b903074d713742e02faaa3ec5b2ef7e3e8243faa691ce6ed0eb16848ff2e0d2869f777bed1b8dce54b52935814096bf1766ebf5624db83f5240ebba451cfe8ac99b575afdc7a8afdc8513033bca6f1a19d848c401507e9d22fe27a58445cf443037672f7e13cee0fb650d52003aeb1b909f71a387ef271b5a501603cc838d9243e80fc87dac6401658aeb46c00804176c7d2c600d5ba7e1089cd83ce814188ba9ee0858692ed01ce13d5b4992188792019955a27a180f56245c8976a6fc750cb1cf3fb3c42c4356b82e6e5436a80688ff1a3b7c114146a7fe69d2e02122404211ccca9ee3305e4a0bb7dd5db77269b65d5a195bf5f03f6643c7ee9b2120cfdfe517a1f01e48d0cc8549f05046609e5068e74494d0538520ca4a9a774d1d4a3e4900e713c5a291725f13345299447beb13ef3d07ef64ee6673255abb74ba794beb78cc306369c5dd82674ce88b3c64431320c0210df07a954a3030dc3330d1b64b9ae3d87d5ac561e90aeb928cd840638eba5380605eccb89706604d04f47825968409f9c8467a0027e7410cd8692c0fc340cf62105fd520f6a224ad7b19781b703ff9b7922c5fe71c6fcb84527336fbcea7301733eca8c2667ceff68fc24ad3b08f5d652c526384a1a4ff410336986140311fff2819b1c5f445b47df8fea8d89ab6438a7c9f767e3d774bf19f86e680f540fe37f31259aebf414b7a802fcc6199b4e906ff43f95e14a99d4f0088f0d7379db0f1667d0cef595a32403b25fe673cfeaab4d890fd5d675f4a38c22594392d20b3dd77de3a4f12272ff2d7cd13ab5034ef5de8bec46a89a960ff4fa65dada276b43fdb7496ca0f16eb8d037d9b87ef4c0f6c96ade73c34a9ff3e9bfb083be89f5efaa90b5bd631dce01fce11f7508f9a7e4f7e1c1e83defc005777a5a95408ead1330bbcc1bf05d43fd3fc6ee77f293e152f396f42382c1649eacd1f377f1c01d1f12fc179766f46eeeeae00fdbd45cab5adbd334e651c46c1cffbbe8e8d5650cba9ee2c2fba2d32eab5e37c3bdb77febe1f787152d1a54dd39ebec36569e8642bfff2b26a833f3205cc7881851e2d434d8bc4f1b18bec9f34c7718ddd787d5f98584acbf80d5d7a85f6da9addc5c9e371d9f07f673b51ab1b69ac3aa1325a217ae6f7a96e6461a93751e2985e05fa3cb5f5ca2242443e94f4a3d5fb9aecfdb0383c91834e177247cd497d993a60e53e674a431e84ab6215fdfd9600401991c4c9bd7d464ebcf84a8cda84c7b9ac0243ad3607af91a04c4b692b8ac1074ffbc43db507d55482222fa4059ac3608b47642ce67e4fae8b37e25efbfccbc8b585124cb33d6ed43b42c7373526613acaeb757c43407af6b3bcec9e85d61697336ee9b80f507a9695fc0e6645def2112d3cb6fba245aecad09bae39ca531e16ae5a86f62694777279fcf88345e8dbe75c9ab681aef5d62668c8ab512e36e314e3dfd5ccdebfe09077d6968ed677f95d7b2782e12bdaf25002c70902d5927caac8abecc7e7df4b7a3cd23beef00e684a14fad5866f5ec777d97c9b010ff8969cec8de2effcc5fd413aff6a6c6a416d07fb2cb9de7c36f2b713b0be2b0f0b9e8762f42b48cad4df66ff79e6e5a8ac9071b3c6436affac6bba83a46e3ccccf6db6de3d52102c3d0d173af9f954fbb6279b6e03ae8ac0d09e1ee412c691c71356ca6ba948ce6b3bea90f99f7eb8eccb4cdadbe592c65d6eb18a04f662364baaea721c438e745d7d136c322747e7e33629f1db4b28251067981dc2a267004bac92268ebe8bd9371aae57e5e731e9c338acb54359ffdc5130941f6832eb2103f12adb839a58c4bc16985b9d0fc76b48fe04f77e3663baf4ff710d394e05647786ba6fa37c7193f24df57beb38c288e9b3a6021fc7a4ea56d72b8f80b2ec87e8b2285f2c8ffc614b0a632d76cba2faeff6ae86679f2e5c2b8c916e19e9ef28e5938bca3e5b2aaf1ee46e9d49f6b1470eb9fc013ac0194b0cd67bcca7dead9d0890d759686e8e90d75dbca6c4c826fea97d1cb44f4d276801006e7d0221f67cc2aeae45438071bc76279e39dd45fd975ef7a366279fe3170cd242b2daf55d413ca3832fd27a1fae2eac7183f03eabd5ac6024a7196e398c69a5acf55011970e6872c5cc1b84fae469d08eda924477a0a29232e9e516afa904900acf118d5f115d23e6070df992dd4be9e25f40f81ba36ecb5be6d47fded1fb5a74e5a6fdbff69f3fabebb66d9b2a7c1c1df0c9737e55f1d89f1a365d348fef7220b36547e5a912f9b05a212060272a460f300592c450facdff39325f049cd4cabc38984dd525b7540e3c3a484432be1fe3a094b57358a2c6333dc2f277b6f2be32df96debfe86dbffafd2c386ce799ca4380c64263dd76e3a0787434357a1b944a41211fb3e3c0b3fde4e201c133da9ff94499a11f60e9f391b9506bff755d9c713f8d2a021b8f1ec97cb8677a3ced64bbb6885d56154e55956a5ea036a435f85f5abe85ae4bf5127e048ed31145f66e369db17cbe819e9f7bb78aac99b333d18d72e7a1ef0dbd6051a550f4f549135192ce7ec5db5c6276cf2d3473027055bb0abbe416bfc1f90e6688a5d59ca866456c835f3257dbff2df00563dbf3a4c413d2a18c7d4bc0b4b8a04e62a2d94af06948389aa02b9cb45086f8e87ca3cf8faf3bb535223aec58e366e8f0cf525a298623ecde0eb8adbc14bb86f2904511eb62f10dc4b94bbcdc00f582f6fff436197632feb68f93d74d3b83bfe13933136ae908ab6927df8881b0267455e7e7ef55a961c4ca6d0fdae1144c815ae280c6fbd49eb16884146b5cbe5ea618993226a6a5f6c2633d00b0d40d3812fd2a29113eaebbe7d01e964ada8ecacaa22e6e4d3cc4dbfab4bc39ac33e3d0fbaf21100b144423f1089988d099490ec5dd0e909010f2f0451bb2260b029a1975e89bffa2abca5f4b0c3d298e50010b9e365cba9991ac8d1c02c3e261d0898b3e90f6beb72463f62a0c5d92eb6eb2c0b7a647ff058e80ea3e71ed9f4e0c0f7feab391837deceb1ed5da795f7826404b857dbc2cfa03f1f6a0555f3adffe2fe9fc61f1a53f949ba02b4fcd35bb877c723decaca5b97374bf78a7a360064ffd96887c708e5d5b06e821219c7b513c5a9821e582c82e8e4504d44818879827e3c90397fa5a097302ad796596ac7f9a9699a032cae443ff7707a9eda8a8404e915d20e5f8e4246d044382b142a9a3f349eabf6747f143d6c6dc5e64d776145c1f60783b86548694d9ba13c38ff4f192a3f6bc966d95ad074a13395c6d105199d7ec4c3cb99c549598fb4d7ec58c320915346e883601ea1ccf595e334c0c205f6961bd858f6ac4c97129bd4356862c0d5d91807c82a6bf431597f55fa1bceb190801c8df3e0f541d91729f2ad9dd396e6f33f3df0009792ad42c244b0f6b7539ec40877e99600f5b97688a6fec7d2aefb134398357f38e4d369bf6ea0063f8a301c97571edef728e6ac26065fbafd390a301aba7dbe6970ffe3cb22bea5e1c25fd593367ed45c60841feff46f46c899574c7e08c12446cbfd0f8419c2493070aec385478b4e04925f6ddd2464206e7dc217035f288adcf833aa0c9206b1de08ebdefab3a5d7e6d7ecadba3e666800d425356aedbaff3795b4b3fa7566fb66d0fc328b9bd6cf63ca5d72adcc165fba834e7af31383389945370b7af88b4678703bcbd9cd189775281810f098779fe45a2a702c2025caadff5d3ecce22c385c1a1f5e964a53d86bda145e645502d1c6a9aa7e465f90b6dd74f57c01c2dcc50ee376936c7c52fb8abbbc7d59fa4b9cfcf3266e3e3a91966d81aeec08e9b284a541ec8819984e53dbf1c51f147a0d7a4e90e9088b6c910aec981aea1c73935d49c53612137ec2cfe13cf9fa3cea9cba35618c69d5031cc59a119117cc3636e68b59ee908d28df9187a658069d59d5556528827ab1ed3956203ca61c1db538304519c50057495a8cb42876ac08656cebbc38dbaa1af783f781708af66421a9a306afd62d0fd5c3fae44ec1e96bfc684b95d89a9a74c0ec238549c06803e5f8cc207ceb35d497a7f97d31d51bc105ccd028a65be0e3316e7017d977f361c703ea857407b7e05be9d065dadf5ca564d03a5f407139bca640cc518271c70f9f403e082331c53753dbe83c34fe370a91c3e2b23fa24765f1fd46d9d126266de3bd189684f8ab61566798be173970de6f59b2794f72e1c67b692bcdb62433330ba6282399404214a7705b7a1b98eb98794d99d20e0022d656d8fba96aaa51bbbcde5395bf0dc2185ffebc81ed2d68b5f4675b1f448a6b87b9d4d2f1a31cb231255efc2039fa5a70831a2bf4034b9b559dd98031fc1469b62e4b7e87c2912a85e5754eef06560cfca0f63fa41adf686939b71695c141b1ef080a0cb8e60e3a877ddecd4f2ae15bba28df06c6c2e475b744daf99c2960598bd7fe34ecc5c1c7539e41540d251176dfcd24fee778c3c1e61ac0aade24946175729a86408303613b37b3531212f198e1dee091146e1e8ec52aff21e3b3565995d95ecf73df10e46225682d9dfd2d448a4074e885551b8ac93184c39a7bac137fe6d5619aa55c805083a07137f342829e184b4532df4a7a03845e55557bd6eaed39451e4b24f7c69cf692bbd3ef935f48202c76a612d198777a3f34fd402ced0c3c191b66f2f7cfdf57eb7270a0b7abf16bb684c00840bba04c135e6ff31e94a3cfaf1dd0711e412fcfcfdfdb4387dbb523403aa14131f7f2d0014241ea42ec0d2c7f83a694709ca2fba107d300ae3cff3f728fad5cd383e3cef91d9e13ff330c41274c0ef78f64e94d8ace18f3581fb1e5611dbf4e422bb245afb863e88efe633ce786973af7913d5348f76934c6ce145de99311a8e2bf1f81c472e7256d876c041858227e93288fe0c661a65d440df1bba78014b1d9a8067fc3c58fd38a1396b6568848e0a2a3c061ef40f595e37a0d05bfa83d8f0ae8445da3773ff35f6adc860a380ffa86208343affd0ca2fc170fae253d03745fbb76067f027645ee86584ba3a3cf734f5214e1a2970f86746c1de1c57c3684c190a68a8921a0c7920112b8a951281861b69810023aab8450a06f1bbea44a5e003d5b3d2d3b4740419f157134a0a7666006eab0630284ed2a944906786d0664a0c6ba9280988fa2994480575b101372b8231588f128ca46017a3607b022073b526058a7c2cc04d06763000b6dd429098c712dcc4804dd2c979b6d89c1adf6db52d466966d27d8b05eabd55aead5cc515bbc349ea846871c2cfd9ea01e2b6be03d457c689383f709fbb84414e249c053391144a7c00f6585109e809e9509413c0d785a2a08e10ceca99410e213d0c7920248e7408f4a85219c053e2c11417606f4585a006492a764209a07c2e553c29190c64e66c0829f2164474d0255d812933e1d7aaa18a34f3b2b165dfd6a82cdd1d394c1501579d494350698563f6e19be01031dd3af60d4a1e924f2469a5b7286fdb14b9d707ddc202c39a42745328c3f2e9ecac899c9b45903d5fb758ded868a5a2d34cec1058b3258f455f8f5d28e5e2fcf305452eb184a1b16e2704b10e3709b498054dfb4bc44710acbf439ffd11488870143a135669e9a7c4ddd4fc2e6401729ac4e8ba4967c367c6ea7a901f6af68b7a3c44dffbb5d3573dc2f09bd1faff2e661e38ecbd879fcf658c7bedfdf296e56ab9c1b1c3aadb3306ff107e0335108f2b17ec44e9ef81e4a276fed60ee8c39ba1a95708414345db452b1d6ce9aa8ad3ea2f8724989aa05b838da5cb03d720613dfa679905931c257d9f327a81281c1898ae88a262ab11a7e35f34e8ee0e69f1a0692138cd8a58a69502861f5acfdd95e587c577d9eb0be33a53e2bf7dcdabc6656aaec94d907f54d8b2ed711b5798fa26359ec421bafa30bb651a280ca7e771048051e16c79d0008d9ef7a2f826126d6438f4ca120041c1c550a1918b59716d284cc1801e672f4bafadc255bb00e6b620e1c9e96817c022a5a87cf3809369a54b4dfb751bd53affc3277bc71175d7616e668621ee2f0096081a1989d5abb2511173cdbbc51bea9032fe044e8233443ebcebc9c65c5d858b43b4d4d81537c363fa82ebedd074fbfc3c2e2eb6b1768f76138e4153a74720e9a0b0a75510cd0770d3261082900d4021ddaacae993331c7c961db3b8027a4c946af44cc24eeb440ae452fe53edb53b4e5f084242cf6013d6fa2067ce1df8e7d8aa84a632a94b9498707c7ebf2757da1b8bee0bcd61c531376092d9bdfa5be52ec2d9d3e8fcee72439f058c2da106e933c1a044c46a6898db05fc6c2d1175d9a12454f723eadd93c5710c58463e9c813da944d044454e4057c6a6dc539e6ad3c9e295d5cc1ea7e3b188218be9194c4852bc4f14a3f0d46b12304dd6291c116daf6c226ad7504ab5d841fa3edc390b4761bfc9cf31fc86ad0fc45e27b73379f206086fb9a36331a26448ead0f04a3118df726c6d4e75011afbd88c0706c8c57113de19c427f816dabd7512405e0a0d28f6d394e4f55099afd4276cf886ce98d24303fed656eb4f26cd306ad47d046d9b13d1b9166b6101e7f8790fb5eb41d0434fc0134f398e46b1c2d0c6c782d3de05fc26ae87be4343790d5edc9b0f62649f11539740ece8dd8cc4a2411e83fcb926bbff64c40f4142632b71c89fd1c0d5c62246517895897f8f25e99c8834bf95e9825e68299621a7941c1ea7e630e1da0052fa04da6037b049428c7e304f01f1ea0e752a9b0cb9302d53a34250c7f5f96b9ab1cf5ade30eb70ca885cc57307fa13ed2dff821ab7dd2b91a171b97f62c42e8b593aad3eddc08b77c52782cd59ce2639665130aa82fb7e349e459d37b2e76f36d404813b3a37ab2b3039d7de25baf9dc2cc51335e416c53e0ee916f1a664877660b6de14c757e5b2e519ed0dfc1b3eb5c8050877664deaeeb42b776a11b01fb857d71b716ee9567012dce698568d9e3682f98b897233d13d821b0457795400da99947ee1f3543c111eab32a2910b73f605eed85d6c041e5529c3d3f246a679c37cfc240331471956c481c4323e3f7f2bd39ade2ac5f611175738af7ce3808336671c7688409467cab698734ab340ebca3d62f3dd75f6394ce116098cdda5ae6c017c5e13db5b8f906b40ef2ff29f3cb64de1d4d14024d88894c72986233d05419c89a67dcbf459a30676a6d2bcd9e0e4fcefcbde43ac1313e8ff17f1403d7e71e4c9702b1befbf0f0cecedc8d0851b286a84fedc5a2d6959feba5af756a76902618cadaa98c9dec270603bcb05d38d0d5eaf0d2c2dce21b09179fa707f230bd33c51eb5d3b21520cdb0da08665cd662b23cdac4bd3d31f1fdd28725adc72f1570a21c68bddc63a87cbaf9a923b74fb843984600347179a0941dbeb3dc39840b7d09be738bf629286def56d4344ba4d39aec16db4892b5e3b1e200c7fa2a46054fa881870195c2b4694367672964876d9f735566d7b6f34e72c0381e12df8c064ce3c36836b160deccd062039440235a7cc05a719fb1f8c04051fd033aab6c2b8699a0abe96be46dd6bcd4b1cbf99dfb51748f546f1215f0b77007197105fa073526ec63d6a4428e596ee4ef73a4b6fe68be32c9b28e3ad9211210bb1d3489fe51de11ab1d8abf93aac4e47e747f0fa5ee8ebeaac18a8492b2f50a9b66bd75560752652a3f699dc3093e8f40a7aea75414140f840dc89246f1a4349e8b2f236a514e24929f1e32a026e9e02775e7c64a098e366566871c51cea9cb2d51a122ce05d06395af4035db967d73d910e5b46e3de1a844d1e7a518db4d9f430367ba2c75e49b210b70bfce160f67e52cd15396c1848bef1ce55d1dd96355be6dc3dbabccf61b307fb00bd696b9865e3ae978a8fa0fa545b1df3fff9767d54aa1de013fe959b6f08a62b5848c91e04d2550363487910af158f980b2fb2297638a64f89a73f9730dfa9ea3643ae14ddfca04ef726b0aa24cfce08f00e9958740b77868b736476e75eefc22289431ef1eb5c51697aa7cef146f8e71b6f034666e9e782a261dc70c1ed96e018e0fd3454725121030a423e94d5383c939c88a2546d085e9e64a4af2c61bc5c1852428753b1c8f04830c8e71b3ee68b7e85b2e457335c2c2f1ae94ec2973c17cc6a78d5b44a89b0e7e966c40ab5e23fb5ba53cc0ee05a3815ec87dd5365b2c2d6a4d271f9f2c35c085da64e6548be68db689860b0e1297238b890802429ac7ae1fcc01b3801f46813a94bbd67cbfceb435b12de3099663faa0c9b34c76375b067f2b2fdcddab5dd42dcbc544665daaefb393f3cf5b828a3ef5bcdccf326d585ce45e0bc38f6208eaa0f94a498529e7b13a97c3abacd4a9d4a242d81af67f4594fbe0de4a78449c62202a4778d1fe1a0c1d272eda152aba6ecd4ae616e53d82fec4d5612da4afd8621f4ccece0ce630df788c65554783beb98a7acc5811e60f96e72bfef3d7bd33835a43bffddfb7b8a9eeadb195e17028ab07c6415df80994501743064c40a165a464033d633c58d3e544ef10f3fd78a23e1b4cb71f04e1ded47d4e152920077f672e2782b0e213404621ebcb83eb4441eddd162266c7b472901e0530bef474be8a654c2e538d01abf0e12615295e6ea720cb0780bad8fb732f44c8ee600e89b92996b5c0fd11f33ebbd62e1d155dbed0929b870283c1de832d537dd6f8b2987e7a1c9781d67300406ef30818ce562faadd79b266eb68fb5ea22e3839da1582fdca2a7bcb74f530f4fcb5e8c1eb992f450011506d8076910bc98e4d69de4174b36786b3c3ea19166fa2f561f443fb25ee025e3171a4b75cfb4da8238903697fc62e22f307f0d1ee70e6f58237c497e60d0e0486ee3bbbe335d4e19e0594439a0f220138983d2d8550eccc0ab62baafd84ae3f19f8dee9e5babfafb1ba5a8f3c0bff2b19c54ef6d87e38ccb1e71e879e2d617ea7314f8cfb1c859453c2970029f5325e243e0bf86e4d88674b97b424d241c01fb87db253120703fec6769a8855060ab44bbb1356542100903e5cae51d20941053d61f0c4c1221a24dc89ab2b0902491828d72cef485889820078f278a146290e04d5d5c40113871468907726afa824042061bc58bba423910a0ae2c0e983859a48389057d613040c690733e896a48a823078ea886a794f93800ce0ad9f07f9c9ea850ed994323141cf0e0dd5aaec8dd7a7030dc7ca9204d9de5624dde549f0434f878897368e6faccc8b556d2347af950b6035178eb4ed33db7ae87c6fe79b76eceb97d860194edf1c8d74c4d0c587cb88691b24ee2c61407bb2d46b3897478402737c9d3b5374fe27654386bf677c4f454e001debdcaeab0293e8fa9cca02a9b0e3b7b8e09941ee977a7d77871e82ccd354eff06aef3e7cd2e8c3517f3fd836430f82b39f21e663917d3b56d65c77c0b57510563f90e1e49c3940da79c0122546c70a7f64dc3d6ba4554cc297f19b03c4709b93416f9f49843d134cc0b86cc59847de7eccce5a1a83326097469b0d8697f9f4c7b6f7e02b2b0e72dd53efd892e7c3c3d9738f15274fc36fa070b2774a8733f8b7ae7f0f2ff93c3ef16924d345c97e549ffb9e99aaadf1e2d6f31a01d6881c7a961779bc109f39b3dbe5c4c068c9b03fec97afc6bcfbab48f416f4690b9859aa5c791dd58c648443b5bfc9cfd06b81f68e881096d5004ba2c59045368baeb2de8706d4423740fe927af8d54bd793b021ef82613e0f34894ec1bf3e72a1c972b24f1f10c34f9976ef254383178c3c147ba2f1a847d0a5c92692648ea97485a1e22de4b0916d18ddc129d3a9382f42ea61c20f6523b2cbfe6887ea2ea3cdf3759328fc955a65ca960e44936471b58cb541d2c66c8325e4e7241095d172cffff2d0d5629440899f8db6f974afaf3f5f5ec60ce2b66ee4efaf070601731a02a03ce514a8c3a11b1443edd4bc771dacc00a7ea39b08211628a2a5e448da12a5f065030157ed973bad3f2511847b3756689287160c0d942973497cea921311f288c5e3a63478030f268473912f2c402875d43e5d940ca4fcde83f9f55c3ee8845ad4495fef066292dc1d14c73247e9172f0d3aef8fc7f2351ace93d8fdb46152d19d3ae35296d24feced6ba4c7203a6a87406fc78eac8fc5475efe8d15a4c9f90e989cd380c937edb42c5676737992fde2bb0d24ec47f3af51c8a6bf2bc97daa7ace840de05a45ee8fe6912f4397a314e56bd3aafb53785849bc7075a18381cb7096ed739c79968cfa1d60d6335c84342ee7e48f39ad84132fe86c166511daa351cc06d25638f261d7faee99f64554a2c31edb1a31d23d2aa7b33f6f3198300be4dcb6567a5ac2b1ab37c76c5c3ce48bf4d8d674f603a7f55c1d2aab26166c3170806e72354f729a4e2f48a54efa46239995e2193cd98075b27928ca04576ef0505ac44be58929c032d31522fd70679780a4e03d1372e614a90ad35f3f862989af2ec26698116687783078fe0b18fe5ab38a3aa02877c66e50ec4c225edf0897eb48b39880fdb0afade937c1cedc1cd978685937718a4252be1649a716c9522f0c2eb9241a2023554eb7e828523f2488d6d036c637e3048687a9cfb32a9f42b2a02eeff6217504be1977beedf4e7b91dc828b0e11f4827570879a2f9d22465013ce8b7ce6fa667699132d7c0046443707c9af0217bfd504aa1fe3a23a838b7a23410f6ae2e659df94800d540f3ca6472936072497ce0bc66ce6e80f553fbac30a7dfedf25a65c00891b9b6f91333d663613afd0a5b0fb52efb821ea4ecffa60f51c1ad27f40bf1dd224ea0ce0c1b9d8ebe2616daf4813f7299f97c94daf6b8d17d12edace15737c2c5f54fe05f0efd633f471597795f480e3b586e3e1fc6194e1cbe7a0cf63253b9c77f3e6e1af0a889edbe5fdf5fad2f99fe2cc16fd038a0718a77eaed80a2819d6dc5f49fda6c79ee34222dda058d6ef78974fd33195945ad59e725f03c7cc93d530474665a9f77a79f87c927d47e6fbef699ed3eebfe20b0463ffbbcbaf1eeb0fd3299b7f65ef64ff666fef7a796cd85eb7889051e81921da14c59d44ee96e49679e5b77947339e247d29be725e2d1a7af68f9c8251c286b9dd7cfe8d82825872585ddb9d6fa851855d42e17fe0edd197c35329fc29d4f06eaf37cac98afa9b63578c1a669e678fd031b9cedf951b8abfe4328908ab509799730c29fd2ed8dcd5e2b7999fdbf3cde563385fe4a5eaea56924e7cffd699456366c189fa8251e8eb64eec338522f8b52274bc177225d2485d60751f6898dca8cf92c5fd7fbfbc4efe0e70db0f619ddbf8a5ce498947283e4dd640b67b7bebb93b990f60c0d0d497d02d35a9195486fa4a1d3b6c4c30a3deb8fee76d06b020c98a98c07225648fda2717dd33b2ae1b4432e4988cf5a6db228c7fabd2e36d2720d503dc23477d83df0468dac2741f13f1b32b6213ae8e2f0d5bc5e4df1f2f3cea4ccfba03b14e513fe0293da23c3ecdf7b23ab69201c485a2d90c2bf08cc75c66318c937b1d2b40106c47a67590fd011cc68754606c00e1573dea9ca16939281985f698fe6d7477e984a629926c6a56d85f900a00c6de208d0825388fdb3a07c58fda5a3a1b81b9ea3a0423e27195d63e159c4339da7886021e4a2264aa4121fd1a35fc7448d9088b387513040900d4a3d88cad6518ab52d8cbebbb572b6e882a2f1bc4a2519d1507b4929996927281bdfdb000f7b60167fd67ae176ea1b8155a6c9bf6287acb38246d464578ffa371b8bc3f100bbd2adbdbfc6d638ec8c70cbaf579565e6c701fa4fc5cf9c6ba2817b42dc0de062d17a1bbbf61d27d4e794ce457f1440e704dcb76f0f1d39c435dc6b395c294f59d9c195a83f1301b3bec7c7bde72270b9aaa23d9f4c9956f23cc63e2bcc95f6ef126e20440bec25de5fd525651d2bf2e7ae4a11a40d4cf94aacf7f3e0534d3cb8a2943cfe30a9e8f35cfb4a3aab1d7e07b6380105f9d9b681bec6a4231786160901468e6de29d78f93f65d8d044627586d3652e045316f26293c79ec5f2ef8cfcd07b2cb26fe0003320ffcfd4580ec42b7d448535ce41b953d705aaf1b3c998fa20b6cd5f1e088e9349af1c418ecb75e90f1ef4ee14037053ebd8215aed8195abc82e40c0d3609b7b25d4a48903208d209f94c1a91880e64c9824c2a537f410a359dcecb921ae69a1a97c7a8680a8520f9a3c07c6d2bb2d22404df5ad4433e82337b2c5b66536a0320a532783ab1d952d9da2e110c0c18acd1b17bcfe12325585c30ee8758edc6a04328e64f0bac8db6463ff4eab080c081ac39cd8535967fe9fb1bbce6e3b929761ce2dd1290f9ef320d0ce32fcdc87d917e08bb3d90e7689143b87b097a77130ad21b8729e57ae0539893c368f2dd54bc33af43494d0880b106c6188a63a987c6f421382388fad16c74131fc7c4a8934ce241835bcd5de21036648162c95ee63ab1fb6f43056719c2c6734a5d98252cadf52814b650003d3f2319e55803f201f8b46934bee05f4b1848a1bb773316e2a070d2f564f991bd1c6d6513df5814ea25946b603fc94799931ee83d00ea95ba70ac5c0a770fda2714a3229fe7f4a5e99cd0883483c3dba869622aa55ebcdc943819011ece06f272529f44a2bd08949ede06b98587e8e3b92f2b3f0b775e92f12dfe63d7de7bbb3afed85beff0ef635f27d32d4604ce42d23febf093cc059b3b6c571f0b60cba828e2476b13b9a300bb4396c148d9b30d678d41e15960bef85290cfe163b84c9da02713cc95e9daeb0b710d58b6522fb3ec7d687ed58bdce8bcf3c0d727b898a4b17a065c39b727c1274a35d07e5e6b8664165cee3f41bfe3aecc242c176a573cb4ea1165a15cd20bed66418f36a5f70d345ae5810bed526bd44fc3f39b5807caf515315a690b9514e7956a1632d941e33a0c6b603475befcb42330bad693ea053321e8ec5141b09412d1db09f76980aaca30a347ec90b8901ee1351719dfe353f046b8f84e49eee14007b96458bbeb5bf8410232ec7a195e70934c897b5642895c9e52c32507c6306df7a528b99233912689a8f6eacaf29905dc6b6ec154660f0afa2df4a9d217c51d8a3a2a3e0be94570d48263609e484c578f3823996ad3a18acaef79bde829d9c686b00e618d50476124117df67e6f202b5184148e6f313077d338ebd515acc0af79d3c69057e740d0d0478629b456c105c90a44d96e7f73a5439b4e05542338c45e7683d559db680414924b41aaccb4fd1ae25391a21df073ef650891ec23a13f2e48e1406e35931015553bc463a9cc1522c579aeee4dba451649ee72fb7f459baf1d6a1fe21a06462ce4162e99d96a661488d4523ec2c233bcdaa55630f2a7f892f79947888cf08a921db6cd1c232a5d17ba54d1ab800a0a69d9c38b4d491f2d74bdb3884a52219c0d83528a03d81955c45eef0349140627d786638546de53133c8727b785fd37aacd825f857fae0cf29f548ec5218378aeffb841afe6cdcafe887edd762800f20800318e419d9656a0cb85e71a7f9f56c05556cc8d74e97bb79709bfe4cf105858b9d5819a2f01038ea425ef4de0ae4a04021aabc34532ae7f085c91c60e1c270a3dc3408ecc4c6153329e5c4ddda9e833f221e194dcaa8a8ac8a8be5968af2b2c6bd084460070df117f46ca06d4d5f3da95e625d96edb110ddb1e235596e63f0f0c23c00185ee8074b21c3c21a12851434ec74686c07542c44e669f4cb28df080894abbba0a5aa12ef5195046b2bbdb4730813846d982789c9b22f11815be5b94c9911eaa839c00f8023ab7ff29f891822d59a0d47c563206e97bd5a65f8eedc38aa26956f9d397295db5719c7b685b0d3ad215993ee8e000b10eb5355bd43d1316ba37a14503e88e69b53bf7faaeb352b474aac42b701742a5d08e492ce2d298a5f7450f7cdacb90e6ee9e2330d13215c9fdee754d767fc4e3e095c84ff3db70f767e35cf039e2c7961ef26e4b3d1a4629bd0781034f0dd2f9bcec862f8a6b3925e048fb3c13d3bd7c6bc8383fb1bfee7cf8f022276579f859c3bcbec5f6765737ef282eeb269e2cae1f75b708f5a601d5f2698cb1acfe7b2ca356593a3fec6a2084510ad8c9dc5653482f85e1dfe2be8e6103bb44a4e68b9ddf6ec3d9c083174a2b4a6a82734c562c913e3d8a35437b60fa019ad784674698e6d1b2bc0f8a8b32bab82adbfd99cf009f4de54f48e4e879e498b5b43cad41fee012ce7dfa3f76cba6759d4da79ceeb1e61444712fbf4a90affe9be2a5f14ec0621f06d4a9142317eaf3d2f5cf72009563ef9d5346937b796a67153468393a3df70a83549236bc74356e47f6d6a4f16a144833b9f404730ab0252e509c04f7e59ba02d4e3c206925ec8a4f20a6a0f2b5a153768108db5ff09573da469dd4d1fb2f7c630390f331106aaaab834e7178b93a630a9fce166a2caf1db98386507d0714d0b0c1370dede63477463bd421d7b9ababc639b4842d8cebb4ecff81d3edc125b811c9b18d478a5b1a4c2916a724f14e1fd2c7033bfc482bc86f50df95b18f2fc5ee38317ca5447c4e4a2c7ed0c09168ca27b3642f97e69d8d55f3bf4d46f4f700c9911e118abc09eb5b47aee2939926030478170120260e5a2ef65f19ef68916d0f8de10cbb8a7d11d34afcc1ee5d0c2b8de1067bbad9f3b90aa56351ac644ce5cdc796bd6e8c8804d851188090bab02a4530ba8199cb830123c2ea8021efff05a399434405507f164a4024d6846a1f80a4acae01449245b706677324bf41a3c0e258c007a6645e4c9f9765f5d9eaa5ce1ea9b246b2912bc11318b30fcc9c2ecccb141d0213ae497d80edab651bd1a8054bc457ff43566b2cd150ecfa24fd1b787ff47e7450fab0085185827dacf86136bea2ac715134c4dea71aedecf23423f36c57053b819d214ee88391a584cd307cbff5e63db168ab931f40d4dab5096f4d28ecd924a8b6f78aa97df17cd44788ff58920c9b4c92b35d795c20c5fee41956833126f25b68d0f42e68224e195b52f7e35b135ca69aada56e4f7cbbcaffad37e9bfa1cf2cffdacda9875db447c8d5218c137ce695ab187b704fedad9833bb8263f02f9a45bb58fbdbadc69c4ce8b9f94e51177f3fdab8cd20683818c9e02148dc872903a27f22406b0b2cd38345fe9607a3ecbb52dac92e59ba18be084c3635da795e94cf98897ebd6e827121c71b177eace6c30b3470479253a20b4a61d172209856233f17d9ad350f9bbd152e5bf0a7b434231ade6b679bd74668ffaca2509fd4cd3466409218cfebc112eb0c1e52ede466d696b25a80b1ee874bcf7266ecaccdc77bb40f7d283d9827ad23fe92fbf7ad4d9e3767cb6be3fc82f905a5ad3723debcccd537a368e7161dbcc1c8b14f9a2d33c7a03e71fea3d3abf3b01ebd8fe9dd81f8cbd14d5489201f843c6674cac2a465e0881121dc83d288d1f689c2da7a802f8bcd9724c379607ed5a78e37ce5b2101d480e60bd69503661dba1268ae01b1bc2c331c12cbf87c565c31bc0e6393e28770d9afff3ae4a253ce7c0adc97ea2953fb28ee1dd27185bd0d4c79a4565cd9eaf459c431afc48fdc3a1de11a8568ab5bcbac451b5daf7bedfcbe7c2e1d27e65378d48dffa2a881c6a43e4c1a1205ab5ff6f38d6e866ec20fcb642a7526c92df43e9201fa55f9fbddcbb3e2921aadb5e07fa0facb3eabb622355e3039dde93c50c818326664828a413ba57448a132056808793cd067a830cd1069d53da0b96634e22ef4812fc60584f57dc5f2c063f72db2ff404aae0ac816c4ba88754715341864a58af16a6cdbae22fb922584ca9d8026a51c9cb928c77a6b6ae72daa73769ae802859d0224e070ea9dad841fd271f0f2c98ebbd453361f35a009dfb306211e1db66e62e97bcc69a13c8191a8b28cb31635da056f5e09bcc08c131ba720b48da0768288eca29c0a6bccc98c1f42316154059dd77b7ce2a39ad90355d9f1958cc0595f554f2f6c610b41221b4b6db61b7aa64423c8510ac0a0f4a4d905807ffaf42070c9f11278f92fe87f53e92daeffc2358999eb83912eee2875526d045806780e9836b6807f9e3a2d1c1f52cbbfa0b7d1ef9211569014a293af8a4b254df14f3082520d8489bf0cc14870d592d807afd83563333b67a0c96470d7309e334fc5f7f8fe777340cd4f0cf9b3cc91c8561be43a3903814a9373a328fc928215b5fd035d80da4412c3c8e133f59a9672148152b46749d0b312108d3ac4aa381a5ac684aabda8d196b21605d4abbba059753dd8798cab48e3659a7e38eceb724964051ff2c7699838919d9a0fc82fa43eb5da38d84a79f5aca7aee64cff7a9f825d381fce43a7d28af5ecb5c82b7667569c5fda368166a9fb46598def0e87416d83e8676dc15adc6e2f1c9f38fcb8a10c4eab6085333c98e3fc4d83e6e5d865b5e8ff1e440dc4d9f479154a10f2f1aac3a5a1ed1253cdef06ace87f83e0d6facc6bfafff8e9af3dfa9fb4039f9d0da3c04da6f6279aa4f1ae69321cde91e311a6c1436642283d7487dcd8e0271c31d3f0e243b129b187bcc87c3a04a25893909f505f06d5549c83e22b3fb3cc63a7d3b8155920fea9061b000e83a6f62b16cf8c7de0417cf9a97bb5a1879c0285c872a2c298e15a2c3f9c78b594a72c5464e87d9e0aed5e9117553f563b79070c51ba32bf2e5da3807efd3d309f53050eee46a9a7fe76caf00391fe2014fc88e81c7caaf050b1077d2e26b3d71da7421db3b6cef0128cffd23e377ac0bc5d6a844c51936a7e04041afffc41dd09a2bc7041e0db1938ed5b6d20e37871b9fd589168714e4e9624d4a94021904279ceb49daadbd48f613da7314f5de594c6d5da25a751dddc6a651e96b5030d25e39e6b3773d011639735a1ff8218c4c054d7ff51b474327fff1a34b7f2444f6cf0036089fce23534a3b379d9e5981c50b5e04e8c02723c0c8ff1e5c6cc11b1c118c190c5078720f424041430d012a92f2217ad250f4643aa3b3ffe4978b7174f95176894b9daf1ea8f4598271f4e8deb948f8e3663f0c173617f856d70de9c4816d7cfbc3d894e78a95cbf6a64f59d4f96edb4ba8c4b083736de1cff6e52d3dd94eb279fb4128c7625d56d9b536120521677946dd3236cdf62137911b9e13900e38d380e0f9702751b5ebb008c123dc40cd10fb619b1cfbf8963479e981b1fa84c5d966e6db0837f5bcf60f3e7e1cea059ca16a9d260ae05450de74c1821d6b60e30046ed9b9f8924bf449c043be162eccadedfe580d8a2c49e5316f77c081c22055c2dd433458e7abacc4b909127128f4153f2d5ecc3ec6533a864b927ec2360872e884a2dd22d2e3ede21d217f8835ad5528408dcc7240efbaf63547d91682bb45e2e0554557df8d036778d49e020039750d97f2071dd856772290dec405d8bd8ad1306cb0f795fac322988c426b7c5ffc08c22fee378a9503ff83ba5947a8b73ebedeea53feb98e794a8185088995606f50d639f3b944b2ff7b2b3701656fd973c56839e4aef4b99717a2459a3f5484d7dfb02ee6328c5b2fb9344681f26ae848b64c94e08d52b7a0e0a700ea7d391cd96a66e48a7fc65749111c25fd34597f5dbdbbe21faff1b47543ab71364bf648610fd1a987fbabb79532d17bc74d9deb935a61c95ac8b0fe38ce0e8c2b898db68d47180e16d9fb2f992e292dc9bf76582f6a20da48aa4d4d084056994c4dc6765c412f5304653368fcb6057deb89548d6c27220cdff0fa58166548819a5c6e1638425c1eddd239ab1d595a66b85d62594c593e3364cd240e179a73de6c793c9edc5648178646bd0b0d2743502158ddeabaaef9abce1cedc25418703407600d52cfad1e3b0c5758a94fa07bf1ba28e753452ed994b1d570259b54c2323e69223e6d5145d483694b37d42d2af209e71cfe830d4dbcda61498d35bedb9bcb342541c8df9109628d190f3f1e8a55db3fe375d34c19b9edaffd958f9a5270d04766894949797fa958e17a060b5fabfb711df8b301aa9c4f13491c663db4636033d1a9002287211bc45f9ef4e4a6bc7b732890be34a01da706e23701a89a544ea3ca2cf837eb6e0d9b4915d7392d7aa6362743f535042fd161c6de7faef5933ca2acafe37408ff8a222733c26720a6bdea8ce793c6c64c65a8e31fe09fbb551f6fa3a8fdfe22f5c6342ff5d83bf9550d1213b940ef3e36184961a9a38ab7c42ad640ba9799d7ed034909f948252a3eb890a84b3bc1bebb281c7c87fbdc01be25ebb402c94d0b8c0f26340d8c990aba2e241a353de4bf2f9fdbdba0f3d358fc318d5b8fb982f1a1ea4bb05143e191ead9a4781030ccd4d69301bd1b43589943edf2779e7125f9ab64139ef8bc1c0034b2ad04da0da119b9a886b5ae0f7ab932d6a0653ec2ef87405c7948d3ed4f912247c3fb3c6ec834c2b01f4544eca708b101bb5be4f6e19ac180a122f59d9c6c71fd9156563f24807f29ed994da43f9508743c1f5c04b5d76eaafc85ece25cd27ce8323492c56247248c08f44cab03cb7153e8007abe75e28d99cd167d9435b84b9d3959828be0b1d42f21456652dfad0be5d92e0dcd27ee4896e32dfa3cf6c4d7b7c2abadb63245d9816cc46786740003e3f3051c29c933afb8645716ea6b42f7e05e619e5887b57f0fd713dc3e3815ac72a29aaf81df91062a58c91af1c6445bbf10e6f7a7014549c400d27dcaf32e1ff74fe327904f19abce51a752938d14d3db2fbd3f07b57b9440a5c2dbc16dd3b8afbb36e78261768f9d8904b1b3a6363d2b84c5efa85a584d41667830ed51419dd44b13823530bcce916d2cc2c7f4400ffb8a35bb9397c8f7625d5ef8cc36f8838f2c211651d035c2d279deb61a1d9c065f3e4910a07682b2ec68b3cff1543872bb5cd89cd85eb014ba2e0828d793f9fd682b0c1b82b6cf98c4e4cbf95e0c1cbcc2609d2a3123d262c43d132bae9fdfbd311b7565fd468a5e4c21e29ab80f8af2fce824d8a106bb0d70462557ff0ed6d2b09b1cd25759ab52136dfddfb62f64c24112d75707323606f63e1bc15f462847eeb956d5b811ea9d3bec1193475d32dbc220d94e4ea2e443d197dad1a314424af13f359cd48724d35a92e7683d28ecbe31f329cd5110e213c86f63e3c2d3be5898981e377bce85a6a0b96e8ac688f544eb9d4732a695179c0cc3d33a9484ea2055215e24a20bce6f8e2f701b4d481ce0936cf43b2bc30e90e7d52db00ac90d0e1e69379c902f8e7e5298ab16b6c9395f23c5dfb4fa08b238c848fc57a3a2e5595625af343a2333320c5724b719ee2eb517e71871deb0c63b570891a9b3f2f0e521dca9f9d03689a393e2fd19f59367c10600dfff9e4daaeea7129cb23803344eb40d5c788feeaf796957f548aade857a99561047c5d11458d05d2cd9dfa4df874e96314c14ff327494f12b8dcd59d039cbcba55710c198a5308da4014852dfe90bd670c04ab7d8a40a1bc7ea4f5e604f05c1ce2bf04c29760d32b42c43870caf38df1baf013dca3b8fe8e2306aa18cfcb2108d50bab2176b2511d9887ccd26c3ed1d012d13a1752c7dd13964094564388698a520c148d984bd8f54870713eecf011ebfbc9f9de4c60d341e45435204ba80ce4739ee75bd3e11101aae771a93ba35eff9a8223e85050eaf83edd6ff9ef6a7afbcd906752c6e93ef882f81f6a51a28b01682e72b07c6659ff987e9696291cdb85844ddbac273001971fb04383bc616dd6f3e4e2fa87c0b80171d637f597c9732d17a446a2d94e8f324016f28e4f5489829a7467a870890d17e252687accc0d8ac37dc6fd31a0e98f25ca02c04dc5e62236c9c8d7d5d0e302f9fd36d64917f58c9b8b8b05618111e66295a8cf8424f168cf63f69fd1b53e52c726dcd7f9f02a6a90d0d3e123a2f2a9ae1a048db5bc07372d0453c106016504e1ff498c56462b41482e4b6eff4fbdf4dfd6820eaa6e2fba5e7099f373f9428c891f176806b3127633d3565f49ceae6831e462b43978a150782d5b71df266d3b195790eb6b7736ec2a47e43fabf7df68b534d9c35b77f0ab9d238e73a8d2b0008101a58490d5f3397858062a5b6a489f292b3c480feaa6532a0da4475d4d58ef3a404c52ee9bc3e4101a67ecca8639e3f6f347340ff3f590bd3ecf10f020956c8fe69e02cccc0075dbc645565369fae6c98f5eb7ca92bbbb77cef670f87c30044ebfd986d79d621b5bc70d16cc7dfe760fc6b0ba7fde284dce231da503b0ddc1ac7a187ac9902ffd03c737430b3eaee3979fc31379df1c9a101d8030bac07ef44835df829e8f73fe7e0ce6d78e7b24b08f24c6cfe0359430fd2ef1f76d2ba349ac1d6ff937e9895fa81ee4455a7a4371c4ebea3e6027e7eade963f8ce5cda7c0a3c6e23b8264d029f67dcdfac00eafaf043e5dc3b4bd8bfb6cff938fc38b7a869f4f427bd2b53fa7d77a02f202f2a1ca59e400a55bf85bf0c674923cbd2fe68dbd7fff85ee3e917e6cd7ee14dfa7809727073a525832e82ff4db939068a294a24c676f8bb85cf192b4d41c82f7cdcc39ce9d0d1310fb0271de570a9127b06c37c334fbac296abd68715062e3f2f68eaba4e2446fc130dba3dac08839537c9d3d1d61879f8b29f6c13d02097afeb529bd2b7122cb7a8657a87d02cd0937b845dd1c8f93aa9ac80e88ac44f8eaa7f7a56ee928f0a689fb5a5677f3de4a79d5eabbbda829513dae914c2a57e7b962e5cfb3a37efe96908f7524ac9a713581817f2988bed3b95f43c7ee3a27cff17f77c59e65275976e96c69c241323b162932302900f543b851d0a00f46328811f35df91593ccb04572485a39a9e105a56024d23c0c63f2b32852fa72266b47c8729262c15b7aa12251d8ac44c85fcc14ab0f945ceca2301d338f2d2f394d9ffd2d5e412ac5b6c4122e459f5db3bc367d0c7df74bb8e1a3a015eeacb143266fbebddfed9f9e0e2f2e900ef1adbf1cf64701995cc7f42a2b7f139c2d0afc0b3c60ba78fa6a5c96b81e7b6eeea5b7e54824ef2f449a1f7b84547f23737037fad610f4cebbcaf0ae625f84bceacfe49d2644db6470f7c692f6aada926383964648e246793e8ec72dfe92a8994c4f2f3fff4951f8ed50d6b6ab601ce924d3e01537262e8728474051ec5619df9b0203353699edddb6ca3515b78e8eb07c06d475161777154052551d5ea676b552e24b67dc6c02a4eebfac5ef6ca7af2e92a67375165ad352c73ee1d76c4cbebe7f0fa0470d19e0d8dbc1a35117736f40fc135aa99b59f465bb9904119c78371e67c4bafa4f3f75c727b30fa148d1391ac259ef3a5697367283dfc17d272b4d6d5c755a6c6d28e8910a0882956672fed28b04b4727ba65101d59ac67a6479d449c1a0cf7583be216ec8e78b678943313b8a260fa0c895f0b465fb006d9fafb6875bb272ad7059385a653ade132cb10adb86faca25628431b2b1a2e892faf1ac7b677df934be0701e84835e64e846678e2a06f9a9c4b86c793212d3c45f452ed53872251801c7ccb35304db25658f89be05716fd03c04df5961c74684a04cb8333d0964649de06c46174bd72c91a5e2b246270a6c76de6bf1a75cda35a673fc53befb05132c1c5a2c9b70c988513b5afafc2608b49638c7d3c9ff8c40b90afb01a3e3d73ff55155181ca89952951ed65bb24b94656617879c0ca86ba7ef22e4249f008228ce198bd3a9bcc1f24a8cded7c6950278babb4ddda4f5b0c0088fa8bbfafb25ba511a4e2e7e02182332c23466c2f4034bb4544955696ab31216f3acfc31b6cc9eed78f43ff854d20f751ed8dd91e29b079ae941077ef813276e7427754dff4dbf27921896f64d1e14eece36c5470d69f8c3ce31f79bf120888d98d30f82efa74d31adb2c9e05f2c4428f696ebaa8d53e496ebbdc75b585b95d83765e4997bbf907eb7de19b97c83e01b440a5e60613301e0bc187311b1e1a37c1feeace2a00e041ae42a7d513aa8b4aa46af845e8852217c9d868eba8226083cad02befc44b00c3860f26439ff00000000000000000004945b37b6b52a12919294498112a85289cecb534a294999e4c1c1bbb03b7807ef1807ef02088117b00bd20ce00bea0c0288c22149b8cdfb9992a5d4a170125f9fa147c7a5ac3fe1281b4e4ff7a56f061d2e42004e388a9e460a3aebcedc6fc2318a8ce12c8dc88473a94ef814b131578a11c0128e67932d4c33866893120e12f39cc9aa20b21d4ac249fdc786a52bb524120e6ff966df7735557d84d3d56451a141fed7a68d70b80b9d1b751527be16e1f41773bb444db97a36443885be7ec85362c1842680219cd2c44e91b97a2c6d8470f28cb136339234d93608e78c69efadba62df5611080008a7515156432b08712ffde09033358928e9a247cafae014ecc45f5d6ebc10400f4e22a998bf2f9392cd120f4e410531fae2e4a56c9a1d1c553543d260797c56822f001d1cdb3765539afc948f3e0787af9454bef4251c1c848974b9b485104ba4cf057083e3a4dbcb3e1163c50bf2740101d8e06c1bf1c5f2a8b077f91a9cd3d67e4cf69a8409a2c1c12bbb4b4ab76f31a56770509f71f2f7acdd8932389dcc4c2642fef591680c8e9765ff2efb7dc9930060704c8bc9355fed58cce22b8e1ec22fcf6ed76578ae38867c11bf7f32470ab5e2149b216c921c57d119561c5286f28ad91a4ddcb28a53ca7a7b4b48b7922a559c2e4609b118ab909264462a0e5e1636f3f9e76f8e3061062a4e21277ea8b9c8bf163557758a73bea9feb252f512bb298e32a3e737de5a8a936d4a25299710290e31958a7a99bd59551c8531a598442ca536cf8c987ba35588099362847ca238e8e9cfd0ca14b2d7e442711c61717d3bcd36a60d8a93ca9ab17f47ff2b924298f189e3a98d2526a79e45bea030c31387e0a3de2b9dca788e74cce8c429f62a9bb8ed280ba31c3e98c18993ccbf69a44bbc3119d3b19b38f80811a48a67aaeaae234b7758a169e232b78c9995665923c6ac724bc99208a6bd8f3d30c2f8a2a46046268e1a7b497575ea04cd317110424ace28ba620c532f718e1043627d66e9dddf12c7cf16d5139616a4e8332a714e731ff513f79f15cda0c4c14346d83c22a9f28b7e7c648e1a63c6244eff12f2be062b953f97c4298ac8225b847c350f7d7c4c6146240ea3c22531ea73572f2f248e593544375196f3aaf488538acaf4914c88112277c4495ca8aebb6c5fac8d38499651a1fcf3e2a524469c456d5d8a5c418b38a87d9bf0961ba2be4fc00c451c4d68881039ca92dc3711a7499b17736b62b8a4828863e8095effbd9fd962c060312000868fc1586cc130987188536e970aa26e67927220160c688005b08000a21c5886388868361729fe5424a9f1c802028528bb424825b12a4655f3aed27923b25f943f958e2c1268d11586185f742101ab2a306610e2ac412f630c173ff7d43b1ced208e6d29c534adfdc7c7478e423bb4e0c22016411c74ef5b4ce2c9be3505e29ca3e2f86ab80171505a3b2faad4e90bc13f9c4e840ca32e6e336eca0fa7f41149df54d010f19fd187835dfcd2fe19a57b3e1f8c6de1e1a22a215ba4d26e5192d7f6df844cdac379d30919fe4ed522ccd0c3297c53dad018714c7c3b780b1c6b84197938f5fb59886432c9646ac7187c818f8f8f8f1ca57838a89425eb4c657eb99c8e2c535aee7050fd2a5bba7d2d2d3fc30ec724a3399354385dd21483197538f96612daa2da96c44a2b00821c2898418793a4a8922fab37f42937471161c61c4e39e23228716b37b9d691850b9821877325b75417d2a5f90d69e00b31923b20f91762244f33e27036b541c4758d31eedb33e070b09399444f887e97bd8630e30da7bb142ce5bd846594f30733dc700a3a629b5b095d3df2369c4b5e8eca7915f289301bce6357522fa494a3570b63c61ace162a83525fba94c56c861a0ebf13337c5867dccb66a4e1dc337a67a742ab08a1e11483680daaea27865b737cc15c6465402330e30ca7342a2e4f4bcbc4b9be18238b8f8f8f8fcda14c98618643ba9ca5e56f4390997d7ce418230c2365388c8d7a964dd01074cd420a33c870fefe1555f548297bb9c01861e4a831c2b025cc18c3a9846ad658b58be1e013844c3235444d2d0ac3e1eeed2387e990652a301c54d63c9b4dce5da689f9c2219e64b70c994f85d24e0b33bc70168df559ad3235115e17ce5b36ff7b23933a6c2e9c429892531b7553d4f9eeb6709e1cae159254f42e73d585195a38eeffc8cfbbb5513a532bccc8c22188bd8b3d3649857c49146660e12c9a2db4924a8ce4e15de15c2d4ae64dcaa641ba3d98618573f82891ca34fe988adc55e17c959a26c349d6f08b0286171790c10c2a1c7d6ee4a969fd66eb4c7140cbe098318563eeebcf361d1264d2c700238c2e8e91c271b374570e9dd9ac54617881e3e3c3a089c2b9254d74550b9710640ea1704a322a8aec558a8ca3e4858e309c025a7cc1802b1468a1a0cc78c2f1ffc4e26fa6190d1b92010f1c1e04031e17c0c223b7f8820b2d7086138e19fab4269d25b7a199d184539ecb7f272495fc021f1fc50583f1a6b2308309a736a1549e92789d213b630927f912d395b577638f9470cc7f8d131933ed339384838f46ca64b59b415d6906124eb755aa345b3e13761fe130215dec109979f935c2494d16adafa177cdd6229cd6d4a434794426954b09e4e0c20c221c93b2aa52f795958f87805b8648d49032ac5b23866add9d65093d26828470f0cbae8c1bbb2fe27ad1450e475a6cb1a30b9f1184538994326c57d2991ae62820cc00c2a9cf72c6ac3fe922ffea193f382599f3ae5c64e7091531626a1500410e9ee18373888f7c5ed693674a8286193d386d6a4a3199342779a82a0700d8308307673b555182f80842a9af23eb0b2ec0d091a32a2b4b0540906304337670487e428388f3fb93a19a61860e8e193425bb94453428db230b2c3cc4302307c7141d3725fab2b8452b605e988183d3c41e5331d9c4fa8566dce0e47111b45223595874860d4e4ac8bc655a3b5b2b8507336a703e2b5da915e2c80d293438b8c6699a52eb033366705ecd705a6e94503b73860c9290545d44bd2a7ab965ea9d7fcbd56669433f2306a71825310993a1d757d291550a21300306a7122b772a64aabca0bee2d89b46f643a6904b8bae38a5f1acb4a7a29b90702b8eb5f2b591fd4e5b48597148b2f7b9a1b99b6359c5e9bdd484cad590cda8e2dc22938a69a3e22564a9386d2a0d9df6152f64820a837a7ba520dea2292466e54b17df5b6c6d647f8ab3c69837dc59e6eb903f34479523c830c559deb47c826a52ea241e5960e1614c46298e262e6e94b5f0c821234a0a4bba86a79deecd201b8599c3aac3124511ab2b83560829cbb462ccad3357a34e828c501c43e7dc7ca8ac5e9b3bc2e822cb0132407116f318b9d06ba2542ee313072923f385e88f9dc4c8f0c429c9d39727065346278e316da6ec6721ada7bd4d90c189a3ccd9c9244c65ba3375e4f01568b1e3e303b720631327614296ea4b901d7722a589a35b7de8b4969c97d99d828c4c9c4bd457728825cdb46b093230710e19f6bfb4d4f5bcfbf83825c8b8c431cdf7a9c43859e278e222af89511b2124a31207253466918b5c4a8bf603322871d86c8b1fa371c62e963189937649b6592b29a3431992288b66cd160f5353cd95b2fae891613f248b2a2312a794137de4cf851760bcf1c802021188c0c7c70332e0a15500e009199038b557a477f330b9ed3fe218ce4cb5a598e437861d71b6a04677757aa6978c8c469c3389d2dc7a7e3551a2234b01321871ccfecaa36fa63f63361290b18853ccb234135a235cf58a385d16ab0ca5e2d55949c4496cb75f2acd9ca9623ab276a81a6420e2e4675559245abc3f5fc621cea923295ed067a56d168f2c201001356290618873a68acbefcd972c611d59ca931715302aa310879f90436d5c938c98f2228c2386013208d16cf0f810afe14da42371e8d8c111c0418131c0f0820be7c2230b2c3c4e2217640ce22e356f9590b32c85d8616d551b73a5354d8620eaa30b198138c619d16b5a438cff4647627511c3e3e3e3c4a8820c401cdc82f7dce6127b0450ff70484973776c8e8c77ae408baaaaac000872ac0c3f1c539efca55eaf256cd38793c4349b44927a4a862d0b2c3c48d940061f0e7a3e7583fa141f3bede1a05dea42dd58dcb4d80964e8e1d47fb53a5ea6a14254f270bc1fd1d31c9d39568387b3575ace98adf26bd8ef70aa4d0de1f486987e527638a5863fa5276bd5c65c0b136ca9c3215edeec8f9315837c6d0532e870d89afc3f29881f5d77cce19cb77a42e859bd952e8783e52df5bdcce693fb381c24b6cc5b85bc9843a423abc690018773e84cf1e5af367fe575641516c878c3f1ac237d4411a341525420c30d07195bc3c87495f754e6858c369caa5250d92c590855ba0e369c64635aad4d41c498f06c0dc7358d5a72632fbc50c3c142d85cd2943021a46938eac4eca254264bd3211d598b86b3fd9efcb49c61222709ce705ad77c9f62be8eac307c07964086190e17f53c839ae8aff482b1c30b1d596538bacfe78834f94559928e2c0e8393993260c820c3c12e6750b3a5c1e4247d7c1c0a3e3e1cf0f1f1f191a30e05b6d50519633888cd974258559fb3310b1032c470903183527e495fe7f4390821230ca7893967217b49aaf53e3e48f14006180e0a55877c384670710d717217b93d9c347c77ab8d76e4f57012494851f35e3daec9c321e59af7d4ecf170082289906a42fe0ea75db9ac58d2e28fdeed70beb98cfd565264f55e8783b098207b4c252525a7c331f897a6459ee826e4733858cab8d396841c6dbb1c0e7162f655d77138a513295c9c5e549be1709c1459444512da84f60d87bd0f3d312417b5aa1b8e2a496c4812d4f2a56d38c6185478db8961df74361c442cca454c5a2d8bbe8663b9040d97967744d0d57098ef8c2157f469f33c0d670ddbcf3f891a74e368385e9d9ffa7bc6bf8d9fe124ae967fbb429fa8b8190e2b1a2946af4c6e162fc339464ef64a2654da9e0c47b39864ca26154fef6338e99796d8cab51173311c4fbd6e10c9f3322985e19422229f9c8d24eb048673a90fb92a290475a32f1cc2e8d37ad246840bf2c231e4d8b6342162beda8593926215d49998773371e198257ee76f346c24d316ce9ae6fdaab552843069e12031979c24e49c8e98b270fc1775ed6a49f8c9242c1ce4d988b2cd39a964d2158e495a880dafaf309a648593695fd31073aab64a553888b958c959b2d1222a1c44bca84d21e5cb259ac2295a8c16ab4fc64e9014ce59fb7722af39f31d854332ddca4d215ce8160a270b424b742f455e8b9e70f293f226638e8cadc809e7ae4b71e7846c512a6ac241773754da49efa322269c5ef43f834c9bae4eb484938893cdd634620c1329e118e6752164cbd8a550120e4a5f9eb2142f93421012ce26cd23ecf84738d69ccaacae196b77231ce3ef2d836ca43a79110e1ab694ac68cacb13110e2988c9d39ac6c44b433865c5ce1373197684847014dd19fd3d7ff4464138b77686642e13454440388fd709ff51ffe0f0a536474b9a6a4bfbe014d2a4c91bbd4f59dd83433ea54bc491b5de300f8ea6eb2673d9da457607e77bdf90f24d14d1a90ece5bdea18408b9abc91c1c369b0a3f2123268b38388b65dbf7dcdce0a01b49b5e41a75e2b3c1496686a0b43a74c43538685453eb1761971a1a9c4345c8f01541995666704cf2336ad2596a5a0627214f877da53a06c79ac9e7be3f016070b2d013459bc66ef05f711a8f51ab7a62579c47e42f1f1351a14ddc8ab34549e215d166ddc4ac38a4089ae744dc2b45bc8a93a5a0352f47befd10abe23c419549ac3469fbe6542c4269906f6a66549c2c34ce7b828ae8934f71d88e705e67e1329f6c8a53dc589e469313cbe4521c82fe50192f63a3984c8a63d024c247dab9091f8fe26c3ad3e4d36a52e5c2a23869df38dd2cf57d1987e214cb4f244ffcbfcb1814e7dcd3b75fd9f4d4e24f1c84799ec9fa9f4d22ec89c39a478cd519725ac29d380521fb6735227523cc8993c9c92611b1cdf57713a7d011df114dc8d3fc6ae2982af76bf5c4ee9c361327a1ef4e5f50ef1ea2c5c4c92c68b7cbb1b70fed258ea2920599494694b9d71287d90a1a29b7b7bcbc953828d3d7ea5d9bc4859712a791f53b919e3222dc491c2d46ff9ac8b6abdf4ae2dc1b5b62988b359adb489c47c7a995cac98ccd42e2b8a2744df45b7e5ef611077d9a53e405edfb9575c47926bccc468acda26d234ed1249e4adf1a8bb165c449d285520d91b1a5da459c2225d5914dbb2a57ab88a36f5e317d6123dc64137108d9b4a8094b7e326411714ad9ebf2ba86e5f2f51087cf2ceaf4dcf3b457431c840e1574c47dc9e65a889304fbee6e3d53a7a4843888f0e115530a3128930ee2545d952fd56b4a2352411c75f37df2b7ac79480371cceb1a5563bebc1d52409ce452a29d86eb13adfee164992fc5536a42caa9fae15c63b3d92e29532aa97d38786cee9f2062b815950fc73f71257237349ea87b386d9efb9351eec206550fc7df24318fb70611329a87d3e8b1987bc7c242a3783887254d11c3055bbde81d8e16f34eaab7d8e1a06b49b4928a195cabc3692b84ae12caef33458773e6b94e4c26ae4ba27338c9780df17f7f3ecbe1204a64d85d10d93571389f88499a47e56ff57038756a741d194de7e90da71f392d265469bddd704af99ff96e2962d4db703431a9246cb01c9ac486f366cab915f7f6e26b388bd0f03c6542b4ab86d3e8ad37297262d64ec331d204a18486d216848693de1b7df9e76ece339cf623691959974366cd70fe91dc2f275b93b20c0771233dc5af8409950c872423eb565ad3a4ca319c2c3788b8f4aad49f188e9abe534f4d459a260c471323d1fd279c4817184ef2a7a49ac909f1fbc2295dc7fc7ac47409492f1c4d756bc4c85321efc2c93a377b979ec8262e1c436552263129d514dac2499fd0d3a59164ac5a0ba7892592e5c83b69e72c1cb4e889572add289962e124944c31c64247e9f30ae7b9985bedf77e4b2b1cdeae6f364b6b6b5885e3571e5daab45e998e0aa79c22ffb63b7bf2140e12ce7e4e2435a94ae1b43132f7c89bd09689c249848f9c0b214e7a0b140e626d726999587f9f70d0b269bb46ed464ad00947bbf3d851b9f15113ce7ba97266d199bd74261ce3c4eb44093a82382de1b0a72f4cecf4ccb6120c3ea649bea56f004938bf7796cc3a4ac6b83700249c2d68d1117efebada1bc0114ee1e77eb2f5b5c5bc0118e1b05e27cc54122fc1bc0114e1589d79da7e319310de008890085964ea177108877841c9b41314c2218a1c7d49921e310ac2719457aec6538de103c229d478289764ebfb0fce969999236a7235cfa05972e9c1316994e8a682a7e98d07877c9a7a7126c639ede0687ae95c4d4b442c1d9c2f77d7e99219de249783638d0ca252ceedd03e0e8e1db12785bf911bf31b9c6d5683e6f80d1fd9e05ca943553545fb10d5e0d4a6a52f3798c930d3e0a49994568b10e7cd9dc149b5672e53134cd62a8363f049a144c49a78c7e07027247d7a6906008363888a7abe2273579a571c44c52031c278c4d518579cca2e845e92254936d38ab35eaec63c5169426258717453a5c2a90a49ed5fc5f16f4e5d988694f6abe2a4624932d99642487b2ace76ea4b484ff5d8382a4e276f2d56a7539c63255e29318996b229ce9972a9b5b499aa5b8a53ba9021fde424b8498aa35752d24de4d8487114a728bff26abd9a2a8ae21484d871194d11f40bc5496b4ec68a985ca729a03885f8e17eda3e6e2fe513275db252b5a6204f9cc29f249159fe7129a51327d108426be46562a570e23c2ae90ca22bd92c563671369defe956be3f56d1c4794f8ec68e799df23371fe5acd8a21c84b6dc2c421a8fb1041932e71ae98b4ea2de1422559e22072f45c2c66a938aac4d97289aba4e26d34214a9c5ed3ba4dead8b9d0244e9bb4d48d3c9964d492385ad694946f05a152381287512b1a671a76c49038c56897ae33ccc5ec3ee258499de812fbff9575c4e9deb4bec5b6118778ae2d1adcecb265c471542d8eb618d627bb8863f85fcc5caa4e6daa8893de74f11e1ac2344dc4318c6ed01a5143c4b93746df0d3285fb698738e41d93dd9bb92425cd10a714b9c4a62839d495568853693535366a7326a111e29856f6aca2cf4710da204e424b69881934411c47c51192b466796b2c10a770a3a11d59e7336380386e6c4f1396828888b13f1cf5375ad7a678e69bf9e17ca9d33535d29a6ad68783f85bcd242fd137313e1c93589131082d2b33b13d9c367dc4a433f72dc6f570d8a0eff2abd7845ccac321dd6c56f84e3519151e4e5a64d568acc5b8a2bac3f15224913349e6d0a0b2c3d14e7787fc3af55f571d0e2a7294c48ce9afeba2c3b15efdb32ca5a6d53587f3c5d2ee9aa7a55e2e399c75e3493a95539771c5e114f465b9937de9225b7038ce55106a95f53dd57ac3e15433e8cb8d19122d379c34c6ebd31a96369dd586b3faa6932137ac42586c3866b9f0be23228c8cb586930ebd49aea21a4ea9b2f65253ceccd370bc8a49a651576ba341c331b684dbd79469245667386c680b13c3ff2c6e86e3e9b5b7e92cb5f0cb7088e1ec3426fdad3927c32958d85f47b894b2680ca71a9d7017a3293989e1b077d24a7df2e6370cc7482a6bb26b53ac80e1b496f2e88ce06e21fc85a39d6aea0a512e6ee985c3c8ace932c4cfbeb00be70bcf90a4d4feede3c2493566111f6253dacc16ce55c133d79cff0969e1dc6d3f372347285565e1142e58381993db9d8408000b07b1392e3264cbef0811c0154ef19478af1063283f730be68009002104608553d0da7e9a4b82abee5538e6b0f91525fe4d43448553f5ee9a6a10391b9453385d88f2cd65d17b26460a0791f4da8b0af2c25e25479547161028001422a4fc50430f47f32f3d6a4b89dc7b1e0e215ad0a3fb47483e351e8eb12d6e909f75957abbc3d9e4dbba684c4b9a2645a8618753ec2c19555f217f661c1e38ead0c97a28ad28172aa4c3f1c4a9d06255a32d921a7338bd09b5212d6935db500d391c3388477053b61b49b2a3461c0e9bcb8496b7b49a2e92639550030ea794473433eea5fc197ac321e8d0782784db958cbbe11472a578f1e4e85bcad568432927b24a7ee54b2b1b0eaab6b3f4297797f8ab87c161884101ccd70dd458435935b76892942aa4128bd1b2f79fe30be6a2ec0b263b460d359c923c9fddb8a5ca344547560e0fc38b2eaa7078786481050e0fb2c5f1b800161e86fca0461a4e51434352596da3d2588850030d67d7fecdd6a2d16cc267389a98c81aeaff947d6b86736ddaa685344a6ea60c0709329afba414846976831a6438690deac2480cb12be7311ca2dd4f300d633159311c266ca7c6704105a1ac301c9476d59c31472b2e0286e39f4852959a27c94dac0635be70881a5e66c7ec2cecc90bc7d49a2d8a5cc9a52dedc2612e28994276041583900b2793e8e323838fcb7f35b670580fd51cd1926eea57430b875c31e3479e919821a6630747c003879502a4a246164e69538ea83156b84ad9964ed4c0c251429475199542c644c8812d90a78f8f2f76984fd4b8c231e99e04b16c122e449050c30a27257c757e2b28b5b8ab709ecdd4ccb70badaeef126a50e1b4556726f328756415db81a582a35338b9c92a4d7d51eae22e8593758854b247e3cb57f5418d289c248a524926d5e4a00614907a72e977f4a8458eaa04418d279c6a4ce93d4d2a9b9563c70efe628cce61f65d6c4089136a38e1f49a828a9bbc33a26443a8d18453259fb8e9914f82fe76508309c718a4598db80a4b2a4e891a4b30e84b72714a344f574309c7557f134958c80e9c418d249c4be488923fd38ad2b58e3435907012f61a428ceafe358e014618570c4616bb4738c586f4b4d429ba52c6831a46385fa6142469371f21535a50a3088768da75c64b47aba50611ce659b3956567e418d219c544e3d2f7552cf4c4502391c021f1f3584700a7a83b6d2d6a0625c75645d3972d485175d7c7c7cecc03186043e3e2830011b95a8118463b8a04e4f5ab78db77464993a690b31bcd0a1818f1c1e383cc6e8c21d0046165db88e2cc6e8c21df0f1c10552c1186a00e1706915362efab31a3f38c9a48257c49844a5c9560004390c50c3070719844a0c997c64838a8eacd18353e5dc486376571be247a8c183f3f977b9a5d97f89e11d9c5b5f470551a142c9560767139363d4e5ea0d4be6e0a4bb2b5dcca72b5b2e05a6fc8b31b2281ad4c0c1f93f3537669b5f45f906a710a2fd52289b70216b8353aa9c71aeb5a2df6b0d4efa27b3da5cc36953d2e0104be3aca46d3161b9199cf72497c590e2a36f191cec7be47f3df48590522306e70b6bab194995fcba060c4e7a49488c9da437b9c6579c279810d2438520c52db6e0342db8d0028b1c3ac2f8c2930a68b8e224ee16c284c53ab27278f156ca16390a4aa2d18a22efdd4eb8ac31489915883eed5e63b92cc80c035c8c13b8039073a0ccc53881a71cc851b05ad05885bff7a9a5dfae1368a82215a24163549c6b09aa54ac37b3fb92845e5974a4c7052e6d400315ba6b4ccdd61e6a3ec5414694a4bab715adeee3e3e3c350f0f1610846185fd030c549a46cba26a420ced22c85594ebc45d26d6ffff1f1f1f105173bb428e66990e2362d9f3313f3a40ca2310aacd26cb428e1f2be5b6cc138b2c008e30b4bd010c5a55ac62ca4f3cf6e078d501c72a85b4ca664c8eb3fe0e3e3e363c70e68802291549d6f52f14f8fc59c80c6270c429aba5964511714830b0d430c0a74f180c3000d4f9cfc674e25252f839e241d59be45d22247175e6492401716f8f8d861022fbcd0110039d0e8c43194edbf8a7cabba290c1d24d0c23be081230f343871c8adf16b29a9a0697549038d4d9c42a2a994786f96f29f818626ce27a34facd6585da96cb105af166864e2601ac526e8b64d25b2a3001347d5c99142cf7528d3e6282ed0b8c4f1bb720925440a73f37fa48033a00509b400c30255a2a06189c3e756f2da708d0f5f4756250e3fabaae964257187f28006250e4169cc97fecf5fe35e55031a9338a9ef53916cfa592aaf8318185f248286248e5e59fdc2f5c68651d2919523879b80c330333cd088c4c994e61ed38ad1b3e32fd080c46163ca7c694b4849a1ebc802c351a0c5169ef2e3830be481d4856ba0a0e0e3a3683ce2f03631a79a0ce1df81e1341c71085a6a261a4986973017671140a311a78a99bbe22242dd4e1d595611401df8c51660e418c38b2db6e0ad000872248006238efa2a32f45fd2937c91a35805682ce2a02f57eb4a89d292f4628b31f8025cecd0828b8f0f2f748b0c34147112254683cecaa4177b8f2cb0f0f8f8f8f8b0ca518796885330b1d6f30a7fcb1311a755f5d3959942bfa91ce2782534458a7532c4419c5c85c8783dfa529a4d805c070e2c3bd028c4b94428bd26fab217c239c2701264e58006218ed92516cff229dd0a2b81c620ce27a7da43a4e5be4c09e292301356838138ee69d833fd10c2270b88c6dc54699c6a2c938eacf2058d3f9c494d5353c628496216313f21974c9868f8e15c23fb7bdf3ecf4e7a6da0d187d39bbc093da3975973ec81c3c30387070e0f0678648185c7063e3e727c7c202dc640830fc79eb10cd24c776feb7b386a4a9b59234554ee748b2d1887e6a8ed010d3d1c2d54d06dc9a4fe26511e0e229648d172fb7826150fc76c77bb1adb3031e71d0e9656fb55b34529253b9c9432a12e9dfb28ed963a9c45c6a03758281144ba326540830e875861294b689fdf3b8793168d18eace2487d3c414224fc31a87536ec6f5c822fcfe3a389ccd2a95896b85bf685500b30220c801068d371c53237fa80ba936f377c36944ce9c7c064bd94505018d361c84aa864a9737969dca201b8e1bd284fcfbdde8d78a181fa0b18693b78a2cb99f62b7aa181ea0a18693eb25a53e64f7ce851e381a69388a65665d08727b74848693e5def58af9249b489fe12c429ad26e9553fc2c34cc709e5c4a041d6947525534ca70f6b0086937b2c837998e2c0f638c3e06d020c3f13f3677d246331da6319c6ad325bb537afd422c8653c4a43233a55728350ca7945a54f0af724bcb81e120d577f3bdee6653f685c36849166d6564875f5e38890fa1ce7db29ee8bb709c5c278489cba69eb970daf8666d379e1ac92d9c3388b16431a785d3a8067936eaacea928563dcad926fbaf2fbf65870467fa28a38f915feccf99621946e85c3a648ff25a382a6cdab707a178bd11a2e8bf013150eae9bb22f422b463a4de154c1921cddf27f8b26299cb32c5a596f47721745e198a5416d5b8877e1445038851833e7f7620a11ea271c53a9ac8c881b49d9ed8453084205214f528f5edd8473bbc5c4ec3aa9b46a261c33e4b7abbe6b3f915ec241c632fed2c9cf3c62251cc3c92cd2749b10c2e6249cbc4f6b8752a5e96446c2e9624b62a63bdf30f908a7fb4d92fdb4aa648c8d7010b1afaca5722afc5b8453b66b9c0a69e94768897048f9fdea32457ada3b8463690f252fb44238e7bfc5df5b532e5e108e9a26858bede8f60d100e77153a63a52ec66c7f70b4546a1a4a890489fbe0f892e2c58cd46b29eac141bdc8ab90d9a2891c0f0eb194faf1b88c492d3b38c9247ace53531d9cb47f5f32d973709a94ddf444450c1ec2c13156501e63e2d7377d8363cea40c5f9577e436385f24395e19ea7eca1a9c242469956e4c1a3438c9c821b5cec2c80b268d191cbd7a84b8107c6fbda42183c38f92573ddd3b7f491a3138c9249d6059bf2c77a40183b3dced6cc9cc7d3ac7571cc4280931c6ac33dee88a739e7e9f7cf1ba28632b8ec972826b99ce0c0d5971b05152c742dc6ab67015678d7c69c29ab9f67caa38f6a628164ae4efc9978aa34b907315c5552f79a83886ecfd267e4a9e2cef14e71c7d2aec8f965cdd99e2585aef928a77841473a5382849a17123b2d9e40d294e5145ac58bc53cdb619c529d4b6c49069238a839cc82bf964b899cd84e29892f72f49d3f17719501c538d7c7e4ade35a1f289c384d4e59bc4a967b878e2947cc38618a1d233593a71922046f3cbc56d320d274ea951a2c836cda297b289b3b5a87c2592d6255634711809a729b63a72939289e3fbc42029cceb2ba460e27c21445e8db90a1bd9250e32696d510daacc436689636ccc22c39d4d38b555e2a0b2e7c8588d6ab61a25cef6562a838eda5ebf26f126dbabdc172489539828ef2fa54fb61d89d37adba7269572f990388996ea3295226e6e1f7192267ee1e6e205ad3ae2f077192984acf9a56bc4498efcd5ddfad89614230e1a2d88254d2fca941671be8b499e5d16f3b415710a9675549eb9bc114cc4416efda4155921c743c441b47a88c49064f5e510e7d45027bc46867c152180210ef236faa84d1272157900010a710a5742e5d532d5ed3f0108619cc8777aa6419c4218197e2e4ba62c88b3441b5557aea1570fc441c4d33badfa3b1602c429c7b7e23c420a57ffe1a022959a38fad72eeb87639e10fea4c4fe08f6e1a4237a33c47806c7505143c4b6cb847c7d38a7c5faffefd3f1a7ba81f7571611d46c7a0f273dd9f4675b7d15133d1c832a656512620e0bd13c9c8228ad979d7e953313dcc0c3c1bf44ea49109a23c5dce1a81f2a29bdee2999360adcb0c3b12aa244f391af17792b09dca8c3d17b44b00af2375f257438a6760655992d758fe770d8907e1b3486a4772d399c94486b99a9b42f721b8763bc7813e94292ffcddf80c3d154d258bfb2f9aa646ebcc1724b277f61f2f191a3bc08e38b31c0409845b8e186f3b58998a91399fdef3634aa36d4572be9897d09980da4e07523d14648bce8420b2ed6f08cb548cdd47e27a3238bbfd8a16a3045ba3e956a3b36a9236b8c30bee0c26f0437d2b02439973bb7b5cb30d090aa55cd7fef12f4de82adb2c8028b62886738bd4a50c253b28e18d131c331a5afb3d30a35db5705de450e2ec0f0e2c178762cae8471a30c07cf6cb958d3f317a61b64284699f04d3ae46338acadbb5c0935d16614c3b9f29990f91bb54ebf37c270ac4da9b45dceca9d3ee60e30cc0d301c35a9abe44cff9b22a423ab901b5f38e4d820bd476b184dc70ed6615f70b1438b03dcf0c2c1ffe594fabf934b425d38459839297a7357fa9a0ba798b278d508532ac27b5cdcd8c2b9f4ba89d68f25ebcd8111c6175a305590f5eeaea7fa13dcc842a7dabbaf3b6ece12dcc04226e76159b22b465f479699bf7105621a1527dd5fb495dcb0c229af4ed8450aaa5fc455384f52723582489ba499a870b8b835151dfe7339bf3185d3690ccdbfb50a37a4709013f2ab72f88b9074140e335934a9e90d115d85c24969d29922f522fd069f703a990f912b5ede3e74c249e98c6dafabdf68c2f9d54ce2fc6c20b8c184b38bc42c49ddee67f49770083224a57bf442f4540d430c0ae4e086120e41c75967ca2dfdf725e1f8915484b413f3ef55750309c7142cd39b867d68fe47e8ddaeafc4650822be6184833ecf90a4e6326ffc221c82cc74bd659a46cb4d84e34b3021bb7a548e096f0ce168b2effa1ff25e64e8123784708a5cab69a57d5b4ec950e246108e23c94b33ccba59883be206100e5afb73498518d7746e8ab8f183e39d88bc2f93328e706ff8e098c4ffa9c8a53f42927c7c5409e1460f4e3229e79fb69bf6a8e8c81b3c38deef5b0c6ac4563ccb196eece03cb9bc54ea154f0b3774704af9627eb5af11620a0e3bdcc8c1c9ccb32f5af8d2700307276db3a06245510f3d8b196edce0bc416dd756bc37bf0c0d376c70dcfbdd53a22dafc95483e37a060b39692a559469709ae015b66f12cc4658861b3338c5883d67e91ac3fa1b32388649d723694b37a9fc460c8e371b4e927aa92ebd5f086ec0e09437eedcaa6714f5888d579cec3486e09722b2f4a823bb7012ac2bceda73a53dc2a75e34692b4e31132edc99ebc911ebc802c3021c063be0e3030c2eb828678315c718a292e95317d2c948031e383c2890010a508018185c705130606315e7dbff0fbbbe55710c5124e98634f9f868858d54e4a54f4af23bb525c9a2810d5418e29a3aedfaed1a6c9ce22842045d4b391124eea638a4d1762b7fa7a2c6ebac14c7f9eccc0a25e6c3dd2ed820c5498345b0e41775c498cc0b364671f4753b4bef1a13f1a4051ba238e9980d672954a698356769b0118a934a9b1be92b42f8a80d20c5051ba0385b87b8209f977a2946f613871595eb2e7246eef49e389c597aedabeec923efc46127e8ba8d1952f8d93971103731b927739a487d13a75c41ab2949ef6b9f6be22017416c9253cac45163fcc96a4853114d983846aa24a9b27c0899842e710cf9d52f124e260b6a4b1c6753ab9b26a1aa5d57e21884ee2f39966293941207d197db2666bbc4244fe214637e748e882453c99238eba40a9627d7481caf432f987cb9739142e22479625525253ee27859c3a8f8ddf7529e23ce7216a384ac5591c6d28863d48d6946244b1d9a31e29417fe52a4cc6aa359c4c964cae4a3a7336b2953361471ba8b9944b26c1ba2ec89388754d38d97b92ee811719e342a2b2615db76a6431c73093155639de9520bb06188d3c9cd2d6b95102fa58538edad46548bf93f94fce32347246c10e264992a86da4a4a31de208ed92a553c79a733b620ce1fdf966ad9a1392e813826bd97c2850ce2ec36401ce44d261925cdfb87fe54cc7eb1bef5e40fd8f0c3215be4ca5496f2880bb1d18783b27017a9adbb2a592e30051b7c38bd6a66907cf57129dfc3393b838ea02b6f0cb9d6c3d97d72dc7ce82ca9ad8d3c9c9296ec88d3e3e110549ca447ad4a3ad1dfe1105265a588c1dabb94763885ecce538b903b64f7f1b15f70b1430b538763947ce2b75297c9a8d0e120c9bc237c9cb6a462733885bcf8a2d42e4b50971c4e15ae525f3b53683ae1f02017f0c0e1412ce081c38354c0038707a180070e0f8285070e0fd2061b713864d31aefaa3264f154475679840d389c42458aad15338a4cca230b08809105236cbce1e8a777a5b6520ea171371c3ddf6d5e7e547e953cb2c0c2a33661a30da72dd1a1eb254cdd330f1c1fc4061b4e627a4e092d3231a6181d59a62c10c6c787295665630d240436d470daad5049464a1b4729f02e48c05d3005101036d270eadebf11a92d77efa2e1acd154df4893cf4bdf2ec1c6190e334a0931329b9a9594126c98e1a04c5b4fad64c8f0bf0ca7319d9676467bd48299051b6438c80849d464fdd1944169051b633828593771e62d04eda718cea55e73fd8c8c051b61386590b52125b91a646ac170cc7d298a4ecba825ce2f1cab435a6e5223e2df67c30bc7aebf88722a5a791b5d38056d7ab6f7434bf07688917290a00b2db46d70e138319f10a92cdd1146176c396c6ce194b133ca09d1b231c8011b5a38a620255e3ac93d5a2610b09185c326a16c53f55d485a25d8c0c2e9278d85281321bb193024d8b8c229e96dcbb69bf3159d810d2b1ca42495d49b347993b220d8a8c271fe84ba18c1e47dbdda0dd8a0c2792467efc7ff4dc8eec7c7d998c2717ec44b7eb608b7902e2ce081c30387070e8f24010f1c1e1e5960e181061b5238bb4499103d764fa58c17df8516260a27e125aaa57f434d1e5940200b2c3c3e3e6c4021a1238ecc88bf69d2915528e081c3038729b8369e80ea7b2fe15ee6161b4e388ce56ada525a7469b009a7314b3b9752839a6e9870ce0829ac8a10cf90fd259c7ce3424c9717fd102ae110d304556ae35f5a948463ea35734d91eedd234838684b505e59e2b78e7e846336159164495989958c70b0e4995b6523a7945b84b37689ce9df635a12b110e4a8747bcb05d937708a72a2bebaa6d0d614b219cc2881149ce3a2d641704e395d0b1e02a4998beb0c006108e9d2de9faf23b7c648860e307c71492d75dc44a5121ef8353f2f7ca1541678bc8f6e024d4afeb428594142b0f8e93543755678cacdeec80206d7ccf630d820d1d9cce92327da3ca665d4e28d8c8c1c1bc62ec52229d5d9f70704c3294d8ccaed191c5458e31c0088324c1c60db6ca501bcbac31ea6fc3065fe86a6e67c808b9b751036f5c2e6967ce11abdba0819a5d441032462b2bddc60c9041a5982d0831e24464430675e5de4a59b162c8460c3c3f2579bef499ff6c03066db6894f53679ae4fc0abfd56c6e2c766357a4d25bf6a5d0a054b615879aa067e656ba654597533d5fee84beaaab40bf7f672c7de2dda72aae1fc97293b26595525130d526fcae3e44852935a824f69d82bbbeddf0fbba9ac2fa12af15567d624a51309d23634dc8962686148ce5aca8b68cc294cdddfe52feafa844d145b70d0ac52954664bcb9a8ab9078a53f8a4cd5fe216be2c1d599fb0b209691b7c3542fc3d718f58cb269d29a8689d38a8cc5ba2d237b34dc4893c4c868b6da2d473556249f3a97c320894a186268e5f5f23bf7534274b7464a10a98021e383cbab080070e8fd4851613f0c0e1e181c3630c9f80070e0f1c1e1e5960e18143f50be62247f292428d4c9cc4f8e48b17368edc6a47560004394a50031367b994c54e7a5d6f7c7564e5f0526088b1a38b631aa87189932aa9416afc95903d0ba30b2db8b0c4a94246cf2e44ca4f534756176088d1cb811a954844ad9f69eb0c9712ab96c9fcbe4965ab142fd498c4d123c44e7dbffdb0938eac2d5a0b24467230b20b097c7c7c7c58116a48a2d0ad1a3ebce374a8230bab8b0d9072418d489cf25443ebb825656d7e7c6ce5d8e20b2eb42875c5831a9038fd8e9ed409faed5de770146ce1c51848c7401ea8f18883f8511abad5922ef28e38c546df2c4b2a8587d988938c88970df9fb4b4246bc265f2fc184a62ce2ac7311536f7f2971eaaa52a54a066a28e210933ea1191afa4a68be502311e7edcbcd142e69be248488836aa6eddaff56fe10471161732529dee2420d439c63bd54b8c93c32ed99851a8538296fef8f993c218e5bd613bddbf2574c07710e95b349c67882307709a14b2e86f63a10e751e2b593745c9b48c2f01d3bca8e1dac45188602b3420d401ccacc2e69abd10de7f02295fde1a023be8988ee9373998eac1d5e7c7152136af8a14c1f22668467d0c1f8228717e9ca6050a30fe7dcde206272fb9e94443cd4e0c34186deb59079ade345d6861a7b38e9b61aed511dd1523c4769058ce7a8e48ee5c3a1381c0a84c26040000c08a52ec70193140000000c1a8f4662c1884452b5f90114800439402c522e261c241a12161016100a84c1401814080903613018140a05828070404891c7b4ed00921cc40004733e0b0454b73349261f9cc70626f3619bf135f888ad197eb7e924d50e0ccf438768dac33f0cb5100e857045b408afac75a02c0bccf0877d85d039592663da9aae9faa0281e226b057e9d588ea51ee92fe884fef050534663389d24455f41f54d9a8b989a5cacae962e0dbd8cbc2540fc93a397dbd2b31a7c5ee7ff3725f227288a3ee4e39e1ee7cdf26926ad6104e0beaa13d9bdf2362df127c18256f1a448d0583a136c08231086812b21e9fb317c0d6e5984db645a1515bd2afc570b66c1a7542767dcaa9d79d940a533a0fc3f9a39a96a8e8aa5f035be11a3770d2ca4d680c2a9bba0c3f12964b34c3790616f064082e198eb5ab41aca0e20cccc2c1c67ef0c05c1166510494888d107fe389570ece7036761414bd7cee21db2dd667a9ba4b1fea760b2d74b253e968c9b9bf70ad86b044919dbbe7405189ff70c91221a02ec838b2984d33c82bb80218b8fe3ded0d2bb537581b070823f2f1d34027d670a94f22ff26ce3e3da04f383ae5e92cec91a53400ac7cfe01976d8ec8ddadc3bba69be906e961eb158238d0f124e457046f091ae86073da99e7fed7606bd58c3c538e348af7281b9279e5aefd08d1345b13e7de41197ba50fda0003d92392519885a47bcf398f0d4e007438e118a692f9cbe0d80761081849b33049b61298622b3029a2c5cec6f2f6043aa41c2a851af8ba74ba1859fd272c74495b2ced5e8d2e23fa016dd83a8c692ddab4cf3d042722f8ec8b388028916ad698811e343feacb3844a0b9e99a3ba73183e1c0a3f9ac201510867f527258c13b2b3cd36ed0353146b213992148c877295008122317468503734a2340f0bbf934f19b76af05e7e3eb17211fd0abfa0317682de00c380ec9fac77c0fdca2734dfe23ec97d42957d4e8d1c6ec5a9859b9eaeb776778828fd853218a7703e92bf6cce5136b9f59ba3b56e1235657da9a32e7c046376751e334a1ed5f24c3f4051217c50cfaec14a089de8ad68476460d6aeb9e407a496fc590e505d53d79c5afc030b9a40ba4e4d5e3a0a1bacd25cd66d8207a6d9c72810e493a21f16089c57970947d19e406054e901a8a31abe22ffa62d1fb8b0b8ffcd33b14e0fdbaa0296c77c62fee089bf7f3ee8c89bbeaf0e61fe41e76a132495572fa58d3817e9e41ccb0a2b6808465301da2037b35d5564c10033cf4f8fde0a8036f7bc1f61af9848e2e30aaa8e700dd7e66a1978a3cfb1147076d6213443a024144b55a45bd8cbec151cb228d51f99b011903d5c37b7f9f447d7f7540ce8577763e47d624fb508eca9c7ee5377eddce4996684f9f8dcc901a797683ece85a18352fe3dd3bab261dab3fb80c4f6f9514a832eac27fe3564d21758f1260b087961975d69187cd832e05350426989c8de8b846528d517e1c1fb4ccb29e4a256c38154c348980b30a26a18f357acccf5e7b532cd5f0fa8145753fdc578b43a2acbc2e14279d6bb4de229a14ffb34ea27d40005a19eb2ea27b4d773a5d4608ddc9d5cedfa5025df87515e8daa88bdbae76bcbf751549bbe71eceaeba98ee9fd4eda5c13b7f0168dd2edd6175cbb14bcabc75989ab7ae845104e2a704bc98021254bc849ec506d99014343c11b08114c7fc7785411ff06f7c7341682cfe53fa26a33c7f362b0cbfb9f29270aab16ac0e2c23e814da4177e8a2e738bdf2d4cf201f8f37ed56b9c8c7143b6c9c22006d6aea56cab5df53763f66863416e17eff9e3d398803e88fc9cc239dbb322460fdb18a5c68cafb4f830e7b5e12445aec5ab2524a425b111c530431786b8082a8507e0faddddd12d92bbc400f9606a94504618357de9777861f5288272a4003b85f57dd49dce58e138ae048e35d2897aa342b2a9e00aa4b0b4a278440a91c77896493440db32e67829edd9846b8df08c6510e1f1aeeeca9875b89b81ff5158095e3601ff9e25dbb85762868af206d9fb02467ffe6daa0c875142c679ff051b3d1e93e96a51fe111847041b023308209ffc4c21a912d6f57dab1fc7ec1b909c87c9c648db7b4614c8d95e1822ee2cc6615b3e462d6a1e219e69143d9685d587082608e8d79555df0933a00c1283ecef6d344ce36b5248fb8c72a0abcff89e1170e46660d54d1974d3fa2bb3b94f66d8e19eeae2a82aa185df4e02e2a594a2b0436bdfe0e68a37fdee1b347cf463bd5ba26d7aec67ec592fcb2ab466b971ca01a459c7ecd5926bb9c03eee475924a506f650da2f4a29fba88a50c4c89358ca4a8181f0c3a1e0f84a1c3770e62bee8f62ca1b840f241d0356140cbca2b5202b106eac2f3cdf55f8e4066bd2aa4e2d1eb219403d99b3fc8b57e482ef4f9433c3ff449e9976270385d1a3b0929c817febb2aff821bcb403fcc97d60f72fb99122160d02394b0a9798389c621979ba2dc3a1592bd350b6770c1d0c201a127db552b074fc5f489fb8b4706deb0012ac3dc50dd0918d1888612f43e0ecfba8caa8b1d94c30cc64200579785af8799efe23968e0fd9ae53aaaf198677ffebb847f90248903839203581c8ed332a50250cd1cea7e34c8c8c116d29bba5a1f492bb63b812d86ee26acdc590886053b4efcea17d7ac4b007c5cb0eda953e04e5e524ec516cc1fd399c3aa4f7843689b0eb4982c405ac2397ceede1452c7d7d1aa128f539a082b18acae85d9f68f38fb7ac81857fc35eedaaf4491b11341e70f243c74eb97341c48d964155c8391e7005cedcc56e08683cda74a0f91c24a028a0334096afbc340261ad2a9615d60f3f0677bde19317a83caa291cb26b818d7ed8d1937759f6f137f3499c9a3cff5c90f72dcf3eb73bace4075fbed145bfbaa480abe99d077fc712af50f6ad5ddb754dd9f25b540d4fb711e0e931cc500f30f40c035b1f84bb2968f40ab30de80bfa1183890c140c6a7d802b82d9186cd66b9011684c18e69c7b9fdf5b706ea39e130b5a214f767144e630238db862021405680d80d40118a5a45c0b705a182de4f0e8c5e87d5526e0983d0adc84376049690850170a026f427011b17df08d5ad49486f2d85f952073162fb98269829b979ecca2cbb3dd5d70ec3ceac82f0b4bb79cb3b31fea78b1a6fd99929a4cf4e7cd6c746e96114b9b2335e010a0685b63bf7c4beb6f52c28157a257486ef92a3c6869bbbd83037d3fc8fb0ed890dcf237ffc9eba49e8bf60eedd3fea9687945a68e00aa03084c489b4e962a8a22085d77a47e03c1141eef10269102288c78e364fd2b466ee16a6877be45f48d1aa0f8cdaa63d0991b539d4a0cf15065ef1396b5d328ac1f0f85fed6690a12f88069d3c54f7bb210203ba6a7ad63e8fd0b456c7d24db27391410c2f1f76ba13570ca1eccea1019476ef8404a8f7070d02907776092c3de6115725e34a49eb5160a4cf35d9b818b8b4ab4a19585257399997038fb8b0a6f1e820b374f2d17ca69bdb30395b8b8fe27fb69768450023be3d8f9cac662880ef84632d17083fe1f7284de6559fa37875fbab937d0cddf24f4c011aaf8f6c5c0c6ab2661c3b05692fe7978f2c2a7a7680d7bb8bfcb4057322d045d13f541d0fd93a3a17223c804be66b36e636fe31af2d7905d29708fb68a6b95072091388abede100c947ed6d33046a24f5eb962552588bfa76b459ebaffee7f73005ab084390b1b71efd258de8f5a9f714582acd550671e1629b92cafb4bc35ab212b7878bef00c94c786ecf5228da823899e518db8a8d3723d6693cd7316b41501c68073cd55f95e772d1caa06108ca5981c983dadc3362eb0ce2c768a5cf5d1776b3c60ae12b07a76e8a9bad72d7ed73ec539a390e3ba57ea5b660b4b256dbcd2da3bee0cc4d98a4b58e22c3e364e8acfb8ba754f6d0c91b7bae16e887d0869cae09d59a0d0da06eeb1df6dad44483d8363a5e75b5d5168e50ae13352499e6f870ba70001afa8474e7e120e73888321789fa40176c352dc2e8474634ad67b717df5c7a85bcaf9db33a488e9b315940a01132e6020eb30aeffe036411a50618a1add348b8a89e7c9a6437e94cc3eab097387149ebb292f2d82b955da0659ca721bb121d953e2f4144a5c33919326fa1a556f75906b6c4802fbacd0cd563d4646b3016e4323dc40cc04a4b5a707ea607b4b146484468110825b20704237c5e5d78aa9aff8af1249afe15022e1407d6a8642e5a97a65d7fe4d371de715b0c731b16589952b156676b6cf193cbb35999b89d99726296dc6bce0755156f9e548c06c3cdc790d2d81b941970332facd824b9e8bc040ee0125396feed483f2c8910722d3b119f246e6ad28ae14c76d29e79360d106360540a93cdb01ddd7f2f63fa37e4da1a06799869f1629902e6660c8ef270eb0ee97c02d93940f9fd8b43109ac2e7e9f4517268e7fccf16250742defe8025930465d98a0920e74e68837c7e88f1f6e5cf591d7db6e2746cd79ffabcedaae435bf4221cad9ec23aeb61283f7ce9c31a45c45c6f12ac350d0b830e789c5755392903b3e16da9f2eb00692cf26e56f6b595f92db2f7270cfaab832cbebb61c59d339785f1225451cc51820d3b6861ace6b6e79db7695731221567203cd4185404a136ac4c275daa83449efe194438c83a9b87adca4f5a7389bd4171fe93b80e75fce898a186feef4c60f01bcaade036b838971487ee87d9c1daf216d2216ecf954eb93732262fdc3f28b2615fccd4c52df8574d4de85985d8e30ebfd5094d5c111cc085285fe764643ad9b50f71c636fa2faff2d12fd696ee1826afa2dfd366df6522a255a13d72b6764464a0c00cd6d04768340942d000a91d38807717891765e0f37d1e827ff85c4ccf78023fafde35a35544d5b8dc8279e2660740c79d17a04cb25c5e39720ed864175489d16dbd84f82a1313acd2022189c35ee11aa61cc4e467aee58216242f4be432045516af0b24ed7a32a1f7d52ebd49883763f3076607868a6cf2d20280742390a148b532ea782e1953df41355e40abc3bec1585bcdcec4eda76d0050a66cfcb1141721ee6a2154f588c2842d80aa577ab2109ab33891710534ab484034027213d2f90052052abf65750182ac75e49efb6d8bcf260928395c1d3bcb694312c7999c24f363dafa976a9140dd971b40e1a9f21429ac7e92c52debd0e878948e39029cad442b8f98b030047d0f21e0cf57d43b72443ee51cdb618791202623f80ebde5148d4d8a2a98ea4dc4ed0132fc9b3422f21a46e467b7e433d21a4c68ce7eb2f684a6ab01e1ce511d481cd36f7c1c613b34d4098f07b7e3723aec07fd740b904d7a0dc8c211eada603e872bd52be92c5838ec1fa41420309a68198f09d022e35f053e4871c8c3f1dd419c95a12fa0b935b929ca210e26ad3e8efa9318121babd2dcba84c9f0cfc1a169085fad8c8d8e6dd7e6bb4de3028c29d0ff68f3932d778646f8ac74a11e5afd77cb5bfa382f694b32b1f8882312666c60849b96bffd7533a19c45432455857c5a21bad69aeb662022594ae9f04c910e2c386cd8328ca52a82a3b189c7a1d458a1e968b6caa3d8340da4226c4506d18478124a5dcb54ae49603d1f323c3a00770bca0e870d52f1c41c09a3de9d27706c233da129818a4bbc2097ff96dae5727dd6b70e1c0881dc19447e309d49eacd63e1612f05dc217d1be23d1ef7eafa480ea1601f58442c6b16995eca420997c7687793df324860a98527bb3ef862350ae89c739b6df2634cc55a28c326156fb17ccd03430aeededa322ea45397584deaeb3087b0937727894501593284b5e979952bbf273077ea7ebf41f6a18237788886ec72ce6e88053b95f91fa712f39e88df533d773f7dfeb79f744f78c1d70250979d3ef9540880c9b889ef9e0ba58844578ee4929aa555454d716c488ab5215e797da0c4c5cb928a268025ae274845e8ab88ada1ff42789622e93bd0532307b1bd9082d1ba654f5028d1c62c7930eec50d0ad379a61f740276460639d140fcc4719196bf9e900426c816f82b6582fb6ca42a5fef535373d112742303ab9c9dbf6be5e01b46cbef9130248b212a39d60dea502636083ed3a871e5f8259f7f4e21f04721249c67cb9678e9600c5373026b02543b21f0ea84dd146a5ae8d7a3369d6e3dfc5426b94a7f417ba6a13760525fe7e26c8118f6751c727f5554d0229c8db27cd6a7c3773bb183799a78e1ffd80a0af6e398d18413712f5315b5ea590ae4c3e9471038940a39700ae5807bbbf0216fbb89b51c3824f6937ebd21fa8f3541a32a24e719de693f7b428792a709b4f0ff8f5448ab9cc2cc4c6c2c790760c521138d7a1d856227b86ea32fb74cdc7aabb329880b71902227d4a0f0901fbe4124aa6a4090e5bac9cd39a3795b46ded5a4d058a69a3d0a05e429aa97e10c3ed202fd4eae09dac9a9fd17170362641b13ec1e59a133e12703417903d4a03f44d41daf82fc580c6bb8694b4418520e33bd886674a7bebcaa2e7db1dc52d4761feaf716c3876b6d023321c12d55707886d8c49cd5c9b033903489d2e79628fda5e5ca8937c4c9a22ec295ce948d61908a41f84e28af8169287f15fb115eb1d6fc09ed112071bb92ac1987d440fa3b6070f880c3871b946ce782488c9dea354e46c28e92ece03a256ba84989698a032763ce31c358aabc3b335bf6a7eda13e837ff4ab2f6bb10c7233c057b162ff23ac2a6b910b1285f059e2393a00f882eaf884b9cbfb3bd672c24512b341b67b2b0a8e583023693112c68b47ce76699c0fe314693539f8cb93011571e78afac54c5aee705cb2a6ff1e62bbf313639d3497344be52d665f4f19ffe8a0eb86ab09833d09d202bf3ce5f5a9c7728628f1af024985e551066673ad3781cdaa50724e29590083efe7f263834824be5dbe254717c668e104962d4baf4c136bb4e195cdee14c2155ea9126682771e996385eddc514d7f8b436a69dee3aa4054df54aa8fd260e10022c67676d838a49fb4405706c7f61e7d9db3a865fbdac08c8ca098ae9fd8c82473e1af83c581e724bacb434b5dd2d1d6ef759b450404ac01e47439f302a3547c1248526ad36aa29741ab7391e701946e5e434c7500b187a40273b4f340ca9352f212784f34cb68c6d5a2c4a86585361ef830ab42c9c0ad6fa978909e8e52a885b6a9026ed69bba4c4503434529bd3312291f8902267a13b7b3fc497275fcefb748e0186ad8a04836d4e9632982776c9dba74650a56ace1f453b1603622c8943949d96678467603edec1490e0274644704120862252990274529393c1d6c0c052d0a39892131359bcc7cbbbecd89a374e0c52c65adf0df639647383ddd7ec035413eb1b3b4529a595bed10a6330fd047d646a4063a2fe8325034999034537165c48ced09157bdc7fe80debd7405fae4b6a60950517348395c6a8a65431ecb3882b04ebd0536de8fa2116c217bfc2a2a91145ff412e28e55d891848fbc81ab4499c606ffa7205c3b1927340a80caef0b4a2a01f513e2f4d541888876445f1d619106524c6c8d560508d34903422a7bacd2d91970c60a6baa10d0f6b246a8600b8c1780e7c6a944ba3327d3585eef3b2e021e6701898466992b04550e236f65d0240802499b2d9e067b5a8df281df8b9d5969c8655df8d9a4629e1319fb1160825de3b3ab8ae3b1ade063140a706c19882a6c3d3fb3e6b2c4b122b75e788c4005c9f2a232fb148f46c7c0269d3cb27849f12beec45655ea5dfcf2577757ba30197278eab55ade364b20bb3624470eea31107984c3443431174508c43a0a621f71c8892dca62bcc02559e52727e872af5e0d2767edec99d57b65580a7ecd25827049c74e547d4596a3b11bc9cb3841df32df633ae5a0977062d8441241b1858a184613d1310c9d9844428193dc5215b48511744206b9988f5ad8211771b0835a984117f1b1597cebb527948d5a07cdd923cbd9384590f6bb612537b4a854067ebfd315205997566897986bf3e4a40437e7d8d51dccf3b199b4b9dcc697e6cf14b0860062d66aed4ca3723184243af151925caed4d56b8241f14fe85c0ef9b8ad43b2d4279be0a9877c8a544ec775d1cee3751ceab85cd364a88f2c415337b9288ebcc57c91cfdbf51dc6f8d42729ea2143d0d44f7e70ed00fe87e6decd333ae65bbe62c777b6f9ed78ae07b4af877ef32fc0290feb3a9fc7b556b297029743b1c54976fa2cf24965f8b0ec7e48f90053e428a4c6aed583e88af4f4a19e3a0884ac5378f927545d8b0288401dea1391f4e98f44ca122f9968937c2a123ef9d19158ea251269d29f44ca273e32d296bc54224cf2d391f8d44744d2d22f4994497c32d23ef9a84858f2a51331a97d273f66ae0eb1f5d42f6f8988845af2b0c1b05e823f0f9b42976c3668e82fff0d23bb8ee524dfb7f3593c7221174e4ee76b3b78e7f33e86937d8edbe4e1ff745c9471a1074d4bbfeb34c450f191d6d5b54198c1e40c3d40d36ce10012be8f722151686263c967c89f8aa17b60d683a442c37b6e64b092db84eac5b1ac25319fb6fb3b7e6b0ea9d8d4f0f9c1fca44520a45c51477cc4d76c9820670c23da3e974b7cc3ce77e90fc45c3a15167dc39678909704d4445bab73c4c392c9520eb8872b7b4db7ebc02ef16098529e9c9a71668ea8296c3be65b76e18efed67a7aea129789a353a2900cc5e5b42a11fbd05d5ac9978288243e05925e753fb8a1d44dc294221f81284f3e144694b8299854145f151041bd8d6eb2fbd4b05e9224e6eb7676961f3d909459cef3951ddff1b78a746b4aef749898a9c6d26cc1577c324b1f8b07a18583302f40a9c6dbdfa88c51865d8042b3358b6c22ce8b5819919a11b9f0efa80c5f3989395d4fcedc48f4419bc516a0f5fac832fd13e481eb4c929279b969e2a3d695fe962531ea816d2fba8f7e1e1d031e2aff37ff25ffe2a1524f76965969985bad0cede7c4cc9528d3503667e5feda233f0c7de02db4a3ce35259057a4d4d431bbee75020400431489361469117520b6afbf91d5a4c0a7caccea6999f8546605fd55d420e91e891222b513ea74f901a939bb1f0e103e575bd8c3827a77815b31d89b0be9e582db16e8dd85f48ac15b2cd49305d2c3c2c8b756587fe6a668b949b7b157ff7f809cfd13a90b0584859842a0697891bcdc77cddced7c82ee31b2990827986e8e1463f7a8480f05226a1b155a8011a79c2f20633d27601ff38a070b2fd9b2f783dc95973b9719c7499b12c4690b17fea81330d7a8b63498dde3f319e000f0634210a105d6930a07001e74c8e05e5d95281ebe3f08fa27b700e782ae030f746e884927588a7330df8a8c20f93996f48b3339f9cea49569343d692ba0107d2ae5bec22d2f70d2f5898c45e5e31ef3b6681c68cd1fa6c1818bc37e32ea6dfc50718c50f2b67c2e4d026319b75438d50ac73e7ca4e49b2c1e527f9884ab13ce1b44a08670012f7dc26a6814f950b3308100149a4bc63c013ef3f89da82551f9edc86be94ac4d5ce169c5b5b8101bfded5113c56f4a004ad2b23c54ab55e6611aa14baf8d477cd801483aac9797e7fb0953e03b52dccbeb7c66e970cf64d13c55b1d25350ecdfaa3b264c1a0394470379a7a07b6006844c53c014d159717e2b3bb85363778cfd9c1e8d87a2d7df0de1952b3b304a605aa0978bcaa974e0a7cf2a1903204245a4050566163bd6377ec2c6ee46d83e9230d8f3640755e3ee3844ac50b3300e451fe7903796aecf7fc4f5e4fd6d8d8fbb1915c4cde49901243ccdc9913218aa14dbfb5d19bbeb0062ca1ffcf64aaf498a808276a9c7d225a79664ae0b4f70371f68b232cf442a36fbf4000630a35a5a6a289dc47a4c4f4f170809acfec1c321f7fdfdb818d03e1407727ddf762e7f8101fabacd9b0d2ff127572ff6f578c641c4d85b9f6110e514da03c7d56305be94434987528e65394b0acd8dbdec624979283436cb0fb5c8139d6ee8ab0ba3138f6c429a032256fdf5050f826c47d8c16e1814173f71e42d573398a523d697186edd4da1c72308a81f31325116dab4a1ca018284e3bc95c3691405b214683716376b3064324430388716eae863a3ed7655d6c84549d4716dc5504a8ff57d7a857215e5c0ac86c3993724e4af10ecb082fc9199042af8dda614597974d4e7fac89efda913e03428f1793394f2b72a2b60a63227125ff5cf150757127271d8049c9d3d782a56a44d430c74905b67a6197b3001e704d8097452ac829c354eb8e2db22d4bb212147ab0cdcdcba760cffa44107350d85b6d7f48911af76b7b9e9a726c836311ad085a1b550338ad1d835e1b83b413db2109e228518dc1ef61c11788ca863a08cae8ba47e52c665279be1d34b94a71de0941ef31b7e692e188f3e48db3010a9575e6572902aa603a31d57cc1078d3203e63a2d9fc29008d0daba614bc7a3a167b6d2cd40b4532ea14a55934112280e8c2ff91b8db809c4008d00bb09b4f4b4a86da0f3e7ff21b05e96e13ccf9291a7140e6b7d16b65d5540231ba498079b7d9dbf60e6ccb0429492b75d4df02bb7100a4c31259c2ac359a2e9ec4f4e95b3a29ec603ae4bcdf43298d9f95d6a1c303e72a4a341891056386be021ad689c691aac3debd062e243609ba87b982bd433bb1ee5eae2f0ee88626e72ea34c9442f97873f6dad393089b2ac69d37f7563060a19ad8936c2c94be1567336d0d786a30a0ba9a589001172ba0865a57150cadd1b8df1a82fc841d5d5e2608c8f9ef70cb3793f01896a776720bf7e6b8cee60f6046eaefdafba640687d3e5be7349065b447f1bcc13e948d4fed4fa9a68814f4e70409331a99b5638476d3353f2ad0738f151a4e265543d02ff3baaed3878f49858ff9819d7b274d26b5b8e2b06a90dbe6122a45839e841632bcf1e3e216bc1507e87b86e4fae08197a4da3d90839032a13fee3e0f1534680690bf9956fb50ceed10cfba8c5a89edcc319f7a5ad82dd8d9299ab15f204191e7f38820097dd5f727e9f6986021e16e2b7a8bc64056be59bba07dfdf4776074d1ea28ef2e8197b597deb303c8d296f53485c0ec2c81efa6974cf9d2a7334f5cb2514b9da7eac81213138c41657c55235bc4cb8360ce9d81386d4f7afcfef3269803cf7cbe3a0464023d294a3b05781ce8eb5be256220759fe30c78df7943163f3b093a92a56dacf6b8244ca0a512850cca6c5b60b77d503ac69de1d0dc11c7f7d01e65f70e08ea77f6d573c4c62d529b93d2114ba417208f1c8105d5395294bf8dc336f72e3b3d6fa1c0dad58ea333d27a6fca0b9ada43b5fc33e53780cb1dfdfe77b30420d84b6ba0fb18c972a125e67d8f72b18aa2f1a4d6203414b36e6f4dd0b98432717de0b2bbf140e9757140391fb24acc5dd68389f6e2e67fbe6decff4e3a2c30a9cb26e824dd5851df7ceeefcb1bddee3ef7bf43ee2943820c2cd568afc5b9d156151ac714f19803d8ffd3f291c9af2aeafda2cf3758eb534e0407597d420e484649aed01f393634488b9a23c3ae12d59a80929fea0229221b7a3a178f59b733be21db37467b04023bffb2785ce4280bd4754b755a089f2c6a0f9fc876d825185f09887897582606498700b50284391f7918f649506f3d85043bf3b941ad77628c53e49837924ec020198690b90b255cddc31ab0d27377543a01df04fdf1b9298529abc1847bd39d91dbe43ea5ff6bb0d902721c14f34f6f61678cd3a6cb57c51b442ee67b6d92355e3ceb21372397a128494a4d4097ff60a460571d0f770c15dc940c3bfb8a7d3855c9d3a2ee3073a30cc3c1538efbcdaf19a0eb1c391470367d4cd1917f0b11c9504c2b9ac9e7e0d0ecf2e21af4c72ff4a7e28a035a1b162d110484a60ba6fa55022925f5e17810e2e9d33a33d6d97ac533e428104e82e424f7373bc47e08befd1a1c3e830b77071cb90c6b63e5bcc49ecec4856a5fecfc2383aa1386fcc954a1c65b5f9ff7871568b43351cd09c2ecd4955dbe9d6b3afeb19c17caf97db1debeef7aa05d7d9f3cc305815741f558849803cd9db2b1243cc58521f1b38a6ad048e38563fb5925c31c084be372a3c820c82d20a98de8072f3d0064fcfa35e27327056c26e905feb378a6afab4aa9ffb8f2b939587afb20d6abb2f13635b8a2aaebcd8c60936013fedd2a9e0e4b07857f73880e38a15a130c8efd74460fae89c68549f2d3b6f6d2f0869a888121b20ab2b2258cf251d63509409fb27b77ef1f44aa078f495389634651c5130267ad92896325b0c23f1aef4202f1673140af85ce9a2f3ea2eea3790351b0a1241563b68146ba3d31e68867a9078b34d81c113205ff40ca4b6ffd9e51613d35f937812c4ed96742ea451a73ae80223eb6c86544db98c054527ae945d842e276d3ef106c0a363a380558eee74766742424e80125b00ccc4f1f9236112db9878ec927b5520e3dc5836d41d4f5819f301f751fb26a441bebef75aa024a2e0570915c8e3377ac12b3e8d266fba095abd97efad890a65bcbca5d0b4195a39a6d47e5290a2bd14fae4d4dc594c20523ffc2ad9da9aa3a6af183ebd0eac86c5d61a4e28e910de6de889d2c4002de1f44760a3560b25472ff000000000000000000c098550908db91dcb9a4739a8d14f1919b644544b2129fae139fce4c7cca6dd6dad676663aa004590a9d0a2a0a45dd92fc96debb2967436b01171040b102c44803dfe7d9a2af2c99e51f0dfc75b4c871349ad237e50cbc7a6fb28eaeafa3909a81ddf491878cb53b3ac932b07713daccae3cad7b64e03b0e296d8e9d755dad186360da3ec85164e6206a1f6260b72686e598bb95cf1306a6eb62f0949193862ca2e9210618d83c756d7e1795fb02977d69820589d821732fb06f6ba962593d5fb60bec777a9cc3ab5c29a9ed5eb40003748588c105be838eaa91938500528388b1054e3ced35fe5f9aea94060d1a1b1040020420010c0820012608038c148ce01ce07f01e7a91431b4c0ee871369a38536b44cf025780a6cc5c802173fa2dfe370a2aa1790800468d0f8046c0c2c30d13d2c64ff47b1efb80297d3a3b8b15dfc27e7adc07fa61c7d7b557cec53057ee26d6abd9c634eb550818f21d55e88941051c32974661fa4c758a91852e0ebef625689ef2220461498789da27e3a370614d8bc15452d5e8eed53e609dc86fe9821538e93876927b01d9997a3ce568c26f02142b010d1f739cae2430c26b02f15562aaab9eb712422c61298762d9f8e6521714b44c450029be390fd61ce5168d02079881849607d835fb4e7ed6036829186184860d36d33c5d1207561e318621c81b589bd2a3908ed39be0b80c0620c2370aa9931c7ad1fa7ca1f45e034a49c2ce66c51927d1081adfb28b577f71098182a74d8d64154971c2130de59a7d27e4751e209023f759b3af2a02f153a40604d3f4a15696259f2f801bfe691625d5d7689ba0f38edcb0f4477fb52e73de03c2b25bc345d04cd79c0c6e9c89532849c36f31d709b2f32b4a474c087ee38e886494c39f21c30a943e3d548d6f270c0d9476978c8df297b7303ee3f3289d29ebcd96103c63bdee6791c3c90b4067ceeb110621e3b060db89fec4bfde817bf6316ac7f1052aba770cd1e59f01d52f2a4c468eb6162c1db65a81c850a2cf8beec717f472c33c92b98f871681eca337789eb0a366b69368f3a94f4b86d051b24d364b1dab3ee96157c4ae1513234a4f4a15d051ffd810739db9dee5d54c1e5fa0d8f1dd9e3c0cd5470a9eb9aabc3d78e2c890aeee30f5a43f0d368a1bf3c68710a2ecd5a627b10899d4c53701f8ee730737347566629d85ab5b82dc94b744c52f01337589c1073146c8c5c196226cd21ffa2606dd2d444cd39db3fa160335a73cef5bce5610828f8341dbf3aaee4138ce4d9ab74ab8dc7414ff03ff13b37c76027d8aaac4a519e3ae538e4041326162c0739ccdcc34d709ff6355dd29cd4116a82b1158f3c7b7fd676e60b13fcc95a2d32c147d5779dd379904d83093655ea4aa92c871acd5d82ef0d7a9aaa6fbdedb304972d2a244bf534d556828d8ed6eae3d428c125cd4154f6d493e5d924d8cc2acfa1079924184d3daebda5316ec822c19b2653d59c13e56120c16b8e1a7354ddfa633e82bd14216bcc2993ba588e603cea38cac871d5a4a91ac127ebd6cd5a39444f64005a30826f11f7705f624c2d99168b6043d7f486abc771e4a1083eb097fedddc39a593083eeb765c973922f878a3c34355888a9743b096b921446a86b0bef9a08521f8d16c17e2a4e0ba16820dd5d641fe38c88b9410fc74cede71ec282c8883e0cba67273146a4422b2a155c346182258415609fa8a025a0882510b99f73f2ea1a5b6a196821a5e7c71b523d022109ce62b5dd18ef9d385e42ab40004671ff137c6bfc2a0c51fb4f0031fba44e28f78d47b312d28c1094e70800478418b3e30d162de8e8e1b513af482027f020a0820010ea041c30422d0820facd707fada51bd252babbea0c51e989092b87994e398fca25ae8813d0f9d2ba956d5fe4f286891073eaa8c69ec9356672f9ba0051ef8b048bd1ce5f289883b418b3b70297260b9f55ef22294062decc044ede7cb7612af72e4d581c91b7322840ec96306aba0051df8287da8f9de37a4b89a39b035e6b1e396440efc68748fc31d0f3df78b031f76a4c1c3e4da718756e0c0e7574f3988ba79039f63a6fcb4141d112d4bd0c20d6c3c0bb153c658a91d471bf8386374bc2152df000d1aa6822168c106b62c9276dad04a418b35b07ba649f3e78ff52daa81cf1cccd27d98f791652cf0042dd2c0ae59fadc71143142e56860b2434ae9397224592a042dcec0c7e519724ce304f1370b5a98812bc96a5f31a7a5c7bd0c4c86bca21562ee20774d062eb48d479df4abebbc31b0d6b1a5e31c94e5ee1c6288811661603f7e6039f9a1df64ed075a80818939bcd496b2c4cfaaf9027f7da9c752f6beffdc0b8c87ea9ddad6638798d205ce3fac8aa7aa75173e17b8ee204e9eee7d244d6e8149d11af9a176ec216ab4c07828126252cbb38ffc2cb0d1417e0a139398730816980aa91052725c81db601d8e84168fad3b56603da7bce6f133374d55053eba8f53bf1df46c53a8c0b7c798a3b3bcb6623805eed7a24e2de6cadc1d297092a7bf6b4cd42e2b516032871c76d5a5fbe802054e72ec3b457b2b62f4096c8c9b1ee684e77d574ee0cf3fb68e17eb26f0397a0e34e5ea30815bc99c9ccaa124956a09ec67b0bbce1b3af2ae042eb348e565e8df9430099c45dda46e51ba4a2d4860538a1332b2b6233092264597fbd9077d31029f1562fd5d50350d290297aa23740ebe6b5d161b5a56d08208bcdb56483a095155d110b890398779bf52fa8ed242603bd47e90f223d5cca141e0377d90757248028191f6983deff57372cf0ff80d12726caf57be76f980b3cfda9f63e64a96e901bf1eade330cfff75d2e9191a4c3d73a7c50ed88f7a491b43d2a8fbb4d00123a9d2781c049558b9d12207dcc771ebe96aec9398698103a6e3e51cf9a568e6ba5adc80af1cf246498d61a16c2d6cc06fdda6e49fdda68f695103367e98abbb731c64da4d0b1a306a1d3c777db4fa57b3602da775871e96c721c764c1490a2d891762f28fa458f0679dd143708fa397242cb8bacfeea41df1b2545e617db8e215544f5730295fff9757d54bde0aa6d362ee7d2e4b97c30a26fd2f68f63557c1446f048d2976773e8f2af8cad93fdaee2883aa25157c52777df5f8d4ab6f5470d193e3aa5e9ad69e4ec1edeb46493eff403f32059bf242f6dcffd14fb6a560a3d2b3eb458c14ec5dc7cbfe17530c268ec2a41d888751df8b824953ebe1d9d3816e170a5e72cc8b97a437b72f503029438ca9295be6a5f5135c7a8fc89e6d6afd513cc1698c1c427ddc11630a7682e9bffa7fb73cc19286136cfe58924fbc90e961d321009b602aa55b0fa725b3420034c184c85479394c395f2c36b44e996582f568d2412c4f34686005001301b804db59f753ead062caec7f08a8be38bc104115010260093edd4ab875ac64fa814c084025f828488e23ed50734ef60580126cae98a1824d66cd21681585004c82fd891bee21684a9ad00c0190846aa14a82aa5484162b178f30cb71d81e3b8406d923021009f6eb4c3df2acda91c67f100048b025b94b3faeb2dcf708c023b8f5a496e3cf659e1fc507017004df397774919ae287b1423b084023acddcb29bbbecd082e5456f64f13f973b58be053ca314dabb7fb4c115cc8c813911563ce214c041ffeaf6f1acdc9be1f22788fc777f3b4b2586a8760375e12b764b9001882cf10d3a28455b6108042f01b5639d6cca999527501012440000e48810d0cb8089210bcdfb8bb27eb481fb736b4143008fece7d734893f237cd1b4c8346df783002208896d8220a6c4839dbfea4f7bcf70930d08287400d620b281451cf747c2264afc4618b27b0f13c4de448b2cdb28713b8101d64757499d1bd6902d3d146795817bbc3af30811fcb9aa23c884be0fbc3a412a6953e966e0b25b0a22fddd15fd2affad8d012c1d2618b24f0fee156c59c35cdabbe336c81043ea64abf621dc2852d8ec079e8a1e5b018197310a541a3a44027d0811a36b63042d12802b7f6b1ee65e6a03958e35520821b7fca015b10e1b618021fbbe9c60dc193a68710788bdc9c9327a85f17042eeb44777aebbb950e2030b1e26577766df2f07fc048cef4fc1efa3e3f3e60f3667fcbd9593fa44f0f5889ac65d671cc5bc30326fb858454c898d9ee808d14153947a7ea7fba0e380f11d9538687d999c9011f474b159a5a2a6ea671c0addbfbc79f31ddf47b0326fdb788c4f43cf66103de3f63e84f314afee81a703521e4cf6891c3bbb2050d98ec8f9171f5272ac466c1c7efa92547c614f190051f344fee8f3be8d14dc582d72c55cd31784485b0e047d26ef2be4d2c44afe0a34e2f9127de670619ae60ba4aeba63ca57ddcb5824febaa3456417ab3298315dce4f0d7c390e7d1b6ad82b3143f3f672f5dbf55c1e7e8dee1e44b2697a4820db5355a612172d087e1456740062ab8ec4934df9d8648ee00e36f985940c62998ce313d4aa69b82432320c3148ce70d21b5ae71fa6f2404649482b5c841748778b1a145a5ca3248c1f7db666ac49c3c74cb51b011913caae9f09eccdf3081090e093244c1c5d81fa748be9e9597110ad662924ce5df669b1719a0e07b2beb728e23ff7cf14ec627d8cd928ad1a9513cc17d770c396d3a2ddddb8656044c657482ddb8b91273c675fd8b0c4eb0a1d7f43cf2d0be3f7613ac879de9614729f65643134c8e791fa5903c2158d686968c4c30212b54c4090f4cb01a9db284a03d9d630f19976034dd4fe720e844a82c2dd17d943e8c7fcaa8041f644c1a9e319225cd29c145f6fc5af7a15658903109aee34af7975bd779223224c1c773ab8ea27f4a79294f901109cea38e43ce8da300321001aa920109feb7b2ddffb5e686ac0dad47b03925975c8de4d29ddbd092e108ee4e5267d45d26f8e2063540462398aaa47b13bcfa83a6c50a3218c1875aa87597d0d04eb1a104d12063114c76ac67affb396a6a3214c1c4145c7a3c47d9276b222311fcc79147de5551df8f36443099fbd1bf34e738ec900d2d13d4d02ac838041faba7fd943d8f7f60f91b3550f037b0c0288108c2480102641882c9e94f4bc553bf68160c2c13a88069d03085e03a3d7f48f7e883640f427097c3d29ae47114e99b4170afd95f3db43819924510bc9e59f01755c9e9d340701ea7e8217f5c17ad1340f0232d16d378a45b1ef707decc33871d5afcc04da8ce4a1592e79ff581dd0c396bb01c3534a48e20830ffc897d8eaae81c1164ec81cb0bb7143972a3e3e881db1c4dca9da9634d56e781b5f34cbf4c271e38c9891dba488c991eb321c8b80393a93e7a3f62a42fdd0ebc841ecf0d3eddf8301c50429051076e5fdcbd2f3d35d2cb04197460b44248048b106f63670e7c0c19dcfce307b63972e0457210dad712074ed47376cae1a6821a0bc6099220030e4c720f730a3948519ae30d7c941b52aaa54fdab1c30dec4eca4d8f0ca934a50d6cfa8e3abc70ef8c920d8ca49cc12362ef25d1d7c0bad7470e3b2a5d658d1ad8f4b6541de48b7a1e69e043d2982cb38d062663ec5b0faf3d0317a9916eaf927bc762063ea7b40e3f726d115bcbc046571d8de696e3c92619d89063ef88517539a78c81fdc971074b391403d7154f3bc689ac0a590e6484810fb7ae3fc8716c312345061898ead0992e34527e08f90293acab373df454d951bcc047f2a852acd0eb0eb92e70139d1d7ba76e139b8a13647081ab0fbab7222b5dfe7c59828c2df0ffa3ebe1a7dacbf46b81c9c9a68388bb9ada6f828c2c985ebfb3c614752cf01727a5bee0e615d83aade497d2a5eb8cad126458818f3c67c869e2ea7a9555e0266fd09427629210724a10645081cd923a2544bff4513c409031052e744a172765cb1d644801d79469973b74bfa2608e4204d38a7af74e1e644081f7387a43c83b7141c613f00e7763fc74cbd186c041861318f3da548f269e432d29c868021f76f0b11ca41026b0dea134664689983e2f63097c87f8593b322d01194ae0a349f60a91fbbd9387cb480297ad5292dcfe20423255200309dcbbc713a2daf122057804a67386fedeacadfa591b5a5e98dd9061042eb24c520e4f43b0c617a79017328ac07ff897121df32a592483084cc5a0c93b4ec4206308fc74eef0fd345c6faf30c0c01b17061942602d47873479d7b6f36443cb0422e812b8d1a06160901184bc11c609640081bdfdc863d39ca3e7b4c9f841d1380ec8f0016bc932e849e78ed3a5d85094d10352a5c86a39c5552ca5985f629bb2ae78182d327850344a2063075cd0ef69fd40d304bda0000d1a5e7ca1033eea4b239d73dc2dd1c941f952534cd14b9181033ec2d53a961ce49a8071822f6e2880861764dc80ddd51fd714736cc04dd0f48d922260b4e0215003f62e57d2bc4b627eb40c1ab0f639debcfce959b0f719a287a81739a6596461aecc1c9235e463c1899f4b0e3df098757360c146f4493f62e521c4f82b987c91e17edb1dbb4a5cc1a9875153ca881c89e53b10a3157c20b9eec3effc38666a083158c175ea1cf8e5f41539d12a5875cfd48c1c872ac8e9a247693deca4828d93f3e68a1d840a3eecf190beba8a1c9953f0b697de719ce247cb81a6e0527af13049796429644bc1bf47fff529450a266d74cf3f9fb47a3946c1696b4a867774dac81105ff5f9dfda31cc78fef85820f4453fd861845f553a0e0a2e4e63077e81dcdf9049b543ca7bf34f1043741cc83df8a048db94ef071e6ee4bc92dd2db86137c0e11be95838aea286e82bf109afa3977844d474088a109566ad43e87961de7e46782cf51c7ff1e25a3fd02430c4cf01e3385a4b96ef688ee20c625d8ff8dcc4ad25b820931b31edb5c6be30f625482c9ac759d1cb678fa8a12fcc79a83b477a16ecde7418c4970fa39cad5a5e3fff84a82f315dfff0aeeaef6458273f558cddcf368d804124cfefc28cb563e824b8f72f06832a406311cc188496876cff9e3b5182083428c46f0392a7d52f60ea31f821b2f011a343e0437fe86092410831132622c82ad4b298587f79ffa3a5f80e1c5179ef680188a608207f9beeb4be27f9f08f64276685574864a352298c8b1684c9321d2244b0c621c82d58c1b993f8e3283188660534eef61fe39ca5cf10fc1da00a31b50ec046d8293408c42b05eae9ab29b7eb0f65c1083107c0e521a0f2af7f2723106c1f7a40cb1bd92688ee245108c8a487bb81b433d0c0682fdf30e2a47122446ccbb042730c1eb0a6200824935e29bc792f599f4872d77a59c13598211c30f5c12c9a1beabef2307e90327911992f998fde7f8ca400c3ef0d571cee31b2ba742540531f6c0a5201d999dc72195e21761d4d0400c3db0ada9fc3af40f21e3c686d6560b6e84e105df50407a20461e98f459f2dfcaa3f668b3a165541c030fc4f24fea1d98749b5ad151ca5c7163d881499e35a4ba0e543b4763d481adcf641a36a12c7631e8c0a5770eb71ca5dcd6ce818d1d78241db9ffc4e4cb81f33875dcef92ab4ad438f025f1e326effc92128c01072667650b1e65e70dfc66f29e12f7781f8a36b46ab4e0461855e5c68721028ce186a27123461b8e10d37878125ba52c8dc10614418c35b0da7b7635d13ace51f06e847182186ae084d81fdbdb723d687ac0a4474ab5137358370f98ca3455f163f59c68ed801f0f24df5d8abe4eaf033e65cc3928a91b69cde680dd74e7414479e64a8703ee62dfa790f81ee53d6fc08765bd1e5accdb80ff18435946e7ff842e6ac09bc52ccb97d9cd3fd5050dd8b43e29538e37b5c7de2cd8f10b39e5b64e21de4416bc068f334c73782c787109094d412cda86b0e062a7ac7905631f5db2ed567dfc962b9810dcbcffa43e7914b582759fbabebf7ef578b182ab0ce1919e443a225a05aba9a3c91f84942af8287243b03896a3bf2b15fc676ff98777bda95f5430593dfc983ee385f4c929f818e2755489eda9ac630aa63de906dbcf9f15d252f0a93fabe4e873887548c18d69871fa558192d4e46c16ad45cd69fa3cf5514051fa57e3bdf0e0f053f12f2f29a78bc913150301d07921d3bdf4f70b749c763a8f9c7be7a82f77ad38d1da7b5e2d8095637454aab1c524eab73820f338b6da478cc8f439b6027efc6ef730f25a9d30497b26f2a47c9ba827d99e0b4ec27c7a7d6917d342658b55c21e488e657ab5ec21da9d1cfddca12bc079de8fe50bd4cda4ab021f33dcc54a1045795a7ac2a64d393dc24d8ac0b4bcdb210429a48828dda99932ea96ea245820f2443ce1e7273d89f90e0834cc13f94c435dbef117c7fa89397a12398aa8d9b39c6ef94956ec4b6951d5e1f868ce07250215ac83b16374f16c104b51f8f7451e37e14c1afa4d6982a485e0cb14470aa9eb6adbf529cbe11c15d75942cef238474218760b296ff6708a9a31ecd107ce4f04232476a3f5208fe4b52fd7ae8acd19184e03c49d49f7478d49406c1646d7a28f12357a85410dca4f5782cddc4985330104cf2242987ff1140f0123f3386ec51e65bf903ab69c4edb66e3f30b6aada41f3f5866e7de0a3f65c9ac3fdb83a8ef081bf0af531ee9d7b16770fec786d1cbd8ea342533db09acfbb22e4e82c679d072e7f88df399e1c6da58207f6c3fb90423a4df0cddc81c9b17ec7b40ecb999376e073eac07463460d41d275604a4377c50f8f0e669ee413da21cd81cd517d5c1fa5f3c8413930a57ea269ed89039b3f68c72d5d9d3a391c78f5addab28cd48da937f0badd999267b6cdcedcc04da8fee61cc7793c8edac0bd785bd2f43d939ab0811d899d3b7f948f859035b02a9972ffdda2065eed5b227a85a8d85f1ad8b4abfd8fd16f2131686024a7a3b658db19d80e2a786065de1b21c70c4cdaff68b93b5dae6d19981c1642fe90430f92c6b4810b323069dad1bc620ed4477b021763e0420e7423b7e4f4b05531b0be913aeb0335ab1c260c7c6fa48abdc4cf6fcd085c80814fba13a1396f1acff611b8f802ab793d74070f31ff85602811b8f002971a237d501edf05feffb2f452b4bbf85b11b8e0029fe3c9da95742b04cfdf71b1852db6875b8c540baa91c5ca3dc8b4aecbf179f44e0596052eb4c06f84f6a0335bb2c0beb9e438654754da1c0bfc756fc6b4695d4269a7821a755c5c816bf1e87f2b7553e0c20aece7d5a013f43dac7a2eaac06f4f76cb79a316dc08a3580b6e6440095c50814b9ffa1c84ad98042ea6c0e5181ea51845b205ef7002175260cc3473a8f7d8e1568256c1e32fa018180be0220a6c87f7de7a79c392001750603c2a5bc7a1d1bb175f78516ee0e2096c8eab59a2e347971ee5e8c08513f8beb4d0edde91bda64d6062b00eab3dccf1984f26f04922655aba70097c94f3b6e498590729a90cb85002ab97ea17572284da7812d81cdd87d7de3148e0f6f2a62cfb5d4e398ec0d8a84b5d0eff32836d045624076e9da282c664725104fed7aaea7220991f4e99022e88c0ba8f647d9c1f736668089cf4a7b09832aeb7466ffc9fba2b7021045e53defd98c63cb05f04355a606a7c8d138081803058c0346884c18267021741605b35da3425c9714a2b10f4cbf0de8eea9d82e4074c072d77ff304589313c41bf8dc2850f30895426d595d54582058fc35d3aef5855da002e7ac05dca7fb92cbb044b1f3c60a2698e42881d1d224f3b602f9976f2d8f1d7faab033e1ccf57a1b2262d015ce4802d4bc9192745add60e1cf0514788f0384cdbbe19808b1bb0712be784a74eff5fe1c2064c5ef705cda3692c5f8304fd370c2a1735e0fea26d79678446995cd08091b6d2ba107f7ad463167c5df0704f296f264dcb828f3d0edf1a93944e3ab1603fb7698e2e040bae53ca1c2a278d9a35fd0a36e9a6f79da812c9ba2bb87aab4b9a1d589fa55bc1488e83cc519c4c99966205d3797a325247149bec2a58d790aa9f91735cd755c1e7308756eb92f7e3b8a9e03bc86de989d37a3d41051b434eed3a9d5bffcb5370f9c3f1ac147c63e4ce149c5ea61c59f0b092a9a4146c99ed66b6ab49c16ac8f651d4db7c221a1b5a27308157dda0c028784def9148546aea8ba26075b26ace1c39a6975c28589398769346eb902e070a4e33e7f38b2831f74a9f60233b27ba04bd9d14f344227af992c7547782fd8e7d6348ae15a2e6043b95fa9563916c82d3a849b2776b8e0e4b13dc4dc8215fc71d87923ab6c804f713d3377d185539425b608231ff1c4dd5b24a417209763a5d786f5e754c59820f5b7d3563dbc6785a25d84d3fcfd9a9a7049ff9a945731cf9438a4e827dcdf49421b5ffd4280926253ff390d3c31c29468297a4a3aa1d0421c14bc762d32ee26db9f308c692a9a85548f7149d23f8c0da233d48d6e88b36824b27f9253f744b79538c603537472d41832691d422faafaafb9e7cb19e28828f2a7a183cceba0fb648c4ea9e3afe30a6902d44707e1e66ec8f772f54c38b2ff6107c1ca518952544eff28c0dad6ac11686e0b52d3bfe14d19dc7f285175f100bb628049fdae3c8b5c38e2eb74f082e5b8a7f602939fae83308ce83e538834d6ccdd90a82370fa2d363c92092da40f0aec9ba43840e39b20208b6ce3b3b2c85fc81b75fdd907344c714a11fb8ad747617345bacacf5815dcd7142d0cacab12e3e70d9c672c4d71ce70ad903eb351ed36ba7e0d539f4c085c89df6246bc6581f79e0ba92b4c6ffeee09fc70397632f8f237c33feee77607c253d7f59eabd6f07feeb9259c776d1336eeac08e7e94c92bc7d181c9a8217dc89243a48e9d03eb69b53de6ab1cb88a112a75630efd3c481c980ea6a143bd14325f07072e94c5cbb7a7963eb437f0bb1d2276ec29377039ccf210eba22ddac0b7d84905cb528d1b5fd45841eb03b660031bef3695d69494a66943cb8687f14715a954c0166b281a23d8420dfc27cfd21a375d638b347097cbe30c16961dafb4051ab889f57e916dc7472c26f812b009be04df5b9c81d3699d1cb97ad091276660e3c7d08e4ecba1a1c232335b94814f3104cb8be169993d64e0f3bf57523a65e44c8d818d153a721df7f8a5d362e02fbc6fa2e787ff4718b8dd581bf363aeb460010323fdd9ab2e8259affa055e3fc791fe29dda603bdc0a61ce759db445b74818f286697cf22223c2fc0165c60d5f3f55f799424473133026cb185bd7ba703b7d00293bd3e2ab524e93c27177001016c16700b2cd81657d8c20a5c1649214e6b64ecc8ab021f2ade872ae95597ef2da8c0d5844c9a6e6287669d2da6b0782cd18f5207df2e0168d8420adce654e92687ead4f76d6881f1450368d008038c2f6c6c5dab50852da9b5ba2b49e896b252c1f87914214e56dbac162af8f70ea2dded34e314dcc7be769391ee2f6d669882d3bbf530be76ffe537a314fc872b3974e8890d85c10c523019a1513ffa18a5d368432b5f306314bce70f238698f61af60944b0026cc10c517021ea29aa94be82f7e2eb923023148c4ac88d54c9b2a1558c3003146c0e2ffa68e8ecd4ad9308333ec1859c4672a816ab8f4b4fb067c99385e89ca3943f16fcf142049de073a9495876e421677a083338c146dd9ca3b3bcdcdae02658dfad3cafdb1ca51c3220ccd0049b3da8b78fad337f8c3e1899e0fa23f7f5d2f0cfd821f3c10c4cf0917ff4a81e64be50910dadedc18c4bb0318e5f8790f2e9595882128880049660a375e057828fdb3cca0f7d735a3d94e073fc1373f841baf060c624f81feb54d56ba9ca8292e05be2b6a5d0b93d3a1809b65e926dd6c77972ce418277df4b9334ea94ed0806ce7804a3faa157b9a8684e11301cc1c67c7d9f3f4655f0d0f72ec1091ac107f935da6128fd1b0c1194e0c310810d46b095c3e41a72bdd489768be0e39eb8c792da63f78b2298fc7142fa69b714a74a04eb9126d353b2d010238608f63d4c72d7433031edb4f459e4ca21c710fc59cc69f337678f1853083ec51c3f0a92537fcc174230315e84e6cd31cbbc7971230561fc8d1a5e1c354c0046afa0c687d15f847187801983e0d48327c9b721e7385b13e8400d2f660882d3e84107cfa4f9e348c18c40b0a299f45e2d3a5735bc104108de580920030c10c18d3014108ce5384c9729756ee738e30faca760318478ac3955cc0c3f701fd4f95b4927f70be9c08c3e70e91a3fc8d7ce7f9af381cb96db3f4fea778294010accd80317af5affc54d3d70a61792e469280ffcbbc74b97f5b4a1f557801978603a44dacd3a5feec08606fb20cc427aea583b789bd5815964b60e9cd9a7cc41e68d187350d281dbb7f03089edad6b5f80cc81cb91e3690f3b5e68bb37982107cea7e3dbcf0f5316f5c481f5cbd651bf0807def6530e3fcac1f8a2024683196f4863cea3fb1b295ef56eb0eb4f52fcdaa3dd86d3afb667d61c7b1803aa0933d8c0b45b7fca36cd1af85493e5bb96560397e3d843f4a49039424a1a580f37f64df054f11c040733d0c046ca41736e8cf5964c1b5ae3461827a0fa9c71861966b8196570c20c325c61c61898eec8b5693977e0e10bb88000948430430c6c908e4f2da4d60ef5230cfc779fe75ad1da901e60e003738b96e3ce7f8177d790ff3ae7e0717dbcc0a846c964923b2849bd0b8ca4bc9672f24548c87281c9396aef0e1d4f3ce718c18c2d70a192e4b26c8916b832cf1e5723c618449b91057efbf28a4db2cf0c1138ccc042d100c18c2bf02bf935446ad477fc0180871956e02a276e96d75dccb13ba30aacc7251e86f41b665081498bd4d4c9d1c7671d31cc9802df3b218a6dcce176ca1c33a4c07aee58439218d16aa20fcc88c29bd2653febac6d057da3c6ff51e39d64610614d8eed03943e8bf14cfd8d0221dcc7802ff3f91263d67eb47dd2b6638814bcd7ba995d4d46365435100091080003210813258cc68026bd7f17bb9a498c7ab1ae83398c0a73db1a0aafe3a21924be03b5a658a65db3152c28052a36e7c1829f8e246da608612f8ca557de3fed2a7b124306942eadddeb41379cd4002ef61fa4795dd1f818b99fe3d43aa20297723b099d73c8957a6084c164979427a89c0ee7daa2bad1006cc18021fe78f3c9ad05935f82f04763f689fbeb4e4b97910d8dcf3bfdc1ee8e68e01026b269ac4c6434b1e627ec0059514a953a43e60e26feecfd63bcd34f6803f8ffda5b5e101b795233d8fa357cc931d3041b3b6a4c8b1660f2a1d30ade951b874e480dd9c7254ddd81b27453c0307fc4f08b97b3379d2d167dc80514f49976c397b1c4d6cc0a7768dab67550d6e912285193560573c9ad8d2516a8d05c6173666d0e0e891776ac837bb9805932b273fd2d5d2a770025e94ea42165dc4828f3b47c134e4c891478d304460021194805856182cf8eb4017b0e0aad43cc716cb8fd0dad05a0974f10adeaa231189123df50a812e5cc17a5dc7410cf78eaef118d0452b789fdcae8f13c6835105c68b200c115c75c10a3e4573dcc9b3b76df6484217abe05c5a33574e8ce96523a10b55b096ed11378fc6cb9db74217a960f37f867d88b977f9630a5da082099110e2715316e8e2148c7d5cba91d1c25f2d36b46a7c71ce14a756d6944e5f4c5b044217a56073dcb13da285467b9c0dad1a5f1cf24517a468a2f47bcc61d08e94000d1a35c070000d1a3518d1c528b8b71a9d48b7f1730ede95e942149cffb97794640a9e34a1e073becb21de74b851920dadd3052864d0c52750d08527588fd43e49e87daf8c68a18b4e70973f50afeaaaff461818b8f109f812b453a10b4e70f766a9736bcce7d1bd09ce546fc2724479084b1762ecd475496526f8f8335245e75842a96742e80213ec4bd2dcf1fb6d8c235e820d91ba637c0b56b13d4bb07629a78fbfc3c81cbb956032a68e0ee2875282edb07b739cce49b029b5a9fe8d0409950a421792e02a428a34296a9b46ebb68b48f091799cb226ff21c1d757c6a88defd1e491075d3c82d71c7ddd14c33ed0181a344e055d38827df1c9f50c218de03d2a7bfb18274670e5c96f35e2448ebffe41178b60da4b42fa1c7b14df530423b9a6c6ecede3eaa044f0e19f1f07d11d8860c33fc6d7cb4153e5c72198d8bd4957f36b083e6f6770e90e932e4b21b88db911736dc81c844808a62ec5d1e41dc759a50f829316cd9699a305c19bab59cc61fc02c185104997ec23ebc91840f031b22245d27846857f60a543d5095629fdb4e707d6352ba4ceab9162aefac085a7d01c879eeba38b0f4c0a21cd345bb3073633dd251f0fd203df17dc6ffae3e82ba43c30b6eba7dde6b1544978e02f2ba3f8ff55cc90dd81bfce29bd4f58bef7edc078d041ac6fc92ccb611d8812fe1fe7130d1df8b4cc61d99655299e736072caefe9636d21882d07ce4a5b52c7b68a213f71e0a2e59b8e53f6983a0a1c98a459fc3dfcd0d4a1bd81f397b68c53413f2e8d1bd8f30d8f1952c816dd6e03571aa78376774abb0b1bf81c05cb192a56b09a6c0d8c799b447bdd6c8b143570a36ef13a5a9ed4a14e035fd751a3c71f47b1328906a6536458b96ab546f10c9c5a76f1383cadc4ac3103ab59b26732b5ca6db10cbc7e4b5de85879ac4232f0fafa29a6e9d839da3506a6b72bc7142f3da46e3130c13dacd46d6e18b892e0492ccca3cc493030561f4c62fa0f720ef12fb01fa5476f1e97565aed05ae3c48636d1393fad5053ec7119d2e6a4b86d65ce052e8f873f812629a856c81bb94d3cf42d2de8f7d2d30a5913daf7e2cc97d6681ef186eb16e92fdb58805ae734364a607966cdf2b709e2566ef454c2947d50aec74a89f347558d28ea30a7c8ecb2be71016a96a4205d6fa2f6d9afc49f3fd2930917b93875e52bded4b81d3d7f45aa15214d8fcec93a8e93e6ff28002eba939d4e3bd60f1d427f0a9366597d9a7f37c71021f1ee55033f8d54513b8ffb0786d12834eee74c1043ef6d0b62c77524bb42e96c0fbc53829844a51026ffb799e2de593c046d445318f4cf9554402fb9555af133f47e053e5cd39f3a23caa6b043e5cfbcf319e5f044e3be54797a3b1b411420426738a0ebdd3857f7443603574fa36b7dedff185c06a4adb92e5215f4e5a10388bb09c905679d1a340e0fcc2f3a49af903c6d3c5e90fedfa0e351fb0058a6aa814af44c2c14824108683a1602810060ff036f31308001828280bc502a158389b773f1400034c30243c2a2c20202218101412128e44a15020140887818030281408850281905040240ae2f900f09921112a1907e2d88db5550fd942b3f00358670ef788a714eacdf453802140c52755ef9b5e80d933f0483741eb3501054b78169308e71bde28ab85888b86a8f44cfc0e383e0e4975ffa059983076f7060a0bf701a2a139d1798ae805fbf2cccc1aba89efa4a628e609f94bd92de27be23cf516cbf9e21395b22010a842f5accc106161f93f121f6a7ee29d0189ef89c975078db075e4f5d9bfdc7b303a09851e3481ca088e434e97a30e21720e0c2e6b6cf10fc5caff705a110f43a1bd250d21d3ace5f6ba2081bd8c47c8b6fa61e1c3d2c1b227b72a593604e3e41b7c151ade4434ccb951f16afb126581c09e9ebca1b345472cd46d896f5153f6d17d6f0f8b338dc04a28e03ce458a026144d57623fc31b978650200ac60a26b7f2792accc76a0ca3a844475cf8336f0822138966d3105e6e7bb035c979a6f3e808774c410033148d6472826426cfb8be1e0e3d3c039261c080e09599f41f21411375f00b51f103fa5cb995fbe08aea0157e7b9c2648228488c1babfb6c721757df35bb0d71a8b7cfc50592facdbb589fa6fc4c1f9a328fd44ef4e09ddb6f614f8e5645bb8be585c4ea3f21688f0ad04ba6a60807948d313078a0f28be8aa84b224ac95e1438cf053332a33dcc2f3da648ca0e2b797cba308e3a975979fbd7e8af7e6238ea2cefef6109c586a16ce42101a43836d37d1c34ef491775837f97abbea5c850c9b38278881da447cb42093f029cd88d211f1d5a0a3cdf183480d1a45cdf0e2aa7d45c5fd2932346cb0908c21d04b3d8377681c6623d219dec1f8b3e8e2d76c3fc4399023e48cbbec1e93cfabd61c683ef09d488e2d1799a68eff6194f8de099c2db2540bd33c65b3ebe704e4a9831b346f341718a8295070af4d4ded60387e4a2e309cfe5ff0a10ebd9c1e56cc64dd74bb8faafbbaa0a43b22d1225c50e8388f8505b07c5e630015987565ff90cfe26f7b8c74676bb87a29c9dbface45ed2efdb11e6cc5499940bae2bb8bfb346782199c89684768e478c2e67c8efe760dc77bfb39400dabf0aacd3cabee8624e1e1c515a1d1c1232eb2fec6e3eb11b7c9b0da48920f651c7b935033cf547a0e475fdcc7d887005ebf561cf12a90c5813bab26362cb2fa42203471e68114c299cc64a4cb866bc0049841f7ba4b5584a1abe177af86573023205b5a7c125099bef1c88cff3212d74a2f22c0f90f8f657cde78d155cd94edaf079f304410b7b496e48fe147cd21621886a77c922c20045bd0a5c267089ca31935eef90b64394aed5ea5b180712015a97b711183e030bc1b8423db4845a6bc2b9833afbf179ad3d8a07b92bbecbcbc5d0d4582f59037fffd51e8484f1eb9030cf73aa6b7c29bcca95ab30c0814ec94966483ebc0352262aa8a61b81deeb40fc75afaeba0239de60f821c8d6af3e47dbef68020e82271d5482834169cf26534625cd531efe065006393a5af50800340428ce80319eedf3f262d7539a2971daccb3944527b05ef66f7fe40cf11164b15d102d5d9101589fd434ce4f854ff699fd16087694a0878cfa6e34c732039efabb075c6f5fbbfd79169294a4b9b02397242a06d966ec93a81a54da224d586da588b364e75b2536db0dd8d6ec257e41a405bdcdc76770d307b2a05c473e5161d15e6288070114687b1300c0890d75313938150a3447cf29268741d957085da459841187413c3be30ecdee54180442110b146f129c8964f24ac0af70ee942002dac1aae0995a8bb6d202089a538af3b9357fb31f1348f5c643e332c89ab5a9295de1c821f8e2098ec266feb50adfec971478d8bb6277998f3e60583eca2964479ca73762ff8633ad12f822012d0f908b63cbd5cba0d2af77836234889308c3ea3c54057b90dfcd86813553bf8bb287cf4bb587be10c9c1365b5e1bf6b03989853d014348c975e2c585600781be4d23b8dbe07f77e820846fd0db1c3e34a7c62ca97d2ac4e172a267a8ec44cd7686692c2c3d6fc315027f671aec515ba1bac854923b5f560d42f0aeb72701a89d0edf93dc67dd80d38718389b40ad495691dcf3037832a93aed886c2e09bcc244c5c97a8f06b4838533c970cfe441c8f31a0d0ff13101f8ef592fc8648e9f271c09ad2f6ce8c8c7f98d5b24478a56bf9551a743a5d0ee71e2b926d589d04f60fddec8aad47b5cbf1365b488644f431e2a734acf0e784f43d3a089f26441a5082792541a44e25bd0f4279471d8080ddfee3a9a7dde6312d0e64987a6ba407079acee8beea306022bb1c75dcfb6d2911f0638ceb18107bbe9cbb1d8438aa78edf94812594d30bb0455eb62f6414b97d2b260c30aa506a132fad7090100ea569029e5046676b8994defabbedd9632bde2d4d64479e1a74b848dc2cda416f9975d8405acdb15307cc44251de6df7274eb96c862187e794cf240913169c824dc65fc9269bfc79e1227451fbe48f4a2d25a55ec493c4f436551b33288301775642c9cc7da0d950afc1b341e655bec91aacb263c49bdc5cffc54a1ba6b880ec7fc28180c2115f220108f4d7af541ca82585f3f0888b9634094740c7654bddd4a8299e04e3f50faa245501cbd0539f4842fd4361a1821a14d0fb13916c3fa838b476594d092a1d4a08e501cacf28e92d053c9af0ece1878aa3fc4b88ada1a250cf50438afa95ba160ad5931b19a108f50c85800aada89e54165414aa0515835065f3a176a162500d5085501d5079a878a816548a3fea723712097e2f0a1e8b6c3b509145c5a59c4801cb3b50ff5056a805a831d477a81fd43dd412ea438abae77f42950f350e850115e5a0801e2a88c193f513d19e725050f201d5b7ecfab40955b341f5196f58fd01e5ce46c501dfc06312a80ad43c94144a014a7e42a15abd2a9e28a1bc505b42cd52bda106407da0f650cf3e0ad84913c2f27fa82fccf703522b149e0443c915a83e6d3df439d4e346d15616a251f52d67190a06aa0a550095d241a12a47aa6b2494070a1b2a0ad50e55d0a86ea92c546686eac3c82a5f16945d87ea4384118d395011a1501d008a0f650fa5821287f284f28212d5176d064549a2688b794ab0325075502e2877286528604638eacb6e9c8b484c910dcb51c18831376a3d1477a3fe6501cb34a8542830546745f5a4aaa0925021a8782caaef63d51e2042a17e51658b8d743642e5a1a4a15c5082a1bc503a5072280728010e05bcc723336c549fb565de4e3ecabd9472099ab09119282ad413d41bea086a04ea7b41e9305d3ffe11541dd4918ed26154d98c635009a8fb400169ea0557301549bf4902d194055f889bf767ca6661c819c4df18b283c511ba0a64e4bf061e6c197268dad9917a522f965fb89ec2b22e892e3c7edbc9cf773536bf2468179df34f04febbfa8e802037e634b71c1c0098c2b1c95dd1027514f1baeef9e3555e38c897f1550450a0664b2f6bf5bff158ce362025e8a7f00da0e12b0752842d732a8d2127098d6b4f39e1d25cc0dc6cdf1a5d308a8fc8994f4821982322298d3e72335cbc1c53edfb3a1977600d44d119101f39c4f85923d4ff5aa310ddcac82f59a2b94191294c329640376a0a0587bc9db2d5ae6a84c4b6306245e6edc70024f3f4e8c5042e3beb9f0a21cc1fbd9f119284c83a9a473ad477c4e941322793ebca786adb34ffe32f430ca036b1bbdbc532f264764fc923678655cbe859a442a419c9f176cda0d506dd4bd7126c43c7afc8602defd97fe3e215a171dbd84dc5042bb0b06f18993a7b42093ac129f46c65597f49112acc084a69d8577088b7d7afcab126609b5446d86d607f7162044a52de9844cfa08339aeef433b4dbfb1f6b7bc0aae1ea611781afb56919ee399f2967e9d68c8e5b552ab242b515a3599b7a3d000c1aa665f3d7878e1e0e60f7cd65423f19c4e761c9df2a09479083656cef5a97236de2eeaa50b32099250ff4dd7cc8c51d76392d8f2c9e81823eadf1683053ef96f97158a904a5aa065b1c6bcdefa022fb1f22962919228218641d013a94c0d3e4925c5a9650458a06aa31bf86f9f6e0b3fb35c11b79317f23af4dac32c51c8026b086286ad4ed9bb077dbb40049c977bbff5d03ca0247287005b1f64c8480f7457770ab09ad2835485d545b1fc8cc703c0f1bea9c8eabd8e63f2ec134c9cfa4ba7c42514673cbc2b1c554373e21a700bd7c1df7e5fca80f6ce86305594af2de7ac1fd00deb4be9b15213b47a85062504e9288ec30935840d22bb1fe541c6830d31403698ae4d5af0028807e065f948664ea8e01668ef22951a9d830d466022843ff8fa6d61f49e9b0dba1a29e5b1ee2ea5348c0226ddf5973854f7a8021220d16d4987cb4f1695b30e3ab05b2ffae0cb369abdbfa4e0d5138d07c6807d49c7b677f7de9f3ce24d81f1ed2adcd5e43ecb1de08baaf7e5bc40c5b988169c28069f24bcd40517b28a1c4186916f1039ff963779450403728d1cee135e4eca148a08a642074173831dc2391adee3b1c61605c39bb07d80d888b5105f9811ce3c42ae38514521bf904b2bd40829423c3362d651c0740e6bd4cc6cd4339ae58792acc8eeb3d4fcc90d14880463843cdd17a103c2a8e800c78cb443117025cae3188faf358c84b5d9774ccf623e78f044a085c418443747f9fdc39091e2a255ef3c590014ece641fd7f090e1bcc080275e00c1e78cd08d2ae2b4e9c7558861da392e016491217aa34280d94074a1c73d09c66e303d60195c97b778f64a9b3b7e2833c6b3cb0b3e6067945dabf0a0fd22c0ee089fdd257502424d37ce71d0601d67c0a3ee243367e96e2f059a7893de96de69362ff23606961db191048740567ebd2c0d4387caaaeedec01daff1140bdfcd9f76b608414c0940691d922d888b345e8249de76b24691913d95fcf9a50c5b5389dfa3d85635ff3563a4251704996f6089b666e96e9d2c59c72b0e2c45bbd7498183e2e0c041df4e1f38db0c134fd63d856cc4fb21996f62cbf721c5be34ceb4b4528f37a3c759529055e4fe4f0b1223c9193b84086f989144a133698f55182cb57cb0d0191a3bc28882dc262d8b481f0563d509aa7fbb8d4d6a1e8a02c81faa3be785d4e8ca256d550b52d59404b21aa35f13604554ed716685309820db0005af61c1b8adbe83473b470d59f58493a7ee73dc3d13c39e1e3305e0a6f789f0ef39178b2cfa037fd92b7d71bf26881e72fcd23f012a2851fab64671ae9b74575b0869d2571e745c6ee4241dec4677b1176c88871ef6d2aeb25a89a852e4013af41d94f543466a0651cd933c0dcb30d95bcf069da2a5ea6ad667f7134ecbcf6355e4a5bea2f8db23ba7bece4b668beecf1c95fb1916728d22b1bb5c64450668cd5a96534d6cf7a9ef3802efaa4c01384ef54b60d9f0ea6affab05bf76ab957f0698649696f7b1b59bb1454eb5d428b6ad450d0bc9e29be1af290329c3804d1c31662aaa4a903872e69ff5aac0a030442636a1514c23fa459a4b558634ac29896b6079ba147c320747635cafb648ea32c9a80e392eeccfda8d8b1c40ee6c28c48519346e27a16eb3440ca019a6e5cc91c4e60d26dcc964ba7f206670367834867ea3af2640a0f539598050b2fe3a5dc3c6395fc32ba826737de546996a99c45f0aea7404102f0bc3b599ce817388d9b224fea8baada44aca54122ac9f9fdd3042aebb8d60ae61ddf4a461cfb483a4e83b5e75d471e92c2a260960d9dd353b006c04dde2346c6d246cb656b083ee8c661d22d6e845de9caae76dccc242ae191b806882224811937e35a8a86384fb3142b57cc4573f013821c6d33a267a6b221638a6cef70dcc266532cdd5567e15ee19de3857e9782c56607c71d60155a4728234d5016098fdcb78b33c8355b9e6c880158608319a0f6bd8b657750fe75fbabb50dbf6263920bbfacb788f4b7d6f0b83a2f4a4c607d82af507edf41b7658e4d9d5c2c4704cd711156b6207327434d83af567e9e718d3da22f8d5f1b99b1868f9e76633e9ecf423e2b43ce530eb2622b57d40137d7a9c99e81ff5469c3291b12fede5e2252ebf995992f5e25c8151eace4a339db6d83441bce70ecfd29be5e3fb1e0c33bd3404c8b6f5b5bcfec03a373dc08604d03b11d08ce7bc86a5e20cc26b7e8a48ac0805be415fd7ec6460932c691400940165eef0c5af191d24a5d7a3a0d56eb0bc22203810ac961572991bd522758107813fc12ec082e9b82188fc177ff18f820d034ee2342d73a372673a6ba751fffa38e9da00bc5f4635f56f3968b9a384ec45be220941e5d497f724cecf85bf3c4ac3456af6c14ed0d44f301e4825e8736e1c4c794f79a18bf742a579f616099aca65124ad2d861694904b0a1c681e055064081c93e1dac41b1b7b3a9e6194936130fd4a27e8a8d6838e2dfa983d89ce6f29828bc2a85553a7de5db5aa765119a762ba32f25842d245eb28ac560563100bf5412f40e7961e83ca206303770a280326cc6106fc10be31d18aacacca84fcaccc5d0aafc3c90417846ad0f86192e6edcc957ceb2a1f9ea84230d0eea91e32225b351e5e18362c04ceda81a17c38b69ed74259127102b446cfb05a0e7aab72654f15db2d937e78aa4fc1844247132d1644acd0291d2a09115682ae2f39b6a2fd8bcd8fd8be49a7b0d921fe9c127124fda29f90a82c63f815e96031530bd8856aad1c2125436e14cd3ff5a452ea57152fe84a1915d1125d5dbf5ba7c1b9888990e488f158c4dc909e04599c19998dca4d579ce417c81c13d8eb822ee1a05a0753f9e0bd364c769c2f904c56476f905d79c1378ce7e321eb892f676ef839323572fc6b7f5c36001e613e24bf51edebd8a4f5f6adcba6520d47d01224e0e565cfd2aaa159d468505f08f00144d74f7ce72808df2f165851bd0f3c1c270311ab486585be9e39f21978e6d23ae10d9d6d4b2ba6f984ddc38fd8306e5c54263a588dfd4a3af360333dd8a8b3ae78b16f173994096ecb7ccae42163267f3fa2490f0c7e039adeb351b3402faed72d37d17ef0ba47dcaac44960d2eccff02aa8c223ec6780a77e4342db67c9c0642e7f41062e97cefc0aabb756938de76c617d2224ac98dc1706357eddc18352874206fa64f8d64c1c4741e67c20bbc5ea70b4e866bdb124f15956fd2e30b1dae526daac9c3196d8a2c007617cb2fbff3832e39c9554d54d9bd26bfd7d3c966c16eb4edc2776656a8fb3fc825f74a60ba82adad5d8165521218e0b9516a9e88077f1061180300f247a1c29ea48ac4d39fda75c842add935a056335db551de049856c8fdddbb1213551e0c567e23555fec0676b07a2abf022dc26dc44afc1c1128c4887f0496a153f46e5574e59d26e14108996b83f5865b14b647809a3041698c4abf018e72d96f2f33bb80b7a977851d2c3bda3578893cd103d305c9bb9e70f5de0f4b1dfe081c16873a48d3bdae2407a2e9492acae0d3a2bc48327719639385121536cfa9d76622645aefbc637c0fbf9391d5f72e0e336383b19669b143388800d72587697ba2f17c9e6dccc39ca0aefe8c1090ec586ad542b65a15629fea8a8423e9d9513b6414589890de9c4a3d29cd83993976b8980c69719ae97ce944ba2fe95b444a3b85516b2a08216fe5b0ee6f692b91259680722f33c8b11e14d3e84f802653daa840fcca2108c821812ab34be58f5420a2f12d814ba63c70b5822a23b7c19044eb9f311a315bfe4c9c51c1c7334850d2f3157a25af2ae2128c2b7c8d933de988dd630bb60df74509db3089e0887068377b67dfa85298e2cbefbc22c308063edb206d9b210cccac4bb03e51f5bb500093d54e5f2220e1d390a8aea65cfc365b2cd43edf791d7cdebacc3421334d8629a26e30e743604002cb3bede0e6e1cbedce0e4ed5ede97f2b666bf775acb1d43d68725078e33de752095f7eb66604b816693ebc2af7d2de12be0cc9007d524d2d08396cfd4b0096fb80135463dcdaccefd5922f159b16a2364180b3ccddc8d8e2daf5e6fd3284d81ff5270e87b56fbac1d49230ede8849bb9d85de5cc8c1a675ed6fc4e20a95c8c5e5ad325ae1d8cdd5d5db5bb3a3be28d74bed6052b58e7c102bf9cc0c9cc24bb77fc30adcaf7a89e29c99fd8890c959cd20416c38d439466c40c7beed742b6e5f3503eed53e58b6c974dd9d0e7a520f404edf6fe5bb7e87b90aa4f39111d8692e861b94c3625185e16206319124dc19439957853134723304b0853511efa253900045dc204d161b9ac5112f2e1a025ce43e79150dbe3b906f1d6cbdaeb3eb9bfa25b4513c7f2d08d4d2d49a4bbc7afbf23fa1440232b051b79ccbfa0087dd735ee7b564fbe509d8273daf2d785d3de40e290a4ea0e4dc64335f711c581da9fe150309e882134bef4e84b7c385472510b69e143adbddb71088a4a985ead1334dd10a0b617581c740a4fff8b7bf0d206840ea845cd3db41bd11381971bb92cf33a2b6721b830283028be04a256fee9aa1dbb705b455aa0e29cc28a925055fea4ce7a921a27ed9e89b986d8a87ee6667fc986f74a18a7cd4c8bcacf036bb467b69a9b95157de489a39a36aedbcb13f1c47e79fd5727bdb467467620b10d5bfb2a74093045d5003d24c2ac1a386bd7bb696043d4071d861b301a6d5f7d341bd399bbe0c9628006669a5fe8a88c2a8ae125c2760bf2b1bf053757fb3471e9a53b8aacf0a6856b04d83c56ac72a97d505f58cb94f651efa628acde59922817b05eaa5c1ee4e41d05788b8cbc83ef289d4b516b34ea5a4b86035ad636ea9ad3468eb3c9a2aeddeb1fe8429e13ae86dd6b910b052a21e06064647cde5ad21b16a4b910fdf7b24daadebda6a38d844a5d49883e0d4491ce1d3ade78483d5c5fe310f2dfff702a09cf27d9b9edf7f5df788dd718811de72a2139e67d710e2ac5aa4466f81e272e2214735e76fe336be0bf005d43b20a5af348067fd7457e82d2da71b57675c11a8eaea8a95976daa16ea570549fbae8142dc9e78a5b4c5e5706d7a4b9bb8f22941d2524870ad185a54e2d337ba7277de66c9797e0aba0e54159ea58935257551971f33db85bd40a5acd951c16ac01500259041fd28428f2e0576e6019fdfd46db72cae7aca16ee7b1bf42148770a3fb404df9a525dab3cd5f65c2003d3734bf967d8c533cd13039dcdea42ef29c6271e9a7ad6556abf5df22f6c1753f41016e13a15f9e4a6c3af1c6ac348633341d4a83342866c5c43463e9d32d3a8a39e252e36ce282f07ecc7718b005c0f1dfbf733fe46dbaaf339f6e9f7f0b57e123b36e8cac0aeceb36be8905628cb1d048fffa08023772c53fb57f9a1168f2b8c90fa355af82e3ecaf37e526310315d8ad11d38c98cee18946266f121b59f04a296d6cabef75f052891040bb765a2ae09ce4db2c254e34e5cb045d1fd9b78973f4702c9b7fad20e866b2260b9a26694b2d4785ee5c64d66af334b918d477c833450c63b711b02669ec5d0a90bf381d03d1ba6184be992d9115d788c2e4323dcf46843dbc7ff240acedff9b6eeae1ba6a7cb49e05dd4a971e92e12a2377f43ddd441b4444d41e7e81b1e8001af548033fcae65f496133900c46310098a907d20b02606b900361cf44f3c212ee2adabd45b13f8fa28c58331e03c1db4ec3c976978caad29365d3978441d9739fd992aa83cab26b0bc2a8c61c27af0322ac759358e38f371b6a3b153a3341321c0cecc74c10d8bd85314f7bd6717def79ee2803cd4f87a71f404acbc9c55036ff0942e06b8e3759dd6b0d3f5bd6522d4d8da800df0548923ede5b5ec99abcfa151d0435946b7beda9119cd1694253402c95388ce76ac04dc03be34e9b3997aa7ad1c7219420c124048283e54565c3a84105fc10e510e76e89e5a23577cd09ff2bf9f1fccaef671620ed5595a12a5341f02bc74ff81115c3887b42fd7d37aefb85bcf71a7b2c541fa8fa0eec0256e15029e48fe655ef45e472f532f6d2f9b97ecf45a7d34708bac97c40bee95eed5eb95e155c7573358d22b2ffb3a62f0bac4787fe596f64316242fbfbe904dd287c48bb7bc9e48c8024c585ea57f295e583d5960c0bde45ea2fe757d5eb750d978f17a69f0cb06e6f33a4e359758bdae54d7e8ae6fd74ed7bceba1eba0ebefb5eb35f37af5fab191fcd3875b82ed2f5f0f434e605f09053071c4abea85e015f6eaeed5f3cafdaf459010471e7b55d6ebab280fdc93f35a7871f7ea7a6578c5bd62f03a3ea82294201829cd979ff00c36acb5b5a610d47657433dafca5117237afd27d5a1494d0cb0d084d9388c0cf97d142b38d9d7815921e654a40201a32111213361f3ddd6106a582c69643c224152e61f3a16a33cd2504e1376c61e4e6a5c4650d9fb4c790fb9d0d4c1e2a39d77a2fa9d474286bb21e512e80140ae2be932d3a9f29501eaeaeb53b5563b84858d3d13e37c8c23594921214ab71f54a32a9088d328e4b12d58e2f0603dc4944d72d6c2292ced07bfe5e291c6935af035af40414cb358aadb74d94e51470f5382870eb47ee039008850a039b0a221d2224dc5b3c79ede48e4fead480beeca0821fa36da8e0153061e2e39904270e7e32593784043a0cb892218a886ef378e25cbc13285210f0a76bee41e45fbf85728d7477cf96ba8c2ce821eb0c3db28ac3a67f558bf7e2ac241425bf41b8c041c3553ee9a6a17ab085d94db157c3eb3d61c4190fad8f018970b62f7084a8c2ff0becfd60fc712209d58d05fdd95406d1aef1e6ff05d42c5384961a7f0cc6842c8d5d90297459ba564d842f0c1d6b0e7db454d6e3b91bd05114bd44518bd7e3f83c349658b2139de56f3bcc0a03520ed2dda2fa8219c505c8e02fe4ec40f0b3550b4960efcffffffffffffff8fd244686bedff563225a984175624255793924c32a5943aed1600450100009a888848180500002780ee0a7c0bbb0a07952ebefa0c5018c6fc2c7288a47d24e713e6bf101ad224ba4aba6778c2bca1cf436e9f2472e3199d30556c245d22e7e528a5199c30684cb2ec164b9bc082b0feabe81d23a509534e0b1ec92a4c940e65c27449e80829b3f3bd7d3061be5c26928e70a2bdeb4b982bade53b28cb11f96209c3f778e8ec22633f2f95b86eb4e895f38a2c3e254cba25264b126acd72741286243a4d5aca398c562a09e35c760f29ea72108d44c2e45712b7d2886a441312b57ad039f67f8449f7f9bfefe785d11c61f6643a45ff32935761234c419efd45d0ad34f718610a39a8e7ac8724b26e8b28a75ab94a2a4d58127a1598b1206628c2904df4fc5d2e37a1548b989108d39e254faa544e7ad3b7e8a2025ba8610622cc29850959763c5dba7f08832ef9233574c695d78885198630270f5ef313de3eb582021446050a61d212275ea3f38430fbc8b44e42658dea7e10e633bd90eb30ffdcba200c9e4be94fcb17653c0f8451c6d284cba7ce697400619030f9d54ca28810ed1f0c22a7d8b1579496e9143f98f542b4f0d92bc2454a1fcc9611dcad52c9075349fa35ddd2b7a6e11e0c663177eaab54b8d1e9c11462a91c22774e154bcb8321dc86e9909784387f061e4cef7b174c43be9eb466dcc1246b64cfb4cd5c343bd4c1ec4987ce709f17357a3a984b07912ee4bf9130630ee689d7394d882d1ea4450e267df226695b13a9921e0793cf8555ddcf491d321c0c97794af699c829def9064394d39e16424eafe4b9c110b4b958325377f95b1bcc9a122b958e9e22478b0da6ecca6d1d6b1755d31a0cd92c64980ea3553c480da67fb7ef90334d8339ffb35a258f063b72c6878c75786aa8bca77dad4b19aa2fcc3883312d2bab2ec269d86630cf2725f227e14124a595c1ec77a7f51f1f31b00833c8605222e2f15bfd74fc0a02a40933c6606aefce2b7fd93126470ca6d8316b2ee71022231d06d358e88c902e7b059dc160d2c8a1a7cfddc4534ad98c2f18cebf3a25881ed94b99e1059316f5ef41ef235130a30ba64ea955f17b2a9a0ab7a0c70f2ec10c2e1854e6d3fd75cd2c34c68f910211ccd882b984b2f3f3942be94976b045c001ae057329adca12945bd26e65c1a836e1b2a9caa126332bccc082298d5df0b49f3324bf8fc008e302f8841957308ca857a5decb5e26335b7451812d9c30c30a86914f2252c566d5e955985105932c496d13c2c5ed65a9608ad9a59fe46bfa8fa6600e13f11371948db6550aa6b0f5d6637b1db4e8a430230a86c912cb83fc7c3b13858241ca8a7fc9ce130c4256fa9495f6bac3c409a67029eb420aa6f56354c1516146138c9db2994e4e4992c9fe023972f8c891e308339860c8df733e495c4a4a74662cc17cdaec57bba35b495d09a6be4966d516afeee224985f662c9b0a2a2539d00022cc4082b97d5bc75468398f715b30e30826d3fd3699243d5cc8463007917408490573efce1521cf391bbd55393388605022044b2a3e580c15ae4adce5dc5be840982104f3ecc55276a75a2c3d627ce143023d1e6704c1902e9577c408ce0082ad8f193f30c76d8d4574ad90b3b383d50739a3076a089193fe6f153167f0c0244bd769b7e841e488ed2836630796e3ccd08129aca51aeb10c91529099400da302307a64e7221b87b7baedb0f10401a66e0c0f8e797db66935364c34860f0203edec79a99999995f116255ce00542a043005998710383e8549e7a26b7a318e9c20c1b18bf92e5348fa72e6dbea3980e1d0ec618611cc42dcca881f94fb995d84a1d3d767614e32bcca08161df6f3d359450eacdc2f0b991fd57843e1141bf03f9b07b820c59984a07f79494489df4997990110be355d5856c936c6fbd0e3bc88085717f2b7d5a8d14c9f27890f10aa37508a53a52487eea7785b9a28f7ed32d73ead356984e75b9abcca5d3215bab830c561854f5e4e824a4d59a671506e139825095de9429b9b241862a4c77ff2521a86b90910ac3896059e44f19a83029d9dbfe6d3a8597ad09324e619ab0a3152bc92a47bea3d8064286294cb9997161cadf518c05fdc58f87801f649482b59c1e6535e2290521831486a05f1d82451a05280c2f1a308a93210acbb10119a130a90b5f227f56324061169d23bf569788adac8c4f18c4aaad27adddc4ec3c61903f227db4d277c2d4934d522a617a3e869c30afeac7c4393f57372917646cc220e74b3d8a34a5e6a46490a109e3f929938c4c98b3be2809967a21df840993fa90ebc4227f09d36976d0212e76788f5bc2fcb7ed6e314dc4e5bc1226711bd33a1bf193862861ca6e155c26e5204ae84cc2f4a6f7b742afa528ae24cc3329755c622512a6ce56695f49a9244290308a4ee6eab1a1645ff211060fea64a8ceca3a2f3ac2a49d9342c3c4eae9af1126db0de5f5723129ca820783870e30c6e891c60845cff9c92ad1e349b7087389cd8d52418a30443129495ccf62443d11b74cbcc82a22c264b1838e2a914f729e439893d2de1369ee3fbf6d084554f6db499df2bf15c2243a82d0272b494f112684f144149114a2b395c783782eafe43ecce589bb208c9a22d47ef98e16cb06e26c0fe95395c5f29f01619ea836a3b2493995a47f309d7f0e99f171754f2de4c090e10793ce7f4fb9d3aae747faf0966ac907e3c812c1828438da4ab8874cf586eeb6a597d301401864e8c1a8f5f95f31bd3a564a461e0c579efffeea3d6d3b34840c3c7817a2ee471b9128c8b883393d257dad32b28331ddf2c949e1a9f4937530c567ac89a91711d672820c3a983f748d4affff59d7e760ca16792dd17ebe442e820c3998de3d09693babfe6924828c38983cea8965d3a39492131ccc2f61f34ea4e81464bcc194e35b6587a7a120c30de649e146450f11cdc2db0643aa3acbd53c612713196c30883da563b53a4537a58c359844a55ba897d88ae96040861a8c791ffb495c5f13c8488331920e559f6a925e48b240061aea0ca61a95a12e6e266e467614bb53810c33c82883f9e3cb3d66891d990f3298477ef63be4e4c9d60146a3f342c6184c6efe96a52a042d272c3b810c31982c89146daebdb4691f06838767ad501b1f4fc7c160d6aca09e92e4d037d117ccd9245a8c29d911f9f18251a47afa6826b2c876bb80fc8a7e623b234f8805efaf82274290c1056367b5512357d98271f482ee5e09ee214f2d184450bff2a02524d15f164c332744fcdcfb7515f3d791170032c8c082d1b35a2f82beaa282741c8b882b1bf624b82a7ad60522a29d7f10839df2e80905105a3df5fa6471369cf6e2a18f48956580fdf7fab4cc1d879d1a249c873deaa140ce14408fa4d5a3651228f414614cc51e742cb8fbeb02414830c28983da8242e8ef6f8d53fc1106dc77554fb63e5a413cc71f5b2655f7e665b4e46130c666ac95ac47f56d4ee0b3298607a4f6f256644fc45d5838c2518d3a28474ba576fd9db518c142ac850026b9756d91c6424c1d46db3b726f2fdff87e163f840825146deeb888c25611339c83882c17afca48a900ff77847311de5b6e80202a50a328c604e59a4bb575a2720a30886d349268cfcd85da62283080621a4c767c74eb5ff640cc19454467a11625a257238c81082295aaaf0a5724f1201238c4d25c8088239697908c93dba886002c2e155d46bb5a3301364fcc0b87d2392a9ef0373575ffbabe47b60086147e957d3da31f1c02052ce91f9f18452bbefc07c31febd3be9ec6c1da08c1c982d44ddfc1454d02f3b0e38ad23af223cb332c8b8816955ef445c5de8f021193668dc638e2e99146114c8a881c143f4881a39946ecf91410353d4b88fe13e725c7266613908200ba3e8a8619636367dee58687a9617b5a355ac60613a25528a96937d4596f276e5074f35bb17047085a93ee47c7cd1cb296a2bcc37fa6f69e2756889b0c2249452b66fb59dfd5401f888c8d82b1761ac3e9b53f34989b274b0220c22c3f74df78fc9a8b4502311e6b417ec420a9fd4ef05c7a006224ca3fd94278b9d0f427e0883ba2076ed3f88b21919c2103554b08b135408d3cfa7f96ac8e4f59a1086bc9fb7524ee9c5393cc0d0c13ca8310893deae4e2a6f9d14f1e60d6a08c2dc97e4841472a4d4cf0261ae3eeddef7e8d9a3dc518c4715a00620cc622acc83ae8c89ef1016d45e20043ac2a8f10743ba1f6517e2250135fc603c5553e9d9fb7a06a3bd10a3461f8c218432934bddec0fe2834154f04e314412d5d883f1f35b7cebf1caea143d98ef5d24fcfee8ececd6c883612fbaa6c5798050030f264b379ec467a7eb5cba8341279f9d532ab283d942dc3d95b3454eb4eb608a34e1ef7c3627d4a083496edb7eca50e945f33998eb4b3dc7ac4e162a480e46b94949b684202fefe522470e5e428d388851030ee40d667950a33e78e4f88ec205670d37580e28d468031c6ab0c1a4b753ca1b232db5446bacc160972b27f9fbbde425359846286d7b35f3be73d74883f9d47699a67d8e27760d3498b443d9859055d27ce9891a673027ed49e2c9aa7562690230786ca286198c9d8277dd960a1a1372c50c35ca609cbb1029be4bd0913b6430cb7d9a112acebcc5c9188c6379c2849e37e159bcb3167c8fdfe1a32320841a62309c540fef27bcc24cb0cc0735c28035c090cec183fb280315d8c2870f2ec0e0d13bb8c891830ca00e35be7035bc7035ba60f8ceb44979173c24ddfb480bd4e082492f8f5a895235e9845b30c9a874fa7d22d7a87b47311d7a5e430b865341c533d5f272ad4616ae06164cad5721628989e920845e0c0e74c1811a5730c9a57c585887df980435acd004d9166c94bdffc5183ece183e120435aa60922b7f317258ed944f3b8ad5a0c2558d291852965b4e39922a3785f1430a4631cb156163ee28f63daa8b1a5130c92ed1d92d6a362ca806142cc7096a3cc114bec2758c0c6b38c12c5e9147f69bce39773972d46882f9646aebe9af06134cf56ffd29a9c77e516309a90e23b3cc54ac7021881a4a30a71826652e9f04838a699391d995f6c248307e72b1ce32912398f44776bb13353722b92ca86104639d98cda9bc4ef15c1f358a604e53b7470d22d8a0c610744f115591cd3f9511aceb410d2198c32595b0d17e22bffb1a41307dcc3395921001c12cf27a55b3abbff2118617a8fca8f103734cf12f8f23eb572b3f787cf145f7f04c410d1f9873991a214ba9f0dbad460f0c132e4e3059ea1dc5c058418be18380518307e6cdf1f211324ad777ca50506307c68b20aaa2e5ea617ca13a30669d25536d5af2053f07e71d954f2786d04e7b000f06357060f616d513b742f55f3d7ec70a8c0b2bfbf1888b698d1b98fe474cf23d2196b49ff0b6e802026060a00318d8828b2d5a707fa86103f39e88b92182fa9492b6460d4c26e43ad79d58cf95a7a1060d30094a6bfe4cc7d3de2c4cda75f9a38c3aed98cb82ec7b221742b2cd9dc4c218faf54a6e7a65f7b0285e5f5d0e39396aaf30a78dfba06919713cb4a3d8ffe061f65e1c7685f13f9eac74da56184b9c3875de2e2b4cb5191f3a74642b25ab30249147c5b4aed5734d150691b4ca278a892c1105fd238f025b70b18551608b2e2ab0452a94f5cb29ad9e122a2ca1aae475f8f8e914344c411229548594ff769702a97595d529c92f448a3246e4e2e42c5eebb6838b62b405de8b3346180e5013a3bf280b028d5198ab948cca09db4b51270d511833764be7740421725c878f2ef3c18031d00885793be48d24c6a225edbc400314e66b0bbbc9d1b47e2d9f30c5ef9e4be5b227cc61ed762e494c132ee98469ee74b0dca3f75674dde3bff81d6218829c309ef5be9e9c8aed0542a063013436618adbbfa374d71f1260807be1457fc1811c399086260c336123898512d626df514cc70e1f638851c63e3813c6b8b42492aeadd221a28109554c55cc11ff5cc2dc2631c7929e2d61ce9f2bd577a5e4f6d14a9873f88b203a54f29b9c0f461a9430ee888a1e3ea9ed0fe28e62761aa03109532ec9114eaf7848214712e612b36ff731498697ce3c402312c6b7aff092cf000d48184649cf33217e362d7f8f16e37b88c1f60502c19bc2008d4798b2897c0b49aae8d0e68e6261fcb8e38286234c62f5cce3575a2973eb0718af8223d0688439cef6eeb34fa0c10883f87593a4848a9fa4640230782813682cc258f9934eae2145182f8894749df4f81dfd7b64056824c26c57a9d357e8ab35bd75d1c50a7a03762be81e14a08108431031bb9dd4c3c28e6f001a8730984891e294eea4218c122c7465cc5d0843cc0f296715df9209610ef35293a6dc4a94350883777b2eede192f789056136a52a25f159e64904d7023402610afa2c841915574b9d0e307ec70f1d470310663f1d8f263967ab442be81e681ca0f107d228e1da223d44579f861f0c7226c90b6741a705a1153c18671ba0d107e39e1291ba9722bf4568f0c11475fe47785ffc0f1e7c011a7b30c4f5146b54a4761443c1bf8f94000d3d988350e174d2ed546005bd01bd8d008d3c184c5e870ffa5e94f6941dc5ec79f4f881001a7830cb6aa932bd9055b11734ee60ca93c22893763e7192ec0634ec604892548f4af5d4c17826d285b4162ac9f014d2a083c14a489f58722f33a03107e3a57411e344efeb7a3e78a80c68c8c178f7a16de2b2859d380e86916e3d5b3797f4973fde8b770207534a419987509e3798d3ba259adcca8e626e306b9ca9dc0db551c280461b4ca1d4a40a2394ee20ec1dc574f8e0c10673127ea51e84a5f9da5e40630d86933d77d93a440d262dd321b12d7a2a95a5c1acf12d3a7a94a06c1de7021a68308a29e9a3a1f49de5eb0ce6161d32717499c5d86630fc8744310b2a8329a5fa75ca7dade7bfa3981d19cc6bf6124455e4945eae63018d31182cdf45b2dafa90af8086184c265a3452dafa8e14de512c0c86999faf13594e29a98006180c6e962d699f51a595b3a3d8178ca3a425e5397414d7d30b26ddb19f3ea7d30563e9cedb713bce78fa8306174c614d6f48337947b11f626cc198a1bcdcc458af755a305a7819f1902f29ea3c880f1a5930c5291d5f112d95dec2a08105d3dd85eaa85f1e2abc3b8a15d228c4d77ab8d5296dd10504787420478e1c39ca0f30788cd183d0b08271d427c80ed24445f61dc55ac082307a88a1c3070f4d008d2a984d7b4e17c4f3455fa4070f3158f03a7cf028fbe1238c0888f1850f09acd2a08221a86c7c5d4cd29d2c0f2fce1819c89123478e1c39bac718cd31a03105a3218592baca3857934ba6690b6844810614cc73ca83889e633d7e88a1071a4f30c5a7f09e4766a49bd5acc70f319240c309468b23413bdb664544a30957021a4c304ab8244dd7538628a52598d3cf9c3a1d272518c2f775cbc871b93d09e619f56caa3c9e9cc79160f4585a291e42aaeef8118ca19f333f353b98c48d606a0f9f2d5df82e139e2218acb72475ba1313c44304c387bc9098273376e62198c7da434eb2af108c9b113f4bca8b1552120473751c0922878e3e4b02c1b8ee419dc67f4a9ed74180c60f4c97699b319e5d92b45f748fce80bf0bd2746082860f4c425c2cbb7871268998011a3d30ebdb6edc5b38ad7cda3ad0e0817136d44484c47760ceaee0593a553cad711768e8c09421667776cae53a2ed0c881b1e47c7a50290807e6244d8891fda48447710d346e6012295a874b3f1a39f343030d1b9873f2bcb1e76e226355038d1a983ec44389eb8ce5f2e838030d1a98c6d52b68c5c7519985496fa992bd33c9cdfe1dc57490dba20b08e4c8a1c3be18a3c70f1ea747ef0f66c8c2eca95259ec09d7b5be61462c0ceaec82ca51be53c9122c0ca9a359aeecf615263d3ae7641699e10a5338b91e94b4fc39967a6398d10ab387e42f59794d6ebc628861062b4ca22a6f5558578719ab3047ef08693fba88eb4915e6f098d827332c85f8a7c2179deb41626551619c20947d1655eba382a73069c5d3cbfa1d7221878619a630df7de4f5e89da6e62f85b1457830252a9a410a939e98f216743aa57e54c08c5198a4ff55eefcd5191187c00c5118f489146c4525850acdcc08857152a7cb03d87848dd5084c1d4bed39ad5fe5dea003712619022dfeb39f7a29e0e2288f17eb2a3391f220d370c71370a6152b6963df25486557a8310893c4a5dd4db73a87b1077c74cd0492e858b9db31b82387d8ea3a1ce3f271085a0ffbfb4ca8e5c6bfc821b80305d70ef9332d1bbf107c38551f9399ddc6cd737fc601249dc2385931fa5721c6ef4c19cb5845ddbca5ad02ad170830fa6bbabca75f71e2d7258f03a4270630fc6cdeb18f97c4e4f8a0ce2861e0ce26c75c64cab76b8910773092562a9eedc0d3c98c62c5d98a03b98e22e82c4c83999ac2c256ed8c1784125eae7971c16ca91a3053f78dca883d1e6424e3d11743b3ae8601266b2fb37d58205ad0e2fba78e2c61c4c9526aafd9a9646563571430ea68fa415ee9293891b7130ea7e7d5eb9e8ca1fdc514cc70f4579030ee68d1b89f92b890002e37b7cfa0db8173c767091e30de6fbca3942fe5ae4d10d06332daa237a2495376e8339c47e1091b278edbd6c306cbba8ad104ceecf9923479d2f96e7934e23323518440479ea7da2d7cb4b8321a57ca54a10125204a1c1ac5afd611e45ac6ee70c668b54d973054bea153318ad62a9db0b9dfd2096a1dc2083314cb5c76d2da915b4311847e86d89fd313c4d8e188ca6d54c74d2a630182ca9fba0746530987238fbbc2a7a183e868f2f98e33a82f6b2f017a2a9b0e086170c5934db842ab1b513b4821b5d30cb65b3cadd562f3632146e70c1a461daabddf2538a849b70630b0699d7a9ff2fc43a09ddd08249472e5d1bcdb360527295db277d86cc08164c162282d8eb3791f562c28d2b98fb26655ee9586a5fb682a95cfbfc576ff48b6a0837aa60d01b5ba365d486572e841b543066e84ada778486e7730a7a7a1b75123f47f0bef8626570430a26f9701d2db6e48b1b5130fd86de5f0aa5f4b3fc0105a3249324dd84964e49995edc78827145cfa5906f928e2ab913dc7042e16d2529e34da2a570de8d2698921addadf78f6821627ce183078f1b4c30a53a61e2a2e5307a4f17b8b1046348efb990b57430783c20478eee31468fe18612cc9d1ebb91bc6a7c824bb89104d38d4e397584286d3a3ee006124c2525c889113fe7a31471e308c68b6b6ad6bf534e7582c0165c6cf1002eb670001741b86104531e7d9d3dfea90846eba03be92d7141ea89088678b5e63ed17205390dc19083474929b4625a9684607e53fa0f82e12e59d8868c9b1d3d100cc2c4dbf4e7cad0a3ffc02c6ee92c2971595d473e305648a972d015fa83a40766adffa09f638707e6ecd26542dbb885d8d98149e4523fa555ebc0f4eb952de693a689e4c07839f589b8d269722d0e0c3a7ab7c949d13ffd3730caeacedcf89825d7060695843e29314f98ac941a1874e224d39d6d77f31b343064b990d74a3d0b4392bc21de6bf2f249646110a127f9439ab130b66ee996b01216c688934e6a64ebbf13bec290e49ffce5171325e20ae3a91869c47a6475bc5698948ea31d4d975ad05961aa9cdade4f7ed033afc2acda93356425fbe09a2a4c41fd3523c911e36d710e6ca4c2e09f1fa2588e51413b1ba830e934f3419dca6e6b7f0ad38ebe0755e17fe745a6308c0e1e44658ce7b44f290c36417f3b6ff9c94993c25412644d774bb5747814669138e24f3c9f9cbd88c29022c94f9fc6df535c42611e195a5214113d0955018549f6bf25a9d871adf513e6f9960b1f21ffc68478c2ac17e2e385bc13a6781db9f63a934214396114c9a96672acd349b40983feba3e19ad8e319f26cc93ade2d4c7d8337d264ce2b2cb93c92451524c184b75c82792df2925b9843184ead361baaa26344be477e56e41df5a306c54c294dd39edd53f7b3639258c9d636a880f3993308bcda449e9945adc5a126693da41eb6625ffc79130c491682a63e564bd8384e1af5f54fc9ad81fb247182d82a754313752a81d61ae9bf71349f6a24a1a61ea54b9ba3c568c30895e2bdd95d3e5ca44cedf0962b9224c355ed971ff44185e3b9bce0b7ba22e8408a39690d3be765a41720e61d0db7b4f153ae824b321cc25e172bed94b622c5208734ed53d1659dc4c8e8430e5cf49241df753a8920dc294e5f373bcbc204c1a7fe5d14ebadc57e5d8088439dfd2c412def1767340182b5e04ed346bb9e2f307d34af615d121eb07a37fc892e556ea7bea8359f3440e6a79ead353f860c8174ab9c6686cd8ba07b38e6b69cfcff4601ca53ac5f49ff5cf55096ce4c1a424858bb514240953c28349dedc47111ac2ee4f7730e5f1f51ecb67a6eb6407531095424877ca33d6af83415fd0fe511d2727f3e960d2fcd9f0d31af23c3e0773d0f221e647895a429783c9eb538a799ba727ec3818574798c8080ec61e9172e48be5a6f11b4cebdd914bdaa53e0bbbc1aca55df539314ca96d307f12aa63a667d3125c36983d9575b0ccd7caa73598c24daed429531b6a3088f7ec273676726b62230da66d9f1137bafe228460b081066304bd7ccf12225260f0e81fdfa31460e30ca6cc3a117a9f7545ec66307908123d825cf58dda46194c42e78e997cd195da7fc006190c92278c30359d7c373406c30815a6d24b6428f188c1dce7419fb212d7a72d61305a74f138f7e6351eda0083392d56b6483127b8e57cc172241b5e3056b65373661782cebc0b660dbff4147acfa45cb8607acb9b22447a5335cbb6c0c616b4870d2da4c04616dcbd76142b89021b58c034818d2b9c0b6c58c1ef17d15f2e32a236aa605bb968856c51267c2ed1be3cc266dc518c370936a860dc9cf9d891f8567ad9988271dceab7d46d45d0d99082d9e382b234ef946b7e144cbe9bff9e153c7d90a160343df931d67b529a888d2718c56e5d52ee9f6d38c17059e4afa8b4ada304011b4d30bc05919f22e4bfe4ba146c30c1ec95aeedd479de8efc128c22ab6ea699162aab04e3a81111d1266586ca9360f0ec5174320b21c1e4da3d1a39b64730a9ac90338229a51c672ac9b977dff609368a605a917eca44a629370f114ce6316b6355ea97d2219893ee28cf7ba5e4a7f98347186c08c1941255947af4bc2a8bd9186c04c11ce7e2af07194b29e874e0477bf1801c39c0f81f3fda8b6445b0010443b69d1b29f2d92e4e7ef8f80bf4f81f1238828d1f98e279b6cb6ffe2167c9072635cf5f76bb7b31fa1e982d94ee244df23c3068ee5fca323a2129f90e0ce94bca08f5614674ae03b3f6db955af654ab790ecc65f2e3ad2fdd5cfcd8c081b9fa43f47412d674d0b1710383c5b55f1f117490efb16103a3991a73155dea53756cd4c0b4a32be59985296ddb060dcce739f5bbc8b6a092781626afcc4f4152b4d83ecbc2985da225d4568ee5702ccc16b2748d5f0ef50ec3c29072ed7d56bfaf30076d317ebd76ab23eb0a83a7cca56d9589d5a51506b97f613b6e9bff565861f21c954c6a7ffe9cb40ab388b7554949aa30c911172dbaa4bc2e4a85a9d25534691e4585f9fc5d5faed35398fe74f2abf0a5a93b3185297f765bdfc931ea91521877c4f38f7f85e93b4961d027a47765f554e5e2284c61cb2d85a43aea85280a43f69f0f1ef31feb5e288c1aaf62da65e47e3c5098d24f9454eb7fc2dc292bc747daeb90354f98c7c4a8ca703b6192992694dcb1516272c2a083589bff1cb49fd036616cbb1f9d2e084df33461901e523031aaca84b9acee847b9860c22052478e944f41dbe75cc2b8f641e6e59e58c22084eacb2397247f082b6148e7663f67f24a9b588312e6a02babbd4915a1d2aa3109f368891f29c55cd3bb15491894fabb7d33257a44549050a31e9dd2a5841a9030f957d210a34ce7609247185f8210d95d3e47986d845fbf986d9fb68e50a31126b59795c3fcc40d2d31c2b40701688316823089aa7b1889332256301086491e3ea494a262b9260138680108a3a8a535d96ad1e20f66d94f6fbbbc1d4a9b8c02b4f083d95594bebaec6471f25af4c1f4612f1e4f9ef860bed131a9f752418b3d98ce6f84ca3a13c554a40b2df490d021b6c6e7b46ed10504b468910793bccff0ced1a1051e4c1f4a35f63a678c2f74a4167730e9db09db213b2dec60d8b04f5f1b394855b70ea6d263ded94d54e794d7694107457c8aa5175accc1903be8a0a3c42b07f347ffff91a4ea938671300795efa6c289e820d4020ec6ac1acdcb0929879dde60b80941944e22cfb8654fa0851b0c1bdaf4c751bf207f4fad3cb4688341c6cd4a98e441a5f4f81f603c1b4cefda1661375fff3c498b359c745ee7b42e5944490db4508349089315f98d0039d7220de6ad9cecc2cf2fff8b98a0051a0ca944bbc7cc5027771a81166730abaca6fddfd6fd6a5a98418b3298e3ad849f0bed4f4fa9eb8216643048f518d7e172ccd2e351d0238c158c81cb08e39245265b8b65ccba8f584a302da1208c307678e13cee081eb41083494abcb21459fe4a448b3018b52aa84ab2a005188c3b3e562d4ab7ab2725408b2f98e4b34e78cb1035252f182e45e8ceb2f031a62e18a49590b05d4a5c306d67e89a18fa6b73da82e937949ea513a6de4d5a30470d11a393ff5930276f49eb3e23242839168cfda6dfccdfe54bf62b18df563ea8c50aa543dc0ac64892a4c5ad10cd37af82294dd6cc1052a48f8aa860f83437fdba96eee9330543d21e29bc844a3aa58f144c273adcd4561a95f1898229774e22a7730e17b10305738a13847c5a157da9f304a3df6d8c4a39ba5fb89d60507db224be77e9bddd0443fe544163d46682494b53fb45844751f5124c16a3f46e87503b9f5582416b57a8990a17eab24930c5885ab2f328d3c92d120c1be9d77eef239892957b461a696a3782f14fd569891635599522b8de3134ee829408e6b9aba81d4443308da94f49521cd33d89104c9f5c3343361204a30713e1ceb64d478180dbfbc40d19c91f6c5f62a2dd25e803636ac65df8ea2a7d5f0f0c9f3d87586a2699f08e07e62442d7d889a495afdb81d1225975e8be90a2d781414f1c11f2294bc9fe1c18cd3e5e8acc7160485a3ace6385141fb981294976f7749335b26736304a2e29b2cfd7e742a5450d4c16f282e99dd49ee25983163430fe7772f3905cd1fa9f853955dbd409a5a22949cbc268232ead8e8a63622ab138da8c2a532d2a2ccc6917d1f7e6f64c29111770bcc238dab5637496b8d8f183c7077c6cc1c5165c6cc1c5165ffc0f1f2de0c0165d54600b9365c40a385c6192a2f4ef3ed5a7f04f2b4c97c428132ded8e6268ca8aeb4705cb2a8c924ea518629fa1d483431526b5a15b5de48a001ca930854e165f44bd83c87b0c385061b01cf9f46ddafb92740a83783115cfdf620a7346360bd39652984f98c550fb1cb29a22e2208539fe45487137ec028e51982d3d33fe427ad1e1238a3ec505295a210d85f942b894b256e44e290485417d7c122969fb8834a20a4e0b383e61ca0bad1fab3edd75eb09533a0f6f13f284167d274cc226a9b539957e52b50130306049c0c109a3a4154f97dd84c9a96cc2742d5fb26ca2061c9a30293d21b49256f00b5a22061c993079ee25b5ef578d90820313a6f09d91afc6a3e93e895dc2d8aba15933c9765fd5030e4b98f44a18d99ca44a18d6b5da54ace8fe95a784c94a239c2a1dd4856a13ef806312469b13513fa9dda9d861c13b18676dc02109c34849caab94ac2813d5613ddefb7728e251c08023120665228f4ebe761d1e215fe0808439a95cb453c96543fa8f3024b5dc4cd70aa575778461feb6f208612988b8118698befa556a4a3fcb8c309887d82ec230ba258810234598938ee589de4a5a421261aa94dd162985ab5322c230ef55ae924b45b2f410861cd4b359ea4a0a2643982c3d4fbcfb579ca04298923e1371b2460861188f9cd4679983308cc83756e245a524434198279d9e1c727b08f9bd4018fd2e5cf4183adaba038441c77afe60ca5dbaa2648dbf9a7e28ffbd7bc9f20c471f4c2a7caeda469f5a5d38f8602ca99f4ba89af660f658da7f5efa6329bd1e8ca7c498f6fd944795280f660d59167f94ea8ce7e0c124fedd82cc897fe1dfc19427b7554c894f7bb71d0ca66f2ef54f9ee56c1d0c29a927d17fba2a214c07e39acad5797a0e466f17ef58d1958349e55a3dedb98c83497fbea4635eb2f75ae060ee899f6bc376f647fa065395c83f2225ff8e22758339c5a5d7bc2571b9a36dc89408fda14cab6c308dcace71514c58c4c81a4cda744e4aade793e05183494684e8489334fdc23418e72e89d3772aaf233418f5db3f8216a15aea33304289903e2f6a0653d411a7bbce2b83f992a66ce530f23e776430bb67cf71a631183dc91f25cdf308218bc12c398591d1b3ab9e0d8339e82cdf26c782f6cac0607a3925f2e4266cfb5f30dfbcdefbe514ab332f98430e19f9b2f6d5afd20ae0e882d14dcd56a80e9d2a45d31ded52143b2182630b266dfa1284e7912242706821cd4a49455259ea08412b8448775ecaf214c09105e39d7f7c889663091363c190526b84a54a7f80e30ac60be93f9ec75f9990c3f89123878f1e3a5ce00347ca845279cb1ae0a882c992da08f71c52a9e97b8110e888010e2a982fc87ae7f37c9e4d680a869bcb39cbe5fbfff57148c1782a5d144b921405b3acdfa98aae13fff238a0608e35f266637184eaf413be2ecb34d3a2428a80c30906153c5fc576120d19b30047134c41e4d8e96a231c4cc8b66bc4f6d900070001c7124c224cd249869f580a3b03b6e8a2021c98010e25187c821e4bb1a372724a82e965eb5347a4d1d32624182e3f4cd0e9638fe308a68e54a1c5ddccf37c4630e58e7adad16911cc1b3f57beea57390ea3c70f1fefe58860fe52b74e229f48229c2ce09186609ef4313a2c7e3a216621184fce2e56cb780ecfee28467004c1e0a142944859d57ee5047000c1d849c79c7afc3c794d3f30986a096a42543a35221f98bb72c9baec0992e2722fc2d0512c03387a60b2129246b8456c33b50ec6c103f389a8901f3adf959079e8c0008e1d542efae6e72785b00e38f7b290dc51cc71e4c06cb9364c4ef75bbccb0e1c387854a7b41d2cc4f3d71b70b53a31be3ec7120de1b081b1d2848d55be0ec94635306ad898054fc133af36010e1a18ad63e51e71f679339d8529e88f0aab2df69d972c4c2d1b5143c3120bd3d7f8e47c3593e7312c8cef298f0e4b4a9db864dd8d57985c4c7b6b777abe4fc1584118c41526f39435b9238a1831b5c258bad278720b2bcc31226af7f785a8deab30840f3ad5dc9e3aa5bd2accbdeaf77261394f4f5261504a2ce47fcd7e031506b17bf1bfdbd3230c1d0f060f2fc0e091374e6178d50a4a9fea0a95435f84d1239da1294c225df6fde8f0a74b5b0a93d24fd5ed2df1fc4861589f9cd48e598594f928cc9e63a74b3a2b0a636435d36b9d6b62c44e6f84c2f8915b44493c0deb1214c6799399f4e629e4073f61b88c20cbf4c4bf46f784e1828cfd58a71521bc4e1826a75ccb69ae83b4d8f3e8d1c50542a0e3c70d4e1866d724e8a8a49a9ede514c47fff0e29d58ddd884218caa0933494fd09599e086260cb144a777eddb143f755118b8910993f8cff90ee9e2439460e00d4c1845df3fb897cac13b65fb3f78e48d4b184212bf70a245dfa2eae386256e54423119e154de51304771831267b83109839c10a2ba1735748a5fb821098376ca4b93f3c3a83b45c2607e49ac47560f772948984da6280df9a2e643b7479827966509ea624c1aeb86230c52949effe69e89943c4b08dc688439cd84c9fc66c4acfaec0837186188264748aabcb99aefd6224cf9b257dd24378cd3a81470e5f27024100883a16028100401f9760333130000000c1c93862281603424d3d57914800351342242362c1a24241a181c8b04e450281408034361002010088681a04028380c89423d581f08b7b70fc81758c58135b3282705863aa340060ed86a386b2893f4a0c6f92b65a817d65080a584375102743b807b371e39c4f015271f2731f2d9b59ed58c3cc5bc15678ef055c1156e3d4ebebcd52697f13738dd2198ff051d9986e8744b1e6e6bb78a94985a66fc1a2e18bc1003a23717d3fa4c48ea3fa882d3fb5977e0020cb4d71dc36e50e06b548bc8eaa19b007751625adad7be31f11afb075fbf2666a16baa284854fd56487427f631cd0afbda7d8c701a9cde7d4d12d3fb294033d12c9066f18fdcd425b95f14fe22db98b16fa83aa2a01173103128d0fcf766bc3fd7a9b8dc8b0153fb8ac36b06f4b5b260f31d84898940ccde320d8a22ad4dc1d3f59171db89f7f48a8d0bee48f66577e84eb0a87169181c727776c4ba3b31c1e53898b24f355955205f96218335279f5958974d9415cffa4c85772611d15ea6306ef21618e8de811343d1e0fef443684eae803775525c3acffc37b488081ebe3879f3b9fb3e03752c1831ebadbf62839d8bcb460823ac36846c593889cde92bbfe7ced58cd0854d099f124ad25e5fd5f67a0a7292fa1aa4c09c7be3e661dba3788b5b1517f8f0a3914f23951ceb8ede37a0bd6ade023f31f505b357db20c34907c15643a604744b01e41be5ca97484b1ab2c41622b9de9769af40bd846c5882e87ba894d8a4a9910bb4613a4c9b69c3449836d38689306da60d13617ab397937b41cf948b613a4c30138641bd28f2fdd7e47988a96101e79461e052edefbec058cb1843ee67a5ab33a74127eca0c66c8a567d7776676f764ac2540c5543dd46f487dc5ebb7c1d399d3e8062a6800e8b3f113bc0755b4166d1794a90b42ea468711264dd7bf068ef9a68a1950b292e137079c173592eabc8e5e5efc2289352ff1480fddc2e29e7b016eb27bce7afc5ebb43b033c635ed49f84a5b359e0525889d6f1388ab58a65808442bf630662d7c7b394a2c5d800659c87050b7c7f9d10325d2f62be21c313545a063a8fbbfb7227e6e4f6069033223398f99cabc42d79472f79360d2be794fe97c917dbd512948594579fd986189c068e0b3938789d523a754cb05ed41b69d327ef8e7fa28ef995080be86ab72f6187cb313b6b6d66f17553a525deccbad902206b38336e2b46446230a3a10704224153857d96f474e0c3963f95ab5f93bbf54be55f5d69e51672185bd53006be93cc089380410a1cde1a86272eaf2477e1f8d890778f10a578782684c347f4879d5c56bbc561706f3301524bbd3b9fdace8d4e83b168196e688487231379c95068125bb42854776cc5d2815941bfdb116388065ee46acd94d275940e044006d75e639badebda2743871ea934989d5278f464c3a5c810252077291c7a643c21eb6e97a1b699d4924dd38dbf317f829007154d19c93c8c62c5918942a604c180faf39a53e24364655d195f45364e16400910bc59192af5cbc406ea84b6b316417f8a2b8b3a32c6681c538d9aa1cc209b29a07451fc9eed4ae9229db0edfc095ca85808cbd46c95fffdaee69b404cdaabbed06c45b0449044fbcf2b77642e56bab19c917988c227701f9a6e358ab528e783fead300ea6bf7f47c58a23415d79c21c4145f07362671954dacd49068a1b908e7637ba03716f37b06c9c74ba4a1be3e813fc50db3c00d2568199bf5c80f2dbf025e497644032250559c62e251b039f2fc9719205d2799f143bc9eb9472e5483ffb1424522671936649ef53dd094f4d219ea5372eb2b57d54e3fc764a936a78f10aafc942f61593e18ee8a465755fa03e153c5760cfd4063916bb9cb13429fcdf60363d331e5b026e1b41a57c6f75904b0d076f845c08325e81cee82a918c25a1de979e05a9776d537a494c9fff3e3476cdc2da291198f0d46bd362d398e5161ae48156691f7a0a791f66148139e5704ac10f6d2f61396cd3477f071b9b5eb90a24d6cc0d925ac4f8573e4182e471ccb6b05962c21486c15fbf97ead018a0786cf39bd4dfe0d42be2749873d52d7bbc18fc25cb58478a10414a90c3f2ffc1b2b99497dc50adbf76f7c326862655d4138a413b46475ba3f4bf9748c30c966385ff1655374cd5c665cce3ea0569c47cb7876e3656e29ad72fd62fe9a962aa2cd96edb3d10b20b065f91b39c0ccd860decdc0154b9dac7738d10019cfe1178c8d1121f92e4362f04fe4ae7ed92da2b5348973005b53ed8d4585ba33e39c8102e66370d5806dc79efa8e92fb4062e3bf0a0f58b4d9e182a6073138253f3e5f68d2c0927d4bd9dd9a09ad9b53ef2f0ab828d99e4b7b3b42af18b6d94bc1fec18a5a7eec31889907a2b8faf0552e54cbe393cd351a227286a822643cc9b8247df7169be8df7c849fb4e626d384a48aac6cefd51645524048a5a7c11062ce504331bb60284f15c85ed961458c6288da590165e055ba0dbe6481c69e852ad661070d4fa5bcacffe9250d0e65b76573f0f8af7e93cc29e836653f78a379449c98b72c28ae4d0b53d19b645fb9d383f2a65bb8980f521230137712cf37144cd689e51dad02f62ba947be1680fd0dcca8ce32e944cb7b12ea19f8b1167f6beba8f04614b85e61d280b95cdf1bf2d6956b08d0bda8dba2af95dedf074251518253c731b2da956103c402331f118f1ce90e47cfbdc27a9fb087e02798add7f531344624940fee1a3f702aa2569ffc37219ef14d2271834582f788830ed880d62efa19b32550c65aaaec0b0ac84a462d51df536db5d459101a61caf7f1ce0e3669144a911caf8ddaecbdbce119b6641edf13a2ba0fe6147a113d387d3919a5eaf5d8d6bd9c1067bfbcecff58b396a9c04a0e899b19fb0a8f45b6855c640d3bb6a8bf7b596d84e6e9404a1724e9dd1aaec8de0b9ee5ade3239b17b7ec884e3b44e641604116670860a15904a09561d417d201da9b6b203e603f08135e32917cec702ebbcf56a482e26f364e7f988f187657945b5a62d4f7210cc2737f4f627f786858ac6620e2a3172d3dadeb78b91a6f773f824d6a627289660e5318cd3e654cdca59cfd7292e8fd337488fa02e27a92706e0e191d29b0a18cf0fd6b03a65288cec900a99ea757dabd5ff6d500f042e61e24d70d70351c45db456665a2001f2bd5c669fbfca8e7ba51387f640a031be6ffcecd92202000ced42a6a42b54ffbb31a3b0854a38830f98f7732662ce156a22d5ab209e7e4725135ef2a230ccec67549eb39e3d72a0887e4aee706f6604075ec728e763d39b6fd5432bf0c0c341bbc461b80ef11dda0e6aed5080126d840b604d1a53699d316883f270876d1019416a7f0702f649cb02e87a388bd7f6a6a60ba696de953d7bc88aadf8135a6573af0caec0f8c9d403d13cd5258a826809cb89734bb178ea6c99e27892ee3f9b7f4a1e5f41eb6a917b48ffae7c0ade08c42e279ead615996ec60d7911d04f6a6e26a838839ce595b698acf1096f113edb5c68c57976a07abab18ef5ae550081eae2d7a68edd2c062b31396e37d550021bbf97da1e89e855c925fba16fa22fb8795b96adc239c78071414296a7ec353daa5fd3f3a0ab395867a21a01918362915f7c0ec9f2f68963dc6a045767913e2a6d42bcd2218bb9a1c71fc39aa0d9faa530ccb753094bd4bfb8be1659be4f7553b6602cb298494e00e31750810edbb4b12a9a16ab72e6df2a9ce66165496041620c12509c98f9923965b50f475f6d4a4402e882750fc21ac607a5e0465e8f7e8746bd3641d437cf0c703c5753f512284d2d8441729e6aec2838e0bd8f4030b71c8da24b063edee9136563c703dd439b12c04e0c9d436d39a572bbeb1e2ac4e2cf3a4ae2e5635015151aff3fda15081dc6f6b9cb3365c6710ad0014319008b70319ce867484c73b7f222921f08d3f39eb9c2e443b5c146abdd64b614904da6facf6d0c5006b0b733ad0e5f0099af38000f29bd9300648744a2ce12d2a69602067d31162db8005737a96812b4b46406e739a78459a38d7ad7b53a1814554c7c76220e7fbd1b3f7d1d70bc229f708972a4b016d485936fe686e82629b21b0863b869a4ff60bc07dbf2c247b66819667720d12b73d721ece0a566e18e6c4ed560f490d83211afd3db50e1ec33a25fb1c3eef6d792401f2c9971b2d03e90b3b35817a86151a259839b499fc01e120048ee3a108b55a7b730bf3ab2620640a182d7be68d644b9cf92d4ebe57b66c961deb7e87a2691aebd6cfd714e52cf1cd1aa18b7795269e96c08f810e99d10483756088e57801879e9006b96355c23c64dca5b04b31de7024d0b3d8e8784ae1be1fb6415834fab0c06add4836f362f90697e29d181a62c028e2c38892111a8d48b581fb3f4b8c77615df74a2070aafd7fb2e4b04d5c51eda8df036d1f589e436ba38c46b70c0a4b622bae3452693e12550d9d0f75ccd41c5c2b641d638924e88d8f0a7f1cd4db34522a4515f0141457005469a500fe94317634248ea8510fca08ad800cb62e15de32fe001d40a1910ea4827a0ac8ab14150051c6a8dc672e1e4328179d5eb4e4628591bf3cea350d988b4c7065b235a34f26c5851ba7442b9bb83c450fb01850bc573c56cc2e3e15ef29a6164f8ab72cf2e2c2c099866942cd1bd26025b3f336c58d071aea324d3a1f8fa9e6016d7908a22e6838f6203abfaed45826d4f88fd613c42782917923b091f72c4be0e073475d6a47cdb149ae0c2f38d7ebdcb84e0763b2af3b41630224000920388077003bb07d009ac8d0c2b60881527afb585188777c60d8583e04d23fd7006d1e5f73853f2730896fff2a54982c04a09209910a1cca3fe8314dfb004854d3063136786ce4d84062c38b8d1e1b48c1cd088ef9ca908cc9998273e437f3e0c0f4e605e1cceadde33c971a116bc655961b921e278e836abe0a7b79c65889d810cbb09f106ccb73b32af462d18de29dd6491b8c2ea342bea3e45aa64842ffeff3f600532faf6251124bab4cbc5b00f81068c60f5247aa682d932469e4b4cebf8adbdb5d3088b41bf2aaebc92ebe959be7dac77213d342b6c908084604013a648518ab5554ef7d74a8ce350899176ed7a1c677632452232e80171e0b8439b8e4bd15ab9e0eb208b94a72d6a78a41b7b0f3cd2132b6ebc22c12e1c39e2a4c0009db761abe5a577f001f8894695470a1171010f8925c8f22481950bf1bd009b02e06a4c9d5f3c32eb9502d92baa55aa4364013a4b21b32a560ecb20d08df8f084f38abe3c0ff0a3e8ea1bca49f16d624704fa1ee828f4be8a9154c2c1e5214a2da9d96d39d2844ed1ff4b25a2a44662aac56ab6381a71079f0ba7731249796c150daebcc9accf10ce74a38345ecaed764f5c8cf6d2f76cc5c48108707e819eb72cc06dd7272e97ee49aba3fdebbaf8bf6e2522a436194a157aba759052d1097e36f36d3c8c0e5e9271ba44382bf195b1184f4dc83ee1d444a898b5a81db0baf730c61e10c6cbb131ffd1c9068bcd6dffd7a060919718b291164ee3c61758f461ae22c33b7b7d97e0cea2d1c6cbee4ddcd3ee29dec1de21de69ef20de61f714f760f740fcefd2a8c14af6b467cb5b9467c9cff20f1724d6b7c5be43eb0e3c50ccf855fce68da08fb73eb579bbbc0e79b93c86bcbabc87bc77e95bc8c519a488411ffb4d407150330c1a9e0a0ac067448507c300bb9ad6bc833d4cd19056ef3d167eec4d7ce340bcc60e20c65f289dd83b05294c7081b671d1c69bd68046b9871c49286c4c31ed608358e9228d45d30e40ae39b058f57d356a81e7a35dfe1b0b34e66f8cdb4a0934557c5349730f6b4debf8b97de46bd74c44e0efaaf2303752058c4d822ac67d0d88ebfd6510e33f22c1f199ae5e26931f5675fdd2b54fc782aee78b209340e872c9bfd2c73156c6a688bebed46ff4b7516829b18c8ccc88e4ff591d605f6edd0ee4bbf13caa4d515715fe3e62e6cca4145c2a82ee10fef877858f93c6cb003f54d24324233be41d1cce96bfc466e3828704e6180452c1f6e552a2238eb5cf5509ab64e6b4cfdf45a91ad23f8c9a69106ed3a8414cb31a0e8b23fd6a73a7e89df6341ff790d68efa5f39cb1706f3acf14eace5223d53bc12ece844857164653600d2afc9ad929897d2572fbe67a760f12f7aa6c578937e0e94cb82fd4e5abcdea2ddc6dfff741877be1f892b4dcc30245d8ebfe00618f265f5491e201902a466e7da46b1ee0f07db43c931057e0451d3f4cd2686aa5a6e23a4f71df71c0b433b183ca1ab6696ba2f21384fd4509d00cb81a0d0b595926c8e86536687b2eb7761aec4fd2ab688bd94b10c92b66c174b7f54123148b19451c0626d02efbd639e3332644d73ae24fc90917786c4415b4c8112d1cb61105dd2495240f252b31c789dd1c68c64111ba93cb69b5beda22739d3a2087713945b4e443d6759c3cc44edce6f23f6c11922e521259824587bed3cb8732305455684ba885c40e200fca5d07cdf235424bd3e12f52b22669ffacf4973e3d45ef7f4ed069ebeb66ca7af92d16d7f1075e6597b702aede23d48f3a02631b3e9aaa728031dfab78f08eebce9806545143c7dc016e4955d9269043450e6e451d69de0a0e558a80b88268e0c7a574550244005a9e41ae6d45f66a9ac18395daff6ea4d7794b6fcabcebd5a952e149be48f2b455e1b8a61da8482ba26ab512ed9aed530c71c731d83bfe38e50047c7bdcd9070b23f98d91507ae6853e86faf1222dd6877248a7fe802cbd694589f27ea8d841fb7e92f3d79a0785c9e09fd4626c8fc50d7583e1b6ece9d24c1a3f61f1e935f8c41e171680c6ee7f0a6baa15adec4b5b26a112f480ab18419450de31919d715fc50682e053dc1d16a741be38cb1fb24e2291e04cb1801348b76930d8bbd7b05dd548d9104177aedad52682191651bae93e45149600d25d171255a053c5244ea416455277fa9865d93544394ba38dcd7d49ddd748e76a1a3e9bae39f29d8231dd77c03b415cbba98a65c086ab615ada30aaa85de95992e9017f13f9c400f8d96ea5a85eae673269d72eca720cb553757ff6fce4c77e7fd731f68c00f1560598b3f1b1864932ebdf18c5fbd032f19c5e64785ef4181f8fdef15267de47e132038417e7790f860d5e216b4248b254f38bf2a18718f9ea753b823d22c29e0b51ade674e26b8a9ef09d5cd424a31f05753a00d132aa4ec7c56fe93f423f7f08c70ebafe928ed91b687f89352f65c1bcbd3d5d235e427b608ed8892b1ee840f1a8a5e56de32dc4dc83e4ab0c493a3ff579d139ae7b3746c956f31a8416002c5be8ef33426daa828a2d2ae3c14d497237d7c166201d3c7976808f3f98b04703a2a76116380170912179d36d16575cc02064f702289c2bcf46e69fe7def16fdb4efe095f9ef93c7583d20d7f7da41a92aadaaa0bc9e0fc6bbadc30e170d588222a52629af2c4603f5bdbd138cfd761076970217849d44d47a2995011798c04444bd0316f0f7c364d20b164814a73311a63febc58f98eeb2f0c311ea12c1083b50128750547f481119f730f39e668a0b72cd573c781b3fc0f34ce65c9e315a9a155205f6d5d02fce391b912d3568b1a4a54172edde31602b4e93e24c1e5599ff44813e1712ae30222e3d087fe69f3a3d2bc71b312fbc6666cf3f671df62a4bcfbc43e3fc2a5a6881d4f021894d07dadf9d550686ac83b5f4a4f88775f2633f4c5ee06f77192d251f955c25567bbf481fc20295db7c53b81ade196417da6b8b885933afedcf80933dbfefa78d5b227f7771a402e920cb480757aace4c8c316161edf12973a64124f354f385e8372d1a71f582f867c93d1491c7a239f7b155c88877588ce3b4f13f6487e91c154c2aa2224f3ff0e18739d06d71acf66f477acb06326fe7192c296ba3e46b8645043bb680696a482783ec9d1e85cf8e648c3e55cacf7d017f866dcd72dfa2066ff18d0d36f229481905937cc9dbfa2b1d371d1e839c09654e76423956f5baa8f81e7e29c90ae91f6d04d05167fd09c6e2efdff8c3cd550eae82ce2549154c7f987a9f927cc5ef1f854df272403faec9b72445bd2a5221464ac3bf35907c95b5cab2dad0112862459fa285658102b2ceb8ea8cfac5b22060763a5dcfaca3a580514e7a83721917ad377d539ca343727c195c22c318f9fd9b332b77cfb4aad9e5d4be722268bfa244fe053ae57f8a0e78583d842334ed1f928e7fb5525017b721eef739c0ca03ca5ac2de0ad976460f755d1d88289dd890c89cf40a53917ba4922878d5b82a4b7b6ada27b34d0126bd6ba52ed4747aa6a2e6731dd0a97871e19afea234f98c5367e62eb04a7e7b7358c1fc12f66b7935300fce2d6568a3427d419f005f7486a80b4317f6983ae06887940ba86a00f261ec75fc42111e893406879f488e0847f784b09e9f9b3ff53504faaa98c005660f19374086a711f0b931f07899d626bb6f619908e71800239441a6df60bd3aa93599d2a0a682d48c20ec5b5b82f7131ec452e286b0ace3bf714fec76ca5e09c6bcfe28180c69de849bb886add5b533a1717d1b5ad83b55564d4f2383d68d8761e331b004f52b036e1774b864a56eb442e951f304a926c4c602d9620528a5ffbdad8c403c195440794244999377889018a81a50e980ade2180bfcb63a69de986c0d0f10345053effe0505e8ea7fec9353cf919ce4aa72cb021a743809bbe6de7487cb59f5cfa027ec0ea28ebcaf2add389c5b7820c193950edceeff460ef6869f94025bd063f274cc40ad981010998ca1f8e2a5614c0e04b09950a80e3bd5bc0e1d49eba8695ead0b904aba228365c579dea7713b65d034e0dd7af18ecceb7aa850bf2e8bccf63fc3fe77102d5113e8ca4c58749c4b9eb4052b3b3bdc036750091811d4b48af5733af4588a2d6ce1a5bcf43a6f11c00448f862f0b49ce1cb7fa36a0d17e2a6857ef6b20e4d75790b3cdc0ecf3bb59c90ff45f4e5ba5c8c4e86a9c1312f839826ceb204615a0d33fdf6baa20e7e0e6b0b2ce467eaee144d13e643adee29aa68c032d370d17f864c2645af02531b05a2fe40e233e4fcfda68fed9e4f0e56b8632b6b5b098646f2e5f1824ecf0398988daaef1a2a609f5d29b3b4c9cc8a4431c22474fb8ed5a808f2b8a0377686ed3f03901ffbcef9d61861b1859b2ae41654083b0321928a8a509021e36ee9d2bc665dfc5318725bb083c26737b65307e81cba5df0f8224d90af1b1126962ac0cc3e57d23fc2fd34071baf4479006e3cd8a6796bb584da9f42aec227ec9a5fb76034c8e52fcd1574417324075c7d53daa30ed1f8c5f537599bfbd17e0e2b4e7a71ff88d198389878d4f07a1f84d1589419fa640748ab08b7dbbcf2b243e56d7d58d2a5b9d57d7140daf57030fef553d1d63da74bdf166f90abd5576c6a09217d8f44c895a62cce950d56259d89678ec4803a5d6673ad940bacf043335e54680fc855e79e9cb9bf78a3fefb9c63e2ffb3121d9fb2d16bf29c61454d159ec16900eac4effe13b36d8c23f493a96062c2cb2fb8986f0a1ca574caa616a791b2370d1f5d75759a3d5a7cc40969f0f210492f9e2842a5f664bb128d80147148d4f2a767c51519d00bcd34a00062a61faa83795bc43a81c75abc7d7c810a60f0924c3ba74fccf9d46d7868cbc4bc9eaf3da34bbe3f55e044cef8d2dd0a4396e2c87c061c3cc250505dbeb46c3cdcd8bf98e35cd8e4313b137334fdcdb7b6975a2f14b24b023ec79f7665666580d4db9dd1191e3f47bdeba1db60ea75f932de36ae530cdbc2e5bceef6d1e96d1efcea71db488610f193b0f6dd3533b51bd418d03106ced2808c2b4661949c0e9ef70a30e9edf21c23d147d1a0ea8079bdb45ce3adaee4ec50c485e1a758cd071341754413c033012cd6807e30ad1b87d7a059b41651b885cdb6e3b53915c2197fa1db180394176931aa9f6a61e8d528c0c96676c35eb1d13e6cdfbd5044d5246d877e375ebb51a6985133042cc77c6fc8ca43312cfb86f18c08812e312c63d30e262fcf4209011b7a4b686720cf62808168e2131b687d19b088db03f86d494bd8ca796bf1bf34831ec5cb40485198dc660aede12632731743f09b858f28b93fafebca55d49f91c078465af8de934d55c3e9b86285b56372d223057bec6ac6c779f24b2b7ff1ee33ddb86c29c87f99bac4eb7028a8c0c282197c774119cf075ada40460915dff68452160685189ec451c31d06930d36042031e0d761a8c6880913dd8273ad915aa31c0dd00be81f20fe0d59b999b18eba465556c7190b8ab4d437cea05929a35f3b365089b5700c4e3e2834d3d8b40e0a0baa33b070f805625e8e9810070019405b008e01f4c0969e18574d3257d0ac09e0cc0d8439ef1c86a2e82d8da63a333b03852e269c351469b3b69e05eac4a809c5a440d55f80c54e0f217c00272c0e07b0de79fc631f82af20e3cc06b06e32e032077c4288d9c224a7d3ba9cc11fd6d090cd54b55039933e361c74e3f9a0e5b046574b3ee1cdb79c1eda8b7832d69710034e1bd1195f19ccf7986f6653b14c842902d1912b74c406cdc0bd780c1f386d3c373b195f43f079a6901870d489bf45f3d55408fd4401bc7fb07dc82def789625310d177ee5f444291000d7d4be4bb362151c044be8b8dbfa39220684a73282fe515921e9b7615ae6546998a511f8c9eadf8303e39c85842e2c4d7e89c0b516ea299626f20ce1a310c590cbd38b3719f22c9238e69d61b37bd5d2243010dbf6d83833af14e22dc2854df03c7db40a3ab16041df31bb285295802c7acaa43f9c6d43974d228859a529d86482895cca17dc44e29c9a904d1994354b1ef5891d3e8fd05dd257f14c36b891ea2f17102d19ef3c3a228f3d16ef2b2210821101d464515752a78fee5a2cc5e911a50a1b49027dbc1e6d5c010402f1bf51114240d5807cd5c270b3f7e7391d2036d95facab5ffe210a70df47df16059c086829053609639658427bc44c4eec540b93e862329ccdff2fdc15ba86e060a193b38ec60a0a9e25d15f341e9c1f3ef7ea564b72c05ed25b7409b87b772634a60ca1b91a7b4f455e5cc777a80239f5cd00168c22294de18162707d83ab7063c7c09632c71e18852bde58a13b20299d982b2299cf468dcd2f36db7878fb3a70c65b27eabd42dbd484c4a8f7827b8d750a8e76eb70fb7eef467b56d6b3e115bb3dc01f4f3b91bff7eef719805502bc1d460e205470cdeea3e1c0889fbed273205ac5ff36640b46a92f660abf9cbaf3ae6d9d0b74c767d59f838ac2f64e375af382502ea61f4bca301da45a1bf11198ca1670010bd58d12760ba8e35310b674804b999c068ed9f8de5bc088dc96d059243721215feb3aa42d529a6420c25d09c50754993d0964680044981bd7b3bc9c4aa67985ed553cc9ad1137a5107dbc3f306143e4025d931e67053647cae58c799000b756f861f50c84e351ddbc3568e69f452286d4d37303e78a3c5ba9e60d6c5adaea8c8b2d70ac694788041046a77d56592532dd37b15067e9212672391bc4f92619121e89d69d2c88c5f4b6f26109afaa59f39b6d0309f91b9dc14b0a3822159ec2e36724852c8ae8b6292f0f3aa1a4896c4efe8800ec25c1a50f94087a6444a7ddb0e1abb38ac097867b9ceb5e0986099a654480af65b1d76be26e7a3ac00125e451258ca8ba6f6d46cad899563ed8f5a9c8aa110eb18a468e4c69a6175fbc28ede76b2dcf857fd54d9af2997ded07f430a2c5b9d0f653811b39a157fe55e52f2229459751850b1eee4e54799f4c2b85c520b9b1201b3d5852b955a7a297625c5bbfe94fa91294ae78f01d2a417d4f0594c7fb7ced311e53d504c16886dcf1186d10b84d03b9236b9595ebe2eb764381cbeb32a94ff18b0a95ab252c085635f1c261f6c65a1c697f139920da690d2d11c679944ff96a0755e4d28425fbcce679e19b27a614d706c57e55261565b129d586b2a93bf26c8f16193580a82b926c4d16610d47fb6553bdac4821231d9c84edc5e2e83e4716d28e0746a2d0d3a42acf4268801d3fef597dd2e7e10635447790a6e39e7cedc7305cf7a87042b04adb73b1f376bcc665d3b71295fe4ec7419b406e6f3987e167bf3615a06d81fdcbcb07087c0fe430452e2e4116144e15e1667f0825909e7e622cc6f20536c64201d72f81783d970ba88833703e4e65d8ea00e8849d8e7a21edd01c51a8ef6b6bc849823d979ffb7eba7d2c0fecd0c1a7c50ff9b937ad33b369a0ac01f6d76faac550d422ed5a75bf53492db0e052d89f28fa9f4c8a022a30ab5d95e3120d118ca84e4bc1c6387f00de5260f1cef737e428e335cda48405322f4c4b138dfbde13d52ef1ec7087e91c0b8567687466b2a74c01b97881edda7800efaf6ea82c9068776f4c460c59403516707cfdc6fe2a8c0944720d0ffe4b71e9851c05de768ccdfcea4256268752a0c45c60ef3300a8fefdd0b042e63ec2d39ffbb66ccfc2738fdbd7d3b1411eb379fc27dc056de8e69e405fa40139dae67a8d3fb31d667cc96edd3e47ae1d3a0474f99a83f3fb032adf1ba848a340abb12482f42a7f47ca1518893351cd42af4c190ad013baff163403e7ac99f4b46febb6620d311d63b39a77e47039ad26a444a65331129376e1ef824b572815c88db172b48594318f2e1d2d8a80e5368e2a3f5c2c7677befa3bdf7817bef2306ed1b16282455c9362b932047b454d4ac205051d092144dd97c17e8a459d25371df5c3a130ea20f7befe37befe3267bd123ae32f34872bb21699474043af418bc6b682b38582d04163aeb8205c298e85cba53868b14e2007640566a852c2520b49d7128cc7e25cc1ea26c0fa328d55673d954e1f5cef97343633c2853d5d56eb415bf298cc74d8a71e739e85775a6965417307838f484158f216778f6e0519fc716937e6246e5ea1fc83c1e679e4de01cb6d07563234b049a87630acdfa82306fa612fe2e82c91a36464314b17edacabeb1d3f0cb468ca0ec1a3a99fe85483e93e1b9121d4e1e07e9a3a96268a50841b3868fc5ddefba34cbcd795e6f96b48860c06d2ca597e1f54ca88fedcad2ea2583a65f2610b1e44bb7f3316ea3fc618ad5737889f83877acedec327a70f23bd0be1d20e8727b2993d2ea1877447661983e351c78ab160e2e9fbac080751f20531a13ef5a77c26bf6f7728b3145af0be0b3954ab495558cfcf35b788c91de647072ccddf00fb4828cf8938069ab2a58634d6587f67a42acd55e72c8b2fc5f2749493dd41e708360a990bb5d17e30edc094e3d946138978ab7728171c37bf33114c13a3f64e6cc84d6c0291783842e9bf9ebc606e3b24efc0dc7387e773d28ee78fc33176fb1ae6548ba3946d678ce3f9f3bc47285e457b4eb0cf85a15f48b74e60ad7c66b2a72a46ccde6fdfc95bcb61b6b971bb3b5bb093730ffae58c42057a3775b5816e89b38b0c2d29c81f05e9abe742536dc4bb3844e9ec9f2378b9d75a6b7f873822046d3e0a06618ea7b8ad248cbb24e3ba551e04d66b01f1d43a6562dd56c02bc34030ebf85a60e3750b0a6d001f3ffffffffffffffffff47f1ade96f7f0056c924a5c5fd1ffa2b0029a594524ae9762b8e6893df08b10da18be0300a0000040a0fa70e550e18f285c2ba291dccd5e4b879f542f9ecf4c7bcaf5d28da85680dea36215c389b8a67dc13d3ce8e902d781fb6edd5aa7b339ed3f8c90d1e0fd142a964def8a671464148164a826ea58979eee42c1d2c14e7e64b14fbf50aa8ee6e5ca9697d9652cb343a85f76b562808b92e71f1662132e880902a14a4a5baa6c6895d5d53a19849e878b9930b9942a963dc5bd3134d947c43a45072bd90a35cd7843f39240ae51226be4d92a476241f70258440a19873d5b2d96f43ab6703a44302366cdc0c6cd8b827944773a6ea488fa36fe284a2fb86079de6e9f3498734e152f26ede67d9dceb32a19c318a264169ad8edb7ac8125a0db1dab7366fd79b1dd1739b5bf7e5d414a284929f3215e77148120a7b3245b997a431a92904096533cfde203c8edd7c2ae408e5901dd4a726732dd91e3cbe47b281102394424bda0a9bf50b214528f588d868388fa274884884e2c924add793d98de86e081942e95b5e6f2fc47721422897cea1429ed8caeb3f2408e520ccebe52c0d0142a94afe4c92e9eb0615427e504c722a93e4f9ca0725a53cb989a9b3213df03b63899f9418da40080fca1e7359968ca14f12ef5d942abbe42b6b932e8abb6719c5fc04928b82970ca17f456bc346144070516a3d9daa84fbdc373d0f122c14406e51701d3731ec96882d1dc416653ff1592147cc66fd6b51923ac589fa163b4a92a245d1534613ea5b4f8ec921b06103cbc40083470e9b45e9e4909f4d8cb9df4f32304064510e723fc632d5d4396c5f80c4a2b4714f7a8ed5c9ad375c0310589424d9d94f70fd58faa1b601c82b0a9f749a92417bfb08d90413a001882b2cd53cd31a3d6b3579d7bced20329e282b6945f1565de64f724158510cbff1a4dbceaf75ceaea21c62156e73a27dfbaa8a62ca1e799d595af79352519c3b319729cefea38f8ab22969a38ccea473f29ca2a4254d7c94a09a426b539464ae2b49ce26bf34a61425e9fc63a598291dc32545a93609fa325d294136378a629b123ca88928ca99573c6d3e25cd420b453964dc09f5be4151124ddd2441ce05f944e9f485ee8cee9b4f48413c5132cfb25217e2d32771904e94baf44657bdd40a209c28f58be89b58ddca1804b209d73ce7e76f126ba25892c6d5ff7ff234d24c94534db077abde24d98f89721235427d929f49e8c9258ab946a98c089d392b412c5190e11a4bdea464d896412a51fa741b736b9b64924ca24449d47f6478694c53279330e6203f072d9e8148a29cf33ac6738f41880f412251b8ef3cb325b767ff41a2361d359d15d74dd615dee93d8f28e66caef7f8f68b31787c086200e2889269cc7df19eab4ee6401a51f25b13d336616412178411c598948af8f2305f26c4c1f6618cb1438c1b3a7adcc8db0106182806183c72802ca224bdc9ea993c8dce25c90f401451fceb18f4c9319d64250ae249c2adad9b3e8111338020e2400e51d29884785042ac079364889224658af8f638e1fd17a2e466bbff27b8e7204584286c0ad9d1eb49bdd767102551ae6bd654499579238824f97f563587924014db3d68932696085513401434469efa4e3a747fff433936c7947221f4c4c6fd50ecd01244092bababf13e94ba35d5e9959d0fa5eb4bb7906be739d67b28090fba4a122544e8af87a2eea5e7d86775a5da3c9474a7953153ff92753c942d37452d467e7ffa0ee54d82b87a7d7a994ded502a0d32af4990ef92ae43f1bc53644e8c8e33231dca3909ff499293b9ff9f43d97af73af48fa94f727228c7cd2ac2456de5e9c4a1bcc14cff9c87294f1b1c4af531463553ba9adf027943e13c68c9bb2e7143319d55a62e39c7d81d036943b93aa99dfddaf89f1e840d057f2bcf490efd05c917156802c81a8a592c3d6a668eb372a2041035a04dce3c0de590b1bf494c9d2b008286926819d3a67a28ddd0d1e3460cc2c8b103070fb43394be33c4e67ecdc778ef3043694dca283b9556a3fb02078f1b670f002943494d6d637b948ca59440c8508c9f9f353f690c85cf4edab77c34f5e741c4d0cadc9c6d8c9d98a7e6de07b912d39f746908240ca5cc6b3a2641a627256e2060285d8872bdef2056cc7500c8174a4a7b434e5e7b26f104e28592a44d348672afb8f15c178a9ba3688d9d45933afd0e8b000817caa32663ce07690d72a064c70d1d3dccb6504c627fdcf8d1938cf2150640b45012dedb4295242655b537783c8f1c34e891c39c0640b2509ad139569ef425d5a938d87ae478410e74038cdf71630639105a4f00040b850ff6a9f539a2d6c2910387086cd8e891c3f4483970f0d024805ca1a44a480f42a598d4b9e2604b2c006285e26d95245f294adaf08b31c048088054a1a4ad4c9abf49ca7e33068fa60008151aaddab5fbcebc13b939675982124f0246a200640aa5d6cf2fdf5ef2694338d8d23a002285c2fdaba6cc6db6111a3386088e00128572e65162c7cb14f2da0485823ac9b38eea5b2899e500f28492c9da2de2da24493e0d01c4092525e7c4dbb4cfc17d838117e490c0ba01a409a5979fd19fe2395dc82e006142e9f3c870d206b1f1b2b384d2b686b470dff660a21943046673005142494993773c8992994194600e12bc2087046cd8b06103a108748f30728cf1821c86c708a30c240905b1719bc49beb8ba4190041424984f08a2b0f95a2a4e30b043942f1bd3406d313aa5de34918cf3a36b001102394d5b53bda35fa99592d269736993ad2cafc17bc40c706c4d0d1011b365ea0e38b1b2045286c68d21127e6868e01840845519fcc44dbd30390211436276d7b72d472b9138810ccd05b6f9bf37a71b13413dff93baca63001094249c8ddb4e993303752250308108a6a266d7c730f269e9c0901e40705933489f2ee52d65e82f8a0d4a6f4e8786ccca618417a5012a375d8ff9282f0a0dcc13be36e5ee9e9fdb18b925b9e6d65ffa18b9298a9eccbc3780c2a03c60c3e031fb928c6987e46c70ee2e22ebd6ad9181bd7dd52d66edea26cfbab2796b0419f5cb6286f762ea53b46f7f346161fb528ed68ee9a67f3831625b7efbf52425b66db5914535f5dc74e1a5970719bd65eab3657829fd858bb5f365f2cca9ad684ad0e9f3c27592b7cc0a29c693586fb7197d7150c30f015251556927e6e2dadcd7145399cecaeae2353c4957cb4a268b23adf9d374f3de7831525d571cafc3adec8df57e1cd9c788e788596ae5f4d3f934bc3e6545136b5e9b1c7c354146746881633ffdae0a3a2184c389dd504ed76f54e51923663bb9a8acb4d51aacda9d6744c55bab3521434a61293e476f2418ab26cd0294ac3fc07d3f918454986dee898a32f8ab2b98712835bad6ac784a2bc9983ca3ddd31cc536b840f509443a3e6a9f50869e2ff8f4f9464cd248ebaae8c9ec431f061e4c071120e7c78a27cc28f3a615589bb1525892da10c3e386118cb98943e36617aced92e0f3d9eec137c68e22313eabaa7baa5e8bd9b9a6b8589e276c9312e3ea70b21bc4479848e96db57d90d5284014b94af4b274fa25d89a268bd779697b2132aefe08312e50faefdaf1e3489b2a78d10ea9d0487f0218992ac396fca9884ce50e291c034cb877afcea84f00189a2acac9ab07c55cbefe311a50c32fc9309f3b80d5dc093f0e188629dcc303531757ecee94723ca25bcbeeb956e9e9c4a227c30a224b4c71b919e3ae9dac0f858049bbea4d7ca58c2c1c625f85044f1f4e64dd6b81f63b63f12518e613a69fd10228a4927594c07137f5e458728bfcbb896949ab6fe3344f926baf799a3aaab27ca4e89a2b3a45ccd59218ac9ba4b4ddeb365cc0891ea3cbbd751ff4194db94d43949b24deab73b081f8228698f49135f6381289f14a6e4d3082920ca9d4e38616227211bd2c237ec0f859feb4c52f69590b9eee8c1e3868e1eae63041f7e38c69c69c4d998863ffab057d6b779dc76851ef5a6a4dffc8d71c1071fca65caf3e79ff551b2c4b60329b891828f3d944349597222329987a78772bbe9242eabd4b3b323f8c84379941894fa1e9d4c4bac1f782898382ee2dd3cd6295376e0e30e85f3d5f63137b1830a6f470f313eec60363eea50d0edd68e231f5a63121d0a1bc3af9dcea48f39146e54c5095b99ed6ef1061f7228c66d57fdcc9ca34dcc39f88843c1c398f8a62b44c913eae0030ee59113cee424264d9d441b7cbca1d879c73d3ca87ccbe986b9a19c338892d7a89b3d43376c061f6d289fa02fe54a9cac2579dcf9c186d2976dd0f3214dc820e5fe5843a9c45f919f932c39e6440d257fffdc8e9b6a3b7e692897f253a63fa9375d521f6858d5e733f735f771867210328e30a554745eb6810f33acf91ae3e2dda235272b63b247bde893e48bfb518662887c092595b57a4cf24186c288ae92a6459d6cfafb1843e15c3fd697c66d097a3114a407a149d3bdf5aa260cc5370f934c64fa1c4ffc030ca52ed9aafafc2f944bb0bb1c0d93369e245e289a1cdf2676fe93d7f42e942e4ff89313c7ee4c920b05aff5f992cadb4249cc242711791e9f4bfdd0424168e5e7f0ec319e587e64a1a4292e565efa1d4ccfe3030be55d13df4c30a5db4fae50d2f9f1d53d79ecbe93154a523c6fc8ae3959cf5485b2c7664fb25f27494e3e154a9a5d731c2b730a650d7a4c7efa662b49480aa50bf5b318138a42614dfad9ced653531b2894da763ed4b4ca86669e50921ab6c409c51ecf1d5ee27426a9a4090551d2f4e89edb948d33a1b4497aaa2fe92ea1b89eeb7c6cf3e624490945bb8ea1e25a4a8d0c26a1dcd9fadf73fab59322a118aece4d6fffb4537784f2569b7c6e95b37b628472f56c49e2f676fe664528a7bacec963dabca1a428f04184057c0c41e443083c403e82c0e3e3030808f8f8018f3f390ef0e103057cf460e48307df23e9c163868c5d7cc8d0452ebc90818b1e3a74ec28808c5bdc0891618b92510b1d298c1c3768710019b390450264c4c2062330800c58e41023027fc20891f10a2f64b8e2060d19adb891438c089880860c56dc58808c55f050050f03c848c5870c54dc0091710a1e339021c31420324ac18100c8200501648c42003244010019a118800c50dcc8214604921d63448000323e517a514a68519b17a73c1c6ced8992a9a78f888fe260e3c13b90d18992929edf7e554e906914030c1e18e831868e1030278aa1f4c95b1ddb1ac8d8c4fa2547d1bb1d199a28e7573f5332b33157b4410d74ecf8808c4c14f49c9c756e4d8acb590732305110a7524b5d5e21b061e3ec907109b321c3122da312861e9041890cc898045a7ead31a79090218994796e8d9d993c8f83ad022118c148144e494a9d5019838e3941a2d839a86f8db974101d96f1887229496a964bd911c57fb5ce124fd291ee69847632f68695d32774198c287586f4305da98c4514b544b46bcf77f2e88d20b609137436f9ffc34a4994c4c45092b0d965645224ca25322793bd498dfa215190c9aa379de811a5d4a035346f0c1f77845e2f56629e06bd68444928531d53dc33e912f48211256973447eca17511a934d0a0fb522ca1b3cb6778e3111e5bdd499f730eaa24544144ccc15d2c4d337490e0f51b4fcf81d72efce33344441862e9331194a77868528d568d125e9587234434214d3cb65a9124cacbe7010454db2f5a1fbeae14241e0b14d127efd1488924cb28c925eb43d9e0051d2244e8b6913db38fda118fe46e73cebd969f24341f4878ecc9a44d37d1f4a276be79c3aa567233e94647c500d1ef388f4eca1a0847defd7cd6bdad143a93a863c13fb345d270fa59222639d428492763c945ba39fc712afa4d87728a78765127b73f888ed50f6f439f9fc772ed53a14f5548ab40d391b533a143f76dafb74cea1787782de183b8613a71ccae9d2c4cc7eb319731787727c3da94296b41aefe050523b6d52f64bf76ff786620c4a4bdab49d1b8aa2ac94dcfec145ac6b43c1e3a4164b1d6daac48652e8cd9f1feb0c395a43c17bffd34f29990fa9a128672e9fd79386728a075d273408b91e3494d4ed4c924a3693f29ca1209349ddfb2e971e6386e29b245d795c11e17b190ac2733ca184128477dcc8504e4ad041682a3567c23686920932c9257766f7cb2686726e55c3906ac91c4796a481a1a42abbc3e45c642aed0b256562bed6a3950c252f946fd3ae3ec8871353170a9e948a14f1a4de7e2e94f3c86a8809da427137741275d3d2ba168a16e74128a567a19851374c9236164a258927e4f8978c21be42e153f38ef49c413e6c85926675ab509635e9b92a93ac792a14654d92c3c67bcdba4ea1b06b6266769542f17c37de98a857a22814d48fd6cd7442a1541b532817cb8b27942449ff4bda54ef2896174e28cd5ec9bd23736810cb8b261445469b35318f75deca0b261473890669ad2f9bb6f2620945bf8fbd4148cdff9a174a286fccd629d234ec77c78b2494dc4d4e8368da0b2494e39d984689f5276fd2babc3842b9a4d5d7f0496cd638cfd40b2394e4d151923c4a4e5a2dab16a12037f6698ebe33f34cfec10b229434657695eb5afe99428081b419e08e1c37c4e091e6c510caad66592a2fba4deabc1042417cf5e849565a7b4741288973d7b32e21108a2785470d3a8f3f28e7980495173e28e96ddfef1eed2e6f82832d9731e0450fcae6a5d6d1fa04a56f2f785012837dcea042bcdb5d14352775b274c813f424e9a238ea34cca6b6d1594d1c6c65387824383817a550cf23eaa2e260c3cbb1a347e2a298448610fa2fbec811835b144e5c9349f3fe3c6c51ec158f5da72383f65f8bd2b7094f5567fafc4e5a94528449d17ba364167f16e53f39879724476e982a8bd29ffa5895f26935e8635112749c1294683fb2afc3a2a0edca558497ad9bd8bd22f3957d862baaaf1d17ef11ab6de264462bca72d69fa1d44cd78358518c2fbaaeea396ab8fb0b31c0c0800d24c08347f28111d8b06163c62aca6252e3a508e9a6776d559437b96e8e4929cf124c1c98918a92ce78cccb390d15c560a244878c995394cf4c9ef7cfa99aae290aa6c2dfe424c8ce3c11cd2845692dcdb4fae46c4a691f98418aa2e731e529d3346314c5f34eb287c6694ffd88a214fad9c4ca64913d27334251ca11165b1e3b1c6c0e8a82e69c6799d983fe507da224323cc9a23ba9268d278a2deaadce74bb843b75a29884898eea99e7443126a115a3c38bfc516da2602b9fe4684266cf63e160036335514acb7c4d6282527a264a9230dbb227b6c64431acc6a05466fc24c8380ed64b1463ea9cc3b5a491257e70b075ea31c68d1e6158a268258d90ef771a573f3c72809195289d385273839ea79d7d04332851d2681f7c4bdcd1d8231e93289c4cc2653d363cb889248a1eee66b2698a44c9839439312696ce270812c5d07dbaa4de5afd647a445953470fb33021b664ca0c47943aa5dcbfe798c5336e44d1d3e3878658e85329230adf49349a678e07662ca26023f3aee6789265a6aa88d2a690cb91dba446bf386ea41b3d78e8c071238c1d17989188f267ce59ea34c89ed88ae08b2f7268c0860d1d396e88c1431181c2c8418330c608c08c43f4a13e86dca0cce47cf13776e8b82146028699ce30c4692c840942b733c62359ef01469a5188e56e5566d6335db3947c3a95fc27276710a2d8e59fa45e888569508e94120315a800183c121ad8b0f1274937eaa630631025f93a9e12e3c9a64128cd104431c7e0b96df7e4a44c370acc0844d94e36396a93fc9f6d6cc00c4014664529496bfb7f28e9f89b6a6a467e28e9a449fd45c6f251f7a1309ba126427d9492191f8a9f777d55947b2895d2497f784fcfd043493e511d6632cb63461e8a1a9438a1e2263c1433867c9ad0d61dd01131cfd62fdb180ff313d54e7ddecf4962daa1a0e4cf28db8f137b928830a30ea51226ee5d4e9243f43aa34341f67e125fb129e49cec8c39147f358cbe2969dde433c10c39144bf8521b4eee3c62b5ae4008463a4330230e25995d776d4b5607af66c0a12842c838261ef41598f1867257bc0721bd9320a2e51ecc70835a5da287a8b66d28cb983caed7e6fa696743793bf9969ab1d750d0b9d64b86988def693520a71927eab2955932e7cbef5d29531a4a976dff794a5b338f869210c2ced5c284bff69ca11cfab4972841f466e76628c6b916b52fa2363633ca506c0dc29324099f837b3c830c25fb3d2bdfedc750f492e939be5792a84a0cc516f3b439c972e16174c3fcf90733c250ce63f27fcaa4fdf862c2039dd71dcc004339764eca8612cf3c86be5010252713fbcd4ac925ed85825259f24891ab49ac521ecce842f9436f6795fcf653626cd8f81d378c0633b850d01126299d2465324c36630be560df3e5a93707290272d1443dac99d9470de8c2c94b4da9d984dbb858cd50c2c14b4eca851c22f56fbfa4204366c3410665ca130ca6265368c7ffeac19f30c2b94c4ad4ebd1a55153cb94ad7b5cab1caedeeb24ef12e372a9d4185620e0d1b93d0183a767c91ecb8610373c3069882195328ed5e6596d40c7220a480f799a714ca4187d6dc4dd5b17c138572e629b3ee13bac3a6502867e7bcde2899f184c2e78c5d23c3f384ce9d502ee569467c6b424193266730a1fc79748b163dfdd8f38c2594a3e6491f745225651c9550ddaacd6586901df4c40a13f304cd4842a932952e259fde2432b52acc4042793d6cc398b4527fbd0d1b7884f2a79f94bede046618a1b4aed7252615e3e019452809a58490a22536483799418492c63c99e6eaa451b28e83ad7b241d3cd2660ca198b79db399975bb7efcc2a23baeecac60dcc1042f1a40659f7c983836d57cf7346104ae3ee1973d4acca1ae1605b3db3d54b70f0d84b70f0c80ea4e046cd0042398a7a10bd8f0dd294337e50ee24dc95786b6a4de70e1d37c2d8f16c3bbe3b30c307650fd2d77cd5c45c2a2ac28c1e943be377c7bafda9ac67f0a0d86fb28ac6f59783905d14bd764fde3c31a7742c85e8a2749e25de4fafba9caec78e1c3b7af48e1e3c72512a256613ae5c84cc6921b82847934e8eda8cc6f8e27788c11d728b5208ff4eb1a233d65a387a241f481c3d1231d2063d2e436c61955aed95c587d5eb68a6074d6de1e8917cc0860d1c3d12316cd8b0418f0ba94549d0b12b317e52399fb91821b440aef39e24d7e7cc62cfe439b88bc6655152f3df695de3ad8c2916a51139f27e1f930e225bc620041645914975fe07a1e5d48a30087945518397b6f5cf48134a1d3acc1721ae28cee77dd4f06d1f53c4c1c623475b03425a51be5e131af64b3d7a9c1505bd995f13844c6218d92a4a9ffb47f56686b19f5115e513325c26694ea9288b52c2db7d9d92933121a8404ee655b4d5ca6cc7aee41b25357aee2c0163878e0a80b143470a394549a49e6998a046976c88294aa393a444cfa7a564874a510c3a852e4f23ba4970495112740c9d29cd1a4539f8a714ef0c2689d21145b9f36a8d08a1d66fe250f4b9f162f116a6e775abb5bd1d2c93203a09142521acbcc6b4683a994f94fbd38f3a930479a22493b6dfc5d689325e5badebcd3bd9868966f2e7db7be4f8420c11ece8c1238413a5ecd73fd5b5d944c9b3be24c1b277f3074334512e37b525f5c664a2e441e3464ed86808c14441c9a8bb69f2f132c72e51fcf255cd724295142796b8c48faa536a6e2c8454a21c55ae6364100f1a4bc4f8624708259417d1301379edce248b3a13eee821860a422651509aa47a6538b9b2421c6c2689629e6b85d093d453281205f1b9c3c9d5983331be080142a22458da7c6f266993ba53d2712349d390479434099e25b731752911c3063d74f4d0b1430c17e38b102003421c518ca977f44b750e427b70b081a163474a1d290c0c988591438c2f76a8185fecc00ea4e0068634a2cdec6d572a46d4b1a621fb99e12d679220c6b488f298a8417c099927328a70d5d45b3bdbacc653e394203a942aa5bd9a7d22cafad9e4f45c5632699d811044944bae5c951613cdc1d53b44496cd1ab41f4e633fb56af430c51104af947171bd5da056cd8d04148218af199b75244bd7727e54061fc0db5d52341de5023c4eef2dd312f5b66b27552b3687889d69a64075270e3840ca2b01f83ba6f1d4f677588200aaa935e588888cbedab082181286f8a9f5025dc75ef6ec34693210410259d33c388ae5c28a9f387f2873f617573c9ffab1fca329fd24acee13f28933e144b9f3e751deffa2e3e94f44f8552eb98fa9fdd43b1c53e7d2e1d9e4ca97af0da4c5445e7f334bb6c73cfd54407f350eed7a0939dee9ca3e884e0a1a41e365fc921548708dda160f229de36ba103b943349c2698ca55f95b93484d4a1145f5672ebbaa7d66c4d21840e650fca546ebc5967aaaf103287e2783aa53ccf8985103994b64f7c92c96f3dadad33b838943c26f1537c1204877232f5a4b9a4c6494dbfa12094c922f7253c7ccee7047c087143d194a427eb97b24e2616d2869278f11a6d74216c28789ac88f519d19ee453a26d079085943f9e43a519409ffef51e710a28692ff78896947deec73728891340742d2501eebdcda77a1eaa3091aca26c599a026ca903394644796683fe2e4b7cd5014d9b9ad63ac11b1ae0ce85b8cc75a7cd6dcebe70c11a2df2a32944f9263facafb848cc15d11d70aadd592b92e440c853551bab7c46e732d8584a1a4275d9c5677e93c1f140286e2af8fac975c4249567a55430c66230ce52e39cff4cde2ea3630149370d224bf1d3d0ae36f6ca1c6178af9d5a4d2a4f642b1241fa565cc759390fe8e1e877f831cff450c6cd8a8d185d22971295fca3d69eb71b021bb1ecf034d50830b652bb5d91ba6e43cc8a550630bc560291b736e3af96ca5400d2d14370693326c102a63cc0d3e10d4c842493a2144e4ac2849de140eb61b3dfe056d8bb61d48c18d0bd4c0c239c9b9948d568d2b94a32825977f770d2b9862ee46261963154a63a3754dd3f649d951a19cee2e977be229a0253bb7976119fa95617b9a93d85c9f63ac86144a9287fcdd6826aec6bb46148a7157fed38878c934d6804249105b624bd0e7a1733ea2c6130aeb6b71a35df6caf44e2899b827cd3f4735f3b946134a3a8a1899e454956e0c13ce93f5d6d97df91cd3f18411be77b29696509add3449e6d012ae4f6a28a1245cc7bae39a3841a70c057650230925f984fabc27669297d640423189626f427d55e308d568e8862e8fa1420d23143facc8cc9a74112e57eb7abdcc26636cfc9af545846279babeef87507aed93a3b3287da7db1a4228b855ecc8f0a5942497d408424952a226619b93d40042b14cb75486fd4ea37468a06302366cd040c78e1d3a4cffa0c60fcaa19649d03aa56479df07a5dad250a725cb939441418d1e1494e812975efbab21da400d1e944256c8947667eb3aee62bb3d719d990bdd7c5af77b7d458e745152efeaa26ff22d6a3f81462e4a424c6913d46e6c46e843a0818b9298ef564458e9207ebd45e1b3efea867b09f2275b94c27fd4afda4b38394da316053132d263dd6af052f25f981834689178ae98b66a75767687c6650ef149ddff061938d09845f97369b390d339c77e6451fef826867fd8dca8d391c2c000fa22078e0978c5223567fab9d984fd070b45dc2e45e744bbd2cb3d73189525b74ef45f61cad825b4dea9b7345c916d57d69d6caa8e8c6f3a549aec5679b6a2ac61a6aad37b58514c624ed406d553e289345651182509732a553d897fd25085a55eda613263ad972e37a271ebfdfe4a6f9f8a9289faa94284882919a6818a6bd7543eabf276d3c232d774a7522ae518d0384569830ea6b2e34d539493f0b95f44f8344a51904912d74f684c096890a2701bab462919f27c6b1aa3b83e4ca88b29931fd01045496acee36626c7860d1b3680402314c524ffd6fe19734754344051cc247df292f9249aef343e51ea20229368d2ef2a4d70b0d1e0c3b8b143c78d36b40ed0f04449f80719e5f5f7f6eb08a0f5a8c1e3046874a2ec49ecef9e328cffc2ee02343851ca923249535ea2b1892cc4ae5ddfed2cb7d35d4e4cbb9e3749562fbb260a32c6309e995f1c6c3778fc0bd0df48c9179a8246268a5f1db4a6d2a1f3a68d18628c8189924c72126af3a6efc4b0c1084626e068a07189b2cb9f78e998246aff9628a77fd4b87a9146254ae2ba6a7952725ea7438982ce194a36392c1a93289e24dbaa66996892fa72d09044e9d342de9d98d7c504068d4814e3fe8cecf86d26069906d0804439e75172f8589224bd45e311c55c82544fa34c8e28e87492bbe9e91a512cb14194a7d22d619f146830a2184d091e379e67cc6c8d2183b7640c19141a8b2867d588fe6c72e4974611856f0d2e4aa672cdf8f0a0918862559e1e93a31fdf5d081c1165b5f4cc5c2adab07188d2d76a5439195ad6747240c310e52443e7136b932cbf691a8528cd276993f49de45c92922f121a842808194dbd53eec6b07ee280c620ca1e4f90f9548c10750bc26c04a2d4a6d2b46d671393aa38d8ce6900a2a4b6619a3dc367b8265540e30fc5b34d928c12e36d87fc5052b33926f5bc9db0bb0fc53ce2b4e4bd3bec523e9477cec2ed32c6d149b987922ee1e493fd4c8959abb3630c1d22a0a18762dac8e571a0918772ccdad297e7e3a1e41a1ea49ffae6b6b5061a77289f9a65d21ad4090d390eb6307298b543f945ffb599099e4fcce181461d0aea635225d5fa942edd1834e8503a53171dd7f3d3984341c5da53b8c9ac9f840e0d3994636e70134a76938dd00b68c4c1b6f392bd0eaf395711bbabeff817bc40c71734e0500ad91f4a2a193d83fa2540e30da58bd13cead3f54bc434dc504aab2e3b4d2f629bd26843e1838b3e31ed5dec7768b0a1ec25646fedda95183d6b289a2ee13d670223d150434969f61093cbbb63384d60021a6928eeb9baaed6ae29fbf4c8b1c304366c201a68287dd09ddf7162cd981012689ca124425654c932b21f7f339494e8bce7a5d232e89c32949327c9d3789992544b1a642888eebacd60fb18caf919da044ddb7693184a5dadfe260893d4974d230ca5f84df224413349393234c0501ab14186a8922ffa937ca1e0ae9a9468b6bd19935e2889995cc4c93276a12423ad3389731f6a7334b850f6ff5e3b6d72b650922caf8493c1440b0593fbb34c49338d2c70d6717a6d7195a33aa31ac3abcd9fd81dd3c042c16256e4784c3753ba4269445507599b3fa5c90a05dd9264b35deff27c15caf994d8ee87986ca950ccabfb6be614ca1e45989ef8213b87a450b4bd123e959828945e47895a0d140aea614b284f3b21933ca118abcebefcc398a89d50f0f88d29d2e4778e9b50b62b316fda6542c193dc7fd3fe49277509c554f3276c522b6eaa128a396e83d0984c42514bd4202689498c67262494ebef4fee4eaa4fcc7484820cf679e2223342b1c23d4cd6782c95a52294b7574bf4cd295d9488505e4db22f47f38c300da124cda528294b4228bf75ccd87fcbd43f0825d91d94d5773aa9014241a9f6e0fed7a34c921f9484c8ef0913320d1f94844ed2274930e119b34fa307c5d8367aa39e7c21dda7c183725032aae4a833aa94bf8be2db98bc28c932d8f9ba28e7bc51837a3c51d4f7b9288e188de984d8891e7b5c14ffe6657eeebf4549ea3c1a6d741244696f8bf2bfc6b7fa2bb15e7d2d8ab1ac24e93f9454f769514c5533db51a6df836651509214ca7a3c7dfc8f2c8a693b5e7d9969824c2c4a1993fc96c69b560d8ba2be8e8adacccaccaf288edcf9cdec7c4a5be48a829bccec494e5b51d61621abf24d4629112b8aa167527df65c32c9d02a8a192a94389f93652fa48a721635328f189349552815c5367965649273f628a1a2acf9d64197a728a91236ac4ff2763399a254bee124efd469eb2f4541e53db74ab8d5d1214539bc6c93f09c51144c36396651f392442e8a829da99f47f998361d8ab2065da5552a22d406457937d7dac6f6134537cfe9749cbd3ed1e389b25a491a2a62b336783a518c39c624a7121f54698713250fa2fce4b2b38972522635c68c2921aaa389e2e69264df48d3a93f992809fa4c14f99375730613e53fcd691b42c87efc12a5eedf12e5b8cd72a564182f75258a9a32a9797c261d6d4a1444987c728d4ed7263c89720921b73b4ba294bfda952737d3e34894931866f9c1a4657c2151d231879809327e4eed238aba7dab49924789b58e287ec7efa7dfd4fa41d388f2859d8e49daded6ce30a2e01fe447dbc74eaa338b2809b5e1efc4cc28a2e4a6cd437c103f7d22ca1542e6f79079ed4b88285d6b0c7f9bec83e70f51fa91214d8b6e88e28f651247eb4294e452e61d4bf690d712a220ae7cd304a5411437893941eeda9928055192e4469353cb768602516affb0ab1afa840e204a1fbf4d4efaf34e8b7f28c6acd2a93eaf32c3c40fa50c427aa8d25d5fbaa40fa5f0a4a26fe743e94a7cab0b795286d31ecab3a6c53d7af450dcd78d5623938772caf09e7d357828c619d78bbd50a7277728f7b6e6d01394fcb1dba11cb3ffe19dae435163365fbc9aafa6e9509af937e9ee3994767312fb94924ef8580ec56062f298c92fdf1387e27b0765366a6b533894b2e52441597b326fdf50ea6825975ab790d70da5902549c22a6d436934e7a05c83ce86524992a47b476a7fd0f91acaa383c837d550d21bfbc4b498fb501a4adfa7b2a439b193248786d287dac9224728bd9fa198f719948f6f876d6e86d29a289e4aeea94d4a6528a7343ff94a5dc7982743d9c49e743b317a8dd0184a92fca2e25d4266d088a1687f42c6d59230944dc92453530918ca1bd4043d1ade4d5f289a28aaf637f7c22748f3b8b1ee42499b12c4c9b3e9c4d65c28f50959f939e2497e0be5924ffe31312bd5b550d6199d44c427412cce424989a9b664ead5742a164af2875299f2b5be79ce74f41c6b2b144ef2989ebd949bc957a19c537394fa95e8ef53a114a73e964c2568357f0ae5d2d36b1325a78691148a2649d6b9e3319ae944a170233249d19324693b50287627615c43c6fc3a4f28cb7fb80cc2e2ec354e28abe79333ef34982c37a1a03f8dd6bfe68ca26542d13aefd56f846a7d9750da4c262de5fc93adac8492ecfda4f308377b9593508a9fcd179b8f992a23a11cf77f3a4e6af40bf908454fb28b661236c6f0b1118a1bbae46a934edc675c8492184d51523768b513114adfe16dec4d4328eef7c793f75d3d4808e5d41e3ee506654ac907a19c9ad17d7c45a7ea81509273e14992fbfee4fca07062daf9894e268fc80785192b49d3857a5090f9cd04a53909bb1a8007c5d4a724bd739e2c3cbb28ae7e5226c710a69ad6454909bae4291f531d3917a5925f8250e2e2a2dce14fb6ccea1645575169822e37b1ab2d4ae341ed95d61847a9b5289f98c4d1266f8cb89416c56c42be85df9b92e52c4ab79a04ef64f24939caa23c3ac9cd41e71365dfc6a2a4043d2535f869cd6b61515aaf6b5f517453a5841ef31451724539ddfaee4ccf4c9d5a5170cbd50e1db1a2ac77ebc1eee374daaca29c49d2a02594a82a4a327c8c6b9aa7f124d15494836daafd7c6aaad48a8a629a31d998638f785b4f51127f94dc50c2735c5b4d51509949f81b69294a394a0e19aff1b42949512e71849aec5d73334751ccc132e68cdde3218aa2a4169f9379c96b722c14c564e2ab7add89b22905453909f31b19e97f57e9278a9ffe3aaefbed6f92274a7a7292a44c754d893a513c41fd24ffd8f9b5c389a26f971a9ddf5159b28992ea20ccf3c91527a935511272bdf94b0835279d89826626b1b1adf47c1613e5249468223d989728866b0e26274f61329a96289a68aee96aac44e9839217b5f723b651a21c55b34a84d0a6cfca4994bb28ab283531969cedbfef115d94bdeca4d970ca4559ec838c14a5ac379d8ce0a214abe7418577b6a5e71605a99d63d61f6dea451eb1453193f28cfe7bd627ff5a94d76ae4c6d1195d7f97162549369d24362d33cb671665b359f110b21b914551c3fc9de6fa3f6dc2b128bbee85d0d599bfac1b8145c1633c4166d226670f6d478ee4033678ec7803c68361c3869d2d465e9123ae2808f55f9abeaa2683522b4a9904dd4fc2444cde20561494a0429b18b4fde9c657513813369bd2d973a6f154515027a388a92895a4dbb3b7894f0f1d44541464746c9ac96aa3521e394531c91aefecb5842cf970c414e52b5153dd7f52bada044b51bcbf3713d564c708f12129d80423a3c8118c88a23114331801457ec213c5f41d167ac3d689c25c2633931b13f67b4e143d6cd0a0cfc524edf3c82690ddd4f0f8aa6f35ef583f256e43cb114d944ecb96a063bffb7a6a2413a5d0b03ee2bb644e5e61a21c7af31bb944e94ab6d0321acaad114bf09576d9ee65eba66a92d5ec9528c88e9ae62739fbca0f8c50a2e0676a3acbbc2393b8114914f39f12faf15a1ee41d899287d41336268cdd092151b2948fd7be5d72d09b4794b2ccded7dd833ac984c388238aea269e43ff6aee5c8238d28892d84ca132a94cf6261c6144c93be992f7e4e360b341caf1851860e4e0d11d185944d1e7fa2d33bb438c335444e104b73c69f216a69388d28cdaddf14d42444184d22c31870f511a258e90676ba6c1da1143980d5cc148211e93e2a45eabb993445c821142fc26f667d930eab29141ec2e969edd62f5181144415efda74d82692f493e10e5a827e856f7cd0820925f292ae546e90f25492819de33444be3c70f29e195e943e1438951f5c45877970fc5cb9362b36e4acbd9237b28fc58597689ee9dd1eef4b0897c501ecad1c4c9f82763cc9f9ec3433107b1b94bd9497aaa8fdca174aa45f6843c7307237628e82cc97666b3f3c1481dcab2b657bba2c3fc83478792322badec9cb93994e364ef9cc142d449e264c1881c4a997b295a84e5a7130361240ec58d252919a539e792cd08237028cc87d224d9fc3d187903afbdae26c67c8fb8a1a0b3c9f8cc1d3fc99e431869437153ac26613209cee046d8e08e6d581555a23ab286921cdba407d9d89cb51b5143b79af7ed961a9ed6c43192864b96901d1a966fabee12da66335281108cb41b46ce50b24d328350d649db09f6182366182903da1821039fa71ae6ee29f3265def9a3ce2c47d640c6593d36e6f92d6c4a62d8652ecb5981c93acaf170a4341264f1d9336efcaaf236028c9a73ac5d89ce8ea170e16c1c8174a5246ffe05669fa3eef85a27e10bdd6fe98d9ec42b94e7d759f6ed1d90c174aed576b25fef9e2b485d2990e7ea561fa1b265a28d766dcd241aee72877164a9ec5635271b7b12363a17097fa5526bd57289a92a9d39b10ad5012b3fd1aa4dca8b60a052fc94ed034d94f7b2a144efa966bb69b35d3a6502a153eaab97192f8fc30228562973cd93b768eba354561338fff42a1709e157b9208fd9f33234f287aaa9bf1d8d5096cd291e5ff7b134a1d6a6269b76a8409cbddeacbe9eb8a7d8a6b89d73b9fe498ee5e42d13694c86ed59524292d614409c5dcafff31e96492768e24c1a0fe5faa4e8e50184142b1d364e6b449baebc2c8118a1b839273897797a43e3762849236a593d2307b79f7f148114ab23e982925aa36ea384284d2585fc9225f5485cf23432808d59a642849a6fe6c1242f184ce073ff3cdd8cc48100a3ac753a745dda62ed9ad300284c2776afd24bae8ee07851fa16ee207cd49e7ef8382a74d27ea97181aa79484911e144c1abf3a19843bc6080f0aa2eaf7c41cfb5d14f4cd262f5b134a64ae8be26736498ee5359f99cd45a92de7f326b933b22a2e4a62b8df51ed1de5a712b945490942875082b6cd85902d4a1f2cf4cd7f64ee7f6a51d061a3dc84cc7552fb0644685192b328eff469a429a959306fe9257a27f671f949e65326a78af922b228fe89071761e235c6209158143556c678c2a8cba4be082ccaa7d36aac0e4aee15c513334e30316f445c51ce391d26b6a6d912cd3088b4a264aad304a12627fdb2ce0122ac287fd44c9962525b7fdf9123d90544565192ab6376d6a0edf617dc0823078e1e3968a00a2d67752e6b6fcd656436cc4fc7694c1e1a839e53515e7b3d9374d5ff4dba23e971230766400415e5cdee3de14914f17bf048be103945d9f39fa989f224746638d876470f31444c51fc53626ff7e273da064b51aad2eea04d521435dfb8c63c7a829ae4284a69274234d7e507f98ba2a07a6afe836c4361366220028ac289cbc8a711b211f94439ce49d7262e643bd49e287fb66e10724b7c484da41385f5ac103ae82aa15217e144498e16e331ab854e318a6c8299b9d44a11d99ccdb44c4413c554a29953dd9789b2ea68312d9f93369b98288608b126e8348a5ca2b8361fff3b95f6d30bd32062898209626af7e4e7e78d2a51924f5242de2749285152b23d6a4e329dc9b74f22d95443598b972292287ea89059f3735208229128c96a3bf22b7f73494a9028fe088d2566dd3ca2dcf984e83bf9c3dd653a7a8481011a8838a2ac3123fbef4b7b10adfd40a41145ebf2b8d3e531418411e59e171911f616729e0f4416919ca0214a6c47498928a294fb27fa2d94321198dc77855766c55c6bceeb9edc1deccee4560411e5166562921bfb8312371ea29c49f48e51729cbabb0c516e2f75724ff36fca07d520528872e824f96ea9f8159511a29894a6d233ef3167dd340522832828494e42ca7f923e87f14e1025991ec54deeef0251ae0f5ad7b4e8542584778028fe8a12da6392941cb3fdc1ec7f8df1ccbcf343390993932842452757fdae0fa533b532fdd96eecc4777c28cc95ee24c792deeda1a8f15bfa7bece4fd54440fa5cb71d3b177ac83b5792889f268a7273e896a720e0f85ab59adcf29f363d3778792a4c44ccfde2c33b1c3b34341bb5ce63d2fb1c4dc89d4a17c6da1ca3756aa79121e39f43e20428762c8f8f62f42a72a41bc39944629f9b03f2336f3787228accb9658bf265e1c4a929041643e310927bf452270288fc688a7e9d9ea92b4226f2849b2789b657a720b226e28674cad501fd436943aa7e7c9656a8495a412614369d65f76664b759c0f1544d650321ddcacfe53d69d3e89a8a19cc5d7edee3715494339e3cf6bc575ee13ccee400a6e84218286c28e0765e9fa1139434166b37ebf53b9cf9d881950a40c05934ddcdffc25064d6a32144bc96d597ff22d273581d383c8180a9a29e48c12f5c3765a440c85d76c5d3b4a8e2261289668d27b3ce638d81a0322602849ffe757a667a54ef603cc43e40b2593d7e4757c4beb9879f4f1b0168878a124bde7b88de9d99ae51c225d28e9ecb877c2c66abd5c1a07112e94eecb54dda8fb8ed6f0e06128b285d27a8dec924d180c225a28e8c92ea94b123aedf9a501912c94b5b4538a52dea7bc14c142399bd2deaac10425485be40a2549d85392582adce1011b36c400e3b040c40a05257bf4fc23ae840f954815da4c9dd763b21a895081335d9e414d9255640a8eec6e8f14ca79212e645ccf18b5fc42240ac51ca37e3cfe66177d028572b54731255c7acca07e4231772e27943ce80cbb9f24e917db5940a409857dd12d4966f9bfca608830a17ab3947dcdd239cbd72c313cd54aa6129125147dd3e54ff84a28acdfefc8344a693e4942c93599768890dd7c915092a1dbb3a55de9797f89ef2232a5958c505e0f5aa684eb4d7d11cae1d4854ce2834e9f438482fc791ad1e8104a26fa68d026be108a25f37d0c93427e0e3f08e5246d7dcf7b2014cb724cc6c46bdff1076533655d562be2e41cf241614e768db490480f0a226684c8ad9becb0080f4a323ea2fe539fb07917a552be1a424df57cd545612aafd3facc4549a6f739d7d613fec34541bca90efa96994f756e514efda1493ca9b145e13d4753a29c24b5f0648c1e27d3a645395a8a8cc9369d9999cca2a065c6c4cf95d6a04a64513a1d226428db5d1bc5a2243a9689113ab0288d944f9e4208fd12f6571437cf8cd075dd1565f390df31ef924bf556147decfef663b4d6d459511871b25469851032fa2a4a7232cd234c9099c97255944db2cc9ce4f01fb1e9549444c9b32fad4145517d4edc51b5b924f514e551527ec4c64d62dc14c57c9e5b4e92bd42c552144e09b71b45775c3b5294cee3949bfe1ca5598fa294d5e7aff7d93c8c4451eca093fad78f419549280a676a2a66c49a691b14051f99f389a727c9f1e24f94cfeedfe3f224a57f4f945d63f82fd5d94e14a3e6cfa03d09270a6a44eecde9c9f0d1dd44314952adc7986cc7bad544f1d3a9ee6c9248593f13e5cb36a5fb63a234579f35d294c929d64b947fecc7b26489a295652653f259899270672679e7786f9ea4444134335d7adea0ef242751923a7549943bff27f7d327a7b52351decfba9672db510389b29cc7d5867cc7971f51dc92bbabe3e667ae234a4267d18f5362921acc46940411ea3aab456b49624471d683ca86e60f9b5f44399c9be9a03db68b4811e512774c29393d7b73228a23c2bde4641e6737444449c62476795d7aef8728fb8a9fa0f2d349526d88a2492db13a25a8cd72210a222a73a14b559d4b8882bfc6f0d9fd4b65378862e8514b1005d97ea6fdf54014ac476485d201446134cba7388dfda164ca473c67f6762fcd0f2531b6fb3a96fa50ce37a99367eda449c387b265faf43927254cc8f750cc9fad348d5fd9460fe55375adaa2629d1da3c9467cce46c7a25e58887e26ad09f9e335668798792ccb1493a41e7249acc0ee50fab1697b19baf0e2521b5dac7349d5255d5a04339d5377c38d3be1096f1329e1839149428dde49b9f739e588d3814d49b9cd5faac010773418d37144df447bf8ea2e4088f1b8a39463c73d0999385ae0d05554aca3dc2c83146d6a0061bdc1cb76a4d1bd72e9bf9cd24c68fc16434a14f4c75d45843492849497df2c3be8e5f430d45f3fecdd4359b7be71a6928fdc976d2e875dda9c5c8ab81863b43e96e2d737d96209af366289d184bcc24e8e0563aab518662f270535e62ff39e56428de8892f38cb410b163096a8ca1f8977d67aad4b5018f24a8640e6562025924120ac40171281406a0712600831308002040200c86a2d1581cc89af803140003582c1e32302620242418181c24148905e25020100804026120280c0a8582815048489333ed06ae7f9f201f0b1b7d8f3a80d18e62ace9834754ba3e71f98670e307b10ec0b8a9cad87d1160db42b39a5309ef5650d80ba7c0b03042bd52813c323dfb1064de3dc3bc20b785f08ab2209a0db8e758ddd450a1f5e8a847fe29966f6c6b998d8bd4251b2d9e1e8a38a683ba29e4c9529af469e5bed2a639ed7d45d7cd2ca96d9b4d342f5ea061ca2ae024fe5b42e8d726dcc03f697182977fdba83dc9f834ae6a1497e94db70296dd3861e73c44e1f1310ccd2f1357816147f3523130c57d68161c722e6b929d52760fdbae4ded98217cca1b7c309d5482d229e1d53994a9d944c89f59bbfd6539f6fc7d629644f8382710a7a921f21e3219dd75e80fb1aca5e4a95c310fdd181c98c15c6dd10e297a1b23a5b65915c4700bcac49b4960a810b80d521adebd3f99043c72b98b9af09cc16e9c72cc47cef328439b41ab6963e5aad88397817e45881249dae04e0f5c8f30aa1c26c1b69ded1e31db25951f65e3e8add3883f5ef8bc78747065be5437ffd8be32cc6a4d11e84b27c493f7917092b17c3a65cd519584679d1a004e12c0f698cd9e3691a8b07092d1aedc078eb2b272ca83cd6bc26b237bfbee3ec6e10ab69bc553f4f7b3e46bb7ff140671e526355135db51e5dacce09b4a5a2be1f44477b6d2b25c0f7213fb8fdd7388e753ecf7e6e6ded90810eb4e119e2b70e4716ca7ae0a39ef0873108ed874b172ce0db11749d423d925338af5b340f4aaf350149a2124f2ed5311cacfc93a846e2230b45e048ed7b4c9bc4219882c9fa1f830c1141d2b50451af953b068e491ad41d708d772adfe898969fafd68076c6c7d4dd32d59ad797a00090e910fc34fcdff167cfc91cc0b21ea61edd88be722ed2b545e3b26a0950280e882316f2fa14d3988690a2b4d1b5b41f609903e7108deb967421add23f268380c39540eabb110a0869d82468c39dc6b3ee50fd62709c883ffba218f845ee4a94597cfc617ca49c10b0f7a211e037190387c555588d0f7c44bd8112de7a16c5bfb088afeaa08701d7f4c336117eba0852f87f050288020304c08a32ec54c5eb8ce95daa72096e01681058a61ed65fb48f0a2e810b6f5c59f88823b0dfdca044e61884fbc06ba3cd0f71b3bd3cdb716a006e6aafdc16a1d04d133c00263bbd48ad9cd94485eaef17cff9d91991f24093e6a1bc1bdafa227b471206bc7a05cf12559eb9c7dc2d9c910f122a3326cdc8b2e9d5cc6e341098c651e70cbb4935a46f8ed2cfb813a42ee643d367d17c9913012fb02e18a0b0b59921ba08a1d6076fee4a4c52c818e8c1c5d569acf8039687fa8fc41a74686998430a3351c7189d10302fbe08a90dc4136b60f999922eaedb419df9a6f2b1b56a580821dadfeff73a3c8fd07292c7f425a277aa900752c8103113707a61c6b691002a7686a509846ea60ed984d1993de70d8a3ac329a2f6cd5223a15b2228b913362a36bd6472a0424af3f127ba5bc37705cde0446269a85331aa9c3ceb25d15938d1ef3a214e81dd52438ed008bcfcb4d049fa5239c50c5347b5c02a2c6d1cf03e535bfaa70b2a82b6a2307d6e0a619133d4fb142d649e84ddf93609686ce9bfa5478454089e28491248613c7657a89f569a7299e1cabf810ed9a64893a13e031642f7ba12cb152ab752abb22925d1419e0b6a1c1ab8d85893b340b9f4a756879dab7993c2fd1e19f70a79261384b12f6ab98918606034d6c06e6a1c99ab51426f2d2f10dbf151b23cec4aaebae025d7d9544558f1ce903dc2c86a74bb5843b079da05cea739f0d36092c5024c8480aa4e4f875537c847e178097ae844962d4cd6528b89a367cf238c3cb3484d8028d63fb29a84c41cda42373da3dc18445a42b7b0150ece2e387afa2dfb2470774e5ff11a90cd2856449264e5d30c290de0f3ca9d99314a81c9183350223d1ba1d76b128044a151c99a296a4a099504d1b25e8e24240a13bc84ec6244b135b2b80b28f6487260b786477c1f33b908548ad42158ef599c6687c9f009b33d6c1f74990a0752885f8ebea3623ab5d5635264414ad470531212d140660c03c6f76b1580b5747036c97f4e45a13e14105f46ec844f56607eb11c25410a8a193a3a8de3569d12bbd0126b16cf36e22c08ed74203c40512ddabb01035a23f360caf18a62725f58d31483f3a1695fbb57abe17ac4f577e28fc5569c3248143ab3378e124bc99db35522a3ef0d473c816be5431d2861d2e22f116060c3b6384c750df416a30bf7701b9c8ebe2f8253541814f6b881ca025d70e9c0a2288dd192727e46455119306c99bb13702bc46cd93fa3d50cf9bc310cf794f09c4dc0e63aa6a60694edef9210732bb3a45620eb07585cef2e29c3c50281c2f57891d3a3efbf831bbdf9c1a14c0f23ea0afa245c9551335548ead8b991c47a89c04a0cefb8c3610bf4fcf1daa71c576b4418e1a57eb6b48a9eb5c5dfd224869e9020cccf40dd1d25ec0a74fbc5da18eeca22661184cd151731c268f3d9e04e7ca0b111b81f8975e079c50c587e835afb54f509a6c2806d28cb596af4bc505d827346065aef3277c47864fb8b6f28e8e639bdfdd1c0f6661c627d9ff72630ee329e9358f12303fc96eb71439cb2cfdc193ac87c411829aec8248254d937e0786cbf9fc5bb43985ab7c87877e9adad146a8d29db810febfe30901355ebf0b88ded289602708bc6abd8895fa4ce25f63f35b5f0e9bb08c6975f45005a1234bae3019269e893cd62efbfb6c14d060d2aab8c5724fd8e0bb77c6a4bd18bb3e967743e174974782fcb13eb8b530889428b57e06e58bb03343aaf657b96c278954c6e15e7a15d77b4b25842a20d3604c36027934e965448bdf8b6ab208d0a43004360ba51617ebd460eeba8f7c82ca202505267e0b6a74b65455c767180d2bb2bb7d5a9ab6820b4d979a968c6e478d39e3874c758ae64a8f6bbc866b19e4a44c869e1556cf6bc17c044a6afea4fe081898500208073c5bf9795b73e6b4dd2bde6e727077d74b4606d8b625c5d59943f30391c830919e09528db1541eb04a76f545efa12863209a2d0f38c4fd36bc6e6f04493c8fbd502663d2d3b8ef55d250633fb7e353ea9a9df45941a5b5ea8b07c4e5859b132ddebc1ae52d1e0a3fd79216ae75a3d6af9b43c0d1401f830a501874cefbcb56429fab3c4823ba7f93da4c23b8ac4f07af7284801eac8682561f99edf4d6bebbb37aa7a15e367aa757567444d47fcdecf6f7f901232d87c3fa189c443c52008fe3de1884d43209f70f2293a8f9968aa9f30602d39ca27112da151532940b49a965920aba0c161844a8d6217381dbf2bd346ab626b4a4849c259ad7e2ae7ac2d0bd58d75f6c2551eb04be18e82c918f99b7011af0f9e54057bdfa47f8b28a760e03e9c16d7ea528b220e317f8cd6f227c94fa91a9817f7f8c6048e1b3e6f432763f0921c63826b5702ccc09cc0eeec6f98e816b6b9348be316dc43bd619e0f97eb4e7e5a3568f0782fb323c19505d0cd1022c3ba78d553298dbdeb4ce135df4b796cda21d27576639e9ddb79e25ac4f7ff206d1e95928c1b79b5dbeb4781c73512df5f6f430eb4c6d2d54d03f9851f66e2a6245e4c04f45bf894775c3d634eff54c10dddc0c5cb7a2b6e11611abfab9cdfc3aa8277c1b38036db4b6be8beed9624a8231b86306310a44bbfa121312a1a0bdb42af1daddc42bb7b1acbf73c361a3e42f7054cec7993343cc304f53eb9937a750168866a916b9ab9d3659343ac236a1eaf2f694ae1d1e5224d3e0c0395dd1dff3c0b0638bd1a3d8b9b77994d2e810c636bb94b2a043a57e7dac1046f2436b43fc8ad12d72c71a5e81b5366ab50fdbf7aa24a209f2a147044105d1df584fa6d7bb9d4f62f76c0acbfbe2ba4c135e066a8a383839673a7d5c49d18aa7b1d091027a8bf8bd85516c22424ae68482119086cd45c07cf8b4416cab3926526bcf27fb56d3cb6179668a9331d59278395cfef33715242da35fb4f37566728a3cfb7ec4a82d7293923985072353a65e497cf1bf0879a2d956629e1274d0af9b0057723bcc66e6612153c149340ba85cc7c9365e4339a58b0344308d734e0165d8a7b3b96a85cee8348dca37f064d662926dd6f911e3d2cad448085fddc5048209d1a6f95bc9f8b65520fdafc83b8f13462cd5bbede0e6f8be6789fcc4523ae803d0c030651659e7bde1aa6404ac69f18d328f419fda1c3d5351b17f6a94d8c3d0198b91c00071d156bdb1115e0c783c481041761f2afb2ce2ded52716d7399527ed311412b525f8b275748f97ca01c1a4913056ae56c11a495fd31de9a52cafc52ec609832a27b4a4f6241bcad3fac6a8e7867424b7e4c16ed8d003d5d3da95a4431c74d3d48b467252239dfcc125386e6f740124f27eefa7828c7049132c7c79b274befe0365938b218386378c07c8303eba3e7d533587c5a4153c6766f74afacbe05c36fdfd85f32558dd76dc4d94a81cf01fd232415aab7f27155c082a5b7e22bd2b74742ff4c2764c6fdd61ec4ce88627e5354bfe7f687eb11b39293201aa0f666a7afa4dbeb3b5f88d924c6b91a9cacec5db7d17c45fdad413bd4bcf4916c90da46d1d9f823ef260146e8aa1d050827cb55504790a10b530327aad42a76eafa4a44bc9dab146e993954a5ac7e80ba87252e4c041b48a9374a26e55648af2f4269400651a13bf43d9837901c623491d055b0ad1af7e325de0f41a933d43afbfba9998466de5e0fd5cd1ebad5a1ed9ca456c14edadfc90edb53d6e52abb1fbc3ab4726960f4407a48a9c5b978b57d292f03c29e64ed61803fbe6ff214ce4e63b9b03b3f425c06d63619a5f88600010c845b66261036207f89a37392df466a8fa878b9469918a00555052ef91b6ab29fc03cf52c587345b5be5bfd4ba0f4c61bf3838b75cf834418a38bc9e7db36ec1716670ae4b059fe168b254e625669179fadf4cb711d31a847e1a344eb09762ade307a5bbe5f40b4ac0db0b5773fd1834cb17f15b58680e8b00c6993493d2433e8d6a2059b970fea3041a8971d9e688eba757599c69b0973bfa2c3235126f8b1393ebb7f904eb2771d398d618a57bfc14a94009e6e78d41a9eca99098258c93944918e2223e024b443cbe2324c3fb16814fd164305e6c0d4c4be31c731374ceaca0671ae1ee3b62dbd4b923c0b7468e067cf29a1a26816777b6eed991d2719249930c323f8e973f6c74ff85d41504908378bff5161895a4b926181542d8f831a8888e28895a3f569883049c162c3d8b07f0c7926d88818ba6449ede8105613bfc95edc8722eaca428e9d36050720b210be3ff17f293739244b06682e968a25ab9c0a066f2bdc1ca5d2bd96a5585d6677985d8ce683cd0118a482cafafde0aba890c714b13b9d90b7c780b36ae37210c4a8428024d16b09ab1009e4472493f210a38bd1f7e0b5cb12389d78ddd99bbd04799dca63a9c3a23a99d9cb9a77903e14b6eee6e1d1a104d3eeebb7c2ced824a2dfb8ece50c6b96a2c3205dac157ec4f2a5658688165c57498917c1e9381d15a98fe5fc6bd92c7c193dbc5d66ba608dd550add49045214b5bbc122d9bbf2c422722780b526121d12fc7add7c0b2357ca4a3ec0151a60aa47e7970394da561ee58be47805d22a8e05716d7388f43290cd44c783deb6a13866726b155e7136313048f03f9c40846db63e64375d47fca9067e7a0e767efb750c2e43de0f03bb29c802b2a74e04ad9e77c7e9c84fe81cc13c33a8d0f7539f601bcd74d0c50dc4bfb31d32de49da6a5e592dea365f801bb4ece1c250518e7c5286cf09f049a0433ec34004e4209dea36340ca8001c03cd01dc1ebc563aeb7c86b1940b454892f9d8ea085c3646978c5a6e2e009c3c42a538f67d92069c874ae9685cd26d1e551b2ce4a86328def89f84890f498a8f173751a9b39bbf930782d97994b70827971f25df66164b70cc67daaf71a780b51e776b7fd82447238f045a36f027da6048bb3689a6b885318c1a65971bfcdae03f666d77acfd64b5dc613cdb8008eae263f3b57f72d463db364a336e1f5210e8d14f3222dde398e6b6718aa26c39df05460c87dfb6fdefc953e63031e8d72b67d11a051361248c5fa4b37ad007690fc23d140289a9f264f59cf0476d4de5450be07c49fefe92c0bbced171a28975a1b6535ea87e215a7eb3a3feca5579469334828e06e7e67bc8f8000d8c732cbc32376efe9a3ba7e9829aed5c9c5aaedb8f0498d605da6e3cc256c76aedbc0518466bd5bf22bb71248bc7e267385190d330be6bda81f6bf3044eddc9bf54a1c33e60d00814a437ca1618e92a95d29317508573ba31b38e5e78e6c7a8c4a92dd6f380677df594c9d383fe57e4ccf3186430405bbcf7c87ff49db64c86e50d928cab2273d083f6fb12b79002a1cd3038746777a93dd06116905fae7bce04140efa2d96f9cd7300c27d2725f1c17d3e6d77d385d96c65eecff0f726249563a0454e983243a1931450167793561dd9ad72bcc763f47c97d183b65e4e1581140c943538228580c7edfc721e18c23dd59391f97920e7bb9805df6e9b8471a76999f202a352f1aa3f4d118500f809cd5c89987fbb8ffbcb524476daed24c9a22707651cf6427e4f4912e7992d1aa64c0c1566d234df25b515e8cd33c09e7f162583e562b2d0e6b2e3697f447a0b4a82cf37d40fd062df4beddf37332ef032459c2c7a93893f707efe33d22b450982f2952a2919929bdb16a92885a76d2ef6beee348988ffeaabe8d5fd6b372e9613b8eb037d9065ede4be7ba9c88a99124a5943ecc14d97a2376b9fefc3b45ce567655538b8cde82f9fe29b4d1d3407950a7cd76e1414968c0742a15212c2ca71eb334a64865ccda0f9cc28ef27b7b0f6b337e9b34ddf9b3d88bd0fc680667571fae15f166777db285ed440af9d10c2015a932cf889d4562c5afc97868e64890732a5ff4d290798541906a5d3c0f67fe122e536c4c1bf05e8b2484354d9f79a554e511a18d7cd71a07c0a08db237238584f2d199e6295a83a92392056490cf53979dd83373462e8b4b791ea7f3345c8017602a24450a0a435da4a1eccd5aa192049f59a39d9e4faca999896b91bb41ea9ad9c66dc549bbd74d41caffd4eeacedc8a15570713feee419b80f0fc1387416c2d2c2279309e194b5abc6ddf32204670c85dd79ca999d795c9e3f31d211f1113bab7c61c127d674d92eae0b7486264caa54d8ab7356cec799bc202e952ef290acd2838446b5620cba6a73feb643bf14f263be16b9c13a943e5b30157a80bc523274c797c65054d12922139e54956e4794a9c122227b12a6a7444eccb44af657f9f806b6cece1dbeb727f3ce3d3e47e44e9ccb5371392fc3693c0b3033c4689b75f30d8780e800c84ca5a8d089e952f93c116794b60359b5f3393b0df7f1145c78cb9014d923b5c2b1da002bde4dc7cee49f2e0ac3dd3ffb5d8423a38a45967ca98c792fe7de1856077515b9cee431e2b0b779d7d5ad768be74c72bedb458a4998f565b28e0a15446058b3291a44322deba806627e464bec0ccc55953708de3e4157372724b0502823dbb71c21968418d4c515f66602e8f57d946687700ba9ef257415d277da30137ee9261a214feffe36cca5ca64f0a980c12029baa911202c356d72056895580c708fcab1e44140f6c6a69b7e546415acef42721c703c2e0aced9ccde0b7eb844b69955ca4aaca46d66458ad8a569416fe802cd8a909e414cc8e45fe4965c8647db394c9dd2d0e2ff0104502b9c1ea684d9de09364c170e69c248d2cfdb5277e4b40ba5859b6f99c267134ec403311eb0d638864adb11b666c721d98ae7223827262801347364aa254f37c73d1b099f62d018ad1e1b0fcd9c22f84529600fd1813ba853765cbd40de19395453b73ee0cee7f06c410157a2cc80662bcd7c8bb325f9cf4be644d89a24b11d7aa6c46f48d9c03c2af98969571e27307e82245ae67df3d41cd53448673461594f3156884669a9155320098c402a58b478f12ff2f570a4be08b9e99668f20b80ce55f1a84311e08050b4c0031ad8d26f18b866e8be2d9600e3ab59817057a5cf1680cc7450e6dbcdfb65712335631e0ba1f295fbda3d1cc6e7484247ab954bcc12f0ff4de5dedbc955e2e54d78fd17091714f05446565a02b423c801ce933b64cf42129ff431deb3a0db6bf90205d581bf70b5f0d194018d93540e6e70a686fa9de69694187d1c515e87a603d30462f94d2605c118022731e43335aa96db51a405f6134a9a09d2e52d2fa5562b41478a5a1154dd0aceeb172399856b3b0fdf844b455a554b73dbc7a0a993f8d68278b57216f7236047d6313d7c804e4cc894cac0f67c53d29fe38233f19ea436e2bf7ba7a3521d3ddcb067545658a0233e24427ad3d30725d3c40e0191080059e937930a711b48ccd38584ead68a389f03845245f35c4cde54f8719b4e25b1583a5242ebc15955b57df2a52ca3ce2d81abb0fdb6c2635350445f296c679caa93fb815c0b663e9f285707747ef729ef35c9709a09e2f44aab8be0dd0ec205de201a1544d761d3ddd2de46dc2c1c81b02e1907a121dc0c7d4f89ea3864df123e2ee380330e9b75324e8562d65fa3e1e626e2f0b6adf8f7fad188eda23895e9e7b223ce250c14f8eb8e91514e06a76d684e620f762a4d8cfa3910b9a39d989cfcee708ff1b782d5ee463482d831ab6580203653a18ded8d10e288c292b46d9ed37bd26345a3aa1a893221c0862a1ba77a864fb42b6dc0dcda3df8d813dd45ed404ee971babb7383adfc67b0bb5f049e39dcbd080c8d60a4d49fdec6660a6b23b08e10d481b790d261e9a87034879f7c96d08591de1f3e4b9914b353c6ec68554df004872bf238e65bc783000ca7d5b7ba9a9bfd5373c09401ceba3a4ee3130612a89a1027d06ff2cf3242e21a81da96ba0fea0102823efd92c39af11deaed7ae1fa7f9b4dc9165192713305ad87fac349e3472959f49768b41008854fe1bcfdbc66bda374ec83f40063f839a0106d5bd4773086bfa043febeaf4e09391cede8dbd3dab7de3889c6ef4856cb62f34a69df9475ced3a1ed04cb2219e133676c13b5a8bec03d3e660cd932f84b074197831b9d13d52dc1688370b631d228a8c9e7420cf2368002de862d74c446b3f5c2a2b7010c0fde738a24090c544a9a6b2a94ffc7cd6d5d57825300305b048b1ba2cd98b0e0a0d5717e94ebc1c2c6ccdb3467bfa4f83f7d6de2924f13ced9af29681e5ef8cc22d9b8edaa364961a8fd59804110b64c452eef83c1eea26985178231bc17f5a2985d3b9207c7887aecf0106f434b5b59a7c1e88076e20ef0e5053b23e2e13895266940522a0d08b60eb33db5fa1c8d27d59162ddd9ba2000f67f32df18327c120cb436c991634da8f490fa7777ba92950a3ead1e3197ebfa45f7ebedbb3d0c60fc95f1047d1c60453811433934eb89a0d492e7dc5bb8e2be681f3c1d12761f1eefa66f629e2478bc1afb8dfd751392ebe7f65b4b4f2723af66ff0ce8f4db42849ccf06fe213566983186e801a93379a978f56283aa835144bbf309aa9e1a3a6fd940144400b1b6bda7c6529f4bc8a92cbf64e8e8cf4473081033ac6158de928e581a3d6a59a8e0b468508c79353df80e083c59b9ed37cbd3408474bf81b05ec96f92606dd7d9ee62c4d3653180e75e240221dbc07a0a72ab9dfe2f26c69bf48cd19a4f3c8e4a2a09e7d2133f35f71a22ef9600cb7bfda351442b0a486d9aff25f85ae6bdda3a42b8d352e95e7b3d4d9e3f7d2cd1ceb1b6c293625e9bbf31c3bb4ec841005c757367f3c4c12c3ac678c4f90b0f35d9da2442c6d4979518ec4011bfa689642c07ad5cdb0f169c30059e3f4c45cd9feef74b7936944ffceff5f4de15efba5dc3d3a60d216578d47acaea6042139ce6be055db3bfcadc82106c30c9dd6ba414e5217bfd4ba1ca3a57d32a050198b3132f80208a16463ae395c12e2c763522887594d2822521c30cb9eba38648951cb2327816f64943232fbeb6091c33ec22b6434f6ceccb714cd343aea0efcb784dd78667a56721c5aad48dc09237ee72729eb2cf21f30351418498e663077fe259341403c68c162739542e5bd6f5fd492882e8e5ba753a94d8b36e3d313cd3bfcb7125a0dba1fd83b82ec57106bf4bb6e3a7330db99c38836f41aa138d83ec2c678464fa0317bb322cde52da56246598e2b59237a68a60ad64df1c5512b6a6653a596995cc11c5080c13c5bc10a371b7cd214d511c4dd4c7e7af088c2a40d94fcf3168ec692f22073733faa2409944c240cd74be15e4030e8dc35c70831746a08c1fb40dd9da33928e042b3846adbb809d731266672fa301a8db91c9bbfe69b318a5e04873d2dd015c25e5badb8b1ccc81cb25f2b938ed2a0d2bb4801c4240bb7d0f5b590346fef84b259617c4b4cf36231b70312b2412ef409d751d2d69b1bae1c8c3ad0075673ca702e06e9c3bbc65f3188b5f26f4746c7811b48edbaee7adc44d4c87e88e6c4e23c9195b29b90ad5a1a036109f7024f4f69e2368c5877d296f563508af79d96541506b68e2af9eda11396820113918c7ac631540afb8d592db2bb3c8084cb86df26eb72cc0a154dab73cff4e5b32dcb995d45ec36855336757473976e62abf01a8a3123f69c418ec01f8db74b5a15215e424ef48d9dd33861288166a12c432f3b6a1990af698d211189a8f737e3c271bfdf860831795f749337812cc96a4e6a5b532bedf254aef42690f8141ed70731207806469ff79c8add2e6d06e4be8e9f71bb969e325c9b9e56396ad2bc04aa994eb6d6b7af40cf9d2b4b047a8274216e431a98652a8a9add202dff4f8b28fd8b5df616ed54a608b8f84c8a36bbcb7140852bfc261e3f3aaf97e76074d06e3a1941bc90e8d005fbc70b13d6c6a245432704257e3c5aac9da8bd21bae74dbe1c2206ba9f02afc121c68141e4f88b72bcadb55539ad42627fb5d3d8e3606a3cd1ae140728178406b6430052e26c61d39f0860e2b304143a833dcc779b4832f4f141063e238154fcc64aba541037978f63e31f5462cd1a57a1ba58746b9279cb298b1d16461e3e2877db8805080498cc721f93dbf2c7b1ece2634e50557321ff84656be4911f1451740b98785f3bbbcb4071c1d21c67802ad33cef660dbbf4230560de3b818a8c745922cb854c5beaa0fc2637295a316e1f203888773e62a05265d0e9e8ab2a4e5c0dd83fda183e326b03dac1464069d245fccbff010280a156c1320bba07a7531a24dcbaca312eafea2c4048e385a024230492ae1331e06e4478ba92da158721147a33925029e7e3fbc187a7c37fb8a8e8979641ccc2c574954b7b82874e8bcf44952d4fee8944a1a392e751ef724b75d69285dfab99a1f26efe335da207bf83830d547ece70140b69782fdd6304d9610f1309421be6239ba649f5ff7eb5c3bb921bf4bcc3205988b46b623429477ad6687c6f37a095e0fa6c22f2afb90f2aee2fac6552e61993c0babae787f9ac0a60e0e6fb067f144e6f916d2c180d86785962930d473d2f864effebbbc30a403816e2baf7f2f926fb3b98c88b862a6dd76ade2ea064ba38df64f800597d6b0e8ebed8b9e2dea0febdf6024a61190ea0a7cb75143890f09747d3f6ddcd12740b1552c459a5498a7eac4dc2426c68ba459cc2699476047b1408144a05091b04d3182e34f12fd2e38e0319b98cd3d2ebddc35ae563ebf4f30bc0d7da0dabe404620c34b350975cfbad825121bf5eca7ac262508f8afc39b0d840ade5ac27ad79cb3ac15662865262d498d72c5445729ee5784a897b374d52550fab6d826a9bab65919f49954a68d95599027b2e04c502eb64792bb20c5cc5f1d8b90f0b54d17288a27b0acc7300b5e2c2584d7418458ee36a47be10c0e43f92b82a9d07767c4dbf506d6efbbef31a868420c48c77344b649292c890e0d313237295458a2e7608aa6cb5634a106273ddbbc9bea0a1168683164400128492d83d656128350e93eed756490d192059f842741fc6646b657b19c4e83a4eefacd5d56992ef1ff4439162bb444f0bd8046318ebf1190e41059c4bf0205a35f08f04747155cbcff82fdba84a74baf76f8c73aec58f8ef7d33b880e61cfab43eb4c5313c71d6b8fe083fb836fcfc1a925d138ddc792d83b60f2dbf4d0aa785f6849dee05e6285b6d43ac4c84d1c0cd182ad3a04dfff7e52843a09ce7c2dfa7c91f88c149a4db5ad0586a8f3ce5e35430ce9f8f5a29841c49bca0497aab0b705ca9d7ff38439b4ae67f2c6691683399aa91a7e2d08a3834bf7783a2106dd7c444e9b8ee3e685f51eb171a23c72c9137ad5f52659a41d7f7f5741df2c81b7e42a09c93db0d786d779d4cd2e6023ca89fc157232058de5e0afdd546e8edda5eea5e3cc9b722c0dd4b83f7ca58b1516dff25b94d97e3aa628482570b7a4d76d8330fe107acb2db6431db9835a47bd4ba66f758e2dfecfa01ca9d1a23bd8289450021cd51946a6a5604ed1ec0378df8ac5ffe21dd9d1c50db884687170024a829704fd902ed0e97e775ea12b0f06a0b0c30b3fab2e793c44cabe424e7c90c2879d224408e24a27f0466f26cb0026779c9d6c2379075fc844d41855abdb234ef65c612488ac9b3db0924c18dfc2169c1f304adb1936fc8b24f0ca3f9f87f7f658ec6fa6bd9b986fca6a163d064a5b3a0d355715a2ed6af612673f256b9afbc4358933a879fde029800551375fd7388a95179fd1bd16f3852224a5636951e34b216ac422c02996baa58198682dc7e84565dc176c712509ba26fa2ecdb4de95d1df4e8267de169258b313a9d483c1cf1d4180edac1b4e8f32e867148647a28c8512b11e68fcbb6149549b2a711945fdb6b938368066f5940d8f3491c40c802e4f7a95b706db7ada875312f11f0cd5ccc9a76d94d8fa46e2752fda97a681570fe49b98a34b78094dc96b5e89e232273b4e08b0a96819af440b66a93dd47c49263b9c171183e4783f5b6a124133805cf8950c1cfe7cb1df1de208c6008eed0183a2daa51e8a1246ceb2e62d58fca9a368574da9588df92c7b34b16b003cef8403a6361eea740372e20553d12137f1484f14b896ec6b00bfa714ca3a0d6d12a32adb77aeee848053a8f3df71e3e97c442cbd27bf44641aaa1349288a1a68758880898184fb92741a98dc8b1e21973befed054cf755e8fa4fbb90fec75a040d30c315f2164367db1aaebf41f9b7316445ce81f523818a497376a31b234bb68bd10dc1c913c1142f2726af28cc073e3a625dae80f4ab1c4708d4f323f276c4ca7a04709a54f073dd2d8dddc765f2ddd6dbfbbda21f43bd11fa8157db6eebbc10a48808d490e311ea4e710fa23b3e8d772ad28c112efe139d657d079bedb8e5826e6d8b63b2f49155669503c8da54b67c94b7671aa21ad9902fbfba820b116a933b2f15e85fd7449a16d97deb631641fecfa6f1f1fb24ec201be59eb201a5a7986fc571e9b7c1dd6a3ff0ced9740378afc9df25050502b0d77e38e602754442ae4026cb3bd7b5742af405aa31bc54eb7970774b2a592543d73463686646029cb2b6d635ec4367c113d2dac2d41ce8e359cbef6a02dbafd735b5e350b6cc685321322f7af96aecfc548a58ae3ee4c362d39b66c0383e210d89ac972d8842694d1b9367e5d4e318aa88910be21aef380a5a03e0558ddab373d656c170b71660c4cce144bfe8bdb03a4c7c0f2935d8f2c5cca6af01b2e4069bbe3a0fb24c0eb88435809006c26d5dc66f5e001756dde6868ec8c3a29c14ff752b6e7e09daa21c0e7a46b521cf3d1394ffb2b692ff2a6f84d8aabbe6f981435da23a2aa382d7e8ba25972035666b7ce689403812d74fb516695f8ac6e91ed77e820ab186acefbae0a61b7f6a3dcbff622c38030fb982d903407033b284ac8647665a89a697b65e905779dbaacda90d1e852a34b0bbdaffd253ff59e1ee162c8b8cf6625ac46770991f0f6cd6722d721e44057f76cdc6a25e042cd585d45edd39df67677cc7683327bec42289a97fbaab79a2f35cc5643fe0ef25f45447b7e58c1ae49d16cc65d7427a5e452eb8abe0526d8107311f30a322ddbd18c764356e9254096487629d7bdb14c5f0251696af7d0c4e1e6c32888508c42f249b0c3843c0cb65b12220809822a31c5fb79c1cd2f468046ba40b94ecbcd27354c23f9b242b4831c26cecfca4140563768e0a4fa23630169beaa1d4cbaa39faea1d80512df5d13d4e688517882b7c9dfac4e6aee9033b2d388109932842b0eb18fbdc3be1ac24b1590e77393d7e4c7d55c9cbd22fa445c08d0904ca62554f41939aab41520948a1129161cd9900d2ff37394a186fd214204da5d86eac93e4ee2fb9e4276af055546b6539bb316865b7a68ffdaa816b8e8c9174ba38c9a48a4c28b12ba3914e89f9d6fc325a5cced9a12614e91eec2e5af15ec6ffe5232f59fecd7063805f0900348018b071e9e96ca28eacef5ef5db2a2fba8f39170b0712c07d0060c6485e0055eebcbc8a466868ecf27e064e1cf4d54d9998aa8a6ef1d0bbd8fe00829ece539ce33730289aa92ca6aa153d54517973d783e2c6c3e27c9d7f28dd0ca82e8e87e92f5f359343116bd8242abcbb36c3a212a41356ce5e64c319af9c5fb8f72e7c86aa206837adc9f1288d8a2aba812d32bef5832c05cc5c62a4239c9db808c5c16991ea08002198f0f66ba2607cdc42f8dede0e1070fc7e9d5e3dba9673b16cf287227832c87c482118a6045dd7d381928d2e5f7851bb98c77c637a05054bcd86ea863089d52dcea54f98bbb64ceaca63e6ab0c0721e439b5f2c9d7a0c64e2a71462f00dbffddb887acb14ec384defbe31c6d09fe03cfb889cc1ffebc7da0fb1fd093682def2819077e53876070edfc1a109ad6d34d118278d68287843f06ef15cfcde61960ce7edce3bdc617794beb84856bd6f37c8f4d0ffc8eec886fe5245042ba090d43ec28eefc4c901722583094bd67f2b93db17254eb5f4f18afe19c65ee25d76d351e9ad47099b09a488a98d7be10e515c094c8293ff4e877216ee647e39d2412e062ecf988bd277a6642515c2daac2b8c317236465bbb90e901ad664336aca6ee64a2639dc0604bcc78ab8e294a06ec4dca7d30191c08a55102afa83bd6bf9856326387af342b546d6eb49f71a3b6e881172550c0f024d6a1c2db4fe8c0b7f3210d9a7986ae33f0f363bd42ff504357c70f9530a8e1223666a7f6ff5b550d667a9a86fbd6a814505742053e1aec4fe70e746401c3c01fce9253b92678cdb3b7b804886aea8c237d5c1a25db7792e7ac5daaa28a24079dc34af1d4dcc06399458be93dd04b58abbdba99d3f4bc403b152cd3e447c6d171d3aa6b02efdee0b8ea9cbf377cb34e25195aedb3c745f4ff28950915ff3c03567c858d94acc7a3dad639e4270a2748e15486aef19ea7b309f6b377f12818f9019d3636e77e670d65f02fb8bf2d750efdedce9d5f2e33cd2802a82f189f32dbe7202b6b44cbeb9a38a70bc54c4a9d43c0d72d3a2e9f6fd06d3f355397aac56879ba7c1f9f26037c1bdf8faf5c82ebed90cf61247e346d59dbe5fb1e1716172a509b6632a54a739a3d840c00b1a2997ab0e1a72e128d8f99466affcef55ab50ba97a3bf367c5591e7ddaccdd227ccc85b152b64c38e401a27c10e390432dcdb783f0e61292c01000259ccbdbceca085591bb943fa1dd2dab19c192e1b513237d45968f88d9a4e12c081ad34bf5bddf7072137413701215d1c73a30449bf42a4a81faf5dc4f9b9a14d64458d6cc33612b8dfd55c6233779a00cb39860341c85c100ba33b1333c50ee24e9cfcff033fc0c3fc3cff033c07326b2ade9d65a83b566659252528dbd58525ee515642a534a49a69454b4ccedfe6689030c80609490b91fd90b032d0313030a1e1072aa4f26ad3ab516ff1dd069ca17f39b2a46864ab1439f1b4ca3e6e831ec99b7d8ce4aed12b35779943a2056a77c55d375143a5cf6696ce91f7e08287340ca56655bb652b6dd7314395c52e7da33cf3b2db15b0f250e4aad74871dfdf6bc1f50e0b0ca1b1855cfac1f2ea29be3e3661dd45faa5722338a1bd0ca5f73ae2d636d40cd74d9b0ea3b36206dfdbceedfb841a3465903abc11ca3ee2eb2c95c67c634951cbdf5344f88c7fa424903fa94b710d7692a68406c2d7bc616cd19d01b364ca566db6a9b9d182866606fecbbaaf9ab6a19520f4f9e5973e9da26a58761ab750a19d052751619638d93e2fe3120e6f8fffd67a9d7b45a0c8897d24c97b7d276ee8701b5a46d9d4cf4a396d58101ddf996bb942673cbc3be8054e3b3e9b6fa5725748e46c50b689b52873b61afbaa53c51e902ea75b291f939a95c50b880922fbc4ceabc9fb6eb5b4077b9bcf5b56746e9191e82a205b4ed7e9dd6db3a93547da06401a5bb5f8e799ab24e4c295840e7287ea3cec7dce825e50ad7d01353c934753a142ba096562fdfa5eba8797ea50a68573be7a4651cd99975381a3f3a3c54020a159079c54c4ec7d9e95ab3c1fe02ca1434d541e567db8729c3d1985240da277d2a9e32cc88ac01250a68b937e66fa69d93fc2aa04001313eb8dc787ba13c01bd698dc9b865fb5e674d78c219509ce0c6b44ee6ecdcec51a5090a137209ab28015dc347c6dc52e12a76a32461cda1efe939797a124fe65e9ff3d5ce201ac61c8d8f8204028964a2020509c81cc6dd860fdb1150de39efef64a518218b904440893204c4dc68a337d574797216c2afbbb2df522908e8d3327ba81633eb1a77530102537e809ad13d6bdd9c5175f4283e40490da507881d4e47e95aa7151ea0640728794507e8f398a4769951f5a9949203e46ad038f6ed6955ac70e0dcc6fbe426e719d3dc4ed3f1b986aa05ca0dd09ae5fde6bb2d7f73a8d8003153aad4ec3a0af9a8aa01ca746d8d8ad170f3e5bc088506a8f5af9d5725d5b439cd002d63966afc67b399451f1419a033ab0d3fbbdbe4867d0c102a9b5f97541518a0645263ba2ef79835642c9f8d672371b9008106b8d8b3d4009dc6d232f6d334353be7689c181a0b0dd0733a9d6f527f0668d5b33f865129969a593240b9065325f62a47b0c400f9edb359d6cbd2eff2020394a869f01cef647a8629af38aa9ce75af18cf776e7b27abbee543627d915a8b9633fcb74f502a51568d726d37cbd155b3ca8b00225ab40cda8d334b1496f56d11555345ffcebf4d454044a2ad032a34ed9a0366cac534105eae4f8ca926ffc489af1223366a4532037c7cd366d979922dfd13a445e0c554c81525d9e658d756afc2222bf31923c4440dae18f24313a444236368e514a811252a0566f12f7b0528cd3218f4119056a5ecc308e4683b73186c245564481d2ebaff229d396765e8731a3a37d80789118f285443243d5f8c0088d1a60782149911703243712431a6316645042814cc367a5877e9fffad8002e57a569af2db398aad523e81f8785aa9316cd7bc9e7902b9e3b23ccd6dae13c8d9e4f14faac9d138a9d1f1f14a225138b19bcc8db8a9aaebeaebde06b7959daf0e55c30b89a742d90462a6cadd939d56777d3481389de2a69add622abd9209b4f2fc7a6f8ec9b1752a9840abfdb97ad75dad19a3333e3a68748cd490482e817a3d5fe3683448a363a486b1046226a556eab257a9043aaded27b3b9dfb956e6814209f4eb8c1b76f69cc753469904fbd4b6dfdbb92b9240b81c9179155b2dfbcd4502f51ecddcd4abe5aae31010916e0512a89d77a9938dfccadbae0f1580b05de511a84fbad4abfd5af8aa9ba3315371046a8ba74a313c8da834029d64d8dbda75da244436b2fe040ae4f01446a0c68c52b5af58a3ca14288b40cba454e5a82a313e7b38fa1545f49d3de7f4962f99b195f1203ffbf3d437ed381a8f6e22d0bf261a648b59410462ae9499d39c2e91882c8772089498f1b47ece4abb142a36f20197288640ccc88d53f1acbb5a850211902f944220938fcc9cbe844650088172db39a316ae73346c9ba00c0271fa4aeb241501451048d97473d6ec771dc70f046ad58497be4619fb740081d45b5a9f967557b7f53fa0e7fb54c50fda07e543ee01a9b4feb625ea52cf46e9e15c9e2f62b64974819207945aa634ee69d51de39115fcc762e00303223f82819f21f260688afc48470902791cef0862b903c23fdab9b9edba10fd02b1d801a9598937a93c8b98a840686c84ace0fdf0014b1de8907340de7f8c629bb4a13b4e3a40449e04920d8f1a1f72406db5d5c40cb977b0c4c153cf28df2d7040cf30e12d3775db94990e9637205eccf73abd49eb9e19372065f6ceb99933b74db6b401e9356754691d64deaad880566d5e4296ccc15c4b334170903db0ac011d734d4feacbc6cb771735a044124b1ad01d3b5ee7a91734a066123f733193dc79f50c2817af648827f93b31ec132c66406e5e3d6b34ac35e6ec322064ec9ce32fff536b73210342c379de51d999f3751c8d1b61f48c8f0e918e14d21158c680f417cb644aad2676ffabc15d2f81450ce816bfd9f02f1506b44e17b1a9363d8a312d60406e9af9635a32664d63b97c0165af3d5526ad4376b62d5e80c4d205749e4f19b7d556f7a7b98054aa75fcf7a6735be3fc650b089769c6bc97f672716b019d37db4e5e6b5b461b2e5940cb963abe3a7fd8f7060be8cef2f5774deef5c6e50aaed6f1518919331b938e4c2b68aad6ff5dd63ab12a30630919df1bf3dbd65241b5d6da9d3ccbae695aa6d096b3314cb137191e132c52682c31cf6967dac951c0e4dd65c5d3e6e46973b9ca9cb125b38c031728a03bc6b7fdfe2b7b6edabd3c01b5d935ecc76953e72f27a09458d761b5fc8e51d7bb4b1310bb55e62ca3ebf66e1513d0620755f11bba2666b72c01ad36ea7359ee3279f74a407be7b2b16ba9ad7626e29204a48a2964e95ab56d0a170928a9517b2c3b0d7233b31c0135b58ad99f9e693abec50848adf6871d5f9752cbb31401f5613f9734952e4440c7a0f276abfae0b7c410fcc8181b3f328692485830992042611902f2bc4d79ada5d4a37a090161d3fbc5afffdc2c6d10d0716534a7f11720a0d44bcb8df6f901525cabb8c79561c60f171fa0dc3c93383fb1673fb6f40019575cd7dacd99558e97602c3c40674cca4ede18af6bc852587680982ba7c35ed32d30a1418130d2021249ce9048122734542e3a58d7f5f36664c3ae58728058a6f427f11c533a931f1949fa81050768d9326b99b662b9dc00a95ddb4cb5f9c6def44f60b101629ca88ca95101232bf82e3e66744c0ad0804917bf4503b2987cdbd8c2013218a0d9855f179a970a5880024e922dc104ac91490d1101603186eac200324626353c10002c7e46aa31e323d128401613915f35188848881660c8c042860c195838608b074c3a429201b068828cc944490290451a494c22002cd2486220221202c8000002b410f90d14ac3432d9d8d84206160720c0c84f5ea11b05585c11e2d10aed1811e198d0d8d8d062610501965568c7080d8e49dad8d06251c5009654a0a69b6d179ba6702d7654a0a35e9121ebdcd4a7e614e878f2766dbc2d4cc7e33dda14a8f5b24c46adeeb21c236479bc077bbc4729b463448c0e460aed1861201e353636b458468192b7efaae9e41ced8935123758c7fa90c9c1918f0e9d7c748c883c8d8e100f90d5b801126c74601105ba6777f6cc52ad97d2335e84a1012ebef000178ba1b83bee64ccc3c6e0f2f13c8995571ef7f623289062edb8594e7c50d3ff2750e7c2969be7be7802e9fe77e2fad5123b639d400ccd39bddd8d6deb36270ebb2b6a2a1b55d5f58919a7ce7fd64da0d4fa515f9d938733294d206756ecca999315b60e8ec99984806cdc9209b4b4f51837b17f9f1b79c10452acd6c9579cbef2fb12285da6eaf56b837da49084c16209f48ccfe5f9b689a371c3e0c75209748d61b766b34e8b5d520299d546bd4f6abbaebf2789c6e124103adb346b4dfa1095044a7e67dd7df3f81b3c9140cd2c199f3eeadc7196221c90c10209e4a6b7f5f3231a76658f407a9a655f36bfe275ca1128fd25c592b3dcd58c37d308a452a7e48ae8c7e81dc8c288337fdcaeafa9dc8e6511889dc39a523ba810cf5204628a59f363747922107bdd57de5655de928b802c8840c997ba767b67ebd49ae510b788ad8ca6ef1c573e348d09f59ebd9549864029d9f9d834c367b1691c1f894618cf4036e0cf403e8c2e85406757e559469652b93921b633f398cc3b77bc8fe9a8f90b590681162b1f5595988d8c41814510a8f3cdc9d55a2d6c3cb3040229d3d8cb9a7ab35e8cb30002215bccdb1fd3da619a82cff52140d4c2f207a45637a77ebd849311913432d991c50fe85d91e569e76c9da6fb01e2e101b274031289416491a50fa975ba65745bc2357c586cbdd63ba97bf4d4f16c1a5dbcdaf2faee0169ca5d7f738c6558f480702fe5b56fa6973ca0d312d36adb549d5da6173ca0bc564c5db20e8395a8a2631053c618430c310491692b1d42091284213988c2381204516da907124070490c45298e62186314418610420831841042083164448486a63cdeb7533957926c5bbbe93b1f0e4ef73f479d9c8d8e7eb567d68e7d8ef1ba995b55511d117cec206756233d918e900671295c8ea9a04338bde6c4194d7944d2111f0c23c57e0b7d19c80d4d8655b7255eb720730ab0ace890305f5a3138b9233cd04ddbe27e826dad9978b18983a4123ced1b5d1ba40cdfbc817ba6016573d31e9dca81ce1437795752da46b009e44bf7aca4a68efc8434dae4d745072af5dd02584cd4d73da2f953b1e553023fab4df98b994d521bf4af51c94ec628e0203a9b51d7e8410072583264ee4c902b50b6400e0b600a6496881a59e037000e76629313a353dd0c215b2b14d61e90f0544ebfe71c4dccaf45599a3699094e263b8434434e052db75e1ac501958195257aef7e89e0aa83270eba96efb5945a306da409cb58b9c9e54f848d16164f304ca232969b074365739715c265fdf72f67a6639942f81dcd3493796d862ab800077ea1e0d56c9012e268ffee61e58c7dfb65750cad280326659a3c51a6fb70f25a78c83711a127ec991d52ca88018ad6bd4014a0a3873e9c21e277b0e212df4d935f98bc85f3edb26ba3f316f5974c3ac5d3253276037566a8abed9041b6f5ea024280e133a4b3f3b568e974e01b3505c1ed417574920296d7e6d04ab3601072c4b918d823cd3e85ec58e7107f62f551712d78d6e74835975276da04ef190a18a48e04574108ca7a80c14c7ae4c2e0f7fb29d2d4d20a2e112dc8f04a68f980073421ad88642873b1f072caa8334f027d237fc04ba732bfd36a5cf93b59580c9671dc6643cdfbf0f3e81bb07ca54b73cf5623efc7b76a2ec4c74a887c4bdb5acfd4ad71ece7822089bcff2eaada96e0cd92f97b78fa3f6e1c1bf7b4f91ea20bcfaf339a900de501490b19b69ffe2b2fd138ad0105e28e220fd16044b0bfedd5855ecd3ef8c868afdd78a8e0ac9c0e551aea77e630da6e067a3ef68ce0b16c97aac4759861c83e48dea8ddfb4ba62cd50125bd79a51b115ae190ecf235b937c44bd13ec929a1d91f14d22a0b84ae15e7bdd2329dfac948a46a6743e93dae94737cc828a7ebe0352486ed73773ea2281a68aa90b3a60d65e0ffe9ca04b7b2397b17766c96cf61ac2e594eb8da59893a9dcc0930b73ec97ad48c2c16541ae39efad85e3deb71c9ef249ed8bbe3e8aad13f35228c2d33787122d5e4f239d1db557f563f982f2c185f42981e557413ca311e307357882958957ba47c866b0b562ee929a41ce0a90f809da73b77f9636f85ef2e871a41dbdc275199737f020009c1ac2f15a92930efe76f58b3d6375770c3064d0584482130b513b4a4239a88d557473dfc659726a4e1321a2a8d53c43402c838c7d669bae1648cf94ce74a5a95d3d8a4626407525a957609912d36466007e349c85cea5e32f6fa48ab1275d95ef6ec0bf4a99056a56663aceae1f0992616234d1997927f4bc6c586cae31600a1fff8fd003a734b49e111aa716487e09770b53b6c709f0d75cfd93443c7ac3239867137d76f36c0d28d2c6820383acc8b2580bcd9517c908e1d80e0ee68e4b8d265481f4b19928cef2093118340ade328c32a03f737d342048bc8bc0808a0f4ee832d9e73d74db9fef572e45a54fe5f30b3f8fbbed8e969b4ee117b06bf37f5302e1ec018e9ca97c650163bc21d3b823d2ec5173b6f85d07033d1518c55b0c4375ab0e130f4d3736231535e03e23e4a491fbcb13e3a6008ede2f699f70c9e67fb3a43e011f27239015fc71a4720b89ec3fbaa98b9d4b2afca2130fa562dc2fc704adfd3b45838c7acf6c19edaa65056560b27f9655e1fe6fb421145a975383d7e97e98a19149b52e05b89f4f8e6a0b7c722e7f08b746e2bf6a3e4ba27739b0acee8b43f77c4e37d9e61a04480811a1edbf9d2035064864f1a4a760d4af61befaea69d044e46e5d5e588a826726b945608530482715546cfce2ac49fa3a967c53d8df697128a774b738c8a605808d74c222070f0404a7815b2c071101d05fc06737cdfe09a8146a55466856098b476d9c838daf06582768f4945da7a5fd1c15d72173cf35a1a64eb7718a7ab19b286621bc6ed9fd683fe0b7d962550f58b16a9ec23647c38fb003351d610ea0ae50223b0b7c6471df2127cc85241cb6f372be84e992212c9e238b05094716e9a8acdee410b92a89fdee4c7ac27d619077e80dbc28768afa0503eb422bd8631a365fe00077a3c4743f4417dca7b1e968c5786a6d8698b8a1f46d4c47a4ca57fc50f0b405cac837a05d49380c3d37e8bd2b934c90f54c60190dd2a495b61160a4c67f74f2d536c4ef3962fca1e6d44280321a22e5ca845b85ed466fdefa5af12fe5960b9875d4e85f4ff86f8f1e1ce5946e72167fc16dc0ed326e55281f6b52e9ee121ffe3a5f3c5b82ea30d4c0d517a4181410e6b182d303603515f65d648783aad9fca35d33b1b568a281f63f47758841f128eb07f9217e9b59b4daffbc0da5a02019318bdcb9668c3be0d0c94625713c104fbc69bd3aaabe6b385151c9f6a9f9c62a9f225e1547c25b62be8ce887b040a01d246ae20e0399c20f0a2cbab01c5355c60355bd40981851e35672ce913986481a9af60f1aa94b2b40d32c937c9aac35fef0db4bcb29db47bdb4eedb2f85fe2b42c981e1a668936818dab5efd6a6660cbac8c9aad0826fc003bb54caff9988d83a58aebbc340dfd5f82496c0c6d855dca28c1a1a59607f78d18b1d33917670aa5d046e1ef96ea5a21864a50b865641356ae279ff6afd1b86450446e04702c92a065a892716837dccbbb10ebaf0ea3fe4b61316d12ab42f26e6da7156e293864619f0f5fa6481d43d4682e32ed35d8b4630fcf86ccb876eeab1c50aa9e6578944f6174707d44d312dddcfbbaac7fc3930eaa5ca9a68836d7fbecf43e1010e393fc7d6968cbe00a752f3a6149b109f686b0f4fdd77287828c11ca2b569cc0f987870c9d7c171bb419f834f76dd2dbdfb7ecd664e4c49f9a3b4f1a899d469193d4f143fc3a87ad121c359b56d8f68485b7eab8144cb5f9b0304d503de2b4aa3aed67a1b38c69f31a12ffe6b6e336417c04078221cb987710b59b840c2fd3e24190faeebc4b699502d3f30736641c6ba2c4463b0429c8e6c14237ef09be8632ac6e107c8d32ba77d2a36e8dd092e3b6b95def7afb685eb883e64c4ee161b8df94ea401ca2659b9b6e78a405054204387ab9003d4c6eee689696de401a4156b13beb2f40a09fd00dfc00a9a9881cff23c538ed6b35986e640095e1e016a2d5668a160b81c1e0c491a5f3bd965a421de49a01d96ada05431279ee87f50aef51c29c7304cfac46f148cbff7c561fceafb266041ad37a4f05e4922edbbb36b8b6903b2b4bb9fb99981ac7dcb7d439f125f5393d5f95ebe520197d35394515760f881523ae282fa768c7a285d36a17917f434d2516ac1cb214a533561f8e6f8125035dc9b42da37b09237cf6d18e30d752853b8ae08063672835d4ca9d9c54ca2ebbf565b64bae05c10282f29405148d110ac05c57411e436aa3f973eed13b14cb1b141f293815a89964a54cd32bfd7632b7c877fa958dfb117bba281cd868d10a0177a3e6b8e6bbe89940dc09d7d53ed918eedc91b80e70feec91fdbabc845b1a946a8f5c19429a804964da7e2fd4535a3539feb4dd39d73686785fba7a4cb55bc7e08e0676b0a81f916e7e01435dfc6c58a40d3e0f889ed3ceb6ec2fe17233502b3b278a32235c4473210a75b1f3342b2501e499084c9991ac3c5425ed53823f2777cd2ee18d74d8344129f4166beb363e07c535adba78bfe906114f4f10be9464af255a1508a999796c2257899ffa84e5c8c41e6260a4ea5e66cf49508dd75c8102fa24002a216e28c887733a851657126b4bcf05585fccab38bb761fa957830294b94884797a01dafbeb47d00cb1ec78a8a42dae2ae8292d10c24de715c471bc9141ab1d9a9d102c4f59a3085130e1592ecd29f7adc702cf02cbd250d0c893" + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xc8100000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x103895530afb23bb607661426d55eb8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x01000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da915ba7f8e948fe69399899872e1b0402518eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43": "0x0000000001000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da97eed8e9f29ef7599680859f048da355c7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121": "0x0000000001000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9afa172acf083421926d6cfe34805cb00588177cf191e890d83b10e9653cfd663c8a382d5afce8518636dd9565c22a631": "0x000000000000000001000000000000000000a0dec5adc9353600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f3348a0d38f168e401926958f66bcf0b70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c": "0x0000000001000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x91012c6879706572627269646765", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd00580c64047efdc418105420580e710ef5c0df58674a9bdec1c8ff7b0a0466adfa1c7127ca5ff6de3ea7b0457227321a6f306c95e023f338c2ebc838fc05fd4df9cf7dfc028268fc1b8dd3c07ec80d5551d5e2bffddf5eb2b2bbbbb74c49ca8012260f440f229d21493d331034e4fafd34d4803dcf5c3fa66b68c378e6fa60fba0b99f8636d433d78fe9988ee998a624ed17fa34d5d88731d3169bae3d854bfbd2bef569fb28e9574cfb853e7d9b5a4b97c24e57a6dca5abb13e7deab9a451ab00042d3e97990b1c2e68b894e1320397285c705c9e70a1818b0c5ca47071c2050a971eb804c1650a172a5cac7061c225c765072e55b8f8c0450b971fb8c4c0e5e59285cb162e4db8e4c005072e357071b9dcc0c5065507f3a007157a54a1c71a3d9ed0038d1e69f4e8e901851eb21e53e821851e6af488428f580f327a30a187117a70d1a3083d8ed0a3093d603dc6e8f1440f1df4c049cd525a484921f546aa0a292ba46a90ca416a8d141ba936525768a942aa2785462a8d162aa49ed08246cb15525ea49490faa20506a919a47490e249e1a49690d249e120b5839415a9295267a4a248999182428a8a544eaa8a540f526aa48c48dd20d5454a8a940c524c523729225adc48c95ade48bd5ad2683942cb0b5a9668b1a2e58994ab45899628b434d1c28316275249686122f5440a8a140d52364839d182d3d2d352450a06a921a48290522275b674d1024ba9208584d4115264a460a9325a745a9c9002420b1529395270b41821e5825413292e52466849426a891413a956cb152d3d48c5006381b3e81fe01e6028701518073807d807f80abc058e0227818fc04c3011d808b20ba41da417483848299064906690539047207d402281849157904920cf20b920bb20b7207f406a4166418e416241e6802c83e401a943f680b4825c02190629049629587040f2903a205fe4105872c0d20252092c5a609983e50e961e963458d0603983c50c963258c860198325c6d2041626b02c81850d963558a4c01205163958e2e091021e274b139630b08081050e963758dc60f1618102cb13589cc0a206cb1758bcc06207cb0f4b1758ea60e1020b1d2c5b60e16141024b172c4560e182450b961fb064c142041623b01c81050b962a5876c042058b152c3560d101cb0dcb0a56b2b072c78a1d2bb3953356d25869c24a6c458c152f56ba58c162c589159c9527568c582942050e953654b4a0e2a322870a1654aaa042051529a8b8a11205952ba8bca112878a1554c450c9824a182a4b5099820a1b2a6ba8c854662a4e50b181ca0d545ca0d2021516a8ac00e5051523504d505640b58192a1a6800a0355059414506aa0a280f20245068a062817ea0b14135060a060a823a096809a02b5832a020a082828a0c4e8172a08a826a08480e2410d0125056a0b540d504e40290125039419a831503e4025018506ea06a8184a0bd40c5067a06c80e20295062a8b9429a44821250aa215c437c4364a4b987e20d2498989749072460a1a2969ace0a044462906241a27364e6b9c627062e2440594224e65a0a4e06406ca798aa14081e2c4690a28324099018a142834408902a50628ae934cc58e51eb84052a5f381143858c1325786078628c50a0ac3146e18571a5705a8234c6188394304021a8fc98a680fa81bf61a242680d540fea0a141637099531c020745eb81ba8394255f0b8406d418c82890dd310585860da3101a1f446098e921ba5384a3e252c94e43005615442b4423445a949090ca52f986890fa01480b5b460f248448108551c239f9a24483900961134a51a4c84a520820002519ace8a0c5e8304c7590cc30b9412ae36405230c4c4d4864987ec616193b51c1b804a90d938fe90b2630dc159064a6374c5a2041e1e408121a5e132a3c189b50e901a965f2416905a51b10139d1c271e510d6e0b5298309a9d2481c48293304847f88bd402d28d080762152b5a3813d107df16ee0b6c1c1809ab05bb8665e3e466b40382a261232cc22c58a220ad40b6415e21f4414989524b5405a809d10e4a2d486982880a5316610fc2170807f4842827858c93284ea4105d21ea818807a297e8062b44b0b820650c2f09e20c48364c5630b561eac265e22e115e119211c640320871c218844d844e80a2407902050724852905a6133403b1471c432c0364039411ae0e6a0d11cfc90a2e44b843b842b83b778b1b840b841323ba26a4175824bc2390e820cdb1d282eb84c80a9316a524422c4a4c945e5042a2e404c889508b70074584f007e116275e98e43821a2e4822b832bc5a5c19dc1e5b9aefbc5adc16d62058c3b450906273c3706251c94aa28d5a0e483520e4aae5216a59cd20e4a5a94b0280da1042b6d51ea41e9082524948c50fa41e98a92104a5494a628dda0a4532a4269a7c4454907251e9488507a9582500242c90a918fa80cd113443da22b88a420ca82080b2239446e88e010c5217a43a48648264243b486c809222a88cc105541d486280a22364450087d216447c80ba11f511aa22988ce10cd44561893185f3022717570777002c6cd4951e152e13286a823befc0a0fc3c1f02f9cc795e05e7812bc0be7c291e04770981bc18be044f021b8103c080e04dff12dfc07ae85a90aa425c4274837f0ba50e9028a0aa62d6017f4c8812807a9586a8b2f08242e90b640aae39404120e484e905f7c3b1029a1a5092c57a0d8f090e041d161a1d34237871fd1d9c1b245b7856f06b70acf065e0d3c97d771cfb83d378d9314884130cd61a2c37487e90a262c7471746f885a883fb8689045b865a044c114070b1058763a31c41e98c2808b405103c50a2c5298e0f038484a9060c00285b845a989d411a82f9cc8a00383f4e2a48448c5a906a49f9322441adc145832be9d93151d1ca33a4646189f5001c355c1880b27e76501ea095d18dd154cb312938ea7eba2f309d521a201b8634283c5255e7172adec9c74be1f90ec38f1c0c405b106a4113a258cdc1871506fb48cd1b9210241dc6929239585942c4c6984d4486161cac25703d31a274c2c182322785ea0dc3079c1baa04bc26523b5b3f273c91815c17446f78588859845cb19a26b658b963044218c4ca48890caa26444ca072d5e883c18bd31ea42a80b2b5fa452401261a50b2a7780406801e39a912a420b12569aa0ba30f5a0ca4869e1f1784a4091428b0f526e785fb47061b28304c324f3986879a592189d08412135460b162a5e6899a374048b0f4e36f8b6b07258376c15ec142c15c81d2bb359b04eb04fb068581efb85ed59c9817bc1c14082c1def81df608cb02bb02cbc4bfb0c203a7c3b7e07338175c0b2b5b58e981b7b142c7ca1ceee372f81556aef027b815fd05afc2d5f01e4fc3cb702738194e854fe139be03d7f1285694e0307026567ee03e58f1812be12d70167812be027781cb151c0a2bafbea3c1d03f6ec44a18be445da3b2d15ce82f1a8c953a56b8805bd05c3412ba8bbee9239a491bd145d49f1a855a852a85d6a2ca1a8bd6419d42ada372a162a1ca51dfa86af414bd4545a39e51cda84aa8655432ea1835569950c5a8615430ea17d58b1a840a842a84ba45dda947a8b46a4ea5a2eaa0e6a05651add861a6ee0089597fb0c4a6a64e0144088a9060080f3a04119203082200814c03eb01ed451f21060310e00003a859f09ab835d32a62c26b9f5122840440900899e90108905b5d07233340807c31c4803a620890233b18b1a148912310a0193253a4c811080800c5842ae1c108111d8ccc206941194195a8a08aa04a502822669418398244c90b4a0554c9cc10223a182142a364260820669200a199e10100a814d01e80cc1821d2c310213f00a151324323a4480050279df981080d922052294de80c8d101a25409404a1c4c88c12234786b0a484812aa1313223c40c10540a18e80e478e24417207d5e1089124334880ccf06064a64811213d0ce191f2054a73e4489293142f50203b0899290224c90f3f00218224df80e4072536aca4d80193f253a4889021458a08814017a892224484d0cce8006466660700a4d4d1030d0f46668a7081ceccec00e4080d109a1e7820325302550a1df4c80e46880031419299208000420449a63132f3830984fcf8942dd09921468e18012204081124796688912319a03112c4912143f24aca1c3a1c1932a3038f142d50252698f1018812252698b180912344942899e1009932a3252082242799f121c9110f68804cc902053213c44c101b90d16186071e84c80820050b54890f43667a1822075542636426c94c0f46887c4a1c4688ec804a81830e21a2c40891243443cc00314111244884004932a343112241b07c1872a4084b25e50daa2425c50d2a04884a8a0ffd0188101a213a08a199c14028e50a2720524408111a2246889452da0862c8cc1033a2142bd024472000a6b04195cc10a12192e4480f34410819a5ac418d1c2142232449103a00a139524409102248b2902441e4529806f561c89122240032432344072245840821d23a53840442688c04b103109a21334988e860840810202802b842e0c0586296a9cda5b0f3ccd5c65136cf09cf898aa472b663e906d00da0ebbaae63616169ea2eb6b7534ba96bad695ad5a8d6dd3f4aa5ba6d1dbb7ab55e9bf3d6b41ea5aaa6690e00addb354d73b79a6b96b49556aad1764ab55a29ad5a6bd469a594524aa9a659ad9b6a9c534a35cb51add6f6ae605beb6db9da96ba539072de9a53cb518dd2d63a4d73e7ae2d719c085aaa81406d5b7417bb6b8fa3d6bb37dbdb75d736aebda96dcdddb6c5c005a8bb0db9a6ce69ceb589d3a86beded9452d7685b517bb7f5ceba73ee946ad47bd4946a6dadf56ecb51cdd2d66853d76ca5d4bb6d5b4deb6e6a3f6f4dd3b4b09bb61d2d57b52a72d4decab553da54d36a777b3bedee3a6b9db3b641d082a0b57d6fb7b5ee9cbbe55450505050ec2dd95bd234d728c7518ea394f6bd20d84d43dab42fa5b6c74b45ddd2200aa2eeedd652da1e76536fb79abb37d7ee943aa77dc78d16e82cd761da59e7045081d005405bbb2e5a77aeb96eb1dee201f000b86dd7dc35ef16b5e69aa68521a5edae6d947688524a3d1b6853a7b4520a4c20a4946add4d29a59c7b48a9ad6dbdad6d5b29ddb4a61b75eb5e693775da755d67a9a5d6ada59d5bb7b629ed6cb7a55c538e132d78b25c5befda9b735b6a914824eabe9ab7a6b9bb3bd53a6c4aa904dc5aaebb1bc55a77eab5476c4d61d85c736dbb76addb5db3deeeeed76a5a7bd7b97356d3b40ebb3ba4b6bd29e72f206d0bbab5965a6b416aad05dba905bb41afc1452d0294da5a2bfd346b3bb79c75b7dd2e6a4e398ee3bab5118640776b9de5ba6d53eea4396a693bf53bb228c7dddbada2c2699cd6de9aa69d4e5a486953ef766a552677afd629d5c2d014d6a6b49d0b518d768356e88a37281402d515505dd13a34a9426b0a47dd3d6a4d36edb4a64dfdc582deb5d64a29a54ddba916e6510380eb6eafdeddb4d6ee6eb76dad6d8fe3b8cb711cc7596b39aeabb46ad45aaa6994769d46dddd9b52ebee314eb6b737a51af56e4b6d37a595d2b6cd719c77534a69731614397b4bdcb66dee5cb7e6f6daa6546b4b354deb2c673b4a3b4aa97bd3b6ee96524a29775b8576f749a396e3a8a6755377aab96b56b3d6baedbc6ab5d3aabbdbb6ee6dbb2fd79652aa756b3468284d7b80e6008e7377b79ad6719da6f5d8001706a0468b9a610132b8a06cad55ebba51734deb34ed579a8fda79edead8719a06ba7b6beeb5baa635a8eda6b9b5f65ab7d6a9e6dd6da973ed5dd3d6da72ae35a554d334adbb9b76b77bb7b7d65dbb76735abbd7dadd69b46a5ab7d3f6da9aa651aa51cddb6a54ab1aeddadddd5dabb746b54eeb34dadededdbdb5ed346f8dd25adb250d1727f888b609921cf961e6872d013e56863852021a161f0a38c0930e6000200798f14148111200e1c108911e9408e9e115a0c34c92191d921c01e243010700e283902143667c00324324094d00105084880f30090000025e0ef092328014001820003030273a1421d2c3102034458210b2c38c0eaa1f6246072344f0015a0a402324c98c91191e92f4f8e801669428994982e487234380249911e2c8909924408428d161c80c1003f8e043010740326446c9aac70e4492cc08016401065080020ea0c30c902334484c9064488b576446889922408610f2038f110f478a00e1c108111e78385204a5fe40a9016692ec0024c9111a21438e14292244850045840c395204881122427a18b2c30e475e667e2042632488243824c30e3441ac848698e901088d112249900019a284c80f4984d02801c0891214113dc0912349527c18228404458a1c4912801884cc244931c18c0f4068869831c2c2836686071492121a2544869851d9a8018c1c214233448812234292d000a121f2c30f475e52e8018620b2c34c1224389444c84c912128218cccf8093500102347882849322304101d8ccc20412204c80c0f42921ca151028466881915218ccc7800422134b5e982509bfad120d4660b42b5254b96d8d8588e2e59b2c497d8744bc22036d5a6da9c04b1a936a3203615b46489f5f62576c992251ec466894d0759526d962c714b97d8d8d8586d894d5d62e3416cea92254b96d8d4254b6cb420d4e606b1a94bea1784da7841e892251d84dad86c365d109bbaa47a90254b6a109bba64c9121b1b6bc305a1365b9025d5c66db4209406b1a936366d6383501b1b1b0f426d6c3a884db5a941a84d0d9201ed0695525a7f50228406aff9f8310d6e9a6f9a6b8e9f4965791fc9a4b2c2bf21f823787fbbdf6bdf0591db8ba4f6b7825df79d83f6169bf6358cda5e13475cb38dbfbd8e8cf82011d78c582787f1c76e7bed73581c7903edfe3ac87d4c83c8ed6d4c8bda0db7c16e5cc40d0dac7e2695f5e2aa900449195c55ff921f490157d577f287abea7be4075c55bf2339ba75dae86fc9226e6860393eb9aa3e93caea25a0fa61e1d2a7a9f26bf9caebba713c9dde0323f8d457a22f6540f994077d497bc4a7bf8111852c7d0a49fa6b223730f67b60ecdfc0587a2f1c46193ad8c8738abdac5ff4dc4eedb4a79f5dbe54f1413a32276f3ac13524acf3a3e6e447cda7e01a13d6c9c1f4226c8ade84c5d2933ef4a0bf161491570351c8f086ab812864e953c86b491fd32632bce13a0822c31c401fd3158453025043d82461b38449a6eac5eaaaad7a22a97ff227d80c521f05059b3fb069727955d775adfaa68ed4a5742902487979977fe9b3ceeaa7a460b392aa099b354622c1441c270bc94c33d32cc5d4b23699b0d9b1ae1fe25c55c426470255517d92a87e285445f5c517b1b98566e2836a7d91089bd625d60701e051eab7d427e11857d5fa260ce3aa5a3f05bfab6a7d01e0d155b57e00307555ed5eb473752dd097b53b412a6c56176c9a2e0fc2ed7255adff82cd6e61fc2e1cacc7e3ef71f68c6bf9b6c9ec6b7bf148bdcaa7eab760536ba160b3be00d0f23c6635fcb2c26a6cacb2599dc9ce58b7602ed8ec17c68f82bde5aa5abf0736fdc4dc493ecb932edb2c6c73a9fc8ffff553d8d45cddac85c7abc8dad5af867dc7b857cb3857cfb8d6c99d189bee626179146c4f57d5fa6465696db095151b2b3d6a6b9d7cfdc7a6f6ea78e031e62d77f90bc661338fc95c16f3198c059bb6351a3d0ab63357d5fa2bb53eaa843a61531bb1d9a960f3b42d9789c25ea4172ce48a895a56263e65a55494513551b0563ac1e669c41dcc55f551588bd959a99d2dd397f5e5227dd9cd5ea12f3b98a8a2d4940e962b4a096b3357d50a7aca4aa97fbf1b5fa58c1ee547a5d29f7cca8f235c531a8d7ffa933f8db86684757218fdf8a51fb1398ebebe271bbf8e46a5d2c9494a0a0acac734f5150a3675644e8ff2264c5dc57d8f9f824d1acbf451b049c2d455dd9ff0c9ebc8989ef47e42c266085357796fc2a5d0dbd286a9abbe27e1d79efa6a7445d8143175d5fd10d691117fc426088b18c655f4b7a7212e694fa6df2f3382d893f9eae20d97dd87cb31d3d770397ad8e2f2d461c7320ec38e70e598f65ebef264aeaabf7dd995a3fdf2f47ae5fad793f98a72e185264d9a3499e30c1eb0ea7b3ebe3a00149a8481076fd0a0091858f5bd99af383a92f8020a277410043758f5bd1f5f153163086de03c01461d5e60d5ff4e5f1d61c10d6ef0c611659c6eb0ea7f37beb24658c1c61a5db8a30826aceac93c99af628e88b98af66cb8ec641a2e471f8bcbd3cc71f9be8a39e2c755343796e5fa3455b10f5f2bd396252c366123200697fea53d319e4c9f5258a64fa9ab340d973264fa303025cc7df19559f3e22afaffa7d3386ec08655a323d35fbf735cd3b87315fd8a5138d4a72c1b167deaab9a8aa9abe87f0b6479896dad884b5365bbb23ff595e9e977be4a79faa3af504fffe42b94a7ffbe3a3dfd175f9d3cfd6b224d7f42fe484786f428245c7372f2a4d791293daa846b4eb04e0e279f82cdd29f98483f7ad187fed6d0f434bc818639943ea651640a19de701b4c214b8f22497f6b9803e951c80e6fa0610ea38fe9130d73107d4c9f901dde702b88428efe448afe840c3d0d73087d4c5f1a4e0026372ebbdc1f224d113647d82461b3844d13364dd397f00f5779ec74c231a31136615ce54f1261f35de53f0a61737495bfe8049bd455fea1a7ae324f5e4706e54fb0694a49c135e62a4f49c155264b49c175364b49c17dbaca535270b75ce5fe2838444a2191b0597a1446321a6193f4285c4f57b9bf4884cdd1a3706db9cafd43216c8a1e85abcb55ee7ff227d80c3d0ad797ab7c7c98ec277e92824dd3a3e020aef23ff99734d6ad93fbb2ceaaacc64a986c9fb2c62f2bacbeee97d5057e595be197f57cca12bfa4b3ec2798bacae29205f28f711349592592b24824658d48ca129194152229ab499a471286a464478ee4897c57f57fe40b495dd5f1e4fe8da4ae2a694fee7665dab2e4c4a63b945d9a2abd97f6f4af46565647477aa24f6132fdcecbf19539d344084850238bf38a3658356e5d59ccd1c40aa2b0c2183ad881e8385174a1f382d71a703ce1083f6a7464b40f5fecb06983738530aa9881318425d4606db846c33a3968bf7db861737c0da3469f0296102ace93092ebcce9365522f4ed8799e29a0e20cd6e8fb6f0d65bcf3861aca8c4fc31b6af834bca18632e2d3f0861acaf8d3f00619fb95f4727c65b16961b9fd4d1d19cde52b1f33d55cb9dfde189d71301c5ffdc8bd5cf544f222c9aa8c8bb57c05f3610cacad9373bdeaacca60276ed6314e76e3ab71f4260a8329cd62e1f8a2eaaa2d50e82f2c73ae303ceb0c045bb0ef45efbf9675ac7baf8bf95b96f733f76ff635e766dee9ab1136cd951fe11f266cc694527e45d3543ee555ea2bf78bb0595d2119ac5b7dbe32d7ea59eeffb0d9328f3b737f87cd6ef358eed7b0e9302bcbfd8e4dd3d49141fdd6cafd275496fb5970071b5dff828951d84c5bc1a6d64ad1de4c790d5798abbac272bf0a36ebcb64fa14edd464286f7a9476754b8b6d2f3f7bb6b9b8d3651edb669d6c74faf14f3596fb4fb01964c366d7429dfca89bd158488c1bdce18426aa5002152d3a8b9daf96cb05ab2f590a36b5d364fa0df7cb55ddafdc8f82cd7689446fb2332d567ad197bce5a706db5c3e73d9d6da6623e9c127352cf79fb05963ddec64f4a3acc26ca01842136250c869830a1ded94c9ea0c76b65aaf76c54cd8b43391e837ec2e57b5bb727f099bde02815e24d360a1077de8f499abb8bfda6b6b81e18befafdc4fc266c33699abfa47d8d491191fdccedc5ff68b0b5588c11352d471046c16e366b258cfb8f374790b26c2a6958140bf61db7255b7727f089bf6e41e14cbfd179bda4b149f7b5347e6bee8cafd21361dc4e63662534706fc5073b96c9098630c39ba60030d39ce90c1ce984cf67db9cd7ce67db9b5ceee4bed05c2a68db9aa9fc3a6a923b3bd49c41216f71bb62f57f58bd80cb1595a58ee07b176b175b53e2100c10c1913ba5881165894a57d696354e65f76ad1ab35f76b3867dd8c391f9cbc35e6b3b296bfbd2ba3aecdd68ae2a5894b5e125362bb0a349932634d9421330b0386c61afdcef128691b268b2a084368e4085180fbc60519686bdd355fda585e1600384264668d2a44918cea082e59d2c67346902070b9ad0a4c9cd1570b230e3055a7011070e581bf65a6c08818c5913a460220746606dd8c37155bf253bbce1692843c340c28d3ad88082074e48836571873740c1f2d748caba15b4a4e70a714839bb20c20dc268b28427588ebd1c57f5c7b40b1846984c614c6667fdae1bc7d3e973fdf273e99dae2a5f72fdf22597dee9bd326d59a262533b00605c3bcbfdd792540b81fd42bfbeffad608396a4f95e1a76ef24f794ecde9265cdf63bac54184bdac3c48c5c5231328de5fecaeaffd13e4a18990b7818a5bdf7d647fd7e19224067a0b9bf75e81565890193808eecfaa59f23c118b713a059c363bff4fbd3f72030ca864abf343ef54b7f874efd32807ee97fe997fef294fb2bed91a17d942f41da47294303da4798fb3f6045ee0f0165a35f30d697f6e1f8fb85e254bf846cbf7cf999ce326db1e9aa85e2d2be540b8dfd62fb85926e4c68041d353c4d953496fdefa53e802f7e097e7dee3bb2acb9fbdb498cdcb78f120646065fd1d8799e27cb3fc657cff2a7c005e80c35bb8a339122bb256b1e00d83eb47790ce50410f1b89d1660a63b244e6fe5ab0c31a1a8991cb14c6c405dbc7b4875d83f1cbdcd7b086ee69580310b549b6e2bd34c4407be4f797a455fc98fec8b2661197e27f7f1d14c9fe8fecf748f06928819abdbf2279df2369be349c8088cb9ac5bf7f1bf426f0654a6328c5f73ea663fc56d0bb1fd3a22776ce78dffb2ffbd3d0862e5fecc3f7ed91e5f7f5639c8616e8ff2e59730d81a84d7288ef83a4f7311ed397a4f92369f668e6c84d232d09d218288d65da6203560b1b63489bc1590682cb4288425004dd062b59334867e833d707db071dc170691bc1468b91bbbb5b08f7777f7737428aeceeee295752c44ffef6d16fc7f6517eaa0899e7fe12cce537ab04b310b34c614474910590298c089c7c413465db874d2efbeb5bc0878a5116b0b9becdf58738cff3cc6dedd3d08672886cdf62207ca84f73c9e5faa31197a64a985c76aea20918b7afb97661a430226e3285a940961190294c0556e40e6b18c7116c1ffdfdb47d74bf7d7fd7610dddb6fdad60cdb786dbdf8da4a940d460fbc5be0d63debe8614a0797b1ad640fba50473d5706993b5aefb18efc8d2e6d23e8d67ee695883f6317d2f0d1d9766f6efc8f273f71b59d27e1922775fd27e2991e4eecbed69bf943de4ee63dc490e036166ff8da4b9234b956cff56902369de489a69a8019b5d2381f0ec6f49da2f15db7ee1aa8371bb7efad749cdc92d375973254b13e4fa97d66eb14441ae4ed6a7a10d5aaeb9499a6dba02b9b4c221459d8d14a60217e497f651bee4fefe018c77e4cefdb4579555aae47e1a5660092ad018688e6910a6e65caa1ae9d70ac748612978e5b17d50d8f993e977ed83c2529093e983eda3ae28ab24651a73a98fb13e7dea5dda3e2aad99fe05d1f641fbcb2ed3b7eda33efd5b532ab0848cc640f3ad346c1aaa8012ae7ea1d9edfbd7f7b0868a690d5540899c7ea1b9dfc9eb60256beeb09f8616c7340d6b88e91a5ec026774cd72c5c9abbbf76636a53ab0db77e252b1546d829cba5cdfdb67dd0efefea779daf68abca31d72fc74cc95a7b70ff5a6b57ff98aeeedf6fdfddbdbed7eeef72639405faed77b5a17ee3b2dfbb56265c9a2a3ff75faa80f1d2048cb45fca2ef77b0d2530c4799e29c8feb741da3eeab70fda2ffd31eda4ed977e048cf5edf75fd2ade0d83e68bfd0ef27b58f9afb4fedc3bf9f8635b82585e09195d874b97e972b4659a0befdfa16d3d4ec98f64b69337d1ad650715969aab10f9f698b8dd72ac6a599a6ca31f7dfae7d94292e768cf54b30f797feb45f60c6fafef469a881fa3ed87ea11fd3b7929d413a43920acb426ce16d90d219ea97feb45fe877eda3acd99ffe75b2eb17fa0518fde9d37002b65fe83beefa857e4cdf062b6933486748526719082e0b21caf5fb433a43927a662068c84280b97ebf48674852cf0c040d59882dd7ef07d11992d433034143aedf7f1daca4e790ce90a49e19081ab2105baedf0fd2199254591602ccf5fb0364a5b97371b0ced51c1869b7a7d8e04dfbf6f7d5a7f6549e59fd396f5a3839e5fb7e7fb9cb71fc6607c613f39e5837ee9c1e994fff787b5fdcfbe22bffeabf61d48f9a7e6b9bfe68714d639d1cfaed6b8dbb6d5b4ddbb65aabbf56ad3766525915897b5ffa350ec6a23f6eaf61540edafb438145bf73ede9537faab9b66d1cd7ddf6cd1757d1c6e65b6c7ec79d4ea76d1c4d1d997e0d9b9dd9e9c8f4fb536c2ee1aa4582263372afe178fdf1ef1bffc6013193ca22f978e5f1af3d3e30f7eae338ddea9b3efdebcfcc71b2d799abbcfe643fb3df649719fbdb75fbe7be78bd7b3d1008821ff8dd1b8662f7f8eafb2efe3c1cdefef1950836799bc75761e9458c0a4320288a4239843e7c306432994050144ba5b17340b184797c259a70e941e4c9df4b9a46de8f4af7964e406f2a7d7f724ba5ef2b95be6b32914aff954e481339f2c87bfa107962327927a6f04fbcd3e9249e4e2f9edc0645d2741bbc41c63b912179429a48f096c8931f91a6bf2052fcf1150993be7ecf4411e4e32b90e965be1a81beee01954ae4c97fa4af441a91402249249148a0fef946b863ae32895fcb44b17d403dcbdf83e4e56958bf3ac7fa388e23d83cbde3aada3057d57ef7d72a4c75ca8151b31887b1b4b05aebd75b5b177441201886dfe7640953ebcee78939b9ba72ad2d57555c569c5cffd62a0e511f09d8c317c45715fcc03014c57beb8dafbc7a534f5fdd7a0ee12b710824487af015d8439020aeaa7f351cf0fb40e1f7150455d057d0f7dd2b8a9e27aadfade2d7d7706e7dcba4b2445fff5a9f91acf49e07027def81402ffe0732d52a9ac4eff3bc526934123fb1beed017d2d7df5476fc2f5af883cf91069fa0b92a5b1f4f7c7afafb54a180582a6ffd2d7d049f8a6afe089e8241cc39393bf63c87447d3c73458ba63e98e771cc17a120a55532dd55ba9ab2e89041f4486b747a3af6fef573ff07e05d5b73c602561f0eb5f17bd9108feb5208904df1b81465f8f1a1c7974f481a4080bab8db9aade6a3930d65a2b9555efc7f7af325f79d8f4fed61e51f4af315f85c217dfbff25498af44f8f3460d1491df7b63e85ad01b631a1c6b2833561f57f97f6428fadf1a7395bf47d61e577995b9ca653263d7d2beb1bf7dcd02fddacb8ca786375c5ad8f6dae330ee78afef2f468536c757e0bd0fbe75f90a74b14e0ef7c507dfdfe280fe5edbbaa00b02c130fcbe96af2ef69dcf135fa1e8e5647fea59577610b61627fb5f7b23fe7d4f1441f01341e1077e60188ae2bd9ebdf195676fece9ab6bcf1f5f893f335f85331f5f81d87d5ce6ab0fbbcc55fe153861b2f632a36bb884c9388c2e6b97abc2b2d732c586d1e2d2b185d9305a188c6cb4e99a0738ee8baf6abeafe9ff70cd86ffe99fbedf4ea7710395f517d77c9b876b3a0e6fd8d491b17feaf069d43087c7ae73153583a57deceddf1ae240bb6e1cb70d9f4e210ef6b5a7a18cc5da07fcfbc31c1cbfe0255fb560841e18cb3ad39e0363e9dbe33076cfa5801d2e6132874b985c7eb0ebcaf6cb0f06caae0b3c65e099ed7f2469df0323f71e18b7971965f565c60a8ce526cbf671184b181c467f8aa9b79d9e4cd67363de7f1815be8e0cc8f3a3267cfb1bcc5720aef9b0f8f6b79df1bfeff58ddf288a61e8792f5f7df8f678178c85e0e51131081bf1f67295ed70b9ed6c39e07f7f4150f440f143537ccfdb4e4ff4c43004c1efdb727c75b71c97af3e178eaf409c96af42dcc2dbcdc6a4b22ede4e57d9e770595baf8d6ea7630a8ca5e6ba1a8eafc0bf9ee785de381bc331fc3e10bc271efe30f8bd7d0dc757179b7ec44c2aebe4ed5febe3abfbe08364a2bf1875c711f4fe8640f0bd710320e8d550868637c8843c3cfe1d45d8b30ffa11665259a1b77f4fc8d293c8d15f9114e1f8ca24faef4d6f5f6bf9eac3e6f7228cfa51236a37be124bb86684c51f915ef4f6b5d3572411ebe4207ef8a31f5f85d80c5f2c9d94c2cf1496fe5af0842cfd67225d073fd348f49944b74193c9849f4965e164fb25924965913e248dec8fe8bbc9f6995496a8b5b2fd1b22c51fc9f0af47be775ad64fcecce583236b65ebddecc07862d97a3dd92d98d28191faf80a84bfbf0f7afb56e6ab0fa3c2ff3c11d78458e747cdfff7a3877572f01efcf0416c82ef89216cff6ae00581e2df0d0c91e283578b69ef5af082be8fe9db20ef9954964832a92cf141115b58f85d6c7b5c65b195b9aab42ef3535661eccb8cf44b3ffdacad7651590943ed97ede240b5612ca92c5bed4b2a2bebcc050c23cc6bb8acb04cdf62af301a43fd980e61201bae5339f7c5576ebbebdd8ff06fa793c3f6dc874fbf1bfd7672f2d4557434baf7c557d6dbe926ca40311792562826be069eb11bfe096b551909161a9d845e23516d69a2c8c1eef8deebe4eed65992c9f2272dd70c74b9d9669d872bc69d2f59cc25d364b2b3059bb9b6560cc6b5b4d84bd6dace19cc75c6b673a6bd4619ec76afd8279379df82cd342d045d1683bd28a62caf9d0ae3ccf55368c77c55a3fdcfccc7beb5d871d93db9dee6691e57d587c176765e2ffb359fe32b0e9b9de3aafa16a370f876f9aac6560c8c33ec62c7e89a4df7765e9a2a49af52748381b11cc5728b6e286c05ad5c8e623ba21eff5224cbfdf7ebfe728fcd7b710d87efc7619d1c38ee7ef7dd8fbc5ed77d4c73bedd4d3bbdc535c7555d7370706e6e86d8e84f0fd4e703aeeaae3bd51d0a501934195eb697dccf9dbc13a752b79494ee699ee6e99ddee99c1c1c57f5d6b5fe549fea537bb6dac3a1fc66b337fee33f9afb68eec3798fef58cfb19ee3388ed3591e6e27f77f1f36b51c2d47c3f1c9fd9e874ddb736d8fe7db8dcfd6e36d381f4fee2f7538db8dabba27f7b3b486e337de53795cd58dd351dad3519f13fd79a932541cca73c266dd3199de71fbb8aa7d72f7f4cfcd9370ec8df3f80e8fb6637faccf8fcf96b3e18c727287e3aae6b93d3567a771783aa7a7777c9ae7a77b6efa47247ac736c7559d932dce8ef36ca11e1eedc6fe70373f3b39c755e64eee72b81ed0a77dfac77b6edc07c77f72eccd8ec5719e1d10e81d6b3eae6acda7c7550dfac9da8d28723837aed23ee4c9bde3aaee7872b7e32a1f8efbb1397647c3e1d1727ab41d1f8dc7fe683d37dacfb6bd632ec755cde5e06c3b99e76dd7c3e3aafee9341fed67ebb9d97c70b69f1cee6687c3d978366c723ba6e3ce47eb7c3a6c7637dd4fd7c3e5703bb91f8727a767c78787fbc95dcf8d779dcf4fee17ddf8ca7ebfa8e52bedfb4538be1abf5fe4f215e8fb4539beeabe5ff4f2957fff1d49d18348ee6f476ed6be6f1dc77de847cd83704d877572e83a2b7aeeb777cdade6a3682445ef20cebb6d03f94dee3743a2d45bfce5b8aa733e9c0fe7bbf96e7eb89fdc1ffa10363d9fce27f78f2336bd1e5775a96fc7f376b69ddcdf79399c9703f270401e8ef3e40681f07fcfe5b93c7767dbc90dc2e6e56ed761f372dcfd7eb4efe7f3d93e9feeebe9be1e076fbaf0e607fc017d36d027f7731c36c11eabede4fe0dccd17272bfc5268803e2384f6e6d27f7731c36c59c9cdc1b36459fdc6f2d36c39eb08704baf1b1a09e15104ecb6b23cff6a0100ee8c6550dea09795cd5220e78e3aaeec9fd23fe787072ff087b375e4f8fe71e8ef7e35ece77e3dece87e31e4f0a9bdf0ec66ff1f57155fbe4db83efcfbde1f1244e7803f2803b3ce24ef813fafcf8807240382ba19c108eab9ac755fd2d78ebf972fcdbc1f97872be9e1dfbf95c1efbfddc1e7b6fee0f0f1eafe130c7559d93fb496c86383cc21d9047e5597a78c49bf067bcf901ed8072503b3994333ee67c727f797decfd017b34f006f4d1401cf0470373c21b0ddc0971349067e381cd7047e5372cfab8aa451f53ec7155ab883fe20d0ace7803e201ed9c787268c755a38fabfa5770f793fbcb500b71c29c700bb750dc444e059be28fabcc94e7f09833e68c38aeea949d0ce23199423d211e578d3fa752f4d9c49f1ed08d0f07c201fd70a09c1b0eb433e270209e6edc7195d9e1904fc807059ba19fd04da867cce1c61d9c912767f4e5d8b333faf0e4eec69f504fe826b729f413f2e93ef493fb2f891c912232448e2488bc152491a439a0308516aa50820541608d48d21c435841189ac082cb8725226d6cc0c40b2d28810e970b07ac1049635da8c0056b1421c71d56b046320076f0808105134d8471060b44daac80c91c43e0c1e28c1670c1a22185ddc8724cc7b4488a6e442d118ec825ca11bd48d10e2cf76f3cb95f8be57edb93fb5d241be1e4fe0d8c14d682572e617229bac92dba21619bda1a70c2a5758e91c25c1047ae59882d5f503f4d959a2bdb6ff203a306466abfffda99af6a369cd64d8d8e0cf7da6bda996d876b385b71697fb2bdd6c7fab8cabe4cd6d3138b3d0faed9b0e571150e9aadd1b085b9cadac6d67e8ab5d8c58eb15df9efad6093f66f18e9d7f087f64dda77b2e6940e8c37c90d94a561226e28e23c594c7480a0215b5cda64fb95e5efa106e8116ab0966c598a4b9391ca32cda5cbbeaf0ff3e57f79cab51c732d3b192ccc571fb6335f519687638ef879e970cc11b3e770cc113ea70dc71c211b35ec24cd32c09aa4b0cc13635998e6da6c6c7f4bb020960a4267f0efe989c57878b6fbd9a77f41e4f897f4fe7e64f7b7c16d7beb7df761d3f35e4746fbabe19aefbb17d77c582787efdbbceb2047da1bac0541e4f8f67a1fd35f673b7b6d874d0f9b23cb6ff887ab3a86c2bcf65a83401f8b36db621ff7daecbda85fa9aedaba7fb159bdfa611389e76d308f73b9bed37e7df6573d6c5619d771ad9637f366169b9ce761b3655d6fbf6193eb649bedb8e7b0e9b06d3b79b87bb9aabf7ecc03c774101a439d8dad7fc16c310ae3da6a26eaed8a76f229dbab672eeef418f7e64905933424dc88c1152e399020635196959dddd972b93a0e75729272a299fcdc5c2edb66e1c73ae4230a9d38da2002b68305ed94c56667abd57127a391b7461a4c1381204d34bf1422e190a5450b9a1c31051d4644318bc164dc7976dc681c45a3f6d21e249e2633ae508215af33b2d0052b8375af98d771a336044b0361530c41256a6ca1c5167500210b2a5894652f7ee95c5fc9c1bc8ec39dabaa06f3b3042eb61063075ff8b028cb620a3b336d59b262d3fdb27369aa74596e2acb25954d602cfd2c61b2bffcf4d34f3ffdfc52865cfaf97efae9e74bff04c6b25dedea98af5e5efe4fa771ec72fdfaa50c30180f0f8dc5ea97b4a7bfcebebfc25e7a3e76e209bfa4b0f04b19beecc6d3bf800fdefb7d62e86b44efa9af42d8c4352051ec1ec657352286b939738861402c436eaeb73e65fad95d9669cb126b03b657854b7dd57120d1f67f4f5f065fdda71fe3abf0e953c057e0d3fff1015ff9d3bf21393e486edff7bebd864ded378c023d5c33823fbe8e8cf7a1876b40ac9303f8179bf7c16ffccdafe6f7d3fc7e4c8763488e013a32e0f308fd47ee2fe277958f201a13a908a3e259455815cf166c7230927c0f7793b18de5df9e56a6bdfcb5c9fae4e13ff214064558b7c2b15d61ac5f61ab6161ac63618bc42617636179cfbdc56dae150d6667dbcc5fc555fc7938c6378c790c7c9781329f812e7b8232db025d9c0c857acf355343cdb617e7287fa29da1ce3f50d68271d5ebd76a31927ba6e93dccb95ce55c6b8399259f69b2cbcd621426f34ecf61de49f2af3bb997abba98f9e14ee69dadfa72f5ec75f2907fda2c96fde35e2114dccd5c35f2bf22d2f42712e52f880c8d240a0ae8447a101ea17ce8c76b4191e94fd7c111844d6c925caeec1fc226ea47f86bb9ca5bd941d8fc4e57f9777a3390373b61d39389fcc7582cfb9be689ff0d0d2cd4e8c2b2bf4884cdfbba2fd17589aeebb66eeb9ef73cc12336fd4b27936825963d844d1006c2c017e87265ffd363133562d36b8d1ee6a102cbfe26530a0a15c6b23f0a0a36435808c3668812beb2bfe9756452de844dd39f9cc45c2ce3e8bd3e5808a5c7a3c0b2bfcaab60537cb9cac5578864497993e99954564c96fd05cbfe8f53529e49659d32f39b79b0d3f7cafee363f3ce78ccb217f364deec3bbfd68dddd8ca0a2803632da5506686311ed84cc12678baca41a656fe66dfe3976527df6c657785422f580f514a2cfba7f0857dae7bded675ddd785813370d6329a65ff1236439989b14962d37c6c5e19283c411ed8955d05ebc8a8fce943d804bd5ce5cf8243269898828222cbfe2b1894dd18e8025f200c8c8132d28f58144b2f43f99809829dde0c5dd80c4f536c657f152c72657f140ec159188661483a4720192876c2a678824d5874b9cabf84c59928be4498181365e22cfb081a413f62d0cc55b3ec4fc2264826c2a016069dd95dd95fb01848967d1c4fd04c3c431878672dd1f57d399e61cb05c65eb27b7ab06f06b6b2872e11bf7caf10ff9581f8149ed92f1ec5d687591eee40adec23d8d61e68929b64d74983988371fb69aaf4b3c260c00af3b00659a6a1eb952b080333839106ae4c430a9bc12ca7a05df463bac99afdb44b4821ebae21091d17dd51a0b62b4ec56183d67c41f44954b6c9b78214c6572f2fffa7937f9344464b9db60f4abf524afa992b0f9d81d620350b31d2b7d516e1b6d56ec891de879e1772de775eb899da5b6c82dec6b8aa294ce7a6fb0d0d2c119bb6abadf304b13b77ba8abb1d363fd7c98164a28fc07b4550e899d86cd95357bd8ecc7d109b208c6b6d335775b885e3884d8a4d6c96fe62f3e5a3085e2dc63936b7d8e86142b65d27a0d126f605b593c4c93c0f0577a7136bbd55eaec6c936daecda523333e3713dd50c49f6bda3bd6c23034359795759dd6edee22366dabeb6c4c93757b7b7b7bfbe6dbbbd6610b93cdecd9b22eadd5796b2d4eb6c1b657a76ddeeeae0e637bd262956bd5fe2db4cc575e65d15975d59cfaaa3b155679725c2e1c9c56abe6c6572136fbc6558d438d87bbcf73b3b5e3360d89afba6df6765663f9d5610e6b78ec4ee39f5e28ab8635fc90e1c2f8ea82e1f781e0d71fae0171f8fde3078261288a303017c755bef240e2ff4d789adf3f0ef1d1194c731de66004895e087ae4f621a9fd05490b82db879f5fffaee791db7be8a0b59b6647727b10a9fd1549fbb7454d13b9aeb9eeb8e6b6ed410ebe2e4d955496bbbba97396c33a3f6a74646c7bf63bdbdddbc6715de7ae691a365fb4ffc00faf3b9d28c08d31db37d691f187a12618eb9b6094d5d6c0b834b57d47d6583ec914f6c44ebeb4ba3152d81339792553d81337f95aff5088a42c1ac2948ca575c95abe7298bb3cc75fbed32f0e6f5a67efe7717dca5e72bfeef7f2fac5f5ab5f508c659f7d8ef8548e629f619f7dd240f6f2f27f3a6db687cee0719bc35a8d23bac21cf032f17d8d95dd788ac95076212e47f07eb8ce60ac6f7a0fdbe77ed3de7e45e2aafed2d8914ee3e84f22dc4d7dc4e8411179b3c15550a150034707f96a20fd986bec4ee39f7c96fb7f663e32db93fbef06525f859efe0d91954559de637d34e7bcffe130574931f6e0ab9797ffa6699907f38ab5104959158ad1769a6f7fb91ad6f0c361d58933972d6b99affca57cf9f24fe5696c19ac87b17ef735ac41fb8e2432ba866fa02c22fe270be1d9c6da87c56d493fb3eda133f82a468a97143b52c0ac4b0a1e2962d625454f0c132d57519cce35e69c98783d133b0ebb0ed2efb0861f3d4831961d0e7a150303315a455d4a14d12f6adc61061d62c4d1051b4350238b181898d12a9a4b6b45eeef489a65f095cd19bb930c271972cbb001cab2f589b1b4305f85a0c9149a3469d2840a3458fdcee32b10154d9a702109533469c2eaf798af6cb010030a2a8e780215ea5082d5ef3dbeb2b942066eec4cc1041628310556f7f0828e2f5c81841356c0c1eaf0b4c10cc6b042933696c0ea1762ac383dd9710993ab06ea8fc1ed61ecdaaaad20305d378ea7ff0e3d38fe07fa4efccd7e758d417a184b98ec610e6bd8b108d38c7dbde7b4a7f703a71f997ef8ea74fad15792e9f4f5637c4522fdc95712c9f495f415e6861acad450868637d450e6f434bca186324fc31b446f9aa5173d8caf485f9f96b08972fa134f7772fa4c5f8e266c8eb059c214a62b5f5aa1119763ae205c9e721571f9b986b8d2a0b6aec7694e654dc482f8aab6a88ccaa88cca6aabe25457cda9afba535bb5e5ab18274e276e9c68d5961338b5e584abb69cc8a9ad0ae36c440db242072d2970328545318455a6b0288ac817f4cadd49b48fd2ceecac7d94daa99deda35f2ba27df8b0bdb70f0a93a20b3ada478a1523b704607cd0c8d26917e799399c04083a3bcf4c23cadec7340d98bdef9e861a40d2914980a08166cb5d92ee69b6ec3d0d2d0024731fd3942c3bd3f748207864ee3b12081a72a53150206a93cc3d25cb7a019bcc614a074071b9bde6bf611ad31a6967fdd26f494b47bff43b697ffae58ab1d4cedc2b18290c8a38726967b9dfcedcfeb40f4ab3f5f155d8e308de36c52ffcbef0b688c78bad70765b44fa30668e4a1fc2a688cd4f247a94bfd83cb9f7531ef52abff22cdfe6f7a41e85dd3a7b26de1316458f7ff02059c818b2bac83ec9969128a4892c91e3c3844624e2e984147a0b243395eaecec56bbfaf57a9142d0388e236ccfeedbdfdf0e61d30461db72d88b5659c71c66de0fbc67cb753f4c5fdaab6b9318973ed9498c95a23491054a2ec17ce45627c65a9b18eb6bb9c26ab8b5662aab5496579dabdaa7071a438f34d30c932dada54dae94c650d60682d2181c97f5eda3ead3906d72871280c9b59234d3b00620282cd32d089da133fd1a4a80ca60b263d789c11d3737beaa3f9dc3b2ffa5096b1882fa0e8dc13fa6eb6becfaa53eed97fafe4868da877dff21ccf6d1ef5fcff64161d99b7041a6b018c491cbfac3842cd73b7ad53eb45911d9ff6ae4169b428c2be41a7a0c8f18ada2313c66f40bcdb7825fc31afaab8fafac043a6b98f68b7f16a37f9169ae3fbe8ae1e9e19175d96ad861968c6bfb857694c7a5a9175f993a32d67aa3c771affd69bb0ec6378e3beb0cf41b82fd9cdfdb32cef40f1bacaf6e81e0f51fe3c3f4e6e7f7711cf7e2b2eeae8bc9de7c17bb6d9fdd72bd5e61f79decd96ddbdff6874d73b42d87bd689575cc61165bdfb0d9599fd9d3b6ac635b5b24051813b1aa8591c29660220b205398126ee4aeeeee2f8e4b9bdc7d9dc1582d8c85b16f7fa3b5d61a92941aa0ecfefb1ec692faf4e0abeab2d487fa7c64699361aacb57485ce53f82b8ec4ee39f5e3ec3bc380c8caffa64b1c6d3e71226bfe412c607927ee9154dabfcbd5fca2ac50006904aa9a8e492fa90b2b7aa24e50e3740593db8cabf3e3196d595fdabcb57e1fbd71c5fbd7c057affbae3abd00742e40f57f98f24055ce50f22635ce52f9232b8ca3f2483d01968068247b6b82c402e61ec57f223cb2e7f0f92e57df06f8a8b1de3fd0a06b161cbf57db8186543fe48902c3beb83c5281b0a90fb0b00e32bca727777cf95ce00beb5b82c95d864fbdcfbc08124a531545cdad7da87fa15974b727def2d5976fe32f8f63f92d218ec533ac37df0299d81d218405cd6ef9ec6f0e1b26aef913e00c13569d2240c99e6ba246f1c16a276d936b446bad831daee9a6beceb5bc7a54f53ed1bf89b7defc14dc33516ebe4605f7b6f7c2b128da3e78160b7bbb769be9be0268edebbb9534746f4de7ceeded37e9fcb425eb25bc81b615a350c4bf87beb2f586fdbacfb51e431a4eaea33140ac7eebf6f7bd9ad052ae1b0ab695f44f2fa2a89e20c1c3bd3bcdbd5629bb9c54cf8c7f83014566325ec9d246c8ab0a97da725bd292b7d77c2dc9b27676d55577d9970cb2c6193a46173844d3bd35e78c3e606c233fc21f6fa55637d76cb655f2f0f9b0e76e0b74c7b6e6f6a2ed3ca364ec4ee6a58fbe92d97d5beb1c75c5543109bb6e5cf6193c3e68735197698ab5eb4ca3ae630d324a286a57d57173699d498cc595d6b7f6bad17c79db32db6b9ba7a6b93c1b4d7d6da4e3b8b69b057bf8bc32fdab9e19f69f8e42a5a2d1e5d45bd31d73ad9d4e6018646627cc11d995258ab8b4c314d674a63a05f9ff6b87d83cec1583fd4b7aa3cd94c8c1496044ed6328525a144ce1496441279cb1496c44daed8f64bbd63ac5f18290c899f5c82727f3f81041db93e9da1d218680c15e7faf6975e7aab8d25cb2162bc92e05bf2d2b006f02bc97d0246edbb7ce9759086159726131a58a2b5b80c22f670535922131a58a0df7089241721fe4d650d91adf795bc1ddae0f97b4b7a2f9294e54fe90ce197f62d3982d14ec0e2eed6bf770455226a13964e887db0dfb96a7f1ba4e1042c2ebb6cbf7b9134e9d7df70699ffb0e2560bffb90ac4db2485256fd9be2d264d43eaca1e20b720f8635f477b986da7b3801cf1603e1790af1b5f007119525e290a4d90b7f885f4991a4a104bcafef95e43ec6af830e924078b67f4920bc49932677648bcb9a3f0c442d6d72bf47965d7f4c57967fc7912e768cdabd96dc9e86da46d278b6a19ee7795ec0266b98c62d409b3469d2246b24cd96ec96b8cd6d2e0922681fb67dd0dce0bb5f4d7b7f0f6bf07eb1e95ad5b8f44199c290c041ae0f52b2a118290c0926722953980beec8f53bfa4d895cdab76f30d6a74f29caa57f3fe03dfd1f2f560af8aab7af9b288260687883c671b5769de7d57c4df89f677a1fd66cde7f718df860ad191dd76cdf15fb6f4fc29ce8bb179ff4a3af6ac6d08f3fc235a27daabd08734f0a611d19f1bb1f4930dc8b4f9f3efddb91dc7fa489cddbd8ac169b3a32fe22367564c417b95bc30f700fe32a4d7b11bbc36ae5b97f5fc42079ff23bdef48ee37527b4bfa37599f8638781fde8ff470477298921aee486fb2dad425a3f660b77e2561fae4607693f9bb6038185803a3c0a864cbd56745d231a6d4cc88088200d000e315002038300e0a48a4619e68a20f14800c70a2545a401d8844d23c8971100621e69031060900c0000002000001e3aba77b3ca4e695fed0678f06ed936d92841417db54e554821306f293f621f1384221cb64382e468d365ced45b3efa5c5b8fd06d264f5837a126b07a94b59abc24c54b56a803ea8662fc3147a917577bc28606398867cd1338b071b26cb6bef0084ecc34ff5e4c1bbbf76c670beaff3796620fe444b8ea2bb7fad7960864243b3be48b5ad2b6259b706bd8b8903f0eb4f6d288c6c183ad1e16d2b9ef2988389de07e5109b5921d348d137a28e08c025197254b9ac93251266cf9f6068de1b622ed856a13077363647659eef051db80dde93453885bd1c357e81f9384fc4613dbb3e737ddfae1a736bc4c9796a5c9cdd48067fc9b5471c106de1c8f64bb64ceb3f790bb641da568c518f70651672effe8e09de1e76138989be96620d7ff6902a877b767a0eed0c1043d240db4464c1bc8f29ef3e729aba8b870f35226267455f683bd3961a09cb56f6f8e4cd6fa270645a945744a56c508350c92d58e0a1354a4a46f3012a265a399d7c784552f02a6e819fc49ba3033fd0ad11354965ca53f7fc0d7f65b2442222883ced46e2743a51effbdf5269c02a195f84d74ce79d56badf8a6c22d2b63907994e38bb2937793c9edb8342c5d13eb5530a0c646da49d537d2ca8dcf8131d99714f7cfbdee2504d3cba5414e0ef6998a1e0247e03dac637f5cc21ace2a3e28228acc14054016fb8394e6382381bb5a6d96441a7cfa5a45cf3e19fd28b2a3977594813cbb5ce3c861d4e2a1160bba2e80400c636914a89db5a3ef9e354adc4d54ef405248278feed2bd978139089d1b57037d6c4a293eb0ba9698c93b13f67bb1a9b7ed1f9e0c9fb46cc7807046d9958f40284c761c8c0da3398cb9c1c8c134fe450e2146457c3c8531650f1225c5825a9effd60a8766290573bb74bea426cb9f6c3efe2806f30ddc4df8fbeda7c9a65d587b6c051070511940a35614c00c01697febad1f41169bc687fdaa772ad482df2f7c85a0004a4755a403a683922dfcf05dec8d69c2f039c94c901a834f1cdbe07ca23e5c1c6aa57dd49459171a28aeb009a48711eca2d063cbdacfe29810e73b619b2f37b4d13871b40fb9e17dcb370d79a577cbca6b6cfa839b484e47dbe6dcebc5e9c3c166b32bc9826ac4be665f972aa1bf387ef41e044851b808289d68fecb64c0ac71dd9c3e56a7823200fbff289b37ed0b458852b337e01c30e0de0151669804003531035cd5cb6b45dc380fcd5b8c5eae92f7a26023e1996f673beed2a825cbf9ba8802f8d58e1b700c5e84bd3489235cab48006f0764f92ae2e52d452ffa1e1a392837304ede82b90226da06b868c58f1bfe8ce37476947cb64757347143f8ae444a8520a119a3fabf7c566fd05432b3ea70a91923afe79915d971db6878a1ef4ed7a946dc50ca08a01e2888dad523aae8052a0aafd50147cef64776b54cc82f21fdff83a8f7eb3a5528d18940c5db65ebff0782c9d30cb8ff9c1fdd365d3c3474141c4ebab0359f890ca2bb5704f48363bd25bd980cc661a52f218d7e9752128b0fc92391aa3c48de0c14a31f8b25292a3db6b449468b95ea30bcce857fbf024b57419b99278522fce084e24351a721b5285f309d584b30f895aea4f9495491f1cbe25210f63b1e59d57c9653c1f8a08cae10dc927d97b240dcd8351c9931362b5c607b7df92aa37adf0d473639575f0370cfc3a0490d21a57c3beb11eee69248e1b17d1e8c83e5ff9d0c6d968bc52942b8d4b791f1574cc9dad552e8f9c8d76c425e33c79e76309152a91f1d1d8fe890a2c29f3e93afa89d4222e660afa140068bc8d0bc4b0625736c9af69dac322a93383abb86578a731670cc3d36d03b514fb7d25277465a75409ba5a1722e5c9c945d24791a7519885e5b43dcd0eb32e086a8a6c1d7c5658484caa05d83942a413d9ae58b43c5ebd501b1bf37033109c81c38262d0014f4af8286b4765777bb0cb55205f99e8d646cbb1b3aacbf4e5b2d73f212bd3c4040feadb9e2d66465aece21d5eb252c172b1b6125164c42642635e441f021b07129269347ac3313ee4d7ce1fb174829fc931f20c328943def650af4e53506204b9445d1a14dc5b79bc7b735d934732de51dbc05c044103a47cc737548853a87083c6659235dcdf17d877f8d11150f2ef8a8a1bdf1245ef124ac7949c94ee46d1931a25e9f2df4c3e93a4ba544e9b75c317ead997f1302465b9f92ea52b51f12b8fa42d978158add958ae898ab3665f3dcba975644ec44f3305e30ee422250ec8b445aafcfb187e43750c6716a9f28edb516b07e3858179ce75d724aa6a417bb39032d8e4d3c6d24d4625caf51c4c2fb30ea89a3303603d43d91f342524a520f2823514411a83e08197aba806acc57142a493a7431a1d6407ace94e102297caa853658b0ac20a53593f54f0aea870a04120f7294e970ab8de143b4e8515264b2dfed106215a1dd6d6b50adea9285c25046c00c7d4d992884e291968b31a7eb4c2296f6e7a1fb89c362f54108b87a07791358eace564723afe5db7abf941cd34840a201a3fde08de31a89e5e1b850a6506309b36dc1a6ad6aed7ac59a17e2e421ea182933f0c1625a18b4fec6f27ea8379c8d044701c9cb225e583e27cab31e0c46e8e1a77746f55636e66f1fb350eff534606646e7e55cd5c5c07b39b0a0f51e5a2920ceba06ca4a0a91a1931e7031d028d19cebba2bd5fd010c271a88e336b28caf16d34599841400323bf825df9a5785a5376d4b309aa28b7ca22c902ed093b8b9702a90138ce85be4b156b80e3f28253c33a05719b98a53cd9a851f011054d39141bc95f431365f60190d670c3a0fae8a5fede77dc109309e5d5dd9b0003037546273130800f29abbcd7df90156511fc302d8e62a1d8b1e314a1d94333f4a4c0f718e982aa0a16d87ae856ce866d4bab6cade23e32da07f1050eaf9763b36e862e552e1e14703ceb1640d4cedf04c2f31d139ed08c8a18d05117f7abdced8f1930e3b6cb86c0d7eff4f3fe6a4c04f9eb275f6b0bfeecc0a9e2be5762ce6a540b618e0f576c0860bb724a3fe31295e74cb574a21c12581766a17e608142dbadd0406cab039e09f6a925eedf71525f8bad5a82ef1a3f985597c31ed49266bea6862fe6aba6d395f1af0025e5aa67dd578db039ae2fd7f0a8c4e0ab6a6c6a40a993ae2726ed6989b45673005d84de43e5a5516497052dad6b6c70d1026c486ea3ca2964eb01004aa7878fd0826885692cc5f8c8644669ca1e3f41a54a374ccc47e5518c97c9d042fdf63a24224a8e69e48a0e4f44654a1df6c7862727d5a1b55b991c5b2d740bfaa7211210f4a29921ebea8948e800d0cf9402e613f9ad27e1f4928e7110826d811504de88f68928f894638af86360416c096429d10f3dc0dff88c4936df59117d2bf07ff13a87baf21502111640cbaa275b0828193f94827cedf05b84ee5f21133bf4489be11605d9ef316a242910132702976a8f02c3f04f450b0c6ce5c8a6a63dd8d3362f8c14afe806fb4411c4beea4209c2db0942f0c7442fb5af3a9d3c585f75b664894eff4004b262b3d9363ed003ab2620e2248286da3b01ed3b37845ed7e591452920168e845eaf2cce7cf4ce43abbacfd482c857753da70aa5d336e0354bd1b227a14dcf127402653df55b158c27979512a2ddf40bf455db60d9bf9dd87a2a2299a9e3112f759362d410b048b1d49532aa6a5ef699796078d375304e8e094e88da72e286a3716348a2a36f7747a7cccba23c01c8e599023ef4fd875e4f2542761ce3dabc02e581be765a753ab93fe693f674091eeefae248cae7df84dbd30f94b75e1388b518abb23136d78a9e8f3da40a345c1847b5d53fd88f7a9c32f664cf22ec04b73e22f07c100bc649ecefbf67af857fb4b758b5fa3d2d5a65e36fc5f74420be53642ca4df1384d796e2d5303a3c7f28489052f0e0d2da7d53636ef370348f61bfe9e11fe76952342149ee4659be6e556eaeda1756a7a96586a87e6f6eda2efc9b0752087c7204c0e11e7227d6cada591ecd0fcd7daa7141c4e4c55be914ca3f20a40325d5e3a897d7d7de5c736aec7b829df0ea9201e647e3f355f466431ce43b855904078261570f874b5eb584542c43a9198f19316e447b6a7dd2debd2e1118644708d45a45a613ca282680fb3cac118e95dd29611c36184a74e59974969e7de0ab02b701319d140ef90fb96291d0d1c3aa1c0c33df512c39b93398e678fedae784b78381359404a3a898e12f1fcd0b9a4a2f74ce74f0936e90418e18b40061ff940029723ca2e3134f46f36decc330d0445f01b8213d0974eb1372689c72152945766b44318f49046f61553413d16021c936e534f0fa135c51e8da16e760ad85900083a2c710e438dd78fe71e3ffa8c344a8a381dec5e3d0a7ece113fa1d8753d47110bebb3e22108ec1f84f924ba069e87646ea46d45e1d3f19342a348e72551b3ef549545db3e0f645ae79e9fff8045935d401cfa0fdc3e2d309912362efc8294be3c4648f6a3138440ed3ff0e1c725060fe014c1eaf2820a49c88a72ea1bb416250029713ed927960ca22e36856199dd0f446befcd3eddae8210af370c24843f51d991ed899848d476a1803d372af991f94a15c76d7e4722681be234104e1e877e5e987396aec07d291ca6a8a75a9c5bcd470bd3e23fde706921aebec1ee23110e615d100f95f2a3e3e763b3c205ad7dd3833e87e74bdd3968081ebf0cf5bf64b14c07a03de4e011857e184cb0ae51acf8e4a50fc490b486c7d1b8c55c8b9de75514e1d3eb59d5d73b69eccf1da59731cd1b23e1ec0ba8d9d150f8f26093d12b0ff29ca06fbfbb61fbf24558e918018ee7672eea8467a6e6ce347af827e7b867551e7cac596358db30dd7b237909e58db3dc14f9fe0773fe178594c0ad99f882700010c44cce945acec02b075443cab27178e4a30407bb253d855d281b15ef98e26aedc1068e519c4ca39ed9bd744d2464247f9ee3a9912c757ab823c0964f6fb142b563810fe0f01522f82e6691ef3f6b78146d8708c0caff03b60924305e302aedc5443b3a0bd80c31bd95c1ae06c7dca01a9c19d3202cc2c5cb89b75772609a6fff15a9002fa28510fee87402831d0b0a97943a01d2e009407f65e82437f922858e0498ab1dcbb8e36be4646f54bddb7cb47eaf37f64bc55a56b9387a7e24c0a4be3938b77893381dc63cb63100f035fbdc437d391004b70a4ec74fae41412762f2c5b2189c775fa452188a1c29bb940a0064494bf35b89173d6eaef97baed4a9390f115223ca7f34333a97e877815ecda9fc86719d452942fdbff558b8eb96240231da846ff8da2e299f4b1b353462eaf902f5a3b6b903d76b3c5091c5d5de73cf745fc2ebc81e9e32b72e99def3291430574cf7e898e9cb9d426db52297fa8bcb64c5798c33cc2538cbaf060dc48e01e59e2ec98fa5f82ee8ef0b1207070d1f47de5e76befc78e15cd347fc9fdf866326be155dbde9c13261eb23100b69db0eccbabe255d71bf4021908fd79f2301edee4236ecad9cde5a08770437a175cc6a819b4e03ec386c4c7152fcf635cfa255408105b6f2ebc99b017ad517379f616498c8b21495dacf7234e881d13a72eba166d5a7812b9313a05b7428fb13a94659d76abdef45bff25b0da43e34cca802b3bae62e03a1f78a80574dae0a57def7efd7490fdcbfb4ef8498a3fd10fec0b0480e530374b00818805823aee00d5d34a215c1c963270724b80b82b3f02004ace9bf0df542623f4359ea4d17e560924767a8dbdc3c3a215bab891cdc6c338ed8093bac365a77d4068ce7e7f5106584c880c32ec3578e5a7892f74fd0a510d1907c2e31b17ced17bb22dad9b51f5683a70ebe79de2fe512735d83aa70cf02095e5e14f656cb571e974e8960f4be0e81c60809df57989327bc619802f17e74f90a862554a345c54e282b67b0d1941d7c1ddd55349041832b6698a3e85091052acd64ad1a3f004b9c8797758c764dc4656c2ac14e8a0b69005c02f2b322ca0352bc741202a910f1ea275bdd98a46bcb683152326943d831213a4245d2926002cb68b0960404859cbcdd08b95ecfc38d9ad9185ac1263403d81af08ba0769846f456bd3abc619c3e12b63df1c400ccce152c1b5c54eac84f054ccafea7fea9b796b78c499cd33f0f2971a315cd64abb2f54c949aad202cca67ced57b40d79c0aba2d4bb34798e03fee90db6b021a2a059e326c4fdaca5e4d7260a55d981364696080dbd70b07facd73bfa9a7019eecabbf20c3ad030821122c8686b1119a465e13791cedb354518184c3a13b2d30213ba5a45039a099672e37166c2cb049988b7266474d1541ea0a210a7dc304b12d25c75aaac6331a31772a264491309692adc4de312b64e103e94e19ab91f181601fcd32e6871db59425fa6661338348d256cf406abfdcc34d9452613c4f12c60a8b9095083180d9aba4f0354a276a484690e9e3e88c3e1699e3eb8a2532064a461cd710ab88478b8e0cc0a3c9bfc405b6235098e450eb19a285702243d8ee30c1484804b4ab545d6e367b9a1f05da05140f8e9e1df9cfade78e23eee0a102debc321d036475eaf8676284c3b523a1e4d6ec47291e392489e5a1bd4e37ffb555a13216fe8e04680bd1cee989dbdbfc5f9911cf7b0f975d6ecc695d63e9aa1355574c9ad3edf55922494240136d2e32d43e3254b1042857ab24c7300a90b31b4a75092324789f005d12da66e8a4b81840b32d1da3389d430fb388c24f416fde55b5005425192493c94005df547374efcead3d632321fe0b6767eed84b578b690c4b708c358e0810317482c9d74c8317795b5e52c08d404a1b240773ef13dca1516bebd9e44b279b85e5171be73148e3f81cd3103008ddc74e4e056f2ad6bf7d050675a41eb0b4e2d50bec8e181b084c25428721a87e332ab0432b7a7f01a1d427230c0a839df2040c14af646ca48a7abf0f0db112801d681df7e8d280c33c1ddac1e6e48fb02e008b544a7fd1d72d17026d2c3f6d66e6fa3dc577e33ad50d923ec94abaa3f0ea982edb4e2a8410dd727546de04c55009c01e8e3316ce2686ac9dde1ac00c6113da4ec6e3c081e6f900063144209793c263f79dc8469126e4614827774019e25df5a3d01326d00023d3c76e67ac3726eb7e7314c7c6d85bde2d41e131591c5e349f00d4ad86722533260443ca8e1b1d11fb4785d7c3be1314a6501153e35a60ed607fd4dba164e1ba12c773da5ac8cc787be1f5e683499431ebe43a97670bc84bb557d30ced8f8a1d06a42ddf067d4d6b4472095c7ed343b42342d70e3205a21a84d95512b13a37563e9cbfa6ee63cb66f7dafd009c054f0987752ed3b9456d1898bc4022e69eccd2a3b386b5c8090ba1b666c85a0947d3efdea52287a44beeb290ebfd398f5a4e5223cf2d4c6c913c76d3dac45e1a0a9227a3a2dff5d1c5b109a4fffb43b9ad921327a49e0178a3c48b4f335fbf8446ddb270f58b5528f761579c8175b48f0ee4b89613fbc35948db543db9642e0491ab76d7f4a46588edfcee1e380455c73761f8cabdf9b74f52f08b4123ea24f842a7d7e748cb645222222aab178d96077e51f0a8404cbc3b4fd7b00c2b141c662df94473c0cf612d4b8c1d7f42e2bf6c3ae18c794cb733d3cc5870ee47f95906dde3a9467c4fa711bb8f485a890b473f64dea6cddd0b13122ea4d6e828b14f04575f02cbac72f0baf173db6abf2b2cfd7560588d519d77ab0783559831b31b6e553d8a82aed3e33c9f026c2383e202e136ccfa5e784ce903ae3e013eab41b2d7bda096610e07bc3dc9f43f3be8519206f7639936fd5a99f4c0f91f83ef2c58b02cc0a89bb8ac90336933f00a5dbaede38382a0f7e05e8625feec0f17bc583ec102d5cc8e5abf6c42be22bd46c5e7e0e01debdbeac2dd1707959a9db37203e490477478b5d42ed79f91ba27a80e52deb3b62bf5e7ca7882a764adac6b821352ec7854c2fece70207cd59334b1329824cbec61e3ba99c4d040a9c4ca767f5930e64b94c85dee667cca2f7bea9d465035e469ce220bfb0b41bf1d7c7286a6f14a434a1d4c851cc2f325950e27bedd3b99fc05730f0d62dd44dad895433308b2a40675268f798da8d43c0850c4993c46c1e33818823d215d71ac69275e88005b5319651f10dd9e79509fd3ac98b775464ba00cd27675d2d31525d0020ad434eed312150961c8bc8479083c94c1db89319caef552147c8340212070bd6458617f50461cd58f839bb3fc86ca3d8a50f8966c74a7c605d2f3e208bac8bfe9824f4db0455e24ee5ba71345f6bb04fca9e1093cd7db2b24b78e583e8e9a84902cb2b1e37aa0cc41914bf600c05b16826461117ab0e708cd89efbc496e7b15c6413e92d76ea63ea3e7987954cb1a2b736e285d2b2c01f261253644a7d3ce5b4d10a7ce0474e842893126c342a49a566257940c9cbab3f3209978d89be1a7659c8724ccb29ece630dd4e1a0d6a607c375fc481ab9481c145fe95643c486b06d5e722cc3c09d10f579640b340ecc77cc68200be4f6e5d55f6a095d54ee258a9473e67c994e561bf4224cb8a138a7a0e4c2d27ae84abb0e1061666aa9c80f63506ac288dfb23288b7ea7dc7e48e0257b3c054ba31fd7c6517fb9202c34ade3be7544bf52d5072d7e11fe640604dfdf891664ceec840e5607bbbf6cd25fe7c2f395eb15260e6e37dda9a9e55f51e8b9365fe834c70094cc69bd750f982b6ad65a91c2789d9f0d4923e343e4290a8ae44622098975eb25c6f5ea06f22d5b6e15ddd115dc8366b220906ee538d10c7a285368d41c4621c71d838f9b89e1e6c39cf28456c152922b62dfa42dfc922dd9e22fc2c1fb271cadd492c09aa2e01314665fd08d58a1494ed084dc48bfa6f004a6bd0b60933f6fc2eca226d8c2a364c4c286fa26604ab08f441a6bed2c42cd6f84695121f1b3a2a06a6a411f498469127210c8e4ccbe41deac643c030b88476855c6dbd2c1f6b2015fac8ee60e7c92525094aa96e2a8cb9678c7b01f1ea1097e3395b13c84fb128a9d44c9c1970d6168ee157585ef9527ef9c61873a9b4b0884f6b0d213059d21a2b437a2e0b4ba4c42461b0af9a59969c178f9b33f37fd31e195b2102ea6123233800982e9574639e142fcf137682384464cfd7938ec71d04216335b97b56e9b1dacfed1714a76295314c0687800a9702c596e05178cbfddfad2ee4fd2fd06bcfa2f911cfabd968e344c32725a8b7b33e74cc2048f75addf5c78109a0420eb54a983cf38768a9df76c17814a72a6135eb7178d438c6f62165224b947fd6c3e8f992a9ff20bf61e250452482f699e4bce17586904cc6c355d6b35d168bee27ccdcee4d94ce28ddce5a2da49502bb0d94b563602c7c1ea93f74e49423b335f0da2bf3c2fda0e2db3567b4be740944ce4920b853beca51141ddb491142f644b82b7efb412063ef6493da7de64d80354825c94c9bdc9d3d4f4b1633209a934f11ad117d2248118619185265d4dceb64f0af6d1dc65c63c4dce100a7de54ee5649dae97a43c4049533d417ca9f4c39496401649e74c4ea4ded2f74eb91488593d3680aba680e5b1c01aa17ce86f2878a9b0777aec52e39459a06255da663bae84527626309c9b02957547d0dfc5cd1b3b5bb3b57830c17b2f8661b7091ce52096c0b8bcd83306dd3b942c8bcebd3a321a7771f5326d0502364bdf6d149d94f9b83eb62181fcd310c2e40e9d783d01335b1d7211f4948c8133278feb8e6f50798e965b07f29d5229e9c8fca627ed528167e9676e7293545888e61fcac94f9cc872227370295ccb144779024c35d7d7b1c718c6dbac34c8158585b25a9c44b7475131edb7522104a7b896626511b12ca9866b2e54a36eb304662ec97407944e2eaa1ec0bb7026c8ddfcd59523eddeda3cbf30353fac5ec0d6acc7be3e261868d828f697f4cbebad048924aac2cd3b94cf78669d91137c29c6e077dc494c5a22000765ace7e41d35a2df4d69d8e037ace917e2012cd79bd534e70c8d9e16f5a1419a55ed7f1a074cc5d0658e64055d3e3f617f4b67c92c2b88187506ff097b095270c45dba654b0aad3552b8169189fdfbc8ae2b1f1850e8c683cbe7ced2d6123047822c08c2fee6fef13648ee9efe60d1611a25bbe600968b8620cfc74f6ec1e7cd947e389d5b18b8cc54c3b0776fe9c5a7bb61c78a625043444a0240211661d48ad65ef158905862510d1bf13b1881a42ac815b78d4a97ac50cb99ca20752a9f11e0424d03ec9236551fb2a10de4e4b3aecd4ae68c5e923d1ad1d31713e4ca80affcb004544681fbe7fcef6f9286b69a82911dd0c37b69403d14382a61065da189c402e81fb3af38fa6a4ddb028876bd3a66f3ca0d82acaca1d73514503c10342e19d0273544106621729f0b9544210217b4cf7cd7f5c1eb010c97386dd33b31981846830d2539497f8640e903ce707f51d31adc4304dd5810e22d8182698508be516f150718adfcac0f24312d8e0f6d8cffef0a7918596a642991302ec68451ba0d23cc8cbfaf4580263efcfc2e85accb1dd28a39d2d9df2bf8636f820d9d401f4de1219b0a81bbdb7382ec19b3178cb7fca5dd8ae22f6da9de0a149e34951002a5315705e9b5b357b8daa6b2f2dfbdd8593eb7f0678c26c85a9d07bbeda094acdc669af21799807d1ae25fac207e5717a88f6f209a7dd59d19d8d3bdf5eda1117819c0bf0d2666f26441b0f7f51d320b56f78f9b92d10227210a48602463381ed4ee271077395ac02a793218cb1fd01478a59f10b437a8de5879bad5aa0084843828b221d4cde57f903b021a456005e8ef5c83a4e6140caec83fde50ffea8eb10925f29221a791363b6297ef63d1c1ee737ed5dcd9015c0aec806859dcbc501a265ad08f01ecc23918223204021cdf5992e012163a51bf0b73373a1fe1da56265c09c0610504ff68479e783f33e0adb0bc6cd0c0be19075814773147333fd56559ab92d1251e339a244544127df935a578c473e01946a827e0ea37cd9640a50e225a3806524a9d028465d6f139bdffaeb1456b1aabdef3ddec507688b7537097a5c701c6aa352aeb36cab13331b0b764f2bb1f764f84ec9b62244f5b288e47ce1f6a90c89c148afc0b1e24537cb280829c1d85e36c24328fc2ed77ddeeb135ca14d51bcfdf1eb3d64826a69f172a061b35cdf35138e1e6b590839f36338b5a2e32b2cd9dd0e1e2b8fa08845ec0c09a74cf3490c718592dded2e869758bac600ae95a1ac7e1f663735aba147f44770ba267db5fb479ae995b78852c221fbba899e8c1cf84f6c11d701ae2e51c1e910ac545dafdc437cc91176f78c2afe755bc2eb84b5ae467dae1b3a6952291d126cf1b8367d4be9fbbcfb8808fae8c69f3f9671ac3c6f01ee30da5a26210ccdd17a3fbce38122be99ca1aea8f9e8f949cf83c208a6c3369d28fecc81f71591082e49d7b22cd2a12492da3cf6875bfadd4e912230d99562105de2979ee57e25c76433f5e6528ec0498bf7df75b27d27bb5143c73ba2a68584d99d589dfd97e5cbfe7bbe2e980fff70642cb99300fe354fde898e944917bf133dfb35447832d2ad5189abdd1f5b73cc9c28b34f93e1be013594980e7f33625f9fe5afb8279e3593ccade934394888ccc205ae4c235c0d7fb5c65b7b41b61bc7bed3868066f18918da008a540064a1ac6000d0d63007c2dd87079e1aadf1785f402a5c29af996220c96d3e8132eeee6d1a0b49d0e975987f8bd11c2189089dfdf978ed3d5e817776d9117189979ce6c390727ca2609d5b11dad21f887542d50bd9efba07948bbaa7681a5478a0ae392f751fcfa0cf604cd340115ff28de180bdad287d02668e1ef9799c57fdea7cbe366a400747625cd9a0593e142932cad2511424b286de9791b1b5414121e3797e36f784eceb434744e481c0ea00a18cef858cc95b8103ebb9ccca654818e0c67d2ea5f98e87bbae3f74274208e82c5b01c5385ee5c4848914c05e608658d7f422ab18baec8ebde47c1d3ef044f41af8ac83021a9ffa1abfba74ddafcc4d0415b859a24650f2f14b7c161f8065b13e1c6242a04c8307d64ab0c902946bb16fc59fa6789622ce128d6d8452169a1ef5b95bf8f9d61e4cb12e4d5cb9576696153ebba717fec0cf24e8d3f8866533a350e12a390e777e3cc012627540fb23d9706f7c1353fc3472eb023b099ad8bc4995ea20d87426c359a3e29802b8ad0bc4da58d4c674a256dab3cb58cfb5d4c7b6b2d302ee28d0b253c682205177d45d41b73f9a6d652bc4e83f7d90db9c4b0d6eb4775c1d491d354b3b3a77eee0b7c0ccc6fa8b7a485c9f3e927e28cd2ab538414d13ab679b759cbae727e5ca6d4165bca8f39a3ad8ffb40f05e4a63e35c04783da2a403bfba33e11cbd619fe27668a1a6be650f8a952c3fa405c18d07c4154f706c8c9d2bb84702e254f731f420e08379784c742e5629294bd0c6619a65c69a54d44dfb1f11220e784219e81a372b192e6bf58706213ce9151aedf5e103d0885cf3e94b3241cbc11d3f15b711a91081e586d0cdcb86e8cb8b1c2ba1b4ca99a8626d955e325c27160ec2b1d1fffce5d9d4337ed570a1f4dad3097a61a324d32933fba9f2caa504a05a2272e2f7d66e48c97f6772149966cb3494472bddf860e54cc8c61ce347e6ec1859c8999a09c5c3984977658505d674ac35aa9ca8ed83e9e291bb28666c6a5cd1d2fc502b9503a6384fbe345781dab1144d9e7d21255802d4603e01f81d09cf1aaaf5afde646f80d7eedd5f24c15a82f2d7b30edd8089639703ae746dafc3bc074204a26086d3682440ec6f74a5d6d05c4e7141a3bd0795f531a00b3b5f705a9c0d067f6438e107221f80e5d7fbcb72ce48fdfc387b35c48b486ba07ce37a9e36db342f76f0d67a36f948c86c83137986eea784476e677c372ab607ddb03b878030eb2cfd015406b1b675c7c1cc38f079f41c7e1ff3379dc4f99437ccdc3e753e48f55a761279738c311ee4b5287385bc1bd1da51ccf23291190362aa486568f6e5a53eac627d4a3dc8372e938fc4715db18c163b84afce7fcbf5e5c55502de2e4fa97a87068d682c0b5c339e093c9fc020f7070e2978e79b7ffa00770cfb1ac6df9c2686b6ba1de16fba12af019a96b21f9a2e640362ba32dcab1e1182e83afb52209401b0080b5dd03bcf9150090de6e580f141f1fdfd98d687b13dc52b27239166bbccb036ef896b4a300ef45ad05b9613ef0b5477e484343be3e9e54c1d742d88515fc0866ede66ed65d5ac0f247d68224ab3384d2e15ce43f1379dc6907cd1ea76a22a6027be8bdb94821bd81525913653f6be84189d1691eef345a0ed08fbb4128354d3507542e0d4b80f16c05b300e32dfaff309158bc5ec0a54f20f8287a586bc1d7c6fbfa432b3565f8081c51144e945c93366002136021b94345441b8e2099a255cb24980149976c8cb69faa8eecc679062524918cf0b709b7f33e5ce34a86c51157eee4a37c49f86503599efa59043842281e0d9c8ac479b9a9ed03867afba394c808407b47e33793c1e123c9283b88357f58112d067267aec8ccba4c0d5a34ce5a18282d765f3c9b82e796d414281d8be8ff12c510dc320432dc26ea6ed502c7adf5b6097c2ce2bef843a81076908b61f296f247fa4ae1c7df92f4cbe2c5f5ce2e0d25e27ce16819cba791059f9e9aa4540ff2415a7726bd57549bedc13d1c23f4f0d3e339a0e490eb0a3df0582607ef252249144209d5c74a80b62dcbe980bd7f406a4fe253c9777c033787650ccd124a1b377d6db2aec6f623ad2c7b3e53bccc1a64bdc1c302b44b8020c5f5a46a4f2c06a121faa862c8e8fd4b1d2012b46d8c25c33f90fbb1f7f1e6ab937f0aafc82cf24590c7605b2270d76e700efa75e9696419b427c683f693392845a2a1703e1bec067c7b58e554bc4a8e10f676e18c9a46d44bd39e14f6a803e380232c87a4f86ec85c610f709402e741929abc184d7974a2d5ada1d85bf0c626186a2f582ffa42994a26c4e532ca837ae080c777da59e538946ca609282c2a31cd60f939fde1e3dc6721876ed9f22e3ebfb9d94edce993f57912ad640e680828e5105fd222f4e0d9de6625522e83bad10c32a5993c18c35240c487252b4c19f309b0744449b39ae2bce9cb7fc81064168df09a09e40fec0207c23c58254943195dfbbcf4ba35d3fa6c8612c67327e498031c006487666fdb48cc9cd4f4fe613a7b0bb7e39e545d6285103a3fe2dcf21fe62495f52d8bcf1558020d6943aa49b3daafcb0c11d6edc95814a554419c0e05e0274ac5380bc3f8814ddb5bee1a691043d6ef565c1350002f404029d6a7e3fa5f50907426580dd98d9be03564c753a0a9753d9b52760eb197f0a43590401cdabab486e841543f83c208d2e69b22fdc756fd4a588f7289a3e442d8fd0765dbcdabca44d3d71a0d21085e70ed6818c54a758ff4bf25e219387d41a123ed9c0d95b198a6cb131ce16e27b58c3249a446c341b924102f764e9187c7aaae8b9206c04daf59565758e45319b8b7d5d0b78120c494aa0f397313c00408a588dc5a976e16345bd247b3d8180e3f38aa9c5c70f87eb7630f78b376ae4f96bf4e861e795986245ddc729271a5d71c20e041367729c3039a3fcb33fb3a3260be3c431d147023c4e5f737f9ad0c74732407eda23434ab0c38e498604d2e1541654110bcd95ded40ed57cca9d5b814ca68d331f38b2986a72acbead8193366a1e7a1ddce6b92b980ddf94221c5f8921722b05a6053dcfad10a00368d3223ee9704cdb0d3ce4a2ff58d26503d02ac5cf55af33072afd4207d7907dba4cfc1fb52a73bb100d9b7b9654be6f915c4cce58c252bcca7695424bf20d29f5c874230bba45bf30fd053b566234138427a9cb0079039f61424127dcba23b0ad82dc63fe9ba625ad09358665ddc60de9b259e75c3968ea1786191828f2d2ed5163e54fdcd22d08c131b1dd8981da08000f64e9d8bd58a995796ba3c0aa567a19b150207b72cbd56db9ebb5663abbc4452210456efd894f97700bacd8e7275603f02832972da2842399c2dbfe47f27d60b528c8275be88c419db292a19005880bf550cd1e59bad460f3904f500aa27ef405f56309e0d988e580bfa8f14cc1c68e5939b2087851549d390d483a66e7b863bc881e510ec287f9bebd705ad1078809e091df21069235135c12a9af70e24f7d67332c60cd8c006e3ced8606223936409fd3f2038f59eec4c08d50171c99f7aaf187c5465fc974defb86ee193ff550755061683908d52f8f4f598131bc83832f0baac297fdfcc61b1e9b362d2cac30aadaac1027686095c36c9238d52c6f4ab735b9729e919037f78ff89734b12fc06b04119d8313e9c8e053420aad317e6363404c0d5560d0e52407920e98273a13411bbccdad803435a300f2f9d963ed198e1ab74e1ffbebbedb53fd6956c5ede32b68749abf06ff82abece82f35f0c0388df614d015f60cf08c9c698c0241a8be4728d482abc998b06e5c0654be2056b0e258d7012df36d720725a164656cc12837f44583d51847a19358e55b1a03869d8b1bfc30daa8ac94b41a6d702638fbf8a994f7a7efc586caa885dc1ebc853135fca8940d0f7890b644ab53db0d41684a64d0eab41a6f20b5638fa06ed3a44c9b600205e7405ce8ad4fc2b5f59a884a94bf6247a5909aec9985fb487461c90ea2670cc0edcd52388d7ba53463ff11448026f7b48a42ad6aa5e554147f0e3313b822e33772ea800de7a7ac82882943995004141493056b0012ad3eb3f13d6f9abad475652122ab4580f44ae41c994bb2ce81841e68684dd6b3005c5fe65d8daa6380cda32c63ed7863410ce2dc2fdb45eb546052585188214453b3f84460cc2cd8948b1cae56ea480e84a1768ef20dc5712cd7cb53dcf4bbc27baa699ec57123a72536c15878ede8abf4a0c42469b6654321eac8a0b7dc31e5712eff800939fc282145bd855e2e54a02781231c00659cf22df20bd23d2e2a7a03baa629f96a4640fe29c5f51672c8995d189741b1023dd5a79bf64816b9684f2caba55c1a0679475fc23c874b224e8b46fb2cf77fc9c2440f839f22e4b4219b623a2ae859f1562ca46a4078d13693e87ebc743d1956fabc7341c10b914938c2d10704d044c1ec84f28ca8426085a2d12d12e484017352e6a6ae8ff55b30c3e0d9631fedf2688f0a7ef711b8424a9834195702a104313e449e420b830d909904d3809c90c107a340a990c90792c0b190b904cae9ccfebde53239f060b1c619892910f411822347a81d438499221dc15321bf294fed7ae064aa2439e23734ae74390931ea5081184a2c73894f9c13ac65fd149c4ecfc418c1891b7a0be1e997cfd888cc16c978853545f2e1320f91391b33443a3a2b1af085aa322ea321f2a7eb788a2bc52942e994d926ef283b3cf371d998768c2efd3dbec0e0c5c77964690035c463eb605b511802d92671009d3c59135ef3566fc0b8547cb11486e94150ae8478b867f0212a323b91262ca4c78449e3c4ebc161fc9e5c3e9d70324caa4c07889ca358444e0af2fd1a721926a3f030588964fc8ad89a42738fb106b8fdb36e66c88ff16af5b16f9d1474d9d2188ed4d00ef01ce01c87ce51497a3e7a7673b620c0540f11a3a0b31b42a6432432c5c9b31bd65885528835a559c533b58a93db435611b10844edc73e7451ae5bb737dd9c9e0fefd6d6057b2da5be809d5a7175d55676a9a50f96ead733fc13410cb19c1ebec6419762de92696e263eda3e90803a7d41e83883002695a600cf802ddf277e216d7bd565a04a5696d4dce36add6aaf46738538af1b86027781a891f64cadb04b6fa6c06424fa10bff4de4c761a73fdc2818895a850e94bf81a15f569f24852a1b997933bd0a3b4b43fac0fa481f2dcf6253f0e1bea663077f5b45f84df4f00a17dcfa8e8665eb451a3b792f4035d5f6b6ed598e623023a05873052253d70c11f85a96ad991df7ba2251ce453b114a75de9d1a50a9eba524bab5fb56ba449479a8916947178c072852bc88d49fa2cbc08627f6864c95d908e0d60d0dc7d3de45d03b5f2a01f4d5a4fe167e2abbe2ad1ab1e200dff3cea97bbe6f2e34d27d832da954b120d663e7ed1f0ca66206080d0b9e843611c2a987b8cb2fc5e961e36dc75c96606482747626c0e86871e711d871ffd87c764d3e90f0ef202e79ac30aa39e0c25d770710da53d08d6d9b01f58462ba906371bf2e6c46946ff0fe9fb1a46f92d4e49eeab322255815aa8b0d439a63b9abd478e19d26a4a5b69a55a4c5ff56bfd5a304c2d5b4aa131f9298a24cc0a90165c42f3dbddfba6257a0f950df9ceba2c2aeb8756f2c552d12550727980af2a43daae27d2fa25853c272ba05adb8e1e6b0802e3a8bd95e0d3624ee08c858f44e26c465d1a2177142176bfbef745ba571c0b5b9e96ae6be058066d2cbaaf2c24d9b4e48394d709677feabc2fac60762a5abc1559d46326111a440aba7af6b5828d3c51e872288303a0c58214c6ae2da920bfe0c527f5a7967b16c679d5fbf17a4032ebb55cb3f866a3519258d885a633c75ba0821827632e5cc9bb26cbae8a6c82bfad6755e517985073317e9b4b430344e7dc48b34134620b3befecb2f1a5a230efd4dd29a90c6fce4d02f24ee8a87d5166f01fcf1de1b4481065fd5f0575b021465957a554947b8d3099799751137b745bf7ba42b2fa2d92149b438bd0e625c4096538ba05e86176b739482eb17afc09016bae0fabb16001ef173411048f15dedfe3aeaebc8ee6ae3a31ec66094f9d68fdaeab869e8e0e5a16f81e97abd95c210ff45d44860d9ca73d205b44a3993739b6a09a7e53d9328b740602df0dbf9e612d3802f286141ba410e80f5e403e584533e872b3c519ba817412198400abc141828a3e697d9770fa0212e56aebce424c3a411971c71c9d71619e2a1d6f8434fdcb8ad0b2e46220497cd7aad5153696d80e44d917b68e2e8cecf87d05b91072986b099477678c9cd3bdae933746bec826095fa3df409aa1efa2dc744f82a145ac36d6ef2337c0bbd290c9a8e370574d7504895ea9153874dc5083161fe9e3e1f6d8b8e2aa757d22be89af5364354cbdbff84aa7dceb448fd058dd5c64494db58f4878b8aef105c3425f341f2e340a192d033c0065cc0fc42c695b815198eb2dc6ffea77254a0e19c342194f79f25deff3fb7c2f7e5e10400bc90604e8f2f9cb33bc5864bdcc412b0f1f0c0de1ec24ca1234e0155156e5b33ba8465f375fb990d00708b7ff6986a28f0805338927ed8c520711cf935ef653b539cae4d3c792a39fc59ebac508355d2caeaf3129dd447d0998642da8cc94901b016c2dbac8d4f067f12632fab5c9e820e01cc813785da82dc3c441d9036c1d4e3fa01131a5c2d5789fa2f6ce4c90b923fe4d7ac7ce97108caed728b6387527b9e6a5699bb06a1c66eef972e7e97d6080d0c1ef6b5071c733b44e868b628f42d0def7ab34387c173122020ed228c189948a07005dc77d047d55d54abf174c4ea610bbb3916fd1d9a1d3cd72ad88196de7314c58e9b6a69ba02e109fb3f27d010ab13d0edcfb1c51c76c1ab60caffc8be9ad02b08fd80f12f2b5074a0169484ef39022880bca655dfd7359ef68ecb2ee73a46c3483ccaf94579f8575a39868588139a94d3ad30b17f423985958a41aebe8496ef03027bee2f19fa94fd65cda7d2212e408bc0afe1a7095bb850931f0201f9c1a6402d48324cc526aede5247a19f8f4b5ccd8289ccb0e70d1459f68deda3498b1e3c6e6e95343650835c6dafcc1d988187255a3ef8fe69c8434a648426375c7adec2a3d230a7b7e3c5384d7dba7b7299990e46694edf4c1a2d265445829985f16c78e93f27aa321523967021296373c81564ca5f3153b2b29d96e7b3d2a689fffdd58a9419e1de11e8179a037189ed7c8817005a5cd68643eb5f3d3b485c555ad19bf4421e6bafde1017aec14a6958bf597e46b68c51bf7a7fb730391a8c730931a15704f7279d115f5ba137f8a185626ef1134ab07d6c15fa782d1448c643e65b36535e8c4e5e0e455e3e1e2fc8192f11ba59b0169fd1ef30a0d9a71497051b82ae4faf9a2985dde3ab41412fac33b7d3de76745d87a50e762cafb2c1729117dce2143382d88e19f03d9c6324806139effe487556f21ad022013a7920d49ad677d867e383e1d89662bc0d9382ce416e0465f3ef012d653667c9dc27ea52f8fc71a70b57fa078fe28d67f54e0f053a73cc6b6b980912aa0db1511b5666dbc691df377d82efa73169aedfd808b126db21129be1b57f9add48d058a35814a6c4a24ba285f7b4b23b0488cb5bf75764e86f87a9a2e1a0a2543ff5b2dd0962129767a4178418677ea871a107d5680cd1c86b5294d477ba51faf2f0970c74d6b33f40ad7b8252bf63b2734613fa49687d468bc140694c257504a2297d8529234307af22297d875f1d92aae238c898066bfd7e5363b7ac58500ee35625e5d82c392d47f50606f02f58ac5e19d57811436373d5edd9090cacb2cac0a8ed6228eb322a0263b58181e831a510eed380e6c8a192c132d63386385bed725fdc3156ee2338c7b6e8225059effee196617d95d194019661dec72182e062ffa9034ef2ed169f1082d38dc75fdf187175ebf86e78e50f4a697b40f17177e1749c080fbd17f82bc1c4dd26d15d40040010fdb9225824a322969882e9dfc0723c4626f3010e0b63954c8f290c70bf8a6f8a742590492fc640d2491700ea62f91e45ed871330199e7c0f6d8b42891a1d4a796366e216d33d2d857e7e1b5079763c880767f1a9d3052b722bae5440960c57f9b408151a381ecca375843a78502addf63e74438811210ea8b7f23b88bce415ccf0709700247c571d858f5ded328b40593364251adf29a646d4c5ff41e91b71123243917868b9c2d0be55bcd8566058b71f02302036247ebab5c007a480502c66bcb101807e672ae283e282978f82f370f0a62000d91cc30e06c9826aff63deb2a35271141e92e36230ba50b2e78f9035ed765ffb3f6cca235efc9da0ec95270c0e2dfe950eaa72912f32e9c5e0c07b25fa11ca21ba58387cc3d0f2ce8d2d486c9017259093adc1986584763094d6dbe66bb22cb53ed257af0bbc6e3859702b6365cbcf5d2650e00509d8dcd7138276e3414f48a0e013b0828449b32e1cd8985f1254200425507a4879202c400942824afde6033ffae05a1e88b91d815f59dfe0e639d980b41c19f658c2018a4382c20a790b44de48a55dee5448d08aaa52c902db31a418ac460b5a6f8d54035739940466b51b4a548d3abd36093ee91067ba0f3326724047ecfa0c5e641be7b7ffad7e48600ce055f9910453a4eff0ffd62d2ce3815ac67029842c6e12845c9b184584242b7a3f8b3409c4baa1ecd1d659f53e14204a6032db711df90f80a663fd01f26ab59656028c25c232d0066996a044988dc34fcedf1bd0a164d1f68b3f7b79b262dfa07cad5c2c81e49d5d1d8025e0ae7d0c5b39dfd251fa89448f5982d7e911104b30b73319b0606d359c1e3d2e0171d9509a26ab2f819949d760603b624a076043dda531818db5be4cb4a84e263281ac010d01cc889fb7e43b6a37f1de78624d221304ba1845cbaf39abe55c7ed96582cb4941209e86e69f4688043f35c0f38edc4c10994f224e7ef5206eab0384aeea58561e510a45fceb3ce8d9a840fe33812b7dbe0b7e970071ef50736d76a4043fc38e6b6c5cae127c7a1d064b97bd128c55f88152a761090474ce6f08f02e97515488fe2d8ed21d8fb96308feee47b65dad7580ef7e83431c2ff01ec379a3f0a1cb7a67943be8f4ea65de63a74df361b633762089dd01b0e8712e2c4c4a575132260c8a383f82c1c488b2ee48b2f1b8ed3de6b7f50f839156da32c1e85425426a3af6ca62dcff23b4143b1237149460e84e6ac09a568adfd2a811de718fe44071712879ea474ccf30208ee2cf013e9d6d0053de0350803f80cdf3071048337c009cc9c9aa8404a8e8920ed290cf9b2fb91e9a92f501862ab3fa5dffbf094dae6ce2774faf5a288b2156b58af665400ca837c197273049ddbe2659565e3b5fe2b90c1ae9dde7ee86fb7d1f0939ddb0a9e6b8111bae3bc0e3895e647dbb9788bd699a9167d064fe55e8ce31f89feffbbdfb1f38827d9648df4ea2f2de8e1940beec0fe94583964012e3bbe6f6a2c3e3841d4a950ed02c77c71ff8c847808ed8c27de65c082367259630a32cfbfe9dc2bc061b46cd3b58e7d2cd311f8b7b0b58c3e2d4a04693e753bbee7b280c8dc7ea7f300dd5e89f460278d3a811d630a95e23205f3f19a708d7eb6f2b43fc0a4513f54ba920be8299e481ad762fdef0f1126192fc663439981bf2be47f7354b4feca29b05b3e9435516d36989096836e17c311d315a9d3a313457fa3ed4cba2e2b00c462df89f3693f521fcd636aba17334a6ef979682353d81855bb62fe6da8012aef579d29a2c8a2b1097251e55d0936be32e903e51315f8578f5aab746cefdabda3c4794a78c7083489a5d42c1fe4b46225c520ae1b0488d069b25fc955e43cbb8cc5befcde55bfac8055ce486255cd82d0fe202c8d530f739fb17398a43508afcacc33e8b1c122a2b8138b66d8b5cac4f074c98f1526426a267c7899a7cc8f361d062039b13a90a893746ed27f7fe07189f063334b65665f21bc69bb7089aa0d9a83507688895b390a2db15a00f5882c23240e3c3937aa7ec0b117501760a4a46114ae7cbb1c0b060c591ff58ec358602a7b971f9e8fe3cf2ae1d6b7c82a061866c5f16a1348a1ab64c57cdb1e4ffec3a16335a5a495114c65475a7e2985e019c10a4f1d114dd40264b311f711152c49f5ddb790f9d575d0625537851d133a14ae2983646c7432d472d888adad161e9ef1219b45056d44b5a2ab051c09e1dc49d6186bca1dde4c6b9f38d8c0f1b5664f69d57295aa4d38005874ff6a0d8367ce30e4ac6f3af5b05fd9ef065b837e4456bb163c3eff2a695dd77e3530668fa638e1bf41d7d348801204840dcea88cb96ace4080eb4a00fd66be1ea2776dd6d0867afa2b74fef4313cc407e95cab6bd5abd949740dc1e0905d618c4feb169b52d172cf8c8f23be304dc2a4d603957b3e77f9ca8a090fa21085780bda0b86f1b185a899774aa0088fa42d5a5046344ec07b6006ce3b639e16c91ca498bdb3845b288c260c593aef0e043b148ec88a08a6c288cc8a3f8e81e3bc03b5948c3b1ae07555a6a775f3733f0562636a21049666a93474fc3029dd95221e0dcb08f29f003fd563225d1586c061f6dc50fefcac62071c11dd9beb91c08190b4436432e94e1932042314df657d0ac80d0608a14a2cbf0901e6251837555a2b1237ebd7076f62782801d9394b9ebe1eb92468dc3d6914129d62a4ef3f23df90dc69ee06a58ea8d589c66fba6a30af04c9a0551a7d6187ec7613ad51cfc962f382bf8ccdd312c10597812a9547603eef50eeefb29207f5459739b28c70acb9b91b3cd428d0e777a8b30b08da53543e247a60cb6c30990a174bd46d229130dba76e70a4db44658492b3e20a25695f273e76c386deb1c509abadcc46815864603bc3c46cd6d679ca122e66ebb6a799e62a91e8d08dd7638502071b1e32064bd951a474408b212edf231ffc1abcd3c904db9571a44a10a10a0e176dc9b2988cf26f7f9e0de1d62a9921dac7b96fc112ff1d40c903b417712d6e89f708252fa0abb2dce7a38bbdc934c6fc89f4b9fb9b6702e2eb7e55e08b1fcde21a1aebcb6c50fe29a6b5d37fac2ec6c525e9b28f60c8ca78f10b4ef31b38ddf7953df13e9d5365c6734fd37ea595cdfb1dfaaa53a6070b2119a7fb903f3b5ca63f495ff84104ba64f24bbaa2a86082d4033af5eb68690f7698172b5397df33d8d800930ac55ced19472f33fe7471a91cdb17fc913b0aadad2a129c35177c9f6b03d7e84251c5d720392936431f124abe1f6f43f86265db43e78eb1d463d8bbb811aa625ca6f0e8c2220db07cc1dfae3fbcfaa43b3e9426172066a178d0162dc67852833287405a51e6764c65340e5f5915beec8b27fbf5021683d4eb939a6a538f640d05e7554d975ef5839f4aad7bf9b13175ea73018041d8f34e2c9e1c5f4eb4e1eb051cc050521b333eeef7055e60045c073404ed7101dbfa092df823e8c8318e0d60e10cf63ed5b71657243cfdcdbc3375cfc0da01f1518fc62adbd4bffeb72537f169587a8aeedc39172b6a2ee402e1d4bacaafadf5d8bf8ff6b075e94ba0a0b733b5e4b65cfacec4d78128015ebaf5c075fedc111b6fcdd52303dbfcd9929bc615dc2f6a4df7899e83918bfe2ea77a665e5bc2b1c4536508d6cb3fcab1764a8d9687a854f867f7035182bbf590906f3254e3bc160b7c46de98e67ea5b78138951a06b3652293f39e5ac3fc720241acde543db4f004c90a702e9f384e5a7210ce1e9a036f90ef807f103982a1b8905159f6766c5da6224471e02d24e2c66a00ab2e0c631020ead141520b55593c1b3da4dc1ce15ea9dd360feea6ce63e9e1b97a0f351dddf387c7c67538eaa9b32dde174622ca7fabb9ad6d628ea0b08d42b7d2e64fc29ac8c2b1b0e7bc419582bd49b1d993d4e43f558ca3b221423ba7c06ed265d641db47ab29441f98ea66fc913ba9f4910fce417d4ecf2f45aa4e874c482f8b375699fbf699baa5bb2c50415d14095edb00fdf88972baef5643e22e989ea8d7c3c7b5080ac15f221340e68d46ae1875f5eadaa3cbb552ec24470d4f68c83daad9d03cb37ba3239a32ca7496f9ae19c8a88d2fb540015e6e67f967e456efc7fc3b02efe5b46b9a725bfd2a76783d5065aa06c1aa8a0d3de2ed7df55422da419b5e00233d3831f4f06a2122ea4698a7a128023ca5a62353bcd6da46ac7a5020bbf24a4b7e8d5dca25c862b28db2f40995829fb0e0e229b34e936844e72f15ea952a7830d859bde6b591e03feaee3320255d38c52d55c953f80b89d4aade25bf5232dfca1ceb764fba2432f8eccbcb221bf46344b2b96272f565ba0abd702d4e1ea48fd6355c211c21aabcb48ab3fb70700ee9523cfd8c30a4b88010876b366e134e77b94e39e866f6b708d469569083a95a834e29a593a00be99cf1e289b77c24949580fd58e4a0f808e85c7802d7687c483216e8124a6385cff3613b8b9b91f71f650b444f124e943524a6e966c81aa08e08a03be443d21ba36264741f537175f937c6b64eb30ec117db960e03cd0f1f0c7af901870c42d90c45482eb4766023737f7a3c41e8a96285e6c6b61205471e01f6175160349f847daff2b1fae8b12401649857013d7c5daf6f61a181c7036b5b2581ed80063a08c5d8734ff1d371e6b40c6db2062aaf635d793c0e395ba4ccd7d10eec71a0e92960dbc11dd92fce03ef371f1ba358ff3121335351ee20048513e0b342b2b46fc55d9c6b2a41bcd6f4507e66eb0c73ac09de7b86a3a81f1ab2bc1e16eb35eb24857342a10c913b32dcbeba069e204b7e50a902652011c35c21d172578de7af1a6883ec82e7c7c1dcdc3f50ff1e2bfa0309db345274211e1d35a541615d79a938b667dd1a12d4547aff56b08d3b38aa93f8d1ec4661732315ad99dbd6d37b08f3b386e548edc8408c7362bf4bf65517e037df26a6dbb7690af36a321968fe801f49c2e7a19e167edfa45d0748495a0d8833618d10d28c6a614a0d9b35f9ef9642e7fa5176b5b2ae6b04c9a48665baff7c0a4af4ea721234db04693406eb410fee88a2e8c241da043f5c303c2767582d477a8029680d3c3283aa0ba344a38eeff67ab83ff42fffaa7acd89d87970b82fcb6cd06ec4125e48801e8c193e7e42aa9ec792562965a3aa3edcb8841d11d332fdfdfb54ceb2e2088f51806d26bf06c1cf8f96bd87a559af931ba1e57edd480f42da2e9fff4f4cea6ebef3989a3ec115eb68ddd2edf98505dcd2cd9f1e52efd15a528c8758b632d2427718290f25f6a283bbbb9b037d3cdb9a91eaffbab006d15f338a5bb7346d087c965b1532248f34e06fe1d081f877272b5b055b45841ec83d0b3644b26140c732c6b5de6f29a5ca55440b35e465e85fae89b127f01677eb53ec63959d7305e31ed51484256d2e02b31424a5a5b5b55170d13e250a71957afc833d6650093aca634bba530af85a569868147570f97f5027cab6c89c3edd7a88acc1557aad8fdfe808aa7034d6be87fdef5bf5978e00d04cb84c8367c0605a8cba6298946b2d97a6f9ec19dbc2b3c37322fe4b4fcae7d04aaef3101bf14851c38db221a7b6a467c7d200d87eb83cad9ff8fe9772596977b26fc6ee300310ab901d3f62fcb0745e6e069af1c0e5a2f86780fd96f5e57ef64df3f17fa105cfb45de6b11623a387cc610df5b6773a019fd8f3a652e50e61f8242a847e813daff838c37621f9a3e0a2f5372cb1a4545fa3fdbdfcf9560b9de8642e0553d7dfeb44cf79e7f596bff867043267631ed66b6729e920ac103dd4b488ba1777636ecf73af0e3343baeec23bb22d8d3c81b6c77c8f169608eecb4ab55c3d1500f09268ef421e2ef8f6f455bc652f8d5ac97f92703dc22edbf38c5a93c9828c41e526cc456fd3d8136f1e289e26ed92e0009dba145620935f112b6acd29b0eaeaee1bc0b547a7a1d5f7aba686e66446d5ee4d006110e8e285eb081b2098cbad284bd8916787158a07b2a1ab5438e3943b000e9429a5f71564e070280286e7ccab10a9dc3048de7959b7b971ac30c62cd2869522efeffb0457fe2ecb4ffdceddd5e60583a23043b1d1125594492a19d6a4b2a853fa64b1c516ceb95fd0a3e512fbf899e783f41c3851243807e0b70861c5b0909acde695cfd970c889230923e9ebb2ec1de06a78b9eafcb499bd1af107d6b8b4046f9e2f6f0ef7c51a5f6ab16abf98cb906e595ec53009264fa54813458f841ed7010d028f6c488cca278d26aa594b6ee3ace126815a5368fbe86faa7de26b1130439c06d47bd66769e7e0092d601f6094f02a5ffeab5042e461a57cc594258b9000afd30e5981223a32feabdd000b89dab64f2c030959a601b73813237cdd36ca8d163d56d2270a4e050918ef25ad4b75f47379978474dac0247907ee76ca138a6dc6c10f58a34c0714cdfdd98629691b395f9054e7bdc5642c4af6ce313e89d071c3f77b8f5646f3026cea7e8c010e7fdc1b58d7cc8abf4adbac6a68bd0a3b83c4f1d36459abf184f3aa0c94ac30cfa9df676917d81fb4cd079869bad12a4680088a5c789b3bf563930070f62bb3fdead54ef22687a75a585043348997ee555b41b922695c89db2b912accd8c45ec04d397f729a76fb3333d4dc09e876a030f8fc60cf5067ad2133ab219f95ca89bf2ade297504bb16a5c4f52ea0b4e38162a82a16e0a481231894ea6ad1619b3032b32a89c3c73314520e8a58013a09c3b8d210a1e2f2af8580b6ae5fb1903ec39e2e26f749f399d224b5ef17cb883802be4ac37a20008d24ff0471b3a6d17fcd0a490509b1c037fb0d84b1a844325b0e53ced0ef24c9369a97f7141456d58d1c661fc9f74f3be40502fb81249f6f18b7c21d76dd54e8b68fb2307bb4c5424868c818f5cc888f76ff73cebd3f3715049f096ceb1b8c94e230650479888894e47fcc752248b4d329948c9fac1fb3633ffc9f79b2b61791283c24996b1d6f3c4934f473edfdd7caa7c116f0b72617dc3c2590de3569c8771bb834c9048b1c3392884573396bf37e9380a50d699f387cb186fc86ec218378bc24b206b5c620c8fafb160b83901443a43e3e900ed934ebed2bfcad8a8c95721ddd760f25751ceec0d07a7046901acd0cc23e16be08c3cab17fd81d7b92231b3040d631fdb034aa693346e82c1975fe87bfe857cf69660f09f5780d024bfccbbe8fd6ad79f97ae178818fe848ba9660fa909deb15dc0465c588285ab6ac9f1645f40ad62c29b1946764657d20c306422e7fb450ce2b61204e35991a4c4119c01b6661926fc1fff121099aa0a94f98a74221c85b8b39a2609a182fe509da93cdd08cdd8a279bf19b0f11253cadcc1dc96df6fb3f852446f2818a625b0bbb667502ff47eb0e070a7b9aa2022da80d9dbb24c77fc3db418e2ed265065fb84212bab1e297608e16811b76f2301cacf32ae77f322846733101bc15bd1e006b7705b32b2c44f82f3ae508558679321ff9bdeb2387630e8a17383c8872bc368c01e4f3500d2292650d687a229b381e29d93823ca70db1ef4e4c00e6385fc1383403f15787b0e32486f312971a9ed5842271621becad926716a539867a0d06a1c308bc567f09d7d48c35938fb4be68330e78f143b4c42926108f93000972e412f725cfd4a2abb84ba0e1e78f22a501242b9105a7c8ec4b6888ab62a92667e241f8c16c8309f2367c27e520f2e0f726a454b02586c445c036a0587a6bdc232aa74c3da4add889f90dff6c5603aad3868b564062d1a6035935b37a227379d5af73a85f4d87b7a3e9e87e21b8ab996f3aea578bdb6d7029f30129dccc869500016237945594b36e1420213caed9e16c304a8e91490954f1ed6f37111122bbed2df7967b4b9964920193088308ad089d6bbf722ef735fa46cacec6a1c08d4dc6de8dbbd5733fbf5d4ded5cae63b9e77434dbdbef6d93794de6379e0c27f3f94db3e174381baab19273e8a268a2b6a3d476de6b38c31bc7464a87a379fbdc6fdbe6d1fc863377a3b1ece4a0eaa3ee857a154ff1f416861273c0f6d5a6ebafbf7e044749495c81851f4fddd789daceb1f738a6b4180112cc947f0eebb58a42bb75dc5bcdf78a9d271fc554e8e5539d8bd56a86655e7ed2e4230df6422f167b9fe2b0d7b94813c41e9526693ad97694e723e7e3c8c79f2a3e12f9d87df4dccedd6c1ef41d4ed96021365244704a878558a07b0ef6c1f9ce03facf77de0270a3c0c8c8dd6c4020506d6de46e5ee482bebb9f879b053a9c0d055d9f3b634aae36b9ea1558cf4b2b1869864fb186aaa82c94218aa5f311e4f9b8e17cdcf4f9b84af71cef38dd660302ddf0928f38187a3e18e28b62af243735994af1948a89f9ac3e301f5527a5f3e2a34f7ce9743a38a652294a6df7c554fc3c07f4d974626781e8e962e7068b8f3ad11341f163e4e04f87e3d7cd1d79d015f9fab9dd57d7522e822f546561ba958be01421d9dd10e25681859cee45447e04e4f94f6784f3756a1e8ea7f3d5d2389b91e78c3c4a044d8d235265173bd773412357a4bb1c10ee55c4b1dbc21f824fedd7a61cf1dbe42318db84638403b6847abc3c5a1ea73bb51dec0dfb61231061b8bc638ccde1601c8ce3b22c93d168308cc3b88ccb3259c6dda37ff48f2e858defdf52705ce56a966d5b268361b5ab59b59bd532da866d20ae8b279006da4020d006aab7733977d3e168329de7783c1dcf06d3601a4c9369321a0d763d17eb5cce8d3131155de2174f9125a2624bfc5c05021eb051606714dd5b2693c96459ad99cc56b7acd64cab64b20dc3188655ac66321a8e9aa94b64ba557ca4655e975ae52e91b949e4b8385c546bad5acd6addb6dad56a37bb6da04ef3cd6518c771d937863db7e170300ec6715996c96834d806e3b0ac3599b6da06fade3a1dceb7c7f31dbbf5d66db56d6b4debeeda9b7dee6a5f33d92fe5945e3555a3c9c8172c1b053c78f0905ac3f84883355b8665b07f9d9806b3afc13459959aa66aaa4693b117eb4f73d96ebfe056a1a0a56829a6084030b1aaa3b02da82c936d994c269365b566325bddb25a331908ace72a95142d858a070f1e3c340087294e5af4c97abdb2f31b051d85a57f45717d9facf52d850581f52c958ff0b01e56c25e52d896d1681ec3300da6d9b64ca6d62cc3b0ac66999a6d59d56099c582d5df340dd36c5a26b365364dc3b05ab32ccb6acdb0ac6a35dbb29ac95a0a0f6852d3bca6d9be7e66d368aaa666329a86615956bb9aa99cc66dd8a6651a48d369323825c35e6e32cd0bb9b1919c0d4ec9b066c39132e3641a4d268361d66638cb325806c3e4d7ee7e9ebb9eafdbed70be3a0873b40775b44dc31eee3b2fb7f77ce4575941b54eadbb9f07719b6b208d03e28040dae76e9e4ee5546d73b9d7dced6be66af5ab3cad98463a05167e9d9adc246623a5fe865330cd6b99ec378c935fad2cab97b6b9dc5b8da5d94c9d9ac5acb55cb6b9683c5a8a46816d14f098579471820a6578f294e757086778f4ad469b1e603d7a5932d4e22d2e9a8ff25145b1f8a8b29c7c544f292b3eaa194ec1b0afb8683ea52914cf1b7c5e49f662697e4b9e7f618f7e79be853deb92672acf973cbfb1178aa516580fae66953960106db21e35e5f9357e3b61e9f78def2ab034e246f14771ff7cb4fd74c9137e7de197fd657a0e93e73bca0d56be429962c545703e9cc2e42238a75071119c53945c04e70d625c04e7bbc7e459ca53254f539e2b92fa88fefc1c7046fc49e10d38004d5c0e30347f3e842610863ca845f6dca4c54bf6689edfdb103147ec2b18c47c7adde4a2f98d033be79c0f654cac439e36548d37b022c708557f83273e0815caf03cab1c3f0a940d4cb936fea10e1676fe6188ed2fc30cb5feb49506c56d4c9bd5c6c499361ad44a7cb9d9e096b02eb71a9eaffae6f972f4381367d256daca3279c07ad2e505d662e1a4324eca0556a252397e9fc0ae56311d532aa9a8984c7125ae602b1fb98b4b2af5f2a252354cc3b8c9b4b2723ab1b0a050ddd22d3030ab158c8131a552ab7854e5a3f842513e7a7949a55c5c28eae484526bbbee3fb2d8b096894ac0366762a99d928f52aec8725ab13aa61c330ca750ebc29d928b624725c7ca8959f9085bc1f8088351f90853bd705e38294e0a7b9c7f9fc0f67b9c940d6bdece239d02cbfd9b34258aa344cec4497126ee0b7dc799e8c79938537c6db59db495a6adb4d5fcb24bcad368bb740c27e541f9a87efc8eeabdce4beea43a2e9dafd302b3eac4943c2a393e27e5a38a516450ea947c240396982c8313672583150e8c0c4c1c950c54382f1d958ea9b3d23975583c2b1facd20767c00c49e27f7046ccfd654faae4e722179ab22f9dea3fe84253f6a553bdf5d3c272e2504976295d4a97d2a574e9d2dddd5d7e40caafe43d82cfda4c15caf038af39f928e4f25fd7925d83ca1edfdba84c2cd9ada9a24a28140a852a954aa59266cafe1c938f369b50cba33a167bda705628b5b6eb54b9c6edc646b582b131abd2264625964cdb11d96319c6b066c545a82e596f53426d501bd406b5416d50d9bf7254382a2ef2e7947ca4714a313ed262563ed2562e72181f691b1817b9ff46e5994dabd9a6e4a30cb69ba55a67eb7759e84b36ecca459ad4460be740919df344768e13d93940b26fb6c8be9943f6cd1bb27fdda83429a796b7891b958f2089626f0373616fb3b2b0b789c9137b9c92c41e4793d2bcf8487b7f8dca479cf7d7c0f8c8bebf66e5a3fafe9a181f619a26e5a38d4a93cafe1d85dda87c94bdf6147bf4c29eb56f61afab3fb1f772b3f2114a149f47e112452a8a97285451c0d85576f845f165ceedbcbd99aff56e5826636be76dcdb4cbe1dcce63f6d60ddbb08a3da7e3a387cfbc4ba524b00f1cd6621f991ee4b76d25fbd90fb3f771fc22ca2df6114f5bc6b46d32e5bfb94933655299149629adfc335ad5fc350dfb70576daafaa0ec344dfe5a57ccb554d4da4ca7fd35fb212a770dac5792d16a60e37bfd79b719ed9a5fe2a30fee8f528bbdee7f4e29a54f1943fdf2e6b0f16d6846386035b809eb7b78a67fbd7cdf24309f6225ac6c61ef47b63efa75610c79273dac3c8570ea5395bb57999861428532babbbbe5c468675ec328a59dbf3ad8dde964329a26e5fc86d7e45490ae3a1f79ea94846993a22aeba3b7b6d4973e53ab3c1f4d511fa0edb98e73dceacb64328ba2a55a27a59f8bdc478cefb00d944d07466fab0ba5a9b26b45e7e583a2be444e172e7ee26e940b92ee493e5a96cae3ea3a796a64f3ad7251c80457aa92a94d5f9f56d467a9ebfec2b1338140eeaeba4c2911fcb3d4aad48a964aa86852c59349ae3a9dbfb09b3a2a8bb359ebfb8fa322aa5bc5d41755abb83a95526e922a8de62f2c4bfe16caadcabf5455b92abb4cd847571a0bfbf0716169f998277932d11429a4ead4a7e297e1ee7314862d2a3b7551fb253f899ad2871c5428c36bea235a45ee8bef2e8c07dd7fcf75f1dc4da14ff29d9bd27d8a7fe739a0ef708a7ccf6f386b3ee8353785c322bfc129d6776f6f0ae7399c72bdc85792cbbdc8b5df5dcd83eee63d97e3e2ebfdd72e8cdfee8ba737857b92f78b7d4ae7fd25dda4701efb7953a866f3f5ad9b42e9ffc4299ad7feba2912539c627ffb4a72e96ff77aed5a5fef7cec4aac6a1bf61826a1cf3ddc8eb8fe7a6bbd66b391cf91cff1eff8772ae0a32ce5f34ff21b4e017d85318186f1dd6b1f1f05cb7ccac3c0292227239ec74a3c8f7df4803a11ecc3be0793c4ad02dc11f6af8f1bdc8ed0bcf571bba1796bb31dc179f971bbc139a2f3de79c7294226d0242129d93fc90b11c129f54130708a07c60b21c129da772f6c8c3cc90bb100f69e878153b617f9f83946fe054ec93c0926b981bdc7054ec1de83bde7e17644e6333ff2703b62fbed451e6e4768af7d07027db2ffe0a8399258a2557cf4f90852516920a087f1dd05d2fd8b17b94044dec58f5c20234ff2f563687ec3d97484dcd83cf68163601ff635bf790e87f34168903abfd9705ea3d9bcb59ae7b80f4283641f6e35b8cf8340a0efbaee4544447e64e43d9e11cf631fbf9ef828f331c6af808d0a371b4d5c9526f96757a581d487f1da05a2fd8bdf2e90ed5d7ce602c93cc95729fde7946f59f4faa731aeb7defa697d101aa4f9727e101a24c73f1ffba0d70bb971c5c03eae6fa24914c60d4243a549f55fdc203448f5b5abd224ed5ddc2034549ab43dc90d4243a54999c72e109526613b3aef007b94d87979393fefe6adabf9ebdaa777e4de7befb51792b80b49997b82d2001bb702d7b5eebcf2faedf8387c844245ca45f085aa2c4c47458c8b602e29a131c6f8dd1154450e4a5440685261087e37019ad07fe8593802d2440f215d4fbb3722626eec261844fceab91f861a7bde82f99eed948b62134c9448d785a4eb3aa9b708d80bcee88fef2d8026f20b30143f36168d45b7e4c6f2050611ffbabe3285f5da65259a64114de4cb68e2fde80f0631dfb2eebc5ebfbbc432fe98d1bf40c5153840e5ca69be0d017f7a9d657294274dd86b11b3c8e4eac2fce394a7e9c20cebbc5efcb26a0291a4e138af274dd9bf0611d74fac83a47eff7c587111fd137700aab28eeb63fcde86b87e9b3fef10f0ad6febdbfa71cad6f57d3b67d793d8f564f66216d93f5e0f09783ddcfa5162fc5a05492c2aa7585a8931f5ca9d257b0bbdf3da188a596e36967091ff12d646138748d0c6c2caf768d66c97ddbf9fb0be49eccd8743ccbfaceb5d1809a8ea1a00bfd3c94a8929f5a2513b2fcc19177ad085dc486c46645ce8011772d33c53fd038bd9117050fa22ae25117e3e8ec0252e8a2739c6ffef3a6b5d1429253942c288a7172a94e1651a05d6939f08ac5f4f31ec95645a4f5e3d4d4db56df3ebe9873a58eb5eef41d5345dd7f5e579410cafef6ab95017cb75ba562ed3656a41b19c2e13f6ae954bc5525d2a97ca55ba4a568c1563adac950563c1582a4be5a2f92a2f59f3e4013be72966e5a39aa9301c577ffe54f9c866e6cf17fb1c97e22c67b7add69989c953cbacf2ac1a4c9e9a2a4f3c532e9a1fa41b7bf325cfafd325abda969976db3e53719da7fab366321c974d171f65d3e5f391f6b5f8886b41a1587cb4e1c932e769ce39e79cd3a735bd789a3f32e7fc111e3676fccc89c6112862ec93a5739e2e6bcacc64ed4cf9c84bae9aa7799aa779a227d63ccd7992f3344fdf07bf930ff646f33df642797e5826ca47242ddf74f111c905fdc8edbe7aaec857ff5ccef59078b0100b78fe03faee453a1f124b23b9a01fe93c22221ccee7764058f711f9701c05310eabea541fba5ec98ba7dcc53f6f7194a35a4ecb51d7f70a65a15012854a398bb3889c4456444c9c14159cf2c1ae4272440a07536a6dd7c574dd0bb9210212e9de23d279917ff17ca793ea783a1e10a8eb3a9c0fd2a48f08ee308813719b4ed6699ee4e9b4a2bff2f86191526bfdbd92799a273a4f9365a266cb74c9f1ad9226622654ec2b766b5cd6ac2cb0f4743a9d4ea798196592f31eb55ee72847a19a5e31254d1b8de5a48983375883ad3cc5cb9a328337ace18a33dc2cb0fd5e5c81a55fa3c6de4c575f0faaaed82c39362a7ec501b38ae952fc9218638c31f677e2f21e4c752218650a18942954302b1853ca5e9bda24bf4db42c50292d9104972389254a2a38727c5470e4486289d2c702fbb18e5c2939940882945adb75ffbdeae744349ab79d46f3dd8b8c683edf8dbca647467abb01370adce030cbc90e4f0db65a0a0bf35b27c3f16c36990dd71ce7f1743a1cced69e4d7fbff8a8d3dd1ce66c9d0ea773355f37ae7138edd2dfd6d96c9bfe342e96eb1c367a382d85fd78d2907c7f8f7c73df5fede546baae1bf98e64040bb1c0c86b6cc7711ab85180a4468dbb24b7fbda9b05ba8f231cc9ed46b8cdde4cb7f9dc110e58b882145a105edf2b8a2d3cb1c4de3b1ee934f64734426ca47c44349ab71aec43e4394e44c4dacf67e4e146811bdd6b9ec31a6e8abcfc9a61147b0f7a4b73715cee8a60ff600c71f55ccd07296b06d044660186e44b2ce4d3140c42becb0d029de388b456866025768241c84789cd04eb65a52cbfe57b59497e2fc1461912ebd04e316e69e0e66b0fb71adba3c4cca3c4883d7c09a5eccd01be8a21f99d230f2cfc78b045a62f5f6e12904feb5fd8d3fcf5d6edef4d02f5afdfbcd7713a5efd768cbf0e9f430ccd4789b0c3d9e0d8bda51f6baddbeeab04675c3fbfc28d88cffd49c01914661df4bd9ee1efd12168644abb046738e611ff2dcd2da2fed5df5dd0fd5ccf456181b931d7abc4ccd7cbad08f8e5eb33a55d17134db09f8de18ee91514ec6798bdeecd8818597ecf219ac89fdfab68425f6e35a89cb0bbbbdd728bba6bdd4d3ba67367af1fa3177e3c88c9bd610f47eec7bccf1d69a4b163646a3b388a319a7cd4251a4a02ce80793a0ba55da2b8b1f75bddde3b50e8dd41e3eaf0cfdeba9ebfe6d953ed3dbaa38768df377bb815e158077df8e5ec6b3fd6584767daf4e3011d68e4feac9f3ef6955e8fbe07f3e09172631d1ecc439e1026c0c64caf07f3b0804cfda33ba5fed1bffad5419f7ea4feed1f377f8a7534ded14372127006fd4a432e9a2f01f865b77edf1d3432ed414b0f50dcd7eb659f098532eefb7afd1cf799f7fab1adbfdeccd3cb3ddc8ac8b08ec6497c997b94c84427c0620f2841b3fc1e344b2f1437237cc8902cb98739f3bdd5c8706f6d0ff891b9875b0d0e271143fd9db71b9f88a1f9dab56e5c8921fa5e5c89a1f9d11443f3fb0996be174de8cf8f2bd1e4b1b7de0727956802bf1eb4e4e983953cbf814493cef3617422cfffdcb94a8ddecfd1144db09f1f9f8823592a954a27d2a4d45a0847108ae863df5b0dece94338ead59b5058520df3ecb067595e58542c303fb0d867d8a3b69bf4e763f7873a588a77d0c8f4bd1d2899be95bd2524c34a58997ea55747f6f4adeb656f655847f6f0cbd67bd9630fb71a59f628d1ba3d60e08cec67f6de0e949c616f02d9c2de4793fa8475580fbf0c3f9655f6acc73c4db6586266a0f0a00483c85856500473863deb0790ad509fe677a959bcebac2dc1de3bc1baf42ea1143631dcaa3ce9778e9b45dfba9de1467b18d14332dd221b416348beb017234bcf0c0b4318840761603cf73dd44d9f2ad5cb4b2a7572427344d918ca018410622825a59a666da7d3752ea089073ca04497fb95a0b9df033d68dee17928e5fe1e5d6eec02a5fa5fefee0e0388ed1338babe799cd0bfac87d9eb3c310f1a596220419c84fdf5411240c2b010f9f4e22018e98a41ba38d2f5704697f2c49ec4283088fe7ef8c9385f70041a7832fc58a87891e1c762025086df0e5635d8812aa7a6b683af93e1b7832a76000527c36f072ad09ad263c32b136750b198842be21082905ae42bc34f0561c8cd65116b869f0a9a90fb4615d84c869f0a86101bca0c49119ee17b8678470fc91e133106351e2122210eb6630b6c4793ed478906a850468d2e29a6b5299a641e7e7f6f9803dc22c30692613b91e5f5f0714493ed3d212ccd620b2101bc0c179d2c9f88a31c2e8a0f05155d455b91a1d75764f85baf7ced95c75656bc5762bb64726cd8eb2d2af60490bd769191bd7681691717faf97c3e9fcfe703028140201008d4755dd7755d272222222222222232323232323232324242424242424242f2f1e5c9c9c9c9c9bb70e1c2850b172e5cbc78f1e2c58b172f5e9860c0800103060c18305662c4881123468c18314effffffff2cf7de7beffd2027a4fb3114b76061afb7e839f41b72a48dbdd622c76f31e4f8ed8231c61863acd2430f3df4d0430f3df460922143860c193264c858f1c1071f7cf0c1071f7c38fdf0c30f3ffcf0c30f1fe484f403104000010410400001c447298178184d98f880b8108ae4ff702914c9f7e15a28922fe3765024bf87fb50241fdf1014c9bfb7048ae4ff3d8122f9312e0a14c987711b0045f25f5c1b5024dfc5ad0014c927b938a048fec8cd0145f2456e125024bfbb4b40917cd0ed126c152892efb96d8222f99ddb2b50249f73fb0445f237b759a048bee6360a8ae4dbdb2d502471c55a4a29bf5bbe94dddddd4c4065e28b3a7484d87b500c19c22d6408b3c022c38751880cdf00717462a23dfc9268525fcb104208bf62d85fd7cff99e83e3388ee3384eab19462f6b4a6f8fc7e3f1783c9ecfe7f3f97c3e1f100804028140a0aeebbaaeeb3a11111111111111914c2693c96432998f2fd2813e9e0e67a3b15c0360e6c2a1451b04777969f0fb8b3c80dccd11594aa08a26dbc78f56c451f69e0064581115232ac62ba289f6f1a3167144bdf88638871cb77020b9bee74ee4f8dac967274f4fde3a797962596badb5d66a341a8d46a3d16c369bcd66b3d9b870381c0e87c3e1a43a9d4ea7d3e9745e3c1e8fc7e3f17c9093ed26d143c35e43e18532ec09e029f664741696d8eb640fc2cccd468e88fa7c3e9fcfe7f36901814020100804fabaaeebbaaeeb5c444444444444443ec8094964646464646464e4839c9046388ee3388ee33efe884807fa786e0914f977ee0914f9732e0a14f96f6e03a0c8de0a48e7a0eac789c20540127c08150049deccf021140292bccb61195cfbb1450d152a94d19d219c21b3962549eeec241c1880263d03188a0fe1bc272eea950662959879c7e77e2ae2083b7902a0efc9b8de7391bd3ec5ecf5295681124318664014c58ff80df4840e61bd3e599665599665b15cd7755dd775a128a594524a5b300cc3300cc3be2ccbb22ccb3ec80929fb080408109513d27c9510693e10958e345f8592e603011284929ae0624aa58ed4c4b50590202152139e8712697e901352133148f3e3679742913f762d14f9d3db4191ff751f8afcad1b82227fdf76c0cf013f72e30f74b9479777303184227fe818be8c269e8d7a6910cef04f076fc8fe14ced0647f6babe1328e1c552a956a40eafeb8d568ed8322e802e1084507296882a2032fc010cc1e283b49368200d9712abb428ddf31c6166019a6fd67d5baa829fd14ccafc43ee4955918c3eff0d2eb76ef22582313b56510ea35dd2d2ce4729c32b110ebdda960bb27f5d5a636b5476d472d3c9f7e95d36d603da8b272581bf88f2c516289735bcd86905787f5f46b7f5e679829dcda14efd947e2b1f084779d0b7f991b0061067f6a9bc18256dc49f15f462c6903d0deb0b001a40429642841ca54022990410b5209721684f0822c60ea40872690a88be4494639f9727c211810474d72019f303f626d6bac65f85d019525eec450bb0c2a845105ebd1dc33341a353a61650c69e2532764ff2b3891fd3bfe54bbc01650513e8f1aadb01108197e6a18418e5264f8a1e10c3994e187061714d1a38955f67e5c6165ef032d884286958b56587742865f1952b9b1c8f02383135900197e63b0a23722322ba8f20e1c791545dec144267b3dce40864e8aecf56091a29e217b3d527086993d1e5f2042009090ca1e8f96548645d052692b42964a190ec9f03fc387014c9e5ec8fd95dbd17933c23f62cf0d2b597eec47f178619ed7fb4162c4f442963fbd903d1e3fe2dd01870c19127fe4385772c3bce49b6fae518b37469e777a21f746c42be169dc231af1465c1df16513d143f28e1e321d9314d1ef4f44bfe31e35b7e189dc23e689bdf84a74f6ff91efcd7e6f8e17e6be3d6a86ff6376b4436503153983337a469600d8048023ab45fe900d4588094061831590003047ecf57b56f6dcd863222b11f3063cd0b91feb64b66a1add94901938e38389c93b380ece8078c8fe562700c6ec5919c2195d5a83edafbdb51a6c5fa104b1a8b143964a50255ba6182c2d3a010d23860c3f76d6099025315c94526a454abd29a53394e9bf9ac8dca166f85961953b2e2af4d1126b18a2861cb49cc1076920c131ace00a6540c214d290851c90e0c35ba2c9280122aca77183532363087b3d3cf7676fa349fdab5e18863decb3c7aeebfdb56882bd7fbd6ee78a5d1843bd11019b90a5f6d95f3d3c635f3bc2f60cccd895114d429e8bdcefdfd9dcdd34fae9ed31737772b41e5e8f7a144e8ddc20d56831f431e47fbd8b0bc721d6ede1197e5fbad5809a8b6802df5f463481b98b269ffd437086673d73d04d581d5e8a197e59802277eecf8213b94b198e2043ec75298fe0554266f8f21661337cbfde0f4f3e49115996780797e1432fe492ecc121d96382edef0ce301e0f7b6e377d8ecd8450cf597b4505d6529b5024c8eaf65a1caf13b71de4ed7aa0c02ed878fd8aff2de8d0879c4f54c90dce156811f241f41844492755d1f3f482d7b78961343305898e38dfe0bfb608234bf49f23af6daf32cdfba17096e379c44823ea2242b632cc2177af8ece161c8229567865f15e690e1a67aea42513bfb1b6289b5f0e1d72adf6a74273efa18bf010f80333c47cec54bee4c039621e3a8bd0d01c590e156230b6d6e353e6bab61c5a55942c41124b1581346ef21039f833320863008f71558af249f54c1891cbfa40aa61cdf002e444e4d35ac5a628a7d89da58b01eb2154afca3bb46124c9c011858a9208c1fb10e88773091e17bf11b62ef8f0aab0c3f2aa43215a8709b65ad727bc08f4c2d5c7dab31a335ff4a223a5b4f324467eb2d4c73db9883da58d011fbfe70876e480417b19cd6bf930c11dfdfadeff93afce55b6f3d8d26315b3fba7b0a15f68bad5c154af22bd756d8c14496b0b3c419a830bac7eb70f3d740a5daadb8a7b045f66c34f184c8fe2e8400a24901a6f08602844632865a477ceaf12586520363087a5a9e021619bac064488aff12d318d2bc5061ec263c16a00aaac460856e428c489464a7b987e7223c3de0820f72bfbc489464d90d86afdd1f83bce4ce26b5542a955e48f0fbc22f0a56e4fe7e71028ae013140a4b4557c55b015b0a48802298bd01e4a67164a94aa5521148d024734e50c9707bc928b1c6ad461358324a0c818d6640657f4f8b814216d95f9b83b64593c06a307006848931c618638c11539199878c31c618afc74789555e987774de5231e4afc3b2b8fd9096518a594bbbae246e35b01617f94f9796d4f792dd3155f53079311895ec5930dee572b9c04061259b64f861a1c22295e11432fc9eb092a340052a9ac00f0a5bc8fe95259ac02f0a5064ff5a4534692bb2bbb45469a9c0d8f880ca8e1e92250e81f52c156d65a9a020a9b22c61a58b3b4c76e9525960b057e2afad321798e82aae92b9647f97c8f2c5d417d74ad96b4cf60a93bdbed49aaa2e2edfd7525b5ce4a8ca52595ce45f59da455b35e125cb9f5b6a4b654aeebebda898564e1996ec3f02036126f64ab614558275424bae2cd9bfb26450170c15364cfd56653ae726b15f2f7b7fedeac85efb2e9a4c0f09a80a7558d87ecf76701743738652df6538fbd0479acdc3cf8aacbdf555bb3aacd73e2a98e0448edb10316f1f8a261bf63a73b66ddbb68df38d391c0ee6697fbdf528916e4534f6fcadaf9db79f1bec8542b97b4e13122579fbdf6ec809767bef89901143f3371cf2acf59e3f7daf4dda5b97d6d71f6e12f0af8f1243306bb7621a4333c3ae67fdfcebeab0deb3795a17cd872f238e2269425546899daebfb6c3cda37429e503c1f6e88c8485bbf4fe836b8a87cf05feb591af973f4ef9c2de7ceb9560e34becf16b6f35faba73d6b0e64502fe981da750618e10c8416308ae41d225cc217b32531a230680b0e22bb911609da5a5259a78fe4513affd144de0b70431e47ea7229a78eef72ba249007207207b5a54c2ca5d7cd42e1d4d5c62e8b35ebb3461db5d727fc3ef0a96ecb98be90ef194a99732cd958b252294be89f0350cac173dba4a8e3126c795130baae40aebefd912039c44936ed8565bd6b7f56d1901a6afccf47a7e49b2dc6a582fc3b2e67c6b4e6b5a0193a7ccbed598369a5c1fbfd35dd7755d17869f105cb2e5396e35acd3bc44c0dcf2a12ae65596df9fa7b2fc7965869b9732fc80e092e5e7de6a482fe591fe6010fdb42b4b8506b025b973e5fc6348465c818ddf7d3f70114d00e0c20696e692ec1f82a3d0c3d17770d45938b2148ebc2497b0a18a4feb20b66ba7dcf4035315ab5c5d76963f82ca9dbb8a538619e62b9ec8f04bc213190930392bf9e852a92a8d17962f2e929f69d809ce809d729375bab25207c17ad775954ef23d6bb371a95c247f09db57b5bcbacab27e232f15aa3276f2517dcc05fbb0160c85b160aa2c31982cb15596584c96994a7e35c9aa92652d6529a5ac2b5956962c2b2acbda92e56725f9192a63c9524a99a9b47c2ea9ec255b6b05cee897df01a05aa854add635410a959a110000002000c3150000381c0c88c462c1288a247d1f14000c7c92465e48208b44f240c9310821639021c6201010901100100009021e863b44a12458e5a6b4fdaaf8460e73777c1960dd1da6ba16c0beef246cc41521a4c6205ab8f4106195017b60518d9b86d8bca98505e0948675f906182d560bb8530f01b00351b4834790ea0c3e5c16f85550c43dbe272c23a7de1637b90b9e306fa144b32f39c9d0bf4e088b82f839456d6c1b2f23096b0f931fa196e80ee55a339f343b47de7f3356bf0c34cf82718a915b59a302810405cb137610906392e3b8ac11b46347a2df10e4fc427e1fbc028de946ee56b8457c7498d59f6ddcba197a257d8e600729e18d4d18ad2b9c4f76e1bd7335c3c13cb1a12dbf57651e8f893b983d68c9f17ec6e42e927cf47a4f16b34aab2fb6e09c29e73e4e6e762316bb9fbbd1e4ae04f108e2d07f98e0c76efe8ad2816aeefcfd3c8b1611f89f86c195cbc117434d45ceef5b9b9631bba61a56b08261f104b25d33be9854035d3ed8f19ffa41f0053dc7fcb811a740f4e4ab7e008959a05ce3ac0aa11c26699b0a991b86a11719656b2e0e780e00885e4e592e66152c23a39084b4c1c200c8ebfd9e7b459d79a1c670e287f73b0c2980f1b7eea3959e774fe0861246da0942271a6def766b89a3b4cdc38c7ac97bdab89fa084fc2085c1f6a319f3d406766d10e4450c64c38f7f490a74f6518b243b1b60d2232480d9cfbbb7f730bfbec44513943e3c7909d01a9a3aaafd18cc7b78d7dc745cdbc629c15fbb52f54cc2e5ad3e57de9b8dd3af255c6e4ef60ba98f3c2303f09c0786db4877dd214b35adf0cf282d20f6ad8db66305502068cc56a91d5c014e449c14d11a49f92c6e0ebd131e1254102970085d30247f2ed3561482fa314cb7ad324a5fd4828a974aa9ac55604a5bb4184e235473073ab8ac77d7a5db735e70d18bf1402105541271276b2f7e67423044af952d884decd2d919049a1aca7e158a284ad11d8374ae48afee69263c006147c6c418ca67277238233fa880c97e19a6adc249bf5df1b1d212943e616a4c465fa88afac19eb81d46ef7d6926470e1bf13f177dec6c6bb793e75cf0bd63eb094cb8a043a521be0163008a08551883d1eb2e1e9735c47564596569f07e67bd914baa29d3dcd0cf73a4d929dd00683d417a5fd64c9cf1cf898f4cfba12516c00ce18abe1877e58617026c3799204c7d1b955b130df8cd41337e6bf6904c389d7df81dc7ecd7eff3af006f75a2e3970b36dc496f3a1edf7ce66063188c6b5c009c1b235e403a66edf73e45e294cfb7ab1fd97ababc45e04b16ee6513a6c7109905b6010c1a5927e627c837840f0454423eba3fe958bfc199c75a890c71fecdd311e6ec6d4e178b42ce6d9d904391e19f66b5705e1ffd96fdc9496e2c5078772d4784fa909de72f78c31fe41778bdfe3474a80bff468fe89d6efb1ed7a2d909341c276a60b64560c3eb241a51737ca32372a71012965dbb83764fa591e31c77ffcbf7a0dd7c9c119e4e4c624cbb516fe39f0f6784db7682956d313e86bee92d98567da9f6a5a8b2086092ee62a69f99b99c05e85163361a768fbffb08ef62d7b6466f253b5719aecf15a57e6f7c8101723bd34f0cddde6d8075dd603b28531452de201859851d74158888411020145d5e0aff8c80838c80223188965455c9bb1e93bca0ad57683184f11d21463c5f95d2b11e4d21e66815e42ceb854d7826f5a929c7b89a2250367600905b524a53588a1f595af9eb4550ec5249e6d0ce6981cb58f363cea289433b955c26c4a224e178954808c3875f964ef762a725659f9dcdfb097e137b7a771f542a8edc1eca8977b45932ea41f497022eac4c20296b5bfb6e50c91ce2a20f7245b0140609567b508853f6e2bd0291ad49265051dc6425fbc0afb194ddd311cb1d124080b6ed74aaca3c4b443d57134ce7e62e5bf1f25d81a05fcca7b365455ee7bec02dd0721f1cf10f890e6f0c1d2be1c3d80613136669d4fc9711a379e29c5999cde9f82a3e417a222314ba5cd23fb5b1b4e19366e26cd48411afd5e99848fbd340f51ece3d930cf54bcc03f60bdf59a0a6580a357d08a6a53ae9de1a1ad16aa979756eb28f7528e6c8bad2213fc56afc26197bdd96e99980e571949eb013eddf23f7a42cb6bfdafdf208e47675f5c1b275cf69005f9d29323949f31739a811b67313c8921ad5c1550446178859526726d0372f39a6df94c4c1ffb946b22903f177389ead4fd1708f7c7562cc23eeb03edbdf8279ec94e0a7d099e8189f3a615ab4840b58aef4fd3c354a1b0d6d4553a5ea08d90c6d3721312980c0e796246c7b5594659fbe8a47e92a6e6ce9e16f53cd2efb5a43d931647b7278346b63032bc06bb109ad2d298dc7857debf20b58573b26fda4d554dc2b30ad9e9533d5c889d8c7a854856c4cc67531287f8ae11c81a95c2c97886f36570db47499bde998ecc7458d1e4bbd88b651c11d6ec92257782710945582b687e69c7a406554266c7dfa80bd4892fbacea686374a30f29ed08346a6035333e9f07ea0467326351ef8da2b4d70b21c202f1ccf41e92632eabef714982c227d3631783b24d1e47fef819b81298a781f19797c2f828557fa0db7fd0eb4fe8290dac8842db742ad2f510075aebc2fec9ef77f9cb55229318a6e7a68877587cc71847a65fb82f034f8e483212ffcec716dbc96c99e07ded3a1fe79aceaf169ff39af741f276ad84209dea2b2b94272e3c414bcfdd82b1bab5135f1c02d6f52a5b2b68d4390765a9a1c07488d14710a8c5af514782bc105e8fb8a5638785ea56f968ac9222bed17141718a36bdb16a27b6231a54a790747b1f1ed669a985638a78c7e9bbf8c41f7a31ab4f872304ebae335e41e7e46c52dd2aa92b9cf191d2b10204371b7d434ea98ca0828760f71573126b2a8042383d88bdf2ee592c4cda24ddc314d2e3d9ba14da282502a2fa4563795a9efebaeb3b31c0d30a005970ba45cec1628fae98d5d95e47dd00a8a9e898a08ea1fd829a5448efe1a090b0e42b9504b865f35561a5feb8662da90beaefcd3c3d0570e555d079b6b9aec33cda2c78d53d4aae6d21e728cbde51fa03a4d135445ac841bbf903bdab5aaf940e658ff151c9a2126b3c43fa54722a2074d5c5ac0d39366f69cdcde36688f5b85bce31bdd0253b4effeede2146491a3a2c78ee96520bc52925ae90f0ae132bc452354ebd9864138d29cf7201328722d8e6d16b6084b757106da160fdcbf3ae89d3e277b869c3e2f5024428d21f400fca7081a2a525b0046e887ee4f2df05b8b808d2cfbf4467bf21e7b5648f0f05002a1b820617a6a46b55a4b19c49e07c8447ed96fb3c3a37ab79ec8967cc6ebaef1af01e4574d6b2d55573ff3aeb2fe9fe3edf3cdaa186dc2a6d4e8f618e87c335eff7eea697712988a29fa0bd7264df92e6b4d92db3987ee20f41a67432bd0a8c3fec6048eed393f05f49ce608d1db8b3efdb9ca7a2d484186b969854386739dee800b2ed56e6ef36a0d2c1ae0e579f4ce4dea97a0a40e0a0fa2bcc740291f366491b0bdd3c29f27422b5ca8cba47aa5f66f15d9e574824f3aa3c176a3037ec9b49044d969596811b8898f41c47d09782f492942a4fb4c7d77aeecf89093450b189105f48f67404869f4d986ec7b5e9e119f0bb86e24d6a1a2361acc647fb390a851c9281508c0283d2debcf59cb4fd86c4da300545869bbcb4ac335cf2cf5ba1fa832c831b11d6bb430eab5358ffe9524971aa5623b25d87bf203fd38eedc9fb40f510e067b7a9f6a30dddfa44fcf8692198718708251f2b2d8407eed08fe42be305344e08ab3a4eaf1c79c2e0e6c2e691afaa2421ab5b94444f90c87e17d981ab60c949c819ebb67c4670d93678e6e63196cfe9cb9a89af4f26a0ca6ae647a46e4290efd298ec8abd17dca1c176fc4f4f31468f6fd6b3e9295e1c393875a89f3c9e83e156f5a8411e6a78f7e6f32fe09ae12a97857cacbefdad1f8d1b9466ad3d3fa09d9703535e864ee16282da49dd1663a581dca925a6057cfed92f5c976128266d3cf84e874bcb17c935ea66c248494969038ecb7827bc19f763e436bb12e188ab6f08f2575ca6f50ae136e4b5fc7e4cdc8ea71f280475de2300b621136ce5454b866c996177b8fa9dc21a9ebe37b72c92d78bbe0723f86ea0482248c12d1661bc9b47e5c97440c6e63d95b5f2b4494a5ec7a7977784f26f206bf581dc4fd322854941b19303c821e411d229dbe7fad7fb8c7a2ebbaebcdcbe591da3766ae9285ae10acbb4513cc67baf81f3f7792f0067b0c292c5bec419917de388c24c92f271d2fc7d0039d0e721edd14fd84093e055a3e64c436ffc50ecaf21c1ad6f04cae2108b1cf6d0671ba87197238e1ccf2505e780c413a76ef96d112020940512745d486579140659f505a1b72b32710b1da8f55a5935ea32350aa00e88a93831394edf007c1108396722b3896c8a3e7eb1f53209eae6850530a006783d63656cc9cf4299e1c8eba92f0ec354b7bbb4548b7a8cab1c143516a351b0801969241950cc2a4804f732a26903a40f8a8eae0f51fcca07581abcbb91b50fe58a6472aeceb6cc9b6bde7b8ac36857f2994df02fb970c4005c4faae669625ac9d7d8f970c2d63102b9ae4d53b7645d5f4ee8fa071bd93e245bb13a655a1d8251c9cf66dff564702e7759f60da7c4138727d83a3a97eef981d8c90583a7fef7b7972e6a673b8879339d6a281c2c55924d78a9284e60250b9d1e0a1f6e3e2e07fd1b191e457a3996c7143c6e8a120a194dda029c56d15839d6cb6835b2713a75b1ba6837da7a1772e1196cf9a6b89aaecf6e0f4b71c88c4ee8353089039b2d34b67cd146e168f93e82ac8169bc4038a199f2e7157a1ec48503e41a194a634758cae4e86fa8ebba3640e58ef7cdd13d89b36d3048808fd2b8cbc5a8d8d92bf9e3ec6b7dd8ea2b89fdd859977700bfb96450daca0138105cb36e4cd041a88f30a8ba7603b97faae7e047a11b36f91729f8ad3e378a6e7af91ef505cb416526619df4dc28ee90e3a299421707dcd9ac9b1ea229906397a78e2d1faec605eb757a0689008eb23cc49488a75986b8038dec0f595b6ec00e952cb394220aa8781a8120306482dd5331523ef91db5302efae73fbb1f1fad52cbc59be6b6c5b4f0c8658ed725ca6d9efb1bed97950704eaeb9e5970c3fb6612e4967418b99e8d08ac1e4996bc6d3353407f2aa0035c213d228217039bb9edf020a09ff4853573f71a3b81e8590e3e5fbd6ce922025631a4b0b2890ca164d36f664c80b2ab9d79952bc862d8dea8068f88601da9038bc33e0c02680313712ca6bd584b04abd98898488ad6667e5a49df173f37261e81ffb5f12d1e23b1fa78c8788cf66b039af125f508dda4727b620f7f4fa6a306d99b978b1dd9cf0eb2fe1919627aaf39377b86df2ad346e43c05e175531ef610215ed2ac25d281bb2858f003227e3ec8a5c399b66bb8398e852e13b9a96a01dafa5fcc232f8fbbd5e23854aa45715d5ccfb473017356ec504357f5b9698725c702530c044e98c7c156578704e3c85dc363e87dedfbc8e83be47ca3b8e5271257fcbdaccb41c7877809c42f5a79b1138b3d379c7a5fc389678053e5f6443e98c65ed9a096c40e6e45ccbf62fd40db76b49e8677a77bb401c6d31d817efaa97f21547cc59d5dbf830e2c5beb0d72cde0d5e41936e1a63e83d86a2e6a67b055026ea9abedb37ab88eab9e342800b523bc40571790f6ab3976ba1f7c1454e66b5b80caea11a14fc6ac60998030ecbf23e548d909b391eaeaafdc2df4bd82fb56495a5db301913fb02ff5c5bc5bdfe742d618aa408acbac8ac451aaf269092213381289fb9ab3655ee8a7ec1b631b03fa3937db6139a1385ab6349efba873f9fc0d72122247b8de4e91c8955fe940cfdfa92e59b865fc243a15321c11d7ad44e39ebb7e1b83b7c43e5a4d6557ba83d504e7915d2fb324c5c85976afe6c95b7982af2566c9f493548b112e37cd0490b5d570ca21b8f7e65a661b49d0161493bf9157b3327a229333c19b76cf982290d43b19fe7d4064a59f38c8cae12421b772d31c02e7110c87ad9c725572113eb81d86d8781eb62552f7a1ba51e1b588d88b7babc4f9d3b78c4b98a90b2b4f59bb883f2606939c70f26bd98eb397065a3b370b3d10bc9e83b65e48e8cb013846dccf6a23c94a3de518fa571ca03c2a3334f551965796d4787a67ca24512e7afe5e4f35e3e30b2e0fedc947c3f72de6d3e739eba3bda8fb6e6eef1cd485b0820657bcd0e999e4ddeea5928c4dde938da74dec05fc8e7c1473664aabee075985dc101fad2a9d994833571313c6fb754e4d5f496042c1ee8c0ea9b812cd616fdca038a3e5e4e8eecbc07a61ac46208bf81c5349ba67486559e0fea2e5c88f68c0366d70d73e525f3058fc713bc18aea0fd7bfa04029d7f654644c0a5be36ce0c0550e2bdacbeeffccfbf6ed2a6efa3c6fffe05300dac447ce21b6854fdcba53425471805260d445fe0bb608fff0e5b368b23b61974ea0fc8d8c22dddec788fcc9fdfb38c0970abb36aa6302b874030a15c6c6e20d2d8e4d8962e58d7e866c7e3c7b2a1bedeca228eca4175321fad35f0acda16c4a2b9ff38b116156ac65e402613f5cb153f98c2eb71070d06166d712a534970aaaed3bd3f18fc2066b869cfa596ec49ff84dcc9fab844aeb2878fa16cca3628fba685b1da06b3f9eaf85a04b930ca26848bce640d020ac48c439687e4e681a3964963a4e2b16e702df40acac58b6fb5711faceab9905969eaca5d6e34fb18f898588a0d11bd51f04395652a3bdd36f9497c12b3dc6588d161573bb10868bcaae679bf7ed08461e3a7423eb27ffb9b94427aec05ae9624cfa3be5126650d421de20397de4f8f448d4397290d11c226ac36c533571bb11609f7fd8566bc179ffe304733a6361d7e53a2c7703eeee25e714ba963747d3d130e3ae91ad59d1d25a5af20c52e5c66664c09e3690e3c76e8e933387cce58a33834a97c531588f270398c8ea647d8621cd9d2a38d2428bf4b1715e551e317154cc6ca9b78f6269a673339d9ac96ca66633490374af1b4245cc588157639c585866e572b532609d095d67b4e0e05f170171cc1efce87b2d5180024818b894b59319c2e455b1bed2b95058bd086787cbab4e4a9a8e4cec1042a913b368d26326262d2a362c7144c058b4254014904d4b3ec3272508d4d42039dd05db3d5f4c5ffec49eca8337051217eade7293d15902b1f677284a4dcc9a85fed1baf742e3cda254c8ab22c59496bb7f6ff855c8bced709542154e298cc2b1f8c2208bbf17f16e0d38587c6f9cf32e7ea3d88a2c48f576cfb8561f30e796d2a59b004ecb596bf9bba1dfdb6e30a96e77d570ae73c2f3aa052fbcbbed5bbfffd8b68ca0c4c7750e6d3e66d41bf866af1ca3d6537d93819ece497bbbd23c3969de95f7e3cbf76577582d1f3e39b2808474d5d795fe8d76735b361faaf4b1ccc7f25c24428d8760469e0ea37f56d022df674dc1f462cada860ab38bb1da835766597bdcf1277a15d3761fa65b5a9ae0f7dbb0d396b77e27903603f1dfb3466af672aac05dd90c44b77127b5c3568b61b19ce6dfe26e966c11f0967e17faea46b7c1e86b305e0dc66f83e1400309d5bccac2cc0c4dc34befb83c04b78a84ae3a23c06590c343e91ab571c3253ade884ceffaf215940e3373da0b7b8824221095e9e2d243ea54267404809b5da1c9e7eae989b040a219410ca121b0b081c026be0a318248555a7c12b2335381ff932c47a9a5e4aa945011bb9da1af71241c6f8d45b2a379c7a570f0fa78cf1a778b5b7b21be3d1f27d6ebe97f379abb3f58efbaf3e15a3a49393e7dc3121470f6af5ff94aadf7ad7fe988bc28fa37377230242c2083684397b5f40aff1d0bf06291709287cd4bdba6362bea67f5e8e08494948202649b12d5a8889e110904b4385c32069d82c6468069ce4409b4a20c7dd7aafd495187e94de64eeffd26bd6a8e8e0b72b0d1521905426a78a0f36d7d5225561bf7240874c35178e5e9ec5179da5df59539f7541885531e6a1dd7d61209a267f6c73bcce47a8271be4004642c922106b58402b104e187b729f3f1ce0b9f3483480406843400d0ab44cc5e5b59c2563398bc461745458614b1426c68d1094275185ce2fa8a4fb49b365c38a39dcd9df9653c817201eef40077cdc6d381ad9ec4f44f3a7e0d84dbe0e83718f288a506d31cde577402e0de8a2c22ab533334977140586146d0a209ca9ebc263087d095a9c2f273da554aba0941372ee2f4efdae38db446a286337f16fdfe914b0802afbd251f686bd6333bedcddd98f1854dd08ce9f5c3b6f1190364879f5c75f71808a13f1dd154caf0d40121a21ebc80030f7cb9a22776ce2e438a42390e56a462f3f0825e12422374ab33baca6bff16c50b0de00d98d902f249cefa9570ea511561cb51e19da768bac6cb9cb2ec591f44802d803c5a059dbf4296aec7b2e8c8606f757c3166c5665a6942bc1794b0d7540146659955774551d0901c5da84bd5a5779a5f9a721183efdda46939b10b4003b2f510673e1be4751794723df1821a0a6931b645837655948f1864b04820eeee9b18daf0da26c65406383ab6d6dcb29cb0c1f36d529c6be0cd55b22188b50d0d32a193761fa17cdd7f10eefe4cf51a0517d5ede9e19c93c65048011a670a9981756dd4a6827e1e27c3d0b5f0d634e21c4f7338ccaabce33ceb219e4aac7a167c5168422c615358a5bbf00b33eaa69e3daf3aa0021007d282e822cbb9c4a9e5e2795ace6f38eaf183c52aa5aadae1fd8425e0031b2ff8f9cfde81c15005e7aba3f91de7d9da9b4941517b65141e5cd6fd9dd1f9f5f9bcd941eb57534e0836be4de684e0d9093d77584d8c046d52eedd3110e25a283c473510efcd3b9eda1436a4655235b2593fa450f78ce65e70d36b7cb6d560785bde5935af59ce826bf7b6bbd154fb6a16d875012a9da916340bacb3b60b74809866169c84b3dd3d1f5a081cff10ddd39a5f6622399ffa4470be88c58707e21a89628041ac400340d9186d2a884f914404a2185e9a069c416b2ace2b8126e02e850062b1ce8a407cd203f7a5327ddce2c928f719587b0d50c870ec0b5f69859c462a5f5cfcf0a99778754aced1d02396054d17f3de326c63ffb3f4c236133f5ae158efa772c1dca2fc12ba1b382209debc9ddba67ae1fd84651c416becdb1f4179408ff81809a0a38c9f8b4f2d9338418ed0c93c3aeda4e3d2cda3cb745a047644fc08e99579f8f609da3cac17e4bc6c245f0b7350a1e74396d38a7e94a985393720e6648df976994a30c241495a6f466dd626b766131d68c55f3ca0208f1a35df036c33dce8eeb5e40b635f2e34ac1c34ebef337bd50083bd950a26cbfdc6b9b5aadc569f519a39494a1aaa42b219a174320fa42fe453735c5d342e08127e552c1ab7d90c634eda35e6a8b9f6189acc2904c527152f5adca13a6dc76e5766092b2e00ab8543ce1117858e22fd1000d8208f8ebe447560551b7f0da78b382e803e5c93b723fad4a1dd160ecdb31ca46eccac2733c5858cb5f555a7be76faade7ce7b3681762224ebef203bf5f4eb146c722b5eea55157b934b5747dbeba64d4ae20bfdf83c72398e0fe05be3ced947b47de473059ada0a000f409005e7eed0bab8a3f840b65440fb657a41e27b4727166a1f879e13babbc6351112e9437aaf83e683d06974e117a94d53309ce94199fe6b489bae1a3840b77f18eb8fab2533930138393fbdf5b6cbd6ed304b27f77f639ab8c0e53fd611d186b54e9fced50d020cc9d724c6c6b8a09963dea8a358c29319887037721fb0e1fa0149ba69535aeca4d2f1480cddfca5013fae5ce3b121362ade0a351403a26fe91ffae773617ea687f23e4b247d99a24061efd2f8e1e7cf1037be867ff2b677c0824096d4d19a9cf4c9b4e67a35bfd429c198f4330ab4fc5329e6b70988829fb70fc2efe88b3a02463fd0018b901c59776e01c1f0e2a2332bd6eec04d4006ad8140390263eae41a757290a52766d5b62c48b91066db03373d8ad352d64d439ce2f1316a63380da1152ad90782e373873e3a8a7c9bb6d3d3fb6777859f1ca2fed888ceee4166efb16f3eea39b9af4bb4507b8fbad6a62dd46c154f416d057d40ec472090982baf3d00e64b47cb2c4a18258db8d7581c501b1a71f910f3a133996960d7023094f9017d0a7ae76a0aae2594ceed8f2402567e0e1de3a8d6c99a5305c4adc40b967e746b639497ec99cd98320f8207b7feedb6777fab238a00d76894a12a15cb05b80baec3fb8d4801e3f3a0fcdeb3681657b3c4b2d3a06eaf1a8f9a53324f23cd869d8d8257d54e08742563a1149a3317997ee51049fdcc204485a484a1fb1ee3b9d16da692ae478cb3b74c7359de0091a51212c083607d5a782c04ad50112270df1782406c386d8a90b4cd02a3af5dc9ad4478bbf923698deeed7aca82c4c7398d43a453e47b07217d1399d44ecf6d4e7fdbf5965b8c29fd207abeebcf7681d0443ca8df1d8d49ede328bae1326988bab42c126c9f82a203efb04d84d5f237ab990b5d54da1e45701c884824330d05cf4c22f1bd5ec010a1dd092af835af176986c269aba7ba4b117b4f4f25f691b6160807490828de5a3da4518e44465192a5e27d32b521a2b0095324f0a520bc2a02f6216887d287654f0bfac81a0e25a588f5421f4663454ad1f61fbe890174081303c29edf28410ba1fd913a11df47b475516e6f1fb7347d120b96402312ca5f689fd77a2123bf5beaeee9f3d1d6c8a9e273b4e431b74850cb33b33ee8dd9d110122abcf7cc8ffe3cb862d70454d49f9209fe38d31c812f58c144ebaee02a7df28d5a970b24c666977afa4883ef2f9ee0564b424e14d1ff3747b106859ad7032ba7545499ac32c648a2f65690a34c7bb68afca3ec7d0d251168a7c99015b610241b99b48ec4a26140822d835993e710eaecc8c5f23b194917672d573ae22350d1ea8fb73c702137fab21a32d1bef88449f02e2d7a7dd9171e0502167f06fd9c49e05c4c1ef130d127b5f0b7edcc9a2c001894977004c77c2c7a382576440128399060a801b05a8cf6966e69c03f030eda76169271dd97e0f6a1918ca9c4a54b1ac066a4ec69288a5a9551d8512684efbd53e10c98ac4d7f13fa4b0a80c85de6536b3e6d3699a1f696182b36ec7aca9f52950b247d2a31576001876fa18122632e45eabd7a9490a1abb51303aba6f74d22bf11299d45030c411e2bc5660429eef9ed979e17b36487e8b43a0ca8913ae2ae5958e2a77664c201cefdb42072d736ff6f83faedacca4d23c1ab5f4061490e2f105f5642240dee11e89b46f4001e55dcb1d9940ba997ec967721d5e996bc8029a5871dd6979a0d3d76e9042f7c21ef9c14639afa31b4babfe76e6069adcac89b110a94d00bc61ac72d39f09b9527c9f60c7de6f73e72ebbcb6cd2fc9c0bcb55c27bef09176fe396a789b28e13027087126b8d701d61e97c164ac364171819dbc28d4e2d405276f8b642254e157cd330d9ad281cd3daf323a37e57dbe3a3a469165aeaab7774af905f3000d1312e0fe0abf2940948cc524ffba04b8013002ea6b8a3e180e7e8c037e484ba656b021ad00babbea0722f5e16734a77681b23a4cc1f4ec767eb12353def88e8f6877b4e99b719dfe504f30ab3da1ae611bee0a73c1e760ff78fd6d7fe7ead85cca1dc634b6fad847df45fdb402fe494ed5103ae80bc55bfa53a76e20e5f98e97fed22cdf5078ce4d0894187d02eff1c2206af9859beb0a5d24f39a0a4c08b580044d1a7167d7172800302126bc4a4bae309e5439a8e7445d979ae2e9090dc851278b8dbc18828e6ea0aed02180d0742bd0128f5ef190585ae43d3fb17e636b8937d575129fb4c8aa21c3401680ea2492d8674e86cdcd2ac8ba93bf86cdb0f59704e2968c7f7a27d3cb4be3f25300319e62aff0221e2d1aedd897f5e91df5c2a248c52ff43b90b9498f858e22efa84456a1bf814f1bcd62f4ccd9db5fccdc57e8d347e2d7d6da5a66747f8d417f19bac05c04b95f094d59ed06411e87460625eba72ba1c41f412e204e556ef5cf412487a4910b8a5ea7a7f0eaeb2768ba7ab36d92202e07db0325f5cf8faa000026176f8f86eeb119a7730f7ac205009dfa086d14c980c7284075e59841c97ff87a55dc0fda387f09e7befa8e3e4d5474331e960c688cfbad45dddf32b33991cf0af2d1ab402b8f0fa22f06156c0bb41d4343e941222a9ce33572064e86776f77ad5c12b6f20b6d456f6d3af4fd0c3a9b7c4f686413611e09c21c1938ad7bcb410f627c3bc3fab2f4eaaee44f704a4fde75cff543ce6880e64520bdc32c5c75f9762622bcf38c69f1fe6887b871bedb6f2c38578ad15b6ceb1e029441a0b1ab8a06d642299c49a44153534c6cbbc24dcca67907aa04172a541ba022c6eb206e970a825018d1201883943b7d94efa2e1616c1eb3d0ae4e7d9dd421cb46b60c93aef688fcec03d73b7f098cba962dee22f588c944aa1525b48e2b41df3aa594c63d56d40649114bf6630ac6ab60b545822817f5d1ca5fcc1060351e5d0e606fbe87f851a43694dca46b29875390f06a71a6cd74aeb9168889b976a7ec4a236d2ebb75dbed07f937aed7873f9df6237e020ace9ba29b2052d729d73311f0e9733f3dd2106240d14f90c395bbdec71608cdfde6a7782178a7597a46eb1447ce4ec249dd084c53bca6042622dc409e014d1881fb8ca9a3969e72f379a951c51dd1c824e477068edd211e215cee73aa937487361c06bd62afed593d0b67a18de5cc57e7521646eea2c9a49086bf2949b71a15a71409e8fecb32d08b34d59bd96559c1f5ee24f486f75f6e5098eb97facd0f1675f031aead1105d9048c7075fb37107e0c48e5ba71bd1b51cf0515d5f71902888205c4c1d06476a808c3e353d712e320d8baaae0915df1e623f644119f51a923e376cfdcfd137b3922ac2a40c6f29970340490c8b4473d4598be8a99a871fa62534dbc84dd74c41ead493dd7eccaa88f2e44391df85ee3d10b14e55e9c53fedc3666c1a4ffccd68ee455978204fccb7d97629b31bba3ccf3d0f5eb7f2c139e4bbd882a250706d207ac1b1f3284098ec1cc87f6c581d9b7cfb2b1a33360b21e0482f33dbda98f9a9ba08c99ca1408e8660ce8339cc78f29956426c81cac4e006cdf140dd035d76ad75a8b07f758b6770521cdae3fc589c90020c0002d3e0fd58809310c92616aabe858b175203d082a64f2ee3c5bc04d1b95c57fe92a8806319b62dd5cb105e3de351b7d7f3f5d7b38962963d80247a1766ccd167ec694a75ed4b7dd1c43ed03334dab6daf06c8c3d1ddf557f032044ac8534ad37445048655384abb969a823d0438755ffa60d69f4473195780e2daa5dd4a92b39e69a46f4008f6faa3ac7d075b3377367011c0b9e4dbfff58d1d12c2c3aec436da74547f1da439a0e030c3fcd8b8eb92fd4c0176c117462e82d7420e56d6e84f4061373373898445373e6e3d1b9b7cd8cf4b26b09cb9c046d2f74d82a98bce3e600cfc1e0a00b5afb686796f9e4b18655489926cc3e62e528c136712f174df045aab682720725d6807dc0dd63e8f11f1ee56565a670f8a41a897a4294b3f784c7285d05da8cc03cc6100701203c74c9bbeec0b155a0433b8f66171831390d2b412742527764af5ae0138d72f6dad486b149a0c8ee64831110d98c4a2d76cf900a374901920ffa3bbd45ef28b5ef3fe07abb06f87729686aa241d313659a0f0da906626c3bf8138ea07a8e604fc5109ca4550deffe01e9f7d47098c3a21e8d383c9ec46b514766a543a557c8643bcb210b920547f99f5a503e4977e91a4a8f3bbc93c331cb98a1df1e8ad63169c68db2eebe568404080c0c60061473113fa9c657e02bfe4ad18ccc47e96532e19891f0ea58f5680b40d602105516fa0160f1a407f0db82dbd70b31d1ea2f4d9e1e5004d9662d684c11e70e96a108d96eb7e31682cd2778bd346a6a9df34c274cf37cb9bb5cc25de0dc5dedbabb6b1d852e3f7ac1e863b63f5f2f457fb55433638290030f0483074b44b025a68df2a51ee4eb6387fa46d6c1b3d37d690877bd3e98772c1c6a23909436e0af65d35488179d50893c86ddea5f4a61593058f21162b2580fb89c6cb8c2c0ef4fbe67cb85ecbe6a05d5035326c4620099d9e318f9028717bc4098f938967376718030392282c7193118a3e2a06de891b25b1cf52b01308c7cdf6257fe32b49295d65dabff664ba309ee217d331d85c5045a1c625763518201c57b4355ef826d50196cc95de3338a33049b6990a678112ef06617e5d5cb7fe3dd533f4c89b5d4b7693366ba13a3af2daa4600b55977e6ea10c29e78ab85f42b6fdeb38249b00e0ea10f4669f5313d2b46b3cfd959202c59e15dfa09a0f8b30f19a76a8b042ab108a10a0961c42254f92310c42224914740108b08bcefb1c162ab8c7b3d6f1d2593b21ae9a5c2c45df7a6f25cf34b71390744d6aad38b018b73f5c1effa95a8af41afa948d2d30261c991e06800847ee7454cbb128f74289ceb7e5a5c3954a872b9d8ee70a8f2fe43727254f55801d22ff6bc5bd315476c5b80ef9aa338d4123f9852960f49196780073681773d51af849488983a62e6a3e7e9104b8ac34c2d9faf6b38965c383d6aacf59678313ddadf178b8ecc61528c0f2c8fca9728f34bb241ad83a9b5f752f95bf3216d70bbf5baff3690e500fabad1f79fa9856c3ccf11050f2d5adafd4b944a2e9c1a94dc5af788ea73dcec54050802ef9bb8695a006842e2966f532d1a9565105753e9843bf32341bc2b29bc9c619ba3219d55b9045c53edacd0f6a350f0e79a45844416d487248e461c38e565c47a41c2a2e3d66a6d7387d0d5cd360133fa8c1e472f95e2f727b0e9b508fe5712292313920914e5d38153e87f80b01cfcc4e850477e9565acace8c3bbb3c94945521b0f41902aa342119b27bdf9e40c87079f0a5d1fe47188f13dd5e9db0b70e6dc3d55b85baaefef4314f16818055bb0a633d9578bb904f4f1b71d89fcfa2eaaed2dcb7d60246a06bca9013f8a52a6b8acee4704953b2010ab94f6df7b92d18a1c6b556413e358f107f93333121fa22877a9ad060e1aa5e082d4b24b2d370fdc84329aba3e645389d81d220865633e4460d388aae8a185e30dcc7939cdc6b016dd4c1f221a4de3078bdb64690037f413cfbf768e8403a30972e18b455d2ce17fa944c470e7c9c8703988b5abb64676a1972c9b48ff6ece7a6e851d3704343a26eae0047b1ccb2714420a6e280aaf7d08e387e2a5eb2a59981a998b4e361bc7df234a58a04ea8c283cc24b61bad0365d92e40ddce8421eac3c24c6cd27f8e2b134ccc3fa904465ed49ffc1d10050234bf93d59a8a3d8e78a250333a293acd64829f78df1d5c4bd8535acf2c37aa3993b8e04c7730de5efe2b220b9654939416084ceb2a2b81b18df5a12a83d163ce77118d0f5ebb32851ef59f186272ab9bd0b1944e58a1ca5d59a4d435a4e87404eae69ecd0215b2a21d79dc44221c47fe49f0b62479b31cb01529fd1bd14de8968e2e87dfc83cef6865a4f49cc0be2720cce9f6235e3894b2aaa89f052a2f35c5e8859254940409904316daabcc7de48d9a165219d990ecdda855a8337bbb5137a8333d76a3209162b9a690b1c712e40e87555f2c17d9c85f49056258b85e92fb8dc5d422645dbc49aaaccec911e6ace27a67385ae5d0c3a55ad1cb8f797ec06851a11268292d278192cda2dd69168d5f4496d1f982654a146284bfdf1030dccd1aa379d6c22c0961831442ef16e23832c4f20b7c37ec39b3b6940d50127229f4199397aa90cd499abf42e49b44b49d44f7c0d373a18731c9d0abfe1c560d5d5a1b9de429dd6e361d17ea1c858ee5e694c76e87f3c36ffebce164c1bcb221c319be7885fdc2a19c88e1cd2b1c6b57e795398a1bd3ea5828446879a55b3d3ed41829e93bfeec4a3e4e639b7433253dcb19c50eb5a451184a9a0ba496312981b9e65117221e9019e8dad1e18dfeaf2e2853bf48bf14c65def8ba4dd95eecb18f54bce97c591c6fda92f336c5f38b320b37ce8ec8cec88f22339d605597724eba26cc7b0dba2f32d046996fd5505dc23442a7c1510744f40c02a7db607570c22f14bbc28252f3181ac20d6b0f9df053f9756e05a617e7b9905ab06a327f82660ec3e714b87aec9bfc6324ec81142b6eb2e539ae9cf4aba1e75052e98c049cd714576a025631a38a8c37e9816bfdcc2e3df5af85ca27474fb39722501301769c7f286ae2889858fcc270798fc4f680b5663c471d5c8fcc8f59fa69fde6b3af4dad4ffbc6f0e442c10a7506f4ed9a153964d0951ee94111bc7535406a45051868d3ebad2de3ecd937e5a0ea0c2a5a07a6b314a09159195d9c944fbf1311e335942bdafbeaf48acd9f2241124ce6c3c1b7a24ec088fef059036f6ce951ab17c6a98f0a369eeb102126c96a06106f76d3515160d879ecc728dab512b28242ba7376eff05beb32af7566d1b4a6668613900856c6014c606742ea1d2744b0ee4d178c64142d66ad0e9e0c8497865a524d42a941733fab449b900a66bdc9b9aaa47d2600f951f149db0e8ab8c73dc49ca8fe3fcd1f8d602ffc16b0a2264d4222a6ada1a5a6d561057d446aafce88682d0fd58f6543113584cdc69c805d8cba057bc50f59564c8476f729c4947319845d017a1076237f44c00ebf07d48b94aa9ec8782707e974107f90ec4397582728e50bcdf7679fe184bd7708ddfae61b2c94e79ab60cfbf00635fa4ede5190ffc647ca5b0d54c05248e15c049b182e9e2c7189806a08fe856739aab848306e0a01bd69abcfd7c873a0f5a428aee0e29fafaa9a5621dd4015065907c69f41aca6b4dc4819810dc21461a9174ae32cab82fc737f9aa81972a0adf766cf6ff8ef005eba336c3c4d617a553049edb6f8b812bee4c37802cbbedd17c6e3f194e499f9139c796c2feaf60454d1598392d7a6361f89cce61d5de16e4801d3fedc2584de0936fffcbdb1d15225c7892ba852500c0aaee5382ea3647fedb3e0be936e16ac5d5b26dc8d56f5f2f502cb8b2f7b44cfb6470071a0732ab2e3796b61bff69abb93814c00e62d2ff5f3cd42036d3893b1eea7e71eec9353c00ee79bfcb92f30803e0e4b1a05ed62a06452d0530c9e2d90d8ba856f3bc9f8e0fe5096960d914f02211f0f0998094d0a17187aa5d3c006e1318e0826393653ef50373c4509a4aa8b91ee00bc576ed11e6b2ae2e24f7c3091cc2b6309755215a8129560da2de97d06920983291e8a9cb7196bcfc03c7b739b58079a885819f1464c6aa402d008b618124a70869154a4c77a9b51680e7d8869497eed59516a08fd51fe53dcf476133b4ae9f8f06b4c5ee459be80e06864d03dc27a97511c18bc2135d559e6494e763d2f047d9ee2278c6494a3a62b83b0ca5e2c6a99aa1282439a2c586c2de733bf71c36577dc32900fc0276c2f094c4093450f4b62a0d3cd5b43aba8756466c3d0dc2f7c6292bada173068c0e378e26c6688e4bcf6a955f9f83bd2815725b81c3074a4db1e7a442c795a8901e25159552491b145f98e5e1f7298be2f3f323587f3070d27ab05d930126e91ba54411df9528001b78482c9f6cead10b9d4f57e306aee46cc77942783e999a0caf31fdca1b39e52e3ff915433682f161e10705731c04d2ad63fe01b46c0449624b0a8d9026a6888fc9ddffa0465376d0466c29e4bed829aa5f22a60a0ab080786061b320b580e6276b26bf90877e3361f60a4c085cc129f618053649d94ff485d2990be50df32c4c137859dbc2e164dc782d0083adee2cbb2b82d18037c4d03e3692bd35110eadf1fb4ae5341327c5b7c13d68c2baa4acc704dc6676ab48bdfa2fa53234546e9982d2cc9c79de74bf9e394332b208e39fb9af032b15769b2b21d538ab1291b38fef60091695489c388c4453897ccb675ff8a21fa2dc2535dacb995da98ebd3fb288ce32611be6b577c654c102eb192730ebd891498108950444cb8113d617d8f38c5bc31b716276af61fdffe5da5e20e06a4432ba273bb9b53975c0aa4c4fc0b5a999c70bc415aacd3436580999356e4a46e72092e7ebc93c080e1f94b7df7382dcf9275889be2fe584dacef20441870895932844def86e74d43eb6c2ea1f94b9800f0f1d27d4ca01200852b741ee12e69f01cd6819d3b4cf928dcb6a1a70ec31c3061c1fc5b501f88a63b4a4590705258a2b69188007b869ef23740ea13d8dd2e90aefd587123c6e0e00e2c978886f5b1ed36a6ce8d8646cd7573c21a2785a04d4de6812798a06bfbaf471601c8a9ee8fe3d3b3d0d143b436ec600f4e313642fb275b42c00a84019cb953f600861bc878236abcacd609cfbf218f8e87c0b9aea37b68686e9a3a4ecdca96f865d922ca9ba2057e8c032488a61b53431884b460c72e369b0ac7ae5992e6d60ee27203162a90c7a756a17a646fcd4e6b9ba65396a5b1a36d76623488c846f28d41d0fa226e2809802b7f7cae6924155f38f6a522a01e5d3cd86605e62d44691c875c34edc6563adbde84c4b52c15efd7f03fbfc5bb86f7e55b90a54c60ac79a1e564fce526060eee0dd6bf2070de33e88e7d286bc86732497c0d9eb1c26e7d4ce379d37ff768ae2f076409976525c52272cea5069679635a34f1ff42a4951a545d9d38e85e84e4e09205494b3a789cb692296d29e403ba0c5ccad93eeb1d088c6187e39469e8f04aa7d42cdab861c93ebb6ded89ea4a728d48b86f796465b34b006d4a81807b6700c29ce79676fabca389e13a93958146894ee5d9bf9f590d34d6b88690c566964c042df82c047fd567562553f621064f6d378059cd3bea90ef2a71f252534c816080f5bbceac844d29ec87d39098becf5ac91eeacf0ecf9a7a498b4c85efefc503298ecc1feeafb932cbb7d0e80281363629359d756fe0a29c895f140c80291ad58357d07905f7d5ff40e328e15dcc8822f9dc50a6aa94f5e1d214b11aee9007340b6c1eda4a0e3d4bfa2c0ae3e9f41884cabad932832ec8318008148a1f120eaed1300e8c7bebe41ecc2336f2aa3013a5cdf0478becd68bd53622ccebbbe6a70c7c6e71f461805c50a5cd11802b3810ec0ceff6c26532617ec41038e3b948806e49b35d552f1b71f0797068d35135963ab6cbd592b2fefc16c54be88774e6fcb177d436065224bde238a9b21b0e0f5ea1192ba1074a33a734733b4f1ffe85a927f103c3cb77cda6a931622c6d7650e1b6d2033e7fc83c9d5ca7d7b77063543f90364d5a365aa86525ca2b0493c89ab1773860535c1d4d2d0891416d0b7f4e7b7e790f7213437040de2f202f6b2f69df8bc24b424dc632a59cc2eb6978a8916953eac4a5bf3c9ebb4b8ba4654084982e0b216424e5b623e424f4a3409e49b97f190b6f589379e9cd07ebb856a0d40f99146c59f80c5e3428111a382b6fa3b8dd076f20eba25d8fa7738d7777e301953ecfc7bfa0cd48b7d2f2b3d1df46dbe023569e55c22729a2a8aea8f675f933fabc2667459a30cb7ee35731588b7d2e83c2c36dbce2e595a837f7385315863dd1af44843a282faf208229de2abc7d5e61bb45e783421f33bfa0defe5454923866ade8a22fd4287df63872cb39688ead077e6a2a1eec00ce6f8a6c00486a1e46076a2410b5cc318796f913ff5561845a225a3985fd1bb0fea15c22e2912e7f561dc5ecd23e299ba97197d3b0a71c9220565728af3a21c54a926307eecc38c078a4e11fc09db20539b62aa2d4c1cbdc4b8f68964935d0464a6a1d46cd26619be9519f5cec57d63a8ee01008b687e964cd9b2f415b1b75d6b935744d76eb69712d6139fed951484dffa000c5118dabc26594f33dd01f61bcd067be779cc6e43d0ae103e5db4fa8dfaec26c6307d6cdff4e4073b3ed71b463c339224423402e0d13cb1fd0e8dfc1549fbc546ebdb528ede3588a80e9fa0d33b96109f6063892c34a1cdbe386ab42f4ec5db89bff51107df62feb54b89f9b380760ba4e7541fc053a769b50f7bf13f6eca303612662b26f4a3281b3e4d9431101a20a31c368517812ed37da39fac4603a3c77d351ea5beea2295ca03c6a802b049f383a58a8ecbcfcaf532d11887a9ab908f76418c279aee2b4003d475b0c7b9ff9a8a632439306d09d7eec601e0881eaea71f594347cf046838defc7058f9bce0139c035f608e0378742a858c6a5aa33443a748725df52386ea18c2db7e0277808f65bfe849e329e60030a8349359450104ab320ea8b71eda15b861c0338f48d52341f787f69ba6f4b8c60b0d1fcf33e3ba7229e0f7e56fbeddbbff1a0dcdcf9f53183065d3369b6266c67f8637d037770e413eab6917eeca27948abae6f6c15d33d9ea8927d51d827efe2695a4c353f430a587d8ab17fb14fd0bfa770df96deb40954a7cd2e80f7affa9791b9dd4631acd7863fe29b9aea4fd7d525baa9f42fa781611f1ee50d66705b96f109633697e7b9f1bd1ca4a82bc9471dac72fa1aeec276474d9d40be96aa29f329e70a623ce3584f07c4a1b6e94eab5abd98c87a126323f455ddf520e3c10d69bd60dfb5852ac9416eba5cedb798b1d347bcbe11d9bd39abcd6d598e6b4615c35e3d58d891859d9fbf911c4d154bdd64352afa18f822873580fd17db62dc106fca5fba8679ab6cf523b6d177b227e3ddc413bbf6e8b8fdb6e16333eb8920f285d2c59dafb29fc4f08b62d76dba92eb1fcbd0cb2055b90fb21b15999d72966fe945b9f7dd9b29546986ba8f2f99d7671872ba5b4145b292ac542339b68d55a6b49282a94c8817945310d2104dd9cb6fb62941c408a154785994e4c6717eb8769751668c9b65129268cb35135a39bfc1ad5c5c3d8e62fa646e584354d00f9262283bd21836b4625107f4d5d1955346223c849fcf159468b99e1a861c84f3245e9197d7bc730d6e569ade69ba8bdb58f4e71915125c9d02346ad91f194c202af42ef34f3f159d6842a503c57210f897ec670f7713444281a2854711945c426c98c12117e3d46c55b01bad1a91174498b94b24ebe1dff94947b1aa4ef67d791736aa9dc76fca1cc1499f9269adad522754d39f96d568563334b65a1fcb6f739de1c49ea00ad51e2e65cb927086715555e28a0e72b3a400252cabd7c03298b73ce81e642623acdf7d7baf28a14b47b91a60395e4e2f7ffa05b61124534a353eb135ca353858a6a85afda5d87f13523b57c646d69099584ae73e42a505127af25846825a56177783588c7281be8f9789f18d7944b00bf2e7f72c73f954e516db47c4290ab7d542924e520115e26bb752c8104f43bf2e30b8cd9bd7bb07867b80276360afa9bb92fecfd8f18fd49703e697e6085dd4c822ad538b422ac80f69537c80d9dab7133cb836579ecbef6050bed3a34e45df47ceebab039bb7afded091861d4ec462a4d0bce9e18b38940e9ac13b6eb1beb463a3724f02e238f28e6eefa2415775e523b358461c41f4935fef41985455595057f24dc3a711f2c482a9b6f2ec2a398e23cb2c03ea68aed4ae92353be4471b0bf45268bdc26eae420f988dcf2bc64d1d949e761cb1fa9fe8387bcf9c09d063cc9d455f8dae6ea639a12a3c8e7fb7de6335e3248f453d4c73dc293c8b3da39a43515b0a502c0dc992b7bcdac06c6660239f62c05067c0049a532335f564b2516b4548b99a55225413bfc46f2a7e75e907f1c96aa872948df549774f34da29221ab48b5c64457aa54b12ca179e32a10b6cd10edc14a95fd876b1eec3f438682420656954acb41a5d5d321105db35af474d7bd0a1c484fd19f3becf1ba4a0fe2bd0dfcfd36f4c0ad4a55b2969f5178c2032b4e42a9d07ee062bd920a58dbd23d9653a96e0de62bc9532ad86f10f123b7874a256f919a5826c624ee37a5ba7f3fdd9f888d8c4a9650c1a5f7f3891986070e464aa517cb5d6cf9d0bebf3fbadfa354bc74cb445d9e75ab3bcf4343a9d6be77cb5d6f12a4fef354b36e559efb843871e856cba7756e2cb7eac17f88dc8a24e2f8712bcbf499a7885b9113ff76d27f0f42edd2db8a5274158376cc0fa00087f20fb23e47f477685b092be35b7ab5ea7ac20aa15b94f9cdb67a6ca07f92a10485c40c1eb615653841f08d3198d3e23e34dfc698581a436c5a9f9e6646b565d300aee59d6e9f0649526fb6795a56b0150db1adf40787469a1dfbd9ea29d613d5b68548a7977d6dc5dde9e5a1513d7836583c199cba88a95216e53590fe93c7f498be6c1ac87df9bb3bc6ebfeef4686e5e33cad3eb7deb5d5246bab7da26051b5952b7c65b583da8a971b47a147dc67dfc00e3738eef72962a86db74ae780e6ce859064a910e9e0422a5453cd81f2d996ceafc789014de944a08bd2233df808e4b27e02668b093d80ce6cda8ae093d1f844153c4f8ca14e2623b207fbc22159b59a044714a103c7d00b97c33e8619f6d31506c1b0269a6ca7e6d087d30414ec5b3975ba8c015ac9a82aa8d530e66ee252abe2a0a8fc97d2c203a6d2568451cc10c4272eab6903aac6d46f8ed7a8c44bc72eafb6e0f5c555b725cc56e84174491e2cc6a5ad86df1fb3d5319441f636176c6162972c57335382f2940976f3a59c7d90c03886905744b7b457c4b4d2b81f3a2e37e31209d6e2c290635bb261de4a920656855c234bd6fb2566072d5f2110ef028cf156f38fc4f24c93f977a3165e6896b40df9e8717f1dd2fc0eb35b81f7cd299b07a0b8d017fb865eeb86002b8a9561b227c7e5d1fc0f8d0310b2a0b8955cc10ad83561cdd4868f4a16e405a346b6aeb95014757d33e9be5b340f42f6fd9fe7f7cad6c1825bf07b6de499f699c63e71d3e472fa4270171acdb95e01a7ea32de4f511b690297bfd8f923ae3498ac83b8ea22983d5c51cfaa6ee45ec3151f7365b8fa75441a491efc8c0ea57b4ca72b5c2dd9617bd6f495e2dc4dc3fa56299065fd7501efdbdd8ea887c329d763e477d321a7af7a8aa418542daa6a156ffeb247119284088027373866ea703615c9b880f2dca09324d9dd329ea8268128ed7198444ba57a1528347eda07f475fbe0c4f98c718f0844d5e050158f310627f2cddc1cfc6637e8522245ec480b10aacc0861c65bd6d3d249c1ac286eccb036853f07975ef6201661361a904e127ac71e1aee04d5b7b99fff9f065357ea85395d366a5298b4d4d15d08b8b77a315adc9e4533b7f8cef28fd395c3806e2750d470bf50f3a074ef99e0f96b5166fa866bc0c2e3e58567adda5415829ce0abe0713e52a836a7b47de8e450cc8cfca8aed496cf3311e3f289c0b42df1da0c68cd7c242a094b97fb4a7d732462368060ad180a4a73f535d6a2d62bfd43b4fdc5cd39c19fe8e78dfbf5e4b5144e807cfdb2606f52bcde4499dbe28f4590cf0222455a5d04881c7bb76d6a39a677e345f9f33fc6c8b452ec4e3fe163cacbbf0609dc4a21c4ef0a27b58e19293a6dd1901e701b43ba17e3803d74514f64008fb048c98eb05a64f9f8b3c30da596604245f1141da1f250e4e41bb1e89260da186d23637e03d14df05d497f7bf40ab66923d5fcae227914246de4fa5fdb447c7fa625b8e869716ef5cf1d3b8ac32c41c295fb60f01194d3acbef642849e067938c45aa489c1f24e7f454e321c12528ec12c0441de24145e3f15cbc13236d3069aacd184e358c0e1b814a8dfb28eee433e1e51af2d63360d1a208bef182db3bb69885e62a3b8dc8524b8d75d6ecadd50f3bd9f5df9c78c36b66604c16988347619a9640a3d08e280b013f7eed93a6d82d65e475b900f8227e183e049fc78d7eb28861dd00e0928dd26e5605257181958d6e64c8656a4363f46982b4054dda1a66c7eb561a1353f6cb163ce9756f14087e7bfe081490107fa472fe6775dd23343af90569580fcb06f5ba5415a37b186f9e013f0aab144eea0ae4365a328b0dea2314815c91cdb735b10065426e7b75f7ad7086e51e93db2d606f1a1da42b25f6045693a8396bb031039da744edc44dc400d3e9238dd029ee81d4028ed9d5480f53469a3b7af4416e0129bfa8605980e22e3c71f3498004804d712671f20317ee6fd20fb516f83c0def8b02ebfbc1c139f000c1739f5504aab7163f729990c144ffe697da3a32a5299a48dd2526676ac6cf1ea0ec7e461d5d81a79bdf526cccf6d6fb71d687bf6808ae7cce77e141ed79aa2b7400710423e56bad04a2afe51023f92e81232d15f087d3ce2570fe7aae549dba054f9fc710c4d5a4a772bb027b9ea3207bcdfccb4b85304288b9a0c73ec456ad240b9d87f10a5d21f026422aec0254c686903716bda5f92eb7163647af446c680f6f53b50fbd813aecf645f995ee23472348250322dff32567ba171b1d4ae9c86eb55df280f1e910e247f77bd50fb58b94a3d21c9adf9b4e2d3b28e55f011ae71bafe72c88ba76492a414c12716e42438f55a204cfe295aac409ec0406d8bfd7592c3e54b37cda344237e3d9772951216f3640c71c09a090612a4cb724a6ce880d58077a1997a0ee6d4cdaeb14e6a8419a778278583d10ade6f8d66b8568058a1fc1e0f170df740a878539c0fa06a3bc2029b3820d4519b6a8456375923be6c1b7754bef15fe51af72aa7b45b8310addc1558837ca674ea5a5541d9ca2b8a265357129ed454f3fecadd4c04e28191157adfea3da5bfb5e2dd7f2cbe133fa0a1ea8bdc83d033a0cb119783163dfcf1d9c4d8b619b18dd7fdae2566b95439c4e4634052b719db9370c0c5704b38e80c924bc271ce37e447e1f81b2c8ab1dd930cd50bface035cbb85f3b96813a7e298686fa8f62788f2bcfaa5ed25504bd09300860fb7cf4e0858e41444b0235ca12a80c6585308592eae4a654b460a779a216528dd1a373b5f02687533ae4850396f163082b47c33d28aa076e02c340439499db1ce6c3908b12c01b5096383d244fbd8f7beb3e5f001753a4a9d538b15ebe92d36b1826b842fcfb0f966e8aa9b00103cfe9aeedd68036079a7c35e446f9f090ad5391bd649fe433466d5372e7d1d176312f9ee6e77447c2fa8662fcf110a42e8bc44a6e4021116c15e124e2ff1650578a3649c8a2904df0cb51a048f4e18b7858f4c9a9b1de3d48991c041ccc339db71b5033184aa15f228122cc68920f37b720ffe2e1f0fd332921accf9edf56c14a42a0e09faf30340f54ca192ec86043ae4e6084a96e78b0b582ec36f4f6711f3df22aa90fdf06b7367cd7cb16370a430d2b995fa13ddd5f637ff968ccb40b764d65342b90bc55f7e9da220c784b5d464d7f1c677c82557e6cc8b9e030c3a86848fce054ed3ed490c2e1bc93ac232f340183be16708e44c75144957a262fe5879665ae3679c6cc7013154a9b47a45b649c552b9444a9084dece78fe0b217f1886f68fd3ebaca8e0a22dc20c9d17fc080941f4f84eaf4dbdc710af8541d8e0cb2e6d8f3a6691cc63d59d894bc9167af7c330bb0b32c0ec2031ae5619481c43fb216963e81ba0afee753fd0989b42cb218969164feae3c8b1cb49e27591ee613b02613eebc6aa0426c69b35804f2a16c8807af229fc20978505cbfe8a5708cbed60c5455cd67c04b2628053d692643157a9fe9637fd589040166b666996b6d43c8ac0ff956a0d62dba9eb31ee5476bb7510e6aa817fb8335844ee15c3e79107ce4867dc3d1cb49e10d12d3598d8a08389908fafdaf8a23d365a60d25936ad872c035f2e27d668652896d32b5e42311ba3700ed07ca0793d6bfe2f3c03c5781fa1ae86772420310fb55ed6aa935445c02960439f3dafe501708bf72f64619534abeeb3eef86630705b7146434ec10b7f776def7d54f5ea9d7c86079ac3b9b57329901a7ed2b4a0364a8fc1c8de80b3c9385bc687108db00056709b491e7e269f79726d38be6813c7da0400ab17947b7fffa43fa105ee45e14b6442cc365d3ac2037cd0047e9004180e5620581aa5039a0c941ba5ba09822826cad2a324722bb14b46aa3b19225302a634710515e43db9c39fa3a51a83290af5b0b01fe203a0081bec146ca4449b08c43217f1db231f674180cfacb54dc8de9b6c29a59432a5147a0d5a0d280ed2f771d07c668c5408f5903a9944efbcc771d1cf6d37d87db413c5db923c9fe76ec341f379beeb1c1542bd93f7b66e9662053b4b769da3958b7e33215a91f7cff9aedb5d9d7586e262dedd1556a76b981ddddd52abd3cedb1955be62da0adb43e637e90c2c5fb3c6b55a301d971043efed077e8b06b15b4650f2ed03af0ef4a1f9423be62fdfcc07e62693ef05b9ad6d9f634668f2d8b626b7767588ba31e70c88e5985f0fe30c485e2f0b329d9de9ae0ed7a6bc3a5b6dab88dcea101345ee2e576b019d6bb6ca8f2ddf6e030ebd6a55affab5a2a2a2207c782c95ef59a3f4adb2c15ab50ebdb35c79eb21841915563d1e495998f44efbb4bf6d88db7ef4ead900847639ced7e5382d31987e5d40a44be9cffba7c7b5f59c738e06393ff96637385a9dd7f60add06b3fce43dbd55fe5a44e67f10aa2e60b10306e99d0b5517c62c79e735c79829bca34eb6227f9b0f3022c1c487a98d7376b6717402303089c10fc6f8b0ef9b867056f3d7d531c61863f476d23bc618f75b3ed9b2a547796a1968eb9587a757d569e8d5f429430dbdaada8d0dd3a7cbd0abcb6d80586fb9f5d74daffaca367edf57b91df6e9176743981a611f773bec337db3203cef888024c02608cf3bcae31dad4d31bf2276092102ff79633eb314fd0a9fe5e8adb77e3320d663fe36e7561091cc3b95bd1d616c5134e76bbf7684b145531e9e3fe6f27a498052f4ce55500092a4189de37811092610462498bcf39a2f8edefbe2492eee3177d9db78905cdef94cb65bde658ff9b5f5eff3cb9b87ddf2ce659eb74f1f0e9b59907622f0ddf46bdbbf2676350d22e2fce2c1f4ceb58bf2e09dfd38b731b9f5d6b53c908c88304110495aa2f8f0f8d9abe8d3b15e499f73fae24c1ab44b191a5763a1b51b1109263e6d24290049128fd911c616ee37890c36b67c7d4e0408a45f87b294072cb4fe4d22030cff3e94e79209ff1480440b9ba642fc9be4850bfe0813b9e6209091052443c943089d0c2effc26f1219542611235328e952f4cf7d9da911288ff2408d10f9b82c4e505234c4bd12386767f7e7ecaf8b303f06978fee86e4304616ced944771c950e3b4481557ed0053a6fe7909939bafb81e5a043af6a6aabd8d02b7e2886987f0fb74a9687be75b73c8c427af8f6651f8abf1b3f4eaf84788c0feb43314806be3db62f37ddd5c1f487adcc068c331fce49bfe45649f0d18d94a0e791f6d1eed6a2b53d68d21a63cc80385a82eecdf9c51dc405e14883b077aa57d2a1dfc82d6e9b84a5cbc70dbed6ab8d0edd78e38ddf6cccea588f1b8ff9cd8a56c77a4fc7ad8b7e33201dc4b3b7f3626d71551a58abe995c9d753bdba7cfa12bd69cb7c6e44d765babca7b1cd66bae59915fffccaf2cf4d7e2d47a18b7f6ec1e03515da266159f2d12f2c46ff3cf3cbc2e2f6fcba88386e153a66591bbc1883973f76a12e0fb209221dba945990e797cb930c32a53cc9cba75f96c9f72d13f531dd722b08ba452bbfe4b738f36172cb9409796ea241d0eb739809f981e8310fca843c97b446c7a8e57b99b0cd305a8d3ce671436dae89d82d00fb20413911bb0588ce3e3d0edb4c54c88873d4b63e9709436deb63725f89362117c4a241993fcf7cb39f18dcd03277411e90cb23ad995fd4e672e9cf336a73d120d363dc4c1e34e59441a69c409e5bb446bffc0a32d5e8189629fffcaa98db58be7f79dc6c2cea03fae517356d98577eec72cbafcb9d5b5488c758ee17ad268f17ffc6fc32541a26c6337410bf7ee2c1d32be744a6c69e5c9703728dc4e448a038bfac6d93baafc6b414156536bc4c887be717dc56e2a7101eebe5013d3fdd65411af5d22f86a77ae4e847cf4fef0c8887173a020473c1014452fcf441c4e48574e0d3a91e61b2aba34ee7e8ec7262a6f5b92cccb4ad8fd51ea9a91e792274f9e8ad156d453e57632eba739990e7ee83dcca4877755c10bb4d902a82d8b18808571c1190fcb0d42be9ce2fc70f81e0f3869b0fe7cfa7cbe83f107da43f40df56495a1fdc884c244204f1807817643e482b3ff4117d57a7426fffb8c1476d80b847831a725b71316fde46da7ac5767b7fc2bc7c911954b6f163db63bddd674522a6e58b60a7ce84f0fafef8f02400ff7a5727fa73ae990ff675d3f690edd32608bb747649d94dcfd474392cba6c8feba6157251d3ea34177ddceafcee201c522d00929b07571b69ecd7b57d85f6b03cd3876d03866d410dd4cedb4db44d0283cb9bf60c296f7accafcb619c014db7dcb455cb832e37f56693f97bf79b796ffdd9b5ab8372e8bd9d1cb5d5f6aaf9683fd1ca9df968d7bcc7699b4d90f693b79fe86613c4f4eda6ef853252733b1bca4f9910d363cec2f6700e332136abe31ca32caccec94f1b17fd66405ce444c67b5a733b3214153da8dde4b57d6d20d2c264eae9eb653f7abc1b48bbc9834cabe3fc5a1d57a404ed1505f1993bbfac2ba328dfd5b1507ec2dcfde5a7eb645d9e6d3e9c5098f7340af3d395616e5d18ddd57151cb4ddeecdb1b9491546b55a4f421ad6955bb9f5aa6e06a2330acb491067f93c0e8e219861cdddb174dbc736e99e96e73759656207eddb5e11c1a655996617e79966559e68b937510d7755dd7755d972fcec586655996655996e58b63a921a594524aa97197ad1bbe5641e7b93a6d4087edd0dfdc1ed6f6f871beac8626a55f28e7ce39e79c5be7a25f1ce7c661b44d02c38a87fe13e30614a537941c3972a8e038bcc6693c478e1c3972b441434333e33234343434345e5028d4c95128140aa584ebba4c6e5dd7755d635cbbcbce0fb42fc463b2d591d8ea84d1a2948e6d8f76e9d9f6d897de9b14c2b56e795cef85b422fd42ef6f6a16a7390044ba74f9567c964f8fefcd686d45f8df56847fba6c5f9d9d34687af477c3f468d14af4d67cce965f939999997dbef6c9cc3fdbead9b39d1bb5bed9bcde6c9ebfed47cfefea782ca50bf1181f86b01762c0730f40747000d1cfb55c6e757ad073f9ace9ce9a6eb27c1062c0f7ea3c1f8a6fb2361f4cd3f29e9e969bdeb3e8ae8e97f980becdb9716de7ecc7874727259a3c4d00fd82ce2fce1c6e4cc5e7e2daeea4942edd351532b2eeb67dc7be0939962e7423fd9a5da56cd5a432baad3a2fa23d915714c4d7e942524c1063bec8fce9bc552be879fb85cd29bd4e179a488408e27d28129fbd88fc1f885e3a673f32faac43fd268f262ac462fe39b501f2d31e37aed2a3f756a4a7c7aed185a6cb75de82fab9aefcd3d7a503715f7f207a1ffb8f0639774783d67b2d9643d4d4cf9af75ff75dedf0c6c560b43e81a184c1ac71a180448f1edd347d7d583ead4d336d57dc8af0577ecb792bc26f59decbdbefc4d42d1a34fdd27c7b6026fe6bb31f93f3f8a0e997c9375f1d1edfaa9d1f834cbf286aa70ce6af8fd09cd36d155b265a89dee4bc5566b7bcc7b55f97c99a9b8caba31dbed5d1efd9da5caf8ef6ea00222d9e5747bb0388b4f85d1ded9669db08cdb9f66e7fd3f9b8cdad7285ce2e371ba642fc71004f24396a71d003f01552366ad5be51aff69f73607bf8c0e39dac13ed129c7d69a44888d0f1899e84c687fd391bf5aaa61279366ad5731e5f6b1e49f422adb2d13fafab847f6e645d733952586203296c1045cbf3e1ade85591c41442b440073e4888d0f1614f42e3b38e8488f6919e64013eec714bb2001f235070603732c4e53ce7279f4b6a040a0ed1991af1f7760a4500c1091a635051c6107cd6e735d1850ea28842098b0cd4f0599ff5ca03072f2af261a35e05e09ff351f79be26a2156858b07116a5f08be77e49db319ff28acb11e79497988b44627e2b0f0f29046afcff9f96641f60b253e5227f3825e8f7c6577d089102d79c9c37e71f4d0eb083e4acac36e791879500092a48774e9c90e7684b105ad40bc730603c9dffc260d61097f3506372746d68fa0c68c19b3c4c7127263c6bc93a21690708112263ec0c02406558a867ad82defa46847185b141551009224208af68b239ff53a828742ec161f8b16ed169ff56ae421adec6e77d44db212c50ff13b2952e1085898e08ca2229fa546d68ff010050bc408821621292af259af3d6c36811d61288d99804c194778c11b261833c6a7e65dbce262bf789ff035ad226246c0b5ce6eb8f736e8cc12c2e8ce2584f08adb4dc668fc34e4486f5a05e9bed70dc6c53eb707532fa07c4dabbaf05b9d15e2314db755d33167c1737b40c76eba8beea23b9761469703deb42a3afce926f4747316dde6397fbfcd3b233a94f4a655f1f1bfb93adb5daef6b73d7a4b5a5ac2d7a0d8dff660ca84f79bd561ba0db40f5767899af904d73af4b73da2afc3edc11b8f5641dff23a5c9d0c4647caed98b687afce74e8aa5e69ad9a96bb9dd939d3733c74d839d3a16fd2e7d81efcd0e34daba6bb32e2e6d39d2f59f1a9564d1be9eefb6d244d5acaf29bb48484af69d5f428f4dea237ad9a7075d6377babb3fcb05d0c2e7e93dc32e8d7b5f920facd7c485f0f92be8e6d55ba252cff3cf3b1b5b5f1cfe9d64e5aa76f5fce139b5b5f98636eeacc31cb3913020289104114bd500d411b6e8cf91f76e98912237efd07a25fbad8856dfbd95681f8eb868c5620fe726c29e630f3617a37798f5b8c06994cbe54c8c857f7a639e77e9c6f731bd1c58f07707aafa0cb003d15e5a1dff4aa1fa6da3974e858af36a9888bcbd06a305f32f2c1d7bdf25c4ee82f3dca544fafeafcd1abaae9d02be93448cf81cb4bbf40afde4bb758785ef6e0eb76b137576ff209bfe0a5f7a79abcf46b9bd79d8478ccafd62be752faec15bb74ac57389206ad1787c5b5fc45db2430a67c9d9b74869877bedfeeda75633ef32ba3363ff63307dacf9ce8b1adfa63eeb6aa83f68c799b9cf46b237ab70dc13749b7b6da59eafc476b5af5fc23ada96f264e8b26c53d7fde51de19d765e35c3a14f6e8bb3dda04a83163c68cf191deaf739e5f9cfdbce75ace0d5b1de8712362fa20414284fbec1ea0ca3c742278f8ec16a0d23cf46c1100bdd7d06aca75a1d5d4c35e4012f791747d1cc6ed8caeedd13d604ae71786168a68cfd76b70ace26a57d3d7b5e59da1df7b3e0c69bbe379d7accedb9ce732b8a09382e15f0bff6adc8eea72a1baf0d04991f9cf556e6788cb813e5526d543bf86b8399768c5b10464fbf6ab97fbc4b46657e55c6e73dfaa56e588f8d0bebfbf9b0a197192c47d328a43ab86f40b34e7edb3a30baeed22a5228c5463b47616f80d6d95a0d0f215fb6e261a912270086a29021d0b31b4e1dec603cf0739c75cf78ff4e91dfd7925c2f3d32dbf261234e9fb6d6df3a79022fc4c1f0f3c1f69d073489dd6475cdbabad9b44648323a42c514943c9c9127caad63fc4ed3c9f751eb7f32400e5054f98b284832d463eebdb81e756d285db348206a1af2afaf6cac883d98fe48e91da0069488b745b3902a57d216ddf9f47db31235ddbf0b529d219df7b16c72ba92bfc3856b155627cef3907dd73ce3d27c605accb31cce45986694f622125848f2333e4e7c5ebeeac27a6bd4b663f1784cf2dc7b0f7f82f6c3ae79c73ae5fbb6e1e17cb2792ca7bec8bf764941bd0fe94f1f9625dc0e8f341289f8412c2171f7cefc1d7e43df8b6ac94d141f8e283ef3df886f0e206b41f657410c792e3ecb9a5e7e20b1e6c29bdce8a69ed462f5dfadc5a260501e993aa7079b76d1210a8fc4ce2a2e8638cb12946b5563d3af7dd3c8490a584102e417f10d6f770be39d7418e9cfa07df7b0cd9650ea27b10e6d0aae975cad48d673f10f2cf4d6e10ee7bcf41f79c733c22472d9c16a0fdd5697eec96bc3c7efdba1d373337fba061bff7de49e69d73b0fba173ae5fbbee76fd0573bb2e13c332e7ba1d3733374f71d161dc76caf07b2f7a74f1c5f716bae79c7b6e0bf87e7e207427b343abace91eb718c268f09ad94f9c6e675aceb984f3653fbc4bceb907fdb9d9393d39aec117ea395fcff9db7ee06799fdf24db91feb55f6989b566ededda6b506d2fa205cb7dcbcbcbbbbdbcb3e10024babb05e0921a5b118a79479eef53dd16c0c6b156f1623ad5c63652dbf4a415479e7daaf5210527e89b31bfc3dad55356fabefdf934a26f0f2ab244495779efa550a82c95f8e57442bb3ade207b10c412f2a2a1a826238e71cbb73dc2e0b0d8f86eed72dc6f3319af3fde9e6cc0df503914942658a67671a5e26099516bc67197053f6eca737632fa214c4512582bd73b8ff1e1bc5182174479777447f1bf4b8bd7621cf3dc92de55a9c7a4153c480903f8984e4cb23cfcb6e09c981cad0aae7f2a95af57c6e3a5af53c6e3c9e7711b4e937bd63a48722f76123f800eccf53b41aa1b0c709b9ef232f3dfa2c82b6cf31ad5750d2d72ade24e88226bffd26b15134852e50da6fcd6f86417e99f48bee1bc23e5f96339dd815ad6ed2ce9cceab27b96cdf8a16241d7ef5bf1c47a583870c3cd786b27cb3cba239b48adf4bdae3203dc52da5d5a4aede077b080f4276d86068d153bdb2a2f1d7ade1870d4370c8a157277795bfb211bc83288c97ff1186cb3baf61bbd8cc442bf3b3b20da3f5992a4b9fdb50432d199b987031b60b3db290874949dd6691928d5ef623a7f4b897fdc0b8bd82f4499983d13e2117cca155ce7bdcfee664c6519b9324ee33e357365da9e8e857f84d1af3e52f8741266f89436a25fc3944e7a69a79a4958d7cf043cbddfa8c3eb345cc353fb90ca33c5a945d2ead260d28c8f9fb130db2e849be0cbec46890e5273f35e6160d720edf6542e03b2cc2ebf293cbcda2157b29e58c4b89c968979bd968dcdae6942eb3d1f8663e30ca4fb3bd9e9ed964fcb4617e595bc5decab11cdb7c2cb73b65d926b3cdceb1b694a4d508ed713052e7124221f7687d7fc4119dfa34046ac22da846771b51a8eb6a878af2e43d2db730475d17eaba50dee350960ff5a3f61995a150966328c7accb6472944ce69857cd330bf6cc859ac92cdaab93d7ac6568fd81a86a74c32ecba2fb56d21ae1b3d6b18c315b5e00bf4963a48c79f2bcc46fd21b4afe42ed9b36edfbd267a42722233333d1679082aae5d08f7cdc2c9f9979511ef5c3991ba09bea7b199ff2067e8c8de01d11e533a7d3cce934e33d6e26ce386a0bca66b4d3c96766a2a3661c154f54c8fdc97b5adbea910f62cf7cc67bda1de595c6b5ed1d1c5e871c0ba7198fae398e6d3b47a351860ef55bd335df5ec940d75e0d1dea3fd1fa0351a5a143fd33b4f2a2e8299afc79b6b111d8a66541ae37d16a4d2a634dab183e8ab58afd0813c9b4c4c28d24ddd087faea7e03c91be037c98da52cb647e8b1b32d48c88813723e885efa15b32cb66759c7e71b7daf2cf318a33b4785dcf30d2ef3f5cd60e66e24b9819441cf3087310645fafc32399447d7e2890e598ec2e890fb8b56a2b766acc1b29654b421a68d2b7fa1e86b1fea67307f80df24315478b7aa35367390cbefb6a07d970171ff9c25adbc5e990ed1ca463f524c161f80df243146ff239d3319afd1515e7b4773e932db768ecc4e39153a6adb569d6846b14887dc47af308630d1eb506f16970155a22047dba2433ddbd9084983d8a3c30829d6aabdfe35ad393594c19b3c858dfe45a771b9cdf89543afd8314dc627cdea9b590674b9f337dffef3f9bccfce4658f6c3d3afece75dd7dcae49b52732329fe7d0ab24f355ee2de8e4477eae694ecee12ce43ec86404cfe09ceb44f9a495d67e93d1b3cf499d333947834c7ef2e9b56a3f25919123f24d46fa2afdc8f3c37278de958fbe4a375d05d997d7933b5ad9e8a59bdcb177d1a17e8c06713d61352877d4f97413c570f874ce6e38b13f3a3713539b930399ce3e9d3d875e25e119d81fed9ac3a5f30b95013d671ae4d8a5bbf96a0eea38cb80a6073d5a794e76774df98d4fd17934e62440d32f971bfbdba4abb0618e63cbbc46e699e6d02ae933349b8d09e533338ce21924bddc9bbc4aafd24d34c8f2e99857135b34189d337ea2d60cb531b9f4e9d2d93d9f5c3ae62cf0c951986bdbb64a7399edb54aa3d9361d45eb6679e9f1443323a6678e790e6c041bcdcdb4bd5645b7629c948de01d32cef0cc0ec06079ff4d5aa38b3048cfe337090c975f638a147e20e6b1cfbccabf36a83c027e93da306223cbab7e93d8c8828d2e7f3d3bd2bf67f2d0351bcb2fdfc74c8ed587d4b0480b7b6b5a2ad5a72c88e939cb807ac56edf39fb3b42326d2ffb713a381c345075f176f21572e0709a8b69e68edd6472ed2676931bea6c0b42f991e7bd61eebe119275650064b4f6a31c00b4366ff521316d4755a68c726ef8b5a9f61c898c1c61574bf0cffe9a7c8d3ed441ec47beb7ea80b8976e32bd8f9e39cfea5bf235baaffc9206bd4cc84dbff43ad4b43ea48f3ed4df261a93c941071d6c674763a2f1da5ae29c2693f30a5e670eaf1a0eaf29886a77527a539b163cbac979b81d1d5c7016dc539bab60e335aa1907e26c72e74f09cfd0eeb218c26843fd5d87ba3ea437f96943654099370d6a47f92933b90e00f03adfc6b777f8c837eaee2ed03adbb90b2697b9cc311eae3d33d1761700a042cad13a0459b8aaa0b66e61dbc00ead82cea3356fcb3bea5302c47ef2b8b5bb2dfacd86798fe3cb6db6eaa76ddf51ab163a0bb4ce15689d295ab51cb46a2ad09ac2416bea9d86567f95a6c99969cccc92064d37b9e604cf50f95f36b97cd33af42e16626803d58e79b310431bda107c140dca5cba096393c97483f48cce5635c6cc7d8349f38c561e823f5da336964797cec3ed5087ae83dba18b0506af4ebda65ef0aae1380c948f1c67ce17e83a484d1e9d85f73ef393a43184d1ea10fccca3631e3320f6a08c567e93a31c6642b067e7a3cc21d3fa03d19b1ce558d33af4dee4275a5bcb474fa5aaf670691d826ff2e8efa8574ef00cfb98bfec077b489b69d3a155d0ad6d03ad823e371e6db6ea7a499f13bc235266d242df80692f78669a008109223c63e0ec93d17547d1c5076e9c4009a3244cf8d4d43fd1a4a0c6d09145052888a20a33ce70814fa429bf49fd4dcaedf430993f5e0d3b299733e50a3392969220e1e2e3761640460a84a0a404174008c3074ae18887a6875268e2e1f5508c87d079bc293b7b63293654722d4eddae34fcaeaca177a6cffa90de995f9ebccc6f921a52889c14b5612589222e8aa0f0d9217cf4d7abd3c72f603e7ec4f2d1273a8f1a6c30250933c002052794f089b412e179489da6bed07c5886ce61771b8f563d1ff6a52db0a1a57e93be6879d36fd2972a29d9fdc5b51d052c5f677d5fa4ccc9e43fa0c693af530d26efb9ee77aeff837ce0efe19c73ef64fab55d45ab338d34bcb80c872ef2b1fcda2c26993e96b757e7b355bc39b16805e2a1d7e88eb62085c61f9de96c15070df53f5ac4bd739a7f599000bca34e4430bd331c5e515151110e5c5484c326f94cba2ee725a571c4bc8dad4ebbdbd228f212e6cfe8f2f5fd7a91c274def137c90b14a670b591d6bf49608079f63df148b11751befd0687b5b9db6a87f5e6e23b085f31272ed6a2614d30da02bc1780af44f0c83fafd60d6b06dcde061d165e7c262dda2f8e9e57eb5d007ebf38f209c0f3603076769278fe9c33211cc4afdb445f2496cb6db77a04872ef2996e6d44f0f8c822efa1db0061260f61257a9b48870450be7df6267d4ff7740b6d68f3578989265498508282240f37873584b01bbed79bf3b83d6f7f3bebc3ce79f7fce8bdb15125fae8be198ced98aef78a9dfaa302e609f0abb44415e8bd63f269c56d2143ffdc62376daf73a26f2684c8d0d775225825e235b2a0dc13da3efb72179749cc9b1c73a5a32e3f805fa5232641989f8518dad086eafc94a76c3c87abe02ca0501be6d9d6ecdad1d16bbec25191125ede8fde292991e49ddfe0bccb809cbc31cd5119b5c1289019198af2f6cc7b1caacef8ba036c9c06436d32bed903f86d502e436308a3a13cf387c5ec06cc35476d31bb21f39691a13634aed16a2364c6657c7fa8dad05020ed32aff9fed0b7cf78947198fde0b7716dab9a633486309a4689602fe3ed362e43836c9c8606ade01a11ec35efada22811ec6de8b62ae5d90abecec256dfabe02e0bc2ae79674056a0af5529ead0355a73d06d950afe5aa542df80e3061c38b67a2403926d9c05a99a673a3ccafd0db55d738f83d612fce597108da268853ee4feaaaf55590dad71e89d01e9aff16baba1fb7a857906b7ccb3ccad6d88b3a1f79703597eb97b429b6e13845f737ecd9b06614c6d4cafb48494d7f1abb484d107611910d36bbe9910cc4d6eda2e6d43b19b9494d0f27b41acb73af4334ee3db3b373454c667b66d950cadf3b3cc6fbcc765dbc97bab44b08f8eda8af06bfe3220fd9a9bdc442be6daa56d28ce84b4b6f577e643a3955f739477c53e7319baadcafab3ad662e51ae01d9569dbc29d6aa93f7348a0661ce5e334a047b8c9ab49f43bfeeb21fc844b02fc24070fc0a9d21011f7fa694c4956f8f010dadbd57d387e79709732743efcced7db63941b98572ed27c560f4f7fe59d4c6724831985974161142fdf493bba52c5cdc972223a42651a654b9a2a5cb0303915ca80d0994cc89701f282f6641a23f5a29c6d90d3532d3341f80f62d07e27ffd2ffb39d1dd1df5f2f6cc3b0b5282bf32779ed197058954e2143a52f4d22f8bcecbb12c88a3d2aa8c62dbfecc525e40af12b3208f3a2aad2ac232892ddf186737a0a4b3f49e76d6e6acac8ef3ebf1f23e787afe5c6e575a55827f8fbaa5d57142d05c92a3e2aaf46ae111d6d38a2424a2fc2a19251945e9e9cb72475adda41eafe147f4f79ca757267f9cf953692919fed1b09d32e8aa56b167db62d384032dcb3bdb56599656f34cf432da44baf53d2fb9a5ab5e4b7d5cdb691c0009274c3082092db82863c370c2890e9830e20d13b0c0cb13963ca1021578310331d4f0a9feec45454542f868bd5a287c048ad8620b31626c89420b7c2c2025892bb43821086920f9583963096f7061841645f8ccdfcc070ce8c1112b408a421419d0e0093bc611c4d8620c2fbca80005d785b31dc1f1cb0bce06e57294bfebf2cb19e55186e070f4b2f852df93f7047a0fd087e08a79bd823df07b498cde93ee25e915caa13f23a1dd990fe5d7f6aaa4521a4442eb2b4b1e7709c9e56c8fdde1cdb97cad152383e4eaa157a8cb695c887a3fec55fbdc305a100fa9573ec143c84bfe6d32fb110ae27fb9f48b378bfaa0288aa238adbafc7218c68db9d3aece6561d706a6872125b47660902eeabc37fe29a3b50ac9431aa7a10e8c13f390deeb25d84bbdd44b484e8c7a960b3dc498fd6c7e31ea594f9e14a7d2de93f7049502cd7286d8aeb63b4350cecfd951db1b43dbb7dcfd653f9b0fe1194c0e1d02dbe339f408ec4a3ac4f13a7760f0aaf5c0e1bb3b7273d871b8dcdcaa7807bbe5c2b3c3b0ede6e0a09efdf5b35b5bbddc84a271f62bcaf9020c2f784fbfb90599dc077e6b4a46a150285a51ee4126bad4889374937ee272bc24265ae7bfb07552aba0c3b03595ce817ec9b9c4628284f49ebc27bda20efd59d13b380efd25bd2a6fe965f1a8bc2e20f59e7eb35528fef7e43d79563c4a0443b910ea99deb8101a675a847da0547023bd7a8ea4871fbd42d1ea35f48ac6a15f56d0b68afa10bf89c63086d6d3ef89db716028adf32dec59d12af8a23c7c2e8966e32d3800dc795d2f3df4ea597ed42b00387446ead5ccd8a805aface4211294c730860684a241281fe2a7f1a0b7e421b91d076665c2965fd3d02a084f26a75b67d12ae8385b4b7139f0c9a7bcf6c64918c9c645ad82eec040495dd82abf0b26da43ab20bb3b0b6ee33d0c01d12b77e8467ac5535bc17757f09edeb667c58bd22ae82febc17a28eaf43d6915f4161c005edf93877e890008980939397bcc829cbc875e650e7d088899053939fb66428f56cc2fef712e6cfb00d826d62ae8379b131cb7d958d8a0afb0f9f0525b8ead622a6cd0716c3ec89a6d889f86d6d02ae843e00c95a129b78343b7739e437aca5ed560ab541d5d055de800bc11fdba16ebc1ede0f0e8d8b5028e8d37c7798dcc3ba7d97673aaf5ce05c1882d8cf448493e3a2fe9d572f92018713485ca476c7bb54f466b157cecd163e8a249dac3d0c59bd4a80faefcb21feb83e85f0f43bd92f1e85cc6ee682e277aea0c4fe363988fcb2e27fa9c9aa6b15ffca819cd655cc988c94bd7aef722e32ccdc76a6edee1bc8b6e9a6bc2f848520875cc689a8ce632b408ff0cad3e24447ae741da7baf0b84b2651c0f199722458a944bf54e0a26c5edec7292940be3a3a31d7857c711c1791b217ef622efa50ba15e6aae8310e91a2dc2cf7403ad8a98dbeaeb55658f3bb42ac62368553531998da7b42a3a0fb7c3492e27baa752da745290d06490712d7a5bd12b192ae93a77e9d5f428b9f04aa34f4baa89d64b76a257192c476aa38fd1af45a2c2a48b277be5a3639a46b7554e864bcacd62408256dfbba87484155c7ab54a4678f965c2c7553aa249e42e1ffd9a98e32dbd9a9657763eb31f76d7327cae97fd5ddbd259cbf6d88fde487ae5bc07dfdd8cb76d158f5631bd4036374cca9c7c954e8014bdb2169692658a97242abd8a95957cf4e8ac257a54d22b9447ef71359b0ffc958f7a703b53f377d1759017b0b6b70d89be3891067594ac01f0c5e5d76a35b55be5c7f6e8bf9c0810bd727e3990dd49b99ccb2f2b2a31fef2ddcbeb96f197efbc2ede4e90e4b7cbaf5292257f7136b3f730a6af55d22f88c9adcaf02c0374dc70613cd900893ffdfda4f5c8cb565d9894a9514a92e42bcf5f0e69e8d5e53d3fdcab6c0e3b462b3625ad35da4e7a35713d1331fdd1aacbb7a5dbd0ab489f5fbed9cf947e7151fcc1b57d2d47418909900441882fce4a5ae22d9dd33169d29ae22e8e085a0c6134cef295e1b3169702cd579079cedf436687d98f16d7298db570612fef8cd7d22b29c4f2a7654b10cb9f4f97b46eabb05649eabc44b9dc11bf9e5ae179d6d41b6c7776e573d7198423927e837044941fc06f108e40924d8ab5aa479d29c61cb6ea6d8f873b027b15da2a1501e64dee6fb2fc5a5ac1f22a6379b59e9d7787c7e558ce9cfd989cf9f26e667fdf19f338cd62bfb665301e9eedd16ff90576e89573cb13b03bd3e558ae290161ad5c966599361d1b8f5659aa5659ecd0799c855d6e4d8c5a97bb0cc89f4da6cb994d2eb381e84d6cdadee258b009d7fadcc24b9488b8f2970f5a85282ec7af12114f3c685222c65f25224e2067d698a733aca70493ccafd21057f8521a828af5ab34040e18c37e9586b802bb4cd694d9e939b19ac000d5f383e88a1e68a90116272df8c2022017480186081d3891c5116b684d8aa066a43c75333dae00ca11942aaae0c2074a65bc210528928e7014250a94004c71e585244cca012f86a012068a1329de58228021224b193a10828c27a638c2a658225386a5c2afd2104873052efceea43687777adcbe907a1e82ed58a905452ff3abc482a3ef301ab33b4e95b4a755ec4478fe47abb886564d21c4497a8fdc7e4829a5af1a2f5f2ab5e08997524ae7412b119e2c29b8f170d75b0aa773d859c7b3a1f5fc703b497c360901bf4a48ccf8177e9590242dc125f822cb57c50c72c083222dbc3db6077bf63e9affb18d0f7ef67e7e3868b1e76a32f0f0f88165880c327c37501b47b4c184d6c60abc8d29546e494dab1ac974f9f82b7c7e3989be3ecf07670bd02e73be07f0ed302c02daa7a061adda2a364487ecad6a4f7595f6ba5c2ed0abba5436a9a9705939a030536cce423a83ed1c6cce7651abd81f11d7e2ccce7e3493c35cf643c343034a8b15e3c97e68f8d12a77fda676274368fd76b13ace2b4cadb033987cdd667b58be7eb33d26e6eb34382fe99d1f2ec7f90e192a0cbf94a785dd495569f28ea90c8d0a3e64c88f21360c719d04478e4e12a50c0ac551a60a3333ded33fdcce1c32e39776a235dee382579e5a809c7fbe01c02bcfd082d7f86fbe8d57ecdd57d81d4ef9f3d4f660c11fad4468f8e73cadea3982f6bcf23c0d3d5dac748156171e001b0556c7790b5b057887f39bed02ad726eb3e9d02ae7be6da0739e054a64759ca7b61c56c779bfa1a5fc616992da7889557087e3bdb94af9f3dd98fdbcf97ce47670dcad40e9e6ed717394a7b66d550eef71a76d1f670362e388cdd97f6103628389cdd9a71b101b2bd89cfd6bdb362036a6d89cfdde726cb58b168d77aec28663abf36b361eb7a3b91c32b47d49b37117331b2f653f3294abb4ca798c5c6cce82b139fb6c90c18619dac65d7095311a3f1fe78cc4a555cbd3c356b1393b83cdd96723076cf040c7f35f8d35865dfea8966d36419e4b9734e8e46f9fbbb4ca5d272a04f43e88fb489d2471f6c25c2eb0815e11e955dd1c74d8a1875ed5adf2230c0fc27011068c3064bcf9bb7f410cc2b73de4bbdcf8e35b959091ef5d9574ea8edc0b341dffccd89c75c254430bd3c6e6ecee0e101baa5dc99fa7e37f6c68954bb95495c55265b1fc56713bebf39591b0cf58aab82ad2abe4e0c6b8339739b51cb4e690ee3d7d49376dbc6e75a4cfedad8e849b23dddd38c1f1d94b5781e7aca4557cd42ae7437eb43045abcc65bb1c7de9158be955e5e1521d0a927ce5e1e9d57320369cb039abc6b2d10613a0364cb039eb1cf6510b4ddbb89e5fd98f6381566bfef903a2576e6c8f7ee75d44c13860092fbf4a4d7cf9da47ff9cbd3c6729cfdf7b4fd352ee2a95251fd255897ea61007ef6521e607f0ab848517bf59560bcc7e782f22ded2aa7d2f09316ac9210971cb8b714b7c0fc2ed714ac3b4d5699f4a68ed1ea7b8e21839a5bc691516545e5a2fad089f6b2a138537c405db3d19a58c5dc51427449104a525326408b810e2065b2cd1051094e023b148f25262b18297392f198a97524a29a50c83081b84592285155f6a0082247a20c5921c2445e146962868bcb479c94b5e4a29a594329e21e60a2d5c86c08436d260720327d050e2096c349112a50632517e808357705303144ca1066110610506085082124ee0042e59caf8220a2a8a1439580530fc2a41f1028a14227e3a934f5f7fef99707093bbf7de7bd3caf965da36939b7068958976d5640eb40529769d87f48a779e4f7f91367560cfcb1b2e4a1028282df0d94b784da8e127ae4832aec962680f2505568cf9995f252bd08859b055831bd3b25b22614d9c183d3f9cac905446cabb25129a147c8ba1fad14d67770c94be3b065d7c77774be9de10c3096574f9f679e5756069a1c407446630c5922f52e02506432011e9808924cc980113905842ccaede733d437bddddf581bf477b5d1eea557b5d7fbb4e6b5d9f3e9b8bd615c1f3827841bcdab3fd7910d6aa21b49b89c1f71acc43f24c80060fd860620858b814214a0a9ce085173f6032460d6ef06587dde20b337890031c082107437002290b28784102064c30e18d2bbe5c618224216e52fec2125a24810d2e48980115221d5c018520d8a0c88d1810c1c739c74dbc73ee09c14a888ea050c414530c41e9095cf82c608a0a6630e60c235851811539b0628a1533487a275803670d9e171d6ce86278228c2a2e6092048b0ca8508a82125f88e901184d54e102ab8a2b26330166cca90a245260a828c3860a314e0c3041ace0091a544cd1e249156708c1c5142868946184122aaaa0a162072f300ac000008c0f8250c446942c982021091d74b104941f30a9828a221a2a98482d01c6881384d962861b6724c151430c124db008a186133040020a064f98e12330397e95ac586285124fc8852f6e35f4fc68154f7949359c84f31eb7fd70ce3907832bef9460600477aee2418407829950be85c254aa02cbd3fc2a55d103231858f14407d3c9c8e3ad725eb4f7ab5485d2a762153a40f2b6a6beb52aa6cc6e9cde793e3ae2af521535706b1c71841250345184162c2750824889194cb060091a4d30410d9ff5dd41034270021424a31a2c2125891e0cb1040d359660128509900b284b74210412682c01092fae8862892d88b04418644829a54fe9fba4ef0ca349dfe70ea9849b0429a42471c2105ee00618444e8030852fba68512529081fe9cb46916a60b4fdaa4d9fde010fcf96397978c02182e394f01e0f38ed869824929208638c0bf888368c9660828b215a90440b36428187670a169e2481c30918ed49122c5cb4314e3e9071128613a389148b09578d5e9f57e8627cd67b5b43d8adb91c673db7f57439bd051571ef4404d271fc6638bca222ed37c3818bb6d3cbec8f30516c37c414d30d9c113f44a0c00a162f579a78411a45558a48c30d2b6e1086932b2e27377046f098e186587044943158a00412a4198861a3064140a18283379c409139a1e29dc00356a0188214a0d04418242b461851b09841138cb0c60e9c1c65499c1461a9f0c4d46223149e7b4f08ca0cca60c1115eece00844574c610430549081c61626f0423382008a48b97b6a84f9024b124758c2091e407841982f662cb1e4cb14a88431113c6640aa3ddf28fffc29bda006ff9efb7395e3d1abe53163461cbf4a2e40021b7ec9d6606ef802262e88c16ff684e75e7d38eee60927f0793ecf7d7bf07b5d76b8e83d5a89e0bc4bc2832bfc2a3501e3b15fa5263f7829e0217c9e78f18518147899c20d315878c20c26b488c184164cf4a05d193cae0c9c1b29696cc1c9e6d434d50e0f8deed0aaf6ccd76d400cfdbeeb15652642d0b41cb569476bbf71de5a7a87975c4ebb739cc3535eb515bca6de99dcf90adb6e4e6adbcdc9b1ede6ccecc7cd784ff352af4e57b0f42a4baff855f076ae32e32d9dade81d1672789d3b29b79c858dc16cecc5e5748a6ee75c39e8768e0adb0c0d8d77f643e317c430cc81f6315a82288fb9bf83d8569dd7380edf0cc8398a088563c66bb6da4b3f938566f356f50cbd612f6ea7873a358d662737653f2794a3b618dcd0328799c6eef8f77f52adeaccf939cb3cb381e833c6a2c51f360c710e730ec37068550f6955fb8f0dadea1faddaa1575bd54ccc84b3f8a6f237df3f7a052105da773a6ca05790da7001ada6fd66a3f0f238fa244493264aa450c25fa440f2ab14c59787de6d032949ccae080db85c1156f31946081d663fa09ccc82e5fcd3dae474966539d1cfadba0eaf3db4b6e9d19d4bf8913e277887cbf264f6e39cdd99712d4e3531f986d612b654b374b384a3e37d11039140a35fa8e4d75f97764b6ec702638159b2c02c596096962c312625262313926f77d012d32b0bdea4264c4d8793fae8d7a2a552eef0e869e1e618859677ee4530b9e063c1b9405b8ad202e372da4d459a964a39b4c0b89de7dc3d95d2b4b7e589795feaebf2ed3103f28f6f4baf584b4d0c4dd024cd6cde511ffd9bf4d5dd9256fddb6961c1b540abcf02b3a457f5d5a784ca73afb4b4d7b08351942b0c6649526f4962c592fb82f4a66c8f7e2039ef64afdeaf0fc1a157f22d151a744025ad729cd40d13abd35ee5af63bd03917606b8c4ee685f67787445abda3354527383f350ca068f3638a555ed53ae9c5f49c12378d42b777f0d9574432bbac22635700990bfa34aa4731618667e51b2e66e07cea969a994bb4af531c65086e6a443af3013f2dcd904e18fce1f6990f34887f89f4b711b7cbc0d4f26262b645ebe7dc97b59e012a8e4b3382af039e87cda5d527b63aad88e4aabe63fea9288a055d6d2bebb53dbede172a4dc746e556e7787f375e7cccf87ff7b74332f91052cd002706d5bb162c58a152b56ac58b162c58a152b56ac58b162c58a152b56ac58b162c58a152b56ac58b1a22d7d4bc480c172258b28eec81975e152854a93665224260b16295928e1232f4456c74a173f56c78a0a6775ac2cdcc165131d62bb3dde745f68ecd32fcb995c0072c10513673eb417622fef2fc45a7ef20c1dc48599fcca223cc187c2767b5c6eb3dcb43d13ad9c0981fe7c6e0cf8cd8240affd400cf8493536b57ba71ffa8bc49fbe9106d18e10dd264874e8a817dd6a1ef373cc5b323af4b9a15e6efb1008f54492190755c4409a3de9aeaa3e214616beeb2987524feb933e34a304528220d055112349674044ff1cadfdaefba58cd0dd0d90be29d90901e415151515fda3bc2a48826fdf8ab40f3ba00fa5fe9f916f20efdbaf167263c400e9a267ea43f4a1f9bc3ab08f00791f5d91fec7fb4ecbaf578fca200aefbde72ee77977d12aea9f5777ce39c743461965aba1557f29351d382aad2615638c91470bb1842b0d217c8b86f6de7bcff97dd1aaf6ef794dea41087fc031c69f4e43bb711c55744ceb9564961cb14c399fc965741924bdbd4a6ad374ce39e79c734ecbb29c47efa079c02d43abfe1042c751cdd8113a61e905f0ab0445932440a1640997bf7a9bdd89090ab894a0e1bc7dc204ccd0ea7ca7a530009f13b4aa3d849ee276cf4b7069cd45aba86fd4192f5c40fbdc5b34f62d805be7db041c47816b71366b2868afa79fbf2964054f1ee757e90930906118cd06c88334087a7b85d44912f7898e637bd9201c51d1b8b98a6b71aacf566df46b1d12b4555ad2648911b4555a82c52d59bad61d415ba5157079a51568f9f7949e00f3cf6b7a27fb592076ac550b7d715e4371ad6a5550ce54bdc174541cdee1a872386d8e73816cbb711db02d75b3816bab49ed60dab41a22d6864d6ce8dd53415ff888327e485829bf69d52649e942440aba48a2010a7e9882491b63c630210963b48007590b965e9042164e0c2759f6abf48413e213aeafa9162cb98eac03fc2a3d21e5657e959e7002c6a5f40412cf12ad6a3d4ebb7c2ec66a7ad537cd6c315b966f865121f9d6b361df7f4c37883d083e91e479fc2a2161f98861980349c79ce89d9bee32b9cd8a58abd665c45ad5ad7daff3708e6235a9762e68150487705db365e421850683f059a41decdfa65ccef3356d9346f32b62bc450752e3ed4deb8cd738d1f734529597e1572905477f494caa80c35570a2b764f39c524a69796f6eab40bce53dceda30376d974f15b66d95cae435db7bcd6736d369838eda4e7e3186d33bda73ef95a669af693387e6241b172d6975b1bcf4e73db89d973ddf006adb772e529b20d2a303c9deea33f2ecda56e76b51c637e3d346f4bc0df5cbd076ae6d15887738ad7aaee570815e497faec30676e8d5c99fb7eab9353793e654a0551e3d437aa381cac053c30f8ef4ddc4e8444198da00a1759d2846a14d8774b60a6ed569d84486fffb811b49bebd66bfdc7c2f0943e3ce59dfb1e50a9526b278d4af929227bf33ccccccee21d3d739cf3951351ef6e73cb494521360feb9bb9df55182c2cbe3bc28525084daeda2ca9326488ab46499220549c9172f4b5d58f12409922d5a92a62c4102f3e5cad2f3d0d1052a61e79d4b5d58f1e4257948b668499ae2963824305fae2c45b1a28d3a09972d5492788918308b859735a6ae89b6aba3724d6855865f7f19900c39bfae090d3ef7dc73271eabb37e715cd7129e70706dcc989ba2ddbee79e6935c2ceb9d3b2936bfae2549e97bfedf2b9559c9f3e634d6933f3daff7c77860df00e11fc70cc67fca271d7449b286fbfdc14daf48bb9b19ece3d1b20f0b9fb5d9de93dfd339df0f29a6b37432b103fa7e41970788793a135e52ab6693edd8e0a8767a88177b89a72f32bcebb23afd11d56c71169957395f31a7886dd1d26b7fc94396bdb39b6f5bb2bda73dbaaedd5452bf6cfb45c5ab54e04c72688f6d1b58f44321c97de9196e7de32f41ed273e21d3d271ed24362c142081f8c51c61821840b21dc1a42716d39b4eaf2665704c4336740e443bf7663a35609c9e7c7bebeccdc46bbbdf5b7bd34aec55192a48ac4da4f34469fb1bc1d7b8f422b66414cff28cc805a9bd9522eb36936bc74d476f26c931bb655368ace378776a32b0acd44b147e343d646e33221d7f7ab9b65b334841eb31fa8c4df5fa8c69a085aad99ab4a047b1d0f991949aba97729976d15a3e1596e10e9d3270d92ecfdbc37488960ff9a071a3e88bd1d1457afb0d76168d2b5ce61c76a267dcf12923f8f7c943e6caba69c2ee9f20ee96f4bf2efb9343931bd0d7b0784a40853ae6537e59d17b8004c184a90020b13f8d4d4030123096d7c4185142b45f8b41adfdd59fc26c5b4e2d4d0ab7ef61f36f4aae27cf360c28a6f7fa96622a939a8f4ab7505a29c75ca2912490200005314002030180e898583e1804c1747f914000ba9bc4a5e228ac324c8510819630c218610002300200042184d0ca049abf97af5c612c2b01eb764022b6d667e7c3619868b68857fdbd84b25634489a97725ac7d1a6b7ac81a2e2f836c96e4694ccc25cc8d0f7d9a5e417697828b132304267ddce67302e23c95ea3877b91a40c4813949ee1345c9b119a37e9f5a9059b248b3b1ba841b6b478a1a12616e105dac42a20ec9d82249a5b17eb18651fdee2de5099964cfd6c65cb9bb212f87e0f0419249b33687e63c6f5b3ae4c4faa1914926bbc2918a26e39c656217be9be3c030e73f53c810ceee29a86626bb7aab6d04f536c0a13ee05483451518b24f295e13ab4301ea0b01b4b16aa392b8d8b7d19e62306c01031bf4c0e9b12841599e9650a68ac3cafa4498b8c416761deb6f1b7d19df983026bea39c0cb15d22c115953c23e1d87cae5a03517984fcfa2be7967307bf823c6bc3b701502ef159ea2373c8589d40fb0ff0b23906b313ad04a8906ac117903e9e8a01ed8e872bf0e302d8187e78b527db4611d19e51ca89bf7c1d5561d324c03e5776e18f10adc02ffee92109eb9ca4a14ca43d95c1d4e307fd36a390573a6a9e792d8ace868606b3410a506c569588bdd2122175afad077b8343e30961ecf0e70e90628e4e665b425cbbea7088eb3003256e91d556a86c9cebf4a7f0cf2d03514745a122d87d085834a363f8b4e81d03eb7ab86ac6fd02a69f9e42f2c5f3a280262d1a2dadabb9a1d41a858f664a8dee8005cdb83e04a3cf235eb80b8a371af9e3c68be086d48138a0395149a820f3b35b8c4e7aca15ecd2a2cf8bd962bb1f42fe1c41106a2bdf528271dd98404e31c489c2a4105ad26d9764726cdd06bbad1f7e2d9ead1943756042b7f63d8b125a5e6296c35feb5a00ab3d70ed428a11b928f1e3bab49f44394067e1b9e982cf8393081c86c47674de65f9b8280a6b4a8b65b3dd76b6ce7f359296c4ade481cf37d3a2fd7ce9a1aac52aaa1a3091ae5109d2e7894db85219f4eac89be5a175680712b4ee7337fd831815d78385fba90b8a9667dd3a88d159b64ab6ce49b13bdc88dbc148734e49df7cebfd802e3a07f478370d3b96be8ad043f56727d9671635747e23689c2a8af5710f68fa58f4030e275c8798dcaa12d5c40f48668a4b0ded41d6032a8e21a89b4058b901d7496bacbd72d3eddba6e1048bdbde1a256a2f6e8073758df76750349677506f0ae30b5d88833e721bfdf021cd5d9197ccc1f51560121ee16a6f9d4614c2f62f65b92ce1f59e64221bcef8db3e50724d2a98002befd9af863f5693ad0d40d65ddf64c46d8ce9e6e366be1830ef1622ec3a6db1d5b2d876998a48bbf1293dc67db35f48a93b0c3d37313d423a6c15c9f8b861435f72221dbdcf6bbaf53ebd911cd1c26da1837b41cbb055e70d2c374fb293173c97ae1e04f28b32bbe0105cc1bcb6bd98885a7e113001cea814f3964a8496122644315de32622a77c6de4b1e56d6633de030f64ec2ff4a17f029f990e6453b9a9a4944bf566eff8ca43805e17d743f00045e7b0a2d019a87d424345966714c9775f32ae5c136959ae1502489620ef0e684c5d9d9e80e7e52e13d1f2490354d8cf8f22321334f1133d637ed95ef101d44da8e8d6d60b9dc3de477593cb5c79bc8970c2786cd9636e3676156c859a1e7010cab8b8e1d02f6004a6021bf194681f173b868fdd684f4f9f44ac1e2f371ca88558e57ba55e37ca900cae38d1b8fdcdf53b9c47af295061c4e36485fe37c481a8cbf7bfa0bf610027821cd72f393ff46df41afab19b670afb0a41eb139c7d9389b25d88558069655df4fadc72fd5064d3e800a3983f5579bbfeb422b65c94c3ab9bdf0191b450c0c0f64b638d3028ed1a2e115267cf8b888f53f78529dc6419fd7cb8efb4c499b84ef8b6d1dedb44634ee20e1d3c65353837eceeed13c13842c4b511df5f83ed6d8d96c1dcd68e7e854ebf8a2df94f041b03a6afd8433e664c6b0448cb2a62a300ef8eeb4c3b94af4404bf3cc0ab58820bbb0ecd804e3cb71d2e682f0f58c0e6ade5815d03b6331d4b33681a6ca677d21e610856360895085be9851bc1e4ec7a0835de7c53838681a802810954f956fe03d4818c12a5e429ae4d23dad677abf0243811cb64658f01eb51b4195c5199cf78bc030fb8b8e6ba4964d451dc3da1674d31881e6435423b0a04309efb0968a85a79af0fd7d7f8570bb61995ab6bbc4b71fbb0059fb58d0f982a85f4bf72d03aa4bc4c7d260311e2a0b62907bb69492206c356ee2eb2fd44acb1254917a8796209ede893f31aaa48289caf04e094c46f6a64dcb444a56299b016d475a70ac199e6132e773f667c17aff6a9383d214fe348cba0f41edb2cfa2dd665ba3bd7877688e8a4d1a3c7ceefdb9e9bf67713f7e811e5fae5c2bfa57cfbc4619cef8668d771f8631d26f5ad484c3f8cc8124093d6f421e2fed0032139ddd4b5559dd3b691b0d60cbeab700b989cdc012286e583cbe307bd72d09b3470a13ec0f68564756e9c58d498c7302974bcc5743a2072390cacbe8d1fb302ee1e4a4f8f89f54c224158e14590321d3bbed136a8bfd44eb2b75403ead59a63f93e101b57cce4dbdb34f7297666e58e002dd3420387706a8a2e4fe2b81e7634c665c9e06c2fd36632d95622a8d71995f29fdb33d0a24a69ee0638307f82b2493347b3875fee109560a951251231ac4023b3e13b70a341bfca879a7f64f3faa03401ab1763ce2def4300d57b605d32847c021b3860dc97a89b1313dcd8557d4223d497014a44ab8ad6bf9cde8df7f800c3500804c38e1c0f147fb9a12219a95ced02175d14fff953b22a544c519709b5f10bfa2caab6a9989ac9db153b9b43dd03ec682157391d8b2cd6e0d69eedb6c3214a0bde95a72d844bf07bc7402b77920af9e260cf9743d826dece20183176e9cf569565047a7243f8c690de24758be047c538f99410da220e0ea0a5eb484aae68a8f18d8fc2d79846be5ce9757777e08b25ba99bbc0111a050ea6b15359989af14a5d0fad6ba0e6e4d2fc69edc7c2673a96b6ba0eaf8cf92d206e0e580d9d0314f71d9e9e6d3c84a2eddba40834ae77127010bd37d9c0ad063d69b29af902faddcd377aa47f5ac6775136e88602060f37c645481cac1bac1157d0fcf89bc5b5dbd6619f5e6470a4647505154b9fdd542e1f4ce9002413b0aa5a0fd0372887df51c1018f081aa9b2cd2ab89c40bb412bb91833a913adb5ffe828e9f693942442f016ff35bea11bf3cf0d45ba6c93d45576fb6a29471c281ef4415bec9b8b42cb7e1b9635c83fd5abdbcfa0ba51b01ce7d08404687c2d989dec5c84d30874d2d90f4f80f91131cf6d4a00513ad2d88513b140e5674525919a84248d621fdb93a45e893a21071729b78ca1549b99d0997a6c8c9aca26ad8a66aa16022f9a0e83779f27bb88e3a780e49f496ee20230ec777c0442eed287fe5e0e6193f436e883ed409d5b8fbf6e498577ddfd9b8b1dec700d9af3e31f8fb10bc21fdbe0baea00bbc36a68c820dbd9d2e165fc407478044c8b8b27edac50c7ea979c2f3b4f68446b4cab8be4c702017a1ba06f38e2044918964f8ecc9a3ce0afd7a195b5c09fa950f01b94522c7488271d802fda3b7a84f1daa087e28ba7a9a9982f456c49265b3628f52b0b8085fbfb917ec0a619eed67f2d3048d4cfae250101377b47d6ae1093c62398bd162621e84e97cbb4ecf1b09eb64a0ecce391e24adeb86a5156e37b0edb489c3e427bf56acc58dbb5b2cb0f8a95b2d274b4dde7bfc062ef9a1cd735e6ae04cf94eef890ba3404daad0014d61bb786fa06c175be5ec25793e3cddee2d1a288da41287b4fec19eecbd48a69ae02731a1447ed89dda00cc846ed79ec539287ea700c5118c9a8351c2406fcd3e6e58bda4f64e2184e1efc296dc0b7d627a572a5f250affb1913a48710e4753e9892d03b8883dedfcf80c32f27162c43041142d977dc029f871d8f2959ab139cd15fb9ce50c902c3e2f59421deac86fb32b8a9a447d8c8bc636f936f3fb4a21b70f2c48e23a1d381be62a4e8a5a8e047d31d524d6c8265a06fe195ae64781c06822121e980b053130536dbdd1c80344b8f2294dfc3afd778d0450f3da89fd9c5782eaeb5a107fd438aefeb740c598b6539ec256b092962886a75871c892feaebc013e48e29a55a77cc3751999171af61636fe763712293934f26ee4d9a9914dfda3646e3dbfa64ec17755286a96d517e26a3ba845289bc1f9f417b0062919ff2dba1c3d3e17fcc5ea85e341630fe0bab98644a4c8ee00902ba1458268353ad984b946bdc40c833b3d64788b605fabb2509d3689ad78ad9a7e6e199202345df4cc0b5f801d9cc32a565855980a54c55cbd27e3ec0f944f1e58bac1788fc41df4e3e773afcc2321b3d2877e632b3d302c6fb80bcf4c5710dd11ee64f3098809f4ed8405cd2f939b61594374628bffe0895e670097b19321ba31abd2f30b50302b0d09e2d84411ddb98cd3b451826a62d92dc6b70d3d5b0dbed5ed64f10bb295ca79e3b501a6c2c93e997928f8d33f63b7286af5db1421d1a3b497064789774ea34a1752440b4d05b007d03ec3a2bfd0fe8287f683b941d7114586744537e62f01bdd67003df3aafe0d93da6c547dc0c0fb9debf25796002e832dc85fc0b8eeedff0dd1cd5bd0ead01999870f13dde9baacd979e5ee55a5021f0102900a2f5674d43081c17f96e72205ca49d7d284a2c074912e9075ec9f83f3110e783d036192045095560b113e67c12536c5e953e636613f9b81eeebff619a581e64811fcaa372e8e0dde560456f6a1662de8193933552a0a01abaac5fcb15d19dc28f496cf161e6f44867b7fd361998645a1f66da1275cf59796d86e207610d992bfde41a4288c9d2459719e10ed661343002da57a0922a7134abf4b8e26f60adc6b33d724a841288f1c194ed597a2167b69c7edf9cfb8958c1f8d51828e4843866ee164ec4da0505761d1e9e633803d56e9f110a6844241bf45153f8608a64f0dd4a866890e05ad6cd081e07e83d8883715303ed615263b0e1da2cd5082f25ee5d2e0237ddb29129689ec05481287ef587b284c96ce605b9df343c827d804179fc43a1748505ce95634f114e737f25be085277a6a5d6465b458d401bb12f3ec2f1f633f1294e25b3536c1e206c20192e42fd50bba13ddaf50a7c7edea68e55ad2929ee28c03fb85ea99061cb07c857ffce4c8206c0684041d7ae12aa25732cb1f6b6be8a9a7a39cbeebcebe987c8609b7cb69c116d014e37a599d476dfb99ec8a3f65033efbd8c03730edfa83f4df94769ae38a8f0c62b14024f5de1982375571987bb86cee6f1570d57881cbb8124c79a0c7f7ff15e1c1ef92895389211c767f9073dc4c43c8de740b1b838d50f2c8e059117b28c27ebc8626ca5692c71943bf91630ec0e1bf820390224c29e1ed32a81de74acb25ff022805994fc803f595d0f041c905def7f1505213e150bf908f7bbb270dde22093ca1699cc1859d8fb98354f94047e9aac5b556bd226b6e46b0dcd8452d7b058982e29ec43f59f26b5ef39a5835836a1a867d2d8c6fd255facfbbc5227663f6fc98b391f56f262dce7953eb1fba1ed7dff312527f63f5ec989751fb664c5facf2935b1fff14a4e2ca5b42b464b3f74d300f45c21f5b00a867683dd42304c3e92ec0533cb606b993db06f080dcbb317cc2c836d860e3b8557a4a48d93ddfd7708b668080103e5669b0b4bcc1cc8b69aec79cf83bf4b4260422bf462ef4dc36a63aab2cfb59c47067933cd12f98ac4f1c1a856c0d0c81e1e0a0ce4668d3faacb4ccd03a3818d1a03fe6c32acd5b8a11207cd61422e2d06e5127101bf67ab1470ddfb717ef32ef346af8b656a832eae3de02875ca0525a9e5c6c8e3bf5c0c150039267705567deecb7cbd7cbe7ba8fe61028f6c089d05fd0dcf107dcf50d524456799a963c2c755a19690aff9e1399d2c0bc03b54fba21a89a40aebb98230102f03cb722c1d82132754364dd2684b96d832b56d40d105fca7eb878cac62085be8f0c0158327f7a211925d3bdeefbd0810bacd5b46a59d5abfa5587cc2a704739c1aa2bdcbe7c6c9a2ebc13b1987b8dfe39747ed9bf478ab3e0883055bee7d54476ddf70da14a4e2179974cff1983b743b42f99c41c76c4fbe9f5f8b41312756f0a5cc123d826922a0da226118e8e99783fe850647260beb8265038874227cdcb1e83c626f10ab8ebce5424d4649c956364bcdd3d11fbc1220b5cd5cc01348d63a1904a65fe0b259eecb73e89898e131eb2c2baf3ffdd60c4dd88985bcecebdafb949c11be93a50061bbc5c464be4e441eb5ab320da6f4037f8f616fad77f519864b4bfd156c067c738c5d34b11476c24a437afcfe16e15d6dcc6936c0596838ab504e2ab1baf75de36127cc0cdc14dcffbbf57c688d167fcafc8977b6f0e9f3320c81d63b5f0c251718c091af0536e98b31036d680f12c0c44cab61c0c6e557915dc6dbd802276b7476e7b75eb3beb3e96a21b7a9f69051d795d3547485d58a8f655d15021cd913e7b26b8da4f3d03e67144fae62efe05e88d72e94daf248c08b056972ef9f2f8dc69cf4629426f1ff271cb7aa7e0c98d5ab4ca72774f4964a2f5a29eaa9e0174cb7f6a115ccb59cc25a77957d949c05c719d9e324ef72e1e906fe70ef679211c430175865e70896b9db3213cde1fd354fb0d5ba732670f5bb8111231c32bf5d56626f6a06f6b2c6f3fda7cb5f6c489e989909fb2acc3730d16e44fe2202c2356cc795b833c8fdd9c2953dea6f56b9a85f998850d04d9c6996f78c82f0c256cb7da1ba48191a686ac38bdb566c6837a17a4cdd51d011cf9c345075420d73eb5531fd888bdb3baeb63063e2cf4cf01d1779f1de5a947ee33f97d82809cc6a746e2ce613d00173e6a3efebbf4a11120872283aae69f880c0a70bf87544da85901a55fd84d4561e549ca6c874909670721f433d1003ff5af35f98dfe229a75fe8ccf21e81ef3c336073d673567c02c534bf9e8da0483dc23b20fdd961335fc35d902bf4467dd0b1c1c6fd1fa17f9fb177d242dca668dd1acc00e61690766025e4f85f97b85765e2b8ba957daadf88186b14938796c21cc3536b7890d9c134439320b836141969a84305803305bc5e74118091ec116b6934e9728f1286050b3ae59c9ddfa199f9926fdf5e1498162571291911e23214de5e5980460296307408c396b9974382ccdab26ad61dba5ed10bbf0b28c6646b54adc4c2bcc958b61b82e4d30996bb20c7a27de03b0fab018e2bf4a9290602c0758096a24ce47f6dc577332db8db5a04b773e134750266dc68615ee96476f94912e2aca0ff55c5a95c93e9dc3ccdcc5731897d3b67f4c69290e88f307a07d7aa55b4ae7a2f07b9ae19a6e5087f95d5402b5c85701c782af8a7c009c06c5f9674925b14cd85aab6cd1cdd5940907b5e51493f3a435f72430efb28f520debc2b7c66f3a7c770a69d34873e5a60ec0aff7d9cb4d55cf3b833f63016456f5069c74ff9b531d3fa71d7cb1d607293c22ece489e544b3cfc62c4cf69fe61eb6397f954f2307b91856ced0d01c2dd815b6e9e808d5e5181019cadbfe70b750be6c8de5d56d8faf5884019d12bef79dec09e039ee6108a31006daf019944b047866d8b07f9748c5ae5f22d57331f913ace2592be1be2387a1a5d09f4e2ee6d3f990dc17b744550295faee00a6318c47480df538ffec3dd9de6a90d2c03bd207169b5df4a74944824b80d0d2b29ee1963c007538fdb3b081183b41c97e9cfe9dab47f74f48b4bcd86958851c5d8402231fe8849c7e8d3e85c607c4bc8d1b12e35823a6dadda6dfac4cf984ec0c0e67657793bfdc54207cb930136e71fd1d06d885de663ce1d29648b6d8472d4a19f967f898e2e9a03720b79379ac0adef254f4edae805f8caff10ae77db980d5828588f7f8d3aef20d8300537449333a846457610c4294af6de785ea5bccaacaf9333ad6631c709e383d9bb92ae0e348541f77d8c09b0ca1967a331c52ce2c3d156f990724a341bf5cda0f0314f6e4474bdfe5ecde9286c31d3b1558cc69d36ede2a1d39140620cec1fd9e5c5a06d0e70117997483ffa7397403f083d061159437e7b8053b873e1a9f99b1d7d768865b15dd75b491c777f5b3d0c69d8c55bc7d3ecf58eeeceff8916739c62bc8b0795818722cdde89dba5e9397ad105b5332b116878fe44acfa5a3eb1dc0f99d633c1016683cdc4835cea1a900081eeb78b5a49233985609372460379d237470a265b5a9fe7d7a263c6101f479588e4f2c53c3b3fb7da7654f49ec6766368666d6d4b80f0bca77680e82778ae5c6659bca36a9e922b1526ff1a256ae582ab5820ead0c66019475540c315090ab753999bbc14d8a2f095a02c5db41e415bec5eaa34ebb4751fdee49f293e990414446ab529afb4224fb6f586033667f5e5d120a6631e88f958440fff43430e9de9164262e6043d0e18907a2af6a9c57eca95d4d74e72114e02a9df6480e5f6855ba6cac6acecdbb1fc9ac30bf2ab72f981e29b393db8561eb29a1fee6e999d817e4d389afdfed9af9c480fc08a952fecf577251ef155afe50188c75c80c9b97c2d586254515630e64bdd9a23bcfdeab8eedac901d48f5f3d3a8b5fab5cb6bb9d0187d8208e41df7cfb83fac053553f475c02dc11e50e952805cca6022b1774f904d9647b2cbab8390bfb3a416a9f053aebb5dbf075524a60aa0b42dc9136933328f83cb1a24daa68272cfa498a3479114daa68132deaa48b3259d14f56441315ed8445ff44879e6da702016215f30ea600a043815256cd0856e72634ae527881c1475ba139f4359b9316d1fd460b932fd2c48a30ffd8b21317cd84a29fa808e4ff5a38c9a24fa648455ed5c53b5d283f9f4ad75a464c4c9fe1bd952012000cb014f930800c16986be2574130ec03c2e22c37f06f42caa9d1e450d053c95e0dddfade002589b406d3512761526682f89406a21bac17823c90fe381b6bd7429a0058103dd5fda25a360a133b9e172017f2971fc76890d488ba055ec5a03ab8d715aae00b38be14886d2271e54a144bdc1093c32b4b0243c75524897c1ffe1bf893000a8ad05c2baaf5c54b76b27ad702fca27afa71d5c09d9f00ab16b99166c43252c2762fb8fe795ff5204f2260d05a09b9003e3002f3ffb5bda964e1aabcc265c7b2213fda007b3e724ad23fe45cf0119f87a83a3b7188190c3bed20444ae9abfe821d5d4674fc2ff7e7a0e2ec850d5adae0bacd1d83cb7be14e69297774d8e1bdbd45423cafd709ba46dd96cfab74a92d0a0502bf66e16886b28b126dedb510e0a6f4c4cb8bb9c90d3aec1d94fb6774b7a20ae430d55aa0cb2e9744f950b2dc9f5bdc6cd1609f0d7978790f679052dcfffef98b6dc555ebe417965b89b831d429e3a61b514b9dd736bfff80a7fd8a5f1ab475a8b8beaa8b8629f4b5280f6d07c5f85f5645345f0a273abb63d8870f55eb28b5b662e67ba54ae914c699c3eb41dfa88037f208d1e835618027e6ff7f4a47dd1fb1cf5132965fa24a93b55346161c51f79f8b1c15d96368a290e6798c985fb52d5bda431b9dc3470e20603c45cbf0564fdd879d490b789d61a7faf32bcee1603cc1a092a7ac2ab15533038411a6f7f489b4ed4b6f82f74e584b37d0a262334a87f5f7291592dcf9a3638ecd517efb4ae049acf864205acd81453524155428990e58dd0a0a88df76b1ec0ec3513c949536722a9296d4fbaa7bd900f8ee0f9118000fcb5f1ca1f740da7a2b54627e9f7c18f4e4cfdefb51c98cc86aed1905f6e3cdcb5a5b8745c0afbe2196d29bd1781467491d176f4511dbe90278ee36069285cb5b9ff389f84ac4bc83a8f75e4485a582e5cec87acbe2aae655be08774f5fa200d2c1a8fddc3d41b4bd83e8a1c03f04d3cbedc43ce0931e480bee55f6d6328b05eb62dc6653e3376ff5d525c795e6d7ddf1c422a168a0fcc8ee266469d608ceb1f2b57fb33447d6e1a3370ce9bedcb9722be5c7fbc5f088c25140f6e462aea3e67fb2c116b161ce2770a106367dc61ad01556bca58f578958cfd5767e10e0e00ded6b55979dcd63cc3ca82d15a568e7340c9897d4cbb9b0af10d82723d81e771df7d3a7379ad6189f089d443d5f65d1000cc443783a649268ac508aa47f4e4b0a6073c904a19bd66617d7eb6f926b15d092b6fec76f158a467f155f483734a6aaccf03cb72d7525f53631050e3d11e06184a5c36504b24de7ca14f5b7c5332933b04998b90c035ebb2afe22947cfe8f816578ea50e19634bec2181b72a32112a20634cd40d5c94394b94a30f001be2b222376233781d6750fcc5cd1fe4faebb834c3b074c4b7c043376f678200a420b025ce62862cb6020f3e347874596ed9ab0f67bd490ae669eaa16a662c665e1ed4c6ea1d7bb0219462b38d5d7b9b64913f47bf2e675c9080f3c57c9280cd6ec730a3cde0d6c9cc0875ce453a835b9742571138325f4b8b6fc7e7de7e1da6fac43dc2e938c99d396b59d64fb8284c7252c13a48b902787287308a16d58c6fd96059fcfcd35e0c01454bccc1b71678d84e264326dea4e11dd5daf78c162803beefd609ab21862f6633e540dd998e278c164bba0f4e604fe8e2ea0a0c46060eedff70ade18e2a2cb031becac0562a0d0027cf0868e7ce3e69a8b8eb3112d07e14d8b2b08a17f4013d083cfac358da2c27529a8bae9b176cfcc62c861361f8014539119b8747dc377360b7d34150315094c1c8901c675358bedfd0eaf57331cc188594c75db1659f47930dabbc1f8f1fdd0aca3151d3db0cefb1e876c579e6991dd31ec22b7baacebde3caf5ba0aaf1bae0096512ec6c77e48eeec73b8c75218447224d5a8e983028e65e103f2f2a4d9e9e206778fba6be3626fb0ca7a9d09f62ae92dfa1784cf6edc48d0a49e921b9ddff2900d3b4c87efaf4841e4a57089029ddd024b114320215921fc3a58c98d25272d7e6603dfad36ec92b3a5831e18c8440bc8e54788be944ed48244926cc5294f967e129311e1a9ab1a824c00b88d17c56f60e689ba93b2c5ee9918870c62cac167c5209d18182658b01171e29a689f2c86a52681e7504d05e1b33a54f70bdf44a93f0f3b2e07468da4ed79f9ff45ae7d4717b491a93b39694ec27cc466c5e271b3ac4e75cb1aa0ec0d6f700cada03705ff90094ee96799e3c2cd4634cc6551528945b4021300a89ba0f099d7c6346f77b72089e4a494a3e81822e2dc172533f9b72723a2363b41fc21ccb7454d4eaf47640f8dc1f7e1c7846eca4650dfe9c5fce6cb94e8f780a42cd2940811e6e2ed396f63b29a6560fa9d86f40d60402c9992dcf939a6d0c70046e028732e60f8a48f68b484b44c022ea900a34fb8501f80575960f65a3f7282c0af1fdd72da0b77ae7fa6733fffd37e294828afe3584248572fa25ad4834b1a2b0d82aeea0838a5cd2393d11c6bcde8819079b50565dc539b16d2f076eee52be52433e89493f109dadc8b8d897d9f4d78005cfeddfd8f2faa2b6419488adbf87ce5c497775eeac41ee285ec72afe22f3b7149865edf9f8485e4cc330c1d12cfb49cadb4deb91db9065a2a42385b9ed62f67e98a28521afd877f204847aee0a35c89d64bbc51f283124606e0babd73a48959217237adff48c518db2bf5a7c1183f4bde3dbe45dc1895f141dcc7f77a98fd4d4143f1f27ca584ac2f710cf9f124b40fd71809a00b519e358a40d26a19f49a4a9df0a4cac39df2141f121bd4145bbbfe6cd90fb17ab27975cebd4a8a79884a30005594f3737ac13ecf60432a63000d0a5ef8aed8b21824cbcc2e5120b7458fc5a9f005b63f4d662e183267a2e9efeaa64874f09ad089ae81760f8968d6e3e9bfed97a19126795cbea2638375c812dc41ba4dfd458f86e74956160f933ca5a19b28b138197b97c439ec5ba3d1fccd4855e5fef2b2ee1b31334daf2c0f3f87ac527fc80c18e09195041da1edb2e55216dbc0e4c0f83b53c04b144925b5fee945457ca4a5ae9b2a4a14e87c739200d81a514815b872eb170d4ab898813206faaea9846d9f14e8044fdb4d524ede80ae7c04ef2fabde4d988b77b174394e14a2df3f93bdb42ca017cf36c3bb082f4adaf354acf57d7621b655325bc67e1961daba87ca40d0037f758c5b0f7e3660c106308a4ea74a1c845ce2f27c2520773fe9ae4770cab7e5295602a164fd6c235ab52e13dc7cd570e13e1c5992b98921816d6e2737c88ba2024bb317326ae2e21d6c41447b39e64835cb16b8e47a527db2487edd37bccc9bf939cf3a883c782c8141367bd8db0329744a1f249f8e94b16c91dbf6770198319699cd21cea1c2504f55fd5e82ab48cfe285f50acd6479050ca6574a878541434d3d9f2180456f5857941be39a0d72ffa2a00b5e4a59cbd9defd264cf8e0cfd145e5f28db2b4f50190514f2984dd17a29ec23758d77a714cf90b9899c96fed61249980c13dd70096e1e6d8638563c39a32169f5d896620cadd6d033b3b46264b64b73d177dce39c97abda5df677172f3b929d484dddfa4f934b5f4f0135fc4fb3df20b6e9afa7f0ae20bf9c30febed809abe2ad1533f99d3f76b901f494cf16bee5cad7732af8a1ec6cdfde3fef6ee22ee5c2cabe1b74a3fda08f688aba9ff0881d887bc847211cd45894ad948eeb8348a8a11a0d1c9b5e698a4fefa75be9e653306db2d97ed90cccbae00f23c56212029c6e0565ad1108201b25b84cdaca4bae498ff4d1193f10ddfa01d98b009da3679afebfcc41caaca3889e1412ff3d46b5a74d124c7f5ccc45c64ec06f8ed1367c2dabe9797648006e3cc304b0dae3237908f63b13c9b08bb59cf766606c2c2d64180c1b8517dc798622489541bf79c913ff6ea103b45bcae07fa8c8a93d229313e95e8a5f0305fb860b0683fca5b1345adc4cb3787f6943512915f62067086fe374da8b26ca02087e449ef12cdf3cfd6bde9c50696d189d6afb5190f53d5cdf8a25182c3e47ccc67c57953ac175f385f1d71d33d488780f2cea402b24f2010a224fb8bfddbdb56a091ea20c350dc72f894e03a5c00bf9a5ab5705dd062729e1c691d0c8a8b9f9b5a0ec36467bcac9f290dc9b0600660f90504eaee9d7c3df36fb5afd0e9eb60060eee433e94d04d98f586b6f9b8bd0876f282f988c4be98db85a76f526ac4f9df52e5acb3260130a29b00af11b780a05fcda0eeeda21c8ba7435113412fc65eb7ac2746513a0fa634142a2c89c2411de2ea769f93a18054a0203f2922d3b8db7cd207b68a007614f28ed6fa897dc847ea4b3b850454716eb428a69d559887667e6be7ee2cf2bca5379bc13d59fb73778b292bce43bcc7ada4833f7d423965f7858b808c502e754c2c67506c52b4b6bc8a9112338d92c219d9bc35a060e3f7cfe3bb47c9fd64e97a11cc886bfe3529360f998e503e25ba94f74634b7e12f36a614c5a0740f901199751a76139f42ffbbc10180edfd707dfdaea377f1a79b168309af82f21c9e96996e05879ff7af5bd072afac3511541609466903d9df7573576a554a7e041fc4501413bb9dd251d4dd91b4c7f85e181b2306a91f1a5925d04e982bd74d00a64cdfde6c1838f3b80a6678306be06ea0bb76e79926a1b197d6a729ce1f88b8f7f9a41c5159f3650301a1cc23adadd243b028f85a32242aa541fbc17844cdfafa9a9857e6b1a648c315adea07d40232ff1ca4bef67440c7f18ebdc477c96813210334684cb152b387de078b0db246b454337ba9ef7482c4665eadd880e1d3f7dd414408044d34179369e8c657517356ac88640ba44b384fa7a34b690be03d6ec6fe28e1da525f420cbcaeddc1ef269f044871fda9b39af5a92e724dbdf175baf0b66efb473902664e3619e71e819d4e51f92cc07aae08c0b95c7199bf2dfb923c29d238cd19ed0f2cb42b6b487813074fe3ac4123304b14cf60eddfa68d843b03b98a4c03dd6e8c4c160c0d5cbbfe2b02858247cb85f9e09009d891523074016cfd22041bcbfd9874d2a2e8b92316834cf1ba3df2418d8249a55a8c5f4edb0202736f61c60b83a92dbca0d21d88b630c046a766b37918c7d52823b097c03748013fe704325acb90f068a624391229d12d83f51f4473fa59960beb5dc1d8031916a8225cebb742257c70607053c47e822f27d88d87f6af99cfb0c0aa230afed2d382414f5def51408be0e974f84eeb02f829f34015afd99393094507d282ccd9374602ad0ec4ce2e504f9df68863fc069239d61ee104477ea0a42a64d135a62295376a48534b30981c31bcd08a701285984625bb74b3bf53545ed4d54e4bfc06d4e9bfe181062f64c119acdb190f50326e7e6b013d9329790659f0e41bc36fb4618b208329f367da1ca88780b165d14452d845cbcda55dde414f9d48aaf87915800e45ae238728895307747930ffaae621ce968ad9200bf6fd71758e1037d90b1a82e108b3f038b44020caa91a479f227aa710031f7aae7fa8f99c4980c5c3b9fab9f787763aa38804e83c30ea40a2b51b30bccea672a1fc17f7a01ae0ee198e21469b624c950962b1c7e921eff80ae69a816487839c61232d723bbed23d9cab010a1b4f634d5e5dab1974f2cf3451f8027f0c5f884a09b1bd3a2ab3dc8e8c323df4b98577a9d42bc6398acb18f5f7c6bff45cb6dac58040abd19a6b3fda2064fbd492195432750499ddcaa8a01215b60a42f58e3dc5c768fbc0eb62cc2fb51762d0660bfb29f522244fc5c59da7b0088b46c1d7816152bec7c857136cc18ba2dc7b772751f5815758b6c70d63c0d6fcb507c7442ead8a29131a5da971dae7944d1d865c1a6f96edf76d802cab85c64e548347f0dec9a14d63eaf26e7ae3f841cf7618f92f5f95a68d750ff990c36f2c5426893696a7185bb98dd60229105643570d2c85364a1f999da9ebf82acd6ad1969c4e8d63c77f72bb9e083323d68945b0a27f8905d25e39c1bf0325a7265baaa7c0d7826f12fc48e0b5b9bb4ab74d432ae4a500619075a75ec969d50ac1879d5710e1e801986fdb5992b9149a38d3ccddbcf0169882ef9c44805810d9fbd9a7ce074cb0c57177d91483a36d5fd9df08580716c548fb60904cf6b33377c6cc48888687a4773575177f073d3b1ad4135aa4f3ff0284074e29432ecedb07adf79b6adb87d607464a40dce4c1afb3e86f13a325085d620c1c0bd5bc13451e412d476da3009bb68e6b38c0eb5260b458b9a8d2bafeaa33311a49abb709b17089599a12aaafbfa1a54adc99a872a8870fbcee4c4f659af422b8d2a498bdf0a19854669f23538dd46770b861d44459b1b1b8c45a5c4842b8f46b49c7c531fccab212cc6f0740623a61087c3ce1cf5aa6ac9130a4d9fd84fe2bc99bb9ce85b6b4eec866ee743c44935b0b18f9c03ecb8dca1fd99d23b314acd3302d046c7ec5ed78b8e4f248830d36cbeb44d47336de285442a9b03f08e194d394e74052d5bf2e1f2c262244e595a35c9675168936296477c31e3da2837fca8d36d860fe34d38e3b75f868538ada2d6a2513c1ef6f1ecb2ca08707f39ac6c7c4275e94bd3eeb6c2e9bd06f8a2003b1a6f09c18684c84a62c47bd29523d9fa17c14ecae89f73d8d35d28f9b2720eda21701db0956e9d6b4f8091429bd1cd55f0010ff2e8940e133eed643126ecc9815ba4057df40496570c37a124c6b73a6c4a76ead4b8e9e97eae43130623d3f6b937ebdcb446e264bfa5c8053daa94fff599be1d62de95d97897e91cb5105d4ab6c414f49b3adb609e95a97dc25041a102fc03159c84ed90313a83aa9a6190143b76d15afcce118ef8c79a536eacfab3a36e11e5776961af183313068e14b45bb4ed245375c0125019e91a0c7df4057b8a04e4c93e53f342ca1cf14b8269f14ac4fdc99f28320a13373e3a40cde5cb8fcd2151f8aa663c673d0e668e8981403e2c27608c35f111080202be84724279b1fbaf52e6d3b88ed6f32ab73d17a4d188d601655645442e45a642da4437188380a2d6b64986a676cbf92441440a373e310f9a043a6b5cd18dd59780b35e3bd99cd397d71861f449eaba0a17ca4e5135625db7f99d388530a656f5aa2b94901dc6a4f538951afa523eff7851e9ad9e2539302d08b4950e7433ac7d4480109326eb7b169898ac905399f4b12177e6d066cb0bf1c81884bedc3ce4766b34b8dbc8a417996b8ef4f7800d18eb3a89eac88d899db07fe5b76b95909272985cdc3702a0acf3f9013135c05b3da1259eff5b36be2fcc0dc518b925692bf16b376f7c30bc7c284ec161b88730286a28876dcecc009303d3ca1d467e42e6184f30c30e0bc320614ac17d34b259a488c4d4af0d14529ebbfe1a9f9e7a7144d9ff500d5b06e0460555171c3391babd72517bcd1b538c36a2becd6cd7f90e2166f01101341abe0ac3cec95ae9ed57a9bdef89f01efb4087346367712111104a6e4c1fbabc63a13082d054f22c9047fc441547a03bde9897b04a53099a1624a7a9d7562478f0902d3a9238a40a9a04b81322df1b641336c4c240aa5684c839c250ed7b1228ce74730146595f81242ccdb124921429dfdcf1065d05a0452b30aa7deacc4966149c461ccf7986241797c8a064e8be19a8728a18c110d6cf034b8423dda8b482111d79238ed64a74fedf249e09971019abcd59e65292dbc39401225c0bb0e7e440c4ecb4fc66c411f901b89f4355b6812a9b73d60853e1137e5a6ee129edfe5baccb220b2c785b3c72dea82f519bafa5f0e4c7693a73f6f695a6fb90c06e402224ae49fc7db3429d11249b96512176077c13354d062b2c0c664dbdc1f94aba12a24164457d5bcfa8f20397e1c28dbbe513fdaefe120c72bd2372ad1c980cfc11ff563f978623e667b5566123a797f11da84b68be224debc803af5478f775e7e9b62e98b8bb49153b81a7e63ca9bf2cb25240b28572f6cd1e10ee4583ea27fa706befdc642ad1572ca4aed960864af0575e420846727a43735d5ca62890f04086dc6ba97462a576dbdb785190f9d88885224a6bce083b86200ba89b4ab09ed56ad704431f426db07c699407f21b37f368f1051ad5df9baf1a81cec86190a3edcb4ca56d14758afa1108787b8d5a82a07ff6084b5c75ed1e8ac55c4a05e57a5623cc03a8ae48a5412c42aaab3419ad52bfc3b63c70b269658e406688860b6142977b15006e1158a55b42d22164310a70594fe5a4ad96d05623d88fd7c3c6da4b68e187eb7140517ea7a1009879bb19ad89ca5c832d8d6bbc72a65d208b9644c43cbd831617e40531ad2ec9ee0c6f180b639e1a118da3660042a13c623238067351e6930b985b4e28fe079fc733585004b41e043247e6bd4d771eaca10ce1fe80d65cb1bea34929dbdb07d2bbfb0ddea07bc36ca5b8ec4bcd52f61e3db46b85bedb3886afc01bf4861bb90366eb791922b05b18818c0c03a0cc058cceffcf988a99bffc2447a0c083903d81df1beec061de0af6a166dbe492196d94c9a9ffc405f9370b0ee8836aff535cc88cc481ce7ffdf85f3352872f7d80b730c55958b8b74c6af64a190facaa6eaef15ab0067e88ee93e98681f04f4612b4c77386cc470401f0c26dd60484713bca868eb810595fb4e1e8231310daa63c5169354a4d4db379fe97cb223c2f7201b174ca1831a56930bb4fb447844cc624b61e5fba20bf20b33638ebc330ce8695a5a83b68ce2ebf5c7c58a39f432e50d7927b5d011215a7dbf6b18e829ab20449321ec4b7c93a6803ef9d123961c49b851d4d9fb0b6058d282861c11cb7c53b445730c337e88264602646d378b2d70109644a50f83691c52c9fb0576ed7909a8e6a38fa779498aa68c935e3e46de5502082f530eef4dc7c36b3c0108931044c66108258dd7100d81a84dce277cc094fb903ce958e61ce29e1e09266efec7996916089bab11f1b309c5c9ca19b023746fa24f1e175dde44c4f26095056146ac5b9e726a02901df9029904ed2389847ba35841b4dd4b26a0cf426fa7e744d2a216e00b0df33b14036f7607d1e4ebe4318a37073789678e0411389ab456dd2a9c59db67ff2577b9bf81e37cc271849282c2c312708d24671d13141d750463819a9ef790c0d46d08f8ec1538887097474d853a520e3c288a1aee59c57aa6be2d8c57bac3f12a6d7aff4ecafa0615f0d94b67e5546e50ff20a95cbb5bdfb5617c1d76f6b08ff2330aaa8a9a1a12405daa33c86746708ee13037a31f4a4c04f3d6932c9c3c59ecabbf27f4a95e79be5b5315fd5b0035d59d31cb586afe9dece37423e49525534825e9a1f76b47b146d0520d6bf9ac1449a873ce3f1045033db42a1c48aa08d50ca11ccc704e3428a87218d99c1c336d9ca6f7ceb116b3772e15046b55af6bcdc98890ed206b65c4b942ff4681abe51c50388aeedf813b71dda22810212b6ec4f74a69953d4cfa3ed6b4d2c29516a6b021f97d6d83ce4443d873b906bb2a111ac4ff081007472667bb320a82a747480b94590be65977eee48ed057cd9e40ac80327d463cbad96934512bb712cefe3b77c6c628fff5b1adbe142153e68a01c9971efc7efffb107e4a74ab170786f80343c9e07f0d9fc44fca14b4f9e1d5942257b330f71633f8e5433f499cf6857de26e1803c200a49020d2d2d924f9573f6a2becf589ad91b923d02428fb0ec3249615bcd6c7722e74b9d321db4b59459c5493a224d4204b56adbe67ad3b2e99085822fe593069d2fb347b88a9f4a261e60e8003c90e4a60bf1beefa3342bc271d282b6033a1306bd09400bbd328e52454acc4b141d386c3640af0fb123bff1c877aacb6ba4e4aa17d334fd304dbf7a19770f849ff6b3e9c32743ad6793a7602b9693fc49ece3239898faedb586f16cd27a9df2d74b9a300fab5e77aebf6a805c533bb5b4285c2a99ba679590d1d1a47a4d2d961cfe1f968aac2d0528a3e250dfee1fc7023eca403f12f0eea735a8583c2dcfb0198f0e4f694d341bd9db5183fce3548d9b4395572dea86a5ba00e2fe952d195061e8525eb1a6dc6cb2a3e011d86021fdea3acb25e83210418b1947371be19911dd36bc40217f90cf930cb7c6c239eecdc18eea346ef339bfd0042b8a912bdff6b47d1128853eee94cc8138c9be836e08f61a92ae6276acd8b5e3b25159b41c141be93b0be9cdf473c3087cb05622b00257048a7162da81017770cadcd602b721c6cce43e612936cb55404e7c5ee0d76672af95f87145a665cc9fc1a87d1dcce091131acb8fdc219c7398c8cab522966d131020403d742d723363a7c8d5a13274f08d883f1948c7f40626bc5d84335629a6ec3b144838d5cfae508f0bdd879e1334b1bcef5abf34e7249e95f9d1faf700f1d5a692c3a3b6a961043659f0ed32b48bd4d9970c36e897f4a11e6e8a2516bb6ae6c3d9075963cb072d5b8286a288a488dc523038db71e7d389892934503ff36ed7db63e6b9c6faaed2432f8ddf45cf603fcd37e96d938a49ab514e0387c2556f0ac98ba7f4a7f37ed673d2448057cc9b01aa155e2ec12bf51a336343debbaadb8da12d03148cd655c6b3874f4d1e176fe4dc0c08b452ecee6ad76724dde686e86033df23d1e60d036bbc46f61baf4a6531bd13caa63aecfe072242e757b8a2e775eae02c28ec08c39cab26047ce6987151f9ccfe1937c768db014c72b29c76bf92129830e0710b75e7bdfa6ff5686146a30605ff1648ae289bdefe8adbd74ccb997145b98e34deb89ad96145320432c91f8179eb1e7a3be5932fafacdcabe27ae19a426d059bbdad2e9c64dd0bd0b859c26b39980fc6a4ff7d9e936596549d133d3e46b7f0c2dba7c97846a16a5d4b0e46a30945cf71188887fce6230c0ee97c196161a2131143cff7f14728c8fdbaf39da8d61f22c8782c16293230e64add56bd3ab8fde1c3582fe8516349a6d4d007c439f3995091115ee5709736987cebbd05aa814cdaf435296cb6d71df50ad00e1f05b266f98469490f67dd87191a52d4d06ec7f902f8afe9114c397ffba6e898fe5fec63bd84d5d1ecf3d6c0a9292dd3942675ac34222cf0c0d11520f9fd3c0437f5c18ddcf974fec24ac69467433b085deafee72eaafc4aafad0d23aec146b43d0cc0ccf9efd95b63c87bbe2194735a1531d032007aca58747b441271016f514bf1efdc702a373cc59585a3dd3cae057a8d7f681810316a85f27b2019c8efd9c4212855d24b0e9cc3215a90dc11850485aded902685785798648f2a765945dd1906f0919e0579743f10a63554b04bbffe9786e6d6965d0d55c0165ad7fa3bcc3cda7bde2b759cb1a7db13d4d623919fe77b15082f8c8cfc8fb450d1197999e7b6dfbf6147bd2ac0df081d2ae2ddda5d03969b99bed9ceedcef4f51e90669953d14acc8f651ba37c0dde3e4454b9e286fca2c5703aa1d0cb9950a1892be122d9708d518032d0e38e984c9cd3fa6fbc1065347794a49c7c5c1a9d3b595ea2f5a5d868efa550c9bc4e7150acb583f26b3051a7cfa210e341554e667f0460d4e5994b742c0c9fda329bba2ae9f30fec34a2d7155bc298a7293f903677bdecb0e5787f0d34f6b045533e2560341fe32aed9e0f1349dceae120d53c2bbc5b6c1a522c3f66daca3328fb0534b2af07aa49bc249378ec9a078b12965c12209f9eb97111972467326a859b94a932ef48668d25085180241db8c0c40c06f53aad8e87e3969366230cb8da954821e453018dd6d62a5ea95ede89723211ffc06c5c7700636ba7f2f67d0745d69c172fa5aec587c380f921bd18a02b6622a61642b1e4a3c3a3e6e2aaf695686137ba9fb19474fbbaac35edb33f8b88e9eb0455c08597cbb13bce7cc7872d7493d04782e437c5f2bffe5c2f22c40011c278d8ca9f2d1ec2b44989892f08a99487d64bfb8739f3d9d3a8901f689d3f58d366f7f2e873379a35c57d9669f94f8d052b7e1c29d037a8b99f01952b58c2c9bdb794a084cbca5218739cf47ba22645418d1bc45833dd0110d7eef2514c28168dc6adacb6de27eab93369f6d2a904c6f8c8f17058d5e3e243cec629cd7c3df56bfdbd85ad9d7a2f653204eb3367afe5cb0d48bd0f4228f9c13068516a75f0568da3c69f5568d271aff9e4a7e871cf023241782f7ba279d17c53e1eef40c77512212847f7aefe6fe649c615d70a740e825988e376491abc0e1ac7a3a133fc28fae8f4b7ce5d54616d191e46d164ea487fbf8b76ca813b4194375860ef2cbc8768874fec9567ef643eaf14f8584ab4ae3e6260c2cfcbf5d6fc8d1e8392cd38ac590272efd05b89ddcea40a807d2ce8e7be0dfb1288a8ad1e9c716623790c5c431405c572f6804159e8033b06027bafea9f2274bbddfb70edcb4de168fa77390a583025de968f9bef565bc812657e4ee6eff006d8ee3d28e16a2462e98a3749c38fa64e5b5498045d944404cde79b8a7e07ea7b274a3666a121880991989a6a9526949934adc672765f872133f88298757e2c61b81edfba80b99a81fd48d7eceb2ffb3ebcf3d78541cc1cc04ce993815e3de77569b100e546369a632af76c1f77776a954b19886729e651ecfc5ec3eac6e2e38dc453534db8d2042cfe2f45039438d232c0f6075c1aa43a8432ed1789d0f723033a4bb80faea563e3eeca0d57ad1c222e1fc6db529a3641a181a650a5d399855fe33c4434a77f0431bba9816301d0e61bbf1eaf0fc32b514ae5759ba2fba95ba2639ec6a34616b5de33932f41e14e588e2a5a1dccc1ad769682868217da5559ae4bc9e45a7ac3c51482b28f0d197ae55e7a91f364d278880fd8a47c89e075d7cba78228e8be9f0c6a3c39a9d8c1e9b2e967b4f24194e765b86c6b5af83b5f2df05e0835ee5af3c412c1ace4dac7a09b6b56d671c96c95e046c7b291ebf1d34f2849035db082a896f401c53b04e2632c911d68447151e0e293e8f2ceac737065c2cbae950a78b0bd81921f6962f25d2f168386eb1f0f531b5570020770411e46c162beacb210cab259264a4a4db401b80f6f4d121d2698fe2f4d2898278dbce9633cd88ca796175e8257e2179e49b28a72c61ec34841bacee42649a0c599b995942bb7a9966447f7eac18cd654d2d7e9173572e830c665ede48140c183422aacd3eae029d48db24fea1de196424ce10c296bd8fa100028e5addbc50b947ce3478b7556b475bc967f47cc92da7cd126806327341e0c90b0907dae42e5c382bef4ac64a4f1fdf693703dcda2a9b7e163fb0d99647bd9813f6d9da40e3fe1a442caf86f86b1294357b426cbf79414aa33c911d45cb176db9ba1f33f0346ea122d7caa3c146af2d354840f95a1450c92198dc4af6135c18ea3a5a65cd78bd241270f4474393b368d652dc4a5b5e886f2a8954455056514c8111f24c7a0738ed58355bf7c1f3b11529625dae61e0f179419d8df147651e2fd077b77e106a9e2679f84504bc1069905bd48147d6029f23c899c39899e03628d35242b0b9873bef03eb027bbacb4a251394164b112fc174334284311ed6e328a36840ee20f0d1cd5766df50d047d264a502110bc0150eeb6611d2e8c6211671f860d3312d3f3bae9c351abd6b0e16d30e659ffb9044996e36d57925a141728f81be2b127a426b70e840e0632eb6dd7810669b1c9e808d93d99ab80fbe622a2a1297b00b5260b038ede9b67a5ad9cbd949a902bd8282ecc07ea774d295b41f9bd16027884e1495d42a42bc950bd5a71d729a61dd68b66a8cfaf43447cc2d624e94032000791319532e6441be32094f96fbb508bd84e6cfaafaec75a807ea8a267f35100ce3b0869913f0f2762e6c67e33afa6f852f4920dfdc9b09971cab84dd86fb6e2dcb3dbabd0336215c34921606b6d59ffacc891bea85ee0a17105692608df6476ebc094c9ce7253c3d47e4713143e78417282939d3787865d1945e919208d0ca8afbd549b3d5f789ac4bdbf5339b3efb0f5089dd2fef66ff61a19bfb6ce37858851e3603e43b19c38acaae335ed9683986e54eb13aec58507ee1abdd102c8d2def6e367a4ff3bba9ea86535559e1160fbdc424662a16f3bddf280755b4a650567491f56a568638c8959ea5805f6f7c6bd9fe5b26014410922c4410f4bd5d5cca1e5eeb11a3053f075633834523c31dea0c77d0c601c7f93796e022eee11294fe57a1a1376b51ab60f21a2079b600edeac4587d9a71e1bbf91eb186f270dd610297498140c0a83c343f9c69709cc902ea029b8a713875fb8137091d8b6417113d9d880d62d20f530a2fdc14d15a4b587369915e68518cb0fa5f429800304f8f4510550c4d8569104bfecce6de406e1a306183ee37ec4c832cf6ff971524ecba040675a62a1b89fa3e7a771614399bf55623a183e4699dd4102d0c6bfb3cce993ed36a38bca1a5593480301e4540372ce00ec04cf5b490b369412e6e3f291742d426028c9a046b7cedc8e004b0c24b6209791fadf28c342b82ac8a33f2cce06b3f9da4b6a363472d578f715f318ef0810368db5aaf0006a018d0978a8973faad51991afa8beaa3155302c43567ece46f17ec6dea81d331f81fb3d829b881cc3a87e665d784c67f1edbbc77c841303470fd0b97ff74331efcc3f30b9bb5be69ddb3632507eab11a4230044000f16ef0d08ea1e74ee9b84ff7f4d06af415af7599ab0abd24224a6e3460c422a9c535bc1f006f01924473ef4a8b4ba0ae25c4e0916c70822b6a9102a051b7ad08ecc350167c972ef4cffc384f09a38e6300d939e3240a58d362a0fe4682c0a636c736f41338e841bd6d59ca8cb9419643c71818006dac681c951bc0d6b53a88806889f781586c0f28a3916a941fe05479263012107f2f0ecaa82996992b4c178b7558719a1ae0e450381b91039dffe0a99918ab8953c867605200ea57bf6acc6791add0ba6934c74c97aa780d3c668b0d6e0206a00a33153f78f74ced6cec1f849e4614e49373004c8fcbbb5636a341fa5fd402f8671f68c1b80dcdc7449089ee2ce0f30a03f2d1e1ba311d08b9fae5d0481a790c3a271d6e8d246ef40877e1741a8a93bbe61ab5bc2de13593928a6b8e1dcef79f123c09b89db3e545636103c744ec3b83a3285495ea8e7ee902ae8bcc1fd1f45b385bc002d60daec4fa1fc02a1bd40dbd8763b666b7bd37843c260fa050e5ceec0ff635f58e82b891ac1f1268e25569db65faa9b7490ffbc8d87610ae816a6f6a6923e1f4aff005720efc9859b4cd38178dadcacb415b763001bb121e980f98a3e292501d6f2a685a917c502dd2657ef35796c818acd4423876858a7f3608701c4f2ccc4054149089d18178e56d6f34fbfe60f0b9fc16d8d722125ca793a948fe49c9833abfcce543b208f7d5bc92de9ed79cc4fda7818673ad8de745de254218ad29522d8afab028d58d3751317e730ac347200f7183a7ce4ba644351258c0ea5e5ae5f83046d9bc03e889936f7e4e18a0417ac38fc10fe3f30982280c01cb388e983b5ffc6299e522d5cd028232c5b77a0565282f15e0d025207b16aac86d582305552dcdaa85796fb98df00830cca0d210b3ea8b5ef2d0872c603cfcb52e01ce203bade446e286a12d98c15361055f2f0c3d80a73e19d8eb6dc1b6c43bc4e192f5510331413d7d8c17409ffc463036c07edc6267517c9977b4570cb58a1e1661015b1ac5ef03f9536624bc0971f8016471c971148f5c2911bb1a35f4f2b5be67a51038b03f98759b0866ecc3195f5addd3f5df52dfd68a3e0b4ccb64b4feb6856d654f208e7f1418c600c52d260c81497f7ca6c0b443e173e42713f9f9a598faef96a8ce9d4be563585b063d2e18358b57f7639c2da9180a460f519f2fc1f60cde247850a9002592799c21f1b0f5fe1a6b0b95bbbcc24fe2de670e16b01fa7482e7161821ef2071958295828a5fff10e421dd592a8d7b11e876d27366dd93b4bc5638cd7632bdd5165140a7b7e2defc437d69ca22fd37dd2caefc58dee8c5d81852984e2a26aecda700c2f5cc873ba8731f549e2a15d58f73b41d266f248f0ce5925c9a6e4fc731061f3d141bc4986b332177c57287120c456ed10d161d045cf8850dd131a61f2adf449bc5f3c825b6989f04e54927fd65b1be2990722928d941639bfa0c57c8408dfb82ebf4156026a9320083c0db9a44949dba0394228369969aceaf7a33f0a8145be4dcb422b72961ec32a68967b9d394738ed10f633979410e39402008b5174b0a7012aca0ce11eedeb34ccb5a88fe7e5db6d88b5bb54a22120b902c7631acc155bbafea04ddabcf3e5a6d9f45928dc8fed7c945d5c1ecda125414df4f7c0f9a5bff66ba0872745b1e1f3ad3675acf27093157ad3dad911f3d2b1035b7d5d8d97a9d209d3de83e1eee89928588f638c54b99fea20d70773b7f6e89dc3c44b6b5f15ad250eb8f5cbe3fba3b2237b8100e3f5d64f043309f9f2a3adb9864b8981e22bbface5e316f711814dcfcd0e3f2aad4d52771910a355cfe3948a443a9194c0ce0b621d2216c65891cdde63a6d9949ea1d4b6ea2f1606cf15672f271533cf16bc38a36a632b07a3b3720367523a48a09f49e5534aa856e1f790958704303ff60b922a3327d395d42d33eeb0c8a66d090f0a884485b578c53cfc6aba4462cc9240bedb2c161a691726dad9650e41ad07f88f3d413efe5df24a378468f1f54224675fa3e70d56dc590a8f691dc9d83f8f9037a7c79d27415433103f72b8a7f17fc3eb2478613b2c2fdb84c1bbaa558d14e77297aeb8c17a369b027de11482f0d381fc116cc1a86804abe000007367643878bc42dc29f1006ce4e5870eb0454dc7168456547229c0d491254b9a9d983612b54a2edc5f94f082b4781cd4e65d033f57495e21836da2b27370bd7713ebbc8e3bc4e66ee24af105af794319564021c82b80b4fd5b6157ca472a0b1e6afa314a87272f5e10a8355ad681a6b2ce5b687e2d9075514fc58842f52d1b37e2cd0331244256c1ee65be16e5f0bc9a3a483a4d836c1ed3b536c2fde41c50c4c6026e80a1e62444970d1bdcae8a0a82b9cd148f10c1e0aac0d16b633c5779485e5a63f2af49973f8d1f0dabdfedb368668d6cdf0c33f3b3c9725c18461d7aab85c4d9bcc28fe30d8e23778fb95dd8ace24dfaf6b24054c7f30e06938766d7e0452346e21a271c33a29491ada422f13f795f655cef706008d168b4a7629048195a930181099d2d79729d8cee36644ba8935b8d6c8a4de37f204032717541bc8fab2596fddb8c0568642771baee37e82e1e0e9f8433625d4632464a9055c2efe3610fff381dc014d935cd72003b3f4cf7832eb454c30af02b1d6edde3ac133fd82b6ce3e09a3f748a01da420e412606e77d7346679fce9c246b50623d231a458800b43ed9ca71c954e38c66cd8e53412e082bd2ea410f21f8ff64cbc18a7a20db60f0c902568a6703e5f76cffd323aefa03cf62266f41c6a2d0b01490e423172f3b62004480381fcbb63b0c7a470979c1b1e7e839a7d43ef6877a58014ee6d81230c8d1066ddb2fa19a3b6352af2e581f2428b176c7b87fe98bf4a16a7187adba38a1ba5ef4cc45d833fd64aaa6757a945d26894b8b8071023bd8071a28bcadb9b83909af6d09236b4180a6af181d5ecbf12e27b7708931091bf6f6248e2e5d2e60405ed85b1a9bc150ca2ee734f754db4aceec767655e9a09e83695f8080ca56833bc3a071128ed06f99d2ce4919ad745b1863fd26598626457b6c6109556ce1e4889c821390167575ab78917e2da206a94fb6fbf6d31b79e8bd95dd911e73a09aa9e19782c4dc46ef2dd4cddb912ebc38f10c1ad9fa4932e8af28e673d547a0b60b269cf13e98921905909f714d1f53adddc375869037a123e7111afbdbbc5d73cdc2255cdd1af507746494375fe444ead8e46a1a59a359728bb0f6fd637b0cf80f609610b3a07ff414c31c57847a071ef11e244427bbfc1b86efdcc12bcb591abd03bb029ec1d81abc7d55f6f515b8469744070f193970b61097caf01ec2c27639bb68a83e7108b47c0e14c5ea05fb90cf8c7984169566a4bb2d7b187f8bc15e23c2b06eed73c8c8512fc5e9f2b341899ca7842a430d0150979acdbf6277c84b94c4476b9e592571b8c3c37193279a2dd67fe01c21d32cb910f27c6206005d2251ccff108072eb8f1a50a97227f61b685a15c9e9d4c2aa14a1e4629ef40d6965f01d5b249c371d01986eff241f342abb29b97841bd096a6e62867b2992126153f925bf6812ae148ba5884d6f64f1526f919a844cb613a85587cc3166b68723593984d3dd91adbf2e089a83ff5c8839e2f2ff2199b492ec62658e1767f99a88571a8b55573f313a34a1b31eb94e328e2ac5f2b3e4c920f1f0bf6e92d63a8f1474f13e9cd9cf5d2f772a4e16778e3e29a00fe0427098edf9b80f43edef3d64c35cc8279fb35358d7349161d06894f0cecf976148a91c320f00b0d32fd0a27442d60f829977c885f187c2cc6f9422ea6bc6c233573f073af3b2951de2c10661050b3d2832ba4f2d2efde096886ce17928e3c900e5a59c3325d679173b3a3930f5414bcf7871ce345b6c7211ef463262ed3146c42536a5612add55a02320aeb6c405048bc23f5cc7246f4f3b038fceb245129b796f216c9bf58dfe5827ac10b4e5cee49719d28e317e7a0a55e08f70478fe5643c95e01101158460f2b3b7750698b246130e11459232d90a2e0cf182c27830c8e7e30d81b304000b74c80a10f1dcbec402952fe88e3c980701ef15d536b68fe75e63d5bf14b97c0dc342a6ddfd32139a45469c23d57d80a027ebc25601c49a77b3fe95c5b6c80af62630fa90047d74b314235c6ae0a08c02b78f0565de9f195f72304ecc4a3780adc0fe8a945855cf2c0deefc2dcc0b8a050c0aec6b5b5c869ff2848a0bbd48bf2e06b0649772f3c037ae7da0541c5b837134a6446f51ca1f962088832656a02a48e2a04d4c0292042d2b0ba3e3db1180b8cdd9b45ca8a571703e720f188e1eaf08f31f4bcfd810d4df609941bbe18cfc6c353b31704802119cd2b24d697e5f59e863244419fc06d7ea0cd20ac0c5fa78882c4097d1b318a8fad2335345fa9b4dbdb881f839e4a56ef8b64dcd298a590d2e562667b65f63d0615e5b419cc36f84c5823ce134ebbc5d353d493b27c5c5d2fa37529f929dfb5297b8948776586c100529d499e07edff1573525f7264e84fa3b9d7ba4a0cc5f91158e7b056515622218cc396a96e44f92d6c2d394cc14ceef4b6483c0961a9b18a1e5b968078c9f70219c35a15f4eb61845e8a242937fcb92404ba62d62102ba014b026f53911f5764d58bee2a221000e2f00a70db31414db63d2152a5cc710616327315a16e86963b9d94825562386463430ac91cc58243a5e632bf60e0464cc5ec8c0aaa58874affd03963c4e24f8bb5b260fcee848376f26e57c09f5b95700469e24ba9d5b1063f2c694a3a071f9e8e308d09212dd170588188502d0eddf71b334c27da53042669e9bb5198038172e106dcc1e5cb46dea31c1a20e7d2299e31a1706ee24c986efb7c424cca2b2ca19fcd7049cc41cdacec138835c0a56cd520f9542a07598a2c0c350963c023118e5934e6511ba0fb654fc441a3c8481cf4becdc215659fc1f89ddfc663f2338d25cd1332c301350388a0fb758533bb85a39ea460292b07550eb28a9c23c9707cf6268ce80ba2efe4304139c280fd381bcc4881c06c041465d239bc223e64792b5bd89b745125e08b25623e858a90697a52bec680485be43a87f3d0422d412298e45e94d5b42c9934c68b2d2e792930c597e30192426865e4261dc4a05d062e124627fe50f8aea47397c2643710ae699215708574a6985d1d1471750e662a90b0800970922b05e50f94e68a756199eead0039618a697c351bc40b03a38d5e7b75d5fd4cd55cf1d46e639847ac851b91f5271b73e8dd010f1908df30dac6ceeb32def354d70546b46db40e4f721fcd07da17d8cf3c353dab9773763be2d85b9677d0e42aed744ceab756ff70c6aabab5da04cb852657198dff1dab1a4efd4580d207cf5074441bcfa25b630e4abd484d48d64811f560b232cabe37634a109679f72ed9d913e5953ca0cb98ad5283f317899ee1d7703dde0a7db879264b7ad94e86068ede0458d58868303c13a4d710ae198c8e51aa31bdcca0389531126e75c6f57fd81c18e7c46f0cddae9e3c38c8f93cce66e2ed8bf620713ec2ab7da758625b338995898a9d008bbaba7f2f2bc0a3c5c289b561c1e830cf033e87ef63919b6c401d12e6871845881369a8852db802e531631aa4b24c8ff28f3290e0e37af8bfcbe2793d91fdbbe69fa3f915fcce7745c50f7817a5e6da0c878c430d75b449bfda414dfb1518c378ca8ef8c7ac944d0598bec8f66473f564ee282d83d50a9a6e43e64f413dd7d34b5499e551ea6f71053c6e7dc4b4044bb2ed0841ece9af2bb5cfbab3286593d58b6bc96f9daead2541a37478f13d1616abbc689e4cd657ba09bfd44eb65b1319848fdcaa178ab146520e09654f953f8a5024ca6149f2bd7962560a0122174c20b46fee36bf3410eda3dd2b9f8e5ccfebe28225d83d75cb152cc646f22388fe1a3fde5dadf277b38d3a8d9fdfbceea8662dd6a98f6b4e47242973f1b630e9b6d55e8e1a5450d2171fe4f75aa96cd51f5477314df4d52d42bc86bd2c2cb16ed49f6d107a0d7a561e9c2d47dc20917b22dbb4f6f4566428e110203023aa42534047a5df87069c236c55e83176ae3ebeab613c14c6256484bbc16c7908d871b83afd179e528df6fde779d5162c69ea4327a6e536a1fbc6a0cd325d82cc4b7880597843bb10950e0b3b9455905738d13729c62a35e1b27e6cd62646e063e502d90a1b9923af9f688a048e062c8cbacd6b1a1ca1e2cb3a5788de7e9ca4ca4d68f7e69747a3bae59af765220ea2948fd8b87f0c21fcf3acc7a2c9de6c4408281f3804975f7f68d185e980097f4c0d3f199a4e0c64f10b437e33f00ee0a08e8e53d67800e8f6fd28b0fd056931bb9e7c371cf59558bf0696565d2048fea63c657830560b7d2e757aee70eebeb466537ecd8043e6d09b8e29b2ed42430eb3aea81a4decfbc14f4c691436fc91d4fd027a5bc0ffd448fc0ee68c43ee029465d4b060b2ea9044b9dd2bbfc11a232a655a5ef3978cadc96297d0f0561620c17aafd6588a8c43c5cf422bfc5c8d8c3b8c2d2eb34e54379a36b262d1922c10f366a595e36529493145f0f6c6c972c6a4cab77c1296a7c3d145cb3b40b2279cf24bca19e66ea30d3b48614672662583357a58133ec8835e74a9c0e8766446310eb32e59f87a07812ea422635cd54c022385e92d4b89fa29259545d612ce51e11cb053f31a9c51a44243c4d88e0017192ab20202c388b208acbb1d8e33d91ad8948f7ae7a2ba8d91d86c55de637b43ab6120a25faddeb834277c1c21e0081787625fc86586e700d93869d461c5d0931614ef610b1180f4ece5c6523fbb052428be087ef0849a6c24ad0013192152c0ea244c851482f9f06c27cf09a81f44dda0f7da6e9c6f3056c47d2feba78e4e72909fc2fa1f1fc8400ddcbadff3a99103a9200cbcb72119cec35bca33bdc1ed93a36750b20fdaa5ae3cab7d989dec9e0ae3af43d1393f654f035fd0939fef6bbc009d07d208fcba62a6d77094c898dbb386d544616b23645aafbf9e079d9965715d630b7c7266220b641d41c3c2b1310d40e28ef9b208ab4df81f8741be2c998ec2ba16cd090df1bdc5ffd6248edd8014df4006388595f2199d9133b23d3b5ce76ee05ae24ab207044d342d0fc4392a1c79b183b1cead382d9f4f7bd57ded14ecd338d1a2fc1debed20c6b345a20055af6f9846b9f1ed0d692154b7813c4a4cbe42c995ea56bd2b9b863a2f15d20f9a5841c5c812ff738b5816ae380535bba3f7bd1cf245335fd2f27967bffdcbddfdc1c0be7075e05ab5b84082ed78b79675c8b1cf614f408eeaa6116c2979dfba42dde57ee95eee8d005778531819257d898a23abac94d255c48669425a3896d26886ea9b9b73aebf35de1cb1e9d192ba6b23631310e4003f9699b4e99996912b220774f3e1476837a5a32ae82251bba18ef085f42e84249d42c7b54d9000ee848564965bc80cdea1301f78da660e4475986bf22e581135c9206a814651e1ee327223037d1a536ff44b5339b4171ee2489cffcbc06df22c594d780ed6025b91706b650d8843481d3305c5135a659c8aeca6ad047676bb9118f8c53beaf4c3242a4cad64c6014799570a110cc84bf9fbf6b05c30f5ad25febd3df1e7e62adaa9bc9192850d71dbdbb7b810639a47abd7de85fdaafd28668d42608dd8a2e64325566d585a80c1e1ed9b49deae6c83d27e9b0d0133ba7319fb9e8795f0c57db966ccfa1e704d68c00e5b740b133c6e22431ea98a033375dad8e37cb66a9bdc7a3487c47cb7f98b1738f473ef19533711ddf1ea7e5793fc24c4502836599825031d7d2b9da9f934450fce907f081db38f9c3f98a159655bedccf9de829012036e7c992296c14e9de92052f6e28a38c12ca826074c5a3dd7a6a073373902f47f4085b47f019d3e63916374496c09a9d9108376c2a18d87fd88f182b7663f94b7d4ac8a2434cb711012b5c0a5e07b3cfe85e768b08641dd878554fffc43ba0f6f5593c997c9c659732fa00830abb609356872330f84df12fc38e2d4e4ff8482474f9e3013305006419e8916e0163f1e43d30e39a12b6bac0f1cb48d57904a0f898b341eba30fa55d68bf084836e78aff38867f73278a0c0fbabff92154da48fbfbd035460b5cbb4cc7e3da2e93a1a229e92daab1cb18830237016bfb7f7b4f2c1d415af4852da2efc3d8e7adbfc7a3aeffff24a3c144df52fcf9d30e021bb9e8af5cdbfe23835beef09d20db03a909444237d24b130dbba49e1114ceead5ed82a8e8f60961adc40d62e4764563f3db4b18c44face71bae1fb5f5660e7f4f2dd338d9c7118f07ab7d906240eaf2443c00dd411af52629dd056cb5286050b398742942b0e2cdff62ff4415130803e638186239ce22e95a5eb3c604a5b46bdc0789b11dda1c96100565fdfb7d43b5e1e98bc0c19ec7e530169f709b9b4b0af39058a051787c4278ee61004289c1205f8460b2feecfe036419dbcef8ca1b1222b5943c2a76b569cdacfb222941526572ba7733da4fa36c66ab50e2c65878e3733e049185e7e081ff00f9e495d6d3a84b25e8fd426b0282cd832b0f44d6a3a3a3e1795e461221f224991b772366f9b37e6cd1e7100b3da5439fa90cf4e16677e6ab1f9455ccf0dff323e8de6cbdb6c128fee349a6e546c17b03b766bcf217ab1ff84ad3ff08aa6d552f78ef4c9ff0bfa17cb0c6c6fb72b5fd524f707f96c9f1956a05476849d7aad71acb392134941c43f49c2d9154d058aae7b30de3f08f462edc3fe885946c1b885fc0a7b7968fc5e77dd240666027359400d4d148732a325b667cd2c387155bf89bcb369721d3d193df7c8942161966f8c0bc48cefb6db88f19c6df4040f11bbe8c46658a9d09cf88eb4764969984be2880a9fd52ac54b98c855176dba69275f2a9dbf8a1d184f7b9341a31ad8e27f23329af6f32803c300c3a11e1b7c78fcaedfaeb82208a65793c5f78f0ddf743d5555dea428af00e8777efeffe397facbc9ee1f7f0f428c3243f9cd27fea1265fa02ec8f146322b92b993f37a45de4a819a94fc3b639ee1f1846813532ad17af805d1be12cf485e8eb92c0ab68ad55e6ea84e836092946b41134dae9b57468e1fbe53ab00bf553993da04ac4f2810abddcf72eece18b0c61fcbeb68187d7ceafb738e1ae378b9dce96656c52426cbeb63f7e74f486a7c6b2f7fb1685ad327da38d8aa54a8ab0eccdc17d2bb68f95344bf032765fd51d44f4ab24b2dc75e6538a917ad3a7b1ee55828f73e4c3426f6ccbb499c2a79612e2582fa8b5abc8020a67f7531b2582d62bfd0d0032c53a2b282c7e768d64ad7578d61bb65f1eae9b721e13a74c8751c6f4564dc7c13ad833255c182f0f29f11eed0092d1a355d255130762d61ca254689a8a07d032f73c78ca3ca92029623d8f0ba52ba6af3bb4c5507b4a6001b22534e448b89e6c870a547d5b1422663d99c8fa800c62b52ef1f283b3594f7f198fae27db8e3244642527cbfcabdb169450b30048df786e8511362e63d332b3236c4c7011ac5b9de6ce4fca7ec3394e22a949b09d8ac1cc7e3a9ef7870bffe9ea5b1da5efab9e692ed32fc683c2b8f90eb59632131d2b105d43293dad57552566666861e98d338cf401e179163f8d0bc28632da6039c584909358c7bab15221f219bd793ad497f796ac131d962587736a7b3dfe167d099b75931bc7f467c75fcd1211507c713ad77220f8fd9ee20b446b1a8be839a9e9f7412ed2b0a6be00cd6de9a88401f432ce9484166c582c5933ca32b8289dcca23d8233d76e073d9653b6801751ee504842f1274324ca3315d189446b2a897adbc0de9774c999f3498c971364906e239a2a0668b90448da4c50ca7ceab342ca3002a110cf1650c72fe4ee7ca30ffce20d953610477866f126954a5ea65d05055a05ede3ab9f0e22a42060ce4743d8d300856a0e195cc0ad34b64c677a8102ae6d1bd731f302e3bac30ae245f01e455d0fbc73ef85c2ce3d255fc43f3250c336abc26188d4966e4b462c0a501dd995be95e4dff46c2a8093551154a31fae13043aef9903a86654f24eac4a430e0bdd466d1d02a1d9afba96d3667456df4a57b2a7fa7630ffb508b41f2df4c800ba83b9fc1762b8dbd2177eaa74cbef91da38660943e50524bb25c268593720eb6b5fedbaf95a5d1431007809a13cdbf68da181d29427a064c9b80f1bbecf698bd78d7de95efe39f2a60fcbf945e816cdd9bbc558319487c73d4b0cd67bceb0a3beaad656d3df19792b845c66af3196b53aa03c543701de37e3bf50d1498c623ea12315d7639c15a6ee0c3859fe9dba97b64d5b0370365533e48650a3964b9823622df96eff31a37217146a0f17fdc2d35f16f48f36d7753d9519a3bf6363e15e952e101c01169284887c7aed7eaa571f8fe6264bad97b2c2c3db56e57cffe5c24f4010cd92a1ababf58427cb595bd9c393130efdcbc8369e59f040f9c079b987394497d426117c7ec3a04fe9ef8f5337a8d548da74e14700007d12fd817fdfd2dfdf9b7074b853c58335199d8a3e56b654792bda0ca96ae7d47d4286bae4322150e793e336191660c175d2275e01b04b2c1d1197ca08be3ec3f8bd79d699384d34c37f71483f5f06548f830ccc562a109ef843f5a19f5cffe01a525450d67fdfec055ce466989c9daf0318b3365443ea63880d40c25c130daabe897d77ece278665bd8c8d28c7d7f4ac39f387f645693807cb1b8eefc0d4ee0f5ae92bc93c872d3121009b5d145d452a4077824d70e883a9fcefc778bedd1b46e14e2f90666a3ef920cbbfd70f263b887b4aad22a272a66ecfcfccfba0265774d22c80600613e4511ad74d61d52b530d6532450e5f36007cadc78a4ffacd39fe7a782bf7f93c69a8e2171b0c58bca4fd223b771e882b8149010b2deee3e86c2a601dcb55d5228fc21aaa6571486b8a554363e17cc8a39c28d39a785f58e0d8e51705d10e31eb74480cbe1e53603e3d4bff5133d0f8e735bdc7efea3b10b8f0f270e783f1c2c443418bdced5a5769c1fe0841ddd7d6b38e3ae01e05d3cc6829ebe8c685d81ea715b445bc4c4e8800cd06485760b4d8ba61af536bc92b40b6b25e19c164303bd6346a2336bdc6da67dac9ea38eb8febc5f166a60f2774f37d8ce47c581be6e8d58c48759a27db3f69f2b071a442f32dfd5d2fd96a6ea035a9f99f39db277bdafe73d377e4448df04a40297916f7a75fe63c334474dfd0459903eaa0af15e7803d8d1d211a656e0a6226b5aefe6e6bece08819fad20b964b9542815d8225c972051c133292873747fa37627ff003899fbb57cd17073448cf48e3f8140ff0efe07b78cc68d23b6bc47a9ca7a64005098a305424bc55ae1fb7ea3de6ee5a10807128f4caee2c83bf3d406b926460bb79a0971cb8b03c18a6ff17c49d9587d92aa2c959e1b35d52dfaf209f8673ca709f9e58e587fb960120837028cf9cf6bcb31bb2bb2161ae99de5409481eca7fe2e6358ecd632614703cee8bdc09a471cd3ca233ac69a23c23bb738fd5f22a70efea5c1ba1aaf07a35f5b781b2ed5b757930fb2ac7786bf8ac909d391d53a8ab3b93cd7bc21d7aac0d03282be0f2b7b14c75828036ebc09cded0ee73bc7b9d790debbb29a32bf4f387634d602edd17fca75f07ce3b81959c13897042d4b5f133694d8321efa020b9801c45404fa41f39f5c509419a5398593fe6a372566fcc311dc0acec33d1d36afef6d32c2592e42e19e9345185fc33501bc79dea93721eed000b9d6001c1473ac0e457d4fd3cbf3e159d8600d01b4c1316fc0dd281d33da2364adb5ad9b08e926424d48c80ed1097e0a8e09d443785b4122b1dc62b9c562815e412c96153a686459a183582ccb0a8d2c1048c5c22a2ab734150b04b24020ebd441af16c8aab7a292d05f0e55550e4f4e1255c83ee482f74ac9945cdd6278abd94839fb48db7ec8a351663909f46e125e31ade02dcba08b40771d53faca0ade6806e1ada4b97e11e1fbcc7da4c9ab681b0d29877e43a1d02bbcd9871ed2b0771150130a1d7ba689dedae8206d2365d061f6537ff4ac475884419d4121ad7329f413e8fd10aee10b594907d0fb20dcd73352b6b8ca1c8c1f48a4c68e1ca16336208e20fb68ca5b8b0e80a9f3c9e76ca0a2dd01e3618c3ac85ec708a183b3012e368897af527b645e1ad1c64c81c86a10a929bfcd396794a4ac6b4fcbef6cc8f1233a28262094d43729a5d4e196646fb4e449d9a28b91b68f2c5250f65ec07818a53a62f6ba0a6f31c555472d55db48b9622168ecfb089d22466a88b8281c1407c54191c9f1801f361a20b2bb1b22ef0059b5e28d013cdcc8ee260bd7ab0d59b968a4b59822ab2cd259ae72457f9be6fb2191d622d3a6799266ea977916cdf54b7d1706c6a8bc2f630269399c0df399d68036cd631a8e36cd5f1a03da347f35529be6ad36d3a6f9aa1da04df32b1aed97e9608c8d7e99f3cfa2306474444d8561ce39e9dd3cdd6432de64f2d43697956f3bb273771da3f20c6f3f64df723c20bb8f56b403f48b4a486b3145565bda94cd796cfe9a6ffecedbbb8ec9b083338d695bb691ec23d18d045f614c451d75288aa25040390d48b739e71c941405e3050f323254c80eb2779eee1c8f810e4aa64d0ee64584919d111e18f04424b5a4838e981d761bfd362fff3095eb1fae4a3a3c5c932b5cd2a1babdc55bccf6151622661a995cbd26577ae9c59666b52aadbdb44e677b76234a3f29b6d126f7723c409ba43c2571c31c3b96a8ec606eec6aeea32c6023bba882ec9d4626bbcc3d9eea5ca8cf381b4654a4282a6b40a61eb5cd25c7c71464ef91c625531465b2342a91bd53db8b176d72c72419851720c8de2384ce86aec9714b5ce096d8c8f1508b48b217a91262483c3803ec7036bc0d868c06f4cb939912450a153fe8a03a9751c7bc782da6c8689c41b6c164cb552d87371d5a3866772b0bd3d84c298e3a43aa8391811e73c5808e7936f0a5b2d131e49fe3a1aaeaee2191ab6f31383c1c7743f58d003030aa6a5655557527a9adbd6c553d245655352b0bebe8e4e058af5ea6150d22e415794628683e3923900c2708ae0ed222ad2a1d505555a057555555d9abaafaabfe743ac63b086fb402e568533feb634c2e984bcab66a34ce867e4c52555555595555ddb954d5ab4260679c01807036bc3b8cc268c93019144382f722bde81832379521c9209301598b46268a1bdac34c872f577bf932c9c098d0e161e04316853133d90d06e030bda0870e0ab4ea21aca791c9d6e1dda1082a28e10637c8340e4ab5defb84d2803883d8c717db6808f8225f6da69819f8225b44c9ec6fb489012da890b56842e6e66c2d89231da3b174b948c7303224fe863ccdb0c51b89a7c51499fc468a7936f29106d98d8e612fcf001c1d8348c7d848d4adcb243ac6ebf16cb04d965847cc1d5d9c0d7d4ab3b41653641d6f9049297ff8a263dc0c79873799c9bf251cc56283903a3c416a3a8aa2aab6c164776703f519f822bbf716e9e6925d8c97c26f937b51a31186b3c1490d8c600c6c7634d689eadca1731792bdc71823d43a098eb649b30d4fb913f5deeb8e60a8ce456af1496417d9eddca08a1b44b9c192243c7945763b498ee40a2d9544140283344e069271dfb469da87e5ccd65dc788469778bb3ee9af48733d8a14c6880e75c00c7b4882b02ae64939eaf7efd638bdc73cf1c9e2edc56f3aee8d50b9c2a7fefca4eae9bdbe91d241085dda044da43cff62e7f9438e34397ea62518b6eb40cadb0f99d22240ca11c399bec1112449728565da041fdbbab787be699d981bf1bd23f9b58ff8daa67b3c1c6cda47dfd8b48fbee9d1361dc3c7c3e126c6d6a1daa64df106947806caec641476e3d837ede3dd101f2929576738a848bc8691ba34b0e8834c469b5ea4a29473ce4cc6f73e2241e6f096e517713257ddbcb621f21235f2513bf2f42efec00629474cd11819f8a223d36420aad7106430326d99fa43c0180879902ff2d336ba653993594622c551c798af4e3b26768c4a93f4ee4a4a5d621ad20e0038821247c8142eed90295ca25e93df5a8b29b2cd4d31bd0fd1cfdc14c7c44d99c2439bfa07d9bb6b1e644f888c829401e3c5120bc0cc1d40e820e5b7296e32d9e918c26e23191ac743e10a2abca9be38a5bdd70de25927699678a371998156754e732ef23488979ddb62c7f81c75b996363df6abb91ec593e3a6b8294b962411050e2aa25852c40734d0832ab8293c504c197390f52be5d1cff1e41ab890399ebc65794846bfb91eab8a4c0911f3a4329e1d4fa80109c29029da2f1498afa791103bc6181b26472d02323239cab8e457630b327baca948bd701d03336d3099c242c4ac6d3632757869585586695c76d891c40a4d6042a672a5b90c1f01994ce153bc5e1c42a500954ff2f3ae06f66b6d2fdf5f56db64b27db59f6920a0d536984cdb64dd3acd16be14a0f2897a7c8cd3a1775da9ed79b6748ceb3da30832eba68e5161d55b2667af5edfcfb4ac4df69546db648f69b64df6208dd42fd6fed24634a8c4b8b18e393f48003882124820ddca3b79f99f24d64106e129f71fd4d80ebb148e79a3a4ec860f52d4dd85120709111bf4c857871b28082dd5ad515486d06af2c917d41a3c919fdd0e0da0009261b2dba9c14f8e926d7e8f0792636e924dd9ed1c49221f71926bb66ab5efbd37e0e57798eca41859820f24536001141e33dc2b66eba5bf978ea24a5c89d963ad8fe51b14850e7ac4146f0fbb3b58b9e2e7736b607d1c3dce87fa7b8e14f1166b76c46afdf5c4faa4cce8708cef617caa7ecdbf8ac5e36481b7ced5fbc96f733fd6b2e28b31c618e3b3680feda13db4474adee82ded146bad3d0f87fa09855a558c81e93cadeafbc9f43a9a5af1d62dcbb29c13cbb276985a35008b6f33c76f32b77ca3728b3b03c8f0f8e70c70ed57c5dbc3a5194e6ffdb731c69e3ae7cf4fb68e69a05be3bde253bdcbb3db83cdf1cf7a0663bea25eb8062e64f4176ff4546fc57a0bd75fd8de4be302c9daeb0c9a8ba94d2fe288b57b4d13fd5ae83268a0c7a0b1bc8596bd455b794953390b6de4b25b81dff52dc6874ff3d6a3cfe67a72a4b1795a16de68ecc4a7f877eb1bb51e5dac70d470c99efe3d8ba734365bdf686cae8ebd062ed05f1b56b4fcae22f3fb48cbef222da43d67c33b486b67c37ba64567c3c39b0e68dd1ad64f15be5edd1e6cb6fe0a6f98cfc3c18abfee71d04be36a776913c43259ac95525a6b7d0d972baee10bd99f765aa3265bf8f9bc1be8a94f4de2f87e72ccf0be1f1a1e23a5300d5a113bde0c1abe0306f1454c28336af8610c4c129c183051021b14818a19f0356814be4018dc2c8ce0282acf7eec4663ad452dcb7a3e14fb619e8fb599625485c2c018fb7c9e0f0c0c98e7a393b58a14538c9024c364b763049b4c04273c3c41b2cdf41d65e7083f3b4728b2f344153b4f3cb173841e9d936376d666201d9dd3786381787bf7ee60e5776bd44b25391de3d290d3aad57a7dc8baca47af3093353b5a7633c15ca81b931d12b5921076ba22c22b9d93257e23bc6599ea275fe313767a3921b46033c99b751d7d7a306331cb8703fc66cd0ef5ad39f1d639d96ad39cf824ff3e6350735a3bc8378942ac495c12ddfabc8d91ca2f1dde650feff2383a89943227cfecd6a097d8edc1bea932c2f2163c9d96acb1bd0b4f59798a2fb61d456a36441a0f98768389f5994e5203beded2de6720496339a497a7a80c24b5ecef2e809a81811786192c276510931ea6b283eecbde6d25afa2198d369a79ec1b364198961dc33a60c9babc8d56f2709897a3fcbed18c32f6eb1863b935de4f18dedebc3dd8ecdc0b762acbd8b70cc31bcd28cf5f78734e94b4121cad445e1a36c786c6834ca6c4f5849aa5094580428428b962efdd3c9becf04693211157b882cb1564aee0d242fa108ce4c0044de03165c7882b48a1093f96d80013274ee0c4104d0927445039d101cb66b7c3638316947478464a3a749c226b1ca7c8dc6b724d7eb8265708a1901823ce7b144551b86d80e00406e8e5822e54970b28e0812a88000c4610010b7a4011f991e427033e4faa68c284db24cae502dc240857132964143c2d904ed4cd7ddb9a9252c7a2a04c3b980d9a208490045fa256298aca288b82375c8655eceeb6015f1ccf10a6e84c8881220cd9c1902a3c01c7a602458ce044810b56f859d2a406b0091215227394dd0ecf8f5cb3dbe1d1804e472185ec2d05c6ccf07e4f81312e7d53c36978bf9398e04b976e72f17e0b69223006bfdf4660cc2fe5417151e03c21a077e8fd9764f4fe63f27870e48031d72f8bf71f0f18b3a22393ab0b4d869bb4185e9f6da1bd1926adc55b60226ac0d88546c303f0dfcd68a17da65bbcbad046a38f30112d2a2aa2128914d2de0c77690c6179487333449ae923cdc5dd5d80ab2eb48b4b3eb8f8fdc54480ee02977c5809c0574cda854b3e98eee22e3011d94db8e4038b9b6ec244bc9cc5dd0c3703d3de8cfaec9015bf817da65f66f80c0f80b603063ebc296cd0048f1900f8cb771461ba8b03e0051761c2ae09b07591fc4e44112e308f192e77215283f6a6b86825fb920fff5d70a9bee4c355ee824bd64b3e7ce52e7806ad64f10ded251f8e3f032e557c83e5251f4efa0cb864e11b2b5ef2e12c3e03c6ef9a764d062d06ed25812fef2db467447b44e0cb7b497b42e0cbcb5868d74824160d084d457b3c46da0f222d47e8d12c830d65a8a96deda052f78a11c92fe42e8e1df399045d2242bf7733a49d010acd8b6e04a4bdcba7a3c2721a43564032f43b6fefedb5b844fdc2734a7e0fdd8a4621a5078a10f0d7ce871ec244803eef66dce328f910bae8224c847ce86e461c5d04fae8eee290171d079452fce835332ad484e84be0e52084080287f5eb3486540fc2871df6f5d87dd8719fdd027dfe5d1cf2a1df30623e543f1fc225ea2c984889c23756f0bb0ae631d2704a11dfb079d9de0d6c1cf8f28e692d04be081184045e0e228805b819f30b7033420f0247c907ebd8314cc48c791c251fb0cb4b4cc43d763723f41a0e74f90ababb38ee6548a35aa69566dc05b8194658effa606fbd2f8e7bec1047c591e1b629c15f9f691bf0e5dd6ab56f4a10dfa830c934b552e31b30f0e5bd664611a19ad85406d5b58c8c162895c942143a708249a40208110a132da801146c40052000a10a466022454f1576f0440b459c9042e50416560c41c2c7069ee040912858610a31d4841430500513306182c78ce84410728c4e4091a328c716e4186310302085bb0d051821cc828f76c102844d4421c31e33e047ed82056cc309d995dd4e134ac89bd389541343c854763b4d34913718258f052cc86e0a3c921041093eaed0831bbc40a2090ca0000181901754f15c9c110aa420a4702ad229221542286113156428336c92820c6f23158ff49aa841ceb2db6962053c728a7352647707812636412041cf8df7668850e091342ed9069e894f68020579733c3726e669a204110b2e8a26906802484fe4818415c6fa1ee92550f3f0099e521a557d83596699658525dedeabda467d0281481346341326b999e8e4eeeedb03ccdbbba5050173788f9496d1c89355773d5497af68f54e9746b46d7a5de7a531e5d3e1044f9de0add4284a3bc5574ae1bb528a8b0b1162741982109a4990f8801a410808c10d0521e0079336b95cdbc6528cf0158fac050615f9f1fca00acc081329c8db7f5045de5ed02093df9228f22623c2aeb03bf76b74eedacff5d538329189b2dbf901063213aabb2331c1e867e364fab9c426cb532d3e91f51fc992d447f35675f90adb4ce9699ef6d5619ed6e0856ce2d3b46dba099078e2f7293e6e8170489527758c968f769624c9f1d41222790aa4e7b0e19636d95ba38210f0630a26e1937c7f93a7366a87bec4270a5f044b0859a209364b30c18819b2db592267091940f88610041997180419572961ca39e52604074d98c207826c41891be508103ab0822834610a3c9002e5c115620a9ad46420478a1d298450042460b8d1020b829083273c50d844412de13e21324de6bc6b92e7230f8a90e79469426d763b4b00915291deb2440736d02e4e2cc1a3033b5dc840b54305e981d73cc8d94193b80417171e70214f9739a79c73f2400b796260c674894ec4e86407093966b7b35304105478677a8720babdc83d6a530948862f513807265e6024c7cfc018a8638301396880e4be6b23228f9cbcd97cf5f0e0c183c70c18b81d1d27797351e814c979c6e6a6202004503404dce46808b8d17928c87d820c71698797e16bbc0c31024210a54d2ed7e4ecf01097366550fac6a742082929e7bc9b1653f751d2468110cef9be34289c529ed2240529ca658aa2a886f31dc46ccd554fa2a0b6cdeb9b8eb17be4f77e7d13e4b9c718638cdb73ee6da16eef05d916b3f68480d7113375874b3a3847e396e4f8e79eb37235379b490d23256d155c2c8211c9aebed0c64a59b29a368330b31b7d346ff034db3675cc2ec60378a8815b0fb2f79a6c39a15a31d9dd9b5169a7d1af72706d82556357cfbb5cda46ab8b2d2d1b65ab862f64b26f0dd72648a37aacb0835352cb7a4f4dba740cbb65a35c44d75c1dc180399736f59f466a534b471364a603d8380d29c3bfa8591c649b29b749a325c83693a9bb509b9a72ac34d72f73420821ec9c28f1887853e34dc469020eaa48d2932b06e30932cb5937937afb7837384aeb1e99bac36df33cc4ec1b179d36e3a5f6f20e8ae840487e57ced8d6c61544f969409b287caa990fe2a0d6533230f9469ba828de7befbdf79e6b138d1f668e1498f9d47f0f4aa1839b9c9e4c2355d5fb53db7ec893565965fd95b6912e6cce3b4bd6d65bafd60f799e8624b71ba4da7008158f4a72f053e500c9558e113948a2728be5d6f56be52b4ebaa18f1ed2440769fd1eca01911cfac519e5c026674a886478aa444886b723d19b0232646cf4907d0804c242a1872ed2b6ecee2bb7212db4f2990e8d0e127df490b6fd9043973701a06f3487b26f590edd754cc9be272043ceb692c508a8c9192e613fd967b7f6879ce1fc431e3db465a1958fb46d745168e5217c5ab9c5a7fb506bdb08d390f20a065d52e85e2c0f34e0ab4f63b963516ac96a7674cfe10d14d2cfd1f7608bc379a2f0c7c3611e921e7d2bfe56e0ad211011aedf20ec0bc29a1db24221799315d6c75bdf100792be4120cdf2ed8240b23cbd9b95059fe02f4ab3cc4188fbe7c1d71678425cba6658945a20ec81370f07f80a21dc5a5c7e8340f23c8b86e1137cd7195a39fc316b0b3c2b9ca15a7009e34d07c502c325f007bc28843f20bcc9f59de55283afd849da65d16cf750ddf5f007adafbfa7faf97bdd59e9adf0c7acac19f8e3e1502fffb46be607fcf16ee8813f2204327300f863c563a1c28c54e4882dc93a31546e8c910a78a3146f134f3b635c222ee9188fc6a45abc349efda857b46889e17d7380d8c311264fbcb980353bb089f19cea8bc8d18d302c469d392a4df82803d6b6b824cfc7805bf0f62fb4c0f1ce451c4bb3c6085f1fe7c4b466c7ebf917794ef145e41971bc633832a123bcc525b96fd25680aa4cffe8e1f1693e23fe055216322a647ae78425647ae7e4059d8f9f691d108b2c1e2b8e4be052cbbb69f1ea0e5e198b76d27c38cc3f0c8b4b9adeda1a29e91abe9401c7805be0d88276488b07dd5b23dab8445c8234cf329fcd5f55bd342e154f6bd11e908733df9f5127cf4ffbd6e2676049038db4d15968a1b3689976bd1b1ba794fdcd45967345e5efd68020797995cdc5ffa4ca57b452bdbdcbd55379af5259e2e1e084fe64511c150fd3bcc8d7ba758aad4c95632ab8547348172e7e12a947d2cf1719fb46f32257cf46b8062e64f7222df440d84f15c5fe6e60cc732feff20659ae20922548c8c31bcd8b4ce3120f07ecfa8d4bdccb4be32e7937da03d22649715ce2dd305b5e0c531e6c20f387c5eab1533452c11ae610283bc4ef6a16b01b421677245ab363bef9a450e84a10be57a2335cb40429f8091f841042787f1df63cc178fbf5d03b03bdbc2ddf9c3de7bc77ce396997600b6c0163389401fe503bc487507bb8e472f9f99f869ea6b4e9e53c0b16ef7a9086af80354c1802652b98ca4864219cf8f9d440c3815cdaf3d1ee627a0f8b19347bac85ae69a05fcb2e83b6f21834ec2d3495b768a39734d167200badb220611696c7f2d72ccf8752bcd16c149feea317b78b66f964a17ace1595912804cab039fff77a76bccbef42732e146e121251268c9223c5daad73281e604ec6dbbbb08db4928e71a06bf7ee208867cd0ed849e0ad6632a15b01e19b3acf5a81cdb702c2d2e12cc10721bea0854fd7b143ab66471146e475f40946cb7a4666b78eb47085ed5b0bde229df6b3787b4754de1b43cdc2251697b77e1833035fa8b77c650421fcfb33d23918f66b6e34b3dc35cbdd5b361dd1c216f8533ff853cfd14a565a4ad8b61216da016cc0077f5b935fa1d993b4cdde67843acd4418845bc99b6d59f2bab4183e599f98b597f5acf58eb49279391d8a878e71d6133a31da4b473d8712a7231ec9f246e2cc017474a4901e6908fd2a27551158ab7ceffa7b9354244f2c5bde5bc3924d85544a54452e8dce78aa5bb74054a06f0e4054e01dbe4309ab24b6a9908ac8ad8c5c888d2a25ec3d36c2ee67fac9f71e762fe57b97aad9d1555749f25675bc544845e4e1d0879591bc55f3e293bc6ff84a6e3401f2ef5d15c9d51d7505b81bce00a34f2c823413f66c4d83b55fa5c4d3361a5c5279f5d93acd04155211e9aac85615c913bb351a7b33048dacc8579fd52b4b5bc804d4e9c6f29591ae2cd5576aad9c484d45eb273a8aea15912c3f6f8dbef56aa45def7b91b6d91cd284b4ce74316d44b70e8c813a4cb818c884930c64b3ec3d5b2951d14bd9179f5e5d7a93a894c0a813a8039970312ceee5f558202a255c4f7e154be286b3e15e2cce4591c11805381612884a8987c3ab947837c84b93c8920ae9184ad45a15b19aa79f689e4a09b862c9d5329ac808a262e7ede7314922d1375b4a1948c808d2315ebfbebf6761f13cab426bdde269bc65d2e2c9d66bad6a76c0ee9181d33288b40b41d82d5f5542240c89648bae4a46902cdf8de561b7ecee866d5d29af9441246ff73afad40d2d6b5e068e0c2179b34eb2433290783282e4fb383fe79c73d667ad39e78494661685035facf7ebea129746bf9f998dca0a8b9497f2dd2303e741b8c90872c163d7b42ceb160bbc915658244bfefe6c3a28fbebf5c3e42ffc8c086ccb8237d296512a78a3966d22b58c49d624e3699375994c5a329078375c19385a637fb771f7b449e2c098cc06be50a770360a8762969424997a66036332529635a62e0389ea9a3f970c24a8201956f4fd7e2a7ca2ef51109581c4bc9b252553cf88c018122923625141a8205bffe46ba745e1648c860af29325751948c84062ced93d3290901124639235d198642a632203099595d7028a95d6b9ae94d4d54e2dab9494bcb56fda47bd176f7d53f1e96d34a47be96d9beed14d6a1f1da35ffbe89b8b9daecc9a1db76dbac7ed20533b81ee95b381d86b0f6a1f0f879b75ab4cd0556cdfd09c51903de80dba32a2db37f7de1a2b7ff8747bb85719ddfe95b922bc6538d43e6464586d46a67d6058fbb878a32191ee9b58d38dd2ab5f46a2bf7e5def5646e64a12e9caf42693e3956f1feda36dda878c0c1999f6217a222dd4f84ad14b73bd6cbce9184042ddddddd7bb9fbd3b58b96f0d8bfa73f9e1748c4b8392d5a5f15a68f294365f5d0559f9de3b7536a7a4b2f729e57c854f9489a4ad9052ee40d1c05c6dab20fb5890564614ae6a76508ffa8d49502dee8d37d51683dc16f854c5f0d782aa28194ec52003de9a7a2adfb44cadc8148886187eabdb0ae6e1920c1f02c32fcdf0168aeae186faa3a86769f6d6a02a855b3e03de64c051bbec6ff3b26ed594ac2dd44bd433188c4626f18821da142f83b6e3f1686a6afd194883e6f218b4ab8576dfa29534cda24c31c61116e14d061b7a3c82fa46f36808f4be35a81cc40b43be40d93719489466198904430f7a078170a9fa0d18334486371948998df1888d06264f1767c33b756bdc787bb0f97e081853dd39d308b4fc6e336fe211f0d2a843ec8853e2114fc87b8594f93e66dde52aa594a77e11f292725275ebdc14de5e8ffc6aacdd57ad2f77e3beede4a5215bbb77afb5353967dfcdd977735edac9be88ee76d7cd35337cba6e415937c85ef3babdfa92b8047aa8bbffa49cb3d698f65624aab746ff24f1f6acdb83cdfd4f290a553956035d3259cdfdfaf794f6b2b65ea3a5a8592b7ef59a6defc3113bd1cb6f0ffb85e1d27c1ca0b698ebe9b4bf1eaf8b67bdabf5f2d2a852bb4b9b2295144c08092018901f9dd7f37eaccaf3793eef07c88aa672d18ba4f5a49cb0cb82625da1e77361a3d0c87a3f8de5bb2dbb621da34f4adea845a97551abada52b166cba425730fc7cac52e8f4f330a25f0c9b222c4fe1b14929b4505e20520b693064a8f6540e31aaf27cfe636f877f493a468e559fbc1b68eda1afc83b92e1dfad4fde0d6f5a442c4864d25715fe230263306dd333529f4023b503d6554a720678d6c3d4d20ebc9969a39b6c733d397a280d6427eb9415c3060f37188063d64ce9ac0703a3656060c8748c7691ea80cb91a2280f64f44fa6636c8fd240f6723ce0071a20740cd1316afb78373c71f3438e8ee1769eb0c9f00f88d23eaaea6cfb7838bc3a4d3f1b781635786193404d8e0a53ad7dd85779d4f72822310928a4834020f65edad3c39bc35bf1b9f28d6485b01c2bb07ba998877bc1b07b09e18db4b2a239f712bad3409fa1518f0e4ab4e9a0e22d7c338b86f0f662e891c2d6378b2dbcf50f864fa157975b4d089fe89cef0a8542a150844062ae3212c5d04f4aca3436fc417be00f08a47be08f10de5eee8137b407fe8040e08f50a827047f1cc6ace01c21bc91584e62c150843752de9efb057e6455893797ec72d059033be899f6b2abb01c707b324a29fbe6602f9f1da4617f3c6856b3671aa6c5eb85d46060902004027f0819fd20c7f7737b00f20364733d234a299da7724e8a52207452cf474a49f1c8e59a6953ab959a12a980523a4a0fedf96913dddecf4fa67564610f7d7396fbb13ecaa00e483da439f742659f075196c2a80a660cc3a7b655fcf3a1a732c35bcd8567bdf54d13a534406c2db97ea3a1a31cf1a952d63aa60d11895ef136caf4204df4a95d9fa005843e3abc342e4cdb443f03abb499364f3fd343449736af80d0479f6911fdbb1363da1090ed975a7f6da35c3fba34daa67a9166fba5fedd1c9e1d1901030cd386804e351a96d452df03e1ac5f1ea53f3e3f3e3f3ecfe7dd104f3579797725850f62d273a4c9112647961cd139927324c9ddb0539fd72716c99add7a08264f4a3f2db678a379ad206c3369557caaf066f10693e38575f476b18e8e54e353f55af143f26ea070bf4292044924d1963f6abeea67a359449695a440cca77a186d824f82240992244892204982e4c894c9d62279491e14991998f8903c242fc943f292bc230fc94bf2a03c240f098c8e1091068c155f807948421770a18a442230891692630c9263c411479ee2b40824ebe3dd28945d2b3492e6a0061f7650946519e28d94e5b10b637184c9d1bafba46e610a261e71a2df21ba0a6f35149f280a479c23da0461641829cd3212e987312a2cdf9ee737d30cde686056984867d11c7c71f0251e116f6ee21111271e116f22909b7844c4a1f18878134f6d381baa80104208e31152a89a50635f90144f6c0299f492a793e77124c9c9c9117da3312adee83117e3babcc49b8b92fde4ecb5ba670fc9679a732fd6859dabbf309a51a6f617b63e371d30527cb2d88a5d953825394a6ae428c95192a32447498e121c49725c8d51e48d1995e0488243494e141c4a727024c1a124278a6c2538e261e00bfc8dead0ba2ac20ce36cf0024c9691487fce5192a32447498e921c259713ea9c73e2d7a33675e9bc9b71dede9e5655dda8c701ebf5781c8070146d1663104f878db8e560783de0e340dcece1cbe3c0b3791c783d1e0738703064f00e421835ab2f165478237945763b4900922bb4541251080cd23833decd58cd6ac6e7aa5fd5e9651c1014bec0c32cc73c85d372ae3abcb98ab31c99c3341b6643b50dbbf23157f8647175e1e8aaaa92b29aedc38259d4649431de1ce2650448f914f18a30c47703b7673341ce3977a3b22bc2f0785c0d0ac105218496822dedbdf7de7befa9de61ed04bbd65ef06af6c22c76dfb63ea94fea131a5888dd7befbdf75214f5db1262f75eebba17bb1abb7dad29a97743cc5e5752d7b517bb77fdcbfa581feb637da61030e7124fdb7c7c11d4a4a88bfa65e994b0b26e597b416bd96aadc5ec312babf5f65ef6b2ce627b36bfb20c6fa16fd947bfeefafac88a30d636843709d46cd7efb13b78f8aab58f77835551bce56cf600abd931a968a5643152998512ed8ffdb13ed6a76647dbd89e1028c687429f695028b33fdd0383369806caa82c943dcb1eef2088647dac8ff5b13ed6e704a02c4627b146c962cc5e9d64f527d69efa2466598d82c550963d462c1ecb4211db9e1541f291cab0870ed2b0080a81b2faa43ea94fea93fae4f1b8c04e128cd891228a1d2998ec4471c4d23bd01f8661ef1e1805d24e9b75e92c0ca3260582bd9e450d5cb0aa5377864020d0bb07a8839c2e4c04021dc342d84313848380792b22bbdbc375ec339d810ec2df9ecd412211de540ec2dbb3f9263a0b049d056fcf26b4ad845656f0a682439b046a44d94377a1c710de4098c66618c3f5d42fedda0a8f3eebc8c6d1a321a8aa5855d5498d32e79cf371b2b0735e17765d57766cce836c068542197669168baa3e0135fd0294d04a28e79c76ce9a1d765a228258734e6bdeb2d64e6bed2dccfa56fdc2be55d9acd5efaf9b55d7bc339bf3baae83b4cd1ebbe66d8b69ac55b58f91a5554041044209218c22892c80ec76a09002217db96ad894c69a1d6d637b2a2be544dd39abaa623f5a287983b1a2b6aa9234aa43d1034592aaa2af2e35124956186ceb2cb08e36bbd65a0b6abc65a291e8d84778a37d11f61086b70bcf8a71d5b73e238b75b5232477ed896d6975126bed1192ad9bfbcae6b2163f9bcce60c04baac66ad59ad5955cbaa4fea93faa43e11323530a9fda9dad277da36f4b6875694da1ffb43e9dfa52c70e0cb3cad878f876d9c8dd4bd4decb37a09f1c8933ea451fa4ca3b272b6062e6417cfaae28d8604bb4786b73ed6c7fa18b93d7d80842047a022574c4a2877a0c0c903c86e07093fb42d859492cfc51d243cb1838422a1cf793b036342d8a52b08932e8adda569e8187da520cc664af1c5f6dbfd35318dc5b53a59787bf69b755b7db3f021c5a74f0a62ac6706aabb73ce15c082a0d7c41ea05f28e8c347d6c3e95832d9e9157aadd7b27ae5c2dfb656ac43d27b05426bc56d1515eb7d12e99bf5d78f84696c5e4172dda383c4dba36fb0be99ffc49bf5f92d9b58aba84c3aef8c17ed430b931a975864ac0842d86d59cf328b64cdcb8c4658fb28416c61163760fb802f598859acc024ad7dc8b0683266647c642bb05e65a4659acb165f18839eaddfb03fd0c5343667f8b68fc6d8b7cc62cc6618033dc340b854dd3800c523f3eddae08b297a23c3ede3d93c3ce78d8c0c8a3acb235900b6bda93dd08893396b4f6fd17a105af549ede91816ac9786151ba735fb7a21b4ac0b6b271676d5271676a9a8d49e6c852c782d4aaf9a1db246b1dab22c270f07ebb0febc8e81c1da93e5e7e7c4a717a2a1f4b22c5c1259af4e2c086b8f557bf2bcb5f0fa757b98202864f47a45e55ab657a0905619612173da9bfaa4713a480ba94faa936c594b9333b03e44b38bfe5df5c983f67dbdefb5d7de7beb931bb3cdbbb1b49fb82af2fb3d36639ed7a27155d0faa43e7937c8cf6b9fcc597b1a27084ee3d42746b29ba126f4568a501f5c0281e498d9be1552af7ff0f6585efd0181d81bab35a8342db78541e9d08c00000000003315000030100c074483d1681cc9caf60114800f8c9e4e70501889c3284821658c31c41842880c008008c0c86800002320dd315cc7825dd6b40531b785067ee4e6461e40e7f9f49e6bdd874fad613c3d8874c6a19122504b798406790800ac88155b16a2be21cf7000ee421192b0c0763a10c26df02a0b5261c0dc965db4ee032b2dbd20e963fed1665b2a79db3f0b3c492642fc27cf0ea9b01593dda04261cf617969e5d8a51644c14d52ad608154090e8a96906d9cfe371b87e3159923caea6fc1ae1edbca829c7a47cd0807762d3ac9bbb7492c77c451024660de94b6da4b69caca96594c2bcf77af86d6b59adfdb6f06d3f7fcc1c7ae2981cfe0140a51d7602cc8964da2a36370c5aef5bdce695062876f754edf09d8d5b5994400bab6bebab60606815d7af2c65c5c466bf2ab1ee1e56c6443d8ced2fc1ea4213d7d3c2464a93ca64c2bc174e09caf0e32cbb89253ceaeaeb5f2f72af75319d55a5d3b414c8f84e040bfa8fb3c222c1e79cd4a805ed338afe1455dbbf3b2bff2184ce58d0fbc5a090b1ce724a2cdfe8ac7381f21d1ce7bf94507fdd3b5ddff721006c834342fff982ab77c5d7c670359852d0142d2bb49b3355a16a4a86522c4e05e5fd5929ac045005f907d397cb2a0278da888552d74c483a5f700452a081be247a6f513c2caedbb575d47ec76c6c93f4bdd86d72761d45705457004389919739c3da873e24988d802173650353329870fd3f30e9c68c637602b6328cd892c5c2a97262209d81c57ddc9e8d4a95505064b0693266cc330b70a3671f7ab9b2b13438a506f70561b8088e6be711ce94cce90b36693fbb6f5b73d0f21a9348f2034e644518cfde1f0728f334564029e87214404d3be9b9f5622493882a3d769361b740a598ac5aea0b18effb99c614202b0231b3f67aeb0c9c57583d89b600197936f5ca11421d79c6c4e37c860c004a3669d25915972db411b28d17bbe9dca8a14d1636c90f71159ce00deae5f5d240f13f6c455d6ef754e68b03279603300807a3a6849be45c0b9d6a16e0478ac981507f91408a502eb87ab719e8fd4b77ea13e58016fb08ae51e7dd0462a5d1c54dd903bd866bc2411397695e1e1d66f33857d137d438c864363f602f6da3c22ad5086d8515c17e89c981f42ff5f2f2eb738fa1aecab5046ac3757c1b8f074f5daef0466a8a1cce682bc62a98c7eb235ea9c2ea5e78cbbe1b80d8743aa92f530f3d5494b0db6188d349bb3087ca2ca9a0fe54c9a110e1ddc2213ea26ce5d7ebfa802e30c9c2257a47e5b4b30c27080114915771c2dd7d87cddc60f82eced129f1d76b91c6c2f447961531a3aecd37d19c360c3035a1ba21f1f301cbf5c117e440b6856d67a272770f2ec1b540bac3bdf0accf99714f759931267b65747b51c1a3446456bd2b36575f0906ae8f1263ce13ef894e1c9e93a820f4f8a46bb6a68a1eaf9baa23bc2538e34947bfc924a4dc7b61c644d0227ca90752f54a2b755a0eb9a995e44acc531b579ac573456cec071508f5864e4df9b67a82edb0d048a2148d9932a86415dcababb651c5d67b0db3e88da56a57b151d45d1ecf9384ea0480df7781399da66e62dab321ae7e7c1bad3fc9c9fc774469bb78568580c685f9cce0307c20ab64cae57578598372674209acfefc66a58abf1160d5548d13630d80b3041198c91f60f56ee3a01bfe4692f788a358911232d24f479ceed24456ed5ad742e781be5a5b6405cddd5fd00360d2f5bea0c0df45f4360128a6f8b9651d98cfa698f655bce5a0f6524a518820bd1b3c7ba625724df2a71a8c88f55c4d17ed40abe746ac3ed4286e0656bbc5a29815d40b53eaa1086ee712060dc9de61462d03b0fc0f0219ab8a0d47e18bd9e4b470265dc82453126c2eaa75771818ff6eccd86e22a39a1db4dcc9c3743ca03ab330507aa65701037e7bea611ed15cc1d0a1a18de001bcae07ec8c77bff5214347db383ba443601f26eadf1da73fea3abe5afc539e96dfdfc395bcb539ec87aec9ab992f23a72e7f37572e6cf27ea8b90832c0a8d5d83947795ad1535ac3b65b28dec690749c1767f717c00273b916b1756064c0f897921d33ce5f4592e517221d5ec40fe3ef6d69bb1b9d9b9086ccfc0d10db93c60f4caa1e07516ead6f243507375bccc5610a9556f1d121a35015634ec7ae9733e4c32fbba9e639905bcfbffa7d8e6b5594cfff0e8aa62f911922d24f00e3acab46b694fa5bd7b0af51142fac74dc5108229c2c0d157e0592ece932a82af9785c19cb1fe2b32f15e457e1de8fc6e5f31669cbe3108a3685f18e656cb0b6f3049f28ea866b68fee6b528018d130e3f9d3f6ee139acbff735658fc3d9b3e7148419287851e6000a3d6db50da4e283e44d5e60187e3367c1d83184adfd86246ae8fa6a6cee285ac72a330f1ad44e0280774eec0a154d2a7da47ba97baca44f437325bc92958bd90fb35d624f724ef9ee729d86a373bbc3aea1cf357b7036562da1a8274b2772017c5d415091955b70b3ee551ce6522e665f6a3c7183d66fe60a780a4dad698b3277da15b7432328a46c0e8d64f5e3e4e9c27ca081b583fe57baf5a56656342974cda3e2c11cff28ccc1d864fd50323c675e47d8b45d293323dac544c156091aa97621cc20e1a99e64ef2859ccb00eb1adbdfc267a10cdd741b9dc30c2009e73c61b8b575baf2369fb2df13dbf99862d979c44615bd14fd1d54ba2857d75bd55b219bd20271b79d15cc205830af8dec5189eb9ebb5d6908fe316d9cb4b9fa67a081acae3a9a695e692adc3a25418cd7526ce21f3d4d55ec787de90c43be3623e2f510d0add55667088f59bd6f9901aa8345c9947d376a88c9e9c8a1654497b23ab0baa8f0ab86c77b0d47e5cbebc176363602f43c6d10a070fce4a3a214c0d49c333949794e5360c14b4d15fa570f332c34679cd049d4406b0b7003da052c32e5da3d3ecd388c7d6f7c5602ba880bfff7e858bc34034118ab5127563fb6b6dfaac9bdadcbef5eb19cded8f195eac61e9111de28da65f754dcd8841c1a7e66d3da5339f3f37c079754f004e8d8f9e53766f39cd7713c8402af4016163292f0d152b7441f11bb8544d313b988f2c230b2b49189bcedc2674416282dc7834b0bf3d529867d36c30e5b924e7b07774725842aa64c5dd423b2569639e3b15506c72796273f220e2465e84972ad8a8dbc33b862e56ae4cc0c5692073693d11a4d80707c2ca2e7e53ed32a8b78f5f290e5cc3563f7f2352981ffb509d247fcf8aadef8556667e5771aa56434576ccb87df2ce869dc2da89ca79b4a23978785d291df2e2b5f53ae89213623d5c6c7480a0f7643303cc1b71b38ab6a94197ea3a6ac155a9bb7129064050e791c0f1ba844b44973b2533803aaf63b410f31b180801ed6acdbc8ba233cb29cb39463d0cfd4b55b3a4035954aeb64a63e3e575f10210d22f1abd066aea0617c977203467ccfd445d19bccc151719daec5c3f507a76768342a4c608f0c1365320431e60a7b822dd212848391a3d5463799b30f3307dd38b8390dcfd374d7a788ad253d47422e8c80434910993e749e3bba02038cf032f69b85f324507ff9d53515d75209dbca29bb826ddc82e810a61fe0a93d2e863169e3bd168af37c0b9641025132f703b7ffcb4b437bc1c5f334d5081d3dafa8d901e8ef6d305d67e210f3e48742b1ff6a1bc7e1c12a4d769139cde5a9a7b615faa74b3f35db81100142cc93f46f8bcdba13481d7179dd74ec3324137ebd980066a6eda90f04776f7dae54fad0c810c24f43f1db01c34385c7b3db6f58bf0f75e36c0c7125040bbe971beb0cd2147c27daffdfcb1d004600e91dad71eb27f756629fd6bbe1f6473cee5311033ed6e8330cf6d00f14a14a1f679fc8815e7fcd5c84f25614d8fe606ae8d9c2a56c525a8cc7abe7b2b0170697275ef579b3405f5bcebc1ecbd6384452e9833067511880ce262db49ebd120ce8c49d23f0bfea7299cff1fbd48ce2028715690f9551ab6605e74da9e16416590b2d038a3b7deedd9eca8bd7726ae814c8f058f60be414719d47afbd9de82a879dc346eb9bfe3587c36a08849b82207212d1126f5fe8ead3a78c5c362f871720b9273ea235857060a7c72429ce6a96a70e5da6d35967cbe280ca3081f3aea96c66faaa68a1ce8eca34c0a0d7d294121747cbcdcb9ac79f3329dfe110c49f302fc7eeb0e6ea72f3127c01f47179e96f234d3ba4f3bc444d02f0e354d3afda3fca8505f2aa640e33bc039b696c2cc4b27d05e9950a80d7a403d722ad868a7a5bbf9aa1fc5ecacb4fd676a7cc96f74386d2a3e46616fffe6a8e5e910168676aab7cce84c9d45bd428e24909a3a5dee78b7b88803521883146fdd03333a0c53d911530634f8271c3f5d09b8ef887ec3ccb9285310521d8bf309bf73675bf726e86a32dcb9504b8a7ee18bfd359f6e2d8d3a9a54aea8acb4aee32ab52739717940b088a09c60203be16af0620df9d2a9dd9b927347dd28d1e124022e765804304d171588e2f3369b11c6e38af920a535a076063487e7c33f75f870f73a9dc76903c9a621f573f535c5229082d17b84f8e7a4487a0523713bbc72ef2ec150f695cac5e9183a2e851017f22af485a256aff86e64a1c71c2781a8e146d0290f42cf3350185f0dcfca19c299ad26da37dc9fbc16fc4416aa0ffa8e5e143490c7b34005ec8394148623c3cd0bd47421a1eade8b9c01accfced014821ba4707277392d4e723174b84c8bc1c8c1352df372e4a0eadf6a872945353146c0ccf9c1e7d65b51850e2c85284755a718500f5880affc19e9ce89011c999ed4bad624697abf7a96b491fa5b1b1e9a30213814cde5058c6f9f4f191e0601cbf7c774159aeab8abe0022f5cc93eb823e923035b119399fc0dd40e706636d12ccefa0d7f71338db6bb077a94a2b9ee6319e5496ad55f9f8963aedeead768d5c3ef246311f7f1173413df3f17063cdc7e5b7f9f879ff76095b1b0ea95b6ee95258135ff41122e62b86f451ac04813793d2b7833e2bb1b691d888cb39dc6df7a22c16e2af79436e45c36bde9833f7afbcbd849ba0d6fa1fd2a13a16f2f9b0e1e3795458847d4005eee465efb69b897fdb4be5355469f820bb50f4f3070c6573f096c42b5f9f51afda508dca6c47125c56c65d3aae311cc1711dece2fdf8e70d8c5ba2566c3150a9718dd50ccf0ca708996e44401da2ad6cba44e5855ba5c33f40d69e463620431fc60bdd8378e7e5bbace6ca6ad08721f40b335d54a66d6efc82df35bfb25c666c92d31dd38d430fe1da28b319893b4b4ea47a13168d9c20a05c5bf00e5b9e27715ee91038d843ee64e3ba5c6a051fc5ea3157ef7da0d9322af9f98f3430a7d55b864a6af0318c155799b6682296466cb61e3cb2e73cb3d034a71a4d8ef2c9f123b2004fc95a6382258e7f5787d25831860244c9d1f516437c51994599ec137f4807f5c7ba71e011e12a471e58f38632511a89baece6576d538933e068a9e2ed5553ba5f791e507af5d1a1a5007a96cb30fc532b037e05556e18c85df65ec3de82ca6029a3edc014656b856a83092caeea19beb933589295c4ae221a7f0573ce60d9b6b13f9d0abda850023b90281167688a9b2eb07719311c4cd9f364d5ac2cd4f5a757ef6c9c408316b9051429445578688cabeef1355575d7484b28e982b7ea745c644b44b4c79b3d29972dcedf09b00f72c7e5002c6f2dc5db070f4a7d5f02796720023b4b65a73a4c0405f4414caefc08570047e0996b104156160f7d5824d56495e76ca6cde85b9a672802ad9e72f4da22cd0999929e585321c8f12e563e97e9409300b7f2a77183751f295edcc48e0681730707ded21427e29bd79281200fc2289b5c9e41d4b2dad781b7a89ee4a1f9625d5cc8c014b5e3f0593217135f0e0c0ec46b52dfe99dd5322c0781169d32aea060b38a89d8ea3bf2c9013041eed741f7ac7565e31f6fbfe69e2e62e00e58e1a7044a4b4d7a6ebf27dbf3c6715f296bb3936a66e0dd6a7688ff8a200ec46c72640cc03ade5c4765407729901c68f57cdb10e7c863edb7c55f846cd8369862486e302e79417ae8c131ffe5d3cae5aeb1c6801c752efef38566cb6106ec5e5d2ad25b9eac90fa2d1a2980964939e029756280f4cabacee030938b443c6a0ff347f53b39280231a0c5c039002a8af41c047a4860fd8828ddf24471e24102d3f43950e0635c66a2df7065bbca204a68bb7f8ea9842b026414258cba73d4461540263943312957892859739cdbf7c9f6f871dc1752b2fed4ded184a4e105d336bb562341a25259903813c5479b24fbf32b01a5989e49ac6fb935be1e12de25c4dec237f882e140e0142b07a8b70821dd3608213a224e03604fe16945b8fa51ee02d2746f150d9fda38b9b7b67e71a10a9e02bbabd94f668b20d47c072017a0d841f4263b4bf69c9a0de75d3163860d74ec79072c6479316dd28b24f7562f67b5db34ed60abdb04a26610e67ff8ec9976019140410aa80307f7ca459bf1d99c59fc91c625dd9611afdd1acb5d2463790c375eb31e9404f2c4b0055f0132078dedc7d065c44dbd8b126b9deb12e2d0cea0ca6a55598802e1652dbeed91a3cca49380171e4bdde1961356c64ad6e12addd582e8c8d651fc5ea1e23713a4227366399e50960342e411db3f39ee775009323a6e9390afe70fd1a2f00c5169b934a710a15cf3e81d3178e814b2ba2f092b40315bc8066524f88242d3f5d9258f22df18cd470b21c287f369240a43e266f15e7710c661e030776453aec671c2e8a99259ad423461947267112aae0435b2943159db6fa6ef0332a711fc9dc44093d0313390294fd95156513e7075fa5c89d40504c7b97e334ddcd35520a445a0d99e9582a075e9a9785ee5ea5082ab1449b5b2a815bcac2ba224ea3428158f843a8d214b19ac4c2b8d8c735fa85b5619b5c48d252b2a92fef1bc360d0162f8f68b64a66153c4287f4a58b4624263487cf9f44d94cae7a7f25be4d250cfdae2a2beeea96591b04c8f56d964c4193db27072942c0a1f85d7e02f1e1b4f868a97970490563a133350b342f640a94b3216bbfd7a42ae01105edab5efc91dba4862cf053a7fb877aea07858d1f96203f06fe65e5d22e42a9449574fee62708d9b1b43cd737968f6893d6b1f2c8d705bac9aa93c046306463ebd4d70e8f6bcc79f3ba4f81514f382b2ee2738aa4ab568f3b774138e94d903b5fffceb68973fc1f31febf0cfc6f90449944162d308f61af4ca20d79532522b574b2d4e77229ccf0039f9b186b9a711df42babeceebdc1afb90fcbc47614441c1bbdca645cb9addbb9f5e0bb2155c55fe6b4cce3b5af9d6c86b3a636905b5584daf7b650eddf926346695ea281b933379a9d53ac308571fccd1d8ea70d2910fb948fbc86a19ef21d96931fa9041e6f6ee854aa478c2bbdf0d4dad6157b6a514a484833f5b3d9385d3b1a4d622a19e6e9fe27a995d2025e63d9a9a4595c2b4d051ccc3c345d051931d108e0cb87046ac8df402955d244bef540aeb1f404c01c61ab87991814421d8bbfb1a32ae75a37d3da4823ea726f3f5c019ffc0e229eae02a1a2f2c464f1b1e9792803ed7bd9342cd448e65d6bcc30b75bd0fb1c89aa9da1359277588d22be0b8b2d172d478d6c93f742b5a25b85a70adc7c601d2ccd02c155d0b6fde04e61fc0a5054158619e2d234f9baf5e64f7078656c7ad1fc3619092f7d7a5d388a4d46e42655e5bee26a0a04ff66c83b4c3e976d45947bcc65c93fa587faa57ae216209a751ce0953933d97aed36ea0d66aa9459dc92e828d2ad7cec9870986e910ec46b90834d2d79cfa719323f05a12173c5ea7712cffb6f732115fed4b247d832ab1c68ae79e80440436b47d3c44b30e9897b38c89befaf0bd058558113e70837c2c352118683ec084193a23a17af0decb57644b75f1d65a7c4eec4fcfcdcf6c9f480a1a75dc88b6f012fb4fc0191568b73ab23b85e9edae17c995c2cd3eb5c36dda42a3e10aee0ff890a0dab9c0723f464744c98853661e1b929abea45cf5041a8219a1aabf3572991f2ec83443493f9bc5e3f77b13c095db3fb0306b567b23e5436da9965e1fe3bb5b71825395c3b36273666843fb6bfbd831c5621290c054827013accde2d489f3091e33c4a14513d5080ce31086e7ea42d4b1859a38430e33e306094be179a9300455b6cd553854d555db5b47a5bd6670c7c51c2cf9549e55cbd9f990c3feba678637ac53198d5ae1c11b482bbed23c0339cc83993cec24e72358c9604b74c3021660c80880c8872f70903b942bbc2adb621fad65975cfa6a0417547cb48121c9303700b6f7ce9ceafa1e21d15c7cd2b5f91f176e6e2543eb1b276d47936270d51ee4ef35046597314e915abfebab64dfcae1c2c3491e43adff28ddb846c8b27f5d7a494ece162490c1f07d313e610596b48ff0156c38929f6aa349d6674e229212244c956857915d308fd5f0dedf4567090c79bc1de6b4db54013af6063694b465efd749e67473b17a09502ee34bc60f17c2682fa3b2ffac9b3ba6c44e15daf9841b4daaed6a1d1f3d78e4719c0d2889ec59c61e04b9b24e20443e84cc08588c14615f93cc9d25e681cafd3902e90608cdfdd0d2b871d0a9e6f0d2f008892310c0394772f022c26195f5e103665d28da9d420f04bed61facf0b894b95ba56f950dd7d52f967c8125c87f2d9b56f0b7a8a15f60adbf66f4da3d6eb1b7a93c41fa9696d8e92e70147ee5b8bdcd4761f86ecaca14b8281ff5d1f6e94eeb899b88ca65f6a980f4a1e363913aed56d4c38fc11e5e4efe4872f422becc8276b84996c656ef588892506a4d2e6e115ed24c019b4fc7cbc20bbd47247614fc4441f8e1c2c4408fc9186a4e96c1511882924750161e41948d94aa6cb85dc45920057f40c01547a983e0df6f3939b4a2e7624de8157d714007099ed48b37098e9685f7aecdcc41c028ad59f2a2d6f2e4bdab5f355b7596f75c35f75edf88d2eec67b7eafad121c6e0305bde3d5244ea9d7e431322d89087badf3e2d33031310438eb5def06fa3203c9441d2c4a7bd8bc75cbe01514ff6a76e2ad5d1266b7798a87e3b53dbbf0e61cb96625153f56b18d3618e49a6db7c326619689e276154ddf658e3bb6880168602a22cb9e02dafa15109402c274d08eed446666fa00f84304972f8d6125b102fb63221c2e346143581f233f939a437945b2709a20bd285f1b9f834968dfa8587f2fb9658020458f705772ec1370e7ef1b324fc5ca6f3e080bbee9c113b6df3328ad393c8b52b0657a06a40bd9732699ed7ab5ecfcb943722bbfd14b90d1e90579fa3942100ffa819604b9ef966a818bf722e581965e02a5d8aeaec628969c37609f28c60c853d33956e08fe420918772dedc6a7582d69aab940b432b2298a13f608f263b7f2d1e3d19911968fca2b3ca3835e90b73e1737c4dd6261e202b71c59b936b2814b3eb3a790e2f409e81be38fe2a2ad2a6328fa67a3b24fb82079200fa706de8c1432bf781632471ad9617000c15fb8f0e6b2b92382a6161c38ca9fd0f9a56812b3441f0ad151230c9ed6c949d0b8602dd744fd8285c91978d1c773640307fd8eca0f76739af5185cac173e3c1ee089b293b5dcf263a15f50ebd3a1ce6b6bab7d49e651e9e5401e3aaa7ab5fff61aa070ba4a37d959a65541e7def26309058b941eb80356b284d34fe2567f5278a2c491919bea11ab76b9587d9bae1b31dcf7b0373f6a9527dfac41a3bdc4a117e8dbab5b2d5910aab1c30a02dd11404a7677c8e7818ce183780f6e0ebe9b68dfdb6c382e91b90da7277289b06d4edeb8b4419996d72c5c358586cfe2cab0c98f735ef0d19f0a8c6946ff771000904be0d1d18b102e26243476e375dfcc31c335e5e8e39130e815a0b5a5704b2d638b00655b8123b6f7200cbd5c15d0b8bc776319933b94a8570d0ecd5b0e829d00c2a156572b73ab10c225d69abaeab7cf32588f4eb2790344ca9deb5d48528f1693a79c1c89d42c10a0e5b029672e61899103e8a04d6174c1e2e5bfb009a6ffdf43e43ef79c93c4cf84f48bdaef0039276ef62740222096fec01b12e9507ece24f0cdc6b68d65f81cc67abd0e61580fbb776bdfeb66e3bce071f9fe5c7e90dc91c3e5807aec159da634871dab3b6574c2ad68a8d1289ee28593401651d95129fe3489b7fedaca9bc4cc9108a39abbd62cae34356a59396bc7d75d7372f073c17f2b71f2a9a3c7170719521d26e0950768c66dd7790f7bb24e1be2c8c261602aa5f06eb6e06d4f6a6d9b0078dc0b911f8ec5fe09b4ffa32d9fa85b7d776f229e470d9fbebbeeb52df12427e7d629f6a5e6b975ee9a2fc44e86bdd87fd0206d640d8c472d7e5cd77e707120caa8c92e9e2fc24829a3472aae21e718e5931356345762ca97f93345dcca4dcc1ca338632d9843dcdffa8c1a6a8ac92dd8952a5eecbf92f8525956e1d499e021bb89745c7b8a205bc57e611c8c74be00d945bb2f6c6b7021e56df93dedb10510c5ab67c1b1f8bb83e0051bff599284fc0b03df1687f51631967f2f3227c1dc01f05eb882534448fae16eaf18b8600c3187ca2a4a3fbd012b65752d6b28057176c5e0fcc6ea2e1faf42b47af1df3542502c2ec1518b7f5ed9c7c627fc52e739f4fe3d761289170e0bb1f881921cf1abb76eaf176eec3b7884684dd2ffec12a88c13831f963e67e9815b69fc3da9330aae9ea660010753011033d2edf54278ef32dfadc516ef5c4ed40ed46f16f841477ea8355b2a35de395c7eaf9aad4a3eb4d7e79661536f94ca454d62ebaf822921a2348625aba098207b9501b0085e69107ae3c0b5b532e808600e6e7df73f6d7e99059c01f91c114403bd82f5a720078f0e0860b9b254f95876190ef1c97f46c7ff815ebf48cb7716908106973152d8ace20dc969c8851d7fd11202301cb699b055eca58f0146cac163d8fe6545602d46f88bec777c73330d3bbecbba4a8ab1595f9b621ee164df28ca558c52eab0e7a074749e90c7f08e5e0f52da2e763bfa1e77e0b6c1153166aeb548a98686164fcf93fd8cf0743617cfc3d7b3dc3cf5e72ddaf0568a1a12bd7eced57ec475ad03bc02ac7daed0015a535af849763755ca8f88443218b3a2a4e4a00ee59c64302e673d2ce2120ec3f3962a166db67bc47ff0e35e64c910cbd1ca4776babb5af7111dc327ba0612c308bd242db2f82e27a6322dd6b0ecdda27c45a0aaa597a6e2bce80492ba1ad2d44577db42ca9299f3564242de299e25ce5556a46aa3f322dcb6acc45290ea5dbc2bfcb906ce07f0ea27631191cbf580a553b7d3542193df6c926ebb869b0667dc91876f738140164a63ff9f1362d19a51333db50a86910dcb8be1a0247738e9e01635a2747fee811a502b4f790f561f8965b789e58d0aea957920d0ce8a5970c2a754e9a0c0326abac2fbfdf422933655c3ca1b38c3a405a7c1f67aadf2a8a3af44aac7124dd9529bf207fca928ac5ac00158d8cabf9039f26207e443d1815c8d77c7222114a16f3190004b6da26c387746a861df16f40326a34bd2017185598b98e1402953713c2516bb6fa869d702b6e8d5ec8454f2a971e853a6627a9c5e646c62a9183b8e02198e721b69d7d0708d34b06c53e1b19701dfe4e86ccd1e6b4665c0b202945044c8055ed741c824b9bb00641b746c0f41a18545df979608599e7f4b677cd8d5e413dfda58c96ebb6476a8124e93421ad5e2c94ccca6fe05bc96ca06f727d005327df181958f81ed91267f4b52aae659f0af396873bcd2037433da3726b49d86d3daf2bca4549a066c5692cf03f60d83bb120898caf521025222d1a03ce76a3f17d0d362450398e9f09a2c73d3c6b4fa1db3fb09c2f92250bf3f3c01c08de51ce2c071dcc57ffab77fc72e76f24ab330238c0d702c03efac236e4320af088c4f363ef5abedfd4da6d480371003320df4463a64324fca7bac870b67f5e4f3159c189586ee9954a8897287add7f3f1ccbae2217c4d7fecad60348a35eb7fde3e32d2875cb8eae11cc6cef0b054a067ae43c82e8ffdab24f91ab6388be90c2d1498360f54ce535e1bab14cb432a7cf610de52ef3dc8e5aa54b0640171611b04e4faeb68cbea3be3251827aaf2aca39f088d67424e7c8a50269bd0fafd4dbf283fffc5b394d226d3e3d2aa77b040e640b1dd2a9231302f2d866a30a3a42245ca2f9bf5618605f07f8d531c394e7fefac92f122895c5b0c7f518d58589866eb247aabe2990c3516a25da60810777a847234ad026e4d884960ab112fc994b394b47bae962503d647df99fb2fd4dc76fe72420592a9cc7bfd87f09f31e0462db0f6221587aa16c4ca369f21f63957e36bd45eb30dec0773633efd8b6480f82ec012afc06f90786bb9aeb1cd55d9143c2a1029bc2f29ec665ed8a6bd230dc9f99b4f35682103f3c86f04ba95084ed930973a4377b8c84700a05337209ee7a43fcb840bcd3277b120f2bd5b7de440187da1e7771ee0ff08927c404b8dcd50929b4998c4daa6d3bad81086bb6e691e7e9724597c381ace85ce4f9b205002f246bb98259635470a74e0f96179e96a41ae10567dd388fb230ceedc3813517654bc9221d7913672453ac2e550459b1cf148910dceab6b599aacab8159d11f7e28166a87829c615d6a8489950e8d1a1e8b0ac6efaa04a29e3dab713c1b52bd29e4b2dd01c063c3a750511705affa2662f6c3ce03178d23eeb2b2f0c632d10802a76019fb78ca1f2c8f9383ec3582271ac805ebab921c29ca50d4631054fa559e6e0473b072eb636d026c105340fff64f368e43be81e147d1fdd9ed65f76613e7327be5018424d731193e6dabcb092d775d3162f1f5c815d0ed53ccc9e9c410775f7bd07c51f8b52c501206b9e5f1215d79398708b13042ad5bbd62548cfee48c29743ffa6b2db4cee4080785c3c32a5851c8303155dc07aa505a44edeafdc4fd370be9a97a4350cc83a38a8df0a672e9e04e4461d6463ecfbfe0f112d884924c1e317a76514cc5a24762ac3c2b8c306a63c623d51eb00e8e06ff1d6f633b5e28cac64f7ce0342a5d35bc10985038c2889233e94ec1002014d9517bb1254e8e1542e244cb275c216060c8ede28c28634b0817aa8076c23c7f893e3f4b242911db03f8cb6556b8a2e321b6f4d3174c953e498871322d7951719ded639411bece7af91cca75e8145f844b4b7fc26339c449135421b55e9ba294e3da5e1ce0e10a1e6104c81a748f82bd6c7d70c40c6f01e33512ae6a3bf3c533f04364297144fa2911b932c433a2460d4125bf80d65c44f6d94631ff1acace05181161c43cd19cf8b22d0e09318d07c654f8cb7c7e23079347477121966af5ee904bb0ca112468e7217cbba3cfbb98dc8faa7fd8cc3781cc3b9fb26f835932bc8ef14d27d6431ea1b8cdae4050a15059bbf441e45b812c63185dd325212e55651cec2e9f26eff3fb4c5f3e86a10f7ebb8aa55da60fa96b47f9e75150fba85840a857549c6141743f3581cd9fdde5bbb273a23dd4e6420587f20e018817d1ae7292afa3e3754ce340eb08cff01f4329bd19da81048d17d1963642266500f6a7ecd3035e25d392834e7a1c3ce484e90315eeca4506d6a3069c253886309ac5bab835dc9fddf2ba29476db60c845e33ec892a405749af06b476ee559c182fb662e273e5925fc174ab254134abf06920d9a8e1f3dec246ed34b23396293f2c9c6bb6b30a5f5b5e08ea574655bd7756378e40a1778e3304ce124770fb28c23615f5296ebccc8474c272e652b9f0835b8269abcc87691c2efe6995380a113f5a378d7fad954dfdaf5ad802cbda31ddcc3dedd656d9f539df29cec10487aea09a932b49ed6a6139feb5f5db1ffc621896fff7ff4198ea6b829bd8ca7d1777d65c3ddc8e8e868e7a54383fbd72d268789c24fbaf8dce8365ccda204f009ec723cc2c6168a43e1c298be737eaac54f9d9355277f8b9fc8833ef631e034d889ce4071a6466745439e16da59e3375db935f8f4cf3391a6117b3ff310ab4496f5ebb50c8f46309cddbd3538bd1f44cd110443ae25b51290769a2fb1734a347d7dbb0d97258ab224c54292b23df879b3ffefba361a3c235b058b6477e5bc388376ff4339eea21bfd7d26a1096a326b44eb4fc42f621107551648b817e8358c70c20cc501918e6beafec8583acec02152bdcefb1c60526f40694a9bb6a265e01d84ec88c7b28301f153d2845cfcd7ed3cdaa1e5405095233d57ffe7b29bd5f0cc9175d8f00aba499ed023a354be33561532f513f159bf2f4866d6043ea86656ecd2b474f5b5d2bd47fc9081c2a27af3e4f4054a67f2acc7a30f9422c216eaa3f03f123e8cb8284772f01f1250df3faae54c9a3c13ce8d64aa2e168ab15d4b185ae161e0ed9fc7104e2cf63e9b59bccf9c72d0588b6d33bb615fb382a8277638382017d4b67c8f0041e0467f42b744b27322ad90a392897917e085af2937d48e3526b646b60510ad892658486b5b28f7770a02e868c813b5ed2081ff3040619c0a6469432c1c7c45ed9d4a19ca11e8045d4461938fb976eba497d9d62c12151833b790a99df73a9c28f2b7cf6222067b41eee085057b3e97e60fca702f9c022d4cd5e52049d2611ce4eb359c054494119bd86877250b3e9a90889ec924d45c9583ddb2a20704114b1cf791aaa9c3f05c4ee7b2067d5b54158b65095172a660c3511ce3537e8b80f0edaa4083db6f559dc2076072372a813529635b03837b78c74c2468552f8d4f2ffc650ea24663f9f4ada76422527404111b4a5a8084e7d37105b56c4ae02b5b75a6f99ed4aaf702a521e56bb5801183d8510af158e5238b01c38f5b7d0c69316d27594af076167b45641b4996c30a3efbffb260083d7f1788f9d4d1fabd547b43d2c67f94d0c70469d77b9556d20982192a12b152e8b750a614b94bbb04dd0fd4608ebd1329b854eeea46926df6a0dbb59409d10f66938f6a6e200e244a2ee16f4e17e694a107b3b486c2661e44dc21af2b5ff798dc14f953768b3738f02415cf8bae03485e0066f0f80ad10bf90fca6d4e035bdb655c447ac6072d9e8146a1f2c38a82a4ed3f8d704401b7ff41755bbd786f2e7d495495be663cd6828343de29bc985144b735b83cf2ebae40ee30dcb694ad0a3f3e327b6cfeb5a6cd24bf2ab268a1da5845e44efc794887ca3abed3e95b6efb55cd8254f36ff2ca10d6c867eee10ac73a389e91d8efd0d70cb8849b34fead239e208aa018bffd50e64849fa0562c16a87399b27dc2a14eb62f5c5ae33c6d9e3df39a39b1d309133fac3492439a6e03c0ace818c365ba9c74d34adb757440725934ba3100eac9d0dbcb62d79b26dfd0050eb6e6db289ded8833395f2ee664ec01b32a90c5fdf957c1b98386a400389d46059f14c3a9f4fd080a6d5d38d9f4db1e393c50b91ee345e9bf2c4a11c8d1abb06ad8f2dd24b7587f1ee9f744ddd9cba632ef2fc2dbe36380185699e3c81ac5d6faa403c8da910cdb9f4771948b1fba5f35ff39c7fa92dc49d94585287ede6945d96800c305c91f478a9cd49c758cfe8254238e4505c55b14d43be2131212ce9ef08d6c54e0952db38e0027b5667557efe99115494da11f396075ccf203b945592353ec5d9a070f7c8036958e7f978712cf1ba5863e162dcbc4f90a89939fac0f9e8134f7a1dca777c6c88d6da43a14cb3ac842bd5e5763db30ba395ecea09915837d42e983da6c29682ce3aca3056ce324a4bffd58210dca8ed53272d414445d95b1ea25743aea61f3f4a223683ee0e7eab27272157d315f619b3e891ba76ea068dd9b0dc811c9fe255fe6b4a92acf612908fee5c58a83b6e75a869d42287d8d0daaeb2d12f27de156dbeb8ff9222c67adad85391b310cd4aa1e7547791f58150fd698292183e315d821639ebc776b70824d1daa71828a5a40fa21d2129470b202e22d584aa1e2ed9abc82a7e0691b688c8dba4074f022dbbb743be97837f2eb98e6fede58774ad0b7db1c93001ee2acd19ff1e96dbf2ece26ea356984538e59baefc1c87e37cd55774621e8d1089ddf29e8c10ffbaad15aad66436348a1329031dacd4c662d3b24a63ca27fc94fd208c41d5f29a4410f3df7b6a44d73da2a67075221d2053ee020b3c20653baa543044d55837c7cb971b4d876690d299be92ccb69b3bfdb359858b34a691bf3a8d00572bc4ba9ebaaba8f316252f6cb4489dfbd62023165467434c08287453e39d60d904beea4fbe33883a83c505b4acd9f71b5a6285be52092b1dfe1306b7fb68e5e50c072e67b8949b03e0cac3315dbb01c402526550ccbedf293d7b8cf99fa84a560f5d8bb4840f6162c23fe967314f649233baf482624820884eb81ae2a5fd0b12a8144aa76a6aad238301365686cd1c4a33cc481d6f3f745b8a629c04fc2ab0b2b95d6339cb6503b4ee08e34955d933aa04a1097b9be276f4cd42fb8d044893565b4c2b46fb5283e9ecd5e4969069e2f2ed7c0e6952960e81a8d8b5042481e6ea7b2db069c16d90c6ed2fc588c43c3aaae01a555a0d41013f2fa8ed84744ab9441957bf093dc88eaa99af23eb28ecf377a57e810b7df212149bc4892ddb6df2b5bd16d48a003af9aca022ce94ab243080b9c94794302308a2658733914ffbbdac6c15703318f40d7fca77643a827d9077d6b14408613ca08ebeeac6da4644c7bc7124e1a32af1ce752d2e0c0d29196636e1c5137afca80abeabebd8a503bb4cfccb0f7b17b3a7a6b38fd2ce86f7207a822d53ab1efb451e2a8504deb55c126565eab34da8cf88f3d333d2f761f22d064c3d124d471deecebdfdddac1a47054d4c1baaeaa22a06ad247950524f54d8f8348090434cd9326e65764754040f52250afe18ce46807c8730047edab256256cbb175edb215ccd4957d1a62472939abfcd188a4445c556467dfeb92377d6637eac444bb358446dccd7550a76737cf9ca446a608213a31df649676fc2df0c66e2253366b836ef9986161bcaa9df0f20124d37102ea980be7d65c5af32b416bd335c09072d2a91fbb565367ce2ded5a4599e6ec05278835bfd9e226fcb571c7e0581dbb7c22c9c96238f44d21ecb517b8818ab0dad468b28ba7ecb7595f1c4e437c0f8b523a2714064f53aad66d017053ad8f3d60ada422cf297fda4780d865ccc85290eda18bb18ed35a2989a2283c8ec4876d78d3c25f6b8cd690ea1658d3fa5c0724cac8e81787f57144fc98935bd4e5695331555083a01a364722c8ccf70b5ae967af8a9abe63a65738676dd1bd8dd46b5449abde33d19d1b700e21a1cbd5030af14c7bda9f3579cb1d5ef73386a918c66ae9d10309dc61829c40159a9804f08c4080194b76b4643a89ce479ab0139e2836fba2a084cafaaa01bbd92c030ec0d21454f1cd260e033719005e4f6dc8f9c38ce586a7c92e734525d80fa04c5f7d616a000bc77f429d07a7d92e3ba316caa6c21632eded742ddb645976c8db368eef3f3543691d2a33c088e7b536e4f54e3e126372016384ceb271d8d6109a564b243c3056008394467a33e68a22f3cf54c7617a2385f769b2438259fbc6184c346b228f3c17d718b1476ea0e4b0354aca3e97b95b528865e2838b349a9954a34b8579f67b626a59a0004e980b8ee7a77be16102702b3f1266a7f4542c553df322e6e9eb645a4ace7fa9ea3a8bfbc3369b0437fd815c0acdd6241376321211e412c9b6e736ff74f5f4cbde09c76a5631d618526f2d6fe175133c621eea8c8da2282ca4ce6e36a3847214fa1216b25bf3c7b6c209f2e149a2934019c32b6a9c39b24020249ae440cba9424065b4ca3d77b56a82158b8f4bdb55bbbacf132707c44b8282aa90cdc6871d61f18ef0d99a1220ac2a3697028f9a3cff2c36bc5b017269affd6b2d8e5d6d04287a0c592ebad032b251fc5ce928eda9a1bed1cdb18d6a97281b0452dafb65e65374996a7289b2944dd36dbca93627fe61f6983e0a5a9e81393df6a11cc8c3e1153fcb81c09a02addf057cd6da0a82df58ae3b543250e93fad48ddb679aebac14fe86fb608535cea12f6768930cb8c7822e4fbfef334460f160a846885ec527325714dfcaf4cdec251edbc3fc7aea068b71016a84c65ceae81186f47b7946076089aa0bc378d36e64bbb44cfc4a492f0f0adcef5d71ba8024cf1833958666db38686ce921081d11a9fb863c69e47b8e0c39311ccb543388638b4785b65c09162d3e52fdd97bc252a1bb750c932da46a6a6fd47cea1e0426820a101f24b6741d86857826a632a605bd04c4914e4db646a32e4de4a33e672e9c0ef67941c215018606f6c1f1c2c8c8273055404982a10be36e8626a0226ab1aa6400b154080e176936d01f5576542893e74f02b71fe2702300fc4193192c302de6eb9d1c294e00c464d6a694885514af485e929cd2a03a4f1d71a6a3cc191d1c7fdacf9c97f9a772414ea2619a438abcf11f94b612d81171c6b87b53baa2427aca8a6bfb4e395f683d80af3b0d3a4889ed888a4c40f0e54096d93607878d799970f3af4743702b0a67b74dc00698f49272589dc78c2d5db9d1a801beae7918fdd4679f0044e963e02308d7b0c2372e8c063fe4fb4a27b92e97f4da0016bd885f26ca5344d3d38bf57414d132657f68ecefffc8c7f78c3b6f6fdcf1444d3cb0bc15d2d7ece359bee8a57b09da1899880ed5d21be37372d3ca60c317747d12b457a51399f8f4057f149bac2eab292bf5ef10e45eade451995e35d16333a558a2724742351f392dc693a25b327926d2120a97b202792f4b9400975ff6bc420d12441242ae7d3e69bf619f5a77473bc62aa4dbfd31f2143f09429326263c5f1cdeea488ab6ef5f708f4294c3649668fd245608bbec261eda32027f318fc89e1cb4d31d0c90f18a5bbc8256f5f299fde8c6fc04cc21d22eb4255565937154922ade4de6d60e05d157cd6a04f06e13884dc72ae1c77ae442484676cb132b118268bd1180943783cb9be3f9027f7b39dbfdbe9662c25c17f82a623066c7a1d44bb52a671e2b96be2c3437a3cfeeaa91845aa75d15a9f6a26e86dc2448f35ec18ea40f8fa83506b8d519c856526557ad2efe30f494b34c05140373a96987340f82fda514ca102ed714f0bdfc0ae102a11e576c9888bae46e6db6a2f65fae2d321162b4ab33907a4c7a243e0d09da28cbf96e9e639cb035df0af91338e291353f2ee8712e3cdb36c2497fa89b7100204f693dea1933774700de56e811dd74c5bd12b176f0ee6453a0123a447b2ef4072af1c8126d4336dbaa52747a0c01bc9971cd88b6f9198ac777e32024505c98e67a1e2792f8e6e4aea850a266b88f40392ae7ad562ebbe8dfc600ddb4bdb8ec2feb779f50709161c862705c62be179bdea43b29e8e25620b5206d0be5c7980c57d9c2fdd374ccd43a3f1192b7c4c967220ef0bc37d90bbd0a191c1bd8a1203cace86ab4c953e3180f615106f2b8dc2629e4d5e641d990ccc41f9282a665371302500751c89b25f84bf41d0597734b4666a4e200ddb4731c4a1c892e78ce2f6c0c8c74221313d90658cd5e2867943895818192a905a48c111442d6e7d2af49a7658320766da52131ffe8b6588d134a72710aa2387d3fce1b89e52012a2603f4e39438a8e695c896d331e8429bb1db7a1a3f8eacc874f45f30b0d98bd95b70ae6571b55c46da5f6e52e9b58abfb3ce788f92b7740ddef4d499425747a7f3cc7c92e55540eb83a3ac555353de4525a0433694af074950d7e76144c31ee8931d879dd4695ad1bd1322fa56057e6ab3df806ef1209f3bad9ae7c2b001be2062cf72e19dfc4c0bdb480ca578f36227e8000bf30ccc0f73ce488d2be7775855f11cc5fb73a40975abc85d02ee15cb276cd463b5f044e31ad63a4ac8d61e95215964edb819c2be383af298c0f3703756c4227e2e9351f9715d0efddafc72246b870faab63f937da53a0db0152e45a274c9e00d744746e5892a1bf95fa39ba4a92363c63098b9af808358ea6574c1df00206d2daf21721b096d6b81cac4cf51e3194255e2106e9bb67293b8891cc0ca38bc7113955b78e229f693679f3cd76a30626e2f7c7e298139338f985b641748e68115b0007067dc62ff7c059c0917e6d49c67262303ad9a95ba97a9bb1cbd3bfa8473a90b3dcb468cbc4acda24eeabcf9f37236ccaaf243c49888960131e59588d61dee0371ca56329b55a14ba666852045672ee99144393f1835738dcf3b7f3f1db4e5487e70bc301f152c75231b54e4f916ac8aa4fb02a30fcbb75af9d817cd465c306f0e743e20aa608fd70a69a0b268d3631757e61409977c4a6a02424ba11c2726fcdcc29ecc913837684011c69f64c060bc6da1c57814f602376a5bb0e75954d5a45f833867df84765c167bdeb7579c83ead7dddbb087144176738b5468271725d58f9db61a899162e8c6a0a322e33c3837868c820ed2499371bb8dea5c4de396904789ad45ccea020078de7f6a4e5e44ede28d6a874363c278f5a9761843e4f1e570c350aab054a91631733917e7eb72fe515e54a1717e33e5d5614f7df07838c78815b88e55ed2ccfbc0af18de4513b2c98cb3dbc49430e7756b02826b90de59e5b7d853e6e903b0f81b3d2c72c424de38169eba3122f547610d130642462b31b9856dc0236b6a0cc9716f633fd355ff6cad341b6a042bfca0d22336dad8583d4c15b44a8b73106c7755c3ee5756c154224b653324cefde35a1d4eade4da784be219dc806957bead4ec565fb6bdcec302ef9b50b3d9c9bc882bed9cdb2072ecc29c966e96346ea0babce7716916f6f4a1fdef05351a9c9d7a4a98033ffe2d8d20d9edecce428ba225dc815041640c2830212c9cb211279af0cb140206eee2c012eb8e68bcd37a3f99da7b9b041f2166cd8faebfa6e23bcdc5aca147163727e8df882e7e137d4cf428abef21a65a1f7170324f9e3a0d07cb88d22603ecad12d88745adecda90dcf0f467e33c6c61dbd208136ef0d5b84d544175deda20d2a1653ecc5d2d99a281e3dbff756be99a10eb43ed2a33bc739bd1f38e4359581ddcaa0495a7a7ce9b2b7715d399ba06ff840f7592bbac03c7d1799ced9605a14f922d7ec5f04516674ad6513631d9ab78a27d095515a3b34f1ffaca3029e181350a00fe67df9b075d18bb2a3e967be784e03194066999194f41adacd30d837b614644a62cd21a3940b6ec9fc44a3b5517ce52685175d8ac5404f0a22ac4bf08768e32ab09b9264197284c071ea5f2ed4d78e6ddab78f28881039452d12dcc87616023a38c657d2d2639e58b8fda8cc303b941acfe7e64e54154833cc6c3f6388aad82708bae3808ca9aeec8d7c5cb53801993f3240de982f2cd5d1a6989c70a5cf178aeae01f44ca850393e4d87ea7810bc8d18053ecd5e60d2d4f21e63a4aa45ae9ede4acbacdc301ad494dcb1eb6167b59c69d8e9caf4ab52c8e23330356f0250820e436b4346b3b09ef66b614f28192d23a030fa9a9f9e54e3f5767c7f8c7bfc61d94bba8383fbd81eff7cc955a3ded137b0ea2db07a21391631eef6156083a7b08f01a7101e2b217d57ff8b9170b69e40e942fa567a2bfb1caad20a7c72e39d183bfdcdb170099528a8e870c0fa46b14fc87152a6d2af50d297e9c998dd9daea3f2f2a500d0bfd2f60406a8e3a9c41fe566894e9b31c7d27a0a69510a6456fceedc29fe8e8758d20f66f15d68df7735eaf6103cb48c8de67b70b37ceedbb9765a33c47bebc9d0d48d89cb70389ca1bd09d42e6266bf257d6d031273828d9e6c75dcf1f2b76fc9f206a9363dc4c3cd281c04148013edd8f52fd1bb59ec06250d79bc9ae33638332214b6a7cc7eef623a22d23d23274683ce50672063bdfd197c6e0ba2bd8a316049a90a1780aa33502dd23b4dd49d053e7bf54e98ce0c3a0b680375bf04cb27280d31ac41206bda6e8ca9b4b03a623b75fa3b2c00b863bc058bb3b37fc6eb85f060517e7c4f6feed7dab42bcfb85ca7701b8c1dfbf6755374f01611189bd45075f16b816761552499bc1063fb6d5996860efbf1c3ab675d62456899d68f6e5ac0a462b1746c95a84d9fa8c3a5a27883b328cb2d277b2748cc67cb2ad1f87827bc068e94728822892cf021d2c94552970c7751f981b4283cdbbd7c8e6fe4afae02eacea12d0496e58ee7c813e9e4ec18d2f78d521b6f5897eda06f17947d277e05aa421fa86fe5e7599bc75e828e459d3a3ad2ac2b0eca4a7ca91983a414cf38281614fb9569a089f16b564a6566306a67b6ccced07075268e3d0dd97e56c27f62554d676a015a9d61c5d42a72fb8c47b6a46d21fa7aa9d2ffdd0817eec5fea9f67fc88184b6b25867df574a192cfa0a6eeda0cd0d2342c3791928a13b75930dbafd7f82d974478afac5f286e30a2be6da594921e507e94466db93b2098c97dbd8b8e9e3170f2eb410c53fcb75dd4d3ace9dc0cb16e5c8a9411fdaca2eda0d4e7bd7c2c84b8af24513aa2c9a0015ba20a346d7246a742ca4aeeb38ebbdf5bb514c672e4595d9110bcc3e0172ea3879ee1925b8a89ab8d65f1404dc7995292288b4be090498df704efff4ca1f97b0ab4e6ee6ec8269e3d5a3a2ccea84950fbf95946a733389e170a853dd516bd556a1824659c01e2030c26cd830ca09dff344d5a65682dcd8b69e1db4c9608c11766b2677fa80bc50971ae5c0ed0a0f0f0c2e6f2df8931edc01baf9737e5cd847705cfd71f050dcdf0c2a791505fc208e32cfdbefbb093ac4f0a735e40b9f42b015ad245646fec69ffe10386a79f4a91570fdd365d5fa7d7ae187675c998aa30ff4422f824d173690ca1e4a2bb8baf2d0ac27bf2a475676f344163321b95984ef95694f84a4322b705bbf040a38936d8eeb59f214eb85c0be83241f1506d614c1714abac46f18bacce6a6b77ba55e13923f3af462e21e876a62399421b12514a6a26332492219eb7793a2c7177e9429cbbf3491557b5ea7d2fb6f3b10944b81e73a5e6dfbf27461a956a5984ff2b63df694479aa738260237a51fb4edd137dd50bc8cec536303af43c80af51b7bb1b7afab675e2fdc6ed0fd1bc389c5782cc6188734f87da89a9a53d94d27707136d474dee702272c2448159248a7b4b9d90fdefcb2c76b96532b8236f0efc568ace3d68bc4999b0eb69f41417504d7550f9494994bd81fcbdf6fa43144268d69d86194fb07ed1c8a28ad07524721f078011a628b6a5bd62cfcda9bb4d481dd7fff92d2a71b3fe07bad7f4e39658de45f2ed708f7c9752a28f7f7ab7ee312769d58c64145f148dbe93dd05f5fc94fdef645ba784736fafee21c3c6a72bc358abbe5d7a18ff4468936930ae9b685b71bbe6ff3b93cd99e5c5920a5c5da876db32aed369f51ac746df7fa118f3db843802d849dc274c5cc87aeef26d7c1d8a146e714c7e01a08fd144457c56e33ab244ec52dbe293e4eb38e24e8b10a59ac8788248c37bd2351444f7152a43552602b4f45ce15bc28bd0ce67f496e58e19b4178caf562906dcaa7ebabed21f1c17d86c128fc8ab1d26bde812e911bba388c69d9086a54300c3f90c72c80ae73c31e3513ac69afd5a00bb37cbd29dd210eaf07b30795f289dd28fbac920f0f6ea05b837a73aa96a02badc2152651083bc32c009b44b2047dc1b4deac5829f108c016a9587b7f0be19c23d8b4e26495fe12b508e69a747f160f3f883a8402a9def8c7953c7e113346f7c413427b20352fd7487a06a7fe53bf89b6396223915e5819ab82a2767c092ba7633e353f10b518cb85defe5e4d14d3e8f8738ad4d27b2d70a7f39b4178c7ea542c4d31bf3913100ef2c7b95fcf22e43997e34e00a4203cb097a219d9ab5e318dc67e324805fb3877685b343a9060a2735f2addfc1e17e21509fde3023778b6842675cf75f8f09db2623ececfa30a16499cd5bd06d60549089a884bc371cb29122fbccc9b58f4aff9630d86414c4fc21edc8cdbcc8ab5f40a17a0f6b929f53d46213bf6898e638be9fbe18dd1f02fec813b6bbdfe81f5fbed787bdf1d078645031425e0fd8db9016e1daade9758b04f8e853698da1eb66f3696ecdc1f66d6d0b238950a10b9798be5b76139848cb5c5a6361f4a6c08c558efbbb0490a59e1d9e3b2e0604b3ba13fbd758ab0412f2f4a149be167ea9647d13365224166f7f5ce8b71535056ca7347b7c55bcc7501e233d7207920cca32ba539f76ce70a0b1049c2fddc5962331aaf723048a8538e38944307bee78d14e1c6057775ab445b9bc23c05f35af35abe47ec0a30cd032e4e05442c5d9c905f306164c41b628a3954efe22d522aac4287bd530040facc326234b5281a6061619a82d1d3f1bff4e964b1edc04baea513295f829336281b5b60f527171a1bb6b0199417d8c08ec5cacd18d1f023c06c1e2ce4730a6d41130bc2b58e9d563bf9c3023f5895f7893a54e275ec3377299e1e01e3c81089bcaff9eeaffd8e663ab137b86e78aa8a632bd7cb2fd0efac1324a330edbf2bc2248ef0c0986d926bc760409418494f21900a92461aa267231ee0cbbcc4fd95787b16d877c60dab338b3d774b5fb99f133ad09a7ab2d13ceafdb2e1a653bb846ee2b8c8a972a2ecebd63be5c319a87eff6469558d41654baf34c2bd5e3ffc4db69706e7beb1d49bee0e774e7ca3adb794feb620877edd6ceb65d6fb6f2da1694beed4645fdd506081c0ded287833ca1f39bf8de8531546a137ce367fbd2670a68a90eb9f78e7cf85c805fb26f8a860c283fe62d7807aa810ab579cc18192b77c598edb1e984ecc4e42097efa3949cbfc2e3775cceed48e2ef243424bd89fe41d2af98a6d4f11cad68503aafc436b0d4df6f6f4e257eef55eceddbfc2b592376900a2b47241b7e6ed95a64fccb0b3e469b0a7dcfff76ad51a790f2c8c074c620507882492f2be3d67a1a75659dd0f95d0b5ff1e7e96d50bc8f532fdca80e76cfacb8cc82e232e25e496b774527991ce4f069fdf00ba3dd5b0626ee6793d787234cfe22077d0726d11822e17f65f64b2047fec5e9a46d91900effaa7ab56c294c1b411b69c5577023121fb05e35de2a64870d610135fda474cb3d3dd704bc07db8ffcff1c23ed87f11d2474421a5b48b5f903cda5c06abc09a2c4cd27db94ff36d9b47d53de8279cd6afc178e07609190c8eb4e0d39090693c671ea940aaf873af96d08752883aa1449c86608f05a8d5434582d37dff0e110204b4a25680fbd095308f329011a7629d682b08947afc5860e144125031f3edfd88c6c4aa13fd4ff1747fd59e865d608ed0898d300d76f0fef230bfda38e4040194a0e9ea2211e10d06f20cfa603cb945620a0a16d02e5e6b0a80d48980b30b6e4da40a794632ea28537577ba662bf858741391c55a5c0850e0dca4203da6a629b1487a5d4c00afa65ae29636c9af121f67c7691370031b52dcff7c0344388e2dc35b04b5820ed7468c5636d57f4f82df8f98d3cfebf5fada642e0d5e722c3b655442249cc989224c25e8e9a7dc608bbf2fd6cdc69019165d29f6cb731897e26bda6ede995219bd8a2fead9cff3f021d5731e46c8b981a031d610a708a2c31e0ff5081b5900169d8cff44a24f8b04b0f1e317416bac87e86a349d0032eb0832b0c11e6bb2a272939422fe234414264fe685387ac26f81ef0c3da377a02a8ed2be9c9aea092302df8eb7a0c0b4d0e78f4d060c51e7309e4140b95c2e0c15a4b54125708b3c70e72aafcf8a9a6bd15d4f7285f7efc2cbc1fe4b5dbc900a2021f4bf82790325a37325d89f0f1faab52e1662617d919c1e0f226fbab30236426d391594b7c6f10d0b419803d25ec5f91178a4cf88a320e09f86c224455b9a298d13a71dc0cc26b76149e8f186881f2245b4852a8c1ffd1d7f039f435fc173d3dbe435f47f87058581067d0a502306f5fdef805b1dca3d4583655122d20108359b19465cf47220dd431b71b9320c532e0f826955196cd4c3801bf7b593e43ca617972cbe400959ff01e66754fa807947bed1ff2caba0cdd69a7b08d788d81ac6fec0592e8c530a3b8d78730924e3b187a70d17f8c8432440e87debfd9abca7c07907a887fb20e3298232a4d38c28dd439e15316621377cd19e241819cd40e96637c93ae850ca4c160f91b1350a8aef2fee0cf308093b8e1899b3820696086fe10a2e60921b241317bee457f10d33b8762a9190b225667bd29549b8e82b504e081aeab934eedd92adec628fb13a63bc2a183bec75cc31fd7748c7e993b0878364256412a6231e92fd5d0f2b98491a2086f6ecb2a10b12f94d519483a1987ad61b7e013410de4eadd88b2b2ae3162b5f8507a56937a0373c269844d75220a152f0368d189bce832ce893327271096c3839e56e306d05c3678f6a42ca863987f9d8b4657cc54e109d93f424f04a5f7bb331ccd30bcb6388b476b3b12d635a570d8aee1dfd9cb2affb4b081587a7c4a536f688e1716893e6fd448151ca1e6a0814a28751d0cefde9c407bd26b429837b591a4b8f107d56ba1764518d3fedb560135459920229ce67c6b9cd6ac946882fd717e33e94486536d89cdcf265fd489d8421e739132454226a89604980f567ed7261bbf4072bdbe337d653f90360394627d330f91d6d8c85fa661ebc561e9dfeacb87dd880aaa83a719511b1cf693112f324bdcb01eb05b454509dfbd0452d8be7ca7d68911f67eee420ae47f38b413612370c3cd9f34a133086be2af8c3d71c132418045208a4c585aa792ecd1fdc8abca364fae5afe2f59e3cd52cc018fe2a8e9f0c28609534e2aa19fe39a71865d333554188be5c2ea2cf0e1b2c2aadbb2c1fca212a6278feaf410624128b7dc157735cfb1c414eaec523ccd658f57a0f6def4b99f55b37f3d2719de242d94c143c40ba63548361de5ad51ed424bfb2f4386068e9d24462bd3aa1690990cb6732d6430a27b22f4485f22b20f9821c316ed0762dcf1c323eb6ddeb0eae1ece35bc5baa3391494842058b106f85ffa5e6dd96823757f53eacabb462488dcfbd5c46ab7c7dd9b180039677c0076698b04a4f375221d2b48869b80be7d0c06c643900200a793e66fb9a8e6a53c60b1ba8de94ddd4b8aa15ed1b3896eeee2bc930938f9f0f405421937208b4cf08c629deea0aff0fdadf4c85cfd2e154a4b783be7268cd9d9ca1631a9c7eda2b1512772b036eb05f2d9fd99274128ab3d87de7354fb92098109a0ee74a396213361fdcd9515124c0b553e708f16012e69d1a585f951fda80cef7391e0d99e5c9908c962711eea181ced428c4ee0b7cf6d7ce8b10486935bab3e60fb07e1cad0ad82ada22e5c878f83ce5efe1c00e26c31aea221e5a57f914dd3b57ff8dc809f55ca287a01060223ab93452f70e536bdb5671525179f70cbf400dd67154a2b4931abb070bf372dcfff90156cef77376ae2784d8e9f85dd330d1eecf0bbb279982a0bca3e573a80a982ee52e500d9182b962faf4703f37b9664fe00f4a332563fb87cf5d9ae648eff8388e988f3755e6ba46c2620d5e954a0abccf3a552a4d632a811478c3f5d34c0694d93c2289aabedfa4087da074fdabf650ad447eaedb28e3acea0cc97b062031aef0c0085644de6b782940bca47b08a82de970a40acb6e4d5a29eef9c10201409b9f9271b3ccc273ecc7f099a6f01201abd56aebd20f82c8ea9026fe34ec0a038e0b70e9d001a11623700d540cfd2147dfa4841a320ef2c6115f97b78a85c488e3098b398b43f52fa27074ed5c5393fa5ccae4b7df8ee218441c437bd2bea246961e11e9d13a74e0959fd5be658a48efdbf4b1cb5ffe1ce0b6d26d28fa8b5126a8a024c356378a901af2b11868646fad8ff02acc169e89d06cc0ad11d61a45633e38e6c743ca3ca4ec77331c1b82adbf529108020ef93a589cec1b554925ec6f0be2e6f92baaf5fcc46a0b0dbda7f5b1d78e3699296410a8323f09a81c2fc6a23a1c0487d8679f0cc81a9bec089177aeee46ed5a886ceafa651bc05493350bebf22da107c2d0451ed33ef6a570dc84fea69ce74a42225374e2d07ab94808d292173203a218455c2daa2d99be4e4954e01bba1251e0a6aa782c24783613657098e921ff135029fd246fd1b4ee511e5dcce589c30bddeda56f173c0ff5a8e6d259e5602ca578555840c9e68bfd5b17746577552b52c13abf69816345b10a2820e132c7019e9600990e0e8cd7895c5a3c8756e7be3a4b52ebc34d7190faddf388bf3e4eefc5c18804e94f8c342817a5fe4f3948621aa95dd213e809c1f82893d08b024740bdade47fec9a2a02974d1f430d5f96202d33b0b820cac42f492b34daa746e56c22bc847a8e480599487bace9076162bfc3d184f46071773c579071ae49cc943b31463ec90b4803e816194b3c2d345e785f088f2e4ce402970f2dc52fd33e28c9a3538af6ccd69726c7f5c063e39f9661f9d60b361f6ec60f00f59e624871fb3f7a1c3aef6cbc516270d538a4f2b7869cef6047a387e5cf0480579e23534301ff5214ed5002c3e787ec336b966bea08841c16705024aa275463411829f9a315a07e615542635ef8dad4160b5e2885a0e6e5abb9634d98d26cd84a0d9b9d3cbb123e1fb2bec854533a79946818a85aa0a36926e6efc8acdbc2f7d5042e1ba83c42baa6c4cb3aea2fc8991c42971c74a72b0c6f7500cbecea02389d5a55007e35a182af72aa476863be75a00b4c04f8ea110a521e82ca642e9b2d958a8105333eac92d740407a04043e58843aa13bb885c8624d3f665e59836da311e5b960c3b0ce6a67e629c3be4127d9673b0a36b89f222544ab6d50b67da34a4da189bbb98cf5fb22b837e6e7aa400f323a889712121f2b1a38b2f8781c9596fb47070e29c960da8ea999ab98553ddd1a468a6c6de6ffb6417fb3256f423a6bd9d9d43c0ead745207456e545047aca0b4a11c04f58a465f56c43a082e50085c2eda93e4cbeaf6aca99d91502061f5aee7c6199d88eb87704e19b2b4c0316deeee4e565f4261e341b882adfa8f00fff3e11d954036a854014e35e27f6e0471cb22e8076db20253d6c8d839080516858a181384a8cf025435c38ef961f9f444ce42cafde0a92d3fb43cde49f91e797a9fea8b9be7c03333cc1fba0dcc7c53c6ee8bc8c222973061bd0e5fbd5b66604d4bcde8223cf89daeda420a91dc576958ecf85632eb2b09e48b856d63104dcac7d04b0078d2e0352f7d44e4e153740f0fee11f429dbbbf2b043ee7589ecaac672b998eb4ccef975328557a078c57771e4c4bc041a8fdbf50b3fc36fd70ffc309a288f2415270c72e4ae8744734dd92089bb2df03831acd4463a30f9cbd298b3f26b4ada316c42583f5e0945e910304c11c5e5fe25ddc0689a989c30ae7062b438e79af7fb8b3ca1bfc5d93b392596ed2b4608237213400b2718e5c5b81832229248ce16805b9fc0746ad90b89c0e144776884a88600bfa2042a090a58fd7aee32785390eefb88840589bf008faf70db8a535b5e33a4582dbca119ea2281bda116f7c77d205d1e225615bfe0a2c0e1a900fb064befb3a2e37393474f9cd7552c433572ffc3c2727fb6752c2e2ac211a3a6f9895e5f3259f8522fab96c48e2e12c30509b1e684ada48ed40cc12acf374979981c63df9a2c998e0f2c6c12988707770702e716c0e233817fd0e25605778a8a53598660d926b1f70480dddad671000c4815102ed5df0068059f2c8a830d3655a8801ccc22b9690457504b57666ba608d1a6572f1ff1d5d49d8b9d76f94f8f4606d6cef7e8119640053cf322a421e26b4cb693266e93648051e8be52adc79b15c51a374cfca41db8468ba5821d114e915f09f24756144bd91807dc52fdd1c0138ff6464c3110eb937b1e13c60fc4eb4cbdad1f5ccb2544653d15ff4af89921ea0746d75fd3298f3df742e30732788a606b55e7ce276cdea542f030aa8e49b608e93de71ffe864808906787da745da7f247807d2bcef7c673a6980ab5d3d46ad9637d7ecaaae386594ab28e927d419680e242081a880e2170ad92646a6f3b1a06bec030b671fb6c11d1efa331d66198cc033083436187027b8f70a388fd744f217c6665349efc467de9a7e2a8f077febef795c4baf88470fb92f8e243e9fe5186f9931394fcc832aa458368820c730c16d30b4a4ce47aba38e66c2b52bef588c9e0aa28e9c921d1c417661632d9c3bed932a93730e4f5e5cdaf04577026ba5ec964cd03c66e8e2c057c3384adc05da94cc693d8761e60d27bb06d208c7c6b9663a2ffee2516b774fcc7a0f69143818703c12e10745ec16948074c22782fbb3b015f9cf0ef91576179150e500f79d89f69236547667608e4ce111356d02999439da57d171a285ac7c563ee52a3295113d2f93dbe56f33658d71540533a280764dc36487d2ee305bd923e0aeac916910b8b7af093fa6a761d74b55092c9e9a6d42de26f330e963a830c9d43c5ba881e260f1fcd7007e89826cbadb1ef0da2fc0e5efd7854ec2aaca35f91b7acf0538c4b55458f99c6f88f0fa4f317b2c003a18f3b949eec77a230aad8c0229b1804a01a26f017b689a6aca9e813daaa6d1756075b4b510703ac26e2f0c2d93535cdc52f8e72feaba8dde4c538cf7cb56d8ac363a217159d537414463671eaec0c10fee6af2b74a23cb7ad078fa98a1462b0bf8fb136ecee600c36afbe1740a4e35d70ec560adc6acd4ee5bde5c7cfb5ca3928de9b36d3d04bc42687c32b058f9a932248a310ac5a5833801410465e6fd9473338101c62adfede1342c81ae0557570b6c6a45707754104d178cff6ac551f147a907dc2dcf6b58b114692ae120835e134550d02bbbb68d5a5c4427b20b08b2bef55be100a185dea9b03da8d054ae24d217201a11fea3dd368d23fa9c206421919023c2d8fb2c3bf7ff48698f1c8eeb530b067bc65c61bd081d92e1c9afe247bbcd9d2e296207ab90c158c5d35b08f4f82058c0d046159b34c15e1a4178bce128810beb8d9812458a2d3c2ddf86ae7850a067a4fa8e45073f5387267c2c1fd4e39f484810b6001d3963954bdff2499a46b48442ee9fa1c8003aa79974a4741829a42b0273b1b58fe36d7591e89c1222d742755622fac88b8b81ce336486f9c8f717b40ba2718ac54dbd56005626a90cbb4218cbd2c00fe16eec0e31f4721085655fd4cb9f47e9e87863bec6601083cb06ff3ca4806b4b227903ed1a46dd23b5b83d122e7bd8acedf192c6ce28487962033bad442c9cdb51166bd7ea5dc9ebf9f7adc54cd5a30972f908de2a89f60676ff6136a89541ea14ba307cf7058326606f69907a540253aa1a30ff12a71a22546ca0266c1859f685ea77e84a57c0216a8b0eac7b7c68e094b1c80a0f9b87aea5c9ada889e2788c9d507eb9437bccfea8031fc9d5b6be17e78e51ac70665892b95eaff553eb4faa0368f57ad94cba86242891205defc670d937d9824619b3a7e498e62b4996682fbe87b1aaeaa1acb8ba5d665004a34a7e2febbe17dcd51e433a3cfbbb2b2007c4be98b8e57b3394cd379961af51c6c0131e94d4dfc2ac41c21bce225efc727687131923751272b8bb90a1a9890b2dd08591b13de361c9f33d3b4eeb00ad5491f7d3573acf84318874e54ba49fd04ea13fc2d2dd2af284431a97d1b526377bd02c7caddd9cabf54bcbf78688d2ebdcd1ae6d0d0bf1c23edd734dcda223ff335ba9d192624067bb47ed7b179658485b8788bbadc82c45014782e9e0d3d47dd4a1e13cbeaa0168b67d72560617adfca2fd201d6f44534c6dd77c901abd04162cf558fa0f5caa724e90c4bad67d94520347ac6bb0da033fe513ee572a6e9200f33b3f1951e26b241f18b07d441489e14be3a8abf438ff2abeff2d36aaba3c9e628c1261b4431ce02517f946fa6c50500c70aabff92bd2585f0b4f05dabd2f96e2eec7f4a332ae4f6f0e29f5fd3be84e62771609ad1d2bd68c2ff0935c0a56394eeb95505e8fb79655171f36b60b779635c5e9b40340630b94587cc372ae28a6c41f7a7b67c6a46bb0cdda3936ffd3a47991882f89ec9737561f1d54ed6c890a7e7662549fda29c3d5b14a05d9f4a140ed9c4d728a993147ea4ed1ce828e4e3be139d98a34a28f17eda482d59a52913d7c7a33b4d39943503ea51737218136477568f1b602ca6a9dd4873439f75c094c31d08ad3881ab220a000f8b2b622266604ca74fe609b3ea6d9859fd2e1623c6b28de02e7d54975419f622f08ca7724ace9da17c302252c3f65f4a50993eff4492a7fb0ec0cbecb25c9615c1373eb8fa17885ef3f11263a51677abb4d052e809f0d72b5e1fbb9f7e0e5cc21f04e696a78ce21969b330f3b2f9fd580cc9a04ede29dd0f4ab523b5fe7365400088dc8922f09592cd1cd5ae658f61d2fa706d69a81c81b9156072f9823a9ca1b3c5cba19c57b10fb13607e48280d36e933b0e083857f44fb0a4e47edb06cc9526e2a7e9bacdb2db976aa0b1206f9f39ba87457b4ccbfec5856204a1bcaff70979ad2a3aef6f17fa5ce1610cddc9f7036a57d10bad639a16071c00c61b42c0e979f4be057b2ee0f718fb5062f592d14d1b0506a024b318a753f88c99708a2055866cdaeacd025c74d2d58ccceba811b7273e7dc38677fa02e53472114ea95e4dc4226d013551995b7149f883f5760d913d3a837a0056d31e813dea8903ce4e8e1f82d282bcee09c89f46d0210b4bbc20a1dd00a24743b0edd5640f36b915ee03b8f311ae3b3f8078a5723221187e14f8a2c1e659e89f1d7b3369b2eb054ecc18c53e50a4b9fc5ac769936e5096f00bdfbff87d7b50712932070902183e8302146fe639ca84b29628e18812ffec5868725c3474babb789d52603d32d724a074ae6ac4d88ea87f63dec4b704a05d23402ff74603e32d191a73500951041f9197c74d21c862f82b6e8b6cb9b5b0c1c8d1b0faa5607634248d637505f73cda3f4488eae06b8fa8c67c883d6e10e4405cbbcfbd87cc44f8d1d7d061e61eeafdd73bb3344bf6832552b6c038f60c343282686021e3e176cfc93e460a9975de0965a723efab2fa5c641848df36061559d3c55f73cb0c7a45783c9fde4fe94185d867c911ab913a8d67d3812aa32acc6d9578323f949c64a9a361a36eb0559959705f65615c57fb87b18d02a832a981ec400942eaa659a8dfcf3532a85a829107c32d38e38c36c5452fbac0718bc2e46948668422b6adba125cbb8190a233214c4bf0e7f254f8e4e31511dabfdadace19d9c64346a52d6a49eed5032933b0de74021dc9eda2dd370289353b16657245f35aaf93d87ab0f2b46a64ad5ccc02e62015413c6d46b73e07501e5f61ab295f3ad4a5fd612860db7e31eeaef19edc66cdea8a19e8ae369ace3f01e928d4019a63474cd7dc93e1ab2de4cb7dab1178e4ab1b124f3be299477ad63b82d161bc9556ef145135219b5e736d2965e50724d97f3ca20c36f8e44e630115cef1a2e0d7efe05e4e464488697538d04530a4f8a878c70b4a5da497dd6ae8693cc999f457e00030bad4c1fff7b34ca9e7665f3915cbe6513c64f77d822c57281590b502f5eef9b12033a640d16f7d8f282b8b319ee66928f4c6965b534d7c276ce95a191e54621c7ca167aedb855ad2d0d3b262b1820feacb4f79e45cc1262a6a4ccfa067c087df3f90e56a6a0b9bf031beefb9399b0f3ab2cd597dca71860753250a4fe1fc8dde4b1c04cf767d7a2ec2c0e0829f58c3a85b0d3bb8c5212dcd9c0405543d0ca76ceb8d1509168db67ba1946e15f5ef19b498bb8868342b7cf4e26e4bee46a88b2a0cc4a6e349114888047c3815d110fb2454673612c7cf4bb7c98748579e05cb7b5cafa5171611cde1e464dcb74f620f11374f14cfaa31147a0aa2e82b172972ce70227cdbd596448c50ef77ef84ba698d1cd766945a80a252eafae37c7d1c4a51fc80ce8942e4d94f5f807e8c30ad5334e2ccf38380d20ce18870834e37753df601085fc01f5c6b15acde542a2c729cf19063fabfac6f3490bce3234bba5d9393cf0a2b0bee2f2d6d4a023728154d09b62cb8e18daead3eff64514ef048aecb8de361f23110cfd9500e4c7d1cb4ca1976b11c3a7fffe79c984a343d657a6a354006f085f7317d7f2f5ce7f8298409dffe188b9dc0b9509104d96dbd079171517f17269295a19e3b32e4b581c9d1a807b13d711df60f5642b0e496f44c4482730bfbfa8cae3211d7a907ac04b5f27572c1e08b8a05757f6321de5f39a2cd3f3a3aa17d0a03b0d85c69084c471655323e4e475ca1c6b96b601eb3d4e03aea86bd8425b9468ced4ade51c3c2cd8981f11deb4823f15eb74365573c21937a6c7a748c97c063c4cda8cf912b8aa074d8a8629ec004534d68784184872fe747179fbea71823d8a10c6f2f41907c09f6b8f1bed2f90de680a8f12c901b20af473b9c0150ceed18b9211dd9b772f3efaa9a0783d8c4af0a37715352f14fd528d1afef63d940ce84f968c8c4c63f2592273fff1f8aecdbfc8f1cb114b0781276eee4a76cc08059f3373a29e77f7743b1d4f13e66a217ececedf61e16693b26931a2970a0427e9ce1461c2d9c5b298d4a8ea53707891dfa3ab186445ed3e595b8a94e21b49d00480edf750140b076711131834d92d9388278d668699603b46eb5f17c623668e0c61380006e9566f6ec405985f314cfdbdca6e194341f6fc3c483e349242252381d717fe0387bf09847479d667eecde2d50ca2273d5eb59403b925c693c0c7bc80e5c9c394be41629386c61b680db0aaba7a3e8caf4ff25b312303ad3451664d9ba8be3eac89507f56a6fe004cc2cd6d50f61e1ae87e4181200216e6bee658e70a4e1dfa6d4812d43ddc9e6b6173924ac6d030390ba0d8c164f25b0d71f81eb257acbdfd2868a6bc48f2e9b552d523c94aa6fbe30a85e78159c112f579a2bb28fccd8d51ed414b678cf5fd9df9311d6128256eecd0024226a05e1b8fcf84f8e51b278421754ef88f2e7c9990c3d98026147c1d8a5164c1b9b0ae52f62192c3d75253025e9885d1ad2fcf43ff6a268d7926d4da9d3713346003d8c15055c1ac328e7b716bfd8c2cc082dfb400e2819691f6b8348a1646272e4da4aa8dd950899607faeef60f81991747a3aeb8048e61ee30098a96f731e8944841c03896b988b229f70514e3bb44fa23504afc2e79d394c9977d24512cfc07d527c6dc73b4942d6406c201d20b1932cab90fa31e9d8d679ae7b4184928ef0e456b598007a6828434b2cebd7edfa6b76ad355570363e87340fdf97391898d4fbf93f1acd110db8893b515e70bc86a051ec9e38c0ea5902193a4dd92897cedd51edbd34bd623ebe419242637b8eac9221577926b3ad02d3c17ec269991dc558fec89f5b5838eeb5997d7663b1047c28f08e0db999ad0df2483db3226341a0ae23a04d29434f938baa97bd9bdc08b082a17d9911fb431ee574cc921d34866665a51b41a763b4503f1e5546b8766c6aed405d99e26bb2985d0bd17857b824ab30d76f33e6db404302c52def2da7e3f5d0259f35ed9beeb058ef1f9232e7c37eba758f3991f61f7d890ac8b1207e18324e4369875912f5badb1800e4bb238b33e856acad2bb785e80e06d3850862e2c97a797ecae4a8b1208a5a7fceb821a117fbba2315a96e336b6233335919b36474e904251dfd9cb44a2382892e43af3ab0b6aaf90d88a7cc99019a529d4d5440d00cab0ee6a0eeaaba6ceefc6c5e51d58586b5f542ac250a7bbe84ba342c6bd5e551c42f3d6264422d3f00ad7d48253d3394fe0c18733f5950a96fde678085667db7e1db8ede96cd91eb4ed7df769272efbd77a404cd04d5044bc118a0e438a3384e23edd25344d5b47c182d21b5254fae9bb446aa6348e9bd39e79c0fe5f87cf40f1d79ca0a4fba39e779664b6c0d4857ca49cc1b9e547be2519465bc9987841e06b1321740f8f4947ee99996a992b1815b6ae7729e5d546178226c6f478dd719bb1ff96b72586e8ba73a2884672e4c4ca21d0b08168d0d8bfb84ea43584f44426ad0a459235b1a309249cdef99667b6e255578332a5d1830829e9915b7451aaab8bb36313e930775896e429145cbcb23aa8cfb707a616240705c323834f289f4e1a3eecb8b3afaa159191afb806dcd1846834392cd96cdd0923478a9b7b3f20145b2477594117a56edb8e0b0743db331382642233ee49ce6fc9d29facd39e7fc4ae9f3d18fc539e79c73ce39e79cf3990833a1386f64508ccaabaa2e58ca564ae8aeaaa8096864e62d3d994656b2b7116757126fea9a735641e4fbfe34c5ffff635139f7d1586bf4b34ccfba43c7a372287c092d288432092fc7d27a30e1dc3bf47f28cef17eacb8febed9dd79618361d21e5c17c6439363e92bcd29134268cd494583727acf1d9e698a9883cfeeb6fd3680dfbdf7ae116695335e09c4f45fbc6234782c2458212c2b1a1b167787d654efdea148bdbbf72d279916840f97aad19377eff46ccf36adc104d921b1371072405b24501a19eed0299588a843ca3024fd6178722839d56d8b76544696de292b249b2349d24a18b4cbbc418085304ca9604ebaa7053ea40fe943fa8e748e2fe80967e3143a5ef2d9c0df1cac406d03e50e1678ef1abebf96831d381d34a1be00b0bee850db6f7ff00275af31de570420ed7b4590ed7ec17db02c42d374b0bef74cb890b8e8f0de12b5fd063530cfb6048604c6868484dde1bd253032fc6c1648d8ddfe49c0c18b5f0ddb408edb77d1020bf8e50002df1abe900052a8255eba7e8314b67cb181805bae4071e1010eec0a3b0e8280038c57b635dc16ec39d87b6dc1ee17b7053bf8af7d5bb09f03fb820d9c6b090e302e4cd8c099db133670e2a2c3840d3461c2a66da0ecebf0de1336f0a781f79ec020c008e17cff807270031318166cda06d63db881098cdb820db47d768709db277151e17d45906d0a37c74de1a20508b825306698608db171f1c3d8f0d1af081716f3c50d648e0a718f7bdf587eed9f2ddb25c824f86c79a3849b828410e29f8fa6e3130ec8184f4311a7086984b1fdcef48b857599e319a3d6d65aac333ec13a8573c7d050d759adabf5966d27b7f1a0ce5ad4591b215d0bb14af0f0c6786bc081d879e6efed9c678cb7e59e31d7f74cc7f008b1cd2f3ac6060cbcde330c5fcc62f0083346acc4682e6fd9de105200c56005c0b66c0b4f10834798e51b6c70dcbc4618db33edec305597ff9afb318437fd18ef5cf240fef08a70036ec6d56ab3daacfeb01a73e05ad62bac8d39fc36e60073fb0d02ffebac0cffe2e111a93c22956fb564feafb7afd27ca24e559543210cff1375aa5a676bed7fd3596d2e3dd5d91a7bcff1f05dee0db8597dfa9f6eca4d8b3a5bcd01be028087c29b9e4fde1b063e8b70038e95f03156c2c758091f49842ca06d0e8c9531875fc61c7ccd9843bd1473003ae3a7980395e3ed3708f43d8d0114fbc1c50bb826a71873986b72de999fbcf7cc81da1461b7e19c98c306cc4984f011092be9461490b8322dc5d3e2e3ff7fd88d33de8e38e3254183641867bcf786d0c2e5feecff73695f73722421634cd28c3921246ec4bbf7a48fa01e6d08895259b0e928d7c90f2c704d89514cd22ceecc5dd2da5e4b938f5b895645760abcbb4a6c18dec74f52093ba5b161719b6c4945e064beee44c2863460b2ceafd6ff196806a1e9d641620625b97a3ad9a2beacacce8ea476d80eb05a90b56c19c88748d501664c789c82f448408c311591897838396dd56481a03a5276d31477d55121068286d5c343a70a262a86b743a7a3d2ccd919ba3f4c0a648ab2c8c176a0b3ed1f00e3d6a5247d41bb314271a3b45e4747c52b13ef9c73ce79c5cd39e7fc86e36e39e79c1b0597440a34cef37e2053ce51d8e52bcad163cf51f1c95112d967db8dc9bbf7a4b968bbfcfa91503c65b95f53cf235a5171ba56b3b42c191f281ea75c5c0c589536a1c1bee9240075c4af8e9f3121a91101885cb36c96604b4d9dbc0a5fb054f300c1e50505b4b25c5230b3942b8e1ba4da98d26ec7c34ef4a37b7f9b57efbd772daacf477fefdda616b1f48c7520d52c29698dc8ead1366c0745c21a2483034ee1dcc851a196923b6d91b4646d490a8aecc8381576f5de7b9695d8edbaf35b8c2a7de13971c22162c22ad3a95064810164455022bb0fd42fae18c834956fbe288ea4744ed12747af6c8e96e8953b22644e26fa1d7ec2f4ca8aaa9c4f8da76bc3d89abe4b4a68b45923eb3ea660f4e18889a5ccc7e69dbeb0782c91946d35e5bc46d0ba1002514e178a4d1777fb8225956c8cdececa5b57ef832c58dabcbbb8ead894632e6152ae146f0e810893cf1eda905a150bc61aceefbf0ba842a8289d864a452b005a890ab9aef19078e022f7c357f57f7f1ebdb2617055712c453ce70796301d649577ce39e7dc7773ce398f12f1f0aa3c5b4dd691b4675853b7414d154b5095ca5916b692955942723631622c5533625e579948376844f1eeaeaaad1eba5f6e92d255df54c2bf066ec7ac1fa72d11685a9268654acf0525854a057a44a4b1690de6ba00bdec36ccc3960e199c0232bd36470a8c848d40f239dea0dc0c81372d12b088a68fb0ab3430abd49a3db3a5e2f6e6d452051536d6834581b2d844bd570f38446c32321e255f5a253144b0e9b10acaed9f19c3c5789e9f476486b376ea4cc371618ee38f9ecd9e901709d75ba0ce46c4054af58ad1f865f4fe7fc9deffd3fb6d0d21cc22b09c5b70910d1c205ab5999d159d15a81be648d1f37835538785414a7dab3914c29aa8ceacff3950efbd77227604636f451617921f18e6dded79e578f70e65d593e576345ead42f0ce39e79c73ce39471e4f7c66ce39af939df7de7befbdf7deafaadd9e7b674e6458227dee8cd0e258a2d72397b8af956e99871a05f874b29d3bc6efe05090a35d6709284775888049fd69929e646ed17fca44cd2faa137b6bf158d22e8b96c79bdbf58bce0808d38b8c920687de9cf308a0619570974e8a3ade6a92dc8f197647c64b83cf352d14f09373cecba82837e7579e9bdb354cc2cb667c48ee93c1735c182ba40acb534d449ba662d7b503027412a097d0ddff6f81e8331983f5a6f4a1011b34250f761e504fabd7e172e2231862e2e4406cad106354ae536a5a6243acb1331821b9890a73ceb906e79c73b1ed3cc3503856e3b241bacc81407ba560dcb241c924b3a809119069eec172c4d88861b75eceb04c5462b8312114fe8b44676536111a85688b13335afbe1766990d5f559ba9d68b96290de8492b9d1a81b41c8499c737e876d7fd636b322155457b63b8444c6f534aa50054131bfec0901f1879d7727a5c6bc8d401aa54d6f4b96bd96a8dd1d46ec318f39b5dbd77283730360467c302202f57465a0c7adab84653a83974242c71e60376a055ab421742f685ed33722b8e1ce90f583c926ee8869b1989e41526e4619b56186a5993c8d7298c5a3ab2a6dadb097c3144effffed94dcae29bca299ef8766efbd77ce39df4e4f38e79c732eddaba72affff764339e3fd31bb891050a8f342a30f3ea0f335f3ac42a581a06064eb0828872c2e19f598d19bfecf5f87805e181a78b221c03cc4f2628e9cd79e6a728407085bb3c123baca350808c7c72be67d23931efd187111b180a54d80e5c179f7ff3f880b781d88025d4f9cf1de74485af8ec996a7951431982404982534229b902ead2d00b783903386145cbe828ed58a7c23142e32343d366dceeb8dd5d3892f6aca3efffffffff3f642a442be46a412134819be820824bbfffdfa350caa993319835ece3979cb586b23306493e54e987ed13f3b556af944e65d3b1e99cd3819a8c43fda599cc604a1ce0b5aeb6cac8e3f0972009876ac9c3889c470a3ca6a6cbab882397bc1437bb0d0e10e1baee89589911da409273f960a3563de29560e86ad821ebbfe67374e0a821f390a8ba46355913ac1768b7c4a78e2a73b4d7aac8f23093a6a331a42ced8ca3e7e891a723857213479bd0743fc0b4e01493ac825dd976f35ea89282d45688bf1253e1043d972e90a21da327784ef6e286a158807d9ac6a0ce3bd0e5b3f4563613f365c4b93a1699d5e5dc8ce08a90eb6068e29b5e4eea43644a05c9c83157b0ce8626494e714f0e740a95babdf7dead82422319aed96c5abac02d8d2f5870dd3b2bb1a32d5eddd387883218b9b5be28cd714d95673c2541a198fa036777516745353426382ec42321441454d2404a99116bb169199a81c28a256d8965c140f178d6b8521626e426debb8253ffdfd48dffff1d5bdec1e5dce25efaa198a4ccdc8fd2acd039fa8e621e6e521c82079af949bc73ce39e79c73ced99a6d756d7b37e7642fd5f6e6f6cc39fc9239398f6548a816cb0625b7983db538981914b6fa2d76e3ca196f0820223233e3d517b2bf29a98189c100254752dcdab88aef58f14b2669e498656b5b4255700bfafaf0425744d2d5f61611678ae708f40eb4d6aac83eb1bd5ae50bee9e9aac687bdbae19aa1d514101a70f90b493a79a28cc26233b3e3991a49c6f32acf7e29ca3d1d9370b6b730aed2cd649796bdb7be769519b8abbc38248d2a1368984e6cc921aff2ff7ab759cb60b5d285fd29232a11ab3107227d038eb94480bccfaa59938e597aa9c1394292c8ac4582893f0c250eb7a0ad37db1fe605e8baf195882ef5f6e4747c70885f0eebdf7fea1e41f4dfee1fca05a7c54ed0fab860f6c9cf1f2126e09c43efaffffd538987749b33c3427534aaae7a54c85ed0664eb74c24651a9a29f105e781fa28c734166c6a3a32c5ad31bed3e753c7ddcbd375530f200e8a412000a47f5e89a60af23bce66c8cd7302e5cdd9a0767ff518709bbb0f7de9fe49ef284291e68261c2cd4edbdf704c4add81d7598786adcba5d4f0177e7e98a2d41894a6744b34e8565be919373ce3d9c3e0faacfa3ca390f2e85640d1aeadd1c0e5bb1ec2b6e59f353f3e1e6dc4a521249723f66d81d19afe7ffffc8928f2c8d3c092d5052eaff693c66fdb96a65c380a127298b0b6578f5597aa170dd30d93ed03c10c53d1dacfbfb3951eb64028842ac9c2c242b5b6230642f67395237314f2c83e1114b5d93cc4f8f07271fb84c20c3a25b7764841df859d5785a85cfb93de350d9b24e3dcee9f0ab98e46a728282c8c8317910eb70a32da778ce1facc9de7b171b8ee1d5c0ac5f5a09e79ccf117d7345611ae79cd3551fbb287766c0a64aa5a7363d1baa1b242e3325979e988e8998960a71ce4cc734e52505a66aefbd5b5928516a94341f5aac93c40af3ffffe36139de56587f4dafdb2bb7fe5d8881089e1196ce4b69b4a0891a119d123bb4e6ee2a70dee5127ca5566c6c8d9c2e3a7875341b5694ae3c7a44ba5a735719f1cd06eaaf592b75c41c285a2428b565caca14170236b54cd24e75f3f2e46ce6b68e362a77ba3ad1215b2311097b8a73baaae0d444c535f15aefbddf78275a486d06a8aa6712dfb0a22c668b84a42af164855d696ef0b622ac4be89e151d0d5454f9ffff5675f26906faff2772eea333b8cc4f2dce78a61894677ac6097b20c53a923c011aa17af9d24e7141efd88a4561f12811cf505814abafcd43feffffffe14ee05ce06a2c98b353e8075c8e5968aaf23ac939e7db0fe69c50544f669a0e93e9e65c0a4d2e0f99a4f613cb6470f9c00396912724232c1da9298e8d54ca6a044c083ba48cb8286b3b9273527c43318db794f6cbadce53b90eb98b38b920c2498d673a0e0b0ba34f3385055c950612f2971c98e3448964c48810562c42dcac961fa68a89db0cb5cf54598863667aa62b1bf56eef2e1714b65cedb169460bc70360d62f27f2e844bbabd1001363ffffa1f3ff3fe1ebccfd3f9358fb053c998273ceebb8bebbae2bfc71ce834b1be1537d70aaebb73ade1e6271be73de77d418e5ff1feb94335eac84fc1fd3b33618e217e09c972176c9ce2d5c18678c7586d87bef6ac7defbecb4f6ffff27b36a9061855820d98a6cfbffffff758570da7beffd58bcbb3d935c7c927cc585f3e6f7ff8f82cbfeeff4c109342f5611361f163b181421779c9a34584b744119718b14a5eb4b5a101ae9bcdd5fe14b6eea4295d7b94e122f404089d393b5a5e256153455f22ce57062983a42b0b1002770b41a99a2dd37b8978d46cde9415d1615cdfd6c58e9bdf7bed78c335e27b56ac5de7b5bfaffff834bb5b800cea1350b7b57a7e5f8580181b323968440dd48ef27bec317e18cb541eae83ceab1d8803065d80afad11b8144668c8a3cc52ad43804d5a711a22ae49c73ce69e6097cec5610b4f05a1b55455bb60208a556702ad649d8ea846567ce09b52582a48ccd9688286f9bb204df189d31756b4d3d403f373a77bff990213d5a6942facae2c528177c751a0240ad8116794a58a44898b2d680f05142ba898298a5c5275378c39a968b4c84196700eb8e47b3a52936149b28c93413c30c9ea0cdb099ee16119c0b54d3c8d48f925d4d8692a79ae5681776f5d0cb620e2dbf51c51a00ff1363a36a6929027dd08a8b7252316bab9982ea6563126a07325da0db6694204a341346b336e60102631eb645555b3c6b77630618be53c3873f5981810384954cf428a9b3cd9e4089a677e503133542a792211e217670a4fc4ccfdc3aee903fc42d4de4b2d16849a9c019db62912c9461d949138f0c6b8e08aec8a346a48e2eab839014a884ec592d396580520b000003190000022010c4912c1014b9e40114000730b8bc98884c34140c46027128100002006040101000008080400010080402e12090d7da780090f10bc9548a63c5a811a94f5301f5d5049dda0aa0622c6839e4efdae35ce66234959ed5e66df6f878bcd7222337477272c53f68383922e926daae85f4489305dc8d5890e35b6af1ed820aeb02d7f85f6505f03908bb29705d3f9761a8a9dd65be92e3278a1ef166cc6006bcb3becaa9dda2d16d6b6c39a28722290ed794ffeaab35a1a8d25c4f2b5d1a4ac20a500642d0e36e416d42fdc75b2fe1b7451a1fe65f0093fb9376f07bc440f643d6a5420afa04661c36720bd97ff876e4457d19f6bdca068b958a3a5c3b1f8b410dfdc385dfe23e444577e0a5ce7d87d5ce7f61c51d0d78dff69df06f089add823de2b8ea180423867dca9c091e86d747c9493bae587dff74b3bb72a54efe484d30e8f79f3767f44c327b3037553f7ed39f3453de93d0da33043f4033feae8140dc1261d6784b3ff5e567600ed0929f58c3eba994dc06c108748c478066c109eb8f3daf3ccaf14aa2829b0b2a8f806e76e124c6bb5ff2bd52edb25818d1a267795c9b07ea832745ac76c832cb4d5a6c9171199216780dccd65b222bf2b3d6e9488b72af6a1be70b8d2c6b5e0891d1a98dc19bff98a0a8eda848774872e6806bff7ea37900665f6832cbd14c0ecf0a49adfc17391e3dc0b38e900eb01a0d89b135fce8de6f313aa8f6188abf2ae1e872b0a59b2fd1d16a5980f32eda3595bad4df09612ef3f4f0985bf2b46940fc41765d52ac1b0da0e606218efb7d1f68e4683bf0b290883b9992d33695e9d2245b18e1d9690c439b2ecb82f2bb42f65d52cde1d88161ddf48051a21103bab9805582b624b5c250d90e879f3721449aa651426b295cd9936226aded346b2bca62448e310d86c54814696aa76b4f3f4e2b203f66a293d4546161edac68d0388641a5329d0be0f497af127a8f954ae24c96d7cc71f978e3ca2bb5d6dce26ab7dcf26ce6ce3d9138cfe407a15de1b867d5a56a01f05350fdf55b55ab3d0bdd156eba01630cab87a60fff607e4b0820f5135c353cebc3dc3749c1985b4f173a2e55b9de7cc1d40a09a144a4b35552bb5ed1bd4b09937b2c87c8fe8925662318dc2c1d44bb3889a0cb497eed7dfd053407b2fe53e801c03feb66a0c0a203d133faa661fd538407ccd57c15f259de106cbc15aa477e75c19e3b1520a098586cc4c3a072106aa0f637f496c75741bf613d471aca4c988520a45504b8a3277df0f9859716bf8aa05fc51abe00785b73f347e72850f74ccfa36b1a2452dfa643e0a11d3369e6a477999b0036d1e8accb3fc9e3cc282bcebd0a0a8d846d8d3cce8951e0bef747b32482cabfb3bced7f6284c0f3e83328ebfad7cf0f8ab82d02b7b61fff1a13f0c4b7ccbdb15272158c5dc16d30bbdfb33c74dcd0798437e7cb6723ce0e610b34068868e31088c037006044047bdb499e2f9311b3bd79d2039849e7303440c47ecd98b5606929b4db02e89f90d98186168b251996a5b9594841b0bee7a98a9049c8a151329b4fb023beac7c8c59f6543f298d4ddb63a34f02219b4d41eac82dd8920e5f6e8de87236dd8f7d442073ca0500bf19e4407d3c0d79cb8525f91dd05105f71ad1106a8055e13112aa2244c3fcac214588a69f00fba0873e7cf688b4c0c809bc5d34ec719d44a4a5d7f1f936a3901fe018c3a16db634f518c82623cdf3fc17854ba6f55c8a732b6461ee5bfbf8e4ef0dc0945271a7bdb4b5edde7f2cf2d9d68cbf8d3e5fc1d181a39448f1be14c723a038a8c3d7f2f33fbfcb5588fb7c469640ad9704174f5accfebe837607dc7b9efe3d3e55e9f125bbe312fdce079afdec0bc7a2560f7cb2704d91b22cb77b2636a7f1a5d64034fbd4cbe1883f606e64c1deb3a9872e4225f2cb1e71407d66c921f52b1113ceb49ba45c7132e4a3d40f46acad5415933aef75d0e14ea0ce4591c6b4c74f5ec07d4df46b7772203793a9893d4ec69e1da42d2d1ac28eca4b224ab47108182ca2e33a213a4bd0373cdd79049017976bb7ccde789581b460fcda3df4aa4e8c11dda40f552fbde8b5fa0ddd89b0bc54dfa52702cc3d9d6c0ddb454cf60ddfe53092ad6d8e3e2e71b29fbaae86be0d37409832ad3e2e32a491cd4616bdadd44ba1dc2c6a9f24282af4bde4f09b09c3450dfc099c81f822266303aad0f1255ae2f8c4650bb96088b10a78f468349e121a2c226f8d385ab95805c92af3a2ebb63de5db9d7e79ee4746ea9e986d3be539631aee44f00ac1fd8d22fbe4e77d90fd62c2df6550ada09df47b88852960be4dddaddc99496263a878eb69a30dbed0ff34b547ab9073c705937bc24d42809da8dc4f7bd151410915acf9ce360d6bd082c14726801cca4b1aef2455b4bb39b49fd5eafd33b16e6125bede58fb2d2802389231aaa71e947584252548297997899d917d58d18721b89a861b73000ebd5363a1a45c5a177f5548e9ca14d095839b9a4424704b6b4cc185d98dda3a84173266b92ac3ea1b31cf077fce495daa747146ad2e2b80d243a194a502b646fe228603fdba9a04e75dbf2632510844633e30622ed45f1f0da58714119371d37c316760dbcbd6d43c4e2ccfee5b89da86926d4f6c7ba46e77d1c3861ce6ccaa34478dd3213894937b3ff06444a6075be4dd0beafa62ddc27af7925f1a633d3bc9433bf0f56604da6476c659fb209af907412d0f52db07219b2d96d60c98366f217180b70aa0a966d1b87fda148d875ffdc9f2b47d3bbc3d0bfc08eda1e04fbde39c2c093e5a19d4cc5b84342a421035f3e305c83e328ab593f72fad2f73600c2d9df4ef04a72825474ad3d817edfa275c44a81038124e88ffc52c186bd787460414c2309fd5619d55354ebf85bb2ae89a890ff6b77b9eb4c848961f3029dde1a077e541896c41da7c8449a0199cce299aaf7a8868dece31fe2814137d3f2f706db9eab0074d915d4b3fc268cfda95245999e79ae7208ccaf8ac0dbbb77c1a2a407bbdc418a0d2fb86de154495eac9c227538a06a2c8ea5a97f4932478ce9520def8480c0125a356f422cbd69cbd0ee78f24ee4b15a74d997a8b8e2047cdded922fc212724ed0b5d551aed866c46e50ac51a86d00efff15ce1d20c1e660cb30a66eaf9aa4498f2c7d7dfd517932cb1f210c63b23dac34defa3edc4ddbad1245b6f0f9fa01cdf90967b9bf8ec3cda22c8e712553880359df1d9e6f30746da77bdc981637d6d48ba0143b4c0b244e95a2befe9732cc9d7caa40e8612042c04d49cabae3dd8847518011c17394e3ee695981456d06126e0b71711eebb665f3346c6a0732d2bb1990f531cb4fd019ccddf5e50a6c341124aa74bdde3bbda98ee5a3d7f064ae769453030b153fc208387a55aafd1671c304f7bc40b0755ac57695a22e155d3b2bdfba2af1bc41e7ca9e207c00319088021169dda6416456f2e5fcec7472d4d00160108cc6356d9ebb5b0571ed87ca3b36d5480b63badfd6c55e63da8c7b641fd5efac6cc747bbc57ec833172dc7de7789abfd2d67021795b8b907b7f4231450bbc9575f333fb021c8fe9fda3986694a5de4e971c55ffe6f19f6fc3d7b6970f03470264908629c60fa5c8c895ff1f74b647b147cf7a4ae95a3fff77b7209668e268f523906b9c73710a0176b6f4376adedaf12b06f702ff75c50f7b4908f13f4bce5083755d217dc1b839057d0931af9ab10beab6098f1e91f86e74ec57bc9756972def22e35224a445162bf6a13f51283414318a9b98adb12d368f24bb9b5b4628199016859acf7d7302afab1037cbe81f58740c8cfe52a00b0e7a70c587c0b9cb678ae20bf7c9557edd04dcbf3c4e8efddbe3a95228dbd07dbfe4213cb6c710ebe56c45f166accc5bd7c294894a6477bb932f47a71eb673ab530b20827524041cfeaf89667d12eb8e46bd4b9adc966188cbf17b9b0aa0e36006ff7802af67450995e1b5aa58b7d0915b247de6a0f6538c04ff869703c12106c773b42fd769a956cc6efa877ae0a62aaef7382fab0f90d6b8f4ef2bfeb6d32613ef73094834c8c41b010439f229eea2846eabf604d62ac37b1d4b82674b212e764795ac1fbb0519af688eecba66350582d42092b3724140dcfb3a7c33d07efb1d3acdba9ba9b7b005178c370af2f46ec8770dbcf2467f2be47c67452794a3e41983fcd29b5d25f3098d37f640e2bed86087be8de84b93589136bc478e57725ebda633decbbdf97b61163dc4cc1be5831932ca7593c5cb78fe4b8abf206bd1aac441a0cc04c00721a3f066f694b1d50d278e1c5ecfd1434c87b30529fa5a94dee33afdbb911217b3c67168e7a70956db4eb0f4faa23c143196c7a888644fa018cf4473bbb99d08c564140c9721f2f5e065917fc807242862f0156e2386c081e2ced1695fa47634365d293411c32ac8443a5ef6561d978871a4bb48bffaf08ca8eb17ddf01199a12f11e4f55a7c291ee4f61ea74aff0dd2e2e108b33342961893cbbf67be41e59b849a9bf2a03e76179395afe1c87df4d5ac513147d41de0f6dbcca9b79d8a743aa2425afbc9dbe86a2e17b10dedf06f6af9ee239c951c51724032488381d12fccdbf582a799cb8af88793d83aad79385f5f8da2706e25a9cd298933351911de5668bded360ad6058dce22ec84b7b06e686c16d71455503cda12a1678f878b61566c8bd3f56a29026a9dc0b647a4a440b52bf0506f02d1ca5aa0be39e630bc2b691ebe6d75fd9caad38cc15a8fa81987ed188b6caa6948a582cb38ac843ab917701c9c2391695d95df35cdd3ebe9a3b44fe91f52c04adda91dde33a721eab6c9610b77aa707aad2ed4cf4d7bde536191d8e8c64cfc4c4d6b5e0adcf4f57bb0a977e314f4f4ee3b133a85b85abb7a11235e3e6e765841635d550393b69806b8fc8a58572026c7d6896a5be6512608be3ac40793ef9d7a036c603ca4976e2458810ccdb678a416b73701f722f27236bdbb74240b5a4104a2aaa8be561ddfd054c03efffe1e25899328723b16a28072d2f5c1c4aee7fbde42d82372b3c92902c526cf36f9213dbb7ed61498ce0d18d2cb16e66100ac4d0fd5541fc6f9a6124de4157e12466527b0f3fd83b2d30f6c2d05f17cf24cdc05e3560a0fccdd1cdaf04066e2982e631b62ded1ae688df665801ec9fc03a5a03aa115995a6849cc54d0090477d2292a8be6995f1e8514532accb4f382613088964e17ed5c653b8238a41179df22db212fa692ab87b46d7617cd714af09a8c64b3fe87e23b66ffbd74d5838aafbcc2e4fe1881557595e11113969d45e166627c6265be9eea6aed795903301672dcc3e0eb22dc650843fd5a9027e7e341438e0cc79aa8009adecb0b8eb9461656efc0467f2d0c9c97359c56a13bc5e171bb81f73621cd3719869cd5f8d97470c28403982cc3b10190bf720162dc668ea4073c51df582bf9cad8485088864404ed8a873d2110d496b122457800a0fa10ad03d62ebc86ed2a61d35104d8b84451283d254155069e8229519413d5474369b464f91a1d03807ffb14b0a74e0dfa41025575c2b7f8175c77fdc23a45a759a736ee054ae35742e995977d876946568673ceea05d9ba1ca2cbf8a16a42e49a34f03d71bf05f28a65a96366d128231be8ea79794d3a900256808dc76bd10d5d581d8f99050abfa9da8184be7c7341b80f3f6e82a6f5fb454549669505ab44058b21578592a6200521d1d4d9ffbff182619372be5773b2874a3bd9f109a68eaeea70b882b1d73bd2b36dba55a571b6c1b87c4aabced10bd8d4a6d8de53e2885a0d6deb4ce4c0ea13112ae5b1c385fb9a258a6bad03885a5d013ee8deeee6a0b25b385972af92f767f7cb50c39c98bb50b333705b3d8f33797f0530a51a62f7c64e6918383fdcec284c3e033b8ea8a7c6c4e74e5126baf2487345ce5af2ce960922d99371afbffb0d0d9159e9f30fecf4ff21f10844db6d2d4df00244a0c1c57ede463b7587b773d52109b728a13e2f34763b077a65c17ffbcd07c00f40c6bf0e1e87728b7f11cf257fdfbf4bc40c892d9d7cff24511ca22e23539652994b7199114f0757651e3647cb1eefa6caef0b2897d2373540daa920d5c9339a86006a3ccda333cf8b977acf7a1e3c6938e2b718ce20173a2804db1f8e3cd5524d234ea16b96df1172a5fe10b200440ac36054a45d5c68fdabf4bc76db469cb5682eeb22774a4e513f62e4011a103116d2f7e7db75d93138d85bb99b505313772ab83f02e9db0a7cddc8827d70867c79192b470ea03d06621db36c3e7ef7b04ae52593cd9143dcd2ed636f1572e00e6658afe4d25ffba3b4bacec2bbfad8765f63ed40eb81a0041617b29018896bf076cabbfee3bba3980f3d7ebd4cde51b2dd03a0b76a2b29ad8f8d6cde42dc8fba7747d64971a509e45169fd93cfdd956fe83da7333071e7d5779ed0fd86edcd82588ad6a4c0dbc151e27402561c32834edf4b2d721b59b1022e8c4094187ccb0a53058d9fac626b0a5577b4e8c1e4a977826ca29ebbcc0c263c3e38946f3a69ef88a292e9b1cc407e8165181546788194fbd03006da6d388f04b11a4d912660d4a93d201c3204b4fa8996f09393e2249a375088da1bea76c6a24904e06af44531f989b35d8a5897fc8641287adaa6cd4b00016f9b28737688d5d8e9a252656fbdf2de7d4d039b2f1a339c48341b3622e1ab683c211247cb763ccaed08460f0d70ffd2ff977fafa0e7ef8ba9030639b8241f22669cd2ef717cca460abda22b5f6b538c9807cbd2c72980b233c5dd20981f1043d5c1bafbfd40fa646893cd4f80f56ba04ed08017abc62e56bda80c5cc1f100b8b1b6a57b5a6038e1db755f0aa2448805bb378fa6eb33f5ddc543eb446f38e318665811104f0f1900428dd9e6d4afbff8eb84818338c3c0581fd2a639b44454ec6c49127c216c86287458479dbf3a496bba9a7e013b9cd6a4e3fea40a3107c500f9d78947158d64ee4009022ae6fdc907da47bbb6758c1b2f2a3132443ff32e22563e1b0db5798a4b966d13b9dd13ac45549f6406ba2646c8ca7ef1240ace1c67bf7e41dc629e4fae52d1b9edbc18ed07e72213f8c91ec6054597c5a7159078e012e678fc6fc1b8483804b1ca64ae597775b88bdac45e1111006666f4bf9a7b336303299a296565f12652ea27e04ef74671b8f2d4756bde0cab946a7e9a706887865713e1e47b15254c4cfce0c870230e97bff12751b2c444f3829d0356cbf9552f266205572e45c5a7195a7b4af37f92e9ab21921f585c179c6f93c03437d108bde831f41c149362a9f4af225e42f684784c5e248f192545aaf6e459eaf0758383ba16b1ac6f22c750b4a1c74ffaf48c164b579582b5c08110452c88089c65188689224aaff7304ff4695a547a7636acae46a0175234327fd0c479c6c66f4c48916034b77ae4a3645ca2248e8bac870ab6f149a74a795b779d08fcd7b684946eaba230c842e3e9e7e3443f3e3266008df1800b3d2aadd4c89179af2a273832e68aa0e80ad911964406bd58b12298e96e1bbddfcc1ba45f3e918a0683b3fe4b4a00d2e70377369302e6d8aa5104e27553d9811ff33b1bf4b4bfaa730c9c7fa42106098d37afd31e851183a1544ba68590b29cae09d5c67f7b737061cbf6ddc371c11d6f7ca199f759fa6a8fd29b05122132012deec4037d05ace74c9ee672716418383e20676ff500b2fa965d2d27f26b4f2cbe1f165269fc07b006b32b17e530065be4da34d2c52b03382abfa8afa62d5c14edc3c9cfd5559ed33f29c22b32e0e1de1d2933b96511bdc56dcc9c226bcf9186c183e90b1645e2602d796a07cdf159a7e5608d6b072fcce9a2b88481721a4f347a40b9a9ee309be91777e520e69826bb34d6c28cc19b0d1a58257156b6ddd3e4981ca48b7d71452aa039f6482363cec1ef2ebe0b1d7c032bbf030541eb91692f5215492f0d6d625cf467668ecee7cc8fe33b2488a6cd88fcc801060c95f2a5262130a580cd0d2020a6278dc7b8e4e4d71d9f62941610710255c1fde1627f7c388226deafdcb55d3338e378511f65b8341ccc3a129f81650741da03f91c807664be9de86304f33b2208d097b0f5573bab4006ae11fc841439f9b3f1fdb71827e410087057063cf0b9b735e71e00fcd5690ecfbee0308468eef1e6ab04a2306b4de87b936ae23295f3af1fbfe31dc38531beaf442a905e4ab1ffa54c91b487bd7170852a63f685a662880647accaf29d92c780d8a805e0e3626165a27f532280933f8a9a99f71eb2ca81035027c4da8175b595496a7f24c5dd2de205fcab778c47617e6c9dffb9aafbdcdb2f7c84b265938f5fa146ca11df3a0918c0f759295bca13b405d347daa9f433851429b16d08783bf89712a410ee096dd3c7fe6f782257a65fb31f644a2a56fbd673ab091c5da688b007ab25fbad4690bcdc3794332ef3650340cf31629983f68395f4b13cd82fc3132f18d970280dc7bc4a28995de601c138e2f69f24b5e88cddccdb8e6f7627bd5a478d04ffd9069021c8baf8fcd20be7dc3641cb4e9185b8b2753b8d4f5861c0b1705cdee8136e7f75e20531ce756ff8b766f3798cee01ab9926d5904a58a0181038af907dfe91b6d38636aa05598f0ccbc3d015070c4ec25520dc3e217e494beba0b30839baddcf6f5207175bbe9c182133ccca04dbd9e795d9848cd9eb39efceb20c46e7561535615195b4e3e16741459fecf428a774479aabddcc6f88a8a15d1d248854477c0d0e41c96205908ccc79bcaf5a3b4329b925c7aaf4909e3fcee476a8c0ef145048a712be278347e23552384fd678a24c8a0a51d0be829f406a0279859cb547f6eda6e4d13df604683cf488b01144e93ebdfa4e6e00cae13d3484d79e542009c9b9f09348d4bc39619e900d2bfbce9e1a51698068fc5b085ee488411ca2dce1bbd6b7e1b16d8463a317ebc81893adec91385fde9b5ec896642c91417a3c5238c40347ccd38a634fb31fcddbdc94e0b847df3422b530fe008f9371077c5847086c776f52497b3af5ec3fc6662700b46bcb0ce52e17d837ffccac6753c3ae2ad4ba9a52aa5e39fd185bd46019a4959a33157708001c1f141377ba9e4fee79c73dee56575a464cb198622a4c347e42a8d892aee15125bab9dcbc75f14ae43e9db5cfb24708af1d3a23c95648b9b5e2ecd5b414fa2ba2afd44b79cd5926e3e902067922a8bd11120eb2a9d59f2285d82c3c2a0a19acc59d5ee148985ba97fe5ebd23204b08008c5033d2f1656fb39d70a83de7a011bef69287dc4c97d89537f4b3fa11421728560e0846daf7081018e4e9fef1d89960437262d82edc9db24de154bcc5c04eb60186eed24447b2b82652ec04481b4b51a4030388d0470da3f72d8e922875dd0635679ff8b46c3e5fc179a4036b6747cc57be3702ce874fc7097d6381c0d3c819dbd20f554a717f85e07d079b02857ffbb5072ff1ef842138453ad9e3b70c8d70f7c8e11037a8f36cddd5637c9ea3ac0ecfe46846658e50625c7aee153b5c1ff4fd7fb3c6844ed748982ad615359828f552b205a0722ee9b3f55730149d9cbd329e9b8e7f7f9713db0eca5ed458cd51316292dd9e38530faa23b4e7f25ff233dfdf11769de561ad32e9c6ae15e7e607d3cea138bd521882e13d775836ace2732ab41a89bc8e42947a459ab00f94a59a99bbed3314d860d751926de91dd0a623d97264537b21db74b09ceea872857a03db5fc347ccfd0e75e1422c05bc793fe66f51e71fdde0477e317e570f2167bb5a4dec2cb6c3a1da1c824063bc44f6cc02eb7c36fb9ed441d8377c6673bb305f8aa225169750162d7c2ffc8b73b9516eda5f51418c77c0e3b623f5b22754afb17fec3ee3b97b54d04c1450e1c6d63e1ff8e1f47720ccf823a99147dceefda35420ab56b446b340c3f802fc7b86930b447d8f5b2fe7eb377aca90f952a7de9fcdf98ba076c117be4b7f3391b6a2c33952ee06aaeb38b933f3bd15d5fe2612671647175558ca8a32f1cf000234d136126b186ba91e85b54fb34311d65d50adce5a3753e4da1bd0411fca2eb3299e2f20521e77d350d53dfbcbf291ac9bbe7583f109052ad95f2d542935006f23c71aeba18af602fbf2197235e0c6d40f50531ab4bfba6198e8d5479f5abb924beee2e2f4a5175f6cecb9fa06b3ff8b7c45259c5b7b356f28032401d850cfd74fd4e1c36982c9e7f46ed32ebb688c9ef9ca90bd943459db57e1e4da9f22acc9d19ff99a66d35ec3e114645f42dac20976c8aba8001a97b2a38579d60bab2076ff99123d7a7fc9c9f93c7443777929ee048f9442fe909052345fd4daadb11123e400782d973a5c9a05945c9fef367e821cebd533c09a5e9836627e27cd5e6b00205306a1a238093ec3bab185889f45a6d6681174b3df3d2af25d30444fa32a71e0457fb0154970405b8e04747348dd19f5206b4ffc2efef11559310098069454a16a42ba7bd40415eb5c109c336895da73000a8adb87617c3f788a24c2e80d6806cde889698102c1cf746c18828ee3ce541d21f01193176bb21805c3b8aeae689f37ff6ed077e54200d32630707abf786ed5c922bce899818129c67d811cbd3ca94ba3204b14ccc104a86f2f65bc117d1930cd1a55c3bae793c1be34c4a4c652d41379471e995c84f4d26974ed253667cedb5203f170891657036bc59226588011466feb12ec001425ef67e780624bd5d83dfffa4c88d47b1f6994595965140dec8dc1d5854f72e1323733b4b03f4649076170485c740e04ab6f8163398d996588f41da45def07dc76d19a097fcb90b9f55d52eaae8b5d48b8a498a4bec61bd23fe5f8eb792cbb394142a4a3e00eb3a208f3219c5a104044cb1cfc7a3d936e447e9527e464bb33443e8ef5492e127e464ce68a6cf649bff3f036ed094a165f89d2f0fac94da82cf1706846b28648f73bcd5ec5e5a879d6b07b6492c4de0844c6920ae4a2d046594505abf569b68aed43b9614ea7d1e12bce14f54a4a9fa5ba1f94f30084c0e0c838ed64705676f2f21ead568e75b2cf768e770d4644b47033875877e328510b9b233dc8e06eeddd82bbdcc876a6e160f98ebb130f31515ac416634e0ceb0e9b19f3cc70b3f7964d1d84ba761159e8eae464f34f86694c092016e26d16c7c96eb8a5239456325aa09ffa354353a4a31afd56fca2597326526d3bfbfe314ab550ab21e11854cb710f79c5fd97c1ac310c6574b08bca0d38235a49d34032beede9d3c6599bcf48d4c6d12f84d817fce9c8aeb6c32ea305d225e7ababc86ce1f00302e2ec45c70f0e5d6ba1c16673335d807982bffd6a7ca38b93e6a8574f134ef65487506b71c8c883f959954020158607330290d5931f392b10d87fc29edf19a57ebb87328a106379ab84c5db22db10027c3d94ed8173c94457e86dc9f3cf5950c9cd31729ba8bd029b8e9643271d013ba62ddb095e8ea80986ea84b9e3cc0ceb8a2071493038afac698511050ea2a1d04d61de0e6c8eb9be827da665a8d4a1ddaa2ee5bb7dff86b7e2fac4c24f888e00b34d693d657afefb28e9ca0adf4047f71e431ea39f856ca21f294474bc3bcf7ede5277b6fb2a59452ca94648d093e09980946ad774e54de789b774e54cca081d529e65f16cbebe98b2965bee6dd1752d8d0a94227fd01043052938c5a63247750e195b8834a93665031262ff56ab2a2c2774e5f8c89aaf39dd31750734aad319237a89449a96a4df34aa5799253d4eb9d9313d45ff72493aa7ce7e424f550874abdee89080e37ef9cbc08f3d83b273135f8eb03157380511a04c08caa05270a15c8084019d5b99b449412513a825a93a815090ecead4bec9cd5803bad3142c7a23b1783c047e28a201331ef38571ddb6ae963226cc7f90ebbfb9d17f8d9f6f3f737bbd39c2bf280a24b774272738e8788c621f4351f0169fc79b703f1cf91bc3ec29e3b1ad7d3ee34b6d08cca7f699e46020f3a089d7351628c31bef75efb0076379296514668879e9321b7ac5b303aed16acdd823c608570747bd163348e5d8ff67ec17d1164265118c5de63c26c96a8ed906dbaa87ddb9d06c34be29e2e8ac6658fd26feccb46bfecae5b94bd5db7200fa1db75cd622b6147210d0c2073846940b560d7f8a0c584a75884e7b2c20d3a849a9017043a742cc68804be1c4e62b0476f28ad8b7000672ac7a7514013e2ce747b10b6430fb21b5c301b2c5862ea58234295196a0328f452653e9fa459f9f4763d349f73ce19dbf544cf36e87aa263aa2b3a8db1098d324e39678c32be37ae5f72bb475e6299765d9abdb10ad1c0476ffff12e7a515ae98343690b749406218067d3c172618b0a1dd25a6b0b27aaf23848a10b31982207182730e93c515178a60baf78e890c94aa3881426b6e01fd34b23007cb4b0a9e6cc8557fcad4832104211480f5561946ab45c3192ec03bb3cfae57aa8dc8b630193180b139b125e49b2ec4d10e910cecbeea65830d7028d934fc64d3a433bd40f996510cb884270d2c5ee860eba18a38b31c6182384537a11e3a0994604daf350000f517858731c10111cd743023509ba22ab449a3c20560a2938a7a2532b6048ac1e9ad5201b2c33b002833e092ca5c4e68c524a29a58c475e7af4979342b913c4a56512ffd9a8025303369019b51dda019ca9ce6f470812f8c4844142cec9a5cec90fe10b451f65943228a31b46d1d2e68b301c175813a4cc92a0d680cc70c71808e5647668444641847034b1a0a3ae8beea9b304748194735a87f34533233a48bbabd0666b060d4918e078ebb67c7c826a6c6e5496c803c2b1372c968dadb197f5339d2fbf362a69e4430b91a2126a478e31720bf3baa8472afd4d0897a206a4fd551cd60caac8aa9043703142689deb89914a274d20918700e072e687daf99d26c4e65b02dfb395706b7b086bb01f497c961cd6e4544dee6a72b489001f5d134e4e0e609412fa4879a1c308f235d103d8451186e8020a6903e6496a21258f0c4966478836314802d349591815315e50ce19393ee4f7be28e120936241e52bc148262d241709864766856462a5272d9229937a493565f884fc6469625ab6726ca565b2e7cb300c661bbb9e20f1a516c37b9515e252cd222884c6b80623e94c519fbbd0c20a58a61119924e4430f714a37bcaf185a4a19231866430e8e4a562659599a184526e97cef6f2bec0e8364220edae078e8c83f489f87aca29e38c51ca29cfc0cab185c3eaeee830ca68a5cdaaeb111de27c91ac6ef9b051b58f16ba1e08a3732de8ac01990ea775cddc145da06a6a814fddc109e4637a08e1cb1ee8e01335e7610bbe2d0fcb4327541066d03b8e067a4d3d651f6caa10bea6ec3561d78b4e7d7c7eea6f9dcaf1d2bea31bd427e5313de4d2c50a7d4c524a5ef17130c69e31f6b774b82911e2a3a3d11f90db6103e8206d661f2dfd1e77907666de62cae0084d545249afda3fb05b549b5a90482945624514380fbb21f77629b3832be77a92e0fc78d13a9c2f1a1907298e029818a306c48150f2c945f722153e98aa5ed08cea7f7d7c58ef208d2970fc1d21d0a3478b64474874e9dd62cbe3c541a111cd74a2a810467dbf839aa185686d2af590a1a0f3d3036a249543db5e780a8c467ab49158482c2fff845496a19e581c45b3ca5310e98b239e8246b3e3160e0a8aeb90e0a0b270693824489b26a8591d74ab3757db9325b9a4f228546869c992a1fa4af8cc673ec47acc970cd1d7fc5d1a463b6a41e2c3e82a955320ec8d1fdbb1101ddb187e9c9a90166252a6d6bd9c33caf7d0c217d5a0b4d6a1a7a38ae92838053c8fe223948243c0731d6e0151685a9643c0731b2d5834ab43efcc9933672093d7501256773d046ab1b8a0347f5bd2562898b2c480acac32b3f4081b42a60ea1970a21852e9d3dce08dd7d8c10460825ad382cce843833a830830a9839945249dd08263275895c2298a9838dc8c45d80e92232a9a488401f92c42203815754c2b3bb5b08ea7e21da9b844af7d43d6db1f9177f8af9db46ff42fc368241659b7f0e99898bca8e45760d99b5f8e79299fe45f89066520e924e0bea5591a16ab242e5858a0c9fca45d5e47ce01445d5948334663a8d764553357f3c98eaaad231aa63a9527f4fdbb2cd399ea78a7ec1230ffda1cb28e7b4497c84e4506106151fa5087c1e5a9fab6195f7da99a5f7a6448867774ab856164b7ab477087ef41a371616353ed6b514ddd1e893c619639068337b51515bb12d04d23852da21f80f3873e6cc47f8107e0ba09c9bc14347c643a783aff177404d1ecf7318f4d007fa43ed87eb219d951ecf8bf2c239567a4035853a15e394c64a95c5baee137459e99fb4445ccf7395d375647cd4c546962ba21e424928949978b86ebda2bfbc658c97cf5b7b217eb4d5f53cb7f9b284238416baa74c8d2c98a0eee961b7986016d8aa2a3ce4f110e76192d6bf40151414634d1a75098454b684e818a346160fa1dd092282d623a9dd6218c6f98a5457e9428a1750312515a60a1c8081042a566ae025e98c55921a09e82d4c9cd172230a23fca08c2962d4c0053b44a12507680ca1c61a467084d9a96246819424798c1c8161841760dc800830538411050cb8b8224a119ca088f1061532e034a4d2987a6bcc39671b46cde38ebe0d24e88e68c291f2240c2a86d6a5075924bd37e6498c2af28c324a6850a1452990a4144ff41435f0a2075c4ca14284182820c24dad80094d65aa8c81f2e5484a8b11d660411296a04286084544b821e54c10b4306306644c99c1d359c4ac622095e30515a9a7f4f2781c7d5c628cb10653d745678c31469af8656ace396da014e345c59873ce39a70dced0a8e69c2f905fcc3967d3e3715a645f48d51773e280d9e2c916522810a818b105181fb8c08c151ad83895f9d94fb8818413658c31c68cd06c297ece99719164a8d0b06204217c618224369218ea82040e90b4485931c38da3143a0a9e330d2d524a29796074f1e0a8aab834c109397124a59466782a534293128c945443d861c5cdeb62ced9c511549f04262967a7204a3e050cb5204af8ccf428261679f9e9da048a56600d421c60ae8b17a66e2d5bb0748bbd00f6f9f9a1cc7cfaa7fd3d23e7ec4d22b7cbca6a37f4d7fc2ea95bdccd80a9cb513cf4151f6590a5ccc7c8d1b12c835986e1e0f05801a89f34cf2a5bcd4805118ec71d61a58927a86815e0262bf2c439f6d146ef9a8072c417ef9c4603b57d74f478f8e8b28cf4785010c34857d889a9234b75ff6c96cb2ffd2b545e8f997d54ac599ee2299f6a692f3cdb5a7a502f1b3d3df50f990662369a277ac4c9a26350c9c8c8e8892d8ec775a129f852531086e503022918e358707fdd3e3f3d208e7a3cd1a39e807054b7a25334eae5275aa65e8662233eea960e0e6a8c43bacf09378e436e6dd42d8e7a622616a24ffed7076a031c70401089fbc9dc081c50753cd2a31f611fec58901efd8c5bc2333232324a12a2f28e088ec725c141a009381c8fd3803c9dacb522dd8afedccf7357668d2b483f70eb2ddd8aae098ea7e5d19da7a0f3ce691c501be178a24fc1e17a2203ea65a5a7feb9eee9a3c7d84c1f9d530ab0f0961fd243915191970914ad208914c1416e35b06a3714095df082531c47f3445fe9a05e07a5141563c4b2e59906a93a216ad9284ef5195352ec471cd00fc7c31ed48aa2ee870372c70374c4f1f0d4e3891e59530fc32bb3c695a46fdb5ac8d4eba0aecfd47392d2b31f79d17574ce46fbeb005821a19cf2bd77d18cba9b336b18e564a6fe712a9f1a104cb572156e4a84f8cc9df642c64c467cf480aa738db33396fed132d9881e55244ea25155ea39899d68566226eec25e6ab6ea7856fee3017113eb8c4a9d18a5ba72e6a16e7445828f3c2b775a909516334c95498dce0ba7b696c6d2ad6e257a04a482d222a3070521148482500eea4de7a6a6fef1acc462f9115fa500a3fde39cb3f429a5c3fa80daa7add1395a6a7b2b824274f712ce0e283adb2a84e691bcdea8bb1ebebaa8ed197c08fdf98e9c9b686dde0984387ae8cc38d1dba57bed0f493becd6ab943ebf59e6204dc253df59309bc7d826c9de1bf0d8eb1de77a2e0ff9fc7688681f7f87c8f5bcf178b645db12f843f276f7b379a7bdc3dea95652d44bd91e51ed11f5b2986374176fac2e55858c66bc6d358fa932dee292f843317b2fcb9addbc32de545854e798aad657662708b3dd09c2354906b3e7aac7b422d0c8283ea63d4c2be21c9665bc5d18e4c2a2c73a49c6db12f8495830e238fa5cd47d2d614e854ba13bbffd0e5ab60f52a367d5201f4a6b6d5177474794562029bb35d2e37167b1eced240d4874ec4e25cda9466737fa68a7966e3d9f4adbc402a686a165d43b95e6f389a57fb4f48fe3c7ec777adca1c1095deec43509fd40fd87cb6b8af08eb4442eeba5991ecf6c2dfc140715dadb49ece34535d203a2fe1e11546f8d7fcf5f14fb885ef811c1f5885b501bfc9d4a6b4825a50724ab703c2a2dccc2f33763e0c4925cfc9b4a3753e99fc72630fe2d817fe5138d828e51a317f621c9702c3c7f1eb7703d6419aee7f97b627951a512a52c07a5124ba57f3109ae0710be090e2889e391621f3a381694e07809ee273a139e9fbb369efdded0312a3b77f7c55f7e72c4f5ecd042542a5b03730fa8014f9a9c0f1b60d1a4030afa781c8d192e28410547459c34e04993d0b345fc8e7560a997fe0e7b3301875494503fa1210425a11db63c4742ce0c15f74df5d2bf9cc43c3df760729b3c743dec9008226adc18a732a0843033849c57f6d13854aa734729729429008e132afbe3f9eb9c73eeb1a4686539436c51a410a99c53d278c10c05a50adfb36358a6d344ed67c7b24ca789aa65aba6ca8e8252a57f296dd769a2ba67c71cbd00c0ca42c7cc6adcc841332c4ace95a9aeb86942c3ab0beaf0bd77e354d99f73ce51d6dc6b5e317995193cf5fc7dd9ab8e31f5390f7916095b56b7baaf1874f6dd8f5f90ac7fd8d26e9581b1c2d4aecdaee307b5fdf293a6a18558ccf3e3a937f5fd748049bdf77610c77b2c2cf0a903ba5cb5b5dc831e4ffbb0e391d74ba28f7e7d94f2bd27a2175e7811043e47f8ccfe7c6a41e0f3cc22845a10e885733cb0c96b41e843784536dd016596951e4fbb92d2d6669a87c532622821541edd622d55a3de3e6ae0a13f08a158c13c0521538c314230508ac70bd8bb35281f955a4402e72443db56970acba664d9ab2c3434ab0c535d92be2983f95c7bd6b91e2e5a6a03c3685a5881a7a86dfb757775ce654a2cd02953db56b7b173aed3820a75aaa8cebe511b32b99eb62a3a6554a956b7e8bb3518daa91b15e3b713e47d7b5b209e2d753dce332030463927a5d7b5021055cb615896d16898a32ba901a1e941bd8e4cd310f48ae39c06aa3a0ae46a7eab5bf453035271682e9d346adb9ba9b08c46732b9eab5bf4180dd58040d677337ba41a0d2d84b98b5ef39211beab2fd6560fbfed65f26c75a25ed1d3e8fc18628cf826f2e3f1b40e5c8f1cef9a055e8403a2340549204169ad2c160e1c0fe604f920400b2eafc2324dcb3055abe5e3c38202469c2c0ee8ba6c1fb116ccc5a8fee81ca378f3eefab47828657b9bcb5f2a2ccb6a65b1dc5bad151669f30fbaf3ea580e48e5cedd015d6f7b7f3d0ae753558c7073416a41416f8b0fb716a6e6e3031f6e2b682d2e2afbcc8e2e8d0627d4468fa77df5c535d697cb5dabb7f5d3ac26ad691474151616ae4765197cb3920e56723c5038200a446d3f699e1ff6cb357876622e380bae0293425557eae5c4b737966f6fbf2dad8dba55d3c6a5a269f9f64e471616bc0af34bf3b78d30158bd58415a1c23267a4e28265591a4ea908d703879639d763624fb3d02baa266db49ae73f7aa5aa3546ec80dac8f118e1802eefcb52bf8d82cb6f57f956fa71f3455dd168d90a5ba9aed56ab5a2abd594abb882afd9002be77aaab4895ea3018950b277e0c05ed163308333b4d0ca3149c7a8343be27a4467dfa17ffa673aaf9cedd373ce39962ae3a87cfb754078e73a381f2c85596877be2304224102fdb518d7837a2fe15fc24f8b1894225d1ff6d157feb2946fc7565bf765a4bec23e3a0cb3d0ded4af30ed5596ea4eb38185aa752284104d52bb97efcc5e96966fa8f4edaa7e5dc43ca66eb55fdbdbf2b4b89ef60ec33ede148e85f6aec2f5b4d30ef3ed3bc454f6fbb4b0188d0245df424e8301463bfbf2654565cf9a66e127372f087590babb75c8e09a32c207d9e8fbaf0a5cfe065d7f5d182be22f7d519736ae2c712404252e5f08c1d18511ac606982811742d73d7d472d81053ae881540ba0baf41288b88114229ea0050ea1eb5600e6db3d8a6fff31c5b753152cbe9d76f61ef1264cf852136f626f0d4ce0e083259a3e04e13858225a83bd38b80e49350861ef1126f636711f9298f8920f41d83b02d721c99770b057045e43928552832fe1e047ec65e242f852137b7df020887af06a896894da240a519426be44a4c42b09a2e8e04b479a381b1179912fb182021b821e7c499556680ff62671952fb14475882f69c0ed6522ac2c1952a23a2f61912871eda54189217ec2455425aa2ff5c043f89012be34e443f6d2e054890ccca10c5822ca43f8120df60eb14494087b33e04396285b62ef90574b942df1a5ea3c84bd489c8628be4483d360eff5215168f0a5214b9c89f0a5217babf3103d24b1570319f02595bd54e54b74a887215feac13360afca12d510f892ca8a067c290343ce4f7a702ec293f845e58ae5c15e0c5091e1fac15ee9900a0481cb6089aa0c2e83bd18701515197c8987cbde1f9c52f12569efb5daa050e724ec074ae04b1eb07706282b7762af04c25e0885fa92aa043e0394197c05e502be04c45c01618968097c89fee02b4b942d5df6ae9c5aa26c897a09ec9dc18a7fc09766f019ec95c1ca0cbe24c30ff65e5e025f5ad94bfd07f70008ece5c11251207c894a5f415759a26ce54b2a07c25e0c58a20a025fc28063c0de0b58a28a015fba80dbbb7227be0484f3e0714a3c81a531444b94dd606f9c223b60ef0e9e83c710832f7580a7f852b437ce8d684a5f82ce489603f61aa9b2017b895491556615e84bf206af42c4ab18f1a50dcc0a6cc012d11b7c89dab0142195e84bf0067b8970c0978838117b63a042c49762b0c1dee83640bfc137e04bd212cdcf72e44215e88025a23bf8122de215b044d9d2b4b7022e2d51367d49fa0ef6e66089ea77c09772f01cec356289ea67e129164a0ebe64c49f4471a1a522f64e2fe24b15b0577a057c07fff1f80917c144f0106c542444104e8028c10f4d361d27da58ce64d3e13e6c3b3eb4e9780f1bcb976c3abc6e3bbeda745cb5b1fcda7438dd767c6e3a2e3796c74d87c36dc7956c384e822dc747b0e570116c3b3c041b8e27d9721cc996c3efb6c341b0e1380f5b8e7f60cbe11ed8767807361cdf61cb710e6c397c03db0ed761cbc18f6c465c035b0e9e81cd886360cb01007e81cd8875dd4ac12db0a91e878d4675ebf90d9b44028c5410de86ed31318418237c05b65983a31d60f14536a9832168d0c6d7b0b96ea1e0344cf119aa3811a6e243b62c3c658acbb065e12ad6490bf1148f61cbc254ac9327c453ec131ad50b78ce526c141772bd00149a9e02db9d5dfeb990edce36fef904b64bbbfcf320db956cfc73096c776ef9e7306c77b2f1cf5fd82eddf2cf816c579af9e711d820b05dd9c63f17daae9cfae7bf5d19c73fffb1dd89e59f3f60bb53cb3f77c076a7977fde80edce2fffdc85ed4e33ffdcc7767b6c774efd73066c77c6f1cf83b64bb1fc73a0ed522dfffc67bbd4cb3f5fc076e9977fdec27625997fae80edca33fe7902b62bcbfc73046c773ef9e73edb9d47ff9c85ed4e29fffc00db9d57fe79cf7667d23f5f61bbd3897f6e80edce30ffbc00db9d62fe39cf76e7987fdedaee24f3cf09b0dd79c63f1fc07667997f2e80edd227ff5c85edd2a37f1e80ed5229ffdc6e975ef9e7db7669d23ff7ed5227fe790a1b00b64bdbf8e7286c97b2f1cf796cee77362a66c746cf60e9e428f3cf73a0fe39ce664609f5e998cd0604867eea98cd769dcc201eba837e87fe36f9e8f0e37be8377b3f37244a9abce6eee316a992a984cfbcaa66abd9a2d39afac7893983463d2027f4c8fca366fe5d3ae6dfd3bf3254a88ddefd954d609c04c3b59ecb19fcf369837f4ec9f8e79719ff7ca5837f5e251aff569b7b4d36b91e1d4b58f945a7ea1d1870a4e0a16c3262937cb4b109363e22ea9f331252ea1f3f23b1412323213e627334684a03c948884db0b5101f6d4e888dd8e4142521a70121229b1e8f14ab8b4e0c8a66a5986e3d19a65b2fcae684188a954ddd7a414d11aab61d5154d9345971f52e8c21869093a38889e8215f24845b65754b95542fcdbb1ef2877b1babbe76efc27c10d0490963dc92649fc5fe78157e65fdf8e1ee7ab00701732a503315622cffd1d573dc69406a1cf3d64b5695b2e6b84e55c22f574ba8fed745e560ee1e9daccada70d4a8978f5ad1090a2f2f9d0543ab0083daca5142c5f1da3f361e3d49ffd478744c4727c75db7745c6e588e4e8eebe8788ea5ddd2b15787d76e6943a465396b435640a8eeab3bd763878701a4c6716cc0ae5735363540aaebd9e14f0ba2ed702c6672a3711d873aaee3d5f570afa3a3a3e34bd5910e5d8f1b9714d3f19c0d89741dc724cb5f0e2bc7ee08c9f1ea30e4ecb057874d82f32c7ba533e1cff11c5e75b69c1c5bcdb12c9c8d9d7bab1b9b9aadd26c3bf1b56d475a22bd430ae7b077611091f413ab0f080a45a5ea3c3c20773c91c69a77609c79e3a533520ec698c20ce1cb07e05d98a8195cf5b2c9a22279ce7e9364cf2251d12443af7295843dd45e4a915097d431b6d93f66161cb3708158e25ccff377fde5d39fbfcda7631fcfc669b21d1a9afae4211571e2db044a12784037aee39d13145776e81fe77434e6db79a0a919675996b18fe7ec927d5067a73f35f39b7dad0efb67e5b57add2816b57ad36ed178b6539dbafbb7ed54fb0256eb7b31d40a352036d9cecaf9576eb3edacdcbd8d636e65b3e10c516bac4fb71ccec66aa15bad1aa26a7e7d5e90550b41396dd4e7da9095a5cfdd5bd59b2454cdde7ab91ef6b7d1cd69d90bd3a9ca7f3cd576f9cdf6446b9a62c463db556dee69cc549ffee9b9131d3e7efcd379ce9d202ced4eb43e1fd4c214d57d1ccff37996c60dcc35507c02ea2cd19d3f1c1e0c9ba7fd6d3bec910636500bb9a7bd80bd733dbc13e4a14502037f34723d2fa0f00f32b91e2830b91e7621baa89bd768fa66776bdee978e7375a56f8769a28d7c3c7b31c4ffbb3d7b7407b5b5dbe1d4f7704bad541dd6a2728e2f804bc738242ccc3fe617e07a68a26bc03b3e549f4ebe563dcf4ec3a2da8ec42b774aaa84be0dfa0964776e8d68a09d563dc8ef0b043b71a8b0b2a9c396d54e8cf7750bfd19b6c24d88670e027c1d010d7a30161c93ea496c3842237c6770c9059608756320bec3ffa085b1eb0300bad24371e8e78a97287a17fbeddecd92316b72c971f7940979550471e0facb4638aea6f8d9ccd3b309ea0c6b335ea967b21ec39c01b24d466867647da2141ec4111b296cbc2f24475d12e899f6427cbe592e9554d7f59e8eaea0d9f56cb9dc57a8e2d03ea2b930393737242cb3f77e185496a29901b83db127eb8c1a70161c870731a0caa2f13e53bf73110fd64527db0cd72d41252b970e4422a77ee00950f89f14743d686047db459a06a7bc24e5bcce059c341652d7d3c8d4403d2de3f12c935e442b22384e8a71769f1443f3d3e2d488c2ff0c7191df5d0639492d237a0cf6ce2e0541654a534d3c283e551aceb253d74ccbd513ba395ddebb7ba48a02aa36f540ccbe81b15cbb29a699af55546dfa818a55181a6a5028d06038fc76aada9b179ef49f9e493af657353536f6856373737373a9248a66fe7614483d1383e6229fdb4c02d6fd4e8654b649a349499991976ebd26a76f5a652358631166fb8545ddebda7f2d29b8ae96295cbb1ba45afc38149040c3ba3c99479e7ef8c2663a6a1bcb174616a25a9f7de7bef3d18e5a490d2d74ce5a417bd545806b3ec3567adc2326d0557abd7bc6a97692b1a9fcc6d2b2cab2def9c46cb3bc768b0bcf35a33b7ddeef2ce6b986ca08dcd6bb669a56e399f6a238ea936fa491b49890933464c18324f4d57d8094ee22b8c8591da4cb3e1ccb42175a4856ec185165c189fee0cc2aca38452be66c9fd1e8431ca09e77ccd134629e7a4f4524195ea35ab26bd2e950ac3320d6ada6bd654589669da6a455361adafb96a59370d4dd6b5665d5393b5cd0dbcb979cd3736997338396ebb3047e6745890c57acd2cb62eebed06de64bde1e064ed391a0c39596f393418f8ab0ed4d179cd3a74b513e4fa76feb6d7c93f9b43573b41680fcfb69fbae5fc0043c0e81895a505e1bdf79e7d999886efbdf7ded6ef3922fca35bce3515b1fe74d4412ccfffa5cbcb18a4a51e2dec16fc72a55daf7874ab1f8503732a60aefbc1eeee39bb7bce0efa37a7864665b5faa7f91b4785fe71dfdd2eae6e115dc2cb5e8c2dbbe1a6045e21206c0821430861a651e057625c8f73965f60fc0e628f9aa9cf7b01ae090aeb9e2d46842a43536a87dd97bc41df3e93c770ba82899b774e5744a1585c3dbb79748bb3b8d932c3a0746670cc22e820f5ec6e874c4ddfdddd0b00a3740412ba40011c50bc21841e1c818b0bd620e38b34ce685250e82e1418a332298132dde055ef9c98c4b0e40c9e7450e52f6542e5f1ce0909256051e62f7d1c549d774e48806105124c4f2aeb9d13124945549c774e483c995273de391d710449e60834989ae03ba72a5138d11f325efd3aba4e954ab38a13e4d3821542f85845b5316332559a30a411ea15832eaa2a8b38860b97bfae2982a9f39d13172d7f29172529af085fbba61f11900005a69e9c68a2a18e87817c88f4cf7427553714e749d7a9526986d3d64c15a8246f60e5897146a43e63a234ad34ab50d0873671bb4fb782daafcfb7d036688ca9c7e380509f0744a3e8f2dd8ee3ae8753c1bdd018621e8f7beab8a69d3e7db581206085d49caa4d85e6a9990e159f16825ce81f9693156368656def9ca4a8f1f19d9315b40933a21149a83b4248bf3ced1f0643a63ebdf41b2df53213b57179e3b2baf4367a40470f08e9d14cb91e3e5eb682a424ad59e374009e6e05f8a007c4a37924859665b345bbcd6d6569a4d7ad6663a3cd66bbd9260f41d06beb5b2fe801d5b8947eb3d58d323153eb9fbdfcf4d2bac7c3c3e3914c2bcb4c8cb4ed0479ff1c87a7968fba95c383ea849c5074e8d299c9f5c05e3a77e1e101516a250e3934d0caa5634f0322a579a495d22df9b6ab52aaefa85bd2f57c512f331d354525914c2b7f0e5831b1171e5c0f9ea7a23b2afb42bfea72367892619eb84cff38272c5d5ebafb7259cc65323afef2192f5fd44b575d9407795dd4cb2c5e3ae6a8c3009f5ef4b2ccd4a55bd22b967a99e9bd70174a77667da2ad4501f52f8a3b28eefc3d87107309a16bd335c724ec9b24c94fe7797190449f1f5b8b1022d19ca5bce6d06910697dba3565e32ace5436b6e21874a13a272c65debd8f8fca5b3e5354e9b7f52b48eb6385a9384fa18e5120542984c6a97394cd094781b2396128ce541cbbacd3802c71a701a13eb8d38034610a032a9da0d4a767469cdf090b1bbf72238ebd8c6e40563692993285a96c3b45980a13d6893ca2e24542d8acb97cc7c8ca651b48d786d3ad1d23f6857e2397cb31cfa5197786d47329062aaa7f74fc73c946ff38a728b6fc9491d52591fae7caa42bfdd3585cd7e530f05f32e901c5a9a91b2d3dff5c8a79408cf48098097f2ecf78409c8499fe19f2e73947ae878f7f7e0da126e172e83262c49f3f20272cc449d89d953b214ec2579cc4b6630af5219e03cdbb30861863c42fafc131aa01a9e146a340510c457ce53fba868d5f6a42566ec4ee0859b91137629114b1914cb71e83da9019f56ecab65384a73827e19c847582b921d524c453bc4848871bd5ffca242243eca53db80cf6d618ec65e5602f0be71d187164f91eb02c3ec52467269c351b58096724249c9938c21989894d1e3d9ee7183f6676717573f838d52d1a841a0453714e52c26f8c7a1ea31e503ce3c5a87846b76a0bea954ddf3f373efdbbb6f8d4ad179da92851a58a3b214eda766c602bce539ca7d820188a10c6194fa1514aafe35d9828a61ff39aabb07ca91957d9788ab3958da93c8c64ba45e3d3f348463651a3aa427cf21b9f30199d7cabcb7f747cea1f1ffc792463244cb736e944b79efbb0edd06da75a99f42b2b9586a84672945087ec954dd0f550eb561953a19b7be93f7ac97631d6706877c247ce56ac13a6e2ec4ed808e72ad6094ff1296ec427f020988ab7476d0627c4277038433b5371aeb2b1b395ad9da728e981ffa2d8d30c5188aa51af4c4a22f2cb7a26447eeb337122bff499103993a607c4555c8fcb9f634f1819213c0d3e83632fbb56ae66b5b25e045b71a7d9d0426cc53a114288abf88ffee17ac4c431dd8a425cc5beaa8bda7f65936c723db09f36eb96ca31cd55b6d52de9d087a8de08dfb181a33cad081ff98e0ded5cc5b98a0d82a178bb13faf178cae684988ae644dbbd68d82ecf0cdbe5792632e99ff7b0ddfa579e6073c2426c026ff54f8d3f5fc147f3e83c361e0eda1a973eb7a17e1e5ee3ad09b1716849a0f9e67e324df121fe7c4a557119fc3965a2e231f87309d584d212132a09aa84e43bfcb96c0ac29fcb3042f87329a6c89fcb3146fe5cfae0cfa5140efe7cf21048d59fcfa61ffcf97c32ea9f7b833fa748d01f0d4e81195c8813f109b86b07a0e034aa1d00fd79c4f12028701c97c015c771184ee038fe42139ec38124798e47a00687c01117d2c17f09cfe13f50e039fe800cb80334e00d48c273b80b573cc77db8bb80f7c0803300c9737850163e81e7389011ffc9c117c0c45b20720534f10430118e002ec27df889b3b0c40fd083f70cf90a25700300e10570e23c97b7544e80950fc0061740055c85221e001d1eddb25cfae6ae7fdc31a759c0dea2b780fbb0114da28d68d68d68f6b0114dba114dd54634e14634e5463499444952622f0e514860af0e51aa0f4144e9214ad5c1077b8588c2a409133e3837614760ef114b4471f02511d85b03134d8c8aec85ee8325ca7cf02522c7219f69a8c42cf684558c3286664000010000003315002028140c88c442917040cf6248fb0114800b7c9c4c724e1c0964490ee32886619031c600438031041842883134366503436df8d0d8ef2eef85b3fe55d982b8ff599699b823027d486fbbdb25253af70b13bb3d5bc70d5f748e5e2fb66dbd4cab1198204a7c3b74c60d2e76ea553b9c168e3c0cc20d3f7152a1a4384553a7fc0deb6a00ddd548cc32e6cd4f0fc18b17523296086396d5d66fff4395b711da4daeec72b46003c026e0a6d3e15984abc5e863602f57c6b32b349a60876a4c6ed9e96a0d38b84233fe7c0730bb25c0b7b3e0b5c8d80635a2037fda2d02d9b6d9b2b65130a5d0f1b04b74476024ba628c7749f77a9dd580dbb1489ef9d185961db4f99cff3ba4c4ce7d73a4d14f1ebbc9eee5086c7f76d25ffffb69df51746d430405f166abe66d3f9c8589106d72941c751baa8562fc5c2199950f6f290235a3c28dc931d317840760ef7b89cfdda6f91d23052afbadb323cfa2cd83216319dfc259b8b79766d9886c86061eb1fa08002b3e49b415278a68a18d42f4cc2286d94e1b689f6cf35c2b7050b922014e6557df1959313bae5c836a6e9b25092bb253f353590ac9e5bc8bd816c62e13a21c1176abd68c4ae0f1076252984d37c411253e432f1e45bd9d5cb1e3850e86f6281f42b13229c7bbf52428efef8c7e0569097a04492baf1242e5ceaf05027b5d0aaa6784a16d871448b2d64cba6518d91873a0538f55d2fb449cddc363fc6c060c4eb835156713c3b1017796b12ba72df4e01bfb89fb71257040843ea21311fe5c35e67ca797243d132f21a13438d4ebe8688129a8b72f5b45cb59b1ed97fb534e0d39eeae319ff23a29da5617d51813844ef36f985a94bfe81a5a8ac338988d3eb6fc4e6df79ad070bd2fc7699c2a467856542ea8ae91b655761890220fb4f4a060838b4afbd3b60fa13d852312269b44ed0d660d90168ab797cd7f25a653d09105c2c487f14cf4d2f0f19ba0f4d0d89d198c4217d6b2823ecba30ce6cff45bd616c780013133d26d15b5479959b0d126bf9596b9090ba8f572d75cf3e3f4d0f0012d15094a6b43ba3df2a2f7a2eb44d4ad07d01b6a4e4063b228b50af18cce7a70155c3387577f5f88a04bf32ddfc005b1c79553e36416cc9508e6dfa043bd0d2038f3a744580fe28e4d0bfe356ad7b213a830012cbd4b44f2f173107a98c4d0830fab6abd1b1ec513ad62e2d31a69d370a1877dc0d682723bb70bb306d87fd0b622bf2fb65381ed5fb845c9ddbdedd520fb07d6d6e5b7c276ab9173c5b57e31ac102b4b319fb4d0e3606e4de195862c250c27bf32d02207e3ca534f1024d7e60497e056d720dfbd0e41cb479d605497a7679f26683fd00abeed71dbd78c43087702323a7a36a7081dbcbc4a9b18d29169f6cd24ced3ec0b1b55996f2a319299f78808ff829beb1b4f85a94cda10f4a5d6246221f4a7b9f61ae7781cd1e122f00bc15b437ac75b1a8b86d845216015fe11106df7f038fbc34d4680814d081373d0bae024658dce4b0a8f766013e364bbc2898c4b0f817a77a06881af5af8d144f76c592e6338d88adc1102ed27031eea3db6aac1ac9aeb31408d67add752b75f38314b9cb378dca4b0450288bfe21a64d3ae449b3b067c91d7fe8b9e4b5d395e5db09ac5fd1ee725db1ccaeb034e309d87b730e80fad89d4ef978fbca70cc9fe6e0abca2d2967a1b62ead72e8984b0b824875db5f78d4666f2fe25c211b02b3f000e2a9afe95da66c9f80b4da668e7d0c985f40703552468e39717ab7a8d0681280c30b65051c6734fddb74972e6527e0cfc28411c17a102a7ae2c12a5074af7e3649e23df178d0a238a6981a01747ead2a0b2135d7b5136027b9d9bde307aefbd73d8ae959f6810b1ddfa04b7b3e3cd5ea042fd8ca39615cd5830da323cf44788e0cfd2dee023b33ae3647cc393719c1bcb588de3b3d7db53cd362e41c144a52359b5e74aab755fb42a72d3128e5423147d2bb2fe4c97caca18d02320bc2fad7264b7832f20e71345d898299fe6f1c0b05af9eacfc4643301aadd7cdb0d1bb6cfe391fdb78f9e5130667853a6eb23f29f0f447b86080aba8800c6479c0c0832d48c995286c5c5d190dba69d212dd3609b7709bceed00b153c3ea2017cab58139a943f0da25dcf051d11e6e4c4a4060a82350cfbdaf82442cdec876fc61ac27e762c3e378c07feda2bbfb4f0029a83d9d8df67390600611f95dde33f8964cec67c30fdf8716ee0958959d8c8a939701b78e4c99aa04dad2c9f51e9c993b79b5a0fa78026bb3101af7b245b1ddf1e574ca8529ccedd50819a761d3343c34f3bdb0e2057e1dfa0947f918f55391300f6468e3124d34b27b78406a2a5c10bd57d0a7b3ba2a77863fac3264ba18e668b9105c45b179177a29b88c8b10ddf61d72388122884d3eed48696b9eee05a28761c2681e559c99f1a06c4e04f56afef5abce57193ed91ce8ad216e44de5600f710bd3492263ed3a39a680a1cbe69b9b729cfb8925d805d8338ac13439905e0781843382a1063fa59859df292f893d7c7fda9c3ee30b25a564bcb92b9b70c6996282192b5a41249de941e017ee21c524ad4b5fe49c2c25d1645c37c77564f438eee0cfa2f7c8649f8add2787b0158bbe1ca24cfad6f6c9f1c55a1d729ffe226b5dcad4f9fa1d4a1b47b497fdab5898351900ba7800cf685dcd7edfdc64180ee94f43682367d133c50703ab609ca77aa6fdec6ffef72dccf44c21a2b2332a3e2a98dba8d8c7122d55128a7de481b5d4aa0814833d54ce0f32bc32eda071242db4c986102a32132d2772e77cb7e1c2344ab9e3ced9cbad4aaf57756f0469cd216b5d621de6472dd99b09e7ba081c5eccfd3e66be2cb15fb665c7a9907ef106c326b2335017d85c5341138d033a5f2ba5c70b4ccdf75d9582822588869ba682727a2d4f9e5804926fb6b006d3be8f034c7134d44a49af55d4a0aa6144869f892a54d09b6c268cfb60e1a967b79ef079911b357f2d3685420c4fc821f47b36901d1c6a8e8874ca8944d3d8a7e386aa9eb77599953318a4a3c803ac23299ae89c4db8ac5af2a0c39baded8a1eae62a9a18455cafba7c1ef1f9d60b18a978b93aa2c698270d64b65d0b0020603b22bcda3d3ed5c74ccad5eb25f517c19768c7d3684bbe5384a5560709dd16ca516b49aca3669fde3fafde299d1ba0180281033bdfa159a3d86209881c6a4476813947d259a1a2729020f59e48aa710acf0548bc643ec9e60035960ffa65a53b3951aa47e9b9fc935f1f9fad149655ace9b25250c2bfd3c8ab25b451598bee8a56c7e0abfe3f6178a269c0f60cf42d55120b61d41c6274174040ffc23f22b0630636ab40d48e30749842643400020311ec6102b16afff46c7c5d8c4852d22f56301ce4099da9684d29e79af43111fb0b2f90b274f9ee993fc48c262620d6ad5a676d546c720f135f46b59f496d67ffd925c21be8efa317546033dfa033d4e148b0cfa6050e8c6de78077cecd800f5f20cc3b0be41d21665a57929beea1b77d850194b69f40be9dc318d1aaf80ee664a9f3d20f1c6cf4feb6d64c5e33f4f480fe3218dc5c1167e20c3639785085b4a1940b742c31e111ac3ed0bd7f34f99db295276cef3e85a0770326fd8a7c207fec2b1ace0f5f13214e64859b42dac6b4d970b8cc63a933f75cf0a2062537cfc9deeba723c37d34203115a92c20e6512f56177b72f551c596c18bd7cde1542dda2e6e69b184fa8b8f22b0ef53a55592e3dd2760ed3837ae5b76a8b5870523df321681b1c5f8c1e911df11bccb8764ecdb0a4e4f5e678ab3016033bab8f9d8c7fc181e37ad47ad6e3daa8f809935a5a163095f4a68e9e89452bf0f5f6ea6511dcc9a74e4536db2f15cc4cfd356ff2ec8f7e0dc3dc76ffa2afe51ae88bd55519b8f61f4025eb805374efec84675002c0cde04d6993291a9f205541dac26bfc712f91585a6a841c7c25d95409e1d1e7f16067cc084464483fe947304eab8a75b6f18ab9acb6591ba2a3a0f2b779104799400f32c3a650beac7bb849620aa1b3fa304faac600151cd662f0654131570e188ceb661e43ac64640e1dd98f28cd7389a5297069309721544cd2b744ea43fe2d031dd5198b638c19f3e318d6528362279678f2c568491820aa2b283e6749763e8422471700ff04c2731f0929a212ccb438d6a2e699f9c03c5a2b9b03e10adcd2e0f5f48c460a673a742a88cddf1bf4255a965ca01c4fc1af4e6318e66c287a771f4fbe9ee0bd9dae9322a83ec27bbe7a5435a12f0497f45e76d9b4ed7e1d081ddbe63e7177d0492cabb37bf2e8c6a8df040eeceac0e26b2cd7ed3cfdf51a4a30711a1c866cd8d720f31c6672eac0047f2368430e3ec7a919a2cf616783ee305dcf1a55bbdb926b664691466967bde2c6a53e8c0b987bb8ccb0d3829dabd64b2e5d9a42ee77316302834f99345c4890514f05f06e18dc58106ee594ff78c2118be75a4a59e220ea84482a1af9d9546dd820462222bffbe027558b20e282c5852b138c9d23302555a5074132a47c98b839ba4d53c3c8f5d1c17e46336198691632cd403f8cb9f5987006a4791517bd243b87001200b96033f9439a414ee04e98a2608134f429f98f32f03ce1f6a827bbd0ec5229a71d322d8f1e3c132ea214929b50d5a98ef87985ca4dd6332369be701c7914249da703087bb89b6cc0275bce28bb63ad045e6e6f2337d0eb171ffb1ebc18d94b66f54428082db4eba4f50161b0f99d3446af6bdd818582cf8e6816e4e9bcfcf72db137876f26f8fd203d819dcd833f6844a5c2cedddb1fcb78e57970ddcbfb7654d3d16449b4fb47d364b260d7e375200a7b691ad3d77cb962c95ce0f432e7aebcf0ff85017c8d24f5f159cb84efd5d0047570ace5d57b17ddf868dd1069f0260b91f63acc32a8c0e8528d2720dc62336d49c9d8712208d40fb09c50f7e26d984c3c4a4bc737918a80ee1917b35c7c732c149fadf556a59920e025eab5e9f15b822170c863963b83d6b81dae424ced4aa19e84783ad52cd4b17056f5e62a60e7e04fb067e8079fce38e9f29d4172ea7177e4cc22244a5c8b0bb3daffbf2d215845ba953733774a8273a9a02b949dd97615c30c6661cd142fbdf0c3287c83fbb881784e0ba99bec75232084ae64baef986ede2040516779618f6a3d9a4a375e301bbace8d7e4e72d2647412892ab6d7a147894e91187ccc9f779fc70c0ca4828fa5a071304f1c1c52b57f3ff4e9c0f3940e98697ca3f5327d3867d803d6c20792e56650fe81bbee30b71f30d6c92665e87387e2f74b5a19febf8d2c8125de5478935522cff99ce0b554d28ec530e4905e97c3fecb0ed8f2a122956702690501f2b4843dac9498a090115601fb72e0ab4f5d6d511257e8ac395453d7d7e5f44f817ee842e4965053ca3b118d417b552fed7f4f5cb84b6a40d383b70b1fc18537f2f03aa857409c6f2a7d34cc8cf33b7a70ab65c1c6eed12ec5f5dcfd4613e2fc6800077376ceb7124ec9e48816f96e88e3935b12fee08709672db8d943bed97f549697e6130e7838a628e62780b4293eae002c0ed04193d5fd29e89ee2d8661bf8a00bf78805f74422f75e8cdc9d67342428af05f4924e19ff52229f5e896eb3a36b2cc9a540c4696aa21b28b2794f0ef68ccaa569023a1e940584aabc84296a93cd47bae23d1c9916c89a25d06d59a60f0eb7c9d4cb27840c8a318d6a29de2d7b77a78d6d4ac1317aa6d5efcdb136a47375ea87ad48c82d2e4c79873bdf8722e039bf9c9c389fb1dbe8f732db049d8019d7dc573ab5fcb56962c906955f8b887ff34acf95b5eca6f62ebbf8620b6c887f8dab82a46049d46c1370fc44efa4a924fd9bc534175494e6fe4e1c2a2a7fb9b55182df46226c31b0b1afd33584c82329ded7208c58fafa4933c41a2a30fc0779d2cb5f45c95a422b63b7ccb34bdd75c847267c3a2441f8254f5ef9af7725fa8b34247245090f2f86dfba100b7350bc7afc0c0a99df84ff062107bf0ac7672638d3ed7a64035dfb85abe2dd430dea00be5810cd2b46915a72ea1a7146d88e4b14a1a8452f7be2a0fb130431f3a1207bee43b1e1286c085d311afbb065ec6b9faf0361ba1cab81d3545d6cb5398ee3514d2a93dac5ad0f7b7b3cc838657bbec2e30f6ed412cc54a4798e47c1c9590ed6dd0e097e64be46e14af2665b6b852eba20c46199c8f91a6e44fd5f4716fe20a6bb98fa8e76d2281ca1154bf1c5b5ef10c11bcbdf9072cfd059a8a366ef716db32605b23140276f5e370961f00198d47fbe6bca5e155a123f8f67e39849334ab159764d0e1303f0fe550396847fa642d344b43d4d0caf7b0cc3893f457fd853dea38cd573810636f5e1e66f1f30f130e3cb8d1a6ca367201be2cab425514ce3dfca2821e7758cc14195d872bcd174e3d783c4cc84dbdb7910e87f3295308e9a0b82f5b8d82feee225dcb9d58f3679b31a028b48b407a506ca13d090d29bf1c9d1462d32717df12ca324e71fe8e1ccff5ac02346372889a29f8845531c8b036d1a14d96793067b4e72c8fc6f15e04acb48b93ab90d8969483ed6c0f921863ff7f4873e96beb6bc69c37c7e28f3dc2be66d83fcd119b9f5345c733be26c23ed3a86e9265906246509fedbbf45da625eb199f6982d4d022eb75895c8b1650c417d635449b0d18565a6e4a4fc0b7706d88477b0cfa430622b61df55f7d550f0010b77c60b5a041cf4623ccdc4a55e4a1b6eca24259cd65eb3425a2818e287d5cfc3ac97240259c18a506ceaae9b69ebdc044b9a7f5394531755f42c52e441c969bf8ef9edb664166f00391a3568a9650e2d5214339562daca31cdeb71628ede44df36a704fea53ebfefd13197d4824c65fd5b34678488d88add0d1318d2d4585bb36d850c116645703a1fe39b1772a4f50426318f58d153fb3596d8b86760f6a00c9c253564a4026f44b97496dff035dc27b3735a244df866444f0c522c6d246a69288dad724e612e26a2275df2493e0523734bf7895c6e7a31fe74438126d988d73b6ada43049f28c7a5895da4a1dd1c294654f22a9cf45c9109d4d4abaaf795e6083abef838539e70ae72af4441a0339eb4b5bffb5339134f8a5be5541ebf63c75f279013d3b341d3812f903edbccc03f35e1dd87885aa6dc391c0b6e5425efd4e7783bc98f3f05ba82e2584872510751f12635bde10b8e0257e2483fc5450ec6255c1cadd775a1f1f49a4eea180b37485b8f2a83c504e2f6058c7ece07566b34f10308540abc62b266230ab6af8ecfdbc8a3dd88e2f64ce2dc3a08da298ea20c2badb27df2528efe040c52a7b0a64847705555b9d36c830c38d062f7ddcbd0dff392fd1a2afa6d815367054087765cb2ff0a32f9cf8abad383b646177bd899826b2429bf51c83c6ce5229ead81582a2dd19a3614efe9867096d1d467527b321780a056226b781ec753a7db2b01f27bf2b1a00983e15c31fcd5b0e2440deda9a2dd9760b99dd70ee62fe9eb57fc8cd5811fa49f028a3bca1a97b29f68b3f43a6727a3388334fbfcd080c81c7be7b2a8479811a75e145d8b591f0a7e8b8d3534c7e0cf68e1ea45672b597763c4efaa89f049e43864604b28c382348d100f1aaab8f8023b824819ddf036003778ccf451aed8cd29d0b23034497526836b6cfcb2b9338bd2677761210245496e227b42bbe4329b73bf872f791bf1c9b8675c64ffa06c865bb2624f38b63de38dc642c8b0f61fd122fc4faa8114ac8967db1609123f33a086cc379bec302a85feb59117e3760461cbc45b081cd6afdccf8d961fa0b02151ef1d11419f9c8b84dc0314226231d0e7308292d14a99565032546b7913fbab48e3e91900ab2f2b06ea189b111ce074c67b306dd162ba5ae2cb41d56c299187c956a5e7b5da001ecbdbf470c8c708054678bdf19f183b6b0c5e03b68bf85f8909d5d207b797557469c3868ef6e366a4e3b290f4f407bcf8cf25880fec6d6bb538f57a62a7d6f95f64e1694d333e480b718aab76c819249f9f28055789c9003dfd974ae07b0bca2d1fb3dde65a246d84f68a3f2b6f4ae06be0261fd1c48a8de83e5268d78286c62842714a4c65130459ea81fbec17103b7cb7e5be4d74e45255e1e59d7cccb8bccc0235b9c4097e84961318e3e6d18e21a0592244f22d166b9c64e45d0a226ffacbaafc7bc2a7df63db5c326af635b64d7758a146832ce7405f24d64e2c9204e3b955b3db64a0c49d8240b78b8dea200a0b27140f885cb83b9c52d138df84db581110a14b60302d79751080427672cfd4d1fa3bb06fa4dd6b42045e9fc3f3f03896745ead9eae8abc49fa7f31077276a17c02647eb733e02ee2d57092d58fe6a37c24671edbcb8e401e2f4ca94970de8538d4762de1e8fab6a3e5442d0b09b1ba37b155e8e386a7c549604a1c5aad956409bd461f69710f379c98e25cbc6c14065054c7edb623c974449dfaae4bd6ba3a83cba6e06bd470e747446259b89466667005bbb04ca19bc90f2923f5155f7300591cce231534359f19ef57867dc09b411888c8d8c73e3a66219592c7d7a5ea3a15c42cc7362b68541c2b38a7a95b7237e7259d7b6d2ecdc02c928dcebf9033b7f207fa54bba168b6ce82aaead1041b8f9e18cd515b25087e0d38e4191407ecbc34165b53f4a577fc8ced000d73c547d854eb8e102297ba4aa4b2344fee60eee500bcd7966d77381d2c2e1ed18a973df8bcb2afd7ce388cecdda178eb6222e5a589357c882123d1a80dc09558ec9ccdb5abe5597449b421734b73412d71a02a2cf20dd76499b5d0a2570a940745d68e846b4356c2456be625d1e9f560c6c9de8142c3bc3c050da3f8348bb8f82cde1fc72f3d198775b8528774631fb6eb4b30fac55cf10f755106a3811277b5c8e9e4d24716c6a34b08e5d70792864c927519f73d9c8327fcbdd776df2aae6933065b59f44a9d36e6b59b43bc98d41aeb1aae15a555ce807a8be5bb2024d57348adcb9fc428375043655e2d17b93bac4dd1901cbd4b527792864e46b0422b2543a37347f7a7ef32860cb33d1153e46b483cee74411a47c572e2f928076d1362b9f712e8d2e731c4d7051f42ffbad977eff9028353acff6a5e16f6b1be1ed2bac3f08fd45fd9d6c72833d8a90cb18143b710a9c96764eb45f313cb037ba42fbf425d974b0745a693f824424d4e686a95411a9c429f525eba665fa691181d3a6d7517c38578333c3b2caee9d700c3861eb4a284305000fc04a9493fc555954bd1d879aec93dca13893a6cabc6fc9287c122ee973ff09385804b4229c5765558a3a7ada69ae053e625053067932007f1e0d93c99631bdc8358cc2ba321bfe306d47408a8e1d86711950ca5c0fe1f1d29b122f2ea130a6c636f419d3fc71d04a987af8ad4b4422def234c0b4e6de3651becac2b7c6e8ee10eaafd08251a41daaedf86d6fdfcb5179b5d73ce94654f56b134e4a03ba5e0077ddbfed1d8fb9406eae6e248355925b152fc08d82596c95223e02ea7b0a880a9f52c5d20b8ea8ad82b592a4125e691039ecac049ec35aca41aed8249350a2a7efb2abaa87dd33c704b3fde12aed9c45dd62cb715e594cb639599c39e12944c79c3d9d29c5947c7d891b1cef52264ecffaefbc328b8981eb6939442cf1ed6f282a7415939488b046261cc5a2bd80efef74bcf7027adea11871cc58111cf85eb080382735a8f8a440240d135c8357af6d3e4425b05fc7cfff0dc64b35983a90c362136f99b6e4579d4c8d61cb0986c763631507a5029d8d4e4376efbe270118b3892c57b5ac99e22ca398fb864db3fcbff67d36b00d7aa27e84823c7defb3e8f7dc7ceff56e927c2a1d1989d7f6f3b91046afaf6ad90eb5b896a5c084615fca23c9d361afd7b11b89d5498682709ebb180c4bead9a566d6b3283e408f497561a9bed6f8f33a6970c1f5fc30c6b8a301bd1b0e6262f6bbe11e0b62f75f745ba20871413e44598673cc59abe6d015f17031f58e67534a389687cbd01689cc9ced1148bccddea19a050080d39ba09a473f8d6ea8bf9f2938e5cd037df4c3aac7b794325c84206d1567eb5b82ac4b9a054f653d3e94b3fb632cca68224e6720c26aea2cb3e518ff7a4eea5c5c7fcd25462d9a56a5729c9832b21038c02e54c43f5985d13719cd994c352c94f7488b172f61b0889c23be202334c51fa155d5999a17afec9c891e3d3960c8b82f1432db8d028cac46806537fff1df5d0efe0d64d4d2fb4fbd31714e34ca9d138c868200dbdc403189ace399c3f1c3e25af2b5864941cfa51b5d2721daf9664a500ac44f147bb111acca9b23ca84b6843dd296583126e6e83838d84a5bfc0bdc9d907926f4a33eefa0f7d33787c229add3bfef8bbec2a83e5ea8e49931abc23fa7ebafc9730e7e32a4af4dbc2db786a3166b78adb07326f9a10f590654d0d770c05f4594b413e21ba2a81c3653867c501ade0a7756176e549fd9e352f39d93385cffcfe654c635ae351b18ce8b54ffeaa219d988bb216495c89f4e591c0f2d1a002749ea9b4f8e003fde01a1b5c0be556f9d7b80d00b1f9783a81fdd6ca2fd82bb57d38e89ae44f07aba8fe9a9654b27667e514883140b80a2972a83440b5c8cbee275e30cde3a12d80b1b21aad8c0dc647064b5259f433025720926117e9c43e12feab7dfc8be06abb348793cc45f33c9d080d8bf405d75e68652e803ad814bfe7e3880690f85e021572ea747323b84e687fad93db96c1d6c941ba4c23e23ad13d0b1c281743ca783f50cd02c713ba5220d59335db6174560868a00c9f29a0592588b32dd6ba3e69168dce0acab783a5939d230cc2ce72a03ffcc4620f4b40cc240ce1517276a481e1d6ab90e61f5c842804aeff879eb1dae164cb6feae0bb99bdc23a0a0e50365804c67dd5be3970ce122e54cbbe0029f30910844382e95e65432c0db75d2bc04a2b4a297bd17559fdd7276650ac8f8ec98af4351fca83c3adf579214787576c8b8069176e4c4486a52cf1e4e8d34e60189128becc3bce34bd72637e697cf3ed54fe38a31acb0404ba5d9bf0190b329dd339761bc4ce0850d1f8ee83f2a3db136b50dab89c496aa83ca5f895aec8f2ad2a54ea633aceedfb2d46d438f21c27d43a22fa9a5c70ade9181d36079fb0d324bf5ede444b1a10218963ed162849ad8c53628a08afbb158ddceebe1f43facf352a8e0927e19f48e5623d53aaf724ab097e6047646d06faa4295753d628ddd055216e5e3e9678ef026763c97fbc95452a94de42d54d3af21a63a6c404c697c0b13a311bc24ab2bc8c707d9ba65060572f12eee7ccea3a11c4635cefe596aafb389a7edd480216708e6677ae3bf32c11021bb503720f558466cd8f36715c00aedde1a5ac86f8cb2e440ff660bbda85e0164a86f48aa2411951aebd10ea8ad49a2228b3a41107e064c44918d109dc01e1cb9f39a9d80e56c6bd8c1ad5665ff0da3a744ab128b8b82ede036f3b7da62042292642a87d55fd541b78051c103760bdbaf76b5a163ef0c47d94e1f251c704d09cfd7ba42f0a64b7c957f87312b8b1c0e7fb008b39d4d23d0c3c557859f4beb12cfbe52cad091c6acc756418f0f255e9bf105aa1079bcc93faab512839d81b46d8b69f2ac7a22f6e70bd6eac3e55c88b6d9023067fe84a51017db08b6ae358dbf226154f350c77f2b9c57f3dceda10e9b305ad3f5651823d74dcfbf73be147d1410706c0ab0e50483b7eaabe43b9d2e3475ad07aeb96ea82563e2e5b3e5662dfc9622492bccd9359a9aa64c78202ff444212a2dc38534e50b5fac8c92759a34a0bb8f09b524f40efc33b536013259e22301312a0abec5647bebbb6d9741e139af75ea3e6533a4444b450aa771f59a357bca6b4e486ce91c0e7dd6c4c93fa5d65ce2277bbf87c218f1c54ebb7d5ac0885ca156e14a4efa1328128232b8314240f2397ef1b4c52fb22d1c11e8159e72ec9dc610a85f81ffd38af1a86286fc0237864d7c9353ef7ccc7fb6b0cec46bcba8f43e8cc88509f313399387791bf6a00670112fc994e29e06bfc9b4dd0c5ece9c2b37f67715cd7489a5fd1b1011e2992f532a6546451c5a33d540b3c6ad4ad742367621e707c6cd061a3a4ad49cf2d620ab048fd5cea53774de9999b60b82d24bc0b15bba2a11f4f1cf7b1e1a74c157382524fbae2a7649af6bc2388922b6c30793abb49290b333f055c3dab12d3f60af907701418ca005e2e95762f993f2692b0658f6646e87c30544deba760ed23b5a10b0ede33594c00d90a048de6d974a39db8c95994157312b7bd70cc94eb5244f7a7cb14bff425ef2c3f9222c40535c2ddd1bda6f18f1a3c829f1bb069382dabd71464cbef5c3ce54d41ea16c91711a41928f1257cea734e72374aa29b282b1f48e36c2549d458716a5bb0e9ff76b76da600a450424bb4fe0658bf1f52e6f38d39ba661b27f581ee504eb74f3008d3f4a20048401a6aa04a0a98acc66584f494838d9a5685d05d4712b95a2f08c869cb325bd5e61e8ddd4e71312795d6ba9511adc010e689a74db72200910d738e0e4399d013a8736021e1f2ba3f72065ac3f1a029c573de6f69943fdf5b2080cce424fde4c6f2d3e044110e89ec1a198a18009591be233bfa914208db04ab0cff0a85eac5108846d79c1406aeea11688614f10805e42b33540694df75b548bed1a798472088492499b9d2e8393511ed76242c7e2ee3148e0f07d66f8625a264078aff1f3b9623e221b3f1498bc9aa0102ffff47ec08978f1a6d80e84eb0f6c7a6808afb32754fdd58f4053259192e3d88236d6022ef852fe8ff18ed3ff862d5ac80e9cffddf4a319cd83311f212f13a57209d4c3fe019d62708221f48777c530eac498eb7180b734c93c463d05675c5e57a599389f754c05b6f96898188261600f9d53ffddd43e349a6341c6b886626011023acdc3a8115ad8a5d8ca61120d2f945efe4265a0083a1780dc73cab1607b4fa8890922ee11100d3758f6d70f64e577eb4fd16502b2570965d8677f8552c737327194d9c5abb24c043b020cc2ee65e1bd715cd169aecfbfca53bc98a6c0ac1c08fcea8e73908cafa2e6d80ad1a9bd34ad02ac58ffe09e5d0d9ea53c09794926024b1bca0f69c2b4cd25ca54e61aeaa2e2d0660d064a0aa981bc8154abee902cee7b67820a000fb2915c61110d34eae6b562a85fe7792f056ba245c7ee58e9913278317b0240785855553c18df869a0941eef11ba3362f76616b6ea8db3c95b345f5e1fe75e5e4bde5a234610a0884d60e9f49ce42d4e2b397d06b67eaeff6cfe6852c3ec104e63b7a408dfd6a7c4fcd0bebc54970cc1601d6383cec6763789ac52028756db22a56ea97e65b3651aec18cd9b3223af8e4b605313befd28f5e957a098beb09de222bb9edb474f54a82801a69b2fde9ac9e805a6dff44ec711927652aec18c39796d64213b8c3b6fd06f1190d161cd252d09f09869e97b8ca390bd74ef192e4ba3ffe85883fa5ea9840d56469a6f12c4120f79e8e22d2a864e061ce6656c207253a0b2a8f9bc0891bb0ef249c0876ba3818daf7e7f772a92067a3e609ae84775fb66f88343eb2abee46a7f83e95bc8b7f363cacbc2568c84d4810c7806e620712ad0e33bc4a98993500ca5ad10df0b673856aee7b6cea53a27e645a0ea484519ee6a72b10c5e45c94d95335a506cdeabdedbc12aa1ec5c5fa79c5f6b3cf23212d4932d9002525ce48b5aca74e335321b4f2eac9e1319830485af47809789eaf4f84888ea42ac2a49f976723ffa5ee108731c7333df67ae398f5549c05b4201acdc259a76d33549430aa3616b9e2aba6b192f599a8d261d4ec8ad1be6a0080f6196447a425e6eb741ac15e4ba4268df97ec1ef393ca08ba07a64199282c70d427b4da360cf21ac14c263c6f46ff89116a9e8f0f81909408dfa6767942175ea22269fd870a884c1f57f86f5cd9e0b904b1c6e820d2f0951cb2de2b642199d55933fb1dd8ece01d9fca26b81cbec10da27dd4a5282ea9b1c27bb185145cac526c59dfb08974553adbb428b01dab0f40498a10a1e1f84b026073de8bcfbe4595c646cefe869b08cae523d47e597ce7d2663c60696a9d1eb6fc43227fe8bee6514acb0faaf0e56635b7cffe686cf02696cd5c06dd6cf4ef0e97607618cf1a8337169698a065b0937d8516b13b9fb86134b7d463da388472e88e45674003e32107b6d00e3a13a9dc972fb6f1f7839300f727fb01b40bce387c375e96f73a9b1d85519cc7f470001593577a65b3a83d82fe92d184c3b97b307793532959a007414043bfcbe81fcc8ba20ab640e93925ed64ec57cf564bfb80d5fd1808235c4a16fdc2f08ae9da7e70d832d654d502fd41d219940ca2f64f0519dd7bc68ef4a111ac43d44f5dc8d6ae9d6fbf1653ff1d3dba72ca270da5e32d23724809f05d94f3b002e37b006b4373cabc5870941ac4e5a8476fa724b81a027d385cab060cb95fdaa9eb34f24395ae5d6d76ca685ea6972097b7e9fd81cb0603bee168b67209fba35929c234a3e80ade8da15420b6419adedf87bea48211ed9095aa8ee8cb3eca9b12ed6131feec43cf005cd4025ffcd3410f64ea7c7e9067c3b8135628414e27ae96e1a2d9b4b592108ed3f94ee442cd231e737324981f2d1966eb97df41daccedef17f41f4afb01bc7f99293363b4f730f6fab2f83ca4926216a7e08b912df66ad72577a8dfbe471d1ce2cd70b9b6311c67e31e97b1443d4db35d7773e2ee8dd18de5d36f287071254daea2336fbe7bae6435ee45f25e2489bc43960f28eb3949e7abb562ab0f73deef41bfd247f80d9c488a3e555d8c223eaa8910dd16baa7ee545bdce81d04b55aff0d1e033edc6a673a32e74d67015fc59ba59b770bbd80fb7983270eaea12712ae62e8a74fd7620770aba1cc4d60bc91e2a16ee1d8c6e8a883761cf6f214bb8a7d24a9a0518522768b86495a81f871a20cc7633b7bb640924f086db6254e8cb3cc553000338f592e39395bdf87fc42389013bdb474b8077ebd30fe87b16beebc643e5a7067f354299427caf94db49f5330ef9f445b8581dc4301cdfd987673cc8a2cc341208e50fc1d6b39064ad404fd7e5cef54f7e8f06c2f4fda0ea4ef5f4c2252b37e7efa664b4c7d3d75fdafb481cc00202b5be456ba6b9a024ded6d133f6f530829aaa41fab9caa7cd4e529a9a1d112c02b9fffbc1ab785267eeac681a8cc515af6ba976c3cccc92e908894603526836fe3f45e44aae818039c8e6026679d4375b3c32c5d96a4eb44a2c19f71973a547a91a3be11f6c7313ec0192b525cb9fb6167521f02731fd2e0564ac4fbe58a65f740e38783845c6e6883dc25a77d46a6aa8b8ae50f558bc93efea4a572cc5fb6b627a98038e5c42967ac1ab80627c1118c055928ab1c4f8df6b9e47ed8d464d50e99eae0b496373d5c557fe0eeffc81385c8f98c4bc293b92c261f4ace59cba18f25709a255cab90bdb0033152431b4b8dc2e7c1f74217004442efd5e2393e7cff2b5abed6b987bb4d2b30a1208a69a5d4e54e699b36e9a680c9476fe1a6004a905f37858f9b655c50e2ddb6da90c34985499c3c982450ae3291c9f659bd075b561c2237f5c966a986208c2fd3114118e360d3169f42c5012b198eff43c90f90306d9f66f201977a45f36bb78dcc1540d136828676987072794867f59d9b839506bfc17da2bc53b253659ecfd72893f785f56790cfdda4fdf68bfdfdd6d8d5c29de963c3c4781db1cf2099198c63b65e3f5e5e76ec9b11bbc7b1c1e06546f860806e6d60eb0824e57aca787340c0d8529f497a5eb796c86523ed333e52e5331a178ca8dd8fac0bbfa2c6f128ec24e42158b48ec50900574e7cd877ec206fec7f6779874ef95cdade5179ef4e67cf02896e9a9dcb63244c123884e98751f1f15ced5b4001f6ca73254d1f10088482ac7f890cf2258cb4151c303e565bae59e2a066dce97a0c1591f3f77705d2fc3998de879e5cfe011dc2cc967fba5908403cec062436c2d8497fcba45e721b63d58d54f16d9e69a8749992d1d6a21528acde261ad9e71ec0de100e4661b0c4260716ba4300a24fb8a072d115065611cd610355df8903ce3ea1ba2c966e707a017e44365c60a1404503defcef4dbd6062adfbb3036aff1dffd760a34c5282b05cd87eeb00b368453a34dd3c44a8d088b1b69c740d1db232a2e33a73689e4791c7bfd8421f43d8748995cfb8906db57e2b338a4f0ac4cca900a50a4ebb8f9a13de7b5d93e256dd0d2b2c0c28e9f910e331db43fa331aeea69e01dc33cb17dadd5a86e395ea4fd2b76a8d5c472f0cc31e4d11b473cb99270ac20b0538228e5b457fd49140a108b47c2d9f08cdf6e5a5fe6eb233863ed7cc0a3580d0b7e993393df0503a206c9a83b1c5f49a5dec75af06bf9a6316268f85abeac4a7c0e31c54eff08b1f946026d43787babfd6c9a59db068ce63c7a0eb366832e13a151d05046536daed2e33a0c8c0b22ba2a90dc8b55d3039077ea281c4d63fb516f47533431f7f9f84449f7dcb7e2531b3650a52f265f72a5340d296119b4c81d4ae0c61b6acc1e10fe9c768c49e1247d11a289121b6eeadd97e3010b6fef7d29c8b704b012d38b87cb08be1f6416963f598018ca71a0f85d102d9502416171125e81bf74f4350816a9c3139be973450bdc9f49b632829edcbd9cadb43de482a78444d63f71ed084806b1f8d892b2326bafa33b8ffb2532a647fb875113b258003ec7b3a995083e2716230983e0785415c9d3c60a847b03ca8cc7615c5faa1db8935dbaaa742f7ac7434fd86536e542686b82e782e13ab8b972596351cf6da4d4380a6174805d4a1950dd79cbfa896c0799c477c95017b0f1cf592dc005b750df981b47b323ccd575f06fd3e789a83788194b733c3f450c0d565fb2c4f590fa707684b9ddf248f40753aaaa618f5a7fae59a851ff70b6fda34b846c6f46b99459f4daa3d17244a0b67a1223049a6b442b2870e225ba47ba246a11698aac04af94931e0b5b0c1957b0919b935aec10d19f33dfbd937e928f7c9f7986de55699a696807f0ff1ec5b30fd85db3f1b85949484a65a33bb1212e74ae2c7981c89fee0b23c16014ee5fd2335a2951cc68d6a231b0551f75163cde919620684289b26863ad478fc9f211c4d8eb1d133773413e47865a41ca00f19d222dde9c3d15899335e87b2db014041cc37c78e1eb3158a8ca5308bc537bd30ee26340d5a94d3504b2fc151fed2a8d803b53468618761603975df0a2289c1c4ac4d0bc85d41567b2380f092ad01aaadf48e324aabc5029c2dae1862dbe155c41d3094fd5e3b14075f1467358ec066bdb597c6ecb7c8f1b55fc6218fe833db0965761167383ec164d62d0e270110d7e322e1939ed4a71031930c449f5e548e5ffa9f0fa9f00bc993d0d94a859ba359340b58eb2474dd8a86a55922c98c1c57bc9bd48a5c0bef2a89025e1b46f87622001b326751b8fbb7c7ab97881784e5024b38d646d078b1cc2d7eeef2afd101723bed92f4f1edcb38f7628b36fb190be560993f45dd62f015c49ee6afdc748a19b4ead24d01548b2c868beb7190e9483e486788b2c21ecccc073bfea12cc60ebcc8c3e8840e6d9ec8d75ddc4a8f0411940485a406dbf1e919e4a134f87923d5f27cc5835a3789ef84d72b8aa126c204b34aab41ab8c18d24d0dede9f0f459503ea72c6e93e92c7056b9e2b6f8d25869207d36347ba821f32fab0e9a86f6652ccc9adad9158f345a1723a80f9a5f21f65e652aa06825f1d19899adc2a34c0437c090319cd72ac205fa285ae66cc511719c655b7e0f73da90e9281f626008b29addac1337230cf25cd403efdc0e9c870105506e32d727b5305d6aa8388a5bd2743ac643ad3e44a8844e296025fe0f86c7f20715bc0ede91d05a2bbada87ed15492f0aec3ab625f078f4106f77528ce3d0b8bb8b2dd4a10e7d2bf5a2513d5b961ebcfa05d33d4d1c61355914a606ed990bee5483109b13c48f9bf4a2208ac8bc2c73d7920d0c2ac7c58744fbc094f8954c61a34fb6ecfb23501d0087690f9705999a5716854673f1adf3c9f286cb375c2e93d747c61c5fc001ffd7f03cd56366ab31d89cc78c7e4d6181348e84d752ba7203aab1210355ba6af7e828b3eb10e69bcb1d5514b6ce14aefdc8b1e289d863c9ee88c3ae8d6d10a2ee5851a708ed55c28dd9a3d23839f62aa382959a28a6239c075a39692836e6abde6ca4aa7c50cbe7d261362343bb1dadf28a7e56c2bf7ebc3cb743b379c3d59af6c8012a7e0e40efdfc7ed884e4c3f3229a08e094cb9f33a65b97b4948c2393070b9fc28502f782dd0755ba0a9f653e6b1bf9dfb4aa555c57a4ba4b7ef93f555847be90d89eac7d46a2cc91c6e789a26483b3e6c34853263ec3a83257ea8437f58a1a7a35d7706aadf5e340793fc8a3b8adbfce770a7e062054ef5204c9ae7e6a7d08469b116986209ec705b1c51af41f3683cf47e5431ec5aef87334581bef1dc8f593149d26209d214c0d6d16c015c769ce3eb53f3a29f5b15f9d887a6a467c2286b221de38a6c069d3820551fa336f5d5e029fbeda77510bb3c994d4dfbbe7e128437be4db6b809f061a803d0e76e01171927f20a85ef1ac387545df3be6d761510492a18c0b09f779a28f216223af51197bcc2260e247ce17bc155b5a5ce7eaee066916746b9854fc612ecfb02106d02d50ad977914f16ddd16d603a23fa4a2611313e0c26b8a49d77cb0e79dc8c9d7e1ce469cf6be7b70bfa0034207d281e9156dd8169d1455f853a394dafd79d196747d42ad690ff13a110afc6e6304b4e790dd3f5e2c90c68554a5079a55caa9775e2ee402d3d9a2c2c064095519d71749aeec205911b8e567252cadc229dbec09ff91a7ed05e424fa1c1fc24100e6823d4c9e4cf61199f47fc452da2d031d398bbf5a87a7c0484f85bfa3add1bf98778bb52426c5aa83f450988fb453b1ed53a634fab4553cd064cdcf22b59afe09447f89424ebe22a999c6f7e2221c58440d90d48f08ad51ae4118c0a4fa20caeda8a7af557866d0570513e00763d48064c38b818eb05792b978cdea03bb9d40b713f17241ea7f90b22e21c5ff643460e91e3fb2b6a06a4d751f0b388ad2dce971ebbe333912c908b8ce0fa0f309beaa16f01d9baaaa758d8a0e36f43c90359a0a8897d8b42f506016f0a4524d926abf76280a0db792216b4f5312c074a3331b6221ad320e5eac00572ec302886f93dedba3268fd1c64b3b243d449b7bb9ab9074008e6e5dd57ce9417e3ac6ce2cc79407632f2d59739fb14ce8b8df6dc21d68200cd366f8577d0afdd86e761597b04f7e34be6afe784fe71ebda18d4ed426aea21f09e69843b65619da01e6e0444937c5136e9031602e7c349ed72816b81a7834c50e82d90ea1398dc273ae0af574758a8d3e7050a11f78163b113e5c95e6201987680f14f223839c3f15de97da839a48dab24b721d4a9fdd95fc06f927a8382800c392eba47c5609507962ef66d6a242aa80e967a70112b59c93eb7bdc97a02693b0187b9ee22433a871a952b928c3f3bbff06aa19fdebf61533c16a88c984e0500f5b515c81805185d47691619accbc001b633505439c592cf6acf3b95ee9e7a1b96531a926158f17c86638e4f3d601861d8bb0d1f1542d79f734a087c16002d771b1cc30d6829346c786304d036fc29dab3ffe01622255d9d27dda22199b07e2a23918b26e0688b2e24a599b04743603b2d2e1ebc75694eb0915f1f19163e1849aaa68311e90bf21a21b58873ae8fb21003392df8de72296c105a93e99400725556055eb7c629e9a920639818fb7e4995bfde83dc974e554f5054257fd0c91732700bc953cc2e881d35cf305a2fb34af84f7b4eae09adf67d9315f114afb7c9ac8807f3cd32ea6425927faab834d8ecf8024c0b4d569a3e6b44cf659087dfae3b5f21a824b40db6185020f1e51c6f9077c73b85a8afff6688602122d07a5d46a3c24a1d47d86d9227bd33335f00b5a01b251bc5d3d514db63ada79646225f0c682cc253e03f7a1867d3c390ee122e60de2331a59f3aa7d3f0524590b951ac2e0b71bfe66b8527d66379b3ec1c9cbec8714b75aba55b1899500b56e68e97347c0965d79a8e4ed34c26d962af97b571d2f7d10d79f8b43c0d53c222543fb21e2714234e0ca99d164bb136c5e3187085be0ef67fde64fb28bed98372d16f277b6a5f9e45b4f642cb266d3e631c601ed404e332c3e711bb88e7548bb2e52df1f3ea1cd451ca27bd3f75465c0eae83d3b1f1a44d62e06a69fc3c880323295f4d14efe637b450d115698e3f49cfb45f5de49c620e086551b248c380937f7c6fe59c11a7e6260e9b549ae3e37b7ee3e70bf78635cf87e7ea8ce41de759ab35eb7a447bdd8ab43cb0ff01c60109457031a83c4cba817f2eb9ce134ca617d5a20a1a898e3736fcecd9f6409dfd59a38ea2b14303a9ce6316a231c137ccaec1d8977af7683fca4f0af9d6c5eb49011968d692b655515a26a46fb23ce73b1aaac987f9ee332605d07070edc6e2f9e6916e4d33d5b41256d3e831ba9a78b5b1f6da87b540972986e6df56eb2473f8526704aeaf6a13eeaa2b10c6650d5907acb6c0bbabadc1892d9634cd343e181ad4095f3b0ca4a217d3a962e0be8e5475808664c80093d63f431275b7dced6d79607172a98f3aff11264412ec963bbc0856759fdeb238bda0c249ce0c6e0addff7fbeffefffc83c9ea9d0fa35beb894f2373aa04aa7a71a84dc0d98b8d65940425c56044923154c3487644ba2084d2671413527b087a337167fc87ed92ba211997e677444b0c73dd2d4e3989431f4408f098f8d3833fc9839d54583c0481d0a19165ef0daf514c4a8d3bd44e52df6a1d19663c6dd05d2d3444c528fb3374a49ac686abc3064ba1c339c6927d254f11e08e51ac97b2f0795a4eed0486a50db1e9068e15838aa6b77f8a66604dbea434e264a5308bbc98d9b0524add15aa976e6e9c203a766af6a2aad8d321c620d4ab58781e50e5763c080a955130f7972fe41fa85fd4e8f7cb4b64a0a0fb26d1edd99ab7569e77bdfd7ae6dbe54da1e0b67cf679c55f99e560beacd8a674f07bbe5360c9e9ff6525b10da0c5d5a058caf188c2125519de2a13c14ab201c1685abca7434812d1485360a62dbf90f9c2d172284819315531d8842e938e2a83b64240e295280abefe2ed2fce14fbda619804a7feb9b61801c2d7002da7ff60bd85761c99dcea21cdb131a789a8052c203f6b4eae1f5008fa38810261748b15d287a714ad574e14341c3915487085e966712438dcd2747d401536ea944097cc87a3a3bc7ef9a2ca553343f9363ef8da14ca0e203516d61ee6c06433b6f4994cad916c150fa065a6bfcb35f96844f89692c39cadee189ecacfcd1e193185ee224618a1e6ffe0d926d3528b782c145991a05e6348c0d1247bf82a8a94a624ebe4e6086fccb2cc053012470cb7c244a48f8d1ae466fafb9783f42d0a0c730925dd60c8e794e83e8d4a67e555474e700e5b33be912c86626fcb75f34626c8ae5a1e888c2f11be70104bfb2fc9aaccaccc57c8f577c815fed131a24f901bd2f886756c6d4c0cebad7304ff097701396c96312cc73a9bd2acbb9446da7a73364462be210521c14dfb6cf609c3b7bfd86c8f7f8bfa5fd51c14b1e808cb67fd52dd8c1bd7b64cab72db5848ee65649e636b950b5a91b0e9ed8399a6e0ae79f8d1ae390123a20f40ae4e7cbe6058e5107917ddcef696f6e2f28de8f713f155e9fb84d130618132e30bf70e7353992c14423cfb5d51005f98fceb813c086451432fe98bd995f44fbf2b1f184fe7860140e951b69733767518b8942dd1e64ea31d47d8c7c128a6968a749593ae7dbcf5a4cda77c0d7ef86a9df08fc15d198ee22e7e201f114456f08a481034f660a83a6c03a36fa027b2e80e98ad1f1173e5f58e5f931e377a43c419156c56085a2e50d164ed59103b788ff3da2b9d15454ef23635a0fc625bfb2d38629caf3f642388e45d804e28251a7c5672d99265ee4220f06bf8b6d0755066505d7f04fd2cf25233548634d6d0ca0d793572b7ac922f19347be3eaa740daaf474a44791873f78ff06fd4bc16b2efae7a924b3bafb6e5d97e94233f2015dd9fc18743235f989977677b7ce78b32f6f5224eb54c4f0ae39ecfff28efbff10caf80e928d42b58dc88dd31c05abf4d3574ef008afbcaa01ae5ec4f74cfe6bb6716ef99d77ba25704f2ade2366d567f72b220deb8bcfeec24fbfaf567ce6e33a143e924be18fdae94d4deac9aaf5faac9e462660094fedc6f50155207f6af3883a5ca20a36b2656713df7823453bd207672a6a8fc4c25f43660cca412b5baa399feac0dfd55f7037fcbe9448de74a2de1b6d984bffbc154df830a3bac1dfd21bf9f752a569e87a4b99ee6fcaaa3ab001145194347b45578aaf2077471e69aafa45ba9bb53102ea8ef2461d6134c62b63b04e4c0e9f924fd33bdafa5b06cf9318b32c287fbe8139e3c90a44ddb41e8150c33d0b5bd635f6f1c72982cd9d793d385f96271ff2772ac480c6272e1311bac5ab8b872ee943cc384e4a18950c749e25419975a50e0751ad8a719bcfd4051125485f284064e2adcb96a5d52d9963286a0aa9642f16b1806f68b5dbb883448a0f822b24a4cef29914ae54b63be387946054de885d1a70525358e42b3e725fd503d731b34696d2a4710588e12024deb1dad1548047b7c46584a124a889bd4f1ae71e647cd74df5c89c3eceb31b7cf3cc892522fad9e3995866f56d79b19d2add4206eef839b86a70782220a815a994e79ebd14b5f043bb57d24a70639f54815efef00976b62e7e48fa80980e9e833397a0b2a6dfcf2b36d704bea4655a63fae7678d4c8a0c2c90c2725566406cddd885531efc64efa726bd804af61d856847263cf53083637e77e7a72747ecab3e69402f1a3702c16bf9ce311973f29b8a285f1a6668532570a7c966dc7b598e27c2c063eabc9471dda05b4f6861ad22b731233a6f67290ab686fc4d4cc2cce585b32e05f47c43c360d2197b709833773cb06fa41377b38cb32cee46655dbaa4e28f955289d73262bb1468dac7125f02ba3fb4a8a0ebb846a29292571c021d146fa08059af7ab64db0878fabf45023cc892b2ba1f1b750df8544b4c2b07df6ad46a894c9cd9f8874df14c8a68e9c0848923ae851afc1e1d680d9286fc1e93d6091a22cb9e94b84049af8ac97778b2d919c5a657790fe94efc618203819313239745fbc64b50825e653074b287d007fc7fabd76825a6e657861446f49601b86da4a8c58bc0b62db440228060cce5c89d3f82e9b3459d9f392c2a13d47131eca54135e46a959ad403bfc55c0e7c8501d2878be286bf0047033f0a56e49e3cc5f54497cec10d80cee1e3c892395d213c6cffb15d90afd4f0cf18fb4a8bc2fcb47b42f38f76fec1706659c579a415722b72615e18cf295409a129ac9ea0d38e91eb40a0b7cc39e6ee30cf16790ac3d4d0488a74091fda56c79af1eb0b8a46ed1a58bb34cc7508fb11488070381ac8654508b9e1c7cd60cff3da0848c77bae729c944d230d36e06b3d3bf39f8f66f05cd69333faeee234053aee68c7e145a31fd19f078ea9666baf02605a25cca58084ea1b4f8fcd52acddde0bc8addd7d6cc04f1b127bb2de3dab44141aa83c4f82dc21d68b3169f35694b0998c1a4244a5c0ded9f62ad4ba42d3eb9120016c1a877b13d97fd2e7fa950c39671b438f3e86cce6d01e331e32fe87b44fab247fea277b463b27e3923ea950849f0e41a7581a37407fc0aedb78c9c780c3a07311d1e77ed7619fe0c4a73eb4dce3e183b0201a502685b4e4bcd91bb07dc1767b75ca262b537691c16a13b072329fb20446d5b28a5e67227ae3a025634923db9c4263eeef0c616e3cfe6a467f872184d24e747fbc7c01c34862d10bae1896d0e300aa5b58d2d779a1663e495cb8202e8d2aecc079ef537bc4affb16f49efcea6600dcd7154f675ff5c7411489c4224487f8b5fb2bcc916492424a5560347121b3ef30f6033d503e5bd454f7b5e30cc544700a4e08042ff56c16e300aeeb5986caf337e38e4be660efb4182a6bc40f76d6931dc754420e18bbee7a6d785ffd614b17e52b32773b979657e8e07d89bf0cfda881f067441ec3753800fe468079e918e806979b629a67dbb80b6d19c1111be6d20961f33b6b8dc145dc4b40e0ca3d48d600de4a5f01849b11b4d158a4f0c99665e21cc43c40fecd3de3fee8b7c7a5337713d5038da809268e851d211193ccd0132f2cc07aaacea25523859e036c4eefcf97f39205d2c1b80995c15163c25b81fbd8f48c2532e17ae8526c5d997d2dd7a7b1578ea5499b9d8ba5b7dc1e613449b00bb08d54b18d8cc0fd2a0bd79b659b9bb322766650b2cd3c6fb15f0eda11e9e60c710fc068443ea6a3f04b71b9dfb90136957370ce29b3ddee13ee3c51ed2dce23906f51017b5fba93ab84352e87ac8f9fb42e911e0aa9ac842b733cba6f8641ef6267f660aefe98730dab0964d5145857a966e0e0729ddcd8b7c62648906f90f5f1a8bf850b34a4162b07ab8b7f64d8503198325e24806182c6596fbe3d0356aeda7184fee26397672093458dd0de8f29d03eb0bc6232476944d80d8b8347e3c2dac541b6ffe58258fc033bfd55868ecd68c1bd68c98ec3c07caeb28eb3b35687c55810cef5d47f9b448cbda96f17e657c04c4a069022020c8d2f6f342d2673a055d66bbcfc7c211c829a3ae01960edf75909f9c8a90000e10cb61b76bfcbf2ad4f13b8a03d00909893b020bea375e2e10f4e2597c502f0602eb387723e1d180f72a80a592542eec97cd6430680a46dbd116fe1ad2e590487f417d05fab0a81113c3c5208b257dca6ab90a838d3a04c1ecbf46cb956f5c5e601e2b5fe63b85308874bf80f179b1ce75c2a2cb0734e868d970f3b124393a1b3dd850d0de3496ae5bdec142e92dead53c9d8333d125e91c389729ae0cad4991435f66600075a49ae8c1ea79ab161cdd8af666c8c29d9cbb0f59a98caf384391f649db0a501a26da212567b4df3d80c14072820cb2a631d419e206a05aad0f81783c818260982802329eb444d930749b740a619d5875d4cf3846fa681f3f07513fa161a00c94001582ed08e8d359ad9856af649fd9f25089ebc9c88bd0844f2a4eede79474781ce4450c82411486e8a67fdd11744ccf02dde51dc3e9df92171319af25b2524da5202527d33429514887d9c801ce367976525215635c0cb1c874a7448b74b43449e5e33222edaacc4ff50ddc9b0525cb695987b61a36d6519cda53cd94ef40a09b105083419d71c2d657a86f7f31a470a09734dc1933c2dcb818656791f9d2dd3fe977de1d117893c3061e841734b1c8400325ec08b2a3a02331e6323699a1fe83af84d7d9386cccde48404c015720d00a289a50d0995eb88253479929167bfbd67444b2b13e7fa9f13747c377e67e86631687033e83761a46aeaa707c1136151bb8cfadc45cca111cd81d4dfe1017c7f5b5744ada666b8c30ca5c0240e2315f9085c5ca41ebc769d5a698e9a6859787c2c330314d805b99cdf557a6f4b3eb1c344d7c4c1afa3d9dae727cab05d351ddd0fa24c80ab1dcc31f6d2061aad5538adb3ff77bf9e97b2aa3832024c289cff749993c29ff414f112d108b1a3e2e50fb541e91fbe51bbc192d8f37ae9965d3f4c6b66372570bd294ae317da876eb56e8d536560a2a55a57b2aff10b0b74145ab26cff5c4b63c003ff99c44530c833c6d454abcb7d4926b19be89a0ceada16c83587d595a3e143768d81c529c611774be0400308835ae471e1464a30f6634e0ee36d6407c3de8233956d23d39ae554e487ca6d0433a1e6d4f88e4640c24a0b1bc0673caf18fe370c26f0295280c5e4123455bbd91b2cba8839e28615f449036420fa880201741417405a316480a6c4f5b120bd72db7f1c668ff222e3aa256ce39d137d9397007c9038e72b886820857e6f9e944a04181328894385d1b968987469bd76d3ebc1713007adb3ae936b8c9c4e1021a6ff66d6b1ecad30452269402c169280e68a13473aaedf7c2b4c0e39866bf45847e4e9e815d20a589130aa8691b4bee7ec0d0913668fd51ca81e185dc0881ee0200ba3af4b8e3c2c1b3111796f2af286439756d1fc4b827041563ea917f12b1326dc4ba1415670d72621b9ba1184cecf254bb78d209d339921e3296a855fd45d93634b3878cc51bd4a4842947db2ea2fb1812feefacbafc04b68a9107c6ce3af538b87559fd2310671f7cf00036ec9d161e566c974b5d8b71124018368037e18ee2298cd42b7b1fa270b701d7445065e54bf8aa80660e4b3f02be6f5c2eec9311fb01d0952de884e198b5b4dd88fa9996e104efb708db3467cdd9045cd60ad094aa2af56fdb358bae337a56dfaad4f4ab9e830db9a31c94dae4ec268c91644b0466ab3fc0d652375275a5d22003283c9fddaa02f95d53781b4b4145c1a642325548bfbe706e2362826dc37066233f9a0c4079bf399821809a66a29eb45adaea068e0e3a13d68de3fb3f40e8f7d9094dc541608334be9e320db39d823e0ac2d59d97dfc75459d69dfa76665cb126d695fbd1862eb6d87b5c4bba4900ab0972058d9c738f7dce54e5a23af896b6996886056fa8f9dba502da1a2f0516ac17adf8a35f7f73e0ec478808a80f38da300fa01ee66168a2690f618cf44daccb00d3a749d7c237863232e4de1c859dccc2fdc55bfa138392d84d6d17b4d3932dd4496c4dbb2d67ed4b13e37c0989f1384d95287ed8256510ce8a91b9216f3539f1d3a813d35321f9f98cf202074bde8815bbd2f4ce0a2a1612ee0962eb20a25120dbd02699c461af0aa0d93f8d248b99d444ea9eb471cf272e08448e2547f09ae9095eee387ba5db06bb27d0da6f756a35c393b84284a69ff3316298797b5071dacdbb3ec9c52c8bc6154eeb278e3776887bcec0a2ba4c9efe05a94f37ef89931b1f2623b321ef8aad21c5c157afd873833d3e4e7e379dbca470f2323c9a9993314268b6e6fc1d1359c0f0bd0f41c6df2e1750c72d1ebb9cada386d78c528923e70e96e2123cd3d44696c446e5405bd8492edad9cad97a93e26d2ea4859e35d932a799c4dc81474ab6837215dd8e39556509aea78b131938e01dcba1ae1024af2dbf5b708e04c01dfcb6e829325ba02fd26af4929ed30bb63f03cf3433d99f8348d77364684c9bdb52006d87ec08510a10d1bc704bd58371c362e26f4f0763caecefaf97a104023645ddcf17be89318947d94f111d255c1d970a6b0ce1ce803218e15fdc7a84e6cfdb0729cb5048940f658857abb8abf70c631b56ac4867d106572efe443a87446c3e811e786c2cd69ef9bb7ba4f952c1e325db2140cc8420d5e7e227d6387db5f7d05c1775c7a16350c7a55e62fa8b6cc3eace5cb86a9a110cd9e93dc2b825645f11f3013da6c48dcb6246fac2f05a9c3fec5cdfc013d9dc0c1591f37fc3a5415a9df1eb1b13fccab8da7be3020eaf70eac2104aa8737b65620d0b29a0eaa896de0077c00dbcc5d71035e5f58890f7e47cec10bc1a3d806a385c9577a4c1a024f715255ce3c33ef16f9c95255e9cdad1e9e5ab775e93b2e96b6cde10f7ba505636abfb55ed2e4dedde4de52a62465090b4b0c1c0b2cd6c8127a668daca267d6c8427a665121875031e6799565956515e6d957ab714534ae56abd56a5c093dafc615d2f3ea8a1c7265ccf337fb665fcfbe0fca377e4efcf835f1e3f77ddff8093d7fe357f4fc7d43c821438c795675914ad545aaa767576141a41a55a36a54a954aa5125f4ac1a5545cfaa5185f4acea27a97e92f21f5362be71e09be963ea8d6f1c52619e3d8545512a688c1f5333fc984aa566a9a667979c1a5348cf29382e1c37c88fd7a9e979bcf71e753fdef1d6f0e39db31fefbd77bc42cf77bc45cf77bc48cf97061e0d7efae88d79f69c9ebda767f762a3377aa3e7799ed7e48d9ed0b3377a45cfdee8213d7bb22cba2cba1f3f764a9d52a7d4755dbab11bbbb1ebba6eec849ebbb12b7aeec60ee9b99331158ea970ad1fb923ee883be2b8258ee3388ee3f80714ff80f2f1238a0845842242a1ca7846a15028140a85871c82c79867cb316badc562f6eca31ded68476bad1dadd0b31d6dd1b31d2dd2b365430e6163ccf3d6b4356d738bad7edcc68d881fb771dbb66ddc849eb7712b7adec60de9799375a1cd2eb4991fb5256d495bd29e9e5de3e179d482344dd3b4d9b31272c85462cc57a36a548d6aad354b96eae5d92b0cfec71a34d6a35a8d9ac821788c79a6629ebd6f9e9e7ea1609e9d76e19d09856fa64f28d4cbb3d323fb237df2231de948472af44c475af44c478af44c65259043e018f3dc6296347bc7c6263aeaee9e75d373bff1ecb287282087e021c718f3ec238b51d2374e4f8f3306470e993e4e3c9e736693530eab7ee4a06797231379b1223b33318f2cf4cc6cf4ec9259f62c4672f1f2ecb30bef94c037b3fe646f221150e510dea1e238bf807996e314527a1e2711b33379fa0c3008c61a2f3b357c333d29c929871d0576cbce3cdc2a6266cc9702e2e31ace27384851675d18954b54ecf8f6d5857517c974860c5d545f3f0bfed854c8f87629e6db551f77c7f4a2c78f354d94b1650ad19436be7aeac2aa1760072e8eeab023890674c888c186195464b9c1151ea68f7289916620ea544fbea9e550c4e2741aeb1da7433d9d12c56262133d7c3b6dbf17d656c691348752aa52eaddccc1ebdbb90b6b0b64b446142b424568d1032970c490411b4f64e182cb1a534c99415926b0fc906fdfd490b3e9e5478f8c97a38d90af9ec6d732beba9562e5ab7f4fbe7acba8065fddad7cf517ebc8a425eef8ea5ace9312409421c6072218cc40cca1a20589389294b023688b58fd655fa7dd51e89d2e7249af7c751ed699556881c4d2b35305f9a97a98909fbaa8777a86b2e21bbb975867ec2c3f5d52b1520470333ce518107d6bf13d03e6d30193a4041d01f8eac44fad26a6b7b6f85a417c2313fdf4151d5fec03a2c5c72222cb4f670df1714d7c507e3ac7c4f425bdd3333a6b90f8250e9dea25a0505d857076c5277fecd94f303deb2699301f1029be913e90283e192abe91becc956fb4cf32b1a743e63ca2a993369d8c0d5e325f64c054a7355a1f1a32b396619a6d9ecc1994676c6a932763c6a7d45a72a63a43793269709e0c0de8f892a941e779326a7ce34b469d3e2d427999b4bb9b524abbbbf98610eef8661e12fcb142c9ae55ab55ab95524a65efccaf8b007fa0e6777733336b61598437a06ddba62829d257a197e3cd4bb72af44e8542deb0bbb75a150a0148286472a70e852d01fc58a1f896aad2f374ebb5d5a24eb3eef0d4c95a16a953efd4a1a64eecdcd505981494ffb4880a99c4205ad4cd4f2aea939f5e87a6af889e6bd17335ea9bbad5d033513bd8d86c9962b3856e7982864eda8c75f6d3e5174d2fb77881839cbddcf28593ef2159a4651267fa6ca4be71e3f357ef7061f0b0a34b4c9c9ed33bd36905292dc290c1504c9c2c9b44bdd3607809834b4c9c3e8b44c6238b0c3c724c88b514049898389d897869e9a98967b3b9c44ed3a7d13c9a8df4d37da62b91620f75c77a787a678a529c8cc4483d14fb39a367f448d9b66d613841a30739a86802a3a8034b7018518710321c7d09238445181c7e23e137b6f2dbb659590f0a2e084389f61ed6ab63fa9c93e7cc82075f9ec3f1cbd1828a75a87bd2ee66669e73cea0eec2f18daf6f179243c408eb4806b2c637db1bcacba42b825ec8cb242d63bced9d1ff2a6bd4575f8c657f5f69cded1c4eea42baabc4cba228927c0cb242d48fc8d2cc29b46c314a9c89b9f196b5108bffac5aff6975b828a5e7289b9b174d2a64bb9e1752ecfe3fc35e45f0d11d6914959c4fcd6311e7e9cf96d5bb262891fdd8aa01f85fcf672f3b86e765dd779ce71389c77e0880259c7de756ebf43f5300a64cf79d7c92bfbcee7d07717e6b2efd1a5ef9cef09b3167de75d73cfee6e2f35b6e0a5c4e9bc03d9b99612472eb5d14922946bd5088119a99ab6e4ab1643cd5038859c03270e3afc16f6f0f08b4e91fa1c6a0d519f4114e542b9fccf013e3f19508175504e1d042db0ce8b097398a795521ae65895fb135ff51a7e51ab39aaa26a481d067e3047fbb1c4277f7cbd84b04ef5be30cd3b6dbb9badce30f0d7568d771a38bf360cf26a2919be2944c43a46aca34d1c86a250aa859e4dc1f11d7166749b4fff92b080f9cf59deb1c717769def0929964bda52f6ab70a4ff39ab8b9ede5141ce687c6c61e6592e5d8599cfe93d42e35f38b29c66e5dbaaba8ad5e51ba55bf7bcbd8705ce2f61dbbceb407f19d09f03fda584b12c4d38daef0bc7efef95f1a09f710ef3d3cba73c090b974fb92a094b95a771f66de33c568ac655e0a8f2158d6f2dfcc702c799135c98639ec6ddde08ccb07cd607b2c00aafcaeb45325de53420cb7bfa822dccb854a10929671628df7e2d7fe5fcd6811e38b27b2abf1eab8b6ff346e9f04956385a7a91a45ce55dca039fbecd5960a7f1917dc6db59bef90c288fb0d3841fe8bf5aad9c5ed88a05fadf0868ce7216e8358b15c63e16cb43c2aef2bed4bb1baa40d711957fae920947a09fcf9a301affc0912634414a9c957fded32bd0f31950c63fc9afd07584e52967adae5b4ff5b40a4c85a035410b333f27a7a66faa79ab3095cca10c2d73cbdbe2d37ef44711f18db24912cdf08b8675885f2c71b8d690490c221f31e744a307732aa16952e4e41ce1be91a01aa55650d0bee5e85bae6ada6b60fd0d64f9d2beb2e0bbdb3814ca8a9bca7df599826f9f1bc8ff84a6d918a594861b08bf7d4a9c1a7444efd03429bea9c9a8d391bc2c177cd3e5fc2c659d9902219f2f9d1ed51885e2a70ca7b387f468f6499f4e2bedd07e8dd5e1e455d3e5653d814d253be79ce11215d3c7acfa40e2e3576dd58c2f9e3961d37dcc3e2d9c204dc1a03d4d81f6348a39b99b525aab45a12eac88e3b8aef3bc7b2f2c75612a55f5efe9a998d6cab2eeebabb669329e36a3d1d0f4e8317bccccd0f8a0f1e1c3c78c0ccb878fd5e7c3870f958fd4f5e1c3c725b2613eae3ee79cb376adb5d63ab260aed17db3270e3a596b2e4c5e5bb5cd56d048e2709df7dd6b6994a72e2591c4a9201418f5ef9e40bf0e499c0ab6565e0559b5ba0f23beea3d6dbdd595b9deb7d519af823417d6d1781544caf2d47b5c5815f6e163faa829d711ad8663059eba27d3c39bf1e1d1dc164dabd59a9161b55aabafd56aa95aa9db6ab504d00227e3641c1b15c53c39668e67a7c4711cc7711cc709597ace3b97a9eb5dea3d2fa0e49438e75c72dec31c67250ee75ad882b4fc6213b49e85b6592a53ad27f70c4de0fd0ce7cca3ebae7cd3a5d7499b518bfd0401bfe65441e2ccea3dcc4f73693a13d36cd6349da6874a5f7c238779622789d3cdf41d018ef1ab9f7ea4422dd65efa468e7d07b19016eb2c9db659ef9b73935aadb5875f1c525f7a16a473385a203b1b899999970022bd72872c4816ee033a64a17a7b7d000b1d7afec3ca37c3d9431145d82fbe96384dbf289de48ebdfbe6643ea27d8756e23491ee580eada99a56d3f22df3a87201a71257af09afafecde160f9c4c77caacad32a52efce2b9545f7f3d50f6cde633655a64368cc9b40e9cb2a93497fa86697e91c9a6923699a64ce2b04c268bc8cd166166e76e59b1292559e400337e5114c7633b275cb7f39c7bce63ede7751c38679f5bea73d6ba9da7f3349c14de3580b5dfd76ab9bf667db389b9447efe9c499c2abee9e34c79292559446e495def761ccade7bef76af562fedcb778e909a338933c66cb68ab5a6484384d68b860650a6c9a997320a5235cdda78dda86dce4bc93e2fe99bced34ac9946c14df74bff24347a3f222e967a176d31e9d100daba8e3aa7032783016e9d9ef2ed64d4f86ae8f5d4a248e7419c6cb3166ce747d12473af7f75dadfaf65bc87957ea57754aed6982da5f8636397cd281beef11fe9102d49b606a890f1ce7dbefa6c7f665c71e338536415fcbbe93cd1cc74db9a55c4736259ab679fc6192d2d3c3c026a45e8654e2a0784ab03e099eb429ceca3741d79438d2e783cf73d2969a4c964f7a27dbe329ef96dfb4e8a48d521273ce2d9ca6ec223b9652fa09f3a5d7d2d56d64168bc564a03fbd04d691495a88f9afcb4f3eba62b6b1050e3f1af92995587eac33cff82965e842425ffa09f4e5943772b2ec9409fd1cce9e9a16cde88b6f1513431bc325073694e1c5a6f4ad6255cac6447dbaa262f1d91f5b461f855299f82c5105e3e38ade98c126c416c4b78adde063c140cb678fea179f6a6933a24b3e4a94a4f83167b6a5d336df643d286c2f9394a6fc98d332384f6572dbe2517afad6ca7ccedeba357adb7a2a9de8d38fda1cfe29336b9008e8991c325df61a4e5e021151110329b660c3488e178852481334107a7242062836489f45d38815860758987060861148375c88949863075a909881384ed91746981962d0658a229ac4712ebd74d93b38522819b1431833d47012c7c964c68f73c6f492cb906ea56cc91c89825441fa0c9245e84b67b28225d29548ef91fe92fec9a3582ce6830c23b3c1d63ed3721dbe7d7c0979225bbadb5520c2e35bbe41e3a30c1333e3bb470be1d0c3c70985e8db67910e627c1bb58fb34a4f2edf3dbb7cb7baa3a0e1db1b888f33ccf753146c7c07b1100e50be653b171df1cd49f0946faef2cd59be9d653fc4be99cbf7522c16cb411cb9cbb3976f9e1dc1c53783f96e7ae3dbe52387f97e3ac28777b9cb49605764fc2a88af562e5fadbad0e286176c29e30533f041bc63883150a0f1c315316ec0e35709f871e65d22fc58f3aea238dee5721901c4bb8c10e35dbec285b93c4890204182f8082e2c888be0c2563c355074f0351e820babd981e96b1de32be8d573869e7cb5d54bada18f12cc8f328c183f87261400f838a34ca321a739258c1fa7d25cb2a9d5e74cca575575595d4101f091711822e2810e9a1f394a902c21f8c85c58e9abf3520f4e7c652fac53e3d5398c007c643131a11ede70fdd83880f1631719b1ce07535e3e761524d994d68fcda599be7acf1acc8d8ffd463b7df57e4ae3477539d2218f22828f3409a3a31bbc1f6995a51fa9d257a74bac338257a75e84f848bf54a74d5dccfc48dff8e2c71afbea55a877ac58bde2f858a1107df55a04458daf3589af5ec85867005ebd72a9437cac5dbed6a5af54e6a882c41847c880431ae27581173dd400872e74a0e1460d3954b9c11c398c1147290b811b32440963071b8e98a3b214335419a3c505691051dbd0e109283448b1832e62d40d04ed50831769c0c08921b608838731547a108a8138cee95464c44c3c9364204501861051e430c306e2d84317788206279098ee88630a716ca256ea91c6be3a1502ea21091e7020068f1e28d183385224b9032a7470c50a14258c8881385219d05118724859830b29d4c8421ca9d3521c413cd8c11b76e0e08a38d2a7af3970238c39644046102e88a2fed0033ad4f8d284143371ac475f87f81007c1850d19c00006308001f8072e6c00aef3385082781cf7c085e108f1c18e17e21db83021238c30c20823b80a173682085c5e04e7c08589e0eeeebe810bf31b1f7cf81bd7c085ddd823f230c3873bb22c71460f4000a18b07610a182090e89294850e5d8820f8e0e441f00c5c1808af1eb4fccb3170612ffbc107defac0ed071ff8052eec0301f420f402700b5c98006a6a6a6a6a5cc985d524e043e0e1ca87e015b8b01082040912248827b9b0201e80a13c3e004e810b0b80cb5b2eb72e31eff29f0b73b9cd196fe313b8301b000c0df10070095c1800402b5e76f0860e6338c1c3e32e40423c6909c389063be89285b81ae2e1579ec285adc21ddaf8d07d2e2cb4e083533ce811b830100217562b93144b7ffddeeb364258a7c6afe754c1e16f8f2b837b3d03f7837befbd2ebf17847befbd41fc5ebff7de7befbd55d67877f7075c98db2041820409e20eb8b020205439e241702417068275b95c2e9737e0c25c38ff811435f80ffcc8857db05aad56abd56ab55a390a17b60ae36b3c8faff1132eac863ec183af6e02ecc26e14443378cddd868a9a13e189e288d78cc8e095644082594285b682122589d7a208e335194d89755caef9eca27979edcb6b5d5431c30f3898a28e2e5088e3f7151883a8075a4011a58c2822d00f76d45144105958a165898c20bac883083ce4f0421ca52c36915c51747997cbe5f2122eccb5f2d6caed2a0a297ee50cb8b0d5bdf7de7befbd2e5ed8ed5a5b16c50f51f4f0d5bfe7c2b4993f10286cf0407c011706e4c78f1f3f7e78ce8f1f4ec6ff802206afe5bce60ab830ed6b59288af8ea09b8b0ea462eec074fcb0548df72045c58ebc99467b1582cb761790ecb054a3c8bc5056be52c96c662b19c25c293284bdcc1041e36c8c1fad2c41552f030431d5a583b3fce3c510ae1890e5ef32217a6b5be5fadbcb572bb7ab2e5577e800b5b39162a2192c88f44be56b74e78f0d5bf2c5fbde5af1ca1a7af4eac7c759e0bab3c405894e0828d2b3ec4918497abf4370f21ee10a3498d1cd40146bc4ea0f8eb3b17765de7c2583e74a8e27db8012ecc879df1d6cccccc8ce7ccb89df97e4687267e26873b5e23c06b4ee4c2b45a6bad5e800bab4e800b9be1e95164c3f770122eac879d79992d5ec69d753497711b1919cfe19d392463833cac783247173680618628e3b2773a2c45fc00851b7128f184387e2fe3728e97f1193182fc288408183f1ab9f951728982c557af51be0c6185081b8830e3abe710f1f4d57b8ae0e2abe75c5855e21f6d5e93a9f150949658434a93961b3c11a55cd2e235f72baf794f14a4d79460d9c1172ba0d0428a2aa216e5e8351f7261da6c001726f3e569888c781ac7b9301a207c784ab3b84edd46480e0522cbd314e54257940661c45319ca4994e554c6c719c588a887a773ca532c7e9c4a5d28651ad3f082081db64822cb90486de0822cb228a38c2494a8439452098916317964b121cc0c35883518a28a1ad040071959d0a52eb63851258934ae884e7ed072061a72d810451c27521056fccc183fe3422e6cc656973227082a46b8b0fac5cb0011c6cbb808172663991421c41077609932a3228edfb3585b9ee5b677540047163c7061a4a58e2e224b06cff29b0b63f9ca251047fcca5f17b6b29a46b51fa2780d88d80f41bce602b830ed066f3c619aa531e6872571fcfe5ed95fb7bd83c21bb228716061a20739c41f74f8eb2178002e8cce64ff5521c37f6e73619f9dc2caa752de4ab9bf52292a823e2593e22b52ae2403a99a7299a2c2e953ab94969498fa144da5dca652a1103c6043d6c4a4c4450a074e0ce1c6952d339821c58225de888244110e539018810675208da13247186988e38c7dcaa9cb209e52ea1f15b2a70e800ba3ad2d92063fd6dc4cd1069294af5358f1d5c30babfe99f19a5bcdc10bd300f0778a1efeba5fd80d726129a5575dd1e2550ec285a9acd4e03dcf5bacc356bc277afe1292c333e63d239ee719b89ee74baebc17e63d973e8758074aca9b45efc9d1431732a41031c35312a2e7b277209003142a519498b2040cc4f193792e47f9f4de151c3ee5947544f8947f7061a924ccb8438b1b88a484e1248e5f96a73eba78a2d4f404a6072d449a031d4fbde6c2e88fbf6dfc75d7855d357010031de461061e5bc4f1c322812ba06003065e3c216208b1ba0ca2ca5707626305cb6bfee3c2b40bf38c06f09d4b2bdfb98f0bebb82a9e737f719ec3398f104c3c67e4392a5e49ef5891e3b8de914e6ec8e0258c114a349821722f9081164e3838030733c4f17b4e45c30c585cd0841c46e4605af14116c711b320c488a3547ac10e9e4b128206013cb5ded799af2fa0f282275f9de6c26aebc238a78775acc1f0abbd4361e479ebd36edee058c31678befad64dfcdac29e61d1851cd2c59867d6126b8915e6d9595b9c58236b64cda71f592c56bcc43a3e5e3a0ad4bef08d06002cdf883262424de19d8ed2441b1c9fd744543e5e629d564f4e49efc8aad1496f9fdddd526a3274fff6d955869fa46de58ddddaca9774465dd1ddddd429a5a82bbe294fd8be86b4fa4829eac5af2913e59b462f6537734bd4a2388b02c3f518a72d16ce39eb0c9db8977076055fa5bc69261dca231cc771af9695d694389bf36f9443592bdf74dea46cda541a061f534ab7ccc9cd1a8aeb505c0e3ad5137b3b594b2b7de25826a8f7c8276fa833656debbaae7bb5aeb4b4704a1ceaf43b0e75e56ba0ee18134d5437e6be2881a5ac996a9b95b2991b4c4dfc62ea44deec2049e056a23137a40ea54189893ab1e3353ae40e17e386b41e703138b89885028401362774076d0cf5d80b51a1ded19eeaa73df58e14354b63aca339f18bd21da813b9f3b2395ec56938352759444e21fb556928bcd34a7dc3acd3ce94c46c4a5315a72c4f5c688ce669ac773427271ad39c74f88c68a829282e7cc3dec50bd3971998a6379cc23c694e34d6214a56c4375d739238ec9a93e6c419c59e5deb36c22f1418dfc8f3cc4facd3c3a2dc062b723e7e2df4525b7a4643bed847fa9a52dfb08ec66487ec319204764da93526d669270d0da9d374c8576b4a6d8713794383dcd1663d9dfe86e719bfd87d347dd383826c7051ce028c34e8d96dc8a1361860db829063e4b6c3b3f35684cccab89f7a6713ea1bb6587ce316b349edc43a5b6cbbf68acf3bcde3c01e237738676f3be4ce4b4a0bb5a56a28250e779168db73a1bd52e414df74eeed22d1bc6bd51a07de69590b35922c525d09ca5214a6247a36a59baab413cdb753ef6cb12d1633fa729c90f50eca0bdfb07f5102b3f4c6b3a3c23ca3c43cfbd28f5bec699e86a8257eb1cb08f14d1fb798564d9892531acf98c61d139db41999bd98f6d25f124894fefa6e9289f43debb6d45d47aab373371fd9a64b7d44c72f6dc62f8b6f7a0a28096cae3541b96db2390a74fd585408ba7eb42de47003b56ff27cd2bde0c5464d06ca9771a368da2afb8975a6d621955fdfc824165cd2d2476b7d7cfd289d7684d87438d6fc6859a9e6391c679ec39ad148170af9d148121e094c9106d598312e28217af6d792126da86fd89b5c3fd7554c0d2402894992489fdbb66f3cefbc50fea8dc0312ab2ab47da3725528fba676d3d84ccfde299a0a6f088ed5c8e989e83dd0d5d5a27e72fda0dc732f64022476a0eb876342650e2452e750a0ed1b39957a877ebdd4fabcd54a4aadd43b525c629a353df71310213e1f5b63ce038daf3f7ce32c42c2c587a58781bfad4fb7a18b7fd4f1793fc91bf6229659f04089affdc4fdbdd464a93022d8e0535df00ce16592972cdf6111c6c6cc5aa55c020a0ef0b28690974963e4f0dfcba4a528c6906748b7e10ba9bd944e9eb8c1bbe0461e57ee7882450e99f8b14b76cbde6ac1280f222f578801134664671faa68e3790e26ce88411173cc40658cd8012aca98010b2743d431061b930a76d9066563f467369e5dd681652604135c7460c41d225014789cb114c41330f0e248649e37e8a40d9897eef38394a32e5ae8c052244a252fb6b0516486165f441f3a7cd3a5b561362103a22744066dbe10996886318610196c37da106717647891f10e557a30db41638a8c090f972d48620cd4872251b201464f93db8e96219e440665941c266fd5d868f9645217a51f5b5d907eb4ad1ab771f9d898d839c781414256ecba90730ba2e49202bcbc48e40d5e8634167349ab7175a22bd2de3e4a29a56c29a5949dd4c5e847dba5e8d9bd99bfe21b6dd20c84969266c04441212ba2a8942eaf09d2694be9322f650fcf58cc8668c8f66ddb361f79369f5bc8dbb655efe131c2d3ce63a447ab3c467a3acce1d7ab7946c9c4d453023b537777f3f058cb9344c7163c3c4474d2665439bdd63b4da5504b1f5711c9223aaf6992e8b539a4390acc37b7effbe197bd301f353e5904e5089073c898d343ce9bd44b9773c7e78df62c1f2ec52efcf19c498f954b911f056e2f2fcce5c3ead13913ea9e77613b8b03e756c2ca87cbba12d1035d36f441a187bd636fde0dfc9cfcbcae1452a99feb2e1bba52f09ce53f9eb3422654ac33f97a844c54ce7220be301790ccb72e0129f6f00d64272faf4f0fef61aec766391fcf2520452f6c42c57a13ea9e27e92f6ef8304664854dda57dec32cffa1ce0a5d9cb35c8a3fed2e2e74a5e0c3599ec487b3c21ede44c67d78921e4ee3346113d606724ebe6381ae175c40327f73094891c65f8071ee43e32e1f197749408a32ee4ac27296a368409fbed19c8ba239ca27c887f7706d3ee35294015d5be84a41c6693c09cb67c22633ce729ab049131a977129f6f03143c39291a2dbf0a77de51bb8fda6b992bed13e507b8af900f1bd9c5f9aa3dc950275cf7fa87ba1cbba147fda5d36b4e207e486cf7f744dde686eb5fb528b198e5e2e7d10f5769507a4f17da3724e37f56ee7f1d79cbbbb793630678244fce511159e6e0ee610a1e18b5f348d6f944b3e3f4a5887fa53676fa23e279d838c9c0ffa3997be71caa64c16e19fbe42ef7010174c2889d397740b2e7609ae80caf2c9b98294692fb318e3f41285deb152494909cbc71f64c302c4ea4253dcdc670d99006dcee216326182049437626275795d3e938b4bfa1251035d32f459a16fa68b4080f8a612bfa6f3745415dff89ab269ce170cbc23c5e9720649313a9a0d7848038e974ee58e64c30a931359c0008c34964429a55b2ba59d338dc9d9b7988d611299a0a8cc301363904954dc1007932d0d31869926c40e251b4066b1c31046a22463880a9128c1402ae3489c3f5c4942486430a78ad86955d350a9114896c18617422283354fc41878ce26727cfcdea3d5ebba177cf34e05a82d94e205a5b8791e48c11b8627583d757a8f7cae14527e5d49ea7ab6fb822e27765d6f52f40ae07920951e433d908a1562853cbb10b70980666fe77920159ac3f1e64417af5e277aa014eb064ef9aa2da8ff42a7b5cf23a86fb17a5d05bd505bfd09fec8eadb95c275cf955caf43510f37d07e419ecbbee13497a294246c6ea9143b508a1c38b6340ad68ba4006fbd93b36f0a15a680bf9b56d3b46eb69ad69de54dabb4db515cc1c8e9ae9d5f546cf085cdda90c5972b05945b57820a95c810c47c2ec827d9f4402290c81b18b4f90706590f81079f0bf2498a2e4960a7eee2064a1149017eebdf5c7377e67addab0d3eb4369fcaf4f6cef28b8ef1516b9b6ad5c371066924b0f7f3452253696b9aadd6da89804ed13005ac75876d0695ed087dcebbea94e3252813d53bda8552de4c7991a8e02beaa9cf7bc43e0bdd759b7ca0ef340fd5a1dc8539a524e16b3701689322ca5e2432f7013d664c5fd4131f7d2a1ddd9cbd631433739d953c2d4dd52f3ad93b5428edd843815434a7e00b47e894af5ad9677d067fc2296f6af5c9a8672542545ce079165e0f24a2da5d9863506010ca6da0e202cfc3d024681b3bd0f3fc31068a44e637efb8a66c9802fecd8fd0586ca39452a93e9b680926ba4a59a7253dc25e9d851a7ea1a5b2887c0ea293369a77ec017d57bd6a43df2692167c3d429f05cddb3bcd3b4243efa29e609d6d86de453df1f1576bbda2bc0acab7d66a9ab78ff3042a535c98d75e8347682cf6ed1ddafe17e65b5984f603e698a7a1f6c2fc517b764d16a1ceae55af53c0b3998655e2b0cb0babcf5e99983e6ef8d0f8e93edd882c227f7a77f3f8491e5b764eef243aa2b8b70c949b522a2913bfa47603173950e3d9bb10ee6867a425fc9a9399999596f06b6a9aa669cc9aac21bd70f4ab036742ea7d352e81acf1796fd1499b71356533da8306937b01d67e5fabd592d254014d04a09981d604e0fc9cf14be60655f3544544fe334a49d136af8c4f71f108f83bab5ac7b9cba61c39e4ab775d05877cd75e05074029ce2f49408a96ca2aada010ad82236c4b22bc0672e12eec85bf282da98a8a023dbc89cd6c45e7b2486db572523e0620e5a30086bc747fdd489dcea5835201a930086539b7c1451ab63163c688381201ecb60367535a7973964336ef40185a3f254e0bc6fa9b6bf7487d1e798ce1b9db3a945ac7a8efa62095435a30d623f5e9c8630cd784798fb80e9851b02e6d46d5515354dde4e7f304f9766ee07c19a36f25bf14932c42e4f9c6351de85f90adbe6109c62fd77d28518252fcbe237a65f294dc49a9923547eea424cf21626338fd94524a29c1f87cc3cb49a9ec9c74ce5a86a4298f436d9e026ddf80afbe9928295a1028c6cd6885f9b43400cfd4b8b398edb2889c911247fbba85339c6fad3c7bc733d5d99b793a10193e1a4aef1ca813797a3da52e7b897ee92d253ba5ddb576d76e0e5f128bac543addd13e56ec1bbf9916acbae8f730289f3ee9ce45523695524a29e5cc1a45f2d2e06bbd6c88788df49f955d8235778d6f6c79ab465a19a2f0f8a48f9693503f5f4e39795afff8d5b261ab4af985f44fa29e8b7dd27b88ae932830260a8c6fac432f5da2642ffd456bdca6865fb5ce3adde79c944e3aa90d8335fcaaf9295bd4076ee89b546b85a928dffc6652437cd37b58da3a660d7ed8f1bdbe5d4e418104216d886c39dbb66d1ba5b3e9a473ced934698e359a80e4f0b553275202bf78db362943e1fb50e057093d25a0407b4a40812767a602085668a147deb07ffd7d3bf896b25a2402c57a7a478a39763a6ddfb6cdaaf059cf7ed47adf14e217cfd884c23a44338a534a2955a177ac9d71d1944da497ce73671ecd9b76c90ccea53902bdd4b1afbee956dff44cbd8cd137a1903b521ec922f49b88a5620b174f27a5acc32c0c9d3de79c14b4de17f6c2b6d51b6413890beb2c9551e57bca9cf48d91e1c38f53698ca71fa717a69f01d356e9a4946e33cc800a2008261020be4a435415dfb7e4c6a669afb18544a75eeaa85e532a499d2ae5ceb793f2e93972c7f3e93d72875bfaf9f2cea5efe159ea821c8ae97b2dd4be86adbe611a7ea14555618136ee26ea80d2c3c343354a6d0f8f111e1e233d3c467a725ed3f24826233c3d3c467a787a787a8cf0f018e9e961070f4f0ceab8224c140a856e73d639e79cb3565a69a594d2979c51cdc7e934e4e9e199618f117ef14b09927eca1a338799a2cd524a28990d3f9bfa55121d4c14ca101d4d733851a58e21baa3a69452ea4d29a5b4677777cf6eb64f96afe9637b0ebf783a8c7d634e0e111e97f6a8efe19c9c1c995487949c1ca53039bcaccb1a8bc56246a27cedb8d71ebe5966653ef5e936168bc564a2740a768ecb64cf76776fa64194d2ee1f2f93e87082c78da43a6a087383eea8e99c734eef39e79cdddddd19a04e698f974961c000630e2c73abdba64d6d6ab3ce39bf7c616e66ae49df2c9b53cb6161f3deeab6693db5d6ba76f7f422bd24d151c315375032942c073c7938ccd1c19cd385f2d79a7ea1fdc29c1cd80c72eae1e1b16cd9f6f0308f911e233dcd94995700410b4c4a989b999d64d289a51023fc929ae491da9cdad498c7088fec611e233d3933155867e45901042d289993e79c3c4e336a7dbc400796c812c161adb53ee658afd65a6b6bd7aeb5d666cacc44d8a777989b99e915765a570de861c62f69436464d7bc7dd4b41f5d3e764d9b52d3c2b13a4fce9c92d2e94478fc39f434be1c261ed69973bacf9c3c93e6488292c13af878f8c5eec6e73f7eae762017e7f2511405d23a3eb75f48c3b185cac10744c72ae613b8b969a59456add65ab57b82a6699ab6dd13b66fef549d15df488fbeb342f6500fb1ec9d9c669115f63e607b17e61839b969d5b69185ef50ba56834ad6d5a09f5e65903ab6c6c219cdd36edacd15f18b2ba595be60028c76b709dc515889f875276c3a1034be5185f4ec1344357d5c1d3a41e8c73a748214eb5234efa8a7dd9e95d28a42fd5887bebb5b85c42fe68ec24a2471787ac81985ae9f16379f2f650b99c440f48d9cd1f3c8190d1dbdaacaf3a882e2d939a5c6ec6856452e0823956c063a296dcff64e8aa3da92950389d457d03b2afffc47e51f132a5bf8f37d9e72262a955f071235bb799f0a742e4485e1166a75057d43b5bb0405e69329a7a23993feb9cea4863c620f755ac3ad897a61e057efdc95d01215922ca2f3579b2bdf7c49efb8a8f30a7ac74543570a2c4f7992760e9b5c6f4f757b9395b34297e75264b914ad734e3dd05fde855c6843a7edaeee12941a5f75f9d759ee4ae1faca7f6ee84279fbf555e8a23facd0b5b90b15feb4df1ff695b3afdc45431e7e69371acf6beed33b4be48de6d6babcae2d4ca1fdfa05da6fe8da2ec0be0a597e3d09cb6fb8f22629677992957ffe854deee72fc050eef3b9cb2795725792ebd7b525a065f9ca7bf8ba2a05baaaa7fcf324d7556113955fffc226547e547ebdc9e7a91f56116b89121f7e491b6de3ee86ba6d9bad16942fb7cd51edd693b0a3c22661132a4dac7717cabeb93e5fdfb0cfeb135483f5f61ae675f974e8a2de0e24862e1a5267d23f282695855e52272f9d9badb7ff58ef900995cd99843f28e790093bcab539cab5b975ad7e7d537d037d2c0804886f0515f855a914433201422eb68f2ba11cb17d945140fb785f7652d885acf8fdbc27a444f6d94d224e8a750d5c0114b24136f0881dcabe11b2a20d95f40d9531125a82b402a7f143a1c2570f9883025de283817546bb843ab9349716ba52e866666f82f2762952ef2ce87a417b0156ddc76e42966c0cba345f81d8a04b0b7d96f48d68c19c97d38b7e439437273a69d3dea12b85ce374fc2b91636d19cf32d6cd264f3cea5e8f2d9367725d190cc9f2e01295a9f4c7df23461fa589f96faf4096ab71be872a5b039ca93686ec326b609ca3797a21441c02ff00351a04645cdf60d5d41057ed1d94b27665aa59336a81a703e9d08d8e44b97ae1450de9e84ebb009951f76ce9b88e3a7f48dadf75cf356ef74ae75a1f78dd6b2c1d7613bca396cc2cef9f4b1d58ef29f0e5dd53d1afe70bbab863f5ed8ae9fce36b7c60703bf34ef61ce51a0cb863eadbed17c054b5ab1e6c642c5f6947a6773ea4a7a5e9f2d74f95477fd682f65731f59646a4e699d3246df0aed435d49ef58a7f4f57593e5ec94146bc3af6f5e600e58412ac3c4377f58c767b440b824bea972ea309373c9de7992e95cd8c4f3e95dd884ca4f13d9f2e9b94b7ae74062fb0abde392a12b05f6e93f33644245863fd3bb9089e7b37326d339745197223b90282dd8766bd933977418e6bbaaff74f39b50a1de84bdf32654b4f087f3e94d3a97a1ab8629b077fec3de8534fc99ce79132ecce99bd9c79e33b52f6b501ad494cebee95c9f50439aa0d6982cd21ea3471287c6060dd618100df5190a3dbb3c92453e38e1831743f632290c2e4fc2cba430861e48f715499c761a363ee664e0966491e93d9d9341b52471bacaa08a491c0e006e7c334b5d65f8765b978a6cd1b7576e4905c6b76fdcd2f75865a8315944b33fbaebc84feab110eef86aecb9c624ce0ce9114d91165fa5146c218923bdb353d65a6b9589d23189436bad26f4f89e321c992cbd448dd1472f293dc1c7777d01ddb6cda2ac45d9102571d856b1d6365d1368de86dd930c11dc949cf2cd844d4eeef11feb6cdebe69d7848ae226c7cd392d131fbd957eb1f99c73ce39e79c73ce39e93629cd011da2569f53abb4d269ed9cd3fec044b56578534816b13bad2f0e86b943a9f459fb7dadd657df78f628dd7e84df8652e2ccfa06ef68454dac53dd28cc51158314f49a4cc34153d2a0684b5a14262290e635a6de41191915f54e27866fd8838cdec3413bf2a068485e144de625a1297953b4a5ae61f754f44d1f51461247eb1edfb5bda3eda8244c57a5514d2ae566044000001315003028140e88c442b1509886ba5a7d14800f869844644c9b4ac45196c31832c818630c01010100100090c18409027dad87020609f18f77443826a562cc59149cab18c8c59d57454730dace730e2d55618e989bac3ae9efd0c08857d41878c379ef3c28fb8162b1b5a99d276625802265a46054331d5c275bb1eec448f5615148e0bea79bef3bc0c62b3bac51699d0ad8a16c5e45b84a2f5e307da5ff1a7b4f108b4a19a2b2cfb0b85e29fb62a0bbaf1e18152228cb891c31b251abc47d9888c3301bb9a4de6b119b042aba430d274108087b0360a3a69ca83d88a8f49b3c62c59be7b60046c8fad07e96481c5961738695b49d270c014ba39ed12493d9b38ac9a6cf71697c822d251ceaa4e2a9573dc7e4452b96a6bc81373634221f455be591fcba64e99f3d28f5609688822e9d5c27c5459dfbcaf868a3fa935780e1de41467d86f582d7cceae5c18e382e535d6f78846b96c859602384aae4355e0b0bd625727c7b2374909e8ef3c2a59008c0dd97ff4fd33b0ed0c652f9d1279f78e68210a488215d1c2a874b25d266aabdd4686d1ee260978db902a58bf5cfb9492d3307234f908f53f291c81489a1a61ec2e04dbf5d26ac24008c74611e2bf9be353295dc129e480041390ea911b83ae43ff729344f8878d313714e3d19bf30bc39d7349a0e8cbfd5712aece840a689c0035ff8398ebf0150f1e5c60b7510bbd8560e379c7c87ef3d7323d06e106a3fea22d81271c714aa509ed71432f904a24c1ad3beb76c7b21474e3f8101caa67c7c425b5fdc580a9070d37bee848a9595fd957e0a43376caedb0961a1822993f19ac3823fa0c99113c60162c8dfe06bf90dc445e1c1f7880e3d509706626281ae4700196880409803c110b3102d3ef0dd835fa13519038aa1acca6c6afd16636a56ee01c7a245bba77e8b838e283c510f3873aafe950a71d630729a9739e1db6d382d9abaeeafb4c1594735e3e3753361fe6336e88ff47eb816d86665ac57867e09e6642644608d636c4d99b0668dcb94589d63c2c1d203592f119025f436ce4a7cddeba5d96067c06c44d600209c2caeb6e3745e74b686bfa0e823dad7ba72701cb44d5bf6b0b27fc3e1c03906fac866b7d6f7d91aca9243519e695972a60e1a6675a52997eda2e72943e80de0cd6ef2d0edb3453895d9704ac7f07b3fd7acce5f345284870eff870fd0a31bb257d3009825bc17f05df90936feeb91e8cf4ad1963f6dd6027b4da2cf6e276d9c2897ab07eb51602166d57e02fb03f94b3359adf5e78231742027f1fea7350eed40dcc8d0df108c8f1172a03faa8e8ecf830a17f5f33dbc7e1ec9609b8e25752a19a19f77a6b07e47b4695ba6a40ddb7711fc0796c5aa23fb6de476019f1d0f4ced4e863ae2889720df1dfc1ddbe888653e9e576f98c2fd1c9e0f202acc8c088ab4436985225224aa092efa9656abf1b8f4b89b48ee4bd97fe873c434d577b47677f7051005424d14e8bda187b7dde6c914e1cd940381807a1f483016dae046bb147976ad27372d620f2ff84e6f491d3e464c8ef89f849455aec1978a03c02a7b402f95a6b7fa0e556b0da50a8590eb93f07d645248c6235a40c7cf061dc40bf152d1e6c0d8a0db1aa26bfb7ee280dd9bb32fefd9da61dcd86ef475825c4b79a4ef8d4f89669204a29844afa9c5a88b4b1e74cf390e459cdf5ae8d3bff79fcb984b22c4623218100417ce4cbb73f9bfff2b0c19492feb6c52b064fee55a4f7af46c52466f045015b7e56d61c54caf043d887f4553808b9ca516f3734f642eac5d64a157a0c30bff980a28bda517fea417227417468c78ef40d69e1e9c9030af23a412368737622c78cf6b8b6b3a516e664b42b5faeb4ea53c9ebac00135bfbec2a2cecf7ae556500441a45ab15ec8f1c89e0f1f4f491f60b50d72aa49cd8b3ab7d3991f4355e39d7e6a3943ce320fc3bf8c0eef38c4381a4061b915a81e6a40b3df2f6a89c69485aec90b30263f5548ed0e8c7cd033ef15bf2c9822e56dfd34fd101e36ba0083fb4eb211cb41615524e6373e4204d4fb02dd890ab70b752c32a6e39922bfc97b5e72106644681906687e33cdb11cf74792988c1b5062970f6450f81ec850256ce64c16791bf0e993f17b176fc6c13dedb6ce564de09272125ad5581c42009647af792e2683e4ae581ec54c305ef041c7eb637c3c7e9760841578dd233f74f4658916f6a8c56628fcbd4dd9d9c4ec976d9438a8a659123569a777165feb4b2c79b8a6b7bb89147bd6633cd5b8a83fdefbcb56fb229c537a60ebad0c68f93afc545d426010a6b44e9c3c99ed2e451497b7d550490f87207e22f9eadf99b256e3b5d335b988504a14413ff08f264224ef3134cea398db7f134e03e6fb3e74c4a55a5d71e2aa9fb9ad971f5cb5ba1a822fdf2246bd909292f52ff8bb08d7a33139c60e4e13843b67047c007ce5caa8cf7c118adb3be9a24ecf0543cd11143809cb0510d66d2614d55c169076c8762b6e2eed153653ae1ba79c2af6310b1e1a2f2c59331a31cd389b928d08c3d0040308bd73cc0bfc05023436ed16dbc75e3944dfd847fc7f9183274eb1a3055add9a8bdc42eece1898b203f8007ce3d4f029e3f86c3086e34504b068b593df117d531ad96e11980c4d295a2a7dd9ebc93dcdd0c1aec68c0410b5ea6b7372ab733ae4eee06e0b7ee1cb8d25ae61ce61b2d3952aa39480c13d308bb9fdaf8ad2350c112be64de98f111d90d036ff82a5e9a3ce6a851ea8639eecd12c8aa30cdc91182e4cc3bfafe5e701da28325752adeb766a757be41e47dc9d42f110e300dda9c5e48b91f30a521342ef4644daed33d5c5acccb4230287d8ec52ccef9c9c2946d7381e909b9e293bfc1394ebc7c260218b3052dd024c1e44880379c24ccab3d194686e6803bab29492a5329e258f82ad7e12302805a4123b2ce5f95adce0472027d697d1573e8497755152a0b7c39ac4dccb2b35c59bfd625d56ea59363e9bd7422a2c71860bcb30745d88e0488223db42aaa57ec5bba23f11053aa3088d9d2b8ccbd54d17402cd49a2ba8ad197802c61189cba6053452c1e97ace3a0facd5151daa3c30aea50988dddcb7425d7d70da4e7240b7be4213ae936392f8656a9244a56a85954cc1be62093a08dd72354998d675e39e6669ea07c54334d221e997d4239adf0fe8732d4816cc1a316475814253d8cdc5ceccf4963174d2fdee991d839abba8eef55defeeec4a1b4a687f72b024703e93606879d1d17aef0b3c5f72b90610f4cecb1e10027b76a935344e959a7a560b1996651d76f48315d00dd9ec76cb1151e80cc149179ee56e8070d807f6d9902c25d95163d6600d0243d646d74b0820b8c0d482e8f58d4b6adee81dc6ef582c87e1b92374884c37ee396398c9880c921efe5a1c100dc8da584c132fa4d65361b83dd470eff2920b3fedecc4a4e56728bc9006de921bf6863d290e1c1d80456ddd610bebc658df7b90a38d0ef3e471efecb90e3621294571d481babfd365fe99dac3eafbc8f6aa6d66958cdbed17fe71d01a74a463f6a0fca2aa8c0ab64baad27c165e4cfdc74baa24d9d2aaeba2e32c1b9c33d33c5f7dcee4fe4ff3e31e5ee2484631332173c708fd0c45092865f7f6ce2dfa24ef2d63c25dc525190286a8a3d977f9273a42f7cda4e576d233c5d9b4e66b43430325f683ad3a0573492143db47997d6bbcc9b5e2b4b77513afdcaab3af17fb94d04f4480ec3b4c2519317859c598d9ff6949aea57aaf9952d834e33d8609911ccef4ea2cce0a3cd64d19b3ea9e6c51bb456a7cc7e54e4db82c4b9e9bfa1c83663cef26a4c20829cc4979dda333c7840ba897c3a0c79c3c62aa3d53ffd2eeea96ad2ad19e4f6d766fcf98de7ebf0452472ac6d603631db3aef79718627f1e0a9387e994e9924bd0a9c48f99a53e1368cc9b71fcdab4c07c0c78824f9dd849cc9390de68e61631631ce1d5261b52c2237002e802c1b76c3a510a25903e28cf1d9b80ae941397fedab0c60db252ecb6defd8f3e9564eb34aaced606afb1039fc1b7f3c8a1e5c73cc4dab98def026eea22696c57baf148f76b6e2e0e130e6c7eced7482e89b1eabd46b3fe283f60c6ec0c7c6c03878232a4e3b2acbc167543895c84689b46d839781224a48e21dea7a4224471cc05995b74a2ae2a509ffe986a4c160dd082b3132d73d7f40600ecc38056f64534a48fa22775482b7f1e8d0ffb6ee743da09078adf9471ed190316003201de72e6637078f8c74e2afdf05ba71814f7b178c518319d4618813490250c719b0851d5ae693206a9b7cbd3633baf559329647b123f01a6206572ea62031bb453be8764b1e5ac2580c5129fef8922bce0ecb4581502d3fc8a436c61f9ca7702f64d7d6fba4f1da1eb9cff3b4c6610ffd8f139d4e4975251e73c59ff038c54dd9033e8953974d1ec2ec34a453c08da34f5e5ae6142a9fcad4711e548905b7c6de572c1584934a4cf3ff1fb37a0bac22dfd845693f6a905bbc95da10ffc767cc37bb4e7abdd4a8779b6f1adbf2445d5e4a41777a12475b86358e102e983bc5ae10ab95b5ad8a85c17133f87498f7bc2faeefdc79d070a49770a1f22068d62ddd1ae7ab8e3674340cd2407144fbc05682a9ad4720d7c2dfca01de85acf0a6d8bf5fbeed647558c657ffbfb61b69c91941dbd92fcfc8b5ce67b04752ce76848a7b4c198cc7e7ee3a89e886e70529d5d3b0d0a682d3f5138181d9cdba6ac536eb04bf62e4ec09bfbfab75b64b652a04e35898afa2799588e5e5e9401e4f82ada74be70cc1e5c4be1f836ca8132fe63ad08899a0e9ad2275e769dde5692fb349cf18467312679188923ddd889b4dc45d21b21baed551f9cc26a695dedd45de554bd7df6ebfef61ed0991fa08ed453d5332698bb74a7613ee8f579560c7d9fb8fdb7a697f3c73eed90327b1ae39f4ba63f05bd3cc681141869664a54b38f6c7c7ab86cc0db95985618a43a64daa4dff38b73721a657bbc592d999fc8c7802603cefa087f893094f134b8673aa6f37086c9b18ab4c65bd74c262525a41ff9be556b04cd59fbc668eacbcb588cbe3a3abe78f63625cee2f60e76b5f8c412d5a39f15fd7016b73a5a10b18169e0600d8a9e408831b03e2895e8c704a6c5087515872699c6af3e3044f63ddc7138acb8535c0ec4f60452f29854983f67c5ad442cf97e0fbe15bbffdc4376ed69d218ab80b3e4a1a3580597a83bb5b05dc44d7db8bcbe6411c4e587b22d99f8a402d8bf19306e72ab080693b00138b120ac858d4a0c1d67b1a0987ea486b8536d294506ffd942cabf0830a882b5ad733540de5129e9d2feb6cbbfd453113e6aaa0826324ff6801ca1f8a66e58615e77fbcabfa22a9d3c47b924b11087a944e7de847bab27b571857005f5155a6ae6ad955c02655a87ee885d3de2b081aa6efa958c781f6a5fc378c1b27aa11e293c781d0ad02cb6e9d0ecf4cc071660f53a9982d6b9fa6805b927cd155e34d4b58c391582ab8f5b3e53183cc14a0de88061087747dfcbfbc44722340da0ad450207033edffcfa47c3f8d8aadbb2e67e31e5226e6c7eaa9048b3a556a5178019c53aa21df01c7cb51a6f2895c33a3309047c69821c083139d7975482b258a34ae6801ce946c44cd8ec5ff8b056d692261f09423972579f307970beba2cf40158ea9548857ab4529cc5976d14be17c2cbd60ab90c7ce0b651708ca2422ed904afc1abc969f54bf70d0d068462ae1e7bc96fd807630291586f10749e3ce8b05742424e4726aaf3d82d502e00d67ca678d374b42fde185d2dfccd67a500c282fde6c38758ff50ed64541a5dfd4caa95814d7ccf977214abf620f12ba032f394f1b07c85115a4ead47e539b58477343683383990a17cacd178e8b558e396a7ddd7efe45dd743b7d8b9320a9be569d89493161f47cf681347c76ae16436f5122d1f164896942bdd92102b100f1ba0b1f99a3e75212ef97b162632df79c7969cd2fb548956efc336085a50f1145e5f5a8fb3b0493170d6e7b3d8b54a81b587c678972519ad017139695fb4cacfc03db8f8b18edd8fe95a426085ef1e1293edc68e52b1cab47497b776c9069716f6283132d4d5e0a012ea9c6846dd0f72a3a386cfa364a6ad0bb61ac388a6449b9e9a3a0ad1b5312bd728018204e3dbd8ade3c1dc730a58364cb71914ef2eb8f0f3410286e8b34884518f5ad270e0cb119a9c331a31b382d5179bea7afd69f09f016f5d9afba78ea004fb20b0c5ab88a694dc505e5c8868127a78444360854ae607aafc0af72276277d6fd89d7a28c0f2602685998f41c34c8dedce57dd611052ffcfe2c1ba17c7f0b57da9148f82ee49c0362f994f5bbad3c6158be261fc050b01c916e7a16c0a96807cb06e74950f32314acd487e0a95565f71debc2d5dc41f4a8e6d5554197f0d56bc0f80ea6e2e4bf2350f142f68e5d420c56e4851b0cdd2ed24bd63d0d9dd4415ea40f0de60e2da20b46690f504db5101627581bef3786c68d87ff6e57f8e5580c7a6433ebd614d55be5de97f77979a905b39c486980b37f0ae69579100b242d5e8199db27260df4f6548cde89a1909e43d091d32123ebc10d4922d93605add70c44b4ea28ba7bd3e26cd3d5cc0551a2768f16e222021bc1454d1006537a2cc926c894212a524893d703ba3d372ec4f35857c647b921cb3930d084cf47dcf03e737c5bd9e6c7ce4b5ecfb39f1ca3c5db767c64cc2a0f1e51384ca8b9f0f73803a01129780f6cdb380b4ec48e7c7de8fa196f7bf708aa134fd2f5aa842fcb9179d5ea284b2ac38731f101602a3cf41a7c8e17471416ab8c16b45d3d3caa596531634f597bed881cd2db7f4373cf915fe2bd864d790115f44e3c95d19632538195fc625ea693d8670eb7c362dfb23ffbd79cdccfa20837482a4786fbabcebad1235668ae559b2be460e929ad037609cf6a076ee4228dc0719bd9972bdac7ba0805f518f0ed8c6dfa86584fe7ee1f91c74c1f78d36b298cf1e369e445953606bccab3f1fbdf83b538eb0006ffb82b6711d73b271bd162fb58e5c733c7340fc9dcdc32a9ddbf1392537fa193d8053f4da304fa9ea015c3fefbf3d4d222c61e208a06fe0b095f1b8b82a9ea77872d9f14441275a60060098dd39bb61cc7cac7da512dc00f02438a6c04dd5556368ba0100f6663f6ab52e21d55484d2f6d8290400b84f98e62272e399a44edfd368dc3924d088e7803550f576be239461925533cacf7e6369feb0590a19177fb966bd410d9c82b0d8d2459eda9b04079ae6168835f24af3bda1077d52030f843d1f982eb73a0581373d16c8743ab480baad1a87cf75825cedccbd2ca2c1855c495b19d10ddb0e6af5d07e3d95e00e326a5746b0bad6793b9225d8527c73506485c661d3941ae347fe786f20075ac932337b6a852ec3d81e51944e1da49119f7fd9580d7b609af0780f88aafc44e16f1cd3cd2afaebb0cb6ece8087b4849373ceb5d5a343daac0287f198fcbceeacc7c627b0e4f3e7978ba760f07f004c8c3217fcdbf1143f8329c54008e1940eee85f375532038737f2031195f6b15e099ad48ea75690d1a0e373d126abee85b911f9669e50e948deb9df7d2f6dad1cd481779ee850cf878a0077479029a5673555280f06099153c55e0c6f5f54914d3d75940acd7ac80a33a7c79af8770361a972dd9fb4b36fdec90e5eb0b8a2acc079dba2e1165bb53445150be01a432b1e1b8cc4481522dec096607fedfc19febc9a45039d7550c99d659f787503bb51d05b2a39961176552227afa813035967c9c28156bb6ad9d5b39b5d4a73e62b80598ba19e620037d129106279752a4b250f54a1030f7c187b121aca90b3f0dcb3d0cf3823c099b807ac2cdb3d2a962f483a3765d3f5f50a00aa1eddcea1ff9dfe64877419907889e5bc508d849bdd06f12bbf82121c73b7cd5750c431751fbefd5f27581265621dc87426b2264622edfce43ef5ddf86cb15db8ab4e0a5c638be256fdce328b1228c5e74bbe4b4a2924dd1f349cfcf2a1d6851eebef4dec60c15e5c39543397e6f80ac4d01d9414750eddddf821a78de8fa1f09aacc19caf24948c0fa46e3320de3d6824105d61638218770c20b40ce899b9af45e00463f8ebbbed0a59caf732e41005bc9c6351a0817bac881420ed2cfd281bfbb916b0db01fd7e23ac0419f77661957ebd16f8783e7485a2cd39f70b9b3ffdbc52b998f277b59e5b1691a5d425f4f6079e0d53dbf7a0c430d4dec35dbe36409af25888342a4a5daedee8127dacc0a93421f352deda57d33ce449a9979271866651d776ce7d3c5104ad290ecf39a62a766cca15578e38c54fdcab06ed92ba3ec5b7cd15e406a7e505f3a3716d440e8eaffc4d5df27d6d3b0bbe6f7037b62696152d955e932c335b3fbf36ac7473bb8ac087851b6087e77c3d90351cd6ca8fe546e14e22746153d58e6c2eb32db9a1c70b77a23a48806dcf4658610a0bf8b66b7767988743c23ede774928a19bcf09d401e6188af5711bdb4b9700ed0fc27e19fb4e980c24a204e72819a86062274a3ae91caa885baaf708c93d40141a87850a30530df2925e69e4c4165482591392bdf6932789591bde47ea2f843bada4c390c0e83087e34c368963de66323b8c2ee027756e005e6e159b644642ddcdbe02775976eb04590bd1583ff80a892afc9a88acb3524d236883bee786791b9b7e95708a7d0a7668e9792806c0080ca024d17d9e878eb69ffc3dc7163c4f2d83f09a72e0ee6e326eebb7aeb64ad04617b17edfad3f138d0fe9a69d68fe61897ea2cd8a01df69e4450e9490821ec4156c584bb0a864191b7dda4b06b542d02b600ea10127a1fcdbc6a3d247b25c7aec3f1dc0b5d62f84e35142681979d07f1344e2f6cd856d77798f6f15945a4a7d19c9c83520fefe6b5ee271207d4903e6585883cd394884d49bd61236df5c2c996d41f4c2aabe7a3c6d77c2cadaf3e2df6133e058412024ee03775b811446b23070c2a14e467781913f8ad8f85c6a24afd78111745d979d2e41b98875fe7f56b4c27a7203cec2bd8503df2a9e00a8d150065160c8380570d59bfc6a7d1b3616c148b8856e7ca99de2c0eb5c0a95a1c10e30a2bf400197cd8943de970d1bc2c8afec4c5b69660bf1c50673d1312f21792a71eb13dbfd0635bf02420fdb6a2cdc2712f9bbfed51c6319d9805d08de65e2d9f8e5487ac28cb9a00daf843106fe5ebcd8117e2f0aa2cbdff1d6ea4148ec00349788256633cf03b68b0aac85dbdd174dffc05080c534c719d13e3262051320d8cd733ac5d26d2664811edd5c219f1a08e7f8d28e554db3566c16ed60c098f21cb8a7ddda7b3126b3f14db9c872fef9c7c23620bac4e576a944257fc1d17e073b4a16978874929e23b08e08d2a3de3a955c7c399371b3775c90ec371ebd141ae6053cd4387e0e24469f4fc07c200758acfb30ba3b4fbd4d0658613d622d8819dfe6c7125f2733bb0846480a66a4597236ec2682998bea700a6655f99e98e4a6bb9989b40aefd6d8595e3674873c1be2bdfc548972879e7868e347b6a8cd0adf552fb02d95b7a7496d7e42ffb175d59bc5ccc2f91cafec57bb2a21d6cea90ef98b92aa4691298b7afa9d02158a8f947b7887e4dd70c0e8e16f3042116a46ad61e2a78d88c21a04e15ccab768c326bd12f4a10b483d730edd12020d56165c430fb096c1b8d806c594ae1792a3964c0655814c32b5549fcb45bce576803e9a7a671dc837a7ccf30b6eba0ac1ea0a090638e27eb31c0c6b579b2f3d11dd0cea5c58df31dd6d2ca1b67a2138358d5b3453e2e32d8e4683fceb8fb4688362b61a9442842cb84e35169d83f4a32c63214d3b92ea0cd5157e60755f8bca6875db06df40b6c15e101e78984952b31e417d058f936ee47d029614a8f32530130922a6e362df53f1a9b15b501569d1911b1ab86fa3514184aa326fcce03a1db11fd24c86e87de897e2f54cb846dd830aa72bf1f1215cb9639f988428918b9f235ddf3770ae50ebdbf3f74519029291537bfef559efa24846f322c6c1159d956df7551ae72f126b17b71f1b9d35d33730b7088e1162792ea6e61a57eb4920a86de1399dda3cd1c85a23c377f76146a49b0284dde9e1a62849c9d8b3e6e28bfab17b2bea070ecc55f3c055ced0c1f3be0dd06a823fb9f55cc1f46de52e86efc5073f5eabb48f8d38d247d19191d171701405486be0f87fa886848ac2037a5e6eedfcfe784ac07d4c5b9540ca02e637d40e163da76036147bbcb27d3f077a69ed448e1920b4d03794d80a78acaafc4247ba9ea6c89f9fa2362dce0630905c89018ae31e0c15a32dc63967a051430104a424b26a8f5e9e0ad732355528fa5d82c79b4b85f26b965762373fa6ad4970d46961b721ec009be0c038cc606e5d7dfe29be8d988b00ef1c5e87578a797ceff10207df1dfd68c505cbdb765203c73d447e7ac644471d38508ab1da66f95ff4faf27f966af12a7e3a2d6a2abf72e962164caa74ea90b82adf50da9f29a5c5a84b3a93ff59418b4da3ec3b8f19dd6b541ed0ca9531514746c794198c53f96c14116298f11284fe5d12c3d6cf40136407da4397e24b5c8108cba36db96f06f1322ce13ab920bf7d64699a60e7598f84173e486ffa79f0cb6104c7dc75e299bea74e2a5169f651fcfaea6d16c6b9e9061d6e456ca3b8d1ac76dfbdb2f889148a009180fc270135721119c96d16d581f9c9320a9ec2b0131863f13fb65658a9efbb328f780df8dd9c45b1011c925e6e09938228195171684cec348edece8fbdb8ff6314a54e4ddc0e7c8ac44ee635d9f95ae0f83ff758668de3443c24f2f2c553a5dee3dbeed3e2e8e988844cb3acee1803adf27f80773cb85eaf1524ba11b652fe3add2ff26349a82717719a1ad90c000cc59625be32dada6804ba60b50fd12a155f88a098854b8704f7e2133b7945327ece13b119a610824244bea8d115724d5d5015a5888807b689e01dd899c28a1d046b561fdee436b6ac0eb951392a32f669781c9158bbbe3f5103159c7ae67fc26ad50d46c06abbb34b17dc938447cf6c3e9badf144a0ba77025d0e4e4322b87f9b0ed49ed96de1f2f862779be799916fed7eaf05062b7b75e8dd2c5b3dca66b5e5140b1c3e67cf68aa9ab784241a35743d50c8544d7c84219394efaa338ae80f950b3b51af8a775478186302fb9529a739c68408eecc23feb97c331944a984f56651ee69d27e4ed0a8cb4a7da6f0a0400e9e91b2bb91168670c756ef0bfdf20216d00ad2c44ee80badb04e04310c4fd895d535e263681be2606077741629619c21977bb9d93493e7c6dac932baa5ede7edce437e4c174d4a612a3a8f5e837124e8062f03ebd346fe93d2b0876b5ee4844c434ad1f914e23c015411de85d52cd65e6953d2c0cfd670a1be5d53f23421bf99c1102bca3e679caa359cdfcb5cd4b5dfc9c5f84719884acf6b37c5f8a15db0697ee58b774a2f94636ade5e0104c195699de07056896c2a371da5ad18c3fe8813baf8eabd0517c9c3e0bb0dc4c38e7725d274cac43d11b8ad7d99371626cc6700674c267cafb5cfe05dad0b0563f1f2456f091a822783f6e1964e89ce340cb3016e62ce5917ec16f8a8eebbec0cb2c776ac973b07e9d0c0f0806a0a5cc67515e1f905164b861cf23fd83e21c94ed7ee1c8298afd2880055bd7f78d35b0a61ae2e9e262802665d94313fc6e1a15a2f2a867b435a7c4dc9a5fdfc45544f62b209aaaf88856104820ec5b1b699c4fcb6e1c5ef0c2e00aa61ee21e2895ac20adf36eca1611cacef0a571f0f290441656d9e46e166e283cf1df6be65f44e986f3e89e30b6d129bd3a3b5086d83d0175d4d8faae937806fc81d0419a242311e3e62f603de5a3021c33296a9a26629ce0c67d90392d5bc18b3be97ed66a38e1ae4c94a40fae4d3440cf384d64522d392450d7af0d092437bfbf83f5dfa1702452a039e0da85898b5bfc88039c55ad4c78e8b7b6bdf5a53503a990f8a7742a364b5ae5c83c4728002fe27640e9584a8c8bf30234285d551ebb0a51244acc13f2cb5a6d2c6db76c8d5d2a3687f95bb10ad4350a793d6129643762b3366d9e79c9d7e0cc3535c7d5fd7632a1609f8c9a24726c13f900214b26b0b8f8ea467c0675a02a150d12a090d1711d6aa571052f622ba38ef81a1ca286510b943c98a88bd769ed38abf20eba3a09bf9e0cd7aa6abead82a943247c66b46c26c458b785c6ceca502afd7291de5cd18151ac191445b9c4a88d26401851cb7635eff142a9e6e84428158431877b2d869c5a513dbffba2730a4183fe211a399c8a615ac1042efcc0df1b2ba6cd13151f76b3eacc6893d7a24e535cb24669292fad7aaf9e877ad113327c3f673ec4996e02f4a543e1a8c9e26185c112991c84075f83b154dc5abefc6d9772905359d18884c7f363839951a725476bd4eb87bd4e5f4f87dff76cb0069bf94e39cb853ab32695dde24937f7530e44bd18d506eb71deff5705c210e768fe773a0b07cf1c8bdff22e617fafab88a0c47953f98e0194f3991c288fd138d86480cd994539977b4e915618bb242783d59de58008302f91e8a316d2e138bd4d8e0be52ea34c2b4b596ed769709c4f0a792ef2afccf3432363df10c4d27570abdecca3a6330fcce64c4b01ac015a2c1b152134a3c708e4350d0f79a255e6535c9d92e4be4bf4ca558dc8d9cd2c2e094c27a26393aebe658c7e939eb743985c5aebc0803f849ac3038c4a18cc01cb5ecb308f77d6483a6e4ee7d72f21c8521c67092aac6e7317fd27e97a884ea10f5e1c3dfeebdad262c06fb5a2082d2ba8534fc599dfb34d995e06e5f8da6ba1100b54ef4ec9051b5eeb4bb53c543380fd0c50d28cc5cb93d53bf07588f641e91169ba57d5ea19e009044302f57fff6ac0875b24ee1cc1d7583ea920949940f9fd7b08dd2fc0bf762ce664229c7015f66eb9d0d0b59ef44e4a5390088e244a8d4836bf359604b51a8a12671f9350092cc84a40da33582e8a1b9b29beef9a310ac25d7d12e945e492753ed6552c2c6d8990582c268c71626e5955102cbbe8e3224043698dfeec35dbfc88b0e6452ed0418ae8c4d2fcebde79b51fd0ef0b079690aa99df4a37a3a7c3298cfadc56d6cb6ed04d754ae998f097ec86c0b51ddcde95ebea9ddafe14387297b56e56e451663315b392f6231fd841e2a4df80ee68c4b43749d62d0ee1681eab1a354c8f1872030e5575ec7a6739e067d74f9460be05a588c78f7ca4b27a5a87a938603287f4d28f2e0340b4444d10811893553a5b4a94c20e8312417a19895a0759e8103d08a5a6b8718cf56e76c7adf1f3816b3d64c3629439ba65ca5a83d6fe4ba6a5f17ae7dabebed065d68058423a1356863aa6325d07058a56eea304295682e21d2ea8e23b341e50244bdc6d60e120596aa3316d528aa8dd8280811c6527730e01fbf9326045c09b11303c039355daab0694c0b058212c0a8826639e9ecc76915a1d141a6b53844a2fc262598698baa4584c896b39edd6fd2b04787e97bb19696e41b7a7f3bbf477c609543749d8aed08f483473af0816f42298c0b4dc7fcc896b677f8285081eab212bc02c360945b6cfdc61011c73bdbe36e124da8f0e2c74a686e64ef6d83e76ab96764f5b7cb1246a6051e3305489b47a898ce16818d1ad67d81faf2192e933780797a53fe91d7729efdfc1ee9873e0073bc6ce3d1306d743353a65fddcfd36e3c91294c000727f00534d0c44c4fcc1c375d8534c05169401c454a29f4f638d18367c2d1e119194becc0be58920f4b2bfe40e683a5360c5b72dda963d91fb641830d51928e2823ccc10cd562f42daf4bbbd38408e47cf3889ed84bb788403daba9403b6ed9aae9d24b3cd29bac3546baaa9ebfc9a005fecd88bda22f798304dc60f2fa2627e09934bbb91383423bf0158bd2e3297b86d816d150b8d4549c1052406c237751b1bfc9692c0dffa1cd02877dbddf495c129eb46c2c0dd09a633822c3b46a12299afff808a8a48b6126bfe25fbd8c15c1a8824ef1806d4b1be8b1d50e358bf55b2d29bc01cc268fa9faa8b714d568b7251787f0ec47cf228a4e185657c12d39d3017f8ca66739013e0774a2942c937e9cd50f2c93a10cbed94209446f1987189c888265cac7aa0bf57b7926af21eeab389bcd6ce2ea9758abd7dee5e4d5a9651547a7f331ffc0ebbe47b791e47f3ae703e18bdd9cb12f1c45c9ebd0e5b4f872e1da49274352a014ef01f533d90dd84f8b3ca45967ee130c9c8ab4d31aeec0efa01a7351f441507763ba8de959da4c11cb62525f868d083b547038f54ef91dde3e9d732cd7b7c8bce1a5e487b70fb5ce55a8b4fd685f60b51979229d2e955a230a82adb0d29b264816883f0cd04f3724aa1091329a6dc1fcd90e766539b042db6ff4be2c6f48c59a09afc210802cfab9d42469dd3ef3103fe59932c69d85ac2635f79ef18eb10f8536e04a82ef5e4c39dfd5f56845153b05a292a3fc7e35a83e59d343f72abf1ffacb2c2b5e226a0675c14485340c4a569cbe98e263b21549b6bb30c5db273fda5fc2816ec40a14930c28ee9d0d13da1753e8349f58f38725296e2e3eb60d7e605ee7c4a8dbfc94600da033b1d67db0b6071ec28514591deafd11d67d8637abd8ea6e069c04484084144cb4df72b98dd2f60a2d30758f959e1a6b243313379bf04654f86a4b86a90934e6c0bcccc092964bfc4c92e69e2897f12a74049a9502db63230549200860935f6794facf42a991af5b85e8835ac19d3f2a9faa35525aaaaa8530fd0eadb873cd8e417ca4526828bf08b44946c6b3d5a8c104ae1cbd8a9b59a8a9fe4e4043c4b612f885d0ca7c50fdda7ae16e0a00dedbfe3654b6a9952ce922f0f9c9d399e5bc829e7a520227432e2722637c375211f630690ecfd7cb120097aa97905c8e8999426e47eee27ce858236f9a5d9ad83aea8b935de2035e6a8819e6c4f597899c171d08157c8321b3a5169fa49d0dfb3b154620efa2f878a6f1d783e5c3295fb432552ef3967fc0b409bcdfeea3dd2e596d099dd163aa8b35b18d7ff2aa66a49070ac09a05c59a99e84a4aefa08d8e9978e192a1cc96da648d1d66e59cc3be608f9bc885d0f50588e9e10dce7c7ea33c7c6146b14ed506932ef2d6986b4a98c7b4bcb7b00feafa5ceef2eff4f0738d58c79f4277e6e62a95f5224067b3f6a032db28ad8e70e32db47126093b964ce6ab29f45a41517498fa0bbec8c556266796ae1f22847ca1235ea978aa27becfb3611707093bfc23160ef5f42c48d928223896089793d2b39184ee601dc1f81286c0adbb313d2f3578d92f43da73bee66f32ea23f09df01d7fe65caef5ed4bd7b21b93d216ec849bdbb6db8841c1b068f61d202a6566454b8f68c97c670d9daee8bc6ba28848911597930cc29db2609719710f9fd1dc0c8139ebcd5bed9ad367ed79d3e6cf9e647994aed803bbdfbbd59289892c79262760c6eb112864cd6f466a9a4e77bcc2df0cb6e00e1c6885c69f1c0dcb56aff3a57276e2eb0a4371bcab20a997806a3df59793332e50958c6da45a243836dbd09d927bfce3dab858517db12a96674bda34292bce4c7d98ec8ee33833037a9e4f75db10d78ba798b8bdcb38556a4aef7a46ea6d6c33e52cff226100a01c972d94d20da75140fbfb5ee72732676382b41bfa217fdd0a54681ecb95389c21b035220c91e82131012b6b8f7bc9ebec8f0e620d350792017d52e265bc0f43a240b7c7d31b790eaa90d5eb1f53500732d8186441f7deb99a6095cc0f14b05b34f00afaef3700413bec2bc418a56415e7997915b75abcef6b50326146faa5cba5ee2ab3fa98b7ffba5f36616c3396f99fe3cc077e7fd231c323eaa33f9445830b102e500bead2c51297880f1c40eca5251baf379b3d2a1e717a06212521971f9f926a2d532c17c8101c8d8bc15ede5eb9aac1b95b320ded3275295135abb4121253e21d4035458e2819c57f410bcf6c03442526f72afeec94d71cc155b08b35e64199faaabaea87583672c010a28048b1d4c55dff6241f48c3520d1211a460168a644c5d0e0a17d50c17ff271a74a19fa402b0bbec8fc85c56a54ac2f8171be472d77215df68e946639279816cb32c444eafba5a9018d1622b54f1398c3835d6f3add446e26ac6909aa84fd5b80a65eac0ff6c63cd47e5c764a00371c770e82d5bec4b4938cf3b9545258d5d35c5ee19f5b4b6a3bd838eea34423ff2c15346c924340db94a4a1869b6230a84f12609e9d19bec467ad3e52b5117b65d283578f7fcc0f4de4c88a5667f8781f8edd02c12e781552eeb75dd6e9ee345e73dbdc80249a454101870adf286c006e931ec3a54a424a4b1588d2e49be36653c589283ddefb74eaacfa73f48c72723e2a51a0e9000b4a0b20ba4bd75ce8b90b4d9f3cfb55eb6885f97b9c53c8f4b243d43469e421bd423197d4547e2044405e3fac7047dc1e75e9783e718f1933d39e50aea2803938cdc82602716e52efb27ec3c21ba5a9b8f12449c5c7c072b3b884cf8a81cc1dc5c8b435943f96d18b6abbb29e0d09e0acf0ab8712adc8d3c69657fc40f1ff307a369e53b026e4549cdbeb63f63e5895b91301c3cabc8d032c91e6b9017788011cd26871f4e7cfdcbea39801a14d9b6351480abed6101b83c7e7ed9ffbb6009c66862052b4f771a38f275327a49d2b42cba3c10a7db44f5bedf23995e0b4cd381a2cc11dcf19992bd31547d7c9ca52d0c33108f42f646e8acdca346e6cd4e641afe16ad509a84a656e15a9d28b235689aa5168e8b25fbecb5207dd5433a98396d79a341f7a84c629f060ece23c5819363561baa745c901531026c07a88d6c313cd7c53f8a34dcb07e5c6203a9b7cf77f385a88df68de7734bcd3e1793b3a01414bfd53cde47273d497bc90df29fffc01c8c51c512da256f5b49ed3519d2f224abd2650c0dd27f829459fdefc72862fa5492c8df34432449b4cb6b66e170929c0e786ad21088758e755d9f5a385bbdcc244e74496efb4ad8a23420967c3fcff42f8d852eed8d6b720370ccf5180f2526f88719a63787e9d8416aef0f8aa3bbdb2948cab15187585de9d469a3218aca23d1883586e40127ce178abb67cbb3e12db6b2ed2b4e2496e5341067ae6fe0113e2c88fcfad43817253da464d7ce0d476f532e17e43d810abbabc4acf49e7acc4205e179ed0d3a150d5615365c65c2da2571c6bf2349ea0d0a1b515ab6575660d86098bab058f998b15942cda56b6d83d923197cafb7c0e254e6e31e2863595c247a815aaa0444235074a9e338dfa5996d18bdd14e2a6485eec63cd23ab89ba13b583e7d483c0c79407bb1b444f88b449ece507e0c3a36627e92916f07038aa80f9a68ab22c19e4d096093af0967bc2d64f8ca2bfc41363a1464921a47f49b3fe517389465c64f7c6b39cbbf42d7561ae397814b31ec1316356f91b9bb09a8e3a079e3ad8cddf8add56254323ee108bbc627d57a784688c3e10b26c00d285e3785896d24785f92a043e6c75aef3cc3730203d3f954b77b2a207630ad8fe370edc73f61f1174d70d56dce1264d3fedf728cf879d87987eff8208ecb86be775402426fafbfa8788f51e4fdb765d3e2a954fa99944a33af50007ca04fe06404dbf44ef330ab42f782afa1fd3b1117d8da6c176111bf4d630dd4f899f99c8ba317de21b0b9ab61dbb6e2559ebc5179db45a191f4f5ba8e1588a9d3354b090e8fd10b774a8311a741f35670d4ad9415a7ca7f7b0ea04a30319aa54110e08b117a04104e6cb200f2a9d70aa677c214c3a5c534bf1abbe357eae2a3a06fceeea9804ca26819ea9931fb8e6f7bc64100bc405f8284b74230244bff31eb164c9da7b19143840070edf6028c3a240965326cb85d8f64e5860cc85df855a0d16a2052f7d7368033953d70e3c0b2f795e0d7c11b3e9a961513beef661d7162b4617d199a3128e5a872184480eea70f15bb13ac64504f27105fdcabe4144defefd7a71106e5f446c75b1cd58ac6b24d26dcc5c525f565b37a39bf53e0d5472b39f90cdff00651ddaabccf6dd1b05b89665d2a3cf8a74d90d4e053304fd18079a3bb80a3e64fdafaac0d46c8079baa7134e39ca454d6e44f135f5e8000515e03084307594f818b3c5ba603ba6d3b46651864462e5a7e9a85fb0a95bf2d53d9d6d4e4145e1e74049c067cadf8017547ae8cbb9a63da3d598fad485a178ff6ab59905d2798a1f1fb32e0ede2f82ebe063b36f1657753f1553161ae2290622e471c6ffcd9e7870838de162843b6d78246b99f9eb960e5bbd3c179702372275030dcb09fc65333e5451f8978d79e73a43bf4c7a195c5b18c4689401ac7c414ea81c62a57088011c35ade4b5aae3ce96c9d88f49ef3002f7399c7911c5d8d271703e9d3dec1194c4fdbae48a1db1a31d3901f7a912eec2c7821a615b93be7783fc54f5656dc4d80afbe512fa4caaee952ee72c2c5f8cb051c2b95d50db6d1838303b987c5f55e8f3ee2e7cc4f264adff4155a2b288134bb323c4dc407a88d2c19280bce4209e16a113b29ea9880b0565dd35eb377984415a35b4046ece121abb43b3608f0461f8838780bfb6a2c1eef9218372017ad9b43b91b063130f521ffe901b33f31a0b09f326a22088afac0572c801ccf2e8a770efd7ba73f5ce344cd60fce70b5288acfd45929087b67ec56a93c0bd6bd9d41775f360916f2808753d6231c4c4f6633a8b4cd451256fe22e972bdfb41c802cf4647b52b38f6e5e02e2e9983025d1b61e92bac06c7c12d5e26d1390401776b597a9ec37b970ddb807ed07d8f50d79ae7f9a56e17f259093f2515c053f4b839ec5dc3e6265eba11ea5361ad3d84c1886e401d2028d7ee984ec1f6f0f9fe2fde31621ab5858074796208a728359fe1741a93b390a792ed17124289d950a4777518a003a2630e9dd82dc4129db470cca0f029a220feb5c68df1d16fee6887106b9a4027ebe2399ec69deee2721ff4e5120c36a5265a73e61dd5ab5b34e8a0169114fbc3f51482904cc1bc978be53018321bc27a6b6f3ec299b0dc8a330b0a359353ec09e7130b2877c4b55e60a680bbba25ca95cc16d242eaebd1d6b7c854ee6db2b16f8fe8be075996915bea6ff6c10f575c85c20a182e6a7047fcf77311e260dd3bf830e74d60f48c9d402f8bfddfe2b7d2cbc6a0503f33b073b4df29001608a1a7428629ae4632f880335f309213a0cf2689d793b44eed29311b0068cb60b161a91347065fe6a2a25ce0c8928bc8263377e3d50853de7057ecb364af75e9046a8895364eabd6959987e4efcdc242e91181c5eaa57b982b97d4798aa32b5da6de9a33ef49d84f8987d5e76b2c4f86ed6a74719dd7b1e60a5663131372d2961bbc19bc4ce81ead8c58c760fdd9ce1ee7b45ba7c75807f67a1658d0d5f35b45b62742c410a9abcfd19932e8c1062b8f1936500becf52c08c3ad41d1943f9f8259215828f5066b9ba728151068a868cab6496e4576c87e2c6b131085e96fea10054ed45262a16ee757f871efa221a001c1f9d7554b2673bb2cebaa394012f658e5668d7db0c7765b8806d15b79a1a9314b6cf037a60fb308a17ed4951d5f8e3875dcb0e0e9a46aac3f3d953166d544141a44372e66694911d66f02c4400795e1fef02f7f83f3c96665864acb4e52174622be42cb4346afd2741a99ae65cc99aa38fbf82646f6ba3eb076e52c1bb9a60e53aade68dc331b5e1f95e4f02e1cbf31a6fbcd7872ef5db3174e2967ca5ad8367f7cca9b918fd368f6a07efaa79fbeede61bb64292ca80f2f41fb6ef1f689997cadf8e697c00a73c5fbbba3f63319fc31ed302fcba5be8a63219b0d3debb9f162191e9abf0830f87a959914c03f54a63a610d3ae4a5f14432c810809345f027f132c6ded5829e6a252261b4ca1361fd54a2179ea617e97d20093188aeaaef632e3e6c8037375d288952afc17a04d8e7e60850748c5b75e7d8ab0461b2d69536a48fb920e63a19a63f1cfbd6d49bcd0c67dfe95214812866a2b1f691befd707a8a3ee48f88943fec7e2719c0304c22aa755a304f1a54ee12950431f7b47867fbef480e79f7efd279cb9ba765ef1a1cb7dca83e7d57e07dae4e7225764bed808a00aefe2db42064f2df4c68ce02c46b3afe2a341a58f82652660f2e2214dfac884f9a659dcef1b6373fa831a381fad0718c0aa243e3a3db774064912f26f262c75c0a5e986cfc2229a6c24e46df40daa3948e8c7dd0054a48640c6cc21acd6c9fe04b77733a4d6e45c66fd9b9657f5a88d11e705fd7e5949822d999eac0b6f89df039ff1b598b92e6764a3478bf196f6dc79abf89657dc1ebba89bb5d59ed50d1633a84a7bc6cff7b5d392af0654760d1a6375efc031b170f54302998de1d55002cc36497484d37306a217c4dc8f6b754fddfd077c6b2785258b047119016c47430dfa70394e2f32da87a67408ece13ff95fbc4cb2379e1a0be8bd2b62e57219b4eb1ace75706425c44e6a6a9dec132ada8df3283d59880dc600c74d0d8ec180d984aaabb6d9478f7db7e42f32ca9ce678a2d6db6fce1c3ecad46933bbcf74693845d97a40e2a38173cdf273114a9803530f65042243ad1c8c95d823e24ff3fb03cc19ddf4eb924a70e813cc9f4b25d3d5582d67773cb8479763f52f1d33afd3cb0af5e92b2ba8d88a97f62cec2f7c72c22ffade8f873035075518f4d07be640b9e626e4f8a7f8026505a757c2a753135817a04822fea57f1afddfdb9f96e1bfa3858e32922e7baf90cc5571179c922b79e07c130ab8933578197f2b7fc1af475a1eba5d1ac47c3ca25b688b9f0b4c37d923ceb8c5b5ec7163533e6bb6b486fc1d027d1d22b9a32b2283f1240d01a8856bbc08a78b17cb434f3033f209b0cc2ba8cbb9bc8e9bbdc611c42a82f3e06945170d6705af2c1facf7ba415898a8d321ecfbc68371849fb08c307cdceb56566b4b4f5ec6084bb4b77817dbe04a167d0b3965b812991b041bbf059620840080ac80b2bfd0d1afa62fdc07fd120c8eb8277412f45c24ac526a820026322f289b0721be021bd2baa2a7e3ee58b762fee314e50e9ce3be2931d638105de318d7f40e716c8cd195871d1fa37778b83775d924751bac73872e92b0d785de7ab8402644950d1897e0ed618280d97e212416d21ff82be56fe18b26030b7974a5e4700e177b22ba46a3789eebd6a5061f9b194229f238a79107b1dbbec04591c68c337043faa3d68eea2047604f62903598740fed10a0cc5af5596bcefcbda9860de58958350f995471d7dc52c7e15fccb537218b1e7f04864d5b8fac843d2133afa012f36be528e13fbea0304354e33423ff856d72ac571d61c7619a13566ea4b09463a203d051c7d5cf51c9c6984971a9873cf80f091b1770a6799c3b35729f72160d60038153251a37e54585634a3c9c01748ff8fe9ea41d1aebcd70dbbd3f59f2aa81022dbdb65acb363e4e02a755c9096f09df743a485e60c718a2366b039cfb5a40bdea3a728d2349563791d23aad0251bf5199784de5b8435189ab886bae423061ab10aba1a6d40818722aac6542dec5f1a49b3fbef87617754368f539fc6cefa63c153ca23f43ef24f69a7a95d0c4cbc7aa0d09784d48768d5920616523f88cdd421d5cc6c275238248dbfeae9487b736cdb7e27b656aebd64d439f20166da1f39a7008062161f2c3b863183096e01fa44faa6076da69e9e2c33f47375491e70e2bd62cb86a88e3793712aff00069832a626a881805277670e3a7d2c24eec5aa7c1ff143791a1d523071926b1f6d321c28bee168ee97dd6a979f1f22fdc3a97fc3595f72ad14218cc37c0f979d1e18bcbf937483676d838070a1ebdd6489da356cc09f14fbd9b9b647d507ea5dbd4c1296253126737d9b230e216a9a411ffd43bf2e3dc1d863c485c2f28b9388159f85ad8497928834449b6939753d3f80b43ec12d2817666c60074ba8d0a6638bc31c0ba5cf0a97cc909fc90d310ad98c37049a70f319bb105fce7d91d7e8c085750200331a17a16eca4f366fb04c69ab24b7dd82ec67ff0436098b74866eadf7319bd35f7d37471a55beee4eeec4b93ef6933561f8473844e723db4ec94024aa19860283a97248d7e747a78b32e0d808e855856f8c8e90fdfeb5ec8b8ae555c4d1f06eba403bd9b5cf7d585bf1093d2ac81ffd51540ff0c4689a30eaaa6be10fa2442ed057f23f8857f65bc04ac43082d71233615fe8240bb1e102ce47b853e33c4abfb82fef960b0d0601e0e4ba705e752ca16c0e14962d2379dc0ccbfb366844125a83070b31c5b36ea418487373281acaf13a147972c95aed7575e2fa1b25c07155af632080d9c4c24928193b4fdb3d77d9cf9f9597d75405cb60d234e205d5d9eb18f153371afe9442e19d35e6c4a54fe4ebbdc3b9868012c1f1e0c8ac9986e2417d3ac3c08c2c031adbaf60be28415c2cd6223f1231df4410e777ce84ab7eaa27a8b824051b26bdfc5d1c71018b25628e921ab5cf0d4e2a766e8b9776ed4cabed8521632c0ab2a062d326e96848f65a75fe83152209bc87b757a97104f4f0f9c9f08a9d6aa1301209361a354d3509fa9cc4537e207c92be21451d02bb4a23afa31e1a87f49883524873cb85e7e36a031226d367e3fc5a8405309aa941f0c7a7a97aa4e57f75963534b81e203d03037befc524259de9b0d6fbfacde02c88aa320ab902f86911b5f5490810aa05cfe8d3a5f2af98840eca513d6299f7f1322d90493446988a3ead3ec2c3bb82c025ea619c3b92703590067cae28a2ff58bfb4e86bf45d2adb1ea06f133be180c633643602655fe630ecbbf3a61a979b7fca8fe3de66832db51de31ab84568752d492021ea0db2e9c3ef2cde34993dc03ba334ecdbbca49e0401ca455a06d0ca83c49de9f23d30a84c2945a99da4ce94d0dee2dfc8301e3183a3bb618c13c806a8619c842184597388b818e8c1b30ac9e42031fde3f3aeafbd55ac3eff4a53d54fe5f303abc8fc08fd4c745970c66a30680a0a76152478e309e309098092f6f0609f03020522923fe43094315172f6227682b65e3cc7893d2aa05982841aba1f8837ee3ca64da9afc01a9094584ba9d1468bcff80767a9c971de29c2f593be587e86a164703a189fffb31bb0abf264b55fc3172c6e8c239725d58965d024bcd1c38ae670c7f618ca1a0e3eeae24fd40e25be53749a838c75fc039552057f1f121a2582c7e0ffd7375a4983dbd45dc7de35d0994deeb2f662e31e5928f9f13e1306ce11120a832020539ca75a82f244ee58bd683d81c24b94fac911326fdc28c4ef08006aa98416866397f05438a17303e4fab037fee9da97dcdc21b86c1d5880ae607cdc2db750accda6c27a639952921a7e7ec588c4cc136db79dd1429e0320c0f39f5e9908fca0fa82b614105118018cc0a211b0fdd5704d2f6ca459c3a4a085182d41f8ac0c647ff61df2161b097d990f83548e07864256022bc0874dabd2dad863182dc4d205f0a352c87ac44e1d793b9da4e70ac4629b0dd673f7ab3cadb254b557224ac4afdc13ae590440f134a8758c26b9d2a99896af4b99d1a1b05d283731d2afb0184515351fc65cd39a2fa0ac29fc4b7cad253a90d41e69022d1886cf4107d0e1522eaada2197f9f02c2f6796f0b5d989db2f8449bd24d5f8409bc1254e9011d26cea003f35b1713f7ed4763fbe458c2e5940ac364fbb884c8c16fedfd751b6cb5d6fb726138ced692429f0602d685c4aa4d40fdbe3d23bf5f78c4113e4998e21d33e4503eac5a8dba27d8a92c2584a393d462182e1a865d395bee89aa244da6ea664e8dd23301911813d4a03b7ec1ed54a95c2d824344220855be674ca7507420790900e5b3d9af9c2a5d7e7e32a53c9697d08293e57c5fcb91f6f5ad21fcccd7ca055eb51241c74d0ba50011bca42c207c8e97564f1195dfbeed57512c321f51aac1a1993da055d1e8832f6daf8d12ef04808fc9d206fa101fcaa5fd25f8a1b0a21080db7eff2adb90da0bad1c026e125558c253a28076c714c663042edba330de475df25094b248acc2ab39539f90371c72b41239bc737f0e20851aabd48d6d4a55df49d9cbbac2df522921d3518b650b2c981665033e88967aacced2c10e19fd778cc8658eb9dd8392a3bc824db75d61eeb12b72b2655794dc9fa33e2e2345052835f633fc2d58d7a8897793157bdae08f6b889f887f8a9fad0d231798174aa12c5d6db960cdd05e1c7bbf8291b43fd61036671c8b8c8b2d986e92097637c3df03aaa22b2e897fa666ae0a3a680e42f99014a6c051b0a52b77bca07ac438707f0519bc6b123d5d41d9ade25f3ae4cb578eb5423f5447029802ca4898ca09f20ca54f7d903ab6562252d4ea522806620b280335a2449e054c984c4877c65a0e445242c88f54a390e71b61cebd08fa6beca3e53f08d67562784757661977bf55cc12a88cbbc2f08c086310283bec4a947131d59b05772a8631497341a9f1a0c1017f01fe34f7c5b1c64ba3a94bbd3bd60e39cd954685cf3da1c2c4d69c014e855d4c9b4c245959faf2049637fd86be135b9c14c48d9158b431510e597bd49066010df948189180a738656914c7ddc06ca37c1698333748823ae8a29a23994e9ee73cfea33123e764fbf88e2151256784ad6037b8e3a599260b26654414da5f1e042da36b55435e4de98107b21120dce7f3279b6cf3fdf68c24f3d8b47caca93d7ba122a03d5cb4000f41b835f6079442bea719c92a5b8f11c924c079254a30bfda82c82a6c7a780de40bcc9080f24a69f8440de102c82da5cddd79021b6e8d5f62a5834a29b48694fbebdccc5673e99c1776ccad47d3d6dc1f37935f52b6b18926124dcaa53198a3f469df14863abb5b6b027e2f1b359e3f7b780b5a5f740ddf1eab61cabc496ee84bada188e4758208004dbda5dc0dd7f47fafd705a24995af88e2050aeda6f4df7244a0b867154a86432dddfca20e22676676e39879b3deed97861b7eff260719b211e22df739b9e1f7706695c461869ef1472a10d17dcc001b28936a14e8dd583ba1a5ac1c9e3e4a194b086004b127dcef6490c76d7d449ba16b20aa8ee4ebae33ce5c0a98af1a67de8d03721a18897f16e3cc259feefb26209ef84332829ee17d53510b8dad9f81e2b466d6fc3e72a982f722e57595073ba48e47a2ff3e1e200e41780dcc8de0527b10b9e24efae8a4b7112b6ee68aebc7011087e7b872f14a79bcdeba5157aa514c28d1abc5b6269781686dd2dc5410a3ba9ed349409efca050ce206db88fa2f03dfb4633205a5357616afc86d84b83e0ce8160c9fedb9b8ac460b24f491bd1908b214b09e89022eafa462a689b1f126cd017c2f8b5824b1f285ee7b4867d46e06c7762e9ad95b1719caf8d001404a0621667d52518740f8a808055e06f8c713ce09d9fb130451026cb90cdc429de296de597230db40a428c5f89cc1b41160793d982cdc3f22764776237f53fbfcaed2bb519a0a8c6c1be1099275daeed359a72f1cf816f7d71ee1bf4f7660b4248d0878dc96acf685daac70343b1d5025309f5784d4fdee8952d092d9823389f6489c376e2744ea98332eabb5500d55b6df27c115e685e187042d4c4cf488a10fdc256f9983bee00c069fab4b725be23b19a1a8eae08dd363c7e8f2f8edd13d91163d6fb6949d178486f58e822faf500c032ab8763c34410c72ed699068c2183106b84a8b98a851737fd53d11c0d8fb252e0dc1639473f21f33dfda718e26321465edb262293ad01a4a6df65b6870b797748d3108695bddb4bff95fcc125663d7c254826044ffef1366cebfaa19a88bf01551ad768e9b05ef8db74e2961a81eb3a588199aaa74f26bbf790d556843874da46bf7352ff48a4a866b4ef58e39d836b9ef3308d9ca146055ae842030ecda5deb5697e00998e32cd402ac07e4e0c3712c44dbfa58b73fcf4881ab161c1a3f22c3582051e095275b8b614e5c180818116a2008a81b340840eee27ad36ddda09fef8c94fc005ea0e7f06df561fdbe77ccdaddc3dfa969a39f97a970bef2bebe79cf8f2f8e3342e13a54adac52ce377bd89c4b8d50946722a17f64701a93020f9c2121a81ace7fd504731b30a994e11cf7ab905f91de7618c17660dcce0ea16596f3cbdd84fed728afe093ca7cd527a2aa3f41b10b232a4d0b6450d13e8fb6f810231b746f79867b97cfc5d24d6ffc1d375d2391e6a1559ca8522ca7c3a5399229577b3610311843d1d933ad0359d59d10fc0a19d88fa00c4bdf3a078221006753a46d8cabc8872a2d9ac75c232d21953e4ea01768c2c98a68272cb3b4fa6f23f6ba5ea0eb9e27bffba444d2a4be1cddb747b15e728e9604e3fa39f5714811883fcb056679c89fe23e3a446188164b8341c1426a6c4fa8b39868190d820fcfc198802d4cfd4ebeae11474540f2056331ba269c11007838fed9668e1262e9f3d5040cad52b5aa43313c91131169e2d015281be27f6e65d859916a0c7a4958f91dbd8b9af0d6a7837cffa469e73d4bb897c34161e188c661de37055ca7e979723a370d17f9adc69757de5959ace8f15c4ad77e8e259625446331c7af482884dbf14767e9faa069d9bfb1055196b5af8e4e0e6e72c58d82bc9f8053937a7b35b1719cb0c1e3a691a049f6c58eb65c8b9466220ce48b1ee0448e4b78c4945d9c393d873269e5ebc2d136d319d4653614bcd03f0ada3fce9a33f3ed2159161d6cd887a2ba529c2a97a04d05b88631d7a61d83aa60a61d78e2f7cbacb438a592e44a7892e064edb939a06ff724317b5aacf21ea8042bd3fc577c8464ec3e1b327a0184c4ac37511fe2e058ad0621ee51ba5432202137ca46dd8080a13e2c767545306e306b01a0f41efaae0b28d613f202c34a88c047d4a6dffde24885ce460595ca4c1f1bd2d92c38c93c6c6d0f2e256b5da4d28db5b15eccb471f37a187675ca5991db3d6c44cfce9cdad21680dab5508de7735c3fa39a2d50d5f82e3a819081fa3f749e93f270607ab566913a4db8c97b89d43e3cdbb467c0f28ed855498070955413eb8aaad1ad8784227b93bd87b0f694140ef459d4640f2110d81915d743d9176d1a8190d71d9d527a0b753a2365504d39122967b6142f4244c54ed63327500fea1766abeea8397573e4719b20c7d879c97a090930e54cb67169739dfb44f340bdf9913a69387b90d7abc729a71c8c531c9df2d6f9a2cfb59fee49905f292e0aee9207b055aae35e6aa52a64e3c81a8c0102eeabd2f9f4b0bb7e4fa46a3fa0c2060124e6f5bd33937bd56b1985bcb164b2cb0dc08a15c49d753434117b5c09de0559740c320a231d0e981b2a9bcf0ce7f3ffe4aca759f7f5f0deb62b7318aa44fa77376f4886caa2ed1ecf30fddfca58725e98fdeac7d94bfd10888dfbdb8e38f5ddfdb0a8b2ff8a9246011e9e59e40e412dbd629e782b5b048a80ef8853c002512df0a9e8f285730029c5c6155ea684c9de551894384a261a6e3021d1a69123c545fb42b958dbe105451a079d9c09bbaa964c8757c9aadda715cb4c22b19fb436c8ba8640ba6673aad9cb76dd722895fe5374d05083e667ff033e40c9d1f7df12dbec8214b40362fdc78f77c274108bab98b872ff0755a5c26761d4d12666899dc8c4583de16f332227e78160a53b19d9348ef6054a8d939b0cf72d616e91ce94fe2e0bd05bf0c37b9464b5a4cae853a1be91769c45623b8727d9295175c07756e9b4f1b07138e5b718526a483f7d0b85b0b1f5ea3a7a5621094450d1e34a50f2ec0b0ec5aaa53e517cbb14facdf5e9f75e8a8c3a9f54b74756babb225abadd5c7cc2cb5b467ffaf00c358c166e04f9e8f4e980d4c0a6e626213164116944fecee7243a267a3fa8308830c8646afe32871bd8b773d5510cdc066a2bc48d99011cf4aa78d1e27dac354f529dd5c66c4e3dc3773832e82d0d8d7c4b49de9e2e5a81e47bc009456693526572a65c1873c8afcd7931a4552f39477228d5b58615d49d3ad9f799ed1bebec82a8df5b16c8726ea74c6c600c08e9a4a6d8006823a56bfc02e3fdd96142e43257e5cbe842295632ff0337a31e6a364deb49de9ece13aa61d75209ac0e4010193ab108f6679f3be5c015d03a54edc8c18bb6b8e71fb9d6b380f5e17eace1afcffd2c9021d077c53ead4253a7a0dc013745b29e699cb219be9208a0237caf6632853e9a9abc8c5ac5f421e9b92de5f864256f3d3959d565736631c7c11eb1e0f579ec9e7c40977c3f344fdbe220551e2b2f8ac8a12444b1698dbb72ce1b6a3e073628c135ec349e6d8ac03d99d6716dbb40fe9d84cc404667e279cbdaaa3e9af957dac16b2de553de3968c800bf59a745ee237a2d0485adc884a423359a426f6098b675b973a0d7c103ed7370891ad74dbec93c413d4c797a0ee0b94f39510dd7a39420dd7301f2b01b597488ad93c034493f695a2a447cb201f7eefebf0ee6daebb08b3a84534345b94b908c3ed87278e29acefa79bcca89aac41f232892a598318628aab03b742ae5074f936faf5c3e2e09bff9678f4709c29206863323e18fab567c50909f4fa6522290aae3ea02bfa6609ea6c2970b7958ba3240e9c8e6ef700411c4665ecaa913fe17ce71b311b6b6d1c41e0c8aa324efc2a6af15618c480a60acbfeb764e34c0280ca6ee45f47629659b47a16c54d81c535cf5f5302d7cc238e39bceca6ff82201707032cbf57285863e8e689bb3a56eb614ab136bfcdb0b67a5f812ee5ea660015c8e221d2108653633135fa355168454b80172d8b6d482432586bdc25b032806e756c0b7b65fd20f410d3b5b2b5e6a3fddc47ad052781b375fb8be63489f9a1d5eb5bd7cf99febe841db22d2c893719ce5ba28816a60b18a18d5aabd13a66ffc8b3554d3dcbd28a90d1ae0451216bb431f0675ceb9bd4f4f334c2fc6c0ecf4b0d69f7dc76580468b491f80bccf1e7282154d6975ec4487f8ab6778fce07f52ff726dd0abdfd35e51feb43fc6acac3572bbf65b1483c6e35a1d698d015a40b5c5b0d57396e61ecd596c2f98b6cb1d4da687dd16a8594081de6fd3fef25b4cb9fb0338a45d0e3df76f16e6e78aa7f59237ecb305afacb1e436a6ab652884fd94c47566eb1202269942de96bbd00237a24429ce23c242eb4c6b8ce70c688c4299e9f0305ed81e2817fe50590a9c54bbe0eeba309f9e99f11225e03fa2b9ae05c47d53b40529d35b544ab637165c7804e1cbfb9309d3fb727c23622aadd60dbb32643ecaf4d98abcee95b513838011bd9b636107e05e8fcd0e484ba586009b92046764ede7b316335b253c34d814cf2452c8e0c0594b2e1165a4420ee585ea42772199b1b17ead46aad241a559864d154bab50c306f52bc7b7c69ab0158b04e1824eb09ffc79b1fa0d12027997bee517216d17d8e066f97e07e182369bfe60c44933a7f051fc88555f40b50de42021ca18e35834cdcfe8acf6e9c39a29290aca24cc113fe99e78f5586482bcaba248c63cd398f95ba87c8d53b7e3cf3b5711c28b18bd93a485660c0e5f5c5f407d41c472076620b9f6018025b92a854cd3d1702672cda5ec3e356cc0b5301531cf9bd82a1905d56a1a5afd3979a45468554832409050e822a2de2ce02fe99478197f2010c1aeb086fe1e6bd7478fcdd8993200c25fed62ef0450859bd1a013783be2c08af937e3ff56a14091b441bc33220592c2f63dd1f12ef94ccc0ef20171c45b05e2b5b6bbc3e051e35cc81ffd7624ad65d02879bff9854ccee30e350f353792841ebe799cae7dd693c83df8458182086dfa50ec227782b54bf5f4d278d2fe10db009a41cc8fa9d63269457dc6ba60ff2249ca53fb4ff9581496378155b3d39b34fb10ce461063140d8bbce30fc1785be1ae576472acbe33869ac8a489a05f2ba0fd35e7da0c03222e0d23fb1e29db15abea61f94e3607c58a57bcb457a58b6799608300a5c2eabc4ded0d418446f0ebffd333e87a112132006cadae34615bec7560ce5ffbbd7ce01bea23df6b0d1574c572159e204d55c9407bfeb008dd941f13f487e168a8f2d4c27af42a0057ff022612c3e8a27ab1a051dfdb05d1e1addb91d187f0ce40609f8103a8100da1ee108493078a68645c3875a70e10bd24ec7698787ba950eed05e6cf2731ca4efaa8f18ee094b96c5c6b453f11ef6b84e1cd45573850aac29b159f2aa40d054f7db802977f957b2d453713e18f5e5787c5505e1c0c4ab92c0a8b78f4a32e466714c4ba1e0fe5ef3a9a277b5e01031b1cc4f5c043c86c9730363c4e05e0d01fdb29620338ace48a4322e62052f90c1a571558446357f8278a65f233e81c68052af89022a6604b6d000abe5b642ddaeb9812f0f988630a9b884da8f75144d8f230bb302351c4b3ba064cab12180ca43a642c4e5ed39090f1ec6929b34231bd8360fd31d9e5aeb857fa5c4c80c5ed33d935c20cd6ff26db130b34d14d44eb9757bfa472386687b7cbc7281913144543bb4b852c414fb152d5020e6641849f90b5efb4085f32baf57f5d58b64f0ef73cdf10158110c736de2f9d9d3fb483df0aa79cd7b3a04bc4801da2778ecea4b80b5110d1a5fb78e03647a02a5953a8a9257888a4d3f4740a26e4754bfc3574958bdcc6f59564e92ffbaa6a05324016c7e29a177ffc2df86002aa9ca42e4161d872bff04fdf72b598e399018d39553c3eaa184fae2e2dc2ba22dba82f71a23186a6e2d0e65414be5abe26f74cd9cf51970c001be0bbc14d9d6c966ef710f617537e2abb40e2658e53cc0e91e2b98bb5130615cd164da1c96754e6691045fd00d63498b357dd183f4efc982ee03d2f13d1f6a2beb9f4736a17803dc24da4c92d044d721b72a0b6ee3c4d5f62e501daf2f1fa925769e4dee2681d1f2f256965b3128cb90df10db014ebf47638c0825c1548ea356d2a5fb42c02a1d8cf21ae22fbc486da45fb05aa0f8f6ba158c97bebc74c94b5f5f91d794cc1c1c2405d23c0bcbe7ee57bee8bac24e9f0cc8f1c69d2acce58a8abbd2c85a935cabd1047f24360d3de007ae627eedcc7a632c645c0a65de10370a2178629c261347add766806206ac385e911339b713685a0dbcff334ab333ab846a97c70a95156119ebea8785a4f89df8ae888e76b5f5a518c8b0defd0f06445632302721df001a90ac49abba89967d5b6a489a7cd1d12740284036e5ea65bd52f6df5d3f06c19eff3d0ad892ea054656e286245f4247e8b8bdb4dd632afbfeee9c9ac622004efc4c7b086c2c175243d8da7a008a739656a9bf220c546949743afc924cc480d7844a3b6a726ef502dc7a96c80704228f8339f6e964ee132891f19be8ff1ac20092085e4cb03eadbba925b3052b29a0e6a71b4e032c0e89b58075dc1fce72d688bd1ed2f93e8d4371af46ba82ed25a559809d88002b853b1465ba0f573fef9335c37ce0dcbdf748b28caf2a54f352a77d430bddb97171aa9c37534e92664ef2db794322599021d09f408f70841541f1e9c208a6d66eb1354084e6812cbd1607b81a53d52d8ca33c1b07507082b3f50e1616362e9e5275672985368a13ad87a6508bb81b1e5c0be68b14111658c2fa0dcb056f92207449ccce08ba12fbec85921c703926ef84128ddd4b83829344b8b15a4805b62c8d32205446a52b7e587cdc98ccbe24a0f5489a09a4510543095d8d96e6e16433e38f9f9896561c4912578b20802889f5a16b314fc207981b46d1d9dd26f6e52c0c1e105941bfe8b1415085534b961bd11a30a255b0facbdfca40a209654b1c3a4624b65cc262c4c1767d8d3e5273d9072c35aebc113da834d5d7ed28323b88af35e040d69f15308de1418cf4b7537cc8b4bb77577f793bb9b4a5e77772aaebbeb66bb73788b8740dab3bba5b36439e79452764479186395524a30ec7efe4255774ccc890990c58bed299713664a9ff4856e2e9bada81c6fd920ae73a1b9cc413c7499a8f34c5ea954327953bae9d44de9271487b2d5ba6c2e2f3487ea06022e012ed7eea5e6f2835d13b3d14a9f2f30734a8749c9548cc73007e1bc6ab3fdb5ca1c6ff55798973e6566e28c6a5371dfe47cf65fcaa4605e5cec29e76d20c163e60e521a724b432e839312ede84ea1596c837fc371771f22f03be59360becb95574e508231f79b3c4ff2861ee2ed9151e3a652311832fbb309061382cccbffdaa894524aa6e12294524ac91fd906bb7467d1d9db8c2c1c2e07345cf817b238216cb55dceda9974ea77eef956bd8e4ee9754bd48d4e597d0baf7ec94dbe55afa3537add1275a35372be85e9d4752e28e2743aa168dde8945b6c1e7dab9bb5269dfad65d7cab5e47a7f4ba25ea46a7f42d267df1ad7add94d4eb969073ca8e6e51b7295f5e4c3af55f1cc6b7ea75a692a9b484d72d5137ceb7808131e9d487f1946fd5ebe8945eb744dde8949c6f916a01b327eb0fb9dbe97442713c3b3b3b91889624156bd2f1b7d2454eefb8bad18e73e2b36ed424a9b8bc705c8c3c34787931e9cc7f913054d2c9451b78a90d86edebbb985ef040685db0d7878376d2219f2196345460604c3af36152914e08f48d2793c9a453273da552261dff948c91d33bae6eb4e39cf8ac1b2d492a29930efd549591d33b6ea3b5e39cd06da3a54ac5e74665644c3afe3272464eefb89257f29c749c139f9ea4323363d2f19f912a39bde3ea463bce89cfba514f525179c9dbbae6098f6bc87639b7da759dc7f1ecec54af542a99ea904f9cc562ece854e278c4ad2f3a30fa78abe3183e956685d0703a9d5094bb8fe3386e0855429974eaa3e48d796529ba2a51ecebeeee34d659d4496bad8b1cf291a1a847674bd6a433df6e2fbe83892c030915c0e784a942148a28f4f63e413b82724c3a24342474fbbbcd27888261cbec04c3a825e8e5e505a6716aa6ef59890fe70606c6a4231fc6d4425fff947ff5fee66e330e459f8842bfb7ec1246b2d2acfe2bddf2892ac42a8c427fcba296db4fe31733facceb603c621bfd344536c575dc89569bc68a58e7a5bcafa46a06782fdf56cff37a6488cd6845dee77427b7ee3ed68e8d4a18232c13e76a4ad50ce8dedffa8bbe7f95fe32f8acd5dddddddd5f06f7f63c1c56ceb7e393fe3032f0586c9902296b764707186781831b3fc6fff827b4cb8fa0b8910706aaf870a31729922d5611c28d91495047212e3ef1020737c6a496b51763fc1a077152cc2746a42a45b8de4bef3b938ec799743aaecacee5be9a74b81ae57634c5dd9e9a74b63a4d3a54be6e585ac0510f5df4e04e1702ae7cf72b9cc8af7000388e1be2b8feb25dd51a7d71fd5544d0c1f5ff220871fd594508e3fadf7817468c5870fddbee5c2f725b6aacd5c8550e51824516a32a6500b1822c1c06194244c8a00844499e80458974e59529ae94524a19534815e69205bcfd76470fa46041055216242d60518a006205261812a28b30c65001a34c3a529e8470bbdfb4811c643f8c6146104c1001059a14498486aef8c20726ad11ae18a1da191b949e1587a0c4edefef6a9618104a4c998203241821e5b3fc859a32840fb12a589e3c81c9f939a404e3ca20cc9b7b938234e978890b1c18800b2bb77f33e97433f1840f543005133920011b92821d191451041354928004ebe79089dacaed5f21470cddddcdcc373f4d3ab3ab7142dce18629e060a090adb062622d57d2f048841631892b9fc3e8441070b8f2e557ab7ad61321ec70e5e3e4e4d841e5c895cfa3c7932b5f8619a8b8f26908421631a2191522b41faefcb8c343054b4f11141f22aefcf8d32e7eb28511aeac45618613d6429183dbdf4f93524a69ba52beacfc62989d22c4ed669a2d1e5f96de1f68c38c752f9f4707ae7654eeb64a29a5ddd6d599d80fadac3e7d19f7ddefe0b1d5046155dc0d2cacdfbba718317ec9767818fdd9c3aa0aaa1bf955a35b525e5a11b8a4fea9142443cf6dfbee41c3169bcda6a1470fdf70d558118d16b93bce0e3a95d8908b38d0a35df1654c42a19452ee29dd3a4a69f53c4a29ad94524a2955954a94d2f77ad40da6c90769177dfa3330a5325a2ea54f7f866fcca72fc337e44f1d18e06f6dba259fd50ce897ff2a46eae71d0dae76508e7bee87e9e168bf6fb2ebc09bc6f1967ceedbd1c35bf2b71bd65c72330392adb1659147812130f9f13d989e16df38ddf82724bee1baf1eb011470e3832518222cf7f16f1cc773f0f097f7918b3c90270201e1532405eb44030a828bdad64745ed62a0207e6e7c9451bbfa8b1bbdb861e986a8a21b1f15e42fcbc2e9da99e5d58eaefb5505b8afe95860f9596c847473e3132d9262230d6971c3146ee49962e7865c9b32bb210b0d35a382da153e4aa85d5da59de54e7fd6df64f2d93401864994d077b3d54925cb5b113504a28258a8a06e88372786c7b6c6cd3f44dd78637371f2f94dac823f7f2cb1fca3030962b92f88ea72f1b9cffb9a51f0efdcabcb19ab87f34af9526e5f9f7e415497d6707b4af9db2761e80643afca215da44978252a2cf7dd7bddb7e2afafc31098f7dcb7e2e75e8721b0ce7e4ee987c0e2e7e8def46ff2be040aa9ef81ab8dfb0e5c6d4360cff63f7e5825e8aa58ebc0227f82cecd5f328b18a5db94de29e0e090b9fc840a174841a2d6883ceafe1c65d50b70bf8142be1bc2fa8fae0663986d970f7f3682818d90eeed5f61c7c78821a21ddcf08688e78638a4c73eba7d751cb1e261785ca3328c8b617b0a0a91b1580cf439500895f23bfa1b2844f56db2de720930ac1bc2f64bf911c62e48b0728479f4ab5b6cc92ea7b00401dd6ab08bcd8cd1c9be6e36c600be489b47d73f7a2c26057303d2f5977864adb528d4c78016063c7de844210f9d1e7c7139b99cec490027d42900a793cb7ce6449ce6487cc793384fb3da6347b0f1438f1de1ce213f788c731eeff11fef77e9318f3991f498074d97d3e594130095864afb7a8584daedeed4cdee9d3d676667a423eb2d74f92350bf5e9e5dfe058cf5f5f20cf39c7ab6f6a2f8c34874f9e3904dbb36effe6442fdb8a8d44581d267b08254d4ac22d4d09315a42bcde2eda7fbcfd9c523a36659418a5762d10c52df0cdc9ad58ef52c16795191eaa7e73612edcafadcae2a3fdeec80f9aa8d2d4ac39532c25cbec8adf923b8ce5d87d54e7da261c9014ca453d4886589aaaaa2142c2de85c9246c68284b3dd81b5ebacb513844969fd1561f2072c3ab41019a88e9d0f6b41c2183f52c49082040ce72cb45ce9c286499e7841112964544153b18d1e6ca8bade9d8f512c168b0d0d3942464d0758ac406184a3d849b862b389224a1133bc3872c5143029a594aa67dde0e4f81131f210a59bc9952464a6900cc8d811e201071c7a60a1aacb884730a9e0111f9820820d7a6ec0c5e441123bc504a68002832945c85284056f91e58a0f281f56d4a6b060883384b0d0299020f3ec527a16e5830dad9d552a90e40a10367892840c32889034069195488cc042085ba050628a113d060d68789a878b5453b102a32ec698a26527053a8085372c05094f9e8c2a39c03286124eac80c9ea2fd5ac48d850252ff7840d5527bc24aca5743f2232d1449d421115f214d595373786ee5e016741032b9fe5b179c04ff4735fa5e4406e5665e9fecd3282c729d8d48d375567ad31a66e84b93e22d18c439d4a828d6f4a82ed0fe38fa908db2fbbe5675a621bd503db1f5bba0be1ca8579338493ae044309d67070b583bd156be18fd47550485604db1f79766861cf6efc6e241b8bd04f2b27c064fd099595b13aa8bafd95ce183fd9cfeeaa2e584e4e00aa0a1b4d5aacf4c832b6143a6d8ece0a1bd61cff8123e9b64dfe28a574db36ba510a0291778241e68df20a29e50b141b65cf295dca292537cb49f4688db0fdf3638c6cbfb7c7b73fca79c31f2bf7b9d3e7aee40fb9120ce305c011ac4f767195ccaaf136724a295d35e59ca966f1a4d2b9e90f518232c61b7f3e15262db6bfe5ed97f2abd259e83b411bb6b1baf33ab87241be833f4a3e34293871e85723287f7a35c69741289d1394b54a29dfe7fc6dc666713570153318523a5d7a758ffd35c66e261e33335b1ed42884a762f9d766f177c7dea97c63fa26eb8704b84f67f77ceab8ba994c4b94ea57b9aff3d8467bcfb2be2cc1759be101e10712a4de0fc42d9f8ef7737c8e36783c38dd71dbe43a2f47a5b2b63fc76b35efe1adfe1ab3b687ab54563a4a4a97a520d7519fd342a494def48deba49c40825859c3f558e7267d725ba2b3fc15d4fc62b160f0973f7db0dcdbdbfb191505f519d63a7446f732e6f08dfa1eeb4f7fe3249feb19a24825e55fa5eac01ac1cbaf18b35c0edf68eebb8f2618b8ef7e9a5e1841585da89c05b8e5af1b6f751ce208aa2ab77f8bdb5fadaa2bc8836df477516cd3bf8e80a99fbe5043287ddcb08d172776c86bea032f876ff4bbdc78796e425fb64385edc08235d9629e5fd5afd4a39e3fe46ee7b1148ec2502eff8df99e5b2eac90c7b530ee2e3060d8e3da8f6f3fc75f5f3fff7795cac5c5c604aefce8bd80253659300441ea93dc2a05b9a72f9a4a39ed9e9c9ecf29e5acdfc628f89c734a973509afc25c2ec0e50f5f2e7f488099e7af93d68dfb57f94bf5b22616f53b54d88b0bcc7f2096c220b7bb717afed0e5ca3c3fbbea93dcf24f79ea650a0c85dcaebdbe6dc3e57397a813a3e0e281eb5d8326b6d1cf2720d7511634a1c01004339fe45629c8e963b6d16ffa22dbe82f7dcd36babbe7ee9a73971b4521faa4ed74fb98ae47cc60d84e279573ceb942b32828a5946005739a25a3f0b806bd92a8b7db7d3f67b9b7a19315e06ce8e740cb15a836b80dde731f7fd6aceeb32db01eb88a81eb0f5938ab1ddd4b80611dd8fd926ecec711558877fecd8c5ffdaa7f2a6fcd67dd49c3e706478e18ae4c6f4e2a236d67ced28cb519049e4b6bbb5297ffa483941ff772d659ff24b321d77ab46b7bfa75851c3c3030932efda9e5d279a559f446a490ca86c0fcbbdf41550c9e107f0fb46cd953c97ecd2bdda29f324da400c4c0b2e8e7f00d7ae9c7d0238f461efffe6c0b76b46b250186c92692b7e8af563bfc25c030a7b34bbf02fe4b1ca4316fd1bff463882a449e9c1c250461196680a0266e1083312cd620ca113a58e989c5600c63583891eafc55addbe7f8cb269623861e3fc2987ce5524aa9479d7edd2652bb424b63ed0a6bcc2bed6a322efd8905e9d22b37a4b24b6bb46b7e539e8a25e5c337e2f58729e21b32d779080a223cf04099903f453764a2eb42d87016dd227fcd1c70cb5f8aa32a90eec42209c80b5c08aeec24100aced90fdb7020f1caaf6e349ad80e113e4e8881a40888d693c44f2da8a85d613d6a57b819156d7ca38bccc983df06c359e47a9d5d963a60e2b2ece90fbdcb0d02892db0f3a377a5651bf28bdfa4b10d972d6584a111317776da155a9e243b4832154b4835e6f5c70146123e6358581a185b6ec80203e886295cc7e15a7485a8c94402496c88f2f1f117c36689056f7ea273c2f687281f5002466c6863ca07a6e874245343e2391d9d8e84b0218a86a2f96bf678cb5fa5b276a69241d76124141a289f1e9822b6e13dfe3a55e196ff117c4e59ae3b0a87da3d523961c3d3d1f5af73c236fcfd5773f6f86bf24c1f276650bbe6133288aeff4cb95eb483d92343c590acdc70d6907c6e388526d1f5eea75d21caa7d6aeb073d239d1ae1ee3ba18ddcff5f8c50f513ed7bf73c237fafd517ca353261b9c41e4f2132b980c5d7e624613d754eabe3f3564f9460bb9210e31c50647b026dedf1073008415c4604d7c74ef7dc798a97bcef4217d8ffbe2ac59311fd8eeb9ef628cfb9652fe46b9ffe453ee9de3c09cd387a35931e6af08eb0dc479014e1465f470e356e576a62fc69a2553c2e6e8e68f9beb812b9fe0ea5390f734bb5d4b34586aa55f0e4ef7e1a0e1dcc6d96238671871a3e7388ee32fb682e70160c424ad74aa5581832861dca3295da14d98c2cade548d23d060b13811c76ffc16fc469b76c52f055184188a920391213184e08ea835541bceed90757b0b135c3c620b14b7bf6e61b2f9b0453ae79c73ce39e794ee3e7bce189f9b5537e65291223ae50f39f52aa7fb94564a9fb572954cb7de6a8d53fa1051ad9c14390ee0d7b681967960df6e4696898d95cb611b916382ce19294f76777fcaedeef3a9cf39dddddde79c939fe336b4b9bb7becf17e7906ebeeef32fa9ceeb4269bf734c2db289d73ce2f03dfa5574a175ceee459c3bc73db5c5084b4e26d19f89e9b5e2e03df7b89fdb2bb7cc1f48207ee7ca67d257bdd7c174eddf8ab826ff33c5fabd7d5a46c4dd89acb4fb618b969b2c5042cb0a72bd40823189c3174c4194fec9cc144e88c24392be47840d2115e162941d46a6bc94aa1753759a050c14634e3b220f103bda256b3f01c9944455b162248ae40ca122342e493844493f29324450558f4240515c182c80b554709038a4552a96c68398a2d4b9864ad76c8005abed0f2c47555554591050b4953b7842228745cd5720c295ca67777cbf825e105954aaa0c24b248820fb72d106c96306ebf6c6e0b8f99c1ca42ab424bc3baf9b92e979f241171bd58b38a2ce0570d1891587948640bb0f5b9f766b7c586aceb0ae057dc711cbeb171fee4ba471fcac51d16b85b411a128efb5aabd7af773eee4624f8258184ae7f189ba8b7ebb4f4cecee4a92fd9e32dff5a9bd63b487aa759ee39c7a13650d63c88de50eb23be51dfebd736f7232091f3dabf91b4abbe67e99e7b96edb68fd52c1a1958be153ca159ee42f7f539f0c604c329b4a88f794f964e2f5f93c75b5edffb6670beedf6114edca9f1f651dc893b366f576f63b35ad97c74f999f73c5a5700ebe3346b051c49b015c401aed02c1c1288c75fabf715c8dd7a91e00a31d8fc0afc123ec2bc9b1b0ff2ea2ac85aebb3ea77533d5e0ee6430f9240d1e7a527f59e7a0af670a0ac9994d8d5db9c76d5ea269dd2d7124a89a51fca1a959c5cd1d50b6952393094e1d65fbd9b74aacb7b6d8a80bc3e562ed0f79eaa7e45e35732e00bf141d0805b5fc81058096f430960fc8645188db7a16134dee669a8409c66b93cd00788e37239cdf2520e0e0e4fe6bdd5c7defa52e9fb928ffa7c77700491ebfd7fa05e11d0dc5aaf34d50f225f6880bd5e046cbee62f1bae7de27ec5fdf56abb66c0f6519a51d170a90c4b1f4b2ad00519301ce2b1e90236cf7d2530ac979b52e6ab1f47b98ff5a7ccd72ffc5b552a6beb579919a7a04e048378579aa957268cdee774f53ea769a5c1e68bf9626ecc67fa54ad165697ef87ea86d5f49f4db3bc7ed4c2d27ca8aa757a5cdd9e4d11d85634dcea7df1729c8c9ef749c983f5d101f97ff9bb5a036e5d7d5d3df72d21c16ab55a7d0597d46043cddf0063d38e152824c26a4016e86f3f9fca1dccad49c31ff31023c9349885c7c95929a6240edcea990aee76250efc721eb8d5cf71a1cf686ee8b350c67cc6369a61a1cf7c03fc2a8135b077434e429224bb7ef90912ceb81e2d719c62b71ea6d090c445dc75d06beed6c02f29eb164b22397814dce1b516cb83874feffcc85b3b782061bd85501e790786759a886ce8339f4d0f652c82e18fae876f84f14e7047b31a48bc7506246e32d6d7bbc75fa567ef815f2550cada03b95bb12337bc771a8a9a49b3dadda7cffc9a06ef4702fa0cc84a24ed0a7d1693cd2491768541b62b8bc99916d6fb50c62228634aacbceeb1b71c0c65b8fed2e30fd52d49eed3126ffa707daab9bb374b66668eceed31698608dbcc36ba670d0bf84503ba13ddf2d0816210db67d27cab979fb56cc3b5d819c2d44bc82fa7757737cd5f72e7050c516058fde58ebf7ca7fb064354bd13f41f22bb62a1de9787f9150bf5fec0bcff0c7ce3e54b3125af7d6aa65a44c13f15559046315d7b81914632312fdbb66d2ff288e64379d43edef3aaa901bd279a58e0ee0b0c0cb8c4869aa769130f9a9a1a9a1c2fbd7cf5c2cc3ccc0ccdbf3ccde7b44d598bf21e193015035e976756411a4514fc5dde83f9b8ca236ff9a784fca55154210e45145ea451b750852ef7330e7d0a7c01b7ed72edba1fbe118b3a1f6ccdbb13cdf2f71e7fd5bcbf3cf2570dc8dd3abdbb3c6a81bb0d3a4a899defa51e8614b8c4061a5ff3fd3c6a5e48131aefb129061a207beba5e687ead63bbff047834b6cb0f99a186c405efd0e4d3ed0802bff0618576b55df3fdef2573561fb7fdce583a001b73fa7579f9008a379cfabf9fe96d4a0c31058cdcf6f09096a4021112664071b4850f30d800d81d57c8e7bd1c4c21212d47c834b6ab081e66d80b169470d28a46134ef349fea5d3e94fd0112a6aeffa46ee83f40e4d1f58f13e418d3303dcf249052c414504881d464c6670ce9f9a20647f800e5080fa2a852b354cb2b2c250a2d4b52fcb500978fee901f7aa0c8a89ca007275fb4196210d15df9968746450c8a82d8810e3570b0a1ea6f64191ed7089f61dbaccf7d9bd5d9f1a3fb384429bd69163fdd362aa7b452d250fe46299d9482b53a159694d6671695f4e76dc26406b4eb74a4bce1b7f485f9b4eb5ea5b25c4f08743f7f9a5ec8c0ddbedb36c91302f41bfc52b62489889cbb577102083d4d40b9020a285b9011230387db5fb154b933979f60f101e7030f5545ca966ac78ee251676666fefcebf7811b6eadd5c59552a04ab55d5115f136359a7582aa59a619a86e98a0ddcee999cf6953dd4a949656cf269d9a5fad40100469d8d0a0f17ddf67420926702513bef4cffdaf9e69ac5d2f1f9fcada05f3f1e9ac5d311f9f126997ccc7a7b476a53eaa76fc45009e76a18868974b4f013e3e1df2d7003eb63e3e2da2f4a85df6e3532bed3acd7c7c7aa55d4b660f637dfc8dc85f00f8f85b91bf6a7cfc1ae4af007cfc1abf12f9eb848f5f8bda65d4aea376596997e9e357a47aa55da58f5f93dae57dfcaaa55ddd37e13e7ec7d32e133e7e4744bb4af8f85d4fbbfee3774cda45e3e3773eedb2f9f85d12edfa3e7ef7d3aed5c79a8fe0c7ef80da45b3c3cd6edc8e6efca41b9ffaf82b88a888c63ce6ad26c0daedff92b8fd6f420944dc7e1a5fb36a3e279ab573c31a7d87c766be25ac473dc17131a828e8888b9df031b76666663ebeea73249e692bf12d9974a4e7f232ff02f32cc4fbf2392d4d10a87706848991497d4e6b562f29c09b7a0af0b9ac5b28d009f0556eb93cbba03e7eeb636eb914f9cb9d2b72aea85d68b828d64713042aebeb640141d72f4751c6cea54f8a6ed841db26ffe3dcbded6b77ecf8bbbbe3cfda95525a29a594520a863fb6cff10d27a759eccdf9d5fefd5a4bad83826a41b51996156ef1951576d0b2c209726016df70e6f6af8860c30e8a43cd725a5d68de69405634ddc0863f585ada85a3590de2346b86829f1b3691eb2622580a4621202a3aa85d7dd4aed942eda2cfb7e9e710b26a543336cffcbde32f1b70f51f7bc73a9d325e699647a1e6766dafe5278bf51f544b2a7ff5abbee687c0560dce3ccdaf9ee7af40d5f304599897fe0c48c3ba6169eaef5ab33ca8e4c2403a583183b7925a4bbb5edebf87dc5f33eff6e43d7bf7ecdcb3bbd80e9aa9c2863873a80233bfda41f334e08a857929a80287c03cce6bb1582c098cff86b9e0167bc12dbe2ca0400c6ef165010565308b6d6c6e8d7612206cf3502ec9861d4403dbffa33f9c9c990c18fec780a18a0664df692ddfccd741def298b76afe289fb1de0b02831af4246a5a60fdc77d9ae55dccbaeafd7dc75f341f5a7f9a2f87b754e08e99164a6a967f0735cb1fb5a483660e7f56d04d7b2dd5ad14540be39f758b9168478ed7759d2ce6bbd4cbbbbcc97ea934bb2fe693a53e194cf7e0e7d2bd457ddd9f3ed357f2badad508aee348417707e9fb905bfdc21fa528249dbef421fd8b529e3ef43e99e9bdf7a494b2e47d32d47ba7aef42ed6fb642fef3dea64fa642574947e321a5ffa994fb67a9b97f964354ff3319f6ce6559ffa64312ff3309f6c0716ece53d17fba8ef64fa12ac8cc627b3f964abd283ec35a5a729bd6a46e62b7dcc57fad4577a986f86037bb1f559267b19f5a01c1ceedc874edd873f65953ef497a0a4117a5aab572fa5d5a3bf812cfaf33f998c2cf5318ffa6430fff2a74f66dfe54d9fecf428d37b946fabbc4f26f3c9623e59ea93cd7060de83d6fb976f071c98f72edf0e2a580e9b16fbed506139a09262a8cffbd3e7bde9cbc1cf88c1662c1c9807aea0b02b28f8de901ac2f58b149107134aa061b3aaa151cdc8c4a4605e5c2cea642a791d47cdd8e894de6758de01961f1bb13492b47202ab593200f7846631a1448912254a942851a2448912254a942851e2a14c3553cd54f362e0c5204231a376198a1950cc7021304902250b110f8ad1e546eec6fff8e53a18f256c79f40fc8a462ea13b7da8fad090072433450b7cfda9119e37c4c61e145c69fa4c1f9fb5e9337d4c2dcc1b8bc0f01a50bf260f579b3e93c7bd82e1eca9dc267dfa18f56b26d1adfef631b5302f074e1f2836821ed4ac7ef99b8f73d3c7bad7bcf6c34c5c9fe93393983f469ed702cb75d3f9ddddf26737edf61ccd7277777ffe30cee0fabbd10062b11b647ebc135cb9a0baf32f4b7181c438830679b08dd51136ac448d811e3c72b0f3f9c67c070224deb07a4eef0479b00d7fd58ead1fbf5437b007069a05e35124be20b12113f1e841c3d0f51d308174dd4ab7d180e3164ef172b3562ec41a41fa43524d6c2ac5c4364b3e4c8fa5d771aeff07f8c5ca723de2701d878753cadbe6c73b7ffb82ccbb9acf97866f35c11982ac5ef051f302fbee9bbb6f6068659095d360b134ccd02c7fce09db2fbfafd55a4bdb92109932817c108a13f068104941860bfe6b2496a12831aa97a1289972198a12a11bced8e5df2e435112c5355d86a2e4a702366c83e3cb6ac3375c7519647120a572361d126ffd22b3e89c74ca9b211e82c08f2a7a70400608206d2d8f3cae914a25c0860d008dcb31fcc2e918e5e3f4d74a44f43c26137d5766e0db4010c75b29bed15ed8106708dbe83755b1211371604b8f217c834bcd62d69447b8a072e2028cbf823b5d069221b175510100842e80aef0e08415306a3fe861084020a18c2eaae0820715172f0080efd8a8fc953364882c8cc0a4064760c28a99068cd07c504512b4f8e8b08518272e629cab00062a68d0841142b41f2a5664618318f0c00a10847eb8003a71e1039307e12be478800592c56ebd0c944392185e64a02dd88a1e1dff5e3908d2cd59b77b35c38fdbb33766221364c3a65d7e94163dbbec2d4371e1ced39d44b28c14162585dd63129767f42598c4764c30b3b9fc640b940b8443d265201c8e6ec81d3dd942840b94c3d1b5711928879f5bc41110104c8a2842c221ddfe9d66c524d19b73ce19c31f290f8c3bcd8a3ccdaadd26d2ac49bb21b231ac97882399b2194bbafd5782ae1014b6817a810d9dc786eee33d423e7487b0a113214922436285e6f9aa9eefccf39579be314f8451122d53644952cf17e6f9be3c5f1d4576ae7dbe28a26b7abea5bf0264051121232b5a2eaddd6d76b9a23b9faf8e23b4abc348d2f501039137ac47eff960435a85ad47d54abbbc0fe791136917039960e7369d596957e83d1729aac0347f318c7f6a4cfa2561fdb568e321d22efec070eb7930ac484c00c34a540218d62934c0704bc2060cb7da0a0cabac06d46184d62e07e2f2a5f97418a121416264a75d8ee4f2557d3a8cec1849622449ac5d7ee4f29df9741489c9644568ed722397afcca7a308ad089222488edae5452edf984f07edc88a151a52bb9c76f9a63e1d3424da15da95223cedf21f2e5f984f47119e224414218256d42e4fc1e5fbf2e9a015d18c68463bed721f2e5f974f47919d24498accdae5442e5ffbe928322b42a408115a52bbbc87cb17f5e9a025d1b4d0b418216a97a3e0f23d7d3a8c10199962644aac5dcec3e56bfa741c89c9644776dae5b3cbb7f4e938b27324c9912448edf2135cbef4d36104e9ca955abb7c87cb577e3a8cd480808c0cb5cb75b87cb74f879121235618b1e2c8ac5d2ebb7ceba7e3c8ec0891234482dae5395cbedca7c34890905051bbdc04976ff7e93052646464e4a85d8ec3e53b3f1d468e8c583162e508ad5d1ebb476847901c41a2c34852bb7acb35a2c38896cb261d46b448a076f18492e4867416693b3cb4270968083f37dc6473c85b51a4dc49c5a85fdcb406fc9a47cea45ddcaba0581d3051add82b21d5727ba39bdc6af5573d420da954d672746692269870c37ae42fa7825bfd4098729bcebc09b7fa95a84fdc7e8f62e64cbc8705fc0ac0378d01003ea446801f52246e7f3df25700405aa3c86d0008615f701b0807a24b690c8403911293216c58baa1f7d04b63806c0786f147697bf7e9e7cb4059866ea8f2249ece9ae3dee9cc7b7efeb9f7cdef3e16a2903b2ff375f7ef474cbadcac08765258e37b76ddc77b9a456364c3b8d393a442ba219d3d4d17fed32eae47d2859b554b471b916f936db366d1199d790f9366952e4c1836f49eff41fab6d1e3b1058a32e58675ce208a107d12a5b64414239404a376a8e4ec5765390b95a111400000007315002028100c868462914812c7ba2ef514000e719c407458974aa44114a4288a8130641402060040002086080d0dd1080080ec7c535445e532f93884fb0eb83982229da9ebe42df32581b4ec2f3cdf23efa6832929dd809b771201b0cac6e6e7ea8f85872ff038cc3483bcb1f4e2dc1ad0a85cb0c164668849af71b46098dd60fb1abdb43634a28c7e2f2518fbd58ae1136b77ae8a86d91dfcdeecd98b9964f161c22ba06e3cea35a1875af43d9521a34b0fece34e24b679bc287213abcfa2240f8ee0c3325ff95e5a83a8a07343a300082c3ebbc8445a3015f4e4562390396b24379290783bb61153be83f28b969cd2b4b0b1a67fb50f89f3fe51ee713861a9fcb7a4ccf70c1424334afa6dfb0774f90748b5658d4413ac6a00be8862e7615cf83296f9deb7e3ae6fc3477d1216a632437897363eccf444aa60c0beebfbeee071eefd1bdadaecd75a30586708b0febed6e6ff5b27b23bb79fb7df7e51633403af25519bded79d84a5ade66f45070c9f9b5cb095946a8304c644273173c273e62564053ffc691c993c8dbd624bdc9eb9d08e2d4dd0587b9d02d005a4fd3c79627f97e5625cd07275fec3dc82035431fc33cb20d46c91b2adeb1bca3a7e3c75d29d01857f935acdb75f0dd6838e0a7a9290915508f93bc43139415c6b6a6f8c9f8db7b91820d1bd7f3b147aa77c44379542500518dd1362b8a9bd26bc4083495159e04a56ae82d3a457387a9b0ad41c398725caece50c4b5a38de0646b69a40140d90c4ee70e68f2504772f70394c1e5e986a2e612d9f868a72f05e9417978b83b90237a2fef1693aedf6cc9b3d478591a465c01b9049c59f2b9388e18f74419c5b069357d24027412b2cd620ee2b391ce0ee6a9623cfd1290f11913d54d866a2596aa6e68c1d70d3de6394a2081e5d7fb519fd1738253f5cb5233b63236c45bac0fd7f5cf1e9ffac1cc23eb6662f9e86d8eeaf96b6b8d3a0ee9a4754ce578f7a5ae1c635c7c5fad91e993f72ac716a5deb1841b65c2f1494953d85257a1c79114b1ab148884232df06071d9d6d4c002949aea5b53f2b35c6dfd7bda3ceced5e47bdb7c7659a302462d94baa9203198c336896a54d43a491952164730036d7c6f6eae6f58b6cf0d5af4290876e6c7b95d6fbff0ddeb9b3378fe59eda2553e8322060f6832bf76f2a4c6d4d14550580bba5b311078a46dab54d0e8b90b047776b1f91acddfd2b04353f29c14ca5ffcc8b21daf376e8fb2dc971d48eeaf2122d23873849cd7a1d5506e69a3f4bb06ced8a6487e2f2b87d13b3aba4a70f51ada9d91effcadbfe1e5e285baeb857cb4f6a5721c68a11012e1b035a88220e481f4ca57b210a70a2573a2ef2a850b79e6bdca225dfcf686b2f251804a66d86433ee06fee373022f150fca6fab387ac62473434bd1e06b9c0585aa65be5a6fc77f6835b841b263df1d864d681ce10112c34b4ef04274c6ac8d32058208aa8616a2166766e5da85e4b617449cd2830781b1bd67758821be9016c8d9e59434b25c2a25d0e01b3105a5b06d82ce6cd5c71d53ca48a41fbb9429be943daea053391598c07d5b86fce768bc0f9c902d3721454a6b6a52807eb59bd5213843ea54f201340c48489d821887b6b19a634718b310524026082952f0f8983467c01416eebf4bc954404a45d3ae28bf008a68f6e3de1abc481bc4aee9f9120595fa910310d9d1a6f23ca0d7ea7b966e82b33c708fd2ca8edd0b709123ec4f1cac2f52be1255882ca7db0d48886c5089e7302e0e24d79c3ff204e1dc475874895df93ef83dc4e7c2d2acfba2f687969ff43ec734fb701295b075d72b821bafcd68a535620632c4de16c64c755f45b8c7c9fc64d8aa9446d818876577289f7385148ac94ecf7eba978e995a5ea99c0b6ea35dae8cc557e062a99f6f50c6ec9471fafb3d47153539d92760a07769b4dcdf3200dfe884c0b613b0609c64a286e36c8cde97971a45e4d101181a4fa4ecee5cd672f9516825a0d50a2ec440d707942fe1327474d276a4ea827b21c4f72b2cf3eb8408e973e329be52b170fdcfef02c04e0fe2c2257174b1ac6a5c35b2706f7e4b82ce9e67acc02c3a36e1a09470f708cdefd666a8fa488db83c0e414721892eb660071fa64ca7810e6074810560c36b84e36a1095dc445b8cb32e8ffb5e010bea2e06d10071c37de89cc2dad34a8798c36c7c266a6e22d0be81d7c006a8a2eef167f68b102d35853d824274ce909ab59d8f91dcefb90dedcd4db093292d03352e08afc99104481fbde097b6b1db05ef3c867c2fd651a3fefd778e15d73fad1df65e0b9ca534eceda5be05569ddecb8f9c1345a822177fb8760ff9f2faa88bf1455cba78d1176dc9fa70b904c2f72c8a72ee7b735b97e3f2e160c4af054602b0029019d0089163f65e46179c881b81aaa9f14061f218a033316e504d372ee84d19c7101056de626a3cd03415636f05b30a67ec6ccc5735d6e7a50ab0e5f1a6c9318062621a7b11ed2b29659b50502a943384a7067d2fc34b3d64b6c211cc6e9dae907907d3f44fca0b8a92c60234274863811a1354a3203408ae99409a04ae5120cd0ad64080c605692448d69008fcdf05ec0b374f4edfb1b6a8375ca56a8152156a9b4dd7d9feb572d5288046026b109406c135174853813510a079411a09d0b8608d04791a3281c4f4cdd3e8843382b879c224913046fc2795cfcd536eb786c83438a7b5438712d014e5407810c414380e981881d0018871601e2062eac33e807d84ffb46f472d5d9f934682342f5833816a164413813508a699c09a05a6a940cd08d25c20cd04654d1281fe4bc057525f959a5682b86681342b5813813417a429011a0bd69c809a05a24960cd04d32470cd82695220d09008fca6714c4c673cc1c444d61f44e80647f13321aec5f2702c6a56c4b7b204ce8bcc8488d697c2599137fb4244abcb727517e2e4e956114ffb78eda877c15d22d4e9d3b9c7f186b8dbbda2b414dcd801f91b1ab52004e9f9ea6ab009fa79ffe24ab96aa8019ced05a9c5ac9a9b62175f8a80a86ebb37833544931ed5b0dd45ac2f7dceef0805952ab285ff76be2eff6b54b787087943cdb86a1030785c6c6299b0278cab5d36402b3e6be9da866c566811e38a40a2ccd77436eaad57d61f6c2bdd56bd0377e215b15aad7c5d5291f1dab489ac5fd615b89e6b429a3e37597bfee3678f07d55fad56e562e27ac2e1011faafa48dfeab037ee323fa65e91e8088ccdef6df3d48c297699f066735e323bc380bceeca1c9295f02e6a2447d5322fe794c448e9c3f9231a99930ae8113e4e65904487416f95fa52e199ed9298480f564e64cfbf69c5c0e604ce60585e57a5f4535c0b9d59cb30c1de76ea58aad3e4c67900efe1875706dab48a7604f0a93c16b86fd2321c1ccf8c37e58a260b5a98b1ad5911e1b4a61515b94e6f8e9a567f6df7b96a0f6c095c96f2b77f4126f7466da43d154ee77d99fd9457731f4ecaa21fbfd18b4687997c8b44ba384ef76674e96d0b41306f0b3bffa537df2ca90ac0cb95a58712eabfc365068726d471658efd7d63fff1c73cf5f46acf4d59917fb1771a1bdb735b04a9d73b35260828d90d5ffa790a4e6d52310c88fc55e6dc17b9a31eec9c7afb15d81a3f9b62bb198a76d96a3190beafa70e0c515c5b2496771995553e183b32f8dd87b064633764871acaf9007baaa985eeb1005d0221d958c725211b1eeb817d661440bb15072958a232829855743d4f4b68763efdc9be5a203f31198c07bc36b780d0f306f0e0bfcd6290c0d98febfb8f9d970a52a0e44ddba7929443d990c3ef2acf1180c81e3e90326d608e8ae98a369c26c75035ae6e6b6b0eb07b2485ceda3b534055d324cd86415890ab93ece7e8644ffc8184f12fee0869a95e8b338aa2da091ddff6421e8dc5f95083b40aacf555f03d9ac64bf43b5d45aca8fea554631eb324f30d98dbe683ef10d71ec7bfdc0ff18ab747c95f0bd58c9b28a2189848e4a371f862a006f37597a722968ea229f354060336555d621fe7b9a40646c90a36fe2b8c7185b55f705bc2ef9e6d8dd5d9694880ff4f0abbe85df5a29ce3510788e118f119822af680e19ec400aa96ab8841b00e2097bf72bcad47f498317fc58c490df52eda314508f94ef3de25244cbb1b2d07da4985aa9180a03043cf5a39dff0c21daec4d78e685257239c69bedaa945a0e26d52e5b558ee100f885d9b77e221c1a7d0db468f55167f235446a090ffdb397d37c7474a3ff7db872c5dce1131f41f58bc1950f579d0df52eb8e73a126b1367c339b60ae6ea6da66c18ca686cdada50010c4d205c1db2e7ae80a479424db4f3ec1dc85b355aa8d2633564a1671d59555e47abac5667166ff4e6eb3b6cacc8292ad064ce0db22843e2cce2d8ede74ed1c403c25a903454e71e484f45ad51604a3b86866aa1f60cd9ab15f0ee1ad825e5bd14c4f37d25aa3c73266b996fcd818c3ae354c2784419f394b578dade8b78f4a991ca5871ba7a1995ddaaee826cc84cb6844e072cb6bfacb9392b894a5ca30f9cb9cbbb8004f74f1ff332e972395ab903d44ae550407637079006ecfed71ecf8d1180e9a8510604cb17efee6e236685c76d3f4b2fbdb5e8ca9a46a21e0c083082b5c0c93d6d542ddae26c3fa3b648fab07b2db82d3a1a6862078015ed82f05670eb09196b341ea369e6de0087b38c09deae250cb28776ddc25da2b59f54d878d006ab4826baab53cb3b88e363ea8a9c68d1a7f8c3b56a5605d5e0ac830b3c37ec29bcb3c66e3b40c6dda660d47ff618c9b26ddf0dba9065dfc1c5a20ac36d8cbf7ea6781aacc18b448650ddc472490c942ee73fde1d69464d44f61b67ed05a8c565696c894efdf35feca3503a7fe7c65af171aca0d6792212071b640143c1ac4c43777dc1db1a7ccefd3d38b9ac72e0227f49b99eb8b7947111586ec57a0b8af5e872407614cfd5ae67b0e816dfce7711c4deb921e63544554d62e16913b877b6fe85a38fd04ed7eb8ddff991cf93b2747b580f1ae0b95aa887cd33878e7dd7c3a0014c316ec1604a012d03458579b26bf682d090488a86020645413425af66c1bd5f949126b4875a23c8bc30f031bf28c992f62a89dfede0cd2810faad75fd02545bacda18eebb0991f9b74349ab9d16084cd69a140790deef14f3eafc5975a37280c2ff7cc3fa9f93957d186be84d48b6798a99ece454de60d528ce4418b03299ecb24d4a13a5c5f9db7ffa26c1b76862b6bcbb91ec8fb542bdc48833386d5aaa0707c3c2e126d72556ebd12f87be338fa9a7a98619dcf57da42afe514318e87939d84d53cb8997e192a19656a8d99a3af3ff84725fdbf1064770128624e0487bfc97eb87495ad6cddd784885ca3cc9ff59cd8b8d28c4955d28ea33bd9346b068ae9b6623a017ef96b5fa436794367024c9fe0a94d57ea4018b08a960538a8759cc4276b33b95100eea8b98eaacfa32a02acd90bfdc71eb8ce8a3712fd0ce2b1ac7687917a1c0b53b216d09b5cf63b7dccdc331f157278f9129e98cec23dd977ff3e118fb71f8443242ba14aba5349bf02567d472475af399ed4e8e77a28646a472f830e54f071c4280f0707d84b08cef9213a1c8ba15e36144682ce5287629ca7a590f01e4ad964c0a55588fe21b106a0875222645714f7b895a802b39a37b1f213857f923f3b7851d83a9dfd47bf2882bc4f9301c8b940d9630ad2e5509d82bb415df32a768750d097f5d38405896ba32c5671c49269d391d39264574b926343d134a4e61b1a9c687846031f0dc9b417a7bdedd3f6875f2dc281595007d5b8b7230d620f3b92d68256c0c8478f2e6e4478f219c2e64be2ea8e0c3d5af60aa2bb219cc88af458b6e8dac96029b6b1f3b7cc53f3bf0afaf5ca584a341cae660fc2dfb38077dafdff4c7ec10026541258822567acfd17e46dcd7994ac12c8b397e9a8364b42760f8aa3b18defd176457b34c41bc8b791d151f6c57d34da641f658a1764764d3cba5c13c86a99838cdec98f6613c7a331d2433ec118f23662898eea16bb8080e4ed44511106f2c28ba2f39a91c2b9fe7a29dd377688ca761376549e4a4bce38e8e4e5e313ca2ea1211c4f152717029673e75328ea5a15cbc2730ce087805d4dc174b0ffb5929b60f748746c28df86b6a9d0d6ea7de1086733036a47e5d22c1e0d4136a607b6a38230cdf0b0b4f6ed02dc624fcbff4dd5c817d2ada25f6efaf6c51e1bee58a4651666822d9fc51b29fa3492a7d5c27f0b35f072758abca541e00cb274b134ce4cbdbb8185b82a01706826c5f103795b24c66383ea4754bf40ebe132abfd7c8c2a44e2a412d51b2824b7f9e8ac7e90f6522e708bfa7823cc7c085ee8319eea9db770d9fe23cc11afcaa8cc16d8237fc7355f640aa553d0dd0e4ed7ff1858126ea7810c9b2968d92a21446ce4377f89c9ee3b64547203e1451b792adb03bf9f19fec9e40e2d7f1d23a79343f1773442b1077183a15a8ec2f3a2411c9b6b2ff41a65ab4aa2e68201ecd394c67d96abee5a36130109b1f39f18afe8821eef96dc687dc00c0bf5707075538ca4379ce8ca48523022b08b0dcca2a189bd0f4d10716a88be74f8740dbfca1cdd2045866ab2507739ff9c201c49b00eaca7ae7410ca72628205ea91c132e4af79f68780ad9b180a5fb6d66b2d043da4aa340e5ebf9321c65122fa58b832a89291662cfafa5baf975f19b61df77a60629e652cef4f84e5cd966cb2fd0423428596290bb24db5de4a4018c83ab2cefbbfec3044e81264ac8cb48b41524920a628b76b0794e0938ee26fb28c7bab2277733d2501629b167decaf8a56343c3437277dc6c21f1bb836fbca073679202a1d18ee998dc5b3ee266e45d4a2c5bf3177ad7e4b784b1437ec5ba7b4f89550d84721ee57c52607696646178d8f50d9dd147f3d22127ff1e64b27992a9a9e39dbf1780fb983d7fee6e384f072e3205bde0b5135bfbbbb73518444fd5828dc44e4a6005f01b8f4d5777d9d60ac47253aee53d2c5348d19fea8474b42167056e18e4767005408e9c10185001c5c5e21dbb1723e01c8ee9f7a001263a5d1820a1b0b5b9d851eba3c4c971f6cf49b51723ea8028d02e0cfdb390a7f859cd87a06bc2bb54856086ceebce38dca85ca45b1b1a526da3eb0b323d77f83659f0ead440e52f82df4f6beb00ebdf5176e68f2a7d007ee2bec57946f0a3f901a95f5a6d80dfd47856922e49b73c9d1e1041a575046c24a309a70a0072226f182ac4ef769e960e17971b272c712ad2c9bcb6c32129a512553df47c04335795424b660421991d3801f8f40fa12cf5a4c1fc39b62f7462f13189cba2408ea54a97d4fb17b2d81153b30401a2450bb5ea7eefeea4a7e14370e180950a2cd8957f9b161a8809a013cb9315a52420df675324577670f960d451a4cd6e326228bc74c6470075a9e2480d8890ff524b639bc99e71f775240aff488bfb8663a1f2a4e269804acd5f91629aaa20fcec856add00bc8bb0b732b929612879f307b6a0c442e8fa96ecd68d12ce6047416c45617314ef552645588a78605d55aabace2dc06334c5744031d862311e6aac0e11c822fe4aa924e6f3e2af1df66c6569e2f5bcfe92540b0a9d94368be78955f68310d61519e3ccf150f950941c530ca6189d00b3d4debf4230765ba211557d49471e76b79e21ee279804d047106d111fa47508d04ba83bf191cf8748619ba35eaa1a73fd83a7f738c21b66ab79a35dee6c11f9b1d3dcf8ef92128464aefc50f8a9a753db4c2929c0d8a610cbe618acaf8871ab54d03032c9452fd276003b4b56127914a2b25b151cdc6935771349e9f632f235387b46b32a5a914e3b930ae6a6c820056c050e36f16a1c31078b72043521fda6990207d0715fe6c2daeb85ba9763848899b02b2a331a768c41df80d9c3af52818f3aa24eb5997db0d97a7b961990990045156293b93b59a4b18e14051eb8f1c87ce22052d839030e184018da9c89c2322b45c9ca74dae5250583cbea126a0d9feab64f63132d9eb3fe80927cf3e2658dec9b4e0efecff2f25e61d14b6ad803e3471e8e57b12e8d5d59cc06da432cdc1acac974be240be549f50b17efc601427720dcf4df1e8b5703c4a8698dae7afbcd350f8916d4e7fb65d24db08ead29be09247b9836f7d12d0571664db802c88b13e81ae90264a8f1642c373049cc40749f52c325c248513e267db97c1ee16a4d212c5fffc25c7fe12d5d78de01d1af2b43f2cbf078722c0c42b7070f95ba41aaf7846a48321010c3adddd0d151adcf379bd3d3e04f45f6c95ca4ea14383d60c19b5c48cb2f6ade26769d312aa7220f5c3764abfe444593bb61b3c365d49d64af3d8248c492ba92e98f6ab4aad53e2e30e07d71858bfad3a2002bed61b78a578fc13665b6952550f609c562ce3f0b4c9fd98523d4a4b0ad28b0cd028a010137efef678be3723cb1ec182cc63647a22ac42f53eb2d840b08913e888cf63c187dc5a80809871ceb0c09f606b000562b4ddc351d6e4090b8a74e188254a7b09f62233b1cf7290484b0233be94baeacea64fd7928eb4b60f348df17e3e24b0408db4c67d2e248fbeaf0224cb9ed75225e02e395345e84c81778e40a350cb2fc6342acf11b918a84436e45557bc467251d96beff25167210601ab44d22659665ff1c7c37fc8bc1bf4aac016cad86103ae38e570bda2e21916a43f5eb2738e255a09da4095cd176ea4311bb70044d166a2159016cd33b068624cbb60256c957c4256690958f26931790ac9910b2be31852f5db5ed71993cf51256e44bca856a9a691544fd358d875891a0c34c6ac2976f164a209524fd7df515fb4aae00d57036c063935124c9e9309a4b4e144d5dca9e724abcdb177e835e20ba8663cde7a23b14c4ebd807d50bd53098c85b1f1132c99141e5de4a1c83dd050e33c3269462c10cb24b8b8d45e689eb07d72467eabfdc6b65cbcd4344c11b3622edf0e9c07517994d5004fe53fd59229eaab9196a4595bdd6de968a149a00e3ac8572ed4c6d9482a39667b7fc31a1cc006f9613de3b976313fc27bd15457a8a0251a13ab41f4c0cfd2dbae20b6b518e20a92c0d282529b8ade767e5b17f154f00b7655ca8dc08165315c83885697a2426f5a2ad82f5aa36d64f3f50abdf68a8c08c8a919e30938e310632c94a5a20d5503701e8daad811da38ab96e73d859b472bdb9350c090ada364f9c37e351aa4b08a02fb5dc5c0576f778a10e8f78682f4ee9f1838fef174d4e2450651ccca386e8a5ae09e3c34d98613af03769684707e803bc55eae377dec401aaa5ae960f5331c97f8711f26b579e8b9e21d730204a82123c9d306c831628d4f01873d919ec8559c5380839de19623ebca391bdb5052e5f7dd52735c4ff978059da176302cbc19bad255932802510abad83b4240930f50792aa2fdb3d9214a1543281d5311d3af008028a302f4a3e97d971e06c05fb3dc3ee93aaf4682c0f34b0cb4ef579ef48bccbeed5c1fe82cb0c422e095ef3e19bd3b8ddb836dd839624f2b0a407dfca198a7a4427d95f9ecdf3811787da9f83638d3d0a0936cb1e250ae08229580e8531c00800805fa54544b3219540c34fa65bee2ecb3a5b26ff6195a538ffeb3de99d4f5d4ab6b42f52429b9e6bbb6b6ae88d72012f2ea7f63921acab3e02efdeaf5348a41dcc39b1822f74b6d66451000bb841e46eb0d41bf6d4d4e9b3551629a4d33194540578fff5cedb8e66072db8594d611a4048fa2754c519e4587ab70b0422dfd4d1c3fc2a12ef41807a7a117833df949db3ed5bec1da82a2546fb9621299c23bc340f5d111df60c802f42b8ae1d17c83d7010ee4d6f7aa90f479f537913ea015ae4798d25e1929011534b3d3f167af9b06c5679890dad87545d5f87f5dbd1cc77e8cacec93a14833ceb948779086792aacdbc5caa0bed9a918099266c23eac3402fcd4c30d10f05904909cec1d379167e1b3361b7922f3ac073ee60cb95e3abcb5d1ed56b205f437f99a8216f31384cf801e243b3e2827ea0c1e268dbb06d1f660cc69318d00be9613819f5ef4e55a45eec410eaac4da2331e70bf4d17d1387ef199b51fdd96228d25c35f95bed35e08d2b2aa9737c5ec640226fc6d719b0317b19fea6bbb637275590da5c690e5e9ada835cb424f4b1ab221098243985468fdf653a3e9fc09e485a202a166d63f016e5c563e8789993aad0b758176cce5042d0b0f56cd8ae483bee5250e78e6e8becb374f139d3fd4df3c4846f6922796b8963f460c5b734245a911c7f2340201c1a48fa15ec7082ff61fa3e153fd8f1784debd606118ae51f66e91ade0840f6a66ba48b33f4cdb911dbc69dfd54fd31cb2c23ea20bed949c19e4464d19c1d3fa605e425e826e9a291271caee502c342299160712819e67a4346b86559887ceb1449297629727fd5dc7c9c09c054e9a35078112bf8525571202f80a75c6bbf18c909f56ecab8b8ceb2b90c43a60ceefefd3a7b914a83c249e495a7a38554dfc1eabc4b37036f1174c29cb5a2510188b7929a3aa6f955bc7d0873a00d05b35846e876d9c3f77231f0feab991dc160bdbd0c9bcc90c922700d22305605d0a856f49665040866ad617483522bfb385248ca337886ed8d3cdc6029152d896086a6a9b0323857c536297792fb01ebf43861572d55cc99bf9ee5248a9f7f2067768cbcf552341cac6665386d10dca4e960b71e0dd50b4abc4cc42b97074cab42fe7f10374ba2df06367039c371e330c47f743b1f784a4b38a621e53c845e761ddb8edfa408779f3e3e65108c1e4f7246f1a75d6985b46e9a33ad38e984f421482936c15a53effdc0410255e681d9dc82ffa0a00df4ee67114630e40d90d10e23d8a2538bcb198859396516dc6e066baf63f9275aa843c23c9c4cf1da18d8fc09bbfbac76da11ca92915606aa440bfbaf4c5aeb7c2e230a4ebcd6ac4e83e45c6b017e7a6c7fc262a53326fa9b6cc2194e8544782d0d965ba7886ce57811be7b37319d5120e91db153b5494d318879acceb6d6dadfbe6bd4b1adaeb4d2eba56fc81251b3829e0e2609743dcb12699a242371f57daf56f32977cd09c19093c0529cd498df46575128272eae13b6b89846d6635884aeefaf69dc676b8bf63c5214f29f2ff6e6233c439f0bd4f8ab259adc019d4594a0cb76816babf7b37d2a26a94d2e28c9678229b26c0dc50c6ae9c97e87ecfc0f7afd506078e14f7324b9f3a5fc537e2167e1025f2a82caa2ada84a5f34a8fdad0583420b384ef7b10ad31abc75ef0857c5083498a0920e89a861954e48608fd508aab04cd04682e0daa2440391d8b2fb98d5ef58076102673952fbb6274396c87c1c92747eea2d9f762db69251ac4a4be8c7b6b59af3fb39458729b48269f2954ecd0020559b1086b329d5d9ffeec9ba1023d7d7aa1df47fc068dc8e86a44e5b8a9ec4bc1666ed6cb5d46a9404cd403606a61d33b040d9c7ed75872c496cd90dfe789e00a1a63c475390a7b70c10361ad350399bd326c13e17cd475115ec9a906fc935a7c1f9e1a9aee698f45abf89938d14ad7433ed378209ccd2939f5b419854b4eca15283102152016efa3c500448e5f367af48486437339d0c4c522269afc67947d7ce5cd8f25fc30035d073d858a41f5cd7025487ecd349374f8254acc53c4415ae160cf4ccb787d11333241950b405b06b42b12aca04595f45db6ea180ba636f0c0e1ecf0889b6568572eb0abe7ec56d387cfa833f65076df38f636d1f4883c4355940de07f68af7cb632e7162016cea1d2a53d211c74866522b74f8e681615b45ef266ac8eaed1f3504241c74d2399edb27daa71c191075ee05f071245b9773d6b91c1f5046dc9174abeac8b2d9606e95e53ce2f8532188e6c850d396844eb1f590fd5658d8eccb2a571183de918b215a89abf50c86a38eb0134569a0336d5a411a47df326f15fb04d37ba355699377974b3206e1596209809bd7de853233a81d6ad4c4c6ad544abcd463b06ddc783de1143d40f2e8d65730d6f1d21f21d7e7de5db757e19c02f8bb1fc7c6f5b5b56d38b693205e9fdbafaf85dab131024b43842c9440e07d886f31be59929dc25343815648158e708683fd992c2b136db326927c83208af7755d4f73a3e7320c489dd20a64e10328523b1b96821bf01f50a86a99c5162b0a0ee851bd5962b36b7c5cd7419c349c41c57f423a83c14170ed80b021a4a716055763026d6b8777f3aa26ad00ed87378a291b0bf5c76fcdf86c70ff65e92d7c88b52db0c9ca46cf5a505d81be43578bb8480ae6564ccb323f05d27a0b56048ba84528109eaa7766d12ed0b42da253064a1c54faedd59f227199f0399ea5f77a71c2418fd0b9b1682d902a79b7a9665d7dde66e99e1b4bbf247d944b8351169403cfb041049963800da9754a6a3d69e418f49344fa09e77ebcabcf89ca3a7b0f313e1e14bd84ac7215ab7f68380b10110f4e2b743abc505d46650f5a5a2cf70c948fe868821ec9125f89e5b49d475c26685ab77a6be71a9d9ba734ff8269871193bae14d92ec87edeb5c233571c6f2b4b9a45e6f9bd8adbabe6d48e9b4807cb8e0dcc1bbc5c845b70f31481395bbf1bbc1c2ea4dfe1473bd747a90b4dcc2b6943eed0a084a7ab566c612226851cb0093fd4a6a940b0f0c7bd7512bb561be4ec4d808ae1bbac633b4e6f98b29189dc180376055facb31deb183eb3108bdd19cee34b1fc3664fe90880ed48e06cdcfebee01ace667b76b2497b534ccae812a43285524d0549ce21d975e51f9fe79bbcfed5dcb808d275a7c9d3d0bbfd5e9f5fcb4ece3518d7b9d019fd831c3ee7fbabff901065666765e0558dcaadacca96a61acea83a69574de1556d0e51261824bf069e6646ab8d8b42f533e284ee826514c292721e175650c3461efad0fbf80e6cb30e9c2e957dd6a1c0d62a0ec415365453e408d23d12413154364961c083b74a6ac0350d41b5a613c108a4fb257cbb9e1ff70972b7108a3bc5f7a2b2f8d20074334eb7bdbc45df8b38b7a44b7189424e087352e9ad16744a7a4f9e7b497d80a429588eb24c79480f7c99cf18b2e8026913ed46ea48e43eaf1b316a29adba5fe2a917db08aed527aa3c2947b09f203c5f1c0cf55a30bb48b1b764e407b4deb76163c85d7f8213e287576cc9e0db198a489c8b3517ea731af08ab7ee9a1938772715c40bc632b2bb55253c0a8a1f949b64f30c84c6ef8c908ff4136e5564eeb59eafebaad2d51f11c686d2b59efebaaaa2440f04dd6b35643221e876f89327940cb3dd0d1f9dfa575f65d3cdbddfa3a2f8e1a059912dbd592c0328b05a27bc991b15ef5abc0259709c20cadabc03836f32631602feadab9b1dc260c33741094bde9339d508fdc6486677efd7cf8afb058bd5b44a1c26937dfbe51b38444bc1c38914817d6997e48c37244a5709e03d8a9cf1403e66dd590cf045e34a2e8d27e4ba5d332da67db1da71e55f46a2180260a2272b74d4758ed017ec3be8adcb84a741805a60f6e2889366436f1f8ec8a890713e311c3627bc339329b6a6ed89ce3db599349c48d877481fd2faebc7da5c540ca449a2335ddf4effe26a100a223f814a70dbf39e1b975227d7750b5cddaeb045e2777de695c125193e37bbd629be40437ac504b8147691855ddae66af7413d38385899d570999b51f40c745ca3f803abae28214299958406f037574825fdaa72d40d44e0c372dc82d2230657c603462ab0a3e6319184678ba7f6a6ba3e46958ad83115f9a23f64a5ee64ef9c2ac427d5f20138d56e46f3838c7dc8cb096214a71af14bad5a198964fc1b1e26c1373840b35a9150c2b3067e072f134366c3ff47b261a7d9c0b58ea723de8219750c4a2cd7ef5465b6c9ab81a01a9c16b8b2c7219b30d60d16fbd7050220f0909f22dcaff9de6646d91a77854dc54c9e9386091cf514dacf68bb2e0ccafb3512014ccdd1fe201052f4d4dfd707794101b3470137807fd5e20226382e8811dd2cf60bb2e9d4596583b6630cfc774ac317fb8d1d06d446247fe1d810ec4ae37af2276f4f9e43824f82b364d09eb7d799a521ed3196c1b87dbf6f597d49259f884ccf57cffcf092423d695241c7a28dcc25419e528f1b911d7454f69cec6d4563a2930326c73d6c9dcc5ac03098ca1113443cfe1380ddd7078d02d267c84785217c21ebc56f6ebcc58df9592cd542aa6a3feafa99de88d8c43562f817d2a0fa33d2ee615a837d0cdec29044d4c83005f8292f55f71018836e39c267fabc88c0a4027a6c90874cda9d97d0de4b6235a45c310b526cb647c55fa2a720d9d1791d3588539fdd25482d096a6cef135acb87de3d7dd00cde158a4752ecb389ec85d412897dd657b66013280b6ed46593df7089074cf5bf784852f3947b72a3941b62632a6249be9a49ff50202a3655269eb47b687e867cea10e49f7e31c5b5aac2f8270385f46d5e5da7c04f7efe05f2bda08ba2603877858fde91fb285157ea2f08faab0262e49f24536089c900a73c789e91440e470b419dc28d6c18092b0699ac5f63ff7660a8ef2d45e1a46e14e2c72397974ebc38d353e49e6606235d09783314c0ed1eaabc23d1f40d52ae09e0bbcb640dc4775e8a6bf1e8ffb2e82318823da155a6d359ea88f9279a6c5a959ccdc751c9277043b849ee2b5e4d58d0a149c4c3417baf15a9580e359a3938b8987e3e225c98e107e8a237042406c02f4fd07b46e68a46bb4341681abce0d3d01451fe6c1c6453df4db7924808adf9d955ca0c3c11c4539290d830b94ab7c45ee58feae67988e0ba494f60796eb3da83a42c3bc37eb8c3377b3abd5fce12ac9991993c6e5c08b13d9efa7542000fc5332f65c80642485653b8db3bb9e25d7fc59d78ae362176a052cbdb5dc25883864306039b5d3c07e7f404e9d30247b2db58d1316912c2e09c2c76598dd8c910b2473063ad82b277cf5efa88b2d1fdb1bb91787b1fb220e828de6e9565dd3be5332349e13595f2aeab830ec44f27cb7174afa4895108141464782e785fdd5850e628343b0f2a4de4c273fce5dc3e4c69f6c3950a6f9e57a28b501bb4d30e40ec47cdba891252f347fcbfda53cc99ffa99d98a7aeafbc62bbe895edfff2101a6ee13fae7656e2530524d34508a785c2333aeaeb0422092cd6a728f7e77f36c3478315ef605da4857dd94684406aa970550325cf6207a2a6b684a9569c29d9d082a8c4906a97f6600383753d5b45f53ba4899a18e0bf9150d4cd20fc80fea9b0be1940794e365c716cf68d76c3920e30209e66dbdea52aad8638a401feba9f31623419960eadc07de831846130f2912d2622f68c0f070369d700a0310563b14bb3b5f2f8fa1c771d3a70698c4a672bab3a00a21e9d18222543d7de3bc5cd437d83862420e594b94549940b4ef0f1b502e7b5765804b88d4c2529cb916e8c3e2b5d817558c65a1e437fdd02d433b2eb9908512b6113b5d6fe316116f28626cdfd57532ab274421336b089a94d6cfc44dfc3163ed9f4593fdcef82b5a6d086efc9236d7694c4090ff6332ce38204f4fb0fcc5c2dc333e38d4c41d1db62a64db29e5b9424689422cc8a49ce0ecddba4c2464417f69d4533b2ca89d3ac596d792c02d5e3123375d757986d5cb64381c1d679320b878a3967bbda72f48e23dafae87150f532bd4e4afa6f720ad328378df77225258a165e850a426960e96e1248f02b431ca67691ddaf5ee4cb46aecd42650237cc98319c8dd381e22610bc673e9b65acdbd4f01e23889324d0f1094db25440179b3c29be324ec7d5d3a86e2edf7bfc81aa406da74624275f3614e683bdc9d5a6080f65edebde3d5e0a2bb15240c4069946bde08dff15526823bede9a40f55242b0d88ca8d189d96c80da1760ee447f31c20cdbcab769bf05993360d9853711d0daa948dab74621f688bd8c239f3f7c4dfe832f5dd612ae38c3fd49797cfa906d945a82590583ae243076c91430791eff1e7ff28aa2295088eb0f5ceaf65ce5cf154eaa2ddc5cfb1859eb1d19cecf0f33cc555ef54ed23e4e56bc7bffaea7cebbec3916f2299b206fd866e0b7d909314c3c330c2a4db91ac8b90e97c4f7174384d4b434e66dca70565cef321b60d8f32f1c51bbdabdd358a6235ca519a5929eae0bc106a139aa186b391a35ffef071675e86574a86a837934d71f8d9bda423396bf1075be573052ff3e4c6422a7fff01e6ceaaa0aadc9f61f257b5313f6d0896d52df8796397a2255cbd3770c25022e223d4385e3c6eb9740da043c293923cce0bfaee7831f827cbc5e3de43b16c885bba4084181ba3b877758ee69b014865e2d162d6e02c38e5b9ee0bdead289b53f02608a2b1fad39771c570db1eb037a632cc791e16a353f62b1e696623623c71d25a1518560583b0676b68e21d07c288c2d4c7e9af21b73185ffd1e2cb78e64040ab4e6e5ea1cc7daea2f861fd3bbb54cc36345e1a26754748de2d2dd40546c0c2f82a13422ecb39e1e0a9d3038c6f79e5cf4b1d402516c0ce48ad413de5188c34154f67b21d1e9a1a5503408cdcdba8bac77898489607ed6c305d3d8d77dbf21516433cc90b790dea9eb0bd71618cccf4311d1d03d401917885228799bf13e6ef3ffa58adf877536500519e5e017eac9eb81530193c97e394b43169f51f8be3c5d60827e0b16546b25d0bc9643adc4a5a64b71df901f8a259422826824d127aa3b3b6c5dfe4f2dbf9ca58e7222c5a28bbec292ec07a1bdcbf46c8591734354f3838cd3d64aa818ed57c91b97d96acbb1e4ee547db21b544839987d9ae60aef004517b98e53408d289ad050bfcc5caf2548f19a8b1db1344cb8a16d82be9488755127f20583d47b6d1080457dc252235e6f659e220fa012319dd1dac78eb8c3205eb44f24a6391829336dc1624f2004ec43083c193dc613de26b861e4900bfbb97b7993dd6ef273bd7b0838d883c8de25bae630e14e4ab3cd75ba87f4bb9f67537c4f572b03a6dcf6735223441eab2d1aefefaf9a4f64858f57d3ab4b68094664250255f8796072c1729b9b8301676082810eb9b3642357ba18870cfde6e4a84c12a9f4fb1d288fb6942c22c90126b8f02ec5a94305e5efe5cb4c5e97eb7bd70f65d20db02a6104f9df7381eb5c94b52b3e49eae323e91dd51fca6fada70ea1d051cef73822483c1afc7e7efad684085679bcbfb1472a8a00b77e5aab0f45cdc45e5821e1a8b91764c884140d030db7150a7e0a1c88ec968dce979cd8cffcba5251e04dc0e3fb3ce86e06d556e431555cffb3547a082af158a4747f5b21bca8ab62d6fcfbf9c8b160337f0309e3e322efeb28245675e55ed4c4bb4022b197c47007f6dfe4de599e86b64a3e3c469c708f3cdf0ff7d4001ce285030c99b4045bf846318a8d0f2ea40de7e07b7c67d15fcecea44be28af6f3eb3f49e9ecd427651f17f772b702939ad19e88c71eae5e4a3c4132e2cc6f4722415e10de3a0e6e3d0c66e019666cd82fbde6590f9f8bfcf7cb1e33a7e72a29f54136659bad1508c099251fe06db1f26c11d82b0cf9579ea09869daf49c7b1121d3e20d63c46c4a012492ea8b22b9379225626ec8067f1b2989b02f9ec7c39ff43f179d87e0927b450e3476af99efbd072ee66cb328501c890632e9d0bc8a25810560f9b848dc37d9ac7909b3b16486d1c35b77c8272c392d9a272134614e17895501befb839e463385d43b4c2ce3563dba6cfc1d8019a0c9c58410b38f983c750abceea909dc77eb08ee774fac79b68c99d094e95ad86041abac2b3db65a87399f7d92860c77a2a820f9e0bfe71b72ce020e83af529b8b16eb3cb1dd0d602ac341f27c5501874172cfb1ad14f8934d7ff0daf8819ab8efb8fcd72ddffbe3ff6e4ac629aa570a93f87eeaf850b5200e9bcfbd02377eadeb6245fb7795796240e330f288199b21813eb02dcf877362a176ecf630ded65d16e70f04e7adb1ef9295085f0473f921d63d03daf059cefd5dff5d9eb8b4f4617f56420022c11159fbdf098ef3f0e2f990039a0ff123aae1ab14052b406494b543d5bf26a6e9f7d3f8bf0815183e5bddb342f42b273c7e1684c714764f222ed47075ab0526f237b7f10d75496279d6df6191322c51762a9810a3282cbcad16af2ea9dd4ec0185a25d763e4760f629a33ee6fb487116851fd023ed6abf2677957105ed49f427d3cb05a80daef4d5440b72f3280e4396d8c252210f263eccfd60ed772536dc5bf6b8bb81dff8b0de17672281357bceed64476f49c66ee1d956a90f4c5c54bcb63e6242ac2cce61da569302f59a5cc6d3f2cf9717bb2f5208763512c95d77aac787944ba793b544bc0edc99184a667d3563ab0883698c0a7822e72afa05c1d8206298cc7fa124d7cf83a8e59850fd63ab6d3210d95c2c8ef18dacc6d1246c40ffd0eb77ef08d7b36809342c14fab1f94d1a5d25ffd0b5d117affbc0bf5d3ebd6199914a7b00839b5d75611f2bd08758e92df09b48ecc5e06b2784a32ac15b51482c7e338f4068803bb51c485cce282d5d5b6904e58fe077c466b3fb19e5c77b31733ae8d581047c7745b54e02275568726dd7a222bc6cff5657577c98d14100482ebdeb4803dbba99fd38a41d716ff8ea8c8a48dbc9ece2daca1925c37e65bbcf11de843e1d5a5d2698395dc80fcb27c1db4d909af7d567c904028c58260f420577c052ecb560922ea499a211e34112399f4c0111fa5049043fce20332912bd2389d5b22fb4faad18f5a529c68a8e18fe4d77bb2945225ecab574f031e6b9d11ae622d4b770081becbcb58b826a76a53d9cad2e6c5081ea66a41f4b227af299d955ccf3c3fd0021a38283fb480c4c7bc977066c4cf87d67ad675139ee365a9c26b438772bc71d6062be6ccdb57e64e3fc9f73da61799d3d75be94131f366ce60e241161365ac3433fe024c4e269cda7821c238cdb17bdb7adff1da244e5b41dd4e6bb24d92a5aa4347d3bf1a06482e25d25f1e11cee35231a40fe86fd924b1170a1b048cd6bd6c165b01ab11f2e69101c746127a36a1fd588c461cb894f9ca81b5d62dbcc3ed4018ba416b185a20d575f3b0bcef1bbf3fb0f5a76c452045e77daba010aa61e99db2096bb4b86a4e180ddf89ecdd92ceed25e0546b1dbb73a1f3ac63a36661869aac8a5c1a0c7db7d962bc7ce2df770c59d9af7f8c56888f22f122b81d080b7e7f3f5057179cb8a781140ae08769eb75c1405dc3b14cbd7ff3add8e50c1c16d2200830aa4c1e0e1481c6e08e0281efaeb48d6709e975ef5b1b7fab4424ac4b52893b012eac2dbc9018bbad34359f9d5876e6806f281326a19f138352c3fc2b5744fb72ad5f22170e947d295bfd0f5fc01fa99db277f2b65f4af667a70142f25ab72578afaa27c71dea4e6d0010c4833b79da59110be48d79c23df2df35911da18b905601a7cb43f73078591c224e79036bf9bfe4779a18eec492ae40657692870d59a6e3d04501ee2bc12416f56642d70130664d1be012b8204ece050a2735111243e109369158c3ad6b3a069760d4daac5ac4a31fa6340e34506fb8e8265c4a8c6287eebcc405bac80fbb4e19da0d82c00861bf215c5a2292035d8a7022534e7263e2bb5dc06726e70623a40a3f9f3a08cecb554a5aee23317518a42ca27b511245720a641b262108ab3dca49d8613f159558ea29a4d8cf1ef6526907f8888c78f25a6fb2290c6764ecf139b31e6cb256663b873931e469314ab2aaf09019485259b3ce366eed91e281c89d72450392eb4c03fe944918f7ac90d67687d256b26cea2388a76ac1d80c15a145c4b378168c8760a5c855bdcea1db3cc55bfb9aee3ddddd9701acc02de060d8835ea6f38bb79d3b5d63bcb1743092e1c264dced238e8010dbd1aa0580565fd0edba204bedcc1262c7a84fa186120ad9e59bfbb412725b78652cb3ea4bae67dacec2d48c10109a3ce23f2ac6a58c1fdefe95244fe468961f3b6624b598b60b6269ad7ec67d018670541e4284e7b106ab54c3f5d0bbe70ffa482069f946778af30596ec5856dd8e63f980905bc3f969a58b721444268aeb4506a6e7d6c4c86bca9eaa16e7fe7a148b84000f53fd3ed98f49dd30a4b9ab95c72eee62fa3ef26bf61608ecc741749385cb5180b3ef5f6b0cb51c75485f2b58637d3e8fba43f21e4612a47c55c559c3ea1b6d2a555f3bfac0adfa7a1ef55b9222a0baf6361e4f3dbde3f6d724ff4997f3dc846952e3d213c03f95b8a82b9c20dc3f361e23a689f8459678d613f25b4351bf22e6eefec997b1f11173462c09a8a8e97c8015e6600b2a0ece207ff734d36e9b24610d77538ed5c5419681764c312fa2ee7d8666630bcb2ba25cb04731f50e11fea8856d21207a413e21fa38117923e96b055971d1a0c2808091434ea1a8f0a0a60b66e7e8c7c8a137de7bb4f7299fcac6a09ae147852a5c0af7d332c53e4a6da33d02d31427a981ccc117e42fbcd68683aacdc96424dff0035ce0aa28e6f05c840b9e73718d485b8da3974de37c34c185d7fca386938909972302cbe5303cfb60b34978257c7d68ee3f832944cba429018ab04ce44e6ead98e6a99aa8946f6d4dbab47fc03a5acbc7945777a51213df9d5d956cda3dd3021f0c9262a84681ad934fdb297df6c3b5f014982d406661b4fdc2ad2b087f83601fba92b185a04a46b717dec2bb972f096b384660fe044c149d68a90040690e6cc95e79af303362407813319df750d3d3fbd91f720314b1fe7c5123e9d1738d626dec4a36c412d5f092372be7c47ce56d35aae131137bbf12662bcb5e2d77481e5196be7d69fe339672961d41c311d0aaec7fe0dccef89c47f0f25bda018436ecb6bd9066e906536400cb74f4a3f9bb428bb8ff0d0a51511b6a9cfc327a319cba8fc6f0a09f13051f24cc55956ad35349ff86c1189ac13c86a8032f5ac8239afb555ad73eb6c848c8c3f890c98e7f6d1e8bfe805e7308237cf90c23f37946207ed281506109d68090b99ad1a58cc317080c62b22a8fe055cbe8f2d54ee8c2064dfccc28578d51eed781d3dd74921de2bce5395ed2cf32636b7f70d78c287d4c584fadc81c2ce1c6e9c151699b421331977abc547c0b2aebaa56ddd75321bd9fcb19604d3a29ceda264b3ec93d14eb7ded144418459c103604bca45c422b7341a36e5ee14ccabaef1f0320704a5769735742baaf2ce90d1b1945e4789f970c864db7046568adf398aa7ff2a6d0ae3a73b222bf13c7861e6640017d29ddf731f20b04a23523847a3fc6263d28ceefd92971baecb7f2654eb5b7c2ee22b62f61e0944deb8a3d29656ac0b2703f266e5a05a7f5aeb6b88fc100019c83ac1aed58688308706cfe04557368075bb937297d931b0c7abcfc4c921cf3b35ebd263b3257ddf4d37aeb7920b633983d95eed16e541ff65934e700c3d1db8c2c151469d6004353b4253b6435a97db256dcfdf7877d9890a229f6d54555ad0407fe90ecce862c0fc47138c403eaf65467374c6b45bd4e1012a23537f36f436bce40c8789d20a53dc928ec067aebae83c7006b0162bc99fc12a2be49c17a0eb52e4a94c3e93421d496452823d4319526a1a67a4d56b93a766f0c85b0db2e3787d86d1ce6a4e00e4e5a57b32c41c31a65c11a4a81e2302db2a8f7fd2799577525fad2ab15a181019d4a9dd896e37144f23c944dcd3a66c20a511310d0424bb6a98c2ca6371df250dcecdd75343202a154ba5bf264ce2fa09f080b69d7b7051dcfa2934db2d85a002e839d5a94a232a0f2df08a59ba9b61929147bf6ea1eaba90e468b702cd17b204b1583073355622681c167a584dd82254de9fbeb6d4e445142946a804d3add60b94be63471fbac84ca76779ec9fc83189b9f9c590274dedee12a768db41a01809255c4018d22a60ec70d5e0df01bd8357a6b9a6b2660df5d1da6101fa8d2d8f4e3dcf21bae00794bb08878abc96d6fb985faa704aa605fdc2687121427b48bffc946fb506efe432c342b6fd40fc0a94b7c59281d9b4ced291a16bf43170be28289318cd65574316b1a992555ece8913f2307fa4f044ea379a71be270e3c81e987f80a6f701b855dac24bf0b863f4c11a3162ae750498f9e2d73cb79e629f08bcbed2bf4ee92ec37fa893e0f5217adf3314485c7cb82b56192da4b418ece62bef6245c23d3b301c741023ef931e7144c758895c97983738b3a08b47949bb9140807121f6c82630a69beb9f74807ed7a6c4eaf2095eb2c918a9c9a56e16732e6c6097b263f34c501c48187c94a5bc8c2bbbc6f315f5098108e6593c8be4cd66d3288096e1fe4c83762563f632ee1402346cffb5d861f4d5f724508783f040e1f4b88a3aa6989a76e667f0bc70cf15bdc9b04de39a7d73354af6eaf541592b3995ea1e1396471a706540073b33a8998e26d4d0d51e53aa1c6edfc79fb7afc74b7a085eb99709b71bd0229bfaae15ae0ce9450d9a81fd483c3792ac0fbf24240f75db6a8701726eaea65f06dfece16a6fa31b26991b4d39528daa053b4f8f835b0f8a67d082879cb70cace48c67dd399f65d97bb3a497fa458b945b2316e79f3194e4922d469139647093684f69462ef548971d74efaa6e51f93c172f4e1d317a4f528d462041d537c887423737b655ef6cd1783545e3aa73bdfad5659aeeac5a34d92eb424668d3dab9a0067ce6c8a9fa43976e29187804e6182e0716ff5aa12e48d92f71ebb50a743a2ced1d70d53187c41b7ecc37932acf69c08d1076b32b3ec3fac6e45c6658877840008da485102ae7f7056f4368e4c42ccd1d68a00f03e5a532966f28c99d54fc81bb846fdbe12737fe0930f1d4b80fa8e4e3798b291c81eb97f03f77f0a14d0199b0e933eb2197b0085c3b38ea4250c82881208b79f3000ab9ea44cd64989c8a141ddd44744c87ff492aa438dd0fdfae4402ed1cc3872851e535e7c34076929742480f88fedc7dc3b73a889dcefb84728ae071800548275c4bd5b91a418b9165a8855b4b596313ffae820b4b48e6b9175cb4cdcfd9c405d30332d88302f88e9db299af2bf6b30ff1849443639f1c6bb456d31bdc8b463abc07aa9cdf500e739d871425c0aba74349da862c643e067a0ee5af1420756b301f6604a6856af9939cb89c95d88934a2c5f4fde3c5235a86a94a99759997cb4a284a59fbb147e95ba33969d5629124f9fca8380d7f9e1f558bfa5036d95b2510992d79e750db7be25ce2c5a2bd6f5163c5ec6aa17c4f50219e5a7ec032c65e3ad715238a037e66888dfb82944f1e275ecc3e53dccaf7992633454a574d0841bc931f7ef9f94d86b0953b7312270c8af8bc1c55170817d41ea3d640b491bd8d4a6e40e033d00db79751cb8ed88ae4e5a8ba11518bb5b25d47fc749b4d42628e07411fe6239506f9fc23660530488c59dfb0a0cf996540c9d04ba606dce774db9b5051472d0ba19ea9219f67aab8879f1db3eccebe5ac615067171f3d3460a13ac6dd0bc4035eb54e7fa4d762d9687cc16bdb4893a9bfe1ce95ab19ecd20e8b2baafb52659bd92907ddc4646e1c29ce58bbfa5907531d341c127ae88ac9202ec0f66977e9af5cb699047d20a3ac39d06996aa1ce61b43680cee52e4609592fb1622ec2eb50965ce6a2937b0e5bd66f3839c125e89586cf195b59a8f2ffc6dff78644498d9c91b148971cea19f2399a863f53b30a0c42e65fcf0a7920c415939329536bc868ddfceff13f7aa5091e39f58215a7f27c532accfe441b2cf19f7bd33300f1475864a79276fb9343c1669bb0f75a823410600a9c8cf31d2d95ff979e2b3b253fb75242f6881a2092ffdf13e1e7e55e52095c8597f3db91d0dadea52f207ac9aa04c3cd92bebde6af1bf91484f0d0b4a124c9b4c4943cfdd26281a7247fdc0b0305b900ee8bfa4644e503f223b9c6e700e560957d8bd76b6194c3e2c472ae9561e2c5e8c584b7d3aaff828c3c6ebb16819244668dd7de81c94e70cb5e644c33b0014eef8833555b80091e8408e760a1df165eea20d18c80d22a9209ef4c8b09602d9f408023ad57f144a8f5b005db31945c7e000083b2f56f25001f64313c45f881e10a71bd1c633ce894eef23c1e6526816f4a156d35ed50fc7dab8c1973d823f86be41b9993a7535790bf278f0416298a1dc60440d5b93fd6725c692a340280c1f9ded6066637dee2e4f85aadd43710155867903bc19ec0aa8cab3190b76c6d7072b535039f28a578bd38cb46e7776c9f32dbc6997f23edd6b3e6194032105ccd3ac87af82d128e314c438e13870232df81c0ffcf475f79651ce4e81a9d388094978e045005a2ebccff58927509ceb17bbc9f6420b7d8cae087fb8fd9630576b3fa2884b72e91db6f60bd68a639f6d7cd1093425db92a2b94f8495aea502e1c6296a0758a8f70c4b4eee51e73e938143add4b4261d0690db67ad0a8b5e036ba5e225610a6b8f54612dda7efc4d3faa51c6f1f5298ea02947d6b6b77f204471ad5c06da43d94bc7c0c20839c38c83a627f860b2e8d43ca982b9745df34d2ded65abeea4ad16f46ddae0f0a9906f9bb03b4c2a70ef36b7362775cbf72a557b261c982f6f0abec06ca3c3cb2de26834fe2afa3134fc1554ee0ad23701da71f95889887ae185b277067738015a2018deff8dfd5e4a6d9ef340b5af998c380dc1f969122b9fadd4a707a19212c7702176252021cbc20631c3ade18e25adcc18c5dce2318a7006626c3db3722d1f91c9dbee0219d7339045b67acf9915b248042d8808bc31fc7d41f72a293cc157b2ee75d8628b7a89bde3c28d1ac36717afb8d69ee969f49020326f3e4a70a1fd4e45bb07a50cb2a57d3473c289560a97917e2a54caf77660552ace0c24023e66c0732be3423c802661a4909b6496f165afd346dd064c5454630fcdf7ae422d22c30092a8f8a5bd3f1e79c56ce0a262784a5d258e5049f9ca49ea459a2b00d7eed2d69f03c11a8d09e2df4d9a29f4ac6b502a24fd6d49086054936e06e1b30ad800298d777bef02f2dfc74567a0fdc728bf3892e08808e1eac339fae9712401a38ae22cfb04b61ace687f922a7e33a7154bc52211f305d3d6229cfffef79c0e0e3f91ca147d5dd2b4ffa3e24e303d4c35c3f162dbdd86ccb8d477a94a664703aa4847f93407273cda27e789293fb83312a68e489ab9795e3eebed0a69db1d94c14a79f2d3cbfc90d489eab14d1e14a79a2a015d421ccfd374a4b0b9b92ebe819e24c242fcd344050c3b5551cc517d082be72fd840b8588899b0070cf07bf79b859b363341e34d4c419bdc94b6efc4ff5ed452873d3b9751e816de7fe92f0a4a4e3f9f04fde1b5508ba9dbceecdb3e5eda3a7d6c918342a2b81953716a0b9f53e8bec34aa4ae5aebcb47eaa2e300b686d9aaafe36150a171e51a79df988e37fd108bbf0fc6d4c08d6c04e77be1755473399ce1b34644e6ddd98c8759f896ada975f9c467aeb7658db35c03d6743a6a08356384637c9da5898f6d38318b26588e7fe4765636df38c7a1de2168eff4462a8f9a9a352817b091bca2c86b6ac507d05bdf8627ba93271a66b8c3bd901aae84e2440a0c18d4d19c08d3ac6c5bc899bd7cb94693f80269a7727d6a61f5d0425e194db40082708808f8a3cefcf5c58d076707322bbae9e553c89e147bd830dc465a45d44069877380dea69645ae8fc0f51a997134dd2b4534c8c92a5e998e9cd937b9f329b61e9ef0efd3561f960e5f9d3fbcd888bd6d5534ab7d919ae216ea00a70fe102ed4f1586b789bcc7518c8ce216546139b68c166b43050e9acfe47d4de2953eb9afced8cef31ab9c782e4d2f860ec474d90ef8eb80ef523df4fffa73e9f43dee16998b4e9c86767c908e6276c86e36dae56d9597162e482d183b0d7de4213f88599d19be71568fbf5bfde39f526b840542d9834828f8c78fc2218d1cecb8c9a41af6cfcf49333d4a1bbe751ae41eb32027e0a92b10539860651ca6c884b58aa80ee7fc7951ecdb6d2ea5de80dce4831f117c25cbf5b3d3a5f843921098406278fa3edbe96cd6be8e14e4b5fcba7eba2f0e62f8ef60c3d15728e0e78f5a50759ccc881595721476ce2a0f2faa622f8a999261a7ffb5685f47aa1b89134937e455cb69672c2b33428fdab083027b40772be148de0def08e4928fbe6d37d0a219e13a84870b1aac919288660b94d8ff3304cbe7b429289cc57120d56ab01739eb33915c4db90e53b3007561a1f39a1e4621cfb0a1318f83828a0ef12b74d09393e1943b69a4cf1bea231c5e4cdd34c3da7222343dd481ca26937747ad8534773ff61f6a32edd8927aa6c344a379aabdf91cf0fd4c22168e55e4fb779ae0a0b10bf2d87c9e4fee6727eefcad29805dba623e8f2a3e99280ff4bfd982c51a419d126f84c3d1909e107e72c97b0f6046fa539596e7edc662c1200431e79ef53b6ba8ab890f3994fe2ef6feefce11c0d3a534ce2c255205c7023f072ff0acee5f39e92e1016f1137d7cf0e37a12985c081e16b3acf1cd13b5ee28091347ca7e513715ef32b538a39ffc7f7f05ab196ad987302f817c45c173dfe0e3f718cf34f1d2ae6b4dc9fc823e1ac81eab2ec91e4cd18a12632962381a0c5b7f945d0c49af95bd4c001ce123c2a9901dba3267134c6dbf6aca598231cf3dd1431e125a16248c8aae6485af098c2a0e7058d7a2a0bfa83646956f46ba8ada80296f384f4c15e0df263008e6225a3d38a92fe5b698c8ee41083a85d1949fc5cfd889893d974fe9111ffee4fbd14e5045df88775c659d5841c973978583ac6697bd4f9330d3adb01712ebf85b9d577d5d9a808db944728764ff193d67b377bf860ff70715d5aeb3682856b1220b7b1eee88c50e18ec043054a6858871f496bdd639060bcb1118f672d4b746449c55fdc25bc6026459527fd45427c2792f1ca9ee7d9c2fb81dc9149500323c294e2b066736f7d69c97c2a2418c9772eeeafc8fce9289f8a240db3c75cb347f805a655312b47d02ec0e374bc17804900d6bb8c384008b074053e5eae81597f2ac11aed7e5d4e17b94a5ce618a4c54232c7b3352fd069be4edc11eff11704b9de3022cafb5e2c755330a90316ea73cf2ff0cdb51e40895ac82c66135edcbf5c18e8d5a4e56825a5fd573c707692c3c5678e855fda1aa0cc3c67ae15072732610d4ccba8917209779f2b94fcf8e0eb0c16aad4b534ca2e2d0cc66da713892882489c65a7fec9781b740a380ce349529db15f58cc03cdcef4264c2ae9e15fec12ab07ec9fbdc14018c3ffc917f5ebd9243bf0407b37615372cba1cab61f326fb38a7c4cedf731cb557cab3c536516449b9be0398a4f3e0a370f9bd54d760f634354373735eef37d66f4983202359071ae2819c7358fff37748dad66214bb18f8f30a741c07aa0d506c95c809617958297caa986cc916434b5513fe526894f3158e26e425b30d4a5e701a7481fb80e7972c66c0c391672c1544d0ab480e0c3e35b44de5240ba258593178714a901f536e208b5d93100ee095211faf64187239a4c54ca86f7ba1dc35bd35787fe751f9f113f9a494bf28eb69224a30bb64d24f76a07b881fdb6b15e64c641e6caf2dca9ff62ad1286364264c2fd91f325c0b971843e65e201b6f8ec9c2e11d19b21cb68f85013f4d8ce3b7fd5d4b7d4faf78a535d5900d9f4e5cd7a4cbf181a5a4624f2556b4b68b39ea86451199adedeae92088fffa5961305f16ce24a05cbd218469fb0cfa17bcb040f8b2cc936460a55774d045c1a24094e9c6b1288c027ed7383f87082ae599459a247e3d2394f3f38a4795908b06f86c99b1415b5875fb9ce85f516aa412d01025727d692288d8ad6c7d708e17a9f2f8057c2a4775b46673620fd4d441b9ea779d25887bf34b070d92d87d1504f4b8cd8e196c6d9f7b061042fe9b5889a4a4cf2a3ed8b72ee6072158e2a1c53ab946f7ea016b5d500a2bba8f3cec94fc269d9bae1df6d4d0566fa16491cae3c28387750f79c2c7224843b576f89bfdbf1b92446f7db1ef99528a59d2eba0c0d0ca54dcd11a0bbe54e1881b29dd824a4ccec630495495080d7d70582212ca2d07d6c3d1164bc9d1e08278d770a89eebc63f1374ff7253d1ba71b4c97c460266abe012d60c4452c5979ff750ad78a04d551b0523b4108213226e1726d8a87461898bff29302bdf15ac13358e7b6110f67b85328b94f524232b71a4a9a165e67ec100dbc0831850c89aeb5aed9ed7783879624b4779cfadf8c7629483488820668c5cf6cd6100872581997784f06a61fb3c26f8460d72b5303baadd3c638403c68bb35f0e6b9e7c345a547a5fa670e99e1d111ea8560aba2a56d40ae73bc70e2016f5586d35fcf2b09644b242104f4f016761c4c682611d31020ed47904e5d009f67019ba06417c040bf00347f4b665e895a9280f729329038ad213f8a7a15c6a039ea9b59e7c01858b0dfe2a1ae8101d3274945bcfe207a73aece7f43256e9b2b6ecf6ec11712548a196718752c748df6811b9de087cb3f3b68e3057436fc9011ffddeea135832387d153d16d05db4c8e610a33e74cf7e0c793405cddb75b29b34e9e10dd0ed51ee3f37a5e0a538eafd255da260640f9f4f59924018d48440020ff1ccfbd5a96318941f032950a3277519c0bf4e5f96e35d5064b54f176a6641ed8d134659d237d8b559d1e21c8572e3110f02d35eed391d005099b4ab714efbd178dbd08263ce517f9f2cc31614f7baa185c6d9609636923bb00efd637c52c76b75c6f3d1710df2f4387de90a7550a8ed4cf00eccf671c713f3c490e46a05ea23e410538073faa2048aff2ccbc425552170ce5973df59c694c68359cca18fcade6805d230d64549cfb9b8d6c38a43d0723d3e028ed375a6e286e3cc0bb19338ee1e0c3c04e72476ac41529a70d19298139bd273f4e8768c7c51864131009f60f2bf8a0322685808dadc49413ed0129f99ae9a1cd009cb76d92d36979201f27ec95abdf1427bbc218d22c1e08a89909b81ba40f7a693d57ccde08114c5cb03ee359658957b5e4f08542488c008e58ba594b2f0883f92f55e38b11282229221890064bdd3c2d73241965e555bda1fb8272fd988aaf17d3e48cef92528ba6410a0c9d4e2f6ca40606b209960cf6806b0a0d1c09323fdeb6ac8c24a3350ebfd2b8b1b4a99da9873b0ac5eae9cd4ba0d4df9dbc0714b63d38d2076638e6e17599e618780822acd94fb18ebcb10cd1f0d8f0193476326cabaa37f48b404f66124535d583cccb4ecdc02a2bceb3013bbc9a0722333692862fcc691dfd20128e008049213e32293353bc702144e7486ec2792007da11051a47be1219266961d3b0e1dfa4882f30fb745be7036584543d889efb24d998654d8e8b6ddb7a32eefd331555574981401ad4285986dcc16f46af11b223609aab77d5c22687df174be1d314209557948e434b5d07256768369c239d24dfb4bf5aa4c483a2681929ab2c4d79f57ffb1b62989ca05ba7385939ad6d19547c950be4276300b4d842c9b15de1353589c3b0109679614f3e16ed32b4018bd1b49ba2f1256f13d7a4207c45be7b0fcd72a02dc53bdd0eb4316495a16923ed9cbd6fbb6e90d6865981f95eb81aba2372e5e33b6695407792dfbb8f362eb17ab7664e3554bb710fa084c6f0d3fefcc66c759cff83a1f6e3dcea53eefe2a9c161702ce27b71222d14f2649d7fb1422a6dbb8ebeaf35f851228cb6a9345adefde1c7d607d0f410824c1849d661a72890697c8b9faa9d0b06fd9382f2e34303804210d215255520003d1ab8a1204613b6e84dc2373942bf5b709260ceabf8bbd56710f59316f641c3c5370f45ca59a4b2b201de24f74a49821fda237abec93bc3c5e6496f717c7a83618a790cd40af5db24da0e504507ae302fe0cdf5fac8638ba0a90fea8a26ecc5e5cfbaef2ebd573cb945e64ad42b99c487e1823dbf09a443e79af5f2a1c937116b6486bad831f96ba40301288dab40f875ba2983595289eb1db8d64a0aa98321cb82aebd7b2a88238b938bbd657dceac26dc9a08ec48496f20b21fe18e5f6a2b0ae241123aeaeed981e9ed289ee49bce8fa92bccfd952f2769ba3210c8f499cc0aa52044a1bfa3a3ccd983c85bb61553a9674396d840c2e30c1fc231dd1f01615b3e411d3e71191572bebcb813551bc6c0aa88974ca0ea60560c7bb093e1a2d58f505a31b6468ce57c0c0ccf13e611da6fe38ff7cf2bb1975cb4877c22c0af8bdc522c27c25d7ccf5d2e7798e61708b91638042d7c647e2fe5991c7c3cf3e09504de1b91ac581c9ac38e9dda2afb74c0113e9897650ec20d7b87718889916d7424d9b3920a3d4c67408e21253f4ca2f0f5fdb4a292d06fb12f2d9c6dcdcb99982171252e0e665442ef0382edc88a1c1fe0ae1cc490d03bae67c0178b40c43fde071260eb8aea1f8cd1e498432971b5cfad0f7d494fc86d40ac1f9b2799bb3886d8ef53d26434aebfb8d666b3b62a83d26f733971c1b20183278781ab092e1155bbd80eb82be4909343180ba251a8054f36f878dd7494dc0ad50ec04c682f6607434382bd59558912da700d04de733739727f27cc4e897176b9f6e9cda1d338f46c23f443d9becafe7dea86f777c87e02ae8ca6ad096e105bc636e6221ba6940d9b730a9ec33f33728b42784e5be98a1c281c4cc48dbeb1ea17bdc1030181a5fbf8908a00211aecd9bcb48b02dadbed5f5b409655510719e8511fe6f0564bf945f482aa9697f6529b0ef5a582d74add2d97ff843f2c0ee2271ddd8a06be6de3194d8d3709abaecd9cb1583048e1fe418de5dbbd6c7d90b45f23682f97970606f26b4a235b24d97b4b29539232d005db05d305b3fb2da00770c87c414a33d22bfa3d0d13d21eebae041cae5a7ca9b72d2e5b64ea8bd18485aaa4320be0e3c5bbf8ac37c3524a795bcc90597c31aa7ae48af36ea8c9f75331e612704867b4206839a4351a693025995221d3a7b58689b4207e502359ce294390bdf4853297e67d5a537d8c2a9c551fa3eae737ffa55713749f5e717123a17ca4055124875f7b1f55c35037f222d382c587a6d2d352a9572caffa6e4837d230913e6e01869c090bf76998293be6f4390e632aa594d6efb3d4ce3bb20f173ff2390e6393e97fb5622191482fe9bc2db8d07af1a98fdd72f174e6ad17204d5902ffbb37f3fdcb03b43c7db025c2a89e72d984634c9135dff4aacf638c0160c9f35f7cf35d7c31b224cb17b27c8b0c53454b8113d9e4e51bb123bc5aad565506197ec562d5964f8796bf21b38a24e79c263fef4b1d5ac0d8ab9657813864a63449679b4dc3b478fadb8db75c3cfdd3d7e9336b79175fb3be5e7dbd4386af71f024b913643c0a1e86e8e35bc86e85d8e33fde04ff6979daf22dfed3f2343eab5cf52d1ffa4f8b162fd353b67c32fb8ffff811519d177c93fbd19b71579e1aa13f0d338fb47caaaf756cd7d430170c5bfe3efd9235d121f64af538646e015b4c8143ff9169fff196f7a8c090cbd6e4c6172e3e24e5160c18fa445c5118208d225a41c98b2804b9009790e9870e74005a930130c2ac39c7d1f82cfdfebcdf620a1ce219a8c37d1c329bbc0e2d8f43661abca7a5e7e4c64955dfe26b9b5ea13eec58a62fbf1c6cd4e8493d4da940995e51ff6998fa74e62cfaa5960fb95c4a7994a73395a72adf02861cfd073fdc2bfa317ca1cc317cf4682d65092cff472f7a5a2b427da89186e9567cecd6af10f991cf817a45439eacd1e7f00569cd27e83ff443ff91c227c6c806906c20bdac350c7efab448c3f890bc6779198f79c517e2d47763a2bed317de155abbb1a97c3766ca179632cacf8d79f2e190d9042cf5ea3ee99ba32fc6e7354b3e95a16e93b1e80478812a3142ad1a59c20b476c8ec9179418c1e69cfcb0b1c8d5bd5f6050fb033034653a84c27ad51cc7bdca94c2a59e229136030c252185d5e846610d23039c1fc78165eec79d2002a927ba9a88ad5b7ac2891e4bc3a104ea09b9155297a50a46a5d20b9ae2e42607124928a199008925c22489124bc44909ac0f94885e47660f9fe1f0c2600d537fa653f9f31234fb47fe98e285e51dddf2822eac619c5e58a9df0b923f5e9094b3ded92d79a4594d1d943f324b6f822e7a6d332c31ac48a14e2b26951494d28909098f44deb5a18edba24b74e9e8125da24b74691f0e2a61c6ec957c79f11a721cbc4ae9fe34fadddf01329c15df4ea7956eb34423945b7a34b6bc7938c8dc5ebf9d3de79c53464ee4d1a0df14cc3d9a21a38b7c59c3860424e09c15378fc6175de47fd185ebee68c6e53c1aabd0a35192614dde3c1a188f1cb0e5f9768443c8a32167e6dbdbf7de7b65e444d3a3c145975094a190887e0529189279826107b28354764d58ae5b36d3a6be0d8f7c1f56fef16b7228bfdb87af45d81863ba50aeb365046c0c76762bba2210b480934f1677cbc68627ec97efc38b6b0492faf02e9ef93579b2048540ca09d828e363518ce17070a026e3d840cc35397a331b90619061c835525ac0c62f627be4901f74f2cd91f6438f2c9bf6838e48bb59924b5070dacda4ddd032ce9176232437096c6c18cfa1ddd072e4c8d6ef4ffbe3eeddb37f7ea13592fdfb9d86a4d15cbcfe63fa47eaee36d4d5eff6cabfda50c78121e6f2dcb0734f410e5f8e03319da8054cb0265312ec6ca3ce499ca494e3700fa1dbb10dcd76efaeb1721e614facf74b529ebf71ef7d14d178d786bacdbd3e37ab778d2727126c13217bf7c46e22b656feb27a227b0d711551abd5c9a4b232ab3415b09173c8fd610d91dccfd15aa357bda4068ec3d864fa9fb1a9f00087f226a7616e74348c7d9f1f723f1ba68777330308b099651f2008c106e452f915f0b501b9a22b757a95ed51bff2f1f1ca9f50354c6f025114f52a6ffa1a577fa98feb06515fb434e8035026d76b0372bdb37c53319d4e80bf27064f23e61ba6e8d24f8a2ef2b97f7fef7e513ecac67dfb17b42087b261b9fa8571e69c63e7220eedda4633aea69b12f112bd15faa42bf475df0c70cca81a36fdf2495966147d9afba1a72007d2589bd05f70966dbab7a08cb3b60f7df66b5c3250b47b1ba10769782868a37b0bd244676d4f53e3da5c263d308e2ef449d1a5caf6294c3a37ae3b896e1a6ab8e194230d0815d994238d070e7268caa1a9369158ceb6941d65b70c75e7dc8c3f65d74ab7c979d72119ada87f82dddddc102d675987f8b33bd239a5bbbfc89873a680acc3fc0efde98c54084a3b236183d71c91449e48c8adca513480007264d950e38388449cb5f45c43e554726d8ca1183baebbbbbb6b8c74c6c8354c7f777fec6e19638c2251478417df866a27c1d1a87259c618efeca028bab428db6d4624126d367459829e97fb634cac217b6396fe31267a72c6e7442391c8dadc2f3f46d51308fa052124164c5095681b9e2a821bc4294a1084828107403f3ffcd4905dadbb09ec39d278dc60c604ee1c693c64b51a3672908094b51ccac8fd02a4791491fb391a0f9d1cbefcfc60b977020e911c693c70721339d2709cc82a9f1263eaf7a3c498edfd4b3186fbfed923fb471a0f9064077174ee8029873cbc5eaf8cc334caa28c6f4ce806976fd00f759386e7396974a95b74e9982918e6f719945ae2068053a6c0f644cde92f33d8e8ee8e359fe9a6fe544b296577d08e9d3304a80748577de9aae0cd756d7f335d36f57de06c4cd706fa308291ab7ebb645aa6470ce8f79f33e6f7db519ca951c11805103aa64d4468c61da687a40de75143379c2d82c38983d303a76146a12ff4d92080f6e67bfa3b7c1fc625df87d2e67bbbdd0dc4168da9501595516806fccd9bd93ef4ad7af5a97c5f4876e80b5926b0bdf940f0e6c6f610c9d1ec62f8eacdfc5b3981c8b423cd9bc8a22fe98b09aa4f1564a7294c00adfcc8d8b1fc572a954aff8db69b15a9efdb4ea70fb59d9c4e3d8480c3899382f3e19c4e3a603737372b52dbe984da4e50beed7412c267714e9ffa152b52a793106e361d3737a793ca17430cf25956fe0413599e3e8dc96492e96749ad48a14e2b26951494d2c9b6fd643aea58ea6bd4d5fa5ae1e74c130cc184ea2b643d148a398618971a86071a69535f4a78a0a9afaa4334eb0bd3d4d7fcaa4383689239379fb587d86a9f49d32bfaed437f44795002cf1db0af183b6dc9c842d181f261dd8162b93b555f7b60f1b473640dd3cffd8083c894c58795471e7d587532ad3fea6bc2542fd3a31942198de66481325150ba1bfa6eeaabda3481edd70f270cc576942ea265aa5281f3659303079bb0099bb0d114894411c827b672187572c4fad0d3206fb1ecca06d5570e1088c08ad5d58eb5eac2edeb3c79f9f67a22195832b80eb58668ce393db0d624920609d986f8a9be0c4e78229a917cf1c0438b6fad7464431a869b3fa69019cb3427d36f316b8c649eb0099b3b260ed8b57c585f2bddd3b9a346bda1614ec0b9a3c514b867c2bc355fb0d10e8c0383591e9806b09b37380dd34fbfc6aa8e476b4ea6159669dd4103d873f23a541a1ebc355f933e4864e6acf97216200f5d104a4551f994a741d67e0d6af96a6061e9c0fa02bfbeaa4dbda93a5229534e7d8ca9a73b7af5a2dfd5d70884d5970facfe70e3b3be6eeab6e22bf58a7eea0b714889dc515ff515ebabbe6033871376430e2430c418a45f83b77a30c61873786b69d612ac123262d09658e23c96585284a682d98e21d4866083d05c0927a5e22611700843080d889804021623060a96d05080848682d79cb41320f166b24ce7ad31d4bae5bcb5c5d40286abe7529c4e4866129e62ce8ad25b305abc098b87f1c9c89233191a62de9269a14135323428490ba9f2b445f52e7e82219660c8f5a54a9eb6bcfc66cbfc796fafeecbf8aba494561621d22344189367f1219542f5214d42e943ca0418950fe9133295c275f1d9f0ccf7e15d2e5a3ef5f75b2609f78a254eeee29229f52a5a59a84a2a959bbd8adbfd0b76c350696949b57c4a050ce7df2b5f871690d27a25271229a9090b554945b5aa1b2e9952ffe2db66dc88bbcc806adedbf2342bd85902a7ee0811e447909d9dbb13e4d2e00a57b83ad7c79dc195c1cdb931b844dc216eecc260e70a715f707b5c17dc165c1e17e7b2c0e50b16640c5321872c5a12e6381906fb8104c95dc1dd71557053708390c94c2129a56c5a5d958b457a0aa7b4561914ee930869babbfb9cb14969e7689ee9ddeb8322091e28a6bbbbcf299dd25a3d87f34cbf318923143e48d9eeee3ee76cea312826134e6b954d97d305f3652ee8361e1e9e1777fa9f09cb1c944093e33a47f36c7573010f1d82ecfab2d5edeb5679622093c95af2c48d0a9e0b84bbcb2e0adca1e00e857b82abe39ae096e0fe706f6e8eebc325c1b571779e3b822b82dbc37d85010c5f58927a994abce5a71c7f7e9cb330600a79915eb5fbe49612f45a74e957b2242bc9ed437c66440e57e2fbf6c98eee92e5707b754a6aa11c1dcf54314237938d33e5ce67369bcda8d3ae36a340ea64b9df46d9dd15d72a6456190daa319aa45b9d4d9579acca3c56651eab328f5599c7baea744e8fc9281552b6fb8c4f5932166b1899b3e3536b2e845a962e13e2435ce6f54569ca77e43015655d11dad56807eb66dd8e6edd9cae56675dadceba5a9d75b53aabe5beb1ae56f2eaac83dd5ab501a631ce39e774138eb53105aa3814493f5d92c46649aed526f6701c1a14bb9e0db01c7acc946950affa7d625b905d1e1cae72df4d27c6ccef6ae3b295cb562b97ad5632af2f97552973221d2c956dce91c3aec25e397418ece7a405b00ef6816ea672484a652ac6c455b77e46c7e4f97097d15b055dbc3c78a674e430255bddbb4ea535ab81c05727c62ce956b7a359fd468c1c39820449923aabb30a76b02aa40ea932d7f9d1ad3a2bcd3010dccd28175c567d86c618634ccd0903731d487c471224b99774921cbef492f822417fb5a03e07e292bbcb8660a32a8cb3d49c31c6186b2afb636ed571ab2f06cbb501966f72960db0fcd0347311b48cb9d4ac54b6d293207ec92d5baa1d202988417501cb151376960db0e50296afb2a5a4505452a50a2c3ffc925582e5db2d60f9560b587eb459c0f26d122cdf6201cbb7556069af40ba36c8e4e45a2b946673a9dc01b15548b9960a8b04cbb75458b9760aa76ba580ba1628756d14f08a1b634c2ac7b7a915a8d38a492525258ad1e559688e8daa983922dc903b84b216836a45fcfbfdfb91af4e42081fe4464110db0e54a81c6942fcc85e8e342160a0058f14b4f7a391a961666be410aacda0912f67f9cb9b4ebe603833baad05b51c693c7c4239d2780821b78fb71ac759fed884dfb4f2091bb6eac9de8b644cea38cb65ecf6ca8232cd4d0a5c60fb9faeb3d60978071eb480630c7df54dc332aa081c8ad125f432bdb9dc9b09993edf0f9bb010e27b3fef4b5581ef47f39786c67bd1a7928432cba605471fcde85f9ce52ffa68442fe34b6501875ee6f065d4bde88baed0573b707a6c4945757e5ef4fa61a126643d64b26c471555839452e10f355f34e7046de0178134a8f92bfc34a237791ca2eb694c5fe37a19ceaa5fe39ae00d1ca20b3babfe08a4e164842619d4155d261f72134529780387e8aa01d2d450e36a01061c5d22106503bfe83188aa711f85ffe4a54b04f268c0856a100501d193bedf0369781ab42172897ef435ae4efdd44abb02acfac8e1ad22895c452c873227d7ca6df56ba4558193ebfb8faa851cdeeeeee8d9784ffa482b2247aeef4ed0ef457a5c437a943df95e5e0d0f44d9b8effdadefdd97aefb32dddf0224488448c6d1a40f65033fe931e8b1074897f7d2e57db7026c1f25f1a3fc034a4f024140fad2a38020e0b141fa12ca83a0f4184475745da057f551be1d9c855fa15aab247d25f9ea551dbdc8e453be495f7a1bfc2828f9295fe332f94c4c5ebe490a488302ca217ff2d5b82cfe489f0e4aca48a4c72f256803bf0948834179e3acfa3424b0be4c7ae009e2e8329f145de68f409073d67cb9ada9ededc0def8af5f7e1ee0f206b63080e195fb81624c2bd37f6f85be7b8ec3184c1101beeffdf620ca419483343cfe34a1f71781343938ab5ffed7d9da0f155f848a2f8af6e35b1148df822b67d1a789ce0a81b6c63b8bca0de3e5f8dbb6999884de4fead68572076403523fcf1d900d48e70ec80644e60d48059f7b8ee4d9c0fd06863263903b01c1d1ac067748893cd24909252574730ced90226528248d50b991cb33c6442f5bd3cae42e7e959058b1020994949108af5cefc6cc3c853c6483c99978337e22b37745de0c8abad49598563bb0b078338e83875eb5c908131bbade0c94941248eadad82848ca76efc68c8feeebdfec5c3733f74920079f0a488f029eb9ef069e1adbd799fb669021830699598c9717ef0df48ecbb62bcddcbc3cd2c69b99a02814726a39dfe1f5e2ba1cbdd57538b8518642de4c5595629c74935ecad2e7b4d7bb31e3bf99bc668a4b9da574fb62a6d2a6659c5344c17bbd99fadeea27053d37b5f16628284d3a18b58c3e4513e4bc1b1b66462f77c3789d0866842d0857a83f66eede5bb79214a0b61c001981e79122e0508eb421a6c843d44aa0225343d72386004275028f72601a820627280101df17b0608820362130ce9136440fb50b58650a4dc05e122a143126a2c037475aec881e6c73a4c58200c566d0b4980b4cf03126a442027c2660931c69b11fa811f08916b6805792b01981b92328814b48d41cec095102f672a4c1c047119c92230d0641e4d084038c92234d88259942015b61e6f688ed02167810e1072b48928319e88852bca00829b8a04415456081929accf6c41021b0260c310218126a7e50515392049d1a1489814b882aacf02d0821851d31915d092a2cd7302b2695146e43299d9890f048e45d1beab8add2e92de38ae924729647c49f7c72f35bc9a6ac92b98abf948c92398e2382e5f79c73ce39e79c73ce9972e79c7382abceb3c19b5193e96f5bfd21966b98d376caf15bc9a6ac5253324ade3e1a59d3e340ee0570b996f249b6ddf5cf24dbd0f58f94adbdfee16ceff56f94ad4894ade8fae7653bbafedd6cf1f5cf664bbafe85b23db95b97edc9f58fcbb674fddbb245b12a56e59aae7f9eed8a4dada04e22afd3bb5d094284e4b0d3af11b84fd9e94f4a7dce193f4a9f73ce99e75b204f1dd8a80a718cf11463af4c934a8cb5d294ed03f5dea05694ee08e9331fd21d01cb9fa1bbbbcfee08bae91c61b2713644baf64e1acba1bf17fdded94921f2a2bbfbd411559c1ca5c0f2a9e738eedd11f5ddc668d2c51863f41ec41823ad2a1fa857484d319df4883d55b9273d60734ed3ed8eb841b0fcf9dd118e4f2b8d3a49f794bba3622ad5a2c3b7bacd6de66035bfd356a50948e5b6c944946d5a392bc60c271c9cb25f6eb582d1add446858987099487caba072948a18886618981660e95f188c84c65ae15e3e505452fd7302c7426a193ca2ec7733a9d2e366d50b7cf497d525a63b5b9aedace13792292c9490925c5b47242a552a81531b05cce4f5debe9f9699ff6c9fd96a5673da487fc7fa616d2b29d96812d293f5aa77554284868e7c4acaac4a2cb61c1824517eb7a3a27ba9e8bd382eb285017ebbec66918cc652979d4f30496ff7ddecc2d02b6b47833b7884a06932ea624c85941826208ba319ba45b1ca594520ab4a26f529e8f7eb5ac484880805229140b163ef2f34381502716cbc58b17305c4869c4c7e7b47a418144b78b359206924ef4aabf47f650a7441aa8811aa8811aa8e990069245563c591bc55ebcb83517176fe6167177777a7d6e0d00a3188c5b73ae0867e4080e6d46814cdc100b8302b950a0ee933a32c60de180620c916e8d729ad56fc4c8912348909828388ac9212a9e0f29932e00b814e86585dc5decd66c100e0002705f687773b303267bf40aa7573bf645dac89715c01d00cdc0058070ec40d32b1e064000e782281005b25123d62890f27ce48092d18552d400eee5c13128109db786a4cb895d8c0699516d94d32d9b24766bb15b8bdd5aecd662b716cb8db241315423e9a006bab55bbb45bad5e574b11bba580d36c68de1cd4c5a003a2b4784a3714e50200a44815c07e68674399d4eafa89fbc199fe1e4c928c02d8037530b40614231f4bda9fb28d0479350257489378325539006bdac783e562b986ec92861dc92320c703919b1cb6996016224d8c528504cb66e915bbbb51c0d401f2b60274810d3a4d2458344d7137d84205104318214450f7670a2e8c14e1494d2b943297863a59d287a98b13b77a20042101c974174e074dbcece4eca7dbe766690d892ed0b3b380198a3a894acd816b96488660000000823160000201008070443b14012a6a12ee70014800e6c963c685e32124843498ea22006c22008628c01801802903146196768e6280000aafe0b16ca9f0fe7ff24adacc234b6cfbd6c9edd433e03e56ebe0f126bc79cc1865cf081bebb175de0bb4fd18f8aeac66fc0bfcbfb23ea48c52ca888019b2c995bdd2963ef6b3d0e0518b3372fd08ce1d39bec19dd924ca2033e2617e83e364dbe88f2860ba87f7d721851c7f76cc9a0958e8550d9219c98a4e1bf156a7759d01321138866966c6637dc9283c7c281fff4693c4727dabff28336b68d6ed1e1ac3274c3eb8ee791bb294b6263d8aff139c03cd260d2db90a6df7caa0a6002729ce14e6544e4ef27a6191a24626caa9af72b1d00c7ba90dfb2aaf98ba80cd8b1aecd0cd5271d2004cdc58bf7dc283ab69ff3ba7c821df6c56074ca2a4a3e4056b11f25320d774400fa53638d967486f0e332be53fd851120e34d543d0f3eb7e5e8f730f40648e3afcdc5e9b001a2d459b16e29181b8fb9dd064dd40c9381ecfc9cb11bbef0bbe65585d95ac28ef4a5b7add0042a70fd1c5f6da253356f6f67c47d47af9749a49dcf5f29601f9073e8bbfb3f1b6b9fe05ae593bd67fc44cecb66c8e5a179228971bc7bbaf0c63d29cca6d4887603f39f0074a799ca66439ff62bba3dd1df2337a3d727a9520fc0e60bed7748f83edbde3f8bdd4b63dc17ba2e1b36a7173ecea3c043e4d44db0fd34db1680fb19e8e9f9094e0c8a59f7ff210b32e243f9da34764f3a8803a5b68bc7aa7de426d4a24e95811ebd842361a9e3f58783c467db9211c40d7d9e29db13407477a28209d6136dcea46020418e4310fca9b1094b7a1b812dc5d35cb9327e83bd07042f4301403c820d3fc0a8ac14a849d87f14ac4079b8096722bf2d88e87a9b441a8dc009130f8a5de8094a746ffa92a5e8d585bec80da835f631d263adc0dc0085752eb090abfd4435a35eabe8d2ba422612616168a1d328cee15794445e35aa2827162c65ebf75dc07305a9241af3c37bf421219bdab763c6465bddade390eb734a432d68017d88133bc3486cd11ce7f08b679f7731a91b6579b4c0a2c800063734ed18a596892c1b0a0af29b7e95a684d07d9007addc0e349aadc6fffabd49f43207216de01fa587afe38f06850651db99bdd305fc992439319c7a4d8be7c834019421527347fde41d90aa33290a731c77896174cdd4b8dc348d35a90679a5a065d238e524dd2a25c82ec208c084ed3de2d5631daf850ccf43bddd670f7c0c824e748d7159a711b21af5950d83dd349fb8c0147a894712ab77e37fa6274a87cc48754210fb914da6a3f82109bbabb5596cf29999c86d9381fe442d096e91d3fc8d416141a690a5b7b08103248556fa2b8c02039ebb011166ba38cd09eadf98319156f743b72dd198e68031f4064444f7ace13cb7522951b7a765bf34fcbd092abecab831678fbb98dda55dc9aa05fea6a6608f7200b9d10cb24e0ce28b46cbf04c72870f9bc352642dae91a69b2b244db70fe9b8dc44c5c094337029602526ab2cdc34c97f2a0b5a3493c0e4ff6d3ea498bc2f9240846f0bbd2b0cfca6c294df899ca03b514f66c2df98c7bcc8ac0010569139f4f70bed972c8e9e89fae3aa66becede2d24b3a927164686511b0017443ca03c0512b8a8f280dc0c1e43bee78e8aeced82f4d301522ff782a3b56a2f385a23f68a4bd665af38545d768a43d47557e24aa893793d17fcf2f36ec9e9274f9c35267431cfebdd035f5ae24e24f59f96022d1da2b047b751b1d21627a870733bde82a884fe3132ea20dbf372605d692814c79812a0be0f730b2d52585ea6a93ea01f7942d4d12cadf0b0b1ff2aec8877830e101842196998e6468c6949caaabe48abbcaa5376a1a2abba84a50f745da41e6df79a1439c87f172df5be059d4997af159b8fb5cf238be23451608aa91139dc4e5563d9f4525abc39203627fae312bf7028eb5a36a55c30f4a7aeb2b6d3c83c81cdbf3ca4479b6f64315b1966cafd0c99561da39269d4cc9fa0f481a2eb75ab8d4a77425c5fead7173abd98099e36b58ea75291d3d37fb3284653035a68d89b1d7a64df6f2e4943ad83b9c34a70273b49187fe8cce104013ee76cf6e1a1f19ebc7537f4183836a41c4f5c5cc47ac61216a6e047a0fdc43cc2fd634ff5b46fdc239bcbafe012c276237e5165f3037532c2be332d7588293f922e690ef8ff9facb9b945876457c68b01d6884b183a4d2a3dde55d1c44d199563da205dd393aedaa69298c90f9c7997b68bd8655c964d942ee1f916dc3de382e71ccaae71b2da81bb614bb95a049b1263f9b742fe862db6773787144553a806300f2d76d65b9b39cdb09ecd804f3bc029ca2769557e5f76770593436418e8619eab60e2feff131ddecf092139ddaa783dae57463dd1496038e0f55d0bc0dc562465efba5ff7a98969eb4c29dcfdc31e439aa40e7a95ebe4602d6354f3758cd5e39e4c135290e6c8ea2808225f98b393b5f71f49576b8608b8a5e8b4b0e9cbbc8a889796e4542af4ba70232398fb925e18336cbe0c61efe511478cfd933ab2c23f63431a1c2814a8fd8c3d58ee141e69bef1e4e05cb3dd5728ed456458767ef11f4780a341287eccc8c1aba473459f06deb55a3c493f516bbabc6dc286ae1ee0bf9763f4bedf4e80b2b7b682c40d6bacc7a04783b916ba83274e95a759ad581b144199c0316bc2a1a7e0760a4e3b8f90abc07990385744d24a6410872f5aef7571999a5296631988166eda865eb37a1bad5f14cda648dee3222c5d555b579914537a938f5929218a1931cd555f497a72a4d1bdb279768292f15d9c02e525f6a2a92ba8eefd171c14263615e9527bed54af751b20a356a6db5bde96a0bf2e4294e33a13cd5b3f22fac5a215cccf1746b20f4f94e69eb86c0d716e6eafea55c8365ab0b223df2046f8f48d1de6e33bb7c76a65b95fa02269e6cf8780ddc5f05a41ebf078d13d7dd8eaf58f56ee370fc30a3b3eae059aacea5a6757bed88668401d60abcd4e4d6796d9081860730c29f25cabafb7abe176391cbc899661dd49ea9b23a40aa2422fc1d54d78f8fdbb6c2b2158189c87a2162b14058e6c36877aa6c26bac76577300bc29681e845fd54e2635730e31f20cfaa31fa755482308d9cf0853212726070b1326fb01c916852b0a4bb18dab2027b1f4817dd4458bf7ffb09847e0fa48e4d2ecb7a4ce745abaa79f96898e364ab26cd614dfea1fce5c882851117266e4cbf4838fb74e45921911c4ff71781cbf5ff6f6f836cdabb9e5adc8840e540f1069f35eac69599eadeecceb451c7a11c83d80577ea9face3ae6a6ceab350012817a8bc6be7f6d348ac6419b7b24d80b80f05ec1c88c62a7b6058dbdd8719c4c60f350dde97464754d6fc87eb011a1ccd811b35d1b0d57c0be743a52f0f8901b53d907eeac1c2e5be4d8ac1dbc19ad097f4ba9c1d0e3020681d39a7a6d1c6dea7028aa49dcdaf56b212294804e2b66cec2b8aecdb0ad9d03934a6beee3b06350ea5375a210ff65d8e342654196f7574e11f22d4072821d8fefc4a2c8bc08d917a05df36eb30ce045706923df9705eba84c9d2645a6fde58b73f6b5e0f1ab05556f828ae80a5b09bd0ba89e86e5600d60b8d51b1000a47020d4dac55677f0616d978463bf00940a0a8cfeb17f29a2668f45ecb599b9c8ef420810163250366583da6810bbda49b4a61ee4cd145e3af5271984db5b592c6380e4513d3fa3fa01028f8b2a638112e8b3431c9c1db6059e812a79a8302f567239b4b4ee6629b652bd6f2332c32bdd326c5a9bee41f098b03a313edbc44809012dc2017ddbcf1ebae50dfd60fb5c24b0b0b785689209849427928dc3b48a6d5c3e617b0183ad6c7951576aadef88accaba60230c7273699c3623cb0d3216313dd71214d8c86ce3aa446f71712df8bf117c1cc34255eee5dbf05efd77f597203ab54bf62a07da5ca51c29a79a2564132134b137887f9a5e1f9e45c32ad87183d43a363bd4acef460614e51b79a4e6c483680bd00d99d5508578aa344550c82399de944e12a3c418a05a797c1cddb3e851e992fd8a38493a7c223ad9f2d92e8da8f2a55cd5f5bc2a92b951eff927ca66c76d9f322e5996cbeeac5fffa6ffad3efed466db60b7c059a9a0c8e71c67610217ae924ab5321676911df751a659bbc1b9c9163c47fc03ba4018c7582099f37582d6c7c4a28f87adba4d1ef23a1e0ba3e2c0ba981df0de69af2d1c101ac38904fd4a3e0d2cb7e1de5b4bf6dd87176c4e0ccb52df31012416dc193ad9d2f06191705363a1fa86532dcdef89b1ed70ebe12a1b1d96500199d58f3defed4302c9b24c36bc6b57288ee2fbf287e45b9b67de9918dd09cb10338cebf35d3f33b046e10c4998ea6067e194487e15b927bf1f7090b3e98ee94d2797d02c00edf29f42499a63308a24fc610835d7e37cb1733b3a6517fecaefdcb027d82f98d178b1935ef6c7b02ee59a5df03cbb69f58d829295b1f7b2448ffb7f1d9efff0f6e3b7ebb593ff7af7f93f4aeba1694a1e020c99f8d272974099e74b61f10580de1f3641881849e07beeff37d4a684fbc3b2ef6299f5a4370771aff28f001c587eee914247d886b77bd67ddc877be4115bcd1b294c38c83c2fbbe606790ad7a1130675f0eebf620b3847463771feabe07c020926adb7e8b3cfddd65399601f9c3c84c8342aaf277324fb3dc57aeeaeb146ffc4c8591641d465a2319b0b49973163d1cd3094e86290e29d9fa4b22e7838c930a4c4ca7872e894282949a41bfec77bb3b4e4819fb7d6e60c0c31b3c1ef0dd692459ca3f1f8a3c6ca3191b40392f164de5182d7ca163e9ba0de4cdfa9cfb230de610e757e54f5a98a8b17c2092e831e788305df287b5a70137d81749fd34f7802163c6882d4a07910e4c39204ab9232e6c3ff2215d788de2976dd99225a3ac9eb5c89689595af7bebdc56f55b421e55a256f4c6b6a75535b6b23b4700c23fd10e54728c69f407c463c60fd2721709dbd078c508c41bf3e8e35a2c6521a31722a4095e3e600c3b2e9b6f1f5d5a4a67f13d7fe37455112e3a383c3a90a603e9cb86f4811e7c39a8e330d1f0d73936336ca07d8919cccd00cd454108617ff5fde086cf5be005b5ad8e748ac07555e71065424b77dfb6b9046ea7f47fe07dd74071a833a6ed0550c0e02135641a206af835b05c751a4ca67c5b658cde12d69a589dce14557d7de5564b7eabb8aeb320f43b406dde5131d1c3f6397448a0c127dfec4646d955dd8ebb5143a7e3923a8b496715fbacfaf12b6d455838f6cd2a61e9c2ea29617e5d929e80785cb0986282cdb266796a997b2b52c8a67519d686e2c09f1f85ae41b8cc0e40198aac2e8be0eae414fbc1b78c640f896ea41643c0eab68a758519708153d7c313a00305d7076a5bd3447715b62e01f916293fc11ac5373fb2a28ba4dd880203ee1149beb476761d76ca52dc95adc42c791e053139530b13f5cab962cbef26bbd2108a491b8336e6453ac44c048b46d57acd6bc6ee1e394a011dfed8a8c3ba2c1ad694fe38e91396981da7db60bb4ac1b5bae0337fca32f7166f4866fd29adc90dc55a2fa181eb31e3d4e707cc568b7887bd2458d2959f2fa86ff4f73e5195ae1596334c0ddd00f65ac1f674f1e1a9ee27e3b6c1d13eefa2d440b57e67b802c0c2fe51bedd0636fc47533df7de81f5b75fb1aab0ba547241cc18d724c2f8df173de32f79b1e69fe587ff6da166aef253729cd45569516138a7c68c1697ec75ccaa103ecabfa746ccd59d9279124d7cd311334931b6d5eb211f83aa6faf0c66941d3d7fd17a375c1cb8fc179567be6ec660877e3c004ee57082068466f8c29d8e5f908752892c8d07e2a8d776d8ad41503a0f43484a2544412f310cd737c33de5089301e1c88c6b33e41a5b9103e520ec2a369010541ae41838fe36f418f48e8b0b5e98e70c09d03f3d022862cfb252ab2ee52e5345a3dc6b843cb7893f932592a9c2dfcac45fb05a450dc0ea627c9ac8cd8233971165c56cc032a82373575e62ca191580a98526dc3d51998d02366147dbdb843c6b988f865018abfd9542c07120140932963881884249844888bd054670a6c50f86f408894827450795835f7917eb4708de98e50809e4b7684380252888159c3de6ee7caab8d0bd0e66c80fd7feecbb7068dd4d9966fd48eb23003c862425d47a477c781ddf4d8f45c02dfa1a6d46c611f7452ff857ed206a6464ddb26280abc3d473abeb82f48d2830e032e2644b6367c544961a38c7d55178ab56f407e325b2af52e65265b1d57aa667cd01e2ba45194a1d286f5d5abe90ce3d66318c6f788b27db14541ffae72f35f4f96e4a4866a4d16015721c6590d717d8d87f7e33b92f4163e0c30f9304b314d791e81a2ace5bdf77e2dea9a706be81c354550446f3143daf5acd208c7d6da78e45375b66eb1fdb481e2ac4b8d54b13c21974914671165bf43e6cabc84abcdd0be56d937853d84014e2dc39f8cba6b97d9ebb5a964124b4da455a2a713a71389bbbc036446919e0655781ccd7271cb4fe2ab2648b3eaf583518d97be7758bf73a3221096fcffd83609ab22f6cb6482a13823aae33a80d62bde1ebabffac36dbb48aa849df933989915abe1c057a038cc6c088d18c2ae07487ea7005d1d7fedc4a6fe2e425ae4659de378f73606d32fa882e72492b903c75bb94486e7d13e6c8e164441b6a3a33d49db8b5de4fdaecf1534245c61c2d90c148f55202a9a37a722cf327acffd4f9306859f86aae97f5d3e01ff14e4846e580e4a55d033f36c97f33cdcf192e2e5aeb76ac5688acf389742b6215681826020ae6eff03ea609d04898785cb92f8a33cc416bdd9c685a3967bfecb9d8b48c110a92c968be785346ab768fd3d88d057f23cf55c39258629de974d578529ca70769e5a16b6e14786e6efcfc2ee398b4bf57da95176f79ecc2940c8cbdffe040f890df26f82e318c58801c009bdd33a1997091d9f22424bab12484791bc9a58ee7d61e6901b7e69f920f3b6dd86f0f7bfe75085cccd25c42c0f957909e9b1b5bed5eb0df59302277f313c7df8f68e191f67086cbc995d61942485b9090a337603e7fd21a965afffd5e2aae43de91735b63d68ffc5a0a2d2fd3d9a6d0ece5fbca44d006a3b5ae414273dde0b6377a50360c6e7dab432bf2b20e5ec64a19036adf900ae1cc7f8c4840084e40c20249f6b22739e23039c7ccf14f04f6c0b5927d7b8ee5b6c972a6b26df4683343e5cf42f01adba382a7eb16138bf0aeb91b3839a90d44d49a0d254d690dceabe1f59aad34493db8ea9dc0740634862db6fcabd0e0e3d6e70611d11fb455d05a8e909643c0faa15e097ba0da3869e6a3c15ec7fa6f047987d921cc22ca31d75b5eaae999703fdae23b9447215bc02c3733812c51aacfdc34447035114c5c8846d183a81738ec424331d9b352ef6b042609afaddbe115587fbe93c6f12614bf504f6b325ae1938b622cb9f134dc6313751e6ce833d88421038704ca704cfe82a679fe437946185944689d29af0e6b2fec292de6538bfba0fb1a2fdb7b3c3101bf0053930da46ec26b2e4c5d4cdb8d1b17d52bf81017b7fb1452498052da4d4871667d570a776e16e9f410091025977745559651c65c5f5defebcca3d21d29c9ad549facc36cbd9038d4e1a0dd83a2d456bbb07c917cce68c8d4c667e6fef29c07cef4cadfb5d0ecb96c1fb237dc41517303bea93c59c55f18a649770a6b2ca8949ba9cb383bfe74d60369f1322c1f2107a21ce51d0486f658cdf24e7efeb424db616fb5580599960e8c95993a705ef3713465bded3324a493058a7b8898557a9010d4ef4d833032c7e57182e96c7af5b22f7c38ef24bcef26b9bdf86d1e45967936fb30c2003affb514c726721eee379138ff6be264e591d129e7f609e733d867d20fbc2118155e87f3d482d75b38f2453dc8797e64900cb0748394050f1440452a114da40082fc5a18ba0a6193ce1cae6ccf0baae27129ea6dd79288ef9bda9df332df7eed26361a8c992f810e6ac2447dd1464a45f041675c827285fcb40127ecb4d3ca1a70818c33d36f66fc1947eebb64dc71d4eb3206146266af88d6a267567a520fff31215c1ebc3e7f9b6951ad17663bb2df9d7716874b81af6f10f2e2fbc514f7d8babef573908953f3f51269cdc8975aa374081332d1c6c61c0dc7c5d5d49025708aea266696a38b7577a7cf23aaadae6382f3d837dc39f246c4ce95b68ef69f6262382e4ea996f7d2eac681324ba63d7fb9b3b11edbb3997d97462b6d77c90e9817483021dbbaf778a3e572596ee4bbb20f7339191deca0e75d268d7f9d586fb145f0abceea45e97b428c46c49d9160562aad18aaaac47c97991750a82aaea2a77e231a012977c0e2a23c05e5b03b383dc1226c5f4a2beb86736ae6795228f6b9787e7227888a73538a1bd8d9f7b5482df5360882ce548ba50d0638ab300b37d067ad4150b1fd221e44642e4883912b88a3d687790193d6548c4af48bf878fde6dd88fc630c85509a60c8cf80f8cd67c6a8d7689ed209d7134d5c656e7e8a9d5d582fba5322d2c9f17933546369a9d7f5cf5a8df095ce168f921d23d96a70a57a49dfc1425d166d86455917eb1fca63235513ed78222e0d8960d201daf0a4b4febb04fcfc9814d56203e972e7cabb3364edc28ea01dde680229a978c99fca3f979c408ae6ce6cbd8dc09ead1b0a64be41cdf6c2c66a313d10089376fa04a58ad81323d06b700b4a144da354f0cacc140eabfb863650e6c1dc8de7ae5ba0408b209a7881a749658f27c19ef82c1d5aad451a15f46483c834c257d76e70228b72cb7ded6340aa58e09444438930eec62525f46642793027305c3367707d1aba085200d7ab9af984b2883aa67f987a414400469f198c11c488b12ed32a0a47e501d0c3a848a602638e2a2a2148ba3eed498960b35473987aac207b3f972d9115e5e51cb773bb109609ff941f15063f1f4af7d4a11502e01bec960e567b246780b203548861ec109133cb8cef9262bad88b1207b2a1a1916bdd03dc8b9d6b9ddc17e356fd653ec3bcf655d46b7ae09d7288e63600f288ba458da86202f02b53ed0c402403c74308291368f60e197c303271685b4e92e4127b5d0785f5b7bef89bbd0f6af224e856db85f50125dd7d1720c8d3e7bb3736f6dfd005f515602c63f69e8008680e66a28876e7a8036223200846a8772084ee8e46455af891878350caac7a8bf4e77a0da31c8d1e6abdc760a4a6e7cf82a998ab329d138091ca57232143d0230133d091fe24ab327d3592ee3947edd6952d9634c6d00e5197f869e0128dee19c962de9d01aba17f86e161e47f803a22c0ffd82da6b538afc531ec8446a1558512206bff3e4e8a70123efbdb33a72b6d94090ff6ee21abc0aee75f111d7f95d61a3296082ba24a6dc9418529ca094705389475133c2e3429cf2bd02cbd15ee889a798b202de4308302a3fd434af75781efd9cbbec0fb852a3586654f7bf95520584fe1f7e2ce2e123eca643407cc452ec491cbd5026d09742b4c82f59ca01f3d2986174817acb87b95eadd4a9127920c465f913691bd9d91b7da84d2fc2c1f84ad1dc91a237ea8cab37ae77696de535c5d2fccee2b75dc4870e5a9c4bac52695c630be3f238f01ba9438e4a37028345435cbac7424c29d3911f3f0d491ba21bf07720a29e6cd4a267953f286e253578ed07cc60845f35d3d37f29446dc83a87d82a5100ca46ec90d02baafe30f299312ec1d006f94f7e4744b84faed20da75fb60de429ac1b463c89f293a7856e983b3719053134f81ad085c33f07a51d4fcdeab2a0a743c708da99005a5367cf3f6276e90eaddec323091dc5a6e979769e8be2cf8effe2e13cfc2220281fbb343b3f429d395511cf3b77b6c0ef7d7add6b9736898adcdc4eba18c92a02e97d5b59be7cfda54f952fa556b8e249dc238cd1bb1e4bac24b928fbb28c097ab77b9c6edfb2f5b9b6dd31664bca888ac8107b64fcb34a22eb1d4c993070673a3d169bbb57897cc242bead083f8ed4b727767113af599e88ec6656d8b73a0abdcaf5ee6ab3a9c09c9ae3a33bec0b2d2ca4c5b0bb2b4158a4544f949c14750b717cd9ea4b019929ea2e3ae4f2741ad99d555c2d8d7501877bbcc339350acb0101f0900317bcd7c8485111ab0a40c4bd3b2f85b094631867322afd0876e236236f7258aa325df587aaa89d00f86bab3144dc2791e8569646dce1889063b923304588bd9a728d4c5698b49588e662f74cf2eb3d0681c8d4b87176217b9c675172ea8920f9b002eb5ae4a630f2b4a04dd0049d7458c45c133686bc397c851dfda07f51504486eda62a1d6bde32f99b426735c335d66be382841926a3c50f3af4d37b6e1b67a6ba4b3e18bca4b65617580eb6c05526ab6dba152564c421da381b3fd4dc0ed78aa0bec32cb940a31c1fd3670015088c64859f79807a23420874834888b7a7e99c4d703c3e438a193c99bd4e7060f1c08828785b492d77649fae7adf8802f534a237cb7db1552c919285443723966bf22d0d5ea3a78972d4971554213f9b670291fb39e9dc013da2c1f87b664fb4b5a0cbba8d0256316aba86ee081220a9f6127d24c127e254636657ce2b4ea0da0e602c6adc866cd4285ab3e7479ca7519a9b8a7da04528d98fdec90814c9a326e768d70c0a1ac6a517577debced12223c45b4e19238aa0432d76465079dec6f97e089c37de1f5386b521ab3531d06ddbae087a1db558f4f3731702893ca1f404dbd0c69b580204d822db2eb3bc50e55ce12d1686a1296317b3293823f02740905ce37059051b3726c373299abd916c22986e27d38cc18dc3137bbb8f45ba5556d0019e22c0b13da9d9bdcd3b4b3168d817d3042593b0b1ad012d031867e3c9e2425c3290fcb5418e46190c4e151d6bd5f50cdad4e013b12f4d9444e2cd61848af061416c869b3169983514f67897787d7b6c780a40188ac78098867edc7457d54eca998f8946932d30380a964006a7a078e328e3011b5a41c25f60384b254f3b21855087fe0cee486bc3a740203463701e09591feec3bf87dc20a48d9e4d54512959e72fb1465e97bf00e5e9f611c6c160bda6029feabb62bdd871435a2e8b677a6450bd5671c6639b22f40f9633dc1ee969cb557c31fa86cdfee49a6af3f5e1a21fdb1e997864f2b0e9a4dc574863bb91496f1222e4a4bc940e97424690a5820a631e2652be3b047629c0eb074877f4e3cbe93c2758d0fd5f655af467063b82b17cf9acc19a0ee46f1378ba9e9d66c4d7489c5da23abd714175a92b0715f2179f6ef707b283b8fca62ea5ee065b7d5b2f7cdade7520599fe44bec72f954a09444543f25095999a95bbdb79173f0582992e978fe8253414fef7131cadc52f140a351255664646e0505fae1f9b2f04be63960caa378cd7a688dcf71d3e7d7d80a734298fd2c9b1e90139652a9890d3c2e9e467c5f4197babf7327aad91b7df338c5e53299968e36265e4be21a249e12232063d0f94705fa9cebd3e9c372c855925dc2abac51eab8c59f3879ff095111a9cb2f5d176301f2d5296fd545614ab0ef38429de6ae07389d4b1c5caf26bdb162897ca247f576103ea0db009225ccc0b9fed50d118356ed5bfeb201e8c0ee4eab1b9133d9f8149d3accdf0bae4b5d0b7cbdaea0ddc85e07e586fb7fa245b447106a8115f80ac4c80db12ea5cbbacb017218d5ae1c4aa11bfe465f62a09ead24f12d5a97b739aa3a27e6a907a54d0bd5bcd9a13947e32fda153242fa7965e14aa4f0c3cf2505fe97f7c087a5668f2ea73794f781f390c664f88648da84e934dee99fea5652c64e0a8324663df259bf7beb2d1c56e1f91371065acbe60f9d4d183f1405d3d1ab911fe685de5f173d6fce6b3e3dfbaf5dd2e2ac7b34216fe912e5802a7c1d3d46960b891029e3564d69d2f6bf0066d119b79ac2c90e95edd570ec28073e5e383c3c13090e0f19ece1846c754dde0282fcbc5544a6079866b753c04cc3478618a93d6714c476253bf6d915dddc596269418eb446c4eb6f627e612012e360a7346dad5b5eb9b3077c6ef62a7b8a349ec2107d9acac9e6c6e9205633fd93668fd9eed95dfa6b21e83078cfe0f298139a2857f6c7df4c87dbd23b71d24ed517864458f68a5818b56a65aae13fcf8db1ffab9fad0af0154feebd68c7f0b59e33bacf3c8eb6cfdd186ed609a7bb7de892d1c292d80efa36bee8d2ea6a3aaf0f57d25cfa5c7a19271794a83f07c754133af09624a03fea4c4a713c9d9d00630c9989fe24c218fc79428bc4972df0ef00ca83b8c54d69e485058c465706b1155c331c6de12d0f017499c10f72f836e9ecb3c0827f954bf0db0fc72c3144fa5bb91a5d608dd973c225cedc4c50d6538c052602288f0bf93d2010a26310fcbd6bc7146010bc155c01a02896396911b7f0b58b7175652466244aed4b599582c43536257808d52b0c4d7a49989caf4565f32c32d784fe69cd5cdebb2d579a4533c729411c0277fa345c8d52e4323caa2427ddb549da82601affa81e4b673bbacf6aea478a43c66fc8b97b0c21e2e1d077565fccb01895d0b0b2930800b777ec7e04c88c9899d215a07178db455587d2e29b574dd162ddff6814ab9af36a257283b29a1d43cf4dc96d48729e0f00ee16815ede7bde0f0455ac4760300a209fb516addc148518e57a8c53ea7e01712b42c94c9df211c3dcabc9c90d9fff48f681a15627f760897456e6ad7cf1e4f1ea7508bdae2d6516736bf06ea49cf4c9c04b794309d07ce4b9f3dc2bcf8269d3d6b00b4541bb7bd64068307fb7944b8caf14c3815bce272e4812c18b1b95421d8a808c7a975552e1ba4b6c67752d8f139e106c0229cad5e9162bbdd85720da5a8081776d11b9f16e11640a210e88546fe0febaadaa632034833b0b4f7632297e6a93793d23a5504c00a160fb1878998314a8ff476702774cdbf528ee41c116ee1b239d1e727616b7f4683df45b85bf2e46424de02a93933c1d4dd8f5e481e23dcd924a7680f668406dfcdf0e07d769921753f659b9eb846ca1171e0cbe9c60897834482dd22289ebdaa3e1d83ca1140e3d3c5b359562fc711083df5d99df62c0a7714180d5614603a7e14412e76a36399cc55e3e0d3a26b627b8f22806d1847084afbd9df21ac2ae62fc5351eacc0f8bc245516528b14c8fece8a20f81bc5c70857bd6bf22c49d252c8b8c0c4af5fe205f7665825936e0e6ab6c24b31d32607917a132bfec57e4322660bf5f2673b96e103940337496ffca4ff9931c219539ebe27b0865b99f24b0ffd4afb92931cabd0cc1bd1adbb16512750cdbeb240497c8925cbe5637647f7deafc96809bc63ac886e90161a06e60d592bcadae9e649658e68489a2ce87a7ca281841036a0fed65502869be60c6ff38bb40ca98c085fb95456a483bb9ee34c9c423544082f32a1c3bb56bb0f9d58b4439f388fdbdd3c6acc42a9a51e1bded6e1f27a89d8df78ec63b5759d83f10cc3dbb2d6d516e1b74b69e75cf7013d892227c5aa5248ea648f9b6a0e2738bfd33990f803cf9bff4eefc65187d0abb6d160d1884e7ebb953251e7c1f5d2e26404e83ab029ae2cfcfa7213e737f9996b4de0071056a09d50a87b7c17376c6482b61741fa106844d5c88eedfa799ba5a947898fd3fc22c20cc782c5886d650f888705434b587a54a80fe38f7cb52111c050ffd484397340b394150faf0c5c5e0afb13df87d350a0c1db8a9138e5ce9a0efba6d25f8a5ac47eeaa725f4937f32b1b2e7388917691c56c7522659986864de596b353c0b4c0d84ef079bd6b46ac1d011f1f155cb3c12b20881b771a96a4e13436a27fea4226413f6a59f1f3970d1958653ba9837c101de56f64e202ed8cd471d84cb1b0814129287eefb26af04ab083b443486c18c9fd58265178eda76e80fcf5bb8c88bb8e2be2328d1d782429754bb08a14d2a171652df41aa9ae2c77728908f3fdc3bf4529a87977a18193b49c4072f74e2f50ee7f1189c699b7b6f9abc627cc1dd412422ba0ba7306b5ef9af798b24f0e9eee0876a999afbe0780a7ba571ef8edaf44cef2cade5156932b3b2d5b0b1726a8ae3f9d3fe995ee12b1cbc816c1a845408bfc02199d21f8709dc28b61154cbeb6c03321b323cd7c7805ceb7398fb14508eea32c3d482838203370acb804866819bfbdc0746b686ca037520199f3fdc7a568630e71999d138539847133d4083e2c1e8574986bd78811b9f333efd19c80f1c69571d5267adf2571c42d9b6751139a13db8bdc835cc7df950cc31f3056ee27944fc8315d539a9a27f19062f1ea34f200a7208ce16b8152db71d5973c9bc1513f99e142482cb44cc479748d46df376454e11d2abafc02d1e209e64d170b4b04b6dfe9c1abee3cfdd03c523d71ec1370a7a98f3135053becfeaeae7ad573d622c5f4701e15f02b7821a0f4be0c676c408d820b93d81db42822bb8e101b93e6056e6ff0515a040fd48b4242a90d9f0652120a9abc5f81f21171724b78e7290603fe2c910b734bd34857bd290fa0ab871031ba4fc82edecfa48ec90aa3183fac8e9ab907cf766c25c8815f3a199d4a910be25c993685e1fa97a6eaf44279ad8431f33fa08e3577d5b2d78af38446d75df36473bd4eb86e27738fc03ab79be8df7d8331de08e78366aefa8da1bbf89e64427817c9554c8c51345157b5889ceb38e30160766dfcb77be4d3de4aa239544e5498e0ba1ccb791c47c1bea0ecfe365d9b91e59be4d3478d6f1d17c1b8aadc9c1cec1c5de74f3e146c2f9f42c1788dfda6d249e907f344692089009dfa63c8fa0502b36ca4fb6b19f2cce7201edffd5897f63866f4be3c729286a690a39457397a62338e9591f4003b7e08a06eb415701dcc2cf4491e9b6a7fcbdc83f4362ea79147c5bbd154ceedd6629b7d69b0c7c254a986cea6f227f0a71bbf40338b8ae4a4dfc83a0e92ea657a591deae25e482c833fe4befc10a517de83260f3226ac632e46b082585c6ac16afde12552603366cec0c8f5e0ee19d60507e0cd83a1c284a293c596f568e859a0440a129abd438a1481504f492c980ad1bc1aaeebeed695f20f7344bf738c4b275cda64bdc0227b18b70f3eedcc998d98ee95b82c881905bc10c9c116d4b7b095bcb41872554677b6c12011cbd4ecabd88c771e6b8671510e7a8d44c199b0c59703350f038262946a65fddee5eb98376fb11bb3b379882910c2c08e27f665525cd27b1b97a03b7322844877661c99ad4731cbe8022fbbb438c6cfda70560693d9c68cfc7d9397697700ce02a5ad902b4c8b21a22365e89a81620480d17655b9f899919a8ab055c8471a2adb49805ead19ae7f45d6d3d4a128ba8c762e2a2e456edbc4baad5cbeb3eb8d171a309a3491cd9544268c016f5905f5e981b4a0bd145ecf7e735873c134927b78526afae6f754e5e848ddb1326dd1c7a77eea279e2632b7c6b24c25101fe4e9530f3bf96305b31ec060ea6759e756e9166cc6287460c1238c7bbf14f1ecfaa30d1517b11c6c74930c6dc156795ff88c37e073071ca6f33dd06fd14192ae93132bd588beeb0457d68ac6bcae98222095543401c0ff8892de1ce651b15e116e25d30a81bd0899abbd1c2e2ba69ab74104b366bf29adcf6c855f28fea16cc6c60c403a4ecc99e9cb45bf76d6ace05ec824eedb54c68255a13671bbd9e7c93be331c7da18f4b5c95ea2e1f869641f7621599f0ee5086e9f8359085c209628e93955f59f0328a3243dc6cdb0faf6d4080f0f099b615b9b327129a4feedf2f48f4336be21bb942e5229e4a2023e1004f69396f452bcd559fdb5a12b083e7d1293a0707ef9c4329feac452b170674b9fc9d8af80162d2e9be2c8e02c42e65ae8fafb8e45af6cc66c7dff81d0abec3d320bb0b032f8da85339a2c7f5b2026c098fb31f7454479dca25f25f39067d120ad5ae6b2cdb1fded0d94d945f1aa404c0c878e39bf8e4d83feb88f0f23f2ca95a22673de1bda6492c0e513527798e3b90782ec5442e397ce9ce6fc4a334b9572ea09e901443c5a071f74336deedb332fef4013e37f3c99392b83e41d2f053c637c41dc6d8e49412fdb30657c4438cb61efcfb95bd3452f97bdf49b0dd6415cea6405048a9e9a6842a212b468c497d86c23fd76427070781c463e3f4d4e82a8e52388355f3b4832f3918af1a2098a662bad7f1474bb319508d53cff3d2fff3be13fd3fa65151a435bc0358cc355348a5c9f7df60848640ae87ae03401ad936d7f1e0511cecdd479d9285bcce5fc8956bdb672f3cfe961ac956738b9db24d332ecc69ecf2aaffb0ebaa561c69f00d5ba893af08081b369d469fd3aa06b601026bab7c3aa7049344693b8ceca92e6b085a77acb0694bb4ac76595438be3e1485a1604ec699904e2e0d3e9b9321866463e85c216e513159a405f8a6c21b102a862ed84ee0998711638cce617048d502a4c3257c2fb9cd8327cb72bb31c1acf426812392713620fbc91755f0e6f4bc5da761f5ed707439359786385315af35b2ae5afc82ea78242e51223080750b5f463a9cd1569b3a34299e3c9d18c1531eff0cd563f9f7ea59273f457c18cff0895af944f2d24a37474787b9a0f40377d8b442b2b9f7c9e53997b85e7f8dad7e8b4506e37c0dd62abaa31150d1bac02b95111166af9e3384b31d594c78c8243f600204ab4a7ca0392f50e0950e296b555f830a381b7eb2a48d9034f6410cea803281026cbec0b814075acba36955a08c9f6782f018ea30aa9e686777a0887e91dcf87a897716f92913ae9e72d9ce450df8a4cb7e55115f5e47b7f167ce02ba22d6108893a773e08eed119e880e700f3683bd8af6551b1d42c85b2ca44422eedc7a775ad0a137921f12dde0290a14c45ab4ded1559dc0c141e606904e78fd051608e767b7304464de5d8e119c91b408313a994b3cfde8c9aef12a89ab9c28474fc6c94925f02df5ee65f128f8d9ff222ceca4a0e1927b73284226ded6291716990dd3e5afbf5744742bde6165f22245b9f3fec8fb502a39e84c4c15b2cbc9073e78f221279dc4849039535f67103f88ee5f650b0da12c352ae597c45335a8074550e6ba2cb4ed4886ee835f15148fc77d8a810684c30df74b3bcfe3843f1e8d47e2af59ae0ccd84af72d3fa60c633ee08e3a872e2c0ae62f0a33db40e4eb79686e88ec14ea1ae1f38dadc9e307c11ba6f2109d1a85a9558f867022b925100daf3ba4ee482fd87fabccfd0cacac97627a7035f391dabd97416c9485caff551cc1488960622b60742fe8661c75b6e4ae9172e6811050a3760075f7684494f903f64197f628576d3f2e5dec5684f904092919d66ac28643ea37d327c5422fdf97df42487bc49cb420d00f531e2e0eac6cdac8db3261381adcaa9fcffd1aac29e40e7e21d1516f71d6658ab1cec59f1f23115c5c96a516bb242d171e29a3965563a36cdc880a835190da4f20bf2d0259f8cda75e63a69b4db00c26bf8f6ab02f5d5648e476113be3559b478f807f9479dea3e8e3ffb316dbdb6b9265b383c0a21db99673494b68a903fcdc8fd6f3355f75be416e34dc64d3a161ac54d316d4db2940512c1d11c4813c95e20cda019bd678f36e08d08244f4107afc1217a05ff978c19a0c51610a7600897480febac02402dd13cddc3c94ab86fd2f7d912748fb8a5da7834f192f5b6cdd7633fc5775bb06e1953239ad74f1ca8de627a128c0b13308bddcf578b92d92da130098cc2de083c3752fd5ee5d64cf1330d5442e4afa9b654d2999aee07c7db4280cb5cf84d5dbc76a0a2a60e0956c729b4db33aae96243f5f1cf9348cd3e50897591a9e443f5c9b948022423badb7fa15f769fae9e13309a5a2c0a01af928c74b818aec11bcb0d19d3ef29cc574676841538a227f6ee85a0b2eb3fa2b49437e4fcca4a57e927624a18bc2cb926a4c66e8dced625fd9c0cd1cccc556e496d2d3cfa32bf0db064fbb1d450a8b3c384daa79c85a0123b9362e92fe858265500e3a02f2c595a971211381cd7e059f10edb07aa3e5891009068dd513f2829ad8011e87bc0fd5f0103841f69531f0e007cfd107aaaa637a6fc5a92096cdf1980670ce8dbdd42e83ac1be53b07dab9a636541caa012ac4fe463060d64b61b566c122f7c80f13e0e518f10836ac56939c84d76bc242b6a6eca809ef004921925cc8e4f8a555bb480844d9d90ba41d2ec549c07e26fc31c50c4d7b1ebe52ebdf3f5e267e54fc44150e014f50f30fa3ea1feff84501f1c81a3d82bc587ed87bc37d6a237f5d5dc009579dfcd57abeda2faac2165fe714f795a28127415a02e30ea4ea2e57783cb943628f4e643a35b86db00d4a660a88add1a654f1d6e175723fdc0a2400890058de7a340c5426b35b851b0969fd8c1ed9246e95b9e801d061efb6dccb0093b3a8cd3a00a52abf1cf428357282a79dd02288aaa45198cd1c4c09f619b11323692573fa4a18521db360d751b602f5e3b6728bc1d98797b4aca21213b5cdb04a096731dbb18f549aa542eed354515752b415145bd24f97399bb1a1d4e61de7c08fa881316eee3b98f23446d61e2ac74bdda263335c3101eb829ad8f1761d7634786d712d0d19fe49c6a5f94858ef5ee223db067c154810348763841bca801e43bb29f341193e09a1d2a4a8b662cc718a82bb83ab25f49ed32a425f37fe79e030c69413e4f88546de9463b841b433eba3e78d7f75ab7f97fca48b77b955e198d9d365d20fe5ee0803233cba8796676fc939d9a138b7c769f341b06986b59fa229f63fea6d1009af6f1e398a5d55eec1c9c840b6942784510019004f9afb962b7929696000fe4ce82600f1d18cd93ace2e9f4f56f38f4c04cdee868aad75140cac5a82155cbe22726332f49d4031db2168b32fca50ebe0029d8404498e055f9045e7792aba12118e2144431eae59c590135fd275fa0d2a1c4c97a746be34a2d79640e84d1b15b68c4b2345c926a38752288ca3649f02a3a2b24a6b34edcf8719cca736b2003f43e2dff63875acfa91948311509f2cb50c6b2bfc78846e9edad08bf8fa5ca874321999a286990dc2723745251b06283c76a2651fa8224eb157824e16c9ae3f9433d774700069a25429b6be5a0e3150df9fbbaf508f62688053e01be658487ce19e1534b9968af449dbd86f5e3dc101b4b051e4ffe73b3b58c79d26c0112fdb7f038e8de0679e07f3d19c82838f934902fb77c2e7c687de203e53f73070282780e31c829ca1c1027b1a84a05b6ff3e5867544639cb7f808bd01a9ac004f2430b123e235501529bc2a5ba5dc3a24ea82406686b293a09cc3049bfedd343685499ac6307bfbb602d1e83d77c3fca1e114103e7b0c68c45d71b1b5a54d3919eece7ebb15ac916d250263139cd294290ba851712048e03d41048f6c91646186a30bdb0a289bf3d3c894556d396bcfa148a91527afda85a0387e145b4608cc352a1101f2594d5d9f3e5c2371951505bf477ecf3ae213d5b2bb030842c1db81fb1f1006ab9cedd322c742c31973aeed0dee1f181ec02d0b1f3e271f6f76d0e93497b9d41ce1f009d1d1395f82cb7e6589c6925a3f4e132e48a45f1aeeb6bf9f9978b004217b27245d6f2273b3eb931f9eec3c0a87568b311e793274199384673d9fa6586cf7185391869b0c294b716a8ef4b154662a712f4e44dc904b97a9ff3fee3195af66dec07b03390b0c6f7a3b3bc1a250110fce6647ba0f19dbc20881a77e5edcce18e79c8d6d7c468015cfa054dc9e4f06d4397c3f8f482cc757eaa25812b7c1eed8773b0b27414ce1ef4d20d650e6a1abe3006e7ba5e353c9ab38b6ac04234526ebc8621c30dc39b61a80614a9085108fea7b3ea13c9b34ba928390943d6645998ecd46ce56b01e7f4b2979384624b5e6a5aee602c823e36d161e97c94e1dc0c87af77003ec287893db14f8ae9cf4dc7617cf458786507e96c690e580fd4bf29441f1fa01c98e77ae6292e4186db76059de685417b57902d99d03d0e300b314dea270f9acd36fd6033af2713ea431d006420016373f2d943264e4723ddb0a861dc72b0fc2846726e863f96fb30d2ae5c40823cc6d5a4b778b90c87647276c2f44d8a88f4165c2c5946f4ed8caf10d56ea2c6a8b1c19a4e44c1594a0214927e4b1bd899859414a1806272273232341ef8eb4141357ea23600408552511fe30869e8ff493dfd57680300d4985194918fc19a06fe2085c7f01f75643879c5fe5ff7fd4f52c9c80008ce2269d60ebd6614ffb4ffb108d58f1c1fa598b8b86fe958d8e953fdcf20487c1f6af2ed44dd7a84bf6b04509e6bf7e7c782e6a5108e2679bef7db5b9bc624042d6b94da0b0ab184f9982b0bb32621611eaf2b156893b2c40bdfa1c8ad2ac4a79315600d4c18e29c7fc819ab02cb6e67976036e59a3ad89899a7664b6a09ce27a74de80efe119533ee3b82dd37ed508fe52b7f57b73feec7ff761108ed34a1161a6c878db368e2357b724adc3d4d6438c18cd34cc974f83fb4bd756511ed9f71cf205419437a413827ddd855dc2811a03173211b34acf35753e4052e6070d52a649f254df432e48b4cbf8452a90b07ba9be7dc97a2df654329721ef98ec5d3a7be0ca2a8737e9403e29198544496ba928546ec7e26bda21e520571e8abba617673c03f1c6fb9d5b96eeda1fb96bfd633f3e26e5c395c2cbadb06ab74cdb51c39ca3daec804cbdf3e7139db3cf15e66614e19cc0e33dc2f01ac4f045a16c39b5720d8193707b9667e647ee4761b957c5356faaf1e6b871af3b6cfee721653e47ec4fd88f5e141ad56024c3686eae10040afb3fd273ff25134a6a0fe131658ff9effa6a5b393359d9827466fa396365ada24644b99924c01ad09d60996098efa9ee38e60476f7de0dc714e44dfd3c93e6793624752d0ed27f5743f0ac71a0a474bea91a4a03ee1d4ef5d94b932e3c499b9a3b78cb0a3eb78159cfa8144915c085bb8126e3f4b8782ffe856b8540c7099478a2ceec4e1d475fb27149c0a81cde37a524f158563b51fa9e78a7774d284c231513866ea409afa7b36c10609354398ab20044d1fdc0e7d0aab33a577a879d517ae24df4da6fee2267a68818d7fe8370743f25e216739f2ef7b817d7b6002470b3358b90e4ed1457899478a272a0f484c9c964bd9e191a2d31d8f27df93707038255980386580dbff7de8e9651e299adced03e7170a278e111367db68385ba5cb5a94a1841be29c6099478a2554dc7ebe13c7657b48fa4c1c2edc89e3286fb9949fcb3c5340b9355ce6e101950ba5797a0085a7074e80c6c0828c8bc513ab3b4e9c9c25b7b1602171fb27947a5daeb780aceabe302f7ad1b9c9ed65280b61534a9f6a56b1acfbc217ad2bd28fcc9b7d28dbbc2c9439ad2227ced4e99367a0140f25949c293b9d512b6296659c659c655d8f100f7de57b8b53a10f7db7053b7a4f158ee9c73a1efabace09ecdfd175e6685bac8519ce70434e05a764e85e704c8feeba1f61bd2bece8ad3e5127b07062c4edf72f38c54ab8fdda13ecece9935fa190ba802da0b7b2fc48dc71e24c9c9585a1cf57aefc71e254f156d602c2721c1544e8a9cc0c4d9c3ef96ff3a22109337375781183fd9927bdd5a20c3db822161db269a1ee3959530a47f1fbf0270e9bfa59c251387e2eb8a29e38467ab4fd118b48c422128958442216d173376a69e1185108a455382a3e565aece2e74a8b2dfa458b72e24c1c8f55002bda8ea6737be5285ae34fae1dedf2e54b667ad3a24f7ea923bbdfa412a7c6298b535c58ed8e14e70235286d6a0da7454d63d91c0de709c734adb9e9d3a83d61723ddde17aaa3de1149ba6dbd4b4d8b4a6a64f4bfa24bf332dd891d6dcfe1b5a436bb49dac0872fe8d94724a2943b77b3ad857ca90d6200951dc04e1270841266c07d3f964c743885382f5af9dd999dd3925d869e4fa23f562dfb56cfdae65c7ab75fbafb4ac7461e5f60f95ebb3d3228f4b297da745dad398131a3821c66a51b3624550d81163dd5c61b0bc6237dba4c21db33b7ebd859f9bbe60478c85614efa44755aeca782637ac039f45f2095c231fd6156861d3f2f0a3b7e1f56838d6f8c74677b29254bc9d2ffe98da3d8b518038c4bc30986aebfe48e87178505e98dfc66bad3b3b1151781469a73091a09dd2395723c3dfa4a496f9833e6c1821e2aa0c51d40b07caf108797e148adb0c137f009eb193f7b09b2832d25cbe9a1ec392e1c6913577ebf33c627cd9d5cf923a9378dfbaebbd09ca1d09ca14ef43fb44a446f40da84b2e81490ee501efa5339e6bfebba85e9047b8db8c5101356ae6704cbd299c3b0324b1d99ec7818e9c1339bf34a90b768c1ea6ecdc98e87ac527667a407df27dad324515d14a4372dca496dae510f2db86991af3cc3f57012c1b640e5f23944c22ff9650893c9ec45ed3fa382957f0036f563d12a027371fb6738460b3bcecc48144cf6ace2af169c09b1300cfd57b52f195cb38382c5b4ebc71b777e8e8c67f7732422738590cf97068d21e68f455c190ec165dc272d3fcae7fe1b8d46a3d128f442db62136ce826ce8751863e0296e780ccf7e7fefd86e110a39fcfd5a73d97522fd203df913ef71b0d89c85c218620c2ef13d2f3f567018f60791a252a9627812360791b02f15858c02394ac5447d02429e30a2a7c810c6620a30c15cb27a144c51226517a2f0c87982f9f1b854fbc9f21f009cb77e0939a57b1bc832390cb85164c22573b2c9b1a244a6084ea082e43d512d6bca8bc5009ef4b9ef725d14dedf24978218d4683277ad0840bae30024bd5f249b8842c9f444b487af943aab514e284df06ca8b6dfdd7f659c7630ba2200e32ecf5f2c320c021e0bc2e7b473b4a2fecc84045fab4ead30c7bf0b5f922686ae412e6f9018bc6151d9e3bca5c25583e09120be9dd248a32f359be7fc6514a943e09afe491d84d2ca1129f2a099627854a54551249d80a0e194954b9c2843344512511853e4bc823dbe2c572c4b57885735e17eda10557fa04339fbe937b855c7756ab3b7b68c1955ed92641d9f8c628e54b1d331c2273a0610638863e7d1eda679e3d6fdb10f26ef3319e7362cf9cd46184fb23f572cf7f3990ef4639f0077694f9914af0a92ec1bebe5c492464cd104a7fc6514a54f477b80f1f8470ea02e50d817c45d5fdecbca018e0d1dd211c439fc484757697b303e7e538890458448bf43530abe011b0c8469560917e286bbe9d2bfce1be2ca44ddd828bc245f9ae8aba73e76f92abd5daeffb174599f9e117043bf64ddff40ef3a7daef865321d972d37c5bbf20f993a9c0cecf6260fba6fbf9431c55fad93460d3fc4904f18aa7e2dbc19dfde47bb1a3b9851dd98752d2121608c780adaa3a88a8b7d60eb323d82a75f8d7b083640e935335ec3270ccfcd9346014e9673f19fdd83bf8b1a9b8b3afb8b389206d6a07398af4f3b9edb3fa25d03d50e74e52d8416e9a3f6b1d85d6daf0137d28146f173ace6785f5ef97393e5fdab828fe7f5fceb47937c9af3b976d24c99464cd21b1f18d9181ae9481e11fb4dffe872c43c22a9fefbf0f888f8c4c739885350472306a10c6949e293fccac83515848248c2ca4bcdfa74939e4808762ccb069fc6417848f1deb0614c6088eda13b67f870751a016fd87b40884107f1a8df2a14dfe583883fd5869f868d1b9b023056a51207fa735e0b2e82cdeddddddddabe08003002ef78946ef6853a3e6f717e1a8190ef17e663884c302071cbedfd921de1d1c16ff3332220e38cc961eda31657083c4d13630dfb8a3bbbbdba0e105625f6bc868d06a6c3438986e46e845f45997510b338a7ef5eaa6923bc9f348259696918bfd442fa1191d0c4763aba1d190d5501f03af90da9837dc86c6216f606631e45de1c6ad8e92c28a3793a21b033788386cb82c042b9f6d84e0d77069a8418367709f5e2e9fbecb7d6aa99ec72c9747dc2711f729c47dca9ee33ed53e5d7d9a36887d124d37e0b0e1868d10fc1a68a82186308cb2a67466e7322b24f36c91dfc5099b5d17276c6dcec5098bb93861afebe2849d189755ee1b4216aad2f9caaea0f327c74cea79246fda3ae79cf3e23979cef9cd393166e639e7ac2edb27c792fd7734a5945ac1934b580cd183af74ac46814551a3c0a2a8516051605178fba417c65170facda2b824739a9cf4c2a4c4b2cc07953fcb3a1e98b6dda89cf588782d56d3b6297a6e2dd97b7bea7532064a24ba1fe1f567e9fc07567a4b29a3e8b28e0c16b6bdd76b7e7742b9a0f89d502e287d27940bca05a5761807e508a613727a6175dba4dc3629a588d434ecfaeac9d5b66d93629b14467860efdf615fbf48735016f686519d191629c22986122a52a49b62e31b01342ca972fb6d60a153a345830e0d3be04e086463c7464f9fb8efb741a54fa23f526df8f409b3c1138271412cee480316b76b78f152c5ed7f69825123269c78625403284650bce48c9608b9c08ea29f4b43137cf298340d379cdade83018bfd230d4dae17831a3d9ccadedbc2eda771c329ed5f6e3855351434fdd09516f8645455a3aedb093d0b12ece8cf89309088b6c323f45b08f4ae038ff8fdc155c8e20417fba9615ffb09f71cd02772d0c4dd9d7932ff70aa453f1df71bc685dc2e4f58efc26ac331525e70fa348e6c5e9c8c9e6bd23b8c6e5e58231b6ee431e993a8a64518f0499402162d1103d1cf16c6914d131a88bda8469402d112a21f2e6c7647d10f88d5102d4143f473fb3590c638b279b9b9b2b046fb1be97ad4b859387330a74b34148c340a4fb87d842c436543ba72d392db3bb84664146eff45c3262f37b75f6e5e6ec41d16216c2c1d2939db4dc52ac6c4d296fbe61bcdb9724a8e149d9999edc2c971e33fbf1877e53584cf39a713b2766fd8a807267c5a6cc730c73059abe73d0ed782ae93525e977bd7b8bc7161b7db5c5898ec76b7c5eba1a6d3221356d3b99c6a824f544a8b4d4593db1408b3084e9f4ae9133582452ada882b252d0210ee0eeee84da0c06f38e6abe2e3981e8ee9b711063b6a3a5f1977be1179359d2b645f718cfcb027c81b5ab58fb39aa6d12cfb1dad0d216f565d88a36ae55488e25cf9da6754fb38ab695a46a15cf94e5a64ae7dc7eaeebfa381dcd0db3fa40d80bd4bbef2d426fb5132d1317215eeb869adbd773cb45ac5605fca4dd3368ed134a9699dc6711a94166f9ab09ce0b4c85684705b00b5a4fb755d184665180408f7a3cc0074c8100095853c22efb5d6cac8008911258c2b6cae9052b23eb6c7ad75940f37f5d76aedf7fd6f4170aaed730ff7d88f4626d184282b55922c065654e12802e21414dc3cb850da00fd5c186cdf915b3ea4cc710eb00e09858e1c990b128763fa3d29ec289fdcfe2717e687f4a2d8fedaf3a2f4a2946214c3c1b13e5a1266dc351ccb6eaa5877b76bdbb66d1af73fb48a03e94d13d69416bf2b09ee476e939c64ecba2e0ca31b376713172580fbb6fdff604898736476771fc5cb94d5a446486f680b8687d0f5fff0fd0ad9434bd891fa3869b1096b8602512bf0cbff962cc11b1c63039bfa0b10039a6ecbf8734f9fe49398b0d2ffe715516bcb7aae0f1b4e1900cb5898b02f8a542c83192eff60662e4677e41ee62adcceaaa665329332abdb865d4d7c88802e2a6aebc2f10e6d734e0a068b1102b2e3554381a84edda9a13d353e3e4daf392ff739316cea5c39598844d8acd7f6320757be1a68a8f155caa90c460b40ccc7626360cd412f3033c335b77a5dd767e056277671120399270761dc09faa5d8f5d3410d6c8ea19f4d6e825de802459c33fdbe3e8932a11f4eedf085eec3b14fde3f37390d69d01936b0b67e5ea8bda3896177e4aa566b9f25b26ab5f6bf99594f3885037903cd09a73296a35ad552142f129fe63b7dca27faee2f44cb9cd5e2548ae528211573232c0c63b16ac5b04edb382ce3364d0b3310880a58e0025a3844881020ba724fe735f30414b7bff2e0f67b15b71f63bd9041089bfad90abdae8bb2582ccbf5280bb626b491706c10a21c4e9d6ecf7c359becba97df21b12ffa2e84711a24519084fc99fe64014760106ceaaf2e5b3748a20411bd7d245d108e42227afb30fe4142df3d77301efe100ab9c56efac18ac219377194ced9a30b051169318828769cf966420fa4fb50c82136e3933660b8063e551c6effd69247fdee7a345fcfaaf6cd71cd715bd765ac293bad20b6e64e90832325cb51d7f7db3158e63a3518b048ddc77a59b5e6784e155278aa50c2659e2a782e7bc8830c5d00780babe5e60aaa5790a36cbd9ae02b08f3a952dae114b68371c13abaef1f311edcc6a0b8fd23f6c46dcc88db9812f7476c89db23d6731b5b42ad1816acbc2de5155e402ab02d375710a71a6b82d9efbfc6b8c4601d5d4844bdccc0291912f15de6a9e20ab71fc381d25c387e33325a18be0ce50ebe3a1e1264374dcca64f4cc2a8c2ca0d9c70440fb2e0854afeacf50a6253ff75ed5c41e115745d2c47d19f4be75e3832bcc6e8202cd01ff71afed01f5a330ccb300cc3302c14c274aed499d2e2ce157405b5b04256784eb14c8e1bd21d9420d762c8bea3e5e518e61856eb1c63c69d1486c6ae1db2f6ae87bcbc93859a691814a09904ec8080191da4e84764e5b6cc7590862bc51c3101b8fc3964b77f2c80e862b1bfe31c3a295d4a175dd9342a17c3300c7b6dd3b28a6593e562e0c42e90beccba1e7ee56b608e188e09400e321473d4bfa294fc508d27dc936ea6a6aa82845ef4a2d02b51cdf42900b77f87e8fb452b4ee9b8fd223003b2ec3b7a8877060bd6c875c4defad84783533ad4601402d8d49f85335b582e54409b5a077800ce816372dc58998c0b569c999911fd681dda9148e89fb6e2523fd6417bdea1a6aa34048c35f8331997605f01b0a9ff00ddf110ba3294e1987ed219ec0db177c8be5fd65aabd434d69491b97a2cd0cc07589c3287d4cbe5cfc129ff98310039dcff007667a48e50a87d9cd0044e659fd25efa6f94dbc24ad06fb655bfe8bcfe1b78040444543004d7158e1e165144329000f85484c57e664eddfed1c6e50759c70ce7d0cf72092cf60b9fa4cf88c5258c231f1f4e71951b2630aa8894c22de38a108e9140ab8c194e7173f184db7fc92adc7e29bdb8fdeec331fd333eb7480824c34fca4f7ef2a3d77561d7855dd7459344c55d373e3e404d08c1999d99c5f304ca107cd0d333041fecb464f780154be147a53d4c55d5135ce9844b25a76415b5ef0f42130020d5f61a4e0b080fce4f95adbb07b06a0bdf4de294a2a37c8455bcdca345195ab83234d2a36505fb766bdaf4918333e9a461dcce6acdaefa57569bb06eb6efc76aa4d4dcb51beca64fb3c9e574ba7f1f96a38931b2d9da63e991ccc6dccfa99e0112124892815363e054fd54f6fd1248fb7e6e0b2b433aa5c53999b0527a26b1fcb5a252386a199270ec288164189cd2e154eaf64b3174342882635a046cea97354608715336033b4a209b9a054b0109b0bc180cbe227325f872f96f30d96e7f8e1c6e07e076cc6d5935ffac0339b0bb1ef36ee1e89709c7b880635ec03130e098de4279236d98705bd87951f710068614e3f6674adcfe0ac4312dc451d246dab095211851c5752afd33610f59b55ab55a3946ca2a65ad478844f586639ab0a648200934b2e97af4844d86535e0939023c3d98c201d91ecbbbb9eda89654f62e534c19c0651e2982aeb780eccd40791d64d465539b2efbb8803430eff22d20cd8c6f11d26283342eff7d0b48d3f22f63fc30ddfe0da8c50ee3b6bb036541ad2b0da573a24829a54539258c16dbbfa825b01a68d15b2df6f781d6083b4a1b1c920f894425097624f590a8342a872acb916941460e6e86d3af83a383a383a38323a5e5861414e42852cf5849414330c20637347f022d6b6c2e779dafb55e3c469b275ed9834562efd56abdc3eb3fd7baa331ec7aa51908233b1e583e7b1abfa369bc75add20c093be3e52dbdf61f4853e3bf6f9046aafab7ed0ab6e5e56c79ee78b8f8d75a2bb75c9750566901674f8b934a8bf3a7ecb438e794351c336b8dd203bb35942b8e995f04b528c768717e0f4cdc00c9568bf37b15d4e2ac30de54fa3476cd9d3f613ce4a1afdfc9f233bfa1f42947a74f730a933bbf77fad472e7374f9f98270648dcd9aaf95ed3a7b6e9d32883ee3c7111ee742fee94418eea9a1a8d8a1dbba66b7a49a366a8e68cfb73597d621e27aedcf9eda4714267b07306d82aed05f6c6513fca55e8c7bea9359e3b07d47850aa360bd600d94da29fd143f4f6c98cdfd135c097df2457037c79ee84b46a06ade0c73f170bb59fdacff934fa540a4722e1fc0a4e1850d6b84d8b4ce6b380b2498bf3678092e5eca61128a291044bd31f85fe03fa9190e08361d52a1c2b1716e1d6703e8ca66d3b734e7a2714ab3ba74d0ca2981c500cb29850d4cc89d5be992ec69dff37c40a6220f38ae74a5e0c9b73eec0eac06a217779ff7c4998b5b1b0f64ff99347d959d828a594ce3927dda460c7ca1be59abefd5a7b2432eba5dff42b052db5b3e7a4b3f341c985f9417f524a2911ee46773fdafd4ad9891c231f8b0dc4b947faca9772728c7c1beac2b2bc6033cbb64fb4f341c99d23bd17cc0f23afe4b6146a9582ed1fa120ab872abd93caee4763a05fee24c7482b804623778646ccb60be026a5eddddd19ed6984de23f48a6d04a96c5e9f26f7c9660fbb7940e52422034bc4bb729c978877c1c0eae5d5867be36658bdc23ba24811257a371b7070d5ef7603574531e493fae5235ed7db27fdec55ada81756336de3ba90c88e58481e6513fdaed4fd28b5b87c2f336068d4a0a18607431b376cc07183e9063134c3c931f449e01d8fbf7830b5f73e8937dc0dc371b7ca6536dcb8da0358f5d9b8db16de8d03efd6fddd4235dc4d44c3dd6c8dbb8d68dc8d05e66ea41977f35eee566a71b99bcb774411db3d99f2e2dd6d06e96e302c77a331ba5b0d7b371a4477ab2174b7efee0672770bb7bbd9d0ee7623bb9b0df56e38b0bbdd70dd4da47733cdbb0100871c5c06b24a8c1143934ee98dd56c0d7aadf57b300632380eb9dce44a1c6458c6945c068fd04b431d9c903920e9b93d651036e7c9be955c06c73a1691e37a8631061ef17bcd19562555ca2b2eb6383b9c7d79785d53077694c121863ffff4e9026094e42042643885fdfc7f79198d5ce6ba3ffff80b7410d107c7be724722b4c9f65b68b79bcd860c3bda1f4a7f0773012e45d9267de572f672dfe5b8b7e0e5e4e5de3b1e9c0ebfa3b91f051899f24a2edc31b22dce1d54e8e97ba8caa67ecb0598d1c1e6b8b40ab13da64313ae431c64d85772c17104573a8ce3402577035be76a3bbc82b3c58d75cdc0491856b07a341b044d09b262498be49bc96b3ce2435ee691bdf6f24a0d1c8ff4adaf64d3b4efc1577b25370325380eb9f65e1a983df6150be70bba8b6bb928cef29b50c76d5a3823b2dd0cf7bf69337f5ddb425c6773db6bdc3443e99c4280708a514354a155a4bf16d981bdbed22ccb324da3374d5840459c553b5b8274a7b4efc563fec5b2847d82bd5f665872af70b4a1cbe4ca6f7753124c44f572081b0de10760b835ae78698b7368192ca12dfa054e79a10b9ccabebee609486d81535ce01411f5728a3a388531cceb35697dacd649a7502b3826081c7305dda13a54678acb568bfd455f373c76a4a59a56d32ad40afde9935fe9d3a42c1caa934373a4b42877785a94393593d913ef0adbcfd20648c8eeee661658973a67f522b8ec0083fd6f77c7300c53824f8f3ff7fd38c5dd60ab48389653f3f6878064fefb651c753d28de1645d1149bfb749feebf008fd2022ecab8d87337011baea2f82789229fbb05d40d467362af5772af7074795dd2b718d5b0c8a228d66aedf7fd6f45ac86b45c4d51fc112f8bb7d2481eac563faa241c0a8054dbffb46c4eafa4952963e5768eebbae4fd298253ac7d0e196904eb30491b1c38460b45297694adbeb969d2a7515ae91a4771dff26686a338ad388a0b61ae67557513cc15b28a432dcaf0816ce590115b943a3a9c9237b20a46c926d8d4ff7da391bc913a43aefb97fc02067b79b1a758285b12e80977c900184aa3c51731c896942d295bb2f537fee6e6c6e79cdbb66dadd6880bdd711df7f239eebb900689e843cf8d9074cfbdb51a968dac866548b8ef422f0a22fa50f7dcffd071a210d741e9b82ee4ae8a75b737614dd17a23a3f453b252abb5dff77fe97cdffff8398a55d91d67741cd52a01b0404593c400aaeb93d8a0a2aff384203801828a2609f6858dea0aa3249148f0818a865160fcf94a9dffbe4be7d2790f8af5a25c158b2a4a2b4ee970eafa2e2eae22542c2e1d29acfc7b00465d55b0090b2c6e5fd2c34b670737970ec75c50ae9c0be7d2b96a2e25bcb8359ca2d78a753541c15862e693b9742e9d4ba7f4a34dd9f8c658b229d5b45a1fa7ba69cb51dff685c1f6c728ea059bfa5b5f8bb6bc60dae254df994bc110e332e2f673485b1cc3032bc391f6c8ca7951ec58af67ad17859569b5ba25058387735bb3e913937c3805805b23bf3406cbd7cb0183cfedf90518c008801620b0e83fd218d938c829ac65b48d76fb2db43fbebd6c951cb500814f340b165b74fa4477da8aa3684fcff5b7386ef2b73b2dd22cf864b3b03d6e729c5b3febb9360b3e8571fdbd0cd2b4d3270b068bfefeb6c751f6678482eba3125cff0be77acff57781c27a386214ccaebdb14deccdb514b4411ce38f81560c8ef1bfc0d1126bc54dfe9f0cecd8a263ad5cff161d47b5e0b4e45c6fd161550b4e8bde02e54261c7169ceb524ab9d5708cd339e9dc566e726bd3a2bfbbc4d956d75f0372140d7188614736239b161f1193255c86aee4d43c430fee0c99a594fe5314e07657f0368f585e1836a59c94c745ac955cad82b418c2965c1886611708e4ea1f2b96854272ac480c1602119841384529f884a397bb1f8c85eb1bc6554eb536a71686d9f1d8c43e35cf6fb1a3b0fee3df90ab59a55c61b75fd4333bb9aa4584455accc2604706d29f0c9be4d7ba855fc67161b0f4c9f6137cc285353fc32a5eb9cefddcc9d56ac5e29c68268d4dfd208be0f52bb2993e7a442a8a34ec99604f0b200708104778aa4459dd917f7440e59ec091c7b83dbdaf7f8fbefe03f3e24e2f8a6b0c2a5248bdf266d5bdba6f218d1295d6f508dd2a598640b86985282b1546d327f9bde32252e521bd0d3441556d7c824b013f60c149013f78018b2212572fab20b300aec732f7ccab7b75edb9d35ce3989b262c27b2f6d685ee45516764453f378e1a85a21f1b8a5e6e7e382e2c8cbfbc303c5cd7c3d16511fdbcdc38e1862adcfe1b7e3815623f9d1819e8ca8edcce0e47b1cc6c9e524a4eff40ea90dd53ba1c7d927dc2b89f5fe75befd3dc27adbe355825d76ddf7d08bca410daf5d968abe8b9931ba8e44a51d77d05c722b7fb1d1e02bb906b91f9e21639f00617de685114bb6fb4d8bffdfc19d98f1b287e8b93eff8cde3ba535e8def08b6ec7870329b727554582ac04796d58a85cf030719763e5feca2d281b6edf2ae877e1c6268bfd58e87d65757a7e0147729e5f7e02b4398eb8508f9afc7b87bc44a56721d24c2dd4e4a198b5caa712d4a2d1cfdb1907d03e7dcc229ddfdb39f19a8bd509917b89ed5daf9a0e48ee0756f4bc6c69eb5efd9f74aa5d2676dc98eacb5d6b39e673d6badb5d696bcb7de5bfb9ef7b6e495acb5d67bffcd82f53d505eaeb55697b7a5f7feea7e944a2fbb0694defb165a6ab1a0bcd65a6b7fe3acb5d57ab664378e23913c12f83d7756f34a5eb67d090c026e2291b5f54bde5b6b7774add67adedb92f73bba7a5ff2acb5d6daf78e8795e9ce725cc9abed795f72992df37aca712e588b170a6147fba51f69d00ffbde6fb6e4f108bd07765f0243bfc912c82d8ade039feb93e8ad7d2659900773e0ac9b9655eca2d363820e937be3320f1540f7e7f2e54bc170656bf1b1d2a7e50876fcae3b4af2b449be53b9b2050956086eb1b90b1e38bab0558eb6a58abdfa24efd7a7c9bd068e44c44b430dcbb6f7dfb6fa98765d1bcdb4fab9edbf40212eedb10270aabebf013895bdff9681438834ad63c0fcebfa393fab1324425eca9f753cba2f2a998660fae75445e99f204cb30ff6c48dacd5da50e60c769459ad988b4c1a8ef64a49ed3702997f1a45299d379b913944037df2f79f4f04883e5105344af493bb1225573a344ad6b849144a9bf923cda520c65d0f7c3b64c96d604b0cc3be075f2c4492a3e43638fe6d3af6771f0295dc1adabedbb6dfe1dc36a557d17e6c92ddd535f69dbdfff598ccd95a63b281e1ca8531d6d8754dbfe6c48105dbdd2f39555f88f90e0e21a2d47f8b1254b95e7f60434c9008796788a30a56b65a740e6bc98ed1d014ece4f26df5495edf02aa257308651772c751b3257548cea1dffb12e1b6bbbbdce19a05a32687fc016147f2f489bedb16a5d7d90b1cb97b85dca2b6ddfdd8970b2d9139f873e128824ba98e948bfdfc56ddac281c3f9e255db8e40b4d298a55d49f4aca05f6ef50ee00c18eb2255b21e50e19913b5db4285f64d93d3426b9d94e6516c9225964c743ca2ebc507bc9d2bade7b5e9225d74b62733d123844924b7a0f24b1905e92dc07ed820a1fef49de9392a07249cf3265bbb80b75d95cbfe181748c4e0b1b468b4e7affbcd1f5247ba4a75db4e8f40a6de1b88215e288bcb3e6715cc10a41fa23f27a8fc30a76fcb964d6b488c30ab6db6968aec6b9e2c10b3f25d77b2ffc8860b1200ca845af1d0348ef3dd6fd20bde73d89f41e0b8be7fd0d1f3dd6fb9106015d1a447aed49a0bccc281af4ccb58ab48e08b6e5bd9758c7009627fdd5fd60795212502ee9595e760d607952890211216f29f40184f961baa427853e3a6e763de8cffffa14c62522c3b89e3704eb7543b0e35f7fbe1ec8f48b169f8ac131419d16769c2bd9b2cc43c5cee53bfbe4bdb5e37b41bf6d9ce7799ef7ddf1f0de82635ffbf40b52d8a3dfd12e3fc1d173e7fd8ea6634399ab8984680a9d7ba42fe9b7a7419cea2ab7fbb1a10469d1ad17c4aa104b388aa4707c8d84a30b96f42c3f577d6a79af0fd402540a5b8a6049a110de939e251cbd67f991065def49200f2ca49085bbd306b10fed22a341a3778dcc39557e4bd26b62a30f5e904df34d3e02b873ce7f8e994bf8d09f1e96e869916bb57127367e2a57fa60cae50d6c7c63ac39d7e79c5306714afedcc46054b3a4cffcd94bdcd94a1871e79ce1165e356ef2cbe65b3f61fb15ce1d27382d3a65718c5f35d77f8633a7561f3bce9ceb3347a74fb2e6fc8c31c6175f74d105152a524a9652b2949225675184b4f6fb1e0be6918878b7f9cccc5232cff9ecd287d75a6baddd332e3b76641f22407c6494eddbb8996516b071f3e86871e304a01cc336b9238d19d20c96797cb0e4d6262c5fbed11e18d2811b9e1d1c16076ca4ccb09acd8bebb2e7fa3242ac3cd7d1e72e4a29e52e2a1289bc1012cd16c1221c66c572428068b188e74b650884900844c18edc6227442003f9d61b2cd69719bf4a5ff607d1715b588110eb7338ce5b694824081a9f4d486a5a587550f001481020b0bf7efbba6ddb5635add64cc32efa7ddff7f9d0557c8cc102f1d1021fa80f2af8f0390a08077d3d5fcfd7f3f91843cfeab2829458e57cc8b41d0af051bbb6edafdf4686d99179afcfb8b71eb76ddd17388e9340dc6c91878040b40885ad4c3a28d820fa74c9d6009122a164c23692051cd3df3dc146b240b2c0ce1a517c2b62606547e0c1942bac5c11d4d2ddb9ac12e1ac0e42e7ebbafc19e33b29db95a58e4da1be94dd103ce87bb6ae6c145e0da4155bcf68d9ddbfcfda79fd65669ed36bc58270136a0ea149643b193d3636407de29e1554b9fe57ab4fdcc3822faefff505c69a2f67f843f7fdddb7fc504843a3a4c80e90213b7e7ed82788fd5018a4d4f2a557a21a19a8c1fed0ff20a528948f0466f6b36a6445a11795429a28414a25191a1a6ef9d0cf0fbd8b7eb4f267e87fb02d5f7a24a3b7ff430949cb979e55dccd1108334ba0f71624bdcb2b51b1c89c6ff512d248964d0e6b256d5c3c12cb4ada7cac97877925aa19cf2ad28f3247028dbee569a20429fda864df25a4913f3f0ae5289436a4913b3ea39d914f8bb4305482300ee8be1f86863f74f51be687ec617ea200258c31b0481f2508cb73cf71df7d10eebb6e16f7a466d9703ff68ab361b9bcaf5858bc1f924b48e3f25d1096ef421a97b08142ce0b4d2055a41066c6cb377afe198530746ca09667550bcb7baf4445fad1572fff2d342edf852f21cdcb775d48f312b68439a109a4cae56a95c6ce298530f495a878c786303404828412d515e426a7e105f65b3e4a90d277300ee8de7e90ee6d4813a5cb09d27d4b29f4401278b1743fba8dfd8e85c486304144a1ef42981f441f7a514813050648f70f605517fe7005bd800b329041cf6aa5f29d9a56d94fd27de8458f230c96553e3e3b5352d0032420e1ca6a8524c98a0b6168f863ae260c0c901f6bff890dfde7b91fdd070148eca8a6ca6ea10a55584ac8b95a3673250a61b8178530d8abc66973fd71846159e5406ef2ee43df7d2884c1422057cb4dfe17d0e840d7bfe7ea71d4d5baae9eebdd8f970ecc0ca74e8b2eb0e364b166563515ab3aa0284df801106c3016ed0f0a229f7185b226f4053bcad67cff16417f79d55c17f663832365e4fcd92f7aaa39fc79e4e69ddf63de296a546c8e46cd50f98ea795a8d42d1665492c158d040000008314000028100a07442291683c284f84693d14800b809c4a765a9c09d32887819442c610438c2200000000406608940400c098a877360ec60a339d1151f91f721bb2615dfaa0679e6187c4ee3c9c7257315c352a47cd775bc2553a006fe50aff73fbba59e8f90c995251f6d262f78e46ec275928da5c4f8473378a8ab5e9982455fefa73ad310541d872c86c5916e64d26df5e44e96d193f98c6c541650851fdb61f99304d3023e272e040feaa1e03c5af589d0ac4582778eac66f2d45cc30bd83754c9670c7b12895f5c6cead7956867075ae50d24acf671323304684f773311152f00ff0bca23ba155ec57dc7483a214b71ca2f1aa00a1aca239cfa1a949cc27c776004a87566d16d22a83549cee53aefeab583c14b1dde6fa43d2eaf14ad410e7cc3e5bf4844ee2f947ac6400b8adbb41a6f7258322d429093e75a6d4c0362d4b6b4c12f73b6472f62fb5bb6c530710eb826b60b397df60210c35cb5ad6f80c98c629c396ff4c654364a22e304de2a61716d9cfcab4e439585162b1c9e69c1dbb43a0ed8cfff1ad72a07aae2adc7a5ed7295073148e2cddc52c5595353491fea949046ec0bffef91686ee38384c01468ec563e9edfba7b150dbe4762ca7bc3486af3e800175ac9eb2cfd9adfbd7e1e68632dc1e3e981b0a3b49db06c68f0eadd5d067a008f845c71263d6ee678ae6d43ac7f5346d7b4c465b2bf2c0da4d1c44994dbeed595517bf7f197e7e199063adde96ab55ee97e1ddf8ed5310dc92185fdc5aa844d1e824873f25daadaeadb7656ed9835deaebe43d328ff43e2638e5c757878eb2605aa13297b6b0681d30c182ec016272f6a7669e3219b272523f2874004b27d0b31f325f88df87c4f54a47e9960fb78633138100148b2ff447d4c50f05f5fc5a3dc42a3c9d8623690d247ee4585e64ed1a356949dd0516603f7c0c9388c27fca43d48915ab1ef215f52206ed5535ed9e9d12ce6fb82dafc01870f4b1da60ce7b9eb4347e0bfab067fc047940da2f8bfc85dbbc48eee716a2065445f3fb37eb2f9e755669b1d8371b6c0e2989c9afbb04a66fbe680580dd2a9e2d17275cda44fbcbf8806a54e01dd58649d4781f4e18514811ca128554afd7cc28bdec4accebfb90f7400caaa298c0fb1f444f844bdccc11c6d7f57ec0c20403e2c3227e151d6cf277a97ee6e0aeac6ed4da3547e186820f8575606c8944f1bbaae45478482d708b73e7bb0d0b31431d37982893e924bcc9ac51257c41dcbbf5ae04985e0094831551996cfb437dbdb6efd605521296de501051f55d527ef2eae0be5a7aa5ffa516e13df16f28b40bdfa977e854a822bd62b691eb66de8835a84c6e40ee5661e4ca239dcb99b6ae655ac9500d0ac981bc46a1764e2dc608d51f742894d7e9a468bf4be9f364e9aec59c4c7d7b1f9eb025e9f474d041f7ef656cae648929c49025609fa42cf5116dce4a614e33d7455db322784110aa8cd375d4dce92c4be9c77f1c77d2adcee84e3dae653baaba17aa6e7237509c3dc91677321ee67a3d804bd2d574bb4a8c6be051a728ea7c0113d096940afecc673f0357c24a983e64c5d9b0bc4ccc0cdfd8a9f77ad770e959082e2a084fa89919a65d7ad4ab8fa833ef2595c02cc64d3719cddd9e4c3c742c0c5650e9f82f6f4d4f5a12c77bad8098e0b6e073a776b8da44a5ddd9aff688c896a0085505b2813648b1fb737943bb3998ce69e88bc7a11a159e194f05db83510a7cf54deeea4e8966f966302e28f9fba77bc6420c6b0eb0a5f15537251373de466061456ebba0d4b7320694f436ba5ff141e0f195c66ecaa761ff80091dbb64c2128428ca6b9d74ba824f5edbcd2beee308c39a06efe9f40ea325a80be9763b667e74dee42c156d3c93590a132df310e1a4363ac8d6e439ad3eaf916d6e7ff368c07359e9c1d89aa9d6368a77e144bdf0786878f1c404ad132bf18cad66a6cef9ec01fd6d6d4073de3a86d2b8e205311c272fdbc1d23959d39853c3b3cc1adf9e0e9f492bc320c14249fab29d30f6c536930b25172b8eeb03e15268795ee8ddf6d19b45117ba7c8e9ff05b261104203b8d68c2665c4d5e49380498a9cf87033809d4801af77574d52af07f53f5e419d83a5c314905a1da38158c6a4e171063fc9b3dc7f0160983afae5ca48ee23b6a179b351c13a721801b85a7c4144b26a46cf6a4666c32be3cb5daac2b818bb072962bb94dd1d868acca41b53dbeeca296bbdaee36bb14bb501bb2cf1260936a1065ef048aaa5c72a79000c4938a42d3d00536f7029459f12e95e447cda0f4562ca6eeb549f06674573d56bf5b4c2ca5610b3553c01a0b9d20309a1e692995fe596dc73b0332eb7f31f464934cf4531edf1194edcb8d887b46f634aaf35cfff8fab41f181fa8362a50f509eabbb3f656c6eebed68f66550e75152a88fb6f1ded03e4a758fdfbc766bc92e478451d9786a812cfbc580fc9588e2eccd25d0efd2509ce9c777661f87d9f7837c88f263038124ecc2fd2b89e04723636696a4f9cede8dc38c63b8073e9e44a033cf016a98faa9c3a3cf8de3096d153d924406952da13c129f2e62ad52f9d15cac8596b6879b2a4b096f03f65c11bf85c7bb7bf82c4486e3f89be0f719ab14ad15695f6b9aa30d66e9e773815e414e458dab2f3b834568335964fd7081ae077ce9173b09380a5f4782888295b16fea45342accbb84fd773144a10696b35c463a2962df68bf3e23918ac8339b910b8aef7e4e80d9163d33f4694e72785068956aa7c3805db5618d0d98a94b9227bf4ec53c3b491ad07a5d970fb8fe831b1077d8b32ff4e43307da2ed6a177f0799968f887c0ee984d2b4722d16346fa4556d6bc0035f43015cedf87997aeffadea2761ee51a175aa535b40f7034af0219a9c669dffb3b06c42ce32d06e28634e51abd5040e9f9d3f7cdbb789f23faa36487e43ae3d99a5b70424cd198b36025a5c7ea64fc90028ae3326c9b3d925c9c2e6213b7ff2646843b8d86195969014b167d0081030bd3049ad4147700132e0c2ee48dfb06624584bdf6919f05e507e426608a4f3fe2ca61f8f98e1d4c1f7c8fc3ee3b280058784326c66503697a3d03b41203f943da4b993a1ef168c80fc5a6aac55e4e8717a92420a5efd2d0dcb65a258bbca3d598837822af4c3c0a7f9cce0efde7e0996978482f8c37e5e83bfd660b8f71d4f68599a5b6c5057a4d10c34e8852b696c0583521e657ecc91a091458e231321fd162225e8149a24b31aa12f3e773c8304e7de62d429ca0b196e04c2f7a2b68fa768822e55544c12b87e2fab83b9c88a609ad787a3cb7a289d54477b314e707250788bfde7ed950026947feb91c1c695086e104a5e4ef30c92020287ee0623409e16c904b23bba8ac4743a9c3707d9c3e9832e2f8bf1c67447f7ba7d53bdbe992359e9b13f5804310fd4c4518f660d81b7e96d549663ce283b5c799ae6cf36a95fa68160c471836ea3305e9cb20789f803d9d5e7d61b8c534a91a27ec886b843b763ed03752ac9de7e0b965ed6b3b3162c9d448daf20700a54fceb437392e8e86befc50a5dde19cf5c680e182810a1aa27144358baddd09392da2a214b39bd5b31535f7a1056316885a9dff3a5ea8141d66d9e2b8b7cc8359918ac81d94d2e83feb5aa820abfa214459174f43963e4d2ad281cf5e9252777df8f1433960609c45231adc526c94c9691d99c45283b6450e5a8cf36019edecaca335e5296cca6214fe73613d2fae809cec90cc1291e5c0f18b5fd3ca69d29a81cacf8c0934d9dd5a8ebb922fe3f22a874c693125eebb7da3f22daf9ffb4d611aa21c6c41f98cf313c97d3eb7801f2fa2517d4224600855d2e993945fb919891c95588d4f02ea9583f85dd7478c4878e2925d63dbaa726298877d1c7fc446aa5f8472b1e2b2bbb6ae781037f7c6891a41b7e0bcebdeb04125d5b774e673dffbbb98015edd8e60d14123517084b39b2903ff8512cc6656304bb8e9a3b173526505f7ef1b28e4b1073ca2f7e06535f94ab79103540719574c833938cc7cd9cd7a34151e1a231ce1d0d9604b5ba79a979513f9b9d54a9eb04912d65a36b69b7ef669c3e21ce759d2b072cd1cd709bcd1f9747016e47b09c0f2c62751f6b62e3155e286e4ff316b1e985aa8043309876b5a04da432b308100b79bc4da6d8272ac78bcdf8807543c595d23c17089383b34ba2c81e3ff8302d074c812f712ece4bb07377d0e859e08a3b7a3d1f777f1988e0c7ce5d6f2f323cb18c9830e26aa0be70984f5a76e340bce3b8ee307d27363bd5d8e8e3d0f678b42a4d1cefec472c4e7bee41519b28e0585c17775111fca645a7587f5e3c23e841cdf35df528f9aea17fb2e148743c3101c5e588b0bf1c423f4b9d07162e875b969569aa861b5c0d3209866cccabc5cc068d5bd4c0e040ba59f7c2d28aca34a4d4eeae766af19a7459b0c07759cf0f138d9a49000077bcfd0b6beb3c19d809bd4081cac203b17239d5f5596b70e547fdff94323a9c8a4e15362bbe6d4192eb6fa55212db04ec276c57601d8c87804ea163c4809b0591b4cde41d0d639d23f91454fb70a0f81702b894454ad6ab77211ff6a6cb88333b6efbeb2d38cb3434ce31abe297e6c8fddcac14fe4ae252e11a33d2b372c62728e8472abd20dacb7df4503884ca615d1850fba427b43cc58335f9d593fe91e4d57ae25abf94d0d8f69ece7063f7e677d0830a8215ddcfe3d26312b1a2bcb0df36e9f997c81f4a333699fb19b756f6e99d3f80aea1c34cb548bab83b191401497a0a41b996bb158d4c71d9051e4095570b6b89e8187e5ad0fb9257d16674c3fb14d898bc5341956033afeea69863e747150d3913a2ad6371c496b6bc9e824c367c295a6fb19471586822c100d593cd17325a507dd747b9674b93b20b6346cee9b06b6a70876444eb783929051c8d8b2ebbe1297a63f8119dc84c05533659669e081d9df8d613070cce6d17293368a6a73637e4e5d00eafe855ac6928f1c7f02ba466744e7de6af0f6e9f0db30cb6e221d724f29c666df84a7f73ea09c074debbc42bd965b743181c768f90b8b3f4e3c1dd5b030d385b391f8269d4b082ff397e5fc1b5974ab1b5cbddab1c892fd48d371618f6a5babad66d7b7e613aea25486a016c848a14ef53e70316225e596ef47d03ee153726e5e8d2d765ac0bbe8cf6518fe9abb4b0957727499d6aec32b8399f31359cdeb74cddec18db60fec3417de6bedc2af2131408440e314f24f5d87e37cf92bdf4d5bb3dfe91889803a3a83000188230074e465d54808fc906edba3ae0f4170a15a77a0e5b7a04f068f4a639e090f9d498952b39cc8badacfec5534acd618799e33239e885306eac5692cc5908433920dd9bdd11abd492ee63de5750afb70bf013bd9ed729cce1d1fab5e05681608bd27888364828e74a9e37ee9da30a9563f5cdd05e8f3c573531e3b4d68cee0a1e2330a80269367340725cdc507965ae96601eb3c2f3de5678ba7dc793903ac078c5ffc959ee946a93e04be7c5e2c916c3d6adf8934eb8f6903ef292b35dc95282530645f8edd8c74bec0164422151dc9156acf991165a46771810e944832f2c0ae96d5b98b0bc3361232d84c30107e2b493366c012f5e28abde6de74c943bd63b2de4d72e7d84531d6d6e107e09f2b1c3dc79cbb69f400e6c9fe737f94eb42510266e20b700388fcd9eb199bf6c08ee86d123368ee12e0c18d3cff70e56ee7d1c877409a014abd31f905c96c4bc48883e72fe5562e7c471e7b0fe4f14851ae016e1b1b44858ab5b9090d6ce6635f9347178127aaf93fb036a8f75794db365bcf86c423ffd2433597080063682d263e35f1027e8f0faa2dc6c36caa8fb88486112bfa6ef26778565beca66abdc0706894dc694613990e2e7272c3390bd839f0daa0dc2e1015168176f9df11d6add820285197b32a574ae46b2af5099a3637d704fd68fb867f6fcf7657e32f5dd1a38acc7348a63e3f10f881b411f5552ca060ab193c27bb4f411b95e5249b1ed340a7effae51066bd8d499c49ee66cf626b94cf419de1025fe07e4ab2fd85248c4a4da95847ef08991d3b9a4e4813cbf1597a027358f531a5cc3b47d1c3a70a2ba045ea01c03f87f666f10e716c2c53c7ef96e321fece1417a80337b6218f366333d033e9433254ee853e4c31bfc167b5787e55204535f808c4743adf363e0c0be0d2a8fcd0dd98ef37c1241c90d014755ce92caf46d2b080da6ebd384904821d203dc8607926eb15025f6b75f827e591f82a34ae0fcea90a729b0a2901608cf1a63556f72ed1d8f43da20b644e44ca883f490b0b6ef9dc0199dce50948f791f090ea2ae46f85a6d15536dcf8a6bb2ebf465af923501a0817e8ca932c1bfaef0598f0d969da6f7a64091e39d5fd9de192e1cb63d5780c2d6685b713db0772a65d1c533bf1030c1fa3fda702f5e910d93e19e419f34daf27a42108f4f384a3b9db93a7d1037df0bbe4001782264c2836b98c01b40da21ce96a8fe67233938318286e8dbb72ec1f17e224822680b4b4ab0e60c7d8c724ed44c412db8a8816b4237089c946a3101db31604ed8631fce3c7f4c2611d748a32c174e94e9bbfe01891361ddd9323ac229eae06093e1098feb82a6269ba42448ead9d65aaebf36874aa09a90bc6b16fa66a8b2a0242346b6761862c526e35b9cb3bb10240c506b7528f232151dcbdad66b61d9e976279bd942d901da280f34caf69bbba12c90e7eaa466c2d1a3210ea4c0ab558f65405acd88366d48ea860d68a0e90a010b3f62c0df576b191923273ed4d882aefd0c32bcfb4b7b1fc038696f2d10da548921a767b240397f902f41ebbda963fbb0e4a9dea00d72a3bf1b944de7ab0350a45793ac01bd854891c859c26acc1a24a5490fc6bcfd0752ce03aff815f2367ad4299d44dbbd7f4b339bd2bbce36b626f0afbda73680407255b8a1dcfb6903d6a2a2b492ea3f3e06052cc81ecbe821ae14a9008916eebb14cc39f4248133d643cfaa16186426d74f172cd206017e25df335219c6c08866c63733785577857f38d9503f9320a30027b16a1b604a7a8f0d9e1169b4b957f70a7461cd12e0d59764a98c1ac704df24cc4da1c1eab0d09386d03fe1e6db440597dafd367e4182d65a11952c4928b9823cc0c284cabd0c1d091f4283906ac52c73b96cdb96d5175a59dca08b904c1066e58651c76063d2690be934487c2bd7010425d6cd035904889becaa31c0c33f4141986fee8dce2bd8c759e55a470c739b610b996a84c0d2328f77edc1b00b22b4cc3b05c2503621cf252e160035e80d783896d97f4ab13cdefee2a0e2bc4eb2d2a3d0d634371a2a028580b5de7c3caa055b99568531c0ea0bd4da0e0b8e2010e3099ef9e6089cd81c29d016848ed1f04f428a509f9767e54415f067653f3a613c2128c2199fe163f38aaa17f0bc7a08326fce7976ecc358e13c7613afb8c6e8621e7370538b2d82773231d823e3daa530abdd500897228f536ae04d3eb2162532c01823c9bfa782f86be1b04cf97b4f906816b980dc3a288605235cad0bd232ac18fe87cfb98af128a8ba4160b2f9635f483cf00f04bad9c663a9860f88dcaec752b8197b30dd410c4f9179f5eec4c698ca9a6f293aa296c00bd984119b76a56063b2be5bde658b030b94dd7eb8a1873daedafb978eab83787ad6acd68d0074a1a3157c699e8d9862e2a9134746d4db8a113d9d8406bba9fe36a3d6974921336e5cc00e01aa1f96e46f682716fafdd3e1a8d763351d7abfee783ae28f8c4e89ce75701a6370a6e150946eaa0464b1d8a79d836def8110d4e35f3f4e73b2bc9c1d785696db0da9933d4726cecc860ec6fae0382973d6d895fa28acbf34e0bd38f95d773e0342554af930e9e181f72d6a7a1a25265e2e469f5fa15e8c21b318999e23c427ea30b2842c09a0a8f5e37e1ad79bd3d7bd9f74530e6d5a46902cc7e8c722595c56b899b584cefc39a4f404cea7ad60c602ba5d6608dfbd3049afa77a83e481e50e7e97a33c919745dc004ceba068a4f8a2094115c2b375d75ca35a21805e8e773a4f3ce6b4066996a5bf88c5c5189f6dee7eb5556d35c48d41d2ca889fff830ad00296618045d1101ba4acb5f293d62b69e1db474eacf7d3231fc56134898a4f056d33a7558ddd70afca1155902ba65f53a8e63e72c892dcfda2ff793fcde7c971b26e3b12609f12c2fe7e01ac8b7bb455390cde091b8308a3358379a3c522d97232fc5a4c9f0eba54a40135643a6fd04c3cc22d05204515ce7e3d5d541105e12e967b1fd2c251b1def95b7a33f21dc1d0c16d8f320023fed47728e467ba694e49c508f2c655bd7e7d84139ff0e9ad0a4d55729e72263ab9f405bef38ac773d1d7dbb6bd5acbc5ad6c3bb3116466b73362327cea4987ae610b7923eadd7fb523bb4fcb60e97eb700ec5523e9c0b97c896100d9669d01880d8d90c11313f91bc66b6317aad62cab4cc068c7ed79441dbda49b5b3f4e531b2ad03bf2364aa807d7c30fae361e023e495bb9e7a04a45769f2dc2bbfb3fc0a642d3f5da55ca426c1856b21135358e172ef2bbdd37b7f55bb60dbb52d28b6559b07f1ee3e13188d4d01e70a6ddd81f892b39976cbe9402d32e4ad17547baee4fd0a42ccf894a5391f4d34162d122320381133353e93ff57f75b77be15b07419a891836f8e0db958096b7a5c17e76900c277761216255cac72418d0e7cb23ba23044ac8c3664045726f3e9c69d77fcfcc661992e0b147c2a4263ffeff66bd86a97f9adca62d1c9856496143ae505ec03636c8db476d6219eaf822658a219a97378fa4151060644925914078ab37820756c0bbb92e93d9f1111dd9889f025152f668e2a78f8b67cf5faf076d75d13e77866606afbb73784326435bc2a8d62e954b906060c28d47d98026962385c54ca4a2f0434dc059427c82a40cc6b2562500ab0e7b06c40e17191ed2cce5484ed9f916612fc208b7157d231de54a4a3ed8b5c1b955ab9fe65575ba1e28314eb8ceeeaec2513f1f006a283865692229aa5610f5ba40109f1ba36b411a4e26c0dd20ac82eb3816f9a6ce6048e6d4b6ed9c88b83127b884979950f27efe9a607de2be1653f63a6023be17582e648ad15a5fe40be8cbb13689ec6e0326fe1a9c435d8ff9a10cb3c5affe3685bf4d5841d4f6b60b808d3e1f27bfc0d71060c69320bc720709e4a86942c2dd8044dbe08b07c2b1b69fa9746009b2d86c5c06bc1a00e5586c099ee9266155f4d0b0483c31718712d0517e1d0ce13da25985f18a8255d613b830068473c5b611f61cd00c1288f81c130dd92dc8906c6facd86db2a2ecb5e5281ab3986015a97a72d303a2cc8c12b30c037d5c85e63958ede4708cdb49c29664498881aa41289b2a899a86da8d12b13a984920502e49f085598657720e30bff45b1de51003a7767c8e44500eb6757780d36d88f99c92b2df037e80dda7671c34c35642aee1766371333a8d51ffc2daf53333892bb01062bb2dcc3906b6170af8770a1ff54e9e8e214d72acf0ceee2077f6cb2fd5b661fcab1580286ccd40cb0b44ddf52f9a2d8f8faee27285996f21da9af64df7565f216d8400173b898a7102b23471cc2d82b81a501cb00b24294eb767319f1843ea32144e27bbbc3b1f5f6f42510731036680247e79dbc53f04c00dd436fb354ed38434eee417fef45cadbbc09f5ca4d35d9595932293bd8d71287951d400f70b51b1561aa9f9e84faf930ab9402a4e2065d2dc47fd4cfdcdd30842a9870414f663bd468d89b59b2ec83f93419befa4d40d1f54358ae6d74ab17a17f227292981247c29ae706abfc4a18a41a380d52a1c796c361b3bbd375c38b3276c1d94455ef89391f4e6365e05e1c31767fc57fc0941e826706da89cfdf2fac88dc48b4734e7c8499e94374a950d7e11ad6e65fc046689457f8479955fd4e8a8ac56bef5e601a7bc7be09a7d3c802e8eb13ae074b7d2fea657b0be452612dfb9697f512a4012a1a4d411f3b25f922fcdb6e805216be65fd231106bc1c005134c093b0fa5da67169b6188483600ec83d8943b18315083b291728ae28063f0b1dd1edcba7cb0ca395d5a6a81da46dc6889d4980641562dc072639e5d70a2c34999e28a7f9f3ed133e8fb41e9cf77a1000213716880abe7f7fbc61514e88a2c72b954244b8da99164362517d7bcb2cb4fe94d7fe21c4c863318b083d78ae2803125b1d98be246413b2c3fecd8d61fd387ab0703bfe48a3d12e2f654909d47faacf7dd85f88a9ee2b65e1db1ab98e4ad9bbffca0e36930c4984d27f639fe07c37ea0e472e96b247e297b45f78895ee11355f17314e5e4834217f1f5857e4dd04ef6829b3d8c452cd32907f88f64b79f411d8174578b9719c8949c071e7f037d13139b0219f45db54a30faaf4c46e661f4d5b04548979fe3d2279f4fcbb6afa1249d059cade88882518f4519c63d952ff77b519887dbe2825f5d088809859d21064b8fc9a7de479740a911d5c9b00f9972fd98a8d1a61e434026f5b3742e7a1514032d1451f63388d03699429fa33680c60d50c07dc31b2965367863516ed49e3e49e85f3b3c25c0c25160c89b27127160d85a2104061d70199a7498fbe1462c4126eb0d345d080ba83dd0fd8d95078551bc52cf4b976cafe8aed624968dd092c424f19655635362d46fa1219db4d24311e8ccbf32b4b5f854035ad1ba8236ecc0f2117df45034b24061469a54970a783fd5c935e24d108c57377a0a6f1716c0c3644c02956631549daf82d013edf4d542f47397ffa2f9a46c0e4e5d79a23c780038e405f041cf2a567d6d34f1b3061145e39b94fa5983b08deb12903450c21b8a641e30da76e4c6fb3e66215200a3ebc8a483029681124e253f435d7169133c48faea8f6c2f99ec309ce2a77a986b87e5642ba3e89a7cb290360975a74f0257c87130c4eea8037b6e6187be052990c43ef1c456687877618c1811629451282eef80937af44da7da24ba72e8ba13a4751600d6b74f0bfb54152aa9b180d24f7d8390b59b041749a1205593e1b1bca2fcc9766e611e62a532fee017ffb039e4f0c2bdfa0621a1eac35ffe49f02b93904cd99ca1eee79a9c050a74a00e6109a571ef8c19f08127ceae7a88b0077d310ea253cc7d36dafd25dbe76daffa0af6f44b0f09ce28d4a6a82d5b327fd5e7fdcfe35498e4894824fe430c858c8fedf8e99143e63c91609c81809a665f1e9b1f258d6916f99198386ff88b43b41fc7f2311ad0bf39f8691cfadb8df8549d09a49857d687dfe68c4a180b8b3ac698280b560a8a2b60eb2e838b65d97a3dc5806265a061d899e89166c65a3419ae33d220609e1dc61b4d545292c951dfcbae3ec1f6c7d06994b7385661f10e9c04da979e55831ae7b358ac6b8633a086e6ee33d9d92dfed2f3a7a20d55cb1fb2ab03ba086573880edb0e7578318f53615a3c47ded9e782aa0507a8fa5d5d3040d6b8779acd5bdbf1c43398c5797479201012570dc7bde21ce9b2e7376dcca3dc3ebfe194e9d5a63452d460e137fdee622425d7c338563221bd79ef43398a5362a10b2c60590a2cb716b63a793bef4ea132b95469cb2bd383f7a6c065c4d3a71e10b049825f168fbb5a4cc1cfb42e4d850c14eb455eb63778c12fe4ddf5df57629592b6ca3ce5b9f4b514356e019f2bec66622d09c1b060417f1789989b046bd9b2e30af0cce5f8aea3293df6706df53de21f0caaa381d9147e838a3eb37e4e8f81171ebcd7dde823ce093dbaf58b11ca96b8f88b0da72b387d4ca6de144e1d5863c4b992c724360cba4f8a699c1a8ba71aac8edf1b55cd7b3fede2ae87374373b63d440609f8e11f367ebc2635f48c93356d4a0948f019ab574687afb216445497133acee77a90b9638095eabbe598efc205a99b33992a9ac64c0f796dd5c448ba13a8a0309048e0eed5c9d08cad887ec9c9ec55b645bed594cc2701f16609fb730deee617c4598de2642147d6fd713b305ad5b81e44225db40848ab547559ebd689caf505f893ae2a582761a20a5d2cc6000022bc39a8a90758a9880c6419e5a2ad25cb3c3e73acccbf887ba4ca3134e63f66f0fd0757f19da65c98982483ddc1471133bd06a86b8a3453b99893698ef5a6a3fad5ebe1abb5145906bfe74b555cd90d7427fa1a501e2e56ec6849351dc9535d497a62457168e1e0b345540436a3bda15865d9a75f9e1639725bb736796fcda15df3246884a5ba6bebf444e5919734dfc366bacaaf390606f283ff7a2b0e13b11f1afb32fe111523bd71e9c673727b6a2ea734d75c834f4d7f7c34c93c605767220e235790611eacee4d0cc7ddb66c3bac4834ce69f0189234d1e15dcfb5360ef61a953e53943ac8d38d5d49ce231cc15e2f9906dd5f2ccbe5f523fbfb848599440f2ad0b4a6776f86722a9bf1b1db1cf890c0eaf5b279360e1204b1053e7c5468ee43a47b36eb1e6b461e908f967f093eba4b701936a1b76cc04e825c5127d57f5a0d1b013ea050b40248c67fcd2293b430c7ad36b42f448fcb27b1124f59cb67b792728eb0d3126de49b62f33b18f1a127052332bae2245b2607b716759a429877997b843709ca9b94706663d896e893924482dc3372325c6a9ab3ae8b5f1153dd9e79b86bea0407b99586b82fc64468877c6525ffca33288457de43033879d2a76c3c8d906067e118216cebe16d52b393d8a28539806b4218b09c4c05af3766e5451c745aa08d4767eef089b75a93736e0cc7c6ed2773265ffd4b5f3f898a580c48f40439001106c357e8f10f77935ce47f248fc1ddd07481886d1814dc9176a01204811f46da3ad4050aa0b1b090f691b36bcac3512a49b441a82c30b43ba0cef2f5cc99f75ee16fee4692fa2eb756d14c647d98bd2c3239270e8b7599855dbb890252002ca74309c2a67c3d8b9fd124f23b38033c5f7cb253fdf378e1bc52e95c7a63cae90926566b766d43d048ffe7cd38b6e5f67163248114d4f742e0dce374030794862fb02319ea6f61563cbf4748e6e2c389ed4a4f88c77dda3cf93f9ff2b047d2a6c36fc68e5dd73b339bc04a16d6e93911030ecf3e79162cf2873f6801fddb1970fc9fb05ff369a5fb992111450c10d079c69d8e7582b0fa08c9c9532062fbd988b1eb796e3b98fe229e00ee65386d6922c90eabeab4f2eacfdc1833eb4ee5adb84bb460de533906265d715ca546c7285979a5efe643f58a5522c0ee002d5fe9b1b0c64818883102af2f1d09c72c71d90bbdccc81c9b81614431f3b0b142c5de1d64848fe8d31308e27f822d076ae35cdb62d108e0daf538a4f7d8c4c21b87a1bfe386089332d2ef82b42bada6a1840d872a40c9cb3427c0a2e33c3daed134cfd74d88b616dcf55a335a8dda4c134d137066105c99856e84f547e07af2ab823f491e95c3c9010d5c87ba23a176696d66910b054148d6d4391e9db4479b35ae90c8c733dcda18071318d20732cfb00cc06226601f5ef66b1000de8d0c76a0441c60e9139763fb1473623983144c382c5062f15e23188af5c643e54f16eaccebad26afca7e3ec1f33c5776f9ddaf510c7a29c21e901e8a620e27443ba2e83b3a4d3965f14b117563e39822883e9dd3eb3d1b07a81e243d5f9d1084da6900a2dad800a74be8eafbbf4c57f70f837050c1233abb64cea87da3a8abaa6ecaa6d32c99edbb59c84359c563dcca6245859b796de59e4072988b97a9dfe8003454e04954d8a03cf5943d7a058ef426a25bebcbd9d10e11754ead34ef970c53b05eb80823dae0f100b4126d4f824e5dc7d61be843a1b9a463f79d8abbe06aac1da6fbb20b632095164184f4cb0aa3070d18611f9dfb1b69f35c4c9db07709eba576e59d55dee8a8fc5e4c2ea4ba93e04b1cef1c071598f3623d578471edc968b9a681d9ee3c76f1d62a1ab09741113588c4dd57d0aa41cfb108f6ed0d3854b957fa23f6d3871dec0d58295456a39bca756fe610b5edab9d52df0f0277e12e72d09f6f73cdce4cc6283a9ea220b589554a2d081ddd357aba7a312bcf49c651bb5dabb25976f9caf3969764ce1c2e2f2c9d541efcc9cf7af6e163496b3c17f5f642ada12801b1d28c55c57379c96cc8d27e372e5a93f05ef895b162813863fdd30aefffd12de30eac45ed3b9f4057f2258b3b8bac98611681f16241a1878b3474884a5f11839e5104cf349576ca3affce13a25032219a8437dec7f18a44e34b22108c20af25c55f3c1cb751d77b28260b8f889c2bc6888383754d9e8e95bf413c9fdc5d38003375b11053496b06aa834f027f0d93463d9c283f590299a6cacdbcd862c73b18cb76012bf908aec0848b734359ce779a253127ed5c0b03defd412c4906e55adce5832cb78686d21db3a6b182b508707073996c16b9970387bd2d8a74c92c7dde788343f758c742e0bc9a6feb4d6d4c17d7976f3d6ce0db35ca2aa323eb0789be68628fba14e6bb7232f4f38706817984c53813680d3ae6638aac61977d53df2d521a273ff21a2dd2efa1273dc23b0ef108e28c492511bc0a75bc643868985c813f6e303feea81fdbed0712c2fae08fc1e596c2f7818252ed12780b5d8701bb84d0842d5c13b5e5cc39cb461234f5eccf90d49d98fdbb41a4ee05030b068192b57a20af30f2c195b2364a36466126c719324847d84dbd630f5ae442e6c6fbdf304fc4ad6acda7f0e7771ab1f8adec76c979b0cd0f8e6bfc6d12e6c83e1693a376f774f127e74b171086095090744da9e9d767862c3cfcba78e02ebc9984eae9b13d328a49affbe1fbc0d9238aea83ef28fd82938a08a3387df9559d1a742fc222d160529472c3c8baf4f20422e43a0566adc9b0741afcfe1fb59e3e6c07a89d0de0a70ed5e544e93dc7a7f6e93a2f424bebc307744636186177d612a2e72bbe8311c6a89a9fcf85c7f2306605a60ea731c6dd45ba96189bd4ccb36a2f1a81aea75c9dc46e7c2c5958f403e85709d2912428319c8259c117e462f1282bab85149af89115b0b4fd070a245b56c7cfd05858fc488630973a5c5e91fdc84e368fc16ae7069e15d60c1e62b13ee15032610919412b0ae4f620a7fd614711b252cbf393dc563d6b35784ac6c1e0762c14ff09851d909c8d312b5f754feed1adc5e396d275ab380a6e7282a49ce5a69ac9dfa92e11ee9dc45b78e3e8ef4ee1ff6319f46d71aa2e4e315a250e2dca7eee493891b3ed1a86c9f0705932f3af38b4bf105cea8ab7dea7bd02493c681267a0fbacb5b5fdcbeef61d95944cfc4c100be647467df6b1945e4782e593de431040b46f0cd807153c4b79138b92ca990a841af2f6ccae8af08ca7fa7440c43700d2dcf38ec07cfa03b7e2c4d26fa7302b2ca998ef6df30eaddab06bead9cc0efc1b6c8c57c28618e3fccdb1c8c035061fae70b276bbd501113b3ec83a36495ea3d5bf6f6ac096aadd7a31059c7d6c8f3654fbd6f1cc7f3b15743be6a0a1db1ac3e2b286931649b6a413589f65bf20c6c471ca86411e11633a40cb790b7df3d60ae23095c73259a1c6b3927faf27f12331970117f356aeb61860fb655a644744de70db96dfd930924d25441f1b1143190dd7c2e649f71237cce4151d599425ed84c33973c7171f0bd3110d7f8515d77650795279b32817cb23ab3514d874726241afef6b55582bd205985762bf0756528ac90d379688b4c5c21fe48836f2ea4bfac51d3f4f72e2b3d3f38b53ab1c57df60c6cca6cb0f5d33f21d78f88af1581b4d2b336bbec5abff1029c62d210ac100de593ced9ce8a9e0e560a9a21213d090b749c532220c3dd8255c2f05b4f5cac63d1d11f5ddb73572fe859b143e7fe81d48b71464317686443c91fea2467419bd4b860cd749240164c522580b6e205b93a2557e0337b06197aac0997d707ce65ec13453993e8792a4247207d9fe8b95834d4bba043df8ed086803e601ade89b16b0479e881483a405cc21c04efa982539a457d5cc582384454b7b9fe311ee7cf7c6ccf62826e2c27b3edbce4b05838a94145014a8f5bfe04b214c88ca76d325c653042f808e520ab71aa56df2305b79eac202704c3bd8f48da054ff240d08fe4373383d7f4e364ec2ffc864bd822472d22a9bd058191a48ae3cf5811479ba321eff9ac974ec36d02959c7e36d10a913c97c71ae59c274322b39dd799f38dd50a145a85be83fff04b0a93f19059d883c09d3d59cccafbad0549015d71e0ea98eb89071ed7ea376e72b38442e139990705255136d67f2e15b6b2ec8eb80f2c847a2bf4c29f61712d4b40c70632c4019e10badf07495bf6ecce1d08bd030754580775d44340aa2b6a35ff9fb7c6c8c96078e3e8b29461de08ddf778892939cbb589cba7d989c98c12f3ce53af9999c644366dbb9ec730e36934e94821f97420f112a9e336f36a350a673264e653f05a6205b9c50ff70081accaa737034f57f7549b96868e61b114ceb9dbe096d9c53636c5c4d600b79d75c30910bcda72fa4cb24626be473767be6bafceb186de9767509528ac8d48931802689cd92d2cc610466f23fa136793096518593c962869c38f9b82911680ab0d2d4628217ea4984f4975a90efbf9e4480b5d74fdb2611a0de28604e3a1632448c039a296e1ef70bb2bb8ad5d835a8148dcc8b71cedeac2107004d3d4ab9db0b30a004ffda01cdaa6563a48388fa40b358eec44875d8b9ad6ac5d2e8d86b4467d3bc7d4278ad77a284f250936e1e0fa7f0f86797c55c00cbca138127411a203f7ca121c48b46ad6455b99396d3c5ec12a49bf6dd7958ff072e00bf01141657af830c46d742b6d73e51d00a35a2f7ec33b8723268d85476e22db4c75ef3922ea74394b7937162246d092f5d9c45e97a83615325e7d3283d6fdc78f2fd44dc0242ac7ac59140eb7cad87aaf82f5f1e7cadd8fe070a1a63a0c8eeb48b3b55d9e38c83823020a23383a41c7a2c28d71772439c3114e7423c302df9c9c0190e1731ebf2e3a69e0eef43599576cd9a5560d4cb606831033b4d3726dbf6fa5eaba82fee0f5adf84d858f5e2202bb4a0215fc0f429aca9e112b1dab3011b7e7537d3b89b5de333ce8c7fc466311c88511aa70dbe3f181ed081c58e42cc019e924dff181fef80af6db421150d8897066d8d02b57cefa025d16f1826eca02ddfb8cf847f543c6b355fe3c980416c8d164b4972905b2844f4d4b328d9c8b7ff17b44df05e214e0c1df515c260ac3898e4e7ed4f21341d089d90ecadd4ff3acc151761a8641d99d13913178782fbbe2919913bc781a500a2f3ebce517e5836b17439e60111bf089c289cf9bc4281746ec9c516eb631f717f1b34fbdf55f18e86412a5cdcfad955f1f136436c34142a187f8440129283d67f2be18bc0f9ca80cf306fb1ac2be6644887bcd7b8fcd23f34dc9414e32851ba570439eec187d9e011dfe098cbbf6a3d6a1289b33699f82b88893f151b26b18046d6857b153c5c41ffdc61052b40210b118db8e970cf32c974f99a98ca48605aca7cd1edc8d78360c0eade6c536d458e145ce0cc14b04732890bc6eccf117b1930f7a2f090e5cfa40e1ca146b5207d13b89b8593337f8d10ebbb9e1542f9e358246d3454d008a92d7b17d998c710ca6580a234f9f352dcba144ebebc9933b44898e9f8342ef648afe2293cf9a52bc46764c0466d5c7618d547db3a5a3a8fb12add96a485de3bb490a142c1335f705fc75703058781939ce8cded80a47399360510f2e2fbd54892ea819263dd400e85fc0bcf1d8780187f00be6f29fe5f1c2f4992945b50a6f176fb8a821a4acb886b9d5b7eaf8eea552751bec2000c8e3a1b1a09882e75358dc0c8e62203c52d5549f72ba69754e6facb4bf1dd6987102fba2f4a1e7604dd04eee3b9d5eda969b024108b049550c69e608649c86e0d7bb3a0dee899d9af5867944695928a809350be3295fbddcc56c19b1b78e1013f3b0d021bd6074417a98c7dd513c639a79c75f349e641d24af0ff09c3e53f6d0bc751544054222aadece8d2dadb24127e5e3941b16646b50f144b500332a38eb7479c824ce8d26b3835aa00c9fb652eadd4f2b1108a40987fcf0ad5b52593282658c624cf457589b029cee78f17194af1490c23c2e772e32ca2c9e812a132a1b471ab0fd22aea39fc39f575aded353923e9388335ad6330d04f71a2d468b1ff9e9c68632d97e4e3637c58291fd10358665977b06dc6579c8e2bc4401a7850d33afbcd551825feea79dbb2a321986b9c58e7bfec629db6a4a58a3c43c4a71bfd4f2987fbde72f4a27f673eaf05c2f419d2bf2d118edc55b67e34841ab09c77aebee9ff3509d9acf9a8eff72eadee1e6cf0e68f18a0ff3d17082e2d120786e75511b137a4d496e663502924602d766d16c3c37d34e8566613e34ff83549103d0e36399b9804ba12d5d2011684ce37af3d4a209871565495cf20a73689615e465d02e23a12b5a917cb587c935b91263a431806f921d8787e13da159cfea74d06b8945a2f9619e6dc0722e08d9d596a17f62b5cfa1524843c33989b1073497c90f89197fbc874778228fa819612c4bab50b9ffb6c2fd7fd81110941cca2dbea75872925ab807524d31d91c9ce4b7b11cc13cdf52297a9a347b7b380c5ef86ae23df9cd5dea37a2c57d7b1fa557673a159de58e36962e1e9696d6e730410489461b19000ba53b07a65e34b4ddceb70e4936c3e555760cc3149f205023cea3a965e7e398ceccbc61bdd0791c5cf23a6a54e20d19bd8ee02268ec7a7aa97174e2f265188ee0598a083239bf483c4e769bef83e631e3db75fddb65109159da094e90d349394c32d75aec2885297db049624164a504cc7076e48ce74ed349e346c6e5e1546aba1aac02aa8f2c7b675e850a829dc6c25175ca633d77a62dbb2ca7558570fe062b7f27169cc7df0a897bfb4f599630c81778c013863b4477b52c3aa9413bb7ef7bf02dea5281c01f191a635b2517b385ff15a379a9c820ccb219e924196ff64073aaad6fa11b9cfa379868775c54566bc40d8b9a5c28cc3c82a658da89d2684607b276b368d3bf72b5e14032f0ee1e1947af21117219311703379a7c0fa5c95be5eb350d56605ce90415365156699c425cb2f2afca9ac98940f656e394b2799418b7adf7a29904b2da26bfa50f7d111e553c8c5a3a510744c1c032d58614021da5a1a26fe837c67617037a0bfef0480e671a70b91cfd9396fc04c92397a84459acb1e671e287c8a6736d3fc53c00501fa2256b6a487d4697b88a11f93824b8974f1e7910ad59fbf88064dd9518be1eb572957a294a4c310909866b42069f8003f79b03024660ebe02342a137facd32d52a0e81c924f6fd2fa5a3c7336dbc2c19feb3e38a382a90f9e2a366610c76864db64ef838138fa59bc6191708b83487c2ca15fab07ee79329d9a5c3a7acd49b2981197fb5c38cc0486a49762b5ff098e5746681b3890f46e9544c29342c246f0d20716159b5c366f1267af8b18560a95fe61aed15218b55a9b3126a918cdae46dd116c645699b89a7cf6995102a72d57b12ccee103d616572dcde604679662444e3082640edfd5c292367c6e61c1037f32b88b0c51240a51f132df15698c3ec26c3ed1b819b4a998b78b54d3e6f75eb5c9911e2aa1f3da0c996cc31c770625b58c45fba9c2940f48667be7af9a72ac595bcc732a0c25c8c9b3fea741dee592b0c3895161baf3bce29dda8b4b7c1af5f981456625ec17f5e4a974e3111e5fff235fdfe8d44055d908ef0bffdbb97f7f514e9ff9251bd88c9bc4f08e0e213e6c035b2c4a9301bbdfa9abfcf044ecbadc5c7292a148144478605feec7cb6329ee25414c85426018e5d79803de39aa3fe7315d2c04f9aebe6c3c32f69ddf406c2c8fd1acf56568762bc0949973bf2c41c4eeb35465b34dd5b09d375d1a6b05793a1a147f28204bb1d28213d545fb33acb3c34c4667d8a81cf9e4e360941d396681ce8c5210314c616b94a0a6035014a00e1ce52c69af5bb82ae2020dd880b24e690a3a7c2ce08ad6565db958a4608a95a21109a54c2985786c202affd1e140b3bb7d6dbd24b6cb3912b8d643f11fcc53572135d09036502a414c51298174e1817268a006ccd74a02d81d118b44013cd98e7af2cf1fcaea9b0e1ef31f989ee05358f5e9b5998d2939524cdfbc63ab9ced220d9b6d2301659d35184e825cedae38139c402408e4673ef66c668004afa14eb33369ede0ccc18f153888ebd8b167fbaf990534be49033142bf1be03db4799a40886acf406fe682fb6e047e3d948f3c89b8fc48d6164aa1108e13b345f9510c8b68b96295135c0dba11c18976ba6e764c881d48c8149d8bb1e51fb0a3c1656b9cd720196ccde00c4857f9ee378b82215688d85c019475f7863b50a3febbcc7f10667dcc7f593b905120090c1b76fad50f9813237b9f38882b51824449cc8d2e2bc0b4aa4f805d31dc62b420e64121ee5f8990c678bbff24f3160f4b64154894eafa214e2878e6468e167e1a816a953a71c2a9eeb0b53281760885f1826ec500c3a5b84a453615cbd9b225ee7b9a3b51be4bfc43fc17d47fa044f58b7e90a9e26e9c945a280ac972ce00dabfb718f2d50d4034d0b87df03d665ec2d9a3ec54d00ca7632e8e6ea88d6212bcd07ed5457211974fd042010ae700348d953289941014e3a046ac3031ff75bb851254f5c44cda346982ae981c75da8176c5c2afe825fa33757429ebec1abe851d3a8ae8313f678019afe831854dd45525121480118bc06fa5e76b29289cdcb57bf6ad6eb09ce2a916579a12593dc9d964c8c1a30f14ffa27be15b4fdc686cb6923c59237fe32271cdbbf1acae07b317678bd369982afd812fc0226cb059a591b0d0e24925c4029870d233630a81f9b1a2fe5b0e105fca1fdda8cf4b137a297c92c4262acbde5323c82332129898f981b7d7580083b9e8bca39c22c56ef56537f9675da5255c4c24061083205c1ba0b6041c8c02227b43164a3235d1ed83d23aa2067c07600c94d66c858d2524356d2ee101670772ac7bdbe40f32bd27e5be0260de9b2e0d549fb0273c31bfa364cfe80456dd33541b223d65a37c69749dec46bcf157b6615c4b7e1318821949d2784a3733eae44e3932ca937487f3c976f0ed89f1456e25870c00313bf1d18d518ee99f6144d8a4aecb18d2199653c42d11d10fa611b0797d773dd7c1155ba9a6808e67093c1721b3b2e23b880100f5d96844b3be8de9270019d096394ec3bc40d4a7312006cfd1e77800888a7f6def898125a4e9cb8a098634a847b0a3038b3523df73f5a9f29e28ecc052ea6159f8aaa41b72a9a35394406262ce10b094b08020186097660a77616e3ed618a3ef7a0c9438b7caa43139284c99201c9650b213635a2f39c0620d45f0f173c8e016c1031f1cf41bc0439af6f12bf23e1b5c6c2ad7be73a570a5c735d727caf37d58a3d478a13388bc06441e99cfc16b37666392edd5ccc911df55c5f15dd5deb75c4ca5de157251304ce00055b24055d05e710eaa9bd61c476ccb9eec149fe7045539ac5f79e3f47ab59ab281d47de19efd78410c2e19a4cb55d22da7604d1b81e125499b0c53d82263b80f91f28bff8346bd897ad9f144de3bed3b58a635d3a242cfb2cfc13dfc958d47a63e67ef0d60d8d5d261a390648ac13674ff2a860d7c86869c80cd3641a5748705dc1169eb01a6a18b8a1960e28cdb9360ff4017b6e02eb27de06aab63ac14e15eaf4c3875bf6060bee5f812c6edb6b08228a37274e1cfd11862dda991e8f20ab91eb786c9700ef4b4ce462683058713017dcb48869dae428fcebc434c1a03a44ddc9e86126a2596a9dbced4bce9dd8c3f775f08c9690ab6a0be60ac964325c5a13294bf9c1349ccb60c05e44fce93aa3b64c617b7b0cd227ed4b613d956522b23694ea7e66604f502bc2ef05b87eff9640070eeb2bc203ea254cba5d6c55e0425e7822ea8d8cf002114b48262b3893a21078d96afd37091e34b1cc7fa1677a20c1233e1520140fa5cc3bb4d6cf82a0b96277a642691575c34ef1c13e25076bd2faf8b210e7d4b2b7cdb455306327214626e6155a2d7a7abc37f0629442db112f8bd779af10427cde6b5c74680a4cda0cf7c92cdce7d08a884efd5432254cd688b2d05474f1168970825c841728d7cf2c33cdc6c7ea10572630e0246396548f3d959ea88b320ae2161afe744a2567309113e7a3a16aae0c118008d158c61bdabe5cc023ffbdb1c267b6c34f0eb13a2fe65c452e91f739b73768b999fe900047ced5bbd2b2127269e851944f1de6d052a108292aa450bff0da3b0e102c7ef792f259aee0202bbbbbec930c91dc826ba6bf4938d02942c47a63ab3a705e67983a4bdc1878b6a10404217cf30a36151f3a9a75d06f55d5fe101c0cdb305823dea48a259501704d6ad60c37e9b409a4d5ac816e08655efb4daf4b185418ff3ec3e2fcad69920574a2de15937f3b35d85c2065a8e50814c53b750976f577e5d41774fb9a0f8bd19fa1e631b90c16a4fc8c3889910c40a149133635623f84579dcac91e72cce2d56196b6d7bd777da7405cb2f6f569121f05226bb823a6ef4ae47c7451dc77c540b85dca09654b8209b7e5db194ad4518d9af62b571a26b3eb4d1f559d9111d8fbe8e51538e011da3771b021d94932edcb679fc7fce2b4fdaaf40fd96bb5f5fcb431b44b53bd822e72cb6be983f51c9f03730d5c92b20dd7a3714033d703f875322435c0a9c039ca9ff8127f2f5199abee8b0569e5833ebfad26347db1f1ce837b58bcd723507e063b18bcbf2f00ff4e811adaa5b35abacd9c2075aba9820d5a03f9582cf06ca729569bd55e2c48f67ca95b657d20c58045e791d1f3ebb4ab41d93b890d140e0530ba65cea4d29cbe8752c432ab734da0f85346694454dbdbf7e10c56695e39a3837e50dd0b3440371c3eef9fbe33d2ab4031f1b2d8149a8973fed5884454be9b4d1136ea1ec83b45b438fbd93ffad7d7c8ed61f649c4acd40954db24794c31cbe9cb6234bfaed428a0155b2fb2c362517be867d5e399a8ad3ce8feda09e8d5768c0e9e09589124afeeca5722fb4266639465b81ce80af28b4b58033432f1fa3c2ffb482460fa61690109cefce2687275a7cfccf3d3b449f1009bef80fd4464e57990d825bab7ec8855a5d278e4567756dc3ea1c0e73b66948ed77c13cf887253575f9963285137cffc75f544cbf21cb8ea633fb6b4cda6cff999ec4a5d12129a1e5c6b7f917c9cdb5dbbabf7481bcd4f6e05aeb0824a80c379c6ebc039487e45657d651d2d59c41f2dcdc160c10910fff9851b34a63fb6f55fd9922d1c9aeeb98fefbd5d144d96d9646f62493d879f25f53a48a89d28f13abf0f4daab1e3386b41ce93f0da146185caf3c60b9723de2627258e58a34841a1b621bcc0e80dad4ada7a25b0f515c10ac3c109398af9f179acd8ab9b2a946bf49be5a782a1766f56f81670a7310d016432df99e45acafa09026f108749274b9ffe8eb173b76cbeefcac50899e494245f9a07caa561c3b6c4d9656e7e8554b945f9863d5892d7597ae8f5ba4a4fdbb5c97d9ba6d681bd49014eb1c935d703977959e95d71dca470e9b2a85e218b1a2ed86815191a2f1595a2b3762f90f95fa941edeb88621225677c1aaf153b8302309ff239055c6974984afc6de1fd93fd799e1cf3a96c2b837fdbb02b29066d643969696487ac0ed178ea08a81e2b3eb620290342e94052458dc79e9152564cd205dc101893aca719fe7103d43e61e6b6039e2ba72c9ac6f450ef834782d94e4f6ab2fe6dc251c655bdb5dcffa3e0b624cea834489975e7e15b9dbe0f5bad450a120c430969421fa3086501c90340e30bec0dab7222bffd3b2da633ce276737a360489c1cd77bd0091350005876eaba9b6e0d79079358a711a3eed86851cebf1c6cd9107effeb2d4e2d9ede96613298b5e1aa1c4ba01fab933db48a23d0747e828c63a0e12985ee0c58ed50795cedb045dea7aa7f15638172ed5a452f930007b74bad33a6305be0bd4be380790fe3ee9ef89fa550592e47d1d5fd27fe43b75f57dd12f8f2157c961defc1fe3e0c002a450ec2c005133bf9cd0cbb49d956de7ecb702556d308b9173bbce216ccfce180d174c5a58ae95aa9c984a11360d90bb8d744206942f986f7a92c053a1004445eeb936bc401ba97153270c140119944e7bc604d0c33867320142398f2d8257f5cba7a35963412cc60b7439e0786dd1535ced3fcadbede4131eabe34535d3e811a6ab726a67e38765c8caadf35e30625375067d0fc0d4ee43d8fe045cca161639ffaa29b8de8130f4ed4e64b6ecd62065a3242f17cac45aa1c250255a20e0588959fae4ee6ba48fbf7c1a1b8c9f398059c217ccaa711643a810ac857219477a8a9aa1e38c0e86cf0a419f68ff0ee4d324906d658e31303c456d3e334792bd0f31be40eeebe8aae58de6ece783a4584b5ddbda73c55a79ef9e1a638f22def020f287b2a63c4dc99ac367079029cba1d25834f59b183085b51a12533189140f6062aa1baf291ba9e3fe711a75d71f2cb61d5c06b6893403e09ca558a9773802115633a903e43b937a62532766617d1f78c8be23680e3c27a66224c639f57118bceeba7c26c1b8d9e53e50af3933f23e5a134a4d938869ea5313496a1b87333fcf9098c01707ec3d17d80fb38ef72f30a72096f5815c3d7df439e272426ef95b3cbd2d2decb798017d072a15bf1892c6627056d84a4f26803165ee1f5200a16765eac1d124cb3350c96ee774af212dfdbcf02c5e08cdc5a92f5a65535938b3edf545c283f2d8125a5bdcf93f3b145629a5afe1fd1c1e590b66653f86681a901169ce1aa1ea55849b7b368ce9d09dd6d6dc1539259f7a4bf7cfd530aa9061907aac35420896a76ec637c1f179c67b49c94609f8365f4855994c97df1c5f5fbf09e582722a5d854b846cd14b2a8616e27ac935a225bb60372844e5cbba6bc759fc83f62711064291eaec90223b442e1fc4622c95e26cefe464cb57ee50d9e27a19353081bdd1e85eaafd9302c1af5f5e56db81d69bfa8d51488bad37b20bafbf48beca2a6adc8c0b59d2dc4cbc683101fd150e20f121ef9b81083a29ca3f6bce7e03b8be5919861e9f01cced75708d546c3af552d8a6eba0d35353ceebb45af7413c5a64f44973bac1037cb908a41452ec8052b66d73ae67190b182ff368733dc7e322874fcfda2f062024c626731ca669c69baadfd028e23fb57c868321677be59499cfc2c1bdf7f3290e60b55c2ec63908b4686cee8f4b38e62ae65c9a9b8c0be15ae95a86241d52cc64ba671df36427446086dc17af12305d0377acfd1da793f9041484ee04c4f84f9754b32edd4a80649c9bc131cabd53cb17437920ed2226593786baabc0af4286466b3ee0311bf0cdcf8ba6e2d0d98726110ae81657156e91d3dd14d274fc8a0e8a69eea26cbdc2d7034625ca615e4ec2c81b4845a347b5aad87c1e21bd1af7071ee05dd9849b002aa50024417e04cd10dcedb430eab0dcb6c23aab2058eb88ccfc60037515ec56036996b84b62825878a78b4e0f2dfec4756cb514516d09b8b7aa3e777fdb2738e095bd21179661b992cdc99e0d7ade0245a39649d2da4de4b7f48ba18fe54222ee67acca9c150e532c7d94434f581d92927cd783cd8fcef8287103820b280e0123bbaaf0257af85086718eb6ebbe628f06815a0a1389814b424b91d22206fc87b01ccd1f824c75f47c0aa115392f42ea8383d0c6943e5a1d84356aebbf71caa3c9af4e6017e89735c71b491a7937307422da745e7660ec09007b628790d372ecfae0d6651c16e3647eec9c8c59275dfa8192ab13456ebc9a098ea28699569d038bca852f25e0657021a808d3f38806551c7d178527dc2bcfba59314a6e4f71caee12c169bff636a4b0fef307939e545a127d758e6297b6b02e9a78dfa9d679974eb0f24c332feab9e0e48b64416a092525dcbc55055e55c795dd19b0916ad78c7deb9adf8ba370113fa4ca839024d7e4dcebae8055e6053e3618b10e96bb9d07f1541b36c3b722eb9a5c604671831a48333da4cb5a6afc0e96f30cf5dff04f4fe82148408ee77b99efb7745059b819d49de61f90a2fe264376626c18c7b5b2eae61c234575d87fc8c996b87069e99a6ecbd6ec5bd693dedcd69eabe04da76f4c88d698a2e34287d2f742b94b920b4b192aca0792ab7ca36d8bfb84bb7d4360fd2de21b83fb35b7c0e908b2af9158631f5da2e0799079e0eb68c6f06cef702057301e09eb75320463de4253a62e163f2749e931ad6b8215ca5052c67c57dce6d91eb2d0e4fd7563c4cac58eb773e7c3073c83161045a93550b0f9364edc958d425005410b2eb81682cf31d00cb2fd39dc13e1b1b304685d53ee66153eb34505effd9cf3413d134d39c1ce68f8d057f77120df7be2f8cea61902c7b53819247941a162401b750967ca9d01bab8561d1b70234c39224fa7991a1bc48c1b39a077fd3644e6b13691a34119461698722c9148eeeb387f2f4ffecdc2f039475e56e42f5731145eda830355ab998f0bc698af82fe3df03390690dfdd77dca45efe481cfffbc4155fd8450d8a7378908631ae6cce4aac8828675655bc9459d4dbab533b30d55ff2d8444cdf4bfc79b62c9b444a49123884ae1f4e800ae0adf2e087c8c77e095a8156247203299fde0ff0186cb793d21df2494f3b4d5763eb78dc53ab2da1ed626c3f229813d7d867e9de09eb0bde1616df1c45e2beeb42bc3e2284d2e520631f16d4dfe325eabc0de7daf4ede2ea928d72a85015ed17e4973c72667476beb03b391c50009c434ff9c5f14a351c306af26cee6c1ed227a74c9a4c5b814c9d72041727fb1b01592d7db2ceaa8fae62633eeb17979edf860ce7956790de2a56ee828f8a61cc858338526ebe4c2d3d4baf1d42feac37c12df00c7b51b2b9b6ce2ea669ac5c4a14f3a3370a699cea937c8ec342b95dcb34e0e4a796737a9b266f4941efa455c5d29e0139292d2f4fee9d00158f7a9bda123a1f2ec646d0cf95500ece29f2043c4fb36fda2f14a7d8e1d16d057f8cc7ff5468ca331c458963a8b0bd443815df07afe684d93c873df63f16529ed204b12735946395701139e48310b5e75945b4178346f7ad1525fac62180f1937c067959f6e604494a89032925b13ba47ba16b4d0a7cb57ad07692338b7f594c666a09e5d4e5f5abe4f7faf7e427e93eaab110ffab9270cc36343e6f21987ac8d02c33ec16842f585ef211d086bb49bdff3b13aab3503a09422b08585130ce02ef883f5d21818109f73959d9d43eabd319c92e01e73889beb32321869c3b3ca16962378d27b7b845b72a0870be22666e6f7d02fee0ba03cc70bee45824cf004b778d10120c318c11a90d5f8ae9f4c8902bdb6ae428b57847f21e6957c6c79f0fed966e8519f782cfa3d6ecfec6ddd771e8a716cb63b0f5b2db5ef417aff39d327ddbd2cbded5fc7f40973999efce6e31d9463b23903c204ae86f56564a0eb6475f651287cd71dde4d8af5fdc830a2948a249b35ec8587d174e9f630b36beb3458574b6a0e8ffb12c3cdebe329d0ddb5df31a339082def9134ac9c3f102676737ecc759bc90df35a213125dfc7c156e5d3c3be2c45c196c55838e6602d5429ab119335b02ed15198b56d1acd8149a15b829b331f4764d7175b50547a86f11746f59afc13b8aa2a0abfe0e51fe2a15fe5c69abcd45b5ca7dd1f6f21c30e199fe7489cf087b7d7bfd8a4f6cb89e6ca0a2c191c48cc074c493838be0d69416743ec3173ab92ee9be624221ebd098dcd3fc9307545cd7467d9eb0827e39ce427f79f493e7eb881202ebbfb5640b7a7761556e74f069c5127a213d039136d8242dcfafc427b402a4120d3b3098ebd8c991523a4961b0f91bd2691d13d8159accdaa4623a21e2ff184ace783e200e4c916e627f768eab0d204580e3e1ed662090a99f81f4dcca06a7dcc83461533e46406e015d5ba8cdd94aea4cc10538b3ae5e5181dcd4b7af4cd7705a72bd644ebe3150f4df1631804d07819bb5df3146f9365d4079a9b78d9718dad7cb7695cc1a80110a0e9ce4f087607873b5bd2d40b308f5c99ad9dbdad7c4dfd91a3aa6e906236c24c2481376f08b3d97f4abdab5f00b7db802b2338699b0de8bb3b217a731745c9038fa4f810f0517096c58368d7814093cdd6e54191fc09d4d0c4ecd726e692d643df474f3baefee869f145d93ba19820ce7d65e7be53b1187f8d3e9db991515369e14abf91beac0aec23cfce0c89eab2906891989cab6823064637251544c008f41a28c53567d361c4f39774fed8e631aacb5aaa7543a380370e675681fc01eb88ce66824d122c45369c8ab4064154e846c2651c311a6c07d5cfb3e177b6b2712b6b4638f1d1d4ac894c652253a23bfe8db3f2aa50db09848413dcfc06bdcfd4c9ca688f6706bd2cf7f034374d6e7cf5d68dad450ef9d3e801766f2296dff7df40d7889553dcd6d195afc06fe720924b01e39a6e768e9f95303cfdd39291ff386824e3cba0cb8dc5ca77642f7258a99cea563139f1640cb42897fa9c7ca1f9e578f64b1862b117aeb552c2e0a3bb1cf7b3e49a72f66d75a27c132488d1a0caf4e70f26abdfcc3a2034cd2a209867fa317b3ecc065b7cb0a9bcfba42c6d613338a06949df697171a92ceaf572314289cb14f92d2db49a8590d4d2760444c15d01b1d78571a30ffb65466400947514d24a07cc5c01479ed7c3b9f234de2dd707b04c6176d2de0dad20234ea8a1731fb5b53e31ec4e9627cdedbad6da0dacc62c8374785f11b64ef420acc62441b99280d971810ba0e1fb11ec697aa2fb7251175ff83d53714b7529c8760e688f853391f9998a4ece3e16709d897a45c675b0101d127ef2c9b036c723b524f4fad50d767a279f019a54285fa648ede6a2814670ad29e482b56af3632fdaa88d08609aa8a5491ce4f07d80429d24084c35478c538ebcd759e22ea50b214671007a0d03c52cbecf183c13824b1f42889c3fc01216fa2493fed234b2e883156eb1d3ab4b0f9d33a57ddc4e7f8e50796e3fa60e295183da3cc2e7377494fea9b8c40fe0b938b4ee1810d861f8de481e58ef5bbce7e637c1e2d5700c9f9bf438238ba9a476290cc51f53beea11a4ebab5b8d3bb4dc0c10433dc90b5a1067d634e8646a68992804522a915b4b2bc41bca4bf4e8956a2129e9b88c8ed1b77964255d02e45cf040421b655a0127064f8a9dac384c220ed4a1e13d1cb284f1d8a22ac0461c32080eda69dbc0328e4c0160bdbe9f485a299e18e3aaf8db2b2d5b64a094e9f62f6c671c321b1b510c8e8ac2c4f52c5d39cb1268ac20590f0e236e4295360b4063dd6e43983d9c18311778ac99b95f7597b0085ca831b57c0a4488076dbc47f8e06037ddd6efcda412cb0aa25625299914495f86649faf7f61260479fb565cf8c9585656886dfc6c8b0a92098320e8265c42d8a4ca9ed8d38a5267883f07808e30106bda6eba82885559018e21bd99ed90307a0edc65e45f0ec8c9b113dd0cfa2c32816a1294add8b448b12aa50c879ed4be94740f8cccfdc5c5938e6fd0717cc23a4e742726ffe908499679720da89e37c6623c4123b68045b586bd2be2bf8bce758303ca4f71348c45cbd9c2b5078db867d3a6f2900ad80a89272d38c5d40459e69c9c01b9c203237af3f2b36053cdf67b482116051d1ed31607407bd0eb79ed6b733272d35e05183b8835722af6754867a193f7b23daa04b02acf4119d8175048507a7ba725a830c9eb4b566a863711a11b6c80b42190f40f81adad56ab767f2c5be39c79c7df555481572430ba708c622cee7ff94713ac5d33b1f898be15df5b7705a1416fb4d7a19d298372f79e0df22146fef6f285cc1ea68becf6748f08bddea789170cdb4e052037e4d585bdd85fddcb2ccbf1c6a63f6b04368d016fbad9f4728ffa3f1d3ff9ef0e0ef8e1ebf1cc130513a57d0854c18042bce9c8f3a1b53b27bf493cad8ed4258ed194a93a30ba67c5687f06bd39c52937a340260b90cf6b258d97ef402c1883839ef30ce83a2922aac1f118485577a78c7e4f66e75e9f2e8b6b525adbcb5bbd3658cb702737a2101d5250c5fb99621d3cb8d2ac8a826690f81bb453d340416755a39261531ae374d6b1e6b693bf3ae4c3aa161586166184ea50422e23c188bebdc3b30942a021ee9764529add7912839732668ae1b700d79994aa20660534716707c9bccbca9e510525dae1d3207f0c4a84bf971f5265e4bba1781b9acb8f12df03fba6683afe245e5bc7a33f7b46fcfd085a6e9a09a3c9bd60dd5d881c095eaac14479ee981e7c1aeb0b7567a9f61f8d2a24145e1d34d4794edbf39117bdf429e0e07d4386b577a8379f8a330ec16913eebc809738536dce35a780acf2318e14ed845142d2bc67d2b9e1ac403f53c7775ad7c0d15213cbca5989df9906537dd78a4e7a599fcb22e1c4300032ccc72be66b31949096cc470ea90ab2abf414f142b1097714e47eb742c129806be3d6eb05d9072ac37595c833f84db02ae2766fd8451e5a250f386e1b51326c50b4611cb68999020282fc5b3896757c125ee5810fc3d889fc2882beeabdebc7a78ebaa92e1909d079bdd105ae11ee6e9c9ab3ded24e56543e246cfc647bd081c7edd7600b77fb62b5e304fa09319334e0ed8db70205c1d0872efd22c5494dcc9fe74a9aa1848cce786110a9381464b5ddf20e84c3a7851613298a5b2c4b8e997380cf7e9f8a40923e61ec70d26764dc62c2e0833ca9be0c1d9fcfeabb8c96161c828c6644ed458e7924a02bbcb00e61f87dc839aaeff0a84ab094651a0274389d129db13a4c62c663a7678b8bc7ea17ae07d691b8e43e6d82c70d49b1dadfecb665c818ab769107f91886628064ee75cc28e86a0def5b66aca7b623954d6c5145f72da091b3b5fc9b61a1fc1944fa850b230cc7010f5b31e12848444f2bf988ee071a17beed9f3b000f43f354e5ba2c0b79f7d96768914ec189ffa1ffcc3453a32e2984361261032acc306b897aa18a2e9e19662516a9a80c73bb1915b62d8c9d25e299e9405c8ceddfa8c3f5675040d3d3b6034f512934edfc14fd7b495d5636676bf14023768461ebc99fb7e4f3e0da381169520b8d5bca460afd18ecefe5a621522a52dcb687dbcc40edb90a2b4e09a9ade98d60f8894e06b9d01bf8cc9f9467bf05471790339c181d7896eabba13df40e4aceca1d124da1c4e6d95efe12fa32611d0dc15c280b18c4450763cfa490a221f93522f35d8ba43619870c64c36ca0435bf68f04282d686bcaf953f5d9a93b287b3b7a625ed8bcee81492fd412a187dd6c3702404ba8166bcc6d54aff670d201aa46a2df27c191632ed0eae96c633175a1820e41f26c8a9f1f18d9e6a91a86a6be6ce42f93f1480e5e4617858c11abfcdd7038e33d62bcd5df068413bf56879cf4c366f47caae116892d45cbf0805e7e92e110e36b241ee83c98b084bc6be9bdde1cd5173b6e8db2e54e00b6830203bb20d771e133a87c546f4c657eb7a0d278df54985850ff0de9fc148f3c2a5643ff68407eb017cbfc9a3632083ea7f14be7c967cc042114bf2e46b628adb0222444693e5568f893ee6f5dbf7011c671af7210e90cba1d7004bfa99d30f67f6a058e179a9393faa15f50952963fb8dc62199d6dc8d5c2e079e76ecd514d65244e4db244203ae984c05a9725f088cab364be0ce45e7f5b37b87af8457a03f3050dd95617d59f8d176db18f20c2894ce5f51ebcbd7770bd68970184a11e00113b34d40c0181c4f1eb8420d859f0c620340ade5ce6ebda57319cfe813f13827467cc8a4ac527b81ef5b4313b2ee8364d92228318a3693aa4b0d38147b957e6f25d4527a14ed12264bc6385a239e33d1d768e20badebe75d9896e63c9d4123e66b6220b45b6012cd2dc49a8c63dfabd4df22b25f58149b61743552648838c78a911e2ae752d4e8d9d88d6007a98869e2363227800547868d070e09c404879cbdbf55df50ff823ee5615aea98bcf2ecbbf770b996a55f0a37933b5ad5c6df28a5d83b7a87e91d3a369a1edfc96aaa3f8e914ba1beaf2625740082cfec46d052135a477f2522544172da1623f1b0ebab5d2e33c4425535dc43300e2258d1cf6f28b1c5971a44d7a1aa825dfebc33815c75a20198fc59dc45f9c16d97bb4473373be6f2eed6a04ff6d4ed2d238ab5d6a16bcd29dae486c2de864bee0c3faf42c4a81a4147b9ce1db21dfa4a98ab9877b81de620ffaac73ec1877e75dca2f52421266f8dd500cb02bdbc6c88b15ca3ef78831cd04a8ec8ccab0e63c6e7c30633e9a5aa1c9f1a3af1281d05abd1f8d286cd594dfa7782dcd6fbba134275dc56cc4e35c36a3db454a26733c69614949db812863fe5ed019e0dd829895b3c5184b71159a35ccbd7527b50b5a64e757be956e900eeb6e17ed5b12cb21e4e4c23ce32390d27d17b40f822173f0e032292c37d8540fae6401170d1a97e734e9599124366719f08a35e5194ca0fc01d2e7ad5efe7ed769179e9d5a03f9a3a79361b35add8738387364f2bf78291c2819ccd1fcd769c02ccc30c5e2a578081c2af18c18e0ecf65150a0bfaf853a2ac3d13b9441fee994d616c6b203c267088c337f3c54d909eebd7a1a88b4d9963ea165822044a8a168b21340629b28494314fb85e27f55c7130b0d671888cb6b0e94c597731feae0be76a476bf05698ac530f32e9e870cdef94bc9ef5943fe97c6b3b945708683384d5b0a619d1bb1f55df3a0cde26276649e06a9ba3be91d3e18b98994cfced4f8aa65373dcf63310c2a9a758c19af66c921092ba0fd0b9fc2bbce37e9cc4ad98cc29a406c3f897813e88056670651b391289d5d363a1aaef48e5589174c83803e967040e8db930f21246083404ad15793a18a1a531042ad9a8df7d062ce466219fc7b11c5e5af96536bc5a19fd384997140d2a353a2df214684542c6403dda68d3df1d2fba0ae17b63403dd06fff7a0b70b5fcb55a0a3264560ddedd051d4aa42024e5f2dfa000ab140240e242a49031f17c36f2a40b3c9162800bff29907f8501b652ecfecc94c0c089c769a7af6606e98bb877e71076ccc5b9920a9898399511eb553a608d791bfaa903a3ab993715147d8dc8d1b30c3b184a5524f79d2504928621897966861477cc6591868814924e1eaa2eaffeafb12322161178e24d20d355f1e0cfa2e07c00a0ad92d1092d91e573defdee1f3aa2ae7653fed245db5f6b2e3fc2fa45618813f155fcd13937a1997e2800d2e6f2882fa9903c720f51ce1413dc4eedc4db119d406899dbc6f5904972254ee9ac813a4563c353e832a193530e0e3f047bb271a2157494d0fe6ebcb344e78eb85e8ae750c919041e55cb09fd42fd6f4e6946301d9cd7563d79cd60df6f716405351dd75874c85d646facfcdbde084327f883b36a82ae80ebdef8a4ac812d3ec70e287acf340e87d584acc809ea60a9552132c67fddef9bd398b9d7fe8b739bed1546c372476e781b17c220fb05fd578be04f886b16294730c410e5c9110294c4d6e08ce4444f59130c6d5050bb1f660ab1986455049c8b330570f812014b4b8387c34e98857a4c8da8a0acc3ba44b0cef081232b93fa9c900bd5237b16c79f7bada4418b69bfdf9f0358a1ab2a9c726206ab5712a755556ad0e99f92be4393f1f42068a78d93b55d556a0e489c1755f83d4a789840862a46e79bdd1d29b9e4712583415d30db571dd3b9e3e9c019f64aa131d26435d794001e04a4679e62b14aa57f8ae3d041914a803d9793500e6c6a29f852c51ecbc4ac8f1e65001869e15476bb547e3c9f414b2fb96444d6fc830048429c5d509ceae3433062b08f8f9f82df7b66940e38af7a0e27717e2f2eb7e514dd280492ee8e65cfb3064f67cfdc00ae06ad23e00ce54282d29d1f13270de99dcfdfc0202bf0a56aa2aeb650aa6f64958d5e6d22551ba8ea03bdca40afb6e8ab37caea85a47ad0546f04a8bed62c6c80ff4a526d47085340e2c02715c04af6c6eacdc802e30a7e4ce369d493dd7a32bb98ccaa243b85647645b25d4eb68b93cdb264a3967c54271fe5c9b638b95d4c7615929942b2bb90fcae93db7594ecfe095d428dd7ea2242e37ccd1c56f1921ff754142c3266bf6f59d7b067bf5244b978b28f391b41cc601c48d8cf8bafc6d21462e7407c5c4a2184b7bce0d5cdb69e7fd27918cae6545ee69795670a89a44c492e38ca3c17f7ca915b56b8a86fa5b5a825faa7bc600856df80a00b620f94d7d456df655a26c55e362ec3cbdd368731ea7c30558e1155a7c3fcba86ab3d487a9f011e6af08fed1ff297a9e6b75d8b14bc465480d85908722534c84fde1d2147c921e5ea3f40cc1063717a4548fae758c15723c5eb2fa03226a9c34110279cc056a2c3da69fdc75d0bd382a1b2015ba3d4df3028641c9da4cab8eff65dfe73394d3c751f20da4d49aef189dd5ce0dfb779a9c1ce6868ed9a65f5fdc62b24930126adf0ad70f471b2280eb156f67bb347c0aa945761cc57a701fbfb14c43463c430104a6e3c16c45c84754c4e1dff2968dfd5669ccca98d5d63bbe3e1726ad79857c1e75483e1e2964cabe2ff0f8bc3e4769f957a567ee5fcf3fb99d7a18c5189fa6e0b28f904b8b10d7b54c73e7de9aad54e9ee72c19d1ae1bfb630015b9e82313fd750c26756d41db8bd8691872151026936c112167dad0487a7af3cc81f517214e901686f6bd3e301b0eedeb547a6a2da8f21919a01f654689dfd0229a23ce1190958090dd0f71ced0324cf597b9bfad087702a5f0a4ea942ddab1b4139a264fcf697bc880739eef43f318d8f2885a05bdaa362e120cc3047599fbb458f36d78b5c0b95629ff1cdd736e84c453682ddb312a58a19959e5254c500495a1e656ef8f7b5fc61138d42fcdc5be450d075ea82e078d393b2e21908443a2011eca66ac060dd7a2f6a7a95022759b8cbba16f97eae15992d69bfed3ab9828536b8c5984f920c8d5ae6e83ca5c33063c88d1403bee74531ad8ec75a6833b77e40c8ca9b629f4cd5cb226ee019d41fea7d244efba3d296728a2991881e2c1a5462e9fdcea98f192b5694375c6017f29e45f5bc6ae8443761f21f440b9eb7d6c752f59c6928bd4ab4b94a3438cdf890b64f3ed517f4e507f8180bd3bd67875a92cb15c8e93cb7475b18160efd31847ee79a6cba1072cc91c977a0c83139f88b7a92e45c01d3215ea19ddb5b210bd4bf3687501f8493989c8d193c7c62de95b87b20c6d39a5278a347c6876d91bb999b48d1bb8b9c5dd824ce2b64c734be5e1fed16b09e24c6d98db290dd217d0b36b8bdc2c148ee0dcc964a79b3497030572f2e8caca5432eccae7907c497ab13b92a1acc6f66849a8e9bd78a65528569f3dea4e5a85e36971945f2c36526c69e2e0cd39a77f19619e3dfca56a13e03eac961b9e1e8625aaffe737abe722ec603b37d790bfd494f42b06a65d88050c1b7996617ac0b792b1baa11f1e31ab8cacbe89287324da078b7b1dad55eacc31f40741bbfe634e832e24c83a8122a60ec24e1ab4432104d9e5e049b41ff5e9e5aadbc456a204cac07e80b880306fbb30ec252eb0dcf97484307d0caf3e3e6594bb2b4f6db6c7912ecfe83cfae957a50039f431ad201ca2e748906c215a7f769c14c545b2ff03766d5df98c2326dda4be41f06403112080ff0e3d9a358b299850834ae6fbe0566615174a202cf3eaba7a038539ad653636dbfbd1d1a5d253c844cc82b96214aedb622e5445f38e297a0bf8637190fa111425925b74ae7f99c67ec29657178ceab2184c1403aa19226d5d4014564d07e9fde9fbb9bd1f05f29cd6ce78fbe5a72ffc9a3aa129f2e708562f7a96a85f4385da38cffb33c861b0b3f7c1601c30ef7cc0c28b7c06d5faf1320179092fcc1c347fa009b637b3cc309683458cb2dbde7b6f29a594294919940675061e07f7b520b2993604871b1090b7648e4da801f5553e7a0da08f41188c7c75dfa65550301c2dc1a1496833017ee9a1decd4b4f9cb9bf92030e6566323399a2e8eafebe8c162f632433c33827125ed1d5f93e0c526038c23cb8e7845fc94e290698a507869de8fa7c9f00a41f38c43c4cc8f2e54837602933858c93ef3f949b94882a5350290a8b1226a03873fd1b2c19c599fb2816a038b3e5008391c4d134e4fb7406a522871b10e74326bd3037e3002691090bf27de9e9c12b5f8e897fe822e366e4ee4c42f95e8f7403a63871064964c9ccde4528bab697b122aaee872e497228038c7c5fc65174cd7fe93921c9f74f42d1f5fd8622fd87da5e66d646e4fbf765ac30926f15241acb910910f97ee822cb318a28f97ef8d293ef7f280618c599fee6b74dae3b21dfd70090ef871828dfc73cf7628c79f2f54c429784211c8f4463486628cedc5945be1fcafcc8f7655ed1f5fd3522df0fc129f27df0becc5074f9df90c650beefa00378e2ccfd9410f0bc1f38cbf73ec963f9beb7cab7074bc027ba68e40bc551be0f8391b77aeb40195c90f75d402173a010f84f278c2f0c525c4a8061386ad5fd0d84c1a855f73550f302f787110aa3fc04f6e2050e1c3870e0c08103078e1baf1b39375e3784dc80c5907c48299bda80c30f16fb86348bd6fa3386a24b95a750c65dabe8d0ec2f27cfcf69d64b6c95cc538617d8822f8036da14a7f3b72770f8c1668871a95b1f18de1cc2e4f9291e1c7eb03c9f8419e0f0837db066b5cece10176226d2acaf48b3eccfff62cd7a911f3f23cdaa1f7eb01f20176250fe84be24797eb23c3bfc966419626256086944facc1cf2e83aa76141cc1cf28853382751a84319cb210ffa9a376037726ebc6e08b901bb01b3810bb09c54d29026fbf81c31e223a4b4c50c6ded0e6b6d9782b8cd39c1189de4f8f7892218f2286d6088f3bd2b3378aed65a1b39fb3586ed93a38d315a5bedac94c64a69143d8367fb9488e8c96cc71322ba84bbbbbb67c3e68181e5dbd73490b5c8c1344edb60dbf6178cb286f614d539f7de7b2f4a8699bb3bcac3944f828b21c86c70aa0116c508aa19d0155266d475a4943e826e87f5cc47e03aae2304d79991a025e521e33af30838fc1f8a71de1cfef4a3ddc6df51dea82e53ba514a2b7d8dd264eae373c4483c227d7c3669a3f8ecd858818519e6118923661f283188f049ec209a11113d99ed782289884c4004834b3fe0120c2e3d219a43c0e1bfd0bcd1070eff93000214f992393eb060d3421786d0e925eb9f46a27d1a89fee99f1f1e542dbc90001d303b7e8a8ac084f48ab2c0e1a3d0ff45971442be1ad6b016d2421ad644c3667487165e48808e530b8145c1a14a2582d917f2a0b1ecdf92524a298e9ddaf4250a874e963f270e58ab248e1e640d4e6a00c74eab6cbc7bba4cc18604cbbf7f35d03e6775e8dc1112b3286d5e4a414ae9bdf75e4d7b3a357b5f4cbcb42f72a4d704cf177e4e10e6149b758ae1cd708cf91ee30b14ed6e4a6b9d3dc81af51de6df71d63a77c21eb976bff42888019b9b3eb9463dd044570d79d064ff19041cc6a23ee9700a8147bb1f10f1804726933932877e4b203247e6444dd612c81c59b36074702893fb25955d86e98a805f7a8143954a750435634b621949f077edd9ddef0e62d0b1c4ee2edb9d085c9443c03168063939061d29924bc83108899ecc7df5e547806886f9b8148122ea1e03c843ca192dc05c4e0c7a61476748ee3701c1a1847994337e945a705126e62a701887401b3878fb300aa970fa277e7c411ae9966ada347e9b7ea159dff727a059a4f73a1afccd6af9e8cd2b1fbf5f79cea377b372801ed9419be67029ec915b50a58f9e4aadf55d88b9822fa6b7407dfa158ca6394ba830b27cf4bc477616fc13ff0a2abc40c66fe32a1faa7482e96c7c43859daacb9124bb73d2af3f5132fbf409525429cf4ec6ae0fdac6755e84399d3a8ed4258cb96d5269abdb22aacfba69b3a70815eb4384399d6a9730aed637ed6e8eba16d53e3d8b16dc40233bc29c4eb54b187bbe5d6c59d7b428995ef5acd4dddfd3228dd33bc638b3e00e1023e83d81e54715b817f9a18efc22411dfd1fb3fd0dc440fcb07e063c0bc181fe986f0dfd1ee89123d4c0f0367d0a7abf8f1e391af4319f822006e2574d47b6f3252347833ffabdfb3dbabf0f8c7d15df870f1f600782d93347c6d953477b0790e08bf49ec0343dc4a594dca749a95139a5d4bc8f700788394a19a3acd1658e1e93fe89eeeeee34d25db674d92ea5e4640eff3871e964376e4a6badb5afd9d7de825a8f2c51fd94ca6e8f7bc93d45851ee0341963fbe6beb977bb3b6dedb9abb96bafe5b66edf03bd9a3f3f9e0dfc7c25bfd8276bd5fc96b46abafb37f47db24f26cb53fe37f396ac01ce578483738ae4f9f1e8e4c92384647a30257472f81de529f493436c037ee5896179e29d3cbf52c9d3acf083c13e59b3e62bdf90c34eba30b3dc74901f6eb37cc37c1edd3284b0cc94e26441ba01c7202676f20c9c3c411a38db0fb0d8799d176ddaebb48de3388edbb627f0269b7a92e6d0d78be6dc1cece7d0752f077073e83877fa8aaef81ea3af19bbb01f13a594529ff26d9a52225a44675e6059f4cae996fdfe524f74a590fb7b458bf0d0190f9d650a44797a663c72894d9f66d80b1c9eae0987062cf7e31c93b9f6d953c6e9d392e6dbf49739efb61cf0f67448e290af6ffb8cbea3cfc8a859534ab38e764c43bac54517cdcf946e5122bdeaefd26744bb9b0a7d4642cdc2be79eb45079763035f34ff8c8ceece4e9763134548922718629b3f29dfd16764841bd3b045968865fb34804c38f99ee01df704b655d6aad49256d9970d8529157c4c5460955f49cdbe1fb2fdd667e4ad94cc573635cbf65776c0b155322553ada8c8b6b79f5a625d76f2e632644b0d215b195b29a3b8b2bffd06247134117953c99bc906d32b6fdb4b08b1954a85b1d100fabeeffb4824cfdb9064c9f2d2f4b2f4f237f92129ca8ff2b5e8c22b2fdf4d38d984baf85eef2615d42afb31c699a3ccc1a588284d15b5cabe4d9b50353ba51a9e6c6b601b0f0e53b3d42c45647d8849f2a68324cbc8b2cc922c6584b21c024696a9a32c4310490685b20c6920c9f26900459797250d21179f2c5d80b29411cbf643532c4b534fcac857f643134f0e4d42b294998064f91b50746d59b2e40f4d3fb2acf1a1061657569a60599a76b2fc9a1d624d09b24d019d80935346deaa81f92a06a855f6694a96ed7335306fc9e0f1956d568ccfcd618c4fb61fcefcbd4db380d15735c503945924eeedcbbc302a1525cea48e7c653fc6a759a92e649438525fc81af6c3989f02d8fc81292313608a93c3124f4d8fb762807c656b74882eecad95b026962dfe3eacf1c9f657505705157dc600d1b732aa681823946bc6aa4c41faf2b9df019350f287f033ca5606959fe16621569e7b02df1c3307daf40aaa3bd20bf773eeb93023df18b76ddb36da2a0ec54599a34c49e9954d19b52a65d4aa949456c9e04951816b789a15cae0a919e252a707a002abb12f83c7ca4da77d18b26d91c38e04d93ed7a138778f01f250dc775393dcd397e6135ddcdbdf7e44566ab6bda24bbefd1859292ba20df4150344c37d4597f63248fb9c4475afa1e4a766cda2344455bffd940db9df3650d75222db0f4b3cd9b274c8f6c318a06c5f43b16071c6cae0a981c519fbcda58480b59741e6306594eda78ca26b7bcbf56b32db1a58b61fc6a16c2fd8028a33f6e58735b0256239a640407b6c3d83072edac0d323784fad8a57a301f60fbbec9ea74651b3d2596705bd8c5b8badc674d923e5d8accd67cb19a725dd4f3549307c439a5c9fc65bfeb5d55fff05961e4c87a893271d45578d5c5132bdbaacedebafc042b7ba5ed5af9526d7fa214c0e69be7e67031cd2e4fa3436acbffd93b764d13523dba73dcdf26c51c840f9da6ceb0a34adaa21cd27a3262eb07ccf0ae0e4479930c66b55a95593a728ba6aa20e9a6b448fc28ab343c790fe6f48b3be513a76b48a24730cbd4347abeee5f18ae4d94d29a5f4eb69d5fc62797e445a356538fc7888c49f58603a5356604f8744c5ac23b504df8fe7e3f1166e9027cf6fcf85f0ebc953524c714addfa184b80381d70dcc7915e38bca4178679eeeeeeeddfd9c1d001d2dddddd65cbe850384e76bb5fc145191b51ca76c73e492f5c79508023184ece3dc65377bbb777935ed841fffc0581bf20dabd414aa9b5f6deab695a8c1ce779f192eaf5b8707732f4b81414df6bd28d728f495cb650c6254da3023b1876dbc6715d4f1deeeef9d57d3678ca625eeb3300397e37a54f991d75a2333a40a1ebf209397e8c2538ecb2ec6eb35639befff47929df91efc877e43bf21d7902cb972b404c26bd482f1cd2e49b83b5e7a247d993b471270ea6bd9bfb7e63cf2927e780a366914e307d81499f39e7e6ece6dc70911a8be5fb46f26d5ca455b7ad9596925e0c501d90714fabeecf8e840386010c195982ed587a502f12a35e2428047dfc556ad1a5bd7c9250746d2897215fdd6f94cbcc573662a96c2366c387d3ba46c51c18157370241cf07c1210ccd28355fefe0270bc45fffe025edeaa7f7f010b58c00fd1a53d98afcb500be0be448524c050b9cef2fd7629caf7498091b0d31e0c9d83822e325f05b30a50a088067bebc357155dd5e5608e8f51271c49f1051b05e8903d60470a9d165d1295e34b948c17092c5b85ff7b313e9d6ae46ad4aa292e57d3d15774551ad3e7d6225fddcafd608a9d1ce4764eb6d6298baef9d2a9ad3159467b720ce71339be29e62d171f5ff587a658688af95c179febe2f2837201f26e28e8e223c50e3d30cd498c162d31455560f9a18b8f8b4f8b8b8f8b4f744501ccb817d7a25af44500c8b528089f62906a51517445201b3278971bf56f1d275f4d77e7dc638c119c53669a698e602a0abe61b70c74fae4f13243f95f8cf27b649941207b78773beeeebe494d258b7bdddd567777a7d54484259d1813c1f2a75ff74904968f3b9c022c1f178123d729e9e964ce3967fd3ed38cf9320335419912563300d6caa46aa4b025e0051045e363bc9b79820a50962c8961c7eb024533a3d705882aaa8556f5c7783633605c5e54a8a9993666103c657837d38600280bad921474912180e8b4c65affc54340078060c9ea628c73b6de08956a05073c6b56f0128ed1e2ad04297937d4e4ad046139b931a9bc33d07e60f92bde4a10ecad04c173de98d33573307def86067191bc2094524ae99c73ce49e99c73ced9d3d33c1697934b4caa841597659ce9133a140adbd3888199f142c20c9d57a8a60764d0991b801a3eb0b778375e838603d6d2a440e9ac1fc38a18decdfc16efa6bac7e8d15d053fe288166fe515801a3795565854529873ce396dd03941ed85e5636fc507a637f4f36c084008a6de0dc5de8a0fec2f0540e389f67ce1f91c6dabadc480e9228d8ecb32baa494527a2ec1adc5e4d2029a5e525d110a321d1d43b7eef70ad1c9ab60f1aae90116cf0033afc4b0a4565297049b4b88998412dcfda509934e89039ad014e7098e33943a8b7733250f559d70d78aa157aa0e03c122b372951256562a2674d28be104cb133ae9547ede0d05351cb05c4def669ef09dd0ad8800cf972b3660bff1977300e8e2a67154362326fb64531c512bf035b226c0a72124704b8e41532c6179a628c2ee0093720c9a82871e610a1c2a0cb08c2e6a12b8e483306ef063e8055ba80006433b580cd140b9c038ca1178858ada0366496204939f2130311204f6720c62224414263c0081890e30ace518c4e4c50467c86a7171cb0d7cb06821649a41ce8a50c908202a59f8b09c60077c05ce0a0e46f019e1a8e0e005840465084fc9f18030f45d1a245be8d05d7c3b8cc58cd40321dc0f80bc4b83048aede2dbe1a29ea25887858068190a0e7769661024564490fc031e21990ba112f8c8443d41486ad8c958c0a2e4a2222d5e19082f1fe01004a22040c1424a70e8e955cc5bc47a157378716e410a198d8d1558e88b698e4152087151465c238c730c92c2871c5e1a2152e0d42f308d2aec0f7e252c0e8ec931288a20681458468e415100a922c02a60d426b0ede2054b7890c36b715892e48625422d2c01fa22484917465a8f1f22dc7084922749bec0222261811127b8f01104249af48006ad07406c4c1c6a4b18dd5409274bb484018234a83d089a40362dbc90802e2a59b2a2040b971c83942001032e25c3e1cd9d659412f4cc352aecfcd2df03958a823b85047794597e8f2cc154142c5348b094ddc36507715cc442f811edfe7de81e6762fd8e3352ba808bc0d22669e1a44f4f4b52654b72bfbd57de18354d03a2ded27c0c20739c6ae015dc249a2c6b495c809140de2209f9ca471bca7d22693e34324966bb762f4c85340d725316a1fe95a3e89a4020c9fd2a43ba6592b98a90ca4eb3dc741464f45364645624b7694885087d712cb2dc47508a86e804674c9a64de6a274344332845598a91a93ad8b26aa50501e985e973a4178e5e71589f324f70bb019615ac2b47252056c060423f263be815c519fab8087daec6985c272d019129ad4f29fd5ed22d16fa8e135dfef45bc67234c5484a119459a6946528534a12ca9e1640e4ee64f0ea7cba23224543d32bb2c2fe8ccc22f35697d3ab7e9da121332245b9885136a2f94cb07bf9e47e47759ca14fabacd149618f2ce3cc740dc724dd5dc656b91066271db2eeae01a639062981412d02fe720c52b2821c46590cb0cd3148090aa4129d951c83940841e6720c52f223cf2be875975c771c285aa101d70aca40b30479d09d3cbf534002dc7fe94f14f822a78c1dbb174f1a4efe30020c4c9f77e3335038c9fd24efa687081a22e2430ea838c28b26c278ed2071920220945c9f4098e8e28b6f27080a227985dc2a4678a2880bfaa107079c1f998e133d46a0b38323042133c24310395e903f7fe449f2018718472572f76fb7748574129676a0c514b2259e38c13cb5143149d041d1dd41509e7f718b0b827420449e3fe7dbdb1273620b16f2fc3ac5942824b983a1dc414e0c4912941b439913260735609ea01c1421cff7df8ab02c05219bf2bc4d927411074972abe8e0e4f97d3c3026190ebfd927a55b9fcc574653aa88ac0d482cc1a2ab7f7e69f6313dd1a5429e5b10a23c75d021cf59a9c84b4679fe8c28c692baf3f1786bfb84bea00fa8559f91ad16b2f8915904c92c70d092654c7654ebb46afe4dc35a355f0afc739672bf7c51e002cd8ad9bb24f3493e68a3120aec73ddde93a2d7ca3d06b63ae7dce6fc39e78c3cbca74f69adf4b78dfb95efd694f5cafe50695664b9228e5968c592f6aca05a72b70487f767d0586599bd962cf3d791620481350d8567b8364bfbcb42abec974abdb21faf0c0707c7043e4042d80953ae6f67781d89090ea7916cbf48ab6cacc90f6a932e6a1322b4c0031ae2aa56231c1c1c14d4a1b88ab35b848d4e571cbdab052c70e24ccc779557502b1c6750a4914ea1564d9a665597716a31951cc542ab649c629c4eff212d755e110eddc84724db2139766c6bdfa6db8656d5bac55a6baddbcf5a351c1ce2e9376ee3182431c145684fac06fb1c1704d42a8b5bdea621600121404093a305b78b1efd12aa86d29bbe963e66f7408f1ca51258fa8a9a1fc0c95e2aa17c80b7d4b52a16bb3d1515d29e6c8b8034d62a6b573a9fb88a49d015949755ba8a5ae92a2a9429c9526992ba89b88a4e44152c9490b2ca77de8d4a4bbeb1f6495260faf8432a64a9908ccea0342b06616183942e093ec2d1a9af29cdaa395527ac74c849b354de3e9d1135abead457cdb19d77e345c061ddc9f6655755402a5341d12420158a02d3167076388fb2c52090cf4f500f8dcd236f4d23b3e7680aa126106a06b52ac6b77c784daf57b631c0967b4d18af405a82c329a3471446759a1ed99f326ff9cc57f6f13de129cbf64950e0f00291de7626548cabfa2554ed6810b848b6dfd32aeb3dd9feecf116c6f649fe52045c3fbcb221af4b6f4d6fbf71bac7df1ab560b39950d85716a657f6ad0d1b28a0202303634be0f595f5a16cdf879aa5bd75fbf61d855d45a655fee1ad3cb587d734576837e010672958ca9f5f0eb8d65a6b7577f7ea35c729460b96dcdddd055468719665a5c2c635fad7b9d949cf051829875ad5efa89885845ad5fdd3aa86bd83218feb837dde8635ac617d1476ec74c17076f87d2fc6d2c857fd5216814309a32f61d1e5357b9f9a2583cc30f24aef052428b0b3c0fd48233f3f48202163fdf3c3d1d70c13bc21e4c62b165d31375e2d6858c360205f5326e703ce498c7706278a4e88889ecc763c21ba3b885ec0f1a0452e91dc6943683d1088aebe3c2ee0a6e45347d43de966fdea97774434e03aa262af7c7c8e1839d2eddffeeeed9336fd66b3a2cfdb74773d8226873cbab054fd4f218f530be957bfc0a0ed39d6ba75ebb3eb10548e32e246359483210f9a39e79c73ce89834881c84e7fa03f62eb3a7d81258481e92bfbd357cdf5475cf9eb90f10fd927187e8f09554a6dceab5dc751a9d339a93b759f3866b45034f8ad12026ad27389f08833ad88d67379b421b422f7462cf682171c71448eebadf946c81c9a1113a05364164418119b0c4189a493215c20c320c212d141541d2412438ca08710617d8cb080061da94eb8806a21a918e2884c470c3b203044ed410bb149c85c25e6dd6ca5f3c482190d1d2b34cbb2a0e5d0b7fbd629829a00a1812713b4244e7fa95953e62a31ef662b9d0eca74c4b003023834f1e134a33dfdb1ffeadcd3c39c60891da6f8d8c006407270600591239814551c8942889c4172201bf2440f4954d0050f3942554e610754dc30050b8e5043124d78e0a2490fb983d8e0faf46a6d8f2270a0a2861930b185500f076012032082e0c44e9126664eeeee7b65641335f1416980459fb1a7d54a948c0d38be047b6c12e43a89ea7133e7913a894a45c19d4485539ba86e6d36aa31a594b290bac8650e8732f7a0522563030e6f0178d058ae2409b6446912c56303cab4271505879f234b27513241707c2e964ab3a4927bcb20903d66773e3011d8dd3dd2588e1f61262f4d161a5b71f718ddedb793e3ab587c71115fc7c4b5946787b4d157f4380ec864704163ace9f129e5e9bed3282c52aa5dd056ba93e3bb7bdc48946a5d8cd27d835d7ca355713fade20c55512dcebcdf3813e374c74260f9dbbb7b6bbb31befab63c0d1c04963fb758dd7a32e5c9b26b220649257b0a5dc8f649abbddac6751ee9c32a2b2c25534b8c930c9717981931346a66c04caf3f632a8046f4d5ad5ae43366a050600a33606040304503f3f2924a793754a6488d4dbb9b76a97d717191d97672fc0d165df5254cceaa0e4c7ebcda2bc08c2c43c2482861e69e4e2574a4173601851c93710b4c9842d1b5135dd55b1d00845c64b5e8aa28ce94b189aa45ad9ab528468c957763ff04af2fe9ea69111aa3469a755fce5c7cbc559fab506a51d134994e00802a9700062609c5226fc9592d3adac1b0ee954d313c049631193353cc575d32c54c179b90230a3325984c331b6c92a02991e38c6322b91d1ae24ccfc81267a5066c2a9502504306031730da80068e6ad0a42410819d8ca8a4eeb515bd643433120010007315002028100e090563a14812e5a9a2e90314000b6584427252409607438120c6611004511043410c31c820a09021ca3844451d050c803b9df8a5c48849680d890d3a6f12ed117667a20793e62231adb02abd74538834903badaf290f57adee6fcefc7063ab255e2783e12ec4e47f51be3857ed122b7d84ddc36399be272992f7218d01a27b1c7fcb6eafe9ca1cc591c6abb0b0d665f731fb8bf0b469aebd04f4152a111ca80e6dcf9d57a9568fd5c149d766e24126e9c5e33188d006da729885f0df8125327acb7f174e5dce99afcdbe3f2800069946a4d1c96b10dc9ac8c97e96c00f424760b0e3a250e839d48b8c6f6ec1a9958279f684b49d2e268cd20ac8b37699506214c4934dcdcacfe8c02e6d1010cb6766f3683025000360b82b48ef28884bd19869649a3c8d7a3156403f420521071a6fb8d54a3ee8b040a88725d03c249aefbcb207861a7fb1f06239ca3ecd29460bbac0efe3e1fd1672298c837a5aa05fe7064907b1666f57db73c856bf0751e26fe94dcf19789a1b01a2be72dea412c1b2fa676fdff0bff1dd9f318d09d3dec0932cdce51f12d39bac0036eb3485567f65318059d150fa30e367f24b428b24e9f7f77a3bdf1e4ec5c70eda481a13f18d161a8dabbbfc43e5478a6b683adfff8becfda785e30ddbb11517c4f724c94102e4303f5065d68a48819a6266489c7944789be77b22333dddcd3a148d046834a4da7802bc5cace50ec8e25e7201a3f21a7e39db9962d272895bb7ddff560e93afba2d2d818834a7422753cc5ccb3f5585180ac910276e41256868db609643c3d923d5d27bd2ffc1996602de1e43dbeacbbb5c33f712ec390794e3a7e2935518a65fddd3de47e7755464154df6becb3f4eefffc5e61bbf43e6bec16b76c3fac3106bb1aaba643ddcbad7da1497fff24c0b636c45eac77f1a7c67ceaa953b79fc4aa86c4250271ebd9763cf0eef2e084413f06803d536567a2bc19ee5daf6db7bfdd9bda0b78e1d40211dc746f422a05dc504dc872e1b751edf383d18bfe99c65ffd04eddb515260679f9510df6196e448dd24cc88b1007db519ed3ec50eeb6967b3cde760d0192c699c9d352bc1b58af32ae1081d3ece4b3006009e19f32a3201bf626a0900bd1e6677eaa262fd4bb8a7f58d3b47e41ab4bf289ab0adf26797f2291352181d8e7be2abd43ac9db2043a9bfc474eced425314e05a339201bc57fe964b469969f9ef1f8ac679b3b4c9b7b769d76747def91eee2a8c475172f1c258de039f5bb545afa25e112dcbcb4bf90b0ec23b8c5545c12f1020a767f26a9d0a9dc955a61bee0ca7e2ab70bfc28ed0c404b6ae589800e7ae8217bd89651a1312bf751fe2bd77948dba0f46db75009f6c815faf06ddcc289f93ea480d7ec59a8969ed64b43b8caf14e6789ce0e8baaf89122f62917d9fe311ed41ae9e001c04f9aa433b3203ba02bc67aa100fa58bbeb5c3057e08b9c45035bdf0f14136b9e4a28e20928163aa3a77c0ab2ec9c22811edd117372dd52bb2dcd0d527d25ffe16e9bae5334b892f01689c312225c399b630b48ab129b1973e2cd33bc7b144d0f763d62b818975e2d23bb3c617adcbcf3938a78e60a758b4d6d688921d6b513c586a37caebad80ae621223ee867f12abed8a6f596931619efa2d9b729f23e33cc8d5499ea802f573c7b7ed4dab76e95cb6539d3fb9d5ced3c60dd418f08ec9f29a444fc319473bfbf8c508cc80847aa1731e8bcf867404c4ee8be929446e3ca4e638755a87ba22934fa87fb84803dc7326415e6082a252f91e0bed7a5884b9a8806525cfcf34b6cff6759d725524ebea865a309a0f926e5f40e57193ae39b6b6b8cb5291262a76a5168e2438c8127c108cf446235b38edf812aa0ef94b4744485ff369e37da566d7ccdcdeebd07f46a9376a5887cd4c5c47b8d26a4283e65c5dcd6646ef5d88be97b8a304676951191e081b874f6fab9f6e3d6de1d762ee89cb7ec7462764a84f4269b91db42e65ac4cd229a30ca7d9b7110a7267fb9136b3135871625b189b6d653adda26ad1f60d2fda83890ef9ba882ca45ef5e4fb72d56af7e79d23bc1f9596325a030f58da4b60c21d0a1049b173435e6fa8cbbbf32dbf4d27e927cc45d2c6ae187060092d46863ef60db6dc97ce4371166c30898f99e5ae3b15a2fee15b85f4572eee02e1ead6b464bc02f5553723f8abb732dd96aeaf3a5336e58d86296e121937d85905346c3e2f31fdac61c4485c92eb7258571d72d8088e73ed4497b4fca00190224c18177ffe95eb24fc698617186478540bf2de2912c69e4bb77c5e2ed14878f380e182b3380f8c8c3866b288204fe88461d1f7b55c6e0804aaf6069fc2980a5022763a11786758c7d1644e10200eef2110272eb041c515191377e52a249bc276e59d3d9a2ff56d61f168f0ffc720f7d7fa5174fcbea3f5713509cbf49aaf4c05e0c1e5ea5293ff9d95a9aacda1470b35e28fd2fb0a1a09e22f102a1f7ff400702936c431d199e628046e35bff77c13caa9ad09447e8f93797052490b833b1e2cbc6662cd0bbad3696d2430a59726b5398a33db17a21ba8054307805155ab9642603a0f7b9e1b2427aab54520284ec6a36c5589a566a31e50b909360d48cd18c9463a9c16f15927890ebcfd86647ce537c2c2acf48788d42786f2d28737e4c4df3159937d96ffee8162d130867c2067073d697603e94f79afe34769a15031cfec4ed5e69d4fc86288651b71b88f001a91fb5e48be6d952abdde1362ae5b73515f20cddf8665b2eca0fdcd2115d44afa843c4f4032267cfcf30025c1fc64601036d224601a21a13e517fc119de434924d337c4acb8c33d312734d41f78022d13496caaf9bb320454c546646da91b9779ed7b4cb6cd3e37ef6d9a421589348523c6399e976f3073cb146a5bbb7363acdbb5a0b886a7a174ed0c40535cae72fe9f3b0c256535dbb4237d5947205f5331fb61a78ca149ed9d185923b2cb010a0872a6b53d2ddba9b22a931b4d4d40fca7cb53dea74d756087f938167afc537445158302ea0f233b9230cd72ee534c2e87794840f1a0863b52aed054f5c9485490c2dad420085d3c67c19b92a0b9771e047c75562b78fbc8dfd393a4f4ef81180461b89bbfce380b34551364d2bb7a6ed14846750194130dd92bec74c086a76bc036e5b4c7526b6a9aff6eb4fb92110c990e2cbafecb52d91d476cd9b59e344267f115ba161c6fc3774e0913144a5f718c90de96c3777589e6ffb9e8b1b56654bf6ec80c54e972fb9d96ee355970392933be55be9975b90c99daef840bbafbc2f07257945ebd8343fed13b444b3e62772441e98f880e7d132310ff0ce91618031da83ff6965da780314808674835c056e6611317e88149efa5b5beb8664fd9962e54526e917d8cdf8ad854f7fe5d3763292ef3d417559a47564445c516cdf2fc6e3935aed2a4e5c14240fe50e312f12cc8a2402610b960cda313e30d2e217be79f24ab7548a6b38db2dddfaefe1122f46a6d2f002e8c3efc4606fadd7f68d1abd705edbe397bacf16f46abd744db2f64bb4f14f4dfc28662c977f6762bd646e602ead2fafd5f77c58ef6ac9b33b4cf8b49eaeb4c7a79e47672ecb46e8e246d748402bedcadb16777b68dcfddd4a2e6274f602edc13f788ad87e84ddf38a3bcdb611eabeb55a4ddbbbeedbf09e6d580aa98a00cdc1aa109bb95b412a18dc45795056f4b8fd91f4444fab8acdfe9f662aac222952f8a251aa09080035dce035e0947b6d97308949640d86222896b9674e76e63c366b73da4c488138c403cf4981b6d1f027ff4d220cb1ea9c023c3b6f1084cb51f91cb17d7392936b36d9601066768123321ec305998fed433c5680d6b7d1fc621724210c79436125901ce411050534d4e950b01e9535c2c2d3548610e72b131d40db0804ca9fd2000a452ec835ca90fefe846966f894fcb4818835abad86c9b1816ccbded7f8d933c5b89a72e9d454da4d71f0ad0141d3560f5d70a2f536dc80b060ca971a65adf698a1b547e891356b4cd872cb9e9b0a481291b540f558586d38eaa41ac3b44fea9d8f586f25ba958b73a81e427f3751029045a5f5eebbfcde2926012fd67a34e87aa4916d9341ce0df6ef0b80eaa406c8631012070484f4e488e4e2ade0cb83ef208df52e0b111248d78921a4e86d9ef5ef3e6cf09085e9336f1695fc8291eb752d112802c83780af5011091f159579453d918a7a425f811a242f89e13b2937111a70a0bd6c3416050c87ec3a9379a0ecfa58a0f0024bf5517b32ff74497b76f770db75738ae85be1af80687757e2961e4d3cdcd7bb15c50a5740d8cc68187c53cce8e22fa39d585188c22a2bb601062477ad6af197b9bab1a35044f7f0ba3db0eeefbaeec1b2f728bcaf8e4857aa9856fa607e124fa6ddd8a20885f55a80f5e0bc57a1c3574fb79f1b75dd545c5c75e2eae362902b7a6c98bedca873535191f0c6acb409ad6497038c45ef1536c4c44a4d9b57a857332880b0e80d85afad98f8d2cf6db1e6d1cf81df4fbbdf720be0a5ee17690072d113b0632fda46996962dccbc81f57ebdc4a5184e8d50f30af8ca51f9f8d857b93aba07f66adba1e2656dfd26f24e716bc744e98ebfa339ff7ebf16fa06f25be9b758dc3476b98eb2b319eca05ee0c295aacb7890bcee3491ce5c199de12fb342a9e6daccdea6aa3d02b874adb2278e13e22264cc33c1f5c60772b2b72bdd0a8d58e2da167d30e1398010c0562281eb99d25be080ac694cad6854e0a7da90b6980cc392282c2f4bed31f0f25bf6880eb1add9035800fcdd10142e5542db8108783a9b2fe8b36564f975864e0f3a28377aa7c083b5188c3b853a5788cfe1e3cddd6125bea57976c893435bcc5f16e6117aa898807026564f95780e5780589e8b100ad3623c6686a71a2baafa10311d71f0278a6ca8545c5e679907f8c3ad14f222ae839d93661594eb3ba089eb784e8767b176df71561608ac82288085b88ce831ad71804a7dec0110f812243de10374000228ac83109e1e588cc6d36ca3901fd893b0e3999fe6145e32d6268897364200bb34f076e8046717a9d05c29b0aa56f354fbe071879171f6e05e87eaf3409e8cd3683945d52c93398fe439053f043692522a881bf8f833d1b0ebc8aae5ef200eca222717db9b02316ada6e566ba065ea3ba8faea3fc13a848d7172674f14f81d355b6a90715e5b27d73a0155d5e3471ab1baf0ae2e562bdc663e1677f999af0cec02382b0a54b3cfbb146205479f6c1ec65c7931909b53ca488c05ef7202252ee64438f79fc018cd4ec7731aa957cb200b28957f39dfa35e9edd4854849b4aab0adf6e64f8456dbbe92333830f77f2654f25cd0bae1c5ddd36caae038c9b77c6f418e2b3a56f05ed629c562cb3f4b060700f1242a4195cadef6591b115014ec581508a56708e2caeec020a9c152deb659518b93d124e58c56871188378ec620e44d5caf2c16dbf1856c2f553eb04d07cb43064124d095cd91070591cb94a9646284b69ed24e1e9be4005704dc4b2d8d9cbacd9f582fe148627d82168cccc0b06d8ea8797ba55919352d438796cf0e62680e71ec81c039a4fe0a6f862807e446a7eda272670985cc5dd26d924f9f54c9b7bd5747f9325ca61dafd567b08fa9cad7769906792712a5838c6c95b8e1534989df73bb9f4ad4f0c2972ce1be26c8bf28d1999b8dcf58d8e24547a9654ca5ab26603a0089a0990a3a586846012892af280ed0de8bb2eb088f3f545a50e9e4964c24747c3e3662d85aa2d19d47d5cce214580cd6faa4782984fb88c7a462e78e2756605727e7bde174b285116f8e66f3d2d04851003651fe5000642569fd5b8fdfd49c99f0140ff6d75e4709ff6ae84e0abfb47121fc945519992356e0bc6905581a6e007b84dbe24f52a1205268d6f8dab92fcb5c453bedb520d475c29058857817f7abd117bdcfd1f278601ce87ebe838d1c41186b3d526ddf23c68d4ac23a2218648100b34eb5035971e1ed73593e72f30efcb15650a27e2e891f82f985b19d62d582dabcfb7308b93d74f85837460392fb502ba2677851c05e85a1b457a13bec9c5e6107f75db0c3909fa5aa84e157d2178083434ffbe9b3e49127148717c899f795f844b5627f841670cbcb6e4ca402a0ecb4b321dd00a3776eb15f301b3fbaf79a82f0be3bcca35a1ad400dd81631606ed403dbb993211821b0345b7bd45297728ef13b4ba85aa5caf627804a390547bbb6f5a344b9146bce8554d62824fd1071c8e61b302b2a82cfe7b064d735cfd3a634c598242c71595884460e2a66bc87f9dacb784742c6ecec5a2535985d25f0954d4a3aae97f4f14f7e78806604f5fdf192c2f07898336d7c667fed51fa51096ea5632a867379a12506109dc885f7a5b26bdc72d2efd3d937537f81f6094d48c80160cb1270c2de3bb9ca61c0341c7bebef7f7e9f77df9debfbfe7f93ffa167f5edfa291e31625b64cdbe9a195799eed5655ab5fb7e8ce8175d4d0496a8320db9ab6d9ad8dfaa266577b14045673cd7c481de68bd353c64135546553a7ad7ef4d532959e646efa7c1b3508771c39433efe7437e57c19200c7acba8b497654451736f229c8703df53aa359ebbc7443e0e3589acce086dab250a48f11b3cdc5df38c09c9a1f47435e75d85496fb3e05fd1bd76604c0cee61b123bba45543bd9e361b05f88e405a95757cdd9847ca98fb4a0c28a30dbf8cde2e05513ae53c4c3f3e0e886d50f789101d4f82770e31b5124488667e4615edc93b8621caa6bca9732ebc8910d9ab76594367053adb53642b790f94d3ff1e6c4fea5fbca87c1203c63167d048c6afd6a57cc715718869df4114898de0cb50c8572a307dfa73ed172e8411fc11f24828a90906f7d125a60fb62b84d47dab95169a020458665c5c5bf1c23a1deb9eb4c1d2e228f583a49948f7b33414b2b6edc28541cff1b957036f83764393afc1405cce9146834bd377b44ee39a601a673a08e0ee24e4bf8060b028221735b97c2134571b129603d53abe89710a63c986a276578fcdf68eb681eaada0348eae765578ba87960f4a2bfd9bde464a13355de0983486c4391d4016ad72ef794513bcf7e91a7483b0d164eee273140f50759c7b0b1a17ce0b465a4b03349df6184274688067287067d08ad0edd3047d3f0602fb05cc54cd64d7ae15ddb4592f6a41e2c60758da3c8b4fa0f44904535e79ea491144e2b76f7a7b62b9e9f723539c719a2ecc5364191d41b819cd9a4fd8108bce85d01a2d11ed986b4f7b59e353180ad9c22d4abb9ed378251560012e2449667366723b005c9c1c39f824c770ce370e7d61a43fa3bb35ac3aaf0f31681704584b30876cadb705eeaadff54b8d36777a8fd2fffc247180f235dace2ec876cd7916088efebf663632f9b083ac7e4a721031b9352b639c1e48f2254a188133291976e4a5cd016ddb059932ae53b91d88d96371fd66f66bbaf576680f1438cc469f1c49793495f22c0085acea56d8c8555a73c5777dc705c92fd75127a4508120043b14233e1c4b5bca037a819fa3f1f41d21eb1cb4e707d97c3661d5d91c0d2396837b42bf5d4126849c5b94bb799b192f4268ceb9749ffba9a7b3b526349c904b0b4d854411a76d08f5566e0a6f5447430bb78256ee81c5f7cfd6c63955fb46a6a063a088e857a729fce5b53a469a03829386eaac64a41be350c1b4ca956ef5b7b7123bcc1cba7bcbc08c5058f46838c78329ded70a8ca377f5be6f1e11558bc29f6a2d203d2545f0807fa043872669d1c073b0e33e77938fd989d4e506f207a08073605c517eb3e46fa8724695efba30fc54e27fb2b8b0e11678db33307dd9732e3faf193fff094b284704328d62df1c0ebffe62ab3a145410743ea79b3423803a59c752087fba8ff3f29219465ff5f0356adea7ff7c8c000196ca5ef116ede232c1f6f3f58ced565cb05cff13d79a47adaa79548f3163685f7a349f1ee01a2088098d0f8c70aa6b7927580ec084e854859bbd18908d9393289f32390b60a10f56c0f44524b3edfd6d243becabd9a910eda0c10224ad5d9f5bbd2132a77bdadf9401ace783035639e47b06e027017f45edb641932d8ac9fe4b568c6e79bf8b5a47e15b4b33c5e73db967a4a64d5a1789bf2233a27a34a93213420484fd31618d22458e14f6949e65949fb8843063876128b384c3f03e7235071a225724e7907d3c8072986c439781d37ae574f5f25658ef9667b50b504c5f06d4b2ec5cd1c2189ca55690c5f76496b7c1a850bd4ce7e5265202e1d9c8031d217eaa05f2e33dd464d975290cc35ee3f55ad6565aa51b1a30892b7b56c27c606b92bf533234f2795618a2dd1ab272db33832e6884370c3be9f4cd2ecf805c31df2c56403aba0ec742fe70efeffbfba73e1cfe05872cdd27d260dbe51dab9b26c9df177ddda263715d9c5d4053f7bb2cc6798c40b2bd9d1083fe11224677d1ab8ea30f9ba4990fb9d50149a79e59a84490336b631cdb81e943bb84206bb8ff460a8b9d672c89566058e2b8bff73459055fb281a213321dd811d1692901611bc2eee6662238d95ec4cc22400f7dac4158db4dafa2587c68e14dad4623c7b8424d70ce2cacd5e781f13ff172f0c8bbc5e638e535c3e2ebc023e0804052bc3fbeeb2f9ba77c2c478d7cceeb6bcc048adfdcaeed50a2a2e89312a77805c7cc50ec94a69f7831640ed3e61450c317e1f05ad44e6ccf77edf56b05322920062cfecb1ad5bfb6c32d84c209224568dad75c52d7b34652c62998037ea6d496be76bcc041b292a05882a43b3b3052404ef8106995ae6ede317368ec20cad8d33895d0af48901dd61954767aad7f403b3ba3324cdb18155c6d1e022e6ce267d7fb9f8f5f109026b0b5d7f9968a5ecad52ea044cc57ea207cda936034e5f30b69b59c6f199cb59064aeea5e4206c3c0a3032ab403aa416fd6c961bddca53d5be7d4f8e97510069c5e08d6fb6b808f518c10c1549a7034c290c572e7df2ff7a6210f97bdcfd88c01863de2d3bee125274b927c32f62fdf6cad8b3fe4a4cea00bbd7d3e8612acb65c6c77fe99cc32fa7b9fb0cd8796e20a10df1902b2583afc5cf486f8b66f82ede86717dbb1c303290e31ffb016e8f4b1a9a6e13ef3d269bd9a289ee84e9f661b9105623cac992628d38309cf3baa46a6b342b9323855cd1125e89b626271d64fc1fb681b5eb53dfab0a7bf739a10c9e8a1f46379fc9d77d07d6a4d8f56885117a5d91b7847224727c2e4cbdc75305a9c9f927f2b0659b0a71b364a565c55d80592ab648a32ffd882b04bc0bb8e5955b73875a044783555b658de92425df7ff5d003af0232ada08c4128f971c971e326f4eb3e79b06d258749a0a41a4505537ac3d683474d77a98ca9984e0166f02b322cd00fb2aefa2e822c2dc3cd5cd5dd6489f43ab8961c2f80ad7c86580aaf3130bb87113973a820d35625612f7773d3bef2268b5b9d37427e0deccd73207bf85b4c347324a8df91208ed6ee1b22ff31e74264bfef89a9652db55b9f35ba9979a5c72c7dd28321b9441e1e8a72f727c0bb68e8acecb5e729a6750271ee7535c50a8d21ca1a301f55ac755f7447802cc28befcd76440134058d9a66506b2184f4445114debb21b5608daa9ce068df200048b86514fd03c956002ece60fe85efc3c38e3570164ee171f48926afc35f454c5b62709f3f81e92edcb5a898168221e31b8b14808aea8a2479f5ab8d1e32d6e3ccee9f544e303466349faa07fc777b5448401fec4d81e4d211e061607836bf72d35b5bec887030a168ca54cd781118eeafa5eb7e9da2924316f46362fa197e6db49e843a20e46b36738248010a5807d1d68421f4150b0b6dec93a14c2c807c4a9979d3bce98c1f71936123051ecf6dd3137deb48816ae2efe68fdfa18b4b2ccedd5d998d57213cab25727a9917f095ddd3eceb7a149c77611cd66c16ac215bdf6349aad3e63a09af7e1197784193d85ad9c91e6e3d165f41e9fac85c91ffbecf3cb376caeb18899a7e92407d5ad0dddc4e5213bbc80d6c3474c7946e18a2c31b6ad299d4782d20830b6452f1c33f7f7cd4f9fd8ae9b564863aacd32fc8e837496e80524746e0438f04ec68a19aa5708759e9541cb4a9ac01d258b9f21cdce9ae1ac49b8292c259323f759e36992d7334e4e61ef7aafb04c36b2e8417545b2e8932cdcb2da87eaa5033499091517afd550706d921e1d60359f626b44312c376753db58538502b6b9957d1de05b6a8f937a5d53f86c91697ed65ac8023b7eee277746dac7f18f9bb70d2ab8bf22db7e44187a7ae51fab162192ad78e01c19be3b00c3f06b2a48d34c5bfcc3fee6ed2a1532b85fa01e2c7f12bedab43175c286ad486e5055c188ed0c0af1ea76d8f52b4e6f2f653ea14fa6971332d55191d317b284547d08f38bb6018d590c3460ecb56706b82c24dc22e200f3f51a427722a727fc8f88e7e7a24eff776b82e60b7d2a0209d9d026ca2ef6fc41133d531a5d579b2783067eaa2bff9132615ec835112e4c93999cf079b8e683e3460528835bcc4488ff89edb056891328b68c1edd04b8d862875b03474533b6411a13b17ebd9be280bdcbe2f6457434907aee90ac33b56a0fe65152cbd4a893f9490905fba75502ca195d5edc0aa4c8baff7e172dbe5c05e651baefe9426fc48237b3144a4a05fb71f9a4b40e7cd1d5ab78937e477340dc668e295960bfbdd2ac09b9ce49390326e408bac4f2b104d8ab1bd439941c61d1fb785709be9205344e43357ea1a05c8d009edf4f3904d31af8cda27b67e2a70a3dc1e098b705571fea38802da056b4b10cb94c9f48690e9016f0bccfc70664395b39f799e1b26ab75eec93666363f8817b56565bf6c32e891ed8a8b83158c7f25442cd7a104d9bfd6197accc677f66c86a8890fbc9347413c46086d79f035d57b2a2beb6dd906a88730e4da9c6bc94639691ca4aa9e88056cb54f0e48c977aa39bbd898e51ed9fce80ed8581f31cd410b8fe34142955cb5086ff204543edc7eebd28a90f670e3c6adc7c0de995d4f2e13219435053dc70fe41fa06dfbfe978112886c133a5e243b094a6161f500c297291e5cb436fb7004e8a7c4059c1d2b5c285fffca122db6ae2ede9055979ffd0dc0614cec56146dd8413938948c73eac299daea95f920b2d3a9e2cb083ccb61962144037e8f1626b1aeb43ba1b540735b2f40d5320e01cb27db793236800dad04bdc8aaf2d7c490a37a6ecb12e168f5bd471c93b1a1195983803429d582853b9aa4e8a488489ecf475c98426852e79dad307d2450f40cba5635126dbe09bfbc0c1694615b734be17223bdd438e471cc83569d98f4f505193164f6612aceffd0eb0aaacf39b3186385f3dc65b62cfe2908280e255b0e1261043edd5956b0be299590d8308329df33901b6fea44a0aca593eb164179ce6bf704133c222a312897f786cbbd16418f833960f1188eb26353ab67aecc765880c74c85dc1189502f41fe7bf4dc7f5618edfdb3c502a48688081198805838fd207b128833b0d6938caa19f078e27d8164b20b7aabe63ef3f3c3669c37beabce0a3e62638241ce72d72edc2e09e7592f6278521b7ed1a06650b561f9618817116f9f7540932facf1ba62544e1bd9e6f80246d260ea61ffdb25fc0ecba1407ecd413a0a81f456724a86e1e284e10adfd6f84146ad040039f0b4f738c340f7512a8c54bf1ac621c1735c768b646f61c20584a329027d32adfc9e9454f6dcd7b0643f8e084b43682dd8be106956b0405983486704a07980480de663d32b435ad8591a56b2518c26233731a311ca18322032b2077b0b72f10d26e7003b179d7eb2ca74430cba3fb848f5281a72e10af4258f03f05880be688216d0f5596b92e0a7a12b4f7306545eb16bbd86286010a455d87cf85a67a4d0c64bff5f5889b5c2588e6154000986f6031d69b7cda673810d87a5f12906743b5c8d3810bafc5661201df1c0d89520169efa16de28acc112c6a28c5716a4454bda9d4f50643edaa94cb7d95a1baf091727456814e77f2b270099fe9d10fa49ce7e870747761660ec0ccb27ddd1f1854c6eaaf5c95c6d9d983fcaba857f23a02f53e9cec13eebd89bfda189688e1b922ac9f172b94dd5d6a3519d5482ffd6aefbfc2196ee81c1da25e9eea082e67e43ef2137fcb389817041e791f4328870d8421d1b885225820a48ea8745cb8e82fac700743928ca9e7d9b461ccf7b2c43b0b60c46d3d3a61df0a7683283b6dc9de4cbff49c5aad0dd06135fa4067a5226d7fead970adf43b8593580d0c2a37450e35f757b9a30f488028b78694d936fbc5ab5e37d0107878b20a2f6756204b14c00a9b0635a0e1241970e4a27802c8a216715b5e3e95da6a6100f9ed0f730c166f2b9cf4e6bae2df36e9ce6938086fbf786ae78a9c5d6e06c3bd4f4b73c8532cd1394a386090c2f167afc85b07aab4d2eb388e6b56d84af9028b69c5a139745b6c5f1aff225ebd4c1d8ed74185eee5af42735965e3b41d3a61a0cc8cc7bdafe173e655e417af33ac64e29d97e2fa79512be3d34a3d8de26e86fcac12e20e441a08f475a613919fa8496f1f0b88ec4f6f4591ed8f87bc67c85a7322a22d1a5c4ad50f7ca75077eea2ee10368a7996b7375b813675f8cab25d37b38c1708325138581c2a4bbbfed032696505e1522872469cfe517587c501c608bf855c2a2f6c38279eb3f35e02391c7e562577b663f7b0c2955b46c1b85a45f86315a13819c2527e398bb9aa65cc1cf1a5b0374c113088526acd8c1522d98daa4abc474436af132fd276adfad9e26cdaf141bfaeb6c9dbb160db7c96fcd1aba7c0f6e422bc22f7884040a106b81af6ec57a4a088076b896668d1ab03ddfa1ab2b3d5d1aaa9cb9015dd476b1bc7019b941b753da04ad604225d457bf0c850be48952b3ea5847b081b772c33636f8d0badd7b050414acbbd65a5a9738dd216060842e2a8488739ee176086c2f76d1da585ef9587cd2d010dbdd83f313e7858af173e30354a5cdafaaa51973b052be0c30d90b83bd87cf8bb2eff1b38f2439703ea35a8044ea6cfbe52e5ff6d07674ad25ffebcf58714d2ecc813e55903b07af153e7b7ae79f451640f960359f1141c3845f9ccd4cb22e993290acb16d186eb4a13dab848633b4975e3bc71d03264941248a47f69b43e66de57221f5b3f3bf77fd32055ca628f087998b21daf730b6367638b97acf1fcad607a26db09581c515af84af56eb9795ba4b264e7cd5d894a6f144644dbf1a6c043f94d0897f4938021b08f49b75487a7b4079bcb9c775f964264443962015773caa5c229f43ab5f9daed829126ce7d097cd73b85960891f8eecda0e08b11818e76254a23acdd98794bd5f40e308cab7b64afcef6a862ec26706b30b267118c6d8c6dee0cb8ea76c786b4194aaf19e7a9de070d1f4fb9a2d0dc18e5affada516d41e40be9f81bca15cdc7d5d84fcaa2831ac8ef93aa0627e915a4ebe0133988c486aaee5d91e49f1abc857eed1304024815836bf5eb7f030ea7774acc14e888ac233468cbd07a7429cf25f22bd1dad113dccfe9703a361a0b2013afaf923dff97d09c65d675e76e0fea1c276272f102b009c100ec2a97caea6d2e98f60a85db9822d10998b79705426370ba59f732a60a5f2d168745dcd338a7a2172fadafe248c2de7b15130847ccfbe143f958047a0400083fc4dad2699383f3e6c5fa7796d007ab382664f8801d2a94115cf4cb25a374a46fe3fd418ec7d05803499d886d7f1c815852ffa1a6b352f40125e2a0229bd1448a6606c859c51a4a0cd272d0d975850e65674dfd5ab1ed4c79ea3384e13805b3010593c1b56ed06b31f074901c2ba897afc5730a8dbcd907f4614ce9f52c883b40289345c0058f0a055c93076d8a2e0f6b2e5375d74fe60f358e9e0de4f094b0f3570abe03f4335ff67353872157acd099c687897378f3541113a6d2a262bab5faf6c03028cdc27f7b54f5fc37cabd6a8a379690ee8b905733734e3c78884b7818d4d2dba36b6f4ae465e5c237d196b10db31e20b9a1e9eae66e91262b96b3fcdf2d7754852522d81b5b6310acb53e3d006048bf23ca11a3e85de291fa1170cbfb2d8542087c132e2a48e6c610574c26d2af72d230fe638fe0c273114715c91a0ccc13bd884d7ed052a9b229f9dba582a80a93f160029c62976083519ca7ae4360124bbda9830d620b81a128f78edb4e780c0027c42864f826b09a51dad0b227a6edbff257da1c187847c5660282c3aab235cddf68cde409aa0fe8bb25d6175b992e104509bdc8c5445f2ffc777464521e81ca5c8aaebbd138cc4ed2c78fb1ca47cd39ea4626a2311885efd59f0bc7a2f180977061e0d8c6dd6dd392e5ea5630a0b2a9300c48b8f7005c489f710f520a57fe6497e5ad826e5162476712521d9104501d841c24c9c8a0eb5c00ea381fc1e3eab973727547a0e86c23cbbecccf62813b62b894e4f9176eba4962b85e8adaae8b64d2b821bab8fb15da7ab68df55e53093f04b8d52483ad28df2b9fe65980011f3732dbc860ac48da736937f8c9bc518c72c4a424a1d04d92ad9994dfd8c587ee9844b47cecea28804f150c5c2c37d0b2ea9d1528ec81a7a10233eb4d5432747d0c33ec4e3826503b6ac944390245d2c86e26a6cc7a9cf82c1f1211c3de0fca7c4bd7b6d9dee443ae1654b5011d2ab712f57735fb28f979e36dc0caef1ccbfb37f247ec5000c6a179b34f8a4192d5226d5c2929eb885160cb1dd462b7c2598673893c2458be282802b69a5334f3de64c2e521eeb8f11ce491670618434af65135e90f03af2d776711628ba3af05cc17582fe35ca6d54020fe60236a096e6cb1ffeebe1d30d404485447fc1d1046b0ba7a5e3bbf50568683f88e88fcc1707a7ad70ee6f4ae7d743a705995f815d48e891771fa647ed8241c3b64ddf008e6c32c41d67283a33c786d925e4d8a30fd0e51852f7d66990e19a702ddd120442655268405d6e1d24ccbb0a55d75b7d7e6b8717c517238c136880de0a24d49b0c44878e6502fa6b7d51363a38236aa8c8bda740f03699dde1408ddf6b013a56cbba9b7a9d054540971962093c0f8f194727a0bf8c4702b70e755bd1727260cadfec22a9b72a284efb31142e44886eb471ea1640c5d1e65af5b7ee343a09b2b68d1cdf92ecbbd4f4ca2895807d18d689bf1e7b0d5143b3a92a0931d98507e00bd127d5180d4796e6bd36c0249c5584816e38309fd3bb64d868f2eb2e9936706c9f8e8e20857ab53c57cf083b9833e8a94126308298b68d6c5aa9812fe7b9c7e3219804c4017fa7ae66649222b296164494d168397a47686743ae0627c8d8a978e2cb6c3fafef5f40126a38617e37a7590b0c68665048fb6abf862e277e9629fc64aad131af848ade0b93475b94c190a6dc3f0c29fcbb2581109b841432f3f3d59beced20c8f808c931c5205e5c17c3990eff2d4c9b7974b0c496714cc79e9611618772f9b22a4d9838e9131efdbe19716dceb1ff34f650f4c1a081e096ea0f50dba876ec0c0d6d2ccc64f16263ea8c4deb008280240470b27e3663fc810a3a7cbe3ed4b1b9c0cb589acc366ef963d3c388b747647bb59bbae85771dab6bb2e1f655b9cfc57246771b35d91edb76d7c4ac218565ab2cb2585a044b9b52b14bae8a7eec57de3916ac125f8bef1f6c138562394b24a677f493d280c4ca03124aca64d0198842a65aec0e07b229aa1163a289700a1060539986241d356f391f52500d3d2e9fb27dafb9a41b11c09a84d62abdf719bb642c5f3a82a3ab85c32da3e466afb994b30e68affc10cc91cb5175893f1972447030b1bb25046d218f0b62b538c9470dea1cb6f0a9b3f90426561e4732eebd0099470ae10674d4ccbe051e47c801d36e1a47cbceabfe0997530c9c015d206239cca84f24354612e29c3509471b36c5ed10775f24d0a83ca8062c23d1557ae3e958e774e91bb9617cb66086e7215dc25c9da4a18757b75892a43dfd10854523a12b8af2ae76cbbd1cd646198075902d769d6d21b2a846bf05832e43f13153cc9d80f40c8af3bb4e290a6791db9411767e4f22bcc098c747b6bca2dbc16bfb0a86509ae084db896439ebc88958b11d527ec97c3255dbbea4428000dc94515b332ad9e22a923c9e24d09496a2fb83c376ede3cc04c974175f0aae49a7aea695040dc20e5ac08175d4937922be4785368af019caeb6e791a69a1b85fac083245955a92799e103dad5472f60c8f39548edc5d31322f3fd5631b4fdf862af2d48e1b22b0608f8488f0b640af1364cfc13c2ef9bfc3c558e053c83c7064747b3ad76a47997865939cb7ebe2db5326c5b5c3b313e0f9e6ec8530a72d903e5455327f69af50a47f25aabbe62c7650c1c397dd53faba1d4f3e69c57b7acf14a0352e14b2d2d2bbdd8a5a848cc4fff6df3f53dde8f30168e4bbe47d0e1e6ad13b53731ac8c3e1b6b1f3a5da19ed270e7948200f57c3a6435eab5b6a605057147c9534ac6e6d5615e609dca0bcd6edf103d585ce63d2c6f622c4454d0dbbb184260fdd2b6e4d77d872fe66ce7ca5abdb345bdcf7bebd695908c818bd7953663a6e7babb74c1b4a19ac3f0a655048bb441c55345d9984cd6dc31f9d538e464008c14bd5583caef3b68e2ae5acd58348a385356779a703db3c776c54b0df14679003e285fd6ac682e52fcdd301d809758c763ceb10d37ac8ae4dd4cffcc11746f55dd7fbe8872542d0e6d0c76324f24640c0f5bad431ed40071424f90c5398b3821172846b4f780a796b331497474cf2eb2393f13b1ce5c07fe7ba9ae25eecd6629f2987e3a3ddbea2b5be3a31c9fa605a3b0e6f2b711d93290a12c82daf0ffd359579d97ff4a5ce4fe008a4a8332c2f547e05b225b1498ab5ce48a70d48e39e173b9f4f021005527e3f3e70353028bf9aaf2c2347e2c462c3cc29381f83898717413a4b47de00a4982a1ebbc222b265a5ab1ee7f830a2849d7b9603ff7e73be7af7ede2a4838d9ad489aeeac9db7b308b73f98838d3560cf11c34456aabacd8faa7b97df706c4e3fe2ab609b91804b214ea376355488a3b5c2c628144ded441542585d5d66d4efe4fd76a403469b9ec5057e0791769204756a65442f4f4f17d2e09b90fac02a1d6ccf78b9f8b373b7bb196a0717abb352b671fe5f7511cbfff797036ea89f458dacaaf8c3d76e34dac0dff671db28850c3b20e5e5d19d3d3d4e7bf8765b7253a278aedba404ab63397639d1988700ffce6d884343f040bba438ddef97c365b3dabedb4f1b624f64448a2e686f09d0d8a519952a4daec431ae111b618a93bd50cc0b69e4271376e74363dd28a2f5f63d0f624b0cf8cc2620ff5385120b5f87a73f1f31adf80871e9d05f71439ebf669b47f02bdd8dae18f82992c893d0c23cb2e9394df6f395f90c8d010b0845cee9761610b70f82e9bbdd6032694b353e257ab3744622305d41c4da8c0c99afea014eade6b02300b2120a22c01af57d209c1c0ca3614e15509ae6eb376c486d2f79f94efac85a9cb6f659f17aa3b9927603800168442bebbef0a7ffad1590abb4b97643b1fa1aba078fe7394652ed84d122006c493acfe465ea357798876d451f22eb4334139874cde26d9b878184018b34db22a95c9250f5404b6c063d149b6bb5ad43044e71715a0a9136ae3b5a81bf0e5df54dc83dc850d5aee2f35d509f9c734a85693b410754611bdc950ef66a25e210366abf5f71cd64ab4e15c7c4f4aa2671730cb554a9192a1e9a43f647862e424be5740ad1f769c1a1b92517a20475b398a1d48a6916f68629a9b061213cf638bec49cf6be34860e0e61f730f83ff3320d5be8b9d30e5214c730630cea0a5323e81216859256ae60c8bc7baa32b0bdb1559890a5360e64a7dba69643ca1ae41bbdb2de0952d1d365aede69e8c4881b19d99306010c21d72633d8e83f63c4516330aa3c0a8cc93f979b62f9c00250a0cf7d485217f9ebb31d1505f951a60cb428c0a141835afde075c359454d0426d741bac119fc09083980a5404d5d0fee3cd0c894a3a2443710389d099264097b79d6455ff7769360f3781b7131a833c2c97ee45617baaa6652bba6f68dd682a2bdd8f61f00b318e24d6b2655f6f010ab28d69dc5b11fcfc06624d4fe109d085e4231808ce7e36de0b9128873ee6bfda09fd32d10536fa944a05188a58bf4009748ce36472f84dfc73875b978ba8dafd9ce1c3f7abb4b189a35d507ad856dfa3236ae6d14918d3171b68d11d0f8b89136084be758a05d34f96b0ed12720968c54e8143a02e7dc761cad6305453e1f164b37741598bbcb935a50dad314d80f1e592c0321dff7cafa248b2de0326a4eda68485e4b773d4b3fad370c41955ca72536597070c3b04e845bdd5d16a48bd4e1a69a3aad50e6251156000f1d0472c7f4c891eec40dc34e78c4ab19f91dcd8279a3e69bc8a64bc48ad0985a9c275bc199f4c0be16d3b7c508a82bca3ca5dc4c818a772fd90b0d82b8177a1f91ddb9bc3f3646d75bd87da10323f68f74a56dbdcce1d087b3dc60f81b215d4d68947314e2e5f8fecc00db5f6325ad2eb1a8bd4bc91cc71e1dc5c622be6faeddb6000feab8bebd78a0da70311be0c9fda9737e0fb685c3f874d631e57bbf9764e3f3dfb7ca30edf56558682edf6d062e17099313d9e63558dde311aef416366459d821231dfce41b443ded581f7b0ed296978da8af55b84b9fada5035e7a9acb916ead322091255685f90bd54573d604115fee2977fc54849881bed73605980108b6302c831990ce8a1e7c919ac20170031f0457a061bd70a0270a6493f3907e356fdf97ebc28d2460893a3a3843ff46e8970687df34ffc3c00b290a59930a01a85eebbf21d821b348c793a1ad4bac01f025b6d1fab1e310fc86a99e1b1988a946cc898b96db98855bf1c835614b2877e7cb5ad932eba4785b67c9c5b446f127f91faf4a24eb15ca477f572831ec5f671dbe502d28bd4ca64f40ef8614c5d180605647fce468239bd32b094a7768e4cb85e95cdfd2fb2a384e51417b0b6285e42d595a62481213fa1a1410b411a99fdada415d213b12030702b7f4d98ad8657048ce5eeab092bf1186221c09cd9f71246b1480e83c444ca0ff66226558fdb24087ca939f779a459048d96b104129df4e47d838636afa0fa85265f7a96d59aefbb4a6d93f889ae5f9f542432cd9850c5cf9a59ca07ac19740edb682cea72a8e879e841ba51041099d6fb029f8bf5f362dc04a3083a296c0d88a7cafd29880a30461fed6b952577f91ab53964feeca5b7b9503f824e35d5800a920ae51d731a02ef38a948147cb1e98a4469cdb202ad437c16a5f1a5dce13d8d946b37f1a034607d761b605f1320c90e665af441628f46f334a335c247ffeccb33b8ca8be536910c2e154bb3dca0f56727f1acd471fbe9c08459cad7f8b5a2f6b5160419e79925e21559a060b643eb60344ec748f7c72ce7a2c30e1606e34dc1eaef7976c874491a32dabe3cbbfafba3a755e7ed10158d13f9905890f33677f1ccb5eaaab0370063e9f2524b7a528828d2a1608e0e1f004e57879845c0db205cf1de762ed65a24d8567a4a899cfe761e2ac5f3d75d993d3fee0e06461af1055be87da6bd62556e90e2278926d05b4027093fc5784866eeccaa3c43d5de37cc860273f9ce1e75044626e3a1352da3c65b53fde4521cbf8d75fa81da3b14ba0ef65bd2492b545d4e2289ba118e0742cea84380ee30fe18e4a5e756eda14b58b2397e6035cc6ea2b70c89fac17d1f521a934627d60e3f94bdd939aecc5668d2215993a6834a2a755e14b023ba0614b367a227d06a5bb92ec5837590a0beb73b08cde923479fea6c23f70fd60d6ba5a639cc175ca5b5f9b1368aa6ec31eca3d508a5e618d0192b90431c1ad16e41ad0c9a37196c7a5b05c6e756488ad2e71e519dc362b03ed0efd50fa613eea23eddcd90280622c1441ea541d64791625c3e41435fbb39832aa540664affb34acb1f489532b767543c14a85594bda9987851bb5b1ab31af8960e6e630283daf57e362a3cff8a8d004b73264f0c8ab6d0117f57a7093c5f6c2424d3435c06967158d4d9ef7519db66ae3e0b2f2fc3a87e6ca58ab2bcfe7d8bd9b538ec24dc61387cb53a88cd951796ac4f05ca8b8fb892faf428c8470850f2915779a574ecf99756e4f0b80a5b636fe4e152bb7fbdb06871a5e7cbb82711923b79768e93cc93016ecf90f71894a3a180f856e6970a239c9e337524e528c5c36ae403fc6e6befa171abd163a17eb3dd6726058a8856bcc568655658b04c7be2f2e3c2a76197cddd8127318a86e48f56cf6eb1d6150c5e2e560d30b7109bb5b633bb34a9b048729f120443781c08a2d87f47a4a11684bb2fb3be4391eb19c35251146638d7844ede1715309d00357a562707746a189f41b5f998effb29b320a91614da1d81cda3b3fcb7b20468a1888638a7690d0477cd067c50acd3076b382fb000dd3a65fa78cd3c78654d966a2c50189b8f4a9136b2accb150460c3ce1587e1f24a20533b5724418d1c8fb3c58a541a1eb3442fdba380e9f7ac5d868b865fec5eaea2044f2f489d58cf48672d9269c2f190e4a46c1f7de86ea226c9d267839d9ad854457c679cfa93e77402302f0b691bc4b3635277c314b707bf4164eea09338683aad35dad7bfa6bb716912731910ed8d66e0a81444d0b5674a01151496d4f5ff64b767a6ee3ad9a4fc5beb4db7e62d8f7c1ccf4f45ae3f4036e2ecc4d3756b823d92574841f20ac5c48bcf2fc40803116978818dbfe42042802aad30970b0034f14e6dc6836c37959de804f80cb40f7fabe871fa2cc1486d2c9f4dff806d33473051c7496ffcf816150b7b92327f772041028da2c281a23505425c5153f0b91873636e346eb40d77a93df8d74ecfc04c20015e7f72fee6fcb1b14ab0a9f76da10354d3a41fa67d2af427ac9fcedf4293d64fc9d76751eb07e4b07b74d145a7d6af71de5ec996ae56a5bdbc005c321c1bd3087c03c365965318cfb564628a62a5cb28efe8ae72d5c5949b51f4fae2ee67720cd30d6f3eeb17e18cf1c4f664035cad33bdec8fae493511f448b8a6cb1ec82e2fd3a6406442fd502b3e9201131ae83ed12e5f17e9df9641f3566c75e0504628ed8827a7526e06a219ac8a3055d37936973eee7fbd618c478a44e5ad9a731ff60d55186ff07fb5500a043d7e2daa124a40340a5729fbbe44fe6310d2e9d8df306db152099e0e8e55678adea9a7dabd98c0ac5f09b24cc5558b8019a41ffb44fc5305e6cfa5db0cecc0ee0674febe34de4277bd49974b6fb50bebcc21f1bf55e74d90e70179fdf62e91b29c321d3fae2bfc8d4684a657d41119a9aee5b964e587afedaa751d9caabb44c1a2d02178343454e76d47b3140cd355b8344ab95bef33973a7caf7b170e80eedb33700d84f40a84a4d8862d5dd52698c1eeb1080626789bc7bb959f65073f4416eeaae6e88d37b1b1e186f85858fa541a22b80b9f0dc2d958d0768953ae4d5beca55f9a36dc5ce122e04b03ab07a5334cbd63bb7608eaeed360e627cabbe8718f537aefee6304712e874c098e594b0a7f31ca5cb1f552a91ecf2995569ca5f149e8cb68ade5ddb33d9f8e4e514b8a17b9e4b4060c199c7e6ac2870ff43b9629f422caedd55a959c6e698afcd1d125ba61f600148beb5c890b304517131a991aeb2162b7f3561cb750441cc01a016afe0821af723bc64e3995fe25603c0b34234b9614e481fef7be471bc9cd3862b2a4111cd173a648a4fdec6decb7c30fbce46d788645ec305e765fec04ccd8d040d0dc7c5d72b64f3fa42bf7718f08ae1d4e84fdfb93cdc3308540fc52c25bff21929a22300c0ad3790443796143f13b1066a385db5c7ccd2d5657a2daac5e02ba4c6592f0a354947875675ed95cbaaf0ff956480ac42bdae2f20efd884762c52a48ff3c81dda0637d93abec67d4965cbda7e6d63cadde194d510de0533d20e4c4c6d1a59480469da1903ef7b1e3900a2244c4200d49ee13ce865489f617b25b9b17babe5a8901a020f3bc7be9813c96fe8596cdd8bc7bc305ef8e11ff09269931b4864ec24da535c83dcecd5ae8de37aeeef13e823d2a4dfd9ea697542bef54efbd7b5c34c84db372e1c42f5d30f776b6100fd571fb2e1fe0c7415c0a27ece4add67df38a70350d30121d341a6f740b070c5b229188dae0ce7d2ab4c1baf8904ef10d9e6908176893f527c11414ca28e22c90c10723da6434908d2c7302849a6b2b48c4a34505890c815020985664431e59244e4dff4c001d96225a8366656e2a6d10ae134cbe2bca6f501b42437c1d57ee696925530977db1c7b90f0675ab943d0b3b28d3cc2c842f2545d8b6d7850491fd43b307674fffb43e4d0f07a490848bea2e10d698d38de320b43c8661641790756096ce967d6410a181711437cc89c8075add3e0d94b3764a8ca02463a0bf6d9e83a5e9e65de1b4390328bb7d51fc8517fa061fa1992ccc329152a1725cbbfd96846a05fda044793d0fff96b6816d1307f8928393058b3a530cad199db78cc17b73db21b3a4013f4397cc0b0716b31812010490c92ac28ec79918360d60b49615036497cc9a2921320529e10b51ba8884d8b7888396efa3b658cb6eb814427ed311362747213e11f36180891a7e16e089a0f392d67cd05990b3ef6948c106e91ebc996199cd468cf267328ad7147b6ce1647c513293785d6cd22dcc66f1a4ac20a28f255a4e3c225436dd11e405df15407dac00d6ab791c9f637813c726db3b2fbeed8b282318ae02a0847480b4263383bb6d44b6f6ee2f6cabdbc4ccbaac776aa4db887150b4a82a85067059994277f3db6f72cc1bad78f0197ceaf58e03b324d6fa5c17d46eff68c02137c7806207bf4f2d66dc175a405555bcc477b044edd1d9d6b70f8500e410c7614923c9244e68d366c83dba6ab481b6ba20405851adb1100929303c88aa7fea2ddec6047cd293822a9dea9b8a0290265e3a30fe8fee38956f3c0a1aa0ca970d3d711ff780f352015b3a4b98401dd9cca38622dbfa43584c50885ad8dd4d0854338f80cd19a8f27badc75f45da1a27b0e072b7c7b6e538892b59286c8308d6baa5b56c4ed823dad05b7a425e6d59fa1308d30a0c306d550b8b2c0a4a04590813272b18fecd3a4b8dae0bcc9eccc12338e12d9da96026046addab6f7443f5c451a45401392f0dbd042ed3c0fece32ee249070f808a2495cabafc68d80dda42cf632f77d65741894df2fc4339e41764546d18223f2dd363100abc492a3b7db457112c631df2fe9c64f891cd8578b6ed5bd25732216e082d84e16469e60367d4e75869b93ce16e4c0d036dc22073ed880fa0f491bd84d9ec2591160148fd144a2c1da09dd220337463931a4f5a4cf2994e2c1e2e1c59eb5f3c259a918d7604643254d886235b537a28c6ddf0ef3eed3b1176db2ab63a5a9360c4e551952b9f5371640bb1a35702d22e29e12a7c62d9f94e71644d5e64a55ce99a0ac0d8c3071fba4b64e3c882e62915364711362a0c5f19bf82f4bfc185f489924363b9d5e8d47ad6c0d423d855cf3a9b30ed7e183e5fe891e26dfadbfd5ef516cd9da7e4e6b15cf9a2b56c473101ebb051acbd6ed1bb5428d68fd32c3faf845604ecd999cccd80401f4107112d7956ab5a85d6f8ce67bba2703d5cd41dc934b778d40045f71a8b1d5fd2cfe92c4534a7c97837a0dca60bad148d42012efac25c870f3258f04c874fb194dd34f181fd0dfdb2ed207e5cb029dba7cebd7c0841b35b5b5d12019ba999800198035c9af184e83f24665759a06456dbd7d162fcd7d5f297fa18e79c2aeef1650f07fbf6189f4ac9d63e05f6a16d23306d38450abb1e05da1cf78f0a64f65929c39e5c2a0d9d80605ddfb6f29732a9d767fa63e2989f28ad8cb5e93c67abe52a5950acef5c66e2da58507c12861fa1cdd26f1c4b80355d87d50d115650de17a36b54b8d7eb724d0679d28c09d418d90ab3ae2b9a161f8f633fbdc8641af6a4d8211b7860817e112f2e2c778098a1a5d42f4e8ed3b412b71f959922c93ae200462966582be7e80b329470747d27764f0fdcf57736649b56257969f78462ac24c3ebf6e1a962501f9f1072ebc9426425b00e038c667c84fd70cf9245b21b674d78b5f8dff999debf37172efd767dc98204df0de805759567ad8c3242017c5faf85d86bd27ffe3a17b35938c38a70dd02afc3bb0d8e75b15be4c98613ac9bb9ef4cc41c437586cbc680d852e5b72f816813d803b63b41bbb5b0e5c6b2c81509be80b0a70f3a19620ec8435c686ce56c6c43d937caf6ca33f0429ab1ace94fbf55a5a6998cd849835c7cf2002c2e5a0fb6fbbe9d46de5baa8de0ff0389d5f428d0bc1dd272b2d89f9b794680028d32f4257f75f13b54a1bba2dc498375400a5e12f5d340cb92053f6c490bc8077cb10d0e72648fdd6e54c85b467846443275062d63574a5cd913b2fa1704dfcc04ac669bd49c1764db40a76abeb464105e79bbae8fe1328409683e5d8afa719a499abc0f60ede3104f5da2d39082b4c20d9b9c7f89978cb1906ae95a28b6f8934aceae9fc0b1ad479170f7fa090e6bf9744876bf52711aa7731b878cfdec4a7afa87f28e306ec9ec4cd27d8b487c2479adb66cc24459e953e4c8c0e60adf721452b2d9bb80e81889b2aaafddfa80d31922fedef8f7976856055e3f1d5e8aba56e0e5d961dfc76ac433d2b76b28667053e4f0e647144d8d288fbaf54d8ecd353b55db88a6eaa6ba44066013aca9a3dfb2e3fd2f18c4dc71d86a18626164178644200ae2484b856370d679b38d965acc8428eeae5fef92680da0f1dffd128d85536a1d35e2328544657e569f91fa90e137fabae56715dc0eb03d8882bbe2be881efc6d4702ed5e17c09a51d7e5cb00cb159d2797f151bf13243df42f6cf322b4527944acc7f1f1bd3ee9854877b23b73b5c8312440aedde935016015a730a03a71cf8f2afe894be2d7dea691740731519fcf2884dcadc406f610ad4bd88721dc4e1f0bf0637b5990cbe8951e91b4c3c978f43715bd768f67d476a1dba1b00030bc82005bb751300421554815aa56e90d1afd101959ee864652b081debbcffbc8453f6f94807894a477e93b8cb78bbcd493e0b29955d7d42216960e380484976754824ceb7c36c4a9f98dd0dcd73fbf5ac2cb74bf87e79580c20fe1b391778035888542dd48b3f97f7ef0c304c7105a3cc86b52261d710bc885488535edff2d400a082a8153bb73408b62beecaa154ab8c7136b5082b4ae76e749f08b5db09dc31a38ad1f8a26c1febc8eea4e1356342816981bbe6f92862cb0105937cb507dec670002a3c0666d96ef9dab070e0796a2cd66d9ccb8466549fce3a58879d61a4e45f88af30d998b5ac236c931f7ac9382032c73972c04ff433cd8b77640928e7a476388178f3b4711ff9d10c91101321d7362f845878f3aff532bf66512ddbdcefd1ed2519f6deaa85ffbe13b48f13fda133bfa55eec26acbb0dbb9b62c445d84f0ed556a67f4fe94334968909d47fe21590445d4026d951f4cb506e48b9051c223bb0075909e8e2ac2c6274fe4d734a5f5ad0ddb364f166a4178821e0259eee31d3e3a45a7b117879f36539778de86fdd3d3937d984d12bb60eeb0fd43479edcc9f56afe7e16839b0a01f664447d9071c9b0c5985700cd714b92b51fe9c01340eb7f47d7fd1e1fecc004ae28c40e0d50107855a5e9a06959ba79610ccf35c4354770d3b093031dab61c7b8923270345f2ab43f2f697727e918ed1baf36d181f0402fdd309116b76678828d42755b9c577facb5b5d6da7613111111217bcbbd030f0d560d0f0d5d7b00787f36aa87d8a6e52b19ab700503739ae64133cce76703739aee62043103c6f719330e86e146e4751f76e1a17966efdb8eb868d637ba4c975657c233a78a609ce6ae76b392ac1a9fa12143c6e4596304420142012a552da79f0dcb5b5a8000020820fee27f7979193d6f2c799ee78d075d5c662e611e34bb9ca6b130f2b038f296e8784b76bc2554b464cfb3443a3a3a42f060a43c100e17c4e2ded416f3da6923ea7e3a214da29af44a9cf12122a224493a9e4aa2884a0a74c21139e1849c704160bc3ce5723bdab8e07402f092f3b123e325f7238b5c8b03c209c0b378080f6122dc5349d5c9a121dcd3827b5ec438ebea05f710315edcc3018d3e9c13f010878411071bd1588510a9222052452fa9229754514baa2855c4f9607c41168f713d1bd1490e6e890224f664f195441dd595f8139d2c9eec609d66a52e1efb6816cac5e39d66a55c3cfed1ac938bc7ad66a12e1e036996cac5639e4a1671d1f664f1e50ba32cfe4551b34e17ffa249b34c17cbeb87288b17879a752f5e142f1669723352c59e4a1271f1a24fb3ba8b1789348bbb7851bc9254922c22d98c1cc922111f7f680db0e8962a415709aa24ebe2cb1815219556255180ea4abcea2999a031666654d1182f33ceba5a698db3ae867879555a413c54c8e25582b298e28417882ac8a237c458de95b14c798f19cb0b31962aefa5b18cc943982bf1a5ca1eb2f81e537c268c78164e15d59598352cee73e9e2e35dfc6cef054816fff2a3552f1710ee4482055136cae2bda39a1c356bbc788e8b66e18bef749ae572f19d8f66fde2bb9d66bdb8f8ee47b35e2ebe6b35abe5e23b20cd62b9f88ea759405c7c17a4592b3c2da926ad129f2a02df70cb8dc8a5952aaae48b91ba12b58c02446bcc2c96f47ba9ca5e79215ea672cccb9b3dcfbb5649f2de58ea984339250a77be18a9a45623be18a12c4c8bea4abc8b4fcc85184b2dc7cc98548e71f169b9f09cb42a89e25357e249ab95c50b00972742b278188c42d42af1214681815190b44a7c0c8c92a455e283c02842ad121f008ca2a455e2571865a855e265609425ad120f008cc2a455e25518a5a855e2517c2a89d2a457220a1759fc87e29351b6c8e23d940bcb34cfc321b05c6dcacd0b2c73b24b4b3c8df1a759a58b4fcaa859e5f853a459f60645597caa89f85251464152a200cd6fc688020463e7961b51890294c56f441b9366c9b8f8ada859df8db62d3c23951cc0c573ad4abeec6c4795b4f194cf4624bee47eb2f8720e117148b2388bb8244242bce494c8bcf47c68bcf47ec47b40cd82715149164b2f894be921c9a237b4248b1e11eb6567847cd90165f1253794c50f0057a3ba120f822f954a9e782ae4c5a75562f65ebef864f1f4bb7921f22ec94b5050abc483fff0cb91668934cde305c984f95e809a3573abc489852b82e40ff3981306fc3703a67410bf1c6995f80fbf004d18f11e9e79062e6f2e1d06975a2e8d21b0b97c31f262a455e267462a1d8479f962248b8fc1654c57c2a211345b1be4f9cd286171c66881645b83dc51f0256c6950c2b68a3c3faff41080ef51b30d38f2c8720ef0333e3f25c031c7772dcf384dcbf8fee2d3acf0e25f88bcfc84637933338f2c970e45cda5b1fc461e5996f119231435cb186990568994e37c64f1c3251818eff3bcb1a43c3630fda92bf121a6b906165f121fc3a3a010bf84f825c4e76723c4287ee6cf47160f000c630ff77bf7799fb7f256abcfcf6635ea64916610d33c83432cfe65a7923488f268b7a42f3b59bc4b1150161fe4e253855c7cea108d22f12e419574f1116f44e627c647c5936addba93459f2cbe838137a2562537262ead2c6e4db2f8d285278b57011f4410ef82f882c0a5cfef2ba96e3fd5adea1df53ed528b6b2f8ad8978733896db16593cf83d3c087edf8f2cd21c04a639c43eb2789432dcb21e894ba0202eb182b8840be21231889fb3c601966031678830ea51abc47b78e6d148fc5010915051165fce21128b6761a32c7e14e2113ffe54b25b75253e48c627c688aaa43ca99e3b2b298e3fb9bc279f4ab22efef45349f2e24f469ae55dfc89a89232174b2f27900ce678ea4abc8737a3568927f15674a4c58b447c2f63802f5f66bf5cc9e5c9278bcf3e05740d4b4c1565517caaa85933d5e4ba7820f0a9a755e25ff00948abc4bbe0d34eabc48ff8a4d32af1189f7cb44afcf1e947abc4bfc0a756abc4b7e0134fabc4b3e053107c12d22a712c15d59d2cbb0a0e537018d4aaec291ca2e0f004874942a156652f02874375951dfce9014d1824d2aa6c2797c672025d3f393b0973415a95fd628ea755d9168ee58a068ee5cbfdc632c656b435c19b91026a0d11e36674cbd34694b39f7c9aa58db355449aa5b50a7b11cad325055c0e4c23e008211648b0488285122cb0689a0e89e439615a081d84d0210991e7eb746a26bf99f925460ccd115850f223971389662f564ea6425410654b69466df54a2d7be991ba6f17f89da6af0fed186e1f60dac3612a246cf0da2ac97a2fb16bd8bd4b4fa7d3e974c25e3336f2b899794e9853674b77e60c3d928dd4223fada269224768cb7a16e5f6b339bdb39e874fb806d524556804aa4230fc70b39f8edd7e37194a926c6f91b4ca9e2aaa0bb7a4ad9924f3d020b4e787da3991b0412897b44816b3dffc342bab468a342b3b92ed6b56eb4fed7925d2ac2ac4d6aa53c953afec577662f09c300aa5354e2f290c32e625e10d5d2d27a94a7a0a3ac3e2217b63b760c2d89fdce0d656105a83d25c759ab553b2c187b6ae531dea83eecc99eff6f405b76782753aca4d27a5dbe36816e9f7b6bb45dd622fe751b6d8ef59af5c418d144528d39b8632fd095e92e94bb8883ee4a1ffba275322991a4d2c24d36f3fc84934696b680d9a6d4d8c97956c2dcd70fb20d3d3209406a944283d8059d021739803902b1011c2913c910401482e2750107cdc69c12c6aae2badb2585b65fb00d0c83665ae6c0a24bad526c164d63cc18623b99c4cb2c53e33f64b4f546b96fd029077d26250196b2d900cb6193df80212b8c0032eb8c00a3bcc20c80678f84218c460861b5c4136dbf2c151b1cd33cc200848003b894c96052ddbb0cbd624b00882c5820a2c5e1087263296b276bcc7e6755dd7659fe3ba72e8e68d10211323466573eabeb50f1d5886db75d3eeeeeeee49cfd0cd5a6bd7d6d9b2b17ca7d5d1bbb74ba5b85f07285b1cad64da0db3603bb771db316cdb7adb1ad32d7a45b968af2db5d68273c2d4b18fd0b8650365fbb2878e040d2d69969d01901b7e9024c9f67da459748b56d96b63134fece81ff3c8477dd93a44576f442016373b7886ecf3b3555b6fdd4d9649d3d8a548bcbefacab29fb8e63ad5aaac0c17a5a4e79cfd3aeb50ab32a136603bcd9aaf46adcadec4a859d867ebcc111b6bd1c6825bd6a39cc534eb622f2e23fe0becc565c47fb169b58be8e6cdefc6fab0748b16dcd3895219174d5a26bdcac45984ca629ad5d7d55ddef5d9f4afec7572c1f292faf890ffe4c569fad812d555263211512e7e05973979e5d867b3f2ebba7e33f3358a29cf319ff293e76494a76014dca2828273545ac463b8b4593c4d5dc1b589578b9ad524bb8ad5c32daf169115d2acd92c6cbca6b8592dcad9b393932f9e4d71ac45578ba5d52da4a3a36383338ce10c6838c31ae66aa23c205e5c46fc0e42544920c6174d73b9774ca5f0ffb1c812358f196e6985ae1e1efb9352117f61b5366be5d9ad922669935e655f1969518f434e8efaa9242d12c732a5627f52c69f7cf5f05c3da667a6a7c652331d652cafe9d7d883f2ec3688ed11d22863b86515dac9cf739230ec944f4e7e613b55276fac4dd5c98fbdb93a19f2637c61f02838fcc903726a5e7cc8c79dd7bc500006c4821d2f3e9b13ab83c2a577c2265c6a36f5a87261759a852dd68a8caa502569d1bdd9e91d9b279c4bbe7a5c69938f1665afde8b67d72af967bf323626500be91e7a4c8d9aa45c10112306cd8a14d0352fb2735adcd212e5cc125926d96951ab32a30b28d5308670cdf9eeba2eedd7a5514ac575fd98545c576b528d627af081840e45f244b2061e246bd8e9ba40ab90122419e74905253df6b35abb7d5afa053161b418adb28fb157cf2d830862ce4c7bab751df534ed599689e073c0f0c31571bed77faf07df5ed903c41f2eb5bc795d4eeeb00e2f736353416768df709d423d35dbe7300bb87773729f997b37c77de23a525a396ed44e954847478707356d8f549729c464d593797ec624bdf798a737d2a2bac2c65ec184b13db60e14662b61aeec39dcadbaea29ccd50ac65c764b87d6e81f3a03c85a1ed29ec62d3ba89b34cb96dd44a85965f74fb6efa037f8c8f69dc4b6926cadb576c864619fcf26eb9a2ff2a41b5dc39cd9c63961b4b189bab183eacade66d7dae7e81fadea160d54f517cea6aa1ec375aac2d944d40ed9b27303799eadd2722a29c51457f090d9c6924714198917b96233dbf66920ad2a6ed93b5def54ebd597b4761d8770b71f1224c8951b7d701cc7711cc7515abffaeb436207cba1fdc2d1c6ebc23cfadda0df0d3ab6aa25ff001244488ac85c4d4ab5636f9c434382ea01472667e390eef673808f6eec565d59ed39ba653f1c8dea21d7fa9c0f871ac9759c13a6b9d3234d72b7a7cd936d6b54d3de5064ae6630a73e9802a743b08c5321e0099c0601cf38158225700a041c73fa031c81531fe0009cf660089cf6000be094079889d31de097d320f801a73ac0439ce6003be014079888531edc80d31be0224eadc04b9cda00b34e8160069cd60093a734c00b38ad02cb9cb6b0024e6780699c5281719cca00cf9cfec0374e63806b9cc20027e0f405d8c6e90e56e2d4057800a753e0244e5b800970ea032371ca025c80d31560049caa00d39cea609ce7e0fc14e0238e025c7329f0cd4f800d70136023aec2077810d8e61c0e330a16227343001982678856ab54340b388a532c60129c5e0143718a048fe0d40ab8c76915b0084ea98043700a84799c4e01ef38950206c16914f0074e8f600f9c4201eb387d02ce71ea045c9e1ac11d386d027ee294099803a74bc01b382d823570aa04ecc481c0304e938033708a040c80d31f8c81d32360214e8d802f707a058e714a0437715a040c734a046c81d3216019a73eb802a743f08c5321600a9c0601c79c0ac1133805020ec0e90fb0044e7d800570da832370da03fc72ca030c81d31de0214e8360264e75808938cd017ec0290e7011a73cd801a737c0ac532b70034e6d80c953207889d31a6099531a60069c5681699cb6f0024e6780674ea9c00a389501ae71fa03e3388d01b6710a037ce3f4057800a73b3801a72ec004389d022b71da025c80531f38895316609ad31560244e5580f3a90e46c07370cd5380718e026c804b818ff809f0016e027c7315b67910d888a760d453f8e41c3e1d05aff26c97a1d145a855dd47d5e196ac56e92294fb430c31444f1dc2a75935ab8e4a8065b800d5fe103aa4bb009186d0c96d74c232844eb00c25282c6348a587d5aa6414efb3782a4982f7593d9584e27d964f2547f03eeba7923dde6719a9a408de6701553204efb3822ac9e37d96502577bccf1aaa2408de671155f203efb38a2ae981f7594695d4f13eeba89239de27752a59be4fee54b203ef93ad4a3ef13ec953490ebc4ff6547203ef933e95d4c0fbe44f259d789f3452c90cbc4f02551203ef934195bcc0fba450259b789f1caaa405de27892a5981f7c9a24a52e07dd2a89213789f3caaa404de97d1a96404de97d9a92404de9769559289f765782af980f7657a2ae980f7657c2ad980f7657e2ab9c4fb32462ac980f765802ab980f765822aa980f765842a89e37d99a14ade785f86a89209785fa6a8924abc2f6354c924de9739aa2412efd3d0a92402dea7b153499cf769b42a79c4fb34782a79f33e8d9e4a1af13e0d9f4adabc4fe3a79207789f86914a1ae07d1a4095ac799f465025f3fb34842a49f33e8da14a16e07d1a449524c0fb348a2a3980f7691855d2c6fb348e2a59e3fd199d4acebc3fb353491aefcfb42a29f3fe0c4f25c9f7677a2ac97a7fc6a79245bc3ff3534922de9f3152c9212af972fa145acdeafd99a04a0ae0fd19a14a06e0fd99a14ac6bc3f4354c919efcf145552c6fb33469584797fe6a89231deafa1534921deafb1534900bc5fa3a5d2a32284bba63a17c4398ee39e7d361cfd6ec09d5baa30f72830abc5e261f5b07c583f2c232c2056104b8835c4226215b18c5847a40eb943b6481eb287f4217f4823241019440a9143241159441a9147323a323b322d191e991e191f991f192332403241324232433244324532463247347468ecd068d1e0a1d143c387c60f0d2334806804d110a23144838846110d231a47333a333b33ad199e999e199f999f1923334133423343334433453346334735746aecd468e1212f5fe11dade6659c01aaab1366c95d7b5160951f12601523506015a0116095a01e5845480458652804588588075629da81558c4080558e3e80451d0f60714707165b39b0c85362b1a70358f479028b3f1cc0a2910d60114803580c72028b4219c0e21006b04874012c16358145230b60f1a802784587027865670278a52501bcc21301bcd20301bce2c3045ef979005e31e200bc02d400bc12b4045e1162005e195a005e2152005e29c281578c6ee095a30460161d2530cb4e1298a5850466e1410066e9c1c12c3e4760969f1bcc62c408cc02648359820e8059840c8059866a300b51c62c3498a500988500b86500b8c5066ea9815b66700b0ddc22835b48dcc2c22d4075d52f02b710815b86c02d02c02d01c02d31b8e5a8aefa33f00b19f8050c7e1103bf1002bfe8a9ab3e00f00b18d8c7cb5990029d1a185fe1d2a788a9993300a6b764318be8d378875bba18d168cd00b818e566a1d19a33a7f76780e60c4beebb78e1962a2cc4d18469189f09d357619520b00a1058a555572aaa23132e5bde50b6b421f749328472ffb25a658d23658da072223143ee7bb89461945b728a4f1c6e2943e748864e2565e8b082c8d0610541f1e1563c338a0fb7e652c6f1aca15ff70ef38e3bce3b9e51862ae87855eb8c0beee8bebc0c8db8a2175c83a7aeb8a20e0206102f5c50108b0b0ae2e24e2f081840346aa7555d102f35ee25081840bcd08cf271d4aa1317adea088cff282ef1bffc6594e2cfa9e9507ab8dccb0ba85b885cc2706b2e657e6af0b4aa5934f578d6f4b70457e332033461528aeee9a89230dea556ded3118c91d5aa408cb3ae5844fe70672e592d4b6705b9bbe01f1dae46c6a7ae582d2f73b3e6059e352a34b8cfa5cacf4f25592dd6f8a3e5aaf2c36afd604105477265b558ad2bfc0c2187a5214045372b396f966d5ce76536f2cc70ea62d52f131373040d1375d538549787d19aab6b0c2ccd72d79e6dd6f52141829c0dd94e37bb6ddbb66ddbb66ddba55d9bf64bbbd5a6b6a5526eed33cd5eb68380e40e4a8081d1134860e023db3b619b647bab69f7da399ba8e4ed8fb844ec1b1395e44629aeb8dea3f86be481a38e52d09a6d14af73ef8e8d4b6cd74629ea3705cc9a6c9482d6644bf4bd518a2be85b5579e0881997d0be8d524c416bfa9aa6699aa6699f42a6461b65eaca8eafaba35425a790a9b9e2529dcd0754d3bcfaa35d3d6cd5346f07b597529baf5f3dada2d86f67d726b93d1bcb5a6fae8dbb4cd3def7b4acb499f31171ae0bab5849cad778d9cbd26f4113e6da021804dcc1a59f130608a7076d48a597615dddd7f55a63ac56e53074d137a5da785bd5d6562cbb5766cb2ddbd1ab503851828dac651d364eb30a25f6d2be5e572a63187655cde68a5bd3344d1b675d5525689a57a9d5b07b3b8f342dbb75d5a79daf2cd677192442d76a310cc3b8abe28a75b3c11e0eb7fb74598e5a7b599ad95f17b5f6d602a1d9af6398c56e2e5afbe5edaff465ed65eb088453934fb341ed6870d9157bd99c5ed24dc7f6baccc9a9bac26c3d21855a5d81797e386a137b6159dba6a51bdc3ea5252b6e9f963797a470fbdd6dfbbab26c6e712f3db44629d6e6b58dda9859cc5ed65abb021b6beb6b113a29adf6aa576ad27c9b3ab9c8e2f6359b75786ae8e6ac3169538cd119353a5f57e7eb74a433eccc75691386656ded2c7a787cf03c2ddb7a2dc3e6b5399f8d76ceb2c79d30f3f35a8047b416f5f56e62184531a15dc75c6dec5dea70af929e5651e735ce7a8a4333c9658f3e35abec71d5cf59a6206397f66de346ed16973957a581662f4cab40304dd3b06759a6651866ebadfd92ed2805233e363a2fcbde7dc904354ff3d4eb4231aacba4626e555dd749ebdab7908e948a955dd3eebd4cab2e6a93e1fa105f9a66d1b4aa7732ea03529fec3219a669d5f51429dceca58c8deb1c76f232158263491ab5aad3aa2bcc32a1aa532f1fed23c32da711c6c3cd5ef64ebe0e62ea3361aeef888951a9feee652a5fddcb3b8d4863a9795e0dae7d3983a6922392c0b104131368c28927ba13c66ec2d841dbaaec21d65a956570cc48156f44401208d6fb0cbc619865ef2c9c390b33bc8507ff7d59883dd0620ffb943449498b7819be0ccdb4d7295fda58a6b2976bcc514df466c1d3869d1ce68984894e9ea5da632d8661d895eb965b02b3b29868555327544c8d4c18244860b2689149ab80e52c869a90bbaf5c72c2a53fb98eb40485ae743648274c1db3e84a37b8f49396acb8f4938625293496e784e91e74b76fbb354cd3bceec32d53da53cdea34ed1cf6288f9baf6c2c3b67df6c145248c1712f75ecc8ad23db973d726fda6db44c47282c1226180311c670847c8d39e81bc85ad1693f6ae8b980d4208650480d5da801c864b5fbaee9e86744cd77c2d4ccefc674d574ad5dcb1acd97ced8caa2070b408a64ee5c25b5cd1b65be25ba310a1c3b6e4c373aead5cf886de4ae61ce04468cd8ce9da6660d9777276fef284edfe8fae1583a53c03961366e1471aeacfdcada338d288ba2fa0d370ac07fd7bc83a34c5d65dae7175357d99fc8be438a2b67a74082b22a84063ccca29c19a581066828ca25fd415b99ad29694fcece6dda11cd2a5f7034ab5431d1ac32a6896695134912cdb23b38983c19dbbfa862c4c81cc076a53f2112184bd0902d694336870c0f591fb22c8341dbd075adba7376731e359944d50b43435160d2e39c4c86eca46ab651dbc32db552b5dd5e653d5a27d0e4ecf6d19f42abd92896bd3bbbf014a99a952ec27e7ea92b08b76ff1106d9cbd02ea17a19c7db6b4fcfbff81a2e87d04bd7f17b128969d3d71fce7b9b8d8f0087f3373cb81c8349fa04e4e545454545228a9544a4aca69da543a2d79423a3a3a415862c5121e2c11c235424a092cd93b05e22b7d87f9cb8fe83d7cd779a82659448598e6efa549945611a4cc399dcbe0eda8aeb622f05684d2aa24ebac91f3d12bf131c6128527a3f464f11d915689016349b70f092e6bdf0d865db3c1342c7531acb63eafbb76e88f7a24779b92cb8d654ea73585c14561d08d4591310a033a63fb6cae90662b38b1ccce5a16ee3f47d4553d750255c124b57a017a84e2a08a7ce1309bf8ce5d199a232ac97d5e776a92cb744aad87b665444418868d445c6092dae4b1a389ae6e916b10517ea259417235dac9b5d2fa091c0293f5a615bc461dacb91f3fb9be0b6770fb097335aba06de186217586af4145663a3a3a3a466aa676596bb35fb6c3a1393381727dcac7ed24324f44b133b4a347b3ae79f5eb75482193657e7cd43a6776cd39e7b47376d381091d9ad0618b4b072e522a606920ac8f06bac2fe54332f042368c184a93a795abcc06a5185d50207560b1e58cb42792a59c9edf521cf9c994059013375eb1ecc90ab9d70c8f5f4c3e1860bbeabdeeb65682ad91d9c4141341f96c1317385dd6bb27b9d424e34f9595718b6c2541328f3d8f1cca389fe018e6823ae3c4323acb73a843de7fc8b6cc4aeb1bbbbbb31da5974739298a6655a5da580e6d8a661e86a7777dd42676da55ae8ea290d43add75fd01ee79cbd825badb5d6d689a4d65ae93542bdcd3283ad39e7a2ed02ea69ad9a359c307d16d8b062cfecafd70f0912945bbe9e037b4dd77493769661bf3605ddd40dd7e754b16a4c554a265553007040b6cd650c0a2edb6daeb0d7c84e9a2baccb6363ad5d6bad954e0ebc46ca75b976a143049ae99b88b5b55e612d45c2ed1a5a63bbc16cc2744d461b8a6e56b2e697ed9a9c9a02da94081468d6cc22bd42927fe8cf192e7d97d408ad017242a761594a4e65147b79169bab0ae61a82eb943bccb3d8944ba49b3bcef3bcccf33ecf62afcb5de891bc8ce459ace5aee4599c6197cddd098a97ea4415d08ed784a9c77235416e3a8bb0974ab3446b83eb288f2738b8f4dd35b5d6603ad405978efd9933b4fb74b49765e99f39d352bbb10f89faebd7916ebee4a654e47be78c15dd7c292fa3ad1e554b67dbcf469be49cf4e8199b7409256a1693f959be7a7450825dde9ca4ecd43baa4d9a3533f6595399c55eb7b054ccee97e90cd555e522632ffb47c6eed5a14a56a3baeaa1fa3bc96935ef12b3db777d13ae1b6b110e01cdd84fb4a083bd4cc918296323f6ce6a586b15f654ea5e9d8c5db3a4ed2fb4c676ac1e7b95c36471c75eb59864782c44c9652d7a91cb5a94b18738c375aa666807b1f60f6b156baff579c355fbb6d5daaa4be8fbc2494458cde26c5759c6864dc9949e0e486610fd6ca6f699ef78b1db5f31c8c1c7026c5b6bade583d5b4c672726b3755d65963ce19deb154aeb69322733d9487b6e8ce1117464593680e4d2519c3b0989cbd8c91310cd3308cc33ca4c85add308f9ab5b1ccb1a50ab3b1182e1ba8798c8268cfd1142a5519e31145ce5ea6804796b3b1cce9eb164b49e28622b9df9a92a9f9d01edb456bd6794dc89cdda359fda3fa3ebd4d4f7ab273679d535420adea7151cc04c6c1821b02ab01d18ca5964f534c68406bd0496bbd2663bb5144027bff06b3a51479f63673f2c434b5cef266f43a424fd93b5ab557768d1d51ab6ca6b53adc3f7678704bad5e6b6dd77575776f6f4c53b3f1eaf1a80d52da3ce97bc76372559d5cb690dcd3aafa0c7790560169550f6eadf7f1e307b76d8581457279f7c6bb825b6af9a77c5d9ee7799f8077cf3b13b60ab406cddeb5e2715f67af69df58de9b4a7163f9ac4dea619887395c4691b91820fe7069b3f7edf3e3bc9ccc6dd8350c84874ec5fb90f013c7f66df4bab8a59763625eed3ddd360c630fd76ed7843439abd6b31181d6a0f9b2dea59f1f860fd02bfbd0bba5a9ab4bd3b42ccb0e1ed384e4eb3435b37d517b599109651e1f6ee9cd67f759f62dfbf1ef0dbe337c805ef50130bdae77e3752fc31b2e7be40dcf8dc33979c33abcac8d140674467d86696ab67845a70dc3e74b4e7171cb944aa69253b572394a7b878aaa791ea679d27715db9011a251222b68c7d1871d9a766f2a65d4444f251be568473dfafd2d6b85e5c1b1041395e462346b356bacd02369c94db51ead6a6c7291b7eacd3b4f5345bfb2426b6c73468c2ed3972d59673ba0b9b726a7a3be6e93eb13b8cec4750101a89b2fa5ed3962ced48a4da9ca4ccc4ed3ee4da5fe36aaadd1ba8448abaa4ff5f9b9d427759f525d2553b2c22d67517ec228d72e9a45b3a8b3607849a5b149463ed7a78d7c7ac827cbb49b532f554b5a551a499a76ef0d532923eddaeb11aacfac4598e4d74af8c6ea03855bb6cf916615a927e2f3d3ac1e99781b6924cda2b9d227c9f5b6c72379adda8e040e4932bdf6a10f4ca19cc3de7bd945727d13e1de79580a27723f91fb03532897393f39c7750c48ceca9c9f8c6157566263fdb163f5a944c070772af91d7c7dfb54121cbbf452f55b1aa78a34de27e4469f3a5457b512559fea33a4ddd45f7d54421688e5b1ad56554ee7d6a34a86af5a0dc718709cb57ec395481daa3e43651d1aca52f729d5551d53bac22d6fbedddd6d5e090bf7b3d69e133974f3a59b3ef2e4549306d59e23ab833c70719d6ed131f2cbcb9c4725eda13a514851df552079722c15e4e95d0a0d5f9c7dd6703a63141af66bcb70ea071ae7d16583c36c9499ce4ef952a3a8ab0bd32d82a2a8b5090f93d61225b9569f6a73902b1662c0936badef344f6b419e36c81cbec2bee33cb00d17e85242ccbe00eede69da7e4b70f746d511eeccdebf77ff469daca8a61b45999a1e999e4a52a09e1e0ad433621c75558f6855cd1c15b7a44040cdb21fc21dab4c78b8099f9ed9825b52a00a1481ee9cf70eefa8ab7a15196c7fb8b4d38705e1622305dae25a2b6428525480e25e215fefef6656815261aa2ad084a9e77edc721e45d12c9d9b832640d9e6691a335c9e7e79ef1fb446f7eef71af6eff6df58a24e428da56e2c69f721c165cb7d37f6e1bbd9547c365eeade316cb31bb661de67efbdd33e1bafda994b240d48a6adac85fd43d3b4dbcf460b8570c1b15badfaba91bbe5c6f23bb6dd6ee3f6aca479bbd1e5ed7aa7b27d36daa54581353b7291fb5be43e4d6dd95cfd8d677b769ab19757e96596cb8ff432cc53bb93fc480f444d163759d9646193d5cd99c4646d3f9908502200e9f35d00ae2cd3d359043da9fc322dc322ca2d5f19cb1ce088ba6acc61d8e221a5316608694c52c76b046d1078ed1563f984afd828f8d95cdb97444ec557ceb08c0d4c7380234e18071e523a77bac5434ea57726cc4417d10f8570e96d0d0e144619c32d6564bc8c6218ab78c83dc543c2a3b08f2bb24ff19a2bec4fd8c715daeba7788d69bc82be8477b41dad267ce77d36553bee3e9b0ed35c354c839dfb6cb8cdd21cda839f5a773f1b4ceb0cc3347f1e4e556a8dc828d75f17c2300cc3300cc3300cc3300cc3300cc3300cc3300cc3300cc30fe803029f00c280b6b52f383dea8891ed582992564d8ae4b2b60af6baac0562c256c15a241609bdc54d87ee0e855c1b41300cb311e4b291031b3c36726023888d202eb87cd0a5f2d4a62aecc1ade9ba0cb320bb8661d967cede9d65d97565a7e9ac7603e96677ddcca19446f9a9852022225e8e892288688b4c3f41390891801c80c4618b4cef358bda4cb55645a09b2fe5bcc24f7782c2cdb6ecbb398da573dc3b941f5cee945deb5ef0d4430edaa0531acb9c5ce25459e58706b764b56adc61aafaa1d13da98e68b4e64cf853884d0f8d5a05e11db873dc4bd5d1492773a6fbd2c5c83e849901e24e261d235af269e4aa0a70596f6902a35c66d771b57269c88bbfd4e8421280c23ddc527656ab5998fb0bcc62b5a8b8dfae4d2ce8e421d79314ae0ca1669dde97a144c650b34aefcb58d22c0edb7c8594d425ad6aa256554ac19ddb7de08b49860704c4e13fdcf2ce5eb54b61366121b2f8d25f308c09aff2ca65e89cbc7f3a3a21914a2de1103a43e80ce1e3050610f5b3e1ae5a2bf9f218a777ed7181d2391db1b4b8a5eae874040308964fb36ab3c29b9a4b87399a33a6b7156a4ef99c2c17a0d4617ce60c771942b8868b15509a35a70ae57d161942a89f8eb831c56788960966881637727731c2308c542f8cbc78c12f4af885d08b9438dcd245c845a892ac56a9ddeb22c4877b7fc28282c2a54502561d95970a72a93a2a513f725f54f1aa77c273083e8787bca07672efbc069f6ede0977962143a7553e4eef463be4ceccc90092b18c1ff393a64d3eb764b558ad6671409a95e269564a0968162816e81528126a055a054a050a44a740a540a3408f5028d02750275023b40994097409b40855024d02fda146a0442811a80f150215427f407b280f68109a03ca43ada040280d688b52417f5018d01d3a05f5415740755220850982509d00053954059405b405d405f405340654067406b40a5a036a037a038a03aa03ba03da03ea030a041a043a840e8116e1fd1a3c95a457bc5fa3a792f408efd7f0a92445c2fb357e6a18199a3328efaf95c1b087d079d7711c07e6eeaa57d36b5e19783a4d670193e02580e2f40a7804a748708f532b60119c560187e0940a98c72910de713a050c825329e00f9c46017be0f408d6710a059ce3f409b83c7502eec0a911fcc4691330074e99803770ba04ac81d322d8895325e00c9c260163e0140918c6e90fbec0e91130004e8d809b38bd020b714a045be0b40838c6291170054e878003ddf72ee52721ca49b851dea87b93e9245c9e328954de7c7a1f85290f9a4d46944cf87494d33d914ea787e5cd27d34fef4cb8df99f01d454033ea33df37468d3c7066ab4c9fad32a27494c9841aef4da77b027f1a7380478d39421da77c7ae312248d27a750e534962927279f12dfd7754894a7db9dbeb14441ee7e75a7db75b6c3a9eeeb3a0ff7a036a4ce7bb7c5dd2e22914ddfb987b71fce77eefb4c1f6a2c7332ea1797a77cf3e9f366d20a0acac94f2f7da5049e980e1e65fa34e198ce8d37fad967bf33e16b3abd84511f4ee986f4d38743fae9766e5c82d7ee4db873573f9c12291c4973ccf19d5ec7297f23f7797387a76ad2743824559ef2762fe4b87b397bcf79bdcafee12cec9e48e1d091b25fd7258476d0344d0bed104514da65521d16e9deb8bbc51d69bbb66cdbb6d6b47b53a97fe39e2d00e527a7df1228e3c9e70782e0078e22ea208eccf564ee1d0ae368d7c63961b6d1f48e5e1486e245de0ee29b99b79c2164ee98bd3c14c6d96eb61c2299fb69db727c323796e1a7fdc59a778227cd7503c3bb55d2957dde0ded22d3508b1b6a71b7b1c491b56b63a9e3656ad31163ea4ca81397c07e7d01b96ba55d88f55dbbae7476adef4aa7bd6e6c9dddad8591f80de59bca554efe3db5d23d3c783a883d534c7f37ba87372108822058228d60aaaec2b38ca9baf2c297aaec61af54c2397c89b3179ee47961e879e196bd52297cb965ef226913c67b18862727855d77f0f648bd366d4d39953872784fd36e2af597aadc8da578d3dd338207ed0247f61e2ed172962fa0c52b9fc8dd73a288a254e2c1a354e2c183078f52a9478f52a9478f1d3b16d07296d174338f68227b2f9bc8dd2fa65de4f9dd5124755e479b84a1789167268d10c8de4b08e4ce5e1e09e3a04efac53728efa409e4eef5da9046d358e2c8dd696a189ebcc365f7d378f270cc7172d2982345474c0e1f934f6256706991a0292a9f37a27c3b8a77f01d88121eec4094b0f3405114bf6111044110048dc27b61f82ecc89c9e03d5ce2c81eca4f4e2cbfd9f37e721a4b606a4419cb0b9ac6f2e04963a9ca153c0ba6a9a58e981b8621781386231863c4c591bd7b770cb5b8658e91ecbd7c227b21f810779e38d4e27ee1186a71bf97315947cc3cc21193a1a02a2d16d963bf2edb8dfd0d671bfd6eeca0a91bbede592ebb87eda665d865e7c071dde307865e48fac0df0e3daf41efe3388ee338ee86dffbe3b0299cc36d9630fcc2ae22dbc8af355c5eafc5ee39b673a36da2b51013cae1844a3d051ee527295fca0b8f9ba1107378a3e48de20d6dab4abf636982f730af543ae9d6f4af74ec5382f4d6b46f47fe3ef0a611c61eee37822f8d39c07b638e948b7bd85c7aa74e4af9bcca494a0a85727282429d3b0a45fa48a88f443a9d4ee7f0894422914824395ced3b728bbf78f68a04beb17fdffd6cab4eb787cddeb8ddf9926a900bd73b904b650e6b7f97bdc3a51359d3b697292e4c004e3e61df8dd9aa32897c7ae9bb31274c89934f47ddf46d2c8fc8a79bee443e8d65f789af9bf09c30f625d2b50969da72df3a6e2c35562aa77e823bfadda013863b9d30d5ebacd95669375d37ce09c38da51359fb36ce09a3659fcfc60e01d5b192db96fa1a82c3ddb67b29e73850e5df67f8772146f9c538276fd30dd24311d06cbaf75e53cd263c338e0d4f71582ae1f0f33302f5d269da844ba59fe0d2f47904a48e2aa54ed3a5dfdb52e92794931ee2d2891c1efb94b8ef52a9f432951fbe2be17e473f1b936944404e1ec18b5c1a1bf512268da578c3f412e98d4b1135e298fa250d64d2677622b789844b37a1c6d2583a91492f4dc74aa99370191e6584b1875b7adf48bd34e6481d35e638bd7413d6b1128e3a5672ea8ea58e957cfabcf89d3c95eff394f0ce9da63def86d70bef4d4949398753eebdf7de100ea4d34c227d7e37e8bd6f1d0987db762fc4e54a0e5fc2a513b9745328d44a7f0575574e2f1df51554f812c67ec2a58e954cfa47226ddfc6d20b6f34e6d9a675da9e24d258d28bcb947c71ee8733274c89827c53f27d376b5b3a2fb0749aeed24bddf9e2f27bf6f0a4236686e1870be2d8568107df59effa70e884015932d86f9c95ec8ddbf1cd1c9e4eaca434ca08a6ac9d346b787446bda669d79ae55dbb77e70c1786e1bbbee93a9ceff737dfcdc9dd28e274bfbfe97e9f9331bc75df67633a463a8d38d8bd635eb97d1e1b31ec9443ace578b446f7eae18d346760df4c1933957087b39dbbb977142f8934ce09133e27df51c4b927fde69ea491f0c561debcf7e78d38ddb777da36de09a39d1bef84c1b6679e8e8cfdfa1cc15c4b14f3f91bb2ec5ceadaca8b8b576179cbe9a69f70786ac22724507eba498993a34ed3a5928e5c7a1762ef28ecfd8449228e481a6fdc879f594736dd87d87414f672dccf8cfa099bc652472e4dd771ca77148df80e1ec7081eb48bccfd1bc191a63b3b614e2261d27b984ca7e987d71bc36c3afd8ea039fc09a384e15934ef7435afebe887533aaa3beaf3bb61fa490434773f95a752cb272727ef428c7a8769eee87783f47b7a47228d22e9220e78ee37e0c88d3999348a38a473bf219de3481f683f94879e1762ef441aef84391d85efe8b5ea74d497c48b937ec261f813ef84517e7d09a0f9c54d28a8170fc71c2f4e1a73b03c2c759c72f879142eefa8e3945f8cb3552b3f61f97c0b2e6d5679fd90a00fdf1f0ecb685bb532fe525149b9f6f9299192a2a5681aeaf744d350bf299aa6a14eb47b53da984a5dc329ed5eeddefb4dbb9af686d2692e3d1cbd538e7b56e29ee1ae84bf2c3bc1285ceac8a887bf3fe179f3bd9f1de293c0a366af4e63d9fde434f504cfa3caeea60fa7f46c2cb9973ed4c3cbab1f0acacdecfcbdf4ce7e38a8ebf493df9c48179fa048b8b4b9fbfc4ca6109bba11e7e4e14fc6b1d491effc6e50e39d30a58e532e73c63b612e387aadba63a9e3944bf71e62f034b5843bf78753f3f712eefbd008da05ea9d88ba8863fac96f4ce39c30a69fe464d428e2a07ef21bd4c5a5768267abc2a3f06cd5fd0997368737e1d2e6fb913eb1f71269c4b90f6fee78278cf7f0257ba769301cbd56790747ef1b7370a72fbb51c7297363b6e15bead071ca50d0963e3903932c234f2479289237ab5de3c6926a18db71aedbba1d3dec8e1eb67f7cda7079dac303123c1841b24513d33714959f1c959203fc3d78ef1d88bf7b38a4bf4bf27d2ac91647f2efb024d73b28c9f52b2fe2c3310441f1033dd2455c963e8f00950ef3404cfafc1240b30a78d208630f1754e2fbdda184378addef588a374a277de61da5d88d38a571b68a34decfbc235750e5eb6133e9e10d9593c61c2aefc61ca9df1e36df87b804c71e36ab8cb35528f7529f4f41a546db2a94f1d7ef58a26e5b851acb1e957e4a84dbe9db690befbdf34edbe681db16fede3b6ddb16829bf7d0336da3c9f40d6fdebdb1f4ee9dc33d68f6b6365c11099aef69be0fc7ef343c97bb3c00c931f2448287a2dcb5a7bdbbdabb97ddefbb8b417a0dc91644b2f62ec4e58eccf2d267932b0f3f7b2cb51c9e74163cdbf3be8267afbcef212e7bd8ac1d3ba86963d98120f81093eee1f2662ff43c5c7ae0785bf581638eeeb4f4c61e3677a3c6cd3c752c8fac8b2eba30ca3d2a0e2aa7f4a793615956eb7551fae3baa86845a1b318bbd6d32a8b6159a65df3c656612759b8a597b18766c832ed54c3b20ccb66ee8464cd276b716062dbe28dc8a66d1a76ecf3d34ed3f5f616d32e0cc3ac76d5d32f1b7964b962f6eabe6cedcc0bf4e7489123479e6084fe3ce1089233a0e1d302f4715dd7d584dec17ed894c7a477bc35784c9a7c7348d9eb695713f46a623396d598cc46a7565417cd2e8f2271e59d2bcb3aa22e0d1dd1335bb16ba39c10d882399bfab8ae223f3b49e8326d1b4beda6c700b92eb044ba21d7dd66f6d8f5dd74ffde5ddd95fa6c15d625d20db9cd06c9f4a0d5beb780d8a0fc6164b89ee5d16e74b96b5cb5cc0a3ae33afd6e582bb86eebb4aeebc652474cee7e9da6dadcb88cc91ccd4695b5e2fa6c2e21ad0bcdf3b6efbbb0cc03c3f0bbde4622910f998459e4b08ab0f565f11165ea835b92e92707c42161615c92ca097143995a10a553e2f96cacbd78ae9c1a0de7d470479733d2ac22adea5aad112be852d27d15c9dd433dfdf212ba862a57843b423157a45908a841930a24d352a58a4cef4d963da5b4a644f928513f32a5da4b952099aab44a54c82940a92a326d654a4f9e86c525a418b50eb569373cd1b21044f1b6ecc3340ccbb00cfc529e86736a3aecbb6abaebbb61c9768876154c55290b202cd76686ac0d210b9f09b30dd51e7db621faf2f2f93e1b4bf18abaa1d5407ad55d5f60c274bfec960f918a5dc0bb2e1f1736587b33be2929b5535252522c9d295d4c11e891e6bc30bce79d748b4b3b9bb5436b64c77ab4763b8259a802a8b4cc7496554d32846646000000080800b314003028140a078462c158349a47d2623b14000f8ba64e684e9a88c32487510819438831861802000003020382d90600503c869f77da58b2d8e5bd9898327e8c918d794c7a6954ec9e76207a2f51458ccb1d2b8dd94ae68a468d93feb6d571b5dadabdf5859cac9e564ba554909e68464009e1b6f13b95e963b6734ba22adce917986d926e0129f1ec9e0a8edf7204c6295aae04d4219baea36fd080a5106eba8e6582a4d753d2fc0e197b40803a8321eb6000be3de69b80086a357f3d800ba983a3916f0cdd976e3e2bef696330bf1ef7f47e61abd500d57d43fe1ae466b71860827033a81b36d27a5c64ec8f28c75eb3d8f94c3d7694b4fb16f3f335e533241aa22770c8674fc301bb0f210eed5c859b12dbe8f04bca102ff35981e6a92d568c6db84cac238e0ea179ee0ccc5da27d99eedbb9a27d30f471b403d34e9d93991436f8b671b60f5e847ff04c7935fc98e76368ad68a5e359d8529934f6eef0635c608720664ede95b5d65319e76d8502575059b84684727c0dbded33bdd229e94c3711f9952a5cacdb40e11f8029f61429f16aea6e2c009dc7ce32e5d37958806ac5a3f21de4fd0b942b20cad2c8c10009bde03e0f284440275290a9ee3c82620a57b60d1a278f6af13ba66abfb71ce38835c0fffdd00e35f2bece0096f7034b2a947828aa901843bad0703084c4f197504b49337988c5eab193a9b0bf88ff26ce2dc38836c269259aa9b1f963b649c6bdbdf55be5320cfad262dae86f15439c188ed75ef37a7cc8c6c10e1662cb3a46112712dcdd24109689847b49ce9e2e92b1227dc340f1e6d36bea793abded844b0c6e084949a310e1abfdf8b382b726288ff00f87d5ca0af0292c008a29014785287c00227e1dee833ab4070afdd93045e137732fea2266437b87f4f235ce89ad542ac3b054317666fc2405c455278d63cc5fc1452a04481245da84ab013efb77c48111c3b711389cb6d88e28ce4560cb715ad227b60ef03db7af5d18dfcfae6028b0f3d5c4f5c5c7968c3ee92ca043669dd5e3b2ab1f10f8790f55af17a5e6f8b88edf7d6e7c931041e4ac892282ce089b4ab2330912ef7e8f9076d914c858a5e12b908d5bfe9342d23460792d86b0ac159395f0dd319a2d453d9e90448113d702df86ee4b2f5008cd6d1ec97ba27dffcd7e4b7355c985029eb12868015ccf0f648d008409385c2fb2e6c049c80d6c7418155ef1b3443415d666e10293d2d8412a341031a5c2926648e0e2368fcee2d5d892dbe36b0c870c06d26e6d0bf27ac9791c400adae3df62b1678d897d08b14ac132f6ba9126c0f62fdf9c4c7718b51df5f2ea6b61a836fdcc24bd8245f79ae6fd1c267ecf1659cec161be71661942265c988b08eca1c481a5821951c761a2680ffa1cc348e2d099063720255fe03e13625d81ad0d77cd534d1afacb65b554a225a033e31e9d070d1e4a1211b83e0196414a10009e2cacd3b60b00d079be1d45163c178e5666101242fa1be6ddb5a4decd62e4d0606575cc48baf8213bd033ebc5d98e319ca32555ee090332da639efddb3613c09bec3a935e9c61cedecc312e0474b0278436cf609f7f80643279dbb844e3c3c0754c30f00d49bfbe20a467fbe1de1838f98ced1739d5a60be30705d3ffd6f3f27c429617110bdb311b7017f7305d1113e029c8e4d0c9019d057dae8fd6609e7f559ecac4aa9135832034a0f40728403f24645a3b44309746e64a6bfa48f1646db3bdbcb43d6075306fbf09846d0db0bdfedf64eab083627cade099169bbbc2e41e2247e8c300ba30d206c127b825914fd1d3f2ff65408dfea2fcc5755fc9732afa3b6b6f5b85982c0f6d777fd9a0e22f4cac17b9c85964023610d0f09db6720e8dbf758520b8fcfd4674d295097eadfd7cb07b6cff1b7ecf275280d630dec4dda0756318b21343a92ace23f903b1c25e1060c2cb039ecf9bcf2f9a98081b0e8175da0aaefe338642636913685f43d485ac8703afb54244e6ff78274f0801b138beb43e9273a18b789652f45792ff9d04a3e8fee3fe05aa0d087a0bfbf6f36361aaa488b03815348a02cd50b36a244878878b9c2a8f8d76659f304385b101035a26ff7a23f74ed5ea71e798c3e083e4b44dd590a0c9622a24c5c55b0f28d602371fcab80af2a1427c2fccfbdacff753c9f19950806df127c509fc49ae72dd2b8a4970b866090e5df03af6d7fad53dc0b7d2c875f6239b02f8cadcbd717a2a9b02c7d749ec63956cb40442c6cc04bf90d6d2e285b25b3728d72e0db8e6978a7ef443bd0b80f975b516d028d40184b2ea7c4e2bfd6a09bd0ca0d02cf0aa9757864eb4a860d34dc2ee2c1c32097838941c4215566f2a27c2b1fc8182c1eabf7a87972f1ffb6482cfc2edcee0bdc3ae681345b1d17e4e0b8254ac6e1c8de98bb0be1925e250a8982df60e6f2d1defcb80ed3e6503589f94f631b6692c52bf9c0816e4c05985e0193dd97a123d3f631b8e0dc48616e18b6d7f4580355670162a855463cb8fcb58635f412cc13cc2c5ac3a5a90a1f045a1854a21303978668d751d3549e02980f9562fbf8bcd631779cf06936f6965257cd5420b890d5a5221077b4043f2b2ecf0a641c342a56c66f9713d010b843ad2d698cebc2023ccb25680f14ea5f9d30bd2843face046dafff8d3982437c7c659a814fb6ab6be027d0b95022d9e9c8aa4157945170d8ba00c5491b1664832a55871cd9874d2927bcc17fb749b08105982e8b31971631ca3d82599571bf41c5a97281a973413ad1f65268b908c8ab2c61e7342485e49848a709b1554129378625b61cee948df0bd15e68d7b27ebd71b21de33203f24472e4b6348a8fc9acb3484a922ab67afc132343f09f46b2f3b8591d146c660b130bbda6c6a9126b24c459ae0c138b884cb08cd63e006c69367b7b4b994bd1f462ef2434167a398ea923c80f89f7381037eea1a1979d5ad30b4723a317270ddd70b51ba5ce5bd26ae9d079df440b508af106f3478762c1c4473ce96b997685355a1b89106e1c8a05fdf4bfc5cd00523a0b73f8eedce929319df7da59db9e5c0590be424395859dc756ff6abb2659162ef33673652d167d6104e3555d612d58d248e554d9ff6ab8bac5a02146d5ffe283275715c15407bd7f0092e1c413ea7b2c7e37f50e34da485278c1a7f8190ad65032e7c35741a46dff6111212ee6db91aa52175e6cc1007b1d84cf1cc86211bbec65b1d0e3702c61e21ca3c41dfee381bb3282784cdfd8853b0b6ee21d8bd7fc09324e7429e365b340a43c4d9742e892c4091346299ff8c5d0569325610586da2ceb98bf9a679ac84481cb29d2e3e00ebd2dcf038bdd355fcf4d7d8a91060e15375dca18c8f8bebc8f04d8d382d9abbf83a5d28fd02853ce4224c22dba3e37a97c9e03f6f26789bd1c817868a90ed091480285aab2c776306916d04ea7171ca46160b711ecba3feeab12339c5f19762269a220ae22ff4e84ee5c9d9ceb43f051d7832d9a79c15b9812887c46f901cd04581c9ac5dacb6a15d391318d0b794a5a302f98dba4c149161df4bb18f2cec91a8ddd455051c291abb2ab819a298b99e3747ebd7c2de38433ef6dee61773b07003a8744e615ca25516e303c6ec04f964435b7a06b900bc02b2ba1f84974c21a1318ddc04551f8261b37bdadd51051ddb606100e034ff54a24e05725e7c97767df0b901dd1110fb48518e50dacb9102d8e668dfdc8e28b82e24bc776e610f7b5b5c1140594ee61d27dd717a7812ed8e1188eeab83e0312d010024547c2b2c061c1eee05756e2d82fe20f36d12c85acacf1c220959b11d035be21f0f3865182fa94437272a63f6a4a51d07c0ad126a5f399237403236c86b3a09ebb2ff20a580c37f2d234de1f5ef8c35537f800b1f1f49f46ab6825852908220fe615a81d1a060335427f18a279311709364bb452a62e0f98877f862e5a8596a5084520406d008f5ee48ab7cd8be4089290abd720f21a55e07e5d051c1da782def99060326dcf220594e9de1150d0f66fc2e4a8ae959123cd2a3a61d0551cb4706d27748a420fcbfe638891acaebdb852d4d302d9e4a47bd127d75f732a61769759951c730f873604485d86ba61903ec7486554fb7f304cfc6e13210cad78265146ef329d9e0cd5b92ddd5b322c62a3473fdafa46580a672e762d866657d10f2846724fe5eae4f93c23f38b50f9565f8117f262a82720e48e5026ab2226d28123626dcade9cb507d936dd310f2b63ee6a9845361d002fa9e1ea6ce31b7012bde57ee21dc5c94a1e637679dc91633236b10ee74b7427f27757356666236d840e033bf28b97fccff1f14cf34c63909f4b29ff01be249b94fd648737f5559188707aa4016681d95bb63dd06750d4fc2e3311de1fb43ab5965896ea752dcb3f35d667767e80bfa7c114676833932e2d787ad12d7777479b821641075dbaf6e18f182525e00e7afdad4def0570b02c26cab47834c208e8c05e0ae5144e39bf2c3980bbaf8e4c922d5ee3f62cd845d9b394a0a4968af38b4d75cd411692529752659d46ef5dbe976332bad87b9c331c54349faeda4711a0df9f94aca290d0b01951818e3ae01d8b6b260781e85f235c5d6bc15a81f04f9658d4812d724275156c5d152353593289a4733e12e5bbdd7e81ecbcca75d92d40236932c8e789d435cd9b8e48de25c56c22c8a972bfbfe1423666186e323960ab75f172a66a55eba17b91536c64dbc0a117fc6b464fecb01c4143ca9f9d6e66390602dc26e4271884c9d9fd2989efc1beffbd6c9b83818f20a89628141831a387317a74483ac9109a7a1cadacd902113b2a09936f82837b08f15c04134f429be0a53bee3e6e8491805dd18f48a289524ad1f7d69c649ab80f5878b2dee5cd81946ba69c565a38ef0d0663ccd8bdf42111062f1291c4983ccafed54a65e3213be1ef360300c19a9df86a3963ec0af59ee8b4065b859aebe0cfbd84292cb7cb357d03a88d4b942a7c0145376564b25bba59dbae8ed1a6967c4b08233482ca9043010647af23741f10f90fd214ed45052a3ff8b463c6419c07528773cd348a884df8d411f26753f79a16b9285850b98949c7b0d003521722119d3999dabafb6d7c2f68f72ed2e94af2e3e0090a8ed724d3f2ad1d66c48f032e804ccb564abd8ebee5c78cb63ecab1c5f42499e843870377bc25902a4111ffe630a2b595475b066499f0c3bd74db7fb6656e4627f1f40c9aedcf0a8680d7161ea27b5f66c49304f2458a6e478c8f65fd27aafcde38183a3a8add8f0f5e4ee186bdbf7268a27316dce7995bf2b0ab9e967e71f4bd5210ebc504159ac38d2053e504c562ca095d1782d5c9ef9c8e3d81066ad6bc587542bfe078fad9710c83a2f92ee07660f8e989f327380ac87f6e11c0ed2b01a297a79d474382d160df360effb31ca04332dff64c07bfb53527979e9dc7f3554fb8065caeea19f22067e37c87022d6b2f217540a24788d522337210f9021c7b9234908039ac5b5847918b82f522d8bfd2a03884cc2edcf5c5d9ddfe49a5747dda9479784f663945f62c30349af484640782e77de935b6c6ae85f1886252d3354df4911bb88d0f181ff89070477362d1c7ae54d429647a23da81060b70fd70983d716a61fc0aeb21016d888c79fd3a88c7b8f6b5c679e7144d1a754896ee89f358c473dbefe387bb89dad04413326178dd13f3855d88e032194fe5b16e951f3f3d4d1a33bf51a3169a47d1840ecb679fbdca6421d37374ebf5e6c3ec5b45db719e45b8fbb52488bb25019a28c55eac319a4ebbee89ab0a48d3d5289a68026975bbf030956d9ac9bb22bc021e9d95410b9f081e181c3420643a218c73e4b903c3a0976714c343601d4d09b99f7f5e9fa8775d0eb945a71838574d509cfd15e49b54097649bed0dffe3f9d377c8ca249b8cffb67fd9fdc4edf359961400846d1a443b9ca32133e11afba888d4f1112e1f2b5628d66c4812271d741988ccce107b8196112260d7f144dee33477e004d610e273b0a72c666f72f1b2f6be992b15070841a26afca91419f0f5f1435691002cb40f7480fad62fc6d3646d0df0bb5381700bdae2e91dc4ef96f05d466c246286001db5cd73a364d46aede108618f7c0b1b28d865e90ca31a560c222b38abbb7050b836597be318ab0ffe14ab635cd4e48d4370d1245b57584243be7b282c95083803b8b8a261475b88320c5d988bb23010c08f7af612d9453adfc45255d1cad362f283fd07d597e4c6bb6171db8b8a6cef5695c9bfa0b77a49bdf3a8e83321ec70ff9268c0b0da143a003144d20a544deefb586fa193cdf97911c451a8181bdf9246897e20633ff504a023ae33117fd8d169ca7da365c7d1dafde8d2f47aace41176c1ac763c6796d696e87375e89f6e67a370d174dd139480b391b4b4cb80dfaefd6b879c5965b8d8cd0b2c20fdedd6379fdc0020fc35d39639808dbbf7869d770bc6559b130fc7d234b155221b8e2efd0599f0dbb3085a0724a953d2aff484fdbb65a9419d244dbd0482ce3762d9053e74689a582e7611608d128567225268ded4fa7bd5b78b05398abe9fbf53b4cd20d883fd075b3ef31631eadad72f3b30f5ca971d5b17d29c4ebfe0d46e3359e48f949a47094b636db0a6d71c82fa2c45cee392bcf7ab9c8f7fee715a4b3a2702480fe446466af0c810911a43e6266bd76502a7da4cc982aa0ad8bb55567e932c76133923e982485ee90e60ac00406cadfafd4c3426c4ab35ac319ba656a7111b21fe64699e8fca54f76c5e6d0ff2b4595426e732121bd5c4ae3effe2fa52554a57347506f03a3d79cf0d5dd1926f7063caf4401ce36ba0eca6a0cc4064415401281384baf017f1aaea7ed23bbbfe2ba84fd70b66e94a0644d6bc0fbeddc65db5ecef9c38db62e59c7be2c9dbea5af19d0ecb70f8c2f738a71e6ad9c78e78b78596ed2df8805f124e69fe0ee320795fc78ccffe6eeb774554af559de3f1197d82259f376d07b7ee39fc4a997cce34acb8d6f53e050627ab5f35a3c40b2c295b154125c877cb338de2af5e7a0ae436d469b199e396487df56f41a8d056af50205e10a31faade7052e65f5121678abdfe969dad92f0f1be4829eb488d765bae7958996dc9caa560e60d786580d9bb48e99bc841cc2055043096163970a059c2c390af04f4979bfa950dfcebe2538ad3ab606fe861814e40a7ae2d3a757a8e4f1f3fa257fbb13283aa51ededd3f9ce83420faafc0d8710d9bbc0c23184047a9056eaad4b23b8fb1e05ef40e2b1d070b3c0fab29b456521f03e200fb12b0744145e67ed116006f16a1e2bf2a6c1a1210aa44d1f8423fecfb7739fb9d3ebcd832bbd975278a02b88d4e667c91127cf740169ef4a51780f7e5eb451abad562d257bd24decb0c0589ee6787286d059679df62dc4f4a5c0670008939b0a5ef9619dc240204056e9868a94c774bf9f04249f2c61d66e8d73915c3b879e779f1c33701425f34197d166c67f6407de55e47de7995302323a78214251569b7d2b18b1396c7d21032a3aa8e1c0932623c5231e7f0bc68c572eaac09d482e686e0424062b4540294710a7f421581a604508c01f776698daf06cc9ef7193ca3a504729b3c1623162a54caa31d636878e9f048c0cf4860d147a608daf27cda82b06b4b53445740ce03c9732754670523cfaf481a1c8b99275307ba7a3fc00d38a11467850d0ec2e0c45d57a6349487592d7ba1a786b21ec9a977311d7b5b1d7813da70df3cc0873c4d4f51989f296dee39cf788977624bbc78aa007235eae82c5dd24b36f0ba24ebfed7c9a2282425e87d6984095172efa5368a68c117d3308802be44e07b1d848458cd9d3478101487083c6a15d427eea6143e8444d9c569206a4bd66f8814e70e4adbcdadaed688ad31dc8383bbe14725c2f5d76a403162789b0936299a41faa9ede72c24173381b85678f0baeac4c2d28dac71983d9126722c272cab6f8fffc2cd2b79cb2bb7ccbfca8d40953e61e38b94a5b7c6c032b52247c549d259ca36a040ed0583e41fc4f730b3e5078976e7e74b73b7f302d728df4127c129860537dab0b2cb1a7ac8cbc918221b500b422050f96faa0d2b9e7931aa7559231b2ae62f7abc7f18af6b0a9835944e581830db03b8d0150875e56a7011cc1a8a9e725fe6a51ebfee1c48e19ad630020be22c69db37ec872f877daec32780fbbc1fd4c06c93dc8c5626fe9095d8c56995f99c12567791c2d8d17aae4aba932d2999beafcdfe4498d6d9d285a5469340fe6c27ba0186850079b7487d9d0ff05845664f8f1bacd00a5ae63d26945dd27aa0db92c01ffe1238301a2f2026c1281bc080fa345c5ced1f857d742a68912bfb4561dfec17c0e29069f0be89bfa6a1ef0abba38abeb0f48e22f629e179bf6c368034c06595ae9639425b6c06714f09976977dc62b5f174f19eb4163e54d4c149c470f2ac3688cce458d4ace294152184f518075252c8b32caf5438b58f6520fc0d31c31bb19856aa169bbd9783c110db3d8fca7a5f1e5f491a43359f85cd8194907bc1c3eab3765000df5592f42144c38fe0fac85f0e283b8838f8bcac4465d0ede10345999dd610566ad84158c99e5c9f10f95144137a215718276df399ad004e8ef57942fbfb2813d8d1c779939feee54bd600d77adcd858fcef0552a46d63daad1446f4be9fac396c284001ab7a88d694f82ceea0d90c88de30e5cf90376bcbd2a80aec85875043f0610251fc1c6627d137efb9865a034e252481fd17fd06499e0c6893bddd9c4357e98ac5714c0b4c65e575dbb467b2ce5b498dd256ea3ab47e71f0c7dd11ab117786f40403eec5f1b6ba3f36392ff34c1fe7b081ffbb28c1bcd0ccf6a342fd663476875d41ba4238b1041e08141ff889cf16a22ec63e0bd85ae5f897579b2f23ca6a35f2767b35a48be436babd94b0c0c8e0fe38d410ea4950e03ecea673368d7bc9f81061bd0c982e68d812e5b9971a1c5be4cce09b7dc921f7929dc2583c35104c0d245d523d7d9a8269c4b498ee1266b21dea0ca9910d20837f99e97466e6a61169b73b63fb8c9621f44c4e5c914793e82bb2f57e71c9eb7a32e5d52b8c35633ce847ad78924f0c37829f48153d89c6745efafbea83cd189ddcf7f22608fbcc8622422aa106711561c24944ff02bd1b19cf1d303e2323d478e2f37a2cf0bb5facb9ceff0ce6845a9c3e827e64a5945d06bc7433e6266b3795d3090369c78f613d2f301362930b3a997a07ebe6801a2fa4a05f86e8a6cb1c05bf62503cd11361cacac01a1e93c2b05e0262e60b938c8aa75f1ab2b0b8b01ab6990bd09b5b57d0413434eff75f5ebf0a079d0df84dc859344fd608890d508233c9f947557439b18a51c942d176ef187978e44841657f95e80f1b73a6b840611e490f6b0fca9a9a99265ba449317dbe51b5aef3da006e81111b414ada2dbb42837250b5ee01c1bf469b211efcd7ae89d8f52676664c37e82e7a04e088feaa9de4a2a5a9208a9243528c48c32154c5ddae6a70a944a15a00b0f6c2fc6d8074e1a2d44fe9b3da21f442d818b28e9682a54830c600411be15074aa046c0dfe3d3f6138e3b4445c14f85cfe00ab2c4ac677bc8f840b6b5f6d9a5955a5b8e8228e6f2ab9b1da815168c44210b3a0dcb84b4d0b5137bfec3af6d3d014b7272412f2ce6b584ee8af19d2dec7a0396ac0c3cb8841d69be5ec9ce0873090bc48ff826423fc447232fcc51b8cd88510b550d3c39250819fec044ec461b4e324f30a3e41b4d38ea6f54a18e72ca2138a2d7e38216fc76ed28e4e28e7ffe6a58b61306882e31cb9fca34b0929e38b18119dbcaeb9b8e843d68ae36dcc2c356ea842982a1158ed35853a511944552c1d26399a359b4174ab25759a157863b4863798383b90a1167c0192ef989e2119239ea2e08ebeafc29f12bce1b4ab179a6ea722d2d9368d2ea165837420144f58f36c09cf8be6eaea3918440f86d79f213bb3b2a1a5f4a0b5eb88fb501c6393dad595b26a084df50ff760e7c6b67e4a8111607c57321446f0a5f82883ef2bbd8874662be760ce7ff8b6694f2105d46083aa4441f44cdd97bf287d2ff32dce52ad519f8e4ec6530e2360cd779c4ca1ef34273c5edfa9ec2d22188736455696d525297f76745ec230b301198960c512d6726fdfdfa983d114672c63c0d2f76d0c3dbafc02d126af38f8773f1e008e64bb0b243791fa3e1db32ca4ac931df1c9efd585595b2266bdc06068c726185a4a327ed04ea2c0d32e5fbeb599045d4f349445109d40c97a230f7e9f337596b0041e84f65ce291da09235a0546c4573c7e6eae29e01fc209ee0cce23202a50029c43f508d83fb9b5d07c0f9a8358ecf8fa631951d49908f824fc306c486b9b0ae2397eed7897c739101bcd46badeff6100871f5631b7602e1afe9841a0d8b7f6406cde3f344ef5badf5404bfe1865da29a46ffa5f9f92c3eb4a93441c003c47db07854afb77a0318d0b15eb5ef7c9083dab38d5d8fb38076511cf61638c87e6f90e593067ef8c9b43c7ba2a052c894e744a47af6547e8a6520c56b161982e36cfd30169d86273662a7e8b6d2dc36494eb9e829c0b018bd31645e40d566388d0367936e227076993710b8392217260d893cc6655064f9ee91e625eee27ab33d07e9f2297aca3ac4845b9b169d8c1e92b9ac7a8ced5e5789e699ae0ceb4356dfb1261f066713638246f8b38a61d37a5ee280d28776b34c96b877d8201952be01afa2be5366593b902dba8a24d566ed8a6312368ad4b778ca970e3e71de45b71dd571ebb3e2c8067a555743a768704a7e844d6abe6224241307a8594f63805ac13b4744925737a0ae4757c67c7dcd9e68e4e10da76855234a116ecbb3d9385cf6e4f4b9dbf0299496eb73c855ff6d446ca25119698d660c04b8001aa9ebaa6f751ccbb948c6ead330c0addac6575776131b95079892002b90d1b30a99184e0a2a1d04e525f1ecb7a10446f6b0b4f955917a14f64469e171f62a81d8260f1a8a0f61a997ac9fbba071871d90dd1bfbfdc4b1ed64156269616855f3ca7a177e4fed6c423052d93581279116a8a55da5b0fc326357d00cc9cf4bb82edce793e2625b34dbb954b5c9cc6669647b4ff21bfe10209c301ed90e419d56b412947aac0639cb9abbdf88f67a0311768e2991d03f6214ecd017dea757419153d77d2a00a647eb2bab3c72965c99f41b30ecab8c151a0d14c2e38f76e591b357115aad682c0ab7d449851a5437049698895bc009a319112911a234781008c58800214d242dcbf8cb49f34a3087fb399be2f17a0459801a94c9b613fbcdf9f75404d62a6555068566dd63f7549d45f37926da2ad51e529edb554d3592bb4221e946a96ca3b29413c5a86cd57e661a8bc8fe79a4dac539e1582f15a88551854107cbd72488c925e45155cec24ab9ae91f09b9d97c13981f1a77cbf89e30ae6d846172d8be274660ccaa5963fe5c8ddad2e67f7eb72c918944b5fa5b8bb1c6ec96b3e2857df8c4dbcae978bf7057607460f2e3962ece79c307c8dfc344f12de98bfe70a7f1b3565d1c966843eb6ab0a649c5e220f340b54630031c684a3598126aea7312270d430d851df0c3377d592000cd2551eeff4723483e8e5831b58d4443dd3cde8a6001dbeb788b9559adec2dc1d026062a40a8d7632e3f3ff83c1c00412e237549a6375753466395acc71a7a5d3f4ee482e371b6bdfd21653505062ab66929da68a341b65a438a80670d68dee058a617fd3e60ec75639114a6f9ff5accdd8fa9f72a8a4907e6693e054521d48d61414322976e05e1149a27e2edafc641563428278e6e4c13f09903f3a8be9e94685660912139932515b0ad83e5e22260af11d644f12e64c9e4afc59c397e5a4bb31db36569cabb207a8e4e5718ae3732149a72a13013eea6bdf0cf49898fb5675d6cf3fa679bfb8e62b23ff623ea0e21e1e62866ded18c95fbe5a0671a25d05a0f208fe12be3a827add00e656dfaa82fc69c4625214baba9e3ed79cb159a1f2aeec4e90e48b757835c62cde1e1a8f4263f7ae903bff7092f6ea8f3965bbbeee9478ec085c81a68b99e7e200641af962ec86fd4f892cb8b270d9091b61d8a703e3a08bec4538c54a6b5e0efd696a2dd8947b15315357e8f849046dbb6912a6a920fa6183a17fb056145914a68452e26fd9412da2809f7797c4e4506a5778280fd193e81831b8ac0db822db0a4696ffb78d026fc02d41c8ae4471e3255a91d58382be993c75f5582f5d9e218c8c6a6681ec5fd9c186708acc018432c54fe3dc313d9f94c81b3c0ac2cb2f0c832c69a00f9edc5bbc9842937257640e38414e7f7964404940d35ce339019209e9ed7a731f3d5afec533c659cb87aba231bd35c884d89c1f9c279bd2157e381e251f38c393dae8db12131f9060eddd8d388c0efef8d4d214a4545372241e22a57c6c3f22841141e77a10cc8c7e466455737ec880860ba76590951b123c2392019bcabc97ce11fd39924d1b9627ed08e3891b0305961ad838f53b3ab9492300cf56cf1a39c2c40b28e22edac948785ddcba17fb5aeea035277bdc04bee0cced59a3b187d9ed21b5322cd21cb3c0ac1e3fd4112c0ee8c32fb849eb4936ac585ee93c3f1d919e17ff9f1415ae8dfe13ce79145d08d1a536513cb5ebbf0afa6d298393ddcb00bbc4c1ea82858190f06cfd77dbf0948b6edec6bb3691e89e0327844892b8d21deeafd3d7f217de134f75174d5b21e5a79cef26428f0c33ff2a3471ca64360e2e460bf431c25ec960df44a4d12fc7534612ba28c8139c58928cb8e3aed8d2bbc69ee04c1fdddb2c708b94cefa45205f8dd8920c28935fb2f4214a7b7a747e7ec7efbd8acbf7988fb433986577cdc0647fccc7f4d14406016dc5c97e08e400ff575e00b506bc438162aae4c70200ab5d4148f69c692f332b76e8a18ddbd9e0c0d147ed34c5774b62216d72e2f18793ac6fc24b6b8d2160f346c85c2351d861388baf557535af314643a5a5c74d1253062d2b4b567864e684df7a5a909a2d363a1b2bff08cf56cdb78d508aba87008bde16de93985f404e03bd71b4dd1a20714e818c6bcdd5401a9fb076e6bc256650197a8379aca07c4980c5a1a88480caf6d44e8694cb39855079088f0cfb3479386971f68486f10eb0d1f621e192c40389bc5766266c69b04056ea01d41119d63a5b40ecadbba368bdc3a018fa4e0af0d6e6896ca1bd3c71ede8d3e47b9db46f413b24b99ae2463c02ab6682130e1f8cdf1e8f7a08fac79b3e1c962ba2f8a241570dbe00f60bdf3ce8bdba78d1bd55c73e20e2de09b02fa2fd17b6c5eaa7796fa583c6e487ebac780e35663705b39692104535ddbb0e3f98c96c0368ebd7dfe15a6b809c810485d53ffc8210edf99b07dace4dceb4e6f3ff88dce90cfea8af024c1a3ba068b9f4cd9533de29512eff71932e86fe1d8e43a5404b7816b42f324585c35e019ca91b2f7be7f597a8cbd4d549cef7034df772419bb69f56081968635341102aec73e16cd7f51f2c2091d3818005272d63297f37ca151be529064ef27cf19fd45045112e6330a4b0c0153fdccb68d60a9c1370216d6d893297c77be0be208f805c662cebe6115ee1c0a0750891c31932979d4c638ceda3d6e03642ecfedb477e7fd7fde153923c21cc387049f6cb20e2196dc85eea686dcf040f734495ad8527eb54112efdaeb80d26d9537ee891a22f32c238f9f1c2ff7951bd243aa72a1cab32a4fe79fb32164de068e6218eda6d8d1091e8eba47e13d7b04857002e1b566671434ab1031c3701da3c160bde7be539936105ab11a55b82f00ddcff64bbca437226d5a0720cf786e716abdea35ce9164d4b1d7e1fa0d925d6576f3ba0b372e064e2576bfb0b874a155557ab4b1cbcba717a972bb6a0050f043e444bba7d211128990e0a0af069716d498058f165e138d3ba5c72fe1027abf152fdf0c34bb0082a90b8ea8510ddfd8b10e4be015aa0242a41c042328ba9395d10708ac63690f9d8b027d834c3b2dd81bd43006245e33f0c2dd177de33ddfb06990897ef110f464a73b305c1a92c9c3f21eda57f00ff2a78dd00f622fc1f7ac4b7ceb01031a0fdf844efa99cd0f229adce6165d21a45a9e915e040738f3a464d71ca189461ceff1808b6ef8708e0c09f171515e99596e1f0760bd21f265c9e9340c6239fa627b0449a1cb33040e408164500ba837c8172f62393dbf4b095e80a0410fea3f0eb554ab9c45d6194735bd1ffe65b00725aa8530d245a1c40216fcb555a44d1f97e8f56a8958b8234b2bd8023e144526ad487b7ada52808955bb3f4c28f8e1d9b940d27f1749ef724727b93fa2b771ef52d2ff4ac4e1bd117c7eb1422743ece130f3059acc6f58128b3fc6ea0f5a72ff0d283e8bfc0e66061b61a7d3637adae2b1d4c69ec3555e77aaa5216f5f4918f99de0ff451979640cb7bff61c1820ec8fe2e8f8960eaa153fe6e7a4c085a83ae1820583e2cb776d6b874cf8dbdb26af1ef9c7c408f96c834c8d20f73e23d6043627ee5de15854dc95e630d34e0aabe5112a0665d5d8f43ba9be17b14701928dc9988abb0fdc501adf6999bac1cdc9570704374c94f123bb8bf9920f10a8e3e31c0e3baf56907430084104876a6eb215cc7214417aba8e81cb4b6eaf2e66604ff5c614b33facf368837e9dffe9ce043e04d58cee1c547fb2361fc1fe01a329f47b4c86b9cc64145e3e42676447ddce524cbe1308d0fbcee8ee08d02a27f980458aa5eaf2a20c5739fb6025d89d1411a23c3d2be1bad2c3e585e96949cf2c80c9874c427e52fc41f2fb9947a4aa076f9298e335f392d7e22be8fe0e17ffead0bf8956d01573d3bbe67f8adbd72259050ce8157607eb2f3dc3411f4c148b3fb2d8053c4abfe2fd703a839d16ac2674ad6eb70f83579149bdbd0ac30a0d12006e0abf4e7a1e67db5eb9f2da26c24e72ea09092c1c2b6c283e41c4c409c2983111cd16bc8fe0cc63afc56414dee82c6a2a09186007c028abb890d8c8412ffb99885b040aa6c118a3c6801cf9c024602876e427c59ec843400811c1498c118c66cc2b3710bb5f1e1c626e2081882bc08bfdcf5f2234e1de8842db46de41d1cc13671d147b05faa758121274982478a3f5308e846ccd84650122d9336b6c26b52fe69ff6867a517c8e485830a9a5e770ad3913bb4cc5b5b1650fbff11bdb076b306a3843655f011f36c48de4a7383f8772d293b64cc5dd499a462ae87503fc2d53aa902acc0743eeeff56be4df9a272cba74930522a041233bd4acad95ce90685a3c6f6cb12be5cdab2e3e49046c17d8ce33b0f4305d0900988bec18f14e145c83c950cfe4651fbfc5dfc50e89812b49fb2c03d45fcf1a9c2c2d2c75dfff661c7ace431a8aa2b436b02a0dbb60187bba9006ae2039a33391e90506c8ea3e253c2b3e7b38637594b8d900b3907951116141a67fcc18a60c88a1dc0ddbbdefe9fd9249fae8c87bb3e0be4304005f225acf5468a74fcaec2d4cae4104a70569196becdd17269c3f993eb17aefc7e5f05133c07eff92d946b0526b47895656eb08523b71d2a073e65aacc9ce30e58ab294d780b2b57cab0e1b2fad97d67426e607303811080022edf89e239f8eef41af06f2998b10c80e1e344583b2827d098060c64db74751a38718084dc95d82b21d1dcfd08c05897a092681182b4cc8eafc8fc9c6b68f9d90b8a579b31c09c90abc6a6e3a6cf21c417e46974980c4fc7b95ffd0f0a0522ee2d22fb4d7612e4736208f958c3715b06d84aeb59bb2aad002ab531ec25f92f832976bb59da50a34bf64e5cff4b520b09c574a3299bddb2bfe0466583b5c036aa5dee88792b66d09f39f5f62aed3461550d74bd40fd2567e481826321af297debfb4afc5b4498ccf73f632687608cc7b4ee9165ffc910582418b60d261d372fba1f3c3e36873869902258d10a9a1898f603d515cf8f812b3ee8c16329ba6baa802b12194cc53bb1d4039e00ea9a5dc2a3bc2e1c325a942bdb5ac9211f0be277487008885387b2395c0b1183309febcc7f5298d79d630b971229e116c736c22988838e1251800a1ff8d7e972d1de19cf58b73178e714ecfeafeb2e98dbfd877d7cf77bdceffb1cd024cc5a0bb191002446e144ffe5a01ceb5f8b1cb1038171a31d29082433a28f533b30b4be1f0f711a288303bacb15e616a68e5d74b1b7229b62b1d260b3841d42c098056705851452de2e602d19e12168621a92600b6ebbf86e90a82db87f446c443cc3d4c39643440acb322265d2cc75405c34ced70710881f2de6e9891a71272db1807c0d38ad0c68b0b53ef2b957d2333446e3d82f9f784164bcc53e7dadb19956d36c2557ad885afc970a818b63f562a8bc38a86f7bc3b167cbb09413571db9f1bb172a7e57a7c179dc5fc225082fc3d9e04c47ca886053feb23679b74fd0e50ddbe41ab6283280254bdcb17c8f475a08a0bbc3f23b1ed102d722fc3cc03773a454418e07393e0bf1370985b8fc53e25271907ee287650a7f4aff077c4ed0ade2d4e63345a7bf66dd24460380092346b479bbd358e9fd98d962506dff4769a8c97101ada24788a890d58f9a6146d7d2d0758d88d904941e2a423444dc5b53a482ff699ad749523cbc31060204123c3cc56a400e47d17e0400a483f8d1fddd4682a24394ad23d8f8e1db677e6c33b0213713eab2f2f4543ade9e798bdc26659e145152303aaabdfa045db490f6d81ec7b421db7f0863499c001fda6359db8570db50fa5c32cfb6e99e2a1e09b5cb1aea8a83cf227d1fc1d544ae7fcb7d1d63ea4fa1f7d5be7fb14dd911fe3aebfc3550b9ec71f61808466923a0fb80de7c8bbf0907ed49bdc2a84becc6e07e434a18c59fbe3d3efd0f020cd98d2a2d9b60a7d8821267ad150b42606dc818c7e1a3877686c9224499b033d4d35a44ce67b0b7f1398f77559d58a94039675dd14515d9c38cf78bbaf0758f9c25233c28e737c6acb68cb9d9c49a6535ef6cb0c3e8dbb897a47286621631c499b0ba10a0b835611fd06c4e15f40d2efde10b070b90d1316d7509a67efe6ecbb358afa0d51753be54df8ad48c055e7062d968b558323f1779040bb5e099eb061955bbc42c2f9a467aa9670a51e68edddda3eff0e145fa15f5b28560136e2a5eb8d5493a73607f3b67879f36218c602b5641f689fe99bc70519b041cd8b37753295bde91cdd61bdc0c3a33e23930b32101f081dca6a179443f047f497a5d9f9b8877b28ab8f368592e8bde9b98ed83a62925f52eb8c8d01f086d5040b4d9b023d1ce3e867eb2e90533289318bdd6e59892903169ddb4e00e1dcb5c3ee3416364ba89d10c9fcf28c3c62b01f0a8d258bab266626c1c3d8dbf31be3cd78c1ee78b17e3b02fab80cb6498ab55c5eb8285ef65092baa413157adad822791a0227ea36d694edba8cefc7d3e4d9d84b32e873d717f057c113b011993dea66e94602c1f21329c1e65120e5485827acc11195c14e9c28cc9c491d8a62b27145da3a604fb4a41f5d2b05f5d8e807b29e1ee7301fc82eb75b9c51d3ae9d2180ed63fcbc945fe331566dca4650f748421ec7be71ce5bc273f7a5748cfe0926ca58b29aa8c5c61991c782b4eec4562d198098b7be15e1af9d0c85b4c4553348764de2d060419950fbac66092a01201b9014dfdad446e920d0793518b0a13017d7ae3c3bf27a304865f5b52e9ca0b780e3eb24fb8c7a35808765ab3c252cf6fdbecc83dc80432e771e8891b768fb82340338acc95dfd0407115f6784eb6df07ec49ba9ac5a9855e1617d3ca5929f817794d401c29599e9adeb4d18aed09feb8d62216679876ccbeac216bd3b0a8d64aa87ecaf3a072ac056e99418ec124745467276e030c19ae6e4fc086462d8c6a19da06c49e8866cb73f4d42fc77a9d83e61ad18d2ceea03c54c19a1c53350573556c03483f9073e36811d8d8e7a6c9fa3eb5bbd828db275dd62d220e83327f1b78811d8742e1aeeaef45845d36ccd552fbdf1315604aeac41c17e096779c2a87d147bb1945b835413e546d10bd9c229666e31ae60cca007be45567b2f99bde54a8eca43ab3eb33b8e846565f6353c0e72fe0ede3d4c48c8bb63f16f5d3458f6718867dc34b0c2239047507e9aec43b528f41237b306f1f4c64280ca1eb5f49f0231bb9e064bd22334a1c3766fb8fab255d9ab3c90fb3790762dcfd163dcc029fe5443c3083c001ba4666883ef1a139340b85c20b6f9624e588da5f3dba4359d103bd5cc33b8d15a1de6a6468491ee0103be3f7e2d6808467332c1b7846ad6f9fce0e991b3207bfc74b4fef234a01f40749cdae8c59ecba3803fbcb0856f7d199e3e328d1fcbf138512babe71c5811583b654c9a50e1f0929f33d2b6c294ad08d0907bd411876ad2404c6620d0a240929875d8b4d6241863c2a97e1f3a87ffaa9b00943f61370793a3e1880ce6c37628f4a2e6049142a22e3b2e2b2c4d422bcbd0bbc9a6b5ec06e02828e5f8987f337e6f42e71f4911986cc55ae1d2cb6687b6808c49824eed1ace01e4bbab13f8f5635eed4554e2be39e4255443049923ca4daed90295468e6e46e483f185603473841aa9700722ac13c7248dd3e337bdf4118dc93dd4c758cf7a81479a7f6e51cea34ae1e066a9e70d41182e8f7625dea21d7364d61194f61f400c19c8cbe4bf7e1ccb247a7f9405b688ba9187a18f8886dc58e9648d4150ed297dd5e8abd428be2004877b4c85d9eebafdd3f867b0264d4a92266ed31c0597f021ec35957dca420aabf503ae0ab950ec5c86fb4ebf457370f4e971dd007f2c1c717eb050572fb2a3e41e00dc7bf2e8dc36dd89f792ceb2110e0fd5966b2624b0a6f227f52ec40fd01611c2cfce990e21780d4524dbfc5dc626b542476939ae106d2d81876336cd69ee311e2b5b223a237e2000d713031ddf208a349c072e6cf1655a8c1f0bedf21adae1d1e513956225ca293b1470e326101888662d6dc488a4fbdf1e283fc881740b0206829c8ebf32fa4bb27c4e6a09f6e7d5316f21e16e762df33d04910fabf2250aae2be41d3708118701affcb9aeba59a3d0b7c8aadf1d5b19d00f72ef6887d6257cc891db146ac1133314f8c11bb628f98893d62375755c751ee3bb147cc8a396227e6887d62578c89356247ac113b314e8c1123311fa529b192282ea397ac7d879e61542d5511190b346097b0c23b3d442a96fd9b8dde3963a6ff5d6d74cba3c0df9f9e9977b1f13e29da3ab80cc1084ad331b6544f0b31c74265a78825479feabfe00e04f75347b495f1980911f9869fba0f1b3a99231a674675df6079b669b3baf3a6b495c37674d86e5aac16f9a8190e26701f3a11430efc8bd14b3d2d95f2f9889fe111f4f9a7fef1700b5ff96a3ebcdf879bca926fe53266ade1c3438f77a917225dd691cb6d72b86fdb3264a7935dac62d6bd14905c8223c39ca0925858079bcb526744bffbff87b6441c99f286eef50a815b11447a9511ed9d20f053e55cd0e4ab49e088186511d395eace57aafdd88f333833ab5e0b28f5d89921b51fbcfe1b71886463d07a8d2c1ffbcedf43f4efe3361ed2a283e3f193f79320ca8c585c08dbc9f955535048bd9b1f218b52d286254cf34047016db0a9287eabbc95c833b837872aa7e162cdafd519ee34fea4d271575550d0f24a994bb212b6d46c8d89629df40bf4e307136f6af13218f73230831955c29025b58524a95bd63e20ea9f4674a469b1caa5c04a69d4e88a9a5c7a821b091a952bd298044bd6e9fe531e6a75b2c60733af7a59fc4fc4ccb1e89a82f06e35b11419e216833fe710f2f6deef13372bf94c524de3d6f351ec3497cb546a34bbc3ceded7f7773399d469e2c76ea2f3e84aaecafc30d99b17497b997702830214b89db396c3fca89bbb6e683fdafdfd2cfab957506eb4c99d8668182097bceebed42c0b5a2f5c0ab5e5096e96c52ecc8581fb90063f0f84feb51978364c80629af2b6e5def4c05d14edda5e0de66ceadf9cb0412f39ffabf37d27fb15474deff6808c6aa750e3cf7bdcc317a71240b581dd0469681128b53b87138e6f300e5d2ce705c55f9d1f5793b4c9403b25ee50e98d3cde04c363ab5abc6cf51a722b0d3e0b350f926f257b9e25e4d7ead09b46506214947ad4df38c69fa657a276edadae72b1679ebfc371b9796991245ad044cea148fad3e045e58cb1dfbce6894fb2e7131f09a022b287f94d61bffe43c1719d009636842782dd3367146f3e6eede187b88949e9e3c02ed79ee4380866add45f76b3b6b1c205852da9aa3110adc79f204e531843dfdedd057c2dab43dab73b7d4dba786a5177c00821c28f23d6136df58d45a21e05d101869f0142856129024a14d775e70a90f8b103261c79f412b613c859d992b90d157a2fc4409191c87a14eef20eb02d52991026e56e3051f44ac734200ebafcf4b744233a5981270d2ec6f473af1fd2b92fb4f451647f9503831cc327c16a59ff9974c4d8dfe2f195880f663d4fd72b396478fbd016e4060010d970b82b0fa792e21a590e60d4c4da0412b9f41db1f104d9a4375aa7ba45f3571ceb5e003b39b83cb3821dbacb05d1af859217c5ce664d30f666546fd0a79741f3fe61ab04930472002340df0d8df36fd4961c78c7bd0cb0576fc7fdbf24dc65b20f80ae36edc249ddf5c124a45c16d6ebdcbea000d8d4b1b7057f1bd846936a00c63ffb9dc0a2e27a4648741526c20a2c2450c4302bc3ae6c64ce342c9441792169410b54db59a2927b9b585218d569f43148ce9e0002c7e8bff95a46bdb7c8d609d2dc76715115612868d7b39cee78fab83102b405377baec777571c3008b94d49491ce0a41b3d07e4c97a7370685a77173aa47fe61a3c33e8ce3819ce28b11963d64ce064860a3083e8cbac8d6500a23223a68ce34c460d9231e8c8840c6428f1186476cce23406081833f4629c8d6214911823464c700e430d8641e8c2ac0c61003198310bc6e10446058031f45fc2c62f14d11724e6cb72f6020c7a19745e9c0c5e94b88b31db2570ea42057441f45cd6462e00119762942d8e44b16126854b67bd252c6ea1f8b62061b62cb3166057cb20a7c549d0a2fc598cb1590253162a8f0b22c3652d6f0178b78c705b1cc316b5afc520ab252469a1fc27b4201f8582009f6da1551e74b2e98b4e363ef9c8177447125bbd025916544a62e33aac9506289c1896e126b68e5f1c4f44c6b3dd9c02549aee7ce9ed313b9410d5b788358fec117ce47dc8accf33299436e285f36d4719212054cbe0af94838f472c5044880687cc02ad93f1ee04689089d15b46783a782f2e26026847435cb6e37027d8fd380c7c44637e803b1166dbb4e501d1230442aa04a8146452df422d8e892b8ca289864c2b9e48501680d01ee10829016b18dc8e1580536880765503cc22bae8e5134e6decb9c69627e0696328fc7c567a973a9517ab9c733e3e2b5001928b8099532f9881070cc127a3e82353f88b657cc2387ce510bc64101c72469d1983ce59896736c137d3842b63c08161f8c4387ac41e7a648f1c33871e38032f188247cecaa7fbcf717fc55fa01937a509faac4248f8a102c9ffb8e244fd46b9247d4281c47fae72a2bea358923ea142f23f5738519f51964a9f5022f19f2b9caaef284ba44f2890fccf154ed6679425d22f9448fee3122256cbd218641292fdbb767c1193086144c04cf092f28d29046bb6838057d16ca89f6ca51da9c60fde9130a25e6c1d40fcf572840ea7b7406140b81c4d49828f866a7bcc34fa51385569a9cc5fe7d682b737cb897bba50c01b39a645f9213d069de1295de99f06524abe03ece326e430be35bc0ea7bf177dfde51ce0fe909e77c5d0fcd29e22be3433e79885899489dfd664b832ee8549b77678acd27a9459e02bb78c3db81a67336b9e6db79f5c956e4ab2fb702fe53a37401f66be56e3dbdb11732a7ac7955e3c7a7fa948bcd6b450c43d05533a4b05fa9e835c437da7a19d90a11d4eac04aa40ac33eac604141806a34c1b0b490ed983d6b90085ec1526d884148758ff581a8fe9ad198e82083f54d5a348b756e0801af7f5e00d353828cd5a4cfa47f025c3dd24db9f5997ee2f1468680c44e136b84db3afaf8af83a16de7064fef1854aef6104768976e42cce58bf899ba11bc2c178f21f226fdb15faa2efac89a9b56256c9f1c362731e27db9d9286da7a2dc1f3cd8681d622c6fb01ee9f4a201f5451cdbc2923eba30f346a2757518b7d0b6d581c605d609e54141999e91eff6ae0a36a90afc1d7710da5ab36b9debc417304d454ff20888ac8bc967f74d2404e3f0b87f13415aff258ac5c1d07f9f22db664fe6ff019fdf47dffcc15e3a1bcb17c8a73e59dc36db35d4c09adfefaeee69da374bf6fdde38fe484569fff1d9db3cd7d60513297e589c8f034563ce205ea35b491a46612b919eeb5bae442914a3391e73761702937a53f934ba52a88c0517c8b9fbb69c96100f8e6fc8fed407cceb61f2fb442cb62ce8262c5b6683f2cd9284771b74c59208a357b0a097034b8bd84e2eaa370ead5bdab2ad86d38613677607edde6e596667fe434eafbfae91528da217bc6602fff7b71aa810d1e33793500969293eee2172629b7472c0309f81bbcb84ce761325d73479cb023764896908a8a3aa7ba9b15ed0d8989384f5a51d3dff23f844ab920481633dc0ef04bb8d051066113d39b1cb4e41cd03697f87aa3afd6b0af63d2c40b14e92362d2409ce47c5b9fac174098a399dd24421f95b393e7c08c0521f0279a88c423940e585b6476a0e5b1e254e17d6ba4a16dd34b69e6113eed70d2e55f206b5344d2921ca1be4d29c5403edfb9f288cd2dcec60ec13b6960014dd4b003cdbf2d84a24d9c5be341af19253ecf27bd145167680e93f32530cca73e9e0e550c5dd1ccc4cfe4f5cd864dc901f68d705fa7beff79344637c21dfc130c4b25dec7571d0fee6bc49579e29d8b684a1dd3e9a15ad3bae0275bd7ab2985f0402902cde3e43bca2c4cb11a7e068dfc86457a2452a92932cc0709f1b17c76aec7a8e7bd7bb2b7ae5fbac3a876758ae77a41190c6c9b9915c518c0981f24d251b94649f0ae5bda72a3135956ab45645d94b9e6d664a21e614dfbaf272e91e17d5ed9e4d894dfc69442aec726129ce37cbdc942c935c0861d5d5138a60fdfb1199c5f321c47fc6457ae848651062258f7b1e6998028aca9f6f278a5e79adecfdcba4e13e38744f71671cdc4f67043f4be3456e7604dfb624948b195644a323e0962be480117deae4fc506d7413948fedfd4ca6d14bbc0a4c089fbde2a2692db21723b23f5e4371355d1a39c91bf6fec99b259084455e9e561e41e0d599c5b6fe2c46eda0e89069ad66c72dba801dc3dab7b88a4026060d34760ca1f4d4a373246a5b4c10fb00f7ff1ee7ff58aaf8956b951f0d204c698ef233635b97c35708b4ce7de8e4e021afeb59551be4d95b23e0058ca0b73c7a9ec16267fe6daf6a227e68c1ecaf895bd39e996e76bc805005362c61df0a6c1154c069af28088cdc627da17bb63310c3c6a30bcc11f33101715010bacb93c8dec180e107dd0bfda4ae3826180d100c78d40b0d7729cef2f56bc8ac62fece7a36402355e117d7c5876111455d6b9919f1c3fa5be5dada3a7fc2fa4741745e197ba6c723595353501247fb334a759bf1a149fc60f0985b66224b1d58fa7a42f003204a99a959d04d7fb13b4130a4a468ec0e97f304ae103c4639699d38042f3d3c4c90a256b93f115cb8071f6af7ca25437829129cd05e208d47ed3dff7bdc04edbc807f85ddd39204c0c889b931d18a9d17f48403c52ac000c136d544dfd3e3dd5ce44dac5da4587609abfaa1d0f84727b249122d0d2442c3f8b843b892b3a08befccc4ba79478b40f4fdca92ed707011143d8ba6aed23c266d1720530feb3da2c0e8e5b5454502790c9faf1b1cc0734d69e6b28a81d957fbda8741f4aaa5c6ce30b3b6985d3c233c8adb6a110f786f8aa92f163e685056a5c669e91a16f22315f3b7a980ba3e26ac8bdb99a91b893053658834445f77381349596adc3be6d3fbe299d2f2b9ba2512e48406c9455ff49a1ed072c0389beed4748e9c359a3f3b82590a4381955ef23f7611b66170b85c3d247818a7a6c5c913d56c056ce50e76050a3c56805086a8aabb4a66e664771004ece756867099f3b5d6c04890f2414450cdf9a5c76370e9129847abc7c9a1533ad40622abd6d5f16485545217aae465f0c5c19695798b53050aaab4bda04eea70b5414fc41bb57aeb5bfce7b817a821254c8bfdf3b50ab633f81ea6865fa154624a384e1a9498836de631e92355e2d4c6620084389838b70ba8e28ee2b02a33d3f57cec00ef858175fc6ee907631ba2ff1c4c3f90b73fc6922b7e89922735d07f2798de066a49a54908ab431e2f05a43ac551c0d55c170115b552b12d720a19b699384721aca5ecedab21ad9940f6810a4da8ea1414f04f93445f5bd1461e6dd1bf637ca17fa44a5e8089b920d07a8609668322b475914c718a76c4b56a83ec1a6791d3547548fd3f28bcd10a4df0ba96e84c071320c5a8d3163011fdb73a8753f8225ea895a5bf5fbe3b98901cdda12273d233c49a8d8b69b64ee98b8244aedd8d4a2298e032e362e03ca1c8c656e4aebcfd04e618af27ba17af3e28ea29763ddc957fd8f8158ab857c2109a479f827255a0f50cf5d3160ed461f58bb000fbe9ddd5c469d36720184d8a35ea0736e0244988781bd35b429cd3b868abe759f08810bac9f604ebd337151187715aa9c604d35984c98a5578d93db5202084c36680490e2f8b785c1189c8b2c2680ee4fae5ad09ecaf85cda296735f22fce092f5d3910263f03a034778dbea3abb90067506363d220be04cfe208200dd2e3dace8ad7a7cc7edc7d203dcda02fa07a8291728523ad7cd88fc7ee726618be68a958485a35b34dfa76127b9fd75039a5e1d7a6bb981a4da394f65b5724281663f7e1f5d314fa3f9422b260cd147d775b6cd2b12a7aa0b53a6d680e7071c87093ae67b6f5f40110568d3d3955085ec1f9747c4bdd9481b4c86a5bb43011bd4e354be58033309eaa6b4f8e09ae510b0423963b73968854b432e54faac47ecfcd6a0f5c8ac32e0b96e3a7ad3f6a84d608e90d1feae13b8539cb2e38186e9032373a83c53ca0d5e1467b0d0cbea8503b4056355449812fb1a9ba7eb09912b859d05612ecc3426772db50fff5a2c34f0ead69c4968306cf74aaebd143b4ed66b369b7a09fa06d84ff4996559a850a27d46201f6a38063514ec85564a731a80f2a1fe8462610969f550b65b1f528d341492696247360d3d0ba9d377fc4d49417141f6a52b8ab2492bfe018fa3c94c876bd7aa46b08df6a26e9107bfe700cc89b44e77a6b1c46848a947a5b8b7437f5881765e0367e712e363f955ab5542a2708fe8f9cfaedffeac07d71b60a9ce2d4b148487ed84e4cf41dd13b9596448b29a34c1ca4eb77ec2e4766f77dc9e62274a801da426857b97b38a07e287163ff6fa1414ed0af553cd8f27627fc0a02a8e34cc24390d2e99406498fdfc761de21b1f8db9101578744f5c5dfd852915120798e435ed866557a0cf783c687c72816eec21dfaccc6b60a5e4a9ed89f80933111bd020a36d9459074c8ed7a54020689403b9bbab01b6ab7a6a5fdeff4af82a93ae82be7727aba4b3c688c58e2b4a2aefc26caf2706be483acf435c9f817b1b101bf11aa89fb8722a080969c9c0ab19f7f5400e9af7ab17e8b2239a7a4f3465903c54145fe33c386fa1466848df19869c861d9a2cf7174a89f951ef67f4185ce954d00fce025b93d327cd98dacbb04f8cf1d0df402ff2e89cdd8e7ae4307eac0424f21da53cb6346341862b5026775d81fda2e290583d0b4ad142907d42cb0839c738283da118dbf78c97c277db6f12bea9108d45c43d4cf9c66dbe3d0552ac827f510be2efe5fb8fffb73fe80d89613e3c181b27b35916673630dd3b24c7b9cf514623af862896648e4ac214d14d7a43b26b0762508cf5c3d1d4d239907f036c46c8e20c22a563837a715319a23485f2f0f50b3e80f95e70f8f9947331572d9f063bec220db55125450ffc18e7d2e5c1b5171faf5b2b77b3cce45a24aa98d3be1085ccc4b78cb6190276838b77fe2501f751d94864b7fc4b23d2abc2de649cd2a29e836e27ae78f9314191d993a2a80a02db89e264a130c8aee1717b280cd9021ae2d76231596461bb9b8791a79988996134fd7a694553c474872fa5051925e9e0551d448bc91e8b6d5a55231ff8fe2abed6f569057c17f0bd760e20287bd44ca030db537e49642bfa294b2b08e6c5f904cdb6374507aba4860bc7093157d5eb125647ddaa9b7468ff296e9230dac2d69052c9fe98c782fd2887aa20912f13c74a782d7601e7a63218f695fba596a3ee1b6f5ee43344c3764b8108516b673c15cd216197e0b96df894d664a795f05468b164faf66ef74990c8477cbcf0aba8ca88d7436cea62ace074f452d06628cf1ec32c9533ff5a40de32169301e788190f47db783586c0dd94683815c9e4f7c1a296ab79e0521241b40d38f77516975367d2695200212de03df3cf1b7d283d39a6b3577e56768ea7dd96ac2015fed0c07245f30d453c0c141dae6b8b9623c09ac0d96c35c2b8e88a72241715b05e721a3806cf60185d8bf2481543a5222cad45236ed4079cd675536135a216a67e0575f20f0d20682f8e14a17e54ad33a777ff280286374984b92687b3206564023398dd81e0285b5d662c0790dff7a528ad8735737af40ea8146b831d9329cbf72e9e48e7c4cd2ddb2082039565350c04de9694e90489c21229a5a4bf135c7aa73886b8331ee4a8689bd9e1b9c400b621ce56051c16d8e1dbdad7119a2c54e52ce3007e2368a92bab7f5fed193a342a3f13645a1498b12ec34de8a81a278a9320b18285d3046328e25d84312052880c056018d125a0af95bc04a5cdaac1e29f9819892425a81088e79373a54ca5ef13bcc6073bd3a4aa5bbf46c667f4833d108e1b881d44b1bbf01379ca57207c26f7490e9e1c5cb73b8004a6e4870e895aa10144be52a05dbb6b6b514ed6902f2842b42bb3e45d220c3068da298f7473e3f3f6e931490f0163d679c980cdb74755112aa44ec3ccce0c5630ca918dc981751c35ade119c176ec04a1181a0925500e60f278c9a040b1aa1e017fd3a2093d752236d9bf8fd7f3657b6f89e301ac7f9cbcb7dc7f46e3dac5895e4c7cb146e47d67bafa0ac6b464bdc45c5ca70c04805485c7f18b31fb4e3411ae8dc517418dcca8192fafcb10801357f1601c8d50e7c191ae4c6274019344032a2c9b96ef8971c659bc17c3b89150459188fd65869d59ca998d15bf0d1ecd1f4829cd5b92c562b23b99cedfb9e42d4e7b732a35ce89d98f3936c3c1f84b5b30a7438b362154d6cd0e36d943622baa8302b26b48396e71daf2ea39ff9fe53107ae85a2547ab868fa11c31b8a24551a73769fae96c8c9fa71073c3e5029fb1fb2512b164390d22ff400f8ba69fa50176f5c8daa9271a5b792fc55cdde38102613b08ffa9bb1b83d7d7210f912f6efb8958f44e09a19278c34a7c82470aa252a76ef3ea66af7244298a4279075aa2a37e193b42745847955d35bd5a13af93c4704c4e9797958161b2701a881afae04152a73167ee2d211157921556d389f8442075d680bf35c53e24c2e818ee4db34011a7149bd459998afd709cc1b7f240745f20b6a8ada50a5d04387458a90fea517d425feeded29798d1ae7e935609444e45783914f2305152bf3b7e2079b799e73bc21eea52444452e2a8f478c7a302765c2cb309d1e3e54a0deacb385e694505c04605dfd12191c466155b8047323f1ac940feeb08abb4671be027a0bbc32aa78145937847934b6fbe2a82119314b691223bb23016e183ddf92f68e660a530790b15a976f1146b9f460775f4b1dca620baedb0faa086f37f535c8fd7b87a5a8b717ba71ef364a674d9ee093f777f0204d79bacf899a2ad4f39ec13619b6658b3d68eec197320729b4f3cfb8a1dfbc66b403f1a15ab8bfc92b9684374128cb850f1234174a5255e5eadc0dfff331ec709ff1799f6bfe28d3eb084c82cfc8d5262178e5f60b9e54908c34627ce6674e40104fc1cd459325ea6c051fa4726a178ada3d22c580a57a82c4e2b757707389f2cdd7269f67d592347a21281c1657e920e328bc783ad3a62c4696ef45aa4bf1d074f57f722c2832a956df52ebd38b8db0276c0f2fd9091d40199ca1744e1e182deb62013a8f396c9fb7ddf63954c14720ad11aebd01682b6a6a0c001fa879bd0d4fc84d4ed357bb6a4a9ff957898f40615040e1a4cff918ffe02d72d602fa8bd04ca46b5c7111c1cdc22d3a7e33db46c8dcdb2d433e3fac6c197f5691618676958095632bd9241b80a95e34e6ab435568091821ec1e574aa69683a56845401b031eb76c3c27aa817b7533057736dd61330c82303afb906b0e1c2c26eebbe66b6bd7396248340a2f0ebfdc0f2d4202a8c87cb585de10d41c2a98367441729577ddfd29d9067d83e73e79ef1320a3e1f21c4a66665d6bee09f4cb3e8a80dfeaa6234468ffa0f79427d630758da4dd92f81a89c7e0e354079793bbf2f6a6448542231b8cc4fd2c1ec4732d4b71038bbd85e437f5a0999fdf0a88448978724d437c110b828c40eb215a730676747661de9c7e019399f5856a73d2cc7e07f17b2bd6537cc4dbe6b91a94b630c5445231d0d1a0be8774c8bedee7a94dafd1ba5b4d44cd1ac175f8bf079e8e4393a690c6bda7200619714cca773004ab56466d9b9d5cc05bbe6e9e00ef12365d1c102e6e508f7177eaac873f933d7f5a2d22a21e01524bff57d89243e03b78f00021549b8d116f3d87d35b551bb161b509e93dd10fc05b5c4854836ee17196413fc6f5f57e69ed561b644de5e30693bd187ba1ddf9572c4484cf53b2ce5a3a4d9bccb9f723b282fc85ad659bdc3bd8a02bc8f0846530463e149a84fb9e6ab20d550252f12b0449b19c3cc2230c7d4096dc7e81898be0617a0f33bc2da874b7e5823ee646c59e1626b350fdf9e6fdf406e973a45a9b867ebb6e8bb76a656793937191b6418124337ca6b998019ea8bcd9ea307a3b619d9f27a21b3b8e5a34c2ac0bff36b33a77c284bbe90177c1877f3015beec951ee1a8061d028ea6af6d7b3ac18c7f1c4df258be357f58c747b0fca5a87871f642f17846c71ee5a244e094cd12635e70e3ed760d912f33017c365139849dc09a10e9f709a231f0ba97a15989bb5721cc32fa93f29d764778c34a1cd9e058706520d31c0b32e82dff82caf58fc9e2851881d19f9e971fd12d0601d56d47deedd7d33b83b52b211d09c63c22b05f5e452cf6775f07acc8ad6d06460f82de88a8cf19dabc1c64e0b6763af51f674e3ce48202c14f40502d8487159a0e7032016cb4f62b0bfe685300220b85e960b01f53d340e77f522d644da47030a769e4a5ff3aa55aec8622094b9c52d0feeb7bb818d54bb5e84f52db8bd08ac7f7b6dfc84c570a6f327592be16c22a05e843015771d1516c29396ceeaa05bf9170b1e103daa38ee9f30afb56672e2bcff6dc5de681be562cb709b71db77bf9d997f5bbab88d003eabb5c12299078d279233caef5a72656bce30e274c5e6eadaef3ba1636e2406855abb67da4ff4503c736fec911f9bc80b96d3b9fa9716437cd10e3e695a47ce6edda41d3cbc1459f9f70f3ed245092dc742667c2f35924439dfd01df022525342b2fe45533397ccfa2f4285e78823d0da46b12757b0ce468df8c376070634a8972a403d82015e9f6641c60119819a22c7be771b5c4315d45699feed4f1ed56f10176e59e0370b01895742fb55395c3f3b24221ca8d449569f26b376c9fe72f0c01ac02589b4b1924191dcbd7a69aec4bb387576e31a4f60f3553981adaf0256f16c40c7e7e76b2c057ba18f79b226402176e745f9f8987a94aaa4f43da0ccc13d285bfe974babc327c9631b11e5f128e62e4ba9304df238e21784c821114dcd6c5fe77af5a29673cc061540677a217bfc2bb8ba7ceb73d7e1e579a6146be3970cab403d7f1872739cec923cb1b1d9483a2cc26057e4937265a1e4cf8ffdd3d1505ba28130973fda3cad7d1f1627d45fd2f30e6053096e6c8f2d0a65c8d82a816c2a55999dd88020e92e1cdbb14847e13583186725bd94c78c174d85cbbd73767eac8edaf0f55356cf9a5d97c951248b941dbbce809a9b74a051262bd98dec1d2e81554f082f80581b3bc3e40b4b221d55505c8009817a0d101c8f8d3bf837bb105d8007b80c9ad0f35090d6c81c417ea02dcaa2d92459f7c87b40c67cde83884022eba239fe1d1c2b6215ca8170379ed7f8cee553cac02edeba8874d7abb2c8c6534b2f1012a083e0cd8778e0244ed831300c9b55ae9c6c1e57bd592a280fdfeed8acaea0f2e53e8395cbd0e64a6ca4a81b3c848302f6fb7d49bce25fdca5ae6f53f3a1089f6e934d36931c0f3077a6d8c421910bab202a3ae18c574e248b51926f2cc380c3219c7685035ef89468fc4e831b1df4d25ea9f4e441d7604827158e00cd5af1f27ef2cb89c07e394833509f3ef8ca365c06591a3d4eb79c0120221e65a252a9b8824a02a158c81790ea9fdc466f045609d35c9d77f22ccdffc9d0a9ef9259c12e732df5147086a34ed75dab47d1e21a9875c9669956f5fcac352c66ac4291c11c09c9b37ab96f9abd30ea23f9745517f777ff85fbc4465e90fb47026db61d16ecae812535a0d1208862778beb0f92004384101f7cef859756933980dd8cd2c7ca2b8d1dbe01b42b0510af84c37931e329180ccd1f6caead9bc676a4b0f6cd468f5b36bda6e6f1129410a95baa462d7c1017e1d263fe5c0b0531d6741c6527fb51183b9b9ad59ad8ac0e645848f5c4c17e5aa5499b61dad037236a35dd914421ee74f0d595ca929027bed379d95ae7918f4df433f7d0c19f515f745c8be9ba820a2980636ccfca81d9fb09ab9e6426c4ccb2639bb4735305ce5b2df1ac977b9080609fda08b8e173287b805ae9c84f6d37da39265b7b06d58db9ac47e430dd04a8e2e82f24bec37bf6af2bc20ca7bb7e45d54b91c3542888fc0964ecafe033318e6aabe76f2b410bf05b7dc5461c9656ac0ea8b3d3a1c66bb153a4d7e0ad0544945eb3064d18706c6add2f00be8710990895c1216d17d7967757f6f245941b584af8509273edb30299702b5517a6d60353cb0ab17bf547c5b1f4550c2be1a26a8924d120d22bcbae5c6c5d0712e5e3cb45c0cf5092bf154128cca02d005075a16ff1c898dd8bd8270991b8ee01acf2437ad90527a3e4e994c56e7c63748e237e2758e0bc704405469496673323973cc737efefdb9a92e78a73756aa87a1223dcedf69680667e669d8d4a311ec05c28e1e2b726ad828aaf1f23d1ce73a48e9f6357a35b6f8b5f2ac3a3126d94890b2a32f19b175d0a2973e28f945cb57ef4dbba85b019bdf9364e2beda7b949cc1b33618cb3fe57962dc61b943f6fb755a8fd1e6fc590c611906d931878c243366382a5538bfb1c4cf0968ecf6ec034cf0984e2fac6ac855a6f891b9a0a660ab60eeb79ec425903bf511be63da0490c9477ee231d254eca1d552964145c8915528d8ca778cc94788d640b6023bc88fb7fc5c9d93e26ba9b023e920c40d39f1b462702618807b53417093bbe91b1707bc2880b197f275b04efc2822b66b31af36697c1baca240ef9010518cce5105cae44c7439ca78924cbd63ef35d4fda8eccf1cbfaa43e0984ec8e26cdeb2300c88a7d014beec70acf84c3e522989dae9411c939de137299e1de3956ff04fa727a8ce7e071e2f22ceb581f5d22b16fca079c99c7285decaea6880f1016a2aba2ff1c75a8d0f88ad1e52bbf385c8b0322f774b554269eea3e6f2ee7ef7170538feabf0bbf8eaed6f45b19b04411d2cc67f46d1d12a8c2af54d08cc4f8aa9ceeb953954372c734d65f70e21b08940c825d46748b7318ec01fc82a981e3c60547186b8cac13ecd80c30da8670404cccc37f99b99d33a220159c083b1b513cf529303abf3580a9cddf1ee92410700bc3e4eb144b4be4a694f206c50fb6ea04fa2c13ae1048e1b3989a3cba59c78d262df668e3f5d80a978a0dbd073c9b16603cb8ccc410ac34d69630edfe6fb1699e83e17708376178edac060bec4d60cc00d2d0d7a0fd60a86dc00c0a5893b79df401aa833989dfb90865f021a8692839739f2b40b785ce454d193bad764d806a2f7b6583d8713bc932cf039287dec3315c78fe422d98f74b93ee81e51e7f656283156dcaf596769ddf17f34519ef9440fda41104920bc8dd6438ceb71650571f96620742f65e14bbad0e9e26c0cd283ef444982c0d1f94f6e4184875ba04b03c1eeb4dc17fba1fd332a6d0f154df730f36fa452d04bd096e0b53e54102084b2421f37fdeea89d78d8c2948e4c849de08acf937c69610ee0f8a07308ea17a9729d58e3d8a40452608c374af01607a6afe8396b270269c92b133b612b972b546c772ad9e777ea0ca2fff6b38b88242d410b56318a24c302f75bd829b60c3c908e5060b99c66ced7e4d447cc4c40459807045247e755c941376417ee9b69c34b4d55154c87c8535757f8fcc343b87b450bc15b251145b830ce2da6bc9690ff4c8050e8386da57250e77ba25ae7d0164f9d862f68f6acf8accc2a21a9ff8b32e71c2bf7d9012dfa172dbe4e00d601ff3d076c2f44d516e5878c2c5c8ce83fa6a734215f60ff9285968e7a1bf2812fda5fadff471ecb01c62545ddd67c6d342da23ad9e8b37561f00e2351f17f6aaf1e1dfb4073eb110d735c87ca52f4f4c0ea61878a1ad01a3c827452df906e7ae9f1bfb8c47374f0e74baa75639dee76f81094586d1a59808e6c044426cabf364928f37c8f23908b008e27d5da056c49dad3abb627cf84d76190c765d240be504d8c2a2aea2e4a48feef830ea94cb16c7c21375692fd481c50177b3952ac72a9ad50dc6a3755c6283c833348d7eb0a32c807cdb1277c20bb9da7faf0710bfcf0d64d1101a64bee0207237e115c3f2a938edd184e74fcb9b18f8d5393ff524b299a6de076de72682d45444cfe7a7edea3341dc7802b547b52233c255fff7178a87bbddc9d6ae11b27dd08d8c8bd699516eafdd6e936d181e847a17aaeb2c8d7161999f7ee07f7586b3792a21ece5d6fe2599961cb21429190ac520e1f305ec4589ea1015a2754f7366d0225958c4dfdc048e81b6cd125a24e212b9e9349a3c832aece32f56fa67b2f023bf135986e9490481f544e32b84123af6f879a52017a64a281162d3ab860d82020217ff304bebc098af7046f7daa3b7ce5c2ee18d91768bd9024fe422251559cf35ad2596594c23d6a22cdef25c3a05e165b9b8915a1ff1b6ee3bdb4f1a630493c898d7fec6c18321f881b685157409e0242bd7e313d76694b2bef2b00ad28351b999b2f44b7f2fd16614a109afd39aede51fdac0872316072bc0ce50c7823de13cea94669feda7b89948023802f710086be76398bc164efac9a8646a052f56f8e3f307a76159203ab50385378b3f0218f536ee253de61824e591ddada9b425f38688a47c229574a9fa84efa5ab3b62dbf578bbf64c6b9d32ea27217801b05ce00432701818491c1c59a8b42fbd4593c3f2ed6df6b457c442a015e968575d09010378582b5d6be91ebfc662d077b24f277edd21364e546130532e0ed2ed5ce8b1fa3f18bbf5925a1bd903870bb9e0c12c488cee50d696a9dbd812dce977670988579877ff9498a737aaed83cdf1d4c16e1da05872b84bdf1e1dbc31704b9513e0bd94ee3b1586ca39687a5519b8b30363dd0fc3e0c08b6c00a223ffce5bd3b09b2f7974e55139986a86e1d9cf43a43dda3db328fb8e14ece3740668452fe4a1e1dced5b299e7166019b0062c9e14237fb54f4352697e53e1d6ece73f7bb3189f05374f608159b2ff1024541ee14782527736a469bfb9e139c92efcb810fea3dc2864ade6cc3ac38512055667763e38f32d27db3a1f91a3624625d464e9843d4fd400a1b957880c9acdbf5dc7c2c3ac97f4482b6aa2cf9aba17d92625d88e49d166255d66f9bb0aa408d52b6408e9e8d922fe933038c8273bba6865a0bd468f33a809f4c855e4b5527b1cb333ef978aa4cc12efc4e60b0ec5a5c2e5000ee5b1836a8c99a916ee3ff51c12e6becae458d8caad82b88bcd2a5d2ee85c08628c29395c59c18759d13c84c2688a1c447e4c1415ccd0555f1c2733ee8372233d679fb8db4fd61e285e4d76aade80f07b2b9e0e8cb71b585974d19346ca949332e9436639d10896ed8dfaa92c04c85f1c83caf2005ebcb31fdd98204fd08b5c09df38913088e602fe7833fe8017bd8a9f836b745d2e5ade00207553c27ae32a26ea8a3d0217e68e9c38a24d14a763282ec071b71c9ae0f42d9e3038dc80aaae2ccc1f11ba59e6092a246e9bb15442bcf3c4309750e933ccff43a6cc928d914817187262d2855ea716a434e2a9d9c3ef512f9c5e16f39c1af9c4abe9304ff3819529b5ebeec93d47257754eca49498a904eb58a2007a8e4f33f3a66d455ff5127816dd2f9f00e4bc86ec838293c25c2fe7db0309e7fbd53bf93443879894d775232316a9f33ed8217e7b8574e169cca4bf8b2f477419d0e89d037f0592bb260ee4680ec3e07fb8f095fbcfe06bb615ce8b1f9c98accce3e8b01b20ff628acebf997177fe33fb6f6d5c1619db6800827d8122f3d80a3dc34e6cb817999f21b16dee67ebfdbb7002c6a44ac52020180c8705fc0e7ff8e35add46ddfc2f558061478cd1640252ec4cdd30f2b0588f498f286c9959debc497e93689528610a3a1bb062825a904415614a2200cecb04dd4b414b8b45a53c769dfab6ae6cb6b5d9b1a78f8c5af50b3b9b7e0507993b25bcab0fe9bd5182c76da582263381590c3f31ae1038dc839abd7bd8dce37cc7ff8c90aadf6b73b7dc437c3643050d174d250173b366a2145d24c78a3f88910c792d52914a3b505f28da9bd201726e7fc91f602b38a58952217e5e9b6f491e9f28f621e2938cd06394b34a44148a22d5018d00904d0e464188b6fe9ed084e60d4f9b1baa51967b0691a05da696fd75099468d13cc3bedd8660708196a95b18b66bc7b61a0a1f256384ed0686a1b2a1f4e12f742c850a59e4a3cacc253eb27aab4b04a6c287f35bc1604f12f39a58c86b183a6aac029950a1345810d69ff39a8c6671a73695b575fd1c165dc48f689f037dc35547511826807e14b02cb0ab843485c869520c8b530c8c1def9325e21b864adcd9c47370c6e883945b14292bee2a17d60023440a21e8053b055ebea69dd48b66010a507d85ceed7eccb4d833ab47fdc4d53131a14d644ab52e578cf8fb20435657b7557659efe383bccab3126ed64b78c312afef40c27be7c89854f434e5f1b555e8659f45640aa1eaccf31633cf91a812b4d67a2f87003b981f5bfa0f7bd1f69b8b1a4ad7a122884d940b90c77f85268fe678d2354e9880d2fd4188a98039ee7e005e631ad05d40bb926aa16e94982925e6dcd554b17d91d12ff7d694859c3bb19d0a2b011656184cb3b92d4bc0925fbc44428a629360475eb8b96c2f722f2fc330b4a9cfac1324349fbb2c44480f7dd53f2781f4c3703d73e4f6072c5b09a0ea23dc7045f46a3a30e4c0abf12b4a7aff917e029a82f2875701bf1b9e5f9cff407b73bdb58c39c5473cd8c7f2c4a457fd4b7b1e9aca03c61d647731340c7ebef4e420a9390c1a80135fb46371339db3ead528d0f8050213bffa502060d5fa62af9f539022680cd041a542bdae9d25eb46ecc6d474a7da8c3305c33d951dc48aa90c3b2bf98feca3eacad426ac9cd07608c1ef10e2db99da40ea5302cff7786f7f6e8920656a0e25e3792b6a5d0d155341b4a0d43666e124383affcdec5a4fb062b91709eef2dff80c2008b51122ef2df512d4b320de313230acc60eb06526bb48194b792ea4d84de837f28c1021ca4a055fc5a9f43de35a98601aa690b60ae5ab14c66e40b4311b08b750a5f20bef0928956e7e8bb4803c934dc396e00534d34e9021a310e735f4010b2da1c5afb5d76ef31d4e5f8096b657cb4da1ce889b812425532bea59eeb44a137a379da8ca8d646245b0a22f72893b82740dd7cc0f6c8ad5f80f9719dfb41b49aa8623056fe5afb4be122cac76aebf5b7b525a1252bbbbbb794524a199506c5067007bffdb8d2650f1c220ef0bfdffec471f5ad0472fb86630be95badae2e5657a737a75f4941ffca5b506b0aee48a570fa95f2c4e92b2b9c3b3bfda7d77f9232242548afdb57c11ca75ecb9b8dbe3eeb6ac535056d545f9e2ef4af600e507351b449e510cbb386fc7421f51bfd5416bdfe89eb13b743f5f4ba8025a75d737ec11cdc87af395e2db84355860fee1976dd427efac0d1a5bf21fdf62a6ef5f79a6f5cf5a2de4a2590daebf9e39eb5831dbc017ec857db8bdb56daa852220fc53f490fbee62b91b422bdf8fa43be0a9ff4a5dff86afc133781a0e6a63f716dd2f4417e8620f86069a3223ddd7c257e897f2b9e32f1aff4d1ef718ea5cdea57e2ab3ef5a758daa8e8872b9efa50c553bf71f137df7ec5554f411b5549af0b0dcfdac3144ad85e731eb76f2510dbeb9661d7a71b03bb7e0bdb6bdfd5b77137eb32d9140f1ad58a4e1c9eaf308ecb9316750aeed0ca4aa455ddeb8d7bed713b7078faf61a87c7baf4f617ffc66957a9b45a79ed38aee332c6bfe113f073277dcedbb0905dc3de364d7b1b1607dd81b156fb09557b8febcffca44536deeb6a7be6678579a50af87a24d09a629e392ef9b94f187a255da86fcb7d5d68dd714b8b7363b0b63ab1bdf65ae31ab8630377705b79eb129def0f8a74da2dc6b56e38c45ab74dd36cb52777bbc5b1e14a8dd7ae5d6007d9f71360f145a13e8efa7687ad3d9ff0f5f173bcc3dc86fbaeacf7f273775a7236474caf5d07b4a7b4d65c7bd47e1fffb6658e6951d7722e4f1e56d3b4b2056e2b7d685ae943d332bef61ec1433e20ea95d25a71bdaffd0fb06bfc729f9fa39fcb9b158efab96c612b813ce0c4d14a1ff97de40732f65cfaa8ff03ec15972d00a957bc7ed13b10dc7e85de3d87bb6e23c3155a566861c176915e7cd2dbd724bef2968f266d5cf99194e364f9d962cbd54a89e35496ec7ed426b75df915fa7e0d4c80fd91b43abdb8c2f8a41556e8e3ef93b4c915f27779437ea9bc31b5f4fd634bcbdee3388ee3388ee3b85d5dd7755dd775ddd972aed0c571b42f561ef5a872e5348ee3388af4c596528eb1a58ff6492328dab285470b255aa1a503a93c60edb78d2bcfdab9b79fb7af5ef7f4f373d9cc1df7a3e7e7f19d10f87074b5445c6c0d7e9b7cd1520212a387df939ef4233fbf510cc52d82afc1ffbeefc77e4a122710a38bbfb528963ffafe9ef61ffd133fd20df8babc493d89c7d749bf411edfdefbdbfbdb5b731c17bbeb74d76d7efee8bbb437f89b6bffe9507481146efe7de2dfecff5efc4417be70ef127c6a4df0fbf0046215adb5d65a6badb5d6dca4b5eeb4d6553ccff33ccffbba2df1f83ceffbbed2a39bfcf1c18038bcb210adbcf9a5e2f20dd6d4b41c5c795660d6b5dfcafc9a80513fe9f3c82f1b8416b5d89a547a6fe54cfde94d1f57a19ea0f853fd06c5ffbe0ff787e3abf8a9bfd4835feabf1f4bd58bff7d2b3087fe51044b1c9bf60accfafeefd4a91755630a2c6f502ff2d861897afaa409b5523a994ca552a9f4855fe90bb74822799cb4396f7b9e165dd7755dd7759df88d3c84293bbffaf797477ef5aeebf2ab7fa1900ebee8e5e8b2db7de3a7c85381597ee557a720e090f593e239eebae06de3eaf6fc347dde6f6557e2cd65e34c2a98a41ca1c0d4a7c82fbd6afcad477e7a91dbd00289d14fe38ba2c8d91f5f34fdcaf7a7919bb649dc1a9800f1ad9d408c7df2e87b8ffc247da7ef4de5cdf7a7f226fccdc334963c4c5d0c9fbeaa14a640b2540241101447111c45118542bdc751a2288aa27864fa713c897f12c59578fa15f8246bf9f8f734756bb91efe6932997898c0a2df36ab697f3bb07b4aa9a64bafd39ed3b42ddbc87f7dfe07fa3bf2bbd5af50ff7d4af5a530e45ba5edf26ce13b10553ffd7e4dfc909fdfc3300c4ffbc50f57f869d2504fd2506fc38a1f3e3efdc8c2e6389389962b52ee1388d14dbf229507c0e9a6f26481853ebe6842fdc8cff14f650853f658a25e2c6f504f2a6f542ffec87d2c7fe48e2aa93557b9ab9e7e29a55a915f2ab5c90490df919b1c37b9c7adb5fe8eebfd83bd4d7ce4270b7d0cc3f28cd1c3df7fdae51903060c1696172f562b952a952249146a65e57432994a2512691cf716c53004c1efd3daf3ba1e767da350e5e9f4e7d652fdeacfdc37b92a4fad879f7a55aa3cb5be7f65ebfb2959ae3c8a53ca5a294fdcc33fe51e3ea532a70fc3f22cfdf8db34bd8d6be2f447ae7d899f3f3269f3f1effeb13cbf6fdbb701683fbf67168cfa0f9caaa6a1adbfae7f5b19efb5d6afcfdaf19347efb4d6af2d0a1f8ffefdc64f15fa579edd5390d78e3b1d6a5d350ae0eeba3c79f4ed3dcbade9079dc5cee1b99e10b53dde8345d4d65e18cab230198a2d88918f51950cf493cab41b6cdbe910151bfa496558a6c346851586b4e0426b11458b1c18f9e0a7f4d62fab3032313b7d8b2dbdf7de7b33367db56b1c10fa8504d25612c6d4c3bd65a5ac6a2fcb042df6fd7a4bd3be38678acf1730d7c5d2a15bcef8e14584fe82fc0069020c37c4be5f4dfd962e9d5a99974dd3862082081e228eec27764e7d5d17fa72aa823e2581b6589d52192388a0514ae91572850bf7da28a5f40ab9c2e45edc1501f03a13915d3f87905fdb2404005a28fd4000bf6e89255e5873fb9628c3ca6366ed66be3f9394f143fdb069afd89a2fa660d7bf31c60ba39f94524a4b24165309bc419fbeb877452956e5154a51dfa594528ac97b494a55a995d3bda6301663c9a92b58be541659db3412c71303c6f831364e65b14b7eefbdf815ee9c531594527a535980f7664dfbee3d550180100260d2d746f844385591711ee13b559131c618cbf8c427767d4cee9d5d5fab9a04967d21a1c2bcd4171258324610c1c50c400830218d2128f5eebdb7de7befb53033da7f5c49c93e5f7cb7abd54c0b161b538bed0c8fedb5a823b66fc90fa383965ebf59d75e73dc3ff7551a9aab5b1cdb734f632e97ab484df74f65b6afb5d61bb5a5dfe83af75be980950eeeba68b4ab7ad8102859b0a6f6496cfb2735c21bd03de8eb550af4b7b7fd769a7fdbde0465f35853889539bfde909f746108c7ed4d92dd6ff9d5737e6e084984db1cb99f247bc671b9684d266f5600ad31c075c95f852acc9ab94b62d7dc44d396adf4f8aa6a2e8adf87250ce17f30e8077f01deef1206f1bd074b187e6cc4f71e86f1757db0865f3f0cff2b5ff85e2c57557c1bed3d58be20bef72f8c3835143cd5ffeb726bd9c3e6fbcd9121dd3835237fe1c7663c77c45f40ed810852e0aa812186d225be58c600838deb72c7fb7d5872db0e803b61381dd7250b2ae338351fc7a91979f89b8b5f790a1fafae1724e0725db82ef77b2880d6b4a0005a337e7f56d7873ffcf14f3ac32ffe7e5a037ed5b92ef7a593a3f3e2e17f5cfcee75e55d09a4b6606b0cec61237e58bef042f8615961d7e5be02684d902a248a4f6b421bd7d61a0ad6d244f6edaef0d75cae70894b1664dc90a62d9ddebe1ba5efb84e879fb84ba4f14f13fa0c2b73ff9e3817d3ae476bed577b5f5350013840c06488d2030a7e6050831a6a5dbcb71c2c2dbf7cc7f4a4b2c5bae01f79ed19bcb185d81f2782e0c731064ddcbfa7e0a7b9d955dc62be6b93785b8d089cf136c3da0f50e12a13dda665acc40253746f4807321b9bb5f6372d631bb35100032040c90c329801e854c90c16e85fa743538460804d5f49aed3d751846b686ce740654045c5c13e55c18f0e4f5800072b7050639f34b77dcab5c049b74f39fba4d9826e9f72f6491c683cd217a1765ad63e6b9aa6695bf9a376d5c55a0eed2fc6da6fdba651a1dacd8ff18e7c1fdbe09c31d6f8cdf8deab84aec1f42593c67354beed0c80e0ef0c6cfcfaed6fdb06e4ae706c5f7f03752d7d046851bf5979fddca903716c4f5f3565035f8361dfaad5da07ceae9b7ea5bf9fea18706c0fd2edfbca936e31742aebfad35a87347cca3f1f3839f6f7e0d33eae4e085f87b48a62597bc4e0ea3e70faa67de0f4ad0c3f6b7a03fe87c3b369fdfa2b3f6daeadb53c6dea59bdd7fc8441a7871e2dcfead592d2a2ef13ad51d7fff16bcfef2bcf1de4a7edff38287ea0b712df13ebd3174b1cdf8311a0455b89a3c28afa56deec0f576a75f590df94def42b65c9644295de549e381c2dbdfed2f7a1fedb5e453eea374e491e21a5281e220dbf5da2be8f8740657faf5f1791dda924555df7df47c3f7815a6f1548edf50b53e4a342be81e0f621ffe817fe2986e24a0cc3d7fff1d56f7c45faf13710d45a872208aec0fdf44dabf0c7075f9b78e9e3a537f1cf64fa9e6edea3924a9bd4afc2271ff56758da908fe2418bc24f71d48b2447fdc6c31ff9f6294e3e056df6a37609bdbe056eb8309255b98a88528ee8b948cf7f87f4fa39db6b6dce91ff5a8b69cff4354e2f8eada4d7e55e3047ada70dbd2ed7a53c6dbfb7fc51fb75925fef20bb67e54f43529674fd2b55740e27e0f42baff11c5cffe55bd7fdb661e3df4e1c0ec85de1d0b52bcfd303b9fd06f5db736fc376afc317450a8227dc881bdfc014177fe3357c31fca7e7ee600c38f47ffaeb83e0156fc0a75dbf2ec350ace0cdf7a00f9cfe7dba82ff3dede3091faf95b2c8d71f3fb7df3efc522d95e7f6f4ce9cb5ebf2dcce1e1270f51fb7fbc0e97a8ba1935ed78fb7503b69f381d3357dd293aafeed757913bef85b79f33de88278bad0f5b370392b8ae569235657dfaff9c942b8c5f2ac2e6e2bbde6e79377db1eb56f0f96e70ed583f9b717fa383ec8bf32ff76fb0896f9bf9bb10452fbf69baf48235f910218df272690190111040840403c403c4040f762ced35dd6b411cf4867a49be55826d2b4cc03e444d6340c94cb8d31d0b5cf536bdd382d73db0c1f60cc7ead09449dac012f27a783d1c524b2a8c304880990112346c895930a36d6364ee3b5867b92cc18df274a5c8c374ebb50acb5b633825d23e3f6bc7bafa639cd3d1eef3178c3eb3a264050ae14186dd3b04c33da360dca927b2fde625bd39ee3b64d8382e47e1ba7618e52da0476b1b53b30020202020202c2196b1bcfb6cd7fce3e81b58c392ccb465dd165028471c8187346f932017a92639ba7bbd80cec84b2923052672040878db108c63462c6b2bd5630016202c404e8052e80a15c6c635c022c6b010b56f0840a52e0045113285032c4c4098c248999a0044b20512209248448300211001d1174c4082c04403ff79a2000242b5801ce3d414195c868c4d833167171d6b658f8d2c21765d9f015be7a9871b3ba84082ea6154130c6312b8244d1c4071d10807c7c72f0c9c1c72767ac1d397224e79c878872f0c9e13bd8811be6cedc5bc2fc3803226aad59c3b97c982fc90c3b00fb159c31c3cad019946567cc00e2e2db049ccd688b8233960ecec099067c7c8e1c3942fa004511c5c5b92663d2c8c8c80aebc2e2c8912318f6837163e103c76df94559f6f5a22dea15f178a6f860e144144d724ecec1c287582c16c3a2d4c5a7d14c9bc0f0056ae2e3e3e3e3e39383cf913ff236623c642de3289cc8d99a744646b445bb255d91cf0b72c65a93a72c5b7d80ba0fd41eb76d4d320c465936d3163df21dd19e0cba23b986293e537ca6f8ec8c20a0b74bfdc3944bec9188b1c838c4f881518851670c62f4c0d88191c808c4f8c3e8c3983372e0c8d8c3c8c33864dc61dcc0a8c3f81a3530e630e230de306660c480cf68c32864ac61a461bcc018649c61b4c0f7df84d036aef362f7e27c2fe775b7e606d55a2b91082e260f3cd90b99c97d0ad41c2263006bcafe6b5003994cc775b16f53a0322050d33e495692bc34bb74cc807e2d05fabd1c75c10f42def4f35bac15f100085a6e02c348f4f4f4f4f4685b5050d02c96897a7cb8f8f6dc1e29b5568c61cc9b43ad55db342a2c4bef404f4f505010090514386b30da93240f32102528280826c3b8a314e938cee3e17aa25c25504429128b723110141444051254f4f4f4f4f4f4e0ac693d390807dd6b3f70f1962fd6a0702267a3a22bc2b787c9c5176b19df9e4fdc21a83d8f0addd5cab1f46e07894121f805651e7a7a465717324680e95c6c51858a16538ea4fcc0075944c102caacd61a74450fac78c2831de8c0490e7070832aa8a8b5f6d8a089510d68c0640a2936d8ca49c900ba25696b08e5e956a7ea602b840c8a62f07aaa7367b4082f4625348119256912be60b1f0a5ab57c99ad2eb224142bfa010dc0cc7401d6e565720828b5992dd2e00b6e3b0a8c3cd70ac9be1188e9564438c3c22fa26a2d7e572a850a7ef11dd190350226f0a6f0a2b737a52785264a223922449eb7503fc40e419611900406350614615c682a9032050db7d57fb165b39f578c76d2f482dc3b4fccb30ad69da27def38bc9c745995219d83fa96942c9b1faea66b18d67a23c946304b0a6fd193362d703f56fb6b556d3fdeaeab7c4b8fc5a2546a75446db3e6f25a9951908d2acdb87e91be78ddde2241d49882495a403ce3a65696f593a69663fc0f2826618bed5561ec89553dfdd4767eedb92bc66a9897d12800056e6a441630bdd99bc6512f4d325c36cb027497c61dd1af560d7dfb1178907969a7949f40ba6c132cc4ba25fb14bc3b8b1882b57bf2ac33de5f20d23a5916fa82f96c79ad62b624dfbbad6ba554f27bfbc571ef2d29e089733e4864a23ebb0430eaf2822a57546e4d1bc22b6a7ded12e424427f70044a79f7da034f290fa6273449e3b9373724ece119120b132a7a884a844e715d1ad77b4951d514e2e9287541ad9870c44ee21e7e0224a2e1005dcef0b09c124e8670d6558a8d3cf43ac0ccee94ab043a79f75a0343a24f5c5825062b26ec1596cd372907e764f087544dd10924aa34bc20465a566bbfed9c54a2e0dd4afcf685bbe4d601a2c7f13182ec10b46f7ae769a5fdeab8b51d6c9065984f9f9f981f9f9f95a03cc6d216d5cf0860dcdfc14f0b576c7a85b2ba32d0d1fd14f93139d931b9d5113fbec649dccca9c5d0bba16843a74fb2776c286afaea85bd2c93a2359b755d899ae0aa3cee8fe70a1910d328cc532b1176d5dec8224b2d75ed32ebef7b38e4d7223637c236b7893e2459263df532ca32dedd25d09bd33b241a52cefa9f75b0c9dfb8d5296c7d23d6ccd6d73726f8d6724433c138127bcf45b66a1186cfb202ce3689b7fb015e6de6f792a0f030398bd64d767c9e894d25b4b9c47d0b5b650bbb05d6bdf8662d7df2fd8f5b70b76fd2ddbf5770b76fd360b76dd2bd8f5f713e2b755b0bf9d825d7f3bb1eb6fa25d7f37b1eb6f149cb612d4bd7b6833b14ff06227d9f5772cc6b74db0ebef12f06f2fb1eb6f24bbfe56a2e5db49ecfa1b8910b690792f07d3bb9f4d0211be3d8211be2d825d7f1fb1ebef2012be7de4e5db4660be0ddb257c94b6604a80d98eacb5d6da1291ebaad65a0dc0b0b25210f6a1e85b61dcfad755df2eb1535451abf45a667bed25e2561a6bf9dfa66c6caa5b896dab0e022726eaf471122a9399c0483a7d8c0495099ff4f4b1106d8d4f3351f7347ccd0f17e4c77b1abe26f5f4696b7b4d6bb516638c7b0e676116e1eca4ad984b380ebcb1a37aaec46984a2c08a10c287ae830d244dd579f6cb21b7bf18a5eef19d189f394d1165d13ca171426344338535ed1344b18eef04513d0d5c0d0c29ba7d1a19d0171b83fca701a626f5309c184aa282a31965d94fc120da6a541ed8e7cceb0a55d8ccabdb279b4482c183c000e16980a9299fc710a92ff56b8c4ecc105e0c4f4c0e628c62663151ee16abc631474efa69f6b3150381b7945001d82f79ab095a827dce5b49e80088ac69745d2c8cb215a32580603c8a211233448c8e17432486272607314631516266378627862786278647c7f0e0189e98a37d5dc85c2f0f8944ba7c64f1b08c60c15857b09cb08e5854502ba7ade34870b18c60f1b060ac2b584e5854584728166c85053bb1601b0bd6b1601c0b46824b974c26ef020000b052a9f4b167e289780ddcc55f7c0712b572da3a9e439f898f3d11af81bbf80efc453e11ea89569ee8f444db13754fc4734ca7130a5c12faf2848905863f1d16587fb10c6179757b5ae14dec29fe93bfe33b2c1eef81c5ff5888b0e8b00cc18d47a19470c6c2c362c48bf9e391293387ac8b8d593388dd8ad55e89866294c542a4d2a049c2320412161d2b43830413dd3ec742a45bd76c3c0aa5b018c1c2e38d47773c02c7a33d1e8d47e391c85b4c505692b21503c13e55aed79d6179bd54a7ef04dec87f0251c0ddb4729ae2c8831db3a63984326287dde33bff1ddfe1cff11d1e6486ad8b2ebaa8f9b99fe27866654eafd30ea79c530ca84bfd7a929d8c4e5450b37e45d5957aaa5bed6a3d39e9b596459c87595817aee7b7e00d5d86336bda3763668c46680724fdf4b2323448849cc6e5e4947367686438c5e0e824bb3335a09ca8787232b232357400ebf651e5f9d2cf53ce4a799afd3c9d4e39db29a73be5b4624f2a6b84b3a3f1289c15f5b37544d462a29564e6c4688a226b5ad9164d65cb948d606465ee0cb779c6a39570d62d4af5281005dcb5eff8cebfc77738f79fb266acbed423fe3d7eec8e5f8fe38cb246593f69b85a2ea02ff663f0d6d1ecba38a982b78c5a45ad252d1965b5885a43d66c41d1ed9bb14aa395a4c58419ebb615dbd9e77874129213ceee4ba350df57d2085d178b9ff4001fe9f8a0830d3298b25d49cdf221a71142594794a562a1b181ba7a8d29b406149a4517da31a253841047ad986ac55bb2eb32d2cc4e9a12a75d05ded8617955c777589e637991efacb890af493d7e52697455eacb0c47a132de0cf74fdb4ffca45b4fc875b1335e13d4c57a314f66331354c683829af6c13f5fcc93dcd52b4a8d475b279cd108d11c4153e2343c552a0d1a23f465061a9f6edfd2144113d4edd955a961c46b1409b93335a034419d74fbb64613ea02458d296456a6860ca879bef8d0ad0e6f43b7343278426a4c1192e315f5b0ac311b8f4690519635652c7cac625d78edf5c537f26093a02ec723964fc5d2afc777540f83b762ad27784bc65b45d7c5a6b890af51bd093ce1a57f65ab0568efeb42561de3058f5182ba34652dabae01b0ba5cd53bbea37a4f95e23b8454c9c2f2626bd9fc542c2c2f5e7c2fc01be612b3c894ad5027d04630752da3637d2aae6b8e49f8549cd6d4efe55ba9b0cbd52fd77ac77756cfad5e65ca76d08ab9f0dc191a4efad97281d1592b567fc5b995153366c6cc5838bb33dbbad40f67afe5d3783456318137c2d97814ce70c61fce4e53cba51289441a4799b904bc311ed5609fad98acdb6fc9accc391e751b1b6a11b59eb03f1ed101a0549cd6a438adb9dceb2798691c946d802864bf668b92d5374db0818057af7449100c8bf5f31395c84085121960fda443bdd68c2d75d2eb7fa65e3dd0cf17bd5e9315db5e2b0ad11e45ce5a86691996731581c638e3a08c83f085717766b335376c0d0551a83608e2419bc14887d671f75e6b2d06b6d8a9125712598a102871118101d60694b83ab06960eb4e95b836b0f5a0c475819e1d763ad485949d2746764785d006a338a28b26ba38628716265de820e9628876451736146d930f72b0f50e9eec8f061a145b433165772928b910d2a182cc46052d082e78645c14e92795e51b70f1834c07172f1d2ac86c5490828b0bc840b0c50ffa49653a645be86089fd31d176b04199165bc43424b62882680b2221d8c20620aa6cd14f2adb8854c142e653e506fda4321db22a33a842d44f2aab22822a44b6229beb74a8ca900e5471b9a21081831da8c83212fbd4e9101525fa49653f1b04c25ac657a6c3468515f41046a3cecfcfcfcfcf8f8f8f4f0e3e39f8f8000101f100f10001dd5bb7493f7df205ed76f4773d5a0a305d2b5f5873c7fbaec704eb92024c4fcff5a1e597b0b86224707c701832640917078a40829381293bb8401313e04027a84a0f1888620327ba6802060fa044512444055448d1a1b2828f5009025379f5dbe910151c6a0ed7de8a6f78cbbd6330a60c93a3c1fc381fcec44f6ebcef8c0c134802bc699ad4a42daa89609a32ec8c0c738a89a94959f64d9a71c098076ce221db5a37186c8c81b00cafc3305ac7d50a838134cd0371e4bfdf65bbcd60c8907b39237eb08c9a61eebd303f1a14b2934c96b1b671dc36ff5f10050c9688af3ba36d3a5cc813eab633ba1b0be16beb14115c4c8c739c3aa8248c998c6aea14cd000000007316000018140a074442c1348dc33ceff10114000e6d984460503014c6035192a2400882208a8118638c218630630c32c850911100ba255741a962dd396d142aad22cdc62a4aecc8fef5495d80907ac7765614fb9b1fe42104ef2d2624ec8cd91f9fe6fa803cc00f765782640460471cefa6bf4a7ef47b0dff8e3100ec06d08aca9416f1ca3f945ee1e04c4ac16625b503cec88dcf58a7bec2d76050be03a17dfd9dca3419b15e2433009d00b953f02e05935ebcf2bbafef4263be6692e3a68f47012100106e8857ce8c08b5c75bb567dcf9fa8a9d77d6761df10de046b6a7a885f1d64d7fe853b4a032ab1ced4b18a7e66da6a76d8859f99ae818a91a2ed4703ff30d841103e3a54a551dd818b0de4a5a3acb1433658b2bb832817c58b9da6e637cd2e222e51bba2ae88f6e6a87f0fb349a2e33b755b06eaf4f4d7aa9db331a14f0555759c6f4c58e23dfd939d70109ba147ab4db76fc5a26a7afe6e2392152b40fe340e85d9a25c2e7ee3a32a078ebc8a81e21ec5eb59234bb5b58416d67fd227d16238fcb0469fd2d71ca8a471d978cf573c0078a98bf6684e60dc035ee5ce58f56308a4932b10a9eab8e94f4d6af54e44e243b814ed47e5f162ec7012deae362db91bad29c7d6e0eb509e8cf356abae7315cd1f58058247eecc8b111a34ee0528e01ddd1ae4c825521680b561857c455f51595d5ba9c16e2c4fb6502f044093239063e359ade3df92e620bf02a916d9bc4a28c777bc53baa80bddf5ebc7f799d239606411c811f011c097f4cfe031156b269cfcaf164c669363479342516fda0e51e069c97fdc2d4e63bfa07dbaf42d233ef07aa9b00ed6fe9c73d4fa5524f38f1a4d14e9474f2504e967084b8d2091e9d788b596e83ef184f2e04799e0823a97ad64a502e27a97306576211a19911b48798ba1af26bcc938561850dfb4e50122af0073ae26a13d59f5b759f139ec54fb9d0a4f5040e4f9c2a96a1f01d6d5e6215576e6ba39607dad0b32883019ba772e021118b4107b32a3c4e47adf78cd06ebb088dc323559329dfa080d1dd4fd0622a30f79a4cc732508d367a1e0072f62314388dc545f3186312a968dac22ae69a1cd7844963bb6125d91631cb563c57022f135a9745a806de3e13c2ce9d8e72123fd989d1e3907641de120bacba236a2d7cfc0f8a5d0b10e5887373466e3ee8946afe6fc676ff01bab0a87152dd61c0938ac5cdf05055575541d57b13a3ed60ab1e1899c1e85864c0170111f799be25a3ef0f601abaf3431e30da931278c0a82f62ed71a40477a39729bf2e6084ca703553fb1ea3f6f42b63ff8f811500f7fa0a0629f92a435bda2f3d032ed691612c36d7563d49339c4498b14463838e1a94fb497d0b9fe216b7f32577fa06188c59c1ea8e8949651eaa3931ca05c5444e0787eb5e791f350f0db8334426ad655201c1dda33170934696e09610157874e07e3ce5fe8d2324caa9a883c988ef5634b95001d369f0c330a27fd671013071f7f614db9793a2ec898d05eb7e44bcd89657d8801bd35f67143e4d891f36fc43a3864623cb47137f7fda91a15673128c514125cd377e375a847b83aac638386323ed8cf1a0f4b37ac9bd12292e3ac0cbebe92f0452c79d372dff4c5f33073c6d4893b4f4a2cfd19a5ff2b3b127e012fb87b56c4c3365ba72c14c23da8efbd614f0200096ff7be4d154dbbe95411198b2c6ff5ef22a221a094b8f400710e4003bd870284892d1b80364f3b1c7c0f11ef6f0aaf95ccc1425d9e5e3a416fefac6800188e6448f89777d6bf14e891be90b0b68ecbc6347be53e1d5f10586888ba7544e34420f994a5666c8c55397022a70c837cebd21d1a0c1d18f925d06238aa38cc46dac23722129984a5b3946cffbcf14711026a378c9869308253fb4f33eb02e91c4ae1d1c409d57caa5e24b2f9c5a9b82720d74e6bc3f961fdcdfec7d46bc2f2330778abfd0a7bbc18c10f89aad995c3db46f86f17ba39835ef81a7dd7c6f197937b30e56587edf4bdfc49fe0d68e7482138a75a373f4a32b2e9cdd6a90e2416f159aacdc2afeff430168562990408164ff6ffdf8431fa061d2f8efc8d13bb669e3e6b8102ce4183d1bba65f81e948a1b7472988c3e2d44242d029a55149f21e503137df2644fb33b06c6d48219fdfb214693a8f5c882869dfcd4f8e797737e972bae40afec95a245beedadb7292e5918969f07f550677645d5ba4c3f5f98541ee1af2da46809cb84f26a954afbf8614505b4640ebeeadfc54e20a1a0466b7f12c1724cf1d000cb6f80218b77d567f3ba31602a43a0fafa46cea5334eb6034d3704776f2d0fb5c5db892b744906f156305f76303fa9ac9273c7057e2ca24f23f5d5d323503e0f615ebd86b3d25c4503363cf2cc9d11c722f5415e63592106f55eb7135abf7e79084da62743b678981400f69dc33448192754e1e2179938586751be791a747cd84325a8aa40c4583c2e0e682be8d35c97e9fc4fa06973a6522061dd6babf1ac9b7894b5869b13a4b811fcbf4d982f271281e55bd675182c89e9c6007e6fed249d07f719febee935e548c7f932d57e29acccdc909267f62f2e2a9e18f52691700b2b6315621cd38bdadd78b432a77cd32f903f274e22c89ffcf9815eb30e24e29e14796661cd5a7fc7e62eb43e1b4b5e1498ffd0f2730bcbb7db3350058b92ca805d0dfed9eb29d633725feb1d60a81adf3c3431a97a5bf6a90b9b0fc17e446d23ce680012a2ee89fe292d3731f4103091bcb87c8ecf243a816cd094f37120c10b28815c35817dd6d0af57d04771192347e42f1ab312c55935276c7b66abfd8241ef4e03da06d04ab3d4ab699cb0ea9252562a940fac586bc816b1488e938082970bd781d07fae8f49afef311bd0d672ee5b4613fbc5ab559bc230ba2b2e895b0ada08b1b7251d98b9e03bad664085af63b49056bdf2d584b6fa3be25ae692d0ee5d9bf8736c3836a0cc58a74950e0baea5b6f2cd5118be603e3b8ff1eed9240b292ef62753f1cdde21f1425d740932d4a9f5088270887d27dba432f2484aed0dccad1fdb4d0998d363a48b89965a2f6bb3c31914b64aabdb87744564993a2d4c52d9d09eabf703be46a4d100d9dff340f5dd4fc046301e77f8ab7e56957394ec263c3fd547614f5d60e8810d163350e9d02bfedc7408f400f9a5ff771283fd5fe31129c79a9c1b4d2070d7f443d09f8afb1146ecce7fce937220768426f405497259f2b977d3819cf08038015fdcbd6fdedbb34b1fd3710237ce1bf2f527c07269fbd34eae7b468351972202afd926115cd6b214be48734ab95636a0f5349f995010bcc1970d1b7489735d7136f00eafd0866c5d4dd01d5a25331ec33b7c952aa39e5738595122489034b6d90210603a3f05fc6cfb0c06e10dde48bf6c40e032e0fd43841b30f82bc542cea7050168be9d6f6e455b8d094bf54fada9ea970825e0dec399480790c91b34fb5b71e34fb51f66221310368f63f933e639012161f72f58b9d8d60cca4627fe55d0be0061a0f2c940cd4812b79d842f81b9dadef341dd5e7ada622b179f9fdd9a242925fdcf9aa3d4e856172e49c3d82997d8291e828df3e1b708835fb0590d25b8e7d36ea080bf614019e22a280ede6bc4059df2e08e33ca8f3835055a6560328f579f7e8ea34453764912467b85b03691ce3469e9058a69f2148c2e914439a584a338cb0781e68a255dd83301e9d5917b4a5f152bf35c851dc391f61e2c91a418b9e6468066398d729c3ba485bdec46d3000ce29702e3b8642d8c37e053741c4f0b147f044bb632c4433b7c013d0100275b328f5c0ab7b135ad5345c437f79a58b817419be0f11e3cdab34765e1d163d6ff16d3e4e3bb61acdbc1f3c744c0a23fa190ee70a6cbd8c44be276ea6f7ff279489a3ce0c2a6e33832b7fa2d977eb70598e00e943c99fd0decda8d96828153ad44879112e45493b93e49f8a3da32ea344218cce771e03359f83b18384d92c7398a80476d60fb2aeb7d8a19195b4f882e22358904bfa15dffd2e44a41537b841bfad2c0b16feb02c443a37cef8bbedb60d2e77058683ee9c35a20ae41b54b4c013bac0cb358a4914ddcfe3773eb05e9e102cce4eb4aa9b60e1bcfb4e7e0b38d1c44f1796abe6713633c0703468789bafc2364dbf651d15833cf70683afe9e1ac15f9531a890c6eecfe961ef8a64e18f48126b985fe8303e8b4186add0ee465c72c1dc805054dde914f37347be0625f598c836eb6695b3e9c415d90e737cab0bd08b308ff3ebca6c255b42116c4d6d27fbb4e985da04efa3f41834fcbe6702bac7e52304bc15266cf208a71146c4c4b5741c1086a6f07b6058b539fab89813de70ef45bc49d9a7ea7c05acef373c34e2e2de040fd62ecf5e94d864f1c7fd1b6602c0df7106ef71fb09a65eb67b5c529555a9ac3b62d15a16b94e5804704460a141b539e96ea984f5846bf16a541a351a271765f17abe7e0bd0faa7d665bd9cb5b4566dfe5c427f8d605abec8c4a66157764903c4e76ab61d037544682c1a393c7831521b96c48ebefa33971bd1177e8b578ca0f788625d70687977e3181157184441f7f90e610d2f057986d7a256677a3b9bc7b49fa3d0b9e95ca79d63834984d6c420733b4e80bff2e51f54c932375b66392524cc83dc81c223738fd40a39057e63dddf4f49e2f91d8d34fbd28fa0c9cedece2ce9d2b53a3ad1c90fef2ff657fbfeb8bd18b72b49aa9180edca29529e82c88ed7f1b548076a337df4fc5267d7186220a2dd09ab408d427c54d90b7458b7e36e6192d6d206dc623c3633f0a5383e619dc1d45b3dcbc327498ef8ad49d924f6ae0dd7378c1a79974da2dcdea3e113e0a5a51c0b4e1bf8641e95302d4e50202a1257302941aa926081a808e719477aef80a7dc945446274761f365434dd202c5546bddc1ef69f09ce5c55b4eed02fc5790670927614555e3a028768a128e3345d837c3d7939ea3b08165f98deb6e94bbe2074322c683aa0e11b4fb930255119dcd197d2c0a862f9faf5b3683ea1414cfdee4a68b99123d9d26b363f60ddc7d29e3043847cf034100b170b97b33bae64f198656618c7f9ab4071701509f3f0c9fd1bae0775299af96db73ae453cdd959162f023cedc5d81925347242b19603f0c56da051a89c52b4a250f46483084b226191ec3df51c853a2f2b8d9d859e8b3fb0a57805d4033db6c18b31a5d82c45abd5c4f8b5adceea5571033dc069c616cc88cc4f392b31fe241cc68c4ef39d4f8f8aa01ed02f781b76fe011fd19c9eae7f448d71a9d907dd5743cc3b80bd12d06803f64d7dff076a6572ad618e8233010bd4c7796a6fdb3e0885ac6652dd01e039656cda462232ae725d4c65c6810b1d8c6ca03ddf490271398b1d7a59ad107697710337e95f907050dcb267105e7908025b756838218d815281608d28aa9200f209a30e9fe8717f6cc3f2ef4051ce5bf31b831e78f1d7fc4c22b341938dd6745132181e8c277a96387ff452b4f302d034a76141b47525a38e27d301b8173b280eb074a067aabb722474a0ef759902852657001d90ed8b0f6727ce414d0352588cbaa7fa57c0ad5f3ed4f8ec71c4f20666c81d8cef173784b6619f689f2bc86357fa264fe9a63e33bc2123705d52ccbe017e20928b69783f7ed8c2f05df56ea4510e44dc41824358f07f7e8b69709ea0b3e3d70018f8a04b2ab6c0ffcc2020e2da40c1d34d530023ad0fd24c042cc77a24f254ef38a72f5df46a29104f248fcd6698460865228c848983ddd055bd667ee92216e52aaea3acaa7e4793f0ec8b08319cfac1a6a50f1e79dac5272826048cc27ec0717ca934295f411ee54fa22483f4109becf8177470f63062c9c5b465a02aad8ec39a70f29b7f29476530b7c2e09315cb38577da7fa1c6d62d5a5e2a0b5d17dce1a0447a7aa5a95a4ba5797a3b3fffe520a36db78788446e3c79d2eaa5b3d9fb932ffff8242fe0da09bd3e7b804d70bdaf4fa572911eb0af15e40bf9ee00e2fd810dc5f37ffa95a5841a46d30eeb3f991cc9b6817f7ca462a8969be2dc65e5bfdb73c05b6a8bfb49673e3c79589c8d2e8c6c2799c988f9840bd02ec4e482096cb812c100d65bd10c0d6667fed17198a2917d003b7881265d3079e133b4f3c840ece6ad666173dfbe9caa4291ea502aca1f8ef27f87dad7c3375cc9384f6877b2282cc341bafc0643be1d1c2849ef4651dc0e6a17ba0d5231b47894bcd14749a40b95a1c18746e5131b589d0832a366a36fe59e5704f244bb111e919d8989170d2c8b366c31a46cd9f4d13fee939330a40b70429f5e72d1893cc5b883452d7a02643344c83a0b0f9678f53aecb3c17648f34b62c3b9dadfb2c2e63c01505b38873caae59f136aaaf4b71e8bf93a82f393ccebf94d54d5b4348877d2256e68f923e02def83a6b2ea17d9974b18ce22b3e93c1511d1e9a70c30d5ca22b8f28cdaa24c56d36271a51bde6c0e879fefeb080205a3e8228dd3c021860f1b8834185a3cd646d7406b7e6306a7d608232587a36992f6948b6238b05acf11840abf44706bbc242ed6b996ce3caf540cfe2894de9bd8556e9ccd06c331795289548308fc121b9e4600e871c8e5850a744bd2d35ab3fc0cb83b800514049becdf126b140832686f7c13f8c0db01e5de598d8b15777274666318e10283ad54317c41616f0010094ce7119928ca5364380affc55438df737f909c88540d001641240d051afb5b4a60b64d18a6e491dd11a45e328c28dba2296cc0b25956c6ce8474be988f3630e3c952b0371721f745491e6239c8c91a42e386b1e4c136427ed869facfeb3e23e357d12877e8e7f074a353f5bd057b4f210c882873fe50d16a69f794c9516f2b1451f912da30d9354716686dfa242cad1aeaf98ec17922c3fb485c8f20e1718109a1c5895d899d2324c4c9672f88bba394b9d5f0741a67778e00b5f2939c2f6406ca45abfbe7e36cafa8951e3a38b4286a4c34ea017816e19d76cd10736989b098ee1a36b24e2acf2353b410a2bc3a0e2692e6068ee8f9436001e2bdcf62e32050c028fba41e75bb802104ba8ed9d8dc442e3503a939aab45e03584966032a2cee7bb787e92a054e3876483f2364962908edc1e8195067911ca1e25217db1e7e967d9b717aa2f01885ce983a930d6737b571461b312803d3ada025144820155387ae803c816de9a9da7ae07fec86963afaa8707a8c4b4413dec0143f0fcc4117fd8d1cf77086f7b8065c538c06906fcfce81f36dcdb10f52d29d25db0f30d5ce9d169cbfcbd04ea47a6ecee1cf79c8db3b2385a542c088656cbbe58874320203b6b4fb8d1172e8f1dc1cfb00d6ca310e185804b07175f3c59245659f4ecae46068299c3d6878896d59f90cbcad528226168b9f2d6c05bcb161ad377b281fd91950159eb70874f317d4ebbc359ef21a5abecbaa43d392ab86830926235c21d21e0d07a6da9aaa8c579ae2e7136ad095f716d8de75cf0012e60749b25bb1e8189607ebca59a1afa6f3c21c1b55fab28270c2c901b7789e2a0864bb035804f3e1d9424c61f63e08bb67ab409b9f25aebc829960f5a85219cacca6d3a8d859005f0f621e6872a8be7f0b988c2423afe2dc88011413accd46f0fd2d6c5df8312072c814a9ceb25442e38c32980475f9a0d28c7c7d31548effc3e9aeb7db97074eda2c5caeaea10e6c58cc02d5931994628c109d6d1d252cbf82f84f53160fa89c302d7b9341ddc47bc197904b547337f706f32db249424e697c8b7467fab8ba082e142448a856959576d792d6411c19024c5b83fa740dc4a648cb880adb4c25ff68f30f5c9ffc6b83ee945cc1143c06a92f25fa703d8bf3c4cc6f2b9dc9823c90e697bbcc4b903a44f5adcc607b81174e9edaa3485db3464aad1c6dd9787d81ef582f45ed3b6b2fcda689622d8de6888cd17e6d93687cf88de1c88ca1e13e4823f7e956d609a5e727bb02013718ab73bac4bc1df6b7205d2e1a7bc5ca9f3284be01b7521a78e210e65f9acd03bb84f0134373a1fe2185852d4d49748526f42085d17f78864536b257597c5eebbacf9000e466f62588776611203b1accd34f59cddc37dac9413fec962a241c8c6567523cf688a272f943612c789ed8ac3ae9f4514ec2c4ead5623e401252b8cb9e405d248025f05bead46e2eeee12a662ed69e23bf2b4344e50d8715119f1d0f1efb07a0697555c98ddc55d48b7c0b6a258f5d34718ece08d1fb7bb52508fe74632774ff0ed899017f878372ca7abcc30fe5271f942d7380669cafee45b2d80ddbba40febf65e2c6a44ed048071af10ec24d384d269fc267aa81cc9e16d51fd4024c550601a91af9203529a37990b4aa634a1d60ce577405e540c78878d66d08c503cde17a6e17982bbba5e08e90aed08a0380fb9681320e291818a4a97e3c4135a3e147aab7db9129e7c71bce4b6010d171cfc6bd23f22eefaf1fd3b801173edbc0911673f73cd59d254ec0fd874bc3433976c5ea5482baa3027bae5107c6e6693c004210c0704596eb15058d37292b3d9b885420ae91401085124c028e231ce5327bb5012087ebab84adf11158f7e8e1a1b6430815f56ef5456aed8d0e217ac6feb7909d1fb746939382836285ce89186484b268e2e5b01da5bdbf67f07cef7cce7a97ea98093f7f53b3018eb1579fed213ebc03a3eb92a5ee9000fce136e0fa93b0c15be2254a0e33054767287127a24d03fb5d032b4496fc9534d0b2825ab847da516663b9a13a52bb29c01b5ea612e2424c21be8ff60955b421fdc0973368459a85b92f38fabe84f8bb2ec7a47000b36051307f5c17c892c3e482f06650a3b89cd72b345aa77d280fbe2797d6fc07343b79e97b98ae34b656a6317e1a1d1934438f679013913e009fe5eec7a8f15b575ba495ecf58a31e769737f17e2a4d88e71cb0421e3fbe61fb4ecacbdf76c342e8c7aa5e3339222a173a1fd5604b2202cfd070604680f21bbee7392b4cad3816c7aac61d037db4b8190ad1053c12a7339fa72dccab5c0df3936091713b9ad9362732117bb9de977b621fdd9835ec661edabab8f1f3fd985b48b09ab1ed4d42645b1fd0c8714b2950abc8587656d8b8cd7482e7e5f6aef59fbe01e8cae62e1b92fdeeb43e5c32ed112546e8563491bc328b624391948e799d65a6fe8fb225dc2e7e2a7ef45ea4dafe55f08e3d09f535149a1a0f9559f83a0eae96420dc1203343b1ab7c9d9bf8aeeeda22f8a224971a5b6fa171779d36b809ea42fce1d691758e4348113034c7f1deb9929de3ac869713c6109aec82c0ffd271441d06c7ad0f004f8bde96d52f22dc6051f6f7ca4d28d586db4e81b2932bd295a21cb4b717bcde59d4a44153fef6e7b5e26560990f3323d55969ba88c58b704edaa52016981bec304494ed7763b8fddab23643195e3665fd601469595b280e96ac9eb68e381c7ae52b836d2462a2d4f6997c126fb4030a9261c0bf6af6cd16b8f9ff2f21e50d365be14ab948580dea07e3470dd7e015e2f9ebcdec7334da3faac1a5a8cd085d0559b25ce76f52c31473ab2151571b621cac42c441e07473204f3050e6e857ee95441958638321e61ae0aa468b13fcaa33b2f0ee33bf05d90f3306e018335690e32a8799d5b4c854017d86cbadbfbe41a3ea69bc1fb05fd4806b19697532d9d68da0e63172b7ce95c4bf5fed865a5c0ea17c064c61c1a12a830d1ec2e75286872ba2628bbb8d16b57f85185c1a307cad2e20a73b4466724ee757b01f665e538b9ec788b7b40fec9316f6397bf29cadf88b079b80b8949fe59033538e4ff64d2c963467b07ef304522f43f0bd13584ae68afba23557725256731afc64b27631ac460396786b2a105b47e6e05ce0c64d86f0e84aa96751441f18b0a6d2c904c6c88782d758eb9406d53556c92ef42e2ec953a059ec3102755d80d428d9dce6f203cafcde921f01cfdd152682c3f9b3b1424c6ade902f3db57eed96955b7a395059a40410e3a32396a8c11d8ca22aa9cda24d0a1e3b35fd2cf0b8f3a8e5ff23e80dd60df39015b77a20e6ce0b9ad881d3f6cf975c3d05dbf029506636e924a8677e82f06cab34a96741cc587b440515a68a01d0df0557167737224fb5df9208cbd3e770985cb6fedfafe77607cc2a6886c9af6b783ad59ca20ede9bb8c6186113c3e771b1064e51a6e4ac68516fc216d57803be9080735a2238b89edd5427e134da0e461fbd0591320c3e364fa3690db7add5cbd8d8af5c3c309771fde56984913d17e8bc381745922c736fc15c4d29ac1cb8d39b6b62f95855db756bc6a9458d5180a12a151c6b61358da12aa3ea0add176b988860423bf30696bbf7e699640f0849ca164aa6a6878d10bebc0c90dd907e9fbf880d1f6fe6fa8291f708641e131fb488290ffb21580ef428e540a288ff18985144ccc27c5ce9c5b6b09d8b4a4c826ccde64e267efd792bda9a974d7f214c8d46e1f020096e08f8d72cb73dc0c87673e73d3ba1c935040bad751cfc4067099ea7ae75dac229eac93eb8a60ff7f21a1091aa78d5b263a0b9d42db9d4dea0d46469df0e2658f077a79e9d725f1cd783900162c60dc3b4f571747404eba101ade32957cd3a384406d030cd72dcad3fa6864891e03a60a44051ba44df9cb34a54611363cbc29fec42b283b511dbfd36da49bad11f8667de91fff9649cf88960dae8fc38f56d8670c36569cab1d936ff8aecbbe74e956d749d9932b6627a59cd25c8ddae170ed27c5393e4959f23dd8762070e9b75863537767dfbdc8dee6574824f097d6afd79a2495ee1cd4c64ba0003c72bb636b585510bbea85dccbb88206ad5434d10bc6dd7329bbed0a1a7d0203d1fced722b356859507aa42e3533b74d8153cd4d6f3cc388c58e7a832d9735823a526b04e614be919653ca3b735f821c99a9748afd0a658b559d16c418cf11ffbbbda5f918a6e29181d7b66cb940ce22e1c25caf7f5d9c4f92303d0c0cfc8bc8772e1ac402ecbf896d78e795a9056e64e93a985f83e6c7a6634f46e38a777a6e3451531246114f31bea107d9a0681cb7c9b579df2c959e4395ef82f1e696b8813345b795a535b1905050f7b7570788165f51aea0e9e4d2cd99769646f46236b02e28de378e96c9e93306c2e6ca9b60c2d7332cf62f1897b8cf116790b2bd6632f7fda3e763869c0f4100c9d7f94599c6d443f7508484ab5b5a21ce81638361605cd2db62e1734522471f6cf7d4eb9f9c453704518e6b76b568f2607a54b6d46932af7f516beb03486f4ecb199781180e7dfc86b9edeeaca039ab354dd50f32f782a3ec2958d3d768eb4cf490b35abec0bbfb394672deec9a255bc12ba5c8ca113dbbc9b80bd9f633f90412fb8b4affe315f0f0a67b28ad23d563897f7bc2adff692e1b9414698b477d7aa80d3e8a4fe820ac9454d48d7db55924a1d87ab0a26d7ee17c7f1e36993b2ac5ae6c97f6b34442275f7178e9ad4ec7d82aef6505031af943725d05807d69c52cf3f9916a8cfdb22ac82483d58a9786277abf15093c1e95edce3a85e6ad0de89e471ecb9ae02b362007246562e49744914dbb580d3adf7ff7447e04437929fe81e84a2db479338d0df3bf31531138542d687769a53f7ee4f2faa09f0b80103edd03f79b5e0413c08a5ccd07536a7becf123f0dd653ce19bf0d8e42c7ebed0152079b6691a267454e6abdec6a3f2ba5460ae3b466ccbf55d4b15bef4715bc06b760635123b8ab3f005ff8a715062ec03921f58a3b3e45747d3b24a7e0c4adef73380c547077b2bb0caacde49669a8dd980372aed328f75cc67e7053dc9a606097f9df72d4a604bbd08a76ae0e22461cae7b348804ec6507d217f2a261d5608849ea421f613b75eb804b318c8699c6601bd4c756b02aaa7c4f12d04ec85f3da17200b395f576c2910b14597cac50bb975e9f102c3cebc72dc2555c9aa0260426c3d5111eec55c3adb26d88728a5f5029af3569f8fccce56ad4299e06c8266043c5f35d0505ae7cb841e4f233d4a5839ef701400fb84c2e11e6e77e46ac555c89dfec3e98430cea64b0f7bf5d012a53f9956db252f59f4df854c8cb4c526f41cbf1717c540a4101c969dfce1fe43660a95fc1674a243a41e92e8523f49f69a169c242d689049e7ced5e2c355a3f8ce3931981f5787f9f4337fdf41a65fa18d7439a26bfff5a012915794af02cf5b1bd72bd2da94d4a57e25e0ad744f3809b658c666e65995e476b78aece5b8c924819c1ef4737cfe0a395768203c69d789d2479e4b05f8a06eecfc979a72d8bf6dd66b83f88ab5a20dffeaa292030152327e7f79020c57303903b857be8ad0c842b822a962a2d6426484573cdb804d2781cd37b71cbb838c261488ecd0bd44e7907b6f41e5d7be1105e46b6944189654fb4285a933f9e7ceb79308216ab9ab7dbdf445e095ed0a727a03e7270c974cc91a710e8465eec0e4fd48ed14879241ed7e0da219c1ba8691cbf6664d6a29487582bd79c2278d2007331ada38e6ce5349fe9116386608732a62c665e96ab6244c0afafb30a522d12aa5723945eace3bfdee88bad18901c2f6d66b889bc1cb61202644db54cce408eda155c92c2e5a7332973c0b398ced405e405cbecd1fea6a04def31bc1b8088a5be331d5cf0d837db4047409dcabc1cbcba61189d719f806148f31e3da11f3a7954baf5d68cc4a0fdb511f2293356980bad37559ff070404e2724dc01c1771bffaf05ea0dd929f32c9555b927aa66271bce2430dfe754091dbb7af8c679259b07dbbeb1b69f98f6574fbcfa1fb65242d65df7ad3657dcfc6451f7446f873dabad3fe018902e62eee4d88fd3af484c41fd43f2b26429ba53be23239f0722345d2defc2dffee416e2dc968c2b1feff61fece4ca95b6abf732b7060461919f28b187dd4753212ea43fd8d4c074affa76d49d1c183a54d2182710d80411a201096f73a0928aa7ed8c94409016a4deec41413a1e764315c668be3f9a99cb2f94f00e2ebfe6eb2fce5866dfcebc97d9cefee5bb1412cb3fa9ac8bd159ac8e136c05c196cec863432fc44620d256ccf2d3ff2a109469c829e8750e05aa38ad15da475459ec13d550d886858632a124eb060a709118984b18b65de7ef997a203de7cff4ffcbc9c0d8bd8a5f9bbaaaee368bdd0cfd6068f390d21d43403869af13f4c2dbefeb8353554ce175499303b93280cb681c5d283bf0f49541b43fdf8d98524c9eadc6fdb469daafca1199c5accca4002a40daf2abd5d427ff0f9010a05c4058b302a8e6290a209abeb7d27009506c76dfc882042be7c30185c2e650179335480295cce6f6c25b31b5e7d47beec69f2da0393b709edd1b84e4916e1e09d8f9541317d7d2e3781f84aec8396abd9f214f9806641965ba8e96f1cdc61bab02e10d59c14707d1ebfa05791d9a6e9fd2b7b658597baaa06268cc96e326a7eb0f678d0e4448dc3c572fa387232d39dde07b35bd08bfc383d8d5a4889744c6a1f2dfe80e39ac5e4edc318fa19cd3aa48b25177003e8b112dee2fe0d8ce418ed5f7c9f76b036a85af9745e6ec2fb6a4591fcc77190f60c378b4351ae9d76a9fba0075f253e2c026ac79fe0465e0d172ee639dd1000540bf0bf4910498b31b7ca5bda387380f2f56ce3ab67cb4cfe2d55a4116826b4b5b234b3f4c5d955137e592214fd24d89b264769154458c3d92d2b9e4f07ec9183dfbc7dec626ed5909200670076aed0e485bd1cc0e12d64c9e18665bcb3a0c59e411e334b145e9ed85c41b549326fd308f65481fadc6973d60440bb4b5d0d1515c873eda4ccc5a411d95781dd20ff1fc3cbb2404323eda44ea44e19dad904818d4f8a3a024f788fe8f038970c5935a8be02a4687a33e969240fdb127e57dd820c93ee7d805df7ada475ca13944878eb22796924087ac33342a12e197f993614a7d901ed10ed104b94650968513037c06807018176fb84b850fd5df6f745bbbca1f4b2aa37c1c2463610b35b167c3b42ade6407b9bb7df3c6c8bdeb79158b2ca2cca54f7ecfc7d926f2b1887f92b3c3d180842515e8bb7aff85f57395adefa86c169d8e8667d390b0513d5c6f2ad9981315f193036e82397b8bbcdfad234c9d75bec21c477f4f76b0eaad8e3c38de0e6719e18c4212bf0051522b16f6b3175abc21474332c2bf210cb02c3eb2fe21f39d569f5ef39bdce89d2bcd4182dd36166c6dca0687a08472c4404bc5f36e0b9ed0120287ac2f84a34cdabfc4e6f5afb88eb757963f2d546cee91dc4b4008bdcd9333cccf9566768f36caab089153e99adf2cfe2f4902fbb946c8f7c7f3bbe91681f1eb3baf3f6e9100474a5009b7f4add909b0d074c0b8e69c2a62712d86d2eba8110c028f57601fb6695ebe082436b817b7c5c60e8147d1999865d76d93a6d1334ccf0ba27c8f266924c291d87f9915db4310b5aa6b4c903ecfe9fa1dad2f0e06d08f8b993f53860935d878f5fd0a521e9b0fd30242cf63359a7ce3e76efd61bd2d2fc7b8416ec8d592b20589cd9ca7a85b059438bbd9283de53f1a5e5b2048dbd00bb2a057bb58d9510095cfd7f3e5e7a7870defce5a86acb6a8476889ca643642c4c3dc8bdc7cee6a1805dba400783ecd93cbd1b1cba7a51a576399f20af7edcd7cd0c3217ddfecbe555964001df9dc0dec26b24c7fe6c3f81c8d135415aa7dfae00ad21a5b1a09fa25df204c96bf01a6db2c9f6feaef78e9a5c938667090b2e47e66df704062412d272df4495c29a9bc098313dae2fbfd6160c44349d31f4922f6c42ae072f703c52d25f0158329c9734fbfaa28dface0be232d909d94f391866ff136f76cd3948852e807ea7e054e7c8d0f2e216cd23ecfd077f874ea174a6f5fead5adf161bc5eec43904693026c29f34dd5d2117d1f188204e753f635b6d0df1a39c4a88fc4b000f98c7305b616d200c49c37a3818674c03d6fa6202bb58ede6ed4a279e6a67fa44f142842b4c3b00a26b4d4a1012e90c0e9442b60200cf6b14c14c1a063030efd92bcc215369bbd6c03651799b608aaa6491a6ca2d11c802df2e762d323a16b8a174f46832cca3ebf576a56874aabbf08a0e8022397c6f4dec636053cd06cdcfbcee0d5c50a67acfe5ff67867709f411853ab059d05f2d2487726846d708251e6b37d26865bec23271e20b0d5614ed2bf2c158c36dd034f185eadddb1d7ac379209199a28738ecae6d9cabc6d5dc3f0a49463f89720cff043f376c36d427dd5b0d9f9f372790d4182361c9ed304b6a9dbe73b1c729ac2dd38e40f0c9d0eda7a5a8659c8f9e4e0767a03c6df42b5d431b154a1729f2b421a46dd9dff89fb237b8ad9d530ac1004c83041e6ee7431c5a5ace665142d1a5ffa4abcad8be4e4cc5e2bf6946e256757aa1dd4243b3ea169c51de54844f87d3e5edb8ad542d98267162190fdbe596a728e16a9345d853e68bd505786640df76c835383fe0cc5c2da3392d0a8c8a183c6725d6ee5169eb3dddc21ef6b065a37e5ccefb3c58ea57ef67e2fa69fc5f4921286c179414a27e55b238571a107fe1aaff1f230a09ea2a49746d3d3c04a1f9413c99ec9b99b11de8ac18f791e9b0a8f8ca4dc24061770d6553b1ac1c0d0715fb1c162c9fe697e9a89f13510b1e3199b77dd197030bc2308d0cf06196e8b10dbd0421c22c2408a2f6f287ca4a1785eb30ebc03e7d9dfc8064f0c284f5b9825ee9498d146a7e9bbb418934088855a41a2bd2b8d8b858277561add8e600dbafd38a5dcdc12cc0e68f242c399decd0252f825c505e478e33229dc6470332a74e7049e9b8ec0f1ed5dfaaafb0ab00655c769209fed3e4254163348be081359a05d4ae73e9083bddb726b6640f8a8053da3d9ce70cb984d17261ab5e3d2f821b2055e6dfdc7352fd6858b6783d68562ad59884770dd6395faf056a392e726b04be8cc3c60fc56e0e128d4152d50c0ab56ae7ac3d83207e758a4835d7a52a52868a0868bb3e80aeb705cc7583a1cc1e6469118630abe6aa548191cdc4af96d6ed31173eebfacf4b50726ba465241989a6da99d7aacde5d4c94ea5de86d22ace714d6e64685ce07b3cd52b5a08734ec535dbc2834659f61f7b287a7ba30347879cfc8abae34ffe047fe35a47912b131f4985c907a09bc3898800f79469b8de3fc149eb1a0b9f82d77e7b0b20100715828e6006507f6e2db51552b5925a55c275556ef3d5d254167f729fc37448feaa000a395bdec2bbee79b2985fb3b046d9d6d9d43f8eeddd04b42e8839a7369769eca46f6eeafc319261ae73c758bf67591fc86a801a2ac5f381e0e45df56ddea27469b26e41ca729977ad7b7bda7b326856863d02e5466c949803006a7bba3ea398eb5d0b4193d16e14219193e012a140c4c4726719f2ab0640f01a3f54335b179bfa4c5bc144e5d74f9ecefddfdee2c57a20b824c756613fbe0cdcb8a86d6dd5b4b5c6264504cbd9e1b5573d98017a834149fee719447c8b8c4eadd79dadf056166d1b1232dba06de3301771aae55c0afd599869beaadae3218e38a33c402836e1463b3a3a5aab74c51ce75a025108a260956e76330348140fb54178c1b22b116c86aafcc527306746fdcfbacc81be9bbcee96d2e25e39595b7a5952bf9161d98d4b2da35d814b8280ab15007e10638c0d1ccb507dd353ea14f602a37522043266ff2be64669c8550595a56ea355b1da67621cdc0206bb62f1aeebdd3031e4c9a41c4ef40390437007ff314f3cda5445a405c71f36c114374412e22ace41f361e99217f3dc685acd07b026adda3149f447e0521cb73978a3d73c4aba2d54ec20f32892d98a12536eac7e7784ce4e310a9bb19ba2ceb4db7e0db5141b60eae3a51a0fbf07631f863e635ef76c2e15a9d0c3533ca531821a3b94b21a197b729a669ddb7603524fe64bd27a5caade65920cd019fd95d5e74d377df15f9558f850ce7ae9a0fdc82f2743e90303da21852132710b7312f430068d76c3c0dc5aaa8fe42a2b93af50c699e536ac56eb7bc305d32b9080782324d03c0c964b90456e137a02db84c74f0cf76b5ae44f2737efe3cc64bd267be7c4d72ce673e0d221256e7682d7cf26b3b4282965200362570d1517817d0354115e5893a2f024dc380b1d76bc010ca137349e027c1ae99f1233e11116af88da39a6b5c762d4bbd710c04d5af18f5152890e63d18a24f319f65895d2292a48a595eea096e2831b29d39008e04c75b7079609f7303bfa524d846af9ea82ed7319767db73a1b5e0b4e50cea95ce815086a2854716a9d424b2de6466c4e688bae5c13fb3a9956444ead0cad28d5ab226cca7149ee50d398cc1cc36e6e1ace2a09fca06376636123a832973b4cbff9d702665e704f6040ad75dee347a36d7dfb7fa44d9325d440403ea433fc33af482c5c3a756d0a5a00b57eb2a9326bd6ea99e15aac7db40e0e749d684fee43bee9b3a6fedec455f5ddaa67cba306bfb1799e4d39d60e6b7731695e0e777b93b817b657ab10044cd89818026b9e5b47ac7ac1728f4eff30b4609df84db2d521671a5e99cad48da6313b7239ec3ea8458bf02aefb8e4909ec47e701bc5e685555e5282b86eaad32c5c503de97842342cfdd5de295d1ff6d3304119ee95212271f63cbc91136f09752d29134ecac9915d4d69b89e1991650a594b188666e3ead27b990d4326704f26ff8841025fac615dbb8172d366440ab5754666271470844dd7283e6750a4de43dc634fd050709b1a1840d58b94462e64558333845cdc4ca201811175e0dacd7c838dc15b8844e3e779fdbc21e2184480f7c4f2665a5a4b449b3e6f5939848ced3bac6fc3a766b10463ef39e9b42f939230445c0230fb09d396dd0580925ad17c0f1c614ab1acbe3864a8855aa703d22447154eaa42da21094c9a4653e3fb7f7ccad57356ff1f33a42bdf148a0fcafc8c2ae80aa33bb668a616bf4c13e2e2aa0b153a0c0a56a8c1204150bf2f73c25be484c5ff5aa78917dd70b4a4832bd8f130078aa355aa0127becf8072a232ed199faab11d0b85ecf802c1d2d59aeb652b9dd9c25c19d2b3241872f40087c2b58602bc56684d27a9a49013905f2fa64e8e64e0718c7afb9b85375cd7fac5f1c5004c880dd66a129dbbf512cca34211d080c21938240323197dd743c611d0b412c10740ab642a2499451d7955f1b66598094de941450bb7c49a09807518b7fd883e94f7e65e70e8d03bb6ff3b17fb18323802d037f0cac0ebac0d2cf80bb77aace764aab56174abfba603141ddb7da2b21f5fb9169acbe1b3c0433a59f76e7bf466857ca9848aa4a9d9fb26ed2dcabd826042ec62c2265c31aa7b1f2ada7051d5acbdac7c72177298fc3edd8034bba491673947b257d72034f38d1e37f2104ce27e0ce45368da049b39888e0c9e8f59b7164151f9f302c1ca02506f81f62416721fe0a3ba40f44334f7c910d6739d0b51cd1c160ddc8f30e53a3308dd98939ce392f9ddb8df02206fcc61295435e2611712850a5e5ad66280c67714faad0039e51870b63d43411467acc46572ac25108bf6c723d2115c2241dbae818888f9963c5cec7387c0b832bebe158fed933c744d8fc02618cc5a7ca8c19023a4cdfdf73076c51083bf83a4f3e978d47c853fefc51f280375fd37670fed8bc82266e1af8ffe4d1ca2a2c8a19f4dad717c9917d544e16d26f448a04842ac153a901cbcf7704f55d212af35a905044240824b85d80342e9b6d99bc40fd76578759258b384481ad841e8a165da19bae4e9988a9b13c977b6a309412d05f2fde5631909e315fbbd8e9b9f1aa6772f4f466274b981da0daf1845aaee11d82d755a4214334cdacbfa59d4592b72afe09d9d70acd83f4bb72e934b2fad225e0686c702662348597e2d4049577fccf39ec68c0780f96989f36f2890f3dfab988d04ac2ae3e94b9dc5435a53027a2464f54b3f0931150c93c1eccefd22bfa108c0912e864d296ea7e20ef687a5a0b6beda81537a725e1d00f06ae7e9665193a0301bfcb6ea86fcdcc00ccf630a0a340196d5c774fc7b8e9af2b56e166d3d25de61cd5d4b1e61f216bcdbfbafa920ac216a34af4c1bdf4916c3afc60432ec83b059d80b3e761c8fb8f290aa4ea1fb56907244ae66ec24b692ac309b432d20f679789732ac09cc8dc67384c4826f6840cb778c927896797c37883766f6c81908db4c624750098a17f041e80ec8440115bada1c4c3a1538955d8c8cb0b2ab0dad60cdbfc032de4705260fc628bd03a4a48523a2f9922ebc6b0d3cf782b941240d482b6383aa04c722d702e0515024affe9aeefe5d45454cfb181e0d8d31c7ad0825a32418fcb8c72618ffec8b3f3d91d828b4f5694c4482bcabfe35ca2ff783664ddee5de503de698650afa576e494f76e988e91f1a06b094137dd48bc294a454f7461db799384fa0a6bd410526f33d0d45a22ab47bcc0b8add1226ca5bc0af6799529323826db0fbc525456407a374f8ba068810eae8fbb59567a570370bb2b2c47d134ea71e368c23b46e8f0272c727851eeffe18945b965baf85c47aa467440c5ef002bead627ffc431b70fe1e7ff22fe44b787afb03fbdc5da6627fb727b6f2157b5aa8f1d9808e8b3d459ed4238e9a80dc35ddc6a1494854cefc459281688dcf51c2ab33957c4ed03a08d836ef157acaa3cf3088c04927240cfb9d617de4bc2a62cb86e02d5daadd96e078230e25fbe9b37727727c54cba63187127f590f903c55fc31f7a1e099eba5f3f0844d693b8e7bae64c854c9ef260060c954ec345850156a17424011aa5d4ed281cbbca439fa583d9881c9be6b768aca362136f2827573e10ea4821e885a36ba0f3437f40b8e3f6011081215c4d5df44ba2a652d0dfb91db2eb22355ffd611aa613e368262fd80b6e195ad6ad979ba7597360a6e028084e8fea6991cc4996b1d9298c000b180d230c6428cedef96cd9a8687218e4b5f3b5fb9cbb1fbe8668f454c3916c02b48cf96fa9138c7c2801d1b64477f94898e5607685e197e959caed8c7c4457a9ce9108caaa21258291d53a51a3b3d3142e16ad52e3cfdde88adc2fe878bd15be5c41f8abebc2d513760d189f461ac523fd655270001a21bffb13d291c72d373164fc5b1612450dca3bc4d5ea88b963b867c5b3e6439a50d40478212a16bc6162afcf7d30e467576b9ed8e879eee4ba92547c96b167e5aecf46c44b102bfe74cc1c9e5984069225eefd53849100ff0eed01ff56620d7473d3e193429d605d862722d7680c42f94411a7bb2bd6955f215ea051f49b3aa71ce20f631fb315eafafcf7fde677d837a9b0a1ce2a10a79fffece62bd3761f3a36924a38686b4670aa02d66478c6c3bfa500d82fbb991143fb253a529e2716554db09c259a2c89904aa2cb7b9fa47d58f5e9ce4386da6afeb738aff28e5e94d04b82164cce2cb2209ad0f2145488a2927cf45069bb2f3591d10d50f1a7abfb7e19216685e7d8b9c4465c000f156690ecd8402dc3f04a446f28680d01a1461628fb21fb5e4e9178c8b1e5565a141c91e37a9d92129c231cd4ea0f48f64b638887105c02db7f3b152e831ab75752554b2f14ea0ec2c3dbf028047fb8209eff18b85cc4beeb040e214272a79d06abe2545002132d515f339966aae4a6efd87753476730d1aeaeca85d35cbc4ac4d41906430c837e009e2a0874d1eb7828bc5493b5921b82b7b3ef0d527a52899c1f80c8b58043d0487c9e0ebe20dd061c055aed59337027d36dfa97fd3cc244b4205289e91f03cdf53704f20c50aba4bc3a95f45a72217266791eba5e5e193f7da4c244303ba39e2a37f692edaf3041952a8539c2f89a08e2d70084c0489cd741ca066905956925e19d4b58e6186bec93ef5324218d29c83703c0034e6460d9259e2d6122fd1c8cc60863c02c9d523db308dfa5bd4f78236b9f23f9411604412490dd797c32ca0fa9b9f9f9f140e0946a3c8fcf44276266a745c2bf3133f15f7f49e16dedf497f3ae714233dccb529484ff9d1ec4f44aeb9e7636760c4923f2ba8782194799deeb12ab513043c35f872a496dbdb72233eedc02e718f30e0601861c089c2f10f495129e44705f75207efb6c608e602369856c9f0360d00a662e1792e1919845308c3e635cf1bdc3bede1e31feef5ed3a06c7f21984de76636610bb5154cb24a0dba6359785b13bce7e980f250f02c740025a1a1d05ec2bfff6998cd495e35978330430940ba610b0f0ce1db555691b1979878435374f681921e83d91be2c1ec98d2a930a2914887d1c33032f1863e87e310ca9458b4dcbf4f6b181adcc21fdc43fd6c010bb3c5d39a0775aacd8e6bb6a5d8c723725ac92b3005bace5b6183f3967db4b6d150af76d4886fb1b6dc5d5e7c83c99be81cbf848c0a2ce2987c00d082bfadec8edf3c342dbe1216d092a0343f23c63e0823246447cd3693f53a2aea0f6efbd01271aa2013178591dda14804eaa84c44a32576ee6cb5d0017406ea8181918b551d130de587a82f944e35b309d7d119472a7571b347ebc673e2165d50b792bf6894cd7b21a95522dc9db404ed1b151926764d40624e9593ff513ab8298074e697ec00b72ee8b1036e90afc18e2913171555223960c6f253b1b587414773e596350aba3b7bf0f49d432ebe56471a6ac2b72320052318e4ff43a88f052c1dafadb49941cfa736a0813b8b88ead84d05d620376399dba728fb2381103d96394d42a7757cc63d48645067983c8cd8bfaeb92e1aa13fce8d3e24e95e41a38c5bc80158b8be53ad5dfe81505d13b046406eba817212dd76bc855e908f17d5c10c6afa7835869ceef96ef2bf15a495fd91013ec1f8b8a4a1ac80b4a06da60d2df882c9081081d966ac85e5a8089f8d5f4d034e01d4860a7c9052a94e1d3255ba4696cde346f32b642323136e30f77381234a3f953b4279c52997cb6eb4e06029f7c9e707178ddf3db0fbb1a6345a96275dd1b5583554fec14ebc08549008d9e32153f4bf77da371135748a06da8898d95d00fa8489ec34740dd2e40e1daa235a3555a85941798203162bacbd836d57a951addc89629e00d9cc39073116aae33c3ad9e4ba361caa8b536fd33227166e558d35883612efba88820706bda88bdcadb96d0886786eb4ba86384dce17abd6ed3f0feedf7d235257cdb7a494cc1c021a42fdaad2c04166611474fdf14add9580223f6a62c91259f654bf1fed63c75cca284cf8531bafa5ce4d4bf093fb4db56e33c414653e587cdacd145caf07504fc32931610a092635cbe013cf3536f2168fd088156d7410a7fc6f7d608b434f7c0486019ed48d96b9a6d48eae20d0fce6e8c4d2b6b04d83ae8a710e285260f0d7e08d6f21f07450b490e923ce98fa2b43d2e844002d14c4f907ff50747c21a3cd99f946f8c53f3e5fc84914a1cea259560c745d768a8dcbcc600bfccb9416f8109339cd92ea9ce60d8452666f279c5116722d295b026e3a4a390697791b708bc4a95f53863e8474fa5d5a4e0347eac4c9a78f227b8a1157b3f4c18a664ada8df8268c0920d7d5695d76c2506957d6d2ae32e2282abb8dd19d08092cff3f4e1a44f78d9bddbefa165444c5c485f2bda5eb3c66f7bd71997851e312c0c57ea7e6e525f8b88b36e42379bc53c03e82523f617495e44717c8067c5b4f5e2166026c64dc4039267efa5ec0058c6ec155889802e4821095542a3fcf1e61c2f39a8e15c6c7e7c2ad25346d72592c4a305316a50d4740f73ee802e240f02f73b3373f2c91d19f8b5f178c3f556b9d9f30a18506844e10c2a19632d1839f1b39a112ecefc479048032e1722720243915de62f7cbdc573d8064465f34b6c920bf5f5c255069a1f85a59303f791871f763df038b02a11b205a4ed0224330443f5ad9558460c817467906ca287798c2490823dc61ea33a10cb884aace8133e20ea93c07c390238cf20492417e2885d3851bfc82085c1afb8def8951af9612463da228944d87ecc85cf8f7516b766f4c8c3c92c6143950cc6e1fd4c0ea91789eb635806c78970d2e03447730b0c4004e570259d1a8012d287ed1d2eec1a10bbc740e6859d77071512a8b80b63ae4330dd0ae0e29f62bb730cd04fa8fbdc4b6d62007ef0ff2bfef22112ed17ea4909f9a1d2e7494bba800e97b113910067339b15c28d731361e02dba0853d132fceaa547b6939e387988790d628b4624dd366f9720996372b13cf050c5dcd020799ab543bb2638c59998a3e979f2623600528178e66a5d13fc712942fa0301edac7d0f375e1790a8c7cb43734cd742972862ccd44167fe5714a5baf8d5d5e6f8a8f8561032c0d047acfce8e5b30161aa703d5fa793ba811768a60977e2f327768e651a82eda723f2d5e825c80d12811a7398b270484bb901f74a73f1cdd12f39d5f9d09c2d1ee2140077097f26f1d07315f8cd29ebdc4d04031c67ed242dade7b6fb9b79429c9141009f508b408628aeec8de35aa65a0537ea1457e5a6d2ed3b428adafecfc0177378dd6597bcddab3a73273cef973ce6e98ec474abb524ab3a735d09f7649cdb2945d626b0c13db62c62a194619778641bceba136845aad0ced9f5aeede4dbfd2acbb555575e91d6728ada0531a49ab02fe72a048a5aa6739a0db3e846da3bf7d4741ff8c9d0beb16f25fbefe73ce9fef383ebd8f8a9c5a447c12a18b502d495cceb62da6262a4c657394737a388bd8e9a7dd1add7826a34e4b72ec2ebddca277f9933e7fd277d0bb9d7bee4d2567d9f7cdc23102a11da20a0d0a4ee974a2ac6ad2270ab4ac67e61d86500d22cb7490696034f148bae9eec4ddb5253891a2a37432414fc08453ca3ba0bc849d590b77769967fa8cba74accefccddfcc728b6e3ebf672faded6cb3f3958e99999999999959c8a96bfbe11f698b79d250137266a6469dff8fbf743158f4771c98258f7c5ede3dde3cdeae6e09358bdffbc705d5d56250139262549ddbdf1507f6b9d2e32a4226636eb7bc46abe2b2d23cda95eed1b05c2d4bfbd056ff70a8b5186119ec23ccea9fd425c30af596242ebb6558e1e7392790ccbe724d7f16faca57dd846e526f779792f60cb9bbbb89690beb355ccd922f3aeda747bc076669aeee2a5da3f1b88b750cfdc0a206fbc9e283a5b51e0da8b3013d729be8e321fae143725dcdfa8e302c8b4f0f49f60864ccd578bcc6b3e25dd17a3c2cdacbcba2f97c3adacfb7a3c1b4197a441c4fbb74a63c590f1c882520832f74c0831f04210539e08ca93b5de07241122cb828820267a4e1319d90c1ce0e86f839f204132845c07c80438023472a78051185161138dc4f8bcd3db93d52701bdac28ecf3c5363c78e768875d4706192e0faf7bd699781090adc775decaeae11077487ecf6ec0d8ef54a2f943ea15ce221a122245d48e44b36914fe40e37398e0d60a7275b99b7a9b6cdfbea356c050ff716665eee73c0c6172ed74251ee88d22101fd70c50f4077bc71391a6ef3bac5cd185cae23dd70bb7b9981b7135656da545ae4845ea2583ab690275bd563defe1df3f6e7bd090ab6ebbaaeeb703462c30ebe320c9ee13e067a3be45e7bf9edd02ac8571eb10e0985b99fb4c8fdb458fa9be636af96be7e39b49b5e053b0ad814eca6d7dc774ce872dbd31d5be97b4a8b5c4769b1815ae45ec60eecc73d4db32ceae8729ffa179ba5ead7e59efb1b1ce48395cb7dd781aaeef98efdb3836ff7fdd3ac2e1c2bec49b33a2813757f6abf9ad5df4b9ac5413e085d6e7b4ec25ae47e933f2d72dff25abb258f78867be9b3848272498bdc43b9dccb3058c7f6dccb274817ea73ef8143664b30240eaee5173cc3bd0b11dc73f3b96f9d66d1e7be5d9d44bac097eb1dae7bbaffbedaa277f3f0e7cf853534d1298b5b4ae504a263999719d8f9fdfe047d8981752fdd2ce49815625cfe6428dd28537af8f999628716103d01440b7c524044134443b4a0150bf2e1f585e52e47393ac21d19e8054743c8f1a1a776b5a9305270a792ea02495f524ae953bed265da7b971ba66b6e2acac2c8dcd1cec04437219d99cba6938da6a8e294765ebe95392b413d94e0ba572158518bebfedc97e3b59ffb4777770d6e078931c5edd3edeeee1b8dbafe3cc2c8dc0f43e8fa5b1e7eae7f6a8cebff2e1e5ce7e1cb71916ff08409095ec00316604431b6a0882ac2400284114970e693eb4da6cd1dbd1d7ae8c10eb207137c3953ae7870fb2b110984b05ebdc58377fd79346b3e8bef9c3574b0dcaed5a7a7dce512aeff70fd616816778bae7b14d63de535e284c2be70e5bf206f74cdc4d1f9818ddbd76b2a4ee988cf04764a0e0556be11d92a9283156b78b0d0d8c2652c82808067422057fd2d8f60e1c3d06d99435784677a8642686871f3c27677bf6cd0f96702ebf737776739e5cc323ae7cc40b16760cd9fe1901cf933cc49e4a3c8339e011a5a747f2e07ebffe37dbab879d1caa8e858865439005cb385417a3512e0faa8e3ba092cbdcc8395fdd9202f0778c6c4851d39c6c36be598bdaaece93fcc070dbef2972162bf712a65328df50239948ebda6bd4127392a192ea536c83186aebc418ec05be6d07a65cff4c423c4c894c672bba71c533c7a92c0405e1d537787a4494d196cebd21f6f5c1aaae80bac8386b6c5a6b675b3b033cd708531ac6d5dbef3b9757b421b5f2281a718938b0c318c1e8b6ae111eb8f00b830cfaf02bbb0559138dadb6a7cf7c5c4c46c3131a8a1a198a1989850ac5265800980bd6df961f2ea49b97e0004d4d050ccd016862512789a12031262a21303c284afa500f320aa052616f3364050075c418597d1383f6d0438aad95941051952e96d84e0508c8b0c0dd5c2f9c9b4d5ecd4ecd8a1144c2e333f33309881cd3861c68885160b3ab0e02240cf90cc360383999f19d88c13668c58d08185d60c6c0636039b81cdc066602cb808d043809e1b9b128b4d89995c5043a613482abd8dd005168bc14c2eda731f7b1dbc101804e8027b4023542f3452333cf0a5c2933a24b3c562327410b32d4241b88ce85109bd08d1104a091db1de0b812f159ea8f0e4f6c7a6c46253622617d490663a81a4d2db085d60b118ac8607033aa15f4126ffa737bd963069d2028df39218f384244ee9558bb1588c8b8c179325afdb6f512d2d68a2715e3a99629e909e5c934b6c4acce4129b124be12bc844e63590490ca8e3c4052685171aa9191e0f8d2c12873624b3c562326ccb0b8dd40c2f95424c880a8d2cd2051d3f00c2edd76864b9a8a1d0c69748e0c9e43254148b0db9c45490490a5a0a0f233314b3289914b616195b68e34b24f014e3128bb9c830c5688f6a29caa2ca15af228ef48043b5435f23c6a21ee65df0fa8fe97846c710b020c26dd97efbaa9940263560aae6a7911bdaaa481748576495db0f13e322a3a25a343f99483c578663056a310acff493ae48177404e18074c50e0dfd36b4c5c4dc1a60e96d68351e268614530306b4117e205faf06d8c9a4f06da138d462cc0a5e0cc838352ac824e6b918981a00f0622ac8a4c66b356ad488198aa951e3cb11ab24413414f364a84a124443304f866e7831335bcc06ce98adc662b1a18cb6b49c4e6215d3d09058a53454b58c9e6240c68179690bbc3a366d61f97bb2508b5d98b6306df12717184d26cc2b12c7f482c5a63d306b1ab5d85d6303a86b60605d13e3d5787eba8459b646a7f4fc1409b3502dcf4f93a04a5026af309e0b9e24d0d6a4826be691affa9de0557feede937f6067ad5c7453b2383b8c6e4a66b96471b6ecbe32943c33678966d35b5292efd07dd7710378e45ebe46ba69120706f12e73931ad0cf610dfd3c7a2f7f76231d6948a4ef7c0f0ce2813564cf779bde73b3bc5abff74059fa7be31b58830d466ee9f96ae058c320446ebffcd207f1db35f47377197d19ad04553248ba437bae9b610d63378029a5e158fbb7d7b857f2d24f86beb25a90be46c1b17f6b00bffde6dbd00bbb905bbce9094ab0895c0e12d6c0cfddf466f661102f88776be06f4077553394e15377e4ff5727838edba4848672840e95227ce4cb40ba2c05099d2b8fc072c77ac495dbda6529471c815b74495172e5b2142555ee682f2ba172598a9228f7b214254f5cd365294a7c302045c99194d7912bb7cb525eae2b3fac4027f38227d8feaee555e9251ed452c17c4d91f305452704cd9cc8e80b484ed0b22657ea6bc94bc936abbc90d8978e922c9d922bd51be3c957aae251516194ec80c6e47f30500253d2c42a4932515f9894ec28e1140522b454819a324e577e0b18339eccf032234b83e37999aad7d18481421260902042820a0c892840f2af7c30a842352c908d268f44122490c8243c48ec58703eb13d613d220b8fcc1157aa0a4754a92b744158c1db621e15a71800bc2d26df83e2d9cb414734a9471c8144feab280a2997a29a64a89b222b92004071d87cf20258722f072519228cec7250129eebc50d2f494b3bfa22ae8a9d12e5f68b0cc37290114fee7839c88826b70a613f24b0e0228483c6635066b1fd2f520ccb414624e92e42c2f4a03b31b1de092c4b7915ddcb525e4497a5bc846e94cb525e4f2e7f7659cacbc9e52e4b792971e57b1aa8fd29317be8d6388b0a030f05481b978388b801114cdc4c8916d05ddd4d9f86b0dc9f7efbef4bdfe263859460aca305c93a4c5f356e0756aba18d0d0dcdb7fc98ba327eace14863a05f4f9f81dc53ff725e9332d8562b2492f5288d31c61897a33981628d0c6ccded9185d86824423f4e874abe54c9e74b44254321b7c80e309fe29eb063dff936448df33fe91169b185b850880e2e6de07e475f8e7bce0755cbe81438fc304122d9dd4022d9cdfe9b377bad75656be848c8285644045463f2459a9b3b7fb4b9a175e77746eef6f3dbfc6e2aaf89a8bc97d70b85c8fbb2848da40aec776527c46b980745248f4ca283d16db9e4b624d271ddd1d3f9789c80e54e1d8eee2891f40b3569701b2524a49359688a160f57869f10920ba705641c1e53bbe8d0f8d478be0d2db22eece97210113d77a4218227141ae20846f8dc7927efe04a09c4955f5d7ad7610e7047992aee7823c70bd7fd8e4cc5ca15df01c21d251299e4fa8f72491043d7adb8fe95a789eb6f533c4f5cffdfb91e7444caf5a74972fd6d8410428cebefe387eb0f648626ae3f0d2690e2fa0be1e1fa1781b99040e1e1b9feb2a7591c74022dae070d11c575e9a3834abac44a61c40909af2cb21051459423b8b8c98288264b841084129ee0cc400c2392bbbb33c00a1550a1850a84f0410d70469ec255523a50f283128e644184209c27a08002d3f5e7513371bc364e73b59ef1dc6a834353fb2b2532fd0318c714b2af4ccfc41452a3facdc357d97735a5cafe061c8ff92a0b7de838cc57f5bb5904272d1cc67122df3e105fd537e2c488931ffda537e2c48893fae3abff7d29fc422336beaa1d1af1ea936c60e2334efdea17aab6908616eb0bf155fdf1e507fbd561bcaa5f53190aae1446dcd161b2e5abfa1e58df1e61553fbceff7be7f74586c555f0581ef997ca17cf9aafeadef53a40bb3a759f2d6779803557a82fa3235ac21e3995d59d0b18c4909566acf3a1ed03832567dd0bf01a7ab200555fddbdf8063a4439503e8d7ff41bf1a71d2e18ffa5b0ddf57fe46d85734f41c1f295f39cff86b21fb2ac538e0042dcf50b1f2895c8a6e5649e34e959c3fc3f6dcffd8b82e34c2852aeedec816aaf819677bee6fc0696f03c2abf95ad6137c271a6d6c5887045ae09a09c8d5fc69a304295c843b7f641b00e199f939b88d868780784dea67862045a44e0986e40e0fb11167912de9923c343964bf04d268d0b14cb3ea1dbdcb76484e3ae4ab498dacdc99c49dcfcd56b3b49f4fb1d0a3faf3b9a309d4f154c159ee14e2c2b8b36bdde9bab3ffeb37f2bdf783f4de7ba111ef4943cdaa32bd4fca9d34a33dd42ca279e54e233a44ab601615028bf327d09d34f33e2118d1a1e9a437281dda2b51574e4f52afc1962b334f36a8ca34d0bbf274a58a525483dc95f41bd4be63affb9844a25878663efd826fcc6ff0bb92824777fe175223efd96bbe2f3d0448ef8522f09ef4a550044e7e784f0a552d02d27fa1aa1967aba0f5d57cf9e135a911afe6cf2a292551a316e71256debede7f3f8a57641d948b15d7d02ee46afecc7ab833cbe1ce1f5bb8933ee1ce1f0770a7914c8bd36b295bf60b0f3dca74329e2c49ebce315b72e79c73663e773e6d295fd267ceef5e968beee752eddb5a243073f98a7e0b3cb782342ef54824609612dc9e332dd39e6659cb41ee25282fdf4e226ba6d32bfacdaa9c454d1966ad130a2c7dcf74bc70f4c016ea5484a5204b69d2e47699a7d1a216afd0a317157eec98b5b0fcc8b9e0d2e77e58473fa5afd1faf43bdaf95c2ae4ab971178e752a2ae874bbb9e4b497f038e7c26fd177ea191ef4b21cbf0b5227aa56b2ce9255f71a9241ae50fd0952f7fb6972fa1b4e06aa104d205295be0a951d42c9ae5d2ace52ba266d1a2a6466016dd8245faf48a2b7fe42b5e970b479b2b3a587390c02d529245f9347349d05271bbea491075a1b8dde6fddc8ef324d872c4ed3a2fb6022c97634c287125a8ca427f1c97753b8fc90f14b904d1e520262e0fb85c22f6fa77a4267ef0c6382d21e6e48aabbd0455da9f3c09723db85d8b9781da4bb07ec7282691fabf9b811c737283ab814cbeaf5fe6ba547e3bb21da4db6198e9b45aa4363e74be27d9b4906a1b9b4b5b29281695621d190e3c439f3e0daec972902bfa94c61db3168ddbe1980921575938663e5c9abab4c533f45350ac6db16a5fafa66599067a2dd2ecb396d7c0f0642e24cd7a7150104063f6e4d29d22dcd1330282873b663d979660873b664b64dc3b663f978e19ecd2af5af6d32c4a47947cd2ac09f3ae8b66a07ccfa29ae5dd4fcb0cbe232bf94f4358f9534a771f2df29f6ae0eeee9d02d1dd9d7207375a9c2dcef79f30e836540a1cb71055dd94c56a9fdded41171d2c7f6fff2203bb7dddb694d875dba3649ad57916a6dbfea6c5ad57808275af610d342423ab9a41c61544280f4a3a4e98fa7368e32b5f5de1b343958ff96c836bba37dd8417111144536787c7c95e7b034803c81b601c8369569de190793d0cc2b1db2fa980b175f137af0130c56cc53a0270b5f7f6da796771743fd21eeeabd671a4cfe475e011fbc5905d2da43c151c29ac455d303a6cd456cbd81ab027aefc8e71f13adaeacdb35bc30aa698851795c7cc346e5cc93a98c757f27b7a5e2f0aaba2860a42363e85ec3797a1af7d37e3cbd1aa8c2fa7ca3bbde6ca0acaf019ee29782d1d7f79861576ac3154548051d9171aa90008c0e5868c19164e309f1534f9594113d84f9315c07e9af87c60e5361c261a3b80d29b50292a2894e933942580f5e92922c98c2aec5644129f5792227c5e34090d694f26bb0230ed695991c8ab1db9f2d593fd4d339290f2780eaf7a0d7706f09875749729ab669b5547fd4e4a991b5f8e164a1c32ac60aa73c1a3947ead95b6b4d6e6372a0d7d1a5aef0bf332b507f0842eb7749c68dffdd02777a44034caeda8b8f2fbbfef401d84501ff72850140fa42d5fb9acb02385397f5938c3033bd22737ae06521fcfbf1de29d60006e06d216d5e955d3279a96ad2e87804e8037bb86b67a5e1c2ba82093020b33370410809508001a52ed1c2b98b55923a535d0f1c6ed6f7e9637eedf7e19d629d977ddcd3fdf059e773e4b10242287c857b1ec19582c675f3d6b621ddc672f83756c368c20fbcb3c236d9037b4a2b84146ec6459100a786e96912ca9df06a8da9e6f0d55b5853d1070fb87dcdc1aaa6440c0ed47c0edbfe1ba4603eba4ecc1e99af965a5742576c8948d6a6b93b0e3bf8cd841742110a23f949dd908b3ff5c7f8f9999dd06ed01a09103ea09d2819e52106c575454545454345b4e265b9a2d618b54d42a6161399a1c5ecafbac34c1c6d2a5a2a2a222a36ef534a2757cc76b9c9c80a6595b929247d32c97401938dee03376b6414fd2a217555b545404644b2f2526e44f1b1db5dce53c5cf6b2dec0d62fbda9d4452622e7e119ffda547a286ca11685ea8400fbaa27609f49494bbd96aadc675f1884876e7b35f49e13d8329eb0a4f71f6c0d1be8ef226e7418074aa3168b7ce5df8112cb518b2e8b483ca8df9f7d0d77f0a53224aba83a94ae16775a847d5158d1579b8c17d825bdf2225e53fad297c22e4240e9c8f6783d48b0a304bab38b322acdaabf85def3b2f3e5571cbe1e2aeee601d686f45b58badbd66d6d1476514b2bc23185aa6dc352027df8aa5b5ed345ecddeaa25658d44533f8ca81b458023b76d1eda2ad04d2206473828eabd5a2f79536eaa26e3533771258f42fea56d1f5b06b1827742cf372e5ede4473f87cb1410455114c58c022dba288aa24833f29522428870964bfdd56ab55090d3938557ac536b728ad8e115dfd1ba90530412c6c12f579b379e662f9d607df502841579e52f1ab9785b488b3b8028c1ca2bc46b3c94911311d222a5524ab962d145230f5332be39e9af3595b2f168901185b52dba0c29ac7c4ea645af390569d16d7c6573ddc6266573d382bffb3469d63442ee20b9fe397a5e903e2592189e91e44e8beed2c76b54f619a7a515e1b4bc3f09942650f2b46863e335a7175bc0f7954b974d6692ae8d3b6249efd9db78cd77c56d2a6edfbaa5d85ff41a0e6d5fd268e36ae1c85904e12250863341ad2f5d5e73b2b9018323c272ed62606f6c5af46d0c3b840dcff84bd7751aa25a9ccf5e6a0f089e416469d879dd55fb5eceaea3de979379c3784503ae01d2525cff8e73bc6f7f52033b4a98a4d22c189428343645a8a812b903f11a7751a116a95cc9bd6b8752aafdd6514a358d526d04b746b99bf3f41577b52865d8454ed8b1c70a6b564f92972f711f6fe23f45545e19d6746a918112c5967fda791ef7e5f09516bda1b4e82f5f6a4f29a9549f524a27e94b2450d334edbbd382b05057650b2442c8cf015668fc4b7faee65f8ec6bd0525cc575a17ca222e944450ec288d6496fa1a96666d46f2a85b1226611226611246d4a25b69d16f98a184a54492ca272dd9922dd98a41fe784f101156ae7ca176cdf0811d3267c0ac07d1b6f7a7c16bb53695faef1d7cbdd0c66bec9bde14ca96affc65fc288e7f12bf254c8516ec225fb911ac36acd66a535b38a6607f61e26d229ef197405a286130ef09bde742611a76518bfe0d835907027f5cab93faa49956ebf4af95522dcb3277772b19137456cd9ff0a9d549678b093a6990031e8d54e947631b57ec74665a2dcd40c4463c06adb3fe945dcee7c50f1fa46e062e32ddd1b2d1952dc903cc67373f27a5aa6593ce50b4a2e1311c4a26eb1733f8aa6bc7d16c33d170813c2b226ebf44bec12cd72add381e5ce3bd10d932b98260f1fbe01aebab7e3ae7a4efdd7704db354a468672498bfdf225a5922fec685f28259ad762fde143013c60b859b906c06343c40c68c69681fc68f1a6459b164dbcea07a52b886b353ab927c8ecca9edb3d3e78a6ef690656caacb33e4191b91321f1b19b16334a39078d9865762ed1b18c4f4aabf6e5544b2a655a37e9d6160a17fe8ca21737f92da03ef7dad7e303777b0eb433f5dca4ffff344fbae997537aae661ab77d243bbd52cd723c2ba5906dc9d36276de0206f5d7bee551de577a7ef1060dc99e4e60ca92482530fcbc56edbcc5d45b11bae772e8675ac64d1b46502f572b934a367bd69eddc4158a95cffdd6c96f01db775fbf1e1fe0421b569b1c48b0322d768e16c51b2db67fe23ba53d3e3069684e4ed0709d25e9987e4f9d8aa6e767130d1a976db36a5c15d2027a11ffea03134cd4ca93a79c1c3884c8f4dcab077660338ef95c36e7acb49b9352f749e9578d2e607a0e7e52ff7248ee7ed352faf799e7699ee835a59a69943bb5a058a4e787a159396c28bb505dc89e1f5592a62f59930d4b255b4ad92880a816eda9de6ddb36afcbbcdfb6cdebb68ddbc2ae3f19e815b3779f4388580a12495d4a6ff6cd383219e89ddf02fcd230d36c735e74cf2bd9eb32afeb8ee31eddc33fb3dbd7a3ceefd19fce4981e8a4772b0df36ffaa63f15589edc44d7c912b3943497672983d16856513bf06b2814d1ad9e0f0c4c91f75c961e6a072bbf6baf3d6e71eca1cb63ad2d4cb05262413d6a075b2a7d3b60aca9c4dcedeeeedcacb5b4b4b4649406493ecc95a1cb162b3aee6ab6c3e37874f3bc66a134b0fe37ff936100777bce7efb7232adf332505ee979fdf5a0b9f35f560843deec5f3622acdd39007ae78e4d1372e7b4d2d59ae5c8364a27d5b43949255a6d577b785ae4f9f155131f964472844567524ba06a7bd2976e60a054b5551b7289f20c0d55a1dc97545c68e9adf629c8c4964814d5e2943cf225a744b9db46e9466d3a4a6bf55c1e58b76df3b8dfbed3be9c2d07f7b5524a376ef6943c3c487ab64d6e5a388a5d97bc68c141b2662681240eafde89ba13c86bea9c19902e5c8071ccc700cfcc1fb33bdb0649834be4eba15d0966c0a751110a6af7025cb3f16afeb6994c34644c028561ebcfc03333c802b4908908b6af0fa4be1127db465f86230ded14ac41523706218ae5148553149ee86afd5ab3acd65a6b142638ca6854d3268979e6cba9415f087552f834d0759a4929e9cf8cd2eefe8ef0fe427e00a140068230c855eafc101212120232332a97b4e84637b2c05c79bb2c333d7f5e279de01a5328ad8da294c201e97bf08940082516a18f16b7caced3582dd3bec43273263d6221a36e9ede696196c4c2058bce8466dfbde7e3c717aa320a866c3dd94a35ecbaf4bea980169d26259412127de5ef819fe2bc9822bda022bda8c2caf1c20a8b7c1d4635df53cd2faccf81f46eaf814cbe50e5008aa3831c03c70b59f268d159c80710d6a1bdbfbfba1f07b00269947e346d3fbeb2a1e48257fea5ad924cf6a391f2c01f2591165a288d8c7eb4e85f65cb07919546cdaab26a99b603cb5150174fa4d1c842d3dd478bfe2a9f1d4028287eb2f440e8a78504b69f080c2d02f1c8226b36259ba8d7949eb9cbbacf76ae67912d224734b4e8576e78c6935f8ebc13ac3ad851be543fb6259084d2a2bf074a9818cc924b64122c3a93d397def4249004438b73ceaf77bebce6640a6b687dd52dd8b11e59cf72bd8b26d7dff31a927d01089033500ab4a52f85bf811ec8a41492c2cb26cb37af256c912c7220220d337e6047cef258bafae5649dd9b4686f78f80032030d4184102962a459f3bdcb3ef3ea7ffebef2b1421836fb2ecb62f25a30755337b0d266183cf91a399359d6524a95fc5154c950bc36a712b089af8d4fd13049f617fddd63a0f1e1ab399f7b0aeed06e57ebdc7eb471e7edba1008283ec18e4c348ab7fb49b970c81079bdb0e240d3581e45f5aaa8094d7bd77cd04227337bcea6cf6c32d3ec25a54f6b0cac94609cc0387d71fa22fbec0486cdb22ccbb24cd3b22cabe131ffd4f2a72f6c4b188425cab52ddbc8cdbe7a17455f6e561766aa1ef66a6faf16d616399c99155d0dcac77a2d6e4d7c0d984650a1e220bf5461c7da43247f6ded19aca1d68ac147d0ba446a903a7996109ad31bf4405435829dedeed91e62b774398a9516d0123ff2aba11748baa872a7e862cae529ba806273798a261c5df9e5b8fc60f9d2a74b58ede5779efc6d7b77ce9d7beef9eb421bb4cba11c0c828f7f27360bdc649216535e2371a4ec69b13f971dcb37fb1baf3ab787d0b18c12464bbc2e072dc17347e9ba1cb4a4753bfe725e806c2b4174394809a13bcaa0cb414ab8ae7f392f30db5b290bcf6654ebc051e6ba25fd7c6b3d9f5fb27fd3260f05a6605ee6064d8b36e00b372dcabf819577f6a0d7dfc30e258f16c47439e79c2f403578a18165ce659326f451b75f2e6951368162f93b6c72a59437e8988485e4859d5dc73263f3dc9d5ed16769ed734080b4ec58ee47e9fae9cdaed1d9e17166bccfc964c6773f3f48ea92545dd883deeebbb0488bf3678b3364e27a225bedb6edcb69f9edcb4175dd9c5d57e4a15ab4d94dfb9b0d5573fef42fa7050b7d0d94595aa44f3f697402dbbdfcfe6fbe7dff20a9cbfd66ed5bf719aae6cf700528d8d75ee6a0a6dc913c4826e8dc37e78fe0bae5eccbefac6db6cf63bd0d447d055bba0e747907657c2797b84c363d7db9a445ba3db78bfb2c289148977c590b4a243d21fdd2cee9a551ef902850efb824a2efa02caad22cbe555aa4bf0d9940974619a58f953bbeeb89149941bfebbe03c7794bdf7d4fea883a929cf66710ef03e7db6e7e17b2c4d175dffd0c5087db0063bd12d5227d1b033b32d0a447a5ffbc6f161bf18d16e990f9527e31d8803764401aee900b3997917419d1ea02324aa34bdffb667107c2b4483fd5c5c03ef7445c2f4f6ca0dc69917e45225d2dd27739816d9e53cf5841f762033bff3487a68b10d6ffc5899719586dd3344dd37e035fa2b0fd55c3a16319af7b996a71dbb6ad3ef72f50c86c2488655ad7a6dde198c9aad1c86a69bc90c657f32b0d69b630957a1f2f50ac7b9414c028a5ee3de79c4e29a594524a29a5944e4a9f524a29a5f45d84b0f3e9a44e744ce2c2ce1625517d0d3c3d129f528cca93cb592ec77cd0a4ebbe1c9274d9433af9775d27bf1cade51b94db97d312aab486a1451e0af86e8a60fdb7afddcf7d909416aa3aec416f734f3f1e7c6bf0cfbefdb3ed494ba4e7bde46e7afec9e09b76b7bf697faf9cbf07763504fdab74ae65f0525fce101a9efc7698de7b69dfc1d273d504ca78353aa8dfe17150a193d20a61d8d294a52ec9d2bc3939c16e3f32d0182475733c54c9b007bdf26558a4c5ef678bdf9d250b96408bf27ebe247544ee242939d0bbdc4bd7e5e473dc3b289feb9692e3c09c21ddfb8e1dddac35e277620e0f1c821d1988eb5740b35afeb468675604262af5d08159d87e91c27e0a5cfca1c5c2f64b70870e1dfdce68523094d2fe6cb6f441c77e838ebb66e21c492adde508fd72a6949e0427101dcb8cfd925acd5a488bce46359431fea6102f621433ea84645d630c2479dc6d8c8d62228d7b385a4f75617f64231b1e30c82497e6851bd963df8369561652213e5fb65c37fdaa437c50124597839218a2b91c9484cf9d36cd6af71a8a53569b9bae3b729bdb02afe6dbc04a996ec333f3495858176718a617a8a4ed366541eb14cd08000000d314002020140c074442b1482c1ea8c1b67714800b81a0506c501a8ac324c7611884102386114200c000080000cccc3620007ac903a33bb4bdcd5b4ec13477137d9d6ae94d9b50ff85e17abdd964cd1268069f320e9340e9b67c99eeb99731cb35c8ea7fac9016d4b3db0d6cc0e45ab3e111506efd3d3f86957524ec59734349e6ff6a3ade76ac2907f43e91c5872d8871e8b840a558ccf8320abd9139ca3c0b433a5fe720f3ae9ee4550b47a932b7cbeb3ff767aa553404c27ed323fd7d71f8d65f5767ea375b4d8921173195a7b7e70e967f346d244bfb0037e8b553d76ee1eed71a6aaef8a09bbd56751cc50fa1939f568e488dfc354928f469d84b89b9ce1932a2dc7b2697ffdc422272722f8e13d17ea5a0b2d4cb64afa2dc1dc5ee20192aefd658588cede5647ebe03f65f01d0f9853fef3c7211ae4cef4f3f3ef3cf5b93cf92142f9d1a1e27269bba86aa02170ac32f62772f427730d55524e4b5863a6864db69012f678045bc81b29082612ed36eaf0e94af236d0fac24e64af55413fd7036f573edfcba88c6b991fbf840138b3aeaa819ed86758ea9f3597f0d87a24100e4e39d3c6df0cf43bac0ee65bc19ecfcf9f0ffee0c48e259344f649d6b1c58464391019cc2bbf56c3f4e192b7969816311c830e4be00b9042cd83e4538273e97d97c163ef389dab6fe045e72c81ad121d9807b43c3e350b9a3e34d1f0fa0bcaabe108aeef0c35d807f3461f94a36822bc1429ff3986a6d1024683ecfc425a85cf6887b440688833984e8d7d2cf72aa8fb0915cea0785ba0698d7064551c7ee67fdde5e3c65d3b7a25d7c81a5ae4a042e8fbcd982c825d3d90cf175544ac1c6e0baf41eb9f0636721cbfbeb3d6c7c0f89ea9c2571e585595f087230a8f933312b40c6f8151988b823b96f808612ab3430d143cdb186cb2989785b74f1dd63f7a8296d186d6a4534f3747cd7f9c3bb919c3ee671906a8f3e224148104b580ff5e43b1c17ea5346d6848400efed7431d4523d1db647e698badff670c05149b64d06637469edca26d43516afb29da37f29e94926f1c12474bd73ba2e643fbfac26ddf6842f580d61261cac6258499380e9e0ad209d622c13574c5692a2d882998c7889973be61797441201b939ee1aad07a50acff297d2e68ad46d4dd977628f24812c7bfc1b19a0a1249e16ee2dcf75593524decd3f070b6eead110d8aa29ece2ce66a2347b90e890ef1433408ee08bd34eccb60fb1fbd0e964453eb544e2500dd1fc53225ded950cf70c30605dfd8879a2dbdd51015060519a2b7eea3cd2e2431c414df1f7ac2111c1642843e56b2d78a6fa6a5fccd1b6e44a4a48a873d94745c5bd58eea8a86a66c4bd0e616328d8a748e1fa00d874d491c3214ae73cf0727ab1219efa2383611c9be8c369728c281885bb685fe043280aed17c415822ba321285e63f3946c95035b4b8fdb75377c43c0eec7730ffa11bd2f164ccfe8413978ec5de02d1976b0545077102455c9b5301058928b56e4597ad21507452275ce7558c2807d12b3323af39ec7d3fc330d36fd912bf73615827b579e05162189c1038c28d4004aacf0ab136e469ad61e5ebe2db6cddbe231064129ff4028c52624cbe4fa0eeae5858c90790833e3c88805ee3d36d8c0ab4dc2e39384224f0b138abe4b3b9a862f697ca7e76f1adfe9fb4bd3471a7e69f795a65ffafda7f39b063f69864f9a264c16c0b47923f54169faa4ef733a9f69f848cf7f5a3ed3f09db65f1afda4e193b67fe9faa49d3eb2b4b6fb23719a6f406fbaf8d64dea83f39e10f4a48928730f52acdf1dae8b7f981b725e0c03f34f99c1fa4fecd98100baa4d2ac979cf99b79503188d384917053c0fc45f51e31d0a81734326e293ba76f5d52caaa4f207609d9ea2b7faaaadc96bb1af7d28701584a789f691e283c37e26054c3f2705236670f16898ccdbebbb1be9ac8955345447509d6943a385a2c6f174a92a9763c680396d21dd941434cf4592298d44285f05f7030fac7c6a51c6b9a801b38228ecfe22cd436cdcc78dccb727960909d20e0fbf97dcdb77a687aa60c185905028ced29d7bb894fcfc0d5369f08c5aa7128bbd8ce9f12b8f95969392d030a2d97afdc3ff4b34a2230f7076f347dad4adf272069f0ec024fe9570826712d7bef2e4c6097e9236d4ff46b3c250a93cb849f18e816eb06d962dd444cc5804b2f75e96654dd59f52b72aebd86bbe40037d9cb85543a599a71d166f1c5e26a0dec5f6f8cd776efb7c028afae5851aa538096365be9f3ab0c92629c578474acc4fa0d9a7bc640788489f710810a58349e874257efe70268550dc7ec436042f2a71e48a98004d4d3cab47a2e5e652c14d01d8705fc48f069e24e0d1a757b0fbde66cec5f2e149013c608515feee9697aa93ef1ab87ed8ba22d7ded7ca0feda6015c4c5e38a31b8ef79f5ad86cc9db373299e0b2c73ad9267cd517ecb14292b1b23f0ee79b8d17b7cdd41fb4a4368b9a9e79d2decf3945ad1b8779a0dd3b2b91b1195082265d90f15cf8b154ca45dca1a425d9ec0da57560425c1f7094ac2c0c2459fb51654c407928d7ecf30d02457f14180e5c01161277681ed76c6b6256292b1ed0639634efa47c9180c96e668ecb7c6bdbd44ab4ab39b6fff558371f1789df64b90b0ed469c2c46d09e529327a1d23102a07bbbe295adfa08b9f30b3a57495b80d01cd31ffe427e9053c5060e5ce99bb86a4a9c31654434b4cebc1ee4f63c33eee46fa6ad6940f6fa826380831e782cc3b0b3a10245cf6a5bf3cfeccef451bc868ea6a3b4ca80b53c87a160bb9cc57606e2e8b812195eb25c55ff4a7d6663946b05ccca2f638c84aa2e3bfd6ebc08d559e0adb245596827e94226d6ac33053c98829d1ab1368dfc0b7d80c8ee1586eb94201dfd1133c630d64df9b33b0a136c10824b26fe8d2164996aa802075ef7a06e0ee1409d01abed0233a6d1e7d3daf51f7acd602f1e9e99f90934f0b792aac76a8875e5f44543d3f2edfe804dcfe77f1261447cae6038c2affece318ca3154053743f2c370d1605905942386339c936be2a8c80f01b9e921ce9179826d32bf0d1f9ebd1cb4c7869883cba1e9415c62e4b1011a5bd8bae0e0aacfbb3b70d476284a34059997c1753aecb5e6a8b4888b11c30ae48034937a6304000fa9387b141968b6e0e97ca8389b2d70a157270bd17ea4b58347405ed32262f2f4ba7e2700485e56eb8adfe8f659caaa81646aaf6c241719ab8eee980c5a18dc506a0ffcc1892e7a6ca1e6c54eca966075519bc3fc0ff96bbdc25f964929a50a4388f5f8abb4500c69ac9b69679be8877520e023195524f52d1e6d5a00d40e311a1836cddcefdadd3ae47872cbe80899784c65d142ebd117bfcfd46f697332887f4f0c6ab1e48a498e2db3619c6ddd3050f2e7c013f0d52cd36bf1fc1cfa3c7b44a7666d0e1ea3da5c9e5b3e01590075de02a2bb89e88695bde828d0dfc59aec4b993551939e0de820297d05d6d3eeea7b8eb51cc6114e11207bd9922e2ae1beb0f844ca2d19812ebf6322480776ff5428a3720dd27271fb77ace8296301a575dcf89502ba349bad047ad17450f7104dfbf58270e1371281775aadbc09714bd651be040e331558efc9e24711f36a029812b96b2f4980896ef342c2449160b4f2d3f6f191a4251c6ac958cb8dc13e46ee88e9265a5f6d16505a27d6bb9a8d24d19be73bf2682d2f162666c8052e0e6c6bb2506e22dd4a38ee72a621fb1c674c64ad4c1b46e98ef722921310affcbfe993bdcd1c5821ed52e1754ed8283e71b5f34a7c323693ef02b96c0c324dfb3edc61450e188703aca6107ba66ed8d95a94c4caca6a570ca323364f0a4c86b929cbe1fb5069f1daf9d81ee3a96a0f8991e603390b3ac6804110103a97ca7b9122388ff81f4df2fb31aa868c42272150334519d039f32355007b154e32f0712ce3e1d3dc70d8d0037d3724cf351ec87c03d0683df1da88504b67e45a72093d17efefd09054f883a3703943f2efbc7301a38e003740dc0db014f43e74d60e08aeb584bbab315835aab449fd03776da264d7816828c54bd5cab206ec8fd2579226e427b640d8bc1d7932e505ca795b1e17574b94fdde8ca570327da271a854e4130e72b3dd632b6164c65415ceed463689512935e77827add16f2a80e613d120a7f6b363b5136e45735bb609205f779fbda89068b17a1a1491c497098882f3189645c0fdd3c0680e09f2de240709cdeb5a7eaa888356e18845b6a56c42466576de55dd4fb88fa2f51672fbda936a58698b4662d33206968bd21a791c498b3cffc7e26ee33bdb7c97ecdec61b267537b9aedd9fc1e937b98b3cfb4efc94c682c8213b4f48d4ccce41e26f633bf97c93d4cef6db647337b98ec6ddebe267b9bbcc7e43e26c8bedc04ed5e260eae2a31d4904df93ebe196e18f16b405c9b6ddf6ace5bfbb98cbc958bf877b57927e93ac6b9fd4d097d830323cb72286fd97d8c137a5ff66633d1ed6f2805e56ae9789a7946b1e68b8d6fb08ccba883b7e09d8d1ce19880a7adf77b740fe3326a6790a2c9cd65e6312526cd322ea3681e5920c9f9ad027b90cf4e822c31ae538b4254f8cd2d76550719cde83d3202de78c00b06c24d33f392aee2e70fadb431e215261062333486d331047d2f8f66f088b1f25a045d81780b9f15c6d0371916ccc6791547628315a3745961c728bd48c8b00219a343805fdc01a4c0612cc24ce090cc14b570f6c14e5dc4a875cce4baa0f3b6fbb1f89f1b04f32faa4adde10efe1b0733cb32a14408b5306f5bc62fb20a8544e03119f13cb8299c9f24d782c76a66b2ba3fbcae61b95a1bf749743791436f952172acd68042834e235de1f2940a508ae0264ccf1231f75510ea15917112becd2020197c1ccbeb79b8ededad1227d51f0cfe49e6112192b334b5c7033b9c8abf47c4dce9919ab256d1850e67502d60d138d3dea981d0ffee6e41cd6266e1a603cda9a885039e8d0e2b5c4e656d764acdbd1bfaeb25071fd200f128bd9a633b50bfadb2ca4f97c337057d7330c7f88cad95aa68dbe08025a6c5a2c8dd4bcf0146303af0797344a0258a081df64d77ed58aa888a116b828400d2e48771df47ab1cba71498514487d757bc43c18ab0894cdbaa3e68d09e555e1771ab73c8825054cc1d3c1258ac229b1d75f238efd601b7ae984bee47c0bd871190c1b11d5f15becfef669ff5c2b6130b006bfc095dec2302858afad4542dc487638b23316edbef1fccd873cf8cbe7f757cc0069c4e6aa10500f004e979b5ae5f52766f4f9d84e4cd376451609039d67e47c1491e4df27de8c234001b70ddae8e972029319b732a6e488c5e592ef50aeb0607634e76a4def19a3202c5f674e36518d26dd30b0a5d61c278a2a7677ef744009f42a195119fd8b44562cbb0af852e20fe90f71942fef6b5d7d5dffe79587211401e73cd969aa456cd241287fffc61086696d7ef92f98d6b92949ed04eb06e56a236b758a22168ec5a6ffa2819580990498940a02a85c87511ed1f1cd2e1ee0da31937da01c9a5115e0e4dd2fe488348f4001cf06cadddf1361ca20ce8ba710d9b4b66e80198b65706a8e12727e81a5042f9edff82d978a2cf581422175c2bc4017fa32d6cd9a86e14e5028d850be2aef5e11f4283147ad13ad9f6d01d10291c8786888c69a1b5239ae9a6b0ff354ca099fc065ab8d81cdd0f83d7db285beff16b3c236c8d05b4014f81665b136e9e9e15f39224682d11a34d685516b8de44c432feafce8300f9993a2bbbf648b2501969f54c92813518071960e145e74d4050d34837d9dc0cbcbe52da3800dee1144f3120fd344d0e7e324e3a115c3cba06900254e40e8fe7634d887b16c3fb9cfe6024301ad46e49998f6704ee75c4064df5fb03bdb48b7476b97d29572e320eb90211748888b7c3016562c4b29d7d1213e703b99fc843ad89c30a7c3aab7b32f5772561230a0f290fef840975349581db7ef4b786b10485b79c19df99ad27f00117020eac26239ed6443cfbc0e36a61233b93e06b025d9d9992981d9f7a5acafc19bcb44bc0526942e74697759619228e7487ad61c1b70955822d932c00ec8d37835107aea313c8d761f612094175877f716fc7e05194b69d5ac80fb1368e597043d3b623fdf666246b4af8b280e7bb737439cc797a594210e360205d18a469744804fe9387d8645baf9855e3f9aff7b74c5626a08e6911d76725643680e517b44b79218be40d0d5504af38500f88fba6b054a5aedf857b2bd7f908e1d449477551ff776a88e4b8265a43a5c109c431f63bebd492a38877239244c1e1019ff5304356b692e8f5a4d78346dd07bf780149baa507a6fa847a1f9f26e2e6c36281f1917640faac4470b198bcc612777412f5dc8229592323c766b88e595152834f9da4141e9947df69db7db79b610c424b7ac031a7f89b304325b2190fb5d5f7a324e4de55cbd7b6c9783401d91b1da12e501cc70a326a08d7106f488766d81ac853643ef40b451d067a2f3e74f7271206f31f10d0122a8a24fab74adbe1c2d0c0bfa3950f1e86c0baaed0e0ce740177f73fc533c6e291328a5fd235cfc617d558d5f33cfb42100010476d41fbde8b1967313f1736bc4526c5de4e6296c3b4ae65a9bd579ea743491b9ce25f890fb0b75411eda65b2ff52a14c7468bba34c28ab574ee19a512002b35e505915ae04d670e3ce65131439c72c0edec46900df79ed05534bbafc085253b23a4bbbe731e3a005525aff422095f45a35bc91359981943ee5dfdc4b65f6e2a5c134110f7713d64ab57b76cc3e929193d95078766e7f59ad2223ba9574e256c361be651d96b4a4c567739e26913dcefefa799bfdc1216e1b28521c27b0cdc3a79e96cff588f3c78aa8f5d93f69743c37f15e0ed846db37ebdc026ec4582ec4785d3e5f580eaf4b75af2c61ad555f89eaec78d7dfba2711e49734427287eff015c299cb76187d2a7980e1d552c313796d28e1def11bafc13dbc638f710c900d2e04a433828f5574c9fa8a95cf353b260da4dd8eecd2ea04617a960d10762daec82edbf87ea1021ac7ef2598d2cf93a27e7c29d10d4b94d74b78266f295a1b27797752850b2354274c72dd4484cb993f40514d48d8e6dcd5408d2f24e235b3a3591a7ecfa409880a449b3f2df78bcd5907d92d8b750aa391418dfd79c27045de0728183c91b9019aa1113012ccdac31c40793ada58a21099835910ef8da2e40aa356d1c0e52b8754f1d2da00516230d401c073cadc33e43865f7e69114dcc8bc2f11715e6a0362d654a9248779e9e6a1b95643572c5a4e449c695c0e9845db63b211e1b00e3b6658c0ad0e03038e2bd1bf62e72fc941ba2518e1c8bdad62ad1cab57f5495f68e555edb462e61fd4281614e1f18873d2ffa32a26a0da48549e3fa7de4e7e469f7b87d8d2de0ae81a0e7fd1292025bb52963040b8e16c119685899205c4b44e2ae7d1528314b9999e062b0ae51569ac86285be417116307fa8ad72f5b27bdcb53bd2569d50f18d4c889ac70d63e72deb368cf235d71c2b910e3fa32d79e8a14bf3b8f73e5ebd162699be5fb4fe21e4107cf38b28af9ec69dce392375c289f6def5a783aba1961100e0b407403ebfc52b07091203645627b3ccf2fbc97b46a7dce0b16cbc46e57e32afcd898143c79799718350e02cf06d1d2e1b377408234b186e5bad7612e2c5948e2a4463843b3c3718afd4671dc1962c817644c595d0f5584233fcc13c32ec9bce9ff806045dc679966e4662174fba557baba570bbcfa7d4cd1a8e30fe01ea7809c4c247018561723fead9587951a382939e4cbf8db9e3de7ec86196eecac0662846cf336db02bf3cccf1b4219cf2a33b104ea1f9723cc76ca546f7674f88ccb3c849cbb0bdd4f5ba4d256193ebb169f50ff2166ad48023681597b369faaa008023ba9fa838738d5157be50fe5e238f6c0258d3a446d7b553a80faf5bdc5e4bae01ae1e39407961a20ffa5abef690aa6cde7c212ec7e813884f17a10435fbf098e1afef6082fb3a3890c6f1efd5d697df36b15618cc1660b64a8a000219d223ada0307a3a85443fb25d1bdc9b081199a5470e993c1633519bf2f2dbeee0f0e3a4291398cb623616e4eec80ee063a1e1f7941a9e9f20c6c7b980c6d7006dd61bf7f8b506fc8b3ad856305c00122e4b0ef5fdfa0de8261691e9f3400b71abadccfea98b48a6468f56baea04ddb7f9bdb2e7c572c0d602ed3048352848e639fb7eaf03cd36f6ef1574812587c8528d8ce4888a9c910901e90d67938aaa0c3bcc1f974942d55e6cbe8d3ffa9dcd21a7e888c15d10ba1a6d99507ca1bf60132e1a627a1f88086b3b172c3b4a4fd13a1aa6e1fa969de59d877d61852f101bdcabd6fb7f8cce20a29e51a1d4e723c5ff7f158886190999066c57c43ea13f1472792078aa5784cc408e025ee7712bfb20cd8ccd48e8b36ad61f060a87b99102389555eefd3e0acde42e8dc9b51cca8d30a0ac2af0d697ed67075610cac490da6559fee0f43200bd5f68ee3a207ba48df57042ea40676b75077c9b2bfc9de9a1bc053a9f51c344161593280c2252e1b2e438903373fdbe2537b5730f270e30c1c226144a52d7cda6370bf3a54688ea3c0a0886c28b871c6eed9a87002ef8addac57a2f1b44c7bbef9caef81608a0bb08407c1fcdae1c2882ac6ff65e0a599965e700d79d6e95877917b8835645b0ee364654c03ca6d79f1bb03b71578e7df9cd7eabc98ac3eb5bd9ae5f91c3d4d7091c4d5afce60493f479f75ec6a290502483d33bc7c70631c8d88e648a1a936e6ffeda43370f317080c106904499d4e5136d99226ec2288a32ef8f187351e85b0ba0f9dde50af463ecca6c785457a58ef1eb5927a3a9d168850b4ac8bf2d1c3903a1f67a65edc476737a42b7489d9ce42d82eea4753049c9631657b6118b42f0dbbcde081726ddb77050770bde5dfc109a6a8ebc590fe0ff12350a4c4c9d68d85155b3859b5264207a6202184389e7f638601ec2e2a9c95e9b93e2063acdadc6d04a0a62220df4e049134b9df25fdf6183a37a9c6de2ad03278a82d996bc3d30695fbdab679d711e2f088c269a858ea50eea313eda379157824c0309261a34e331683d3a03b756239f4a569b8ff53e91e9f0f937b6383b568b74654b8f9b765960bedd7fabfc28a73036356642bf5326b5501af886296283737cab309d79f123d5639e8017510d423512c55f5eb00e9d9c7af085b65c9c8825a27ec6008f5084f71d4a5613ca11eea82544142172c3acaf9287f22da96aaf7de016365149e8b8427dc28d56762080246882fc81d54743d9a79f38883c52a4b025cad2c630f1afa24487703a279624077f47b878144cfbd9d8c19866fa2188a8b4b2010f0d651383be867f730aacd932557a1fb37b66c6891e850ee781b42064b79d8282008221c670781e507ce02433afd25fddf3a2d35b4f818f472bee33d4626c6599b7b4c54bfb3684c40ad077e1960318f3b8b39c1db5e3b9e88df96d531812cba1bfe793377eda59adc1ddf58f076eb4d26b250a7c19b0b35c22313c4301fffaa20e18eabb76adc006bc07ef1312c02c70f3496380bdd9e45bc5737509562470d7744b10eba141ddfb34b32ace994e099f3ac47fb5ae225f3c50e8e925720bdcac918ac81278843a6142923806d920cfe6ddc94205164577cf032d0c3296db6db0d20dc6df603f11a8691f5efe19a10de44efc3da113ee84b6ff8d7caeba80c179ae2749f6bdaad16f1c1ce19845f3e80e0283af22eb251edb93dca004ce41b91330d7134e5d4ab24ab3e3f09e4bc4ff53c97c1471ef76f0dc4582d8611e4604bad0130c05a5c87736b81319a1c8ec71b44fb909f424f6a1a13a76c5118c163dd02ef5b2dd66c055dee34215bcf13c25eb128a2f288633821d0539d86e23f9009832db98073119542067e5279fa915de900af41cf10c98d648ddb8abaecc8bcb398f759bd8bdb6e3fda847c0366ab8f399494534642b6c1fef83044bfa45d993e3fe1124e08a8f7be4eb3066b88f87ac87815644898a60a828e823c4231252107cf18f04a1739472784ceff2e1ee6cab2216d5d6a2ea6c6481c7f59033fb318fd752168848958df4066c2589a87e784cc4840caa8e25815908bb8090e9a85017939f2af9b55d4f6f5f2d901113a428e817e8dd4ef4cd7c7005ad093d421c18e0586b0deb24eae50a4ce0bada4b681fb48639a0ffb80deecc6eac1f0a2c54bbe117776fa72b52f0df14280d83d49a08b9a725260b2882c023427ba6de1b0d39c93e018703095410f05408c2c04aeabaf361e9b64d3f2cc117acd315b4837f6aa40c387a2b713c7b7786745e29d5a77825b47a8082ca48bb11969cd6c44b463df9e0c6a994b27a5d965a748864f846f5d63895acc204372338440588d77e3bb0a56c77d76ebea52a7eb764c5055e441a959c815c44fca583bbbde92abe08ef60611e0c8e34e893946abe6d0fc549a4c301a6821773bff13bf895a185b529179326d28a3270926cf77c57ef29e0c27aed6f60e8dae3ac13484f002696ba1a1382f791275763f5d5e73ec5fef509fc748d7a82e1e255ad0d296025fb5bc5aa7be8bb887afdfbd65cc213a11558c0a8b543b782e554a82bbd1d9014cb72a920ca6e9322625ba62a6502bfe97927115f4f004f21dc168141a51f020e75aa93a87a60a203234beb1b61a1cdaf5806470465a5190c989d6fb5c023533da88e4a1d66028c0d40dcf094ab74be83e0e14dcd4de96290697d7c9f632d98cef6cd32e3b819a93854f4214a9a7a05628d73696fc439d9b649b8e385fa1881e6f4c3935c1428e62fa29a35acc625e8a369cb5cc8974b35c8ed0503025e91baf6784b46df18e493478d54b3eccf6ff0c585058897b44b4c8d68f3c41cbbff8f2db8880bed43f161b3dcb1cd77807a8925a4e35132ebea4184ede8ea0f45764ad9a10a27f744fd23d186792b907d31c247a270cc40c889218396fe5b2489f75c890a53087c1cea18fe36f7f940a412145d554ddc6ee8db052c0162f187bc4afd20561a4f0981a79537a12886866bc87ed06a3985ccfb87ec7408c65e961c23bf44a29988c53a8233783f622e186e783f1a243be95441869d499d1c81ea83bd4661bc501e0533773a55687fc6f990c85bfb98a902c79deb7fdc6c7690aa11c16e096265f6c310a204e5d346f64bca7328f0d238ed8295a248a749aa836797d6105098d600c1d8ce8c61aee4c3b800f1d63b45e9b9cac96839463bf76e5d530ed5e4af459e47b2b271aca1457a712336c342b67c54b969f4f32194469629893ef05d90dfe535c68e9256b042f778c5bc973813dd5951caeb0cbbdc99482510f1156f2b6f68fae7367dcd8f00bee85a477ad019eaa450e216086bd4186692c74c3c31951bb77388746b9df04fac34d1f2f4d0b0f831660b345e5dd96b7b01f1936bcb3a115ec82b8a4f814c6d8ab5b5694f2a3e97adac0b9ba7dad44205819f23cbff4dbda1ee73b4b48989eefe3d99fc34bea6a2e5216c9d81f9cd133364b3166c6163a8fae682e468c5a6e008dff8b6fd048093eb73bac666c66e36a6758340e18be44c6b8d27e8bef60d3f1a98969eaf53ed495fbbeb30b4730d01d08547250b03ec98c35dfd2a1b12b480ca4e2a1fd978636c816b0ece3680fccd032d2108057fefeff547db559fd02b85a81b696ffe514c69f4b1d51c93be5a60adb4d00c3efba66a42092416aff6eae54c440b8c387845c4efc5c573f3cacbef13ec92a9949431b61f4e970b3a60caf1903b4e21a79be11b5e0381d2f6837957d5207bc006afab7f7746a5c2e09370aec354bd99d9feed39257ae2c95731066d97a492b35f00e71d5467a4517547c0a8713fab5ba0168b7494ea428f410553157bb42c1747ca391f21543adaec92f6abd8ead56975618af5884cb26f59f70d4d3e86083e2a7df400d6e2df4e4c0a604bf05d2b68f01ab5bbfd85a7d94d1aecfc737bb08bf623d3c948fc41f817998428170b70b333108dd70b62889e5a7525e844a769f7152668a922c9811d23e9c49d781d07d9f4e0d52cde232818bcba91682e825a23d0e355b7b9e25b752d850340b0ee93e849795fa507a6cdd0c51121c571ab9e3e7a01c8c9bc595375294081e8c850196b79e483e2c111497f56d95396fe98a3f1b94ded4deef3550c7f52f85bd0100064934163659e6455c2cf9881abad939a17d8eec9420d43a0cd3fbac44de5a0dbfab92d1782df44ba628b62d7efb7fc3a8ff4cdb12e3d94568bbcacc89ab2eb5c9185b7b323d18bfbb2c141eb6cb7dc2de9e48046f5888c514a0d2d024439bc2484f6e0088467e8f1ab8626b131874191deb032725005a4809b423e79667521eccedbb84a878de7db902043310b82899273b77bd3a679d5e4875003fd1a10079e03557da748385fc71394b1a6b75b49c24af653ce4e0b792ce2ebd1f6233ac2f9c9464a5e1ed58349db26fad721d049440851ae1dcf54b881b2061cce5a7c019817e130133b8dc3239917e33f3e11f210ec93fb1343f9e762de57ce96828f46fbe616b13618953f4185bcc2cf58b8c4e23f9d108c6eed0762072f7f44459743f00abb41f6afd72b89f2d3a505603c49ee1eef60f75a7242c75a25dd45bc9756d600c06f6c40c18d60dc8da109222798f76c42ac22a50033d70018015102aa2cd24897755d855ccd5dff8519a94377d807166c12877769b365cc811df100eaccb26efbe4c85d43a55d69855af6bfc05cdd95a7e3e768182b80321116c03237118dfbe9e62c6202a9588127a6122f0e0149ca584ccacbf08fdcd479c06c23cc7ab1d58979e19b225ebaa2fa0a48b595653dc3019d7f50f79cb2d1929727bcddccccb8ddf6764772376749a5aa6fbf67f03ef080404988f19c1e98d0df98e9caca369bbf83451a36413ce2e1baa42de128453c226b8aa206afe45c8ed86df5c76c77157e074030294e7ed01ee2ac5a8b56db866be11b3fdb909948d781aa2023b63f11db8a665aee08a42520a2b42fd301c46d19fc3cdf16eaa3b772a7306c50953ff698d13befba835eb203d017f0bb142beb756c157c4d6c3ff4d7284629bd2421a5cf73429021e9e9d49ed2dd622638542ed247c2f860978f027e24e9fc48376ae8ac4b3c70426a9f261e41c66b022bf4d77b792ea1a54cada6637fde0a5f271a2c54c7594b1325ab48b5e29247099a5280d4d2e1fc8f2a8997e09a1adabddcfdcb7bb74afd4b256f226ad910b217aa24be99ff8092c11c260e41d97930d95ef00902f44cfb3ce2f562b2b63af4d446be66229154399a5b5620a0aa00aa3736354d24b8528cb94d231c50a0f1e523e18269076ce4c9505a6b8216e64a0451a6fa017d308437fd9df903b140ae04afc623e46a416537cf4281ce15d019681a30e0d9a8896f6602a9e684f991a4dedee040231d339f9c5982f648664bb7a6d3c9d4d8110eb64688a797604463985d3eb0ccff3df020ff724baf38db8b4017bccac9c46e6d071dd2d94a98753dd51adcfce7f0f7b298a6a74cb5a312ae1c68aa12e1cdc5f238ddaaa17f02026730745416e6b1a2266630229d96888ffd8ba20d72703ff4d54cbf2f0ac043e5ab8bf80c2dba12d161883705f70a85d6dc56884db5d6b2e3c843b4316d050d203fb4f5bb77534d2bebb9a4a22a4fd134e2aa0bdf74c18f053fd70ab15fa46688658de0d149eaacad53b11fc41dc0f3294bff957b35fdf0083c359603cafaea218f68f32869f4706334b30a84baa934487b2ea8873af756127dc84dcb73d517b9fee42cfbf02a2e50c0ffd9c918f7cf535f358e024f30b9492882ebc9917651cf0781bed72613a12dfc6f341a1b92bd13aa1810f201d6cea7a12b77466065344b2222e05f299e4fe3403dd056058c1044042c8983ea2f5bc71a9683e87bb4843efdd211be672de0b0e3f9f6543d9d3500a495204a19481c1360ed5d18c9a3939e817766bd8bd82eb49e45991b94fddcab4b64d8345b240ce676646b901ce3f9d3634328651e7923e3390102fb4623e0b9cfcebbb168845ba486d7ba20907bcea61aaf33b687804043d6f19228680da41a870ca073d932003a7855f0e47d7300a07cb3d8116dd6d9f527240e25370a9d293e1c0c35c5b5b1984e74d52a6b978a52ff26be76779c95deeda7b7d85a32e8a002f1f2930fb1440f09db2a9a6c682750c82877816d7fb816b424e4e1dd85c682a102cc9dc081546be0a85d8a45084df47ae83bbd7c18a7801b3c850fe3597cf1ec009e07fd9a855005d5ba836afee06360e7dc5540e35decdc95800b14de880c2f43afe7d7bc2417146784386af23aa88082e1e0ec4bd7b77512dbd7aaaa8ba1dcb12f1f22704e10884d06947488809c5aa2763e28fa22884ffe0867113ce400a6867eda6c66a44c67924aca564fcc69bda50cb24bde662e530147b1b909a665739ee115d6ff5ccd33c95060267cc2294d281c8f250a19c89d183635e5c88e858fef1848853859a11d6f8af53decb689271d588c0152f55599376ebcd26ecf361cc6a92fdea2f8f61f703279b7219ff4138e043ef40329fc952a81c1bd9636f31045bb508ec2233a3aebe67bdaffc993313911cf350847d9f60d4b4e0e3e3b4926c5c2642528b4d8bd27718dd6430b13f8a4fc7df46829fd2653208d2a734b3f438d27c56a434c7e103f0cda010622a0b8d9dc606b8692a2fd013c1ef2ed9c1e9362a302e2157db2c450fe7b7e94291328ef40d6fca16f084c51e5a6304f4d2dfbcaf365758a22923da612b473465dd0a6831d414a248ee50da15a1aa374b343d0d2e2d826567cae6049e7d203f843773e772e1beef8b1d9a74291703f27735fb2b886e0926b3c6f3c8103a71fe81b7c89b844f7a6a03eaf4635b24d4191dbac3e1c81c5d4157763c84b0dfc0526dc69f5c862eae2cd8def4b3eaa47dc247a7f81a628cdc4715552c82e3c6d97d2b5c02e2b82b754bb39f4939c9cf42e913234c5ea32b668a94d29020f8c8c743dec802b2a432f846ad5e255c845f5c27d9891d92d99baffb75b45bb48ac579954266a28cea89631e14332f40cefde55efffafbc6b141addac954e83c332e52162d5eb1b24f2f92119fae1ce04db3a0e64b01b67ecca1b0b708fbddeb7d9a50e33dbd53fdeead5bcf404465ad2a5894bc636fa137e2413d43f3a190eb4086f9ad79657268bcfdcd75b22959f975e6067af8cf5acf927dd5fa7ac98b0217afd9fe05b9011e3b35c51c4a29348f693a4800408d8aac0f00b0932b9d835449865d7cafbf14877b28cc4f8295e93e17a945584c99a631b1032623db4fd84a09f9b2594c952f75a885824903ee6f370a2885e903ab9746854b27f79383056026820bac01e56cd3d15a4dac96f495833909021910ae2a48020d5cda588d62dd184b9f8928672315abf80ef7db1db747326d3518aa9408ee60eab5bbd39869157a35c109219bb8baf6385484e8e62aa02f0563cea51fd26f1b34b217badefcadf3073c31c1dea92982a79784239a49fd7e83ad25c851fc47d41a8e78fdb1b0affa4c3d6e85a82635a34e598ee8715cc17490a6c9bfed49513384f98833fd279e7d125715cf0f3c97fc8e1c2ccafda58a0474f00b878a0f8bd2e126613e094e1431506c0f1ab10f4ac9f8ff58a736304377fb61c2ed8f9a304004e2f047ca3e157b1b59ad0301b13a21fe464ac2c12c1939c003806c377a221d128c4fa5dd5d3ef19440377135bb0dce4498c483ecdd7d4d94e763151fb704062d46e4a8e19012203f5f8aa7f56149d27323a77c9f954f01c1fdaeef741805dda5c028433a72d2f0dd00458995cf1defeab0f52aad50ba1592bc2ca336c87812a9ddaec49c9db062a34188d5027e8d06a0f6e8a9ead38c1a34b643d6308c07d2355edb902925841bf7e5e03ed6be2db8636a609bfbc93690246d73ccb0eb2d6a4bb7d96afeaf5b343085c50ff7a21cdd0d0da20d08bac4f8cb8d5088ba8c361f60e6715886194a14f2b9ad79fc11357b4b928fefc71dc51f519bac500ee9e2cda1bf26d3d55af2f83764da0d01784eb82a5ded18a2646aae48f7ce184dcee9eaf0d058077b4b6f4492fde2219ee7e251555595232ff704e8bc65a15e6f5b1c4c6d9eb0527a21127ecf5007493aae7fe80c21f3c3e28a75db884e3a876001e0a9f897aafa3d70ef21b4014f55489c133e9a9b0f461624b5c3cad607e1d5906f0dace61989b86f5ede4b3ceb44fef6e7a3ca4c3ee4b4944f74201ca7246b53a6dea3741c2ab90195f114fd3128b9d26b710744400c97ac8d1a960928023056a78a2b0926bf225918967d1dc24fe77c44b5bc4119c1ba2fae008dd7cc233cfbc52d097f64dc4403da5921ada70d4e36863fd059be641290848b64dd2cd0244b330a2d52cb18905228a1ec4a4aec48b987b4bb3ec3c17ec4654496b36492011849eefde0daf40ca5c5e1db438ee547ef7687dc6c205b28965ad72cacd77286eb482ab86a3029ce3afd554603520d47215043baf9ec076fd53e29d1540079549fbb17a880635cb76dfa4b23f9256b88ef875c236cdfbe3d23633720b7f2238f064b78cd98c7e9364e112c8a40c7c5afe2eeccd9633bdde3acba94073839c8dffb463ced20fd4cf2015e91b3d541d6672e7bc09946fce06b22be5f8ee143d7373b2ea6243e14564b1e78ae2962a552a3332e54e69309f131bac5a26c95dce6ac01a3f84d3b412b69a87ea813b6d3fc55b1743b1b91120aa070765440b23bf7e453a6a4067670e2bd0a628ceccf2f1340c55c8dc0959a25b4301065df9b96be0befd84501b020679b6455c774474a71ffe8bb9dda481037e5f90354e09ecade1774eadf171a2e5895debe206da3069d7291ddde45587d172407ab893931fb19b8d18ed7f4af9242ad9bebfa30b7fcbc57d03091ff405dcacee28a8f8825fb60dda58fba19042a2020b12f67f45ef00636488a1fa3b3bb53fa562a0596838d6f7ca4204bfae4a773b88619ac94f1e59b2f0b14caf54959392a0e8931c5f8fbccb33d49d3764a65e982ead2a79e41961d220e621e6564931cbb43181a3a85a96c31652643b27a2823628d37b1fc0ef8f190c0c737d478139e549ecb98d0a6f8d488432fe7e779502dba46fc2e6fa5be6abf5ca0be099b508aac3f5bf8f0cca1cb33e11d446fb6fa3242e1d8c794efd8a09f0a8aa47bb005babdc472f7a6b3fe8e2c93fe33b14ab1a687b989b2ccdf8368e25c1a2826ebd6a4cc4b69357e6874a1463559d46d3c2c039bd598fd872f06d0e2da6337808dd3117a7df3c052a3a3c8b86683a2d1602d6cb33fe97005d63cf506b4ecfdb9aeae9848272f98fc3e8f3a9116028369dcf921e2b126f9390a2674d448a7da881a01ae08cb404609f6e228637cc6ff7fd9cfd5ab34b505060e110ba1be6e327d8b8c20efb34604abfd629e17d62838290b28f920516549549d716b529709b21f8ca5b618e22780621fad0b20efd71091232170f4c77918f558b381a0ec335675a524705a769b236eb535be04d8b268393a8b81787ecf2a10ca9a5d41f6519dc043296a1cf3f6f5d6030738eca0268911bce42bb86718cabf2999579ea9cfa0fb08d208d69888b226911b346b51da7709ab2bb5122bdce2508e720c8c0a952ab14c0b7e94c7462eb982306c6c69a4c990efb459b3700d399e09577749644c318e5b71c7bbcc7322086fe747210d49a67f695adc13cb1f881047da5bb03aa15f49f83eb8e83bc14d506a58b1703f268be028b6fa6bbeffdeab2a1f5cbcbba2e0d1a80033957db69ab370b38e70f5bbab1347d7bf2a5a44a3546aac590fd0432fe4d35727a02d114dec9ace0dbca7e15a042e118fd33957de2612ac80967c1094dc261f65f1c645fd9177cc324fd3896eaf4e92147bd502d6c55bd2375e66b775295b5661eb856652d6b054b246dc0a89d4bacd519405ae8dd1b62642f2c711571112f202be04841e72b5ae130304c91cd4813cd7ac5fdf608bbbcbe6762bdd63e4de1861b269e3d8097af085b538e3382eab95782f5a088c60538b6cb7e0746293b055992d09ac159514a6917879a510d9c5b70bfc477b5093d3ebe1c739995a8e8ce4e2efe257321e6035f8eb5863db0ad571c39f048f977733f83a23389b6ca94f1f9e74bd47a593c5f1fc5d27418659dea9046af87b1735c8a3a04c2d6908446af3cb551447f30e2a9de29b063a781f0f1020ffe66637e6704d56f87f501091cc244b230e2778a09244ce0228d4c409a038ce39b15b0d0d2177cd7902accfdb24dda3c0871bd4da87a48ec3ba48a9b19777cc3d7db4cf136b3a84c999a131fcb0a4cce3142ccffc6a71c81ec0826bdc04b44d21aa707e4105a1f2444c5c884482a8f77f264b6dd5ee20136e0a1e98769fef1f9c70bd92ff1c0dcdae0181e1a169d37ec0e02482f6bca60bfba193622cdd1839c19322f0eb843a8ddde102a071adff04e5ad8b843b3cacb1aaace723f4e483ca283c411fe85b60ac1e72ba8662b17d9b312108675fc3d2ce29c8a3648b6e990bb4e08f0b52b33bf0287e6f04dad9e55490e2df712aa7110107f14cdacc2798dd5c9d17a1971817f63b977d792c9b09cd486fb0cd1d190ee880b540c062232301c6bfdc720ff3e61c24ea8f5db51eadc5cdb5bacce8b6f4296615df03a9f298d94d89828d224640257954042fd10f7c5c1b8ffe1802a13f4cae88b8951689677312934b40b65406d8469ca0b74438b619db0271b43efbb7ea0d90dd42c8f53bb37216fd08fc043863ae5392ade1e96feade194a75d0330d0e9be91916ad7599b0515cd165061d7b4d1f3a7e5b4dab5bfea125786f4e98cd6e0826e0e17b5978a43408060387d8829bb99835ffb015fc32c57f92d14cee5e3148d5b0ee2acbc8b2d4e31af1b40d042dd6d8b0b5b43a74cd319befc65db60e33d0bdd3369de9a558b06d76496a6d0c12cc7704626a0557430089b98fcff721f23c326f68b988083af6f390f72c13bd02b12f9a1cfc210a92761a6f7525aa80976a5b7d4b569056e40b38d844172c29eb54f5fec6eb594e0661345b59d0aa5e401ce74d480d7fee2327d529c9eb7275fa8515a038408d624e3b7aba150c449200c3d2aa19ddaf31a52312515c8ff2c08f2fcbd717543d393c580348c44d8df39b94e9253cce69fe08c28a4b0d273dffc2604e89458ca8920efe03d542e00b3ea3f8cf51dae30c6a4107b876a2791bdde5be58febd9ad6d9a880f5cdd742327effb16126a5742b0a200a2c7d340dd34601780070a26a3bc148c9ad1bd4c763c8d64ec07bc9c3c10401245bfec1ffcf585d7ed0788e4c021c15152845d8e7d754b6fe6be07ef6b6fb0c1ba634bb2fd445e612f28174ebf7f3a1c0e9324fb95eb8daef004149cd0318e7658736f2f060d5d9b3e34369d496af6769401cce11d4f211c8d37d3ac4640a6b400945d774b985a5d7515661a0ba26d28e16d0a5cd3cf38ddf5146968cd264a09027c233bb5f8984f8154d7283e3e0e6207b9ad05fd0fe0d2d2a8162a70171d8170ac69e313c0f55358556cef5c2c902f4d764b28773824fffb8c397821f3c73a590475a1d2a915246d63db90c7e7d70df5f291af46c43d3ed22ca99c8aef268e31524b8a19b098c10f9b5a798cb77292546c3fc703fc1101eac74efcf81fe4cbb3ff4311ea62043102076da305ef7a0be91afd8af8f420e0110dec8bbe1642aa78f93933dc3c3e77752fcb823c80dbc2c6aaadfb2398fabd8b0f24caa16f1cf5bff46b7519ccffb3488b5f91c95b56f50ce04c213feb3eb65dfa35aeafaa1e5a96c5b5ad2847bbba24ba8c7383637d86773333066d08863ef3112cb62e99f93a99691f740ec2287dbe90f88fa0dfcfc3fb6bd238c84d13179a4a39e90e7a809307d02786867125095972ac1939a3de1b17f3c6284f7b6019d5cf4c2febc8d260a5c3e8ed210205812c982fa2dc5ccaaafa67f06ce6e6085cf9bb1c9dccb52c941157d10ea0263c6d8e77a8e70015084ec6ebf3f614ccbe359e4c1524b48e19762916b6244e564554c1d7cec947e0bfc88fdb41f27102989c6783819f9e1e1697ee4ec422eaf727f8c77554e19b615b7cbb71f2cffa8ba16fb91d993030bc3e6e948133faad48788369a9858663ace6a334e8ac970904be025529312874a6582d7617ef55ad6dff120f0e6bbe90c34b84964009e146df0716fd64a21d524c72c918c7d71df5a6c7e405b24eb61869a3b8ff4fd289c9185bba0cdc62196249ee90fb95d2cea863a34968cd59dbd3289588824ecf7364bf19d400e50410f1b6f326d4403f1eb72da43744384f412c8e5e2ec9009fbad8109e0494e73fa03e1d611f11e54bdd0e7043ca9318166c7b1b5c4f6e3509baf9bc40f10297599e4fe53b98dc8acf2f49d2d2ffdd6510963a29600a4e22c329240cc130c952c0a92fb769130d64f403bad4b90a65a72a2f87fa247c969cd45b42cec86424cc5f3d1584b030f0415cbceba8e513bbbed3d9e07af6475671dd348273f4582306a18feee53db3cc3b019c232c0b66edf4bc7d02be2777577e2f9e11028ac24c76b5c3249589f68b80084ec363195c0420e34009feb088594f6d20a9bce2722380c84f55ca7b6e02222736a8a8134298e7cdc15c91bc2d641fdd7b209b0c2217bf593dafa39196c87b5d0e8a8918cf26fb50b9837e7912f350e427f739b7df9470f693f83367c837375898312d2efb80b4b04623db9429671e835f3ff9b691c569d80620ba0392c536ea075932effe58daa647b809804e358e582d8f82700b667781eca6780a421d9d21480ef6d3b2f9ddc5935bf64da404babddf985afbb5fa59362855cc9f8e35e4b5a6a8ff4bc3335ed2ea76b7149258513ad6dccc5b7cf11a6e0367e595bef8e24b1dfb5aa75816da4b7df29472f08479c02ab19e066b31935ce121152ac9d729b9d585ea05d2ce8b159c54c05d922bf4c5bcd82b491d103bc3cdaabdd42ab98bfe29543b0b13140b43094f6b7a6ba8d558c9e474f18650379e9e79535186e5d0643abcd7075f5340b996d98f573ca22866edff3cb95960eefa6ad06924082133c1f1d80b0ec7a7f03130c5f29db2583f99ff1deb84d912047d59a3acb532a609c05c226ba3d13f9d463087be0a6183b198fa713e76c217e3a3b30b6a3f95dfdefd05d7b0f029e3abd618012015b7da247896a413856dc95ac12e1d68c14b28a9d1db040fa19e69ed3ab5c3a57e062575d1e7810c9c4751573601363682344ad2d9f99ff302e89af92f5b579ccc5829f882d2debe6d7cfd031204c7599928a01aa5fdf484ee69237f3d78768cbbc449eb1aec8bf698858c53e9245a8f6a410ba2f2ea5fb74a3b2d1ad8eb05d913fbb3a7c7c0398e392f705d79048eca7e68bce27d4092276776cf7fa0e757adc5f46b89d82f6e70a8f71c9e1cc57e76d1a92d4d8ab4b2067f638093c1478a10ef8c1ffb2ec75d49744ffd6ece20a802027e4050da8ca0450f0d4ba26fc7805e0e9fe78403592f0245e6e9e912ae983ef937945ad9e3f707358cbba86f4894353c5ccdb244f83f0c185af88b76cbc298136b7e4b32042ee278aea4e543c79fabf244fe100105b1c8b39fd60cdfd02b16f3a47475ba8be39fff9ec1708ca57fd81989550783c46fd370ffb5681893c68470964beeb8cefe81d80771ff4db252634856d6ea945905923e8f50fc900f70f6786b215f8293b84d95207ba02bfccccec13cd1c7bc184c99c54079aa9ef445e70dcea6d160fd41f2642ac6a92adfe9c40a01468f92189a47a53d0fc2085a106315495a34b09e92aabb650a48bab7b6883873197a4b5e1aedc0a966296b048a1b74312367d29eccfb156b2764b777fcd40f010615a0aee394b65e0bb4fd85b6b49e659788346bee599579e686e9c58e53393210ba610f66a9056aa430ad3384aa5d593b3bf3598d912f8fa6dd5f844903958f5add4261e6753074650b5a14170a87a838d5f966ca27c29c7f4b55fe7466744dd1724daf9219507d088ad31a0dd31adbbea6d4912fa1085dabdff1fe0244880413b8426e474b1e219cce5e5600de4013ee54d1497c7d5acd018c129739093bf11344b13e9db72ca4425d1d3779efd8a094c4c1a310a6f3020b3a6d119dd7d8a110067d30d0b33248f7b6f2291c14e9faa8065170422b32838090886a913c8f5d30f76aef6c4b4071c612d158aab5ba16181fb5c41fbce29fa679def7794de1e26b3201b36c73e80c56317dd8b6d150f628642ddf8c0e1fa21a8cf95b893ae86c4283a11ac4d9d94d90a36de22e97ccbe32ababe04f7b2ae1adfab42815b5c33b2c1a4ffd9d159fdc7323c298e53076048d52ef22395d890e375083045276e433169c3674664b4cb2b86c3bb0414397f23e6f8817ef84c773109ff4cdd3534d16a6b1cdc576a4a1929c4ab7cb5a391395ca60c69498eee965c54caf5787c65981db86891d178dba6ca184682292c043d4fb44ab21259c68812f52ec514a39fd7bc933ceb9a4bb7067c05861a58b68c9781de603d4653fd065bffb0ff2ec96b053f725ff5d4f7bea2bfe4bf124c34a43b9214ae589cc7ca88c5195e7c51eac7ac76d24388966a702994bcc6bcc1b0b052521444e0e455f4fadf2742945529c79cc78b724d5cec788ddd15e11f7179a260a3ac0abee3324446ce6e2e4854d413a25b295af81406ec4e6a458bf19bfe74ecbf971ce1f9dcbf70acc9b803bc1ea30251eb05efa9c38df30851d767e679bcb7c64e185fb79196eb9dd4151114fec59df37a0b6bce9a6b6ce9a0aa37a19e8ed2167efc4df6440e3e5681a8797090d250ce2792294be4693e3b8163334857e7cf0d4c72014cc6c4ad16ee40a32969dc0ea8a317f21145abf1c4383fc1bdb293b62d816fd0b163d90664ca8303ce14e1dfc31452527f00cad8ee1e662eb37ffc57252fc619ae1b0aff566ea49e05bb82416fc4dcf4a4e6e0cfaa1d712aeeb8521ef730871db57f770cdc4a2d7ebade0440aacf4e2844c3f5cd2893d4820b0f24e3c50d2766943bec8e3170f028c7c637c323d1b52e365a2e32ee563eb4f2e20b01d1a865da4c875ca8bc0c4cd4cea84abd365fa67934218bd1d44f40e681d5f55b903d1ab8e2e40c6e9104a409acbf2e02f28ab60bf391154c612278d0c7cde133b0bae0a9174659993243dc382325a30f7e4c0ae6ff3dbefbf27a9075adfa9dbc09610dd851bd6d5958ad70e148058b7bd76443f948234b685d75f97a9be2ea11ab6f01b819e2507a0ee9862a85a694bdc2af95be8436ec62cef72ad3bafb341f85e6585648421f99fde9018771dc13cc9f71c2531ec0525098317b00658b54d725c1a43c6b2e8684b31892ce566091d0fdcccc50f0993fba2c00fc90c5af2696eca80fd32f05e6ef2e7c4ccb0a86880aca9098d19026668d16a7421fe444bade76339739105c74b71539394d02566d538206872c33e9064725b8969a212487ca963ec029a7b1058c5847089ccccc1e1c50c1a68c9c18973405391062d4eb713e26c56777528debe8500986149b243383b4c2c1f0b6128a1169bb0375e71f7de51b637a9213c670312befee9f4c51584ff0c29903b673fdc00baa9d877374e3546ad001c43a111643345efe404f4a3d690d833da1ed564cfc3dba3ae6e2be00ebf36a4401012b447236ca4771a9296daa368547c3d0a1649e3a3b64382b1d19cf2d4885071a63eaad4c15896a189f516bde265fab55a2b763f0a5a6394715058c11c9a603ef6b9d7e8f9b3ecf928e77e147812f051803133d1fe93af97e033fd5ef3a0eee45901acfa64c8883741481cbfb7ace30b8c70f9f38b4ffe28a7f9e3f1717d518432bda2524c422ff76ec885bdde848566bdc54c669c5f6c78851e5959ab3f31affb854da78ea320770a04d8368af42fa2e3bfd5154596cd0157c76af2eaa0fd9356fec3091f665c087f42b0df9fec75a959a8f8a7053cd8d342115d373a4e63f071321784680a1e6bb09b220a8fbd3a4d6d9d87251bcb32deb9961ff1742ac640521c1af81d7d8822e3415e2088a24dfa67e0f2fb92b10ac47d6973c7fccb592d41770b05a3b410aa632450e6be561fb5834fd7902694565ac4961febc8f57e221b6b6fb5539dd91ea3cb011d695365994380f2e539dc771f56cc17ab5cffb6f92a6abfc410feab997175010e3d2addf4a89e13e801827ccb4e3f42475ec4ac4995a19ebd6ab016c8c6169a331a2ae3090ca2d211a16fdc8ad3ff8feb8491ecb952111064a04b992aa356e7c83dc8766aeed7a9aa87443b838e3086f012867bdc862976b752220382ccf9d3ff777e33be0c359fd1b39d9336f7d08e339994c4349657ab303686e34dc082c70fd2fbeebe99b10f6cc4f689c10b8e13219dc144fe4dd14a4ce23baf3d19f0c13548e987e105653bb1ee779730816af83039c5af49b296767e5e97a7a06824e3ea6f76fef834b2103fac8b7f6853b48d1bd7f8f6d73a073e0ebd224d9fafa2b698f41cc4b018ef2c806500399ce1b5a4fca281aa7be940a819b19cfa6c21c266fcf2e44ba9f651cb76e7e83300b90a3d360613533a8f525752b78503201695d0c285973337694a5bafd748ca0eca225d7ffdaed0a4ee3df1fae90234f5505244bf368c34508d9723a4b8fa96ba8bf1f1ea5c69a5f15ff75d0f5ae24dcdb0bb0b7d0fb024570041f822e01065cd5a7c15420b83b37f021fb3017919e2677fcd566767889ab0d01b8a24778e9911e9a24040d5a602181a37dfa23152a7e3799aa34f8e2906ddfc15e60f7ec707e3040d91f3a0d450afe8773bc59ed61e4e91d25242c711279c67ecec7d9a645233826e40d7d5ba8dbb43d0909544fac6770adea8008fd0acdcea6e54d0ede2f964361dd8f7f5a9ef4eead1c1a5160c891595a4f0d3565151ccc0a43be51f4440a313859b75d72c285e0d4b3212ccf52ef0c5b974efc1b6acf3910d742e34440dde5077fba0f901825766ef5e75108bb6209c10c22f02aac08daf68d4856aae4f7aea24c6a7ffa9a18e1e3fada5a369ba64be505b075f9dec3ef0fe0fd4cd1d6e6c4dde5a99aaa01d23633162caa75da35f17b00904f1bdb78930692819a6ab183bc7509f442c5d070eacf9ea4056c43efb33896344dd9358e28d89383d39499735c606b9ee5e6a7a5d475ea9d9bf2047e8589757705c9a1c640f22b01aa3da8deef77733a1cb6c3c233ee1b59d6bd3b0a8f9ef8e0393b480ed5d4207735c4e3d7f781b0daac1816306a916ac9feb500264053a5e099960d575cc82c64c9c89bc8653cc7b5af9146b9ce3af58f6af33ce220860fd0a6246ca3cd56120d84fdcfa8b60bfead927f1bfdf5df00f1a8d7aa2863d89bbd9d631ae0352d1a31acd01d18d0291f6d740ac455857e850f9b15f416cea59e527bd7a28ac8313347e53ba3f620b1ccab77c9bc04547f6960ce6b43c1c3160e7203d627f99810a371ca00806f95250f63d378191e190abfdd0f912f41ce8fd59b30f828f9c924477be2ab5555e5e0799ccfd3b4d74cbf4e0a68114994fae4d0190052ae81f6913382c1bda6bfe6cee105426e8c465dbc5119e078aee86c22090ca04401a9b81a717f683a9e63eee92527fc4db675522360465c5a40cd1443ff3d7fc0ed07a2b56f220ee51b6ec962d5caff238e47605caa71cffc3efea7d275e70d613ceb4b304002b8e6b4c137718b284c8c4ac9bbb1c9d3f43917f22d08a3dab50213c14d718497359b286d0deab066c4d373c4b8cafa3f0e28bb80551bfcb021dd1323a6b9d6c71cde601a10f9ee9afa64091c9aefae8a961c0e79a9f5bab2b95be5a075bae926ba33244ec887658d09470d0ae4b795784a497b78d477fc20394e35ccc0ba04251787c683d08fe7423d8505653dfb1d66098f5d844fc3fdc4c8a3965741c13b8409bec829c610880d2de21bf3a00149217cd9b0154d7b8b57c68817a56bb73b8e4708387083be92a9af46ddc8a66aa733e61fbc4d1946d575b83cb8c038721189d2946673653006c0c6d28298ce92ddf23ad4a0a300ea052adad7f02843e17281c6ee8e112a56b4fe0cb6ad39f83f1a6c36bd060689c05d80cb40daac9fff90c504d19b2b8ed6000c647e21717dc4ae765d697e6470666d1a4546299e43874554adfd3efe89a00c4d4bcc76aade84d590a8e5aa626c0e5c21fefddc8bd1f8e556e9c76376716286362ae2e4e4634e012f6a2787d0d435fcc9cd56d7fd3f117fdc046d007b7c3ee6fbe5b7474a4ab797f34c7739f01a0571e66fef3891c77bdf1ee67adb6639a3c5bc8840ee83186dfa04b82673c4bda1ae69917f17d3a98183078080b9ce221b408726df0b5a0000af55301f897ec072a11a057263ac35c741051ccc108488ac6baea7d3d9d132f5b4a018a7f7bb71e7edc23859590e256388b0cbac28919981aa00a0a55bb1db3d4e02f3a921153a3d0db7338cc3fbff94ed85f9f04a6a926df267893a968c2207940831f7cfea054bd26ed64e64da88c07ef455b68381d9dfcefc59f1ddfe8bb7f952288c48e68e5d4811af6a8ba88ca3c83f476bccbccd0aee4510a9aab13de8aa5524006dfbb7f6a067efbd927a6ec566a3f7659fed19f33ca26526c94e5b88aee4bf9eb18fadeab51c01f82657d7908a97d8022916feec8c394e5ee9cb103da773f218bcaeee0bc704aac385b45530961b6557bcfc27bc0260675f73e15b96051c05233abd97c2f97a84eba0f117b404b6ee3bb5a333a233c0550e2ada9094d99fb00cdd740aaa543f94c5a85057842d2f22f0f3395b117abcb96e4182720e02a247c199e6a2c2094617613d378a473c567a84b0ef3f555bfca2620a821f06e243f447a1e205db764ce29b18a6102f3890c064ed3480042c4ea0bcfee8046fdef0a1219586657584b59d87db9feeb8a9ba39aecd0ec8aaf1abfba466272620f648c6eeb147d2311fda6f8a4e18270249369f270a4799000b1005d8958ab7fb4722d874105d0a3d797094d1f57a81a80624514db3012d1eb157a56764217a3ccbf2b91217b6c2181b0826f4b0775080fb50bd64091d8daeeed24ec284b48842b67883d3c8872334ca6d5a650986e1253ad6f2f70214db39fb14c55ebd7f67781cb58c388dd3300bd87f0bf4eafa70b9fc97d6de033f577d31a4710d2cd997af7cfa33cdacdf9e2a9c532003fdded5a790709ac2144d462161a1c7a03cc7c4576e16157cd864063570eb68f47938440d1b68053ffc689c890a7402d87616715648251aab294a69515898125e84ac3bb841a8ed0fec23b94304b7e3e2aba3dedbe7a1bc5054ba9be3dc0520dbe3f71caedeae023e2bc5446cce18a21fb42cbd322199deb982c726e4e99bc720ab414b3388a489b060207239975df86d1b7f43cf9a376a56703c75d6b11ec3c217e0695ce97ffe5259d31621fb6d490b69767029f5e695268d175a2a32e4025d2296758fd6ed4360b72ded173a4ce9861a3ecdde86f85ecd7b04a0baea9a841f85d144297cf0919f132473efa1b4372ab6ea61aac046b70e7bfb0142e33b4817f5a55cf63da55762f0822dfbac5eb090abf5b246441096ecfe21297dcceed6c23f86f8ce698b846854f28448cd0a26b6ba066e504985f447b1d2239d4619834239536d4c514b9d2799a8602d52aa0f09cb554c688c73df83bc5b53c904eee18fb4ad45c64171ca6ae2148930e742808d2cc22f108d4480bccb972bfcdcbb8203d026c00e46bf12ea9dc82298f36810c84ef7c130aa8749d84e857dca096413651ab60095525f32f957e8eef9f3301327cd0bef2c20ec7557116790870564adb48653c5d8883191947648a5308f1240b0376980ae390d7b5fac2097018231ac0699104b371f9b4545daacada7e457aacc57cc35b263bb38e1905fc6952fcfdc9b35c3a67bc04b12995709b34d061f161f380927a3bc66583e4b5e9650a1c286d23a95d9f0394d244715d613d1a7888c1050ea0cc06d780b2fd1f922aa3951512a93b26a811542257c228216cd6546ddb988f8dcf9e9ed5e49e6dd49227e6b0c8aad28fb4babfb28e5a68be17ab7b31dce24e8f898f32b22732832b1d0db6a22da6a24b0ab36597681a3c7aa038695b4b2505f100a44fba77c8aaec84344cf10c1ac1352352aaa21f99c2a5536b4b200c6a5ee0ece5738e4292b884741ac8c9301d32d1b1af4b4594294a624e5b659906b09d9ac279e9047f866ddcbcb98268677e9d19aefeb2a865bca9f915ae9c231b8a65af5ec78ea78f95b94d2eca456c033bd82a49f49beb1f61c6e5adf541867dd374758c65377e0050c760a67b561dd2c6cde541878c8250b453d40108439c7c4007776b0bcf4296596c821ed44fa750d9947509b8b849ebddb900c5d9ac1621745ad959933f891523bfdd4a03e3c2facd8f7ee2a2a173ebef761429d3802497e4faae632c870ac6c9669da139b6c8cae71656f073da5411e1b2a76c8da261ac7c1d99d14201d5c872b5a2e18362dbb2603001eb305949e24a2d05347a83cf986df90eb8cfe0b7c372016f6de3b452bef950ec609aba810807497f4b6db517458e1678dc85d93dbec7203c1a918a36d1c861042577f3c10491dd488d7e8bb112b0d4880c08ab2d04b47e8000d13259d48092bffe8817148bed613e6e58190dc1742f0f11eeabc2d2dc9262c5e8b66992d1045755a009f80c03aa5d466c5b83594ed5cb04a03d82fa98557c9c3d36f7c98e08a727ee48731bbe43b3863f860f8046647f6db88c0b0be971df18eb6ad08bf6a103ea03ed0c3b2ecd62d2c0508a680c8e783c13dd499d768725c7816e35e0f8d06e962958cad8041f67d88a1c4a3fa13725aad14781bacffd2ccff5b3a4cd7927a11ef9647a0ea727e2a62f69d341adc9d34ef3f110124a80b9c0d198019a9a48518a018d12c1c309b40c209841b7263d067799faf9608c3784d0d1c970cc15186311213ac40e4c84c6e182eb51569f3cc7170e9d2aa3542374962d27bc740849b7d75eec52a7e1f3208d4396ecff45f11db378cca4139d36d2162c089e33c832bf86d76421b244bcb18968305c52a0dd8877d765cf95f95b094400eb6086389b3bf964d94292f09bc64c2b3c8fbd2e2ad97c4e9bc0ff08c3e78257009fac80103de544e55f36a9441bd27d735ac73eb4ef895321ce8661d561c9cbd1640b0380b1606404b72530c809ea89363489cb4ffb930bf2291e7a084ee7066f6b832a157ec56f18c26f822aaf38a1a286c433af8f88ca5c09e9b83bf0183b708692d55d34bc5a8865e251eab1b04ae4859e788ea61d36d7e232bceda03293e7e47e7659180c5b443a8e053cd698735b461d96bc91d9ba7eea37f7838b715137e144ddcbf23070ccdacef8289260d5e47a82b22a32974b0133381048c3efa249a3d19d49d2c5e56d421afacfb2688222601a8fb84456ed93ac07028d893c6f628afdcda9ccaca7746b81c04bcadb5fd58158f53e6f130f80f6cf094547305318f6c1f8b8475f2f7af773229698771c3b01794cc5198340b3130c5e41deaee2a3a3d4d5e8553e265828669cd869c10082a9832f00424631875700fe8b3bf830f04453bde1f6e49dbb1b665b19fb62ccb327a21f05975caa01e09e63b7b937007499d126a888d4008e700bebd751f2615ed574e8fddf849b9aba1d02cab812f9106546e3ec950af3bb5bb8f21aa5d72f7c232dac4df0cbd1acdb99147390e19b8a148bded0c51e52050e853bbd5ff07c53aefdeef8278fda0670d722ee29d137c600046be5686aced5035c32a1c4626383c8ba5ea85af50741ac6a1e91e9d875097f83ec971403215a2b6d2b1f73d07973273af966f6b508dfc12b47d36fcf644940a40635402e0bd257bec83e8579ac95290bf536dc6477ae163a865340daed0d7b5e9cc1efc65e9fd0af1a18ae27a2e6eb8eeec4506fe0e6f56ceee403557e5e0a9e56aae004eac9d23aba65f67f9d3e2be5afaff634b2e195fc62d7e4e5e4003a1ab16bf075024097bbd8aff99d0aa3f78fa1f58c38b1b4fae1b982d20b04a509fdb191330ca02bac4da9c381e452b71edeea61d8953849fefb937b727b850a4d52cf5145f6f9c33ff107cc4f9ca775ed2cf02a56981919ecd8c48b5f2d8af3b04ad38878e9962ef0c0e226bf241d3fcc2e2d36c487ff6d29a8263819ae7066ed29aab8b2219d1c09965d5654036d59e1d14d5e95d691983bf6f5e825382cdbbe23689b98ac6bb02e36e88c6273b18e1b5db91561b5c6f43684bea00c620ceef8c4f059267dd62eff236d43effa562f1cf9dfe6ed70aba918e9dc9f6e6459817ed2fa36d4048857ebff596ae339410427bd5c18e376942375d413127f43f74afb1669166e7350b65139e0d0ee3c9a5d91189400e193787490dd257cb9c8285e01a4145bd6df026f4f7e8faa6d0920de89da199a6858af7091be71345dfcb6e3067831560f6e9d6f210448637a046d4aaa7092a09afcc440580fb66803990b511211088bf26584ad6354e0ace8b60835eb58882dc78fc1c5d6753feaa23fb2ebb2cb53c34d042abbeebe748898fb3365d836e1c5195d5a32a8f7668e8c110232fe081fbce6e343bfd114a406b380c904c1ec05866d738dbea29a959b1b6adc9cf580b17100fe4d5f7fab7bbdb37eb95506f026d806c19c9e03b4c852126547e009132809a1917063568ecbacd750d8ddc488c75843b773c48497c22274c290260a50de1328e889458af15e521058c8f4f4c456ba64ee0f9e50ff910cbedba0df7858af009bed4a5d25b1627cec6ff4bf0e20d1b58fb711536d23670174d725ca0143936378cccaa516b58c59fa8311ff9a0d83694df1110f66c6949d5da0e57d000c7caa740e94dac7a04259d837c67c91cdf3c067119276c6d2a559184567f61f8e2d633bab659549d982d08d4e1aeda91f38fe610c61c0f89b3c6ef7b02d03bfbaa89b23f475fc5739f4a3ac3da3a876d731debef1b5b1f1b06f6c7d36a547b97854a65e78c616abe672519d34e2013219a3f52c96d609de3e424a66964fbd8c7702e12a38c19080a05d971dec52bad74ce51d3f8180104c9a353f66c2cb59d50a5c9f91f36900d8a834bd4f6a55ea60f738d5583239c676e57ce51d24de97ae4fdce94351b2c04a9daa4cd434706bbb10d75bd0acca207c8fed18adfd29cc845756d2305ffd91289a25a5e9b58b0497387d207fa71800092cd73c2c5ffb57c53efa7d8bb0407abd132300f44d445caea5cd328af5cfb9044143ae0d66ef9f9e36168fdc4f83c7fdfe00a338c080b043f492284c459f9ec35d8d8617d9af5e90173eb68f4c0daa48cdcde2522464063dac72d403adcc0bd3bf1150b382aa7c7cd3320e5d09ca00805774be3a281cfe87e757255f0f7addcfcac08098f1a0ab87d409a7f1529b30acd28526475d426c788826542b26c844ecdd80bed260111af3637a035ca5b1a6af3c3f37976f347ed6d862b18d7317058360358ffb8ac0afee326a9d2b83bb0a3a648b95832fabaaba5ff3a3951bfc28dde8e06fece95891612656e9ab3e63ca9b4e6740802bb8448f3d6a4c2d30e3e4d9d98e238957f3103eca5b25bcc072d778f3dbf3865bb90de040f59a47097aaa53a77798a42c41f1df93228b5c9a874ec9e495b08249463712e6e2d55e6a205d75efdca59a2adacb03c39583474fcb92ab9a86ab23ecd38b48e58442a8f22a56d1e1fe3dba046faf01b7325262a116b053bef056cdd93dec93ccc90a437785a04b07e241b6ba2cbd99a0e44e85e6d5554a7a98ae5bbac1d68eb075283a164755296d4d3a394336481c1849684daeaa63f3dd7449e0b9e994629a3644e86bd5319d38deb5744d049ad14b4b05802a616ba685b36d448b0a06df1e40c62e266f6eae709092f419a30fd29e5580e55e0a187c0c97980bc0a00159a9d3fe0be2df0527ed10b10b00d7822d5dc959b096632da5f3c33c23ce8da2e9e9379e1635546616e5beb12525facaec7428263e3f6aac6415c3657b2a22e435d6c8cfc82d556f00711424602a6e8744c0eb6b1eb6c496d0a2b1702b2f009910c382c75f8905cd8514a078fd019250be4511815aefdcf3f00a3b6f2989e3976456dd278760013b3094870c6029bdc5c80abde209950b30ee9f68bb903e24cf4faf4f9935be8e1e243922be9c19bf9eb01f96314ff1d3ce634fee6402ddb78fc305d179801008e55f40f84edc761815b83e17803ea6414f5126c39156e1ca879c0423c7cada0e2b9053fbc22e44e1e1e8ce479b862a8bc7d386c8cb0268201f0172fa18988c8bb584bda9b7dd376d27835e4e08a3cee1ec6e05c76733816a0883f839126f6da0c0efc8ceb8712f503cb94de26161ebb26909ebe53640ca12c2b00a27957949f83b1765d3e5e55c42fd47e1e833e6a0f75d08dce7c4269ccf492a44149543088f230bdd42184accb41e54a99c2a7d8fa78c5fe60af7321ac91c42cc04aebe88ccff7f776069bdd6e3528916b6ccf84ac11167388525894e703938e808f7ef1a07efc0a498b8306303232656453b10b2c644afdd08ee1e0650b4fdc3a5e2633effdb1321ff7a2ec1d87d39ba5568f83486ee56dd8685947f94ce141e51c946d07f57d3a0dda7a17779d4c79935ab4117d9c0e9c82cd48b538c95586f0d0ff8b9f49582c7037d6097210976a444db5b371679a565042f68899d0ebb155b635f0de70b47391385ff06ce0792d12dfe8faa8315d25bb529687969bae88fcf859bb0482745203eff954aa3e8da58099d0f221735ec4cdb40b69a3b169499f40832849b13c6bbb09326b4383144683876dff5d4c732a9d15f0c82a4ac4121950e63ca2db25e2630c8eba40f3948caad2ec72eacc1ff387379a107ff9080ffb94875acf4d2bd3ae6ddc2ee0ab0a6f3baab926cba32b6e96958d5da607f37be3356ae2452476728210fd236048ae56c49ca1b1b0c196eee51cbc44780ab4390421a8aba0841c5f6586a8a8f62c833d0000b74e60cb750cd2c34d49ecab63788d8c76e924e5699bf19385247a90b697a56d28a9cc1a6692429a5d05f0f005bf94f16bfbafd5660f13e64f16f42233124d4a91cf76ff5f3140f898002b1bd0a8dce07fd4a6685b8e92175115b47c821666d48da24a4fec1d85e38b812f439e14d920501ad01b888fb578db6660767c4b608cb3325f0adc6a70d7c9bf916246f469c1f7e04933a8be81dd404acf128116641a6c89ba66ac996c09c48bff8e03d496433bb7afcfee7732274df423f7617add25c66df2eabfa363946a2278be363a23911d30cd90ddcadf9f4a00b48449eccf297c210e1582f1b16a2648305be7035237006be9fd04ce67317a28436203994cf21d09033dd436ee3ca12fa9d138e265b4d9c6ca58cc826dca12c13ffe82ad2effd4792e8f61ebf7f2c5b3dd436411ddbc1437a6f52be39cb14dfa00a1fe4b26354101eb1f63d8ac535bc0618023c596b32c090d8161338706870d001f4775f1e981030043b00ccc7f7e2054434ab139f27d403e5c9b3f0ad7e47ed4d3c260776aac26af09a76bce402a13e98f9a607f5a4c96615d8c2ce4d210423cacdf16b380cb10d873266b1911f1c5e23c5942dbcbd430b8454eb4db53f09edc94048552834d1d00321c1199da2c0de88d57d58ee9888e8cab1f281a425f1b0c662eec65d434207c33d07be19d57f6d5f5291188e76afd388341b14114a20117df276c18f8e4744417ad82ac29dc8166390598786bbd8a7f813f7f80b4504c143291be28c5a965fa01ddc389127e7f4943382f9001ca8d0a95ba48ce18ec01ab7ca3f02a953ab4da683706143a76a37e96fcffab537f37fe91276e28ef22ebf7d9ad36d2344d03165b2a0c93c005642e0693b107589b7e1c97537b6aec2dfa785924f99874c1312a91012b974ca9ac63d50b1dbe1b0c703eac964973ece42ee07efc9ed67fd004be60801d549ce1dabb081f1aa5176ee00e748753cb253170c37bdd4b22d705cb7022a06ae695b301612710e1923f4a663b3c8eaa737b373b371921373c9d69e7b54e0ae379bbac8f030643eb67c5b0068e32278681b0314633b6f79f81a38cc2fce9b2c0cc5e26115c96b39605d112b9140b5d55fe0bfa8acfdee6ea14d649252069b0663065d06e326daadfba25761b2163896f91bcb1cc6b6562bb6c562a15d1db4eb4d1909413776614a9cf9d90e400104a5c0d660e975b95c9b0b0673c15c34970d3430178c862324f11086a2835380a5f88b2ae31b88054b392a40d6ac46dbf6b99bbbaa772644162486b32f0764775bd06eee64b1ccb9b823b65a5872ae24775b71bfcd68b59e0f78fa3575c13304ddf5895d7990d8627f2230b42be84b900ad5357df9ed1252e9f4f5692a8cf8f573541894afafa3c29cac5ef2872b7be0b207ae9c2e397bb129c5940282fcd8866d31de267cc3435084273b38100211e5c51e789f9ef4f9fdf3246bed87f41bf7a7df7efcac4b0b81eeed9372dd23a83df0f6938e53e8abd761f2edfd9e46d358e6f08ea5b8f3287bd92c7b0bd1d66587581c6d49da02d116b800067406aa8b4b08b484eb62735c753b5055f76f0badf6860a326056f062dff7404984e57f9f056d92b7d9599779dfb07990d0625bab7bf8e646bb042b00ffcddda658910e6c306b36d87b7b663b5a2edae5b0bb6f34368c188dcc19e02ede009970a3c23098c5f612ba5cae168425e7e27238cee572752edbe25cd6e50ab3cb9a2b084bcef5a55d076eda5a6b6da72f59722ece456efab32b4802360b7b3b6fca76dc86732ce50846084bcef47fc9dccb2dc786f925ccd955202d2c3933e54c9990d990adc8ab0775cc9935ad01071e6f24212c39f3e34ef081619cae2da541c10961b9d1cac7ff59dc5ea4994e500873664186b95aa84b074d61e5d368341a8d46a3d168341a080561b9d1ae906dbffc58a89782ef2535a540488479971889b0db25f62f41ab1154957fb9edd0c6e3e806cec36eef295332aaeae2ef7fedaa05abf2779389c9f6afd6f3b6a7aa8bcf204c79c5e3b51e85bffbc3fbe63a737375b13ff2212c73bdd7e25069b32f0ed88a7daf48828a1548a1882454b10539811f5821c515c478028602f2be9804844395b1a01cae683ae5bc6531c2722b51a6ff5fbf0206638b71a01bdc8139876129c65cf592f2150edf62617d5759d10547bb30ee41b6ad95413d7ca4f08557bb307873ed6ddb1623c2bea90d0b61b9c53c9aafb657955b902f54da9c273db37dff1bc34b7b03831ffbdb971708f0ff560875662ff4c294f58b22df911d773623adc65fbe501f6b31c71ec047d34988bf640693842597f3c5907cb20b83bfac5c0e675eeeccc9590c5336e69b91f16da70f3a6711bed8133e6cf1840f694dd12b18e13baf308659f2c1c10fbe1a0e8480e3cdc0404d21f08011051e30749ac8c9deb2411660cc063d4db44c5a6992161339a45da01f0a04864f27803821756205297002482cfd88d01c10c9413a0577d6748a5cd144e72921a40970f68412563ca1c4907c854b6c06dd27a56209b304c74e2d7848b5a0410b3c1be64d2b76240c2aecf2250c2043989862a096143968be3573160391134128e244105829e824c535225908b58ef02226129948dc7c220b2aa04f6021e4892cb0b0f63f7c3176d379245d028f0b0c1ed71427d861ec932ecce562db7befad6ddb42127c5737f97f3c994722dbbf3ba8e37edb2e374260dbdc73e30d37b5b97b0d09246e8ae403244652d711244ca0be7495158d1a36528ffadaa5b47e1b7ac6d7d02b4f43b7bc8a4ef90b8aa1dba547e912e4d2f8cdd0e3afe819225074f216f49de636ca3bca97be9db4f2e3ebf77f5fb5fc8cf7924824fabc4522d1b7a271d0e86899a16b7015fe9206796cbb47dea707c11311283a01c1ee4b5fab0c68a613e991498f1b6a4cd083df5b1e9e112c9fa28d58097b649648ae54d08c09cbaf6814d432ca28a30c964721bdd6a97715c6b8ebbacfbbb3c10061b63943f0db20175b3dae3c7e1b56b4e82afca3d6d955312494aeebfef340339d773d6ea8e94aff7d4033a5aef35dd276d76eb4e1a6f08dee4b249188135d9108d589ffd9ae2bbd682a3de9bbae54ea3af14b263d026b8188dade60515bf3a6f0cd4d618c312ec9202cb38b83171483f8b67a0ca647d1767f780b95def42ffe57500cffa5b1561752e9f3a9fbb4a4451dd555279aff3f75237e71a7765949145d77298588b034da538cf086af56c64cb28c2bffe38564f96ff9198f5f87afc6c7bfc357fa31fe109c3cfe1e17a6f4f86fa821dd5816298441d0963ce93f07cd9044273ffa52e9c4ce9c8c2f35d57d49db4dd276b7e85c55ddcfd06155753f6ab1aabad7faabaafb914e55176aaafb120d434d751fea989aea5ea4115053dd87f48d9aea1ea4195053dd831a478d40ad05e89ea5fb15dde96e7b6329762618a4390bfb7979473a84659da11b7ffe782218fe7b9fb125c483b44e5c0304aec51702dbdec6d0f69acb62f3b81aa82eb8d35c6982cd8d3baa4b789ce1de1b22c48fc7cae3d48d34a8af3b0779fa9347f99bd2abbc505dfcadbe3100abf2fff4cd8f27ab254019dae54b014a99ed5f5faa8b3f0827ec6440bdf83a4e6ffa8e26e55137e5a617eb4d8927edc3f4dfa3b40f229ff791f2e0db2d3ecae7101f65ac2192bf26e5c5cf630e9457196b4c8f82a2c557d1288fd2e09ff4498694073f47caf81973987c639df727ead0a368d09fe8f04b5af423edbd892e794ecf50c99b9bf0437f237ad06ffa7b92aea4a82b89a22b79a22b59d29524694b6ebaeed18d0e3625e18b420fba01ff23610889cc12edefe97095ff8d1bf342b2e9aec88aafb6784184fe12937a498562b6b7e4013ec0f246b3715b8a0bae18c21148b79eb2a3ca0120bad845f8d285e8ff29e960291500c44d07d68d17c51f618c63d164600325cc0b52c177084b8e0552c1b6b1582c562cc68ab16aac1b74c45831568d63d164c1b91d1c4b0395093cd0d05cb08cb07561ca9c6b979af47cb5a5dcaab050b8b95632c2b2c6685908588b0d8bd43187a4db369a92512542dd6a7599659a9aa289c5625509b40b8323164ae1a68f86f03c1381a2129311a9844b25bfb654da66285b92ad8b7d5fdc68a6ed8b7d3f65ab9df0e9e4d79eb6590e4e909c5348eb841d9902967248b858caf5705f7063703d5cedc6e46a2d979021ae5611b4d5c93a241d169dac8b7997a49bddd4ed622ae075bcd94f4dddb78fadb5f625664375605b4c96a6699aa6e7799ee7b9cd7291bbcd66b3573e124e2122d9170bd0c663286e78e3ba344dd3349fe7799e9f6db6cdb619cb078240a190481496e09212bfb684b4379375e6b4b22c9c89b4afa57ab8f474d5799ee7a9b3a5b0d826034121519aa6691a9ee7799ee7beff262312a9543a394119e11896cd68b59e56abd56ab5b85aba99699aa6e9c6dace2d676b6dae11a9d56ab55aa593344dd35434a5a49c4e2894ca0a5e59f16b574c366eb930aa93c2b05a8fd9b5b81a8ed56ab51a96d16a3da69456abd56a9db81a57e36a5c8dab71b57d7f54d12535efc12666e173e39c2ed6c2ad56abd5c22e8c621d9c62d80a4babd56ab5340dd37499e933d3695dac964afd8b627776a7a3a1b9b7c84273b2698e5d2c77b1ae8b715d6ceb62b88bedfb2d99956f3290ccca66c6c96716ed94d357d9f434f562e8b0ed3197f98cb64488fa2a7f63193e97cd2da3d9dcf7696434a33bb46ff8ca730e43d1cf1a7efaf909b98a9f55a6fae9a79f7906da928c929b2321dcad796d3ebc715dfee48f3dd3da6dc572776f9dd933addd1657beec3ab3675adbb88db3678acb973ab327763cbbf7de34b47fefbd379683b8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbf5eafd7ebf57abd5eafd7ebf57abd5eafd7cbfdf57abd5eeeafd7ebe55ee385fd85fde55ec3f10bfb8b468d570d1b5260014110fc6860df71eced38feec38de71bce378c7f18ee39dd7a3d16834b2b1e3d8db71bce378c7f18ee31dc73ba3288aa20dec3b8e3f3b8e771cef38de71bc3343454545658585e55feb71069e31c3af9d91da71bce378c7f18ee39d16adf53863464b0b0d1a356c601b36fc5a1b29ec3b8e771cef38dea191da71bce378a7460d1ba9944ad5420b007001bbe0825feb420afb8ee31d1ba99d940afc3c2084bdd77589ed6667f0d516eb78e37ce3bafcc19f8f5ffbe1f2c7fb400c827e2d58b31e080a89be10180a4b4c46244c22f9b5a41476640a588a2496d67abe18a3a7b6dd6c660d1c9937272c149c7d5f3c4dd864f26b4da64e91970e7a24459de649bc0ba7f9ccbff09acfce1c9c2039a790d6798f5c3cc5beef18e616c9bebf8162b01bc76eeae2f4a66e1061896f0ea9789ef7b9adebbae8d5b9692d85c5643347cd1b334dd334af6c46abf574288aa2286a9aa6699a2b2c25252525a4ad657bcccb3a7352584c364bd3344dd3f33ccff3f4197a5b288aa2ae8b5e9d9b5e58f6344dd3f43ccff344511445d11f0511d218a516269b9d699aa6e9799ee779faac36ebb9e665ddf3e674699aa6697a9ee7799e3ef399cf7ce6339fe9d309a5a2b2b2c2c2f21a6bedd76a137be69c96755974479aa6699a9eae3acff33c2d2c6665335a9aa6699a9ee7799e270bcb6b3d8e3366b4d0c03468f8b5344a76549a896bb55aeaf64cd3544cc36c5bd66551ab93a6699ae274c638a3a585068d1a356ca4702ae5d7a6c22dc784a908fb56cd55a9d4bf28d6322dac896fe650c4355cc3b5ad8671cd71ada505b45bcbdeaf21a4c6907ddf86eba6ace68153a018acd52ab405dc420b7e6d0b397472c0c706dbb3222c6b12456c7f061c91847dc3f3de7b5fdda82aff9d3a1e33111f783ff022a0c84dd52368e82b181a7da8fca7827db03c389ef05712fc4afe76edfd4014e53634a39d456332140b0de9a4f21f8a0eccb6d034062a02d241515011900e8a9e5650df4a252f28e5c1d7c1f4dfbfb6e4afe4133d3357032d26068370c20d55d1d5abab3ee25b57c639671e382c52657450571f98c24ecf91ed359c6440bdcaeb38fdca5b100dcb7fec0dcb8a4a6a59d6acd58506fc461ad4e88348f73e521e1c7d80dfe1223647074563dd3551027f2ccb6561513941a14e291f8a0928964c26d39b581ef5292cff8120f82c608ab6e4f54124c7e94d0fd23e4e6f620159b68d0359f0e733ce70cfc242029bed3f43256fc447f93c9ebaa761f4294f034a9f36167ddac6dfb6ff7ee56dc8efb37d3e59ac2181b6f18bb59f0c538f502167632eb66dfb18ecde4e3280cff239c06759196b54c6d3f6a34f79f0f398e37bd458731a4fdd28c3e853feb48d39469f72ca417ad393def49f0ebd49835ed4e1a368d197fc64979c3cb1e16eb8040ffc3c7ae43d025bc6defec793df932731db44f4524027a451a85402722007722017d2a60785f417124d27d924de10831bc771dcd6656ed315b002b0c0557901ca9a64db3a1ea73cd298b88d75b7ab0e3a5457e193bf714bf4e7473afc8e33e9387b7323fa1bd07f7f13fa1bf0bddf6ccc1deea8890d7741555cf1e545bc2fe2897a1bf274ef458d3529f664b9ab6b0e535fd9d39edb88efe8202ca67242a590bc128aa98596368d5d9c9c3bdaf3bcfef7c4e12bd48ffeb3e048e5514f52794fa350ffbd0aea49da92275f52413d96d1c000ec6a03b397d90b7772dc9923047d85e891d3554427e5acce8509d1b0488886a9af6ee809d39e30bda167479da1aa4c7eff17eacab6e07c39cf333c7253bef2d8f62f11b0f1e681f78c0aeab1f8277f83f2a5ff5434cba3f4ca9ff4e85334e97471a78c84ead8f47b96cff13dcb5843847b6ecce1fdca584352d1dfa3b4f7275dc9944a96684b964e342413fd79930e5fd4a247d1a13fd1a091893e92d372a13a292c76c2d2339d7177c4c52c5b51bc31e1487c31e5dd23bd97fc67752ccc02b12c2c66e073c0b2fc39f1b3e0d5a6173fc5f49e16bf4ffcef4d9ff829272fdc184002f0c5d88ead223d2fcbb22d3b04d5b137d4f4ecf0150ed80c47757110575fc4c77687ea926379b02cebc3f6b779b42c16f799fb3cce6cf9f3486332894f021baef4287f43fa93177df599f4f7a2f61e84133aabca844e329c3ee56938a58c27ee53be9234545775294fc3f729a30f2239c4f7de878906ef4d6f1a7d885c2541920ebd89fe7c890e7fd3335412eb93aea425b1aedba492674ecb85eaa4309665cd4636dc1545812a11a6612912e988641726ff5798680a25c2fa1574ca9be56439b71da38c7d4f0df0cd3df777776f5f04d3f55336054868c730c87891acaa44325b45b23b96a148c682ee3ac326dbcd08e97659cca6312979ef41ebf6bd20a14552fa4a96de4b9ac80c95bc21bd09e93d6df21634f37da4cfe44fb4253f0b92e14443229994bc31c6c33f597fcbfabed05853f29ef735a3ffbeb721731886315506cb7c40c6ae4a935d565a195369658ca522b43f43254b7f6bb8a013f6aa0cf7fe06a82b8b430c375a737beeb931e6c2acbe5f79ff7da10bdd9b07de33271ad2d79dc341273cd27c9f674daf32dd769812b72eb434dedc841e14620f7059dbff7bd2e7f89e34d610d9be66347ebf8d39bc37196b4af0e743d187400f0ab9297fcb02c34a56f263cdd015b66ecac3d64e4336dc151d813fd6c464744d62c2d1674dde83c4c7253884513009638c87f857108d48074b96b48eaf4483efe9cfe44716c4a37b9168f4a237118ddea42d59a24dded3a3bf333cbaafa40d2fc67e72c7d831c618638c31c6db07f3e09e04763b05aa0bfeab4df08f30fe8c3aaa0bce5ec96e869307952c88e6c67b51973c8a0eff448bbea42de99c3ed198986c18638c31c618631cda701714830fffcd8f4c8624f837ac1712fc1f21f97d196e1e295abb2661579e289cd82814e99efe5de54be0bb65c93228c300d23a72e8552eea4f2fea4aa2fed327196c48f067b021c1b1c686ac2192e3f4e0a33e470d0d70bac08104c71af2f4a2b6e4d575dfa0bcca57d0a9439dc2fb794284f7abcdb6b56fb83037ee2814bb045d907ae76eef0b0eff08f885428559fbe22b51ccb5ee10debf633d759f2704056aa84095d5256c7faba3baf87b29082d686604f971fedb3ed71372bbf218013b327393b4f1ae3c4770320e5e7ff015af2e2b8f1566d5a5fe7731c618ffc761e7a8a8b5565cf58b62ded8bd74ecee165b8cb1b56f31c618cbdc5a5518a7f67784d0fe6637f131fe8a31c61873b5bad43762eda7a466caaa7dd6d7ff15dbd36b22c69cbf3d6d8e6d5917777eceaafaa1ae6c48a96c48837d87b8aad4f5f185971b31343874b8ea7e03425b4775b9ff1921e4aac98ef6761c67cf473f4c48dd108cdd3aab6fc7b82bf1aaecdfccfcf06108b21a12206a4096f7be2ef89787bdcb1b64dfbf393cec1b23c56c6553d3348190b726b1e1887deb17fb5a96c5b936884b8c7cb598028995d160df243b37dfabb29fe280594cc18228b8081204079e2d48182b5450c448ac071a5841566bd610841fa9ec0816319802f9054e0fce0f446881853b051fa628821131c8a14708594e60053844821431c4064390a5ad99643a78c105921d802003f20e80f6c412b6e019c38718785e9d6297a8ededb2a58923b6f79ef7611348b6f7e20cb6f7ef4414b6f71e68c64b1da0850dfe0734035e544b1339eceff185d1fbfb0c9af93c04dc94f55a6e4a34016201e9175755d331aeaaee9b1490179c9820575599107255759715c684cefefc67f5b5ae3e2bd452323f079ac96167c54c9761dedc1c6e216dedadc2dcd43453a669e250bba0190c827775d30bee321b99c17045588a291037b6413258e203fbac8c48151c1128acec23f21cc9b128bbf24431248a20f5b34478bdfce5e7bf9cf3e7f3795759fb7ddff77ddff77ddff77d8f3fbb438c84d056932b48a18717183c48f8a1364144cf8e174ac8610724701278c22023544fa39125b67f4ac6114dd6e40a234396b062fb8ba019bf444a16a58a2572d8fe270438028c287ed061410f2eb20c770d70e0a2871e7001042a58e4c558ecdb84499529592b76886c7f52f6586c073a80811023501041962fdb617a50228822aa48c1ec0664cb344d1edcdddd1d09dbdddddd7588a8117e134be30ec100b508bb44c0bef5884e4f143d6062df0fc51e3861df7f1ff860df4ffd4005fbfe4be5f9c1919710fbf2bcbcd8f74b626394e1599810eb48b1fd45a42cec5f120cbb44ed5b2b1076e982027609c3be3b80416adfcfe10e78f67d31b62b0f0fd2caa3a3827d3f14b38512f6bda42b74c083daf7c1326a9c85f970133a40c10d535bb6ef44157400e118ad7d0be81051b6c8f6c7fe1d173c4558b0efccdd6214116e395862fbe3185ef0821b307c9041153c364000a9b1c0c88d49440fe4ddf8085940c237e241588ad6a63908b2c7edf9de7bef95b936003888c216619957210e8ac8e4f0a9a004ba2b8f122dfb2eecca837eb1a531abecca18effb622565593803ebeaca5c65ed573be4a65cd9e59c5b175af2801f5879c84d65d4f5232c3350864b028fc3c6ff436b97d9b599e169fb3183262ba0794876edf0c91db7b93e7d5ee6ab6c83aab2af44eec1b64c6cfb577665d9555dac37c4478721f6625ecc57368996274b32c685a93c28906d2d0c5ecca36ddbb24b2fb6add7b3ed979fb92d48961f6b5b7701414f1d9c34089810b1216441aed8f66db9dd6cfbb6dc8c6cfbb6ecba30621b6187bc8eb420816197d695a40bfb85117669c7d856b6cb7bb3453125a63eb54f4f76e5d8b6dfc585e96a5dadab75b5aed6d5ba5aceb12eebb2aecd480c8e1818e8c19243b77d1b4e674b5ddbcd85294396af32eacef255257328b236f48a2a73df26d946847ee7b826191a75b8ca869ea6b249c84ad2400356104d683cddda4dd9b7213f4084e595a1750c72615af6c3eb1243eb7a596bdb96af2ae94275d2f2c2b67d21b2ebde21aa8cfbe7339e6ea849b273c76d58882a93b316c14def4d4d73dff164cd1b33882af3d12208414db2bd9a696e6f3c5916909bb24976d635c966e19c38278f1845ec7be604b17f815c98936d5921d6c56de9b6a5d9b5a5d985f1d7fa6e0d239fbbcddd8643646cffc09baa6506b2ddb7ff16fb8e0bb38d270c8306ea2a5555f72d0efb8611def1090af8aaaa5fa980625f021c607bfd934bd1634ba2b62fcebe36bd410fbb049de706e6bd77c3b56281afd8171761a776e4faf505b02f57c243b4df7dfe3cd65457bd0d6943dafffcdeb0bad8ff00119675d6e5aefb3c76e3c947192ec98dd543135f8ff5b9c9beeeeef7bafb758cddddddddadbd957581b863bc6da019d28f9117a157efed3c9d5dd19baadf87bfcfef67bf7a122a6df6fd5a5deebdee8e4111d8770cc1aa8b8744adb5be9d42127b6dfd5ac3975224630adb8e65d8524710d4d8572b084578bf4211faae5b579bb0b9bf35c83072c53e7d1714c3b73fa894c61e24f4d761010d8ea9f320dd18b07dadf74758da6c0f8b310428c81339f8e08df847bbf224b1139b1ba8d48c99eb59e810cd0000800000e3160020280c0a068482a12cc9a14053eb0314800d5b823e605846180863c1481004390c83280c31c60060102000188490d09c0d00fe6654fbcafe8a32b8318da0a780744aadeeef32000d4c868daaee5804d031121b2b101d952bf36c09916756e6ac185eccf01e32bf7c77cc18c6b88825e554185b8c477e3fac52b64337c0322740bf61f7926db237d67d5ce5426cf5beed8842e62aabcd4bd7908d76e44e26cefef79817800a4a3af3bb6b4f4e99965f44428da5b6d583246ebd807256d0da01bfe5f74fb3cb003d5456c53b11e0f61645b8ae922ad9da39dde9b314829536264e4a0d9ff80d3003c72d9b574d5af077384d4cba9fa1c1322f1c759597d5951a6c95fa4b894890d8c8b2eae7becc248b3c3898914b41835dc5cbe428cd9534cd5c9a80b438ea8acdc80fccff77e181c4ab93a50dbd44b51c4641ee799eb7f3e71e83ab25644dd4b5d425476e21e4ae568a0faf92232f16972add148208ffcded5f1cfb42ded053f4416b737a3cfc11a3ea898eb5f052b9e959ee87449b82392847689a40744da0590332edae50e14e67d0c8989b431fff71388844c222ca4d1f90514d3035179299e71817f3ca64be2fc558f8131fc2a5b941c01126fb604d9f7b5ff2f0a86dfb8cc850b8247d5ce43b4197236d58dc5a6e67817d47c4446191aa80cf85d88a8da5b2891aa79ad79ecbcad87379edef99f00ce80b0073540cc9ba318e9508ab6ca996c1c88875b1039d8a8984f20276986b540b18da32527f83d1f1593ede8630a8b6a202fddf4a859f6eb2a73cf897a5f498e4e5f274cce5b6ebfb435d1f2abe4b9b724d4fd978f244dcf92e69d978e3d4f6fe2120fae8d137e9cbd221de0329f14b992fd02e9f143be553d88eab88794c22759d759a946320035b1529e1107c5b7bc2de3ba43337f44ffc331f30f10526e650a002e96011142d037314834211af170a418c6a02b9775f39e2cb4047d52586a8c14903447f79d75ea1f393f6ba9eea6025e57ea1cf24618158f015eddabc683219169cebcbe01950be310023cb64fa71b8e089b4fbf99a69ac7bd6049051a76f77477ef18bb4a65a0b5d1007745b8623f12cff29de9343ed81ae4fe43b3b035e712fa1abc268ebb733242dbc0c3e0a0ed5a5eb8186def88f0d295e591f174ae4cf32391e6fc3122347caed9b6ade595e01ea6922280650c4ec5e867282256768cecf084d03fc03c10f1ed8bb350d0a658ab37941c701c6b34e89a8186888f2c709744922e34a6630f96242e785dfc51318d373f1b37a20f5dc7cf5cedae0326b2e2c29f446739694cf4622329bcf1294941dc661a4896ee6e0c93bffcb2416d67ff3debfb33f53ed961d16a72b8316fad42858e7f2def1c2a6a441386e756984d72e969ef2bd002ef6af009102469c77f37a4914eb1c1619f7f226c0b85cf2999f1eff080d968bcc27a609f2f638e5e5f618f02b38bc56ee521929ef1796ce69d03fe8945920d1d224b774e2c230193d6f7223474aa7fe0bebd803478caf9351a44ee9c6846711f12591b4b47e4029b432ecc79b3c4aeeaa5ce417111284f9f552787ea029aa0bdf807fead5a6695c8a9bce4c102d992b32f911b64f4804e590de5fad51cd9bd77694c5894e82bdf0b7e403ce6f64bf6bca03778e00dd3d5daba136418058cb50ff0c4b8742c7318d762c5a0d8d1ab63c67df31b20866207e476f01630604aac1052145c9342c8bde20c36b2f61816ecfef6d5d9356597fcdcf01a3021a33ccad4f344b7aa275e87f6a50fca6bd8a027873dbcff79073416b38c7411ae1b8282f99b49bda4caa1bfff4457947b464700726d84d78e8bcf6997a052f336da2ca4a9c37716b559a7d1d29258840634db98bf3951faa9838c328e6f99b95b110b589443a021926e2f62b3ea4c49f388b7b803b04a1f244982a4a5d66fce52601dfecc29b5c3a58636df3b8b2f87460cf9cc3a6041ae01d8213f8b5d9ee04e5b8f0eecfc57d320c3c41c8ffc95c8a121670e612bd6e88b1e711fe14e978ece3b94217485b92635dd05f4162c042695c8685b61043daee6004d3e62f54122268b05694f3f14250f75b8c06f71b0b8a062ecaa061a7735bc9560c3fd02603c20e00dd55a065b875c0bf638ab2d93f5c6e0301f7f062b6c5eb049c02ed93a040ed3d61eff25014640162043c2a9e02b4dd269aff3fbe10697833e62fe54be51a84008b4dd66d9e9131c030f5f341d55d6e3d64b5c44765526bf43f4d650c96b9ef293e82106d978e2dad3d7dbe2466d1280e17007af4ca2068e0057dc54ce7828be0c1209e8e404fd8e8e50f960d0d9ce37e4fb33a065c6ca41b76887ac0c8d610648910f63406e8f4c1ddd75df022cc10ec457ef3a5e23304a6f58076ec30ec9a83e4a75f67dc5cc891dca83fbf7f0a9ecdc70fe20a33ec1621689a4d9f16fb37b4e4625c74b257c9c54a401f6d6b17a48fd44f2dbaec80a0c6e2ae3938dd160200c644960c5e72795df705f00bc14d7f9841a22fd84bb4b3f46cea235c5751ca3137b2b0798642db4e9b2eac526e2078051606e13ae11d15a2ce08d149033792164c182f6108b1d8f0506871d1bf680877ef0d97b92ddb72d789d8f807a75aee64ae01e04801a9160c96752d80c42b96ad9a0a607fc1e9850b9a0813e995bc7ac76bf3a97d12f623affca875bdbccd2686868783ac05893808f6eb0a7c61cc4f1b6a81d5f10c5fe62d6ebe68591b6d38e5afcba33aaeaf3151b81bf03e10868ff3d79d3f1c8af99df3e99872892f516951d4473414bc1b8a1af7886fb71f85b40fb6e7365c8c6b930996cb087e25b696d6cb754bedab00f235b9f5c705d57973405f73c0329b83b6c07fcca5eb3ccd1954b84249c7692ce454f4889087d9abc7b0bf25e679f8537c391a3f6ad89b31c84e0b8ca35cb615ce1afb8c7d5da7c346686920606b3696304845a4f7c732447203ecd33d6915b706588c4c2d2ba5ce906ca8752034a6cd87a1e4a04703ec7e60855239412089bd843b471061040041728353f9af8a228e2b4b9a0869f763afbf3c06f62d19651b5673fe8f5d94f69e61e992dddbbdd7e9e1bf631361cb8cf75d6277fb0367b43f85a4118d11c3da0b0aa63f83b692439c5fc8c1cfeda9616d86aeaa614b513732b032cd9ba81c2ff7430f81953fb4e9faea47118985a3b95cd26c93b69591576c75ec3d24eb14420e2741ae55e6ad7faa1dea7f0a096d35fdc20994ca1dad5b7ee8d13a8aae1ad7a255c42815777ac6fa8a0f947787a9a2ec69f9ad5dafbc98682f7e2bdf630cd83e0b6a925b88e349910ff2604a32ad28069542e125408e87f73adbbe678ccd5364789d42d36a919171bf4a42e48f7d58b2af6204fcb0e22d208b1da6f553154692007ff1cd47c38bd07e7311cc0ffdec252a2cb9ef4a73f95f642c141ffdb8940214593e0a6b7d05b786e1b180b7ddb6da6509897c25022dd0015f6bd717bc9a53edd313021f4472977250b9f5fb6d40dcd29eb1ad2c56328fa43922c7a9840e52a3f6d88dd6cb646eb4ec0c0480c592ed72875a2c0c15cab82a4679f1914d867964497dcbd5ec1e425fa404595b96b46082d88dde4803796a0c43d04d0aaa705692e9970ae6dfc3de3fc10bd94337c1e4e2f3584cd990d8f61328d0d1c140e8081a13a5b0557d548046799c3b0ec6471b4720f4f40c42482e86cb34b203ae07cec719914d224051d53503f521bd4c408fdcec3c560923025c2ef8537caad5102d51d146e54343140fb9b8090ee254363d022e15166812ab2b51644336bd24a1695e6c0a6cd2a40f4198fc2ac67e5e8d5508950e964f0fc67b6cdba2cd188d54d2f5af46077fc10a2054b6fea12bcd9578cd77b06fac79b97c5f18114d8050c359b68597c7d5008e422458c2613a30578590606e408fa60edf5b5cf5a70bcfca5578a522947b811adf2b15190bc774e4cf116b68cc4cdd1583562437fd7cf0163894c00e2fe6d00183ae0cd5efdd23fa53a11f068a89a698f10c5cc785e97a3cae4e3238946203a0f445d6d95fe4d830c5d36c8c88ee87ea28939dfbd0459a5488335d3c21830c90094d9eb333b654495fda6f84e19b972f8809940191749a19ea4333eff7f40c1290139b3f0d0c8b8be4ffccca0d179c57500ff598988bec87e4420403c8b79a72b143d65ac48210176bdd0d40a1da444c54d2cd7a90969daedc7c488071d51ba9db1c647f4a019ce47200a482456e8038c38b63aed044a6bb184f7d2740d1a72c97434e305716ded1657e61d0b4ecc8998ad193a36be4363b1bc9ce2275a43eec2cc544bb8069b16a26572f28f3addc2f8b3591140544c192b54293c0b7953b9bfdc0cedc9d2028f0a5350fde4ad0aaafac2474289f86bd0235d8d27d7ddbc41253ba9935063f0a2f2d78d7143201db0b8990724ea39460d1cf59308fb9271e34fb95c5c17b8164e3d01aefb5592675892a1defd9cad510b49f4c4116b62768393072c233e124b78ac6d9d3ce173d7ba9fa62849bfc42f909a8dec016e9d7ff894cf83ccd2cdce98c0fd767e594ad00ca13262213350f08499c985232e597934ccdbd94e4b93054593127f69533cae5958842a09629777afcbde371b0c0cc0ce4c4a7a5732fc9ed4ee92c258d7b9224e4124159cc3dd5ada5f2477e30828149aa1092d96855525ff41603e584912a7fe8ab392fe8dfec955ccfdf57566d48b3fd4ac2fd416ffa04f15addd2dd6aba7facf7446d25c6a49cd390a8a6ff6c82e1986fe0f37d283a484474105b86dc3af1bc8109b77ae7e3515e75ce5cb2de96a8093f683ca462b66f61ad3927cb27f4d205f6e23910e2739ad16fd00485c988fcbc35e49621c9b0ce2ac02f7474a7542f7e392745497b7eaf498262536430493c212abe6821e439dad33e99b6e04ec3f350f9a63eb79c5445c4bc9d9b2ad724161e898d405b051b154b66909d98a5e68800e248c43c3a144f82ba2623fcbd01045d871dad2a0d912e046cb64c53b04d61a88a770e988b7099db890ae504e92c862056d1828e58f66fc1530f860d29650f75c6d1319817100eeca694c2cbb0429613a09068a8a6d847ef8414cabb2e8f7ec82a54ff833d8e42b3e9c34121733ecbc43078ca5495543ad5a45253060567dd9c4bf9b14f66a1489f05302efddc218fd6609955b0ca4f03017fdd1414c365cd3049f3852d52480a5dc6d1090e6112710bb416315b8308ffd8218fb0a5afc131a34f496b13a41b8cfb7774143eb8745a56b28720e1d957cec7560a3cb266049756733706671ce7e55496c66e5599cfca7c99c1e9a826d7529e7956ce43f9492139fa40e291b18e87a042f13fafc9567ad6b2a0bea5b51252c36a75104c07c7de0ca6f6d077b6b9fb9700ca6e0cc1e1b692783633eef810ca2d5a10d2245a6d698261f8c5c207d562d6ae3033dc0adba0a183388e9741bfbe43751067e6afff94e040d50433c7adc73505248ec7ab4ab347fffbc7710370a36d623ecd7dff71ff0123e4a2226db836ca2206186c0c13689197c2cbec8bbeb0f8c0fb43ae5919920c44c647e1bec65416c827937056f3f028e82a2c5a5e874889542d5551216ee61f73541d87a0b065be7706558147bd101d7355003d82af7907fe1c4125d60a86cd54c70c966325db24b42c41542707897136b0505385e1ab9de3ff4089f77c9d83243e3da294b2675e33f4e9d545b25c78e68c964b631afa236a5527016032996448364afe43aee34229b8a011c53225aa06f815165189c1860e7ba87957ee4eb4a6202b96bf19fb7a6cc4ec7e080a6f6980ce9d5ca161b7961c98aef8607e1b0fb864cfe04721cd96c8b7d314242f3dcae042e0964b6daac80757fab51fae962b6b4e63a8773513efe057351c15c5f979e283ac4d7c82e3c4bc533100838d83ec148897c36e899c1da2f68a4a063c7531354244b9faa2fcb0215ae00de7cd144bbac885b97a457709eb93dffbefb31b4bb931e39d9325d23686c53766ca9bee6f9a234f93381ba1f0930e34daa949c1f0aaa9cc584dcd683e7ba7442c008a074acff5c03c8d58b35e72d45d1c8b1a811ba696a316c8fececed93950a4f422cd2a21060a624a255d55b6398371c5aaf24822f4a1e7c02535399884cd8e66bd02dd423958c1e470a1b2d7e69365f7f96558cd339fe1a435510920a41c2e9a96bed4c5006a1587b4baca8463a9db4d21ea0f90273f593f1f998408c48e1d6af8ca8151497ff5a6f1f3d347c5a72f2d4e86a5d47333cc75269037ac06d0f45fa5950a1e944428c58d4701e9eaf7f6c49b980956a69199dbb419711c7fdb2c0182eb645e49e6530f77d406c2d4f3262820a63ace567f320553363014e4f642bef944a3104f2ac39c55ded5cac922e044d7d8f0d86f7523e9727571b373a601ae17bd12f61973c60b8ce3843a8744b3776ed181fcd0a580a0297fce8d3d462ef60c1ecc07b4d1562a9a8f7c91ce96e10367a9449d156dceb0e774fe2923105d69c4b207b0a0022244d2f938ff8449725b9043d9acdff7d1089791b055a7e9066c727832b187d7588e541afda2450b9ab71e1802a6151606f45e3a3a6008198263d2e6632075efddcdc583255090294599b966eb2a9b694437e0b30d0d983dfa3f8f4eb17216011a71f68fe2d7a1cfcb6a570681121024b3548d97f283b8cb1ac4fe015fcb1fb5643db9d41ddd9b64e7a3da8734411b6b4b2bf2173567dbd51511e50a5a5a12b57d359889414b42a04ab1485a6a2ebab9c90d0f7e368001ded7046cf446f7ce18661790262358ed75bd7872fd9eff53fe5b137b47fa8c77b1289f1128413dabc96478a2ffaba673f46d3a6130a86304986253959c99e989db04ad4799bfd7954db0e44f1570b2e99bb3423484d70e87b9721ce8df33ba581192e424941f185cff49513134f94ddbcdb323128ad81022615108ae3b6008fca4518d8d21623e8c6118223fbc88169099ac8304c4befac28587048d4593902416c3471865e4998e5ca6dbcb4ea8923547eb1758354ea769e2a91ff86b44ab57045336a7af36d8232e8b858f60f67a4c4d974de35350a0c146f36dd000726a8247aa633fb0e147571d64ded1b1960d0f40e66d6e365705b01e20308420ab86247d422a2a5443e4c37e9c5cb56bbe3ab7e9dde8d4c51f816285581bf683838b97a23b1ba38d1d43d5f86763d33462efca6f52c86a40e373fac891f9f17e7c2031258c364313e0456ddb502c8ab02c2ba85003b78f922d4e604a0b55866209fccc6b5529aaeec5483c156a3f0f543d2296281f3614594c6cf791035aa4bb1a7c752eae4ba34fff015a9eb3013a39d9053a506179b165bb94434b2d68316c279c01e1673fc41da6f1423d321b40de20db2bef4ebe13bf35a4aa77771a6ef039a5d0406fa77c78ac186a112655df2996da9fe60c6dc424e2b1bba80c679fcb09a98cc844e5f880f3f9e0170ae4aef161a8c61e4df622028531c3af58b9b4320edc27c24ca11dc18c8aba93e2eace91d15b62b540dc798083e04d3f3b908a6604074650b5132c90ce78a8c3225dc13f1b34285790eeb2155686424c5f44701713954426e100500af14802652ea145a49b587189323339a34bd2d2ff7530e53db036d7de168a0d08c28b076334c61f7280ad8111e890c56e492319a39346b2d2c334b5f708d87c2245de626e233ecc5ff19af5ba834e1b02318ae19ebe5508c472b1bbe3ca8f7fd86eaef86ef32f58ee4950b787fe56db27a12460e83ef94e269f4cc9067925686bd17f60e6bd85edba8fa8c307b9d4ba2a618f21a8b710792ae0563df5e88405c2fe1ae2799f3705dec0b56d609aaf5e0103d9583a33895ba7e62c87c2b99534cde2d233110e1f11843775fe098f7b762abe927f6f76243d377a97a1a3658c24c63e88d8a2bc86182542fe182d35c624d533406584e31a809924337b5dc193ddb0f6ed9b157c0521d3c53a04fa03ca5a9a16893661ab00894c5ad0f0a221840d02518f84b8de9a18b4ff2fbbc97d003e22818010ffdc200e31292f484c94521826853150681dcbbe802c203149c932187f54ce973bc3bfa7aa701a711ed6c55bb2532172b6566fa9b141af098c11cd311855a7e417ba7a4a51d220c7c5cf8a80224a830cc59b5ba2d36f6488086ae1a53eff1154d6106d599235a5996491c97075815e2a561ae78a4b82e89b6efbb3d117434a636666f30f6ddb410bb9aaa20a9549d40f65a376924f45c88f1e2dfd7ccbd6dacd18c38cbc394cb50016b1f5710083d2cf8948dcf718dd0112dce7ea2ab4175f27a077fc5c75432fac123028ef184748cbd1da5a4f7b2a95b59bd75ded420afa15464879753fe9bf8c67f597c6a7aba569083af41a85d1a75e586718ed9595cad3afe53a1f48c60da2dc0be4eeb3adae99146fd234643be6338118115df1fa8ca2b333a7c515bc5ef5813461dac9b529840aecd938b1339cc2d6fd733e04351c7d760c9dcb4246bf1197786f740367b3a190e1d580d8d402b184540d51045ba49f3d55b8bca9700312808a973e1562f31a00984a83fb3ae9b34069dff72d62c2addaf4c10682a47d66ed855a808046c1f93991b92962830b9c4d5a3449e5b66a57af67765ab43d3bdacec016e6ee35d9e817e89e0e5a5a28fea8533dae20fe041d87371e316444feef84821919ba3d87481f2a41049d68b3fbe09dde514f5630654d17a3bfc550d99aa7b718e91c75f4467021573da641b8950ce29ecf8f7e951080e43e573ca76ee1889462dab7302b0136550e4c4620e03ebe9d296ad3b922e17bcbf5f98b22d3deb273a14192c7ccc6c0f18d3101fc65c4ccf448e03a7e5825065ee0678f1d8fe6b07d07612709646ba62a9a82291c9c3442670208d7da518440aa27faa5401185b5cb8eae04682c2c6a4aba5bf364ffd72d0934fc589e15b38ff65faacdc9a02092b31fa230ae32c71a4091843873a112f01ad3bc8d242d13438b8b9b709f90973ead73f3ea2206f59259e89604c6ea65eecc4fb6b5d8b02058beeae529fa01d354f27f4ada90ac6bbe8b537163c9389ab8cd48808e35d986e751c9d9120fcb779ccdec4517cf9f248c49a543a5af8e2ab2eae578712b4980ca1307bc5950ae562fc08467d979b5b308c2cea24a9bf4b73968d2055294dbcb5f1998086853d510ca35155f9dd9b49efa7ac1ad9bbc9dd87727b2047815500edd6a65ce22202f7494970e1740ce11985d8d3012dbda89e112a9c9ad205b41304e604dfc1067f88535c14e035e6402136230928a14a07a38a76be4f6de03bb0152081a0ca5f236967404d552db122f119458df0a43a4246d0fa86849b5153827450fb02b8023c5b39d489c4f8edf34aac6beed92fcece886890b95e04ae355eb9fac6d0c6b30681b9876ec2a358b87cd1eb333453f0fa02bda1ffd976679b7b6cab22df5b9d6cbf336a98936084435f7fa73d6f672aa3544c7de19402a651dd48266e9e1db89f4b4ae20558c055511abbd667d4677f139d60fdbc1705fe389de7b15f5c8fb7b1421a38a1a8291a642336c9959accb3df1cdaa7673ac713347a79454fe4c2044eacd61b6401ab0a86d83d46b977f1230c165482a49557ba44e305721f9fcc4fa35ab81e4ebc7ba453cf0d173601c3520f5d2be0b665c5cbe578aa54e2195d5464b3b6c3006f16496ad7ebb55f06ed9ea8958386674e7b970d2ebcbf60b82839167013f9c725194da0fdb11b2d1ff5687b8d88915969410ad6b54a9b3878bcd945b9c567b3693c5e7b5c8248c761d716beb32c35fb9ab8c07c038711dac0b8b1a028e501f4cd6e5b52b00fca01d7b79c44f7318ea78c176018d5d343eb6d5c26d31205c93c3028d4757e0722a04387dba32d1897d71dfdac0b340ba6e4f80ff98846f8f9c3f14d6b7b135a33b800ffb0dbce8118a809b855859beae4124e25e05cac3b6074ed4680f05fd8ffcb8e43736e51c8cbb8e51164ac375e42ca03d4756604f51fc2d3d12e0675ffe2ef8c4266f8d8733593a327957db5404551b67cdc50f3dff4a114f903122018af45ca713ae60ce07bce889964cadc68873798b631668c2601cc4650491403e108a0f14ec1bb41ef6edf3c501bef1c57c3ad506055f9afcf9add35982f9d4e94e963eeae3795f446f5e55188e8ea043587f39bdeb445ce3a2be9398fabfae5713012d4cec9d64dd470921987d8d3071ac2890b4af53ca15bf7a949c42e81a360faaa605ef6932d2526b5d39a842f74a4120634f2a814dd950c5823dbb613cf58c6f23f918fb2a01cda49c46b31751c54a113144c8f5dc3d3d150256182266d9ff37faeda072aedc0b75f6180f79a1377a31fe40238844650bbc12ad887b98bb7d40415696b85fb4b7104720f8acf75fc321991c243defb4a0a9bccef0366144aae3eec48930b159cce51122b9947a9b49d0253b51ee5634877b96aabd48d540ed3eb81b6bd7bf72ad689d1fdbd12f6de92019b1962a47fd731987432b252cbaaeccb167d559e320fd2eb0a29d45cb17481a59458e7daf60499ce341261e6469c21cec3a29666997d6ced9df2c43f00b6a4330d9a9d211c53f67e0aa2ea6d384eb8db3a8c7918855d8a09b7b860d5a26863d0bdbb77501f932a1763122cbf5c861cf8d0ca21993c9faea924e78f106060d55394144448e093c563f35f8132b24873b14992c607e30fc00e3462067728aac201c218d1277dc879e90881d5dee81936443e2c44c44a94dea00d8cbbe1188a01adeb9470cd43c232960088963dd57e8dcc5a0dc623e3a2f240b6c2f8fa71814c1f2a9343af421e0fbb71937dd0418b4e139aa3e8e4705198fd19e7f2dd17789c99b583054232e9f4c86630419b4a98a927b4e144bd6d6457eb6d2394dc88cfd670b53ce52f6d07031187e483423a4b0d71d151d06ad9271568b44dfc17bd4ba9ce8662fcf1f6502a7218d95157061089a3c59ec244fd20dc4b013db5c930345ee9ef47532094c28bedd55c1ed6cb7266d218673bc4059527f4d7b2228152f3072f9008ebd928aa00defc0a36b41200fbb560a5afd9bbb75421a716a50cf73bbf931298b9ce11fffb8b66d340357fbdaf052e3d7d780707466c34ca7244a2176aeb3af7ac399e4c01f78a3a2bb08b03f372d1facef0724630ee5d02a4c4c9a472e9111c41fd15bf7cd4e53fbd6b46ef9081384fc9e26878f64eea046850ea6542641afbd9f6e5f099805f07d653805569e9d255d8082cf9b17ed7ae09324e2fee676a229a31cfaec2cd25076964b8159f8000daeff0fd5b3d05b46bd16a9d442ab4d10bba1ef9b3d09cbd7076acabe8b14ab7b104a2f5c3d605c555229c4cf0ee0059d88ccb70ff64aeead66c123383a1963c564feeb173a2bc5a8f0269204d9f3e800e81a26e4770d86a3cf0a01696cab4eafc63c5319fac728cee1de8a6aadc7318769f446a1c3a615d56895c0c083b9e4cec9d9a031eda7a2a6323483a191a00cee20634568a8196fc31c4a9202670b849c29eeb77d4151953488093c08307d65bb803a178032356e31b126dc44d5114d1abb88ff8a7b269bfc44cec857dd367d0a34834303dc3b267f92c24a5bbce128eb2aad36f083c5bf9f57086b8b188d9a2e1f3629f69cc015f46a7ce6b695751057f662b66f2a39149adf8eaf49f1cc26e1f077d777049c11b04973dd0b6874ae0e16fc4c9257132df8e94497050e5d0e3eacf7102ada960c1f60e21ec88f602368f8460618481171765be32d006f0368d410f8af37de3dcec3874db970dfdbafbcfdf9dea6aac3715fe65de2ffbc6d4948ac046d117c4ffaa52959b0f6ce7dfc9d886c57066109a70cd915650ffcf72cdc8a3688205c1bb20b21f671c09fb370abda008a726c58b32fedd53af0f774648b720023969b17a261dce36e9d91804e48edd92dd78489301a040b3c6ed9f7bd0c164cc8168b76f3dfeebcc4149282287cf1a9f045284adf5d2424744f5e0b45a99b9b84d4b6e84b19e2f590a67056ad0388e1a1fca3cf45226921a16ae77a2d0677442ba868a8da5d47ac1d97e1d64a06bbf432e0bf21192196dc857570e960989f649f0b847e1b29a8254335fb07435fe24c56de20537132b9c295d79ced8ee178f3378fa57f5922a86bde1de787fd66bd3b5899924320e717add0580cb8470bb87e4d1ee031923bbf90d281c53d0170dd388b356dc5abb73763c33e735bf1e5ed98db100fe76b60b192dd1a5468e354c945412f2e5cb0bf5703b259e8457cfa4efb02589eaf906368746905fc7a46eb50423dc11ed40308db9ed0419884e00b564c1d260d26cff42199eda448e399c1032192bd99a5018d2b599d304a3f9fae12827d40dad045f7ffbc428845ce5f1058f9a036bacd7f9a15a58cc7de3c30cf578779f885174716fea7115c100a0be667273000756c534b0c0f5d3930b7b8cd34de5402484ac27a25fdea40369aba21deba86a8b92236f3a8bc7cdb49eb56382e87acf016916cf121b810c1dca811310710ac196751d362b176f1670e9817f39aafe0c752e89fc4743f083eda8199c6b786b65bdab24903659e7618e0dd4dbcdb780f923a17130a188c156c919502bdcdcd3dd8a47b374eed8af56d118980510496da1e1f7b35d7d97ccc4e092974227e40c755faa256d0bc8af05000b4f1fc0065ec1b702694fbed9d5bc24e66a77c55c1ee1aaa346289c2f6d74297a45f9a95690104c7bfb5171c387f2e011bbd830ddef552c26427aa4c0026577a4b19b5ef9bb60a28f3b037c71435cabd2118d915116d85f7cf1807164387f70542ba94f8ecd69ca9ea8e143e0d891478103fa0e3dbaab2bd14a33a96d3969353c09d3c388eba7517504155331889a5270b990828a6a0141f4ca2fe3369c610ceb0c1780bd64aa88bce158dc8161d6a18eb1e36242664e9fc2975afa7b705ae4e8713fd9bba8447d888cb85e56d2c3b6a5a0edc0691f01bd91bfc6ea5a2c446ba37a5a5ac9036f49971d505fdeb744e7b3a3a25048f12551041bc1b0281bf5bdd0fb304536aa57301d93d52e32dcfd35529e2cda1173f41e9afbf64da329b2191c5060e54da2a706eacef46da7740d9e4cd5bd5d9e9794f6d396644eca0cfff3831eac420c56f201a7581a067053ff808bee963b8fce781f55539cde603dbda54be8fec6baaa5a1a8945095b95a8aacf39f714afebdca4ca94f730bfaea55da094b3d6a62cddcc1e4b8c70b5c151ab23160006b842db4db43869cfe23f091af6f25d3d94d2205e345124dce37a45f5dd94d9573645825eb0b4905d842b1588cf1fc66460cb1ca0082fe812749a30685f13a752684d0fe31f261dd69319b1934868b3ac8bb9549f5d46857400ec90d3278e8b80a01c29f739e08f161c7246e63868975b228eaf0b4b48f1cd7b1b68a50f2714285e81687212c90bb24df5fb3b03c4b1d71256ada02b18e62a4b40dbd0a51c1abf89e0509c0a0290570a91105008dd46e828958e065df346437418b08602eccc5a8a8706e9c391fc367aab9414b41fbbdaca8a36c6f7db75127984e238df3c6afa989c8c133661319ee09ebe147da64031b26a6d72843472b14ddd1787173b2deda6b76911626f0115c7d0f90364e5ba954011b7721e67d3db42dcdb629a5f397d4a6928ac968e1310b7aaae734c4eacbd7198a4c29b22a2c0c1cfb05def9bceade972f96dee57983f90e4f7359591fcd83633638f7c8729177ab9b629d82a2d8988f2f55b9cd77f61d61526b2358c3e23fc8158b088a2903fa3e2112260a7ffc6775196d592a6dbace5ef9d554273bb2c081a834c502f10567f7b69a96bd7becb8d24799022d20b3fa49b246059b5dd03dc650fe488f5d3f49fb7ddf00b10a03a0459f0ebe3316b5003c91df43bb81970a4e68b3a993c7efb4936a88ca0563f8a924dd4f20fd912f36556f0ab1fd5355daba33bd672c1ec8d4fda8840f5cc50e76d8c92366dfb6de505d9576d2b645089f68b54a04d4e7157ffc93025434e9d6438042756240aff5600cd34d5899f589d85ab7c544dd44ecd62c3152b38a4dfd44ecd46db1b26e894d8d25b635ac98a95b6253b7c5ccba2536f59698d6b162a56e89499d2d366b9618a959625b6702cc57a14fe844e835ba489a0a5d849ed3f4cea522ef15b7c86dc52d7aa335f5abd02a7411ba1a5d24ed432f75dea157971079abb845ee29eee2e6551f8bbb1217714995585a53d7e97ec9a96735464632c49c0dd06bc24af3ab2904bf894d674c80c031de7bdfcd86ca3ec43ca386b8713290fc08e1d5ccdc147878863165f25f5f736f3c909bb334273290be44843eeba8b7a152280e23634e762f05b3d426ba39755d4ae6d58d6872eaf8e7070b84332d374affd5145b8e1b5f70e8de0acd521be872eeba2a995337d0e6b4e39d2f36a61c345106fa1323f4ac4bfd864ac5a951826f8d6b53245243fcacb0409a6e5792d045f8813e3faabc69e142ff6a88906c143313e8d5185192216e16382819b6c2c38375419f0743c04c385661f63800e1b2d0722b35a36aa0c9b9d3d10bab5be897468e9edd64449221ee2c40580ad95a2793bdc9dfa0ca4432f4a740089e31fdf9ad2954f49cce0f85a95a67bc9b1041d0e3c9795687366a0335aa228c0ddaf3a873de05f89f4ce59739e6d5c0a16ca01badbceb4a6039052944add5e5620965b3c23b1b3bb12dc4ac1af48fb0ebdeb4d9f407c489545fdbe61a87d8767d6a59789731a7b7d6425554e2de8d3a8d9146644a5de8a8160bd35407d2001b1fc9d0970bf406800ca8c46a3e2372353b809ec0f5512865b8d4ce35e8757128356c3537ca6bae487f6fe9ef4aee86b33593c7ad3f91f1a69ea4d320f980f6d11df228859522c4ef016de1ed5a0fd83c75c77ff44c4490162eb7b7aeb9d09e67ba2d5e1e10e8b9333dee2f8827a3a599f5c8f92d2616d18c70c8792dc47e8f9e57c37997c3bb18faeb9b094bcdb2223f7cc928d50d7903eed5ed075db4fbf7da038d2e35bacaee360e32530e6bf7148023782b8d6c2a57677e0b7b7331290f195a1c553ac6258077f302802ad719b061e51704909413ee991b023e803f4ac73d463b70752e01abe0f47fd40abc1066b47a193e96e19a84166099ec8db72757ac0b70db750eaf60d1f4e0b14e6e6059f7d603111b222323446e590afcecccc5d912b68199cbd2e3626df3795b0aa3d51592429a3864720e0ce282dae72ef46bb6457a62114aba1d87f68333fbb528d0c5c4968b8a2e967b513d1584c02f9db3054013d6ab21876e0b2ed850f6bfb65446ef258fc5f9a2742c26a79385a8e013ce7ebcf0bca9d4b23eb9102a3a863705258ad421e65ace59f25094421a05c7d78ef1551db34c33860d41dce86cc3b29478d8cfb1b32437b5ec83b564c8fbab262f57f1dc35494cc05d173fca3e7d66b4c44306d745d3e493c6cbbb376741af9dbf79edea6bdab8e0f66b4ae4a0c2cd6f29b35cac8a8a1b10a578d45cae45465d7e0cd4cc6ba4bb633be6bc58c2349be8b44ab0c04c610c8360eee658a387b86bb9cdc26f0f62b646448888dc22ae5f4eee2427afa7e02a41ec52bb9b102506bd5a5004890a0b92013359d4bc787f172e65f6e90b9966ea3ca141c553537f855ca03aff035c15f5fca98db19b3bced55eba99a64d5740a753c647199cefad8af3391d75d3e04085de2ded4991928a7a11bb96cb6ce5ef50ecccc33344c0b997f876422e19345549854a245566188e46f6212eb703924afe3a9f24d6235d185c2d4b5646f2b631b722a6471533dd4099bd7be764e63584efec65ea484059de0d8cdae4c8a03f6888662a2cde9f7235669b4a6d54305042988c217b52b457a6dc513c576d5fb8c75d948f70edbeedfa7e6b42ec5ace66841f55544cbe5626d23ff39eb34e867625bc2a916da3683153071c5a9dd0b8fc40ea1d2fb085220c619b2352c9e7dd7a6050d69c29bd0aa4468f65d3cad12a9be8a603207ba370bc94dc61da06d45b251416ce0c40e8e64a751646cb880cbc98f8d352084c08ded7ecbe4d348ab6a4bca70ec3d8d4524ea6750c93f8df84d7723389bd07e1f002822e2c8f0385e29081a034e686bd7812741d422e41ea80c07a48e8ebf893f919dba523de4f0cac17dccf0a9e8f0e2a68702c7d0b24c953b28c09e948c76da2173022f685d952f02d3166d3218ade69f9f3bf12822175575016f2cda1516510316f748de4883025b685e6cfa9707f969ec0d5100584eb5b9892741d7986c6cd376ca6476a2394078babf3812f4ab54bbce4249f8feade6890920451a433e8792f4b9d3d0fe2638737e6864f2f6c41330aa5348ac098081a79ef3e5427beed1af3697aebb94d1232050c5908897e83c50228cf2fb64e4226d96716b69e74c3e624870fca14826145c9932d8c35691b06ef841cd63ff64e60164385c595cc387a97d61e6d37eb66d655976211618115bd582221bee00e457ac6c51f1af2e318a8d3db74c04220e655425f7d5e79ad87b1522d8ec2687ef7262089f29a03e213a986c5a24dcf1795788a44931beef620c7659a92e05d29e28969989b0a51448d7fea1e3e71d3c9f19614e6dec02d751837cb4622b1365fe6af4e26fddb5ea527b91c911de62bc14a1be8264c3a9b1ad5f9eeb8ef4111d93bcddf21bb19976c40ac267e1d3ad036e8d2789827b454d19e0044e6ead73380fca78521072b3f1150f11593560de3221535989ff4fbd945210a165e7b3b64dc10b81eff070991fee0638b922e850edd1beda89cb89a235d815776eb4e97bdc29580e06f9a4c8eea1d38dfaa57c00ba66d9261c95dfad769fc1d093d384fb409da460a661d46bbcbb8528a15e0431987b0e127fdf3ed5fbcbf8c8f7f247f498dafc241e1bc15a2a1c3100f583b6c3cce3b1a01c411351d67c115e5c9684631d62f3cce1a3b3a715fb98c47e16a27231a26525b169058074f8266e8c13f0066448258119704a3ce38ff69275266c476e813ed9e8e3ac15280445e9ca5ac79ac9e8697f3511adad38445996637a0a9d991c210e0e6635da14ed27e2341765f9acd31be276460e5889258b8b0e944a14a1183f9545e8a269e65418a1bcd797e68006336e8f9b3878a5cb1c249be645db5ae082b96da1a733fc80893211e26879096c7875191e1c0525ad47553e3fdfd95669f615cb633714446f277209305e944150e51b25c4241c4fa5a78dc36a43ef59311fa037c2c7ba36b7e496d79b8f367a9719bc7d3dcaf6b4ab91241777a96633d3848459e67c97f6221b6d930da93c077ac00673c02050be1c0e377d3221ceca73ba604032b79a11ed52f060337b93fed98880aa59b62deba0a5b643c28a3c2bb828e4c7013896b428e77de38a8be6b7bb1a6dcc5bf5123fbc5131719be4fb7b3472c0b5c7926012f66815426c5237780304f0f7cfd97a5b3e6876954ab7364979a684fce05238cef14278fef58151b2b15b5f174efc8191f0f62af8a39127d03e202d151ef9dfdbd8ca88e78e6a4a21d7d0518455a808094a242da9880c5aea13daf2a738156671645633f2f97bf3c989ef67d268208288105615e815bfd2c3cda7becf26987372987ac78477d41d8c72411f0398372f62e76334786bd69f513618e5574069e9907780bb7d3fc30dff107a634554901da38ad7e55c59e4154aec379ab9bf44a37698a4803dac685de8fbb3836148fcf7508ccfaaaff4965a17e0d3ec3e5daf83e21182e220283132958438f5d3ca8d8e13f4dd58aa8497c77361315a7e3f32002ba45edd71dfe908219df54a224404c0050f039452e666dc8f7525863d500660dc310638c05c64ebb6bef42a987ea4aa8282096201303faf3198094d0bb39f4f6d3fb96e3c58005902cbcc16183307121e874c40be7c1ed49a4c8c9d1ea93a9d41715c3d49b982ff2548a7a48294eebf0d0965f05aebaddcb9e5e7c87a800cc784eb93ae714b1fdc371e40be72ef83935014be7c2394bf1f3a3b9e8439b198fe18278a192de50598e897c820dc1af316aee480fe3882b29c798770df08142bc576b3d1c06c8e64ba35732c9a665441a78072f17c06a3b5c1d90c16032cd11922e52fd58ee4e5d1d0fedeba112c8c2e6bfbe8fddd5e18d45d1504a78ec7bf01afffc2f1e2972b76cdfe31cac7feba73ac5d6f67e364e28286035b596f9e76257752e60820136d13ce2d1f1a0200a4e7c8b7c49e1886bd08b6fe299509d12874f6f27571293275c8f8c0df05d2546b0df69de7613c570dcb44bf939993bb6661c94466cfab4b3229b5a24d745862949e11a01927c8487a81f7b942105fe39119c19d04fd431c7c54830be4cc7e221b9a59ae01559099f518435f0e51d5e0afa885bcd9ab37a51483c7f1c35950853656bfedcb44c8617300abd99b4a8434c46d4bb048216699c050b0467a540aa709abc2cd28e027f4c4844d9e4d13a82f2221472e5d0dbd9aef8af873e78f31b0bb3c8312961af5d64a5b508950507f232488aa07efdf02af175a054a8a3c8443831aca351b99623203e4d3dcdabaca5c22396f5f87549a8ec40bf58dcd877f62fb119da903ae2e71bdedb01a6fb8f199069896726380b88f630f1167f9ff29288086a2ab2afc2798ecbc2a57829249db882508595b70678706b80b75cc9a66507442c5664dd1c4238a852a8db5a74f5d1d9df259180a32417a7202ab1ee297e2cb6495069d31dfe1a86a540ba952db40b11f4d2f490af9338608fd523bc03a7cf0d8c104b383d333d3c5a5ebf67058274ac029e3ee3782040100ab81c9af3b6788dbee636d45fd691ea47a038da93c463ddb2d1e1a10fcda4dbecabaa55d211e45f049c73684fb4028c6830b1a27df6ff1f50e3bd5c2cd1a9277bac1a482324dca204381651b19ce072e7e2943c0fcfb2a48bab5061383413e4c5e9bea6dbdbadc515c37b946d91a89e2de55a0b410e4b2d5921c2369d018989463e207d31ba878fe1bceb11c834073d721fbc811a0e693cad916aafdf754f660623eafd885b1486fc17b9700b83c67f09634764c8c1d5d2f71b6dcea5af1cbcb5c5716547c9663ea590cb04b030b7d0c265a1836b3e7e532d931fd3daced5bb9a71cca5dd349d865eb76f5d0bcf8a3f9362cf903a21bb371df78f910e71ff308d47123e8c0022688a6779110752990aad3505327d1be1d2c5065eb5d227c30835f3c3df99eb04c495c1e0fdc788a062931650d0ce2eb8f8f0e331c9608636723d693f65838d7423a9fea7cdba58549649b70c6c88d315afd410446d2a3f8fd314aeb4924a64994f43234894ee83ccb833ee726c456e54e789f31da0e5ea8e42e5efdbadf820fe436e63a745295d99d256107c5f4f309b269d15962d6552bc05f1d6d7ca96687a34b538962deddb71bc0b8828009f2bee16c1bb13d69003f8f8d5d293543a3db074890456ffaf462c3671d31d814e9818704810266a0077cfe8e80f2e601049100110820a768df4a8ad59b86348c8d9340289d1b783b72261862acb04590f1cb942e7880bf9d15fb82a1bff0a297398be56104559ef9dc02923429d44c7d48dabe16a2bf9c2d9fb6948af4b07eeb1796bb3a2ea3bf03042f8fe08e8a5cba98a3c2bdc1aa8daa966b4ce33f2091be29702d01ff03793bbb28a90ae6174683eb42546a662bca6d45b3a0c2c2754947b0b2cc71d0ce55de3db4fa06c44f5fbb7e86c7866f7b0f69525d0032943aae4f8c6da68513b1854fc7d33f39f1eb793e391bcec960ed8fa13e72a4708cd422cd8667435409c23c086316d6e95f9cbcd24484e0200c51f1345f19e7ce399158a101d2b996024a9e618a633bf60e69ef6662b698bae64ca1169e618e4a131c1473c3aebb7bb51c5b56cc45974c56543e4e429043eb184b33585b6f65b46c5a7a24609c0294e42b61f033c4883d6f4bd45ff29866c1cbd16d9e2b9792c058150f08ded1555344293a6c60aad9cee94754b5d1df7edf6c27d9d2bcb68ee031c1d0fe2f9a3e09325081949f595cdc6328db56478d05d3d9993583d30dfc5abaf6955d62b9c88af3ade3a616fe43d6c96581d94ded6777bd93142fc7c5beadd9de98e9164b5db95f901e04956bbe3697c2588dbd3641262c62614d76c71aec8551ad2c0ffe446ca543dbc9fcaba7393cad052dde5a40981ba3797876f3c494523481a2ec864994311ef17e399ed0950688361b92b3add8fe1f6594f4266f7308a201fc996e70ff0e3c49fd222b25888e41ea59931fc5c8f500698c7e7ecbaffd4d1d515531db8e36daa17ff1f8f20b3523b123141cb6d0d01f88548302b9b0e91d695e9295c4531f06f232dde5b8e478938fae1f343c10f56bc1fa5671ea762422d141e3252dd1bbe47fa8587e7c86849128987700bd7be02e7ea9dfce78b4a29340ce32e46ab2af33350eb33545b19f9a755d8151c2c0f1105f55efbf77242939a5422e311824fd6a2216786d2e021caa4859343ffef76c92da05a6284a00e851db55a6e0174eb6dc6ca93bd78040d22b741bafdbde02048cfc9230db634352e0aa6ccf64a79fe8f70429d03c4f5b877c0718152a8a1f983d493b0f511857623817b413fdf4a45849f3dd54661280a1abd82b0b5bea0f5120a3d882cad98dd27576a9496ade2ade57ed3ca8d906d387426e25251e49cc6f234974cc01bf65833faa73fd2ba24b24a0d7e778e7f3c50c776208b311deeef1d38d17bd2abbb81992fbf9c7417789aaefd83533548d51e8cd9638a41b837c5154c59d3695546c6ee9547a9b709057dad62f2835c1a9b5c8f39a06780190a6ff1bff3866d9abb75c1f992dec621172dd6d832b9ab4dcb12cd563b3ef61acb9f1af8cf0a54bacededa0c057faae705153bc41a1cbccab13022456710f8b6b4bb6b553aa69c6e1e8a321d794ead798037d1ab123001c72c5473b584bf1d6b93f7dd808e53ead9e2ffcd3ede3645a8eba1ed7a46c4f4765f03ceb87e320e00c017ee7c85a8f30618c78e9d4c2649a0917fe973484f6530d90130de0a7f654e95e0b580cc8b88fa4ea3369cb61da05cbd20bb200775028583e4851203e59e47852f474733a9eb5179fe8c6a9a7d1744df22ae769fda20f3e30d18e587c3073d34491f720f55182a197b01268e9b02edad66a27bd66e4adaf2a7ad956dc130177a5d8c4227053e5de363245e81ce08fab9221998b240710603c4c86a86d79374bb309d4512e812bae47ca6bc8b25cd0d6cdacc01d9727966e583d578a74403700d705b738812e0bccb420a82b6748e5295fd24490dc83fa927b04117d0c9706a5bf6097eb62d58cca6049aeaddedb2ef1445b709db420d023e9cd847fa0ea99760562fad678c74c4dd83989245ce4b84c598052466726e3b3bef80f625234defda029f7e19dd6b6cd121f5fc18a4e8e76b042276eac58ab749115d048274832bfff6a8b97cd3be4b2623bca3969e9d03ee0ce84e29f67772a7e9f627cbad04cc6cd8755fb05e4bc3c08e29af6fbad1b27fa5c66f6085d40950935fb29764383bf533cebd11a92723873eb76d89704e6c636fa07fcc8297d66cf05b8319362c75a1a9f437c6ec6e3917edc73d70814a9f820e52e40b0e9ee4391fc36e0e29790522c16749e1fe1dbbba8250b578052b7f2c47e5006487eba13664beec85dbb9416a3b577b9b5817044955317a49dc8febb45a71e44b9255858dcc0d1f08bf2b325beba7ef415587676ef18ac132e8c2c7600e0ed81f5ad4850ba7004dd3dd7dcd972107ce5decce04530a23b89db9aea6c2b5b9d81d8fda65db27d4fad00898b57e3389df915fc6ceab1a969089c58f17ac435ff2ffe7efe682c914f251ca201fe7bc942acc96eb24ddfb055651a4079abf77a171d3e7845f016312241e63428d5e877128b064905a7c5a9c53ce0c88aaf83d6d0c0ab58ddefb34858cc5547f657c2203daffa9685c30ce3a4e078073228bafdf81b1b00014ec4af400aec164b05dc7f1cb4fa7eb5df4f3296fe219494623e3803409198098c5327ace08148090ca9c89f92236482eebd448e7951815ac6ef9beb520b2819fb02fddc8eeb6e59652a624657e069d066b06d66edb867d45e9f595f49ea3156c2f94ce022cb2e490c7cb822c2f2cc902c392642d57a9f70e0f7a8020732c346bb74abd59387474c878a8c28d3a4f86e74b770f4717dd7dfe6740b3cc1d8e12c894688a58a7fb680079c810793464b673e003f05236a4a4377aa8c9a186c9efd9832483e4370b7585c61531c370af76bb232bfbdd6c058504f1d57ccda4a323241fda910fede8e8082929c9c260a825c1504b4a4a8259ebbe1a826febdcf0f3551778944716d62d9ba43f8420e0f5d511d210f78a34441e5d6cdbb46436a3d13034a36168369bd188888a740022d201888888a8c85ad7b1d6da7babad3ab3039be34a81475695453a3aa51f6aa84108db5325a94a92edb13122a2a22640444d808888888a6638063ce219fef087b1bc427444decd0ef200e9c46cc70e17f058935e5e5d27697650c3047450cb1037b05f2e7a5fe0b18a99a8265527bd9a36dab57d65d2ab69ea882cac26d524d4113bec80f232f27443445e4d3f62424a8a845a443f62432ddef080eb4b2cb1a12c4d3c7ec4a85cd38eff88fd8811c59cc49cc49cc4866254624331a21665368a51f99850bbbe58502c28e624e62406f4c5624e6a1b1149425ebcf09099765eb75a05c479b2773f0f95b3bb9b52e972f694534a29a56c392b125e44e1c2858931b2942d61f44496b225602de4342559ca684994c852a60409f20cea56891b4ab994d41da7f2f8a69ff9a016857ae5342f69c21baa6838835ac439017f45a5d1c2f52eae28a03e852aaaa24f55b44756061738d5a2a3610483e52e54d997790ea9ec53950d679050d0dca4c814e704dc44ee9a41214e034ba6e18893f0cd173c7ed91b5349679d4e83b2d8c498d244134d54c952d604953c5e19cd49beee6a25bdeaa7f9ca184d38c963c7a4ac0926b967330983c768992add0a3fe8dc45cda45d4936241a1254dc6513ebc980dc52b665e6aef9aab06e754791fbe991bb28edd2230e4c22b3c491d1290b1e69104dcaead543caa4ca2a83fd63321a122dddddbdc56ae232cf98b3a9a5d659a525c99d42ee47e5804d61b00375cb29508bdd4aba259d745033a9f4a7c576226fc88093c46078a441aafbf20b175c83e850b724fe3a41dab18e551a963145ae61c7362979c0d5bdd4cf7729a9ac3db27f5259f6aaa594524a59633362ae0d519658a2889bc3142d5a4a52071b2dceeff6496bb576dbee05e2b8a052a9eb3c2ff551d3a5a7f9a7130d18d81f65da91f2c6fc70adb8bb323b3461f058836868523538344f6089c5a74aaeb1039e2dfad79befe6e6c6a646cdcd4d8ae6e6e666e64606dfdcdc707783936d4029d39147cc151c67a6404adb069956ab7d123adbb05b63fee133eb399b69e1624469ad5c5a94f987cf142dcaecde0f1fa34b6b97d4a26536abd2a2fce1a3a54599b118636986055b4b29a558706996fb9d5a6b2dc5b36ef1504aa9879452a7ee4ec3120d8c2866b31560eaffc3874abec2857a3c962754f918936d388a30c7dcb06fc3fbc387ca0f1f2a57bec869b2e1b83d099d6b671bfef0a1d2a2cc3f7cdaff878f50fee133448b32bbfff0216a51e61f3ed4fbe133049576e15ef550fee123d42d9e2c739b7c82f00f9f210f7ff808b528f37d52058c922cab60f173a50a4f77c5497b35ac26cf0af65a9c1f6ef1e3016fcf6393b5e26f9e91a5cc0a954cade8906760a6a9014f6b6a1ba9ac5696840b59caae50c9546685859acaf34ed922901a16619a405b6c23da801c688b7960e01a03ba338c0724d4ad2d66823c7f02017d6d648b89b0826d86a310429037b008228ac562dbfc4ed67ab698bb86d4b6586d8b4dd8160ba10b46cae3044ad2ae08349876e9e8554fe142ae3246eeb749d616d999a5e521ac5195ac25cf1fedd1043262922535c1bc33baad25a9534aa9a5f6474a29957588cdcfb7235f3aa55e7bbb77dba4eca2d0f7bf2f370bf6688005d201ea8174ba6d3185d62553aa0d9a62b7a02b399033dd2e3785bbd6fab83eee590aadb44e0f5a6c2969a020458afb3d72a5dc4d89d4a1a3d3dda21ee060d16a3910abad4970ceff46777c2d46d9aea7f2a5cf652a298cbb944a9a199be5bc28a5c3aa465839611b01366529938dd123f322cbac68a12fbe1e169a2d91d1869015c988b6ad642bf51d1ef4004116a3d9a0ef0655a46d490cf4c5c3900542051faf97d5050e7df1f57094a45e4958af647ef5d0d32b9947fc31614350cf2b2826fba12806282f28d75df43577643d2c1c3a3aea919744db61abecf278b72d58264b59100ed64ad00c5b16ec65290b52a1528162461e6f0dca1725f866298332451eafd5019fb2944159e206210c757a9ab720cde3d016f0cca37ec0f6c7f9a39279d4cf4c4195ccd3c7dfe16e6df6539f8dfffaf56da4c036eda4be9b3ebee4d273749ce3f65dcdfb06ca5c13fa0cb300dd14b25f577f061ce9083e30fd1d94e951de1749cd20f9e3ae54d79f09545398d5bfb5ce59ae5b58512bc02336fd9109d4d9d162fd0f861e428614c911b80008412810c2f65b78fa001022a5ae14aa80949e7b1ea52e1cb72f61e6124fbe3b7c9a76b6d30730fe5aff86f5535f8ed82dfb154946cc501e511985d42d49fdbf5b439ac91be21356eb7f58c3af3e77f108b0aa7bef250d067ba9ba30c8e94d3feaf4e8baee640285e028b921cd0a8c0a9a203c4a1acd8e80cf1f8a7bc5f7a1905048ee0a2257fe9286424221653f0305b9f481a31acae40055ffbc824413d86ba2107ba2e895ba82871099f55358f04c143d8c9f3cce9e30624af21058ca9080907eb24f977cf90d001add90b90d915b18f6f498dd7b45774d19ee92ec3f75c82eba286ffc1029bf19ce9e540fd81fca0d6f1e6187863c649e3dfe812ce34b9a4f2f9fa25e7592efd1dcbd67fa9c6ed1bc2bf5a3587a99b99af0dd756fbeecf85af0a057fd3427508c3a0579510f0f5ac4e9f1127839d0dbb1b56f46987ac0dc9f68dc2df93541e96eb6a284aee79a873c8482db69460db9ebfaf54bbb3fa092c0f35d4a29e574fb19d8efba7b75779752ca3ab77b65db6cd6c161cdb474bddb4aa77c79b8c3efbff835100d04ad75b33b6692268dec36efd692d76e2799439e8052777707e50df97576bb098deb83e90a3cbfd4023cdf3f536a6bbe6ddbbd41532e57ba5b98e9bbddb6ed96ba7b83a6749e90e7a3be5341739409ca1b94facf5a6b057bdbe68c32a572e9640ecd9e949ec3ddb17f1fc66e931b4b1ae9aadf3534e2ab5621c9298511291288090563893c5e1b032388cd070c1cc600c3853062dcd05f24f94aeedc5bc33759cac2188529fa0953b46d255ba9eff0a007085cee2dba2527647061d362c65d96133019ecc5d7c346beba5bc63822dae205872e625b88b0818ba02d42a8d08517b07b595b9a28995b04f94ade2d311f2e927c25efb6956ca5cec2a1a303658526852df2288b4e20e58923f097a54c0a112c609a2c6552a24849438a8be9f7cfd2a157fc5848a8ee4ea385eeeeee141820b0803923fb3f066828628b100c7df18404a92e431c35a14594275624e5c20c99131a3247834992fd33a0e2e68b295d30c05288343ac30b0e0a04c8a3e7050f5fc0b2ff05a61dc7663c015b36a80e28ba08d38592ecafc2473547b3ec4ffd714c3b7e8574f390bb6d86fc650747380ce0e6336a2ec6e8feef0332d9885c68c9fe354c3bfea161d4ddddaaa11d7021c316567c41f43c81644498254280021e3b8491a4061fc22051f1ea973db7684177b74905a80f40f16df1427677f7a7f9d4145eca86150ad08249eeffb42023f7cf3c15113cc9c24a085810449754143d44fcc068c8f2450b347a909132a61d3ab1e4fe8bbf17e5171a723fee0008295e78d2857682130ec0f0d0840b492990408c97bbfb97ec35b2bbbbcf156652367238c0914594dc4fbfa090fbf1f7226ba775b248d91839b802c8005734818510166358ee3f672c394d2f6523c795fb2ffebed645eef7aca4e0a48a1db4b8028d987b0b59b660c1a5cb962d7c7839cd22bb1946d9dddddd3b2395145f1e71505105942ac6586183152ca8518125f797ca08e286d22b54a090fdef18586ace29b2647f6bdaf16b44b138818653834066496597c2082956907d8a22b27f2ed3c0e2ee6ee38516063672a0d8c24b1444442145ee7f29a8e47e2afa4b21f3045b6a8450a3f9458e86508e8929460eba7841f222c6680685132f36e46661cc0324c58c274060c941c919af9fa12c8248e28408885ed2a896a71442af4823b72ab7115ee00f86671e534f5889a20b50f6c75d5890fde747050d1579cce1c0091c726f71220c2e57b844f184103a5e38aa96fbbbd9013d41ca4b13845b85dcdfdf73fbb13467e688931b556467286a318f2821f0a38adc25af4ccc51dec21153ea40ee8c823f8f2854915cf55f1410aaa8075411aa08558ba1ca16efed16103ab30eef2ccf00ca4a5cc8ad8492095342498f93c938b76ebad18d52ba81f41da46fab8d7b34f3d9d1cc8f72ba3ad179168813f177138f3926cfafcf637e2da223df70f42fe1793c9c6ab17164c09f6b35b2b47242934cc34ee529a59473db40992738278fda038c20e916ac5d1cae2f93e69759a3f768d676a84d30f5a8c51a174da34ca45448a9f0291646ff5a697d8f869845c3e3bd53acc76da51fb7bf2de2dfb6108b4dfb3dbca9726ab661112f53e7c946747fbdb4fd589f721c27e652a9e4953a1b3c3935e79c1c374b9d6732795d89bba1a6860dbd76ab4eb72953b62953b62976cac5c19ac2ba1a9037fa87c81b549c525a634acd748dc0dd57c3144d8dcd8db7bdad352913100bb88fedfd67b61b21705079f60735b953d9d88a58ab99a4c35d90320121924ad1ccd8b8c02b477fb89b754c654af964fae0020cb6b982d85ef31a0bbfdf8bf1f76d30b71b7029a34e18645063f26237b5a139a727bee9b1299c4eb426edc834413102b33857af9bad9bfb74c2d26cfd0a84349cd62bbd9ba59556eab508072b0a6b4e580928c89687455a8c22c5faf68694525a27373f5f9580ba4f1fa0e4206b9031a48aa4ee6669d57117f7e24a06c682cd1564299c3ee4e6a0dccdd2ea43b09eee219d345ac368301a8cf6419727ed085a9769b7a8acc95c145cd3355d748c8aab07bf1b2c7344f2d4dc550a3d1c27941c6a30cd154ef79a30f6beaf9bb3366b6a4ab0c934adbbd5c80eeacb7c7d7abe146893100f76b80e1d293a34048f7c0764cb43125a6c232daf7e7bca2e9db650d66de6d67037325386ca1b25d104e57d15446976b3f3663aac1578c3a58aa4b8bbd9ead578ab2c4d25f0bb9badd424672cd16eb2e6aba63f67cd5d73d6e6accd59db20cfa1138858c8fd405e3e28654fd980e7fc5357933929185513b38171f34ab9d94abda5bcd7451f67f9534aee96ba372f3f7c2f31ca048403dc9d73ce10753201b100f827132b0c76f7a14fc47fb1af684996326532afdf52e680c0b65aba719e3f7edc18e3ae54ea24c6df8ba5ae7462f57432477e0f77a9e4bddcdfbbc957d1c577cec95d07019d1f850bc563a53e7cf813a929742648f177001d83fda50fff241dd69c916145d6c96eb5e80bf3bff9374bd3645941c18c238ac4c061280805af118bc9f4e50117c600e3890a5048317ac99719e8a4cc9143e6574741240ec692a397099d0f9d8489a197093a76b86186177d07914cd8cb0469e5454329aaf93c7986e374169af879995080316292a0496a3f2f1358204082f2a2605f89e10b31885e48e609518c5e26b80af3ca7b0a33cb02e9e560a7c4abc1994de8a2211f0f789448464838e2ae9a9e997719ec427d0d98a45f2afc4b8acae4a542d5e9e52bf527f0a640ac53fa6c7c5d89bb5b0df0ce80acdc5f7209c97cd50855decb578d07c5df64496072b3708c2c2348dd35a0bcbec29faf5eec557f0559d96b6e8c5acdf83c4c0306ad94d2f9b5bda52a0221125388c4840e9400a2e825e57ef7def3bc4ecff3c2cff39a799e970ccf4b3ece0bfea3a47d281a440c7e96d7f9b0ccec80276702fc807a132448b76ea82ac92898db2ab5dc0c771d7863a8210da4d76645d110482f6e4707d9425a08c5fc2f092c3f65023c5ff3468ccc0c1b72b720371248c8f03fa99fa6116015f72aee7d85d302feafa7a7e74fc9e427faddedbd3d4de2bb87664686fe97bae071fed4bcca42e6e68fcb84f65f14e74f95094b4be0b18b28aecd235fcdbf5e38dea2b0049a4f322d43c8b450b142468f152da8e469850cd87492b37cc95296854b98a52c8b0f79fe748bc904ea96936ed1a00f6cf670329960ba5593d4fca4f34d5e601a8e3369fef894f963f7501a4e30d37d84aafa32dbf750855f5e1c2c16e73a3a746a920b7f98c81c44c81cf7a751f774cbfb8e47e74e726f1299a3f3fc89348fe60fce0a3dee60f02882917c0a896cdfbffdf61b47254d4d2e1c8d6422fef403ffd148a6a8efb8d9cefd3681b670fe4c265760958ff6a73b5af4007c021ae90142153c4a30fda9247017b9ab6bb756542baaad80618e031e8be8c853a8c5d93369e06422430771d764d2abf94eae10a692e79c43e439abccaefdf474ad6b5dcbb367739eb9249d9774cffc49c13afc1e77cd9f39bb7b382ee68fc37286f8be2959b848b020f7cfd41b6032488cc9fd325509b521f7631a83c1603f3ff749ee47cd0054aa4244ee3f81600e754ce47e530d06832d29e29a30c9fd9ec76030580b5a681528727fe7b4adca97dc5f6a24180cd6c2115e681533723fd735180c06c3a642eebf4b1c26a324f76f30184c06194a5272bf1d82c16036dc305344eeaf310c5d188a4c5ce47ecf0106a3a16464e4fe8ed53825b97ff6d39b255cde2b3712569ce4be36b210839625972c65486720e52aff76a6fba612f71b388a79bb77db4cd7c32615e0131993e8f1cba02691d78a0426afb2942115e5ae6b6f9a78e8c8d3ebcc7cd4f4709d41d1cc99bb4c0dea073ca7b5d6a65276ce223a72ffb4a9015e2cb06a7be92bd41606c1beaafff9aa9edef4f57194cc84e3bdae1a9a70fc6402244042a27f235d228cdf27739c70b7a9ea9f7af278eac9f72f6bbb17e3efa36211567629d87b9a806c5c645a82826988a34589d449d35ffad5fe0e671531e2fdb85bd2482321c538328a5ab4f901e79eb3670d9e220166e1d099261e276f0598e694cc71ca2e84c5f27bb952d07c1f836422f447565741fb4fdfd222acb63a385a741ac3cfbab9ba7b95b57a82d410eeebe3067838fbfff3b7bfa500a0819f847abf6ee0f662ceb435fc162595e172d062081df6d750648959648d2208dd9adfa35b93356b3709357b383fd1b3984378641571acdbffc91ca76dfbaee6ed3bd901f6f727610b9eedb5fc3e70d0e60fbc3639407ec99d3401a144e8e63ce68f2454ba7da5140af7d75d5c18c24b06c684d5a58c16a6fdce823e280c56bd7f763842eda88f3a4d3c64a5750333ab7cd40ebd347a908139d1c99c51760dcadcdfe108f64728c07c12be7c0079637e3f10f7d000529cef0dcbe108f649b8d900f2c6f4ba846e9f929e44f06c4396bc315f55330d6f9519c7ec09d4c99739ed94be04e703ea1678ac3f79c9d7597fc8b3f290a70cc7aa4318795ce5d95d6958632d4a1450f66d88c48497f4557520b8ef55dbdce6b3dc15e4d292702ac9812a59fafb52dee790f0bca40902f3f5f94a7220045e4856a828dbef5ed25dfdf3f15b59a4c312b952874389fa744e986559beb2d6daaf41f9ba4ebcb73f874a6fe71fcd997f0dc349ce42ba346ce45f0d2c3d074eef39b07bcfe12824d77f51af038d804772f29016790820d79a2ab0b4af20116817f7f6bb3900215aa5e420db471dc91c0bc857085311873071e054d22b3b63ddf6292d1501d9580cf6c71943b66f7f0a5527bd000e2868c62073c87b017088af6c18428b335830be18db47b2d6beccb12111ec5d2a412773f0cb7c77b77fd9473dc7a166d9fb9295148c366f33ed98baaef4a7fb5dd7954a7d53f974812121b45829d02d2225213f6da6e0cba92004d49f88a0def3817a6f47064422f31844821ffbaae29d20a920dc7df7f5551368ac9ac0fc999f093d20739fa2c0d37320095405a44444843926df2f65994b5ef7b4f45c17ca7c294e0f78481802a7026c7f875f50ca1b8eb4653165609902e0e5c66d5cfdaf4f65ab65777457c371a135ea9cb5ce0a4ea316c1496b917eb1f101cfa3c9a900d7841cb43869ee62d1a7cfc1b6d9fa5bf8f7dda5c1d3e75e049a4bb8206f802773b7d3d16991fedbb7dfd5803a3740e09a1f5999470d947f69b234412092f9d2a000431a706034f859fb7f02642a3efd1e72d7eae93751f87fefccccf7e3a7fa1afa1b0a45f3a86775eb82a736701e830c70de0607070794d90614c1846cf316031b9933b0b1c12064b5484d3b17d8b041b38d117c6c4799b6487f47b62864830f5f48001e7c5504725e002f801049cef3bc8254f4e60bc7ef0c2819c77a7c4513847190ccb1802303642af33add9238ba65f3f4a9acf91bd567fbf6a5ad0165c6b1e098ca37aa5067e726647d1ccd36cf99803060cc9831d9e6a5a901dc9768b60155404a44b82fbdcc1df572402439df0309003e886927c88e71e6d4af42d5041c00ef2f5f62a89a403ff860e8811c205cca86ac07e7975ed1af014e302dd277d3cefc0ad6e89439056600ce80a30c3802c5200a1c693e81a368da0054d9ffa730d82b00e183af01900f33d8ab4d1fdcafaf81074afa38ba557a4a3729b6483f0704008889d49b5350f0984108ceda3779fa94d287807ccd9aaf28133c67ad6605c625672073481958c763a3554a50afeed59ba0a0490a2c8d3569a236e932dbe88a164ac59457885961b691989e9931ca4081511750e890e58cad887a75afab273bb0e0c5125788c464e0436556050c445f8cb852b59468195d2cd1f5a129b6c9448e981c72cbc4b020c67d42db21e906000f8250afee2a5010740de6fbcfc6e7ae1d435d9843e582aebbfbcd15ecee9286ec4f8f686243f6f7fcdfdd2bc8f2f1eb1a8c1cc5243172ff2df2b29425d5929e58a156ce6e97e3386eb35e8746545096cf3d91eeee4ea974a74aa4984829256d77226e7344e0f9a3572f10783ecda3cca57493d2414a29e58c90524a4929a594529a4ad1e838a5b47329a594524a1d4adf47568a09fe52e6884085f13ccf7442618c3a99bc25d56e4e5d48484848484888ba10129d3579a3d6c8d84a7b62473c8fca911d9d5489f3f0bdf3c8e4646e40393bf247ee662b5d7244de68ee84aa39f247640e19ab91a4a3789e0fa8742e3715ad746648000080022315000020100a088522a14024cb1455563e14000c62884076663c1548634192e43008a220a48c218410028021c420a486860c086f66b9fac23c2701129a5229b11d3ba68969f3f0383200b2b35f5dee2961c9ab2316986b001ac094c1ba0542d172347c963f6e220d80cbe75ccc1ddf5d66437beb964fb07f26a55c56df2c728199e2043c96618c75f1d42d510472d8069f0530ed14f9c3e2128d74e5234e96c87b1386cdc2c6610185149911211b0e3893a6154d0e26ac6044be4f41a7a3d53bb462fbb7c60297d380a9fde85831ce4205c1986a77262768609779f6c605716e989c1e5e52201e03dfa68e6b3a67cb18dc9d315fee6b766b66493c1457002f007a29796dc5f17d6703e7aeaef5be19e5a5246a4089358d09e2cd87a2397b955a3bc8b29c09a10e5fe97835724cb3a5e4c6692350acaa45da1d0a0dabe7d3b7cb3aa04c618fa4491c990e35b9dba641fd91f5e29c5178b6c7acdebca8126f5cd511be74f4ddec324969cde0aebe341e566f2bbc65f693909bae2e5ddec5a485929b02fd4d6afebc6246c7dea9e3734d313067a6a53fa82b6146094fcfb04ae166a0edbfa8016ad48c2bee52a4277dfa614f0b6dbe4c89691b34dc58d1b03a3c2296826fbbf0667cc6e98b0d8abe3985d98a09c452ab58c254d1d009ba62fea2d3625a52980f2faa83e509b668a9ba7dfd86417f512030732fca48b74882168dc343411db62963b075948bfdddb21ee6f286854dae0ae2a214a85db69fba457df78606fdfb70c19790c3c147704eedff0617703b3c36e4c8cc674d00dfd45c56088d57f27fb8a4fdcd9b6f7b54f905f628910839b33f87c8d6b8c78c021da57366959d1f8310f45015a73e3d658799dd2339546f1e103480908f476d29a08d25b04e15002bd56f08009d2ad0bb48334f232a8ba9d012b960a9138e8eac38b0c314a4d20885e03a113e10260515c8126ffc919cc6ee3fa90854807f93c3968aeb7fa828f476b491115f5b91d6fae710d305555c5b6314ee790309558b8501fe9f2ea4c5e32b35b895608114e0cec61e63ff427951d282368053c90c8cc51b24d26fd900b255201ad50edc1c9405ac8c338dd88c8ce24c6c27a2543848a7d4aaa48895a776cd4a4d734f944c9234ce58fce0be69d8cc1f1748ee506ac511a9674cf69a531b69eaa104e078f1320924568a7e59b8f6b7d042d35e61bb09de3034aacbd0981481fe008d354625620387de9e144b076b42d628ee98dcc9551d018728fad62804474c5c8e6606bee35a54fb1dd5dc92c1c9fe3ec80e0de2fd4ad074cbd97f320c024e8ed51cb94e50364111054d44628a84fb1479d2a44df47bebd6325cb8b24447233fa783968ccef680b18ba4c648264e8c85a9c80705cda256059cf7242b717657859debbcae2047585cc11626e3a7fc87ed2167857fb1c9ea01608d82a940dfbb19c27fe381397131f375b1aca3ffbacad074ecb17a58a50970600b612aa86d31467b4fc2f33309aa92e6da66940939239f188838d3b39749e0eda46a9394d9ed97c99a4d389ef79d770e096fa33894904c741839c4f32934fafa0bc9f19fd8e10b9c1e1af406a31fb73a859bb7c5e14173556f500eb127d26af1ee0c42bb727ae11125812b0e8ca4460ad8a1ff524949fe229c85ef82481ed70191e9e6dca87d1040728d526c939255ae64aa978868096f4b96e72d27f01d6f8f4552ab1014d769325b02eb83739e19edfa0371b4039e39139d6433ea5e6471b2ef817f4352dfa2c0041e3fef14185623d9282da0f38ae188f750fd94fe1edd5b64b071308cf5d85a398aaf967f242b1ed0d62c5869014e3b9334859aee46c6ffe45e5a14c555413780a93996a779e0e067745d5f47cf3176be45c0ec8f0cf2ca5cf0bd63920a633715101c6e28a5121f3d19b5c0336b82a465de65562d78a5cb9c6b27e6d4f56a9310cd47d14c0b5e8b8f9cf8c807444e4043be35c9d13e3fc1bbf74aca3081cd51050fe484398adb62721aa85fcd3633b4ff26bda5242b624f7214bb0772beef0598ff3b8c2938159e21bcefbd909a979c05361a466cb303ebaa9bbf1de3f9433a192080120847e9e6badd6cc072330af6403b49be887d5eed94883f3a6fddb062a5d4bc17c59c29b363fbe4342589d4c4dc678a4fb613f965b7fd978a50d13383533be49c03e1102e1c4871d0e67d1a9674bc5e06cdad1cbb1160dbf9a79c2a2c4722508d74c74dacf8c9b052cc9d134d1e23046341ae144ca331568408eee1d21aacba33df03db60d816d96494b6b55bcd914ebeec90ef65c72ac1ef4724ce3fc6ae08e96990025c723854399741d79e8a72e7a77a1e5f7d11d578ff2b0100f8fcdbf2712d41c8dfc3a1bf32afb8967b7df81b92b8d5a59183d83067589c366250cf699861b69546e465d9c6ec23cdb0c514043725a6aa7cf25e739ec2eeb0959ae964087bc30e84351990aae9839c3d35a4e01ed6746e07532e46990c71f86d9bd7ae3c0f08548c0c2b607be97c7e7a0ebd302f11f6bf61e9cb32ad5c0bdf505e11fadd7f6b38656567d76c4c68221e07b31749ca1d423701f5aeada3233ff830964450ba7f80620f93afdf39d21112c90a2301113d8733ca4dc6c9a4e188a5d40e59070cecffb8348a4eb5a1b597fb3c4222e6a20405aea8085adfbff39424898331dd5ec6c941dff5e6b430ff2d958208ee2d970c444714241a6db3a69e4494ee19de4eee45b6add1ea1c51f0fbf4e0fb5049badc9492faa667e1040c9cd2a1f02862ae7c5fa2dbd4cda92f75f2d537d142c75a520a35ad6a028ae2bd893bcb73e72f067a6820c9d86175a0f9416c0007533e3dd2a77bebbfa6ac8c1f768cc95ac145f7053f80320a7a7ab36a1a5d4d5d30942cf13bd86ab36d4ffe670a18e00a8515c92382b76d43a67946ecf34be5af09336468c4ea9695816f087e61e11d04b738ebffc2a5e89a30c0591de379232dcca48bbe1f9b415fb787923f86bc12e47a85f16772cf38ff993b88bc5cb2d27367923210129b0642117ddb93455e1435b747e7f1d288275cf8465ef52df29d153076743d1411264b72af8118e294666f9ed1da7d0cf085671d92c462ede49edd7f11599e1c0a7ab8268b09dfc90bed8dc6d988a2c78cfe5f625dfb3cdccf9caf220c0f14834cfbc66afbaa0318e4077f247eb1c7901cb3e0dd2529aa1e3d76e5a596d00def6896f8a55b4a9703e1bc46e01832b2639986f92786d74c8dbfd86852c4b9d92bdc9a854f4cae30353fd2211d7895ca86d9f577c66c16ea4a493ba01670729f1b3be2cd40466503131797280459c44cfd020a09bb7b6dae7053780431724995ea916f8c19120e49f91a8c1c2588d8c5b6db5a3a7667416f4c32179572b0978a1cd0dddff604712db5bf6c99c57ae1da7d1dea931cf3cf264392c3d76f153e4b506af1279a2e1409f4c1c8e4866cc20d15aeaf19cb039d2f25344f3f4d0fba26b0f5bc71949b14ba63a16b8bd1521607c04a3d84df1e53564a05145ff5dbbd6f2ff0157de6227f97e610c3c3412cc771c77d9e478e255dc494067103f28d1ad7cdea5de5cfbed76df8137c1e1f3ebd36abe77408e10b69d2a931ec356ae87e871bba66b0d058bd81bdfed24adcfe5f0b3a2f0b54c25fd4e0fb0f107711874371ebd15c75a0b03cebf934a96b08e2cd902de7927bbee825ee59a15e864ae60acee7736e7740c27113d2cd295fea23082a42506675df14bf8c8466e23843aed60b8e284dde9c85110831f602533edb820937e7a6a517e4b4015bc358a57598668b4900e0079f265f472f031a820af7d44229c844400293608a86e28994588f88fbd5334bcbe977e30580d7d651ea38138302f6dca2c160f6c84ec83323a23089eb2203f526c3bed3ea12256b7d2feb5795e6d41049a913d993a30bc4bfec32861efb417f23d7c66f648a38fe141842310315357209eee6722e200473c9cb664f9404a9760e15c058726cb8c498b47a9a939f63388b801bf14009164060ea26f7a2361cb2cabb9586bd59237a3a17d863cc720db4a81700209209c4e343ee796ee5051b2c8afe90435ac678c195cf09f42f2a8a030f2db87e79c8f117879621408e5c7f27f299de42061cae227c1ce4207a482559d67f2df2d90c0596078764affee4f9a013a81243a3d044289ac50e630d135ff69272baa8140fc7451d99b1b44d960ac612a1a432441f90128fa7b029665a0031d0ba8a40babc7de7214f08a1c8c7d84f2e57a6c9e71c4d816f73f4ede30f2db7f21fe09f1d428aac347cfcdb114c12034d9893e3a7bb0b6e8cd4ba6925cfddc9861f20ecb97e53b5ebc81841f5e822ae30096bc5044e2f67c04018596e83159d83c447747915f11d6b68bd8a9afef73b048ab693abd22771380ee6e82cc09e922926a3e15c1f0fcd604c273839d47ccad8e387126f8acd82ab17c9416148732777fa8ef18941451867af59a6840d80781422d7b9d424378b0f50fbfe593c48479ad52b1de7bb7ffb1264095e24d460dfd1131f7232347166e363c87a33e4bbb0b5f71cbf8df8e181eb0326e1f89df5b8ca70a3144b113aaf2ac253b9f8e4907949fd176bea2e06b4af12dd3b53177a617231a91eedd49809f3e6a682eca6d4d17b134bb0fa0d56d0609c72ad7f24649b3af616c12243eefceaa4c4c9ac0ba44efc09fadc5faf8c8fa15f224505606038512ef44f175f0d822020043ff48fe88aac0264105e0611f5fd13dd0138c309cc47002499c30fe5750c0b61627413c30de3b48263611912fb0201af8219fafe3e6e043ef091a4a0da37059ea04da8e2bc276447eb4af9eb3cb1deb2ad86f3a74e3b9f1feeadc1d8ca539b0970038cceb7351f4f68d71ce0b39fd9fcbbe06438c78f87c56e492dc9a517dc0c7454dba1c4b939efa13a299a900f0e9c82bc7f9cac559f4883ba67a07101adca7cca6314407d94458dba75be2c3cdeaf84aaf601a80c604970334d814010e5d2f4fa86604f9c4a86a64d12ac12a21e8e628206f654d69bfb2ff65878889e3c9845d6b3790bdbfd0566d1bb922254db07ec3134ba702b34342c48702c21a2afbdcdd4d628868ba34812f9e05aad52152937f4ee491b16aef82882af2e9f119a2b77b9f6b5eac91749316ca1bc54e0ed4b192dfcd70e38aa9bc4f3e3d00614e55632dcab0b6ea8c5449461e4c6fc40c16c0097a55922feff7782758b4edf85bb89e2437a93f4394404003d35e025a9bb82320d70690292f2d7da2d479195213e4880ec0231c15bfb9b88228b3dc0da3d548579a509257f2883e2038771797469204470d0974db671e748537abeb4bbe34b3b25cebfebc7df6bee258b357d626440d916823bc328b440bdb72efa58a6c7454ec789cd3dabd86f85d0efa140d013c6538ed7fa1b6c59ca7350d56d95493039dffa7f4328de02531a8af4845d4e7f5cd32f0befad5246e8420a6535f1a5e92771f828122bee08335f926fda81ed190d316051bf3a4f27ca852ff521cb169ab56f5ef68fa8b74a3f123ba5d6864399b6e3ebe5dd44af2d825900527692c310a5318468e38447867c2783ea51edd383f78ed3afdeba80253ed322b1b56bfac38433adb759422e33ef992369b666acf15bddbdf9856529b3a319a07634e9a195167080c2c99506e221fc5c3f236c9c18e9028952cd6b453561a5b88c24455a21fa22aabf9d662a013391a8b516137d7002045ff07641882c56dca245482ad1052b48fbb53af6a95b4eeb521dba93d9eb9278f123bfbce642c64fe461e87f74a95df65f263826490552389ff9c1f8fc3a8586c21268fcb3a5da1046a4a20e35dc342383a93b493df5c193c41afbd6f6031024b7e7c6bbbcc641aa639b625840b8488accdd7df82fe89d68915885dded95744e9001cb133915c750b467072fdd4bb150bd8f5932f73b173503bcff8f0f960133a50041909e723ee75cf7ec8784fdd55920a92021f6193258df7005bd2de5ae9011f9b54fc6d426bd68ff874232ec2b7020eef28c8467b8b6a6530eeee41c3ad034e4a8bd61c0232ed9035107188580020848a0e6323d57060c1a0b794e516184ca5ceffc3e9281b98188bf2893d118e8c2e8032fdf433b3c14074464153ffa8d27cd8f005c3b493333072075ed5eff26f626e7bd245c11dffb687c4514c9e6b13e7ad7a199d73c3adc18b56895e64ec5d00cca50271453b40845ae29ba9d2de3958011639d1b098f773179b8b574fe31b4e05241e6da4826f2f784c1c35fba79e2c46c61726800afecf79feb97c8a44938c75c0abe45d4cb273b9d39c782a5b638abaa641c7aa6eec0b12a7bb095a3c555c7733bebd926da700a518d26b50030bfd26564521575aa14c21fc51ef84786fe573418dcde2524a9bd09094c8319b95a92933bd225914c0becc2525536dc8c32322ebed56b24752dd799be07b1781fc9ce556d0d6018613eb52300fccbc2cf8550a0f337cd1b96675b9b4c0e2900a21750d014708e21004196e8c08d13f0857b55df8b00fe169fb4eba860ce502d8e1f46a4dbf650a2437fa34872c081b2e6e237466e7f5bd38b4230815039addd4ad0f95b0677ba5c6b8965ad5de19911a6a1df7487aaae4a629e1f67b1c23cc0ab5b0bb307aa19c6fcdbec7f6c05a1aea1e0f06d4eabe40f8a8e613ea696ec5b8be794f161ea5ace5d716c306b722bcbd707c39fce89c052d46ade41ac412fcfb08e3cdec95da00f3d87935c499fa5de23b965bd01016aa8894920e09b8321aabb66da6f62d14f756c432e730937d575ab40713fc20986a360a3818864fcbc1e1342038e17ae522a3f9c5ecf83f1cbafc151a9d920747ed562da71ba401638d8bca560ea56731d1320b49f13bdd4d87c16ba6c808d185818acf81bb97e782558f1083b79cdee1ce43bd26e0d4425bd400c8dcdcfeb0b8945f7272f0c0c8267fd43af2a619a32bd9478f659d62539c61bdb188b534d5d0af03646ee464a058d881537be4e2095b8ae945e7d09501a4ea6dad6810039cb62672da98adb5b902a37e143af71b157a06f85e0154dfabff1738bfde1656c3a1ed390629b78040f58352fd02aa4f97b8484313d5dde1312c229be24952c9a022a679145e081083afd3a6aaa68a9a35abf83f368c81ca6891837fc21b3142373ed53c460cb0013def72aa84d2e8acca6f4af542cb49bbae9c605d86cc4fd4fac75f9dbc9230f9168709a045a90cd44ec9a9848ecbf102650062af390c250ec00e69a8a5e4082044381554d079ea551745d02f6ab1f2b5964a6db67340aa8cfaf67d5e328a851d1a593178a36d7cad12d401ca2dc45f54f821703f5f999d2a1df2885ac5ef9c04e2f2958e6d088c885bab4d75de135ca95897b94ddc5e658514516f72224faee5e2a270d4c0517bb88614ecceddefa710246f4621e956e896970041f28e40a0303f7423aef2522f0c682aa2422d4690ac4d621aeb6e34c43ae5c22bb063cc70183b99c4b312ee23b62f5dda24eaa35e20c1305378d79956690b66ee9c08d83d2062cbb45910edbb5e585a2c0324db20116936b4b612d0dfa378a825397e7fd691478184bb56d1649baabed8879a1fa0f337863a1e522bb6529113d6e0ffbf5a2cc4865ecced69c1a715f6b76b9aa4b304df8ddf0b6d038237c7f81cea7da1eb7894d67974244c787235574b0dcca98172bce3a924600b31cd4c0b6dfd6362c8f507bed45234b21b98129decbae23c9a8b53bf16224ac4a685cd39f6cd90f17348a4cf96bf8245664ca700523f8854ec1e5c8c9a90cd10fed4e6985225e2ffe8d06c35f58b5cc865695e0c834cdbe4eb3b927fd2b572db36e7c71089ea1219c8173afd7e73f1a843f35ec0f5dc3a3fcc7ecdedfa5cc6937b41ac9176a6f7338d8b0d63b631000c130f3540bdc3a42eb93019cb6d5bdb38c1e484e0fc20ad4a99ab065c69908d1def21f0f2497d54078811f0082a70c8551d53148be6708492a0d4d7043b2380df3fc64e16006a6130f967fb4aef00015faa8bdd20b9363da43cd750f1fc539b399a283cba5a64000440ea68e935926ecf8e82b420db34af0820745a0d9c7a34139a6e1b93185f0d65e9f2a52a4011e263f875be94bd201cf888e295e9ce25a004a1f910d747a9007948b08210251d30500d56915cc619c9a020122f0279385000f20caed7b169c1ba0a0cc534717b820c1581018c1260894bfb7ce8c9ec1ce5e177f6de681d65a6c2abc063a351917afb1ff71e315ac2de2290abfa1d915ffe67df21736e40181b489b7e0331d2431ecefcf5c23df1534792e7a9084bd35d6acffbe68485bbce5d0190ef81c184d7d8a24553fbd26f968a5557304ede20aa62907068c6bbb2957122b49d56341f0de148db1543253a608d811c2d53907a5ccce1ab5506831712c069e1782a1706a23f7b7b5aed0b578ab3639641194cf035e0779904c354762af4baa07c7795830fc59b9d158759293b005aef08ba8183527ab0827c2268663587bc2377d951917018a8a914e7f69e23625e976898fcdd415fd34652db624aa33cbf40eccd19c9530111fdab13bfbfae38a7c370b83bab76fd80c12f667d32651a0d1d644d83e728727c2703c1f8f0371a403f78cfe92e163820897e99ba99da4641befa6dea30094e656b790434f8845f0bef3cb5787ba6abb1b06deea73a371e218b1ba2f2afad4d2190ca836b0610c03a49caf62800821c797ed667a376166d7912dabccd4bc90aa5522b6fa79358cdc619126fa95ab96b529fa5272a81b3a078967006bacb2072f78f46b81abbbeb03886c194c6032fd934d345d5c0932b62b086f06c152433214ffe58b3c255bc5ce1ac00da142e14832aae76ab3500f33f1205201e46818d2146fc1c4894fb3b1ab0bc85dc2e5497f2e6e305b56f23f907bc7bfec476a92e8bf9d0d9e59b5f9b1578fa628aac7d579f2f4694ba538cd91667f28b640008d9ebd942ace3a5a386e5274fe54037315c35eed5f2eabff20895fe615189725526b9868e77190c05a2a9529112c4ddb49a5d0dd15109bb6574d0590dbd0bc803f82ce96b928d04312dc2c29a3c63965dc046b9b0a6b99b94ae6663798067b03f560fef3c699a839140c5de794bb931577006764cf36a3d90832d8ccfb7dd8af2af1d3c9d5200496fbb05515301f6402d7715e6ab55da57138503afd3d8882d5d2078e86eb9ca0f1687fcf75692fae5e496e0cf04fd2ef1e49efeb732ec67e2c098c9311707aa359d659403987afdbb4304d6f1fd863409dac4887fd948d5345c74e13d81db3722dd5517835d42fc66382d3f0c6cbb386a0de0c020686b31a2ca9dde146961bb2327e7960cf4a79ec17b1a51838af7581b306a3d3c3349a272c2014db0805a0a569e3782df580cd96bcd9422996fe50968271cb194957065b8a0ef582f00fd5e260ce7f4fe461f91c883a4e3aad50c0435adc3a4563ad6512778b097f986d77c490ef84f4ca3fc2795e2ca3982134e3642e3945a1a5dc2e14cf85120a6ec8cb802d4fe284bf393ad894edb2df7a85bc5edef27f877c963fe211f599bce4ad47e35b4efaa0289ff4f1579e43c3e1671564919fe084343f7f1538fb26417b9a1ce24559196428428ccc4176632fd4ec13061ae3a3c489621890f20f421bb39c39387565c0a060d89c25227a37658711ee71921bca121ba1c1cbe426612a427f73b9ad6bc4121fc4ce269ea48471ff360b27e4e01a41ff92253641cb14fc12784d5a47ed531e9c58f76d4c75725932eeae1196bb6379f048a1f41530c3d6a2fb068afcbba5fa31dc77911aedff099960e1f3b50110be57339fc3f425d4604772dc57e63c09718ff35602f6732fb5dbd4dfedbb3111b947fb3545370537e0ecb5cf3f82b35e80245a5267c9f833968ff3b9e4ce579f91bf121b811fdd92878a551985ea18d54c06370cc991de602f125914cac5495e49d19e4de03e38eef2b2255bce0165d008aa0f11744bc83b0fd4239c6b043083d988151fdd750ce2db63537491072fac22616f8f36611521333f0109613b186dba3de70ca3763c7b34615b52b6150946d984694f0da5c143c972a2e994138ab5fc7a8ec0a29971fd304a8535a36f35fd33a838ed9ea6631eca9e2fd88128e91b2641704da23c84f666d69045dcf1689e7026b24400dafec7c3e0daa67268529953fcec88d9da6d0977d4c3405616ddfe4781747653b303198d7d463c236e0fa083d69351dd8a5413c8cde39c6477bc32c8f2cd5963184c0ff4a8b463bdd6c7850f1136107f2f0fea22d4e935c414ddf781d6b9c94ebae65cb31f7d110ce8421b132966566c088629e192a66df8caa4770ad01c1b0f8d6554dc96310f485bea07359da3375690e5fc467750aea265ac8a0604761c15e8de2e6b0bda4d9ab2072b69e244452d734a7480f291bca8006577e9a2a18833ee35550ea22fb3ee28885fa5eb19ec55d69e3abb798c181526f63c4956d1a0e954875aa74a3dfc3240549b9ab50f6c600637630dbeae0432927f400132eb9701d5e4acfcef9e1608a709d76681a59f3ed827118669afb2bf76a904215386b7a3ae978a95d43b7e291dbd9926829ffd3b8c796626056409c969b9fe172eaaf90d744772062b54894a09056e965b993a81c2066102bdc359cb0a1c761e47952111e67fbda85eb99fb64a82a52ce7801de599206f78420bc7573000ee58175a6a57ea5842a9583e655775014f5673f23d9850412e41f438bfc75b2928f0d7306b38f1a5a00b99ed558827c50221299b420037bc69d91452e34484e422408aca03b3b4039e0f66d87133008bd9f18e598c533dbf501f5fc91e74c24e81a8ba873b526c4558aee3e64a9f12ca807977b5160604b1cda6320e4629887ad937bf5d7c7065169cef2ce33001a0ca305a71a6b66883d0e46d32db504645089bdd0437cfb4752cd72c45141e83ef7bbc9440ce9ca121931c260f9b3d01fa8c1b051a929e7c41c92cb90cef6cbe45c8d9738a3947849aabb44bbb1df092713ccba268f6e9d4e322dacb9a7c574a275222cb251638be9e9433a4be8c0558d764b2289738b4e42b139c73427a8824ce91f126956fb714240cc3f81e24aa8740067eded4a91013015f592bba7186440556d59fd4e90624c00a230fea569e7288b899ec0c8306d0782a3059b1b28993f44dc6f7e79b7f536f3f81643a26348511abacacd8e791b6b90c328b8f966bd9a58fe3d2b4786528c9454987cf7dec73a7ab972f0ed7e0e0c8e309c1c7b95bf178029334bb2c74ef60bf364c52a4a756340763ea65bc7b0be1f658afa87e771265784138d15acb71c818ecef831a7ccb62cc70715312122150d8ff85315aaaa8b24559eeceb15180ad08c63fc4cc8a236a0898714774ddd6f9284008c4e8c68af668eb22cf1640c3faacee70615077a5be564e9278457663e6195044f7b98f97715826047968c084d24e17b49ad50b4cda30ef3bbade51fddf0b2fbd1c5b6e55dfa8208f781e17164cadc276c7e90d9ab331e7b1f67771f83bdf327b4cfc914b8ecc8cd11b855874d90b897743b1f9574418f1556db11280729181d711db429baf8c4ea8ba735c0580025147ece4dc3276dad1445fcc77948d8cc9164959762119d337e30910adb87571bcb9cc125a530414eed023282c92d2131fee6184871b8d0d1441afaeaad694066d2b0deea1b953e870bf0ff1f22f20c2a5ede4648ba7eca0c02d34a0926657d58a9da139ce242ebf302ffea353e7a1adb83e9959e15d8550d96c8bd64899d51a2b99013d232c7ca0edfeeae3cff4cfeb6881e87cef8143313d05a8d6a015c4468ba5d4ca0fe8b325b8361f5c5df789dfac5c58e2fcfb8bd6e966922f7cf8c0a2b8572b59d271118b6932c680ea66a19e80f18783d43a01dae55a7cc065709f35f8c99a82d1a2f6fdc09302a6625051609afecb31bec0d0eac864651813e72eb68b9fb9e029d83808321a6cf39dd1338123f8a71faa1585ca07da20ccc2c61fc0f59d2d7baf75643a7a0e88a952abf7c1eab199e9e2752912c1c7317c1d0f52eace48c2dc307a19127b5db13838267a5a8164aab9512da55c7f86360980b0b10f60a9de67bc1279895b48783cff6a9d3cb58c1b2c6eb6e78430edaee3cdc44e239b3befd49381958fc145ff776ff4195aa64594580abf894826300aa2dead7d219e244c7c26ef8ef5edeea5f3f2035d3391c84e27b6e35374d20b9713b280e43fd144c54b97c8a832f96572bb01e557b35e8b2c2d8ec1de12c1cf8b55b2c25a67703bb9bbf02655e4a2979c5a4ad5e528481ec195e974a0805d8663c5539932077ead0865fc501d033706d96d226dd36de28238f6165c5a0861544d8c68a5c5ba114dad5b16eae535a25dab60358de07899da64a10c3c40946125a2abac2590fddd5073d301c190a11e0551293ab86967e524fe1a573cbe3b07511ed4ef7445cf00177f39c05162d099f24c0347ddb80d6c44b63b510ca63eb04c3b2ca8b5d07b416fdaa7fd42283555bac2e995e29371e77d1b57caa7e87787ba4f64d5b87a696e0f7e09ebf62770f460d423f44720e11d347848ebd3c8c26a9147637f21c078c686e999bfd2fa637d71002875019d493525641fc7f14ba538286b38c7a7f2bfe4d3363dcfdc3e7c91ea6088c8232e3db4ba56f8920de31be8e4895cc8bdc181f3ae294aafa3c108bad00a470c97c649fdf100f16d7649be583bc4bc4855dc2aa2c44c40d3128efcd4d90c59ff6e0dcc4e538a9df772004d296a85312d4833da1e7a96d9fcc8af86dd80d16a1440e795eb910699163f2dd93fd49b55437534c3b19cc4e5c558b676358bb0efae247b57c68563c666c63f917b319c2e40f17ee46ae5ba034f23b75ec984e5bb386653b12ae9def854fe118ad01927ed35b8807fdf02971d689050e0bdfb3cdefd0cc09fd09c0d2009dbcf53366918ebd0c6fad9e49efb620319e27e6bb2b324a38bc549f830fdd08a33d03b64d73e4459c3c963dfd967c5fff2b4a22be69f9bfd6afa23d95cf452d94d8d67d7c1e24479db57fe4a0840b5bab760e9ce54dc224e40dcefe3722b25199032a0a50877083612adf09d9f8406b0713448401ffacb720018dfa1c59697d8536001dfb05f1e7ba9909f96018c9bd1d8cc5ccdc1d0d06b63b897421e6a54c723ac9899a4214def7518fea3278a703ed110f723d0b055961cbe62834c7af25f17821dd325c80df36b3a90d6614e8aaa48870fea5f258108c39725fca16c4eb0a92f6fe7b704b1a9a92096c1ef30eef2559da342aac76812075db40933cfc7bd74c2e2736a735d44b0b95b1c02284073f752820802cf03a5e94e9df7ad9f2a8e16c0834901e9bb5b2f7a38ea3cad1747eff53ad5f8d9fcf7c6586f12b88ae33fab3e002e3cc9a90c5bb77f725a22dc534c8adec096832d919b0efec1be03b07880f27d8a305cbefd26da8fff9956494419dd2d6590cd708e1c4c813db1fa51355450f0d286f00e074753b27ccbe384fe69d146889a292defb651b05b4b4a66c6cacf208af365e86f98453fc1bcd5c402d9e88294d7bc3ece6477adf0c35f8742cd51f7cb053b71a950a84e60ebc8c9b52bd7fc2afc2d2f5905f2414aafa9864c9a5a28b709853849fbb6e55c487f3ae0a379d30a071bc616f5a5433b74c737ee8226834b7e54b474d7989c4004f5a22468590096839c91ca51acc13c3b92cc07ae50d4229101b2b748e52a6956608286d3eaf2465c0bd272474466b9262d97bfece39a0909950aace9b971519ba4a5f78cec9e2de01170080073e5567372d479a04a20c54f438bb9a05cb44c55114e894c7b5e5f45d0346f50417cb440e265cb96e5209c50bef9776a3b943e524c751edc1082fdc2ca89e1e504238af46e12003f0c2a329dfb73c349dfac14ea8223f5c23dce95375c4ce5dfad33ccfaf0406f7337773df37c92fbf80cf36fcac244449321eb718276b01c50f623d39b8ef02af18f416996fc44e62ffda892380e310e3bc8449f5589b997762dd43e17454f0a7f8f97821207a62df7b56a91f438611e774bb342c2f7b0e83e3e7452033091e48beb52fa33bedf6b9acc4d835a70f849bfd2898efc7f7bdaa67039818ca535a72b5c90f6035421c38e82da01d18cc990d6940c652f983960f161bf7e63c35eb0cdfcf2273cbbe219c37e6270aba11ba922761a53a1793edd416b990dc95e43d00b4b27c95735fff08a1377788b2cd059171605c460b43ea1b7e5172b61b645f8f0faf4b213ab629092ae847207db38ff09b731183064656a0e392ace1443ef872eac05aa63925a146ff0e7c554c192456d9e18e365bfbf8b5da4552ea8939deb837067962124469899b0eaf8391e7ae1487fe1b7dae9eab83c0e5229898e156ba7ddaa0c37eb48b8d953dfd811236c9d1e31784936c5468d3a0a61267814bff909cfefa3b2c671d4d2a09a11fb33c479057760b0520f247e1ffb5f6f33cf7b59fc0fb8c64d2148478642b840bcceba5982a6b9f81c58c00baabff79be29ba454e3b8bbfb23f1358ba4413014246e167247aa5aeaadf2efbc751b9dfcdb3bf6935aa1e0fe64a2507bb8004b2d7af01c6757f88c85751dbb50e7a729c6bc429dcd1c51d38666cfe5cd5de6036cb57d78b8ae2ea2571f4e39c2f9928aa497b24d9cd3ce2b67a3a9ae63713540ad9c8ac226c3e997c892973726cfbc70f3f15eca229b2b12f3f26e798cd6802a87a80c671e81aa013b68c980c8719c2765b6e91d629f59f294bae62f60b84e8838247a0e10843794676e1790ee08f264adf0ba480667fb2cca82175b4564089a856cf4aee5f961912a547f7d3cbe3742124814cbfb24926594353da5cefa6d574a2f59ee2635a0f274d458fbb4fa81cc28f4620819b92167498bd846ead8d4d73fa53f53dd9ea54038d783993f9dee83b7af66d768c60c5c96fd58352dadef09cbafc0e5553492b6485670210e715daf566da12e8ceb23777e7ca53179c0061b9297d3c53b294313e61e6f20601102949507b2599a9e449784744bf0fe65b2ace59b70943b08337cf3fc35b863ee7f442d4df3cadc3c67674931914dc11a9b315aa09af599c5747b88ef9740c4ad094878db7513f168d0021ebb8135436354ad35fdeca1740bc4996e35d32658f5adf4d341440afa34935d6797420cd1b2d72f31809619b84b0287a56cd1cfa3924deb53958efba71701b11d18809148fc58322e2b59c7bbffe89789406af6b68198d61c0476b679bb1d170492d0cdc50587e136d077d84ad61d64b5c92ec9146141ec3392398e36c5f74d765ec5c2dbc2a1976e4e8cbd07de502d8bb53044ae35a24084bf537a339ad6a279cca8506e6894aa8f81cba2033ecea40c921f4047880f530ad89ad1b134a5ad5a209f4891ed919d0fc89d0a97dc1622fbaf9914102841c9f149012e506511035014efaf8898701443604f033c05bb07d4177bb471f93d26ac5ec95e254584fa00a5bad4d5fe8c89410765b59cc3636148f5706d033a8582c85009da586ba4998de74efb912c2b14bbedaa1e123e9e00c0370709cc3ea03a2dbdd7df395f0110627137bf1855d767a36d33831d31ef3b9adabc507fd466fa09fe1703072bc6edf24c9b56193278b33c62af2af440a81b1345ec024d073c8369f098d5f1e92e38f78e9e7466441604d5c4251aee0a52ca0f4f90c7140ca4f251f2ec136c024c0c4201c7fb8fb18609594dd22d40d242e4dc5d91295ec76e62b438ce4338cc8599415f72a1515b021ab02f5a99532e2325ebae36a0f16b6b90a61d791d2a96c431b6abe37e670208c0de5bd0e60496075c4085e5dae124a66151fbca6f3a1a4eac1b0ea309d8a34ac05005bb975dd1e0e5c4bb3164f98852f7420aebf375dea49de089684bee3016f4fd3d64aacd9998a03139b3b0e0a68071ba523b5b8b8376bc1ab8cebdc9a798115b8bd9262783bf4212f2207b51cc55559221a68968b935e9f91cd095eaead3e8fa3a6ee8a8896f8ebcabec9d16db63e0bbdf360d376da189af5921cd3059a1f7d0e04ff42145b32dbbd8fd0466a9e0bc3a116cdc7e557f94b5c93a360865cd4728fe99fe4cac6baea82be1b86d4699aa1c5cca2bce6fe92670c976a1e2d1aa0ab6577c5528ea120eda83f9f979c1077c1943536f9a91f8c04f13a4ab83ae88e4ef2afe90d8f3682f0c05d399c09726ac02caeefb52e399ed9614d32d326c691e3e655063881eb3b85a98e0d07a832977a84637c96e70779e8194de99ca038c94a37639703c16333b602f8f380e2e722eb8b17b1c1b5c9370581fdfd7f58b8ab5158a6e740e382d41b63eae2ad69f5dee17f5c6597cc8171471e63479ef185da791cc11351507eedbec9ea9b88127982bcf033581f023a72011b385c45f223cd1fab1e86d2031dd3a434ce06ea835087cd1146b26b752970845c054d66327a918ebb0cbe30fc39d322c839686e831112b7e66fbf4bdb288edffb9fb55506de8770ac010ae429f2bf00660e17103ad95e1132bf028e4513243c401dc0d4bd866a4958ca1bcdf0b4d0b6a5b26748de746ae9079807b79492620eee34c1020d2400fc8cbf45e749a2060f89abe2b471de0ee4b078a7e6bf231ccec563b3caa8ecd51dc171f6a08ca3f94527bdb44dbbcb72f1762a2c1c68ba6d78a9bd7963ba0db397245bcd190ba050c981bc1761475ef8ab5e2bd7bc78d378fd66dec4d9636bb69036e8b45a0c898dfa4ff4487663a9fe900f64d1a89e304fcddb0879224f5d2f87e266a958fcf0f0cd69f5f788407f8b446e264514ff811f1e40508a583408b6eacec91230642c731ee5d14daff591f21d4dd6728d1576ebc9eebfd1f01823cc863e7157a82b74745a81132bb7730e48ee63697e49d68245d85748220ca71318eea26fbc7da1f48678008dcf7808a065d32e3c4d3261c0232d28a93c26a3f0ae7f3a2117e5205749951690b4e188a381d260fcc631f1c2fdbfcfbec90360fec824712f85059a56f8d8aa0cdd2a2924b38302df8b027d6eb9bde320b80c8dc513d5b569e9735a00450b16609e50019142ce0412f1ea0701014464f601f4423c15f1abedfdcc4bdcd00e413ab955ef47809c0bf9cbcd43360f1a8b3963138b770a9c481b419d1482a5a6db6cc64e378c00fe133d149505729b722b0517391d3fd67ae5dd0535612322b50c81e3110f618472c51638bf0ef907423b948643848f7313f43c180def9c24d0498ec8c60eeeb1d6e14eb27f5e2c56dc0091adcd9fc4970ca13f593216708b893b87c9455cdf599dd0d880c7d69ce39bd4dea88a5a21372ba3580ac27cdfa248887db923460427b46870c4dfc211cd2bbfbed83f7ac2e0062579c7f92b44481fec6397d16ee4371db1349d6adcd122c56fff37190be182c699d5ada6ff2e86543d4b82ef7c51121bc9c8f0998a28f9721b3e0316d47beb17507192baff49fbcb4a957cc04632756c3ac5384c9e8174377d2126579c27bc0b7411e23770802d75824a4d8ae234faf05bb03f89f636b181bbe4b2e421c8d03f0ce619f8df2b6cf19715da2ad120825b02ea5d0894f80fb2d6a84bef9f5adfdc267d7a7e35b2b713c88bf2300912f8c02488055ed55d28cc63b1fcfbabbb9d844f6234adfff4104bf6028f45729412cf779a2010684007d6fd09924bf1be1c3990bf821cbe8fa8dd24c39c92528f3384ae2c7ea6300ff284f55f9b9deaf75de5944b6beafd93901910808d55cee99b3f0931cbe08857abff5b67b6a4ea507fc6d14fbc7becbfbf7756600f747e57f8a3501d022ddc6f0e31a28227fad930cc95d2b19b48dd571523b9374d1e8ae21f46ffd25adf97e8eaf85ff10ff2abb5a30b8790d1c7ddf9f1e6fe68069bc8ef753cb6fd58fdaa62eec2051d2dabc9ab3caf68e220285217c9261aa4d0a03f5024844af5a23db4c1b264b07700c4389a4e1561539408ee754b0390e373e40dbe89a198c218afa650d607bed039fc94cd8c81cea2615d5dba1695e52c5305b70907bab877a9f1a06cc2a4bbd1ccd9671e9effd90438bff7c0a5a71dee15ed3443350f79ceb1b2daf3a133352ef21d20c5992eaefd7b1b2cf79f9f3ca2669f83a5b6ad0b2fc28f68278736fcfa7636a7413946ff83cd66e5cd9046a7e44a6d794bbf8d574b20b1c720ae69e93bd11dc64f7159625ef0d1eeef3e3d301e00813451eda8f678fe1f93b9b61689dbf4fdd400ff8d6354dbcb5332fe63cf35e6829be2b3084f6859916bdadff2a8b1fd527c94db95f61fc9c279d863f7c0c2bb10b969e4221f92aac4e15060a8cf1182deb4e0df12db9971a563f0a074ce8a82654329d8426ca370e8e5779153390e3dfe47ecda3a06b8c3010a5128cbff9afc2318bf38a45c708fb5340e2ec6e27cb845d4e80c4534722bbc5958567109485710d61d681c0363e2a817dfa71697a168f356b5806b36594a49612add15d03eb495af82eba91aaa54dc5e3965fcea79a727fc055b06b16e2a102d0199b0388661bcdc6a624d67216d59162548123301210726ca0c2ae5d1e49f4817bd2a8b6c641e446bbd5d2ae02e6984cef1924e44b1a8358e29668100ea1adc5ec9e00dda993273c810fbc63ff6f26bfe57e3b61fe8784a19315b5e4990bd2c5285c46f88c6ea0dbb9a6c236ca16f3ef5a5e447e019deb9d358b5aedf731692f4054a51d89c0c377c1ff9633fa1c23c87d4d85fe7d6add8c02d020c0fc3edc047d7561c25e7a82a1518b1ef8357bf060e88345fc482d3a75f2f9e3acf54b2334b12b52ea136fe9d0a7478dd7758be10aa9b1d2eb1f877956f11fcb603910e5d63660e40419a511fb99c994226351d38b1944935b0b77f03d6b7b6d410c3599fb1a4835301a546ca45e9260f94f6681f67f34d2979b23922978777d78692fe2486e9790876d7dfdb6a32d122dc802624f4817f2404062e60744f0c9b12af97cee77652cbc3de99914ae4fbb85986e29f8555e839b2c0375cbb246ee1f09d742cca1bd96c0c46626873f87f5fbe1afe35dced52d279c4b51e362f7652660bde0855b8921ed8fdd19bdad21c1b81001845d99ad29c9078ee9a2d73b3b8410d7f94c6cdd02cff475a4322a1fe12a22cbbc019dfbd416f34be17efbccf100c986f2cd13c60e8ea9c91c8468600c191e3ac590b3391eb329e9d57ac3a55ad17264c18504c2bd134989eaac933a939d56592fdfb79fdfb03c16f174fe5a5d3913f1a9f20878829b0b834154c929bd3a0c98494276579e207eb299e5168306d13d452ea1fd36f2208d9aedc38720c0ddad9c6a8b9144870d29b232c2eb29cbbef7403948bb14539ef476facbd826d3b16bcdcf76b47217de23833da874e9163091eba817a53e26adeefb7cc45eb040aa2a1f0bfc177bfed005ceae8d93eaf9639d365b8d2750aa4256b8408ef2043853d262852830c8468832e542d53b6e7724823b4d4f6a7699b52978483eec2c81a48c51599f4cbc4c70cb29b8673315da23c4b9d0809773084062c7f28a3bf73cea51c17129952bd0e249ebee5b7ba64cce94d107f1ba134c200538222861c88ec4ad9411bc3044801d63f56208bc1fa6ec048c37c01d2d10cad53d623f90d1a14f34715c0afbeb1d607d55377e6ec12d271ee2e7ba37b1d9c3e3600e360d8dc3f08e0c47ead496fc173f0022ccb72e52b90010458de161a5acf3eb554bae304347954a41077e8242d7c9774014642f6534a4079ff223148f8d43b5ac1da1a406fcb46ca840fd15edc149191081350d431292d4b83fc31e78c4e401efc0172276c6b64c4e47678b35264fd0bad540f4f9150b6585462881b2def3684633c255908e1eba658474e153f15849aae9ddc974fb4a7aacaaa66593b9c8f90a7da3cbef0ac442d0df0e9e82aab923c332f43ac4409181d6883c55299db80aa4db603562afa99fa1696f8d40a4be2171c7d87e5f8df7197a15bac6b0b9352be37eed5dfd1ee8a2490e58667ad207ec3e0339859570e829fccffb6b9ec7e3c972db5f194f0a025dc89605d763aaa97c106587e5565ac710a9088a3cb61ef3a0d7fdc68d6118e0a7e31556ae7fb81e6341b06bd22fe6bc1ecfa8b824990bbfecaa802b3a513655408dead489e9bc965770cb23731a5570a1434f2d46a225198a22898f48783e6ba1e11b99ee14dbd6cbe859f7265b8d8964f5f6f050c1bd09bdf0f0cd015015e0bc5b95f1801bb2dd63ef76c1fd632fe7aea94d139e27d954dc2e9971fbd371c318e5867d48ce6dbfe3c1becb71bd4c704b77a540ce30d8bb91cd617fc440e7435083c1e0411ca0fa77cec291b74f99a5ebb84108da04417e49801fa8615d827a364590ce40c874cfa06bbd850d4989c9316e32dcfb0949137629810546a065d7a4fa50f1c656ccd999d93fbe5fa3dcbb0c1872cb8be0e9a789ef97da90bc71a59ad0a0e51a0dc65aaa28488a154217cb2862bd136f2c0af3929356aa1648c40d478f4d5471bd85a39fd38de012c2d58c72d808ad2d2e19f8ff5d63d56ea6792048ff46adc76d214db7d876d4d9041a4b03e3920f9b4c04e44082f4d5547d3b98316fb9617be939f0cc51742cd3ed972a6dbceb637031d7606b599fe8ca6a0a9ba6e0a8ca77f8521ef7cfc6da88dfd40d5ac4aefc8bd7834730d187bf312a4d564ff10af5f6cfa4eb3152f36c570f88c9544419a9feddfafc965ada98b58d1edb12e2217d0c643b0566d855a6eae963705a6d34745ac6b4c3dd3710aa5aaab97de1c860d2524581c4f8109a588a12eb3de92736983f95a6094483ea8a12b185d1c62e5a6cafc9bc59a31f4a67fa4fed7fb675c1e0b5fd718385210da6d66626b7cd1225a4ddb6b61b06ce3a2ce8d66a86617e2c2510db2ec202e164cbc3c34a262c038a430698c4753819966af5345bae7a83aec709fc428d83563914166aed3b5bdb2a1782faa7a7a986fae3bb411a798c09b5de976ead0768ebcbf1c7452b456dc297c4b8d329d073f08bd5f15e1af19e23f0049a05c8ff04136ff1880473210f4aee4d1206f077cf48f795307a4afcededcc29354f601a545594882f84361651627b4ce64df9b9d2f2c1e46ec9778ab8b06720f4569636f8781dbed9811a5308c361d20e6405c6db6e48f8c58fffdaf6ceff313a72a313ae6b079795b5ba526f683943150d40431c120e1f8485c637b20ae0179f83b7ff0d00f71576c6c59223467cf86c8e10c46594e66d115ab6780e16e857fc3480a42bdd8c301aeadaf3285d301e5fa6bcfb201ce92f0a43771d43d56249d0f31c89601f107a0c83f188484f9525803102585d8b81c12911bffc20ce7c0565265df355ca475ff99a74d0da9486fe5753d71d0fe268e8a0ea13435dd0e121fd5e1b1e030b8f029af008358307b0ebb82a02d24ae18a0ef58a49789edf9f739e1d38a26ea1fcb216847ef4f100d09c80d31a1c10595891c957c5420e22ba53765f8894e5816e9a2f076456a4d0aafe007c48f7d50710e8139bf7cfb75ea1a761222447e7ba4a307b8f94d019a83491de99d480bf55a35b6f57776ef0a60cd7518a08f773c1c458e3a313310e75f8ca0d19f818f2b9491fdd615c7ecb65e7b7a59d76ddd42095750c9466ce0eb281c05913529b63f642cdfc26f7a0880b0866fdf2d92f5ebc88c85f57fe43832ecb3f106de9d8cea211c45a0bf404bb76fe015f7545567dd89adfd8f522638bb28781b9d7bcb10c7e5f586b85b9e43de27d57bf1567a9aa0bde83450ca1fbdf3cf4a820e1eb01ad379b3a8373930ae3201dc48ae77a8481af53316552c93abcc8009e3023678c49dd0b90e43168e360c173c24fe55f075f422104ae59c9eb4e869d2fbd48e471edd4300a3245f281a32ee6f1800163d69a74aacdd66d986f42f76e8102b2cf95e63fb9e6fb0a15946dd32b2de7053523016b856d5da8c4da9affd875119b2f587afb9fcd928ff7db722991f53942f45118d1d29e24cf49035171854e057d9aa4df14ef1b2632c95f7d118c728e073a1878b4fb3594534cb9756b0725aa977724b90fa05c4225cdc65e8c8d394784536241c95e47ae1d7e76db9a2dd22f41f6c8598ff733745bcd836fbaca327597eabc39e113e8e08604846fc95ac64a77060007252acc9e7366784f6ac97c5ac2693f2e2aa8b37cca8c8b05b362defcc4c17341b1a88c373ae00e912aa3713898fcb8690f1cd82087d9f8128a200fbf1ca9f97581d0170fe4cc716f877f7b6683bc381d9b8854c07f2a4961fc7ded15d34a77f771e543a90e191154d89f0ce22516c5504db48641e2370d4f103b92a84bde845f39fa1e5a8ec71c99096916a72a3db2a26f19690d16e1379bcd0ff2fd63a22e9d1f10df4b8407dfb1b36660066f6bde4abc8b37dd928caaba65900f5b4349d0ec42f4d076cba0516918e765a8f965631ac842cd5968cfeccbe0d95a4e7e41dde4d4430b94ecea01aa311970a2929eacba67a00ba2e879c09f95d051a1025d7b3cc04349ed874a0a79051501cae02d1e1bcb7e73e6e6bc661c8a52ed7c3863a8529a11d59282acdf4aacadf11bdd2e58ec51ca60f37ebf7b23b791cefe5ede6b8b7b4589e3858317fe93d91ca11bdd5038e5a48b8aabb9bf33e0a736d30e6799f83f67a7d124cb7ab86ad55c334aed17b56b54745ad34b302584bb1e6d9e1885b0ed433ec07eb31cc3540f844aab23c950665e7f16d7a238e47bb5b97e400ab6e9949d8060f6383fffcb1e80b2d68d7790ed2fa7e1579cfbce4e8741dc6371f29e277bbcb8c319e3274b7634d75abef9aee53e60f7661f9db67b3fb0c48010aaf9515fc68c8c75f8daa30f1eade3a8fe36a7827bc4264b4ad936bf437675c6bf13ca937f04929ef9f03123f6060f64f9c9b63255ae2cde91b02c167e00c29be001ddc2b0a9664caca5ca980da3a683565b19a4aef40a0cdcb9d1088f1b87b28beb94654fd17aad7477ed8a8f0374580bdb386348eb51612c6fc38658c78c92da5d319617fcf483ce0e33456738cf777b437dacd01dd4cb437d9fb0a14f8bfe61f96c37cb817b6f5262c425e333e726ac1df63d2ac379eac319a2cf2d85813c9b5ea9096024bc4854a823cc318eccfc7963ad51d78386e4c7f2f7700dc654adba7b478fb8c5fe991f4c359643b309923503a7285b06071b8199c3c540037fffbfb26c8f163f50e2f3b741ce70e81a9bf299677be8b5648779244235823fea3ee7c8047ce2396cc7409acba0cb89f3f7239354cf8a02258a372a15ee9a39a9b77d84dbefe7c4986becdc7056a9eba4cb6986e75482734820c10d7235d4127f92e922cc57189fb6134ba7965698a289333cd4f20c914c9befe7348aadcbad0395bc9f6aceaaf50100018ace8f69f3f5e5f69466557e803a9d531bb5d89adb0d95f606f3a8ec58519ad54a3039a790f74d2c0bb7c9a47dc69c643bcf7d1e1621ed8b37a9accd18c754107bed1bb6eb636fc08e12458124f352106d60422ca18599316fca082bfcb84ca210195802d2fa5975cda6f0d109efc473fde4ba8cb6b98b0b61fdc828b678b20b7a7afb70ebfc46d745c636c926882bf822666bfc8fae4b8f4d9566a919b76b7989d660f2bafd4f576aced07de194298dd5609ff203e7d23fe1b6852cd895814b6a87cec00abcfd120518f4f4b6250f6f90f8fa51408bbb552d3b9629e7940c98e856070f0c1751cab7aef17b6f710a153bd1a233a70a023870d4772f036f890fbe6bb0e1b5b3642ddcc607f57f910c59c06715a937cdb16440d3716c3e557ce1ef867c019eeb683f4eb2467d83346054b4879448fbcc91b7480b09fa6b775d50f67491cae2301371004492d674709e43c67f0802ab0c298210b03f246473f9175815e416f5db37600d5ff4ce2733955c167a47409358b1fada52165f9df473d22f56b005b2efe4a93cb67627d1b305ab10a6dfccd6b9cdfbbd6b4ae705f6df6f4a07ac82e7b2c58595dbe4adf06dff99c8bb3123eda931320b91d5c85d6cd25ffd5f65a88ed32675fbf2cbe89c56cb4393bf4a67211b665be6091c98a2f47800e30dd74f355e6f9c4137ddd1cd23bc0acb95f4da7577caea8a765ccb2478539608d0a955c1dd85e37491510604f00295244d0ede8abde553b7b46952c67833dc73b707692fab530c6c55f3f08e9d0758d7ea2f47d6fb2dee6eb3e3c7ae0c94fd8811b1460ddf7bdd990cecd529858cb6d313c051eac1fd32527ea1911781d78ebd57f413b0fc56148c1749142f331c42a3130911214ea5d8b50276ca3e4ac2d140b4dbf161149b40ded54b99a8661c849c2ea330e5b9a5696c3fe7684a5679cb935c36a5a20aa77545a1db3777a67482db5a9bba07c4f5e24becc24432944f22269a35d110df54121ac0ed28912efd9361149527180bc1bb3d0d5c783e974e6f14759d6de560adcae13954ab44a19c91e5110e2a823edbb8d51a19a7f844ec829168d978cb765d9ccb6c933178ba67601e4dc4453781f03bd1d765ec84674d3f15ef1191dc68072448d31156058d7da4471fc776ec90dab18afb204622c15ed9f13479a3721d15491daec7fe05e91218ff910d6e2a50c3682c01d83e1ebc7f1edd609191ec5fd4b25295a843ee393639711e8f3082179ae7735d5ba2bdaa4f6e5610163989846ef31a094c32e54ae670b6721131d5ce9304feb654fef1bc2d5ccfeccd15e56e8f3cd14264b72b85df9589de0fdda3adca541627bc69141affa990691dd5d37df0c834c212d34f0a0dd4cd5cbdcbbada8b319afa4b6a5d0e9215191a028de6466afaa2d3352b483662c27ee9f3b6a2474285a6ec05036a669be84d3ccc5b6e17dcdfd4e36d34687cf3459fc7d6008f6714ff53a4370ff488a2f39ab366823c559c9cd9e0f75b98087f7ff6b5e94c1850de7f0d89a3ce6ec96fc2ad95a75cba7f99b3c76b8c5588051d32d5bc9d4044d784cfc0248fa53858df5c6d414cbfc44f93d54878da618175aee924e8bb6c50c37ab0a6e2911e01f583f0dc499545b5da3e15ae462d0c883ceb5607ac85245f2b757364e25d8d9af6e5b20f174ed15c9c6878d9e969f0d3be6ad8ef1fc6440ad33b240ee35be38bfc21e6a0a34e9591e1eccc43b04a39a347e4f97812e3cc33a7ad9fc4b6333df14087669e45732816ab12e0073c371c69cfdca3f629db27b0e87b21bf0e2048250b3e02bb5e600b39b5146e708782e3247a98ec991f80b1900054cf8955a956620e8865401c0061c2673d0c2e858a0924e93a4cfbe9cf382c00f89cddf16ddcd4c0ba4bd1c0e90690d7cd38d887aa61a69451043dbdfe1584489afaf2f2ce2fee34cb41e416bc9077c7ec4f8b07307ef4c4a6084dc14e06381039c1746e1d6e53155b01703f1754c7b9ef2d63720d1ba638ebc6a67813254e719576f769d6cadf6a7825a0a104408bc9f7ea7982c9e94d2f7a5770d5a2755570f605b2d8850b7d5f3ce9eddda394718dfa1716d380c715773ed9a4059c8c71844e802dfa42d021f07ae38130759c32624c085b6aa3079d539e1f09cb85e32ca0972214306a4fa8fa7ee3f73a866d9edd31d026fac9c4cad5a6da4ce9ad154c63e8ebf8486819800aaef8f5062896db657352b28a2b82c15bfb2d04149ed6046b0f688003266db4e2d872b3504f8f821cf429b7e071c72fcb44c83047272c6271596041088b04f5f7c4adb1e0b229a6b4997931b48d379550d38d6a4ad250de45e2d4ee96ef0a0cb600230c859b0ebe0dc16754b448ad4af546d171ec01abcdd23514286b3387015e3428534ab1b807f3f02706bdcab12c3db3b80c83ef1319a48006539ad9eee8d9ff3e712316f9f7707b3cc10b56dd6124a6e76a1d0d232d71710ec399876fd83683bd683e8f47445c92d52ce8a33cd8a228e57009a2663888e94105ab548149693b8adf404cfa52a762850c1b63c78afcfd41d8817d59aba05947f2e50cdc5b51b5f97d9e03e77eb74fd9b33b4b53c30cf41798743f1a5480d4773c1160e918d2624a50a742fe6d1b356077c47afffb1a5d7b9147764816bae9e6901c038959eb904d8b0c32c39035691e129e8965d28e48770e19f957cd9679fa42addba57d1c7be4e84101c877116eb1d6e837ab2353e2f32e16f6fbf7a6586d7feba4fcc1e0e8ab02a709a203c099b632f60aaf3ee410142a7300dfe346a39337c64606a48300cb5e87dc938ed3b53add121a69aa5e346a22ab3cbe35aa1ebb24d801561ff4f6a958156c89dfa763ac779f00406e2659a5837d6ad805ce251a5369683ab40b101406e45125c9f6aaf329367623ef920b8d34002e7b5f96bc689529cf65300e3a65ca05a5261d6315d72684cf58595505792f7313a708ee38c5aaf5850e4680d1e26828125622e59726657db165c370c39ba2ace47736056dec0326b9070bf811e8d96c69baea48a4d87f463338d9a7a9343374804216e434010124d42204b557144dcaa611fbf15be68fbe28fb9b7c065eee2521b2c2aa5c8cb9e524b9457eada49c24cd36f549f4061c5241e4acea1a3e09f1aa4e4d97f6e740161dc3698487221216899f34a97e686abb7a8aa809d2b4177cf93ba92690ba1396361617e0784c0949315ad18e3bec9c5ebce9046889a7227d1aa6361e3b69ce0e06ac26e4abcf8375e5e774e0b60f28f31a50ffc6971aca6d53e8d47c3754b178c1eb0788fedcb642f53265e38f291ca3e82d098f676ce72c412ae3ce4eef423287163290b7d30464e6fbd9e8d4fa5ffb9153fbdc61f161511649ad56e4360ad2f258a285cf3cd2fb643d8e69376d1484373f872f6898e5d931f294754b454c74e55d6c0f8e6821b0525380a4fde4471dca0f01b3bac9500a4a6dbd9ab23a2c48266d423034f19b920e354473021252c363ec1f44873f843a6214b0036aa192875c3052b29a0eaae9682e3b428694162176a589566bb5380b2033e181f5ab864a7531cc6f45735624a910eb78a7cccc700c1611f5b88e618e415875a49f9d0360ef8c9d471717beb88b857be92c3e3611111112d2f6967b4b29a59449ca160934080a08b8e3a1b35269f85d2e3a75f62a4735a9ad54866013e3982a9f096176583f18ccaeb9e1201176a84517c3a65001c51a73ba092a16869f4c580dec460d9526180684c4b1c69c766266318681c7d68755afa67e08bb51bf1a2ab1196361b21eed9a04a7beaa5e1c2ab63eae3d43d8eb8e443d39ecd095a76013cef34e5badad50887a7aa220e2f1c266a1533b12f118e5e0b142e99d4e7724e2f929aaf22d14c95244871990c080441637602183a203221840e1821c6c240184961c4431e5065018d939e20a12a01e9a48e901d20d443120a9810f2cb072452808a1228cf84110a6b002071008c12305862331d810748226560004911810b1840cb8a8d4526ab728a1210536388125083b3d62dc4087a22c52700425ea0d58f8e8e0892931d0c10f13b2ed065b0421893205143d2841a5072168a82284113f5f24f153441158a468c1c1061726c0f09141083a6009a306113cc962891fa2c85cb042ca8f1f8038029220308192a58693201f78a0c40a2f6a28d2012a508adc80c89222b39f0b041d81e4861b5060842c92c871eeb889343058a4d131764f28fe797f6a299fab724e77f726534f59659caea7add55d506bf8e11a3fce38e3a71d9cf3ce39ef0c9e95c31a578f93914e269986946f6ae2342621293ad42621279ac3314ad933927cd0f14d42d4249427fd4c42719b846824991ad3a961a83f6dfc289d5bb4c0e53edde78cee24bba28d73bafb984d00770f11a0837e2e970deca98a60ccd1f989b493c48a23238cc8c288e80434bc70020b032d3630c2891f183901921744a596529b64c42729082d2ed08ad080892e908c11c5c80c31466a80c4e8871910662002068f93150451031fa218c28a164942206a4113488e541fa258118c38c1119e00a248173050e00462c8f0e4ca0d474821c396229c52a872854f922c4d8851439448b62c619a1d810220d8100430963cb9a24370cf0c331f26c3c15218da15b118230967408807258c00b4829f246218630bd44e13141421c2072d3bf080e8051b60b121071ef0c87e767c48e248500c536068a244ede9810b487a66d0021918b1a2f2c41d2059428b2d43fc64992d416154a5821f9608b500071e92c01586318250c580832176d002868c89185440082f7030c30d2f88a286178660a1e881912b726045910e2c8840962b5a8a30c2173fb8a0ba200691139622285baee850041617a0d0225aa094fc082f62288a9105083df041a202a9871adb216717848045125f0c7591c30d47495a72882206407021bf08e30743a19144f4440c0c2122665b7aa08a3ca84106556e10a28959102cd09bee26feb518af40b2e9eb53d3b40f743d5d934995514a6b34a5f49f524a698d8f6c01d10b3df81d895e68626f3b12bd2003257cc95196179e6062862470423d271d63cf398fc8c0980113404d70e1029c885d2e4d06ee3e3dca10b113d1898691a1c82379a4c41af9184ec5b03bb12b4f28e1c20a27a29072845331542085082ab4c0e0899e2240c043d81e8af011824881124a7890d2242a11050fb020e2420f482e205d23502d48582d9070420727ae10696209096c7164c90e8698c2083330722c251ac6117cd85b7cb0b0e383859ddbc456eab43371f97625d3142a262fe2943cc7e93c5b64f6846b5464e908b4564c1fcb04c81d9f84d1638c3132b9300afb60f5085ba9536e964cfaba0db0e643537c06d9b6d9918808155bfb74c785c68f3d26e9300efda8a9af7351f099fbd63b1cab8da34ca8aa7f336b36a537218e9386e2a4e08fc80d7050ca136a98d01fb5b0e0337286fdeb4cef33269ad5965fcaff40109fe9dea5ddcf9df7e2d1792f1e9c0e7630e270ffb0c765558e7f61a8daaa47793aeea97714ea7bf140bd7e1da82f3aa8fac21f27ecd417fa0b911bf5dbebd27b9c274fe5a0d463e8b066fbabe02ef99da05fbffc6a5008f2811d2bef048fc1ae685423a39435352ad78cf01af937537a53e87f8fa14c58c20e67acb67c55b49135fb4e1acdc69fb068af8b11bef94dbae74b0af4630c8b325ec991c3818e537aa6ecf09340e289884cd951ca8e444462b07790c747c69e89c9f4c5ec458f5f3f47af7efcb8d0f7b3ed746c9fdf76397ce79749c84022dae44f469491db278b88322dd0a18c4923d1a6f4920335b63bfe2b559366c5014d66c7c725ecf8dd8c1d9ffb6aec30e660c31f35db9fca586c28c686b412fd49836c9df83f38fc510e209403b8f150fac7ffc16d4aff8738c616ff710ad5cf0764322499c481a5805b48416b01bff6459cf7bce6ad7f84cd188739ccb5dd65f51a04cd465701fabe7d1fa45dafbd6fa594b2e5245621edd70ec7b665cebbef71d7e3be7ab83eaeb55ef9dbf679dbbc7b398ea33af66ffdcdc39f6bf682f8ce893600c20641eceeadecde769f69f7ded9bf5d8efdfb2d215c52feed78dcb71f646f6f734efe5e39328cbfb34b2fbb1ced7b69bfb3b59b1906d93fb87d734a8f43ab3e7a682ef9375419ae77b9ea17ea9d4e9672b0953aed4a339bf4ed4a267d3ddfda642b9d1e692047f07dc044445f598b2a92cc2f63a906ffa63b0f4fd93cad6e934763319c92179f18e2812c86e338d9b3456678c6488c918eecf8859a52e1554c23afa28ba3bff7ca7befbdf7de48eb0973626dcb558aaa505154a50a2b91eb4a26ce2b39310979a147d9f14f4fbcc98eef312dcee3312dfee33e494555a828aa5285152a7296442e913399443a9147bc8b2c48b10b2d4973283c61c73056d9f1ab8933e9933c9d4e5a9f4ea7130a954aa95417634ddbb6ac6d39735cd7954cd4649a2e4d5c572a994c5a9f4cfa7442a15229158a9baab99aacd66ab6a66bbab870f3e5858b5ef4421d67d0f0cf34b858c3e5fbf442eed5e990fbc54d4f46a743eee9cde874cce0a647a3d321b7ab06ad5163baac814baf9c3dbf90857d3f3c037f2c56abe572b9b896153dbf90eb70bc70d15f2ff9aa2e2f2f2ff4e565fa8b7c399d4e28542aa54aa53c954ac9542a954ac0124d5452f47c988a0749e9123dbf10bfea967b670fc1b9c8b3e455c937fc468d7f34b06fb2207638e2471df41d1dbcb1430ced8e0eeaf02d778cb0185fd2229c4e0ae3ffe0fc6a1c94afe3300eca10f6d9e77ccf89437b3a0c461453623b7521feccbd04489bbefba35ed0b5fea8b22c3464af5f87d55aeb9cd3febcd3de1d975082bad05dd160cd0e136027dc4cea529a7085a633eed29d892d4b6c07c119b43907bf1822bfe4c3602f3af84239c2642971e2fb3f510c03b48478e32f2e2e72ab38eeedc6435afb9b1737abcbe1de7eec56ef512b772ad5e5f0bdfaa283dbcac6157af5a5a73eb3fa5e42e85e3d10d35efdb6fd6af5f34fdd03e8af3e2172a71ed5e548bdfe1c004e461b1b3ad02195c9aee99aaee9e6fba97bdff4c468dffba115cad607d1068844da4672df4f750fa06f1fd5f130f29999853d42753ce85b9510b9555fa87ad75b2a7c26dcac159fd1d96c91cfe88e077ecef4c48f938d9fe34c5dcee6e25e68debf56480b5d0a59df7a1dd6afa28dcbdfa74aa2cd8b07c4b47deb592e9edc5de780ed57abdfb67ff1e25efd8dbb7871b3364f88dc2c299305c0459980b8e36f2fd3e5c8319cb49d8ed417ba3ef5fa6fa7c3a5d4d9b842653aac0f22f7ea5d9f8d2b3aa4b27d3ff418eb6b7d21ebb5bfab4f87f5abbfdfad9eeb72582b027032dae48857a60d231d7a6cdf0fe9cc676c687aedefd3253ea363fa8e07ce317d8e659cb5841db46f1fdb0fc7b0846d676c1baeb635eab868c3bdfdc21f305048dba72f17e3c77f33555edc292f6e9477f242acbd20d212d9f74bf8c6e75f6a89c498fb37ecfb214ec1bedf5dabe43ee7599f2873bf250e563aab5f1df41a71b090cbfe7dd98f07b86f6cf937b6bef762fcdd10efb72df93648a418a5348a094874090d4da26f4ca31d7a94ed8fb49d278afca2e989963b4a299ab243179211bbcc65a0dff8b4522d061cac9a763a39911853da1663978b0a913843bf9e16a05f1e691e73e4311ee3435ee74454c167e4a61f2d067c26d4c1810f031470b0627c33b7af2037f759a1eed07d82d4eced70b07ec95301034e232b3d921fbbfbeb88eed910433f97bd0d7ff67636fd280a3857aec19452f72f626e15a429a594fa94bbda41e7971c9d60fd79a4627d84829bca582591287fba184a23bb7638b4d055dae15e0f07ebbb00a11d85af878343a6eff3239fe6b9cc93e8f8ed3307eb910e872771b0beacb5e9c72ea7b5dd48fc59816ece0bb5fb34a13bfa0dad88746b47221a88d8398cf9c6ce1926e7e79cc673fe7cb9182fd68f9fdd67d238951953613010bc5ff85f574ce8c88111d030db3b7ad81043d7a7a9af0b407d0873fa50c69ee2607d70d2a430ea5da7af82430e7653747df9a1d31973f97cb17b8443bf179e3077022603045ddfa3ab91c714ff08a3d6faee614f3382e6a1b51ef90e96fab055851e2e17d27421a90238c054ac312cca0419c107a2951d672c084c3ebe57ea61c81a7aba093885b115d4459a9ae48e38cb06f2a64354cdd78aa03d94b34e9f1fbe0ca245681c0267421f7503139fc7a13fa58cf12839fa17c28a38e033613cfad1c347ac82495551e1b4bade415cb16b180e39c26c3f3ae4b81264cce498018c2863e328e32f39f8ef9f40d08f59ecf97a7a3784040d2f54624c2cca31839504e498c1280e206e302491234c11a820407321688256c9d8ee2e97f897b7d5a04319d8ff7964f9302f2ad06068d803727497b645fbfff8746ac39cf1632a6b0fd3be17fe88a3bd148169580b2a9d73ea806027b6e327646e9df92ec336993e7aa553b4299d3a54b4e9505c2ada70a96813b36a156db695c68a361aab65eddf6813a38c67a34db4b12ef525dad49728e3f4156de294e133a28dcf8832fe34a24d7c7f196d6844991af184fb1e65fcdd15e511f68ced304ab963ec74786ccf2076cf38b14f579422cf899d827856ea8558eee8e0f482cc7d4770bb1cdac728136a1ef682cc7c3909e4eefb8fabfc6294911f411bdc695de2de0eef260ec5a138148750f013b60f0b3c3f3aec570ab605fcf67bd1c761a2e11d37274cd78c9934e8300e590b031d9ca0a452d26f68a6c52a8889bbdca1521a9971d2e09f0820c08e5b18938ee6cf1889c38618ffd91d63e66b52ca89f19cd5290bb2932718c87e440ca41070b002b303239e0cc15ce150a2bc80688699cb000977e146f61e9c873a631723f049d24410a8ec245bfafc3401842ab647a47a64875cb6dc3c61077738e88f8283fed86502cc8ee98ae0ed78d4bf737e9df5efdc1163fd21a9e8f93caaa44f75e87ffdb48372460f770b0ac3575fd0a70a3d288d451189a77aa1553b12c9c0c5121a65e54ad128a21b8536195185b46652850c3948a1bb1d8964f091217663c0019dd118a42002450c4dc8008550a562da6a6da582288622982882143ce4509fb80d6bee4e832a4f38b1b3536aa48246bd7e74624a54a27e3ba2949de7075344474abddf43e40e75ea8339b536a736a786b1cb45298d484877908a9538a7c4d8d648a5d60a524aab9cd3e239e72be7ced290943e69b5171760efdf326ce7e8d28f9febb42d0f206f3e58c35d891b8271c9f8f82af4d83169b6974202d035b248ca3c1f170adfc3c71c123e5236637cb4beb600cf01a498147b97918ff4299a3ed3c7c7c7c7c7c7a44d781056a30b702ac04d41c1944e4dc4bd51a3d3a5c471a04d8291c465cfaf5510c5404387635aa00822560491c50d80a2200205b12d72f882083440c1d1810a8d6a0f63636c829617c4889203212250a61157847e20f3811638a88233a59e15057bbed0c39eda9b98341bceb6e583b2e7687b0e72183f946fedbd2f1f3286835feeb046f7a676570c718c47e8cca34d664cf47fe5d46c7f1d21736fc0738c268432467da82c4988cbb0c321326cff4226248833260c3237086610bbfd8fb6378dfc68c7f8d20ba7951d7fc8dca94ea750c519638c739a68ad54bac7a2cadddd2d0ab2d76969a5b3565ae74b9f95d63a71f867cddd5a5be7cb9526a5a53f632e581f4f6d85203a886bd3f992d5da32ad955b8518af74567b7ebd126bc92729afcb77f71f35769d31699ea1dd94524b7f06716d5b82ed2fedf6fb7994a1e16cc1876c7f39e76965728f1541bf9c6cc501b8f68b06b6bfe46ac7636eff58ad0e6cbb8a32f45961b8da5467fbca6425eec9738bb3fefc6d7ad402d123805700cf0051a662d847020c06f3191260b6fc37600ee698e2e10e028eb37da624562a180c46b5529560d126a69648cd949729a594d2d2ffe8b167a8fd8f1e2a956af53dacef697dcf64cd2f9dbcb0472a552a71dafba71e6f2a3b4fa530a77a80f6a92fe43eed63a2fad80d81e108512a614e552af540f917f698a999eae12a95cae5552e2eaf52b9ac5ee5d231e6f4abef393debf4adef893186f517e5853d7a6c13ccd5f2589ecbebbcb0874aa552a95437ccb6c69ccae4a92756abc779e22f7da6ee5e8efbaacf25d56b547b201cfd84b8bca6711af7f4b5cc7995edeeeeaa8fb1db11555ffa1b4765495738ead87fe5e0cdfd8fbb85ccad7aeed33c1b5bb4f672cb0efdb9ef72ee962d5b8eb057bffa42ed63c2fad80d89453842a8fe9543b73fde74abbc506e0fdcedbfb3bdcb296dff540f70f7efc5437b7f6d04b158cc858db3e94ba57737b9fe2cb9d3873b421b9bb66cfd996e6d6e3addb7ffeae35ee5c528c37da1fc42ed4b3b425a7e20f7c3116cee13e2b2b9a714c87d10e8686f7a1dedfdb37bb14321f40fc8fd903eeb7bd00fc87d4f637d3126d43e6b1f10ee634284f53d437a76bed56ab55c3326460284318a8d7ad6c7ae87eb6f9ce5f50c9149428b1f9410020d78b082d3fa1d263878018c209000e1420d70582c16cbf5a11009807ad6a7674c7d1691ae9f7a3d6988b4707a5820d2c2e919c26ab55aad56abd5727dabd56ab55aad8fa106a7f5f598c009247480820508404238ae2f461d6350df33a4470447529c0c0188c88722e0b47e08ebebf97a5a5f8c3b4b78a4e0e1a18a162c8a70aa0e484c51039623724024f5838509232452b6988253ff66fe46e9984e61fe1f3b42db14dba6cf3fff8d873b423be3cf21feecdc90adbdf3c431b6e0b01e0689e373717a767058f40bdd814cfc9af3109283f7fd1f776b9f90b9f117ca13c8767d2c7bb2eb53ea5fe86f9f841764f9d483817e8d917edcf4a5a494bac72f72432028c31b5bd2a21823f1f5a1940a79cd955b6127b62df5c2fab43ede81925313a3094d664cb489c36a3f9df9698fb76bd2bc3f8ef6943ed53e7a64c583fc150f3ac4f2b5c7b6fdd8961d52e97fc40e29a54fe4116b6d92ab524636a5945a4a29b5d65a6bed5f2bd5ea39bf90fefdb8dff59bed903e0e6bb6a4f6665bfbf45fae7dfa3a54e30febdfc72e5574eed38d3d9dfb09997b03f64db05d3eb3c20a425cdbdaafd996c33129a59f6a0c2dbfbe0c2eb4162dfb29e2c9307424b2418ba740c3f80372b49ecac1a90a42df384b4eb3fdc4326ee6e7e0fc1b3730186ca4fd259ddcb6ebf69bc7237184c04f3b1cf565a731d928fd96126d6f340f3a8c43370dd851bf15eafeb10d6dfb810fe354485a5a37158a32928b701bdaf15b4a34554955ac34d762fc591ee8d0b59df318f92ee58c529cccb0a757777639a3d3524e94f128250822ece9695b7638525e48a74e8f64df89edf93b49ecfa2bdcecf93b5126be6adc3af363f47c67f7e2a4f134b63f0d06d05863e798e3c718eeeceac92ddbbd70880bdb63b5c3417fcb7ccd38b4e347b7ec4cdd71cf8acafccbc35a21542bc69a96ddb55b6bc57fe355abb5625c7ddf6f3253a26ddaad72b6c467c2fa76166d94441b2df426404cb573007d4c5fc30dd8e160d5ba3c6516d27814a0e5fb88d9314e8cb576b9feb57dadb2ad2b3ad4dbeaccfff1f4c3d2a5898ebb7e2a07ad3fcd3cb4bf9f4339b9eb8541e4d6a99f6b07edcf20d2e5a07deda0854094b173ce5a3f1cd23565d4f09733bffefcdff1e1a095b1bf22f2e1a055a1060603c17f97ab07137907a42a7d768c6247a2224e76112d5beffab7fe357daddf39b91dfd6afadae256f7e9adf5627cb78b73ce9fea72d07dbf78e91782cf4dfce1b81fb681850ee5c6a2b1d03a41e4d679b9b4582b552a15a983f8a5eb6aada79757e8faf66d60d1f627b7c38e8769d79d67442cefbdf9c59900432383bbf7de7b8f501d0e377223cfc2678ebcfbf9a7ce01f7ebeb2ee77e7d00ad79ddb7cf39783f1ee0be7fbf1b4e85be1d0ff9b5d4e5745f6f77f5ec7eb5bb4e46f7b4fb6ad20fc4b4a9dca5ce01f776de7d7d4d9d10b93b9deebbd7cfe52ea7c315d73aab8d2b74e4b4d61c1765bab7a1850ee350037c263c3dea754e4f7ffecd8e362a15ea4ff553f529fd7af2eaab6aea543d21729f3eeae9d0af8ff2c28e87e9eb6f5d0efdea25aa2169c71fec4854c3cfce247051a6e3680e65c7a39463dab405df610bbe5f39738743362e759cc9544b35735e3a7d610b9b7e986af8ce5238646307ef675be99ca53877e7612f8bee4b5e8877f77d5d61f3e4d1fdc21d5b1d2567cc20cbf730d6dae57a70cf1f717eac3466524a292c7a8cd8a30c9def4314092a6f6cfa31cef93948a38df1e3ea09d980a19a4dbf1046291da294092d635976ae949e758d07a123519124fbfe6f2a895025cad6ea735d4eccae7170e65c6d893771707e6e3242872ee442d106e3d0d5e9f87d7fe36ebe17e3a771468b3334ced0fc1886c37638b4cfd903f201ed35ede96b40e4ae2ffba1aedf091e7dfaa998707fb9cff7e7fb136dfbf979bbdaa65d97b9118fb1a1850e5dc8be26146d868836f9a7fdfa99bef6d67300fd5a9fd206783b7a5cea0991db7e2838883fe771309ba634392d853bfb863e35c1c1f9186bed72adb6d0e16b7bfcf7b57d3cc08d41ef466e0585f6974a728409edbd3976b21963dfe293fdce7e42288e6b59fb9c7deef34a26c063443f4e36fdd5675794443042dffe809f8bb2b9df80e07e5bfdb65a3dabcac62f9ff8b1a18b1d69b023910d4776f63869e30a6ddaa5ff38196d22f8795ee8fa1a3468bc5a1fc2b4bef42e2faf97cb8b27f7ebf5d26ab55aad9fefd2ca315cf4f3f5ea535ae58e2ffa395e14d3d9e5f097de95127b2f0cc6177e103fb8e98e2ffc810ec6b75f08d26fd503ad232710791a459bd597563bf68836a9972f67d106156d4e2fdf87a28d7e59ad0f7bfe955fecf9d8a7ecf91a0e7bfe36444e79aa4779a9cf270ff5597ba7cf264f7f5e953cbaf24ad55eac6d992b791488dc22e37e187150f25e3ebc681fca2f4a9ecbf6a14f2979aefce1c481e3529eea4b5e8bfb7062d9258fb5bb8c578ff7eabde8f58832f2519e9c4519f9274f22f95094916ff266c99b465146ca48832d956c39049712ce1bf6bc6255634c645aedc51ad6f09db269448568b5175f3c43f2a12933b2d7bacf907c68ca68b5b56886e443b4d219924a8996efeeee2a25eeb148a54428ca30090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a9a2e573c4040404040404040404040404040404040404040404040404040404040404040404040402b223e69b5176b5be6ba92499f5029d58ad572b9bcbc64cc98bd662f3397996bd69ab166abd90c1a3566acd96aa69aa566a8d96956e367a7999e9966a559377b6fd6cdb8599e6d33ef9b6d336d86671f09333cbb33126066770653c26a464ce545963e69b5176b5be6ba92499f5029d58ad572b9bcbc6818cd623364bc5e5c5c35de933165b35a64e46ab156aaea7dafd994a550276dfa487891328be443a652c79100e32243e22e4c092ef9f15f3ed9f6f74d56828dd636da9a90d16a6b422ad08400c0a9540080151159dc4dc3f2e28ba58fecc8bdf7de7befbd17df8b2a9258beeb44a59e8628bd17df7bebb5f7627cf1c51ecaefa38cc8db3d96f7defbd2435999a6dded6e72bbbfdd7cb187a2e2e0fdd3d0d0fd1d9c7bf1758cefbdf7de7befbd8eefdf7bef957705baddd7eebdf7de7befbdf7de7befbdf7de7bef939695ab5d7ce5bdf75efcb9b67bf2de8b3fdf5bffde1973ff7ef6b0f79ad4347cefbdf7de7befbd58caeead4d56fb3ebef765f80cf7f77f52ed63781c21b6e7ec76af9417dffb19df0ddf7f6d9b76efbdf7de7befbdf7370ddfbf57d2406a6d413fcbe8e08fecc7488c67c6c57cb6ec89b17a3949cc47ecdb6c649778ecdb2c7350e69f2db338aa59ae1763cc36e70d36c5caf2664315ad40b80b5c53fb42abf2197dedcbc7f4c1029e3114068d3fac3966ecfbf8ab46f7c36a2480593daa59eaa69148c90fad499b5a9e4dadcfa6daadf119fa37f2ab7fb1e90f3be4e2944d574822cb3eaf14fce7cfaf853c347138ef95c2cc54660ccec6830eab516e7224b4e5e72725a8408735fb04919b0b655b7e921dc22a2ccad0df8674f75c11f7bd5268c164b232695e3e3816baef9542f7faf5d702273b15b8144c5f2efaf2504765672b5be612cf1038a3d6e39fa51237e2e03c12e78c651e07372db55a7b2fc69a36e396e0d04db659ce1cd7e59f2ccb3e5166963a1c39b669e171d08883f348944599f9e3a096753ae8d63e9a6339d1dbeb0e47ce48591c9c3ef9c7c169ad129d7d1c9c5916cb28548723c7509d16727082bb56224a9e76a285b892a78588d04e1c64b124abce57ad565a1ccc3107672b02a0b4254599f9decb8729f5a6dfc1d1a78f85d36b1fa83fa13e763e4e9ffa583825b1623366aeb6241dae505ef67129bda222ceaac86776ecaf8a1c9c9a96a39f6ac75a520350b4d2e26344cb119fe1b2ecf9ac19ab4987ebea68d585cfb80bf62c9a34ab27aba2d593d51370a79cd0e1eac99ebf2a3a7ae233e1aae8855e564798fb2d29dad49f1246bf0fdcdc96b425e55511b84df8b10dbda5bf429a343664ab241e966c3e2b090f51f6dc92f6fc90b5644f96cf9e443c54b14396913d5947f60c57487bb29aecf9b6d22de96514f3993087b968cb7c26cc465bfefc4d8bcfd4267b16614bfad9331bd9738873160da48d7fdb98986c4859427e310afaa95476a432712fac111f766893a06693a662d48c7361d7af76a04daf3f72b31dda188e4ed3b80db3e3bf0bffe713959d978ffaf73b0dcd186cbdd3d18cc1d73b19a17834edd33cd40f4ae6207e941107b9ee35af84f2f199888a3988bff3503c0ee2770fe5e3207e2bdb3654916b675d349b31f8b10c358acad093a1139549f3f2c1026ae83b519931f87770306ae6203e75291e9f09eb91e6a91c7c755f0edf9d8c2a8eb224f9cc4b50948d3f15f319b9f1a77cbaa9a166b50b9fa92b5855a143d40c3f7685f8664a5451688d8427a3f08484e3917e1c95e464848a4d1a8c83ff343469224e929123948f955d4465e36c05dc4d367e1b3be233a7d9a489384d849eb4ace03ff9f84c6c196ddc3adab88524dbf89464632eb6f16f5a8d790191bb0e45192c250215a71ec9b4f6dcd7a38dbf0a0d451beb5d4fbbbcdf7cdb349758928d634352a08a84889260214a42c4cb9c9be6a08fcf842e99cf84d8f2f88c7bb1f1126c8de0d793c64ab4616dfc15778fff8be86bd3f308b1751ed53cebe3d91f0771f628f603786cc6e024ade14715e1cf9a57851cc49f4354d1c6ffca797b54d1a4a14856b24021f94cddf8515a7c262e61e5c8c6618a676354151b6354968d51491ba7621ba77c36febc69f868637ffc11067f3a34905a59b29c33461b63a434ce1923a5b386525a77a043bc5da02777f4549c8c8fb8624d732d853b39a6cb9d86c6181953db1fc72a6b691ae5892f2a36e79c1d163af19cb3ea39e79c4e815232463fcd395d9ace49e934d592ed2e9d73d298b78d4e0dfb0ce66e674b1c2a868a55d78e26aae79cae39e79426284c5268f9d5ea49f18c9933aa5c46a44f5aedc5da96b9ae6452edd40651a9d309eb94de9903b107d33018acd4a956a615ab04f3cf6a750990e16ab938970bcd76583440a4c18fbf9b19d8ae41bbbcbcb4970c2c63c65da966d0b0aa140d550d0c9b3155f360a81af4df9bd1e6b55d01a205a894ed775439cb10d5000000002315002020100c084422a150280c6441563e14000b79924478561f4b83498ee32808820c01c0180408210618c00809cd1015024d20986ec6730df53dcc7ceb8301fedcb8f6e2c22d1f688c9f98d85af88b36b4094de2fd6d30f8d12931a5ad4172038285ce6b9e89e0a7275b32d3444c1fce82a2a5a29422110750b264d5f0e1db7557f635a8247285e968e83cbeb619a08bd6c488a03185ce218848d6c3ecee5260100650b0c597fb5e2b1ef1e769f1076215aeeeea45c462453524ef0baeffc017e2f70499490a9941c599f1779e04db08e767efc74c0c1a22d4db00adb53e362a26acc955c486273b8b4c993ccbc04f399dfc47146a08f32e069efeefdde6c1d91c11a9ed008c7ae57b3a44383cb9794a5a91b19084842029ca1ea6f801add44edaa654540fea2bb02070d740111a74cb6f20923ef7a68c1d24cc44c94a1212423ecdc6c1abf35f453a37b77fcc8b3dcd02f03ee2c8209d29b34501582d91efd4dd1a7bcc27ffa8e9080db3720529e8dd873c7cd14347e2cb8d2c4b23d3d92d0760e1b034511a1e3ea06bb5d9ec154097e9e1ae24ce268bd1cdef046f422c30c417b2cae288e8c47eb7092b4c4840b76116b0908d41421ddb01d1b6317877e4923223da94620516853140fa67402177959024fd292f7f9b44778994cb30ec87d7eac0d3da93b83c98b3e3d2e796adf5bc23a556378c2c5bd2809b00dcd8f34e69d4e8b8f9441b31e72d4471e0a38b668c396ea65d45c50f7ce3e67ff2e99bcc7b5fef3db2b65fdd5ef7e248a67bd7e57d323fe856efdb64bbef3a06b7c34d76fb5a9fadecfe35796772f56e32c392d5454e245179f56341ede941eba7077963c52e5faf32df4fcdde93d9ded6f7dc1667d9d95fad7793f948af5adf9799fdd563b80d6765db6fbd7c357b57ef1d93ab6fc914965e3778a2a3ee04bbaa5c90057a379ef86594b7659fc0c7a401da5d7a24c45369f1cff12839569bb02c06f9f6f82abcd54d608ac0fadceeda44cd55253d37675f3b7773eecdd96e67ba3c73f32e61ed73d0edf69ccbb36fcebb39efeab98b67815b227ad7cebc9df57e8eabb3eeae15ae56b763007439ebe6b9ab6785cb91bb58005e9d7bbfa871094abb78e6f25ae17265429b9a0d274ab591dee189d7f24489f03bc4b945a9223dfadf732825140fa14f6807c7d3a4e57c518f9eb7ca58db046511d4dfc555e32d37825904eee5f09e38f594fa30a8be02f0d3d5ba2ca6a8aa9fba81fdf6cc8ef3aaa7c2872c6e24a32cf1ea7f974c6cef715cb7aea36ba5db290f69065b90f44da00c5fbcbeab2ae28433c7e14e76df9753ae6b0eb1becbb6c516d1113947d0b894cf29bc951b917438fd4370ae32c1d857b6ebad34ca1316ed0db635a3e9271b69f98e921d715f9fa4f7c88998c4377fea147b8d71057caf38d84b8ba5c01d0d1e564cc46e33553fed7bf0bd359aff1673b02944fe034077f783804203e5c6b85409935da5dd9f1815ade42dd0d707ca3d1b495d1ce208e2c720a7ba728588c80afca3f39337cd993b8e80658ba414d2157d92c06274618eaf3ab9536ae1111a75ee4ecdbcc36bc51f0d670971b102be781f8ce0202508a022dd7f01a1d30a3cd9cd20083c893fe7efb1f2eac2a7a3f51b97ed9aff4127eddd5ebe011af89c06f0105896b33aed15127171a4e08b24b27ebf6442eae8803904faf147f386ebc543b6af71eb0b4ffdc6d29ac63e9436acd46f14f04382f912b54f22480e8a4bee259196202b5dd6c843d26f76c60f8a93e56be0c5b5a7ec6482d35d9c81c52fd6efaed6efc7ef0e18f27e1b91084f6359be056f8b4fbb3df658bfc98e488a69ad1488df1801fed4effa80d6a42eb92ca731e69b777e3e9d7e400ea955ebf78ee41351cd475da098dca40601ac7d76279d4066fd16546214aa1e8cf785e783495a18b4c98572c03820c53dc7b8ccae2ee928c49cb62fc9e86ffe34c6261690a4a8e45f120a8b7e5377a415628da6e5b7a2c737783d57cf8a75df104ae150fdd871b294f8e977757efde6515707ad353f9201119aa1a8ba45a3857e3b2d7b0fa864e0cc9c82b0cf63cb948a3fa4b3a6c3c3db3e3b7c2b4999eae79462d6197e08a67e23869617fcf87e6c13c5f97e934eeb1351df732e2007c80e0e3261e73357fffdcd2dbae8844e6137030ab97ed7bfe7fc252f1900b07e7b443f23c06d2a11c0291c128dd8d860bd0f3a8255f8312e22ce08ce6d7d7107f29d34f7505e5dbcb13c82fb2c7b123fb31927ccf8a8798cb08cbe982093097a02423bab63bffa37b1365fb98ed82e721f0a8f88e065712f34b722f1da2c58592cdfa93708c1e8521c02223716a8f8010125ef97d41068c194f351bcb130a21811b47a3f9111a981a1deeda9d713b75b73189083559f026d48ab5d0762fe22c624008183b35fb9e0492954d2db13e5d336adf221c3fd42b2f88645ef5d8a2d3f4cfb3bbf33906b9f257a27929e6187bb91746c622c8e2a2a5a60131083d1472f2b6793934d0b79c77f66cf04c9f8077371d13dedf4d29fe9e4ee5d87085fb662573a595f87614533fc2c9d1e4480fcd8111f036395532cade9340b86d23496c1aa839a9696720d532fce091c646eb816f0e500246c001e0f521a6ded015b4bb6ff2ea3c944e225461a9a7ef4e9f877218f296f6234e7b231ee9d4b3bf1d47d3b53f538bbe55e95e1b2683e805b37d40be58593f764d10d83fbad6f486d501837dc81e580796c35dfc37333e4415f043c444f107d69ba1d384ce3641f25c1d23c5af169a4f958e2dd87dd8e86d87d2f2438b31c63a2c17efdc2250acc0d5c135642b4568c366b00d10cde3c7f684a2cae739b95a43dfe53f53eab09a80b28b2315e0e97d5b083048a6334de954ac948eaf8044a57639f0bdcecc287a638cb041d38a39a81f2d79be49da6246ab5df9259ce45dfbe5202f7833bd27df9fbb75afb775fd0fec92f9c0f277a6625f96aeb81df45348108d29e5792ab2ad179906dbe97c274e9f5a904b1b7bca16557b06cde4d60577f41f107e5422bcb3eb845b1ecb064a3bf5504b758e7e3c004e21d1d986173cc7f54b36ad1cb453f7cdf76a58f60b3c3e07328a2f386bc11022a0787c3b57dcd3bb2f574bc81a993340ec7cae7f1a4c4e93031c1ea35fd4ef8a9180f8b5aa54a059e481e405c078db09c4155b3608568d58edf02173339f93cefc1244d51c250d8050706ca5831f19501fed41394fa824b3af6179e321373b29325295295969f50095967b0af39ced70119448e8289c21a87cd62e46dab5070294a32bae07eb0b2c70d09e8522b77d77c87135c2516c6a4a953a176fad79cca238f585178e13950abe86e3d27413a878b2e13cca354634cba90e139bc18a3cffb1c26391683b004695058e8ae71b0748bafe18d1dcfb64f1faea3412cb212dbee02b12d09a334c24a8726a0ffab7d6416f9e27d5c4d234c92caa3bafa40089ad91b621880f3b676e1fb9bbf77978e932d467753174024ce90795b0565e7560638b10f6e1b0fdb6443d09d1276d3819d84bfd3cb440143dcc9ef347583cbe428c58890908241646930806009aeac0971d6df98fd1e07ec09748314ea22808e2a659076c5948eab87aba78a509a4b0b4fe260149f0085bf956000e12483b6500afef9680535195a564f9c2d4ea12ae034aae4577ab0efc65dfa5504d0b83854c96138d2aaa3591709805636dc06cb90c425f2596863e4ba41fe7b2c5452fd6bcd8454a3d3798fbe1fdc0f5d3304fcf83bc604a7697abacdd10d12b41f631212e0449f5f97859fa16798a900a8a9a9c1a26b58e451d0f434dd5625d6c62aae08adad09eba25282f84092e45de8bbc2e06156eae4758048fc03a1b086325be881d4124c4839bc6461d1120df999da8023534f2692f81c510d28d0f8649d50aa18b8dc519901dfdbe361d00988a1f5f8fe9260d8566e892eb0421b68c199cca150905812f101dac7156d7d0a285ae10b0a06b47760b484be317f90035ab80103490046a49021222aead5e539ee2cc6d39d9deb102a9161e6de106ef441cd5f85e79bb44fdc98dfc2c5228c2a3b916ae47e521dc11b02510c8e9515ef928e2553e080be8c4f3314ba31b44411b4698ee6a88ddd37183178b8d7f8b1618004445c50d67c12bc71cf1ea45eee07193e08938db43d41ec8756e3316f4079d77b15f8a8eaf9f67e1656801c1714aa2c59716c4a8e540e60bb27024323d587c001fe4bf4a5a4a20533991748817aaadc4e9c2d4a58b16dcbf05b13555368810099849094a36ae977623407119f74cab499551dfa4019ef67645d3b5079c210cf5dba14dccc1b7c5cae41402d9cd9f785cdbd140b63109a28b3fbde752a57d7b73e84fbecaed08639e429985bb8e5346b5506bd6b2109e153be0a45059f8f9947fb2e0e336890eaf5884c6c89fec23e450355e457f9f65af1b3100d9f98815c2f88945c73b09ddac436b044f552cf07e56b5e7ecdd3fab2c3521cb33b0ac7ddf28238131c93b50df84f88fa7a277793f033bd9da2ec261e7210c94fe6a196ac59f16805031a7690fc06701e8eeae9eb1c3f8ab4703e15b215369b20006d3d843239b9d7811f469e58f85d6de584c5f06b6558132240959df88d0a77ba84a44590da093747e4837b29feccb9b104b3ddb6446498cf2ec8101e488297cd35f763edf14e133f1b6f9fdae07815c4b48a3251825dade46fe1b2d46843c87e87f6d3b50942ff5fd564aabb4e389706ead0bf97045347ad04df090445fed493147d83a29473721e985cdf8b8c098ae7c4fc2dfe2c319abaa48733594b74bb9fa20c671979b32ceb71e2bf802c5a0361e2f5b0672ab392035b072ac4a8d82eb73d783b9f3556a1c39f3b6348849ef20ae1c1780c1ccc879d7d3b5b48fb74e9b10514f06a6161950c337fcf82accc50b2dad750f7ec8c9fb484cfcbc4f8a81edb00cdb9d61bfe15f4a0500e62dd1df2e9988cd3620fbf86c440b06a601e589e6c01f3bd1cf43db0e628e658258b6fc4ee361f545446a75ced175385bdead4863954b68549cf585ccbea7e9b0f4cf627ca94b01a30f599426eddd490da013b1aa624be82d11826fbc029764b3bc752d95a3e6122ca6928ad618c6085954c9df4e394f4412f47618b1922a81990549f4215c1aa11abcd77bd36825ae065f9b366dd709f248597213004c168340c58f78e31c3435e84d4db2e0325d0f2a757c04577db7d2c1e2bb95ce5742540d87b04316c489b57156b0653b156f38899af785dae0f9dd0a2a8098b1bc9657f9ef0cb2f8f138760caba6e4d58e3d72682f7d268c61d6e29e8a427f2431dc5241d8ab856e16aa83270874aa3a94c4acd37da07029a4f06d7fc9c5959867115f145a1ac01f89b00acb9e6d3db288cf3e6a76f6fa0215ce9c84a0d8ead14da2a5a4052c00469aa3cb201395ba09dfb52326044411768ec0a2abef490186472d6f155f354deef92a3523c89704a3159def04db7470b8b63859617dac0a2dff230fdf8252cfd9a5729904febe0b49503ec667a4cf3d836352691a33cee271760df27195a8748a144a7cb1f2a90565c4b0f510b3925a7aa1d1f5f2d0d82e1788ebc02014daab1bd7db224a53ce74e8a008ed7ea6eb6efdd4f44e1a70fabd5bb1ad2af04103017a1b845b06cba30f7dddb7418895e80b7b26feba7d7d1ce9ad1bd46e9d16dfcb5057b2b366bcaf2f7d865365b743d9f039991d314d099d0ce70057a87b0e92f3961be396572d3f7ad23ec0efb557c34bfc8256c6cb97441c8fb53ff927d14fbb3f791509d6fa77daf67bd951bc31d1f3aaa1af126423a5ab68075cd399dccbf430f3f987c8cb6b7b57f79349a6115e3bb3dce18fdffb2c85ceaf36a21f439267d5e68d7384735aad8c1863d7c8ad981984fd363d08c9d79a59d1cef07c17bc7d0508a8b5143971abead550c9d17622248b637cb771ed1beff61e885a94859697f4a18842d84d81a282dca0f6a6e2ca7a771907afbbfcb9d9cf09abe4569bd7ef163383d1d9f552e755dd85f0806a322c3242b94a4c2014cd61f17edc789b24defaa45aad7e8cfe0ab97b3bc355e93c386f9440a7ef871457421ec0e5219f42a9db45eff6bc3b89ad6a69c221fbfe1608d62aba0934847bf4b65c8724117669dff1aa253f299bf1d16198c720492f29fb979e386d5264094f983616bcc8bf3671d2173c81968aa1191557a89c8c12d320eb04a9d4953fd2ac8cb626360e316db49e9d98063cf1dd285c5d69fb24ca1c137a8caf4b7a25405b529563ed65baddb3c92c1026781300ebefb00356546f85487cc6334b26b06ef9876755bbcc9f4adc3aac8ed0ecae4d5618f5f4fa5038a5a394dfc7242451cc8e7c6af3887535a26d326d324bba343552ab90c8185e55de13943a6584013f1bc196d81a004fcca584eb84917edc806f36ab9a16f78853e13cc10badd4b9b306d52b101f9c29f0a269c9e735664667910168b47de4f8a569de2dd72417b0af1fa1c8bab808f05c193d15cb8dd2ecd273c20b54ac782a73307fa5bbeb547dd454e363c9914c41deb5668d04b46cfe08212fd3a27de01e313a11de6df3151e2c8347cc9421b3df7c4b3205b04574556271a21efb5f274f5d54b4847f9100ef6c82ea91325eb867fa483cc80de196e8e929d1a70162eed40c716b50542818ddacec3569a9be2d603a684a5bd1f64dae4466a3f84ac14c2d396340a021f817206a43631336c426f1afb83fa57a6f095267fa6f589301fbc48ad4ad5ab1a8062bd22a5a837668e153ce0704e7cac0e102abee6ac5219cee14dc8a1f123b51e33e73c39caba8157f99b97b851025e370318f94138100b19de411ad31e5cb68a32af253799c930d9805a97fcabf225e5f20b1e7d5c3126c5ca7eec8df46e6077217f70dea813375cb409e4c2a06c614f5b08fd3a33638a28190f44a50bacff0a1cecee77310eb8526d8a0e9dca518ef22cf0b50a11115c0ab663c738a1b748feea6f6830a321dcfe43aa78b36f1f63fe63443fc698a5c24186f4ba0712dc3007724f2c48d93fe23607aee9e880318aad97f083babd89896ec0e3ae5168bd2c2b7e8e0e83f0470109275304f06614da4d1bbb06cf82fcf95984607089523dd853b29381713327b1b0b515dce3809c4d7b4cf7577d4312ab800cfaf0bfd85fe2aeaa1db1de97c0a48644226b4717c7193e537606633be62059cdb3e3cbd31ec1917a64b527d1bacfceeb9dab5343e9a42d270f78f7e9a30e380e8ff6c47fac81e290237a2904e594ca5198e57f67f935feac22542a9fb0711058a55cd6c895fbeee531606ee382eb6da09698397a8a3333ff6c99a12a8dbe24184586dbf0b09c2a16fe231755c979e1f025698b182ff9edef1067db8099b640602050a2b84e58a6985792a3b508bd121032c40ae3bc465125f00eb28bbe2850e035302981368723280846212056dfe8470734f6633b3133a370a31eaa3c91226abf82fa795802224f2c7e56c4da85ee54279b47f3c1c62eb2a38e1b324db6112c7bf41e545400c0a86c8499fc86ecd63d6787f322d5c2f6a8f14f6dd1527ae600f1251d4b1e8a98ed3de2a132fcead4d80365acf8019e7c6dc4c718f096c373c6664c41c4926a63de3ede7ef4a2ba7f0379ba279bad822e70d95a299c8d2d0ed1ba976d683d35ac385d9bf88abb17b745b4c22fc649846cdc849368207e3f0a68833f550397a5d0d2872d3facfbbf7ab7fa07478febf0780295f41ec6c486deff1230343ec214ce406b175b4cb5dfeeaac7c7285d2f6f37d78f007a0e264676a33df2ff117177324ef33add877534850002ae7f553bc76bef8d2d9c0d2ed7d426c068570b4e9bb443e19ed8bdfe9049a8026d3a73ccad71b4b90047a99ad9a504b8c021a7f73bef215d5ce87fe87e21ac6eb2cbcb7c91c7d31b4cd2802e16335907b4c6ac86d1bf085fa43d54c0805af15383b14ab4ac8853e6393f1ed6aaffcac17d3332bc00def75e212ab0a823054438e43607361e15d8549225bf5dd7ee848f31ddb9d7d66f1d352e394c07450774453f838f1fc2204688cc4d4c2b8e34e21bb4b720e9d80af7f8d6fef6c3da55fedbc8580f56441c26abcdfc6cbe627eeb027285b631eedbae6c37616d34ff9957d4b763087ffe919b4217d1276948d159b8178ed67681c804147cfe814d63a3bb9f0e0a320eb7a2b00e69c1fe8d38c0fe41e544fe14124280cc927521feb3b0d93e1009205e3f8658bb7d6651d8d63b254fea185e2edd1b9ed339685f15437c3288e95d27d41bc0e80222f516b7a30b61d7f5668d6d6cd217283b721c419e5e7a388b4358a9bba77794ba1a10b6f410d42d8b1e345baa5b413d6d7af83d7e6087afab79e71d8a3aa946b99cc85537655c6740ecb0f2ec9c446565426450a381823ae1109b67acad25da80158094790ba012738544ea458f9cc60f1f0d1757cb188cc49946537215e1af41ac2095bd6d929bc44a11a7ba53d6333830c36acbfc8ab0fe2a8aedf5739bea84619d6f9ee532bc8b5a896dc6e1ce32f42c9f3e72421aeb5bcf7b6ca396f97a2bc943f87bb135412e0a02bb90d4d9f48d256e82b98c58f3d5c206f3bce3aadba66946d112f9ef6933e638025fd6dcc3f641b88d000f48eb2aa3d4c14d5fb12fc4927a372ed61dd7913c821e1e9da1ab6090d0e036b85f4ef1f9081b698191f23b4d555c5c9b5830e84362f031a50ae653dc6e980419149ddc4d17957db1cbd89af5cac8fac2973d0ec268d31b5bad94be2e5d4df81d39ce4b63152be458ab53740e3a20132d380809caf790a9bfcfc196c7f2a44a11fcce0a10dd73f9de58751d30dbb6bf1edc22d1fe59caeb9e4bd9075539800c5047e6c18867c20fef3536c3a8fed978e065b9281a4de5a37e91d389659e0f8aba588f2ba99d27d7de35a6363fef59153b156583228634816e98d6adcd60b0a75b48de2fdaf4129287761432a2a5d64bce3ba73cfacf8bec6d46e0b2e1277ea872a056c0f0b12f41e83116728ebab450f5ac1a019b6c69cd4eef9773c0acd27bba93ced7ce96f5d1d2f611e807096b5d800ba38132f7ff4c1217187e9fbe5424d267df789766331102de3c4767712cfb8b3eeaddb1a1a28d76c43979a26c461d3add8e6d05d0653bfc8d541ac761f4ee84290041dc4259dec2288af195e482dc2ee196a69aa04b1d301aecf106972cb4972090e7a393ac7a5b469b0dbc1605f3296a01171722899ec7a57b24c9b12db7358549101dc6797e8394dc9e6d58bc2494095869c27eae88320f2a4b9debeb6451a8eaa591504b0d996fde1b7654ba4492ac4e8f1111d874c32732b12749e63031fbca31b9c1f84140979a99f801d836859c38e440804c4941b54387d4235e4be0e691ae60e13e87bed558238ca686922b38bc1ee983475d24e22380d7d865872a45d64b48aa8834470c43369ceb946b16a4c5ebcc77f183ea82589642fabdb39adaa5a15794c6202cc02ae95b0b47a4b5bf8b36cfbcab869ad12105e61b44347c617e9ecb21c0a8db108365de164ccf8fa4da7521c801ad37d7c9552cb46f3f32777cc50ee7829c99eda3cb91eb045f8f52bc58eb6d4288018b13a0b72f0a0e443153b27e06ff059cb43ee382893bd1d697f812c71b47c7aef3a8b90a2b8c023eacb61d993369cfc6dc78c243be2e2f019fec96da4a8547cefaa37a2922e3bdf987c161bd0c629a250bb99e89e23482c01eca431c5a9ebe7fb35b77b08a24771979d36e67d315f420900eb91ec16c578339dc81efea720fbcbcf79b0643ff08f65eeaa3c1a716d0e7e09d1136320173401ab9dc29a98a01d4b50654407200c4881b66bcdfe2ec0b8a47906244609204bcd844ea2bb50d4a3a44d2ce7a6a58cc608bc9127e898b7796070221d5f6f508036a42db20eec0dc796884d6eb2024ad929d9e91196b6cd3b35b61d9c96516f0c735391f60b8885356207cfa462fc66fc9bac7e413c00467e9a9048a5b2ae5a1975dd2100e6536cd111e0b9ed78d628f4cbee5559826b539efc4f156991d06a6410cf2fc64bbed62fd126e5b7e830b6ada452574c0d1395a8dc263352d258c1502670e7f5d3c5d9a7d85c6c50d06c6f581432ce9defdeb83263323a87900e18b1e329a84e507325a090f6a537b046f6eb48c341092e70535669ccddbe3ce19b999ee1e44159b273bede2a4856ab59a9475fb237589b4f3f1170f464504a5bc597f4f4f3306ace757cda8b802b9e5ddbad487acd36fd03c4853739ee7c7c675cdc558e0f138d3a0fd3b5cd05cb85bfe600fbd4100e22df540e9c45722498c8be33fad29f03a1c28df4a15a2bebab7dc6aace170b540fa756b65c02ebd7345008a385d331ebc0674630aa3297343f88dbea023beae04c325520ab0e0b4df3b32606522f2620d59ab950579a6322be7f50d4126aca148e8d9727315bba3321c5cdca9744b00d216a223eea7f922cc9897f770dbaf75592cf3049a762831c01505102a348a393513007ca3569778d54a0e4949bff69290b8096881229a1d8fdb55c85afaa9fe0130b4fbb461ea210b9d5c2f479777b6f647ab5b7f10c148a080f407aa01139f630bbb1e68cd81f4e131bdfa6631f9ebbb6e5ced9e033c2518b126ddf770a5a3f4b5f17cd456d3aff47ca54229c74f172c621903f249e61f4cc0330810dc35b0ca4dcd28893702809791edee965a4ca07f52554030e1358a2d86dad8b72f027ddf3a8eace365819be14f4081b187a8b58a2d3e64d9828582fa85ca5d426615c4c2a293815234968e977ee364c7dea2b26e2b6a1d782e31c0341f20e8cb97944088f77a3e994e601568be3ffa06be509b8e28563d8a5a7193be685637d6e30a295910f8ed89e5f6e5c144e1e086ecd925a3a4217df72166634e712730c02aabaee7dbcba646bf07db51e9b795bcf67687397039ff51b7d4964707768b456f8703d9283c46578c4e30b502514191638e51f9a45e984b78c660f8cbb901b3c34c1b0ab4ad99d82ccb6687d2b5965518adef2812303f8f0e76076b070f45029219876378f5917cab64dafeb1f5838c3ba0f6a708130897508f08998b6105331ac610dc43e6a7a4953355b10b78b9c0f2970d291fc134d72a5fecd119eca363489bfd377ab725c2b88262529512c0640815b29257c28698c46ad4dd19f7a9126eb73c19e726de12f5422090f976cbd3f8b90259ec36c60fc0c564fa3106857c83f97c3d6c0ac3896a4b7be16b82c243b306e186f72538d9b8a39b52caba80101f0411e1d571c027515eb4a28241015690219ee9f7386eeb5962cd9aadd2a2928a2b614da8d9043fe11655f6e6a2bf864eee334c67af14edf68d06598da92961598d11bd0e292042b95869977e4457c37a56ccd5da56bcb2b3850237be137a2e0d442ca534e113a14c16039ec6a5dbb2cc6233a6de94e5d3edffcd96fb5baf73dd430e5e70ebde1eccf654f464d202f27b3824d22589b517137cb16cab839b9ae42f24b14dc23055d697f2467c463e57fdc20c88f2ffc2ea70581e67d3bb001066ebd90a36d8609f98aee6a15df946bb612357adb3d1c74fd0883acf623505e45dfb5cf1eeba25c3c1424148feac7fadaa5aacad8737542706e29329183e9ab8e45e0a960c662e56ad23453dbb58eb50f92156d913d9364cba1b711db54a286a08f912c2b5229f761fe44894960bb0dea23a1b62522ec6f7b8671703f5bac8f765414940fe3eddf7f1903dd8f469187a3dae97da4e59418cbfda4271e03bfef20f17cc36004f19e94bf97b1c5fa8aef5e2c52e384e93be1049ae5ac407a62fa955de02201e7a50ab4c06db54b4dfaa1db8be3d0dd73918363b92d1e403c59cb40a46db256be86a803d98ba343339d98cd59ed4dc34a12493cdfe893f243e71f9911d2a3035bcd96c355d0a0473f5bb2bc4e6e07513222f5acfd7868b26f6a77c45700128751c2327bcdd0685943c5bbb6346721914c67e3f2ceeb0f13d57451b734134e1570cdcbdf4e78080a834a5dda4aeea11f64f5d0a48a4cfd3096c9445c2bf689711565b39b7b3ede0bce7562a18c3285511be051306ea1344a2c2e8cff6f05904dbc39e8e5c886f59aa37076e57e07facbcc0d681fc96ecfe1d4eab5ce0387c50b1d30b57719e9aa6e6373b648e245618680fb23647220b9650637ceee4ab40c7c2dfce1f2a41f5985bc063a27d3369e5fb9338991cfa902e3ef6cb2810e1393339264a4bf9aa5e57ec88ea97b25da51986044dc251c0a41e164df6e03ed6313c0159189b69cf80c12cf361828985d2fff0474f342e236abb36e3a46b2910d10f0395b00ee97afe4d26385418ba2eeb620c9b0ff695dad81e0a5a255234294887b67cba3bd03c665329e730656be0fa18b4a09417b44bb46a1accf7e2b273649ecea68bba6674073193e4e7c3c5e95acc88de4871119a765fe0cd10a7efd1baff33c49994f8aa55a7e0e5d1e5df5243e79281bb7192b3e11d16ea4205b6d6b324d46b0328499f3cd410a8bca7bbb9d780eb8bee12370cb7c628a21a95e9d24b6f1a625485e85818dcc4a5ec56e47b1a6c6aeb4c4f27f3c2150d5efdfc977de9fd307161076a57aed0604089e35113f4bdaad2d377955ce28cf6e034fd2330e2214a3c1afa83050198d8bc9f16538028a9bb07718880227955e47bac11c6b150221a4237ba32cb8d16c8fd85e8ede0937a9c1680a1596dc2323bd744fcaee3d0c542b0011f18db78f53d102e238c17b45e27fb6edc0b537952397edbb3d7e2a76c0b25f2b6763a6cae2dfbd0a13bdf39c41d24323d2bbb64bf2988cdb8c240db97a186e4463af091aa55b5154c481e87b1406157ecfb4921ef625d69654866c39316de09aa2d9c71f325118398a1b072047318c36b148b4f5ae8270b6382f539e01d89fa9bb3e8c64720e6f89154f52e4de6f1812d797f29c40319e82c4c857581fe56b20cd61f7ba7f0ff66649fb035726517e05f2187fd265896c02f5447202a69ddb63f1ef79f1cf618761f2313307e4c0a7aa29023a10012a04757a5f065c1511ec9cd43cff395418b4085120148de9cc74a9960bc1d515ceca014ff998fa69eaf7a51f0dc32060f2251f68fa144986f6e7c53035f3f7aa3dd8032196d9a7218b0eef4d684003a4eda4fa2085c18b7dc11ff88b77cf5778dc24f8471290a6167258f4970238b90b40dd977cda03432ca504133e968c32d344c6468bf4c089cb899f03aaebfd0faa74a776d6631b6cc3aaf2c37d8920d508a04cf8b5354dd6b424a9f4264f4a402b19f8599120064ea1e8815cc3a087e001d624f7260feac4a8fa024c0c7d82f62354cd81462662b407690d48f67ce09e85c4e7919677902fb5277d940ca291594974aae8d3255a8c8cd3ee9fb8a38227d5b0288fdde3ee20043e60a938154e55488c2be69aa8e074b5f6a95450b3b681f880e8c0aee3629270bf17d7d478edda1e694533f5d36d22143779659b033bc31360a45231812a7269c8682b168ab2c1ea95f0a56081e0f0d394d889a0ad63cdc3836073d14c18a6354e4ccc3c106783e82b69740f5c2b3240192e8290d52bfd4bc519bb0d6ae6d2a5ecc86953114b0bda16e21bf8734e98290ebe92b101d81c1446ce6ca4b2eab4b1cfd7bf70c1d86c483ab512dd862a8c55658b4ead35d542805b6433082b8386038e70c45919be4b3a12911b2fd181deed46a54a2e0d554951c2c2a1fd2fcf0c6dce7f7b4688a8f4681b9a4deb11ffd7a1c004c8fb20d2171f9c372435871b0df8228af21d9feb2f40c3392980688330314a3d2d3b2ebc4ccc317f12e4b987a3a165a69d8106fe3d23ab69b78189415084315d5041a991c889102a245a4dfa23a513b763852dc7cdaa7ce184809f14faa92fb2aaecaacb48326c3d406097f83006a37724ce2759d49742a18c75a36e44f9f9af78a8a3a4df6f31803351d519700942e35299f6383acf19fa0f183e077adf4d74e6e49cd032fe2fa095cf219b290e82025ba09e015c8d1004732bfa176cd4d5f50129f6c17b5f7e89308d230cc32c8980737fe7bc07230fa0f0c3280b0dcfe05c6a1b6de3d8f6fbb9480c36e8cc18e39d8ad286a4329bb0e980e3258c311a14c85bad875267d890f4c24832b7021530f0616015b47b06672e139a44230aa7b91075a5219038d7a8fa2488c2cefece19a970c479ca4581b7a35866d299d8a449f63b8759e50583a779a8dc48361cb541d7dc4a6286d0c75aa2eababfddaa0fa816927b712937a3a4d9e56e15bb99b918ebf9c7972d78ad71d1f1a58beaf0a07f469cce6b4024a92747e6f1bad2c8bf6f7a43a8b6e823b2e12fe018ae1f07cbafc68a8e33fc327e4c6b7141fd600a2ec8118d095280fecb75c604563483070e892880857ee5947933dd391fce463efe2675737458074e00774e28a71d15f762fe4c6c5d8d7aa6c2859fa0a012fe035ad51ab8dac4d43d90280b5d818cdca62ceec08b16239007f1c94f07d0dee4f26519376d7898af585604929615609818d28a836adf17da53aec38c1891d0d6c318664e99608fc995bf56824c1845f263ad758ef4b27aafec5ef6cf469baa0c83831331b0e1554c86048b2dee7ee09635a794459c5b48c37ca692d0ded7a7f5b52d7682ba0fa3c096a64086c2ec35db6a7e45f0b0cf29bfbe7793c3410fa6eea172fb7c23cf5c4c38a0a0d4095bc64a3a59b46a06099a8daaee30c421af62990fe19e31a30b1463115fd62c140a127dfc70ae309aa2341c777b7cd41f167ca7fa868073f1af1102e1f6e0aed6c1838f903ebcb4d20678906f6ac9e7420c8fd6019319b5bb3d3799330f50d94e9c529ab6b9dee89247c6949673e5a30ddb5c9137213099d1b5fcd34b0a9dafde881328cb36d71b58187a9f7208c9a594b5ec5df2bc1a61d789bbdb309486e15e35addd887e2ca2aadaa858cc1d88646d79a2f4e8211a4902ab7722c97a560755d1fa474bf52f4b7018841c9ea2234468686eda8e415217256b2928d0a7af87cb0067573d0923a09830994006630e02b8b61a635b8480e26a35492c38ed022178a702005e21dd0dfebf9d47cf167ce6e0d8797811ff6cf946d788fec53dd05c8b27f2bca0167f21c61d748cbbd1542ed4a82f523c637fcb324a0855ab77eb30a0dc1caff66a5f0c2dbee2eb1dbd2e84b7ca488d2f3d72569e1328118dc543c485895eec457521069aaf51854bfc3e97190393021cc555267650fac02f1880d1cef7023698b7d7744c60b512989c1917d3da17b304c9fb92aa2c6ba86bd977c2bfaa4829e84c2c29aee9faef4eeb278214838edec6693ad4bd94064c7558be5b990d1f14ed5b992803ec83e6219b5ad4df97087617e7c18a91fedaabb7d659f4bd74bc02087872d7a8b56af3322aac7741288528aa41ddc940d95517403ac01d4c03a1caab90f68029e88a96283d542cd55f0c52b16d7069d5a7f243037f6d00c556046fbd2e17fa9db82464891efe62313fab307682f17c78aa449014d79d25825d4926f398e558e035b292a01077388b492f33da7076d91876591f280663ebf1cd0c660a388ed329641c77d156c49b7f1e2e4b27b944ee6366889af4bf3ce58ac458903cf08b3e4e645830525450d7dbc535e7ac16105001b9d5619758832ce2256a94b71058f4ea0bd0d29bf889e65204d9a58053c366f753576f13bc75c035f5b3212601419aedafb66b2785ceda6f2078d2765919d62c65833279351dce8e1e716da48145414fc8faeb4287ac1394d725fe697652c1f79051b7c693a8d049c47c3756e09512821aa981f6fd2c93b029181c660d0b3015bd554703669d5b01b849dce2ce5d88265c2cbc60e145857d1ae35a9d5c032574c12043d634dd06d6afd8a789e774666ecc85291565cd58e3910598ab2264685c745576cd48846bf454434b402a8790e9cb0572913ab72e1af542d9329d9507c0357146069c465f1b89253c158b9afe39387b01501577c1c965b11c3fc4d7135fd2bccdc395ab79fea386c9664afa380c42072f0ae07d45ade71b0914ac35656c51399c265ae513a8d9bbac7a44f32711206d4abf650317d4acd40608509bd6c7d190cc85f769747df984bf8295ba5c3ecf1e48da1121359a5c312f9e8196812207ce9160d796908ff1ca4a879ea6f9b742c0b8286572f8c41efc3e6b5f610d800021a25a1cd31a8830e1e7f70a455c9586143c570b01c3524fb1f4746efc3f6b69aa525d2e2e1c03e993b76b601acffd6a9202aa5f35032c0143cd091a7233db2cd9947e832112e5663b20bf600771f480f1b1de9d224703103d23132065d2ab1cd448cfd609663c8f98dc9309cfd45893b9591acd03d843222e4c9615afbacb8e8ca3b4b98d5cdfde823a756a35df8b96e3e52086e4995a4b9d81fe3db44e6b1bfa42661c91c73a972955c0358f50e52c63a907e8d338b21efa0afe42508bd180fa14dddd058ed90e72bdc8c6ac7b4d1051c288b48e5a7708310842e5636253f9c8dd6e0b2ed27e20e9ef079b4d3ed8664765f0f102abdd4d71ab13170cb9e101920d1d8419eb607f34d9fc327e867366e233b062283d3e7cbd0b58c57f269bdd31b2ced8b715e0d9f8d26608cc265b7fa2f9820aba9b5f4109cbdc116f2b8d3c096f258e083b6439fa99a7696dd5555fbacde12a494e2316f17f0254a9fb0108dba7cc4c3b6f09b196d2e1c327aa613da28ad7044d45d6598d13f041da4988a4ebbbe999565c3b1ad6dd86a79c6557570c24109cbbb222f0da88cccbd6564d11098e25d6e964e6c83a85ff6ca07d38a45e51e80b962407e54d1c190c4a7e112749150049cd19185ade9560753974483c1d392fae8322cc1095f7910f2c750b241c3fc8a8abbe5d303744b4266ccc6ebbf74487b4b18e786e4622e181702fa4f3f42c8ea939e4a6a26952ddd475cd7cca4a8977b2079652972b95bb62babf4c73c30905d546a9d4994d73d570b0addc5bbb2a7473e3a2702d10d7620fb07d654460c21d7694dd83ab5de908152aef75a287fde22bbf473dd84c5c21aff06857b2c809ce7ca8603d39d3880acac5c407971429d615c035bcc58203544b39fe6070c8cc0de6fc384e1bd23c64be3b80f9d3eb662cc1ab588025c8f78805202f818ac2c9650c965b9fe518d5122cf124e6bd78b77240cee5570d855e4be407cd1c0bf0c5f04b7376b1603f6b0575440a7e79b8e5f3ca50fadd38e91fa08fbef2beb35e5d09830226a69c8b4b6676dfd6afff05a236f7a972aa861fb295ecf7b03047c6cbf0d47ce814d5708e4c4c8bd218454d81211a5107290d27e37cad2efb5bfe5f9b64e81ef904d9370048796df9de58bdbe9464ae56b748dc2a85b5f6f991a2e9f187cad9f217982674ec4c4e63a500b24b89dad69da905d45b0dc7165cdc1db5a4a87a3c9a26ad784be5af3d01c0cde83e95b583a2efa7e07bca5d8d136b34e5fa9542ac7170776bb737d5977d786306c61011c4194e254ac55219e541d396447e3a742d540f8ecdb2feeedddaf40423eb5042805239b12bc5f78438571b3b4d21ad8d6217f8266b876ba0683a421a28b3d8cb9c2e89df6e6bb8aa86f6d1cacc2a1f9b00197891bbf80d7ec035e29b04be0671f26828f90fb0464cc5f1dc3c9ce1ad1f0cd5ca02775a7f42477bfc92059eb2f8e36a28d323b873172138bce71491aa15ab677f5729ea30cabf3e63074357d7f32287d675b4c43b4adcd37e29946956153f6c72fe30c2a36bb3a8ca5a6d8ca5e6d596434b25f828c1cdc3bd0f75289386d5cfa0ab05a9887503deceac797714cbf7893d3117c13e486adab5f4f17aff163beaf2f8ad9ca975018b1616578ac5cf1e50b26819a63084265e89ba11ad963acdd23e240eacc4a6dc86a495798d35fdafbb66ad507dd39d647cec88f2ed903c0e7ba551b870f90a3806648d3c9afca54f9ec2fc30490b440047b763c09aa11a48d4f0e8282652c10990812b82d7dc1eeafd37218c9364a9ab853eec397780a4a6d8a172365344b3ac82f860c528cded83ad3581fa022b4d7b1e4535f0b938e9f81d0d361d985d33b5355f1f787875f941ef59c51ba1b9e83df4b38506fd810c09f50fb0397cd14fe90a909cbe571299a22ea91f2102f4e7c220dcda3d44ada13e970c874720da5963a1a0f762a8c1390dffc82cb68b52bf5261a9f6172ed11871fb1a3026a39caa6a96cd3a3ad7d1e40efe8d2a4e9618e7efe2af157adbc57192ad0a0c3a5c2c42a4a7bbb3959bde3de24ba141ae0da18519a6ec2dac8fc156a14a2ac9071a50b4e906e29a8ad0439dfb328243eabe9c723e1f7ee2cc59ec3a327520df6b64f630e25687c39d7aac6f247bae1a9f155da5bbd93e30b629b50c3cc1edee9189d93309034e82326c24fe6d32a962b2fd24b8fe5044cb8e0b37de7bb6305f461c8d7a6944f2a897c82e308f2015a2d8816624c45fc0b38573a8748b3cb0871c3bcefdf803ec2798d84f4978487d638bdcd33431630f46629220cb0a953c81794fd875a92e9928211386d0131593d21610325958d77a3213e70e0b2c1200c18e837f951c4ba11315e6b603abffe678a3b9008a7305872fa7e922f007103ca3c41576407e89a57022179da9ecdd98dfa8c17303dd4190c79065bc61335b2b5107636bd9b0c9003ceeaff3004fa92b4abf1553596074544262d989f7aabaf253c10aae3c5624f1d0e421475882a45d58c6679f3d8784eabeb32e61b7858721c6a3159fcb55af774c6fe2462ce176ef960b45c1d4f9d29360c40136c3be9d32148e7e2398d081710dd6b88ce3e5dee7748c10a6848ca645a97649b22c0dba219287c4c4dda9bfceafabdc7ac744e8663803e98a1810e197b186b9b2fbbc3d7b2f11081d1788e833692f521c34cd555924d8024ff767ccbe4dfc4e871851638c5f80f8a01c62d3ec091061153ece0597c124842a509292b4e1803026c1b1ca700859cc24cd5b761f5284f1ac1132a4e2efaa4afa16dce83721d1c4e27b7d76022e484b3d694edf9ee14f2b069d18fa1c5ef03ebf23ee5f215f797f3d5d894bd191a9dd5cf7e14bda998c03785f3892dc0d55ae6b3352373e6d8b32a2bd9d34da011a317e69b1f7a9b34c49d83c440351b1abb705f00385520edeed130fc3c0c3a58757cf5b8f90d68e74eb958b20bcdedb011d16a6a0f02649eb86dc5252d221532121de940fdc793970b4bc303b401e162f8fc802786554b77dc73b13b0d2cacdaba98c077d51f89ea95ae8ac7159dd9a7c0f534796892842fa8193cfbaf6a368e322d68d871d21e3f5b486d1ea2d5852777017b87dc754e87e4d6863416028a31609a0a01b387cc3c74486b26aebfe2f09d0579b87fc772970d167501056646b0792aa26e720fed91a377fd1982c2c8efcc3f000d870d0cf1c7d34f481f0cb72ac406879db5e216f8b34d1ca01f91f5a5c23a2bbf2a2f848d33a7c98bc6bee24e28c9325e66d010583c9c0d942749382e3ba0ed5dad3780df5a661273a02aa6db8082adc129978d34dc43ae980be1032e977bb3ec26a5211846150ccba7d6eede110da40daf72c886d872a00b9281823351ada6341d3c227a665e3d9491b496e16a3122ea758bcbadfae76e5adeab34b100e764a808c80dc22c6c43cab62d35ac09d3bb09c32be822aa4a5f953fbd20d6a3d0d88ada741cfd7cd75a9068f7a59d8c245700edbeb946ea30c19768bdd889edfdf8e72d733c42552acffcfbfda91adde53e66c4c3d7412774f6aa7eacf9527c521a4125bcc7f03fd4d4bdef14bb994217f80820a2598be1b7800ba021010df1234178f2d5dc53e1a571cc30f41187823bf6047f0d4b32e6e3bed8692a64a7e80585e273ae4c920b8f5dd475c2e1ee5ae3f1776bc4039889aca5db42b9d80af1a77273714765ce6a771aa2ee3271496cfaba7f5618da25bfc83ab6e0e5d5008a3997fbe48e797c7f8321f44409859424797c977341ea9da875f6bd973ff74ea5c5dced18c6f9e5b0ccf3c85550e8b92c901fee68d815371de7b49467333ef771c38300cb7ca9f2578788a2cef5f5c67591f0d395cd54f24d7f7dcc8ac8a05cf3d906eea02370b7af21598564e973c70d44c3bfdc0a797808ae0a2f7e692d65de9a006e10b7e362634f40591c0f70f3fde8c8bcb350d72322a2abbdb000e0175c690f84707820e4b435b5fca2e73da9340f3761ce860295572b81981e1daaa15464f84161f4c1211a4a2d8066d0c7568ca55840c8d7a834eed7a4d1f541dc5e223f7aefe3b06ad8e681dc0d5dde225738b4e7c3a0d9e808569d923cf8a19241be1ecd6f24eb4d78d93e4025e01c38714aa32e2ad0e262e34178b5d773b617e1609370a05b986547111f36d25f030bffafeb3d8a27c44d7f70ba01e5090dcbdaa1e0148bcfcd1f24497fe5d541becf5daae4aa6048c714c9a78f4c8a6f7e762a651799a79bdfe8a0303846c22a8c71fcba068599f79ba359086eca8b8a1888b05f6dffda2f296b50b019818538446a4b006a8450dfbd2a05ac7f495b323795491289e885e6dc663363b4f1cc7f6a7bc19143ac72c61fb4d46a2ade7d63802c517c66ccbe6444221b5705798b856e12b4c5df0c25f8e4d40a576769f1f2c352a0a9eb220dadf3e95431322ce393bca827efb5e0aaf4f0999f246576887dd3290ba8d3c0127c77ed816a4df7d019f24c0fdfb57dcc499f8525df47d122f059e175ee3da060477784761e95565056891c909150501f0252ce427f78e5e123ea29d70c5abd013b2b19946e3f7e3863c155a1ba5a438fb492cf777391c26b8b70bddba74b1fb29e40df659d17bdf19bcb409cf1e25ae24c0562df6b88b61e356af26a5899a28600096cb9feb26b47da751805bbe2f27ce6ff755287c94337e042309c3e4b1e0237d3215d920e5f7e805c62afd4d362685879562eb159a06c5c2fc986101397d86177714183900d7aa4aabb2571f7d20c4d4f583dbda9c8d8f645ffac911850024b125fa7c304d8f5bdf39cfb358b16b795ab39268b57ed1aceb5c8e1e749d5879beff9b6c03a8a709450635796254bf0a46f53417606e512e59b0663a927c7136cc80846f481486cb4fc3bdec2ca89de44464ac16f8d1d25452501e9ed4f1004fc123d5b6cb91375875bd6f5d272fd15387d33bf0fc430bfd3da81ebe2a7da04fd8841721954f2b1fa9b985c7cc081adfdf7c539b9b143039e9f6e8447fb7ff32753269610eaecfe337ee45c74a4039b9c5bb6a5fc384756f47b9f108c5332f56b6f213a3566441354310d4ad6b83c2ef212a1c29c023f43a3567b0a0dd26e2c42b04b7af4cdfcf15aeed45f5a26bb5f1d24f062181bbad71e0d13857636dd0f38fba24c417b166db8d36c981499b14dd18fc6024fb0b59224f32c0aea35e1438c142dcfd6bd121b2d580385503704a41e8715c6528d6084aa84279e8ab076bd6619a435cd9f7aea48dc29f035e5e58cc17ef93bc969f0f0d0d069fd41ef8f22eace75469510ab733f2529731a60104c83532988daa2d252c777b29c5cebbeb81d07b5084511510a042bae960ce4b100760714312797e5e4b8399b55eaca6cc840adae89d55f50f880257f8f729c552284253a0941bb3e2b95a80348c559b9582a4cab1e712dbf577733c54cf77b192f009a2b155b8866cfdbdf94a835e558fce429222e81d08359affa1a9d01abf395ab3440f7aaed80fbe41c4ebe6efd1795a7117d1faccf65e6d5f2434c5f088b7a04077673391daca91aad650947a2c4ead37635c410c1bfb52b5ad152e3325a7311525d4e7e762d4243c1c954e4df67c016f8b267c1928615301cd36c660a5c3155901c10752499f64c5ab040b29243deabb717dc7f52d57f78637a5afe7aa89a8727e924ec8702009a0074d6366a3f655a798617a8cfb6466d718f13731a35a662d2943fcce3ec8614186b6166c6303a538f18262f9a54e13b59a9f2d1d95aa205e65dc246fd8dabf48e92d24880c164e977e57f7e6160716af42ba4d5349706960cafb9c232d0b6d5579249bd27e353f2c233f958c76502099a8e931a0fd00fe00268e6d839ab718943213b3f93fae6e0866b7b617bdefb1f52bdcc38f5d9e444e83771681780409e3353f702e8969ad8fa59ae59d8d10d1ab059be96b448badad337ea5dd5e45879007864742daf71f63584a85baa12494431e4d8fceacb3396fcf10e8fa3978155be16bf3513e18046dfb156fe4c8fdab05a90a2649f9ae652c6b7a6290db687a1032b6cb308af694b6e83a818d0a5f0d66a62c7ba79868aef37dd27c0263f835d3ed46dbd526cc021d058150f40f06f6a9debeea90bc96fbc71542ee9769a02da59fc015bb09f86cd1fd41a849427e705a63b05fd5a8fc1df2fc4128d57f020c73c52eb5a1d1135aa4323cd4aef5d690ae59a946ea32c2c774d40a196ebae3e961036e576a5ae732763d93615ad59482d95217bc1c1abd3b101f964a3a662ec57fc5c54dea4928f132692e9270ed1a06eefffb46c9f314e899cd21e08e535ea074ff0fbccb9a1b8462263d49c1b9a74ebbd15169fee6a1e69a586faed43df8ce8ae6ebdb7222fbe33dbc632d87529dcc77eec9265cd0ef7b43056a94e2e8fd2a81b6aa87e4cedf7488feac5875ca51158caabc58334fd5b387147d7d75be10237ae2111257d9abef1e06ebdbf422ebecf0f57c25464631eb6205a20fcf4303f24bd3ee1a255762fb2849bc0213804dcc6a462c181cefe4a04d5625a61673e131746d3d148fe8815ecc2b16875cff18896145a1df3eb308d1c4828bd182ac89a82e8822290b427ba99bc3ca68d29b473bd7a7887e6c99d42ca47db15647c5366672f9be1dcf7b065259b090439bb84c2b34542eaca4b8dca8810e2e6793b02ae1b677c2a108956621c8343007c6cea904277df9cf724dc022a26dea276b08b022735059b8ef6dfabfe0321571c813b26066182379f0f7d14e4710ffe8026992262181e8c357e8e7ee7a17b112b927e652880649634ca09d2cc2ebbf8954eaf47a6ed290c037b54c3c5316416d8b9c09a8ee8492adbeb7a083017709ce3d9d2a88999daf63e6429e120a57ba2ee375a07609e512caf5961d5e7203948795cafa90cc5ca889e8cc275e4007498c11c858ceb91e505ef35913b32646a9ec6a01c1736ec8680289a20eec69e130ab0db41ea41e225843ba397e8e961801f10afd8fe17f2186b80ab75c21d6fdf85fc948bc803fcb2f28059501a51c0600d019eb999a0eb696f167afae676bdfbea51e9910f445ab7b928d501e0f439d084993f831c68af6fe889551c70d3abf52ea7240e18c07d793f7ba69622911bb4768ed8d0e75525eff46ede59d4c9e86993122016f3d66a87fc666ce32eff0184bc19ace9d30fa2f495111f3bd52cb67d402ee0073c81d99be64afa804c21e92ca6aa2068753e4bae2efd068c28cc16caaea97733244f02c0124aae319a962493dd637a889b836947b654da59527583d672c46c250671ad84d030bd1889b181e6efe14b1b458bf44e8f58c45e667bf8228687ac6cfa2bc5012c5d4dccc39faba252c932a21ebe7ca9675abc3cf1bbfa37053e9f84418e476e0d8d829baace55280fe0ee338319806bf21b1a30878b1aa7e6d33aa74e0898aeed644aa4998e222e8a6a69314bba0727e7513ec3bbc031e931b4ffb1007b1a6f1b01475fd6882ee93421cb5426bc158309f518def010cb32e68660cfc22c3ed4266b2ea39dd1d0defa3f1cc1e20f620f0539ac772a2918541c751b23b80b8139e5f31a71b2292d8883c5bace1a0c724932cfe303bad78b79b6102aac5abe1ae7d4f90f356fae4238c2babebe10578005ce90688c695a81e84d3d68df1256c65557b506358023c5d6e354a63c58d097eba4e73f4ec13c99bd086c5bc0dcb129a2025e25767448b609cd8bd83babec13d61a1baabf942f9bced173b4a6e8eb21cf14d5ffc9e33098b074d5c2fa534fc9b4dfe1304d27cf85c540bd4c2f7a5f3d4e05980990dbd62db7c56d720bfd2109e9a34aa6dc8910c0d747fe2dbaf2e91bb2527dc69a9e0b4503e8bb912c824bff4091744af11aa14c7a10558173d1d91e11b9f135ab22b26b01484a6cbc5ef0577888959e893b9ab80274f2d9d60029e52c76feef37205dec3e76a1af6e1f1a6447386cfb2441d7381f17ca0835758ad22818a14b1acebe74a2581b485e46593c02925e636f859dbbf3c455a6d447d8bd73983b90ff0c262676694e66ca737fa1c4b274d277966b6979814475c3258a709d86f0a120604a4ec50035aff0444b72d1f88d5cc0de1b57831bf53a40a6692aee85a17a0bc732c86c9ecabe04557a5f5b415a2115f38296aa0d470447559eeba42d36cf293c0f17f93980a91636518b3b2b3611867668cd27f2738d54706285160f1f68a58c01d278fbbb50687b6ee5d84be22eb4f08812d849804d7afaf331831efbb0952c0418baac33a8186fa196bbfcc7fcbfc1bbe7af78b04fa2a7518f54bf5265517fa9c8e873adad217f64f95d34d6c5da9ab34a7112ab86f5d516bf7de312f980feb1f6f2d5f586fa6d41668081ba1cf61b88eff17a99c3a2b00fdf79db32d065bfb5060d1f5eae44a47da800e377b1af922dcdee7f0738a21977d17cc28ea7d7a7f54b852f50cce1e55ca925e10a81d2276e724439be16f6107df6e8e520ce008f3e28b315bd0651aa7cb43db3db9354934d07d51cd6e0400ce1fc4d90ba15ff0b01a9f5248de0fe23e988f25243f4977ab98655db78f41a0f1f27380efa89309546088e8d1e04bdd463461357b4fb5ad03309cb6971e63a4bffc8d4d8bf5b7f9908659f6c3fc23481ca5dc0d53f369d8df108637dd8e313197621e0da7018e11654d3c3dc9d4e7e43d36108f32b778da35804f41e15668e32503b8f17d94ef772bb2fbd2937bc8553cecc8e39223026ea54494e5bd7ad58ce56c6c46a0d7200a0b5541a378a0820a1cbbc02e9c5a3037822db7fd41c3fbfc4f0de5ef29a919a439443fd8ed5c0e9132dc9a110707d7c6296a8d45810bb7621308360daab13f2ff412e1100a7317f57562ae0f0adf6418706162db17f19284578e64320696c0f3640261808f1f48a6e5ddb468c0693309937c3b5bc59ef5f2971094e5dec6072743b6f21bf8acf79622753971bc703f36153e694e86ec83e05944e1673500cc5a82eb9c2863df4489d9f04e1392b85578c1e2076c4090d16f5a23044675c1080f4ca4830e6c45781266bc582d38dbf83703106e72c8491c0e6cf4364b97dd8170291ebb088ac5a14bf2bc074dab48e123aae41cf8030b439f5f8c955dc6bf3317dbf7d1302beab4160e2e6b7d826d96c33514f67c3ed68108b2ab6128210d27984f9af5f253408f6af2cc8edca209834cdf2f801e17e18ac6ab0694eefbd10a7af2a52550f4a8624f03fc0bed8111c555db7625cab62b3c1aeef5f1bcba51ee05420419f1bbcdc8e57c4ad3d3182ebaf8123a4e88c67ba0c18225c637337390dfa02470fa25e5abdc91b929ef0825379c4481de7349e28d59a61668045aa396f36cb917d4b9acf0b2e6e4405d0d0d1d8edade7c96dc681e5329ce636d9568f6686a790b92ec145f5fc9284f80b8aa506bcd5257b661b5afe044495225e5eead1ce021069c6c1e486ec1ec237daa98ce60c579500348624f163078c784f120f94a0545d0be1966c8270f05a24b85de0b5cd1c5c1ca7aa818a1616f9ebd190abd61940ac498dd33f93d70806de8cd2b536186a7f19565b95de441c0bc70efe06a572d028c52a7246913f31c6f1803eee116a13d3cf9fc9879aab42710566170e8f474c7b1cc2134149a21e244253e30b209f297b6401aa5e666e26589fda60e9968409210f0ab6577b93ea4f0ad393f67fa01d1a4290a248e299dcca3b7b26e31a40d8b2daaf6ebeecb43a8e6e8512616d1fedbb9276722c0044bce8945ebf44b7d7a1228681d9bc8db3a87c00be76d200afd5242165ae4770002ab40e00fd0afd451db85574614e0d7250c15267b2ec7f510c9f139b2e9e253704e50c73254d4ac2e274928a72eadd0788e53a2b7d4348970a1498f415d538190e97e9d3bb9c5cbd09e5249dce8f9efb78e4bb73844ed145ee8f0f3e38bddecfbc936282973c104453c8be1b8290888fa3d103d4f800713c8c9abf4985069e93855af0e917bb20d1a432acf1b56a7da12cdc0266f13689d05b58a48b6da9d118ef7a1d1c3d06280d15d25872749d1d30c05c1ce8abb08779603e12643a48f03e636a15909f525aaf94611c37ddb9838d7a6921ca8f035b23c1280bab27c57827ed30ffa0143babfaf80d72fa53b21900870d62c638babb764a3ef88962163a7cf4be2277a28f1098d5c60a044f6884bd59a6d47e91cb3ded8c005db4cbe8cf201849f6c88e577016464500a92ba9d025f3db42f8ae2fc5ae12d14968488afe7e3fd04f7dec2f3ab60b445bf6389822fefb211589f3f77ec215ee4cf187da0a02a85f6f572bbca407c2f533ea13f288a309b2335c43b7ebec76f79821ccc26ed3d764567af635307c504eca9e52ca9d1ce1c1f3ae92f47f5cf8bd0dc1283c99ad7cb548d8cb169c4cc7cf4195b03328b4b491aa921e4ad682904ea51308d7cf4a0b7d240c159537837baa487b56b464eb268c31e5b3a01d105d14643f4873b3621d406792d7fd18c0e3a86eb7b5915f01c086f33c7a3791fdf2b5db020084e73f8d0de17cfb4ddb9d95854f1bc88a56d71960fa607d97b2b46e7d84a2f350bf45b3c0b64c4d0f2bf7acbee6afde321912b7e2255b36fff142e4b124dec030141392dacdf289efa1083c98a06037627dad5302746cb45704160b3bf12d9fb874eb6ef6a8c8709ca00c39ab58c1254c243eba63f5e30818977b60f35aa0f06892351d8eeccf4651987dfd295b6d233989f96e821b949a0dc3463f3f24232a32b89292d57c0ff94cfb07a97d1ed8fd68e8fc0b062d7b8a722024f6a0d623ed7a57476ecd8bdbf4473b1a9f7986b6085592681cc14e13924c02fee5fdcfbcc6a9ca9a13866f7f92592463cd47004decd82068cfa18a3535e1e1b5d1888a3f461b35fe7698164d2871a8a9a69315a5e148c7b18b5927403d1e319850031464ce76338dc78dff48f82fd0d4c833aecd3c14fe677931594956c52af349482b079062ee9b398b63b938240d5a16a0c825125a07f0afc393134ec641d637307629fc641d8915441fc22da584a34e19d98b9679ddb9967810fe9f9ed767a8b669d3b3f8e54cf8559e3102eba0992107f22ce4cff7d8c4db749510a4b05157f19178cc0782de3d56b908004cb568d90077cbd6882fd003c9188fd76cf6b8f4fefa534cd81b37372577412701fc833720f4fa8474538ef974e388685bc256e38c900d8072fb45bcc6bb608f3587ce2c4306e7e4ff8c748836236214fa77f6ad1e3266f3352ace5763489122bca6ebf7bae3b79540a24055f39f1699df84bde8bf474cc9587cb2e16f18966800f8e09a0da7afe43569bf3e80620ba762ffe9072d29f731413a09ba57dae9185fcceff1bcc709c9a48137e0ab6a8a5a0c5f477279fa225d59a60a486e8f326b9b373c0939e13ed1e251814caff2c609138e5b8f455333cd39b51662682c349ae726ab55109dacacfaa2a50db967c6b72ed7caf503dece1bb0710d8eeb1344b5d654b527380955e1fa411e99c3df979155982d354e6876a75641b582868fcf8c49c3a3dc0e3d444d4a690f046c6ae98d2685d9c5a29404797124e0e70b799f1fb5e257e0cfed2fcc8b61f71a1539e3035a41f178084172086444d11a897689354ba3847c75f0b92152b50b148fef7dee888833bfefd7896329a05e3f49eb15de48434296828b8408dc46ab82df37964015a2896fcc2b29541f269d154a7410880067b32281e9c4978338eea1624ff2a06a2b274cb0aca62d462872fb12e4633bc7788abf84b7decaced7da5d2df03f1a03a79b583b6338452759a6620249db2cffba1f3fe3f0a57a393211b3f02052c213adf6e970d405ab88ae89937aad99a64162f0ad88e1e49b124975581f2dbd41e2ae8492833691aa56b03fdd95a146cd353ee45efdb150d0b9c4997e4e80121265e63bf544117a724cba0f2b9d16531fd43660051115bb2bf432a8ee82a5e3e7ed7d3bca69e35a8ec9d2f487e082ac961cd89881a1bf0d717fb8482f0d8752e4dd74c74d43f2d00584d177e8aa0b7ed631b2b0d60b88e50dc622c95b4fb2899185486f728161dafc39b9c5cb30d2dc04b190714698d3e6382e7d64bf80307c64d6a233b0993997fc2e0934b338c42653c44a9d8c787493bf40d3d465378c2ed4b54677486e20777d8249c0e29dfdd6da26e9a297841a245ddf40b2a2f78ad03b8cbb996ef914e48e094903b0de164dcd1540c3162b6edcc5864698d8988d9846956af8c9623eb8dbce9e0bf457b3825f3895e35e18a01b23f5ec7ef7d9874eced8200094586bae6129a6f745a694b30549dc78d564f6a1c8d6c76bc62ad18d825a90cc56229377057be7a516b6f5691abc61e3f446cef72ce7774cad26451a444b86324e48da916eca7db53ed4e5928194e0c1476df0d5f8ace0252deb1748b65d1114a0a694e760fbeb257caf507b821af1c0772a5967a4fbeb859ff921bed2fa0120a3a198adca81f3872fa0d787dcc095068fbe72db8d7c4e0d7baf797ee583e9f88e4682ac9c5bedceac54afc0ab8022a458b57653236a0d9025dd1116a502ae6120888d59a6e3cb826e8bd72a82e6af0d0c88ab16f2fdd6b1f93ec14869721ffafacca450c7f337c0e949593f2fd41e917809020ed49a37d913f71a208b60915a2b3d7c56cee45abbbff4c5c2285a59db35425b6bcdee6641d7df6c9125c29e3a5f0949eddcc179b5a0b1beacb8019eaa1d8d68cef09351644fb596d0fee520d9cfc30c4482f4be94b6f3f4ca5f8112d3b40e56a66a0b7f0935de6eb933766d43c8c1127f888a9cb6359bf558d6c0ca059ad2600ee08f3312514dc00fc51b2571f4cd6948e2aa5276f17476cdd3108eee19ce51abf6bf22fd9a611bd1709ae06f4192e91b18cd25a8e99afb4a02c3f00d370c3bc85cf6e48d10a2fa83ab814c39f4198f58e60eeeef71128e535adc21ed4452a34e89f5273e2198665013920e0838207899a9dca78da6c133f7753d25c64c32996e4b8bb1920314e6b4608ddbe55dc3c048f88552b8f4de720eb007370bf9c03071de7298ed74fe047070196f0d4eac5203fb7641eed5fabb964c90961b62cf5a5b6514a488396a349d9e19e4f4ef5a6abbec6b4cb416f945716dc590fad2087d5b0a73fcb586d7b971981e56655450c650ea2170b4b5f0dea6df04d242c4c20d045a602698848bac8eda0ae4202b161228a77cd2492d45d1d02dedf37cc59b1fb9a9ccaac4aa4b7b59a3a23028b0af6aa9c87cde4d91537ad4f11400ce618a6b342c9509e2580f7a6ee41ef71e0c90d6473c6760667f25c89744ac38bafac5950657066df4df9172292e2d462f48181df0966656be413839e4f4f5dea142ee418cd671fcbcdf38d45361c90e5d5f153642317e142384c5a501556fabe45301f25b4af4ad019e7d888249596830ea1ddb431b9f013678237d4758b136686166d247ab02ff5163e95280b561d9f486fa0f5543881d254f1e469efd189d34209e020147a1acc83c711681134f8c0489433fa04d17b41da780064fd9d45539a8a53aa633449c29f3f99496024d27e162e16ab3cec39bdcdbf0ce07e9e9b2c57771e18ac85ea3f44f0bfd0a43480362ddb2d1011e20b80aa4a4bd65e48c89ab2a161c9c7823d40295b26262e4d1e32ac365d5467fe72d59d7cd0f7f28ddda16d4f621f844af8735019aca16e20080d9e0fb79b41e4a040dbb48033399db1c03d3a60b42b88eb8568705307c4a2a7c647c37ac281229aec276904878bebf4495665d66f0f7c75c1b121bbc5039468045321ebd44a554c3b92d9affb1ae983e330e44e58f18ecef8e6cc6308ce3c476b684bd6c3bc2b2c388cf68e9ce1c4a7e681f917954a927243d367900e8abb350f5920cacf16a423b7029de7e037cf0d4d49cabe5a419f560144c78f47397bea7dff12f382da44b19e587250a36bf202640e1bab6834209fea4e726443fa00f29ff181527de7853325e9d6e303dee1a92a4e95b85385f2e1e5635b4b953b5039fd6f5bb64be8d4c77993fedd5c71726555488f2de6a1ee2a931589ded05f5c41c93750074a0874cb21ddf7a2f8232a2f7f8aee69402b26c8605425acacedf51c4a685eb676c1e6c997f740b1d197d6e239a2b700427996528a831c015ef49c3d8890f4b9684055efcd67d0094f8718cc7a44b65a4c9485fc616130e330321fac14440101e5afd4746d38610265076ad168abd653d9a76943d120d814f58d41fdc8c208a260d28c0f4cf2bdc4a19ed579e705fee33e6484c8d4de9ce63e7263fd5d402ac64de052403040ac2f68b59309a19a7036cc1df3b100bb5ac11c02c034c79f7d9c1c55e8df1d20113212d36641402f48dc034f392b32b6a63e4aed206b5af55419026ddb9194b8ae27f350665ba1ba6338ace3e4add9335e29316d2f6de7bcb2da59449a67708bc08030930b5650851bee0c9752ade8bf18bd2c7e145e9ac397c4a25777777f973888b54c260f443102b818ffc11d23054f0e9eca323c76ba624ece5a00e38601d8e57c2bab0032d43403fa42fa461e877252a5801778f115124ec5e8c4ba57f189de3055f70d0cf54afd73588748216228cc0c9feaf2342a235759f312d446029a59432c42146ff4667ffee31224a9e11225f06c5889c3f8388e402f61fdf1e11b9e843ff3524baf7d3b4bfa1e7624cc28ae0fa7247d3b0466f75577d7a6be86be8b9ef42603fb769212a128944a2d744afbd28c4ef8d41f28741d1f744f2e75dea71d665d8d2c74ad1d1dff9a6804285c30e1d4a789ac06e5e3a394558475a405c1c2244c70e1f3d9ee4d8c163c37c3e6ced94767e3432fc1e5387428547a2c7dfab5c340a4dd6cb211289382e07cb087ba26f47794458f90b4d32fcde86448ce4ef3f9f1c7772dcd78fa67bfc3c8430c4804d39d8976fc37efc9e1724d3ffbe9e1488974361f784229953e168d54cc5843150f8887cf4828efe0b8c649984dc3d2e252e8a82c7ab6387b43f7cb828a1481faab3b3c48fb8705e3ec4452392e5a2fb0f19a5071378ec183599e4dbd62c92270d4737926738276c677a3a76f8477ffbbbfd1804bc02cbb76fc32bc369c31e41f208a4943b9a2e34c9b00747fd2425f726f9f3a51017454be0fadc03f1f2e4c0366d211017fd7df49801fe6862e60c674c0f766f5aebdd500b6df5324b6e06e8e062bf6955814442c0c9dee3bac9fede9c92ca6f8a5a654f21b9e9b72cd5550d8bd0da51d7e7569faeb4d734cb0009c4f3fa9de65124ac47c28520f06692dcaf9f8dfbda5d85d76fae9409910e2ed2c8d130cf22bac4bcc8c880cdf01a5ed75948b7754f93a669c34e70f4e808c4cbf74df3ebbbb8189ae6d76c675872b1a5049886a6559d3f964a35f692d40ba279be7cf2d35066cc8caee12517b1e7a4ee6ec36a9ae1aaca77297c80321d8394f27dfb0e20f5e703f723fcdd08eccc0969b0a5c8e851a114e9ed7724b008e985f8195298e4fe0cf843a119baedb978f3f8637b91ca935892bb278727c7e4eec96165d37d99ef9340d30d67b8685f262614057c47cf6da64e888bdbf631d9e2173d102f873e668a409c4734b46d2ef247fa14c1a30bd7242677ec774cb6bf5d2fdfa71d90ccb9efe2a87efbfff2a2e8bbef44607f07de97df7d993bf7fca3b9df018930c67fa3330e4da3e7e13ec67f7ff4dc18649418fcbebf10911cfaba8ebaa8c96bef9512c8f32d0b4014a44fb414d9b4ed2f0e1d726cdbf6dbf6dc6be00d4badad0fa494b7774d7bcd1b716e3ce307bf055d1c556bc8a2cdce340898a803adc0d6daa02f3f88676bc3f147b774b19bc57edb70dc6cf5384b73373f6b1fc617ec776f9fa3d9ecb7b2b6946b491343d6be858430be80b51048a919a0752112efed0782d24c12edb5cf8666b3a669da0d53f826ad3f3469af7dcf32cd26ad7f0bfb86a52cc4c5fec7e9ecf7b400a867052bc81d66c95cd852e4bef6f6bb0b6a4066910ebebfbdf740bcdc7d0f47715fdfc70f0fc4a61bae6abe7f438f036ff4d51e48e96a9afc3c7c423e1772d807fac48b3238b29c511f87971153e329183a1fdc26f773df11b9df75a18efb5a3864fe35794434f0ce13f71be89925092c339ffb9899f940981c60ffbea94d3aeae44c1deab07fdffe64eebb3b3ddcfd959929ede74340fb39a7f63df39fc7fde45ee37a703f0ac78b391157e2e697437b395dce90a93f88cca6ef7568bf7de865062289f7406caf3d104942a10efaf781d07efb099a3a347da18eedb5d0d43fe3853aeed3078286a60e635ce45e669e380e096cea5efb9e3175da43609b7f1f08fadbcfbf634ce6b45739776f5ecad9a0ccd36b9a4ec1f8827df6148294d2a35f0ac6972cbb0a6de2286f91d65a6badb56a4f48026bddfb3cac14eca05651825c59fe536bceabe5526097490a8aa855b43c2017ebd75a6badb5d65aabadb5a9b5535c1e9557ae3945768cf0b4601e50abe9c52ee20c9697112353438a5a5a1474f04dd5021e6572adfed53553b7453c4f55868b17fc0fc9e6041ec5ee5395350d9ea6a17dfdfa04ad822d6207b9fe34e2290dc6922901d2f9c1c9b5c2e6112da80b65e1895c3f26b6884f82329d9f5828a861eab38882a6c166ea3be2a7fa37311c1f9d9f5c95e4fadf1259aecf6941b9be7f2b2e3c72e0a861246986a3c60b64ff6a3180dd34270ddae143962b1441f5e50345501e3b4aaeb5c94fadd4a5148922d8f5c212debb4cf93346f99aa6c5c8689a4ccc8ccea14796efd3c6f867198a50dc7dfa74af4d74576a170758b32f2f28f30c30be30f6fbf61d96b6ef1db6e7a19fc5c5d9ab76b1b797dfcaf31662fc2db5f0b2a0e25bb8374da58cef1a97863872e8d0fd5ad8ddddddddb2310c0bb2457f869438d030870e9f0ebbbba594524a295b4ad9b2314c56e19476e19d275b69d8e3870f2019faf8f1a307d08fffff1f3d7c00f9e8f1a3878f1f3854455a3e2da023ae1bc99236402e19a887a346d9f2d102226d642b4fd903e81fe8815c34134c61ec29544022804587bee888a131a04ccc0c17279d523e95f74ad9522666460c8d91a13466860cdd213e489f20236546cb6a4891f2543667f7d0c027d3a905165ac07eefbd9a4629ddeebd97e3b8ed6a2c2c2652460d1c523e0e09e690af838b5386239d41faf5ebd7afe7d0212671e4903874c82155322dec3fcac470e4d0c1513aa64c0112bb3776f14d2c5697b8ad38a446ed7df4cf01ee681f1d3b28a53b1c4569e9b7ef2eb82307a8630580ec3976780e47c9ec61882346f504ea3f437761023c7af409d80635270bb74f0f0d58998638f82352be73ca825d9c9409df133c9fd2eea6361f11f09c53015dbf7c4e30fdf9f5fdabd719fa9d023c7ea6ffb2bf21e011e7a761cca43d484cc9dd83449418b97b90d8c9b0fcb97b6af0caddd44e1eadf26b40e76a1bd036fc355f892b7ffb9403161889649577dec19f071b9aecd7af2fe61a8af465ce9e1ab4f228770f125572e79ef6e3fd312e8a42076dae20ed0430803b00af67cacef47f8cd78ebac16ba1011e1b26695c33dc70c347a3cd50004f7e37b8f7707c7144f7fdedd0dd8482a0b47e4f6dc8865e255389545354412a2a8eaa6fe328fcf3731147d55015e4e254510955535433505505cc362ef6679b7952cd5441f32423b9006f3dab6911e0b3a165ca44a6e0ec5840ea4e8c4fc1caf3b78f7e054d34f496de795f688a30ad23d20a0b8b961625196630b9b8a001000f5a2dace1e5058c6b83780ac01643068c000610d3fe32956ca610a00a725495ccca47647952c1e426cf8651c9530641796c2646f26c26ad3c99b82693579e6f2bad69d51c7154b5419e5280422e9138a10367c8a1206e49ac2cd4733144591c65edbd2ec5fc220678bcd3633cc8512e19e555c906d4345a58b2d729af4ff2595c70f2e8c9ee450aec375ce40d88d61d8e27dc6a988279b8252159d368e178389e99f23c8e87e3f1381ecf13c172d740644aca0a813c2de45c6ec3f6936be45cad2f4fbab331e15c7214ddb8f24a2c04459e4fc116339d15d1cd4accc5f92f526077c99d0b9b8699ff52043caec4425613b4893c830b9b1aa660ffd1c5cf8f8b282d2564358d9659f2f19929173f2e7e4a3eb2e4e3e2a7e4235dfcb8f85989e5e92287403df36b78e1b1b64a27c0636dc919dee82c236686bdd1594e197a2e4a391dec172fccd252b297e75358d368c913e316c575e7072f589e5fa34963a025a80cdfea072f53ff76d09e7a7dad678912685ad57620a283fc83fd76d0acb5b56ed6dab05b460dfb3ba2eb97d183e5da41a05152805f2f878b75de1d1b1b9b200421774f0c8c90676c0681fbe6771d8d61617171c9fd22a34af153fd683a4a416b84a7e309792ee7742b68010c2f11bc4cf04ac1eb05af19f4003a8f322f1b1b1b1e0c41231fa14f3dc585029e43d078a9ab46dad5325944439dabcbd5b93a57cb0d1e3b96ac697837c553de8d7bdde61de953df5c1cac5352f24bc4267dea2386d0a7ce232a6b403636363f98e95740153ae5b9c07379aeb19379aeb19379ae86a9df62041ebd56ae1dcbc5238eea5ade42092ca7d3b2ea4f8d52813cd7d8c9ee0592c9a65ceffab3fa74898b1577aabbd0c13a9ee7e996d42f75eace80f5679e6437c5c52a338de5efbb1f3b195d499152a3bf5fea1476b0937553bab09bb2c2c2c2c2c2c2c2c2f2a495959595959595951f9148241289447a21646648cf82050b162c58b07821646658d052e7612773710440a66117e462bddecd0d47a3d168341abd103233a377bf8e418c31c618bf103233f8bd57bdb77567f3e4ee9dd7cdd32ee8bd3c9777e362bddf85decdebcbf76b0ca2a8b98b41933e75eec2f186decbfb72e7d173792f5917e428b0f2d459c3c8675921e19fb0f42f8a313195a75bd2c1c28ec7c5fa2c908083f2288a40592a61dcd595eb3b385299956fdf5405579e799a91be3ee79aa9d197bee3f9b936ac5c37d84c4dd96ba6666c039a9b6ca6445f7f0b9aa9d0d7e75e36b35950908fcfcfcf587f7072e57272dd5eb39914a86aac5cedec562b8b599f599fd3f1faf655dfd6259c925cab65e5ea55ef474b02e54cc73353b74ad991c09ea1b03ec99726c628940be668be1294e55399e222ad52da1b8886e7d28bd648fd21dd3bc218974aa2ff5014f301f0d8f1386aec6419e6a8d17379378ef226ba25b56b922b06298bb6a86b9ee45b90c65c94a5129e9605058f1d8fe7fac9f53dd74cd5297e925fa964c9533b9e3c7f8aa3641e6cf319ada0ecd227f536daf4bbcaac69b44aeeaf5ff04a25f7d729dd3765d1d64c8d5c33457ae2e3a85b77ea4fe5e95657a0ed5b7bd6cbd5348855c2b3dcfd5272d328b99f42a1af9b333ba6bf7dee4edd69118465ddc0aac445af3b41587e0bb0071264dad2b4566d6581884c5f6e2fc31c0d53251399facc54ddb9f8451f9fecdf02e2e8532dc9e05038c6c46231d93cbdcf0e0b50d5a7da3c9c6c964a18779c8c72ffc5b48d0b29b5f972b6d64c7dac790a9a294f364ffe3939a59cede5ca77b6d9783e72b2209f99e262b19f18504cc6e5e4f0e4c072b8ebda82e6c93f68f39927ff8b6db69d79f2ff7c643d77c4ba83bf98cff4037c8625fc623ff83ed54ab5ff72be9c2fe7cbc9ae7d3e2efaf7e48b5921968bf4a91af900951138abd0221fa8024e2ab64a918f552a61fcb1b24f2ab90e31e2c93c9927f36413ca101d4e3695e43ae4499e2fe973312e26953cc9a194cba15c0ee572e411104ce1581c8b63712c2a4682e816b4054121a2a3fde633e7d630559e3ba512c65d7b7cc85db0343be95b6b434e0b78bcd96a9aa669219e5f7d5bc2de8ff6af8bd8daeffbbeef35cffbf9dff785a307624db35c38e22d2cf5a9dfceab69a129076b43530e23b521901ab9522b85f6201b681aa1ef977194f655f4ddfda4eb9632a586a7648ece12ea89c022a217e267446112fb33c8afd56e60f620c7905d1ac6ad8b86d1c14ff3bf70bc51462c25c767cc67ec188eae8aa4e262108b28308b27d8be943d2e9c1c7bdd88b7fc25dd65b2ef38262de846d09d492a8eba3faff6520b721490a3e4cb2952e64b59e7f54e168eb981a6e112e8937c3c854a96e39764544082883395c39c53b4218e2130f5d0abbbbbdb033e3ede92d2a36b6181d5a4944ee379609124f285f89976038c324aae2fa1c89f2aa1481f2260194a28d247fe287ff4b27d3181e5dff8dfd75e2e176bcb7cbfbebc097d34f7937646b6325eec7b1f8d8581128a2b4614af4bfab8585fb2aacc4010212d332256fddbc31e12f0fad377e0d0e1a8ab83927c4551944660c2ade2ca2a5ce9d3303e2d518f15415604b90dcdfcda4f9a946a6019b9ffa5bee499a7fa77e3995db57171888bdd5d9273ce39e74fe9e7e7fec84086859e0d115279ead3212ed69fa02c226d7e7e64f875252c5c31cbf5e58f953fd75ff2dc8b71e91a79a1431c659f9aacdd31efaddf32440acba8b5942d1118cc32cb5c9f04458541d1c1c55a2550c5ad42aeb5bf906b047a4708a8de68e97294fd7ad382f2caa354e23872e8000587155b9c2959444d22949e650b669dd00983b85865b85865f8a98aa52cb8065e218e1a4b41ead4ecbbcd809643e53966ead4a7fab5fe560f90eb5fcd866207524aaf73ef2ec9ebeef2bbe1b7f4a0a5085d170a028f2fb908ee3eb5e0b4e0b4e0b4e0b4e084360897524a29a5045c9c52ce294306879a72b9b2d65a2b955eab54d14c991f99214c29e97c2a6a478912e6c205ae2c235b6326ae0d0a26796e5090660d3343b19b50291473c19a259bd24f298aa368e882d5345a42b1ebadf8cc54e9a7f4b3e253fa59f129fde4f92e64d0f5cb28a395e9cb8ec8a4d29bcedddaa2ee5d1abc9c43b9dd8656a63f7a3618116da3910d25e813b581e533c110e7e5a20bfa2e8ab860b930e2a9f088cb2c34323d1ab2f7e5509e2fcafddd98f986616b82a10d181609592ed27799b954717761e3327399b9cc5caad49cba33a7fd973ce75ffcf37faa1c831b7724d3a73f6e3ec8f469c83604cbf44959a03b08c16a6ba656627ea24fa9d36a47521632a5da8f2db800e6340c75993dc9f42fe8327391fe8b14b8c2d86918fa2f45c02eb3183c14c64e7d1b58f46d30e228d3a51f83a769b464faa14d68335336b0fa44ffded026531b5899be064a30a769684f7f86d16596a98b3c65b44246d0ef28d8a20496dfd4450f365ce419d29dd006912973e8fa656c2940797ea739c65d475fb680bd4ef9996808d329bfe9b52481a910b8cacf230fc79a535fc84c559e1178e7e9bb9a9c9ad73cb9e6c96a1f0d87f1878270dfd9f9dea9888ae46ba7e688eba606c7515da3e32819d6846ccdab06a726c7c5e9fdd7c72090ba93a9ffe67156dacc34ad93b213d2ddae9bde539a1a70be70f4804ffee8d79dedbd564a29a5d946257361ce3e900abe98c1b0c1b264a29cf55a92c0869820b062f25f7916bff23d332299203002c2347a961f7dcf90543355904ae60698df9dd7a283bbd5ccc5d94eaae4161ddc043cb6945726d97a73903679256db267cf2450880cb0dffb2f8ab4e3d17203cc67e1caf359c0295e79f2700165d28f42551004467fa1e01547cdf2ace6c65342fc345f64245b95733dc095a769f8d7d4b4fc343f0a0b8a67f364d62468894c09509edd4fee766c38368c4a50138d5255a1c5d928d59416e728b7ac93029dedb2d3ae375220aef3e836056d59a2e6c8ca55d3aaaaa0ce896e47d4b204ee9c582d59b9562e274beacae528bf79ad721ce58e32d190ab8993274e7c9c4071f2e3244a9e4f473b77b4131289eeeac643d18b30e017b3900b49d630cc4a251cea96886c4cbb5895886ab7a489ad15c9e8472db03c9feb680becc511b8fe4cc74fd3f09faf036865448bf3c799256ae57271067d87434160d193409a9511dff75cb9b087344141c0a2ef42456ed0904352c8c6da9a1987634391853c5fd43dac3c3fe483b4507e2815853ce4306f527a5273e4c68614b17b5b52c472b7e322c0a1ec7662b8bee7b1db097da7e445976b78e1b1a968499114e1ef5bf288eb9392c7111e372290871117c1172fecfff2225848050ef69086aac927cfe7443fd87b9113300cd68b999f78e0185d817d4489b178491fb1263e3e9ef880d2c44794175b7811e4c3c5f92c4d458e833c585760ff517c11e426bc7ae22816174d38b4e119af628ef296d50b67fe4a67b5335345422820018fa3ea1221f2fc156c05c5103a79fe6a3e468d2d250a0c1b478d2f82f29c0fa388a3288ff411c551b257c954414e5a1cf9b0998443a9a69583c776e27a317b316b8102fc6296b98ee3aacce7604d83cb7e85bfec944ad6aa2b74aaa6b585ec30c063cf30f6e1c447cc53e9ec2899824734c59229605334c9b383c5ba262e3ec91d8f8bd33dec94743b373496042bab61bc55d37a6da18dbc08725409b2ffccf9a2803f1e3a1ef61803907f081c6716853ec8ec4da1d34c6c1ce5465a475c8ef29b170f1c1e3948483f9f878ea3ec8e1247f1f058e228cb03c6a3098f98a3648864f4f3793c7194c9c36ec9d8ed744a768e90c29d07e2bad903e529ce6063ec5e2d9f516882000b102bcff23db3321a420a4d10208d9ef43d331a3d698585341a918064b3888d0d0b4846f2a4b481113453a497dfcd666af452e619c992522a649e26ac0b9a27f92b61075b79966719bb5996bf121299f170ec6a5af334f3ce54ab4ad655ea3c9cb878187154cd74e5a242e40a4b880596a58f067fb5316634f435c62ccf17d95058e3026b8ea00057af2c162c5a8488c076e529fdd1da5c6aaab645293404aed5a661bc46e183a0dc512c092a40ee28962cc932848270972f7aa7964a29ad08eca4a4e295e787c0b1a9c0c9f345e0d854e4642dd7b4641e7b26a469d0ecc9f0d1d41c71318bb9ff850c3015ae194c4ad0ecf420694cf273800d318bb9fe031e307a3a2bfd6ed4774badb4d6da76098d3f648ae406bd715d74ece23245b67c0ff9b7b8943ed0ec1f0c3160faaa29df0d4a2423193d95cdd428a441793e0d34742d49e09a16f862f6d1bca8e2e2fc1c3ca27666a81d94927a77304ff6d20d491821bb7c7f02d864f78711c026cf2830c9f3c71b9270d19065480158c3c400c3f8e8bdbc524d71ef95670b12bc62791b50c83ccd3ac59a237e9a6fc19a9b9a170d6b5a2d8050657c31cbf39dfe08a494ebbb98835432d514f1c56ca6280c1a68904196660d1f0dfe6ed5831c43be377a9863e07e7b0e8618bcb7eed9dcd406a6a0554d918d0083d8a5f34a2fa26002c8d3575d0b2560f9d24314ccf35a816ad8d5d0bd3aa2db09926e274ce4f9dd8bd773ce956ba6564295ec7a8eaa01460090b7bc704dce4c753bf3341bf5220c2d16d1a817b39ab943c54e9e54f0846167a6fec5cc7fce6e274f8a002019bab20dc79a535b0de3ae9777d334b62a4655a859c8950959c2906f830d1f8df6272f001e0f223ab512d6b8e6693e09b7608a236a5a9e9099c2e2ec0620000188e1cd8f11e3bb41b3908699dfddfd620c9d5a0957af799a4fc224c83374028388bc72b520d1edf0382a00797eb7c4532a196aec9a944e80dd433f3f1b71147ef1080e6f74a64c5ecd0487490e0d573879a5e3a2ce2a67f55ab956372ece1f40642a99281cafec050e1e7be6c49567cbbfbe62c8f0bc66690f55321960208f0194670c997bcd114fad84aaa0d01b854c6c3820e01831d28fdd2b466ca654533c86cf6c5838b2c4887556c95ecc00770d48e011868b5479e6c961b8f2fc4e068c27801b2d38531c65ca4010958cc6c583355342fa347f0a96f70cc68371942877b594caa076018f3d05aba6e528930d1799d61c30d760c58cc6d534443f7f5301564d9935aae4f950b8c0c5c42633c0f802eeafb78e40beefd5c03b4fef4229f5df2ea5f4de074057973dad7322d0aea5a14f1446a63c5afa74699840cb58b57ba218c8b3deed873c658bb26ef56a1b68803e511f1390e9026e68f11a6095a776ca3764ba439e2fed77db28885faa95b20061b11a6d29d469e7a42207769fe6977a52ba81154f5c9a5a1828d5a64846d3d0be24131333c2348456f4e5f0d92032b668ef7d2b1e58b0b4cb5f01dfbba4c5cf9b1d575b66aac40de3928a5b725d5cbc3dd0de729b7643c372d07ebea635d016f37941925610b096bb27490976a0030611e8c8808450c5d31c147185ac47276788a0d72b66864c8d1e1d967d32441254c07097bb270928950abeb97b92786243c786872ab934b6318982e409162479777777b7e2086d32c1dd5d9352b83b0a669b64c2563a4b4be06c4b2419e5ee498112722877cf122cc81b94cee557596ba55ef6424dabb5d651862735e481d212c52ed25ac3928bb4d65a2b286dad95524d033bcf92a364bf50bb8422484104aef3b7d6deeeef8aebbaefe67d110c22b37c8ee3fc391e386bad8c0cfd760073c95afce3e76ddbe4672b41d9dc5fce723bf0c079e5aeb5d65afb356ed5386badb5b6b3a173dcdc36fbd6dab7b7e4a22d65ebe1bb68efe5b6cb5d9b378e6bceab9ce5eebdefd97779bbddab352c4485ae3d09eda52ccbe766f9f3fd964d83fbbbd2564064ee6538bef84083c83cdf8273bc5d25d7fb60be9f63de8b33b65f01d06236eabee31f3d3b7e7f4d645d1cf1b95e1b337b586a98faa52758fb208e1a3b87900e340d9489f4a1bf5f49e14acca42785325cfc7e547f045e17bfffee76b7bb6dee83ccb8541a69fe424f0b29962e250ff23d89dfdd73b1d6920c5f03694b8931f7e3cd5c885dacefdb36efedae3d6f870c4fb0d411ee70b1bafb0eb23bdfa677a9e7efc168b6d43466be1bcddd7af3aa8760dba4d730f2594881c7f6294904c85cdf25878e7a350ec8ec61488ebaf093e95f4ae9f70bed41e2ea41d24a40773bca3467b8124517bf5fc60a23091c8ba3b60e097e4e2ac9de8f1268c6605a9124b43eadd5eae4f95cb660f72831cb151ce5ce9590e5ce9d3b6a9f68a067edbba17d34f7ed7703ac4bea4ecda92f9cbbb7a0f6171cbd7c7ffb0a561d7bbba7239536b9070984dc0df3adb617fdf6e5a0bd281c6b7d559cedbf1febab2e7171d68ac3fd386113067bf223256856611d79e9f034f18922a3541ce59cd464e76d6fff7e376c7de599c23a8eb23baff9c4c5267e9ab287cf74bcaf85e3f6dd73a0a9079c6928f3fc682a78df82170a1eab8df6ad72b0cf85637dab19014fd88c62538bd4397d260c8333369bb8a885138604dc595adb38348220abbccb349c3017e76c12cbf35d1c61fe38615936f819861892b022cbcf3e3f0fd1a0eb97514a57a243c293fd3bed83e531e6da0edcb4176f7cae4ff38b8b5ae8650ef420caf2bd981a3874f40022a448eb264709ecc98f94021649d2f9b70ab3fd8f63e9f2f6614ce3c30d9941cf3d80d1bef4efdebf1510dd73df85a367fbad389b93fdde29a5e5e80dc7d7e4cbe842bfd21278fcf0c5450ed81e24b1ec4f9b46e76680ec41e62dec418a743a0522d35a043cfebf0475be25114591d7ba4b49fbbd9aa7bb9c4287672a1c3390b96f707b6b41a794524a29ddc21b76e198012d744a29a594d239e79c734e1a0ab9417078a6e8cf39a7b5f6859099b11fc490faa57992411099b16110f6277d0f7b9e64b701faed90ee02f45b2279ece7381b76dfd02f9fcef4b79299338143e6537777777777774ae90b213343c3206698841333f71c76105cf782506bd7d9b4fbee9eb5d6ae697777775dd73d471f77b7ebef3ace56ae6fd3ef9f739a72e8d09443fd0e81d4c8d7454aa70deaa89489115dbc4d4cf9026a0150edbf734ef12708a47d72ad3ebdeb28973967adb5d6d973629cafd28edb34cff3bce955ea795658c1bb8115b6daebd149a715bc1b5841e79c56f06e4027142aad4a50cf0bc26ddab787035a2f124a7895d2aa84129e376d94e02ae72b2da094ab7306ca90fad144f40f637071d426696903a912d57fa6384b3d8e7a54f83e19479df8a81092ed216eeb3e1b5f087375d23aa98c73820a75525aa98c7362b688826c0f51519d947ea268039108568c131304e262d5b8805ffe31f53e06dc5a353b6d406d30e2b8ed5b7d2053cafdb001a95a7b359626a4a74225196630e5fe512be0fbfeaf062b66e60b5977e6878ba3a8aca80cd01963f11c07ad5ecd5376859d1508a7beef7f991b62c04e89f3e473878b12e8e1a2bf945d133b68ae98de04a2818b3f660bd90f8d814e4924ddac16796c5996f2819285a408db35315b544147f193856c88e4b3fdd90ffc314f366c255409f8e5ff6ab682405ca49e0c27c1fdbdfd76a040fcf443566d5d89d2f58b8b1b029edd2da329a5945a4a9f053ba594d2f0fe044b93089c129d12dcc66285e5863e4f441a618d854501fe9aa5fbedb1a36cb7792f8eb2dfea07adfbfa5823812350368cf618f486d1c251fb97aefbc13ef7f245dd77dd7ba0374c17765f0edcea039707edeb4b91f35e346b736c50c3a05d16c9deb02916853eb75fbf869d44d6499f2f2edef78894b7a681bf5b5ae495f0258db6044cebd77dba7704de9b81fc22f0fe777f5e21defdeedeab7de7faf2c5c56b8395b822a74f74ed2d3a3b5d9547648b4a9edf6ed355e84f149994096be2f364eaf0ccd91d3a6bb66837add4be89be265b261aae6a769619fc0583cec5e70923513c512477144c7247c144963b0a263f3977144c8c90bfdc51306992e57b35c83ae4b4806f4f7ac3eb62ffbdf76af76a9a9d75d2f9f5882953c3ff3ea61a0cb0e72fe28e972f12c85f2b2a044c80b913efa1c10d76f727de242850b0820a54b0a4892b6688ec400c2c30028e0ca7073633c664fa3aee4eae4c3c0e8e62c94d901ce104227878002509222178a2c7c809ba70e352c20ca53434063c93dd085dd7a283bbeb9824b087ee58d4ed4929dda5cba58ca8c48dd6453dea10cd00000001058314002028100c078442d168349c07aba23b14000c8798447c5e1acbb32486610c19638c014200040040000064a46940003675463f8a85534a1376020bbac5f64302230b8959d35335064ebdd0d425d63472d8348a07918d6ca5dffc4ac3bb4054e55c6b8156a8d031db0c43dc418007d6709d956e846a960722ba704608be2534757cb50bcfacfc928c377858558a40145459d0a6ea1206a03fe2e6e01cc204029470940944e3865d0268c3d35e851faa13806c5e2b3eaaaca9cfcdbc2abbfffa959477b58df61d91f457d0bebf587e19db226b4d04f261a922c302d9d5e24efd3c66511b29a5d103d527661379f45c007ffba73129a171a19672b77f824a2dca7df29c50d379030f00701967910cdb0fe201067730473d4575e79beb514e358691828e42dacb39c30096c7cf7f2231fe4225c588608e7f4d04f8397970e47380f246a200c1190729cdea0eb56d7b59659e9baf15076244a243ef74ad2cf0192f3eb258ffafa3e6a3df5b1b030c5e670200b9c26b5f9b950c2c0e448ac13af2bcbe99cdf269ebea212bff4aa45ce7223e1e60f91223cd918d330870fe89f13699e3ce30bf621e28e34f6d0ddda0a5012336436f751852666f7f919e32d10f67288017fec028c25105fac173e2950b2cbb009b1ba21e7cc0a4e4c22ba6c22c56d2ceee5aeee997a276201a98dd1d4700807c6b5a01869d3a8189b68f31b6a12ec70ff4b9db551df26937237403cc81dd19eaeac43faf4e2a2d28ed398e33d62301c231e7720398f89c35b322c6c33c5f3ae6114e1cc191d817f9eccc32f78dc9497210114a7b6f8ddff50d4eedac19c7ed3cd555c90fb68adda62acb142c3dba83b157533f1c32328047c7f6ba889ae65c9e524e98a727837f779d7e33356879c0895371993018f3310c9c412ecfc0d14eac67dd9a06217e21cee2589b9cde796ae8f1e254fb3c6144ab0ab40704423b01f92e8e34f19cc3e1da8b1353a0bfcb95c2025aa655c66dc29da44022be16f88d8a433868d6828033588275ba5a713a4de8632288bd8055ee2d9a50c291d80e70d4c9a25ee905e83863dba47767b471d99e3c2bc46aa44a2d45151f1d80cfb083e2d6865e528f6681a50cc994f0b1c9506f8fb828fd55018514b6ef0c884a9014f7c92b4da5a7cf69a91224e1943c80890989862a18d678ef9ac1030db2232635ed748161402c0064f200cde507ed89b18939fb9ab0e9bbf0b23d883baae833e90ba93cab02d884ccfb5b869f67792b8538cdecf01b1af880cda6b1f9541c68e59cff6c559491a0c336c01e0c32b60dfc304fefa30ef46e66ff101985998d829579cd9942526ae67a43674b2214a217a8d613baf9650bdf036eb2a6db29518a20281b732acf47637023571c459ab3bed92fefa252494e9391657b30d131965ee9b5a2d0c4193cb1767022353873d3f2edff9c678b35ece6f45ae1cc78c16c66bcd9116cdc9f7e633b21d6cb076e59220b8bfaae12f8b9f69e88927a0ec1cae7f84a8089ad99a5678dab46b16c1a159cc3a71ec054f1fbf4f7ec873b10ee3ea2e1850b10f407a7193dfb91c56a840bc490aa3da8c75a464a8414810130ab6400d768ba55f70068bcf2e66455e84f8dfe7757c740c17d69ac3233402e67df75add01b04f9f0e398e9eb17a4f3179fe1237baea32f5d254f880e8ef1105eab27efd097f20c052f055567b008e88a404cc32dde9bc004d1c16b926921dc482582533d3893d4c0779befe17fcaa5af52e9a4a362ee69c1e15de594b16e691535a2cf5c23e518043571c4f30735764d02677699069d9015db8bc8e515287a05bb5c021547ae4b9946a1c50eaedf2915c28521b7ea95201e74f79d8383e9448795941c5452b337d45ceade7725aae41efcf74119c153cd83a403186fa294d8da844dad3f84ecea0652f7ad924fc8af8dae1e93c454cc13dc5ad8e674615d9e27f0fe729060d3268456f2691980a2f345ca0ee48b62f39ad2a8546efcc7f7e39b5cb97ca3c4fca1924192f9fb8fed2994486518ca60b69dc03a897f051185d6c71df0885ee9e912fb7924f2749d2ef7f0900cf3d7a4a1e26398330f90fa7b2ee505ec46f89441216d9b9b7de1fa48420644f3dee79b009b1d2f8916b048538f033cd0b7997ed798b600fe2e44741ada1689e6bf03468304d0048004545af595d933a48d302930557586e6af1b3892f1fed99875a565cc5fb282741e1193c6434d32f1688fd9e037b31298889e709d00e4ebb030fb15e38fa7b7e7daecf1c08c276892cb20606e784f341cba83f0dafcae88ac20b2006427a939dd1f9b3c68079c0b49eeb30c5572ae8875eb332b1388ddd44798863d168a7b7ac0afe5fbb6d0c4740f51ea1d14eb2b61cd0d8207131d0c123e125c81167495b7a0617bf1b9d59ee9de7a1fcd39353c6350d3dea0d9b4994c7da0e01764d3df2794205b1578f6d90808defe60e0c4fd6cc36b1fb2c19c3538fb1e34597058f2108b45f01e2b4d82b80ef30e01203fa1df8b14ff4b164d41e4b46a782879710752820e38059fc1db2bd11435800c57d886c4116cf204cc35d2e9c8cd1d90330bcaa7b36409fb3851d35dc918242e9287e0c0dfe97b44db837582095b29589c2fbde774c057c4ed7688758f77cb534328c7b4c01d0db35521c7c8cc9abc533b9e423d7da7e82592a215a784ad3e880d772752e303973fe94f5f9e4c358edc24acb39a96dab3db664322d9440289ecc78f397d0ad843da7d46462f4c81ccb54be022b6bb95643aa0badfdb305401d70928d11b3d874e7cd63f210d61379eb510db89313be4a573624a8cb5f8b0cda3a2f3e6dd74805e33ee1f8a4d79642839347dd84509f346ea1d41eb5bd7d09300afde12427ef97905e66dabf41aa46706f8d92f89656945ddf5abf8d94b5cc788829d8ed7d5de0978b006f6882ebe1e5b17b41fcd06d19325db6d44cfd94733acd86eee3532ee037fb84794254662c0629a3d7d8cd298a22a63498a0d7243a9ef9fc4465c926145ace6dd6663cad91a2a7b137486bd2b7fbbf3bd044b2012b09e962eb47fb1b548207ae7084ab542b6444cf07f5c6c16ab5ff9c40610bb4509541ae2190f0bfc9086da3b4e60ffa6dc41cc22669070f9abf8286edd3aada10a6ece2c1fce798296b822e460cccfdaa5f57d6c57f7e6049cc582ff13a17a5474d57cb76ec836cb35f963d496433ef6537b87297f56a94bd30318a54b7242bfb1bb54d430fc04ce563e447bac528454bba73a8980d5a62a85e909248404a9d9c4dadf0c0b93ff023b5169614cc6436d8f48f0fadb520998c68787b282a56f56c25c29bc34e6fa7675f31c5449bd028050021a5dc23813344b0f9bc6b3637d9139ef6a66f5d5db4945182fe50ebb76d48c189538b5083cab66b3611e83fe81aa37755c09b3ef021cf1d10843c88a761645a6d1ce24c9157ac58b49a340a4f61f5aeeee5641e2b3a972c91ba1cb50bcf27c6d8aba6e1413b5303db4a8ec30aef6e2135c704c35a1e17f406f85b782bace92ad5b663053380c7ecb269c3b5b9e4f2d7c3ef99203844a5f40a5531485d1f116d88ae6010548400864c7cfd89a5e6912deb1cd5041674012f7121ad63922dc7f00b6b762eb465c3856febf2a35c783bce389dc27554a0ca1548b207f7060f4fbb6f4cba666f2c1d68957f3efc95ec71785640bbbcebdfa4d8030ea0418c59cbe0e8843ef14072eb7d5307d54468626cc312873234456b0fa53ef85f3371a32c9cc732d497dbaa37e0d45cea3979bd1629ff1fde2a715451a1260f8122a62c80a7d0ee33a781dacaefb6804f84ec35293140abd055473ebfe7fc8afde1b2dff05547f080ac3430eee28608d0cfa6b475f360426734d06cdd24e6ec678fef198d978e0461fca209bd3b318db116346ccc472bbf15be81501b6c077b063b426e93ac0b7b52ed0d48516716e652d984879b51e6b7cc426703bcb2d029155daa7fb1abe32ca8a86cc02511294ff1d7aed718392d8a9e5cc40e081d6a71e8088882ef3012d504e6e1505ff8b4aea8e23247fafc35607ec6b7eed6c387387131fb0365cd920369f5a8f59f9281534549794e938977e9aae8c177950ced45c84154913c0869f9229d7b5e559aaf1106cc2696fefc39bd826544d8cfa440976591226bd48d574f3e70981a64b1870e0ae153889b9cb96b650fa3bb7a7a4c737b156f8ed8798d7a7d4003b701aa90773e4aa6ac50f832aa407ad43b3032513a6ace5c53dc899e071a79bfa1214e2bb896d06c6ab5a4c078ec55e137cc0a3193864cde6f5092b73f511a65c06183d20368b9e682867a041e116436d1ec81364d622d7e3b637553bb7e06c799360af4ae2e027d891dbf185300c1ed1253ffe39b9405af750a5659b10e8ea0eafe6cff4cf63aac0d9c540711e42c59e3ddf6a630b28392c1b720cd5827fdb8ec24be15393222c3d68289ad8b620fdc1032a0e6976d55b7ac35aa5243bc87b263e1f6154e045373f3191b140f022372953e30fabc9c6c10628a6159bdd7445530e365de938b36f358cd26df9b33ee651862927015333d91405ec4449470450b8032f91c16ad132772a2b607b7215efca1bc1128d57374ded1ea037b4b69c84a4c7223da5a6454a080961c47c7c3e324c71d4ee3aefd35e114bfb82d70b8770f4f4493e64f4a9901d9d6cc66e45d4ffed9f091e176b5e0c779cecafaa541e2f9ea725d98ffe401e50f6595cb4bd6b6795f99b1996d7ec4a8fec6856730885b4268efeae34b91c4b3fa6a6889d0c1d5e2e94821c5c8c0c93a77af93c3af969fb52f668c9ebe7cc238ec9bb5f8a0ac23a0cf9f4b9b4d1d09783cf790bc69eb08040a2f21a04dba6cf3b52e08aeeeb7d7ea0351df441b9f9b71eb0c27da18ae15117c7b116501e7764c1ecd133dde82e33944cf9e637768a528d4a44fbf2a135a9a650c43555ff91b10636eb6909ea3853f9b6d8a299bc2c2c8130581c8d149647324f5e9f6a9a83816db3f9b63f0a0e506c1123a293568b885a9e997f0d56a63f8d9211922e1a9342ce5340b19d0d2bdec37e694771c8f411d18cbbeb4e9ec100645af50c631df49f3c09286a7bdaecc9ee62daedaa89b77e119d926734e94e339a05495ccec4e05179a8956fac93d0d2b7bb29c978a77b0e0f6718c0e1c97498c7c17358b4c650bdb0da66761624e7dea9472da65998efa82982e8a73a915ae42ffb3dc8b18e89c255c9158b699f1e30e95c6728d798ee4fe13ff8fa389c3c6a8ba3e5711e5c6e90d1ef9bc67d0ab41d32de1775d4af4729581a9808dd02460d09bca72faead2112a864d803539fcf5759adafe3ef7c3520f94fd3960c42bcce5bc1c63208140c5cc2d24080b69d838f40826b4720c046e94b1f24cab42225938f194cb0a0835b045f896f0382864783dd1b745a6b339a8e759c7398ce16ad235b53a51d7bc1ca7100e132451da89e80847070703fe575ee04daf37a59221145a0d803a3d52af30a665bae86bcb7401092d9a89eff2d68fe408a2d5f1d72c8b1d942f9c348813ca14c091cbef87504912cb09c2f54788cf2fdeae4026713bf7bfcf6dfe2a17b1ec03685612d1186ee7802f3cc69747aac8b71c2de6f5d3596dceb779d9b06fa5fcfed0cc425eb3600a9ad7c7dfbc6b5d8d0717621b7b9237401bd4ae933344eb9e6a6f15f655e67c8a53e6759561eecee8a3e3e3f89a1ca9d5ec98f2214a4a460fd4803a6f2e0a79949155761d19cfee75e96cfb6e5fd49ac8f06e6a7b1ea85a90ca6fb5ecbcb0f3dbf4d6607040213007be9c868d081ed6b62ee0c54d60a2d7bd7e48cb1189a15132aa89431dcc0288cb5ea3db1024741dd8e8d728d5f3f0a3828240149fa27bd494acaa170ebbe0dae786e43b14f6eacc45e271f9f892868e736e1f9a47e9fb82e11a5cdbb754c2d4afaa05653008c87fc4983c30002807e0019011b89666841278a377666b1bc528625545b9dd010845e13d366623fa7933846ffad53a271c167e75a6e122c0f4b02d1703d1ab78ce41c0d067661b5952076a050c96d66033e408569dfd0f61e87f61176f43f865ccd8dd00fe890f418bfc10d516ee79661e8d0cc5d807c2b620743b7f0e70ee9e1afd590fbfce16c770b432f3aaa819174aefd36d4c6a8b8f4ceda85485a9604de7db82a9d1b4d67cf84726adfe04e560a869ae715a91bc8047d060229d9cef73f3acfb67fc5b73ec7d03eef9499dc7a92275f022f826e0146f4cdf782671fd8a0c48070851f68c5a1d1281a4874c12537158b90d27a2c60281ff7ddcfe5ad7f8980387f920add43c2251e829d3f0996f8ecc24c42da7c4b1fb1f0889997125befc33c147e7bc2b4b970add2190ac789d1a29e2cb279011e6a908574583e0a5b9350782c30095f1e48e39fef885012d4f57c0c88f63f6b7dea95a8e5af99b5fa244d190b4c58966b1e3ebb54273bc7bacc6b8b138fdf4947aacea48de9f1d4fbee23d3530aecc358ab93c74b9a4b30d39d58c719b9e2fcba07c5cc99adc8e619535a1274bbe1fc985636aa34bfa83d4df480fd57ba8e731f0bc820bbddb61bc0b627b3ce5fe9e6a441b7b7fa2ff0f740839a73a9104f33b6bea04040cd8fa016c4784a1e95e1f1ba887ccaab09adbc77356e857b3891845ef27e7b056e5b2100a2807b9b146bbae0c101b8cd9a1fbbbed9c85a847a8c7fcf500351f0caab74d7a13c1c4bb419d14753069b389dac23a26723573c49c8826da3b46821147edd0cc03f5c68d471936736e0b6ea382cf700584c63b3c0b54b66c03442c7fcd0a91d93fe9e003dc84d33625b8d81564b36ddcebe65eb805a0379baa5e467bff9107750b860d26397f7a858575e1d500bfa0df25d60667e9105ecb36825a6f1f0613973757c16f3f3e1ecf8d124a067360c8d1a1f03f5ec84291b96f0d277e6b7bd89cb6689ced78f4c3b55ac7a958ae28b2a300a2a4e4cdfbdd9fe2ce90d99ab38f0a00186576fb5c2374812e40c1ee6e252c31624e4c1b22fd5595eb6ef9c7322eb3ab6fe56a2783ec4d06c7f591c24517dc2af791b1c433daef5841c485fd8c76abd55fa599f573b96a2906b13ce3f3750746bef12c4f493b97481d59d4fe66081254af4a06c939d5d63937392b94c2f0b3d43a3cd1897f48914ec432c40ee22dd6e2fc248a7f6bb9f1c40da145d2d1fc303fdfd27e8120942bd6151964b97f8ed88944a176d574b900f3bd79e3dfb0fac16f1670512c8b24c05be3258bc2de86193d7b674a981c0994ed487069b18c0467d33f146316f5b9366dbf1443866bfb89c1a64542eda48fa0244157b390be064d1d0c9f3d99a7836d21a620e9e27563c410b883258aa1e8673419789e787dfe84fff295fc248b51c37750b4bb73b02486ce4cb21e1d0e1d12b295de82e0a58ca0d5bcff667624562c64fd8fcaea72cf9ab994431a5686486ccf30f699e43e0b25c32f1b0de857835eff39ed34c082902bdb5efaecd68d0c4d07e904653c5e589378ad10207d8160695ea35f352c130c0335a2108a901a279c09c99e8d46607543cc0f3e497318d2f1848f2506432107a4f0da0aa80a386b00f1e99fa278057840040c45febfeeeb4f594f4f715b6f227e6db8b5d154639937816a0300e0047173fcaeb124976feb8fbfb6a51c29970a199c0ef4651c73e59ed80fabe735aefeb2d0c909d02f29a5c01d153f5003a103e82d377226bd489daca4201a406fa115752d19ba8454b0deb4740b6387be2d343305d3ae83f5e255c64a2a60d5259d78fe6fd10239a93ec4f9294265c89072b6613d368dc4ebb22ad60eb4d5aacaf580d65d742f6b90f62c818b5ec32444b4234dd4f0ee7f9fc342ca8d70ae05600a76e3037bf04244d027c0422bd249091b70e1d9cb2dd5271a3ad23b27d8d3b6b44488395ebdaca8f96ea79bf08cfd6772cd8768b77333733760edd3c6c7a5acecdf6d6cf97b2b8c60e9c34b5fe207ca9676af9fad38307dc04ea49476423d966556a8a11690e107abf446b8d0cf7e5bda7274e36b10d8030706969ef8751f6c77d97b52fbdaaa61d9c1ae8f01f6b91cf419a2b2dba291282788e10753b339080f3c0efa2bbcf904a3c2914b1d1b80ced23baa96bc47552e9151f7fd345e1b3244736f3f11e4b833ff142bf630f0a58d946d74742fe01e6dcbadd14ddd541d5c35214c88753f4417e47f15fa4cf1a5aa31e550fd96a13813b630cc06505360061b315550b05dc08d3f30e8e28970665fd443faf11b0581767a13d73b5327bcb77b7cd3ec9888a7bc09be81f434bcd1613b93608fe9722a5a7b41b7ca74786447d7ee5090d6d2b7370decd7659582cb0e8debf804cd38f6e7329de7b3f085044336c83a8db6811b1ed497b79b6f5d12a2321ef19f4b2fe4f944047873a0b509869137ecb0cd9f392d38257119ec66fb7be393c2e304f0a5d1fc5a2a3d128b97b7574c6f7be5c90b864560a041e02a93e911d0992b7cb6895cbb72de5f57ed23c104627a0fbd268c1d764ab08953c033dec4501f3d4c909b4a04fe16d15380315b99c596114da5d13d20982f6828f0b95fd5859a8a3f160cdc58d3349b6c03d07d632521ccc9a8108916a460bd3797e2823b47f0b1f1938be093be260a2a2a6cb83026e54655f205a268be44acb5c43eff8230aba4dc46f0e3208327d8759d7706430d84f8630519c15cf627073448e2d54bac4ee16c0cb091e80b8c439809bec0d0e3ef9e818cdc81acdb22ea51fc35297496ff41d94cd2e6c4a8ad226a691deba108b1d23e15c92b932862c3bde66b2ad3e348e17829e00a15f523a7c7a7ee1948222b0615756f666dd33c48c2cc9130e18a3b9bf8f0bc333deb514d165a53f6272a7f305eaf33da4f0cc8bf8df33d87e4196d20e77673f99b5b24f015f5af94b54f8d26ba32b63372c84e3f183b68af4857900fb99c3e69659331e725aef35a343a253097cfb15959759af61116061c98fb77be3723f858f90c4038abaf2aaa0fc1a0eb5862a4db1a0c01442308e63f9c472e3ae0556a70b61c9f2c8c8ba33592dda60e52085337a1393ce32d87927bddbc5040d671e0daaaf897faec7c60c87f26205609db12767684763d382bd909cb9abd769d0bf17bc6e198ea323580a999b580775a3d90b8501d0bf42c907471a01dc2b1080408f0f81673c7a04705f7038cc5ee1c9204e71c5c84df0ac99c399e01dbf9582239423be39e33951f1ca7259a543ae36d6b374fa6d154e4bc6f9f00f27e7db51b76a09a51801f387f89d2fa3ca4970b9f4475c83a1b0d8f56bca23b5d25a25ff83aba024e0d1125621732536fb55d113ed022eaa60c7ea985e2eb5150216fd6ed3389bf89ce65e0700c7581062a608c69e2c991667313972baf701c40d294125217359573c7924bed5204aa70090e2fa5a77e32c80912977d6a2731c905edc25da89ceff76d95d26e8acbdc97585a8a1bd17dcbfa6fc203795ebefc64b74c3ed2053ed632e2f50496482d85ccdb103cde64052bdf939f0d119c15bfbb860f7807def5fb506df14fdf9cf2d66727440129939c7e13253fd047d9d97028f6666ac0896de83bd487ff235413ddaa3036c1aac3ce52c6616a39b2a3566f76eea205445a8137ddc14c99d0e1b2727dafe9ff0d90e178693d041ea712ef4828cb2a4c37de6aa45eddfb152ee33cda5847bdf77e2040758d4b69f0bf5a9fde2ed5c82b3854d9e39dc0fc94b96e239d6dd993b2efaad18d3d1a5daca502b913b47c73b112d72dee0accca7c5677b4e68a9eb96d3dee3679b5007a9efcc690f5ea689a69f0396473960eb400eb5eea6418311928db5ad5ce29f8bc852d6d173b0418f3162d5882c8b4f042497f11edcf3742d3745c9351a4143620e40a3d3c6ce4414f76a881f19c5ef5dbf0f2350a73ea5bbb85163518669734828c0ce85750d0d98d34e43a607e5db7f384be6f244d031ea91887d2a285b161270a63f0470ba117187436cec5901a28bd3d18821b4a0f24dfcaff6cf4eaac9c898c6493e27ba2ee6f3ad6581dea7a2dc10622c455d2554c87710e076107fb064e47ab1ec002a42004e150b7f04a0a9605d087b914e13fecc567a4e68108516536c238753be5583b6a659b8d24c8b524883345fa06e12d97f144acbf07b9fc7a970603cf9b5d589a8212040d1d0191137c324834335da8f0314f109482fca4478b74bacbf97781407c7b02fd9a8e27c3c68437238c162256c4b8dce6abff53dcf36585a4905716cbb03fb61839382a0cf0794e921a883561273c1a689ddb1102793978efd4b7e70d4e68ff43bace91094ab809bc376fe29a711fecfb9b38effa6ed45f6acdf61e820fcf67ca4a54d17db74d05b04251f32f628fbebae3c61c0e5b386dbcc4fe5e07ca1c837207ca5e29af9c5fdb2c97d4a0cc5470fb8b9667ed43c954ca8657ebd7d3d6ea9fc10e9268ece38d42b955f11820719bc2e451ed8623bf1839dea63c7242d50eb058290a62b95dfc01ab0c6f7a724b4db8ebb21da5134bd6367a8841e11739f97436cbfd32f634580e055202a9c8e2e63514097d31c7b12e84d1011f58f157f139e0474e8512512255e3eb779ddacf86e167976cea4c7fa4945beccd92facf7fc6f4db97479356e4a89ef23afb79a827dd4dd13f8e20fa42f87b7581865c6fe639d6edc5ca3be77688d7400125a77bb20623e4acd666a9f25d936c2cce33b00b4b170c570e2faf7939337363ba39925acf53efd23fd94744850532ad1f0fd5f5d2c3135527e2e8c5c2be441465445a30edf00eadda8dfa6d32192b69b04160391426be38648e4e2b703daf71614557a8ccc3eb8abe4a33ee693349e06587b701163fd71cce5c27cfcee3c32e1d905962440a28062eb7044da9fe9a28fa05d39ab1b82c9e7b38b98fc80816537250dbf0adcb4e1bcba4ae263a8d44e16077a7f3d55536cf1fd956dc81fc2e7e81026acc048a4f661a69b8756c3795de268895a00403bdd21ac4ffdc66964702f62bea92a336d637adf20979cdb5cb8bc2fc2d64f4522098fe4306905e8b80053bce4eb9d0c368a33812541cc0c13f89063b4c7299e48f903817f5df27e2f4fca22f919973189d9b5963714e00a328b5eb16e29cbef6fbf6f61da8e9127b723c8fc0053b8e700f8040809deba0c4e15f527de5eee398fc0e6f9ee5e423ecfc924b7d5f54f6bf793c68e79e86d4047fbf5bec3fd869ddc438b404f3596709fac138b64170c3781e34f1c3d649c417d66abe94efb9490e00a23fad9faecb6e29c4d46b022d05d57622a2d8a14374536167b095714c9c4df010e4b94af79d5b42894d006ef75d80209042b960eb66182814581f8e612342b2e196fdadfe07131fcadbe21910285c2ee58100f19811655e066ccae50e3b05db69675bfcd36d3163a8a086de6d726b3c85836d3d3e5a87a73b003459d1be5ca2c22b2f07d912b2c2793bf069707a1972f24ff49e5b4cd53ba34a7b3f99d80de59b2a35f05b0a2f5a94798f630cdb26caba57477cf0e9f224c726dae79a0cac329579f77a8e8f5974bf1c953c0bdb3cf821dc2fae0a430be1288d632d2214def5dba4a4720f096d3b847a74900efa4550832e8457de05b43094c8adaf324f464666f7f8e27f1ac9fe817d6017ffa24398c5b0d2fb83fb4409d0e1810f1bbdd7cd5a018a5065c3960320ea3ad98ac7fc6958e1a8687cfa0fc04c82b24728749d038d7b2a33cf124ab66e3674d446306849628d8c616ab927bacb0041a5129a3dda8799812741b3e30b47b6a8ef69219de0964abb86c9895437f325ba7c0d4cae23c673adb70ba4e3de2c2f06e74732b3522084371721a71a808f938f033c2801cffb05eb1f9d788414837b44c541f3c712e77a16af0004fcae18646bc047b887e0f9c0c5244e715273cab7316c9d2385ecc8b915d72546262142324c517464cbb3254ee762602a58225cf38d8daa96ad4b7b5439cf9a5c307404c00fbd4616320f31f5ee5242b1a8598932a689e5c92231a6f28d5088e112e77e3861bee69a5d4567c3f0500a47d207317131d3b121ba92c523b30533e8d9d8a4b684bc8b5b0a6c3d448216cd1cdcd93b13e85bf4c6cf4cebe04464764e71e7a7fbbdce2280f1d103d1d9691142fa42436ca7281326ea5182eb168e78c83918187e1807d4261cad0b75d6ad189836508349d592c7dfba2c5c5b8c2d465a4011576d563c9f587e150ddf2be635249502cc9bb0c714e14ac608769583ca1e368bbf6d30c01b88dd1f0444161e3bd16b1d63774ad8a2543a206adcce035cbe45c0de063780dbee931d9d59482e30d0b077fdf1b20c36b718ff669bde807cdc6a3d0d0634c45452ac4bf9b5e43390ca945e16e7c1ef837a97a8de0c531c5da538d80065b58feb4fa889b0efd0af013300d28e4d60aca7e0db857c95d156b79cf3c03880738232f61daa6f206d97aae23d25b429eab8b3fd6571dca8193f0a7c18851834d51b5f83ef1e8cba89304d2bc724f751dae3c22aafa4474a3eb17a7811e6e5435a789d943f12ecfe5f29011ab8587a6ef07879ee973f5e7cf0c41638b1b2bc3218ddbc6fe877e2bd5a5195940e0519a916593598669c09716027c8ede89e96f9a45253091c5e57879b4fdf5870129352f9cafdabac7c8b8bc11c704afe51a9ef902777c565c684cd8ef19a75cc2f1902b77d0a36bbf1f8bdd0005c839f4be527cd5dfa2b1b2ccd2fea728f5ec98699aa1c9c77196b87d9bff8497ddf4440839ecdfda391738e0e611c03652f038a5c959401601e4c221c4bd56201ec1fddcb2342d71077d5c43d84f8bdcfda7b69940d86415c7d17e4ca2af9aec6b0b3562aec09ec1482cdd1bdec74b7b8cfbec7d86ab95de731c96c799bfea842cad706f3458d185688777bc04510bb72ccf3ff703a1e3fe055e63c27542a2d9608ec344d5eed1d33e473e4c4e51d1402a90644aa5dafe0dbb10dfb4047f518a3144a01d67f2720cf037bd9d7f24d57638635f7b9aa7815af6d5dec89a6f54ad4eccb2884916263b87cad94ed6d7e934313b178e0a1b7b5af36b5268b4072be4de66b46c99c610b7b881023c73e115ad7a5a9f03edf4bf25eb9a49de58e824f6d11cfc5fcfdca567964e0364a5a328da65d4e8e6c1ceebeebb7df44a1cbed0c46a72d60e04f558626286c995210e1159b74f29e103b1c42225eee2a1e0b756f4d210c4b9c920bc07a0746e09b37320253e075754d8aa59b9bca0020c56c1631f6d9fec53256a63a14409d06ffe15bd567be012d657d6a9fca8d2f184474f512a38c9b81ebebbebdf27b3980d5ca66901865ae24ddcf7aca14358da06dce2f377f11a0ed9a550b546f479ed980b3967c3eb33cd02593dfd8deb814818dcab27b52a2273075cd8a06134259fbac0f75e8522e1dd16a63fd10c67170190016d6f2f012073b88abaffe3aaa2b22cb5963895a4e71c8d70ba00c50a753d8047759ced33965951785d6854f8c2dca5c7ec96e0e9b18e0cb0b41742cc6bd2db651a0218d25339d13d0457822a78fdc3b94aca5bac2ba296eac7c697f2c52688489b7352f69d0f4250fcf43c72dc55e5bdc5805183b13e4c9981fdb2ce0c92feffc00124f9e31a4e316fc3bce35331a961fc3d6892851e3d0ffc40de26cb809bef5e8d95ae48c005452a55d20e7ee94425eb246f203c98a6e4702ee413f19ce53d3f7b95d703d6dda7659ee8352d33b8e0190371cab5d026dbbd4093ddd28723371a3d6ea4ad4fb91c8b784060832a1e651c3517603d230c487b10a8726391fac79a232c8d72787477e3cc73ccef33fae5639bd99fb66800918ddf3e64804a0eaae11ece10c82381f37ca3c1bbbd0f00e8c494005a11691a4e9a288da618c5f2df303318800c2d65d3882c2e6acf642907ef9e7b44133968a097695fbf52effdba76db8e01bc90181a2fa5efdc2f737be6cc2550b90bc487d99572a75c7921622bb00f188c3524d4411d65f426971247d58444f141d7b76e81c4c666ad0c7fc762f047857844c0b232dd9df6969b44620d491452be1136e463659d0cae9c208cb0ae9ced4b1801c6cabc2c948b0362621120e1fea3edcbfdbd71f458fdb08cf614bec142108311b7d5709714f3cc3012dc2992ffd609cccbbb07464f31acf9400b66c549b02e8fa9315f8f0fdb42c9f2760b4837da24b7b3b6d64c50b21853b13c677704ae4d84ab985b28b776b16663c8ab8c75e6007eff6cc47a843f9744a95bb56f8bec636c15e64aa50924fa0ea3373ecea89c182dbd8c2596506c0d71e5b7d35beb68c910e298ca47ae3d57b17ebd1dda4971e9ed0005c77d370b21cc42bff06124e54475dc4b6b452a7b3c969f382d982d5e4db948eb302cb461a293ad7e3bcf78cd14fe93c5c95eb07d5cb60a949cd2e3375ffd01895febe127e8165b8b63f7ae5f1a5fda2313cff8645ca68f729fa434f74a80aabb327e05f4995c4acc10f1ba3873f64110ab713a4977a2dbea3579b9904115f2aba42e70362bbb7ac575fabf01ccdb6e40431ee1358bc7bfc31d5d226e7666ae423190a86cf464b39996ed626bdfb2af82a3b52ac5bd05e11c994dc7b8ef84103afe75ea24c181a7392e65f3feb6aaf315ee59a32ff5e6a91ab3ba11d061632f372efe9362148d9bfba7de702a2e73fe645dbd0c6bc161355c1a634ad888c0b2239620f81ff02310c0e76e6e449547ee8f0bcfb952b63d2e3cea8b83013206dc7cebacd9bf42a739104514765bc9e093d7fff538cd7b168f4cafbb88936c5d8fb3d063f3303ef01ca2378706af5c917e2dd59f80358c63e97b3eeface233ac5852348ef74e9834be11c398f367a80789117a7e3e0c7c5f57c4e14fb8964e5bee19d05e5423d96ee2f8152957d1fd706911e4919c80fab5cea3ca85b4f14f2a53d7c9f5612d9ed8027363d63660a4c15fe12e571af4e135be5fd8254ebc62924fb970aaf13505161df839730744b0cca4a770874054a728e0462f5a23c990db3e44877831e750a41fca4f85770c05dd4c23cfdeb04be181c78ddb1f0fa9c534eb74e0b122be0976d2ca8f14f10db163a1263eb9fb0ca0327f41bb723dc400be5d27c4329f4a28d95e47fff07aede851ff90946d69db907bf01a80b9e1cc153cf7919316b81657162608cdf2858f002486e201a73b0605b2731a0032fef1c2941d180749bb7b35112e9b38c2a7c013dc61918367647120b9d7d27799fdad56f78f0423df5d66be53a9b2aec1243e1e5422842ec17addaf50648f1fa1d04ab331fa5166249b8656cd104db715a0bb116bccb9d96225e8d8698c4eb70b37ffb5d796f3a7b1f246754cc0f93bf8e90b3ae37706157569487af20ab829a62634ee89de5e655dea78f303bb555fb700478e775e32e8df7792ad95222b1c23c3f3d0c30b9192dc5f1761f88e58b31858a0b6b5bfcef9577208a0c1a1b8ef89240b51b390e3dcd525148fc2023e508aba2d6ceef1906558d0d7d005b0d6c3fff262d1474b2f87b20543177dc145573ecaec7bfd5ecde4a6468a1c67598f77ef4e8ab16a1cc77ccebefe0546c5c8fee8c472342dec338acc67640f04995666cbba8f92ea4150d37a7b4eab1092f547482969d07857b2098e3df91e2c439aba056248c301760a765ec99ce8a3d1b2c02423cbd257f61331cbd087bb2e2e5636285d040a84b3ec28aa1683d15aa7c958d9d476e87c848d9980cc15095474a8ec0aca073314407a75675d4dce116b259d3776a4ea5a366d60238ad4712adba1361e7bb0c42f09f508548e295296d899d3c096d2f5f0a0bcd4213fa158e1f5bc3b8175b2b94398435803a00ba30024b9dc96d6ce420151bc7e54e8526e819a467d678717503e125197c1f267d907649277b990eafc8a7634a94e3c66d1935ed0df9c794a7e9ec31956266997ac259b36c361b4d44e915878dd8bc549f3e76fb18a9e521816ffda9423ee93bda87dc20cc2070e00a13ecf124026000ae15204f2c204b02d5a657c134913734cf91814ecce6410e60e853e4bf25560a351be439b090d5b0fe54ca5da6cf2d79a4d601b53e0f5361df3ae663805a9bb923e393bde61c122b23218da3c3b35c644305b5d1554499ac34fc55e21c909740f524ad7bfd8aa1562e76e250a90745e25983622a88c6081c4aac3041c823a7a40354b601ba246bb73044522d8e41f59cf0019653fd70452afb77e9795d3a8ef7b07c8b87caf6f4ce04cca95803ceb2b77b36df5de08283305c92c818abdfce8724d4759af11688bab8d596356068e4990830112ddbc71f351dde95268734b59e1781fe75237a6a0862dd5b0eae72309e2e573556ae80bb86f431ccbb798866072a9ce8e6bf860b7cbee809f614957d5a13d05d481cfb23d5dc5149ab7d7fac3c61ba288882dd487b4066b97c0a672103bd223811388309ecb49258e94278b7c404cbee3ca01bc41d021171f4d691c58d2070377067a131da06bc383f9085268efa26f9005771bf51bdb9018b238b4b95943abd29ca0f9f6b3feccdc5f035bb48566cc02c7d89dae5bf2c7a164a0e1f0dfd9a070fdc4219c9c2434a1a4be119afd705bd0acd474bde080d192b75d5b6349a1c9d5df005bbccd77b529d3928bc9ff8bb932b7c8248544a037471cc5873296921e4a5d20ceb430b2c662beb1d254db1274105ce20ca83498f24c6d1989c2f82bcb36e4c510c6b09abefdc36b0a9162e251aa0e1b5c4962ceb8c91bbfd5f0d42f3b42cf72ad89efed35fba5ff9901aba2b0f6f046a5f973d73c8482964ed4baf10a87eb5a1a086dc056fa455de531276aa3a55796f7599ac38187533e97e8e085ae495fd66ee332d7f7b5c48e45509d767ed4f015abb9c9ec3d76d0da9057222617ee0891720b01550e2e7f8c1d24c4785fcc95f4a3f1dd1132f6cb2441c2135b085d390d8b73148ce8c8ac94a058bed74f9c27355f615e63d6eecdae7b810f031be427d567b78535cbf00dfeb9f188b3b51a8a95a53111bcf2b5497816dd85639bbc395c44eaa118efb62fba3ec12bdf41fbc24b0f32ecde08e9bebf8bc1c30fec1802380768d76e91a2f4783862d652f8d7df3ef040fd69a525b9f5d06c184db09e35885c5fac6adae758da667e73d0293a2855e24c229c9b78448e5eb81ea84e0155c3ea065fa9c464309093b2233041cab9f3f1602eeac3d27744ba0c05fbe493b6b3d8590183e5cfe477b4497480e35b757c8263ec317f48bebfd273cabba69651f08ed47f38adf2d09faf20623753a60136d1b5e031be56594206ee818a87f78a13ef45e61b54fc958f0504e47cbdbebbc8b6db46f16209f08abf41be8d61f07f05f37280a23a3a8728253b8c3daac840354c8b7bd0fca22e05506882130dd83e6753d19a48356b42a728dd3291ceefc8a2220503cd8883a31ecb0a8684e490b15b2ab2513c451cc672a636968ade4a605136176227774a1f8b9f89279b666d8b1435891c28db28c2d48bd6147358184338cacbdfb2a1e2d675402f5e632abf501ce9dbb59e5d9006cdd31709065f5c253e155713ae23f03604a75f06f18fd41c0b57d357484cde8411bad657becbe737126d60d80c1cb41f76803ecd20657607f43017907c005f39f19dba1086b26a05119d2ecbc98d79de29ccf707b45b51fcbee27542c0faa72dad4b8ebd9453c21c509806ba2c20edab7f345b6fd045ae4e5fa5c945fee8e661857cbbf6d21568afc0bb071ca785605ff55db68fe061770f5f6a2a90da105c9f4d4c05ed6dcfa6066636f9d111eeecae7e6db2ed73be5906ca541d286fc7eb41bc47b1b997d9bec8abde86cbb1c9c2d33fae4569811f0df273af5ba54c2fce46b24a5f2293ed9de3a83961150c900b5171e40355a3b9289c10dfddcd7484fa2f9d821604b1aafbe658e0cd9cb7099a701cd3ba7469feb72834c102e73c2856a87105f540d2205fbb0755268fdaa1cd73abc9dcf10fab1761da22414a139fe00d822dc0c086f84f758ecc5a0d95deee54b2e7bb38d2de66cd45463c6942c487174cbb884bce2e371970e463684325296eae6fc584ba68e33d1a80a1bbbdb0b2d47d10719ca5650f9bef314146c8f70c0732309bb3af3ed3d579dd03f910055bd75d58901fd12e2e0b016cb1730b4fad1f192a99b0d4b518ae0b7cca1d3e5bef8c394a71ee1de5a11f579302296922d03b3c16a90435600130d59102f2505222c574a6ad9359ffbfbb6402a1c8baa30b87becb11c45da1a5533556a6a2cb0bd894debce724fa06a5b57a32e3d97ec6a7b3240511c0cb781397b274266dc1464cbb2619d5feea9d93161e12cb64e6983d9d0e5dcb3b467af42e65213d056bf9c979ba2f8569e75351658cba915e30f6967ae7537798bdddedd65e0c0dc5cbf7781b6babf0bec79f508c8cc4a70d86b975e77635d0670ef7b320bb361339a1a5f5ad33d2aefec89e6ed3c7864eb94cdc364cb5e09520008f9ead5809e797b0fd65298165849c9efd8e11564672e73b0e351642f6133669cf8dd090a639aab600e9e74b3169a6ac2128219f6e97c89f47af520616b971be997695b736949087d689b0d79b8cad709c7c62ee0cd9de85ad4fd9b14719ef769dd82d9282e34c27f42647b639e1c1276cf815f672d3ce5b00e1ce2762bf93ab2c036e18ad84ea553b0cc2574133566a847617a1fb528391385c541eba4f77abf4772206241762bfff40185c5a8ae9fd9915e15a35c5a5414168ceb852a28f605874ddd11a5c389527639a385b12594be26b61b0df7c29f95286832905209e35ece62b5abf6f5d7cc233c0e0118fe6ce5eeb5bffdbb118e0def1ad102d3cd3a1fa58ae65421f50e50342a5f9c4deb487f9d3f9be68dc346ad24af41394400e54f7a5ce656535e5632ce9e5cdc209a69bc505dd13dbcb238a91133be43ac206d4a3d65d42a1d87ca6e9ba6ac037d695cdc31fbc9de5fb166c8ca205d681e8c3c088b494d4227fb446288c001475770187f95532fafe3f051829d120fc278746fc0da993927047e232ea023ed657ac3f2fbd52756c79960011b188204607027b6c63b88d51ca3734b22868f82dd5c6cf60f6bcb8dd204e172a6f9fe1473b2e08a62ad8e20bd6e492a9598dcaae8d6a940fd6ca3ef39aea4422ddcdec10ce0a2015015deef1c6d68df89b2f3bf281c412ef9679f0f6aef7dacc2d1b146059885004a5aef64241db8322ddad09964411555728760d0af2f6e9aa326f2654922c2c5a12453b25092c0a5ff01118b68511e3085ecda43b924b104d3aee4e1534b0aee0e519b69bfa13b4fbd48f6fda38365c8337fd608604d7a77b41b1d24506bbc417f1f8fe34936d201eaf1c672ca0725c594a61fbae7d4e8c0f538cbc934c986abd2954b1070f8272700f1127c94293d899d6a18232b3726e170aa8e4e51ab529f5377c9907f34ef4845d89e16b0f6e84d618c0e1e045dbf6f8a2b2b10f185e60df9495f1976b3aa9ab8262964fc07638b1800dd6d0f277ec1ecf40453167afa9612cff2526090e7a47775016730c90ddbb3d7c0886b9b6c9da415e57ce5c583a48ee45b641fc6f717e43b20b03f02dd65a5f6a724001cda004b43335011ee3998ccf93525d454df192636ecfe9ece0380b06cfc0173806e671c1e05804e8dc18dfdc6f6f42e16bc87e7b47d632ab0fb1f445c5e009dac9a1d8a7dc99b2e0e8bdfec70a6946f806402879bfd39ddbfca385516d69579ffc736a68e36df2b5339ed8d4c68bb4b60c96455cdb686f2b8fa0b166ef8ca665eeb0411d07dbc4607b3c5bb0b603f27bf2c7c8fd7d700b54102c94c7ee568be7f7ab37c86adab26b625cabf2b001c866e5701ae7c042bb76de9ec1adf26479b0227bbf5d140b5879cf88198a3e28b336dba6b359c4dac2e1287a8b3de86a96dea0d0a1a44bea3af89e84c58c6e2dd79e175138f7bd1898894deec67a355018f910bb7c8761a34808cc0dacb7d8d4428e0f325326eabc640efd1637c8fab004e3a1e85588ced3415532b4896b943c25e449ec9af5972cc8a073195edb342ec89819bd1ac3e2788f6bd2e1ca2df66892e2c6f840e3d3fa47329dc2945cafc707339e926ddf0a22f689ecd08179c45300da0b7761e8dc3353b4c01c10f1c88338f7a39b57809de5de4e03e1fcd7861fa940aacb579bb7119d6911d2613815f9428a703b870ff8b91535683a42505d8c76c5736bf26ce32d90f20b4b2e7a3ff1be51fe6bfcff633cf515022b192202aaac8afcd206f1433253ac3771315ccb90d411d2d36a9c0053c82afbd0b20ef5a9615baecc082ab4310c8614f0d8f893d6d3afd2c60db3a9b0edeacd0124799b4298036aca5c20c12ee76ba9e8c5880f9106261ab662d33d0ec19edf11fa11c868892670d5fd6a5f2a18a209d0cc74a7542cd1d7e54ff1e490f3eed2fd4ebc62a39a48a040348bd5e45172b973dad000246a8f612bcf85388af203541f82c26d57b54fb1d6400e4c91b04ad51a6deb88123764c6f0e8e685249631940f33ac719564229bbd65e7ec872ef3bb2c2a299a9677cd7406a57ccb9297bd4a4e19430d94b447e30ac2d6dcf863ac3b438945f4973d4773dc1c5a8889e06be07fad8732ab8bf770ef9c5d6f0a8508026e3456da3aad56da262d0eade6d98edfe81e698db35e00dc777a2d896757b4f5fb5754dd3e34a0dbe5d66ef249a83bffd28ed4cb1788014e9ac3ca8bfdc0f86939a0c542c20581a8cf7302f9f0893ad7507f3e70677bfd747c2c2ec593ca4d0c281aad68ff5008646d447d0d87b6d58dac5194ef94793c51e2cdc12d607ee42d926022a45dc746a4b806ce02d851078bb2b7fd7a66a7437d8e990f609a81eacb324541ed194a9f9f699816e659a61a86bfc9f401811c5a5e0e8a4f9423c8e386ed765487756f797774cb4db66c344dbe9d1a19fc7e95bd19a99843311052b3d14d6c6b83a06eae3d01c19432024895a9b4248cf10db54f6c8f3e169a605418c4c47b4ad842b7f0666454c961a3727068b10a22f4c76f9d022cd076e5e3367a5728d1bbc66bbc27e1b66a8721620a19fe7a0e62463f2ed21d39bf849748df862187e16b20d9a36057ebfc0bcd7c84524476e41134b691eada6f6c03191b1ca97a14f5ab6013c8432354a7de8d076c989a0f196e561e97a99ceec3831ea8ad8b2093faf229b17a20475bf21cff67572f6f3a15d38062ec59ce3e4fd35a5655fce087a39163167c26c7a22c3d8d76af1fa2638c81b003d4259aa9b01f3303b38363a9ee58eb96db9e355fe533e2a9018b840dc257777b66568fe6001bfdf6cc1f12cdb735b2dd0c40c18ce4e821b95f9b508aa59694c3347e162ad0392eecb5094410b2ab81777eacd92bbb00a1b96c245da7bddd867fbeed1fa2889c371884fbf512ceaaa9ec0b6a14734f6e7bb6d87a0403a6167793c4127a590fc9bc942955ebfa46c2c69cf5471835e6f821b0c90e90f3086fc01f4dc5e305f173620de8800c6153fd43e75c9fd8246621342d4f48672202426f9e8c6b95b153ee9d4c7e43872e6fbf5262faf8224d0cad8a3d946a65bb59819c526cac0eadd7b7687d80a4c07f3e0a44594c8102e37f3f02dc7158897da04d0cea41e6428e4b0c7f7706ef9aa81b1ac948eb40e83be3678d3c40de8e03114b61e46ae31eb2ef587456e2981b720a7cae9a5e4fa815e8e9dbfb94962ef158af18204183a46aa37ccab9ddedcd0d6d65dc247d2b7406a9a0492e9bff57b7aeb09bed41273521ad28b0beb9fd16662b089a3c0d388f4df4762c39a839ce3d17105d9f12681a0a85454310bead6d2ab120fcd2c84a8a79dc16a1f6541b832810bc71cb42eeaff17803c1b07390261cfe64b32e0fd463a3fb84994a6485ccbd924d7da5c688658272883913dc8d7bc0e9e734602325c66fce0e7e6ecda3235e7fabb1dbf796779a1c63b548ad6697896015697e60155c93f24e4797f04270dea21c13e2eb3ae8d564497edcf439f8c362195d2f5799364743df024d09efd333b2b789a60796b20a1ad4f57fbfeb0dab0db86deac2cb5e03d981d8c595cc8dc30a1a8a6478c0b604f6f8dbb58be0f0d93504f72ab248fe5ea7a3bbae5082fd2997c3c11704ddc099844a5a651b0e060b99a929bd87b8fb037c8e9d4a31634f2d00742bbcb1c0a329d20a48644c8210f5721c01fbc1a9c5d89200a02acc4b3c129eb1bc297500558bf0100bc66dc843e2664586ee83ac1f114f17d4f84763db3f78b2324a13a78331ed973c7faa53088d95ec59f2a8c2d74040512cbb2eb2cd975d672e8c741144cc44105f202234591297998a1d1d38d2094d899e368ea86802a4531a726f0def7a0e6db7fe89d499ca2a8762245d31deb8c059e4df2884a28d1e3896b67884ec40a10b351957a6e1cd43546af51f3cd3316d3ca1c17d120ad6367066d4ce80b248c0c89193b33414659074834b4b4476454628e20c8041c9bb3c7093e00d3e5606c977153dc485757ca8c2b0730b6ebc2e4c135e64846043520bc6f78b9b4f6c1b37055ed63181485806217a979a1cd0e452ffb3449626d58bd68da3bb1eaa45ba9efbe074a1e71de4d860044853f15c15e02326ed40ca7ac64621b875ec7c30c02ee633fa2a0d796dd00d3206871e2abec6f1616a06ea49838eb816dd7acf4a311927ddf7335b849f31288032be4902708df9da42354f28e0ebef7e460dc3f5154b5b69b8c2a5fdc8d90e8e20593118d701b8c121bb0e7bec491c6d05b92af57d92e91507d546baff499962de52ee60ababba775bdfa0fa81927314901ee83be7c5b173ee1a53c038330a34a1e9c80f71bf27c3b637a1ec95a43524c919350bfaea82c8fdcb9d3dcbbbbcd49a434de6dedd78ec51401e8c01efb3683d4e3df29c5238048234dff205a0749f0841f02c54a07a5d4dd159527f334a02f3274e7a8d37b6122a31f5a5bd0c91678831f4558657ad5283ac5e6de1b395a641a9bae7a320711720345249574a420d7a16f0e0002d08389d7906df078a375f98d0612f41c968189be08210b84078e5f6d55485e841aefa48d58c4ca0f891449e66c2e1d6994d6631df856ad2d1d2382ecc0cc2049f52772b6d11bbe7b1c9518fa0f1812dfd8db0d55c783b285a0fb97016a58ced64c99c6d29cbcdf5a8af46d784a461130d11c83a4200e6d170fb39ff59c05216217104121a199feb3bb1b3d4e182a33a5b4acc2039e68521d25cc27bea0962c78b00bd56e6ae02d60243471dbf6397168402352f28e5af6d50430573db630e829e5e06b68798d8d0d00c35ce0ab93f82dc2237fd20a4cead15ecc920ffbef3cd167f268595a8c72f1f4533e00ec440b20711a53562ff93de758a9f7ddf20222da1c846b6ac4991fe7124931308712ca548511bac64d382efc0dc3796b2e844ebc394b071063c6c198f0f5dac53e92ccae9a9ede514f6f1e26dab8dcb21cc0c0c3204df5c305d93325352bb9b98af1cb648712c9e2f6d01bb7f6f0297723f97fd576213669283dc70ed27e96949bf8deb7593d0fc040ce718ae0b0a8c49e22178c28ae06d95f6641c9056958bcda4c23da0f8c38b15a5cc65dbf9aa1ca15a5c7e308dc78a8e7033356a561a029e8bbba73eb3afd3ae3ba076e08d18dfd6d7f0cf9c5ef19e26e5636d48ca2cda3e270acef74fdd73009d92be90da9024d17819396c223237eb459c683857ff023cc689483aaf0e342cf41a5bacd3d0742c0ab261317e9abda56a10267a569655666814fc4e50968506936092c17b0e5c514d0a8cf8a7c237abd4c607d9dc2a2bffa9152ac0c6d15f350b7fcf66c3793710f3fab10ee90a6a780d8d6c65c38528113fb8f18a22db93f224281df8ff9ae7aaded1d3bb35bd65288559b744aacf36bec9de086b8b9e3f593a7f1b10bb35cae92bc59b73040a0ff91d5b1cf7c6138d1024da726182b8ea8c41edc369dd1966d1a7727a6c43f4a8118e86ec5fae99170a6694b87bc9432ebc6a6186a3ea985ba3a88d80a655a8705efff175a6c9ead042aa09fa40830cfa68cd5cbc22ff4be95ce52d677770ae2416f36b95637f04be9fbd5cd4729e34c1813b2e35dcf41531d9feb4e8d951b1ff6965b745b9bfdc4f685caa532bf016822bbf24f162e6deadada15c68a881739754bf80a8ace5d162426508aeac4f521b5b84a081a4da086a05f7cc48b11161c3ca96ab9f28d66fdb3cf2889453063f148968734ce384685f3abd2dd1aa6b15538d337bf683f03a8236dfefb93b23539878bc57ebdbd721b107cbd5a59b70696b1d92a2dba6f76cec40a9d082e47a105b6c1d708bdb84b5dde4dc3efb6fd3adbce7997d3c95d2019581178e689510dd7cbd352bd3b681c2f3eac456d8b3f1dda2db1ddd5e2e4091e7fa4397c383325e722c663fd619e5c82b00ada84652affa9adfe522643034d8b51f75824adbc9804f9dd9b77bcd30494901ad1937af3db8727746123a699194d9de069fa94e65296a09f1e43619e38ba96f8c79d3244f7bcffa61d14de7a0fbfe485bb8af00d9cdc1e1cb23dfbed27467d87642b6437927b302ad44119b2aec2b7d2560b7c6c8138f35ed3b37c007dfcacc0eba45cf9349017a5f5a3318e039dba54c88120c12db3858d8335004c148d6c43fea9ad54e6d2ee86e9e46fab4626bc4c655637a5242a6c4e62018f63226259ce0b60cb8c0c988fab82a2e6bd5dc757bdc15296b652ec751704f86b1955f233c615343f071938c450e41b1b8fce4f3dac3b064cc287358468a5f1272bb8e4a3c358393c624ad04c4e957d4f20b121ea0dbe5e09a1a911b481360d8e6a3aa8fadd40e46acdefe2d0100a93bd7275cd0643d0ecef6a201b53bf9d5e3455ebcda4e2ed0a791329765ad743aa3dc6f04143d121069c8dfd766ef4a29edcadcf522f692848551b3b456f431bcd7d959bc38b08f20b3d132944615858118e22c80b29ee7e17fc4b013fa3d8b4bdcd3a9a6826b046fda374cd9d25f3ba7ba86217967bebec5b1f289913bc8ffb84bb204f1f1dc1872161ff53aca2d911f2f0222b782414e6406d144d311a03af324ee099c6608870a8823e558c809af7ff26f0bf6bdf84f7e40a98efd3b8448eca2f3e6fd2b0e2c4f0c0c53817705df83a2cbc0be509ec8dc0606512d468ddd3aac39ec411c54ff1572db8245fcdc141a5124ec3c2ab4dc70b656a7df7d68d5e3d76c07c898661549dec06bb392cf0a9ef8543c298e8535526c4dc006c93a2063cb2c6c497bdca79a08170452f0fcb474670d5666900f8506d540d7f97417fc0987b4bf1217605eb93f9232eb862f17cbfcb3c2893760d03cba5b3fd6887a41c9e36eebbcb0c0a4e3e8303243d9d79e5bc5b86b0b5389118860d8cba53137c720e066fa8bbf6cf2fc28b487b579ad8ff7c722ae71ba9a80183f48c2041ae35593ed8a90e0f2ec33d875a84556e84e50816e96effbc2e9597798eaf057df3776e3e7a2037271bbd6fc5278649d8219bcaee213376d07c4267c4f694262bf008462320982d3dc801ffec6d417fc6b38458ff1ca8ac177b0e377129d327bff245640f891c1f3a1025bbcd811470e3584f0f420b8d30ff19194ea0ffca71f53aba1b43585e6f3f9f334e14f1cb579f593b8a38b098201ba283e83c3d41ddd15202a5564d4536b0314ccf08dadef7f07c79146675e010e5d7c071793426b1f00d39ee9d9ababe0cdbfa94e1100b352ab3645c0bc2124ea7e28e208b866324858cc11187377621d66cf5366532e64bef20ba764508daa47f273ebc86f231fb3ce85f8047349b02a6a606396a8c2ed18e2214ae31e8b4455c8b2330cdc0478821501cb586c85437605cc9c414a1d4f07ac5508abdd717e9be7c8162b3b23de7885c54c3de6a315230c2cff9a7421315b07858703fd4e52bc3850cb9099e32d33f072c49585c8409487209f3c4a5860f044cb8ee20a17819a11bf5cc82fb8c8f3d8378c716fc65ab894a3b36f1ecbdd9976740bdd9986be2ad89b92246350a0661f6272d2d18f39e94aabde616c88f0f2a0f714a1e1737ab2cb3637d35ddc437cfd39f10e996568619cdf0c1aa1f1190f22bc04bb7e90795a32deab25ce570c64fd414b8996f25afc0c21b8aa55648b6ee50915d6c3f2ef784871e5ab90809eeff5b6a9197e276b70ff09bdf764c525b085d598c6f95ecc40d112b38e23de1e30cc2d8b88f39caecadaa2af859ab0aa2aa1a2e4622716c7a32dc858451aeecc09e837985d67ef55822463910f963e6738a6747e176669bfd343faba57038e7fd074394fe42035b6833a0746cb028a5c106220d74dc1950b9e3cbdde4c1106b059ea6803fda4b00ab1da85ed9b388e159d11c0e89bce281390ef097c96d78320e8e68f15a5ae1177f731fa095252eec6ef8690630f0af9bf45f63cc26321fd79b45c0c88f0da1d5e206c87951b503a46d91bd6fd009793647e344ee5054ae73ca302f6152d03ba69f5403891ba0e9d0acba90afed64c87bb8d84c9c9782cf7cd377ac1dd807f4e867d086ceabeadbc44014361eba67da6840510fd2cd88c28dc250337a40816072515053e6f90f0df9275b78e75286146c199c2908be8d57093502d8bb2bc5f88183a037dfcacd1dd5426cdeeb7ade2ee4630d91731078bf0d816e757d688bc4201fa2a541ccff77a84836a56e0048528140a79e09732854ee966f3659716e77a151dcf36a16b436b2ba8108f9b95a552ae9b16601aee63c48b63d2354df4b7be0b2e5d501e1ff08658b0d46ddc1992d3e932aa5f8b034a237e91a8a1b8cf57b7cc5354263dabcb2d99dbbaa6b892f4ceeab8ee2c06da373b2d6d56b450af98ac7fc574238f0c9eb517d6555f2ecb1a6cbe5e07e3f15ce88eeacc33f247d27cac7c8ee415024e36c935da6567925798e6107af48bed45e67d2efc0c78db5156606711abde288360dc336fe7be904696e8d534befa0cb00cd1d6bdd947732830f7343c5aefc2060bcc02f4aebbc21cfa542913b6230f2400e55fe00b1cf059745898eaf856fd20d0931e72180990c94220e1f2ea4c130a80e5c90aae38cf060aef1b4f95557c1f429eb8c083e42e3a89298e094a9b7913aeeb9f814f5f412c0733cec7b27c4121bec35471e1e8d76d70825e38ec8b43dca8cd09ae9ab263445f6575f706e3faedf3bdb5be87a7d1e99d3cdb9e25cf3b5e725592851739e620fc3d504a97b66245c96163d993a9b357b1a1b70da97d850586e6cbe45d372e2bc0a3f508a50c44fb848497ecfdfc39892f049140079dc6b77ec4ef17578b7fc3ff3f69ae3df6cbdf1241b4e8552bcd34c209b86e9f6b1a18bd36a980ab290ac9304a00f2da43e9884589308d21c1b41efc3b9f5164d3e41167691c24614d51de6baab1e49fc78edac4aa138413bed093b739bee16051f25021bd3e231ed1afcc9356913baba85015b492a3920bc75fe09d0711b5b4f31ba1e66e0751e73dea320018ec168e545baac57513ec722c237bb1abec47432dd97f37a74f7c563ef098ec99fd6890e005877fb9dbb13abb6a84a830c47f2376aae8d09aba7b0730b7246a909e213f423a49dd8d7501bef4dd7808cbdb19d0de9820f3aa1b86ab1c7d6f96a4e76540d4501e10d1ea631daa675d5007f2963c769705f90ed5fad2b215cf0b06b7a62de7dc37395ef0ab241feb39b91f1ac1c3968c89c05d711ac92ccc57479fac1a528e02131f5d550201c07b24a119b3a201c0aa203517f4367a1f22a84c387e09d99df6538454b7d8fec6d18595515cc5316eaee12d3b6e175ecf54d804a11789c1dd3051b1138c0b1b0aab443d6410f05c45d93ada95fdd3345f8da9fbdb1e1ec4340dd40f55ea88d1b1c7d12ccf28f28b92b8e92ecaf7550ad55f995a72af5930e1cc4290a227358946fb03ba5712656db82cbf68325d958fe787dd3f9583bde1f6af8f552e3c4a8d8e427e000dddd36f3f1d5800657ae7b4dda6808c771f2f56b3de954d3d51efd9f7c983581d9fc0a54bbfec5c200eccea840bfb961a7b7f69b76c13fea8a4460acd9c5ba2d16e013cf65bea750a6ba2184cc41a12bc2e3f0fa32ddac57e622b1fc467acc88b09b51a9df944195d0c873103de489ac84a043e5d2ab5bb668000f3e5a6ac56c6640ab0661c6a7e707487b316fab93d17e9e639cd87911bff06e820f75674db1613873878781169dac2dd761e682b31e25cf0f8c2488130112cc9b464155fd168a71b43e3549ff57a6da470f2874838b8f013c9160dd205ada3cad3f9b4373efed709e11dd2b80384a608b8634a2aaa53f86cab28926256b0dc559e835d785447411bfa29760867f9ad4e6756e046229ad3062c1ba0132f6046ac9c23da2825cf9a2f10d1dcbfd514874cd54c54d24b3ad149d4778d93dfc51c97c8ad148d64b9cee9a7e74da115e620805345c552c9cc2e8318070c8d969127d178fa3e0959ed5fde416d1e0bac0735e88aaa7258085167e728e039694370eb8ba17e93472253242ea80e7978b165a5d80673bc095b93b7fa1272344816e3856127d55dbd206846443ade1e823814aca06c0d79a684f1dc8220e98a0d54167954694d41e43a7c6333b97711e7f758e6b373ff9b4d20929599f3dcd6ebf58d04b43a17726d9af5e343b9af3d1e7a86a8aa739a20f6fec104846cf529cd7fa0a2162d8647d510f2f806624211df4519c639078d8f5f287c20ad0fe4f84344fdba40a287596bbe9ec4dbcd2e65c05a771bac75b759aa0bdcf82e8cd88ba2a5e929ffff5e2910c9845b63ed78a2ee13f6b91ea89876c5bd2138ba63c775619ca7531a05096ec9019d6c932615aea390e90d6ef7fb23b6078ad5452908a851ad134607300d65c228c14ee944d88b61a0955b7c481edbb38cc063975b6b22726342edfac9974a641e38e482df9925b5334b147c4821dfa425b0d779b5099599cb9a1db808b9aea92e1e55776a8e41e99e212a1e2200d0e15599642d0ba65448852838bb1120eef8365dafda85a64fb128fd1adbb46cadf1e9a693702441e94246a693627b6fd4861f0aa4fee14e7427f52bf679df175bcb530633f66def5c0a57b33f928d3454299f2ba86bc4f216e495a2f2856e3aafe24f4e16759f39766115109806d9094a718e9ba4e54897b110af3b62aacc50e98a11a6764316ccb4e5fa9d830deca7fc84db54677e1022f4dddb32fb40e753ec46a1240930c06d777fa03e159aa7faf2bf45ee1a15327a989e0f683d790f7404fc5c1ee30cf16004899a48701d48925edfc279a97002cb86b6ec90920bce0f091079b09f3e7774b3da904b70532e31cebc244c015f1e5c7fd95d0763b683ddb53914bdc4e15cb16f1f6302765a570492c3470d475e012b5b561fc3e0c4356a6969d33b3fb3a2e0129cd57580a557d5f2d4c5f4f87d62d75cef83d60b67082501df82182212b0019511f0542cbf1f7c30fc2118a837efb120ff3c86888273c3d005b0ea1df1120309b953b86da3b94bab41c404d4102361e4b3c92763b9b814983373d5d4bf1152052da1fe41a765f2aad6cf342e86b7a80714264ac81682b88148d3a1e7c18c6e7d20e227ab248bd30a5ccc3af1410acc83488a3c4b89e017f18a7de94d515765a7604b9d7b038b0e321b41740e74f25411d7a16fb806bb2aa1d90bab52c989023f5af5a3cd6bd4e5fbefd62f03443cdea16d3768ff7c63624b7f4c4d45e5d188c2d4623336538d7670c536acd607cec713e855c501de1d6e0785f52876af4b3f910408634df12e4b3f865f26675d7e392554db03efc7335c460aead5452e6a595ea6ae20dac3507fd0fcae0a145d5d9b3a7ffd466a298fa9e34733942764a7eaba2c9185c2892d0d9f061105b21fbe4e55bbabcf1ac5b7e3b777032fbbbf8cf09c3bb3b99c7c98a2905777a1d8e72c598f0f281997c9c206cc547794047f32718ff84e672b8c9851f61090eae7970390a7faa2af683db8c8eafe6e96cfb8a33a0002e2495dd7da87b91fef0f5725c2657edd0754750a2296799b89579c113947feba38d46372ec210a4efe4ab7b65d47198ad9adbd5c1acc1589d20a25ed5301be6c4d72412983888d60c10485584a25f7349450cd3e62a9e34fed3e8c3213f7bd756214d0582036bdd23f68c9ffaa637bbbc411e05c5620ac34962e7321b1c6c38b785c0c9e458e8505cb5e7f03b2fc2b0ded8f795d844447eaf00bae397da02c83fac833ee6ad79c1923dd2d02bcd56e8e16d793ece69ecf4c261eeb63eb1261fb480451d97753703bba35fd1d394515de88f50a5648c097eaba2a5491b4d80f48aa7db34020753b5ca7d5629f33aa0834b83d68ff4d8ade791b9c88887ecda3ef8255166145c8b86a96b363b1964035cee6d53eeed4086efe3ffb7ee26f2a84a319614af5df95a532dfc8b4a09a83b673b09523ccc6cdc8a30af30dd978de48cd3734d58debeef1ab5d286764b223840f6be8cc333850cd51f221b861a04e7d7fcf3553fb26d9d44b963494fa7e6546cb2fa366f3fd1af735e9d726e2b94a98e7ac8402ee204c7bb225fd5422621642e34b5b8c5c5b4e313d5945dbb802b55fbd7cfc820a4ae800dd6fb836145f2f105295bc7d54268d11c43b29625773ba64f633357a9f34fca8c0258f532cc3e4b252e1a0f0905173b23dc4f695f006f9aad9c8104f7b2480cd1724dc9edd7ef304bc3a17fdabb95c6dc004f401a38186378c134aff379239defe76a3e28def7fe0581ae5f03ee1b5d9035e51fe2e702e53df568fa65afe5c9a4fa3ab7a362550abf9c80f7a019bac313c7b6441c5d31287977c39567c0b998639fc6363c1655bb1ebaf57d1b865398b2fe334933e7f55acf4ae2726231a3b9d4291e374d9739722010fc6dbb13ec10e0fb287da1e73b6cb49f8c23b831eea9fbebbda8531cdb63eaf6c63138fa84ba02b81eb12758920bf16d064639889e310d8b1b2c7c4fbd886203ff37099464fae85ec4b2f5351e610a312f27785096ce96522d07ea22ff0bd0629cdae4039e69dcae4be3661a2a5ef35279343e5526efbae8bab05ca2cf9a10ce91b2aa9c47c20f73b2672e23d114a24f5803f85adb7f96072389ee175e2d4127ec95ed05eae72cd4066348382780aaf5a69706533dbd0decaa85e136a49693ec974d0feec67609354f9b31d520e3f691b89131143b85357b67d3069dca42b60a4b3c5475327d6d43fc57a81ee4426ba948faf064ca48b7d5c90a511e4f64aeaf8b6c120525f14bebd3a6bb6aaf6aa9b2489016d619de7843779af3f60195a2160f090a1d1c6c8c4947aae57da135f66e154cd1ece0939f0dc45ae87024edce10693f72d1902b62cc78eb4aa2f29d18a878f1f20ce8bfac9c3dc984b14189f1f06683b999018312e7979abf9d6a790e0add24042c79d0c478258252d7e147f6953245f8ccff4941059ca4f64ff94ce9877727450112c907df52f67aba93c90507744f7d674e2764a039ace7cf01472ec1cb4f662e21bb1fa9d66af57163ca263548a3c0ce6dd5a89bf5e2e8c3c1e36f39078a3f2b49643e56cb697b90aa1817f89bcb740284fd29ac65d03217965d3f3f9338c1ac173e0255cc5b3207bd65f864f826fa71a7ad4c4e5e38f2a6c5a2a9c7780c85845d147800403f9dbaa5e70c76acceffce2aa8317bdd01b4ef024edc661ec96b484692b5605df5e9d869a2e31e0ef3d4a0bcdacae97f990a21a3d119905f82dbc3ab01b5266b9f4dd665668153cb58f3ad5bc0605f0023f12514a29f04652a28a1015044aac004b9da608abbb7397160670b6626e25ffc2f5857327801887ac8cfc20ae7b4e55f1e834ad81a14517cfa8dbfc689cdc34d43f40529b271659b770c3f95a1e44977fb1267f9ec3bbef8f794a787807484f29b68b518cdb9b2842f8bad337dccd18fa8d89f74a2c1389d1bada41f32f2fa19cab09d2f865479649e3c80d2b1078a23f5f2fea7072be13b3133cf25ab6bb7df6f2a8c8e4d18265fa4835527114316ed11bdcf0bfadde7b5ca5af4dfd15bb103f433b710af76c968dd9e24de5d520958f2e19e95fad5b8f705b30909437ca113fb8fdd7f1bf4a3707e41472163cf662aa0f6eecc396ef33b6f27f73b6df3a63af7811cd28c86f94290130f14b135a857660ae55650f52573c7aaeb1871aea41dc63183dcf73f622d6e04ee75bc53200752df1791b7dd2f9c943bac70c2edac918b32be6a029866185641a4322662c9ce70e0cc017ffd3614dd8e795ce180bd7d26fea5005625c37558af10c80c7969d1f55388bb5d50fa304d5c8efed30c2c335017811b2ba5b8a0b9857845934631363384c404840d20c552ad56fc8bb6d037e1cdcfa32af8ba2420ccd0cfd47d2e0ada4b41b7a85cf0e08d0722848b19e5b13be7b8ae90cd3934f861c2550ee9f530ddd5fe44c90b4d52176ab980f6d42c9b9001c9df5708a58d26ab82f2a20a625c3d382c5869b0889a284ffbbd335c3166bff9b9fc2082efffd43684cb3d37d787a4f83e43afda4e778fa83499b9e9ba7f7122ddc9bfe6386cc372d32b39ecca27e69d6e740d05ac3bed5315eca03505100b59b53810a4d7a1ee50082b71006857d13edbc31fdffec94cceebda5da4231a634a912547f021e19283ea0f80664dbdee012f70cf43c70e8b36834772387153163e54b33940a24d75c810494c1a6627f092fffcb58d467bf60b0926402d208d65fc61896371eacea2428f098a74ed17de6daba12abdbdec5a0d7d6f12f47a51762dc67c7a01f8ce513ed41844726cb203624be0b24e169e5311775dc2a133d4f5117e00a669a7db2781eea17ac600f8aafd0b7eb7062cc82a530ea66dd40c15ec72b8582e40dcf144cb06d0d9d1843c12675727d6c40c2923093cd4afe499b8834e3d63f640be43f0932e7d7562ae4a57126ebd1e79bb6e5f914a7997b301a0c7b981054457718f8b55dd4bdb3091a6d0ca0c1167c8e26a42f0a93a2634d6173f3f81487856f39c3997af22c3cae6e8da8f1e3b05b33bf046cd7d8dfc6b0e8b3e23d2e2a3dd5722c8ab062b72e16cc21f269680628b33d917d8f88b9bdccf4d01e27ad01c9337c72bf2f131c56ad779f4d132cca9ee336a325402eace7bb33f0761e74f009eeb39c479e4aee1a4de910c8d724574dac448ae91dfd39b25103a42b1d969c1df57b806d16121186d4228011c29adf817385dcacd2f17e940f729ccc4ec51ff4ea56802a447d7ea178b949a3d912901e2251f403fc3cbf2fdc31a52d73e71711e32d4e2065a652d0ce4bd6a0797c31171774dc5520b11c50db557823e3c57583a81982556246d30c164f60d10f1080187861512f5a223bf0af89ed78d1173118d13be6a66d0252a72160cd046235b96928cd023304c26b890c29297e8b8b77766b420c2a086a38413d10f37ccb3af1545d69e3e1219b9c2edfed942157b6a5e5333619630c03722bfe9b7dd6f88ed1b8371063109c472900add4198e2f242dfc7d8c06f66c98e74c816a7c8caa24630e49be6b4972276390c19d2e8e60f37a322b9f2457349cc6b50b471ba175d03726dd5d35cc7dbae03922aa64ee8c280e8b6c0297a6b462a2e9467bf9b6e4326fab72767327dda62145f4ae8078eca29e90b6fcc7c0dcb49dc8018baaa6153f81c594ae322cc0c976b5fb68587b9bf6dc522a0de3d679af17c71cdc05e5aefcb15c7be674cdff88a39b85ffd3bbefb712e58dfa67df97b21e5e08d95e6bdd344c02541e61ee8612f591d35443dd76e1d41133fe27545094f451d4aff708d614a86c069603f7a5535fa75c765874f393b046ba9da19fb20109d33ca7e1781f5d3455f46a336dc5b7580365a092ad6d53d6c759b388e4e98857cfffa19728deb72e41130d2f0469e6867c9d1b501b72156a54f5d88f4b6914beac3eb98b78c0d285ca2ddb71ecb874fe030a8ba2825a30ceba4a07c00fc1a7bfbe1845afc321d2019fb8391cbca26605838fe15ac636d787bd3969680ad2d4acdc84f1598857e7e8fe0808df06719c973c08d7cb6ac5a07ef63757cc12957dcf6275dde76f9290bdf7de724b29654a297308a8071d080cb4f32176bb07fd27f5dde3f01fd577efe33ff6bbcfe13f34df3dcb7f6abe7b1dfe63e3bb17f21f9befbe7b1ffe73f3dd7fe03f38dffd8ea7e361e0e578cd4042ba8ae37515375e83f9d2d45e9aa9b3b4521721a4d6ed497bcabe50038ad59cebba948c726a776439aea647c7f1a9c6b79178a9b07daf8b87907b5d5cb5942e8ae58b7dc8ca92d4575287e6fc6604cce715afe6d78f9983d9320727f9d8b655316c7da15834d4afd5a75e7d0de706ab698f1939e8946e76dbf85a9597f29eb49238e8b48983fe1a1a0e7acb4167dab66ddb9e723b360ce86f5130308175243f4ae18852ca51bfe2a03b96cf931c64f224ab7dd84c37040b08e1a0ed7bfb987b28fd8985356433d7e5578fd621da416c89a5efd3ad0153100c8823848bd3a08be27775fcec85e2eac6245901568d12c4815d0ccbb958f67d118621dda2a22ed4e0865d842db1fefdac08d43ef0ca3f0595b5e53a2bb2fa0c1b76d849b7b7ae4adc93ba4e081123ef31c6e873638bdee915e9a6b09dfa4216d72d50fbc2efea57e406141bba511be92c4254d9fbef645906de8c753330cbbcb7f0cadf4fb8ac32ae87e0fd022403520142013e13f7f8df8062c36e19b55c65a4c29c6811815001b0c6227165645ef9419ec7412ff2414ddf82f88f2ca102fd8428d626fb3c91d2ef5637145ef9fb70fda7f5f7da4732588ed163548adf26457bff89dfef1ebfd036549592a469dac764655f9c797db1c7309a13be46d91168d21d326db060c3ac8983763cfadafc429ac4c1edfa21cd093d7c6e7f3a4adce3850d07d052ba0368eddcac892635e96c3892cea898acf5b566ddeefa1cb3ebfa1a1a558adb3ebfae2f2442e4cea74dbf3ff9f6c33cf956926ec84cfe3eaa1471fd3fe07a8436d104ee6e99ba1270250757aeaef424cd9552f675ae621806825bbdd9c7f53283472ebd66fcea93902b29f317d2bfe84569bdbeaf5e44bacb4b349f1b99af8b3946e6ebbaf8e9c7fcb3f247762c1a8dad7577a9158869445ef9759364c9cccccc524a6666174c961ce79c7ff15f524af911d9ee8c61ecb003b7630a1c1b1a0cfb16e706bb85db9539ba1d1982972525b783df3ad9f9792cd94123541669eeeeee7e46635ea2e1e4603b77b7cc8c53023bc55a1c2916c786dfee092c47f7fee6a4d7552b866d52d3b64d65850df9cbe58f6f6da2cac96ad63d7a9d8d124dc3b9c1f2779b757e57c339b657a59473527a551e76641ac77131c6d4d69eaa87bc1d17630d966bdcddddddeb2b58b0dce0764ce0dab0b169ef26bb826bd8d15c4e7a21552466fe4cdb986be0c8ccccfc9a0a2707dbcd31324b1a1b5c8cee693736f7b01471f96df8c68dbe91746dd8a86edcdcb8b9b9b1b151737343636f6e6e543729eee6e666c341eb76374194cc5fe51ef9724e4a2b92bd2e1c2a56ab5b95533a2c1e5df938546c58b30e072707cb2ca5657e1c1baca5a1a9a961a6942953a672caf8d586a135c1c011d3c71bd09ebf2a8032a5cc34b2c71b7f61bd73f2ac6c4646292bfd8eb13f4072f594724a39a45ffed594db71f5d5b2a714e2a29783b28f409f6a4f7d32f69451cef99a9779927b664fbde88c3668ec3a60542e3bafeba5831c10fad857ee07b7a3c6fac9abe99cd24a8482ce57ba3ecebf3e96b1ca0e524e2993b04d239d2dbd68be9caaa5900312e71a767acf6f881521582c2eb5b246e021c2f3849423f06c601a99395f363031146cc7c964bf1186d40c5cfec3f7aacf2f9ff28039b8942aa5fa86a452dce6b18346e4dda236a9acf325fd86ccbe3ceb607df64bc8bfea60fd74b4e71bb58fe5a0fc986cbf830eb6f6d6a656ecafebdb80bcd93704abd8c75c795c5ffc1047e8c3c3636b13d2a54c37e688f9cf5bce94a59da99da99da99da99d2964309dc57f420613044c07e686348ec45aaef4561de2af3ddc5b33d890a3985a5eabb55df70f82ce14e54fdce36fc6f65f5b98e1428c0d5e94a0056494f9b2a3a50a309ec8810b2b90301261490b9aac609112c3165ccc1550008c2523800862cc0b4e4460810517688861891318265305489e6001430d51bc88810561389310263344064370c1823172d0d2851050143dd1820e30c010051532446022420e52a4b182164a539e90d27466062d676208e2c9184c845c200c2c50f8b224863294788200455e6088e224821f4e305ba0e2023389103036d040860e516c90e18b133e704121890a32a4c0528590084871068a304a30a1a4d4c4145da09ac460468731c688d083154a2f8c49c3072f96c47811d2a404d30c288052c60c2234b9000728b4a04594304c9424e96203258617a678210a2e8cb8c0932a2320f3c409972d51ac60723181122c9e6cf141ca152f843c21a20633b0eca0430b5bcc84f922296ac95110600871355628daa2c40a2418c10e663075e0072a4e44f0250d2352b89c8a7052841757a250d1a2060f66c274d1050c462c89c069ec500415262f4a23f061092a0563cc5049c119ad242153a4a841c31a55a86022456be130489e88b1e28216a4e0ca981b2613518b14326840220b175ce14a3eba508a7286082a4ebe10c151d000283d34f9a18b12175264f9c0165dc020e20c2a3c8871012762aef082821413285902c90e4fb0e04419377831d385900a5ce9410a2c6630430b3494e02ec6286901a389268aae1cd183133067c67489ea22091c2f88c1e50b2b74f0c1480409346151c41831495994515a40972434e0e0c4c208c2d051c10e5b90f9a105189498485c1500d1030e24d8a10b1043fc7c817984085eb0c00b28284f484da611b7c1f8e1054ca4c052268831d88a1c9a44e182131944a490a3a888284b4cd9e10a1a5afa7969087e16610bdbcf465457369652caea60b79c73b68c914af959abf221658c314a30524a29a5f4b8a2d12e65bc5310458081e8fbfb44202ae6da5ac5d6ca1c04bed56ab58694fc103307443c5c1f08bef2276ac14aea11116159d78bcafa17278039701aa2b822a2d67d6af5993ea5954fa449a2d7cc483a2ff0daeb92e3415f7e1f3e785c9790d39ddb1e08763b5fb739a7bf3747a98dbdc2e94a4aed16eb175276425adf24880383bd3e0477aef44a9037cb6496654e387ce5ff8130d8eb63900954edbe7d3a5bc0b673b1bfe2d764db6a55767daf5977372fa0a80e6d7f8143db87534778ddeb2bd6f2a377c9ec93b27d605cd5a7b77e927e085e9f645cd715b118bf505e970a4c29bd6c6ac5aa0a55ea57ffaa9f74f0fa1806af3372e4766773cbb2cfbe506884ee0ec9de8783feed8183360e7677370f1f9807df9727a964dfb998f7eeee9f65d9d72c46ec33223f1d20d2f374e2178ed0dd21f1e50e199967326519f248e7e6b723d5aa673c7ef6d2ebfcc8bd3e3441b350d8be3cde92bb9b7943e24b0cc8622f81341e97f57b31567633d11801b67ef416d2be58b92efbaa02ab531fc9ad5fd8de6d56286a7f6f097dd567eeaaf4fda2eff4d301222f7d79915c21f3b3390c444448ec2437fa2a5c71e8ddf84f4cc34234f2bb4c102fadf698cc6ddb1361dd115877486f180ce00c4b84752b11d6e478f457376f04d60d7ddc21dcb36edc42ee0b59b7e76e1fe6b8db33076d37ee96c1dd0470377be3c37ab7e73efe0d8f4600fd353754cf29fbf517723fb5a71edf23ad564bcaa57fbd565f3efd86cccffa8441630458afce8ad409ebc3ea6e68ed94d4be46e98c32c64f07c83b4885edb74284dce4858c3662faa52a57b38ad51ad45fbf90888fa675b17db7cd5a2ee5ddd1ded88c6c0dd3b4c718c86a5f39a8bea66459d68610574cf6e2417bd0faacaefb8430d9eb63df1cec4fb6d135850ab43de917c6266cdff6892b8f9ebd3cacae6e327e381cf430ec9602ad3ad723b9afd2f981e46ab57e36427dec5d8787dfebfbf9f6e05beb571ff336eee91ebab8399797925a37ecc1770891eb47b8be4a6e47fdcb73f0ab46e43dba7dfb325143d1f574c8f5e9fcb87ebad673dbb867be90532f4472f51a97fd270cc10997fe45ffa294839c7a9756eea19f0e6de6e6c723ded0e67648b0a626b8c4a82e58c181ca0a545c705541a5051eae1758d9c28a642a4e5c60549962a53eb1529754b9d4a812259ddeac027595b1529950b9c86021292fec5e544a29a5945f1c41c7cccc19d8986b652f44a2bd96554672b3cd138abd6d245c170b4370b98df3b6ed2be7857db9af4db61fd5e3bb6d5aad9ba6dded2bffb623abff316998e67969aab5d667ce3466e6ecb3cad5ab2f6badb5d697f5aab6db94b46e7777ef19ba75f9b5f6f654fc24f7f8e3f8b1bafe5ae47af8178257464ec9f2ccf654e411717015d7a54ce9fc90518747bdef49230d0292253347ff9268425a7610ee756c6f12ae2b6bd9584f7fb9c928dd8188f131acbb6122b5ea9b537c6271f006fe994430e8ef9886e52711da9c725dd3beecab95fe4cea2392406226f511491c2165c8e674c2867fc58f649650d25cd46cb3b3971a0637376cd4d4a46cdee6fd35d5096c0d92e3715c751eb5f6e7d4346edb5e4b713b549c6ab39e4d66f31b7dcde6351b1bda3f678d56336be80da5b4fb9bf7c7f99ce73e3555efef813f1108346f5fc3b81d76633039dec715fdcea3f1881ca4aff2346deb8be3a93ca1019461b72d878374ce6dde78373c4b439a4541458a1abf7240b228a844516d78860593d1fdfa56a56a3e7c10c7f5c9c10a757059f6853534de95d12cb3fd5986510cf3a82a35a59c483697616c2a158f8c71d670eb56791bffa8bedf7bdbba1dcb30d73828d3529ccdfada14a765aac782b0f7cf38287bffca419cf69e52a5b8acb927feec0340f1c282197fd2289aefe764a7d06b0cddbc923a84f3fdf5c677fd3e39583a843cf09fd4f7fbf8c07f6abe3befe36a7bcde3fec69b43d6c361e35569bfe6b32f949f7acd0bb7abbdc6713b6a7aeb1acd7a342f14316fc35234fdccd1744693a5362fdcf7ab541ff70192abf2fa32c77d53becad33ee56d8fe315d5a19b6f8df3bab89ab3356dce2cd3a60dcf7a20d39fb3e3fdb16d01840cd77f5edcb46d010494eb5f6f0828121aaf57b0e9b5a678aeece4c6a5bc1d5ff56b9e7c4d93de86bd94b3e3713df5bfc02b2711f0e678d90288ebd1e3178d73fcfcf4421d777270dbbbdd61bddb6d6b63df86479acfd3acd7f7ad7dfbf60be7d79bf264d53c9b70fefc2bfeccf1399ba628cb0d97888e1bbf39203445596cb8510641e4f344c4a7889d3224847fc194f982230b1ca96062083f46949169e4c617146d377e1de2500e3e951fb60e94c95ce979c01375e0526feb80ddfebc887b6ad6f022a8334d3e3e70d09558210e3a0b2be2e393e3080a8382baa08e32ece85252e21e7f2177767676067d72f4512bb954eaa3189bae7b2b09718f12cb4a2094068319299831230533b1752b57fe22cb9c780572d60ff98b26199a9a58d65cd154d324435384e28c7cac4dd8e223621813f588491c377a1e1cd5e799a18d4f1f73fa8840456c173253cb8e52fffb08aaa260771ceca334906e6d72960e076bcaa829a38a613b7a23b0534d19580add4b6c37cd9a866d9a651fb73d86611a86611ef79ac77723a12ff69cd751b02ccbbeb3cf8cb22cc8fc2ccbb22dc9c1ecb7afa7a430e5c38e1886613d857bb2d7b26672307bed6b2507b3c7a2ac66c562d9d7ec8940f09ff919f6d8d7a68b7d7486f55edff65d4c9afe7daa076a703bc388eafff31045a023d9c7d8b71de98a84c43dfe3ca9a4f47b4a89fc2b371d89405d03affca35a89a3906cb4ec0e13925f6794daa9d6f06f97a3f321cded9e03fd3f37ef3f49fce7e66b232dc6ee352925b7e3e6b5b9fdec72ba1c9cd764f7d74db81dfdda15bd2f9e392fe45eb77ae47c9dd4f1e59b39d9e9c27f70fee6bbc63038080c0ed279ff86ea2f38a893c2e27cbf2aaa7533c686cd74d35e5fd6a2d7e035a452de752fe4381860d04d7ebbcefd26de30008a735e723b30789cbe395dcef31523eae6bb9c9bc7b9b9e9ee9bee6e70bef35a0c9caf3f749c9b8f51fcc775703e1e7e71fc2902b1121294ff88e140497a881daebfa3e13f2dd9048cff6cd73f3ef11f5e1ae22822e1dcfcf84ffb4fce3b06efdf68f80fceb4e27a4e51130ddb18ca9f9e38282a02adde9f26898318701d0baffc37ee99fb95e745dab5c90c04c093c10078de35f000f002f000f01a789d85c76ba5be9ab67954161b36533379f11ff69f00bc7f7ff19feffd1b8cff74efdf55b493fffcfb7716fee3bd7f3f6901f0fa3f0fe73d0fe7dfebefbcf6f876ad81c73703007c5d04b723832e56b09ec1e7581a76e77b23ddcd7912e21a6bdc12886a5c790bb69b786df4c4eb24dce3afe3b50aadc4eb253c533917a76f83371ffbe6ffc6036f6e769cb0a1277992c7ce933ce9faf874926a4a4dd1cddf78b6c171fb6d3cfb0e7b766ffac36e7821eb666fe3d9f06a3c1a2fb437fb4a905ef829afe17ad8584ae8eb9383a5c308bd19e811c0ebe24a2b63b3ef6b7f32c3097faa311280739084c63c55bc815c97bec7bcf2ef1ddce1215b6fd841dd7862c3ee4e1638df27cc9d38eefc30879c62e08cb1f22507cd8fdc4bffc19e551fe2dc1de7011d87ffc4d77e7a5254af6ddd1a734ffcce3e12c5ba7df4b86f2fdb9edb7c153fe50591f76a5e82bc52ba08a4fa4050c3ce387f7b0cd3b8674ee3b418bf6e3adb46148136240bcceb6f4296eb49ae3781b9e176936c4958bcb8213f61f1724316639b63aedf2b03dec01ff334e01edf21b97edd0ef7340e0e36ceef1ccc2efd1ed0ad60436eaac00c8d44ff074c300281e003aa57d243aa1f62d5a753ad13f1ca1f04392292228d8da7f16a6a6cae8bb9e66d2e1b554d4a7b0a620dd8b81b2995a5a9b1c140fdfc5890f642ed3552e8817b45397a62d4a42889094788b80702bcf21f0de09f0f1804813ccc79ca141e983e200ad2367d75216307c5ce22da758b83aef7cfbb75c6861cd526d096340e78a1f3d2c260c89f85bcd574fdfa520abe5c7fa76ed95cbba85ba2b9fbb7cb2a801bda2c05c17497b0c0706318377e77618400a353e3161464b909b8bc8402a7abf92685be44a14a3fb541818dddc37f7e7b936d93d1d87f3640ef90e6016836eee15f744a5f5a12c34a1b5c5eea62c3adb9bc74e504d4a30a94bcde11ba3bc45fbe52c5923405a98de2914e4ea2242a701a49945429334608255c86e8a78200a87cc9a2852b529454b855dc10147343560a73430643c50d190acc0dbb688a1b369214376c2cd79f5b4e62f0900612380c85afc3d297251a9ab2703214b264b0c2873154184141418b21f9b576204195a42d2e0cc5cf034ea88183115dcc40a3684801604c496245104dcc70c35012269ad08204528c2104d250d849d30b239e58728228c2cc30c6902fe145921609bcac608b208680c840c51951bc600d16ac0c99896a4991c18b0a58b4184a51a10416493750f1c568c8a1700189a3a4322570610443fe4722d00f93175b62203382255e0cf9911888788a6a21054c1c3184a4490b350031a6cc0c2e9031642f284a28a18412140b19847e3a3fe6d3f9792024bf2d0827bb8991232674ab93e8d0e142471c4ce2a09c42464c70b0e5a0cde5bc90bf5ca85aadedba7f10ec58accd16f9dee426a94bffd17708fd2416b68b40d9b0f3c9b1a3cde7e1f1de9318b8e3a0835d13350ba3ed332fe4b9d957edc2b2be526a9162d8b3bc548277ab365e8d7cc3be8b27acc0bd58835bbf29c497201570d089bafbfa79bd875d67737b5a2171d5a18e0e1f440e7a10c9ed08bbfeebf29752be6b117974ee06e41dd23a584650caf93ca794f32f29993f9e3372c741c769d22dcd8c548991ddfd848fdc91559e9b796177436b2dad17eb593b58bec8826707cb175888612db161348a0ac1c9a99fdc909db65869c4a4710eeb533ff37a73ae79d870db4b4d8b1a5ff6566fc8943631c6a635ac529d615c09e52006e4d72f2c7285dc3021082db9d7e696d519c6f63ea1b4f7cc431b438b74b52fb45026d3cdbc30c7f57046d9a4feebc8c6f51381547f5d9432ebfc88f1915c6e304748d79f16a554b0f19de3e19ad1934d791cc55a280cfacbcf1e2d61e3879a110f4fb74ae909db4a9b63642c7db982ba5df3c3661299cc9d5e7f4f05906101ae1bfb89c9ad377efd94c46e0eba00f35ba526e5176250c8b8f9b7d14dbb9e3ef324900cc80764fc6c77a9b599737a3c1fd582f698578fdc887dbd2e304629b969a4b26dae26ebcb5a2b83d8f58d49063120588318563ff94384aa5555c6869dada95bd368aa544d7c8a5938e8aa2f06497d57dd8ed028aebb9c17b3b80ee6fa77eca8b0bb5eabb5dd6ff6cbb232411c7429369eb91d64d8b0a3e22a6aa7a3824420a1f814b378ccc2816c7c0e262af9dc302a81f166051bfad3752f8a1ebf70ebe2865d9405f7708950d913274e9a2af6f16d68e44a2a6b14b6378a1383bdfe62f5eb566b535c7d0c9d11a785ede7b4b0a1bd5e6b6d4263615f087e6d777a35d5840dadb5d747a946697c8ef10366726a8288175e50c94ddb68d6040f1b073dffb99e62b452f9fef66a3632cbb2bf1ecb304c3a78bd1cd362a7b24a4c8620657f09a1d6f9d489bb6595a255ead0c7c7dddd7dfc8ad76593e0953f469508d036c633d62a7190bc1ec6863ed7630f065cd593fcc28af4d13cd9fe352a17ca2f6cf7c24df6173694df7e4dfa4ddf5f5eeae900f1e7d27d9c6ad98ff59a5da1c572fd65e7ef533f369112e3ad699a163d2f46b0e9876baba8cfbad9b74fd45837874b4771ebd74ff7fa429f29e3e72fad0d32df6df6049a7ccdc9293e45a818255bb228b6e294d88a403af04fa482413011687e3129ae64f8ad5871e0b8616cd55a2f6b69d7cd242cc8417efdf23b98eec0bf98d41bc897ef6138287ef107ee91110cf6848def1fc6a4a8286f79911bf9912331313581717a82eaa3d0a72045a029495894f849489123dd62164bc807519010bc216c1893aefc6a3b1eea2b910cc9f7260e6a2c11480ec9f729d2a8fd4252bacd1d08e90ee2d74c7883a0fe9a852c869832f3843bf70b59d7b7af99b074496bce0bae38e8feee515d4486cd9458be6127294d24ad0423b5db5e070259b67d20d8b6d7b80781f6d9a71379a862c082fd397fd66bf69c1d51f6dca3af906f9ad79a938f08e443933ae684cd3e9fb8e217f2966c81465fe30e3650efc584022614345d2d6ecfef1fab37a04f3fe3a0fe82b887be4f04c23ec56d5a56594a2aaf485ca53e565cd1e76addacd5ba2efbafa090b8bae2e3aadfb126aecf9600023c549f39228cc52bfaacb0a358ed20f321ed08ebdfad285a9753698700b8b9fedc1dc40fe58ffc7874818241befdf1e812056fc0b73ba70420d88d1ab8f19deb913509bd866f140b262a08a36f0f8debd17da9fcda2f45639e8a75392d5bc6ca403d8032fece38391e7fbb63142d2a555ae061de185d960759ba6871439e1492173137f4f1f2e5863a588999a6f42e51aebf8dff506fc1f5ffe17d3bbef24e76ec1457d0346756a9986cbf3773d85a18dbefad4a31732ca3318fb77367ab7cfa7f7d31727b4a7e2f394872aae95d7f2a24b7a7ae7f8ca9e79efb2fc7d0c2ae52581e24576befc6d5fa6eded564bc71359fd73def6a33a6a4ce8f2f0cfbe5bb94dd5da5b0d24196b2ef191a4f19db1f73ca30337bd731aec0364717ae58259d32ca4969bca262d9755d15c3ea7545a65dd587cb07e9b4b5135b7c818a47ce4bcabf45729cda63ad15cb32ac3a4559312ca926c9eba38c91ca0a058d3fb2496bb7b18ca54826b8aa7bd795b1b367f7c4d96196b15d5297d425b57bbbb7ca0acb6f6d36ebd815d6bf70cb324ddb2cd329a3a7fae29dbff145d7abcdf974e3699a5f07df1bf87334e2a009df1d90c01bf837c900f7f887dff59745c85c2e7c9743538433c03e1a86cb673cd35f39646c92e5ce4a86eaaf08f734a931d6a5a6179cdcb056d1a5682ed97979a94b0a4c60318a059e6b1d8c12c5050af3c9c1d2a1d4c284ea725de1928592b1372e2f71e9e11ac2cacb4b5c7600a28a15d68b94f284145c79e5fb00a13d2e2b78538d945c806106aff8f2e08202aff8fe8d3fc1f08a9d6a073cb63cf1067c43eb48ad56cb490ddd3a28b6b71421b764915bbcc82d5f18e42b772e493d9512a8bdcb8f87f6d893bf939661529545c5a4524a62c3578a1ebd07db4f79fcc38f52d28f2ca5f4e25f1e0921b8527a15eec58a4e527602edf6d5a063a42dd4dddde449108f2d51b847ba51044a1269e015e750650bced5e06a70e5963db12178b5f7fa5d3f1dff74e4675fb54f477e3ca6bf90aaa8041c44714c1a0738718a1f58b51a03d1e51b607ca694524a1a6764c99591514ab949194a2965947c19a141a7d30e06e0091f4018087bff582b6075ac0b0d6bc3b2965e55d259291635cf475cd56c4aea831b134448dff8cd3b80b8c79fb3c286cc04828f0f8880a40fff618d6e12db38883e77f933cf070ff953cfc70feef1979e03b8c73f7a40b8c7311f4c9b0f1f9763508ed41b8d448e2a276ccbe8374ab69f856eb218a11c639448dd4d86cac6a5545e0fa934ed3bfbce346eca2ba9c949aff4c08ded576edbc6a554d6aa52dc6684142a525e63b4a4ae83b2369dad62a3010ab002d2388c93b25b6eb27b48f39a5713bbf32307aefc815d39342753d7755de72bcf3efb70843ba10d081a39a2a9eb6a94ae4ad2b58693cde1c36c80025cfebf9e8328f6d9eb532df153f2530e10c7f3743b36bef2ebfd2b12e2e0d9c958214bab35b316ebbafa8f846dc992758655afa68964fac5c475824d5d5e6252838ec144c609b65e5e623a736da0e20bd3122b3025f1609ac2d42aaa94ca0c4e9a88706106afb66ef605a721c0a0c1c99421b27002a525ca27074bc792162db02c3cb11b6c7679498b982df6e6f2921626a692c5b9bca4c50ae562e9e5252d53b4202dfd40c495979796a610d3d65a5b6dad4f8a88e10b33433c3101a3e949540e694ce1028a102d349dcc7022a552527bf3e2d6a52d377e67a3ba98381b3071c48d558b20280ec04492eb7f030b1fdec696f9b238f1b7c1ed70170460092f0158e2ccf5afb1a093994a44a951259934df0db7258a96a072e35b6e4724a384962454d60a0624a1c4921245d73f656da8584ae206ce0b253136b8fedb164a442440b35c28156566a2c0c62061c301ae6761fa86db11475c0f83c4d175b74e4b47a4d04176851143802c446689309c4c34e4cbee12eef414a174978c60a108186eacdd96d466134044981bbf6b192298b891d38336bf877ac8c9917b5dc44eb0614fc1110ab9f1d993fc811c0e366c2cb7bf8b70a0cd5758e447a4bbb1aff8d7475f3fe924a4468aabfe1e63e36b5d829432bf9e22fb0c1bf694db2108820fcc57d243f3fbd3f1e721a32e8a4060fb17da6e3eb534aeda3ffa5387c7bcfe94d53a6e6799dc4d958cc63c618d6358b7bdc17efbeeee2c6b0cebce328c8410dccc62d95b30029980e3415080074d30c08360f504f8b0bb7f1987e706f00ae0adbc0d87c74306f078a8001e0fad3c1e42c166ad4d334b5260a06f16aebf9604d4512bcaf5e7d0c5b81e5b4320dd68148f22d2f5dfb279c57525d8bbe7bcd61b3726aac803a556a8c44e9644ad64686c002000000963140020180c0a870402b1583420139555f914800b7e8e427a5a9e8ad32cc88118420819630801048000c8808ccc8c4420005fa000c0054467767bab6fad083a9cdb8f06a1b55e88c7f4edd1f2d5548cce2175c1ac76c0b559f3c57595966048915ca05ef501bc1388c894c42e1d07561062b44cc78c0266437c6918305996435af7b6ce968bea311e1d40700590b836da5a5c271ac4c6989ffa72d7c67a8e6ef40e09de57c589048126f0c66193fd880ebf8c3c604f1b20d489776532fe7f8a706bbeb0f04bbe6497d7be4c4b6c6dd4834c095ac102ec4c5b262526242b1f2ea6528f554f8b37aff272061a7457099544f3498c655b6cb34350b0eca2383518cf53cdda72aa1291bd9e31eb699643959e7d6fe4af9596b81ac592fa97e9dc7bb40b5230117045999307b1f39f95ad0096f6425f0e9974ac12af7d7eb8a69190c0a511e1330a80a8088efcdfee6f42dc7b7d8799d8a0ed0728a2122cf15a4427979dd6127e2ede1a27e424af8e3c8dcf70d4ccedf024c2682d69f60703da5b5a18a1a881cb9e47954046484633d81c9af2b5e85d5f971433ec8d947ccda7c69f207a9952e0115de7d49c7ddcd1d8cc550d5aa666c227da391d15f6bb5690191341a8c96649fa7ac4c32099f126f0df73231086352e105c626ff5603808dba7ef5f12517c65a911b9ef89d998a0530e0c22f2dce3a9c4ac34ea2884699f05390916c482524b849b494c55f8baff8046e2f6f4440d54c91012fc934eccf10ab38806896d79b4241166cc0332a48dd7a048505d3f25ddaff1ed75387074b921115be7d9a479d91ecff6a07255b4265cf8c2b5f1043e12d0685bf69a7d323cb7ebd48e70f97ac553a385e31cee323fc92ae708da2066eb275dee9cf2bed44442252ac593b3fe4faf8d5c74a1e367d78d2ddaab58a5b367519121efc6fc8e340b33f20c304b650e36ff356cd7f25eb381fa747c9dd9cc15d8508f631c0c9a5d5fd80e92bded3111f610297a4f2c21d59d3fb51988e3a9ac858126167567cf269be05144ce819ebb89c53129ab8ea59c804d091fb49af336a3e3d1323b67801dab818a901be5df1d88c6aedccf90854079ef153d91260796e94af12fec676d7f285532ff2011a076b1f66ebcf00ca1243d12d10d93055717f46f9d8f774b245a499da13341aad6b3480d62f1751837fc880c7a6fa0cb058e122ae2e0771f513236f9c98a2e9ed1925edabaab97099af028281c0a358c3d7e3c4fb055a0e45d94ecab781e6777865425fd57212b19e4f55ffa3d172317eb3054d3a48ee1fe4a7c3054f371dcb3e0e1cd7efd076ff8333bfb77da5b3161a8a9b2d368a31cdb18881c5a1d4040301718752334b2e24f598963ab8efe37271b7b90edcb43b63a06718b2ef823ab2c710eb7cc23fc5f5a9f3a80ea2f1d2855b60fb2f4970d39553001f21a70653a264475c16844d9f8642df9c6a3376b765127398cafcbf4cf2c11c7263324f19b682803f3043a433251318c40b228d43d18b9f82694ca29e5a599b9099f91b1ac41bb767e45cc9f12deea30eaf395f2ce7e2ca18a95010e2910453f1a1f47fb3692d596be5ea0e1feff5be3672121f8ef324e5237293463ca44477d7637aa9e1a9958660b1d405e8217cc66bd1b5e7b3653da3c6fb902eab86dc6ff88cd15d2d5108d556ab4e69381cafc37dd123b42ab8be5a926bb5857d181fa5fd310b53162ef741d7d919b31bb2945e04452cdcadd6cec46cf77c9cab60d58f5a317a65cb50ac45cfe8d6cac5eddaa4d70d71f0175d43ad0bcbf4115a56d9ea8cf60881bde24a3abc8129941219fbd7678d42a77050e5f4e9c1ef68cc31a6c029f701a0744386d0d292cd905429af41dcf38d6b4fdec93a336cd0e759afaa1aa7432284499d3af35d5e62b0186b2e451280a3ba340899a9edb1b8f1c5c479fc80dbe7cec237b8eeeb754b876b10a333255bfba9be404c7cf420b5c6668599dac448d8ab64ce83e2c4d1c0beb94f9857b5ac7c41b378d9b9bf91f5c8ffbf0063deab98d802f17cec889c083cda7889ec7d57082d9a1cd3ca81a0d9ee7b8bac9ea07d2927b90d137784944bce3c58233eec0597620c1cb9aab89c14582450bfda2ffe8b85b69e9e0a34e486059938b2319928a67f8b794bf12652eb0d86351bb53c85ea10dfa26d8d6ee9eb088c9ee1803268a99d24e10af3a3c11fb86a5560b57052a6cd416b7eab1a5a2f98bc1af026361d747329494a8df0f3907d8c8b262ebc8fd7c9355573749b3825d8bd34903600a525c5b0afc6e6fe27b39b311f7bdf458331364434cd88e67ad1c5a4aac963018257dbfa2bdaa87d435b2411ef5fd3fb57dcf7c73c4b74b3e89ef56cb6ab1b7ca05b85a31e9bd719eff1014845c895a9273b35a4370bbdfca12685c11be3aa288078957ad6d415ec1ad3dc842b3c321e84bf600d8798c307d46cfe2e95f73b5d878a8c9db4bd262b8c2252b04961c954c8ed66dfea230fae5fd87ee21ea3f55b84a28ca88959e63722d2f6f094c741d29097e52134ca76f229ca912bee63f0f766dfaf7076216842b93d70d5f28de4123e18c1051c04d7fdced50443ce3e92f10cd5f8295481a30c95c4c0685812293dd47e41724a5821430fe66f4ee82b9e9c8855216c61b12fd68b1fce094d1f6da46272342e6e9d174acb2549fd9df3437cbfeb5d275102286c4023e22ae04a927614b356b400139a26bafc9f4f3e3b758543c2688226a54500c74ef85198f9c53423053532710fd449f47580202e35cdbbddb3c2395326ed50e8ed5a8abda7396bebbba94fdc69abe5f36d559a7c5198384742ef5d8ff022d30802fbe5ea7ba4011053146b814aea3d3c49077e04f30246dacc6193b2d7e2e32bf230586bcdd10cf597ec322270daf7a49d835015e725c7c530587e1c9bc99a423103b8b2bc84751bd83add2006af3fef9c5e154e11474f30d41517a7cf2f13d617d9c66006cc5717420c067d954afc9bb652b1e60478c36b6bd113a64f8b0ccb78dd4559e623a9d51d6f972d07455f3ed6769113a2bf92cc282201ea0e2ad7a33add9808f7c533775401985704c1012c62142b0b7f33fb21498328e3e560951e1bd2029014ec94e33cc7625436082211bf6714eb9f9cd62a764d664602b20d81061b6a0e7dfeb7ae765af87ea9230900d19f43aae4793fd4b35f13c9b9f83caa24d23b6cf2f2c671cebe5862f7ccc49b054073a3bb1baf9063a0c1909333d4435f2a703a2522b990341e5bc5b3989ea95d14d749a273bca8b0c9f46aa82b7a7bdd171e016bb366def456050bf3934b32671c0b10eb0682bcda9e9005449bd27f1061c092e9a67270a54c44cee61f5423593915c01847f806141a88a9e33fb7bdf6cf32210dfca6bec226598de3269c11d1400264cc3fcdaf249901b70fd3adb603626e957996a36cf7b3aabb67c6833697e255ca45fd31b2774c535fa054fe3d75fcfc01889aea655554817a3520042d649b6d61a02e6e06ca3ae633396da369d8bd4d757f13b8a0f4f36b763adc6bd22f47e2a829aa19c87926b1e002f5209672ef3a055186137dc81a7e9fe0bde4adeb793ff020744cd705b8758fbbf3f3cf010bcf8082cc762aa313d0a7724d8d2fa03e793d680621381baaaa1463dd6c58447c7382d854e527b576e10ad2e30e524d0255a228cc5a915aa50401d46c68ee47f53be5f7fa3d9cab9217f8c4759027b21ab2245e481ee8aa497d41ea6b8db01f1d8d48efd0138981c75844a8063add72fb7c756429969570bceff23f884a7a6046e7328a367c13d347cf42d94352394804959e1daa178da417b8a136a09e5c3538c903eb8cda63152ca0fc03a5d1690a73b8ab84cc1d26d3f302c36bf423c403ea31af28e1e05c40860c77d1ab9a6cf64ee8525e73ddc31b22b93ab8c95b40f0fc60ce003c6d933bc6a4c55784c88a692efdc35eb68d25bb62598bbbfa12eb178fc844c2fbe7b458a65f87b935cd289c3c5dfc0ad9430ba2d48e3e1abc3632607e1f3b2bd86010f0525dcaafb69d2cd72592bd95bbce3accf0334fe18ef9e54f9c399ca3340a28e43e64ee13e4fee6390deade835ec06a2b32cb79472330e3fd40125edefe9535e1aaac59cf70953b3d8dde4118e96fa43c6c03f53687e8516136a1743c5f249a630870a9dfe62141239aea350732fc8d43b9cea3952574790d6480faa2c9348cdd31f7bf8a80229813e55fe1fdddc9359c07ebd13f53ff0724627193c0db9e5b52060488ca57d37f44759d51d52710782e4111c0d09259c2a9e37c5be9c341851078292b4f2a5731943c6fe2ab8f7544a8e7ca52dcac1b4977f0a097cd28105ee7c32f555f39e18839ee60fab0d150c9ff1d5c880ec5873a7549e4361c09915a8ddf89870d11a9d42242e17af1f618ad137c14113beec61188b31e135d38489617823fe4ac305a8cabf36a6a008d0d8b86d693668329d9e0203038488d901b3ad46b52d019d789cc3176e6bbf0a71ec111814d374efd984ac1cd10e75e60b5350c9d0462242b5036da69a95dd40992b4bf0e0b8e581a994d2e6fd62805692ad63bacb0fc7351b96aff92e7f7df9948038c1d7245aac5952f4c50b07650e9dbe575ed620e15ffe2a8c83a7638af17a021d419798a8ea611672bba7191cac5f35f1c57467823b66793cb059a458bafa94f7a0890959982911247e4972a114b2777c633a944577efcd58987a17c6aee25f985c9c6d8a5fde0dac5c03b88e27e8c8e700b6433adba354063bc7ee74675b3382832997e23edea77d0648644ac1e78b9388fa53d210361c75e11686dd9fc0f8f56f256bd8a7f23a8d34d9c95d6a1c12320dd169f300cf2fc5ff4b94591804d18a924f360dfe1de2198f949f7e07676faedbcfaa963bb2766b5edb8e6ad97a4566599a966e9bb7e1c034a6bc80caa23584c159b1eea3943ff2c6f406ea0a7748e87f98854f5ffcc490b37e898b9876a7e5be494e58be67c39455111dcc6d131d9c7fa617a393c7f9bb6860dacaeb03669ecb4dd9b9e6cd641fb464b6901dfbe556e31627cafdcc9bf9b06e776ab5cad4912cbf0d18df497859b7bf1daf76340203ccbdf0f77802d0d7bdb1a30456acb881178debf398713960629c7f5db8ed9a6dcfd6d24adb699a950262ab07065888be3b6aaebd0f004fddff8173764f8698e6deecb1f1881bc7dd68a88f620170306747218415e77b0436876681e61b9580a8ade84a076f6c337eeeda4cc66c9854a2c308a51105b917116aea18cb4b039e9577342ced2ccc47554fceb657290471a138d93007435c8dcbe9607b88a2bce31095667e6d038e5f01cffa25b7bff6491fbc2d8e958d9796de03967ebe58a7c42274c91527297cf156d0d29cf729c06e2471dc856249feaa46f50c4383837cab8c122ec99bfedb6b28eea07aa3c7041ad4b379b118845f30399305c91064716857fb74d0db06b42f03bf7515611a31f3d4b9192671d3674034674e408f2f9941b6c745d1d32fd83ac9932e2e5f75c19f1a0de51e7aa49871180b166e59392d07713fc77ea55ec415f369555307aa091d9bc0c2388703f793600a8e00546b12e9afa1b6d1b4bee6a44bd823a8173e85caf94d7b8f2a2ec9bca0303174906c53e86d1ceeeeab68f781b3f8a7b407c8e32fb080c7b2c00f12f4f9377d4985019afac0a4520aeb71bb4a57d4c31dc3843ebdf0f03d5654d880de44ba8c1afac9a71cfd9067007adcbd5370030da7e3776b7655782888126e4a09a372d4f004219820a60bea1f630d07dd3649b8fbd5d7cf161760704de844edf7566e5c680f1f57539c59c9101ae4dbf0f100c07b578eb6c15d50088b01b380ef7c9b33a6600c502eeeabc220584623a27ae355aaf1701fd946ed43ac70e4299e9058ee9b9d034d808788f7fd6849737cc72722c0ea390e186e88318fbd636bf53cb945d91e1c7d8a5d1e15d6ba872e63cecd633c3ab8e0a75fdd8192f601510316963f4b9dc674205c57f317e15454440b7c372de0d9977dee63dff36fe8b377ffc375089a21f7af4bc0ee4ee7a7a05e0fa58c4cea80a2416230d1457993e13e40c80c1390e3523891e006740dd29008de1e1233fa8098b82d0a584783fe1b2000ac94a72ec0b1af8fbe756e8acad0f0f14e322b3d98451e1986409f2a6432074ab5cae85ae352b57f61db6135591fc72a7cd317fb20989fdf23b0b6e6b643dc3d5beb4e1829ec792ff7e697c21a1ff751573fdbb60c180408d22549f612b2b72d1add7ee1348ca289119add76b91f817990c08844dbef1b53cb2220df9860f44ba8ff2cc654862d49f3a0f607a3b6cfe2c5f702513e9a67ea6ddd574545ec425b468e72c7d5f3c9708cf6e07fc37792f551ad76713b4d9a0dd4d71d2769398312a1bf3f9f68ef3096536b78048a388ae2b0b578bea969c2fe49af076b8d27c2146b97bc839e038561871b6cc22168d6582f4158f482ee0baded1f948e61c74c96cac43a66456b35bd3b13221f2b699e1c822615c5b371a22d238f098b6166b092aee05a3050c710fc4cf910ad6c7600c2e59b004a52f30fa911bb7b7c119e022550542acf684f61233a23055ea36b2a80621aeac416f1603aa43dc42299d777b33859e4e3d40ef167fd602ea3b42e479b32f704501df1a0d8ab6947984c1d28a0b6be7ed8044ce9e1d77c921f91f5afe6edb7fb67093ef926588f5810ea636f14ba282d58a355b8808a36f9274aa108d857ef107111088a19286e75e488198b1ec1e4630d0427c3629c3657dcc4d49fa0b2b1ea490f70af862baf75b131c0a29cdce9f89527e2d64010cf59d854d7223f36cf2d7f42e2f168506d099e08fd579ee50c822a54c56585850a7b4e86ceb4eae231b8d28dd8f3e8a8144378aaefa3c34f2168d40797d302b0153008e8efb64ea19e9cc2559b97111a93ab9f9da7f70c158a4e835ed2bd78f95359015faf58886b59b7a8743599f4bde9f180a75fd9631bd311238939d336f3c4eb902a29422ad45f828fb57e43c1cddde355858c5b86bffe51d235609544906a87d9b59be0694bed1693c82d2350d2e3e67c15216aa58597eaa926018252c33b870c6822464a492092426e9f7509f7dd454ff67c8ecc2358be9837697041a880f11c49b9b6e2263b6c8ab4d187f414bc7aee78842c7ad9473c0882f193a321ef2e590ddb2839448ff3b131cc88e88a23f29a39589bcf8018a10f86e760883d0f50d197cd68a14e41562503e48ef3c07384b1eb1b6b4a87b426ac76b978eff18cbd046187c4adf138431c5d3148f2d141ce4145968183fbb6716c86b8ee0c120c3ddb3c90c4d0a5b663b24fa7666074dcee2bf3f95e369b26ef327c83b50fb42d44d9358eecdb1f6c0b409af77637838bb4fb5fa7317197fb461b87ea9362b8cf4e258abeeb4c23059d29d818a5e2fc9aad066335db8a6f009b8e724c0d39afe47b8dee020b9bbbadbbf68592c8715459c0134f1d15ee3393e0c9dcf34d05f3e479a1a536a97eeb3b0d64934a02c63035de7ffcb7c6020311f3bf311bc0a19b5941eea00e92272579ae1968292c16c8ede0ab016a8207f4460ca273c96976d3d851d3c7d767a58794dd48da77483609a1bc4360423412a1da52ec6510397fb0f3ae2fd7cdc40976b313e4721676b8d6a4ebc031a413493a16717560938abd4635eb4440a9074a4d0398d168d3dcffeffc9c43952b8f601441418c85632d0a45a294104b2ac0207153a06e64d5b2956970909b6f0317efa0284b09560744b6925b6e4d87d354ccbea4c538336c14a08757274c6c03bd694a313a4d5316ebf532b7eaa8d7164ba1a0df27457b6cc7a7d6be0ec2bbea2744c8f84660f0aee11ff9df858f41510561624a090e94060847f8e90a9a1812c03a63fd67b04ea8c01cfa47a90dea13f0d19065768735840d0421fe95c99c8edae56c3160861e26fd7842363cd15f4d34aca1f8b7f69c7bf3caea247b2c06f4d6bb8375682e31ba41267eeb028e326d274ab74ae3b82afa65fdbd93fabfe93f98581be1cc532de147bad26ef4c3586116e379d256b343f03e57d183a6126d5436a2bdb86e9401321efacd24e801a813ad45c0d705723bad70ce5fdc7a798432144825bed71984bad00990c3ed5bc86b212e2be88aeb524f2e5bc101147b4a89d3730c3a2d0129914196f4a070b75c5b71d8ce081c9b9f1e989bc602e2aae1e44cccfe39dcb64b844ec56d1e1a6d8c082348ee634ad90c28c4eccfb2ceec3c4d2088e704428abd111646ad54fa6e2c03072ec44d099740e7c6c1f3d48742bf874ef91685d1bc56180219b68cefc0323dad540e07f5a7cd200bfe72486112be8bb45112acac586366f45d694ba65de9fb26be98a9cc6689a27c74b3cb5709517dae0e2dbdc961b21e9c2eb1f5bdd7025e0f55cdb11b2cf796c673c1fd61be4cb625b7e01332eeb5e501029d16a8c8b8b4a0f26ee7766ec868281c4372b7d3fbeaf3671c4f94dc58c63607113f37b91b55f65089aa0980222f2b3c08022bc6ac34ee1c6e91c3a7ceaba8819930525ce0ea891b5be06f28de3c80e7f537d80c68a8491aac38d60208c4da5b94cd15b8bb15bc0758341fff15a0a1a084682a03c8956e466817f5b628af61009d2158125986f99ab56af02c4d76d3018dd9b6b8cce6bad1ebf4e749c058ae925558a56e7a73a77078d6882862d6bb345c2635b256c8224858a7be0676bc51e3c00b96c668592274bfdc2be63c97d58dbb47ec6120b89901f7ba17be2e97e090846ca8011fd2ceaa7f6b16484d4fc68fc14d32b76bbe66530b845536c87f21b53154e3c5454022e861c34364c2d380424a2ddc4ed1ce62041213826f1f8f24d28c530dcf0fea2893c24cae90119df17e22d5c100ae145456351ecdd73937eb6377480386e9d571e9b064c29601fe77a038d1429e6d3dc9ec1e1a8bbf93d21151edf031158809408f69f8ce2863b7ae8b761d943aa3c215db923a7bc01bd082769ccf8a63f4672255a282f7a1c4d23230c9a7e6171d8e3b97db2c0c3cde2e0fae89d014fb0074834613d4f2b1988341ac9614e704ae4ced2a07bdc01410d094b3d0d6b70bc8d53fc2aee6371e972ea00f84a1b9d8c8bcf6453abd478d788008c5c2206cf135e0909ed01e5ebf2fee40a0638f0f76d5b5830e7ddd671fa17751a93d268640beeb8e9618fa4de615ca0f0443fbeed84d21e5ec73498fd1215a3f5a1608b830e69085c28f58508c0b3ddeb1b2a28615d6fb1878d5af54978b243b2c239b9bff025e6f010c48e0e34a326b925d175057c03a3f405fa281d8672fb1a13f49380431bd12522b0b8052301f478230b9c093d8b5637c9528500a59efb3f6f55352984a744648a05bd0a0fb83896b0f3729634aaec94c14a33687e7e6bec0431693e72257da8a33b71ac4e270171d63b02d0f2ca88cfa584d402b68308af9438a2212334ae115446cbdd4fac08a962aa0997aeb7c6393b3fc4f800721f6739a0646e87df127ceaa30db6e20425d702d495b52264a90eaec8a15ba35cddc30e790d6350a8de69c3d3fdeb8290716477cd3ee885958f0879ac7c1739e3e51fb9c3d7a1400dc18267bd5aa32d4021661d9ed8280a01ef3f9dd7f0fec7030ad03048f0645485a99688dfe17b406841f28a492eaa011d54a9e1f6631258f107e0d7d65d2425aebf0c30a8d60d1d335659fef4247c2a82cea7766be8d4921999d81ad88391deb55caa14834dbacc6b403bd8c48e7daf6a7dc3c4f915b0af44921fdaa08b41808f2d61c87a98bcca4db40a3ccbb86fdd61dafcd05e6ca8d9db4ea4099958abed8e8924533424d5c83f6b9a5990e27e567374e1e1d7f57702c328696480d7495e2b881b0204d64205cf2b1e03529c99ad262fc32d7acf95565789b0c7549e9225b2cf59b11e5c26ae0a0b382d229ab9bc26d04fa6136b700e5e691e6404753445e87f02f0bdcc7f9851d5043026508290add7c2915b51f12e5caf9f041adaf42d8394b039a2bffbcf8f6e2fd1410aa5be24212d6f0e164de4c990aebb8a3801ca1fba3177f0ee03da2397677ef9c5c68effc57be446a2590e054c2dfbabc9738d799f1d875e77ada3da5f464eae0cab452a635d473e4ac9c53157970971a0f826e48fe18fe6ffc1a06a52a086a84dabaf4f6bcefb8fc8f033aa19c52113af4ebfddc039852280c687c3095a2667e579b6ad372df8169d113b14371ea0970054fc2b151627855f38845c303266bf305f53ee4caf9fe69c309488d6504a8da30a1cf94ebfc5c6f17a172404de309e741dcec284b516d2a04daf5f4c03d55f3e11ca0368c1614dad4583b76fcd81c9efd994527a458979334b42f0a7259027389aa9796014a14b8004c74a8e6531a14ec905501518208f3ea959e5c235e6a2fa5dad6bd32b29452539e033bc15181bfb42ed24cc127429e477b650e82f5f4d6095cd16433bf965c0b237038063567de7cd6fea9698f64e5cc248bc126dc8d7db2ffc269da75be05ae32f4e0a3af0c113c5ae3d8dc838093661300343cb1df5dc0b79771d21419df98cfee03404114b31468d94f9679930ff79aed5bf2fb06ee67ffea1caa397e7b46b3e55ef474d45a55ed0d8a8afce2a78b31884254556a492b1045571550707a26b61d09c2ef61806148125451cf0f330aa98dd5501198911ab1b82d1dc931ce1d8c35ee3d1d748128c4a1480c730234ff6116f1c30668a24c15ed462f1924a52a00c00d353251c6abab185369331ec1d177c4337a4d4957ed697b68b64b45a7345ea55cb771a12fb675de1d31724a0d72cac14befb543fc0547405ae3dfdf350ee7fa552506c1482f9d57557b0a900ea4f3c0b4981178b0b53514c5393c0f475cc5a8693cd58e066ec9e5125cd0c1939c9d7de36467efa674a1e0fd6a27a2dd462a23ac852589cc4cc6832612dd0ef697d17e2c1ba838b445a49eeb035417f60d839cad651dcd7afba5a4c3e4e3f0e3baeee035d5b4bd8f0330213d67671bae285811fd9db86e381db92194961c515ae946a193410db66aabea274f5b1be39ac2eb61c006278dac5f194b888661a0596f0c547bbaae936840f801c82ff2b56e4366b24711ed92548381ee63afac01eafb87ea5490c1599cfaa4c8a962db87a9487cf716145f30bfb7439d8dd19c076d918e6101d310886173cbcda1e1e4115db77a152203928936ae3bdce64defc7b645da7d318a1ae734ad19b275da9d097e9f0679205189b7fe824c406d5367ba9747f4c3161f18db43e78a3ed42e8eba6bd6089bfa50aa642db4fecf26cc443b1f766ea9ba574cd32ab90bd84e219e54c58d540a09fdf30659d7e4323dc1579d81350b85a891fdc4cc1cd774ca8219a583ffc94b60c0e28bc43ba814bac16d21d579f79ad844af6bcf2b78695f59c105677045dfbb5f660b9268a5c1cd7d62d4ce7d2bda367f1a5fd1784ca63d5d8690556e9dae3ba8560ca2419852c80d25c215d45895eddd1b51ddb557c307156413e99005886492406ac7bd8d1c6526b0c3fee9ccd8d524ec7f85e75f04c5898902bdd424b21d45100ff9aa6a8b11d0a04e25a78e6236a23b0973397d2aa2ee4b2e034f0ed975ec5adc7c8e4bf25e3f43dcf08a668862e69ad3c3025b3eedd95e3071f4282727f81ab093c28c38722546926b74f9c1196bbcf4d249df917ae327d850b5bc56e8d43e113a45f85384ff0451229050e53c4c1d68805a2834c6ce7a5131a369759123f4d074200870f6d8629414c2d4f387e8d8cba0f8eb1861a3ee8e1e6f7b1a9aa2ecab05458c6adee65e4df40ddaf8a57e9796590cbc05d615b987b1c6386a500dd2000d6034f8d340d15de9aba4c5ed95fc4e35ffa94b7e878045bd7009bd792dd2de355d97da71ec89cba8e8e0800502fe03d794f4140cafa79502f4d64282ff574a9aba8fe41da1d713947eee9843801251f263ffcda9eb50a3627ec83770917f72379702b2de5bdf330e0b7bec6f8d48cd4dcb98118f8eecb6eb95c07f97e6aba76ebb09d0ec85fdce43bbea7be302794c220ee15ed772a111316a7e23269e94c921f500cffefaa9dc8fa6c3967c935f1064ce6076f96ccfe44f99f1a327dfc303e0dfe576553f788843d8a314054c64fb9716c02fb4339828650a76e77efafdcb1319940a83a63e05134a906f717083b6c1ff95faf33a597c6b71dcbac743d6dec8c20253dac54476e45d026a06ec47aae3f6c17efb1f1689b00963aa448e4b3b0b3a461777507405c7d2986ab62bff8b6a49d207d564c104a9b62370cade38023b4dd76306e312cb0809228983e843c49a942a64ab9529646d04723dd19685956e948f1a19296cabf6c4d6d4fba671d3c5edfcb838e65875f6b1e886e7fbd783f4ab0cbb192644cfaea2c0b13f02bd9630f85caa1185a39a34a80a08fb716eba88f21a02a70a3f929ce2828f053168cc85dfc4cefae83521074a474cfdb7cd7d48f6c428d985b103adfd4f62f48d0aa3d84812dd4684d685bddc06532426129495952180b13843903034330a535ae5b02168d298bfb5ee4f8194c6df038f01a6f0393cc7880b578207291b006b54d48c38a0c610aade3724d81309cb3691b06b4282449cf0df149fc4f584bf2035bb6a0cbbd8e5d1141502656b5501eff6b84612e4057f2ed58f544d2bff4fa20d2898d9ce31fd533e52c995049fc7c59a0582b09f186a69c9ec06b455add5b46411ecab8c9980279f91b7b20a790b87360579aaacefaa36a180216367597aba34214cd640e2251e7231448d55355759a20d7d63186514a56d5b57c140c1e5977e582fd3a604a31aae0a38b2d7af2820189612adb32921e34aeb0096976c205c1de9c69b15aebf9018561e2e3d06c2a46c2cbd346c5011875bd9d145bb02aedf13bff615736b9d6ddb9d677c625b64a82a96cf943b8ac06e2e32ba20da2d39b9fbaa2b6cb7ca6a18471eada77f8c8144ea06d1ec6fda068ea2c4b67a9840c11aa04feb4b29a0d255c62fdc532188fa29cd8635aeca2bcc4f70026d71b0468852f40f95b7bccae8c9dbdd5a84afb747e18b13a4a937d486c6c06f4d4c70b2b74d14d897327060c72d8e956b49c78745476c9e8505aa334a3e8d91262eec5fdebc96815587cef86ebb4945aa9c56556036e33561750965c6eb38fea5ee46831ceb3102e4a967952cf409112a2d2df08f3f5120a04375b1fdb171d4811eef75d436c55c8ca4c574abfeb008c11c2238948635c8faf7c3fe668e4a22f16d596ec184f2aa5fb9b2ea6c13446da4bd485fc52cb4c3d363c3275890bc5ec060e3356e496c2b5b6f4662bdf04238b4b2eea9f67910a61dcdcd2f5217699104c5bf88ecf1035eb9d2d63750458ef880e821069a6c48f0fccc908d03c244086a2dd61b696bd9d69a1c3e1714e0d5c83f21a4856570c6066d5b6e18915f1988f52c081c63488108807ca945674d5d2ceea5ba35b70815d424361051eeb6744563cb429a6de3341a81085f810c306606434b76268c288e46531c8013094069870209e63d3abfd6b0db1ef1a68ee7540388bb4ab47e75faf2c13b30c825c9c30620a6f99db781abe09f8159d7ddf9a1126243c6815638cca3a6b4cc43648d0399625dccaaf6becdf9d4b32aac9bb1ef17cce82cb9afb210d479ad76541dfe05252d268a4c8256817ecfaaf811a4b2e8d365142098812142cdbd8be6c8624029623640a611902b046565efc3ea02419e0162e806228609ad21709441cd119e772589225ba360aa6e25dae953f40212b9c37489e0012fe98922cda65f04eb24e7d1178620abc8922232a44b625a5258a4421762b12599e8a6d15749e60c41b866e53593e814f2cc3de76c2cf5b2f8b2f2ebe112aa9c5708075bea0fc856e86e2cfb4af52fdae97bbf76cd2bfeb1fff7c82d31db67ea797504c6e3e2a033ffac2215c01b3ea22e066028b7a38cc89c127bf297831f0a3d67a2d2bca6585dd1f02e2f63698a0703e81cf176d5bb10cc1d0db712ffc126502d45ec4ec7a2135b888f304196875fab04240a47d04b33020beecaf027f1726e54e510ec57412f7184d072b5b282a7d4710817ac23732f7ff1319d15762ebc70a25cf05d723b6d98668bf478f0f6ac26689b163a3e1d4451a9cc42d513b415c75edfec99afa897e834a65114b4011558bbbb47fb696a5946344de18ec2cc5188586a2476e2d7f368117275451831f868d594bb2b050eae9f6eda2cb5bb27ec8bb6cc549ebf99387a7ff9aab5536349d564a1f4d66d2fbff2b8d4f2f2f5f690264fb85332103ae872d951a6e3d4159447f6c0070701f2046aaedfbba24707aaaf10c5ae01d72779bd9be242414a2c562fa21c80ca0f14e615b2a8d10457ee40a42a51440d3b1f597aa4f1de0260908d045ee202bdc7bfcfaa5038b33251410df084418b6c2abfc83b7c864a1bff7972498328bdbff0cb0ebb5ac83a20ab84fc7ee9520eb3da1401823f64a108d10738f0f6f3565f87cda397efeced02847e8ebf96021412e04aee92112e4b25c2888ce47eb28e1d664b277564b0240d80bb8647f870f68d0f31e77a1b463f7d0b3b47c6695466b19c8068bf44d8ef7b8dd17a38e2d0661ff71f24a70ace484da1e91f6a2e2da5fb6c7814f3757427724789fc370c093baa7d85213cc3644e742ac781f4fbe58318fd276303617898e453f7a3a18bf8514174378db947a6cbb67c2d1865c1bc1808aedee1a6fb98bc6d100e4a10abc7a79122b38607775e684202ce0d25eb0f48e213b47c7ca3d1bb0741e313ee995f9f7bacf9f46bb6d6f6d74f8b5166c6f923f7120a8fa4cb2cff93410e2531db7146ed2882075a1378c5a296a089a73b204e3490a00c2a5e105cd72d3c521d45615168e83471eaa6062bd4ba3bba516b698afbaab088992c01bbc1ecfd2288f77174ae4687ada943172e01c827d39cc8de5b2b7ea1403dcb855792b158c9290369081b56d1a0eb0aaf0c920c819da214a4c976e56141b82b582ddc9b711f4864481d7cf7e8ae4034ce636e691c8eb31120540a8f8c71ee33a54ec9209f3e748b7cf66a011379a541313ba5d26b096b1ebf361908334fd8bd3087b8f7a319d16cc447ad500776484891ec6e8175026ab588f1ab836f66a9cc6804fc01886e1ba27893c93de917d5618363c22267a0cc383770a5a0bf6d987314614c1f742d0e351b6216deba2f08bb694f6d6a635718b8e64fc26e5ca9ded86549383ddf33e6371e821631e694a8f81eedb21cfddea70c2b1add8c26b073c1ce122399470b48e519979f19c400a4d59d74bcba9aab113c3acc93c8578e2f01db7768a93eda4ba352fb70f77768f270449f46e8c25dc24d9faab42d4cfbf768162d15623800421b53ed707785ede1035e13acfbc43194e3b4aade09130b14380364e99073b1374a8704e674edc62db664d13276f379ef4eb75e5eb8449e2dabd8452d86c3940b7eff9f159d71929f697645197efba16e7943818a458e84c50638b63eb3d0903ef61667bb3f85ca32844fa7ba2935cb0772035844885cf6323f91c3ad4eddd909c4e2c311abffc8048857722e3862bbf4c088b2e3c80ce4313ffcedabe9851b053f9b8acac1bcdaeea426d31725a028b8abf860671653fae17fc41349d3e331d6d2ca9695275ffa1fa941d769bf3fd9715a31a72e8c606d27b6f9ba34d5e53f95e49a92facfbbcafa6ea1ca6ccbd4ac83f9ac64a50871a85026595576edba3520ab9832244e201987247bc7bcf0d894474537538bb0ff25c10148e3940a76892f3b64cd92dc71eb8c8fcb195d73f25bb941c92a9896ec9965317b5baeb1766d350f53166f465cc01e0bd5797c0e13d82c58dbd4fe0b143a68885b64fd6ccf8e0dd14169cf039e246504aaea013ebe70438660147b06c3ed8b2bb40786459d810f0079f72219b84128886748a4fec67bafa5953b8cf6fd88e2513b91a4fd82e68bd05564805d40b9d25848668788a3c7fc900c39d902f2eb5b42cb9663bc404b96908625dade55abf3ef16e48d2fc84592c3734fb7b54ebe2d8d3a652261282940ac694357406701dc34ff9960c32172b977b55f75418f64c9edfae67a523eeae2810275e0627bd5a542df6a0ea3958b0aa451c4b06f6633ba43ef529eb1747aaca598cf573d28d46a933da12d60f924ba86c5474a2162394642c4e6c3142bb17156203eb219a29420f7db85e53781f3359f74c2b45442578f28dc859e0b4f6144937c37c616b342aaaf11c3816d31ff37e51e916bc34bfc8206eeb63829662aa80859575eb74b79e9cbbbbf5603ec05dd1c8fb6e920b1b3111f6f3f3f591684d20565a8725c009a89c3388e27a547f69c63e5574d2eaac8ff0e34c90c44b4a1d63852106d14a7cb581c20b311d58fc0b6da832f7eb7e423afda50d8a713d988cc1530d32d17f6609831de57282a7352528e452a803322c47bf6c1391a8c6178f81982943c1cc6da4012d685d213c2d9b4cc799f32f9484fc4003683426085453cc72a2b8c2703fbe11ad3a8c412615a12e511cd8345b3c19b765a7fa79852ad23357238a491a9d6918314cb84050d30e953ab20b0a9cb54d66995f8d49c7cf8149f0a5e358b4f3201e218418ced1fcca24315ce4131b2c3d6d169ec0820a6bfc3cde20e5caa6125a01414d77dba6ece9a2f2062da65c95c94680dda6f8c3b140a09153cf18c4e2a1ec6f9685022aaf574fb89734f339eff4ed9b052e3270e0da33f941b08a11044f132898770fb6fc08898294fbe1ac303b49b5155e674d979141b2e14d7d1659f37377abdf21fb324968c9150da81827b9e08bc50d30bdfe7e602c215f0eae19df95d0ad001c1afcb8f06695da066baea20d99a900227b0a01d23ef3291c54f239843510fe94d0c6012e5bd2a7e17404345bbeb41a1260ae8290d1e31755317cdea17af81a1729083e568bd2bfff326af6799d8b2b09d9075edb8217f08dcaf9afd52ca7e7a761470625ceefcb7ffc5a383601e261423219ebec40be7de8124e3b43e7c84b4fe49cb9d31556caf1fd9e5c7d426bf25df795d9752ba63af7c2fb33cdf711f3ad924c65174ede64907c416570052fd2eceb2b52bdc592883651385905a1bd27359b386420803979d494e3134ccf9089faa782ef7a1d2d33063e8c2c86c2b70f703bdcfbd937450222ecc53cd213f0382ec7eb522d14cf03c430aae52a22915c7437c5cd4ac548241ff4a3314a17deec818477c3864f65b84e20151419590fa0af0f0d553b3a430d927e9a24f28a2fb2582818fe94be87ed6ceb19cb63fd76aa716795839d37629830659b012cebe6007eb24d59c6ab92e6590ea60033e429385ae4eb9014b7441dfef9d59c537b566549dcec59272067490858ecd502523da2f951f27beb4e418a65151ddc9dea0ab244a0814a9b20d8fe1e01e64f4aa12c1e5fd3e561d5a47a2d6f20c5521c21f0ba0aadc6b6923df4badf4840a24831aea0a70e9849a88c8ca61961a29a1f2eeb34c97ad5401f213225f5af3dc446ee4c9336da14f6afdc5de858ac3fbd0e1dff2822199659040fbcc091851c3a6918a8e043b432e1ebd9b22fa78a20e2add11f6b4c74848c8eadce19590de72aa01ce6d5a8aa0fc982dfda833372853002f812a43f184675058ba4c53cbd70bbb1e57447f1ca11aa20c5bfa43ea83f2d2be9286929b016f941906e4d9750b0090f17756efb2528546357744986ae5cb2aa90a7ac895b72962c51592da501fd043ef2edba2947bd0887fcf31527d09ad21a9de02c13d7c212683493921d3f385a8ab7528072297949af9b336a4dfd49eb43c5aa879cda000bc55d386289540cfa8d0997b5915d2248d3ceffd1f0c8602775e935e6d9eee3d8916d7e1617b10048b5d2b7a120e473ff997d12fea7f37cbef93cf58b5fe22804e606733a027a384f31839e30a2bfc533ea5749318974530a2e4fb6bd77bc90bba0eb3da4386de9789e1bee6d9b1365f8004994229cdaebf8946748b60c85e42a0a822cb2446aac8661ac5ab229d1ca1d8ff83ceb2e52765cbbc62d2cd95ccc3c52b53c745f4c86e44830e1979fc3286cddce0d7371bf08949978e25d685c60cd1ae50001e795721f379f8b73bb10a9418b8e5cc5bc076144ad5adce289ab6818546a34479788c629d85bc706b67f0d0bbeb7b01edb345bd2fa51b511601136d2ad1bf65f0a1ae77059b3729e1f450bc0066874d612020af436177c7ae29a6f179b8e5b65a289874e4919e56d61a34a52a84c0df2f10387bb51770ad5714113484ad68cad525b474ed3adc63065d7acb52c44e2c6a654a34cc765b2dc8ad6f27796a07ccf444d7f22b375f33492bbf06e5434d2b1781fc5c8dd828f52b8a10378c00dce833e1f19301062f4a7193725ee25cfdbd62986935e1434ce987ca8534637a3da8d45e8068e927027c5aaa39d8017285d8a1bcc233ac109971041d5cf452bc66c884948bcab80905017330e28b7fb8162b5ede4f31f4a644fb037d2df6e3f2f5905eeae2ecde17afc5b8077ebc27daa037e7aabf6a93f5ba09b2b4a66c6d26788bdd1e8ff3d70015a31f404b63217e3b9be8152417a1eadb68deb2d8a3e4855870c125bffef6f2d9efc4faf29cb0532795c8d43d62dc4ba1c5a05093db80a6745b0298a170302b8b6d43a52f3824f2ff122497445da61215ca93bc1668ba1e5a91a3049034c59d2c5c035971700fd31f716616010bf62e9f24f93c41063a7ddef7e76161f23fac1959f45b004ee6f97062450ed64e37ccd06263bfae5295e0ecf5b187c7d425c8d36aad8ee687432b1ce98cc1b6ebc843a616fd09a9406e8912cae831064798e42fbca160eec68335ad38e3555c4384948a61d2b1647a500bafb595beeb6b674c210033eff6a8e50ca7c7eb16f33af60245ee57ef3bf0ddbf1d30b22f5c7247778dd6eaa6b432a0555b0cfcd5b76b7ea3588bdf2f47e2adfc51c3a8cc2bc1c411bfde5b669e77b7eca568b15db0c7e517659bf19a620aebabea9087af61e267e85a2bc42ef06df3feab0bfecb23d4043b5a6eb0b451e254bcc341daf5f68d019659e8aa7af635db29142be09030905a4b99d738b98ef4297091b3fbf82ac84829d8ede216058d8e66f2838593cfbace6a117149129e80560920d5350e0adfd675f5fe1ceda86c9eaa94d498330266dc38870e2e2bcca32822f1bb79c4df4462d99183e127ace42470b1e155554f8dc8d3464424ff6ef440375a9eed53c1a31e59f9b608b755b2cadfbc87b8af69675c61f5ceed3fd3123cafea4d505ad7f36a53f6afc008083798553111e8d3021f00a13199ca9a40de07e2595d5e224537057e29c7d6a1c744b6be651c43f29c873154d54964e9b1ec895eb5d021b40385276c620f4880eaaaf483c226200ab29423818f0d82034cd76a0589c91c6272524491f6c1200ea0a53adce55e363186bb990eccc94d6f223e028cb8085df7d86235d3ec73c6e0852640f7c45442b91a1ca7d43e11a7e107965267adfee58b48f77e46327895c7848e21b357da7cc12fcfc470a760525981ef7a0fbf481c211fb0ead5642055e3a8ae552748a6bc79c2528ec6cb312f70d6258afad1699b6c928cbc5eb1fa3f917127c518576c6a3071320b522517a8a5dd013ce6dc3343806c304886ec8dccd16a15bc2ce2fec0200e6a9ef8af684d1aba8599aa2596aea8b3a2931dd274e3ecc0e84c6523ee28b6b7f4e3f9e81d3f6f28257b16f37215ded4d69be8f5e2b1d726ce01bda08a4e28898c5cbe840e4a4986264721eed1fe72b079c3b6618e1104d04b51671bca9c2093127f5e83070dfe308e9a1d6c73cfdf3f1b3e80f47dbc7ce0e512974884ee0afd2a1d776a9aab26d210070fcbacad65f401b023eebe0272cef41379782aadce8e3a1a9fb2a56e3ba05d877088181472562657f131f3e46a4c416c2465286114a773294c66c5312c6a546d777d5c8740499bd4c2fba0337c0c7001cb33665596488b1331b12764d36d6818e5f0d2d3c2844de8f5c9044c8c49bcc77021d4159f2f71cd37a4d25ee11e085d995687dd794969317126fefab249010dde01e3eafa8ce99dd35e5ead1a964e3ef070f922c6ed54035ba375fde1b1d5d104a625e493c911b7ed55eceb4db3a97ff52b273830c22a9768d9ecc20b0de088711b7d9829715a944a86739e11cd1a867d2f8cb69e37bd25a2c56316801d6c979f1d7b5b495def861b19c2f3d634a564e94aab694a59fc4be45f7c6825e283715eecdcb8ad74e5596cbd98943c386ff09d90fe2036db382d281720a55005a7373ac3df7f80e7159ecd8cdde31b7412b654d9f85c3fac5b71b103e65aa21b418d8e08c8858e71c6917cb9183f9cf41f5a728e8f1dd7855880fa5ee1593a321da84035ac828ea2d0530c6795df46bbec0a4803e2a85f8d01c8aef6f323b6956371701eb0fa813b8c5739b0a7e7db88291a44220e40adb8b39c239cf8457dd864618a7465734f062cb5e9820f823179c3c213f4a5ff5fbeac5ea564a746c993966bd45978a29959c71a9b87bb84814eac27a0133ad51e9bf236a6e24163a35c01e5d6d12ea0f8ed6866d448605ae9f6316b6ddd1faf2ce9fc688e29eadbe99736a7354c547768b61f8295f67332535042a94d0f5fe3b13b6e1fffda0ccc58b9b06af6899d7a18e9c21106fab6ea452451071af5183b090a9f7e49bcf931fa1caab1119bab9a654c0816c80524efb1ca1dbb002b2168d82ae211a3b9b7d802dee4f0e1a2431c366cf1b9344d8410599072c34e9a14b9417f11149f9fa26edc08348c86aa0c7216f5c521b5d02e2755e26ec612786244d0b8c897dfef3ed7d3a18a24c3b7b848818304f03db642f6402872c5d5938ee087350235a10a55abd32bddd60e6d9a23cd05375392351df9737655f773745634267280a354e1b8440322ee8ceeb5fa0136e3a4c4c0ac644f6419df31dd41ca442f5ed28d1eb9b350c615b634c7d6473a25062779210090fefc4ede2b6b70bb07563492f9e94cce267d308acd990c098556b6833676295630a024f8ea49f0343e2763960a67033e879dcc6a1b1da21d04b4569d5bc82e5dbba7de511bdd5d8e8fe82a2bb3dfbf972881ff115165cd62e470b49c34bd5a1f3c7cf33ba1ef90ab023e1244a3b2c74e340177682649c69f68419b942b80bf8b0d5074ef6756a25bce68212406e0f0e699869ef879e0556d64ea188bb7bedfe806748f2a073c28b4556b63eb2556b5a2459ae09d241a98fdca888beaa7af6f0432d612aa016295154a98c8112987a4b64c1203c080e8e26de388a53dd8530c668ddb210369c1ee51743aec1c47f12b07ef541d0c621a5c3c47ffcde68cfa29d021e4433da51586ddfa47da4b109ff35956aecd9d87fe77b1a7a17b0b76804ac56024317d018b4891935af12999de536312e7cfd850bdb940ef18015a4571d3aca9cc7201013940c4d2b87948e24b25b06e06dad275486e00d64895e1acc50b19cffa64a2e3da94c02c6851df5d931ede69617508f6ca9379caae2004cb735528d56f0f9d74377af76a6496d8e652a42573170e9a476f9b9e1c2803b945db0a9aca8ce5efce6dece3e37419251779c246f8b25a9048126865d78c404c105b8ce116565d8f4c7a96066010094bb7bee6d1569ed5cba53cde66b5a7473d429cf2e92b2d22fbae2a9d2a467976635cc31e19520c371c1307ab07d588880b73004d3456b78a7ac9eb08d36041cd6e74e7f5cd79cb3ee384c6422174331f8e2ca94165cfe299a7b2db5752221bdea5ef8418cbe26bb6a7f765e2201432e082872a4bdaeb424e32be8ecded23df0fbbde3cc19d7323af18b42cebfc2b8b0e49dbd1f2c20687d9710e81064679c5d5825cdaf730a05c38f26a4fcd20eac8d3baf93a323ab6bf80088891e3655d47e85d3b2131dba425e40a22457fe38abe1f88aaa3cb21e8d2dcba79eb28f08304e2825ebd37fcf80214469a3050f73fa284e204422e064a7129d6d21463338ee1992c7124ea4c1f891073fbc703d925c9b03ad6496c5c63ae6e0dffd61ed499a3cc7df43dcee1c09bbcbdec004718398f12c147850c28f544fa3b2d193568226e732b96baf9e454d0471ab7d0b05e2289d3cff82051a625afd48a4481686923fd0aff987e955568f3f427019ca6c56fda77a7dd4a7731614ac0aaf1fcae0f42f810154a425312c42e908f5930b5dc66fd2684bbff142a7d5c712a62d6cbe1fdb3245cc600c92d2a8e06ca2caf66f4d57ebe88335dcd4549343008ae3cc7307c40e832fd5b79920a07c3ccef9c3fd5239222fa278b535d38af0b693e99894753c239ce0f9939dc1815a88efbb9b8a31fb9adcbfb69f6407afa82e41d12107ad4cdb0283a6049e9ebb0377cb6856252d4f8e660a2f34288036807e62aaba648b46522e4fa7c723d090894d2a7bd68103708e84a8227e270c0de94d09b654763ab2d3ecf253e78a03ba6642f2afa9a6350b479a1229eb683ac4c62b356f1912c10e04852a4a9faa1167616e1e5e3ee4f4a5b775f3c281ad2f2561136af51c757e3e1ac54eae85ba01909ef91157c8a90f30fd26105499a6789d0079a5765e8dfa58d1dc3e1622e42886ef805bd4c71ce539b9df265682fa75a800950dbcb474792197f47e47408656118fd61b4475649205a5c9101ee370bb4979b65d9964b9a2cc4d052a15dde243a71c2f4a8b184c60726d755acc06fa31172dbfa8255ce931291957258865107229e0950473781aae98f37b398c9ba278af7a3475f7792dc3b714c48bafa2feefb449baf7a17a0cebd6f7c0122843767c5043b1db3f51b8a3ef374df56c00dc166527d27857b19aa26424cb0e7a3956a7df29d9a3c168290d69271be8bf823d71934d27b021d33ced0d5ba57497f00d664939cf60de89a7292b3450922e7752065b25f6ad2c553a6a33272243124bda3b96be837f4348ab22a2a765ed3315bcdc8218135cdebc30147dd45c9d6e4f1d6d26f5a1479a1640d34f008cbad6791e1d5e1d65bfdeb7ffcc8e93b7c70d0a3a1b0bdcc963117f70229fafed9ee6ea1b62820eb027140048e062aa8457586acc8fd70bc0e3f3753bce5ed050480c210f1b93c1cd11d350620d82787b43d52e49729124428061aa37ddd01b417f0d63fc73c720c45ec91b3f9a02fa5f1b24873e363014794d00c7a481a35b464731587988be45858f5420eed660aafd71df4ab63df7788844331e88ab40bb2258c17d53498c7d3a9a98caeeb214c423c9072a28a11ab9b3869763a1bac410c0e12407a6e4d9a9d3aba8ae94c70be152a1e434f3035a85349b2bf4534afd7ea474c337a1e9202b1b39b2b203c7feef000456566c91c721e67e9232b646fab9fc862891b85086ee7709c41f6a72dcb9e00f8a1137e3dd035654ef81a03c511be2b59c5f0138795ec5c403d05ea39b6065a0b2895508b686a15c1a617f3a1371dc29c2626aef8793590ca6bd166024eda3e5af6065d8fc595054235ad704cb535d0714b96bcdbcc0a60a0ce3d2d014e9e2630821b73172b22e368c0b5215cd5e2ce219d112c656bb6f6941448c150092b9b9a3e7a91c77c252eca9a05e34686984057c1e875dc68d5696daf652d610e062b3b280cfae65c902cd7a78a0b10a2b2e0d80602ba0cae073dd5b7c22f61ca912786ce620e7a6f3152ffa483650c15c99c75de6a965df7a37edf4421720dc639f42c9988bdb694a75e4ce84af15fc031ccb181c47983dafdce74bbf856413b94f827cff2a92d9b962d74922ef0220d8100cfb6070e9a3206ec7e5e5df6f7a1a158200abf5cfcc06d1597f2ac3cc1ba794f0c0d45a9bdbd302d3519366e899ff8023335e3fef840506c956be3d1eecd6de7ce62ea561437d2bdd17c05b484258ee5caf9c9411ee309ebcdff99ab06f024096439024dc57318501fac6ae8ad6a273ffe45a1c0bd2f436fdbc7dc086c2103cd972f15b71e906e9363d8ef577cd23d2739b09193b0aef6c68be8b8b57705464f36b21fb8a0bd5114d3e37bc7ab517a3e30f935473bc2a922d93fc4540780144eec973c79fed14fc2b5d8074a1177afc0db8d7499de718d6c3a32d62ed0d3b7a6efa903994858fa3e30f614d0c8f14d6dbb7c7e62826bfc82190f259816d2607016990ceb4a304ea09845b12f2b5462e710d7fb56f164712ebb4bfd4efa09fbc7a433364a93706d7cd1509a560aa2662356482656a4682e7a91dfb0ff07d3059ee2da609f5598a728dfd50655fc15afb57694f389c4fcd01c8ad376e80fa99c1b0700eddfa1a3c4e6e7fce9bec3e6fcc13e285ed41ee792b53352b2a4062b1589a742263dd00b8ce5ae22f16cfa4a11402c42ae942cce3acdfe1d45a2d8d3d2ab45174e1820d568d61b44b67c7eb676fc61a4b6382565b1af8b8221e82322381608e3da886b2bfac38e5b320c88aae9219cdd49cd45bd363bd3494f24fca77e946f9105fbfa403e5e48526f5c7620123c395840aef11443845c953b58f71c22c713273c91dcc600104ffa6e8f2864f89a1459fa08da404946186df5bd18fd419f5b3ee4236a47c266d93a313d80f269a2622d12240869f5edf1096b15678798a22e9eb6b55168664329a174dfdd0183d88fa461b430e843740acf00558f61c15ab600b9d7487fecc816b30ec7b5cb1a65a2cb4c021c74ebaf628b1fd4ea2679c5609bc23255076338dec1b27d08503889151280826ccc589d58b59f704a475d071ac38a271c3a50339047d2bd1ba740ce2d4ca3f91a010cd1ebc85ae47f031f9bdf03e07f955ca3a8e70f8f6bcbcf41b8603870fd177443d134c66a75bbbb7a25a694fbffb072cf9e4a68ce2c4b6d668068cb4fce60182806097fce86bc5cdadc388cf7ef3328f024113f6f86a9af46ccd8670017f097db14bd4816ce79104597329d883c067cec2572ce9d438f65743cb412050d8c67d245dc1f1c4caa6007db22b3df721dd1abe92d7a945cad384e80c05c1951d8ec629529907a9f778cd7d71d9a5119296c966944ec4f85336718e55e8925d728176db30c2807b6d110f3e9c644378cec968ad23cc345e87461eecaa29c8c80f2550151279d46a3bcbdb66753774c0a597561fed0acaa19523dc4a36ee41ae90d00b2b0cf48a095b23c21d74adac1359357557b78f0de43a87fae2cac902672281b2e3f562a3a1ebceab448bcda54f7aa83cf26faa213cd4437dd2d55badbab897949a12dda19569d95acf7417bb186a0f7e93c2c2e63f8b7c89d905895968be37b2a5985b576d66cb04531683bcd7b2ccb03cd7041ef3fce339396c809199a24877d7697021f0ca355119226109c34e07b8493ffc3bcf1c25bcdaab8c9614cdc64ad2448ecea196ab32426e016dba6cf57ac48c0edddfdabc137177b93b5c2255a31d3a8b76b4afca846da052e035f7d7a5ebcd1bc1ae8bca1927ed0f71091c0e6c6449719ffdc19a17ce623ba3517a45f83a9c03ec1cc07ed25f99171b3d207b8595d147bd0bf7e9e10905a97913789f62db533e1eadbd2ddb5ba743bc9254f8b275565902b69a80195e68cb5b46b18914611df803cf65f9a735b63a4966799ba6b98fbfb2343815b349796e8089413e8093ec569a34ff46628a146733b2a03dc81625585703ec07f9083d6e44f602ee1fa8cc01ab08c4bfa81bf4f6b7a34b2a4ce9c0be0c8b53e0b726c0914436577d5b0899f1489e56a14d1958baa0c86ec5c563969dd29283c1a7350940a73cd4c0e4b0fa75bb8c5767f06fe9ca1fe6fcde545cfb43aa97f1431a394f66bcf25cc3a51784f0ac827d506e22ce4077d71fee4efffe94602ff641b829ed5381a2d71ddbca0d067e70d94b13fe82c2e27282f7e9874e1874cb00264f8162cf6d0a104dda94b4e9f2d40a8e238d8249eb1a0e54af76c734d9d63e83d3629fbc585f142a11d426d41b0c5bee81a58ccb278561d3fd85d1fd72081a0389203df204284ade72fe4bbc1b6d0fe43a31906cab84623ba663b9f1c6acfccfb468b376c43801f39e51a3726cc3caf715303b8033513b611fa5625941d8b21e062ccfd9ad996e7a331fae6dbd5ca609e6e40e1325aabe62cd4c39394c25042ced068e4f563feebce80e3084b69e12f9c50c30899d8509d2dfa2672aae9cdac27fdba67357de48e7e9b19629b63889231a5e78719420c3da52cb248c943dd9201baae5c7cdd0ec6038196ffecec7741c9c544b1fa95387321f5e41065c6d4d5dc67b2ea7d7249b774b5faf4d38e21895d5972276549a133bb7a14dd5b99bbf31fea89351da27034e4e14a5ea317b7bb6487c260a589847682ab75afabc1b8e620f35243c2053b1cc0039e0a8e78d8fb475f20940a63bd3bff4845505e6c25c08ea4efa4a1c954032e7617089ffb47e4c7a50b654a6e2ac76b40a9e8ca6fe883b7d168475fd60e38e939400c159175658f005279c3b410b3ac4efb9d8f3fd0051027ecc9a993100f679526a5b21033de8e69ff609fd445d4f907c3b1e15002fe691c0fd34b9221d3517370c14e6dd7faf6d0cb94c425338a516a191c04ba87733663d123b4ca83b6a6a4b006c0b071db66f241b57ae9be5d52e58104f6278a605b16faea87e972762bb991bf8bccb03253a43117209fa722e9a00a916df85e060713f543bd107a38210650728cccb9d1889002dff02d902da0ab9535191d6f0f2fd0bb2c3585b192ddc6532961ad15fb58615c86df3246a6527c0c8ee2b8f53b8ceaaba62b0dfa808416ffcd3ec47b345ccc5b3ea5b1576075e13a5a2c0e0f745fdece0d98c0956b82b7b3c34b38c4cdd02760c195a6d74e41b01629cba8f131b8dfb7d619b5c9897cd3b0b6bf2219b74fe7b8b5d7cc938c0939710a603d432190894a8f67841e1398ddf1e8eb7cde2e4faa05e2abee3fe262e282fed6d1edc926ab8ca0665851041ef79f27b0a2f64e4b7de8c643ad4cbf3388c1fadf3dfd328f2058a1f7a57771fef3e6d9998bcb20f31e7dba701c620093ba4d54c41346faf02fa6790c3c6800664f228cd459285e26d0419a5f92751eb4487d9aea24f6e2d0acd1d1173e0cdad822ac4f8237e80418d336ee16864985040b1d088b2fbed7c751ed0d1d6a5e5e11235f87149d375e52a6b39a10276535b90bb32cbd152f86358dfc29a161e5a60f017bc2e66729b478dc530a23c3b55e3c78763f0ead81b553df7c3c966053e636363e41fe710f7e680c0467a5284a2bba5012e9dfc12640d0f7ea97f5a47b1c30b80548b146e6761c41357104ca1a607e9f025cd9dfa2bd7a478141c989e93a8c2a958e93b0a1c7f96f6aa41b19454c87279c7b092527c99d0006ea0e0ac0d1b344281d6abea42a0d7687d2eb7e2deb3b365504b1b8fab996b3a358aaa8df2e6a3e476891f113e80fc1b3da0d42f2fdd9d7e32e6a790e5b98edb4064e491fd338227f41be683810fe973707679caea712800540d79833f14073f4c65d61430fdfe2b0a6317efbd73901ce2df632c61cd5d12f21a717bd7be8371be332890175d70b26d62857d784a3b2b73c54057b4cfad9301052a689824ea27fbf6c781a22398c90184f6244c43baa66226da378f6c10806014d890f61bf53711b9b082a7570c5245c411fc1b5c40ec11396d615dc1d24f88349787df641a78ce8be6f25970086500df89c6da58fe200b05c96bbae6bc21e68cd282efffe8a0cd44f74d99a8c3b18265a30dcd7df038ba50210842f5fcc851e14f1535b5200396266cf84a832d44dba0d4ac6c66fd5ac6f3704bdc06c419c8449c6c23aade329f65d0d70da947a9124ae435074d664f59ac234d8928f27de4bd58da23b6177ee54e1c04142a56e2a477b5d4d3675b53a989fd0512b8f84345becc5a6abce643d7143f575254075fd8a99dc489b58b6417b6cecd9895842305751eaa3c1c3b573a1825beb507166a51b6ae3799fc5f0f2f1908e54f1ab12d1b9f57a3b5dedaab46e843e43aa08cf6fe34c8b4bc48eda4cee7a9ea5526fe74b2a618d0e6a31da53b1bfe5f0bf44e1b1e63a8ba212eb652fa4dd2b0a14ad3f077888e9887fd070b8a64eb2cd46872c177b27c3ed5054624f679343b631e0d509eef5b870ae78bd2a59353d4d00501f995724e8c0c8cc407a83b4d55bac3e42068312d20033f16872a22fd699956911120a99984ad4a2dc25aeb36f1c2137517d81d966a1ea5620513b002191a75fa9c8eb9b24ba62a337fa866da2456213ed0ecc6c83533af76705154c29e7b8ba1c40a11f590f2300e4cf119c43aa5511511591c569457c39b6dbb4d41f4800a0e02f882e1d1d191ba1c1d3dd162af352125f534339035a613f2c10ef1a9be2564ad2b049432cbc453b6189025f3f625a4690d75dc283b112ee86829b500dd9370fff1e1582d4fb1e1808290990a8415e2c7916968214b6fb2c59d3f7e31a192e03163324fe2712030d8ae4a1104946fba7354b1aaf52f3fe630bec0588e7c10c4998b08d5b6c892e478b94bf9e218ad8870ebe50688f17f8653592889e4db530767412169da9864d6c765b2260974165b44134863d181b28645ff3873fc3d9802a907510a438718927d00eacbddf1924d4af0a57f50334d4e1a510a45fb8ddf3f517556e73104e2300628e5849ca878a2ce2ebd1a2cc82e672a4d92c7482a8074c8afe625f8ff6aa38a194d535cfdc2599c08dd8954587c434c89b2848fd272ae8721bc037ef2cce651ed14bb336ae2ab905b508a5fa196b227baca18f57140b2e534c810bfd3db5a1a17560c7a1ca6074cd048c078a72ad7606930f3628f454f3e3cb012422b0281a302a4cd9a1d04511be2ca7be8e72e2a20131289690916372b0a7aaa00842666250830c94f9c7fd694a2c09c1777e2b18cd137e0f6f593d871bd84cc2aff001e655938219cd832572828a47e69781ceb9b8090cbe9291989804964f7ec0dfcbfc5e3f9be71c97568491ae148a372f9ca520b9cc6bc82438b9872d75460fdf4ddc3ca0065ecb792ef9b0e210c9bb6e2e7b330e8bdfad81996c14189a4d6289a86e93821025765762f0220fb0ef4dd5e649421ea160625696044de8d22f17a7a77035b64b6a858e7b30292006167d34ec3341b4ec86b6110e755de34f7ec02e5b6a48b18187223bf7c7fc13e5a314275e31b344a100a14e490f2f0cce9aa5503bb711544ec342404f42c45e16cb2efbba70f931cb8d513cbba6bcb5c97798bac79cb679a6736f9fa0f030d78f40d4b0de91e7094f8807a6bcb894ffc1d7fbbf7d7125616e13b2c5dbcb9ff54885c14cfd4ce819f896f38cf0fd341edd0bffc31ec104e408d59ed0bfbdbd9cf9129609e969a044929af7e1492dfdcb5f9cbb0b55b4b89bc317134babe2d0ee98b426400c9ff917bac162f4b892b175848c0280b15039294532f4b6ca855e82046152bde1a2997f202ff298c30e06975cb102a7796e42adfadad632f4009ab8f20a0963a4a8b4a7a653626c9e92616e0118ca38a164dde4bdf59c9c5b38cd575870ad84167712833ca16eadb60606bd709b622ec87c45ebe91439985c5f00b2965e2a66a2f69131023a21cfd286b754aa3ee99230b04a0119023023059961c939e7b1009f16dcfe0307f2570209ed318127e152ca9048d8a038315af60f223cf90f9922c86fe9bb5d6f68d5ca935dc539c6eac4404a51841b3f60431a2c2f417e4d7c2d29899a65b90f616a159a209f7785468777de82c1010781509aa9a9ef2d2ab4da81d3517e626f41f05d53fad25f27309c6a42349573483eb72817202846bd52d2251d29818092634ea99e01976226f88e5cd28eee2cc7e5f56945c5d634ee76052fffe0aaa98814e2a9efde48b6738c05b6f60c44e324d4bacacffe6f22cd5eb0d90f44ba62933b973fdff060c591d2d97c4c348d504aeec748da9d0a22a350ca8ee3babefacd907b4d86883021625d703006e39776e7faa05d707fd7322ed523437f00afd4ea1e24225b2ad9131364150db1c17e5d9ea1b78871190d18ccd2dcfd40f3cc2c714ae310553d2c5168f0af9f045f16b5ec9832e3608edfa52a3ef41ed8002532e57b30745b721c58bf1418ed9c12d54af6d2aa5ffb7627d0c4fba8eb5ca08de1d01b3533cf432e954dc012e2cd8701954a145078393242a17c05976003be6dec6912ce1b47fb4adf1851921cdcf305d0ea608e53c882435036daf1b975dc742c91f9176dba44d4219321aa56eb7ad548b2da430223842894bd8e70503e2b8d3bc18a2846099c6242bfa1c25a0c22fbcf86a426d33ccb2998920c3ce010ac0033919b8de3a15cd6382409e5e0408c4795d27ded8d801b863e564a8bd83f804b15af5907faf23febfecc481a74a331e86ad982f3c47ade07c2e810f8719d22a4b09de5b0ed690e6ef212cfb0e966f0c990b130503b2cfb815ae6d818a4812e72632ddcc6201b25dd18043994e503b1c7161ba16cd47d34e7bc027237a45e78da37304383dcd87beceaeb658c0520873d8508548095c7d732c940d6f8afcc70a93ea3bcada49a8ceb93b839d4a221e3979babd5bdeceb42e18434b65fde67836e181a50c95f5ba3a80fa3ca0b43b7f808bfc8017f7f5e820ace6b01a72cba7d8ef6d0b8332c79bde3dd38875446bc46cc9ee9af57d584f914dce519547e995b32b65c780d7c63337f2713970ec9371c4a40d345ae10541e71d04e84e377719c435e0c5aabe7dd8604a960a74f39fe6e7b1c820ee8d218627ea5453d3c1d332f2170997dec081a8848ccec173043fe990119e84b3743f31be993b69c087cd53a3b35fe4356bf13dba386888add28754089e08d505c051c400f6a11127a7b7ab026f7a6ab2cba6f1e777433e05b0784cad07c5bfbdd4971b20e4659f6b46b268008ae1d3a0f90eb9b9888c0af1ab3fe0550f1b0bf22efa2930cf684241c3c0479dcbc0416896eb211b709747287dbf0dbdaa9a383637330caeae9b92dacbcb046eea166d50053c40e342b5a6e80eec5ed7474e0c2c0e192ea409ea1e264406f65fc4e18ef25ca85098039f081517131aeb603f2c10b33542292bd4a195c70e571741e41dc2eee4d2b9a353112a04f5b3ecb54491312bda0d3144a37019ea7b8d129154c919f363d712f13433a51204cf9ac82b2b34b46865c79386a655ef4000c65e8030cffce26913ba4030e01770ad4fe0ac378fde44b766c4c4490c393c93c0203b0ee8202ba08a4890b0156520dd8a0dd17fa8424f6fa2e3886a9b20e0f3e846922d36d1c09a278d085bd12fddd5089b1b346485372b2bd8f45cb54af03343c0b1d1d7ddc92c61988ff0e035facc50c7160c0b29078e39bba70eacc2e80b162091ff83829ccefccacd21965249aec3b79b17fcfa7238e5eb6a1d532b80744be387c37578a353f3db4c63398d46553a9ef66329b7b4a7da0e7b27d3bc31acfd751d45517cc67616d408eb2e959b51037c6a69565e7f1e72288bd1c644f87d131bbb6a121285defc85d0d199c6e682ddc330bfaa21446dcfcb8acc829fe3b5a38438745e30e88d3e9328f19fa39cd2566e61d9e83bc2666e5796627dc234cd6fed79efa2eccfff6c7fd8d7525fcc5c96ad801c9ce91761b69eb7b1fe1e0a9fc17d9a71718d48c20b4b905992aff0838cb861f75eee65a7a5c38822d083fc770ec0ff1898289287d84016c11ebfb21aca281efd38afab7e50109cb48d4d6bcf36b717e68f8a586f4bf78c38a8bc9de630fc37ba7b407d3b8db7b78384ed2b6e97400f08d9dea0d8599857048f3b8004521d5a9a8c13c83a3bd7d67dc59d67b4100b9898279af4754fa6a7adaa15a8693713062cb8bd4a5fc65c161793384aa960cebd13475324cdda6412340dbf0a15577dbb3939fed6b0a9d827fce4ba1896c2a9c9d6a1d431f67576b4519f0b8ebbef764743f38bc421cec12cb2339b2e07cd6b3c4d73195a3f96daf9183dcb78735168aaf62c97d82d02ec1c8d2c9e8de895b320037be362b041d0e724d94fd83d83c154bed500b0413bf3ca5b561be606327525e1e7613c6cb7a494843efd5ac155628fb63e9f8dab7cdc0507697fa8ae5fd52539efbbf812db00588f0f843ccb6f21d7f79cabfb3e841779b262c928b893d848faa0fd7dc47146a88e9e843eb40f3f2422e7a51f4c1a77410272c038396101dc06583d57e9bfca2e32bd9d7aea0c54ef08b6fbf95104baf7d07e2c51640124bc51118cb80ee5a19df8b23983ef7eead29406b4b7524743e52e9a1d0b707288b2944993456c52704a2b869137fee9052df818fecd8bbfe9efd5c3e0cf3b65a42f89a2785687c11b2491a3ccb30a6c7dd00bf2cc2a192716909e3284defcc59d7392e52d9c62373f8ce3eb1020b63b917d1d656c1ce9df623db0007bccaa7aff39d11dbe6f62503c49256a2da52afeff49d8b9f3de696bf4cdbb65a0e30f9c8db43a929bfb96bd7d9229f457603fd89662f72d40492707c248291f50f69285e37105b0aa22a6a137a86b93e5cc0c1912bdfb07b3a0f1848d39aae756b1d22fcadb987ad92175129d2c0bd70bc48a2fb9c2390ba7d860d85d7bfd3087616cc2e52ebe20b7dd2b93bd63ff0335765cc09ae300242d3bf0c48b599718ecd5aa0f382d5a6ac31af9a9a13d50f18e62d1aab85f64771bf9428edf79b3905953e3ee78fdd7c186a666045fd24f8204de54fa0a4a7dc96209dc467d83883957b2b3e425230721e8c8167d7071df6d6eb173341868aff01f151de97b95a6cbec97e5b2ca2a29705afc69723b334cfe8bb222c6856a1bd24777ba30d95111e15f6bec3bf952f3ea12b365b4b665314cfa11050952478043cc4c01bace3a7d09f0a624c2b0084a797e99ff806a49b859d1eb0c50719107da49548fd270e8e28adedc9eb5c30b2d27308906111cae34fb0229854df00a98392e55e20cd75bb16e2c240b91c8754a3977b9cb8ec728f369416c8606b242acbc21726b3d610c223957d61421c8087bab5032b4b1a5d184619b7a1a9aec7069928c47322d8573d8478223c7dcd6debfc464592ead68f065368f9d84352001ce3dc9c2f1698cad121ff6db4baf54e216e2ea344059e9ecbe362e468056729a506f104eb04b83f94802d4278c851b18442d8aea54ab20f0624439f90d559f89a53bd9a399ee19c4b7bd6f3e3b9a8600ce16191662309047b4f4651a7804bd16f49215654c7f3f142a499a54c3dbfd36a3987fc23842f88c5c129b92a3a9ec8c175a73abfd3d40f979a4ab3b03d80c4e685955955f2dce67df13af5c4f9de74bdd8328a7ae6683949d31c9819f4f6449cf28b596149ce254e11fb5a0ce00e2130f5a0e4b2e63a2475cfb7c2649d147080d5f7b660cc381b67c964d54901594bf2962def2b7431c3e020a7bd511425d7c01e0a3bc1e3ad989ec24a0d41f47c7cd72b0abba3e7ec378c340e34649c4a276c20ab7657a47e4fac5d2f54eb6f32aed71164144bf2db4afc99f62604dc880ec2f5a476ae37c1ca0cc685ad7f26e1fe14d44885c6c9111b5524d9fdde4e41eacee319a1632aba29c2c42ef3f5939a219ab8a54567feafc94f9a752b8f61fd2abc8ad9aa7ea3a0ec602fa6c9b5443b45e1db898857202da641265c0f5a0e945620c6b9e4f208140807e8843105cadcede212a686b58e7e3102cc15ba6fb4c1824fa15eab43077757b72c5f923b0766142c4a2a5cc448fec48b123f8ec7ec5b472d2e29b7a541335980abeed5b77d45f30573c5100ac6af007d3401aa789f51df6a9e16f68c8815e2ef69ac8e48dd110cf0df1e40f73a5f5c9002ecfd1ed89bc4aeb30181f92d9f22b28ac95eaafa460f6f549bec3abfbb749092448a3f1825f182eb6c1d7c2eb0b610a728339d58e450e632ff8b2735a406949000f5906e9e903cda34e4630f032d6e8e469e3fe41356d5f1a16649728bf1fc3ceafb29963df8347da3a522916ef51dab73c9ed33c35bd824c113e346357fc47fd916c6444cf7cf451ddcdefc3481de46484524d28da7822243f0a71ca9d9faa1765393d062fd1dd04f1fb2998a849e0464d024ae56708ab6a51612ecb91be9aacc7aa7093a1654edc710447271811912262936ec58ec2ef769249a93070cb1902e7e572a79af5a11d0afc5d0e53750d21b3a3dd8e773f53204bfcf800ac1309bec996082b8d8da9d90753a945070b364e28ef3400f1cffa75944a870fccc235909840d16d0fb97f1a27840159816e137dbe019100d0aa4b93342f70c3352c6a9954171fc38d1cf322e2887a7aa447b7236ef21b631a6e35309ad1e8b8c70defcb9a3e52bc9dbd7a3e87214ba59addd7437349123218900065fe70d7d3dcd63d739fae9d286ea79c5a4734681907fc08d5ca15cb4f104b217fb78f3a0e149d41197461739c6cb433838948fd8b697c6f4c58cfa692d9283dd592fdcd670692efc3d22b10cc5c8d319cc1751f5c7a7951cf087057b0cbf9027b77e9a3edee6c61c528cc066e2a769e68aac43020e1c2b690ae284425efcc55dbd005daf82b4e97af101806b048900556edbe69a4e5d899a5ac71164b4027a28e66d56205ed5380791bd0b3d86f1ece87c6feeb986c51be6f5c6738a217e4c5ba88d444eef6860a728e8e84120fe1ee1b8072c5e44ebb98c8ee6fb307166676d2c22e901628a451665ffda515f652dd4444448681329539201bc06b806c506dd2e64bfb8a9fdf735496fe943dbcdc5492fed85fceff7683fe97753f3682eca4fd98d03078e8cc3ad9bdb69eb19a2f9e6ee613b208bb0c7b66ddbb659efad6e5bf9f6356badfdd3c4610f0ed792e5f15cc79f4261854d3e3f6bafbd108d3ae9d12c7d24ee89c6f18e131f3db19e3a692c0f81cada13f1d9c9b3ad15349a1532595343d6a0d84292acbd0d1c9d9bc50923f7d3a629fdade5c89ae6a2d6c6c9df7092a63d106aa259ebfa5a3d837bf7debdbf155cb0caf73799ec7fbda0246ff6ec1c5ef7cec9ad0c490fc98d24c24d6a849b74e68517b097278e4c57fe8a09ff44213145e1906317f059b7ef3f32b660af87f8232aaaf458e930373dc3601c25c392b849ed4b8aa654814f3b33a47b252cccb752f8fdd5c649a775e53b93dbc65858cc4932d4f4056a02b507c1f26cebf2b79c2bfce1dfb97bfb82a9b2aac4515c52a45c8997429b6271948af2ece10f355b178c936c9972b35f4eeaa889063445292dbb22ddff5073f8f5487571d20dcb8a7231051ea244674b6361a314d89fe6d3523be3e20a9842a1855766e18aa4eacb9116234550727d71e1246cb3b87992fdc5cdb6299ad231b8eb1ee2df7dd738c9869ae2404ddc7fcff9f99567db9920fcbb3be3a67ff796c68e5cc07626096b699c14f212883225b32f2d270345e957c38503a6c3b882dcfdb588f75951b525776571989362423fea4ca571d209e676b98984ec969ba9997e19a0b1833b5ad37f2fc6e3d8d5ec1a62c0a111d8bc7d08c4bc05b9bf9523e810109dbbefdc95433cc4bdbde5e6d5c5494b6ea4ab5b1d79b6eb3135d1ace3de7befc5a1430af8e628829628a594d2710b38e4c3cd94a6f8dfa76110bf43423fb8f79e2bb3e7f7a46dc8fd7649eebf626b100ce1a6975a8aa620e126e866b7e89e9aec154a70b97b1c6efa56fa0301e2a67f3681b879836fc901276967e727c20812ec2b506e76d7b1e1abd55951b9bfa25a9c14f26a1167f910a8a93a41534821dc7ca9a84eb9f93f8e18a3eeed54a7fac4ab56691835e880ed73ba570e7bd8905c41f84ca08047b8ea531054a5fba62277ffad80d094ae3c82c2c2a761197e476e650956b2872927727f48ba6665562ce8e61cbeaffbdb2975a7347461fa5fb594524a4ffca99fb8d9cee9dfe14d29652277ed82cb4daeffddd784ebb70f9a6be852a0a213aed2700128a8dda8b18a9b446c265329640a45cc48fe462ce0a671d3ff73e18ee5246c1538095b630d77865b7192e556ee4c9dd14a4da3d9cb0616a8a0df91b400bbd99b6fbef9e6db167a9db68538df7f3fc4298130ff0bb7cddbb66edb2e696fe76d9bffb6f9e6dffeddeffeeda1effda9e926e7d66b98016b6fcd0dc50215785f3deebf16e07e1fb5742fc65ce5ba7c6b90016fdf0f5a013ecdfca1f0eb7f1f0a4b20cc5cbf4deeb95b72a6e9719d1337ef9b366e5013f85ca603c8dcdbafe18ffade91ec793f82ce60692354bfff52abd530c30967cbf0d193fa9f29a9fb5a3f77a66125bfffaa403284902a5eb2bf101e0af13df8346cc0f760f915c5858c5da8836183b196b0d205426edca8e54961d73538787a54ba6ddb56378eb3b01ad84cde018b65ff1e7a803fb4c708d8830477ef51020e7f7824e5615f70019f98ab3c7ea8bf715bcd4778b266c926aee7052ef7190de6f34401db3448518314e4fea6a1260acbdd2b9ab55ce966f70aedb1513d43bd888ad25f87190ffd3d63b59354cb7215aa3ec17622b95d95fb35eb4d8622a889079ad2f78851071b75d37dbb9aa66d7f33e78279e5feee46f50a4da2b3db1450937f27e1f9ec9516034dc9d1ffc1b8ab0977d55257368da3f4b792dcdd049f45d492094ad07d0b9a47f7fd4bd452f7fd4620d1dd0ea03cba9e999959e919176dda7c1887821b6ca18819e83007334c01051f8d0441a5821715d82ce1e1da18827069d720c17a40858cca9ca27c9e482145761ad95d8a37649f620a2a84605a23d504b01d94739d58ceaffb65b28429587083832b9a1859c1861fec6085652587203cb169622045a37b4c12186487e20a65b8821523ec48178527dc937d1f9284123637b1128a000fad44813cb4c71585e6d00f31d717731324f09b8f9e9cd6591deb42b3665a8508d4d4651d03d9047cda54cba62c13583665533635f32d86afacba5287f0604705d6deceb6ef592d89bfbdff8092ec0ba8b9fd269675e6cf5b9e367ac6a2c45eb9e16dfbb6289ab23df641c36a29140111bff894078dcdc86a44d2042a10bff3980713e2774a1c0a11cb5b518a10a26e6ebf6dd585a26c7f591b6bdbb66ddbb66ddbb66ddb72ecf021648822c826e06dfb8bc7dfdebc41fbe491d5120a786c2f9228e06151d434536b36eb0a999023dfaeebbaaeebbeeffb6074e0c4b805ee93c267c58661bcd87d0d8a191a0e17ec07ddc87abea8db7251596bf1fc714432b24b042c391927b39cec1209b9939e37918888aa25339489898564cfb8a93d00aa6df112f77a1d79bd648e4592396f574eaaf19cb73ca0278ee6e44feb83cc796b849a663ce7ed8a973815caa6aceac64925137ae25e5ee830a32652c64bdb6c46335332b38939e9b5404f1c8b1531d424a3e5a52d1673c55e31d88b936220819e361b9b24b850d3e8395f59bcb4b55ad548ab85237ace5719d0d31613536790395f8b5053a8c64b9b6ab6a9ea4a452971120c34d0d3f6f2a286cc0125f192369bc5cc6466342f278d54a0a78d6563a9020c35bd6079498bc55c622d8e8be7bcff809e341b9b2064cefbca8978496bb5bc48cb595a4632a736a1272d26e68accf9bea126cf73be67bca45d194735cb9c165ca027ede5c50b316a6281c44b7636839925e1dce77c3b414f1a4bab684bc89cef176aea3ce75be5251b8bf52ac6e2601b1b64ce370de8c9da28e1257b6f88e4983664ce7b809e6ccc73387f04357d323cc379215eb2aa972b64ceffa027fbd22ada53983ad3c1791b5eaa33204ef29ef33ea8697bce9b4e02ffdc21739e53330d7ab22c27d4e4cff9eba51ae37cd353b56915ed399c8711f6e094ae5ce345f3344aeef5e25e27254dd6de678825a7e25e424e15722a0ea7e25eb2f62092f3338e85330b67e18c63c9da8f32c218311b4e8c130b633659fb172350b4b562c2adc5d95a9cad1593b57711fa606caaed25dc54e1a6e26caa0fc81b671b0b6716cec299e745e72266c389716261cc266bdfe273abd68aa95a8bd3d25a5a4cd69e85876ba1a9b4174ea9a95eb2f61d16db9d692cb3d94c63c9dae38e964fae6563d6a6b4b1988d599bacbd68836d3eb9966dc5d8d6b5ad6a5b31e277863659b351d997115bd50b67ab619d59165c6796256bff919e3f10df9ad04137352d94d9e135564b5766d3a74fc98863366428e3a6f622f9d9c86ae99b5514ad4f9f1ba88af6dacb4699ec83034df99464edc1af060ed8fe29ba602aba685ccb496708e35c4e3a41cf1c122791212c8cf1d20eaf7d2813ce84344e72d023b55c5bb514c22a0aac4fb855b4ff6a2a8af6adb0b62a2c6b3555a5cada7376f82f243b8364a51c35b78fe20ebfd0226f7f86b1ac81e10f9f37106c93e6286468e328da7f6458e3a616c2605f4d08fb6ac61a6cc067133b3cf7dc87332779de957a1aa73e6973479ae40ee4483e7962baa9cd84b05af2980fd36a8d357c359f8d97aaaaa2d480af7c7a6ab2f65555eb2a6ba530c651b4d74e702b43989bda87a587208a8fa39dbb0450c003f4b02b3d6e7edfd3e0edb5fffe9df4c39b4eeafefb1327d1fc7defe37b27bfeffb93af1c42617e86584c0d4abca79ee739193ee769cbb8e979b7a8c9dfebca56e2057cdec8de075a254d7fd3c6aa793fa20177ef7fde905d4b7e5fc9245a23cfeb7db74cf69eb3df47dcf464aeca5ebbbcb8029f2dcb2df3301ebdbfe1a41f680e1c3a56dd92bd4e654f95bddf414d45bcf7be514ef29e035eb23c3caa247b36d9bbc95e917cde3f6f14d9c3d97a940bd97b7ae2752da8012a1653a286a66eed0df3c65851ec3d41465094ee87e2062cb95a2b84566fd0147b82cc019a621fc482cf1b481c11a303f42432829a9c7436cad69aedff8946453646b6296d0e1f3d49c2b6c7cd0d879b1ba6299b6fd8b7c61c28c2cded417f76aa53f5b76fd59093ca20514b3b8ee8003d11610435dddfc6dff076e2ef56226ee4deb61660c85bb6218661bbb7420c5c995a2106dbbd9c095f1f949a6aafb437d012ede8e8057cbbd10bf836129c2bf79ff81c198a25fd069f72ca236e5eae51df459cc4291bc9d768c0e08f639f9eb513f56ed194fba30c83cf292fa74bc23679dedc3191067c76abeb9525dcbcff820eb85bd7311ec76fe54bb3b54838693ba90d11b76479dc1772af9f9d5347be49b220d33543f868f67c72d2264de9cf6777a634c5a96da7b561ce8269144fbc3cc1ca348a2754997e9869144fc0e08995dc0ef844ea258d7b8a5237b2a2b47b576afd87bce75a7e7dcb23261623138ba19969f75aabb55a6bdbc671f776dddd3097077e2018861c8e685353040ec75173e0b8c13992e98bacec2d4d9efee2e22b37e9dbcc6c6e663635302f98242f9818988ba555cdd2465ad52c5aa7babb88eef6f6eeeef6eeeeeeeeeeeeeeeeeeeeeeeeeeee6eefeef6fe5c4ee847eef214810a918f7a7fc00d3154922f5021239344c6dae8b0fe57cf2f53a7a10ffdb53cbf0f73f8c8fe4d6d0757f3be4651fabdcf8896f053dab704bffbd8f36858620f79448e4cdf87b6048b1434ca49ae9dddf9731b797b2bc080a6d5ad462244082ad35dab71f76e9cd08f7ccbf30458bb17634debbedf435d0984d93e4e7e1c35edfc84b4d42b15052cf188044fbb04ad52ffe432b97870e588dd52795e5279620394321ce5e9995b29a24f4c68ad5ada6215a54f4bb44afd02a40890ca0348dd5029cf110ba03c714a69a33c3d6f2eb54f4ab4ca863aa24f48b44afd0094330028514ab33c7d87f21c6b9434ca93f2f4fc7dab237dd266be953673842449922449f2699092244fbefc9392249f2449b2243b9324f9f5b519922449f24f6d0c211a7f42e3a4a42cd68cffffff2749f249f2618441c8ff194ffe0cf2ffffff67909dffffffff43274f9e90e569593348cfa5976af1525e2ad51263341a8d46a3d1ffffff081406f9d188fc7ff247a31f8d46a31864e7d168341a8d46a310f94f7e795a22b2954c265bc9108944229148341a8d7e34128d61909148f43ffa1f95676791482412c9203b8b44229148241285fe473f2acf3a2383f41ca37319e95c9dab73b511182010080402814422d18b44a0a7414420d0e8453f1295676710080402c1203b8340201008040281cad0e84523517956249d4ad5a93a55a75285c6711cc7710481400f028d2ec220a071143de845a071fc711cc710d9791cc7711cc731247a90a8f4c17355e5f60ca386a5a6a686c5c5e7f3f97c3e9f711c7f1c3f9f30c8f8f9807e7cd0f8f9fce7f3f974fe7c3e9fcfe7f309817e048de5e936b95fa9d72bf5c2e3f1783c1ecfe7f3f9cfc7e309837c3c9ef13f3f7e3c9ef7783c9ece1e8fc7e3f1783ca1f13fe3a7f4ece2ae8adcd55ddd5511169d4ea7d3e9743c1ecf7b3c9d1661104fa7f379cf7f3c9dce773a9d4ea7d3e9743a9d4ee8f39e8fa73cfdc80c359bcd502d30c61863dce974bed3c12cc2201d8c3ddf794fa73c3be31263dc19638c31c6189721cf773c9df274540bd2330b1811180cd644386118866118628c1fe3b01306c161d879fc1d5c9e9dc392866198c3300cc3300c439dc79dd2076f1a8ed52b1c8b63712c1104411004c1300c431087414210c41f3e0e41f041100441100441100443f8431c969e392b4c702bdcca8dd7755dd7751d08820f829d180601bb2e7cf043b03cbbebbaaeebbaaeeb42e18321589e6d24b6442c169b7df7de7befedbaaebb9c3048772ff8dd83ddbdb7f3bdf7de7b6f08fc0e2c7d68cf1ee9f9db5c94707171b1d9acb5d65a7befb54f835c6badb5d65a6badb5b60c757fbbd2076f542d792d24faf4ad5aa57efd11636f43e5ea9ca6699aa669d65aab816110ab69b7354de3bc9686b53264df963e78be4153da73083c117927483f2070ae17ec5b99dfbdedde64088368e52d69d6dc673c27e4fae31cb0bfd75a9dc0727d7140a8e1067c9a59048e265ee5faad2a0a777360d7f87a98d7d27297bcd6894cc0aa961440516045c8f50ab9abf6c9003b5c00cd5ed670033ebdd6c609d9ac95e709c00c741bb9592287254c983061c2840913264c983059b264c99225b3d96c369bcd66b3d96cb664c992252c2db432cefed5d6fa7f694aa56e3a16023aad1ec57775d0dbcbdbdf52c0fdf53618b2d5e8a9655ac5a47d7797fdf57e9f9e3c76cbf4899c41536ccb64fbf4c737e0fabd622d758de2fa5ba5a6c689b4b7323451b6eef5737538b457ab6ddcedbc0f0c39a22bdbfc7558b4f07c5cbc18413042a2510c1964f536416a6f502186f012caab543af0c332c5515b1545a3a70a8b6915ede97fb515d327d10557198e513509aa6e663634457b8ffc6a680ab81207aa22ba80a925a20bf7e25e5c122f018006872bf76a15ed31ae2a90e59b7d3637fe2a92b52792b53f392b64ed472de0f3cabe9aae64bef69f09a28028e10f941cc670a0476ed9c93016c6e0aa92f1b08aaa5242136681a694216c84c3776557c9ad7192ff2c8485316e8661cd8837500b7f7090c551526ee252258a335e146b88345efc5eafd74b3c79f1937d32b17cf163b1582c51c68b1fcc0723922f7e2894288affe2d7d222c678d193c9c4d08b1f1171f4a2f79abd44d18b1e0d8d08e3458f15c3125fbce8c1c088a0173d94288e2f7a2d2da28b173b994c26133f2f7a444451ec5eaf97d8bd44f0c58e86466cf162c762b158228b173b9856d160c47eb14355140d258a1d4abc2f8a5d8b785fafd78b467c0fd0d3a5e91435d5171f0927755efc23a809bf2863b1c417e2a5cbaa281a8c7861c48bc23a50e2dbf0d245017152f7e2fba026fb628b281365a24c14dfbb2de2f6222713bf172f11d15f7c94b0873883f36a581ae2c94bf69af17a992547c6c782f958323ed6c7da810cff437d2d1f4ac687fa505f4bd6be460c3024fb88c864b28f48d69ec6e813bd685ea1d78b266b7f02c37be1b160582c164cd67e06a81b3d54cbbff03c54e9e27e3a994744d6c9c44ee611c9da937100bb5747f312cb174dd6fe5b6c2c3a560703762c56c7ea60b2f6323a0e58ebbab1437528b16bb9326aade5c959e1acbc68eeebde57bdaf4bd389814511e6b22e8b552f4c5fc8421dea30451dea7b6dffaef63f77f7df02fe566c69898a62ff074ad35255755dc0f5abb5b38a626f5d9955d5bd2a15f8fda1e6d4d48921dbe7d40f6bada58d90f6fe34a479405af21845b13ff343fa576eda2e9de5a67d4a535d2eb0804f3bc4ed4a2de1b71a11270d69bbe224942d424de1dbb732a0268ef81c91538641f4773487fdb5c6ebacda5495911a974a558d780957979bd663b52bbb3a7392d7eaa29d56759ddf50fc7ee324d0491a2e6343acca4dcb6257462a6ccaaa685cc6672a114bc44ddb306edaee4ae3a6ad2e9a6287d457a2b3bf1dd255e5a495935aaa10a809fcf0418d3cc3f2ec4e1844bf776f5396c54dfb76bc02dbb7a958a84dfbe0dbaf3227d5922781a2d877c29d02be42b6ef599071ebd6ad9295935a6cf73677f5ed574f7d350cd92e77b12f033476705df1989bd6651cc5bebd799b5109769a6c7f46d61a65c015c649677d577d829abab7f5d5df39df3088f6ef1e41e71aaa7f86ea67e697dba6564553ec57224ef24a1bf55e8cab6b7499fed4b448b889ddb449d8acca76b42b3882f0afed55fb1dfd75d3fcf1ea8d8c27d7e32df893ce9640e5ed9970d26f7fb2d9c8e2ca48edf7406e2fe3e11cc08992b797126c9150e2d3484b4b1f4aeba6e7eda848282144dbb6af24e995d6ba694f1cd9e2a8ed9af7367bd65a6b2dd7f1b8e93d85225693ad10f7d210378aec57cffbbc75de7f5e577a2cb67f6f20051715b65a6badb5b6d67ac35a6badadd65a5bad1da7c0d65a6badb565b869d2a44993264d9a3469d2a44993264d9a34b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9a91f06b11676838f822bb97ef924f9ff3f1abd48f420d08fe37f3eeff17ca7f3187f183e087ed7fdbd6fed6bdaf749ad1e924d43c5aef1e5cd74b256f9569dac4f5eaa55ea8fa94ef6adbe227c2caf062f2ca55e0d725dd13f412f75837aa93e518e9ae7674554383d57aeaf6d21267b2e279ddfea5bb5782d0f06d4c89a6d6895581ed052bb08f1a71542b645b04e34e04f8b8464424b1d13f2a74d41b62fb03408e24fabe2b4404bbe02e4cffa855ccb50d7f0e3cf6a3342022d39cb477da246d1e3cf0afb92c37af8b3de20571e5421e43feb11d0fb6cc79fee86ec4c6a0a0ef0674589a8404b75a5e34f9722bb16fc0b3cfce9349f527de5f8d387901d09ce0403fce9aeb15471fce92cc805f8d365e03718c09f6ea453b237fe6c31a8fcd9686837e4140fed9896a2a960e3cf7e42f66f2768fcd92e3d841dfeec1f987fb60e427cf2671729ff6c16d026340ed9df032efea4b2d0532efce8c07ff107c87e1a207b4a76f72efbd3b8423cc0012e0d70111224762f1ec71f2b3e7ef081d583d543795e3c8e19b62301083880cf749c4c2b1e56f7e27134c00b47010830805a73434500aa14d5bd188fa30c13e371a4616527a972460c199a16d25c68ad17e5296364519e236e519e9873f1b779ad6ff545e16b7dac17e9b5648c783b6d4b26475eca3b82c7f2569e8a5c7999e7026f6556435699273d8f48cf1fc8158618ecd51275aa25b8b0bad58845447a06919ebf4f4d37830ed5adcc6ac6fb3ac34c0c06f224b93d7f48cf5fe7aea2d07261b13cb39b839bbaa8bbf221e2f1fc85b036c8666233b9595570b55c3a8d04939e436e85f301a7e2521c4a953bb6a4463613529bdc9e3b172a5eae56e7b5684a7d2678b126aa5845a1cade13b9d61fe980fd4f1b36688a467ab6a56da1a576d9c09ff68806feb42f1978bbba518497ef6f5768a96318f8d3a22ef0a74d59e04f7b8383089f21434bbe52813f2b0d05feac4a26f067b5c9fe35c7101ea3454bce92c09fd515813feb0b022e7ad72184cb78ffca424b0e7bc09fd58803feac2d0df8b31ed921c461d4d092cf82fce93306fc595782f077253d00f150125aaa2b20fef49805fce9323ffcf3eec3c79f5e05186a72c1a2a5fafae14f77f1e14f6ff570cf3b901efc7de54468a9d628e04f2fc2e34f67c9de7917b2c359ccd0925525e0cf9621e0cfae3980e377217438125ab2aed39f0d63fab393f0e0e1fb10ad5273f8f70b3571debf55b46465a53f7b45fab3590670f07138114ad09236803fe90d01fe6c22b90037fc3d404f45b44af7fe475093a7f2279509e04f3a93228496b45645a936fc7fd013075aa53eca9f1406007feac801781bb4a4cd54203ea8e933ffdc21ef30d2f0d78c68954a9b9e8e68953a863ddca596bed5064a0d941928cfb1fe8d3ef591226e640c94e7ad17282d80a34fcd2202c74a2d79a90a94e7a5c0ca04ca73ccd1a76e19a23cbd9349801501160474f4a95742e880d552e77a000cd6803eb51121e5e9b95305f119037c1684cf72fd1efad42a20e5e9f5d600b15ac0ea878f3e358b8fd7ab96eeeb87f2bc3ebc7a00e953a77aa835b574570aa8353c6a4d2ecf3117d9519e9eb9594549800a01aa0394e75851ad525f47797a7debea5313e15844f4a9570299fea824ec17cc92a6533403000000531600002810100704e27034c7025970ee0114000f739a4c6662381689a21c07328e10a4182104000300006000000030b40152e97d7a4370e32f9f1d3d6c682fba8500480b849c2ff026a53a8a0a8c2a391e6dd3131b55d4cb3e551991aa6fe5cecd0a4f0ddedc9be4350bfe3de5cca9a118039761228662c07104b15366d4164d94b17526025c70d7dda96120c1a5e9c47c8579db100509692711e56b39589f390d72bd34fe54f04a4e7a23c081e8ab28e644166482b587e5d10a6048164d268732575a1e5d13f686ad6a802bad6f9bbfb2bfe2e3eded775ca4d513debb9c4cc489cba90e022edbb8c1d62e78a738944b916863c0b16c9a81975df606192002fffe8798e5347d1480b05e109569d56ed34ed0685d564146f03793104f334e49696d1558c73019b87e207f81c93ca5b7ac5e0f647d7922cc80ac9c0820009b36f1f0fc9955f7406ea83f673d90f718cad0856d79bf76cc88e6dd4cd837e41fc10b7049dc578659f3eeb550363a02a8f69dc2c0578dca5b70675a074e21123ff7145a58e3c31be67e552821af9fc29de71c6d43986865f147ce2db7d60b2dd571a996359ed78e4dfc10b54d7ed2ed1e7d60cd5e8ac4ed882a589ec7d391f5d2c3d762045ff2b03996eea5001648fac9dfddb064929108d94f5fcdd3625445c0ed1ae8e05a6995fe583e52a039bd75d20d09a2a595832c724453f20c453568707cfb0d7f5987a361e80e8f35868ff43ba9728bea600207f4b78a305c518d31f11495aad962489e06a55ac751151a39575200c8318d8327ba84c2d5b78109100c53b457b394aa0e1e4f7894480a34c4f316d5f811e0f5e2b5eae8ef68b9c3b1d7806e3207ff34b02f36dca945958c507e9b37ed5eaa0518b971ffbb7fa48ee1318ce25ae944555edd7d9ea9c6a68ceab4980ccd16000b1805655431e300bbc88ac146dc7c0d68fe72db25c94fe5f07468b5a15dd723fb3134ecbf50ed8bea84e32af740dc0d5b91c4b7127b059a7d20c217d5372bc93bfb74b3cdd4e2bc1db3a1b9aa44632d4f6c0497c6b62d49004ecb768c9155bd04e2a823ce052422d0a75fcc2b3d78cb91575d6657d6b103d1ff25366f1d9dd4ec15669d145d29d9a5141ff9d668eb33ccf04a5ab727fffeca97dab61bb3731c2d2a44b70e94d40b7a06e3aa7c6c9e4799842d7a2c401a553078a18f4655f13626c6e1c821b3457c64061f29b7643097ed2a87977aba663f99f932a666ad3e6be40eb9748d02d10ae1a9c91220b134aa56bc0b58afc08d6b30d065358dead5eeb76f90d2ce9699071278375d1ca1afd1b0d89095416bab33b5f2aeed3d55fb11816aae18b270f95be194b811a6be138f5af9002e97afac65230f7393b9f6ad9c4f3123098c2901e2ef24b71a7685c62f92169d12270d5da2d949985fdbd38c5f247d147b418fa0bd36a459df0fb5bfc112a2b0470d77e79c0dbd8426dd30fb031ee6665aa27bc26994df7f9ca9ea352aab64794df90f9c2287cc840bf39a87b9828e42c21caf9b6b717cf3cbe4dc984fa4c03ab5a65a5320a278d91713ad42915944cd65992672cd9b2c7dc757331de0bacae0b7f4545b291be08532762be57b740bc49dfaa0890dff6dc1f7b6c4acf489a5034368780d315b23a54ace5fcc409aa135cd3b914156a14884234d73f184cf4041e4f91a5517afbbb449b7f698efaa97d5bbcb46c889827e7b550576b625f8e7de5f560b4e1c5006ba925c67853444568444d1392d529022ced76205b5210f5164f5a92ba45d4d9c62b567908bb3e3d08ae309c359585caf7a4ee3ac314e0a3418d4d948775f313c48c764a807868a076d842d624c5815198a77ce3e09009c3452726f34252b42c9d29be2c40846dd862612b0936fa6a0c79e1ccb3a330b03e91b5aa58d594a4982a59fac6ca9deec59d3e7598d043dd46d9644fe9e9f7234909afc5d3462ada3ca546b35e41c21f698619d1db3187066a0df6b85c85503501525e5148e798e7c8d047d6eb7589b71116ee9edcd08c133ff9e63e34cef18b87021d9976c3d6ef8467abbed95ba3ecd2e3d985e72e98c362a7731db854906568475375b5b9e550cedd61e191be0bca6fdb25d86029f49b9898374ed107d2cd6f5282854364eb99bce32a8f118a56b5103373a288c14ea1dfb7855d7923cc4589959eab70fbe3b9130639d8e5fa6e34d3032819c87334aaa7b291d67e0a204bd2669808ed969820564eb5c05617b904664b32eabc288a20b23b6ae8ee417ae0c41b470725cbd859f854d92727905e617f8e8845c3a5a891cd9652b7fc75c7af8e4efa83f4daf2fde1cea56d53f668490c8c89301990181a17ba69c156f90ee4474eb1a8074901408723737f4017e2e2ea38b3a63d6f5a77356dc67f1b1acb266c059b133a8ea26a7feb1d08a0b41891eefe0fe840e801bbc14406c0c7a806eb1120062e3e800dcc69402c68ea107e0062fa5dfbde3809e31ea4941cf18e84d516f0c7aa7406f8c7aa4a027067a53d40b06bd28d01b46bd28e86d89ea45950179851b5ae3f69c237c6247687b7fa33ce687f160028d1020a6fcf5c9d27f51d43d9d43a3d310c448ba38bf5c5193a2dbb8528a188989ce1130e99861313c86acd540ca0d59d15cf06ffa0e3d5e57d447c7f0690478b99a103545c0107f8b82d13b1084547c4303904e43fe08d6964447a7724bb2832d0a62e54816f72e8f689e869608052e4c3cc4d32a7cef59d2821e09813b073ae1f2c69047f6d6486c5d4cefab39ff1681cd70de79530d254ecc4567bf786a2a7fae55af909fa71e19343f6c4c11e2a0895ae22b773481ab4b98ae4f84e3ded40d824b2e9318bf214824a9e227b20a5d05ba27bdb8c15b25c79a973cc72b456dfba64170c8b1006f6b24cffbb91552aea1e7c275a4b68753bb9fff7d45a66e867534ca299c2e7c30855d494b4f2ce0b52621bafcf7fc29f5efcd0a898d01398d3785c70e715f3fef671c3e053f8de614fec6f5f2469ccef158943eae59730f1095c14e6c877ce9fb7cfcb146a47fae82243e66a200266922a9436d278944423e52e5c3479eb19e3245f856b681478f82923984ecc1bce7050bc93c2a9f5078c3f01f90abb7b290e147990e15305c8f7647a727410bb5a28b677a1351ef227dad7a60aed8dd42861f6583d10a54f3a770cfc602eb5c76f0a3628e021806f180b01ae185ac3cca1ad28a88c916e0a379c1d317927954cea4c5afc9e8bd080844c7e736fa1732fe28cb4aabaa9319410d1abdc217402a58f61d094fe2508569564ca4c4d63389d95f31e31f522c7c4d2335464da3c2bb51b95364512ae9481965ca5533c2f7bfe69da811b4e14d239a46e8be944a928212a27a773ca2228b33dd5a2846f83b4b2db5f87c277ab3f41f9da2fdb99266f003a87ad53190d2d3e45de63abb7cbbba0832cadc0d3ebbf0dd320e302edd8a2e751147296d51f40d87ef4e6c293c6175d66fa2c08c68d051bb2951bc042d8e73ad1bd6cfc01af71995d577519194e5e8d7cfb8c50ad6ba3125b7fa9e3bf525a73ce81b5177de5ece0d7087f85430788a80b2e156c8aa12c71484fdeb15d8b955dc5557bd73fe66f392baffad536e007a7870b22a54b7a8fb7cf56266401dc25bc1a12942ca865b41af36f388cf7b676787c6d19ae6f4d939be8abedcd92583ea181592372cb901ec81c10bab54fda6eef9ab9799037608210b864c152a1bd7896d56e499d67cb41c3b5e5e5a27fd787d22fa8f87ccc1daaf7d3c4d1e7bc7d652dbfc783fe7fd1fef1b777759375a2736b66268b7d2f58beb7e2ec7053bd14047e16ab9cff97567d2782a32d5de236bed2c592deed796292e077bae3572582cc111378065bbcc408b15cc695657f3f5e7b013d13abf59717df0f1e04bb39cfd5169b95af86681ae061ef7bae329ecd20642bd1e3a5d6f7f172433a419166650e3208ca4001422408d0e959cc1ce3c6c140fda5740796ceb0b316d28ef3fafd89f79594318889afcaa192c350fbcff6d26188114e90f3f20a7490ef19e11da9eacdc82ccc1bb8cfca0fe1353c4ffc590cc71def3e29d01da7eef045908deafc31a0523fa22fcf30fc4e93c1fd576ca944661964878c817559d978f20ca033114297fdc2a8cba56c73ccf68e40e45b75bc2dfad446637b6f329481e21fe796f66ea4af906d92490c23952bbc3efe8480091af87fe5cc32e47c77c675efeb313e496cfb4b8a83609a1275fc94b0f7010efed6d1b40554a2ae6b3dfae5b4e3a19aa51330c8b25ca585480d0dc1a186476f59dec7245cd10b2bc98e243d135e875c843e91ab4efae7ae2616e8decffe0ab38147e20da81c146346aaffb022a0e9cee6dad14261527b4918dd87a62351131426a910a082bf2f107b45169d02389d8bea9cf24d66b97152028bebaae4d53409d84bbd817b98ddaa43d9660c10d41e6a1b7398d32849928de6c95cf0208e14ea8946e0027e215296f41994415d16e2209501421b5703e11b77d3b79c30122df1bbd611882a9be17bf1bd4be208ea25df42f3c23dac6088bc97486260c4dae76bc058e45f3306c168e8270621f45d909a7182a398ee826cf0f86f536f71fd364b0c13c51a90c950738d52af2e07af4fc7d2d88c0089904c9248c667f23e113c611a931251aae7cef5878372ae65e55e6f3047aed0503b2b2571e0f677849b37354c1459e0dd29d9a15acf3a2c9c91d9a4df5f7692bfd0930dcc4d1213ed0df282a2767d9430ef004dcca91600adb8ce3f09e5aae2a4fc3948b9290a6aa21d9f49137fe9670516b3141079995e11b7e9caa219fa1abb2972113366b55b18a309e8e32b088c5950cbe0f8ab8c7f670bb1e2cf06198ec9a46b8122e01c19b34750a047c5baf342d9c53b86970d08d35a49a0f5ad71b2e19f6f89c1e28ab4508bcd76b02f0e80e8759180dc60d8a07512ec8bf5ead2b58ad17f58ec1e109835c1f1bdccd7d3a309ab52bb0595de029c78a683f973c4325f9a0ab88c05d307c5491f5f8f51ad5842c6b6c759afc006dfba045cf19d5837c6ae2eec1b395396f797c37c5adfeb0d8b08c270924295565eabd1dca7afa2c7281c4df55c664f1b3fa6482bc6776326788e6fe2049d921709156a0c5e33e8a3a6524a3e452d01c4a6d1544a928b04a9d04c3d2998425550b5bfae82ead91f1fadb2cf1962bb2811811ff4f5f32d07f9164c07eef78d696dae4e44077f9b744bd68640bd23c9ebea162bd882cc655ba0f60c52f6c86682d4cd7eb0ef90a732c1d615278e99fab2e4bba609f5e3f773ce6bd6d2d48a0bf3ccdf6bed77d14fa4d554dc158a618ae228792a05a44fd92e1c76bd288b3ea4ce46f305423e6dd98afe78e5e8df915281aff97a9984fa7d0de93d62c55154c8f0b2dcb04439a0a1a2ab31b52d0a0a38295bda3c20b69145ade6025d6555c25f90fd6452773a8c16d46293e54fc733640bf300181e0915fd73ff8a95e484a78bcf43550deaed17ca7513d81aae33948ba18779fc7801cf536873a5b51ea719ca97e6ca9dfa05080dd77c116c827d8b30245b47e25557ae5fd9750a1bbd36fd7683b3f9e7c1cc6e300df76fb2f8b7f5836f0ecbdac3a4ef321fed9037b88bd4b5014576ff2255f5e7f102e7bf7fd7718b5c3e2c8cffb44cbf01e26aeeaf1e9b9599b95fe44ffd582fc664b7750247205708cd7d47cfb975de2114d2045bceb5003e6f5849a08cc5593a614acf7bf2c9e161c453575c357cf05bac01e01a897152851a721a2000a442ff29d3c6fffb24b3ca209a488771d6a8067a8ff3246aae1301826fd2dd6a4bfe186a2fb38e50ad105f34c0bb6a8882c6fa94a9fbcfc1556746ffadd1965cfef27e30bbfd4377ef4a5bfd52d71654c142e039c38c580e824f0c6bab0eac1bdc5b6252eca8b47aa322fafbfb786dc575f49e7542028c36475a49a3cc0781c6fe135cfc185e3da052df785bc17eb4bd01f947f0a589ca0870a8888fa450ae9c53b174618d6c15091a20968d6117ee649f8518ea18967b040e0d9bf4a6485ffb22e0d2b6b083a063b474838302eb84b81454bc94d92c4cbabd3bc30674e04e043d63d8a42cb23becf922c835b98dca8eb93e36c6578e203ff2701f08bd42e00510d45c4311e1ee36d4ff609c5a138049eec87e2a58ea877ede4a8296319e182a190dcd4efcbe2544979fef260a43d48af586e8b45c42f16197b81c4603f4e77558d77b319e48827d0a06b34b0230004a44bc3064f3a6388f133ef2f2c3b2dc55f895a54a327f0f0597005f0101c7fc8478d1712de70aa5ba125dfa7519b68321007f20e519a183bf99fdf18c15329aa1c45c062149fa63cfaede13dc9581965a1e235d64770f55da00af49db6ee4811c8e51f0d47fe972295898bc82c6c0a3c1fe50b9da2d4cffd757f3e0d3dc6e3b032e01407160625e16fa809ab0efacf45eaaf779533243c882378e00964e9c7947c75468b184b5463d7e8914e3ae41bccd8747c7e0a7470025ff48b9721ee38e1a0d112fc01dab024045e0028eefdbc9cdf41b715c9966257635119921f7d4946de0f06b6a72be1759369a078869c4f8a0765105201a8bc486665fcef9cf84302094beda494f6fe05f21fc4bbce9f0bfd89a6c94c014642a781bd8f17359935d3c02d640b1dcd24074927fde4a0031a8bcb4e40ebfc2074ce5faf3a21a853a640c4d4a0e6e0473fe5a859bdbb953650af5a5a0df5511f6d1417cfb915a472085b02d707e540098057895ba3a06d18c94da449828e1c77240563a1db6a491fa1e648945ba29351e504ced719f213d915ac023ad04f287d8a7981622cb519b3a6f9fc71a8d3ca2a76b15b494976edeeffa0ed94260a751ebcd40049e5f3d944aecc3c3464d639ab46ff49785d48c516927d332ad41c80ab24854d55730a2f847b49634121cca7536114a209df1b90300b99866063fe22d90fc741f344e94c438a6cbf528fb6d33869585a53d9974955077e1b65233b532ecc784eb690f48eafd78715b38f4715da69dae2d4b60391bac8ca0c4c588115d24711e0bd4ca8c0a3c9d743a26f99d5932e1e9990ce23cffe0ed9b02b1c23667574039462fa5bfae6b705d7b38287b3565aaca122785b8eb7a1301e596bf0410d69e981ba51396924d290bf90cb5c20b334e4333d13c81cfaa29a619f751125a2ff0c33fd7ef4a4006190a037c8abec4fa0316db6fb9c971905e13ddb0bbc02300c6f300dcbd36f6a5cb9e0656e6564d6866e31a8ef134557ac22c62b95297473c40decf6521cef2d87f722dd0b568ae05eb842847b811591ee0b5646703fb97200bd3a6e347c83cf8d87dc5093ef3ab82358e9e81e61a50337882b1cdc11ace4e88eb07205f7fc95fbd0e7e3e663b8014f593f5c2bff71dabfbe54d11743adc5507c8733822b0489c87d3aa7fdf374b8c9f231fb8d0fe20d979197264b56d723e225a150f035471d86d1e6c381365887c5b7b7ea4b5234a58c7264d96b0cd0f6667b0db2185011a60501d1e1c0e8281c533719e7a57b42843856210e2b6da2c245859c6902c941a9868df32b7bac809a37bac7b04b73e9c1e19ebfe0c7920800215eac85590c70bfef73934d6e1338dc63bdac0221f4f0994315b8c584c88b0b9eef28bca22599879d6d892d5a8d87ffa3c4ec482eefd7f8a0f3b165e8b8796bc1887fd41a741a882f45ee501767871dd3ef0a627de70d2fbe00902cb64e9353c55a8359c8daea402627ec4c041e6b8f5f6c34efe4ebc0d8043b58030d7d829f2cd702437ba485dccdf339cf5fd7e88eced00d9c1308fe106252e5fed8c5d6377b8e6725058be479960e057b9fa41879005dd5505ffbb5bcae3d59710f1d57a805b6a58662635c4b31069b42798ea541c042aa7a5df5fde226716a0a3f155a291cd153d239331cf8f80fd4b4af885a334f351c19389c7778306b780e1a8bce2c596777561139de196292a0bd097aedc6a7f1a9470fa90f280aca77c80dc8c70210ad3cb3327549c759a34b3825bd56d7ca2ad4576e19a9a4d2202c25402a0d3a73241a3d56b6b1632534385276e3e3cabeb11afdb04063829e9d018976a0742347ca357e5069a37fc44c9963d77820171e2e34d519b5f63e1b4f0fe39bc9458bb9472fca69c2506b5033c85194acf7be8d0109a533b26b156eb0a53f7b0a41380854127adedcae0e980fd8876e40ac0687e24e2f9c98b43e016ae1c423e61371160f27c95422269254d7c3d48945cf2b5118d8598b28720eff10706a85d290f6c8b14fb35b913ea0e90623cba35c82fd3fb0699b1d89a5749937ad0934fb4802c2990aec4fb10ac26f9a9320662487b2fd6e9d87349941582de83103f0908fbcf3b0b5b1da4db9de491a92c27aff7c5588c0ad93ae4d372b8c698b95f1983de80c7850c5e232f8c1fdd0397393d97f2b54df0ca05e5ca981e0589ad2c94705eb06da6517784cd811d5767d9c5ed9b1653296024a5a07c22727030b6bab89e161c42ad6c075e77cf982aebdadc5362a5d2364e471d93d011107a7b76721a051cda5d441f4d8f59906d7b20612ada33efa04ce45dd6cc6055996e49ae1ceb0680b59aa9dd9912ec532f06cb8ef85837a366415cbaa9c7ab3ad002c64580dc76ead90a2f3d03a392382eb31b6b492cd337f6d35c59699e5567ad8026faec9e736eddb5356fba19d4d29835ac8294050a1e1db3323c5429b7167ac25ae07bd34aeeb0c394fb5a927e91cb8c4266868826916b7cc0bdca5c495e254e2f71055d7a1d13b5f52ddcfc5d5d43fdee4cd67c2bd68805bd01fd52f71e0dc8576fe4a7e18870af325d03fbfb9ff529cff7863bef922851f17b9982f51fea17dfb5ff1f89bb519e78b8450cfca63be04fd44cc4ecf1ae30d68d76aec6ba4cdcdd782fcfb6fe9e60b197f4d41058ecd17bdff60a002daacfd500aa9f9b2657ee581b3ff147dbeda4822fee7722902f562ff5c09e67c71d71f9e5773bec22b12ff776f26427357bfb849d47c95ac1f1e599faf05cf9fcf7b3072beee6c63a811613edc0207430a89f3652fffc623a9f355e0bf7ceaff48a53f7869e9af0912c3ceb262be56f98f9feb7f7f416a4a385ff6f837ee48315f05f68b53fb0f2af183372b9aaff53e1726523d5f58c333e27b04e14be7f105bf868e3ccaa0161d46f2f147bca29b267167bde1125b69f02cccd7dd0f07d9288f410d7fc0820c45881c96e8ee7cf12d40b20f0430cc7cfd4339297a83219e4f9eb3f9b22bb693d65843f3a783f3651f8227395f19c87aa3d98bf736095b780b1dc8085c39a369f5896228f83fc9345f6b176c99de1b2c18148be0ffa4d2faba396f69f5ed59696c323441b500a6558c5f2155ecb32b76efd8a412f87ff7764c0548ce5f63105bce30342c5d096be54186931ade5b43b436a7bef7a084fae746a6326d42ac500d4d831e0c84930d9b1b1e8316c303a0010f0f1e9d011b30e0164705ca59083939ad85e6fdcc3980ab3d5eba8bd5308d885a9ae438a524abea4b385ddbde7ec1e56a2c719c648786a453e366a373188382d5b981b4028e4c2ced846d15f480a420ef6232ed562412ac592260765bd7d79f63ae5f0c6db58f08dc60f1b53c3995465e197cf7c7623323bc7c8c4d4ee705ad7f790d803601cb2578adfa511e72f65339043f226724079da3a7e1ab1f760bef17dce9497c1b79d2ce4dac2a2cb0ae53aefb848473b0cdea08079efc9b7e844b9558e794b1e0b62bc75a55e2a3a6e8211abdf331be6552d3cd2dcf0f82af133d1dc11bda9fc2f6b0d42b1ff4c307eeb135f07f68b365917996e672353f2115d21a706e565ceaa157b996e66b1b4dca25a0c202e609f5fb7872d3d33d53f71094c47e5fa93765b667d5dcd388ae0e5ddc06a013648c4a7e74d85f888f49f4c7e5f138ef970ea8dcfed6c3f8bff3ff946625ca0d4690bf1210b98defd64e619c0532269e748ba22689ccfdc109a0492c54c7e5594c2a2abef3cbbde198a4f090bb06c6b0cf666aff2723e57e4b3a7c7874e429cd4a961f82ddeb091d73c5cea13fd2e338b379c20451fe1014db9fd002bb88792d66dae60f3c70153d60bfb28c7d06e0006e610849eac8b049bb8eee3b181792a32922ca4fe4336215c2ae3c57d6706b223dfae76107227ed52829f1d289da5ea988c9b9d849fe03999eed708c50faaae4a50cab59bb2d590441dd2a19d3ef200e2c3834c05a0e71574134a22adb53073078c6c78f4bcdf3c0ac0d49b4c4c7ecd1bb73dcb3fba20b965f0347b7dec35f748bc264c3e7adc8f677a8ae9bee7585b0e877849b207a68c1c777eec0634d8053c6cb5f8211481880d75abd03c3c086bd8f33dee71ce82ef640d5d78943d52e1bd98f2189882d7ade1e0857299716c133b6a15af541d1dd219a6cc57b1938374ecfd6fa60504bfa97690f77c5ed06ef6117d8a95eb26758749676593d1569b45b2a1d6e363dd4da671a4d136b104d9b4cd86446952a1cc6ab711ebe097afdeeed782630b1f97382cdd6b3c37ebfb77bac6630188ba74a3f392bed8e51bbcebc85406a65687ba93a7fb8ebda368b61ab54ab7810b1d9a2b5e6925c02f934cc0f982b0b78e74ca51c24e1f9f859e1eaadbc1377c81edecb4d533a1c6b29e354479c30934303cfdc6296a9dea5200c61ed50263ba192d406202b99493e017ec31a60ec285b7f030d30ffa73aa75b82e3c7064f0cc2d57d3736d8b519f7539ad2efda680a9318008dbe4122db9868549cf68724ae4807f88fcf8b28882e6cdd06b7287e66792206a9f74f0000d3e293269ef89b3fac9d05e0af029771306944f8e42fbb9dd95388a4492c321d5d00ab41abc7dc63b4bc4d92050857f232ca074bfa169a1cd67260554392b7ab584ff482198b45207dc0eb55214f9e1720466cee071b1fa295106fe7a36214243fbb0df9e2fc0f8ce071b78be7ecfbd29d9f202977047dbb01557eacd15e2dea98bc7d5b727a92db76ef15b6a16139c6b0d33116a61b7b94e5edf0d6bc5d83186bba123280d5d5a01b9b0c4736144fd123b7a4ea30ea0225d3d87be6cb254272b4985507b158f4c794a02b6076abd577653b95966733bfdfbe2e5ca66cac53844f79865bee8fc7e6443d57c56c5f5138f3a684bd6fcca800f42cd270a8e171df26d62ab3fe062f6aa7af668fe27f0f99220b3f3044384b00709121e04a79927a0e1b1e4d0c6e1e665c4bb7ad0c2af00fe063c3a25404735ac163f6e5d3bd3e0ea99e20c4f46c3e54574c8117c874bae07d7df3a57905a8e23c5a370a272ca41f644f01fcf9dd9ed6e491fde20b482b40d8b13f0ab9a84c1ab322179d7b31c18bf1f03c76434ebff9e057ddcd8e3ed97614167bf3ab6fe97fcfb3c2d6ac5212ec82d98f857c72b1fae893c61fe9db6a412d3ec1c80afa578ae0c316e937baedcb22e3239bee90160fa4565c539bff2208d186b3ad9a7e4e578684b05b7e037c113e57a3b322508f81e2888f7c53116d34bd3f3b434071e93097dd925dd58e81bb9bf2ccdec7e5ae08cadc4c4bdda40ebd2373b9c6ad3ba6de2254b986e7b0532cf15493bde358366da5ec9d2ae398c681686b6e282d89ac388486f57cf6be4d3d6ad32baa6695a62640fbee9e997acec88752e1499430a2b5f404670c49553f78ad656882606091783cde8694502918d1b5569922383f2f9ea10728b5032b748c391fa71964d2857b5c34e16b7223ba3211dc20ef22152959882e11ee132c981c5f7442e3fb207e9ddf834ff4ce6ca7104f35cfa3a89093e0271b75598751595c8bca52c6ea5dec632aa82e8d0c780581acadd1d2a1b8464e5922929ffcb0f7c4d361aa0dc86546d0aa080eb4427b7b0a5b40ec4e94537eddd209390fb9f3ce8621bdc7d9121ff3abbd1c6ce4c2e57e72e56d1b8d910ba854ca1935e6179a54fa992967c39b9e843c661d09b68112f6678396cb729648cf39e050d2fe229534b65f8115093a3601211a49bc48f54ab43c1cd00ba248b4ed360966200d2bcb95add351189521d73f8a675ff408269ed9329d09d0876883b7b77a9f0d83c1788f0878d7a48774b13bc43bb023640e0e51ea97f4e3e8534261e41116f966b446493c7715555872abc7d7343975dc78d274d7c4029cb6d0115824406ba55028ac32cf2b6530bd117ad1c843a77a22f04725ed448796dc37d9c006f9e854a23e7a23a67df16eb171cbb385bcbd27f0069bde1e7ec391bd4edc8b563ddef87e691a3cb473b5afe9b76502998c03fb32417c2c90275528ecda82edb6a430862f4f9ea319cf57363230e3b096bc4d9cf7899f49d9537599ca05e554d58c6fcce02d0c0977e51d4b760d4a0b893141e83935c45801598c5be1f485c9c69968698ec601eedf77791b14f1d02a19c7889b73abe8f300ba0bb1bec691822e1a5a3471b62fe00b13f0126d6f0a84e37d3bb57f0d27c9ccd5e6f61daace304478483937aef60a3a709a68c93174b70762d390ba01baaf79ffe27796c0f83a118d043c6795d39141f080067f83a25ffda35b92694f471e1e3877ef0378c7ef656c8347c07c046f063f744caaf66b86cf3038ecb48d41f38c958d4a8778d59576579f52660963d73eb83a2a189f938afbbee6fd35cc0ae44322c1cc2af9c79531761bc0c74d55a368a3b56fdd5c8fdc48fa48799a712dad3bc6f335fae15ea276d2aec34155af4d0494741fcfd0a5c0e39cf52d22ac5702097389262962400fd7ab1e8a71003684a209bb619007bc1c0ca898fe260dbd5d2820b86ea15ac71d5d17115ffeaf2263f7a47f2c9858f201c1a699d973730c334956483b2aa91b774dc81c765b51ff3116b1a42221c25ce4d7e33b63c8aac9123c6679046a43d4431681a19c0a5602bb5317ab85cb674f1e1dec8085f14c828ee984a824e5b91f95ddd1aa3ba09f0c35d844ff3025f83c6c41f34bf02d604adc1c7e7f9248106459bbc58718c381ea277626c9f066176d2d7da02d72c73372a639ae42181a5aa345d83d91196fc2ecf3e98b36444aed8d4a000a0906865039a4353b8036b1adba45f5ae76d3b3e96c243fec3cd8dcc52d14d26400281838d3489ddca6e9ca0c540ac59f6bc2b77d0f4cb03702ff1999d4c49902fb93aa9592dfc7297b09b1beaa6e795ba9d1eed1ac8e7ba73a20be71f50f64387beff8a66289095cac70f2d139c81e0910c025143eb45ab5a502f2643c8b2cc1a2be47a4d8bee67544107d5346f2c36ef855f186e7bf893b5349ada19e6a559c46f3ec2a5b29a760e04cc7b9c491bab7009877e5872b77e2bea5925aa90963a5dad39c2d4f1ee03617dbcf67ddd0bc304f5faa4126fbca36b6e194c9c02023f1ff1a28acb9742433374a8345855f96808145982a898f7353e0132840c0c484649f17276e04b52c4fdbdde2e440f5cc54f65b626af0cb33f2dd65018dd57387bd70cc5f353b6be6e8c3ad1043299570a6879368d62a1da62481c539bb50f324402d2455159183e408054bc632760f3d14f09dd742590993bd33aeb99c86fdb784abed55e78dc59b4557f062f5f6529ff4f46fc6a019042a3da85e19283f60132a7b261c56cbe27060e65179bdf1a03ff2992c85196d4b3bee10a4118c7eea8847b67f107436a899b15a4693bfa8215a2e8742d2157080d8e5957eb806aa2e5e8c402ee1d4824baeaaca69df91f07705200a14f1eed17bc3019c109c406b0b399dddebc9d336e3d84227ca257433b7e518f866f7ec8b797c80f6b58cde68c21af0c90086eedb84574cd4a28441828188546f2480373eb7c64c380408a4ff6ce040aa2cd5fa2a27f7e3a0a0d3c9dc1dd7440180385b3a636f5442c38443d0648212741bab107ed402f9077cf55b178128efc3b2ac6b6334c2a0e958f2f4a137324ada072d88ffe0abffb52f7b87bacb2d4b5fb47e4157a4adf378ada533ee08b39d2e13af02af6e71066ee0e320b1309e6768adb36b1ca66a4099ebd5e941c823c3f8ebf18cdb3e99968019225a16a808c9f7982b3112108cf344bd69e8923a767821502a2b394b6496ed63befced3b283c8085d5c150e7ad64aeeaff6b507040fbd639b3b1ffd989339b27d20545dbf27b33dc3a8427095a1f292cac1638b25528819005eae2b0701b16756569622c6180a6d09581160ea38457e44aca0a6cade7863586155d3a871976ab935b55916adaf3ed1f4e6cab46d4684b977f9eb0a51251c32eaefe79728b3a424dbbb8fcc3c92daa889a6de1f28f3c25d419035e8dd2d640f22098aae726ef491585a43284e352a4b5465e790e81f3eaebcbbcff6121458b1c983982547e55f5857535fce6cf0ee884fa56b7a1178a0b0fc67a9eaa97443d21a497c68b4f8f1bdfcb8e3ebe1ebb16d6ea8de23d1a08136b84358c2947d1ea46d1841a9d42d322d3a7e3015a2cbf0a367dd3e0a3ad1fb8f025d542994a4b259a7a4527f9633fe3813705bfc636a8a344772c792a5bd2485f89c68de5709d84c8e5d2f4f1df0e9222236d3cd26c6463999fb93c0063b75a0ea1a988df24483910a2979c707c0975144cc741ec252cb3012f399c614a96422a76a28751b65028e3d8dea5e7b99f2f8c8bcb9e6f66d0ec54fefb1f14d02afb19007b37704730d5b45f23191f0f2d513a7432f9dc4aa06209dd9d6dfbe8142a8158b8355f835dd77032a71ec1487664c74653e18402eeef2944f62b934d3c6150f62fab614010a9052dfa21b5f4de57774c16b0f0e83c5cb5bfb134ba28a66d7e21047e231d44b26c425f321c395d1099a278b6762be303bcbc84c20a3561e0d89406608a1f5e73e4e89bc11bf57ce52595a7385b5054fb2d38ec7876af54f444e8c318e55e73d35dbead79af16543cd47655ddc5b0854bbc81c67051948037b0652ba55b372ca3ab314116c593901f450ee38f135e1e7bdc230ded476d7dbf49a2f09ef6bd2a44263684bcb860f3d2fd54abedb6896854e510dc4c9e3fb9078fd2697eb3a55d6af240991a5ff3eab68f2dfbaf8b13ab4ed3e351a914093e2f2297ffdb6d1926ae37d58206c55c1235a4c450d9332cc3dc889446047577165decb97295732c94c155e93a4afb3842de6ef2fb0c44131b067ffee7e9702b2e23707afb0c9e9b2390b37dc12004ec4782b2434bfe682b1ac7b39a25113ef082ee99637d02e803cebce38ee20002ced5da5418b84416814568964630d114ebd07d0550c97367f3158395bba184cf34a19a63249cf8d645aaa33d09a4ea2561d591e6c623e6c8b03d54838092df5e3dbc903b637e6ea78a14a437b005a44a05a9570cd1d42d32f191238fa07e3f74f72bacacb3a3428ed7612391aeb0a48482cbec5ec204ca8cb067050c499d6eab978cf4bd916cf94f143bed6043c721a4500a971f5074421a3e9c37ab0a54849724718bf3c152805402db35e52c010396e01c59bf1213a04bbd6a81ef3e1d3b51854c0840d0c0d4401f6bbb3ca56792301da65be61dbe6780439a8f444368a582617d643acfb9debb60690e2cdea13d669a0302b81ba300e3f02d945d4f55a8e67b11983138c2ae3aa851eb7b32a4f3a06a49a3a057a38bfa1586aa64f7dc16167ebdf6c7bac890b3b21ebc78936a3dbde9b8fa2865a12f4162c5353ac0200f7c998b48abccbb4fad1ca75fb44ffaafac4a13100d7b68a3b6b2f921837beea09a3c859dee88a86a8349674759aef6fae27a77d6885a0ce4fe20434f89e0706c605f33ef11a10a2623377bb050ac4cb0de812113f35530040b3252287c243d8a8ae9520493cddbfb2b2fd888e926ae3e4e1cc989afa1a7b65131d6dca642d0ea0347e3dce916e8c586942e6b7900a4451ea63279962f2547f456fd3a6b550dd7cd9f0c66ea2f8bd4be161fdff7c44d3221d59ca6d2dd674391d6c70bf53ae12e5e47445b2877945228ef6e4f744c16e4f74fb7fb6bb46e52c9aede2480ce649729e0cbfd64571e550b08d486fd3ffeaa1aac31b68310690583810a4abd8e6ec0eedf39d7275ceac463e8d01c5009b53378399b164271f84ee2d2988d8c16dc71ed4c89f6a391a62dd11c24b2104a92de4a3eb3cb8131f6a2763e2fe4af570693d3d5f48d2713205006d02ef40f4aaa191c29811d85aa899d91bced1be7402f6b1a3813a94620134f707124d89e349a2c1d2cb9b049578eabc1b60dc082b76edaf4cc5e4b6a36ed1c5ed98fee608e37e28b309268652af2fe04a9a932bcf85f86c71ec042fc392e62d4580fbf220668e0afd1478032cf3cd7e422a85aac5d1106c777abc67608c134aa5a52aa6bc33fea229c95a0c6454b1bede8ca6e868de49bc4f6a753dcb39c9d7664cd2f54c45a0743f43ae85782ca2208e96f6b5c4423836a709443b945305098d63244d28cb55c708ab3dd492515002a49faf5ad68d452cae078bb914ff7cbb045ab2bab84bd07f8ddeed02d934a74db490746a4b1039498105a47da4128cdb1b19b8057dce00bbe36eb434a3b4fd7a80288ae0d88922893840e671dcd433b35fadb56c405b4b78025d76739b9680df444b2cb9fee52f159695d6963c40fbd21ab1b816f17d8cb73146c3b946817e0b10e25e75591d7453f1457ae8410791c2126636e224a1c8128ba90039146d99aaa9e91c86b001d355036e148478397dcbbff16a1e9312d86372aa412da4d29da28fa44c60a912c7816119c93756114721e1fdaddf1608deec680b7a3e3a5a40bdb1ae0aa297b077c702db91fe1350642504bad0bacf8ca165c6fc015fe82b39139beb8cc29ace0571e0a2e99b33be389c8409a597e2222577d72ea205790c63e37e39501b501901b2baef665f27c230dcdc5794f6166fa871dd3df9ba2f18c330779649e28e02a3446246ea6bd1f5ffa8cdf55609a8e563d2b65262f81f719b54c4ee8049aeb9d0b7d2192d3c83e6cf6b250d3aab829f4ad52b30290298f5db68ef2b3198a4e6be0024068a55693a38588c0832c7b483f4a4a91bdfdfea7105ab6a5a5ea4b405fa8082899313801e4ab4153a14112dfe19d0a7ce31b4b826633f7748bfa3dc02495b446374ef3a8f64ff84147b1c1f1d993bec10dd81cadb71b55206392551b7539c178362b49e2635d094369b0450cfeab271259fc1a50eb02201dfac343fbb5a844aac38e105221b23a14811c9032aa196b3d797bc41eeef040309a950dd4399026da833f305d4e831d92842806e3e67bcbc9c609c1b35ade8263e9019164634b81080e5b7889897f4b4b55a0e0353722165a65cd9b01f952a903193657ce77b6af2f2fb5769fb03f8c330524db2682423201776861c15e6058c096ab4ef56526473e4e5dc5ef9966f2fb58911001edb09c0027984c133982ace54657609977ee742f6f8189a2fe780d25ea80bb327ea6f0cbedd046e546b916c2dbbe74172df94a43d02b01e41b5cb79fa8010a9ec4672607e0d5ca9596299a508aa3dd8c48dff2fa4b9544b07f7dafe128ac75b40399457f63e6c404eb84d1609e3f06cecb8b9c73083a562c3a47c54a4e6c7f4d7ce5f0251474fd4dcfea1f009f2f6cfe8f820033c01d278cb0f021b29374c85e6180b2ed6c2bcbfef7429b5d548ea5c5a28a6bf2829dc17fa4ed6765f5815d84b7e290f1dfe08a7aa42ff2e277a9320ab09ece7cbcbed125e8256d8e790964242fcd7455c44af845f83c1e5800214e1498b4d9937dde581ea7f4a847c7ac56252d90e69237ec30c5c74f4d592b309eaa5904c15b530e43184b368d8b76944381985983f4acf7550b3004868e140798e452832b353794956fe53ba3ef716fcfdcf2093b99ac90d5dca0b9caca28144a3c92c2c1649f189e3ac6a94ed28369e206c0e86b681b03ae818c581a7942532b444035a7dc4b86697b27c1d4cea705fa670e511b580d8b4a128b7d30a855579242e9b8e6b9d7f8f54d6d8cec0e2aa1b754ab084b5892363ebabe48af2aaa19612b98df65452e614bc516f603b3581d734d932447c65a58b492a4c7500b6c44cc89708bf8379cf3db75174999e80998cde8c90470eef170adab78f34c7018336bf4b59455acd604e61f75c30637f9c386a473ffaab493f716a526e116d2ff1379e8aa71c5f4e2b1b03b2039984026ce41bbc0548304d75298a65839badcf4b9cc86acdcccbfd3b5f6f23e4202efca5086783bade42d75f1fe1e7b4bb2b98852485a484089f7a363ed12e3f83fb2e54688e2db2e8ebdff628053d048e722c913ce04910556aa4b0b962900537507af6a6793471894e2ee1f7912013efea4c4fab5b049d6c8e17919bf07f726125b9762dc554189959cec9c50589d22a0e3aed0c08c87015ae80d2061aa5925c9835d410607708a6c9668a6bbe7ca59f1086f4654176bf2fe3c209f05345fb195328dc75115f4a8960884e90bed9fb186971aee0549ac4520b4d9fa66df4d67a371931e4e68b12e00535bc0eb0acb722d77cd132111b590c1350a4e8ba4d6391d8783729d1a22d7472738dc45ecf89210e4573be393a3eac16c41d0a955327644ed890ebbf677e067badb71b6b0adfe4752dd11cb3b7101beaf910220a871420ed171c6ffb4de2fc44f166cca401fb4c2fbbe44e1cd605793743c26fc7b0a33964d785382d7fe4b449ba9eb82b3b581c09143eea2beaabb3154d3b1ff33e118dda1a7b2c8598e12133bf73cebfab0a7d77331e0d43f3ebc6be19e6f3733143a64dfbccc43ed23dd6d0a7d6886f95d853004dab77f8b79ce7ab7d4b1b6687ae9d4402cfb053fdc0394ac883488056abc548c8f18482d8343df6fa58ba24aca4c84c2c82f4c7f0bb8ba6bf75644b14426af317c0e196a49d787be22cf933231b0f94a76b2fb02ea0c51c4a846c0b679997a46690a05364aec740caf21b10e1b30fca0162703a3b61d2e7efd371812b6c29ae292439774388990f9fb45f8a0a8c27ff408ff24dfb4c614c8855a585f580a04d12523e964194f36079c3c363250073d7b92b4b659abc2db14b32ad0044902bf6de752812c86dff69214819942a675e5b44dbdf03de6492349b5bd7eab93c520d683cf1c43a5b9130e8c22017908e8d47b57213db00ed8c2c915d351ed6a9bc1302cfddcbcc09adca9a2b53a7b78398d9598d940a141dac34f2f414e60687313b8a96ac0b0494fb1ecb4a843b511d1629e41873ce88bae0a7f439faf95365ecb0a765305bb686b726f08ad96a17d20366fa55d7e3cea0e15b013428f9e9443cd218dd760ecab6840a6e377f7c1178f9dc9d75e328cfc3ea4d6c4238b1deab5e9229a3a16d8a9fac1a94f19a498a0709206d142fd80ced6438193c4096c73105fcbd3790858eacca99dfece4298d7b1978114ca1c479b54e41098e4aa405379a75b57285430114fb0ee0ff5ce04790da31b295ede97b8f095cc94e8aaf5023c2d963e4e15d89c185c694aaa4659726c95c27712d78320842efcc753eb8348acd093769fc63a575f006c2603b1a70b4e0d0b4f4f4594acacc9823142b252ff08324152070d980a5a6db95c7a414de309d054d193445986da1539898c041b2d90eb8fb20bf46822b961e0ca1eb95733f6136f4ba02a0d4b88b621d59beb2968893cce2c2acbd9f4252955766cd7cd251dae12e3bdb4a430abed22e5b129c2d38a26077792fdf677badef40def8337166f3b2c3dbec62a4fe165ad24a642a7fe115467a94339253f3a27d46f1f3699630173e86ac434c2b4fc9df6c92346c588df5dbb4c07c76d6b1e18921ba3428f1fabd12bb10445e36ca070bbcfea3f073d62de71a3dc01a62adff5660fd99c18d4c67e19d7336a1f61ba43e84d9f0b719f9fa4fda36e2301a59725743033aaa43d828ba8539709567b78e903dc38d9694d8cfd25b30f3185166ea9ecfe29a86e4c6059489a98927f1a3331120e0be069aa1d541afc68285b216dc6ea6247bfeb452ac00ac95cd4ecca925e33c24084b93a64255564d8efb0ac1621b26a5054c0adada3cac515b325e3c3bec89449c11f93add24ac369ff9d415b0b5cb3aa3ebb20be349e659ff1b6fcdb6153c0617213f24968bc0fa49d878cedfd2b150c662b8757ae920c7ce01b9a757fda905c43993469f871a851ce401b5e4e6721c591d20f7a13a860137a42d1b5e913b5b22415858ab5bada1249c4db950e7509cdbc922b52e8b7ef0c8ecb38f4e7fc1039c60d45cb4532d4bf188fd0a8d78f1631765d54ad23a1e94f3bd379b6c3bce98ad29e2378ffde7ef0ecdabc82fcf476d29b60ac8749e0d44db70bd1b95be294f3024b96f3f3634abce176e94600f89bd7ede2036d974e80adcd4741ca91636306cc5841c2b8ebaa14f34e8e294498660b594865e096eac08d714894accaee71e55d20664e724a0afe26209b27574cd7d5b914830822528981bf479d4d1c4f04c8841a45c61fbc452cb97976b383848e7b27c448d0fed444608beaa3e8d9b8a5daf037ff2e66403f602a9d17de99ca08ae9ba5a62e53429f19926b05ce80a962c86a68e5f4cc821bf5f512f1b6543ceb7ada274814212132260c2d3919ad1446bf4d9702dbd76b43772298666226904cb2ac42db0fa03bcbcf0f9d779643c0146c18b8d860ce5a2490e62620382c2cd4460a9fda6070d518ef81b9a7ef392c77bf8acdea67729eea861e41f827b02ea5ccce749a6d26c93f923cc2013e1145e26f7f779c2a06c4c4ee46684d700626a3ff8c0ee9ab9c362a925e3354b06a6a48f66d531f5f0417f3117bf4a816ac11aa8614f1093204085ed0956889b82fcf94052d5cc5802f14f96365a00218704a40c78fb259ff23d3ef3f441dbab988c6d2109c63a7320f3530a115785c5bc7579199807e76608a6905ed4a67b4144db9042df00eb735004eff60612550cec99e016277fcd9081771ef151a6256f8eb30df5086152f5f1f1c6dae0915bf9d6122f86c87b5398f2d3cf6d17b04bb7cb9269f9c078fca634a6faf007085a5643bbfdb7309adef4d6a3ae6193df844f662f8a512c672539ef9a71abd3a00d7fbb566744a17e258521182e88e26e84aa12d505d98c8cf4f39250e02a4344675d8a4ab472b1d75d7f9a0c0416eaaf2d5c493223a42459e1b2fdbd5d80620124433af0b04b7c4a033f7a34db41c809ec82ef839595922902dc85f14b7051e779dc1304695b04f56da84769d89647d2a7306109f7d8350bb714332e74a47518564e3085831f3f0a5452e5817063bd9f9006029b52b915b36a005ce7f42b1f8ff6fc52f80396c831567941a1c4bc9b89b69193f6fb9883b27f5440041d25b316082b8f235877d50859d86a57fee3e6192fe5015a47bbbfdfdba332a53187502cc944f2d9e122fa84ccd4e81033f36bc048e811bb7eeba9586f7850ca7c7f2d045e382304b40cdd968b460b544b2e0549944e6862d233b678166f84217422ff8dc91951446d5a22f53a3f0b831a81164e202ba3b4a705a309c55a503c93bcfaca7603173881b61a79cc8d3f13b099ebdbf2ee4f330e5cc2b7b1441b34d2c12b0116d080e3813785a80b63088f0aa300082c75834f48e02a10d3b9f1d10dc0ae8fa7851fc206d93bdb7dc5bca2d654a3205b606cf061407a3eceb7fdad7cf6a56b36dcb326bdd4d93b0427944966dd936dae46b367e66e5ad1608bc3afecac7000f65f9c5864da3c57f636521c24506a21fe1cd47e39ba994625cec629cf14df1a515e307219f6cb4f4640dfcdda5fc96dfaa3b5d9eddbda8e5637896179fcbaffc07a3f29fe93f1933dc7f33eee8378dbe15b4c473bf6ad03b8580512e9c2d31b0742f5c7c69458594c26d3fec781e4130a06bbc871f81a6a910d36ca0c3f89787f1b2e3c7cbbbcfe9d3caf0d5621f2d8c9732b3f4359bfd66b51fa558ee4936e5552ce957acca97eccabb70f11ddf17eba3033e601876604e3765f8186ccbb3d818beb32cef62e1f5ec9018c07810fc0def598870f0e192ae813bd83fe19cec08fd7f5dc4fed729b8d3e5a9a597e3da859be6334171f9af3bef33dd17cbb77a5fdf8705aecb679a8da6a3befbd7ce5e477d7979d6bcc7b64f921f18d67398e898f9554221821815b92717fddc39e3f262699b5eae8bc5e1df2d31d866b1ddd906b22e6c97e6afd8166a530fb5c9c9aa4d4f6c43894254d4a61b97443ceb391e33bf5ad7e99da6815020c9c166f4747933461a29d0cc0c24b8af61d0cc9d5fdbc7073d3c4d23a96623bf797a7ca4f46090d7c00957ded2a549633ed38069938d778c531a63dbd7abd6fab46e5b674221701d4fe23f4d33fd49365de84e5f72a703ddf9ae639a9568f33ccfc3bcfe0ffbce3c4ccb817941e0d5b20a8dc823128aaeb17a3a664a24dd53bdcf5a9204f33e180974e5923b6b9078aff731ba57a591461a6bad4febd033e1280dcfa1ad050dad456bd15ab4163364cd349badd6d16f3faa5eada37ec28413f1215dd451b82ce3aecceb9f0b52cda65e1ad619a51c472d4f87c9dfc88d1739d1f54c344b939a8e4a29a5943ee518631d8d481949cb1173a3913645bf304c7bffd6beb16fecfba26fd16ffa0d5fc6fa9aa6fd45c5ceb5e1b2122a865c9d5bbf3de004872cdaed2d1bbfb28d9e313d8454d3bc637b7b7b8d36e5080941218e99d7cb14dfae6e359bf66ab8ef68d335bab6efebdb83a1e5d8bc20f08ebc6ff4dafbbf6836fe57d5a148cb1173a591ee0aae7bdae4d3a689591fb0a68f340a6c9f8065e64bdb46ba092833ffb29d023e1589b165d072400fc3628cee1e638cee98c530ccdddd1d83e1d66f563ed643c3e299312589410587bde683e6c520f1fafb18f137bce99af5d7ee9bae81371e33bfed6d96db0db7d76cdaab712104b276c0f379a4f021c24f10cc0f43e42061028b54497a4564678913c522dabf68382cff71af813eec3b6807de98febb8ee3ba768be67da3996d56be33e9955779cc5e36080dc20d5d2d9a0df67dfd9c9a0eeceb91f240e025bdbf0ee5b891f62bd6bdc0a7f92ad6b76099f9249b623be8b24c29d74dae1fc3b4fe79917d7bebb48e7bd7b48ec7cc5765475678eef489c1c423bc0284c2280c0a8402a1cc6f0cabb4695c341bcd8b39eaf7d083b6697a3db2fe89d108169972dd9b646031c61873c82af4a236cdc95ca44d93d9ab39646ee7b5ac13f916de052f8a3751a74d45a28a8948e4ceafd4ddfd331fd2619273e7e3fc5ca771fde9674c13dffddd69f62a9a8d53af4fbffa80b76af4fd3feb5dcb382d47f5a81775c8e6bd2c5f1623384bb582abd334eea56836f45ba769b8394d07a594c729e5b8aef35a6d07058e884f4430c57c63919c35f3626f35e15a3c345c66cde2e198f9fcd60eb8c6899c69b2f82aba24704490023a748bc34c1ac0359019c0a867614949990f3b48bcfd3e46b71febad07ea51a2151c979ca89af12ccba78727899319a13bff3301dd196314a21d1bc6087700c5a082eb17bad87f91e825134659b7a88bc23d914eee7cfeac2177d219f7934c38269a81a605c5a73d76619a57bfce9061d67a42adb5b29c586bc85a97b57c60fd00ca24b17e82d0eb1e14a08e996f11d11b0e67e64c1d6bc9eda16f08881835ef7c79a469fae7c72246f9ed68471b722a124199f919cc6d09126f7d1fa35b6bd478fe66ee8c36c11a6d829a8a4950f733339f462830dd0b579ae2cee79a3805cb689aec3f32cad26c2e4fdbfe7abe17d4e0f6d47e33787e94ee09e7240a5d9f8d32db3a5a8ecb0b02efe6691f8ff069be66a3c51389602a3ec13233fe74dd84019fe67f3d7b645eab34af8bc030aa6fdfbe7dbbbbbbbbbb5bdad6b1a98fbda5f580bdf69586ccad75abb578acd5e3e331f3bfeb38f9de3a999643b66a06ab874ff3a3b5787038eb653ce2430fecb56fcd1669aba353241a519960dea55be1667c7e82626434683db4d743f33aa84dd56b202f4693196fb7d0cfbc1fd18b809aa68552cf895aa73fa35ce7df3a77be1379cd4cc7f4fb703f5dd09be6571db26a31cb798dc5e331f329e5b8ae33593c98f799e65f51cb01bd20f0cea9025294123ad8efe5ce9f613f7be7e7603f1aeefc00d82fe6cec7c17ea73b7fc6cad84f873b1f00f6dbe1ce3fd94f0077be8cf937d88f8170e7db603f16c29d1f633f2ec29d6fb21f4f71e7d7603f56c29d1fc37e4cc59d4f83fdd88a3b1fc67e8cc59d33f2693ec9d28ce3d30a9f4ef3ad75e1d3fcb7cfa7f92d16864ff359ac894ff35f5819337c9aefc2cea0c1a7f9d4d6e0d37cccdae0d37ccbf2f0039fe67376079fe6972c0f3ecdef6c107c9a1f8365009fe6cb601dc0a7f9306c04f834394e6c62f1a2d9a5e5f0fb75a09f98dab4551c52258dbc3813468d5ebe53c128ede5c32940293471adc03519802b98f222391d021c0ab93075d49f757efc19e3c426165fb08fef126d4bfceddd88031da13d590a5c4995d750ca65dec7fd7d8dce9167a3295cd18619a3525ebe4fc128172fafa75f5d647fa558782d1731e58a1f6360c10589d70784722fe78bcdcff17a4d9b386b7644db29499f2b6510aec45eaa9af868d986e95c3a3a2c3ab00743aecc65254d726ee53229234e08a313a6b71dcb34d226199530bded587ac5314c539f7b0a5c6311c131f26798a1a54545e57ebdaa334c2348b6b83442f33156dcd7ab6fe24c1c46652fa7e7e265069a86d4342adf45e19e48e8719caa0aa06bbaaca40a23b77233c3b87d42e213e18462912bbf98822b6ddec693aa1f9bffd8878707fd176f7e35a9a983e850679b893669c131f27d4c3c0f570e5757a05b74f26f9ee814b9f225a402c7c84f72e57f71a8e79174505c6984a72d2790f82ea2fbc20dd4c29572c592c3c5ff66dcfaf1e757c144932817e6b29226417736b95171f1461ca84d52d5264971449ed8d326f92c48703cd248d3d0974f781298a6be7c47421297095c037f2691212b5cc9240a3f4db831c65add53bc96f8b625fef4236d92360e8523e9fef39e179f03b5fc572a595f02c4712ba516fb837e0c2cdd8b15bfe815638cf167a42d3a4cd7c5bb788ef6240253f0e7caf944aefc9a923df615b3fe632617cf544b07ea409dcc8e2eccec96592db359666b66b1cc5e99a5999d99b5322b331bb3142fb30d03533c19243bc35917f69bb9f0760696e4482d635182e3872b1746c59757fe0bd7401b32c8c9aa464af972fe77ba72fea7c395411e233f4eeb31be73a4f9b065175cc9874fba866f48ed72894ae6af29995439a51cd7755cc3a5ffd550e7371f7372a3f7c1dc59a7e5ae65471716e113f6a36bd98d68bb96d59668d7b2d94e762d5b9d48bd96c5565792eb5a96b68a5ecbce228b89441231bf43f0097b1bfd835f0b864f582973235da38097ef2a074ac07f9e0401ff790f8fff1c898d7ca9f21a0578fe9912e03d02bc8ec7ab6c3c2aff878fd1cdde87052ef79f0b0fb2bea44d52e2a0b485be13361f6d0bfd94bfdec5f5a6fb23da789096bc8f722b5e908ae724effb14cfe36372c42f3657018937bb73b664ff23f6137b0121b757d7038177beb42d997cd6e25f96469b8122b293b4892b4d7266e53857aeae7c88793336f249defe3a433661b7c43bfb678e011a8ec0892d1475b7d7b081ddf22d39c3a13811651424c2296804cb44143c824f10070bdcaa02ed3185fef510875130d371227ec4772e1351765de0be19d588f810074211e34fcba1b086a3c59f71402a862e2ba122e87e8d73e373979550e1735bc321ddb21668539c71af02128787f458a6746d8a222dd039d80838f8f1299defe3a2290f2da594523a3d2e2981bbfc1fe518638cf37a0861c9faebe7dbe820693fec2b11208037484fe251f28828fd0684dc287db5b41e7cc030dcf83e6018eef62da3ffdec479dfa69691a7c374473ff2b84dd9671e8c8febbffa41fc3aa181d2f3a5cfa3e495be0821374a5e112b4faa9e8febe34feb311c51c84ee957523c1c961b25cf88d217d18122843c894422552260186ec4407e49721148a552a9542a954aa552a9148392f74ab88f2b91482412894422ad90482412894422adbcca1741fa0e94bc2252de2b82f4fda6ee388ee92759386a9529442082245cdbb23ada32cdeb081bfd36d2a494f24a2b6fa4aa4df3d2119422c8b6aa8d465addea56b74d04db9669da56b76ddbea36dab6d15fdbb6d5abfe37faaa695f47d7b66919a423b8c96cdba4dce426b31abfda56ddf9718b4fdec8eda23f8a5936920fb7cfb2d736f7cfa845475bdd648cb2a71ba5236e44afd1e8e2bcef7aed29fd9115357738aa998c728b18166374f7186374c72c8661eeeeeed8566d1c59916a96f59e8db6bad551b6d52cfb9a6deeeeeeeeeeeeeeee3fcadcd2b6edaa9aad367ee957ea68341a915452b8abb4a2421aa570d7685437276254e65e54a469628c476cd9b66d5e8f9ad979ebc8b5eda3df25634643c998d1e86f5b46336ddb1855bd2d18b5c922aa188fd836a2aa4911546dcbb4ac6e9ad7512682ac6e5bcd6a966559cdb42cd37e9465353e8cb27c45a110c99d98d3a558be42334b80a227719f1170f0e7739f6ff4fe252fc553add8de41c2037f2090752111f8241f0a81656490735ed33ddd951ea677fca74d328917a30927254dcbe87fc48ba485aeec1d2170867e88877e72767e87359b98eb4f1afe04c529ee4fd3d8943efe40a0a609b2341da58f9ff5c8e27716a397df58706af47245a57786da49d370bfb33334048b6e689b6c749191acbcdc8e6c6faf25d2c768c2c9b7fe7a92d7a3f440e0a53f8070454d43bd16fad9cb874499f7237a12cf7d562c370f51804ff24976022c233fc50ac127f9bd2361ce902b7ff3768a7654dc111f1fe83e2fbc21bf90dbf6f16e9a17013ec9b7f0421f46615e0f9707044e1be34e0f4b54f9de7dec3e1a89ddabeeeeeeeeeeeeeeeeeeeeeeeeee8f80f7d25d03873c463e84e2b462d3b257e6f2305c7ef8bb3c1c924f5fec37c3f2f548dddd2d778bbabb4777187fd97ecbbefc9c99add147076e2e7d17db375ec3371eb3160b230e89c1cb83e06fd8779ceefef32557caf09f2be14a5574892a3f72657cefaa9d85570697d7349beee367ddc7aefbf8361a86ed2350ecbcd8262aea18f99176176cf7cddfc5d21bbbc5760cb6a1b0ddd97e619ba77bdaa793b449be8aed1fdb294b9ac9665ba887da499b560dc5b6b54d64fda65524e0dc89df507b657b8d10dd7537bd3420a0066fa4aa8f48aa6a1a2aa3ca481f6114eba0943a8e9474c70e6a5974d269391497ee7696482289252cb6dadffaf81d17711edd0a8910ca20e7764bf88404f149fefcca32f23b98d9ab995ed1e8950f8e54b575c3a6eae3cd391f8610cc4f64d095f325c0292b0a184516777e041825c3cf7f00a7ac1bdca00977be031ac0292b07ddcf5f00a7ac9e1e26dcf94130aaf4f311309f07a3b89f0fc40e46a5fcfc1cf37fc0319f0746613fffb21477be0d46a57ebe000870e7d76014fdf93ba4ee7c1a3ad4dcf93318e5e2e7e390c355712273e79f38653959dd70e7c770ca5acda78153d693f996531614198c7af1f34d8c62f9f9308c6af9f9cfa88f22c39dffc2292b0ad17c164e59442fee2c6219393f8553f3c68551f6e7778c9ae1e7af306ae5e790d19daf716a0e997f716ae2706a12e114fc6119f9f33346a19ef44fe3e7ac30be359b59677daf1ec355afeaacfe238d5d5c9ed9f39eb9468d671680009e39871fc0009e99000478661b369ef9de67be71e3990b508067e6818767c681a38767eec18767f6e18767fee19973e430808e1d40bcfcd7f700cf6c8067d6f1cc3b9e1988973fc0c3a036e5c266af625dfc0babf22cf6c5db96b7f67f066b7fc5ce40b2a88f96f4346cfc03582076e830408e1f7ce801070f05b8716d10600002a8e1b968232b0ad845117b5a090ede4fe6c81cebc655e7781359ad563a32ca182b2ef2f464d1c94aaa5452155731ae564db86ee4e11d708d0c32657165aca24b0247842b3f480a187b9aa6c5b23c1d5753f73eca75d5fbba155c3592b6c195df385e63c3281891a777fa48ab5a8787c7ba71f9977f79d40dae7ca8e3315af410ae88c22832689a669b6e3c3b8005c2eeb0df1c72e5ebb006b039ec0fd607db83c56179b005b037ecb5362c01ec00ac006c0dfb59493ecbe7b37a3e2b0757dee0ca287ca0b7fe8bfd01637f481aece7545cf931ece7565cf935c837d9cf9f70e5c7d8cfb5b8f26db09f53e1cabfc17e7e852b5f86fd5c0b57fec97ebec5950f00fbf917ae7c19fb450f5cf933f68b455cf938d82f1a71e507c07e11892b3f07fbc525aefc19f68b25b8f275b05f3cc1954f636bec175770e5ef60bfd8822b9fb8f2a7fd620c1af60e1fef0e3f561dc7d1b0f446243065f5c41870caf2f19901148c92e1094e594992c0e008a3626801a7ac9f1f278c30aa5b01a72c20f9515a415046bee44ec0296bc91214e8302aa5049cb298303101138cb296e094252444821c46614870ca1a1a52e2ca94119cb29c3839028751b4084e59ab5508aefc38247a8053d6132823ff0911577ebc61948b2f70ca826245d102a72ca22b70ca2a2a6294cacb172fdfa3308ae5e53b1446b53c94305cc9852b8bb270e53b1538356fb4e0d41cf2044e4d1c2c3835894099278cfa97ef2b46d997ef4e1835c3cbf72146adfc8d15864c0187481637aaace014fc813252885157caba813212f532f29052520565e47b15577e95c1c2f819acf7315819beb3317cc976bf626778ce962c9bf2975d7994bd1eb3d6a72c060343030d3162d45083c9141363830d37dc2043c6e9040000c8c8cccce08043000290c30c1d686a6aacf5feadfd16fb1f6d7d16dbf22f2ccbabd8174f52f9ccba4059d2a72c1562fe94373d30303d3e34d0e09324468c243f35d4f00364320105c5c4042db1c186254c6eb88189900c194243a7d390130000c0c94a4666f56466e609141c70f8afaf1525001f008b2887976f15cdb8d161084dcde76011cdb08a74f8af6986d4fcd77712a1f1f2e1cf0ea8971f79522f5faa684c223bfc87faaf533c35653f79e7a3ec0ef6d3ee7c1ab6c67ea53b9fc6060c08e2e3201e170f2a142559dc5cec5b96344dd72a83241449e00782e411244dc3f323819a86956481a4fad030218b340d6c42662e6347bfc785792bfc36c61634c5bfbf85d2b4502f08306062480fc110dd1ef29a215b8e1392b82e6ebf0c28b77f865e2e55ae456bd489ecdb2455594bd5aa4d7cff93aa0927bd4c97d71f07a54d7cb1a63f55fabcac8b7e400696a6437ef48ff6639f194a67e80cf5a80302327068b9a0762b1e5cd3fdb00f8d1517988c2d0ba463da02f3095ce4c11d733373f04636d1441352a083261091852500c1082683ada5d0b19ab03a09b564741f1b3cfcb0e36a9fee253a89096e2b19e2c2c5c2ec8944e421288d81fef7a2021bf73af891330237c4ca95170f38285af3a2df835ed3f2be19bf78c0d1ef182dda91abf7d1ebd43dbbf093e1c5961cfe43ac5c29e4aa3eca3829ad010b5450051348413483254490b0020b374f1899020911de854e091472f0a698cb4aa09033e4244a8139c6083dbabb3b7374b7722cabd4a324dbe78490debcc0b2a4bc6c14d57d658815d92ea57b1468d6b1d00b42e8ded10931ca187d7a9456a06cbfa8cbd82e479ea3bcd5ca985117955718e201af2025383ae9c32160ee0f9e0d1f7057153e4cb80b0913089c1fc152829397950c0de17e1c644d81ebcb4a867e6e5007f10882010ef84cdfcc500d2e3883a19edbdd65254328b8d96525434a0c2db9acb0b2b0e4115a8cbabb3b0958777737511260b0a4e87a6a1241c8b0a0318340e765c9e891c3620796122c7ca0040b15164b748c21a1b258c1f1f3e49fad080f16f8c7dddddd615fe1eeeef0dde18d5be1eeeef0dd615311b4f252c8dddddd615fe1eeeeeeb0a9707787dd56b8c00861042d7081e96c50c20b96d0b9bbbbc3cc0a7787ef90de7e7a8419ea34dcdddd610b3984b0a1bbbb3b6cd81fbffd0aea4da2c41398e088289028e6fcc008482424736010a23044e608711690496f6e6e804c189826de334042820b5f68753d71228a9550ec264cea143ad06122b4b2d2a4b3a16b52430e51a3896571436432abc2da81aaa027ac1d21130b101490989a44af42ee48f8424343425dd300891248ac8206c988104830048d26b19b4419ee0584603e980f12669a0cc141540ec141420ebd8a28a1bcf023131cb2dbd8022e3e905e01d2f0e3c38755945ea004185cf80e6314b0210ce2eddd363ed422a4358850d20641e55218c53589a21c983641ba84ebda44654b10d606484a44b1ba610521df7acbdb40a5c20d29e4065dc2c536411f8421073c37a4ad54b8c130edb910efd6a92dc3841f70136630849d8329a61ee304ee7750254482d09de919349070330594db7d23fb511fbb3f7c5c40c885cf1a10bfd8430b045ef9f5bf9b0167fdf536faba3e88df2bbbbefec832cfc7bc3db2bfbc1ed5f3312f7c7a2f7a21bc7ee5f282f83df95b1a0e27347875c49b1b3d8d0817c1c0cf2a3ef0e577acf2c5d74a9b2cdf8fc3e447d6d8636d082b1dcbe5cdb812586dec9a966c24976345e058c6a7500bb565a611818b4fafaf7ee66535cbb2ea4d4bc22d662a4a3e867934ce80e0afebbaded2705c2b1d0bf4622ceaf7669ae2579bd8c3ae6f0cebbeb83f16c1656dc2324a443fc1f18f5092eb8f55593b1646c98f31ccffb0af5e8c2d38ec690eec7d87900ff368e493bfb4174cec2d6a7561090a821f1731b42ccbb2567ac049539be42481127ee0812b7f07a3b426b8cfb4a203eeebae7ccb6bc921a97b1633d7987f986db8e1d1347c2bd0347de503c1b910072d903ca404073a97a1dc86968584eac2bef07de80ba306848584ea5adf9a0d7df850b3bedf86dbe81a330b07f5388fc78e364993c96b7e3059f2c5161c2b11c2f98058969573012db1c4124c2cb104134b305182122411218e106d920e877084bdc5e655e4de84e093fc3870f9e3e5d8359cbc62a4b262c36364caf3674dc33900cac867526af4fcb55a587b874f48963099976b8559c9f64e36b238266e0f0d24501429954adcbbffc87fe43ffad2c7d2c7d2c7af2a59e9bb2bb895954a7f7a55faafb5b8f2db490f354d29eba151d574944a25cf7d873a1177d3e1c49c4ce3b2cce2c87e1429ed8ec0a722b00c50907f6c1a17596c9a14984294118dbc87dc278615dc175525ca715d8adfcac2523b26701d5551e535ad22bdca8fc748d37fd7f950ae7b934e09feb0d0118ab0a463e43fe99c08fdf06987e33848848ca8b7206254ca977de939cf38523b91133911b4cca7148fa3f7959eb6c9bf244ba51f79a5928a2c79a39ccc328d9f9f187fdc67c77f18e53e4de3dea5d9f88f7ca8a6c3bdd1a829e5b891e76265a6878686389b5796be81dabb1fa6aa6499c5e1bf33fad1d7952cb3b06bd034e8200b49765eea835f21f8e4b7b6e5bd7d1db8eec51a178cf294776121c78cb894ec5952463e7ab7cca7d1e87d8ceec8e3c7349bf9232fc8ce0f912b1f73a1e588b9f545166d3b41d24241e84d79ce6bbc0835c241d173653bc971228ef3c40130d558b08c0fb9b2b3e02bae24793dc427f92c4a70dc51e5f47a9afea8d46cb86445cb14a2110000008000831500003010080644a2e178244af354560f14800e74a24e684c1acac45992e3200819630c018600000000000800199ab11a00ea59035520a784a3689e404f64d1040249836de5456d9c30a8c91a008258a23c6fe9f80f8641ddce5c3832dd2e06b42287abd526f81175f47139b95d0cd9e964a0d1553f67ffa906d693e86e53fde4e66d959203a65ba40800f5db48765ed253d6c071b4de6807a3ff11067493ea0385ca7eb41c03df69505b27218cc9b6d1fdc93bab74e55993ec525c19209ce46eb48263b22f131cd6b9e411f16b1c85016471fa0b35a9269a02cfbff52482a93466e177868bc9aafc724afa5f48c49809109c21e3542017d488749851d6eee3e3916edc8acd46e28e24010ed9f557001ee8dc37abfe4c02fee7503ea90b5e9f02d8f66048bbcc5dcb97e01622db0a240577d4136c1531bb21c96e89e9e3f7bd6262b24757671b502258ac33767c44e11a089204169533c53bca7a008da588fbf2fec6e75e959ae8953a1a0d455db7eed170a6252d89291d0707410b8adec3d1580e11e110363cfd32aa5b378fd15da56ba72b6202b3315331e87a68e2326ec8bd2aad42efdf9d4bc0816b77f2ad3b9bfdfb98932577a8875b7f9ec7b5d2b99767ea989a712d87dba9f2058e61b518431590840dbfae20420b84f616469bf98044bb71880b3ad53e59a17e31b9d1c3aed0242d580d53678d9491476a3c7dbb98ad89b4f00db9c08b18c4fb197b9f6c9c4de9b17ef40b8da8049f13d33e78841468a960ea3a63a9c8b8cfebe3d607ac43934ac6e2a856f62e9d8e186fd3f1acfd316ab290518ce0c078988f20f99da9dbd7df061144a03ed36628ee83b80a9c897064158f126e359facb00a5434812e0a9def24fb926909c444b39dcf79cebca72b35bf2888de37a6060d6d9be6666672b3a0dc4ceb95a6ecc71072f77f82c1fa9d8a52a682f72609d7da64c0c76a334c3c005e6ae433c340fc532940588488440fd43fb19a8217049b09c6fc6ba6c96a9fe257a94adffed79a6f4cbdd91fc50b24e7bd14d3c49b16171a2d5dc41ba7e415226b21a3df2f9c48185c25272f8d2f91c544d08481b7c5d7c12a379c085f6620e960eaca9e98d65620f0658f25f4b1f5a88d80602730ad8b48df6d1db03ee4d7e1b0923f48691db3072b1b84333f33410ab341aca402cf0fbbb586d170422bfcdad5aacdf9bd76092338151187d69ad1d3e7aa80a827f6e9a11ea1ceb780294d90d7c09d161045c5e0c8b379a57c09991688053dd2a4e3bc0a897a94deeec6384d0db58f435c9dda039f2d49d1a1c385596b5fad9a5899e97c0d7bdeead3dd0b95d7fb59bd29afea60fa9251eb3b3ae78f4f213cfe4df70e60b43b278d92f113c6d479bf4a892be0c6e5821ab814780fedacad4a3551ddad18b93e7fa20516f7db51b4c69ee8448927b9a356991fe14bbcb048ff8c414484986473014ecd3828785b442da5be0c4d3d9651f3921de965697190a97da7cec070162756c703bf4df8e45b0baa7daa57a4f7505001dbbfcf5c0d343d61f425ce2b2151e076297cbb9b8c598359667929813bfd7b84f83e4bdeee5704b47877c7b35118f0ce632c33cbb1483fd362fb55ff7707162a0be490053291c631881f46ba2e7c946a3aecfde5bdd52453bc5913f11969f687184907d981b493b6e6acb15072282a0b223981e8ab90ded393f544d9e16977e0d9f21328b86b84c80594b39488e5190b208ba1e90e6e8404274e6dbdf1d27f60f76e8b5c28fcad5a9428b575288251acc038a05280611949f286e4e46031ef2e46965cad3d8911e09e3e3de045da3713d945293d50658a7f3f9cf0280eeacda4b2774d92d5d990cc7747d92313c44278fbb51a9e16a593f146c586e57b3aa16e609dad3f1137ed65c2d2f152a1bda031ed84b236c6f7339bec41f90957ab94f26afa5d3886fd1496df4f89bb01fc2585dc7b75261db56daff1c70fb03a7679ef2d1f635c857cae24fda97b6b0540ad6577e4fa29608e58fb3dd2df437c5aa847bafa4e14f5605e74666c9d86c76503399b1bb0a370faa1403cc20abe8318d379b7253035af388b3e689b0f2ad7aad06df1042bf7a2444ac009b96f524d03d03d5dbe371781009f7a2ed6524b9b956a13861704222afef5afd9cb64da65e56e8825dbfdafb7dfcfb6a2b8e88079b5b7a2c199b21af486da7cc012f88ded8f73097738a457af1b416b3aff652e5c0b2be4cb0a9a064efe9dd229dc52f17b72f41fa4c4944ec14fe2afb1d015d218c2618617ec2acc5d84a9892f1e2207d855921785809f99300999117aa668f9401c6c913ecd1651a98a05fa6555817162ed9a6aae2928519f52183e2216efa772cc97064915e292c1d297a921505ee8cd8a24b8d7407628fd2ec452b5acba8ec45b53d7d18f0bb5d057aa4a827a5644247d4a9e950b6acddb3d0a30f15a4a979f013ac2543ee71bae7a0fa79449f7932f13c5df41dc2cc723c8dbd5ca208f817ea8f814431e929d09bf3d841008da748de511e1deb238f69a9e9cd2c0764fa092eee23d95a24310dfee30ee6adaf5a1b5ed1186290020ac0105fca117b0d36cebe75096f46ced525d64011639b250d34a27c1e7693ee82536d3c5d7fc6e1a54644f445aa3f6a9bb641702fbc4fe49e3f652e56db5ce8aefc4443783cf9f5118dc8919f61aad71bb7f8ebcfddfe35e0f002d80ee851ed0cacb7098d43929c9507c493e6e6d6273ce8d8b2f1b6f8e4dc3ca13c3f97dbde2d8bb0a7eeda1e4951a9a9d2728743ba27a67890bb205695c173015e84aa6dde16d977def4615ed8029a405404344052ca009a44ff871fcfbb36c892180bcfa80c2818419a037bb9691a5332efa9a1df8639059d7303b84393ec74cd0b4b4e23843653e0dbe2f06b3b28363e9088cc5f34dba03556d3cd54113b44175e4a49b192e96a033f24c4766ae2a8b036bd32f489ceaffcdaee5e07983af6a3c020c6161bd7fe1aa5f4e91513ec3af14e1559eb8ac9b1cda30e0b961d28f60dabac073353229736e8642fac70421df337ac3e3f9d8776564c9d0d52062ef0789e860e872ac740d2f8eda8019e106b30fd36aaf13a47c5beee34cf008f4b492040145184983f4014e9a3998dd3d91424952521a977301254aa428e1fae103efd4d0ce1be50f6721a7e97d9b27fa578229a016ecf02b53e255a5831767b0e0f106191fa48a58bd8227364a22e5177b5f4f7172076fb48cefb4c51f06fb41f30713b627a5492937fd847d7d49929592ac4e978c831ccabcd20bcea01b07434f95ca533abfe4919e9d8e106dbe632de814a6142eb67907b0a8b7238602f664199a868dab208c65d8bd49f19905fb97d003688a66b36745247c24bfac58e0173eb889451cf512287d0c5333f60ef2f8dea8d42ddb044db2a261b942252520898fc0806c66710dbb7292de5753420704bf208778b91c14440840acc3a4310854f20c7e6b8e953731e7937522cb60dce6a91125b7cfd26799a957c606bea30de0d4c2d96c07508d3545a2e81d6a5be470dd292af457ffc064715460fc772445fc26be081a31781cd0c002c139fca58b8ba66d1af26e8d3fe34b56f7c5775111364bbdbd892a64c768cb09e090cb718fdf2996332390522ce357e3d2505dfa91fcd985acd3a4f2392d81b06a516816b6b475bed07e6a9806e01bca2022d07b169ba9c6f340c3510ad368272e4eb09d0c411370ce6092ef446e9ebc69a42500b71d6028890b75d49c40870c1ad92a8254860b120cac978fc428958b390fe89aacb3d696c340254d224d7ba45b3d86a21081c98d6cf90a131aab3c2774fe3185d1f7555b83746bcf813e4fbc907391ee8d127d84dbf8415ef66b5e0178dd332db01b944a5126adf5432a1e3253bdb47e668d0493c0b42dd15847d5ce0ac4f8fdc68518bb1595370632daa6446ab1dcd53f5f0297ebc94884bd599595279727337fdac6bd305d605aed6b7e040b9a7650151eee7753ea327d2335cc292ec8e8837c1c417dee5c8f3a72ee870b11f3b10a7ed87397e037ea6e5d59e327becf662a2f7164baff8ded70ecba8a1c7445d515fe257454b06c420224ff172565991b7094272b0aa7c34b432b2833f786a77ba769193c3004dce6af003d4451e0340eb75dc0a2e41e140313f7ccd2881e711a29c06ceb44b9d0e79b02186b7003b78629964a7ad369688de20023471e517f1c1fbc803e8614cd59aea091f743a69c4313ca2ae71e384f5d30a43c821716d565404b30211684c0a5ca15213c38eecac0d4031785a9821e87d10513ad862e3685787c3fc12c814503747008d1080f2efa14716d63dff84b978780f22008279848b8ee40c5d14a42a7b8e9325295c081613270b7fe8c953c751af899c411ece167cdfc4bd389ebcdd53916c2ca1f1d78ab72cbe623574174d3097f31303ce2bbcb701cc5850a37a080ae61a47eac6d014c996a3ee9163ddd29c18cf7db43b8e885e1baea8b7e6a037a6e14d278d251cdaa573d4c02da7065d8521cea192f64e515173044c97d4e0043f28619739fab92c13a904567e752aace93cfe96b6075917417e949064ad072ad2911c041f6a679718ace64330172a40b2df6ba90543cb5805cfd6a0c6a77eb7989958765838d2cf1b8a2febea623979931817401b8d6415694d97c118273f1ac7ba8bbc5fc21f034e0d43913be271b184ac3e12200dea379ee7a3360ea1c8e73cfb63153b181e016c669acdeff2df836ae5bf8f5cf3371a2755a23ea824fbb9c1d869757f2103286eb88d91e574f2e03d994bbb44c81bf27642af8a5f88f0b7ff9707c8527b940879003a10d08afa00f7c8c15931a4a7bce8775fe0360f834aff4cd0b289012064e8273eebe0341abc3c81042ef9aea960d9498f9850b89a933c272cc6c44d33c98166a88fd441b32245b79086400f1837053488020256f5819dc935b240a03e4d25529c56e2ae3b9b247d6135db0bf1f1f9695ca1b71ca0f7b612e33ccb3aa0acd62d5a99ae9307ca736b5fff06330d3f7c123346c9a6ba1d5b695d15f5aff60d6b88236fa1ee6b4e1e30792b9ca0a8411ca91f87a2b5759b3f2c282f2e84b897100ac8f08d8aa5a1a82d371d1dcd8349953157f84dcae9889001bb13d81ea94470a1860a8388a140727a09317f325f7cc1e9c81e541288f52a3c64465f7c555bf7a133a2cfcb9331dd855e19c75f6ae808e80ef1c0dcf9c1da776e0ef57da380639d261781b675c3cc3b868f65caf8c70e29caedd166b697344e3a47ed00ae7a24e4e192ab50f5d8d8f6da94da727993052e9fddd51a9a6085ef81ac5d3bc04b927947328fa60cc3f908c745dec1f02bea907f42d5acce5261d7165e4eb68f72839fbefb26d47a318e358db60c654cfb440bf1244de8992b9731aac776609dafe01c07a1074c761b83af01c0ba1d5cbe2eb37faf5dfe6d3ba9cb4d573e49d6544e07f5cd4ec19a75697bccf174226ef8a587928865e788042e09abd1e386d0d2fb4633fcc43a854aa98be711501f08a6ba3e54aff0178bdf8f1a3129fdd574fe36b2b3a55d8d3a244d5fafaebe446440e373d8a0178a5202a1fe0359541817688a2524df413a585dc8b964af7d8910bf0e007aa1badde2308401d4c32c82e57d66b3b1aff84b04de32be74fc17cf1a49f4ef4c59efe4ae8394faf72d1ef552f672f2f859e625aaf26d21b59534cf433917ac511a6e368b96505162f2a559dd18e64058548f626569cff91b5cf7952e3b4d81b42319e0eadf5183508688056df7b695ccb825657c3c708b79b4418ff7df5ecbaa8338710c310885bebe5d37ca5a1532eb8bf877e43f355679aad4a3999abef602515039c34092df6194b4a081131589f71cb7a9ac76522d38ba7909e8bedf88bc38c4d87fb3b48717c9027d734aa56358e54151e1df1cc87cfc191225cc5a4c0b93dde507282997e149113f0281a0447d4fe0ed1ae093886e9a629755b30cb4697fe5a918eb78ab84b5ddc82c1c7ac94332e533488ab2a1ad8bcc45f8a05d579d72127b2bb5b349cc536e290c8b49aec1a67f0bfda3d43ae19872ae450c6d6eda6262c5f446544716346fedebfb9e9aca293069947d2bd310cfd6801c95f33b828d762bd60571088eb3bb1069d268a3e1063d64c547e594a2713419c0f39537cab85e1ed7a5e74fea6d278f47b73e90e0773eadfe4cbd32aa89ea45495159ad18ac12f614c4285652bd5c6dec66c25c30b21dc9f72b17b9ef431ffb0a58118566aeace9c2fa6a89f989a40598a683bc71a98faa66ff501dab2c0c8b9abe3d1174dc75de5d901dac0df23865a4a6342169cc7d7cba409eb987d9c908d72fa6b2e126d226134369f01fca4b6856758db7ac09f2a648278bfa5dfdfa3ba4b4922983a091545eadff7a81b9a01c78abae06b03948fbadcfa69081402725bc26f5de980c1491c8bd0c6c9084ce798d1805c8616cd21079a134a70ded854eeb3bc5b11dfca14842c6e8bda69de0bdc63f6d722bd571aac31cc0cea7c8cb737bd797edce947e938d99845187cf9bd600a61aa8095019f9aa61846942bb8d57f64301c87e41e145e6b84bf909f7a5b3822b467ff9c17a87878a188c5f19f628661a48033a42633f24064b90dd62c54071928611c92045b87b40563213e515ba96cf8105b014cce7e0c8a742ac289c63a5701d8a973b16581f0f6fa73af96c06f511e4838c7bc94e4b418e86ecb81505eba5d417989917e926398cd5f8732f363e892f4ab5a99e0cf79af41e929284160f444411d5f7d07b0629dce8d485b4432cd828c09f9fc0b9207525f1981092927bf7de38bab2d7282df96d9be4116a94514b7b93571ef52bbc1862787f869a679a7d944d14540d0c3844c4f134413e678dc373d421629c7eabe24249ef9f66c46b9f9b0399093da95d59a288995acdba9c9d46cb3ce998d3f868ec08e095d0459c811b1c1be26cbda0c907b36aa1864acf48d9778090419bf5a775ce22eebcc63448bffaa8d09504fd672a0530fb8b4635b5e538008b6cec593f44f68f05f5ba110efa462b87456f9cd707771c0ca103cd71cec7c03c1cd5ef6ee7ae16c9453bcacaf5948d003568969640494c8bdaea1b05e5336f9da638d647bc2fc8380845a9a24338c479c9ba433caada29419cb3decf24fae48d34760985f4734a271260811c3898562a621fec72a66caedd9b80824b34dc9ae0227c18a0cffa7e1e88bc50a7780451f329d32f712b1473a116db6de6a45d66703ca6590f523f00c0cdb37b8ecb6f4072dadb0f72bac3ae3631519ff1c2c9ce5a2d3b24faddc89976fcd951011ee5013291da11b6ef4c67ee88677b5d88cb14a5b13d305b9f0d8ba836715e5c3f39b0e602efc6db36c8b08e84e86f94e95648aadf750e926d4bdf98a09c9d0904a2cc6e80f8c00b00a26aca6e1106a98f7125cc18d7feb8f9720feca8167b82a902b8e1b959eb68a174681e1a27fb028e2c558870603de96f864f8f3accbd63ab22afff117ff29bdc49b0dc99aa9b4cfcc844ca8b9f2b6bbf849c57fa68666528c6d3c90eece88f3e93e2bb156d103281861880fa189a251682cdbe3bf5524bb1b0a695cd1133a5e1b77bfac809181f4ddd6e7cee59ad2ecf40b700424087df1279816b5d52cbc042da4ec7f4b66186d75245d30ab56cedac41e9283b61d18d3d6ff49476e402e79e3ac7554adfa9db43f3b820ed25bdf5f0ce53697e1f157d89cdddfc4e8f1a76049c84ef100af2decc226f4c609cd1472e4ff0c65e6f90bf6f1010c8e0a345aee44684796c58690722a3f6d198f5902b3e610e36192def1476ddcb1c27ae0a0d152280b269783624f9f8a2cb21259759da6dbecd69c5420dcf47e545a52cc93dc1ab863d472e85c31d445096a44f4411eb362103dd95e3a0b4a981cbca0087db95926bb6a0e336bd087d5f09e32fc809e5cfd1b901dda5806d7ac3abd39a8a9fba18a1bda50375fdb533ed9994fa72aaf85cf848a9180b89263731dbba980d6b231638f87c030eaa61837a4a24be02b642c4d28c3069684ed6d40306162e8c168945139f1815a30c65b55052fcc9367b4db8d7ac036e93fea8d4436a33964e1aec397178ee7c2883b2b8efc02620fdc00b17021acf1de224642b006b5a0a2a71e3afcc2857f9907d64211cb09cf2babd9ea0387bceeab75ab3d0646928d662ec9d5cc54201e1c18951dc1df0b0c864e72e9fb3d2c3dab30a6a7e95d42eba1e9c2e5a9a77229f2c88a6d98abd131d4a5e88162ed8180d0ec4bef26f3b218b499f81b3f5da46a0c1b1502cd05a26ae6dba6611c926b183520d5f1bc7cc855e5734ce7d32ecf030f4d988afee992839df7f2da7cf50204a8a33d025c6aa5434ed5e8bf6a8470b3f054a7db3a8865d7853b636cb05919e5dd03c1f81decbbe9d9ea2835ab1790c9058353ce4ba36652fea75b4fd9b5ec79b28bd6c1b26396fee7003664e568180607c59a603a2252f67d6d6b3fc2c69fa9554a0e2be76f50644c92322cd42c784a6bbd8f3b46690e906766b1570045f7a3d67cb7fc06396e4de09c961bdbc926bd5b7c5f79affabef0bffaf472a4b471c19831177d675162a06d1f1d237451caefa9db4073fd91cec5ab71f63ef357bc5819e5eb4a6e91fd8d3223129d39f9c04e881520e22aad84cb83aaa1e4cfdd4c16d4ed1a35c9dc24c07501b603809929f691a2ee3a5c541987fcff2859782d040324439804bf3874d19ba852cc61b6be03c8d4fc0738d7f5a3f865791ac6dd38f1e0c71e46f3655f35c20e944ae21fb473b23cb122103f0d90b5665eb4bac11023703b7e56e4d9372a31be165fe396e7938004573d51be261d5f3de182fd6ab3373dd8ef46abfe61bfaa7a65f292f641351848efa954632ca5f8f7c18de448dacf50aed763439666d2938a30a50642b234bf0797e22093b44759c8017f35dd56ed26a061392e7527d02ff9cbdfb3c7f34cdaa3c2f3fc25847eba315d35a430c5d2371ee0eef50eca099609bdb5b085d9965963a97ca9c7a189ee054efb977ecdb27ca381fb613bea0e6d87c2ef12c0f20d65f3af196d4531b41b580277dc1ecd57d3e0bf950628533be547c5e0a5ddc05ed17c1709a2accd7c27465d4e527d9bef514027755114ba90a2cfda7616dc121e5a64b9c400ec1ed1d7547e441044bf0ac1bf732835d542f7303a312e29a62c61d2238a71dc997be13a8a76e5424f3b21b7d211381904e7bc3d7a64cd29bf9c29e7daab3bb8ca49e4ab4621b22e9851fb9deeb6f5b547c6608e8ee9ecba63814e951820ceecf7423c6b2024422dc71c4902d8ecb45e62b9e7a8c0c5bb480c59d9d78a6bfac3f4da2bba808a796558923211a70e8a664f6a70844b28c6b657b8686a3a42c17af7731da762c0e56f2282207ee160b2ad375d6f30595ce9fa26517cf9b4707c2fa0f858d0e4334b77528a58ccc7e1c385fb4cffb6a7fdf5e564e56e228aca8242168f95beb505b1261d084c8b6e788d2bcd55a8bd1495f1da525c29bd7c0e22adcbbc4744e81fff21565806924b429e2b37773f8273eb81da159273b59da052cb344a61c601ebc32efde9a649d3aeffd20378e741969d28c8d553108b5da8432c5b5820f31f7d1b2fdbc759f45ba61f578fdfd6eb71bf3c030ae6535850b8e77801df2c44de3fb7875384f54c46406593a7fc41c0e9ae5c985933956e282513b3d7071d00edc402f242f425851d3137a1003e1eeb81885842283c8bd11c80271aec3b4c87885544a180e275c6fdb7750bd116000fc56618442ca2cc1286eb007d8aa5fd71048f143c62c1d84c55f6a6dc997271aa62ff4c5e728650ceaca61a770ea19f4f17df0d9c529e83e9658c0634ea344426f8ded0dd05d5301a2c156c0f8a2a7c8374efde8c2da2a8d36fb0ad11746bb8a9d3010e39e483160184e87656672938b4b82693bd5655b7b1251c631be9bc1c0d47df5e3a924b1ccade614c55d3d504318ebe3d0e487e9c69dc300e4612958d6d4c6822768ae26302db7520ffcf84d308b173cb0b1e33e91347e20ced8000b1c90f8b174f2bc32185124beb7d34ceccdf0e45677b7bb212a76d0f7591f360f4464febd083aea8094aa808f83960d3c0802038c310a8b956bd0c597cb45f9acf95d2f10bf4c608a03c2ab3e8151911c1d6fb9ae067100118c262e1d0af347bf22c02c6ff046c079b3dd573afd46d6f019f676a3d2e08b7196d842b756d2c3883af84db34c07257f2b8ac6f45ef417ddcf025b3c76141cc90016e8b44ca2462d4a8380eb414d442d7fbab83b06006cc90fee79b792510311a8c116007bba3c4d3909016473786480de8ce674c9304e042558c15c9a504d3f1c4222ff12efe48cd9448b1167f78727354c23326e92d240914913814b08a73e1044a51819098810f4202b887f4734bed141a5d854abcc228dc82e0313107c61f991274f9ef812531752723d08ff2b6478ac92b70f2de91b38b95eea75a45c72a065bdf49c1c0ac8d918e4376e9e0115a99d4e08c3a684abe941d30db322260e0d7196572726c23bc0a53c3e1be899ea7972bc0b1dcaf6aac010bf75d31bd4baed41ee2fb9abff451237e2ba97fa5afc31b87eb6515885068a591a6458bc37f56a2411c873fc9adbc12674fcb08b27053cb09b02ad19da6f16131fb5e253d82325e2366311da64bba8ed3f7bf490af5d97c49f88a923ff169fa7a1710cd613a0cba369d3003818f4a0c972b3a7368f5ccd6e3077a4ffe25a3e28ce673b82695ae2aedff9371744d7e3eda0b020520388ba42f7451b303fe2aa0763c3e644889cad0a179b65782e3a304e07ccaa4ab0d66015653866687f951d594a5c8ed401afec1b44f41ee762311d8390ef57d2f71ac9d16869e851012ff03735472354fb0a58b73fd7fd6796416238374c7bbaa102f802963a9b33d25094fe219991c6874b67924a0c5737b16b9d2b1c633625a7ce3d434089219c32ba26079166f07d321f9217b74cb2ef356e340bdc3d009840e6d0b6607072c264f6f777e6e2c9813df80243d227b73a98a3f6a7b17b99db2de6638c5d8f9edc05d01234f6e08b8813b039f22d857c0c87fd73c733472e5ede1bb3b20b25b93af03ee199f3392d2791bce89cd9287954e1f8bf8ff0e6af88bcb4422866421a8247053f66110b52efd0e2af6df484b9adafb99f3bc0c4145363af583815c6b1c6f2b8920cb8701f71463520849d991c4424e7a7e5b426339a3f730db444e672c3671c6e7c768ec42109e82e8aaa7f28c9988e0acf2cb0149fa3c358cd24f43464334f598e8f7752e34cd1da8222fd114b514ae19f98f414400285b9fe4f432a1c00841d0cefccf6ae30699477ce98ff3536404217e7907832945bffdf46fb6fdf30bd4e38fc6b79c32bd4f92cde4f13db6c8ed7b88a97540309030fe560dddb718cfbafa1d2ffca2970617442a325779774da6c1367688d20d689d40cb4218b8e80f1646bfdcab2538990acd09c0536c8dc2dc8dd09e810c100159026c9bccc02878aaa32b5d40f784b414d279d5404c39026fcfb43717c3308d699508ac2dbef864744eb4a7a3ed36b5b584976fbf61b42237a998cae8ca09033b1b051a54140a2f05880b3780a47c8aba63c3f870ff0f8d3c44f25d5710ffb3964b48fd8218d9994ab053415c8092b39b940c563beb27a50d225d09143de1e3259eb4985b0e4262dcd3544144ce48cb87f5264d5ec0ea0762882952cd2187baa8892af7a849ad8a917991615eec52cb3b3e515b6d742662c8493c63fa016b757049443abf779557a696de3740fcb104a791ee169787c2dcf30d52cf468df4be812faf74588d5cbd65bd303a69316b2f0c5822cec20b83a3098662ad9e9d596f4dccfe3f546b329ce47636aecfdd2eb67d6abd9ea6358c5f52392fd77769d7db16b53e4fe976121a22f64f6e8721b3b1abe700ac403868f462bf58defb3e4566d81b5db14e6d58ee382f8538e0f38a7dcaf65c0d070af65b88e31623e70fe73fdd6a9fc9e8de3d14bf6486aa64e00f8434c3e1e5df7162d68eceb4c4dc2aab777af14eb17bbc627d9da684f007bceff7474927e067b24a0dae20f02c5da9a98b31b70e927430d1f5332bcb5228af1fbffba7c26346e49147211f81e1df46c04bae484d70a20352a08aea3fc25fcb01807bd0020e9c5052f2e1751327742f30d00e984126a85ec303cc3012628c781d081e0087a1f1cd5a6812911d9522b210e28551ea01ed3e3e25429b40292c5d3f009ec9b9b6d642e46744e8ad56300bc52c5e2077f14af7505a1132c6c859de4dd6c8186aea3c508a4c06153dafb4af1c9d0dc3de5d3f94c9dc6d7fcbb6dd4d27261088f805f5a480c8ffc150dd228e9b4264b1bf6303be17c7abec669cb9e9a79745ba28070352796bcde752b040e3e49da6997f388ba8b1cad4268d99010a2f7f7eca6029894d26bd8642bf8ec0805c186c1bdb6937ad4947245d92c11028f49a79bd872df38d4e87b33a8d546cafe302ff48daabea377d6c999a5870c24609cfa9bce7c445e650798de2ab6811a4ccc3521e8c35fe8207c49ded541da6ab5e7831d874f6f4c489701ede7a887c5cdd9af46d03b46e7a63548ae9820a1e42a29ad747407fbfacf6243fad7c173bbe427f7f3ed5b1fc1864e5365f5f97b90858a8c565899aa17a472e5b1b7e6bee8a75c2021e46f61a4bf69b60b70cf0122341853ccb4c7f33a4f5aea4381f381ad6ec092d0259a112028b8ea70ea442f8f135fea2d58122ef56669e7804e7d51b8efa1df442405f018d34c884dca1077271c5d8ac62f6aa6e492dabf01eeef185156eae9af1c04093e93b210273a81b98227d118d4e0ac407e62a6beb13fec0f2a76d5b05372fdd7a47bdc5771b3d9898026817779a7ac826bff79a8120b11bf18aa5602f691d46763b5a5836bcaf473b9f21d113492e000404000e35fb2046fa716c2129d64853cf7e366813bafb576c0406fe3fc5abbe4cd0b8ed5c65028f36b806ebcdea3e3c8d2651134049bc124cb68fba0e3b9ceb98f183dcf6b9cc409d2eb87340cdaef0bd93d7666b58e8b4004ba77d6c3d65b108b8dd23ab4b53d08a2ab59b7b236d12b302bd0e5d10a82ff3169930b39b1af4e01a26200a5768a8d6dee31fc3fb3ed2aad216f3066c9bc5bb5fd4e9b5fc0477d2e381df4f10b95fa0f0ebb89e2694327b7f25b698cbc8ba84e3cd9559a0f862c72bb292b6d250e17131ea5534c2a490f78ac04f640342e1a96e894a24b29d884a6e4e0a438b4633b2de414488d0e245a2355311d1b4085a6694ce56a45110774084ffb846c50a3a485c2a9781815ef6601accc13d5a27578908320c8e16b55fdc26e806ae24b2a41c2c76851c4fdc1ac7206fd0f429f7ddb57500bf9f2b424cf72122286c52c1b9692e16af9ff4dd4beeaa50a18b9591edc648bd10dd5cce12647e3cba97f5f3164509fd699c0303468d88bf2c3fe9a394cb373e43934961f9fae2569cdf2d403415e9b3241955c578914f8a28ed2a95854f070d3b83df2700d10e0eda53c91d9200c1013aad2d61e92881431b135994200c4a41ac6b76e7b3b7a39021cce83e16c4c4a6040c12c95068d91de0642233a860371cea4767de3fc6d6f1ec79eb272f59de114781b66c6f5e3659dd3c304563b9d69a142b68185c3c21e53b733915da26f3e45e0fbffddaaf30b4901240fcbc34175ca0351d1744f9d743e17643b7390f183d5ef599ec445cd3052383d7b18055457ae8c2bd387c7059de7f9583ebaf4b329ad3a23823a38739249867f6074c702e8a07b2e746a9ed81cda545bc5adddce863e7f19376fafe9afe434cff688f481ecdd3800ca51b0ae223652ed3725620b1eb00f0dcc4ca21ffd6d8e409029322eb7bc033d6f31ca9d1305e8b95fcc7c33bd13233556ea3e56d5245a4179aa50a8be0d3837f15d4433919a077a5f7453d1abb63821344af4723f6cbaad97a978d89f083467d5ae01ceb298987bfc9394b8c9834c3559ab21b168dc828be04787c2228d4cae6de8fd29e632a27d450828a5b36330f6c5d2663a7011bd47eeeace7a4ff933feb7ddf70139cfdf53bc3584416732956af341113c4a186b5a60c8461a315c188f93f6080b1885fda709a2f6b44ae973407c08b3525b04b70a731125dfd956b30f2e1127f7df4204b38015778ae988cf989dc1c07e8b44e2bfc641c9519750425f289ec2196b847fb2d93d000d699d6aad56ec0baad63e7048b2db71f9b6cfce1fc6025e9fd0523614453b3a37a02182ac26225d35f28021e4b2880fb35c5bb7e235a0c8c042f84569e03adb29fc8f33c85ccfae6ceb8369278f00ece37c7d7fed9d89cac7145382429580e570de4521c7def1c3162a743bff9083f7feed309414d947a7795266d96c03b97f63d4cf196a984fc804e930ac1d2ecc7dbab5ceded9476334626d084cd5c8d215604b3b8e4f93408bf00fbf9a8cb45181c15191344a6a0871680eb7ee3b76f305902bea52730a83648b0fac302c6a1640f1ad1cb25c642529fc8ee915797d39dabc9de5c92b713a169485a6ed00ceb903bb5bcba5820fcd08c30ee7af22ce8fbc659aaaf8aad3a87d47ac36a0e4db07a7c7f35f64577a00f65c4ce97acc42cb4d8188c1383c8e8362830cec08ea7fa648e853422b85150393464f837d39cf19ee20f9d532fd404f1400ecfc84bdec9eff6e19175de862ecc05ff86265f9a1e322cbed87248ccf86cc51b31dd6b0edb9fe0cef206c812a1169575c5363d67d2d91611fd5cdf9fbe6ebfcf73efe9845c6ae40e4e337982167d261f3a9582a963a7402032779cbb8bcff9664bae3d350523415d8c3e91109eef7838071f144a890a46275f26cda9e865239c8aed6ff6be38afa3648865ef01ef6182dd359ad3613429ee420262098e453d43e1fe45e01c28d700c2b4c1291c8e3dd54ca66fa4068fd240687d66192a06c698d74a198b6ce9ca9dfdd4fa5f0af9ef1d6fa921d3f4188b33db8f19a437549a87b3efa3c3c75558c46db7a66a73ec62ed9ef620fdc2553b9b2f4242e43702dbe1786f1c3132c86abdd76121204d1cd075a6614c0de95bb99682efcb7f39212906f01192ed12072cdf305fc9c4ce8f15e7e38a46f73587017d4909621d3dc2d10f8085e1191007cd08c1abc20428a2e090374309fe98a9b4feda76623cf6b2f4679e3718446d228222524b218d2d5eeba134cdc521ff83baebf50460bd3a5c34349aaf8771e7638ebd3b76524588f5346c23773973faa7f4a4c1cc7aa722a1266351e23918177c02bb61aaac192eb58267cd9b50cd69c1f97796f69a0784eb445de962b89ca9d21125d4fb1750686711ab4a38c3c582026c867f791de057ade995e6e8fe4b3924f63b13ec27201f4ff57054c3f0bc1652c4cf385171a9335555f609da3b19fbe7a585daa0218ba6d7db3495cb5cfacd3e0b34c304854615d37ec40cc8dd7a057dbf80eb7358a556095acd7770b1348d0debf6616c152c7bf5957d2b955f07cd81552e1fc684b6bc7127cbca4f61880fdcd1315d7f765f57c61fce337f8303fbcceef5cc992e25682300f35705ee7636e68c20078eaa4b0670a0d55ac340a79ef002e60748d22345cc3080607684d63ad3b3968961cf766484c1cddbc3b0d89be8edc93814d202d8c6195b71bca06eee594710a4a06347bf15ab43fd83b16bef0b0e094592a57acb5fef37fa7f8cd40f91aa376b9255df405d71152654031215310a95a3054fb1374f6c3cd7b3a8cbad95e85b63287ced792451fe248b257e51ed86eaf611dbc5ac0df47ba24be352fd79e35b549f25c06611c20c610115347048d2fec519681a07803134066352c98e4d566d1212dab05955d4d8f890b66b7b2ba6906c03ea22a50f803a40dc200ec0be0d8faddac142159e3a50254319ab43da1d33c83cee3d8729783aaeb90695a33cbf2a66a98ccfab0c12078448c715847be2c665d22ca9a39f8f517f248f9706e63eb7203b8e7a7ec6493fb18472de98131de4d8005084864eece397a636c7d93113c14cfd77fd814e70c519297e296535ea1823fdaa18c42e2922d026d44db2d3c3873006d17ccbc6505d66ef187e7d41836079124027134a46eabc3bc832737530f7b28a77db9771607af62c0818fe9fd43653b188258114d281c24fe5f361f669632c0fb3f5221e6d2149511a07dce43d15682f6ea3a6bcfd0784ab81cf86241ef68fa4ccc53b76fd6d515719b397592aa35c8b97c09aa4ec1a90961975b97a2170f6396af9bf67f20d6429bdeb6a3261dfb18b77467f1d5aa8e5cde2ba3bdc4758602dc0d9b7cccdeb56bc71fd9a07979e20f51c271963ed95e2d32fa14e13ada5e192f8fd2b7fbe2c1b5f88113c92d8e7925078e101056d007b60d9462efd808ed23d2d82f2e8dd3dd393cd069bd94b652a336592c4ba43bdfda3e8896f1c27fa20f9824c6ee5aed85c3e3bafafbde1598f24b484521f87b8eb2061e32641cd1a1c64af39d7a666fc32ba32122ab366e891e8e770c7bb25613518f50c635d4eb26319ef33cb54fb4dd94a3448c3f8349647e20393e4bb6e34696f41ef1052bb39e2db5447ef42559d23bf794564f843a55820f813b7090960c747fb752043e2a48e3c56dc991b0e9b2db6f6995b908579f505c2d6119074de17cc1e4d23521dc139d674edb91753e816fe4c0f82718bcc91ea8a5898632338e38b00da3ba909d9584bc046083d918fea671fe76710e7f76e08f71d652788a006a2e3dabc1cb5b090688d2951c7f67817518ca659b9a368a5d3f0893efe54a8907bba94917e731f0fe5b2751429f126ba560bb7a5910455d275e1b5e59698f69b9669a6c92fdf322fc8e2d388e8453759893c3f32c5368022d74d924865d9e81e198334b2ddc400894b9cba93f958f91191ad4363b7c16fa47b81ca954f0f6d505c5c7b56137cf619d8b5a16c0d0f4dbc74f6a292f49e3057d2a0e9af3f36f31da086f5bf47443e55e710828cfea7dcbe88be77ab30d8e7c6d38b7d7187aeb3dff7be13272a2ed47f081186137b9813386547b7c95d02407c7b9ff6413036d44e508acdd556c898fffdbd8e40e7424e6a478604f9615bf04a2622bb167294e0483170c30879dcf9b38125b020f28320d264fbfd6efd132eea18442548ebc655f5bcbc89cfca8e1b9dc638a29f20177f62b10eac971841841fc084e219352f1c2690d65902f44e75a2576ca3710e2067aaab1dd98f4727dc4bc386e578ee9c6fa4c06111eb0ddd3e65d11363e3c7dd43ebaef07ede73e46b5dd44db77f87ebd337ae1d008116b7c329bf3ca6a688e79056dd30940090e3aeaa27f80d6ccfd8ac4ac049bc278cb14117734faee983a81072836e0a2a71c6105bcb013d6e053538e6d18962c1414623a9ffdb23dd5ec63edd3abf1100c42bc0efe9d3bdc91fd7e6d2b5de332a89e22a47c00ec65bb7934889638bc1e4c19381e9c98285616541bf0303e1d514e4ab0003bff874a2c1c918e1ecbe1c88aac999526c0acc4b3f667ad2ce91aa72695426942a0c6f09a28a8408d81478c4533b9abc3e939034fdaab034b0719af221fb66205b7528aa73d5008b2b7157ab2b874869eed1204297faec5005de9e37a3cbd511caee4a9282b1ad0111a12c9c7b8f2e1f44dd155372f9084d948fbd0e9c3dc88fc300085729b222c065fd04024b32c621531561d8dad2838cd1a328f29431a4f8244636dee1cbb471f8211a523dcb4ca97cd3debb6875c4b8605ae0758e7edcacd680ff541a5012f1c8ccb69d69d18e5f28b872ec842b34f67c64647cfdbb6d9ed391d1600688305d4285d30b0cef744b2f18846f48d525b426e925ecf95698b71bd0a9ab729933ca03a175221a7ab3cbbd68dc465a92575883be1db76a8b3029d55858d086a5abc19e9a4db338ff96eced1827504510630d1fbaac5eecb4366cc2492fac795b733585d9bd6fb5909ec5aa839228f631bbc53d35b34951c3300771b819be7dcbcc0286a3d93aa7d4d188023ad049019b936256e008b3f8b7ebf86f9bca0ec4d6a3b2687135681c61545554f32bbcb51b54cd985623d055721df38d7e61e46ca3192a3e050678ce1b88eb68dbb856ad7b149b2607475c2696743af51d56c827dfd230fd175a3e770ec8e57b628d5a4e4b3fe8bb892ed2701e32ba640a57dd6671d9fc31e5be0a76cfc959d0a055b7c61fc47b9145bc0fea88d3f8d95b9fac22e12adceda4d7f6497484d8fb70cd4d4bf0f8f41827abb7165c70cb10e0e2728a0caef00dc8fb3a1af83429e858ec387fc746fef255915620de4520875cf1024a579a90bfed00a322ceaab332eff435e85a2ab50f1be07332cea5dc6a32154064aa8510899bf565b96f3fd582fc0b5d7c57dfd5a49ad5b22d2bdaa12336d7d6773c46705f602e2659dddd6c6119d60a3fdfc6e937e0ad11e1f48271f40304542249c42771e8703b290f58b2d318163a1a9fe38091f6d15754a2b50264765551362e6338d620b6c2bc70e038e35785be82a27249d0216ffc2c1a390093c4219735a48f42ed79a9c68f554960ae81cc0ebd4ad4871ce0dee9069bff849b985fdb985a4d178e210c43acbe0d785b1668ccb7946ab0d36e716e8063ce85f969847dd883ac6073dd7e7bdc051d846bae9e2e57969b4b6074e26b0a1d86d8981418c994c6eb0f2339f0a2e48dc084ddc6ca8239f4bfd3545908442801a71716f19c7efdae8a2824d453393f772e8f7006d20ad66445fb63809e34851d34719f913fb475f76a27d28be18f4da6dea7faae43309215d2e22bad7808442eb518647d0d6c6e75fdc5a014c01be181f7720080c1e7d8af60d083f659811604152ed8f341de36b889f0d2bae04226badb1e91d808a8774ec2a88758d3db3640e4bd40645268327d614a3975c6a8a7456d77948dc8cde0a7b9d65b44efbee85699f3bc0ffb59bf648e1e4520970948b5aa7d890871c75991e56e3a06e4f10c9800680ff4deb0ac05ccc0576e1a92f78da06af0f212873ac3820afb2116d17dc5284339b5e081175a78391267f728dbde9bbfcd7750deede396a3c51c179ef44892704674b89635915498d76095ae61fe381bb4a14883290052794e320113801a6928ab40430f0980d76a1d940fb5570db7988ae26823ffe6184bbcea2c7d17ab81eae826c21812746acaac05b5fe1dc6fb65482d8ed2234124f697cc3c9180f468ef5ba42e4d5e1b780e3d96628a2621a91273da30596af22a3c2d9614b12879bc7d2289ccaede1a75cb7f0d3bb94d2ab33a50ca7120ca10272123ea1d98aa826434daebc48070bb816041d2f3493b1e8e3246564c09491e153b30606691d0903221e88fd48c5bd3fc610310236253ee3d52daa8e3ee685758cfa400dab9b2f277ac65da7bdfb3c63aa136d289766461e142d902861ec78e2897b5d1087e18da51dc64e00b363dc3b3f32825235be2d1e29059b90de1106c00ce47242f7ca4b0eef871d291530406a3504a1607ca1d5aaa618c6b61938730e26a1c481538aba07e700d0caba273834a9745af9877106d713655c8387355ce4138c61532fcf716e7884f7f5805ae463439518ab4d15b3849955deb318748698e1c83ee2e1502e39687c85e0a24d5685f1a627334cfee48d9c1449cd034e8e1f6f0f16a458637bc44be0e09f8b4b95e7a29bc1262672efd0c09b9a1cc9d0e4816c45b88be40161a7a16f4c93004ecf01a66177b4308b7924011ed8c61128fef33eccca9c8d8ac7d598c93cb1bb6c67e275188587007b1029fa39cf2be836df2609890adf99c1188e1e6a58f94b81ffab85de9497b3f92cb5916dfacdd321bf338fc0a72d8d70134b2e6b1a7e33b16624861a5c56df406047bc34fd2ca07ae0b45b0819190be4db211f551ba3c2422fb1022ffdb11c72a6c2f532147b150f575b905bda05db642ad4013d8793a84a25e014299840017c7205021895f6427936ec85f45646298cf3abc59f920f041ea187c3c5d09a88b8bb66a26c672bb028f8d4d668a2ac38d5eff76528d439aae2698bd0365bb864da9fa868b14596ddc4206e7636b8e0366f25dbe916b2fbb3602034f088442b52c4b6843694454fcdc4591c2665db282eb9683694da522f91d370281344a02996e937d2c807c3a5acda5f5f19dc4d86f799f9857abcb8e587d9956ad37f7797947d522e3d9d93418dfd49a98d8b26c417b91c7e8fd29c1e2b9581eddfb404fc1e205245780e830b2f75dd643286fc5066fb55939a5f86aa72ab6ddd91c52e32f25abd3f8c6df03d96f5ad39a2ecf475a9ff9812cb2db030576f4988334793b4f4cee9f2f82e2777a20a39c1de9b5176840160f2cc39879cbeebcf3dbace26946a234d9d7053a1fee4edb472b250a14788b3fc62d78cc8d416c0d7e0b7f13c3211d8369fbbce0f00a7721b872c20a72bf9e0471909be0714d426d5043c57b81f0991e35093d58539d577869e6b28c3b438d8cd379bf3ffaae8c8637916d8390ddb477d155c7a05ae58c17c8656d9a7c39f01466f8e699b06f9ed62eac662a6e6fb0c4ac6b7f29c1c71e65e26e17565b3aa41a4b738445eb6cf5ef8b04717fa365cc629950e6760384a297fe2df8677f78bfd7321ee53fafb86e3b85522df486ecaabfd23ce18d7c94c8cbda59d3d49d163deefc740531f4201766056d5f55240408a1158405ad3d31bff67b34f4718d059dcfeeafdb37b2284762ed21a43814dcd34fbfc35f7dbb74f19ec57ea8f851886666fd6a9f018d651712ea2098f593bcc9ff901f13c897805a491122c4e5cd55d7e28d5a5ab954c4900ac23c3730b31b98e35bce2c524c57cec4e947448d85072b18c8c57a7bee9a68c1e4d5a83809ed9ae2a73b47484b45135b61f9ba83b69d910fd1379c4118f8b4a94ce03a4a0990dc757781e4e7e7a9d174cc0e2cc0fafcbc0204999c173b980a37e1490c6c5466360cbe746c51336dd399bd1da03f420d67b0ba83e8dc77c6f540457c5882a305067124914f10d626f83d352161db11105cfc859001a2e596506c9626c8291bb310d924178f405c6018b9f0c8aa8a4f30b1c0bfd11bc0805b7085bf7482f2238443312828418b730aa81d56e4670b42d7b30c93727456c4fca52b4be3721434b5da405151bd02d4f438c70d73b21d9f3a57334713d5ebf8e36d842e8b58c7b7f6a9cc351eeec054f8b686aea5d4f500e929376f58a8d5e6a6eb4ecc1450d7de1327c4b8f1f98471a695975443562567320b11f1624be4a3545bf5123a455b949f608f76a045780b21c2672a5384f17cc63600fd152a804830dc107d7d4b4287cf43bbb860b709619fea566ded8ba68232ed79f1697578657469cc92232633fbff3b36b039db454b98a433db506eeac3f1e0eab79413444ed1b1d19dc1c671b4dbbaecc9e07ad1401aed98bc6e8b5251ca109ac5a4652ec1e695f78654eb1e0987c36e39fb6a89276189c84fe2796c4e3865b92958a58469ab2f0f7a60e7eb8b4cb2e845d396d1989a6a95e4140ad652503d201010d23072c3153d0cd91a62642d07c88d379a3cfb8221b10860298e10987916a725b49af02868ec9a2a1a7f8d43307909daf54b11b526b3d0db2138ba4e3b228d6669a9395d692afee750cd904adcc7ac92df4ae13f84fcee38235606a186cb1347071df0e5b424999771751387036aaf69ae046c6b74972e16c1f48f18b1051404c518617e0061e33d6e5c380ee91b17f12b22f08dc9e4bec98331cc108cfd4ebae242edf30c9c04294c75cf8cb4f2c12d8a73ec3fd2ad3f0c24cd61ed8d13c4694686b86b90552c319d861559063d4c2cb2b08148543f120765f831f7d146151b8819b7c7f4c27996bafe9501c2119b521286dda67ce2c080dbd5a5219463480696ddef537ed0fca711ad7c235f2e575c9c05faedd16bf5f49af324097d9757ccb6fd67ab6563d0b2532f73daac5852fa701b01e900ed956b8e7963af790ac16a2b9d00b7aed30c336b76d0b49e52a203e2b5f50175d871456a46bc2ccbe3b18bdeec52425d60d8224c3c7b139143956c406988c8c1a41d88aa403fed3018a66a28c217e6c94e687ce469894657d5a28a1b6e4b99d529f6c97137b0ca78338c9be8d415ce291520aa75bd20da0e5e6115c8b087900312410442aff757f53651455184a9399a9c304ab7e1535c59424e319e9a3fef76b41a079263925ffd827617e942bbaac3510dd57554dccf3fbce9ac00deddbb63da17237769ee1de465b9071ada7b023187a89829311adc0e6a3c71a588eae45aa0c7140dd8bb40c0f247553a72bbfa9672fd9ceff6a523172d6c1ef11405d4a4876c398b44901c9b7c29852218a111c7386aad40a044c45bbac0fb716c69797d29c81444a2ae427a4fd5895a48fe8741b4572de91211a1458d7fe4337461f5a1538bc7004b855e3f88a33b68a3bf73ede3f7687ddae0a64e72a26cfe2c5fd75c59a53f5f9c8df36caaa78533c595d0067379b27dbf8727da106272e25149f768d6c851449c4cdb9e89768b66c82fab473c2fe96c4ca7220b832ed4f690d4012dd6596c744ff25c77c8ade5d02615b612e673c175875cec57b47a821205941f0319f387dd1239209e7fc5635e0cfc30184c53216f8f738cdb69e1e3adde764439305217c90031ee440ee4435ff8aa2ff1c3ba03f6bcc953872f6bc41f90473d0764017a407cd6b837696c8a251b620da2be2ec823911ff3b80fd4200f8e303b40e039ca267140876e14c4d70019a551889a011720a3001606b69e3bc185b68430eafaf857280c3c16bc7376be559bd842fe4afbc95bad331cf9e19e9ef8133d7241e319dc6f0ef37d3b7786d7ec234e73ac72bfc68c64a7d9227de2d0f4fb6e1d0f7debf6bd9aa3e5f0841fde4315730e3f8d150142ff84aa4f604911f452b84294355f9c11ac7fef8bf74c4981ad877d6a27ab40f2447e2a421c6a11c01cd090f87a5dbe30a93b82b6a8295f9706afdc1d9f45aeb1932abaf07ad5707cab39768cf2017112beff542e8400b3cbf943ef169754ab80d83c8d1ae61e0373dde1e5507747ae4fdd5eb74944e2c4871e8644b2a24141e06f7a3b58e5d9233ae1f90e85b49c9eb2c710c0ac1b49e3daa63fc28e94de796310acaedb218fa3bfd70ce7169e51be6acb7b6474869a6c604eb5da8a8d5bcb0d64918143013d07f7c9af294c78075f49cb902bff812acef6e07684e893237ca18fea7df7705d34eb943609d61b956370ea2553d035442f3ce5d466037a3db6715a30543a4a078956d4fb462bf050a7eea4e6ed0644299a3f4e79c0f9b11395d4e368bbc1e31d5509074cda4a1fd00e477ddbe00008f003bdb67bb08296a36a78b1e601c48b0da09dfff65d63c614563db18decbdb7dc52ca94520ac306c006e606ad1460ec78bd58823801d8ad168492060db5df5dd766666f18de8072dded2144db5e5d9d999e794686ebb4671d9fe13e5a217ced334ab375845ac64d8cdb3813d7dc7eb0c66fbb6c7ab5314d73116ecc23d3b18ce7ec9bbb5e9b8951b268fac1c1bfb87e13101c6cfa4f19fffa8b8b38d9a95793bb5e7251b08f1decd4d63e4e577b18da255f83d96ff19bcbfe72581b7bf70bebed751a9a80c08ead75d61a6f71ebd7e9285a9c9963f133ed753afed6dd2bc7d93a8e2fb9d929ff1a0ae7d629fe189a076b4945db2e1c271b26ec7d2b5063fb65af9b723fb4a5dc8fdd1b3ab1e7b93de2cad7ebedb747b6dac1ed6afe88756600271ccf3fb7c78ce3e4f81e616f2af69f0dcd2955b1c736853d0e0070d8d370d8dfa833ddcc51c59fe162c576ac27dfc479ebc9f75ddde8d5a93de92966ccab37b771743df91a47b39e7c1bdc6953f233eef746fe0d1b14b79ec4d693df9fa3c23a59f963409d7be3338d45a1697cda9bf6f5b43fb5174d3f607bdaa3d6ebd73cca64afd35a87d2fed4a8ed47acf1175d134c2ff39353dacba0aa3602e5349b9aaa69a87fef1c33b166dd9bba1c3547fdfe33cea646bec6f967dcf52b31ee3b7d3aeab55a2a676a7983b6369f0f55feb9cd6bce4ccbb22ccbfa1b4253e7d4ba23dcfae24592175fac6c6526435de3014c88ea1aa9a04b9ddda75285a9dc02b36094fc04fd78935e3518607ad87db824681963ec08cc04dd2c5f84a99f03f16f05b32cd6bae63e9ada5f08550fda2c5f04557e7f7265bfdbac177377bbc90068d0eb677b17905def7a2d5e3bb92968d67dfd7ea43d6ea55e75a775f4c6113aa8d0fd1b56a84f41bbbb3fc04bd585187cea9ca9ad630afd7649c9f49c61147096a963238f9876755f1bfd722518696fc128e2e71618449599df817a75ade9e3a5da99098856b79f1a904dd3baafe3633cbc546147a4bddd27ed39ce0836c52f03bd817f3ace327b50e740ed6535d08f470add211ae6c411c61863444167071afff38ae2b391d931b4290517a21853bf0e4431a6c6ae48e41d81dfc8a9f2438f5f093c7230748dbe50e605afb15b0f76ce5d4d17e57a9fa75e4df8031ef854ae6e162f5ebc74a904a89ba50b2c7548f5af8788ac48e92bb5a8b6112150229d5aa5fea93add83d6d74a2a51afbefd9cad8cf121931f69264b443dd49e085e682be91577516698c20f9ba83d9a23a0fb70e6caad1c30a05f0ff51011c7e92122477aa8bd1e1adaaed5dc75670033e8d401c608218470b1ee6d1eb2565ddfb61590da54ffa4fc6bf2441043e9e731eca2a084c4ff3c0e897c13cc105f76280b5c2faf6e0a8a2563373d153d6fae7b7005dfdeb7a3fa6b523a8e1705e9d5561686f4e0a8bab77ab054bfadfa00a87e8bd4832fd461cf33aa070177bd28d773fc3fee05c644e43e589d8986ad9d4e7332fbc41fef1f618cb121840cb32570621784102b022b024e7787fe506a124209259404d8c075e9a04d50fd1976cccc738822b0241e25942c8523ab14906323253ab441830c924142424242100b1b3468427bd3df6e067597e29a904e0b093104afd90403c1d734f61d5e24c1f40257952aca4b017b637a816b8e951c68f31915dbc4046aec0da6651bef4d4f2b947f0b15b58702506590e368429eea9f93d2a05a44ad885b6690b2a0a769456bde0ccac0cefb47a66d0c4506090509f58aa5348e2645280b0a8520956d089381169989b8dabb4b1a7b0d4719a3ec8ba3a19a739c556929a5063736c91a89c8aa484d4a29a1cf2441848fc4b4a0fc104219ef364688237b56126005d435259dd27cb87fa74c8ff4073bf2b8fcdcd185f372870eb71ab11950feb9db691032840e2184701733428b32d3b04ce36e710759a8fc9099564884ce23cf1c8fe00c3b82f2b76ccd0c41861ee185617fc5c726764d4cfe6840b59dfa4c8861186625765a8b65c5e66585f22fb7bfd6e2426badd5c298f00bd3b4c7320dcb34f6d174a83d8b58f3f1544f27b496a77a3ea1b5daeb3903ca9aa66931f06ebe8c966173fed56a31bd92b0d9191514226d0fb8310968455d9fac88689519b5d7ab8c06ebf567448e931d79caa85759527b34d855d6c57a979b7cecf46c9fd5eb37ae87064e83a57d0d93b5655f927a952d75b1abec0c1b36d5af71acc392d02a4f2df58a5bed9db12ba6613d6de326dc43e5d14e9675c0be9f5bde422c16a74f66e303b4ce568cfebc5e7cc93a2ccef6fd6cc3e2bca7a29caeabe3afa7cf64699d29ca364fc105a0728b3baea2b57a06005d68adda2f99b6b02dda2c0a33a424859722a32e48495fc02c11f5ea933e4356ae4ca2794211cd8e496bde539590a35384659b8e138b8a8a8a36bacd297f826a8fb8b3455d3bc382f4ea6320a8c27e2ad38f58bd1deee9d53735606467050eb440e408925ef908696f4e21581fbbadd2d47e58d3de49be2ce17ad75c3e4a3e87921f6bfc8baf54c652b9a8ee37279fe05842e763ffcd396304cd2216d8056673a3a9709b79f89b89fb78aa29c6f8353e7b9e2ab9eb927ec96c2f3713b79bb38d9736dc860e24e2e9b95e5276f03759a58c50a625176d6de49e2077cb826ea78be3fa55fe36e785927917aef7976196e196e7f39c36b27341e53a90f1649ba89b650ca43a8651dd2c4a43f5cbe9e7fdddef2edb83bb54e75761676ffc659aa01fadec35fb0c17058158e17f3ca6ee776b7bb4fc871863944b655e26d20e25f3cb322d23639299d192e6ccde05f8320f3f7b379932f96bda78ea2667be67de34f33a9ecd742b3129a6979acbc8c83c90ad329dcc5f327fcdfc7c9edadcb719a926eee36afa194e869b9cf66bda78eac60dd9aad66145f008f23f9ea02a9f6cc7545a01c50a142bcd539d636fdbc31c07c9f6dbc96b360fac43f9af694e4ff1eee52f7fc8a9fabb940fb3e7f111b50e0564ab7728ed5df0a755abfe340b1952ea8ea972352d58c21df2466a3a0316231c74a96b748327f59b4637b801c712baaf0309baefd7abda63d6763c7a67b8a0278fa66d7a15bbc8abd3deaa989ca1c595fa4d3194b420438c217e78317aa8a6cafc6fc39f039aaf064c153f280ac349186154ffc6418012ab9f93a04f246169f80073be29aa3d278410422884000421741b301366e1aee30a1d5748f7f6eeeddeddf55d1d5734bbb7776ff7155487153aacd061052a05990a43ed2e470d7e2a7c2d72119b103b4ee21178075568ecba365a7569409d3d6876a154e538d81739fc24aceb96a7e0fb54e8dce442fba1379973ce6f888ec7ce7fda830e74830afdfcc77f5073fbe93f9b829f0463f7d138455bb81b638c7187d07428a0fd9cd389e085ee53a1fc5d8e23badf3e686b43d343f7359a23ba3d5afef51025444e4ff1c5a1845c48a8a7784d35503f160a32a4608f65f2b35f96a9fbd9e48277a8146ed4fe1bb55d881d2a85fe08435717fc170a32a4d46ec8dda8ce2d146324d5e688dc405960cb7eb38064ad6905edaf0e25e4c2fe92dd0a59f6b243099172ebfa54df53ceb1d1ce49870ff4c445a75327d5d8918ab6365fa4adeaec63c423248962263ab13dbce352d9dd86ca427bd39108221b6a57a9fd1c7d64ea0d23e8d74b3e4a4b506929b6e4eb74f4711cc926f8f74727220f8bc3556caa884df08e8f7884feee3295ea000720000150a81b469eda42fd3fa83454bfd3ec42bffda254bbc7b4d7deeaa5f67a0cdafdece3a996cfd1c771784e2367c98ebf786ade807ed1888d96b8d0a3c9445b1e34ab6cd31e774308a1c7d8bde7ffbed59d5db53dbaa2846848b40e25a44322f3dbf3b0669e87558412a2bde94d1d1213ff0cd74428a0269a13d01d37f8c6124ac384176796c62d6b4d3003b215d5ffc40c7250fb4ba7b05e6a63d149dc483a82a0df4a8ea3525229dd18a23ca4c277eea3c9a93d69b820b0cc47365539bddafa8349216143c890bbbbdb8994314228a58c114a192194d26594399d6c5116066a019484c6a7a17df0de80d9a5b8f241b2e4eeee5e55799e9ae1817eab44c4483c4133333377cb70d7be850c30c218af784108219c1113c175c56b1eb9a2248184d7d5c15882211a02cc5d1b1422880b0c777777e72852ceb979153046082184982a46186384f1bd1835299bd78bed504ac9338782bc9d1538400409f71c7d09b3c43e1128c618bd6d420124c4995a9c1df93b8ea39ad02f90aafd2a8f7b75736f44907d47b2ceb6a02a3be6a6f8f748ed1790d46ede64c8923cef84cc2b5a2916699332a35e1df5eacb90aa149991935a843b6f6ffc079a1561f12c09e6006384eeee41746ef080422e26939d70ce092184108586105bcf1482daef10064cdb8c34f4896530389cb39b98086abff33c02316c6d00bb8010426f2321bddad6ebdf54ffa63a27880a3beb75ff4764ab10767003baaeffe205a378df917a713a7b63e38828c3500e964d62a105232d25474d3669977a7c7e80767af5fd0a42428e4ed428c8092a5c9ef736435008c6082184d0e77a8da4f2f7e6622094104229a59411854eb9f4f5f8af114c0006d915911e2bbf2481fc283bdf9be89b6ceff7ed8e5cd71d31d48ecdeb50664e1806c608e7a4f474faf7bccdc9c161048dadcb8aebcbc5c50f1c9b1d420817c632e66cdfe1b599bbbbbb994d2944f67599eea0571bb41fd6e4968571cb82314208216416addc1b9461660b2c70d8b874a5d13046082184f06b743426b4e11241b8c718a3ab361319f45355378d41bf2d5241a7fa6c8f6542e3ef1dd523c7eeeeeeeeee6ece696666de6ef99c4d7b6dda1fe6a187a315d64b13294edddd5f4648e48891110821c47efff7e77227eafe9ef2fd72713951549ef28701d5c090151c2788b382e8a07004c5789ef6001ebef8975ef5152b75eb037e8a7ab5462a007a000f5f588c2cc0cc380578a98168dceddec42f069faa64677bb0970aab16b7355120cb5e93d2f40210ae706fa209c8567b08aaaa61137a4706faed918f904ef962b1844335d0388f23a4537e997cb497cadc4e7b1af89d5e7d5448fb6ba04d3fa03b2cace9c7f563abdba12c8084bf1862d9780ccce7aad33a96a88e545335a5290232a68ddb4c3eb60e05642b3c527b834c9b796acf985ec6f43a6ec2be06374d5a0744abddafe34b15881d91f66ad804ae181345daa9578dc950b18a5d4fdb3bb10c5824dde88176bdb6d77eeb50d82340898504014aacedb5bf9a7575df105eaa5947a1a05b3f3ab9257ffdd5452d9e54af6e962cb06821a56e962cc6d49bba59b2705239697b70b1382a22202a5a7e38824b18085c75ce2c59e850a5ff3aeca2ba048852686282aa095acdc9e19c73ced9736a73ce39e705e79450c639e79c73ce13dc66a84062ec793ecf2e0af67cbd86cdd7ae39af39b139e75fdc9caf458e1daf0ef5237a590e45104208218445d06f919068bfc6d835e7d51042082106bb1b7b38dddfb4edb94e5f5ce8661042d845c96636673651f351b3fb11ebd5cd4e851aa2fdbe0252c6ada78a60e0a7cab5a6797bc0307b7bc0407d7bc020b4a91d02c205d1694ff54404fde66c313d518c9228a64914f324c65853e33653c55740a6888601285d40ad56abbe70a4d5f29e56abbe708d695dd15a2509460db8b46af44d15e9225c7b7a6aa0ac5d121200bf86134b6e02e047ae8891597747806fe454618b20f31da95d427bdb5c24c7998b34e7143f270b346ee68e7f799a8de8a8424f4d4677cfee1f26dba33f86cb7d907b76dce346a85b04807ab326ecec088da39add3724a76e15babfddf4692f763ff6f939c8fe80d0bbe550f0fbe3ef9cde9e76bf59e5737bbc2754f90c23b7c78dcf19c00bdd6f30557e1e64b80dbf182a1b800b1a65593b7ab5c3bd51d7f37fe72d1c020e5b7000aafc5b0e0e3ef5db760b0e3d75b7240953bbee96246324f141962010ed5bd9a264484950adbb4589936e3fe0104ac939044a50e1bbc041d0b65ef9c78f4c39cb1035401bd0b01241d8d0107ac5f815e3a31fe64abd51374b1822b4b81068bf696ff8a7d76fc8563dc2c81b15266a7a05ffb4c1268ab0e944fdf6c8a6047eee8a3003511a03a602d42c6403caffb150ed9dbd691d50fe1ded797b23c2163e85c65bc3f716a24d0b4ec48f388e36b35f808dbb6f47ed8af00c27c331064caf69cf2cadf3d958a6f781ac1e5e12b0df3866b549fb6649b969dd2e09d84b6ea262ef840cea95c23ac11d242747a5f2bc7fcca8893a6750af1434983dd6d7134c3234c44811c2fcf05f10767777c7d883b1fe9dd427abb84804fa7df8cf1ae611fbdc530ff41b4253618792bfa8b9ed9ca09c13c36af0ef1c7d27e73dc2a64008e1ac9a9432db6f91875cb96d73dbe68cf1af4b5edbb63d90ad5b8732bd0bdbbbe93bcb7e4e13b7c92cc3e24bee833a7ef1100503470d14b6177f6e175f729ebc2bd65c325ea1ad8d4d9b3a46f2940d3087187a5a42bf6e75eb27686bf7f8c0260da469b3c154b85a6f9c17b45e577ba98dd458b497d4d3ada48fba74cf052474fd3c09baba7a7150c591848e3985a16072907018c953304cabf2a89ba50c1a541da4d3b7d13927a5a793a7da2a44721c57d2a91694d313ea4db2d9dbf55f2355ac833c05ff48d52da2938a7a731619359421833a84cf48bdfa12b19060947a257ff250d151aff84a678588b1f46a21c7d7d1f1a11bae2faeba59cac052b78befbf575b7c9a66ce0066c0d7a2c907c4b2de562f43cb7a7e7d4f5d550e5afcb5f9fc08ca9a7ce0c0a1675a6925fe252d74c25f98c1ac6328dc0587168afd8c154aad9f304b637ad53dabeebaa7bbd55ed73e9a422fe6a0a42f421ca42a21a7bdaf5b5bbd94ec0dcf79f2e43bc7b5bd57cb40915ba5b3e3ce5f420e0ade7e94f1c54583384ed0d3376ccbda1dc62d7b9869d98ceed1dba11721f406d2fccb689263f2b1b5c73870a05bbfeee955f7139a938d07844309dd1e4de2a081ceffe8eede1c9f2c962e262856a058e125cd065d5a9a4b748997e6a4d4231fb1149ac646a78ebeb6a160f2d11d9fda6021c7b9d1754242b4d3e106327577378e2574e3257e1d48f4986ea1fa9735c5f42a66de98977cb4eee955935ec5e731ad8d9a0e8f69efabcccfa555ca74dde2a536994edd15e1ca2cf93afd257e8a04a48f937e6fe2e3684237d30fd09368da70220ebfa756eb28344e1bfbbd0dae8610d86a459b1adb47e77ae8d7f02cda4d31bdfc7e4dc6e4a34d269366fa5dd91a26e59b5ecef9dbf69aa67133bfa6a9713c75ce745811d86fdcc713f404d332d39a4c269386997c98340d9b53c64895af71df8e81bf26c95325c4be390d7b1dcfb08e87280704da2f3b486b1c674809574608fbe7a776136e9bb2767794a23c359620218f20932e1b109e65002714477f2d4ac2d41a75b528e9a9a8165cf2baa416ec0968448da13ebd941caeb874825c5794b8c4f78ad35f971c025eaf13e4c2e1d93101a25e97962562d4eb622df5ba2e892a4272aaaf39b567d32946b180ea04712d3b2851615d2d3b2061a252a591964529a16c09a596457f4ff16bd9093a28f40afee430c7d8bd04299972473ccc7c73fe7baaeb85e8ac1176d2bba8d56a3561ed975377059bdaca40762862ae80ec60c47a5b63a5d1c1260745840d8a0888c140cb0f4b1ec8c9414d99330725058688a2d2811698cc08ed4008307e90320313f9040c19d07085981a6409020ae9a4033fd4cfab2c9918a141e5055138f1c189a12aa40f4a9411c40f8c78a28a175c58fbdb0273690924b182041054f4c4a00c0490b1831e7ca8a2a40a971f3264640a7894e440850b0f34e0200725195081942992c8d8a28c30b8a82181b46c18519e206389209801851820c081173382985225490653a09328a0f3acc0071d68195a02cb0f8600440d8288010e78b0441328392ce9c1088b690ccee252a840073f4fd8c003294e76be68e2cb194b0021a10493d68c4481962024110612473461810a5ac0b30314265e8c41e30c16ec60c324da9a84c13930e1718b0e347c00f38505412a70010c3c2cf1f1010d4b5ca1e5870acac396187a52008327414d082d31c61341d0208b2837100db183134deea04532a1d301582421a60755e420032e3c57c69881192d17e8e0072e00aad70f10f167ca8750ceeb7705f5fa718149eaa06402266830469219c6e0c18acd892a9410ea62082738144d99c3161e22b08841c150153f2861030f900fc6b822085e005103284c7490914b8cdaa85e5664a9615d1713da5e57bc2e9b8a7201bd9eafb803197400041d9610ba1204eb3b553a06184524e1c2832a6e605d5a72a8b2492539a822280b76145a094f696a6c986f80a11fbd9106fdb885c398b9b5e695130e451abcdcd9dd7d4e8c3ba13d3fa1ba77df90b53105edc788a0ffa3792a1b47344713498eaa905bbcb04cc35ed813beddddbde4560563827e39f03d5887ec51859d2a8712db96e4c9b65d4924f1c591a03998a01fad89e2f32cf8cc00e8edb84168dbe15d5b50e82dcde6c4093e5f18fb654d66cc069bde9f41bf1c052c037ce60276013e5e03e6f7e7e86c8feb7b9dc0b88b32f7921cef0dd76d8ea77abbcfb142bf3fd5402a7c84c4401bc0b4cf2ca1fd1f751d32d0136d2f0daab2c11769b18148fecc1654a73d1b5ce849065a230cfaa94a601d9cf5ee47eb99b2df3439a0507e1da741dadb093afd40bf350aa2c2b748272315680d47b59fa606051582cc16d08d1a4d1a1d1492a010580d4f22757facae6927d67411d009bcacd73c86a0fd9fe7eeeeee5e846bbf08623ccff33cafabc0e86a3941abf6977002fff0fad7b8a956515513df21cea4cacc3c933e6ed1242fb3a749eddf68269c2d244d6e116925ad526d230b7b06ada78f68f3a0fd471c477e1047c2c3faf8a7a2988504c51d8a02f3e30b99313ed621413227b5f1847ad746bde5eabac853fdb26b224ff5b26617fa31d222d180e4cfdb15e1225c799fa7a0b06e07dbd3e9af892a515f39f29643cc16106df568a1010ac3201b54d01a5fd06f0e21d202db7842e10f93bd69e8d3f254abd55df4cdd6527b148030684b4e092730b5bfa6dbe9ae075b09ae9be43e2d6840cb0c566a77b722b3d05e73d1e42810142524a24eeda1503bf22bb02cd9c98f22fb8db009452cb420002794eb083970a2c55a16eb3b2d794a8847d1f93689050eec08f11f1885d6132912fddac60fd497e6843eb57f8ea9fd1b4d059ce5469e1a02853f35989b145bb482956b2109fde67c6e2fe0b546e0531a9eefe540bfa9d37d46a08e11742b5c1dc7814cb763e97828689101a87647a94161756c4e358b46ce8e24b8330654e5b527443f8a03a3401a4a77e3f77678d20bb4b4a0406804856aa7a1c4470da5bf5b0ad0931af7997fb91762a8cc0dd45e0fed4dd3f656a063887e50c8e7862126300542b0b5374ba626e8378fb6d0685d58d45e6f0d6f07af2c24e2d1af230afa41a228f4a31dd43a586868885aa9fd3435ac440bcab47beac7423850c8852a14721c78a5b108b143f1202b908174f8500f41a21a876490148a52e250b412876410dc38543b0808e8c9dcae9f09a4f54c81bf3958409999d753e5807ab7cc3b54259ca0d3ed4907179eca4ba355fb775c41bf59db338141174619e8108c6a3f4dcd7c1abeaeebbadc2f7ef93acef204a7085ee8476f1c41a9d049dd6f58a16be28730d020d7926b9310b47dfcc8911ca73ba7ed035bfdd33e686051fb5b493799827ed4c8126124244642e2a4a42f5f588995dac761eb9bb0676ed0068886507cf83c9a47ff24526ab55a397c7737694d9f2b3c7302fa847edc3a9d069084facf30a6a7956cecd33cfa052004a5ed35fd4e03c04108fa71cb5b533466f5f730efee2ef3c68e5bccfb4e695a6704f5b0774560e5162b7d93c3ccada768ccea174108f43bd55e62a08e68d47e06fa52fbf9492ba9fd39b4a04f18c839065aaadf09c8837a1a07fe78aa1f8a15f4f32013fce982cef9d303faf9cff7531a14ff3e04338560a5f66f34fe353c0bbe6a074deb04691efd365040bf398bc6d09e93926fe23de9d4e61cb995efdf8ca3da6fb34342e6de6b1efd346250efbf9ab1642929cbd2d6c3365924b12a3f985e61351eb00351079fdca814ce16453dead4d000000401b3140000201008860362a16830982772d07c14000e85964080521689b324877118849401c0180208010010002203332454840074b3eaf55f79f8344908f898e6b5f21a4a6b8ba401fd5f0ad869b1782acad19d28347a878a55919c1599991a97ff6a658fb383d92a9307ea50f9981eb7004f5df817501d4150c4f619f03f03287974746a084f5ba810078760124acbee000aa8d15102b321773268298810c17d943636b97b14d71d081e17c49fec24a13603c0a0be1cbecfaefc64e76bd398512dec67f9d8e77fca31acebc0aacd703d5b72658adc458ed78ced0d93607ddf9f0de4f287f9a3553a43b519321512124ce574c03ea2f9b8fa91f85bc3c070db3cfb8db9fed8bae4e8508853f3f78d9ff77d103ae360f93d910add081e00107d552af0a05d4a78adcd80768d4b4cf2b7bda031531eb82808e4e3847eda07083e12e7800ed0db8fd8ff0a6c130812f90e24c361cbd0b26bbf061fcf2adcc0f80340ee02b10b1b0250a6a99871ff0c9f40c80361d432ae09237eb2e546699dd1950f6558d1891915268994fce84d08538ae6584e6adb6f3a0336ff38b687e9b2ff2a98e3124d685637c21953ba306b2b78fe2bd42ab4202ddfe15ee6b1d54d40328d4f9e1a0db3bba1b0c27aad9dba73aae2f74e29575d2002609d8e9d1722047ea5b20924be135bee063746f98d31ad1acac43c246d214960966e0a8bb72509dd976c61a51ac03fbc455001726e8f458405de4c72d9fdaa658ef141582838965f1dd3c9a89c9adadc70d1cc9afc7a62ec2f049036f13044b86b4e3ae3477b706d3decfec2f595fee439d3caaf715b49a739944836f31842e1d78c41a5ef5bb469464fc9e25856d31531d3841f594ec7276905feca06a591451a1743c6db07da70df17eee9c4ac5f1ee17797fe1153776a28b7fcd310f79bd82e0e25e1ec7d070068deaf135cb5960820c1dc0b4e7fa0a725162c9a6b9436c1087f111f3837553ee3e9ba2ee3eb61b0117d7b1ad74cc6210400c4d903eadd58ece0aa9151d03b93dd871e8c3a6f05c3a0a2bc7564e8b4f0032b88a84d2d3859753fe514720b10a04ad753c74f8939082acdc6421d3ecd65e2b9ce1c2c54bf18008d205e305ccb1303e22dffe13ed9d327573d3ce400497a0496b93a1e22b5b2c65bf85dbe72133f4787617d2772ee3713125db719ade04c229f21b9818ffd2c2253f49ed0070359c82c1ee863df3a050df0e98f3d232d7a54c27746f064b57ec8456f82defbcc2478e50c58afcdc3889921f03e6cb172cad9097de0dadd6d8449b40020e4ad67dc2314476777e6b71cb44293da9358fe83f54c5be4de6fce468d7d757b54b74fec894ee5009dde3cfb482433642566f570234285ed4e49c66ff75386d7701a7701723bc8f9cc22406e4bfb14e9835e9371633a3e0fbca9c443b15cb28bf55b8e41b31b26d72a0748c4d5b2b592f9ef427a38310a15ffc25a85b88614b16fb427aba0033dfbac55831d7ad27810393fcbc0252d1f2963ba7843fa52233d7169cae7fc927760bdf9fe74c9472ff29cb9b4157452a748c46683f7189b8ffe4ca3e4c60789af83c5d65f097083d375837a4c955432858c65f53c1759024d5b39603e85e53c523a9a4a6e679db88cdb427b974fe00cd351be78b2e987952ce6ad5df68d5e842db4c37eab9faedd2772044ad44458bb0d3686baa0dca335cae090cef750d1ef19d107c9ee00ad0ee5359f59cc5ab663d3535ee9842659873b9ba7f785d0b0b4c47d73b072ac6ee3f87cc18886a886f6e0bb38aa4862f45aeb8f02a1f661d60302d41949f73d5836fe47118d69b450513189f793636223f3f4d1aa0944af6e9bedf5dbdff7bb3c2d524c866132b66dd7f0e912798aa491014bae77a1ff52ac24b96803f05a850ba0f3187a53399b4a3ed290f1521952ad7c6db0b9d1189961a9bece974afa9280559b23b420d8fea139ca28700c44ed7a1720ddcb5429ca2b0754d1a3397000a66fa98f25990f9553c0ee73abbba4f7c0860398791f2ee53e68cb300ba97946e57b36029f4e1182cc946469fe39ba1999daa0cce99df52e418dd0d8568574aa45fc24611748d1b625e43764e27b311b002baf3ff61401bdf20b59486183cfc834e78878154a91889f029d60a6a3b0f521a041b76ab0a69adbabd03af53ebcb7b1227de71b723805db7a0c5d4ce89e19a31abc5dc6f8e7d4d36d5c8b462cda47a40b0935d5ade54be37969c9a5ea568885f5f43900721afe06c922208d74c86bb00f832f70b851ca06e7ddc9ea5ac27b2fd041c39133442bcf24dcb75bd5f2e7eb8d501604adc50c0970f5ec2fb9b1f78ff71de2c401d32b52013b993f93f2b6ae54aa71a21aee10fb1d2e1afdcef48a070a06c253194f234c7d74c63235a3a2875e1416923def2ede643a57189295af7e481cdfebe99f4d88d7b0b775aa5529e2e6d3246343ed786830426c2d4832e240dddb124e663045285f32c72d35096fe17be2669f3e8bb7ea97dad4521a268d5a0114dcdc07dbee4c7f4addea6bc278e0ddb2db6ed465ac3c53e950a0d4f919e4d041f73234797a4b782ccbe63c514fc5a1982dc61b7a52a8f3117d64f8e8db9f0623077c366ae82ea5610f9febab3351e9ac524f3ef0d9f62b9a2a67ff1be8f72a3b4b8fc7c50060a8df30ca938769813d68d85c4a4a083ed100113a31efddd84c4b4b82b901e39f6878f4a8b6cdb66c0196c91bac6a4096cb1c3ce5c55f2c1d6e8522201faac798057e0a8497847ded842861381b37527b73d04ef5e630c7b976f1e52a5d90c2a884d0648fd2093c2867e9ed7459d83850c7cb6b62ecadae28e6b6d5ed08062fb2a4cf2b5cf1396c0aea886be9ed9d57499088a9935a54b55bfcb4f0d53897f1910cd6fd4926a514d661232e8b243cf5b512b02187d38cddf55c88bb7aaef938d0ebe24f7e1f92e8a63589234163b4e0ffc4e858cf36bc9d0f1028b54f77c6e717b70c00c0ec2f033c194171af41c60c819947720ab1216bcf975555d70e6d88015d0ab5873edfd0683eef34d5880d2b269d6784779aae570927f15b34ed78733532276ff902bbe5564761a1ae815ac2412a95dfac66a8f67eb0e3ac4c3470591969166da561f24666c957e520bd2dcc5a5a9aba6900c7bdd1a6120601cf946244830d42276e19235bcd8d7461695c4f1d23731474033ae7b3c5d966c9cf9991cea09bc198aae7dbb61e6dabf42d50d0ce69af863c14c82b8a4c82b5afdacee1a385b03ab0143114ee24ae4d21a9da253269d75fe98d187ae2f27261c097bd45c0195d152effdf68ac3c0b86b6b776623e5cbaacebae522a81bd945f7f3e8c0c40ae150047c901a31555a5107b1719b1c650fac0c2c807e4a6af2e1d536be919d9c865ddca3cf9ffe206bcee143e7ceca6b349a4d6c610d810f17785d807a58858e6a845453c2964dfd664914932a6be4c8bcab605d9130b46f558964151853cb04847f871a8db0d756476a3630ec9a54ae6b97b0f9a0b690d6ee6e79e8354039a28313dda650f93a88493c83551ce45f63cf13df8b43669df28dfc5b03887536c23c674788d36f8872fcce2656e41dab640385c18f36ebe0c7bd4aa0616905099ce09c059fb2a6df8609438f8daa4c0f2641c56b4219ac61eb822a9030a87685468828947753c4719a9595d1823829e192415deb234c3e802704bcea2150d0af9622a0c4b746b8cb5367ee96b85d31096e82b554b0b48fa6537985489986398d91cf99553e8322ba429f8ac12950613645e167f5d876194122e643fc79e167e2193212eed8a30732eb642e139639262450ecf873dbace826eac1cf4641460d26afca76f9a069513b3e50fe6db2df6c5448256808eaa4501fa0d46991b2704cb120059dc33977f3abddff1175d2892a6523a24308200012579c474ccc80386e428026d08bf033094e9f2e587b0a360fc3028da84a42685f28b352748c8d1949b58064820facd07917a4c36b42f18fbdd5235205462425584e3f809d8550e701a061f909a52a7010aaebdf4f72edda6864f4ed21c8a533d93dfa6c79986a4172003741fd4ac406b07851b64d7e4bec1a73557dfe733e211ee189f440c0b362a14a6140abe6c19cee2e1b0ae77bc3b0996d76543c7ba6938185f052faf49eb5201cb615323e679d82f67f7ed72e5bcecf92545e1ad624475a6b909e82148f38622d3bde0c7c40ad9385820c7575d680313e88e1e5f1b0f343440bbb448b043e26ae84e40d373ca3d7fd854abf354f4dacd2d86e06a624ad0c7bd670e2c6e32fecdfa1a68ec5704e40477a21ff14793ac61550e20561293fc035f4c0b8da77c2f5306bd2c2788d45e95ed2eeadc1cbb01e980a7313f0492256e4cb27d9bbffb68ec05e6e7a92af95fa12139913c050216fa4a64be3c4d6ba916f10844e26b1eaa8be49d69a3114f1f2cabd653b4bd37a9538afb3e477fe693e5b3824b5072105db5873124756e988c90ccab8653e355df82169f0da9770476d497c865fe8ce7de9159e22432dc360c8394ce536d414c8f605813d5441ac75f21967142506d81692e192774b45c34838c8581923706aabae75dc2ba04be24b28ee0252251ed839cf0ae6dcf09b425f137544530f849f14cbf17cfbbeeea098d4715a7413aa6f595f20f6ae8ee4ba555697c16cdbf7dde8a0d79779602b9dc5dfbd2bfb04431bce06c6e36804575eb8382ca95008e74f87f5b19f9559741b603e4c5d7dc2ded58bc1dde7c3b29febe629725846b6ecc94a8ea1645b3d07f45f059974cc847b8512e6f7cb15133deda37528c7a92d33cf16fdb3e7b66d1064dd652ffec930b5d5438f4968b1390c537dafdcfdc1a403f93edba70a65b287297dc52fa6589fb302aef7de4bd187bda1359f640a7bef61086452fae181dcf3edbda7e953dcc08cec6e86505bd3fd939b82adff4c9c5f34299282881b43ea1ab7da69ec327ef30fec41d6019fce8f5e4412a8c950af9c38edcf3326d5cc41dfc669e452a6e3e2a51b92a2eb2dfe7d1f780a3e5a4a618686a79221db27611c919d70617728397fa206bc312a27d4a4603082d1cc05076b6f09a98b861540066323bae4d6e29b04fb0169b099e151a0943982849218c490cf234c63e511fd39195d32a7a21cdcb25054e036d00c38ceb893910e754081885a843c1439c4e5f2d07802121abc2a377a0962e08a75c06becea51ead86d9b7ba76846532772508c66ef85f903f43d21adef2914d82c268fa0902c8329c41935d7a17a2b5a7c99a979d0df8939f1669a99f54600b1fa9501fe73a6610103445d79fcf7cdb0f60d60020723f0b2f17ef1f3994aaf34abb6478e88f814ee973470a3494cdac9eeba0aca1adfafe7f652ca7feefb50c02bddc8362521ff266039007ca25429b21f596ae613c58ff9e7763a523727b9ff2571fb3ca8d4f071738c11822b6ec04b088dd5b19dc64445d18ea46fc20034974c61e9a6da3ee2c12b4ed124bf67d6220486467b5381062cb2c64853949791358014fbbfc29b188dd69afd82f1a09c8ba1a53c09b564fa93977d338e759bfc5146cf0d9b05e3d499b820a7ed1392f3b0323622fcaa8f0b03a405c95117342d8f5cf76d4a8e2d8038fda37f93917541b296a80810cce4db67204e492385b5a57a7175f138c2c75b74b3b3d9a855a5b8d92b059209536819677b6ad0cd8c89ea5b93d3c9cf98dc79f3004b116e76756e9b05789446f13a71e4601a8900302a8eed0c62b14926c37aaf5b216a1315ae278f74a10c838946d1c11f234d2fa7414ee60316c2345a9c8a78ecaafc56afe531d8578994bb8edcfca12abd206be20b495db4f437c811113f13c80f5ac240024e2ba868f3150396c1d44e1f1fd3c3c5dbf144177000cdcd0d4bff08a9774b49169dc9674780f3a62ac7de8197239881116dc04868a4666dd472a2740cbc36f8fbf55115cb8cd0692b1321301b1eee44db76f2b3cc974d386c26cd232171695760bb43264355a8d004eb66fc664678645f8809568f2feb6e5ad5b9fc21d084087fbc60faf48a385c4ad0f27daac811343e39ea97746a053fd5d57a2c3ffcb6a474ef236e3c863f9b8d605d46697ddc62ed75a1cdc83860b283370979d107d361391e3604cab339e7b2df02e5cc9a4358718d43455417770f4a182a54eef1851ab7d59404afeacbfc91e836859b8c23f46d1decee4f754e66ae17276408203005b7d622cd80b74d4613f2e37aeebba49913c6457757be34d1565d37c4070923c64915a0ba57825740d12c1eb6cc5f608bd56ae9213869d129943db076a534809b91c3d45e159aa6accb35f71f3209f4351cd9a54f5be6919c7d22c2d0f4c9d71c20971f953418368d89211a93f7d9816906cdc27038c74f183f98c579898859c8635fe164bef8fc47ac0e3d8885244907cb569b765679bcf7e848bd34e56575e82b128d6110f3b0e5c5bf2692fde75f90b1eedc26a0c2e133721a07b5467235ad0d77653989541a8581460ac49658c235da89a8266b367b67808f3766a331e81769f5691c7ea547b5ac2d2ab3adbe18bebebe72eeeb7d324d3b8937df52b3d3a48ca7730c950c537c560e88f6e18a14aba0d2d80170b78311fda89eccc89c63ac25ccba84171ab487fce53bb54442c470d2665c2455d0073662d5b697f7ba6e51f0a3c50fd23bf890bb6e62d0b3f3809ec22683c7ba0cf4efddd889dfc0ea72378697d4659ad895be54eae678f8850ffbd901090103262b5e0d79a682bc63caa000fd92bab991304e95485ba146051f3a2d4ede463c549aa6af86c6b2ce7c522b73880a09c13fd249cdb8b517ab1a513965c6b3f42afa9262a978f4a803f1635f78785c66657a4665576a853155a7f14a99b63066683218ad7b78ee599dd0c20bcc52f205f141dcba05171ab4e9e0efd20fcf93ed9a2f5b215564c4a9764c649398e58d00baedd8b0226548514d469897dfc51851fba5ac8c9a0ab7491688a36cd811428be3115973edad86916f95d736abb7abf854ddab2663e405c3e1e7e324235c0206bf34ba7e2a4d1ba997a9f4587d96d16f1da9c0d5544f730f01381e820590fb0ea6680673291794a3c5eaa6360b6ba728aeb1bcbe9e27de5245b388ce7dee738702b766469c672bff5d68e5a19f9dec85e219ef46ce0660820d4dba26f41ae47e5b01a98fb57c2eb8d98770dd55460673575b51e7de59cda480386d6c653fc918932ec16fbd6a6cf228607eb058a11fb63da07113952d5c377a82e292b072e7b307e61a2cca58cd28684421f1a79d971d803e812dd79ed2702c582e4212c28361a43f6e5f43f1194204211bf4a1a22264ee082b551d8f55cfd7120e54e7e437ba0730f13246ae724b1117d92348b47fca9e1c0525a5ff664f2e12400ca33824540f3f1c7bcbcc03a0f611d0d48c6dc3f33226139b85dd4599bcbda4d9e6710a8d92abf012c0d0d3fc32bc876bf30f8cdd6e27bcb4b4d4a133ed2077db6c8eee0f750e7948a4473e0f39acdcbe0f6e2ec56badb00f838cd5c22025929bca9858252ed30be34e650530a36d84309bf0bf3c019ac1664173f7aa9040c61d297e20a00bb9c9ba59fb24b2a1e1f26d22cdb046a94d2fd9dde492be25eabc480e41d89d6d18519d340c0f0282e66a30d933d13d20e6bf107ede504ced7ba7b47cb34cfe4922663fa5e4011f70ca8209f9292829941e0cb18c7f710478d6ebc5871f829c47188feb175a1a3c75b34e61e7f913c322b8f38d7cb6966e5d783b90f00711e2ee2c3b67ccc272c83d10d38efc774d4e4b24c7b49a7c2ab092122564576e1156c93b01aa75a4948a7578d53ebccf3ac4833148005485937c174c58dec89f3ffededf39b80186d1a4f099e66b67db7d2d77f0e584c404baac8c4952c7a7bf5f5df53bb2c0941218d306154211fc5c126d1ec538217e38d5735168104099269b538cadc054a0efc2da6f7878a1d6373e29003ae521440153e1d8ea8623b464785131d2d7e9dd221f05977d0449360e0050fb7d6528abd7c5a3d8edc73380cb81e5540871230ae81f292376ce898f4e3105628d5333caa40d4f0097cf48710c212bd233b32eeba2ec7ecd8b67269b345880e2e7993dc21b7eecd5d27131eef7d53451c22d6235f2784b449c4d09c2c63316adc88962122830cea347efe2251110404fe7be913df18b2fd7bb7438b7f1a4bc84d880a27a329a9849344c4dedb476515ceb88bcf3dad349c3e43bc71f18ef1199227ad8a2c90c963fb0568bb034fc6acecc5d3c5b600016825ab1da8786246cc2d755a551efc63b266510465b2eb7573d113a5536808b1bb0ce5c824c7e82450fc5a64fd58632e1230bb78ac1958be27011a4f0d33c84880c44c3890ee50f1a014adcc029ea42a4b4a46269fc92d03f8fa6b6aac9da9a0e6db032b8a024384c4648ecc42064b83508b843b565717dda2250a31a053387f3499f8c83f0eb624bba1622693e7161ff4058e305c524fcf31059481b83a1db195ce52fc85f95ac4679524063015173efed4275190017bbcbbd58b9b33d355aec1a910edcf26b818e2887361af8762bd1720f84dd68f16b296783f0b29d73a73b4e1646837d356f0aaa9e71feab0b5d25adfa5fdf268a8a24c840b775a5a40660c8602acce7a528851eef7e3b843e093c3570df1ed2c71604026b999480cc9ca9a7625e8a2d6829fb6e9bd3f54502607fe68eeadc4626ec9bac49ed721dfbd703c8256193df1a12708e196d71655286fe53b96ca4fac291faa0c5c72c24acb0b7c6d65b2b36a2130ae14d93d35bbfa20b297991196e9b711afb2035978a8e82493cc5970739ed48e84c4a1e2986554fc5479bd8b35f50b106d8d2d01845aa886e79244550a1ccb2a05929a0a47ec148e2c8ad10feb809f92cf222c2fa702c2a2b46fffa6396d8650a4b75c761e128c967805da50481c2805ca53a0861948e18e3aab2d6110bde7abeb4feaeb8c5376148cd4d40420ddac1acfb2263d613c788dd5902e07be28789036b280313d0aeb057a129b9fc1befc8a2d9bf71142c2bf798ff3961cf7eb47eb64c133ed8e62435ac3cf8336cfe9728206df3e6196843747f01b41937c230cdeed584e2b4dbe8de8e100b53276cb42e728c2a210b0fe82ef107ff80bb7777489bd7565689b691b3da862202e4a6e451bb4dd9e1e536fc2b8de09e8c67e5df36da74d88469daedd33ea12c256153bc013107a2c2e5584dc05b50d158536494a4354bf1faa6119d78ea0bb4d0383d5b1084ab9409c42b3e73ce706f4e92611f50c7b9579a3fdc1baa3f463aee158c82b3f8d9587c9fc2e722b078248ef4f194d6197aa7bec2a86aeb51c9f1347f777de88a5b00b5ef550c4cb8d52675db4c0445e939a764d805b3df8860611426e945e5b534ab474909bb14667a17d8559fa2d1ef7f2788491e12f8b03c1f27768ae3069c3ba4c50895e2eeb176e92c6bce8d41e39de883cb4e372a86c7b28279a3a1b75f1f5f3c33fd73167b042e94f88187c5868b65eb0399cac97714e90b7edae8b25b22f013c9e006773f530eb2d655c178eca77961f6da47bdc2be90e2dba79b183fe81ab77f8d66251ec013eefa63acf0b6d981e9955d60f9f7ead688dbe8c630439a971a4179d9b83fe26456be851192a70309b57a9debf8b57f02ff118ab64afd32b40dee99516bb7bb0797d5d2b2f2c478c03d9c56418ecf896f6dd4ff4f33354b97992a0e3f43fd841db2bac05b3577dc00207c6187d404d2ad2bcd213bbb242e8272d30635e71be636334af3c08c8562d9de0fc3fb3b46a8127109bbb02f2f69f04775847e33c0b4e182276b459b9a4daaa112cb19ead2b4b3f97149cee7f90693dd15ca33d2a946ac30ab65f294488486e34d8c375dd0a3782d9157e7ae75dff89dfa7c3d53c98a07d63f5cabe2d8b104058bb3cd8b5b955f61dc9ae34e39618702b360060370db83f93e27b17064dea8a80f2ac6d54d8f413cdcafdace1c19f95fb9cce8c944fc181820c3c7ce8d1b98bfc78241d770705853e1363879d48f2a34c161e98a92d870b0ab2dfc167d8303a176fa4264765a1201593831496abc35415948252286812c3eda520c04749f41d767455282aa74b72d66cb9f419b8456bebf7703c8183b6b5be562c5a9c87bfe2930a8928f14f4a4388fa6530aef03db67b5859db64df61e73948353d022895e92093dff0b011ee2ed1f7091bf062848449b9a7038d7a8e87430d649dd33e92c83f7abfcd07eb171e4a1836aaefbd67adce3c94bf00e190b30309b9cd325dc1fb147096988aa803958710780fac2d3d9598584b310c6e42896ad15d95c3a14d2312c847bd7e85a337ad637c386f9dee5ecbcc774243af02eb6054ad55f0a5acfb7f762a921b916711a26a2bfc88af3b5d9e4e19a1cdde2e6e81dbc72f77d40d646c32e408371b6f4228fb874c013a42ec5fe96a9e9a7a151a7334e309d71004341f2bceb0ced64c9bf08436371dad658c9aa3b9495e28e43ec41a4c05fa548124e9c203a612175b43d16451285122710df3085252ab9d2ac2ced82ca35d3fd24399711be9137b44829cc50498a223d771ebb8e1c925ce6954d29adc935216cf352a33fbe6033581b9909ba8832f34d425e940e30a5a04e2661f550cdf8fa3db5276dcb46b727aeda7884c5fad9a9cda616205910eae4cfa7c2d515ecbb7503d2e55bd7e24773edf146d88c4038ddf6a897b04e8ce2195aa3dba924c2b169986e4ed908153d585058198eb786c8603c65621aba1b2be5984b0bf28d32b830d425571b05902fcbd3f19a583f88c41e2473844107b7a244a3293948268c7ac24b2c11c0f29c9a8ca5840ff6719d136ad6964aab4b569ea6abd4219a9402d99bad5d8773d44d5ed90e220474baa34da94bc43644275ffc900b1c8d69b2f4e9832a67ab97a2152f0ff68ea82c01756d2400d047ebb825636203e0b4d4b6e5c27f36a33d6a1fe20a1792357a1378cf11d51f3b93e67694e996545047199fea5989dc9165b1da8e92db0e71931ef99e90632ce49ff87080720b876a821a77045718165769048e8b0cd7fdd2a0c94fb530430e6ae97c11f3231ba8663f2a36d4d8fa021b019dbf3d5f1c5405b4e29b87f1380fb84020f1180516c4a92882f8334ab7e6dd0390be233ab129f0955d5886965a5d2ac3b0015f91f11ea556f4faf921896bf38c03ac510393ae4182e76ea8b25bc9aab1cdd34b89ff76f83e81ae0a58f706ddb438594fd37c2ef980de78823fa685b024aa2b439a0ecf056f764d2920b8408a9d7a442d41381da43cbccef6ef1b4b76857cf7a31cb9fa4e0f07defd9fb78e7df94282d4a4c0454bd24382b374991c65ec8efdb08e854c499273d1ecf1b02af76d74dfef554b2637a2fda2db62cc315560d43420ba7bbce8be55aa57b19c5eee3dc7271ce83033b25639d29a98e795bdc47f4898759b742a144b15da2cf22ff86d61d21772bf41626f5c06d973fd153dd1d9fa3e70b4cf4e9c78de7e896212afdeca17cdaa4c3dc3fbe0b2c196e80c7835c45c6d696c9158ced4f5f2738873864b619f164ff3dfd07f27cc40203488b866a67f126059089f43c63521b477162809d0a6efeb73ec3ff2bfb4dbc23a3bb3872191509d0fbaf40952a4ff8f2bfeaf629e2f889e7744f4e89125a37e378d581fe4aba2d2728961a7666f216d7c18be1f9b486672ad0417b999f56738d9adbc10b3067e8031a92b5f137bb5da3a5f5f88ca409524b787619ed810217405f5f12e37358c818c19db2e70b995bf0091cc21c3605e69b116b27af9a9f829c4b6644f8234e1ad9905bfae74e24e2ee86910aa7ca32e467337d845655908c092e3bce9cf37966a03c1f1f31bea6d51d3cc117648a2cb96691ac51b2ed3efc367774346e52478a14f1ec414752bdb1d490949d1b06f1a4118085f35d3fbbfde0cc3685791d534fb9660151c71e3ccdf4aa57bd28ce66e4dd2b4e04f922a00b7d3605ec4612007824dc923819400312673764a9f1a43adbf5ad4890417b9924120450a3049906eb3c93704b3aed5294aecedbab86e69172ac1b3d0ebbf976a7e50f80c0b8964214c6596137fc688c217023c18868444221509a95b88ec4c6c9ca12afe6ef513f4b1cb171bac93864936b923cd6d04182dd2b4eff16121996434052c4a671aa21da057c7320c94b3f65b8a786883de2857d9efde26f64f6f9de17ac30a9f6e82916a403650fbb30b8ac5262cd0c97eeb86eb9a251a2ec72a0cc65b5375f3a0fc940199a5ab68cd445660895a6b765c3ce3e4e04b24617c48c98cf0699812188077d698906b91f54e41dd38553ff92a56ebf84890ae648dd0f02169fdfd124814f0873950277d4d2e4e80908f1d0f5c2da1a28789ebb4d1ded092220cf3a736c52e2a78e43041f5f16457a7e016bbd74724befc3b360a620e8025f594742479a197ef99391af30f826fa17a9e30af5c7339f9c1d4e6d159800e20a81555207e59f1829479bc20a96d64dcfc94794778d47e8058a92c8a7736bbaab2bc30860a2618a237b0f9e7e57339e769eca90d1fbed657647c281cefc1a1bdc5f70c071da63b42f387b76343cef6d3ece6e9ac65785edbe526f052023da7f3abf1f3d979ffc1f0bd810a442222dcfd44a696f8bd483a6a3de93e39f2332c08e861cef40caf9a33f937acb2d808f4f43c21b42492d1757f83cef33bdcc7b7752b7b3eb18b20ee77c3b1ffc1729b6848aea757477caa0535674516d62f1afa9472ff367c09a9387b1d65b8be3164b92a8137d0839a450b96c731db0cb59d2b67a9f3859bc85320f41f538c4901d8196dc6ed50565b9f5b4e1e94c8e6502e056b93452f7795b526277b88aff7b46a0d147adfd9d236d2b55beba3449783f2a90e16fc2f9dcd5b93ac5aedd1a292d1c486f5c0ff5c42109478611675f47d3707bc7df2dcc70e37c9a11684df8053e2fbc516309f2b0ee5e806e2f84d3d68098f3b397f51298d3e2dcd63cb6233d413cd25ed7ea7f7b15dd491ef36f6557b3ff0b76235cff0afd7d0285c0cafac5cc5ed0af3b558e364c37fdbae2508c0128faa096a38e45c3b417a61ca70d0a6593bbc67ac130f258174a85a058f6b214701341dae123ebe4fe8cf3f160b67e3a378c843e1945ffd31eb288e4473d344205db4ccfe250e8c0574633fbe612daa9c84d4ea2e7f13bc08fa27b1e80c8c1d38c81a8006c2a162018dd63171ce71031366d5004e9f4902e9d7f7de610312d19239d16b848597eacea80b01d753fa8f544565b40592023513177bb61b8772a6c7b392b2979b73a3f5d502919e6cb60e705e784ec000b76c6ab047b91277dde7d5bcf82ed59e4a9b2ee62f2b2be157ae3a1a72d8489079a07fb51a7999b8b72e44d93dc9e66c1e29804cdd11e746e02aa784c5b1388ba81660cd86c5cb1658311360d62bf2f1d7b7c0cfd49c6823fc837e5d00310c9e032f880e59dc3214117329c8c659887eea59320adf3f9d6056f1d3e75a40ea54d321be4e76f54b8f4925096b683adc42fe02ae296dd498e8029a9a0932a867b1c1f2f59248ebc03e877f85fe460d815a3afde56a2b08f6002393282e87720398ca304d992a47bcc022cd88cc0cd0e78a8f599550a6ef8f5b83f2ad1c65333a53b962782b91cfa82b2da4120eb8a76678683d0c61ba2dc1fb67f6a728cb08b00f69a78cba1e24727566452bcfdabfca87d1b2302b2b2541145fbc982e32804b1c4e238a49bc124b75110151954a0b37f7426661ecaba1926fb746292ebc85641d3af0ec611dfca4c1d0bf8323a11b7c892c041c8d2f2a588c46f2d8352ec55921bd5683f780e6939cd298896515738c0c301f881c4f8381affcd17ebe8e874b9d7c3c468e4141460936ef55a1faa6b073a5be79db6218122f56e92b8598d2847f0bad6a35ccb156f3bd30ef360c331e54089c4729843abcc90b8195cae34b8784d856223a9035d3824ede4be5246071b5b96bf32a9c34055f297eb5badd5425701d28f5b02512e95e130a0d67f6be277018c275616269c12fa97b28f83c0fdf69bc8258fcf7aaf9bebd3d81ec3e0435b14a9eaed83e2e6f583193292d4725ce2c0e78f28f03bf6185b9785999b4b56538faec332e6e133a90487bc08ae2d68fc54a50796e45421342d25f92dac57c7b4dc8b57bd413922ca9b0c0735e5e47d589bf9edc413f7b10452dd21ecd4151f148ee158c4aa7350f1de6eacc50af4d32dc87af7d2af2c9cd07b739600b58c06a21a272b72a19ebc81eec6f31039fc568a9a4d41be05537b7b0babd35ca1a2aabe23c2d86b25e709c33fa0c6e0d960cbf9b514163d5b6e1d2249f7bbc423ef7fdbe2ac6ca4552ee45efdb3cff12ca378ee2cbead45424e33ecca33b85d21a023fcc71996b0567e3d8001a26027c5fc0f19651428c21f4b0589dfe4a83e208d659749f1244265ea2392606fe232af2e111b20c3ebcf16fa2246cbcb9bf9daa728664a88ca1e1c5f7666ee49b9f629821132ced9662e94b072ba826438efb9771e071a82f7dcec487e04f0fb864a14abe1f91ff2e9ec5f983277fac4be5eaa2cffecedf71070887822f5914417f0a28dbe2c713840c6581df0532795cb4a667a6850530b3c014d58d1410ccf3c3c123d203659799d7332691a573baf6dc9a25405a1c7a36b0264395c7cccc63a83f98488427df564eb039e14a2c26e8b9a8fd8e03796230a1dcb37d19dc22f61e82b083f01bd6f743185a279c60d60ad826bb24055bcaba0ed17170d8ec539c820210c90556c8edea707bf4bbc3c8837bf9e89825a57c7180956ae9814a4e34fb5338c54759d96105db087f239efcecc96e8f3f5892ff48acaf36c8a550a2f0a6b5197133480d332d26e4d2a9c139ece2fb26bbfbbc2ac89a2770f5046d869b9154cb6c736ba2a65e0c9e6b9e1a694720fd908e6e691cf2f943ca11fa16938134171e636b71134cb0e3221f7386f3cfa1910155a4fb247375cc448d4db827a3bff0def62904e7d4f3b85e03da2d84091c603f27bb463177c81ccbc201e6eb6dc430846b5ac8fd8dfde375ddcbbdaed82086be501a751b42ab4403c3a8da2a0b6bed797bc88cb34fc9c971721941d1abee0ee687982f03caee2a2919ed3c110704648242ad5b5c91d9bdcbf2851cca1386b9f949340831000128fde7a08f8b8c012fd66776b608f1a10abf2024d91759346a1a94a6077333490c23e831bd5232e2238c9af7fa66eb9051bd1c28f0ad633a1499e541af3dde959c2a4c6464478f1a49a1ea932655487eea2f17ae1ba2b7213f8f328a0f6dbb9435d93908d3499e716a889f44dc888502bf1b9e446facddf6f8c94e89c8afeac995842ae544512877ca417dd59a4aec1a732bbb1974ea69b471408609c2ee2233615120ab7383a7a0043bb9bd89f98b63048d9349cdd28cb45beaabb089e4c6fda1aa346b23a80de758522ccadcba457816d25e642505d351cc2531b0e4ae025723d435edb460f9b034d61cc2946861049ce223205b8ae5c815432cf90bd9b73ca9b55e7ae99eebd8b3f4740d6734002345f1b537927221cf59c3e08aca05c8ec30d531b16c7c3da79437927098124d558b4b54ab12b03ac5554b04c3219ea68ed9d8051101924fb8abbbfba45a23756301c6a0989a2f9cefaac336bafc06fd29e604129187c523bd880c7adef03493681c374a6fce1c5a13cb6c6e5cccd0b65ab8d3d8879375d00732ea360e18e0ba49e09bba735010a85bd626084a8e4de02cbc82304c4c55989d1acd8628e6ab92861a027660cb69b64fa6625501bae930d6a515658a123cae5f425530eeb0cc925908279efb9bfe82e6a201c408497aa328a96ec4dadc6af73ca9d7cafa11d630ca5fb7fca8fc4a9d0702b940b9c8c79e65191849d47d61d949f9712e8ecea15bb34ed8a3902882cbfe10d506e4de0155647a189c07bc2bb9345e26d60949d3b1214b102aec27ad3ff1073b844a1498017236dc4d3f499dbdf3c0a6ad75d6731966420cd972495ad04b382d21ccd5b28b9e252c2af53aa1762d8f6bf643cd6006d0b5831f60edd018cc001416bee8744de2692f0a20744ce9c8f05024dd4bf4aabbcb824e94a3cebb5c27894cd98a87cb0b3c406da0078a61b7a5e0b7e457c3919ab7797ede2e0d5d0a38f415278b94f9605c2f659092fbf514d30a188421fc7ae2f4b17ce10a7e58a77de4f0f469b9351fe7b440d11931ecb676e1559e5f8298b368cad5a85e90a13a30ab0ae39fe04599d58303066e87748d45cfd05cc9017dd8519f3caca42ccb671c7e6081c9fe9b1c770ad342858809277aee826e9b3b0455b3391b71c61d2cbc3d2e05051399ab048c02c7d19c7f05b56367982ef554e78d2f714840e106bed9c2ed25caa327468269bf49350b768e78a423b33420b546a8656626275b09b7df2aebcf0ade3c2ea0c8f3cf58c972afb7f153df06588d84102179c1d58376b03ca4a98b45a159d81c3206dd3379988d54f9a365f0d44b94c016825d224fd3fdec48c699008cf7c1ad3b7e751629ae3b23f43556570e00e449a38c6da088bfb38f4e18b4c51dc8054da80fc0d901c3808cd5241f29f98369b0b06fc17c2a932c04fb6b06df41ddb878129e97e110f0ba625ac93247b56d4ad29fc41366cae0e673b3169bd22b714c3e81819cec6c483666e8b91dfd828ca80847c193d3f7be115817961163e19b829e8c0c9fd674dc88819219fb79a1bb1054b68c072548f126168939a27428ef2655442c904c73fbee83a2d3a3c593d53ac18412661d148be8c6e31f27425e83f33832953130559a73de2c6814532562128a9f721f44d051a568afdd5dea0fb4527664ca4175ae37189987bc3271426a24a6f3c74d263a23f635a070240b1c1cb6f60ac1b3285c657ee0a720b04b7867a3c546604668de710e44c2dd9b6c016e02a4aa6292e344ecf49e4e2c5cef5817f90afc5022dfc3f232f2619f57d0532b525f0e8973f4d178c2c6d99766f29c287b58ffa5bfef65caa916dad11008c487e6489f09675133e66e6ce045a8900e59463a30e7ee6f10bccbdccd3701f69b6dec665e4ee3552e02a092e6ba3f98801129c7206a3ad621a23d2f92c0f67dda15351e5fc3806a4ec6ce0d913bf8cefd3f1d1e36282ba41d58c8920d5c5cf39cfc7e80ed4edfd32a9362f657fc72b265f24b22708f6f80b27bb6c350742e6c858476d292277ff47937439ff21ca2516e62292056cff1f97f031d752765ba4c5712051e1403737cc4a034dff2f4ab2261fde4960b8a8613890afb68ea04f1e2027932e8aad0e81ee8306440e11a073e67cd93e04e08be816157cc157d28dd21afb60cdbfb40f23c399c4245dee9f8a4e07d8b21dc2cf145225cae8ab105f0b39be3cd4a15b5002ee44ac8deb5312462155da038fd385e5a63be955d909e467dc536afed5cfe61f199cfa4f77b48c35dd2123585086841d725722638c4ad895b333b66d00d4f17eda598645ab89627768a2297024d5e50505df14bfad21a23ce7d96f50f9331f4b6de934137918bd7dc616359d3d7dd70c755addfe08c91559e0b68f3c66ab9b05a93cd5a4b80226f15247b7d193814b63e223f42946fb1884c77fe0467d245877677e2f11a5162fa3d02bde27928fd66d17c9860b1596507f32dc66e8f8fabcb9dc9d06f671fc08c754e1debc20dc516afb08dcfff63b874ac8a62908b9869c34b380009b1fd0c35c3b3b2c0d82c583866f164d907b4a7bf5fb58206460eb6e1a90aa45bcfa8807e06f2dd4a8c55426b59e0151d9a21688df8e4c9e1ea4bcad83d80351df39c824f422e17d83aa2d6e97d5b36eea87945ea2b90dd7ab521e5c0f9c1f9c7096e0546c881cd2b3a0bc7843f02b0ca150dcad75cdc2ccbab879ac36871488be1ef7ac08ff05858767cbac8126bf524784b7fe8c8ce6bf2cf644bb82b017d2debbb6912f9254c309aa5abe7f2f380183717d43f981e5024ad6b707dc582e2863c5acad9def688347cbbcf776f9e1222e915b6568dbae5b57fa88e84a9ba19a4eeea6347793afe7279451331b836716feaec1c684b4d5c04cf3238becd0f652fdd4c6b823f7a179a6fce514ba9f3212830dfe859a9c6bd4ca1754fe5e18dc9b2b430108a29f227eda4d29b94f79a0105d409036671eaafa7daba549c60eda4fdaa8133b4bd1d484ec2d805f74f8157ddde4ee63966858a4362adbecee9bd52ab9c22899d76ca927b1b86ba9d3aaed28eda375a513a4f6f19beb61b8e4182612b92538d33c259807314801c26c6a65689e16387602afc3824029c3086d4ee0ced89263187c052f58656118e73916cc989c60a94c3bc6ac908fbc6e9be4962f7dfdd1713a8a846126630c34985ec3c0530849f2ecb263434d1af4a1e825f3f60b0afe3cfb62bcff7d3109f739fb62142d7ecb7187379b3dc0002757d86a915575f23c45cf7ce99befb8346eb08a051404ce92df4655c085e79584f95275d9d79171465c4f4948932cd64b08c8a871890255bb452958fe32d2bff2981c539cde9c07c722559afc3273f337b21838623f27ea99bc80da35f260b087cb75fa7a961b4b2e2c21a68243478dbb685d4c4a02c370290a1d25bdcf6c2d0a34c5179fda44428b66294b30d79f9cd88d64529596220567094240e28ff7344503c619291d0385b7e4e34010802c3130e545b465866917068c0e121d4690cc91441bfa809a420412295d300e109497a0763a7c544b32c62e109891a9947aa9bf5ed0a9c35684b5c23cff773413aac81e633b855bfb2a2c0fe7ffb713f7fed2a8da7c9e138a7e88b6b6f32ea07be0566e9c7573b114b5d297be7b7a0f119a5bbf9b3adc4f256c4fbd7d7e7729e3e094cfed2388670b0bb05b653188f634d09cb686edb174f54d39267e98f62e803aeea317ea76986002e8c59c1fc39dff2f432c2b45c4f717cc4d513bba9661f7ae423df4c10a128bf58c6198fd489e4dd7edd606d99f9268a187bf720fad30c0461ff4afa004fb44c08198dcb70682d5608211a3ed7d2458f3770084c4bb13bd87f8cd0de4eb6c8961da8505a19354831524b2ece1bbf05ccfd282e0a21becc510a172faefe0608f09c8636c772c8aca04377385f8ee7f637ff1be66b89e117f9abc4092e206fab2a073f6d36874d6e8a5265775bdbaa6e734780c80ae29cdcd83d64e63b67c9b3179c9eda3a5f77678832826dd1be10f5c27e4609db4e1dbf4ae7c706f1d35a1a0a5c0e30c9397e746b885abca06880644327b3182bc3dea199052e18080f4734a1942241490e62fb35a091c411c7359d704fc87ad30a3a8ce6a34346869a0c7823bb12d4ca6e1544eada790116f4631441b950f5a62203e92961d8101f0262883e5671295eac2e0633706216515188ef4058c21a088f2cfbee75a03cd6e3bcfa38c111bfafb007e93e70695587ed3b330662e27e46a34dd8f457fdd006ab3baf8b5edf3e8f52384a7fb60074082565a52537d8daf0c622343a405cbaf06c455e193c98e3118f82b8e0b10733cd8ff5c0b86d5e6b1acdd100822b50428c63d0044b0fe4fd76c303406d96b8ccf2fd7c747dde5269545e069336c072de78ec31eb34b9a52d0c753ed8f8c145a4129509d39f89e3e8ba49661e872bf624add907e181c5948c863cb6b917529b7273787ff1030edd25545f81fdae88c03bdcdd83547949546efc39b7dcd5fa041c4fdd13082f50692b7d22f9f4530c66c6c59a0c55f67727a27b87d35aba71f8df7581cfd0214aabf5c982170a513626559cfd961e292ac8d949fa023a0e7b8e22b10853b350da24fd8363a624df157cf4587fe07cfe1fb570d2ee95788748cd0bd5f06bf593a684bd091fa66b8755fe2fa8f6bc0b903f4c71df23c2c4b2987264922f282d6fe502645305031e94981542a41b5b3a7740cc264ae3e371cbcf87ffd83c705b418048ddd5b2da0a7b8b32059b9f5777be2f30d0e50fdcf9de805d2b62fd262390176727ba8a6f4a24ee7f4819e7c84f1702104110c9348c2732b90ad59b14cc6d06779712960ffe8f7ae3aab61b920fea62d973bc14b702e0f3c45a4d6d10d87479f372cd32eab1b41626038002925e1789fada8b54e1a4f1997a660ab68180832d1c203b6119b5d9dbca6d32f7bd08a7e27a5f00169f6be82d57712820248c1adfc5ff418cbfff0b63912cbb520e2e6840d25a1abb4c4d7dde16295767ce10914609cfea6c1425123404c5816e2de007efbf3d366e602ed2548e2526915586d06dea15326428cb8bff221ea17226a3dc1f2b7c2cb46c4909757e937aee59c526dd2804ea2f8630f7041909433d5afa28572d04c83c23ca4fb2171b759c6574188e91f707449b690b133926ea5364872a5629562267dc099a8e34948d437c06559b4a502a6c52974e3f5edc48a4295a2299c025b327a0e0f091ebdbb082befd45a186406e730d5bb9ffa64650d2ddff0680436c71f5d858b10e0b310fca1d5ea207017c2fe8538fc91bcbfa713931f42c59c0c295033a0b0097b7bedbc8532854f311f32288197d95659b08869059efe6baee5b340ec4983423e0e5be39d50b59daa74b0d12086308e309824ba850653c6f1c598205a939540db86eb2ad5c992d5cc2457b8284ec61d88c3496a1d69dc4690f1d8f240e72d535e0e247ff8ad81ae3b9b9f48b51e1b0f47c74eeef6f32bcecbc5d720438b62df15f2b0525ab8680172e308f680d3a9ecb70f48f6e4bfc12a0533d9ae28bf38acc2e41ca9fe726b928b22c5c97e3d78f18ffba059462ac2db4b929e7f1bea8d850a74e25027281efd7eb545695557f3938c072a2b49d71bd8cba4af64bd79c10d80aea44db09ab4c9a439a036321ac41678fdfb5f23be2c2ffc9814328a0c04ce17f027f7e2be3db8534b7691c26e2099c6a4bf79d294b6a6decc8c5b7ff4d20edc1ebe9dbdd948afbf07a8b075fb9706434221c3b649e0095d3d0214544286c8efbb7f3314680dba75d17e54caf7f35f0b013c5c3405e818a8fc85f5800fc041c670b63bd49b77cab970356678d1c2114c641bdbacdb6810038ca3ba2c920e40d0c9134896d365362fe8bffcee1e47b6795fd33bda32a0d12efb61c1920230997c7e8dafa73a6eb0b56ef2f7879ed5a21b30f0a231f7d6168bfe74a25cdce8c0cb0c2e523267145d986125a0ab16ff820d3542ddb3c3c848265e76adb0311f2b41b75e98851d34e869261dfb218e86d8c4f6383e52e98eb92666418b9d37a9f4c4cf12286e3eaa0881fdaf28658f4c9465f32f007b419af99f93287c8a088ce43c5f2ec58c83a586c3747472d5e610ba6f70ead3b5421ebd73370eb9f07b00220d9b3b068792511ce78d0e871305d3f9286b0683b6bb8ef7b7494ea958bce515fe9d2977212fe29dc6387c818b1602dc07c7fdca91d98dbd4db94650432d80b3d9ffca5b995e38bb97463a48d9361962e89c9a429ac01883571a13d4390b331ac88ba9acd9745260c117b9f4b07812c22085395f305c047c5d75ba9f9f756fbe95170ccc5cffe5b8a3fb3d10c264606cdf7616e2bfb5873748abc2999486a006c194a7129549af5b060df5adf6ae5b9f0ad586f47c97f51c0676d4a58be3bdb61752fd10dc0d8a9b8277aa0035bf440022ee7c506aab4b1c38ef44e815027a89f04d9d62075480b1c25d5efa6831c5e1ee0a78593b03b370c2d5dfa82641c44b4c2f87e130349b58c08193ee2448fe558e619801fc117137e2dc00b50e564d39263080402de330aba8dd2d02c409b44822add69dc18a5064841e6408c896e258476331d9db6e6b0e42b77d30bdfa341326ea40c6d149526f409aa7df458ff7906593cbae524643bb2cc18b68fcbae0f41967f6ef22f46e53414423745fd7726f26e1c2640067d11d08630fa88a2de3abaca4eee4888677a949b244b8bb45e4f0af52a4768a8b683a5ba94b881f0679d7d0d72b8a246258864507004b5c121511a718d1d459f7c1e6e5e75625c75fbd9b3e8c097eff96f2ffa5c2b995d36c009779817922e5b640a8f83be5c4d597661506a53fbe4293c6919392907fa10ba1e0190e7243c79ef88f9909abf38331898416750ea5017323159fc75144f06a4f96cacc2b9b9712e6e04fb9c9c0de09e26d35fcb9bf786da4b07c88cc3eebe1a67aa8c47dfe580b6987c07a089f57e6d7609b881103672753d95871c1a34b3b8eba7cd10ba010fe4571bda175c2d416dd701de8bf3703b70ddfbe7a744e64f24b14153566cae25817881476712b412a69895a2db703de1f9e37a99724405fb59eb7204899a40886db10c10a9463b3cd831eb119e027ca67900de93a14a33041ea3a44d1f3616ad35d59d841ded35c435d006bb8dca800abce78e0cc69f7effc377ab905eeb9157677c612890871614dd74aee57f85bf33bc4884883e05e231d29ccf6b2a1316d7a056e832b1f63cf80092b089b6ce45d0d2615e4468bf19af022c51de4dfd407370ce4ea2e2e55510fb175ab454b00e246f2389a0963bfba0f2e53fea1ab4534b46c38eae513eb3cdfbc0b198645a4f85e0e64cb28d09439bd42a00ec888a46754bd50c6e6f47396c468504c518e2da64bb9934b4fd8e11712174f3183880c22ae0da034f0be148177200fe482cd99592a20dfdd1ab62903b226290f65ab6f61d98851398bfc3c0acd3c2835658a9157576704ca3615af9768c58b9be5a54aecf159e68aec09fa7f6d2c6dc861622dcdff0070d21f03887f0d47d9dfa9c8ab384a877f24f0f046a48641855f867be49252c7ef11e15e9f4350e06b81a8cf8dc04a194f42484528ce3d7c69e6f589e50140a10f11288f5e6a5247035a23af3e8e99cb6ee1c0112d0d2d60e22179c4e64a7d5539f1708ef34e2a2925220aa45cb5e1ad7a10dead2d4391dfbbd14dd4725ddbcb83dde05f5c5a140d3e22a3ce2ccec2c3880e9ae8d62a4337a0377448c3606feaa03ff6f5f00e298256a2b2feaa28f624cb219c86b9188e69857119666b108a9bd85ba14a1d1c4d294e06d4a48d11c648ad2a27a029c27cd54ecf5413494216dd99120ca9c34b4dfa96713b5b25162220be0828a1791d876a2b0ce035dc81b1e1a4992f08d89549ce412b16b21096bcfa190457235cd48664f07189265954d38f3a6472894c0b2f4d3108a1544778b691a508d7c33c3bb72b8e0d2349330237acf5b9a1dbfa06b007b9fd69411474986ebd3d2074b6c6e7d2d42efdd19ea90badc22531e4f9d9b63dd3627036955d5a9d68d9013f4399958c42ccd918ebb3aa1b81708fa9223d262e1d953099eb4b4b2f5a12f221ec4c70ac5c4f3877de5b865b58fab4a635d7e8913f6bd98224a6c396f1940b274146fcac613a4be97f61be8de734540d8d9bd7e94b95b335bf84018ce68bf1253fb7b7c140f8f1e63da737963ba9b9c84234998785c7df9ae8bcb1b2ce35502e5f8652b92c9d01b2a3d4ec2243edad631c22115f380931d909fdd003bc5b92916a7ab5979e03a9504303adea0b48f5a8e0953956389d6894f7c631ad0da74e63eaa3e3477897292855bb8aa008c971ac547c5402e335772435faf0a4a24e83a26a0a81c701b7654c2f526516754ed495e9d332d4e0fda5c63012aa58b2f0f2e276178c847489dcc04ac16349abcaab745195bce83ff54ba50c9500238c33c9eaa3ccbaa6f50be4eb7c07dcfe7db39cc40d64a0884a23f02c6d45f28b0687c73639d53016e29b79cd407c5076f29c31f0bca4aab3c5e4a81792b47d56abfa3220eca25a50f4ccc8e56072a023192cad5d902010436ae2621dc322b3ded21d303a2a3a853b4b539a37eb9af1e7feb109d39025ac2a8ac26ca03dc9052e79f30f25e5b617041d14a69e8854d9d1aa5d069fab701fa11d88f7fb8ff183c6155c00a38191134a620b80b2439325187e402f4958520045fe0ad820b248ae7d3eb081d1d37e7d721130bb2ea561dd19c00529540ac826da7d600144745c4511f7060dd7fc699f58d9fa82a55cdf014ed3c08063202008047ebee9e648fbad35be59461a092e8806bfc626f6c0b5e16c1130391979de5c8028ca219a596c76712ac865d201b6ee0e6330f04f35fece4303cfb9d531b6a00589858ab29c7613e940e3f4959713033a98c46dd5dba42400db260beb9572a20b2eba06858380e13347ffb2ed934163b98449315815ad68da296dade29263657058691cde81240e4d165e2718f828bcb731b03fd84da78e5f90ae8616df7248aa8ca01dbf5a29141312a73b3634086997760217e60bfd444858084286b86a5cd7e9e06aa026ed60cd200be8cc2903a0c0730885aa7ca9306a28f8ce3d76a3bf7cb8733b7cc8dfd555959c50a8dfd2a21097258294177ea5a974b8c289744f22203536f88d1fe886a32e13b023d6b250e9689c69e70f1b85e6202cc18306e8a16110c8c2827ef413dcc4cc54047e4c106557966857e7bd33531355ab1606b91a02b2d7f60a1a8a88e22e667a670e50ac194149d45847bc19d9da1b5f203cd2792f225f7c8050a583579d484926c8f68decf495c67e9a059949447b502d5ac14ab3fe7c9a69aa8acf5938475088f97e5709580af96547328431c254851ef6ecc9a21459205549542407fe01ae4aef8131579a4cc20cc7ecbfe23370596ea6ada56d8ef48729b1908e8512614e83fcf6d48a4da598c455431b8a41474b6258a2ec755c29b654dd7e9f46773a0852b8a5ac3316ee4b36f5c5e9d1de36879259a4d306878e7f0f43f18b0c9389cba48ded194bfd13868a6cb99a6f6cbf9af7d280800327f1f75d3d51302511a10a6ab10d8e9a770cce2360cc40148a4296bc8a526938059442299e4bc3f915516c6ca3a8694cf080ffb12ce557282faeb578d247dbaf908e596b46eb48477fae836d568f27a10f722d8a72202a1598f80781687ac5318ee5c1ab418cebfcc519059e1cef4f847ab11c2bb658c745f34bb019c8f77d01362aa6bd424eb62e0088f217c1fb9d59330ebb2fc69099e0966bd8a7b00962e71ef976e4adf583601228d966b5c2bb6bc294d4501c3eb7552534541341c0489aac72bf1c5d87f242f60f694744e348545782ec491cb87d2ea9038e6af6feae8d297568d09c841a084059ea48b36705b9b404f8a5526c1ecec1fac80fc0bdd743ade1aa242270109289710ca002f4a81eb74da01ea7380890f63c631f4f34e3bc1c754e4533626dd14c3f392aab393ef5ab66bcb14166e58221da3ffc3eac318b934026d4e8aa3869c8ca86a8042701d1d78fc609ffe5059135a61ad64cac5aa891433c385a1a5df54a200c85d70f61b5f80bd53332b8512b44208d28706bd48c093cc1e614f4d6c743a6053c05de4a28bad6159d45811802a69628643006a6344009c218b97a50c9bfa771fb07775d29ac7aad87356c19f50a148a3992c41df03812e44a8682143f4c68d05ad0186e84f8faad4b134cd463b201b8e625fb039dbab17f7ab821784c182f456c61a30bf5cd7de49aaacb8a94ea59974ca7e3090db52df01f0d7b328954f10eb0309160e7b33daa60495259d63a22575b21a165e6f905ee8b5d849dd26e78cc719f5c1c2280ec49030d71ba68f1b88b1b718cb5121308504f71366074100be852ccbd4a10decca09179718b45c932ffa0616f6b5a00ab611f962a9282272b61a831bde5669f411c6617fdd8051e3e3315283880fa25388f6e1dbfe1af2d9da3967de325c8ceeabb5bf831e1b3107511770cf510862aca17b5abb3fe88500521817423d65305d5a05a93fdbafb9eab405b6f2ec2d614b39672f61291393e815cbfe2692d4cc4dcbcd4852cb1768517376104f7eb0925e8db41acbad9ded2989557fbd1414e3a67f26f1228b63afe510da89387fbd72909c0eb9239862c956b292651a14f8b06e5fa7275a57c2856f48179cf759c10f1a610e1897ef40d0a58321703c3c7bbfbc005d77c7b301c71f9e940f4e9f993645ea2e32d9a16c82b248006d6328b6194b789ec67449b6f4fe4a71c2de31030cba718cd3e66e77c83a8207d5d05e403bbea29915c8b15b76e58b718022240276df08d07ce55a3863fdb0c791dd5c48be50a4e69b0615c2b9de522deddc929a2136d879c8106891e490c8245a48eb1c06777bee47379a02f414c3d1f42f0b54443e414d1dd25791604222fb89828272a6b286aa6501c8ea4b95b706eae302128af5d5cb65da9e7c712b4a9f40a1948fdd302fe621b86a1e0c6b8dcd86df11be4bf4c15f486855c8c9969994eff6becf25fe4f8ffe4f319d5c2242fd9b0c4a5a450ff194f0d63de42e3a48959247ed91824af9a71f5d7fa393aa4bcfdc652ff8988502930707338d17c34de488aa8d85d9841c342e4b3b50598fee92c9bbf605ac21416ec7b96b3008f18a1c86c6c2d2747b84d76c663bcd25520f177e4fdbb2e697026878f7c7a3e751bce7f6e1abfa6811a73964868da16349899641662332e11d6fc6203ba8b140e530dda21effde41219212acc03817ed0faa3b9f149aad4f0fde5c716835555402cf1a71e931b66bf1e88a6fd132395063deed116407e1da150b350d11a2b0fae8e170143cf32b2182df46ac3ed1de05949485fb8acd0ff38d817fadc6135c092ec788ca686ba52b40bf42c491912a84fd051a6aa2ac39ade3bbf1e8cb81bb06e95f08e9a1c3499f3942dcfd75fb535559bdd40a96f7233ae4f88ab3627c29fc9141d6b451fc6b190100cd29009133a5a0d9f4b725083b94646554879aa560472520402da21c7f45186459c211f37f8a12d0f7ae638c69979666476d4e9d3cfa9c6f38f030014573a2fee03d6f9c849311eb83b9c370e5b78412ff4ee137085845f17a1068bce0004bf251d138667debecba5c58b3db14b0fb49aa75cce7ba1b1143f3fad4ff08e120e2401a71e6256a487c37157e61acb237a31f28679cb5177b75ac5a7f112fa5cedd6980da105e4e2cb5067e91c01140abffd44bf48cee48330a791e1f7079ab2f373cb3f331251da2578fc3e78a555f108b45e6061ec7491a0da5aa6a484c6926678e69528f8371dff1ad95da61d20cedb6c890b3808cc81f80f722da9a4c6e5f55bea10dd50fd81c6564689eb75d6bb78446f3256d549ecb3bb20a3f1855a3a2cc7687cb7312e4100020123ab17bfe64d109dfc5305895481b60da7201ea3b1021dfeb29298c50f5df128fabbd84f137db3a854d510b297a37635293cb31db40baf30040d6e7bc486e8ea1143f4c99149724262d5168daf2025cec01fdce92557dbb87d163e93cfed775495bd74d5f1c03efe3a96af03623c14ac9839c144bec1d195a9f9d4c204388693bedde897422a510f2e0896d59e905e8a53e6da9e6b288aa3751d4bff9403bfcdb264b50f17ad0c41202d4c0aa8a8d0b6c25c513b94c3ccaaf841974ce40961ac486dc3b05072b16122e22495e351b384436a182c79206b0b0696b98af628aadccab36af0e12174d0fe031e0f2fa89edd2d011ed9c4beda137f1fba245a7a55d3c3d2ba8ebd09772c0002bcee5236ec2dba2ee41b8ad93e5509208071b611ed0790e32da9fc58d62a6da9684c15861c0d5fb496c9c5fdb7a7a08411b6be1788b6308a18ec087348d25f85a46365e8c7f1ebbe7a38373b75061e3a017a36aff8c7dd10ba47ba07147551bc6849cf7b03c8bc28d6a371bb2887aaf1297eb2799e80b38913f20a49a0dafe43dcf9c270f93c056c05f9d927f395689200271dbba3100a5c85608f0a4c8625b6b080090dabc90787d07d0ae3cdfcd36f95dc3fa616391c418ff0b403c0371a0c68d22b2f548b0d93525374a055dd9c44211e0047bac1da1a9a4c4644f80c50ff5f0c870608a3a791b270332a1bace3906cec39658137af2afc37e4aacaa21cd0b67d5c905e458a6012674c84e0eb547aca5b52c50a4901587989f098067e66995f826c841aeea187d5987cb6ec20b44d9da4ad2661d05c9579360e183eba85ad155bde702220dc8b462c3307a2a94efc0e6efda6e6f02d8b45f82b71348b0a80fb33744a26e8e58aa872fd965313607657f1e430913a0f6991abb92e083d4a01f6e7d83b7d84c8e58275e0099d8ae2dbc174c086d04b214b84f323d27b38aed43ef8164e74ea326bf745d9ffddf1cdb445675c9d069f0e8610deac28f20f447b69d17c56d714b2c0e2498752b990e02e228ded8218ac4b6e807f84799f93b29710579409eb505eecd0f4ddc19738d623e33487344dcf619786e8a895ab6b7d873396d4c54024d8c8210d8be84de100732fd40b91569ab295a23229a61e5e95c7509232d57b58904459efaca07235d94b2944e688ddf7f9b4485a5f00aa918de50c3f8d375d15011a3490053d2c1bd72e1e2a6f42c01a0d6163a90f0cb6c154e646437bb3a655c6de471ce4020e9a2fca48025c8fe9c029cbe9e34c13da255c4147b284880fd5e808566c109c11c7bdc2dbc9046bd8a7e9d05b65e9bed12b609eb0eea6dcfcbcb9587538282b9d4a250e4d916d1610e17b640f9be3cad913468aedbd2683f9b7aaa9e8b5d4aacbf208bbdb1e2e8f7110734a5f1619b91d02d31ed1f9112a90604fbf0b64b3c673cad455bbd85c1ae1a53d5d06eb2377e60f0c687ed2ba061297337105262d827a6f11df7bd7f95e81bbfafc341a52f5ddaae4482c67d52a9276968d9950025754c655684a98417a89b04d88a29e65ad331a232863563b90521712d7db3818acca29bd7554574df582e9e729a8850cd8d5378b883a8306f15cdfe215314d42502844a5cea1a3a8af11024b5fb3fe3248b65243924be0302ad0a8983e4668184b2c686e8806a414a03ec2d81a7f8147df233d58a16fd91bf94209132c92631f95c688c08d5b90a9c63c4059a779844667cca3590030144b0c55a42498672cb944ec3362de25dc01269b01f99022d3e8f413438c83bfb82b94144f540f23334e17f531861f07c73bf9a095f8cbb10261fd0a6182755a509b7064cace13d683b1cc2a770dc9fa02cbcce8cc280ad04438ba08b2fabce04db09c40268bb4adbf402c572a1d0b488fc89f019dcb4bce43ebc561667ab75cf8b801635e3692c57977307dcaa1796081a379882332553c36bb44549cfdbd535cab642617645421bd0708c458e1c8558a796d1b05fd8e7254c0af88e5df6b30d471036a79ea7d5c3052ba4a07566b4594c4e973b629d9a283d1bb5eb0b58c5e22dd666b044ce44a4fe0d3d4f9baf58e1fde514284485a30b80c0080846118c6cc0c304831c01d98f3b420cf19a64441412eaa5235afe7d1df84ec2de59652ca249394016308370746077e5f5f7f8790be39a5f48159ae866cddaf64a091fb69e88c9093149778fd00e6e543179b2f5630ac82ba66db66b9ed6d77675662d2ca18fbfe3116a76d6cb85365fb90ab5b4af5a4ccae94f2a3268020d06f0ced7c792d5629eda6a8ae2bb832f7c31e0f0a0232df8fc36f28e57cff2bbda534bd84406a4204db94ce5e978c7df60a62cb2b88219ab7f64fff545a0abe4c104159e6b52f464c4641397e0593e367292034328a47ea65702713f31aa3641c10e3b3df6908c87cccd45098f9d4cb701d0b59e6a78642ccf7f65dcd32dbe64395aa9585cc424ec5f8192c802fb614e611e365702bc667b8a5bd8c0f5596c13e543946e74335838bda0ef832df5a8f86bd5927fc3254aa9a9a9a1a99944c8d4c8a8686e62d4713b312c156ff2584d12b883032d9cbfc95e1500f8d9632f6190a774029ae6321a7666cba5931f00c67f3317057533ff336331a0dd7f9506599d71e95a1360d85fa8d9b89e1a276c2cbfda9d4f7a7f0f63332325ce7a79df0b28dccb762be5f26e6fb63709ffea9b6fc3a1d16548f2ac2c6073660d880515db5c977b701b379b37ac6d970154337815f62acb00955557dc4166f2a162b2760ebe052537f4f172cb58df702c2a8ca969ca3bab012da3a38f4716b0821c4b225d2c71b31a87f5f1f8e9b5f391433bef32af9b9959aeffc4aea3b970249e9c88d72bce1452f3343832532354530f1c6fdf79d0bf9b3f51f54c282a40491e20d68740489200b2f28c32c9a7c8b3331e517e133e524e92861e91b5e05b27862b8579db4a45703feea0519be0d0c32fc1931c8f06550c9f0554964f8354a64f829ee9521914cc3b500b3cccbfc0c9792496282e8252935e59997b40495fcb4540cee25c920e86968320d4d96c233f8352b2ac526acca70ae34b918bf64f8405990ad6b9897106054809474940790dfcb07a10c26b3f27b3d59ca3e6cf12129df98c9275e149b74a6d861a63849f6ae8768e7d9d51d865dfda7f7b8c3febd4e87603f5e5ce357998f702853585fc6338de15e87a0e12ecb94fe8655d82af691e5fadb893bfde304f0c586611f59c6be43611f59ce5ec3add9ca70f7a7efb23fe10e0a0161d3b08f2ccf7fdf65d847e63257dceac77660dfb8355fe6a8f190796294be8d114a4a29fc497dbc5931d68718a5eed853ea0333fbfe7a92d44312d9e14f7fab134388ff3a28047107338fa4a1a4ec6de3468f1842cc779eb3184e2786109d1231d243aee5a0aee59073ce21facf8611a880214f7a93c7959cf7dc4c7be089314e672524368c0debb3e83b8777e2bb9c91897d62961e9482c163749fedeeb9391ff8724777dc6d5e6c1ee9a43f7bdc57316c70273e2dc7f672badbf4fcc8b27fb1e9814d0f3a665aa2caa607e2cea6be1a5b52135d5f8d9c050061d9545995ce06becd5ab679af469654803250c615faeb78ea42c00467b4e142f53b0d38c1a64b0f3ee060a3f9bd7ad0c1739af552f9bd7a40410f513a87eb53f280e44489872f240f60c42c5ebc582f3c4891f1808597d9c3911e60f8210739c841173cd0a04a27bc287de141894a252f483e259b4848994e8ca893134c61f37b39d1211629ed80e50629ddc911eac48629bbaa9a304c76773224bb4b29b72cc5151f7439b22209153c9d2a17e9a0088c43286438c28730b630c10d967858b0697b1bb7a53a845711978ce5f72ac292bbed61d8628523daa37b4cc1747777f768248736b2c3a971c082a4c393fc5e3a24c94f8712a030556f64a5b8bbbb67a086212868008430472318ca8a34c98c90ee45846477f78952f2a2be74e892830ff27be9b02577353f1db2e8a0045b6269408fbe51dba37b74777777777787ee45b6c8eeeeee1e6308393405618a227a2872e287305e47c2681cf20f4ab264d1c41518fca08b048c6019720222410c99019051103e7c514ac2e80a2e944ac002243948c1611be2861c9c0c6d587eaf1c887278011325398c80a9d25a690d5bccefd5a448132f4b3d52f0424a094296a5252760e2c4d12c3245a6b590f9bd724072a5caa122273df8708318b038428b1c2e5caad00045922c52d4a123d824e2c4050302620b23be600118215ef48044952b7a2065484912476020e3881611a69423db111e362360cc1ca121c60b2a35037b8359c4095b040baac1541a242830d5af9ab8d02640e4a5d9c5ab3fe8e81401824c91cd06233240d51350f9bd9a10e52dbf5713a12b33dcd7177a395a010ba097560152d20843463ad533f5800bc0ba76d71998ec20776fa907869b8997dcddd5d2291d035c5e4c72d02484dbf86064c9d6a96cd848b0d924d96c886c4b3d5260b282dce530d9b004b1273838573285413843388133040e4ae00c819429c519c26d7e2f1caec85489103310010ce00c4f5ed04c3c5144031b2e8413a32432fcf7aee5807e24458010be4b9561d19d4309d8448082439008744a77a9c47d7dbbc8a694dfbf9860c90fbbba592c59047c427180cd24496422244c869fe1d0e58543960c3fdbe2dab8e0260b94f1825ee5f7bac1877893dfeb06a117119817d116b226bf17d152867f27017ea031f27b110d559c2eaf255bb8fc5e4b56c075916d26bfd7921f960c65efb5c448fe1c6833f27bd950a59e3ebf970d45a096d8c0434c7e2f1b68e8aaea0bd6c614a364e9a5a489fc5e4aa6e4a7a488fc5e4a9ee46e7b2969a2240619992e6668b6a49234f14a9244aec9ef95c404344988b25712a12cbf179230f9bd9074c9ef6f7e2f244cf9bd902cc9c8ef85a488e61ac9f65e489e6499fc5e488ee418f9bd6af822c3cf6a62d0a454a7f835474c07ddc3065f86cd0faa5fe9604aa20620c0f085491820684245103c50a2c10544f0cc1d72777fb360ad15e27438b9737c50831725988e08d5fc5e356c91bb1ca66805a8a804478c00490d2320e2880d4cb1e58a1d3ce1830a80e065d2d003ac43977cb744b154b3a84c355bb272991ace25f9317ab4796243c3f6966c88b6293d365eea9102cdef55c312b9cb618a5860509ad54750ea6bef0e6dadee95c33ef6ec23a3232422ce06c37dd4acb93ab245db27c797f33cc3c17c359b1ddadfd7a1697fefa37e06c5753f7986eb7eb2632f2796cd70d8dffbf7d65b65957e3ba9598dd4ac599ba875507935a6923cbf933c1c2d44c462f068558391b791911194a3a3eee0519e464748cd9a535e8df92b26db6dcfbca93c1c3009696564eb5a08a9524ab1235bd7484253664b9b8eafc3e2888172ffed96dfdf48f69bf3ac458bd9c7da6d1741d95e7e2f1304655f15d9ba16cad1541e0e17cc399270d8d403f86566b6adb59a66b59723e72d0479f06accef5224b4b4c4644b4a72f9e5bef81a17adbf8df1258c17422c6250b3fdceb57cc48c7d0bae2dd7c7f08f981db7e0efda72cb47cc15ffc02d00d9b2e3eaea1cfbc9ab31df724dd44d926c9dc45300d4e2156052793890a016af8ce41e296da3d86da42407c21e434adefc7e52fb3d6a3d9eaf86e16e5283d8607eb95e9a4db7dedd43495e8d87414b5b1e8ef6a56e29e78441733e844d1e2492d4c4fcab35f677a9bb467fcfa2dbae1f3bd894e7d7e8734e89ddd50d5b35a16af235ae7367a8f98ba74ef26accc72de4d580613aa859449a3567c4108cc52dd4423b1ef8b2aa61eb1a890829ded8bc89e20d15a63c1cfe131fb96799a67d8f14b4bf6f841bb3a8f983f3c513065e8d2802d143280f4777d184b9fe750cebe8d1a5cbb9c26193d8bbc5620e4d730ccfd50d1bf6f69dc804ece7d469d6ec7c3bcb322d4737b9ce558fce01f58da900d00ba7cc0e29e1852125bc70b23786b3e941bdcca35ee62f8aeba2261a6243fd4ea33ce6515c0c16f3188a8b397d4c8e985f2dd94e3131b5c6d41a1373a5d65a85f1243513604f59116d1fe3bb466aa45763be369f729dd454f2fc1551ccd3ccf7b896790ff550b746a2505ff3ade5a87189c24b197e7f135b9830e2baa1e430c75018ea1bbb7ab564c31e85fdf4d477b7ba3a9fbaba32da4eff72d744a7ef26cdca5d84aa41a928ea9daba152c5f9c04cd4c91e39bfd50f7d8264ebeead96ab672e6ac68c9cc109fdcffb9e79e7503f433e9e4a67b8eebd0c6ec587f04f284761cfa75317390a8e7ae89f7e5c31776eb970d4528fe2fce9e9777ab564db3e068e799a266ad2456de3f4b5fdf4f79eba0db7e2df8fc1adf83d110381ab27db7b1d1de5ae914e1066fecbaf23a33cbfa7b48da4b6f15e26a0d24d6482137489b2050541f97e7fc9f7e91153b6349a09a8d7b49f4caf434ce69f0cdcc5fce9a966c2fd99bfd9ffc8783f45199cd0f3c8704cc66332f04f9019ff9a049791c998c1456919f7fe0c6ec9873bed5a8f7cf85e769e6330e536ce07fa04717d0ad36c3c2dc084f915e61592ad7b49f35748362618b4a2619bb3fff6927fd6b8a314b7e85ffad97739289cfefeca86ade56fbf73f5ca86ad7ea7758e5bfdf6edaf6cd81aff88f9be7dede757ada761c8d6629bb6e9b7368d399b1d32fe52865fbf555f56143aa05c2dc6bc5b1eb5b016f6f03b208abd6657bf5e9a6918b403993d1c10059b8b7fe56c9fddfde419b9fea7e58018bbdae793cf811c46a26d83597e37c5fcbe8e9f69adf74c8c437b0ad05e01a64f18bb78ab235b3bf7b211d8f124ffc1d9279ff3a7f3ffc93f3ae853fa154a5964eb49bbfde94b49fbdd3b4269d892dfeddd2da77dfb7766b0e5df505629a76c0cbb3e27f6946212ab10526bad9d724a39e59cd64efb6a9cd266c77cfa3a2650965cf7235ffbc975f2b387b1dec7dee2fbe2494a39a594725e30eec369af95ebb62d6755648b5d0e4eb3e2a77a8b1bbc317b6d63eb6e4b35cb4605736cd90f338e2318d9ba9c9cdf817df63a308c9ffe7571dd4f7e3ab0a755cbe12fc27882bfd3307b35fa258da0a577cb11e66a886d65c4da4c4afb735afb34cb6ecdb287c306fecac806532a4cbadfaff79e2807946d76d0afaf83e29f6efee929d7cdd75cfd93311df7f55c1b336f16cc0ef35d15d95ac5cdbf0eebcbafb8ca5f15d9e0c7df52aa8eb2fed364adb2be78f21fe1c62c9b4dff6993465767d830fbbb02ffee0340f93df6f2e77b9fce2df91277411ed775ce32eebd1ada67af71976bf9502030774132f5d67a3ac796124be9fe1176f36d7a6c9e4f8a9c1365dc61b339860de6e4986b063d0a5b67c487d67754209dfb1545093ed47a20ae3f19fe8596ab3fe3c3f8f05b3e9410be2779af2858b267785baece5e81c7a3fd098847c3a7b77e71ac8e75ccaf1fb59e5901209ed3d7087f7ec3f93e3b573fe9dcd5ae9ec07ba183ef50e38694c0731a520284d01f72113696e4be11b6ec3d3fafdf1d51880fa4638d7fed4fc7d7b8eea7ab1f6990248f90dfcb09a69c71966baefb69cd8cebe6539bd9ec7762362b00c483fafb73fa0cb3f66fcdd99fb82125f0a032ed8794c0837a0d855f784d4276aa9f71f4af95afb3c4f5b67f01654a0e40be0f366def05a5896c7f5657af8080b5e58a79beab73272950fff4159b307be65b57f7ca68b3df6fbf75e2a258209b739e66ccaaffd868b95aa3c076fadbefafbdc55dc55d0b3dcfa9f37c5fe8794ecf8013fe794d82fd9fd724f44e6c7facea98b8da9e59e41735ef6ebbc635ffb41c353b5dc3c5c0d65aa50cb323614c105d984d2dc7cc57d48feab17f6151a209268472fd9925c064fb33ef9a0e5b839f77db6b6f3f5eaf791a5f833bc7af92a141a3867b9906f7681e8fd37c6ba667e62bae3c3e009481f8af7fff1d77b6555383bb20b9e6699e137a9e8ff142cff31f03779e1fffa8f042101aff9a847fdbdf9dde9f866bf9449b9e8ee6511fffe51aee394b838b92fa199c4f4fb43270a7c2aece33ef1fe39fd633f3f63dfe59fd7f8a83da0582d87c94d4d3c03f374fe377220dee87c6ffc8f03c6f8107ca337f531c10dc3ff74290d5733868f5ff8f8544a979199e274a5da229c39cfa25b02885c93766290e283ff713a57e949a6fad27fbfa34b8d724d4703170e79806a6b82ec80c475f3e00b8176eb00ccfb3c251b27f3c325ccb27480ce7035bf25f46712dfbda9fb896862d76b500c2d8ec5b8cbe8dab338efe8b18e7b94edc8a5c172407a140357d42e2dafb5eb7f63bf186865ff55c7dcbc9b7d242c9c3e2167cf92e38e63a6802500602f517773fd9de1327b39d56ea92fd1246afa67c69b612e273ec9cd0808c55e3ca49ce2a885510dbc53e34f27d8873d8ef54f794ad9c6cfe53d356b0d8dda2326bbb4fa72c7b1a7cfb28ee8520da9f1ef536487b1416f29a84fec63b11e542eeeabbba831dc5fe6d99b57c5cfcc37eb7fdecaffddbae0dbebbb5d967288b3a3d1667632fb6d971efcd38aeb3d6c6d76e1b05c6bd3a3ab203ca7076733d364329a594f059cd8a37e7e39c73ceee7921a5d3a1fbdffb64163d5f1e10a7306ffdb621256dfaf1af7ad5ca0336f7ff53d5c7186d6efcc366f9f267c069a99df03dfbee6fa869f025d7f2f19c6829f18fe7c4509e3f5d1bfd0ce7fb001d888b5890837fbbbb330a33c86a568439f0ef85f0e7c39f0d21ec09e78577cef9b2e137d733e1ff80eff01f841016400a1804e5ced59425428e90f8f3654416f9e567040fe8c39e7e28a59474e21ffdf0e1ef44e9b959b70756dfb1ff9c74ce39ebb46eb1a9e580feaa7803f5f17bc41b28ac937dac19e7ee9e6537c31a70b7d6ba750d342b5a0c04ea5588375058e549564d3615dac6bd32831a8f895378e7ee67dbfc0cffe8972fb59fdcc6595b77dae79cb703caf3dacfdce74b29a59457ba6b3960c6ecdf19124b6bbf5acff2a1d623b1b514de77b526e1538a7f40f8d9d30cff804f5d5bbe2f5d050d3c0b2788c5079cd08066c567ad8ab64e47d331ff66599651aea39973737a8682c8135f30ce0533a642bce131a980c396fd8d8c7bcd5221def0f93bdda359b1334ea15991fe0f8a027d997d0f9dff63be5779a5a088dc30644833c9e978d96727b14b3ecc40240602736b05ffc73ee0439d66c5784347d3d9691b1a0a25a440e9c10274ef95022a596257cc0d436e081bc6f9837e6b85cc25b36b39686b857e1a39054f7202f27b49614a657301292d47e5917d8b0269c9a7f6775a6707facf0c37eebe85f9ae7b71fdace3f481b2e57c68643a250ab57dcca33e06076d74050c77d57fa763642b1d36fad0032df934f2671a59e21520377f46933fa7ea47ccad15e0bb6476aeb1e540ff4e4312ba28e291b0bd8ca0925f9effa4b55fbf87be0f0c03887fe3d50f0f7db07fd93a5ece31ed0780b2ab1f283be7432377ecfb17ff4491c0e3792148f6dadfd7705086fbef5fee85282bc82079a248ffcf73f3255cb59a33fef397114ff20f657f498192dddd8824529272d47a22fe91314a0e3e9d1f5b1525e5ffc30786d15c96f4e1c0f14884e76ae3633f8fc61dec17bb882644dc30b9968f7eec27867ff4cfd92f1fc33ffa31dc02902d4fecdaf27c0cd73641f670603257ee655a1fe618b7144c51ac924f2950ae95aa5c0dc4f652cdea0e28da8fc2027d774c880d665a5d064c4c6cc9d3723f2af0d88f1a953076b9b1ae82d87c686416e03fd931d71510186291f39f4de879b0ac0a3d0f261980e19ffa41e8bf26217e7c3c90f399cf23e62831fbc40e283b07e0eff4ebe745974a3aebf44c39cf928b1a0ff9d3bf9af0136b06b51c387e0b208c8d6290c21c38125d1b7772fefe978b15cfb7f1efc5260a93877ccff0e9bc9c67cbc17fda94f373d578cc87329b9dab472b618499b599b516dbd8f7585b5e4ceb99ef77c639fdb1ec6b6d69ef6a6d0255ede3f6773efc1fd457dc4d7ceba3b68dfe90127836066c753eea67fccaf13c1eeca4c598e7ac714698adabe5df17b53ed55ab1cad94c9b1df5b1d7513150a66e718fbf7c29f327eee28e7e899d587067d19366cd3f2aa2a94b046a1aff69f7058d524a8b5c1ddf7794365a1f988be843ad87626804a1bc1a33339482911d8487a46c9754d686603c76107df845193e7da8ed383dfc1b1f8df1e43d34dd85dc72c578b59f9b9d3efac0168add432e98e973184f9da4e98bcbbfaedcf291c97b371f196cd2de5fc3f08b8f98e943d811c61384d1f573e58fd0ff5c096b0375e781dc72793ec5fbfddacff88a3bef997f3fa4041e19df3fe331ed53a7b731c69f6978842112ceb49ec6aebe0f4cbcd148619a35936c1d6c12f27040193f1f8291c14130318459657048535aa86d44ec6b13fdcd71df725fb999785745023738e81c81040c33d28ad96ab12198f4a58b7a09ce482b46fb31c1a1a42f5dd4b3cb794c7028e94b8c54520887923c7a64824372c69cc734e5cb00db3b4bdc7d204be93743b67e297fbee7f539062d17b1638cd1e3081c4622ef45e8d2eb7b4d6f886cf03bcf31ce25904b7341849a57dc30c10196dcd52e6070a094d3945fb5c2195a3d8e4d8ffa2f53a08378ee6f35deb06f392132e8f0d417aa3cf762fb95f30002f2d376bc2df2c340d848e0f160fff3927840501f04980705fdcb1480360ff7c070ce913cdc0779b0bdd70d9a72575f5c08d5d8b0a11f754001dd10466fa1b3ab3310150748b9ab2f1c1cbd1b235b57c1e4be41506e0a74100fa671429587be7f9a090fc3f57762fcc9752ee4f914bf39670b48ccef67f2d3760c203f0c0408eaef340c0ae2a9bfd39106f73d1beeb115d0dfbdf75e0b30bff81e0605e587e31009dd064a7088f8041ab6547e2f1bc090776eb0a449144b9a34bda009ca960ed62d870d66aad590ad53a9562f3d621d2f6f5dbe74e1c1972e61c04c499a4225694a134a48d15dca39a99c93d25ab11869ad1866ed8d11b3f6de2cd36e9669dae9843a213b4da192fb5149a8294de4fe18a5dc3d44a4879a4837216a21a62d59306de9d284e5084229e208429982048dc2c0a03050080c0a03894021179ad5ddfeb6e43ee1c2ee18dd25ec18dda59cd4298d0da9cb3929ad15b36e6d6c6869c5306befcdeccd324d3b9d50da0985dab6981899199f99890d67646652291a9a18316a62bad701f3aa5991eb707478e43a1172fb07fa6fb22bddd84093fb25d1c3f18a0cd0839b22548a19bc63ccea0d11e918ac4de374293dc7ee7c53b479ee626e8a3698bb2cdf1cb9297233837b7dff34726e66d8e03f29b1f7629c5c979356ac7ee9a438c4e4472e27ad94c9e5a4330eb99cf24b27b9f42f1edf52078bfa2ddd086df05f8c31c60c54613de2de24d9e0c7bf415237fa1e11bf39523dc618a33bd7a5629cd94d911b251b7c6f9ac5f8374ab697dd107958ee7e72eda24b130ec02c2951a7ab9b1a36cfefd56494bb97d4f444c84d13af157269ea4184a61cbc3861a7595b30d5a08b262622c069aa61861b116c01668b2f4baf1b18c0d00103278b2c4480ca5ef7aa546943276704a9855296b2e684b13da50ee1e55a83a50ee1e5aee65a2997c91a60c952d6408b2ca59452e22d687b3c3ab4488a21bc1ce3e374085acca085909c24657a740f824e157729b8c8eeee3a5596c0c24b4ee5f7c2428b0f9ecca2a3c325d371f728c5d29542cbc4a253a54a2c9e801307078b294a30284395090d148b2344261633c83045dc0b19228e57781962e50a1c58b942cb9037151764516e17b9e288a9c7927429fdb5d1fc5e57ac90ae9861055c76e0022e4cf38a1cae201282714697ee984b1522b0e293cb8b4b1291ee1f49f5a5705f2b914eba8acaefa54398ad89298b2ab9ab4c4b55b6600a113b28bd7628c10e4af27be580650727f9bd7640ca9d2abf1d88d8e187eaba362e603a2ae24386fff0056230971862a565d502a620987c7859987680aa27c35d6dd9fa5fed5775c32d56596696559539c58b4907261b96240c2e542630b595028b8895e37fe0c545e8f95861c1c1b9e28eb392426715054e145164472a53675680b1828b0cdfdbc612a61ed04617805825298a27f24acb9c7345c4d6bd252b8c96c8c060336263c156c486e345051b125b12db9c1309cffc543f1c1687357ff5822d061629d8943625b6233619af29db121b13db2a695b51d9a4c75e6dd97aa4a07ad6cb8a2c59cbef65c50c6a0bac7001b4c268b3424815ab0053450daab092df4bcbfb2a8e50791548a0c4f1e29373820aa44a85d18b0a1ea820826df922c3bf760b16f07e0c603623086dcb90eab425484b98fc5e5ab4d0c29465bc54282d466c5a8a608c96212d43724d7eaf2c5fb23065a9228bd28c0ddb7b65419a7992df2b4b92dca9b2084d1126bfd7145d7257a7e032851256e43e9cef58dd08596701195bbd90d3a3fe4a6e36f76208902702ade1ae66d9ce65ff785ea5ef1cf6373563560d4d91441344812639fed0d10bcb122f2c4f32086720a1162d1bfcca037ff2e03e197ab17ab29abbfb751c16ab34668b3754acba6d745349bfa8f8cdda9e8d8eb0d2a02395318b62e77d62c3c01128605aa20417352811c1050852885831448612c0309344c66c025fe00431980009cb175e54700549ca133834e9620a2caadc20abe2c59d32a852455665e94e2388440c0605bc828446af145d39ca947ec9248d6e25c98a1250407a040ad6a77404891f522004982f5486b8924417466a9001d2152e4e57807080c9c41519c85ca1626b2962a049a1824c8a2856cea0d5287d829825a0981bbd62442608315e7cabe22ead5031ad6031dd0a1659450b7dc1c9ca0e327d626665c58a17546955e5b53a92d56954cd286690e79ccfcaa1dc6ae25fa1a0d62d8a18243515095daa56511081a2494e8aa1a708f98703e224b97b142bc81e038fe3aca4f87c45d143144d62701f8eccd812d3d2178a615cb9167e5e08d2c27b3576e24fceb2fb95f3a1f118e71373c645ae95619b1efff8b23ffee19fbd582b36eba41ef08fffc33f7e0ef7e1d6b6d1f51b6dadd8838adddec27d6d43423a5f4f95727e7d12bbfa075b0aaa11c39573c1afb67ac095e1db07d2ef42ae0fa4df62f6a5d61333f6d162b0d2989da76cc9199356296596c98eb4a99c5c6ea84ddcead77a1a051b1ac584d45acc525b1f06ff3ec6ddae7f1fac96cec0be852b20367881d88fefa21f718bf3a1d182d8d526e00a9f7ee76a960a7be8f053cd720cd76651efb0afb8eb7e72566d465bf3e8b6a1aa396cc5365dbffc29a7575ab18a598949ffd7aabd575a562f269ae8d7963a47df739c81bdb52ee39d50dbe14f31faf4a75bd8778c8735351ec3c688f1d662e7fc6b8c94fda024a8642d9755390a11d1000000001315002020100c078462c1603c0f5345f01d14000b84a2426a4c1809a35110c3200882186410318418620820c81864688e58014c9d86eaea6f4f248053d1fb8245b89d33e15084cf3c1eeaadf05010b383704649e71b90c796dc92efb8254514a8c067455744888bdaa3bd37a322e28ea89f0b741e63ccbdcee349a8e73bde10fd1022cd1b89dcf7f0bce83b03948b8ccb4b547b09d4690003bef8f9e9239fd9f1d2cab1f5f5c3df402617f43d5bc25e5205eafa7b04b3f97de4b9e391345f7e33a862e97faa05e867e511d1e87b5bbcce204ed5a90bbda25c6f7331389af213bc8df10346d1e868ad29c0b325b52f3f6735a899da43454ffe63e4c2938cce1687a8e3e9abd6c3f82bfdd37863fa8878288768f136843beb7ee6e40bab7fb49dba4780e1b923fad5fa644d0fc3b758d33fbcc01b7295ff58731a993780b81ac2edd9c4574ac6de073124fbb1a63995655404245114019d8b07043f14d06dc6f9a5c6d8aad7a877cd9ece57d1d069303ecf0e9e9bcb2e588ecbfdd1cf1da727f9b77c16306adcf5be8c2293b1b13a5b17d390824a8423738c53039b8b19d70cd74394870194c7f28450c9b482f8c495b0d96e808f7bcf1557ad97fb4a8562b37fba535b003b6f7df7c2ff7aabfad6a9462e262047920863acee8cf438896ef091c62e636c09579bcef1861b1132f4b1f9c9dc6cda1f799326763dbdccfa7fc29d07819b16d863b09c1081bc8a4c167619144174de372e8690b2868dfd404ac47b914840aca81d4a17b52c4b4c2d68c5f9286ef5b6ab33ffe03cdae5750ef7491fadde5c93bd36c32d5b79312e95dd76dab25d32f38964e7168d652189dd0761dadec633807e0a0c098dffe766c89539f1a391a35efaa44c609a41875370d1eede322e4bb0e4f987009c0f37dfac18e5002828b9b69a3a6c70d2d4015a351006525266d01e51aed5da2e16b9d3fcfb6f4382dd865065c7e7a039750e0637f9f5756c86fe547b5d8062b03da9155a5630b7bd848a88c9b4f158f43b71e9262759a3bc8eb3d86262dd5ad36f41164526c29681c7089e2852fcc3dc00d2c367cace2ab8c3001b6e9f171db5ca1a06d348e0200d8c524639fa1d58f44e62c82f6abff51141a6d87e66d6d130577d1aa4ea5f10fcf2f5a4108b7319d67bea8bfd68d5b3ae7b48929408196a5ff2b07b00eeb4af9dd274274af82b4278a5601303a8b0de5d3a764ef19b6409f9e00a4dc250e00dd64dc0dc8f3a5634928c7ad12574617da6841bba005b3697c7045aef2fb00c75c87b3fa4fd62a30f2ab029814980ef03b8ddc39992ed1aa871195a9b94667b94acc21a4bef3e8935ca11daa8e0e8a1e64beaf50a366a065de58a66dc0369af786753819c53510258bd190d7e4dee368aa2c6b78e918c278d399797848e3e897838241022c836ff606b26029b659385027778945ac8f30dede62545cbba1e3456a9f7052dff605c48fa963a9feeb3761da1afdef8765313212ee582ab4559f0bf33882b71f929c13c09bb80c4fc07ebab1df3981648195a2399e5e7c7b436936ce5a65e0b634eca332990347445b3d7f3a63508f3c4b1797a809e09654c95976b0437fb590faf4beb477748c20bd83905ab5f7b8d488653f73a47e1705db5e25e695d740e77641a78ebfec83c5b0b283483d4a4453eb4410c7b869ebfa5fb5d696157fa00ffab0ce3be3e063a10c599bf2861605baba4dea8b300b1c776aa51df322cc8252bca94bb759e2f92df918959126899613e1ed1889f7d5c548ffedcd265ed89553cdd3f1fbc42c4521cf52eaf3f60e26b9c730e4c5cf445cd44b4a8e72e16165e59b2122a4256b1aff1455fa19fc234f7a65ef1bcadcd3a1d7e5f0e7803a163eb2efab94ac93905a4029f8046f803fca279b298effe38a1069066b1704b6a4d02eb9bad84b3bb7efb521c6e1a5c1331ea898ba17583a4e6e85806bfd80cbe6dd1dae14663eff3c61eb80c506b0bf5c3a1cee970069ea8010e6cd0b4fc0662cc831b82b736b41ec314a33e12fd6221685a0f85bbf4405232a0905e1ef4d74e51b73c146ae30c6db08a1eae013a56683deacef2b85cda74155aa7a1e91b7cf69e1098be7071ecb2aa858263a5125e97c763a546945e547f147cdedc6c893be4737f3dfda5c45d1b05fc53cefcd26682cf547e5baa6bfb27f07e98ed40c754ee121ca555b12260c26c0515f25964865bcf0c60f32c0ec8c7609ce6658edb0d3064fb2c70a6b7824eddaa8f2bec45c121c95fc4ca6c818485f97024c4832d9a39904c63bda2a87ec4c20889296475ce113ce5a59cedbbcb9b1e8ae7644f8eab3c35091e1b58cc16de2312a5f8da0f697661dc41dd250e8c299997a8512790bb80b966f9aa7da81a9c8dbebaf3c127f127d5afc39d53f6f8ac084c7889971424c0ed68bb46d55b7b12c71e27306e30f17a036dd1cc197dfbb726800963e225861b3fb528008db98e26cbc42b0bd471abf88e0273734da189b79ca33d8a4ff4bdd020503c1c2576fe54c260a694f2488d255fcf2e98e49481ae17f9fab6c83327c1e83898e50ba7a54994dc1bcdccf457b01a24a94f7ddfba20b0e64274774f528d1d206b234576d1f0116c179e8c5bb542bf2e61d2999f8fd2d72f65e87e946a5ede3a553859cee6253f3f8620438a88e9e03e2fb380c06741a0effffa3d0a5bfef10558e0d48dc31b08f85dde63a53fd726e74dc54aa8b334db16c51741a9d2a7db4cfb27c5b63f0411f23dd3e6be961ad5e90c2a6706f8944690507a76497f0fc9c602f4d50458c5c68f86f0e5e5b4603e3059362f5998a051ead92030b88519755181ad37cb7def22825764eaadbe9bbefa23b80b7e1a08ece5034839e11ee03c88dd8c89d167156d10ccf741638cef71daa4656d51269418a68ef1c837f205da4aa4bdeb2b646b78a93e1083a376e8e44264a211a896337277431593c496769ad1bf09940f0e1b14fac64b82ce28f85a4d8a7219881ad0c01079e8028f40be4a24ab280914fe7c812ed98de4063328d10859a3cca2dd82997c6551864f8e2c095852fde6205988d620ec238a61d3d681ca095ecb3a2bc580768a8c62cdf1321458c419f71f4de1ed67416f68347218bd2c93e9d01ea74b36491ac67ac4cf7987feb2114d735121a2978889369386062acea490453893901f29a1c4b8be1b46c383a527f07b5410ddba83fa200a7c512a0602253212fa4eeaa98b61501585d440eb0aabc56b0003df10209818f422debc09bba0909f18c654101cb6c32a21971b225583e2d1a81e4d54d91bde51af5fd7561db14fde5c5d47e27592d24b26aa886d88e17745b62d6c1e9162bb59920c29accca6048b55e4ae21b2bddc988cd8f466d33c4941891cd25904691ed13a12c1064a01bfdd94e1fe987e7995c94d942158ff384e358a6e4f75affc65c034f499e7f7785f86c701b31c77b9a6bb969563b8def6dd2dc1eaea049969438e2e50360492504442a0a866ab2df5df079aca7621f2280ee1947f8f53d627fa902302c7f09c99759d305649c56b97b1fe7d4451bdd687d1c640a451c6e4ac45d66427ac3d814f88a620f181fc46d2320bf75a3bb01c122d8fb86f6273f28539215614d3291ae7d4606eb16f75ac195b18ed6d03313e93f12f4c30b59d6209e7ee2bdae43509a84c5a49a08909abb27bb04f815e05da9d11a00113b0fab4619c0c1ae59af34e9f38ad52d6df7957cb88e4610de616415da3d359639665134e7cc118489709cfc08c6224ac7b2a50a29fee533c46f5ad47250a628f1d118b48f0e101d390098c94be5533137e2e28eaed3aaa2e23aee1ae1029cdbe0b909e07d2ca7bb8095d7688a155b2b18911f0d07eb7f0af35b94a9fe560461ef4ec1e56bcbdb85ce1e78322d70932e804c76cd0752e81c202ecc1fc7f1b2b3a9c634c1d481d38105e6c24f1a68ee3ce5e06e7d53ab65d0d2ff3f6b47fab9a48b4813f4ac82852557927afc2e2d722f648530245de931fabf1c25f24709f3e61a6938f58bed9990809f509c35f7895b53edbc054167c4657f441420d822d1eb68d54f5f9593559acade2d0c7551dfef859a077f8513ed08f2e3b0d806471e99f76e7985bacd28e6aeca128e3e21abff06674ea18d0b54a4a0aa873a2f6e835c5a6d51162e486a5ea1ec955fe959600418e55db02e6857da65fb9fa46cfe84cd1760f3fd5772fbadf1c4826f247c4f73e8d09772f51ba7a276be4451718ca4221d071971e73150ddeb435e7a2c04e3420297157573e56917d0ff8c8b0cf797c809ef7ff1e94c6a1769103e3e8218075c22683a726fc863f387c142ee8ac7015632c1d15da684d86a71edefa6e09afa769f2e24080ddcce57e68dcd1e264baf98b2802032bb52d3e60980a0fbc7c0463eef5508ff82e8c49bf4b5cc82df5f5f28d01d4240c97002e1aa2774c1287c1ae9a89a18cf4895cbf8a214797dda49c0ec46db4d598c713b70ec40fa1ef17be65f068103b164e02176ca15d2d13f9d0d9ea836e32b529c5231ee6c0962b9799025ff8629494312b2cdc105909d005f32022fa9fb521123821f78daba9953930151ee1153bb93546012d21825a8eb103ee6a8683579827346cb73107de74514a8c11383c2b33a085b7918ac849aecb984cfafbf942daf00ed5e2a71a2f26ca61472b52788822bc914ebfbafe0f412d0ec2d6a3fff342c8bb75d01e21ef3cc00cb371f00db33307566f73980063afa0bb5098a1ad606c4bec64537a64cdb345d502b4bcdda54640daa6f636350e47137870caf0a56459a9fbf83e86203e9f6eb104cd81db3e869e61ae12da21684dc4044021f6726aef315c7ab7971e2ecadb3b12a762d60c83a8acf4f4faf284222e90823a8ad42c43a0531312b18c119850fc2792b4847fef39dd89a1277a5a8f1942712afdd76efe5b713ba3768b1415bc2c4a3d68abd1d4367328b1cc0e70646366c7fd5888d8014e8e028d45f0f8589abf0057c3aa038e622cdcb65f02f2f56e87660f916bc376dbeaa2b3e60d5c1bd15613590b102580cd5ce125b7c5c9274bb06db9286f236866e4f1af96461c734312a5d5b95d45301d49145339b7b5c966a1220638198e171b207fa96c67401f8742f41d80bab71511a5c0005b18f2e5380428c135e0e639669af965bbab88dc241de4b666d8360e26ad16f4bc1f12ebb80dd35779fb172c65079aec062a84426bba7039f0899502ab5604b0a880a2f8417852a1462026631127d2e6371e3dc193261707d7ea5756e36236b13dc2e996f65001135a7bec03a1caf17b4e6bbcb156b5f4a1a61351977566b5968a8d235b5f0a24804d1e39a1a3856428db49b284d5481f056dde776ac820a85c3a7ec194cf7348f66d23e6170741dac42813fb337a4d1717dfdeeef8676965c37aa90d4b443c600c2d4d79f101445fb6d772209eb0cecaedef3287cd697d931141454b3bbe9385046c43cc8a8024556327b9ab2be2d7424125e73359ab19bec2df3dcf853f48e0cbd97caaebe99770fcebf9b92687efdc6f067dbf04a03e404820212665422e3bd01044a38666473864b5becd47f6289b4c2b1e8e3e12e79bdb19cb65b78e46cfbb69bca145cf9dc0200f8261d61664255945957fa5a5fbc0497bf025326e0ed3af8913946eae0a7bf5e5a8ef102b76a1d9b341474c43f880317c524abb311768cc03e592beda8a20c147309929e8dfd505af08ca3aa31cb87cb40e19dc7812a07964e0f40be6abbd2284aef8ded74739aae927f89444a3419b812276c850d72fcb04c81828025a33050ab5d9be77dcaf86257aa614a09e46a1a617d2ae407fcb9450e2b565ccd8cccf46b7c4aefd6bb932f371616c856747583e4ca4c612f8bd69ac40fed2cf060b2e92730deb334bad6a7cff42d3bcf5b4b70897624450a5ce26fa57ccd5324a9bda298eb81d8672d6ff453aaddce0fa9c2e2de789cd4d55ae5dc253b7df7b6df3b6d0b95ea00afc15e29a6df6f47dc0ad1a7ce0ffa6f11523e936e8df8db0ec865da2a33a5679d9b131eb40e06fbff4b3bfb68f6d396ee06ec5d016370b1b132eefe228f45ad409a97f13839adebb2c86a68d8d476ccf960e17936369aab90c65be3612dc8ceea6a0cdfa211176ce0fade04665aebed2385b0e28f7b7cc4f0dfe92475f450275de64283b7b9bcdcc4f7626f878ac8b0d034461f3d81c42f6f1706d54cdef23d0a883e8d9b90628b3dabf35cf44129518d524dcd012bcada4f623da293894e5cbc97e9bf59f59271abdca33ad7430359b1432b067e6ca437419c55eb186025916df6ed5506422d0d29d6710e62310ecc139c1e71a56c077284767ccfc8c440295a8233c25ade643d7d41da29fdb7e177e9553d735a7b8b9cf8759c2c970e1afd41f9a9cca7b0849d5f2732262f56eaba244b8c1c6371326567f6b2e32d8602f1f7046f2591413aa94f839ebdefbab6157402fe53ddca2a3ce4bad437e9035300f49abce81c332c241ea90e12b7974405a1d8de5f1b352574afabf21a187093524327c9870523ac68b0d4ab45683c3fd224bd7d2fc8fe4e02dbb300cf01c384bd47371d6d242600b74788046ee3bf1eaf24b159a13887bb1360385fe831e51d5984e0ecc7ebb78e1444abddb7a0bfacbd7b4f22d704dd35afa23d000d6474e8dfdba9605a45d70af04610b42d8ed9fe75608c37448489e8460a4d05d6d2590947cfe7cf0257ae41277f2d245307cd083eaf9b90f9f8c95b992b6907892504a1e57e0a538edf4e9e5a878e284405fe77328e4ae64c1ca7612a9be632ccaf1f9c5233f7cb04db47b7e041173a2efca3dd3f11dfea45eaf2f79c2cf08457f73544f85482511caceb15339c0adfac2c987134909b93385891c4fbbed82eff6ac214bab1967a58e9b965be2918179b0c396e565ee839015eeed1e43cb91939f4185ce06c80e4033405708d41237393ef36fd278f9599950e866192c2ef9f18aabd1aee582ceb3acc68e609c02100eff8b0515fe22eb86270dbad1cce444cde381c22f2c047ee62ee5dbe72e047af55c25938588017432387ea861184bb5814e6f458df29fd402c419ccab9f01d40808525ce8bdad0589270fcc85524951667e04bbd7c5e786596c8e362b1eb106e3068431996507429cfdcb145170fa7da0c4135a0d207c9fee087d897f785455d21424d3e88938e8934201344b2d806b05fe2b4f9b8c837a3874b681bd5f093419fcfbda4eae1729748a96fd60a94864f1322edf9c9ef9c976298da80b1a78fa6108a921864f250e461666d62b49f50a0d56cdc5f01087339530a2f0f582a0b4424b405502b0684e2c15eea47eefd32eed4ed8f0fb01a11cd8fb5d1121338f38e405caab46a145cd2d71733782d4a4369dd1f87dc1d3d1a5fd063ee084867943c4f3de28a779618545fb3b32dc5b9a1b2babad2485079a88ca9098076b59fce45708e7acdebcb452d32566a79b08de27eeaaa64102547c22203384a3a9dd3f452b58f08952194438e2b10331257d7a792d0cf690da619e2f8750d0fce846c10562163d721779c1520daf3484a47d22e693016489f20175c05f73c98d7fca7ed9b144323c092023e1673796aec2925330a149dd976d5340ab79be071888cfc76a7a98cdfc7d907c861e7152753f989310c1c4e36245d63116576f12d59dc678cc08a11a6721c38094617fed583f275711b95769ace45382a867b36e65d083958f3d5863f5a693d33fa92ffd073d85224e44e53c61062e979a0f728a3b63c41b4701a7297838238004569fd7604cd231ebd42b4da53a56aedb7283d3ce53b5eac8e329118552135ab2facb8dd46dbff38b45d922a5125710a6b1263f5c0be589cdefe7e275a5b1eb951d889bff6b28c87f80d39014efd708c027a3d67e00352b844b567155df6c6f7a026b801a52d5a00bf47501d11d8d8b6438af570e97394b7318b45479d5aa1305969517e054ed78b78864744fbeaa3db1ed412ae855e4a42884dbe1d722109c3fa918e02a96d84a4e8e0bcbee40f1e8fe7260ca1237a0f19e074028d86e934b8b7c66474d1739c5c089a07153772273c6a48f2b4156cde934b6daf1f6b2c3f927683c29819182411661dbae31371c6bffe70a8883a8408e859d32a4b15e5eadd69d98364c2f3c07b55017187c51e37927d2e06cdec75597df5df883d585b8911567e3af4865682694478fcc9cce9ff35a5594a1f81e6d0c211231ec10d0e9ebf3823359ed7b0f68915df9a8e691077ec1db3ed92fba761e1fccc6fd68a0dde8bb0f8749876339232626a5531a9fa67d797adc986027fc8d9cfe3ac238330fdd5a87cf16d5eadd99346d0012aaa7a90c38dd08eada1803a343eefa95e6f3123ec01d52c1eea88e6bbb0bd1fae3d3bb17f5b3cb035267c81d4b226326bba37819ea47a06856f29d16415451d1b0034b92aa8c61810aa0d20e1502321244f524a323d960da65662fed51f51188e7cfc0d4151aa1807be02ae81e802e575bc03012b2fc77e8f720ffe144ff6d4161d42ae4a23199ea9c9f80426c627d58ee1e9bc2f1eaf8af4a9d1812e0ba8cd3e90d683c89f21c517d85c9a9d8f766dfdf10b6016e495b765ac799f20e728088216fb089d965175e541d84c874415af66c52646bee439859e36a91957fd4cfc767ef18e3e6a820a80703c453ce2c7f0d14052bf1ace016a2aea63e9ac9b2b0b57dfb40ca78ae4037de35db734d718afa21a46bed07544c50ab2b79dfa98a734f0dee049ee6fad1acbdcc2fdbd42fa68ca2d89139aaedb7a464accd7bc2ba1afc857883fcc01c6a6f321e611ad8eb281fdd2717bb728187effc418c050d621f593ce2a436a7f1e764f9b51e34279486b672990b4f2b14f8c658e88edfecce2e43a727ebef949bb9cc5efb58d83fb95a0a0b2b1503fe4a1c8f461a951f872f650b710135aa04f9925179ce308f55a8e53474d6aae24049e9282422d6ae0a67fb5d840b625b9f502cbbc9b8fd822e22a97da35fd28bb6303f47433f27036feeab497636d91e83e24623258cbe035ac0ff50fc1eb717e3d162e52c0b8c7b754212faad24c60c580e6d0d211911619ab78bef9a916400a12239a1a018cda99120afa18e84b84c3448254e854a76f8e44509f09cb23066c16ad1c5aeaa6ef8cc7f2e598169f2956b4f37cea54e195e152ce7afeb3ac001b69bac20cc72f1efc69f919753fee9da24e764c848396e5b58e548f1c9969b948711b45d654d41c418653a82465384b183b8ba9e53bd737ab71c0aebd58ef16b09758b1d6bfdfcd3b6ce4e2be9b614705a268d13bf19897c3d624e4cbfb304b1c45f24f1112ed0cc1b14c83999f697bc68dc8555472f26061b3b6cfba3d64e4db2c7f86051db15abbf93915f52a65ff457c2cc67c09a2edb39e9089a574eeba60db1c5534b5442bd402130a6e458c34dfcb9ad44b2dbc29459c4e3168ccf27f1316bc54d230f3efe10ffd4f3a4156d6fc10502cc769d02c0e423367d4d93026cef06b9121548025b7037bab94e9a0e36caf297f71ef7e3aa140362a55ca3b4851744b5ec69c1aae0baa0356e271e1e92975020c5b1f0f526cee6b83c46b155be9a84d666f0efafd5e65ec684e4e2f76b1a3e1a6e3032ce349d5c725102192922298f26d798b5a31d2dde6ff0799d5f0b8f1755514ec284f584b24af99803f15595213760dbe9691470ae3015de01ba383bbde01af4b564b27d5e92bb61888bacb689fcce100c40b3b40e062f5d0db64adc37f31025cd5bce903676bf6e3e3664bf034bf2b543db515637bec7894d4fc0324f07f4c5c137f4719bb61b02b98fdd25f00e2ca124a93f433afc9384893baeece8d1a2b6ba069e097234df4647f04c4da5f63a8c96004841fb520ef1cbf4c47bcdcb6afaff993b4dfb24eb84d240c841262a905f0bb0d14d9f9a3c52217dcb4b854ce044eeaa2ec0904b91b21558310557bb0f70b022400b9d3653141ad4377b29d695f11da8c4377fdd11cd573ec35ecaa4a3157a73d8bfd510123c23e87e82417d777402ddb350a00d6a0966ac254903aa7e3dd3d09ce484ae61f8ac7fef633752120741d248dbed6e4b84e107e2749691a972faceb4ed13a9c9821198431b7d03394cbdb1bc0721ec76cd6a2d8a65694a375c49eeaabcdf98a36d333bf463d24c6c96ceaab0b1ea9cdd083579c1ce1c35512b7ed5744c06bf40d3e74b8aa2e2e4db9f21c70875a93bdea571dace915efa7ca78afd930cdafb0664b079a5cf28345cb47d8bdca2bd09014c15a5650ad2a36ddf99a8182e254b1b1d4fea7f93250fd19b20a88c705eb49a56346c7da8bcd1fae4c6b92633e64bdea6eac4023223b623e7d3a15a9df3a36124b286932e09cb0146d4ccfe908449c81ab5e34209966abd1d0040399051014e49bb655a8be37425ec050b81ff2b982568aa4f36bca25420b64439dab7797622f464f6672a5063751cf17b218f32ef62bf9ae309e428a7227d43a160b75fd2659c846e221447da9594d4af01bfa15f4be357bc009ad9e23c585a9fa7186d8c6be0d254b0e9525f8b05d847b12cfe7af27b7f0a9689109c6393041886ee2108a16876cd7e638dcdbb39daefc3525d10145aeb8694a57c0d7bf5f98fd59d059ca110db63dd2ff650a4ea2d226a4940d7d137effd55a35f5a4bd021b90a47caeb64b0257229802d8f10be0ca5d9a0553c15ecb5c0007c6a5776942deeb3e379bd343cc28503a34aa664aa94a30dd6cf813434cc5f640c698a43f7c4fe89b6bd3e016636d60c5f34cce4d4ebeafbe5cba472c3868e1c468eed995938d3b297bd173bc449b4efc770d997bfaf3a1b81e57915f4594ffa2b84eb4efea93aad54965b5048cf3e4bb9fc28c930f3e4e454f59bec6335c002d69797f111f874cfca8aad4a2a8c81445a40e3d234591001a2d34b9f2d8004c6dda368532b73aa91411238b584fb46ef3c16a65e27d684a2caef932e6aaaa8b64bf9c26d3b366856fcec20d9793a8e8a651337bd228074f448ab246bea463b67e18ff2a1235c0bed14180d30938c1047d3d0b4cdf1064b1c188a081a3ba2f3313ea83214169b64662de94fec2e8510f41d597371263ce93d7ca963bc5de520c0df127cb818c127f208b452f7c92de57a9d2e950803a309c99a0679d37d20c9d26d2f6bc65632ed27c5f0c64269c0104b91ef4b1432c927d894a5891e34e98c544afa4fb24ce951bad20c337a133ccc3af8669806beec057f49bbdb01dc088f6efbd9f48d1c75cdc5c8d684f0f7cac3f0ae3e4642286fd34d22b28b602a8ef90d172f9bb1f1814fb172b6701664b4e28cc6894877bc406d0ce786a704cd21bb9a23ea06968c405f075abbc0ca7f8cfe1da7e0c536dd55e5019fe2eca6f3a056d6446406753ccc9235e02cc4392754bb5936fe436c401fbda039d4e824de9e7abb73b6a9e1704557418c10c67812e5168f17a6ba59093de4da633de83f6c0f7bbbc80661489f05756c9028559a7acc0d03340894fa8586a9027b8aeddc99a70f2fbd64a16c7826c5902e0d9c4855ab72f22f6f6703d0bfc81388aafdd86890947d89b80fcc1d74773340ae18188845d9cc7e6936484986d989ed0a2817565280c7714ec17b67786ce1864e67baf8a47af30c84680f1f08c3860280d581385b8f931546ad7dfcfece08ad65d7e304ec162cfd1e41238a2c952fa92ee01a3fa5df724999c7404dbf26c604ed4c4114494badeed0bb590ec0460a12ef5c120093fae21a45f935aaee0672a3d35c5194f70247d90a9422214ea33e7b3bc921dbe860157dc39bedd19695db8fe129026dc21b27f2c07bf64e7a54e9682a8704f9c2963a5cf9825c542c685d3b0a4845118aa098d481166c19a19021b249c3e9fa553c7ae1843d49be208062b34171e1543c17c01c4a719b38eedfa74f7bf9d6eab5d58afba9984fa874ed7429599aae00f64be14bb5bdc9f4ab10c0f5828af649a9d29d00a22970ef4ed6b61a4739ba4f9ef5d01446115b067f2f8c0e42f1a301153842a5c8c51b2010e0ee99af43cbfc8ef086c6c406ce689c37e5a04fd260b316aa147006a9b0dfed22bfd61ea0149c8c51022872951245403649262680cf9ef0ef388e1d5fb9d8c3d8760967e881c03c8f20cb616da0ccc9db642502021ac8200f8ed0f4167e4d1147471cea15ecc85a8bb63abae94985a9b9cde8fda4a7cacfa373403d559f47045920f55bd341d3ab2b4858fe8ba8347afe10085a35de2a37bc21171938f59555254cccf1a52ff2b15040dca6cb8b924046f9a7b6ce015aa37295891ada34812e34265ed209a6631a5926ebfe03242249c5bcd2187e0a43a4e878510324add211582a1262cdec20b7ec47fd27e91892212f912f070884fc4ed4060dbe5c2a9caf3d93ea97bd10d5d986d0c8bc3ad8d2a1350858d79c1482c8949a9f9cebd8311381ff587b6d12dae89ddec17a7173e4a5cd3479280e9fc90072f23c78db2637207d0a17d1ad428e08d43603428b88004699c5741c120936981d7194dc44ab940a49cb9719457687bf123d4f127cec756d7c8184fe0834629cd7d7c646ae38d75d178ee7e854961ef3e39d3453bc00de454c6334fef88259f31e51903fb75430d0d7a8c1610dc9c239dc6d80e7f731b333c17f9104accec3185e845ec3acdc399cffc714de593b9a38010bd4ece5703b4077695258234f67beffd917ae9b3c0a2ebc5d59d6fda2cfb18a6979b8b5dd4407e2f2f52a784375d76d01ad86909bd46fdef81931746b215c6f5f0423c3c4c4c5a66a0186a28ab3ed4a25d89aff0598578d9dfb9fac4b2d2f38bd49b08302b39a651d9f73a6068126dc22ebbbd1f3544e62e78cd4549254ebd0941c76c38e26070ad090b96c2e9edbff3993ad30050829aef8cb05b3f4c89d1b50fa0e7884ea661882b423239a94f121cda7d25ea2073441fed0edb9be9761b185e973c7c95b034298b20299b4583b36fbc4d02c967ac7bbfdad1c7776eddae754b1574b08183b952ac95e078bf079ec30b5ba754e848bb5fb0f49f53253d57380454c4c323129c4adbe2bf8b388298ecc0b2485935b1d8584258e45291415437d91417265ccce5e3579d083d3de27c9ec657e515829092189856c191203b2217598b4e1880f0579121ca8b4e1bec74ba8cc8b65ed8a7534b34d557aeb66ccc1532a28d9b3039d98cf116a6507ded46851a5ca5c7478b1f7766c141319dcc463acde57601956d176c10d2b88e12457dbba54e09e4be40df9d5a2048da666b70fe80502627a67f5a177ac8436be9c72571e0d22300041d7a4acee6547bebfd8a81b4c69b26fd4a660d4b83eaa6040390e25af09f4c283a9c8b46f1da063e6597f1cf3814f68ca1444d446960e7fb136adf1e8e36193d545c0d6870e5ea7425c84928fa6e3373ed59ebab594de6624c88e6815a8af774786de72ab04b8aff19538fb437be742751503b2089fc19a814f87565fce0ea89b4465d609adda4972871feda51f088f8d2793f3f520fbffe8b332f8f021a022b18108ab92b48a1258c7630520e6a3e9b17d60a4eb62c0b4fdcd235a7360f1e20ccdb8340e9b7d4602802639d9ef1acd930aa7a727cbac2c76632a65449b144f9da6259525d5367643c1bffa1aacdab684fa16494c9886036434ccbbfcc6fb933a7deeb0ebd24173b9b2bc2dc1d2a0805bac5aa6a0769fc70c03e66d90e3a60cbe66e51e32963d1a0bf2fe272e63d677855bddf681cea6aa4d2be03c88db26a78e707da5163b1ee0e2ab1fca5459dcc301d0873188f3163d15933a5a4c98aff0b174c7971187ed6559e56ae018c85e50e4ea960c5138bde668968e82a28e2aba778d63496642e46ce1009014c90a27328594a8474d728cd779d7819f53e44e86e8b0228c96fed5010d119789d7c0c58473636e61fb5cc3b3671e49d6dbd86e5eef33e48a521c4fa71cb08b9a86865a793edfd23b1124a6b29a23d151db10b912c0a668b76ae5c6ba6278034c37d11e404029e999929300a9a6339aff24740de8b74166d42eb4ac00b12f1c247ce3d40118a90f3424aba4a39f280a7e7e7b03d01e2e3f4022099a8b2ab90cfb5c823df5c1613df373f3ee529a666a8c185ecf61b8b7c78120aea60a5cc4e923dd89a6304e6c58dd82f5c4b6505940256bb5430406f93a4d54b668341f2ce46feb6ea33675bdb30413daca53c43397064e41b7e967d2bcf56afd892e20a387063e2e86cc2989a009a923d347a85ab2a72235d26c8fe54f8fcc9771f189da1833b73b6d384e56d6cac201f293c4ecf27b7f170bd3bbf5192891955baa51549df4f3cb5a078b3d33ea38094311a7982cb3caebf8e90a77d3259c8e2c52811b40fedceafe5cb5f8546ace2b78d84d215345ce9c96f0f0b326939be42fe366463566b294cd95633f1a24ac5ef2f7a774a03424f74c7ff41937080a9ca1902cb73afeab0e318cc4d755d38fe206a2772caed2b9aaf45765a196af3c8de13313587619adcbd8307a2fa323ac39fe72bb1e458b2d989a24eb156582a27cf95a34686a4114adc9d80cfb0a8b45cd7b19ac2935200b85901119d38b927368ccc00bb7e249cd0aa6382f3e9f4f95578b0cc46b1145be48a1a912dc48beb1196cb64e71f5519cc56d6c79c6212a9bb4c7bc2ec586e97681a1c9711aee96ef7c63b13f08469546f7d7edb6dd2cb5fc6ca153bf5fa467dcc1412abd4adbe5ab43733bcb3ab0acee31d9405c2dc2783ad6798eac6ff53c7c7b3d050be1e66ef3d0b6f1293cb74aa0f7a3ff255188f5913541c5c4beda707f2181994445aef540e50f5cd193b3b8a75188651647c0429b3849d937f42d8b8dc9990f2b61492ac036d23fad3d08be072bb6a5fe2193acf6cfb1efef8b897c81c6ab9b9ea713526ec98e9268c947077c0cc7d43c9fe7c9279e33c3f615fdc1bf6474134909a407f9fedc9a1475203b9a17bcda71d7ad94102901a92b12214d13ed3df2c9efb9546dc5f9d8e90c80494fdaaa43efd76c6e0fa6a9fe135f79ef62a4b429cbbd599f44e26523e699c1506ca3ab66122807f889afc60c7cc58045b2368df02e0f981b5bf17e580c28a77e80724fc500b24738a356e7b04d5a384f7fb24616a4fd61d4b0b51b815183ae82d228d350a0125eeacdf8cd30225eff8dae80ea1dbc624f45879a905258caae84a5980e6ec9a01e0cf940a6b23816e810ab7bb3be2e5811d8fe7f70e1359666953560697888300bd1e53a0ff360486733c9ed7e0f838399533277f9a0ddaa13a22042240d5ea71d4932344fc1ede82b6c0cf0d4bc2166f10a3a425b6df20d32616312fa3ffc437b9516157c49bd06d79fb8654f5b1667dcad3485154def4676fa3f38b464a327b4da7f48775bdb52bb9dcf7547965fc725392b09e0a3ff62f675a6b63c9767f0d039e4ca0c598c1efe53915f16c1444f2cab686117481d99f034ddcf367303bd98d1a957343904eb5b3659462f837e3c1030e0984d6a39138df5545f12288a5f91c9f1acf19ec72c14aeae8f13b689e6e180ab5eef8ee7c98fd5a24ed424ed19745c53349d3c877582c851a5575856d8c79b4077ce3e2be11c4a5281d57197f2b0a52ef87f64657253bbc366c0244862dde55ce4d751a05f5bc22553e9dc47776d453304bcc77d6c84bd229277e3dd1afa4244bcaa13c2583c83761781d9f9bc433d733465d7e5af99cc6bacfdd51acc6d5a8e8e1d8e7c3c77f1708ed9419ce50401537163bd678bdea89fb6caa953bda99bd784948a8f1497900674593f35c48a4b38634f6f210eb33af7895cff75b77b0024279cca825c69a237c96e99c431b52eac99732ebbdd110af30f3515d373e479489a0b043692f07a27a6e41f9a82e38fb04c99b13018ca4bdb0395c40d87235fc0fbf966c1ff74efd3d01c22daa816eac6872822156588bd36cc9914cb472cc4c4394004edc2383b696e2f6c8106426a30cc3ae46f45693708d35e9bb9879afd692aae70e0b08fcb954da3debf74cc392027a5593aecbd93c55ad9b73c4e5540eb47bece2d4fc4ade9086c07a90cd1e8914b011ac6fe7fad1cf6db74f1567a93d83c339484aa129ee7188f733f51a595f489928b26dcdf91cb8c149377c01880e7cb9300785e48a91f798151b21b5429a2f47af3a434af79b59445b9ae0d641c0decb5c634e1bd0495c59912e45d9e7e91c592f5d6b4badae8a244522076fc151a9e2b125d64ec6f52304ac69db4c6cee45fb453ca9066ab5e3bf87207881999a224eda933fd3feb2577ac03eed8ce2c9dd083ea3138129c62ef2add2d33787450973b62811c1f153346207f213d19fce53f02a3cd7788d495183983c9745cac3ea33f5e84f6fc3dd3ea9e23d088965ededd10b6a7079449e378ccfb1c648076c71bcd17e6d7282a99d5a1b4dfb721e20aa1f455a427e296cacf73b3f132148f3ac0437373c7252eee5d4301ea9a4a76043894d9840ee93e493ce207c0e1ed69e19fc9e3b4139f58342b7a9c209393d67a690f46db948754f51b180f8e5a61459e36251774eab6bb7c252b0c25d57e0add84aebc4288f42a40e9b83fd563dca4605879ea841095e7dc5eae72841b693a8785458097d210487d3c8e52467a0e5350ba8e59ace5582925e99e3bd37ffef517e8918a77605fab639fde6812a1608a1a6a3e7855a459ee512dd216879828800fd190a918918357b8f74927c6c76074d886a30261b8872b476305d9f8c284e29670905036aa7ae08f25cb3b1b842cc5ab449fdab083084ab0bf344b53869caee0b0b608470aeb34322b97ffdaed5bc80439eee29bd3786938f079d2b56658e50b72215621462135861cdcffb62ffcca141dd9d9cc708aef81d64ddcaedd516349fff422df640590a15834678982b98fda6e4cb0bc4a4bd2522cd770bc1a042f9d8b66ef02c9f94f9fe3bb8f62aeafc1197b2813b694a942c127663051e41488918abc180add07728c835317db35c3d423fddcc39ccaaf648ddf350a4c5dad0b5d49be481ccbc3deb61c8048144dcd487a12c6b7c5e1239f4e180e9ed850c297628d04c07a5f4680351e21bb7e66351b985d2b6ebe36d65bf9093214441f92a0a029af5d41b506ee25958385e7039b2e46d515a9712ab42c78efd646316d3e5848c2b1c95b02ccb1bc7b09fd80965da5d88021caa496eb16a280ff85753a3407b7ca0dfe7e960332d6688b8b559692c1ccc5d04cb0dc32bbef8c692a1afd678ac15be540e2f2a1cb7c27bc8ae0962ddd84cd8ff32f1b0a5115012e03f69a21ffe04042d79a9d11430451e6a9cefa951f11f8660b81a624c59431bb9cc0775da6602ef9ab7e9cc0b56d5bf60841f3796d3e54fd7a0042d8c467e8a9cd58096434abf21972480a8ce56873dc22e211be59bc7760428510c14c75190331e1ce329131177a854b86ecfda04a1783b2b7d031d0ea5b1266871d37248dc967ae7c00deb0943c88f86ca5deaba619d8265ae3b444af1fa836b39b8eab2e600af4e0602c638ab18b9e9cd9111831a709e19e0947942cd3f44945c8913c6f31817badbeefe39ac8ba1770078833b309a505d35d44f6d9de70beb71e30960cabc11ea6d38eae1f99ec37d40fc44f4bf2a11e46bb1a8f10bf596e53bbbc9f068656bb22d4b29c323e99a57dcc0eb9d6f4db2494ea8f6fb44ffd2c292317f9c402d73cd48274b2d987417d22523ef4d02765f3a960b331def1bfde272414d0f27952b1456446e0ed3f9fdae4da13939d5c00a5fbc6b11f15737b53761b809635a24dd69e8118b1fc9e0a0afcca26def286848448a2c87b65839ae7f76e12eeb8103bd7db7c09d9557acffc315fe04b114b13994fceed3d3618741f15ff5f184ca679d8c65282393008fc6e2cb1e993572c46b0c72cb2bb4222ed6571560f127ed5fbfb8ee16f8fae5733457524cc6aa253511b95be6f3ae58d19916088bc748c2eb0d60471d88d79fa6a0d5377231e54d1ae9b792e57f7bda4b5b556189cd49a30294c9ddc807ef478dd68bb5e138a4a15df0b285c217572738af98bade6fca71f597265e9d3e0a30d804a635d34bfeaf52789945b7da395a0241b729d97d3a6feef7560f249b4eb3ba9823e8716035cd1a59249290731a8942d82269e4dc209efa5569620d96c0d46891a419360222cb545260bfd611943101d2d98d11d0dd8a82f63fdbae6f22e456783af780b50ce5e160513e4eee907ee08549c25559b2da4784668c3e48e0f37cc506b8c33fb64818c78fdc9066840111bf0d64667ee2cb61d6636720e71be1a03807d1e7f101eb0aba600d27a1355fb1b7c4058fa0cc6e8bae1cf41f95f430846f769debc9e8bac076a6acad1bb1ce81897d8fedde832391b08f9a6fddb09e6301240afc32eafa79c8bce1a6363e37e53868041571c6bed1880f483f52468532205b0f8085ec6144e40e337700a59f8e00fd482b5b3937c8b57bb8721adb0fc77ff38dd043d2237f175a6de9cd065be9b8c091eecddf8ba489a4834af80dba0b5b5e9734722ce39aede0910408ae2c2161fab7bf8ba2fa4662f510bc5ad6e3ae2423746fb6434177153da813aa1f47d401999b911f86d0641e6d63fdf883492319760c2d984ce9284e6c87c62ec5c750df807c791170bf05e49e95df9f574a0aa5b6d4169fdb5097e506cc6461ff40ce88ed9e98e38efadf6ce5746f05a2d9a5a9198b6c26f6309ec7e70428dc0a9582f9082f617525f514a76ca038d24be1b734c2b005928abb5f24980acb33fbcb9315177d861ed9e7355bba9e83d11eac444524988d0b562c9cdc48984775ca97abf6f5ccef4c99d0b06fd758544b7399ee93f5e3b031d7144fbee946ae2ece0036054d0c4c31527858e9b8a96d0b3fb127bdb4c789dc6b0c385d7b5c8066705c1ad516ba72b3124295eab1be7a24e3317bcdee2f3364f4fa80572234c488414597194aa56930e7b532bce2de67a8c8434b6c8bd25dbd48056eaf21db3dada31dc0ea2a5eb781430c64e17635be84a3a92ded7681f067f74896e2cdd442427d4b2d0cd9a5002ed80ba31edd1b2752a701d7a4e8700a13b5d06a7606461a660869e49076b9668de654821b22cd01bf07332b16e68c373574a6bb01f8e1fcda1d6232efaad605da28645f9aa55a7ed0fb1a9a71548a6dfce6704ef798a1304da320b8110e853d0b51d07d6e7021eaf47cc4d729cd17c35f2f1eaa53818f0a2575da9c11d427320d9bfa9d3311834df27e57b31011b81dd14e7ef105fdbc5639ef206513759759bcf25ff07d02918d08ac91fe99580c31df5835207cda7515392b91b0e121646f4f913b1438fc2f5ae42c0a20cd1f3c9a4ceaa31e5dc847e29eb8ccabfe3aedc68e2986e3db00e6860baac30c31ae1c84b4e199df3e5986e3ddad80f67c6079736f3ba793e31066f640340736e3452da00bc1fa980f03025efc4ed38c6a90df510aa36ac05775b862ec2b6aab23385c3ca44909c6431ba18d1d4bf03b9492f2727072a9f1bc1591c60fec87e1bdc712a4c7fa3e250d80757f51d050057a165e4acf4a2706d0b156758bda84655bc986c3b55c06f1949ab9c78d7047cfee1c3205b908071f9779d0197abac43c8697978238430de3ea04806b6477a9fbea4f4f376e2fb44898960a34eddec948bd6c39725151cc0d3376c0cf742a9a21b90fe79c4417249b0eb245177ef5da3cf522d2d7018f64f4a032b3d7fd5d1a7bbcff5fad8620838cfe242ae9c23c9c7a952815b9bc13ec1fec0ebb94e2e56599c6dc09f5b2f76b90975db8b8555769fc4d100f601b792e67c1cfbdc46e3d328ceeaabbe483e0687288cce5d37d745eafd12854b20b679da6b0b559041ccc19b79291d082245d01fde489e4a9170b4e8de578ef5d9e5057bbe5e82a7c01d7d1643c8f447a2fe33b6ea77f581658fe761063675166369c6b9a6280e05ca25f9cbe5f1b79db365562c668e1342b1fbbe645efa265741a8afb09fd5c7575dc2b5e806eea8c42101bb6bbdb7f48cd037bea613ce3b5460a55ed75386682ed927788f93f139ebf63883829e92c7962edcd0eeffe31ae5884be8d734aec9b3c21e69e550f7b69f6c90cbc9c00a180989eda7736ed02dcda33b3b8e604548ea149afd548fa2edee8fdaaa421439d211a9f56484fb00956c40903522f41a0633d74b92d90bb43c439b93a8e9ce09922f5784c358b51d5b69a0b95bde388013b4fc0e1669326d05b5db120c2434168e1c0b9a6b6443566b4c6e110c1cbdd10884f27ec922643cd7097e92b1626235fdaedb54d02c5a1a46a58203bd909a7a7303b847f851ae4bedd55646c5762edbedf863272f62187c60600b103b80ab212ec94775ad568cbe36b4e83efb15fda69a2a8c84df56f5e9332511ef2ef9a7557e0d54c34c19f8cdda228d6871356aec5beba2463b0140d1d296b38fd3f3ac9834756c0777c1cb4fd5e4432e76b186d7fd5b57b374e9a7ccc532b78143abc481733307d43ccde3538e5e3ad452e230eaaa2587569d71775a1275409bdf534db91a05a1ea6f25cc4e1e7058bc4cf2d6077c1b275dd04126487904950cf8b453e47075ebed07ce8f0e454322e88b5c2474be50e278494a57fe7120e699896493d76dfee62f0803cee783bf3cadaa7768ea2b3c662e547f2b47b8763ada6f5bab5d248aa9199dfe8fbc6c5f3923dd500d36585b515419c7ce252af24bce390c2bf37c5decf51295357f5e02e0cc55aba78fa788719abc2316cde8ecc6a7c4f20d143fee316912e5c4e77d2733638cc47c8c9958a48dff6a8e7908a27c4b8d9d935a9bfe26f13649d21f45839b2248fa04a8df38d5333796d4fe0bda44aec47e2ec644db3190285e5f816547ca2b1f056dfba575524c91b69388acb53660dd74a3daa511f0d79e62034dd70ef289129e3d34744c7a011112327bf648c81b83081c528505ebcc97c279da11286a037873b1009e69984a427034f809990b25ad20c080e7fe608baa6ccd4484ee883be7d9d087417555be1dc66fee89a972824e4f39f791dfedffbf61c34745ffbb246762ecad8d72042881d5c9a6a4d240bc9b2efb61dddd6d52b426aea923ee6d5308f7687dba6fbb450a76d755314fc6e290f1e2d45bc9b11b8d21d827838b3df9ec2e0a184f8403457a59e09e329ae3f10bcec8417adfa15b4c72601755a39dc97347aca4bc2a33230d4d7399811babbf03917237722d94b9e54465a433bcb38c12ad96b10b390ec72fd314ea507aff3030bcd04cdb3e80638f07ead15afabd0c8b027c9a70eb249b8e09e24a5f9f0128f4beab21f206bdfbf2a854707c5017cec1177bb5513513bbbaa9ce6e280f06bc5aa0653711791860da9fef7a0d958f6d46f306f7819ef0603b84c0c8f4fc11eff05c8907bb6c7d5a996c1ba6f18a0998183472b7e9bce9c2644f36359964e6953ae3ea9418322d9425de55a79bbca6086952dbbce225e4b3b700cefe5ae7ec0aabc722ff7d0afbaba57a1c5d151b3d8a614cae1277864b097e198a2102c3a3886baa6a7ccd0060fdc9e20634120e6ef281deccbbef72f6e84fae7b645e6a4161a94ac8850cb44a719b693fe51eb1b258217808d7afb82f5624014016399c1e1b2806461923b7c985b9b19e1f443c7770121f52f6c0276f04075c2c76d7026288c7396822b649875e73920e7ba917106a9231813c0cd2de562ec5c6372cc0df074b0bb93c1e6d4dec1e4d2edbb2b70cefe3989cd3c28ef5e946671e55194a85fc6f42eb2679fa65e8882ba165a097324891e2828503f7679993482d30e03ac846ea941cb14cb947dea9d26a404216cd8b9062a6f9247dda81c29c65a38429f914dc87d984e193a990f452f98d0d2fad60aca9166d3d318c014ada66942bb1cf0f95fc1332be4e80ffcf5241374abff8d02a7a89a892c66747319495c6a799814666b3b3d67b99aa01df7ddf96c7e52b6f2b5399ec18c92a64f7960070aba767f5f481b877a9c84483dbe0098a618899a8582615ade3625c9d394b5395135fafae106d93ac98d300e34449179badc3030abac441c466f2be41037c4ef19e0e1a45df830b69aa790f04ced10725f14abe6fa532f6a44afa2c3561c614c935b0e936f455139f8c6f7747fc6c2cbbf1e95f741c524d8832896821ec4c1e0a0b42a05e51a349f312144b002bd4a6d6eb2cc0334dad3e2945dd84c3d9193228baa05290dbecce2df089dba7343c304e1cdf5ea458ee6b87b02598382f32a14fa48453b129e148475cd11e0e7ba9421cd351f6366ae5ff83c4214c8cb70140359d1c4cd043be177557992cad3dedf023d7ebf6e3c0a7191b963bc7c9fbc4c9855a0c7ff834046075f5fc9202d52afaa4b8a8f1267de70b9d909bb3b83c60f0f324cac51d5136d89e10cc5f9b14c465cb1bd596e75f59074a352d997abd393cc1095d0fdbfdde2703dc1e1d7a6928a9ee21d507aa377b4c18c8a0ae02e65c2f53a122f92d50b86d01c1768a4a4c22bfd3a302f89e10315d29495fb88c645a369f4c3ad9ba4aa893c49c368bcecef5fd444ee828d5ea13ba140d8b72e09b70eb2a755f95383431ba99b13cb9d1093b07af21801f70563a953e2a98f97295a27b1eeb11a428c27eda7c7f94a01d90100e1ee7cf0befe4e5327cb251cc46eafc75b84ad73c041be940f8b12c0f39f23313099d118c5817ec4cf7802421454bba974cbd0a7dd55128567fb39e0df5ec7e0ac5b10246de73ccd15d9e24d2758a6d247e64359dd437284ddd2ead12203190a2a2f5864b22280eded2612eeae453f086d88288a90d481260b260c919e20b44404436f94bf2b3a249d48982b2a1948e7510ea79b18e4283cfe4cd9dd6199ec320354c30b5221692f9f258755bf2a3b4fc7bc123237455db418c4e13c25114b7ad9e2ca75aa67743c808e4fc3037ada71171394b0fbae5b6f6b09248cf234f791a22c8bdd7161a7a23ba55213879675c526d016e6366ce4bd2374e41f41a75599dfd63378a6447e0ad7309ad43c731b0870ee3c317ec1a9023c0ef6a8c9020639037c8de14a217ae2a76a0417b883906b344bf650ba8019148ebb9c755bf1b25d983897fab8990034c4ec0b707359d1ad32c5265fc641f66a5f9eb6f47c53b48bd3d69d2dc005a14a983dc1d95ed44a6249544a76d3c610d3ca807076b421fc8895bc6a00598240f56f09358b6c05eed2d8ebcd443e13b2ed46d718bbf68ca0650a1c7298bc06c595536b0eaca2f9ba7b625eb226b9c310d22b68279fd74101f42d0d9f3e86ccc8213bcd6ca0bc26d4082991780bf1424653bd3d752dd76f2c6f70e7b03c3370bbe6289870ccce4ed6cba6fecc84a154c884bbeb8bb03d7e07b0fd9ed968b6b63abc4e657a16d50c440248999dee7005a123388eb7b1acd34580e8bce658938044eece108f0e48aca48837db8d57d99ac37c10e3575362c516710b8a72636da0c9be39e04a090a96ecdf969a990208254b2b713c99f05662a568e0f9a6c41d396dcd03816993464b752392ffa2c558f45bb178f9e98e2f330d49c3cc1c9827f74c4afc280d1b7d2a49d0ae68c5ccf75751ae589830dba6b91a194d6d9edec3148333569736cb689b408ae515cc9c73e4f2c84bdb2a9d6797edf30cd53bab028449df7e827ceae72fb7bda5c092a3d9eeb0d9255380c27e29ea448cb46486f4e0a16fd893bb72481b894cd0acf2c70270fb113e7f775977821fcb9cb349c575985f2c732c7a9b2c6b9b37b2a40912cae67a16d54984f489c8cbd6ed9f6ab69d3c3460ab04fc24c17946b4c643de38bbf2da35f0f13905083a42a2ad0891945926fc63b500896b1c9dab4a757022deb4518a6beedadc26f5ac461beee0fdecf0271b1e5bd8628342e01e0e6845025116e62ac63d1442f91d705b5c941dc7ee5cf416e8fc90b2e205a4c84a6248bb985386bf262d85a9c86a16c5346dd5671ab86f18f2934c814158c5e71074d8b0c1dffc0e9f32813d3bff1e2d9b8dff67cba134c17ebe95477374824dcc64879d1c172184fc5019806b6f3a6ddb57c0dda5c16dfcc983e67c31de66232d61a357ca5d40e6ee70f9586f294ca1c7257172b230ae279238ed136a4efb2f6602e35f0fde22e8acc32ad1dc2ad2fa5b87ca26e2380bf0e808da0386fe85600c4a1564b2e8fcfa273d29fd928ca427516ffa325b2bc27092639939d46135b7180b196649ed4c279c238088967143d67bd83aa45efa1d3f2bac30a2acc24834aa1fc922e4b337d573383d56043978594f2a65a983de1bbd2b72264b49a12f78e19ffb9f0b1532684c4f341090fde19dc856b215975d3b9214d9038e994b1c41f938f629907db6de02302fa5bc7dbb5cc6cb946428eaf29e53bdbbabdfc1ee237588d95c51cba4cffc12b47444dd26458c7ce309b3f192a3e6b9a83e2b0e038eae0ab8f5a6b36ff538699436cd71de5c8410f022710d6f048ead6d7cd382b8548a7e8ceadf6adec1ff2e7dd334e9242ffa6ad8edc31ad2393596d392ddf0932f210318ac9f9e951217ccb55017022cd96eacddf9b6fc0360ce0d5ef23ca170d750238886a104c17c2a26565f56ecd8fa138a45638b2fa5ffa2bed4116fce78c71d0111738ef5a36bf7fa4bc8abe05ac1df73c5a1f741caad1265e5a844828e3dc8bf14bedf5a86937e6ebc6f0e6a1b911700dbe506d1579cf83bbc103c0b202294dc9914a245034c5696caf5ee686c953148f28a5769c327d5a6b18b4cf6a1ae6b17361da2327f3971ff4ec22faa8421d4856fb2c749f57dd12af2c8f03b31372cd96dd5485c3b151f2661ec9a7a009480118b90a452210cee68ea5fc8125b2cc9048985872d55dd399df8978ed95390cc39474cc7950bc61e8c7c8e328b688626aa9082b66065b02f23777fd560c214db50bb2235996002310d154b9c2a8c663206646d5362e153b25dd8fb95d0fe45b3dd077353bf0329ec392e0d1158c534247c89f19e62bf1c437a09cdab330fd342288e08f0e81890c9f5a48980248f879eead40f5f2e35a0473041a606b895171c72187309992314fdcc4a8257d146a1dffa882946afde5a090b5ce2bd5749652f8dcf8cc0854822270a064782b6713331dbad3dc7ef53bdb28ace03f39e8419247f97b0bf0dcb8d40b54dad6e9a4e46d832431d7569bdb999bc39c199c430e5aa1122f9e23865962ae0fa4a890f22a8fd0d15a3245e1d6f261b53b5c6c49a41f586d31f84e2dc43b0a68af85076012e8db8f09ffae92eea600c2848b6826a6684af2bbe5f1c1e25e40e712b458a486f0020649ea724d7f6003d168a550b7e3e7e6f55ae446075edb6e2d98d17f1056516ff7562837c9b08349484c78403a21beeea1ad17bf074f19d842bb30bd5df8b7a277b89ffe6d45a4a91a193fa5cc8b377622131e6aaa50d69561cc3fc7c7a81f17883c5bfac211f4581207d11716cf6c3cdd3d0622842cc3c031e5adc833f63f1e76e9786ed3c7bd0e560e540bc22c82a790abfd95d3189fc2795f9daa3c4034f38a72b7c55b256d8faa2d576452f70dd00c4b14d80b8002df42ce1d16afe9c17132cb3eaaf3a4f3c64b9bbce86044c558cc90e6d2900020f3ced4a9aa3c2108b440139e9ceb266886730b6e6e679f84cbe1dcdef43b1839856503434ccbb96b97a3af41ef9de68acc5e74b7d8ab4ce6845343c934278834cc5ed53d93cf70b9cf170d905b2ddfc3d3cb6460afda382eedd0259a53e879a18b84dc3d3145c73c9603dfb0dd3e7a52e084d684c25c6473b63e42e45ae00d2c86c2c35d4b08a5fa9dffc775026e28c4453a906e2cbcc6f6ccc277fc53f14cbdc35573e49aff430b2d94ab8c8c8842ff469b86e1d3c0514f30cc76b8d51776f951b19740217ee783c198fac20c23e860b6ddc2c2d52659396f156b524c10d84cc28f2febcdb0d791223889b7187208a2a9f61393c81fe59851fa1906c433bf6b28e47d265e329bea56afc84fc3655eac0834efe38b95f658de9671b59b89d1537b87b4be74db0606b7e15f6f862b13dfb7b2d4ff07490cc9251c24fc74fd5fdd147aff5763ba18290455f1c53aae545b985ef9224009263b67931a46ad29a19cde6e1d26ca597d90cc404dbe945c3846c3f6140a05690d785a143ad3421a72c696639e7025cbc08b1414d25bede59b3812c89b1ef050869431d04ade46bf2ea84aec990ff3fca7193c8a11f70e19d884df6683e671dabeeb03907395a884df15728136ca3a633b116faeba2da9ff76888aebd740bdde34fb83fcf2417c48a9a672b8070a0e3a69ab580cf79266f9dad01a355d2386f6073ae28dc290710028ea58d7612ee654b72d066a6a373427b75b70984ece15c8c9cff8193d73cc3f0e24926d92ac3bd149ab952a4945922feecc20a8b4caa9b1a161a35debec9f74eae4f5d1baac741b4ab6e9cf79e21217839f20912ef5c68a956c5bf22b30a6f756cae7d690aa9371e299b656a9cb1397e8c0282db227d89b009359a2e73fcc4198139766bdd73472a95f4d0b4885cbda955754a0378e947c8f8fda24956dc18d4f0961674fdcbd6c8bff46683369409c401351789e7008d6a304c374bf2080fcb5f5f3880283950b433fdb2bdd0ebfcbdcb7a117fdef31ddccc9b136d6276292f1c14110edbbff022092aee0f0f97af6e0b0304f688254f12d4b7ed22d0dcdc3aad77c70d0bb6eec952c5cd906cf3eb5ef3cea1f6e49fe163a2fe1f85a848197ba83ba8f1fa8331c139eb8719206f626cd1bea6028f8e33d44b6497aa2baeed1db11add49aece510b8c718b5414835fe3c7d89bade5c36272fb5b3b83211ebb945a292cfb5a08f42b34da4cfb6e8c342d36774d7f615b0f98ccd89efb1d510d8cff6e9f3d6f7f2bbc7d3656c641c39a317dc8709793728474576bf71929b98ccdc5f62cb9989a5109cd30c2e9294bfa3abbca68aea01160150abf395747a599aac4320199afd92645da9a569910761a8e9eac5f45d422b2975735cd59393cf64b18a7566bbd6ee9c075a336d8aa8f1431dfd1d1bccdcfe65a66f3b16d2ac0c96ca92ba369e4eef276a9ea3e5d5feb14010e1ff7d6e0f0f988ad1d44399c0d974c7ab02933c002031b4938891d7a965dd9295d29a1485e361aa5aa086648b0a7972237a10a7e39b8f219a420958e9c35ae0f342bd20f0772867e51503d0203eb08978bae5efae47835df54678359b0fcab5032848c34a09b6047252a2e015a0d32155898c3a513507eb55fe73d640a6602be2dbd8c270859b50db1890f49c1e749303f24440c3229488788a324d5dda9c9cea0b71d549f9f0781e989d3fda7822f1d55d67bb033f59269f12c7307b6caf0f98485d9794cec242974cfcc4c56fb495b7b3659c57a18a7977a2cd8230a9b4e03e76c2fa75741ca3fe871dbbd5f6b0bb65348f4950c5b3645f86b7c3a741347d11717b37be3f24a1e36bfea4d648bdf496ad07b15ee18e895b96d5e865b88fb86c37c72d66d0c8abaf521af5941a7407200a72b800ee6ab3418feeb8bd743544121470d383eff722824dfc83223bb18a885ea642e6393ad87bf25e2aabb16157c114ee3b82ce1e3dd37e49b0942a4110546055835acb414f86b7a2576a6054ac2caea7b8153cea0eaa58a9eb941e1b3af984613f07ba3974993525ff46f60680703f02056e2eaaa713c5c271a5de89bb4a37c4b97382585704679d083c092fe944e66c99200c7e6d73911f7bd78c03733eef9da7a322865235891fc358951dfc13a44961cbdfde8347911907b2c2fd89978b0ef91fbd86b228438d2057c90078899b67e08c81082776078e548c616841dd6af4c8353385131223cb4c426be1e74f17b6cb5238be3f08e566b26ea1888afcb26227f13e37ec74e541e730c82f494e5439ea1f3ad20f5a068884aec7c7d1209839f868f2b1ffaa4ad10f9f1f4523b6ccd676cc8764d4860636a6e743ac78ce4e1346c6af4e4b6e8e615bcd2e96ac16e523f166b9769ad829a5f6bdf532a8660b45bb900a82d88cc294a104162b0517133f3eed20c99603045756677a25902e578a42e583598104d45b57132a88765280a793ac85dd7f05c1155693a2a0349141713befa38a383f5a8841521092968308cc288bef71aba1c2d446796f4b309cde352649b7f03fab2289c2fe357f3542bad1bff407f904911448db88ea32a5c2ab0e136d085c79e02485fe3760c92d58cafb835ef157b1cf79e3bc3cbb31ad56c71bf4d6f4c0db84f858b2ebd1fbbd416c1ce804af8b192af97a76ba50157fc0016316a10e25682b005536e5c8d01911a37be9051e150ae552b069ab1ee0d2341949323c7e4c87468878e5c3504cdc5fb4f604d6701704da31c9d04b8f112af15173b6cd71a8ea7dedeafd9d22d18290d3bf6df3c3115ae4140283fe375206dd92e861ebe5626db2e3377cc236bd6609892f98ff9d4fa1de03f8513181b3a900d32778730b3a4ce8fc76cfa904db485a3b930b3a7bb11682d86797f31bd2449daf6b850b21d15901bca52a2b44e206954156e15eb95821460abee09721a070ce38998bf5fc2d690187c16aeb4ea4b4021103e4eb02aa45ce755b300e334e632f2da7f3d311957bf27df8cd83ace367c667dc187e9fcb53643f4a8289e5b4587db86d068ffd54d0e62d4015aa01a4c200d0c9e8b6849bd884aa7d84b0073e1a01f972138948b32e3d3521216defdd6def2da59452061207e7067b072208729f23087a8a17d4a1e0f7ee3af5e33230d33cddbc361c652a603b898885613dea447b138bf638d4c130bca439a7f9d6bf2fef69f39c17451ee0949efdf40e8a3a6c9620e8e56dbd7fdee7e7fdb3f843b4273d6fc698a617d383ef93473c56fd30e54d35be2a6faac589454f554c9effb6373defcb3ce2f1e7ccdbeb8280acd7cd5b6f6ee1f13e444f2bbebbc9de34cbf8bcd50eff3def8985e6a870f77d9feaa671cfe98580f3f442bc277ee617067b789f3fbc6fdee6cd7af71d04ade9a6ef9b15f379f6fb62bcf3580f147584d4c9c4e258c3735ee1e1cd55eb75745d359ef40c82d250a8a7c6d777f0528b1335a788f2627ad0cd21fe7d7fd6fb37bfcf4b6d1fdb551c1585caafefddb1f7ee333e74cf2134ce1ab081b78f0def1a65bd69e5ed634f7b0ec1639ec724fe8a3f423d3fbd981e4cefe2c3cf20c24f2fc25b5147e87d25ceeb308aa6b21039a2afe56d0524446e1a47cb1b11275a052f3a005ade8492a0e85073af87a22d165d002d6f4341e8db9090a66979236265488906b5e949dade5407befbb6499b7badb5d65af31672c324faf8f40c0fded21c4e6a2e2914757036bc8bb65e87cdc4b8cb4ec51d3513589b356f9ade35cde5dd81de35130882a00f358d3c5fd5ae8360ada67ad33f41d70f199acbd0e44db549967707576279d3b0f814b0dcca1c67da1c8e9e1c8fe9dfb96f1fbab9aed220a02175ee1474ddd68bdef43e54f3d40fd1dcb8ab9eda74706e3b0fda2d7af754bde78f4d5df4e077f8aecbdf7168371f5d5d9d5dd7e1e8cc3dcc5768587c0a3c651b40773837ba3dbf5608df349d1bf7f36be6f4dda71458ece2653d5dd6d51f339e15428f7aee654c0fb8cf0c72737fef4e220fee33f3ccbf6e1af3eee079d356a7c9b21ccbb03c4feb61fbcb9cc8a39e07ccdfc3cafdd6bcbfcce21ec37dc5fdc45de4322c43d1afd166991d48993fd43057a92d83c77c783ad6eb549911bbc6eead1175d61833aec6d318d6306d77a06f4ad7ee531dd068bf0b7ba9abc6d7f7f0527b336f1c9dbae429ddcb323dd8936e9c1a3e925422656e1e3465d07470829fdb6a2e043095caafaf848a63de0e3aa0d1fe7b2f784ec80d6dfa1672437b3f6d1f8a1af128451ea6f37879cf4efc116ad3bc42df7954f3167243833f65cf045ebccd323d489a52e60f94766710fabe866d96d90197afc4e685b8095db90965819da030206a8c0df626a4c27589c1b9a053224c49d4b24a181df40ce344cf6bc1304740fb5a3d4aeb7d36e6c067bbb4054f9d9ea2941c687a3aa7143bb5a439d6fed2d8147889376df763ce69bb7fdca508ded00b69cef4385a49d471bfdb8be0bdcd1b3cf7ce72e07d29fcbadb7be31e79b8537beadd529abd772f733c40cd653bc6a6c0f697cea36beb3581bc8bd2aac05560a9579436e7fccb47cd1ea8656c0a5c6f3f91d094120912bae08e3cc81de92b5fd89107bd52d702e782be1f4879807ac65c80f79026f4fdf4416ff31ee284be9f5753514a0e66ded3f64041eb68fb6bb3941b7c5663589d05b52c680aa49071c314a0bc9c692bb7c034b501b4ddad2e304abe40c9c1e40b098450d243942b428892e50a1766a8f2a48d1d0b068a3746149e97a32f4e8e90bc20690db4ace2e58af68eb9aeebbaaeeb6ea4b6d3659e9d5a2bfd1550d6aad0ce0e3d080c90396a38bf1cd19c189aca57708a1406bc71b6d05f9468aa63401a8a1d111ba20d17b441451b53b4f104cd29dd5bef951dd8811dd88112bc60e69134af2a2d51afe4555aa9ed46fbaf564a6fd6a91648df7bdbde16a4eb79eae5373919b8a4b1aa1971b9a2f2625a5661c348972d5514c58a8a8abe883181370ece0d4b0faba5d814ec79e1f6c287f74a0f4f45ef60388af7c61e3c5d38f2cc83a3d45e771eb0c48153dfdbe588de780d296b409975debc79eaefc11e1ccffde4e16e174eb18693e3329b2561ed2c7654abc594f0bcdaf48e98cdd6283243a2ebba3584745764a7eb90b8b65235d0900149a7da7a44d7753335c8d05ddeae14169648c7050a8f8b1521162e497c5c5450e2d203e6f20113971842959655d4a0d245098d2b5ab4c45420f680c4650724a490828992266158100551145ad6d86265b5a58c1a2e8c2317b8e5c96b0b0ac02ed98a59514b4a4c9e819586500a0b34d210d148c2828b19795ccc08bce5910e086ab0a006d1e90c24a411d24cef991632d3f772ce6aa9963158a05aaaf041cb114e7cd0e243135a78d01c4777a09284a4a549153003354fdaae269d81a44549c781c1e57db3909a7eac07cfab92144652174e10953c5a801bd6863112e0edd583947a94867fef1612629a53eb7dad73e239ebac32f4ac77cfe75a7edb17776dde3497a02d1d37a6b11f30ad1e57a71429d6daedd98c6339d07bef3d01a865153378e082b497df893e3c70b5fde50e76e1f5c2180ed8d29c7b020d219c1dd05b318603a67a87d30379650eeff48e5383a5f0dd5f7910d97ad006e9b9a58fbce9e9796ec6d9ab62cf0c5313100b81141aa413a476d29f117ba203ad8cb1e9e27b6ca68b47a150a8bc59790f61c11529d861b5facf8c5e7ddbf07b6cf8a5afbe635028142a871cbde93d367ad3455114f35ee5ed012088ac21854aa5fab671de51df295114c5ec5d4a6f3299fe23d3a6bc5179772d70d1820c1ae3ffcc68fc6dbbefb171bfc7797717bf4f2693e93e2e6dca56d266cff7d27b6c1fa5ff91e56d6b90d28513bdc34ca94bd32c256d7edbec7bb8db6c3f9de66efb89d1dc7f66b4ede7d3df6d3f2afdfd47a6bfdb7abedb4af7eec35989572e58cd44400bea8d1c58bce9a0e795e851b74e4a2b0d044f63c769f16a3c4ec98169fc3e5312758807c5fb7ddfada69fd5a56a1aa91647db85a7717671328d96d25e806dfd971a25bd295d3ca561b53e3e081326ccfdbee9dd5aefb453e2fb472b18f8fbfc40581678c370ecbfbcf1f77d0ef1851501150147be431140ef0a3f100b374531bf5e0ea83f1d02f4a63f40ee0c15a93f65598bd09b72900647ef25709c41e8cfc772d7a99dfd02a4e03798f74c83e7bebaf4c21ce3026f1a8de6a48efa3dce8dfb5a183e85c7f0aaf1349ac6f053d421666fdce0ebbd304fa92b0503bc798335a976800fc28401a3c1cb1da444d752ae99273c4f77efbb667798774d87ef2ea0a7103dbf4bd073fbe8b951d013a836e2d41b7a718c65c1e0bfcb31a43451ca97b22983fb59e61d8658c3f4f6b9a5716fb0434a13b56a584b73a69d344d739dbb7ad21caf9b595a90fbc62dc475451da55bb5a508082f2feb5d23d596e6c4b260122210a43e1ced384e1ddb62c605be00cd89c008414898d233715f5eb042d77777095dffaa43e87af08528bafb6ae4e9bad78e208d026fc759d08655d4a153411a4eb107d81a5ff6f265a5767d8a3a5e87c50e8aa11dbfd8480476237307ec36089a0e1e748df6ad71e69647fcccc191753be292f8a31ecc9eeaa5f13573d7a596c99682e28ffa0dde76b7f93593770469d45f3e5c57dd75d555ab97c6d4c1f1bbccd81db603748d2810ccdbf47dfad7a33e26efeedfe8d51b952de83b776facf19cb2e9cbf4e00b04413104c587a109a108862068f3c01bd6833ebab1de8e5fa9664b69f7f6de53a2a316fd92b8b9b0c4cd8515689496371748e0c20d23dc5c40a2f72b9745d75b0b5e74ad1547576a699e93de4f9f6a6330c0db7b85a10528b4bc1dd9426f8945df1340cbdb111f340942a617d38225a5cdceeba4cd3626036c47084289a4501e53f8795c110ac4b4e0ce8d3d3aee674f05b47d179f083a6f394e24f48c0e1281e6be4376314e2d69ec0ba65ade8e44d1f33257f9c3e60df4da21add6a153ec416168051584b49434796e940d905aca0decebc8d55f3184799b79e46bee01d2bbfe893dd5938137d61efd68dd41572829c551c28df5c4bc431ce6bd8275b479f11b67c3b4847285521c25d41b2958e079e5ca2ca2740ace500cd2a62ebf7cd477e751df7dcacb29e48696e2bf71dbd214a7954e2a8e20586260b07c12bde761ec72fd34dac6de3b109c791febcff481dff769d318cb82f794328b38515107f88dde4ba3d5dc3b2094068294288c3dd90af8e33283c082058b17576610d30b4ab14c2c72a45ada34c01819d88a4d92b40c69b3d82422e0a4b4cee5953e5bbaa5d3cb3ab513af3cfa8c5a6aed5d5204cb911a5838b285dc18a26f97a4437a2d476b16b9333dcd05d9244a0be27a3acd517b9a932d154bb9b0f40b2d625f68a1e463e09ab6a7cd39712210c3825ff373b6e37ed6f49d6370d3c31c4d7379e3cc5a2a4a13335819110c8b8d81ef19969e9693777d4c8ccf561b47d3db7b4758ec0aadc2ddcd532fb52d75b9cb7bfb54cdc3e3eb426e561fb2cb3bec2bdd024a1a05cb9b912efad60e04c9c154c0d843e69832c7d458b4fdcbc7bdbdea76e4d1e9ac68fb9b919bf6de8939732c1d758efb7d49d4511ab7a8a394ba941c84a16a14ff8df8a5f1746ebce369dce24b4fa1aea843fcf1e57fa28e6e1489cc9d19aa2ef6f02e8a3a6cdc67dde977dce1ef4129eef861df7d3c4a17bb5fd539557ef9b858ba1935778e3cf55527feb0dfe0b7f7cbc3112907b0fa362aef20ed33f1c8237eead3c813de3379614dfc61bd9b658e79c7eeb6d92388bebf39a5ea618574d552ec817b4c21cded904b080999d023938484b44f4a7faf3ea1fe7e94a04de8f94053c981771f4e976e42e93edc8f209aeba1e9f9404fc98177e96111041d2de506bb7b157774bff7de084d5a470fc9811e32835254149a9791b935dd7ece79f162cbce5a63eba56ecac8ccea43dad6b8a2b47b3ab55a2b152b859279cc5da7879d4bc62563674b267b2ded6ab16256aad4098fe24fe1c1d0149aacf8397a26f19c780d8e72de39e9af15e2035684961850d7e4a000e50d8c6f2bddd7ddb5d75ad3f7aa842ffef4d3c1d3884fd8ce8bb34ff521ef4a86e3b4d3527b9a958a9509e18d836375cceb23853061c284b11a8f19a8e99b796ca62b8072c663ef81e0c340e860d2f2d6a485264d6c2841107526a91890b6ab372645dc9634716312061719109d9637265e6e4cb2c8a480202c5a52430d3adc961891852c04b72552b4d5f2b624ca8d86db6d062c829af030051a234072c3162b6e35d470e2b6c8319a1063cb0b98b4018616321b6e3498a1392d6f3484c184cc49a53be0abfbf71290fa9b81884a19883d37017b2e03cd1d2b8a30340fea783980cb1008adf8720097ef8fcc1deef3406e06223277eec33c64e4831dee47e6ce9d3b209df2526b9a3b7614610c526fbcd7d1bb4ebdf1ae43654c8e536fbc6a5f29ae375eb519cc94dececb9dcafdcaa42150ce6405e5e9740422021282203df85206627acd40f3a6871769c6b8de74efaec116fb2f5710a4add6630e44046475fa396f0f64de6620aed70c54efca22d49b6e66209c7ad39df5566665209d1c938168067abde9becaddbf117c9881885e0ef80e1ec877300311950e14fe7b290339dd9481660e335009f506cc34034da13c6706022202324f33d03c101190f0330305a937ddc30389c7382824950483e54eea72675ea7ee9c46b9e375578d404440c0a732d0dc017f3a6a4c21fc773ca6400424cc134a0a5034008339306bb025cc29dc2fa7702f7786e8e70c526638a2261eee75287808cc9d59770aaab4f34126834c896c86995765496676264b9285942da9d5baee268314dd7535190cde2d862dbaa4e54d0914eb64de6470a2a407190d332930502847665dd7dd6068a2bb6e268bc1ca6a10b5bcc1c0843e697983818824496e498eb4a7e52d49113a891426acd0728666bd796812284d86c1b60fb4902641c60246b2849e94e6390e994195521d4039b17c617b9047fade906cd124904069f23ee491be24d01c9a7b3ed0f4bb474753190bf8048ee7ca58c054fc21a7682bfaa097471ac848dd21a27fbd9197330025cdf94a1d77413b76e3ee4e8f3aebdcb857dfdcf14be32eed55de404230a1050d14ea3f331af56d33a12ef35df3d96cb6fa56a9fe23d3aaefef2cd6551e58038c2a6b88a268ba98656e6363a362b158ac0ee430050b324c2693296fd667b3d94ca552514a3950a48d3033a08ff9aeb90d0c06c318a300c60d4347f0ecf57aa1502814468043520d28572a9572cdcccc744c2ca17246cce9748a71b95c2693a98b6254248b291586614c4c0ca5949ba2822ed2a0a9540a637cb748a30629f063df35a7d30985425935bcb0c209d463df3234343461188aa2784bb24108168bc5ca5b7cec9b2593c96c6c6c6279d71d92600305a68fdfb1d3fc9fcd6694526a040e59a0a01fbf692eaba9a9c1186be1c2ca11168bc5ca1b7ffc96fd63de930534acc06412f1e128a9d56a054129828c22abd52a8521be40120586e409241b5e30180c8542011d61021d7440cdbc5e2f5114816600824992e89a999931994c1d3812038a274c312e978b52da812561a6c081a662626230c61ca0e18645118c42a15080e18519804085a7d34914c54e08294b8e106dc2f03f323a34994c9c941ec0b0c204fb9ed9d8d8504aaf0d579ed0405fdf35b3d90c636cc53081165ef0ccb74c4d4d0d4d2a23892250acd56ab36464fe23a3654451a4455b2c2922c67cb7582cd6c4210a1561fc5bf6150c06fb4e7daf5aad16a534a7072a53d0ff3b76d5ebf5fa3e7dab56abffc8e815c63805a210e450e5df3487cdcccc7c87df59a5523d6fa01408354983e63d361ad461dfb2bf5c2ed7b7cdf798f37f647486e5ed8119c8204c15d97b6cb2d33cf6fa8e7d262626e67bf6fd711c5f797b40088914371a1a9a6f5bec3db6d865a799f986dd954aa5be6bbe69fecfe4dd81295590982193fd6746cbbe6db0f7d8609bdef5fd7acce9f49fd3b78c8686c6953707a6b0910315b158ecdbf67a8fed75d86331df334f8561f8cdfa8ec964b298bcbb148021431630d87f6634ecdb36f31edbcc5f87e5edfac9c6c6e6bbf50d8bc562a9bcaf0c92a8628bd7ebf56d73bdc7e6facc5fa7ef9887b3d9ec7bf5fd82c160a7bc6d1554b4e0c3ccccccb72de63db698bb3e137ea76e535353f3adfa9e79bd5e61def48c2856ce70b9fe33a35ddfb6d47b6ca94d6ff37dfa4c4646e63b7fbb6666fe23a3676cf2a6504ea00414313131dfb6d37b6ca7a71e9337feec3b7c0d8bc5fa1ebf635caeffc868d72c6f09c6c8115452a9d4b72d7c8f2dfce9a9bc51aff9b6b94cabd5fafe772a26e63f323aa626ef215fd888c1c8e974fab6d9bcc766f3f0a7d959abd5ea9be6fb944afd4746a7864431a38a2961f89f191d7edb66efb1cd6ef3306fd359df356fe56d3be1d3aadb7e44adfacf49abbe65dfe16d27547dbaedc7a44fff91d1274a29109233bcd86263f39f196df36dab798fade6b3dbe42df3d56ab55ae503b4f2e6c00b9292f0309bcdbe6d32efb1c9bce6b3bc31ebaa9aa44d148ccc2006606a6a6abe6dacf7d85897794ddea8afbe5bcf37246d764bd830628b8c8c0ceb32798ba34aa552651c2926684309168bc5cadbf40d280e545e30a234348cf1ac2109112a60d94cd2660a472168811354ec92364551ec80152d452d105b87c1f029bdc964327140063045a2b4de636b99567fb950c7efb1a18e4f29a5578a2574d0b27a8f6df5d665be559f61b158f77169565e49da148f7a8f4d3cea18639cb74cded5092d86b468b55adf36d57b6caaafdecafb3bebdbd56ab502720b2f460628925b2071618524606280c30e3d5fb6083d81e2e6a58d9b08d2e8847002471118462398225fc084b7da228c196a104952c58a9c257e4001922f3bc08085ee04545a60240331be222985443a6336d1a06822396224bd60830c2369c51846b6a8046d18cd6024c1c0d88875319a29626224dea25591ac224b1146538a12afa22e463945495718bd2886c983d12d429a4a4833e6170014c92450408221a4187ac10a354c1184e14246f18189c5ca14231903171069c188a6e88995249218c5c0280da31c455b103103a32228a204216160344365091fd2900205454958808910585c2086972ba6ec74b71028d19980caec61aa20b96cf1c1282c22436ea1a2091a8c7630ba01c30882996414c19c73ced9cd39e79c73ce2e4b4ac302bc96e3e6bc97e3e6e426772f3d419ee6206e562b7b466d67efadb5d65a6b773b9d6a4b738b0e5852da3c22ad04c9614feb4b28d5ba97c6ed7553a8cbdcb534449234bd21a05adeee80b73cc27fbde67b22edfc5a4f42c531befef5aeef1a4d11fb7aa346f3fa7985e6b0640ed401f0aac1eba950c561f33aeb4e6b771a1295a26b45aa5128426a426abaf4ae3b4d127c42a76990b45e0fb6a890f64e25124d0b784f24249a333f4db037a795b7977d3a3df3fdc6c91a693a11a0b479d558020a4043d3c90c629cb779a58b17006830da8c20d49bf9d91864cce338ff7122c9c6d828ebcd4b66b46225bcb2c202eba22bf7c2144e98319126c7dd3be7cc3ae07bd767be2552042a0e99d707f19139be9350c63c085e1652f34e43046f1c9a94671942230a40734872e084d2b6cc2e5ec6711e9151a2635e8ea394524a2967a71723984e2db7099a9ee2a0187a2af280640e0e4872b0a58c11e8cdfcf5888092e6f090ba3e88cc016ecc5d06911cd49b1aa551241c40e80dc50102bdb9432e9d311881d2e6c45282cc01e4faacc8bcbe693ab4b199c3c65abd99978d475c16478feb469eee262b3bba6f10b2e9a97baa98b7ee3a9d2e1b63a3a437adcc1a5dda5365572a6f98f6f29e690febd4d3b8ed766d531652d39e37c32c026cfc8bf6c98c2518bda1342b789d27b1e8792f77b20c12286db6685704ac894069d68abd574a29a594922381d22c376b094eb89ce9e0f75d878a340cc3300c71b6a73f103ce16a9e7b3b64afc3b0f330fe4733847fc294163e0c292d0cff8a63456fc2d387174370b4360a9e7bee9eacb5b6d69dc67ad03b37ee9ae636a03854e72ead8be688e74ee7b8d368ce0957f39c6ecf71aa51d21b107c105d3a1d8fbb947dae2efd946994369ef24de2e8a2b452e9a9d1a337a5a3c60d74a9a64bd9f3c68a2b968ea34982b73785b4a55d340d0b784f255389177d4db0b20b0a4be42043187d7f7342e7cdf644d2dc3b0ea8841f82d037488abeb7128be6b9de4e29771ab08125169a2398da59ba4ea52f71394f24ca06489b0140001ad80001f30c0b00b4dca5f4561d2aa9a492d660fa1797bd7a53ef9255c1a7625ceede5cb9ca5d2ec0110f11d9fb6c29b5f9b82600f4a477e95d96846970f721a2cb7de64140263b03c7700cdc3135ec8575e7592a6302e3954b7baf3ab4bef010d17da93435d2f5ae37eb32cfc300679080e00895b22b3889524c69322f60a7767216270bb57674c9cc1f1d4769089402b9c7586500655cad999417bb5c33b3f010d1cd98d25cb400ab2ebc242a9e1d481ace0637b2e04da36d8073a31683e9e7dc31f50ad7c7c8c09081312f67376879fb80127d43cbdb132db4147b70ba478575a89c491d3085e558655470289341b59f337b588020c216b511ee169afec67542438ecb306a5d7283aa039ea795bb5428ed3a4aefa59ae6e985a5535b2a0b83d2893d6ce5a45d4014c6f292b3d65a6badb5ae3aa79e9f93d6138fc982490cb1b4e0878788683d02e8c431692ee1390d007a585a90e3acb7d2dc6d955dc1dbf342d0de4e5a3b7c3f4454ed6fc40b3d2f30b702584f693218e06aa7a57672f9e21d535b1458b1b43774d1f27603162d002d6f3714a1a7be5cb8924181b7a7af5d7147a2279779e8ec1ce5ed861fb4146997291e22aaefde4d8d9443b95f2d451ddcbf80139b403c4434b391ef0c11c91bb901ba4ef43167d79d4ec983ea206e521c40293bd402660882d2e4132bf4d43227644dec90d615d173ce1764add500b8f6e4db1328b4d476a441758e00ce32664344538ea1476fa869bdf7665918b5d6322896947ba5b7f4b20acaa3342b83a23e03a0a41d776da5371bcaf0e2adc0cbbc9cf7aab8c068ca0b372c9468c113dd0a1d38c2c3131c60d0461731186184a4b50289e00908d838e2872b443f6630214313528cb84051046714d22944602d48082f33204205a2258c00f364871eec6ce1e6c40a97292b27c5a37bada0b202126c7431722b32c61938f8e0450b2d6a20b1636df0a2eded89908db660b4b59683c2c9510f499c00f1815b932fb4bc49cb9b13287a3ebc395192d2f2d6048c9e4772b3e1e684931dd55bebddc01057905ef0c5892bba0401a1c5112c68818b08a24ce1bee85aeb1193f382e3856b43d35b932f9adebb35d99205288f5c3920756c9c9ec8e206c901a1c3e9090d1ec98888a6585a8fa806dec51aa5264f6ab55b97203447e4035dd7ed299ed0c2c4125480210309788902072a4e50850e5930e9c2e3d6004207040ce589d44196c3922b6090835195142c31430e51a041840a98c08288304caef0383502999ba8f4ec3665410d99aa190800005000f314000020100a86842291502812a9aba03e14000f9ca63a663c9686921c074114c3300840104300200001630c00082003d5dc5100581f9ed1d318a988eec77d471ca0c13d73e1403705bcf014ae2a68ac472811264b38e4648384124ff762609d856dc5e2ab2ab6d5bc20ea68b20286af84fe016af0acff1d779efa77d60320592ae97559a0d7c7a7afaff4058168462b0694f1b0db8e707ad088fa38d395fd0979c4be13dc76048286161d0f8f3cc3f7a111fbc1e181ae3c82474343d38f038f707bd151fc4270a029053c9967d07774e10a419a40f3d1e3dbd9cb93fecebae111441f0dc41f20b8685412006f02edc7675f5a28779f8c33631aa051f4058983becc4fe47131798674a15bf905a629016e6247d037e8e502e14e46bfef0be587ce056c922718dca221e785da454707a0bbe35cbdbfe7cae86291496cc943743190523bdebeb39f56d477b83b8276415ff305e0d9cd838ea7589e5d79d0798ac9b38b071d3bc5f2ecc683ee9d62787665079d27c4e419da857ee517c8930bcfa0f308790c742cbf100234e800741e6e1e46cb33881834a560273547df67b54f482e4d34177fa1b8e84bc0933cee794aaf276462e1ddd0df63039d385099c70d4f3d77869e9e12f801c6c21485272028a59b628c5e81c8f3d69add5c5cb87b8eedd8d74e3d1e77cf23f83df41379a170d04b0013dff1c6b3c7e3f4f10c794767ee20e84df6847ee6c729fe02e22b0466ff56d15f43705a6f48b967d615d0611569c77a4104f8c795e37af97834b0c188f5ad755a96b157acf57dfe40281d0cf8fbb2ade3cc4383a51f891973e628aeefcf5417ec6f918a378d476ccc0b5d091dea2e9f18f803cb1bd766f7a981335bf1043c2659918b513bead6849d43700b19b489a0f7567973f3c437982282a978b45053ba60f7e5ccb70b5ae1f249f90964317bfc99012a0cbb9205ba2c755a6327d9adacb077d9b80fed4a2faa5fe639d8186ab9585009795fa0ae1039cc6c653e9a84a9cc4bf71460bec90fdbafbd9c2d35ebcb1946c40076e19bd468bf5eafab12a8036cedd05360c78785d9990a077707fccb81562013601802bac5e60708a0c7804a351020853fa09b10d3cf8db3559aff07b53ad2069c51ec4c1785d703d41e2b3b387c755c4e19731e8222a839b3362ef083b22f54be542a3868104e67d4016522c6730553b4aa75d5054e601ff08ab8d4a26651cbedab610c412c066c24bc719bc75038e0a0579a1af218a4aef206da40d3452d327dc262bb5c791c7ddeea92dcb8a4a9658850e7c9e9c528c2526150ae1726b2ea5969bd4ce550e6400c86df920aca68661ad1f5d8045859b09ce6f27f39d28c519a3e45243ea60138cd683a40917151931f25447fb2f286569370a6c99468de814a695550db26f9ead72376748d23c75fdfd0af92f7b526890b4182cb6834a711b9f73338613988418d5762578d7e0f71c4fa42dc6cfcebdf7b3243615f773c1371d32c7b745c26eee8d386e96f8850a6a25a6392416e50437c711c7392307c9f2c43b0ee154c667b6e73b241f4498c335c5a7f4e9e24dff81d517891ce45d1edc0279c961443bf355fdd8e5b752d1a12af1f74e15574520322318668c08a101ffe87381d03ffaf579f658ef284954216ea12ada3ce487774eaa47d45ec3cad25b120c6bd1fd8d7dd91585bed1f4f25793e019d24f5675dab32d1ebbb37b5b87747c9a148a95758a9c394c4afb14b807a2c085a3f01f9646af12acbca85c4d30036d82389a8bc25b9409a03fac59b0556454e1db603ce0ceb3b620e51e916ceeb341fc60b8264e92adeb8d87951d9701c3734d6d056bdac45c839b56732690ec4f199e54ad613e9d6a96819e0910011637243efae2d22c0a5234a195a061910368db42a832fa51b872ec0aa97c80ae344d5457c1ba0186b2395e03d0245adf17162092a02c7231e0815133a7a483845fc38b51a3aa0ef18b4ddb7233b2cff8e7afab1858360ea13f9d2ffdbb297f6341f0f4c58c8c762d342b28e17140d892191f22d78642b721dc194a0fe662917efcb8490b1bc28a765c15a3f6922571d2447ab02c67d7fd7403ce15f8ab5032f5943d4b65103f539518144ca40a32ac7a9dbe4679ca6702342d6259eb0eba31d21f97efe2c85faeb429dcd8ba8b178a1be9dc8238505e55b199c5735f74cf1f679c34eb74de77c40f24e0d8b877b4ef170e084ab47ea801bd357f218d05e361eab439f7362d9290f14fea65057f3e8b91e80be91ff31e5e14a9dca8d1eb21abe176c08cd6dd5435054dd88beb08249b9e18d2a42322c62999396f2635da39db28e1687c4e364f91f695e3e42220f6682c7cad809224cafbfffe42b278f41d9da3d080fdd38b5e81a9a8ae5560a39dccdc454d441b7c4ceda73a491d0ee3b00905761209ea1478adc7bccc22e9e9882af81f2f7c9223f74e78ef37e56cb349f6c8754061b3f08b7348a96625e11b08a4725d7042f73b4fc0ec927706d45e0cbeef2acfdaed7647077479ff5355c431ccec557a02dffd00e3429b4e84edcbd2aaf172ba95c46cff39ebf2ce7bd707db5bbef95b3c243ea34fc0b56659e2d8bb9ae1398513979dd5d9b6959ce8994f50df7e2b117cb15ce0666a0a5ca31a285934dc0769c486a193a9fc1a57e8995c069836c0db7dd49f11b5413dbcde20118aa1ff58ab38014c5ed828b84282f58ceee6b19a0d7410111a57ef9098e3b22c3608ca391dc7c419601d424b8d05789ef80dcb82f1837000273e8537acfd69faca723c03a6476dac666ac4436e266e255b3913f22d454481c1988e330a7c8b0e0542174cd7614f9d5dc311c3fc6e8b6e7f30831a25c345689a2b83ea644339c4a7fa25b2166a835762fe15a34b358fd18da09b4a610462405c83e46b6bf0ddffe3a949b8fa1bd6ae882e82e78b8498c1c01678c2e7b011f554082b960fdd6e20db223203c0996a2348b459626e57ab4876a80e6b0c44a9d52c4945fcf7093111fd5cab42931dcd90625be8e86b01c788b0f05812097db9a2bb4f5b52c07ca18d1444f5b51fe1e7baa90f992067ea1501ab46807788fabb6cbd73f47ce2aa0d897af6054b880d18fb74b924aa8b640af18419d444401ecc5e98e92924c4217696701dbbb80b58692895b07631a8da6010125d7996136850c736529930d5822eac9a3f143b631e430eb6409005093b469b0a8622ddcbd2da110b080a38250b6777191f72f186a86170a17dd929a029f8ff69318b5fa354dbbccbb47c27dfab6b6dc1d58719f29aedf40b2508c7fc710fc34799222bea4acea08e0a21af11ddb1adba14241adf9adabfaaaf3d8206310952872a5768dd936c2c7e6f0bbb19540aa149d28ff7fdf7fc741bf1424b274770d84d1fec93173153da3a993f79fc6a48d26ea84ffb273a3208f26a02ebdb39ad230b9534b08b6d19100b4988de5c0ede4ca5ba33fa95badb0896e40358739add399e475f3b28f3e9dce6f726274ccdfea5b32224654885b36f9a26e09a315025f011b1442032627e0031cd9b462f7566da607f0151c7ce7f5ca04ce6f09b01b8e163a8a1e6fa5ed51572fa686f25e08dfff6bc1ec3e3e759fb2909d0b09dfc3c74730b96dbd8a7122acfe2290b32c82b57900f757493b95093668d50bada36def0cd3ec5f1940a95dad3c570c44b74fc0ec2fc3017759d10296fd9103ae6c6f166439c44afcb559b2809621d6969ccd185d8a5cb9dcc07529a4cebf21ec2a7db323c36638b813af15cdd987ba1542c626ac4efacad58b3a6253ea20423b97e553b14caefa36f64ca86334dc817f38319afbbb6a26da916d7f47f9435bb2469000bf361241fe853dbffc70ae846491758d546a31a4df43416445ca1c6c2e49b450d5785609718836172c1e594d0f5733394ad9fab3825fed9cd292d3bcfa539246901f38cc7c7cca929089528f39d499f37321ea7e62956e15ec242c38ba969d3eafc615168c13463fe160e11f5ef80a39b7187b386dce6149909d8e27d7d4d777b32370ca2d8c9372c0113f0b7d3636fa05d43d275f8c93c78bf8ac46e247b4b486c5135f08d4d886e7d5434bd41dd106ebf863aa818446be8968535c412146cf2b338727f2fe6c0cf869540b1a80b4949972b438ab73ff811a25f26c662bc41a188385deed7580d7791985e2d6681d7b576ee6d761b69b9167099772c00595293160c0cbb5ad43c17d6fced6159864b101f35562bb4f28a1982f891ce22a59730586046be2070a40a020d874fbd7811f80e1cc3ef6adcbc28fe52aab7547f5a1836af623d4eb99c9100a73bba36099c8023461483b0816f5f5671f7eda5cc4c27de9b494987b21752b0c11564e1314f4868e0c7b2095173a3535ced806a246f757754c09dffb445acc8b22b3d04c88eeaf469bf264972800f029e6a44eccf5a2e0847e758ea1791951768d10c590483eb8d4fdc9eee0dbf1c6ee54c17a2f9c01f494ddac9db98005df02a79d1ac6ddd459f0ce5dbae07998b497d0c55d4dbfa6c0ff935afa347911f8991db78fb951a573b8f93af18d06e242b946de728a32757eec9b4dc4d790ef074e1913adaaac4df34ce0da68de9a63b3a01d16b74fc9c11618288fc4da195a30bb2198c0994ab837487b35f836a17a7d4b6da647dc93da7ca21aea9e53a51909651bf8495cb50da7145b1b35564fc267dae2c99e2a13226d4a8bd683d2d9959045400e9c24f6d6d0fbd717d0503de99f99582145c8f6ecad089792a8ef2f8979bfcb99f63562ac56b2746b9fd4650a9aeb0f942d59651b48ccdc76e84ad9658f7b0d52b9ac310c4b7f631439c4a8d68704d045432ce90722a2f13a7ab6e5d4512ddb6ef7e8f7b23a3f40d8c4e665ec6845245d192aed7dfd364e639bd548943f7319e9b4d39c3fb341dbb9f596a72e40f86746f9ec03f9272b361183bdd9bb3829a496d92ea473779887924dfd0edbb5907f64a2daf92af6dfc29071946ce502108d833a935b58f714ffb06d258fd6823cf2a63d91412f9d28309ca37c04ea5796b2af25ccaf288018ba215ac60e51d6f81015a853ec1fb10d77948845d174f1635831b424878a7d0b1ae0a2b85d7a3706d14cc2f87d68263ea674b26fe970b26f313c6588d1b90e93c490c4fd2729983d5aa2fbc3a00dc45796cca3a984260b55537f6c48b61a673fd256ecde82b021cd8353acaef9c62343688afb589026d9c3e00835a72b1c214cdea657754397042892f99734b60a1f0d58d17a8838e62414a73cec6044c275a18602a262fb2dbc4f4432c39a5bd18358e0047a685e38fa78addab5759bd3a2032d48ecf860083467f985bae1e11709b58a91c3748da11c72189e1ba27784e84fc827e6f3f597bcd391d24015b872af934c17f93e5fc4a3fed7f11d63e72f000301e068a14ab560943a4a1fbe6accdc00319245fbf369d7cf329d449d85f73811f91f70de6aa1a945ee282f81525f84977a335300f0b7734ad18e0f7a7a0d2100f83ae71099f34b989e75d8c12037d6d5cd050c0dbec6b1a71b35aecc1c741d8d3ae18a113f6537e82cbe111fab0a21077b8f54c6e421e4d1abeeff4e831ac398372cff45f83065592216c292fb41d7a3fd6846435a26c6ac39bb279498555d08e41a77918f0147b28dc0425d627360bf3e8b2da209d97700f8a467a3820509494048ed9b12878b6211fd25b3ca84b58fea2b53bce078a8142b0f7d173824c6ca7e9e0f36bf181e90920b1c7d206fe4ce2fc60d639054b62feee69768b60aba610472375b55c7a9dc7b9cc8109f5462435066986dcac4210cf2e75d6ad08695b0c92a1210f01610ee8c5e469e734fe58770e3fd06f40c84ae0affd3c587269b1e986b852022c63798a13e84234310fe837acea102852e5fde2c39f57f356aa1d00c710a679f1cc9de5d9db530d3c4507bcc9cbbd66798e1c26021b5fef0f56bbd9a80325c2d454f5d0096fab528d72b7f9021ba4fb316bf6aa4aa3ab2e63824009c56612d929539af49f44d3add0582d1c6c47d1bc5ccd7ab88ffbc94a67f3d6124b7b46cb8f255037202c4fd8459fb37e4ce2271eac140a85999bacc90f3d34cb062614ac3f6b1bd61bdb14a9b70a24745506a1113470da1264a97c7af379625bede8898cb2449961ab4edafcb2263be342c90ca40e5861f299e6847f64f06ac50e7891feb95002b4bdc6d54d2d5e0695f1d9343330cd4f864cdcf24d48c21609d0662993d236710b2b802789dd1df45ea6d113cf76980970dc9860d03f67e11a77ea1d66a517a2af2bc4132a946821892e170b6180178c17c39558524539613eb2a4822537963e00c7477f85680000e7e10d3dfe5aaaddc73d9d74b371bb39ce56cf7dc5bcbd9c58fb28e98a2b2d5ea1a6d79b3d13ef9d977286172485d1c0bf8eb80ae94982a6ead0d7c0097124f8280b59dabc4ab83aa1cf869e54625c91a67d1b19d538987fcb9486e6cea7c7132e2a9614adb1002d08c7c72d5f91c861399f8b109f86555b8a9cd2a3e9993e89170d70bfd472ada138322f0d96f048083c6012b4ac5961602c71e694cce9b181d883d8e684581ae3ead99b46237900cd0fadbccca55d82d520088164500a045a2aee2e0d3c2dd561804abe7e4b8aaaef42f90f70a6264f06afd4adda34ffa9d1e10501004df71acdd30781248a089dc6645e5555e89f3ab48a49b7652458a001f3ce30074481c9080b48126b26731a81b0a1845012e8ef6cabaa12e6d5e66806bf9ff269bfb310107f8b839283863fd06c32f691b88e11d80e8aabe9e07c320729795abb7f267bdb7844dd0790f63a114ddf5adee2313cc123044f2d3e424e49d58830b9ccc66091283414c626fa4e18cdb14dadbd9a433b2c97f1d00839bc3b26329b832a757cf15924de72e94cd25139ccb32911ee214f4008a9119c8ab666220ac797064b84e2d9008e7339e7bc9a0339d74be8d0758f4a25537810459ae201a83c6eb8bfccb82c94626af0b9c6bd0faac31249c87ef6ac968fcfaf0d4a5af51fb0df4ed06231eca9147e23df1616d2bc45322fa8a54cfb953c08860b49dc588c4638a8a6a08c910f42b78f8e6f14d8256ab54f81b881f40c7d087fb4f1319e71385dde7e06f1ec3b57dfb908b1275787ec098f4b8b9a68c8655042deb290c0e6ba7110a6fdb0892bbe033efb46a52f550a1ae467badeb403256c6706de74f67ef9aa7536d2b70404a51f19372464e2cd05632d8ad3b50b2162671a3c1a4b487d00cb3dc1d3b6445e96b8da3d8a75e05634c1b87a8ab2f3cab9aa829f7de8c7ff19496f74dca6352d5d2f366339cd501401056111323eb7a64fb481b1119f88062497f31dbef5e818e4211d8d01c8e2acde7a6e6de279670280228d3c3cc0ed36446e3363d79eea7d828bc8c5198396845e4c5500bb86efc7170ede24a8af5a2163ea8903bc4fe3b0a96b21517963406eec6f8b57da71daa85db1c3d52a7652ca90d190fba68ac7f196d9eb727831aa94c3ef7c971f63e61d356bf034cd6225dd089e2b60e93b76c72d2b35340d52974567a75e003db53347c7927044e30d48beb3ee369cc84a395a3dae9eb663d2815f67a088b8838d5aeab89a16b4907781a6064f5d31fd8ab294d4328db3b42b14feabbd8b1526f22ced8974de2256cc2b6c747ec5ff17dc1e66abc8867ba3442fbdec9421b95af90289246153807c321d4cad314dac5b3beb1c2fedd1649acbb7206715c5172f0f8f9a9da48c595baa323d839c9b1b13d91c9e36c7f03aaa5fd4c217f57f9053c2426828c416c55b62662a86f98d9b0e42e0541f9e6f3badb44512b8a095ab65e00f6466fd30940c860597bd3399d783e0e5daeb9f60f4963422096ed1e2abd255701b8bb310d43b2bb6797f319d134b37aafddab5d5fecbd3bb788a96947f0551573942647bbe4c44dfa0121aba3b676a8d4504c08c692c4560bb9f5c04bbc8604be4df84a466ce631be913cfe042689f03bc3abd155685b328341efaacfc3cd1c4efb4581e94ea988a47a833feff995fb986a056cbca8058f2fc18d35d2d2be2917e3e63d44a73944a65c829b014e4b73f54713da6aabc278e9b42acc47786e03527c4d11655f0d71ff93e65d79da00fbdc203e5dfbf5eede51bebcf449245bf0577f57bc8b9b02023d62f98270e13355339b701f26ced253e4b02c0033040a0b06323b7f4a3175c44fd331893f7332ae2b08d9c4e9ea8140a5ee190b51c55b400d77b943b74a26989edac180d32c7db1e0abce649b8f7d2852be67a60d5defc279e417dbb620d3dc8805073a4599f10147e1c053f58145b1dd23f8e57aea3ad3652229132d4fa386f1193e14c0fc6c07e143efb051d35cdc35b771b995d4140624d5ef85637533ccc897cf6503c04fbd55dfb0b9b299b489e0f9b9305599156c053ed59270a1d7674b9cc0ad6685441773035fe70643f383b07ba417300facbf6187d92e90e71b7b0305168a3b4db3ef84635c07c780daac40edce0c6c7969246d5004901263fc7d9b636216ad59e35916fca731324ea11222d948e8548d8ee6b963e3a1b19b1c0e51a341c8f8605aa90603f02327dd137e1b455a051a64b46e4bf065160bc5a6c9add42fb76b5c78f28de9797a64ea3b05abc92c0ea88b54954aabaac01d7dafe4513e65d70df0eb613dacbb43a285c78c117713ebec0e217bf1abed683a5c9b568187934d40c3785c747f2b4d9c29c58658df103bf2abfdeb52a9b63735a99dddc860532b028ff9f4a3e672f669109c41cd9ac9a3dd2fbd319d1168f67fd89c47cd1ac943e614f6064a97b098dd5296f2aad9021183bdabd20186865c3b9c190248bbf6a0bbaba6852312ab7606d9060e9c890d536956d57488a021271289f05255b3878a9031d5e9c0aed94b7b526735acbd3b2390912e099c5d3286728ac672f672cf0fc928918ccd740664c09f3dc18c6a4543abc8d2e5e7541bc52648dc72ad363bc1ea75598a063a323e52055ad8d9237c54a76898772df379908714e7b6d99134a4d9ff663b4f34ea2c915e3eee78574b53316f9d8fba3c1ab86b47c3726df6d29d39e3d6505f2bbbf28c72d6c399865ca365ddb5687873bb56f6360ec0a3a6dc228bd311d2a5b4b203842d839a351250d9b38a0eecc99ee088c2a801e4c419b3253ab64bf66e4f80574d3f253ea725a04b69b263085746357b20e1b167151dd843f6fc3fa504d62bb8409c2f7d31dd4d933d8299325083d14203bdc5e9d89eecd9a3bfdff82853a66a24a63b5ddfad64dfca72f36ade243c902e806ea49bb2436567f369882a1b3f5d335d4aa76427caaab92ccdd7170b1876f341a6ce319722ed1b046cb4ac4b73778f913dd2ab73dd6577737c4844405ed97c446f9e83e4247aceeca0b3af9a466d935fe8c6086606b4beccff1d188736cfe71aa151a3c37f19d305013f4585d81e8e7087110e193a8166296c7319764be39e31c1d087aec7c6a8d588999a4ae5254ff975f01c40db8a7f41b755422ed5e134e3e9d00bbdc78ca9b988991a94e4059da52b8f3709973f2430d28c96e3a9eaa3afc76a1062983d553c3df490ef31296b1a3153a3bebcbc777e38b803f59676ede93f84ef8cbec68436fd5bdbe9f439a98fcc5ec58437f6db9be8f493faf63526acea6148e03271acf14b46e5e55718a5f07fdc2cc6b26fc5c5d2a381bd4edfa592fc570b68b67c8c988073d639ed3a6e66d993a9b695ef7fd9998566a4982e5cc3bd7668fc29fcf904e4faa06987d381f09726b216abe3b04525bdfc401dac69bf6dd771337ba98195d78c73cbbc653784d9b5c5ffbdf8eec523553f3dbaee49d19dd682ece92ec9e443dcec7fd85c4b293394ae84a32fdcbee96077e1657591cc5e97b1bfa37378ba6800c639451f98ca7a8aecf76a83fe45e6f243e096461a8bac3d11f75045d938e1b803f164c292793db7b16fbdf8324aa8b139b41250b3210b7d583709186e4514f8cbbfb0fac39a6ab7f7b4906658e8c16943e35f4991629fd34968558af2e7084f9bac80dced2b956a428593b21b806ee1b47987b8a750990048e2d7654c25b76e469bfcfb1a046826934b4cb2ef961e71eb174461781fc537340b5308b385ce3ee6ad08bc8ba7cab737ebda89d03f5408de0781c933cef46a89c04b32797eb6a2d6a069f064dd8c67ff39017d0e09cf95c1c9018a322a4d9c2576dd4c3a677c10f2fa4eb72034d5cea9086b831a6a576a6e55358446d6c7c9f6b8d8e96f76d3161f194296e54b5389cf0116a73536b3e17988070147c7ce9d6e0bf47edce8e012c06d4071829aa9717c417214c35f14b935bd6c5038da2a4e3527cab6775b8f38863800ed5a394a587476920b2ce506a4a6754bdc72f7419187fe39b387464306ed4e01fcfe5f95bd42868c87e0b79e0f57d8829775073643da268b42be0193f65f4f64e0551d6a103882773b8cf4d10fe30844c3e808dfa19b251e6139174a0dd21efb9002583af7326af86accbd383694fa7a042e5eba999cc9a37f6012b8fc56f3f6b94015a6984a8f7f0ccc6255d3416c98a405759f2c19f7201607f8be09506317d8e6c8d265d29ce1199e7c9f50a78458267a16fbdd6e6ab23857ce3350e2431b5923132fb548de9d8c105d33873ad208a4ce4f426e401f4960f0bba0f2eafd3fee744e0b5ebaf7d914af2a858247b602ead370095f4fa9deb864de8d480d91f6e2cf1914f8b7af325b3cb6797b832e3367a5c7ced907b6fa943c4612eaa90d9a4e53e347cfcde3d5cab1327af113f96d3e7382370d0b715d6c72cf3fafe3accaecb15eb68b7cdb343bc5d3d8e774d0fb1fd388d5ccb4dfc7dc6b225a39d71e4bd36cd65430a84a11022588f5eef3c94e1d4d9e1c0d70420d2bb068bbea55e3f12c5c9e406ab2f4f77cb65020e268cf063e981d80112aa381e199028e6e3c3232bbe3469eb9bd6fdc79e46f40b56f90fa50d5dfe3364e81689a7a02ac5f378be6d7c907fb341bd6d6b2d0da3d692542b4a28736275af452de09520ee40c0d4304062a14c9476f4b62d70953fd7fb4c96346a78b367862051190f0a5354580fdd00156120fe105be8812e16875de26e97c0db2b684786769da83ba4b89b99efa5bb09d3630311a964b6c470b101a768f1363a09d4de7afc2939c4856d83db4eb1e5788ab2271fdb411f93cab144edae962044b011d5495529b3b4d6189b60221e87f5a658b9384598bf10bc7ea7d80dacc92d8ac1150dc321900924b509598b0e4226db3e6cd9209257fd88d4ad3a600e7737c0f2facca24a5bf2fcb80426f6a88ab953830d3ef0620979fac83938c677d06d2618baec27739a779545b0939bbe2947963848b10ff00312032a7f9a84fe1818343dfaa1f66d1c09cf9e4ef2cf8bd9c27199261e52d8aa12a0cb879ae89cc907b2c85ddc6507cc4e9e36c7e8e33cd5133abee00e69d3438e9d0cb170a9d2cb3821a95ace2e3508664ef4d3d3e51c74a281a56f97108996798cd8975f472b86c6feae63d378c58d284f3e0f19c2543c0d116d201713c653a0b32b5841a3907b8d332c4ebd8a82f03e21f0edade3ada5b35fd350af1822b82d468475cbd339a12ec6f2d29d00d0a2a74929b23f76c56bfe7a5182130604e16a226aa8febba193b3a609c7a0644773b67060707f1ed88a426af25969b230c8e4ed78042b04912e99ffc51e02471733c0dc3edc88f8d38f0d1c42486da5de1c1b9e4eba3344917406369976cd5902ea64daad6f403bef0a8dee8d3d3e38968a23f893c288c9f6191b2bd871c0094a0d0fd4b5a1f791037daf1896d37273da38751be5dc7b4c8de159c6537a5a7f495cff20ae4fd0dbe3d1d37b290a4f22cd0a7857417a56c4830468e3c7013eec66e42236e4a36b9f3b7257b5d459d27462f85644b505a872766810fa8df97c2546971acb5d4e5057e6711f5e4806c164599557df7b3913dd19832af95c98ed253f11ca5351ce7c744670065341fdf578605b1f316bada496f712ede58c54bcce9a065ad44471234a10fc034d280dfd9f10f25b2422da3b6e0be98a09617568ace04a55f780a4c76549c4c50c86193f63f346f993f5490971eedaa0952505d16768e2cf25f5b823f4a6034d7ca6b7aac54de41b981707b4b9e601197829a768c0fdd8a0d6b04542f5d6416a7d920fcf551a9eb70a1a97b03385ec063407aeb7441f42009c3b394017b7601981c61848c2ce454b59342461ee29de7e606ef5ed029b4c9d5df26f528b44375b5e7cc5edbd09026dd1a120ca7d051a0db197a743170e71493296426dd015ac718e3ffa511f3c0dbf64cf573e83ea5396dc7a830b2fabbd2308015db59b723753c0e460022e8a83b10eedfd7f2206c97146ab1669fee7f4c2302e4e5549b2cfc692f3585234394b90f92f6cba10f89c1578a083da91ea6edb93e3b6386bad5c287fe2907c5c3dfe29cd2237c39cf9f16e775d5b1292e54d1c4f934749e714d46f9493d4608bfa51d5f637dd585765a83957f263398cafe96b2c821e5b5a116a8bb4071c512dac3def201864b6d58a7f681e9e740108be36613ccdba07868a6088956bcb5dac7281f538daa0241a5d35e1b51c77c77389496af874357c438863b3fdb66b83439a2c41c26155b66a277ff6062db4bc39d6c6ddc4210652e5dc38e7c45b2b3eb75ac8099dd25ace0526c78462200168d73f535a7ab0cfd8642571796a6c2e2b03fc87f42461b16faad612624b8eae741a6b71ff990f994b201b391de9f2faa07b1cb6d05e18dbb67c8019c50de0c45ab111b417515e046c1060f126a1f298d1ededd01dfdedfeaebf35528bab27b4dbd1deb3737a963c2ca0f776a4f6ab38ebe20c4066c1ff6d236d3c8a45bd87d81c857b5d6b1ff22af56333ced494bccc3e4f31e9396daf10939988c27a5883c2257884f2a796513c999e3e658549051f64198299b76d83841d91156465e0f0bed00b087daf6e1a3dea20e28c594fe8f2fc28e9918022930308749ef74717ad88c14893936c539c261279b440d81ea91753368532a28dae57399f785431de9e4b460939a521f24ef9d67eef6a32628baa7e912ca666a6707ed6d707ed5b525a0fb81bdad0d657731518b87961385bccb7984db78973be3d2564dda2729101d6a6488956f8c77425f6bcdff84532b2bc5b6a7995c165b0465d6dcf14e8b85c920b21a54fb4e44e40398017eee18925aad08250bca271be88e86cbd9fdae63ac3cb2026ce0489948fd8e1d59db9b478740b77484e2e71ee24c4cdde3eacb63eeca5ae3eebe54bdc7b5193d76884c6299afafc4bfd4299e42169dbcce3d0bd7428a024cb88ab2aba44e0f685a042afdd9bab49b13f1d8b7b32e7a632172f31ff4f5f8981bbbd7c3828045b9bcab4eea84fc9972c19b52a60d09899335ce7346837b872ac33f786be1192c3f889d3f3c54f5858e37f7af1efff3d3ac1350f247a98e15f23ac038b386c6fffe112239f62c59c198313f9bd6faba3c0afee750df9d6435488dcda351c6c2ac538a7aa38655be1136ce7638d2e23d63f94f4764bec9b415a74843fb75fe7312a3d47fbb081601313d06781bfff3f32f931386e61d9dea9dd0b8ae5829756da6e1653c8fa1822e38195fe539cf09321abfda5274b6feb786ee05fa7f1dff1d3987aeffed199a6e3f5d640b8f2a241e494c0ea6ea3397653f2a3ac62cdd25235f9438679db502b8914452e0fe309a47ae276d0ef2482ab3ee4e62ee0aa03b6c3fd3429d432e940fcae37cb50cc388e3be4122e3e391b73d332dfbf43cfa2b684520c1ed5281baab37b5403604fd279a11908b2b4da11901fb00961d8574f80de45ee70dfc0a17b322d956c51b26c817c6189e2313885c58324c0d9c21a88cd0fa86cc2e735501ed4d6b1987903870bca88dbda36bbc660822468d9ca8121d1d8c96bdebe726dcd688804cb40293e613ed9d47d79807cd80f21e6fb82a7320ff9501de305f1d71a977a12497012de5800e37b72ea5c238ce2d0088061ddcca8599649f6075d9ac829c3ef7c1ecabf3d8fff61b31ad156fa104a66224319dd02141dd3a63c3f45063278dc0294a83dad22282d0a4c99fea37984d7de4d1a63f675dcfbd901542c303a52f252523b00017fc041950926ac9bd6eb1c0dfd92f56aec8c1649bc0dfa59f91bd3bf2f0ef981d32d5993ecec14cf20dc6008d56e6012ad74a80fe49e3c1f43205a74eb9178fd64378adcfeedc84d32650193ad2592150ee163b30a3ab770231ee594fa97556f9fea6e9b2688c8b8c739b2c4b9945314b55228b9a9bd67348fe89117c913ba6d7a3ff1250a69237e365fc138c7e97c7102effee39daef0a61614004b16eb6f9fab56b849b08cbe3f80ae88f55c9ead11fa291a50ef6923a485d8684a99eeb89f927cae21dc5d91e42422c688299acd7af34515709378f62229958e1d2e7b2ceefff24d92693689fe16a9b09854eacb6de8897ccb10d00ed1741a954fa3ff3a99dc0b3793b25a3f388378d0aa8d3f9dcc979e184f83ee82386f404afe40df190951cd940ea9c5d98e23b9bd73e7df851210ff2d7d5bc600c9278ca85417c5be2f5a79c8d2af444c4846e379dae5f7e03828ce3f31f54db35be7645ee609cd0c869b2bb5513c8efc4dc6b366e2afc926c967b19b6b44206a0b618bb17da1218eb4e18b1ec2cfa819426033cbb06e7f7fa51f0f9f9264fcaca0578cfd3f1d4e022854c908ca0e160b5af82c4de51feae0fd9a5acdba80a844e7e99c7dd0c718dae7d6dec5752855ad901c19177f4cfbbcfa978f9c22c79e7e352f5479d7d929d6ff816e34fedd55e842f7700b53e2d8d7ea94e9d8d42a79fcf3e29e25fa2ecc4544a7bca73c76d3837ac51a34f46fd92346ab450c31380ebe41141c2632707142550d26687a27891c8d10b4c63eb776b1cf7203c61605593b2b09790558f177f16447d4c51a51ee737b2ed2168171ca993bcd53123d673177997a382040286913e5d5aee727c387c37ddf5f6bd30fb67739362fe79a8fe98a54f57832810547457b9fc83c93a492e32c644cffe1abb36a920f56d23c3c54f2dab05d8cad6868cb95fa7bf6efcd00266bfd0fbecdb77ad1b0fc1a4fc6019890dbc74ef66a62872c67339b189bf43169ad1d422fd6f4f8b49e65f4e00dc30c02e97759a582f806ad7b0f9b42ed99c4b346a637a67c7b74558d2f3c60b5e4b386049ad6363aa0ded089a58ea844091a12925e1291c6d8d10abd07c777ed5462af07753a17a30068a42a6e194b039cae4c37767c308c4cdf05b5941f55c5e6decc9bdd5bf02f9754e211063d88c6e28d58e551ac0725c3f474b406f129a95f8621b409c85e7904b2d9963ef8cb59c1809f65da3b5a147cee2a2d877f85e4e4ecd509b0502ff5268349c8faad0ffe8fdedf85a83f53f29252634131094a15bd2ba43a4e43ad6eacc966f32fa0a610809b6e16443e1548285f32fe749fee1a3224c4c1bb295c7f94eaaa4870f1b85f87d51a5a0d8f2df7b4b17e74c6373bb087fc1cc1be037eb9e1ddfa8ada4945c517cb732b102f4bbaf1a53dc09570fb755798048bc2ad77a9154db53fe3a1f183351edfc47016820ea1244727351eb397fb97e4da091f59b117f08a9f8da116191d64f768431ab0c2af7091d8846959d6779c789a4af431f6426b3a109faa7a5043da7995efe15072905435fb990d7269c44b82e46821500b7bb68db0e65b6412404f10af9e691a8c41d23c6f5103cd7ce15aed1a75a5ef6e5c34935b5c3bedcbd95ec1ffc9e41cb0f9d6931614499d828f8d7cbba219af62f6983d9d7ab07c4d63ab21d54ed3b6b49fbfa8360526cb62b93f48cce5c0d0677698b3d2f8242820ae84789739709d34a39144b794967105d37cd224fb667ec3a90844fedf9c5a20ef2b8efcc19708f46f1fef6e3af7f254298411b141b9cf11aeb36216c577cb6b9a450e5cfe3ed181a302e8f7f3706d2470b5be068a346640218fe28f16118ccff8468c6dc692d53b9009a13f1b4c85ef67ca5de46666bdb5b3c8b55794faf4498c9a52e37e915da7f3eb178b3c32e8d39094b8f682ab5b5f7ad4c7bccf3ab5837d6065220a2f65465f3b80532f9bc940168d3b9262d33df290a10a9c485ba4899730e9d9e30fac8d996ea633dcbcbf34acbd8c3057b7427e80778d8f5e1293a317f1906a0d0e92d9e2abb2f10b016a93deed5b06e007c761fc50e0b270ee812daa456540f2799d05d7e0a6cb26db43073f597b156b49c02fb5aaf34eef3be47a8eea33414ae3e835438fc6575d88e483d46f5bcd6deb968855cbe05c5d7df542af0cb040e286b343286479e904bbb22b850edb8646b01b4adaf026cba45429f104795daf5392d83c341e8f755be4d6729eec279673533196a4dad03d21e2b6864013b52185489051ef57fb3472fc43b38e7764ad61a6d9b7cdd17322845ae26e49fe2df04b71df0a9fb00ee05fc30f0c4d66c0384d95babac7ce029170a6f27ff1ac6fa45f93a02974ba3238d9799ba869f1560db4a327ecf9f85b988c774a23a022feba5b9f334b3dc3ad1a26bf7460dcbe684277d6600847c605271baa2dbb2c1c738eb179921963a1c55adeed2863b57aa52ccad93bfca4fef0b47b0432ce37aeedcaaf389a3dae7d32ad84dae052055d1ce3e271dadf1de54789bc4a2aa9256f8e26e57e1818374567fccf5f7ffe46ac10eb9571597c6f0939a58c9db6445e98b74c046beb7ca4bf327be598b957a357d6253636b5fe52386db3aaafd22acb112684dc0069fb27428d22f680bbb17e166b97e8e534ad64de15054a8f6b6fd3e2319c18d15e8269970cf2544f72a50489c80216ec18f57b0840a0ad9747e3b7a24c828384801abf7e836e084ce7d836cf32fc8f51faea0998bf2ea7a993dfd65bc12298a87db87c658ec637ebd19e321d55a35c5a6dd87e996dc7c96ff812597071f32402d5ca27e768c32ec92b812941742de049c390fd5a6842c258a6328808908c7a7ed19cb411778bc3331f5656c08c380158975441fcab7e030fbb1e91b9de47a8b872d9f8f8ebc0edd6dc0e1e2c77e61f449748f782ab0f3ebf4c661549c2e77171a81338f43140b58c8c90f4b35e38e2e13095a07f299a4ec34567013df00928650f635ea80facf61a544bd6f1c962854bab9617d9e155664ed466b03d3c6c599cdf99628a21b6a0fbdf82a9756a6aff1a48754ecd9aafe10b8bb65ed8c37a8677ae36220548cb9e2dc18683901bb17a778623a8b554605ce1fedc8651de9768fb685527c85d39e321933cba3eef0c4b59de3d978ff25af3154630c959c46d5e4211306b2bf81a48c7cddca51a022da5f97309bb7f25d15e95d25818caa87b692acea08514d5b4f6f346b3cbac8a7d554b9fa22454ec8c6c4a23ff173e39905a48545a4f29421a5cc2e313032547a21770435f03a98fe34c615f5f6d259bdb3d4209f5edc95db9793f84c1da39a5882ad7481a06341c5b02629c6b3fdcdc44a0fa049a8eb908991c2627e6128e8abbfedb3599b8bb246f961d6887c379ac7b91356b41e776888069e35f71d42fc1c9bff4a0e628463691bf060ecde44a1e8c3ee99f60cca44010c90188ca5e06b5a72b00969ee7b63f60b592503a623011230208e0b54b30311c8c028a0503955ebe73696887ddd17740e340187b682715e0acce9924729458223b7d1ab234e682f14242a12ddb3f74bce7d37e564bfe60c68616b6c965ee70ee56c4c912e0fe1fe21cc3d6c84cb4fc8ee08b2c9568fcf9c0ef0c0862bc48ec900585ec450e5aa7b693821c2830e41cd0713ed90da3c88080af127cc9fadc372d602130e82516d718839e36712da3518b788ada688858f530b9d1c6a03e66d9a750cf66f2fffd121d4a28bbb106fcd85e5edeeb8898f3ed908c30ba848cd7c0e33aefa86ae8850896f85dfb3921cc12e92a93d4b61438f9c11ccd8449c4a158abaabf5d09ce999fb29c67465d24d56df1929ec5f76e739105cd75f1682cd86b60a53989a398e52ded20398e7426d030a6ff961596933e13103e50646a0ce68587f0a941ad321eb4c4767ce1b94cfa664cffc636ca7b32fc8431fa774146e0eee43b45b68347d2211d5c94f8341a1a96a1a023954372c5bf23910833527d98f2e474890a8911709e8eac9718bb7d45496c54129473aa188a951d875baa6f75f45f3e43ae6eb0cb7d0f76c0d5b2342720c627e08adb41ffb93a1d35e85506d6ad30cf9b02caf04125fdf6875465c3036719dc02f4a84c4b1ac211ad441795745040e0a20685ca0b71585c5a2a45c3e82b3bbfae88318b0f72b2c4fd8558e16d12b37188cc04a8d9fe17e8905a70f554533e5b3f9bba337387c43f21404062b876baebb402b877ed9ddd10a7eb97a3d61e018cf7cf15ea444358f8537d59a7770796c56c3ba53f3a530fb1ba0b0ac47e295309196b91c556b7ae3d68cb82b27256c977aa7971a7ed7e2651d0964960a50923e3400516c98d50d958733cb8d48263a5a4296021ae86599c269a666ed75978c65033d4c7f6da87931d8d7417eacce986b483966f022be2d650586a69c606c98b14a79c01d95e80b307540be95c219aba33763c5a1837ed693ac9e60d532886b370fdf4173f462885b14e21808f9849bb59cc1d2a3adcfe5d6a2287f4d2e4096f89655fc1e2be9521bbcaf59557173e1ac8500a5dd27b9470634e56dd065ce6fc7209e5c44f5267179060e0e6ca5714e523667700e145740785441fad25df625c9dd272e7ff460076f60fd49ce6d533d54cda5fe67c848bad84119aa0a636aeb979e2f95c6dbf06d935be5e05f36a8cd065652097a182356d21188de7489ff9e9434538511f874dfbe9e0d97a5836c3aaac12fc2b264c73f1073e0b0265c494ab688421967942b405443ee8c04d78735ee2048ba2318852a61dfcd61398065743bcfde51de5ec4442149818746dd6a67a251b96a3c430430a2f7d4afa8d0cd29907168ea9f3c61d7479d454dfe0e4b53c45bb12eec232aaf4c0404174de7773b66581cb867c94408e578eea07d3c13819d3ff1f278f7c10dc13e8d5e4be210456eafe0a67d2f28f21705cc42c4da8d580e8cac285cc54344f879d87b4d64600642375f2f92ca7a21a761eaf90c4d779fdbb72f9d81e88b46bfc0b87bd6b76df5960c51d71a731f34d8fe1b16d8e15a310b32b7d1c1281508b7e5eba4cc6f01c82405b84d0610e5672ee176d79553739d5213a96911accfc22e9dcf928b7eaf5877a5987da6ff967f9335bb6be1b1e86a59613257d13231d615fa1e51864b1e5ea5cfdceb2fdea2fa195b6e4fbb3374aabd85487e0e55bf4ea12a017ec4b972b27822392340f9cd0ef5ce734454b67ba2fbf2c22920d75894c36cf071351f4912328677989586be3b820a052a04b1d773ce8ce614dc6ae28693b65195f293d65d827bc0e528ebe61e64949183d8c6853f369f36a5f980074f13be94b08a9715ebddab7f2e8029d4cb047fc6882f2057f937e3e9672b29e2a2d87ca8b025c6d526b57b277da7326fe0bdc3a5787c715265ea7c14f6d54bd3972a1aa0f988f0545624efefa1750301983922ee4ee5d7648016bfb4ca5323c87752002ed562b90a7067dbb9cac71317c4cbbc23bc22d0233a480435834fd9ff9c493b6d4c47b285a1eb2ac50e8e182173bedfd83a9cf84abcc22e70a31a44e2550ea0d027f4902e312c35d7adb1814167f882493310d20d1a900a1b3fead6253f2f0d8e6cb7b288eab8d04ba395cf0fa17c2d9e6cfd17574fc08ccdd9480186dfef4c6fdf9bdd269717516650d608397d52d4ca4eb82cf026e86026c107ea6041e59ada330bb5718725664e4cfad714c962c0561900f53150229302f1f7e53b9158387b755e98d35943a17fd2f79a1089e9afe3253095f99b05498aa914938b6a5101b55292ba73799803fb1656afe6afb4b6eaa93c1a886dc4934210305e813e35ef38ac7c3e6522af0088e8d97697c64a5bef23c154402b5c5e4a1e4f23f79422b24821016148a4cdfec69c4a006228511ba53dde4636d0412ba501531863c0213bc80dab36403d30b9282eaeee58a2bc71452761ffc8bcd3002b1d7c54ab932cc48322155b041d168da13a1f160b1d6bb3a33305f0e99483b5d8f59ee4db05b0bfdf46f66b6dffb3a8f540cee15c3509b77a68b0d4315dcc886869e2611a92d30e95e5d933ef01498eb40144561770ff8bec15ac6474806633310b7d54c34cdb2a21dbf53bcd33b04b9da55ff1877423c9d2751a014fa062ad7b9e686bcf8fb3b7b5b3207181b560cc9c6930123fef4251ee5304acd59cd3c9a4137a9afee5a8962c54d3fa1c3ff291109a144e654ee2ac7a3713776c48a7452622d6624a31b74f4de0c394e7df595687f7764eca6d22b362a01e2543d2221107d66bae23afc12f29a7445e044f05d42d2cf0fdc51c8d92b060b2a8475e2cf45c541568accbab9e1a196b4e5d689edcd3a50d6a665c9a871a4b9790ac1f55e445b52179512ca8269712b9bb64023f4d41ac295cb23a62d6435bbdbbf68082dba92d0444037be9280473dd2ed2232376c11eeea9eb2ac18d12cd6c394a4667394dcdbc09336661f5450d2ff145086ecb6e1fbef027c1000d2855e2b824ac602eb0d65acea27e20afc9b7b51cec30f6010d79653ea2aa5753cf9c6c1d4f77b2cf9899884d45613eb424068d5d5e671dde9117b5bc055247586decee2a915eb411ed8beaf4ed101ee06e132ab8823c0d684fafd3e632e476a577db8c95f960867060e7e7f3a6a9671d12e9717b6617025cfe69bdae9b83941ef6e745cfb74373e31f99028cdaa9858da491826873d35ff20213e306345399572f80f7593115387cad1e45285caaae8017b21a5c5a0ca182e7ba26e2b76a167c2965056667c9625cf175d1ea707e8b395dfb647199fd9725cf34b0179b6476f198c1aa768fd798b64fd9d0d7d28c11f688c59c489a7408de74d899b5892e6e7098f281c71f6d86e55f102f537be746439cc34d5ad3019b7c70bd33975f44b333d49a14934dba2c6a5a5c14f98db83d28c3fc5122a37a063d0147f50813f38adf603e072256e008340d97082763ab81d982c3c19a134a0159562aeb41ab2c3ad9ee3cbeb924de79cf6204164ac605fc66b3340555440c03baf27d88a230adb2c120268a62a766dff2ee00e19fd42103a85a8aba0baf30e22ed961ad2c040aec48949b8b59bfcdd494f15b032be6b8f6059769a7673e54a011645e4aa64a7895b49792e1f379f7bd5d492b31a7397944221d02239ec911c6d68752900db5f183bb1bec1acf9006c0bf44bf7125a96142c328257efcf3e2dc1eab6f76cc0cf13efdb93666b89cad0470ae263d75264b8f073a7caee847783eec8d98433d25b77e79074c5c7092465b0fda46c943f397437c362dbe12fe46a1add026c01c57d5c510abe9d488f20ac4753e20f76914351ddce3b12026068dd9b3a42549bdd640e0338290b217155b64700c75776cd07bf3dba8979780b6e672f4b60932d6ee8822a41c81e477c4e8100447f28e4b89a44e3ba9846496fb95c526e8603ae837a636feba7bec7e67c453fe124ff4148fff402582435e64a9148c56eec12e42c82bfae3296aa6ad22b04b3f3ee7e855cf136babf824d254b117e40c7b91dc449fe20016eacb9ecad08fa251af7497b2cfcf3cbdd41e9d95fc411cde10813b80fc8a45ed4544949ec7fa7555d4d255fe8a17480be746515c932bded288c7d8e2636f9c556d85227c8823a31d4c3d44b61cdf3077295ee6424ba0619d34c1b50283247e8a8f126c0ed6bfbec23c6a818b07742d638cb6becc7d8063ce7b929c8a37cc7f56089b79bb9e7ab637c394e2411b54f91a89701dee08ec0408c8bbe537b3e3e8cc0f4205b66c0564601a3beca0f20553e0dd17fd6f4c0cddb3d1b57926251a7c3fc2ba9041d45d4b758b5966a9676bb7673dc256af47add5dc38c69350fc7b72bb885f28f47c94ffcfa0d62e708bb0a5b5f7f9971f8b6499693eb3f394e5b6b4bbbbffa9b70c94f2f7466fe0bbdb67595f625016b9fa20463396d72194b79336c48734df647fee6cd822ba8a4208856e393a0c0658099dc67b66fabd09b67864145aaaa0fd44c5858999a4f1e597ef9898f5ef0a2523492a1ad9654f72d80d7ac02c341695018c3cf6d0e9f1c31202bed8ec5b97e485a8b2a165f29fdf37374f652968de3f175a82020a9e5ea40d99e71c2298e335a610fe858ac6ce6e8f7242c6fbe9d94e8eb02443500c6b6aedd046b679d1de75b8886de18150214b8cdb1e523ab9c3f53371aeed019b89e700440062a5b40f757b3ed63b6e6a4be61f539b036b45e849ab353403d78171d2f07e8da204cf63f07b835791b2434970214b5b4e243e1ac4bfb62c0668729b2a165ed20bd1021c25192faa164e1e3a8e28c749a0a7efd41b01eb527d2eecb5b640eced7bd2282649ec0010c75f878d39ef15c32104ca5509d05e0fce968f4450fa444e66e27a796a663719355e3ed15d0fef4441aef3ea8b8bd64e5d063cc56cb99a2460d6820f4cd760c3922d357d2bca5dccb303204d0e46ee32488ab8039825690f595427215ec79295c88a79f8bd8150d5112dbd06669f137b3f73dd95eecca8c210df9b88b41df00df3e0c59783c3e511fa2121e337cc91c55a3403dacfc78dd5f10e3aa1fba52f0156283b2bf8637c757c164bcc2c70de02f7d6da010617e1c18aec93aa93248e96ff876ca4dc88b7acc2560448d1fb5419a6a7b32764502fe41e61be0c36d70f82e9b15b4e4e300fa2a844020a14b6cc30f944817c979ab064f6b4ec83ea0a4c6c84cc99ce2d3902001c27f82c408a113b5803dfbddcec7e3fa206728c81765774106e0a47d4e63f5acb1a948615601d533cee8255e9e0a02146a7b13f9cb7b9420008a0992c932bcb97287c4c07461b270ba7efa003162025ad2100c21d69e8d1ef2a1efe0b113287867d77e086f536d45fed826f3578f3c8b49581b941cd5259afd0b6618f6c4f424f582004bb7d05f1d129ab0e68a49ca5d50691dff70ec66a43f9d04487753bf9d28cf5d90a5ea328aa1555bc5679d40ae7594e7093a5ba07cb3aad9020369b4661ab7e0bb24b03bece845f0b153538ed824b3be742184f9e9a97e16f52de5d3a54af1574ffc34b27d96de115f72031ba014483029acc6d6159304a71261d1ebc6a75e5aa42bd786946f1e19f7f4820afb2ebc6e25e765757b252c115e5f2a12b8a0de98fccffccef0f0e0eeff4ea70d4e6b9ad22616e0b58d91671d431517f903254fd120b69846c42f6de646fb9b79449caca069805c6052cecd052e1c98b0a303e8bcf9018725862b9ddd70a641e460bcfe6ccfd482197e6c96341e1bf10c94f3c5280ca6125060f2b4c2dc870c691e185dc53570ec897b7a3258a951c15e5c12cc945299263f8c82ca29e0b968c9c15d68a1c123c19c7d8d761930b3b23c685a7c245ca959c8d2939344c9ecfd40b01b245028d1466e4a652689173a3e789ccf916dce450f0cab5099355b09099b0bc53952a554a4ec84d0952828e3f6157230765f7227766861fa1cef708f35223644c6e0d578e05ad07dbbd1a27b91d497251439e4a407365c3ca5553903ebfc50e133859603f55068797dcd7566e0c2b9700e54b5092bb63020c65d8e37d94e10daa2a32726caab0c8f9d0f35774fe4e580c5fb9276572337cc92eb63c922b8f0295aa2d48bec990393c7260377260a8c8f817a8b0781e7ae0d0c9c509cb39f9aa2ab3e48b8b2d2aaedca012a7ea8d20f132a40e8f2773836b8a8c9c95292c724cf4bc159dbc222cd3f84a53e6e97c791d5b72265cd143e58754e5f0205961080c3c72356eb49122c3861416527a6ed0f9166139c7571ea1cc9e2f53b69cb9f22ba87c96aa9c0a48726f86b0e051e686581419df250a8b0fd393d3422749d8bff92a53c6cc975c972db9315780509152f53590fc93214e3c722edcc84d50643c1c282ce6e8c93be8fc95b0dffaca852943c217154a708b9c12bcf2325409e224a7a56404466290caa9d93102283332b649bd5e3c6961dd5aeb64cfb67fdd5a9d6e0c832fa755969ddc1402b85cd8e962260a0b221be01a33e6c79616b2ec2172a768d80ba8b3da0511a9d00d77c371b8e138dc701ca8e6426db5371ca5e2f5f2fda85b0f8fd743cf9248f6ca3ca44f14c9100198a1b40fba4ec4a3d5099bd6af54462289f6afa654994b7947b0ba17995cb48e1a403f0c7de07768cbcd431eb8958ded4cefeefe58564e36ac9a1e4f559f6d0ee12863d826c53a1251cf2a32555cdbfc1815a41774cfdefbb5d65a715e6badda2669120746986606d8c2136e92e56b82f5c26b62e5749b4c39d9263d9c9a3c35617a926d7a3689c148c8640e0d92091b1923133323884cc0608cf191d2a458744a13975f94634fdff7820c0f638c31c6b91e250e634c35e57452d910f2714372d353499a1913933995091b334cc06491561e9193898e8f491313264c3526be10d6c0c8b2a8aeae7852598de3a6271549a5e3a4b2b14751168233cbe3c81d211f5818638c857cdc257da891d14eca880518cac6deb58e7524e25328a3b6899333639b942b9767c98b9b4f284dcaf541054e254ae2885531554df98c3288feb8f10ea29a520cce25629b9e4beeecfad48273491798ac6d8255363d9724d9e6782e21b2648705e894911190352110394640b6422f372b46408014018128f9c12a00012203c88d7d7ff6350202969b9e535d58424253609aa6bab8983a61f814941b5ed57e5cd6bbded3a0fa19a68edc0fb7e93965c3d3787dc075ed3dfaa60ff8dcf8a0025c12f8800bcbc98898970ad5c71e1f23e423f62b868ceb4698e504fe8ff2dbf434b243c1460f1b514d355cc478711a5901e32cc2a7c56f7a1639b36d9b9e45b864c95e1c9f7c3023d1ada318e828066316f290839090e3702546a912ff4702eba4cfb15b2db1ab4d8bede711aa0fe0b6126efb52ad21b888f5ba2055b679c3f987ba8190507db7b24d5005b16e7aa5f6ec3ba7541d0a889d5377eed6f40162fd34a5094a756df3ee320322dd669e53d67af738b64de07dc246d2b0f6898facdbed264f7cbb7e08d56b9ff819d52084cab44d3060d76762da25d3aeef4d44ea598890d0907b857290a1245feee0f87211640c57f141258531318048c15104031603f8494662947b18dd720fa12dbb36bbf26a0e01aeb0763b76418656204ab24b61015a80a860f063776567b741183b25541589618d113963b64ee0a2a5a4c98a858d31152d5a068d8f1d03ae60bce8b3b3b19be1043876a882e008a38207c82ca9e1e5048b963a3fd8e0295a2e7821cfa568c2213b4fc72e01b50390d981600cd7d3140d2167dad8ea204943078e0f34555c602959ce9dcd827c55c1553563802b26bb2c3bea45861d0c76147025c36ece4e0057576b84ae6856bd0a23664f2914b2ec46d879e9ab29bbaa1e3b2ac7851918293bdd95921d941ded0a869de7033fd1e254ce126ee8c10243943555b2b0b88911e3878fa91c38ca5977d44b6b87633700dc95102c1aaeea1bac154640e35430caa5f2d98da9692819a65d98fb9ca97273660a0b2933ae5e304106254188e491d282a16889512d16f4ec64ec8caeea131d2bc0950457580b6c0f6d81cd8e0d88ab9a55e6fb42b75ac0f88913764286d2d96db8128118148b3b7886644591da3105069e1f585444fdc083adb4c65417557633ec62d855d958abc9ae6ed9650080ab0e575a7670dca8a901153b0e04c1300cfda910f626cff32e8e85277f721adee434fc494a1643a6de2b2398120f98364ee304e38594d5a5abcbb9eb0ec130c7292b5218ea5a350ad78560ce68ca1a443f7c1a441f7f107d2f88861794c974f825709b3ead5f5fac1945ce6d4ad9d3ffbec4b84123cfe8a31587e20d92e90ebc2d7b99d6b5a95e61deac51c0b082a9626a578df2dabc40bcd11b8ee348fe38ce7e1cf1f895aabce1bc7f212f920dd90fb4f7be1a25e0287320c2035aae64883ca6bc6f96640e7f1c35e5c01b31ae465e240b8bc2f73e3204f87563310fa53e81799f526fc1697bd49194f46ca1c70e3f7ceff17be9b1bdc7615941845037388264885babf574162b4dfc1fce5e25686561dd38d75d520dc27c77e94521f856f22d7a98bd5dd6681148865f49a4a0508fa84123aeb7eff63dc237098c317e509bf83be11b8780edc5ba52ddf79756eb7766c26eac29c5e0d3d9abbc1e1e33fae7516badfdacbdf6c391d5f4c87522e8db274175927d0e5ce75584ebbc4afa1c88b8d65a4b87b21ab3bc1deeb576fb1cb1e85eeb2068ad4abe37a545de839eb594525a9d0f0a2f024f2ea7b38b180e777689b2e9d965ea64410f7e145ef43daee274288888d457905a41077d318337bac771ac4fe651f4b21fee2aed0b7937591ecb94e6bf90ce8bc0bf7f64c907d04a8aa80fa03bc87ed27f5e7d865ff4eaf5193eb980b9f7dc22b6ef052f8984bdfa4c5665fa82a3f769119d662e799ee7798ff187b59a499eb65fa283d68bfcd26cadf583c24f0a956abe5f7d9bb7380d918e3583ed718363edb34dd43e2f6613b745529c6524ba6711d0b3bf3fd3a6f8f53dd9f7b2ef67da8a1f3e9965bf711bfc96c4ea793438d69dc99e6ef2fbbecffb9ce49ff87ddee79122751a6425495127d59124499cbf267732fdc95d362391684f929f3f9164884a7aa63d4892ee447a8ffd3dce49b51761572fdd9d3c8a2c75d2f835ec342ad057d3f71e9ff677d32658827aac8d5f7bb3c47750fb7450edbd27dfb5598d6c3f4e1fc6a6929bfeb4499244d2f055279995fc9f07b2fdadbef7a47f2289645619496224f567d326189221c4daf5ea4f7b2fd7328bb4ec7958d7ffde2375add49bec87c7332dce7e36d3f5c7b71f8c6c87fb6569e4342a888f7cefc507496d3e36fa7b491adf8f2fbe3789e27f6408516694801274ddfd28d78f9dadacf3d4d2620bcc3ea36825e9dae3067a27a3b610d122a6a58a96282d5cb6b8eb0866694f436f1a4730ecaec7f2abcc9bc611cc8b643784ec3a95e7d2ba36afcc1ffb6399973fd6b21fc99c04f4226ca73feef5871cd55410f8ab9293daf40db46760bbda6622f6eaee5827cd1ee81d7fb5fa7b5375731cdbe6f739c96556e64da35705217b726c9354df65e47fb2116c7f8fb1c6d85a6c2d9e692cb3618cfd4977203daa9179d0edbfc710a8ef4da38c24c7116ce32f4b20d2acd87eed9dca7eb01ade2c6df50dd8652489e49f24d3f847dfe058833e4b3c90378d51bb66a7ca6ee5515c67dfa8acef9fec3189e45ffe97ebcbf2f73f792cb3f8364b13cd1295566b257512f8b2a78d6abeeaf48db377dc1ec959254972a6bdd21e0785ff3d194525a37be81ed39d36a90380a72c5102b7f17f3f6259899936dd697fa31a528f23fede9bc611cca9dccaa94635f6a76ec03346bd40b000fdf081b5da1cf70df302210359561f798170012f2ae14538f7e04577d7049c5a50db6e7a6ad5d87db45e6c53f74a66c85da993db13f626a8f93c63725d5c39325a5ebb0cc3c9c748322457a484ac238b8dffcad2c4a54f4ecc4eee0b9c67020b53e6052fef642bd7072b57056587922e135e4f8fa7ba91338265841c1f2c2a3ec99de792935baca162c60d981c912d24a8f0635266509d6044cc8fdc121c5ab29cfecc621195e7060c0fd52697054dee6a045f6405a8ac9c1495cca3499e71240725e5428e2a5768acb9e2e2a1ecf91c74b0bcc91d4993b3232693d025d7c8b2bbfa315632906419423e9549ea8e0e3a2662c693ed40377dd22135857594e97736d60d39298d44540027e088030f3ef022eff14bc08bf0e3fa596bed9783465cfc47ae8d94f84100c1b2a47604ee5ff728bff72c53cd276012edeb134ab344a1a34a93d6f96a937e529aafa5cafa9e27937d9f07be97bf073dd9e7895325f58a2285eb64b2dc81dd9f5726143a47e9295c67df3d50f789babfba2b89f508dfdea3f0227ffb293c50b7f79e52097a6b44ea6dceb0aeca65d3363dc3aaec7a8625e1b0e919f6648346586d8ab08b6812340965b03601363ddd3c6d917643fddd1d5eff34a8ab1722af714489679bac2d3e79b6b13ad9b41947dce3827ec36b90a0ac5f8ad9e54b709e6ca28839d770cdde47d09e2cdf4682f6e4d7683f7b40d0cf6c99f6948c40dd4126f934b27c329b29361934a33ddd29667a84ad7c9ade298e2c49fb51f24da3cd949cd44a5ed3a6928fe358be588e63ed4b721c6b5f8ee35823c7b21cc77cd3f48eeefeb2d147d98faf7194e17fa967e48ba41ba67dc1a6671b3049e48b4e4993a41da92fb299ba234d9b4a4efb9f4f52f2fdf3e66cf6970c61c269da04516c0e1f62fc06b5daac34c937817f68237c8f20df699524e1ae695f66dbd7b22547d88e6c2dbb677feaba194d2791a6f8b4dc61a6960ce15d2552ca9a1e9e5751d828d75d771bb53dcf7b7bdd0baa6670355dc2d5b1fca3601ef6ddb499ccdd933e770765dfa7e49eecc537c351d44abe93defc6fd4e25005e12fbba607ff3e4d44873e22f0f3efcbe18b62b997528020c5da4963098275a882f8defc7907c51f4ba20a027cff6ca54d9b6efe6850fc51fc464d5433f0ff82c6f75ef67d9fff9006dfc3366defbde9a1ea24d913d9b4926fdbbb7f9f64f34f9b1e0829189889e81533e843df87ded7b2eccb4cfe91fd886495e685443808fcf0e9c6ff75307bf1ee990e02dffbef3df36ed9939f378a44e2cb9e125510d443982f26aa1978fa9695d6e3ab8a1aacaf3d7b68d3f38bcb564395c604307bdce2dbac177a8081815981398165d9285aca7e0c49d2b350b66ea822948d1ac910960c216a2ae60d4f817e6a2f8ab556f1ddcb8a330a5a0ade1eb9f8476ea12a08fc560211fc9988f39a4d1bb5ed0395b599692d0eeb24f0675fdfb4dabcf5b32f5651b452dbfcc4fa226c2b92e2bb36ad0e5b25f00d6fce28296ed049f6675ac9771d41b0f4700535899bd91729d04abe41ab8366ff7df809829ec388faf6dfbe7b79117ef1ed5305615fd4d6a4206ae3df90cdba373c8d4412dfea19490273785bd004afc1fef758a400d71173d0262e488bda54f226d062d6b6f988031155dcf05fb62fe6ef25c8e4e332ed8f6cdde0e53d08a44db0dc9eaee9127cb32c2d54ad1fe8edd99b24498a20293a58f9382c7590ccfb52f43cd274b04d3e0d6c7fb3231c4e5ba80aa2ceb485aa1978916fa1c8bf6408d2461d71e03acf425109ea0638353e29cfa6e9719f701a166966cce055e762fa7968f6a0717a23d36609a34ba78e14176c3ce5a841bbb8bc0173a6c90b9a351b582f34dbdad38bcdb6169fd9d6da27eca5c52bab8f68d60ada0e6eedbbd5d476a89b856e720ed031d84c1e1c6144f69c2c7c31e12e8aa6098e10246eae7cb9f9ace75ee660867d2f9a11f6bd4722ee7df3b33464852b5c4ce43ce551430205aa27ab1a5736a6d0dc2f8ccb7c19b5c65b55cb3a816a15a962e304ced6961cfae51ce3256777c64e923351e1191fa411fbb999a63dcb23aa39d3c20c94c51c70f75e1d4388cc931d23a0e34290a8ad2e3c4b396874e992c587971a7aeca667979d930f1fa3acc7f3e89b3d4f96c009f325430c0a5069b1a58a880962c2c8549191c952c363ce2eaf2e30535ac55966ea2c8395e424d3c3c848af0041a330a07fee493eba56e1d929a9ebfb794f49bfa71a7c2644286f12500dc0bf4f01d500e3fc6913757fcf00ba93be20fcd13ef6ab76ea0cb3e70c3302addec662b6c5569b9f97fb541ce6141325c6090bcbb37f5d0ca4433bd06dd2448c008c76a0fbd374ca0d9b6ed7c0be1b76d520551d18329c0ca74c1ef37a3d7f95f8e9365f48f705e11e2d4e8cf0b0a1044d05626145ccf00012434d8b2b9bb4dafa02cea1e0edbb296c80a972e2664a861a307898b083079c226da45c58c9e37d01e758fc65d780746a71c4ea8b29d91770ce8b1e2319383a081eaf1c4fda70a5c94d44f860e1603205431571c5ca921c5ee4506111011f6ca8a8c0b362cb4a4da14ac54d643823e7c68a2a604b573882c06871856307151663178f69396651c64b4e8a1d2dc89084cd9d2247ae706911c34347578d231be0a756367e31a516a726594db94a93c5c38690df2a22444c1a245a68b8e0318313022c45985618638565cd04baf4b1f2029c2f184a60c42062b12c6b621823a14b921a5056debc007353034f18212d9ab66815ebb416e0e9f26c18c65b2ec66cbcc56267c32cc803678c5a0a82bf0652f58fd22c2f78b15702b93fcafa633044b8ce62ada30cfa7e697f1e870aea866aadd7875ce717e8bcab03c1b2ac4feb673febe9f246695e6befdf5b49fb62adef45ef3adc42a43e566dbce9d68a246014914db756446df36eba22c6f636dd5ad162d707eb0f1575b654b431522166d32d1551549cf735a0e0d310bf590373ae24d28b137837a94f793fe9fe9bc86deb7547096e5aaffbd88fd5b1daa6e718d598d498d3ae6608b67dbacd14fb49bc1f16d2d9f7cddaa6e7981e300cc3f03ba827eac6f9d222a0a2dbdf1f69d10f2db2fdfd1b2daa15955d7bc4c47695c3655bb0e919278abf9c2adba458387c9aadbcfdd468b71f5bada48910c8d168b457c26dda535ad401da4f8d76b395b71f5bada4d5d97bff987480271b818080805e09b781fe967ff2db722d7f99cbb27c25dc535ad481d790222b65dbdf3c290d37db67a082176507ec075abe877b0f57d6ca9c01d779464643433addbffdbe0217de57e0a2fc9beb97da1c3f7c8f840003fcef5b7244fd3bfe4822898f24d273ec6b2b793b49f6e09b27ea16bfe613b5c4edc096103fc844f52dfa3eb25fd66d2f74f7cfc06d7a9d47878cb04e2e8f7a455e5007db5ca26407d7acfb0406f02fedba4f78dbffc7297051ffd6f16aebbf4de4f67dd0ee28ef573705a9907d225277cf8307a221224a3d7859e3e45e16cadd46ddafdea520546982eee1aab561183e52fda5ede02fed4b5433f0b61cd18e589be1a8cd1aae206c8a3106b35018f4eb7d761cc747f21f7f697f389b5fa98c5a28d7e14fd37ba19c719ee24cc52112472a491ca6fd59eb76bb08db9ad76a73a966e0d65e2f724d9442d7b7d66e6b3970b8edcb07ce9818d81669d5f3b087cfdf2343b8761244f57ab62eedbf33c40ff7a946c1ea24e5efb2a4cdc4f746f23be2408407b26c779969792c6f380e2fa49b400b09ebae3914ef08626fcb3a387137a8410d4ea08794200e0b7911feba6999ee197ebb6b264984daf3be1fdf8bc0ba8110397ba29ac1f8b371f42feb234b92dfec7bba4dd183ebbe233b92e4e85d5c0492b22317bd7cffa6d026ecc641443583bb3d0faef34ce08d1fbc561ce237c9feeb22253dc540dc77f348dcffde7aef3a0fecbdd5a7442da494c96cf8f691c2b7bfb4f3f54b94699f07aea3a48b701d254540006b6aa91652dea71e88d0424a4f68532da4a42fd22bb424563b255221d33281a33e529ab88d2ff8beef7bfa5e4b216a6fb3710033ce75f6695ab47948a4771cf2a2212be628ed9bba7d811ea245b77b3f0425255c498bacb5af84e360b3d96cff3daed6ea791f8292126e7b45fffda54520f821282939fd0dbf2d3b11d77994ebf0ec6bb6d956af8217e5e9ba26fb0104db61ca7ec03d1e3589cb5666c350f6a286ecbf21572ed731b9ee8e8dd2ac5cb5376b9b8d311671f8297530f7b250dfc53372c4b5c75fc5d42e2fd2f0f8ab97b821ef6bb3d9acfbcd6fdfe5e03a7c6f052ec298c0b22c6db67b6f3710fc0bbe197b53b56f4040407eaffffcdc40d8b75b061f28976fbbd984082690e8fe799b8d92216e27eaed6b35a007b2d9fe6613846d03ff96cbaf650fd44c6bd95cdab517edf853ebc0df7b24f732fdb2ed97b64d7fcde0d772a939e85c876d088b629b40b1eb36974ce021d7611324bdc9c46d31ebb6e94d5b0782603f8898d1643bd08d3f9770a8644c3867416434350102000316002020100a074422814090c548468d0f14800b78ae425e4695c845722045410cc42008c3300c000c300418020c32c64887f201849bddeceb4c1ca786ce57b6092a91d09edc814e197466778877cc834b701efceb2289c86aca4ca147cc569d2e87f8a01f127c08cee58d816d014558c6f7725e862508de884d1cfa16fa6610ba047b327d62f150c40861b56844fa32f4576e3e95288d970273e46d11a1d450c492b4e8c687c5cc33e1b1d22426e76ffe2ee21adb498b217691b498e0f5079aba6733f8eef5ea7a2276c792dfd5abb2c5ef471c0d1c9528b93afdf8e2bc6ccc96c94e48ab6d410ae24f22bdc576d28bd5edaa957d6879faa851ea9570b25c685761b37f673d1021ab5bf19e7e6307295f0734c70d41ab16e7ad6b940871e597309776ecf368df5daa799cd39fc5d5cf26c249e40ff484dc0d6b98dcc0e7d29ce0c07729bc1513d4206c03a2e99900c4cdc0c3991dcb6a0ffc75a41cdb94e53bc64e8968baca0f79e868c00c8b4abe7d28d47d7f111f187d19e1409eca60059c9bb093bbe8450d0597039a5c61cf1623345069c20a5c429c60514ab1c1a5581b523f6efcf6c028e6f9d15aedd9782d2b2e3c6b5d640f9379068e28e876768a641b989d3ae147cb4c934c911b1de7912de52152ad470b57214d89d76ae548098630532b9228a58ec71873a3ea5cb7aa5929d4d62dc9daf47de7c704036a7314b574ed884c70f75290ade3b1325f35be291f734fc7521fadcd90127f52e7cfdcd5a9c425024db91d346fcd54b7f70ffaa79d682cdf079526673d093c11c728e203d316fb4cbc89c4526a350cf331166722c40e296a2ccc2e1cece706f95a0f4e444633103f9ac0bfdb75c9dd0675b6e666d7b2ca220f3e3620457183f17d2cbace07d932c9c7924841d91c97432144003502fd98a7b6487e064c917f3b012e5ed9242ec5c8784bfe525cdc8a4ec986228c5b221f481e1b17179c29b40ea9bcfed770d6425fce98f9a0d7796c3bdf9208e629bbcf442fd0bca9bc3286c8bb1f4bcf225af19e48bf4fdf62f550ead943d9ea2a77f6521f9576e98213972cb4828b81b6dabd2c0aabb4066c631cde940f8f022feb4a73ef4eb89789aa703d39f049d03655f679cd7e4c2e523dc910d5adbf48359149eeaad9366ca35f2f3eb6b026b13a9694d34b61f246287fe0de97a4ec6fb35b4077828ea53134a9d386c54a54934f523ca303ccc9db26629202462a31f2a7c3bee6e031bee39abdbff1b7e2ace1bc42e1594181233ec54cdf759471a47e010c5460533a2e73c1cdb4565a3b5af6e60a71a9e81a8702229949a0289fe474ca2184f2c32b7645a135a57e68606b1b29cbea925c545c2a4f8a6d94667dffcadc2e742d30fe373ec44dd16a98579c5f2824c1e3cb4d3670e9b72ec7e21e8ad8daa39b0d66350964a1b24f3ec3a14b50af193895d3ce21cc47d17866ce31d8fcda06b72cfa01710d2113ebca854e8e40b33edf96119160f61c878ac46628a77bbb9be40bb6deaf650d7308565f06b975487ad32e9c1d59f7834c8aae5d3d494b95a49482b13260b97989eb5fc4b0755cadbe7e5f271d9de46836a4aca189ee587e8b2c8d25d7c5e8aeb1f33b273de9d7931408d4da2312a7cd244444eda2528c8648eab0c0eaf073e4f3e9842b0671bfa2a40127193b3f7b9726776331970606765f32a796f11b3e0bd571233bbe82f1033785218f3a503be05a5d49a5017ca179f639f682362e245a3049d205b002e77239e4ded65064514459554000bf4028000804b8a89d757921e12e93cae5a383e39e8580ba82a3f176d7847ca05aa65ce9fd20e48234b5cf5b18124097af9d5a393560c80f21282fb990a13f45476c1f78fa0b416840f68b6995c8a23bda9901665941d05bd7e0290b41238fc95f603fb1945897a6bb89ce1bac3762ed8b681b94ab6c690e8b9733f0b772f3e6dd20202e102b36aa1da530f62899c0a40adbcf1b9b78a6ae18b98dc219c8528cca06e54af47702596508661d839426432077c6867b28fa30599027b97416848e127a71c809b0eb826cb428dbf7a227923d6564d45552343bae9d4ed2cdb83e2e158b296587490fb887fb310f5541477e1819678d23569a724808c1d96795c3c56407f201b1cd9c682f7715a70f78f8c6c179856b10fcd6403cacb504ff84d01b43fd21056f6b2dbc3e2e1b048509f8894d1216310e6f590376d785923e3e3a695eac59a055de07073a72f720f09bd8e5dea7cd0d32ee891d8a714d740d79ac157f773e1e2d015877f83100c0ee1ad5d3422d81a6ee0ea7910a2dc516883c8f9ca938a329b3d0d02a7db5c12b1c1590df824acf26be539905efca8d42ece21284d0afaf703539ac0acbff7eb732d5d209f0528da00851af6b502fc54a54baef5121486184bbdba0ea3fb649683c9db5dca8392583831a44d8dc28226a80a47ce89bfe8e9c036996d61c946d123c29af88e47ec488a28230b0594db81dd4f89f11ed3db94c5c6338b58dc092fe6f9b79f8867462c375b7627a7561955830593ff5bdd5040cad8e8461f06d75a18afc04ef043e17126ae4d8984d4c22ce64a1407084450a4eec3496a8310d466e0e9c356368adc2dc1b20803846017f3178e69a2d66d6044c91f683b31f0f347e2a18c9d81ed08af57b92e4824c40ab0d9b5a55c7ff7912754241e52bcb3c292f10b694e7de3543e7b26385c96901f38a8c3a06d94c54e4235c000afa787d840b4d6af8b4f67e3ba6bfda8b236693bb7aa33d7b0d9fd9858cacbbb5058613b5242e32aa40272b19d8a00d9554c44c6f617f453488ca8979ae9e52f89d8088607a9b9892d6cc903199bfaf6deab098b66f03c90df2690bed4dedb183e50f56dfdea909c5cc0fbb186e4fac8fee1561762981702afec803b1a4d0a82c38d0304ba8aea3461c92748e58cee292d48906c8b3b84da5e507fc4f11e5ae24a9899d08e7d3176362b4cfccba4def8b3b3049dccf89a923c3cdb1a1cb4622a6d37661dd4096308169b001be71bbf992770a70e134f4423e75720c5a013d68190b425a892d5b7292054e3fd7bd566d1ae01387d1f9f561552fa6bd71768060fcca116492211186d06ef92563e6974ba706b6ac280b8ee639b9e08dd063ac0e7797623521e0b239e4e815ce5060180b2220be643138994ec5ba19b3277aa2e50b78b1a9eb11c0d70a918021982c513fe2203d5668f67fac3dfd75b5505082fe22c00265368978b5c06d2fa9c92e4f98ae8bddc575d07024efdfd6727f6f835349d907c4cab8fd13c4551c5f97a67a97a3b14d0cdfe46c53b91ec50241def48c835c01ccd60dacc9cd036c3429fb524a0aebf2ceee509ae4bb8d84e791a75548822c38b45b8203468aef0a80ace06a1436cea31ee8a2664e53b07bf4ffa1567b18218207a6c05ac633ea75271cebf6ea4b3575e30c691e259c8575acbf752ee20ffc149c81d29308953df6a3e6f7b5e6c63e47ef0fdba6c2e5061408badabf962abc244c0c9e41876c0515ddc0a0b7086b286635d86375a379cb1771d00d92dd1ea089fc14586768ad8848733adb74956417cbd645f70c5d832d2b93dedc313afd64a311e47b5a06a21b1b097c6febf4dcd3693862f087adf4659782ae8182e83411ee63482d31e50c5c448949177ec8ea92a6a2885dcda6bc635f3d6f2dfd3d583bc9d5945b892acab9ffc97511bf189f6755c8edb44cd4ec14e27335e48e49df8b2d50bfe4e01c9f6da5524a3b74acffa09e5a8a74cfab42cd5ed9bbc4026c934123381e475345e72563ca57100eb2e234990e2aa67f4f951c64ca600c65a882cc0085676caff0d09b973194bc277cc2a159a8b2658767479e439d8c639004ac6560ea42ca8b8bd2103f8f564a863f738e63325f36a18f487429acfc8917a1b3bbe736df8a6007033cfe76d7143c9b73951a4570a0b19cc4060e77c5b465ab34061b285d1313c3c341a7933cd02f360d17550c2e66dfad07ea668c3786a0b43271c2906a1eefc2b906dd50dea0d2ad95ba9b6897def2b8bd0b9a25655199b7fb19ab8939f0fc47bf2f5b22d956fef6c2434ed0503685285c1d8a16b0fb97a68e36f18851a2ea35cb526506779446e3e00eb32eb45ab107251a95bd2084b5899aa5325a6c4bea9079dcb62c20e5c2c36c1394501f1f80d8d4c53411dbcbe33d39e334ef174d6533db345c8a8158e7c8eb11b4dfbcd013fc176a33c35007ea36521b4af699dd5809355296b6beac95bd2b40944b3cfacaddc263ee2bc30ec64eb603a50dad9e2d378d73123b0d9dcb0e983ee32d0bd172010d46868b3894d482b0a30a02402cb4f973292fd5c5ed29675825a01f5a0e8863d8b037a44fb9f54862db277cbb2cbc5ba546944f8a2c3a743ac2b80569c96c413e3ad730f202737d155f6a0609277ff2cdef100bcdbf8e3ebdb597af454da226c37f74c875a5ff3caceea3eab02f4e7c2727f90817f2391398d9f9ec67f2e45c1d67bbcab3e919f147e7998b48422f86cb0f2e7173dca451a703a47a87770295ef8a4b96c651eb85f1163ffe8a985d8f65f4884bfa4ebd4acd1208174cf72c717c7e19142010a534091a3fc8f94902c70a01d647aa068dac9438020584ee89d30cfa186f3d4e6dafbce2b730726017501c200a4f2669b56797ce0894323b9ffa21fc7df6bf8acba6610cf45b0b5692b20ffeff05657c4f9787e398968790bf7ff0e296593f1847de9851f7ad5af8b905d8d42212e062b4f1e5d07bbfdb2d3414093d75fd02193ebba914a611f8c42d27d216e855e8b4a99d68a7134fe6449b75926c93f915871662dd6ffcc9c6cb22c648e8e1c574b54277541014231179a5f1d4309fd9127e7854421e48232c6044492d31a2a6e08d9ccebb4a145500cf62aefe1798965181fe51b4818e0e5342fed1466c4c1db81fd298381750b803e9cb9347d9161d331827c68cbbdac3a7bde9be1a0a41128c980baae9f491ef55cb17990368adae2fefa1db8fd92cf808b6c04ac99364e00a8f3a9e2e24f33011ab1afdbcbefbbdb13ecb699ba36e15a155a113603456757f75c12dfc9824e361b1656644d0eaa68c3cf9ac0dc41b8484a0fe470ebcc8df402853f89d998e0dcbcdaa93108b1ab0e18d9b763dc9bb4715ba39f11181304199203b307e41a80ec8563e9bf92709f3110cbadbbb175e5d5d593a5f7e301a67ab25c97388359e1b8ca91fc2bdb4d44f9dfa7069b36f57bf63279ef74b4f811befae44bb4cf85d0f02e9b9e7b61528be13876c836046098f29c1d0acd35daebf5c103a96f921b213ac56dee8f4703059fa89d3fa0bf97bc4d850cf46bf019aa3be5e634ba48c15aab2adc9e16fd0ef06379595ae93df6cc8c675f61c3e90f4890611094430d3ac093a8a5fbcab994c83c1077ec10e68a948249355953d24bdce9a83a60161a2c6f363258994b44c1087414d605defeeda6bfeeb6bcd60ec5438b74bea143eef317af97e5094ae21b3481fffc6411492fd27a04120cf7fc769fdd0df4e2a67649e21344582076b74181adabde22befd36916165c048f3a760f5a1f6bf56f47d36b30d3388ca00cf656374a1f14bc053379b28508298bab778b81a4a78a8804f745d106390281ee54f8d6bf7f2a2739a12ccc15b45fb99a397a7f3de2dd84ef8567c48be33de171ed300d277811a010bd265dc1991392fb50f8a9834d0112dba34bf6ac8de7ba9d6934fbd20cf6da86fb78cfdba8664c4dd7421f692708360c904708e9cea6092f21fb549f32a0c7e579c7925fa3e761a4257395688ed0f0f20e69b600729fcb2c7a903af85cf8b63676d073d2faf33673e4f0b42794da182fc1b9a8fd585e70256021aec448b004a690d65ce3ab5c48348107e7f6e75d5b18e489fcf9113eb3b884482f88f441406315bfbb343072a80df622166e9dac720fa208b0f862fc44b3a483f4bd9eb2149544ecd2285da62fea563712ead3ac99042addb80a15175c5848f146566a20268bf51bcd1067e80e6d54eb9e3c8a7c6b9ffac4f6711f92d1f5a56a844c4b6014ad991c2e76c2b871c1bc0b1c333b5e62e51529c3663a3c444e69082384ed46f1a09d0b2b0bad6a884bb9e0ec2d7218924e61505c96682bc5ca767389a90459ad1cf4621f11896794d213eb9754ca8a907a4db51b93256db32ab5b10b0e4d4191e7a56a11a897b1d7ce0126452baf6fd03a81bb42fe6d2a858b3ce880cdf4c895ba98e69b62cabb74e4bb1e4e0dab84b01920a5231fda86f3ac13574b4a0993804c3794f097a373ff7b772e05802dd12150af0c8e91e48af3f90495d48a6de110326812e8aac6917bd109d2d866db32a24bd229384060c301a550efa78a75b417f4d6599278a12b2f738d6374492cfe022a2f6481a862d224950a7d3041ef50828a5aafb774098ec58a96ca77174e72497a14384cd21984ebcf316a383923a4820db0c6a3c24631536ec0eeb54b01ae61363ebb45f180aa1f8532ab553586c8bc9e04a5ea2d2fc0bc5e166be42f049d8db0d58202a4af69af3719b29f99446cb42baa297b0a6c2f3e4328665e153761b1565a447566921f4fe566031ebfa78dcf6c65135a9159fa0367186c8986233c503d521223208237911a52744752a620651c75245960c34d801f9978ae27117d5482975d455b86cdf954d7549a97349d0d0d456fce927b24d7bc99668e1838a6cae89d895c4730b931aa176c51a2bb07a6bd3bf693461886a31bb90be7d0a4af91794e29c912bb764cf2981e2acbba572193deea29fbb65f249e003092dc4aa8debd54abb86387e5436d7b1b2662301bea43c48fa51c813008092a6d2027075df5b5154d268a217ccefe2dfdf8ea905d0beece8548e13a7463bc3e98a868bb0906523217757f54ea813b4114c4141af5ff1e82b5c57d304ff107a48be093b5454ee124321896c17a52daa29fac1340ba181ae2ebf04651d7078e7e595bc493c938847a0577f20e4c273b36915e563d31cadc312e6633c8549e1789e2b06d7b76086026426777d0b6acc3d68e2adf5e4d68374d3268d340e6a8928cacc2f04e66f761862dcebb458300f7d1028b9c9615c632e77583a8fe029ec153cc36f7a5d546f90cfbf121b53f38cb18977041d37da05685b893d3b1702211b49a7f4940812e2a036646706dbd0590487ec8dc3ad31a51c66f4cb17dfa4c5088149a15214843009036007c2b21d95a83eeb47be4525af8c551eec3a3943f07058e2fedbdf1b02050f8a9360c40d39fa9208a327526304891692092b2b4fa6729cfb11a0f104998d50298f5893b546880cf5181606b1283791c0fd4254477c9d37015c5364a88fb5c8f39709a1930b9adae81cbb2a435e96908c290a1e6bf95c576402d40ebfff644e707d2ee8888a2753d777a830b1a121aac26386987970f943eac3a5ec7d2ca04a0e88286b56c8150a1e5522667b9fac89ca62b8b1d1c2d6261df214c15b9624116032cce40a3031341fe59f139ce737713a463ad2b7f8047608cfdf9933c6d71838de3e0ef557283ad8191da010e760d3ab780c94116a844fc898b83fdd3164808568e71ecf3973842936c7c02e1a24d545d80adf9156107beb81a14ae0ad74653920a24cea7bbb15af2a88e417182914eb6e6c7eaaf15ef3e6e68ce111f7f2585518022b5403a5fd32f4c90d890758105c35d6760aa47e629d8a5742c8cd24c994281e2d25e846ee18715ed0d7b4cf3b5e9d7199d1d21e883c017dac85e5dfadf402d16666adb76334f9a9260e8a2b37a1276c6d76ff8b6fb073dcf1d5772175af5030c0cf8ba3c5f201c6c1c34f50f70c8db8b91f1af95a26eb09b88df97b1d9fd40dba1c96937ec2e6587e3bba1a0a14c7dee7bba8a2cf9743352eb6906eac68b9910b1138ad2aef46ea8f3c2724482a43e0504cd03f1891f36a0c3f6510327be923fd50ab9ea8ffc1abd3bd5f5c6141b5a11a722b6e3d95c731003d7c6e5718e0563c4d615fba7aa8d8436f665f4f2f87bc2d1402009c2c354244b93668dd85902c0ec6a375c15b2c10f253aa535ab7e75dfa84320a8df14d4501ad4f343171848db0296607f5ea6ca3202ab805903a0e763abf0efa132576dd4d3377eda40329f0ae7ee9e4edf79fb2734c1ed12c0f6138c3e2a70d06ec3869d9a6aa01a8432a06b5a2923dfefd66a59c4df61832b686f0820b71a4d985bc3c9587ab9528ba1e190818b44c61d5958bab6805d8b4063bbc70c7a9eb31ab002a626a7649d6c7d88d75409c8244d89964b8def779a39cbffc40c79407bf8064cfbb3436ad0cf642edb6d904823093b7ac97ad2619b9ccea049d73551d38fabb75714d186f767c23588450795926b7a63e80ef62ce47f5c838b1da8c2bfaf2dac1adb3dbb18d929056bd9eca6c950214a4d85399ca373dc0b6cb0dbec943c4cca4fcb378c4daeb2158f365f8438ec06069b6a16f64b4be0a87a4005a4f296952be4687a03f3888239ae1990ea8be37850c407e6c75244ffb92a9dfa8f538cc86623975554586a3d1ed4c784443663972ab8283a8ffed974a60bb4d93cb25935237fd4f3a78e4d34b1b4abfedd630e95bba5bee0c2b7dea51069e6a48c58dd610d0e4391008d6949c66dcc9bca180af30d32741194a36a186c30e38c6d088a3c499ddc73d4fb6018e659e6d28b835cdd793576dd78d1ee9f48a11ff8665afdde44ea09c227ca265af5a368a0e80ec2235101219a4038caa02516ac44c69980fb1f68481034c1e22941cd98a2d01d7b65124a9c2ee08a0d4a5ab03d917a01670b5a80e6481cc2e72ecac350daa3c8b0bb4efc330e3a909611cd6009e74036553bc8cbbc27c259060b7dd59795fd3c84d3f5c76676ffb4a2456d5a27cc256a2d0d98724c6eb61ceee7301e392ea357ea4dde04b1a92655857ac3176e80697da901b583e73b16d5115fa844d0df6e7c4c099a04da6f43786a1c6327b7d9bc4c38a9c13ffd00c4204fe98838e12a5a4de19a997d10f592162e2c42bc23e8fc08dd71224a25586c601400897d83cb638e4ace87b2fc53c23549894a212c94b8172f91aa5cacc63361c1b156542fd63e21f4005ff69fc0aa391e647c9ba96940a05fdf38d938059fae234d26a0a4ead76b1c9c82800f5edb8fa998c1eb9bed64f0024c37b55fa333d85ea45c721f2f2c228464273b72b6e9cde227582947b08797cddbe46d7540278a9803e99f8d882b3e0c0466194c8cd98cd8e44d828c94081a39947451eb3455ad3789da5751a432d9ead0fa99f3a2c139b6c540a09664e561e4e11cd43fcda07e971d3971de53a4920e44e9fa28f3f6c478b8dbe1fe2dbf430299bcb994af3716a1482f6f0fa62abc29e7105984a8c3728575603a1fe5ecdc123be82b4084bd3bcdc6f4afa9cb80af6acb5d2562e76491ad21529a94de916c386472f310e5e92fcd21bf6a4db8e1b57dcceeb67907fa05c0d963f29e1ae8d03267d3ccfd1c9a3c6a76c06dcf31ba74f12986cb80d66170269e8fba0845595af04c22de274cf51b3a45ae31349c2c5c3ba49eeddc19f8334247b905fd17bae329cb3356e9b0660b1a2c7f7999faa350cd2d25168e1c04259366f10581b23843f5c07201565ba3a00d5801f844e9318f2bc170c62b6bc81f21dce28f1876e1124839b3f02ff372682b0c9fe44a8c6f2e3d42430d7e4f7f7beb6b4cabc4d82d54f1419c6cd90aaf23fa836ab1c21ddb39d5a967de1c491d31823a62b8acb9d64a043af0c6e48a4370209195ee45a2d9ffc11b4079d611050b72b6c595f6f5466278dcae852a038422e14661a814886d11859d23552bd875521b18d07c6d175bed032ccbfb85ec168d0e110dfdad17cf98ad59e226887310d3bad299343fda767e00e808bc816b1a413c6b98267b127097b622405c5cc3f0781615e27afdbe4cda4273c34afd296843568fdd26d2ee8bc3e9b259ef1a41892782127e03b0fcc646129ca860160c110d3d6c3cbd660e9d16409a11b641ac7406448d7bd9a09903db5245253de40c05fdffbf43ad88dc9d67daa2c8b07db26b0e18acb9ebb231d90c6f738e2b63c5c1e4dcaacb4aebf8e8560b18cc1d4ed3f353e41659b9e609cb5a77eb793e84bc27f1c96c1b1abd28375d01f7c7964d0163fe6fef3a6fd33b6e80a9fb7c526031721dcdcd2076cb92b5cc9795a7aa609086e7ce8702ccfad6549bf0e72c37ddd9685f560f166295585bd07d716568bf36ae027b1e2e0025d8cae29797a09007dffd2bb5421c512f6325d0096206e08b760c0a175dfa125024b4d812843fae166d2ad80116d2fa7e85e15445d08fe9b885d0332f0b5e3779e823369a8ed4c336235d853afa09e54157385c647293cd9fbe2ce3900848073110fcaaa04fe1f8277376bbe809920e5dce0957d91221d271819149d986843068b369112ba1a4139a1fa5400a2dbf47c54e11678ce8e52834b8fe3d82089036de9436a051682941543885b6d12e35ccd3323ada7db46e866f0c3656dfb1cca2298520c4a9ace5e1cd8943ff433d50a70b70c3fccd6c01efaf8851798d1e8582a92b1b907bb459bbf2077b6159ee56643a00ae7824f955a93daf703dbdd493f9f6608838c33e4ee0700a94875aa5e0f58d2cd21b455d888f6089a17d8a33469f2cd3f8580fc063841e04705ff3861eb01a33c823828a37f5bddbd27ca07741811596cc06a4c31343e14a42d1aaa92745a6170510a9c912b7decf0b132f7cfaa3ac1c2b1bf6f1254ae390771b1a8bd8eb34fd7534cc074495093dd81cfb7851a8d9c3e8b864567e66716df6277e287cd33d580bcf79ee2dbbf147f01d4261c8d41f1c8997060beddd0c95a2883f6b990baa5a248aeed7b0c50c5c8d9488986553068751e851ccde4cfd1a9840a084e002b49a0378e26babbdab772b1cffbb1ce5063f3fbf3ca6e09d4323cf2d104605374c1d459bc1b6f347a64acf819b745295e7c72050dc40b750adb809665931fe6626201e12237c9d07d75c384049198e89110406ea2dd2d6814582ba5ea39295aefc5dcb8f0bf0ab43100b740a188e2c6701b12702be664d8c7e5b02cf387a9ba7121181f5dee6adfed9ac178aeefde356fce0d094e4cf162266778d743a19f3281af1562e8b373ccf78a5eba25f497c92fdc8aa29004ab5996095bd65581644ff0dbd5992abd10172a6855c9024517591c12bfc2a1d71b03c7460a616902f6d9a860825e4f5cb98f7820f5595ea6de2838b32c89e9bd832f6fd7cf288ba3921e765f44c262f9483b430851cec005a011d53ba3f2ab7c8c16f3fd7df682889fa5c0c260c77bf32bdc475bae32483f756fd8105f9509e97c33056e9bac8df9198a540e7a498794a1608c7eb7e31377ead9a102389b4a1fc7b4f4af1a42de3d5e29b6d642cc6da931d4fe8c51983429b3367c0b0e2372e1e2e2e9611e0e1cf109e958503aff5125ea0e5ee9ffcd1c8bb7ad0c04a3dc325bad663b465ca819e7c6e3305adcf878211f1093f3085ac580a816596b4a954710a2066a74f40e15e2f47c1105c4078aea040c36a5126f3d88605698aea893492a0150081a02a842bd6424ec4ca379962110cb1828d7e216cc09e8ad983817073143a6df53dcd6c89525eb566c76ed4ffb436dcccde429540eac7c3621924580ec8bd409385c4a788228239fcca46f03fc9dcc0eaa0cc18973f778580642252d2219776e589b5508f6847580b8f4cf41607dbb8334772de8b61f7f3c86dc780852ad2c450a85ee60ebad8fa82962c9c95e7d6a36ae805cc26c5e272a1e2a2a9c010c0a8c4ffe8093b098b18a41446ed0ba8add97d22e8ecc4f834130c8a5f8d05f1694d81fbe9583d5b70bbe81e146b4db1218381a14e4c613f32b862373c87b023a505891934f1fea109b84af33b524c2baccd5deff24c2e523b8b8910036d8a4ace740cef7d953d796404ad5807b9e9328bcd7b04320c07ff02e5957a773ffac92d396e33ff33a036e36c0b58bc6fc004f6db05eeeed9ac63ae65cfdca23ed7f6c5f845abc0d415405792a35599151619a1edb9d365e574e1a3b78a398b42cfdfb2a12b16fbbffdc8e3c1e1e1191a386547132cc8059f28838b94f72af733086a673344a005a26b7e62aa60ce4a981a9aa478c574af09041fa1b9849e31567453a4090fc678282ac30e484ba213708359200db7dfd9ee5d154e0969a06663076c94bb43ed61666130fc66f29c8fd53fb3227b53993a0eab6733852dc0a7eb35cf848e11ac4cad488ff12a0f60b24be4187b0d4a5094cd194c7b7f4217be507674e971604ba674c30258a2daa7c23da7ccc286bef0f456e19ebeb109b3f756b897ff591cbdfdfac4816beebdc21d8a1b0ad0a2467247df66d95d39e672cafe4ae4143993c3e9302213b022835e0d57c3d3dbe1c2ca0fe9ae5e04903b2ce6929bc1ee50fe10cb55a4b161697ab76f897b6fe5f9a0d64831911be23cc6c183c5c7c9caed423754a4f2bdea451232fb224b5ebea870c3a2ee8d59e291d46609519b63e0663a8045e8a3071852beb5e43f63156a8de9800185012a88b27f1cbdd937a2615c80f336e9edc591622f8a4d94cd1d602c0f08feb80b819a58ad31d2e13bd808982eb10fa6b0b4ca10e9540f419abe8e9ee64507a7269862bd9b55c619377bb255cc6820a20a65fbd31df08ad0d19ea64682cef843ed213ed8b1ddef95de175c3b83872101d8b8aff96e229da728a30203ce68c50b235bc25bac71beed7135c7e91e7326688febf6414c09050e616ab4d6f02b0762e89a3bb85221726a4cceabd090fd0598a06bbec9745b79db134ce529e79aa1c24037fcd608d502c519a2f79f7cfdc17cc600d8230d591f3485069537a0d6ab59cad7afb9fb0490775119ee617dbc09cc350acfc9458173f1cb2841e57323601b03a3c964c1e06570e542a760062ee2fa41be63a21e07d25ea991ac8643107ee0daaab6e6d1596389ffc41ea933e503ca20875a8ea83d912b6630ee0e6f201975ec28dffe5243c89e86e4bbc9edc6cfff2f5430f2bddb7b3a23cb6386800a507cf4c01d0e310e1d870bdb95dd8a8cd722822847186a507b36e70e46cae9690f3d34441962d91ef805baaf9286b79968069a285991ec7d09da14e6649a4e14d25c7e92251dfbc4ffd607b4c226ddd1c63e71f83c0bfb1909be06202b8e53a9efe7f28801cc318544a2d7836dde6ae3a2253638475a12bcb8e944021d68f6f709f0a26fb1a1929e3edc2bab151833219be6a2d3d049fd72306293a6a25f8a41d34537fbeeb89c4013ea28415c09242f0814a711ebdb6c29aba708647b0148bc71f374708622a32537ad74e940c892c229d401a1e58c4ad824e468a01cfb1b4cb755063250398408344698eabc01e6b68975e50d2d541007e251785a06a5b605bc01a911c2f35b006ad0c52bddcff3ed04a77581407def90683a55d8d56bcc02139b0d7a20cf399de5546ddb63299648b3f540335239c9dfe4a0cb81cc921e41ed70fe1b78508aac24432455d96789a66f70842f03d37f5cbf80ded51e84d83f46ede09a4c6189be0172618811e5dcc55869f60e0f7b843aa73ba6f835930c799163f4e6afe8b8b2f973fed1b24dc26523d5c214dd1c8c8824de3d288d16f67e15af52d2be919295ede94f44b64b4cda0e9d5ee0e2a3ed8acd7ce9b54e109adb9c59d027aeb287c6cbd1781bcd686f0b97aadbd85ccae54dbcd55cf2ce2ab9f36ae09130220dc86a79e1ae81dc41cc182111c14598142a5c9866b82534129b028f61a5b0eec575e7f2348521bc6364c2a8d4f506a4ef50fe8e3c6814cb08351e622138dab705790410bc487b25b50f1dfd3f2a4cf9a61f220f6a6bcf33246e253266d29ec8821d994f6b4e05959cbc87009c1a9ba5a4021649fce6bfaef6d986b5bf4c0a3422bb2ad204a27cee8be102b843618eaa97eb64cd2c297ac1e601b49a7c9d074a1b95b3ae93c9f7e30baa6672a8fb263ee46a939ae02255e5a66665bb82d80bf260efa745cef833d400f2bacc3776f3a7180d7058e3ff0d844e2d26800aa68a8106a90cca99e1a921e8ebf5d0fb6f77ebe667226972cf7144a95f96b4e19be60d458747b8e63ba50644d2fe41294637cf9ecbc89f9746f6df6d5931516a0a2c434f0583558f05e4e3994641cb0504514e7b1dc984ad61b9692b6021cb71ba2ab247c727f41015a3b3d168a01e96546837a9224b73c5cdfacd40e242b615652964634ca27186a92da899b6f4e357423c1991c5fd3a754f368a0fa5f126eb9adffcda34784b8938da5c1055ad0325d444fd0c2eb9a982a2c37504374f1a87508495e2e8dca759b2a7614c0b43649d48b3a463662e217286c8f691c744c1c8ef0260cbc65f3213cd405ac2105b161f2f23c0a7283679a6c0daa23cf04faa0c8405b165c50119b2819c414d5a3ce8fef286c73189798d31f4e8829a90d7d8ea927efd0a5388d83105c235174e86ac4ee1ee891e9d0c59d2d2d998245a7e1f80d32c40b7a7480bcf9b3b9727d120b86cabe84d6b4e50dc377a854c917e18698b411dda16a4d2efc96ac60130bdc7db64a358c32d10c289885ac81ef9a7a6461dc89c7882e0799bc75dc94e3096482d3beaf6c7eba6fc6258d9bdc45124126ca7d3a7c5c98f3ed9961a74db2cdce1fd09dd89efab08b5df624b7ec2f39651385b56ea7e4cfd925b54a4cb4cee71ebc70caba56ef7d8ca6c8d666816b49c1b321d4193b6e38be1e13a7ab4b228b5a35ea1a2f2e2261abb3aa8d7bc3a4729168caf152a60d4e638bde4f4c61504229ade1d3a329cc83f48cfcf352b2137e8e5ef989bf09158c2fb09e76dc90e9444faa2403f1121183d99e20c5afe99d47ec2b74c827ef200b3ba5acb922cc1357167ded5f91dcf9b5da2a710f4f0b2c57ec3998875f5d710a1f0be264561bbf61480919168696044039026033c83fad0b16835090bd61422b78500d6fa325b581409e503bf4f9e3dba5996cd35dcee4d1dfd01b402a5af3d6cb6973c5dbaced8f61ed071ab0461cd208172591b43b75f45b9c468f17a580f48a1fa55e4e6ea11b14adbc5947e765b7d1fcc0295a37b21b59ca52ead15bdcab31253bf72c28025b89ec18f48be4b874c04b462337bd64a8370e5c94f565e6b638d092bd84e53f2f906d8aceda7a0c6fe4c8cbedb371125fa50a08b002c05989d61742f2c7a99fd511aa060febcb7c8e6a386b6ed3a2fa2f74d899431c91f560f49217458aa8a8575598699ca51b171245ad229d1b346cbb7bfeb052379267084dd7688c05935651ecb62ae00de71d950a212ce13ccf719e6b5108213f9c918f3d42bc4fc58a032ae4c9fc636883b716cc6aa9043b297f8a569464772b3a0a6904ebf0499d7b90e88a0f848d3876aae50b89727a962167b8efae304d6138d27b94ee6ed25a7a8241dcc83c4fca52d74653f48bee8573b89caccec1debe8384012a8d56799963b6781a4e8cda28221ee0584c88ffe1072f5368c695795a70c27c11fe556cb02875228f75962b7d2f1c01057b3165d9eea690a6ef3de57005c36f228a808f3b9a0aceff3115837754d2574bb6ef728d8b90fab2f0a5d0aa07cd9d1204ad103abd5f79cc06ca0fc83f416e60242299cff13f7218baf3540ed86458526625619b038e8620513bc51996825baf1a85e82bf2cbf74c5212453cfb37bc24ec426ee29cf6925916a5c01dab27ca3b84835b2e25926e917a694b6595168e4f949d3a7a7d65212673c53fa24f734d375cf1b48d552f4a00e65bddb0c122f313deccc756a876bd75cfba40c657e8b3ca87cfcc1ddbbd9bb94d33cabc114e7aa2e694a63ac82b275e7c16875658e33a71a079509eaf50a25ffe28c0aa966175554c3fcc56fe5d9d76b42afe772c003d6acaad36e1d0c5aa379a1f81b3d963a95f0ec38753f7793c4fdf38b842d78be26747ed5721d2e5ebbd8c144fa2ed63b253fa4b70ca318913a6ae1c14d658b1064bdf289189a1c1679907ef141e8cc55b76b003e1b7b54a7f43ec5bad698b735f462f86148eeaa39fb385b846d8369934f07ac6560b5782370a59145ba6a098fd4e719a89389f4e1bbffe129bb7a97c2a1c4bfbc738ba0cde08d0662f68309d16bafb04bb564526f3b82d56ddb74c4f1ed2897c002365552935e3025f8b444702679c84ebf48a0b3009cbfbb9f76367c3477f267197919ee06c80b891ac5b4481351f3467663dd8fd424de7cd577fed8953d782a05c801a8d93f618123930b77461bc3c4839ed1383e4cb79b8188ad6833ca34dc9d067ea7bf261fb033b2091bcdf132b567ef3873b446e36b75c0abf846c9ba23ecd0ce4d4101de3eb9cd5e0c56101d77ae5a6ec190da7ebf8d7d0f46332f9ff0875da7569ad5ebcfef8a6097dd1ca260fda68cc9df77411a5da62163a396e059bc70be6b59b0c6df3d433ffc0964df331a54586364fba67d15b68599e9d698d72bbecc119f5632aecbf16d797ad11970e433ab2dfb748a8a5b8bc4c583fb85670f2a6a3020a7daa56b672365e7f6ad61e644f044780e559eebfadb7ae3350a31dd39cb46b52689ad896cd78c774a2b0b4f79fbf25def4eb8db2ecc76f2ed8a41be89c8c913eef1eea5064aa73c12e7892c3355fd28d83a7afa614ed6b9826bf9663f2b7b89bd1592c7da66900a7dda1ea10ea50a756b8b5ff9451828c87a7a03b653d10e47585f2483af09f951bee7f963d6c10b4fc094df1bbf824c5751dba1235227122635ed990c28c8b544c020fc683927789e61817b59136bc460ffb46ca4f165e8729f702542b2fb51e1b0c0cfeca27b8e91560f9bc7a29a902bb4ad18712d5cd9323919e578122dd6edd5189e940694532a15e2468e2ff214d7d224920c231b7bcf68b175e9cac4ed34b028eadc5b6e853dabc39acce2777190f23c2cce857cdd24677da19e10560109919bd46294246e7d6f7ffb8fc6543fefcebe4e7b86d0425ae96f180b46ecc29d0c57806e28b7b82ee03f67b5d3074860e2abc5ed9d1ab9098e3bfea5df302b2d58d300fd7c41e70728832e16092f76cebe96a0b583e641a9b204b172ff5ec12656682012099edcccac72725190730067c29e8527f68b156f21246c344b68cde6d4b4589fe9cd7c7b6479825ceccc8c2381be9c31f4e915a92b8fd5ec900da02ca177f161ec47ac76469883cdc8caddd03aa4f4ff13ce1454b7c7fc20502bc22dfcdd5bd59d36fd1858372305c24d3ce12bbc30c964f4116ec960abbad83b66c1f46694c32bc166a233b7f9613d4d4e5d6324156465a58b6414f1160fb106e4f8b580a28702af9ba9ee1fd66e79223cd264bd6bcf4758065c6bc2d03d03c5845d0e27663690702825d7b78a99d43a82cfab67c436215cdbbb350e7bdd8b2862202d652829a08224d53cb6479cf9b45231c904acc25d401b98c2ef11cb3b96f391da6d2235741e24b8ac5e3d17e6a2b485889d290ef69cdbccb80c60fee02097f7cd9247becde3ef3419a6074abbbb7d5dae3912b1f98929ca60212818a8a0f3865f166c744e81b09f26637bb1eebd9a63b91883d45849939d83e21857fb2d361088d3cf8a9756ecf6bff6d38ce250edd8ffa0328e393d88f8773e4c5774604b49a34f9569bb24e9aef973b76866edbe8dfca1af3b47d2cf458dc9ed6e8d1b5be34e9931182bc1334cfdb264f43dcbd6fefc14648ee208748f4e87652fa908c4bef61649051cdde81a929183bbeb8b72b04d946dbe79378379cb55b39f22cdf514347fb833cfab441d1085d201d409b308508aa9f38a33438723c228535563a48c6279abb472c519a8b5bddd74ad95c16dbfc943ff592220887c6da1e83cdc75d373aa13e9f099ce2f9fa62a9ad2466382a3a3699f4c0f048886263691d2a4da61408eea362d292be75f7f434907b2b408cfe62d8513a8cd97c57a2a8161d6b31f7cda127e277623dc50ac90bc46f16cddd5a4a1c186e256f5fd8e6306f7f7286b5170c92687003bed74083ed85c5af4b07020cbe566f0eb4272d6e82551d0395891738c19db83ebc7203f9ddee1bd0ec5c71222ed8b705ec3765e313efeb29d7ba42d5ac703f658daa29e83b7d2410bf15d8318ef1678be1fd9b1879a15031fcb4d0defcea7d35bf54b4c36ecb38b7f1eac3f18e19ff6495c254947b03af87ab2e8407cb687526db9c70da5ec927cea2ae8d438e5d07cf35c5c13b802c3b7523a15e4b11a657a646c893e9fdbcf08546ff4a2d4ce9b7864d66690aab075a77e35eac4a487614ef631fd2ca79353a6a533d5c68965bf5f89a69e007356a0871ee5967ac24c0668dcf351caa926648a14f0e0a964a59680c9788d7d3eca3ed1f79230cd58b57027ad5fa6447411127f70137177b287201dfea11a623c161912733121e0940644850683818c1302c4a0527f9ff17a7a473fc6357448bc8160e59472bfc1780b32283b113261982f7c9f4d472ea36a8df3e39dcd583b0af11efbee6ed9b909f604d1dbaa9ba381aeb828bc3e98fe8fb83cf46eb67b569e6f365355280c7c0ba82de7f643a816119153b9f57e65303c6688a8d5abf8ceed35d1a935c9acf574b9884de1dc557ecd96b99fb3917a189feebeb4850d747e774dfdb0bf6d6e9717cd6b2b4c7b93b2f9b1a257e460797b98cbcc50d2b9c437524c2bf50d1a0b253fe7d38b1547b55ba2da6a5462ff2f3d8bb56683272691621948b4994d54c24c840454722658c637418f4db0d245a712e6b3e0d8e712b3abe905959cc52fb1a7908dc564f1dac1a82f126df51f5b2c7a359471aa6875c70a1055cd3c3f9d163cfc32cf8813f59c64ae43bc00e1be40da9418689c321767aa9486d9bcdf997752d0f81be6318263263935cc8d2c185596a854dd1dc1aa9ca3b3fc09e9fcb8904ec3908eabaef3425c8a7b727752dc1fafe333948fcb230fea39e0b1efe33abb8b187b391a666f1162124ca6f63c31bce603adc2f391578b1ede31bc45151a56a5469e4df47e3d7d51daae27ebb7e90c043a1f8a5ec0c4dab0c83d78644b3c4b843d22b2550c6576f77551a5293a1c45abbca705e4f55c74ca218e780162e9370da33e6230a250b7d9f9333707bddd0974e63614f83bb565bdebec08ea6fe0ae8cae738670255f38961bbddba7e0b4d187e02418b532fe08e7be6d6eec75818ba08d4029c7bf7aff3023aebd375915e45fb6c657ee72675bc4d91b1f811e477361bd277d498cd628e7ccb5144d70ee8316d4f019d9e5208148d5d1c6f285eea25b60efee08978ede9c30148f261910e4b5e856b0bb039d276d67d3f200233a1a2780df19e311bb539c67ecce8ef30872314d9bfd998bdfd847458839eb3efa7281a178683e137e05eac94d8b8a291795dde8b48da9fecd60abe3ebe6a92e1e8174a3a1f50be9a0b7807b1e4de319921e6db890aaf8e832ebfc864bd35dece743a403e4667d2c3cfb3cfa836a1f90b6d7ef65db393d1e41fbe84bfeb37800d28b56fe593b7a77689fe718f6a2ad2cec28f5bbdc095cdf98212f26f292b4e0b37e749f6fbe736f3b42dea1cde70535a17ff1d6b423a43734fc862144f4e706c1c7a3677c0cce23e418da4c58cf435d3bc277411b2b00e30e5db801c8f1e89dfd811658d3f3326b40c261b98e660163b7235c3a3a376fdc0e349ef43cd377d078d0f044e349f43cc280863e1c101e1f3d036cdd8ea05c68688141f6d07120e3f008298696e507e711db21ce63e279848fa143c60b0946cb805fafa166d64d5d70793b34ce8fb1b4d038e0e26e7cf4cd848cd6d0dcd2858eb11b54bf4d8b57286e41f0f6a1e835af141d2d3e5120ea48bbd95be26a8efdd748dea9fed4768a9e67a80e4dc400e0fbd1bf1f9b7784bca17d40423a4ea0b81d41e2e832fe209e319e6379c4d939ce8e383b3a3e3d648637604db299bd80801a3b5cef54171e2675a159bcdd15dadc10571b61300af734e0c30e771e73e3197a83265f1024317a733f2023348efe713b683b949e90f4906b36b28daafd453be8adffbb39cbcc32ee401ccf8da707c9e635108f57c4d1640052df11d40dddc2dbe1e116445d809fe182fbeea4f1f4f4109a51e9b3ec0aa498afd9098d1ba97c2ed91578be69f42418d4f54625f001145c497662ba045224cb7a855f1e0b44599fce340b9c5d107c016920b2cc3da05f148c0e998111aa83d144e6e6161a00b1d813c950b8d3a00f3befce33881b5a7dc09292281194633e2e30b286de96f38891fdc1c63c264062ea4746a9a690f0ce487b18828f26f300cc039058b4ec5fe03b4262689e7d4140d13a1306b21ecd02ee79d2f2a03feeeaa5015c7a02aada85625f400ebada1c124ebab9602720574245eb2c197f0f7619c54a01e371106cd828e44f66ea7dd698c0990777e16341356760dc143774470a0fc877803229c2d6b9b97d0acb503d38313524f8a920c726842fd030335bfa6bb6b67b656f295392328f06c505f80538ddb3c69eae7b2d89e58ba4dcefdfe16bffc50d5442f3cb09af4a406a53c2c8dc56255c2e1a43a61583f537e63164c86bba8ba48e13d3816981de348c50dfb0c60f068ad3df1a3f155aa03a9f0f0a23d43a9fd93730d01dfcfddf0b74c7fbfecf850fc8d702dd397dffc702dd09bfff5b81eea0beff13a23ba9efff54a03be2774ae8047487fbfe2f05baf383eebc7cffe7e33301dd89f9fe0f05baa3fafecfa43babefd4ad0800fa0e5c5faab2eb4bd627eb04b8c89f9f1bdf81eb6fc0e840e6597ebadccb77a07641bd081ec05984f053422921776fef50f657e3b5d19c7ed5789fa039fd31e3ad4273fa61c66b85e6f4bf8c17e9424173a2a039fde278a5a039fda9f126d19c29684e7f385e168cf7ca7895c64bc578b18cb70a9a736b03f4cb8c940ea064b9465a0b50aab8dc1aaf1414a79f35de2a28cead828a49fdd7e631ea35bbd145be368ff19914a71f6351a43321d8b75f7e662783cb74ecfeccda91a8332594fb45a99b72d25099120212f403e627a829b7ff2ce52eca656a28f7a7845a07484ad63af6574c3719088191591f557f519874a7b319c24608d149fb9eb545b2fdd216c9d6493ba339f65f4a62c7b2ff672e0141c0df3df56f970b77ed79feeddf96c434e725447c9f5c439e7ec9b5ec21459a4373684cf75b721c3b84bde0efdec6f7de7b480301fcddcfc0dd47628fc41ec2441995ac34a73e253ba5cae5061b5c180a84700626372037701c0e1676a18c4ada2e8481b90b7b54b9bb61c0f09a7cb1207ad9bf8a3fda1fca7e076a17eed27c2097607d6e7756c0b74fa998cb5a6136dba714c33cdba7d4bab5a50f4c86153ff536503410103ff533443245ce40c1b80ce3b2ad5d6e7f157f576badb576f88055e61e6c0b946b7777b787b0ce9dc889327d7777cf605292b5f686b07b2bfd7b2f17c22c57691ce71d65cae083f874c717100ffc3a0e74e71ef4c72180a9d383324ef5412199f6b8f846e87191b9ef71912f0973c1d5232edb91cb25581fb04f26681fb423089d28d61084191c89ed58bbe4fe14898116e2e7b276d86fdbdddd2bc5a8fefe4ed111605ff6877da9da2597d03ede0dc448c70f1d318e76d65927a48572989e236604c4648192809ec0464047b972015f29e00c1ae988656aa4032805a3a09f4c8d762ce5b2667acad46847923dca2536da1165492e2dde8143ae6f91e4aa04be4ca01a5e90703f1c947a6d5e05ad83a31a356210e2043cd0259ae022c396184e2092a9700e5ac40d146c20b285131f865e38a20610a025a25001900a493ce490c40f45ae48429348c107a61b2cf0218404c681896823b2bc60a1a2c5557ce44cec682224880d2b53443124a446530c40f078c10aa0a0d3161ad87ea5f1946bf32abc2cb054281da7a5d3516ff6364b824587581055b0f8d902145382c3cf134e9e50e11bb4d0e81ac15e45119766c404434ec47000b24409529021b524494ce0a082aa47b4233c006184861e94e072c30f41b4d80045902449aa601b4d6c8c54a172c4b9c4d9c427c713264e24c9e2871e8452f8e7071537acd4781294a3cb064b442ca5c084c5cf960e9bc0a980b3491ce757b84b23d504e457aa0b494c59aa72c414294061012d644347b22041c50f5c8389158fc20a2f0b28bd81207a3861e6044889110cd480c20f36a85c9921878d216e25c2077bb5e8dbc4af28e140dc2c3fd40b6c610e09d2e4ca09a868b04595242668246d190224f452a3f870b1161fbcfb4941876940a9208d13fa81bb6171162072d75a92b0b4892428a832040c43599ac8c854703651c50e39c0f08304c78460a249c9d18c8a8da2262be861d6b8a2040e1c3e5e5dfa2748b705cd46e089134ba830017204878f0a5070b9c28622686c3971ac00834e0b201d5250ea8c58972cb5139829c8d1a5c98f10569e30d9f1c20f92dba0916a22a27f98e89494b89f251d978ab3f8144142058e169e0831bd90812c56aa285d8962cb111dcdcbe2da9b6b4169f0031228a840ab626885981464f801831615f8c023c4c2070c1ba203a8052e437ab4408740da218829558490c0f1c342ee7e5ee8986812b79a92216a3728a1f9e850628b8188099ce0928488fef9c15d616f13bfa284fb152ada924a7458bc608185254a6049c18a1d4c296cd1a1891f2c388ef8a9855ba0608e0248a860021796907c40c15304152f509102275368e9b47c416ca0b8bdd24de6103418988ea098c28815506034c3b964850a40b1539d42b358a078341fba133aa3dc75497008aa2d55a9b14495011059e2872b6e4b82683061e9bba52ba5895f2182c33a6ab300091786fcd0928314409c004b151b88e800050f364cb0328152034e830bf11adc06bfc18774a3a7c055e044c61b303af0591ba0cbacd169588d4e8473222ec46577a971fa9b7ec420d78fdc408658d9c97b7b25114b6ec6cd401b3e01ec077b420e022e973d9d5ca58261d585596b533df6831f1b608ae7744abd987821065a88f62dac69ed1026f68d47bdda6e2fd779a8cb5deb3eb7da25a0d73284ec2f2422f797a9bb3bca944473776fa1249ab596e7de5b56a1cc71254de2e9ba2ee4f1f19583d87d896d4f7d9c6b7dcb838116627d1cc260883d0ec4dd8cd0b33aaa23955a628c31c6b51e564fe7df12f0d5a45695d3701c3dd5da5ebeef96c3f97eadaa1cfa871edc6bed75715ce8008ebbb91c8571a177a1073487e05ce5c6caa51e70619501d8c35ef5388e23bb4a7d0aa66abbbde109bca58f8dd383a80f7f062a3c815dd7751d4df71c89bfabb5d60784997b1a2d298988a8b6e3e0ee6eabb5f612ddcb552020ae2be9d1110ea2ffbdd59de4de23abf71c345d65ee7b56ab55e6c83b3653d7755d2ec1fa787d5a72abb50e2bd3a65ea2b4e9b7948e416b1964ee7d5c658e0cc1ff792c4fadedf6721df63ef014a252e20b4c8c6ac56ab96466782e494373eaada139d5c5052fa8c7b8458dd31f054b11a95ca4abd44db9bfcca06fbdd44c7db3ca4ede9326f6d77acbfdddba2994883653a3a0999238013153a3a022db92f83241a422ee406242ed8309a032350acaf104079cb7848f10142088d040c30d3628e34f14447218e1b0224416bc03d38bcfd712a925903469c1131b53b8a09aa20391293f4c7162c5a500b18e0db2e89436c8029f1c785024e2a31e9a74367ca483d624570c00ed30d211034a656aa483c8db3ea8b524442d443053231d4347cd199920c4a806d18b0ab903f984288558d21ae98458ba4826a2abc318638cbb7775af2962496b1f4cc0280714398e7a505a6b63dc360593616dcb706df5cf5de4afeb3cdc7dd67ab8b38fbbcb6a9bbf5a39cbb9bf9a70c796ec5adfd42320b52959b79b1be872e0a5716b1e1697c647f3d145fe681e03c5e438fd29a0dc5f43ddeefd1275bbb88b8921f7971e164272896242dd1ca76364aca28ed1529a75362d2d2929e532e6563fe6d654722d636ea75cc6dc727fccad75f0f7c72cc530b5ceb5d863f6c4ca0e4473ea17a571faa3f50dad6fbe287dd3359ad38fbae5ee4fc55ae7b684626a9da67a6b7487c25ad9c9aeb16e48fd928934fc6719671afa2cdbf711d4f27d5a1f5727017eef9ef3c7ae24d62f6f117f3adefb99150b482383fe7d182561fef77d6aedf61379ea9beeb1d4bb5a9a0bbadc3def76632c037641f022dff7ea22cdc13684d94b8e34e786308e0602dd733fa37bce86c785b0eb93bb106629c5def77da0e77d3ea37bee41e8c80d36701cefbba0cb05862074e18c8ebb27cfbbf7df65f45ff05a6b6fdb4b624c73ea7b21ac33a5e017f60876f5248aa71084eeb95338a3e3480ca31ed07cdd3e68db5a6b6da518651fd681da25cbb81ff67cdf08b02fdb77d897bb64fb1f7d5818f67821c5e125bf0f644b7609d6c7760a0b104c51cec31e2ef7fbab05b1234bf01bbff7f5c26a17f0fbd21e514a29a51e51eeafb5d65a3b29777f777763a12477b71d2df75b6b2f53eebf5cc9096122bbf1358388df3b752103ba7c7a10a4b8fbd3e37cbadf21ecf4e07f47b97b5cd09c8f2c3fa07b949b2cb9b71cb82d4750cb1ab8b3dc18ac5e10608aa73eb5b35c79ca2a0b796a033acbd3ddedee7df6e9d6f2dccbc3f1bc6a10ebe3570d22f8c1befb6b8f8b6c493beb71919bcc01a436e58b53a7aca83575abb5a66e56a4aed09b4e25b146d7b746197c84de7cb6b6d2d65bd2da179398d43a5748a7aeb418ba9ef53664be858116a9fb29106672a038380d9bc981def4af72d09b7ef0c2c03c751aee5defd3016ba4e1481a08b0bef53358df7aee5b240929e0c819ad779124c87ceb49703deb7d3a98f9be863caf1844f0575f431897fd61c83ffd0cd1e9316460bef4f2e557a3f8e1af805626ea29ea86720514435e22ecf27b04a66768a99b08d515ae80f24c0e33b572c696dbf37b43a537ddf7b7464c033514413204511445aa20736082bbd6554fa6704ffde5c48dbfd31d9abde2f8dbe4f84e00f78ddd2da6f4bbbbefeddbd752184f7a4cc1cad4e8841b60fc2f0daa436facfadb592d1da84da037de75d6e4fe5e1be87efdae91d7c735afcd06d44e75ae0954a7db4163add30101798cce741c7f8c45d1d5404e34e20bd43af7eef8c9f59d06bda99dd9999de97f7d98edd8fddddd3bd3639c6e965ec9d599e6e7ffb2427c7949a552a954ca8459fc82d438fe543092a8e45afa1b4f7567768e7cb1f58dff8b26962933bb2d823ae94ef72e24970e0414a24075283d2175e87fb295e18fec4dbfe4521291fabbb56b5272a5f18a22d6c717645a1d46d4f1b2416b429c2074020b65cd25353ae128d7649b1ff1a68e22d84ad2c8e0727d09e43a8a706d756a81dcdd4307b1b99c2768d10358aba95a268b81395faa29da52a84cff1544d75a27ccf46bad564abd275cdaa86cb7873cd696274a5d2f8ab93abedcbde0b55eeb4575a41d5f5088cd952073b47540da3adfad35e4f1075127b774c7d29c4c3106f1a90b7b6ab63127db3a351071cba50b0a2c2f1e4aaf180b8c48d2124515a424b13335ea51a547133f5ea6463db2182dc0c813376a38717204102366d4705879a1891544c46cccb00d22af3661a386ee824c8d7a9c3d8ed46acbfae6a7ade6a5369f4c23a396363b449b1de2a98e363b445ac9ab04529b92abe5ae6ddd5e7b6fb75b7bafe598ec926db24c3f3766afe5ba6b3dd01b4b4b2b8a422bad945ad761eb7dd78220088ea515b23f805821702ced909581e0c986a86b5349484a494b37f3bc65f1050628a65fe5e6caae56de750596be84a86fda9ef67eef36b02de781610a46c592a119552ff6a6a5602a4a3dd43160384e3f1e2222e925455132fd96d5a58e4181a6fee9205bffa05e51c7a8a619c594625231914ca54cbfd27a9e15c85987ce3ae444b22446c54ab7a61a4481d6e19eca4b7145116951066fe3aae99a061da36530ba069aa7d3e9743ad12fc5ec7ab733f0454ca14207bdd559749ee7a9327f98a669c67030b31992d96c368bc57e62b1582c83f1020a9ce6e9743a9d4e3060d4320c183040b7790eaab0833167d1e927789e3037ee4768ba099a9e894fdf6c8664369bcd663918d8f81893ab699aa679822008822005525f62d17313a750e1c95f70e78967d1799ede99024df387699aa6e93647729bdbdce6b60000000726d7d3f33ccff34c2a1849344d13f7d9852707531cf6ceb3e83ccff334cd1fa6699aa600725e72bddd6e18638cb1b5d65a5bcf7ad6d3cfb3ab372e3c39e8a138fcd9ef3c8bcef33c4f1827019c5e50c4eed432998cb4d65a6beb0de77aab95abb2efcaaeecca427bb337bb04da9b676fd8de3a7be3eced8203380d800004288006a702801a20c9fd0698f58d017474bcab0e46d1c8f06aadb3caf453b96b37ff2e9728f6b70ce32f45eefbaba308b6bc1e599e4e2a1559d6d490a58d2c67902f2a77cd635c2a474836a5a55b53873d61bee4adf54dbf8d0eb1e462314c2323db30d72c002483931e30bc7e7af498e18a1e3ff9ec41a3070e3ca0f070c223cb0a8c101989628a18b024c9e2c3d0132c3081a2c60e30f0900248096a0cfd388204171db67021a1a5c6cf96169c535ee0c184940f6a8d2c4204a168e18447112f1a3c665c7808e14134a529484b1096a0a4a02a414c0449e1c183474c065a7b3a84d10602babb4ad527ea35553f04eaeede5d51c4d873b91c07732ad5e974dffe17cab8d75d14affd102eacdf3e18cae8efa9f4fd61d4bbb00787247c2800b9ae03c10eecc0beb9e1421ef045249638b3f20a44902c7d96bbeff083f8c1c7a4ed063ff8fd89524a2965ca5df7b5d65a6bd3babbdbaf50eedcdd6d52eede5a7bbbeeefe5ba0e245f4bc0f1b50415923438876439420e518f338a2c47c8e0b7873252dc51ee1ed5819c90dc757e94bb92845ad68075f701da98985ac7df719ab5faa69f20183f9cba7c6848a68d4120405adfac46ce098703c5e90781a03adc117ad3b4d6e1667de384ee34e198d09deefbb9259c124e072e078e88c381ee10790cb0c6ad804bc221e18e7046b822dc8ceea840a215375b7dc9ca2537cbfddcd90958d5c05a6e0e655de631b80c3e830f390a3c482a9bf802031313a3c20faa562ccb62795716aeb19cbd35313bc0580133f472c4cb0d9a44eebf20d8b28fb4d65a8fd901c68adccfc10cf5cb112f37c45acb5ac88ce8a4c9cef33ccf33f78b35b126d6c49a58136b5475a22b166d71d9c5c9338d94119415d48fb0871a24f7db2f3c8156a491fbdd73b9fe4c19c9fd8df13be9ee1f269d4459c97d4fe0e7a81f610fa17963ea5803992d74d26a47b6a4d56d755bdd56b7d56d6586666886666886263803660167f8a4542db9dff37a9e9ee47e115b6bad0766c9fd297086dc5f3f29f7939db4da912d292693c96432d927fb649fec93812e994c8457031fe9c79f57d37a44e47e54079298c45f7a3572bf8c47e223b971ac9ab22a64461463d55835568d5563d558351cc3311cc3311ceb8ae4e6aaf8c8fdb5c3ae4c6d5d110e93d65a4b7255b8fa4937a61aab40b1582c169b71336ec6cd5a442d24ad252da2d6ac55d43a5badfc44ee2a5c8dadc65663abb1d5d89e9235b6dc96bc4fcc5a338f01039f22cd55fbd66c369bcd662f4e2c116abe648d497392c4f2d6b8fb8df98df975618680694b16270f4c50b600b18425cb10e24d8ab885681619a160c36cb2b36a6b37bd4f7bc372ab0c0b16b1a4352c58ae00712e7d6c6ef679abb254496b74ac0919a0724133d3a8520417ccc8b81c558ee085abc55a595449825fa962605e2eaa24617c1153a8f0c4a1ca12901daa2c21832f12a3ea5b2695c6d6332e635d372154335f9f7e40776474a7f5f5ebad4fbab3fafa5edf6df86b90ecff81a7109512bb51e64117286b6599206ea3399ca8a526bf1465ff97229af04b181fd97fe56306f5254c95eccf82a92293fa564c1197f8f2e5f530cde34c3343dabf231e5959c6d5628df54673aa9b3407a646733ce68bd19c5715deab40992a06662c3f22b2ff8b986a413f7114befebea0ecd6e80eee1e83b20ccaae2af6d9dcd667c8c56c11b7795fbaf97dd927f865bd9dfed2cad5ca2acbf5c1cfcd5cfd895cbfaca11967eea94719d7d89a1959f962f2c63e1a5edc18dd09ff56b2163ec09eca9a6f7969a06634c76ba9181562a9aa7db1ecafaad19df02f0e47987cc71919cd2957b2d715b19cb9c9b2ff0b19ddb97f47558ceecc5639f5cddcf24c0cd95f55cbfeddf8c556f7c50d08a436a5adf14a63f39e31dd65dcbafcb632754aded88aeeac32ad76c6d51e6791eb7b408ba2d859ed36b0c9408303d0221b0c22b725ab6a6c34c86d4631fd85a53ead0b596e17c8adcab8168c4c5ba654727db133a398b48c8dcca25cbfe3017c25755dd7759dca55e3d96bafbdd623ed0c065860b21b4f5788343b5971102bac225fbef4211ea821d72f7da874202ebef41f177ce9412fbe7a2d835c6164f2cb7f71d037acaf5fe2fc5f8a2fd2894827a22980ea3810f8055962f102b2145d90a52bd790e5d390e5cd387a1214a7ff477782e2f43b11ad75bcd60e04d57123e84d3fc81ab9ec46501dd6f77b1254670ad5613dcd8338e4a1a95da5a9377bcf1aede8d96bacb1b3d7da93a04e24805203505a807295ebc73ca507a07400652bd787e95034cf41b3586397c7a72129cd59bdccb8ca33186021ae5ee65badd5aab52269df7840699446699446f35aa3d7faa67f667429a31f71322491f5dfab9a2633c6e417d2e96546982499f185486614f30ba97eea65c6547e218925951951f98524aebef45aeb716e91e508b9f561768d2db2bc7f55a3c78ca523e57e98b1f4284a7a45cdceaced9af7bcb79293d5f290067c966ff519039a08121364c7f982166fb81f5580164278a66ae2c37c31dff77ddff7899f7a30c545ac591451a718b2bc81a11cf40b89224fe447d67c7dc8576b7dda1ed44d4ce499458fe1a7e3f463145962d1e5fa17b393ce7467adecbd9f58749d7e967e32e592bbe5f6482fa96f7410cb8fbbd11d0fbf47529a733f99a704533fa5566c7567af265b3b5a3337a3ab12b7c0460991036e738258ded33151ebb06e08c344700b682551329a138392e51a26ea1bbf274ad68ab5bbd10c4920786df1d2e2d58507067c56106205175a8004498b8ce849124130c1e58652154228151a3014515a61851a3aaa115d428a0e15cc29508408e123c30c57b00c6de14210132cf3b12197baa8b44db616b184668e00001014017316000028180e0984425112c4611c34db0114000a66884a5a543691c962b1581003410663188e61000088018018600c32482b5b20009e8da77e9082e909188cb96a11f5ccd0f94d372297e583f2b13290439012381bb2688770b66c05d5b614599610d3ff89b6593614bd48f718401b989105105a09e8a76ac199c900841d6c8a4c5b025dca5102c48f899a32306aec2b29217445b0bde6ae8a20250f344e621f2162dca6528e9586bb0631e99430dddbd368e98025bf95c965652b137af6c2c2fb016eed3181ec39cc6608947a82a5b0d9967c7f2e1fc0688d7612cae5f3cff1c03828b3270abf3913ad4f5a4e8f07bf7b8cff4073925631e1ec49dc7aaba54a8391a4c1d960422390b1f7e2e9e736db3103e8a83d9d33fc5304a73d33bc448d1c2f15128b17c42cd8a1ed4241da9fc8af15dd2f79d6c9cdc987c0dd3760d605e96785be68b30735c0dfefe23a9669133f6a3a1e607727428f9e1fbd52cf99f1485241cf5e14f6161857b40676357643ba6835898413f6a101db203ce70c3fe33b3e513c95a7b6acccd6df41bd210108f51ae35779e34256b66408fdad2cdc5b0e4e68fbb7e38897785a5ebb445dddc18a8d3c8549f21389adc7e1836c027306daaf2b45b7716bfad991ab18c3f5d04d562edf3f9607e0aafab90f93810a8154160883084cea90c39835c603ef3652ea1399066b7a0cd0554df31518fbcad2114c11959f478a76e666897cfe817f238f8826b38ea39ab2b1053d62aec15592de6ba7d8c24d383063874010652b492c8eb4e016b8d78962060abe6a46e4ee050bf7079c4f626e634b5a59e0692579f2f26498ef65866b05becd1fa13e2a7ef76cff0a9368f2899c6b2a4cc2f1b4ea2e69d58543fc47293921894a0b519b087708a4510a4f595365fe26c52760eb1b701263080b86978bd890a613b04b9684d68799ef60b85907abeb2f96acaed4ee24345ac4163b0ba428c8e04f7831ea643d8ebbb49a684396b0823d45e80264472054e013e37921b09e85054df3c6b54cf9ab8c9a44fb971590418c2a08b1b308c383d1a606b299ba26f8d89d99e8302fd8b80be5d0057046f42b79c923dbdbea68122e6f2d0b55a495f45a5b4fe96402ed5573d1cfdac620e83f60cde6ff884c2b8be65ceb62457e19c9311b3a9951ad158ee7bd2bba925a67b54e7aaf49fc8495de551535a000de05416dedc8fee1227b38275c2b5746213b9800a4a3fbeef23c15b7a4eb01d2a5f20f06522a8d3b9e6397cb305fa7b6d9c16f6a7675e50efbfe36fdb89ef4a1395df44572d9ab0fe74879789fe3b886df19c2db44b0323f857848d00a561f93f88018fb8e0a9492cbca8e3f06eb7bd2770e8e23253766a525177f20ad0c583ecc1a3e3899168338f5e4ae780f55df1eeca68874822baa47a63ea416d75efa5e68c78f873b30fd8dc36c6aac80395fab17b131835101399a7da49ef83f560cfd64f952b718afcf6a2df75342761e2e3f8f5d4ff38ae8c36f1be62ca290100de17f0fec939f3dee5a468c7603a26adb596e6519791f76135a023e8990aed1db735c6662df8ae97c827021e1b4447823939d56a64e8abc4b40e41f86fe1fa2ececb3dd278475432cadc044670f0bf040876c9f90b3f040e04c7401852b0acc66357598f30b84d421fa09acab9d0ee98c2de46a1914d618b847bab25e02777d7fd2c71dcb4fe69745f9e1bf926ee09b0cf5e1267ef51525d18defd57bd436f218428efbf6289c68f69c614ebc0e15ded75ccc7d66453fa6c78e974b4ca3de3f26528dfe2d2abe32fb37b9d400584b4f2d46ca439be1fcb8ff3b29035e437fc1ddecdf3ff2a5b342231ca701808bfed8ae49e0e3aa819fe24bf52f1396358fb60aec9543a0cdb1a8d5d399087089f0ba910b62f5f877d78d0db40cf96cd978b53a4ded2758b316760d3abcb0f45c15507090fe8ffe21226dacb21f15a744f437d11de0d7738bb581e8a3af835e7af67138b3461b2e01e665cc64d00426b05da30691ea903ba9c267be786709c73aaf2c48bb69a15e2aedea637f7f0887b9c033155595f6eeb76a2eadc71e8c81f5268d3e062d2efbf4ea53cc6b0143ae8f3ada5fd1f482724faa17eb38f1594a49a94a4217904f0e38541b12074f1eac3c28c8eb22279a422b6f4e9efc0caa79c6668eaffc85671380f559a37d11ee116e4dd0581dbd3b6abdd35bd4bd4a1914f457e8dd3f6754ccc7634085c5d8b126a7a03ce6a0fcf717b0535d80de19f504084b16875fa3f7c3565ae7e1cab47a4a077d7ee9a3fecf75ed6de6625d91cc0cd0a19ba5ec0d5affa2a6e500e312ccfd30057e04aca444116a4d92025e0420d2f361fc77c2ba206aec2b621b492d5a14030417958dd48f007151620729dc09d1409082a1cb826fe2d01bac14a46404258370756d089617bf4b30de52a33a9d095efbe04c9941466a95fd832f9805035b7e08bd8c67a1c857e51ad3322c69bb1aa0ce17434db01a8d1b1e068a7e31d49093525c25ab463a5d3bf52624a7249edf92ae302d1a53a791843682c661c0b4531387747ca797dd4194f5839b57cb395f3afa781f559246e76fb4aef395dcb9003fa5520f21c54c8a46622138e839909f8c052ca55ce926ec312eb4493618bc81147dad9147a3151dab69de5d7417924a76089539b44bdb058fe1271a7d5a9193a943ef57af2456c5e60645120dd041be78aa2310d181a5e2441a769900ca7fc048349ac5dc0615f789eb185630d1a3c5db48654033ca19cce679e77c54f781960a2f6a6602b9a12fceb89a2dde86ae1a31821bfc1075f567f2fe5d3871f746436d187f51fa100d9ab380256ad41e029683ee39d4ad37236033118440142291fb5e2a479f318e4167c70c2f42b877ecc683d9a1c093b082387ae7b92d282776e4c4cfb05294df25573c41e4b7285d5667c40d716b0e85c9a0c111eafa070abc15b3e18c0c60ec8f49347548e512cfe0a007a02d4562149ab0430332141262ae75255d2a0771d06311ed4fa3844f022b599f1f4e49076c8aa74449719fff671c4a47b8199790a9b0844e0d3738adb1980f95d1913f48b85862fb0a7f38a335fce8ddb9d0c773c19ddb84dd4862f44ee8205d42a61b1df60ef4d2488b63e413253b1743eda926a91b6e13dda4c6bbc54bd702a7e627e2bfc5cc8eab6514a54e7cb2c84caa4257cf512842a2795cae6d1e99787dd185f196fa58b3d3b6a98c4496adc331947cf8ac0c2910306a601ba50198231b96b3d7380a462562b8a41e6c6c0f7939ba4b4adbfc858d78e9b8158beed140346d340405641b40303766e483e0f153dc40702b4f50eabb350ae0d938cab60d5204b411c4952aea28571596aee881ed4533479c37f9b37eff43a8bb2ef0d6f547c68a605f02964d6a87c097c1b9db8cf716b40d97037de81a3250d3520a519e8e8cea5a3ceeefbdf909e4263bc71cd118a9cd1228fae38b8e36417231617f27f5bfd2fa7a09ca973283a2a90343eec93bfaeb928453c4767254ae9463660e2dbc071956ed90d1b60d066733a5faa0d36b172c9a03c8e176211de701998930b31ef6c88e05504bc44bc49091092a3062b135495e0cfe95f89ae193b187b63629ac538863a5539c091279c80b19355c4f120a0231370b329970fc694c80e9619e90e5650c32439447d42593040ee304fe75f077eadb5a8de91ae1a175061951c7378bd8e2400a5221ba1adec50428919442bc8044e49ee22b2a76de23c62009889c30fced124126319c30c0e40a71d23d1bb6b717499f7f66d80ccc6f096fcd26da169c09c6c1b46d31461351d3c5abe900948f140f84712ee32c709e683fcb6020e4378e8e848568d290f701f27473bf0383e3474314fceb3c206c0f6982f2ab676f33db901ac3606ff61a612543c3d87c9d9d33efa4fef12bb9cfb0fc208d772d3ed0e072bddd31fd8204301915b2c5312959315d317f5d14968d5181311a4d670fb936a52447d88796354cd63e7e757758dc1a550017fb00a981744b61fc661a21ba46190b93804aedd506af7345e4e8817ebfbd9a337c7f4256187e258a5208037a60a2b2405176c0e5f0203f2a9f95201c810869631b39451b58be279408c4fd4e1f81e379deeb63c1833228b914bb8887ff330990786fbdff09fb712380122b2a4bce0be91f55fc3964a8d8b6de6d970ce24620b3d82b5d756255b08cc4ac82fac81cd66154080f83d0e9410c2fdac6cf6b02c05e13489fead554995258f53ed2d4fea0fdd2fd2fd234ef7a396a131fbb32c988f81d3a6b6cb12500aed766e7072018d6faad388d30b7798cc956053cce0398f1e624b202235a3f9b1f8183f72a225d0f8968aa4181f2cacc1a45703f8b6f5e2117c4914c0602524cc36d14cdad651bdfa3fe5fc47c669ebee38e630b9bacea5062487848d36099a366b0730d8e74a9fa3be2c0371aaa2a81ca88ce4e77d45888327402146f53b81a297947142907c543e8337f6b981730ae8413698b09b4debb428d243370c0a9c0b45a679eaaa054317511d547a87d04c8040cad59282923f7708bada62940435b7061d4959dfe5a40c684079fd783b6cb5d111274295158acac08d6859b46df60578fe71d27cd811032f78e061add85d9a44647633b1b05758d5fd9e02b7efb011e80e9aa47616204788842e497913e1c0df97c692d4b09672501b9f6f20d2e400a04d231fdb2f257f219b0ae0ef743556d9fadf07c94480a25e823bf91524e09334472d75f3893ce0e7c7ff7e33e90bb7992dca7dd39ba91afc27eb6d3f89ff06a17bcbf2f7bfc4c0c69366bd8e34b75d3d8c8b61192a95202d4a08dbfd2a4db564fce97ea86b90c24873cdaf346377f1597f2c6cf963ebb35b44b6e980f243c8af0321131a00aa7262d68d1c053fbe586822c754acdba68d72d1a433a10f266df6f4bc4ae250bfe38d7fb85f48ff3d74affca1b2fce8942f78570dc2b6fdc42f49c673ee2ca4bee82c0d7d470211cbac6b2ce2ec14768c6a8458bc0632b6f1c751bedceebb14f01d4311acab08f6cfc0ff012c11df062d57b745fc41454fbe4791f7021e7945f1afaecf045c3d570812b38c02b6b78089a2fd88ad301b14a972e90c2ecbe8082dfeeb752700817c22341f1585e77a5d8cb1649546ac7a9f39581d5f7bdb790e008b521eef69316d4755336ceaf8dd2aeb5ca068619f8aa6dc7aa20660bd9dd47842cc0c4dcd3b85720605887c0f161ee2811b8efd21702038f38be33c276f9439fd74993b9b96781efb22b44032238b86d7faba4a908dfc0b9c164d252aadf1bc5696fdc84df56f5efc7f46b72876229274a7aec240298a0081ca67d842aeed578cbd62ec4a46734a98825e4ba2d3a4bbab193c25f573c72c23ae33d6660dad608c1fc9ed2aeaeddc066466c16f689f9fc4af19805782ea211ec59e029aa7a1c050688169f9e16dfa427d2073005dab6b76c9c0316560913e355aaae262d024e2905d89e40360a5581a171c888e379b4800299a34e1b3edfd368c7c73f252fa00c22b376883ee265e00b87612e812a563e05d762a662785634263a344a3715cd7b9f20ce73befca1a46ed3a5e2e0341e94969276eb139e92927bc2673064d1719454498153e79b26dc5efb478fccf38ef1fb8c5ae851cbfd92898213c7e67ed97fb9e924672071f80edfac772a77c9cafaa20a8955da37df5c391d407a9ed01a178513d2d5ff582cb631a68cd1fe64c97b0456fbbd91def98162865e1d1f8ac9279ea763c4b5e68fe0f3a686a882c990519da4385782969b70a97f73d0035615cedab33db7b0ce23e85bac6c272680dd0015c6afdaa38222a33b873bbdf060ac8f79d145ddbedbf2044588ca92d6692a8452b572f7475f3db3862177596b478d79b834bcb97a42124a9ba6c23b404f1b80186dbbdce5b7a19d84221e5b5267af76057a67751119839a19f472c0154c3d48e849f50ad650ba6d260ef9417bd068f21ee3966f3240caaca8ff43f995d3cb8bca17142e3f20f6fb8081f97664a1c01d5f9007470c381d14abc604366f8a80767bdcd471b50584c3e84ad456a691a73ea004c49b68cbe8376124f2878a78ed680511834380742022c1e443259384ab5f709c418bd225e988244eae47d199bf70b1a513f6b320a2ec1fa9036b848560d4fd10ed3ca5529ce41a737d5ead3d0bfab4a0eda59019eca2fa947f6e4fa2c41bcfdb6baaca025601e80929d54b5bc98930a44fc06fc00cd6b03c89956946aaaeab4006664ff0c5f3a58e25b19515972e6aca92233aee056fe8f69532d65e31c3dfce2708d93ff4f6a4150f7818b82325b7ca3b4bc0bdefd86ffb86e4164e57fe03ebeb01ee131f65e6dc841b0ce091cb81ec560aecbee3bc889ba5634aefcaae086a696eabead2d401552da9199ed11fa78630ba390e75c9101e5adb8323b10a3426a60a2513c71d100c18ae1b582664274d36e5f3bc6b5b1405706189581d415211270d9fcacfdd7554d2d2d0a488d2fc168328767f4008c83710417cf075dfadb4b9ca523bb15404d1a74ee92794bfae36a1244ff460f255f17072b1a083128e6cd7e799a8b0ae096d68553eb051aceaba093e7fa5885955a9acd72905dd20f5c0724b2135e29afe3ca7c6743a45c72c3833aabcca0ac288293d0a644c5ee49a34545e99ee160b2a2705f46332d606d88222ec567973a5e829d4a2c18074074f89fc81db7df0496028160a3f3c6f99422db578ca3965b1f0f1b2c2606227beef9e0894920ce47cd33dbcf90b039a82f64900f34f470abb2e7ec8929e611524980275e80c21cf764bae00993ac16183b6ed8827561b1da250e73234f4d449c0a2e49db271ea2a75c0ab4dc3eb7096071a513eefad1ecab0e962e544451ace1fc9e9acf6462f8324b03fab71e15f7df2f1fe22017b7ab531ec7bb49b59749b1bb387e804d077167a7faa126827e3814946f449d6470d3c2fdc108184a9170daa07f81a52cb95b0239559ec600275d3cc1214377ac11444bf96df9736d830e3672f27762c20343eb59118416c1bd32f4e1b26d2282138473046b87ae4d55384358f685ac15452fcbedc207f02f0f38af2a3cf1e9b2204e3495abe80026d58ba030540938910ac3565ef333939583c2c84e751d0cceb8a83ab0f81ade689dc920fe1b498fa19f7b22a910301ec43f7c31f6cdb4aae4ca2489760553e1d572be36c32900580c7e8a3c9e2124c12d832611c2cdb199b546eba8adb760f4695559764ff3ab0e5823e321eee7a38d4e0065beb6541ecaf125b473e0985904d698414bb35d211dbd818013cc4f0ae175a0406dc0f2605c6716b32d65ce04c395bd95246d108a3865f6d467ab468b6321ce34970230fe93f189468e66090eced8a8346ad178f4ed4f626b195e03beab75a0647005338b9de0e510ec7e3e15c43a95d3a7c1172e2f2263f6143b2c3ae5b61821fa26a2276f1590af846feed572f9382708c325ccf3d398514ba661b267bb8d428a487516dc7ab3c72945835a176d37efa0f4ee6936b6ce502401231022c08e4a98f7945209d9200b2170b309722e968e805d2ac3b54531a807df4d8c05ef34b4ed55974a05291f54f72c6c56a1f7b1cf870c6013619da6918f6b750b61f82b00124e248cf5f7511d612c6c3ad0370fb2558cb9209e03ac67c8f66bb804a981246835b233761b194bd9e9eed034e42ec7f7176e4338481752cdf3fb5de43764d6074c30044e64c24a0cf6780b290e0c657494962b48144eacec94813c46e48a7480b5a7612c52d1c40a336533b43592368107547d94ab8c1a9134b79825b75d6d4718d748c178438cfaa54ab4e4c04b4b1fa5798c55064ccdc109c0e99f142a82b8298cb08f7012e001cfa87bc7275aef85ab1fd10034352f01d8122db76ea5b5a8b4cb59871b3c45594ace90c945bc1f7022b67204a7ddfd787bc67a5d112a289156cfcd0bbf94b79b1374ff8748714d20e4fd1e2cbbc13234b91d284b14802e8182f6c0afbf6045d60e4b854032feb2aa8cfeb0517706720d5fdbbc6d5f33022541012471ae8f358ab948243153cc6d0dec9daba11500ac2490ae67373c13905a9340ccf2f200a0cb6b4fcb3a8c62e6aa0dbfb644c40d10408da9cb678bab3d854a80497d5c2c28af1ceca2090dbb7bd5caa3e8f3bff47e636e5882cdf06a07b65fc21e9236d41d3f8b3d31f5a8c3687e6844ea222eb747fa02be26dc3af3729fd12e87fe29345be7575822b25c4f570167203d17fb530c20580cb95aa9eaaf3a0eb8c943a0a5c8b34caea676e9c49c93490b2ba057805ea58a6b23354c1767f12ee86487459eb832336111133f43436534a43c5417ac37fb6e8eb7e9d7699fed5b36a600bac4b5bfc44ee9e61ed59b49c2ba4f0ba41575f72b821b1139bf7042657096ae7d7c07ba9155445d09f33822728812c0b8be2e75264115ead5fab3c6927c1f4711c6ebf6211da4532294cc4abfcba3af43da95d9381310d4bec7a33ac5e3148672d18239623840ebe5d0e3f16bd118a3866e13f7a2c1750409e856d810156e08683499a791761f4a2660792ba16158d77942e77e0e8513ce20323b89366a8b82ffbf1d2d8fba930088a31e458c336fef451d24d54b7267d40978b02f8ef902c109d5c9700743ee35921043261b66c7660f20216576027a26b38f8b7e37f28970262d5bda934cda7eb54e02a697120e8e5fe5594e18b9a1dc878ff55945caba47bb98d52717cd978f526d1013603434369714952f4a53cc8793d0c186808cf82a408beea4a66dcbf41afdd9c272f7666887d168263876cdd45d1dc12a1c99a717bdbb5ace43df2919b352b19901ec160f634efb5b19230d4071dfc8871693a286d21a1c1925cd88c7d158be96651e2a17da166f04ea751aeab305d2318f75dbff29bcaa3a798908bc9fc8bcaad6249af785339eea71c8ebe6d8730d060a93e9de0aee6c3607fd463ec56b9ce13652663ff3eeb125183cac424345d01a00b3770723415e90d0631e11476ea8476a1d1db3e0f2f03e65e101343a1f3b304a1cd222658fac32753f756af42f6707f3a82718824472962a594f22ca4eadbc90efa8c88ac14924fd505c7c2c1da722e2551efe731c83035a1c72d5434efff97dd22e33ee3c55087fed45e17917e46c1ce3ccfc0183057ea40700eeccead077aef3b16a4da9e780ecbe236caa367cccc03a0424d3a434aaf79a04221653b7f6ebf3c6ab76566fff2549c79291fa41cda9e54e34291450c9077297bf6c2c4865192e28b0380cefb64b7ae78045f42af731b159ec7ed151b737a3da1cb57f5a8134c10d02ae0eaa2ba32956fde897c4768bf1b22e1f250f863adc4323f503752780a7d51408dd025e7f6714ac9003c3b8e33727ffd9152633b941b4393003b0d45040a135fd299c85548f6d3025e82aedc08e3a28ab5b82392cdc62b5873a9b96e34a2ddc4c49b835e711befe1d94373366ff451d9a6e762f833fd869606de50f4416616f21c42cfc9e71062cfc5da6af2e77f6894bbc99cfc1ede4dc9d90ceb96760f90b11119e23b34dcda4b6edef76d5d12b8117c8286c6b759dc906a3bebfc21dc400e52832ef42271a06e1db6901ae83d98f46e6558b01ea90aa408cc2cbe73a7407214ca5d8046832a7c8002ca3b0ddabd22905a8030430899c0ee0a486d12224e61eae91045877dc15b26b076c8027cd7f4d5c638c1b82c85e3613292d7f75b33888cc1233ff0d1e1251f54271644051436538103d42b6c401b14eedb7fa8a75b7a8a96c1898b712b998b307db8be099e124b8e717a618a83d3ab4735093d45265531b15a6307bd0466e93961873e6b91744724990086b43c8907938102d621b6402d64b42b4163f0dfbcb325d4bba92d5aca84c01535e85af375ef2c20028c281e8c5408be4212b107f521d956d4c8c14a9e1eba3814f5b886922ebc84e6522a8b2da70825bdb20474d59a747e7da19583c28eabeaf934383bc2634f06079c1b557de8bc04f604d244a97b61f9e7101f625beb0333e4052216affdd71cce32d1f97f9c6e2b89c309de7e00be96ec1ed6fdbacf8f771bdd722e17c0e6d3474f5c9d511da122e77da8c1c481388ecaf1944d003997a4d4f472c2e771056f062b5ab38407e9e7e268f78921e0dcbc1c93b7bdd13f84c55aabe2bd76d9f752afeef4b9ce96915309cf82fc98d600fc514b9ce680ecb97b482c2d007d9e1dd02055a8981b61c0480bfc60a5cf14c0dc22fa5404a818b3542a5014ffa30dd4200a98e86ba4282157b95a8b266626a9030dc07acb08392b9213cf6e3a52ae62dbc11c20d1e7a0a16ef6cc53bbdcfca11ade127d8dda1090d5b987aab5ba41e49de6893ea4e1cdcbd65e6c5a8d4af76dab2c441fcc872d84dd1f90be59be94ac6e79376c75154f73a965fec5d35952a90ad87bf0ccde86f7ea8ba46f7796e0077ea881d4d03bfb6d4f5ece1a3af4e9b33cc855144244efc290adc7b76b6693281254f6632b1d9457ba750c90545096bdfbcd35e320a886dcc10aa50e0ae6101b772455700fefba5d558aaf398bf86bd8e71dc1e684a7ba259811a53086e6f35c0cbf9f0bdc837adb60330f06ff26dd44d4a28be11ce86555dc67e58b17baaa1cd76455a055d0e1e3d00645ef654192918149aa0c2d8056d181e841cfa65c82e6804a4417bc44b14487479d0131a564212a3d1d6fc1c8f908f1519201998af3cff7811f21f53a1d61d1ce415125bce9515f3eb901692ee3a63739fa004cf35bb34b7ca54ddb08cf8eaa33d98681e959925b1ec4638ae476b449516d08f3c89e5da4350d5f02147be13e94fe7492f87be122f9f5fe52460a9dba50f5049cde9f1dbc4ea4f2abfcd424533033ea9c6242398a6837a14972f4ed11f5a563007479bacc96a3e0b0c2fa3d9acd318f54f9f50f3ce979793279ac3c506cb145844a687572cc5c620f42a3a11ef096148abc39b7d7309bf1b8eaa2ffb18c65751648092a19d4328354290b8dd0c971a0293756e88b03ea4fea6c50c750c5499519ce39eec8d4265176f94e69816e44ac62efd1aa9ebd0fd7a78a37c098c390c5a398e88332b99064fb180a079994089f10f008a96e7d33aa0884cdd8770cf68721e2bec1c78ae4a79a5059e3bc9c484b591be8b353763062663ec026297bfcc77f833b79b8df2b1d2f97b9f82d4c5aefbe396c532c5f66f069fc171030df1406abb2d677357e9323d92ec8742976bb8bfe19f68a601492e84195a1f53c0cbe38e7bf6991265397dae625ebf152ca344af2a8f399b5804f1023e8362b5223860f37ff240e49b5cc7e6247bd1283b63009013a4a6cdfa97295610b4adc5cd3e5d164037a1df29c8ca1ad9f0279d023fcc73572f73df82ff7edf2d1b6c72ed9543168667f71f5fd1ff8c0838c1c4c59c29bc234e9a4e19a4d507d9b51ee7d37086cfc06d3dec929a5b1fe0473594ab8178ce629e5a7c1acfc4b893c5a550d3b4b6241024f201793704726a1b23509446d12fec549a8822922107f3e96442ad20848e5a9a35cc33610ae42ee86835a35d65e23e1cf53e76be4a08155212347f6e1af4b0a112f83596bd8b5f7022c1859348c208d22dd144028ba6e465696f1d9e7fcd504f25230d57dbc2cb4b5724fc82a1aaa78f032da16a4455a8d0e818ec699722b46f26fc513726cc889f96dd4c8018733f3c76ac883c27e1e8e25852822bd7ae8b9f4fba255ae50ce491284902b5666b9e7a818eec92670f981e404ba44ee2edc5e948bb1619d489411e75341461f083ff0457fee26c43e21baadaf53b83e76d32a0d1d0847535a6d171b335485639ad9e776d72c5a455619ab5a9b19854a292c7ad99638155740dade42c9a96946acc7474dd09d4f2eabe19d0383b0b44c87556606dff8b6a18a7ba3c335e5923af326ea0ed174758050518ee059120f83c089755223fb6532731a794b4d381d8f6005ec065352acfd3f3ec6e799d1d0c4149ade7034563d9268994cd1801eb7b742b9248dd2041d9b03e65f9a7001261af7072a81f8d25ec8fb74f4e77233d5e0c8f5e516847e23635e2c4a7f1ab4820fba760332cd8f0ba86214cad760bafd3f0bbf04ec43ef272072779dd4e94e5e96e8aff803503093865d902a131969f98b3e902149d6c74bcaf8ceb9f401c8a58c2e6c1230cc38a2dde1aae945d49bd11bbeefc178563e23a7bc57b1687c1bd358706b2147fa57898b0b6c6344875bbddaa45b4bc42c90824c0280ed54a8d9ecc85b97326809547402656241d837b4192096d7b806d9880f8c9063a1bcc5d518f47eef232da386a6d6f86f421b576631dc5e7239516bfb5d967355b5083c68c334b577e5327bf3eacd0498825cee6ca5c0a2150ced71de1630145ed8957e87a3470127f16d67d3b324b8ab521e94d4b63758a5fe6e98995d5316419157b20fc3e268f4f89e3f1a875c0e5248c32213758228d28da0b01b02d10e0444bb100a764710d18940a0bbc0846c377551b36e819f77fa02fe698ed0d9ffbd792d7579d236c596a166dd31740a49b6c504c254d850f28ba380ea2b0264dbd9a50994bfe7697ba4362b946b3c28923d60d5b5ad809eb4b400dafde389417d96bcc43d6960c5629774a4cc7178431aeebe9968971b6861dfdd47684db4000ee0be621cffca01ec150b759bf145873a15cdadeac177af9232c90d34e3afcd8ab5950316d7b0115593d870ce110068c5d71e920d1ccf34b9ca698d8f62931c845c3ed4eb80329c6cc2bba47a31e4416b9b174d48027d574af9bbcbe2951b27f816095eb5238d19f26e703744b9897cb77e1649a06c61aaf369e442e3c2059dfcb2dcc689bce5c045f312bf308e4a17f7bc7aaac06154d915414d7bdab08765ebc817824326c6fccb14690b7054abc8d6209ceb5b540623874d42b05dad044f1a23280082fbba4b0f9d003c5d288d9ec907263e3d0fc57b950b8cbf940f5cfd0eaf77e14fd0993fc27d0e72c1217508947ff3103b9689414e26a6146f6024e3b0490f6577804759065ad273ee4caafa80a8f32c17b3cc1f04f3408b80076bd06374d8d17ab847776f54d40029e8e84b131a43b435dc27ea050009e1dbf4494d1a59ee9a889656e2c9484ddb1ac575b831deb99d1476bc83c03077407e7c18d297d019f25eb2cb63121b841a688cf9a3956ddb1a131b670c78e48f5640e3ecb2108350c316dd80aacfe4eb482525874a80547761df934c05d070e6ddf3aff8293a5e9609cb61161c1e692fbdb0970ef2dca8c18561d02cf72f333bce16e160384ae18917a7b06dd5aaf3a17547e81ee5452178e037038448d60a220e595fb4e60a43f9388cbbf500c33b7ef6bf8f0c4d03ff0e426ab970f8abed2a9416920ca7c084c7279b10c6c270360dc300d3a1fdd3869eaa89b109a7aa6804123e58015ebeb92cf47293adbdb30e79c4eb51e62f9ed5341c589c5a57b04d006d16b1c9071e6ce30871d892964481775fca71fab4ab6fa2994ad0f6180ad33a16cfad6c005ccf867fcb08e8bb0f510ac1fa300d551f8200c747d0ac5e5b481dab371ae694b456e70ca9e5be3e88a9e2f2396ae8ac3f884c7b457863ada7fa9b05c6ef57da9747a85cc4d570f39607d48cbfb0c9094f20809a48fe39f85bec2164060ff31a99f8632df8505f468e4e3a739925d03d04cd925713dea5f4089a92e70d6816dcf6256632e67ec541a6cd8d7f3369a60fdae97b2deffd9603e385aec6252356309487fa8ed350fa25f5cc97745e036cca904511de7856d1d122d986fc6f56fa96f5dff0aac7373b65f5f2371ecc19d5b60e4360d51f63326a0ed063b3872399bbf893628c16c8083371ca619202f13d2bf55a0beef6425323427cdb572add44d61b994552acdb78420681986e233a3627322cd5d7b37485f9da9c67220158f3b40f5ba599135f010ef35dd7b08ae62775f3732546d46e06fd150d0471fd3bd92c89ccd2c6107b6bc9a0b4c7b5914dfd50f1c7008d91e0c922ab87768d9d5d9e527f00db4d5f5c1e8292d0ceaabb7949516d522d5e3a85bc433bd72412592ca571bfe52a28696d5b5d50b352bd69eec2ca822c70c1a2b86964219975a0a9181b9c08bc136581db12f53ae6110b6cb781e1f2716f792ded61ae1503953b3eca679a3b9463c4724876d733719a998d5d94c17ddf4026c89bc8cc040706f063801834f3cb8cf7bb4bed97f110e581c2d4412ee7b572c19e241ea598b6ca80bfda8eef51caea7b21536da155fa9551ca0411df26209b208149df6a45c84bd86d7e6b2ce2bafd669a3db94e1e3d82055463a745bdc1852b25cb4d048386e3f70613fb82db79d7ea9ccec43d46e7e96549fc84c323610a2e3d7485030ed65874de7ed4d5fd58103ccdf00e717541c2ce9e3bf0f841377efdbe1eeef073d4edb246cd8b01540e9cb1db3f103ae38ea2235146f63451f2a170326dd92655ebccc7e104610684b0bc26a9384b5d7621a865f9630326a8f90d477acfe6750d61bbf306880f2ce2ad9369acf0e3eb28dad0079a0e8ecfb81b52cf9f1752817838f0552b89c42b3815be7050d42d505367701cb3b88fcf481750319d64602fce9295079eb682070db776c6b9ba2fa1e6c407367780e1d5e482916dd7473857bfc41bb2e49d488cde5155376b911fbc5191392f5cfa13722b8453d77ff9b8a11ab48e90a6a69f3d2df833365554a3496782ce16d30185cf35dda491a650de32facdbd6700fd0b7c8c79ca9f8955c29843780b506760d3063e202a639888848b83a634093de7dd3de0d232a440b34b2dd72336b6fad2df9b672a00f9d9237ac91c84db9209d90826a9cbbafc813d22c0a22f3f0c017825b0f07c2fd5748775d6d1c4ec688046d856be1cbb7cee0368bebda49a3f60afa53922463c7a89ea31acc694e916dd6faff9604d9a1710d49b9a834e05ae75bfb936480ba537f946279983049408e557cae02fc2e0de8722392affdb49050cea80485bc508f1578481e3c975dc96af64de3711eb8c9c25e40fa5e7df1187b189c7e4b904024f8462c7e871bd1ec47955cb8504a4016dc1eaf35709d4b038af3799cdce4f4e05f59f3e0b084dfa509ace0b64d6b50fdecd98dff0cd7e18df964c5f2c103b0f05ad01c873615807aa51a394ebc5c64adca5dbe884076eef3733e69eb115c1ae17421b6d966e93c39e37f9379517dfc28be5a41c7b4f99c49ff4d01f9b2270d96b65fb6df3d2a20d7e39b63f42d2bc04268e1d552799c024ac49d31bcb6097058cbfbeee754bcf0a3d434ecedeecfe4c9fda28a0e27c87321a6810207013ff46069166f952f61097f9533c98369ccce0f9c9c41857adffd98081e287ea8e823d6021d98af4acab31179cbcedda1408db57ba0c6532d4e3aeacccef4c34fa65a0fe5d0ca51ea46d809325cb79ca644a2330d4b75b28c4e3c91f6ffd1b29e01399231a1f3010cb7743f59a46cf31fb093cf3b1109642386ec5957c3dd5414ace8f32052a492d9365a6729e1a4b37a26a9bd9c16b990e93c1ef3bc699050d90591f814298eaecc488aa7d34173e49ffa2f948b3dbb4aa142c160b582b122d013e9836d0e74b4949f5a2045ad6764f1d19c2da1bb910183a194cc0d968ff6b9d30333acf95a86c3033f1c0df5e36dd09dec81e0ff781b9f274785cee4d17b2ae1d80863c239cd823a62abd99a6fd9540bf8102568beb60b7a517f6d8046182e3bd4dedfd16422110193c408e9a75dbb66af3e03e59ca7aba569ea24a803798e8620e5dba8f5533ec0194be621ae9a861c2a9620129cbe467c9be4147fdd7abc9226b0493171ae50d5702437635221381e37ba4abe9586ec3ddc16357d46db46983746e2596c3aa8488e40de6d26b94828b684254b23724e4199cdfe4158cfbd9198a87d3e6661e7a93289fdb205108a04c61157235858bbd1c46d414158a19835d197aa4decc70bd12cb59ca7f3b4c3487d26286ea60131035229269b4ae43702963b10b380b218c6280357bb7b072a6be6eecd9ca62e415a749da0300fb32a6e3ed5db7f8c8630ce4aea79d7200013925d5f5a82e0ad0c73ff738f1a07e16d68757bb2a05b7bfe650c15d1ff4ceccd7582742f14fad274a0d99d94686c308271f5eee84dd273213f9183bb09099483fa65e28ec65768061c14c685f516acd38dc1e38dacfc8c97700040a54dca51efde61e51c4aa63cb2a5ed5fd16212a88267aa4c7bf63d4b96587e50f7fd35d4855839cf93f409f6e976526531ba0b3fd1a8ab3b20afa0736a47c83582195582e1847e253ec4a08a9ea9096bcea529ed73c404207734221c5081d6d20992dbd2f37225a21e4ce57b050fa184aac41d77a44537f489735a232578fbc7f1eebd3863ee09f2bdb10b092e656a36941e023085176a3cec5d4837666d3200cccde65395f6c8218bbd617530419f298c17e46adbaccd88d74bd11e8521e11d7bde8595cd18df4282d6a2655e10fac036f37288c38a6dded7fedd5b7f056857c78c0c8e0d16a9fdb412eb0f0d989549bbabc438e3a5575aee1ed2466076f153162bb999e005ab05f0edeec2e1cc02cde745f8578398425832254c2507badf0b71db1e7d09c2127d9f0060cc56c5d7478eaad481855e8d582a9b14b9da603e2d8441cc024c8175e23aa57fc759fb64f2e896a17cd405dfa0c9ff8d247e960a748a0a585d13d821b935d33c2e0840243f99ce9b586b8dc041efcc8e612764e4c9770c10abf9ccb140bd23b704cf07246d0ef3bb327e9178cfa9588d5695a3a808a1bf707472e2725ace298207c4e3b6915551fb2cb46c4617987351e0cda88c9de85460cf22f804c93d65faa4a41e8c4802c5eb8d70d3d454ed0362026cbf6785ac2f8783f74c12391a5dedb2ac988b3cb434c58a08c63eae1c526cb53ee98a0f8a973148122756c0837f966ec0316b84bb2e02125bb0f802c0200b477c1713b4348faebbd8443c4c386faf0e139e8c34a0d305a96f1fa3e06b212679da8aee7931c3c343d931ee5c1d8f7b14a1166139a28844912554ad0e15886ae3c9e471d1db905e3c3f074c9ba46d063719e5e15860742455dab9d32200dbc42ff3c52e4e5c020058f535b2c5517dcd4cbb633c8e257e3ab262bb6bbe1a99e07099c5f27a9e9dbad753f00ef1733aba0da32833d59889e3bac0ac7c2aa958331187fc844282c6b128fff81ea354b66e9c85d22604abf9681094490541d6802353c4cb4492fb80d83056938efce4169030a1ee8fc009370563d9041122527c3f521e1a9ccd98d416ac9afed27b5c001dbc1196551fb8339d4c0bd9df92b356bc04a0c00cf1047c436b8457cd774f3a605fa1f8ccfa7830fefaf9b6c371e979a3f3e65b73ac6fd2771363caf7a057d1ad70ec1f255dce1ea4d9611aec93ca1b311a6c68bd7385071f63ce278e0b884e30fc645a508ddbfa8b1f9e61ea05f27ed747aef99c078b89c7909466e2d7fa9f70c25ba5b82b270d475358088c3c9e8020193f9132f9509821c14da3afbc96d4eaad5f71f65dd81a5f31bae55b7e21f3aa8317faa349ad6b911b84bca424d021f0f102ef5a77f65fab95f50a088a0c5db2a0edec342c56a43d2dbfab844e0c3950ac22c146efeb223395b3f3307ec3ddd385f97cf720c213e92aab7f591773cbf2fc72e98332f29a1532e23b4a30cae0f64071f6dc472eac2d8d9bfb7c80caa580c565407105d6e059d24cfca6f01211f32ef325d3529f43e11900dd5f6e398f9aba9dce8a74232d9f572475c3ac24d277a484b4674c3dc93245b1a35d65727fd62782d150157ab79ace57ed9f33e114aca44a58dbbb0a36a36ff91466f70a7d232d91d14caf11df2bc0c93aea6341fb17e6654d69a2f6f3cc749bdc005de62402de0d00a3193f1ed3e0d888572f1e6441076e32b61bd9c041a3d74369a375787b7fbf32249d6d016fb34005d353c477f57f99d21b035b8d93d5b21a6d7d1f6ce869d70c1c4cab917a1d1ce36e637467f4f69d934b1e8824d31b9bcc6daa9fb5f75a5a6852fc6a1891a40574a16d2c31332f6917d693c4500a4b66278337d0a3445b8c9f3d9d9ebfb0e5aab3f944bbafd2a562fb8015e2a3bef734a00abb9c9f57e1cc4c6c18fcc3b102106d43067677fa70c0f29e84f5fb1d18ee854569a53b5ab12b51fd70b76ff639f4424a457bf54c31e6cb60bba76c91dff5b447959a364bdf8183b87c971c1f61941608639ad9334123440e90060101297c551cc94fd03c9e6ee2705882f73fdc9168213cdc1ca5b028fcbcc554f8a2519702b8b501b696157ba613aa8b5b6c89c7511f01680023e01ce945349421039d1d0d0b5db8fba31f69c61c9cae74efbf84462aabfb49c86ae521d2f3a6c3c519773e164a587bac0b5b0703b124a124c15d5ebe47acc19d5b299f71253b570f2877100e52328b1bd9e8cfcc708139f6ceaff47d0138319df6f073139b3ae92384277f82bc4ebccda083670d786c400032b4b660c8bc9b8928e27bced31c2a72fb2262011608cb757afb1149041f25f59c3f13e470d17a769c2815469ed6022269e2e9cb35d06f9fbec0f41933c20375650d05068fb4f107cea7c40dc6fd9eb3693fd7b451178c4f97714d7c82bc56a2bbb27a15ccc9b3db3a4b800d0cb044504f716cc60981bccd03afd27028843e5b1560afa9f26ecc0c250a1143620fde42f1aeb0a3f52390e0e8b03531ebdafa4983c79bdaf3c29042f325bbf8a35316036c55deafdbb200b432bb8cade008847d36b9678afb302c0424afc63502c67b31a86a106a4440b1c1bce636130264b2cb29fc7832629f4110bc3f81ad66a3aeb68d368e797b4edf32ba25bc4bb20ad26110b1156954788d3f54aa6f5feb00ba507ca811e94928c054414cdaff484aa0e5113ac9f133bc4d86e8f9c7e770bb6e06f78481b77e7fb7763802b84743aa05756a571d8300c57a4e990e7e8565ad85cc645c0d123165d718daf18ae88d76afcc095464cdec06f75566354aa902794a024ae4a897bab2195cccfef9e48b7c89674ccdff141e30d84f04a9b7ea45f7c361e10833889b881fd89775ffcafc15f3f917433756431879992d57cc32664cbd75e6924c6fe52201ad6f0d0af81cb4befa3f4038d7a0e315fc74fb4b82247eb903224fe78e780d5a15538a2607139a0ffa33b5d25d8145c2a9f3fb3198dcaa9553bb29c9aded3e6f07276cde196f0eed92f81951f4b50749523e9fc86986cb3fc184cbabbb44ca9989b4939e2ecff87973cce5d3e06a00b0e508c5abc7dcbb59e63804d7799fe41bfd8292042cb8a5d7ebda8f1a6417734f3eaf97526aad24824debbaea63abc0f0afb516ae3e7f67eb90d2fa14e43290274e3d1b7f8c4c69326b4c006fd8c9216efdea0ba574f016f4f38843e5851846d61ee2249ce1f6ab6a5f40f3a2d5a69c3e813be502e9343dc3d57d3c304c088fc1d4723dc2e43af4bd6129dcf1761625a3641263334916071bf828820560347eef7c80cbe2df71989a04e6efff279a5ba8fe7c3dccd78d2024d89aa97526ed6b300a964e37be553192df94b0eef4b3d4c60ab4305fed7823455482ebfdec53436b1fcffde895b9eaf8660e1e23f8b35b3a44eaf8fc7edf5903dc85dd36f62e769bb475eb74cc3978eb0ac6562465d2a4b10c183e8c7628a9bffb3bb755b7d558c722dfd8af95a31eac54c88b7b8ed96fcd21aa2bd1cbc6a9601b14cfc0a4591841895655d8c5b40992210c73d4023aed8ff2786bb2ee21092ab38efac24107f724e7742cce7ce42583f3fe810f8001ca69b406b18879a2cc45c036a74927e5c4094594d1b157fa9a7b6266343d1961ac2e1216c390df15b5a36276defe322b6ee43766ef2e99a1f94f6536cb91e3c0923fd4741d4b667689f8cb98e65676dbfc7f568f06784b02e6266d492788c26baf7f36d1322298944767777ef6608a607870710164452df509351c2bed764d26ce8f3afd70d419bafda5071ed6cd0e76ba60a33850c710afe551bba94271724ecdd10b4999dced0f619a2d9ce36be53f4cf3ee639f8cb1ef7ece442cee555c74a4e4d4896082e5ab08839c1bcb8ac6831b1a85829ed8edb74d6326ca510a198818a95d2eeb84d672dc3d7564a4f90a1047a0961244451873021a274215c2004500458f0a62fb343104282d021882249a4e8557637e505db1a61083c82b8c12807498438d02e75084bb28412b6098492fc24092287243d488a71ce387b92f0acb2992892f8008293e8f8b8492bab841daa11f5c18f06b02083830e6ec0c376084b0244a6410b7acc44c1c4eade7b6148944082040e486042c4a29919243ff43b67a2b81709907e67a2b8d7670afdce44e19334130545a20449911f581040f121caa7894f10fdc70231f0f1c167f5237708f369a520480f1c708f1d2e137c24a1059b8d02b659a553be66b080246d141b083b138168ebb08db047e0c303770823818f0f3012b06024d0894899081288a05b34a3744174044c46a044d711f84ca0df10db3cfae732c506a2083e8660a040102a830cc80c16eefd319df0c1633a114596f1378e9420a103040d829e68a0c5c451106030248a0b72d08307cfab0ae67522034207da1002d0a59c3206e6814337c514534c81733d1c36a0a3a3a383633f5328650b3721cef5204e8c35310151857e33800e9fa6cfcd06cc8436f2238d51ded474485f76ca3f0c741893080944117a5cbd5adbf60f1f99d2b50e614044e951015d6a1da399f42f26e648ff66fa878108ea1f026e000425f00325d14c0ca200bd98084282bfacb0a49991c1cc0c60475ad1080fa08d4c810aa40daf8e7bee86664c783329a594301c80b26b2b951276a44797a80e61468460044a6dc203186dac017ce2862292700e32455c0530233fe0c10ab6112298911b2cc919c68f7cea54993ca68eac12e7ce5cc58f321445ca50942222a1294853a8204da9927414f40a82f20a8a220434a39493d62969b5f6e24ce24ccb526a596f526ab9719d84cf4581df0d49ef9351a447d4a1f491477ce411d9429a4205694a95a4a357109457501421a0c88a3b911577e291d85a154123188345700a3c8a52ce49698d724e4a6bb5b45a7b2fc6d9c559a669396b2d6bbd6d1cd7c52eeead63696545050b8b69002b6056a1a5f721a0c36781e17d03e8f04d262d75c49791cb24185398b556ce84dd83c81146448aa04482fa07e389fc456f86c786333c367cc977f439e78c2fe79c734a2f760a74890318ead09c53e6074f13439307a8cf39e77c18240d05e694d9d95a09789084cf806748137856370c39eac1901f386188149e1bf418f40f46126698d8b14f0fca0ea9941fed391f98bdd20f41fd5297f4619715763c6b583ae474c78c91d28fb446ecd88b909f6ac42ee1173bbcb25fc8e9c54e32077677993136fd5c8638bac7e3c890098876f3643499b36618949f6a6872f6293bec2feae9ae793cf4fd1c7934b1b8896d92fd075c7af677850a718b1c2ddda013534feb9a0771b2aca716bec3e8e2419b98e1e945684365a841a74fbed54ca7c4d7c68cf340b7d4cbfd97643813638c94461a299d1b4b9998930c65fc1ca590b743588bca34b2b33204daaa41e5a4603527dc23e02240c88a48d16c87b02234c8455e1a5624898da5435811241cac089022376c22544a448c8624a9d02b6031c15a4834ac256445cb878b2685978c0a4c4b87b01f8070fa01a6613f2cc1e2879f16b01f5aa60e613fd8e0227e7e584f117a8a388c480d6460445a30f3d221aca78582f5e8bc80f13801003c4eec8ea3e16e0f79c6c757d950fa1713e3023b840d79d2e34b29b53a7397f50c250d07254d0c84bcb8b8fccbf810d2c382223d4e7c2022e4d4032b318112a21eec0009306841910c1244870829e061c20f5e3034e3458d1cf101002d2984bc6851f1819ac102e5437ba543586b08335a35102253440adc093a7847d87920fc702145871634e8c1e2052426891d4e4886c0140122e4a5c80d3b2e930a90153358802f2246ac1697a71ca92583fec1d70a76589ca840c25a690901526af9d8d952d0a1a3f2830342cf06e3d14b04c93ffcf4a0fd60034f46858722f450f4a3063c30222dd8691181f5e8b49cc0038f139dc28c342102a4c6890de2e3f82e2e905154659e30e79c35c7740141869cb107324f98fc2bfac010259d001dc28450f9d83d98ecdab71ef0a92e1d21ab2c03c50e9224e4a767df82058992c3bd7eeef367cedb3e7b5d7c1dd4f5af60a261418ef4f938a84f1f9ef4f95477fccbd1bddd6ee8e7b8cfdce7d27ef96deb9db7d7cf716fb717f277a7d26bfe9d6cc0de7d1f0d435d3fec340cf5eeb9aff4767fdd5888fde5b5f7e9dff88b28b6e639ea2e7939aaebe3788eeaca2eade24d1c4a1a96173afe45b8bda0c2fb6ae85ac7bdcedd73b9d32b2baf792b5ad7695da77dfdeeb1d669f8351928b20c26c408260428cbb22c8b9fc182ecf44c975e735fe27efe8cc9655916371b1df7dadb3ff56fad6b6cff5597abba3e1a86b6164c7545d441c870e663c1ea324e5a639cd55eba42a74a17d09320201794974b537b71562da4b45e9c6999e63c65cc4f5c2d3e17464ae1cd3429432f7c638c30c21b63a63356abcbcf438e74f9bab529e9f2399f8e76dd94b1636d364cbdbe84a044117a99a498865e364cdb4d3251f94c554c49d184f4b5f0d03121459f1f254c7e7c9eb87ce2d3c8be85a8454a4b510b51cb106d316a8951de32a4b3e2b1b3d219c2d29134f2a39473529a693adb3af320d6d90a3de10e84d0e1cf185c213ffc38840f1dfe6441d144872f5f4d40822b6021483abc0fe1c30057b00f4f4dc03c0113c48b94971c5c945871843bb92df253135dbe9d73c23cd1e5574dd336edb5c673071344979fcdc96d597e91d2e5d3ed2587d8e7bdf7deeba244972fadb5d6da1547882c318cf7767191433eafd7ebf57ab1582c168bd5e5b3c458622c3196184b6c06afc2937fbdedeb8ffaa3c6baae8bdfe5705f438f187ba9d4ce90feddee06cb5581b5e7300ba77d2956bff4bb37c6d8f11cedb98fddbc77bb8fb7fb3875a47fdb5fef9b1d77edb7eff077a94d6a3ce74adcfd9662f5ed1f4bfa524a193b2d4bcdf29147ed3b7e32ddfbf77e6a256fe4e34fed44d57d11c57f2956cadb6c707f79cefdeebbed8696f7b7971b96f2da881bfeaf2382f1ddeecb2df5eabc6bf23a2a49376e37b5d3efdbcd8bfba7d782bb4fad341f2c249281880fdd1e756bbfc495b62f456e7b2e0524469e67a0302fa4d0baff3aadf45bd67d1d916e3fed087730abbefaa2abeb5fe1b57870e20ec7ee77b7ead8d56f0ac8eb4fa580d814abd3bc8e8aa4b14829205185cbe2b3e50a256438f3995e9d965c43fd3b8499008a135687d121cc15eb2678d205d021ac044450e9bad75709758c44504f4087302478f423a4749a0e614730e94718f5dcb5aad571cf9c56b1b41a638c91fbe882b6d9c81ec65a5fd59237b6c6fc2d3c8e4575bdac78d343a8a2a4a5b5d6e644fbdbdafcdabe96756f3734fcb9fe0c527ffe714102259552e2583fbe503fd6eab2fcdb917f2ebdf2eada4095e31d3ab47ed590fa5aa5a4e76c5f43af2c5eeba8c25175c4ea1cfc5b8bb7334817375ab850b554fcc397af3f60fdba822bb4f8caa9f6f06f38bab0597e72f1b8fb7dfa985abfaa58aa2151555fd5d29b8dfa96e7d8df3e4b98fd2e2d31cf925fe6fa5ab659b57e4cfd53fd5dbfab39ca8c371a9a4ac885b7835e35bf6a48af5fb3177fc66ce1719efe18efe441154bd2542df6c23fecc257f06fb77016ae827f2ee87f347a45d56373d4158f965e8bcd189b356be87f2eafc5f2aa1295e4ca97a304371762899ff0d7d7bff917376bc6c01dfff076b1ac2fafbc1751ec1cd5555dda735e5d6d36f4e76c74bba1bb7e9a19a3fe80f007069dfa10f997a3be56f54415542c554f544155ab6fa01db481baaac714135f8690e353578c31de1eebf4484a19371b574e16d0d19c76cad711ad14a94523d6e82334ed0787ec13f75afa9547a8036d286731f97b13ce19a7ab1a551f1b7af782353102c684ca26678452075de6a07fda098c69355d37084c1be39cf2a7b45d0c3264cc6aeba534a8763709814d5619a3076d706643634a7717c3a501014dd2c8526bad758498a39b0dacc1a861adf24c0630c08ae594d55a1b657f7048077bc85a6badb5d65a4e84ddc4fee050d05e6dcc4449ff60c4a2a0d5296b30b4f1a66c593b9cb57fd4ebb67e08ec973aa5d4c6ded20117ddc274ebd2ed8a9605f49c65da4daf1c9b3aa55446eed1f618670f40cf53ca38352e3eef70aba151aca5003a7ed1b1f4663af6643aef30063de739658c314698356e3e0bfadd6a681d6369b34a31943ba8b45358e22a82c792244b862ce161490e3f51f869c2cf9113fbe7c9922292c42543e4121ee6921ca2d0849fa39f218a5f3f40d9cf931e7bb0d7da1f1f3b645576c051d839e8616b60adb5d65a211fd4e55fd65a6badb5d65a6badb5d65a6b5ffb83af24ecd78f919f20fd8341257b30a20cf832aab2875bd41e8ffc3d300e8f26f3e2d79faf17df8b389987bd086dee4f4f429bfbd1eb57e268afc7e334f9009e5ea963ef3da8827cebc94000c01a59f3a91003d6480674f93d0dfa9057ec5168135730f51ab4d70c07e35c9c8c75f62c4ece74f62a4ed674f6284ece3a7b1327ebab35ce7af4626ff78c35debc899335bd791227e70865a61ce558644d941b8d6d4ab8516ef3341c9d37bd71b9cb3b42b929475febd9dbd2cbdd76dacd563a96980893214c78184105a9439811b11cf4f8da8828460875083302a87f9e114718f16344cb889d22aa4c89b688a2c9033d835b853ecc75082b0205521701d421ac889f227c8ae08151c44e8fafa9107946082b4fc49210e490a20e614ba2f48fc296c09634f143b4246849123c2c71f52893e49920fcf820a20a8c88294410a1fb60da1c5022842c112cb844a0600811ae0e6144f8101184886f0f51a57f74082498e12b5990677e10e30b394b29638c415dd6f0ea5256d1e9f5e5131eb2ee441d8cf1c517637c31c6f8628cf1c517637cf195b4da4bedc519ceb48ca754ad66bd694774d51b67abf7451eb53ec7a3d6ef6ac5b54229528a54d2eb4f22794432e975066946da8a6dfdfa337469abad3f41108362940d34403660b21077220c336cdd31a44b5b77b4bab415dbbac3a74bbc83d56594724e4a6b9473525aaba5d5da7b31ce2ece324dcb596b59eb6de3b80e5feb7d3518d9aed376db5249db157c6bf53e18d564d2b5a545d715f663b7f862efabc1a8d75761d4b1c77284bd6f00bddecf268dbd4f01bd7e4b0d485de28f1de3c71d7f35c47a9d405d16a0074af87aed21e80d11eb354b3831129372441094700118b0800712c482d4600418ca104143a40036c49121820ca14495a02694408248081560828fd70e4c204594222742501102e80119cec830617f75a86b3a1b949456496b0d9a8ed712a4810fb71a2c9246f6ee3b1b1bdad0ef78ae1eb4a1ef411bb8c1c023871cb61a196bbbd19091655ace5abb5b0c32660d7da86d36b4de7dce3a6e31742e7a8380dc68d0cf3fb7aedbdfed128636b0e4616883e96643db6c687d7fd63a6e31ecefbf6d10c0b306eecf9c9e1b0dbaf766a3fbddadfc7e9d6d35b286af56fa150e79f7b177fcf340df1c9e5ac01c9741da3933e6e67d598732604cae0d6933c87046889f0e614a98f402f4adea23573c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c2c146beb684cb8baf63e301448297d5e79d39b5c7e454ef7db77decb6b4fc6dfa0f4ed5b9ec050a0e404417dbe667979c96ddfd98853e8eee5772f5fef7ff13eae73f997cee5bbeef5cbd370a45f9eb8dfb6efa3e1a86f0f3b0d475d3ee7f2dbcb7ef9c2735a3e476d752e1c4a1ad36bdef2f057a8d85a4c2b5f7fe31f8b0a152b2b2b2b9dee563a5d2a953e7b2589626dbc6d1f7f83313105c6c46bfb747c0d0543baf6baf3be6dfbaefb72d456fff4f63e10bb7c4de39fde3fb7df9bec1baa87cdfd575b5d6f3c476d75cd77c7ada3968f145d8ddf14cf0fb8e85176173e0e386b1e7ebd95ababbaf26cc897efd9802c501e78e081071e5eb873ce3927a594525a6badd55a6befbd18f380e241ce39b71a7247951a0e281d503f76f418638c319afa8c4929a5bc73ce6929a5b4d65a297c6b276a878dda61c7872d484eab0e9d9f2575d578a190b30c710a2dcf8f74cb8a6db000bbd65e7ba5e7fe6ea7184acf3d8e126fe1d83c5fce7e7dddc256575de3281f5be35ff6f1bea7e5a82b6833bf63f55943bdaffee8f36ba03b34398dd03a42bf524c798a95625d38e9671e9c35947f33ace84f6a2705447b69427dbee6d2a0f4f95703eaf3e7869f14901bb4216b03cbf5d5279d520e02554bd25c554bd2545a69f4ec572f5afec5219d7ea40fb71b3cebacfafc1932f3e8f32b0a07161943478fc751f5c403c49f3a78af5f43b715876d3d9c594c26ad4f80324893623e82b514f6579130d28c51878690869086909eb073b2aff96a444531a3234dc5cab8aa87d757852244b19014b426d1aa3a22c5b61f85669eb06347aa55ac8ee56157f206ab7a90928690a85451f560150bab7abe0eb8b6aac7b2342519ce7c15a9cfaf2d7943eb911eeb6ac6e03e729939d6388a986fe4db63a9b2ab6bd6ccc778bb0ccd4061e7e0aff9b1bcb94f806a507dc91b2842f26607dc72fc2fa6562e50cdab4aaa8fbca14aea8fbc9190d6992860a775488f3f1385affacc9f81c28eb548de64b16a246f8ee48dd682ee5fec5f0a4854817f3b9c1c752279f355571d923710e624d6e75729b5a8cf8f5b0ac89671fa37aa46bf19e27d6ea398db1492f604a51ff18f0b128cad8e7ecc2dc658c552f5a85a916255cf2375fb8429f2e6edef2379f39b8abcc13cc63692bc4932b217e3a26e1fb5c4fe2a50b70ff5e9856f9ca7a3d25b9d1923bb668dfdedb2ad4eb7f6a7db123fb5a08d7d5796fde69f8b8e7f2e5c7659a26e5f7ba6236863fff4b2d99e8eba7d94d0b66f4242b7af7d3bd6ed8948b756e791b81fdbb66da9157d8ee3ecb476db78238ed6da4fad3eb59362d9b9c518b768adb571dbb60e68d6e0efb8ffea928e5db18ea18053401ee38831c63805845e6fbedc5027d8f63fcd55a305fe0e66bd7c0458c73fadadadeda7d7627b1be3bc17526c78843e3ffb34578d3b3d2d5397876babc7c7ef82bee00a2e3aa62f68833f7a9ba7250dee5e1d7ff6bedd69131d7fe6eaf88725a6a596c17cc2ceb15ffbacfbedbd1905b4e7c71823fd5aefbdaf62b1543daad6b743d513e955b1543daa96bcb9d762554f8c2a56bc5cd503352114d056b5543d1a127662e24b6d8a7ed775db0daa31a6b7c6faa53430c6f83ec6297b59bc524a69507c5333a60bd7e71928ec9cfb1568d55f3a842d31a5d7be79c81b9dbd236f2cff52df25c99bd566c99b2af8394a898d52627f95835972d2c1df0424f498ed0ba6b5eda0092f2fedbf2c64d2993551e7ff6096b0fc409b78d2e9f15131d8f1bf2ea9c3f898562c4b4cac97267a3deaf1314ea552a9542a95b2a914b695bb32131b27a5e4aab49cd4a42635c949a93da79f9352729ab37393fa3797aa47d5a3eab127c8f9eed3afa1c77a6be5ea73175f9b69afe9d42ab5933a92931fea4eeb4dffa6c51bf9375b7f6edbf6256ff36f861c5a4f2eebdf36ad498d430a31ffaeed57884d398ee32ad538aee2e7aeaaa56aa97a9e9b254a39ca95bc9ceedecb7de97e89a3b4ebfed2d273eeb3ba9f7f1dd8f1d5ed52f5749eaa27aac0a97a543daa9e2ede1f4b14f50074085bc2b5844f8f3e74085362a7e76efe497ea9ebba2ee7d3dec658bffbcf1fed197e2105e4eeefba52a92b95ba52a92b95ba9f31bbfbb05b29f1aedbddef5be232cc5efa6ae870bbc17df73364e97f73986f86fd78c660f9cda528ed6751f1d9ef6a897f1dd8f159a0defdc6f9eb73dee6511772b47ff3659134d1f2949ea19dbc50cc6dfe4deddced5ebaa556a99dedc8bdd9bd74dbb614eb6edb86b7f930dbcd2ea5fbfee597e39bd6f9761e9c1a9f81f62c0584456f12e39084ce9c79ce24aa740374084b42684ed5103925f664985d72bad5d8d131bd57c5520db92a2539f5a1766f8c543ee5343e7c98045012ac17526c9b658ee45fd4b2965d16ed728285532c8af1c414e3144b4bb130c678c6283d9ca599a3aebaeedb4adb665ea6cd6c7e0a480a480a480a088b93d6071254908842a137df43ea07e810f62428f62ff28f7adf961acfaf3932e8e8f76787f9542df85fe433c8ae6af5274c3a003a843de1d1f3d5b6baba76b98d6f474ec6e7ac3f794efc7b35dc5cc8bdf22f3e9d9c4566912e814bbd522c96d44a67915721acb0ffabfc6e9b8d9cc2a9558a256ff06b1b0d1cb7cf013f7bc82e558f8aa562a95a3d725dc95aa1bdf7e25bdfaea00828a905fad2ab54d2392795a91508e494f3b996d136694579c197a3ba6a55f5a886f4a86245d8a79498d2242758ad2068069ada49ad522c15f8a8a08704c054a0f3225281eb14258ec131965a3bf97421627f9508b584038ce6dd4749f26686a4995b1a4dd948972b40ca182a892d636ca24d249f6c2a6938a594d2ffe4cb79e3fe3743d2cc77c0aa85fc18fb9b68ca781fc65e0c47fd4869246fa42c8ad529f2066adca2c5d78d468b989898180b0383710bfa39c29c69fee5af12e5d7748cb939c71999a7bf75726cebb4b4a20c0efe3232cff9ccf31cab44d4148362735f4bcbaa4f150f23d4d26a39d2e2d3a2a465b5fddc5a56321430f9534ae9a5f46310a5a698bcf964988ce40d354d9137decfee4dab19db44bb48dec4fcfc1d336ac5b8ccf3c721f39cbf0c8e99977919ae43e667baffb68e69056de6feafa515e3eda21ddb4692466a6fde6a8ac1158e660c999f2b1ebafc377bcbc36972f9164e63623e5ffdde7db8c5e018630b09bb6d3163c6780a903453c63bc9e72ff333de2906fe327f921c077f99138e19d4c3bc8802e5e159837fc693f1b847b90c4933379116f3b4a359339f7bd9466317499af9d9b68924cd7c548ffd55a297ff36517f7938636ca259331f26c26c980b0313f39b6846fee17d37116ed1e259b0f8bcd16081f2b1efc7b8882e5afc6df1d4fb36d05bac3c0baff4da4663e5f17f341d9fbcafb6562f9ef6befa6345dd445ff5616969b5b44a1ef5bedd117d447dfee67d26a33d5fbb97ebb963cf19cd6646397b6f0d2a43dec0181067ce938f0b2963431dca1385c4fe34ccc4fabbcf61fb1943044de48b407ff77793db2906fddde3d0df711cdb732c905a8cf1d74d66dcb5814ea9ae4399ac2f589d7e3eea348605eee7fc19f53327a193d04928cea03e6b19922e4c495ccb50f4febdc7f1efa1fe7fe61f06897331e3e9bf8fb5380f6e30f040fdcc67d411f629f02ff314f09ebf089ae09879fe2240f1d37c99ff977911ccf093e531c8fcff69721c32fc4f38de7bceb3cf92a6ae503ef6fc2f4beeb56b035d47a9513350ec963f99923a55f1bf62f913d1a9e8143b1999924c4f4d4916b8c07f96c92c9b59a63fc39f6559863a92371f8ca1a6c89b8c8abc69f11409e5f21fd33f31a527a2579fffbf03474e7e8ac1fb7f1cde3fd7d164be8e7f8fe3e0efbdc77578cff90e9c9cadf8af6508c58236d4e5adf731ef8dfee8a7481ad4110a69c6f09ec57fb3c3bcc43c4ce6e5a95b78f3adb59b9cde8e2e2f206928b700370d411b6a229a48ec95370dc115e4d33711cd18ded36e356be8773e92867ecc437863838034f459cc2d34f9e7b282d026bfe7e15993ff3d1b04b53ef9bdfd2cbc6ff618fed51f9ab7857687f9defbaa8f8bf76d272dde67f23ed4944e9fc5fbf68af7e9fd3a254f87070249b3c40c5324a71f737c2c86e4a19c6c386ba0906cb23ffbc3011bb0cfbf54c6f8185eb6ef795ad6c4f7a0acb9f667d6d09742f6c7fed81ffb73e34a4a69a3fd69d151ac22f6677f9cec7fefbf2d04a3d38f791eea68d6d07fa44e51ac22f68fbc914042f2067f669958977d6283eccb4291379253490339f0c2fc2fe3f9a296d85f7ed58fa18ee00af6a1ee1ec21827220eb430ffcb80a4a13f83a4a17fbfb812eaf44f44fbdbd3c545979ff1cf45ff4e44ddfe748a4242a71f2d471da18cd85f7ec55ef2460ec572e661284251e48d944c246fecd3cf45f246726ca9cd80a479a3a3295492be8abc99fc0294722076cb3f066de8a390ec0f267dacd3b7db7c10cc1854d6c8a79472f6618c94b3b15b0e7506660c8843a9bc60c5c8173cb13e3f4f988fc633792f2cb487a046f41f0d572ad158f94cbd1db75d2a95b25f99316b54c6ac51096bdd6894361bdcefafde363dcbe33071168e430587b346cb99bff4beba42f9d89286a41eb5951957784a7a9af76da0d79f316bf75f75599aac51f9420a8dca18b594f5c9565d92263eaac78e6f1fc77977bf2efd7eff420a97c629ff5e6e35acc45edd523321c319d41250346bebe7ea656f27e454a394d8fb61a098ec6f77ad804e278e7f595c620640fba4772af1e9926b3128f6a7c5fa7fb9b592df09e5563e226f7cb292d5345addacfd8df74950d4629a9176a44dc9bafcecb5155c213e8c11557963a4d3732bd66fbcfc1443cef775689f39c6a13f7fe63af26bbe0327f2d9a2c7ffa1a3ad7c3a51ab12d5224913ab469246a25c5b8b69471a525297f362ec637fd145743f475df5fa3326fcb87db0c438643803852e4a09162890011c1770000c15b000ca04fb72540976849e9611884d639cec6bbf03277bed7164af711d4d6cc671643a32be03a71e412dd92ea825db654e1c179037935307c050010b3c8c1903e0d4c20f423eb5207fbe0df933d33ff10e485e8305afc1421ae962b16a0409c91a4142ba468c18c1466e66041bd18cd0cf9b9431460155045504550455045504e564d7b7303fab2ab3a11fa2eaf3537d7e32ed47fb919c0a419b5965e772010bd20aae603520a9d5d5ac99cf62b55a9f06f423595add5177c4d2ea8efacc62d08605498721428408b9a89df8042ac8def4feacbdfcdbeb32f7ba0371a376b6ee7eebcfde577af8dad39cdedc97748e1ccd513bfbe3b0775f2ae9edb9cc73744f1f6e37b6bcedbd514168a076503ba89dd912b52b88492740873026aa50fbf5330c8d1d06ffc5fca3e1a8dfbf2deb81a1a124a8d40d5676b5746848200000082316000030140c08c422b124c8d238d5e30314800e7a905060469a0a04b22487511004710c528c1840003164406466646614008d08a6c2a56c1f42da7a304f435a7f71c507e29e3c383190c44acdec2a68cef551009d417fca17437877699d05d3d96369f2eb1d1f4266720d7750dba68f462e68cd529690779c447d5f64a938dab711853dda7a2521fec87534df854efa41546b0d48e1d7d31276f59ec4842a67504237525648c651295040ea14953c28fcdfdaae05e72fe3ce4dec206e57857315cbc696a0c073a01d04b28d1aa54d7e4babeef14ef386e99697fc8041ab6eebf2f8d12e680ad6146caf1a26ffacea9e105490390912ac1009ca957342cb4534a2590f75893652615db2e4b6de135a7c1e8871e4daa56cd992c761f9a55f0c6b433d8c661ce555b04238d0f3ac1a4c5f08806ed27d135b3b5ceab8787abc41ef5bb1291b6d658d4125372092df6051f8890ce428436d8e447786329cb605bbf64219a6e00684970965e8e1a00c2cdc02eb7b62ec80956a036558ef26b1d40abfe09200182603812b70236c5b5f1522ff0f8ea372274397eb3bd3a8ef20c90df4b6c13a13106cdef6b942cc7e6d0a5c4bb6cf803848dd5a64707adb9be5f2034c317adb9bdf95085970fc30eda0b71d642dd7d7f5d449d049213fa56479799eb4a4efb0bab4fd8d8d85955bb45afb2e03e88467c4ec24070ba0179e835d0282ae078969869eb50f3630f1298d3721080b1fbcd9508c08f3a3059640aadbfa51978e270eb307f63d9512535d5fdde4f9c9e84b00bf5118960dca144b03d28ca06021e27db8ad42fd1d2f5e00de9055162044a46b8b231a1698f168d6ff5b8b7354b8657bf6edfcec912964754f0ac350cf0009d85153406055b73bda4882f73577252dce5e2a3809c7f6f531d04ca58c73e0d3b6906cc121cccf72b42be62aa41b9c18f43903331a1503a18ed17c6ba00a5ef51a7b69035de9a9ccb3ef9e494de575b7a37db0cfc50c242df45537db0d79ac1110c91dd5e619d1e50193e0deaa3231c3b520ebca6659d886dd20c2176ffb61f6e5264ffc4c01d0f191e78cd749dd156e8be30888c9733ca388822b6ca16f6478aaf19a8107944c8652fbe839bfcac659c39dab571181158357768a3ca29aedfbe33c1185202d15047fdfa08c9b042cb3677ca34733ab112d290c9ca21b60e2386662059dc26db027af2921381e073ae044c1b9ddb6c6df18c9eb185d90a0205cfbe93e2473fa77e5a83ee1ed641b725458dee0d5809d1316af2d3e07c48e6cce88f7431df23d7735b8dadf2fa22772c3ff7c732d2366f279b24f8f67c07341e2589359dafb07c595fd02a051635fecf3d18a6ac9b78c92abe0d3474642f5831d17ca6b48eb1b911a024682d2d86c15dd0ace3e1093669074bd9f81e2003cd1dfc34c5d3f79c3d0f3964f3eb0930205242d5c1ad8fa16277063f8f509de4233b735cfcf51cb07e45cb8a0368780b977c0b0581053ddb12c0a9e87fad2f51ec801c6a068d828a205dd84f7c1a8e76da15eac7b80617efdd48c8914f04d1ef746287dd57982fa7e5e921985007cec3dbc5114b7f940ac438ec4df3dfd9c8c5ee35d881bf84306188bfcaaf100532201083bca647fbe48ad4fc11f95f2e7859c2571418b9873a01aa65c0747c2a3e05609d2ad930aa95b6129a607577b252134433d17b915ecb86287e20cc11238cb6ff8314efd26272646a308150583508e26473f568cc0a7331020f09aefa492ae6ff64b2eae3c966b4cb3a790f45b64c84318d83660e93d74dffa4ae9d6db66d0f0ff0fdac56cc0b3a9caf8b994c1063afe9765fbc8d0f4fcc59db337121b05f88b244e56522e4722d90b7f7c90058e8f5018294ec6b2fabdbca798c325fff23e41e01c27ff380e720cf79e71da807970fc10243f79a951be6d5cbf7e6b306d2a09c51332225b6218fd33f3b947c9f7c3c18d6df2ebbfd881e80b2e16cd7ea3b12f0e2161bb8ad9c5258e900a667037bf5ecead3fefb7c8cd992b2682d65aff262a61e660a05ed5b615f25edb8ac0dc670a2c600520e4ea8a8bbe8f4903caf001e9c9aec48f482f8e2362b99475a128e40086cc1c8e91f0fedd063b9cdd00fe171cf32e3c46b7258bc349c10321e65f93f75e2e67b52f3719ee846ebbdf2c1010ac1a22e845391158cef5c51252538b5a9567e1f8a4f87b658bc8d4ad609a82f384d43652a5f47c090a176e0214990df38763975d84002c536e26d565f242a7a70eca3cb165a5eb98ae631eea1803ca253549509c22de6956dbadd83a8fb4ca69094098adac7a14796f3994c0522559c718d311d7e087c5b1339629d4b21a427f98c10e3bcbfb79d9f9c56d0790ce3077b6425e47ab35171751a6e69498c53a043bfac51c5c87abd9fb2223a58bf750c03787b27695da0d9f510e0bb9ab445b570b573f1c4e6b7941e6af4e4a7a23de3de4a002545f129203bda74f33ba8bfee3198d42be50b556449c648607a7fa0a376b42e1609f2e74bf094ae97611122ddc6d91ff9292eecefe2c1a25e42247b50a98b292f1c122fec715a37716826523beea9c84df016ab05aa7acf1aea6b20385498dec0fbfb749e99325d9587eb6a86f6bec8e107eaebacf66c2d22fae28cec376955582697ab50ca2fa43a88187ee3163ed9f98f597fe73e32430656430b1155f602af0bc8ba71934493f3f1f4a8f78038083a00c8117faae8486e6d5964d9cac33510950a3e9358a21d095fa0aa9a58ba06a8840c2b9b5e6113945add46cb0eb101f1a1caeb48b45711d135fc4c2420824d806221df8f0cde0ba066b16674b3ead316ebc09b3282fd0b571456fb3cd8e7d639d5610841b6966f6b7dafb740cc02f135b2fdc67152fa29a8f7aad36331f60cebd0403b7a9e49c42208066d83da3020546e0ec26d7e7941829434aacf85ac57751ebebcb32dd830bd96151fa03916a086c9352bc530369f4689c7454b0ede0ea5c1520a212c12f69d68911af7eafd1c1ee02b1ac018a75bd1d24b49528cb0a62d2c9b018a43e936121f511128fbdc4bbf495501d97a41d4e8f1e3411429611f28b051b704d1f5b592214d07431f8a390333f50cffd2d2ad75b353cc9967d1fe897daf4b81a5c208d4cd23331e11f9f3d22375753fe007957e2e726efab7d4710b11192197b6d81ddcfba93dd37481abbbb699f19126457b97cd3a895cb4a7b5eea80cc85788cb4adc373fc96a4e89386538e1322b736853e01dea47a40700054918c6ebcc26e4b9f42ff2c9a8420760e13eedf40d44b359f9ad31d975e9de40a77a898456b35021a78e716a0d5c47804c4e67e04725131d5a24902c8cc3671e348c3cd4d577063650064f67a0e2128713b0219906795a5b00f23704f9f4c192e81f4ee84ba98a5aef1a7568f0d026fbc87699a9b6c2f9c970a5a8fe4cbbd3a2162f7d7997ab6d84c38072c2f1ae242492eb18da3500f990660f22dbe75d96ce0039aa8bc3e3a27617b778cfc543c87c93150892fc45dfd804b182e3790274fb74340cc02422e80df8cdfb22f55468877a71906f1661eb441b25531092c8655218bddd84cb870149acf42fe334ddc23d225acb26a344b860d7dd822b1ef85da727955bc98e2525873e122e84a30c1528b5f26f9f604c174038260c5eef7b8aca6fc0c724a887174e4e62df56989dda8716e9bf669edf0438ac719e445d713e1f149782529a5fd74bd9d6257e6602818fce7c0238a3f2caa536f2be3c1adf47aa4b19683e5c0b2d908cccd0575dfb0262a2e836d59881b3f8b920ba2292fcd0e2b6bc30ed3c931b32598681031deac862e57337168b32f1e60baa635aa28bcb9a7267d4fc1b6ceebdb0bb518cc6a9f08391b71c615724003b38e34ac00567dec87989a363a7dcdfd141da90146a6567f4d66aeb734f220f8f436900e06d434c6589cdb8b0492732b3b1a0c54958192dec017bd60c6b0300e73d65da8d6674da08e6a3bc827844d030340cd513949c25019376079604187772bc8b591e2b71e759d0db75feb434120319bb6fa7e0804c681e45fa421782e25d0a5d12cf3da8e5ee18d6ab580e6b54cad90f2a74f41752d846242b45faefc26829e66bef685ed2b4caadc6a70f54e70133ce2de0de02486fcdfd5b8f15b5998f1a16649632f50297605c28c2218352f7fe010852acbadf2c52a9331046380854444a760898a63e2d4cd328ec5c587f05673580e916c6ce9d22ce0b15e4e1150c643cf89372a151881e4d8c114d15accb0402dbb611c54a308c03526cff0480ec1e627d161001603c73d4f45be54afc8a655e5a205eae51c31dc3ef00065a84a6f975618612918f742b89e27b15d4214092f7d18974c6314670ab95fe8461006ed3240e9fe1554113acdaa39620dec7140f44c906484b8c4a052842a3546b3ab7cb687e553693bdac3ad77604a00a00b54e61d85017804845275dc6e185170d235ed9767779808d8c8c96bf276ae4db93af17f9df0802e6ac957eb38201f56505fc068faec8e62563f0eda1f16451daf6c40050724415ffa0eb0703736da5c237199adfcdbb0b7bcf159e952324be486538843df1e0dd053ad1f36ec919f969e1681d70ea9f7bc774f5e6dca71d00a6ea86b0412b96a93d76e276de50d800927f4f9443855f01ac623602ff30ae7f43abb458fcdca82ba51916cbbc72f4b4a9231c860bb5df3ada5abb1f3318bda4b59c34759f994f4d92c98b903389b1635c3ddc56a7825add6fad81e8b4b344952530d40d621cd8ba3e7e12d396952fe6ad4bd85fadb3b9d87ef8f653ae3e18bd8152dac204a904a773a32b7025f42368be69186cf66b503a3d1e8c3d8895613b0b63de0ecdc8e3483970652ef2bb84f3b45ac1a3227838540ab3121731536b6faa2f17b2fc9f1c2f89815ffdc591fc58f4a526362ec9bdf086b4fa83a6106daea53877e100dedaa8082ef31ddb122c1970564d658d84a1e468589aeab5037f09441d0759f86a9e92cbf2eeddfcf8b58c92e3b56a1ba575838eb0cd5a7de3bdd510a73ae7b2118ea701ddd025db0c553c760071cbc51972233b82b74750915d9d2b13c779c163961be99ff2c15ac7e8a320cb82152afe6759757b8d68c9b129336398ce8988bef6fc90267f41cf6bf1f9253d422b0300f211a3d1ba150830d9116ed3adbd5f976116676f1ff5e966a54d2c674868250db97edca84481388da36f0cf1cee8fc8f20b30c12eba19398dee9cb4bbf67412121a0428b55ee9ca3410163a9fba51e427d91d3964e36757d8450dfc52788048b695f808b3fc122307eacb6f1540ffe62f3a274c96413b56f7f0408aba9eb747e0e00f34860f29e0955615a1a28551703b5e104bd30d6a32f064c6c152a9c379c40f1d4e56dedd99088bcf1761db2c957d51533459c7eff3ee710ef01c083ca18b82d3aaaf6c364f74155bf70c440a34203acba2129df76d9919f817bd25d6d336e456dbef751e0053d98216e1071ac7936114aca6617e61eec22ee417f28b255aa848e0455ff15a5c40455e9468e48927e90a7dd80721582e7f7bf44107eedaf05480b0054dc28b068366816b4e36ac586e7df6c1c621bff7314878ffe5537eeae24073573ce6272a0403ecb6edf5196c20e1671f66568a2214c6161795aa9a4bf9a2addf8bf40ac606ae930c24339c7f90d33a7249ca68e2f4d74083f7e7741a03c69f6cc47936376b7a0b8039c4ea077cc68cf8db65242d45dbdec1265a7187561332add0fb6b42abf3ebf487a191edffeefd94d4396cb2a567a2f064692b7dc3e7804667b1f0c4dcd07a199e45a3ed219cbc1caa348d00904e11591bebec5371f62fedff8635d3de2704c87ebd20608d77980b95d4d39a5a72ae237a3be52a9284f65cbc2c8f9259a152925d6f8d2c5a0a5069651b765579169e6dbfb17a3139b3ab6612d4b06ecd9e80f5249fe4e1f9c5bb96e4fac9ec4b5628c01caa042a99952c758b4653ccbc34c36c1fdc8617a4758e76ee886a45acef069199ac747656d6015281f3f28709d28b5730640acb28f29511b7c04f5cf427fb757043b61847b7c494c39a099e516e0f35531d18b8f9e65aa385a55242851fc1f85bdc8df8e71bbcfb2b83d0c195c515d6e77577a7d02adb321164a6b74661244764d22a98b536e964e186f494490ee8932b466115c1061c85162989185d16556b24aae55700aa5bce36c777377f68db0ab361decdc4434f01a15f6902d76f3f6ba9e82a37b800970f04edad77095d508194e2193fe93ebaf6d8f60866bdcd1c84bc6e4b1beb7f39868e395d1d0296f7a9b634e1002fcd048b60966f221a9e26bcaf9a382bd25665cdb2aa341500717b74cda9da8939ccedd12ede2b9d5ca84fa025e5512862aff4008e17840722e32a47a6d30a1672649358861f04c4ea793ad4ebc02bb47cafc84c7485cd41e0d4cdf546cabdac0f7e75331d74797c5326d1f4ba6725bfa71f6f95466cf740feb2c0ce77efde13c7592deedd1313ff1284f1d733ee06a07bf74cd2699c2e960bfcaa3dab4d77bcb97440eb5e4cedc3b2c759d1bb27e591672af9b358f9ee69929d32a7d296f78cbfdfe5e2faad80a35def6d366468b120ef75a9caa4cba99f3954b1a76ff81e5087dc73d20fa815cbd62e7c18eede17d03da5f8f523d43ae460de5c9758a2b7941da45f0e8bae214d7354efe50d857bcf3e18f23dddcfc1bee7229f7b5160e1cc20df5028517548991b2e776263f188132333a41b33ae5a5915d0f1346c052d6ad205766714251ae64a614f889a9bf5be6cf20aa2bd0d04ee321f28f955a2a4efea1c488750acc002e51d20ca3f7e39b59d016fdb59ca706b9b0b47cb5995e06f350f7e2b9cb3c26b394f019c2de62b84af9dbb195628467c1a5c5a999ec43d5928077df4724614d0e5d607e1aa222f0690cac17ffb96dcde5805b6c6cdb78c61db6e48fcd6c7841ab6784e5e47bee2374160ffb7ffe47a7dfc952807c4d90787e524e479226a99a5582d2e49d5e8fd59c0286dfa3390f430d00787066305b177ee83c3123c5eeb99ccd5bee1f19ee8839348c2e9db73d507072ebf8c4fc04ebba907d407c7bce7da93f726277eb1ab7de7894edcd415ac97ad81ae53907e729bfc07203c2a5f739139777f507ef1a993f2327bf9f623ffc495a8aa3688286fbdbda4586ebd2e435de18ddc5ab107f8c5c4d7db8f081d437a3af7e03caeed490b0babfe21d384fd837a018f66b9259becec469fd22f7811abe506721f23d839b8dd7e72fbfa71af212a2b441ce5369df762b002760e99fba08ffb2b508e4e14a6bac1713ff9b12221c713f9fa0ceab2daffbef1171a0cd9e01e9dfc17bc694cef11f12247d6136cb44ae6c6f8b2c651a92d97ce34907d2f039cf9b5216eae014a0af420aa9b631cec923dc8001cd5d77f312ebe8ac5f48f5ef6d7c91fbed60e2c9f868807b07405ec6e5b1898cf6159ccb7d9092bff991587f1b2e6bfcfcea3f8a8be2432dff4c63e69b818d5a366c3d73a4b61831462db3594b2087bf714d4728d2aa39dbc72eae5cc17af9148abb2b1946f2e6dfdf8e23eca744a36cbf9e736c02739b661a9a2cb3457ea5e420141b464cf8ed61ea3a646db1dbd1ef8ef9d92f053fa5b473d2cab0a079a65d1aa8a04f8d8668dbe4ff345e115e15beaa9ef331394aa62bb000d11e40622ba413644d84a2fa3ec0495fa9902daf5753fc1b6537a431e86b1571b9ef26e2fe6e49d3415c71c3947e8c0d536307cabfd94c62ae77370b79a0517bdef84fcf70d0778d02175f21c2d67c19130322e6f37aa45698899eb0cf8bb7f836a250de8aa65fae26a2e4f7a13a15aeb3e0dff68b08965dadc00cb5d9236eb55eac077c805c1c501b5d9b8d734b2191a30733772e54280929a0933a4183d79000a11dff6e72b32a8839ad53bd9dd18fe515a37eb4bff474bd50435eb48c00e8fd973c56004e854bb147beeace32fa481eaa67593ef5ee79dce69356e464061772415df9d5b7edd971a32298eb2ebe3bc6820ac8d0ad16a942b866166464356a3ede008a07aca6e2cb9b29b57836aaea25fd4c1bf14e3013162828bbb3ddb8972624a3000de8a4ecf5d82f6f5cc72d7473324ba06b6627ce50833786e0fa0bc524b90c98f67ab5989219d16caa40c50bb357d94e168182d057cbced82f6cdd796aa02648f2c8a08c384253c1eb1db897d631e96119c228950704e6522a40625f5b45de93f5209942dd80d313e62ca67a9895157a778e681b9fc3c2c6a6ae0ec4741958bd2f9ed84d2842a1bb3f7c3f3d08d09acbb2ce282068ccb38638c33356783de64dd3400fd85176ce0b700fd7ce5eaf2b9ae69cfab369c022cc101a64612c405ca2cb8c1350e83b699901d7d4abb81dd03507bb6a94b92a046c2682148209a752276e6df74bb9f68c5ea033d7166977361bf1155e48ce20664dfcbe882be75bc48d9d2871e6d9331414eeba569a6f53ec1d66095e0cf606b457206971cf87ce75e3a416050532ff8c69c42c47b1ac1c46b835e656b83929a86ef314cf8243ac6ffafeb250416fc85556a22231c28b1216820399d69df923e1f37629041fecc16b46f44491c5f579a67e91d8c29f9bdb0beaaf7f703f5600e95e2b10b664ae44060f3e83c9674ff54024f16c06be9c10fbfa2ca9db07b61b52fdba4b44798915713631bc0465b6f1a0f0497d2fa9709b081e5a9841df20b20f2ac61168f4d4d9df3f6ff628c9abb384d1efd8b6c7f526452e8c3164efce8f5f35a2edcbbea08a58cbdecef53bf63e6e0f49dfd092b8c41e1c824b8b9166dc77cb9de3a0f8f531d2ecb9f6a703a0e25cbe18401df3fa9391bf35f9cab54603fadb2b798caae899aa280855b813f594f6ac8a88420e15672bdfb16028512d284272272b5c1e0a419aa343c8584ff9596533645c279d26d9863ed4256d7468e3811c4770ae6cbeea8d7c36b8b6fa345378e8f774e3e455895870687662758b4654a94e2a8a3d83a19143e29ec060504dac9f98b9561ead7354ac3a217cf3f711b837d72f325b4a962cba875c21bd8cba019b90f68c80543356113d95bc285d8327f97c3db0e36d2aa256cc74fc65355f8dda843f47bad2ea15b1fe16d11563b51d758eda6c7e49d3d4934638543e50b0d5d5429dd97b9cec122f6a52b2f841e57306ca9870d37cdd2d810d596da796c2fa977bb93b4681cb8bf7d4dc1e88b722d01ff2c3e038d8b7a880a8ce8a1a846c06b6ae883e78804eefafc655b9a0ffe4178f02ffd1ecd6f80f5130a953b71524a078e4b6bfb53ac7559dbcf349d3a0d28c99f0d1a88596e2ff47ca8076feb8f0cb45df9660469c2252e7832470ca55b88e35ab5a97c0066de12d40312e762771921b777c08d553165a217414d1c151dab423c3dbda4187df1c6fa5de6b1775a773851a7109e8e3e3bbd8dda938204c464a01c2cf8f8939b2902100ed76012268611d9ce38916ff3f08220872d724ed982e777e9f75207bbb03d0ec6cd207400f0eeec06d0f789b3e22b813c656016a840f3dc363722c31dd4eda21732b4c3ac451ae0bb9a63d3aafe84792862fee6f3e235117ea0ad77e4544983bb1a21121e8345e5efdd82540cde150523eb4ac1d451c6c3945aa25bdc561c52c402fb63db293aea638fb9fa0ddc08e09d5b50582bedfe16dbf5c58747bbf4963071d8152aaaf52852bf3dbab480e05b2833d23ebf611a49d28638403fc97b6211074ae1557645afe37a4a0fac87bd5d0dbe87bd1d6c779116a4fdc9f118a4e188dce2860ea6b160afb4b94da67902be2e709fe3943687f0ec03cc37a5cde788261092894b9b9b759dd30a6906ad14f6a4e9aaf5697206ee554f5c078e24dcb17512c286b36139a5f7a0e042030f62ed5ce357afaaf32661d9fab213e89750391406399d29719891e706cec5364472dfe0be17e496861d5d82f64e87c51ae3638718db2be4b3410025893dfa8951af32ce69b209cb6a44e9da58da3f18d96179d3e911818373823a5878bcde5b2e43a5c23758e116d7922156bd28608bf7aaeb5af04f313628cd5d037b64f8853813d11a47d951eecead874ad069567f74331ca534f6825f2af4795d138d6661a591e9d1383703cd9499b61d5dcc458eda8efdb8d04905a0b5b6dc0323af56215a39fca0789b8e1e5fe524772f58a10aa5474651076b4e8a778fbf604a5f75ec4567c23e432a4a5d8e008e9809818dcc2e3b4f1049829804292b9668075923f5ae05901a0009ee9ccca70673593ca5cdb498bb8404e2271a41405f7970df8f335af224c4738b007f45a39db96e3590772877973935295990dfee2f107abf5edb2dcaf74a264e505f322676ba926d649441e1ae7940a77d8adc2a667cf1cda6e40f12031e242501de98923a5ed799c96cbe916f27561a110d3b9f475fb8242758416c23713c32a9ec467805090b607c94e808b2ecb0b09a6035f017f8a8fd590337dea2787093aad407f320d4f577b9ba90ff7e7daa1f8132b29b9cd66c1767c70c1f6dea1784322ce87c682f79383b1490b0a5a05f042b1b3294b0d3df0149e84bcb81066376149b08423ca609fd193cac0f1b3a428dc9698d99ddf8916369064166b4843b50b2490eb0ecbc58a920b026d0da013db1145856bd82bcbec5deb71ced291d4d1a82b0b605d2810ab72b912658fbca493ccd8d419e5bdc1d3fba0465e5e120a517137a27e0717cc0ae4951a4d2930113a2fc89c266e918c49a80514fea0a133ca15edfd1a50a872d97c88bec8d113d51531b8fa4036058c6243e1569a63b420cefc20ace3d135f47072ce5cd347003c3f64e645efa0bf1f5fe9fee02cf5b1d02109067b436d4ec3403e6536dc972b11d1df358fd7da1a56a22426e02e925874c73ccc9a07e0b08bf2fb4a98b72aa3ccd64747c34ce51d564b557a19e6c5432802da7de9eda11c4da41cc22618f00871a45b8c539116690066511cfca430bde32e17d5fe0761430989e831d262217369146e01927aa70de6b8c1d7ec90082a3869f97ddf2b44f3b14758acd49b8d8343e1c0c209a5ddaa3b68689dc622d1e5b307660d262ca5c9703fd96281dcfd01b93a665bf8435f5818413bb05e5bebe63286ce6257493c72e4414c4d773d80f4b4e14a0e8ca73e7b0e3015fc8e79d1e8658e2bb44d1e5cedbada35a5acfd13f2168fe93e85fcdc4bdf479652c6c508ee8171e613f26b7f6897a27d03dc96792490647aa2e05416bac026361bb452e361c2eb91e1d02729295af66ae0dbabe2023115a2b43c28f42b5baa4ad339f649371ee1d0e3659d7d07bf9a2b32349895a0bc6095aa6b018d3e4dbb7944e0a7d56a200d447a296bd40b457dccd64bd667259f6a13bf95ba988f06b7d25f749e4721c25c59d6684b8f0830879f737a7a127ba05f80a6ff40be32a0c4496274ad002c019b71d3eef65e0f5daa9e5c6551dc4dabb827dceca2611917100f531ff3c61d380bdf4d6f5a507262b984d9bfeb8233c1d9b52e854852026af27908982b29c689c0e707c22334222e51d5cd267536ea05b8b79adca4e4336d8a8b6034f28ccd08d6f9c23f636e507f2247fdfa469029e87def2b4885f2614c40d0e1914553290d39119a95be983f4e10e9191f96df5dae759ec9efe0d08cadee6061c77d28d07005183bd53612a1f6ff6048e29678c872f7b92235664be04dff9932012fece36c713d9a40d4a531ac6303d9f22e59634882d7f92dc9cd61dcb7769a92aa7c77695a8e67407e6c3c24b19d791b538743c049785561d4f7bd72bb2554209cd8c972cd398718e7b98025dcaaccd5b17fa4519740c8969d7e203b1fae13e564d0f8622e150951129a40e4ac311c2a86f7abe212ca14725d3cab61efc60a77290bb45f4e6d9256097d4271c54597f9f5ce94fbb1e166a8bbb84a4dfc774b7221e81e30aac467186a10c3ed237f8256a3a5d0a67b6287e2924a0af97811abca44aa1c02c43016d6646f3d203b856884b4d255245494baf752b12699f9fe350ae7985c79096e8bef7360d5587822b3c4a46994f37c1958078960f1c941fe29a41e0a016208379b067d52601e9eb3f3a19fec562fe23c586f95ac0411df1ce484bc75f678db196ccbf555f5b2278fe926d73cf9412baa69d052d78497aab85150181a747eb25b41d7ec0561cd103a969d5daf1106bc4237b4e233530f0c6c3e695f80ed902e0b59f5fce3b918eb0b0fe280f5d34973ce0a238a45ea5ae5137e8f687cf9648db5f64bf44e7a63a51529048a12db3ab5c4dc465253f5c1169a041fe85f40fdd1882ab5a046260a0a27c48e743807182451d3a87a18cd5c513fbc12f75b3112a6cecad385d150274186771cf821a101a3cd4c09d567f13c46dd14f662d39821495feaebdd1010408022d6d162c00828283182602c73c0778419c91f74c22a0c68486507cd7506721a000ffb4a11438be00403552a442002773b9c050b6abcb7e01781823e64bcaf714fe5df5daaceae250571c1246b99a87e6086e6ed9050057c6660d8edee33f2a31e5128abb35672454cf3f2bffd980b04f2978d05598aece32b69d65eb7023958ddceb6c821b8c99d079fabb3edc645c23b31305be6ca150ffc9332ac174ec8c3057fdcf73a5dd23971244e42c073e6d9cb298aa4b2dbf34d22049a1b29703f32c79d13b50936b31cdda0e052c8ff3a2cf898f07d58129bb32805ceb37a75835c55f8545cc2bdf13a6f88333da5bf7bd6ba0a52e5cc0c32e1c0ce8f11306692d7a59680504934ef3854ce1120025de9c401cc4304c8e5e29606c60a42a20a1f85a5542c690c6ab51e39d21f160bc71ab15462f422a0ec6e262e10313c285c2780b0f54f1c6298da5a776523b6121dfc2b3e0f9626b18859ba5ef95a14013cb2da1947c9db30b051872c62eab7d5b039f8032599d28acb2f213bc23a8334c2a963217d45ee2e8deeb7e959f402bf5ae3a5732aa6b7b570f2fb9f1317abcd0ba37d0a6ff0183875f29363d95d450ac331aa05fd125cf7fc4f084d8775b3c812a8e3160fd3d38abec5f04bb5704e4c11dadd24d5c0a6fcc3bd29007bb47e001c001606c55d68724ac672236bf3613e8803a177cfc944351159fe9914da1448bc18e6fb0621ef9400c1be8aa080868af591787cf5f7c753994f95ff70908ec6224510243c43a830ff6687bf9159833862b14ecb700e6ee203b66beea01bd4d0dbe1750f4534ab38427498c5b42c463484e4db323c6fcf0daec361aa03a49e78ba8a11a95a5d827aedad8c1cfd346f86abb2cf306a156f3a80ca1282aa0d7ba1b3506e0832a051343f320fdc42a9d392846e02ac556502a76cae7a112e57c0bde105fcca20fc3f355ef7ba77e3b68b650a4c293df573ca509cf4a012631a5cc6c0598950fa2085aefb197574119d4e58633e077b31f00058c33819c6b18971d40065dfcf0233b750805679381ca69f2dfa2961499e8f13615f65033508bbf4e910c0660fb5c797e5b4261f5278dffb09590c1dcf3b4d06937195aa3c9e7af5aa0037aa84c098958ec0dcb4c6ef784ecd6d3a9802c96b05fd3fb2a39bc970514b314779818c864b38d4a7ab1fc6384f0cba9fcd9004aec79b8e9f9d8d2b032ca4e10f3c42b062789dff41faf5f2cbae503d8f57cdb12c23f582a61b1843b8a0d10b037fde892e4754b6dd9b9c750d16a62d2b823bf0090030ff58d8e1d3ecf045522e1985daff59234c6201489e46bb1f6011f62adeb11800f9a59a7f657722012b9c937f722445922b0685fccd4cce19fbc5c9fd1c9e5a1dbde1f5aa73d471b64685f0c521ab3e48bf9fb15bfd8a091e36dd278c1eedba2bd97454009de8fc9740371a84a6c6e32f818f6bcb242d87ed0e72d2c9f49fd1910e2937db22f9cf5c3e9303f03a338b348754feb3c301d08ba4d632efd7e8016f5c57d387a2a295ce5025e2b6647c63035767eed954a1950142c849a68cac298f7e49867cc1effed68560ea151d538988bd0582b72ecdeae4ce4e07aa25a5ca2edee3a5ecdc2845d79d85328abdf358db8a65e0bcace526732aa93d9f9939ea885d1340ae356b63327d0e164745eec279bb92c861a7672479333fd9823f083dd885096e1220aafd771c2078b6d83aba4236ad8a533a779d307cb21ff44766ed2a5209616c8c80d9b37a8113074be6f57a9b2f79b9111bc87d32ffbda134f306500c64425f2e10d965fb00b8befb07af39a61b061b0dc64a930d8dc9f101f2829c4eac5c8a5d7f398ef098a1b8ab35f935cb8d2571a09169b38a24384047e7729065bc6563fe15c39315853e0738efa0f5d55261baa63b6f73b02fc172cec3c9dac6862afc2fd4c99063d607ee5c4760b968410692f10eee136205b078782790fa52a583c85cc18f818d136fc26d265bf47add667f94f7c73d246a0c98af98568adfb25d881a4ab2a79e00a5c7fef013730b6860e6c1699cfa318294909167486281b29091688754e95d3f7439dc75fa69620c914132873d311c521c04aafb6eb79ae7b53c6385aa37cabfc2e94a2977400a7c700283e6864d5da75275027a234c304ab81c270307465f12bf08bcf505a8fca2e32b8a8fb2fc11d93cb284ecee18137b55ea0f0a78feed30c1a1d39141bf24541c131287c929de17abc1fd53a022bc955a7342d9e0db00865ac9ff9bf52652c768645445ca768ae3c6e120a2f9c36ccac18f7f369578964539cac3e8b3d2c8626174e93ed05d50fe0f8794938b5079be15c81804e766b83c60cfdd1b0f6549c4a0345f926cd8cca7967689971517f03041c5d004ebcbc279a6e88e1786d04adeb5aafd337d03be4ebc5e1bb35324f5d6622e8db73c4c6533193c0a93bb8c072f5419989fe8581e6aea3108d60b0bbda1eca17d99552e78ec7fbda1d3d9fe2ee9564f15a7b7d35e450ea2f1241c44c0255e74fa2b4d6c1c0bd30a64464c5669ef1cdb96a5a7af3c2f8d887ab61ef0e1e6d06c2e411190149cf7e16acc516ab0b0448beafb7d9b5e31592b4dc049f03cb719ba0bbe3a2f9b0885ccedac18cefbe978879ec83670564bf7c7c9e8a2c3c80e9cc09fc64945663b0154c02c93612a724f449b4142f908bbf8eb0476749267b714e3a0cc304d281cb16507fea1c9112757e5ac1f6e6a55d3185b5004bd453a9c4c2865a38358e5c4a2b838c1d145692891ed9529fb41e63e1517e1ff20b3504beeb0b6a7eeaf58eb77ed2c2c900d860f2dc07fa84111fad3c43f78e315900eab159c5e460096d181e8e08ac05746ee0d3694b26736b14f070721029a0e11de1e9fa7b58a60c08dd72f8841f374c29efd679232fce16baeaf32deea991f8e97927c753ef046b1cbb713e2447aec3a1993a136d8221f7f6a098be46ee11e6dfa4ea7804b3e74ee9c2c000958c60596255c88554db18e56e3a1d855fed4a02bf9ce475eeefd0c5da873b63e5887a5828da2866f62491ef076760a13d2da26fc45a12fae22e076e34465106d609cfc6a22f3a52aeb68ccd8604719361c7158319f820079448b4f434baddd48e6c005e35730d40f45b7a840e206102f346e6715241ee633a3aef67aa9367bd1e25fbdb5508b28554b5c8fb1337cfa4303b9da854092dc3039e0bc5661ec800c79a7e20d6f46a226d48216d277bd2fc11fef0458ccc4d17305ff9573aa753968afe2e4eb637ab045392c5c99ee3523ca9a479346f618606865aa9c7f4103c36b7882a19f0b40930ad4d600560f30c5afb8c658a5651841fc80d74ca1e013e147e1c86f14e3f80eff900d6c2be97a207e1c709bcbdd9325dade7c4b8c34b84ba993243498671b60da82c8a2952a0a8a14d1117a1f97f0aa25c83a8440843239f703e7425f225d22efdac536cf442d146f16d35a0ad98c66039caa117a82d297b63c055dde0a698a2b04ed0d22e55a79a94035fa8d22b6b4ba9993480452271cbe70409b0a124b8798a7082970e39a60988345e83a7ff664c87721984641cf95639a451010b0e210c360424b4a076c77e38b50026efbba3d88de590576d5d89ac40080027fe8e6589042fab6073dad31316a4a6d450a1bdefed5c425b62f4dab7dbadc2ad051c1e5270cca276bcf1612f35f16d611d5bbbf3bd51af3be5025b0aa4410c4e600c813098bdb0a792d56717ac676b4945a1cab672859a5fc0d2a526c74f113089f1ba2b6de56dac3899d110b53f7409dd1428b1b6e644a76ed331bf64f814bbc9de6f1376d8704a25276801c2303fc953ee604e3076c0bcc7531634f70b3f84cf1d02c6030a67a82808c44a7db5a0cb3e5a1941420ad0b53f4d815c0e64c2a0bae0ad17c27751d0a70d94790cb7b0c68ee7040956c26d299b9ede48254e8e20fca62dd0077913e9abdd76934680cb30bb61421b8f302f9ba2ec4ff418df6006b38c2a7a4eee08e463782c13e7c3595124d84e7cb42e825896a59e7574e3052535caad22ab0242aa562dcd209823c0a4ce6a979bd496506cbf5383d9b61807207d5f1ae161361bd7da9a7b0870faa6137d108888ab8a2b7446e63a2304e42866e949798ab23c6d60e0f3cb6d290a45b8f6ad1d7401872087bd4e192ba4f7b75fa834b84758f1c5002114c7e7a1a5786b11c15b1101ae9888b0362f445862eb5a0108311a0f44104243a9c24a883374d1ef7137e1e9b420b19ac0022f85458d340bd738c186c70ae37f0edba954a91b473c5a55c3159807b5966c0dcb5727d5083a987c77d2e6f3b2e80b6a7cfd92ea445ed645a24a4d71370840599dd4829ccbab279177c5bb58d25e271bd7f1b2b02ba9e0c04d867eedc3b67fc6cc93db304003a68c95a40591aae0f59d414674e4beadc212cdc9cd304445b0465bd6d1bf5a8dc8fa54e4e12cdc829601a9db7280edd29207ec4f8e3c5a0edfea84b6457134c1426811731fad6082f5f307637e254b983f11c186b28404071df26791fe9bf878caf26784ce8e945d448eda99b89638e4fb93efe6ffa0531210bf09ae6d4eb97838c8a487b928c70dee02002c0374e8046386abf4ad493443b78f955b09974d9ad24845ea30f0cdf72f94e070330d692feecb1ea926c6cb5f9325e90b725d29846039d2d8c5aa2c7bff864556d82c6dd1fbebd5e0a979baba1ef8f15c90a23d5f91351ce868db0b65bcb9ec5c4c86dd706d6e3c8d2cbf06de60e5de21bd6a6e3ba05f2d6ef8bf3acd1effac3950f9858c97caecf3d068ec80fe57d0e20c59ac80b9d54c502bb46e0e70f27b1a26cbea20683b489a35700dd31ba71dfeee510025b2683c98b03c9da6b6d72b7100b43303fd80bfe698995d3fd60a4c2933110c42c213388a3b4c17b4abe4deabcb702a0c81994530a165bb4e822c35f0b15d28d5d3f21a16a1028df146a90fd09ac671280ed371ad7f74cb2895ccce75e8a52e153e24f5013972c5f92e6bd5aa425a71308fb037107c26b4a06ca4635b9c3fdb3b88ab43a3b18cda0437badc616832a7d4706c7bbe34503a28d47ead4ad40d8f6fdb04fc12bd38fdf1cc35778e2ea65d5491b4ab7d9e1a212a48cd80b9b2ffd842170672e49d0ea26eb517aaa6a6610dbaf24ef2e8cfd425a95d41491913e149ce36a9d2b47d9ad6462cdae58d8c7f288a2165293421f317ba0b005e0bc2a3d141bb289784f2e8219c85d99a39e6798347db1e64999b2e2438e6c5e37673e57663cb5b65bd56bd02c6432cf562671312c45b680488d334b99f6790e57c89973e4543dfda00ed1dfd30624e267c3413c6f9fd618ff877563649c095f8beb7ec4313936743cff8a46a2afa89f9d4c585f61688ab6d4fe23fefc7d9d6c3fc12fdbd8a0cb2f68ea8738b107e85bfe686d455bea12c082dce63a011757460ee82980ea719babf0e133e8cbdb71e0e5dab4f35eb4ba4ece403f6bae46774039c8c66d185fa0798d2fce8a2a3b2ceb883219b7f13991456c3f5b9098b412c055712a7ae7cf460cb8fadaf9c9c763475473d624a885c4ff19cde6eb07ccdaf06944f06e64e23a712b9f10da251dda677359abfd814756aa24c206306311aa049d14ebdc35142b5735f3813421a7c0af865282e2b56f13a999b9994293ca6b3804fcd5ec5b4bc17f16597fa772f5312e2e7ad6e5182a28c29a2e9ad02ad6d016c2e188f105ee73261851b992ff48aef92a479e3f1f368228337eada3b42d697080a68a2ded45f515ccef1273fcd66f8fecd30ac4043402315d6d761e22bf49deb50894d461862914b1202dc8926eb11c607ef2296a7fbb1f788b57a511d829740bfb0e7523fee1205c4a11c646a2f21f06cce5c92da89d0802421c919fb6655d42e44380167a8744c212880b69992e769185f91e48cf274a76a490351f64104ebb07c078f388ead61e8ca46d7cc47d5e72b43c1e2868a8fbd8e7625fd45d832f96dfc0c2aebfdc0552610a684ce626a6b617ec6464af23163c2799f1cd0699c0b65395d6be22d225a1072612a5dbfe75be1b65e03804aef9d4cf0aac01e74bd9c2a80b608edcbb09b4be8acd2967a02dda0af57d885ca3f28228b5ce638ae7d6eabcd7251d69d0b62dc820bc3261eb35526017e4d61603239b3bb68975d00ff49301c3265e6b1cdb20afc6d065780d6aa4d958a144aee0321529e9233c33cbd7a51ba0437b7fecc12ef09d438401c06f7fb79aa670c204a04466187b00b182c0f86986390464ff41a2fae9a41ace972028c73389809dfbd1c3b29b966171513ff35980cc48dc6c19fc7a3cc0582eec09edcefe36d3c7be49311f3204c9206890384b44fc426e36c17791328f6186404b4d049d972887b23234c007e29235b29f379e176785e73271a550b19a5adee039e8a311afa2563b003750aa752dd1a9ae5df7a82ac10100f6dab9bdb08f073638b564281b8ad2308b4ab2d10319d26618a47fd6c5723af328215263119076efcd68d804ca8b1a0f3e4a0820d40622ef99a4b305137cbf5abc498f602114e726f6a560bcb25addd9f082464177b044926c8c56310a133da36047033d7e9ed41dc978c74a8a3860a0e426cc852f97325fe7268c7e6463fc02986da1ea96beb65e0950f0ec535471df9b642e9b6445b4d9269bfee80d036524458b5ef135b2b8e2eb755c0aa7421aee2fbc2320762aabb3ff3707a07eafa2fa663f1420826fbd1b52c479b2d541f3fd2abddc616e68aeee5563aa3365b984f363a097c1fbeaa65cb920d7d7ea9914aedd33635a3b62bcca7363d297a1fbaaab296f447cede6380aa70a3175c2ea2d845aa9db2bdc6a9132e21de3b0f98c24a9633390bd7a77c41b213b147dbd23ac091d4205ca098350a938f49633abc9660dc942fffc0d04d8db11a56a936f643830af6fb1e1954a94f58b93406ec5496f490c2605f35d15a2ea1abbf7d56008054ce9399c4fe74790ab17ad7ba9b0fa2a2de4418a7034e09fef2cd0fbc5ef2ef91431de1ab1170d7269b7c5375c81b8326997909fb43c6ecab3b091d41534c59cfedfc093dfe3d076b6dd99f7e8b1f90790746c79a2a2b1dcbe49d2a742309e90fe323a4ca6421217c04c9123b2ce49ff8b5f70749f87c1f5e4263deee8f5d30019b5ea5549ab2f231db94c326b237e857d804e12d0285c9e1e197c8c0807e412cc001d321fbf7d578de41c6af7e4a7916fb1942c88af6b83e0abf837847c0a2cb58ba1acb1d2916c289d38bdb6ec784eafd7808cd193c73bf9aef4799d497df0df067986b86db22af52b9b88a2b9ba16ba95cf7644010f45c7a2d74108995812bfc6a6842ed61b9830830a7ba3541acb85908b9fd3b482aef2a7f9dd581c5c6fd75592b72441cc170feeb68dbb2e5df8d9e89be7602c707ec8303a30a41c3235c3feccbfd227f329cbdcf83ea9c0c79ce5a6e9c7901858a070136b624082f3d2280a147ba4ee700bae89a646b6eace5f4a02be35747fa768a7c70567c52f58896a9107f8358c1055c95fbfe467de20e8e9840234d657b200fe61bc765c835a8ec432ba000c6b83a26b2c13508f95ed7f075c98594aeb7111c3afbd10d94d5bc42050d833be679a555a0df68e22cc66881fceb5c57a6dd77647a859bdd1b2bfc96af5f8448ad57bfb66cef485fc42864ad20996a645a43943b43e2f2b02ab674c2bfda6dfdaac53f586ab603d281db5638a639c1fb1b959b01427e6513295d8f0824be6b35044bedcc3c76e1aafd48b0da301f1d51f63ac636d5c5fc72a52c401c1dd51d97d72e2f92537e91597455d3b81d38293201e2c714ff4ff286fa6b57744930903ebd9d8458e5b3b29767181cf645f2bf4231b4c1cf1317852edc72f5be0cb4fb1bb838d7c83db4b1aa3479bfd9e04d5c4a3fa8da7e1d594af479dfebb02f65ff5914515fa7165c477edfbb6e95251710bd2fb5d6b8185ec7e0c98f54f7a508eaf1d71afd7c8043eecbdf4b556c281c734a733f992b6e374cdfb72bf8628d6bb8d78a8b854ada90c3938bc01eb1bf25a8cdf68bc889705a70543cb5dc0aeba4395c78db536739f0c28f205115396c36b0f87ebaaaceb429cc060bd69c6a9aef00697c593a75acac4917d0c731d5483708c799d7ac13e86398d674651adc342e773d4ec79b4e9a66f3ea037a42e2015118ca029bce208c354f436710c635534347d0c630d7228f119f409859d3dcee098f2aa124fbc1993379ee930414ac28f2cd17445a7cf1a08530357f21e98f60f9cc1fdb7a378d9c285ff5865961138c7418f430d61b72d24d5030842f342a335f42a5985fb3ae2df1996f1c4e9b7aae1d77ffc333ddb7048249253ce03f02edfa829887855e9f41b96ded7dca9d1cdd434be8a9b2c940231fb369d5e332658bc81d9a67568ed550f45dbb0a7abb78dee19a1ab4955362dfd7237fedc65f88350894199045faf4ea1965b62cfe24cfee9e30fa2de999a024700fb6feb236e610ac6d49f16df069e8328c90c5bd8ef4811ae17b16b0a282756d8367dd87ddb91bb2d4a92bc2f91fc485ceb477814940c4d32e2db381ddf57037b14fcf3314938da9910851c37fde26ead8834559a2f5dcc0652ff457ac926c11ba349246969a879b6ad510bd925d3d81f65ff04d117f5988ab9d9f154ff3892c49db543a5db3c534b165872fdb23118b7f52164fefb5421057520d2ef67366055c1118b4147a41cd9383a4df76d166259f131d4f116bfe3a78b2d846e6bfcdcd30cf1014f149285ff1a3ecb85fe233ca1a17ed84740c0cb66f6ec022203d2779713be59b1dbf4863fac1ebd3b50603d14ef7e43cd06923cf779ba4ffaacf288c413f453f2b292b5f099e8524d66be3b03abd98eb86a4aa11920a2e2126f6ba0de83fd2414708a20e918fc6583b435240f0a32c03f3dbc00217500a9245c4d9e1e0af300b15840f71b97d693d41ec64795245745ff6bced76a949c471a7e1eb40739bf0214e3bbc3be31c0cab03e504b74ec0954efbc91018a1c96abd420069a3e331d942b381573a5c509ecc8fead043bca349234a2a362bd2945dea6096930c6ede8f2b0fbf80c305d63a18b9d6cf72cb6061ae54a7ad6585d9421955eb1825e716241e7ec348dd79b49cddb3606916386472335c209e79c338b9bb654f3ee4d3a0cf3a0e2e9a7eb4a2292b3c79396e702539db6bd5b6a9889a40479be26d2bfdbcee612d13ed879689598c6ec89d8ff130c78ac1a5f5f102deac226d43687d59514f5e8f8c5b57e313b5e8292a9b73c35c8c8ee6d9772571e8ad62222c246bb17fc8702c421e4a0260072b963d200cdd378ab48f3cdda6bb385a4e8952d3b8f4326fb406834d25ee975d5af9d0900d9e852620a505fc7c345b3b70f18aa862c110a8d8f957b23c11dd860ff2fd7ad1b7f7171497fb232e5122070a8eff5eeaf794c7848f2a52827be456c1e1285d0169aa29bd26ffc2550df9a4b5cee3184d9bc2ba30757954a370a16cf68f8915d5c84dbaef27625b8b40bd44896d2572e032b1fd20c120ebf6d64f2d01ecd6e99c056474a3c826c1ea06bfeae3527ed6546c64fb9a19b5e97d974fab6e1ee0dc00988827ad4e869b4dec128965657380f61d8e0b4c5eaa4b9e5d560819652295dd477b5ae381af7175bfc677602377ae61278e6923c84f6a671889541da13b1bd2185311934b2bf3db0558a53542fc0a1696af51b075542a6acb6f2a2b712bc0cb5fbfeb396925eae8a4dd5ab28cf434c9c0903add57461d45607700675466f146ea6822d5a766de397c5863e3ead1ee27cdb722c2163f1d907a475580a1318bd50bbbc828c2264593af8e8cd6d03ea7502b4de718d239de085a9b6b64981b6439d1625fadad6c029df905d06c20f9dc67a283155b772105797c61251d025c3665bb2fe6822179507fe370333ef29090d68f7d4474459f9a3d04de207df8554fb4c9558878924266ff23c3af64bcbfa609ba328f239374ebc5dc62db8b17595a29e2f167419de47275058da800bcacbe0b663eec0b3033987b115cbe847c7d51ea7437bd388339e28f18efee3ba71a2b754c63d4450ade51b97a668011476cecd15b216bd2ee62765ffdcaed6292ff4afb1ba5b0016b66d10e6ef755ae911dd4637365c4c7ccad5d950dc5dad877fb2203b789ff9fd6aa62fb4cfa91c9d1bbfe908c00fb6a61b65d6fb0312a5e333ce4ba9fb797017894fcbb66b9d204f0b8e295105223a272699087194b350c4a2035e1ca0eda232200890a06eaa895e77b74b9596d5fd7fb51450c1bf0f60ea712716b2b4ad95eccd702fe09d1676a3ed894fe72965f6a982a88dbb0f65468399c4268896bf8eca9055d0ded164b0126b2b0ad9fc901c0347e6e9652b07adeadb836aab6e6c55c6cdbdd09e8f84763b80f44bdc88d39a5208eabf195108fc93015f6132ecf3cc4d5ca8514db1704cd799df89e67f1d5816f8b188b26d7b7710a15fd4d5174a3dba1c126989a4305f2bc2e221cb818379235a834f49b4681af0a2a3e8d678eedad1d0be82917f2def4909ddacb13c1001ad893e761beba9a71ef2fe06b8b0243705355aa7bc77013c29888623e8475f5862b0d80cdc4b6ee2adf10000b7f0fc017c8383be2dfc9eb54c02d9c7e7549d5679e2f845e3a0c06b8bbb578e29ba71936ffdeec435a48aa92fdbdb600ef140de6779bcf60b36643ac6e3ab20c94b7acaa940dca54ead8f46b99cda4950e6b99b9f8db7c471ac07543718383f735009dab4f0726900ddebea00f35f1481987d84d9ad012b7900e146395baa71312629605c855963edee4fd69a02ab9c2f3dbc0daf101669c9307cdb6fd808863800b1209f39b97f53c1a436dc6d85d96f40940ebaae4189a4039e6e297324b5357a88280e1c533054c29c15d013ab7e44b34809a848c5698c3451068dc4b624797d6b3d01d5621c03ea9c12e44586b43381cc441db5dc7b0aa84c04a9b43d77b8d8896df1b0ac631a65beef15273fd2a85561bc9792fa16120dd03071b0b4369c4a455227b37f15ea5cf6c6983de3ed9fd2a7d07b242656d473fa9d0df156e08e5aaa4b5bb907b95566cd38ddaf02a4dea528f3d20e7e93c8a70bf283291812ab8a9e92bd6af73a082011203af74cc76d9f6e247aa0e7da07b05f1538aea64be351d1591f694ca4c556550b6ae062f37591d572aa926514e0577bf5f564aeda9278995c7bd97de9321f537111b03ffbc1203c729b1c526836e06a29bf53d56f87b426ff528306dc8fdc0ec84390b6af6fd6cf449eb26f3acdee531a1a8de36e742e23924b2157407bb2a2fa3906685a2f14e15a2d78d5ee72f5d149f992fda326e81035fccc11cd2b3e3adbf86e6bf6cfc599329cc0e57346960a456e3f8367b76d0b0ebc4527304003d823685f7f3166206c37b302c407bb1e8eb08202f7b5e6376a7b838fec3bb15491d687101f8a57d729da0fb208564d88f3395ea0cee1791852e4983e7b3dcef37764e6714b9288bb99c869d2a64331ff9bc3c6e33059279d737b58c5aafafd854df34327ccad102a717e51b6eba97b27720bd18e25e0a1eb0b3d31adbaefb8a795f6ad493346fb60984532e752293670f5004b9a090ec503d2412a56f06637257751cf3ad5aebb3430aa1da5218c56cf23f6abb49d0182b3540ab5339b35af789f4efabc811a252f6d4bebdaa04e7a17975bb90e36bc78722ef9ee4959da1e1d49b8b658c00760a0e2f7fcd043249fb72e7d182d88a49e4a14247a6d6362639ae0c9685a55db827030bcefbe131b3831e5f32972504aba099a943fd7deb36740fe1e2b27d454394b2da7e03e512d1417c517823fc007acdaef74d33be16d2dfe475653503147bca0aad8ee812da308e3c6dbd6e997183debd1e21bde1b8dcc134428fb9e5dad8051b40cdb63a757f7c3ccb19d7c7e776aa435820cdee0a59827210505667757e84a77bc129275a8da42c7a3b294f89d1d82fd798d93b4bbe9c97f39c8b97e10f25bcce0bd69cffcab4b920674c27a24ed0a6f18d6ccfa36607fb09fe89448e2484ff36df248ad222b05ffe589f67484fe25b0678ed57d2e0470a5ef3bae1db9dcd87c696a13d812ef4fa340896dd79b1106b72ccfefb8f49041a53be15b5ae20762816370c95e7a6899fcdda5ebe81241f45d1d39f7a22217834f08b36b3374dff9037fae74c156e90ff876323994b6b9352d25c70c07a5beca0d2570bcac5b79e50e596e8d82b1370e25e404ace2e22fcc48282a8b0223a45c8f8deeddc2286b4dc124a008479d1b898de0ff4ddf3b3100538748f52daf8317a6dde813a8bcd051cfb2bdb0b78b2c1f39b87f9b36504b5ae057ecc6684127450a99763087ff50112794c870250e86a0395f7ba740b8480971216349c50e7c80c9f47dc035862735e453c7cb7e140d555505dd5828191b4b36d6d2358dd82cc613e9308108386215dab5b0d12077800f7554813e06457c8b520e63e409855ef691c70cfdd916ba1738521eb909f7bb6e1f6cbc69011dc3308b0ebfc4fb0339b91e5b38c14499a3b2d820eb852961fdec4c23262477ae3b16026cec02fab813227d6ec581ae2be6c06bc850d8291660d15e97938a78049f7b7277cc0f7181ec6419a21d21d37def65fa939fd292746334ae56677b98fb09fb255cf76368bbafc3686186b039a92dec6dc359bfc897f8de657ad42a5c6287d0cfaf415b3ab75964c8032aa44853ea8d44076253d09e11c68eccaea4ef10ef04c15d6a0f714daeecec1767f85a8a72b9960f48d6fd396040c40b2a9a503dddc9c842b9945f743c99fa44556fa0638b20d77c8056df12fd5c63f60c1befa95baf51a5aa02ffca7d274062fec96fea9cdf0875ca858298190eb572ab5842dbab27f96e02e4d549a560831364920f91428cbb1affd91448ba995bdf6e62862c844e6230ab86cb5adeae5d1e86e7b09cab0ae9d8510ca49a595ad1ac511e9f43f6bfd2bf58d7bc0827cf12735d73764015ff24fc5e90d5fc82dfda7aae10fb880b5fca59409847833b648610ebdf4c7dd917079bbd1c7498fb39bc0b2773e1c94f21977122e6b37f271eae91dcb43167f306182e5e5463d4a7c949d04970f5d7e42d57e0eab2ef2624923e4d4d488273d349709a70692301c8d66624755a515ce0211911aa0e0dec8f975c0f191062879579612aca6c0f35652fba9a411e6f4d84717df24f80873b398f43a384a253457a5f40f6df3ee005272fa8c2018b39836e14f6150b6846e9c4901bc6f3d02d1eb66479aa3e90d85ee771c77534b43296195a56bdf8d6fe899da15d9b01dc3c31a24f923db4aadb69bbaf6e049acc49c4a6a7766c8ecd22a5faa73672c40e71ccaff8793cca8783c75506742f005a97d6e1610056242fbb895c83d4ccea5c167e794cbeb38e19be53e5247809ae694eb5c7030c249296f33aa9e2a83ea94d96725bc688258571826729a0c92858df9aa3a3727cbf6a13eec93f06167202cec933999cab1a9472cd87c189e31e69c466c023a2cfcdda6d171f1491c31f7ef96edc10ad83d91ef820b39b3d6f7601e3de7f23ff4a59c21d8a77c516f5ee2db5cfce11c77d7131095e6f7210b8dc1244aedda9f5951d22b11ee03f623bc6817537f98a99ce3ab26e6e804dc758c2becaeb553dc2964d56d873dd0350a04c63db01128958f0c785114ac805bedc331382e03afe70ee318c10ab8039aeb755c539476cd46e5531562b6f623034ce18d1ff57cf189271a208669534271fa100432a3441ae8b58999ac0eac80e9574e638ce3ec276bd07b674a6239906fb11929f5719b288f4eb965107fba2e271fd6dfd8bbd713864ed4e3fddccece1eba6f327cdbc8900de7bfca036eab72f9611f741fae55b312224dc27efd3df7a1c34e913924923b81601298889ac12d75ad5d9a6f19711570d7ea514a6d741fbd694a90e7205f2212950f6633008df9946970fc6eaffd594fbc4d9464e2c07ea72ca8d6e9ff93117fa1c092fe99f648cf84e968a70c3ea57d4f555e4dd21929e187e50f154e971b197fe7b3cc45650b8dc45b01bc9bae4bd6293a53319a8a9d157fb84df17b4ebde08add664793df9974c8b4faa065ad57b7009465c5cbb7ccb1c5734d0ca47542cde37c155b36813f5b325fe172bccf2917ed9c60ed8ef1b59ab92df45b2268fb742a48025db8b23961a517054ec06db379729f4ba2b9f186199e2bdec3fcd965202d3bbd7d4985fde05b8c3824dd2e5a912bb03f0cc1841788566db26776f7faff43d17111e1e7b25f433f868fc85f3e8462aec2f16c3e3c09a7971b8c7d4eed4e30301bf5049e18466c3648bef161636532647b95d8724d458c2c9bc4176243d144e4268b90f94f66a8f4d054205b32771d36b0405639f9a2f5b3663f1dbeec08eb0ea6e82e2a29db4d92dcf8f3bc12c39297d3c420970e80ed955c8258d6d13d90e172269b74301f9799934fa7e4bd3b5a4ebc693c5bd85754a589b9ee5df8afcf405fe3eae3f5660a004504ffa97fca238a2f28ca2628fa17eaaa50b6344f1b36b5ab743eb9ab9f4fd68e2ff74ba2501f879621712be21b1ab48c878c71a4108415eebe2019da434098c21402e83c38d818eaf52fd097c3deb166aa84719cf70f3a78b220183c3360b6acb1a4900976249a48965ab8b130a89909b561755562450e8b836bab0a1be3d1970d058a7e493b5b81207e8154545690822ae7eb0f83484ad4a66c28aa6a855163bedab61532bb7770873305d1c0de0d02e4cc2979f9fce839541d5567e82dc8c1cb3394bb3ee334304844cbc05b225f102a4022f84f60d85c7487d6b0a14e2cdf51be3e9cf68c75c400d5f618e9a7845d073dd633917ff5972d3f8b7c6fb1b68e286ccc356a0717ea62f5cbb99982a39fd9ed6adf381bc5e06854dff01cd85b89c1050581980d147c6dfff9ee5d18ba83e6390708290a7aa1963b7cdea5dfc61718a3d3c9a74e62459f70d5c45265699461e10e6602d0b1cd5d31c3d4244ace1775cf40e8c2f0105231b98be2470b9aa6ed5437d9223852d49eeba68d69748038aa76819361643812eee458f0a805a6802fc08a3091ff000657a39b786b533fe61c857833ded20d4514e31a2ff311e35dec0f04ba56d3aa5730da0dadc665c88a601dccdd127f02dbc3932c70b6ddbc93ebb0e918cbaaf303fc8f5e30d97b663b0f616d0f7f37819e0bfa7e31fd95f2188385ee9bc0eb5b02c19b3123eaa0dc409550e5c7fffff3cb2db764259152cac20a4e0a3d0a6769d79456882d3e0926c17b5b391fdb0a86f0fbae125bd58b5afc4bbdd44bbdd492e5dbe5a5ae42efd9555661f7eb2aab903b769555b869d7d8aab2fa5adfaf8b7ecf2ce477adb20abfeb240908f1bef69abf4bbb7e27c1a38a16e175152d427d152d42ec2a5a84d953c0107e4f014398b5bcab000937ef2eca5d0548087ebb0a109caa0009bfafc4d604841b10fcb26fdf5656af73bbbca8feb64bbb62d776b6cb97d8aa7284f0b24ed2feca28694becd258332b4dab86a5a51a4a15a3aeb4b455a39dda4943359896ba62cdac34199a49650653666999a1ca4c4596590cc9b26234580babc9ceec84ad4b486f4a5a20599c6489d36ddd4e326b5a27ba9e7ad9bceb373b78ef57dba66ca7785cdba2683f1f10f4bcebd25a6f53ffbab46dda2c25f3f5666d91f9e1d61eb2d27b26817658da1a66d2c1ec64813f55b4755790ba3596669a2c79d4c154d9790f679b17c5be4d4be51c3c6fcb7ba8d2ceee1ef70eaf98b59dd3beb5aeeb9ba997ff7ad66ea65e743e686b6a1e6513e5e226c24564c6518ea94e3ee32757753f2c6de195cc545ecbc45cfeb6cdaca56555af64feb54dd34cbd988ccc91e77f6baa56a8a567b164a77e69554ca551b6a9d119fae68ceb7d4fb67955a2f819275672f125c718b666dcc258599ad7cccad06c0591bc7333f5021a1d61ea759b22d4b6d613bda8dabaaa74c636f5298acf3656e5512ed9264e3f9f8bc742ff3c94d18c61ac9c574ccd3a22072f729b47e66569561ee58fec8efa3cba2bebb6b4f3546174c5292a6ef36a7d3ecf764ef328e787b699510ee79f153b39f79c19fba75b980a6e1e5e649b58d56a1ee5ce486c61968ad7166ea5272ba3d8f9d9e6b57238cff685e65146c97ccf36af133b8745e67fdbd4ad303c7633f5f23d4cc9fccd36b36a9b5ab5cdd40bf8cd731aaeaaa7a958ddba72199a5b1ba69eda6de97aa95b9c6d5eac6d9aa997ec66b62f55b8cdcd36759a47f9dfbe525463d9d5dba2d596ba7d16564535abb61daa6c297b795db7256d6b3ab5a56c7fa0471367183244c8103186a8a184ed4b555943684b7adb3cda97ba99f4b838e506572d232c5954b408edaab20acb8b9268261dc192492c2cb814919ebef255b408f1ed53ca08ef53bc10729b664426990f185284247c1184f3075c2899c4d0450efa3d113d824086163411832d3401073a2885b6490c5d7ceac5f3fb01f4884116a913a8b40086295c5112d9a6d04e11614aa55e3e1fb9f8916df680e9c21652b4b4b042055f943edb24b2533c44fe79e8f73932e9b3cd92cf415062c00ece730564a8ceb32472d32ee1dc8117a5cf5f7a5431040943860c598317529458f28efbbd9c7831640818462064c8909248176082d0c509848045185194426014c1092d214face006482889b7df8efbbdd82d4ce085328001031e48210a25cfedb7e37b2139b33002145b0c9181410904eadc34532f2307bd73964cfafc82b639225e5ce99018da26689ba917cf459e7a01ddf33214d9a6bd679ba09d7af1fcb34d4f679722cb0977196ec0bd55808478972ca4bdb6b9bd6d746b19102cd2fb1ba29964b140c39021438690c10b1e94ae0e5418b2061e7c810643d450ba63380104062ec001115099844417567001e6055a94010da55b81114001c51744f084d30ba5fb1b934917135380a1053394e1e347e9deb2901688194e6479d18b66d21058b0f2c8ce646dcda390268fc4d64587c0a2268f2c897729223b1814c3c5685812b6038003c3f69d620a4c42c2d26c9986dd1356050c7aafb063208145b80581939626e79c493bc2d746c48c13bad80dcb3408e84043d164281a0c4593038049770ad4c55a78b50c0b9b2285b0bcab0f6179d53ba3afb53190da02c10286d4bf59e45b2a3dd23e8f74999156bd38e8979ab42d254360b2cc8012da799e0ec022fd6bfa57bd4030c0e2a06f37b6d706d815fdbcd52352bc08cb7bb2282deb2e5f67ad75b6764584f58f942d8497d59c108b3c0d8b74845a276291c7b9b088080b762544f860a2060abe00848a22d4004545099f891e54c4cce0064fac808a278e50bab7a48dcee29632487bebc1d159dc525e2cd2de8a5c1a90244c41de8705e8f00d3014e127058cdbb2eac6690c388b5d452397ecb2b61d70d92df211a7bb3addd5e9ae4eba0141ae6dc110e4aeec7a5797c5f4750bc48ef0392c5c29dc26dcd557d68a587264ac8bced6c896d43ddf533b390eba9d6e8aef1134ad23d6f5185fd775dd5d83b0bc465e975d59969de9f6bdc28ef0b91171e3a83885e266210589f555ed0afe9561fa97861b1137ce6649dac5c292b4bdef16370b4cc253e0abd620cc2e517f3aae8b588e69ba6b2fb56d631d6c7c851de95de22aece8da25f601de2a8c521912de94b464c6ac0d4d6731ca89c98c129211312402fa24e1091f41b6d8620b1599dd5f31d975ad0473b2adeccab53b98140646db41aa699c0a6b212323232323232323232323232323232323232323232323232323232323232323232323834f28b8984673b795ba097b6130ff5e21d65aa669da77ef4c74e7be7b7ff9bcfdd2edbd69dabea51420a10bc6b46a733139d8be20ef53a4b82fdd0c6b2ac028e10762c058214509b8468d21a14864030b0f5aabf6878cc4af01440a0ac252d3dcd08ab0146133886a4b0d43a5f9ac241a752d051267cf368c654278c92e484145691457b33f2a3706867263e82028140a85e290385fcf5b7f56b4860ad40979ed8fea82b004a130152d4128127faed8a05412558701a48aa40cc1bbdfc56e7675cddd486c97ec84f0ee82758d9689284274535cbad4ab6e0adcc2ed4cb218aff00aaff00ae3165476ddda4bcca3d3564994218437a50561d9a9c218fb43beae69974621f5aaeaae0da2b12bf765b892f8582a826e48485942ca1042ee5600d797b1192c45c8f18045f82954cc84298a4469720a15293b08afba5de0a6108108f7de7b899071ce44c8171172d644c81811c27b21700a11b28c2fd652d0d91a9eb522917704d8920f266c1e95de9900afd13cf2ae43fb10d004095e376147de3b0c5f7b65ad4f7605fcb795b8295584e02e213d2b00ccda9cf3b3de781e8891d9b8d19b5d6a558691d94ff687ef1e4a1992d6db274c5fab32b7792799c9248db3772f5065e7be2cf3348cfd2123ef551a86093318008102810201d375f6c37c38698c31ec6191cddfbdc672c6b06c6dce19e3df4f6f25b25b9d05d6968168c1deb67b17089e03b7fef677ed1d1028102df83ee2fd75cfe94d6be3658510661d89cbb7e0d2f81aef05a20540a040b4c0bb7a48a7aed4ccbb64d97df34b87c75e766d97da17eef2621f0138fd09e1c3c90727323ce31ab3e4e7c30aba31c4f872294108b7ed03aaf9e0c485812b639dd1661199afca0727b00f271f9cc0ac80e916890f048ccda346d915fc141f6189b2a2d81f2c89693032e514deeb8d91ad2d745a4a8813ee5dd75d311a1cf1fd465be2706ee2729b3b6191cee546c5c168ae04a48621b52dd9abe993e3388e3b71254033e9bbde54f607ee3ad4d7a7f6d15ddf92cda4ebdcb3fd384e9ff346ef1b310cbb4ba662a35dc51a232cd7945b2fd705b7de3ba63ab01cab8b190eb512afe0aae07c204ab500b06723370646623565905c2be5c4b5487c310a896bc80c05292008afdb182bd42c5641585dcc7cb1824113466b8c9a3280408168010a839d6b08af4fa10a02b685b05994dc5b1d04b76cac2e66b61096c4c0d03425d8adbdecb66d77b49776d95f6b3f9d63f08327c891b4407c50737d386d1f502cea7126c232c3ba672fcb9ee795dc33ed3a36ea8bbb980b57ca19963348fcf2fa2512abfd0185c4d77522bfd437c508217e2092f1db31825e32f2f87c64c4df0efbf1f0e06f87fd7874f0b76364f45c1c3b1f8f4f0816126fceb84dd036454c91833611799459a2b85dd4cfa7b34d4f18daa63543a16d8a9c84249390ecbbae2424fbb65a242499846463348ff2c8e680483e37b7097ac94e228fb2c7b3cdcf4b76a7b34dcf4b3687b3cdce4b7628b44dce4b760e8542a18744b649b24d918fec23f22863149f77dd5e5ed675552f944336b46dfeb525cc9247780cc5f1a368c429c6dfda591e421ce425bb4d4845ea2e858cb004b5d22030a43e280c0dba38c5e911c212440121a403db023a619a10880456a44b9005b2c5c90217a90284b00451241308f34b1065495a759ea714253d634776d568c2961d5972082d6aec105a0cb1224b9636486b71804eb865e3052b2281cdc20ad20297360588c2a2ce72b26daddda030ec0a0d1605097d08cb6d0b0314b3b5624056e86f6390fa200ba2b97b436357f45d7005c2ab4373028162bc0c5f7b4fcdaad1842d11acd99c6384252806446961266210058ac924f0a0d0b75d8ac740a0d1f3d0d879c719432dd0558bec2e4bea7b6032099ce9d064523633c362ad562588caa185224c06d3a1f1acec0f770838051669f05fe7e13b9d7b88303db44d9c9de2c1e1809e771efe7b97799b0fec34efdbdc05833acd038d9e7f9ebbe050e72e98d361deb7e3e5dbe6363bdbdc989b83db088f679b2307f700c26de6b888b34d318ff4f76d5333bf5310a56d8eec83d7ef74b6796b3a341d9a3b73577735f2effb2eeac68038a0ce364b50a4be0726c3e413689bb8a686d4e770b6398212bfc39026173908246eb3f391872e148ad457e8e241db045d44640666c6f73d14d278489f649b1a95475a6f0fca0cf120d07fdcd2cc8ad4278e89fec916c5ffb825d036c51ad0456236c0d1ff4e26274d14e5c03025253926a3c67fae9579c5986c33dc66068168481d9347103839390ae566ea8504f519f7c4d0d89dd08ad4df1b9ff2e8aeeecca5b935f8945779a4572378cd90fa9f6d5e9d6d7e9b28db0cb779b24d8c0a7336d99d6b929d7a2139e79e6d6233b609c256ab3cd217f7a764e7180c836bf229c364548ed9fcdb174d1e6950eb9919135b6dd3d4e136f30cc9f61c86d41fd9d74c5e5d3017ea8ab956d7cc26fcb7c16d8a33e03bdb14d9270c268ff43f5bd35c345a6badbff00bff6dac268f740da9c1cec63c5bd790fa8b21f5a767488dd190fa0b7776c26abe189a50c370603a2fb31388ba309b997b5a853531341c7dfa5e5a1af025466d5ee699f0e55543ea123b91faa0532675f6d6ca237d1d6aa1186e2e82bb149d888d268ff453ce10da63bbb5b69af3c4a1a4ee3ed022b71434842588c653a37286307b09a22135f6ec8e485c94b4a5a7666b7d3ab0bdd16c6160110d166d61609106d1a8a06109a2361a521f54e1961244034e815b4a243c1283287d51b20455a47ed9a101cb10da97208ad42195867380c29dc9239a99d68c68fd8cc8e2e1368835c219a71c3866067567ee0c0f78846fe211fe17a267a1c5605b464c4c69cbb065c47443d440de994cb2252d143f430ea1457a1c9349436811051ed995487d8ca80a51f934851684d00a518b2cf0c892e587d0220816591297b68cee0bd20297080358b4ed173317c7e0530ca6c134380cdca29181c4c727dc628168c1d2f2860005c2d2b692c0c006704bc6f7b63a34d8512548889fb3f5ec67770eaeb014af1d2d99ed0c2cba76632c5e58f4a96ca1454adeef62c92108f5a73fbdc1e986db709b96bdcb5d87ad180dd6c26ab2333b65680693a5192a53653199d4f9bdf085b177edf676c942da702c59586e5e416f0372faf334a75f62672685324924c3e434a3b22ac76435af322bcfac2b77cd61a7a6656d85f27bf26b72ceefc82fc9aff83be3f718cda492df63984cea8ca0cf28191979a7e4f39901daa692921917676c53c94ef12839c936495e32d221e98c907448ee82457104bd33a353d2e974f67fdc92f81fb734e324334a0e2a21f9876464441bd311f493f1f3ce64f48442e78c9f7b4c40dbf401fdf3d48bc94f4cb6096472d0532f274739d926939de26172916d8adc2404fae79e73443821118e0b4601a18ca0734e3e9ccf5db08987e3e170b68f531025ce7fdc92288feec89247395c0640441edd8ba31179741f1a452c48fb849d7b461fa7204a9d2dfeb825ce26514becd4e9dd6556b1e0dd0ad72cc436f8a0e3fc0b37dfc0eb7b9d2401dd9ada9677550ea7a55df10524e45ce1e6b6b6efcefa2a7a7b7b737b9717cdaa6c9776db7c5d65096126d12a4cb83676663474f8b6460bc360149ff0d9aa69a934eb0c4bbdabab5ef4c65cd545ddf4a2f8887baa69d198b01996dd2cd72e737422ed62687ab64dd8d67249115a6f8bf7b64f4398ae26ccaa7c96397e9e5844a6d4842b2b9344a25f4c539cee12a7f85715cd9cf5bdb4ab4f2d73785dc77120e7a6d4cbe6e0b7196ed36683da5932c904ee8f05db99996eef8d338ce10be7d5c31b18b8cbf2b8fd1bb1eb794cbd6ccf524498b66dca76d67d7a9bfad9beb66bc79e77e86fc7f6bbe06fecf2b7e3e381dd05e7ac6d539bfad73e228ff0b76d6e7e6d22f208bbe411b62cd9369f6fe64dc3ae6d6eae6bda36af4abbe8796e9e73cece4fdb26c6d9b66d6214a36787b36d6e56307bee75a0f64dc6e0879f7a01df6db3bbe7a9d44ece28d6ca23fc6d73fb42bcb76d82de75ff714b2a8c861b04ffe396ba6d82989932352bcbdbc4589ef02c56bdac3cc29c7da9701a3a7782b6e9c1fee396c06dea16e6c1e7e7b341375791ed1bd9da2f9a5e1556f30887b6995153649b206c9b9f6d5e2d6cd59eada52696eed44bf8ecdb3673679bdd79d7cd7136565dd689519c629556a9b86d5e98655e597653a7e6b5ee6ceb36389f194d35f6bc332b8f30b84dece6b5993e4e4194b4ffb8a50c6fbde6113eb7b39a472a7bd7ac9238e79b4b984ad8f3be547984b779a5dbfc714bdabed48c33be5212678de691969d19d6c23096c652bd662ba6622876ea96f652b354ecf9b6a42fcaa36cbf856d318f2ed63bcca37be51dea71f1c5c0eb764b24692669a85025c69c2b549ac2c0a0e877d329933e709bba8d4f25456cf8cc24d3b6313e71a6afa778212c2fcbaaae7645bdcb5f9895434d0c2f8a5ee620f196238ff053ce10965ac7027ef7de79df3681dfc1a75ebc739cb74ddff6fe3df5c27d73f098db266fa77878e7fe719c777c8d3bd6f2087bdef781e0b675ddbb6d9a66ea65f3ee17268b447984d10d086ef3a76f9be23dbda75eba6ff7b6199a1bd8a9976b49e411fe76f153c410e65db2e858a9e4fd9a61cf0425952caf2e5da931aa2ffd505fdafa603ef43b7d67aba6a5d2ac332c75a57a688cca4379a987e61186213d74cd245376f2ce1ad2947ad18e1d6b91e6b64d5ab6bbb5d4d190b8eb6632c93475337984cf62ad56aa8a801e25d3948ac9a46d9bd86da947099bb0dda9f2081f77aaef636d1feb53651d4460541ee1ab5c41c2baaa46b2681885af35155f9746aa2021cc5b250949d8b42eece942676be8aba82a6a68b705c28518d2236fbae633ab30ebdaa505e2854a5e576922d4bbb4409a7092d746c2ae6412582ba58c30efd202d9c20c89040ba9c4b54297f5dd9a9680f05cafdfd5ea175baf7179cdeb5d61e7b6755aeaf4ba322dcd542dc576c9a2f7c9924f9d5e3507dbe67918e79cb1693edf9b297c333441cf33c6d9097adbb3aebbe7c5712dcfcac9d785811ab6852cf77edfe57e65957afdb3b1dbce5d3e37c52787b386e7384c7561e877bc4df37ae77ef2558137034dec79defd70be16c6ddd42cbc4d8cb5453611e1592e2b8f2e68679c6ef7ec687deef4785a9f9b77d5b42476f685ce8bdef4aab09a475764dfeca3b7e9d9e6d5d25f4dadda26b6d9a997cdb16b97e3cefbed0dab2eebc4284e2f956adb66f62e8765773533f05e8c2f7d7cbffc2cc3b8fbedde8b2fbef8e28b35d6c7f7febacfdbbc54d69a2fbe58df7b2f8c626b86ddfb8b2fbe589447f66e6d6fb6376f8bb726f6b818450df95a976fb17fef6e533cb067ff6eb36d7a8e85acb51ecff7755dced77561395b6c132691b5358ff0b5b7313faab64d1116d3ccce772eaa3d70d37ddfdd36da696aad2be34c53cd9387444e3095a7e426d703bf4dc76d5a865d79c63111a515725a7a164b7666aaeb649b1a2df97555255808672be7e6117ac6ce25a1d031124c442467e877595adc9ce7d10d793cc744cc8e87a9eae709c3639dd0d30731679ba1da692a16b7e69686a91976b14c4c839fd76dd19a9f475816f328ab76984757bab53cc27429df1a5b8ca8542ad5ed70c20d1812017d3cf6f33a2ecf2821191143ac102b2402fa78c4f3def3bce279cf10eb9bb1bd92dd916c6e646fe2bee2598ea0774609c9482922afd6c25ac84a93c0c1d2d66b2cadb268ea458475c1aca66a69e933168bc559b61ea1570c63683e31562baf197a595a9f77d558b7a59df8cc5418c5569c62e8d5ca2876e616a66255ab2aaccaf075ead69555e9a5d3eb42edc5b29a5e2f2bd358c5b49a555aabae4ba7973aa384445fb6c30937e05556a1888a4322b7a08f4754a5a1545c6794908c8858cb307d89e7c808fa8da0de08da8da0dcde46d050ea4143bbd44476197a7688e541ed2ec37d4d0b435114cf4c0a695a188ae22f8658f6652815514554115544bdb6a4ed520bb12deaeec9e424c57e03994443f4141aa2db4360e528391c91c3ed1179646fbf1d2b22514a8a89c9c9c95db0799292d8e4194a6e270a4745e29f8c42755b395b8a72548e6a6b6d2b0bc7caa9a19a47f89b6a4b451c164715dd38b7d61ab2589cdaba6173e2e3d89b752587b36c2a1c2b87255ab90d37b6c99d2a3646adf5c6452bc7dae1d9bad9a9ba74dfdca03edcc43c49fc1bf6e63c2293cc1ccf616fac4dc5a187e768919c95f41e4e4a39d406e70c5bdc1a9e29f8e3363bd5dea4243e8d6d6ed07be709be49cb5154125f8795c4f761832987c3e16c1c931cdbe4f4b04db3879b6c4e954798b44d2ecd2187f7c0e1f0f01ccec3c9b5c234443b75b3f1520f05555075c30ec7f11d74d8a6b7a26cf3535b74f80dac8d756a6d6ba86ead9075ae688b4b39670fdbe4a039e47094dda1798479d86677aeac3c875015a62c5f394bb776accdc6537929c802551c385c741c4e127f876d7aad8fa5c30dc7f1ad9cea9e1c9453372bc7dab4b8353cb95688766798e6b0cd50b5b27294ddb5f208b36cb35b6fdcf8ca6663c36fdc064ff55479a4721be00ab244a3d7380edbfcce6fbd01c7455f0bad09555dba393bd506edd44ddab136aa6edda82bdbdcb06edc38caf65879843d16896fc3363d55e5376ef3c8c636c1b5468dabd8788d2f25f147dbfc44dbfc706c33f522fae867abb5619d2fc1147d09aabcf425a87aaa9720cb535f82eb8d6d82ad6d9a28fb537d2a12bfc63647db146dd0c6fe528ff5126c796bcabe27d56b8dac736f5bb3a234caaf456294975f7a5bba054f12a3a86c10fd5427bbb39aec234094c423a8a68599888d5c1e5445497c8f9a49e2299a394b5c9d670e4d0b4351bcc8655756cda16961288a2a55a54578d70b5a2f68bda0f582d6bb7a500f9a4927c728c7f7a49954e3f81e5426a51cdfa3caa4f1f89e984c32397e777272131aff532ffbe3de261aa997f129e336ed31e53552b669dc291ee351b689f2f1649bbcbb283cee826b8c2e3865e4e182c70e7b26e3ef82c7f14f1969bcc6b86f3f1e261b8ab8e611de1f0def1fb7ca2ab42a2dc3ec9a49a052f356d0caaddb2ace882c7125aa628ca8125597c63251a5ada28ac417559924828aa0a24a041555a24a548921d6298aa2288aa2288aa2289e99148ae229a6a8212c43ac9a4c3aa17193e3875a994483c6fec9f1433499b451be9f7af9c7d3387e68cda4f1db84b2533c50f0433399f46d8e4741c10fb1320905013b28a1fc7e2fe3edb7e37e2ffbf6db71bf171ab7df8efbbd9cdc7e3b4cee8243ac4c42d93f6e09e526ff8f5b129d7c9fc6c936f98f5b3ad9e1f6710aa284b2433f6ec964db3cd242ac509a49202d147f855212bf2b412a11b464114133a9a3ca23aca9e14a04154145d07bd0aaa9e14a647d05a9342d0c45f157911f16e8d4b43014c5ffbc2f3f2c123f250d617ef9b916f5a01ed4837ad0ce793b67d7e974d24c7ada81c9a4c374d04cba09ca7f72939b744e9974946d3ad91d935d528489c964773a270b597a5012bf73669267456a33a5b892d8437392f8bef4a0236a09be22d8a2b335ca8b66fabac9c5f1e4a1f1078d33be194dda4bfe194d22e8a6fccf3d23a18bdb741df4ce68da3cf582e987ce194d9bcd3bdb845d7c389a3cdf6c53f6917725e3e623637871e48046cf8c77e3ef8d27ff46936ba369f392e7d1e46d377d9e7f8d266d6b1d93c7a43df5c2e9bc3b369ab4adb78973efd968baae6d53c9a8fd1bb37bfadbb875dea76d33fbb64b8cd0bec91e660fb173b073f43bfa9deb9eeb9efc4ffec7884ce2b62985805b4abd84dedd24f29778ffb6293480ede4e2bde3bb6c2009e8db4fb66924074802fac602faf6cf08f2843a2267649b9b6d8607ed12fc19b129227c76fcbd84457c2f9c63b75f119d974e119e17cfafdbaf88cffd5e3ecfdb34f29489fb4b9e1ad9a6eea11304dcd2c95325dbe45d2479c9533cb6837eb24ddf474cb6097cc92e79d90e9ab14ddb41c75b11e0497ebf17f024b75f11df477ebf97af08efe2eff7e25dbcfd8ae8de3d74fb15c15d84bb08e8f81d043289e4f84964d2c8f1379049e2f16f08f4cf78baa5cf45c6d3f511fac9c5d18778938f8c3e463ee324a30f9297bc3b0ae71d4fe7d3f170b6d9f93637df26ca36370fcff9bcbb9ec7f379a7f31f41943ce7703a0f43ce379bff08a214de7e3a36220f85421745f1232323272139084402ba0b66e9726412787cfc01642332893b3e11ddf63c9e6e299f1b4fd747f7937ba30fef26ff461fdf671c1c7d802f79775df95a5fc7302dfb3594ecd8afb1ff08a2a47f8d3e7e0451cadbd4dbbcb06da65ef4b56dfeda51b6f9ecdac9f8238812f76e3cdd527793f14710a5eede78ba25ef33c61f4194bc7fe3e996be978c3f82287d07c7d32d81df461fa75bdafe2388127897fcf9357aaec7ceb191f36c0cafa9a82164211947c490c838e6701900114674d8b318100313b8700516583c5842291bb92254d1c5ca45164d8ca1848d3d4c40862684210b30a010062f4a7ab45d20230b2c78c1136510815abac61e30b042088b0a282eec2055ca232708146548a10552306107a5fbe1972c430449f3c8a28204091224e79cb32d056191f7273d2e0e4dd1d91a222425236248e421d1f3d9a6d007c42374926d821e12f9dcd3ddcf7e3c0299644b218961dd8383bb13749a0fec3e52f73f44a525ee90f720cb7bfce318beb7079db53fe8cc6117be0ae87ed25d6288b1245d88493e7ca47eb09496b8434aa9ebc74ba90ba5f5817b17706d885b2cf981f6076dc4c0057a9018502223259087f4b8f701262b30c14299608215c028e1334136e1072a08427306335ca17285d4638531c095159e20edad08ca5b190e4bca760176c87e0d6133db846c6381c4a201a85811de97365b8c45677b0859b871eb7e61d2f7cefbbd20082fd99d1bb9e7ece10d7f4264bf7e75b9eb3a0edb462532727bdeb84b6edcf5f1fdc74bdf4e5dbf3a5848ecb70a657c517371f6dbe8fd1ab96363475edffdd5b0d17e19e9919b00b41fb89dffc12d5db7cb9bad15952662d861ec062989575223714b07d3742f6d18df4bdbf27211545b43761e96a186b457901608192cd28620bcf713a2736fdcfe3dc7e4226c7adbb64d8b811da7db95420196d87aa1b04b0a4cb7b4ccd3ae14b760331714b605a3c285c22d18eb92c2b6604fd074cb1b2f46056cc63be725a6e2be634f607ddff6d2d6dc15b75cbbd4f4a8b9e4ebde78af8d971b73103a3d20f1394d683520310d56c98b01db82fdf35811dece00db12420009dbd27d7b12b6e5fbf623ec0f96dcf64aba60dca2a1b0c7b668525812ac61bcebe82ca09f38d915ac5780352ac5e6f81a0ab7582bda461096b6868cf9442cb22d2556844f02ae58645b302d5811fe669799dc5002e832ee3eddc225b6458b61c11a6cc52d59d5826dc9585811fe85428de0bbf1aa76a5bb16a3bb57a87705eef21e6157ba672c6c8bb5dd3103aca87bb72f14e9eddb5d1f025874bba09b287115a5a521cb2b065b615b1be77e4850498dc4aa8691a82c115ef26aa4c52e4a7a3e44f2d3915daa2676a8328479971e89b5d8eef5487c2fbc5d4cc5f5212c4f8a5ea3bc4a90f0c2e01618f209dca28de18b16999fef80fc83dd38eb080352ba109627b3cd346c8b036c58d27f816080ddf68706d895fc1c844822f34b1c8eb82a23a85811961e5984105c49acce2861ad54131313bd7a1adc46c2118ff3dc9d3644fb2836a8a4d03831a971db9d5f7ae3861c58701871acf4d0421ae1c0f1d10ed7e1a9171e4ee2619b76e0e13b3cf5427a0b699b78209d87a75e5ade43cb3691768a07e92bdb5c3909070fdfe13a7c345ac171fbede861e4e12eb865dce12e98d4616f34ea401a7578cbb8c37b1879b8fd788c6ae018577848a1f11d68e8309abccb615c39cb88e31d0ee3880aac29a8c82e7b376e7cc4f1d1532f2bc761659b70a45e74388b0edbb4a2c3579e7ad9e139ecb04d3aec140f1d7ec3366fb80e37568ee3a3df30dee8b437de70179c43a7bd1cc6958f2b77c12cdde58d38ee8271e8b2378efee3966cf88df186957d1ce3fe6844e97c046146300551f0049e368c200a548131a00aae4c469005cee4d14ab26959aaa924e039f2d253b7977a558930b9385a32076e21418265cc718639be9b8c4bec38cfb364f20e9c995161a9ac54d49394abfce4db4cd9261a1b8c2929c274b2c1fc03e5b7d481604a9a02934203a4f194141a2008822797968c9cee4419535a87ea545d4ca776ab8ed5cd9c2714a62319392ec5c26ef54e1eeac178a987f2545e4cb776345dababf1ce3cdad40ff4bcaeab7676a7e719533d75f3a5dea7fcd22fed9e5246b8a9206d15d14e9caf2b63aba1f2088f9a4a8bd1546da5b1b419edd44e1aaac168691e6967f901a1c24a7e69e8394bc13393681c1f3c65d23e3e8866128ee383309974e3f8609a49a3e383285005c66492caf141155c6592e8f8203e38d3d518c78bc6bfb331a2a4fcc4c62fb26143f41aa26db2b1533c6c5c659b2ab79172f22e7b2a2729dde59da8dc05d7b8bc1ae3f813910dec9dd4187fdc92e82a221b1f6da8fc2a29291d8e71fc8df1ef46230a905619e4ed30cfd2a0f1a33cf5327e346ed3f738fa8dd1368da38f4fbddc388e1bdb34da291ea3ef6dee8f688cff516e329a8c343a0c47877938c6f1e35df08df1dbd169cf641c5d3bf0f76292adc68c35666336663463d61a331ae34e47d4b3984ccda32c054f9a30a58c103c559a08b197e0099e07b057905a8776f1f766ef4186b2f164213fa367544b164ed8795fca02b26c6a69c76d97ea9a49437ca1669426afd78cfe82f505ccaa9228558e2155bb72318d96c5afb684d73c3a43e892493a34a19ee7f9821d0829f5a86206e71226a0023d9928d928a85c7005157e94a10c4d28f520410aa410c50c68d630842d6c186780a20728094e334b9438647c410c2a7c21022594005c4b7a946023ce299c35666827c57b370d630d5b8c41adbb5bc35e6bad318db188549808efb14f0776151f612884252fa003042df8cdbae02e5b1d9bce5abbbdebba9b6652e82a67086f6a45a1e6f9fd74748e8d970c8d620e1f8ade22b99799155af28bb50b9164af88dfec13ce0d3e909714b9fd94d0715d00216ee1baee9964811d5919dc600821a9b01b49aab0853090a186b01bb7669de799e282868b161735424e21a70b31410ac93d5c11f1547c602775f2b118380d79094bca681e61326416d851cecf68664156214158e215b9c41d52326fe9c44a7795ae385cb0b898e162ed641825d89195d1bc08450feb06ac144b0846b0234b7e3a3890973f9c6251be8a08c2f0227855c14198038ccdbe5f60d1964198490b848b2cca9b5e1659e6346b81cc190ce4293fc1047694732af23263d1799999f07999b720f3cb2c84cccfea05023f7171c8cf390958d3c2501c71103cca0f8d168f3ccfab6d6531c8bb55617957384888bb20b3092c09936147b895474f5c2030108b432be32fc8d825c602dec2aee01990ab349358ec083f87a6a67615b3224b1692e5045c04c18e2c1745c848eedb8654c14188f1406ab2c480c68eeddff44221450884f4da00bbd2f33c6dc05a824502960a5831006208161416076d77116612f139f47c1271b47814ee324f41deb403f706f9de205741e66796902d0809831032d2f0845004ad296608f52e3dcf05872733c785a33724b4e4c99165146151be1199746b6047f9f99b9d6740e2edb7b02471df28f028b4f113c8acf2232c735a82d00376094bca51e051fe258125e155ae81c5211fcbd8d14a264361d140e677208feb2ea4074286200409e1a7834be14288bd88c2096479d38c2c735a034b1a828b15d8917d4207515c1f90998b294638605b4a8ae84aa177bc88c209248917513881ccdf80fde1e2991484658e017000a081070b805bbc1ff447c8320769bd20f33baf13bd25ecc87241028d0b15841cb21b39239e1755d8421844329e06951bc86ebce1c22d97ccd0f33cab78228a29c2208856d82c4441587e1842a60bbb62c932870f273ff974a0e0305a76b560d4601c06d9c260681c175e4a7c80e43a8e86c478c5221c9ee7799638999393397132282703634596cc3e1ddc0c4cca2a2708cb1ae45571222c4504905c3c37961ac8f93362e421f396482e1e7f4588e249f6492b9d5869647f46e0877e3f23c48d6786a0402695c843860c5903a9812e0c859b735d776ed3a1bde3c68c2360ed4ab7cbed206754c2243bb2d3c23107a1779b31024a58bbb23d07a797c41c883ba3f22877638e22abf228474089951c05b8736a33ce76cefbd65dab21353390562b03a991a16da33606693fbd8d9a18a4fd3acb8da5766eb400770c645207b795c80cc06d09e86be4c68dfadd580a6149253292bb0632b96d25b45d2a71870c195243625b898cc476a9814c5ed73a9feba506b184ecd8069b817d3aae217e62f7f76edca2f32e43ad35867db28a133fc9eb799e3328951e69b110265c2142ae90285c2155b0234b96223aee09e982bbcdbdf7a3e9ae183a51f4425c828b664e3c51c545d7606bb42c6c90025b63859435a4ac01a799094ec800a7bf38631ec68830ddc3f4365d3bc5e3bafea6b719feea74c230a617a8540db47741eb14aa1901040000018315002030200c08476391388c2331b5f614000f80a44a5c4a1c08245a94a328c590420a1110010000010000032020006d30b363fc7b9d2c6c7822050812938d38c5ed9f91dce1a463069a9e112fa6ea46ccd728ff00286e612dc6f7e9219b3015ec9f1410611c5191c53e37642d8e45540cffd83fdac6db80e087feb4a82c92fa96c7a846eb995f14919330267930ac438f368f5a22ebc01a884c5d7595f738921d1d955c01c1b690e2eafc6eafc00c352117297a5e8011faedaa3c6c5dff15d7cc9a94133bb1aad2244141409a6fde1fe013cffee57e2cc55b779617f4ee82a71690ae10b69af41ff686bdd885cb6faf9b138d964eb4fd001893e832a4ae854573e288a26a28420da451579e12a2e5b79a2b2fbd7043f915ecd82497a1c4942bcb78b8ba88ec347b56111506610edc27e607e6f850889a245bca37bc3e8d0a93d92406acc43d05cf2e1237ff269d89dc4c810a062e5db3e653c8df62a817f52a2b9a1876b8f615fb3ae4539bc29fda95a09b7800bf7188e2a8c002bec4dd589550ee4b3b71a2bd81ed725046ebd2c3f06058f707814e2ed0d02ea00c8b9559ee74a1bbb5b6cd0027fd62ec722ea8fa149509c53da33bc8dc3cd2f69723b21f6a1a3e66a767636939fd993e960378912dbabeac71d6559535ae3e4ff721bed5ca2095b342b4343f278b513b5dc9697fcfdb39a82a67d9dadaf06f30a7170adccc46d6ee68751b1d87c48fe378b67073c800d7c0d28f85811a66a02b840a08b832f85aa59c21c398a04de28566a5a00865a23bc8cc0e024f312c97c4e6ba9d5a360c8e21ad92af97fe0bbb74b6f5819297431d35909f7c8508257dffeb01d122b9940512d291798c8bf7c52abd559a93d3dc25c5ce9c3a0abe67e8c5cb486ba08c29f9500bc6143fc34aa6b1c7007471762148867d2ad801cf2a47a32b0178c2234675df411e0c4bdbb86978f6fbb237b1f3dbe2eb3b303e649f2176d62d2e7553bbeffa9a89c05c00d5eaa14b3b3a24e6bcd16db654e5bbc469d28aeb122f4d40f6676b30640730ed003f03070e3fb0ea491f23141636fc59eba2d193ef4963415f1153f0c2f10584dd3d7e01176510f338c39db9b47f7946836a59f39bb96c9d34726308b9736e94569a703b6bb046c99f5f60f33eeaf9b129f985dfad2130a43eba40e3d64bbe60ead7e82168848abfaf823234b4452a0a599a1b76b71cd774a7a51c7e643e100bae55252afe0547b0a2f61a42523c8f68b88262d440a79106d91d18487591e68a3b9146232a1e5d255378d791bc1b9a9f04481803a40bd6efe863bdd2adac281d125908430ba9016a7930f382df7b6f7785ab89e9f0994d7e9e8733c3896bf3e40d49b18a7bed63f416a1ec67b34b588b94262a7ab6fcc4d4753bdda4ef54d95e8bcb381a267536178c2d92c8d865ebdd32b5b2866861d058892f0f96c563b3c22d4d5b5c04f70764f68597045dc6f7c22041527405082cfec12cb3fd6deaa00ea070b0378870d24ccb4d3540dd1e1fae57a004ecc53a33c1208d818dacc9cb2585d37aa95f1a429bc9d7f74331c50bad5f6b0f86b0dae28d7d8889fd61614abcfa9a4cb3146883ad17ec4e0bddec1d9c82e7d84941e580f0ec2f97c8aa35fbe4a93b52b9a72ee217c18700d01e7814a3f0116d822d329125366a716e8a481a7b808d9a0a44fa7cf01041d27211188c6d022e19041cf4100325359903036a5b26276f19f1c9176edb157c9a197935113912a2ec5943822771472fb2e22d67b528925b8ccb33d97dcf0e15a4bda5928421adf7458d1d5dcdc6ae183f8f8ed209e2b4d48245224f56676146609f60d4cc2928aef8e51825e724fa7cb330fdb2e782398ab12fc01a85446c8144c75fc82c8ee366a3d89776689ee41e0e4df8d41c418628726b0ba7df847142454fc75a3486107953b60671e08106b4a34c9221fde171e4fad98b0130ced514c6cd85d29c8dbe1717f954d99d50bff23859609c189815530bee667e182e46586a1caadaf2f8e1e208e6fa49f2bc692de3a451c488397c03db1afc628c9ad37635ca28e9c344f3b12161e73e02ca7f6522928f34060994ea5caa7d714d63edfd0e2245c1d08b4be532bc28f3e0cf953f40e25245fc8ff1264af8fca5fe20fe22f03e739056c03c593e78538752c993a86f30b46fdec10ed2b69e7c8b8df5e8d50be5cef77af93e09d69ff6452c94da4a2eae795068bee37d5c8476b4244a97ff4e2a1dfca5f0c81b89e348b204af476235c6be6b973d9490c8457cb32cfc1bb80b31d39c5fc024f1f102e281c5d3b405d8552b1e8db6b6f8844d1a82402635c06021db334cb9275aa3715173d466e7daa310c785ded5cb5db37e6c1dfe2f3fd8503d247d189ed9a3b5799bce8eebb0db6d4ebfe4b1fca5b754b4102b29504df7a4115edda315fc440c3791675705606f800f8c99782b637377853c8b2aa65bd210ec1e55df39c1dc88ffa3b3d391c9e5aa94deda154e0d188fb1e2194497d636fd628174a70fef9671cd31759bc2183652063e00231ac529455736ce35115e7064a53e325dedde048c188597aba92f784170f19633293e34899bf8a82db9c813d757b977721261bdd2084916777f77ae68c0ddd660b6047fd1fd858efcc1cf0eb7316256ee7c3bccc2e8786a15fd212dc1d7bd4a8cf51d46626ff5cd6bddf3540f4dd22d75b5d8c83e21b9e7cbf5fa0468348723282749844a0444a801e01f7750c669ee28b4223db8030f985ad43c0f78f9992c30a31c9bdd17d8201d8c9c74f0f7fe7b2cbd84938cd596c914c129e536e2923bf3d3529e1c0d774c4659c37a0a9a2c626efbecefe53fa42a724d53803a7e45a44aecaed14d998ad1c2391aef40cb665176049e55e50f6e58dd893b2df3e559242b5c6d932d813191c445435ce99988b67bb699239f4c62934959588129460e91c93c1bf2165566cc7cab7ad286d06b343a23525424b2a2dba8baf8e290e205e9dbc385dcafc4201a27f315372b0840a53d243c0efc3bd35346a80ef119e00e99ef339a93361f8c1cff39182367a5e198e3b709dc580c13f941c44de8d88136c8917afe4a9de1c679ad67aef7d9bb49218a5b64ada5850b125235b384d95aba7dda14624f10689c96bfc4dd173bd92b45c4e84280df0b5adf267b4103322a377537dbfc91d90b0b16bda920c21924f8dc6706c52d412a79f228d22057d544159cd6fa09658b166a2640757fccb9d34ae9332b07215d62ecfa73d28bc41dbb039d586668803db66a61544f599f3e47db0bd2ff6be222f27e0e25e27851136e3cfa22ed7b66f4dae8673d4c6fa557a6fee3ae2be55225a84a58b6695cd715bdd8b78875829e05c819de640b4a1822e76ee2ec5600287aed7151142eeda2a372fc7392ee734e5218af0058f6cb613f38a4acd64c88b54fc4e19af49a0312ca28138b46fde705825628c42e32e38ccdd8c6555c3740b105c74ee8636d0dc72e3500e224bd9e14aa8dc5d1c9a042695a623d6bfe1ca61f3a4ae532ddcf0e27acceb704189b0abdbec40dd387214b69835a69c4576c348b8bab30f4561a47aaabb9120ea8ba162dbbec71142ae5c3d42f303580b6d2000234e6fac7caf806b8faf13f7a883bc4de63a81a24796902117e2ac8cc5023734a9527ef10bc3a53a7fb434639cd73a3d8bfc403dd193b940498f0110131ff99a1c3791766cdd9f2871220131a416b1d52fcfddfb03fb0d74bf0005c2c8af5ff8ba028387227cdfbb1c93dbdf5abf13f2b83ceda5195f46166c64e2b02736da581ceb0a814607a5263632a53263e48fdec869649bfd4009285d239dd744c8cd9ed1bd213c77b97cf3124f6e40484f0869e37a51bd08ff4c7d1b5dbc6f25ca46d631a0e3da8ee6c3948e3f55ed352b9ef6574993eb0e91b028352c9fc922447f48700375ba7eadb228f945bc36bccc7c80d4a21a6ec1864df50653f550bb297b8f53fcadea5702461c5f3390c162de1f9491855d7302807673ec6aee92affc2be69897b73d5565d535c2b65a97ba531330796375c8873c087b83ff664e5e47b6c5c2425b185a5646e5356d3ccd5643f08eb848df33863e6bd3b3ecf60ef203f598d046be67cf0ff65580bfde869bec960f56eaba9ea0698f253a0711342bbd81fef597f294c5d253d124faf2386464f26e166c99c88ebc963cbb59920dcde418fc216fb5a32282a8c8e6b02df39b1ee47dd271d7f29ec14e005e6868201bf8c3cf7984b344713cdd024f0ddab82ff02c804453f28711c0ba877bdad799dfa579ebc960126a3a551da9c42ed41a8cf18b0edcefa50080be6b8ed33dd2860b11d2df14371465a91d0a5f2acc967be87d2c474882ec00df888623d7ccec939e160d23083f8d4e6bb4d436b888cca7c82bb16a2a111a0e34d4e2219893cd1afd15e16b5a02b735a78969a8e522afe42a10b55d942ce0a4c1b618b95e8d3219c0b10a0ac0c0bdf084c1bf630782e89e450262033b7706a0087f17c518cd6eeae2e2c62b8b442bfffd1e9f1e6d836b139898eb11621eeafe5cbfcd86523910ce8ae3bd076fae35ced3e85a0e6077a51bd2caff33e6fa2bca3869a739566b34b02e263c00ff4ede011e12996dfb044688a0143728ce5466aa0e8d8f6338ecdba083332981f38674a93f29677ea74b0bcd29b3b6ca62d70b862a6c95988c1fd7d92bf71cc52a17bb90f152b254b9c2269da72bc5159792f6c10e65e6a9f50c72f93fcf4c0dff652846eb82f95ce6f446d7fb9e636f32b9d96c1f24cc349068dbbdc64fe944abc112565af115c87f91a6e755227e6c7ebc40fb5b55435ad2c5cfd788dc80daaecd71938907914649b316d7b966bb0a8983ed7da77c70243b0a6d403504a9df5def245a9548590a78495253e64dbada71e94a8900a29ccc9e35e39b92bd5680f70647902b02430961ae8242641f36bd2f3a6efe26a4a35f14e833e31cf151c9821432530a28767c7a3a4001e0f80c94dcda4fe12218ca08249e0ffa9411f1405b5223debfd04d8cfa417f8e6e72b7078266bea1f8db1b1d8080eb29c97c1674de8d0f3f14f786222d9367467072400479777f5a7ad3815a5630669a0c8f50822548fac31ded0e22a0e821932f615516be85b04459235796cd7c90f80b401693202a1f85ad31aa0adc64dcb155074a6d53c87b0f01e26a9b61e090b176175c2fc280ec023ad3c08fef25f624288b920585234ef2f48985a45becaafe29a26d43502110e6a27125ee0189eaa60b36313a1d5ecb6a9b7a03747cb96a52774201eae9b957978100805012f5fb1290969747dbd680942dd5893f474ec12efffb18663c20c81fd33c3bfda9efb70201bc3062595fc946351cb79a38424238782418939912790a4aad59b7166c40d22e0a3ce86d6933189648f2ca1f3a15b989fe601f894cbe80556dc99b5b3e52c064d350469da6ac994c949f07c13f5374b044657103b290b8140fb24000f4e297ed7d2a71020a167e74855c83f4b3766aaf7606f33a703607c710a0466343b3a0522d83215d027c6dc3440d4918348f9fe089ad712ee23f8776dc97cc4ca684f016c79b8b4570f739d4ac8d595a37d953b4ac2adcd0a526389993096e275a90776d71453c0d7070fb545a72f9b9bbd02f76a9d733097b227eef9a9a9aebfa2053d550735a7eacd16932516f40463d01e10c78430ae7d6dd49775ca66ac1bbc723204756aae4117f73fbcf3c956b2fd70b5ff249e925cd8f4c6a3c6aee776be0c6782967e1e1f60e99eaf8a8d1cbc2af23d54995e6f08d90c1469a1198dbab5e4813a6094e9b63b73fbccedf6361b0fb8dad3c69d9851f6eaccc61145deff6d8afb54c86bbde03aa2ac7e62df53fe9f6e361150d629716e9f77f2a74d5e90b3bebfe0a2ab4f2f0c44456a963039644e7826b80d00d77af962b3657a2fafc66894708b8ade6da02b6922fd2d836bacbb2726ecc6754e92330b7bd03c6fba9294b3064ac067a179c089f0097759ba272a4df1468641389de17622fb67e5eb3d05280b50d03c32a00d60177d13d9ea0d33aeac2c6cece4109a99d781a8f58c33b29b8a1c32b5e404b158b6df904e0eff24ae420a249721bad26f09f7cb7f454dc3eced2c0c14ef38f4557a9a532c2cc9aaa060ce5c9fcc1a1aaa97d0319bcc6c1c0ea17a7c45d1ef826ad1e5feeed56e9b247e11e1d15b1b0c10d7daf272aa09342c19f99f98cbcbe165e1f33b69ba7961ea7d3bbfd132325741b35a976592068abca65c327ce0d6a363bf4c8ca5f1346762a32b89f63207dcde5e24f35276fb86877c2395d2ec614cec5b2eed573705a47394786c2d262adb95a6ec3541961765492bd9d9cb524635e8ad98b76a8353defe0776127e1f7ee4123418cd5fc1739b7460ca1b6b9ca67b25678810c95830f746277c4496e43259da536347b34cc4543baf536eec2029e7df28be321965fdd683b171ee6c66355611f8d30df7183d3c444d7908fa44a877bea6bc6141e5f67ad82ab7b4bfcdf516a4d158cc7524869bf84cdbaef23ceafdfa6412c69ab81f22f46a521273374ef26850b422e359851557625959b6b2584e13c71b2742f592d82a8072ea8dd2fb8d183066973546297104d14d60b7dc3cd92a27b0423fb31f70921eec61b4f96e5729a119898cf891cc25e70469d7ca906499851c19fa11f3a7846487e33a09a76338305458d6f94d928e015b7a1cf5345b1fa36e17f17785071105ca23fb1b22c8aa304e3cdf807d97eb28e74939e33dc2f6e3982281041724e033fea0f691049c953ec31b95b607c22737081e7d8b622b23496dcdcdc9702dbb8eb03f31b899da9cb738d9185c633eb2b0a19b014422ba83136d608451885e9614b2d2308ba74c0a697354a8e838b2fa39e5f89bd770fc58bdb0f9657ed92711d61cbb5e2f9f9fb14cacbdb8e743f9e147738c969b76f472b09908c34e61154b5c655e9ad92be6ef030034868d5c8d6b81dbc6460be8f91bb7fc8c16d3ca27e14adc486a54090031c96d4ce6b1d09fb6eb98f1787d1d33b2b744ea8bd9c717c2f6f47b82aa7d1163ce6f30528d3d662784800c2ae1a948be310521a6cfc2b8e156c4c9bec45a0cae94ec39d3b05af4660fadc9dbb6406944c3b1c0a080028246e71080332b75337f53482c13e705905f149b8022a520355b4b8480d23f4113c934cdfaa33e95c4c4141cea6b4f3201a31482a5361f63927de25d995f15f1f62679b41773e332d4002155e072756c1b79f8129664b025f945178ff3b9e8abc8e65caac04cfdc19a320e28ab922e0016a90078abc40b7e5f46d4e356934de736a71758a1c1a11a35999996750e341055b6dfc206e4653e076cf153b9a291979925194e9630c5ec93c17a29f28f895c956924d36446aa425c6a8cb1fe229aba5ee86d6bc7bbd26b1fed01f1154bb3a175d27178b7f3cf26c05cabda0abcbfde7c5bb4828b00b2cf513842551f4ae4f4ca671740f6272c6a026e448a9c6d3fda60d2653c7736e0915a5dcebe7462444700c67e4d29e261c1e247c2bb6e987007b336a9499370c1b9c6f200471c837b468566361ae7065c0da5cbfa8d7fbd21098d8b855205ecab946eef911eff7e13ff59efd9bcb19e1ec3f57e28af129910bf606c7646d58819f49312590dbc69ad41fdd7f49b0c5c8fbd654d237411ac5c8dc39350d271918ddd6c763369f5ffd8a47ec0352a12bf881f39843c342b7ce007be57c6c55ca24ab172279296ae0d2d3c56bd69316230882506f0f08613e57fd93fcd84d8d72c567d3a2896bbb71454a336433723e7cb6211f8718bb402d2448b7c6a11cc43e29963070dc6165876652881b5fa84644697c51a635337ab3a0a21a6462a14ed16cf3b3775ed45b6753bceca42429bc399611cc6da8f23201985bca697ef230156484339341a50aa93ab2a92a66c622d9bc40579cfe8e853e40ac6e12635425cb029f4511fb8305102a2233931e82352e669099fda476d1d5a9b76612be1671d5f719f18a7b85883e372706ef6ebc661da43d9b6246a54678207bbb56438566d314f28bedd3745259a0995b5c656126b9d8cc33aafb1f6171d9e1e147762f83204e4741380028ca9ac715810d761a971187c57b7b0b05be7e02566aa364c8298376133cb30f020436c912c3425c6a4af886089a7bebb0675898bc6615fc0491d42af99ed9185e03a8cd35be35bf51ed69f34e8934c528f52c33986035c5c613f0349c6a4c6b4611cc68b7e7a40ecca4350cabfd41aab84f53e488efaf8cb532d7b854000652b295872d6e395060a02478e8f4d882f7d056da51e11048add8b5267371f4bab3c0ae83d1c047b8c04112f53e513a124db4c6cdc9cf787c3f398c52003612a28c7822e9a813048117971d34d3290fd4f030c182059d13dd4ab9aa9f41a52b539331030c9a16ee5913003a9a688c9b418f54f49066a5dd4ae970c84ad18090795e716f2e10c04b670f12267f8b0d99e814edbb03298d6d64c213acbfb773e60eb9492916f47f571172ad9f8436a76fcd02d198815577c768bbfe06b34c22262ad7b01ffb99e66e119e87816cd3deecdd34d60c79d8136293252a4332ec50ec166a0b393d35b1af3c850cd5f4763b11808c63290c692b5ac8749056ad0b433d00827aa0756aae5287c788cf8e9c1286e2efa7830e0183dfd4ab34a2969a3d39dd1be48f4f2feed6805bb7226cb40139da47e4a0fac2093fdbe709e0f9e87a51e4ec0ec1a47e1dbf2fe2131776227bc90d747a819085340fd9f20bf5d68ce449cf89686c53c179331debbc918ef5d937dc273927706222a0fc3ba08daca402770852235056c6e013650e3941974db2d0ffed105f70cf4f642d9d49cd2dc84b16a1a5471309d215dc842b5d15a6665069a3063f73a06fdb4d523037549d9e4a3b32bc7f142cd9d8b8e55e7657a00e1781439518efd5d9a8170f465a08899cd9d93be9281d44751cb38d56796143558b32003352302af6878f43e6c2fd29681d475d7c6b081c9c6c8bb5ce4d6aaf7aa1981d4e9404d486f4eb2bcf45349734b072ab40931bec732ed0971d388aaac4c0f30d981c8a6811fe46d58dcfef80e843acd39a8fe8c0c538914c5c60e34679639f5ec8e03bf4569c90c3f96da8ef40ccb22b9e7c5b7543f6e979d1e3604312f9670fe0fe86dcc32a392f7827fde7f1c40da3421633ba36da100ea8b2b8e137fa981b66c983d66b24fc915c8596f29c696cdfb9e8b15822ce1357815716785c034b8cd192104756360b35886f0a4950bb7bd7e650d3b9a6c8cfc0a6722bfe562b757bd6f4d83cab7049939a7377b2c8fd763e07432a6b47fb496755e1c53bab39ff7af50fafb845328eaab0356096adeab2ced685d167fb5e5888326639013e7ae85d0987e476648ff757c11c0594328b4c8d46354031f7228634ff4f85b0d4bcd23406bac147f9d707f177e94dd85912060e1a62092162c547e25547cd5bcc2cb56740cfe913da1ff794b9d2285a7fea7a60e0eda1e49fd8b7cd07e017e87f8221e95a19e2c44c886345b4ce8cbd749e64911ae8295902ef39e95253dd760e472de9f6b630704bf5adc05a104b71f71773b746852baeb9a5cfcd32b2c5d9a4df999a37fa04912127448bbb8b198bec118b18a5f1e46d283c651313e8d5095ca89e75323208ef4f0701b7a9b3bcd46e8b94926a242c42ee77d294491722287d833444e4e1b6b0ae84900437f3e74e86807660547655984dfa4575b0fd137ed26e3dfb60b21a9742e1fe1873c08993042bf055148e8a0c3342bbc5ae7f7f36e4bbeda4889175e34a316c370ea8bc017196ed9e775a65c7562bceef3f08879d5facc84eb75e314abc7781d07c61e0e2971e9aa186d790cb36853d4b2a26dfe8a136d9b4fbc68233c5fece5f820a7a8aeac140092610eedead0d4b751678a5e770e8fb45787dc28074c27b04a301bce2e629089e28a130e5971da7c8f25ba5ad0214d7a69ecca30707b16aa1c463eed7fa868d3c33d3e25e81c7eb4406e40aaf652559be5f916c7ae0c452709a3ca382aaf43220ebd7b644bf8987017cfcda449f09fe952f46fba14ff992c457f4d4b548e5d1f4e182b01181350dfa1156f3815d54abdc51eaa66e2875fddfcd1b410fd372d45ff4d0bf17fd3521563d78792b7bcd020c1888744cdccc3a971d7281ccb4f97c0f5e1dd13c511e58321d27421fe370f5d5eeb4676a8d6b1eb16e257c90d0c570f257797631ac650ccd50535630b892c5808df848f4556fea12f7402106759c2dd847466745eda18ea2f81ce8198b2e328625a19f99f56c6fe26f51df4917e41ad63d73df914e5073181f82d5782684a1cdcf23f7845046b3278416cd577bc5d46570453d35d1ac38e2662515d3586579ad738443dcbc3af2b0b4d636c4c60c68793a913e83065f0e3c9dad54e844f3e12ce89891f0b4324dd420ab77181866fdfc145f00214b1c7e1cd59acc2fe3edd8d9049cd53c4240aaa78f425c396ed089365512e3f26cd39165f22f76fd8949b84e7fc54c445545e5923113044dc1cbaa7fb05c95298aafa822137ada2d53181b4304ee5d93aaeae236049c84b47b3c993a67b3a22c9f65110149e8cc55031529c26db314034d82f283ee467182ff22b9415f9921bbbd554fe6ace749fba4ee9c4ad92482a83984300da631ab5be9b92a5dc9a6366b47d4e327e5ee212292b97c26d42e511d71c2642677ccda58bcd8506fd9a5270b84dc7a3bc4cfb3ac78fb19a60dd77a630c1337d55f52534c333c1ed6d63f49a47786add01eb0ae6d0ff1a292fd00d881923dc161343d3e503b68d4a169dcb14690e68a9f99a51388ed605ce9fdce875b18fbd3158256e32f5f5e26d9aa869749b9b9e847482358943691626b6ffd24f420c60d3ac8094636ab9a3119e6e8d3905d171300eff30ec032141d2446167922911437b633483e1d78377c9689c7117a243baf223dd2c3ab6c22940bb46a3c0792c68377680add748e9f034c220178f3d41c4ca6b1b91b6e6e9944067462db2d7d3303ef8b1c7ccabd92c05332353d50ba2dc3871311e846683723c7cb8b2f71f5c91b5f80563f235e3fa4a1413c2679ff66cd073d8704adbf9b466624a1aa3c6289f7223a79ec80632bf8b770051e1bb185a22418ba251039a9effc32ee907869d9156fd911ef1d78c309c431314cf6ac42a666aaf0bbf165ddcd01994c7aafc56e1dc79a51db0c791c04141c9b9007f8f06817b10d8252aee8650bda2fbb08da0b25fc292df7ec8fc84c9ca3a42b3e5d46f07a8b41a349b95b8d5a9245c8f60361b9433720a1968bbc4d9c5ebec3e8959dfa7463263e978f68346911f3e448e2bb5ad4b72ddd990126e0e379ba4efa768e27e4f0a1ec182b914b62b1c6d33baee74e48731b3b5c1dca3b1109b51ce02224d7d77bbfe689cd834c8b8f206e5c60c5a4d1c4c44c60cf9cad2e78d4cf0c20cc902faaa3d3311cdb2d2a072c2ed53652b0b1f298f5d62444899b7bc7319704ff6960372f2a4bbbdbf09688cd04f5d86db533d30d8f278fa39380c321c258aa15814d0103748c6462cdbc28585d04d6879886a4b1da49bfc19615e216b5baa36d86781319f8a2d4f3ebef90a9351b7fd77e2667d0670d9a5714e5d0fc289bfc4585ff99d0b1b697d0ffc7aa433c0f72c5424e4c3d2fef3589569421a3eb2a0c9911b09c9a10ce00df20ec4355d44b609db1cb655ca320ea072f144dbe3b5db150dc0c37129df219a4ed82595b396dc400848b54271c69d1e2e2a4bfb4009e1a7701db8502e9abb606db5c970464fd72238a9da71e1af40cb604b3cf90fc5cd7c955e130b9d42ccffba238055945207b31bbe28457f044ce2da42d220d4616e2456bedccfbb8940e9c8b26f5c4a6c04153df172628eedd46513cad8e3e2c668f22fc6564f521a22553e222908657283c8838b81dd653985d0be0c273712295fee274544a64664b9117a1975b202dc87103fab1296e5322169b2be82ff9d68c6216bb3641489e375eafa420c419860119d5c61be1b81110d6bf056a3bba59e4927b256ac7264c2eb4f1a88cf69e7aa9487d79a7878d426265d9656f29713cc32c4364ec624914fda5258ae6e4431f6ba9f59cc259a285ddc1e1e4473018195a368ff1d01fb35c766832c74e39615b2e56389c9e5b0e5b9cd925c91d32908c53b08ea477cf4e460c6cce9f23442f7e71df7ade9ba9962dd91a05462f0a5322dc8c82faa819f2ff93497763069cc46dec556ffb40e107136dfb42f9dd3e0448d47d32bb1c1f80244cdbbcaa90e4cfce146cedfbc158962a0cf8e4c4aac957cdc8cc8180becfbc3524983a02d4efc1a3aa90f2800b5ca3825bd88fa33531f0b794fd38a4cef486ae9e53ffd71e3fdc81b353984d768e66fbdaf6f9ac34cd73169b4a3c345c17b772e2253f252e908a2733413779cb04252e8ef4fffadd13b0498cac104689e1170fc9608baab04e12a4be5e0099b2e22879b2af93be180115f820e627f826c998455a5a693b38cc96a9775c808f30b22efe2b785f1109247707040adb191f5cff2d254fdc2e396ba03d2e802c1a02d0dec0dff8c0aeb8b3683f54312de9240d7d151c6c3757de70c65196e9107689c60bc8fec7e4c44687b7b5c54337982a1de56fc4891bb6d9e54fdd34a35c1832b209d00b88b1d79553304aca183d9a310a4eb8069486c20c857dd44ab0c55a7abcf0abb7e434f5d65733aa8c5e3e897fc9ed2c46271ed5b0b76535889ab7c66786a369740a59d8467c67c07d074e4792f10c852b0f1c8c60be0240599bac95c161c866cbf8fbfe1e7fa6bf603bcbdaaa1fcc64181f0f0817f1862a8861018e8d7c16e01ca857f0be11dec967752545b708d845951d02020f7f0d664e1bdfd0fdcbe0263ffc0d8271f92403c1b3897da389134d449cfb053deb1e8662806f3a9193335f1c8a15346149cabf85c3807e9a91ab05b049804d2609feecadb70cb316489ff88d81fc48736df98e921e043fbbe67b2a4c4452e534b1b380369e348d859ae6a227f00ffa931c4291fdc0aa216a2189d265a82b5a708c34cc5fff112372443ef05b67c7f301bc801deb25bfc0cf1e74329579da7ee827a374194fd4e60a4b1f79b73b1833021d93a2042645207d45d7ae98079536fff3fe02dfaf37b8e47f40dec883daa21fb379a093672ea6185a981ed445a8fc9ba92a4cbfd552b61792e6f981b4f09bd14ab901f89ac015f0ad1235d06aefcce82c0565d5249274433bff428dc5a63f743e6f356e643c5117b4a16e80f185e1415e4e52b02c4f6bb722aacf1147dc356f5cb70caa1714c78f6168935116f4b1715bc582ecbab831618c0e9d2e52a27902f1715a00fadecb7588cbfac7bab35e0e196d5150c3c696401a4ecdf6954995ff096bd1e819da471e1079d90a93ea242b9fd694d0f2c746fa6a3095a822f40d373a96ff2aab149b1ff742b3378618c036fbfa8472a3a98dab547dda831ed61657f08d90ac003397b46618dda3115638a9d676b1f3a37034a7b24d7bbaf143c514b1a5b2cfbcc0f2dc3c598d83eb9f66667016f5686e2adf918a5f3340d29504db12ba0ac9a93215fa2857002c30e9aa32cbd431ce8c7fa6e4f062b380af361977346ba4f72fa603768b977987dd5f1d7df6e9b2cad84fad83976efc659bd523297b1b6b9bcac3cbf11c514c5e977986f13ade87ca3b2b0fbc6adf92d14c9fffd2ffc5d52cd7cb4be46591eb8a544dac7747b49de52d2c02288ebf7184c95e6ee94d707890a4a779ed874eddda695ee8855ef625b56107f9c7bba54e471dda6a79f6a9506a9747bd223c43529504eff27f5f93b642ecf0ac5709abee84e55d6136409df8989d59427b52dbbb77127bb52fa7a84356aa0bc9b60716ca5be315c25d21043e83637b17a2c00c9752cdc9a455b65414ef87571e4904d38f592b9489f2f1bc211be9986a8b2abcb5d160b92f916ca6cbfa4eb3f469ea69f34958dd47a3148ee43ff0dcb5335f2070d046acebdb3ff4bed23a41e4082db2d3edd89bc103e9e22f241f4ebe637d8124ff110d25697656e4023f9336d5734545b9af929b23d23fb92b074862a0ab470fab0bd57e4a84a784a8ffcc32f6f3e1eebddbcf58383aca52f8f89db47d0ec89d06563a978ff8912069b301a0cc5587102cc0d4eb2901cdc2d018dbc00715b70c47017609d5b87bf737d89d10d1f898e83fc974f58b8b11628b5b4287f3278e246ae3d3f8988c6eda2354376446509e3e0d182b6cdd1b875e77b896de8e8e457d422d72d8230fc1129e11a2296047083915e1fcf25f5f2eee607a1210d34c7544ea11051748542690e21d03ae7e334d8a5d71d8e88947965792441d71c110ee475662ca665ae12bdf34fe4754c40916cb22136e97e7462f2a0e229c3a15f7f055eb088d3aa5576e01ff8209a4e559b667cbcc430643170d018ede8053895cf0f9fb76c2c0668d47a0f9eb9302d6e989d7be2eb233747982887af2342eb26d7c601824849b09b45bfa1d4a2a444bb2997904354eb1147beaba7b91f1dc032d3446464d21910a83b6073a27c887cad503914de717ae1e53c8b9f23fcf55d1a0d8a5202f1cb625fb224110903a5c7d4d8397e98ccc8950ad0df5027ac7ad53b7c9c0a33bf1797ec0c1243e24350a0fd2db8e8ba36625eab1fefada0a67a4efab48eb5b0f0b096f3e5aecd33eacf4b29fb5567248435eb1e96342031f223b0929a6f72f0b874d1d105a585041f4cb46b0f7d97ab8231dba9a3791de821b19ebec6b771641bfe9d43789b7e0996f75107c9b9412698db807ff0fd7b2878322f2771836877de458d961630f5d14f5b9831f95c30cc4740d8d8141463fc7064c98e853d3586862ed02288a805ef4ade92342224497724519df868886fb2fe9e5cc59f9e65f38f2b622e9d718077356cd47db294e28a28acb0ad3317d5a21226bd22e1c84f96c5b1a34ef93b6f9cf9a4206e9f4dcdc87955837079e9056239d6ab72023acdc3b1c271ba8450c741bb98f6dbef299c6cfc5267885e75373e355ecc32679c32d0307fe7b8ae641a28e77bb75cb8c2217ff39aa599d94ce138c76057477b9170b49e1e074b2d2dcc9c28c46dfc7c94e536f03b34f3c303b7d281a7a97eba51e601fc0ff18a38c5048d60121e48e40f9174c7bf53421f7100ecadf246a1ca694317d3bfa4e9b8bee532b4622208d037600039e4a4ad7bb3bf795afa42285c01ffaff59d28746b063cd49cc2b49db8a04bf32bfcd2150501fe06a16e560d9dc45ad20fe20ca5b4a5cc64a6a7882f1d7ee126ab221e48e3fa224812900258f6013ea803ae60576dd26ac43dad254060d2991baa0082b4f65e74638428aaa361bceb9f893fde16289d89941aca19e922d0665c543b876ab9967e05860e4e3790b8b6e70e41de8952c8c51d9f21241990125d2cd796e1814c35a1f7422106418da5871ad09a010996b7097b9d72190d2f434484d4e6c836669342ce8ccc0ee926c59abfbcec4bc59dba41c318875468cc8ba2259b32971c8e72aca2c44f85fef8e2667630b2b7322112668f12706fe3213a0e6a6af66b0cabf605d762edc1b847ee7e62349285fbf115e788908d03a6a3a94ce247b8dacf6d822cbc3da831a7da0b61f11cadd77018bad4c3293472ae18eeaa693d225092936a3cefc18261150dcae8e6f6264482ef1a6b286595bfe8376e71acae24a98c316c909e3d625dfb524745d5e3c71817ef42c854d590bd9c3d816aa064a59c2445a200455760b5013d49d231177938f2a4a53989b69451239a42ae71bfa0d0291bd096f344128e7067379117f4fbf318a328e3226970388551c3a2c58527b909ef95b13cbaf35d5b8868643c772be98fa584c99fa38d2ac74152ea72861f6107e2933085ded0b4b03826337b960612cc45684e79219b5a34183204d8fec83d64615f7bd4cbbd00f166ffda771edae18c8f13d4d81550a7ac804091e61e00fdebdc71012f8b522f57e2e52bc1ba69ed381deab6ffeb10913c05b81777304c49ba4dd25c53882dac7090c669740864e8fbf152bd4442fcbc7e391ccdda14f5f45f0848e4eb03129e5a53f2d894b3f2ed68f687beebe23d3f1aa698018a84a341c65fad29f1ee65f56ed8d8cde09f3117b49bee0d17f1846520a8729b14ee20e7e136d885b70674734a962007a8d6df9802f0d6506c34b8d10604b6eed1895d9844c706c78582d4fea35b008c2085fbb133d629edc4343b15fc0d93959960fba33c01df9e405b72c960a8d6bc9580ce1c38342278ad176c3f0cee3b4d80e832a9327ec376b9ecc57e7edc5ab3bcd65b06d83da5b90f8061a3adb47e3d0490b4d1455347be0551736dc992b945ada4f5024553eb5356da85a846d1c82ee29616062885ebe895d17aa294d8832c16a9f1d6ffa10f75e25629577e4dc7d2805e0e14a89626672bdb14a2cbf7521e53d1fcf335c4d1cbe91451ab175f5367ac997a45c3aefb295314a9c07c2ddc8cc955600cf31bd96a1ff7b14db4e4668d02b9c32a7368bdb73bb68949d7d62d9ac87c8e1c19d1454de7bea5ba31a44fae62ff44ee9c9823c9fec57f6ebcfd9e1176b12cbf2d80f229f8dd774035033399791be1a8b4c084869096a10db7a41c2770a5ef2e24fa370f57fbafc45d8e9bfb81715d5206140ce077a3433c0352a3e85fc2d0e15e8be2fbd3c573962415e10c8a3f65c33349d2ffd504f8f028659009c1f5bdec74afb884f6a3eff69b4952d7f7c3cfb05a995ecf47155086b581a292fbf73f3a7612ba1c63152b205b9971899cc80cc71f6464ab7028863499eeb8aaf7a143c17884bc7720428dc11c20dd666b73491051388274971cdb1e2fb540b9d5da4a9ddecbc24418683de22b2cd09e7ef1d36a7996f4026f4351cb0cfc6e466d0af267e1096cc119f241f14f8a609729d1abec6dca67b6b3c237260770bfd941e842e4eec02ec202de718ef497e92c4beca79857f24d495b911924054993d6c364a40f8cdfe200af52280cbe3c314bd72871472029f40de46a383fdb7ee2b999c6667990455fb6112bfed84acc1674114fa991045e0ead7e60338ca1b24729a8375da0128fb891e001f806a2ba979bdacca822a20497bc3781936e58c64603393f9870323a90c5c98327e82ad558530f3ee13a810b1f0dea2d9b1b1c04a1dcf421d6014fa6fb1719fdd55493f05fd1bc029c8b3ee42efa40ec3c00963d9fadd867bc1f04526c280dbf0503d5379d1f9c5f75c395b44382fb79ec9cbe418732dc30b4398b62b52b03ef4f3d7afecc79df2292a11c7557b55b1f580ae5fc4a6d69f13628c73ba65ab6006f5d21e80e616d4115e515fc40f519c2efbd1def2f0d0d3443d9d4b7ea81c9679c396b11e44c9eef710cd44c4225aa90bdcb3e64aea8b6dabc750282470c7c836279a360900ace07b43d30085989fe5267e5ae53e898243ffbce5406248c5a3bc78c9cef742f388a7ccf370298a8edab018b82c0a467af0fe575099918253bc92ed2c9de5e9019ea665c692c4c53227d3f8285b040737f539e98f62f65610539fa94e0c7d1cb6dd54c62263f3cb65122ed428e5896e99c6a0b51c73bb095c100031fc590f322b001b0f695586f096c536b11207881c0e7d4d10d58cdf496a76830c0e1bd57e950c6a1913d1d475cf1f25b8d50bded0bd994bd655eae43477c1062fa85492fd3f7531019412b89b6e089ae3794046d99689192363fcdca43946385f3bab4c8d4d0c3a3f793c0cc3dccd820662b5764a0f886874ddcee6b1a93877f2f4f79a760b5eea5ff384f0577e31bfe43d85b8b504d276499e1e47e3fd3431dfcdd0f453da226a80bf4d3bd08b89ac46f6b8a632fd69695bd334d04a99a1b6dd1b874cb46aa9c96b4bd51a293c6c0d240781e8c76a96cfddd9470c7e5d748e58c6893d8fa25bbecb115d27957484bdac55138e13cc28bd93478961abde7fcdf9981021e041c5721286e3271d4c5b18565b523065df28496b6b353e8a13e9066f6d91cba183afab0172b89da002209885886044923020297d12d96e89898be061c0c6445ffdef189a0090390426b317dc8500a1f02459f72f93dad98b11b495c6b4ecc90a060a263add3a64e8764795d3572e3211d2ddfe7d61320ce3d0a1ef85665b5b88621f45ae68b1a25cf204287163df31bcd7dbf06e6ba02ce25b7c2697e11530982b358aac715a0df15f0562fafbc2a1b631906596057d298c82d3df9114e345d6b3d381706e2fd3875edba2d51a0f06dc6a3b4830f86673b101c4aa1f6a340a9541d8e9ef8f87a08892dba95da959b9d31af677f3add39fb7e36227e745a36ad5c38ac30f23c22bde6f8329114687ff1aa1457dfde5ac1f7dfd8f7d7d8edafde9d4b85182545a83154d3548a3d0a8e4530d5ba48649df1178e677168870d84bb214d1fbfb651ea80093214a5e24b61e54d126ab94e238c963838ddeaabf874a55b8231d66fbbb86596e2c5164b0e4e1169683b86d2e8717dabdeffb7ea321cf1935a0de13f95a64752e4f5c05456c6a9a07b302aecd5eb193a0a543d78a2b77cc97b00302e389bf7bca4708eccbb3548f3c0abce859258fea4ded3320b2b73574755f919117553fcba34a4d38d32e1cf9a777d6dc89338dfae8ec4b3dd7a5c9f734ee96b9314719022613a24f63ccafef38abb9caf1a7e1d3c7305133fef8a4036a069b655b460c5f9dcecd29265ac183cea7ab261027af6bd59c8428fed0a2d7bd5f6c8a1aaf368a531df352382b14f37482ab125dc95ba61726a9144a51917930fd7555e545be0ced49a6ce6c2456485073ad4ba63693489a898686c2f11d025015d6181a3db47f0f9a94e75d5f9386e5c4da95a283f515fa4fdd46795bec65c127f3a8eab5f9fc890336afb65131bced229f3d45f62c79ddab2496dd020d997023e0ffdb5a0395bc92492c23c5a9b845cc7e313d3ae64b51f7a76f5448f68eef50cf58c7a8df542ada3ba86bc1e514fea1dd50b55d4761148a55284eb05e819f50cd5eb495573949c1bd3cd5e14a4f8b2bb510ff55cb35ee809d0cb29e12b2a5b7b2a5245a9a92a5471a8741d7de653c294f3f490c4418bbce0ca40b50110e7fd2304b164f95f4a3951a89faa7e4d26282f70021db2fc12c3c9a17f1dd3c34bf866401fad8038c45e2385ba04d84b87400ca6613d3a2d031880b86c5d3a7b9e446200c45c36cd9f300fc4710d95634f9c5e1851f5754028ec535cf1e9850ec149cf13759590bbeba48fa6ea2103a588a55295c6594347832aa74709ebe5e556c503de97570e56aba317e659049a0630c091bbed66574c049a27606356d8963b3226b97f7853da1f971ef28527ff772922e61b0c84addf6c2eeb4000091644b28506f49ae22a7e6a63771cfa904896c6a06abfd7a016f81edfab4b3d89f955a42a7ebd0a7fc2b95057119840e24ede922f632326fe14c01ee81a3d15049702f4387719497250114a63328fbf119aed943678ba098f13d64a2a6686123e4425317f84f9f51c1071aed29c8d2b144dbdc7204a1403c484262ebef2f1c50691931da42fe196e2141c3f94551d53d09f289b69ff19d2c7e88e14651b65559512c155e67b755c2413653bcb43fce7044d139a783438c8aef2388c031b332c3d93c4e989b1748e131d5af106cd0f6966d85dcf1593737b549fea1ecec6bd288b756d176eb2b1ddd9115303bd9dd2291994804138db2d4c91e1089b6272d70602329e2e70876ceffaa3228080fbe28050c09c91d12b94d3240848fa33ea828566e1bb2e221afc76cdef0f246fd310de48504aeae078a08d80d635034a351f0e65106f777a253370bd816928d78b3d7c90fddb9f0e225d35cdcdd8fd2d81143d6b214063457206b8f194db0cea6db90aa99497888678a857d97c1ad5ce4ae768b48fd3e49c166080523b7c7079d0853909308be071dc09ccc29eb012c01389743bd95b3645dc79c467b4ae8fe8e401921224e0e14dc408d2ce3fb57c264fab1000c6c3b4e6754e06a60f03a3aecd6ed7506738770bfa6bcf53b40d7ac2e15609c55c4dea05be963cc16dfff5849c3e2035ce54182ed3b75fbce44a8344484c4c5f1d852d57710b894725d2125b22bea079368c40ee9ba44f3ca2c69b0274360b088a0d3921d106d817619748683eece7dd1616b650cb6b7ae7570a2b0df23e4b82123f8ac2514ab656743e6261d55d029b330ae0349c7bcb0c4d50bfdad9a982c9a41e6f21734f31e43c59a96730dce0151d11976a75139ef3e88d319e7edfea008621e575c0967bbabb74e63dbc28e3255d4eff61d30d73e934b5d4cd4183f9435c41b4bd8de0d4692b9a58ec9ea23f33ddb66b9b53ddee1cc653225def33121f167bc39b1921a617e7841be504da6efa582aeabd447519eb903a3635f07f8acf51f0d4b3c13a56539e59d74c07b52f7ee9da7828a774aeb9ed8bda3c2b7cf18098b2615f11fc4d0cae9fe23fe8f39403834babf1ac9c9f62e3b243d7e4cfe4099412651db7cc2c378189edac154b07bc38936b1e66ff5edcd6689695e7394b498bec9b67a3b74227f2ef63d706d7e8d079d8f7ac9513e63993541a1a350941f60020e67f3f451897c67494133f3361e0d2fdda3dc15ebe2132341eb4d6c6368ca64c4c96d05ea94a5d4d053a2dfd9b3303512d2e79f9d4cf12f7c21deec44397b9c93c166aaf663b27d5d8986d745e34711f751d2cd6aea2fd7757fc6e0292646b40c369413980cd3a2dfe05655325062eb6c2c4cb0abf2f37e93ad4b60f0439681be53a04894a0451ac44759d8cb335ca7fcece511f5a52ab256952086aa7ce74eda21fe21f576ef1f9202b5681359b870f35407cdcc2fd0eac0fe82a59ab3ee4922545fa203455350b990859abb975124ef501e1eb3a816cbd3dfb94d608879f89776fcbc94f652bc13e708d82feec666850c29d805a2cb93f533bae16bd49b1f09ac107e09cb665a6179cbfc7d2852aa171121afdd2d5f899886d0910c4af9d3d19f0c405d41c8dbc980426abc13368cfbbb516a1b00c6b92c9c10f5e0df6b3339db6e6becea7e6663535206821ff72bfbde177510039211bd3d2c4a763eb93f709970cb8fc929306ecb77fb833892b2f5cc0f9b8c652d24d984847d11674b47b8d387d9d7400d2456f61e8661d8f0562b80cd2878407a0c9df9c8c5ed87ed689dc7ea2a29241b7a818046233292dad92f331f51d54d93a9bd2ad0f1347b0e3f9723c2f42023b66c7832131d75382ce9a5db41314c0d458f2a77fdf59eeb6865f3fb60e01107fdc209a5b3df82559638e5c36b83a92090b5f9cb46c1504c9bd8913b337106277ec865189f1e04562425ebb11232f3b85b561764b709ae0189a757d70c825f628a91da040dce54461bd48174da5bf94493724b0b2d0981624863979cb8021999e8ae6b986c13b836561966664cddbafe3129ca0f49f86a014ddf124cfbf64b71b7eb2903cd0847f56f6168c4133505af250c1d3a1c7eefab67bfa91ad3f15a6e4a626c983720a70eecb8fec2b7729fb4faf6d12ee3a80bc62831fea76094c75d827008ecc0398210d4ede2618c24546c502666b55b68e420a1c157ac3f9a31374a1241e20363feed0b279b631902c1631ba27912c8b4b61b655df00754c821e578a67f9e9f4c46702f4b11ab244942fde2ae86fce37c78d7cc3b3d9622a0be06f0c370d6f883762ed917ac3aedea927ea9d7ac7fa11fa345ebd52cf502f38bdf32a68f0f87e2cd572acec6383e122fa3885c62119544b8e86d353d436a0feb9be3a07197eab0e7725a3190c201d55dd2412787d664094d9c4e4357a55353d8ce30b4741f388871e503e185317468239c6630c22912f0ed3260808df06f7095897c40fe3adb96f9048049fc1c66648515c283c8626c893e380242e24c458b94315aeb9f2b8371dfe295746ee6db6faa9c37e9f456caad6b59afd53ef086589a5173f9b09be8746e4e08f711d87c157df7b12c4622a210bd2d3eabcd7a22cb9f513b8892a661168c046167fadef5c8cb4122ecc73ab090873b14af0ca44070aa395f2c8be499bb4219eacae0d365b516eac37ebf496e5b2d1f0b2719161f0c3d22adb9ae7d539070e3decca7dd08a522a37e98ecd86369505569f086a89e57bdf6a7c5ca707d7148e3282e56333dd930f825c4cbd3fc9d06e22048374013be642275223ab367f56f4b2dfcfd32c54819d5ac9ea1caf5216046bf9962c04f2d1d64d1044ff73d9e1881e840a214b14d70008b9dfce79a4bc9a6896d42cc0b603877b12127051cf8dc0b769ef973770a33b693e05aa71c06e7e22b8466ec6391b842cf2ba350413040847834d472b2068b21b38b4708c4393fde84ff0cd4bc305ae04e32869aaed8e2dda8d8a1cc2236a10abe624168df46209f146dbd6c024959057fb3934e31b3a1c7174775c4870f2a7c74cd4723e71709de817ffa3d658c17c72412eb36a221eb28c02f804b494a4a30d394bce47225646f485d155169a7b373a68c773ea793fd197c5f43140fa853b61920b0ef21b25d59bf9e32a55a444c93d5c3f114c51b81d0b68cd93b89e28e347c4dea65df4f21c4b34b493a2939074c35908737bd2226dec793a3d609984a6a50bffcd2a60662b72f257949463012213f58b5612ad6b284e92945a2dacb2a43ed52d37ac321e5fe9a97c26ceae5695bfc003e2b5338eb19fb7828db357594c4b5c299862a58c668bc9f8c9ad15e6ea25d060a866270640a08a155ec673ddad4574a32db5690712d4c03f2071d232b88aa155513d389cc29ef1e54a7f326be32cdc21d73f1a7790766dfbf7f33b4e08d4cd75f08e9ed3bc544ed20db153c86c8bc3292f1ad14e6f1bcc9021545e3b956d74a5f824daaa65d38684437c6a031633d449e3fd54831cd1061932b7bb578638e5373f44878d2004c3194656600619311b06275946a2abe5350c1e9f1adf7f6867eeb10dfc053d08505d77dd6f620211e8205f1f3622fdf08a57787dc41e9fd27f108204a52f8c0648cddbe6a5b0e1bcd842fd68aa37103bfcedbb5d8ccf83e0136d6f976c2dfc0ed21b1dea1df1f92362c444074823a7e3a4828513302c22d25876321438de2311ae6615e266e860e9de86b6def937811449b1908639225428ed89996b7a202ab6bc717b72348e6b4a4657d911a69c287d4d5decd28534367c790d2221b0f49a3e9127e88a6fb25f8f4619749a8c0b663905d0a80b1a48beee7988d2374a5f4d6998d112150f19d687720c8f0f345d13ba9cd74e29a986e7d19c6a25ac9510240b99f6458aaa1c3908ae5de0bc693c98dd03cde9ffc1bb0c700b44b792876a39a668930a89983f7306e1c210e2d2b26d7de18d735b3f8a3e996eb43d657dbb620361a28965f02dd43bd8ad905e9e2aa7856e12b7e010eef4972a2546ce3a90a28608f6f003276f9c9d5ddf96e3aa215c38a30a26238b4f91cd8ef8e1accbf39ff370eb08fa04a52d0154684c02bda3269904bda0d2e7556db2593bc6ade04ecb41ea9b74376851268baf9764a0c199834c26b14c01c8b2ce6e31ad18110c3c48e30f845146fbd00826b49d629f1c43d18662bf997b37ab342d92dfb277cd2b38fc1b24307bd5e1043de6b1a9fc7b13f39441b4f5cd067f2f42ab8025d8071cb02b5c51cb762ce9dd4e7abe7591c9ae0e505fbcdbbd42c6093554b105ae2d4d20cd0584b6893db30b8b58094ea5ac921d7bf742e913b7e077792f49fb6f861b859e67ac099acd903257d2686910ffc09bec4122aac4a148fceacb44a0b488753e9d3a71c44847a92fc041052a5201ce6942f2a2cfe67eac25b4f989b5a5dfce1eb9e986063e99b40536c1bc06729f2185ed987029c6fdc9dcbd620e51dfc0b830fee6ba92908a8648dec4f2bf41f1d87228591df0b541ff3c487de6545094c85ad1a3cb5c781faafcfefb9412e1cfe96f370bd74928afe9a907a92757b07342d563b5bda4b2911a1084633aafac063c769ec079002b8901cf0e940e5627cf1b639dbe243f4e1a3d354547844fba7272dc20bc39fd71867c8ab1b55394a64894e3a85c8b458600843148c2f26218887bcc519cc268e9f0a18ae322654c1e3ea557845856360bf3cfc6824b8068c27810ae7ddac84d0068e05d0e59af69414b77c3444b7fdf494842b99b13ba6850169dbe7b60bf49ab35ba579d08d2e0a8cc1f1a779c5a361e5592dd46f25200138b7cc0bfdd366bdd4c280c92727e4b0d60fa92043bf61994983456cc8153e1e48e92c67a299182d37e1242d5308818699d2fe583877f437ed0d57a6c0244879ecb20b3929ef3c5d351a5a45878296c869946620d619897c31e07dc5f278c4c6391d9e8ded93516b4f53905f1d0d365f3028834e01d4d9fcb7e2112c241e2f7bf480b81c4934d66877d16f68747490f0e8dab4a73d34b427f25d00835940e9f34c4aba2d0e06fd413e4d74a52fd56cea7c5bb8c7de47015cba92bc0257b1511ba28c021ba23573c0d6b66652a4d8f3774ca67dfe54a31370bf387136a2d3a841e7602811ae9c48c9a61b99406bd9e4d058015a6ba4fd15cfb814d70d57e1f441c5af49b82d6b939ffc1d2fe9da28a4be602a6d00af1f7da6b6c394cdaeba0953f6d03328e500d88f6e45022a15c822dfc5aafcab1507392bc95bffa5cd088bd011d0c87d686768574a652ed72a077d51ef8a9aebb454d07276055f6445263411eb184d5ce0144ed388dd9450730c7c11e4ca20b8ccacb19d39260f75dab045e046b0d912991b9f41f2a0f1dec93b4bc3a929d8bbb84b4822c5b45bcd6c480b7ac572e0c4a9ac28ad16ed93cee186a1c3ebda275d0007dc631aefa21902bc4a6edc9b4c4c5f1529f8d0f0b7edb863ca2971392dd211bf9a369366113251b79f5016aa9f717b9c5372163d058191aff5d52afe3ff1a79ecf5609c200dcc7e36561083633f8e233a84cbab3e70dd602b32c4984f4dd145218fbddcda83c44e6194b8d806280ad7bf2515f2158878368e3d20edfc8430e9a35d40d2316f0a108cd2b4e1e5b7492b158f29cefef9e0ae3f07562ce4db1ff08f1caa9af4211479ac3b7d122dc0a8b0920ac2332e0bf01bd29f2cd78b12ca688596290fe64f199684589623b5f7b4c5a487959b86c6255a3d055ee95423b6197c77683db22752395d24a7d4043691743169009491ba0511e108c64953a6f86c159d9c12e08e4e45c83da3c0f8b4145e62f8e78f49435baa6dae67b79b6bd65a9820b20cd461b878f611daadd8c0f080bbb3030da4c9159815aa8596aa0d5e59e46d9517e5d222e00bb365160940a4a8af28148817287c38f75a8fe307bae812ce19e800c9c38ebe9fcdec5ecd78c1e006d5f5932cacc57459ca26d47d617c48df9b302c2024bac35c9c4b4be2e43e33c78304c2cf8bc96b156cc713f1cf860d3cf790e6e6d1d6f8c1ad3631567962c205206a408a98bc5548c02c57e13fd726b415b7507e7845aa6334644c23808d468072eef0f56abf519c2993469fbe76641ea0be4bb017b821b416f0020f34c00e5c9bc1367d537ce18692a393cc40c916116aca7889e9d7d16de4d4c42b141c50d28564edc3fad6b9e67591e12706e2e1cb847c0419ed1a876f09eccf1c9dacfb7bbe0e503c21faa8b1259e730816068789bc2e80a366c7e84e024ebebd7f809d70f1cb3007e3865b4d5d6630dbd8325c30b88686785534acecd86262fca32c6b899320c6bd4a2cf2aea4552e1774d7805c69459c7963b1367ec291ea7ba6ed2f1982b60a7d9bec63bce7426f2eb262cd872bca0e227fd5102ca2c1136338ac8a213c3051b12d2c235d6c955d071d778f1bb0cb94cc9dcb81d91a866e2d5ef31308b4b90aedc8d940cffaabccd0002074d22edf14bcffbaab6c01567485fa74bb5f080ba38c71bd27666307b9d14fea905c088ad644ce6f7e38c31da2a9c15fbb53b151ccd1a73ebc40c4a55ed57919312550b42d0dfa9db737d258aa206109034ddc1222fe95ddbeaada8593b0f1c08e9004cb3e5b9923339ee0c8b9472e4013603cffc18e841f10d6c09b2cca0c863ee494b24b9562c2c01c02808ea7c0f744befb8518481cfa731268e7721d3000113f778aa1a67ca4308cfab884807713b1c4d94a589fa0bcf112392c3bde0293fb095182182ee879d573913979108fe4509526589c3346a093f76cee084d2bd04002aa6b0f822c6cad06b23a7a6894d7a7445a6fb05434d2e955171fb4db3d03b1dbbd886f5fa3062409f5607537e696a32950ec0976dca63e3830ebccff82f89f6991d0a41d911c710d5c9f9220a92342673963f6af0cd7261b1dbb13e38e3c7ad96f3e615247e47634c21edbdd2c4ea56fb65b553380ac26356b6365bbf053a51056ad072882bbf02824508afc8b5448a202be48147aac61d1a62df02e1abb671f2250c98fe33897fbf3aedb2d4338b830226ba19fc05cdfe153640011b2b3eeb78b78250e9500b4e4026d44557646c44a0418de667895b7c33b9f738e9d24f670e011aacccb88ecb4181ba96abb507bc16ad9a3c62860d9ec5c8984628718e7805a9f98821a840403430e27405c1ee356f4d1ecf0f0b717c7ae71566e65af6ce5adb495aff4955ac9955e395492ce0b7a542f9296137c505f2c9517fca856242527f0a8be482a57f041bdb8745ef01cfaba081a37a4bb86dce542cd9e1360919d86bb1794c16bbd798d3f9d82e89f63bd5061b566169daff39aa09a573d56469fa3b047057904b1f6485d8e0105aa4acc8652187c38a45fdb2ddf41096e245cba71daaf83b6c9945c48d6d693df915bb64418f69a5a4284a21033383614a59aebb355804747d768548769e307f26c6bec75a6cd29d9ef80810e247001021d2c246529a9b4724a545427d1c412495cc2cd12494ce2c46e4c2cd1846e242ae10d09bea9d81d45fa68379e4c0a239a4f9230746ca86d2db6d176e4efa938369c1aa7941552dda04133c7f103c1d1d9945d9162c70ce33bf5be6755d7fb64153b770c9510590c02ce85f8ee6586629248059887adca10a2ef0911e1c2380b320f232ce8db1e02816c849c4d7d42203d4a126bf966df166b95c0303c591aed6b8ba393b592cfbe8be9c595d85b79f065901f2abc801dfcd83afc4d416ac7f16f19a18af7ea971900efcc73427964a62733b78be9f1ad261ad2afb5dfdce3b44efe67d04c7fa1507d76ddffe0d5afcf452355aba0239da99398e9ba510c5773f0a520685bcb749b9921442392193237ff584b4113f4d87c935fd1ff7d1089f646d7e66b7c3b30bd16304bb870edcc17f5518105b44f8a9c8fa5b8dcaf0d2fdb5b6347d37a02dbaa93828e403fc240666f848adffbbccc9b31bf69f4f5809a5affd59f8ab43f547727ec094ed24d60d4e1c0ab4b5df591bb943c7d42d9250c295cc022bac61d7269e656cbe5da52cc8169a3885fde8fb1893a533da41f77b937a0442dd99d44e37b0c8cc410471c57ea7f6b9c0158184c19c358ac873a3a5dd2a4b528603d01066e14c21a3962608d833b0311914399cb615f59144ac09e09b24ba204a266654d599628b94e657ef796d86af2e46ef0faed6c1a77d735c6d00d6719c217ce6640257e33a6a409ff1a1a90dac52adfc2094dd0412b68f7ca1fe25924e3a007223545df666a024ec82970c330a8185abf20704067bac548fb4eb389a85b23950d2f6c2774ed29107b8261a7d83439b320b0cd9b431d3b960385ac684259776266ef202d40387121269e587a205546a1a20a86f9f6a72f797b9d8332e3cfe42166e64e1d8449189e3b89b62e0e3c8746ce695e7f84984287e92eeb8fe2402a9ee688fa518c03749c5b0f1366412f24b55cc49cf287a87b5be58e106638d343e73f9f527ee715a76f8882b87967caa8ea0a4533e8eba7160a8b2273d21bb5bc4171ccea13627973ed35197b6d4c2e9d0c998bf3e43ed9d3685fde2488c43275ede05817f20c4fc2fbab44fedb78b3b161d965d1cab026a6fb7e06e7bd81c015000476d08a1b754ce07f4e320a5a193aadbd4954d346e7133be821869b395d2f8110e84216c5030fab32557bce53b3c790b0c4b821f7705997d83633985bdcc34618d7273a6e397b780257c1d5f998e4cc149673f36d80067a114c0c1fd0c5771299ada09ae78868fb239e098606e19c44d003fab3a220291276e686e5bcb4029834035519e7f70b0583ce30a0b1218fb0f27ef9646439c33fb91bb11369962e48bd30b678d0325e3eff6733d6bfd6a3c186ca41f4eb5f0cf4b3f4207a1e3c327609807c5d268d2825b4b623c98d4687f140f7593c440579cad401cfa8017678fe067ad888ffc753f64257295baf081b33dfe2818efbbfe3593c38196a2b88b8e281f95f1c8c4fb13a8b87ee487fa81b7263c3d8b24c19877888f214e3c19d66f140f7f8e221eea944e97039e9382d1e4e15596e330c46ac36e9b8e2f03177e6afab2d1c363e05553c48cd56443cd00f203cc4ccf444fb679aa3ca4c6e5e6c2f203a502b69870fa74bd92659b84910fd4a02ba2c10d9326f2d77f0a0d69a4c8bbb48cc2a71c04a0bd9f04166b21168196833ea32dc658e7e19f8b729b129268b5e178c1083eea51bf458a7ab56bf0cd967faeb25b21f2a544fe364f3179cf221e4b81cfa9701ab062cb53eeabf0bfd896065acdcd7b2cff8bf0ca09b061b4ac7586ea8c37ac8ff31a992fe32a0ee32c07963553d4433261bfdee763f0761b6d4c65fb5ebf4bf0828d394fe5f0586bd7c5c2dbf86e701989cd640a96bf1bf0c9ac49cdef0c43d767466378a64a598b3b8eccf621eb7a3624fc25408f41832191135d8ba9b5e3a12b1acadb1203839b28291502ae520b211c2d0bcd10a33bf558bebc6babaa525eb3c2e0201ff9715260b4fd80182743a6dd2db727d41e9d023b908600f390a22868608c4389ef472c551a4899064ffdf356e6e0c96147be40f86e6d37c2162bd553c96d054d41c7028b66030168a4e6d391612325a3f857bb7007edb358a09f548e4d56f575761694dcaddd8034241d69d8fe8ae78c301650704286cce64d98f90428646ae89f72b0a25ed88236eecab45b6a6839bdb5b94e2842845a5928b6782a18e89a1806fa2531eb546933ea5d7490990b48dd9fc3399df984a1baa119444b457acbe6b19c6307d33e0c0fe679803c95a80845971d11055d0d11f8476b999190500f0f59308731a8c6a2943ec1abfc10bd266da3adf7d4215e080436c0fd63d243f7a11db8d0dcc92556bb3db322daa75c8ab676f656563da87f4c9a2fdf52b0fc63948eb0ca8ea982d25bd21b31b743910bf9e4f0c600320ba3c8592fb79ee6b5157db8ccd4dbdb47333e6ae9d1e9f8f7a0c45fa6e8b36e4fbe39c89cb2f0267dd2030f2f18a9fe9574787573a0963197d8d08f8c82c2388e73461b901b1141be0207251643cd61996aa745f456f44524e834fdbfec828392e8c403160cbc2ecef976904767cfd40f70a7454cb1319fc16c9952c1b67b32f5974015e59a1485c9b02b334d7dd9fa68dd1a4c1344204429f14557ba5bb9f580eaa4be09f0e9d063f7d5828bf2108562c792e743c6544a3f9160294600879cbc2ec57480bd3dc65c272d20fac5306d3e4c111f2012c4771859c2119fd0a592e66d786849071c11bc03b051613f2d8e232b899747f1a3311e1577dc9ebc5ad4392dd952263d7f1084dfb545b322709e951cc8e98c29646dde42c9fa7082dbd250d8977ba5a0c15928eef26e9b421b01af2b9c951edf959bc0cb739b17f42295c631280c26aa97347198c7a00817be5709ecd0530c11f49225e72fb8ef1774061b2425886a57fcdf3f7091d838ce7484a5e69425c00791a6f7d06f56e1b6adf7810369511b8c20121afedb97d2731bab96069ca9c702cb37a8bd131f34d0dc788066da75340faf532e0c41630345dde48c3663e175d44f9b6b82456e89c874cb15a66d932450b088f9db9032baf8797947fed19745051cfee21b596d4c15b6fe4e9988a1ef7cea19f4db09fca9104445d572cf820fac4213acf6e83320b2df7825be265b97d328406786442284609f4171c208061888d3b3111a86bba08c1f07728e43cdb21a2c7a7aec2e87570f59b6b9cd06d3ab2e47b6e8427c704b6c158f1046c45309a013ce538309cec7f68cbc3b7c85809367c5ed17ea9d989baaae03658d2112fdc0c4e499d00e0aec07f081a7973b965bf43b087a274f946775b2826b116044a649e3871436549a1c581e991169b37e7b0ad260f8159d4134cd179e3146bd315e5950667983444c97bc371a080c8416cbc4c2951c194ffb02eb2a1f5a2a10094ef47e7cc4550866c562ca6e8dfa21226cc56ef221b7c57524d2889e5e42601b10ba5f0146b9d9b47a093d38f1dd440037a52094e193047de6a039d52a279876607ae3189f234df8a0ca84a9acf365d91c73771b371839f3590974337bb8aa0b0af62189be67ff67109d0736155b121399a302e69e7224103a6fced81c4f704021a9959b40fb6b7e624667581a21e6ca130d59fab288cd1cc34676397939eedfd1c17213d7bdbaee4ea5b68528fc47afc7ef141b116805dc12cfd7bc93c86844644b748944bdeb21251f6f6f291b4713aa3bb061af0919e05474aaa2353e20c62d95fec18114711921ecd6d0ad1f14008a5196e659707c830fe3b76bb12c2fb6bca11dd3f8bf984eb2b9807160f9b11abda2d28acafdd1ebedeaefec0f67b7bccd101cccfeaab47335ca2d4f35d11447c2e382d86a76b4f4d3019248d11a6a88394dcc35c57ca08603036e4b755739fcd1f69544256586a476f7597a1f303516eaa4a19fc8de1400172990a6f51ec05c62307b83d516d6df37b5024fad04589114790c01c970c0a9e44749b702716284442a755cf332c968548041456adc00171e296059e6af4864b0c99974f8beaf2416eccb8087a5a97c12b0e57f6664e746083f7dbff010f10040e3af86a1c8452fb08812b0c3555524c16661e3c492a772cc35ee2cd077a07c791d1584de3ef4d9c6775dc920ca2d33eabbd3ad2d8b0806563bfa4a441f754ed6b171d294ecd73ffd604c42bd081969304e8e9f77b058ead28944f460059a6a00f86921e25da1e02f0863aa48fdcc7b08092709fa0e2c9cf9852120accc24e10c86d0be5d9114af3b88e1de09e075aff70fd661baf5fcf83f35c0e8876cded517d8ddbd65a2d45b0e684a33fa330ffd6e7b01123c368907093706ce33f2b5580cc2ee569982e21d20ed66f7708fb8983dc045e028c586a41cc65300fb543f8208f56c0a5b0d9285208ec067e5357277bf02f9cc6dec79ae2bafb9873777b674f9ee6fe3c7ff7b5aaa11bd38dc8b14abfb857c4608e08b9f5afc1d04b47e8d0fc2e170eb7b46081d3fe8f2bf883514480e8e2b217c6fa85303968efe9be32c8c928978a3b5edc7379db7917dcf5f9f07b06745637a8bc0b410396fd8046d5cf1118f241d5651f242bf2e03b76876f4f921394f0b22d38a6b0490c8c5e19b7867abf4193e226ab65d38a4a78827b5448e0491d671310b0b802755c00d08aab2c72e7fc37eae7106eabac07a23c2cd1f5aa41cbf4c1f8d83a0a9e8e3f0f400b052423051e66859612581038ea8a730375100d10c2385e9253301728ef861cd10505e8760db0689c5d559fce17433d1c2c5846e026319a6c17cbb72ddc26a5f109095150b85fe8fa383e7614ee69b2f93920dcdc29f1d453de81f86fffbb89109902e3124a1443113f3623b69e9ddd6ec769c53c3b9f9e9e233e58a7fbf939837c76403b1ab5edcce2d5b4055776a3bcbcf268de8a4d1b8fdae6cdb3d7bca7f0e838f3b9c9044029ba408b6a429925275a58c08c256e033060831520d1101d38c18600587ea83901a2811d9cd08048c60a149ece3ccdc880042e1c4006009cd8c1c1835d019a50c2070798780048c13ed1038f8b85289c9496e842043948100122022021cb081be801870f72a89122e601018ca1a58410c6f8082a450122cb19060820082264ec20877408e13419a3822ee8870806da121422c0c584211e2b5876b872000ed8b8e2a7c18df3849b0d2bce44a122045f7a38d0d8ac408217f266c98b6bd73303093a44bea831fd9cb0830752a02163050aadd1046e8011c2045e3065c500aeb478c48003952d7e514d5861024aa962843b92a10ad305ac3400052bf01805641e42bddc3dac2263842940c85055c852810d167e80590115ba45982f7c4cf8718008ad2c6e4838685103fa834495175c6c504546110baaaaaac61023c40cf1c8dd61a468e416d4fd99778b8b60301d770f9d14733829cadcfd6fefee32778fb93bece52a121ae12c9a02f7e7589cc3e164e8ee4ec3c9d08b7b8aa1f42faf2b30610e32c4e1ee399c0c3770771b4e8645dc9748e55345d7bc5574afb653ff78cc0c95f31bc57935a7afe4b3eecfb4d5bdfd39eefee37e4170f71d4ede27dc7daf36530d6e576e530cf5b97535cabd8a5fd59fcbc101e2354a6d6f37366d779692fb33a72936ddfddd439dbbeb70f202f76a637fbb96bb5f67f9aad735bf8affcceadab27965d5fccb29f0873e8be2957d9e8ab22cfead7af71807bf3ab83b0f279dcadd699c7415b8fb0d275d8a33a728defdce6c50f7bfd3af6a94c51f5cf52ffff66a0b65eedec3499772f799930e83bb47e974b76628fd41163531109b7bb599190a1785571e73dd3ca64e53cc135cf1aa4353775f89a3bb871a74f1c011476cf0ca4105440e1264b22c8d6089a6dd068c68214b8d7068580895a911e5b2bd40862d94d214747809d57210e053a6c68e00d844e06e72a381cfcb0939b83a00fb0226e6545353a58b30185688c584b4204b1612c2179f84fbe367c9d2c5cb13ad9a5ebf7a12b2a0f044ab5a1612580fab797922f15f35125cb5d5b76a24b06aab9af8fe24b0ae1a131206d1a579f13276f132d6500003858bc2d8c54b104f1adc4761ac05f1a44129be535589096101197e2ce6b4fa715573df61126e11ef9398c7629ebaba94c025fc7014ef3d22e65b684ed7d77ad79358bec6711c5fdf728dadd7288aad96f8f7e5aa3989e3388ea2186661bd90583f66810a59f1572b7195c5e5fad0c8d51a694eab9ad3f8ac5ff9c2bf4f258bf694a51c7de17d5f1911f37bef8ae60b5ff4b93ef67a5fd556767c560d2a7cd6af46da938ed289f5b0da5396f2f521ed4947c9aab1e2d12d5d62f9aaf9c6bfb5a72c65abe6c47af19988b5272ffd494d19861270b9102f56b277a2180d2da4a111439a3046a3b171d27a9653abd643307c56ad87a0f81a9437460b695e2e51f8305a172f9708d645f6b1d7ab8b4c0becefaba6258826d8dfd7127b1ad996a929182d263a0dcd0cda250ac3d1e5445e9e28f62e5a972bbbb529b18b0935555555555a48103f569b12bb7879a2fb21cd0734342d24883fa336257ed881b814be68315a975848a319698c6c3a189fe63f105ff62e5a9757952d4434b5fb5a60329a96d85ff24563fd2512f202f2c97b0886df4370fcf12fcd52f897c6a683f1c36f5a1adf6996c4174396025a3d36847afc4bb672b9441b56ebcb97f2f5a2ac26e6b196cc69905ebf62d15c7f691fe2d88891b077b1d6a79a92f5b57ec6b368927c5ba066cc78d56c5838ac98af557bf95a4e5a2d279a97fd8a46b369fd8c8f893216cdb76831a7f1d158ec58b26abe4bf335fffa9a964ff6afda8c9ff138e18c1aeb65350f7162359f4dcbf7eb58fabec7d5e1a5ef8f64367ab86c5eafda139c92f55fc3f1a75bea94813511f390891660289d0916c230b1c2184cb82003132d842144055b28a104104028a104132b30b102132a8cee37764c01062b4a7fa2f4147428c567a5408152fc9b8211a5f8b156eb0635b6688c6cc61acd9dc6ffd25aefb427b7bcfeecb8a5f477f193925a35df96b01c5bb4d5dfd6ea5e9bcb7a712526d124f9b6d88c3a462c1b9bfb2c2736b775754ad74813e5be28feaa8c96bfde45775fad602fd6603b3a2e27318a1730a5f82c2f5fc21087f536ab67f974cad127d65ee4a175b378a2e6fe380529be3bad5ec815e4f8ae5b4d09abbd6aae24d6b3ee0d0388527cf151a0a16cbd0b85154a2f5d9f45f9a374265a1853d6b0b7f4b55a2f9b7b5b57f6faf1e58abd6cf52d59cba6e56ac95a2edf69d57ce24b841acb3bd66c6ecdf5364f70ca96d1f5d93cc1295d4ed777dfc64583e4aa3dc15989e2bbceadd9b47c38e514e4538d3fab053a21904f7c9d1f638bd24b2f45c092c1de378eb746def7b7b91fbbe385d15ef6b736a3a6841a4bd7b363b9eae1d2a12336ae8c66dcd6f1a1c6d27d87a666b3fad7dbc0de6336abd5af5e35a86b037bfdcb25f3c1583fc2c61f698cc6717c9cf2f5af1ace6833fe1dc71f47239adb2ac0093494aee3a5cdb5b9b52738e5adf9c2b7698536ad9ac86239b19cacde35d66ee9d4f2ed2d6568f304a7bc7f9faed30d6b3aae2baa70c20a65ad7426ba6451c66afcb68197e2a58d4f35e593ef758d5a2d1bd6df6ffd8a66c3fa154ee9fa97ab36ea6cb119ff5e9c12a7a4116b3a21ce0dadd0d161a24b144c74a1a1851644d66ab55281cdeafd7df5abda139c72ace1946329eaf88e17e1aedf5286344e9c6cf1bfbf81d76ca0c69a188bb262b96a48ad1fbf5573628d352731ac5d32a4a0a1d181a2cb0aa54b0a51c4f131c4b9ef3838218e0e06c217c3174d80a10c5fd79f5c05d48c2190d37bb84abaa102621e0239893f5ebfad5e7caa117d378b27ac28c72762387ef8a31031910246e850020ce3b74ae852b69ef5e3bbb8aca0743d4b0cc5500cc5505cd920e5945eeaf010a205f9a4538a1fe3b18ee38b301a23160d94d1f52b1a24d6b756a2288ae218fe7d71a4410a6bfe2ea49cf2d69c58effa90e663bdd33c19dff52c1a24d613540875cb95d3aae63ac1d31d6de8701cf15750e37dc0180b999882a174325561ca18a374c9b228bf7426a6c69431f1d5720579bd5e1c6c09128466e3aa4561b19c587f53ae6ace81d5ea5dabd5285220046a8d717db81ac7d6af5aaebfae5a585e31f75f3edc7fb55aefba654896ef25b6584eafbfcfa2794283f4aa8d2bd88bb5a285e56db158bf62b17ea504103e88dfda424ceb5d3e88bf2a2303e5ea59356cd4a84183ec71a9ca56cd272b5befb518ad4b10bed5873117edce4892e689f8acafa1415ad5460f715cbed6b77ca3cb74dcf428396acaa5525dbed01026043222040acb314748230342f67285404eac5ff95c1fbecf8693eb5bac0ffffe934ba60c11d020b9c810c785c4aaad6ae1285e0f57332e950d315fec96ee24e6618c45860caad2c99041041932ba94ab556dbc91e1df522cc36712f2100b77c0c109719ccc0bce94b7d2c9bcc0889289a924ca59e94c4ce950aef7c59a6f4be9b311df3796ceb2116b4f6e5cec58ded8288a2e2150903714fdc59b72f5ad2737a53b0364b11a272c969398c3de86e6d2fc69469a24d978c5da13d63b4e58454c7c810ea5eb634e8468800cbf6673bfe537a5ecc3778d342736b2871a6da0c6d2fd614f3449b11ad4873451c6f71ad4fdd58f3462cdc91d5d3765cb8855c309717c1469211431718c52fc914c185b94a16ff54ead4722cb1d7eec6361eb57cfd28c35a79a532c2c9de68bfdfd300c1194e28f61b0be258615e54ab67aaa29b7fb8e2fe63b62940f57230dea425dd7c79c6683d8cb1f6a2cef3fd5f86e14d6b76a50b78cd57ce28f1fd24459fdad415d2f9dc655ab398dcfde92b5aa3dddf20976c7205a90440b6462f8770c6b5a56ad67b5c69a96b016c40759c60fc3701c572b164b14efbdd7c95a172f1a10dd47e1128935142ed17d2186230669798daf257cd8ff4bf64134bdb4b86a3a9e8cc96a5a5e5a5cb58fd5b4c06a2e31e79505f6222d4b965a9691f5b556cd86ca1344b05a99a92922d64bcc711db4d8a5a1e27291248b55d37169a88c4d636d6545fcb0868323e69010bb6311b1f057ac9a96564bfc505cadc4777d4bac6959d582f860f5a24b14572b16abd50a431c31e73e8e98e33fd6b2bcaba543078b24c755e8aa657189b52c3527a1754726788c97d56ab59ee52ed14c6d158aa28b9f42cc6f7ce2288654ee666abe9e32a412c31f58405523b668b7144201d286032fef0df205e9ae137acd55938538b53048176448c3042630012a2a77ef29fdb631bff922092f642863d761a5182405a4eff532d31245166b6a05469c12424694892c1146253303a392bd4f6666155291e3fb6054669e8728d6ce5caafb85e6f5c40ac238eb572e166be51a3d20c51acb8c38e502434506c982bc6178592a189d3943454583f2c2041a5d845d6041a68b0e8c5d8c5991e92209325dfc00a474325dac208405d215e2ace0c3992fa597b9f169689cdcffd2e5923d19dff74e5853359facbcb1dad10048df7dd62a2b63ac188f2b8a2b887958850c37be9b292a2722b7c78c0f31b099faf13c3c207fbc0f878e8e0f30ae172c267b1f40682f840c497eedc666ca07846686aabc356ee4d0c1e36d5ec81832888f173286fcf1405ec818f29ff642c6903a5f7b2163480cfe064790f7f90023e48530800c710c7916cd97c1b378683befb3b333ae688c769e89b8b3f2f99de7a1852f8abfe2f9d60ecd77cb1d9edfd9a13955e943bb250fcda94a91e66512319ef7f1d9f94be36487a7e7431aeb7d6819bc48e3791bcdcb9d9a6f8b9772e7439a900490e1fbfc1563ac08533acd063eacdfa165f03c829c80f4d1f4d478785834463ca26f4b0f8de72f114fcdb7c528b43dcf8bb4f07768b78cad529c0885209e34282fd1ce87345160220fcd774b9e6722320979685eeebccda9ca1d1a111c54545565ceeb50955eb6be8a311910a9d98cac6722e6d0687086d07c345f082608cd67034c79ff86e6dbf1a5bc8f01cdd753a3f976d068be1d67cafb4ff3dd2f40683e1e602e958ffb3d7050f97a7c36ca94f777d07c6ee6c654793f07cd77bfe0a0f96e10515e2a1b34df0d33e5fd1a3433e57d1a341f8e2fbe1eb266068dcd548f9bf9f2f2f90073cfdc6fe1a02aafeb86e7ceb06287592ccca18a2e3b44081182c4010488cd171b1b9b2f386e80dc7207cc0e1021906f47861d303b3a84403b30ec84d9f9b2f305c8cd979b9b2f40cac4845480f4dd7cb9a12104f201f97203430804e4891b1942a05b03520647ded2c95001a6f40129538a44ce94e207f982f4e1d8e0c009b20519963e1c3884207d34bebcbc884f3346cce182f48d63c6bcbc88ef8221c4116bbe9475bb94d74b794928c7bf2b164e191e41ba20852441fa68809429538a6fa6f4d2e766be1853fa68c694e203291302ad6a365f9e0881c69a8d0c40a20881c6527c212f90be1b9b2fa5f837610819e30b3295c5971d226460c4fc268817a4f79441c6a0e24b9941e964a6b8a2741aa39c28d6c4fbc3cb3563051b5f5cd17878407a4d880b487f91e60a6bad3014c72f67ccf49433686431d8cbd55a05418eb5500c82bc1fd6c2d714794711c8603b6230594c2683c17e47cdd71383c96232194ca6c1efb0f9bb23f65acd90c15cb1d76a860ce662edb82e1749de7befbdf7de5618471b38316af22c598f67fd904f0f8f6d47270322393835da03f9f1b1d6191e3c74e8c89103078e1b373c8aa39acfa3f0321bd47c5e2647cd49cde7638c6a3ea7ba516b52f3091511954c902091c95831301a0cc5c0c86236b516d5929a0b8cab8c922437a120a023468afcd47c382e30383e2e30af83a7e6d361dba9d1a1891179b5705a54438404b9c1a056a33d901fad33327cf132d53ad30aa30c5b54ad33ad16d01121233f3d2d22321fb51d2e93c97aec90c96453b9ffd1232777bf38909a0f9b1a0f99e77017a79ee663fffa5606bf7a24345f8f28140474c42754a4f6537bf9d45e3cb5d78e38f5caa04664a4aad5fe6bffa3f63d6a39359f533d4ecd2734a4e67309a9f9c820359f8e9b9aef31a885b59a0fc78753f3bdef6b3e6a359b9a4b28476d8c97f1286ad468d46635b25653f3098169c1c0845186ac9acf1545193e93704a0c0a5fad952b077943478dd97b8d19d9d44b26fb21f321eb21b391f190e990e190d990d19091b218ec158bfd88f988f588d9c478c474c470c46cc468c448d899f0615438517c191d6376bccfa976bc4fc8a5c3f7385a9066fc0c990c06e300ca27bd96209a9e3c07cfc13b51522d0b949a9627350e6a3654906a2c3365ca1daf17116290ab4c8e5aed46cd47d6a8f974cc5e53644dcd87e351781465bccc181718a7022354bac0c8cc6c1036f9124e952c3360ca1da593314384114bfc9af7d0ddc91088f5364220d6f708816a17881b341bb44bc38521c4613d0ddafd12e2b066cfe359cfe46531d80f14c8fb9745ba0d8d8998c31212eae9c1c1f9d7a1832467cc7819f92ece7e9c40febb66351fe9359f0ed57facf15aea4f7ecc6968fc8c0675cbd93b2df5f7a7417b2296b3276929f9364fb79cfd2dbde69d1696a4d7d06e796b7070c4201eb52316bc303912839c6847ed880530b29a4f47ace62361359feb7a61f9be64f9de4c1625cbcf94ac304ad67b4cc21fe18fd087e8a2df9acb0ac34b3392e218ee08c3f0b2eeb8bbaf9b1b5b8577756704d98448add82bbc3a97ac15de0eaeeb5e31bce33dba3e4224618f3016b6c2d5156f78c37bbb3ec258d81aafb812ef0dbae2c5b9af2bbbf726b93f4298d8baaef186f7deee65e1086ddc31bcabf1220963570c7f5c510cc3311c2f9230bc3c42d87d5dd718de7b6fd747086b8977748df7dea0bf3a88b8948819acae785f77d4715faf5b0c049b1f1783f186212b6ce54bdee8891ddd1bdef0debbe32d07fa075bee190f52e0efc10d5994f18066c516553015424c99c28c37d1a470a61b389099f0065cca0d4ad42085a6a3747083efcc1236c204ca0d3fbe38c23d7a90dca08307099ef143d01434374134d8059ae2920194abb201e8cb705b4e8c1f3bb930a18e191eb4cef710c9008c8e0a80e45e06e473d0703035b0786ef582ff6882bbe8ee4e850325134d997e7a75125c9bdc7d8aeb4668afb811235e80d8dc79974f5387910805011d3152e4c7a7e78afb1269a467af36cce213475323da884b4c8d2c4c8d505844feb658c085e4616ab592b58448c165bcc167cc21d3e1075600a646282f1cb01e3a4c987a79307e20fe88b12c99cb894b88051393e880ad3030e54a62cac6152eaf20b1062f6425985a554dad643c3e3025636b4b4cbd74cc085b49563c2b18bcdc88ed88a1847185a91a5a0d4ddc99f1b874ccaa4a5865e0126a29116b44252e1da6c4241f1e168c05c4941863f11875623ee21d65a30eccc92a8396072c1e2c18cb27b321b3f1d2f1c2d1f24024a304160f9188125663a682f8c6dfe382f8186fb21ad66cbce28e58342ec972b0c25695964d4b697563c665c5c4d528135de20bd663145b535a346a9660b316ab75c5d66864e419af6b8768c3e50564f5c3d40c20b8cc58a38dab490963094aa8e1deabca300108a2e0401932635a1ed30b5f8c88345812a45515a480670859e36a81f04114a5aa269e7002880b3d32c4c0e400258062031780001243f4c063135280967561af2ce8e28c083cf044058ed8424208573eb01245690327407ee4c0a18403b60c2104095a703e7a6815c88c1290e0e9375555460bc0b802420e1c33282e6081084000890514e1420b29e042f0a04a1425a40d3461010d3c4086a31048b1810b5400024bf0a0830a2798d0c519283c00812594484097a97c2219cd68ce5c614509c854c0010d30c2871758c8272e842b205441324a72cb99d16ca0093235ccf042977c86c0019213a3247e85151b6882090b38a0014614e183005ae8828503a425496e41467c7268cc46afbac20a123041c602157040038af041002fb4c042172a7058ae68c0631b430c284f6a668ca0c7971554b052a507071654084a05f1aa12cb08c9588911bec0f5c32826f421ece1fe843e62cfb5c576c40cc4c70124ec716d5a3cc41d57c798e3e278d5a0414324c71ad90c9286876c47ac076cf66ab958ad90c55aadc4ebac2eb02018986abd5aaf71860ba8f5127dc89ab828d55c382c984b6894d5dc51c6c28d51228d381ba330e10053ac1961eb35ead0f81012620a4645768e3a33b884466c5653c6cb522281a925509acc4c0847d80825a70553589440a40c97bbbacd8030e1128e3cc41de0a50dad32f0c0148b0564a9cb8d1aa213c40db800606ac7ab8acc86cc86084e6e005d1b06c69412f6b5414e0064eb25d2ac9cc61456e0e21a67b07cdc9a225c4c80c38bc807bab8c2886146185b3199b5908c4f3208449450638503730213c2ba32126921298101376c5082181770903d5d684c2801a0c054acddcd6002cd4fcda5d2434cd2da01c405d642c282b9668841481b883e604ec09882b59068985c4736464e60af2319d3a984015339461d2018eb568386058b19d542e0610ae63afaa153616a2592abcbea80055b25611119228ab5af21ae2316acb583a6c9eb66547ae5b0d143080f1c445c193c9122840bcc656b15a1291284b4d4928d2d1eab9b08f4928d439e49ebd5828d3cab27a31093bbb211692b1c3d2f972922314aa2c305c3f10ae252a41433e2c2196fc61b9698292233c626ab0c563f5e37638f1a1fd1470953369e2beec49d515c8d500b61004b68750821ca50113184181d02b0458bd476da653317c2950fac5099c2b4048583a39b50111c214130f8d10215a852c11626d06028488dc78e16180d64a0071e3a30d2e0e6e3862cc6a280120d08224c0f3ae47003981aa6e00ce1b1cd708026a6aa3156a0fb40052980004ecf04e002ba1b4a026b8579e14bd00b0451f810e6860428878d18ccd5ea6183c402aee4338a1207484e8c6e467c7268cc66d08c36b4900200689839f280218410b3a50a9520201d36460e94f982cad012a0578b55040a0d48000e0518800a2320c0061b5c88a234946488ec8a21be200c633cf32908b3184d104eb13273a508a1083ff0f280ec899003e1064225c222c42142a7d1776f2e10978f550f9a1d618ef0c6b531abb1a211ce2e39ce08632b58f872b9c2566b158e57bc97e5301ea42d754f759855d115c8934f6da26aab83bb6fe03ef66c3d403e403e478e20e9092a12d463c44810901123423e3e1b31d8fbb78f90c0565dc4c1f769e295d5b5cec40e0dec8e94bb4765f57653db26afa27fc51d28bed3c37da3af73021d2c489d28dc73cf53d1dc8bfa9c5fa2e47196287915e5659d8e181d1c489d2ca4ce53ee77fa731ee73f9fe856d1dfe8b3e82709ae78c53df331fa6c5087c566b381709ace3ab2a2525b1191196ce17bb5a96b66590ce459d9eda318facdc049223e10814145d99dcfcfc1c9e6daea4d048a48ce4f35b3f8374984c9a96622b29c17b8a7269aee5ff56ec5ecfecc53f19a83f3b2f1b2c9edcae69de25dcb0242c6b8bb91ab498ad23d01129f1ffa756d4d224890082109420284e4081223488a20f941e283a40709122121a1202120a1234246848a08fd08f908f5082109120a0a0a020a3a126424a848d04f904f504f10122021a0202020a0234046808a00fd00f900f5002139227424e808d09123478c1c2972e4e788cf919e23488c081909320264e4881123468a18f931e263a4c70892224245828a00153952c4489122457e8af814e92982e447e827e807e8e7c88f919f223f3f3f3e3f3d3f487c847c827c807c8ef818f129e2f3e3e3e3d3e383a447a827a807a8e7488f919e223d3f3d3e3d3d3d523d2889a37fad8a05a603c3c1d1f25a603ab011fe757e840ea228e1e8fc0dc7f5b71c1643a418336408cfe5fe06d3c9e5fec6ee72fa0fcbe5b03d1ebafeb3bb9ce2e4feb6f1ce3d93c5bf25b4254b864c914372eebf51b6d5fd6f34f758774f8232648992d71932e4598caa1838c4060837b64d8ad2ebaa898454e1ee361bfbdbe5559b428270f796934278109273cf79254ade07e733b005759f4f140a3d81bfccb2d8dd5f559c0ce20276d53a9c85bb5f09b2c213ee5263dc7b94706789705fafe8e0ae5e092283bbd782ace05ed3b9fb909271368a496eee8ec4dd85dc81dcfdc8a5e1a647de94216f9ef8db3ed556eb7ea79f6df56b16b744c97b911edb119bcfcece6c5017ee80810480f8f753aa30ad7ab7a36a55f719bc1411fc197c9110a47ec9ff8410fc9dfe2aafa2dff34b68af7b228f7fe7156d9ff2fa53aa30fd99d11d9a027f28d32ec8c99a156ed36b16e1f38a7fd58f7fcd3d16cd6ad417b216c6773bfdac8a6ae0632010ffbe8ddada9dc53df6cfec64e69e99ab6c77d74ed2a668a376139b91ad83b7652894c5269a0251366d7fbfd13f57cccbe9e7dd6233eb3eb727baf3fa8f7fa3ef23c57b225f36197de0bab183a2a58c918604393c96945a4c7500041d6061b232d40a6acab00330eac0cd89acabae90c285aa170000055555552e987265c9bdc9dd7f4e024182c7dd6d3b241023406cee6e731288bb4d9b9a7d9eaad3ac7b56abbfb33ebf67a2bda87fcdaae8ff4d45f14f9fbf59f477fa57cde235ab99b781f8f74135f3f4463ff758773f7277f325c6520a647a7210e3c51549a8400327c40e6a4c2942e70512172f382c010210cec4841c0bf0400052e83dd16e430c208713848a14a003cb26ea0550704007ec862d67c62552c06488105850e540b045836019588490c7105099006d440a260da1c50f054c4146490d65d4600647d40ab0acf07a6106d3162d2cb45b0cb1f3c10e25b859418b214330b01b2ac38684a2b41785184d902138222548b7817ce40293c78dd428c8171b0d4ca500cb141e6aecaa845dfcd8b029aac989b23e5121000540d6131f34725054091981c4a39314812730805c0082bb03491f3ab82e8a6703f14fc76d2cc186e00529a87284e446430398b0616702527851c2931400bc6e40c08b8d0200c482f8608aa12a07145ed5069892aaaaaa2820068b15510d33865e7041ca6c0a981852416ca99142173235c094f1f2a187a1108ed03b708085011b6e82730befd915e1eeb7aa0acca80204351e00841860c02cb03da0456036830374da1c9478a0000721f0d0c2619270f080a2060b30b08202321a808588aa2a3292841570172d00664929c5ac03269e86c67020a8dc6c0a0444c08731ad07a8d828a979a005d0cc85114e70e42688d15e5d55d50ba03e2881fb2c4b082a8e982f4d43c0a0a2e20ccb3b7b4c213300103e90c146a60365bcb0c0191e0b54650851516488114174003ee0061ede1757724431125233e5020a2ecd0aa09410860b163d583e985d984c5459012e8f00dc081ac11822f0562f38850a152b3162c400d142505844062890534565080e68148052e540a48b23200eb490e644071f1c800328a4332ce184b681aa161c4921260c142e9832650a530f56a6a50651ec35b5259c400212e0c4005a400107117018c1070f1d1ea8a10417b480fb0f5290f49be2840a5042644000f674811e2f18c1728d5985e053010f4ca0c6892a6c08220714f8500503186cf1822cc6297470e0a52aa80052c838a2c78a1524d995a1a2005180d1e50163442d81630b939906b03e496821050b1870b463058802581e1af809c102c268411159380104918f89f28baa538d5595a0f552555515c5142d2297255e5b62464c8199143186183f9a902842ec4182ca07102ca049d0a2090e54153c1041127e80a223ff2650ee3e648a6d04a386223440e28314318611c6e40840440a43e71020bdc2163a9808aa8201a11d09707901c218d8470e5543406e555555357e88a1051eeccf54e804aa1a110ce384321e44719ef0c4982dba882872ac602e943f4c60c76bca1411ba54d102133117e8f17481205cac4c0378612a23881060b0d975808b9329547c99a209d158a4030e471cc9410682a02c2b1421a02a052f1c31a58321bc1023460c0f4259d8a8410a5fc1054912249c09151258515941070ad0225241a9f628420c1e187c0483318440e624101bc40801934d9932c583a709c02e5084ec818c0f1e8003a4811ec2157ea8004d025ebab8008064e47372b22301aa0b74004083aa1160e0139303ee8a2953a640792a5363ca8529af3f415901231c408c291304c6c8453c810d69195d96290d10f9828c271c5823a8d2a2c14232b401184f17e86126045d0e9c1b42386618727726eebec4dd814ede2062afb60cb5b6264eb33820fef1f054b4b7710f29c9c91b47ee4ecbbc15a7af35fb1b7db5dd7d6fe3deeff46711d09d7bec6bf6d916c8b6facfbca23b9fbfd3af5116af9f7b4d4ce40d993b904f083fafe21efb6b4e53fcdb3d96a76628aa0d2eee7ed77caa18289579f69a7b1bf7703b4d4c4d768c3e406292624425ca4ed1cf11086634b003880920f86186556f850e6648d9b2431416dc94a85fac0ee0097182ef8505cf3d989f9868aa6b9756ed248b8032f154bd5601b67ae5e95a26d6ccebaa8b5424562b6516f3f2cae395ba69e7b565b1ca439972dcdd85e399b762285a37ad99975915c9dd878880a6185df3ce4ddc5d4810e7a9e8a94dd508e9c444ee7ee3648d2b18b8d768bee6ddaed57d4e752fead314ddbd669fc79d9d00c9162c0ef0c4a68705143d01028a33ea49972f4ea001015e8f1360c020028c9b1f3f5495510453551503caa8aaaaaa12e3f774779c93b310902410411655d19d8a0e807c81fc92f33a3834de082d822768e2851f90a14a8e0b34f921d6415007427c58d7045e6c0841154cc078c075c96a48104678c2153777b18614221a574210022b82281c58d2811dd0520766a4858012a61e60d80d506d6060b2e4081e5475cc4f099e84603ad44ee02e0ba38604bc3e60d4c112134840420b6476120848fdc05344074858028b06dc40b1810b5a88e0049bdac29401ff19c388c3c40c717759d517ee2ec4fd427177d6c9190ef0bddab28aee7698676fbcb27bcd688ec2b1abee6d33ef38add80b0b7a40e08ba4202c742004b41d3ee09480243b4820288114700a90015d1ac1ca08346aa6545559f1a1aa4a04555baaaaaa3c306506ce6b04244d067caf369e8aaeece6c93a233a23671113e8c88f99714f8f5011a01e233dbb5d915d9010c67927e483773d424047784c76b72b8fbbb78078306d3f8853e0469b3c76f71feeeec37db4717722eea20a234c666688fb0593f79aa154f4d51ed3dd3170bf08707728276344b83babf5fa79c5edd37efc190af79eddedef319be626aa46b79a59a98c335116af276e9fc8980e19cb89f570f79cf689fc67f038679a5127ac94d189db0edc738fbbef602ab8bb8e1c7bb5e9d0f4c7d6b3b39d561de5b4629caa713bd525c0dd8db8df2edc1d75f2a50370c7b3d37ceadf1377677277ece4cb9555ad947b55a32c304afea12b90679f694677a44b0677bfaca92622b48073010f02b080165c7c7102054740e0c38929944cd164081064c0489825402c49a125a6aa0a88a6aaaa146c81405555d5097e7077275b2a70b27502275b2570b2f501275b4d38d99a80932d249c6c05e1640b8c93ad0238d942c1c99616275b4f4eb6724eb63a70b2c5c4c9568f932d9cd52b0673d86b3673edb4d19a7b6c9433b3c0765d33ab22e9357f905974adc2b651cefc439bd816a82239993a253ad35657d9ec46a58c8227ce6b5a6620baa46627bd12e5548753144395ac565acfb3cc7b8a8ab49e677966ada42bd9bc77bbcb52548db26656555d746654cd4d548dee8dfed6bc74aaad76c26a2577c771e35451a79f66775be47edd6b9c2afac4dd69cc34d2e6e1d4c999593303a5b23a05b3b8c989a6184a45ca260632f578ad568d58ada49b8038c9ddc91a77a30cb5b6456766570d04b65b9de2ee34dc032759e466515d6a9ee14b9aa24e51cfaaf917c4409e2672062b7fe61f5667c071b7e59c6411725e7733b8bb4db31fa5d30394f1dc3e537d2485937252f9f799eadeeff41f00cc4e5e8144e401ce9cb667ee99fb79505fde6e4d39af3b5c54560d70c600636c58720600e30600e25a031c19404766b189d3e1d8bc65c8a2c7e27ce2703250b90d4b4e8617a0d014e380393be14e24295bd51be7d5c9d4504e34e775a76611f4bac32b2b65b47938dd913164e16efba17abf4e8642d3bc3f989fc440847b303bbd56f212cc4e5262e8d91e88f6760c46bc09d299598c9a7a6ffc5a6907f393dbad69a7f50a8cc1ddbd002070f76076c2a558ef54a4a6252f404f2e9fdabce50ae072effd8eaa5f8d4200336e2b094005c4512501beb8bba76ae9ee444e12a008094318b9dc7f6ed545b9149b4652d9090943129ea6d88421e7bcb5053e4f45b3ee7f7a55b35eb3083b2a09830eb7fdb4f93baa56f3899a68cb348024f2006018c0922dafad9981fb87d5cfbda61dcc4eba760f05f3937c6a7328989f04b393bbfb70520067044065c392db38b6d5f9d4a60064701b96dcce3c0cc40171eee1d8bc05d0c331c95b003a5c0adde515884b53d4e98530dc8625e7b4e25f5e5f80c28625974f2356cd50396fdc030e350135bb7759ca5e318b9bac46b75b13076ce6ad984d71145ecf1e8b46e158d4871bca799d9f222d8e56519c8afef20fabf9d445ba204ed23b1549ef541477aaa8130e49c969d5516c56d7bc6a1ffee172da9efaf733adb02a8e5a35ca66271365cf9c535dab9b642894b7f50fc7629c14fe9d2afa2403832c4e0affb614fefd4c292743aa468b4029f999566eb71fa8330fdd6e4db833d52d10f159d5680a0406b3d39fa94e1264cfdc3359346a63b9b2667db61b842b9f7b4d2d4c5d01e1cadb9cb4669fddadeff5d9ee2f5b60727db64c9f7b2c93ee454969a234c5e65093925d1428515965e18cebb32d63c3920b66271c9b370b44d8b0e43666614a6d71fa6c370b394f1a6221088e5cc18caf2083bbef14d8360de5bcce118e123633ad00e48e74a23815cca06faa5a49052a1574f013452a3a511c4e8526157adcdd08509b4d55abb800a440b77a65773ec30440ca37f0b7519e8aa6a71a0021df4358f4d59687ee50958732a51086bbef53454a618cfb9959746b603ea57c9457cb14a4dcdd3f2aabd828052beeee4b494c4829f4649e9ad314f74c2f59fce714ed45bd6ae4450977f753a36b06ead4f69f1da2160d2545e934c5455e846c5872ac9ad1d434bdb8dc8625b7dbade6ad6a13d702208bf454710f001030716cde433b9ca198e8a9a243bd8d8f7e99454fad84dbe19c366177e6a127db027b1b1ff5361e3a5af389730f4bcf0f969f229f44a805e12c51d0e23be75d94db8ddd99c5e9edf633a5f24f9b6cab792a1ac440a0233f47bd8d7b278a4b536cb25bff4c2b272ab4ea7535fa995656bdae7928c52d6b62b20b165dbeb0fb87aa686963d13ff3aa55dddbedc6bd2eb50c388b4d68c104a112ac705b2ee7754ea4d7c1c96d28dc6bcf24b934c545b9c764095f4ab871c9226d379b7967fb6b7fa814fe7deeb11be562c5ddb96030d5c49494dbd856b3ecaa5fa73d1fb3e8ebac192aab3c15c5ba0fe2292372cb19bc85882d1f6c6991800449c24902e9b6339f39ab3abd027fa38fd53543695ed2dbd2148dfa60aa43f7d6c0cf3d76b77acd3c159f399afd55a32bf0c479e5a9287e157d9cdfe86fb4d7643b832970a3afe58cdb5835ef520b07b47871dbde40cd2e6d1c857fa78db4e0c842650ba669bbdb3b9b66deb897e54b8f25b308b96deff671fb94d72cee6e33710a5c620420caaa196acd6cabe29f89b2f8a7d916abf87bbc56af3df6b3eef71bfddc6b1ac188bb3b10afecabf987e2d4fc692611c0204500e3b6a8acfebaf2d09f66f57afec651f84fdc2b45d022e8705b9a6253ab1bfdbce670d909b33927cc0637da41b00a32e84250c8dda66a608bb34eb33aafb36616a7ff5b45f1cfa68da4c270f713e7f5836b6e9ffecc506836b314291d6c39b99f69e55433532eb33b8d83d71cf53b6727a45407521d90522e77b7e504759fd555eb709e457f37d9d09e995464e5d532aa8c4dd58f7b516ccf54f14e5e8bc8a81e33eed9c4c0fd3bfd4e264e1fff06e2de9a976c2afa39b66c43713eb77ba3faf34fefbc42256123a1beb8db782a7aaa390a54ce6d6b665374f7502e9baaff7ccd7e6fa3511fcc3d36274d31ceab51fa73af896c93b09d99cd2b30a7badce71e9b7b4d2d02da2377b7f154946df5fa3bfd411cf55a0da6a9cec651675ed1963cb399d932670eaaf8f75a37912cd0c9d4a9fe4f5394dd6beeb167cea6d96eb5fddc6b22592157d1df9ad97c7ed6ed219f7b4d2c496e2a5b4e529195dfe95f815299456d3c5bc6f9ac2377cf9dcdf9b76af6443130e7dd9f4d9cdc3a76fb3fbdee808f7fe3deb3e8675345f7a3b8d744ae65dc556cfe994f144aafc0cfe572af69f5e2eeb6536df5cee4a968eeb14ea64e3fb3fa97d72fd79e55ef78685e3394dee8ab267e3543a1bccfbda67564d5bc3feb5eb3ffb9d73494a6b8686807997ee36f5232b459f48742c12991d244eccee7cfb432349483a3e4675a192a87944869a2e08a5113dd6535eb7e8ab4bfcca2b89c7740228053c04d028f4820690677664dc49a77381f2735d0b30e5dcdad6bb7daeaa553cd5beb7077ff995238366df12e9fb86cae7aa79eb161c96d36ef8d71ecaa8139cdacbbeb70526dc2dd862597d9d6cc52f97663739ae2a51c089f33d39a9d4c9d0afdf8fcf0d096c249af9323f4e3d3bece9ad11dcaaaf9442a3af3a9b69a68aff9d76e559b6b56a3f4508e50fb3a422d4e7087b25a65b18af2d055aff96c75fea16caab52e43a1ac89711b1d8282b2699bb46674cded130e977541fc9372662a79ab1ab8f3eaf363828e4165bd119cec95ee7ebb39993abd4690bc1e6e3eabd5fdbf43ff37fab9d7f44972b855e37267ee6d544577bfe633455ba2bdda36103f30e1803bffb05a74bba91add6b86fa29d20ead7969e3d60c94c2bf550aff869448e15fc6e58d75433f455a2552f8a7d5a65f16a3772a92147663282caa22a9a81313dd2abab25246c114082c37ee2dad7929c6833c9348721e79667756717adde1cc0cdc3c66863a7574c861afb61493260e779bcd3684cda90e8bf3ca7b562bbd6d7f8a71da9a2936792a7aaaed2dcd5039c5dd7a1bf77412d09abde5bd66a8bcfe5e6dbd8d7bd704bf3fd7e7ceb82b12b7e07bb5ad9945713f9c57762bf913fd9d196acd3b4bc97975626ef477bb6d353b69f676db2d8b59a66caa38c5447aa722ad9ac5a78a93529c815152fc5bb5d20ee2a5bdda7878d4f676c3e51f5e77b75bfea1acdaaeb959ae0627867bee6fb967f1bf2ed24a3b7bb0837829e71d6e0731706f9c16ed8d53138725040c60c1c122bba2020856fc021f20e16e250c0fa2bcb7f1d1d09012335361badd32bab238e0c62c3e79fbc7621cabe67de2dc3bc2fd1469a1341027857f4ab82a36dc5514f78a501c6671939e8ad444e9e808d4ed8659dce4ccec46f79a97a83cd13b15294d75d19a7b6c141e4aa47593564ad3d6c98984cbab3671f9a799b2aa959a703e3a71ee39adf8b7661647fd1469d5287de2cebc7736f50a1ccaa72ea2a244d5282e2aab43eb518a59558b80aeba37340577ae380ad89e4842ad123353b9ddcc0c75bb9decc6013766f1d9a414c4663e712b0e977fedce2b50afe74f91f64c756fe7b5c7e2364e817c5a1cbb5774976aad63372a95713bc52a0fddac9a51206ae67528c74906fe4c2bafa384597c6236ff70f9b7e653cd5b0a95ef14c56c6ec2b651ce7caa19b85b26a728a6948a948772cecc3bd15557e2283133958d4371676fe3a3dbedc4b987339f7eb8a7df994540f73af43a27125ef192129e8a6a1edb6a1cf0b7f670c01f0ef86b71296635fb53a4e5eddfd20d282cc9dcc92846b83b6d2fbd9a73149b759f37ceaba9a21f4cbf8b0925b88f53eeee0088326e717712a4dcb38c10c5766696457f3f6d5e0e6c4e26d6e9f5b3ee556ce2ade2957d3503f56ba5ab64e4c4b9b714924961b8af59f76b55565212ce0e5177d6e7f3f0aad98d811fc4e9ce0a4e26f5d8ab8dc7c9d4a90e97596c63f34659542abf518f3dd1a853c54467de53328b4fe094a1330381e8caeedb6fe32825ee2ac89d6a060271ee7b2c1af56b46d315e73ff3ab39eb7e0f7103b83b17279f50c0d5fce3e58d3bf3d9a6a68aaaed0ecfe661d5bcf30afcac331ff85b7b7fe615a7663e59f487a659b8144f6c04dddddd0110358209eeaec5490e82f66a3bd5ec64621d2ea32bfbdbd1eafde1ee1ab8bb0b9084a0f2048893725e9b881c07a4223e8310216cdebb9d5e091122e4970cfda9e655d0e880318811003e168d4463391a2137f8c2ae7af3d4707203d6916a10e1c9dd57279d94c1e553ab3b43a12cfa63f1ca6e5dbb7976423e457a80763c4041673e2284441774c40709d06e67ee389d7b87d527764a359067b3ade66151de175e3859d29d195dd915c94809cfa94e87aa51904652ee7f9b80934d80f0a08a5935af79b7434fcdfe3e735675747a66d66c5776abdadca80dff4ef776fbaf6233b5f16359fc98c5e7ef208e4af19f79a33df64f15e35c6e534ce5763bb31a15259f3c15ed99d8cc55dcddc9dd7d2813b2480660eba4b58f2de8c7d6b3a33b7732bab264519493454f4e16a52c56cd5b57e6539bb9c94eeb75e9c4f908af78c9dd77ee46c0df7a946655c5bda2bdf3ca539180bff5e8c4f9c87c4a7afaa12bbbb39413e75ea937c8275e79c029ac4e99527ce22cc5ddf3e9c195558d4e9ca5f44c16ff369ac46a2d8529b39865e2a9280fef9d9358dddb40ac6e5c6631bb71419ce6f576fb657607374e4571cfc42b0616f1545445f12f4bc96d6e3be8992c066eb44910a7590a173bd410810734c0418b2bc884018112106eb0b6b88e80cd8ed8c00bef001142f58b05f438a18a2482d49c00c500b42f343f04a8c1082376bcd051001d4d78c087262438d11480252460b9d31c031dee8e43963d0f66275c8a4dcd833bee8728dc88cdc7e6b393aa7a45a1d41c029633eb3e98ea30dbfe1e8739420f983c8b792814be59292aabab19b4a8577474960c2df91ca913c5617719ca4dca1a85dd6528ffffea95115eea44719977669c93895796e562bd5848c82c51f219e0ce5433f14b865ecd298edde51477a67ae502710422186968f560f7f94b967c0e8e9a539bb96e5dcf0fad742809e3eef9d4ea0759e02fff2d9fdafc9cea70cfc47955db3ff3df52acae9955511a7c7e6797e6fd4b967ccf9679670613ba4026d9e2d4aa074d4845e55259f44a5eea442fd0dd734e26b9b2575b7065d5cce3c4bdd50077ffc0c99b177bb501f18f27e795d5f1ec34d56614566db5898b5a33943ed5bc66566d7551e6e1a533ef168759dc442a9f2a52e6e125b6c7437395dce6f60907fc9d3836a72906e2769bb6bfd64c71aba23829fcc32c6ec24377aa2e72f72b2044c0dd437012099933455b24b69f1dbd53d133ab3c74ef1e8bab90487a3d45f6ce6950c5bff36fc5bbd528545e976e08eede7352e8cc5e6dac89813f6d1eb101d98aec98f9d4e64e4eb1b977822cba62202914c6ddcffcf449364f4583a9aaffc4397dcdfe6651ddb36a464d74b76bf79f99dda1bded5e45e5a129ce22a09b6d751043f13353396ffd5a3efab8e47a0bcc3d57b3dbf29abbc271675cd7cf18e4220ce38f56e872ad46f15ecf11da3cf4dbc1a8c1f5569295d7b0aedf7b572b23612cf47baf181edddbbae1bd224b84f086b715e290722febb63cbc34363164b170607fc7301cbfdcf1de10765910de97afee188af95e5f786fe89a12da8437648de14541782f2bbc1ddc1cf78aa16b022bb135e3e2b8f7de104978c325de1a6e7859e1bda188bbe1087b19096577c8755737e292db0ac3d1478485e1cda901ba238c3e3127e252e8a3873bdc51c41957e1157d45804bc525fdfa1d6f7853705d21917bc355188af716b93cd77565611886aca23b240cc7f0f688b6d80e97904b43834718c35d853bee78c5ebe152f8b03b7a88b2fc739705d16913b8cec402a17859776c5d9a2bca1086ae9b7885c41b86e387b217ce5ddddbea197d80f4dc10c67285ad7b6f370c4320b2ee2b1c45a85b0bdb7bc59dfb8a85d7d5ba619bddb97d5afa60aa4631220329f042053a989962067463ce8e18915d3e3d214f7843e36324bd56cc8a38e17bb5f16ca0cef1591110664574f8ebce5f439b2bd471f72a1b5f517f6a3cccc03d24e21ee6b8873843dc4321ee6190f0c63dc4c03dacd1e01ed28ecc7c70dc7dd643b59466a80c05a89b663d59dc7f7a7572a218a84e89d2695aeed596673c5cfcf7c6c0bdda1a30b30161a3616633e16f3ba3ec9a817a7d9dbf6db4c809379d596dff6cf1ba53b589b36e6571d4ffa45182807177adba871ffe087db8ff037f1b29989dd0a417c29dca1336339d793b51a74841b193a426a6cc5371939a9d7053de6b3ef3a9cd1c3c832766d5cc54ce32c032cb40c73de753cd7b96d382a634c545ab36b31429c11367296b565ab27d5291954fb14ea71bbc2d47edb70555fc3bf475326fc8c66beea7489b6bf2b628ad3a39a1698edae9ffe5a8dfecd6bf34cd51bff1ebecb5d53b4dcbb7eddfad46570cf5515a75da50f60ee935dbe46d39698a59f477e8fbec16674531f06d2b8a819f93db5040477efe273be1f4c9bbbb6dab4ea686c2e624562bed9c9db0511a0a9356ca5258adaab828c8027f4639d5a148ac56caa90eab53ceece45433d3949b1f373af2cee78c86337b28248da5189cc6520b4e638904a7b124f562c96ca02ff80c9df219cafa0cade2337489cfd0209fa118f80c25dd3d87cbc60081936348c0c93114e0e418609c1c630527c79872720ca0936378e0e41847eecec36d76ccb878e1332e1df01997247cc6a5069f71e9e2332ebdf0cea8c941e306170770928b169ce4628b935cb04e7281c5492e969ce462c8492e3270920b1c4e72e162e5981141f11991139f1121f11951cd674438dc5d56c379cc3cc0f199073b7ce601cb6756b4f099950df8cc0a558dd80c1a319f05d9c0674192f82c888fcf82fcf059101a3e0be2f2d9cd183ebb4181cf6e46e0b31b277c7633c667370ef0d98d103ebb01e3b39b197c766382cf6e9e7c7683faec26049fdd308920cc7c28e572553a4df3d166b7eeb179831e4dfecbe70ef0b7d16ca5a66a9445576050f717871954d85cf190b5721bdcc3510c6fe859dcaa32b628e2331e400f69827868e489cf766ce1af13224561bab832dc7f2a1aeef8bfe629c0dd579fe940da19e21e66e1eeadcf72f0b093673946f7edb4e25fcf54b3eed5764dd2f4432f857f35331c533c331ce3942e37a4fca409fa0cd633bb81e3eeb4bcbe0ed678e0331b4af80ecb6736747077d267366070bfe9ce9fd990f2bfed5f4e730666367ab2ae89590d236635aeb8eff629efa12425415d8e8abbe778f1190d1df66acb3d139b978c2c331a2e0f69b3168038f76e2190d90c46650a13f087e2b4480a2dd9ccb41e65a6dda6c0b56592126c89782a7aa6fa8895329272aad9dcb9c98a73aad32216984db35433304a51545602fed6a335efb67ae6a60cf503f533ade0cedb4d9f2d931472368306f7d38819447cfdfd6eb7f5f79bd16c99d1f87e6be0dfd8bc5f77eea52426a4eb72779ecf6459f85e6dc0df467d323e8b18e9e1c138c847772403ed72deed76396807f8dbe8ce18eeeeba62784e75689ae29d8a94792bee15adab915314a326f7abc50b4440b708a8df1766b10eec981bfd991bfdcd6054bb6766f6a74d9baeedb1288f87e7b6812cdb7b16a7c04f72e635f378e8ca662874ab7997cfc73318103318101b479978b7b367341f771f9f2525eeaf2b3357162c70bf2bb82798b9a6662d23ee17eed70b1bf7cc5581fb4d8117c14a67ab31b428335b4d1911325bfdf8d87a767666a315ee6ea26cfef55874c5eeb7a7b7f1d1e5620b77bf7e51b0badf135c2dd8326fac22b9df2c2e0258ada5f8927b94999800f77d4de07eb170bf57b85f2bdc6f15ee970af73b85fb35e37e4be07e49e07e47e07e45e07e43e07ea570bf20b850b8df0fb85f0fb8df27dc6f07dcaf13ee9703eeb78cfbdd80fb6dc2fd6ac0fd66c0fd62c0fd32e17ec9b8df31eef702eed702eeb70277cfc272cd4e265e9b984cb56c41ee52c0fd4ec0fd4ac0fd46c0fd52b95f08b8df25dcef03dcaf12eed701eeb701ee9701ee3709f78b84fb5d80fb3de22ae016e17e8970bf43b85f21dc6f10ee1708f7fb83fb15e37e7d70bf61dc6f0fee9707f7bbc3cde1dee07ec12020e76eda60436fe64a786f6377a8993f591586dbf2daa628de61169faf772a921393aa898aa2b25254569272e6f32ca3b2526882099648f166513343fd99f103f10f174c338bd133ff7e682ef75b3535fb362c5238e976cb81907527dae46d1b43795b8692ca2ddbfe3eeb723b97dbb9b7ed9c33eb0459546d7f3f6de26ce0ebb05a69d54825ab95369a1a4565275a37eda227b613cec0dfbd9587753a2cc600dc1da8817bb5adbfdf115bcf4ee6a978dd5151bc9353a735ff76744ee699cf75cd6218c1281526314226281b88d7a6dec647454b5376077baf598d8a12cc4f80dbc9d429d199d98da1e41fda348555f36fe7532b611637d939d5a13cb585b2539c79786907f313562a2bed3054e0fe5384a969af365c18a32bedb2051970b5dde5b58716d0d002562aebfcebfcadd5dd6e9b55b3aa813f5dc453d13d65af369ead6a74af1a8c1fc0f8e27f3b55d4e975fe7663d70ca56a130c11c0e8c03178167f545657fdcb7be326358ba0572753ce9c4f1535779682744a214a3a736fe394a9caca626096a24e39d9cc6e0d85dd6d933347e9533562010b9490f6ce1654a148e124db0f65775ef5ee54f3d3b29ad367f1b3e8da6a28bc7bfcbb346fdde46f5945d3ff6996d5446f437f6dd9cc0be2df6e7510e96dab66cd0cf54cbac93f7e60fb6a06ea9bd346d7b5d5c167f1ebfdfa5c33548ed2477f669c5eb308b8bcb666de2a9afbace657b1a9a3d72cc21309a629ce2b916f75f077484f8b22c5e883572a8f9a5e8a5153692465833722fa26a629496f5333500373767acd2abded54793a98a9fd9b515314a5b7055355e772bbcd05577ca29ba7ea147f2ef75b67ef763a9f7865334af4b65c4eef14b7da83cfe572afe51fb3b8c9ebbcceedf63a4a7e688f098893987ec9e755b34cff3fc2bf5673b99cd163f03f2c95a150d6c4f97cfd3b6dfbbc59546d9f8b2fb808e3eeb62143fe76a2389dbdcbeb07559ca475d3897313269e8ab2688aeaf0a966a094918a2405ca1648b8afb69d9f29b56676a3600cffdb3eb314ea04054ae07ca2600477dfab4d770206f89a7fadd99e797d157d13e56928526cd3edb66fb75b030d57d5a2c9f76ae32902654af1dc19dcdd750fc0a41405a9a948690a07382b65925413153303999cccdc2bda4953ac772a8a45cf84005c918515445cf1451c5d620d11932cdcd31417ad7a5dab08c3736c0bcc4da9a206cfaad6a68e932a3ca8a2c7dd79e86f9455755614af2d96ca38bcb6f8674ab9eb9d8ab4b44bbcb6252b6584d7b6d467cbe4ee3fd34a91bbbb794df7ab73bff8e6dcfd4cf5d146954a6085bb7b09a0f050872ed15d54318013b8fb950246050751b8a0c40c77173d004af9c10336c63038f8b054c3016382dcfd6a200010a27c49a10625dcfdaa80022039eb4ac90fee1e4231d2e00a1586a8d00277176fb850260531c8a08413eece3a0001c2b84fdcca60e2eea1152c7821cb18315411e4eee30cb996017a18f095c0ddef13ba9f0a5c61e16904ee49b81b8238850458b85f2cee7eef1577c77bb5fd4c291e9ecd9ad9a7d7a59f29b56660b0b7d78cae2bd6dd6e3f538a044ddcbdc9ffd071bf41777337e837c965e237670454dc7b6c423eb69e1d56eb9587ea5eab48493bc996cbbd66598cfb3f55144b6e33e540f8bca7bc4d07cdb178d54536a60fae192a97575d64cb6f33b2a1c0324d71918d6d812a9acbd9896eca7dce3bbdb3e59dcee3df6914582ee5bcc3691687cb79a777aa9a5356aa4581a52df79a6cb9dc4e7fde40ef54a49d8141b6c849285cc0aa185d77d1c9a41a65a6131361a214c54e98a0d0e2eeceaa190a8a2477b7b56afedcaa73afa96927155951d1cfbd2a6fc3cf4a6515ff3e5fefb47c5b2e77aedafc9d0ba679c529caaaed03f12f88d9fd3bf79a5e0aff4cf487d5e099eaad966f63511c95db35d55af74116ddc159ddd87c154d9255fd2cfedfbf75f7b63f52e4c81120ad7e945e79bff3afadf2b65cee734e517238af2a56738f7d9d14b74f3a8a457a9b7e76bf0e4f457fdacc2afa3baa56794ea43503b30ee96df8dfb6d176cd5051aa9c98e8675a393151931359dcdf7c9d3001e21691c423277a241559d9594a2ab2f23c155df2b08a8734bb0566fc4986a8d8144de0eea993160080bb73004b3065d59ca2fb45d07fdb400949494a5945e370b655ef3eea59dc6353b4d5e55ece7c5cf4b635ef56bd6b3a338f879b6c6b3e57fd3988e6dfaa776b06a6298bb512fe8de61ebb733976ab28e6e1df59dda16b865adbfdd909ff5aadc37fbebae29c6a0d7c270da5a2bac7e697629b70ffe975a78126fc83d949fd6df487b240157d56ab28b0c46b5bdaf0dae2efb168d4aaf7ce66561f69a97cdbce288b6375ba37bab39a79c020dbea14bd0daf6dc9ea74b3688acfbca2fb5120fe5c6e3b99b95764d3ec07570cb57736276f63b5faabfe1dfa3babf851e06f236d1458fec6fd069bc521fd0e67282c0ef743f7c6e96f1cc5e24e74ef34c5df33d9ec6467738773ca6abd4a494ac2adbf9f6dbf94a424a634c545b79b894e41816595283614887fcd4eab8ec2ed76dab666a78dd9dd8ee977803f536ac7023deeae5fab7f020d54ec607e62438198e933f828bca229cf66a2a9ae05fe994f23286511939a596caada94f29f13dab16dee4f34ebce9cb7db300af567de1a28953f989d7a2c1a9577304db16633bbc19ff9979b14996a9009f8db481ba9c9cfb402fc9956ce968969d55bef5424e00fb33bffd0a6d76152b26a27698a8b525c74bbc1703b9f38359f27dac3a1408c03eedb6d97e67dbb659ca906f7aad7f5974fe06febb3dd79e78d5bb589dbb82156cd5046b7db101058eaa6dbede6377ebcb6a512bcb698015930608cbb3739917e0f6100149a4d06f41870c4805a3934a4c4cc5492200166f1f9423b3bb64d822a09306ee6f5374862cafdcc5039092bee49e0f88ac499bc43331214e0fd0fca88cd1be82ced1f5e311089251b96dc0f2ae3348b84101c9b7927faf43b7fad8a5b809714085c0013db02dc461e31e6779a8ef8e26e638fe8f933ab2866556d6e7c84ab34420b77efc0492364b0a9edfa9a45a336aad3a9ee813f6dfec63d2398f0d0ddd63d2336aa95144004b05440ce6d6abb7e93027ab45202ca6c16ede915f8c174290102603f9fda4c8090fb4e80cb318b4f361751c6ddfd54731139382eb3ad0e77e6dd0ea518a779b873e808af184844048870b2e5742f2af7419d4ecebb57d11f1065733ab99c77b8dce7bc2b73dee1528cb26bcebb9c773876b7be13e71488b243434a72768294773855a3bb4d4d29fc1b628cdbb0e4706cde438061551497a1704e87003a9bf7104c36c60d2184199c1048d8b0e470e78951dca96addc69d284e8825ed7b2170dcdd2684e735474565a520ccb0651049b8bb07f145eb3e6806910444186e63f3de6fdbc1ec849e1f3cf10341e540c800448fcd412a400041fe60c66dbd1f88c869fa83d4899e2a363f2aef5eeb0ce587a3ddf4c3fb9ad5b48552915e1573464c122ec64bc6fd4c29314b62481fcc1061c392f32178e6dd923e90ee6efb1c268ca4222b61a2f03049ec364ccef661706c3a743f9672f46a19c65d7f5291951a277bc8c26d3d9471774cd443183fd5bcc91e729a5dea81c86d2726ea6ddce3218cdac4831577dba9e28df2407eee35ed4065fb1dbc7894569dd476079e55efd4bcd19d7e1d5860c392c36513855af39eca3b1dacf8d6a1c735bb7570b9e750c69bfc975fb4c389be8aa46ab307c8e7280726265e35ee27cd8104b669aa571c4ab0f37a9e1807fc6d1487c311387471c772e5757068e2b616071d1c6458aefc893e08576c4945565835ff74d2edd614a5d51ba8c87bb79afde97597f3babb41067777f286255bceaa5128dccbba8dfe6e78b7e9c09871304380c96d9d3edbea57792aaad3c4e8a35addd6bda807f3b69c53b30828e3b61c04806155f48780292b41ddb3799708e8b1210c6c4314b6d38629f4ccab49da20e4ee9ac5f9d30677b7e575574318d7e9f2f9582797fba0eecf14b3bac98bedcc51b8c79235e4b03ba3385f830d1a3040831430c8a22d6fc56f3b556de6fdbf7154fe691a8e7a3cb4a7a34283cb53bc59f444db2734fd19a83cc5281ba332a8842f0cf6c11e0699526866602000800063120030482c180f874332b1649794d23c14800479b8667e4e1bc9c324c9610a21630821040000100000801999190500b3a7fc47ad73caaf5c9c190513f30c0e397946f1a71ef93bf0e3e680b82f1d64aa1dbfed325cbff1af2caba14371e6a80977f16f7a0e00d27984d8d2d0e5a1791d98afc38c15abd50fbecfc47e8ceeed87ee497f307f55797cb3afab76402c5c6a343b195ce3011ed949ccfd208b64f35617b10fc4763ced1068f00022944757630abf9fe9f012d63188436e1e9b126439ea20f07aa5ca98d2cfa6dd1a77d297b1e9a8f33f456f3fbea2d121f519bd1b980425407e8dc5ec1bf65505260989c0c3f6309967eadc5a9f1b56c1b7cf46c7aebac00fc50b3a6d5ed5db0746fe8acd167ab3e9b7d446fd34c198e8b7e71cfa157899e718bd44b0d1394c9681df26cd164eaac029f587a09ed540debb37d58233f72bfbc7f66202332c801d63c463d7ba9df259a47d8f0bfe22004910f135d8b3604544a0d309211159db86eb1b74061de89e987fca4fd4c64cbd15729d452f4f6fcf5b4d7f4b6f3fd255829ed27b697b33d64cef3c12b6a7a71b948a041dd5365d66be67f7f07ffe60ee1d4e9d2ad8a7811db63310d27ac05f844e15052536c4453f28ba6c84d3319a71fe564db5ef04882a16ef4ec8de943372b647d64c8874a01b9b30be87d1898d5d733de84dd5510ddf46ad396151fc11164d2a751a44526f0d6ae4a9ab8fc425d67004638196cfcd4582bb343995cc3b9dc203a81a6c9696da1a8ae0133cc0e1a43f785cf851a0bc5eb931e29c10949fd98714f18b86b6a9031cf0200995f9b0b72bd83e7b1987bf95060edd7001c177707c222b85d7378c3ab9fb767e57020d2e3bc8c44240c90450164e07e733808550bae1ff6c374a7c7383fa621647178446ed62c6e2db505f14ceeeb941bbef98f1fcbc0942a88997e069c49b44dabad5aa7cdaaa94efe3f859e419d5398afe4cc2344e9312f692b6e2f87792cce261d4625f2271b822f2cfabe94596b909f7029114a835c5660f4893abdc69a87f4f5b84536684f7a97e399cacbe039226aec19c4ef6649d044fa6cfb0460aa059cf29a52bf563df25cf5dc749d85136264bfe3727a1987f470e730451515802b9fd568e9ad65aa1e06d1f091aa8438545370f4f3b71180069567299092d830f1635406296d43c70520a8b49153d362860fe22eb45d715a89aac474163f56d8161ed9d226a72c4d04584340b69ae10c1fb63805570023a5642084f99361d50b6967dbde60a934b9f460451974fbd66b673ec789214386ecbdffd72c3da208a0e94e8e76d80c2f66cdac444ec23370b425b923b618863f9d5cb462dd3a6b28826ad6df04860e598628b1d1d957dd7389f4212e5b730d5cd48ed35d473c695cbf7848e1c7deb8221c8e48eaa710d8f52ee539f0723ffae164f2b988a2972682b570d04ee87deeff04b2e9904c36822ffd1087fb0963dd294a03f50a5d59c72abe3a9aa9998555992a9ae29b79f99739777e5ee0fb6f36374af217e924d7bc7d88f34a3e25ac8589366fe9043bf453fd397d07441e4b5f44a4cfed0259446a7f90e336d0f0c117578f5db78e479fcf7a119debfe6b670fdecfc8de80f05def8fdcfa8f7e70233279f2a965fbe47f0db33c5ebdb13e6496f7af95e48ac823f650324311dfc53a7c8ebd6f3d083d9f67459ade9936f6dffe75e1e316d386fc1f7655fdfd3cb0fd8df5bdb41a03b6574c6375bef8db4e72512c43f1cb048346191f1388a86f78aacdf8904ec0857c0d7d171c58ef37cb1df8b50703ea89e8faec4eea8818a4e8c38874710035c459fa0f9fddd0a4a6372900991a0869d801a2f49095727557289006c280b1335f362f1cffc906a96ad1afff6598b6c399b8e757f13148df5b3db35a6b969ed09b49bda386e5ffa15ec6c7a1165add8f68ea518c227194ab913787e540b25823ddf89956af5dd1db5ea83123751e80d7d8861248184319758af82eb0718fba6a838322229488d2ea319596c214e79d5a572891df49882e6ed4c80a5a452a4a82082e56aff8a41964c26531b2b84e368c712a40733e467351b6718f9a278c13aa1ae195cb71e1e08d4c88e10ec4e4e96ffb9f01abd4cfe0523b90bf46ea5ec27ee68eca7adc41813db529681220acdc21338951c0bcf484f9cd26e744212b14fc7f77b95edfe1f9382d36fc84fe157962f0313f7926405dbf50be481203cfe878b123f60ee0a4edfbb2507099d09355947bc7f48aff6e3f407a9ceff0fbfd861431246fe54e49eafe12e4154019fc07d2c1f9a328b62f852c15ab8dee0e73f173def30f3351000d4b0e8af003e718af6280cbc34e0d064c20b24b25f14c775353311e093f39c7e32e555ef289bc4fe1bf332b598c88663200edb9043984316b6f6e00beaa74c8d5040aa77c6bde47c93e267fc42e0075ba72685c4e310dcb4e86c5085feafd247879f8c2980607d48e9f445af90bfb1f5016c8cb4773a36005343665216f0a6fb19c9e0dca6b58a6cca5f7cbe1285739bc5dc1b8d8455ed4c1f72bb43313298c1dc7fbd44ee2a3e7f4b05a57a8ef26d4d5773b93f7302cc27fc839d82fa622a7e2b58c469b749adea8958cc877fa72336c1c203b6968eb2dab384f3963a286dee4299a068e3b2d0e53641fcd4c051f789a8661991b70ce49f91af353591b657e8aaf55403bac4ab0b566b8c5a4b18ce2e9ccb47072af566ae47ce0101babbba05a1d8e0c3f71ae8eed943201f0b368fedf8f2417722b501a08ebbfae77503e8046970658e6062b19bbafd526bfdaf941d00db65b07a18307b60ba8559b8fd3c3a5be63eb0d5a500d66705b39e604f31f89af61c7c6264d5fb612d80fba1da07ebed0fd45a83bba9615afae49a925bcce31b7d65c707c7cc437e56b918da909ba29cc1da5aaa2069027551363c970dfec1904d3180bb797c06cecac88a109ac07370315d39a2cc9d999f10cf748121bea30865ff055852a13a4779ef893c6df5888c1662a3106b58a921dfca8f69dfe4446cf0ec4b46e026615534c66b0c7d079578a74db2c43300518a2005b10ae517f382d31da99b20e83ce282fb925e00c200acc0f8d34131a744c48617afbf0b0bd5dd25f0a6b63e90ef922b49fbe2fe42201c987dac5410f28726c318fed7f179d5fa3389371589797567772fc3e977eead1d23fe0e2beaf2e1f36c0c916e14ef59df253e163697b91b2e0c594c75a46ff33deabf3301788e22e5ac73e1ddd7f5714a82195c0af1b7b7442de75050dd3361799fffa07fb4a9fdd982b6b25d411fbfe924b2efdb9f17b672304fbbf7bfa0313b3b3def1a8b7f364d7406f7fb9808e76a1e95c536fc0fef01fbc537ef4557a8eb4cb0fec496d6df77ae530a7b81dba37cfdbf0d6b2749c2ebf65eb3cf8ecc74af6d564e3a47cdb546ef3b4b912b87c5c97536c54c82d27aecf32c83753d5aa8bd94bd9e6dbc230c775abf34339deb4acac3bb40e2ef79c20e01fbb5e559aa6d6a5f424110fc7ceccbf791cf78713e67d06b9f965e81fdcb8f14e41e66a8d57271a7eb08407d5d00e8e2ddc0f5dcb734a51c6de8dce8c00bb0357ecbf10ac8d3475bebe5489ea3f060c6b70d7cd1e01e28973b80ee6fc82c90d191b1e58d59a3ff8698048f46382d207522efd8c25a868459ec9fa9f79d77316a842206a7398e071974a4e393ffbc04d4874476a089ad879eeab5efb75329c68b971d14ff500e7da328a7a69085569d5fd5cc07b153816d82a0620746b7bf042c4ee8b38c4baf1ae083a8bb63d66a9bca00aa7ae07889718aaccc22d9413d7d57c5edf51320d482e3f881df11bf13737fea9b4e7e5695e4871eed0bb6537168bd96cc86830d61f796333141ca4b6a339bbe0ee2845cfb865cc8e5f01e12206627c27235dffb807af2ddc5d4132c09ecceaf5a92a45b5dff58d7887b5ca9022b93b734cda641456143c117a5297a10a76c1682e6183a3a522eb07a56c05ed5d5c39a43c2677a627de6c5da5fcd3b5fa326bf7257ce92591a597d24f0d88ab88dfe2b84d7a0be0e51ca0f2d382f22a48489e6099c36d9986252edb4394e43314a196fc4cb04d15d0b2050618a29997f25e51526c9f027962632a290ce9d11455b42890dc4261149810929c5dbd6b85d787eeca2a1acbd0cdfc465868295b5754fdd124ae7ee6d68c8476084be2919835d4bc0b5a4675ad802f560f606aca724990f5b67b7fb0804761d93fb0d8952a7e1ba9592d8be849562a37d933ac6af633d17de65f5d8b948855063a2f57450757528c958f6f23b8a3973b49295e0734de1722838ee4a3c1eacac9cf95249489010c49bdb1ded3fd6f74b02d56dea7372a681fb2f74532fb7d9a7a3f3f645103e21b33373a79ef3d8b3b719517e4fe78bcb0af7eac14dada578357a165e5a719f2f91afd2632cdbcacca9041ba2749c4f5957b6ced92c5ac5d1da363e1871b5ca2fe7dbddab5c0dc8818870625b8b95fadf0a39763762001edc09b0aed80cd251316eeabbac2c01a5645fe5d1f7ea355aaecc12aee4299fa448cd78fc74361b036e6e081c530641575ad935b63de29584bf7246c3e567b2720511c871558a5248f5d81998d95cf2229bd402fff7a51104b201e79fd6116e478d54f995469442952146e3f8a98704f28580e021b301e800babd3fc436c32656a12a0d1a3800093b79a3501fb5327830b80b7825ab7e144718da2306d0fa7b0c97f44fdff8e2ac25fbb5fe4181221c25d623c18e9a4bee2431d0a25b322bcd1f568bdf642b2312e8c22cebc59bf0c1166aea22292d4f8b1c101c847da3d8bcfef57088e105c5e33c8d32e9373c17e41cf2dbc0994df08eeb35e3ca2237c7bc22b2bec3149f46077c9abd9c754aee84e0f19d56a13470b1a890930b9c7d2c3fdea68aae37797533e43007e6c30cbb2de62140df29e9e92c48568e63ed9b2976b2f1ea65bda6d0e06a5ebcd08881fdbc81efdea375e44b9e24d549d4f986ae308130c272c35ae5cbe8c2383c40d2bbde1916a1683fc7434af0c82b54fcceafdc3d34f9a800d7213a11f0ca00aba8965636fb04efa8b3a745039ba7d91fb43b8856ed34f27cb6c02cb21d5883a61d68c4367b3361b703834439d15e5b53dc0ccd7be6e4dfe1f08a2fa1055c4f7053ba3a209e855985d931a9f07d2c39e21b74e04ec806529ec1a9c92458740ab4196dfe419a748ed15ae44a917d22b3e70d1867e541aceec07877d77ac021ceb9ef059f32ab16b8da929a2f49a5975a2506c547364861204b3060524b302dcc2dc354290374b505e0660184285a4254f649bb6148abc334bc2898fc4714426d3710c5ab6aaa37d692cd9f39e3fc49984039656a99b9f0d949d73cc3f78a698f5ce82dd56f2d653843b06298c8ba822bca0f1d11bf47ac060d09d145b1427ff0fd47387e1a19d303105edf1e3d5f391fb31ab96e0f9ba21db7085717e503b363035c7e95f9141124e23ff2819ef186476c9fc386335d54c4af07db3b26dc047d4c1fbfd14e87c56e07ac35c70711d4cfbf4eebc7c42cc80ff5119de2f33e10e4fb0503773355fa69ab2c63d79d30f4cbbd1ca5c1b96605ade28a8c2f60b9705cf341c36206de63b1c146324ce8bc91850d3c32fbc27555189814ee6b82744215a154759dfbd44727060d47d928e93a9cd046321acff6288e0e2720ce01428da23c0e60c0e184b076d15f238dde4783011ed3ddc764ff77804e9549f73fac910f6bbeca69e9e7f00cb2362067d759fc658f9b9e695c7993f825c88af54c30bc72fb34eabdafb9ee41e30b00ec32d4ffbd9fd8de819cf950de1affc25d8ea68453b7cc1fa6e4d49a4aad2915d3bf3c021638f749883e093288d5d3b1352fc7e0cbada7ff249cd89c762da19720bafe74a50b92feb094a0e9098f19ebe1d9688461fb7be6624a6a44478f911177b1073c47323f856695f789dc8b9ba12988f6516a509d377199dcac8d2b647ec590aca31f14e648332a249a4eb4e44a7b6ca8837cd2cd264bfd28c17f2365545cc94d6fd8fc42c0682ac90746c9336b0026bf991b11a0b2c2cf757fe5a10876ca19d83a7f27ed82faf89992c3266d64ef79b0a0dd9d29685235ffcc9b2d7b1e5778f807817b351ba5535c68db606daafea8a319e2775d73f6af13d11eb17a369f24f8e3787cad1da989a7896dff2b85739932099091ea8c0c1d664bc6a9e0f875508c37e3d6fc2a901fa67fe7d2b7ec7980ea48143e01bffd49f1d4282b713406d3a9555a27b75546c890fd9bbae598aec7a3203bf8cca3144631a8ad85450dd6b47cfd07abfa9dc5787adf2766c66ee008a2f51dceba7197a11f730ec74fa30f4b86f4ec2f61b3545124b10f943661ee47e85080db80dad44736051a70acf034aafa1d87d983a5c18369b4d121781e171729911c2235cc3015d699669a6f61b6168874f919aae9e69719657a9330f35a2bde8b11da74413d3afafda6d4cc595ba604a958b04c82a25a1c34608da61f5b06b004c29a50eedee59df1108820d7a709310f4d1a8d021cbe5188726292695945916aaea9203d709fdcbc5f6741663820357ba5e46b6af56cd6aac5f7ac1d9bf760126ca25457ca0a210cb705495f54e3d405d238b73d79a8c143840f872148ef5c29202b0027b217134b1ae3c9e7457f1011d6ce3439eda008042c8596f340f19e212fbeddc200bcc1892a0158d07fbbc01b22749f12313bd0691505cc2ec75371677bfad78ae099e2d014a35c5d5e35b1e78e0bec1a0b05c7d99cda79374959a96c7ebe64e1d6530eb5b79ecfd4f3ca42b8397c07e0750751437c2e1749ba366061784944790bd29e7ecb7e50119069ca8adb5d6a124b0c46c55c1814a3e3ea29d8c64b5115158a7a6a900e6f55047b1011ea8b3cfa894010654fd45bc0ccd6ed8a7c00bcd141314605c03e538db39ae48bfadf7048aeadb5607253e7a60d221272c61202082a4156720065513ff6ae9ed6f7353de5d44104010d3dbe5bd1715701572d671f1f9f79e0249dc1fd5d0ee1d6887ee80512e169859a04188fbac658cc5eabf8f588b2358b65a4b34f416082824e3d993315ba3364045b4faa0981352d9472824b2b201ce733eb6ea35286e72e20fd0cfe8855a5ea1263014d2e72970b329e3fe053c35bf67884df561ff1b060a37d772cedd6dbf2f8a8e2ef75c8ca9b5c1834117dad75bc5cfca08a05f9af99c33ff5481bc2883364c2c0dcf56cbef24b4fc305797c3089816c84825c862e2f09cd613335a7108b9fc65d4b811dc23199e13ed16baa58460a00b41af24d5d74a8fc36f366337fcb5e932101b2813a2d627cd17295bec7b2a24f6037f9fdf3aaaea376d8ff397d01a02a609833f4f60a0bcc1255e0b6c75d94f602071b717db7f0ce29dff6fc0068f62ad40926b6abcd8fbeae03329fadef63ea3c0c7e0d5c2764efd39e379e7a085c6470d4650af6517a3b2057d7b89a0d9188902ce97276446cbfa7477be2da14895de7cc25f7172fba7688b096de2a9698af3d79fda489f676b8c7a3add6057c7f1a75a69c7e728ad0df2c54df7d8c0355388bc3359b21e0d1398e9d43c88c96ec9665c315df8b017516f5531717877e2a5f22a8994b6d7936237cc5341575b7fc82729599a8ef831edd006269131c90a95e85274a1ea23c06f4a2be28458d5423bcd519626cb530a6e90c856541bcc6df3b95a5dd4187ef929990a2d91416aa88e732b95640bf08ca56c86eee0f5e5ba9002468128896e226e193c136263747854980ee6b8f8b1d164b8e34e075a6cdb5e33a647a317a94857bda442c9dec5f6ee81d6051868289f03336eeb8b0f8fbaedb4f8662916ddf5e08c5d4c9097e97a8ff5f1dc3a109da90f790849c141d36c8ab34f5c1eebcadf116af8c4cfe54a6449712ffff0ad04656288d992be44cea48b2900d4eff2158b35dd01cc551b25694aab787efeb1139780827b0672d9e2f95e9106508a9114d364e89749da1acdc5f9442f0e13b1aafda4125837c2a411ddf981a129154300d902a9608556e1b8a6980d0c38e4a89e2f1fd02becf40124af4314a9d2f1cf272deba00d5d622e892d34ff9b6d7685dd626cb994f6b0df5ffe52823972c4b301a40bb9eee55435d280f498d867cc7e52b52df03626307f53c8ee22e303a7a40e5057ea5fc31e0da594a0393c1910b63032166d4a6a3f5f3420c06f8e88101d471922404fe418c5d11f22241b8051d14fc5a9b21311e4f556a4f48c2d8fec7bfe6100af6ca6394f144b92f95a592ed6f4b40253438733245355d1573095c3ff357b4243bf1b1cf30fb99d12e1f0068d163ccd4282e2577b31595423010affded414714bb51ded7b855ce7a9d7810fef5d9c5d170491e95527560e22ab210a335b6dd1b704e479a929d4a04a81e0b2ca7e916c0ba09324a04b5c12ba148da63d016428a3caa14c877d8cf8c6c6c3104c13ec04850e73fb6b02404ef9cc7cbbda74d761c81044d4710b60dfce904d30bb5279273f3fe07ff16b6cb268db10e3e942ece40e4735b20f39604b92b5a9c7c777d885ca73eade980102d1708fd563fe66aac2826169758054662f1525d7956ad7718af23934e8d635d8000332e454eb76d4d3f131173822001d9d63ab661984e652099df8ae3a77fb03a7a8b6e052e337dba85cde0b3f9b75861a05296ed542b20444997614bcccc6659e8ffee196949dba2219d5b58405cd4056fbd1804f6a064e213fd27867ff8b77c1d771a3d40a5952b9af54ae3131919951b9d864323d624cb997c6e582e9a2455005d092cf54ccc7d84d626a13531a83a488ddd783bdea1a2c585c89d4955090ff54209d8e902dfab22fdfe9e555079be8b6a952262bb89ae1a0a06d6c73031665b87e8560d62d71674b9f88cd2419f898fb08b386b9dcc2e41750ff8f169a95a88d35125ff1cdf177e99a52bf7f911242e0bc080106399870e37511defa5808a9fc656e8898370e479f990c5ba90c54c264008287fdfa0a0de12aa5b878918266d14b6d4edcdc9ac6026148b9e71161950bcfe5266b2d2de8426799ba5d6318b1855e8b72a0f2539fff678de0f58eb7e1cb50280aad645d28b7c097b09a964f2c872b963ecd1cf6b7722001b79368365212ac221481bafbfbc532220d6724c1b613fb0ffd080e180e834882cee08f70093b85618fd7e44d5b718d36ab2339d632401e4476ab34e551f0d3bb074e8b0e35a5eebb93aa2705dff7e29ba81f6c0fabb5224a627074c4f18162e82afa01da71ae826fc10bc4fa68f36587b8950c49d275d0966200b38cf8a4dbd634c6e6a61a504c551242be2e1d8f0ccc7eca98cbc26367bf801c2df9b3c64cb8cbfc32d6533d48307d2a65785b816687d0102582725c8814bd3128b86156074f6bd5061450b9159a9be5b9b82e2b862973b5bd5f45623783e7e643d9eb2bcb9029207d232d862c23cd560846408f703b4493b1d79aef8fd52071e02ecf3ced581f7296e960bbed8ba5a577fce1ae68bddeb08fa70d1868970feb8f0afa53679b79b09ba38e2b67e6c76f822fc93f361e8bb2d1bea2b5046177b65e55884bca4eb73a03f8ddd2ac277979e5ca87eb2ef2a02deed47ee8d3e2b6ed9268d9ab2e5d7dc62a46d403f7c4fccaef76820920b40e418c0c8ccf0548373c933099cf750dd59bb5582a54ec4ed93c8bf54b7022183ed366c3bae36d5103a3996140d5c32ed571a62dedc461fe847908453ef004db99e21e9b8225a58ae6e4d9b13fb542e5ec3f4949f875e5495508f3e4c3d3f1528c95a0546a7bbfa49c457a5133533aeaceeecd389c4f20ee9eb6aefffc89bee033bc8ada6c2224dd7f5bcde67d05036dc07ff6a72ba73806f6d9cbbb7a9859164b5503358c444bf16fdb79cdadf1998e63356649d40c9a9592e3c291bdf905423677117f98427571bccb7fecf0674baadc74541801dca044a16994c4c1b798f686f75b5435d074637c45b7f16377d8da397a9d6268106d53c413e6d7000f1b4c98354e79b60a7a8c0450afa58a3bdba265a0320cc5992f5a0135005ca2d73b96d5a11b4d02c2439c6c12cb7b1ac06017daf690804a919f62e04ed97a00a7e9e5e4b0944be0dcb32f412daa1f27a040a284f69aa80d3658090ba803a3269ff1770e80df4c1bebdc5151668ee83b10b4af7a3e722a2f6b284ef173633c184a235e60d7a9a8d06c5ecd9c22133e2149ceb83e48ad8be3229f52354ed195a7a07120e8dace831e1a5f02a7531421584c7a1c255ac8fe08a83c9c26c8b7e7f73de297c9b7ce005299dcccc4a8088ddf82e50011d3cef51fbc0e7d677e1c6f90653534002a7d4ff8b73bd67fff16fc8ce5cdc5b43b5288c41399f3d271ad6b4b64bf866bb1a625f9148ce5cb0d50d2f414028fe0c5877ab3cade436bc08da62c4df1603e46301532f1acf7ae3a6dd57abefb2ce659647669de1c60d7ce2e1d8d874ec2342d60d2a1213854b6dd58c47e3fe0472540d964a4fef1ee9ea9d79abaa65e13ead8c16ac4a1ac10a2cad316f3ac61d14b41ed6ddb466fde095f9a808bf61731d927e99153c364fec9000c8d2fc24c8fc15d506a3001a22cc9886c002a980c946bbec59c32db53117e2dfff7edc9663db007dfb521710f1036efcbf8751c9c4ad33338d7195a8d152c075269e988f0a34536edb82a4c445393d4c02cec0480be6ec6b1df3705a937d8610ec7bb2bc190a2bcb28df1467d8004ca6215e0fad6b2926b3e2fe43d0bd2662e459cca6602348e6e82e4f251398a175753a4e82b40441cdcd845d53a8eac214de5f2dd20a192716e6aa603efab909e5ceb7750be1f3396b6c9327f30ea637c93a0f662a78b7793234ad5481be500d0b00a96ef481119aa3164dedfef5813b0133da5e577c5a9a752b7529103678d75ff0a3d66c66b820d4c9cc104a64e5019f64ad41d13b3994f85dd9a8e9d76a27fd8b1754c9d9228f672bb1a69735f5408f6f57662a9c7049a66a04114653a1770ad4ad13346dde17863a7e77b84f70dedc29009399e3c217f7d675b654f47596b96075aa0cadd37abd90ea00dbac6a6a5bab70b72445e1e069fada9386db01ee354a5b06ab52de1ca8615d5a2d4d7c2a1751877ed31901cfb26c8747b2c31c4a48626ea7cc1dda37a6744f5797235aaa1340588fe9b9220aef705b465c1970633cb6839f0b90b45b118e0c13deb4e893fbfe6bb07e6f8acd0de5afd6753f8146e2e2083cebd24139757639bf7dcbd461a24a54d924d2ca01664ba86fbf00f7e1a37f730b6e5c97dde91ba81ffd1e7ef07ce8bb8f7f62994af00f106404ce5a2c2ead4edf8d344ba4215d534e4bf8cf39682c0d4ab7c02b2ee0ae0aa4855015ad7b6a99823ac85dacedef09433a4528204daf4b2d7c47b78f6b4e0e219b790d628966b07f01ccf22c16dab86e6a395b879efa448f73f25638443b9ceebdf252fe7a96f00470f14e8599ebd0e7f1b56fb574f65a12cc0a959e9df4d43f32fcce9686b7a5d9850804ecce47f7ad3346b7eefdca3d78ac386b51393cd387e33f67cabd2af7024dcd4914b46541ca3604056f454699bce8c6cb6cec9c34a1bec74b3b977001ba7832f11aec764ebc9be5f3ccb591e0d7510a4b741bba43d58034202ab34b49f81274164edf6c33eb5e6974eb53ef6de4e569c9335fa2b3c653b6582e4cc44328bb797056586f0976cc8f48fb99a21965e82697f3e924acd84c94948a51a6b2517e6942a2215004d754b0371fd9e167317e764f5c54a8a42a98686ce132c4aa67168facc11f10e719082fb6c80021a2647ee51cbc50e3fbcbbfcc34721206f7c4bfd5826bb8eececa4d8b57afadb63420d64316fc86adc398c4931cdaca78558dcffffb88169a60d9455a607fb2c9f675dd9060107141635cee157484ae284f6af9f3987c117ad9b77d7762e45bc58bdc32b9d75f3c08e78eb0e5906de5952ab7a51069a65f2133c56d1dcda320ed89535903a6656dce9664b645c7cbfa44ecd09d84135837ff8bce2df3a7ea644345525a28a833ac680b6f13682be43c03db4a2e6faa23e8ee5a224d00b9bfad52f1b19cd69b5fe481e2027163efbf39511e9f59fb526888718aa9ad5a0be2114cc3d7a2a369a93a404729f317b08d98a8f107e482b5836649343cd18a6876a3ad0a7616d0264c7898fd743ebeec1e45b30e3afde8dae667da4648330162b9f16c80081129000eae6861eafe1f32155e8151ecca59700c4638fce25954ca947a9a2ac5af99f08031484eb4e1bc1d929aa15e97120c6ccbc151f6a309fc9f67e566d5af90bab6f4a4662a9220f28936408e6ae2e463a3ee3b823cdd6195c1a8b08236b0b250c4778bc133e46320eefbfee4f76042317160aa37a925bbe554ea273cf281396a1a2e9e3e14ec6fa7933987de0d707bd8f40915861294999aa424496a461eef6e1ed7591192ed604624249923e6638d2bf932f610701fe2cfce97946bd4f0b0617a7c513ceb19476ecb6115552987e0f11b55d9a1b2bd931d02817db301d34ce69b2cc2fb18154b58dcd8abd6d3a5396c4f30e8894aa484cbc5990e742987ebb5546fb596105a4e94afe2868776f4791730e8eb74dd512b0d42c3dabe7b9d18d6822e5b4740ab67cb104d5cb27ecaac0e69982eef39c84e785510aa2db078b30e523f1930f1b1499493061f1dac1bcae0562138854cd396ba339069b7e2aa96f5d95734981958aa617c9242e3201f22f95d41bb3ef5d97667838fc07e0379036b4616af3a14c770e2504a14b38ea28fe6cad7df3d8846696bc59f890a07654e2899b2bb3f4b11a0ce21102c08c2e4a18848a85931f3f2dab56dcea43dff2fa1e454f2d8e7bd88e234fd77acad2cc6846563b67ea0512236ef00997a42f03cab3af738a3073b0923c10d362fd6145453d462db2abfd006b5f6da86f8a2448cafc92dbb5de3237c126fcf9bc49918caebea42b5e96d39e235aa32e31b92902f3e1be41d2bcbd5a807072f013c31341c7c357e91865b5b05024df6b5011ca1c58aa65d4c35a3b48cc79e8c7a9322c8408e0ccc8c7b659a762ff276c39f5b159dda630d8082bdaedc639f77e1548b7aa96886da229cd534d647022a96ffc99ac44c2f6c9aafe89d244ba3a0f4eba538a1c452c4488c4b2d7e29814e231d8bed367d62ebe23905daa6b490ce73cf86268c7b11c999acc1c2234bc24d090319b955400c8a5ea611580d194023d5e419abd74d23cfaf293384219a7e93377566d155bde9015f269da578be7358acc3f061f0ba76d6dd828f13c57d498dfb88e9aafad94b09dde484b9c5a4999f902879c0274f618a248f276c70714cc81c4650034cd351d4f70f64cf1aafce86a18afce6df7adce4a00507a2478aa84610045ac5d7ba5b30db91cd168e649e41b44ee01962a93166f20a3081fd6689319c683450e270efc316a67e1b21ba5677f4958482a50446c5803968808fdbc593a36331c118ccfef4c5f1047c063df0676e10d9b75e7269b1d8ec6481a5a0b157a954a9e033a60e09bad953d1a0c3e61d4d35cf174e8e6e112944b09bca1302abc10ad01fb7b30da9f1793f91e5487b321bd88ae0397c3136e72f713d4c76c358ee1499710c9989e6c812410585a8bf160f14314c8d06fa568d3ad0fccde9f7a6558793d8515e83875c54702437b1f7c07fd4d3432da96ad700c6c9ac41dbf50bf927cf1269553b36c52d58d3ab0fe1b0ba787902a1ec8a7e3d1a22e042eb0cb02d192cc0d2ed2115a96f0b18c719aa94ac92c7cb3d65b98172536d84d8272e81ad53131d1adf564b7757db290bcd252cc4b6fa5a5b8d2b6a2e929d19d3b6f0e5c303cd7abca0339f54d8c1b0b620d07b7acab4cc7f85cb4b0d74a48d89c7bab4a514dab6e0f39de28f54aad6ddf3dfcf95ab8aaf5b05aad72af1630cf9e2a8fab2c4b751915b76c5c1fac35ee960f838558491da582e0e4414e244d2f04088bd8237988ebb66bafc3870b127b548912b88a96587645477dd48af90f86118e655ebc0c73390783c1d3bb8350b8428a25cd3a4d78f393dfb7cb59a5c860e2c1887586ae9b62fc098ddc788b3f7d5e1a72ab4c67cf6411f788e1a5ddd9faf81342323622fffb3c3176951167a47385ec41c3278857db4fcc642c39b076173c9dc6e8143785449b6b4e4641f26b42fd675a5e8599fc96f26837d947f36fabd6cdcac66c4a26ab335a16ddbe5b081ef5b71cd265984dfe8d54ba8adb1db414fbc4c2e2c6d90e3144d438c09932cfb26c80237e1852ee1fa91d2832860239cbe142dcfed7b26621ad19e1e233b9b9472e6a240cd572b01a41485e5484a892a697f2b543cc8db587037bb22aad4788768f1aa223a6f27d7ed6c2a6f9a2ac6cd3185e012d1c313c1b1f2efb9b606aca49bcc5c40176c940520b9afb838146fe72eca3435ed392656afe7a0dc2a7c3e21fa8f9886c97b207c3650cd1075ebc90b4002c0e52b2947bd61e852afb49d9de0359726b30f2d406d9f6d271b18eb4a9daf24552e5cde56c17012ba7dc708b02566886c5673576a7712113e9fc631d75644973b44c53de6e661e3fe581d6625293ecf6088361b4b84f5358f7c9ad4cc35e2cbd81aec96888308c42afcdbd4853b18875e0e87059346c4b89bf0940f5f6bd77d958b5780a28af5065097c6e555c6ab3348316fb1654e590589100a50874ea304817bd0d75ea5836803f683481ea0deb0c3bb7efada5907bf7be0ed9e3cba255d75c3947fe197e30b64b44e9be4657a18dc75ac33faff9c8306ca68363e41bc2dcd08ed21d6adfda4588b939a2918266bf212c5f8d2457da7c3b51c5d56be696c6e9a3ad24ad580951ff5c516b5dd5c0f71d4562b260d1f2f624ffd795d6c884b1c12ed5aaeee737c9e87011c86272fb2c87e26a824e97f593a0c45a69ef45419f555a2f50f9815d94f86fa632b97021edec1c1ca25b7defb31d5fa11cbf4f1fefde44b62b4ccc2b11584c544ba40e7cf90b874fe45e6607b1c01856c07c1e82abadcae93c04ec6c9b75d810233ba9caa19320ed272cfe184cd45ee88b6c5e2e44e79e18246b0a4129f3f1a174003073d089e5134a70feb084bdc7afef22b4b9d3e01ac6a955f166c3dc592b441997bb05f46ba8f3db8c440db9fc8cfb1fc493e96700cfa9a78c108d1e81010d1cd1fd19fa8457f59ae744205fd083bba88b5215442b3ae1f8f93e00a7afa778d516816b65b18c8233a24946f2916eaf97b27ebf2f5666ca1b6883a7b02b0581a69e42adcf31c14233029d0c71ea92c53d09f277e6252e8d672b2271eda063a3f6ada3d19f9b840236662ee00044323408d4d06ed8a74c0f8b2818308a28f093d7c9834eeee75206cdb7845c66f35234f5e1d576603dfc25acc0236c2bfb2378d87fc209a0b9e3520eb4fc4ee452e26a3d3ec75239321fa5479df226d4f27f58b98d3b72ed6856a923e5b56103f5a2445d34c4f94e1b77ccb2a639dc5b4d90aa96df2213c8d8eff5731ad8e8929fe65ec47c947139291e26068c7606ed098974997f5c0c61fc5d3816f7e2473bdd3da8706cc2cfb7646ab9742eabb5223ced6afbd337a276dcced835db6ec1d799ff08a861545d06ab642dd360a83df25e1276690bb3f4ed3ff83625dc3c5ae5989bf83817a57f2ecda055d8255d11be5cd8eedb37ba90f32172c58bd6d5859a3c7b4d4383164f2e87b962225d7b97045e013a08100378ad5076415fe7830f9a7b998b17e7c5a5aeded2ef0a05c0e8a4c3abcd9dcbc4ae5b2d7bd021be6f061872d1e57affadd92468efa191274c14f5cbe423a8cb4ea45daf1f9fc5ad9eaf375dbf82d71e82e9563acb103d368b6957e6c50bd1e52729e1e0813e9009a58a590f327819810050401c156761007247e7b44adc622fb28dec415750152dbc782ee9122cd7e088e88d4649b7ed09f139062c1fd8dd823adce66cf2cf569fb19d61929eba402bcabb7fe9491a9244d3bc7d55b8cd672a5521039ab278d391994674d91bd5e71abe508ceb1375acfe4482d420223c8baab1a0f460fbbc627ed71b7b341af0032f32c2368f41d9a125495e54de5f19dca20d8cdd1e0037bf64fa1eebe8ad4770d98b23e3937147d0fdeb949811efe44ba6581bc3784f8a333ad5b81a8f5ad0db2ab1e69bfeb1d97c4970ecdcfd834117f4507eb0c7ecd12029b760b9a1eb68a15d1d91d5bbce8738e76f9cb217445adbd06ae57b9e6f8a9301e848839fd09ab362dd74f05388888348a5a24924bda2128e0fce60453cda5817524b5bbeeb024112aa7153f666fcd26e187a5819eb7d495d7ff38032b12827f5cd2bea62fe97264c5ccf1103ab01ebfd974364f272c61981a13c82750998adc842358c3a769bb3f17d3cfedb1e0c3f0fdda253f6c668b362f944d0cb96d56f58d014ec5e3b10a8ec4b52bf6e11ee2d225bd71cc776d5786dfe4cf2e24a31778c719fa21cf3717af558d3e77961c10f9ac1f07e1ecfd9f5b50b469ce7eeca69ce7041a348670fefd35c51ec7861b49902e70407ee6658569a4224267a715d720f5e29621e91c881eee4e088aa36ebbc525ab46e6e5992407696dd5ac2dd9318f9016022dba3a74f201af9a017fa1a4f3abe774e93f7d967d0263c9fc0a398985b156641d164abb5a1749cbbe8acc61d003fe0d00d654755c6bb00ce665677493f85529ee9ae6d844cd06af09cc435d76e0febfe2ebdd3b205b5edd1af18d11ed0edb1dfb7385876ce0fb5ae0fbc1e18891e736d0cc5a9e26530d388f523d56d7a58af8f511d241148ca60e27c1c9432c54fefbf1a558a4d3b8bceccc4ae298356280bf4ca6292bd16fb9dc096339456f894af0b00e3a474dc5887e2695a46ee390ae961777014512e1ac00b8307d3f8a15a76c09eaeee34835115b9f1e6991385e10e0a290027d7f9cf44c067b9a4b0a6bfc73dcdf3da0b9533e95b7fda465fd6e1f902eaf6827927788321d8be23ea8b45cf764db3809b267e380e45a807a891cbc0ed5be72c6e53a900c47667396846ddbe23c21c17f092546114f9ab7365f915b6f4619d85f3621492a8b584819d0a3b2f1ffeea96f65ce76b377adaf2df0d43bcff7f10b778710dd670b1f7d6d276f41ee3fbffff93198f4815198754b8208f30b6c326ee55c9d89dfecf1b2fcee8579392307432eee5c40dedae75f02548383d6b9b6c18a230bbf01c2add5b89036a42dab5fb781d53cf882f8f28b351a059654ff26cbfaa384d3a1dbc091f16b14ea29c81f66fcfda806d91f52bfb226126835637520eb633610b07cf17deae4218c3f6b3b4013d0b924902b5fe0c7c8c40705598321d9edecaed1e684c74f5e1367800cd3b008193f66e696c9f3cb00d96b6a6b451c103f3cd2aa63e0cf79a7fefaf92dc227e790930791af6d1175c39aec61917fc7c1b049c292251883f81aa0bf8b19ac1f97e4f171b9862627849c34371d69d67f62bc7dc813df1cc35554372938f18884bb473c3f318f64e5ba00c98f43d741fbe0347ddc90622f00d383a6ddf8f9326be250f83023feaf9e290f5a3cefe713e516242f75716229ec1be0ab218861d8952f97b2aab3b2d7b4bf1bfcc93e7c07c47b4eb0095f61656e829dd8cd109e4ac3e93df52300e60ef41214a4815b0c6e60ea6511e97f2912c75ce2e9cc746cea70f22876d3b022a247e90061067317f0338625e6048cae4ec8129e193ae9b48bf211ac52fb83af69bba0db25eb6af02345f33faa464c17a1dae535c274f8bf858f158d4d5f03d1fd61287398ec78d3ceb59389d264c8b943ce86228861c4c28628506c5e1ea6a371e7e32318f7afa4de00c7689c72f0f1e4573276fa5eb4c3f3b0b518a3ec3126a5c4a28b6c9eac1ab81e3cdb6aafded1034e361ebfffff24d617722f518f7fee21339ccd81ccc38d0075e8ae28fb2307893b28a5dc405fc2ef72c48faad01f796e88f67a8c27a29fc179c9f644e2b16f2ebc60428a900f9c56d676c875444c5349f98fd5e4d07a7c08fae17eff4360bf06e8ef1ffd301d817424e786e1db52da77da7dfc6e63b30b84df70d2fa815bf8e2fbbfd9a2d14e968cba073af1b7c780ded9cbc933f8da85845dee01a4f86729b9ed65b8ff86b50ba7dbd2e500560f38670ea22f5a3b22331ae804d9716b3ffa6c181ae11fe58217e2407ddb83a35fb734f904ddb5c5646bdc08cb6433f8fd545e4f69284514250d0bfb51a45bf2de887fdbf6cc0f7e08ea142aea2de3776036adbe54cf2632b762a00ed64f0374b02d79ccb991c080ddebdb171ed8a90f4b1b7ac11310c6fa2246ac8daf34418edce7cae56052f5bf7188102b0ad961407f58a5f0d78aef2768fecd0d23fe55d620f861b4deef00fcf7ad227f98746617bc6167afec325beeeb379b67d7e53f0647766773222e856b09b1a9998e1267c34c8ffec0a6f6751c9a0fca2f8dbe4f20636681fdbbf24cdd0999a6da6410e85f6985b20792b7d3cb1f13ef6bd69a3fd13e643ec49be0ad1058dd7a9dbf737fae231e952dc9783d3b6cd1b20c4d2722d6a7e1f55ac0f560cb9b31b2ff6e0f4635ea741e2b3339b2eb91c3aaa03702882dee3836f5cbcd04a4c6b7b7272e9089a7d6690e9c759f0786f22265f91f7956b84fd1556738545c7582b44e79c998293ec8b33c73b953ce44721e5d395809386fb97c7232d87fa78bc1f9ab4fe07301f9c5b57dc3891fee636e78f5bd0dc073ebef1a3ff5022dcfe7f1da463d85f540f697b027b6fde5b550a5b864c1a31c034bfa1a5fd5d84be940b9e3b42fcfd348587a5587c9c6195ae6afec053042230a7b8dbeb0f62599a8d4657f19123ed93198ef2b7c540f3f15f72fb0f46b0272ca8a6fb0f92a6f76441f0eb04ba05e41c17e81981099f9280ff157f00e94061e18210c9a0c7a2625eb19e59d09e4ba4cf71f1819335267e60c93db0804fcfdd7c01e70a755e1331f7ee2992d517004f81981e41323f494a2026f984c5b040e820d0da03f5b14059cf2774e410c5b033415da19fe3d5cdc5ba872794ff8be7e80eb2b9f90abb9672235e4c4e3f8591b59ddd82e30e9ae0623bdb49f53f710dfe654b52c2045819ddb6f9d2dd8292a65ff437e1bfc813f6c15d8fd1d90ba8590970d340aff29f86c2543ceea84ab48f8066ea24fb96e96781e27c2f840f9c489dd1ddeb67beee762ebb2658017ed22f7ed6acf30f34212472b26b2f34e179b31efb1786143de208008eab5597dbb012db7e6de1cf776e2b9b5046bdca1577de4a0413c9712d077e572d695199e92d03be51ffc4ea06490e0797b8f9f0e00bb6976a38d31bebc17ad11f015450086927a6b9b7d9151e211027e8afb19cf300d2e7ffaea5569c81606f0bee5419ca40782db479a55036916772fe0f1b23a55cedd009accdd0bd25b2bb86dbd1bfca4e5845739ee06d044e536fd8b958d68b0aa76257b09ee03fc7d53e3038e5b74da307f7aeb9991b673ea9f0e0ef8d23522e355f01cabb21f90a5e8295461fe8f2dbbb87db328bea6f6bdc29a72bba6b02c2715bf7f6149cee8c4686045f447aa9779f1fe76b77506e15caa71822076c813b438095dad665ccb395f5793f450ae9fe9c72c76c60fe3db8d00b982827a5db948b1f30e651b296ff438dc549a5be3ce73937eca45983e1eee351a6e4b4d3bdf3fceb652706f52dc1219cdc60f4669aea090456c2a119ac1f5a1bd5b2d2293a3913cad8bf18f9fa027f4c9e8fd299ab902e5b014a93b664fbb14c971706ebbff654d613f515babf503022161dff17ac3cadc81997dc072558846a3cb8d62a56176cd35aff54ee58c60be64ab353eb286633b44916ee221f35dab5daa6554a4f7abac9ba21593d095dd7bec417eee9598f2a51e44c38652f97d7de8eb33f23eccd5eb58991c23f14dd71f338a55e367d9eda285d47f779a31feaf31c9542f786fc179c69eb6ed9d2fe189340a32971beda67b140a6c111e9996cb88ba9a2ff963e8c07b4d0e7d1c9bfff87d45e5b3ff4038b59491646e02666a9699b7cc23c0e39b789b68005ed9cb4cfa1f1807e13be3e0cffee72c906e817c834ba12b8177ee6941f164a6d03fefdf0afa2bde6563fb071e5a8c50b40fe5a451f9ea7f8872bcdc3d07e7a5c1f7076b81af5eca71cf1e15b3ed09bdd2ed40198be1988c46f5ef53bf0c807ac5d8b55b07aca46c6f6f102db45ace747d47f08c867eb70bf8e9b303831e582dd8ae34dd674b7ed2e47825a19ebad031ceb18e1a748ed2e947d8a9db8abf735e935787af8da5f7604fc2e2dc2e1e483df107a5a08abbb0344a02204701fc52c8235d59972b12b085d8ddf5d2430d47ee6d5deff345e8ef7e4aad760dfbd9717875891fd9a85aa1e1571104e07d340cdc3adc58befdc10715c194892dbf4c4142cd2deeb159623dc48f22b3bccebbea4966bcced0558185c1b521836171432822731e3e93f6ce53d1c5063b56261191da61cdc95ed55efbcc287770c99a5351de1a0c17c2c171f63dc4d95c32ec501987dec21453efb2170d40197567ba3517eb67775c7588b1fae0d1700ea323776f036943bfc8d3a4b5ac3e15b7e929db485c99f2be22b1aa0c94615fd1e69edc61fd263b52a0eb0cdf73a79f5bdb92cb9eabb665e504df96c834ab62b37b54fd812b3922bceadd42d94dcc29d62ba871f9edeb56162e326c0df20d597bdc4074f7f45fc23cdc929dbc74f99c41e4035ab8ab1a28f010c6874d2ebd8f5f2a446cbcd49989ac1007b3949079b84ea4e9c20789137eb50d306d636b66131be1f2af6db97323b31561d770931a3b1c00bd4416da8e6a322a87bb7b33fd07139dd47c6d638649ab3681198a14f4436671bf4c78fa46ae052cb226303380250870b6e07c5d86a4484f87877b9a21fa8a21dab6e0cdb2aac5aebe3ad59ad38173fe629fd2ce050d66b72a89f72c9e13d94b419ef539accfca9a7c8232da2cc6f989a51240336016fbae2ca4c0a4cc40ca624b3e34d93494343cdc7ac2612436872610ed61a9011f569ea844b629c10d5a19580c94a73de72fc61745f054e8048807095e77d4733dcc509b5ec37fcee6b216ac24b463253263a5bd192b7356c074f11472147e81d142255a6201cae2c6fbaa85d6c23757f269301ae842811d5a800856e160f46d14a96345dadeb3dd442cad5a30a22a053cf8fd340877cadd380a10dc20d741f12ff3eb6ead413bc5bcfe752e856fe529d41b2fbdd17818ee06d9262d5a7849f6513363ab130ee1845b597373833a49b9f710c919f02674f4f053e75445bb028ec16854c38597870f954f07b6798df14a4c95036e94514b524eb837d487aef9d1f1e1024800db8568f48c02aa7eb23fb5a9f09a70bdd196ccbf0849c3c1eef5fb64e2cbd8d3df7a5f875db84d513f2ad06efb9f88467f6a6e41d831172a9963e89d1745816814189a0ae3428a825de2a9264cff669b9d7385d8edff1b512e974f2bcc1333b8fbcede96a2d74c3667c34b04cad8216bfa582641f122cbf4dfd01329667624a48f59145505fae74cf6afda0770a4c53bd29349f29730baea1c1b7096f2853ab4a7c2c9dde922f815023e562d57d53256401a36e84ba2319bec4bfd7185ebb4987754ac9778e57e5d48eff4ad9aa8852f917a6f00dcd0b82684227035b697ba323cdb41b5e4a806541d2aeb71618e22b7ba97a5a143e8e72522c0ef47acc5c5af9f926e0dda4029af18cc5a8cd7217c2b1208a079b1e035c1348e9144897590c88a5ed0e1d69ac8b166ed3f074a2540c0a27616caaf7b71b8353dff38fa1f26dfb78a625d8a45711f3249bd8b74e9ec2e41d4bd1c179ad0b7e67b1e1a363c26e3681ca74693e0727bbe45613de0c4af94cba1369b1965fc3ef43af2a21578480d29e97f375616126771852bf07d03f9aca06644820113149daf11bffa529e1d2c04fc569a0feca62a3b125f2d9755f8e8267a69044154adc817dc7344ea1a78da6eebd21535a22ed03192b5e148d1b6cc41ace82d6f8b83265cce88113c5dede5a86f21b3a953ee075591559982e40e353ee4f86a13193e64bf64f66eff29eb6ac297ac3bfe5f500ecafeb0a594111f63a7137972efb8895dd285b21fd538c9cf8b33dae448050bf05fbb843a69efd4eda61017ced1289c40f0c5b65e8110e89da4985e515f2bd99df81ac4c1e6d9943b3ea3b6420e12d58ff14e3ddd340fc8b78bc8efbf1666d8d6a42bc7531e926237c12b9447228dc149547fda060886df5e16d5b1ba9495d3e757b835064c627ecf0038d1117efa7413d77d08e6791a47c835290afcb8b8c0f72449e226c1aacaba16785c4e09354995d68d41d0c075cd1dc72f11ca3b16a71e8ea101c6c9fd23c5264ebccbba620c7ec59059cdefe353d4689ba2364fb6ef29418713d5f38db10a4b111bc136a3029335eabb0b15a20866fabbdba7138cc4089f5127bd98d5e69d7f9cd4ea42252dbe8e5b3cf32f9c3dcaccdce675f2d0b64051620384476c3e994468070865eb39edf6149b551191f09c2d51f7401c1d2d31141e63bf562fdc62afab216610348cc07f4d117c5275040641b1fe294a61df7178a04ce02dca4d88857aa9902d8cba864e9d756030636a4ed7511baaf72fa806ed38daffaf4f2459e1bf05835cf436e52cdcf6d2004ada372b1cba368579fb44f6ae6bf024e3ee9ad46217ee8d3d5e0d6b06bf2da714e652a9a16b1561a1ed38f0d689ed98cdd9718ebc22ff9a89c872470e28fa44cb3739f381cc854d7ea96e8316b981f3c103bd006689086c75db95a4d6d02818c75b20fdbf6b798be6a1a4a85f67ebca08932f7db40652ecd3d4f99d125a335d1d6babf842f7bd8941cfe5721c714de54edafeae535a73f04b20962ebe6f2f3c852bcf4710dcf3b4bf83c451d42d4aca634cd6fd401b4fd7bd2f2834bba8e2d0fe5857b94cd7e061c30711710bea97231d4371a9193a84d578519408b9982277ed7aad5469e248e4bb67b1e1a8262d9b185d88e96e2c0f5434dc76d0629b4873e20ea380d9c25805fa3cddfcb33ef886d2d22f1012bc6c473bfc3f3c68a8f3d1baf06fa77d2d6c56c86b6a467fbd37cb2a0c234bfc502db95714ef105a4daf9a7f9a9484690487c59133ad42ad0702ea7fe0121dcfe57528990ec03b638b50cf3b6cc7bb41c5a018a81fc0d79d9433bb8624cfc81fa05457b3a7f518cd8717205159183f3710ddf29be4f40c169b3dcb0e02cf88a7eeaee18c5ecdfe616abc209d31e8267313186d07d732b2cb6ecdfa3ef733b26869dfc6e563bc05f1dda2d08773cdb600c81786a6a74afd06161816950fd0a63855a75b9969d969fdfaa49ed500d0d846eb8572ec5136587cbd0d7c8fbc51f793286c636cfafaaf0d55443036290b39d33f36a3e023dbac2da7e00b01be38c17744eff028f5dd6c3746b5112a567b417b3e483f4abef1edd125097e0973dd0f9ed588ea4f7486d61a6360b05d1583ce875c8d31bcdc0d7489345e055bf4e96d235640f60cdc9c433947b981a044512a4453faef36637bd49ed0219a6abb3c9ba4ada572b4410ef5e324b4a653df6d760a1649f7ef707cf28cd9bdcf2c83f98a39c8e5d3cd979d8661388377cdf18a0713bca251f5d3a743e6c9f916d3515816264eb918dd9396f9cb4ad7485fc8e3b6147b6c4e8d541b1240eccd935db540e4fd13b2eb7e6b0e22086bb6cf62bd19d1b27b93dabdb2b205cdc0adf01d5d942a1bbb1a3af015dad17e27e4a83cb6508350db5a19c657005c4fda5f3c1d2a99654b4964bd48150d94bc787d9331b34186206591f6aab7f2a7caee75299b362cfbf486da526482ec57655730f87a46f3b2ea5a1185d9a7720655507b3203dd331396754065a51935b04fd42cf5bc9084363f25b3976a5786463fc76dcd07f49ae7425cb9b3f39f0c466330f475828c240874d6ec0d7abb35a5665e295f7a014f7f79dfc186d02842431b86436cd2e92fdcb0cdc3b4e88ed6b2ae334500176f77bd49d4bd59e7cdc21fb980972b421731248330ab274cdf1fb88313bb0c3625c1631aabb183442b30c46341791837abdce2ea005bc5519574b071a9a25e3a476abffe829bcf44ca75d9dd08cbb950fcbb81040420c6275c161a3d48d760c2bbf1f52fa16078c89374bd3c50d468d4da846bb908fd0fe790d40a9c5a73314ea666ba4e4b97a9b63eb4db48a1007e42c2579a4a8cdb6051c2c254409a3bdabb5a3dd70a117b2e153b2f79a5dc20259dd3401ece130d58eaa960f9334115b81f4c5696854e66ad1e4936ecd137ccc24f980fe4a0da31d97014eaa3cf1532519b298aeccbfe6ad4aa9d147a2858763e3535b66e4269af752d60a24779e20dbdb39bda0d05af98079dbe9cd1ed2c36027b48a7419550568f6e13b7d40f3047b32e2adf0a4edeb64479a4770da594b468250ffd39f609893a164c084d0530618d52f8b634b471fbd6190a22b89d49a42e40af54819de6802e1d16912edb8b445854c5de5c4057fd095d8d39b2d75efcb6d688680183da0af42d390f8b4e68feef41bc545b3c1ce2b1e920a9d1889143318d2c9c01f7f9268ec6955c9444298ab849843b53990339402dea5af27fea00901d1478089620cd4ba0a3e9a8c739d1bb76b038a688e6e8e5f91cad63316e5bfce919856e9ddf0aa912745afb790ec93ccfd13a4e72a2ceb4af0f05f5c4199417ea5d2f9971668c31c221e308a417e297694f7e7944591005accdaba7939dcf1e5170e75154264a50925d77bc9a6fbd14742f3329eb788fa283653e4afc123b7c57c6e094aa17c027ee32962b7e60a053924e3f0344b517c3619a024ef754d0fd859d2e2c874923232c62d93b0b4ce19bb1527f49be26083b0fd5d16e6473fbb845ac0a944627d940a17b985c690a798a53ff69d5a58bf46addd719ffd0683e22c1c2addd7381b82f68484bdf5d54d4245093810a8ec09f6adea6d485a2436f9b6e6bc9606fcac61963ce5811b0983d5707b8a51749f68f28e0653b7baecd053aa5fd5d2af99c4c47a3167bf937aca845f649848903cef14d822548815dd12d89d6b1fad962d74091d3253b26579729d47d0bf27eada67b8773b75088c0451840f57552165d6e679f1c4ab42d7a970de26079f114fd032e61c0c7e71ba9a4462fd8b05a30d91a92c53b5561152be55a03c22d6a197e9fa188625863c62ae60c1420051595e352abf16b75125d47ce77b0f284674d131b4c9d9bf4837942b18a114ec5c2aa8fb884e39e6bfbc74b9730baed65c9c76329cb0e8fc4fa8ea60560486cb441b4b69e5550d65088e7a8b5b8469c722fded466bfb94eba81beaf9de39d1d7c98d0bad55c57666f928e87ba21418bacb4a8f1b8a26a7d405316ad40ccb0b927abe0a7fa948ae904d10b53326089db05cdc4d3086826ee41c5eecba99f5bfec42e6cc413d312a92bccafe378ec2b9c69ae9a9bffd2b8dea19e29775994cf0374da0dace3b63d4b56a931fab869f71f6c94ac7fa34a921f2c59231117cb33b823cece457a47d68e6f9a84088aa08b9bfea81d6fdf85bd193851801c423665992f3936ed869e8d7267b8aa2e0d0fb12fc08ff1a2f59a4819ee9bf2863fc7a8cc09ed69429911231551d330c975dc78c31bc7bd69b184b4c7f535ca8cddefbce683b9d0b221d2b4eb0a34aed877dc201d0365454f4eb2740576f2b5922f75a4212113e2b72d97e68476570967387e0300857c84d96630a992d48d17fd388072fb5899220d42cce7159f188eb0c26c392165bebbdc53f5532228aaa6056d862d75c31feb8bebb836273240c4a7aeeb38e0566c69a1e014fd2ee2251b2df7e24e66031706dc7ee9d0645336a259118ab489b9784e9c12eb0f0589b9aa0d9d95fe6ba2fde8069ecf89458c47ede3ccaa649495f2297a1b6a2d0cccc1bcd1e4f39bf086e376274d89549230d83bc39e5bf43f56976e359ab9583c9ef08032e92442d70980434b1de87a89a6edab1bdbb09991a4db3ac16664f2f56971ed2de150de69773908fbe3db4b03d9b553b7f995435a2824b32c23dbf4651071322f41c8e0cb6ec0fcf711f2f6280cfabc4ff9684ebcd1eb70ed8a8e634eb01772a2838d6e0b7186ef49d01cf4c366f58ff03a6020d7be22a35b18567790e4a9ace643b5c4cca140cc1c970aa1de1252fc938bd29873b9116bb91a57722e567a3c9c76089487a82a87c073b43cf0d8548ee2caaf93fc22d2127ebde6c94012674551067a2a829696b87f7062801ef7a0473cd0fbde1a9514286f188647309e50010c46410f09018b78e42257ad036946bd475e5d70fde931fe2627004d020f524fa2c504694910afcd3361fb8f47888f91816d79291d0612b24332758f6b3f909f8b976e8bc87f79f358cfb69d1ccf610b5f7f20b5b6dd739b57c9ef72df93750d1cbe2e6c47e7e28ab36a2e80964bd93d84cf8d751a6342d0247ab0f2ae2c462a9f4f778291a0650f88374d6b9e71a68b0215ccfe297fd97df63c97c08a69a7118777e64407578e34e2922d554fea4fd3fc3a06d8c418bc2466c7d0e4cce94398557e6584fd5a14907fb6f577b65ef7359b34bdc95cfb196992b9c4f1efe027baddc3fc4b4bb8b7cc9ad816cf7dcf6c38fe6dde15bae55696f640dbfa68767d5890e7f69feb3e5c1b8f771ee665af7889f775fcbcb9f10071bf146cee742895959d0b716c2c0ed01f336860f1971dc70719452ef01232637eecb26d68f1fe29dccafaee8c95f9591bed3eb3ab1a32580b03ad773154fd3224c8e2757e339e2fa930ce7b8e5eda33b631144595f5afcbbebfea2601542f8e4143169d01069dddbac47277c6bcef2ef90ef4b4ccb08ee4318f8c1f80b525b4d041a1d84e496682f9cf6eb42e81ada4ebb4797c7bc5792138d1baab214fe0b3b774d1f75fcd67949ec32dce3bb1f051274df84a1e1cd810a042fd4e23c27846fbcb7db63f7ab69996c55835eecf153ff1cc8dbf5eec69c444826f550b8e98ceb4c0dcdbcf06f6c4d8139dd7b2c8171f5ff063b3b9f14e49f99d71792466bd1355291f09183e23756600747c79e3ecaa2ac26c0bcea071214f053535fe45e28e49bbf0b49a6bb751474c33d83b268187a719be19f0a65612574fb209727c3a0e92d898a195debd4306a1c425ca9a03271328a6fd9853762a19a953e0db6c7e3fda362c70713024721cca3069545cfc98187645cada4858b248724918097fd761f47ad80e4e6cd75d4ef6b7ef0a0268b5fbbacc52aebb217defeb15bb4f37993fa1491ff0ed63f25e21e0bfa009070c5aa558e6fe9a520993f48c011edb5f3e7fb4110ed086060ae86046d686b52b42f85208d7de355409b59bd91420b7ee070293de16266fa038700b75845833afecf154fc936795e4fff9bff834ddaa86591e8e6488e3a2e9725c134ffd9b20232c4fb4e65f1e03eff146e704beeefc39a464116e4dafaae607d24f2f9998f2441b08ff4e421dc44e580adca4fc808797069d27087cec03b4f57086f4251ba74074d790360d27e750890562f832ae35b3b0257da49f0c33d3fee7ebdc083be22fd3bfd10751648a3460b1ec2a3dad7bc633be548b6702305af91f3f8e6b60beefa0ac2507d65c378a24cdf29f0e7a7970b09d6c9955629d6c6d342872b3420918ecaae624672dba8cc8e9623d50e3b93dd9c1561be2f5edb073a7cd9879ac68da7540d11d005140a87926891c54042d8d1d46c4b96ede0456e9bab6765b91cfd2612a3704cdece37c6875d69a04cb68a7ae09ae39ed801be1fe191bbc19c67604550485a1b6f5821b30cd9027b63dd0a14ef4cc13a7657ec109453e0c18b5c5c91ceff43a858fb62e039fd7d28dc0647955650a77e6769a5dbc773ae8a50a2e4f287381a03e14a15d7b66e9a30667d8d98e860d07b280a9a4868b1f1b604c30dcd35eb343a2e8d9294e37cd71b8ef4fea848338572a1781abc6fdf8c2dd5c262e84ed43c4025a0dd0a701611955278b6b28de60c850451776de937e6556277557ae6687c78df454d527637a8486d7864e7e76e18fc64c04cf3db5d13854fbd40f6bce0a5824a1acf57c349015a20773fe0b290d78bc15ee534ec0fc997823ff4f28e3320ccc0dae7d8fccb593c58d135cda5cbd726971935b81a270aeadd58eca4ee65c5b9b5fd915fe47eaa6e58b4a7e0e634a6d540404a3f4144eba4ec4c17225fe405b1f3bd7438c7d54a17312738247db21f480c72d62270cbd12c1889b5367979b33d345c0c0d32a578fbb7483758ae91ea297974a97ececef4656a52342dd4747e4cf07d399db0469fb83b5c816eda8fc83a906b5011d727fe10a449c2e8d8bee45049bf84162a9e964697c19d61f70aaf0049d16abc05b3ed10d3df041e8b69ec5b559d82535ceb4c6b7d75be956103eb385dc20ec3f13d429d5f9238cb46e7226a66b0e2577d6baec9a3261a2cb2b92ad375db02b5932d6c5c1443ea8eacd4920c9c78de043de7eb40179d16a834be7d7764caf85bfaf1bf48299a8a1916d07325ffb78fc7321084a348d67369b02edb3f095a407bef24435e3872f3102b4620d93f649d9db238510ede9c33a622ada6154048885445c3064f0efb7496bee5b7357bd91fa0656250cda7fd27dce58a59adfb5bb45d0b1574647905474d1a86e22430bd049a7d1cab94a104e5f00c0387c3454c4427f63f6db5ab12ced64de3b6c434dd509e04ec74f02f1fd92c4066f959fe5c9236b47c38ed4331e946d86f286776a87e968aad81d3d4d7af41085cc48623b9a11f7294972a86e753f98abd7d91f52436f819482fe724a2bc3aff96d73662250523c1a98d8341911a3c63f106f138f6026da648852b29b07e80850ed78ab2d7ef6010a753a5ae829b3e5e6a5ab8f5f853b15413d3757076cfdb0d1c2019e3a4b6ca68a7294fb7aaf33cdc6c16dedb610ddb52bcfacb232cbd4d9d3d71364bf56ef567aa3a7ae02e9634ceb8e7843414cdcb0141004e51389ecba3c6de4f869ae70e86bcd5d4ea49f738dcfbac31f1a09ed4e667f66bc796e095006efb191d7e2c56d182e1405d6e1136a7041e2f401e3025be8a0e08722abcb1b902e075b86ce5aff915f440adf64698c6a900e5ceb5a72dd6ab524b4882502acefbe1ed6bb20de9c984055060fc0402f148e065435c4a2a9db382fadfa1a762216ae4b3b733147d8955006bb353167c9fbf37f459978062ce13b03a99600762e128e3b9d6cab79ae6a22c33b9cc2eb789bba90d0e75ed971dbbe0cc364b87b968c5c0a819383302df4477910392dbb8d87f42aa55e00f3232901fb2c5fad973e2d630d1490dd232d3259526ea46b807564701cbb28c3671f07bfd3b0e196415689eff36eab0329a455226c21a8c57d3e645a6379ebe66185c8d196ae8f91804abb24e44988098f50828a8c90e9e5aff90e425ab8ea0d06bd77fa600e4a21be7ab3901ae8ee45bcfd0b33e3ab319dbc893758015d4406ca302f39333a671e1e208358098546b49ac6a0cf8d82a723375aba2220e33b74ed3212ef4a8a722db35d564b880a6b448afb030e3e863981fa234d2bc30ef744a60ba0f37430693f81097d5a4891fe63679e201b671359b68891c85a3a9905d376f29f5d3810a05dda80510cfc5a1e2488792c693c351a4a69e3aa456336892f6c635a5af7a389914b594e5e8d9c60872e0723124d6e8c553c1e30d214d7ade9cf84f38b20058e95b31468fdb65d6479e93b2f23a907b028053fb4178301240971b7a41cdea40fe7ad44b3778596efdd99988d00941cf664daa7eb14b887f7f7ff51e39ea969660a5c85f4b7b29f416a253cd494b48439e910b029788fa5d0dd0aa5986abd58704e89e5cef7759732edc15260aba2e16e488e73f69413c804097f98948e4a850789a06f388becc4d900876c1ebdf88d15fb8d3923d21c96d22ad4030d20bf428b2230072db8448fc2d3277cb241a6a37da8b28bf7b5028567035f1e9b1d1d962e3cd8c6925405d7800ef6c69e3b9dd049c69ed5a5460bc93e69d5cd9c1d5528e56002ace0b5d0cbccd19f2b6b5cd72451a3cca0582e54b89deb1860a46b7d8aebfae079dba5e49077f5bc947801f97df3a210f3f5ba76c8eeb517e76bfc42b1e29e1dcea8e93c01354cff614b17ea265511059e56ac68556ebad2856efe83d0fbff09bdbc8bdd184029292295dfb85abe5507aaa04d7583975693454ae4711e6d1470437539de02191878c9002b0dc46d00d6aa4d7f6e6301ce8ce269d92453a071ce314729018b7449533453738f7c73446e96317554713053eb6a30d910059a580b515611878fe8a4e4d44ca6797aaca8734e057f1548b0e09669015e3d004408348ec08109e4a280b80ab86801b41748c600e319287689807b11b41ca13c24e84d42ada1a823a4cb228e1a26d507291ef873a5d128fb8f1b9dbf1dfbcea1f80efe0f2b41b6da8ed1229d056e8b53b24e3d30385a65ea028047eb4bf0ab1aa1ba303e1d399d254e3c7e8ce5b4f4cbc9cf51f8cb5ed461fd977ab15bfc2f47e98e199e28c58e934f615aab8211f3889eca17aa98de7c8e876eaae739251c968104e554e2d945bfba1d43fe5063c9672535862be3274bce291be4c9da6e6ed6cb832bc62101a38738ac6d7f329b448890267134d9859c812c296033f79350773ef6c9a238ea3cc0ace4fac7ee786bb30d13c512ef7c382ae178738cbba8c0e1028fad7395e04fefa1f461891081c2aa996c2216f1ab320b79d2ed2fc6cf4f6449c2b2144688052ed0334b02b13ab09453216032782519a0589e36481febbfa718d949551bce01f6313e88cf687556fc63295e586588acce25f157c908589d801c2cfce8ec2aa137804fa38e551ab46511290aef917ee9b3d2e2d225a53dd6a9ea235e1c461879698c80047c1b16a446d9d97bab5ca559d7bdcd8d98c1bd2693c4a8b0d904639aad494fa7c3e3750ec640d618ad546f433123948d9cd551eaaa8e5adc1960e2a54313291921283bf8f86325c9bd8f9e5a5c783a04833699aa4e459670c80f156168e21d8f4ecb780ceaf3d640473014b222dc2a5b1ea94fe198daf88cc1761d54327b60d73232144bc7b11258e37b65b7866530d05066e8784509b0913dfce09d3ae138428316625218de54e651aace3f24b741d64d358c70506ffdc8fd4e95a0ae44386ba5b848b7172f84807e86998ab0ff4aed35b436b10a7ac4430f6b44bc324dffb17fdf3fca54c0786596eb0efad69e917054ffb0b501321f944e9d0f73ab6b0d669b9649362d87fe88b91fd36a775817827c2f0df45462622529dce8f8a8991bdc51513a4daf1f4a1ce15b82caea5ffb449d1404a0db825a936bbb6fd802f75e1600e5993d6601bbbc41f3fe67c2291efd32e172b9fe2cbe82b7cdc94b1ada65aac208eb3bd612a636a475a0d42bb1881490429b6f4adfdd9d8b9bdaf327c9f442abdf014b49402a06450e46c848b96486797f6739a4c55fe7d0ed695ff2f08fc0149d9ce511603147800b8d00b71801eeb608b0a3408a34a96da2b485a0dd8a64375e9d2c10d816547c3c643a41850298c46a430ce7f2f671c49c837ecf5a3afb503c9cef3e20fcc2f9cb026c4f12c66cc3a3062c0e23ad62608daa990e4c301f4198f2197d9e0c75379c23ba456e08e2094b1742488fe23b31f1902cc3c74c8441089207e634a95248324730474a05c5c99af80eb624005f4244e50cfb323e124c734e8cf653300075a492c4e4b72f68f65bae0028f72a02911d33c3cfbcc0cf417be925634abb2c39225e6fb849cac33bf792f17cbfde2870fe8350e2efb65c888e1a2451ac1802890b1479703e4d86e7f32312f01da3c0f6f477d2c6c1c7731bb3e375b6fa47e9235a91c89d51c2defb49b5ffc3ca2eeb55419fdf522aed95a3fa1139d1cc3d547cdd6104bde5d6005779843dc45fa7a4b9725441ad629fe511c5007f0e646444bdb8f8a534908b72df6e033030f0bb1bc5438e8a3489047011deabfa2d6ebc32d67efe94d93a5e19056863200c61fb060b77ab51f11dec67386ed3b138fcd85a59a85e5b03856606841587ce84b51f2abeccd3bff2fbc52a31d1805892475d304611b8a767455608ccca4d48018bf3a60a87c00ae93775b09d7679c22a41015a8cd654e582d28aa9f215a7d20a1fa18ad13a20c8ec726342408bacb48a937d0494b82651c9b4266dec958b055624e347ad0115b0c82d8794f7f6c85840eab82639fc71c5c542bfbc160b945cc53d65055cccf5c37f8b8cc2dda10d6234eea2467605c4c51c3d3b26f7dec76c80f886dc176c345ae87008fc3a94b16a7d0b3e696f475b2790c678f897014dfed2857891340e58cef9f8e352a0866ea7c2583ededda2717824bfdc588198e3dfa8c87781cc17e1404f8c58bef42e7ed608fece19c70ebe487dd1a634eb3e6544c0712e979e66c52896b414fa17cb873252d44919a9ff4e0013393f8319ad65f1df97a030aaa7a5507d637e4269c8467e8a5314fd6c7b9f6453281e6d4dce8bdc689a25ce4e60ccc68347e0279eea32e506b2d0f97050b458ded30fa98a6c0db845ce783af05d7f15214e5a6998b2d34bcefad7cc67f6b7442127749c2973b858b1201437cce3da627ebfda22130da4e597b2d6c1bce1acec99049e10050e11481020b3a80da197befe467d5dd7d7c1d9b905dffb28cb8fad81f6fb40d844324d48bfda46ca4c26abd7341d3c918f30c6a31910539e24252525c22110e49664e59a42aec3daef96023e1218141a0f347402644400173849d628d0d96c352648a28a14a08860051d2d1ca894a82163f2061f1cf8c1db90d12393213eb022818b0228f386430249c04d0c18257024f938e01012c274f2c31f55eec0f241960c888882851d100e20c8d08d16d91af3013478e04ce0021c4dd868d2c31882dc8e104efa3e4bb514aafc5f8daeef552b918b423cfd5b12777474927474c2ef7a89628be39e05825df581aad6eb53a5e1ad6a5f989c883890c5b3f554fbf23fdf833d9614c28525654e1f54d248257f5452064642160cd33132c24691bb923cc7faefb9f937be57d280ac61071c4ad2e05022712631c3b3be9dff8ff84854c77c3b748cf8872440439aef89248049a200998de23f3a7a895644be0a52fa41923c21c904524aff45319f12f87d19c5cfd3f10fb4f4bbacc497902b4cf7c2df7a05fd7c1f294cf879cb4845660052dea87093025218296fc8dc10a15054464787cc1831273c2922f2ded0095228284c50235f341281c47454af23b1a5c56bb9f995c5dffc7ac0aef4d5450fccff5c16b962c241111da4942018e2eb2902259131fe86c82419e73887f5fc4c418475082452caee903458c6cf0b8ffe27e54d172ed294a3c1059654d20804d8b4314943404e4c134065d2c025502a8932498e2414922d1138a111cfc892226b10c1a24d1c0f28a410398814417c1ae001b2c68f33fa68c3078e02b6e4810601541c00903ba0d4f143870d72bc11872cc08e1b36b051c71a50d230730615654442802d630411461b30342fd670b1c8163e59448185bc825711071555a4580305264ff826f460628b126024917344124640214287215041ccb046fe30830f281e74d8014a0e49e090a3060c1bb6d0a0c70c3e064c6058f3429516e260812f49a528927cc645c435a1e66a0306f16d61458244c50a663c2847757436fc1d2fb937b80d463f2a282a0092021528a039b3c50c0e993663ce10b3a6c823f219927242984282e2008305d09a2f4b26f89f13bc74a15c72b6283e507a1ae041ed984a30694191d04016282344d1c9d1e8dea24fc81e2fdd35168b1a4721544e29587c32efca1a2b67b4110127842d55d050a10290295040f8f9c0863772a4073b1dd85087142898190ea88844005b3608a28d065a066b16c1c0278a28f2021e87055502b006930abc1e146c0163829c24525074c0414199610600a07490004a1211e48001c1163d1ef098ac71a04a1cbc0119c5139f45d630a0b55920884c6422652ca08c1c821060cb152d2491430f7c547969549b1837800cf1614342035962c1a10a12395738d882c11d0130809c80caf1861150c0204e901a9d0205112c76ae20c016121a40048b9b29939cfa182491d3215fac70f2b832c08a1866e31540c2903880409148df6bd56988c14b030d2eca98b9c41229253064c2087f4829a59430e45d4a29331c3f5cc0610129acb43ef610c3cc732ce3e7bd8e3cee8e9f77e471575aafef6693f2c647ca9b1e89d3470e373c52deec6ce143eed1668f2f2426568b2db1c6ced37c9c3d7290126b257d9c3d2cd0e3908e7fa03c00a9f675f36b66ea25018bdc54c932829447622b04c37093f246e746bbd9debb9182524979d2e066fb1f296f749642a0109d12078f3112078f20898307178983c79652a296baff79a4bc9122a5e42efff1b00b6f71f1f545ca1b2c37d9955104575fe43ce15fe240785091f28a192edbc683c7055776847c827686bae0f1445ed901b34324e6cb012cb95214c667e70b5018a14f8cfff11f0c62c1fb8ec0ee7f4420faf9c020296f42f85ceb83093d579acd7be39851fc375534787243e52647ca9b29204879f38194f2c94d8efc2d6fbce9404a39824152de48c1a4947e1bf325d4d11141212d5aac88e33f1a45d0a512f9111fb907bf51fcbec8bf78a8651441a12cfd8fd152c5fff43f4654bda8aada57ebb58dbf8b080a8d2228e47ffc4fc6d58b968cebcd2f9c392a90c30012d3c191438e9c3820c189e38b385c38714c4c078e49a4c474e07003d3f97094812347623a05684455801c0aa0c11b84bcb105a683f3c62825a69305d3790385e9b8c187c474dc706385e9b861454a4ce7124ca70d35dac801d3694308d361a38d94928d2f301d2f71d8f06103024c670d365724ce1aae35b24889e9a8f1078e1aaed305699891062562f8c7df023f0cd3f19ce7e601296f1c90f2a6012c8d7ef344ca1b06a4bc59409abc1b05a649911bd4e974736362e47463ba915fe0a05124b30ec4301d2339eef84390531123f28712c81b248e195b883a71fd5144125d60511957c84cc43e900b8bf3c42f0495be2f82ab8e852fd00c9689df7b49af9187dce5e2d8cb871042c8953e8ff7108431461b32f2c8448e43860d524a8e711c32b614bd312e91ad3f7a20ce1866641ecd1859e44f1a430229331c3182c4d091452d2fc270401871603a9818612021a56423454e0146221f07030c30d6484c071b7995ff5afa48545038386048f0851c526223c7d2fc6f25722fdb18826066ad56890ff99f34df7fdc8b12c62fb639249313279a7491b3c49a2e3e8a2d6fc444cad325529e2c91f254c9e93bf105d6a84066012c9eebd53b88750b4cc98e3e3054c27e0b24c232917b89abfef19067012b5237023851829240152a2ad193729a44ca93049e3010c683a3f83241ca9324529e22394122e5e991365be0680185d20f7a420bff79accffbd0d00293328b3fb2ffd26327852c6e04a43c39d2081606c0428d364f2020e58911294f8b9c12913858a4c1c182754244cad3216d8a70b040cdd1e338e8a4934ed830c2e600524a4c0b86e95c193d2e8c1264d84021fa902b2175ce616c649052624360c004c1031b1f29bfc88932315826facf438fbbc68042e2571a7fc83f0ff392c24f848961b6339acd2be2151c2b70ac7800fb2eb08bd8a842e25f09897f32e3f7551ff87da08fb8f4bf78cfbf88aa18e22ad0bf40ff12b3a58f14b65a2bf1e5123d150a4cc1889c2205de3f6fc4be11e39f2762e2175d9cf531f1fbd8e7b9b00ee4e12bd1fffc7420b6f33f6ffc380b4bdc94a5cf31d1eb40cee3837dbc0a95f07f9f870d8101330406ccf7e0f7a9604360c00c614360c0f8df4496121331a4ce39168bf780609a50404a898961abd56ab55a384c6c2931510485b2a525bec8fed1377e473f3c7cf1255858a28c94fd7b852366658925a2181981e16741f4923c8ff1f07bd387441f32fce13fc343247a384222caaba5001e3847302119a803e7882d4a18118794639a7f85872f6e0413a2c758fc8b181f7aed605f0249232228e28cccc51afa5284045ca6ffd68dc56f4fbbede6ee1ed7bdeb95d6eb4b19a31b420616cee40617ac243220c40b0adf256e0881c856432756883940061670a0c8184d88f0a206caf4401f8108a1650821296590135a3017ef2aa5ff6d724c108648794a83d28c5ee93f89c5e22d510cbf2706b19298ce5098a1a10d932245ca051227080f82e820882732fc2a2b58301dffb1882a2b2a9c356d3012482061cd1b6bb69098ce9a346b5a386bbc48158b1f7ddee7616b72a41457225f1d619fd77a85a3c7853b3a7fe4231017d301e2c90f993c82f3031e52ca2a19a623e503bdcf337afeb77a9a1f1ec0f941fa7009a6a342038c33a49419483512c7070da4ecdf17c5affa59c843b0b3d240030d34e000cfd2187a2f8e81d8ce04383c4092891e0f7d9c0043a641802978c0c122fe23ff1b70e00331fff1a59319529eca90f24400294f6448791a43e2ec2084b383cf298c1318525621a5fc248e0e71c8bfea236fe9807d61cfe0e4b0458987f65302436e25f2550e1a48799ae224451447b03c1e8e23b038387e20c6b55e61c738267e0f7e2a70fc3c150d2634589fd80a39f693402e04797cbe171661fee33a3a5ceb15f650c1f2b8d1f86a19fda4295f69e9862ca09a3b74549ee5adbacee9092925eae4841a18fae8f2fea989d312529e9448424a94202f8f793ce42e895a44ecdf5f6156964657eb732513971a3849c3c1f0559aad8614c07fa4889491b4f0d1a08494344021f23f2118869b387ea00f51ff493a3a2c3e8245208b88032dfd2f4be14f02bbe8f9f8542b062c22e33d9fefc11e71e97ff19fe5f2c222b0c88f9fc87b3e2038e46af532ad5e4604bbef67045944e1fff97ec6343fe8779ce87b2f74b57a199fc43f501766143950087209ff27c6b3449ff00bed88df1bbd346028f4e33f90c862298141e217f94a48e423f729da5ab8c074322da0080358f00cc05952449ed24879524a7a49791ac510cdc925e5093caa42050954895e5645c45ca8a2c2ac8854407001b3225259fa48aa254ea5caac2081042c7c751410aa004a974889ca44898d9412fc507928a92491900f79173d9f1d2e5908b6b0ff89b00e143bb68365ff037db0bf1abd30fc49e087a93ed0a803fbf702bf6fe4ab9792fff8377e31c3b28e89fe2771cf415628f2b005720c14597c14b3a3ec63565c568a38d163712ceb38367e71f456a3c7e2e16ffd952b0cbd16c63fafbf3c18ba7ac628fd2023147e0b2432c2448f257adf0e8695807d3258f6bd70f4df7779980a464628ac3affde9525d6c745b07716cb5361f309d3a3a3232e7d24958ac5fa4a3a3a220a2aa0807d2f5462b13c2b57c0234cfc49ded107822226ee6099288e9fc7fb77893f54fadf3862a2eb7f244c05232410f4227a57388efd90fb1054c2fc1f3feefd27c6ffa3cf5b8d60901fc1a09e5104bd2cf1f82f24865959b2e2431a856152796bac68903a3a3a3a744879fa5a52ca3252ca3b708e56ac1392941268a72828cc999fa1305e3023306282545b971d22a20d8cca6768878c901855991d3046411d9094524a093ce9b3fe106624bec67f647409ce9f44cad2e761369c56f0a49498ffbf0cce9f37918cd9220573c105cc7f8c042d980b2e6055a8885ef67fd8e178d4eb07295542741f29e5108be57d09e28ac0e15a5c14a18d8988b2d17b81e3c8b16ca9838d45b1036c63a2236c0ae793892088ad44ee651443ef15bab051fc9e0552c15cff231d7d1e182c13754410302c5bb9b00ff456463f5c8d60f7751e56850af64516e782654646463f1b459025be5623067e9808f66fec3ca32a56b4146509412e548cfe4afc1d288adec757fdb7b0a3cf13ffd1f7933eebcab83af2589898f9ee8da2d73dd74e9634df7ffc8754308cbb5c1c0c968da2c7c28cb41465316a79fec30ffc2d50ecc02b47e088a50179b0eccae7617c0c28c4123da51fffb5fe8885dc6505cbbc6099911111965511a7b4a86099e8e260ac1cb17895cf5312b1cce7a77b63c85dafce7f30fc673e30a8e70383c6fe031689feffa0dfe241fdbb1428a328494ae9c46ad5e2a2f8f26050992738642439c4103279e09081838c1a64c420e304192670c82c21a50bc4aa7c40050b56e5030cb3f201a6e302312b1f6054304cc70aa6020a386456c0212324a97017188edf57f170fc3c1d95bc81414c6b85cfd3d6524f5c12758ab2efc11fca14030cc8d40290c8d4027fa40210854c052049a602b045a6021045a62ae844a62a1844a62a9843a62a0042a62af864aa023322c82998aee00bef9dc414655f04591d4b33bec68f87fc0803c934e04006769ffb2197061cc844ec8b9ce35f87b13837a3029112c8716c89b3c29f04627f652a626980819412b0224d3dd2b48020766c8048791afa3c96c7bbad4b17695a9d20a5c90a49790a639a529e82c04879023a6df1ffeef485c5c78f2bfd24ef48ca93093f5e3e0b9e7873ead24f5ca43c31d0ea694e4fc4f1b4c9f07b3e529e7a4e27294f3c320251e280b9408a61860364869418d8b1ef85e369e7b405e57f4a90527ab0c77b5ce87f2e1fe87d59414a9989e0e8f246ac033dfe958c44ce61aed68bc396c67f640524c2322397378ae1f7564a235411011be173c142c044c0feef54543012309737aac6cf1aa18acb1ba97416ef2a12b0a5f1b342900b86050b6665145fa191911196bd581c66a58acb1b31f0a3825d5962859ce5b5be577865fcc0d0c8e8e3bff5819cf53a727923c6f9c881ff774649464658e88920d881cb1b432fa903ec8f2f570bec4646580876aed163bd5ad8ff1db6837529a38b175dd680236605d3b182e974594929bb0875c922311d70a58459c1e1c249970c24a633be404c67e42b25cc8a18662eeeb96aa98b62988d69fef7c2712342623aa3a785842cdb07524a9f3cb2a58f240266e47fbe6ba968e97f315afa48dc48f581469fd53171fc47ab1ef22f624b21ff3c4ef430a58f7750c446d747fa492c8e8bded24712a77c51fc2276f479abcf7daf7b2df003fb671561999804062dfd2f1e67271029e50886383b58603abf7fe017665b68c0d9b2b445052925051267cb8f94984e15161fc1174805fb1ed43112b9c7dae99e0bfc44df8360c4ffb98c9f88c5f13f8241481d040a411617c39fe47d6242fe593d5bf8e2b1493a3a764c1d7345d5250b781ac09853ce3101c08491214ac40e1c2d101ccd8d0dc5ee01d3c1d950a4d492482931d1857116d8c1110c5d18e6bf57878d9fd7e223f7e3ef62254a2010081689e3c75d9e502a0dd675184829c5c8949219525ab001149bb02e89c01a26a871446892a51d905e1f2460c807214e8a9803123baac8f20b99418a19b4100dd28409081afec84249046ab429e16488ab9103dc608920b208347e90023a407ae404176a7729a802c516063441e30b0552b810c21b59e841004e5c1a04381c9084670e168e048f3020071b62f022db13dc7108140ed0e18d0c9840630b1b45f248809722f8f820893d8828c30305d2047e5c68248c4a56e002e89005707922490700f842930e021a183204232a6481003053a0d0851d6104522502472892e5007a58d0011073782a21c870a08609187852c4901851f8f852729126844448293454c4d0e32e05809172c9a001a8864c816b5037a8b1414a540d32057a140d334020834c811b15030c2f48897241a64008502da058589a22a51c2353df14f2d5023b65614929f5b972d0a9160a520cb37a84a55291c599a04462c3211aa8cc74e920842e6db49002aa8f37b2cc11891d5da448e0061091782002037e78430a624610379cb1d288263983251fd0d09152810ca8d02227036e9473199d914b83af002b60b964dce4b44400d8d8a13347054a9648c00c41b480010167429891e40c9676f08a7c48fdaf447ff4792018a6f941fe8fa3d7fa963c07594bdc8505800161fa60a10113891e0d3c58f365c8a3900f34a8e1871d376220c26702563099001d02e8a9c1ffd0fb2d90f5811e872d8d5eea432293b007d4d03201a12752ac2081063d94c08534e3c6095b94a922b4e504220431a39a800c031f20a0870e3a064b176cf2031a5b0cd10704897480266552da71e2014d5e8e20e03b8094064cb205922e56c043027924d2a5823045d041c10851bc91c682091c2591d40925298881c39a4d882008804a288c902acc904a0c19222255c6f5ca8e17221d1f306554b50bddb65222201e55971f221511104fea6329bf0447424a944ba6380b057ea8969428964cf12d28a4153c29512b29514752a23a29515f4a949712d5392951dc54a5944f54901265040ec0948452410a49894a010555235da850409d3123a55c33001924aacc1752a2c8c8a36fe4a2f791f8a862f114e248810d4da57041aa0c14500041a60d99212955a93165c66cb2c74b89b2c2e21cc82a1a43ee921245f4043574c2d2ff128194282d424b1fc9f39e9428149094a8a016f8454149107e24ef03c18cde276625f21f823d9f07f6d19b4206ec600787fcf747f19391120506484aa410e482f3454ab9c42325ca841f29515eba4889e2b2494954d47f9294a81e9e9dd45016d41629512568912812b27c7c55f443100ce80243a151e41f68dcf9402edd03c72c852f91ffb0e8033deff35a7ee7fbb0b8d73dff3281257a471f88c5bbf7adfa2fdabc4fccd2b7539374743eef4bf8c557f85d2f222951234829513a284d4aefa9505bf6b47a19ff3f3152a2aaa0342a9bc48cc2a83b464a944569c062bd82fc2f921255a3a0286a4a89c292753c8554d009a92030a9202f126565021c8912210469913a1f89a44455f92c8e7f62c02016ff3e60509acdb35e2099dfe2412cd68b4b4f75b5fc27c6c5c322579a0d048bc4ffb91c7d1e07f35b1e67f580e127660c41d767c123f22cd0cb67711c45052c62b15e5c8a3e31626a928e4ef862097996b7ea59627d6196fe975997fe17d4949bd37cffa1404045f9c08b1a5e9e90e20ab58c94a80ea448c900001810eb186fc788bb8c2e93cbe032b78c2d53cbd0323ffc5ec4ca812c9e6a33dd1b789647464a94067ff43eaffb3ea80c5018f81f86c58ae23f8b75411786c5da61b176a44459204a890ac0f7eaa44455908394280aa4f77e26f8e30ffb07f648894a05d9241d1d9c6aa1640a802dc15de261b823252a02140452faff79ddf79f18b1da241d9d6a9374746a928e8e4dd20276df8fa8e5fb4021f8792d2ddf07fa2d9e8683f92dfef1917b2d2b91affc4f0bfcbe6f819f288a9ee591d192a5ff31dfabf399ee31e4424b1ee41943eef23f4b1ee4d1a2a5da2cd58a19adec57965de96bbf963ed2cd2fafda97969d1fffa5bc61444a39764ec822fe7b8160525bba222f40be20c1887f442b04019a920d0681848a19401f9eade5853b8434150560d47192a311299b1462a60b3a788000060ab8018dc885021d232c8c78401b555e67a40892840b1c5cb0039618849080086540c16010302069c2892927ff001e5278410132433c296203312c1e3031b3412770dcb1c0263a16ec20060d348430c001031c30c8249cd4b143129a0b39703c84c101251830628503475eb8a3863681b8ece8628a084a2831462030c471668a160480c7509b36b4d10c31a410b20040c9c6840b102c59a4052438c9c3034df2431139a4b106262074a14501616cc08746252a318026261cf18038661013c9a3123d4c18814d0f0f5904257103ebe4a5a26cf0ba60e86cb1e00fea871a247e1021d0420a01b21a6e48118696061d3740f2001bad06bca0a186fbc2b80207958c391347076d02c9e38c960408920bd9a185546b8942aeb000e15962023e40018527031c3764284d0c12e0fa200b3f0cc0091c29b84087027a2062c5911551008092cc5bf1b9e4260b261461d3442652ac74d0c38a8f0738b16120f082ce30c40749347c5094c4265aeca8a08415667cc83b4010a290410e004e8042004d0660c553153e56fc1ba864c2c60f2b7460524a4a85d145e700288c318492079c1183904cbca8220dffc7249850701448119068e1c44c03020a5258cc40003a7e7e40094090070ba1242f30406402aa8e497840c4088d157e5845020940155d5861660e2d1c3864657a430062045901e39222f4e8a2014a685815261cc00b20d2783602d0b20292456a78e055061a1a103141177e0462c21306108102148a148961c7842ea668c18a9a053a4a8820c01fe312a023413c919486080298e2c75050047c2ce9224cf7dac40806c64c9e8e8626e8682d400b2c2070c01663b44c9b1144ac8081c98a33ae4a1279e38a6bb98831812576608d4b459ac8c0c01293fca046083ca0d064d4808e023450c425927c3033c9640b1ec2943187255368ab283e8df0e1c3874608254c0ef9034d111624e08c231370cb0a3d1478009a3ca0549088150900b06a027b32c2139244d98391277fd8906a8345253470a1068f12520ca2c29123527020c9165fa8e89068220e32741cfd8429438c3a7ab0e280191d585d30e9423f5981e1c00b5c1e104707423412c91908582102613e0d5d6680a08340b65870c1225924b90285c6f43ff81cf2431a6df43b8c443c9cb06226f941812e422694a1790e20820f9c20811803b28ca1838b3114fc200032147049227cf4f0c80e1a38800c130ce9649be38e009021858b244e72e0814d16134ca1c2197c6cd0002ba09c11c380148f4cd282044b1a14187e081381144bc6151ef02448d1d070c1e0f3c1f9a3864696b8240a4a94acdc27a818010d1cb2267f889902870530d010840c3b94c04242072e5cb69831420165e7d0660c9c1c2e72189b3470461b563217143408ea1c2998e162c6920b7390c18014331c0f454c2b06524690e100293ad417b44400c10194286109339d0c618600b6cc20542501a121554471640510c95779c01906b0c801010b88091f5081c304654821c71fd8086164146d26d983860ab818c34b022251c1a084069d747094c30a6128404a1707a460898046e62201221d1a988388331e59a39036889c60a8640e4bdcc0600f283aacb107124e78e941c90517ccc4700410323880851e6be441c50bd713420c0089147486e4f84292310801420b0565b4dc78628c44a220c022ac9c323700404d064f2442851332b21139a0c0c1141fd250a40894225f8461838a954188aa0610280abcd143181d58e9c4cba1903b5a3081d1b68a16440c65cc6881102f87299a4c208501ce450e3bd8b4b0c6d4c18638de0819b152c20461dc5023881308d1049120780f72a831040a043e70423480704315415cf84754cc800284cd253cc83181173a88d14de1fd082291261c21a22eb4d2c899429de03e6478f043058124c1c1488d8149cfcb102d0e6c4102107fa286211611c4913b3811014b71e30b1b420cf087107770f2038901226014829414174919a12755d858210d2107d88018f5034a154a7d1037aca8a4019d271ac92e5063c768d202e6894cd8182483230b1b073848e408870947504861833eda8022113e96302285287514320464c819b20d10832afcd07088c109139d78d0499531368f1028e032ec903a63103214c8ca464b3af3248b1ebc3024061f76c802024f9c9821cb053484b1011a47d414616903831e3a322182bf31849dc01021c8b8a20178c410c90ce00abe851c4c5e5080e30dd60f784461481a60aa78d0890804782905f16491212ea18212107a38a38f2d5877d0e001eba7e809ce851049d4d460c8068bf061c7ce216118200245687193a480107770b245230a6072011ba928549a246d26b2744148036a1c80861370f4087c8080088bf840c4217ba079e30834a0146244142b524cc1d84001cb246af670638c102b301001220d490c300134233c6962100e09932bf80cd30ea966a526cd060fdc2f12d0c9e9a5041f36e82a57bc50c683c3035c00175e3f7280a0c48ac20923c64784b264170c00006b7c2063006c9eacc008182a3c8047550460c1d113234419da687381153b3cb2085203d2a084921506513d512130c21206a8999c073800c00204026c48a3851c292e38e191299202150eaa3ccac0788c78655143d21821408188053d12e8b0a2a2868d2116a0d83f34f051a0015376206189b08810387aa8e96ea07c1c614021910499e278a4020314f1c62d028b921944fc70a0934e0a39214c03908491a5101c2ea9e2cc23919849b5a1003b84c0c1b4c50c05723eb852e68696d01060bce0039634487a9c818201b21095e025f139a18ea313768f0a26c8a1da24c9db008295188670227008521a1e2801742cf0c1035e66bca0e2b48f6e50402518862a1a100104708882035452152544820040950b7836817060f87983274a18468879b1917c4460101ffe981140d06386951eca98a0260a3f6ec05801660e3fc0c8830f3414e8e2099e0ab21822641327b2a442c051bac8d2c618495a39601fa14f116a2858849101a850c5d7e34a0c3520d122ca22c61928784ca27ae30b509bee084f1559104d516601dd4124382bc81103249b3420031d6854e0615cc1c8012c2cf28000188d64a183106cea382301117448e19237f8d84406e18c69c0174dcc4cc18222c18822900082794082ec59e28806f6f81e4162cac456d08367932a28f9600891e2f87221728894444e3021e511f2871e340a8418d2c6902b3210928514be64ec3ccdc7226209ffe83dc0b554548814bf9f1b4206d922816402a99b878885d3d6e32e63e9f2ee1aa6b9fb7ab58eebae88e11516443165cba46c969307750b6a34f3f8a3172a79fec39b5f3549e512535384d4dc203521a882e97460f7432a589a90affec7889e6b47f45cfce863b95a5e36854b2c521215904832750590d415323265858deca22565a50229820c33b4e042085bf0515c8094f2fbf9be2b0c18940aa1285585cd8e9a2df62b454503556a0a26e5298d3e24085fa44981e02598fa6092d4076348897d1e96c67f50a47256290ffce0e1e7e18631c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a594d259679d75d659679d75d65967d5344dd3344dd3b4bdf7de7befbdb5d65a6badb5ce39e79c73ce19638c31c618e37befbdf7de7badb5d65a6badadb5d65a6bad95524a29a574ce39e79c734eaa699aa6699aa6697befbdf7de7b6badb5d65a6b9d73ce39e79c33c618638c31c6f7de7befbdf75a6badb5d65a5b6badb5d65a2ba594524a299d54db3ae36b2b6dfd4833290c8aa4943cfc58f8d883c5ff0f85642aca275351b84c453941a6a27091a9285b642aca95a92854642a8a063215058a4c4591524a06a02c0081ff2c30c8b3402f5f14779440d6e759627d61463048cc077a52dee8c1e2377948799300298a430800ba8943824143a3d7f280a4bcc1e300f2e60ed48d1d37754829b9e8fb46b48daed7178330cf793cde02c717f7123fa7e4f9ef85fd9073f1ff53e22ef12f7116c6e21c48b92353134891a9142132957a820b0712040bb4542a07994af1c8548a0199c25144a670d890291c24640a6749a6703899c21941a6704e3205e50132c500159962009329062c90524ad0008b7710e86769f4c480419ef380f19c471c125763e8f1f07c161f89b8b058bc6769f480be17f671d6d14fe647c844feb99dbf1a438fc338f679cefdf035beba2a4ae0127761e0887d3f23f8411943ca1b0348794387943773c814941ee490f2260e387e0b24fa6e4879d38694376caca1c276e68748e5b3f984e909da544361868636d50624c6270c1855991d303a3a233fe22e30740125fda30f646939e245421f092cda2f2dad3ffe1c812c2dfbf5f33fefe8db245842ca2a54a494d209169b08a010c36c89538922777de185e49fd7f2c22e7abe2572250cf41201908fc8e223ef5820c82d712410b392858fdc4b7963430d10bc204597e8ffd1e7ddcc20c3e6440c121c5d4b61afd6fbcf45dfbf8ece0f7ff85f68b82bccc5f932e0e69738822c710459440e5c29552e17d7d1d128c635141b58214ceac91662988d1eb72b5589e177b1fe6a579a7aa223e6091429068aa4d4d161000f0f74a9fc4fe3752a97b80386ffcc128f189f9df0d53b78238652b2c270eb208bb51346eaecf060434a8136f3ca15058a6ed080524d0a15088a8a94524a9042b1c4305b8ddc8565697481e210e63d6e357217c662ede8e848e94198ba1143a2f41552a40262a44e97a44e69a04899aca4e4122d96ff76c6ef0b3def620d81e13ff381f8c87d5caca1ef038da207e4620df191fb881ed0e7018b5caca131045744e28f8b352433c0e964b25c2dcfc50a390123e3ea6566ea85132d524a297ff7581b0b01bb9b04225569e60a7da018a990d8818208de75e292460b92ec51010946772c401016962021114ab6b84184138764f9618bd00145da68428e2c9ec4f1480c602060c0236c4840cb90b826101242247ab0a4636e5871d551c3867b14a909688d2244e2c58c0f0372886e2880c60a185f5012456f408209014ac8610513644af8760837535a31ef38effffc149b00d96e9d5f6bfce38ae7f656831cf7c0f4c766b9aa3bc733c69a52aedbdd1f7299fce836bdbe6a6dd3fab3e59352134c0ba09dbf9cd6e9c9ebcd56af34b5b40f6ab9ee9dd65ae759e395f251fbe51df6d96f5fb1f71ed4864c7bd4c9ffd75fcb5dc79cfaac805b39aff5efebf61ed463d37d79e59aeb8beded93c894c786fbc67c538a3bcd6a79ef95a6fe877c256e6d4a80ef7ebdb8529a6bd670adb52260e3dc67ed637b37e614732a6a9a4d78681ad7e69ddcd5b899f5e4600b0c5db416990eb0755acf5553cf751aeef8df95a62e30f494deb1d50e733d57aafda5756b7ea569ff492a70b4d48e8d7efb75add76a6eeecbdd95a6ff03595dd5bd918f62a5d454c7d6b3e6b77fffb58e72d7ef95a69b071b60e370c7f1ec35aedb779e6799a5435f7d353bab96b5baebd539cf7f359e9dafb657aed13f6b1ad31cdbf478f2ddb7bd94e32ed6664c726cd5d66d29a5966b136bbee3d83ed7bdb5dbd28e7e9a2d8563a3bbee8b79dffe6a78da692ac09e75567a35b65a6b1a5b6c35dfdef639b5467daddbbeb1ed3d2fc6d377ba9b9dcd1310931bdbf476f35bafff1dedb84ac59021cab29792a8b721a636b6df75ca77c767b72fdf5c5e699a53d4c5cbb55e98d8d87a563c2dc559e79a33ae959ad6d8fcd69a9b75773d739f7de43ba7a88bd0173ff1fb3ecbfee87daf2c6371af057e4b2cd095654b2cd0a5ab14263536af69954f8c6fd57d6b9ec6f6e7ae1d73bf397db53cb9098d6d62ea77e5b76a6deb0e5fd319188dc98cad7256df9bffacbec339e332b6ce67aebf563bdd7d4e6b1e8fad6e9a0860c9d8b8c97d8eabae1de7d9debfd254e97f3c64b28c53ca323f4c63548dda9aa99df3de4d27d6769dfb7b7a35eff5ee3667fd4ad32596cbc57b727a7238981f6c39a53aa9ee7fc924c696e9d4dae65aab7aeefaf1f8fd1c793c59a683cea88e4c615430362ef7cde9f47476ffe9f52b4d57225fdd9ca22e3d154c4e4f0e97eae58b9c27c3c2f4c5b6abe7b8d6757f4d3feef84ad39c53d425a75566cbb22ca3960a9317fa5a7c6dd57ae269af9d55abb3de6ed67c39afcdce7eb06b21d833be5ce32b4c989c9e9ccf824734bec284647e08f68c197b322fe449aab16799e7e259e6b56a87a98b8dd35babc7977ece75bf3711104f9689262e36affbe668d67bcf9f2df7579a668c75d01955356db1654ff3dc3f7f3ba7a6b97219ca99768b498b6df63cbba9756e7bddffdc2b4d754c596cbf7bfcb55c397a357fa9b7351c5d271f4c586c57ebb6f2ed396abb4db579f3abf5faecd8ead86cb5cb197b7c7fc79a86f9be3fa62b365af3c71fff9ab3f6dcefacd872e5b46d33de98e3ded66baa62d35a673ff7daad9cd530df958a6d766e2fe55d673beb7fe5e8ac5e4c536cb872dc6b9b53da7dcbb756a5d8e8fc59eb492ff7f5d7f468539637beaec8c514c5f669c7d1adb7f6dd766a3f192628b63e39ae5bab3dd67ac71a7f62e3fac4b8f6dfe18cb5bbef1726276a139bc75ddb4ec3b6bb79f3bfb9e41401e5fc50680cd110e90a646262bb54cbd8526e57dbb96e393baf6989ed56cc4d3d37a5dccd7d77644c4a6cf4737dbbc9d9492bce999b92d83aed5abc2bd7baab3fa73513121bd555e3b2a66bd77856bc579a7e2f15b5654c476cb7e69a39ad75ae6fdcab5e695abf4a1cffd8c14ded1793119bfdf773d36b9bfffe699f2b4d59af96ca7f5ce5a9567de49c6adacd5404cd143111b14d8a39cd6a7ddef933a6967b110db1799d6dadf9fe3dbde7f81662eb55e3bacdbbbd996aada92988ed72d472cdf149394e35dea635340c1310345332fdb05d6eff6b359d2ff5579bd4878d9bfed2dfd9feef9e56ff1eb67d73af5e4fcb69d556ed4d3c9876d8e6a4f3638b3bdd7c6fbd4d3a6c9ace6e6a1dd73a3bfdecba95a6df832d95a7951e61ca61f3345b5f3b3a3b8ddbbceb309870d866d6fefc797f4db1e6beaf340dc1b1f30eecd7f5877630ddb069bbe9e46e47f1ec6cf56ab66bb5affade8d2be7ace6569aa619ff51ae2c930d1b37bbd6e5697bb7beb373d7b07538775aed383e3fa51953d1a314d3acfb57c14483a6e16a35ad566bb75a6ab3ddf4248369868dfb9cdf9c4eaa67bdbdbb609864d82c37b95d7dce736fabd9ac420c5bad7e738dd3b5c35ac7a7df38e88c4a05c3a66915db6e69f7bdd68eeb057a61b399d6fcad8635d6b68b0b9c6ebbef9d1ce6b8f598b7b051ae6b7c73acb5d673aea1fffed1676d2058d86cd5f4b79a729da3d9e6d7d256b3efdd767aea3ef1d678a5e917f50f6936beafe5f352cb6fd6aad64a76677ab56635abd1596bae5bebf5fa8d37c7b6eeedf94ad356087e97568d26256dfd763e39bd31be93ebca579a8e211af1f2f0b25f8c9be7dc726d775d6b14fb0e8bdbed72ff98d35b7797ceb9c32d5b5b2bd5f0e7b8a9696d079d51816836aeb9f79ffecd758ddb9b9bd4b5cd59f3fe5af31b677bb529ee2ce0a6e1bbb7b6b8f35eabd7774b1f897bb9b0f66d59fffef1ef785eade28e8b7c6529a6416754ba45abc513eb8da9aef3de8daf46b3e5dddc14f77bb5ecbb8b86b555dfb3fed353adefee534db34ce380b4e9fab3c69d6badb9afdd5f69eac1964af56dfd2d2f149af4b7bcd0858556d8ba49adc6d53d73def9768d77d019550c1ec638ef3aefd672ae19cf9bb9ceb5d338ae9b1bdfcccd3a569b4635d6b8e66ce73c9d7fe6a0332ad5d1a67fd59c7b9ab1d7beb77922d16dfa7faa3397b1e6699e9eb7c0b0cbb2161872c9700d3aa33a29f1edbf76e3da79effba72bfd795f3edff99ab3f5dceeee669eef4af76fc97f9f28cbc28f14f216575fdf92cf2902ca4903f26499ff69400e576a42a35f2de39b2ddd566bd9d2fa3b5bbbee31d7713afbca395ca9cfd37e4de31a5b9f412fcbfc28aa90423e318be33da9cdf65a9bb53bb7b675e79862ca3badeb74ee7faa8cb6fef5bede76d5729a6637b7d2b4e39fca7f5eb7270dffd19fb404d556bde63ac74d8e56edeffa8106b075737a4ab5dad57d71a55585cdfabca9c6b9d7ffb9cef195c8bd64d9d44167543d854dbbb7eaaa394dfb4bb7d5569a28cbc42ceb208bc5f3154261d374e5eedcd8facbbfbdffe8c5fda033aa2e67b6ceb56ef7dcbfd6aecd793ff0532d71aaeaea6c4e511717972cabe14f02ad2a67345cb22cdb1985992deb8e29d7f3b7dcd5defb95a6687eddc9d06c2c9e65ae56cf7294d9f2b49e67caf9ee5be3da934166f3bab3f8e2adf5ad5feb9cfd62cc36b54b6db73dd7a9d62b76bdc0b00bd67410b365cd5bed7396e2ddfdf6937b8993e61475290252f16cfb451122441bf675db7da7ed16db6dbde7b3c8fb9d9fe465d9a64c0c6db6d366b67c5ead615c3b57e5f4b8b8e46bc6095bb79cfacc75fef3a5766795ae158cd0a6ade6b2e73457eef7e5b42afc4aa04be59ab57209b3fdccfdea69a55c53adaa3e5075f4792a51257af39221cab2fe55486196d5a32ccbb29945509d19e7baaf46edd6eac575e74cf5e75afe9afd9a9df957ad13301bb5fad3cef1deddb57fd295a62dd15bb942b005faef574a8faaa80568f37a7edeb756aff6bb8fb595a62ad6ab35bf234f1477582f30599665a20bbc14892f9b9e1cef0ee76e7778cecf579a661f4cd870b7f67ffbbdc6bbab94c347fc6c75ff7fa7cdb8dfa965de579a1efdaf7ad6f7b808e51401e5647b12e165b396b35bd3f35abe37c5dd4a53715475a0aaf512311a2e5986a60bc14e95738a8072b22ccb5c5d966559966996aa542a341da568ba4ab755a990426e69d0199529902e1bdfba63de691ccf2e7b8b579aaa5a60d851fa8160265d0a7f0b3c1a3fb0b5a9abab39779d0a294c81cbe6bd466f97bde7a6ef58e3569a1ebdb8ef714e51971c96abf5c372b5bc7ac7566773d01955d146b3249fed635bbf36adef34dfdb5d9b53d425a7e8dea033aa9f9eed567e6bdf1efb4933cd9a67bb3477bacb9e53cdfa4ba7989d2ddf3db9ed33e3adfdcc33a7a84b4e91ce416754dd968d6bbfdbfc77ce7676b776256cf67697e3ba2ef3df55aced95fea3ffe3f7fdf7a304ae963e5ef7a865f31cb57673f47ffff7e5b7eb40950644c24629d63cc55c676fa5f5ee2c9bc61f53cd7df767ad53eb2b4d5596e64cfb4fb23f236cff778e9b58c318ef9c755d69aaea21afb67f4be2f5d1d96ed76b97ebe52ad5b8a9692b4d5595e6b1d5d9ac6d5cbf5cc7f1dd691ed73de9f7e07731a53bbb05822a960f55aa06f4f6b9af73ed969bd47779f295a663b8b7e46dcffba7c633573dd5ac96579aaaf4c6dbacd7eeda4d4bf1ce1dc7ad34dd54df6dd3caedc67ccedf7dffdc4a5325303c52d16ab79a6dfdbae3bdeba8d7b85f691a8e62dd6ef69addb85fdc699adb74a5e9d8ea343174ab53c357570edf6e29c579a5a9ff38e869d566e696abe59b6a9b771ad568e6569aaec4974b15825ddec9e9c929ca2902cae97f8c121fd2a8c6416754405836aee330c7dbf34be7d4d9ae340df9aaab2699f1d5cae132943371d01915994c5b2de74ab5bcb79eda9d58dbac95c462651bd5bd5fda75afbbe6be79968dad2ecb7e08766d8dae6c176b9f9bdda675f6ca375e69163cf195530494a3fabc5cc3d1657f08f61b744695c5cad6b1ef95cfbda9fdd47ebdd234fc9f085bb555f37f29cd1d573367579a8afe7f5135fe234c738aba883c67d657ce5096b540fff130e339454039aaab059d519972084125be42950a832af4e60da8a4019546014c0121e38a7d50a94a85e5f8afcab8aa9078ef1f29040fecec608a142a18150eac0860ca0621683033a8820116a5830b6ae88105aa0f02505b6057853f09ec3f54a92852c6d5d5bfa82b5852a93ca0200d6f71300155812c15c84ad1160e55fdb05385e0c835804205001d48b0bbf1b5410454f5572a5517bd9606104c79408ce240053168000ce189e73fc48001012c4055ff288a02544451afd502a9dcd40e4e21ffac9e8189aad27c0de4141972e2c8c4f45bfc43e5c8f1059407a41d4310b223512ad4952eb8d8428b2cb06073851555503185145140f184134d30b184124920718411451031841041ac01e2071f7ae061071d6e72b8c1e1861b16244e1f44489c3ebe90e2c68d07a41d6ba0d8919ce0c06681436eb8c108356ad4a851a3a6083536d850020e3c30c490830db381273e357c514396279c6002871d355a460fcc0c54482ebc9063844a90214d0c66c83488901170c4c400d31a26355230e4a1c0f70435c4931f8052305cf0c224a91726e9e14eea051924cf0815fb00fb3eebfb2c9c72410d49422593f024b10688540b840821252a0829516ba444012125ea0729513e4889ea414a140f52a276982de0b05c2d4f954d9acd2babb103ab5041f3552e8f850cd311592038867ff4a4942916bc163882acbed4f7528f94927b759108f49112a5430e01de4eae6b39636f2fa77da5e9f6375e6cf34feddbbe2fedfcfd9eae2c556dcadd74816d77717731ddf7d6cb754ac5b8e1629b9ee3fdbb9f5667db796e8bedcf5df1d7bb73d472bbea1b2db8adb39f9cfb14d78ded026eb2b015dbcf757cfb3bab21375868adb7eebce52afd7c72ab10ecb407dcb0c1b47395d6f9b5e9eb9d2bad5285e0f7c2a2bfeabca7909b2bb6ec3ff79cebba9b5cf5da4b4f4f0e51b562cb59a33c73bff37aabd6bc8acdeaac75bde394ebbd76d55fcdbaa1626f9ab536a946f3b4bb735dbbd973777ac9b29c2220aaa7d872c6bfe35df31cef28a7c14eb5148efb47871b2936cd6b5ae7efd76aeddaa53d8a4dc393eb4e6fda512e637ba1d8a636b7f6b1d5f9765adfd705863e59e60243af69374f609be7b454a396ce8bb59d33533f3bcd738e675e69fa1255dd52fc81ac9e2cfb1fc8ea1a9b1b27368da3b96bdaf5b8eb59f72e0494935304b4c54d135bf59fd368c655ab9c7bca25a7a7fba1f58689ad6f6dd2aa79e67e577bd74edc2cb169d3d7ea7f672bc73bcfed4a5370e42edeba5162c39ca65d9fb7d78efbddb42a5c6f92d834df3fe69a9db973b6d3bfd2aa49c71014ef0d125baf1beb5d29c779af9d732b4d73ff1eecf91e0cd226cd7273c456a9ef3aed62ce739e3fbe2b4dffd106023262ebfcccb47659535cbb3e2b4bd4c29d4a756e8aa8745e4babb557a3786aad33a5d8eaad7579f69b358ef64bc486b5aa55add14eebaeb6f221b67ce7fe1967ed76aab3ff4243393c393d393b394240f77523c4a6e159bbce6d3f27ff58b359bc0962e3b4e779db49fbecbe9edc4ad39e9b351bee9ce6f4537df9eebcbfbb7503c4e6b5bcefbcd75fdaf3b615e9e6870d73d7779fa3b6eb34fff3af34cd14cc8d0f5bbe9973bebbfdbff69d86579afaff29812acf4156968dff28cb728abaa8b22ca727e30a2a157d24cee2c9b22ca363b22ccbb40d47d7cd12373d6c3f6b136b9df5dce3cc75af72bd544820c8d235a7a84bce09393d3940394540391ae775d01955d20d0fdba619f33ab98ea39a7bdbedb0ddaaade61df3dc5bde4acc92e63d73a3c3963b2de36abbf79ae3b0d6ad3465b178bf1f974a6f72d872e6fcfaac697bfdbd9cbdc161c35db33cd7bbafa79cfa4cb4ab2037376c1da72b773b9e5ae6b0a53ecbb2ec521565e446cd563b3df1f67b5b8b2b5551abe6c686add3ce7fe6f8c7db66bb69b8a961c39dbb9daeba6a76efcfe94a53fbc20d0d9bc79fdf4e3bae7fda5595564d35dcccb0691abe5cafd9733febea46868d4e9f3b8d53ba2fed34da3bd934a5babbf37bab356d674e67a536e88ceacc8993add7fa6fcf59d336eeb4f67fb22cfbd326dbae1ce6bef35de39cbf6627b0fdae717cb9be77ebbd33d664a3fb636e27cdb3638ed29ac9b6a9a6d57b6befb4bf7fa72b4d3570fc7e8a5a60c8a57f1791a55c9c30d966ef5beb7a6ab4fb9bbb39cbfa4fca32acb10ca74bb6db69d8ce6df1a5f65aba2dd9b6fe5a6f0eeb6ea9e737af34fd3eabff248de24d2bc999626dea6bb39ddbee4a2747e9ec7a77d7f67b8f4e946cbf5e8cfbf4b9768ed37f92edeeaedf2e73936a8ca797c0a6ed8ee9e45c57ca7dce75926c964f8ee3fe73edce4be78f64e3ac4629fdb6738af5ed20d9eaeef8ce9ffeedefc7db3fb269ee39e7eae6ddaf96eb7a0436daed8efbda9f3affcf3947b69af9f6f5ee0ee7bcf9e51ad9e6bd3373b376ca69ec2b87c0a669dcc65deb30d556c37b33b2cdeaf3e434ae5dff5fed22dbd42ae76ab65d9ebbe3552bb2518ee24efb5bd378761dcd896cfc76d7dbfdb5ee9efb1f91ed5a9c35bbb9aeffdc13df43b6ca59db5d7db549abc655db66b39a9bdf77cf3ba51dde866c98ebf6724c75a61cee2a7dc09633cdfc4ecbdd8d67b71a3da5bbf4bf68baf491344b0bd9389e1d3773b594ebfed34fc8a6759db3b9d65ff5efbaefcd9c06d9f8ed34ce96629af3cf5773c0c62bf775b72dc5954f3eab205b57b53cebe6b57275d34d03d97aaff66a3d6bdae7d6cedf80ed6bb86addbf9ccb2b4d3fca80aa61bca776f1dc5a5faa4dedd25e37b75de6ae9ffa7b4c01d966a713e35ff5e65aeb6eabf593ebdee3f4c74669c771cd5d9d77ae9c6b2b4d9390766ec0c98f6d5bcebbd7f0ad9edfceff4ad324a44bc77cc1158fd302366deb6d3beffec55deb38aab1f81786d99a665325a73e36aab5bf1defddb5dafbdd579aaa40bff9d87ee5335f7df1ee38d66a5d69fa8ffc0f7f0b57f0b4c7d6614abbcd393b6dcebb5b1d79613e2960d397de99a9d64d6ed2fffd4ad3b1fb2c158bff515421b15e5c9a3f6a7a6c974f6e6aad66df7515572d8fed4f8a39beb5fbbaeffd79a5e9d1e789e1abff5045ff91dff807eac91993d393f325a727677c81f91e0ccab22cfb93d61313a7046cbab3ba76dd7387b5d6f9029d10b0693b2fe728d6d7eaac4d7dc263ab9cc376ff8d37e668d5eed492381d60b37572bb6dd73babb1c57aa529d87d7ce43e5b456f0d3aa3321971ba63eb6887b5ebbddf7572ecb9ade564c73635d5f6d34e7dde7a6aad8e8db39cd6fe5a8f354d776b003ca7ae59db344f6d536b6bd52e87efeffabedaa77f6abfd2141cb59ca22e3959d6bfeffe5096655db5347a2177dd59c5898e2d774d63acf9bb79d55aeb3936ee576c3967bdc6396de7be2739b6ad518dcb9d9efaf3db352e8eac697a6b3c27d6365b8e9ee0d86ed775af56eb9aaf55a3b4005b7731e718ebcecd7e6bcea737b6dffde719e72ecf4e6be5949fdca895de6f75c777ad5caf34bd59bb5a9efd39b5b1592d4fbc37a7bf6fff35cdc6a6ebfcbfdfdbf9adbbcfe73dadb1657dfbefb85d39aa39ee7335363a3b3b67f739ada6b179cc77d6bec79c9bdad4301a5bd7acce55cbb8e2baaf3d63b3be5acde33fe7e6b7d6cee51263c6a67198effe79d6689e7f4e2fcdf7127e2510a896b1755473fdb9cdf3d5baa9d92c5be261c88d4e04d8ba7639cfbfee55ebb46d39315906869f850ffcb2acf5fa32191bf5d5d2ceedeecedf719ac6636cda6b9a53ff2db7ade6bc3e89b1d9be35fe27eeb9d26df55c699ae3f52be7872f1164f51c4ebf7254bb9a663885d1fde45ad3b9edac98de96d7eaa80b2730b6bf3fd769ed9dd5dce56c1e73fa62d3bc6a9a677e35d72bbd78a529f712b917d107fa8c35079d51a170f262c3becbdceface5493bcd54a5517cea62d3baade55a7367f1e532d711852311a53a1c45cbc5c6cdeedb5fefe4aee7b8dd8d2f4ddce1285a7bda629b9c7aaedace6d9db566da527d9fa5e9920becd8e3574e510e875f39ffa4c566f1f459d75f6dd613d753161bd614e78ef33cbffffee53961b15d9cb936b9766777bbbeaa4bc3556ffd56d64e6c36ae6b787313d35bb93be7bf62e3775f6af3d699eeab77c757566c96ffca716d6f8e6a5ae36a9753155bc5357b6ab78671ed975a1a8e22155bf7f7664eebb5e36af79586a338c5f6b5d6f47bcff1cb3fc76920c8793949b175df66ae719952ce4d4dc31a079d510de014c5d6fdc4bc5ebd3dd7d456d1634d0ac5e639d761db69bc6a9cef9d263ec1fdfdb7ae956adc6127b6ca757c639b3bb5b5e338bd4d6c9f6ad47357bbbbfedbf9ce32b179cd6b7d6b5eb9ab69d9fabac4a6bfb6f9e65bfbf77d6b196f2725b6a96fe7747b4d7be6b43f579a7e96eb3552aa4f496c765aae692d4febbbedb52922b161add3dcb3d6b256bb79397cc4f66de7eeefb8e53a4c2defd788ed5b8e799eb7663f6bf5d616b17dced74fafc6f9e5facfbb12b1d1dbb9fefa7fffb5bd6757e53ac496e7e626a55a5bce6b3ced4a5321362e5b4d67bc2fefb0de1ebb5caf20b669aff57677bd6afa53ec579aa6f92b968a9ed66cbad2d96da797ab9c4eaab13810dbc65c4f8e729a6679ffbcfb61eb3a3ebfe777fabbbfa5da3df9b075afa9966fc6dce63e7373a5a9e7ae1eb6ee77bb5bcc69fbfebca95e695a79d82abd334fca75da697882fe521c8e623eedb05d5cf7b4b973bbdb5dafcd4e3a6c99739bb3b9dacbfbe45a77ca61eb56fbdbe9cde9cdf9b9e709878d7795d3eceedcfa9e39efa71bb69c396dfba4ffffcee55f4f6a36fdbbebf1c55ddf5cc3ddc9868ddfcc751fdb3e35a7d4d295a62c1e1e7deeaa54dba71ab4b55cd618e7eeadcf7ba5e9aef4a51a5bdde672a261eb9aabfc72d4ea5e37c7d15da9c5add76755a71934f718d7cf677735bdf9a5fa62c85d97b65e9f05739261b3539b777656cfccfd99f3287a405a279bf5ba6f7ab7e536be95eef1ef6de2a4facbf5e5349e2fce4be1f87d1cfeeffbd964a39ce57ad7edfdcfbbcbda9cc0c6e99eb36a1e6b7a5a5aed67b95e455393ed737ef79dbaff7cb53efd4ad34c822993cdfb6c33a5937baffdda3d26a2748fadce62b26d5fb9db75bdcbf5fb8efb1dcf85b24cbc64b35c538e6b9953abb7ffda524b367bebe534ac33d7daf249c51d0d57c251fefbe7fe5bceab768d4c946cfcdbcf6d4cada719d7ca69239826a17cf7ad37d7d56a93ff20930436bc75d570777d372b9f94ae345589926cf8f7fdffb5b4d6fbb3ef569a622ba648367d39cfe58e77f653dab9ef3f4fe97b819f6a5348b6dd2bfddff29e79ce9c6357cbb39ce9916d6a7d6ffb392de33ff7f411d8b4dde9d4b8ccf5fe96abdf914dfb8ffbdd156bedb1ad17ecbe9fee7d5c28cb74235bfd9afdfb7678dee9b9cf479fa74160ebb8d6796f6d35cf9cd51c239be7689e5bc3797bcee7d62ccd8b6cb47bfe71afd5d2cb51ec7155ae18fb9814d9faee9a9e3d7fecb5df7dbbd22ad1ab543325b2695a2fd635676e6a95b3b257f1f008d34a35229ba6b9dd4d8f69debbdb1db7d2744cf355aaefe25dfc99afaaa843beeaf790adab9cde9ecfae7deb3b6ddbdc66b3dcdff8d33b376735df65198bf5fbcc416754664c866c97d336f35d35ce5bacb96ea5a9f754dfb534ea6f27cb963e6e1fb0d5cd55aee3b83f2bf5f7f795a6786c7520980ae13467ffbbfab96c73e7aa9d2f1321dbbe9cd6f99be79ed876f3a6f13f471f1fca321d7446e54d836c58a39efb1d9d9557dc67353960eeac31b599520d63bdb55b71e5bb9bd9627f73dfbcdbaff1a727cbfcf75c60cf3231e3329433a9209bc5db6b8eeb7cbbce33eb95a6bf7fa0f6fb17e6e392b7e80203d9bc466bc5ddfa7ef5af5d5e692a22f1af65954ac521f51e5303e8a677da99ada69b668bb569abf6b97e8a679d9ca6756762c0169efea8b4506aa78916a418328610401065440024e312003040241a0b46a3e19860b653bb001480024e9c74ac44998bc4c1308761140441c418620c3104184200010839545403f84f552812aa0f5e39cf9a854eb1daf04be92df6314eed054ff9363dfe7838088af04f032a247511b5195141f2de53f87cffc1a8b2a135cfe99c3eb4862dd205dd2cd656abd8ff4bb74e5af552630b73f2a7c53c84141e81b807edb7867a68f900ce81c75b46a0840e81f12d2f63ab4408418195128af61404c928badf0479aec6c4828ce51467179ec7f048b492ebf085af15dd89824dc22c03272c016bf4d405a2f7a1efc54c05230600d306ae2baba20ec5233027d830ae84319b3f3a25909ee2ba51ad03e913c8124e16f08c3e5f02657333e8b67f8dc96475c1da7100456fab59cbe3a9e2581c7c1f9580ace5445abc6e2abb171d912a5d1f4f4b81bf154d14f1e7e1e107c92a334474f62c4c2786ce91507d59cebd5447227230ec56836c1d53f8c128e85734096c4b452a68d2e25559fbbc31d9e2f57422191b9d86f4d734b3006f3cfc0b352fc590d515844b16f93e2218f49d6a46c86a52b7342b4693d3926b3ea0532d46d10347c3828233caec6d16f83c2a05cac098d76067e1a5435f6530c0fba2f56b98e4d7ef8248c7525e0244dd61912bc4c37ef7012e01fcd00d87e674d1e180b2779e8653bf24f4436bc7c359efe0e2ea44b1ffeff890cb154175e6b4959d954531fca1c46b8f28c2dfb6703585f0dc0949d9a2cf4af9a4a831f58c41a12474c6d71adb6f80084fe72b287de96d2000f9b67c68e81a6dd731f65a654e15ba37b814f9b4db2cc88fd99c355550ef50a31304d2c4549489f975cdf6c6e5c6c92418e8143ded4efa95ef69c3336157d51f245f1acefba4a52f8233cdc9e6599d84144483ec0a76b6d190921c3bcba320ade816687c62cb7df5270ea019cb7a1f17d0ff3afe69be5b70a6198f582dff2a597b4e7686d5682904a046a7b2cf31900b6b7f3bb04191622e3710a4a2c5192ea0c5896ea8c059dec4f3c277fe1118fd9618f29c526b29d7b43ba7b1ed871633af35f9d854bb0e77edf020b181c5bba13a491a696f97cba8564156a55480daae1fc9da4ff1b74381d129c3c1e52271e026cc8b3ce11868847fd50d15ce54b6e78cbec2a51c49f93e2115f42e591c9e1872612570e176e1c3a540d154693278bb2d9f1990a20551cf29faa5795643464c7c498e7b1182af65577f1beac49ed646386446a39ac439ac4b2b6b3aa79caf247b886514c57c447c8edc4f173d9a7cb2d4667d4be74c128f1cdaa9a6e538f602548e9df73e8b0165b54cfebf8435e55885db57f0afe53aa23520fb62840e1651620fd867c6bd7e0e5bc1d24c2605fe7c68dc8b54cd16d7b8823d909e5b61951864fc1117e3a39535ea9321597a49d4dd062836432a7ea7d1a30be2ccc9b15d3d035af3a379a2f4efde1980185fa923bc2f0071be528bf06e0062fb0a4370bb81c0f52726416c0281e74f3c82d8050297fe043ba5ef6dfe2f2690f3e87f3ef0f0baf8afdad2694e9b701f58a952051d4b7f94a0a1902468b77f2a7f08c87c959536636ddfe815267369e2445d13829cbeb5f9964b685f4969a7c5266e080a535192bb9b4017c8fff62b7ed271ad871e73ad0c80871a2bbb5214431b03ce34797909a036e60ecd3aba1be450dd88922214c42fe4b426c788a2773f7b82c89fee86d455c0eb7e3a1b3e20063db220c58740752fcf11328d0b7bd807dcfb99369771cc95b5903c7d276919dd7692db18dd671122e42c328a8bb1a51e3cdc7c74b8ae4918aa6e2cff9e1c1e1cd95676e353be7ecea809698a6fce80ed3197db0b23c1ee9dcb8901d0c5dc5e31d8682275840c7dfda286afbe76c372ba7152655a46cfd16d4576425f22c3a827d3d90e3662ce07812e9e1f170434c5d8cc588045e6b31dbdf90fa1790758f4343ab0a9acb0cd05d7c1f2dc1ccaf823c506a984c52a6f3fd8ec78edc49ebd33534f887c5fb0824ad075fe02b26a58019a7245d4712b0d09f9db5c82cd3ec35190da707b3c63fd3fc02d0ad09cdb65cd870bddbe04002e6f384bf91c4de734a391ad1bc1b89b0b0ade52d99ec5035d10da734a6f59983e5ed9cfe7d1c5a7d34ea4d27ab4e77bd8f38ad337ca83fc2238b2903e07855feb48a6b277a46290882085f81b728cac6e69febb1b10ecd829979e66ff6b96a52de270d4c7880144755df8a85a665f946754ea54cbbe89ca8217e794a70c8895c1354e7258df5ea050aab4c925dced3d42aeea5f15adb190649b8e069f4c63c5fc30ebb3df0c0e1b42805a20aab4e77b8329fa28f82ac10faeb9deb1fc767e867c2321836eec9141d5280f8e9b9ba133acbc202201ad352aba4700f7d6148b0f67c2feebeac3e9242f4c39494ad1fda41dbfd68d406ee09b83a17ca7b5a21413320822554dfa373ea344ad0c073c03b96ce30e85911682bb66881097714e40570c505490d72df1cbe6a92ac7a4119794aa39aef703c1351af99e8555650f4c7d64e3d08c1b397c5029d34dca05e26074404e2d63c027dc8068326349bc10d32ed5ca2788d320ada37c040ae0bf5ae41930f091bcc501fa4a10a4fccb09064d8d7374301886be3002ef586acb1fa1229146060ca40174077cdce55ef481ed93af48bf7a62628b54648bec7e60e459e4c18d6af402596bbe8057cd19f24c13c6622cf29c5d65212c806a7052551dc9decaa4afaf48eb7596695e619118d72a062768124d4fa6e58b91359b7d640cf1b25ab5f973902304bee8a9af2ca1339462afe681a7ca0659432f61ba44b17d0a004d576436eb7d5ac3f4dd66ca25cfee9f7b34c0d1eedf1a230f1da69740c749f1b278e01c10a1b1d5a51a2c0232744ad6c21f2271285c22312f4eb5d446c7a4f2bfd42be9a5a349cf4eb9ce687a08b4d18f005d27d2a40df2c7f6d8f98a6e2f9d22b27c00cb93f6ba05037fb565faac8b837f497de0cb92609ed22ded175b953ec75a91432924f2a1449d6897b94188483700540551935701cdb80080770b4e393b9843d3682702a198664c42f8154ff7d8592e92709a14b32ee557ec8567b020365d4a36ed36c18342c2ce80660e1328bdd5baf3d4a12731c1fb2a7b7267fd44e72bdb0c14bc30b6a90d1a86d24f789c75fb3dd3a50f1c964dfd27318a8f2d2e9059e7720cec1dbe4260c2605ce20bf6bf921450563215419b2f4efdd5fd94d922afc1b2d0e8a0d964d82de0bc546587003459fa53bee56b203ad8a1013f822ba7590929e8bbffc1443b2e4567b50755861b615d8d97d9f171bbedf59cd0c74eb1c950a297f54a8d143c23709d240e3af638d3450a86366d3f96f7a23d08baf8d3e3b908a0d7749a0b3c2096a6a7c21e1462671a782dd4f4ac2b569e7596a9dcb69aed8193744ca0c0713abf6019f4f68358487d0588afe080699ff98af0b6b4df916a957481fc0730bd7272eaefe33a4f590994546ba3751e083fe694f3ed67018c40e4630408fa767a60f381a67d67856722d36bc859a07528fbfcd8e815161b6634fb9dc34e162becdafeb4f98457a45d18c962197db3750af5bdbbd43cf9039a7a20e57fb76e6e9a84e0e447cfcd2cc2c7dfab264091147f6c52f7d9e6e23b43477fe4d5ff12a99ddc1c6103b9e8a5ef8009746dd103f409bb584808ae95caa64371d080df1e861ae59e86793c94ae68962df266a5de0f90e4051288d2af4d5d66c797765beb6cebd07767ec904af8c2bacd404af717442ccce26276445620b91cc0a335e522eb80dd3c693d346a3614a656786d62a2b010ec39e6ca93a67c1c24b9082da7284112387293cb300109858c84124c8972c99fc6530485baa2c83e1c266f4512fce576517801a1f3f39cc4fa5d01cdd884f6c7e832e86237818da28b98d82cef95156b6e14a6f8a1c92a048c532bd4017540e0f9992666993a328c223d5779970d40d931f9fb7c59081aa1fe9f4a2323a9d013b1af540911baba8e3c20a434372ce3d9d8069f7e6aafc178054f3963eea0516e195fba6f31dfedb6135a56878b1d7424664942efd410e2e3709af42be398b83cd1a22ea8f219c56c6d5831ed5a2b0df949e351a4f47119e0aa80a60cdc7d62d7ad0feb32425ecde7b93d9df731ea034013eba6c6294b005b43b2807c326b26b590e11a207a3822a205dc30b481cfd48fcc02caff8635850aa81301cc210d9d63ed1ca253fb1071869505c5981c5596b58406bdf0d5332f42c7c4086d20ee11e48fecdc3055b76ec2cee9d15ee9af6955b1b6c9d74dffcfa1d6c5025b3743009d872247f90ad681c4b14d1811e4f573a38c43f6ebdebb62a6b1f371fb7993ef791ce0da59fafcdc06d413afe665d05b4617ffef2a666e84056ade19374a2e8cbb6c7e3c453f1c2292978e0e79d12fa8582d4485ff35523424f60372f1712b4900f201125e04c15f2f23d340f39da52d120e3c4427241501ff2cd0b0123424a0a74dd6062a603020c04c027e11db52af4270f0841ebbf904428bb3ef2481949cfa154623ecf27c85f05812e8aba2a312af7b13649aa039cf5da27a9cf96b6f8e68ef87e8be50b82e51b2784ce55471b06694c85c96e1325caa9b3535a82b925f9bf959269e13f3f2b30e308fd0c05c7bd006758b0cda9c504ee4c34b78bdf86bd0d3934d4105c07c6056b4128662d99008c9adba8b93c581484f541f02d43ed9be04aa7d5fa17e8b1d1261c18779ccde0b6046e42a959835ea9c0880c6c22a681c3cb4029aa98e72a7013c77a29f9a91b838119d58b9944aed1d51b3641aa01845337a64e319e364bd0dc1f756f8499b45cb4416e91b94f9e57264a083f29b4a11bd6a45b02e476b0cc51ecbb1de460a53ff6e918fc410a15f205182b8f5926e11c9a39b51f56c66d5ecc9a31f3a0751e70f61b85b45ae6edd220cc36ece4283f47ac7f8d0f92fb435b5a2d9b3aea0e8d775b4651d241569af58a988e73e0e4268e222930ea1a6fbff16bd175de279da7c7bb0e443830808ad8df7ae5c83c0bd32206669a1afc40ad9dd9d5536128d6f47b1b96fa50eeca59c91ca7d38d63026ae4f884d4b94fa22e914e1a13be867c13b0179a3bd867ba566f826a7b83a8bbdbe8cd8294507a6ecb4003e9ff2094d88a20b3998a49726881e81a9bae8f2b10599a3c2504361c0036776438d5b1cd27441ba7af1e6bc6e9c6bef1ff6534d5959e49a158ecff3e83a2ea11d422d377d05257b78e8857f449cc2f61a054206f5d21dd58e763c98d18a724ada551f6797af7205a2239c86433502d93f8102635673d012a8174924975c53167ed0a864b215a44e0f724e735218a47c7bcb9a22051405d5240f987138546ce10aa58457bee5abe072048065d2ed51487fd36833778d700b474e0ef2b04d549615c284553e42dfece11d3e4a0b7f823749eb3714bcf92d845a7bd337a8f47564096c41789f4a4be374656ddfee1fe41f8862e30391231a9d05089cf1b8d8c9270a802ff3033f97c6f414e5cbb43f84586f011213cc84f952f54f7baf69d8c6921b89493f756e0176b5c2cdf552dba4a0c7de5773d5ad87bd4c2c0b30eb1805abb04151ffe877916d7ca5e226c925e211e228a2fcbb14a9776bfbd0477152b2ef1983fc6f1f4a8f8b32dcdf42a093b2658b900ed41389f1203a9207c0ea221669313f11039be016118c308c8b4ee128ef236ce4c949a664ef53ca52a31953cf06013aa40d4448a54fbf721721bd6c3d009ba6174df8cdb9c95b5dc8331e4e321f77c26025e2887c6f0e0f0ebe92d42086b2bfd79d33b0db33296c293d506f1589094d9ff29744e8018b3c6814742c38b1f4c0340f8e93714c3ee9c9a3974e07624bdfe0d37006ec0e7c9ffb3020b03fe28b66ab321b79341c19ef25fc787820a1f02e360b9d451e8bd53a547b9c90fb88e186ddd2d5a632eab586893067405786cca8cca9a3fce8534c52cc43497dd5f185585f47aca7f2fa5abd128b72c526297a22df89a880285c43abd37989f6ef78d429e68eaf6ebccf3f664dae0c69b3432e2094949072594a41f3c64044065e8bc2322417cd8cae1ebc64cce769d8b39e032e598c86b7f2f90253c42b2c0bb072d2c20a487e350ad3e90499d7912ef6454d076dcc45dcb15b24cbab72227ef6e802f472745867c1e0119f11dbfa5a9598dba02ed69c154c88a3999d5a44af4d8a15dc35dc98ab8149662ec68440057269784ea1d209916c42ff667847123ccfea169d28a0673ab29b3770998dcc9a1b7f693d0d6428c9778eb05bf3521b674813a5c9281551200320681223d94b280522f7def3c9a6dbabc0b1802cc9ff580e808de4d543c2bc5b98d135a9d4ce10719ad6112f1184712905c9ae80214740bf12c97e5706de08269b763485b1f09de7086c8e44af1442b09964271d890dad7201f3710c1de3aa899f219604f01d8fed755aa93ed25d8e25e65147d25b50166e321aebdf04aa76232c05b679b81e18ac8689f318dd10af4bb957b5836647a31ee86ef5571ca5c50b261cea414d5704e809b18019030388f5ed990d488b8676f4b6d759b8c8a5bb9f91ce897feefb2be4bcefa1e87caad73a9135488e22818bc9cc998111e4b903074d713742e02faaa3ec5b2ef7e3e8243faa691ce6ed0eb16848ff2e0d2869f777bed1b8dce54b52935814096bf1766ebf5624db83f5240ebba451cfe8ac99b575afdc7a8afdc8513033bca6f1a19d848c401507e9d22fe27a58445cf443037672f7e13cee0fb650d52003aeb1b909f71a387ef271b5a501603cc838d9243e80fc87dac6401658aeb46c00804176c7d2c600d5ba7e1089cd83ce814188ba9ee0858692ed01ce13d5b4992188792019955a27a180f56245c8976a6fc750cb1cf3fb3c42c4356b82e6e5436a80688ff1a3b7c114146a7fe69d2e02122404211ccca9ee3305e4a0bb7dd5db77269b65d5a195bf5f03f6643c7ee9b2120cfdfe517a1f01e48d0cc8549f05046609e5068e74494d0538520ca4a9a774d1d4a3e4900e713c5a291725f13345299447beb13ef3d07ef64ee6673255abb74ba794beb78cc306369c5dd82674ce88b3c64431320c0210df07a954a3030dc3330d1b64b9ae3d87d5ac561e90aeb928cd840638eba5380605eccb89706604d04f47825968409f9c8467a0027e7410cd8692c0fc340cf62105fd520f6a224ad7b19781b703ff9b7922c5fe71c6fcb84527336fbcea7301733eca8c2667ceff68fc24ad3b08f5d652c526384a1a4ff410336986140311fff2819b1c5f445b47df8fea8d89ab6438a7c9f767e3d774bf19f86e680f540fe37f31259aebf414b7a802fcc6199b4e906ff43f95e14a99d4f0088f0d7379db0f1667d0cef595a32403b25fe673cfeaab4d890fd5d675f4a38c22594392d20b3dd77de3a4f12272ff2d7cd13ab5034ef5de8bec46a89a960ff4fa65dada276b43fdb7496ca0f16eb8d037d9b87ef4c0f6c96ade73c34a9ff3e9bfb083be89f5efaa90b5bd631dce01fce11f7508f9a7e4f7e1c1e83defc005777a5a95408ead1330bbcc1bf05d43fd3fc6ee77f293e152f396f42382c1649eacd1f377f1c01d1f12fc179766f46eeeeae00fdbd45cab5adbd334e651c46c1cffbbe8e8d5650cba9ee2c2fba2d32eab5e37c3bdb77febe1f787152d1a54dd39ebec36569e8642bfff2b26a833f3205cc7881851e2d434d8bc4f1b18bec9f34c7718ddd787d5f98584acbf80d5d7a85f6da9addc5c9e371d9f07f673b51ab1b69ac3aa1325a217ae6f7a96e6461a93751e2985e05fa3cb5f5ca2242443e94f4a3d5fb9aecfdb0383c91834e177247cd497d993a60e53e674a431e84ab6215fdfd9600401991c4c9bd7d464ebcf84a8cda84c7b9ac0243ad3607af91a04c4b692b8ac1074ffbc43db507d55482222fa4059ac3608b47642ce67e4fae8b37e25efbfccbc8b585124cb33d6ed43b42c7373526613acaeb757c43407af6b3bcec9e85d61697336ee9b80f507a9695fc0e6645def2112d3cb6fba245aecad09bae39ca531e16ae5a86f62694777279fcf88345e8dbe75c9ab681aef5d62668c8ab512e36e314e3dfd5ccdebfe09077d6968ed677f95d7b2782e12bdaf25002c70902d5927caac8abecc7e7df4b7a3cd23beef00e684a14fad5866f5ec777d97c9b010ff8969cec8de2effcc5fd413aff6a6c6a416d07fb2cb9de7c36f2b713b0be2b0f0b9e8762f42b48cad4df66ff79e6e5a8ac9071b3c6436affac6bba83a46e3ccccf6db6de3d52102c3d0d173af9f954fbb6279b6e03ae8ac0d09e1ee412c691c71356ca6ba948ce6b3bea90f99f7eb8eccb4cdadbe592c65d6eb18a04f662364baaea721c438e745d7d136c322747e7e33629f1db4b28251067981dc2a267004bac92268ebe8bd9371aae57e5e731e9c338acb54359ffdc5130941f6832eb2103f12adb839a58c4bc16985b9d0fc76b48fe04f77e3663baf4ff710d394e05647786ba6fa37c7193f24df57beb38c288e9b3a6021fc7a4ea56d72b8f80b2ec87e8b2285f2c8ffc614b0a632d76cba2faeff6ae86679f2e5c2b8c916e19e9ef28e5938bca3e5b2aaf1ee46e9d49f6b1470eb9fc013ac0194b0cd67bcca7dead9d0890d759686e8e90d75dbca6c4c826fea97d1cb44f4d276801006e7d0221f67cc2aeae45438071bc76279e39dd45fd975ef7a366279fe3170cd242b2daf55d413ca3832fd27a1fae2eac7183f03eabd5ac6024a7196e398c69a5acf55011970e6872c5cc1b84fae469d08eda924477a0a29232e9e516afa904900acf118d5f115d23e6070df992dd4be9e25f40f81ba36ecb5be6d47fded1fb5a74e5a6fdbff69f3fabebb66d9b2a7c1c1df0c9737e55f1d89f1a365d348fef7220b36547e5a912f9b05a212060272a460f300592c450facdff39325f049cd4cabc38984dd525b7540e3c3a484432be1fe3a094b57358a2c6333dc2f277b6f2be32df96debfe86dbffafd2c386ce799ca4380c64263dd76e3a0787434357a1b944a41211fb3e3c0b3fde4e201c133da9ff94499a11f60e9f391b9506bff755d9c713f8d2a021b8f1ec97cb8677a3ced64bbb6885d56154e55956a5ea036a435f85f5abe85ae4bf5127e048ed31145f66e369db17cbe819e9f7bb78aac99b333d18d72e7a1ef0dbd6051a550f4f549135192ce7ec5db5c6276cf2d3473027055bb0abbe416bfc1f90e6688a5d59ca866456c835f3257dbff2df00563dbf3a4c413d2a18c7d4bc0b4b8a04e62a2d94af06948389aa02b9cb45086f8e87ca3cf8faf3bb535223aec58e366e8f0cf525a298623ecde0eb8adbc14bb86f2904511eb62f10dc4b94bbcdc00f582f6fff436197632feb68f93d74d3b83bfe13933136ae908ab6927df8881b0267455e7e7ef55a961c4ca6d0fdae1144c815ae280c6fbd49eb16884146b5cbe5ea618993226a6a5f6c2633d00b0d40d3812fd2a29113eaebbe7d01e964ada8ecacaa22e6e4d3cc4dbfab4bc39ac33e3d0fbaf21100b144423f1089988d099490ec5dd0e909010f2f0451bb2260b029a1975e89bffa2abca5f4b0c3d298e50010b9e365cba9991ac8d1c02c3e261d0898b3e90f6beb72463f62a0c5d92eb6eb2c0b7a647ff058e80ea3e71ed9f4e0c0f7feab391837deceb1ed5da795f7826404b857dbc2cfa03f1f6a0555f3adffe2fe9fc61f1a53f949ba02b4fcd35bb877c723decaca5b97374bf78a7a360064ffd96887c708e5d5b06e821219c7b513c5a9821e582c82e8e4504d44818879827e3c90397fa5a097302ad796596ac7f9a9699a032cae443ff7707a9eda8a8404e915d20e5f8e4246d044382b142a9a3f349eabf6747f143d6c6dc5e64d776145c1f60783b86548694d9ba13c38ff4f192a3f6bc966d95ad074a13395c6d105199d7ec4c3cb99c549598fb4d7ec58c320915346e883601ea1ccf595e334c0c205f6961bd858f6ac4c97129bd4356862c0d5d91807c82a6bf431597f55fa1bceb190801c8df3e0f541d91729f2ad9dd396e6f33f3df0009792ad42c244b0f6b7539ec40877e99600f5b97688a6fec7d2aefb134398357f38e4d369bf6ea0063f8a301c97571edef728e6ac26065fbafd390a301aba7dbe6970ffe3cb22bea5e1c25fd593367ed45c60841feff46f46c899574c7e08c12446cbfd0f8419c2493070aec385478b4e04925f6ddd2464206e7dc217035f288adcf833aa0c9206b1de08ebdefab3a5d7e6d7ecadba3e666800d425356aedbaff3795b4b3fa7566fb66d0fc328b9bd6cf63ca5d72adcc165fba834e7af31383389945370b7af88b4678703bcbd9cd189775281810f098779fe45a2a702c2025caadff5d3ecce22c385c1a1f5e964a53d86bda145e645502d1c6a9aa7e465f90b6dd74f57c01c2dcc50ee376936c7c52fb8abbbc7d59fa4b9cfcf3266e3e3a91966d81aeec08e9b284a541ec8819984e53dbf1c51f147a0d7a4e90e9088b6c910aec981aea1c73935d49c53612137ec2cfe13cf9fa3cea9cba35618c69d5031cc59a119117cc3636e68b59ee908d28df9187a658069d59d5556528827ab1ed3956203ca61c1db538304519c50057495a8cb42876ac08656cebbc38dbaa1af783f781708af66421a9a306afd62d0fd5c3fae44ec1e96bfc684b95d89a9a74c0ec238549c06803e5f8cc207ceb35d497a7f97d31d51bc105ccd028a65be0e3316e7017d977f361c703ea857407b7e05be9d065dadf5ca564d03a5f407139bca640cc518271c70f9f403e082331c53753dbe83c34fe370a91c3e2b23fa24765f1fd46d9d126266de3bd189684f8ab61566798be173970de6f59b2794f72e1c67b692bcdb62433330ba6282399404214a7705b7a1b98eb98794d99d20e0022d656d8fba96aaa51bbbcde5395bf0dc2185ffebc81ed2d68b5f4675b1f448a6b87b9d4d2f1a31cb231255efc2039fa5a70831a2bf4034b9b559dd98031fc1469b62e4b7e87c2912a85e5754eef06560cfca0f63fa41adf686939b71695c141b1ef080a0cb8e60e3a877ddecd4f2ae15bba28df06c6c2e475b744daf99c2960598bd7fe34ecc5c1c7539e41540d251176dfcd24fee778c3c1e61ac0aade24946175729a86408303613b37b3531212f198e1dee091146e1e8ec52aff21e3b3565995d95ecf73df10e46225682d9dfd2d448a4074e885551b8ac93184c39a7bac137fe6d5619aa55c805083a07137f342829e184b4532df4a7a03845e55557bd6eaed39451e4b24f7c69cf692bbd3ef935f48202c76a612d198777a3f34fd402ced0c3c191b66f2f7cfdf57eb7270a0b7abf16bb684c00840bba04c135e6ff31e94a3cfaf1dd0711e412fcfcfdfdb4387dbb523403aa14131f7f2d0014241ea42ec0d2c7f83a694709ca2fba107d300ae3cff3f728fad5cd383e3cef91d9e13ff330c41274c0ef78f64e94d8ace18f3581fb1e5611dbf4e422bb245afb863e88efe633ce786973af7913d5348f76934c6ce145de99311a8e2bf1f81c472e7256d876c041858227e93288fe0c661a65d440df1bba78014b1d9a8067fc3c58fd38a1396b6568848e0a2a3c061ef40f595e37a0d05bfa83d8f0ae8445da3773ff35f6adc860a380ffa86208343affd0ca2fc170fae253d03745fbb76067f027645ee86584ba3a3cf734f5214e1a2970f86746c1de1c57c3684c190a68a8921a0c7920112b8a951281861b69810023aab8450a06f1bbea44a5e003d5b3d2d3b4740419f157134a0a7666006eab0630284ed2a944906786d0664a0c6ba9280988fa2994480575b101372b8231588f128ca46017a3607b022073b526058a7c2cc04d06763000b6dd429098c712dcc4804dd2c979b6d89c1adf6db52d466966d27d8b05eabd55aead5cc515bbc349ea846871c2cfd9ea01e2b6be03d457c689383f709fbb84414e249c053391144a7c00f6585109e809e9509413c0d785a2a08e10ceca99410e213d0c7920248e7408f4a85219c053e2c11417606f4585a006492a764209a07c2e553c29190c64e66c0829f2164474d0255d812933e1d7aaa18a34f3b2b165dfd6a82cdd1d394c1501579d494350698563f6e19be01031dd3af60d4a1e924f2469a5b7286fdb14b9d707ddc202c39a42745328c3f2e9ecac899c9b45903d5fb758ded868a5a2d34cec1058b3258f455f8f5d28e5e2fcf305452eb184a1b16e2704b10e3709b498054dfb4bc44710acbf439ffd11488870143a135669e9a7c4ddd4fc2e6401729ac4e8ba4967c367c6ea7a901f6af68b7a3c44dffbb5d3573dc2f09bd1faff2e661e38ecbd879fcf658c7bedfdf296e56ab9c1b1c3aadb3306ff107e0335108f2b17ec44e9ef81e4a276fed60ee8c39ba1a95708414345db452b1d6ce9aa8ad3ea2f8724989aa05b838da5cb03d720613dfa679905931c257d9f327a81281c1898ae88a262ab11a7e35f34e8ee0e69f1a0692138cd8a58a69502861f5acfdd95e587c577d9eb0be33a53e2bf7dcdabc6656aaec94d907f54d8b2ed711b5798fa26359ec421bafa30bb651a280ca7e771048051e16c79d0008d9ef7a2f826126d6438f4ca120041c1c550a1918b59716d284cc1801e672f4bafadc255bb00e6b620e1c9e96817c022a5a87cf3809369a54b4dfb751bd53affc3277bc71175d7616e668621ee2f0096081a1989d5abb2511173cdbbc51bea9032fe044e8233443ebcebc9c65c5d858b43b4d4d81537c363fa82ebedd074fbfc3c2e2eb6b1768f76138e4153a74720e9a0b0a75510cd0770d3261082900d4021ddaacae993331c7c961db3b8027a4c946af44cc24eeb440ae452fe53edb53b4e5f084242cf6013d6fa2067ce1df8e7d8aa84a632a94b9498707c7ebf2757da1b8bee0bcd61c531376092d9bdfa5be52ec2d9d3e8fcee72439f058c2da106e933c1a044c46a6898db05fc6c2d1175d9a12454f723eadd93c5710c58463e9c813da944d044454e4057c6a6dc539e6ad3c9e295d5cc1ea7e3b188218be9194c4852bc4f14a3f0d46b12304dd6291c116daf6c226ad7504ab5d841fa3edc390b4761bfc9cf31fc86ad0fc45e27b73379f206086fb9a36331a26448ead0f04a3118df726c6d4e75011afbd88c0706c8c57113de19c427f816dabd7512405e0a0d28f6d394e4f55099afd4276cf886ce98d24303fed656eb4f26cd306ad47d046d9b13d1b9166b6101e7f8790fb5eb41d0434fc0134f398e46b1c2d0c6c782d3de05fc26ae87be4343790d5edc9b0f62649f11539740ece8dd8cc4a2411e83fcb926bbff64c40f4142632b71c89fd1c0d5c62246517895897f8f25e99c8834bf95e9825e68299621a7941c1ea7e630e1da0052fa04da6037b049428c7e304f01f1ea0e752a9b0cb9302d53a34250c7f5f96b9ab1cf5ade30eb70ca885cc57307fa13ed2dff821ab7dd2b91a171b97f62c42e8b593aad3eddc08b77c52782cd59ce2639665130aa82fb7e349e459d37b2e76f36d404813b3a37ab2b3039d7de25baf9dc2cc51335e416c53e0ee916f1a664877660b6de14c757e5b2e519ed0dfc1b3eb5c8050877664deaeeb42b776a11b01fb857d71b716ee9567012dce698568d9e3682f98b897233d13d821b0457795400da99947ee1f3543c111eab32a2910b73f605eed85d6c041e5529c3d3f246a679c37cfc240331471956c481c4323e3f7f2bd39ade2ac5f611175738af7ce3808336671c7688409467cab698734ab340ebca3d62f3dd75f6394ce116098cdda5ae6c017c5e13db5b8f906b40ef2ff29f3cb64de1d4d14024d88894c72986233d05419c89a67dcbf459a30676a6d2bcd9e0e4fcefcbde43ac1313e8ff17f1403d7e71e4c9702b1befbf0f0cecedc8d0851b286a84fedc5a2d6959feba5af756a76902618cadaa98c9dec270603bcb05d38d0d5eaf0d2c2dce21b09179fa707f230bd33c51eb5d3b21520cdb0da08665cd662b23cdac4bd3d31f1fdd28725adc72f1570a21c68bddc63a87cbaf9a923b74fb843984600347179a0941dbeb3dc39840b7d09be738bf629286def56d4344ba4d39aec16db4892b5e3b1e200c7fa2a46054fa881870195c2b4694367672964876d9f735566d7b6f34e72c0381e12df8c064ce3c36836b160deccd062039440235a7cc05a719fb1f8c04051fd033aab6c2b8699a0abe96be46dd6bcd4b1cbf99dfb51748f546f1215f0b77007197105fa073526ec63d6a4428e596ee4ef73a4b6fe68be32c9b28e3ad9211210bb1d3489fe51de11ab1d8abf93aac4e47e747f0fa5ee8ebeaac18a8492b2f50a9b66bd75560752652a3f699dc3093e8f40a7aea75414140f840dc89246f1a4349e8b2f236a514e24929f1e32a026e9e02775e7c64a098e366566871c51cea9cb2d51a122ce05d06395af4035db967d73d910e5b46e3de1a844d1e7a518db4d9f430367ba2c75e49b210b70bfce160f67e52cd15396c1848bef1ce55d1dd96355be6dc3dbabccf61b307fb00bd696b9865e3ae978a8fa0fa545b1df3fff9767d54aa1de013fe959b6f08a62b5848c91e04d2550363487910af158f980b2fb2297638a64f89a73f9730dfa9ea3643ae14ddfca04ef726b0aa24cfce08f00e9958740b77868b736476e75eefc22289431ef1eb5c51697aa7cef146f8e71b6f034666e9e782a261dc70c1ed96e018e0fd3454725121030a423e94d5383c939c88a2546d085e9e64a4af2c61bc5c1852428753b1c8f04830c8e71b3ee68b7e85b2e457335c2c2f1ae94ec2973c17cc6a78d5b44a89b0e7e966c40ab5e23fb5ba53cc0ee05a3815ec87dd5365b2c2d6a4d271f9f2c35c085da64e6548be68db689860b0e1297238b890802429ac7ae1fcc01b3801f46813a94bbd67cbfceb435b12de3099663faa0c9b34c76375b067f2b2fdcddab5dd42dcbc544665daaefb393f3cf5b828a3ef5bcdccf326d585ce45e0bc38f6208eaa0f94a498529e7b13a97c3abacd4a9d4a242d81af67f4594fbe0de4a78449c62202a4778d1fe1a0c1d272eda152aba6ecd4ae616e53d82fec4d5612da4afd8621f4ccece0ce630df788c65554783beb98a7acc5811e60f96e72bfef3d7bd33835a43bffddfb7b8a9eeadb195e17028ab07c6415df80994501743064c40a165a464033d633c58d3e544ef10f3fd78a23e1b4cb71f04e1ded47d4e152920077f672e2782b0e213404621ebcb83eb4441eddd162266c7b472901e0530bef474be8a654c2e538d01abf0e12615295e6ea720cb0780bad8fb732f44c8ee600e89b92996b5c0fd11f33ebbd62e1d155dbed0929b870283c1de832d537dd6f8b2987e7a1c9781d67300406ef30818ce562faadd79b266eb68fb5ea22e3839da1582fdca2a7bcb74f530f4fcb5e8c1eb992f450011506d8076910bc98e4d69de4174b36786b3c3ea19166fa2f561f443fb25ee025e3171a4b75cfb4da8238903697fc62e22f307f0d1ee70e6f58237c497e60d0e0486ee3bbbe335d4e19e0594439a0f220138983d2d8550eccc0ab62baafd84ae3f19f8dee9e5babfafb1ba5a8f3c0bff2b19c54ef6d87e38ccb1e71e879e2d617ea7314f8cfb1c859453c2970029f5325e243e0bf86e4d88674b97b424d241c01fb87db253120703fec6769a8855060ab44bbb1356542100903e5cae51d20941053d61f0c4c1221a24dc89ab2b0902491828d72cef485889820078f278a146290e04d5d5c40113871468907726afa824042061bc58bba423910a0ae2c0e983859a48389057d613040c690733e896a48a823078ea886a794f93800ce0ad9f07f9c9ea850ed994323141cf0e0dd5aaec8dd7a7030dc7ca9204d9de5624dde549f0434f878897368e6faccc8b556d2347af950b6035178eb4ed33db7ae87c6fe79b76eceb97d860194edf1c8d74c4d0c587cb88691b24ee2c61407bb2d46b3897478402737c9d3b5374fe27654386bf677c4f454e001debdcaeab0293e8fa9cca02a9b0e3b7b8e09941ee977a7d77871e82ccd354eff06aef3e7cd2e8c3517f3fd836430f82b39f21e663917d3b56d65c77c0b57510563f90e1e49c3940da79c0122546c70a7f64dc3d6ba4554cc297f19b03c4709b93416f9f49843d134cc0b86cc59847de7eccce5a1a83326097469b0d8697f9f4c7b6f7e02b2b0e72dd53efd892e7c3c3d9738f15274fc36fa070b2774a8733f8b7ae7f0f2ff93c3ef16924d345c97e549ffb9e99aaadf1e2d6f31a01d6881c7a961779bc109f39b3dbe5c4c068c9b03fec97afc6bcfbab48f416f4690b9859aa5c791dd58c648443b5bfc9cfd06b81f68e881096d5004ba2c59045368baeb2de8706d4423740fe927af8d54bd793b021ef82613e0f34894ec1bf3e72a1c972b24f1f10c34f9976ef254383178c3c147ba2f1a847d0a5c92692648ea97485a1e22de4b0916d18ddc129d3a9382f42ea61c20f6523b2cbfe6887ea2ea3cdf3759328fc955a65ca960e44936471b58cb541d2c66c8325e4e7241095d172cffff2d0d5629440899f8db6f974afaf3f5f5ec60ce2b66ee4efaf070601731a02a03ce514a8c3a11b1443edd4bc771dacc00a7ea39b08211628a2a5e448da12a5f065030157ed973bad3f2511847b3756689287160c0d942973497cea921311f288c5e3a63478030f268473912f2c402875d43e5d940ca4fcde83f9f55c3ee8845ad4495fef066292dc1d14c73247e9172f0d3aef8fc7f2351ace93d8fdb46152d19d3ae35296d24feced6ba4c7203a6a87406fc78eac8fc5475efe8d15a4c9f90e989cd380c937edb42c5676737992fde2bb0d24ec47f3af51c8a6bf2bc97daa7ace840de05a45ee8fe6912f4397a314e56bd3aafb53785849bc7075a18381cb7096ed739c79968cfa1d60d6335c84342ee7e48f39ad84132fe86c166511daa351cc06d25638f261d7faee99f64554a2c31edb1a31d23d2aa7b33f6f3198300be4dcb6567a5ac2b1ab37c76c5c3ce48bf4d8d674f603a7f55c1d2aab26166c3170806e72354f729a4e2f48a54efa46239995e2193cd98075b27928ca04576ef0505ac44be58929c032d31522fd70679780a4e03d1372e614a90ad35f3f862989af2ec26698116687783078fe0b18fe5ab38a3aa02877c66e50ec4c225edf0897eb48b39880fdb0afade937c1cedc1cd978685937718a4252be1649a716c9522f0c2eb9241a2023554eb7e828523f2488d6d036c637e3048687a9cfb32a9f42b2a02eeff6217504be1977beedf4e7b91dc828b0e11f4827570879a2f9d22465013ce8b7ce6fa667699132d7c0046443707c9af0217bfd504aa1fe3a23a838b7a23410f6ae2e659df94800d540f3ca6472936072497ce0bc66ce6e80f553fbac30a7dfedf25a65c00891b9b6f91333d663613afd0a5b0fb52efb821ea4ecffa60f51c1ad27f40bf1dd224ea0ce0c1b9d8ebe2616daf4813f7299f97c94daf6b8d17d12edace15737c2c5f54fe05f0efd633f471597795f480e3b586e3e1fc6194e1cbe7a0cf63253b9c77f3e6e1af0a889edbe5fdf5fad2f99fe2cc16fd038a0718a77eaed80a2819d6dc5f49fda6c79ee34222dda058d6ef78974fd33195945ad59e725f03c7cc93d530474665a9f77a79f87c927d47e6fbef699ed3eebfe20b0463ffbbcbaf1eeb0fd3299b7f65ef64ff666fef7a796cd85eb7889051e81921da14c59d44ee96e49679e5b77947339e247d29be725e2d1a7af68f9c8251c286b9dd7cfe8d82825872585ddb9d6fa851855d42e17fe0edd197c35329fc29d4f06eaf37cac98afa9b63578c1a669e678fd031b9cedf951b8abfe4328908ab509799730c29fd2ed8dcd5e2b7999fdbf3cde563385fe4a5eaea56924e7cffd699456366c189fa8251e8eb64eec338522f8b52274bc177225d2485d60751f6898dca8cf92c5fd7fbfbc4efe0e70db0f619ddbf8a5ce498947283e4dd640b67b7bebb93b990f60c0d0d497d02d35a9195486fa4a1d3b6c4c30a3deb8fee76d06b020c98a98c07225648fda2717dd33b2ae1b4432e4988cf5a6db228c7fabd2e36d2720d503dc23477d83df0468dac2741f13f1b32b6213ae8e2f0d5bc5e4df1f2f3cea4ccfba03b14e513fe0293da23c3ecdf7b23ab69201c485a2d90c2bf08cc75c66318c937b1d2b40106c47a67590fd011cc68754606c00e1573dea9ca16939281985f698fe6d7477e984a629926c6a56d85f900a00c6de208d0825388fdb3a07c58fda5a3a1b81b9ea3a0423e27195d63e159c4339da7886021e4a2264aa4121fd1a35fc7448d9088b387513040900d4a3d88cad6518ab52d8cbebbb572b6e882a2f1bc4a2519d1507b4929996927281bdfdb000f7b60167fd67ae176ea1b8155a6c9bf6287acb38246d464578ffa371b8bc3f100bbd2adbdbfc6d638ec8c70cbaf579565e6c701fa4fc5cf9c6ba2817b42dc0de062d17a1bbbf61d27d4e794ce457f1440e704dcb76f0f1d39c435dc6b395c294f59d9c195a83f1301b3bec7c7bde72270b9aaa23d9f4c9956f23cc63e2bcc95f6ef126e20440bec25de5fd525651d2bf2e7ae4a11a40d4cf94aacf7f3e0534d3cb8a2943cfe30a9e8f35cfb4a3aab1d7e07b6380105f9d9b681bec6a4231786160901468e6de29d78f93f65d8d044627586d3652e045316f26293c79ec5f2ef8cfcd07b2cb26fe0003320ffcfd4580ec42b7d448535ce41b953d705aaf1b3c998fa20b6cd5f1e088e9349af1c418ecb75e90f1ef4ee14037053ebd8215aed8195abc82e40c0d3609b7b25d4a48903208d209f94c1a91880e64c9824c2a537f410a359dcecb921ae69a1a97c7a8680a8520f9a3c07c6d2bb2d22404df5ad4433e82337b2c5b66536a0320a532783ab1d952d9da2e110c0c18acd1b17bcfe12325585c30ee8758edc6a04328e64f0bac8db6463ff4eab080c081ac39cd8535967fe9fb1bbce6e3b929761ce2dd1290f9ef320d0ce32fcdc87d917e08bb3d90e7689143b87b097a77130ad21b8729e57ae0539893c368f2dd54bc33af43494d0880b106c6188a63a987c6f421382388fad16c74131fc7c4a8934ce241835bcd5de21036648162c95ee63ab1fb6f43056719c2c6734a5d98252cadf52814b650003d3f2319e55803f201f8b46934bee05f4b1848a1bb773316e2a070d2f564f991bd1c6d6513df5814ea25946b603fc94799931ee83d00ea95ba70ac5c0a770fda2714a3229fe7f4a5e99cd0883483c3dba869622aa55ebcdc943819011ece06f272529f44a2bd08949ede06b98587e8e3b92f2b3f0b775e92f12dfe63d7de7bbb3afed85beff0ef635f27d32d4604ce42d23febf093cc059b3b6c571f0b60cba828e2476b13b9a300bb4396c148d9b30d678d41e15960bef85290cfe163b84c9da02713cc95e9daeb0b710d58b6522fb3ec7d687ed58bdce8bcf3c0d727b898a4b17a065c39b727c1274a35d07e5e6b8664165cee3f41bfe3aecc242c176a573cb4ea1165a15cd20bed66418f36a5f70d345ae5810bed526bd44fc3f39b5807caf515315a690b9514e7956a1632d941e33a0c6b603475befcb42330bad693ea053321e8ec5141b09412d1db09f76980aaca30a347ec90b8901ee1351719dfe353f046b8f84e49eee14007b96458bbeb5bf8410232ec7a195e70934c897b5642895c9e52c32507c6306df7a528b99233912689a8f6eacaf29905dc6b6ec154660f0afa2df4a9d217c51d8a3a2a3e0be94570d48263609e484c578f3823996ad3a18acaef79bde829d9c686b00e618d50476124117df67e6f202b5184148e6f313077d338ebd515acc0af79d3c69057e740d0d0478629b456c105c90a44d96e7f73a5439b4e05542338c45e7683d559db680414924b41aaccb4fd1ae25391a21df073ef650891ec23a13f2e48e1406e35931015553bc463a9cc1522c579aeee4dba451649ee72fb7f459baf1d6a1fe21a06462ce4162e99d96a661488d4523ec2c233bcdaa55630f2a7f892f79947888cf08a921db6cd1c232a5d17ba54d1ab800a0a69d9c38b4d491f2d74bdb3884a52219c0d83528a03d81955c45eef0349140627d786638546de53133c8727b785fd37aacd825f857fae0cf29f548ec5218378aeffb841afe6cdcafe887edd762800f20800318e419d9656a0cb85e71a7f9f56c05556cc8d74e97bb79709bfe4cf105858b9d5819a2f01038ea425ef4de0ae4a04021aabc34532ae7f085c91c60e1c270a3dc3408ecc4c6153329e5c4ddda9e833f221e194dcaa8a8ac8a8be5968af2b2c6bd084460070df117f46ca06d4d5f3da95e625d96edb110ddb1e235596e63f0f0c23c00185ee8074b21c3c21a12851434ec74686c07542c44e669f4cb28df080894abbba0a5aa12ef5195046b2bbdb4730813846d982789c9b22f11815be5b94c9911eaa839c00f8023ab7ff29f891822d59a0d47c563206e97bd5a65f8eedc38aa26956f9d397295db5719c7b685b0d3ad215993ee8e000b10eb5355bd43d1316ba37a14503e88e69b53bf7faaeb352b474aac42b701742a5d08e492ce2d298a5f7450f7cdacb90e6ee9e2330d13215c9fdee754d767fc4e3e095c84ff3db70f767e35cf039e2c7961ef26e4b3d1a4629bd0781034f0dd2f9bcec862f8a6b3925e048fb3c13d3bd7c6bc8383fb1bfee7cf8f022276579f859c3bcbec5f6765737ef282eeb269e2cae1f75b708f5a601d5f2698cb1acfe7b2ca356593a3fec6a2084510ad8c9dc5653482f85e1dfe2be8e6103bb44a4e68b9ddf6ec3d9c083174a2b4a6a82734c562c913e3d8a35437b60fa019ad784674698e6d1b2bc0f8a8b32bab82adbfd99cf009f4de54f48e4e879e498b5b43cad41fee012ce7dfa3f76cba6759d4da79ceeb1e61444712fbf4a90affe9be2a5f14ec0621f06d4a9142317eaf3d2f5cf72009563ef9d5346937b796a67153468393a3df70a83549236bc74356e47f6d6a4f16a144833b9f404730ab0252e509c04f7e59ba02d4e3c206925ec8a4f20a6a0f2b5a153768108db5ff09573da469dd4d1fb2f7c630390f331106aaaab834e7178b93a630a9fce166a2caf1db98386507d0714d0b0c1370dede63477463bd421d7b9ababc639b4842d8cebb4ecff81d3edc125b811c9b18d478a5b1a4c2916a724f14e1fd2c7033bfc482bc86f50df95b18f2fc5ee38317ca5447c4e4a2c7ed0c09168ca27b3642f97e69d8d55f3bf4d46f4f700c9911e118abc09eb5b47aee2939926030478170120260e5a2ef65f19ef68916d0f8de10cbb8a7d11d34afcc1ee5d0c2b8de1067bbad9f3b90aa56351ac644ce5cdc796bd6e8c8804d851188090bab02a4530ba8199cb830123c2ea8021efff05a399434405507f164a4024d6846a1f80a4acae01449245b706677324bf41a3c0e258c007a6645e4c9f9765f5d9eaa5ce1ea9b246b2912bc11318b30fcc9c2ecccb141d0213ae497d80edab651bd1a8054bc457ff43566b2cd150ecfa24fd1b787ff47e7450fab0085185827dacf86136bea2ac715134c4dea71aedecf23423f36c57053b819d214ee88391a584cd307cbff5e63db168ab931f40d4dab5096f4d28ecd924a8b6f78aa97df17cd44788ff58920c9b4c92b35d795c20c5fee41956833126f25b68d0f42e68224e195b52f7e35b135ca69aada56e4f7cbbcaffad37e9bfa1cf2cffdacda9875db447c8d5218c137ce695ab187b704fedad9833bb8263f02f9a45bb58fbdbadc69c4ce8b9f94e51177f3fdab8cd20683818c9e02148dc872903a27f22406b0b2cd38345fe9607a3ecbb52dac92e59ba18be084c3635da795e94cf98897ebd6e827121c71b177eace6c30b3470479253a20b4a61d172209856233f17d9ad350f9bbd152e5bf0a7b434231ade6b679bd74668ffaca2509fd4cd3466409218cfebc112eb0c1e52ede466d696b25a80b1ee874bcf7266ecaccdc77bb40f7d283d9827ad23fe92fbf7ad4d9e3767cb6be3fc82f905a5ad3723debcccd537a368e7161dbcc1c8b14f9a2d33c7a03e71fea3d3abf3b01ebd8fe9dd81f8cbd14d5489201f843c6674cac2a465e0881121dc83d288d1f689c2da7a802f8bcd9724c379607ed5a78e37ce5b2101d480e60bd69503661dba1268ae01b1bc2c331c12cbf87c565c31bc0e6393e28770d9afff3ae4a253ce7c0adc97ea2953fb28ee1dd27185bd0d4c79a4565cd9eaf459c431afc48fdc3a1de11a8568ab5bcbac451b5daf7bedfcbe7c2e1d27e65378d48dffa2a881c6a43e4c1a1205ab5ff6f38d6e866ec20fcb642a7526c92df43e9201fa55f9fbddcbb3e2921aadb5e07fa0facb3eabb622355e3039dde93c50c818326664828a413ba57448a132056808793cd067a830cd1069d53da0b96634e22ef4812fc60584f57dc5f2c063f72db2ff404aae0ac816c4ba88754715341864a58af16a6cdbae22fb922584ca9d8026a51c9cb928c77a6b6ae72daa73769ae802859d0224e070ea9dad841fd271f0f2c98ebbd453361f35a009dfb306211e1db66e62e97bcc69a13c8191a8b28cb31635da056f5e09bcc08c131ba720b48da0768288eca29c0a6bccc98c1f42316154059dd77b7ce2a39ad90355d9f1958cc0595f554f2f6c610b41221b4b6db61b7aa64423c8510ac0a0f4a4d905807ffaf42070c9f11278f92fe87f53e92daeffc2358999eb83912eee2875526d045806780e9836b6807f9e3a2d1c1f52cbbfa0b7d1ef9211569014a293af8a4b254df14f3082520d8489bf0cc14870d592d807afd83563333b67a0c96470d7309e334fc5f7f8fe777340cd4f0cf9b3cc91c8561be43a3903814a9373a328fc928215b5fd035d80da4412c3c8e133f59a9672148152b46749d0b312108d3ac4aa381a5ac684aabda8d196b21605d4abbba059753dd8798cab48e3659a7e38eceb724964051ff2c7699838919d9a0fc82fa43eb5da38d84a79f5aca7aee64cff7a9f825d381fce43a7d28af5ecb5c82b7667569c5fda368166a9fb46598def0e87416d83e8676dc15adc6e2f1c9f38fcb8a10c4eab6085333c98e3fc4d83e6e5d865b5e8ff1e440dc4d9f479154a10f2f1aac3a5a1ed1253cdef06ace87f83e0d6facc6bfafff8e9af3dfa9fb4039f9d0da3c04da6f6279aa4f1ae69321cde91e311a6c1436642283d7487dcd8e0271c31d3f0e243b129b187bcc87c3a04a25893909f505f06d5549c83e22b3fb3cc63a7d3b8155920fea9061b000e83a6f62b16cf8c7de0417cf9a97bb5a1879c0285c872a2c298e15a2c3f9c78b594a72c5464e87d9e0aed5e9117553f563b79070c51ba32bf2e5da3807efd3d309f53050eee46a9a7fe76caf00391fe2014fc88e81c7caaf050b1077d2e26b3d71da7421db3b6cef0128cffd23e377ac0bc5d6a844c51936a7e04041afffc41dd09a2bc7041e0db1938ed5b6d20e37871b9fd589168714e4e9624d4a94021904279ceb49daadbd48f613da7314f5de594c6d5da25a751dddc6a651e96b5030d25e39e6b3773d011639735a1ff8218c4c054d7ff51b474327fff1a34b7f2444f6cf0036089fce23534a3b379d9e5981c50b5e04e8c02723c0c8ff1e5c6cc11b1c118c190c5078720f424041430d012a92f2217ad250f4643aa3b3ffe4978b7174f95176894b9daf1ea8f4598271f4e8deb948f8e3663f0c173617f856d70de9c4816d7cfbc3d894e78a95cbf6a64f59d4f96edb4ba8c4b083736de1cff6e52d3dd94eb279fb4128c7625d56d9b536120521677946dd3236cdf62137911b9e13900e38d380e0f9702751b5ebb008c123dc40cd10fb619b1cfbf8963479e981b1fa84c5d966e6db0837f5bcf60f3e7e1cea059ca16a9d260ae05450de74c1821d6b60e30046ed9b9f8924bf449c043be162eccadedfe580d8a2c49e5316f77c081c22055c2dd433458e7abacc4b909127128f4153f2d5ecc3ec6533a864b927ec2360872e884a2dd22d2e3ede21d217f8835ad5528408dcc7240efbaf63547d91682bb45e2e0554557df8d036778d49e020039750d97f2071dd856772290dec405d8bd8ad1306cb0f795fac322988c426b7c5ffc08c22fee378a9503ff83ba5947a8b73ebedeea53feb98e794a8185088995606f50d639f3b944b2ff7b2b3701656fd973c56839e4aef4b99717a2459a3f5484d7dfb02ee6328c5b2fb9344681f26ae848b64c94e08d52b7a0e0a700ea7d391cd96a66e48a7fc65749111c25fd34597f5dbdbbe21faff1b47543ab71364bf648610fd1a987fbabb79532d17bc74d9deb935a61c95ac8b0fe38ce0e8c2b898db68d47180e16d9fb2f992e292dc9bf76582f6a20da48aa4d4d084056994c4dc6765c412f5304653368fcb6057deb89548d6c27220cdff0fa58166548819a5c6e1638425c1eddd239ab1d595a66b85d62594c593e3364cd240e179a73de6c793c9edc5648178646bd0b0d2743502158ddeabaaef9abce1cedc25418703407600d52cfad1e3b0c5758a94fa07bf1ba28e753452ed994b1d570259b54c2323e69223e6d5145d483694b37d42d2af209e71cfe830d4dbcda61498d35bedb9bcb342541c8df9109628d190f3f1e8a55db3fe375d34c19b9edaffd958f9a5270d04766894949797fa958e17a060b5fabfb711df8b301aa9c4f13491c663db4636033d1a9002287211bc45f9ef4e4a6bc7b732890be34a01da706e23701a89a544ea3ca2cf837eb6e0d9b4915d7392d7aa6362743f535042fd161c6de7faef5933ca2acafe37408ff8a222733c26720a6bdea8ce793c6c64c65a8e31fe09fbb551f6fa3a8fdfe22f5c6342ff5d83bf9550d1213b940ef3e36184961a9a38ab7c42ad640ba9799d7ed034909f948252a3eb890a84b3bc1bebb281c7c87fbdc01be25ebb402c94d0b8c0f26340d8c990aba2e241a353de4bf2f9fdbdba0f3d358fc318d5b8fb982f1a1ea4bb05143e191ead9a4781030ccd4d69301bd1b43589943edf2779e7125f9ab64139ef8bc1c0034b2ad04da0da119b9a886b5ae0f7ab932d6a0653ec2ef87405c7948d3ed4f912247c3fb3c6ec834c2b01f4544eca708b101bb5be4f6e19ac180a122f59d9c6c71fd9156563f24807f29ed994da43f9508743c1f5c04b5d76eaafc85ece25cd27ce8323492c56247248c08f44cab03cb7153e8007abe75e28d99cd167d9435b84b9d3959828be0b1d42f21456652dfad0be5d92e0dcd27ee4896e32dfa3cf6c4d7b7c2abadb63245d9816cc46786740003e3f3051c29c933afb8645716ea6b42f7e05e619e5887b57f0fd713dc3e3815ac72a29aaf81df91062a58c91af1c6445bbf10e6f7a7014549c400d27dcaf32e1ff74fe327904f19abce51a752938d14d3db2fbd3f07b57b9440a5c2dbc16dd3b8afbb36e78261768f9d8904b1b3a6363d2b84c5efa85a584d41667830ed51419dd44b13823530bcce916d2cc2c7f4400ffb8a35bb9397c8f7625d5ef8cc36f8838f2c211651d035c2d279deb61a1d9c065f3e4910a07682b2ec68b3cff1543872bb5cd89cd85eb014ba2e0828d793f9fd682b0c1b82b6cf98c4e4cbf95e0c1cbcc2609d2a3123d262c43d132bae9fdfbd311b7565fd468a5e4c21e29ab80f8af2fce824d8a106bb0d70462557ff0ed6d2b09b1cd25759ab52136dfddfb62f64c24112d75707323606f63e1bc15f462847eeb956d5b811ea9d3bec1193475d32dbc220d94e4ea2e443d197dad1a314424af13f359cd48724d35a92e7683d28ecbe31f329cd5110e213c86f63e3c2d3be5898981e377bce85a6a0b96e8ac688f544eb9d4732a695179c0cc3d33a9484ea2055215e24a20bce6f8e2f701b4d481ce0936cf43b2bc30e90e7d52db00ac90d0e1e69379c902f8e7e5298ab16b6c9395f23c5dfb4fa08b238c848fc57a3a2e5595625af343a2333320c5724b719ee2eb517e71871deb0c63b570891a9b3f2f0e521dca9f9d03689a393e2fd19f59367c10600dfff9e4daaeea7129cb23803344eb40d5c788feeaf796957f548aade857a99561047c5d11458d05d2cd9dfa4df874e96314c14ff327494f12b8dcd59d039cbcba55710c198a5308da4014852dfe90bd670c04ab7d8a40a1bc7ea4f5e604f05c1ce2bf04c29760d32b42c43870caf38df1baf013dca3b8fe8e2306aa18cfcb2108d50bab2176b2511d9887ccd26c3ed1d012d13a1752c7dd13964094564388698a520c148d984bd8f54870713eecf011ebfbc9f9de4c60d341e45435204ba80ce4739ee75bd3e11101aae771a93ba35eff9a8223e85050eaf83edd6ff9ef6a7afbcd906752c6e93ef882f81f6a51a28b01682e72b07c6659ff987e9696291cdb85844ddbac273001971fb04383bc616dd6f3e4e2fa87c0b80171d637f597c9732d17a446a2d94e8f324016f28e4f5489829a7467a870890d17e252687accc0d8ac37dc6fd31a0e98f25ca02c04dc5e62236c9c8d7d5d0e302f9fd36d64917f58c9b8b8b05618111e66295a8cf8424f168cf63f69fd1b53e52c726dcd7f9f02a6a90d0d3e123a2f2a9ae1a048db5bc07372d0453c106016504e1ff498c56462b41482e4b6eff4fbdf4dfd6820eaa6e2fba5e7099f373f9428c891f176806b3127633d3565f49ceae6831e462b43978a150782d5b71df266d3b195790eb6b7736ec2a47e43fabf7df68b534d9c35b77f0ab9d238e73a8d2b0008101a58490d5f3397858062a5b6a489f292b3c480feaa6532a0da4475d4d58ef3a404c52ee9bc3e4101a67ecca8639e3f6f347340ff3f590bd3ecf10f020956c8fe69e02cccc0075dbc645565369fae6c98f5eb7ca92bbbb77cef670f87c30044ebfd986d79d621b5bc70d16cc7dfe760fc6b0ba7fde284dce231da503b0ddc1ac7a187ac9902ffd03c737430b3eaee3979fc31379df1c9a101d8030bac07ef44835df829e8f73fe7e0ce6d78e7b24b08f24c6cfe0359430fd2ef1f76d2ba349ac1d6ff937e9895fa81ee4455a7a4371c4ebea3e6027e7eade963f8ce5cda7c0a3c6e23b8264d029f67dcdfac00eafaf043e5dc3b4bd8bfb6cff938fc38b7a869f4f427bd2b53fa7d77a02f202f2a1ca59e400a55bf85bf0c674923cbd2fe68dbd7fff85ee3e917e6cd7ee14dfa7809727073a525832e82ff4db939068a294a24c676f8bb85cf192b4d41c82f7cdcc39ce9d0d1310fb0271de570a9127b06c37c334fbac296abd68715062e3f2f68eaba4e2446fc130dba3dac08839537c9d3d1d61879f8b29f6c13d02097afeb529bd2b7122cb7a8657a87d02cd0937b845dd1c8f93aa9ac80e88ac44f8eaa7f7a56ee928f0a689fb5a5677f3de4a79d5eabbbda829513dae914c2a57e7b962e5cfb3a37efe96908f7524ac9a713581817f2988bed3b95f43c7ee3a27cff17f77c59e65275976e96c69c241323b162932302900f543b851d0a00f46328811f35df91593ccb04572485a39a9e105a56024d23c0c63f2b32852fa72266b47c8729262c15b7aa12251d8ac44c85fcc14ab0f945ceca2301d338f2d2f394d9ffd2d5e412ac5b6c4122e459f5db3bc367d0c7df74bb8e1a3a015eeacb143266fbebddfed9f9e0e2f2e900ef1adbf1cf64701995cc7f42a2b7f139c2d0afc0b3c60ba78fa6a5c96b81e7b6eeea5b7e54824ef2f449a1f7b84547f23737037fad610f4cebbcaf0ae625f84bceacfe49d2644db6470f7c692f6aada926383964648e246793e8ec72dfe92a8994c4f2f3fff4951f8ed50d6b6ab601ce924d3e01537262e8728474051ec5619df9b0203353699edddb6ca3515b78e8eb07c06d475161777154052551d5ea676b552e24b67dc6c02a4eebfac5ef6ca7af2e92a67375165ad352c73ee1d76c4cbebe7f0fa0470d19e0d8dbc1a35117736f40fc135aa99b59f465bb9904119c78371e67c4bafa4f3f75c727b30fa148d1391ac259ef3a5697367283dfc17d272b4d6d5c755a6c6d28e8910a0882956672fed28b04b4727ba65101d59ac67a6479d449c1a0cf7583be216ec8e78b678943313b8a260fa0c895f0b465fb006d9fafb6875bb272ad7059385a653ade132cb10adb86faca25628431b2b1a2e892faf1ac7b677df934be0701e84835e64e846678e2a06f9a9c4b86c793212d3c45f452ed53872251801c7ccb35304db25658f89be05716fd03c04df5961c74684a04cb8333d0964649de06c46174bd72c91a5e2b246270a6c76de6bf1a75cda35a673fc53befb05132c1c5a2c9b70c988513b5afafc2608b49638c7d3c9ff8c40b90afb01a3e3d73ff55155181ca89952951ed65bb24b94656617879c0ca86ba7ef22e4249f008228ce198bd3a9bcc1f24a8cded7c6950278babb4ddda4f5b0c0088fa8bbfafb25ba511a4e2e7e02182332c23466c2f4034bb4544955696ab31216f3acfc31b6cc9eed78f43ff854d20f751ed8dd91e29b079ae941077ef813276e7427754dff4dbf27921896f64d1e14eece36c5470d69f8c3ce31f79bf120888d98d30f82efa74d31adb2c9e05f2c4428f696ebaa8d53e496ebbdc75b585b95d83765e4997bbf907eb7de19b97c83e01b440a5e60613301e0bc187311b1e1a37c1feeace2a00e041ae42a7d513aa8b4aa46af845e8852217c9d868eba8226083cad02befc44b00c3860f26439ff00000000000000000004945b37b6b52a12919294498112a85289cecb534a294999e4c1c1bbb03b7807ef1807ef02088117b00bd20ce00bea0c0288c22149b8cdfb9992a5d4a170125f9fa147c7a5ac3fe1281b4e4ff7a56f061d2e42004e388a9e460a3aebcedc6fc2318a8ce12c8dc88473a94ef814b131578a11c0128e67932d4c33866893120e12f39cc9aa20b21d4ac249fdc786a52bb524120e6ff966df7735557d84d3d56451a141fed7a68d70b80b9d1b751527be16e1f41773bb444db97a36443885be7ec85362c1842680219cd2c44e91b97a2c6d8470f28cb136339234d93608e78c69efadba62df5611080008a7515156432b08712ffde09033358928e9a247cafae014ecc45f5d6ebc10400f4e22a998bf2f9392cd120f4e410531fae2e4a56c9a1d1c553543d260797c56822f001d1cdb3765539afc948f3e0787af9454bef4251c1c848974b9b485104ba4cf057083e3a4dbcb3e1163c50bf2740101d8e06c1bf1c5f2a8b077f91a9cd3d67e4cf69a8409a2c1c12bbb4b4ab76f31a56770509f71f2f7acdd8932389dcc4c2642fef591680c8e9765ff2efb7dc9930060704c8bc9355fed58cce22b8e1ec22fcf6ed76578ae38867c11bf7f32470ab5e2149b216c921c57d119561c5286f28ad91a4ddcb28a53ca7a7b4b48b7922a559c2e4609b118ab909264462a0e5e1636f3f9e76f8e3061062a4e21277ea8b9c8bf163557758a73bea9feb252f512bb298e32a3e737de5a8a936d4a25299710290e31958a7a99bd59551c8531a598442ca536cf8c987ba35588099362847ca238e8e9cfd0ca14b2d7e442711c61717d3bcd36a60d8a93ca9ab17f47ff2b924298f189e3a98d2526a79e45bea030c31387e0a3de2b9dca788e74cce8c429f62a9bb8ed280ba31c3e98c18993ccbf69a44bbc3119d3b19b38f80811a48a67aaeaae234b7758a169e232b78c9995665923c6ac724bc99208a6bd8f3d30c2f8a2a46046268e1a7b497575ea04cd317110424ace28ba620c532f718e1043627d66e9dddf12c7cf16d5139616a4e8332a714e731ff513f79f15cda0c4c14346d83c22a9f28b7e7c648e1a63c6244eff12f2be062b953f97c4298ac8225b847c350f7d7c4c6146240ea3c22531ea73572f2f248e593544375196f3aaf488538acaf4914c88112277c4495ca8aebb6c5fac8d38499651a1fcf3e2a524469c456d5d8a5c418b38a87d9bf0961ba2be4fc00c451c4d68881039ca92dc3711a7499b17736b62b8a4828863e8095effbd9fd962c060312000868fc1586cc130987188536e970aa26e67927220160c688005b08000a21c5886388868361729fe5424a9f1c802028528bb424825b12a4655f3aed27923b25f943f958e2c1268d11586185f742101ab2a306610e2ac412f630c173ff7d43b1ced208e6d29c534adfdc7c7478e423bb4e0c22016411c74ef5b4ce2c9be3505e29ca3e2f86ab80171505a3b2faad4e90bc13f9c4e840ca32e6e336eca0fa7f41149df54d010f19fd187835dfcd2fe19a57b3e1f8c6de1e1a22a215ba4d26e5192d7f6df844cdac379d30919fe4ed522ccd0c3297c53dad018714c7c3b780b1c6b84197938f5fb59886432c9646ac7187c818f8f8f8f1ca57838a89425eb4c657eb99c8e2c535aee7050fd2a5bba7d2d2d3fc30ec724a3399354385dd21483197538f96612daa2da96c44a2b00821c2898418793a4a8922fab37f42937471161c61c4e39e23228716b37b9d691850b9821877325b75417d2a5f90d69e00b31923b20f91762244f33e27036b541c4758d31eedb33e070b09399444f887e97bd8630e30da7bb142ce5bd846594f30733dc700a3a629b5b095d3df2369c4b5e8eca7915f289301bce6357522fa494a3570b63c61ace162a83525fba94c56c861a0ebf13337c5867dccb66a4e1dc337a67a742ab08a1e11483680daaea27865b737cc15c6465402330e30ca7342a2e4f4bcbc4b9be18238b8f8f8f8fcda14c98618643ba9ca5e56f4390997d7ce418230c2365388c8d7a964dd01074cd420a33c870fefe1555f548297bb9c01861e4a831c2b025cc18c3a9846ad658b58be1e013844c3235444d2d0ac3e1eeed2387e990652a301c54d63c9b4dce5da689f9c2219e64b70c994f85d24e0b33bc70168df559ad3235115e17ce5b36ff7b23933a6c2e9c429892531b7553d4f9eeb6709e1cae159254f42e73d585195a38eeffc8cfbbb5513a532bccc8c22188bd8b3d3649857c49146660e12c9a2db4924a8ce4e15de15c2d4ae64dcaa641ba3d98618573f82891ca34fe988adc55e17c959a26c349d6f08b0286171790c10c2a1c7d6ee4a969fd66eb4c7140cbe098318563eeebcf361d1264d2c700238c2e8e91c271b374570e9dd9ac54617881e3e3c3a089c2b9254d74550b9710640ea1704a322a8aec558a8ca3e4858e309c025a7cc1802b1468a1a0cc78c2f1ffc4e26fa6190d1b92010f1c1e04031e17c0c223b7f8820b2d7086138e19fab4269d25b7a199d184539ecb7f272495fc021f1fc50583f1a6b2308309a736a1549e92789d213b630927f912d395b577638f9470cc7f8d131933ed339384838f46ca64b59b415d6906124eb755aa345b3e13761fe130215dec109979f935c2494d16adafa177cdd6229cd6d4a434794426954b09e4e0c20c221c93b2aa52f795958f87805b8648d49032ac5b23866add9d65093d26828470f0cbae8c1bbb2fe27ad1450e475a6cb1a30b9f1184538994326c57d2991ae62820cc00c2a9cf72c6ac3fe922ffea193f382599f3ae5c64e7091531626a1500410e9ee18373888f7c5ed693674a8286193d386d6a4a3199342779a82a0700d8308307673b555182f80842a9af23eb0b2ec0d091a32a2b4b0540906304337670487e428388f3fb93a19a61860e8e193425bb94453428db230b2c3cc4302307c7141d3725fab2b8452b605e988183d3c41e5331d9c4fa8566dce0e47111b45223595874860d4e4ac8bc655a3b5b2b8507336a703e2b5da915e2c80d293438b8c6699a52eb033366705ecd705a6e94503b73860c9290545d44bd2a7ab965ea9d7fcbd56669433f2306a71825310993a1d757d291550a21300306a7122b772a64aabca0bee2d89b46f643a6904b8bae38a5f1acb4a7a29b90702b8eb5f2b591fd4e5b48597148b2f7b9a1b99b6359c5e9bdd484cad590cda8e2dc22938a69a3e22564a9386d2a0d9df6152f64820a837a7ba520dea2292466e54b17df5b6c6d647f8ab3c69837dc59e6eb903f34479523c830c559deb47c826a52ea241e5960e1614c46298e262e6e94b5f0c821234a0a4bba86a79deecd201b8599c3aac3124511ab2b83560829cbb462ccad3357a34e828c501c43e7dc7ca8ac5e9b3bc2e822cb0132407116f318b9d06ba2542ee313072923f385e88f9dc4c8f0c429c9d39727065346278e316da6ec6721ada7bd4d90c189a3ccd9c9244c65ba3375e4f01568b1e3e303b720631327614296ea4b901d7722a589a35b7de8b4969c97d99d828c4c9c4bd457728825cdb46b093230710e19f6bfb4d4f5bcfbf83825c8b8c431cdf7a9c43859e278e222af89511b2124a31207253466918b5c4a8bf603322871d86c8b1fa371c62e963189937649b6592b29a3431992288b66cd160f5353cd95b2fae891613f248b2a2312a794137de4cf851760bcf1c802021188c0c7c70332e0a15500e009199038b557a477f330b9ed3fe218ce4cb5a598e437861d71b6a04677757aa6978c8c469c3389d2dc7a7e3551a2234b01321871ccfecaa36fa63f63361290b18853ccb234135a235cf58a385d16ab0ca5e2d55949c4496cb75f2acd9ca9623ab276a81a6420e2e4675559245abc3f5fc621cea923295ed067a56d168f2c201001356290618873a68acbefcd972c611d59ca931715302aa310879f90436d5c938c98f2228c2386013208d16cf0f810afe14da42371e8d8c111c0418131c0f0820be7c2230b2c3c4e2217640ce22e356f9590b32c85d8616d551b73a5354d8620eaa30b198138c619d16b5a438cff4647627511c3e3e3e3c4a8820c401cdc82f7dce6127b0450ff70484973776c8e8c77ae408baaaaac000872ac0c3f1c539efca55eaf256cd38793c4349b44927a4a862d0b2c3c48d940061f0e7a3e7583fa141f3bede1a05dea42dd58dcb4d80964e8e1d47fb53a5ea6a14254f270bc1fd1d31c9d39568387b3575ace98adf26bd8ef70aa4d0de1f486987e527638a5863fa5276bd5c65c0b136ca9c3215edeec8f9315837c6d0532e870d89afc3f29881f5d77cce19cb77a42e859bd952e8783e52df5bdcce693fb381c24b6cc5b85bc9843a423abc690018773e84cf1e5af367fe575641516c878c3f1ac237d4411a341525420c30d07195bc3c87495f754e6858c369caa5250d92c590855ba0e369c64635aad4d41c498f06c0dc7358d5a72632fbc50c3c142d85cd2943021a46938eac4eca254264bd3211d598b86b3fd9efcb49c61222709ce705ad77c9f62be8eac307c07964086190e17f53c839ae8aff482b1c30b1d596538bacfe78834f94559928e2c0e8393993260c820c3c12e6750b3a5c1e4247d7c1c0a3e3e1cf0f1f1f191a30e05b6d50519633888cd974258559fb3310b1032c470903183527e495fe7f4390821230ca7893967217b49aaf53e3e48f14006180e0a55877c384670710d717217b93d9c347c77ab8d76e4f57012494851f35e3daec9c321e59af7d4ecf170082289906a42fe0ea75db9ac58d2e28fdeed70beb98cfd565264f55e8783b098207b4c252525a7c331f897a6459ee826e4733858cab8d396841c6dbb1c0e7162f655d77138a513295c9c5e549be1709c1459444512da84f60d87bd0f3d312417b5aa1b8e2a496c4812d4f2a56d38c6185478db8961df74361c442cca454c5a2d8bbe8663b9040d97967744d0d57098ef8c2157f469f33c0d670ddbcf3f891a74e368385e9d9ffa7bc6bf8d9fe124ae967fbb429fa8b8190e2b1a2946af4c6e162fc339464ef64a2654da9e0c47b39864ca26154fef6338e99796d8cab51173311c4fbd6e10c9f3322985e19422229f9c8d24eb048673a90fb92a290475a32f1cc2e8d37ad246840bf2c231e4d8b6342162beda8593926215d49998773371e198257ee76f346c24d316ce9ae6fdaab552843069e12031979c24e49c8e98b270fc1775ed6a49f8c9242c1ce4d988b2cd39a964d2158e495a880dafaf309a648593695fd31073aab64a553888b958c959b2d1222a1c44bca84d21e5cb259ac2295a8c16ab4fc64e9014ce59fb7722af39f31d854332ddca4d215ce8160a270b424b742f455e8b9e70f293f226638e8cadc809e7ae4b71e7846c512a6ac241773754da49efa322269c5ef43f834c9bae4eb484938893cdd634620c1329e118e6752164cbd8a550120e4a5f9eb2142f93421012ce26cd23ecf84738d69ccaacae196b77231ce3ef2d836ca43a79110e1ab694ac68cacb13110e2988c9d39ac6c44b433865c5ce1373197684847014dd19fd3d7ff4464138b77686642e13454440388fd709ff51ffe0f0a536474b9a6a4bfbe014d2a4c91bbd4f59dd83433ea54bc491b5de300f8ea6eb2673d9da457607e77bdf90f24d14d1a90ece5bdea18408b9abc91c1c369b0a3f2123268b38388b65dbf7dcdce0a01b49b5e41a75e2b3c1496686a0b43a74c43538685453eb1761971a1a9c4345c8f01541995666704cf2336ad2596a5a0627214f877da53a06c79ac9e7be3f016070b2d013459bc66ef05f711a8f51ab7a62579c47e42f1f1351a14ddc8ab34549e215d166ddc4ac38a4089ae744dc2b45bc8a93a5a0352f47befd10abe23c419549ac3469fbe6542c4269906f6a66549c2c34ce7b828ae8934f71d88e705e67e1329f6c8a53dc589e469313cbe4521c82fe50192f63a3984c8a63d024c247dab9091f8fe26c3ad3e4d36a52e5c2a23869df38dd2cf57d1987e214cb4f244ffcbfcb1814e7dcd3b75fd9f4d4e24f1c84799ec9fa9f4d22ec89c39a478cd519725ac29d380521fb6735227523cc8993c9c92611b1cdf57713a7d011df114dc8d3fc6ae2982af76bf5c4ee9c361327a1ef4e5f50ef1ea2c5c4c92c68b7cbb1b70fed258ea2920599494694b9d71287d90a1a29b7b7bcbc953828d3d7ea5d9bc4859712a791f53b919e3222dc491c2d46ff9ac8b6abdf4ae2dc1b5b62988b359adb489c47c7a995cac98ccd42e2b8a2744df45b7e5ef611077d9a53e405edfb9575c47926bccc468acda26d234ed1249e4adf1a8bb165c449d285520d91b1a5da459c2225d5914dbb2a57ab88a36f5e317d6123dc64137108d9b4a8094b7e326411714ad9ebf2ba86e5f2f51087cf2ceaf4dcf3b457431c840e1574c47dc9e65a889304fbee6e3d53a7a4843888f0e115530a3128930ee2545d952fd56b4a2352411c75f37df2b7ac79480371cceb1a5563bebc1d52409ce452a29d86eb13adfee164992fc5536a42caa9fae15c63b3d92e29532aa97d38786cee9f2062b815950fc73f71257237349ea87b386d9efb9351eec206550fc7df24318fb70611329a87d3e8b1987bc7c242a3783887254d11c3055bbde81d8e16f34eaab7d8e1a06b49b4928a195cabc3692b84ae12caef33458773e6b94e4c26ae4ba27338c9780df17f7f3ecbe1204a64d85d10d93571389f88499a47e56ff57038756a741d194de7e90da71f392d265469bddd704af99ff96e2962d4db703431a9246cb01c9ac486f366cab915f7f6e26b388bd0f03c6542b4ab86d3e8ad37297262d64ec331d204a18486d216848693de1b7df9e76ece339cf623691959974366cd70fe91dc2f275b93b20c0771233dc5af8409950c872423eb565ad3a4ca319c2c3788b8f4aad49f188e9abe534f4d459a260c471323d1fd279c4817184ef2a7a49ac909f1fbc2295dc7fc7ac47409492f1c4d756bc4c85321efc2c93a377b979ec8262e1c436552263129d514dac2499fd0d3a59164ac5a0ba7892592e5c83b69e72c1cb4e889572add289962e124944c31c64247e9f30ae7b9985bedf77e4b2b1cdeae6f364b6b6b5885e3571e5daab45e998e0aa79c22ffb63b7bf2140e12ce7e4e2435a94ae1b43132f7c89bd09689c249848f9c0b214e7a0b140e626d726999587f9f70d0b269bb46ed464ad00947bbf3d851b9f15113ce7ba97266d199bd74261ce3c4eb44093a82382de1b0a72f4cecf4ccb6120c3ea649bea56f004938bf7796cc3a4ac6b83700249c2d68d1117efebada1bc0114ee1e77eb2f5b5c5bc0118e1b05e27cc54122fc1bc0114e1589d79da7e319310de008890085964ea177108877841c9b41314c2218a1c7d49921e310ac2719457aec6538de103c229d478289764ebfb0fce969999236a7235cfa05972e9c1316994e8a682a7e98d07877c9a7a7126c639ede0687ae95c4d4b442c1d9c2f77d7e99219de249783638d0ca252ceedd03e0e8e1db12785bf911bf31b9c6d5683e6f80d1fd9e05ca943553545fb10d5e0d4a6a52f3798c930d3e0a49994568b10e7cd9dc149b5672e53134cd62a8363f049a144c49a78c7e07027247d7a6906008363888a7abe2273579a571c44c52031c278c4d518579cca2e845e92254936d38ab35eaec63c5169426258717453a5c2a90a49ed5fc5f16f4e5d988694f6abe2a4624932d99642487b2ace76ea4b484ff5d8382a4e276f2d56a7539c63255e29318996b229ce9972a9b5b499aa5b8a53ba9021fde424b8498aa35752d24de4d8487114a728bff26abd9a2a8ae21484d871194d11f40bc5496b4ec68a985ca729a03885f8e17eda3e6e2fe513275db252b5a6204f9cc29f249159fe7129a51327d108426be46562a570e23c2ae90ca22bd92c563671369defe956be3f56d1c4794f8ec68e799df23371fe5acd8a21c84b6dc2c421a8fb1041932e71ae98b4ea2de1422559e22072f45c2c66a938aac4d97289aba4e26d34214a9c5ed3ba4dead8b9d0244e9bb4d48d3c9964d492385ad694946f05a152381287512b1a671a76c49038c56897ae33ccc5ec3ee258499de812fbff9575c4e9deb4bec5b6118778ae2d1adcecb265c471542d8eb618d627bb8863f85fcc5caa4e6daa8893de74f11e1ac2344dc4318c6ed01a5143c4b93746df0d3285fb698738e41d93dd9bb92425cd10a714b9c4a62839d495568853693535366a7326a111e29856f6aca2cf4710da204e424b69881934411c47c51192b466796b2c10a770a3a11d59e7336380386e6c4f1396828888b13f1cf5375ad7a678e69bf9e17ca9d33535d29a6ad68783f85bcd242fd137313e1c93589131082d2b33b13d9c367dc4a433f72dc6f570d8a0eff2abd7845ccac321dd6c56f84e3519151e4e5a64d568acc5b8a2bac3f15224913349e6d0a0b2c3d14e7787fc3af55f571d0e2a7294c48ce9afeba2c3b15efdb32ca5a6d53587f3c5d2ee9aa7a55e2e399c75e3493a95539771c5e114f465b9937de9225b7038ce55106a95f53dd57ac3e15433e8cb8d19122d379c34c6ebd31a96369dd586b3faa6932137ac42586c3866b9f0be23228c8cb586930ebd49aea21a4ea9b2f65253ceccd370bc8a49a651576ba341c331b684dbd79469245667386c680b13c3ff2c6e86e3e9b5b7e92cb5f0cb7088e1ec3426fdad3927c32958d85f47b894b2680ca71a9d7017a3293989e1b077d24a7df2e6370cc7482a6bb26b53ac80e1b496f2e88ce06e21fc85a39d6aea0a512e6ee985c3c8ace932c4cfbeb00be70bcf90a4d4feede3c2493566111f6253dacc16ce55c133d79cff0969e1dc6d3f372347285565e1142e58381993db9d8408000b07b1392e3264cbef0811c0154ef19478af1063283f730be68009002104608553d0da7e9a4b82abee5538e6b0f91525fe4d43448553f5ee9a6a10391b9453385d88f2cd65d17b26460a0791f4da8b0af2c25e25479547161028001422a4fc50430f47f32f3d6a4b89dc7b1e0e215ad0a3fb47483e351e8eb12d6e909f75957abbc3d9e4dbba684c4b9a2645a8618753ec2c19555f217f661c1e38ead0c97a28ad28172aa4c3f1c4a9d06255a32d921a7338bd09b5212d6935db500d391c3388477053b61b49b2a3461c0e9bcb8496b7b49a2e92639550030ea794473433eea5fc197ac321e8d0782784db958cbbe11472a578f1e4e85bcad568432927b24a7ee54b2b1b0eaab6b3f4297797f8ab87c161884101ccd70dd458435935b76892942aa4128bd1b2f79fe30be6a2ec0b263b460d359c923c9fddb8a5ca344547560e0fc38b2eaa7078786481050e0fb2c5f1b800161e86fca0461a4e51434352596da3d2588850030d67d7fecdd6a2d16cc267389a98c81aeaff947d6b86736ddaa685344a6ea60c0709329afba414846976831a6438690deac2480cb12be7311ca2dd4f300d633159311c266ca7c6704105a1ac301c9476d59c31472b2e0286e39f4852959a27c94dac0635be70881a5e66c7ec2cecc90bc7d49a2d8a5cc9a52dedc2612e28994276041583900b2793e8e323838fcb7f35b670580fd51cd1926eea57430b875c31e3479e919821a6630747c003879502a4a246164e69538ea83156b84ad9964ed4c0c251429475199542c644c8812d90a78f8f2f76984fd4b8c231e99e04b16c122e449050c30a27257c757e2b28b5b8ab709ecdd4ccb70badaeef126a50e1b4556726f328756415db81a582a35338b9c92a4d7d51eae22e8593758854b247e3cb57f5418d289c248a524926d5e4a00614907a72e977f4a8458eaa04418d279c6a4ce93d4d2a9b9563c70efe628cce61f65d6c4089136a38e1f49a828a9bbc33a26443a8d18453259fb8e9914f82fe76508309c718a4598db80a4b2a4e891a4b30e84b72714a344f574309c7557f134958c80e9c418d249c4be488923fd38ad2b58e3435907012f61a428ceafe358e014618570c4616bb4738c586f4b4d429ba52c6831a46385fa6142469371f21535a50a3088768da75c64b47aba50611ce659b3956567e418d219c544e3d2f7552cf4c4502391c021f1f3584700a7a83b6d2d6a0625c75645d3972d485175d7c7c7cecc03186043e3e2830011b95a8118463b8a04e4f5ab78db77464993a690b31bcd0a1818f1c1e383cc6e8c21d0046165db88e2cc6e8c21df0f1c10552c1186a00e1706915362efab31a3f38c9a48257c49844a5c9560004390c50c3070719844a0c997c64838a8eacd18353e5dc486376571be247a8c183f3f977b9a5d97f89e11d9c5b5f470551a142c9560767139363d4e5ea0d4be6e0a4bb2b5dcca72b5b2e05a6fc8b31b2281ad4c0c1f93f3537669b5f45f906a710a2fd52289b70216b8353aa9c71aeb5a2df6b0d4efa27b3da5cc36953d2e0104be3aca46d3161b9199cf72497c590e2a36f191cec7be47f3df48590522306e70b6bab194995fcba060c4e7a49488c9da437b9c6579c279810d2438520c52db6e0342db8d0028b1c3ac2f8c2930a68b8e224ee16c284c53ab27278f156ca16390a4aa2d18a22efdd4eb8ac31489915883eed5e63b92cc80c035c8c13b8039073a0ccc53881a71cc851b05ad05885bff7a9a5dfae1368a82215a24163549c6b09aa54ac37b3fb92845e5974a4c7052e6d400315ba6b4ccdd61e6a3ec5414694a4bab715adeee3e3e3c350f0f1610846185fd030c549a46cba26a420ced22c85594ebc45d26d6ffff1f1f1f105173bb428e66990e2362d9f3313f3a40ca2310aacd26cb428e1f2be5b6cc138b2c008e30b4bd010c5a55ac62ca4f3cf6e078d501c72a85b4ca664c8eb3fe0e3e3e363c70e68802291549d6f52f14f8fc59c80c6270c429aba5964511714830b0d430c0a74f180c3000d4f9cfc674e25252f839e241d59be45d22247175e6492401716f8f8d861022fbcd0110039d0e8c43194edbf8a7cabba290c1d24d0c23be081230f343871c8adf16b29a9a0697549038d4d9c42a2a994786f96f29f818626ce27a34facd6585da96cb105af166864e2601ac526e8b64d25b2a3001347d5c99142cf7528d3e6282ed0b8c4f1bb720925440a73f37fa48033a00509b400c30255a2a06189c3e756f2da708d0f5f4756250e3fabaae964257187f28006250e4169cc97fecf5fe35e55031a9338a9ef53916cfa592aaf8318185f248286248e5e59fdc2f5c68651d2919523879b80c330333cd088c4c994e61ed38ad1b3e32fd080c46163ca7c694b4849a1ebc802c351a0c5169ef2e3830be481d4856ba0a0e0e3a3683ce2f03631a79a0ce1df81e1341c71085a6a261a4986973017671140a311a78a99bbe22242dd4e1d595611401df8c51660e418c38b2db6e0ad000872248006238efa2a32f45fd2937c91a35805682ce2a02f57eb4a89d292f4628b31f8025cecd0828b8f0f2f748b0c34147112254683cecaa4177b8f2cb0f0f8f8f8f8b0ca518796885330b1d6f30a7fcb1311a755f5d3959942bfa91ce2782534458a7532c4419c5c85c8783dfa529a4d805c070e2c3bd028c4b94428bd26fab217c239c2701264e58006218ed92516cff229dd0a2b81c620ce27a7da43a4e5be4c09e292301356838138ee69d833fd10c2270b88c6dc54699c6a2c938eacf2058d3f9c494d5353c628496216313f21974c9868f8e15c23fb7bdf3ecf4e7a6da0d187d39bbc093da3975973ec81c3c30387070e0f0678648185c7063e3e727c7c202dc640830fc79eb10cd24c776feb7b386a4a9b59234554ee748b2d1887e6a8ed010d3d1c2d54d06dc9a4fe26511e0e229648d172fb7826150fc76c77bb1adb3031e71d0e9656fb55b34529253b9c9432a12e9dfb28ed963a9c45c6a03758281144ba326540830e875861294b689fdf3b8793168d18eace2487d3c414224fc31a87536ec6f5c822fcfe3a389ccd2a95896b85bf685500b30220c801068d371c53237fa80ba936f377c36944ce9c7c064bd94505018d361c84aa864a9737969dca201b8e1bd284fcfbdde8d78a181fa0b18693b78a2cb99f62b7aa181ea0a18693eb25a53e64f7ce851e381a69388a65665d08727b74848693e5def58af9249b489fe12c429ad26e9553fc2c34cc709e5c4a041d6947525534ca70f6b0086937b2c837998e2c0f638c3e06d020c3f13f3677d246331da6319c6ad325bb537afd422c8653c4a43233a55728350ca7945a54f0af724bcb81e120d577f3bdee6653f685c36849166d6564875f5e38890fa1ce7db29ee8bb709c5c278489cba69eb970daf8666d379e1ac92d9c3388b16431a785d3a8067936eaacea928563dcad926fbaf2fbf65870467fa28a38f915feccf99621946e85c3a648ff25a382a6cdab707a178bd11a2e8bf013150eae9bb22f422b463a4de154c1921cddf27f8b26299cb32c5a596f47721745e198a5416d5b8877e1445038851833e7f7620a11ea271c53a9ac8c881b49d9ed8453084205214f528f5edd8473bbc5c4ec3aa9b46a261c33e4b7abbe6b3f915ec241c632fed2c9cf3c62251cc3c92cd2749b10c2e6249cbc4f6b8752a5e96446c2e9624b62a63bdf30f908a7fb4d92fdb4aa648c8d7010b1afaca5722afc5b8453b66b9c0a69e94768897048f9fdea32457ada3b8463690f252fb44238e7bfc5df5b532e5e108e9a26858bede8f60d100e77153a63a52ec66c7f70b4546a1a4a890489fbe0f892e2c58cd46b29eac141bdc8ab90d9a2891c0f0eb194faf1b88c492d3b38c9247ace53531d9cb47f5f32d973709a94ddf444450c1ec2c13156501e63e2d7377d8363cea40c5f9577e436385f24395e19ea7eca1a9c242469956e4c1a3438c9c821b5cec2c80b268d191cbd7a84b8107c6fbda42183c38f92573ddd3b7f491a3138c9249d6059bf2c77a40183b3dced6cc9cc7d3ac7571cc4280931c6ac33dee88a739e7e9f7cf1ba28632b8ec972826b99ce0c0d5971b05152c742dc6ab67015678d7c69c29ab9f67caa38f6a628164ae4efc9978aa34b907315c5552f79a83886ecfd267e4a9e2cef14e71c7d2aec8f965cdd99e2585aef928a77841473a5382849a17123b2d9e40d294e5145ac58bc53cdb619c529d4b6c49069238a839cc82bf964b899cd84e29892f72f49d3f17719501c538d7c7e4ade35a1f289c384d4e59bc4a967b878e2947cc38618a1d233593a71922046f3cbc56d320d274ea951a2c836cda297b289b3b5a87c2592d6255634711809a729b63a72939289e3fbc42029cceb2ba460e27c21445e8db90a1bd9250e32696d510daacc436689636ccc22c39d4d38b555e2a0b2e7c8588d6ab61a25cef6562a838eda5ebf26f126dbabdc172489539828ef2fa54fb61d89d37adba7269572f990388996ea3295226e6e1f7192267ee1e6e205ad3ae2f077192984acf9a56bc4498efcd5ddfad89614230e1a2d88254d2fca941671be8b499e5d16f3b415710a9675549eb9bc114cc4416efda4155921c743c441b47a88c49064f5e510e7d45027bc46867c152180210ef236faa84d1272157900010a710a5742e5d532d5ed3f0108619cc8777aa6419c4218197e2e4ba62c88b3441b5557aea1570fc441c4d33badfa3b1602c429c7b7e23c420a57ffe1a022959a38fad72eeb87639e10fea4c4fe08f6e1a4237a33c47806c7505143c4b6cb847c7d38a7c5faffefd3f1a7ba81f7571611d46c7a0f273dd9f4675b7d15133d1c832a656512620e0bd13c9c8228ad979d7e953313dcc0c3c1bf44ea49109a23c5dce1a81f2a29bdee2999360adcb0c3b12aa244f391af17792b09dca8c3d17b44b00af2375f257438a6760655992d758fe770d8907e1b3486a4772d399c94486b99a9b42f721b8763bc7813e94292ffcddf80c3d154d258bfb2f9aa646ebcc1724b277f61f2f191a3bc08e38b31c0409845b8e186f3b58998a91399fdef3634aa36d4572be9897d09980da4e07523d14648bce8420b2ed6f08cb548cdd47e27a3238bbfd8a16a3045ba3e956a3b36a9236b8c30bee0c26f0437d2b02439973bb7b5cb30d090aa55cd7fef12f4de82adb2c8028b62886738bd4a50c253b28e18d131c331a5afb3d30a35db5705de450e2ec0f0e2c178762cae8471a30c07cf6cb958d3f317a61b64284699f04d3ae46338acadbb5c0935d16614c3b9f29990f91bb54ebf37c270ac4da9b45dceca9d3ee60e30cc0d301c35a9abe44cff9b22a423ab901b5f38e4d820bd476b184dc70ed6615f70b1438b03dcf0c2c1ffe594fabf934b425d38459839297a7357fa9a0ba798b278d508532ac27b5cdcd8c2b9f4ba89d68f25ebcd8111c6175a305590f5eeaea7fa13dcc842a7dabbaf3b6ece12dcc04226e76159b22b465f479699bf7105621a1527dd5fb495dcb0c229af4ed8450aaa5fc455384f52723582489ba499a870b8b835151dfe7339bf3185d3690ccdbfb50a37a4709013f2ab72f88b9074140e335934a9e90d115d85c24969d29922f522fd069f703a990f912b5ede3e74c249e98c6dafabdf68c2f9d54ce2fc6c20b8c184b38bc42c49ddee67f49770083224a57bf442f4540d430c0ae4e086120e41c75967ca2dfdf725e1f8915484b413f3ef55750309c7142cd39b867d68fe47e8ddaeafc4650822be6184833ecf90a4e6326ffc221c82cc74bd659a46cb4d84e34b3021bb7a548e096f0ce168b2effa1ff25e64e8123784708a5cab69a57d5b4ec950e246108e23c94b33ccba59883be206100e5afb73498518d7746e8ab8f183e39d88bc2f93328e706ff8e098c4ffa9c8a53f42927c7c5409e1460f4e3229e79fb69bf6a8e8c81b3c38deef5b0c6ac4563ccb196eece03cb9bc54ea154f0b3774704af9627eb5af11620a0e3bdcc8c1c9ccb32f5af8d2700307276db3a06245510f3d8b196edce0bc416dd756bc37bf0c0d376c70dcfbdd53a22dafc95483e37a060b39692a559469709ae015b66f12cc4658861b3338c5883d67e91ac3fa1b32388649d723694b37a9fc460c8e371b4e927aa92ebd5f086ec0e09437eedcaa6714f5888d579cec3486e09722b2f4a823bb7012ac2bceda73a53dc2a75e34692b4e31132edc99ebc911ebc802c3021c063be0e3030c2eb828678315c718a292e95317d2c948031e383c2890010a508018185c705130606315e7dbff0fbbbe55710c5124e98634f9f868858d54e4a54f4af23bb525c9a2810d5418e29a3aedfaed1a6c9ce22842045d4b391124eea638a4d1762b7fa7a2c6ebac14c7f9eccc0a25e6c3dd2ed820c5498345b0e41775c498cc0b364671f4753b4bef1a13f1a4051ba238e9980d672954a698356769b0118a934a9b1be92b42f8a80d20c5051ba0385b87b8209f977a2946f613871595eb2e7246eef49e389c597aedabeec923efc46127e8ba8d1952f8d93971103731b927739a487d13a75c41ab2949ef6b9f6be22017416c9253cac45163fcc96a4853114d983846aa24a9b27c0899842e710cf9d52f124e260b6a4b1c6753ab9b26a1aa5d57e21884ee2f39966293941207d197db2666bbc4244fe214637e748e882453c99238eba40a9627d7481caf432f987cb9739142e22479625525253ee27859c3a8f8ddf7529e23ce7216a384ac5591c6d28863d48d6946244b1d9a31e29417fe52a4cc6aa359c4c964cae4a3a7336b2953361471ba8b9944b26c1ba2ec89388754d38d97b92ee811719e342a2b2615db76a6431c73093155639de9520bb06188d3c9cd2d6b95102fa58538edad46548bf93f94fce32347246c10e264992a86da4a4a31de208ed92a553c79a733b620ce1fdf966ad9a1392e813826bd97c2850ce2ec36401ce44d261925cdfb87fe54cc7eb1bef5e40fd8f0c3215be4ca5496f2880bb1d18783b27017a9adbb2a592e30051b7c38bd6a66907cf57129dfc3393b838ea02b6f0cb9d6c3d97d72dc7ce82ca9ad8d3c9c9296ec88d3e3e110549ca447ad4a3ad1dfe1105265a588c1dabb94763885ecce538b903b64f7f1b15f70b1430b538763947ce2b75297c9a8d0e120c9bc237c9cb6a462733885bcf8a2d42e4b50971c4e15ae525f3b53683ae1f02017f0c0e1412ce081c38354c0038707a180070e0f8285070e0fd2061b713864d31aefaa3264f154475679840d389c42458aad15338a4cca230b08809105236cbce1e8a777a5b6520ea171371c3ddf6d5e7e547e953cb2c0c2a33661a30da72dd1a1eb254cdd330f1c1fc4061b4e627a4e092d3231a6181d59a62c10c6c787295665630d240436d470daad5049464a1b4729f02e48c05d3005101036d270eadebf11a92d77efa2e1acd154df4893cf4bdf2ec1c6190e334a0931329b9a9594126c98e1a04c5b4fad64c8f0bf0ca7319d9676467bd48299051b6438c80849d464fdd1944169051b633828593771e62d04eda718cea55e73fd8c8c051b61386590b52125b91a646ac170cc7d298a4ecba825ce2f1cab435a6e5223e2df67c30bc7aebf88722a5a791b5d38056d7ab6f7434bf07688917290a00b2db46d70e138319f10a92cdd1146176c396c6ce194b133ca09d1b231c8011b5a38a620255e3ac93d5a2610b09185c326a16c53f55d485a25d8c0c2e9278d85281321bb193024d8b8c229e96dcbb69bf3159d810d2b1ca42495d49b347993b220d8a8c271fe84ba18c1e47dbdda0dd8a0c2792467efc7ff4dc8eec7c7d998c2717ec44b7eb608b7902e2ce081c30387070e8f24010f1c1e1e5960e181061b5238bb4499103d764fa58c17df8516260a27e125aaa57f434d1e5940200b2c3c3e3e6c4021a1238ecc88bf69d2915528e081c3038729b8369e80ea7b2fe15ee6161b4e388ce56ada525a7469b009a7314b3b9752839a6e9870ce0829ac8a10cf90fd259c7ce3424c9717fd102ae110d304556ae35f5a948463ea35734d91eedd234838684b505e59e2b78e7e846336159164495989958c70b0e4995b6523a7945b84b37689ce9df635a12b110e4a8747bcb05d937708a72a2bebaa6d0d614b219cc2881149ce3a2d641704e395d0b1e02a4998beb0c006108e9d2de9faf23b7c648860e307c71492d75dc44a5121ef8353f2f7ca1541678bc8f6e024d4afeb428594142b0f8e93543755678cacdeec80206d7ccf630d820d1d9cce92327da3ca665d4e28d8c8c1c1bc62ec52229d5d9f70704c3294d8ccaed191c5458e31c0088324c1c60db6ca501bcbac31ea6fc3065fe86a6e67c808b9b751036f5c2e6967ce11abdba0819a5d441032462b2bddc60c9041a5982d0831e24464430675e5de4a59b162c8460c3c3f2579bef499ff6c03066db6894f53679ae4fc0abfd56c6e2c766357a4d25bf6a5d0a054b615879aa067e656ba654597533d5fee84beaaab40bf7f672c7de2dda72aae1fc97293b26595525130d526fcae3e44852935a824f69d82bbbeddf0fbba9ac2fa12af15567d624a51309d23634dc8962686148ce5aca8b68cc294cdddfe52feafa844d145b70d0ac52954664bcb9a8ab9078a53f8a4cd5fe216be2c1d599fb0b209691b7c3542fc3d718f58cb269d29a8689d38a8cc5ba2d237b34dc4893c4c868b6da2d473556249f3a97c320894a186268e5f5f23bf7534274b7464a10a98021e383cbab080070e8fd4851613f0c0e1e181c3630c9f80070e0f1c1e1e5960e18143f50be62247f292428d4c9cc4f8e48b17368edc6a47560004394a50031367b994c54e7a5d6f7c7564e5f0526088b1a38b631aa87189932aa9416afc95903d0ba30b2db8b0c4a94246cf2e44ca4f534756176088d1cb811a954844ad9f69eb0c9712ab96c9fcbe4965ab142fd498c4d123c44e7dbffdb0938eac2d5a0b24467230b20b097c7c7c7c58116a48a2d0ad1a3ebce374a8230bab8b0d9072418d489cf25443ebb825656d7e7c6ce5d8e20b2eb42875c5831a9038fd8e9ed409faed5de770146ce1c51848c7401ea8f18883f8511abad5922ef28e38c546df2c4b2a8587d988938c88970df9fb4b4246bc265f2fc184a62ce2ac7311536f7f2971eaaa52a54a066a28e210933ea1191afa4a68be502311e7edcbcd142e69be248488836aa6eddaff56fe10471161732529dee2420d439c63bd54b8c93c32ed99851a8538296fef8f993c218e5bd613bddbf2574c07710e95b349c67882307709a14b2e86f63a10e751e2b593745c9b48c2f01d3bca8e1dac45188602b3420d401ccacc2e69abd10de7f02295fde1a023be8988ee9373998eac1d5e7c7152136af8a14c1f22668467d0c1f8228717e9ca6050a30fe7dcde206272fb9e94443cd4e0c34186deb59079ade345d6861a7b38e9b61aed511dd1523c4769058ce7a8e48ee5c3a1381c0a84c26040000c08a52ec70193140000000c1a8f4662c1884452b5f90114800439402c522e261c241a12161016100a84c1401814080903613018140a05828070404891c7b4ed00921cc40004733e0b0454b73349261f9cc70626f3619bf135f888ad197eb7e924d50e0ccf438768dac33f0cb5100e857045b408afac75a02c0bccf0877d85d039592663da9aae9faa0281e226b057e9d588ea51ee92fe884fef050534663389d24455f41f54d9a8b989a5cacae962e0dbd8cbc2540fc93a397dbd2b31a7c5ee7ff3725f227288a3ee4e39e1ee7cdf26926ad6104e0beaa13d9bdf2362df127c18256f1a448d0583a136c08231086812b21e9fb317c0d6e5984db645a1515bd2afc570b66c1a7542767dcaa9d79d940a533a0fc3f9a39a96a8e8aa5f035be11a3770d2ca4d680c2a9bba0c3f12964b34c3790616f064082e198eb5ab41aca0e20cccc2c1c67ef0c05c1166510494888d107fe389570ece7036761414bd7cee21db2dd667a9ba4b1fea760b2d74b253e968c9b9bf70ad86b044919dbbe7405189ff70c91221a02ec838b2984d33c82bb80218b8fe3ded0d2bb537581b070823f2f1d34027d670a94f22ff26ce3e3da04f383ae5e92cec91a53400ac7cfe01976d8ec8ddadc3bba69be906e961eb158238d0f124e457046f091ae86073da99e7fed7606bd58c3c538e348af7281b9279e5aefd08d1345b13e7de41197ba50fda0003d92392519885a47bcf398f0d4e007438e118a692f9cbe0d80761081849b33049b61298622b3029a2c5cec6f2f6043aa41c2a851af8ba74ba1859fd272c74495b2ced5e8d2e23fa016dd83a8c692ddab4cf3d042722f8ec8b388028916ad698811e343feacb3844a0b9e99a3ba73183e1c0a3f9ac201510867f527258c13b2b3cd36ed0353146b213992148c877295008122317468503734a2340f0bbf934f19b76af05e7e3eb17211fd0abfa0317682de00c380ec9fac77c0fdca2734dfe23ec97d42957d4e8d1c6ec5a9859b9eaeb776778828fd853218a7703e92bf6cce5136b9f59ba3b56e1235657da9a32e7c046376751e334a1ed5f24c3f4051217c50cfaec14a089de8ad68476460d6aeb9e407a496fc590e505d53d79c5afc030b9a40ba4e4d5e3a0a1bacd25cd66d8207a6d9c72810e493a21f16089c57970947d19e406054e901a8a31abe22ffa62d1fb8b0b8ffcd33b14e0fdbaa0296c77c62fee089bf7f3ee8c89bbeaf0e61fe41e76a132495572fa58d3817e9e41ccb0a2b6808465301da2037b35d5564c10033cf4f8fde0a8036f7bc1f61af9848e2e30aaa8e700dd7e66a1978a3cfb1147076d6213443a024144b55a45bd8cbec151cb228d51f99b011903d5c37b7f9f447d7f7540ce8577763e47d624fb508eca9c7ee5377eddce4996684f9f8dcc901a797683ece85a18352fe3dd3bab261dab3fb80c4f6f9514a832eac27fe3564d21758f1260b087961975d69187cd832e05350426989c8de8b846528d517e1c1fb4ccb29e4a256c38154c348980b30a26a18f357acccf5e7b532cd5f0fa8145753fdc578b43a2acbc2e14279d6bb4de229a14ffb34ea27d40005a19eb2ea27b4d773a5d4608ddc9d5cedfa5025df87515e8daa88bdbae76bcbf751549bbe71eceaeba98ee9fd4eda5c13b7f0168dd2edd6175cbb14bcabc75989ab7ae845104e2a704bc98021254bc849ec506d99014343c11b08114c7fc7785411ff06f7c7341682cfe53fa26a33c7f362b0cbfb9f29270aab16ac0e2c23e814da4177e8a2e738bdf2d4cf201f8f37ed56b9c8c7143b6c9c22006d6aea56cab5df53763f66863416e17eff9e3d398803e88fc9cc239dbb322460fdb18a5c68cafb4f830e7b5e12445aec5ab2524a425b111c530431786b8082a8507e0faddddd12d92bbc400f9606a94504618357de9777861f5288272a4003b85f57dd49dce58e138ae048e35d2897aa342b2a9e00aa4b0b4a278440a91c77896493440db32e67829edd9846b8df08c6510e1f1aeeeca9875b89b81ff5158095e3601ff9e25dbb85762868af206d9fb02467ffe6daa0c875142c679ff051b3d1e93e96a51fe111847041b023308209ffc4c21a912d6f57dab1fc7ec1b909c87c9c648db7b4614c8d95e1822ee2cc6615b3e462d6a1e219e69143d9685d587082608e8d79555df0933a00c1283ecef6d344ce36b5248fb8c72a0abcff89e1170e46660d54d1974d3fa2bb3b94f66d8e19eeae2a82aa185df4e02e2a594a2b0436bdfe0e68a37fdee1b347cf463bd5ba26d7aec67ec592fcb2ab466b971ca01a459c7ecd5926bb9c03eee475924a506f650da2f4a29fba88a50c4c89358ca4a8181f0c3a1e0f84a1c3770e62bee8f62ca1b840f241d0356140cbca2b5202b106eac2f3cdf55f8e4066bd2aa4e2d1eb219403d99b3fc8b57e482ef4f9433c3ff449e9976270385d1a3b0929c817febb2aff821bcb403fcc97d60f72fb99122160d02394b0a9798389c621979ba2dc3a1592bd350b6770c1d0c201a127db552b074fc5f489fb8b4706deb0012ac3dc50dd0918d1888612f43e0ecfba8caa8b1d94c30cc64200579785af8799efe23968e0fd9ae53aaaf198677ffebb847f90248903839203581c8ed332a50250cd1cea7e34c8c8c116d29bba5a1f492bb63b812d86ee26acdc590886053b4efcea17d7ac4b007c5cb0eda953e04e5e524ec516cc1fd399c3aa4f7843689b0eb4982c405ac2397ceede1452c7d7d1aa128f539a082b18acae85d9f68f38fb7ac81857fc35eedaaf4491b11341e70f243c74eb97341c48d964155c8391e7005cedcc56e08683cda74a0f91c24a028a0334096afbc340261ad2a9615d60f3f0677bde19317a83caa291cb26b818d7ed8d1937759f6f137f3499c9a3cff5c90f72dcf3eb73bace4075fbed145bfbaa480abe99d077fc712af50f6ad5ddb754dd9f25b540d4fb711e0e931cc500f30f40c035b1f84bb2968f40ab30de80bfa1183890c140c6a7d802b82d9186cd66b9011684c18e69c7b9fdf5b706ea39e130b5a214f767144e630238db862021405680d80d40118a5a45c0b705a182de4f0e8c5e87d5526e0983d0adc84376049690850170a026f427011b17df08d5ad49486f2d85f952073162fb98269829b979ecca2cbb3dd5d70ec3ceac82f0b4bb79cb3b31fea78b1a6fd99929a4cf4e7cd6c746e96114b9b2335e010a0685b63bf7c4beb6f52c28157a257486ef92a3c6869bbbd83037d3fc8fb0ed890dcf237ffc9eba49e8bf60eedd3fea9687945a68e00aa03084c489b4e962a8a22085d77a47e03c1141eef10269102288c78e364fd2b466ee16a6877be45f48d1aa0f8cdaa63d0991b539d4a0cf15065ef1396b5d328ac1f0f85fed6690a12f88069d3c54f7bb210203ba6a7ad63e8fd0b456c7d24db27391410c2f1f76ba13570ca1eccea1019476ef8404a8f7070d02907776092c3de6115725e34a49eb5160a4cf35d9b818b8b4ab4a19585257399997038fb8b0a6f1e820b374f2d17ca69bdb30395b8b8fe27fb69768450023be3d8f9cac662880ef84632d17083fe1f7284de6559fa37875fbab937d0cddf24f4c011aaf8f6c5c0c6ab2661c3b05692fe7978f2c2a7a7680d7bb8bfcb4057322d045d13f541d0fd93a3a17223c804be66b36e636fe31af2d7905d29708fb68a6b95072091388abede100c947ed6d33046a24f5eb962552588bfa76b459ebaffee7f73005ab084390b1b71efd258de8f5a9f714582acd550671e1629b92cafb4bc35ab212b7878bef00c94c786ecf5228da823899e518db8a8d3723d6693cd7316b41501c68073cd55f95e772d1caa06108ca5981c983dadc3362eb0ce2c768a5cf5d1776b3c60ae12b07a76e8a9bad72d7ed73ec539a390e3ba57ea5b660b4b256dbcd2da3bee0cc4d98a4b58e22c3e364e8acfb8ba754f6d0c91b7bae16e887d0869cae09d59a0d0da06eeb1df6dad44483d8363a5e75b5d5168e50ae13352499e6f870ba70001afa8474e7e120e73888321789fa40176c352dc2e8474634ad67b717df5c7a85bcaf9db33a488e9b315940a01132e6020eb30aeffe036411a50618a1add348b8a89e7c9a6437e94cc3eab097387149ebb292f2d82b955da0659ca721bb121d953e2f4144a5c33919326fa1a556f75906b6c4802fbacd0cd563d4646b3016e4323dc40cc04a4b5a707ea607b4b146484468110825b20704237c5e5d78aa9aff8af1249afe15022e1407d6a8642e5a97a65d7fe4d371de715b0c731b16589952b156676b6cf193cbb35999b89d99726296dc6bce0755156f9e548c06c3cdc790d2d81b941970332facd824b9e8bc040ee0125396feed483f2c8910722d3b119f246e6ad28ae14c76d29e79360d106360540a93cdb01ddd7f2f63fa37e4da1a06799869f1629902e6660c8ef270eb0ee97c02d93940f9fd8b43109ac2e7e9f4517268e7fccf16250742defe8025930465d98a0920e74e68837c7e88f1f6e5cf591d7db6e2746cd79ffabcedaae435bf4221cad9ec23aeb61283f7ce9c31a45c45c6f12ac350d0b830e789c5755392903b3e16da9f2eb00692cf26e56f6b595f92db2f7270cfaab832cbebb61c59d339785f1225451cc51820d3b6861ace6b6e79db7695731221567203cd4185404a136ac4c275daa83449efe194438c83a9b87adca4f5a7389bd4171fe93b80e75fce898a186feef4c60f01bcaade036b838971487ee87d9c1daf216d2216ecf954eb93732262fdc3f28b2615fccd4c52df8574d4de85985d8e30ebfd5094d5c111cc085285fe764643ad9b50f71c636fa2faff2d12fd696ee1826afa2dfd366df6522a255a13d72b6764464a0c00cd6d04768340942d000a91d38807717891765e0f37d1e827ff85c4ccf78023fafde35a35544d5b8dc8279e2660740c79d17a04cb25c5e39720ed864175489d16dbd84f82a1313acd2022189c35ee11aa61cc4e467aee58216242f4be432045516af0b24ed7a32a1f7d52ebd49883763f3076607868a6cf2d20280742390a148b532ea782e1953df41355e40abc3bec1585bcdcec4eda76d0050a66cfcb1141721ee6a2154f588c2842d80aa577ab2109ab33891710534ab484034027213d2f90052052abf65750182ac75e49efb6d8bcf260928395c1d3bcb694312c7999c24f363dafa976a9140dd971b40e1a9f21429ac7e92c52debd0e878948e39029cad442b8f98b030047d0f21e0cf57d43b72443ee51cdb618791202623f80ebde5148d4d8a2a98ea4dc4ed0132fc9b3422f21a46e467b7e433d21a4c68ce7eb2f684a6ab01e1ce511d481cd36f7c1c613b34d4098f07b7e3723aec07fd740b904d7a0dc8c211eada603e872bd52be92c5838ec1fa41420309a68198f09d022e35f053e4871c8c3f1dd419c95a12fa0b935b929ca210e26ad3e8efa9318121babd2dcba84c9f0cfc1a169085fad8c8d8e6dd7e6bb4de3028c29d0ff68f3932d778646f8ac74a11e5afd77cb5bfa382f694b32b1f8882312666c60849b96bffd7533a19c45432455857c5a21bad69aeb662022594ae9f04c910e2c386cd8328ca52a82a3b189c7a1d458a1e968b6caa3d8340da4226c4506d18478124a5dcb54ae49603d1f323c3a00770bca0e870d52f1c41c09a3de9d27706c233da129818a4bbc2097ff96dae5727dd6b70e1c0881dc19447e309d49eacd63e1612f05dc217d1be23d1ef7eafa480ea1601f58442c6b16995eca420997c7687793df324860a98527bb3ef862350ae89c739b6df2634cc55a28c326156fb17ccd03430aeededa322ea45397584deaeb3087b0937727894501593284b5e979952bbf273077ea7ebf41f6a18237788886ec72ce6e88053b95f91fa712f39e88df533d773f7dfeb79f744f78c1d70250979d3ef9540880c9b889ef9e0ba58844578ee4929aa555454d716c488ab5215e797da0c4c5cb928a268025ae274845e8ab88ada1ff42789622e93bd0532307b1bd9082d1ba654f5028d1c62c7930eec50d0ad379a61f740276460639d140fcc4719196bf9e900426c816f82b6582fb6ca42a5fef535373d112742303ab9c9dbf6be5e01b46cbef9130248b212a39d60dea502636083ed3a871e5f8259f7f4e21f04721249c67cb9678e9600c5373026b02543b21f0ea84dd146a5ae8d7a3369d6e3dfc5426b94a7f417ba6a13760525fe7e26c8118f6751c727f5554d0229c8db27cd6a7c3773bb183799a78e1ffd80a0af6e398d18413712f5315b5ea590ae4c3e9471038940a39700ae5807bbbf0216fbb89b51c3824f6937ebd21fa8f3541a32a24e719de693f7b428792a709b4f0ff8f5448ab9cc2cc4c6c2c790760c521138d7a1d856227b86ea32fb74cdc7aabb329880b71902227d4a0f0901fbe4124aa6a4090e5bac9cd39a3795b46ded5a4d058a69a3d0a05e429aa97e10c3ed202fd4eae09dac9a9fd17170362641b13ec1e59a133e12703417903d4a03f44d41daf82fc580c6bb8694b4418520e33bd886674a7bebcaa2e7db1dc52d4761feaf716c3876b6d023321c12d55707886d8c49cd5c9b033903489d2e79628fda5e5ca8937c4c9a22ec295ce948d61908a41f84e28af8169287f15fb115eb1d6fc09ed112071bb92ac1987d440fa3b6070f880c3871b946ce782488c9dea354e46c28e92ece03a256ba84989698a032763ce31c358aabc3b335bf6a7eda13e837ff4ab2f6bb10c7233c057b162ff23ac2a6b910b1285f059e2393a00f882eaf884b9cbfb3bd672c24512b341b67b2b0a8e583023693112c68b47ce76699c0fe314693539f8cb93011571e78afac54c5aee705cb2a6ff1e62bbf313639d3497344be52d665f4f19ffe8a0eb86ab09833d09d202bf3ce5f5a9c7728628f1af024985e551066673ad3781cdaa50724e29590083efe7f263834824be5dbe254717c668e104962d4baf4c136bb4e195cdee14c2155ea9126682771e996385eddc514d7f8b436a69dee3aa4054df54aa8fd260e10022c67676d838a49fb4405706c7f61e7d9db3a865fbdac08c8ca098ae9fd8c82473e1af83c581e724bacb434b5dd2d1d6ef759b450404ac01e47439f302a3547c1248526ad36aa29741ab7391e701946e5e434c7500b187a40273b4f340ca9352f212784f34cb68c6d5a2c4a86585361ef830ab42c9c0ad6fa978909e8e52a885b6a9026ed69bba4c4503434529bd3312291f8902267a13b7b3fc497275fcefb748e0186ad8a04836d4e9632982776c9dba74650a56ace1f453b1603622c8943949d96678467603edec1490e0274644704120862252990274529393c1d6c0c052d0a39892131359bcc7cbbbecd89a374e0c52c65adf0df639647383ddd7ec035413eb1b3b4529a595bed10a6330fd047d646a4063a2fe8325034999034537165c48ced09157bdc7fe80debd7405fae4b6a60950517348395c6a8a65431ecb3882b04ebd0536de8fa2116c217bfc2a2a91145ff412e28e55d891848fbc81ab4499c606ffa7205c3b1927340a80caef0b4a2a01f513e2f4d541888876445f1d619106524c6c8d560508d34903422a7bacd2d91970c60a6baa10d0f6b246a8600b8c1780e7c6a944ba3327d3585eef3b2e021e6701898466992b04550e236f65d0240802499b2d9e067b5a8df281df8b9d5969c8655df8d9a4629e1319fb1160825de3b3ab8ae3b1ade063140a706c19882a6c3d3fb3e6b2c4b122b75e788c4005c9f2a232fb148f46c7c0269d3cb27849f12beec45655ea5dfcf2577757ba30197278eab55ade364b20bb3624470eea31107984c3443431174508c43a0a621f71c8892dca62bcc02559e52727e872af5e0d2767edec99d57b65580a7ecd25827049c74e547d4596a3b11bc9cb3841df32df633ae5a0977062d8441241b1858a184613d1310c9d9844428193dc5215b48511744206b9988f5ad8211771b0835a984117f1b1597cebb527948d5a07cdd923cbd9384590f6bb612537b4a854067ebfd315205997566897986bf3e4a40437e7d8d51dccf3b199b4b9dcc697e6cf14b0860062d66aed4ca3723184243af151925caed4d56b8241f14fe85c0ef9b8ad43b2d4279be0a9877c8a544ec775d1cee3751ceab85cd364a88f2c415337b9288ebcc57c91cfdbf51dc6f8d42729ea2143d0d44f7e70ed00fe87e6decd333ae65bbe62c777b6f9ed78ae07b4af877ef32fc0290feb3a9fc7b556b297029743b1c54976fa2cf24965f8b0ec7e48f90053e428a4c6aed583e88af4f4a19e3a0884ac5378f927545d8b0288401dea1391f4e98f44ca122f9968937c2a123ef9d19158ea251269d29f44ca273e32d296bc54224cf2d391f8d44744d2d22f4994497c32d23ef9a84858f2a51331a97d273f66ae0eb1f5d42f6f8988845af2b0c1b05e823f0f9b42976c3668e82fff0d23bb8ee524dfb7f3593c7221174e4ee76b3b78e7f33e86937d8edbe4e1ff745c9471a1074d4bbfeb34c450f191d6d5b54198c1e40c3d40d36ce10012be8f722151686263c967c89f8aa17b60d683a442c37b6e64b092db84eac5b1ac25319fb6fb3b7e6b0ea9d8d4f0f9c1fca44520a45c51477cc4d76c9820670c23da3e974b7cc3ce77e90fc45c3a15167dc39678909704d4445bab73c4c392c9520eb8872b7b4db7ebc02ef16098529e9c9a71668ea8296c3be65b76e18efed67a7aea129789a353a2900cc5e5b42a11fbd05d5ac9978288243e05925e753fb8a1d44dc294221f81284f3e144694b8299854145f151041bd8d6eb2fbd4b05e9224e6eb7676961f3d909459cef3951ddff1b78a746b4aef749898a9c6d26cc1577c324b1f8b07a18583302f40a9c6dbdfa88c51865d8042b3358b6c22ce8b5819919a11b9f0efa80c5f3989395d4fcedc48f4419bc516a0f5fac832fd13e481eb4c929279b969e2a3d695fe962531ea816d2fba8f7e1e1d031e2aff37ff25ffe2a1524f76965969985bad0cede7c4cc9528d3503667e5feda233f0c7de02db4a3ce35259057a4d4d431bbee75020400431489361469117520b6afbf91d5a4c0a7caccea6999f8546605fd55d420e91e891222b513ea74f901a939bb1f0e103e575bd8c3827a77815b31d89b0be9e582db16e8dd85f48ac15b2cd49305d2c3c2c8b756587fe6a668b949b7b157ff7f809cfd13a90b0584859842a0697891bcdc77cddced7c82ee31b2990827986e8e1463f7a8480f05226a1b155a8011a79c2f20633d27601ff38a070b2fd9b2f783dc95973b9719c7499b12c4690b17fea81330d7a8b63498dde3f319e000f0634210a105d6930a07001e74c8e05e5d95281ebe3f08fa27b700e782ae030f746e884927588a7330df8a8c20f93996f48b3339f9cea49569343d692ba0107d2ae5bec22d2f70d2f5898c45e5e31ef3b6681c68cd1fa6c1818bc37e32ea6dfc50718c50f2b67c2e4d026319b75438d50ac73e7ca4e49b2c1e527f9884ab13ce1b44a08670012f7dc26a6814f950b3308100149a4bc63c013ef3f89da82551f9edc86be94ac4d5ce169c5b5b8101bfded5113c56f4a004ad2b23c54ab55e6611aa14baf8d477cd801483aac9797e7fb0953e03b52dccbeb7c66e970cf64d13c55b1d25350ecdfaa3b264c1a0394470379a7a07b6006844c53c014d159717e2b3bb85363778cfd9c1e8d87a2d7df0de1952b3b304a605aa0978bcaa974e0a7cf2a1903204245a4050566163bd6377ec2c6ee46d83e9230d8f3640755e3ee3844ac50b3300e451fe7903796aecf7fc4f5e4fd6d8d8fbb1915c4cde49901243ccdc9913218aa14dbfb5d19bbeb0062ca1ffcf64aaf498a808276a9c7d225a79664ae0b4f70371f68b232cf442a36fbf4000630a35a5a6a289dc47a4c4f4f170809acfec1c321f7fdfdb818d03e1407727ddf762e7f8101fabacd9b0d2ff127572ff6f578c641c4d85b9f6110e514da03c7d56305be94434987528e65394b0acd8dbdec624979283436cb0fb5c8139d6ee8ab0ba3138f6c429a032256fdf5050f826c47d8c16e1814173f71e42d573398a523d697186edd4da1c72308a81f31325116dab4a1ca018284e3bc95c3691405b214683716376b3064324430388716eae863a3ed7655d6c84549d4716dc5504a8ff57d7a857215e5c0ac86c3993724e4af10ecb082fc9199042af8dda614597974d4e7fac89efda913e03428f1793394f2b72a2b60a63227125ff5cf150757127271d8049c9d3d782a56a44d430c74905b67a6197b3001e704d8097452ac829c354eb8e2db22d4bb212147ab0cdcdcba760cffa44107350d85b6d7f48911af76b7b9e9a726c836311ad085a1b550338ad1d835e1b83b413db2109e228518dc1ef61c11788ca863a08cae8ba47e52c665279be1d34b94a71de0941ef31b7e692e188f3e48db3010a9575e6572902aa603a31d57cc1078d3203e63a2d9fc29008d0daba614bc7a3a167b6d2cd40b4532ea14a55934112280e8c2ff91b8db809c4008d00bb09b4f4b4a86da0f3e7ff21b05e96e13ccf9291a7140e6b7d16b65d5540231ba498079b7d9dbf60e6ccb0429492b75d4df02bb7100a4c31259c2ac359a2e9ec4f4e95b3a29ec603ae4bcdf43298d9f95d6a1c303e72a4a341891056386be021ad689c691aac3debd062e243609ba87b982bd433bb1ee5eae2f0ee88626e72ea34c9442f97873f6dad393089b2ac69d37f7563060a19ad8936c2c94be1567336d0d786a30a0ba9a589001172ba0865a57150cadd1b8df1a82fc841d5d5e2608c8f9ef70cb3793f01896a776720bf7e6b8cee60f6046eaefdafba640687d3e5be7349065b447f1bcc13e948d4fed4fa9a68814f4e70409331a99b5638476d3353f2ad0738f151a4e265543d02ff3baaed3878f49858ff9819d7b274d26b5b8e2b06a90dbe6122a45839e841632bcf1e3e216bc1507e87b86e4fae08197a4da3d90839032a13fee3e0f1534680690bf9956fb50ceed10cfba8c5a89edcc319f7a5ad82dd8d9299ab15f204191e7f38820097dd5f727e9f6986021e16e2b7a8bc64056be59bba07dfdf4776074d1ea28ef2e8197b597deb303c8d296f53485c0ec2c81efa6974cf9d2a7334f5cb2514b9da7eac81213138c41657c55235bc4cb8360ce9d81386d4f7afcfef3269803cf7cbe3a0464023d294a3b05781ce8eb5be256220759fe30c78df7943163f3b093a92a56dacf6b8244ca0a512850cca6c5b60b77d503ac69de1d0dc11c7f7d01e65f70e08ea77f6d573c4c62d529b93d2114ba417208f1c8105d5395294bf8dc336f72e3b3d6fa1c0dad58ea333d27a6fca0b9ada43b5fc33e53780cb1dfdfe77b30420d84b6ba0fb18c972a125e67d8f72b18aa2f1a4d6203414b36e6f4dd0b98432717de0b2bbf140e9757140391fb24acc5dd68389f6e2e67fbe6decff4e3a2c30a9cb26e824dd5851df7ceeefcb1bddee3ef7bf43ee2943820c2cd568afc5b9d156151ac714f19803d8ffd3f291c9af2aeafda2cf3758eb534e0407597d420e484649aed01f393634488b9a23c3ae12d59a80929fea0229221b7a3a178f59b733be21db37467b04023bffb2785ce4280bd4754b755a089f2c6a0f9fc876d825185f09887897582606498700b50284391f7918f649506f3d85043bf3b941ad77628c53e49837924ec020198690b90b255cddc31ab0d27377543a01df04fdf1b9298529abc1847bd39d91dbe43ea5ff6bb0d902721c14f34f6f61678cd3a6cb57c51b442ee67b6d92355e3ceb21372397a128494a4d4097ff60a460571d0f770c15dc940c3bfb8a7d3855c9d3a2ee3073a30cc3c1538efbcdaf19a0eb1c391470367d4cd1917f0b11c9504c2b9ac9e7e0d0ecf2e21af4c72ff4a7e28a035a1b162d110484a60ba6fa55022925f5e17810e2e9d33a33d6d97ac533e428104e82e424f7373bc47e08befd1a1c3e830b77071cb90c6b63e5bcc49ecec4856a5fecfc2383aa1386fcc954a1c65b5f9ff7871568b43351cd09c2ecd4955dbe9d6b3afeb19c17caf97db1debeef7aa05d7d9f3cc305815741f558849803cd9db2b1243cc58521f1b38a6ad048e38563fb5925c31c084be372a3c820c82d20a98de8072f3d0064fcfa35e27327056c26e905feb378a6afab4aa9ffb8f2b939587afb20d6abb2f13635b8a2aaebcd8c60936013fedd2a9e0e4b07857f73880e38a15a130c8efd74460fae89c68549f2d3b6f6d2f0869a888121b20ab2b2258cf251d63509409fb27b77ef1f44aa078f495389634651c5130267ad92896325b0c23f1aef4202f1673140af85ce9a2f3ea2eea3790351b0a1241563b68146ba3d31e68867a9078b34d81c113205ff40ca4b6ffd9e51613d35f937812c4ed96742ea451a73ae80223eb6c86544db98c054527ae945d842e276d3ef106c0a363a380558eee74766742424e80125b00ccc4f1f9236112db9878ec927b5520e3dc5836d41d4f5819f301f751fb26a441bebef75aa024a2e0570915c8e3377ac12b3e8d266fba095abd97efad890a65bcbca5d0b4195a39a6d47e5290a2bd14fae4d4dc594c20523ffc2ad9da9aa3a6af183ebd0eac86c5d61a4e28e910de6de889d2c4002de1f44760a3560b25472ff000000000000000000c098550908db91dcb9a4739a8d14f1919b644544b2129fae139fce4c7cca6dd6dad676663aa004590a9d0a2a0a45dd92fc96debb2967436b01171040b102c44803dfe7d9a2af2c99e51f0dfc75b4c871349ad237e50cbc7a6fb28eaeafa3909a81ddf491878cb53b3ac932b07713daccae3cad7b64e03b0e296d8e9d755dad186360da3ec85164e6206a1f6260b72686e598bb95cf1306a6eb62f0949193862ca2e9210618d83c756d7e1795fb02977d69820589d821732fb06f6ba962593d5fb60bec777a9cc3ab5c29a9ed5eb40003748588c105be838eaa91938500528388b1054e3ced35fe5f9aea94060d1a1b1040020420010c0820012608038c148ce01ce07f01e7a91431b4c0ee871369a38536b44cf025780a6cc5c802173fa2dfe370a2aa1790800468d0f8046c0c2c30d13d2c64ff47b1efb80297d3a3b8b15dfc27e7adc07fa61c7d7b557cec53057ee26d6abd9c634eb550818f21d55e88941051c32974661fa4c758a91852e0ebef625689ef2220461498789da27e3a370614d8bc15452d5e8eed53e609dc86fe9821538e93876927b01d9997a3ce568c26f02142b010d1f739cae2430c26b02f15562aaab9eb712422c61298762d9f8e6521714b44c450029be390fd61ce5168d02079881849607d835fb4e7ed6036829186184860d36d33c5d1207561e318621c81b589bd2a3908ed39be0b80c0620c2370aa9931c7ad1fa7ca1f45e034a49c2ce66c51927d1081adfb28b577f71098182a74d8d64154971c2130de59a7d27e4751e209023f759b3af2a02f153a40604d3f4a15696259f2f801bfe691625d5d7689ba0f38edcb0f4477fb52e73de03c2b25bc345d04cd79c0c6e9c89532849c36f31d709b2f32b4a474c087ee38e886494c39f21c30a943e3d548d6f270c0d9476978c8df297b7303ee3f3289d29ebcd96103c63bdee6791c3c90b4067ceeb110621e3b060db89fec4bfde817bf6316ac7f1052aba770cd1e59f01d52f2a4c468eb6162c1db65a81c850a2cf8beec717f472c33c92b98f871681eca337789eb0a366b69368f3a94f4b86d051b24d364b1dab3ee96157c4ae1513234a4f4a15d051ffd810739db9dee5d54c1e5fa0d8f1dd9e3c0cd5470a9eb9aabc3d78e2c890aeee30f5a43f0d368a1bf3c68710a2ecd5a627b10899d4c53701f8ee730737347566629d85ab5b82dc94b744c52f01337589c1073146c8c5c196226cd21ffa2606dd2d444cd39db3fa160335a73cef5bce5610828f8341dbf3aaee4138ce4d9ab74ab8dc7414ff03ff13b37c76027d8aaac4a519e3ae538e4041326162c0739ccdcc34d709ff6355dd29cd4116a82b1158f3c7b7fd676e60b13fcc95a2d32c147d5779dd379904d83093655ea4aa92c871acd5d82ef0d7a9aaa6fbdedb304972d2a244bf534d556828d8ed6eae3d428c125cd4154f6d493e5d924d8cc2acfa1079924184d3daebda5316ec822c19b2653d59c13e56120c16b8e1a7354ddfa633e82bd14216bcc2993ba588e603cea38cac871d5a4a91ac127ebd6cd5a39444f64005a30826f11f7705f624c2d99168b6043d7f486abc771e4a1083eb097fedddc39a593083eeb765c973922f878a3c34355888a9743b096b921446a86b0bef9a08521f8d16c17e2a4e0ba16820dd5d641fe38c88b9410fc74cede71ec282c8883e0cba67273146a4422b2a155c346182258415609fa8a025a0882510b99f73f2ea1a5b6a196821a5e7c71b523d022109ce62b5dd18ef9d385e42ab40004671ff137c6bfc2a0c51fb4f0031fba44e28f78d47b312d28c1094e70800478418b3e30d162de8e8e1b513af482027f020a0820010ea041c30422d0820facd707fada51bd252babbea0c51e989092b87994e398fca25ae8813d0f9d2ba956d5fe4f286891073eaa8c69ec9356672f9ba0051ef8b048bd1ce5f289883b418b3b70297260b9f55ef22294062decc044ede7cb7612af72e4d581c91b7322840ec96306aba0051df8287da8f9de37a4b89a39b035e6b1e396440efc68748fc31d0f3df78b031f76a4c1c3e4da718756e0c0e7574f3988ba79039f63a6fcb4141d112d4bd0c20d6c3c0bb153c658a91d471bf8386374bc2152df000d1aa6822168c106b62c9276dad04a418b35b07ba649f3e78ff52daa81cf1cccd27d98f791652cf0042dd2c0ae59fadc71143142e56860b2434ae9397224592a042dcec0c7e519724ce304f1370b5a98812bc96a5f31a7a5c7bd0c4c86bca21562ee20774d062eb48d479df4abebbc31b0d6b1a5e31c94e5ee1c6288811661603f7e6039f9a1df64ed075a80818939bcd496b2c4cfaaf9027f7da9c752f6beffdc0b8c87ea9ddad6638798d205ce3fac8aa7aa75173e17b8ee204e9eee7d244d6e8149d11af9a176ec216ab4c07828126252cbb38ffc2cb0d1417e0a139398730816980aa91052725c81db601d8e84168fad3b56603da7bce6f133374d55053eba8f53bf1df46c53a8c0b7c798a3b3bcb6623805eed7a24e2de6cadc1d297092a7bf6b4cd42e2b516032871c76d5a5fbe802054e72ec3b457b2b62f4096c8c9b1ee684e77d574ee0cf3fb68e17eb26f0397a0e34e5ea30815bc99c9ccaa124956a09ec67b0bbce1b3af2ae042eb348e565e8df9430099c45dda46e51ba4a2d4860538a1332b2b6233092264597fbd9077d31029f1562fd5d50350d290297aa23740ebe6b5d161b5a56d08208bcdb56483a095155d110b890398779bf52fa8ed242603bd47e90f223d5cca141e0377d90757248028191f6983deff57372cf0ff80d12726caf57be76f980b3cfda9f63e64a96e901bf1eade330cfff75d2e9191a4c3d73a7c50ed88f7a491b43d2a8fbb4d00123a9d2781c049558b9d12207dcc771ebe96aec9398698103a6e3e51cf9a568e6ba5adc80af1cf246498d61a16c2d6cc06fdda6e49fdda68f695103367e98abbb731c64da4d0b1a306a1d3c777db4fa57b3602da775871e96c721c764c1490a2d891762f28fa458f0679dd143708fa397242cb8bacfeea41df1b2545e617db8e215544f5730295fff9757d54bde0aa6d362ee7d2e4b97c30a26fd2f68f63557c1446f048d2976773e8f2af8cad93fdaee2883aa25157c52777df5f8d4ab6f5470d193e3aa5e9ad69e4ec1edeb46493eff403f32059bf242f6dcffd14fb6a560a3d2b3eb458c14ec5dc7cbfe17530c268ec2a41d888751df8b824953ebe1d9d3816e170a5e72cc8b97a437b72f503029438ca9295be6a5f5135c7a8fc89e6d6afd513cc1698c1c427ddc11630a7682e9bffa7fb73cc19286136cfe58924fbc90e961d321009b602aa55b0fa725b3420034c184c85479394c395f2c36b44e996582f568d2412c4f34686005001301b804db59f753ead062caec7f08a8be38bc104115010260093edd4ab875ac64fa814c084025f828488e23ed50734ef60580126cae98a1824d66cd21681585004c82fd891bee21684a9ad00c0190846aa14a82aa5484162b178f30cb71d81e3b8406d923021009f6eb4c3df2acda91c67f100048b025b94b3faeb2dcf708c023b8f5a496e3cf659e1fc507017004df397774919ae287b1423b084023acddcb29bbbecd082e5456f64f13f973b58be053ca314dabb7fb4c115cc8c813911563ce214c041ffeaf6f1acdc9be1f22788fc777f3b4b2586a8760375e12b764b9001882cf10d3a28455b6108042f01b5639d6cca999527501012440000e48810d0cb8089210bcdfb8bb27eb481fb736b4143008fece7d734893f237cd1b4c8346df783002208896d8220a6c4839dbfea4f7bcf70930d08287400d620b281451cf747c2264afc4618b27b0f13c4de448b2cdb28713b8101d64757499d1bd6902d3d146795817bbc3af30811fcb9aa23c884be0fbc3a412a6953e966e0b25b0a22fddd15fd2affad8d012c1d2618b24f0fee156c59c35cdabbe336c81043ea64abf621dc2852d8ec079e8a1e5b018197310a541a3a44027d0811a36b63042d12802b7f6b1ee65e6a03958e35520821b7fca015b10e1b618021fbbe9c60dc193a68710788bdc9c9327a85f17042eeb44777aebbb950e2030b1e26577766df2f07fc048cef4fc1efa3e3f3e60f3667fcbd9593fa44f0f5889ac65d671cc5bc30326fb858454c898d9ee808d14153947a7ea7fba0e380f11d9538687d999c9011f474b159a5a2a6ea671c0addbfbc79f31ddf47b0326fdb788c4f43cf66103de3f63e84f314afee81a703521e4cf6891c3bbb2050d98ec8f9171f5272ac466c1c7efa92547c614f190051f344fee8f3be8d14dc582d72c55cd31784485b0e047d26ef2be4d2c44afe0a34e2f9127de670619ae60ba4aeba63ca57ddcb5824febaa3456417ab3298315dce4f0d7c390e7d1b6ad82b3143f3f672f5dbf55c1e7e8dee1e44b2697a4820db5355a612172d087e1456740062ab8ec4934df9d8648ee00e36f985940c62998ce313d4aa69b82432320c3148ce70d21b5ae71fa6f2404649482b5c841748778b1a145a5ca3248c1f7db666ac49c3c74cb51b011913caae9f09eccdf3081090e093244c1c5d81fa748be9e9597110ad662924ce5df669b1719a0e07b2beb728e23ff7cf14ec627d8cd928ad1a9513cc17d770c396d3a2ddddb8656044c657482ddb8b91273c675fd8b0c4eb0a1d7f43cf2d0be3f7613ac879de9614729f65643134c8e791fa5903c2158d686968c4c30212b54c4090f4cb01a9db284a03d9d630f19976034dd4fe720e844a82c2dd17d943e8c7fcaa8041f644c1a9e319225cd29c145f6fc5af7a15658903109aee34af7975bd779223224c1c773ab8ea27f4a79294f901109cea38e43ce8da300321001aa920109feb7b2ddffb5e686ac0dad47b03925975c8de4d29ddbd092e108ee4e5267d45d26f8e2063540462398aaa47b13bcfa83a6c50a3218c1875aa87597d0d04eb1a104d12063114c76ac67affb396a6a3214c1c4145c7a3c47d9276b222311fcc79147de5551df8f36443099fbd1bf34e738ec900d2d13d4d02ac838041faba7fd943d8f7f60f91b3550f037b0c0288108c2480102641882c9e94f4bc553bf68160c2c13a88069d03085e03a3d7f48f7e883640f427097c3d29ae47114e99b4170afd95f3db43819924510bc9e59f01755c9e9d340701ea7e8217f5c17ad1340f0232d16d378a45b1ef707decc33871d5afcc04da8ce4a1592e79ff581dd0c396bb01c3534a48e20830ffc897d8eaae81c1164ec81cb0bb7143972a3e3e881db1c4dca9da9634d56e781b5f34cbf4c271e38c9891dba488c991eb321c8b80393a93e7a3f62a42fdd0ebc841ecf0d3eddf8301c50429051076e5fdcbd2f3d35d2cb04197460b44248048b106f63670e7c0c19dcfce307b63972e0457210dad712074ed47376cae1a6821a0bc6099220030e4c720f730a3948519ae30d7c941b52aaa54fdab1c30dec4eca4d8f0ca934a50d6cfa8e3abc70ef8c920d8ca49cc12362ef25d1d7c0bad7470e3b2a5d658d1ad8f4b6541de48b7a1e69e043d2982cb38d062663ec5b0faf3d0317a9916eaf927bc762063ea7b40e3f726d115bcbc046571d8de696e3c92619d89063ef88517539a78c81fdc971074b391403d7154f3bc689ac0a590e6484810fb7ae3fc8716c312345061898ead0992e34527e08f90293acab373df454d951bcc047f2a852acd0eb0eb92e70139d1d7ba76e139b8a13647081ab0fbab7222b5dfe7c59828c2df0ffa3ebe1a7dacbf46b81c9c9a68388bb9ada6f828c2c985ebfb3c614752cf01727a5bee0e615d83aade497d2a5eb8cad126458818f3c67c869e2ea7a9555e0266fd09427629210724a10645081cd923a2544bff4513c409031052e744a172765cb1d644801d79469973b74bfa2608e4204d38a7af74e1e644081f7387a43c83b7141c613f00e7763fc74cbd186c041861318f3da548f269e432d29c868021f76f0b11ca41026b0dea134664689983e2f63097c87f8593b322d01194ae0a349f60a91fbbd9387cb480297ad5292dcfe20423255200309dcbbc713a2daf122057804a67386fedeacadfa591b5a5e98dd9061042eb24c520e4f43b0c617a79017328ac07ff897121df32a592483084cc5a0c93b4ec4206308fc74eef0fd345c6faf30c0c01b17061942602d47873479d7b6f36443cb0422e812b8d1a06160901184bc11c609640081bdfdc863d39ca3e7b4c9f841d1380ec8f0016bc932e849e78ed3a5d85094d10352a5c86a39c5552ca5985f629bb2ae78182d327850344a2063075cd0ef69fd40d304bda0000d1a5e7ca1033eea4b239d73dc2dd1c941f952534cd14b9181033ec2d53a961ce49a8071822f6e2880861764dc80ddd51fd714736cc04dd0f48d922260b4e0215003f62e57d2bc4b627eb40c1ab0f639debcfce959b0f719a287a81739a6596461aecc1c9235e463c1899f4b0e3df098757360c146f4493f62e521c4f82b987c91e17edb1dbb4a5cc1a9875153ca881c89e53b10a3157c20b9eec3effc38666a083158c175ea1cf8e5f41539d12a5875cfd48c1c872ac8e9a247693deca4828d93f3e68a1d840a3eecf190beba8a1c9953f0b697de719ce247cb81a6e0527af13049796429644bc1bf47fff529450a266d74cf3f9fb47a3946c1696b4a867774dac81105ff5f9dfda31cc78fef85820f4453fd861845f553a0e0a2e4e63077e81dcdf9049b543ca7bf34f1043741cc83df8a048db94ef071e6ee4bc92dd2db86137c0e11be95838aea286e82bf109afa3977844d474088a109566ad43e87961de7e46782cf51c7ff1e25a3fd02430c4cf01e3385a4b96ef688ee20c625d8ff8dcc4ad25b820931b31edb5c6be30f625482c9ac759d1cb678fa8a12fcc79a83b477a16ecde7418c4970fa39cad5a5e3fff84a82f315dfff0aeeaef6458273f558cddcf368d804124cfefc28cb563e824b8f72f06832a406311cc188496876cff9e3b5182083428c46f0392a7d52f60ea31f821b2f011a343e0437fe86092410831132622c82ad4b298587f79ffa3a5f80e1c5179ef680188a608207f9beeb4be27f9f08f64276685574864a352298c8b1684c9321d2244b0c621c82d58c1b993f8e3283188660534eef61fe39ca5cf10fc1da00a31b50ec046d8293408c42b05eae9ab29b7eb0f65c1083107c0e521a0f2af7f2723106c1f7a40cb1bd92688ee245108c8a487bb81b433d0c0682fdf30e2a47122446ccbb042730c1eb0a6200824935e29bc792f599f4872d77a59c13598211c30f5c12c9a1beabef2307e90327911992f998fde7f8ca400c3ef0d571cee31b2ba742540531f6c0a5201d999dc72195e21761d4d0400c3db0ada9fc3af40f21e3c686d6560b6e84e105df50407a20461e98f459f2dfcaa3f668b3a165541c030fc4f24fea1d98749b5ad151ca5c7163d881499e35a4ba0e543b4763d481adcf641a36a12c7631e8c0a5770eb71ca5dcd6ce818d1d78241db9ffc4e4cb81f33875dcef92ab4ad438f025f1e326effc92128c01072667650b1e65e70dfc66f29e12f7781f8a36b46ab4e0461855e5c68721028ce186a27123461b8e10d37878125ba52c8dc10614418c35b0da7b7635d13ace51f06e847182186ae084d81fdbdb723d687ac0a4474ab5137358370f98ca3455f163f59c68ed801f0f24df5d8abe4eaf033e65cc3928a91b69cde680dd74e7414479e64a8703ee62dfa790f81ee53d6fc08765bd1e5accdb80ff18435946e7ff842e6ac09bc52ccb97d9cd3fd5050dd8b43e29538e37b5c7de2cd8f10b39e5b64e21de4416bc068f334c73782c787109094d412cda86b0e062a7ac7905631f5db2ed567dfc962b9810dcbcffa43e7914b582759fbabebf7ef578b182ab0ce1919e443a225a05aba9a3c91f84942af8287243b03896a3bf2b15fc676ff98777bda95f5430593dfc983ee385f4c929f818e2755489eda9ac630aa63de906dbcf9f15d252f0a93fabe4e873887548c18d69871fa558192d4e46c16ad45cd69fa3cf5514051fa57e3bdf0e0f053f12f2f29a78bc913150301d07921d3bdf4f70b749c763a8f9c7be7a82f77ad38d1da7b5e2d8095637454aab1c524eab73820f338b6da478cc8f439b6027efc6ef730f25a9d30497b26f2a47c9ba827d99e0b4ec27c7a7d6917d342658b55c21e488e657ab5ec21da9d1cfddca12bc079de8fe50bd4cda4ab021f33dcc54a1045795a7ac2a64d393dc24d8ac0b4bcdb210429a48828dda99932ea96ea245820f2443ce1e7273d89f90e0834cc13f94c435dbef117c7fa89397a12398aa8d9b39c6ef94956ec4b6951d5e1f868ce07250215ac83b16374f16c104b51f8f7451e37e14c1afa4d6982a485e0cb14470aa9eb6adbf529cbe11c15d75942cef238474218760b296ff6708a9a31ecd107ce4f04232476a3f5208fe4b52fd7ae8acd19184e03c49d49f7478d49406c1646d7a28f12357a85410dca4f5782cddc4985330104cf2242987ff1140f0123f3386ec51e65bf903ab69c4edb66e3f30b6aada41f3f5866e7de0a3f65c9ac3fdb83a8ef081bf0af531ee9d7b16770fec786d1cbd8ea342533db09acfbb22e4e82c679d072e7f88df399e1c6da58207f6c3fb90423a4df0cddc81c9b17ec7b40ecb999376e073eac07463460d41d275604a4377c50f8f0e669ee413da21cd81cd517d5c1fa5f3c8413930a57ea269ed89039b3f68c72d5d9d3a391c78f5addab28cd48da937f0badd999267b6cdcedcc04da8fee61cc7793c8edac0bd785bd2f43d939ab0811d899d3b7f948f859035b02a9972ffdda2065eed5b227a85a8d85f1ad8b4abfd8fd16f2131686024a7a3b658db19d80e2a786065de1b21c70c4cdaff68b93b5dae6d19981c1642fe90430f92c6b4810b323069dad1bc620ed4477b021763e0420e7423b7e4f4b05531b0be913aeb0335ab1c260c7c6fa48abdc4cf6fcd085c80814fba13a1396f1acff611b8f802ab793d74070f31ff85602811b8f002971a237d501edf05feffb2f452b4bbf85b11b8e0029fe3c9da95742b04cfdf71b1852db6875b8c540baa91c5ca3dc8b4aecbf179f44e0596052eb4c06f84f6a0335bb2c0beb9e438654754da1c0bfc756fc6b4695d4269a7821a755c5c816bf1e87f2b7553e0c20aece7d5a013f43dac7a2eaac06f4f76cb79a316dc08a3580b6e6440095c50814b9ffa1c84ad98042ea6c0e5181ea51845b205ef7002175260cc3473a8f7d8e1568256c1e32fa018180be0220a6c87f7de7a79c392001750603c2a5bc7a1d1bb175f78516ee0e2096c8eab59a2e347971ee5e8c08513f8beb4d0edde91bda64d6062b00eab3dccf1984f26f04922655aba70097c94f3b6e498590729a90cb85002ab97ea17572284da7812d81cdd87d7de3148e0f6f2a62cfb5d4e398ec0d8a84b5d0eff32836d045624076e9da282c664725104fed7aaea7220991f4e99022e88c0ba8f647d9c1f736668089cf4a7b09832aeb7466ffc9fba2b7021045e53defd98c63cb05f04355a606a7c8d138081803058c0346884c18267021741605b35da3425c9714a2b10f4cbf0de8eea9d82e4074c072d77ff304589313c41bf8dc2850f30895426d595d54582058fc35d3aef5855da002e7ac05dca7fb92cbb044b1f3c60a2698e42881d1d224f3b602f9976f2d8f1d7faab033e1ccf57a1b2262d015ce4802d4bc9192745add60e1cf0514788f0384cdbbe19808b1bb0712be784a74eff5fe1c2064c5ef705cda3692c5f8304fd370c2a1735e0fea26d79678446995cd08091b6d2ba107f7ad463167c5df0704f296f264dcb828f3d0edf1a93944e3ab1603fb7698e2e040bae53ca1c2a278d9a35fd0a36e9a6f79da812c9ba2bb87aab4b9a1d589fa55bc1488e83cc519c4c99966205d3797a325247149bec2a58d790aa9f91735cd755c1e7308756eb92f7e3b8a9e03bc86de989d37a3d41051b434eed3a9d5bffcb5370f9c3f1ac147c63e4ce149c5ea61c59f0b092a9a4146c99ed66b6ab49c16ac8f651d4db7c221a1b5a27308157dda0c028784def9148546aea8ba26075b26ace1c39a6975c28589398769346eb902e070a4e33e7f38b2831f74a9f60233b27ba04bd9d14f344227af992c7547782fd8e7d6348ae15a2e6043b95fa9563916c82d3a849b2776b8e0e4b13dc4dc8215fc71d87923ab6c804f713d3377d185539425b608231ff1c4dd5b24a417209763a5d786f5e754c59820f5b7d3563dbc6785a25d84d3fcfd9a9a7049ff9a945731cf9438a4e827dcdf49421b5ffd4280926253ff390d3c31c29468297a4a3aa1d0421c14bc762d32ee26db9f308c692a9a85548f7149d23f8c0da233d48d6e88b36824b27f9253f744b79538c603537472d41832691d422faafaafb9e7cb19e28828f2a7a183cceba0fb648c4ea9e3afe30a6902d44707e1e66ec8f772f54c38b2ff6107c1ca518952544eff28c0dad6ac11686e0b52d3bfe14d19dc7f285175f100bb628049fdae3c8b5c38e2eb74f082e5b8a7f602939fae83308ce83e538834d6ccdd90a82370fa2d363c92092da40f0aec9ba43840e39b20208b6ce3b3b2c85fc81b75fdd907344c714a11fb8ad747617345bacacf5815dcd7142d0cacab12e3e70d9c672c4d71ce70ad903eb351ed36ba7e0d539f4c085c89df6246bc6581f79e0ba92b4c6ffeee09fc70397632f8f237c33feee77607c253d7f59eabd6f07feeb9259c776d1336eeac08e7e94c92bc7d181c9a8217dc89243a48e9d03eb69b53de6ab1cb88a112a75630efd3c481c980ea6a143bd14325f07072e94c5cbb7a7963eb437f0bb1d2276ec29377039ccf210eba22ddac0b7d84905cb528d1b5fd45841eb03b660031bef3695d69494a66943cb8687f14715a954c0166b281a23d8420dfc27cfd21a375d638b347097cbe30c16961dafb4051ab889f57e916dc7472c26f812b009be04df5b9c81d3699d1cb97ad091276660e3c7d08e4ecba1a1c232335b94814f3104cb8be169993d64e0f3bf57523a65e44c8d818d153a721df7f8a5d362e02fbc6fa2e787ff4718b8dd581bf363aeb460010323fdd9ab2e8259affa055e3fc791fe29dda603bdc0a61ce759db445b74818f286697cf22223c2fc0165c60d5f3f55f799424473133026cb185bd7ba703b7d00293bd3e2ab524e93c27177001016c16700b2cd81657d8c20a5c1649214e6b64ecc8ab021f2ade872ae95597ef2da8c0d5844c9a6e6287669d2da6b0782cd18f5207df2e0168d8420adce654e92687ead4f76d6881f1450368d008038c2f6c6c5dab50852da9b5ba2b49e896b252c1f87914214e56dbac162af8f70ea2dded34e314dcc7be769391ee2f6d669882d3bbf530be76ffe537a314fc872b3974e8890d85c10c523019a1513ffa18a5d368432b5f306314bce70f238698f61af60944b0026cc10c517021ea29aa94be82f7e2eb923023148c4ac88d54c9b2a1558c3003146c0e2ffa68e8ecd4ad9308333ec1859c4672a816ab8f4b4fb067c99385e89ca3943f16fcf142049de073a9495876e421677a083338c146dd9ca3b3bcdcdae02658dfad3cafdb1ca51c3220ccd0049b3da8b78fad337f8c3e1899e0fa23f7f5d2f0cfd821f3c10c4cf0917ff4a81e64be50910dadedc18c4bb0318e5f8790f2e9595882128880049660a375e057828fdb3cca0f7d735a3d94e073fc1373f841baf060c624f81feb54d56ba9ca8292e05be2b6a5d0b93d3a1809b65e926dd6c77972ce418277df4b9334ea94ed0806ce7804a3faa157b9a8684e11301cc1c67c7d9f3f4655f0d0f72ec1091ac107f935da6128fd1b0c1194e0c310810d46b095c3e41a72bdd489768be0e39eb8c792da63f78b2298fc7142fa69b714a74a04eb9126d353b2d010238608f63d4c72d7433031edb4f459e4ca21c710fc59cc69f337678f1853083ec51c3f0a92537fcc174230315e84e6cd31cbbc7971230561fc8d1a5e1c354c0046afa0c687d15f847187801983e0d48327c9b721e7385b13e8400d2f660882d3e84107cfa4f9e348c18c40b0a299f45e2d3a5735bc104108de580920030c10c18d3014108ce5384c9729756ee738e30faca760318478ac3955cc0c3f701fd4f95b4927f70be9c08c3e70e91a3fc8d7ce7f9af381cb96db3f4fea778294010accd80317af5affc54d3d70a61792e469280ffcbbc74b97f5b4a1f557801978603a44dacd3a5feec08606fb20cc427aea583b789bd5815964b60e9cd9a7cc41e68d187350d281dbb7f03089edad6b5f80cc81cb91e3690f3b5e68bb37982107cea7e3dbcf0f5316f5c481f5cbd651bf0807def6530e3fcac1f8a2024683196f4863cea3fb1b295ef56eb0eb4f52fcdaa3dd86d3afb667d61c7b1803aa0933d8c0b45b7fca36cd1af85493e5bb96560397e3d843f4a49039424a1a580f37f64df054f11c040733d0c046ca41736e8cf5964c1b5ae3461827a0fa9c71861966b8196570c20c325c61c61898eec8b5693977e0e10bb88000948430430c6c908e4f2da4d60ef5230cfc779fe75ad1da901e60e003738b96e3ce7f8177d790ff3ae7e0717dbcc0a846c964923b2849bd0b8ca4bc9672f24548c87281c9396aef0e1d4f3ce718c18c2d70a192e4b26c8916b832cf1e5723c618449b91057efbf28a4db2cf0c1138ccc042d100c18c2bf02bf935446ad477fc0180871956e02a276e96d75dccb13ba30aacc7251e86f41b665081498bd4d4c9d1c7671d31cc9802df3b218a6dcce176ca1c33a4c07aee58439218d16aa20fcc88c29bd2653febac6d057da3c6ff51e39d64610614d8eed03943e8bf14cfd8d0221dcc7802ff3f91263d67eb47dd2b6638814bcd7ba995d4d46365435100091080003210813258cc68026bd7f17bb9a498c7ab1ae83398c0a73db1a0aafe3a21924be03b5a658a65db3152c28052a36e7c1829f8e246da608612f8ca557de3fed2a7b124306942eadddeb41379cd4002ef61fa4795dd1f818b99fe3d43aa20297723b099d73c8957a6084c164979427a89c0ee7daa2bad1006cc18021fe78f3c9ad05935f82f04763f689fbeb4e4b97910d8dcf3bfdc1ee8e68e01026b269ac4c6434b1e627ec0059514a953a43e60e26feecfd63bcd34f6803f8ffda5b5e101b795233d8fa357cc931d3041b3b6a4c8b1660f2a1d30ade951b874e480dd9c7254ddd81b27453c0307fc4f08b97b3379d2d167dc80514f49976c397b1c4d6cc0a7768dab67550d6e912285193560573c9ad8d2516a8d05c6173666d0e0e891776ac837bb9805932b273fd2d5d2a770025e94ea42165dc4828f3b47c134e4c891478d304460021194805856182cf8eb4017b0e0aad43cc716cb8fd0dad05a0974f10adeaa231189123df50a812e5cc17a5dc7410cf78eaef118d0452b789fdcae8f13c6835105c68b200c115c75c10a3e4573dcc9b3b76df6484217abe05c5a33574e8ce96523a10b55b096ed11378fc6cb9db74217a960f37f867d88b977f9630a5da082099110e2715316e8e2148c7d5cba91d1c25f2d36b46a7c71ce14a756d6944e5f4c5b044217a56073dcb13da285467b9c0dad1a5f1cf24517a468a2f47bcc61d08e94000d1a35c070000d1a3518d1c528b8b71a9d48b7f1730ede95e942149cffb97794640a9e34a1e073becb21de74b851920dadd3052864d0c52750d08527588fd43e49e87daf8c68a18b4e70973f50afeaaaff461818b8f109f812b453a10b4e70f766a9736bcce7d1bd09ce546fc2724479084b1762ecd475496526f8f8335245e75842a96742e80213ec4bd2dcf1fb6d8c235e820d91ba637c0b56b13d4bb07629a78fbfc3c81cbb956032a68e0ee2875282edb07b739cce49b029b5a9fe8d0409950a421792e02a428a34296a9b46ebb68b48f091799cb226ff21c1d757c6a88defd1e491075d3c82d71c7ddd14c33ed0181a344e055d38827df1c9f50c218de03d2a7bfb18274670e5c96f35e2448ebffe41178b60da4b42fa1c7b14df530423b9a6c6ecede3eaa044f0e19f1f07d11d8860c33fc6d7cb4153e5c72198d8bd4957f36b083e6f6770e90e932e4b21b88db911736dc81c844808a62ec5d1e41dc759a50f829316cd9699a305c19bab59cc61fc02c185104997ec23ebc91840f031b22245d27846857f60a543d5095629fdb4e707d6352ba4ceab9162aefac085a7d01c879eeba38b0f4c0a21cd345bb3073633dd251f0fd203df17dc6ffae3e82ba43c30b6eba7dde6b1544978e02f2ba3f8ff55cc90dd81bfce29bd4f58bef7edc078d041ac6fc92ccb611d8812fe1fe7130d1df8b4cc61d99655299e736072caefe9636d21882d07ce4a5b52c7b68a213f71e0a2e59b8e53f6983a0a1c98a459fc3dfcd0d4a1bd81f397b68c53413f2e8d1bd8f30d8f1952c816dd6e03571aa78376774abb0b1bf81c05cb192a56b09a6c0d8c799b447bdd6c8b143570a36ef13a5a9ed4a14e035fd751a3c71f47b1328906a6536458b96ab546f10c9c5a76f1383cadc4ac3103ab59b26732b5ca6db10cbc7e4b5de85879ac4232f0fafa29a6e9d839da3506a6b72bc7142f3da46e3130c13dacd46d6e18b892e0492ccca3cc493030561f4c62fa0f720ef12fb01fa5476f1e97565aed05ae3c48636d1393fad5053ec7119d2e6a4b86d65ce052e8f873f812629a856c81bb94d3cf42d2de8f7d2d30a5913daf7e2cc97d6681ef186eb16e92fdb58805ae734364a607966cdf2b709e2566ef454c2947d50aec74a89f347558d28ea30a7c8ecb2be71016a96a4205d6fa2f6d9afc49f3fd2930917b93875e52bded4b81d3d7f45aa15214d8fcec93a8e93e6ff28002eba939d4e3bd60f1d427f0a9366597d9a7f37c71021f1ee55033f8d54513b8ffb0786d12834eee74c1043ef6d0b62c77524bb42e96c0fbc53829844a51026ffb799e2de593c046d445318f4cf9554402fb9555af133f47e053e5cd39f3a23caa6b043e5cfbcf319e5f044e3be54797a3b1b411420426738a0ebdd3857f7443603574fa36b7dedff185c06a4adb92e5215f4e5a10388bb09c905679d1a340e0fcc2f3a49af903c6d3c5e90fedfa0e351fb0058a6aa814af44c2c14824108683a1602810060ff036f31308001828280bc502a158389b773f1400034c30243c2a2c20202218101412128e44a15020140887818030281408850281905040240ae2f900f09921112a1907e2d88db5550fd942b3f00358670ef788a714eacdf453802140c52755ef9b5e80d933f0483741eb3501054b78169308e71bde28ab85888b86a8f44cfc0e383e0e4975ffa059983076f7060a0bf701a2a139d1798ae805fbf2cccc1aba89efa4a628e609f94bd92de27be23cf516cbf9e21395b22010a842f5accc106161f93f121f6a7ee29d0189ef89c975078db075e4f5d9bfdc7b303a09851e3481ca088e434e97a30e21720e0c2e6b6cf10fc5caff705a110f43a1bd250d21d3ace5f6ba2081bd8c47c8b6fa61e1c3d2c1b227b72a593604e3e41b7c151ade4434ccb951f16afb126581c09e9ebca1b345472cd46d896f5153f6d17d6f0f8b338dc04a28e03ce458a026144d57623fc31b978650200ac60a26b7f2792accc76a0ca3a844475cf8336f0822138966d3105e6e7bb035c979a6f3e808774c410033148d6472826426cfb8be1e0e3d3c039261c080e09599f41f21411375f00b51f103fa5cb995fbe08aea0157e7b9c2648228488c1babfb6c721757df35bb0d71a8b7cfc50592facdbb589fa6fc4c1f9a328fd44ef4e09ddb6f614f8e5645bb8be585c4ea3f21688f0ad04ba6a60807948d313078a0f28be8aa84b224ac95e1438cf053332a33dcc2f3da648ca0e2b797cba308e3a975979fbd7e8af7e6238ea2cefef6109c586a16ce42101a43836d37d1c34ef491775837f97abbea5c850c9b38278881da447cb42093f029cd88d211f1d5a0a3cdf183480d1a45cdf0e2aa7d45c5fd2932346cb0908c21d04b3d8377681c6623d219dec1f8b3e8e2d76c3fc4399023e48cbbec1e93cfabd61c683ef09d488e2d1799a68eff6194f8de099c2db2540bd33c65b3ebe704e4a9831b346f341718a8295070af4d4ded60387e4a2e309cfe5ff0a10ebd9c1e56cc64dd74bb8faafbbaa0a43b22d1225c50e8388f8505b07c5e630015987565ff90cfe26f7b8c74676bb87a29c9dbface45ed2efdb11e6cc5499940bae2bb8bfb346782199c89684768e478c2e67c8efe760dc77bfb39400dabf0aacd3cabee8624e1e1c515a1d1c1232eb2fec6e3eb11b7c9b0da48920f651c7b935033cf547a0e475fdcc7d887005ebf561cf12a90c5813bab26362cb2fa42203471e68114c299cc64a4cb866bc0049841f7ba4b5584a1abe177af86573023205b5a7c125099bef1c88cff3212d74a2f22c0f90f8f657cde78d155cd94edaf079f304410b7b496e48fe147cd21621886a77c922c20045bd0a5c267089ca31935eef90b64394aed5ea5b180712015a97b711183e030bc1b8423db4845a6bc2b9833afbf179ad3d8a07b92bbecbcbc5d0d4582f59037fffd51e8484f1eb9030cf73aa6b7c29bcca95ab30c0814ec94966483ebc0352262aa8a61b81deeb40fc75afaeba0239de60f821c8d6af3e47dbef68020e82271d5482834169cf26534625cd531efe065006393a5af50800340428ce80319eedf3f262d7539a2971daccb3944527b05ef66f7fe40cf11164b15d102d5d9101589fd434ce4f854ff699fd16087694a0878cfa6e34c732039efabb075c6f5fbbfd79169294a4b9b02397242a06d966ec93a81a54da224d586da588b364e75b2536db0dd8d6ec257e41a405bdcdc76770d307b2a05c473e5161d15e6288070114687b1300c0890d75313938150a3447cf29268741d957085da459841187413c3be30ecdee54180442110b146f129c8964f24ac0af70ee942002dac1aae0995a8bb6d202089a538af3b9357fb31f1348f5c643e332c89ab5a9295de1c821f8e2098ec266feb50adfec971478d8bb6277998f3e60583eca2964479ca73762ff8633ad12f822012d0f908b63cbd5cba0d2af77836234889308c3ea3c54057b90dfcd86813553bf8bb287cf4bb587be10c9c1365b5e1bf6b03989853d014348c975e2c585600781be4d23b8dbe07f77e820846fd0db1c3e34a7c62ca97d2ac4e172a267a8ec44cd7686692c2c3d6fc315027f671aec515ba1bac854923b5f560d42f0aeb72701a89d0edf93dc67dd80d38718389b40ad495691dcf3037832a93aed886c2e09bcc244c5c97a8f06b4838533c970cfe441c8f31a0d0ff13101f8ef592fc8648e9f271c09ad2f6ce8c8c7f98d5b24478a56bf9551a743a5d0ee71e2b926d589d04f60fddec8aad47b5cbf1365b488644f431e2a734acf0e784f43d3a089f26441a5082792541a44e25bd0f4279471d8080ddfee3a9a7dde6312d0e64987a6ba407079acee8beea306022bb1c75dcfb6d2911f0638ceb18107bbe9cbb1d8438aa78edf94812594d30bb0455eb62f6414b97d2b260c30aa506a132fad7090100ea569029e5046676b8994defabbedd9632bde2d4d64479e1a74b848dc2cda416f9975d8405acdb15307cc44251de6df7274eb96c862187e794cf240913169c824dc65fc9269bfc79e1227451fbe48f4a2d25a55ec493c4f436551b33288301775642c9cc7da0d950afc1b341e655bec91aacb263c49bdc5cffc54a1ba6b880ec7fc28180c2115f220108f4d7af541ca82585f3f0888b9634094740c7654bddd4a8299e04e3f50faa245501cbd0539f4842fd4361a1821a14d0fb13916c3fa838b476594d092a1d4a08e501cacf28e92d053c9af0ece1878aa3fc4b88ada1a250cf50438afa95ba160ad5931b19a108f50c85800aada89e54165414aa0515835065f3a176a162500d5085501d5079a878a816548a3fea723712097e2f0a1e8b6c3b509145c5a59c4801cb3b50ff5056a805a831d477a81fd43dd412ea438abae77f42950f350e850115e5a0801e2a88c193f513d19e725050f201d5b7ecfab40955b341f5196f58fd01e5ce46c501dfc06312a80ad43c94144a014a7e42a15abd2a9e28a1bc505b42cd52bda106407da0f650cf3e0ad84913c2f27fa82fccf703522b149e0443c915a83e6d3df439d4e346d15616a251f52d67190a06aa0a550095d241a12a47aa6b2494070a1b2a0ad50e55d0a86ea92c546686eac3c82a5f16945d87ea4384118d395011a1501d008a0f650fa5821287f284f28212d5176d064549a2688b794ab0325075502e2877286528604638eacb6e9c8b484c910dcb51c18831376a3d1477a3fe6501cb34a8542830546745f5a4aaa0925021a8782caaef63d51e2042a17e51658b8d743642e5a1a4a15c5082a1bc503a5072280728010e05bcc723336c549fb565de4e3ecabd9472099ab09119282ad413d41bea086a04ea7b41e9305d3ffe11541dd4918ed26154d98c635009a8fb400169ea0557301549bf4902d194055f889bf767ca6661c819c4df18b283c511ba0a64e4bf061e6c197268dad9917a522f965fb89ec2b22e892e3c7edbc9cf773536bf2468179df34f04febbfa8e802037e634b71c1c0098c2b1c95dd1027514f1baeef9e3555e38c897f1550450a0664b2f6bf5bff158ce362025e8a7f00da0e12b0752842d732a8d2127098d6b4f39e1d25cc0dc6cdf1a5d308a8fc8994f4821982322298d3e72335cbc1c53edfb3a1977600d44d119101f39c4f85923d4ff5aa310ddcac82f59a2b94191294c329640376a0a0587bc9db2d5ae6a84c4b6306245e6edc70024f3f4e8c5042e3beb9f0a21cc1fbd9f119284c83a9a473ad477c4e941322793ebca786adb34ffe32f430ca036b1bbdbc532f264764fc923678655cbe859a442a419c9f176cda0d506dd4bd7126c43c7afc8602defd97fe3e215a171dbd84dc5042bb0b06f18993a7b42093ac129f46c65597f49112acc084a69d8577088b7d7afcab126609b5446d86d607f7162044a52de9844cfa08339aeef433b4dbfb1f6b7bc0aae1ea611781afb56919ee399f2967e9d68c8e5b552ab242b515a3599b7a3d000c1aa665f3d7878e1e0e60f7cd65423f19c4e761c9df2a09479083656cef5a97236de2eeaa50b32099250ff4dd7cc8c51d76392d8f2c9e81823eadf1683053ef96f97158a904a5aa065b1c6bcdefa022fb1f22962919228218641d013a94c0d3e4925c5a9650458a06aa31bf86f9f6e0b3fb35c11b79317f23af4dac32c51c8026b086286ad4ed9bb077dbb40049c977bbff5d03ca0247287005b1f64c8480f7457770ab09ad2835485d545b1fc8cc703c0f1bea9c8eabd8e63f2ec134c9cfa4ba7c42514673cbc2b1c554373e21a700bd7c1df7e5fca80f6ce86305594af2de7ac1fd00deb4be9b15213b47a85062504e9288ec30935840d22bb1fe541c6830d31403698ae4d5af0028807e065f948664ea8e01668ef22951a9d830d466022843ff8fa6d61f49e9b0dba1a29e5b1ee2ea5348c0226ddf5973854f7a8021220d16d4987cb4f1695b30e3ab05b2ffae0cb369abdbfa4e0d5138d07c6807d49c7b677f7de9f3ce24d81f1ed2adcd5e43ecb1de08baaf7e5bc40c5b988169c28069f24bcd40517b28a1c4186916f1039ff963779450403728d1cee135e4eca148a08a642074173831dc2391adee3b1c61605c39bb07d80d888b5105f9811ce3c42ae38514521bf904b2bd40829423c3362d651c0740e6bd4cc6cd4339ae58792acc8eeb3d4fcc90d14880463843cdd17a103c2a8e800c78cb443117025cae3188faf358c84b5d9774ccf623e78f044a085c418443747f9fdc39091e2a255ef3c590014ece641fd7f090e1bcc080275e00c1e78cd08d2ae2b4e9c7558861da392e016491217aa34280d94074a1c73d09c66e303d60195c97b778f64a9b3b7e2833c6b3cb0b3e6067945dabf0a0fd22c0ee089fdd257502424d37ce71d0601d67c0a3ee243367e96e2f059a7893de96de69362ff23606961db191048740567ebd2c0d4387caaaeedec01daff1140bdfcd9f76b608414c0940691d922d888b345e8249de76b24691913d95fcf9a50c5b5389dfa3d85635ff3563a4251704996f6089b666e96e9d2c59c72b0e2c45bbd7498183e2e0c041df4e1f38db0c134fd63d856cc4fb21996f62cbf721c5be34ceb4b4528f37a3c759529055e4fe4f0b1223c9193b84086f989144a133698f55182cb57cb0d0191a3bc28882dc262d8b481f0563d509aa7fbb8d4d6a1e8a02c81faa3be785d4e8ca256d550b52d59404b21aa35f13604554ed716685309820db0005af61c1b8adbe83473b470d59f58493a7ee73dc3d13c39e1e3305e0a6f789f0ef39178b2cfa037fd92b7d71bf26881e72fcd23f012a2851fab64671ae9b74575b0869d2571e745c6ee4241dec4677b1176c88871ef6d2aeb25a89a852e4013af41d94f543466a0651cd933c0dcb30d95bcf069da2a5ea6ad667f7134ecbcf6355e4a5bea2f8db23ba7bece4b668beecf1c95fb1916728d22b1bb5c64450668cd5a96534d6cf7a9ef3802efaa4c01384ef54b60d9f0ea6affab05bf76ab957f0698649696f7b1b59bb1454eb5d428b6ad450d0bc9e29be1af290329c3804d1c31662aaa4a903872e69ff5aac0a030442636a1514c23fa459a4b558634ac29896b6079ba147c320747635cafb648ea32c9a80e392eeccfda8d8b1c40ee6c28c48519346e27a16eb3440ca019a6e5cc91c4e60d26dcc964ba7f206670367834867ea3af2640a0f539598050b2fe3a5dc3c6395fc32ba826737de546996a99c45f0aea7404102f0bc3b599ce817388d9b224fea8baada44aca54122ac9f9fdd3042aebb8d60ae61ddf4a461cfb483a4e83b5e75d471e92c2a260960d9dd353b006c04dde2346c6d246cb656b083ee8c661d22d6e845de9caae76dccc242ae191b806882224811937e35a8a86384fb3142b57cc4573f013821c6d33a267a6b221638a6cef70dcc266532cdd5567e15ee19de3857e9782c56607c71d60155a4728234d5016098fdcb78b33c8355b9e6c880158608319a0f6bd8b657750fe75fbabb50dbf6263920bbfacb788f4b7d6f0b83a2f4a4c607d82af507edf41b7658e4d9d5c2c4704cd711156b6207327434d83af567e9e718d3da22f8d5f1b99b1868f9e76633e9ecf423e2b43ce530eb2622b57d40137d7a9c99e81ff5469c3291b12fede5e2252ebf995992f5e25c8151eace4a339db6d83441bce70ecfd29be5e3fb1e0c33bd3404c8b6f5b5bcfec03a373dc08604d03b11d08ce7bc86a5e20cc26b7e8a48ac0805be415fd7ec6460932c691400940165eef0c5af191d24a5d7a3a0d56eb0bc22203810ac961572991bd522758107813fc12ec082e9b82188fc177ff18f820d034ee2342d73a372673a6ba751fffa38e9da00bc5f4635f56f3968b9a384ec45be220941e5d497f724cecf85bf3c4ac3456af6c14ed0d44f301e4825e8736e1c4c794f79a18bf742a579f616099aca65124ad2d861694904b0a1c681e055064081c93e1dac41b1b7b3a9e6194936130fd4a27e8a8d6838e2dfa983d89ce6f29828bc2a85553a7de5db5aa765119a762ba32f25842d245eb28ac560563100bf5412f40e7961e83ca206303770a280326cc6106fc10be31d18aacacca84fcaccc5d0aafc3c90417846ad0f86192e6edcc957ceb2a1f9ea84230d0eea91e32225b351e5e18362c04ceda81a17c38b69ed74259127102b446cfb05a0e7aab72654f15db2d937e78aa4fc1844247132d1644acd0291d2a09115682ae2f39b6a2fd8bcd8fd8be49a7b0d921fe9c127124fda29f90a82c63f815e96031530bd8856aad1c2125436e14cd3ff5a452ea57152fe84a1915d1125d5dbf5ba7c1b9888990e488f158c4dc909e04599c19998dca4d579ce417c81c13d8eb822ee1a05a0753f9e0bd364c769c2f904c56476f905d79c1378ce7e321eb892f676ef839323572fc6b7f5c36001e613e24bf51edebd8a4f5f6adcba6520d47d01224e0e565cfd2aaa159d468505f08f00144d74f7ce72808df2f165851bd0f3c1c270311ab486585be9e39f21978e6d23ae10d9d6d4b2ba6f984ddc38fd8306e5c54263a588dfd4a3af360333dd8a8b3ae78b16f173994096ecb7ccae42163267f3fa2490f0c7e039adeb351b3402faed72d37d17ef0ba47dcaac44960d2eccff02aa8c223ec6780a77e4342db67c9c0642e7f41062e97cefc0aabb756938de76c617d2224ac98dc1706357eddc18352874206fa64f8d64c1c4741e67c20bbc5ea70b4e866bdb124f15956fd2e30b1dae526daac9c3196d8a2c007617cb2fbff3832e39c9554d54d9bd26bfd7d3c966c16eb4edc2776656a8fb3fc825f74a60ba82adad5d8165521218e0b9516a9e88077f1061180300f247a1c29ea48ac4d39fda75c842add935a056335db551de049856c8fdddbb1213551e0c567e23555fec0676b07a2abf022dc26dc44afc1c1128c4887f0496a153f46e5574e59d26e14108996b83f5865b14b647809a3041698c4abf018e72d96f2f33bb80b7a977851d2c3bda3578893cd103d305c9bb9e70f5de0f4b1dfe081c16873a48d3bdae2407a2e9492acae0d3a2bc48327719639385121536cfa9d76622645aefbc637c0fbf9391d5f72e0e336383b19669b143388800d72587697ba2f17c9e6dccc39ca0aefe8c1090ec586ad542b65a15629fea8a8423e9d9513b6414589890de9c4a3d29cd83993976b8980c69719ae97ce944ba2fe95b444a3b85516b2a08216fe5b0ee6f692b91259680722f33c8b11e14d3e84f802653daa840fcca2108c821812ab34be58f5420a2f12d814ba63c70b5822a23b7c19044eb9f311a315bfe4c9c51c1c7334850d2f3157a25af2ae2128c2b7c8d933de988dd630bb60df74509db3089e0887068377b67dfa85298e2cbefbc22c308063edb206d9b210cccac4bb03e51f5bb500093d54e5f2220e1d390a8aea65cfc365b2cd43edf791d7cdebacc3421334d8629a26e30e743604002cb3bede0e6e1cbedce0e4ed5ede97f2b666bf775acb1d43d68725078e33de752095f7eb66604b816693ebc2af7d2de12be0cc9007d524d2d08396cfd4b0096fb80135463dcdaccefd5922f159b16a2364180b3ccddc8d8e2daf5e6fd3284d81ff5270e87b56fbac1d49230ede8849bb9d85de5cc8c1a675ed6fc4e20a95c8c5e5ad325ae1d8cdd5d5db5bb3a3be28d74bed6052b58e7c102bf9cc0c9cc24bb77fc30adcaf7a89e29c99fd8890c959cd20416c38d439466c40c7beed742b6e5f3503eed53e58b6c974dd9d0e7a520f404edf6fe5bb7e87b90aa4f39111d8692e861b94c3625185e16206319124dc19439957853134723304b0853511efa253900045dc204d161b9ac5112f2e1a025ce43e79150dbe3b906f1d6cbdaeb3eb9bfa25b4513c7f2d08d4d2d49a4bbc7afbf23fa1440232b051b79ccbfa0087dd735ee7b564fbe509d8273daf2d785d3de40e290a4ea0e4dc64335f711c581da9fe150309e882134bef4e84b7c385472510b69e143adbddb71088a4a985ead1334dd10a0b617581c740a4fff8b7bf0d206840ea845cd3db41bd11381971bb92cf33a2b6721b830283028be04a256fee9aa1dbb705b455aa0e29cc28a925055fea4ce7a921a27ed9e89b986d8a87ee6667fc986f74a18a7cd4c8bcacf036bb467b69a9b95157de489a39a36aedbcb13f1c47e79fd5727bdb467467620b10d5bfb2a74093045d5003d24c2ac1a386bd7bb696043d4071d861b301a6d5f7d341bd399bbe0c9628006669a5fe8a88c2a8ae125c2760bf2b1bf053757fb3471e9a53b8aacf0a6856b04d83c56ac72a97d505f58cb94f651efa628acde59922817b05eaa5c1ee4e41d05788b8cbc83ef289d4b516b34ea5a4b86035ad636ea9ad3468eb3c9a2aeddeb1fe8429e13ae86dd6b910b052a21e06064647cde5ad21b16a4b910fdf7b24daadebda6a38d844a5d49883e0d4491ce1d3ade78483d5c5fe310f2dfff702a09cf27d9b9edf7f5df788dd718811de72a2139e67d710e2ac5aa4466f81e272e2214735e76fe336be0bf005d43b20a5af348067fd7457e82d2da71b57675c11a8eaea8a95976daa16ea570549fbae8142dc9e78a5b4c5e5706d7a4b9bb8f22941d2524870ad185a54e2d337ba7277de66c9797e0aba0e54159ea58935257551971f33db85bd40a5acd951c16ac01500259041fd28428f2e0576e6019fdfd46db72cae7aca16ee7b1bf42148770a3fb404df9a525dab3cd5f65c2003d3734bf967d8c533cd13039dcdea42ef29c6271e9a7ad6556abf5df22f6c1753f41016e13a15f9e4a6c3af1c6ac348633341d4a83342866c5c43463e9d32d3a8a39e252e36ce282f07ecc7718b005c0f1dfbf733fe46dbaaf339f6e9f7f0b57e123b36e8cac0aeceb36be8905628cb1d048fffa08023772c53fb57f9a1168f2b8c90fa355af82e3ecaf37e526310315d8ad11d38c98cee18946266f121b59f04a296d6cabef75f052891040bb765a2ae09ce4db2c254e34e5cb045d1fd9b78973f4702c9b7fad20e866b2260b9a26694b2d4785ee5c64d66af334b918d477c833450c63b711b02669ec5d0a90bf381d03d1ba6184be992d9115d788c2e4323dcf46843dbc7ff240acedff9b6eeae1ba6a7cb49e05dd4a971e92e12a2377f43ddd441b4444d41e7e81b1e8001af548033fcae65f496133900c46310098a907d20b02606b900361cf44f3c212ee2adabd45b13f8fa28c58331e03c1db4ec3c976978caad29365d3978441d9739fd992aa83cab26b0bc2a8c61c27af0322ac759358e38f371b6a3b153a3341321c0cecc74c10d8bd85314f7bd6717def79ee2803cd4f87a71f404acbc9c55036ff0942e06b8e3759dd6b0d3f5bd6522d4d8da800df0548923ede5b5ec99abcfa151d0435946b7beda9119cd1694253402c95388ce76ac04dc03be34e9b3997aa7ad1c7219420c124048283e54565c3a84105fc10e510e76e89e5a23577cd09ff2bf9f1fccaef671620ed5595a12a5341f02bc74ff81115c3887b42fd7d37aefb85bcf71a7b2c541fa8fa0eec0256e15029e48fe655ef45e472f532f6d2f9b97ecf45a7d34708bac97c40bee95eed5eb95e155c7573358d22b2ffb3a62f0bac4787fe596f64316242fbfbe904dd287c48bb7bc9e48c8024c585ea57f295e583d5960c0bde45ea2fe757d5eb750d978f17a69f0cb06e6f33a4e359758bdae54d7e8ae6fd74ed7bceba1eba0ebefb5eb35f37af5fab191fcd3875b82ed2f5f0f434e605f09053071c4abea85e015f6eaeed5f3cafdaf459010471e7b55d6ebab280fdc93f35a7871f7ea7a6578c5bd62f03a3ea82294201829cd979ff00c36acb5b5a610d47657433dafca5117237afd27d5a1494d0cb0d084d9388c0cf97d142b38d9d7815921e654a40201a32111213361f3ddd6106a582c69643c224152e61f3a16a33cd2504e1376c61e4e6a5c4650d9fb4c790fb9d0d4c1e2a39d77a2fa9d474286bb21e512e80140ae2be932d3a9f29501eaeaeb53b5563b84858d3d13e37c8c23594921214ab71f54a32a9088d328e4b12d58e2f0603dc4944d72d6c2292ced07bfe5e291c6935af035af40414cb358aadb74d94e51470f5382870eb47ee039008850a039b0a221d2224dc5b3c79ede48e4fead480beeca0821fa36da8e0153061e2e39904270e7e32593784043a0cb892218a886ef378e25cbc13285210f0a76bee41e45fbf85728d7477cf96ba8c2ce821eb0c3db28ac3a67f558bf7e2ac241425bf41b8c041c3553ee9a6a17ab085d94db157c3eb3d61c4190fad8f018970b62f7084a8c2ff0becfd60fc712209d58d05fdd95406d1aef1e6ff05d42c5384961a7f0cc6842c8d5d90297459ba564d842f0c1d6b0e7db454d6e3b91bd05114bd44518bd7e3f83c349658b2139de56f3bcc0a03520ed2dda2fa8219c505c8e02fe4ec40f0b3550b4960efcffffffffffffff8fd244686bedff563225a984175624255793924c32a5943aed1600450100009a888848180500002780ee0a7c0bbb0a07952ebefa0c5018c6fc2c7288a47d24e713e6bf101ad224ba4aba6778c2bca1cf436e9f2472e3199d30556c245d22e7e528a5199c30684cb2ec164b9bc082b0feabe81d23a509534e0b1ec92a4c940e65c27449e80829b3f3bd7d3061be5c26928e70a2bdeb4b982bade53b28cb11f96209c3f778e8ec22633f2f95b86eb4e895f38a2c3e254cba25264b126acd72741286243a4d5aca398c562a09e35c760f29ea72108d44c2e45712b7d2886a441312b57ad039f67f8449f7f9bfefe785d11c61f6643a45ff32935761234c419efd45d0ad34f718610a39a8e7ac8724b26e8b28a75ab94a2a4d58127a1598b1206628c2904df4fc5d2e37a1548b989108d39e254faa544e7ad3b7e8a2025ba8610622cc29850959763c5dba7f08832ef9233574c695d78885198630270f5ef313de3eb582021446050a61d212275ea3f38430fbc8b44e42658dea7e10e633bd90eb30ffdcba200c9e4be94fcb17653c0f8451c6d284cba7ce697400619030f9d54ca28810ed1f0c22a7d8b1579496e9143f98f542b4f0d92bc2454a1fcc9611dcad52c9075349fa35ddd2b7a6e11e0c663177eaab54b8d1e9c11462a91c22774e154bcb8321dc86e9909784387f061e4cef7b174c43be9eb466dcc1246b64cfb4cd5c343bd4c1ec4987ce709f17357a3a984b07912ee4bf9130630ee689d7394d882d1ea4450e267df226695b13a9921e0793cf8555ddcf491d321c0c97794af699c829def9064394d39e16424eafe4b9c110b4b958325377f95b1bcc9a122b958e9e22478b0da6ecca6d1d6b1755d31a0cd92c64980ea3553c480da67fb7ef90334d8339ffb35a258f063b72c6878c75786aa8bca77dad4b19aa2fcc3883312d2bab2ec269d86630cf2725f227e14124a595c1ec77a7f51f1f31b00833c8605222e2f15bfd74fc0a02a40933c6606aefce2b7fd93126470ca6d8316b2ee71022231d06d358e88c902e7b059dc160d2c8a1a7cfddc4534ad98c2f18cebf3a25881ed94b99e1059316f5ef41ef235130a30ba64ea955f17b2a9a0ab7a0c70f2ec10c2e1854e6d3fd75cd2c34c68f910211ccd882b984b2f3f3942be94976b045c001ae057329adca12945bd26e65c1a836e1b2a9caa126332bccc082298d5df0b49f3324bf8fc008e302f8841957308ca857a5decb5e26335b7451812d9c30c30a86914f2252c566d5e955985105932c496d13c2c5ed65a9608ad9a59fe46bfa8fa6600e13f11371948db6550aa6b0f5d6637b1db4e8a430230a86c912cb83fc7c3b13858241ca8a7fc9ce130c4256fa9495f6bac3c409a67029eb420aa6f56354c1516146138c9db2994e4e4992c9fe023972f8c891e308339860c8df733e495c4a4a74662cc17cdaec57bba35b495d09a6be4966d516afeee224985f662c9b0a2a2539d00022cc4082b97d5bc75468398f715b30e30826d3fd3699243d5cc8463007917408490573efce1521cf391bbd55393388605022044b2a3e580c15ae4adce5dc5be840982104f3ecc55276a75a2c3d627ce143023d1e6704c1902e9577c408ce0082ad8f193f30c76d8d4574ad90b3b383d50739a3076a089193fe6f153167f0c0244bd769b7e841e488ed2836630796e3ccd08129aca51aeb10c91529099400da302307a64e7221b87b7baedb0f10401a66e0c0f8e797db66935364c34860f0203edec79a99999995f116255ce00542a043005998710383e8549e7a26b7a318e9c20c1b18bf92e5348fa72e6dbea3980e1d0ec618611cc42dcca881f94fb995d84a1d3d767614e32bcca08161df6f3d359450eacdc2f0b991fd57843e1141bf03f9b07b820c59984a07f79494489df4997990110be355d5856c936c6fbd0e3bc88085717f2b7d5a8d14c9f27890f10aa37508a53a52487eea7785b9a28f7ed32d73ead356984e75b9abcca5d3215bab830c561854f5e4e824a4d59a671506e139825095de9429b9b241862a4c77ff2521a86b90910ac3896059e44f19a83029d9dbfe6d3a8597ad09324e619ab0a3152bc92a47bea3d8064286294cb9997161cadf518c05fdc58f87801f649482b59c1e6535e2290521831486a05f1d82451a05280c2f1a308a93210acbb10119a130a90b5f227f56324061169d23bf569788adac8c4f18c4aaad27adddc4ec3c61903f227db4d277c2d4934d522a617a3e869c30afeac7c4393f57372917646cc220e74b3d8a34a5e6a46490a109e3f929938c4c98b3be2809967a21df840993fa90ebc4227f09d36976d0212e76788f5bc2fcb7ed6e314dc4e5bc1226711bd33a1bf193862861ca6e155c26e5204ae84cc2f4a6f7b742afa528ae24cc3329755c622512a6ce56695f49a9244290308a4ee6eab1a1645ff211060fea64a8ceca3a2f3ac2a49d9342c3c4eae9af1126db0de5f5723129ca820783870e30c6e891c60845cff9c92ad1e349b7087389cd8d52418a30443129495ccf62443d11b74cbcc82a22c264b1838e2a914f729e439893d2de1369ee3fbf6d084554f6db499df2bf15c2243a82d0272b494f112684f144149114a2b395c783782eafe43ecce589bb208c9a22d47ef98e16cb06e26c0fe95395c5f29f01619ea836a3b2493995a47f309d7f0e99f171754f2de4c090e10793ce7f4fb9d3aae747faf0966ac907e3c812c1828438da4ab8874cf586eeb6a597d301401864e8c1a8f5f95f31bd3a564a461e0c579efffeea3d6d3b34840c3c7817a2ee471b9128c8b883393d257dad32b28331ddf2c949e1a9f4937530c567ac89a91711d672820c3a983f748d4affff59d7e760ca16792dd17ebe442e820c3998de3d09693babfe6924828c38983cea8965d3a39492131ccc2f61f34ea4e81464bcc194e35b6587a7a120c30de649e146450f11cdc2db0643aa3acbd53c612713196c30883da563b53a4537a58c359844a55ba897d88ae96040861a8c791ffb495c5f13c8488331920e559f6a925e48b240061aea0ca61a95a12e6e266e467614bb53810c33c82883f9e3cb3d66891d990f3298477ef63be4e4c9d60146a3f342c6184c6efe96a52a042d272c3b810c31982c89146daebdb4691f06838767ad501b1f4fc7c160d6aca09e92e4d037d117ccd9245a8c29d911f9f18251a47afa6826b2c876bb80fc8a7e623b234f8805efaf82274290c1056367b5512357d98271f482ee5e09ee214f2d184450bff2a02524d15f164c332744fcdcfb7515f3d791170032c8c082d1b35a2f82beaa282741c8b882b1bf624b82a7ad60522a29d7f10839df2e80905105a3df5fa6471369cf6e2a18f48956580fdf7fab4cc1d879d1a249c873deaa140ce14408fa4d5a3651228f414614cc51e742cb8fbeb02414830c28983da8242e8ef6f8d53fc1106dc77554fb63e5a413cc71f5b2655f7e665b4e46130c666ac95ac47f56d4ee0b3298607a4f6f256644fc45d5838c2518d3a28474ba576fd9db518c142ac850026b9756d91c6424c1d46db3b726f2fdff87e163f840825146deeb888c25611339c83882c17afca48a900ff77847311de5b6e80202a50a328c604e59a4bb575a2720a30886d349268cfcd85da62283080621a4c767c74eb5ff640cc19454467a11625a257238c81082295aaaf0a5724f1201238c4d25c8088239697908c93dba886002c2e155d46bb5a3301364fcc0b87d2392a9ef0373575ffbabe47b60086147e957d3da31f1c02052ce91f9f18452bbefc07c31febd3be9ec6c1da08c1c982d44ddfc1454d02f3b0e38ad23af223cb332c8b8816955ef445c5de8f021193668dc638e2e99146114c8a881c143f4881a39946ecf91410353d4b88fe13e725c7266613908200ba3e8a8619636367dee58687a9617b5a355ac60613a25528a96937d4596f276e5074f35bb17047085a93ee47c7cd1cb296a2bcc37fa6f69e2756889b0c2249452b66fb59dfd5401f888c8d82b1761ac3e9b53f34989b274b0220c22c3f74df78fc9a8b4502311e6b417ec420a9fd4ef05c7a006224ca3fd94278b9d0f427e0883ba2076ed3f88b21919c2103554b08b135408d3cfa7f96ac8e4f59a1086bc9fb7524ee9c5393cc0d0c13ca8310893deae4e2a6f9d14f1e60d6a08c2dc97e4841472a4d4cf0261ae3eeddef7e8d9a3dc518c4715a00620cc622acc83ae8c89ef1016d45e20043ac2a8f10743ba1f6517e2250135fc603c5553e9d9fb7a06a3bd10a3461f8c218432934bddec0fe2834154f04e314412d5d883f1f35b7cebf1caea143d98ef5d24fcfee8ececd6c883612fbaa6c5798050030f264b379ec467a7eb5cba8341279f9d532ab283d942dc3d95b3454eb4eb608a34e1ef7c3627d4a083496edb7eca50e945f33998eb4b3dc7ac4e162a480e46b94949b684202fefe522470e5e428d388851030ee40d667950a33e78e4f88ec205670d37580e28d468031c6ab0c1a4b753ca1b232db5446bacc160972b27f9fbbde425359846286d7b35f3be73d74883f9d47699a67d8e27760d3498b443d9859055d27ce9891a673027ed49e2c9aa7562690230786ca286198c9d8277dd960a1a1372c50c35ca609cbb1029be4bd0913b6430cb7d9a112acebcc5c9188c6379c2849e37e159bcb3167c8fdfe1a32320841a62309c540fef27bcc24cb0cc0735c28035c090cec183fb280315d8c2870f2ec0e0d13bb8c891830ca00e35be7035bc7035ba60f8ceb44979173c24ddfb480bd4e082492f8f5a895235e9845b30c9a874fa7d22d7a87b47311d7a5e430b865341c533d5f272ad4616ae06164cad5721628989e920845e0c0e74c1811a5730c9a57c585887df980435acd004d9166c94bdffc5183ece183e120435aa60922b7f317258ed944f3b8ad5a0c2558d291852965b4e39922a3785f1430a4631cb156163ee28f63daa8b1a5130c92ed1d92d6a362ca806142cc7096a3cc114bec2758c0c6b38c12c5e9147f69bce39773972d46882f9646aebe9af06134cf56ffd29a9c77e516309a90e23b3cc54ac7021881a4a30a71826652e9f04838a699391d995f6c248307e72b1ce32912398f44776bb13353722b92ca86104639d98cda9bc4ef15c1f358a604e53b7470d22d8a0c610744f115591cd3f9511aceb410d2198c32595b0d17e22bffb1a41307dcc3395921001c12cf27a55b3abbff2118617a8fca8f103734cf12f8f23eb572b3f787cf145f7f04c410d1f9873991a214ba9f0dbad460f0c132e4e3059ea1dc5c058418be18380518307e6cdf1f211324ad777ca50506307c68b20aaa2e5ea617ca13a30669d25536d5af2053f07e71d954f2786d04e7b000f06357060f616d513b742f55f3d7ec70a8c0b2bfbf1888b698d1b98fe474cf23d2196b49ff0b6e802026060a00318d8828b2d5a707fa86103f39e88b92182fa9492b6460d4c26e43ad79d58cf95a7a1060d30094a6bfe4cc7d3de2c4cda75f9a38c3aed98cb82ec7b221742b2cd9dc4c218faf54a6e7a65f7b0285e5f5d0e39396aaf30a78dfba06919713cb4a3d8ffe061f65e1c7685f13f9eac74da56184b9c3875de2e2b4cb5191f3a74642b25ab30249147c5b4aed5734d150691b4ca278a892c1105fd238f025b70b18551608b2e2ab0452a94f5cb29ad9e122a2ca1aae475f8f8e914344c411229548594ff769702a97595d529c92f448a3246e4e2e42c5eebb6838b62b405de8b3346180e5013a3bf280b028d5198ab948cca09db4b51270d511833764be7740421725c878f2ef3c18031d00885793be48d24c6a225edbc400314e66b0bbbc9d1b47e2d9f30c5ef9e4be5b227cc61ed762e494c132ee98469ee74b0dca3f75674dde3bff81d6218829c309ef5be9e9c8aed0542a063013436618adbbfa374d71f1260807be1457fc1811c399086260c336123898512d626df514cc70e1f638851c63e3813c6b8b42492aeadd221a28109554c55cc11ff5cc2dc2631c7929e2d61ce9f2bd577a5e4f6d14a9873f88b203a54f29b9c0f461a9430ee888a1e3ea9ed0fe28e62761aa03109532ec9114eaf7848214712e612b36ff731498697ce3c402312c6b7aff092cf000d48184649cf33217e362d7f8f16e37b88c1f60502c19bc2008d4798b2897c0b49aae8d0e68e6261fcb8e38286234c62f5cce3575a2973eb0718af8223d0688439cef6eeb34fa0c10883f87593a4848a9fa4640230782813682cc258f9934eae2145182f8894749df4f81dfd7b64056824c26c57a9d357e8ab35bd75d1c50a7a03762be81e14a08108431031bb9dd4c3c28e6f001a8730984891e294eea4218c122c7465cc5d0843cc0f296715df9209610ef35293a6dc4a94350883777b2eede192f789056136a52a25f159e64904d7023402610afa2c841915574b9d0e307ec70f1d470310663f1d8f263967ab442be81e681ca0f107d228e1da223d44579f861f0c7226c90b6741a705a1153c18671ba0d107e39e1291ba9722bf4568f0c11475fe47785ffc0f1e7c011a7b30c4f5146b54a4761443c1bf8f94000d3d988350e174d2ed546005bd01bd8d008d3c184c5e870ffa5e94f6941dc5ec79f4f881001a7830cb6aa932bd9055b11734ee60ca93c22893763e7192ec0634ec604892548f4af5d4c17826d285b4162ac9f014d2a083c14a489f58722f33a03107e3a57411e344efeb7a3e78a80c68c8c178f7a16de2b2859d380e86916e3d5b3797f4973fde8b770207534a419987509e3798d3ba259adcca8e626e306b9ca9dc0db551c280461b4ca1d4a40a2394ee20ec1dc574f8e0c10673127ea51e84a5f9da5e40630d86933d77d93a440d262dd321b12d7a2a95a5c1acf12d3a7a94a06c1de7021a68308a29e9a3a1f49de5eb0ce6161d32717499c5d86630fc8744310b2a8329a5fa75ca7dade7bfa3981d19cc6bf6124455e4945eae63018d31182cdf45b2dafa90af8086184c265a3452dafa8e14de512c0c86999faf13594e29a98006180c6e962d699f51a595b3a3d8178ca3a425e5397414d7d30b26ddb19f3ea7d30563e9cedb713bce78fa8306174c614d6f48337947b11f626cc198a1bcdcc458af755a305a7819f1902f29ea3c880f1a5930c5291d5f112d95dec2a08105d3dd85eaa85f1e2abc3b8a15d228c4d77ab8d5296dd10504787420478e1c39ca0f30788cd183d0b08271d427c80ed24445f61dc55ac082307a88a1c3070f4d008d2a984d7b4e17c4f3455fa4070f3158f03a7cf028fbe1238c0888f1850f09acd2a08221a86c7c5d4cd29d2c0f2fce1819c89123478e1c39bac718cd31a03105a3218592baca3857934ba6690b6844810614cc73ca83889e633d7e88a1071a4f30c5a7f09e4766a49bd5acc70f319240c309468b23413bdb664544a30957021a4c304ab8244dd7538628a52598d3cf9c3a1d272518c2f775cbc871b93d09e619f56caa3c9e9cc79160f4585a291e42aaeef8118ca19f333f353b98c48d606a0f9f2d5df82e139e2218acb72475ba1313c44304c387bc9098273376e62198c7da434eb2af108c9b113f4bca8b1552120473751c0922878e3e4b02c1b8ee419dc67f4a9ed74180c60f4c97699b319e5d92b45f748fce80bf0bd2746082860f4c425c2cbb7871268998011a3d30ebdb6edc5b38ad7cda3ad0e0817136d44484c47760ceaee0593a553cad711768e8c09421667776cae53a2ed0c881b1e47c7a50290807e6244d8891fda48447710d346e6012295a874b3f1a39f343030d1b9873f2bcb1e76e226355038d1a983ec44389eb8ce5f2e838030d1a98c6d52b68c5c7519985496fa992bd33c9cdfe1dc57490dba20b08e4c8a1c3be18a3c70f1ea747ef0f66c8c2eca95259ec09d7b5be61462c0ceaec82ca51be53c9122c0ca9a359aeecf615263d3ae7641699e10a5338b91e94b4fc39967a6398d10ab387e42f59794d6ebc628861062b4ca22a6f5558578719ab3047ef08693fba88eb4915e6f098d827332c85f8a7c2179deb41626551619c20947d1655eba382a73069c5d3cbfa1d7221878619a630df7de4f5e89da6e62f85b1457830252a9a410a939e98f216743aa57e54c08c5198a4ff55eefcd5191187c00c5118f489146c4525850acdcc08857152a7cb03d87848dd5084c1d4bed39ad5fe5dea003712619022dfeb39f7a29e0e2288f17eb2a3391f220d370c71370a6152b6963df25486557a8310893c4a5dd4db73a87b1077c74cd0492e858b9db31b82387d8ea3a1ce3f271085a0ffbfb4ca8e5c6bfc821b80305d70ef9332d1bbf107c38551f9399ddc6cd737fc601249dc2385931fa5721c6ef4c19cb5845ddbca5ad02ad170830fa6bbabca75f71e2d7258f03a4270630fc6cdeb18f97c4e4f8a0ce2861e0ce26c75c64cab76b8910773092562a9eedc0d3c98c62c5d98a03b98e22e82c4c83999ac2c256ed8c1784125eae7971c16ca91a3053f78dca883d1e6424e3d11743b3ae8601266b2fb37d58205ad0e2fba78e2c61c4c9526aafd9a9646563571430ea68fa415ee9293891b7130ea7e7d5eb9e8ca1fdc514cc70f4579030ee68d1b89f92b890002e37b7cfa0db8173c767091e30de6fbca3942fe5ae4d10d06332daa237a2495376e8339c47e1091b278edbd6c306cbba8ad104ceecf9923479d2f96e7934e23323518440479ea7da2d7cb4b8321a57ca54a10125204a1c1ac5afd611e45ac6ee70c668b54d973054bea153318ad62a9db0b9dfd2096a1dc2083314cb5c76d2da915b4311847e86d89fd313c4d8e188ca6d54c74d2a630182ca9fba0746530987238fbbc2a7a183e868f2f98e33a82f6b2f017a2a9b0e086170c5934db842ab1b513b4821b5d30cb65b3cadd562f3632146e70c1a461daabddf2538a849b70630b0699d7a9ff2fc43a09ddd08249472e5d1bcdb360527295db277d86cc08164c162282d8eb3791f562c28d2b98fb26655ee9586a5fb682a95cfbfc576ff48b6a0837aa60d01b5ba365d486572e841b543066e84ada778486e7730a7a7a1b75123f47f0bef8626570430a26f9701d2db6e48b1b5130fd86de5f0aa5f4b3fc0105a3249324dd84964e49995edc78827145cfa5906f928e2ab913dc7042e16d2529e34da2a570de8d2698921addadf78f6821627ce183078f1b4c30a53a61e2a2e5307a4f17b8b1046348efb990b57430783c20478eee31468fe18612cc9d1ebb91bc6a7c824bb89104d38d4e397584286d3a3ee006124c2525c889113fe7a31471e308c68b6b6ad6bf534e7582c0165c6cf1002eb670001741b86104531e7d9d3dfea90846eba03be92d7141ea89088678b5e63ed17205390dc19083474929b4625a9684607e53fa0f82e12e59d8868c9b1d3d100cc2c4dbf4e7cad0a3ffc02c6ee92c2971595d473e305648a972d015fa83a40766adffa09f638707e6ecd26542dbb885d8d98149e4523fa555ebc0f4eb952de693a689e4c07839f589b8d269722d0e0c3a7ab7c949d13ffd3730caeacedcf89825d7060695843e29314f98ac941a1874e224d39d6d77f31b343064b990d74a3d0b4392bc21de6bf2f249646110a127f9439ab130b66ee996b01216c688934e6a64ebbf13bec290e49ffce5171325e20ae3a91869c47a6475bc5698948ea31d4d975ad05961aa9cdade4f7ed033afc2acda93356425fbe09a2a4c41fd3523c911e36d710e6ca4c2e09f1fa2588e51413b1ba830e934f3419dca6e6b7f0ad38ebe0755e17fe745a6308c0e1e44658ce7b44f290c36417f3b6ff9c94993c25412644d774bb5747814669138e24f3c9f9cbd88c29022c94f9fc6df535c42611e195a5214113d0955018549f6bf25a9d871adf513e6f9960b1f21ffc68478c2ac17e2e385bc13a6781db9f63a934214396114c9a96672acd349b40983feba3e19ad8e319f26cc93ade2d4c7d8337d264ce2b2cb93c92451524c184b75c82792df2925b9843184ead361baaa26344be477e56e41df5a306c54c294dd39edd53f7b3639258c9d636a880f3993308bcda449e9945adc5a126693da41eb6625ffc79130c491682a63e564bd8384e1af5f54fc9ad81fb247182d82a754313752a81d61ae9bf71349f6a24a1a61ea54b9ba3c568c30895e2bdd95d3e5ca44cedf0962b9224c355ed971ff44185e3b9bce0b7ba22e8408a39690d3be765a41720e61d0db7b4f153ae824b321cc25e172bed94b622c5208734ed53d1659dc4c8e8430e5cf49241df753a8920dc294e5f373bcbc204c1a7fe5d14ebadc57e5d8088439dfd2c412def1767340182b5e04ed346bb9e2f307d34af615d121eb07a37fc892e556ea7bea8359f3440e6a79ead353f860c8174ab9c6686cd8ba07b38e6b69cfcff4601ca53ac5f49ff5cf55096ce4c1a424858bb514240953c28349dedc47111ac2ee4f7730e5f1f51ecb67a6eb6407531095424877ca33d6af83415fd0fe511d2727f3e960d2fcd9f0d31af23c3e0773d0f221e647895a429783c9eb538a799ba727ec3818574798c8080ec61e9172e48be5a6f11b4cebdd914bdaa53e0bbbc1aca55df539314ca96d307f12aa63a667d3125c36983d9575b0ccd7caa73598c24daed429531b6a3088f7ec273676726b62230da66d9f1137bafe228460b081066304bd7ccf12225260f0e81fdfa31460e30ca6cc3a117a9f7545ec66307908123d825cf58dda46194c42e78e997cd195da7fc006190c92278c30359d7c373406c30815a6d24b6428f188c1dce7419fb212d7a72d61305a74f138f7e6351eda0083392d56b6483127b8e57cc172241b5e3056b65373661782cebc0b660dbff4147acfa45cb8607acb9b22447a5335cbb6c0c616b4870d2da4c04616dcbd76142b89021b58c034818d2b9c0b6c58c1ef17d15f2e32a236aa605bb968856c51267c2ed1be3cc266dc518c370936a860dc9cf9d891f8567ad9988271dceab7d46d45d0d99082d9e382b234ef946b7e144cbe9bff9e153c7d90a160343df931d67b529a888d2718c56e5d52ee9f6d38c17059e4afa8b4ada304011b4d30bc05919f22e4bfe4ba146c30c1ec95aeedd479de8efc128c22ab6ea699162aab04e3a81111d1266586ca9360f0ec5174320b21c1e4da3d1a39b64730a9ac90338229a51c672ac9b977dff609368a605a917eca44a629370f114ce6316b6355ea97d2219893ee28cf7ba5e4a7f98347186c08c1941255947af4bc2a8bd9186c04c11ce7e2af07194b29e874e0477bf1801c39c0f81f3fda8b6445b0010443b69d1b29f2d92e4e7ef8f80bf4f81f1238828d1f98e279b6cb6ffe2167c9072635cf5f76bb7b31fa1e982d94ee244df23c3068ee5fca323a2129f90e0ce94bca08f5614674ae03b3f6db955af654ab790ecc65f2e3ad2fdd5cfcd8c081b9fa43f47412d674d0b1710383c5b55f1f117490efb16103a3991a73155dea53756cd4c0b4a32be59985296ddb060dcce739f5bbc8b6a092781626afcc4f4152b4d83ecbc2985da225d4568ee5702ccc16b2748d5f0ef50ec3c29072ed7d56bfaf30076d317ebd76ab23eb0a83a7cca56d9589d5a51506b97f613b6e9bff565861f21c954c6a7ffe9cb40ab388b7554949aa30c911172dbaa4bc2e4a85a9d25534691e4585f9fc5d5faed35398fe74f2abf0a5a93b3185297f765bdfc931ea91521877c4f38f7f85e93b4961d027a47765f554e5e2284c61cb2d85a43aea85280a43f69f0f1ef31feb5e288c1aaf62da65e47e3c5098d24f9454eb7fc2dc292bc747daeb90354f98c7c4a8ca703b6192992694dcb1516272c2a083589bff1cb49fd036616cbb1f9d2e084df33461901e523031aaca84b9acee847b9860c22052478e944f41dbe75cc2b8f641e6e59e58c22084eacb2397247f082b6148e7663f67f24a9b588312e6a02babbd4915a1d2aa3109f368891f29c55cd3bb15491894fabb7d33257a44549050a31e9dd2a5841a9030f957d210a34ce7609247185f8210d95d3e47986d845fbf986d9fb68e50a31126b59795c3fcc40d2d31c2b40701688316823089aa7b1889332256301086491e3ea494a262b9260138680108a3a8a535d96ad1e20f66d94f6fbbbc1d4a9b8c02b4f083d95594bebaec6471f25af4c1f4612f1e4f9ef860bed131a9f752418b3d98ce6f84ca3a13c554a40b2df490d021b6c6e7b46ed10504b468910793bccff0ced1a1051e4c1f4a35f63a678c2f74a4167730e9db09db213b2dec60d8b04f5f1b394855b70ea6d263ded94d54e794d7694107457c8aa5175accc1903be8a0a3c42b07f347ffff91a4ea938671300795efa6c289e820d4020ec6ac1acdcb0929879dde60b80941944e22cfb8654fa0851b0c1bdaf4c751bf207f4fad3cb4688341c6cd4a98e441a5f4f81f603c1b4cefda1661375fff3c498b359c745ee7b42e5944490db4508349089315f98d0039d7220de6ad9cecc2cf2fff8b98a0051a0ca944bbc7cc5027771a81166730abaca6fddfd6fd6a5a98418b3298e3ad849f0bed4f4fa9eb8216643048f518d7e172ccd2e351d0238c158c81cb08e39245265b8b65ccba8f584a302da1208c307678e13cee081eb41083494abcb21459fe4a448b3018b52aa84ab2a005188c3b3e562d4ab7ab2725408b2f98e4b34e78cb1035252f182e45e8ceb2f031a62e18a49590b05d4a5c306d67e89a18fa6b73da82e937949ea513a6de4d5a30470d11a393ff5930276f49eb3e23242839168cfda6dfccdfe54bf62b18df563ea8c50aa543dc0ac64892a4c5ad10cd37af82294dd6cc1052a48f8aa860f83437fdba96eee9330543d21e29bc844a3aa58f144c273adcd4561a95f1898229774e22a7730e17b10305738a13847c5a157da9f304a3df6d8c4a39ba5fb89d60507db224be77e9bddd0443fe544163d46682494b53fb45844751f5124c16a3f46e87503b9f5582416b57a8990a17eab24930c5885ab2f328d3c92d120c1be9d77eef239892957b461a696a3782f14fd569891635599522b8de3134ee829408e6b9aba81d4443308da94f49521cd33d89104c9f5c3343361204a30713e1ceb64d478180dbfbc40d19c91f6c5f62a2dd25e803636ac65df8ea2a7d5f0f0c9f3d87586a2699f08e07e62442d7d889a495afdb81d1225975e8be90a2d781414f1c11f2294bc9fe1c18cd3e5e8acc7160485a3ace6385141fb981294976f7749335b26736304a2e29b2cfd7e742a5450d4c16f282e99dd49ee25983163430fe7772f3905cd1fa9f853955dbd409a5a22949cbc268232ead8e8a63622ab138da8c2a532d2a2ccc6917d1f7e6f64c29111770bcc238dab5637496b8d8f183c7077c6cc1c5165c6cc1c5165ffc0f1f2de0c0165d54600b9365c40a385c6192a2f4ef3ed5a7f04f2b4c97c428132ded8e6268ca8aeb4705cb2a8c924ea518629fa1d483431526b5a15b5de48a001ca930854e165f44bd83c87b0c385061b01cf9f46ddafb92740a83783115cfdf620a7346360bd39652984f98c550fb1cb29a22e2208539fe45487137ec028e51982d3d33fe427ad1e1238a3ec505295a210d85f942b894b256e44e290485417d7c122969fb8834a20a4e0b383e61ca0bad1fab3edd75eb09533a0f6f13f284167d274cc226a9b539957e52b50130306049c0c109a3a4154f97dd84c9a96cc2742d5fb26ca2061c9a30293d21b49256f00b5a22061c993079ee25b5ef578d90820313a6f09d91afc6a3e93e895dc2d8aba15933c9765fd5030e4b98f44a18d99ca44a18d6b5da54ace8fe95a784c94a239c2a1dd4856a13ef806312469b13513fa9dda9d861c13b18676dc02109c34849caab94ac2813d5613ddefb7728e251c08023120665228f4ebe761d1e215fe0808439a95cb453c96543fa8f3024b5dc4cd70aa575778461feb6f208612988b8118698befa556a4a3fcb8c309887d82ec230ba258810234598938ee589de4a5a421261aa94dd162985ab5322c230ef55ae924b45b2f410861cd4b359ea4a0a2643982c3d4fbcfb579ca04298923e1371b2460861188f9cd4679983308cc83756e245a524434198279d9e1c727b08f9bd4018fd2e5cf4183adaba038441c77afe60ca5dbaa2648dbf9a7e28ffbd7bc9f20c471f4c2a7caeda469f5a5d38f8602ca99f4ba89af660f658da7f5efa6329bd1e8ca7c498f6fd944795280f660d59167f94ea8ce7e0c124fedd82cc897fe1dfc19427b7554c894f7bb71d0ca66f2ef54f9ee56c1d0c29a927d17fba2a214c07e39acad5797a0e466f17ef58d1958349e55a3dedb98c83497fbea4635eb2f75ae060ee899f6bc376f647fa065395c83f2225ff8e22758339c5a5d7bc2571b9a36dc89408fda14cab6c308dcace71514c58c4c81a4cda744e4aade793e05183494684e8489334fdc23418e72e89d3772aaf233418f5db3f8216a15aea33304289903e2f6a0653d411a7bbce2b83f992a66ce530f23e776430bb67cf71a631183dc91f25cdf308218bc12c398591d1b3ab9e0d8339e82cdf26c782f6cac0607a3925f2e4266cfb5f30dfbcdefbe514ab332f98430e19f9b2f6d5afd20ae0e882d14dcd56a80e9d2a45d31ded52143b2182630b266dfa1284e7912242706821cd4a49455259ea08412b8448775ecaf214c09105e39d7f7c889663091363c190526b84a54a7f80e30ac60be93f9ec75f9990c3f89123878f1e3a5ce00347ca845279cb1ae0a882c992da08f71c52a9e97b8110e888010e2a982fc87ae7f37c9e4d680a869bcb39cbe5fbfff57148c1782a5d144b921405b3acdfa98aae13fff238a0608e35f266637184eaf413be2ecb34d3a2428a80c30906153c5fc576120d19b30047134c41e4d8e96a231c4cc8b66bc4f6d900070001c7124c224cd249869f580a3b03b6e8a2021c98010e25187c821e4bb1a372724a82e965eb5347a4d1d32624182e3f4cd0e9638fe308a68e54a1c5ddccf37c4630e58e7adad16911cc1b3f57beea57390ea3c70f1fefe58860fe52b74e229f48229c2ce09186609ef4313a2c7e3a216621184fce2e56cb780ecfee28467004c1e0a142944859d57ee5047000c1d849c79c7afc3c794d3f30986a096a42543a35221f98bb72c9baec0992e2722fc2d0512c03387a60b2129246b8456c33b50ec6c103f389a8901f3adf959079e8c0008e1d542efae6e72785b00e38f7b290dc51cc71e4c06cb9364c4ef75bbccb0e1c387854a7b41d2cc4f3d71b70b53a31be3ec7120de1b081b1d2848d55be0ec94635306ad898054fc133af36010e1a18ad63e51e71f679339d8529e88f0aab2df69d972c4c2d1b5143c3120bd3d7f8e47c3593e7312c8cef298f0e4b4a9db864dd8d57985c4c7b6b777abe4fc1584118c41526f39435b9238a1831b5c258bad278720b2bcc31226af7f785a8deab30840f3ad5dc9e3aa5bd2accbdeaf77261394f4f5261504a2ce47fcd7e031506b17bf1bfdbd3230c1d0f060f2fc0e091374e6178d50a4a9fea0a95435f84d1239da1294c225df6fde8f0a74b5b0a93d24fd5ed2df1fc4861589f9cd48e598594f928cc9e63a74b3a2b0a636435d36b9d6b62c44e6f84c2f8915b44493c0deb1214c6799399f4e629e4073f61b88c20cbf4c4bf46f784e1828cfd58a71521bc4e1826a75ccb69ae83b4d8f3e8d1c50542a0e3c70d4e1866d724e8a8a49a9ede514c47fff0e29d58ddd884218caa0933494fd09599e086260cb144a777eddb143f755118b8910993f8cff90ee9e2439460e00d4c1845df3fb897cac13b65fb3f78e48d4b184212bf70a245dfa2eae386256e54423119e154de51304771831267b83109839c10a2ba1735748a5fb821098376ca4b93f3c3a83b45c2607e49ac47560f772948984da6280df9a2e643b7479827966509ea624c1aeb86230c52949effe69e89943c4b08dc688439cd84c9fc66c4acfaec0837186188264748aabcb99aefd6224cf9b257dd24378cd3a81470e5f27024100883a16028100401f9760333130000000c1c93862281603424d3d57914800351342242362c1a24241a181c8b04e450281408034361002010088681a04028380c89423d581f08b7b70fc81758c58135b3282705863aa340060ed86a386b2893f4a0c6f92b65a817d65080a584375102743b807b371e39c4f015271f2731f2d9b59ed58c3cc5bc15678ef055c1156e3d4ebebcd52697f13738dd2198ff051d9986e8744b1e6e6bb78a94985a66fc1a2e18bc1003a23717d3fa4c48ea3fa882d3fb5977e0020cb4d71dc36e50e06b548bc8eaa19b007751625adad7be31f11afb075fbf2666a16baa284854fd56487427f631cd0afbda7d8c701a9cde7d4d12d3fb294033d12c9066f18fdcd425b95f14fe22db98b16fa83aa2a01173103128d0fcf766bc3fd7a9b8dc8b0153fb8ac36b06f4b5b260f31d84898940ccde320d8a22ad4dc1d3f59171db89f7f48a8d0bee48f66577e84eb0a87169181c727776c4ba3b31c1e53898b24f355955205f96218335279f5958974d9415cffa4c85772611d15ea6306ef21618e8de811343d1e0fef443684eae803775525c3acffc37b488081ebe3879f3b9fb3e03752c1831ebadbf62839d8bcb460823ac36846c593889cde92bbfe7ced58cd0854d099f124ad25e5fd5f67a0a7292fa1aa4c09c7be3e661dba3788b5b1517f8f0a3914f23951ceb8ede37a0bd6ade023f31f505b357db20c34907c15643a604744b01e41be5ca97484b1ab2c41622b9de9769af40bd846c5882e87ba894d8a4a9910bb4613a4c9b69c3449836d38689306da60d13617ab397937b41cf948b613a4c30138641bd28f2fdd7e47988a96101e79461e052edefbec058cb1843ee67a5ab33a74127eca0c66c8a567d7776676f764ac2540c5543dd46f487dc5ebb7c1d399d3e8062a6800e8b3f113bc0755b4166d1794a90b42ea468711264dd7bf068ef9a68a1950b292e137079c173592eabc8e5e5efc2289352ff1480fddc2e29e7b016eb27bce7afc5ebb43b033c635ed49f84a5b359e0525889d6f1388ab58a65808442bf630662d7c7b394a2c5d800659c87050b7c7f9d10325d2f62be21c313545a063a8fbbfb7227e6e4f6069033223398f99cabc42d79472f79360d2be794fe97c917dbd512948594579fd986189c068e0b3938789d523a754cb05ed41b69d327ef8e7fa28ef995080be86ab72f6187cb313b6b6d66f17553a525deccbad902206b38336e2b46446230a3a10704224153857d96f474e0c3963f95ab5f93bbf54be55f5d69e51672185bd53006be93cc089380410a1cde1a86272eaf2477e1f8d890778f10a578782684c347f4879d5c56bbc561706f3301524bbd3b9fdace8d4e83b168196e688487231379c95068125bb42854776cc5d2815941bfdb116388065ee46acd94d275940e044006d75e639badebda2743871ea934989d5278f464c3a5c810252077291c7a643c21eb6e97a1b699d4924dd38dbf317f829007154d19c93c8c62c5918942a604c180faf39a53e24364655d195f45364e16400910bc59192af5cbc406ea84b6b316417f8a2b8b3a32c6681c538d9aa1cc209b29a07451fc9eed4ae9229db0edfc095ca85808cbd46c95fffdaee69b404cdaabbed06c45b0449044fbcf2b77642e56bab19c917988c227701f9a6e358ab528e783fead300ea6bf7f47c58a23415d79c21c4145f07362671954dacd49068a1b908e7637ba03716f37b06c9c74ba4a1be3e813fc50db3c00d2568199bf5c80f2dbf025e497644032250559c62e251b039f2fc9719205d2799f143bc9eb9472e5483ffb1424522671936649ef53dd094f4d219ea5372eb2b57d54e3fc764a936a78f10aafc942f61593e18ee8a465755fa03e153c5760cfd4063916bb9cb13429fcdf60363d331e5b026e1b41a57c6f75904b0d076f845c08325e81cee82a918c25a1de979e05a9776d537a494c9fff3e3476cdc2da291198f0d46bd362d398e5161ae48156691f7a0a791f66148139e5704ac10f6d2f61396cd3477f071b9b5eb90a24d6cc0d925ac4f8573e4182e471ccb6b05962c21486c15fbf97ead018a0786cf39bd4dfe0d42be2749873d52d7bbc18fc25cb58478a10414a90c3f2ffc1b2b99497dc50adbf76f7c326862655d4138a413b46475ba3f4bf9748c30c966385ff1655374cd5c665cce3ea0569c47cb7876e3656e29ad72fd62fe9a962aa2cd96edb3d10b20b065f91b39c0ccd860decdc0154b9dac7738d10019cfe1178c8d1121f92e4362f04fe4ae7ed92da2b5348973005b53ed8d4585ba33e39c8102e66370d5806dc79efa8e92fb4062e3bf0a0f58b4d9e182a6073138253f3e5f68d2c0927d4bd9dd9a09ad9b53ef2f0ab828d99e4b7b3b42af18b6d94bc1fec18a5a7eec31889907a2b8faf0552e54cbe393cd351a227286a822643cc9b8247df7169be8df7c849fb4e626d384a48aac6cefd51645524048a5a7c11062ce504331bb60284f15c85ed961458c6288da590165e055ba0dbe6481c69e852ad661070d4fa5bcacffe9250d0e65b76573f0f8af7e93cc29e836653f78a379449c98b72c28ae4d0b53d19b645fb9d383f2a65bb8980f521230137712cf37144cd689e51dad02f62ba947be1680fd0dcca8ce32e944cb7b12ea19f8b1167f6beba8f04614b85e61d280b95cdf1bf2d6956b08d0bda8dba2af95dedf074251518253c731b2da956103c402331f118f1ce90e47cfbdc27a9fb087e02798add7f531344624940fee1a3f702aa2569ffc37219ef14d2271834582f788830ed880d62efa19b32550c65aaaec0b0ac84a462d51df536db5d459101a61caf7f1ce0e3669144a911caf8ddaecbdbce119b6641edf13a2ba0fe6147a113d387d3919a5eaf5d8d6bd9c1067bfbcecff58b396a9c04a0e899b19fb0a8f45b6855c640d3bb6a8bf7b596d84e6e9404a1724e9dd1aaec8de0b9ee5ade3239b17b7ec884e3b44e641604116670860a15904a09561d417d201da9b6b203e603f08135e32917cec702ebbcf56a482e26f364e7f988f187657945b5a62d4f7210cc2737f4f627f786858ac6620e2a3172d3dadeb78b91a6f773f824d6a627289660e5318cd3e654cdca59cfd7292e8fd337488fa02e27a92706e0e191d29b0a18cf0fd6b03a65288cec900a99ea757dabd5ff6d500f042e61e24d70d70351c45db456665a2001f2bd5c669fbfca8e7ba51387f640a031be6ffcecd92202000ced42a6a42b54ffbb31a3b0854a38830f98f7732662ce156a22d5ab209e7e4725135ef2a230ccec67549eb39e3d72a0887e4aee706f6604075ec728e763d39b6fd5432bf0c0c341bbc461b80ef11dda0e6aed5080126d840b604d1a53699d316883f270876d1019416a7f0702f649cb02e87a388bd7f6a6a60ba696de953d7bc88aadf8135a6573af0caec0f8c9d403d13cd5258a826809cb89734bb178ea6c99e27892ee3f9b7f4a1e5f41eb6a917b48ffae7c0ade08c42e279ead615996ec60d7911d04f6a6e26a838839ce595b698acf1096f113edb5c68c57976a07abab18ef5ae550081eae2d7a68edd2c062b31396e37d550021bbf97da1e89e855c925fba16fa22fb8795b96adc239c78071414296a7ec353daa5fd3f3a0ab395867a21a01918362915f7c0ec9f2f68963dc6a045767913e2a6d42bcd2218bb9a1c71fc39aa0d9faa530ccb753094bd4bfb8be1659be4f7553b6602cb298494e00e31750810edbb4b12a9a16ab72e6df2a9ce66165496041620c12509c98f9923965b50f475f6d4a4402e882750fc21ac607a5e0465e8f7e8746bd3641d437cf0c703c5753f512284d2d8441729e6aec2838e0bd8f4030b71c8da24b063edee9136563c703dd439b12c04e0c9d436d39a572bbeb1e2ac4e2cf3a4ae2e5635015151aff3fda15081dc6f6b9cb3365c6710ad0014319008b70319ce867484c73b7f222921f08d3f39eb9c2e443b5c146abdd64b614904da6facf6d0c5006b0b733ad0e5f0099af38000f29bd9300648744a2ce12d2a69602067d31162db8005737a96812b4b46406e739a78459a38d7ad7b53a1814554c7c76220e7fbd1b3f7d1d70bc229f708972a4b016d485936fe686e82629b21b0863b869a4ff60bc07dbf2c247b66819667720d12b73d721ece0a566e18e6c4ed560f490d83211afd3db50e1ec33a25fb1c3eef6d792401f2c9971b2d03e90b3b35817a86151a259839b499fc01e120048ee3a108b55a7b730bf3ab2620640a182d7be68d644b9cf92d4ebe57b66c961deb7e87a2691aebd6cfd714e52cf1cd1aa18b7795269e96c08f810e99d10483756088e57801879e9006b96355c23c64dca5b04b31de7024d0b3d8e8784ae1be1fb6415834fab0c06add4836f362f90697e29d181a62c028e2c38892111a8d48b581fb3f4b8c77615df74a2070aafd7fb2e4b04d5c51eda8df036d1f589e436ba38c46b70c0a4b622bae3452693e12550d9d0f75ccd41c5c2b641d638924e88d8f0a7f1cd4db34522a4515f0141457005469a500fe94317634248ea8510fca08ad800cb62e15de32fe001d40a1910ea4827a0ac8ab14150051c6a8dc672e1e4328179d5eb4e4628591bf3cea350d988b4c7065b235a34f26c5851ba7442b9bb83c450fb01850bc573c56cc2e3e15ef29a6164f8ab72cf2e2c2c099866942cd1bd26025b3f336c58d071aea324d3a1f8fa9e6016d7908a22e6838f6203abfaed45826d4f88fd613c42782917923b091f72c4be0e073475d6a47cdb149ae0c2f38d7ebdcb84e0763b2af3b41630224000920388077003bb07d009ac8d0c2b60881527afb585188777c60d8583e04d23fd7006d1e5f73853f2730896fff2a54982c04a09209910a1cca3fe8314dfb004854d3063136786ce4d84062c38b8d1e1b48c1cd088ef9ca908cc9998273e437f3e0c0f4e605e1cceadde33c971a116bc655961b921e278e836abe0a7b79c65889d810cbb09f106ccb73b32af462d18de29dd6491b8c2ea342bea3e45aa64842ffeff3f600532faf6251124bab4cbc5b00f81068c60f5247aa682d932469e4b4cebf8adbdb5d3088b41bf2aaebc92ebe959be7dac77213d342b6c908084604013a648518ab5554ef7d74a8ce350899176ed7a1c677632452232e80171e0b8439b8e4bd15ab9e0eb208b94a72d6a78a41b7b0f3cd2132b6ebc22c12e1c39e2a4c0009db761abe5a577f001f8894695470a1171010f8925c8f22481950bf1bd009b02e06a4c9d5f3c32eb9502d92baa55aa4364013a4b21b32a560ecb20d08df8f084f38abe3c0ff0a3e8ea1bca49f16d624704fa1ee828f4be8a9154c2c1e5214a2da9d96d39d2844ed1ff4b25a2a44662aac56ab6381a71079f0ba7731249796c150daebcc9accf10ce74a38345ecaed764f5c8cf6d2f76cc5c48108707e819eb72cc06dd7272e97ee49aba3fdebbaf8bf6e2522a436194a157aba759052d1097e36f36d3c8c0e5e9271ba44382bf195b1184f4dc83ee1d444a898b5a81db0baf730c61e10c6cbb131ffd1c9068bcd6dffd7a060919718b291164ee3c61758f461ae22c33b7b7d97e0cea2d1c6cbee4ddcd3ee29dec1de21de69ef20de61f714f760f740fcefd2a8c14af6b467cb5b9467c9cff20f1724d6b7c5be43eb0e3c50ccf855fce68da08fb73eb579bbbc0e79b93c86bcbabc87bc77e95bc8c519a488411ffb4d407150330c1a9e0a0ac067448507c300bb9ad6bc833d4cd19056ef3d167eec4d7ce340bcc60e20c65f289dd83b05294c7081b671d1c69bd68046b9871c49286c4c31ed608358e9228d45d30e40ae39b058f57d356a81e7a35dfe1b0b34e66f8cdb4a0934557c5349730f6b4debf8b97de46bd74c44e0efaaf2303752058c4d822ac67d0d88ebfd6510e33f22c1f199ae5e26931f5675fdd2b54fc782aee78b209340e872c9bfd2c73156c6a688bebed46ff4b7516829b18c8ccc88e4ff591d605f6edd0ee4bbf13caa4d515715fe3e62e6cca4145c2a82ee10fef877858f93c6cb003f54d24324233be41d1cce96bfc466e3828704e6180452c1f6e552a2238eb5cf5509ab64e6b4cfdf45a91ad23f8c9a69106ed3a8414cb31a0e8b23fd6a73a7e89df6341ff790d68efa5f39cb1706f3acf14eace5223d53bc12ece844857164653600d2afc9ad929897d2572fbe67a760f12f7aa6c578937e0e94cb82fd4e5abcdea2ddc6dfff741877be1f892b4dcc30245d8ebfe00618f265f5491e201902a466e7da46b1ee0f07db43c931057e0451d3f4cd2686aa5a6e23a4f71df71c0b433b183ca1ab6696ba2f21384fd4509d00cb81a0d0b595926c8e86536687b2eb7761aec4fd2ab688bd94b10c92b66c174b7f54123148b19451c0626d02efbd639e3332644d73ae24fc90917786c4415b4c8112d1cb61105dd2495240f252b31c789dd1c68c64111ba93cb69b5beda22739d3a2087713945b4e443d6759c3cc44edce6f23f6c11922e521259824587bed3cb8732305455684ba885c40e200fca5d07cdf235424bd3e12f52b22669ffacf4973e3d45ef7f4ed069ebeb66ca7af92d16d7f1075e6597b702aede23d48f3a02631b3e9aaa728031dfab78f08eebce9806545143c7dc016e4955d9269043450e6e451d69de0a0e558a80b88268e0c7a574550244005a9e41ae6d45f66a9ac18395daff6ea4d7794b6fcabcebd5a952e149be48f2b455e1b8a61da8482ba26ab512ed9aed530c71c731d83bfe38e50047c7bdcd9070b23f98d91507ae6853e86faf1222dd6877248a7fe802cbd694589f27ea8d841fb7e92f3d79a0785c9e09fd4626c8fc50d7583e1b6ece9d24c1a3f61f1e935f8c41e171680c6ee7f0a6baa15adec4b5b26a112f480ab18419450de31919d715fc50682e053dc1d16a741be38cb1fb24e2291e04cb1801348b76930d8bbd7b05dd548d9104177aedad52682191651bae93e45149600d25d171255a053c5244ea416455277fa9865d93544394ba38dcd7d49ddd748e76a1a3e9bae39f29d8231dd77c03b415cbba98a65c086ab615ada30aaa85de95992e9017f13f9c400f8d96ea5a85eae673269d72eca720cb553757ff6fce4c77e7fd731f68c00f1560598b3f1b1864932ebdf18c5fbd032f19c5e64785ef4181f8fdef15267de47e132038417e7790f860d5e216b4248b254f38bf2a18718f9ea753b823d22c29e0b51ade674e26b8a9ef09d5cd424a31f05753a00d132aa4ec7c56fe93f423f7f08c70ebafe928ed91b687f89352f65c1bcbd3d5d235e427b608ed8892b1ee840f1a8a5e56de32dc4dc83e4ab0c493a3ff579d139ae7b3746c956f31a8416002c5be8ef33426daa828a2d2ae3c14d497237d7c166201d3c7976808f3f98b04703a2a76116380170912179d36d16575cc02064f702289c2bcf46e69fe7def16fdb4efe095f9ef93c7583d20d7f7da41a92aadaaa0bc9e0fc6bbadc30e170d588222a52629af2c4603f5bdbd138cfd761076970217849d44d47a2995011798c04444bd0316f0f7c364d20b164814a73311a63febc58f98eeb2f0c311ea12c1083b50128750547f481119f730f39e668a0b72cd573c781b3fc0f34ce65c9e315a9a155205f6d5d02fce391b912d3568b1a4a54172edde31602b4e93e24c1e5599ff44813e1712ae30222e3d087fe69f3a3d2bc71b312fbc6666cf3f671df62a4bcfbc43e3fc2a5a6881d4f021894d07dadf9d550686ac83b5f4a4f88775f2633f4c5ee06f77192d251f955c25567bbf481fc20295db7c53b81ade196417da6b8b885933afedcf80933dbfefa78d5b227f7771a402e920cb480757aace4c8c316161edf12973a64124f354f385e8372d1a71f582f867c93d1491c7a239f7b155c88877588ce3b4f13f6487e91c154c2aa2224f3ff0e18739d06d71acf66f477acb06326fe7192c296ba3e46b8645043bb680696a482783ec9d1e85cf8e648c3e55cacf7d017f866dcd72dfa2066ff18d0d36f229481905937cc9dbfa2b1d371d1e839c09654e76423956f5baa8f81e7e29c90ae91f6d04d05167fd09c6e2efdff8c3cd550eae82ce2549154c7f987a9f927cc5ef1f854df272403faec9b72445bd2a5221464ac3bf35907c95b5cab2dad0112862459fa285658102b2ceb8ea8cfac5b22060763a5dcfaca3a580514e7a83721917ad377d539ca343727c195c22c318f9fd9b332b77cfb4aad9e5d4be722268bfa244fe053ae57f8a0e78583d842334ed1f928e7fb5525017b721eef739c0ca03ca5ac2de0ad976460f755d1d88289dd890c89cf40a53917ba4922878d5b82a4b7b6ada27b34d0126bd6ba52ed4747aa6a2e6731dd0a97871e19afea234f98c5367e62eb04a7e7b7358c1fc12f66b7935300fce2d6568a3427d419f005f7486a80b4317f6983ae06887940ba86a00f261ec75fc42111e893406879f488e0847f784b09e9f9b3ff53504faaa98c005660f19374086a711f0b931f07899d626bb6f619908e71800239441a6df60bd3aa93599d2a0a682d48c20ec5b5b82f7131ec452e286b0ace3bf714fec76ca5e09c6bcfe28180c69de849bb886add5b533a1717d1b5ad83b55564d4f2383d68d8761e331b004f52b036e1774b864a56eb442e951f304a926c4c602d9620528a5ffbdad8c403c195440794244999377889018a81a50e980ade2180bfcb63a69de986c0d0f10345053effe0505e8ea7fec9353cf919ce4aa72cb021a743809bbe6de7487cb59f5cfa027ec0ea28ebcaf2add389c5b7820c193950edceeff460ef6869f94025bd063f274cc40ad981010998ca1f8e2a5614c0e04b09950a80e3bd5bc0e1d49eba8695ead0b904aba228365c579dea7713b65d034e0dd7af18ecceb7aa850bf2e8bccf63fc3fe77102d5113e8ca4c58749c4b9eb4052b3b3bdc036750091811d4b48af5733af4588a2d6ce1a5bcf43a6f11c00448f862f0b49ce1cb7fa36a0d17e2a6857ef6b20e4d75790b3cdc0ecf3bb59c90ff45f4e5ba5c8c4e86a9c1312f839826ceb204615a0d33fdf6baa20e7e0e6b0b2ce467eaee144d13e643adee29aa68c032d370d17f864c2645af02531b05a2fe40e233e4fcfda68fed9e4f0e56b8632b6b5b098646f2e5f1824ecf0398988daaef1a2a609f5d29b3b4c9cc8a4431c22474fb8ed5a808f2b8a0377686ed3f03901ffbcef9d61861b1859b2ae41654083b0321928a8a509021e36ee9d2bc665dfc5318725bb083c26737b65307e81cba5df0f8224d90af1b1126962ac0cc3e57d23fc2fd34071baf4479006e3cd8a6796bb584da9f42aec227ec9a5fb76034c8e52fcd1574417324075c7d53daa30ed1f8c5f537599bfbd17e0e2b4e7a71ff88d198389878d4f07a1f84d1589419fa640748ab08b7dbbcf2b243e56d7d58d2a5b9d57d7140daf57030fef553d1d63da74bdf166f90abd5576c6a09217d8f44c895a62cce950d56259d89678ec4803a5d6673ad940bacf043335e54680fc855e79e9cb9bf78a3fefb9c63e2ffb3121d9fb2d16bf29c61454d159ec16900eac4effe13b36d8c23f493a96062c2cb2fb8986f0a1ca574caa616a791b2370d1f5d75759a3d5a7cc40969f0f210492f9e2842a5f664bb128d80147148d4f2a767c51519d00bcd34a00062a61faa83795bc43a81c75abc7d7c810a60f0924c3ba74fccf9d46d7868cbc4bc9eaf3da34bbe3f55e044cef8d2dd0a4396e2c87c061c3cc250505dbeb46c3cdcd8bf98e35cd8e4313b137334fdcdb7b6975a2f14b24b023ec79f7665666580d4db9dd1191e3f47bdeba1db60ea75f932de36ae530cdbc2e5bceef6d1e96d1efcea71db488610f193b0f6dd3533b51bd418d03106ced2808c2b4661949c0e9ef70a30e9edf21c23d147d1a0ea8079bdb45ce3adaee4ec50c485e1a758cd071341754413c033012cd6807e30ad1b87d7a059b41651b885cdb6e3b53915c2197fa1db180394176931aa9f6a61e8d528c0c96676c35eb1d13e6cdfbd5044d5246d877e375ebb51a6985133042cc77c6fc8ca43312cfb86f18c08812e312c63d30e262fcf4209011b7a4b686720cf62808168e2131b687d19b088db03f86d494bd8ca796bf1bf34831ec5cb40485198dc660aede12632731743f09b858f28b93fafebca55d49f91c078465af8de934d55c3e9b86285b56372d223057bec6ac6c779f24b2b7ff1ee33ddb86c29c87f99bac4eb7028a8c0c282197c774119cf075ada40460915dff68452160685189ec451c31d06930d36042031e0d761a8c6880913dd8273ad915aa31c0dd00be81f20fe0d59b999b18eba465556c7190b8ab4d437cea05929a35f3b365089b5700c4e3e2834d3d8b40e0a0baa33b070f805625e8e9810070019405b008e01f4c0969e18574d3257d0ac09e0cc0d8439ef1c86a2e82d8da63a333b03852e269c351469b3b69e05eac4a809c5a440d55f80c54e0f217c00272c0e07b0de79fc631f82af20e3cc06b06e32e032077c4288d9c224a7d3ba9cc11fd6d090cd54b55039933e361c74e3f9a0e5b046574b3ee1cdb79c1eda8b7832d69710034e1bd1195f19ccf7986f6653b14c842902d1912b74c406cdc0bd780c1f386d3c373b195f43f079a6901870d489bf45f3d55408fd4401bc7fb07dc82def789625310d177ee5f444291000d7d4be4bb362151c044be8b8dbfa39220684a73282fe515921e9b7615ae6546998a511f8c9eadf8303e39c85842e2c4d7e89c0b516ea299626f20ce1a310c590cbd38b3719f22c9238e69d61b37bd5d2243010dbf6d83833af14e22dc2854df03c7db40a3ab16041df31bb285295802c7acaa43f9c6d43974d228859a529d86482895cca17dc44e29c9a904d1994354b1ef5891d3e8fd05dd257f14c36b891ea2f17102d19ef3c3a228f3d16ef2b2210821101d464515752a78fee5a2cc5e911a50a1b49027dbc1e6d5c010402f1bf51114240d5807cd5c270b3f7e7391d2036d95facab5ffe210a70df47df16059c086829053609639658427bc44c4eec540b93e862329ccdff2fdc15ba86e060a193b38ec60a0a9e25d15f341e9c1f3ef7ea564b72c05ed25b7409b87b772634a60ca1b91a7b4f455e5cc777a80239f5cd00168c22294de18162707d83ab7063c7c09632c71e18852bde58a13b20299d982b2299cf468dcd2f36db7878fb3a70c65b27eabd42dbd484c4a8f7827b8d750a8e76eb70fb7eef467b56d6b3e115bb3dc01f4f3b91bff7eef719805502bc1d460e205470cdeea3e1c0889fbed273205ac5ff36640b46a92f660abf9cbaf3ae6d9d0b74c767d59f838ac2f64e375af382502ea61f4bca301da45a1bf11198ca1670010bd58d12760ba8e35310b674804b999c068ed9f8de5bc088dc96d059243721215feb3aa42d529a6420c25d09c50754993d0964680044981bd7b3bc9c4aa67985ed553cc9ad1137a5107dbc3f306143e4025d931e67053647cae58c799000b756f861f50c84e351ddbc3568e69f452286d4d37303e78a3c5ba9e60d6c5adaea8c8b2d70ac694788041046a77d56592532dd37b15067e9212672391bc4f92619121e89d69d2c88c5f4b6f26109afaa59f39b6d0309f91b9dc14b0a3822159ec2e36724852c8ae8b6292f0f3aa1a4896c4efe8800ec25c1a50f94087a6444a7ddb0e1abb38ac097867b9ceb5e0986099a654480af65b1d76be26e7a3ac00125e451258ca8ba6f6d46cad899563ed8f5a9c8aa110eb18a468e4c69a6175fbc28ede76b2dcf857fd54d9af2997ded07f430a2c5b9d0f653811b39a157fe55e52f2229459751850b1eee4e54799f4c2b85c520b9b1201b3d5852b955a7a297625c5bbfe94fa91294ae78f01d2a417d4f0594c7fb7ced311e53d504c16886dcf1186d10b84d03b9236b9595ebe2eb764381cbeb32a94ff18b0a95ab252c085635f1c261f6c65a1c697f139920da690d2d11c679944ff96a0755e4d28425fbcce679e19b27a614d706c57e55261565b129d586b2a93bf26c8f16193580a82b926c4d16610d47fb6553bdac4821231d9c84edc5e2e83e4716d28e0746a2d0d3a42acf4268801d3fef597dd2e7e10635447790a6e39e7cedc7305cf7a87042b04adb73b1f376bcc665d3b71295fe4ec7419b406e6f3987e167bf3615a06d81fdcbcb07087c0fe430452e2e4116144e15e1667f0825909e7e622cc6f20536c64201d72f81783d970ba88833703e4e65d8ea00e8849d8e7a21edd01c51a8ef6b6bc849823d979ffb7eba7d2c0fecd0c1a7c50ff9b937ad33b369a0ac01f6d76faac550d422ed5a75bf53492db0e052d89f28fa9f4c8a022a30ab5d95e3120d118ca84e4bc1c6387f00de5260f1cef737e428e335cda48405322f4c4b138dfbde13d52ef1ec7087e91c0b8567687466b2a74c01b97881edda7800efaf6ea82c9068776f4c460c59403516707cfdc6fe2a8c0944720d0ffe4b71e9851c05de768ccdfcea4256268752a0c45c60ef3300a8fefdd0b042e63ec2d39ffbb66ccfc2738fdbd7d3b1411eb379fc27dc056de8e69e405fa40139dae67a8d3fb31d667cc96edd3e47ae1d3a0474f99a83f3fb032adf1ba848a340abb12482f42a7f47ca1518893351cd42af4c190ad013baff163403e7ac99f4b46febb6620d311d63b39a77e47039ad26a444a65331129376e1ef824b572815c88db172b48594318f2e1d2d8a80e5368e2a3f5c2c7677befa3bdf7817bef2306ed1b16282455c9362b932047b454d4ac205051d092144dd97c17e8a459d25371df5c3a130ea20f7befe37befe3267bd123ae32f34872bb21699474043af418bc6b682b38582d04163aeb8205c298e85cba53868b14e2007640566a852c2520b49d7128cc7e25cc1ea26c0fa328d55673d954e1f5cef97343633c2853d5d56eb415bf298cc74d8a71e739e85775a6965417307838f484158f216778f6e0519fc716937e6246e5ea1fc83c1e679e4de01cb6d07563234b049a87630acdfa82306fa612fe2e82c91a36464314b17edacabeb1d3f0cb468ca0ec1a3a99fe85483e93e1b9121d4e1e07e9a3a96268a50841b3868fc5ddefba34cbcd795e6f96b48860c06d2ca597e1f54ca88fedcad2ea2583a65f2610b1e44bb7f3316ea3fc618ad5737889f83877acedec327a70f23bd0be1d20e8727b2993d2ea1877447661983e351c78ab160e2e9fbac080751f20531a13ef5a77c26bf6f7728b3145af0be0b3954ab495558cfcf35b788c91de647072ccddf00fb4828cf8938069ab2a58634d6587f67a42acd55e72c8b2fc5f2749493dd41e708360a990bb5d17e30edc094e3d946138978ab7728171c37bf33114c13a3f64e6cc84d6c0291783842e9bf9ebc606e3b24efc0dc7387e773d28ee78fc33176fb1ae6548ba3946d678ce3f9f3bc47285e457b4eb0cf85a15f48b74e60ad7c66b2a72a46ccde6fdfc95bcb61b6b971bb3b5bb093730ffae58c42057a3775b5816e89b38b0c2d29c81f05e9abe742536dc4bb3844e9ec9f2378b9d75a6b7f873822046d3e0a06618ea7b8ad248cbb24e3ba551e04d66b01f1d43a6562dd56c02bc34030ebf85a60e3750b0a6d001f3ffffffffffffffffff47f1ade96f7f0056c924a5c5fd1ffa2b0029a594524ae9762b8e6893df08b10da18be0300a0000040a0fa70e550e18f285c2ba291dccd5e4b879f542f9ecf4c7bcaf5d28da85680dea36215c389b8a67dc13d3ce8e902d781fb6edd5aa7b339ed3f8c90d1e0fd142a964def8a671464148164a826ea58979eee42c1d2c14e7e64b14fbf50aa8ee6e5ca9697d9652cb343a85f76b562808b92e71f1662132e880902a14a4a5baa6c6895d5d53a19849e878b9930b9942a963dc5bd3134d947c43a45072bd90a35cd7843f39240ae51226be4d92a476241f70258440a19873d5b2d96f43ab6703a44302366cdc0c6cd8b827944773a6ea488fa36fe284a2fb86079de6e9f3498734e152f26ede67d9dceb32a19c318a264169ad8edb7ac8125a0db1dab7366fd79b1dd1739b5bf7e5d414a284929f3215e77148120a7b3245b997a431a92904096533cfde203c8edd7c2ae408e5901dd4a726732dd91e3cbe47b281102394424bda0a9bf50b214528f588d868388fa274884884e2c924add793d98de86e081942e95b5e6f2fc47721422897cea1429ed8caeb3f2408e520ccebe52c0d0142a94afe4c92e9eb0615427e504c722a93e4f9ca0725a53cb989a9b3213df03b63899f9418da40080fca1e7359968ca14f12ef5d942abbe42b6b932e8abb6719c5fc04928b82970ca17f456bc346144070516a3d9daa84fbdc373d0f122c14406e51701d3731ec96882d1dc416653ff1592147cc66fd6b51923ac589fa163b4a92a245d1534613ea5b4f8ec921b06103cbc40083470e9b45e9e4909f4d8cb9df4f32304064510e723fc632d5d4396c5f80c4a2b4714f7a8ed5c9ad375c0310589424d9d94f70fd58faa1b601c82b0a9f749a92417bfb08d90413a001882b2cd53cd31a3d6b3579d7bced20329e282b6945f1565de64f724158510cbff1a4dbceaf75ceaea21c62156e73a27dfbaa8a62ca1e799d595af79352519c3b319729cefea38f8ab22969a38ccea473f29ca2a4254d7c94a09a426b539464ae2b49ce26bf34a61425e9fc63a598291dc32545a93609fa325d294136378a629b123ca88928ca99573c6d3e25cd420b453964dc09f5be4151124ddd2441ce05f944e9f485ee8cee9b4f48413c5132cfb25217e2d32771904e94baf44657bdd40a209c28f58be89b58ddca1804b209d73ce7e76f126ba25892c6d5ff7ff234d24c94534db077abde24d98f89721235427d929f49e8c9258ab946a98c089d392b412c5190e11a4bdea464d896412a51fa741b736b9b64924ca24449d47f6478694c53279330e6203f072d9e8148a29cf33ac6738f41880f412251b8ef3cb325b767ff41a2361d359d15d74dd615dee93d8f28e66caef7f8f68b31787c086200e2889269cc7df19eab4ee6401a51f25b13d336616412178411c598948af8f2305f26c4c1f6618cb1438c1b3a7adcc8db0106182806183c72802ca224bdc9ea993c8dce25c90f401451fceb18f4c9319d64250ae249c2adad9b3e8111338020e2400e51d29884785042ac079364889224658af8f638e1fd17a2e466bbff27b8e7204584286c0ad9d1eb49bdd767102551ae6bd654499579238824f97f563587924014db3d68932696085513401434469efa4e3a747fff433936c7947221f4c4c6fd50ecd01244092bababf13e94ba35d5e9959d0fa5eb4bb7906be739d67b28090fba4a122544e8af87a2eea5e7d86775a5da3c9474a7953153ff92753c942d37452d467e7ffa0ee54d82b87a7d7a994ded502a0d32af4990ef92ae43f1bc53644e8c8e33231dca3909ff499293b9ff9f43d97af73af48fa94f727228c7cd2ac2456de5e9c4a1bcc14cff9c87294f1b1c4af531463553ba9adf027943e13c68c9bb2e7143319d55a62e39c7d81d036943b93aa99dfddaf89f1e840d057f2bcf490efd05c917156802c81a8a592c3d6a668eb372a2041035a04dce3c0de590b1bf494c9d2b008286926819d3a67a28ddd0d1e3460cc2c8b103070fb43394be33c4e67ecdc778ef3043694dca283b9556a3fb02078f1b670f002943494d6d637b948ca59440c8508c9f9f353f690c85cf4edab77c34f5e741c4d0cadc9c6d8c9d98a7e6de07b912d39f746908240ca5cc6b3a2641a627256e2060285d8872bdef2056cc7500c8174a4a7b434e5e7b26f104e28592a44d348672afb8f15c178a9ba3688d9d45933afd0e8b000817caa32663ce07690d72a064c70d1d3dccb6504c627fdcf8d1938cf2150640b45012dedb4295242655b537783c8f1c34e891c39c0640b2509ad139569ef425d5a938d87ae478410e74038cdf71630639105a4f00040b850ff6a9f539a2d6c2910387086cd8e891c3f4483970f0d024805ca1a44a480f42a598d4b9e2604b2c006285e26d95245f294adaf08b31c048088054a1a4ad4c9abf49ca7e33068fa60008151aaddab5fbcebc13b939675982124f0246a200640aa5d6cf2fdf5ef2694338d8d23a002285c2fdaba6cc6db6111a3386088e00128572e65162c7cb14f2da0485823ac9b38eea5b2899e500f28492c9da2de2da24493e0d01c4092525e7c4dbb4cfc17d838117e490c0ba01a409a5979fd19fe2395dc82e006142e9f3c870d206b1f1b2b384d2b686b470dff660a21943046673005142494993773c8992994194600e12bc2087046cd8b06103a108748f30728cf1821c86c708a30c240905b1719bc49beb8ba4190041424984f08a2b0f95a2a4e30b043942f1bd3406d313aa5de34918cf3a36b001102394d5b53bda35fa99592d269736993ad2cafc17bc40c706c4d0d1011b365ea0e38b1b2045286c68d21127e6868e01840845519fcc44dbd30390211436276d7b72d472b9138810ccd05b6f9bf37a71b13413dff93baca63001094249c8ddb4e993303752250308108a6a266d7c730f269e9c0901e40705933489f2ee52d65e82f8a0d4a6f4e8786ccca618417a5012a375d8ff9282f0a0dcc13be36e5ee9e9fdb18b925b9e6d65ffa18b9298a9eccbc3780c2a03c60c3e031fb928c6987e46c70ee2e22ebd6ad9181bd7dd52d66edea26cfbab2796b0419f5cb6286f762ea53b46f7f346161fb528ed68ee9a67f3831625b7efbf52425b66db5914535f5dc74e1a5970719bd65eab3657829fd858bb5f365f2cca9ad684ad0e9f3c27592b7cc0a29c693586fb7197d7150c30f015251556927e6e2dadcd7145399cecaeae2353c4957cb4a268b23adf9d374f3de7831525d571cafc3adec8df57e1cd9c788e788596ae5f4d3f934bc3e6545136b5e9b1c7c354146746881633ffdae0a3a2184c389dd504ed76f54e51923663bb9a8acb4d51aacda9d6744c55bab3521434a61293e476f2418ab26cd0294ac3fc07d3f918454986dee898a32f8ab2b98712835bad6ac784a2bc9983ca3ddd31cc536b840f509443a3e6a9f50869e2ff8f4f9464cd248ebaae8c9ec431f061e4c071120e7c78a27cc28f3a615589bb1525892da10c3e386118cb98943e36617aced92e0f3d9eec137c68e22313eabaa7baa5e8bd9b9a6b8589e276c9312e3ea70b21bc4479848e96db57d90d5284014b94af4b274fa25d89a268bd779697b2132aefe08312e50faefdaf1e3489b2a78d10ea9d0487f0218992ac396fca9884ce50e291c034cb877afcea84f00189a2acac9ab07c55cbefe311a50c32fc9309f3b80d5dc093f0e188629dcc303531757ecee94723ca25bcbeeb956e9e9c4a227c30a224b4c71b919e3ae9dac0f858049bbea4d7ca58c2c1c625f85044f1f4e64dd6b81f63b63f12518e613a69fd10228a4927594c07137f5e458728bfcbb896949ab6fe3344f926baf799a3aaab27ca4e89a2b3a45ccd59218ac9ba4b4ddeb365cc0891ea3cbbd751ff4194db94d43949b24deab73b081f8228698f49135f6381289f14a6e4d3082920ca9d4e38616227211bd2c237ec0f859feb4c52f69590b9eee8c1e3868e1eae63041f7e38c69c69c4d998863ffab057d6b779dc76851ef5a6a4dffc8d71c1071fca65caf3e79ff551b2c4b60329b891828f3d944349597222329987a78772bbe9242eabd4b3b323f8c84379941894fa1e9d4c4bac1f782898382ee2dd3cd6295376e0e30e85f3d5f63137b1830a6f470f313eec60363eea50d0edd68e231f5a63121d0a1bc3af9dcea48f39146e54c5095b99ed6ef1061f7228c66d57fdcc9ca34dcc39f88843c1c398f8a62b44c913eae0030ee59113cee424264d9d441b7cbca1d879c73d3ca87ccbe986b9a19c338892d7a89b3d43376c061f6d289fa02fe54a9cac2579dcf9c186d2976dd0f3214dc820e5fe5843a9c45f919f932c39e6440d257fffdc8e9b6a3b7e692897f253a63fa9375d521f6858d5e733f735f771867210328e30a554745eb6810f33acf91ae3e2dda235272b63b247bde893e48bfb518662887c092595b57a4cf24186c288ae92a6459d6cfafb1843e15c3fd697c66d097a3114a407a149d3bdf5aa260cc5370f934c64fa1c4ffc030ca52ed9aafafc2f944bb0bb1c0d93369e245e289a1cdf2676fe93d7f42e942e4ff89313c7ee4c920b05aff5f992cadb4249cc242711791e9f4bfdd0424168e5e7f0ec319e587e64a1a4292e565efa1d4ccfe3030be55d13df4c30a5db4fae50d2f9f1d53d79ecbe93154a523c6fc8ae3959cf5485b2c7664fb25f27494e3e154a9a5d731c2b730a650d7a4c7efa662b49480aa50bf5b318138a42614dfad9ced653531b2894da763ed4b4ca86669e50921ab6c409c51ecf1d5ee27426a9a4090551d2f4e89edb948d33a1b4497aaa2fe92ea1b89eeb7c6cf3e624490945bb8ea1e25a4a8d0c26a1dcd9fadf73fab59322a118aece4d6fffb4537784f2569b7c6e95b37b628472f56c49e2f676fe664528a7bacec963dabca1a428f04184057c0c41e443083c403e82c0e3e3030808f8f8018f3f390ef0e103057cf460e48307df23e9c163868c5d7cc8d0452ebc90818b1e3a74ec28808c5bdc0891618b92510b1d298c1c3768710019b390450264c4c2062330800c58e41023027fc20891f10a2f64b8e2060d19adb891438c089880860c56dc58808c55f050050f03c848c5870c54dc0091710a1e339021c31420324ac18100c8200501648c42003244010019a118800c50dcc8214604921d63448000323e517a514a68519b17a73c1c6ced8992a9a78f888fe260e3c13b90d18992929edf7e554e906914030c1e18e831868e1030278aa1f4c95b1ddb1ac8d8c4fa2547d1bb1d199a28e7573f5332b33157b4410d74ecf8808c4c14f49c9c756e4d8acb590732305110a7524b5d5e21b061e3ec907109b321c3122da312861e9041890cc898045a7ead31a79090218994796e8d9d993c8f83ad022118c148144e494a9d5019838e3941a2d839a86f8db974101d96f1887229496a964bd911c57fb5ce124fd291ee69847632f68695d32774198c287586f4305da98c4514b544b46bcf77f2e88d20b609137436f9ffc34a4994c4c45092b0d965645224ca25322793bd498dfa215190c9aa379de811a5d4a035346f0c1f77845e2f56629e06bd68444928531d53dc33e912f48211256973447eca17511a934d0a0fb522ca1b3cb6778e3111e5bdd499f730eaa24544144ccc15d2c4d337490e0f51b4fcf81d72efce33344441862e9331194a77868528d568d125e9587234434214d3cb65a9124cacbe7010454db2f5a1fbeae14241e0b14d127efd1488924cb28c925eb43d9e0051d2244e8b6913db38fda118fe46e73cebd969f24341f4878ecc9a44d37d1f4a276be79c3aa567233e94647c500d1ef388f4eca1a0847defd7cd6bdad143a93a863c13fb345d270fa59222639d428492763c945ba39fc712afa4d87728a78765127b73f888ed50f6f439f9fc772ed53a14f5548ab40d391b533a143f76dafb74cea1787782de183b8613a71ccae9d2c4cc7eb319731787727c3da94296b41aefe050523b6d52f64bf76ff786620c4a4bdab49d1b8aa2ac94dcfec145ac6b43c1e3a4164b1d6daac48652e8cd9f1feb0c395a43c17bffd34f29990fa9a128672e9fd79386728a075d273408b91e3494d4ed4c924a3693f29ca1209349ddfb2e971e6386e29b245d795c11e17b190ac2733ca184128477dcc8504e4ad041682a3567c23686920932c9257766f7cb2686726e55c3906ac91c4796a481a1a42abbc3e45c642aed0b256562bed6a3950c252f946fd3ae3ec8871353170a9e948a14f1a4de7e2e94f3c86a8809da427137741275d3d2ba168a16e74128a567a19851374c9236164a258927e4f8978c21be42e153f38ef49c413e6c85926675ab509635e9b92a93ac792a14654d92c3c67bcdba4ea1b06b6266769542f17c37de98a857a22814d48fd6cd7442a1541b532817cb8b27942449ff4bda54ef2896174e28cd5ec9bd23736810cb8b261445469b35318f75deca0b261473890669ad2f9bb6f2620945bf8fbd4148cdff9a174a286fccd629d234ec77c78b2494dc4d4e8368da0b2494e39d984689f5276fd2babc3842b9a4d5d7f0496cd638cfd40b2394e4d151923c4a4e5a2dab16a12037f6698ebe33f34cfec10b229434657695eb5afe99428081b419e08e1c37c4e091e6c510caad66592a2fba4deabc1042417cf5e849565a7b4741288973d7b32e21108a2785470d3a8f3f28e7980495173e28e96ddfef1eed2e6f82832d9731e0450fcae6a5d6d1fa04a56f2f785012837dcea042bcdb5d14352775b274c813f424e9a238ea34cca6b6d1594d1c6c65387824383817a550cf23eaa2e260c3cbb1a347e2a298448610fa2fbec811835b144e5c9349f3fe3c6c51ec158f5da72383f65f8bd2b7094f5567fafc4e5a94528449d17ba364167f16e53f39879724476e982a8bd29ffa5895f26935e8635112749c1294683fb2afc3a2a0edca558497ad9bd8bd22f3957d862baaaf1d17ef11ab6de264462bca72d69fa1d44cd78358518c2fbaaeea396ab8fb0b31c0c0800d24c08347f28111d8b06163c62aca6252e3a508e9a6776d559437b96e8e4929cf124c1c98918a92ce78cccb390d15c560a244878c995394cf4c9ef7cfa99aae290aa6c2dfe424c8ce3c11cd2845692dcdb4fae46c4a691f98418aa2e731e529d3346314c5f34eb287c6694ffd88a214fad9c4ca64913d27334251ca11165b1e3b1c6c0e8a82e69c6799d983fe507da224323cc9a23ba9268d278a2deaadce74bb843b75a29884898eea99e7443126a115a3c38bfc516da2602b9fe4684266cf63e160036335514acb7c4d6282527a264a9230dbb227b6c64431acc6a05466fc24c8380ed64b1463ea9cc3b5a491257e70b075ea31c68d1e6158a268258d90ef771a573f3c72809195289d385273839ea79d7d04332851d2681f7c4bdcd1d8231e93289c4cc2653d363cb889248a1eee66b2698a44c9839439312696ce270812c5d07dbaa4de5afd647a445953470fb33021b664ca0c47943aa5dcbfe798c5336e44d1d3e3878658e85329230adf49349a678e07662ca26023f3aee6789265a6aa88d2a690cb91dba446bf386ea41b3d78e8c071238c1d17989188f267ce59ea34c89ed88ae08b2f7268c0860d1d396e88c1431181c2c8418330c608c08c43f4a13e86dca0cce47cf13776e8b82146028699ce30c4692c840942b733c62359ef01469a5188e56e5566d6335db3947c3a95fc27276710a2d8e59fa45e888569508e94120315a800183c121ad8b0f1274937eaa630631025f93a9e12e3c9a64128cd104431c7e0b96df7e4a44c370acc0844d94e36396a93fc9f6d6cc00c4014664529496bfb7f28e9f89b6a6a467e28e9a449fd45c6f251f7a1309ba126427d9492191f8a9f777d55947b2895d2497f784fcfd043493e511d6632cb63461e8a1a9438a1e2263c1433867c9ad0d61dd01131cfd62fdb180ff313d54e7ddecf4962daa1a0e4cf28db8f137b928830a30ea51226ee5d4e9243f43aa34341f67e125fb129e49cec8c39147f358cbe2969dde433c10c39144bf8521b4eee3c62b5ae4008463a4330230e25995d776d4b5607af66c0a12842c838261ef41598f1867257bc0721bd9320a2e51ecc70835a5da287a8b66d28cb983caed7e6fa696743793bf9969ab1d750d0b9d64b86988def693520a71927eab2955932e7cbef5d29531a4a976dff794a5b338f869210c2ced5c284bff69ca11cfab4972841f466e76628c6b916b52fa2363633ca506c0dc29324099f837b3c830c25fb3d2bdfedc750f492e939be5792a84a0cc516f3b439c972e16174c3fcf90733c250ce63f27fcaa4fdf862c2039dd71dcc004339764eca8612cf3c86be5010252713fbcd4ac925ed85825259f24891ab49ac521ecce842f9436f6795fcf653626cd8f81d378c0633b850d01126299d2465324c36630be560df3e5a93707290272d1443dac99d9470de8c2c94b4da9d984dbb858cd50c2c14b4eca851c22f56fbfa4204366c3410665ca130ca6265368c7ffeac19f30c2b94c4ad4ebd1a55153cb94ad7b5cab1caedeeb24ef12e372a9d4185620e0d1b93d0183a767c91ecb8610373c3069882195328ed5e6596d40c7220a480f799a714ca4187d6dc4dd5b17c138572e629b3ee13bac3a6502867e7bcde2899f184c2e78c5d23c3f384ce9d502ee569467c6b424193266730a1fc79748b163dfdd8f38c2594a3e6491f745225651c9550ddaacd6586901df4c40a13f304cd4842a932952e259fde2432b52acc4042793d6cc398b4527fbd0d1b7884f2a79f94bede046618a1b4aed7252615e3e019452809a58490a22536483799418492c63c99e6eaa451b28e83ad7b241d3cd2660ca198b79db399975bb7efcc2a23baeecac60dcc1042f1a40659f7c983836d57cf7346104ae3ee1973d4acca1ae1605b3db3d54b70f0d84b70f0c80ea4e046cd0042398a7a10bd8f0dd294337e50ee24dc95786b6a4de70e1d37c2d8f16c3bbe3b30c307650fd2d77cd5c45c2a2ac28c1e943be377c7bafda9ac67f0a0d86fb28ac6f59783905d14bd764fde3c31a7742c85e8a2749e25de4fafba9caec78e1c3b7af48e1e3c72512a256613ae5c84cc6921b82847934e8eda8cc6f8e27788c11d728b5208ff4eb1a233d65a387a241f481c3d1231d2063d2e436c61955aed95c587d5eb68a6074d6de1e8917cc0860d1c3d12316cd8b0418f0ba94549d0b12b317e52399fb91821b440aef39e24d7e7cc62cfe439b88bc6655152f3df695de3ad8c2916a51139f27e1f930e225bc620041645914975fe07a1e5d48a30087945518397b6f5cf48134a1d3acc1721ae28cee77dd4f06d1f53c4c1c623475b03425a51be5e131af64b3d7a9c1505bd995f13844c6218d92a4a9ffb47f56686b19f5115e513325c26694ea9288b52c2db7d9d92933121a8404ee655b4d5ca6cc7aee41b25357aee2c0163878e0a80b143470a394549a49e6998a046976c88294aa393a444cfa7a564874a510c3a852e4f23ba4970495112740c9d29cd1a4539f8a714ef0c2689d21145b9f36a8d08a1d66fe250f4b9f162f116a6e775abb5bd1d2c93203a09142521acbcc6b4683a994f94fbd38f3a930479a22493b6dfc5d689325e5badebcd3bd9868966f2e7db7be4f8420c11ece8c1238413a5ecd73fd5b5d944c9b3be24c1b277f3074334512e37b525f5c664a2e441e3464ed86808c14441c9a8bb69f2f132c72e51fcf255cd724295142796b8c48faa536a6e2c8454a21c55ae6364100f1a4bc4f8624708259417d1301379edce248b3a13eee821860a422651509aa47a6538b9b2421c6c2689629e6b85d093d453281205f1b9c3c9d5983331be080142a22458da7c6f266993ba53d2712349d390479434099e25b731752911c3063d74f4d0b1430c17e38b102003421c518ca977f44b750e427b70b081a163474a1d290c0c988591438c2f76a8185fecc00ea4e0068634a2cdec6d572a46d4b1a621fb99e12d679220c6b488f298a8417c099927328a70d5d45b3bdbacc653e394203a942aa5bd9a7d22cafad9e4f45c5632699d811044944bae5c951613cdc1d53b44496cd1ab41f4e633fb56af430c51104af947171bd5da056cd8d04148218af199b75244bd7727e54061fc0db5d52341de5023c4eef2dd312f5b66b27552b3687889d69a64075270e3840ca2b01f83ba6f1d4f677588200aaa935e588888cbedab082181286f8a9f5025dc75ef6ec34693210410259d33c388ae5c28a9f387f2873f617573c9ffab1fca329fd24acee13f28933e144b9f3e751deffa2e3e94f44f8552eb98fa9fdd43b1c53e7d2e1d9e4ca97af0da4c5445e7f334bb6c73cfd54407f350eed7a0939dee9ca3e884e0a1a41e365fc921548708dda160f229de36ba103b943349c2698ca55f95b93484d4a1145f5672ebbaa7d66c4d21840e650fca546ebc5967aaaf103287e2783aa53ccf8985103994b64f7c92c96f3dadad33b838943c26f1537c1204877232f5a4b9a4c6494dbfa12094c922f7253c7ccee7047c087143d194a427eb97b24e2616d2869278f11a6d74216c28789ac88f519d19ee453a26d079085943f9e43a519409ffef51e710a28692ff78896947deec73728891340742d2501eebdcda77a1eaa3091aca26c599a026ca903394644796683fe2e4b7cd5014d9b9ad63ac11b1ae0ce85b8cc75a7cd6dcebe70c11a2df2a32944f9263facafb848cc15d11d70aadd592b92e440c853551bab7c46e732d8584a1a4275d9c5677e93c1f140286e2af8fac975c4249567a55430c66230ce52e39cff4cde2ea3630149370d224bf1d3d0ae36f6ca1c6178af9d5a4d2a4f642b1241fa565cc759390fe8e1e877f831cff450c6cd8a8d185d22971295fca3d69eb71b021bb1ecf034d50830b652bb5d91ba6e43cc8a550630bc560291b736e3af96ca5400d2d14370693326c102a63cc0d3e10d4c842493a2144e4ac2849de140eb61b3dfe056d8bb61d48c18d0bd4c0c239c9b9948d568d2b94a32825977f770d2b9862ee46261963154a63a3754dd3f649d951a19cee2e977be229a0253bb7976119fa95617b9a93d85c9f63ac86144a9287fcdd6826aec6bb46148a7157fed38878c934d6804249105b624bd0e7a1733ea2c6130aeb6b71a35df6caf44e2899b827cd3f4735f3b946134a3a8a1899e454956e0c13ce93f5d6d97df91cd3f18411be77b29696509add3449e6d012ae4f6a28a1245cc7bae39a3841a70c057650230925f984fabc27669297d640423189626f427d55e308d568e8862e8fa1420d23143facc8cc9a74112e57eb7abdcc26636cfc9af545846279babeef87507aed93a3b3287da7db1a4228b855ecc8f0a5942497d408424952a226619b93d40042b14cb75486fd4ea37468a06302366cd040c78e1d3a4cffa0c60fcaa19649d03aa56479df07a5dad250a725cb939441418d1e1494e812975efbab21da400d1e944256c8947667eb3aee62bb3d719d990bdd7c5af77b7d458e745152efeaa26ff22d6a3f81462e4a424c6913d46e6c46e843a0818b9298ef564458e9207ebd45e1b3efea867b09f2275b94c27fd4afda4b38394da316053132d263dd6af052f25f981834689178ae98b66a75767687c6650ef149ddff061938d09845f97369b390d339c77e6451fef826867fd8dca8d391c2c000fa22078e0978c5223567fab9d984fd070b45dc2e45e744bbd2cb3d73189525b74ef45f61cad825b4dea9b7345c916d57d69d6caa8e8c6f3a549aec5679b6a2ac61a6aad37b58514c624ed406d553e289345651182509732a553d897fd25085a55eda613263ad972e37a271ebfdfe4a6f9f8a9289faa94284882919a6818a6bd7543eabf276d3c232d774a7522ae518d0384569830ea6b2e34d539493f0b95f44f8344a51904912d74f684c096890a2701bab462919f27c6b1aa3b83e4ca88b29931fd01045496acee36626c7860d1b3680402314c524ffd6fe19734754344051cc247df292f9249aef343e51ea20229368d2ef2a4d70b0d1e0c3b8b143c78d36b40ed0f04449f80719e5f5f7f6eb08a0f5a8c1e3046874a2ec49ecef9e328cffc2ee02343851ca923249535ea2b1892cc4ae5ddfed2cb7d35d4e4cbb9e3749562fbb260a32c6309e995f1c6c3778fc0bd0df48c9179a8246268a5f1db4a6d2a1f3a68d18628c8189924c72126af3a6efc4b0c1084626e068a07189b2cb9f78e998246aff9628a77fd4b87a9146254ae2ba6a7952725ea7438982ce194a36392c1a93289e24dbaa66996892fa72d09044e9d342de9d98d7c504068d4814e3fe8cecf86d26069906d0804439e75172f8589224bd45e311c55c82544fa34c8e28e87492bbe9e91a512cb14194a7d22d619f146830a2184d091e379e67cc6c8d2183b7640c19141a8b2867d588fe6c72e4974611856f0d2e4aa672cdf8f0a0918862559e1e93a31fdf5d081c1165b5f4cc5c2adab07188d2d76a5439195ad6747240c310e52443e7136b932cbf691a8528cd276993f49de45c92922f121a842808194dbd53eec6b07ee280c620ca1e4f90f9548c10750bc26c04a2d4a6d2b46d671393aa38d8ce6900a2a4b6619a3dc367b8265540e30fc5b34d928c12e36d87fc5052b33926f5bc9db0bb0fc53ce2b4e4bd3bec523e9477cec2ed32c6d149b987922ee1e493fd4c8959abb3630c1d22a0a18762dac8e571a0918772ccdad297e7e3a1e41a1ea49ffae6b6b5061a77289f9a65d21ad4090d390eb6307298b543f945ffb599099e4fcce181461d0aea635225d5fa942edd1834e8503a53171dd7f3d3984341c5da53b8c9ac9f840e0d3994636e70134a76938dd00b68c4c1b6f392bd0eaf395711bbabeff817bc40c71734e0500ad91f4a2a193d83fa2540e30da58bd13cead3f54bc434dc504aab2e3b4d2f629bd26843e1838b3e31ed5dec7768b0a1ec25646fedda95183d6b289a2ee13d670223d150434969f61093cbbb63384d60021a6928eeb9baaed6ae29fbf4c8b1c304366c201a68287dd09ddf7162cd981012689ca124425654c932b21f7f339494e8bce7a5d232e89c32949327c9d3789992544b1a642888eebacd60fb18caf919da044ddb7693184a5dadfe260893d4974d230ca5f84df224413349393234c0501ab14186a8922ffa937ca1e0ae9a9468b6bd19935e2889995cc4c93276a12423ad3389731f6a7334b850f6ff5e3b6d72b650922caf8493c1440b0593fbb34c49338d2c70d6717a6d7195a33aa31ac3abcd9fd81dd3c042c16256e4784c3753ba4269445507599b3fa5c90a05dd9264b35deff27c15caf994d8ee87986ca950ccabfb6be614ca1e45989ef8213b87a450b4bd123e959828945e47895a0d140aea614b284f3b21933ca118abcebefcc398a89d50f0f88d29d2e4778e9b50b62b316fda6542c193dc7fd3fe49277509c554f3276c522b6eaa128a396e83d0984c42514bd4202689498c67262494ebef4fee4eaa4fcc7484820cf679e2223342b1c23d4cd6782c95a52294b7574bf4cd295d9488505e4db22f47f38c300da124cda528294b4228bf75ccd87fcbd43f0825d91d94d5773aa9014241a9f6e0fed7a34c921f9484c8ef0913320d1f94844ed2274930e119b34fa307c5d8367aa39e7c21dda7c183725032aae4a833aa94bf8be2db98bc28c932d8f9ba28e7bc51837a3c51d4f7b9288e188de984d8891e7b5c14ffe6657eeebf4549ea3c1a6d741244696f8bf2bfc6b7fa2bb15e7d2d8ab1ac24e93f9454f769514c5533db51a6df836651509214ca7a3c7dfc8f2c8a693b5e7d9969824c2c4a1993fc96c69b560d8ba2be8e8adacccaccaf288edcf9cdec7c4a5be48a829bccec494e5b51d61621abf24d4629112b8aa167527df65c32c9d02a8a192a94389f93652fa48a721635328f189349552815c5367965649273f628a1a2acf9d64197a728a91236ac4ff2763399a254bee124efd469eb2f4541e53db74ab8d5d1214539bc6c93f09c51144c36396651f392442e8a829da99f47f998361d8ab2065da5552a22d406457937d7dac6f6134537cfe9749cbd3ed1e389b25a491a2a62b336783a518c39c624a7121f54698713250fa2fce4b2b38972522635c68c2921aaa389e2e69264df48d3a93f992809fa4c14f99375730613e53fcd691b42c87efc12a5eedf12e5b8cd72a564182f75258a9a32a9797c261d6d4a1444987c728d4ed7263c89720921b73b4ba294bfda952737d3e34894931866f9c1a4657c2151d231879809327e4eed238aba7dab49924789b58e287ec7efa7dfd4fa41d388f2859d8e49daded6ce30a2e01fe447dbc74eaa338b2809b5e1efc4cc28a2e4a6cd437c103f7d22ca1542e6f79079ed4b88285d6b0c7f9bec83e70f51fa91214d8b6e88e28f651247eb4294e452e61d4bf690d712a220ae7cd304a5411437893941eeda9928055192e4469353cb768602516affb0ab1afa840e204a1fbf4d4efaf34e8b7f28c6acd2a93eaf32c3c40fa50c427aa8d25d5fbaa40fa5f0a4a26fe743e94a7cab0b795286d31ecab3a6c53d7af450dcd78d5623938772caf09e7d357828c619d78bbd50a7277728f7b6e6d01394fcb1dba11cb3ffe19dae435163365fbc9aafa6e9509af937e9ee3994767312fb94924ef8580ec56062f298c92fdf1387e27b0765366a6b533894b2e52441597b326fdf50ea6825975ab790d70da5902549c22a6d436934e7a05c83ce86524992a47b476a7fd0f91acaa383c837d550d21bfbc4b498fb501a4adfa7b2a439b193248786d287dac9224728bd9fa198f719948f6f876d6e86d29a289e4aeea94d4a6528a7343ff94a5dc7982743d9c49e743b317a8dd0184a92fca2e25d4266d088a1687f42c6d59230944dc92453530918ca1bd4043d1ade4d5f289a28aaf637f7c22748f3b8b1ee42499b12c4c9b3e9c4d65c28f50959f939e2497e0be5924ffe31312bd5b550d6199d44c427412cce424989a9b664ead5742a164af2875299f2b5be79ce74f41c6b2b144ef2989ebd949bc957a19c537394fa95e8ef53a114a73e964c2568357f0ae5d2d36b1325a78691148a2649d6b9e3319ae944a170233249d19324693b50287627615c43c6fc3a4f28cb7fb80cc2e2ec354e28abe79333ef34982c37a1a03f8dd6bfe68ca26542d13aefd56f846a7d9750da4c262de5fc93adac8492ecfda4f308377b9593508a9fcd179b8f992a23a11cf77f3a4e6af40bf908454fb28b661236c6f0b1118a1bbae46a934edc675c8492184d51523768b513114adfe16dec4d4328eef7c793f75d3d4808e5d41e3ee506654ac907a19c9ad17d7c45a7ea81509273e14992fbfee4fca07062daf9894e268fc80785192b49d3857a5090f9cd04a53909bb1a8007c5d4a724bd739e2c3cbb28ae7e5226c710a69ad6454909bae4291f531d3917a5925f8250e2e2a2dce14fb6ccea1645575169822e37b1ab2d4ae341ed95d61847a9b5289f98c4d1266f8cb89416c56c42be85df9b92e52c4ab79a04ef64f24939caa23c3ac9cd41e71365dfc6a2a4043d2535f869cd6b61515aaf6b5f517453a5841ef31451724539ddfaee4ccf4c9d5a5170cbd50e1db1a2ac77ebc1eee374daaca29c49d2a02594a82a4a327c8c6b9aa7f124d15494836daafd7c6aaad48a8a629a31d998638f785b4f51127f94dc50c2735c5b4d51509949f81b69294a394a0e19aff1b42949512e71849aec5d73334751ccc132e68cdde3218aa2a4169f9379c96b722c14c564e2ab7add89b22905453909f31b19e97f57e9278a9ffe3aaefbed6f92274a7a7292a44c754d893a513c41fd24ffd8f9b5c389a26f971a9ddf5159b28992ea20ccf3c91527a935511272bdf94b0835279d89826626b1b1adf47c1613e5249468223d989728866b0e26274f61329a96289a68aee96aac44e9839217b5f723b651a21c55b34a84d0a6cfca4994bb28ab283531969cedbfef115d94bdeca4d970ca4559ec838c14a5ac379d8ce0a214abe7418577b6a5e71605a99d63d61f6dea451eb1453193f28cfe7bd627ff5a94d76ae4c6d1195d7f97162549369d24362d33cb671665b359f110b21b914551c3fc9de6fa3f6dc2b128bbee85d0d599bfac1b8145c1633c4166d226670f6d478ee4033678ec7803c68361c3869d2d465e9123ae2808f55f9abeaa2683522b4a9904dd4fc2444cde20561494a0429b18b4fde9c657513813369bd2d973a6f154515027a388a92895a4dbb3b7894f0f1d44541464746c9ac96aa3521e394531c91aefecb5842cf970c414e52b5153dd7f52bada044b51bcbf3713d564c708f12129d80423a3c8118c88a23114331801457ec213c5f41d167ac3d689c25c2633931b13f67b4e143d6cd0a0cfc524edf3c82690ddd4f0f8aa6f35ef583f256e43cb114d944ecb96a063bffb7a6a2413a5d0b03ee2bb644e5e61a21c7af31bb944e94ab6d0321acaad114bf09576d9ee65eba66a92d5ec9528c88e9ae62739fbca0f8c50a2e0676a3acbbc2393b8114914f39f12faf15a1ee41d899287d41336268cdd092151b2948fd7be5d72d09b4794b2ccded7dd833ac984c388238aea269e43ff6aee5c8238d28892d84ca132a94cf6261c6144c93be992f7e4e360b341caf1851860e4e0d11d185944d1e7fa2d33bb438c335444e104b73c69f216a69388d28cdaddf14d42444184d22c31870f511a258e90676ba6c1da1143980d5cc148211e93e2a45eabb993445c821142fc26f667d930eab29141ec2e969edd62f5181144415efda74d82692f493e10e5a827e856f7cd0820925f292ae546e90f25492819de33444be3c70f29e195e943e1438951f5c45877970fc5cb9362b36e4acbd9237b28fc58597689ee9dd1eef4b0897c501ecad1c4c9f82763cc9f9ec3433107b1b94bd9497aaa8fdca174aa45f6843c7307237628e82cc97666b3f3c1481dcab2b657bba2c3fc83478792322badec9cb93994e364ef9cc142d449e264c1881c4a997b295a84e5a7130361240ec58d252919a539e792cd08237028cc87d224d9fc3d187903afbdae26c67c8fb8a1a0b3c9f8cc1d3fc99e431869437153ac26613209cee046d8e08e6d581555a23ab286921cdba407d9d89cb51b5143b79af7ed961a9ed6c43192864b96901d1a966fabee12da66335281108cb41b46ce50b24d328350d649db09f6182366182903da1821039fa71ae6ee29f3265def9a3ce2c47d640c6593d36e6f92d6c4a62d8652ecb5981c93acaf170a4341264f1d9336efcaaf236028c9a73ac5d89ce8ea170e16c1c8174a5246ffe05669fa3eef85a27e10bdd6fe98d9ec42b94e7d759f6ed1d90c174aed576b25fef9e2b485d2990e7ea561fa1b265a28d766dcd241aee72877164a9ec5635271b7b12363a17097fa5526bd57289a92a9d39b10ad5012b3fd1aa4dca8b60a052fc94ed034d94f7b2a144efa966bb69b35d3a6502a153eaab97192f8fc30228562973cd93b768eba354561338fff42a1709e157b9208fd9f33234f287aaa9bf1d8d5096cd291e5ff7b134a1d6a6269b76a8409cbddeacbe9eb8a7d8a6b89d73b9fe498ee5e42d13694c86ed59524292d614409c5dcafff31e96492768e24c1a0fe5faa4e8e50184142b1d364e6b449baebc2c8118a1b839273897797a43e3762849236a593d2307b79f7f148114ab23e982925aa36ea384284d2585fc9225f5485cf23432808d59a642849a6fe6c1242f184ce073ff3cdd8cc48100a3ac753a745dda62ed9ad300284c2776afd24bae8ee07851fa16ee207cd49e7ef8382a74d27ea97181aa79484911e144c1abf3a19843bc6080f0aa2eaf7c41cfb5d14f4cd262f5b134a64ae8be26736498ee5359f99cd45a92de7f326b933b22a2e4a62b8df51ed1de5a712b945490942875082b6cd85902d4a1f2cf4cd7f64ee7f6a51d061a3dc84cc7552fb0644685192b328eff469a429a959306fe9257a27f671f949e65326a78af922b228fe89071761e235c6209158143556c678c2a8cba4be082ccaa7d36aac0e4aee15c513334e30316f445c51ce391d26b6a6d912cd3088b4a264aad304a12627fdb2ce0122ac287fd44c9962525b7fdf9123d90544565192ab6376d6a0edf617dc0823078e1e3968a00a2d67752e6b6fcd656436cc4fc7694c1e1a839e53515e7b3d9374d5ff4dba23e971230766400415e5cdee3de14914f17bf048be103945d9f39fa989f224746638d876470f31444c51fc53626ff7e273da064b51aad2eea04d521435dfb8c63c7a829ae4284a69274234d7e507f98ba2a07a6afe836c4361366220028ac289cbc8a711b211f94439ce49d7262e643bd49e287fb66e10724b7c484da41385f5ac103ae82aa15217e144498e16e331ab854e318a6c8299b9d44a11d99ccdb44c4413c554a29953dd9789b2ea68312d9f93369b98288608b126e8348a5ca2b8361fff3b95f6d30bd32062898209626af7e4e7e78d2a51924f5242de2749285152b23d6a4e329dc9b74f22d95443598b972292287ea89059f3735208229128c96a3bf22b7f73494a9028fe088d2566dd3ca2dcf984e83bf9c3dd653a7a8481011a8838a2ac3123fbef4b7b10adfd40a41145ebf2b8d3e531418411e59e171911f616729e0f4416919ca0214a6c47498928a294fb27fa2d94321198dc77855766c55c6bceeb9edc1deccee4560411e5166562921bfb8312371ea29c49f48e51729cbabb0c516e2f75724ff36fca07d520528872e824f96ea9f8159511a29894a6d233ef3167dd340522832828494e42ca7f923e87f14e1025991ec54deeef0251ae0f5ad7b4e8542584778028fe8a12da6392941cb3fdc1ec7f8df1ccbcf343390993932842452757fdae0fa533b532fdd96eecc4777c28cc95ee24c792deeda1a8f15bfa7bece4fd54440fa5cb71d3b177ac83b5792889f268a7273e896a720e0f85ab59adcf29f363d3778792a4c44ccfde2c33b1c3b34341bb5ce63d2fb1c4dc89d4a17c6da1ca3756aa79121e39f43e20428762c8f8f62f42a72a41bc39944629f9b03f2336f3787228accb9658bf265e1c4a929041643e310927bf452270288fc688a7e9d9ea92b4226f2849b2789b657a720b226e28674cad501fd436943aa7e7c9656a8495a412614369d65f76664b759c0f1544d650321ddcacfe53d69d3e89a8a19cc5d7edee3715494339e3cf6bc575ee13ccee400a6e84218286c28e0765e9fa1139434166b37ebf53b9cf9d881950a40c05934ddcdffc25064d6a32144bc96d597ff22d273581d383c8180a9a29e48c12f5c3765a440c85d76c5d3b4a8e2261289668d27b3ce638d81a0322602849ffe757a667a54ef603cc43e40b2593d7e4757c4beb9879f4f1b0168878a124bde7b88de9d99ae51c225d28e9ecb877c2c66abd5c1a07112e94eecb54dda8fb8ed6f0e06128b285d27a8dec924d180c225a28e8c92ea94b123aedf9a501912c94b5b4538a52dea7bc14c142399bd2deaac10425485be40a2549d85392582adce1011b36c400e3b040c40a05257bf4fc23ae840f954815da4c9dd763b21a895081335d9e414d9255640a8eec6e8f14ca79212e645ccf18b5fc42240ac51ca37e3cfe66177d028572b54731255c7acca07e4231772e27943ce80cbb9f24e917db5940a409857dd12d4966f9bfca608830a17ab3947dcdd239cbd72c313cd54aa6129125147dd3e54ff84a28acdfefc8344a693e4942c93599768890dd7c915092a1dbb3a55de9797f89ef2232a5958c505e0f5aa684eb4d7d11cae1d4854ce2834e9f438482fc791ad1e8104a26fa68d026be108a25f37d0c93427e0e3f08e5246d7dcf7b2014cb724cc6c46bdff1076533655d562be2e41cf241614e768db490480f0a226684c8ad9becb0080f4a323ea2fe539fb07917a552be1a424df57cd545612aafd3facc4549a6f739d7d613fec34541bca90efa96994f756e514efda1493ca9b145e13d4753a29c24b5f0648c1e27d3a645395a8a8cc9369d9999cca2a065c6c4cf95d6a04a64513a1d226428db5d1bc5a2243a9689113ab0288d944f9e4208fd12f6571437cf8cd075dd1565f390df31ef924bf556147decfef663b4d6d459511871b25469851032fa2a4a7232cd234c9099c97255944db2cc9ce4f01fb1e9549444c9b32fad4145517d4edc51b5b924f514e551527ec4c64d62dc14c57c9e5b4e92bd42c552144e09b71b45775c3b5294cee3949bfe1ca5598fa294d5e7aff7d93c8c4451eca093fad78f419549280a676a2a66c49a691b14051f99f389a727c9f1e24f94cfeedfe3f224a57f4f945d63f82fd5d94e14a3e6cfa03d09270a6a44eecde9c9f0d1dd44314952adc7986cc7bad544f1d3a9ee6c9248593f13e5cb36a5fb63a234579f35d294c929d64b947fecc7b26489a295652653f259899270672679e7786f9ea4444134335d7adea0ef242751923a7549943bff27f7d327a7b52351decfba9672db510389b29cc7d5867cc7971f51dc92bbabe3e667ae234a4267d18f5362921acc46940411ea3aab456b49624471d683ca86e60f9b5f44399c9be9a03db68b4811e512774c29393d7b73228a23c2bde4641e6737444449c62476795d7aef8728fb8a9fa0f2d349526d88a2492db13a25a8cd72210a222a73a14b559d4b8882bfc6f0d9fd4b65378862e8514b1005d97ea6fdf54014ac476485d201446134cba7388dfda164ca473c67f6762fcd0f2531b6fb3a96fa50ce37a99367eda449c387b265faf43927254cc8f750cc9fad348d5fd9460fe55375adaa2629d1da3c9467cce46c7a25e58887e26ad09f9e335668798792ccb1493a41e7249acc0ee50fab1697b19baf0e2521b5dac7349d5255d5a04339d5377c38d3be1096f1329e1839149428dde49b9f739e588d3814d49b9cd5faac010773418d37144df447bf8ea2e4088f1b8a39463c73d0999385ae0d05554aca3dc2c83146d6a0061bdc1cb76a4d1bd72e9bf9cd24c68fc16434a14f4c75d45843492849497df2c3be8e5f430d45f3fecdd4359b7be71a6928fdc976d2e875dda9c5c8ab81863b43e96e2d737d96209af366289d184bcc24e8e0563aab518662f270535e62ff39e56428de8892f38cb410b163096a8ca1f8977d67aad4b5018f24a8640e6562025924120ac40171281406a0712600831308002040200c86a2d1581cc89af803140003582c1e32302620242418181c24148905e25020100804026120280c0a8582815048489333ed06ae7f9f201f0b1b7d8f3a80d18e62ace9834754ba3e71f98670e307b10ec0b8a9cad87d1160db42b39a5309ef5650d80ba7c0b03042bd52813c323dfb1064de3dc3bc20b785f08ab2209a0db8e758ddd450a1f5e8a847fe29966f6c6b998d8bd4251b2d9e1e8a38a683ba29e4c9529af469e5bed2a639ed7d45d7cd2ca96d9b4d342f5ea061ca2ae024fe5b42e8d726dcc03f697182977fdba83dc9f834ae6a1497e94db70296dd3861e73c44e1f1310ccd2f1357816147f3523130c57d68161c722e6b929d52760fdbae4ded98217cca1b7c309d5482d229e1d53994a9d944c89f59bbfd6539f6fc7d629644f8382710a7a921f21e3219dd75e80fb1aca5e4a95c310fdd181c98c15c6dd10e297a1b23a5b65915c4700bcac49b4960a810b80d521adebd3f99043c72b98b9af09cc16e9c72cc47cef328439b41ab6963e5aad88397817e45881249dae04e0f5c8f30aa1c26c1b69ded1e31db25951f65e3e8add3883f5ef8bc78747065be5437ffd8be32cc6a4d11e84b27c493f7917092b17c3a65cd519584679d1a004e12c0f698cd9e3691a8b07092d1aedc078eb2b272ca83cd6bc26b237bfbee3ec6e10ab69bc553f4f7b3e46bb7ff140671e526355135db51e5dacce09b4a5a2be1f44477b6d2b25c0f7213fb8fdd7388e753ecf7e6e6ded90810eb4e119e2b70e4716ca7ae0a39ef0873108ed874b172ce0db11749d423d925338af5b340f4aaf350149a2124f2ed5311cacfc93a846e2230b45e048ed7b4c9bc4219882c9fa1f830c1141d2b50451af953b068e491ad41d708d772adfe898969fafd68076c6c7d4dd32d59ad797a00090e910fc34fcdff167cfc91cc0b21ea61edd88be722ed2b545e3b26a0950280e882316f2fa14d3988690a2b4d1b5b41f609903e7108deb967421add23f268380c39540eabb110a0869d82468c39dc6b3ee50fd62709c883ffba218f845ee4a94597cfc617ca49c10b0f7a211e037190387c555588d0f7c44bd8112de7a16c5bfb088afeaa08701d7f4c336117eba0852f87f050288020304c08a32ec54c5eb8ce95daa72096e01681058a61ed65fb48f0a2e810b6f5c59f88823b0dfdca044e61884fbc06ba3cd0f71b3bd3cdb716a006e6aafdc16a1d04d133c00263bbd48ad9cd94485eaef17cff9d91991f24093e6a1bc1bdafa227b471206bc7a05cf12559eb9c7dc2d9c910f122a3326cdc8b2e9d5cc6e341098c651e70cbb4935a46f8ed2cfb813a42ee643d367d17c9913012fb02e18a0b0b59921ba08a1d6076fee4a4c52c818e8c1c5d569acf8039687fa8fc41a74686998430a3351c7189d10302fbe08a90dc4136b60f999922eaedb419df9a6f2b1b56a580821dadfeff73a3c8fd07292c7f425a277aa900752c8103113707a61c6b691002a7686a509846ea60ed984d1993de70d8a3ac329a2f6cd5223a15b2228b913362a36bd6472a0424af3f127ba5bc37705cde0446269a85331aa9c3ceb25d15938d1ef3a214e81dd52438ed008bcfcb4d049fa5239c50c5347b5c02a2c6d1cf03e535bfaa70b2a82b6a2307d6e0a619133d4fb142d649e84ddf93609686ce9bfa5478454089e28491248613c7657a89f569a7299e1cabf810ed9a64893a13e031642f7ba12cb152ab752abb22925d1419e0b6a1c1ab8d85893b340b9f4a756879dab7993c2fd1e19f70a79261384b12f6ab98918606034d6c06e6a1c99ab51426f2d2f10dbf151b23cec4aaebae025d7d9544558f1ce903dc2c86a74bb5843b079da05cea739f0d36092c5024c8480aa4e4f875537c847e178097ae844962d4cd6528b89a367cf238c3cb3484d8028d63fb29a84c41cda42373da3dc18445a42b7b0150ece2e387afa2dfb2470774e5ff11a90cd2856449264e5d30c290de0f3ca9d99314a81c9183350223d1ba1d76b128044a151c99a296a4a099504d1b25e8e24240a13bc84ec6244b135b2b80b28f6487260b786477c1f33b908548ad42158ef599c6687c9f009b33d6c1f74990a0752885f8ebea3623ab5d5635264414ad470531212d140660c03c6f76b1580b5747036c97f4e45a13e14105f46ec844f56607eb11c25410a8a193a3a8de3569d12bbd0126b16cf36e22c08ed74203c40512ddabb01035a23f360caf18a62725f58d31483f3a1695fbb57abe17ac4f577e28fc5569c3248143ab3378e124bc99db35522a3ef0d473c816be5431d2861d2e22f116060c3b6384c750df416a30bf7701b9c8ebe2f8253541814f6b881ca025d70e9c0a2288dd192727e46455119306c99bb13702bc46cd93fa3d50cf9bc310cf794f09c4dc0e63aa6a60694edef9210732bb3a45620eb07585cef2e29c3c50281c2f57891d3a3efbf831bbdf9c1a14c0f23ea0afa245c9551335548ead8b991c47a89c04a0cefb8c3610bf4fcf1daa71c576b4418e1a57eb6b48a9eb5c5dfd224869e9020cccf40dd1d25ec0a74fbc5da18eeca22661184cd151731c268f3d9e04e7ca0b111b81f8975e079c50c587e835afb54f509a6c2806d28cb596af4bc505d827346065aef3277c47864fb8b6f28e8e639bdfdd1c0f6661c627d9ff72630ee329e9358f12303fc96eb71439cb2cfdc193ac87c411829aec8248254d937e0786cbf9fc5bb43985ab7c87877e9adad146a8d29db810febfe30901355ebf0b88ded289602708bc6abd8895fa4ce25f63f35b5f0e9bb08c6975f45005a1234bae3019269e893cd62efbfb6c14d060d2aab8c5724fd8e0bb77c6a4bd18bb3e967743e174974782fcb13eb8b530889428b57e06e58bb03343aaf657b96c278954c6e15e7a15d77b4b25842a20d3604c36027934e965448bdf8b6ab208d0a43004360ba51617ebd460eeba8f7c82ca202505267e0b6a74b65455c767180d2bb2bb7d5a9ab6820b4d979a968c6e478d39e3874c758ae64a8f6bbc866b19e4a44c869e1556cf6bc17c044a6afea4fe081898500208073c5bf9795b73e6b4dd2bde6e727077d74b4606d8b625c5d59943f30391c830919e09528db1541eb04a76f545efa12863209a2d0f38c4fd36bc6e6f04493c8fbd502663d2d3b8ef55d250633fb7e353ea9a9df45941a5b5ea8b07c4e5859b132ddebc1ae52d1e0a3fd79216ae75a3d6af9b43c0d1401f830a501874cefbcb56429fab3c4823ba7f93da4c23b8ac4f07af7284801eac8682561f99edf4d6bebbb37aa7a15e367aa757567444d47fcdecf6f7f901232d87c3fa189c443c52008fe3de1884d43209f70f2293a8f9968aa9f30602d39ca27112da151532940b49a965920aba0c161844a8d6217381dbf2bd346ab626b4a4849c259ad7e2ae7ac2d0bd58d75f6c2551eb04be18e82c918f99b7011af0f9e54057bdfa47f8b28a760e03e9c16d7ea528b220e317f8cd6f227c94fa91a9817f7f8c6048e1b3e6f432763f0921c63826b5702ccc09cc0eeec6f98e816b6b9348be316dc43bd619e0f97eb4e7e5a3568f0782fb323c19505d0cd1022c3ba78d553298dbdeb4ce135df4b796cda21d27576639e9ddb79e25ac4f7ff206d1e95928c1b79b5dbeb4781c73512df5f6f430eb4c6d2d54d03f9851f66e2a6245e4c04f45bf894775c3d634eff54c10dddc0c5cb7a2b6e11611abfab9cdfc3aa8277c1b38036db4b6be8beed9624a8231b86306310a44bbfa121312a1a0bdb42af1daddc42bb7b1acbf73c361a3e42f7054cec7993343cc304f53eb9937a750168866a916b9ab9d3659343ac236a1eaf2f694ae1d1e5224d3e0c0395dd1dff3c0b0638bd1a3d8b9b77994d2e810c636bb94b2a043a57e7dac1046f2436b43fc8ad12d72c71a5e81b5366ab50fdbf7aa24a209f2a147044105d1df584fa6d7bb9d4f62f76c0acbfbe2ba4c135e066a8a383839673a7d5c49d18aa7b1d091027a8bf8bd85516c22424ae68482119086cd45c07cf8b4416cab3926526bcf27fb56d3cb6179668a9331d59278395cfef33715242da35fb4f37566728a3cfb7ec4a82d7293923985072353a65e497cf1bf0879a2d956629e1274d0af9b0057723bcc66e6612153c149340ba85cc7c9365e4339a58b0344308d734e0165d8a7b3b96a85cee8348dca37f064d662926dd6f911e3d2cad448085fddc5048209d1a6f95bc9f8b65520fdafc83b8f13462cd5bbede0e6f8be6789fcc4523ae803d0c030651659e7bde1aa6404ac69f18d328f419fda1c3d5351b17f6a94d8c3d0198b91c00071d156bdb1115e0c783c481041761f2afb2ce2ded52716d7399527ed311412b525f8b275748f97ca01c1a4913056ae56c11a495fd31de9a52cafc52ec609832a27b4a4f6241bcad3fac6a8e7867424b7e4c16ed8d003d5d3da95a4431c74d3d48b467252239dfcc125386e6f740124f27eefa7828c7049132c7c79b274befe0365938b218386378c07c8303eba3e7d533587c5a4153c6766f74afacbe05c36fdfd85f32558dd76dc4d94a81cf01fd232415aab7f27155c082a5b7e22bd2b74742ff4c2764c6fdd61ec4ce88627e5354bfe7f687eb11b39293201aa0f666a7afa4dbeb3b5f88d924c6b91a9cacec5db7d17c45fdad413bd4bcf4916c90da46d1d9f823ef260146e8aa1d050827cb55504790a10b530327aad42a76eafa4a44bc9dab146e993954a5ac7e80ba87252e4c041b48a9374a26e55648af2f4269400651a13bf43d9837901c623491d055b0ad1af7e325de0f41a933d43afbfba9998466de5e0fd5cd1ebad5a1ed9ca456c14edadfc90edb53d6e52abb1fbc3ab4726960f4407a48a9c5b978b57d292f03c29e64ed61803fbe6ff214ce4e63b9b03b3f425c06d63619a5f88600010c845b66261036207f89a37392df466a8fa878b9469918a00555052ef91b6ab29fc03cf52c587345b5be5bfd4ba0f4c61bf3838b75cf834418a38bc9e7db36ec1716670ae4b059fe168b254e625669179fadf4cb711d31a847e1a344eb09762ade307a5bbe5f40b4ac0db0b5773fd1834cb17f15b58680e8b00c6993493d2433e8d6a2059b970fea3041a8971d9e688eba757599c69b0973bfa2c3235126f8b1393ebb7f904eb2771d398d618a57bfc14a94009e6e78d41a9eca99098258c93944918e2223e024b443cbe2324c3fb16814fd164305e6c0d4c4be31c731374ceaca0671ae1ee3b62dbd4b923c0b7468e067cf29a1a26816777b6eed991d2719249930c323f8e973f6c74ff85d41504908378bff5161895a4b926181542d8f831a8888e28895a3f569883049c162c3d8b07f0c7926d88818ba6449ede8105613bfc95edc8722eaca428e9d36050720b210be3ff17f293739244b06682e968a25ab9c0a066f2bdc1ca5d2bd96a5585d6677985d8ce683cd0118a482cafafde0aba890c714b13b9d90b7c780b36ae37210c4a8428024d16b09ab1009e4472493f210a38bd1f7e0b5cb12389d78ddd99bbd04799dca63a9c3a23a99d9cb9a77903e14b6eee6e1d1a104d3eeebb7c2ced824a2dfb8ece50c6b96a2c3205dac157ec4f2a5658688165c57498917c1e9381d15a98fe5fc6bd92c7c193dbc5d66ba608dd550add49045214b5bbc122d9bbf2c422722780b526121d12fc7add7c0b2357ca4a3ec0151a60aa47e7970394da561ee58be47805d22a8e05716d7388f43290cd44c783deb6a13866726b155e7136313048f03f9c40846db63e64375d47fca9067e7a0e767efb750c2e43de0f03bb29c802b2a74e04ad9e77c7e9c84fe81cc13c33a8d0f7539f601bcd74d0c50dc4bfb31d32de49da6a5e592dea365f801bb4ece1c250518e7c5286cf09f049a0433ec34004e4209dea36340ca8001c03cd01dc1ebc563aeb7c86b1940b454892f9d8ea085c3646978c5a6e2e009c3c42a538f67d92069c874ae9685cd26d1e551b2ce4a86328def89f84890f498a8f173751a9b39bbf930782d97994b70827971f25df66164b70cc67daaf71a780b51e776b7fd82447238f045a36f027da6048bb3689a6b885318c1a65971bfcdae03f666d77acfd64b5dc613cdb8008eae263f3b57f72d463db364a336e1f5210e8d14f3222dde398e6b6718aa26c39df05460c87dfb6fdefc953e63031e8d72b67d11a051361248c5fa4b37ad007690fc23d140289a9f264f59cf0476d4de5450be07c49fefe92c0bbced171a28975a1b6535ea87e215a7eb3a3feca5579469334828e06e7e67bc8f8000d8c732cbc32376efe9a3ba7e9829aed5c9c5aaedb8f0498d605da6e3cc256c76aedbc0518466bd5bf22bb71248bc7e267385190d330be6bda81f6bf3044eddc9bf54a1c33e60d00814a437ca1618e92a95d29317508573ba31b38e5e78e6c7a8c4a92dd6f380677df594c9d383fe57e4ccf3186430405bbcf7c87ff49db64c86e50d928cab2273d083f6fb12b79002a1cd3038746777a93dd06116905fae7bce04140efa2d96f9cd7300c27d2725f1c17d3e6d77d385d96c65eecff0f726249563a0454e983243a1931450167793561dd9ad72bcc763f47c97d183b65e4e1581140c943538228580c7edfc721e18c23dd59391f97920e7bb9805df6e9b8471a76999f202a352f1aa3f4d118500f809cd5c89987fbb8ffbcb524476daed24c9a22707651cf6427e4f4912e7992d1aa64c0c1566d234df25b515e8cd33c09e7f162583e562b2d0e6b2e3697f447a0b4a82cf37d40fd062df4beddf37332ef032459c2c7a93893f707efe33d22b450982f2952a2919929bdb16a92885a76d2ef6beee348988ffeaabe8d5fd6b372e9613b8eb037d9065ede4be7ba9c88a99124a5943ecc14d97a2376b9fefc3b45ce567655538b8cde82f9fe29b4d1d3407950a7cd76e1414968c0742a15212c2ca71eb334a64865ccda0f9cc28ef27b7b0f6b337e9b34ddf9b3d88bd0fc680667571fae15f166777db285ed440af9d10c2015a932cf889d4562c5afc97868e64890732a5ff4d290798541906a5d3c0f67fe122e536c4c1bf05e8b2484354d9f79a554e511a18d7cd71a07c0a08db237238584f2d199e6295a83a92392056490cf53979dd83373462e8b4b791ea7f3345c8017602a24450a0a435da4a1eccd5aa192049f59a39d9e4faca999896b91bb41ea9ad9c66dc549bbd74d41caffd4eeacedc8a15570713feee419b80f0fc1387416c2d2c2279309e194b5abc6ddf32204670c85dd79ca999d795c9e3f31d211f1113bab7c61c127d674d92eae0b7486264caa54d8ab7356cec799bc202e952ef290acd2838446b5620cba6a73feb643bf14f263be16b9c13a943e5b30157a80bc523274c797c65054d12922139e54956e4794a9c122227b12a6a7444eccb44af657f9f806b6cece1dbeb727f3ce3d3e47e44e9ccb5371392fc3693c0b3033c4689b75f30d8780e800c84ca5a8d089e952f93c116794b60359b5f3393b0df7f1145c78cb9014d923b5c2b1da002bde4dc7cee49f2e0ac3dd3ffb5d8423a38a45967ca98c792fe7de1856077515b9cee431e2b0b779d7d5ad768be74c72bedb458a4998f565b28e0a15446058b3291a44322deba806627e464bec0ccc55953708de3e4157372724b0502823dbb71c21968418d4c515f66602e8f57d946687700ba9ef257415d277da30137ee9261a214feffe36cca5ca64f0a980c12029baa911202c356d72056895580c708fcab1e44140f6c6a69b7e546415acef42721c703c2e0aced9ccde0b7eb844b69955ca4aaca46d66458ad8a569416fe802cd8a909e414cc8e45fe4965c8647db394c9dd2d0e2ff0104502b9c1ea684d9de09364c170e69c248d2cfdb5277e4b40ba5859b6f99c267134ec403311eb0d638864adb11b666c721d98ae7223827262801347364aa254f37c73d1b099f62d018ad1e1b0fcd9c22f84529600fd1813ba853765cbd40de19395453b73ee0cee7f06c410157a2cc80662bcd7c8bb325f9cf4be644d89a24b11d7aa6c46f48d9c03c2af98969571e27307e82245ae67df3d41cd53448673461594f3156884669a9155320098c402a58b478f12ff2f570a4be08b9e99668f20b80ce55f1a84311e08050b4c0031ad8d26f18b866e8be2d9600e3ab59817057a5cf1680cc7450e6dbcdfb65712335631e0ba1f295fbda3d1cc6e7484247ab954bcc12f0ff4de5dedbc955e2e54d78fd17091714f05446565a02b423c801ce933b64cf42129ff431deb3a0db6bf90205d581bf70b5f0d194018d93540e6e70a686fa9de69694187d1c515e87a603d30462f94d2605c118022731e43335aa96db51a405f6134a9a09d2e52d2fa5562b41478a5a1154dd0aceeb172399856b3b0fdf844b455a554b73dbc7a0a993f8d68278b57216f7236047d6313d7c804e4cc894cac0f67c53d29fe38233f19ea436e2bf7ba7a3521d3ddcb067545658a0233e24427ad3d30725d3c40e0191080059e937930a711b48ccd38584ead68a389f03845245f35c4cde54f8719b4e25b1583a5242ebc15955b57df2a52ca3ce2d81abb0fdb6c2635350445f296c679caa93fb815c0b663e9f285707747ef729ef35c9709a09e2f44aab8be0dd0ec205de201a1544d761d3ddd2de46dc2c1c81b02e1907a121dc0c7d4f89ea3864df123e2ee380330e9b75324e8562d65fa3e1e626e2f0b6adf8f7fad188eda23895e9e7b223ce250c14f8eb8e91514e06a76d684e620f762a4d8cfa3910b9a39d989cfcee708ff1b782d5ee463482d831ab6580203653a18ded8d10e288c292b46d9ed37bd26345a3aa1a893221c0862a1ba77a864fb42b6dc0dcda3df8d813dd45ed404ee971babb7383adfc67b0bb5f049e39dcbd080c8d60a4d49fdec6660a6b23b08e10d481b790d261e9a87034879f7c96d08591de1f3e4b9914b353c6ec68554df004872bf238e65bc783000ca7d5b7ba9a9bfd5373c09401ceba3a4ee3130612a89a1027d06ff2cf3242e21a81da96ba0fea0102823efd92c39af11deaed7ae1fa7f9b4dc9165192713305ad87fac349e3472959f49768b41008854fe1bcfdbc66bda374ec83f40063f839a0106d5bd4773086bfa043febeaf4e09391cede8dbd3dab7de3889c6ef4856cb62f34a69df9475ced3a1ed04cb2219e133676c13b5a8bec03d3e660cd932f84b074197831b9d13d52dc1688370b631d228a8c9e7420cf2368002de862d74c446b3f5c2a2b7010c0fde738a24090c544a9a6b2a94ffc7cd6d5d57825300305b048b1ba2cd98b0e0a0d5717e94ebc1c2c6ccdb3467bfa4f83f7d6de2924f13ced9af29681e5ef8cc22d9b8edaa364961a8fd59804110b64c452eef83c1eea26985178231bc17f5a2985d3b9207c7887aecf0106f434b5b59a7c1e88076e20ef0e5053b23e2e13895266940522a0d08b60eb33db5fa1c8d27d59162ddd9ba2000f67f32df18327c120cb436c991634da8f490fa7777ba92950a3ead1e3197ebfa45f7ebedbb3d0c60fc95f1047d1c60453811433934eb89a0d492e7dc5bb8e2be681f3c1d12761f1eefa66f629e2478bc1afb8dfd751392ebe7f65b4b4f2723af66ff0ce8f4db42849ccf06fe213566983186e801a93379a978f56283aa835144bbf309aa9e1a3a6fd940144400b1b6bda7c6529f4bc8a92cbf64e8e8cf4473081033ac6158de928e581a3d6a59a8e0b468508c79353df80e083c59b9ed37cbd3408474bf81b05ec96f92606dd7d9ee62c4d3653180e75e240221dbc07a0a72ab9dfe2f26c69bf48cd19a4f3c8e4a2a09e7d2133f35f71a22ef9600cb7bfda351442b0a486d9aff25f85ae6bdda3a42b8d352e95e7b3d4d9e3f7d2cd1ceb1b6c293625e9bbf31c3bb4ec841005c757367f3c4c12c3ac678c4f90b0f35d9da2442c6d4979518ec4011bfa689642c07ad5cdb0f169c30059e3f4c45cd9feef74b7936944ffceff5f4de15efba5dc3d3a60d216578d47acaea6042139ce6be055db3bfcadc82106c30c9dd6ba414e5217bfd4ba1ca3a57d32a050198b3132f80208a16463ae395c12e2c763522887594d2822521c30cb9eba38648951cb2327816f64943232fbeb6091c33ec22b6434f6ceccb714cd343aea0efcb784dd78667a56721c5aad48dc09237ee72729eb2cf21f30351418498e663077fe259341403c68c162739542e5bd6f5fd492882e8e5ba753a94d8b36e3d313cd3bfcb7125a0dba1fd83b82ec57106bf4bb6e3a7330db99c38836f41aa138d83ec2c678464fa0317bb322cde52da56246598e2b59237a68a60ad64df1c5512b6a6653a596995cc11c5080c13c5bc10a371b7cd214d511c4dd4c7e7af088c2a40d94fcf3168ec692f22073733faa2409944c240cd74be15e4030e8dc35c70831746a08c1fb40dd9da33928e042b3846adbb809d731266672fa301a8db91c9bbfe69b318a5e04873d2dd015c25e5badb8b1ccc81cb25f2b938ed2a0d2bb4801c4240bb7d0f5b590346fef84b259617c4b4cf36231b70312b2412ef409d751d2d69b1bae1c8c3ad0075673ca702e06e9c3bbc65f3188b5f26f4746c7811b48edbaee7adc44d4c87e88e6c4e23c9195b29b90ad5a1a036109f7024f4f69e2368c5877d296f563508af79d96541506b68e2af9eda11396820113918c7ac631540afb8d592db2bb3c8084cb86df26eb72cc0a154dab73cff4e5b32dcb995d45ec36855336757473976e62abf01a8a3123f69c418ec01f8db74b5a15215e424ef48d9dd33861288166a12c432f3b6a1990af698d211189a8f737e3c271bfdf860831795f749337812cc96a4e6a5b532bedf254aef42690f8141ed70731207806469ff79c8add2e6d06e4be8e9f71bb969e325c9b9e56396ad2bc04aa994eb6d6b7af40cf9d2b4b047a8274216e431a98652a8a9add202dff4f8b28fd8b5df616ed54a608b8f84c8a36bbcb7140852bfc261e3f3aaf97e76074d06e3a1941bc90e8d005fbc70b13d6c6a245432704257e3c5aac9da8bd21bae74dbe1c2206ba9f02afc121c68141e4f88b72bcadb55539ad42627fb5d3d8e3606a3cd1ae140728178406b6430052e26c61d39f0860e2b304143a833dcc779b4832f4f141063e238154fcc64aba541037978f63e31f5462cd1a57a1ba58746b9279cb298b1d16461e3e2877db8805080498cc721f93dbf2c7b1ece2634e50557321ff84656be4911f1451740b98785f3bbbcb4071c1d21c67802ad33cef660dbbf4230560de3b818a8c745922cb854c5beaa0fc2637295a316e1f203888773e62a05265d0e9e8ab2a4e5c0dd83fda183e326b03dac1464069d245fccbff010280a156c1320bba07a7531a24dcbaca312eafea2c4048e385a024230492ae1331e06e4478ba92da158721147a33925029e7e3fbc187a7c37fb8a8e8979641ccc2c574954b7b82874e8bcf44952d4fee8944a1a392e751ef724b75d69285dfab99a1f26efe335da207bf83830d547ece70140b69782fdd6304d9610f1309421be6239ba649f5ff7eb5c3bb921bf4bcc3205988b46b623429477ad6687c6f37a095e0fa6c22f2afb90f2aee2fac6552e61993c0babae787f9ac0a60e0e6fb067f144e6f916d2c180d86785962930d473d2f864effebbbc30a403816e2baf7f2f926fb3b98c88b862a6dd76ade2ea064ba38df64f800597d6b0e8ebed8b9e2dea0febdf6024a61190ea0a7cb75143890f09747d3f6ddcd12740b1552c459a5498a7eac4dc2426c68ba459cc2699476047b1408144a05091b04d3182e34f12fd2e38e0319b98cd3d2ebddc35ae563ebf4f30bc0d7da0dabe404620c34b350975cfbad825121bf5eca7ac262508f8afc39b0d840ade5ac27ad79cb3ac15662865262d498d72c5445729ee5784a897b374d52550fab6d826a9bab65919f49954a68d95599027b2e04c502eb64792bb20c5cc5f1d8b90f0b54d17288a27b0acc7300b5e2c2584d7418458ee36a47be10c0e43f92b82a9d07767c4dbf506d6efbbef31a868420c48c77344b649292c890e0d313237295458a2e7608aa6cb5634a106273ddbbc9bea0a1168683164400128492d83d656128350e93eed756490d192059f842741fc6646b657b19c4e83a4eefacd5d56992ef1ff4439162bb444f0bd8046318ebf1190e41059c4bf0205a35f08f04747155cbcff82fdba84a74baf76f8c73aec58f8ef7d33b880e61cfab43eb4c5313c71d6b8fe083fb836fcfc1a925d138ddc792d83b60f2dbf4d0aa785f6849dee05e6285b6d43ac4c84d1c0cd182ad3a04dfff7e52843a09ce7c2dfa7c91f88c149a4db5ad0586a8f3ce5e35430ce9f8f5a29841c49bca0497aab0b705ca9d7ff38439b4ae67f2c6691683399aa91a7e2d08a3834bf7783a2106dd7c444e9b8ee3e685f51eb171a23c72c9137ad5f52659a41d7f7f5741df2c81b7e42a09c93db0d786d779d4cd2e6023ca89fc157232058de5e0afdd546e8edda5eea5e3cc9b722c0dd4b83f7ca58b1516dff25b94d97e3aa628482570b7a4d76d8330fe107acb2db6431db9835a47bd4ba66f758e2dfecfa01ca9d1a23bd8289450021cd51946a6a5604ed1ec0378df8ac5ffe21dd9d1c50db884687170024a829704fd902ed0e97e775ea12b0f06a0b0c30b3fab2e793c44cabe424e7c90c2879d224408e24a27f0466f26cb0026779c9d6c2379075fc844d41855abdb234ef65c612488ac9b3db0924c18dfc2169c1f304adb1936fc8b24f0ca3f9f87f7f658ec6fa6bd9b986fca6a163d064a5b3a0d355715a2ed6af612673f256b9afbc4358933a879fde029800551375fd7388a95179fd1bd16f3852224a5636951e34b216ac422c02996baa58198682dc7e84565dc176c712509ba26fa2ecdb4de95d1df4e8267de169258b313a9d483c1cf1d4180edac1b4e8f32e867148647a28c8512b11e68fcbb6149549b2a711945fdb6b938368066f5940d8f3491c40c802e4f7a95b706db7ada875312f11f0cd5ccc9a76d94d8fa46e2752fda97a681570fe49b98a34b78094dc96b5e89e232273b4e08b0a96819af440b66a93dd47c49263b9c171183e4783f5b6a124133805cf8950c1cfe7cb1df1de208c6008eed0183a2daa51e8a1246ceb2e62d58fca9a368574da9588df92c7b34b16b003cef8403a6361eea740372e20553d12137f1484f14b896ec6b00bfa714ca3a0d6d12a32adb77aeee848053a8f3df71e3e97c442cbd27bf44641aaa1349288a1a68758880898184fb92741a98dc8b1e21973befed054cf755e8fa4fbb90fec75a040d30c315f2164367db1aaebf41f9b7316445ce81f523818a497376a31b234bb68bd10dc1c913c1142f2726af28cc073e3a625dae80f4ab1c4708d4f323f276c4ca7a04709a54f073dd2d8dddc765f2ddd6dbfbbda21f43bd11fa8157db6eebbc10a48808d490e311ea4e710fa23b3e8d772ad28c112efe139d657d079bedb8e5826e6d8b63b2f49155669503c8da54b67c94b7671aa21ad9902fbfba820b116a933b2f15e85fd7449a16d97deb631641fecfa6f1f1fb24ec201be59eb201a5a7986fc571e9b7c1dd6a3ff0ced9740378afc9df25050502b0d77e38e602754442ae4026cb3bd7b5742af405aa31bc54eb7970774b2a592543d73463686646029cb2b6d635ec4367c113d2dac2d41ce8e359cbef6a02dbafd735b5e350b6cc685321322f7af96aecfc548a58ae3ee4c362d39b66c0383e210d89ac972d8842694d1b9367e5d4e318aa88910be21aef380a5a03e0558ddab373d656c170b71660c4cce144bfe8bdb03a4c7c0f2935d8f2c5cca6af01b2e4069bbe3a0fb24c0eb88435809006c26d5dc66f5e001756dde6868ec8c3a29c14ff752b6e7e09daa21c0e7a46b521cf3d1394ffb2b692ff2a6f84d8aabbe6f981435da23a2aa382d7e8ba25972035666b7ce689403812d74fb516695f8ac6e91ed77e820ab186acefbae0a61b7f6a3dcbff622c38030fb982d903407033b284ac8647665a89a697b65e905779dbaacda90d1e852a34b0bbdaffd253ff59e1ee162c8b8cf6625ac46770991f0f6cd6722d721e44057f76cdc6a25e042cd585d45edd39df67677cc7683327bec42289a97fbaab79a2f35cc5643fe0ef25f45447b7e58c1ae49d16cc65d7427a5e452eb8abe0526d8107311f30a322ddbd18c764356e9254096487629d7bdb14c5f0251696af7d0c4e1e6c32888508c42f249b0c3843c0cb65b12220809822a31c5fb79c1cd2f468046ba40b94ecbcd27354c23f9b242b4831c26cecfca4140563768e0a4fa23630169beaa1d4cbaa39faea1d80512df5d13d4e688517882b7c9dfac4e6aee9033b2d388109932842b0eb18fbdc3be1ac24b1590e77393d7e4c7d55c9cbd22fa445c08d0904ca62554f41939aab41520948a1129161cd9900d2ff37394a186fd214204da5d86eac93e4ee2fb9e4276af055546b6539bb316865b7a68ffdaa816b8e8c9174ba38c9a48a4c28b12ba3914e89f9d6fc325a5cced9a12614e91eec2e5af15ec6ffe5232f59fecd7063805f0900348018b071e9e96ca28eacef5ef5db2a2fba8f39170b0712c07d0060c6485e0055eebcbc8a466868ecf27e064e1cf4d54d9998aa8a6ef1d0bbd8fe00829ece539ce33730289aa92ca6aa153d54517973d783e2c6c3e27c9d7f28dd0ca82e8e87e92f5f359343116bd8242abcbb36c3a212a41356ce5e64c319af9c5fb8f72e7c86aa206837adc9f1288d8a2aba812d32bef5832c05cc5c62a4239c9db808c5c16991ea08002198f0f66ba2607cdc42f8dede0e1070fc7e9d5e3dba9673b16cf287227832c87c482118a6045dd7d381928d2e5f7851bb98c77c637a05054bcd86ea863089d52dcea54f98bbb64ceaca63e6ab0c0721e439b5f2c9d7a0c64e2a71462f00dbffddb887acb14ec384defbe31c6d09fe03cfb889cc1ffebc7da0fb1fd093682def2819077e53876070edfc1a109ad6d34d118278d68287843f06ef15cfcde61960ce7edce3bdc617794beb84856bd6f37c8f4d0ffc8eec886fe5245042ba090d43ec28eefc4c901722583094bd67f2b93db17254eb5f4f18afe19c65ee25d76d351e9ad47099b09a488a98d7be10e515c094c8293ff4e877216ee647e39d2412e062ecf988bd277a6642515c2daac2b8c317236465bbb90e901ad664336aca6ee64a2639dc0604bcc78ab8e294a06ec4dca7d30191c08a55102afa83bd6bf9856326387af342b546d6eb49f71a3b6e881172550c0f024d6a1c2db4fe8c0b7f3210d9a7986ae33f0f363bd42ff504357c70f9530a8e1223666a7f6ff5b550d667a9a86fbd6a814505742053e1aec4fe70e746401c3c01fce9253b92678cdb3b7b804886aea8c237d5c1a25db7792e7ac5daaa28a24079dc34af1d4dcc06399458be93dd04b58abbdba99d3f4bc403b152cd3e447c6d171d3aa6b02efdee0b8ea9cbf377cb34e25195aedb3c745f4ff28950915ff3c03567c858d94acc7a3dad639e4270a2748e15486aef19ea7b309f6b377f12818f9019d3636e77e670d65f02fb8bf2d750efdedce9d5f2e33cd2802a82f189f32dbe7202b6b44cbeb9a38a70bc54c4a9d43c0d72d3a2e9f6fd06d3f355397aac56879ba7c1f9f26037c1bdf8faf5c82ebed90cf61247e346d59dbe5fb1e1716172a509b6632a54a739a3d840c00b1a2997ab0e1a72e128d8f99466affcef55ab50ba97a3bf367c5591e7ddaccdd227ccc85b152b64c38e401a27c10e390432dcdb783f0e61292c01000259ccbdbceca085591bb943fa1dd2dab19c192e1b513237d45968f88d9a4e12c081ad34bf5bddf7072137413701215d1c73a30449bf42a4a81faf5dc4f9b9a14d64458d6cc33612b8dfd55c6233779a00cb39860341c85c100ba33b1333c50ee24e9cfcff033fc0c3fc3cff033c07326b2ade9d65a83b566659252528dbd58525ee515642a534a49a69454b4ccedfe6689030c80609490b91fd90b032d0313030a1e1072aa4f26ad3ab516ff1dd069ca17f39b2a46864ab1439f1b4ca3e6e831ec99b7d8ce4aed12b35779943a2056a77c55d375143a5cf6696ce91f7e08287340ca56655bb652b6dd7314395c52e7da33cf3b2db15b0f250e4aad74871dfdf6bc1f50e0b0ca1b1855cfac1f2ea29be3e3661dd45faa5722338a1bd0ca5f73ae2d636d40cd74d9b0ea3b36206dfdbceedfb841a3465903abc11ca3ee2eb2c95c67c634951cbdf5344f88c7fa424903fa94b710d7692a68406c2d7bc616cd19d01b364ca566db6a9b9d182866606fecbbaaf9ab6a19520f4f9e5973e9da26a58761ab750a19d052751619638d93e2fe3120e6f8fffd67a9d7b45a0c8897d24c97b7d276ee8701b5a46d9d4cf4a396d58101ddf996bb942673cbc3be8054e3b3e9b6fa5725748e46c50b689b52873b61afbaa53c51e902ea75b291f939a95c50b880922fbc4ceabc9fb6eb5b4077b9bcf5b56746e9191e82a205b4ed7e9dd6db3a93547da06401a5bb5f8e799ab24e4c295840e7287ea3cec7dce825e50ad7d01353c934753a142ba096562fdfa5eba8797ea50a68573be7a4651cd99975381a3f3a3c54020a159079c54c4ec7d9e95ab3c1fe02ca1434d541e567db8729c3d1985240da277d2a9e32cc88ac01250a68b937e66fa69d93fc2aa04001313eb8dc787ba13c01bd698dc9b865fb5e674d78c219509ce0c6b44ee6ecdcec51a5090a137209ab28015dc347c6dc52e12a76a32461cda1efe939797a124fe65e9ff3d5ce201ac61c8d8f8204028964a2020509c81cc6dd860fdb1150de39efef64a518218b904440893204c4dc68a337d574797216c2afbbb2df522908e8d3327ba81633eb1a77530102537e809ad13d6bdd9c5175f4283e40490da507881d4e47e95aa7151ea0640728794507e8f398a4769951f5a9949203e46ad038f6ed6955ac70e0dcc6fbe426e719d3dc4ed3f1b986aa05ca0dd09ae5fde6bb2d7f73a8d8003153aad4ec3a0af9a8aa01ca746d8d8ad170f3e5bc088506a8f5af9d5725d5b439cd002d63966afc67b399451f1419a033ab0d3fbbdbe4867d0c102a9b5f97541518a0645263ba2ef79835642c9f8d672371b9008106b8d8b3d4009dc6d232f6d334353be7689c181a0b0dd0733a9d6f527f0668d5b33f865129969a593240b9065325f62a47b0c400f9edb359d6cbd2eff2020394a869f01cef647a8629af38aa9ce75af18cf776e7b27abbee543627d915a8b9633fcb74f502a51568d726d37cbd155b3ca8b00225ab40cda8d334b1496f56d11555345ffcebf4d454044a2ad032a34ed9a0366cac534105eae4f8ca926ffc489af1223366a4532037c7cd366d979922dfd13a445e0c554c81525d9e658d756afc2222bf31923c4440dae18f24313a444236368e514a811252a0566f12f7b0528cd3218f4119056a5ecc308e4683b73186c245564481d2ebaff229d396765e8731a3a37d80789118f285443243d5f8c0088d1a60782149911703243712431a6316645042814cc367a5877e9fffad8002e57a569af2db398aad523e81f8785aa9316cd7bc9e7902b9e3b23ccd6dae13c8d9e4f14faac9d138a9d1f1f14a225138b19bcc8db8a9aaebeaebde06b7959daf0e55c30b89a742d90462a6cadd939d56777d3481389de2a69add622abd9209b4f2fc7a6f8ec9b1752a9840abfdb97ad75dad19a3333e3a68748cd490482e817a3d5fe3683448a363a486b1046226a556eab257a9043aaded27b3b9dfb956e6814209f4eb8c1b76f69cc753469904fbd4b6dfdbb92b9240b81c9179155b2dfbcd4502f51ecddcd4abe5aae31010916e0512a89d77a9938dfccadbae0f1580b05de511a84fbad4abfd5af8aa9ba3315371046a8ba74a313c8da834029d64d8dbda75da244436b2fe040ae4f01446a0c68c52b5af58a3ca14288b40cba454e5a82a313e7b38fa1545f49d3de7f4962f99b195f1203ffbf3d437ed381a8f6e22d0bf261a648b59410462ae9499d39c2e91882c8772089498f1b47ece4abb142a36f20197288640ccc88d53f1acbb5a850211902f944220938fcc9cbe844650088172db39a316ae73346c9ba00c0271fa4aeb241501451048d97473d6ec771dc70f046ad58497be4619fb740081d45b5a9f967557b7f53fa0e7fb54c50fda07e543ee01a9b4feb625ea52cf46e9e15c9e2f62b64974819207945aa634ee69d51de39115fcc762e00303223f82819f21f260688afc48470902791cef0862b903c23fdab9b9edba10fd02b1d801a9598937a93c8b98a840686c84ace0fdf0014b1de8907340de7f8c629bb4a13b4e3a40449e04920d8f1a1f72406db5d5c40cb977b0c4c153cf28df2d7040cf30e12d3775db94990e9637205eccf73abd49eb9e19372065f6ceb99933b74db6b401e9356754691d64deaad880566d5e4296ccc15c4b334170903db0ac011d734d4feacbc6cb771735a044124b1ad01d3b5ee7a91734a066123f733193dc79f50c2817af648827f93b31ec132c66406e5e3d6b34ac35e6ec322064ec9ce32fff536b73210342c379de51d999f3751c8d1b61f48c8f0e918e14d21158c680f417cb644aad2676ffabc15d2f81450ce816bfd9f02f1506b44e17b1a9363d8a312d60406e9af9635a32664d63b97c0165af3d5526ad4376b62d5e80c4d205749e4f19b7d556f7a7b98054aa75fcf7a6735be3fc650b089769c6bc97f672716b019d37db4e5e6b5b461b2e5940cb963abe3a7fd8f7060be8cef2f5774deef5c6e50aaed6f1518919331b938e4c2b68aad6ff5dd63ab12a30630919df1bf3dbd65241b5d6da9d3ccbae695aa6d096b3314cb137191e132c52682c31cf6967dac951c0e4dd65c5d3e6e46973b9ca9cb125b38c031728a03bc6b7fdfe2b7b6edabd3c01b5d935ecc76953e72f27a09458d761b5fc8e51d7bb4b1310bb55e62ca3ebf66e1513d0620755f11bba2666b72c01ad36ea7359ee3279f74a407be7b2b16ba9ad7626e29204a48a2964e95ab56d0a170928a9517b2c3b0d7233b31c0135b58ad99f9e693abec50848adf6871d5f9752cbb31401f5613f9734952e4440c7a0f276abfae0b7c410fcc8181b3f328692485830992042611902f2bc4d79ada5d4a37a090161d3fbc5afffdc2c6d10d0716534a7f11720a0d44bcb8df6f901525cabb8c79561c60f171fa0dc3c93383fb1673fb6f40019575cd7dacd99558e97602c3c40674cca4ede18af6bc852587680982ba7c35ed32d30a1418130d2021249ce9048122734542e3a58d7f5f36664c3ae58728058a6f427f11c533a931f1949fa81050768d9326b99b662b9dc00a95ddb4cb5f9c6def44f60b101629ca88ca95101232bf82e3e66744c0ad0804917bf4503b2987cdbd8c2013218a0d9855f179a970a5880024e922dc104ac91490d1101603186eac200324626353c10002c7e46aa31e323d128401613915f35188848881660c8c042860c195838608b074c3a429201b068828cc944490290451a494c22002cd2486220221202c8000002b410f90d14ac3432d9d8d84206160720c0c84f5ea11b05585c11e2d10aed1811e198d0d8d8d062610501965568c7080d8e49dad8d06251c5009654a0a69b6d179ba6702d7654a0a35e9121ebdcd4a7e614e878f2766dbc2d4cc7e33dda14a8f5b24c46adeeb21c236479bc077bbc4729b463448c0e460aed1861201e353636b458468192b7efaae9e41ced8935123758c7fa90c9c1918f0e9d7c748c883c8d8e100f90d5b801126c74601105ba6777f6cc52ad97d2335e84a1012ebef000178ba1b83bee64ccc3c6e0f2f13c8995571ef7f623289062edb8594e7c50d3ff2750e7c2969be7be7802e9fe77e2fad5123b639d400ccd39bddd8d6deb36270ebb2b6a2a1b55d5f58919a7ce7fd64da0d4fa515f9d938733294d206756ecca999315b60e8ec99984806cdc9209b4b4f51837b17f9f1b79c10452acd6c9579cbef2fb12285da6eaf56b837da49084c16209f48ccfe5f9b689a371c3e0c75209748d61b766b34e8b5d520299d546bd4f6abbaebf2789c6e124103adb346b4dfa1095044a7e67dd7df3f81b3c9140cd2c199f3eeadc7196221c90c10209e4a6b7f5f3231a76658f407a9a655f36bfe275ca1128fd25c592b3dcd58c37d308a452a7e48ae8c7e81dc8c288337fdcaeafa9dc8e6511889dc39a523ba810cf5204628a59f363747922107bdd57de5655de928b802c8840c997ba767b67ebd49ae510b788ad8ca6ef1c573e348d09f59ebd9549864029d9f9d834c367b1691c1f894618cf4036e0cf403e8c2e85406757e559469652b93921b633f398cc3b77bc8fe9a8f90b590681162b1f5595988d8c41814510a8f3cdc9d55a2d6c3cb3040229d3d8cb9a7ab35e8cb30002215bccdb1fd3da619a82cff52140d4c2f207a45637a77ebd849311913432d991c50fe85d91e569e76c9da6fb01e2e101b274031289416491a50fa975ba65745bc2357c586cbdd63ba97bf4d4f16c1a5dbcdaf2faee0169ca5d7f738c6558f480702fe5b56fa6973ca0d312d36adb549d5da6173ca0bc564c5db20e8395a8a2631053c618430c310491692b1d42091284213988c2381204516da907124070490c45298e62186314418610420831841042083164448486a63cdeb7533957926c5bbbe93b1f0e4ef73f479d9c8d8e7eb567d68e7d8ef1ba995b55511d117cec206756233d918e900671295c8ea9a04338bde6c4194d7944d2111f0c23c57e0b7d19c80d4d8655b7255eb720730ab0ace890305f5a3138b9233cd04ddbe27e826dad9978b18983a4123ced1b5d1ba40cdfbc817ba6016573d31e9dca81ce1437795752da46b009e44bf7aca4a68efc8434dae4d745072af5dd02584cd4d73da2f953b1e553023fab4df98b994d521bf4af51c94ec628e0203a9b51d7e8410072583264ee4c902b50b6400e0b600a6496881a59e037000e76629313a353dd0c215b2b14d61e90f0544ebfe71c4dccaf45599a3699094e263b8434434e052db75e1ac501958195257aef7e89e0aa83270eba96efb5945a306da409cb58b9c9e54f848d16164f304ca232969b074365739715c265fdf72f67a6639942f81dcd3493796d862ab800077ea1e0d56c9012e268ffee61e58c7dfb65750cad280326659a3c51a6fb70f25a78c83711a127ec991d52ca88018ad6bd4014a0a3873e9c21e277b0e212df4d935f98bc85f3edb26ba3f316f5974c3ac5d3253276037566a8abed9041b6f5ea024280e133a4b3f3b568e974e01b3505c1ed417574920296d7e6d04ab3601072c4b918d823cd3e85ec58e7107f62f551712d78d6e74835975276da04ef190a18a48e04574108ca7a80c14c7ae4c2e0f7fb29d2d4d20a2e112dc8f04a68f980073421ad88642873b1f072caa8334f027d237fc04ba732bfd36a5cf93b59580c9671dc6643cdfbf0f3e81bb07ca54b73cf5623efc7b76a2ec4c74a887c4bdb5acfd4ad71ece7822089bcff2eaada96e0cd92f97b78fa3f6e1c1bf7b4f91ea20bcfaf339a900de501490b19b69ffe2b2fd138ad0105e28e220fd16044b0bfedd5855ecd3ef8c868afdd78a8e0ac9c0e551aea77e630da6e067a3ef68ce0b16c97aac4759861c83e48dea8ddfb4ba62cd50125bd79a51b115ae190ecf235b937c44bd13ec929a1d91f14d22a0b84ae15e7bdd2329dfac948a46a6743e93dae94737cc828a7ebe0352486ed73773ea2281a68aa90b3a60d65e0ffe9ca04b7b2397b17766c96cf61ac2e594eb8da59893a9dcc0930b73ec97ad48c2c16541ae39efad85e3deb71c9ef249ed8bbe3e8aad13f35228c2d33787122d5e4f239d1db557f563f982f2c185f42981e557413ca311e307357882958957ba47c866b0b562ee929a41ce0a90f809da73b77f9636f85ef2e871a41dbdc275199737f020009c1ac2f15a92930efe76f58b3d6375770c3064d0584482130b513b4a4239a88d557473dfc659726a4e1321a2a8d53c43402c838c7d669bae1648cf94ce74a5a95d3d8a4626407525a957609912d36466007e349c85cea5e32f6fa48ab1275d95ef6ec0bf4a99056a56663aceae1f0992616234d1997927f4bc6c586cae31600a1fff8fd003a734b49e111aa716487e09770b53b6c709f0d75cfd93443c7ac3239867137d76f36c0d28d2c6820383acc8b2580bcd9517c908e1d80e0ee68e4b8d265481f4b19928cef2093118340ade328c32a03f737d342048bc8bc0808a0f4ee832d9e73d74db9fef572e45a54fe5f30b3f8fbbed8e969b4ee117b06bf37f5302e1ec018e9ca97c650163bc21d3b823d2ec5173b6f85d07033d1518c55b0c4375ab0e130f4d3736231535e03e23e4a491fbcb13e3a6008ede2f699f70c9e67fb3a43e011f27239015fc71a4720b89ec3fbaa98b9d4b2afca2130fa562dc2fc704adfd3b45838c7acf6c19edaa65056560b27f9655e1fe6fb421145a975383d7e97e98a19149b52e05b89f4f8e6a0b7c722e7f08b746e2bf6a3e4ba27739b0acee8b43f77c4e37d9e61a04480811a1edbf9d2035064864f1a4a760d4af61befaea69d044e46e5d5e588a826726b945608530482715546cfce2ac49fa3a967c53d8df697128a774b738c8a605808d74c222070f0404a7815b2c071101d05fc06737cdfe09a8146a55466856098b476d9c838daf06582768f4945da7a5fd1c15d72173cf35a1a64eb7718a7ab19b286621bc6ed9fd683fe0b7d962550f58b16a9ec23647c38fb003351d610ea0ae50223b0b7c6471df2127cc85241cb6f372be84e992212c9e238b05094716e9a8acdee410b92a89fdee4c7ac27d619077e80dbc28768afa0503eb422bd8631a365fe00077a3c4743f4417dca7b1e968c5786a6d8698b8a1f46d4c47a4ca57fc50f0b405cac837a05d49380c3d37e8bd2b934c90f54c60190dd2a495b61160a4c67f74f2d536c4ef3962fca1e6d44280321a22e5ca845b85ed466fdefa5af12fe5960b9875d4e85f4ff86f8f1e1ce5946e72167fc16dc0ed326e55281f6b52e9ee121ffe3a5f3c5b82ea30d4c0d517a4181410e6b182d303603515f65d648783aad9fca35d33b1b568a281f63f47758841f128eb07f9217e9b59b4daffbc0da5a02019318bdcb9668c3be0d0c94625713c104fbc69bd3aaabe6b385151c9f6a9f9c62a9f225e1547c25b62be8ce887b040a01d246ae20e0399c20f0a2cbab01c5355c60355bd40981851e35672ce913986481a9af60f1aa94b2b40d32c937c9aac35fef0db4bcb29db47bdb4eedb2f85fe2b42c981e1a668936818dab5efd6a6660cbac8c9aad0826fc003bb54caff9988d83a58aebbc340dfd5f82496c0c6d855dca28c1a1a59607f78d18b1d33917670aa5d046e1ef96ea5a21864a50b865641356ae279ff6afd1b86450446e04702c92a065a892716837dccbbb10ebaf0ea3fe4b61316d12ab42f26e6da7156e293864619f0f5fa6481d43d4682e32ed35d8b4630fcf86ccb876eeab1c50aa9e6578944f6174707d44d312dddcfbbaac7fc3930eaa5ca9a68836d7fbecf43e1010e393fc7d6968cbe00a752f3a6149b109f686b0f4fdd77287828c11ca2b569cc0f987870c9d7c171bb419f834f76dd2dbdfb7ecd664e4c49f9a3b4f1a899d469193d4f143fc3a87ad121c359b56d8f68485b7eab8144cb5f9b0304d503de2b4aa3aed67a1b38c69f31a12ffe6b6e336417c04078221cb987710b59b840c2fd3e24190faeebc4b699502d3f30736641c6ba2c4463b0429c8e6c14237ef09be8632ac6e107c8d32ba77d2a36e8dd092e3b6b95def7afb685eb883e64c4ee161b8df94ea401ca2659b9b6e78a405054204387ab9003d4c6eee689696de401a4156b13beb2f40a09fd00dfc00a9a9881cff23c538ed6b35986e640095e1e016a2d5668a160b81c1e0c491a5f3bd965a421de49a01d96ada05431279ee87f50aef51c29c7304cfac46f148cbff7c561fceafb266041ad37a4f05e4922edbbb36b8b6903b2b4bb9fb99981ac7dcb7d439f125f5393d5f95ebe520197d35394515760f881523ae282fa768c7a285d36a17917f434d2516ac1cb214a533561f8e6f8125035dc9b42da37b09237cf6d18e30d752853b8ae08063672835d4ca9d9c54ca2ebbf565b64bae05c10282f29405148d110ac05c57411e436aa3f973eed13b14cb1b141f293815a89964a54cd32bfd7632b7c877fa958dfb117bba281cd868d10a0177a3e6b8e6bbe89940dc09d7d53ed918eedc91b80e70feec91fdbabc845b1a946a8f5c19429a804964da7e2fd4535a3539feb4dd39d73686785fba7a4cb55bc7e08e0676b0a81f916e7e01435dfc6c58a40d3e0f889ed3ceb6ec2fe17233502b3b278a32235c4473210a75b1f3342b2501e499084c9991ac3c5425ed53823f2777cd2ee18d74d8344129f4166beb363e07c535adba78bfe906114f4f10be9464af255a1508a999796c2257899ffa84e5c8c41e6260a4ea5e66cf49508dd75c8102fa24002a216e28c887733a851657126b4bcf05585fccab38bb761fa957830294b94884797a01dafbeb47d00cb1ec78a8a42dae2ae8292d10c24de715c471bc9141ab1d9a9d102c4f59a3085130e1592ecd29f7adc702cf02cbd250d0c893", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0x466660cf7b14b9faff091c218b7a25c74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x52bec856e0fcfd14e28957c3151da0574e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x588177cf191e890d83b10e9653cfd663c8a382d5afce8518636dd9565c22a631", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0300", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00c00e109fbeca353600000000000000", + "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0200", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb325863a892a3ae69770f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c": "0x70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb338e51eb38881e3ae18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43": "0x18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e8eacae64ab191ec7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121": "0x7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195010b0aa882841dd68617572618018eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43": "0x18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c43", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509b075dcd5e9bcdc0617572618070f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c": "0x70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950fd2b9eba034c493c61757261807071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121": "0x7071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e0121", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x0c18eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c4318eb251941f0c47479e66bd016d1e7693c109816ac9e4cd2a90e4410fc227c437071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e01217071099e45cda0dcec9b0bfce94e7194b7a466f80640fd17140b4807825e012170f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c70f4edfe03752ef15576b1bd42dcdcfd112a768b1dcdd94d1bb5f8fa82d6a06c", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000" }, - "parachainSystem": {}, - "parachainInfo": { - "parachainId": 4296 - }, - "balances": { - "balances": [ - [ - "5E4kXkguH9JAVqymJZGkJHi5se2grgR1ZKhhkNUmxqMTSP7g", - 1000000000000000000000 - ], - [ - "5EcoymUFpbrankU7ykYZqLodvWcKEpSR8dZFyhfgcAQD1Z8K", - 100000000000000 - ], - [ - "5CdNrn1awFNr4QhHDWvMCTgk31PdVn9UDRb4XSdCiFKSzhbT", - 100000000000000 - ], - [ - "5Ec8oKW5L5RNjQguv3qxgC7HvYY3SWHkn93EtUGzDZjHxsK7", - 100000000000000 - ] - ] - }, - "collatorSelection": { - "invulnerables": [ - "5EcoymUFpbrankU7ykYZqLodvWcKEpSR8dZFyhfgcAQD1Z8K", - "5CdNrn1awFNr4QhHDWvMCTgk31PdVn9UDRb4XSdCiFKSzhbT", - "5Ec8oKW5L5RNjQguv3qxgC7HvYY3SWHkn93EtUGzDZjHxsK7" - ], - "candidacyBond": 1, - "desiredCandidates": 0 - }, - "session": { - "keys": [ - [ - "5EcoymUFpbrankU7ykYZqLodvWcKEpSR8dZFyhfgcAQD1Z8K", - "5EcoymUFpbrankU7ykYZqLodvWcKEpSR8dZFyhfgcAQD1Z8K", - { - "aura": "5EcoymUFpbrankU7ykYZqLodvWcKEpSR8dZFyhfgcAQD1Z8K" - } - ], - [ - "5Ec8oKW5L5RNjQguv3qxgC7HvYY3SWHkn93EtUGzDZjHxsK7", - "5Ec8oKW5L5RNjQguv3qxgC7HvYY3SWHkn93EtUGzDZjHxsK7", - { - "aura": "5Ec8oKW5L5RNjQguv3qxgC7HvYY3SWHkn93EtUGzDZjHxsK7" - } - ], - [ - "5CdNrn1awFNr4QhHDWvMCTgk31PdVn9UDRb4XSdCiFKSzhbT", - "5CdNrn1awFNr4QhHDWvMCTgk31PdVn9UDRb4XSdCiFKSzhbT", - { - "aura": "5CdNrn1awFNr4QhHDWvMCTgk31PdVn9UDRb4XSdCiFKSzhbT" - } - ] - ] - }, - "aura": { - "authorities": [] - }, - "auraExt": {}, - "sudo": { - "key": "5E4kXkguH9JAVqymJZGkJHi5se2grgR1ZKhhkNUmxqMTSP7g" - }, - "polkadotXcm": { - "safeXcmVersion": 3 - } + "childrenDefault": {} } } } \ No newline at end of file diff --git a/parachain/node/src/command.rs b/parachain/node/src/command.rs index c775b0c8a..b1a3036cb 100644 --- a/parachain/node/src/command.rs +++ b/parachain/node/src/command.rs @@ -26,7 +26,7 @@ fn load_spec(id: &str) -> std::result::Result, String> { Box::new(chain_spec::development_config(id)) }, "gargantuan" => Box::new(chain_spec::ChainSpec::from_json_bytes( - include_bytes!("../../chainspec/gargantuan-raw.json").to_vec(), + include_bytes!("../../chainspec/gargantuan.json").to_vec(), )?), "" | "local" => Box::new(chain_spec::local_testnet_config()), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),